diff --git a/priv/static/squared_away.mjs b/priv/static/squared_away.mjs index 001e41d..ac1e28a 100644 --- a/priv/static/squared_away.mjs +++ b/priv/static/squared_away.mjs @@ -5155,72 +5155,6 @@ function visit_cross_labels(te, f) { return new Ok(te); } } -function update_labels(ex, old, new$5) { - if (ex instanceof BinaryOp2) { - let t2 = ex.type_; - let lhs = ex.lhs; - let op = ex.op; - let rhs = ex.rhs; - let $ = update_labels(lhs, old, new$5); - let lhs$1 = $[0]; - let u1 = $[1]; - let $1 = update_labels(rhs, old, new$5); - let rhs$1 = $1[0]; - let u2 = $1[1]; - return [new BinaryOp2(t2, lhs$1, op, rhs$1), u1 || u2]; - } else if (ex instanceof CrossLabel2) { - let type_2 = ex.type_; - let key = ex.key; - let row_label = ex.row_label; - let col_label = ex.col_label; - let row_label$1 = (() => { - let $ = row_label === old; - if ($) { - return new$5; - } else { - return row_label; - } - })(); - let col_label$1 = (() => { - let $ = col_label === old; - if ($) { - return new$5; - } else { - return col_label; - } - })(); - return [ - new CrossLabel2(type_2, key, row_label$1, col_label$1), - row_label$1 === old || col_label$1 === old - ]; - } else if (ex instanceof Group2) { - let t2 = ex.type_; - let inner = ex.expr; - let $ = update_labels(inner, old, new$5); - let new$1 = $[0]; - let updated = $[1]; - return [new Group2(t2, new$1), updated]; - } else if (ex instanceof Label2 && ex.txt === old) { - let type_2 = ex.type_; - let key = ex.key; - let def_key = ex.def_key; - let txt = ex.txt; - return [new Label2(type_2, key, def_key, new$5), true]; - } else if (ex instanceof Label2) { - let l = ex; - return [l, false]; - } else if (ex instanceof UnaryOp2) { - let t2 = ex.type_; - let op = ex.op; - let inner = ex.expr; - let $ = update_labels(inner, old, new$5); - let new$1 = $[0]; - let updated = $[1]; - return [new UnaryOp2(t2, op, new$1), updated]; - } else { - return [ex, false]; - } -} function do_to_string2(te) { if (te instanceof BooleanLiteral2) { let b = te.b; @@ -8443,6 +8377,7 @@ function dependency_list(loop$input, loop$te, loop$acc) { } } function edit_cell(state, key, src) { + let old_cell = get4(state.cells, key); let res = try$( (() => { let _pipe = scan(src); @@ -8572,7 +8507,8 @@ function edit_cell(state, key, src) { _pipe, state$1, (s, k) => { - return edit_cell(s, k, get4(s.cells, k).src); + let c = get4(s.cells, k); + return edit_cell(s, k, c.src); } ); })(); @@ -8811,42 +8747,14 @@ function update(model, msg) { if (msg instanceof UserSetCellValue) { let key = msg.key; let val = msg.val; - let old = get_cell(model.compiler_state, key); - let model$1 = fold2( - (() => { - let _pipe = model.compiler_state.cells; - return to_list3(_pipe); - })(), - model, - (acc, g) => { - let k = g[0]; - let c = g[1]; - let $ = c.outcome; - if (!$.isOk()) { - return acc; - } else { - let cs = $[0]; - let $1 = update_labels(cs.typechecked, old.src, val); - let new_te = $1[0]; - let was_updated = $1[1]; - if (!was_updated) { - return acc; - } else { - return acc.withFields({ - compiler_state: edit_cell( - model.compiler_state, - k, - to_string11(new_te) - ) - }); - } - } - } - ); - let model$2 = model$1.withFields({ - compiler_state: edit_cell(model$1.compiler_state, key, val) - }); - return [model$2, none()]; + let compiler_state = (() => { + let _pipe = model.compiler_state; + return edit_cell(_pipe, key, val); + })(); + return [ + model.withFields({ compiler_state }), + none() + ]; } else if (msg instanceof UserToggledFormulaMode) { let display_formulas = msg.to; return [ @@ -8917,7 +8825,7 @@ function update(model, msg) { throw makeError( "let_assert", "squared_away", - 267, + 224, "", "Pattern match failed, no pattern matched the value.", { value: $ } @@ -8937,7 +8845,7 @@ function update(model, msg) { throw makeError( "let_assert", "squared_away", - 277, + 234, "", "Pattern match failed, no pattern matched the value.", { value: $1 } @@ -8979,7 +8887,7 @@ function update(model, msg) { throw makeError( "let_assert", "squared_away", - 300, + 257, "", "Pattern match failed, no pattern matched the value.", { value: $2 } @@ -9042,7 +8950,7 @@ function update(model, msg) { throw makeError( "let_assert", "squared_away", - 349, + 306, "", "Pattern match failed, no pattern matched the value.", { value: $ } @@ -9062,7 +8970,7 @@ function update(model, msg) { throw makeError( "let_assert", "squared_away", - 356, + 313, "", "Pattern match failed, no pattern matched the value.", { value: $1 } @@ -9104,7 +9012,7 @@ function update(model, msg) { throw makeError( "let_assert", "squared_away", - 378, + 335, "", "Pattern match failed, no pattern matched the value.", { value: $2 } diff --git a/src/squared_away.gleam b/src/squared_away.gleam index 16a68e3..b4b127e 100644 --- a/src/squared_away.gleam +++ b/src/squared_away.gleam @@ -162,51 +162,8 @@ fn key_press_event(event, cell) { fn update(model: Model, msg: Msg) -> #(Model, effect.Effect(Msg)) { case msg { UserSetCellValue(key, val) -> { - let old = compiler.get_cell(model.compiler_state, key) - - // If updating a label, - // for every typed_expr that references the label, - // we need to update the source. - // We do this by passing in the new label name to the typechecked expression - // and re-serializing it to a string. - let model = - list.fold(model.compiler_state.cells |> grid.to_list, model, fn(acc, g) { - let #(k, c) = g - - case c.outcome { - // If it never compiled in the first place, don't try to change it. - Error(_) -> acc - Ok(cs) -> { - // Create a new src string for the cell with the label updated - let #(new_te, was_updated) = - typed_expr.update_labels(cs.typechecked, old.src, val) - - // If the src for the cell changed, we need to update it - case was_updated { - False -> acc - True -> - Model( - ..acc, - compiler_state: compiler.edit_cell( - model.compiler_state, - k, - typed_expr.to_string(new_te), - ), - ) - } - } - } - }) - - // Set the actual value in the cell. - // The compiler will handle updating the cells dependencies. - let model = - Model( - ..model, - compiler_state: compiler.edit_cell(model.compiler_state, key, val), - ) - - #(model, effect.none()) + let compiler_state = model.compiler_state |> compiler.edit_cell(key, val) + #(Model(..model, compiler_state:), effect.none()) } UserToggledFormulaMode(display_formulas) -> { #(Model(..model, display_formulas:), effect.none()) diff --git a/src/squared_away/compiler.gleam b/src/squared_away/compiler.gleam index 75d35a3..494e196 100644 --- a/src/squared_away/compiler.gleam +++ b/src/squared_away/compiler.gleam @@ -87,6 +87,8 @@ pub fn get_interpreted(state: State) { } pub fn edit_cell(state: State, key: grid.GridKey, src: String) -> State { + let old_cell = grid.get(state.cells, key) + // Scan, parse, typecheck, and evaluate the cell let res = { use scanned <- result.try( @@ -162,7 +164,10 @@ pub fn edit_cell(state: State, key: grid.GridKey, src: String) -> State { let deps = state.deps_graph |> dict.get(key) |> result.unwrap(or: []) let new_state = deps - |> list.fold(state, fn(s, k) { edit_cell(s, k, grid.get(s.cells, k).src) }) + |> list.fold(state, fn(s, k) { + let c = grid.get(s.cells, k) + edit_cell(s, k, c.src) + }) new_state } diff --git a/src/squared_away/squared_away_lang/interpreter/value.gleam b/src/squared_away/squared_away_lang/interpreter/value.gleam index 0612761..c7eff1a 100644 --- a/src/squared_away/squared_away_lang/interpreter/value.gleam +++ b/src/squared_away/squared_away_lang/interpreter/value.gleam @@ -14,7 +14,7 @@ pub type Value { Boolean(b: Bool) TestFail TestPass - + // There are cells whose content is never meant to be used in a formula. // Their "value" at runtime should be this DoNotEvaluate to ensure they // are not used. This includes: diff --git a/src/squared_away/squared_away_lang/scanner.gleam b/src/squared_away/squared_away_lang/scanner.gleam index 2b4fcaf..28d9d75 100644 --- a/src/squared_away/squared_away_lang/scanner.gleam +++ b/src/squared_away/squared_away_lang/scanner.gleam @@ -60,7 +60,8 @@ pub fn scan(src: String) -> Result(List(token.Token), scan_error.ScanError) { "\"" <> rest -> case get_str(rest, "") { Error(e) -> Error(e) - Ok(#(str_content, "")) -> Ok([token.StringLiteral(str_content |> string.reverse)]) + Ok(#(str_content, "")) -> + Ok([token.StringLiteral(str_content |> string.reverse)]) Ok(_) -> Error(scan_error.ScanError("Found extra content after string literal")) } diff --git a/src/squared_away/squared_away_lang/typechecker.gleam b/src/squared_away/squared_away_lang/typechecker.gleam index c4d8d94..ed0f67d 100644 --- a/src/squared_away/squared_away_lang/typechecker.gleam +++ b/src/squared_away/squared_away_lang/typechecker.gleam @@ -1,9 +1,9 @@ import gleam/bool import gleam/int +import gleam/io import gleam/list.{Continue, Stop} import gleam/option.{None, Some} import gleam/result -import gleam/io import squared_away/squared_away_lang/error import squared_away/squared_away_lang/grid import squared_away/squared_away_lang/parser/expr @@ -168,7 +168,6 @@ pub fn typecheck( } } expr.LabelDef(txt) -> { - // Duplicate label definitions should be a type error let defs = grid.fold(env, 0, fn(count, _, expr) { @@ -196,8 +195,9 @@ pub fn typecheck( _ -> Continue(None) } }) - - let key = label_def_key + + let key = + label_def_key |> option.map(grid.cell_to_the_right(env, _)) |> option.map(option.from_result) |> option.flatten @@ -214,12 +214,15 @@ pub fn typecheck( } Ok(expr.LabelDef(ldtxt)) -> { Error( - error.TypeError(type_error.TypeError("Label points to another label definition, cannot be used as a standalone label. Did you mean to use a cross_label?")) + error.TypeError(type_error.TypeError( + "Label points to another label definition, cannot be used as a standalone label. Did you mean to use a cross_label?", + )), ) } Ok(expr) -> { case typecheck(env, expr) { - Ok(te) -> Ok(typed_expr.Label(type_: te.type_, key:, def_key:, txt:)) + Ok(te) -> + Ok(typed_expr.Label(type_: te.type_, key:, def_key:, txt:)) Error(e) -> Error(e) } } @@ -308,7 +311,8 @@ pub fn typecheck( expr.UsdLiteral(cents) -> Ok(typed_expr.UsdLiteral(type_: typ.TUsd, cents:)) expr.PercentLiteral(p) -> Ok(typed_expr.PercentLiteral(typ.TPercent, p)) expr.IntegerLiteral(n) -> Ok(typed_expr.IntegerLiteral(type_: typ.TInt, n:)) - expr.StringLiteral(txt) -> Ok(typed_expr.StringLiteral(type_: typ.TString, txt:)) + expr.StringLiteral(txt) -> + Ok(typed_expr.StringLiteral(type_: typ.TString, txt:)) expr.Group(inner) -> { use expr <- result.try(typecheck(env, inner)) Ok(typed_expr.Group(type_: expr.type_, expr:)) diff --git a/src/squared_away/squared_away_lang/typechecker/typed_expr.gleam b/src/squared_away/squared_away_lang/typechecker/typed_expr.gleam index f1754a9..c3fc82d 100644 --- a/src/squared_away/squared_away_lang/typechecker/typed_expr.gleam +++ b/src/squared_away/squared_away_lang/typechecker/typed_expr.gleam @@ -66,18 +66,14 @@ pub fn visit_cross_labels( // Updates the labels in a typed expr and returns the new expression and a boolean // flag indicating if a label was updated. The compiler uses the flag to know whether // or not a cell needs re-evaluating. -pub fn update_labels( - ex: TypedExpr, - old: String, - new: String, -) -> #(TypedExpr, Bool) { +pub fn update_labels(ex: TypedExpr, old: String, new: String) -> TypedExpr { case ex { BinaryOp(t, lhs, op, rhs) -> { - let #(lhs, u1) = update_labels(lhs, old, new) - let #(rhs, u2) = update_labels(rhs, old, new) + let lhs = update_labels(lhs, old, new) + let rhs = update_labels(rhs, old, new) // u1 || u2 tells us if a label was updated in the expression. - #(BinaryOp(t, lhs, op, rhs), u1 || u2) + BinaryOp(t, lhs, op, rhs) } CrossLabel(type_:, col_label:, row_label:, key:) -> { @@ -91,25 +87,21 @@ pub fn update_labels( False -> col_label } - #( - CrossLabel(type_:, col_label:, row_label:, key:), - row_label == old || col_label == old, - ) + CrossLabel(type_:, col_label:, row_label:, key:) } Group(t, inner) -> { - let #(new, updated) = update_labels(inner, old, new) - #(Group(t, new), updated) + let new = update_labels(inner, old, new) + Group(t, new) } - Label(key:, txt:, def_key:, type_:) if txt == old -> #( - Label(key:, txt: new, type_:, def_key:), - True, - ) - Label(_, _, _, _) as l -> #(l, False) + Label(key:, txt:, def_key:, type_:) if txt == old -> + Label(key:, txt: new, type_:, def_key:) + + Label(_, _, _, _) as l -> l UnaryOp(t, op, inner) -> { - let #(new, updated) = update_labels(inner, old, new) - #(UnaryOp(t, op, new), updated) + let new = update_labels(inner, old, new) + UnaryOp(t, op, new) } - _ -> #(ex, False) + _ -> ex } }