mirror of
https://github.com/leporello-js/leporello-js
synced 2026-01-13 21:14:28 -08:00
refactor calltree WIP
This commit is contained in:
@@ -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,14 +174,11 @@ 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) => {
|
||||
if(node.has_more_children) {
|
||||
@@ -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}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
14
src/eval.js
14
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
|
||||
|
||||
12
test/test.js
12
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', () => {
|
||||
|
||||
Reference in New Issue
Block a user