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}
|
? {module: node.module}
|
||||||
: node.fn.__location
|
: 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 =>
|
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 =>
|
export const is_native_fn = calltree_node =>
|
||||||
!calltree_node.toplevel && calltree_node.fn.__location == null
|
!calltree_node.toplevel && calltree_node.fn.__location == null
|
||||||
@@ -109,7 +107,7 @@ export const add_frame = (
|
|||||||
) => {
|
) => {
|
||||||
let with_frame
|
let with_frame
|
||||||
if(state.frames?.[active_calltree_node.id] == null) {
|
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)
|
const coloring = color(frame)
|
||||||
with_frame = {...state,
|
with_frame = {...state,
|
||||||
frames: {...state.frames,
|
frames: {...state.frames,
|
||||||
@@ -162,16 +160,13 @@ const replace_calltree_node = (root, node, replacement) => {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO remove, after refactoring async_calls
|
||||||
const replace_calltree_node_in_state = (state, node, replacement) => {
|
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(
|
const replaced = replace_calltree_node(
|
||||||
{
|
{
|
||||||
children: [
|
children: [
|
||||||
calls,
|
root_calltree_node(state),
|
||||||
{children: state.async_calls},
|
{children: state.async_calls},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -179,13 +174,10 @@ const replace_calltree_node_in_state = (state, node, replacement) => {
|
|||||||
replacement
|
replacement
|
||||||
)
|
)
|
||||||
|
|
||||||
const calltree = {...state.calltree,
|
return {...state,
|
||||||
[root]: {
|
calltree: {children: [replaced.children[0]]},
|
||||||
exports,
|
async_calls: replaced.children[1].children,
|
||||||
calls: replaced.children[0],
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return {...state, calltree, async_calls: replaced.children[1].children}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const expand_calltree_node = (state, node) => {
|
const expand_calltree_node = (state, node) => {
|
||||||
@@ -257,7 +249,7 @@ const jump_calltree_node = (_state, _current_calltree_node) => {
|
|||||||
|
|
||||||
const loc = show_body
|
const loc = show_body
|
||||||
? calltree_node_loc(next.active_calltree_node)
|
? 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 {
|
return {
|
||||||
state: {...next, current_module: loc.module},
|
state: {...next, current_module: loc.module},
|
||||||
@@ -452,8 +444,8 @@ const arrow_right = state => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const find_callsite = (calltree, parent, node) => {
|
const find_callsite = (modules, parent, node) => {
|
||||||
const frame = eval_frame(parent, calltree)
|
const frame = eval_frame(parent, modules)
|
||||||
const result = find_node(frame, n => n.result?.call == node)
|
const result = find_node(frame, n => n.result?.call == node)
|
||||||
return {module: calltree_node_loc(parent).module, index: result.index}
|
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: eval_result.calltree,
|
||||||
calltree_actions: eval_result.calltree_actions,
|
calltree_actions: eval_result.calltree_actions,
|
||||||
logs: {logs, log_position: null},
|
logs: {logs, log_position: null},
|
||||||
|
modules: eval_result.modules,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,6 +70,7 @@ const run_code = (s, index, dirty_files) => {
|
|||||||
...s,
|
...s,
|
||||||
parse_result,
|
parse_result,
|
||||||
calltree: null,
|
calltree: null,
|
||||||
|
modules: null,
|
||||||
async_calls: null,
|
async_calls: null,
|
||||||
|
|
||||||
// Shows that calltree is brand new and requires entire rerender
|
// 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
|
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).
|
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
|
collecting information for editor
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -602,7 +602,7 @@ export const eval_tree = node => {
|
|||||||
modules: {'': node}},
|
modules: {'': node}},
|
||||||
sorted: ['']
|
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') {
|
if(s.type == 'do') {
|
||||||
const node = s
|
const node = s
|
||||||
const {ok, assignments, returned, stmts, calls: nextcalls} = node.stmts.reduce(
|
const {ok, assignments, returned, stmts, calls: nextcalls} = node.stmts.reduce(
|
||||||
@@ -1013,7 +1013,7 @@ const eval_statement = (s, scope, calls, calltree) => {
|
|||||||
assignments: next_assignments,
|
assignments: next_assignments,
|
||||||
scope: nextscope,
|
scope: nextscope,
|
||||||
calls: next_calls,
|
calls: next_calls,
|
||||||
} = eval_statement(s, scope, calls, calltree)
|
} = eval_statement(s, scope, calls, modules)
|
||||||
return {
|
return {
|
||||||
ok,
|
ok,
|
||||||
returned,
|
returned,
|
||||||
@@ -1137,7 +1137,7 @@ const eval_statement = (s, scope, calls, calltree) => {
|
|||||||
} else if(s.type == 'import') {
|
} else if(s.type == 'import') {
|
||||||
const children = s.imports.map(i => (
|
const children = s.imports.map(i => (
|
||||||
{...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]))
|
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) {
|
if(calltree_node.has_more_children) {
|
||||||
throw new Error('illegal state')
|
throw new Error('illegal state')
|
||||||
}
|
}
|
||||||
@@ -1272,7 +1272,7 @@ export const eval_frame = (calltree_node, calltree) => {
|
|||||||
node,
|
node,
|
||||||
{},
|
{},
|
||||||
calltree_node.children,
|
calltree_node.children,
|
||||||
calltree,
|
modules,
|
||||||
).node
|
).node
|
||||||
} else {
|
} else {
|
||||||
// TODO default values for destructuring can be function calls
|
// 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;',
|
'b' : 'import {a} from "a"; export const b = a*2;',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
const calltree = eval_modules(parsed).calltree;
|
const {calltree, modules} = eval_modules(parsed);
|
||||||
const frame = eval_frame(calltree.b.calls, calltree)
|
const frame = eval_frame(calltree, modules)
|
||||||
assert_equal(frame.children[1].result, {ok: true})
|
assert_equal(frame.children[1].result, {ok: true})
|
||||||
assert_equal(frame.children[1].children[0].children[1].result, {ok: true, value: 2})
|
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'])
|
assert_equal(parsed.sorted, ['a', 'b', 'c1', 'c2', 'd'])
|
||||||
const result = eval_modules(parsed).calltree;
|
const modules = eval_modules(parsed).modules;
|
||||||
assert_equal(result.d.exports.d, 8)
|
assert_equal(modules.d.d, 8)
|
||||||
}),
|
}),
|
||||||
|
|
||||||
test('module loaded just once', () => {
|
test('module loaded just once', () => {
|
||||||
@@ -779,10 +779,10 @@ export const tests = [
|
|||||||
'leaf' : 'export const leaf = {}',
|
'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
|
// Check that the same symbol improted through different paths gives the
|
||||||
// same result
|
// same result
|
||||||
assert_equal(mods.root.exports.is_eq, true)
|
assert_equal(mods.root.is_eq, true)
|
||||||
}),
|
}),
|
||||||
|
|
||||||
test('bug parser pragma external', () => {
|
test('bug parser pragma external', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user