diff --git a/priv/static/squared_away.mjs b/priv/static/squared_away.mjs index 08b8025..33a7418 100644 --- a/priv/static/squared_away.mjs +++ b/priv/static/squared_away.mjs @@ -115,44 +115,24 @@ var UtfCodepoint = class { } }; function byteArrayToInt(byteArray, start3, end, isBigEndian, isSigned) { - const byteSize = end - start3; - if (byteSize <= 6) { - let value3 = 0; - if (isBigEndian) { - for (let i = start3; i < end; i++) { - value3 = value3 * 256 + byteArray[i]; - } - } else { - for (let i = end - 1; i >= start3; i--) { - value3 = value3 * 256 + byteArray[i]; - } - } - if (isSigned) { - const highBit = 2 ** (byteSize * 8 - 1); - if (value3 >= highBit) { - value3 -= highBit * 2; - } + let value3 = 0; + if (isBigEndian) { + for (let i = start3; i < end; i++) { + value3 = value3 * 256 + byteArray[i]; } - return value3; } else { - let value3 = 0n; - if (isBigEndian) { - for (let i = start3; i < end; i++) { - value3 = (value3 << 8n) + BigInt(byteArray[i]); - } - } else { - for (let i = end - 1; i >= start3; i--) { - value3 = (value3 << 8n) + BigInt(byteArray[i]); - } + for (let i = end - 1; i >= start3; i--) { + value3 = value3 * 256 + byteArray[i]; } - if (isSigned) { - const highBit = 1n << BigInt(byteSize * 8 - 1); - if (value3 >= highBit) { - value3 -= highBit * 2n; - } + } + if (isSigned) { + const byteSize = end - start3; + const highBit = 2 ** (byteSize * 8 - 1); + if (value3 >= highBit) { + value3 -= highBit * 2; } - return Number(value3); } + return value3; } function byteArrayToFloat(byteArray, start3, end, isBigEndian) { const view2 = new DataView(byteArray.buffer); @@ -8322,10 +8302,11 @@ function typecheck(env, expr) { // build/dev/javascript/squared_away/squared_away/compiler.mjs var Cell = class extends CustomType { - constructor(src, outcome) { + constructor(src, outcome, up_to_date) { super(); this.src = src; this.outcome = outcome; + this.up_to_date = up_to_date; } }; var CompileSteps = class extends CustomType { @@ -8366,6 +8347,14 @@ function get_typechecked(state) { } ); } +function invalidate_cell(state, key) { + let c = (() => { + let _pipe = state.cells; + return get4(_pipe, key); + })(); + let c$1 = c.withFields({ up_to_date: false }); + return state.withFields({ cells: insert4(state.cells, key, c$1) }); +} function get_cell(state, key) { return get4(state.cells, key); } @@ -8470,100 +8459,106 @@ 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); - return map_error( - _pipe, - (var0) => { - return new ScanError2(var0); - } - ); - })(), - (scanned) => { - let scanned$1 = (() => { - let _pipe = scanned; - return map2( - _pipe, - (t2) => { - if (t2 instanceof BuiltinSum3 && t2.key instanceof None) { - return new BuiltinSum3(new Some(key)); - } else if (t2 instanceof BuiltinAvg2 && t2.key instanceof None) { - return new BuiltinAvg2(new Some(key)); - } else { - return t2; - } - } - ); - })(); - return try$( + return guard( + old_cell.src === src && old_cell.up_to_date, + state, + () => { + let res = try$( (() => { - let _pipe = parse3(scanned$1); + let _pipe = scan(src); return map_error( _pipe, (var0) => { - return new ParseError2(var0); + return new ScanError2(var0); } ); })(), - (parsed) => { + (scanned) => { + let scanned$1 = (() => { + let _pipe = scanned; + return map2( + _pipe, + (t2) => { + if (t2 instanceof BuiltinSum3 && t2.key instanceof None) { + return new BuiltinSum3(new Some(key)); + } else if (t2 instanceof BuiltinAvg2 && t2.key instanceof None) { + return new BuiltinAvg2(new Some(key)); + } else { + return t2; + } + } + ); + })(); return try$( - typecheck( - (() => { - let _pipe = state; - return get_parsed(_pipe); - })(), - parsed - ), - (typechecked) => { - let deps2 = dependency_list( - (() => { - let _pipe = state; - return get_typechecked(_pipe); - })(), - typechecked, - toList([]) + (() => { + let _pipe = parse3(scanned$1); + return map_error( + _pipe, + (var0) => { + return new ParseError2(var0); + } ); - let deps_graph = (() => { - let _pipe = deps2; - return fold2( - _pipe, - state.deps_graph, - (s, dep) => { - return upsert( - s, - dep, - (v) => { - if (v instanceof None) { - return toList([key]); - } else { - let lst = v[0]; - return prepend(key, lst); - } - } - ); - } - ); - })(); + })(), + (parsed) => { return try$( - interpret( + typecheck( (() => { let _pipe = state; - return get_typechecked(_pipe); + return get_parsed(_pipe); })(), - typechecked + parsed ), - (interpreted) => { - return new Ok( - [ - new CompileSteps( - scanned$1, - parsed, - typechecked, - interpreted - ), - deps_graph - ] + (typechecked) => { + let deps2 = dependency_list( + (() => { + let _pipe = state; + return get_typechecked(_pipe); + })(), + typechecked, + toList([]) + ); + let deps_graph = (() => { + let _pipe = deps2; + return fold2( + _pipe, + state.deps_graph, + (s, dep) => { + return upsert( + s, + dep, + (v) => { + if (v instanceof None) { + return toList([key]); + } else { + let lst = v[0]; + return prepend(key, lst); + } + } + ); + } + ); + })(); + return try$( + interpret( + (() => { + let _pipe = state; + return get_typechecked(_pipe); + })(), + typechecked + ), + (interpreted) => { + return new Ok( + [ + new CompileSteps( + scanned$1, + parsed, + typechecked, + interpreted + ), + deps_graph + ] + ); + } ); } ); @@ -8571,40 +8566,54 @@ function edit_cell(state, key, src) { ); } ); + let state$1 = (() => { + if (!res.isOk()) { + let e = res[0]; + return state.withFields({ + cells: insert4( + state.cells, + key, + new Cell(src, new Error(e), true) + ) + }); + } else { + let cell = res[0][0]; + let deps_graph = res[0][1]; + return new State( + insert4(state.cells, key, new Cell(src, new Ok(cell), true)), + deps_graph + ); + } + })(); + let deps = (() => { + let _pipe = state$1.deps_graph; + let _pipe$1 = get(_pipe, key); + return unwrap(_pipe$1, toList([])); + })(); + let new_state = (() => { + let _pipe = deps; + return fold2( + _pipe, + state$1, + (s, k) => { + return invalidate_cell(s, k); + } + ); + })(); + let new_state$1 = (() => { + let _pipe = deps; + return fold2( + _pipe, + new_state, + (s, k) => { + let c = get4(s.cells, k); + return edit_cell(s, k, c.src); + } + ); + })(); + return new_state$1; } ); - let state$1 = (() => { - if (!res.isOk()) { - let e = res[0]; - return state.withFields({ - cells: insert4(state.cells, key, new Cell(src, new Error(e))) - }); - } else { - let cell = res[0][0]; - let deps_graph = res[0][1]; - return new State( - insert4(state.cells, key, new Cell(src, new Ok(cell))), - deps_graph - ); - } - })(); - let deps = (() => { - let _pipe = state$1.deps_graph; - let _pipe$1 = get(_pipe, key); - return unwrap(_pipe$1, toList([])); - })(); - let new_state = (() => { - let _pipe = deps; - return fold2( - _pipe, - state$1, - (s, k) => { - let c = get4(s.cells, k); - return edit_cell(s, k, c.src); - } - ); - })(); - return new_state; } var empty_cell = /* @__PURE__ */ new Cell( "", @@ -8615,7 +8624,8 @@ var empty_cell = /* @__PURE__ */ new Cell( /* @__PURE__ */ new Empty3(/* @__PURE__ */ new TNil()), /* @__PURE__ */ new Empty4() ) - ) + ), + true ); function init_state(width, height) { return new State(new$4(width, height, empty_cell), new$()); diff --git a/src/squared_away/compiler.gleam b/src/squared_away/compiler.gleam index 494e196..9052177 100644 --- a/src/squared_away/compiler.gleam +++ b/src/squared_away/compiler.gleam @@ -2,6 +2,7 @@ //// an initial set of grids for all the cells, and try to only //// compile and update what we need based on the dependency graph. +import gleam/bool import gleam/dict import gleam/list import gleam/option @@ -20,7 +21,11 @@ import squared_away/squared_away_lang/typechecker/typ import squared_away/squared_away_lang/typechecker/typed_expr pub type Cell { - Cell(src: String, outcome: Result(CompileSteps, error.CompileError)) + Cell( + src: String, + outcome: Result(CompileSteps, error.CompileError), + up_to_date: Bool, + ) } pub type CompileSteps { @@ -42,6 +47,7 @@ const empty_cell = Cell( interpreted: value.Empty, ), ), + up_to_date: True, ) pub type State { @@ -86,9 +92,19 @@ pub fn get_interpreted(state: State) { }) } +pub fn invalidate_cell(state: State, key: grid.GridKey) -> State { + let c = state.cells |> grid.get(key) + let c = Cell(..c, up_to_date: False) + State(..state, cells: grid.insert(state.cells, key, c)) +} + pub fn edit_cell(state: State, key: grid.GridKey, src: String) -> State { let old_cell = grid.get(state.cells, key) + // If the source being entered into the cell isn't different from what it used to be, + // only recompile if the cell has been invalidated + use <- bool.guard(old_cell.src == src && old_cell.up_to_date, state) + // Scan, parse, typecheck, and evaluate the cell let res = { use scanned <- result.try( @@ -147,12 +163,20 @@ pub fn edit_cell(state: State, key: grid.GridKey, src: String) -> State { // If the process failed, we just set the error value as the cells value. State( ..state, - cells: grid.insert(state.cells, key, Cell(src:, outcome: Error(e))), + cells: grid.insert( + state.cells, + key, + Cell(src:, outcome: Error(e), up_to_date: True), + ), ) } Ok(#(cell, deps_graph)) -> { State( - cells: grid.insert(state.cells, key, Cell(src:, outcome: Ok(cell))), + cells: grid.insert( + state.cells, + key, + Cell(src:, outcome: Ok(cell), up_to_date: True), + ), deps_graph:, ) } @@ -164,7 +188,11 @@ 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) { + |> list.fold(state, fn(s, k) { invalidate_cell(s, k) }) + + let new_state = + deps + |> list.fold(new_state, fn(s, k) { let c = grid.get(s.cells, k) edit_cell(s, k, c.src) }) diff --git a/src/squared_away/squared_away_lang/interpreter.gleam b/src/squared_away/squared_away_lang/interpreter.gleam index dfb0e59..8c2af9c 100644 --- a/src/squared_away/squared_away_lang/interpreter.gleam +++ b/src/squared_away/squared_away_lang/interpreter.gleam @@ -92,8 +92,8 @@ pub fn interpret( Ok(value.Boolean(a <= b)) value.Integer(a), expr.Minimum, value.Integer(b) -> Ok(value.Integer(int.min(a, b))) - value.Integer(a), expr.Maximum, value.Integer(b) -> - Ok(value.Integer(int.max(a, b))) + value.Integer(a), expr.Maximum, value.Integer(b) -> + Ok(value.Integer(int.max(a, b))) // Float x Float value.FloatingPointNumber(a), expr.Add, value.FloatingPointNumber(b) -> diff --git a/src/squared_away/squared_away_lang/typechecker.gleam b/src/squared_away/squared_away_lang/typechecker.gleam index f6e5034..36e1f51 100644 --- a/src/squared_away/squared_away_lang/typechecker.gleam +++ b/src/squared_away/squared_away_lang/typechecker.gleam @@ -349,8 +349,8 @@ pub fn typecheck( Ok(typed_expr.BinaryOp(type_: typ.TFloat, lhs:, op:, rhs:)) typ.TFloat, expr.Minimum, typ.TFloat -> Ok(typed_expr.BinaryOp(type_: typ.TFloat, lhs:, op:, rhs:)) - typ.TFloat, expr.Maximum, typ.TFloat -> - Ok(typed_expr.BinaryOp(type_: typ.TFloat, lhs:, op:, rhs:)) + typ.TFloat, expr.Maximum, typ.TFloat -> + Ok(typed_expr.BinaryOp(type_: typ.TFloat, lhs:, op:, rhs:)) // USD x Float (None for now) // Percent x Float (None for now) @@ -382,8 +382,8 @@ pub fn typecheck( Ok(typed_expr.BinaryOp(type_: typ.TPercent, lhs:, op:, rhs:)) typ.TUsd, expr.Minimum, typ.TUsd -> Ok(typed_expr.BinaryOp(type_: typ.TUsd, lhs:, op:, rhs:)) - typ.TUsd, expr.Maximum, typ.TUsd -> - Ok(typed_expr.BinaryOp(type_: typ.TUsd, lhs:, op:, rhs:)) + typ.TUsd, expr.Maximum, typ.TUsd -> + Ok(typed_expr.BinaryOp(type_: typ.TUsd, lhs:, op:, rhs:)) typ.TUsd, expr.Multiply, typ.TUsd -> Error(error.TypeError(type_error.CannotMultiplyUsdByUsd(lhs:, rhs:))) @@ -409,8 +409,8 @@ pub fn typecheck( Ok(typed_expr.BinaryOp(type_: typ.TPercent, lhs:, op:, rhs:)) typ.TPercent, expr.Minimum, typ.TPercent -> Ok(typed_expr.BinaryOp(type_: typ.TPercent, lhs:, op:, rhs:)) - typ.TPercent, expr.Maximum, typ.TPercent -> - Ok(typed_expr.BinaryOp(type_: typ.TPercent, lhs:, op:, rhs:)) + typ.TPercent, expr.Maximum, typ.TPercent -> + Ok(typed_expr.BinaryOp(type_: typ.TPercent, lhs:, op:, rhs:)) // String x Percent (None for now) // Boolean x Percent (None for now) @@ -442,8 +442,8 @@ pub fn typecheck( Ok(typed_expr.BinaryOp(type_: typ.TInt, lhs:, op:, rhs:)) typ.TInt, expr.Minimum, typ.TInt -> Ok(typed_expr.BinaryOp(type_: typ.TInt, lhs:, op:, rhs:)) - typ.TInt, expr.Maximum, typ.TInt -> - Ok(typed_expr.BinaryOp(type_: typ.TInt, lhs:, op:, rhs:)) + typ.TInt, expr.Maximum, typ.TInt -> + Ok(typed_expr.BinaryOp(type_: typ.TInt, lhs:, op:, rhs:)) // Int x Usd typ.TInt, expr.Multiply, typ.TUsd ->