From a0eb67656d40e18f792677439955537750fd06a4 Mon Sep 17 00:00:00 2001 From: Benjamin Date: Thu, 29 Aug 2024 00:05:31 -0500 Subject: [PATCH] extract hella modules --- priv/static/squared_away.mjs | 2279 +++++++++-------- src/squared_away.gleam | 23 +- src/squared_away/lang.gleam | 65 +- src/squared_away/lang/error.gleam | 11 + src/squared_away/lang/interpreter.gleam | 242 +- .../lang/interpreter/runtime_error.gleam | 3 + src/squared_away/lang/interpreter/value.gleam | 21 + src/squared_away/lang/parser.gleam | 175 +- src/squared_away/lang/parser/expr.gleam | 32 + .../lang/parser/parse_error.gleam | 8 + src/squared_away/lang/scanner.gleam | 116 +- .../lang/scanner/scan_error.gleam | 9 + src/squared_away/lang/scanner/token.gleam | 48 + src/squared_away/lang/typechecker.gleam | 175 +- src/squared_away/lang/typechecker/typ.gleam | 17 + .../lang/typechecker/type_error.gleam | 3 + .../lang/typechecker/typed_expr.gleam | 19 + test/squared_away_test.gleam | 137 +- 18 files changed, 1788 insertions(+), 1595 deletions(-) create mode 100644 src/squared_away/lang/error.gleam create mode 100644 src/squared_away/lang/interpreter/runtime_error.gleam create mode 100644 src/squared_away/lang/interpreter/value.gleam create mode 100644 src/squared_away/lang/parser/expr.gleam create mode 100644 src/squared_away/lang/parser/parse_error.gleam create mode 100644 src/squared_away/lang/scanner/scan_error.gleam create mode 100644 src/squared_away/lang/scanner/token.gleam create mode 100644 src/squared_away/lang/typechecker/typ.gleam create mode 100644 src/squared_away/lang/typechecker/type_error.gleam create mode 100644 src/squared_away/lang/typechecker/typed_expr.gleam diff --git a/priv/static/squared_away.mjs b/priv/static/squared_away.mjs index 1377604..7f0f41b 100644 --- a/priv/static/squared_away.mjs +++ b/priv/static/squared_away.mjs @@ -425,15 +425,6 @@ function map_error(result, fun) { return new Error(fun(error)); } } -function flatten(result) { - if (result.isOk()) { - let x = result[0]; - return x; - } else { - let error = result[0]; - return new Error(error); - } -} function try$(result, fun) { if (result.isOk()) { let x = result[0]; @@ -443,19 +434,6 @@ function try$(result, fun) { return new Error(e); } } -function unwrap(result, default$) { - if (result.isOk()) { - let v = result[0]; - return v; - } else { - return default$; - } -} -function nil_error(result) { - return map_error(result, (_) => { - return void 0; - }); -} function replace_error(result, error) { if (result.isOk()) { let x = result[0]; @@ -2218,32 +2196,88 @@ function on_input(msg) { ); } -// build/dev/javascript/squared_away/squared_away/lang/scanner.mjs -var Plus = class extends CustomType { +// build/dev/javascript/squared_away/squared_away/lang/parser/parse_error.mjs +var ParseError = class extends CustomType { + constructor(context) { + super(); + this.context = context; + } }; -var Minus = class extends CustomType { + +// build/dev/javascript/squared_away/squared_away/lang/scanner/scan_error.mjs +var ScanError = class extends CustomType { }; -var Star = class extends CustomType { + +// build/dev/javascript/squared_away/squared_away/lang/typechecker/type_error.mjs +var TypeError = class extends CustomType { + constructor(context) { + super(); + this.context = context; + } }; -var Div = class extends CustomType { + +// build/dev/javascript/squared_away/squared_away/lang/error.mjs +var ScanError2 = class extends CustomType { + constructor(x0) { + super(); + this[0] = x0; + } }; -var StarStar = class extends CustomType { +var ParseError2 = class extends CustomType { + constructor(x0) { + super(); + this[0] = x0; + } }; -var Equal = class extends CustomType { +var TypeError2 = class extends CustomType { + constructor(x0) { + super(); + this[0] = x0; + } }; -var EqualEqual = class extends CustomType { + +// build/dev/javascript/squared_away/squared_away/lang/interpreter/value.mjs +var Empty2 = class extends CustomType { }; -var BangEqual = class extends CustomType { +var Text2 = class extends CustomType { + constructor(inner) { + super(); + this.inner = inner; + } }; -var Bang = class extends CustomType { +var Integer = class extends CustomType { + constructor(n) { + super(); + this.n = n; + } }; -var Less = class extends CustomType { +var FloatingPointNumber = class extends CustomType { + constructor(f) { + super(); + this.f = f; + } }; -var LessEqual = class extends CustomType { +var Boolean = class extends CustomType { + constructor(b) { + super(); + this.b = b; + } }; -var Greater = class extends CustomType { + +// build/dev/javascript/squared_away/squared_away/lang/parser/expr.mjs +var Empty3 = class extends CustomType { }; -var GreaterEqual = class extends CustomType { +var FloatLiteral = class extends CustomType { + constructor(f) { + super(); + this.f = f; + } +}; +var StringLiteral = class extends CustomType { + constructor(txt) { + super(); + this.txt = txt; + } }; var IntegerLiteral = class extends CustomType { constructor(n) { @@ -2251,503 +2285,442 @@ var IntegerLiteral = class extends CustomType { this.n = n; } }; -var FloatLiteral = class extends CustomType { - constructor(f) { +var CellReference = class extends CustomType { + constructor(key) { super(); - this.f = f; + this.key = key; } }; -var TrueToken = class extends CustomType { +var BooleanLiteral = class extends CustomType { + constructor(val) { + super(); + this.val = val; + } }; -var FalseToken = class extends CustomType { +var UnaryOp = class extends CustomType { + constructor(op, expr) { + super(); + this.op = op; + this.expr = expr; + } +}; +var BinaryOp = class extends CustomType { + constructor(lhs, op, rhs) { + super(); + this.lhs = lhs; + this.op = op; + this.rhs = rhs; + } +}; +var Group = class extends CustomType { + constructor(inner) { + super(); + this.inner = inner; + } +}; +var Add = class extends CustomType { +}; +var Subtract = class extends CustomType { +}; +var Multiply = class extends CustomType { +}; +var Divide = class extends CustomType { +}; +var Power = class extends CustomType { +}; +var EqualCheck = class extends CustomType { +}; +var NotEqualCheck = class extends CustomType { +}; +var LessThanCheck = class extends CustomType { +}; +var LessThanOrEqualCheck = class extends CustomType { +}; +var GreaterThanCheck = class extends CustomType { +}; +var GreaterThanOrEqualCheck = class extends CustomType { }; var And = class extends CustomType { }; var Or = class extends CustomType { }; -var LParen = class extends CustomType { +var Negate = class extends CustomType { }; -var RParen = class extends CustomType { +var Not = class extends CustomType { }; -var CellReference = class extends CustomType { - constructor(key) { + +// build/dev/javascript/squared_away/squared_away/lang/typechecker/typ.mjs +var TNil = class extends CustomType { +}; +var TFloat = class extends CustomType { +}; +var TString = class extends CustomType { +}; +var TInt = class extends CustomType { +}; +var TBool = class extends CustomType { +}; + +// build/dev/javascript/squared_away/squared_away/lang/typechecker/typed_expr.mjs +var Empty4 = class extends CustomType { + constructor(type_) { + super(); + this.type_ = type_; + } +}; +var FloatLiteral2 = class extends CustomType { + constructor(type_, f) { + super(); + this.type_ = type_; + this.f = f; + } +}; +var StringLiteral2 = class extends CustomType { + constructor(type_, txt) { + super(); + this.type_ = type_; + this.txt = txt; + } +}; +var IntegerLiteral2 = class extends CustomType { + constructor(type_, n) { + super(); + this.type_ = type_; + this.n = n; + } +}; +var CellReference2 = class extends CustomType { + constructor(type_, key) { super(); + this.type_ = type_; this.key = key; } }; -var StringLiteral = class extends CustomType { - constructor(x0) { +var BooleanLiteral2 = class extends CustomType { + constructor(type_, b) { super(); - this[0] = x0; + this.type_ = type_; + this.b = b; } }; -var ScanError = class extends CustomType { +var UnaryOp2 = class extends CustomType { + constructor(type_, op, expr) { + super(); + this.type_ = type_; + this.op = op; + this.expr = expr; + } }; -function parse_integer(loop$src, loop$acc) { +var BinaryOp2 = class extends CustomType { + constructor(type_, lhs, op, rhs) { + super(); + this.type_ = type_; + this.lhs = lhs; + this.op = op; + this.rhs = rhs; + } +}; +var Group2 = class extends CustomType { + constructor(type_, expr) { + super(); + this.type_ = type_; + this.expr = expr; + } +}; + +// build/dev/javascript/squared_away/squared_away/lang/interpreter.mjs +function interpret(loop$env, loop$expr) { while (true) { - let src = loop$src; - let acc = loop$acc; - if (src.startsWith("1")) { - let rest = src.slice(1); - let x = "1"; - loop$src = rest; - loop$acc = acc + x; - } else if (src.startsWith("2")) { - let rest = src.slice(1); - let x = "2"; - loop$src = rest; - loop$acc = acc + x; - } else if (src.startsWith("3")) { - let rest = src.slice(1); - let x = "3"; - loop$src = rest; - loop$acc = acc + x; - } else if (src.startsWith("4")) { - let rest = src.slice(1); - let x = "4"; - loop$src = rest; - loop$acc = acc + x; - } else if (src.startsWith("5")) { - let rest = src.slice(1); - let x = "5"; - loop$src = rest; - loop$acc = acc + x; - } else if (src.startsWith("6")) { - let rest = src.slice(1); - let x = "6"; - loop$src = rest; - loop$acc = acc + x; - } else if (src.startsWith("7")) { - let rest = src.slice(1); - let x = "7"; - loop$src = rest; - loop$acc = acc + x; - } else if (src.startsWith("8")) { - let rest = src.slice(1); - let x = "8"; - loop$src = rest; - loop$acc = acc + x; - } else if (src.startsWith("9")) { - let rest = src.slice(1); - let x = "9"; - loop$src = rest; - loop$acc = acc + x; - } else if (src.startsWith("0")) { - let rest = src.slice(1); - let x = "0"; - loop$src = rest; - loop$acc = acc + x; - } else { - let _pipe = parse2(acc); - return map2(_pipe, (n) => { - return [n, src]; - }); - } - } -} -function parse_cell_ref(loop$src, loop$acc) { - while (true) { - let src = loop$src; - let acc = loop$acc; - if (src.startsWith("A")) { - let rest = src.slice(1); - let l = "A"; - loop$src = rest; - loop$acc = acc + l; - } else if (src.startsWith("B")) { - let rest = src.slice(1); - let l = "B"; - loop$src = rest; - loop$acc = acc + l; - } else if (src.startsWith("C")) { - let rest = src.slice(1); - let l = "C"; - loop$src = rest; - loop$acc = acc + l; - } else if (src.startsWith("D")) { - let rest = src.slice(1); - let l = "D"; - loop$src = rest; - loop$acc = acc + l; - } else if (src.startsWith("E")) { - let rest = src.slice(1); - let l = "E"; - loop$src = rest; - loop$acc = acc + l; - } else if (src.startsWith("F")) { - let rest = src.slice(1); - let l = "F"; - loop$src = rest; - loop$acc = acc + l; - } else if (src.startsWith("G")) { - let rest = src.slice(1); - let l = "G"; - loop$src = rest; - loop$acc = acc + l; - } else if (src.startsWith("H")) { - let rest = src.slice(1); - let l = "H"; - loop$src = rest; - loop$acc = acc + l; - } else if (src.startsWith("I")) { - let rest = src.slice(1); - let l = "I"; - loop$src = rest; - loop$acc = acc + l; - } else if (src.startsWith("J")) { - let rest = src.slice(1); - let l = "J"; - loop$src = rest; - loop$acc = acc + l; - } else if (src.startsWith("K")) { - let rest = src.slice(1); - let l = "K"; - loop$src = rest; - loop$acc = acc + l; - } else if (src.startsWith("L")) { - let rest = src.slice(1); - let l = "L"; - loop$src = rest; - loop$acc = acc + l; - } else if (src.startsWith("M")) { - let rest = src.slice(1); - let l = "M"; - loop$src = rest; - loop$acc = acc + l; - } else if (src.startsWith("N")) { - let rest = src.slice(1); - let l = "N"; - loop$src = rest; - loop$acc = acc + l; - } else if (src.startsWith("O")) { - let rest = src.slice(1); - let l = "O"; - loop$src = rest; - loop$acc = acc + l; - } else if (src.startsWith("P")) { - let rest = src.slice(1); - let l = "P"; - loop$src = rest; - loop$acc = acc + l; - } else if (src.startsWith("Q")) { - let rest = src.slice(1); - let l = "Q"; - loop$src = rest; - loop$acc = acc + l; - } else if (src.startsWith("R")) { - let rest = src.slice(1); - let l = "R"; - loop$src = rest; - loop$acc = acc + l; - } else if (src.startsWith("S")) { - let rest = src.slice(1); - let l = "S"; - loop$src = rest; - loop$acc = acc + l; - } else if (src.startsWith("T")) { - let rest = src.slice(1); - let l = "T"; - loop$src = rest; - loop$acc = acc + l; - } else if (src.startsWith("U")) { - let rest = src.slice(1); - let l = "U"; - loop$src = rest; - loop$acc = acc + l; - } else if (src.startsWith("V")) { - let rest = src.slice(1); - let l = "V"; - loop$src = rest; - loop$acc = acc + l; - } else if (src.startsWith("W")) { - let rest = src.slice(1); - let l = "W"; - loop$src = rest; - loop$acc = acc + l; - } else if (src.startsWith("X")) { - let rest = src.slice(1); - let l = "X"; - loop$src = rest; - loop$acc = acc + l; - } else if (src.startsWith("Y")) { - let rest = src.slice(1); - let l = "Y"; - loop$src = rest; - loop$acc = acc + l; - } else if (src.startsWith("Z")) { - let rest = src.slice(1); - let l = "Z"; - loop$src = rest; - loop$acc = acc + l; - } else { - if (acc === "") { - return new Error(void 0); - } else { - return try$( - parse_integer(src, ""), - (_use0) => { - let n = _use0[0]; - let rest = _use0[1]; - return new Ok([acc + to_string2(n), rest]); - } + let env = loop$env; + let expr = loop$expr; + if (expr instanceof Empty4) { + return new Ok(new Empty2()); + } else if (expr instanceof Group2) { + let expr$1 = expr.expr; + loop$env = env; + loop$expr = expr$1; + } else if (expr instanceof StringLiteral2) { + let txt = expr.txt; + return new Ok(new Text2(txt)); + } else if (expr instanceof BooleanLiteral2) { + let b = expr.b; + return new Ok(new Boolean(b)); + } else if (expr instanceof IntegerLiteral2) { + let n = expr.n; + return new Ok(new Integer(n)); + } else if (expr instanceof FloatLiteral2) { + let f = expr.f; + return new Ok(new FloatingPointNumber(f)); + } else if (expr instanceof CellReference2) { + let cell_ref = expr.key; + let $ = get(env, cell_ref); + if (!$.isOk()) { + throw makeError( + "assignment_no_match", + "squared_away/lang/interpreter", + 22, + "interpret", + "Assignment pattern did not match", + { value: $ } ); } - } - } -} -function do_scan(loop$src, loop$acc) { - while (true) { - let src = loop$src; - let acc = loop$acc; - if (src === "") { - return new Ok( - (() => { - let _pipe = acc; - return reverse(_pipe); - })() + let maybe_expr = $[0]; + if (!maybe_expr.isOk()) { + let e = maybe_expr[0]; + return new Error(e); + } else { + let expr$1 = maybe_expr[0]; + loop$env = env; + loop$expr = expr$1; + } + } else if (expr instanceof UnaryOp2) { + let op = expr.op; + let expr$1 = expr.expr; + return try$( + interpret(env, expr$1), + (value2) => { + if (op instanceof Negate && value2 instanceof Integer) { + let n = value2.n; + return new Ok(new Integer(-n)); + } else if (op instanceof Negate && value2 instanceof FloatingPointNumber) { + let f = value2.f; + return new Ok(new FloatingPointNumber(negate(f))); + } else if (op instanceof Not && value2 instanceof Boolean) { + let b = value2.b; + return new Ok(new Boolean(!b)); + } else { + throw makeError( + "panic", + "squared_away/lang/interpreter", + 36, + "", + "These should be the only options if the typechecker is working", + {} + ); + } + } ); - } else if (src.startsWith("TRUE")) { - let rest = src.slice(4); - loop$src = trim_left2(rest); - loop$acc = prepend(new TrueToken(), acc); - } else if (src.startsWith("FALSE")) { - let rest = src.slice(5); - loop$src = trim_left2(rest); - loop$acc = prepend(new FalseToken(), acc); - } else if (src.startsWith("&&")) { - let rest = src.slice(2); - loop$src = trim_left2(rest); - loop$acc = prepend(new And(), acc); - } else if (src.startsWith("||")) { - let rest = src.slice(2); - loop$src = trim_left2(rest); - loop$acc = prepend(new Or(), acc); - } else if (src.startsWith("**")) { - let rest = src.slice(2); - loop$src = trim_left2(rest); - loop$acc = prepend(new StarStar(), acc); - } else if (src.startsWith("==")) { - let rest = src.slice(2); - loop$src = trim_left2(rest); - loop$acc = prepend(new EqualEqual(), acc); - } else if (src.startsWith("!=")) { - let rest = src.slice(2); - loop$src = trim_left2(rest); - loop$acc = prepend(new BangEqual(), acc); - } else if (src.startsWith("<=")) { - let rest = src.slice(2); - loop$src = trim_left2(rest); - loop$acc = prepend(new LessEqual(), acc); - } else if (src.startsWith(">=")) { - let rest = src.slice(2); - loop$src = trim_left2(rest); - loop$acc = prepend(new GreaterEqual(), acc); - } else if (src.startsWith("+")) { - let rest = src.slice(1); - loop$src = trim_left2(rest); - loop$acc = prepend(new Plus(), acc); - } else if (src.startsWith("-")) { - let rest = src.slice(1); - loop$src = trim_left2(rest); - loop$acc = prepend(new Minus(), acc); - } else if (src.startsWith("*")) { - let rest = src.slice(1); - loop$src = trim_left2(rest); - loop$acc = prepend(new Star(), acc); - } else if (src.startsWith("/")) { - let rest = src.slice(1); - loop$src = trim_left2(rest); - loop$acc = prepend(new Div(), acc); - } else if (src.startsWith("=")) { - let rest = src.slice(1); - loop$src = trim_left2(rest); - loop$acc = prepend(new Equal(), acc); - } else if (src.startsWith("!")) { - let rest = src.slice(1); - loop$src = trim_left2(rest); - loop$acc = prepend(new Bang(), acc); - } else if (src.startsWith("<")) { - let rest = src.slice(1); - loop$src = trim_left2(rest); - loop$acc = prepend(new Less(), acc); - } else if (src.startsWith(">")) { - let rest = src.slice(1); - loop$src = trim_left2(rest); - loop$acc = prepend(new Greater(), acc); - } else if (src.startsWith("(")) { - let rest = src.slice(1); - loop$src = trim_left2(rest); - loop$acc = prepend(new LParen(), acc); - } else if (src.startsWith(")")) { - let rest = src.slice(1); - loop$src = trim_left2(rest); - loop$acc = prepend(new RParen(), acc); } else { - let $ = parse_integer(src, ""); - if ($.isOk()) { - let n = $[0][0]; - let rest = $[0][1]; - if (rest.startsWith(".")) { - let rest$1 = rest.slice(1); + let lhs = expr.lhs; + let op = expr.op; + let rhs = expr.rhs; + return try$( + interpret(env, lhs), + (lhs2) => { return try$( - (() => { - let _pipe = parse_integer(rest$1, ""); - return replace_error(_pipe, new ScanError()); - })(), - (_use0) => { - let m = _use0[0]; - let rest$2 = _use0[1]; - let $1 = parse( - to_string2(n) + "." + to_string2(m) - ); - if (!$1.isOk()) { + interpret(env, rhs), + (rhs2) => { + if (lhs2 instanceof Integer && op instanceof Add && rhs2 instanceof Integer) { + let a = lhs2.n; + let b = rhs2.n; + return new Ok(new Integer(a + b)); + } else if (lhs2 instanceof Integer && op instanceof Subtract && rhs2 instanceof Integer) { + let a = lhs2.n; + let b = rhs2.n; + return new Ok(new Integer(a - b)); + } else if (lhs2 instanceof Integer && op instanceof Multiply && rhs2 instanceof Integer) { + let a = lhs2.n; + let b = rhs2.n; + return new Ok(new Integer(a * b)); + } else if (lhs2 instanceof Integer && op instanceof Divide && rhs2 instanceof Integer) { + let a = lhs2.n; + let b = rhs2.n; + return new Ok(new Integer(divideInt(a, b))); + } else if (lhs2 instanceof Integer && op instanceof EqualCheck && rhs2 instanceof Integer) { + let a = lhs2.n; + let b = rhs2.n; + return new Ok(new Boolean(a === b)); + } else if (lhs2 instanceof Integer && op instanceof NotEqualCheck && rhs2 instanceof Integer) { + let a = lhs2.n; + let b = rhs2.n; + return new Ok(new Boolean(a !== b)); + } else if (lhs2 instanceof Integer && op instanceof GreaterThanCheck && rhs2 instanceof Integer) { + let a = lhs2.n; + let b = rhs2.n; + return new Ok(new Boolean(a > b)); + } else if (lhs2 instanceof Integer && op instanceof GreaterThanOrEqualCheck && rhs2 instanceof Integer) { + let a = lhs2.n; + let b = rhs2.n; + return new Ok(new Boolean(a >= b)); + } else if (lhs2 instanceof Integer && op instanceof LessThanCheck && rhs2 instanceof Integer) { + let a = lhs2.n; + let b = rhs2.n; + return new Ok(new Boolean(a < b)); + } else if (lhs2 instanceof Integer && op instanceof LessThanOrEqualCheck && rhs2 instanceof Integer) { + let a = lhs2.n; + let b = rhs2.n; + return new Ok(new Boolean(a <= b)); + } else if (lhs2 instanceof FloatingPointNumber && op instanceof Add && rhs2 instanceof FloatingPointNumber) { + let a = lhs2.f; + let b = rhs2.f; + return new Ok(new FloatingPointNumber(a + b)); + } else if (lhs2 instanceof FloatingPointNumber && op instanceof Subtract && rhs2 instanceof FloatingPointNumber) { + let a = lhs2.f; + let b = rhs2.f; + return new Ok(new FloatingPointNumber(a - b)); + } else if (lhs2 instanceof FloatingPointNumber && op instanceof Multiply && rhs2 instanceof FloatingPointNumber) { + let a = lhs2.f; + let b = rhs2.f; + return new Ok(new FloatingPointNumber(a * b)); + } else if (lhs2 instanceof FloatingPointNumber && op instanceof Divide && rhs2 instanceof FloatingPointNumber) { + let a = lhs2.f; + let b = rhs2.f; + return new Ok(new FloatingPointNumber(divideFloat(a, b))); + } else if (lhs2 instanceof FloatingPointNumber && op instanceof EqualCheck && rhs2 instanceof FloatingPointNumber) { + let a = lhs2.f; + let b = rhs2.f; + return new Ok(new Boolean(a === b)); + } else if (lhs2 instanceof FloatingPointNumber && op instanceof NotEqualCheck && rhs2 instanceof FloatingPointNumber) { + let a = lhs2.f; + let b = rhs2.f; + return new Ok(new Boolean(a !== b)); + } else if (lhs2 instanceof FloatingPointNumber && op instanceof GreaterThanCheck && rhs2 instanceof FloatingPointNumber) { + let a = lhs2.f; + let b = rhs2.f; + return new Ok(new Boolean(a > b)); + } else if (lhs2 instanceof FloatingPointNumber && op instanceof GreaterThanOrEqualCheck && rhs2 instanceof FloatingPointNumber) { + let a = lhs2.f; + let b = rhs2.f; + return new Ok(new Boolean(a >= b)); + } else if (lhs2 instanceof FloatingPointNumber && op instanceof LessThanCheck && rhs2 instanceof FloatingPointNumber) { + let a = lhs2.f; + let b = rhs2.f; + return new Ok(new Boolean(a < b)); + } else if (lhs2 instanceof FloatingPointNumber && op instanceof LessThanOrEqualCheck && rhs2 instanceof FloatingPointNumber) { + let a = lhs2.f; + let b = rhs2.f; + return new Ok(new Boolean(a <= b)); + } else if (lhs2 instanceof Integer && op instanceof Power && rhs2 instanceof FloatingPointNumber) { + let a = lhs2.n; + let b = rhs2.f; + let $ = power3(a, b); + if (!$.isOk()) { + throw makeError( + "assignment_no_match", + "squared_away/lang/interpreter", + 104, + "", + "Assignment pattern did not match", + { value: $ } + ); + } + let p2 = $[0]; + return new Ok(new FloatingPointNumber(p2)); + } else if (lhs2 instanceof FloatingPointNumber && op instanceof Power && rhs2 instanceof FloatingPointNumber) { + let a = lhs2.f; + let b = rhs2.f; + let $ = power2(a, b); + if (!$.isOk()) { + throw makeError( + "assignment_no_match", + "squared_away/lang/interpreter", + 108, + "", + "Assignment pattern did not match", + { value: $ } + ); + } + let p2 = $[0]; + return new Ok(new FloatingPointNumber(p2)); + } else if (lhs2 instanceof Boolean && op instanceof And && rhs2 instanceof Boolean) { + let a = lhs2.b; + let b = rhs2.b; + return new Ok(new Boolean(a && b)); + } else if (lhs2 instanceof Boolean && op instanceof Or && rhs2 instanceof Boolean) { + let a = lhs2.b; + let b = rhs2.b; + return new Ok(new Boolean(a || b)); + } else if (lhs2 instanceof Boolean && op instanceof EqualCheck && rhs2 instanceof Boolean) { + let a = lhs2.b; + let b = rhs2.b; + return new Ok(new Boolean(a === b)); + } else if (lhs2 instanceof Boolean && op instanceof NotEqualCheck && rhs2 instanceof Boolean) { + let a = lhs2.b; + let b = rhs2.b; + return new Ok(new Boolean(a !== b)); + } else { throw makeError( - "assignment_no_match", - "squared_away/lang/scanner", - 100, + "panic", + "squared_away/lang/interpreter", + 122, "", - "Assignment pattern did not match", - { value: $1 } + "these should be the only options if the typechecker is working properly", + {} ); } - let f = $1[0]; - return do_scan( - trim_left2(rest$2), - prepend(new FloatLiteral(f), acc) - ); } ); - } else { - loop$src = trim_left2(rest); - loop$acc = prepend(new IntegerLiteral(n), acc); } - } else { - return try$( - (() => { - let _pipe = parse_cell_ref(src, ""); - return replace_error(_pipe, new ScanError()); - })(), - (_use0) => { - let cell_ref = _use0[0]; - let rest = _use0[1]; - return do_scan( - trim_left2(rest), - prepend(new CellReference(cell_ref), acc) - ); - } - ); - } + ); } } } -function scan(src) { - let $ = trim2(src); - if ($ === "") { - return new Ok(toList([])); - } else if ($.startsWith("=")) { - let rest = $.slice(1); - return do_scan( - (() => { - let _pipe = rest; - return trim_left2(_pipe); - })(), - toList([]) - ); - } else { - return new Ok(toList([new StringLiteral(src)])); - } -} -// build/dev/javascript/squared_away/squared_away/lang/parser.mjs -var Empty2 = class extends CustomType { -}; -var FloatLiteral2 = class extends CustomType { - constructor(f) { - super(); - this.f = f; - } -}; -var StringLiteral2 = class extends CustomType { - constructor(txt) { - super(); - this.txt = txt; - } -}; -var IntegerLiteral2 = class extends CustomType { - constructor(n) { - super(); - this.n = n; - } +// build/dev/javascript/squared_away/squared_away/lang/scanner/token.mjs +var Plus = class extends CustomType { }; -var CellReference2 = class extends CustomType { - constructor(key) { - super(); - this.key = key; - } +var Minus = class extends CustomType { }; -var BooleanLiteral = class extends CustomType { - constructor(val) { - super(); - this.val = val; - } +var Star = class extends CustomType { }; -var UnaryOp = class extends CustomType { - constructor(op, expr) { - super(); - this.op = op; - this.expr = expr; - } +var Div = class extends CustomType { }; -var BinaryOp = class extends CustomType { - constructor(lhs, op, rhs) { - super(); - this.lhs = lhs; - this.op = op; - this.rhs = rhs; - } +var StarStar = class extends CustomType { }; -var Group = class extends CustomType { - constructor(inner) { - super(); - this.inner = inner; - } +var Equal = class extends CustomType { }; -var Add = class extends CustomType { +var EqualEqual = class extends CustomType { }; -var Subtract = class extends CustomType { +var BangEqual = class extends CustomType { }; -var Multiply = class extends CustomType { +var Bang = class extends CustomType { }; -var Divide = class extends CustomType { +var Less = class extends CustomType { }; -var Power = class extends CustomType { +var LessEqual = class extends CustomType { }; -var EqualCheck = class extends CustomType { +var Greater = class extends CustomType { }; -var NotEqualCheck = class extends CustomType { +var GreaterEqual = class extends CustomType { }; -var LessThanCheck = class extends CustomType { +var IntegerLiteral3 = class extends CustomType { + constructor(n) { + super(); + this.n = n; + } }; -var LessThanOrEqualCheck = class extends CustomType { +var FloatLiteral3 = class extends CustomType { + constructor(f) { + super(); + this.f = f; + } }; -var GreaterThanCheck = class extends CustomType { +var TrueToken = class extends CustomType { }; -var GreaterThanOrEqualCheck = class extends CustomType { +var FalseToken = class extends CustomType { }; var And2 = class extends CustomType { }; var Or2 = class extends CustomType { }; -var Negate = class extends CustomType { +var LParen = class extends CustomType { }; -var Not = class extends CustomType { +var RParen = class extends CustomType { }; -var ParseError = class extends CustomType { - constructor(context) { +var CellReference3 = class extends CustomType { + constructor(key) { super(); - this.context = context; + this.key = key; + } +}; +var StringLiteral3 = class extends CustomType { + constructor(x0) { + super(); + this[0] = x0; } }; + +// build/dev/javascript/squared_away/squared_away/lang/parser.mjs function try_parse_binary_ops(tokens) { if (tokens.atLeastLength(1) && tokens.head instanceof Plus) { let rest = tokens.tail; @@ -2844,7 +2817,11 @@ function try_parse_binary_ops(tokens) { return new Ok( [ (_capture) => { - return new BinaryOp(_capture, new NotEqualCheck(), rhs); + return new BinaryOp( + _capture, + new NotEqualCheck(), + rhs + ); }, rest$1 ] @@ -2868,7 +2845,70 @@ function try_parse_binary_ops(tokens) { ); } ); - } else if (tokens.atLeastLength(1) && tokens.head instanceof LessEqual) { + } else if (tokens.atLeastLength(1) && tokens.head instanceof LessEqual) { + let rest = tokens.tail; + return try$( + do_parse(rest), + (_use0) => { + let rhs = _use0[0]; + let rest$1 = _use0[1]; + return new Ok( + [ + (_capture) => { + return new BinaryOp( + _capture, + new LessThanOrEqualCheck(), + rhs + ); + }, + rest$1 + ] + ); + } + ); + } else if (tokens.atLeastLength(1) && tokens.head instanceof Less) { + let rest = tokens.tail; + return try$( + do_parse(rest), + (_use0) => { + let rhs = _use0[0]; + let rest$1 = _use0[1]; + return new Ok( + [ + (_capture) => { + return new BinaryOp( + _capture, + new LessThanCheck(), + rhs + ); + }, + rest$1 + ] + ); + } + ); + } else if (tokens.atLeastLength(1) && tokens.head instanceof GreaterEqual) { + let rest = tokens.tail; + return try$( + do_parse(rest), + (_use0) => { + let rhs = _use0[0]; + let rest$1 = _use0[1]; + return new Ok( + [ + (_capture) => { + return new BinaryOp( + _capture, + new GreaterThanOrEqualCheck(), + rhs + ); + }, + rest$1 + ] + ); + } + ); + } else if (tokens.atLeastLength(1) && tokens.head instanceof Greater) { let rest = tokens.tail; return try$( do_parse(rest), @@ -2878,14 +2918,18 @@ function try_parse_binary_ops(tokens) { return new Ok( [ (_capture) => { - return new BinaryOp(_capture, new LessThanOrEqualCheck(), rhs); + return new BinaryOp( + _capture, + new GreaterThanCheck(), + rhs + ); }, rest$1 ] ); } ); - } else if (tokens.atLeastLength(1) && tokens.head instanceof Less) { + } else if (tokens.atLeastLength(1) && tokens.head instanceof And2) { let rest = tokens.tail; return try$( do_parse(rest), @@ -2895,14 +2939,14 @@ function try_parse_binary_ops(tokens) { return new Ok( [ (_capture) => { - return new BinaryOp(_capture, new LessThanCheck(), rhs); + return new BinaryOp(_capture, new And(), rhs); }, rest$1 ] ); } ); - } else if (tokens.atLeastLength(1) && tokens.head instanceof GreaterEqual) { + } else if (tokens.atLeastLength(1) && tokens.head instanceof Or2) { let rest = tokens.tail; return try$( do_parse(rest), @@ -2912,296 +2956,550 @@ function try_parse_binary_ops(tokens) { return new Ok( [ (_capture) => { - return new BinaryOp(_capture, new GreaterThanOrEqualCheck(), rhs); + return new BinaryOp(_capture, new Or(), rhs); }, rest$1 ] ); } ); - } else if (tokens.atLeastLength(1) && tokens.head instanceof Greater) { + } else { + return new Error(new ParseError("Not a binary operation")); + } +} +function do_parse(tokens) { + if (tokens.hasLength(0)) { + return new Ok([new Empty3(), toList([])]); + } else if (tokens.atLeastLength(1) && tokens.head instanceof StringLiteral3) { + let str = tokens.head[0]; + let rest = tokens.tail; + let $ = try_parse_binary_ops(rest); + if ($.isOk()) { + let op = $[0][0]; + let rest$1 = $[0][1]; + return new Ok([op(new StringLiteral(str)), rest$1]); + } else { + return new Ok([new StringLiteral(str), rest]); + } + } else if (tokens.atLeastLength(1) && tokens.head instanceof IntegerLiteral3) { + let n = tokens.head.n; + let rest = tokens.tail; + let $ = try_parse_binary_ops(rest); + if ($.isOk()) { + let op = $[0][0]; + let rest$1 = $[0][1]; + return new Ok([op(new IntegerLiteral(n)), rest$1]); + } else { + return new Ok([new IntegerLiteral(n), rest]); + } + } else if (tokens.atLeastLength(1) && tokens.head instanceof FloatLiteral3) { + let f = tokens.head.f; + let rest = tokens.tail; + let $ = try_parse_binary_ops(rest); + if ($.isOk()) { + let op = $[0][0]; + let rest$1 = $[0][1]; + return new Ok([op(new FloatLiteral(f)), rest$1]); + } else { + return new Ok([new FloatLiteral(f), rest]); + } + } else if (tokens.atLeastLength(1) && tokens.head instanceof CellReference3) { + let key = tokens.head.key; + let rest = tokens.tail; + let $ = try_parse_binary_ops(rest); + if ($.isOk()) { + let op = $[0][0]; + let rest$1 = $[0][1]; + return new Ok([op(new CellReference(key)), rest$1]); + } else { + return new Ok([new CellReference(key), rest]); + } + } else if (tokens.atLeastLength(1) && tokens.head instanceof TrueToken) { + let rest = tokens.tail; + let $ = try_parse_binary_ops(rest); + if ($.isOk()) { + let op = $[0][0]; + let rest$1 = $[0][1]; + return new Ok([op(new BooleanLiteral(true)), rest$1]); + } else { + return new Ok([new BooleanLiteral(true), rest]); + } + } else if (tokens.atLeastLength(1) && tokens.head instanceof FalseToken) { + let rest = tokens.tail; + let $ = try_parse_binary_ops(rest); + if ($.isOk()) { + let op = $[0][0]; + let rest$1 = $[0][1]; + return new Ok([op(new BooleanLiteral(false)), rest$1]); + } else { + return new Ok([new BooleanLiteral(false), rest]); + } + } else if (tokens.atLeastLength(1) && tokens.head instanceof Minus) { let rest = tokens.tail; return try$( do_parse(rest), (_use0) => { - let rhs = _use0[0]; + let parsed_remainder = _use0[0]; let rest$1 = _use0[1]; return new Ok( - [ - (_capture) => { - return new BinaryOp(_capture, new GreaterThanCheck(), rhs); - }, - rest$1 - ] + [new UnaryOp(new Negate(), parsed_remainder), rest$1] ); } ); - } else if (tokens.atLeastLength(1) && tokens.head instanceof And) { + } else if (tokens.atLeastLength(1) && tokens.head instanceof Bang) { let rest = tokens.tail; return try$( do_parse(rest), (_use0) => { - let rhs = _use0[0]; + let parsed_remainder = _use0[0]; let rest$1 = _use0[1]; return new Ok( - [ - (_capture) => { - return new BinaryOp(_capture, new And2(), rhs); - }, - rest$1 - ] + [new UnaryOp(new Not(), parsed_remainder), rest$1] ); } ); - } else if (tokens.atLeastLength(1) && tokens.head instanceof Or) { + } else if (tokens.atLeastLength(1) && tokens.head instanceof LParen) { let rest = tokens.tail; return try$( do_parse(rest), (_use0) => { - let rhs = _use0[0]; + let body = _use0[0]; let rest$1 = _use0[1]; - return new Ok( - [ - (_capture) => { - return new BinaryOp(_capture, new Or2(), rhs); - }, - rest$1 - ] + if (rest$1.atLeastLength(1) && rest$1.head instanceof RParen) { + let rest$2 = rest$1.tail; + let $ = try_parse_binary_ops(rest$2); + if ($.isOk()) { + let op = $[0][0]; + let rest$3 = $[0][1]; + return new Ok([op(new Group(body)), rest$3]); + } else { + return new Ok([new Group(body), rest$2]); + } + } else { + return new Error( + new ParseError("missing closing parentheses") + ); + } + } + ); + } else { + let x = tokens.head; + return new Error( + new ParseError("Unexpected token: " + inspect2(x)) + ); + } +} +function parse3(tokens) { + return try$( + do_parse(tokens), + (_use0) => { + let expr = _use0[0]; + let rest = _use0[1]; + if (rest.hasLength(0)) { + return new Ok(expr); + } else { + return new Error( + new ParseError( + "After parsing there were leftover tokens" + ) + ); + } + } + ); +} + +// build/dev/javascript/squared_away/squared_away/lang/scanner.mjs +function parse_integer(loop$src, loop$acc) { + while (true) { + let src = loop$src; + let acc = loop$acc; + if (src.startsWith("1")) { + let rest = src.slice(1); + let x = "1"; + loop$src = rest; + loop$acc = acc + x; + } else if (src.startsWith("2")) { + let rest = src.slice(1); + let x = "2"; + loop$src = rest; + loop$acc = acc + x; + } else if (src.startsWith("3")) { + let rest = src.slice(1); + let x = "3"; + loop$src = rest; + loop$acc = acc + x; + } else if (src.startsWith("4")) { + let rest = src.slice(1); + let x = "4"; + loop$src = rest; + loop$acc = acc + x; + } else if (src.startsWith("5")) { + let rest = src.slice(1); + let x = "5"; + loop$src = rest; + loop$acc = acc + x; + } else if (src.startsWith("6")) { + let rest = src.slice(1); + let x = "6"; + loop$src = rest; + loop$acc = acc + x; + } else if (src.startsWith("7")) { + let rest = src.slice(1); + let x = "7"; + loop$src = rest; + loop$acc = acc + x; + } else if (src.startsWith("8")) { + let rest = src.slice(1); + let x = "8"; + loop$src = rest; + loop$acc = acc + x; + } else if (src.startsWith("9")) { + let rest = src.slice(1); + let x = "9"; + loop$src = rest; + loop$acc = acc + x; + } else if (src.startsWith("0")) { + let rest = src.slice(1); + let x = "0"; + loop$src = rest; + loop$acc = acc + x; + } else { + let _pipe = parse2(acc); + return map2(_pipe, (n) => { + return [n, src]; + }); + } + } +} +function parse_cell_ref(loop$src, loop$acc) { + while (true) { + let src = loop$src; + let acc = loop$acc; + if (src.startsWith("A")) { + let rest = src.slice(1); + let l = "A"; + loop$src = rest; + loop$acc = acc + l; + } else if (src.startsWith("B")) { + let rest = src.slice(1); + let l = "B"; + loop$src = rest; + loop$acc = acc + l; + } else if (src.startsWith("C")) { + let rest = src.slice(1); + let l = "C"; + loop$src = rest; + loop$acc = acc + l; + } else if (src.startsWith("D")) { + let rest = src.slice(1); + let l = "D"; + loop$src = rest; + loop$acc = acc + l; + } else if (src.startsWith("E")) { + let rest = src.slice(1); + let l = "E"; + loop$src = rest; + loop$acc = acc + l; + } else if (src.startsWith("F")) { + let rest = src.slice(1); + let l = "F"; + loop$src = rest; + loop$acc = acc + l; + } else if (src.startsWith("G")) { + let rest = src.slice(1); + let l = "G"; + loop$src = rest; + loop$acc = acc + l; + } else if (src.startsWith("H")) { + let rest = src.slice(1); + let l = "H"; + loop$src = rest; + loop$acc = acc + l; + } else if (src.startsWith("I")) { + let rest = src.slice(1); + let l = "I"; + loop$src = rest; + loop$acc = acc + l; + } else if (src.startsWith("J")) { + let rest = src.slice(1); + let l = "J"; + loop$src = rest; + loop$acc = acc + l; + } else if (src.startsWith("K")) { + let rest = src.slice(1); + let l = "K"; + loop$src = rest; + loop$acc = acc + l; + } else if (src.startsWith("L")) { + let rest = src.slice(1); + let l = "L"; + loop$src = rest; + loop$acc = acc + l; + } else if (src.startsWith("M")) { + let rest = src.slice(1); + let l = "M"; + loop$src = rest; + loop$acc = acc + l; + } else if (src.startsWith("N")) { + let rest = src.slice(1); + let l = "N"; + loop$src = rest; + loop$acc = acc + l; + } else if (src.startsWith("O")) { + let rest = src.slice(1); + let l = "O"; + loop$src = rest; + loop$acc = acc + l; + } else if (src.startsWith("P")) { + let rest = src.slice(1); + let l = "P"; + loop$src = rest; + loop$acc = acc + l; + } else if (src.startsWith("Q")) { + let rest = src.slice(1); + let l = "Q"; + loop$src = rest; + loop$acc = acc + l; + } else if (src.startsWith("R")) { + let rest = src.slice(1); + let l = "R"; + loop$src = rest; + loop$acc = acc + l; + } else if (src.startsWith("S")) { + let rest = src.slice(1); + let l = "S"; + loop$src = rest; + loop$acc = acc + l; + } else if (src.startsWith("T")) { + let rest = src.slice(1); + let l = "T"; + loop$src = rest; + loop$acc = acc + l; + } else if (src.startsWith("U")) { + let rest = src.slice(1); + let l = "U"; + loop$src = rest; + loop$acc = acc + l; + } else if (src.startsWith("V")) { + let rest = src.slice(1); + let l = "V"; + loop$src = rest; + loop$acc = acc + l; + } else if (src.startsWith("W")) { + let rest = src.slice(1); + let l = "W"; + loop$src = rest; + loop$acc = acc + l; + } else if (src.startsWith("X")) { + let rest = src.slice(1); + let l = "X"; + loop$src = rest; + loop$acc = acc + l; + } else if (src.startsWith("Y")) { + let rest = src.slice(1); + let l = "Y"; + loop$src = rest; + loop$acc = acc + l; + } else if (src.startsWith("Z")) { + let rest = src.slice(1); + let l = "Z"; + loop$src = rest; + loop$acc = acc + l; + } else { + if (acc === "") { + return new Error(void 0); + } else { + return try$( + parse_integer(src, ""), + (_use0) => { + let n = _use0[0]; + let rest = _use0[1]; + return new Ok([acc + to_string2(n), rest]); + } ); } - ); - } else { - return new Error(new ParseError("Not a binary operation")); + } } } -function do_parse(tokens) { - if (tokens.hasLength(0)) { - return new Ok([new Empty2(), toList([])]); - } else if (tokens.atLeastLength(1) && tokens.head instanceof StringLiteral) { - let str = tokens.head[0]; - let rest = tokens.tail; - let $ = try_parse_binary_ops(rest); - if ($.isOk()) { - let op = $[0][0]; - let rest$1 = $[0][1]; - return new Ok([op(new StringLiteral2(str)), rest$1]); - } else { - return new Ok([new StringLiteral2(str), rest]); - } - } else if (tokens.atLeastLength(1) && tokens.head instanceof IntegerLiteral) { - let n = tokens.head.n; - let rest = tokens.tail; - let $ = try_parse_binary_ops(rest); - if ($.isOk()) { - let op = $[0][0]; - let rest$1 = $[0][1]; - return new Ok([op(new IntegerLiteral2(n)), rest$1]); - } else { - return new Ok([new IntegerLiteral2(n), rest]); - } - } else if (tokens.atLeastLength(1) && tokens.head instanceof FloatLiteral) { - let f = tokens.head.f; - let rest = tokens.tail; - let $ = try_parse_binary_ops(rest); - if ($.isOk()) { - let op = $[0][0]; - let rest$1 = $[0][1]; - return new Ok([op(new FloatLiteral2(f)), rest$1]); - } else { - return new Ok([new FloatLiteral2(f), rest]); - } - } else if (tokens.atLeastLength(1) && tokens.head instanceof CellReference) { - let key = tokens.head.key; - let rest = tokens.tail; - let $ = try_parse_binary_ops(rest); - if ($.isOk()) { - let op = $[0][0]; - let rest$1 = $[0][1]; - return new Ok([op(new CellReference2(key)), rest$1]); - } else { - return new Ok([new CellReference2(key), rest]); - } - } else if (tokens.atLeastLength(1) && tokens.head instanceof TrueToken) { - let rest = tokens.tail; - let $ = try_parse_binary_ops(rest); - if ($.isOk()) { - let op = $[0][0]; - let rest$1 = $[0][1]; - return new Ok([op(new BooleanLiteral(true)), rest$1]); - } else { - return new Ok([new BooleanLiteral(true), rest]); - } - } else if (tokens.atLeastLength(1) && tokens.head instanceof FalseToken) { - let rest = tokens.tail; - let $ = try_parse_binary_ops(rest); - if ($.isOk()) { - let op = $[0][0]; - let rest$1 = $[0][1]; - return new Ok([op(new BooleanLiteral(false)), rest$1]); - } else { - return new Ok([new BooleanLiteral(false), rest]); - } - } else if (tokens.atLeastLength(1) && tokens.head instanceof Minus) { - let rest = tokens.tail; - return try$( - do_parse(rest), - (_use0) => { - let parsed_remainder = _use0[0]; - let rest$1 = _use0[1]; - return new Ok([new UnaryOp(new Negate(), parsed_remainder), rest$1]); - } - ); - } else if (tokens.atLeastLength(1) && tokens.head instanceof Bang) { - let rest = tokens.tail; - return try$( - do_parse(rest), - (_use0) => { - let parsed_remainder = _use0[0]; - let rest$1 = _use0[1]; - return new Ok([new UnaryOp(new Not(), parsed_remainder), rest$1]); - } - ); - } else if (tokens.atLeastLength(1) && tokens.head instanceof LParen) { - let rest = tokens.tail; - return try$( - do_parse(rest), - (_use0) => { - let body = _use0[0]; - let rest$1 = _use0[1]; - if (rest$1.atLeastLength(1) && rest$1.head instanceof RParen) { - let rest$2 = rest$1.tail; - let $ = try_parse_binary_ops(rest$2); - if ($.isOk()) { - let op = $[0][0]; - let rest$3 = $[0][1]; - return new Ok([op(new Group(body)), rest$3]); - } else { - return new Ok([new Group(body), rest$2]); - } +function do_scan(loop$src, loop$acc) { + while (true) { + let src = loop$src; + let acc = loop$acc; + if (src === "") { + return new Ok( + (() => { + let _pipe = acc; + return reverse(_pipe); + })() + ); + } else if (src.startsWith("TRUE")) { + let rest = src.slice(4); + loop$src = trim_left2(rest); + loop$acc = prepend(new TrueToken(), acc); + } else if (src.startsWith("FALSE")) { + let rest = src.slice(5); + loop$src = trim_left2(rest); + loop$acc = prepend(new FalseToken(), acc); + } else if (src.startsWith("&&")) { + let rest = src.slice(2); + loop$src = trim_left2(rest); + loop$acc = prepend(new And2(), acc); + } else if (src.startsWith("||")) { + let rest = src.slice(2); + loop$src = trim_left2(rest); + loop$acc = prepend(new Or2(), acc); + } else if (src.startsWith("**")) { + let rest = src.slice(2); + loop$src = trim_left2(rest); + loop$acc = prepend(new StarStar(), acc); + } else if (src.startsWith("==")) { + let rest = src.slice(2); + loop$src = trim_left2(rest); + loop$acc = prepend(new EqualEqual(), acc); + } else if (src.startsWith("!=")) { + let rest = src.slice(2); + loop$src = trim_left2(rest); + loop$acc = prepend(new BangEqual(), acc); + } else if (src.startsWith("<=")) { + let rest = src.slice(2); + loop$src = trim_left2(rest); + loop$acc = prepend(new LessEqual(), acc); + } else if (src.startsWith(">=")) { + let rest = src.slice(2); + loop$src = trim_left2(rest); + loop$acc = prepend(new GreaterEqual(), acc); + } else if (src.startsWith("+")) { + let rest = src.slice(1); + loop$src = trim_left2(rest); + loop$acc = prepend(new Plus(), acc); + } else if (src.startsWith("-")) { + let rest = src.slice(1); + loop$src = trim_left2(rest); + loop$acc = prepend(new Minus(), acc); + } else if (src.startsWith("*")) { + let rest = src.slice(1); + loop$src = trim_left2(rest); + loop$acc = prepend(new Star(), acc); + } else if (src.startsWith("/")) { + let rest = src.slice(1); + loop$src = trim_left2(rest); + loop$acc = prepend(new Div(), acc); + } else if (src.startsWith("=")) { + let rest = src.slice(1); + loop$src = trim_left2(rest); + loop$acc = prepend(new Equal(), acc); + } else if (src.startsWith("!")) { + let rest = src.slice(1); + loop$src = trim_left2(rest); + loop$acc = prepend(new Bang(), acc); + } else if (src.startsWith("<")) { + let rest = src.slice(1); + loop$src = trim_left2(rest); + loop$acc = prepend(new Less(), acc); + } else if (src.startsWith(">")) { + let rest = src.slice(1); + loop$src = trim_left2(rest); + loop$acc = prepend(new Greater(), acc); + } else if (src.startsWith("(")) { + let rest = src.slice(1); + loop$src = trim_left2(rest); + loop$acc = prepend(new LParen(), acc); + } else if (src.startsWith(")")) { + let rest = src.slice(1); + loop$src = trim_left2(rest); + loop$acc = prepend(new RParen(), acc); + } else { + let $ = parse_integer(src, ""); + if ($.isOk()) { + let n = $[0][0]; + let rest = $[0][1]; + if (rest.startsWith(".")) { + let rest$1 = rest.slice(1); + return try$( + (() => { + let _pipe = parse_integer(rest$1, ""); + return replace_error(_pipe, new ScanError()); + })(), + (_use0) => { + let m = _use0[0]; + let rest$2 = _use0[1]; + let $1 = parse( + to_string2(n) + "." + to_string2(m) + ); + if (!$1.isOk()) { + throw makeError( + "assignment_no_match", + "squared_away/lang/scanner", + 54, + "", + "Assignment pattern did not match", + { value: $1 } + ); + } + let f = $1[0]; + return do_scan( + trim_left2(rest$2), + prepend(new FloatLiteral3(f), acc) + ); + } + ); } else { - return new Error(new ParseError("missing closing parentheses")); + loop$src = trim_left2(rest); + loop$acc = prepend(new IntegerLiteral3(n), acc); } - } - ); - } else { - let x = tokens.head; - return new Error(new ParseError("Unexpected token: " + inspect2(x))); - } -} -function parse3(tokens) { - return try$( - do_parse(tokens), - (_use0) => { - let expr = _use0[0]; - let rest = _use0[1]; - if (rest.hasLength(0)) { - return new Ok(expr); } else { - return new Error( - new ParseError("After parsing there were leftover tokens") + return try$( + (() => { + let _pipe = parse_cell_ref(src, ""); + return replace_error(_pipe, new ScanError()); + })(), + (_use0) => { + let cell_ref = _use0[0]; + let rest = _use0[1]; + return do_scan( + trim_left2(rest), + prepend(new CellReference3(cell_ref), acc) + ); + } ); } } - ); + } +} +function scan(src) { + let $ = trim2(src); + if ($ === "") { + return new Ok(toList([])); + } else if ($.startsWith("=")) { + let rest = $.slice(1); + return do_scan( + (() => { + let _pipe = rest; + return trim_left2(_pipe); + })(), + toList([]) + ); + } else { + return new Ok(toList([new StringLiteral3(src)])); + } } // build/dev/javascript/squared_away/squared_away/lang/typechecker.mjs -var Empty3 = class extends CustomType { - constructor(type_) { - super(); - this.type_ = type_; - } -}; -var FloatLiteral3 = class extends CustomType { - constructor(type_, f) { - super(); - this.type_ = type_; - this.f = f; - } -}; -var StringLiteral3 = class extends CustomType { - constructor(type_, txt) { - super(); - this.type_ = type_; - this.txt = txt; - } -}; -var IntegerLiteral3 = class extends CustomType { - constructor(type_, n) { - super(); - this.type_ = type_; - this.n = n; - } -}; -var CellReference3 = class extends CustomType { - constructor(type_, key) { - super(); - this.type_ = type_; - this.key = key; - } -}; -var BooleanLiteral2 = class extends CustomType { - constructor(type_, b) { - super(); - this.type_ = type_; - this.b = b; - } -}; -var UnaryOp2 = class extends CustomType { - constructor(type_, op, expr) { - super(); - this.type_ = type_; - this.op = op; - this.expr = expr; - } -}; -var BinaryOp2 = class extends CustomType { - constructor(type_, lhs, op, rhs) { - super(); - this.type_ = type_; - this.lhs = lhs; - this.op = op; - this.rhs = rhs; - } -}; -var Group2 = class extends CustomType { - constructor(type_, expr) { - super(); - this.type_ = type_; - this.expr = expr; - } -}; -var TNil = class extends CustomType { -}; -var TFloat = class extends CustomType { -}; -var TString = class extends CustomType { -}; -var TInt = class extends CustomType { -}; -var TBool = class extends CustomType { -}; -var TypeError = class extends CustomType { - constructor(context) { - super(); - this.context = context; - } -}; function typecheck(env, expr) { - if (expr instanceof Empty2) { - return new Ok(new Empty3(new TNil())); - } else if (expr instanceof StringLiteral2) { + if (expr instanceof Empty3) { + return new Ok(new Empty4(new TNil())); + } else if (expr instanceof StringLiteral) { let txt = expr.txt; - return new Ok(new StringLiteral3(new TString(), txt)); + return new Ok(new StringLiteral2(new TString(), txt)); } else if (expr instanceof BooleanLiteral) { let b = expr.val; return new Ok(new BooleanLiteral2(new TBool(), b)); - } else if (expr instanceof FloatLiteral2) { + } else if (expr instanceof FloatLiteral) { let f = expr.f; - return new Ok(new FloatLiteral3(new TFloat(), f)); - } else if (expr instanceof IntegerLiteral2) { + return new Ok(new FloatLiteral2(new TFloat(), f)); + } else if (expr instanceof IntegerLiteral) { let n = expr.n; - return new Ok(new IntegerLiteral3(new TInt(), n)); + return new Ok(new IntegerLiteral2(new TInt(), n)); } else if (expr instanceof Group) { let inner = expr.inner; return try$( @@ -3210,26 +3508,31 @@ function typecheck(env, expr) { return new Ok(new Group2(expr2.type_, expr2)); } ); - } else if (expr instanceof CellReference2) { + } else if (expr instanceof CellReference) { let key = expr.key; - let ref_expr = (() => { - let _pipe = get(env, key); - return flatten(_pipe); - })(); - if (!ref_expr.isOk() && !ref_expr[0]) { - return new Error( - new TypeError( - "Do not have type for referenced cell: " + inspect2(key) - ) + let $ = get(env, key); + if (!$.isOk()) { + throw makeError( + "assignment_no_match", + "squared_away/lang/typechecker", + 27, + "typecheck", + "Assignment pattern did not match", + { value: $ } ); - } else { + } + let ref_expr = $[0]; + if (ref_expr.isOk()) { let expr$1 = ref_expr[0]; return try$( typecheck(env, expr$1), (expr2) => { - return new Ok(new CellReference3(expr2.type_, key)); + return new Ok(new CellReference2(expr2.type_, key)); } ); + } else { + let e = ref_expr[0]; + return new Error(e); } } else if (expr instanceof UnaryOp) { let op = expr.op; @@ -3246,7 +3549,11 @@ function typecheck(env, expr) { return new Ok(new UnaryOp2(expr2.type_, op, expr2)); } else { return new Error( - new TypeError("Unexpected type and operator combination") + new TypeError2( + new TypeError( + "Unexpected type and operator combination" + ) + ) ); } } @@ -3264,141 +3571,166 @@ function typecheck(env, expr) { let $ = lhs2.type_; let $1 = rhs2.type_; if ($ instanceof TFloat && op instanceof Add && $1 instanceof TFloat) { - return new Ok(new BinaryOp2(new TFloat(), lhs2, op, rhs2)); + return new Ok( + new BinaryOp2(new TFloat(), lhs2, op, rhs2) + ); } else if ($ instanceof TInt && op instanceof Add && $1 instanceof TInt) { - return new Ok(new BinaryOp2(new TInt(), lhs2, op, rhs2)); + return new Ok( + new BinaryOp2(new TInt(), lhs2, op, rhs2) + ); } else if ($ instanceof TFloat && op instanceof Subtract && $1 instanceof TFloat) { - return new Ok(new BinaryOp2(new TFloat(), lhs2, op, rhs2)); + return new Ok( + new BinaryOp2(new TFloat(), lhs2, op, rhs2) + ); } else if ($ instanceof TInt && op instanceof Subtract && $1 instanceof TInt) { - return new Ok(new BinaryOp2(new TInt(), lhs2, op, rhs2)); + return new Ok( + new BinaryOp2(new TInt(), lhs2, op, rhs2) + ); } else if ($ instanceof TFloat && op instanceof Multiply && $1 instanceof TFloat) { - return new Ok(new BinaryOp2(new TFloat(), lhs2, op, rhs2)); + return new Ok( + new BinaryOp2(new TFloat(), lhs2, op, rhs2) + ); } else if ($ instanceof TInt && op instanceof Multiply && $1 instanceof TInt) { - return new Ok(new BinaryOp2(new TInt(), lhs2, op, rhs2)); + return new Ok( + new BinaryOp2(new TInt(), lhs2, op, rhs2) + ); } else if ($ instanceof TFloat && op instanceof Divide && $1 instanceof TFloat) { - return new Ok(new BinaryOp2(new TFloat(), lhs2, op, rhs2)); + return new Ok( + new BinaryOp2(new TFloat(), lhs2, op, rhs2) + ); } else if ($ instanceof TInt && op instanceof Divide && $1 instanceof TInt) { - return new Ok(new BinaryOp2(new TInt(), lhs2, op, rhs2)); + return new Ok( + new BinaryOp2(new TInt(), lhs2, op, rhs2) + ); } else if ($ instanceof TFloat && op instanceof Power && $1 instanceof TFloat) { - return new Ok(new BinaryOp2(new TFloat(), lhs2, op, rhs2)); + return new Ok( + new BinaryOp2(new TFloat(), lhs2, op, rhs2) + ); } else if ($ instanceof TInt && op instanceof Power && $1 instanceof TFloat) { - return new Ok(new BinaryOp2(new TFloat(), lhs2, op, rhs2)); + return new Ok( + new BinaryOp2(new TFloat(), lhs2, op, rhs2) + ); } else if (op instanceof EqualCheck && isEqual($, $1)) { let t1 = $; let t2 = $1; - return new Ok(new BinaryOp2(new TBool(), lhs2, op, rhs2)); + return new Ok( + new BinaryOp2(new TBool(), lhs2, op, rhs2) + ); } else if (op instanceof NotEqualCheck && isEqual($, $1)) { let t1 = $; let t2 = $1; - return new Ok(new BinaryOp2(new TBool(), lhs2, op, rhs2)); + return new Ok( + new BinaryOp2(new TBool(), lhs2, op, rhs2) + ); } else if ($ instanceof TFloat && op instanceof LessThanCheck && $1 instanceof TFloat) { - return new Ok(new BinaryOp2(new TBool(), lhs2, op, rhs2)); + return new Ok( + new BinaryOp2(new TBool(), lhs2, op, rhs2) + ); } else if ($ instanceof TFloat && op instanceof LessThanOrEqualCheck && $1 instanceof TFloat) { - return new Ok(new BinaryOp2(new TBool(), lhs2, op, rhs2)); + return new Ok( + new BinaryOp2(new TBool(), lhs2, op, rhs2) + ); } else if ($ instanceof TFloat && op instanceof GreaterThanOrEqualCheck && $1 instanceof TFloat) { - return new Ok(new BinaryOp2(new TBool(), lhs2, op, rhs2)); + return new Ok( + new BinaryOp2(new TBool(), lhs2, op, rhs2) + ); } else if ($ instanceof TFloat && op instanceof GreaterThanCheck && $1 instanceof TFloat) { - return new Ok(new BinaryOp2(new TBool(), lhs2, op, rhs2)); + return new Ok( + new BinaryOp2(new TBool(), lhs2, op, rhs2) + ); } else if ($ instanceof TInt && op instanceof LessThanCheck && $1 instanceof TInt) { - return new Ok(new BinaryOp2(new TBool(), lhs2, op, rhs2)); + return new Ok( + new BinaryOp2(new TBool(), lhs2, op, rhs2) + ); } else if ($ instanceof TInt && op instanceof LessThanOrEqualCheck && $1 instanceof TInt) { - return new Ok(new BinaryOp2(new TBool(), lhs2, op, rhs2)); + return new Ok( + new BinaryOp2(new TBool(), lhs2, op, rhs2) + ); } else if ($ instanceof TInt && op instanceof GreaterThanOrEqualCheck && $1 instanceof TInt) { - return new Ok(new BinaryOp2(new TBool(), lhs2, op, rhs2)); + return new Ok( + new BinaryOp2(new TBool(), lhs2, op, rhs2) + ); } else if ($ instanceof TInt && op instanceof GreaterThanCheck && $1 instanceof TInt) { - return new Ok(new BinaryOp2(new TBool(), lhs2, op, rhs2)); + return new Ok( + new BinaryOp2(new TBool(), lhs2, op, rhs2) + ); } else if ($ instanceof TString && op instanceof LessThanCheck && $1 instanceof TString) { - return new Ok(new BinaryOp2(new TBool(), lhs2, op, rhs2)); + return new Ok( + new BinaryOp2(new TBool(), lhs2, op, rhs2) + ); } else if ($ instanceof TString && op instanceof LessThanOrEqualCheck && $1 instanceof TString) { - return new Ok(new BinaryOp2(new TBool(), lhs2, op, rhs2)); + return new Ok( + new BinaryOp2(new TBool(), lhs2, op, rhs2) + ); } else if ($ instanceof TString && op instanceof GreaterThanOrEqualCheck && $1 instanceof TString) { - return new Ok(new BinaryOp2(new TBool(), lhs2, op, rhs2)); + return new Ok( + new BinaryOp2(new TBool(), lhs2, op, rhs2) + ); } else if ($ instanceof TString && op instanceof GreaterThanCheck && $1 instanceof TString) { - return new Ok(new BinaryOp2(new TBool(), lhs2, op, rhs2)); - } else if ($ instanceof TBool && op instanceof And2 && $1 instanceof TBool) { - return new Ok(new BinaryOp2(new TBool(), lhs2, op, rhs2)); - } else if ($ instanceof TBool && op instanceof Or2 && $1 instanceof TBool) { - return new Ok(new BinaryOp2(new TBool(), lhs2, op, rhs2)); + return new Ok( + new BinaryOp2(new TBool(), lhs2, op, rhs2) + ); + } else if ($ instanceof TBool && op instanceof And && $1 instanceof TBool) { + return new Ok( + new BinaryOp2(new TBool(), lhs2, op, rhs2) + ); + } else if ($ instanceof TBool && op instanceof Or && $1 instanceof TBool) { + return new Ok( + new BinaryOp2(new TBool(), lhs2, op, rhs2) + ); + } else if ($ instanceof TBool && op instanceof And) { + let t = $1; + if (t instanceof TNil) { + return new Error( + new TypeError2( + new TypeError( + 'Tried to do a boolean and operation (b1 && b2) but the right hand side of the operation has type "Empty". Could you be referencing an empty cell?' + ) + ) + ); + } else { + return new Error( + new TypeError2( + new TypeError( + "Tried to do a boolean and operation (b1 && b2) but the right hand side has type " + inspect2( + t + ) + ) + ) + ); + } } else { return new Error( - new TypeError( - "Unexpected arguments to binary operation: " + inspect2( - op + new TypeError2( + new TypeError( + "Unexpected arguments to binary operation: " + inspect2( + op + ) ) ) ); } - } - ); - } - ); - } -} - -// build/dev/javascript/squared_away/squared_away/lang/interpreter.mjs -var Empty4 = class extends CustomType { -}; -var Text2 = class extends CustomType { - constructor(inner) { - super(); - this.inner = inner; - } -}; -var Integer = class extends CustomType { - constructor(n) { - super(); - this.n = n; - } -}; -var FloatingPointNumber = class extends CustomType { - constructor(f) { - super(); - this.f = f; - } -}; -var Boolean = class extends CustomType { - constructor(b) { - super(); - this.b = b; - } -}; -var ScanError2 = class extends CustomType { - constructor(x0) { - super(); - this[0] = x0; - } -}; -var ParseError2 = class extends CustomType { - constructor(x0) { - super(); - this[0] = x0; - } -}; -var TypeError2 = class extends CustomType { - constructor(x0) { - super(); - this[0] = x0; - } -}; -var RuntimeError = class extends CustomType { - constructor(x0) { - super(); - this[0] = x0; + } + ); + } + ); } -}; -function convert_grid(input2) { +} + +// build/dev/javascript/squared_away/squared_away/lang.mjs +function interpret_grid(input2) { return fold2( input2, new$(), - (acc, key, val) => { - return insert( - acc, - key, - (() => { - let _pipe = val; - return nil_error(_pipe); - })() - ); + (acc, key, typed_expr) => { + if (!typed_expr.isOk()) { + let e = typed_expr[0]; + return insert(acc, key, new Error(e)); + } else { + let typed_expr$1 = typed_expr[0]; + let maybe_value = interpret(input2, typed_expr$1); + return insert(acc, key, maybe_value); + } } ); } @@ -3412,15 +3744,7 @@ function typecheck_grid(input2) { return insert(acc, key, new Error(e)); } else { let expr$1 = expr[0]; - let maybe_typed_expr = (() => { - let _pipe = typecheck(convert_grid(input2), expr$1); - return map_error( - _pipe, - (var0) => { - return new TypeError2(var0); - } - ); - })(); + let maybe_typed_expr = typecheck(input2, expr$1); return insert(acc, key, maybe_typed_expr); } } @@ -3436,16 +3760,20 @@ function parse_grid(input2) { return insert(acc, key, new Error(e)); } else { let toks$1 = toks[0]; - let expr = (() => { - let _pipe = parse3(toks$1); - return map_error( - _pipe, - (var0) => { - return new ParseError2(var0); - } - ); - })(); - return insert(acc, key, expr); + let expr = parse3(toks$1); + return insert( + acc, + key, + (() => { + let _pipe = expr; + return map_error( + _pipe, + (var0) => { + return new ParseError2(var0); + } + ); + })() + ); } } ); @@ -3468,243 +3796,6 @@ function scan_grid(input2) { } ); } -function interpret(loop$env, loop$expr) { - while (true) { - let env = loop$env; - let expr = loop$expr; - if (expr instanceof Empty3) { - return new Ok(new Empty4()); - } else if (expr instanceof Group2) { - let expr$1 = expr.expr; - loop$env = env; - loop$expr = expr$1; - } else if (expr instanceof StringLiteral3) { - let txt = expr.txt; - return new Ok(new Text2(txt)); - } else if (expr instanceof BooleanLiteral2) { - let b = expr.b; - return new Ok(new Boolean(b)); - } else if (expr instanceof IntegerLiteral3) { - let n = expr.n; - return new Ok(new Integer(n)); - } else if (expr instanceof FloatLiteral3) { - let f = expr.f; - return new Ok(new FloatingPointNumber(f)); - } else if (expr instanceof CellReference3) { - let cell_ref = expr.key; - let $ = get(env, cell_ref); - if ($.isOk() && !$[0].isOk()) { - let e = $[0][0]; - return new Error(e); - } else if (!$.isOk() && !$[0]) { - return new Error( - new RuntimeError( - "Uhhhh nil cell reference? I need to work out the semantics around Nil/Empty" - ) - ); - } else { - let expr$1 = $[0][0]; - loop$env = env; - loop$expr = expr$1; - } - } else if (expr instanceof UnaryOp2) { - let op = expr.op; - let expr$1 = expr.expr; - return try$( - interpret(env, expr$1), - (value2) => { - if (op instanceof Negate && value2 instanceof Integer) { - let n = value2.n; - return new Ok(new Integer(-n)); - } else if (op instanceof Negate && value2 instanceof FloatingPointNumber) { - let f = value2.f; - return new Ok(new FloatingPointNumber(negate(f))); - } else if (op instanceof Not && value2 instanceof Boolean) { - let b = value2.b; - return new Ok(new Boolean(!b)); - } else { - throw makeError( - "panic", - "squared_away/lang/interpreter", - 120, - "", - "These should be the only options if the typechecker is working", - {} - ); - } - } - ); - } else { - let lhs = expr.lhs; - let op = expr.op; - let rhs = expr.rhs; - return try$( - interpret(env, lhs), - (lhs2) => { - return try$( - interpret(env, rhs), - (rhs2) => { - if (lhs2 instanceof Integer && op instanceof Add && rhs2 instanceof Integer) { - let a = lhs2.n; - let b = rhs2.n; - return new Ok(new Integer(a + b)); - } else if (lhs2 instanceof Integer && op instanceof Subtract && rhs2 instanceof Integer) { - let a = lhs2.n; - let b = rhs2.n; - return new Ok(new Integer(a - b)); - } else if (lhs2 instanceof Integer && op instanceof Multiply && rhs2 instanceof Integer) { - let a = lhs2.n; - let b = rhs2.n; - return new Ok(new Integer(a * b)); - } else if (lhs2 instanceof Integer && op instanceof Divide && rhs2 instanceof Integer) { - let a = lhs2.n; - let b = rhs2.n; - return new Ok(new Integer(divideInt(a, b))); - } else if (lhs2 instanceof Integer && op instanceof EqualCheck && rhs2 instanceof Integer) { - let a = lhs2.n; - let b = rhs2.n; - return new Ok(new Boolean(a === b)); - } else if (lhs2 instanceof Integer && op instanceof NotEqualCheck && rhs2 instanceof Integer) { - let a = lhs2.n; - let b = rhs2.n; - return new Ok(new Boolean(a !== b)); - } else if (lhs2 instanceof Integer && op instanceof GreaterThanCheck && rhs2 instanceof Integer) { - let a = lhs2.n; - let b = rhs2.n; - return new Ok(new Boolean(a > b)); - } else if (lhs2 instanceof Integer && op instanceof GreaterThanOrEqualCheck && rhs2 instanceof Integer) { - let a = lhs2.n; - let b = rhs2.n; - return new Ok(new Boolean(a >= b)); - } else if (lhs2 instanceof Integer && op instanceof LessThanCheck && rhs2 instanceof Integer) { - let a = lhs2.n; - let b = rhs2.n; - return new Ok(new Boolean(a < b)); - } else if (lhs2 instanceof Integer && op instanceof LessThanOrEqualCheck && rhs2 instanceof Integer) { - let a = lhs2.n; - let b = rhs2.n; - return new Ok(new Boolean(a <= b)); - } else if (lhs2 instanceof FloatingPointNumber && op instanceof Add && rhs2 instanceof FloatingPointNumber) { - let a = lhs2.f; - let b = rhs2.f; - return new Ok(new FloatingPointNumber(a + b)); - } else if (lhs2 instanceof FloatingPointNumber && op instanceof Subtract && rhs2 instanceof FloatingPointNumber) { - let a = lhs2.f; - let b = rhs2.f; - return new Ok(new FloatingPointNumber(a - b)); - } else if (lhs2 instanceof FloatingPointNumber && op instanceof Multiply && rhs2 instanceof FloatingPointNumber) { - let a = lhs2.f; - let b = rhs2.f; - return new Ok(new FloatingPointNumber(a * b)); - } else if (lhs2 instanceof FloatingPointNumber && op instanceof Divide && rhs2 instanceof FloatingPointNumber) { - let a = lhs2.f; - let b = rhs2.f; - return new Ok(new FloatingPointNumber(divideFloat(a, b))); - } else if (lhs2 instanceof FloatingPointNumber && op instanceof EqualCheck && rhs2 instanceof FloatingPointNumber) { - let a = lhs2.f; - let b = rhs2.f; - return new Ok(new Boolean(a === b)); - } else if (lhs2 instanceof FloatingPointNumber && op instanceof NotEqualCheck && rhs2 instanceof FloatingPointNumber) { - let a = lhs2.f; - let b = rhs2.f; - return new Ok(new Boolean(a !== b)); - } else if (lhs2 instanceof FloatingPointNumber && op instanceof GreaterThanCheck && rhs2 instanceof FloatingPointNumber) { - let a = lhs2.f; - let b = rhs2.f; - return new Ok(new Boolean(a > b)); - } else if (lhs2 instanceof FloatingPointNumber && op instanceof GreaterThanOrEqualCheck && rhs2 instanceof FloatingPointNumber) { - let a = lhs2.f; - let b = rhs2.f; - return new Ok(new Boolean(a >= b)); - } else if (lhs2 instanceof FloatingPointNumber && op instanceof LessThanCheck && rhs2 instanceof FloatingPointNumber) { - let a = lhs2.f; - let b = rhs2.f; - return new Ok(new Boolean(a < b)); - } else if (lhs2 instanceof FloatingPointNumber && op instanceof LessThanOrEqualCheck && rhs2 instanceof FloatingPointNumber) { - let a = lhs2.f; - let b = rhs2.f; - return new Ok(new Boolean(a <= b)); - } else if (lhs2 instanceof Integer && op instanceof Power && rhs2 instanceof FloatingPointNumber) { - let a = lhs2.n; - let b = rhs2.f; - let $ = power3(a, b); - if (!$.isOk()) { - throw makeError( - "assignment_no_match", - "squared_away/lang/interpreter", - 169, - "", - "Assignment pattern did not match", - { value: $ } - ); - } - let p2 = $[0]; - return new Ok(new FloatingPointNumber(p2)); - } else if (lhs2 instanceof FloatingPointNumber && op instanceof Power && rhs2 instanceof FloatingPointNumber) { - let a = lhs2.f; - let b = rhs2.f; - let $ = power2(a, b); - if (!$.isOk()) { - throw makeError( - "assignment_no_match", - "squared_away/lang/interpreter", - 173, - "", - "Assignment pattern did not match", - { value: $ } - ); - } - let p2 = $[0]; - return new Ok(new FloatingPointNumber(p2)); - } else if (lhs2 instanceof Boolean && op instanceof And2 && rhs2 instanceof Boolean) { - let a = lhs2.b; - let b = rhs2.b; - return new Ok(new Boolean(a && b)); - } else if (lhs2 instanceof Boolean && op instanceof Or2 && rhs2 instanceof Boolean) { - let a = lhs2.b; - let b = rhs2.b; - return new Ok(new Boolean(a || b)); - } else if (lhs2 instanceof Boolean && op instanceof EqualCheck && rhs2 instanceof Boolean) { - let a = lhs2.b; - let b = rhs2.b; - return new Ok(new Boolean(a === b)); - } else if (lhs2 instanceof Boolean && op instanceof NotEqualCheck && rhs2 instanceof Boolean) { - let a = lhs2.b; - let b = rhs2.b; - return new Ok(new Boolean(a !== b)); - } else { - throw makeError( - "panic", - "squared_away/lang/interpreter", - 184, - "", - "these should be the only options if the typechecker is working properly", - {} - ); - } - } - ); - } - ); - } - } -} -function interpret_grid(input2) { - return fold2( - input2, - new$(), - (acc, key, typed_expr) => { - if (!typed_expr.isOk()) { - let e = typed_expr[0]; - return insert(acc, key, new Error(e)); - } else { - let typed_expr$1 = typed_expr[0]; - let maybe_value = interpret(input2, typed_expr$1); - return insert(acc, key, maybe_value); - } - } - ); -} // build/dev/javascript/squared_away/squared_away.mjs var Model = class extends CustomType { @@ -3849,10 +3940,18 @@ function view(model) { ) ]) ); - let active_cell_value = (() => { - let _pipe = get(model.value_grid, model.active_cell); - return unwrap(_pipe, new Ok(new Empty4())); - })(); + let $ = get(model.value_grid, model.active_cell); + if (!$.isOk()) { + throw makeError( + "assignment_no_match", + "squared_away", + 120, + "view", + "Assignment pattern did not match", + { value: $ } + ); + } + let active_cell_value = $[0]; return div( toList([]), toList([ @@ -3875,7 +3974,7 @@ function main() { throw makeError( "assignment_no_match", "squared_away", - 16, + 17, "main", "Assignment pattern did not match", { value: $ } diff --git a/src/squared_away.gleam b/src/squared_away.gleam index 266a2ac..edec962 100644 --- a/src/squared_away.gleam +++ b/src/squared_away.gleam @@ -1,7 +1,6 @@ import gleam/dict.{type Dict} import gleam/int import gleam/list -import gleam/result import gleam/string import lustre import lustre/attribute @@ -9,7 +8,9 @@ import lustre/effect import lustre/element import lustre/element/html import lustre/event -import squared_away/lang/interpreter +import squared_away/lang +import squared_away/lang/error +import squared_away/lang/interpreter/value pub fn main() { let app = lustre.application(init, update, view) @@ -23,10 +24,7 @@ type Model { Model( active_cell: String, src_grid: Dict(String, String), - value_grid: Dict( - String, - Result(interpreter.Value, interpreter.InterpretError), - ), + value_grid: Dict(String, Result(value.Value, error.CompileError)), ) } @@ -58,10 +56,10 @@ type Msg { } fn update_grid(model: Model) -> Model { - let scanned = interpreter.scan_grid(model.src_grid) - let parsed = interpreter.parse_grid(scanned) - let typechecked = interpreter.typecheck_grid(parsed) - let value_grid = interpreter.interpret_grid(typechecked) + let scanned = lang.scan_grid(model.src_grid) + let parsed = lang.parse_grid(scanned) + let typechecked = lang.typecheck_grid(parsed) + let value_grid = lang.interpret_grid(typechecked) Model(..model, value_grid:) } @@ -117,9 +115,10 @@ fn view(model: Model) -> element.Element(Msg) { ]), ]) - let active_cell_value = + // We filled the grid ourself so this shouldn't ever panic, if it does it's a + // bug and I'd rather find it sooner. + let assert Ok(active_cell_value) = dict.get(model.value_grid, model.active_cell) - |> result.unwrap(or: Ok(interpreter.Empty)) html.div([], [ html.div([], [grid]), diff --git a/src/squared_away/lang.gleam b/src/squared_away/lang.gleam index 4429277..e1909ae 100644 --- a/src/squared_away/lang.gleam +++ b/src/squared_away/lang.gleam @@ -1,9 +1,58 @@ -//// The form formula language -//// term t := -//// Integer 69 -//// Float 420.69 -//// Conditional if t then t else t -//// Bool TRUE | FALSE -//// Date September 10 1997 -//// Addition + +import gleam/dict +import gleam/result +import squared_away/lang/error +import squared_away/lang/interpreter +import squared_away/lang/interpreter/value +import squared_away/lang/parser +import squared_away/lang/parser/expr +import squared_away/lang/scanner +import squared_away/lang/scanner/token +import squared_away/lang/typechecker +import squared_away/lang/typechecker/typed_expr +pub fn interpret_grid( + input: dict.Dict(String, Result(typed_expr.TypedExpr, error.CompileError)), +) -> dict.Dict(String, Result(value.Value, error.CompileError)) { + use acc, key, typed_expr <- dict.fold(input, dict.new()) + case typed_expr { + Error(e) -> dict.insert(acc, key, Error(e)) + Ok(typed_expr) -> { + let maybe_value = interpreter.interpret(input, typed_expr) + dict.insert(acc, key, maybe_value) + } + } +} + +pub fn typecheck_grid( + input: dict.Dict(String, Result(expr.Expr, error.CompileError)), +) -> dict.Dict(String, Result(typed_expr.TypedExpr, error.CompileError)) { + use acc, key, expr <- dict.fold(input, dict.new()) + case expr { + Error(e) -> dict.insert(acc, key, Error(e)) + Ok(expr) -> { + let maybe_typed_expr = typechecker.typecheck(input, expr) + dict.insert(acc, key, maybe_typed_expr) + } + } +} + +pub fn parse_grid( + input: dict.Dict(String, Result(List(token.Token), error.CompileError)), +) -> dict.Dict(String, Result(expr.Expr, error.CompileError)) { + use acc, key, toks <- dict.fold(input, dict.new()) + case toks { + Error(e) -> dict.insert(acc, key, Error(e)) + Ok(toks) -> { + let expr = parser.parse(toks) + dict.insert(acc, key, expr |> result.map_error(error.ParseError)) + } + } +} + +pub fn scan_grid( + input: dict.Dict(String, String), +) -> dict.Dict(String, Result(List(token.Token), error.CompileError)) { + use acc, key, src <- dict.fold(input, dict.new()) + let maybe_scanned = scanner.scan(src) |> result.map_error(error.ScanError) + dict.insert(acc, key, maybe_scanned) +} diff --git a/src/squared_away/lang/error.gleam b/src/squared_away/lang/error.gleam new file mode 100644 index 0000000..2bdaa8d --- /dev/null +++ b/src/squared_away/lang/error.gleam @@ -0,0 +1,11 @@ +import squared_away/lang/interpreter/runtime_error +import squared_away/lang/parser/parse_error +import squared_away/lang/scanner/scan_error +import squared_away/lang/typechecker/type_error + +pub type CompileError { + ScanError(scan_error.ScanError) + ParseError(parse_error.ParseError) + TypeError(type_error.TypeError) + RuntimeError(runtime_error.RuntimeError) +} diff --git a/src/squared_away/lang/interpreter.gleam b/src/squared_away/lang/interpreter.gleam index 41d411e..50142c0 100644 --- a/src/squared_away/lang/interpreter.gleam +++ b/src/squared_away/lang/interpreter.gleam @@ -1,184 +1,122 @@ -import gleam/bool import gleam/dict import gleam/float import gleam/int import gleam/result -import squared_away/lang/parser -import squared_away/lang/scanner -import squared_away/lang/typechecker - -pub type Value { - Empty - Text(inner: String) - Integer(n: Int) - FloatingPointNumber(f: Float) - Boolean(b: Bool) -} - -pub fn value_to_string(fv: Value) -> String { - case fv { - Empty -> "" - Text(t) -> t - Integer(n) -> int.to_string(n) - Boolean(b) -> bool.to_string(b) - FloatingPointNumber(f) -> float.to_string(f) - } -} - -pub type InterpretError { - ScanError(scanner.ScanError) - ParseError(parser.ParseError) - TypeError(typechecker.TypeError) - RuntimeError(String) -} - -fn convert_grid( - input: dict.Dict(String, Result(a, b)), -) -> dict.Dict(String, Result(a, Nil)) { - use acc, key, val <- dict.fold(input, dict.new()) - dict.insert(acc, key, val |> result.nil_error) -} - -pub fn interpret_grid( - input: dict.Dict(String, Result(typechecker.TypedExpr, InterpretError)), -) -> dict.Dict(String, Result(Value, InterpretError)) { - use acc, key, typed_expr <- dict.fold(input, dict.new()) - case typed_expr { - Error(e) -> dict.insert(acc, key, Error(e)) - Ok(typed_expr) -> { - let maybe_value = interpret(input, typed_expr) - dict.insert(acc, key, maybe_value) - } - } -} - -pub fn typecheck_grid( - input: dict.Dict(String, Result(parser.Expr, InterpretError)), -) -> dict.Dict(String, Result(typechecker.TypedExpr, InterpretError)) { - use acc, key, expr <- dict.fold(input, dict.new()) - case expr { - Error(e) -> dict.insert(acc, key, Error(e)) - Ok(expr) -> { - let maybe_typed_expr = - typechecker.typecheck(convert_grid(input), expr) - |> result.map_error(TypeError) - dict.insert(acc, key, maybe_typed_expr) - } - } -} - -pub fn parse_grid( - input: dict.Dict(String, Result(List(scanner.Token), InterpretError)), -) -> dict.Dict(String, Result(parser.Expr, InterpretError)) { - use acc, key, toks <- dict.fold(input, dict.new()) - case toks { - Error(e) -> dict.insert(acc, key, Error(e)) - Ok(toks) -> { - let expr = parser.parse(toks) |> result.map_error(ParseError) - dict.insert(acc, key, expr) - } - } -} - -pub fn scan_grid( - input: dict.Dict(String, String), -) -> dict.Dict(String, Result(List(scanner.Token), InterpretError)) { - use acc, key, src <- dict.fold(input, dict.new()) - let maybe_scanned = scanner.scan(src) |> result.map_error(ScanError) - dict.insert(acc, key, maybe_scanned) -} +import squared_away/lang/error +import squared_away/lang/interpreter/value +import squared_away/lang/parser/expr +import squared_away/lang/typechecker/typed_expr pub fn interpret( - env: dict.Dict(String, Result(typechecker.TypedExpr, InterpretError)), - expr: typechecker.TypedExpr, -) -> Result(Value, InterpretError) { + env: dict.Dict(String, Result(typed_expr.TypedExpr, error.CompileError)), + expr: typed_expr.TypedExpr, +) -> Result(value.Value, error.CompileError) { case expr { - typechecker.Empty(_) -> Ok(Empty) - typechecker.Group(_, expr) -> interpret(env, expr) - typechecker.StringLiteral(_, txt) -> Ok(Text(txt)) - typechecker.BooleanLiteral(_, b) -> Ok(Boolean(b)) - typechecker.IntegerLiteral(_, n) -> Ok(Integer(n)) - typechecker.FloatLiteral(_, f) -> Ok(FloatingPointNumber(f)) - typechecker.CellReference(_, cell_ref) -> { - case dict.get(env, cell_ref) { - Ok(Error(e)) -> Error(e) - Error(Nil) -> - Error(RuntimeError( - "Uhhhh nil cell reference? I need to work out the semantics around Nil/Empty", - )) - Ok(Ok(expr)) -> interpret(env, expr) + typed_expr.Empty(_) -> Ok(value.Empty) + typed_expr.Group(_, expr) -> interpret(env, expr) + typed_expr.StringLiteral(_, txt) -> Ok(value.Text(txt)) + typed_expr.BooleanLiteral(_, b) -> Ok(value.Boolean(b)) + typed_expr.IntegerLiteral(_, n) -> Ok(value.Integer(n)) + typed_expr.FloatLiteral(_, f) -> Ok(value.FloatingPointNumber(f)) + typed_expr.CellReference(_, cell_ref) -> { + let assert Ok(maybe_expr) = dict.get(env, cell_ref) + case maybe_expr { + Error(e) -> Error(e) + Ok(expr) -> interpret(env, expr) } } - typechecker.UnaryOp(_, op, expr) -> { + typed_expr.UnaryOp(_, op, expr) -> { use value <- result.try(interpret(env, expr)) case op, value { - parser.Negate, Integer(n) -> Ok(Integer(-n)) - parser.Negate, FloatingPointNumber(f) -> - Ok(FloatingPointNumber(float.negate(f))) - parser.Not, Boolean(b) -> Ok(Boolean(!b)) + expr.Negate, value.Integer(n) -> Ok(value.Integer(-n)) + expr.Negate, value.FloatingPointNumber(f) -> + Ok(value.FloatingPointNumber(float.negate(f))) + expr.Not, value.Boolean(b) -> Ok(value.Boolean(!b)) _, _ -> panic as "These should be the only options if the typechecker is working" } } - typechecker.BinaryOp(_, lhs, op, rhs) -> { + typed_expr.BinaryOp(_, lhs, op, rhs) -> { use lhs <- result.try(interpret(env, lhs)) use rhs <- result.try(interpret(env, rhs)) case lhs, op, rhs { // Integer operations - Integer(a), parser.Add, Integer(b) -> Ok(Integer(a + b)) - Integer(a), parser.Subtract, Integer(b) -> Ok(Integer(a - b)) - Integer(a), parser.Multiply, Integer(b) -> Ok(Integer(a * b)) - Integer(a), parser.Divide, Integer(b) -> Ok(Integer(a / b)) - Integer(a), parser.EqualCheck, Integer(b) -> Ok(Boolean(a == b)) - Integer(a), parser.NotEqualCheck, Integer(b) -> Ok(Boolean(a != b)) - Integer(a), parser.GreaterThanCheck, Integer(b) -> Ok(Boolean(a > b)) - Integer(a), parser.GreaterThanOrEqualCheck, Integer(b) -> - Ok(Boolean(a >= b)) - Integer(a), parser.LessThanCheck, Integer(b) -> Ok(Boolean(a < b)) - Integer(a), parser.LessThanOrEqualCheck, Integer(b) -> - Ok(Boolean(a <= b)) + value.Integer(a), expr.Add, value.Integer(b) -> Ok(value.Integer(a + b)) + value.Integer(a), expr.Subtract, value.Integer(b) -> + Ok(value.Integer(a - b)) + value.Integer(a), expr.Multiply, value.Integer(b) -> + Ok(value.Integer(a * b)) + value.Integer(a), expr.Divide, value.Integer(b) -> + Ok(value.Integer(a / b)) + value.Integer(a), expr.EqualCheck, value.Integer(b) -> + Ok(value.Boolean(a == b)) + value.Integer(a), expr.NotEqualCheck, value.Integer(b) -> + Ok(value.Boolean(a != b)) + value.Integer(a), expr.GreaterThanCheck, value.Integer(b) -> + Ok(value.Boolean(a > b)) + value.Integer(a), expr.GreaterThanOrEqualCheck, value.Integer(b) -> + Ok(value.Boolean(a >= b)) + value.Integer(a), expr.LessThanCheck, value.Integer(b) -> + Ok(value.Boolean(a < b)) + value.Integer(a), expr.LessThanOrEqualCheck, value.Integer(b) -> + Ok(value.Boolean(a <= b)) // Float operations - FloatingPointNumber(a), parser.Add, FloatingPointNumber(b) -> - Ok(FloatingPointNumber(a +. b)) - FloatingPointNumber(a), parser.Subtract, FloatingPointNumber(b) -> - Ok(FloatingPointNumber(a -. b)) - FloatingPointNumber(a), parser.Multiply, FloatingPointNumber(b) -> - Ok(FloatingPointNumber(a *. b)) - FloatingPointNumber(a), parser.Divide, FloatingPointNumber(b) -> - Ok(FloatingPointNumber(a /. b)) - FloatingPointNumber(a), parser.EqualCheck, FloatingPointNumber(b) -> - Ok(Boolean(a == b)) - FloatingPointNumber(a), parser.NotEqualCheck, FloatingPointNumber(b) -> - Ok(Boolean(a != b)) - FloatingPointNumber(a), parser.GreaterThanCheck, FloatingPointNumber(b) -> - Ok(Boolean(a >. b)) - FloatingPointNumber(a), - parser.GreaterThanOrEqualCheck, - FloatingPointNumber(b) - -> Ok(Boolean(a >=. b)) - FloatingPointNumber(a), parser.LessThanCheck, FloatingPointNumber(b) -> - Ok(Boolean(a <. b)) - FloatingPointNumber(a), - parser.LessThanOrEqualCheck, - FloatingPointNumber(b) - -> Ok(Boolean(a <=. b)) + value.FloatingPointNumber(a), expr.Add, value.FloatingPointNumber(b) -> + Ok(value.FloatingPointNumber(a +. b)) + value.FloatingPointNumber(a), + expr.Subtract, + value.FloatingPointNumber(b) + -> Ok(value.FloatingPointNumber(a -. b)) + value.FloatingPointNumber(a), + expr.Multiply, + value.FloatingPointNumber(b) + -> Ok(value.FloatingPointNumber(a *. b)) + value.FloatingPointNumber(a), expr.Divide, value.FloatingPointNumber(b) -> + Ok(value.FloatingPointNumber(a /. b)) + value.FloatingPointNumber(a), + expr.EqualCheck, + value.FloatingPointNumber(b) + -> Ok(value.Boolean(a == b)) + value.FloatingPointNumber(a), + expr.NotEqualCheck, + value.FloatingPointNumber(b) + -> Ok(value.Boolean(a != b)) + value.FloatingPointNumber(a), + expr.GreaterThanCheck, + value.FloatingPointNumber(b) + -> Ok(value.Boolean(a >. b)) + value.FloatingPointNumber(a), + expr.GreaterThanOrEqualCheck, + value.FloatingPointNumber(b) + -> Ok(value.Boolean(a >=. b)) + value.FloatingPointNumber(a), + expr.LessThanCheck, + value.FloatingPointNumber(b) + -> Ok(value.Boolean(a <. b)) + value.FloatingPointNumber(a), + expr.LessThanOrEqualCheck, + value.FloatingPointNumber(b) + -> Ok(value.Boolean(a <=. b)) // Exponents - Integer(a), parser.Power, FloatingPointNumber(b) -> { + value.Integer(a), expr.Power, value.FloatingPointNumber(b) -> { let assert Ok(p) = int.power(a, b) - Ok(FloatingPointNumber(p)) + Ok(value.FloatingPointNumber(p)) } - FloatingPointNumber(a), parser.Power, FloatingPointNumber(b) -> { + value.FloatingPointNumber(a), expr.Power, value.FloatingPointNumber(b) -> { let assert Ok(p) = float.power(a, b) - Ok(FloatingPointNumber(p)) + Ok(value.FloatingPointNumber(p)) } // Boolean operations - Boolean(a), parser.And, Boolean(b) -> Ok(Boolean(a && b)) - Boolean(a), parser.Or, Boolean(b) -> Ok(Boolean(a || b)) - Boolean(a), parser.EqualCheck, Boolean(b) -> Ok(Boolean(a == b)) - Boolean(a), parser.NotEqualCheck, Boolean(b) -> Ok(Boolean(a != b)) + value.Boolean(a), expr.And, value.Boolean(b) -> + Ok(value.Boolean(a && b)) + value.Boolean(a), expr.Or, value.Boolean(b) -> Ok(value.Boolean(a || b)) + value.Boolean(a), expr.EqualCheck, value.Boolean(b) -> + Ok(value.Boolean(a == b)) + value.Boolean(a), expr.NotEqualCheck, value.Boolean(b) -> + Ok(value.Boolean(a != b)) _, _, _ -> panic as "these should be the only options if the typechecker is working properly" diff --git a/src/squared_away/lang/interpreter/runtime_error.gleam b/src/squared_away/lang/interpreter/runtime_error.gleam new file mode 100644 index 0000000..796e26b --- /dev/null +++ b/src/squared_away/lang/interpreter/runtime_error.gleam @@ -0,0 +1,3 @@ +pub type RuntimeError { + RuntimeError(context: String) +} diff --git a/src/squared_away/lang/interpreter/value.gleam b/src/squared_away/lang/interpreter/value.gleam new file mode 100644 index 0000000..5ab10dc --- /dev/null +++ b/src/squared_away/lang/interpreter/value.gleam @@ -0,0 +1,21 @@ +import gleam/bool +import gleam/float +import gleam/int + +pub type Value { + Empty + Text(inner: String) + Integer(n: Int) + FloatingPointNumber(f: Float) + Boolean(b: Bool) +} + +pub fn value_to_string(fv: Value) -> String { + case fv { + Empty -> "" + Text(t) -> t + Integer(n) -> int.to_string(n) + Boolean(b) -> bool.to_string(b) + FloatingPointNumber(f) -> float.to_string(f) + } +} diff --git a/src/squared_away/lang/parser.gleam b/src/squared_away/lang/parser.gleam index d60103c..925cc31 100644 --- a/src/squared_away/lang/parser.gleam +++ b/src/squared_away/lang/parser.gleam @@ -1,180 +1,151 @@ -import gleam/dict import gleam/result import gleam/string -import squared_away/lang/scanner +import squared_away/lang/parser/expr +import squared_away/lang/parser/parse_error +import squared_away/lang/scanner/token -pub type Expr { - Empty - FloatLiteral(f: Float) - StringLiteral(txt: String) - IntegerLiteral(n: Int) - CellReference(key: String) - BooleanLiteral(val: Bool) - UnaryOp(op: UnaryOpKind, expr: Expr) - BinaryOp(lhs: Expr, op: BinaryOpKind, rhs: Expr) - Group(inner: Expr) -} - -pub type BinaryOpKind { - Add - Subtract - Multiply - Divide - Power - EqualCheck - NotEqualCheck - LessThanCheck - LessThanOrEqualCheck - GreaterThanCheck - GreaterThanOrEqualCheck - And - Or -} - -pub type UnaryOpKind { - Negate - Not -} - -pub type ParseError { - ParseError(context: String) -} - -pub fn parse(tokens: List(scanner.Token)) -> Result(Expr, ParseError) { +pub fn parse( + tokens: List(token.Token), +) -> Result(expr.Expr, parse_error.ParseError) { use #(expr, rest) <- result.try(do_parse(tokens)) case rest { [] -> Ok(expr) - _ -> Error(ParseError("After parsing there were leftover tokens")) + _ -> + Error(parse_error.ParseError("After parsing there were leftover tokens")) } } fn do_parse( - tokens: List(scanner.Token), -) -> Result(#(Expr, List(scanner.Token)), ParseError) { + tokens: List(token.Token), +) -> Result(#(expr.Expr, List(token.Token)), parse_error.ParseError) { case tokens { - [] -> Ok(#(Empty, [])) + [] -> Ok(#(expr.Empty, [])) // Let's do the single token patterns first - [scanner.StringLiteral(str), ..rest] -> { + [token.StringLiteral(str), ..rest] -> { case try_parse_binary_ops(rest) { - Ok(#(op, rest)) -> Ok(#(op(StringLiteral(str)), rest)) - Error(_) -> Ok(#(StringLiteral(str), rest)) + Ok(#(op, rest)) -> Ok(#(op(expr.StringLiteral(str)), rest)) + Error(_) -> Ok(#(expr.StringLiteral(str), rest)) } } - [scanner.IntegerLiteral(n), ..rest] -> { + [token.IntegerLiteral(n), ..rest] -> { case try_parse_binary_ops(rest) { - Ok(#(op, rest)) -> Ok(#(op(IntegerLiteral(n)), rest)) - Error(_) -> Ok(#(IntegerLiteral(n), rest)) + Ok(#(op, rest)) -> Ok(#(op(expr.IntegerLiteral(n)), rest)) + Error(_) -> Ok(#(expr.IntegerLiteral(n), rest)) } } - [scanner.FloatLiteral(f), ..rest] -> { + [token.FloatLiteral(f), ..rest] -> { case try_parse_binary_ops(rest) { - Ok(#(op, rest)) -> Ok(#(op(FloatLiteral(f)), rest)) - Error(_) -> Ok(#(FloatLiteral(f), rest)) + Ok(#(op, rest)) -> Ok(#(op(expr.FloatLiteral(f)), rest)) + Error(_) -> Ok(#(expr.FloatLiteral(f), rest)) } } - [scanner.CellReference(key), ..rest] -> { + [token.CellReference(key), ..rest] -> { case try_parse_binary_ops(rest) { - Ok(#(op, rest)) -> Ok(#(op(CellReference(key)), rest)) - Error(_) -> Ok(#(CellReference(key), rest)) + Ok(#(op, rest)) -> Ok(#(op(expr.CellReference(key)), rest)) + Error(_) -> Ok(#(expr.CellReference(key), rest)) } } - [scanner.TrueToken, ..rest] -> { + [token.TrueToken, ..rest] -> { case try_parse_binary_ops(rest) { - Ok(#(op, rest)) -> Ok(#(op(BooleanLiteral(True)), rest)) - Error(_) -> Ok(#(BooleanLiteral(True), rest)) + Ok(#(op, rest)) -> Ok(#(op(expr.BooleanLiteral(True)), rest)) + Error(_) -> Ok(#(expr.BooleanLiteral(True), rest)) } } - [scanner.FalseToken, ..rest] -> { + [token.FalseToken, ..rest] -> { case try_parse_binary_ops(rest) { - Ok(#(op, rest)) -> Ok(#(op(BooleanLiteral(False)), rest)) - Error(_) -> Ok(#(BooleanLiteral(False), rest)) + Ok(#(op, rest)) -> Ok(#(op(expr.BooleanLiteral(False)), rest)) + Error(_) -> Ok(#(expr.BooleanLiteral(False), rest)) } } // Unary Ops - [scanner.Minus, ..rest] -> { + [token.Minus, ..rest] -> { use #(parsed_remainder, rest) <- result.try(do_parse(rest)) - Ok(#(UnaryOp(Negate, parsed_remainder), rest)) + Ok(#(expr.UnaryOp(expr.Negate, parsed_remainder), rest)) } - [scanner.Bang, ..rest] -> { + [token.Bang, ..rest] -> { use #(parsed_remainder, rest) <- result.try(do_parse(rest)) - Ok(#(UnaryOp(Not, parsed_remainder), rest)) + Ok(#(expr.UnaryOp(expr.Not, parsed_remainder), rest)) } - // Group - [scanner.LParen, ..rest] -> { + // Groupexpr. + [token.LParen, ..rest] -> { use #(body, rest) <- result.try(do_parse(rest)) case rest { - [scanner.RParen, ..rest] -> { + [token.RParen, ..rest] -> { case try_parse_binary_ops(rest) { - Ok(#(op, rest)) -> Ok(#(op(Group(body)), rest)) - Error(_) -> Ok(#(Group(body), rest)) + Ok(#(op, rest)) -> Ok(#(op(expr.Group(body)), rest)) + Error(_) -> Ok(#(expr.Group(body), rest)) } } - _ -> Error(ParseError("missing closing parentheses")) + _ -> Error(parse_error.ParseError("missing closing parentheses")) } } - [x, ..] -> Error(ParseError("Unexpected token: " <> string.inspect(x))) + [x, ..] -> + Error(parse_error.ParseError("Unexpected token: " <> string.inspect(x))) } } fn try_parse_binary_ops( - tokens: List(scanner.Token), -) -> Result(#(fn(Expr) -> Expr, List(scanner.Token)), ParseError) { + tokens: List(token.Token), +) -> Result( + #(fn(expr.Expr) -> expr.Expr, List(token.Token)), + parse_error.ParseError, +) { case tokens { - [scanner.Plus, ..rest] -> { + [token.Plus, ..rest] -> { use #(rhs, rest) <- result.try(do_parse(rest)) - Ok(#(BinaryOp(_, Add, rhs), rest)) + Ok(#(expr.BinaryOp(_, expr.Add, rhs), rest)) } - [scanner.Minus, ..rest] -> { + [token.Minus, ..rest] -> { use #(rhs, rest) <- result.try(do_parse(rest)) - Ok(#(BinaryOp(_, Subtract, rhs), rest)) + Ok(#(expr.BinaryOp(_, expr.Subtract, rhs), rest)) } - [scanner.Star, ..rest] -> { + [token.Star, ..rest] -> { use #(rhs, rest) <- result.try(do_parse(rest)) - Ok(#(BinaryOp(_, Multiply, rhs), rest)) + Ok(#(expr.BinaryOp(_, expr.Multiply, rhs), rest)) } - [scanner.Div, ..rest] -> { + [token.Div, ..rest] -> { use #(rhs, rest) <- result.try(do_parse(rest)) - Ok(#(BinaryOp(_, Divide, rhs), rest)) + Ok(#(expr.BinaryOp(_, expr.Divide, rhs), rest)) } - [scanner.StarStar, ..rest] -> { + [token.StarStar, ..rest] -> { use #(rhs, rest) <- result.try(do_parse(rest)) - Ok(#(BinaryOp(_, Power, rhs), rest)) + Ok(#(expr.BinaryOp(_, expr.Power, rhs), rest)) } - [scanner.BangEqual, ..rest] -> { + [token.BangEqual, ..rest] -> { use #(rhs, rest) <- result.try(do_parse(rest)) - Ok(#(BinaryOp(_, NotEqualCheck, rhs), rest)) + Ok(#(expr.BinaryOp(_, expr.NotEqualCheck, rhs), rest)) } - [scanner.EqualEqual, ..rest] -> { + [token.EqualEqual, ..rest] -> { use #(rhs, rest) <- result.try(do_parse(rest)) - Ok(#(BinaryOp(_, EqualCheck, rhs), rest)) + Ok(#(expr.BinaryOp(_, expr.EqualCheck, rhs), rest)) } - [scanner.LessEqual, ..rest] -> { + [token.LessEqual, ..rest] -> { use #(rhs, rest) <- result.try(do_parse(rest)) - Ok(#(BinaryOp(_, LessThanOrEqualCheck, rhs), rest)) + Ok(#(expr.BinaryOp(_, expr.LessThanOrEqualCheck, rhs), rest)) } - [scanner.Less, ..rest] -> { + [token.Less, ..rest] -> { use #(rhs, rest) <- result.try(do_parse(rest)) - Ok(#(BinaryOp(_, LessThanCheck, rhs), rest)) + Ok(#(expr.BinaryOp(_, expr.LessThanCheck, rhs), rest)) } - [scanner.GreaterEqual, ..rest] -> { + [token.GreaterEqual, ..rest] -> { use #(rhs, rest) <- result.try(do_parse(rest)) - Ok(#(BinaryOp(_, GreaterThanOrEqualCheck, rhs), rest)) + Ok(#(expr.BinaryOp(_, expr.GreaterThanOrEqualCheck, rhs), rest)) } - [scanner.Greater, ..rest] -> { + [token.Greater, ..rest] -> { use #(rhs, rest) <- result.try(do_parse(rest)) - Ok(#(BinaryOp(_, GreaterThanCheck, rhs), rest)) + Ok(#(expr.BinaryOp(_, expr.GreaterThanCheck, rhs), rest)) } - [scanner.And, ..rest] -> { + [token.And, ..rest] -> { use #(rhs, rest) <- result.try(do_parse(rest)) - Ok(#(BinaryOp(_, And, rhs), rest)) + Ok(#(expr.BinaryOp(_, expr.And, rhs), rest)) } - [scanner.Or, ..rest] -> { + [token.Or, ..rest] -> { use #(rhs, rest) <- result.try(do_parse(rest)) - Ok(#(BinaryOp(_, Or, rhs), rest)) + Ok(#(expr.BinaryOp(_, expr.Or, rhs), rest)) } - _ -> Error(ParseError("Not a binary operation")) + _ -> Error(parse_error.ParseError("Not a binary operation")) } } diff --git a/src/squared_away/lang/parser/expr.gleam b/src/squared_away/lang/parser/expr.gleam new file mode 100644 index 0000000..1dcd8f1 --- /dev/null +++ b/src/squared_away/lang/parser/expr.gleam @@ -0,0 +1,32 @@ +pub type Expr { + Empty + FloatLiteral(f: Float) + StringLiteral(txt: String) + IntegerLiteral(n: Int) + CellReference(key: String) + BooleanLiteral(val: Bool) + UnaryOp(op: UnaryOpKind, expr: Expr) + BinaryOp(lhs: Expr, op: BinaryOpKind, rhs: Expr) + Group(inner: Expr) +} + +pub type BinaryOpKind { + Add + Subtract + Multiply + Divide + Power + EqualCheck + NotEqualCheck + LessThanCheck + LessThanOrEqualCheck + GreaterThanCheck + GreaterThanOrEqualCheck + And + Or +} + +pub type UnaryOpKind { + Negate + Not +} diff --git a/src/squared_away/lang/parser/parse_error.gleam b/src/squared_away/lang/parser/parse_error.gleam new file mode 100644 index 0000000..fb99f35 --- /dev/null +++ b/src/squared_away/lang/parser/parse_error.gleam @@ -0,0 +1,8 @@ +pub type ParseError { + ParseError(context: String) +} + +pub fn to_string(pe: ParseError) -> String { + let ParseError(c) = pe + c +} diff --git a/src/squared_away/lang/scanner.gleam b/src/squared_away/lang/scanner.gleam index b69aa54..74cd7d7 100644 --- a/src/squared_away/lang/scanner.gleam +++ b/src/squared_away/lang/scanner.gleam @@ -1,93 +1,46 @@ -import gleam/dict import gleam/float import gleam/int import gleam/list import gleam/result import gleam/string -pub type Token { - /// +, addition op for integers - Plus - /// -, subtraction op for integers - Minus - /// * - Star - /// / - Div - /// ** - StarStar - /// = - Equal - /// == - EqualEqual - /// != - BangEqual - /// ! - Bang - /// < - Less - /// <= - LessEqual - /// > - Greater - /// >= - GreaterEqual - /// 6, 73 - IntegerLiteral(n: Int) - /// 1.0, 6.87 - FloatLiteral(f: Float) - /// True - TrueToken - /// False - FalseToken - /// && - And - /// || - Or - /// ( - LParen - /// ) - RParen - /// Cell Reference A3, XX532 - CellReference(key: String) - /// Anything not starting with an = in a cell is a string literal - StringLiteral(String) -} - -pub type ScanError { - ScanError -} +import squared_away/lang/scanner/scan_error +import squared_away/lang/scanner/token -pub fn scan(src: String) -> Result(List(Token), ScanError) { +pub fn scan(src: String) -> Result(List(token.Token), scan_error.ScanError) { case string.trim(src) { "" -> Ok([]) "=" <> rest -> do_scan(rest |> string.trim_left, []) - _ -> Ok([StringLiteral(src)]) + _ -> Ok([token.StringLiteral(src)]) } } -fn do_scan(src: String, acc: List(Token)) -> Result(List(Token), ScanError) { +fn do_scan( + src: String, + acc: List(token.Token), +) -> Result(List(token.Token), scan_error.ScanError) { case src { "" -> Ok(acc |> list.reverse) - "TRUE" <> rest -> do_scan(string.trim_left(rest), [TrueToken, ..acc]) - "FALSE" <> rest -> do_scan(string.trim_left(rest), [FalseToken, ..acc]) - "&&" <> rest -> do_scan(string.trim_left(rest), [And, ..acc]) - "||" <> rest -> do_scan(string.trim_left(rest), [Or, ..acc]) - "**" <> rest -> do_scan(string.trim_left(rest), [StarStar, ..acc]) - "==" <> rest -> do_scan(string.trim_left(rest), [EqualEqual, ..acc]) - "!=" <> rest -> do_scan(string.trim_left(rest), [BangEqual, ..acc]) - "<=" <> rest -> do_scan(string.trim_left(rest), [LessEqual, ..acc]) - ">=" <> rest -> do_scan(string.trim_left(rest), [GreaterEqual, ..acc]) - "+" <> rest -> do_scan(string.trim_left(rest), [Plus, ..acc]) - "-" <> rest -> do_scan(string.trim_left(rest), [Minus, ..acc]) - "*" <> rest -> do_scan(string.trim_left(rest), [Star, ..acc]) - "/" <> rest -> do_scan(string.trim_left(rest), [Div, ..acc]) - "=" <> rest -> do_scan(string.trim_left(rest), [Equal, ..acc]) - "!" <> rest -> do_scan(string.trim_left(rest), [Bang, ..acc]) - "<" <> rest -> do_scan(string.trim_left(rest), [Less, ..acc]) - ">" <> rest -> do_scan(string.trim_left(rest), [Greater, ..acc]) - "(" <> rest -> do_scan(string.trim_left(rest), [LParen, ..acc]) - ")" <> rest -> do_scan(string.trim_left(rest), [RParen, ..acc]) + "TRUE" <> rest -> do_scan(string.trim_left(rest), [token.TrueToken, ..acc]) + "FALSE" <> rest -> + do_scan(string.trim_left(rest), [token.FalseToken, ..acc]) + "&&" <> rest -> do_scan(string.trim_left(rest), [token.And, ..acc]) + "||" <> rest -> do_scan(string.trim_left(rest), [token.Or, ..acc]) + "**" <> rest -> do_scan(string.trim_left(rest), [token.StarStar, ..acc]) + "==" <> rest -> do_scan(string.trim_left(rest), [token.EqualEqual, ..acc]) + "!=" <> rest -> do_scan(string.trim_left(rest), [token.BangEqual, ..acc]) + "<=" <> rest -> do_scan(string.trim_left(rest), [token.LessEqual, ..acc]) + ">=" <> rest -> do_scan(string.trim_left(rest), [token.GreaterEqual, ..acc]) + "+" <> rest -> do_scan(string.trim_left(rest), [token.Plus, ..acc]) + "-" <> rest -> do_scan(string.trim_left(rest), [token.Minus, ..acc]) + "*" <> rest -> do_scan(string.trim_left(rest), [token.Star, ..acc]) + "/" <> rest -> do_scan(string.trim_left(rest), [token.Div, ..acc]) + "=" <> rest -> do_scan(string.trim_left(rest), [token.Equal, ..acc]) + "!" <> rest -> do_scan(string.trim_left(rest), [token.Bang, ..acc]) + "<" <> rest -> do_scan(string.trim_left(rest), [token.Less, ..acc]) + ">" <> rest -> do_scan(string.trim_left(rest), [token.Greater, ..acc]) + "(" <> rest -> do_scan(string.trim_left(rest), [token.LParen, ..acc]) + ")" <> rest -> do_scan(string.trim_left(rest), [token.RParen, ..acc]) _ -> { case parse_integer(src, "") { Ok(#(n, rest)) -> { @@ -95,21 +48,24 @@ fn do_scan(src: String, acc: List(Token)) -> Result(List(Token), ScanError) { case rest { "." <> rest -> { use #(m, rest) <- result.try( - parse_integer(rest, "") |> result.replace_error(ScanError), + parse_integer(rest, "") + |> result.replace_error(scan_error.ScanError), ) let assert Ok(f) = float.parse(int.to_string(n) <> "." <> int.to_string(m)) - do_scan(string.trim_left(rest), [FloatLiteral(f), ..acc]) + do_scan(string.trim_left(rest), [token.FloatLiteral(f), ..acc]) } - _ -> do_scan(string.trim_left(rest), [IntegerLiteral(n), ..acc]) + _ -> + do_scan(string.trim_left(rest), [token.IntegerLiteral(n), ..acc]) } } Error(_) -> { use #(cell_ref, rest) <- result.try( - parse_cell_ref(src, "") |> result.replace_error(ScanError), + parse_cell_ref(src, "") + |> result.replace_error(scan_error.ScanError), ) - do_scan(string.trim_left(rest), [CellReference(cell_ref), ..acc]) + do_scan(string.trim_left(rest), [token.CellReference(cell_ref), ..acc]) } } } diff --git a/src/squared_away/lang/scanner/scan_error.gleam b/src/squared_away/lang/scanner/scan_error.gleam new file mode 100644 index 0000000..2f8d371 --- /dev/null +++ b/src/squared_away/lang/scanner/scan_error.gleam @@ -0,0 +1,9 @@ +pub type ScanError { + ScanError +} + +pub fn to_string(se: ScanError) { + case se { + ScanError -> "Scan Error" + } +} diff --git a/src/squared_away/lang/scanner/token.gleam b/src/squared_away/lang/scanner/token.gleam new file mode 100644 index 0000000..e126d3e --- /dev/null +++ b/src/squared_away/lang/scanner/token.gleam @@ -0,0 +1,48 @@ +pub type Token { + /// +, addition op for integers + Plus + /// -, subtraction op for integers + Minus + /// * + Star + /// / + Div + /// ** + StarStar + /// = + Equal + /// == + EqualEqual + /// != + BangEqual + /// ! + Bang + /// < + Less + /// <= + LessEqual + /// > + Greater + /// >= + GreaterEqual + /// 6, 73 + IntegerLiteral(n: Int) + /// 1.0, 6.87 + FloatLiteral(f: Float) + /// True + TrueToken + /// False + FalseToken + /// && + And + /// || + Or + /// ( + LParen + /// ) + RParen + /// Cell Reference A3, XX532 + CellReference(key: String) + /// Anything not starting with an = in a cell is a string literal + StringLiteral(String) +} diff --git a/src/squared_away/lang/typechecker.gleam b/src/squared_away/lang/typechecker.gleam index 3e32308..30408d2 100644 --- a/src/squared_away/lang/typechecker.gleam +++ b/src/squared_away/lang/typechecker.gleam @@ -1,125 +1,132 @@ import gleam/dict import gleam/result import gleam/string -import squared_away/lang/parser - -pub type TypedExpr { - Empty(type_: Typ) - FloatLiteral(type_: Typ, f: Float) - StringLiteral(type_: Typ, txt: String) - IntegerLiteral(type_: Typ, n: Int) - CellReference(type_: Typ, key: String) - BooleanLiteral(type_: Typ, b: Bool) - UnaryOp(type_: Typ, op: parser.UnaryOpKind, expr: TypedExpr) - BinaryOp(type_: Typ, lhs: TypedExpr, op: parser.BinaryOpKind, rhs: TypedExpr) - Group(type_: Typ, expr: TypedExpr) -} - -pub type Typ { - TNil - TFloat - TString - TInt - TBool -} - -pub type TypeError { - TypeError(context: String) -} +import squared_away/lang/error +import squared_away/lang/parser/expr +import squared_away/lang/typechecker/typ +import squared_away/lang/typechecker/type_error +import squared_away/lang/typechecker/typed_expr pub fn typecheck( - env: dict.Dict(String, Result(parser.Expr, Nil)), - expr: parser.Expr, -) -> Result(TypedExpr, TypeError) { + env: dict.Dict(String, Result(expr.Expr, error.CompileError)), + expr: expr.Expr, +) -> Result(typed_expr.TypedExpr, error.CompileError) { case expr { - parser.Empty -> Ok(Empty(type_: TNil)) - parser.StringLiteral(txt) -> Ok(StringLiteral(type_: TString, txt:)) - parser.BooleanLiteral(b) -> Ok(BooleanLiteral(type_: TBool, b:)) - parser.FloatLiteral(f) -> Ok(FloatLiteral(type_: TFloat, f:)) - parser.IntegerLiteral(n) -> Ok(IntegerLiteral(type_: TInt, n:)) - parser.Group(inner) -> { + expr.Empty -> Ok(typed_expr.Empty(type_: typ.TNil)) + expr.StringLiteral(txt) -> + Ok(typed_expr.StringLiteral(type_: typ.TString, txt:)) + expr.BooleanLiteral(b) -> + Ok(typed_expr.BooleanLiteral(type_: typ.TBool, b:)) + expr.FloatLiteral(f) -> Ok(typed_expr.FloatLiteral(type_: typ.TFloat, f:)) + expr.IntegerLiteral(n) -> Ok(typed_expr.IntegerLiteral(type_: typ.TInt, n:)) + expr.Group(inner) -> { use expr <- result.try(typecheck(env, inner)) - Ok(Group(type_: expr.type_, expr:)) + Ok(typed_expr.Group(type_: expr.type_, expr:)) } - parser.CellReference(key) -> { - let ref_expr = dict.get(env, key) |> result.flatten + expr.CellReference(key) -> { + let assert Ok(ref_expr) = dict.get(env, key) case ref_expr { - Error(Nil) -> - Error(TypeError( - "Do not have type for referenced cell: " <> string.inspect(key), - )) Ok(expr) -> { use expr <- result.try(typecheck(env, expr)) - Ok(CellReference(type_: expr.type_, key:)) + Ok(typed_expr.CellReference(type_: expr.type_, key:)) } + Error(e) -> Error(e) } } - parser.UnaryOp(op, expr) -> { + expr.UnaryOp(op, expr) -> { use expr <- result.try(typecheck(env, expr)) case op, expr.type_ { - parser.Negate, TInt | parser.Negate, TFloat -> - Ok(UnaryOp(type_: expr.type_, op:, expr:)) - parser.Not, TBool -> Ok(UnaryOp(type_: expr.type_, op:, expr:)) - _, _ -> Error(TypeError("Unexpected type and operator combination")) + expr.Negate, typ.TInt | expr.Negate, typ.TFloat -> + Ok(typed_expr.UnaryOp(type_: expr.type_, op:, expr:)) + expr.Not, typ.TBool -> + Ok(typed_expr.UnaryOp(type_: expr.type_, op:, expr:)) + _, _ -> + Error( + error.TypeError(type_error.TypeError( + "Unexpected type and operator combination", + )), + ) } } - parser.BinaryOp(lhs, op, rhs) -> { + expr.BinaryOp(lhs, op, rhs) -> { use lhs <- result.try(typecheck(env, lhs)) use rhs <- result.try(typecheck(env, rhs)) case lhs.type_, op, rhs.type_ { // Addition - TFloat, parser.Add, TFloat -> - Ok(BinaryOp(type_: TFloat, lhs:, op:, rhs:)) - TInt, parser.Add, TInt -> Ok(BinaryOp(type_: TInt, lhs:, op:, rhs:)) + typ.TFloat, expr.Add, typ.TFloat -> + Ok(typed_expr.BinaryOp(type_: typ.TFloat, lhs:, op:, rhs:)) + typ.TInt, expr.Add, typ.TInt -> + Ok(typed_expr.BinaryOp(type_: typ.TInt, lhs:, op:, rhs:)) // Subtraction - TFloat, parser.Subtract, TFloat -> - Ok(BinaryOp(type_: TFloat, lhs:, op:, rhs:)) - TInt, parser.Subtract, TInt -> - Ok(BinaryOp(type_: TInt, lhs:, op:, rhs:)) + typ.TFloat, expr.Subtract, typ.TFloat -> + Ok(typed_expr.BinaryOp(type_: typ.TFloat, lhs:, op:, rhs:)) + typ.TInt, expr.Subtract, typ.TInt -> + Ok(typed_expr.BinaryOp(type_: typ.TInt, lhs:, op:, rhs:)) // Multiplication - TFloat, parser.Multiply, TFloat -> - Ok(BinaryOp(type_: TFloat, lhs:, op:, rhs:)) - TInt, parser.Multiply, TInt -> - Ok(BinaryOp(type_: TInt, lhs:, op:, rhs:)) + typ.TFloat, expr.Multiply, typ.TFloat -> + Ok(typed_expr.BinaryOp(type_: typ.TFloat, lhs:, op:, rhs:)) + typ.TInt, expr.Multiply, typ.TInt -> + Ok(typed_expr.BinaryOp(type_: typ.TInt, lhs:, op:, rhs:)) // Division - TFloat, parser.Divide, TFloat -> - Ok(BinaryOp(type_: TFloat, lhs:, op:, rhs:)) - TInt, parser.Divide, TInt -> Ok(BinaryOp(type_: TInt, lhs:, op:, rhs:)) + typ.TFloat, expr.Divide, typ.TFloat -> + Ok(typed_expr.BinaryOp(type_: typ.TFloat, lhs:, op:, rhs:)) + typ.TInt, expr.Divide, typ.TInt -> + Ok(typed_expr.BinaryOp(type_: typ.TInt, lhs:, op:, rhs:)) // Power - TFloat, parser.Power, TFloat | TInt, parser.Power, TFloat -> - Ok(BinaryOp(type_: TFloat, lhs:, op:, rhs:)) + typ.TFloat, expr.Power, typ.TFloat | typ.TInt, expr.Power, typ.TFloat -> + Ok(typed_expr.BinaryOp(type_: typ.TFloat, lhs:, op:, rhs:)) // Equal and Not Equal Check - t1, parser.EqualCheck, t2 | t1, parser.NotEqualCheck, t2 if t1 == t2 -> - Ok(BinaryOp(type_: TBool, lhs:, op:, rhs:)) + t1, expr.EqualCheck, t2 | t1, expr.NotEqualCheck, t2 if t1 == t2 -> + Ok(typed_expr.BinaryOp(type_: typ.TBool, lhs:, op:, rhs:)) // Ordering Checks - TFloat, parser.LessThanCheck, TFloat - | TFloat, parser.LessThanOrEqualCheck, TFloat - | TFloat, parser.GreaterThanOrEqualCheck, TFloat - | TFloat, parser.GreaterThanCheck, TFloat - | TInt, parser.LessThanCheck, TInt - | TInt, parser.LessThanOrEqualCheck, TInt - | TInt, parser.GreaterThanOrEqualCheck, TInt - | TInt, parser.GreaterThanCheck, TInt - | TString, parser.LessThanCheck, TString - | TString, parser.LessThanOrEqualCheck, TString - | TString, parser.GreaterThanOrEqualCheck, TString - | TString, parser.GreaterThanCheck, TString - -> Ok(BinaryOp(type_: TBool, lhs:, op:, rhs:)) + typ.TFloat, expr.LessThanCheck, typ.TFloat + | typ.TFloat, expr.LessThanOrEqualCheck, typ.TFloat + | typ.TFloat, expr.GreaterThanOrEqualCheck, typ.TFloat + | typ.TFloat, expr.GreaterThanCheck, typ.TFloat + | typ.TInt, expr.LessThanCheck, typ.TInt + | typ.TInt, expr.LessThanOrEqualCheck, typ.TInt + | typ.TInt, expr.GreaterThanOrEqualCheck, typ.TInt + | typ.TInt, expr.GreaterThanCheck, typ.TInt + | typ.TString, expr.LessThanCheck, typ.TString + | typ.TString, expr.LessThanOrEqualCheck, typ.TString + | typ.TString, expr.GreaterThanOrEqualCheck, typ.TString + | typ.TString, expr.GreaterThanCheck, typ.TString + -> Ok(typed_expr.BinaryOp(type_: typ.TBool, lhs:, op:, rhs:)) // Boolean Operations - TBool, parser.And, TBool | TBool, parser.Or, TBool -> - Ok(BinaryOp(type_: TBool, lhs:, op:, rhs:)) + typ.TBool, expr.And, typ.TBool | typ.TBool, expr.Or, typ.TBool -> + Ok(typed_expr.BinaryOp(type_: typ.TBool, lhs:, op:, rhs:)) + + // Boolean TypeErrors + typ.TBool, expr.And, t -> + case t { + typ.TNil -> + Error( + error.TypeError(type_error.TypeError( + "Tried to do a boolean and operation (b1 && b2) but the right hand side of the operation has type \"Empty\". Could you be referencing an empty cell?", + )), + ) + _ -> + Error( + error.TypeError(type_error.TypeError( + "Tried to do a boolean and operation (b1 && b2) but the right hand side has type " + <> string.inspect(t), + )), + ) + } _, _, _ -> - Error(TypeError( - "Unexpected arguments to binary operation: " <> string.inspect(op), - )) + Error( + error.TypeError(type_error.TypeError( + "Unexpected arguments to binary operation: " <> string.inspect(op), + )), + ) } } } diff --git a/src/squared_away/lang/typechecker/typ.gleam b/src/squared_away/lang/typechecker/typ.gleam new file mode 100644 index 0000000..4b3cf57 --- /dev/null +++ b/src/squared_away/lang/typechecker/typ.gleam @@ -0,0 +1,17 @@ +pub type Typ { + TNil + TFloat + TString + TInt + TBool +} + +pub fn to_string(typ: Typ) { + case typ { + TNil -> "Empty" + TFloat -> "Floating Point Number" + TString -> "Text" + TInt -> "Integer" + TBool -> "Boolean (True or False)" + } +} diff --git a/src/squared_away/lang/typechecker/type_error.gleam b/src/squared_away/lang/typechecker/type_error.gleam new file mode 100644 index 0000000..84fbdce --- /dev/null +++ b/src/squared_away/lang/typechecker/type_error.gleam @@ -0,0 +1,3 @@ +pub type TypeError { + TypeError(context: String) +} diff --git a/src/squared_away/lang/typechecker/typed_expr.gleam b/src/squared_away/lang/typechecker/typed_expr.gleam new file mode 100644 index 0000000..a5930ca --- /dev/null +++ b/src/squared_away/lang/typechecker/typed_expr.gleam @@ -0,0 +1,19 @@ +import squared_away/lang/parser/expr +import squared_away/lang/typechecker/typ + +pub type TypedExpr { + Empty(type_: typ.Typ) + FloatLiteral(type_: typ.Typ, f: Float) + StringLiteral(type_: typ.Typ, txt: String) + IntegerLiteral(type_: typ.Typ, n: Int) + CellReference(type_: typ.Typ, key: String) + BooleanLiteral(type_: typ.Typ, b: Bool) + UnaryOp(type_: typ.Typ, op: expr.UnaryOpKind, expr: TypedExpr) + BinaryOp( + type_: typ.Typ, + lhs: TypedExpr, + op: expr.BinaryOpKind, + rhs: TypedExpr, + ) + Group(type_: typ.Typ, expr: TypedExpr) +} diff --git a/test/squared_away_test.gleam b/test/squared_away_test.gleam index 169aee2..ecdac50 100644 --- a/test/squared_away_test.gleam +++ b/test/squared_away_test.gleam @@ -3,8 +3,11 @@ import gleam/list import gleeunit import gleeunit/should import squared_away/lang/interpreter +import squared_away/lang/interpreter/value import squared_away/lang/parser +import squared_away/lang/parser/expr import squared_away/lang/scanner +import squared_away/lang/scanner/token import squared_away/lang/typechecker pub fn main() { @@ -13,35 +16,35 @@ pub fn main() { pub fn scanner_test() { let test_cases = [ - #("=-+*/", [scanner.Minus, scanner.Plus, scanner.Star, scanner.Div]), + #("=-+*/", [token.Minus, token.Plus, token.Star, token.Div]), #("= - >= + * / ** = ! && || != ( <= ) == <> TRUE FALSE", [ - scanner.Minus, - scanner.GreaterEqual, - scanner.Plus, - scanner.Star, - scanner.Div, - scanner.StarStar, - scanner.Equal, - scanner.Bang, - scanner.And, - scanner.Or, - scanner.BangEqual, - scanner.LParen, - scanner.LessEqual, - scanner.RParen, - scanner.EqualEqual, - scanner.Less, - scanner.Greater, - scanner.TrueToken, - scanner.FalseToken, + token.Minus, + token.GreaterEqual, + token.Plus, + token.Star, + token.Div, + token.StarStar, + token.Equal, + token.Bang, + token.And, + token.Or, + token.BangEqual, + token.LParen, + token.LessEqual, + token.RParen, + token.EqualEqual, + token.Less, + token.Greater, + token.TrueToken, + token.FalseToken, ]), #("= - AS45 + 786", [ - scanner.Minus, - scanner.CellReference("AS45"), - scanner.Plus, - scanner.IntegerLiteral(786), + token.Minus, + token.CellReference("AS45"), + token.Plus, + token.IntegerLiteral(786), ]), - #("+-*/=", [scanner.StringLiteral("+-*/=")]), + #("+-*/=", [token.StringLiteral("+-*/=")]), ] use tc <- list.each(test_cases) @@ -52,52 +55,52 @@ pub fn parser_test() { let test_cases = [ #( [ - scanner.IntegerLiteral(7), - scanner.Plus, - scanner.IntegerLiteral(8), - scanner.Minus, - scanner.IntegerLiteral(9), + token.IntegerLiteral(7), + token.Plus, + token.IntegerLiteral(8), + token.Minus, + token.IntegerLiteral(9), ], - parser.BinaryOp( - parser.IntegerLiteral(7), - parser.Add, - parser.BinaryOp( - parser.IntegerLiteral(8), - parser.Subtract, - parser.IntegerLiteral(9), + expr.BinaryOp( + expr.IntegerLiteral(7), + expr.Add, + expr.BinaryOp( + expr.IntegerLiteral(8), + expr.Subtract, + expr.IntegerLiteral(9), ), ), ), #( [ - scanner.IntegerLiteral(1), - scanner.Plus, - scanner.IntegerLiteral(2), - scanner.Plus, - scanner.IntegerLiteral(3), - scanner.Plus, - scanner.IntegerLiteral(4), - scanner.Plus, - scanner.IntegerLiteral(5), - scanner.Plus, - scanner.IntegerLiteral(6), + token.IntegerLiteral(1), + token.Plus, + token.IntegerLiteral(2), + token.Plus, + token.IntegerLiteral(3), + token.Plus, + token.IntegerLiteral(4), + token.Plus, + token.IntegerLiteral(5), + token.Plus, + token.IntegerLiteral(6), ], - parser.BinaryOp( - parser.IntegerLiteral(1), - parser.Add, - parser.BinaryOp( - parser.IntegerLiteral(2), - parser.Add, - parser.BinaryOp( - parser.IntegerLiteral(3), - parser.Add, - parser.BinaryOp( - parser.IntegerLiteral(4), - parser.Add, - parser.BinaryOp( - parser.IntegerLiteral(5), - parser.Add, - parser.IntegerLiteral(6), + expr.BinaryOp( + expr.IntegerLiteral(1), + expr.Add, + expr.BinaryOp( + expr.IntegerLiteral(2), + expr.Add, + expr.BinaryOp( + expr.IntegerLiteral(3), + expr.Add, + expr.BinaryOp( + expr.IntegerLiteral(4), + expr.Add, + expr.BinaryOp( + expr.IntegerLiteral(5), + expr.Add, + expr.IntegerLiteral(6), ), ), ), @@ -112,9 +115,9 @@ pub fn parser_test() { pub fn integration_lang_test() { let test_cases = [ - #("=27+4-10", interpreter.Integer(21)), - #("=TRUE && FALSE", interpreter.Boolean(False)), - #("=2+(5*8)", interpreter.Integer(42)), + #("=27+4-10", value.Integer(21)), + #("=TRUE && FALSE", value.Boolean(False)), + #("=2+(5*8)", value.Integer(42)), ] use tc <- list.each(test_cases)