diff --git a/priv/static/squared_away.mjs b/priv/static/squared_away.mjs index 685e211..d1bc716 100644 --- a/priv/static/squared_away.mjs +++ b/priv/static/squared_away.mjs @@ -275,6 +275,14 @@ function to_result(option, e) { return new Error(e); } } +function unwrap(option, default$) { + if (option instanceof Some) { + let x = option[0]; + return x; + } else { + return default$; + } +} // build/dev/javascript/gleam_stdlib/gleam/float.mjs function parse(string3) { @@ -313,6 +321,18 @@ function power3(base, exponent) { } // build/dev/javascript/gleam_stdlib/gleam/list.mjs +var Continue = class extends CustomType { + constructor(x0) { + super(); + this[0] = x0; + } +}; +var Stop = class extends CustomType { + constructor(x0) { + super(); + this[0] = x0; + } +}; function do_reverse(loop$remaining, loop$accumulator) { while (true) { let remaining = loop$remaining; @@ -387,6 +407,29 @@ function fold(loop$list, loop$initial, loop$fun) { } } } +function fold_until(loop$collection, loop$accumulator, loop$fun) { + while (true) { + let collection = loop$collection; + let accumulator = loop$accumulator; + let fun = loop$fun; + if (collection.hasLength(0)) { + return accumulator; + } else { + let first$1 = collection.head; + let rest$1 = collection.tail; + let $ = fun(accumulator, first$1); + if ($ instanceof Continue) { + let next_accumulator = $[0]; + loop$collection = rest$1; + loop$accumulator = next_accumulator; + loop$fun = fun; + } else { + let b = $[0]; + return b; + } + } + } +} function do_repeat(loop$a, loop$times, loop$acc) { while (true) { let a = loop$a; @@ -2196,6 +2239,14 @@ function on_input(msg) { ); } +// build/dev/javascript/squared_away/squared_away/lang/interpreter/runtime_error.mjs +var RuntimeError = class extends CustomType { + constructor(context) { + super(); + this.context = context; + } +}; + // build/dev/javascript/squared_away/squared_away/lang/parser/parse_error.mjs var ParseError = class extends CustomType { constructor(context) { @@ -2235,16 +2286,16 @@ var TypeError2 = class extends CustomType { this[0] = x0; } }; +var RuntimeError2 = class extends CustomType { + constructor(x0) { + super(); + this[0] = x0; + } +}; // build/dev/javascript/squared_away/squared_away/lang/interpreter/value.mjs var Empty2 = class extends CustomType { }; -var Text2 = class extends CustomType { - constructor(inner) { - super(); - this.inner = inner; - } -}; var Integer = class extends CustomType { constructor(n) { super(); @@ -2273,6 +2324,13 @@ var FloatLiteral = class extends CustomType { this.f = f; } }; +var LabelDef = class extends CustomType { + constructor(txt, cell_ref) { + super(); + this.txt = txt; + this.cell_ref = cell_ref; + } +}; var Label = class extends CustomType { constructor(txt) { super(); @@ -2382,6 +2440,14 @@ var Label2 = class extends CustomType { this.txt = txt; } }; +var LabelDef2 = class extends CustomType { + constructor(type_, txt, key) { + super(); + this.type_ = type_; + this.txt = txt; + this.key = key; + } +}; var IntegerLiteral2 = class extends CustomType { constructor(type_, n) { super(); @@ -2435,13 +2501,56 @@ function interpret(loop$env, loop$expr) { let expr = loop$expr; if (expr instanceof Empty4) { return new Ok(new Empty2()); + } else if (expr instanceof LabelDef2) { + 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 Label2) { let txt = expr.txt; - return new Ok(new Text2(txt)); + let key = (() => { + let _pipe = env; + let _pipe$1 = map_to_list(_pipe); + return fold_until( + _pipe$1, + new None(), + (acc, i) => { + if (i[1].isOk() && i[1][0] instanceof LabelDef2 && i[1][0].txt === txt) { + let ty = i[1][0].type_; + let label_txt = i[1][0].txt; + let cell_ref = i[1][0].key; + return new Stop(new Some(cell_ref)); + } else { + return new Continue(new None()); + } + } + ); + })(); + if (key instanceof None) { + return new Error( + new RuntimeError2( + new RuntimeError("Label doesn't point to anything") + ) + ); + } else { + let key$1 = key[0]; + let $ = get(env, key$1); + if (!$.isOk() && !$[0]) { + return new Error( + new RuntimeError2( + new RuntimeError("Label doesn't point to anything") + ) + ); + } else if ($.isOk() && !$[0].isOk()) { + let e = $[0][0]; + return new Error(e); + } else { + let te = $[0][0]; + loop$env = env; + loop$expr = te; + } + } } else if (expr instanceof BooleanLiteral2) { let b = expr.b; return new Ok(new Boolean(b)); @@ -2458,7 +2567,7 @@ function interpret(loop$env, loop$expr) { throw makeError( "assignment_no_match", "squared_away/lang/interpreter", - 22, + 48, "interpret", "Assignment pattern did not match", { value: $ } @@ -2492,7 +2601,7 @@ function interpret(loop$env, loop$expr) { throw makeError( "panic", "squared_away/lang/interpreter", - 36, + 62, "", "These should be the only options if the typechecker is working", {} @@ -2598,7 +2707,7 @@ function interpret(loop$env, loop$expr) { throw makeError( "assignment_no_match", "squared_away/lang/interpreter", - 104, + 130, "", "Assignment pattern did not match", { value: $ } @@ -2614,7 +2723,7 @@ function interpret(loop$env, loop$expr) { throw makeError( "assignment_no_match", "squared_away/lang/interpreter", - 108, + 134, "", "Assignment pattern did not match", { value: $ } @@ -2642,7 +2751,7 @@ function interpret(loop$env, loop$expr) { throw makeError( "panic", "squared_away/lang/interpreter", - 122, + 148, "", "these should be the only options if the typechecker is working properly", {} @@ -2719,6 +2828,13 @@ var Label3 = class extends CustomType { this[0] = x0; } }; +var LabelDef3 = class extends CustomType { + constructor(txt, cell_ref) { + super(); + this.txt = txt; + this.cell_ref = cell_ref; + } +}; // build/dev/javascript/squared_away/squared_away/lang/parser.mjs function try_parse_binary_ops(tokens) { @@ -2970,6 +3086,11 @@ function try_parse_binary_ops(tokens) { function do_parse(tokens) { if (tokens.hasLength(0)) { return new Ok([new Empty3(), toList([])]); + } else if (tokens.atLeastLength(1) && tokens.head instanceof LabelDef3) { + let str = tokens.head.txt; + let key = tokens.head.cell_ref; + let rest = tokens.tail; + return new Ok([new LabelDef(str, key), rest]); } else if (tokens.atLeastLength(1) && tokens.head instanceof Label3) { let str = tokens.head[0]; let rest = tokens.tail; @@ -3109,6 +3230,149 @@ function parse3(tokens) { } // build/dev/javascript/squared_away/squared_away/lang/scanner.mjs +function parse_identifier(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 new Ok([acc, src]); + } + } + } +} function parse_integer(loop$src, loop$acc) { while (true) { let src = loop$src; @@ -3448,20 +3712,23 @@ function do_scan(loop$src, loop$acc) { loop$acc = prepend(new IntegerLiteral3(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 CellReference3(cell_ref), acc) - ); + let $1 = parse_cell_ref(src, ""); + if ($1.isOk()) { + let cell_ref = $1[0][0]; + let rest = $1[0][1]; + loop$src = trim_left2(rest); + loop$acc = prepend(new CellReference3(cell_ref), acc); + } else { + let $2 = parse_identifier(src, ""); + if (!$2.isOk() && !$2[0]) { + return new Error(new ScanError()); + } else { + let ident = $2[0][0]; + let rest = $2[0][1]; + loop$src = trim_left2(rest); + loop$acc = prepend(new Label3(ident), acc); } - ); + } } } } @@ -3480,7 +3747,7 @@ function scan(src) { toList([]) ); } else { - return new Ok(toList([new Label3(src)])); + return new Ok(toList([new LabelDef3(src, "")])); } } @@ -3567,235 +3834,284 @@ function cell_to_the_right(input2) { } // build/dev/javascript/squared_away/squared_away/lang/typechecker.mjs -function typecheck(env, expr) { - if (expr instanceof Empty3) { - return new Ok(new Empty4(new TNil())); - } else if (expr instanceof Label) { - let txt = expr.txt; - return new Ok(new Label2(new TNil(), txt)); - } else if (expr instanceof BooleanLiteral) { - let b = expr.val; - return new Ok(new BooleanLiteral2(new TBool(), b)); - } else if (expr instanceof FloatLiteral) { - let f = expr.f; - return new Ok(new FloatLiteral2(new TFloat(), f)); - } else if (expr instanceof IntegerLiteral) { - let n = expr.n; - return new Ok(new IntegerLiteral2(new TInt(), n)); - } else if (expr instanceof Group) { - let inner = expr.inner; - return try$( - typecheck(env, inner), - (expr2) => { - return new Ok(new Group2(expr2.type_, expr2)); +function typecheck(loop$env, loop$expr) { + while (true) { + let env = loop$env; + let expr = loop$expr; + if (expr instanceof Empty3) { + return new Ok(new Empty4(new TNil())); + } else if (expr instanceof LabelDef) { + let txt = expr.txt; + let key = expr.cell_ref; + return new Ok(new LabelDef2(new TNil(), txt, key)); + } else if (expr instanceof Label) { + let txt = expr.txt; + let key = (() => { + let _pipe = env; + let _pipe$1 = map_to_list(_pipe); + return fold_until( + _pipe$1, + new None(), + (acc, i) => { + if (i[1].isOk() && i[1][0] instanceof LabelDef && i[1][0].txt === txt) { + let label_txt = i[1][0].txt; + let cell_ref = i[1][0].cell_ref; + return new Stop(new Some(cell_ref)); + } else { + return new Continue(new None()); + } + } + ); + })(); + if (key instanceof None) { + return new Ok(new Label2(new TNil(), txt)); + } else { + let key$1 = key[0]; + let $ = get(env, key$1); + if (!$.isOk()) { + throw makeError( + "assignment_no_match", + "squared_away/lang/typechecker", + 36, + "typecheck", + "Assignment pattern did not match", + { value: $ } + ); + } + let x = $[0]; + if (!x.isOk()) { + let e = x[0]; + return new Error(e); + } else { + let expr$1 = x[0]; + loop$env = env; + loop$expr = expr$1; + } } - ); - } else if (expr instanceof CellReference) { - let key = expr.key; - let $ = get(env, key); - if (!$.isOk()) { - throw makeError( - "assignment_no_match", - "squared_away/lang/typechecker", - 31, - "typecheck", - "Assignment pattern did not match", - { value: $ } + } else if (expr instanceof BooleanLiteral) { + let b = expr.val; + return new Ok(new BooleanLiteral2(new TBool(), b)); + } else if (expr instanceof FloatLiteral) { + let f = expr.f; + return new Ok(new FloatLiteral2(new TFloat(), f)); + } else if (expr instanceof IntegerLiteral) { + let n = expr.n; + return new Ok(new IntegerLiteral2(new TInt(), n)); + } else if (expr instanceof Group) { + let inner = expr.inner; + return try$( + typecheck(env, inner), + (expr2) => { + return new Ok(new Group2(expr2.type_, expr2)); + } ); - } - let ref_expr = $[0]; - if (ref_expr.isOk()) { - let expr$1 = ref_expr[0]; + } else if (expr instanceof CellReference) { + let key = expr.key; + let $ = get(env, key); + if (!$.isOk()) { + throw makeError( + "assignment_no_match", + "squared_away/lang/typechecker", + 53, + "typecheck", + "Assignment pattern did not match", + { value: $ } + ); + } + 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 CellReference2(expr2.type_, key)); + } + ); + } else { + let e = ref_expr[0]; + return new Error(e); + } + } else if (expr instanceof UnaryOp) { + let op = expr.op; + let expr$1 = expr.expr; return try$( typecheck(env, expr$1), (expr2) => { - return new Ok(new CellReference2(expr2.type_, key)); + let $ = expr2.type_; + if (op instanceof Negate && $ instanceof TInt) { + return new Ok(new UnaryOp2(expr2.type_, op, expr2)); + } else if (op instanceof Negate && $ instanceof TFloat) { + return new Ok(new UnaryOp2(expr2.type_, op, expr2)); + } else if (op instanceof Not && $ instanceof TBool) { + return new Ok(new UnaryOp2(expr2.type_, op, expr2)); + } else { + return new Error( + new TypeError2( + new TypeError( + "Unexpected type and operator combination" + ) + ) + ); + } } ); } else { - let e = ref_expr[0]; - return new Error(e); - } - } else if (expr instanceof UnaryOp) { - let op = expr.op; - let expr$1 = expr.expr; - return try$( - typecheck(env, expr$1), - (expr2) => { - let $ = expr2.type_; - if (op instanceof Negate && $ instanceof TInt) { - return new Ok(new UnaryOp2(expr2.type_, op, expr2)); - } else if (op instanceof Negate && $ instanceof TFloat) { - return new Ok(new UnaryOp2(expr2.type_, op, expr2)); - } else if (op instanceof Not && $ instanceof TBool) { - return new Ok(new UnaryOp2(expr2.type_, op, expr2)); - } else { - return new Error( - new TypeError2( - new TypeError( - "Unexpected type and operator combination" - ) - ) - ); - } - } - ); - } else { - let lhs = expr.lhs; - let op = expr.op; - let rhs = expr.rhs; - return try$( - typecheck(env, lhs), - (lhs2) => { - return try$( - typecheck(env, rhs), - (rhs2) => { - 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) - ); - } else if ($ instanceof TInt && op instanceof Add && $1 instanceof TInt) { - 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) - ); - } else if ($ instanceof TInt && op instanceof Subtract && $1 instanceof TInt) { - 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) - ); - } else if ($ instanceof TInt && op instanceof Multiply && $1 instanceof TInt) { - 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) - ); - } else if ($ instanceof TInt && op instanceof Divide && $1 instanceof TInt) { - 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) - ); - } else if ($ instanceof TInt && op instanceof Power && $1 instanceof TFloat) { - 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) - ); - } else if (op instanceof NotEqualCheck && isEqual($, $1)) { - let t1 = $; - let t2 = $1; - 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) - ); - } else if ($ instanceof TFloat && op instanceof LessThanOrEqualCheck && $1 instanceof TFloat) { - 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) - ); - } else if ($ instanceof TFloat && op instanceof GreaterThanCheck && $1 instanceof TFloat) { - 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) - ); - } else if ($ instanceof TInt && op instanceof LessThanOrEqualCheck && $1 instanceof TInt) { - 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) - ); - } else if ($ instanceof TInt && op instanceof GreaterThanCheck && $1 instanceof TInt) { - 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) - ); - } else if ($ instanceof TString && op instanceof LessThanOrEqualCheck && $1 instanceof TString) { - 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) - ); - } 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 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 "&&" but the right hand side of the operation has type "Empty". Could you be referencing an empty cell?' - ) - ) + let lhs = expr.lhs; + let op = expr.op; + let rhs = expr.rhs; + return try$( + typecheck(env, lhs), + (lhs2) => { + return try$( + typecheck(env, rhs), + (rhs2) => { + 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) + ); + } else if ($ instanceof TInt && op instanceof Add && $1 instanceof TInt) { + 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) + ); + } else if ($ instanceof TInt && op instanceof Subtract && $1 instanceof TInt) { + 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) + ); + } else if ($ instanceof TInt && op instanceof Multiply && $1 instanceof TInt) { + 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) ); + } else if ($ instanceof TInt && op instanceof Divide && $1 instanceof TInt) { + 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) + ); + } else if ($ instanceof TInt && op instanceof Power && $1 instanceof TFloat) { + 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) + ); + } else if (op instanceof NotEqualCheck && isEqual($, $1)) { + let t1 = $; + let t2 = $1; + 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) + ); + } else if ($ instanceof TFloat && op instanceof LessThanOrEqualCheck && $1 instanceof TFloat) { + 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) + ); + } else if ($ instanceof TFloat && op instanceof GreaterThanCheck && $1 instanceof TFloat) { + 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) + ); + } else if ($ instanceof TInt && op instanceof LessThanOrEqualCheck && $1 instanceof TInt) { + 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) + ); + } else if ($ instanceof TInt && op instanceof GreaterThanCheck && $1 instanceof TInt) { + 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) + ); + } else if ($ instanceof TString && op instanceof LessThanOrEqualCheck && $1 instanceof TString) { + 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) + ); + } 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 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 "&&" 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 TypeError2( new TypeError( - "Tried to do a boolean and operation (b1 && b2) but the right hand side has type " + inspect2( - t + "Unexpected arguments to binary operation: " + inspect2( + op ) ) ) ); } - } else { - return new Error( - new TypeError2( - new TypeError( - "Unexpected arguments to binary operation: " + inspect2( - op - ) - ) - ) - ); } - } - ); - } - ); + ); + } + ); + } } } @@ -3840,7 +4156,7 @@ function typecheck_grid(input2) { throw makeError( "assignment_no_match", "squared_away/lang", - 43, + 41, "", "Assignment pattern did not match", { value: $1 } @@ -3909,12 +4225,35 @@ function scan_grid(input2) { (acc, key, src) => { let maybe_scanned = (() => { let _pipe = scan(src); - return map_error( + let _pipe$1 = map_error( _pipe, (var0) => { return new ScanError2(var0); } ); + return map2( + _pipe$1, + (_capture) => { + return map( + _capture, + (t) => { + if (t instanceof LabelDef3 && t.cell_ref === "") { + let txt = t.txt; + return new LabelDef3( + txt, + (() => { + let _pipe$2 = key; + let _pipe$3 = cell_to_the_right(_pipe$2); + return unwrap(_pipe$3, ""); + })() + ); + } else { + return t; + } + } + ); + } + ); })(); return insert(acc, key, maybe_scanned); } diff --git a/src/squared_away/lang.gleam b/src/squared_away/lang.gleam index c1db817..de370ef 100644 --- a/src/squared_away/lang.gleam +++ b/src/squared_away/lang.gleam @@ -80,6 +80,18 @@ 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) + let maybe_scanned = + scanner.scan(src) + |> result.map_error(error.ScanError) + |> result.map(list.map(_, fn(t) { + case t { + token.LabelDef(txt, "") -> + token.LabelDef( + txt, + key |> util.cell_to_the_right |> option.unwrap(or: ""), + ) + _ -> t + } + })) dict.insert(acc, key, maybe_scanned) } diff --git a/src/squared_away/lang/interpreter.gleam b/src/squared_away/lang/interpreter.gleam index c7b5065..f1a9084 100644 --- a/src/squared_away/lang/interpreter.gleam +++ b/src/squared_away/lang/interpreter.gleam @@ -1,8 +1,11 @@ import gleam/dict import gleam/float import gleam/int +import gleam/list.{Continue, Stop} +import gleam/option.{None, Some} import gleam/result import squared_away/lang/error +import squared_away/lang/interpreter/runtime_error import squared_away/lang/interpreter/value import squared_away/lang/parser/expr import squared_away/lang/typechecker/typed_expr @@ -13,8 +16,46 @@ pub fn interpret( ) -> Result(value.Value, error.CompileError) { case expr { typed_expr.Empty(_) -> Ok(value.Empty) + typed_expr.LabelDef(_, _, _) -> Ok(value.Empty) typed_expr.Group(_, expr) -> interpret(env, expr) - typed_expr.Label(_, txt) -> Ok(value.Text(txt)) + typed_expr.Label(_, txt) -> { + let key = + env + |> dict.to_list + |> list.fold_until(None, fn(acc, i) { + case i { + #(_, Ok(typed_expr.LabelDef(ty, label_txt, cell_ref))) + if label_txt == txt + -> { + Stop(Some(cell_ref)) + } + _ -> Continue(None) + } + }) + + case key { + None -> + Error( + error.RuntimeError(runtime_error.RuntimeError( + "Label doesn't point to anything", + )), + ) + Some(key) -> { + case dict.get(env, key) { + Error(Nil) -> + Error( + error.RuntimeError(runtime_error.RuntimeError( + "Label doesn't point to anything", + )), + ) + Ok(Error(e)) -> Error(e) + Ok(Ok(te)) -> { + interpret(env, te) + } + } + } + } + } typed_expr.BooleanLiteral(_, b) -> Ok(value.Boolean(b)) typed_expr.IntegerLiteral(_, n) -> Ok(value.Integer(n)) typed_expr.FloatLiteral(_, f) -> Ok(value.FloatingPointNumber(f)) diff --git a/src/squared_away/lang/parser.gleam b/src/squared_away/lang/parser.gleam index 3ed2457..28490d5 100644 --- a/src/squared_away/lang/parser.gleam +++ b/src/squared_away/lang/parser.gleam @@ -20,6 +20,7 @@ fn do_parse( ) -> Result(#(expr.Expr, List(token.Token)), parse_error.ParseError) { case tokens { [] -> Ok(#(expr.Empty, [])) + [token.LabelDef(str, key), ..rest] -> Ok(#(expr.LabelDef(str, key), rest)) // Let's do the single token patterns first [token.Label(str), ..rest] -> { case try_parse_binary_ops(rest) { diff --git a/src/squared_away/lang/parser/expr.gleam b/src/squared_away/lang/parser/expr.gleam index 92b7006..c91e4ad 100644 --- a/src/squared_away/lang/parser/expr.gleam +++ b/src/squared_away/lang/parser/expr.gleam @@ -1,6 +1,7 @@ pub type Expr { Empty FloatLiteral(f: Float) + LabelDef(txt: String, cell_ref: String) Label(txt: String) IntegerLiteral(n: Int) CellReference(key: String) diff --git a/src/squared_away/lang/scanner.gleam b/src/squared_away/lang/scanner.gleam index 0b19614..32f23ff 100644 --- a/src/squared_away/lang/scanner.gleam +++ b/src/squared_away/lang/scanner.gleam @@ -11,7 +11,7 @@ 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([token.Label(src)]) + _ -> Ok([token.LabelDef(src, "")]) } } @@ -61,11 +61,60 @@ fn do_scan( } Error(_) -> { - use #(cell_ref, rest) <- result.try( - parse_cell_ref(src, "") - |> result.replace_error(scan_error.ScanError), - ) - do_scan(string.trim_left(rest), [token.CellReference(cell_ref), ..acc]) + case parse_cell_ref(src, "") { + Ok(#(cell_ref, rest)) -> + do_scan(string.trim_left(rest), [ + token.CellReference(cell_ref), + ..acc + ]) + _ -> { + case parse_identifier(src, "") { + Error(Nil) -> Error(scan_error.ScanError) + Ok(#(ident, rest)) -> + do_scan(string.trim_left(rest), [token.Label(ident), ..acc]) + } + } + } + } + } + } + } +} + +fn parse_identifier(src: String, acc: String) -> Result(#(String, String), Nil) { + case src { + "A" as l <> rest + | "B" as l <> rest + | "C" as l <> rest + | "D" as l <> rest + | "E" as l <> rest + | "F" as l <> rest + | "G" as l <> rest + | "H" as l <> rest + | "I" as l <> rest + | "J" as l <> rest + | "K" as l <> rest + | "L" as l <> rest + | "M" as l <> rest + | "N" as l <> rest + | "O" as l <> rest + | "P" as l <> rest + | "Q" as l <> rest + | "R" as l <> rest + | "S" as l <> rest + | "T" as l <> rest + | "U" as l <> rest + | "V" as l <> rest + | "W" as l <> rest + | "X" as l <> rest + | "Y" as l <> rest + | "Z" as l <> rest -> parse_identifier(rest, acc <> l) + _ -> { + case acc { + // Meaning we called this on something that didnt start with a capital letter + "" -> Error(Nil) + _ -> { + Ok(#(acc, src)) } } } diff --git a/src/squared_away/lang/scanner/token.gleam b/src/squared_away/lang/scanner/token.gleam index 1a88f5e..a023a0f 100644 --- a/src/squared_away/lang/scanner/token.gleam +++ b/src/squared_away/lang/scanner/token.gleam @@ -45,4 +45,5 @@ pub type Token { CellReference(key: String) /// Anything not starting with an = in a cell is a string literal Label(String) + LabelDef(txt: String, cell_ref: String) } diff --git a/src/squared_away/lang/typechecker.gleam b/src/squared_away/lang/typechecker.gleam index 353a19a..8df8246 100644 --- a/src/squared_away/lang/typechecker.gleam +++ b/src/squared_away/lang/typechecker.gleam @@ -1,5 +1,6 @@ import gleam/dict -import gleam/list +import gleam/list.{Continue, Stop} +import gleam/option.{type Option, None, Some} import gleam/result import gleam/string import squared_away/lang/error @@ -15,10 +16,35 @@ pub fn typecheck( ) -> Result(typed_expr.TypedExpr, error.CompileError) { case expr { expr.Empty -> Ok(typed_expr.Empty(type_: typ.TNil)) + expr.LabelDef(txt, key) -> + Ok(typed_expr.LabelDef(type_: typ.TNil, txt:, key:)) // We will typecheck the label when we typecheck the grid as a whole. For now it's a // "Nil" type - expr.Label(txt) -> Ok(typed_expr.Label(type_: typ.TNil, txt:)) + expr.Label(txt) -> { + let key = + env + |> dict.to_list + |> list.fold_until(None, fn(acc, i) { + case i { + #(_, Ok(expr.LabelDef(label_txt, cell_ref))) if label_txt == txt -> { + Stop(Some(cell_ref)) + } + _ -> Continue(None) + } + }) + + case key { + None -> Ok(typed_expr.Label(typ.TNil, txt)) + Some(key) -> { + let assert Ok(x) = dict.get(env, key) + case x { + Error(e) -> Error(e) + Ok(expr) -> typecheck(env, expr) + } + } + } + } expr.BooleanLiteral(b) -> Ok(typed_expr.BooleanLiteral(type_: typ.TBool, b:)) expr.FloatLiteral(f) -> Ok(typed_expr.FloatLiteral(type_: typ.TFloat, f:)) diff --git a/src/squared_away/lang/typechecker/typed_expr.gleam b/src/squared_away/lang/typechecker/typed_expr.gleam index bef8bce..bb1a7f5 100644 --- a/src/squared_away/lang/typechecker/typed_expr.gleam +++ b/src/squared_away/lang/typechecker/typed_expr.gleam @@ -5,6 +5,7 @@ pub type TypedExpr { Empty(type_: typ.Typ) FloatLiteral(type_: typ.Typ, f: Float) Label(type_: typ.Typ, txt: String) + LabelDef(type_: typ.Typ, txt: String, key: String) IntegerLiteral(type_: typ.Typ, n: Int) CellReference(type_: typ.Typ, key: String) BooleanLiteral(type_: typ.Typ, b: Bool) diff --git a/test/squared_away_test.gleam b/test/squared_away_test.gleam index 00536a7..9b18ef0 100644 --- a/test/squared_away_test.gleam +++ b/test/squared_away_test.gleam @@ -44,7 +44,7 @@ pub fn scanner_test() { token.Plus, token.IntegerLiteral(786), ]), - #("+-*/=", [token.Label("+-*/=")]), + #("+-*/=", [token.LabelDef("+-*/=", "")]), ] use tc <- list.each(test_cases)