From ed2ce2b28b5ffaf8df37a8868fbfdc1a42f781af Mon Sep 17 00:00:00 2001 From: Dmitry Vasilev Date: Tue, 15 Nov 2022 20:53:16 +0800 Subject: [PATCH] refactor calltree WIP --- src/calltree.js | 42 +++++++++++++++++------------------------- src/cmd.js | 2 ++ src/eval.js | 14 +++++++------- test/test.js | 12 ++++++------ 4 files changed, 32 insertions(+), 38 deletions(-) diff --git a/src/calltree.js b/src/calltree.js index fd915b6..25d3412 100644 --- a/src/calltree.js +++ b/src/calltree.js @@ -41,17 +41,15 @@ export const calltree_node_loc = node => node.toplevel ? {module: node.module} : node.fn.__location -// Finds the last module that was evaluated. If all modules evaluated without -// problems, then it is the entrypoint module. Else it is the module that -// throws error -export const root_calltree_module = state => - findLast( - state.parse_result.sorted, - module => state.calltree[module] != null - ) - export const root_calltree_node = state => - state.calltree[root_calltree_module(state)].calls + // Returns calltree node for toplevel + // It is either toplevel for entrypoint module, or for module that throw + // error and prevent entrypoint module from executing. + // state.calltree[1] is async calls + state.calltree[0] + +export const root_calltree_module = state => + root_calltree_node(state).module export const is_native_fn = calltree_node => !calltree_node.toplevel && calltree_node.fn.__location == null @@ -109,7 +107,7 @@ export const add_frame = ( ) => { let with_frame if(state.frames?.[active_calltree_node.id] == null) { - const frame = eval_frame(active_calltree_node, state.calltree) + const frame = eval_frame(active_calltree_node, state.modules) const coloring = color(frame) with_frame = {...state, frames: {...state.frames, @@ -162,16 +160,13 @@ const replace_calltree_node = (root, node, replacement) => { return result } +// TODO remove, after refactoring async_calls const replace_calltree_node_in_state = (state, node, replacement) => { - const root = root_calltree_module(state) - - // Update calltree, replacing node with expanded node - const {exports, calls} = state.calltree[root] const replaced = replace_calltree_node( { children: [ - calls, + root_calltree_node(state), {children: state.async_calls}, ] }, @@ -179,13 +174,10 @@ const replace_calltree_node_in_state = (state, node, replacement) => { replacement ) - const calltree = {...state.calltree, - [root]: { - exports, - calls: replaced.children[0], - } + return {...state, + calltree: {children: [replaced.children[0]]}, + async_calls: replaced.children[1].children, } - return {...state, calltree, async_calls: replaced.children[1].children} } const expand_calltree_node = (state, node) => { @@ -257,7 +249,7 @@ const jump_calltree_node = (_state, _current_calltree_node) => { const loc = show_body ? calltree_node_loc(next.active_calltree_node) - : find_callsite(next.calltree, active_calltree_node, current_calltree_node) + : find_callsite(next.modules, active_calltree_node, current_calltree_node) return { state: {...next, current_module: loc.module}, @@ -452,8 +444,8 @@ const arrow_right = state => { } } -const find_callsite = (calltree, parent, node) => { - const frame = eval_frame(parent, calltree) +const find_callsite = (modules, parent, node) => { + const frame = eval_frame(parent, modules) const result = find_node(frame, n => n.result?.call == node) return {module: calltree_node_loc(parent).module, index: result.index} } diff --git a/src/cmd.js b/src/cmd.js index 8f74841..2adfbd6 100644 --- a/src/cmd.js +++ b/src/cmd.js @@ -42,6 +42,7 @@ const apply_eval_result = (state, eval_result) => { calltree: eval_result.calltree, calltree_actions: eval_result.calltree_actions, logs: {logs, log_position: null}, + modules: eval_result.modules, } } @@ -69,6 +70,7 @@ const run_code = (s, index, dirty_files) => { ...s, parse_result, calltree: null, + modules: null, async_calls: null, // Shows that calltree is brand new and requires entire rerender diff --git a/src/eval.js b/src/eval.js index 3282893..739b0d1 100644 --- a/src/eval.js +++ b/src/eval.js @@ -31,7 +31,7 @@ that records invocation. So its call will be recorded. Note that it is not enough to record all invocation at call site, because hosted function can be called by native functions (for example Array::map). -For each invocation, we can replay function body with metacirculat interpreter, +For each invocation, we can replay function body with metacircular interpreter, collecting information for editor */ @@ -602,7 +602,7 @@ export const eval_tree = node => { modules: {'': node}}, sorted: [''] } - ).calltree[''].calls + ).calltree } @@ -998,7 +998,7 @@ const apply_assignments = (do_node, assignments) => { } -const eval_statement = (s, scope, calls, calltree) => { +const eval_statement = (s, scope, calls, modules) => { if(s.type == 'do') { const node = s const {ok, assignments, returned, stmts, calls: nextcalls} = node.stmts.reduce( @@ -1013,7 +1013,7 @@ const eval_statement = (s, scope, calls, calltree) => { assignments: next_assignments, scope: nextscope, calls: next_calls, - } = eval_statement(s, scope, calls, calltree) + } = eval_statement(s, scope, calls, modules) return { ok, returned, @@ -1137,7 +1137,7 @@ const eval_statement = (s, scope, calls, calltree) => { } else if(s.type == 'import') { const children = s.imports.map(i => ( {...i, - result: {ok: true, value: calltree[s.full_import_path].exports[i.value]} + result: {ok: true, value: modules[s.full_import_path][i.value]} } )) const imported_scope = Object.fromEntries(children.map(i => [i.value, i.result.value])) @@ -1262,7 +1262,7 @@ const eval_statement = (s, scope, calls, calltree) => { } } -export const eval_frame = (calltree_node, calltree) => { +export const eval_frame = (calltree_node, modules) => { if(calltree_node.has_more_children) { throw new Error('illegal state') } @@ -1272,7 +1272,7 @@ export const eval_frame = (calltree_node, calltree) => { node, {}, calltree_node.children, - calltree, + modules, ).node } else { // TODO default values for destructuring can be function calls diff --git a/test/test.js b/test/test.js index f0a1147..bb4dc15 100644 --- a/test/test.js +++ b/test/test.js @@ -633,8 +633,8 @@ export const tests = [ 'b' : 'import {a} from "a"; export const b = a*2;', } ) - const calltree = eval_modules(parsed).calltree; - const frame = eval_frame(calltree.b.calls, calltree) + const {calltree, modules} = eval_modules(parsed); + const frame = eval_frame(calltree, modules) assert_equal(frame.children[1].result, {ok: true}) assert_equal(frame.children[1].children[0].children[1].result, {ok: true, value: 2}) }), @@ -757,8 +757,8 @@ export const tests = [ } ) assert_equal(parsed.sorted, ['a', 'b', 'c1', 'c2', 'd']) - const result = eval_modules(parsed).calltree; - assert_equal(result.d.exports.d, 8) + const modules = eval_modules(parsed).modules; + assert_equal(modules.d.d, 8) }), test('module loaded just once', () => { @@ -779,10 +779,10 @@ export const tests = [ 'leaf' : 'export const leaf = {}', } ) - const mods = eval_modules(parsed).calltree + const mods = eval_modules(parsed).modules // Check that the same symbol improted through different paths gives the // same result - assert_equal(mods.root.exports.is_eq, true) + assert_equal(mods.root.is_eq, true) }), test('bug parser pragma external', () => {