diff --git a/priv/static/squared_away.mjs b/priv/static/squared_away.mjs index ac1e28a..08b8025 100644 --- a/priv/static/squared_away.mjs +++ b/priv/static/squared_away.mjs @@ -1432,6 +1432,14 @@ function min(a, b) { return b; } } +function max(a, b) { + let $ = a > b; + if ($) { + return a; + } else { + return b; + } +} function ceiling2(x) { return ceiling(x); } @@ -1502,7 +1510,7 @@ function min2(a, b) { return b; } } -function max(a, b) { +function max2(a, b) { let $ = a > b; if ($) { return a; @@ -4790,6 +4798,19 @@ function min3(r1, r2) { return r2; } } +function max3(r1, r2) { + let $ = subtract2(r1, r2); + let x = $.numerator; + let y = $.denominator; + let num_neg = isEqual(compare4(x, from2(0)), new Lt()); + let den_neg = isEqual(compare4(y, from2(0)), new Lt()); + let $1 = num_neg === den_neg; + if (!$1) { + return r2; + } else { + return r1; + } +} // build/dev/javascript/squared_away/squared_away/squared_away_lang/parser/expr.mjs var Empty2 = class extends CustomType { @@ -4912,6 +4933,8 @@ var MustBe = class extends CustomType { }; var Minimum = class extends CustomType { }; +var Maximum = class extends CustomType { +}; var Negate = class extends CustomType { }; var Not = class extends CustomType { @@ -4945,8 +4968,10 @@ function binary_to_string(b) { return "-"; } else if (b instanceof MustBe) { return "mustbe"; - } else { + } else if (b instanceof Minimum) { return "min"; + } else { + return "max"; } } function unary_to_string(u) { @@ -5295,8 +5320,10 @@ function describe_binary_op_kind_for_err(bo) { return "Subtraction `-`"; } else if (bo instanceof MustBe) { return "MustBe `mustbe`"; - } else { + } else if (bo instanceof Minimum) { return "Minimum `min`"; + } else { + return "Maximum `max`"; } } function to_renderable_error(te) { @@ -5624,6 +5651,10 @@ function interpret(loop$env, loop$expr) { let a = lhs2.n; let b = rhs2.n; return new Ok(new Integer(min2(a, b))); + } else if (lhs2 instanceof Integer && op instanceof Maximum && rhs2 instanceof Integer) { + let a = lhs2.n; + let b = rhs2.n; + return new Ok(new Integer(max2(a, b))); } else if (lhs2 instanceof FloatingPointNumber && op instanceof Add && rhs2 instanceof FloatingPointNumber) { let a = lhs2.f; let b = rhs2.f; @@ -5668,6 +5699,10 @@ function interpret(loop$env, loop$expr) { let a = lhs2.f; let b = rhs2.f; return new Ok(new FloatingPointNumber(min(a, b))); + } else if (lhs2 instanceof FloatingPointNumber && op instanceof Maximum && rhs2 instanceof FloatingPointNumber) { + let a = lhs2.f; + let b = rhs2.f; + return new Ok(new FloatingPointNumber(max(a, b))); } else if (lhs2 instanceof FloatingPointNumber && op instanceof Power && rhs2 instanceof FloatingPointNumber) { let base = lhs2.f; let exponent = rhs2.f; @@ -5697,7 +5732,7 @@ function interpret(loop$env, loop$expr) { throw makeError( "let_assert", "squared_away/squared_away_lang/interpreter", - 161, + 165, "", "Pattern match failed, no pattern matched the value.", { value: $ } @@ -5733,7 +5768,7 @@ function interpret(loop$env, loop$expr) { throw makeError( "let_assert", "squared_away/squared_away_lang/interpreter", - 183, + 187, "", "Pattern match failed, no pattern matched the value.", { value: $ } @@ -5787,6 +5822,10 @@ function interpret(loop$env, loop$expr) { let d = lhs2.cents; let p2 = rhs2.cents; return new Ok(new Usd(min3(d, p2))); + } else if (lhs2 instanceof Usd && op instanceof Maximum && rhs2 instanceof Usd) { + let d = lhs2.cents; + let p2 = rhs2.cents; + return new Ok(new Usd(max3(d, p2))); } else if (lhs2 instanceof Percent && op instanceof Multiply && rhs2 instanceof Usd) { let p2 = lhs2.percent; let d = rhs2.cents; @@ -5811,6 +5850,10 @@ function interpret(loop$env, loop$expr) { let p1 = lhs2.percent; let p2 = rhs2.percent; return new Ok(new Percent(min3(p1, p2))); + } else if (lhs2 instanceof Percent && op instanceof Maximum && rhs2 instanceof Percent) { + let p1 = lhs2.percent; + let p2 = rhs2.percent; + return new Ok(new Percent(max3(p1, p2))); } else if (lhs2 instanceof Percent && op instanceof Power && rhs2 instanceof Percent) { let base = lhs2.percent; let exponent = rhs2.percent; @@ -5891,7 +5934,7 @@ function interpret(loop$env, loop$expr) { throw makeError( "let_assert", "squared_away/squared_away_lang/interpreter", - 303, + 313, "", "Pattern match failed, no pattern matched the value.", { value: $ } @@ -5982,7 +6025,7 @@ function interpret(loop$env, loop$expr) { throw makeError( "let_assert", "squared_away/squared_away_lang/interpreter", - 355, + 365, "", "Pattern match failed, no pattern matched the value.", { value: v } @@ -6003,7 +6046,7 @@ function interpret(loop$env, loop$expr) { throw makeError( "let_assert", "squared_away/squared_away_lang/interpreter", - 363, + 373, "", "Pattern match failed, no pattern matched the value.", { value: v } @@ -6024,7 +6067,7 @@ function interpret(loop$env, loop$expr) { throw makeError( "let_assert", "squared_away/squared_away_lang/interpreter", - 371, + 381, "", "Pattern match failed, no pattern matched the value.", { value: v } @@ -6094,7 +6137,7 @@ function interpret(loop$env, loop$expr) { throw makeError( "let_assert", "squared_away/squared_away_lang/interpreter", - 412, + 422, "", "Pattern match failed, no pattern matched the value.", { value: v } @@ -6120,7 +6163,7 @@ function interpret(loop$env, loop$expr) { throw makeError( "let_assert", "squared_away/squared_away_lang/interpreter", - 423, + 433, "", "Pattern match failed, no pattern matched the value.", { value: v } @@ -6141,7 +6184,7 @@ function interpret(loop$env, loop$expr) { throw makeError( "let_assert", "squared_away/squared_away_lang/interpreter", - 433, + 443, "", "Pattern match failed, no pattern matched the value.", { value: v } @@ -6260,6 +6303,8 @@ var MustBe2 = class extends CustomType { }; var Minimum2 = class extends CustomType { }; +var Maximum2 = class extends CustomType { +}; var StringLiteral3 = class extends CustomType { constructor(content_without_quotes) { super(); @@ -6698,6 +6743,33 @@ function try_parse_binary_ops(tokens) { ); } ); + } else if (tokens.atLeastLength(1) && tokens.head instanceof Maximum2) { + let rest = tokens.tail; + return try$( + do_parse2(rest), + (_use0) => { + let rhs = _use0[0]; + let rest$1 = _use0[1]; + return guard( + isEqual(rhs, new Empty2()), + new Error( + new ParseError( + "No item on right hand side of binary operation." + ) + ), + () => { + return new Ok( + [ + (_capture) => { + return new BinaryOp(_capture, new Maximum(), rhs); + }, + rest$1 + ] + ); + } + ); + } + ); } else { return new Error(new ParseError("Not a binary operation")); } @@ -7225,6 +7297,10 @@ function do_scan(loop$src, loop$acc) { let rest = src.slice(3); loop$src = trim_left2(rest); loop$acc = prepend(new Minimum2(), acc); + } else if (src.startsWith("max")) { + let rest = src.slice(3); + loop$src = trim_left2(rest); + loop$acc = prepend(new Maximum2(), acc); } else if (src.startsWith("&&")) { let rest = src.slice(2); loop$src = trim_left2(rest); @@ -8011,6 +8087,10 @@ function typecheck(env, expr) { return new Ok( new BinaryOp2(new TFloat(), lhs2, op, rhs2) ); + } else if ($ instanceof TFloat && op instanceof Maximum && $1 instanceof TFloat) { + return new Ok( + new BinaryOp2(new TFloat(), lhs2, op, rhs2) + ); } else if ($ instanceof TFloat && op instanceof Power && $1 instanceof TInt) { return new Ok( new BinaryOp2(new TFloat(), lhs2, op, rhs2) @@ -8043,6 +8123,10 @@ function typecheck(env, expr) { return new Ok( new BinaryOp2(new TUsd(), lhs2, op, rhs2) ); + } else if ($ instanceof TUsd && op instanceof Maximum && $1 instanceof TUsd) { + return new Ok( + new BinaryOp2(new TUsd(), lhs2, op, rhs2) + ); } else if ($ instanceof TUsd && op instanceof Multiply && $1 instanceof TUsd) { return new Error( new TypeError2( @@ -8077,6 +8161,10 @@ function typecheck(env, expr) { return new Ok( new BinaryOp2(new TPercent(), lhs2, op, rhs2) ); + } else if ($ instanceof TPercent && op instanceof Maximum && $1 instanceof TPercent) { + return new Ok( + new BinaryOp2(new TPercent(), lhs2, op, rhs2) + ); } else if ($ instanceof TBool && op instanceof And && $1 instanceof TBool) { return new Ok( new BinaryOp2(new TBool(), lhs2, op, rhs2) @@ -8109,6 +8197,10 @@ function typecheck(env, expr) { return new Ok( new BinaryOp2(new TInt(), lhs2, op, rhs2) ); + } else if ($ instanceof TInt && op instanceof Maximum && $1 instanceof TInt) { + return new Ok( + new BinaryOp2(new TInt(), lhs2, op, rhs2) + ); } else if ($ instanceof TInt && op instanceof Multiply && $1 instanceof TUsd) { return new Ok( new BinaryOp2(new TUsd(), lhs2, op, rhs2) @@ -9156,7 +9248,7 @@ function recalculate_col_width(model, col2) { ); } })(); - let _pipe$1 = fold2(_pipe, min_cell_size_ch - 1, max); + let _pipe$1 = fold2(_pipe, min_cell_size_ch - 1, max2); return add(_pipe$1, 1); } function view(model) { diff --git a/src/squared_away/squared_away_lang/interpreter.gleam b/src/squared_away/squared_away_lang/interpreter.gleam index 816d6af..dfb0e59 100644 --- a/src/squared_away/squared_away_lang/interpreter.gleam +++ b/src/squared_away/squared_away_lang/interpreter.gleam @@ -92,6 +92,8 @@ pub fn interpret( Ok(value.Boolean(a <= b)) value.Integer(a), expr.Minimum, value.Integer(b) -> Ok(value.Integer(int.min(a, b))) + value.Integer(a), expr.Maximum, value.Integer(b) -> + Ok(value.Integer(int.max(a, b))) // Float x Float value.FloatingPointNumber(a), expr.Add, value.FloatingPointNumber(b) -> @@ -132,6 +134,8 @@ pub fn interpret( -> Ok(value.Boolean(a <=. b)) value.FloatingPointNumber(a), expr.Minimum, value.FloatingPointNumber(b) -> Ok(value.FloatingPointNumber(float.min(a, b))) + value.FloatingPointNumber(a), expr.Maximum, value.FloatingPointNumber(b) + -> Ok(value.FloatingPointNumber(float.max(a, b))) value.FloatingPointNumber(base), expr.Power, @@ -217,6 +221,9 @@ pub fn interpret( value.Usd(d), expr.Minimum, value.Usd(p) -> { Ok(value.Usd(rational.min(d, p))) } + value.Usd(d), expr.Maximum, value.Usd(p) -> { + Ok(value.Usd(rational.max(d, p))) + } // Percent x Usd value.Percent(p), expr.Multiply, value.Usd(d) -> { @@ -241,6 +248,9 @@ pub fn interpret( value.Percent(p1), expr.Minimum, value.Percent(p2) -> { Ok(value.Percent(rational.min(p1, p2))) } + value.Percent(p1), expr.Maximum, value.Percent(p2) -> { + Ok(value.Percent(rational.max(p1, p2))) + } value.Percent(base), expr.Power, value.Percent(exponent) -> { let fractional_exponent = !rational.is_whole_number(exponent) use <- bool.guard( diff --git a/src/squared_away/squared_away_lang/parser.gleam b/src/squared_away/squared_away_lang/parser.gleam index f5b4648..58dc38c 100644 --- a/src/squared_away/squared_away_lang/parser.gleam +++ b/src/squared_away/squared_away_lang/parser.gleam @@ -279,6 +279,16 @@ fn try_parse_binary_ops( ) Ok(#(expr.BinaryOp(_, expr.Minimum, rhs), rest)) } + [token.Maximum, ..rest] -> { + use #(rhs, rest) <- result.try(do_parse(rest)) + use <- bool.guard( + rhs == expr.Empty, + Error(parse_error.ParseError( + "No item on right hand side of binary operation.", + )), + ) + Ok(#(expr.BinaryOp(_, expr.Maximum, rhs), rest)) + } _ -> Error(parse_error.ParseError("Not a binary operation")) } } diff --git a/src/squared_away/squared_away_lang/parser/expr.gleam b/src/squared_away/squared_away_lang/parser/expr.gleam index e5e0a44..fd74dec 100644 --- a/src/squared_away/squared_away_lang/parser/expr.gleam +++ b/src/squared_away/squared_away_lang/parser/expr.gleam @@ -36,6 +36,7 @@ pub type BinaryOpKind { Or MustBe Minimum + Maximum } pub fn binary_to_string(b: BinaryOpKind) -> String { @@ -55,6 +56,7 @@ pub fn binary_to_string(b: BinaryOpKind) -> String { Subtract -> "-" MustBe -> "mustbe" Minimum -> "min" + Maximum -> "max" } } diff --git a/src/squared_away/squared_away_lang/scanner.gleam b/src/squared_away/squared_away_lang/scanner.gleam index 28d9d75..2df114d 100644 --- a/src/squared_away/squared_away_lang/scanner.gleam +++ b/src/squared_away/squared_away_lang/scanner.gleam @@ -128,6 +128,7 @@ fn do_scan( "avg" <> rest -> do_scan(string.trim_left(rest), [token.BuiltinAvg(option.None), ..acc]) "min" <> rest -> do_scan(string.trim_left(rest), [token.Minimum, ..acc]) + "max" <> rest -> do_scan(string.trim_left(rest), [token.Maximum, ..acc]) "&&" <> rest -> do_scan(string.trim_left(rest), [token.And, ..acc]) "||" <> rest -> do_scan(string.trim_left(rest), [token.Or, ..acc]) diff --git a/src/squared_away/squared_away_lang/scanner/token.gleam b/src/squared_away/squared_away_lang/scanner/token.gleam index d5a4e47..9c88801 100644 --- a/src/squared_away/squared_away_lang/scanner/token.gleam +++ b/src/squared_away/squared_away_lang/scanner/token.gleam @@ -56,6 +56,7 @@ pub type Token { BuiltinAvg(key: option.Option(grid.GridKey)) MustBe Minimum + Maximum UpAnglePlus diff --git a/src/squared_away/squared_away_lang/typechecker.gleam b/src/squared_away/squared_away_lang/typechecker.gleam index ed0f67d..f6e5034 100644 --- a/src/squared_away/squared_away_lang/typechecker.gleam +++ b/src/squared_away/squared_away_lang/typechecker.gleam @@ -349,6 +349,8 @@ pub fn typecheck( Ok(typed_expr.BinaryOp(type_: typ.TFloat, lhs:, op:, rhs:)) typ.TFloat, expr.Minimum, typ.TFloat -> Ok(typed_expr.BinaryOp(type_: typ.TFloat, lhs:, op:, rhs:)) + typ.TFloat, expr.Maximum, typ.TFloat -> + Ok(typed_expr.BinaryOp(type_: typ.TFloat, lhs:, op:, rhs:)) // USD x Float (None for now) // Percent x Float (None for now) @@ -380,6 +382,8 @@ pub fn typecheck( Ok(typed_expr.BinaryOp(type_: typ.TPercent, lhs:, op:, rhs:)) typ.TUsd, expr.Minimum, typ.TUsd -> Ok(typed_expr.BinaryOp(type_: typ.TUsd, lhs:, op:, rhs:)) + typ.TUsd, expr.Maximum, typ.TUsd -> + Ok(typed_expr.BinaryOp(type_: typ.TUsd, lhs:, op:, rhs:)) typ.TUsd, expr.Multiply, typ.TUsd -> Error(error.TypeError(type_error.CannotMultiplyUsdByUsd(lhs:, rhs:))) @@ -405,6 +409,8 @@ pub fn typecheck( Ok(typed_expr.BinaryOp(type_: typ.TPercent, lhs:, op:, rhs:)) typ.TPercent, expr.Minimum, typ.TPercent -> Ok(typed_expr.BinaryOp(type_: typ.TPercent, lhs:, op:, rhs:)) + typ.TPercent, expr.Maximum, typ.TPercent -> + Ok(typed_expr.BinaryOp(type_: typ.TPercent, lhs:, op:, rhs:)) // String x Percent (None for now) // Boolean x Percent (None for now) @@ -436,6 +442,8 @@ pub fn typecheck( Ok(typed_expr.BinaryOp(type_: typ.TInt, lhs:, op:, rhs:)) typ.TInt, expr.Minimum, typ.TInt -> Ok(typed_expr.BinaryOp(type_: typ.TInt, lhs:, op:, rhs:)) + typ.TInt, expr.Maximum, typ.TInt -> + Ok(typed_expr.BinaryOp(type_: typ.TInt, lhs:, op:, rhs:)) // Int x Usd typ.TInt, expr.Multiply, typ.TUsd -> diff --git a/src/squared_away/squared_away_lang/typechecker/type_error.gleam b/src/squared_away/squared_away_lang/typechecker/type_error.gleam index d7cb4b8..7b7f475 100644 --- a/src/squared_away/squared_away_lang/typechecker/type_error.gleam +++ b/src/squared_away/squared_away_lang/typechecker/type_error.gleam @@ -63,5 +63,6 @@ pub fn describe_binary_op_kind_for_err(bo: expr.BinaryOpKind) -> String { expr.Subtract -> "Subtraction `-`" expr.MustBe -> "MustBe `mustbe`" expr.Minimum -> "Minimum `min`" + expr.Maximum -> "Maximum `max`" } } diff --git a/src/squared_away/squared_away_lang/util/rational.gleam b/src/squared_away/squared_away_lang/util/rational.gleam index e36dfdd..643f185 100644 --- a/src/squared_away/squared_away_lang/util/rational.gleam +++ b/src/squared_away/squared_away_lang/util/rational.gleam @@ -146,6 +146,16 @@ pub fn min(r1: Rat, r2: Rat) -> Rat { } } +pub fn max(r1: Rat, r2: Rat) -> Rat { + let Rat(x, y) = subtract(r1, r2) + let num_neg = bigi.compare(x, bigi.from_int(0)) == order.Lt + let den_neg = bigi.compare(y, bigi.from_int(0)) == order.Lt + case num_neg == den_neg { + False -> r2 + True -> r1 + } +} + pub fn to_string(rat: Rat, precision: Int, with_commas: Bool) -> String { let Rat(n, d) = rat let whole = bigi.to_string(bigi.divide(n, d))