diff --git a/compiler/frontend/src/hir_to_mir.rs b/compiler/frontend/src/hir_to_mir.rs index 41e358f5a..e30c44fca 100644 --- a/compiler/frontend/src/hir_to_mir.rs +++ b/compiler/frontend/src/hir_to_mir.rs @@ -297,9 +297,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, @@ -408,34 +414,43 @@ 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 { 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, _| { @@ -443,21 +458,31 @@ impl<'a> LoweringContext<'a> { result: pattern_result, is_trivial: 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_match, ) } } diff --git a/compiler/vm/src/builtin_functions.rs b/compiler/vm/src/builtin_functions.rs index 4d13795bb..9a5913210 100644 --- a/compiler/vm/src/builtin_functions.rs +++ b/compiler/vm/src/builtin_functions.rs @@ -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(÷nd.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))) }) } @@ -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(÷nd.value % &divisor.value)) }) } diff --git a/packages/Core/_.candy b/packages/Core/_.candy index db324059c..8ca321248 100644 --- a/packages/Core/_.candy +++ b/packages/Core/_.candy @@ -4,6 +4,7 @@ channel := use ".channel" [async, await, parallel] := use ".concurrency" [if, ifElse, loop, recursive, repeat] := use ".controlFlow" [equals] := use ".equality" +fixedDecimal := use ".fixedDecimal" function := use ".function" int := use ".int" iterable := use ".iterable" diff --git a/packages/Core/fixedDecimal.candy b/packages/Core/fixedDecimal.candy new file mode 100644 index 000000000..37ca63a0b --- /dev/null +++ b/packages/Core/fixedDecimal.candy @@ -0,0 +1,131 @@ +# TODO: 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" +[run] = use "..function" +int = use "..int" +text = use "..text" +[toDebugText] = use "..toDebugText" + +decimalsPow = 10000000 +# TODO: Perhaps allow different levels of precision. + +is := int.is + +fromInt a := + needs (int.is a) + a | int.multiply decimalsPow +floorToInt a := + needs (is a) + a | int.divideTruncating decimalsPow + +add summandA summandB := + needs (is summandA) + needs (is summandB) + summandA | int.add summandB +subtract minuend subtrahend := + needs (is minuend) + needs (is subtrahend) + minuend | int.subtract subtrahend +negate value := + needs (is value) + value | int.negate +multiply factorA factorB := + needs (is factorA) + needs (is factorB) + factorA | int.multiply factorB | int.divideTruncating decimalsPow +divide dividend divisor := + needs (is dividend) + needs (is divisor) + needs (divisor | equals 0 | bool.not) "You can't divide by zero." + dividend | int.multiply decimalsPow | int.divideTruncating divisor + +compareTo valueA valueB := + needs (is valueA) + needs (is valueB) + result = valueA | int.compare valueB + check (equals result Equal | bool.implies (equals valueA valueB)) + result +isLessThan valueA valueB := + needs (is valueA) + needs (is valueB) + equals (compareTo valueA valueB) Less +isGreaterThan valueA valueB := + needs (is valueA) + needs (is valueB) + equals (compareTo valueA valueB) Greater +isLessThanOrEqualTo valueA valueB := + needs (is valueA) + needs (is valueB) + valueA | isGreaterThan valueB | bool.not +isGreaterThanOrEqualTo valueA valueB := + needs (is valueA) + needs (is valueB) + valueA | isLessThan valueB | bool.not + +isPositive value := + needs (is value) + value | isGreaterThan 0 +isNonPositive value := + needs (is value) + value | isPositive | bool.not +isNegative value := + needs (is value) + value | isLessThan 0 +isNonNegative value := + needs (is value) + value | isNegative | bool.not +absolute value := + needs (is value) + ifElse (isNegative value) { negate value } { value } + +approxEquals a b delta := + needs (is a) + needs (is b) + needs (is delta) + needs (isNonNegative delta) + a | int.subtract b | int.absolute | int.isLessThanOrEqualTo delta + +min valueA valueB := + needs (is valueA) + needs (is valueB) + ifElse + (valueA | isLessThanOrEqualTo valueB) + { valueA } + { valueB } +max valueA valueB := + needs (is valueA) + needs (is valueB) + ifElse + (valueA | isGreaterThanOrEqualTo valueB) + { valueA } + { valueB } +coerceAtLeast value minimum := + needs (is value) + needs (is minimum) + max value minimum +coerceAtMost value maximum := + needs (is value) + needs (is maximum) + min value maximum +coerceIn value minimum maximum := + needs (is value) + needs (is minimum) + needs (is maximum) + needs (minimum | isLessThanOrEqualTo maximum) + value | coerceAtLeast minimum | coerceAtMost maximum + +toText a := + needs (is a) + beforeDot = a | floorToInt | toDebugText + afterDot = run { + tmp = a | int.remainder decimalsPow + ifElse (isNonNegative tmp) { + tmp | int.add decimalsPow | toDebugText | text.removePrefix "1" + } { + tmp | int.subtract decimalsPow | toDebugText | text.removePrefix "-1" + } + } + "{beforeDot}.{afterDot}" diff --git a/packages/Core/int.candy b/packages/Core/int.candy index ca1f0d0b4..390874300 100644 --- a/packages/Core/int.candy +++ b/packages/Core/int.candy @@ -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 := diff --git a/packages/examples/sqrt.candy b/packages/examples/sqrt.candy new file mode 100644 index 000000000..2bd30c97b --- /dev/null +++ b/packages/examples/sqrt.candy @@ -0,0 +1,17 @@ +[equals, fixedDecimal, ifElse, recursive] = use "...Core" + +sqrt x := + needs (fixedDecimal.is x) + needs (fixedDecimal.isNonNegative x) + + recursive (x | fixedDecimal.divide (2 | fixedDecimal.fromInt)) { recurse guess -> + refinedGuess = fixedDecimal.divide + (guess | fixedDecimal.add (x | fixedDecimal.divide guess)) + (2 | fixedDecimal.fromInt) + ifElse (fixedDecimal.approxEquals guess refinedGuess 10) { guess } { recurse refinedGuess } + } + +main _ := + input = 2 + result = input | fixedDecimal.fromInt | sqrt + ✨.print "The root of {input} is {result | fixedDecimal.toText}"