Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fixed decimals #343

Merged
merged 14 commits into from
Feb 23, 2023
51 changes: 38 additions & 13 deletions compiler/frontend/src/hir_to_mir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,9 +294,15 @@ impl<'a> LoweringContext<'a> {
hir::Expression::Match { expression, cases } => {
assert!(!cases.is_empty());

let responsible = body.push_hir_id(hir_id.clone());
let responsible_for_match = body.push_hir_id(hir_id.clone());
let expression = self.mapping[expression];
self.compile_match(body, expression, cases, responsible)
self.compile_match(
body,
expression,
cases,
responsible_for_needs,
responsible_for_match,
)
}
hir::Expression::Lambda(hir::Lambda {
parameters: original_parameters,
Expand Down Expand Up @@ -405,53 +411,72 @@ impl<'a> LoweringContext<'a> {
body: &mut BodyBuilder,
expression: Id,
cases: &[(hir::Pattern, hir::Body)],
responsible: Id,
responsible_for_needs: Id,
responsible_for_match: Id,
) -> Id {
self.compile_match_rec(body, expression, cases, responsible, vec![])
self.compile_match_rec(
body,
expression,
cases,
responsible_for_needs,
responsible_for_match,
vec![],
)
}
fn compile_match_rec(
&mut self,
body: &mut BodyBuilder,
expression: Id,
cases: &[(hir::Pattern, hir::Body)],
responsible: Id,
responsible_for_needs: Id,
responsible_for_match: Id,
mut no_match_reasons: Vec<Id>,
) -> Id {
match cases {
[] => {
let reason = body.push_text("No case matched the given expression.".to_string());
// TODO: concat reasons
body.push_panic(reason, responsible)
body.push_panic(reason, responsible_for_match)
}
[(case_pattern, case_body), rest @ ..] => {
let pattern_result = PatternLoweringContext::compile_pattern(
self.db,
body,
responsible,
responsible_for_match,
expression,
case_pattern,
);

let is_match = body.push_is_match(pattern_result, responsible);
let is_match = body.push_is_match(pattern_result, responsible_for_match);

let builtin_if_else = body.push_builtin(BuiltinFunction::IfElse);
let then_lambda = body.push_lambda(|body, _| {
self.last_pattern_result = Some((pattern_result, false));
self.compile_expressions(body, responsible, &case_body.expressions);
self.compile_expressions(body, responsible_for_needs, &case_body.expressions);
});
let else_lambda = body.push_lambda(|body, _| {
let list_get_function = body.push_builtin(BuiltinFunction::ListGet);
let one = body.push_int(1.into());
let reason =
body.push_call(list_get_function, vec![pattern_result, one], responsible);
let reason = body.push_call(
list_get_function,
vec![pattern_result, one],
responsible_for_match,
);
no_match_reasons.push(reason);

self.compile_match_rec(body, expression, rest, responsible, no_match_reasons);
self.compile_match_rec(
body,
expression,
rest,
responsible_for_needs,
responsible_for_match,
no_match_reasons,
);
});
body.push_call(
builtin_if_else,
vec![is_match, then_lambda, else_lambda],
responsible,
responsible_for_needs,
)
}
}
Expand Down
9 changes: 9 additions & 0 deletions compiler/vm/src/builtin_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,11 +277,17 @@ impl Heap {
}
fn int_divide_truncating(&mut self, args: &[Pointer]) -> BuiltinResult {
unpack_and_later_drop!(self, args, |dividend: &Int, divisor: &Int| {
if divisor.data.value == 0.into() {
return Err("Can't divide by zero.".to_string());
}
Return(self.create_int(&dividend.value / &divisor.value))
})
}
fn int_modulo(&mut self, args: &[Pointer]) -> BuiltinResult {
unpack_and_later_drop!(self, args, |dividend: &Int, divisor: &Int| {
if divisor.data.value == 0.into() {
return Err("Can't divide by zero.".to_string());
}
Return(self.create_int(dividend.value.mod_floor(&divisor.value)))
})
}
Expand All @@ -301,6 +307,9 @@ impl Heap {
}
fn int_remainder(&mut self, args: &[Pointer]) -> BuiltinResult {
unpack_and_later_drop!(self, args, |dividend: &Int, divisor: &Int| {
if divisor.data.value == 0.into() {
return Err("Can't divide by zero.".to_string());
}
Return(self.create_int(&dividend.value % &divisor.value))
})
}
Expand Down
1 change: 1 addition & 0 deletions packages/Core/_.candy
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ channel := use ".channel"
[async, await, parallel] := use ".concurrency"
[if, ifElse, loop, recursive] := use ".controlFlow"
[equals] := use ".equality"
fixedDecimal := use ".fixedDecimal"
function := use ".function"
int := use ".int"
iterable := use ".iterable"
Expand Down
58 changes: 58 additions & 0 deletions packages/Core/fixedDecimal.candy
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# As soon as tags or value hiding is supported, change fixed point numbers to
# that. Currently, they're just normal ints.

bool = use "..bool"
[ifElse, recursive] = use "..controlFlow"
[equals] = use "..equality"
int = use "..int"
text = use "..text"
[toDebugText] = use "..toDebugText"

decimalsPow = 10000000
# TODO: Perhaps allow different levels of precision.

is := int.is

fromInt a :=
needs (is a)
int.multiply a decimalsPow

floorToInt a :=
needs (is a)
int.divideTruncating a decimalsPow

isNonNegative a :=
needs (is a)
int.isNonNegative a

add a b :=
needs (is a)
needs (is b)
a | int.add b

multiply a b :=
needs (is a)
needs (is b)
a | int.multiply b | int.divideTruncating decimalsPow

divide a b :=
needs (is a)
needs (is b)
needs (b | equals 0 | bool.not) "You can't divide by zero."
a | int.multiply decimalsPow | int.divideTruncating b

approxEquals a b delta :=
# TODO: Instead of having to know the default precision and pass in an
# absolute delta, you should be able to specify how many digits of precision
# should be equal.
needs (is a)
needs (is b)
needs (int.is delta)
needs (int.isNonNegative delta)
a | int.subtract b | int.absolute | int.isLessThanOrEqualTo delta

toText a :=
needs (is a)
beforeDot = a | floorToInt | toDebugText
afterDot = a | int.modulo decimalsPow | int.add decimalsPow | toDebugText | text.removePrefix "1"
"{beforeDot}.{afterDot}"
3 changes: 3 additions & 0 deletions packages/Core/int.candy
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,17 @@ multiply factorA factorB :=
divideTruncating dividend divisor :=
needs (is dividend)
needs (is divisor)
needs (divisor | equals 0 | bool.not) "You can't divide by zero."
dividend | ✨.intDivideTruncating divisor
remainder dividend divisor :=
needs (is dividend)
needs (is divisor)
needs (divisor | equals 0 | bool.not) "You can't divide by zero."
dividend | ✨.intRemainder divisor
modulo dividend divisor :=
needs (is dividend)
needs (is divisor)
needs (divisor | equals 0 | bool.not) "You can't divide by zero."
dividend | ✨.intModulo divisor

compareTo valueA valueB :=
Expand Down
18 changes: 18 additions & 0 deletions packages/examples/sqrt.candy
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[equals, fixedDecimal, ifElse, recursive] = use "...Core"
fixed = fixedDecimal

sqrt x :=
needs (fixed.is x)
needs (fixed.isNonNegative x)

recursive (x | fixed.divide (2 | fixed.fromInt)) { recurse guess ->
refinedGuess = fixed.divide
(guess | fixed.add (x | fixed.divide guess))
(2 | fixed.fromInt)
ifElse (fixed.approxEquals guess refinedGuess 10) { guess } { recurse refinedGuess }
}

main _ :=
input = 2
result = input | fixed.fromInt | sqrt
✨.print "The root of {input} is {result | fixed.toText}"