mirror of
https://github.com/leporello-js/leporello-js
synced 2026-01-13 21:14:28 -08:00
WIP
This commit is contained in:
@@ -7,6 +7,7 @@ import {eval_frame} from './eval.js'
|
|||||||
export const pp_calltree = tree => ({
|
export const pp_calltree = tree => ({
|
||||||
id: tree.id,
|
id: tree.id,
|
||||||
ok: tree.ok,
|
ok: tree.ok,
|
||||||
|
args: tree.args,
|
||||||
value: tree.value,
|
value: tree.value,
|
||||||
is_log: tree.is_log,
|
is_log: tree.is_log,
|
||||||
has_more_children: tree.has_more_children,
|
has_more_children: tree.has_more_children,
|
||||||
|
|||||||
49
src/cmd.js
49
src/cmd.js
@@ -18,31 +18,39 @@ import {
|
|||||||
set_cursor_position, current_cursor_position, set_location
|
set_cursor_position, current_cursor_position, set_location
|
||||||
} from './calltree.js'
|
} from './calltree.js'
|
||||||
|
|
||||||
const collect_logs = call =>
|
const collect_logs = (logs, call) => {
|
||||||
collect_nodes_with_parents(call, n => n.is_log)
|
const id_to_log = new Map(
|
||||||
.map(({parent, node}) => (
|
collect_nodes_with_parents(call, n => n.is_log)
|
||||||
{
|
.map(({parent, node}) => (
|
||||||
id: node.id,
|
[
|
||||||
toplevel: parent.toplevel,
|
node.id,
|
||||||
module: parent.toplevel
|
{
|
||||||
? parent.module
|
id: node.id,
|
||||||
: parent.fn.__location.module,
|
toplevel: parent.toplevel,
|
||||||
parent_name: parent.fn?.name,
|
module: parent.toplevel
|
||||||
args: node.args,
|
? parent.module
|
||||||
log_fn_name: node.fn.name,
|
: parent.fn.__location.module,
|
||||||
}
|
parent_name: parent.fn?.name,
|
||||||
))
|
args: node.args,
|
||||||
|
log_fn_name: node.fn.name,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
))
|
||||||
|
)
|
||||||
|
return logs.map(l => id_to_log.get(l.id))
|
||||||
|
}
|
||||||
|
|
||||||
const apply_eval_result = (state, eval_result) => {
|
const apply_eval_result = (state, eval_result) => {
|
||||||
// TODO what if console.log called from native fn (like Array::map)?
|
// TODO what if console.log called from native fn (like Array::map)?
|
||||||
// Currently it is not recorded. Maybe we should monkey patch `console`?
|
// Currently it is not recorded. Maybe we should monkey patch `console`?
|
||||||
const logs = collect_logs(eval_result.calltree)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
calltree: make_calltree(eval_result.calltree, null),
|
calltree: make_calltree(eval_result.calltree, null),
|
||||||
calltree_actions: eval_result.calltree_actions,
|
calltree_actions: eval_result.calltree_actions,
|
||||||
logs: {logs, log_position: null},
|
logs: {
|
||||||
|
logs: collect_logs(eval_result.logs, eval_result.calltree),
|
||||||
|
log_position: null
|
||||||
|
},
|
||||||
modules: eval_result.modules,
|
modules: eval_result.modules,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -751,7 +759,7 @@ const move_cursor = (s, index) => {
|
|||||||
return do_move_cursor(state, index)
|
return do_move_cursor(state, index)
|
||||||
}
|
}
|
||||||
|
|
||||||
const on_deferred_call = (state, call, calltree_changed_token) => {
|
const on_deferred_call = (state, call, calltree_changed_token, logs) => {
|
||||||
if(state.calltree_changed_token != calltree_changed_token) {
|
if(state.calltree_changed_token != calltree_changed_token) {
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
@@ -760,7 +768,10 @@ const on_deferred_call = (state, call, calltree_changed_token) => {
|
|||||||
root_calltree_node(state),
|
root_calltree_node(state),
|
||||||
[...(get_deferred_calls(state) ?? []), call],
|
[...(get_deferred_calls(state) ?? []), call],
|
||||||
),
|
),
|
||||||
logs: {...state.logs, logs: state.logs.logs.concat(collect_logs(call))},
|
logs: {
|
||||||
|
...state.logs,
|
||||||
|
logs: state.logs.logs.concat(collect_logs(logs, call))
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
57
src/eval.js
57
src/eval.js
@@ -290,6 +290,8 @@ export const eval_modules = (
|
|||||||
// TODO use native array for stack for perf?
|
// TODO use native array for stack for perf?
|
||||||
const stack = new Array()
|
const stack = new Array()
|
||||||
|
|
||||||
|
let logs = []
|
||||||
|
|
||||||
let call_counter = 0
|
let call_counter = 0
|
||||||
|
|
||||||
let current_module
|
let current_module
|
||||||
@@ -336,7 +338,7 @@ export const eval_modules = (
|
|||||||
let is_found_deferred_call = false
|
let is_found_deferred_call = false
|
||||||
let i
|
let i
|
||||||
|
|
||||||
let {calltree} = run()
|
let {calltree, logs} = run()
|
||||||
|
|
||||||
is_recording_deferred_calls = false
|
is_recording_deferred_calls = false
|
||||||
if(found_call == null && deferred_calls != null) {
|
if(found_call == null && deferred_calls != null) {
|
||||||
@@ -365,7 +367,8 @@ export const eval_modules = (
|
|||||||
is_found_deferred_call,
|
is_found_deferred_call,
|
||||||
deferred_call_index: i,
|
deferred_call_index: i,
|
||||||
calltree,
|
calltree,
|
||||||
call
|
call,
|
||||||
|
logs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,17 +421,20 @@ export const eval_modules = (
|
|||||||
try {
|
try {
|
||||||
value = fn(...args)
|
value = fn(...args)
|
||||||
ok = true
|
ok = true
|
||||||
return value instanceof Promise.Original
|
if(value instanceof Promise.Original) {
|
||||||
? value
|
set_record_call()
|
||||||
.then(v => {
|
return value
|
||||||
value.status = {ok: true, value: v}
|
.then(v => {
|
||||||
return v
|
value.status = {ok: true, value: v}
|
||||||
})
|
return v
|
||||||
.catch(e => {
|
})
|
||||||
value.status = {ok: false, error: e}
|
.catch(e => {
|
||||||
throw e
|
value.status = {ok: false, error: e}
|
||||||
})
|
throw e
|
||||||
: value
|
})
|
||||||
|
} else {
|
||||||
|
return value
|
||||||
|
}
|
||||||
} catch(_error) {
|
} catch(_error) {
|
||||||
ok = false
|
ok = false
|
||||||
error = _error
|
error = _error
|
||||||
@@ -476,7 +482,9 @@ export const eval_modules = (
|
|||||||
}
|
}
|
||||||
const call = children[0]
|
const call = children[0]
|
||||||
children = null
|
children = null
|
||||||
on_deferred_call(call, calltree_changed_token)
|
const _logs = logs
|
||||||
|
logs = []
|
||||||
|
on_deferred_call(call, calltree_changed_token, _logs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -547,6 +555,11 @@ export const eval_modules = (
|
|||||||
is_log,
|
is_log,
|
||||||
is_new,
|
is_new,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(is_log) {
|
||||||
|
// TODO do not collect logs on find_call?
|
||||||
|
logs.push(call)
|
||||||
|
}
|
||||||
|
|
||||||
const should_record_call = stack.pop()
|
const should_record_call = stack.pop()
|
||||||
|
|
||||||
@@ -602,8 +615,10 @@ export const eval_modules = (
|
|||||||
current_call.children = children
|
current_call.children = children
|
||||||
if(!current_call.ok) {
|
if(!current_call.ok) {
|
||||||
is_recording_deferred_calls = true
|
is_recording_deferred_calls = true
|
||||||
|
const _logs = logs
|
||||||
|
logs = []
|
||||||
children = null
|
children = null
|
||||||
return { modules: __modules, calltree: current_call }
|
return { modules: __modules, calltree: current_call, logs: _logs }
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
@@ -611,8 +626,10 @@ export const eval_modules = (
|
|||||||
+
|
+
|
||||||
`
|
`
|
||||||
is_recording_deferred_calls = true
|
is_recording_deferred_calls = true
|
||||||
|
const _logs = logs
|
||||||
|
logs = []
|
||||||
children = null
|
children = null
|
||||||
return { modules: __modules, calltree: current_call }
|
return { modules: __modules, calltree: current_call, logs: _logs }
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -634,10 +651,11 @@ export const eval_modules = (
|
|||||||
: map_object(external_imports, (name, {module}) => module),
|
: map_object(external_imports, (name, {module}) => module),
|
||||||
|
|
||||||
/* on_deferred_call */
|
/* on_deferred_call */
|
||||||
(call, calltree_changed_token) => {
|
(call, calltree_changed_token, logs) => {
|
||||||
return on_deferred_call(
|
return on_deferred_call(
|
||||||
assign_code(parse_result.modules, call),
|
assign_code(parse_result.modules, call),
|
||||||
calltree_changed_token,
|
calltree_changed_token,
|
||||||
|
logs,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -655,7 +673,8 @@ export const eval_modules = (
|
|||||||
is_found_deferred_call,
|
is_found_deferred_call,
|
||||||
deferred_call_index,
|
deferred_call_index,
|
||||||
calltree,
|
calltree,
|
||||||
call
|
call,
|
||||||
|
logs,
|
||||||
} = actions.find_call(loc, deferred_calls)
|
} = actions.find_call(loc, deferred_calls)
|
||||||
return {
|
return {
|
||||||
is_found_deferred_call,
|
is_found_deferred_call,
|
||||||
@@ -664,6 +683,7 @@ export const eval_modules = (
|
|||||||
// TODO: `call` does not have `code` property here. Currently it is
|
// TODO: `call` does not have `code` property here. Currently it is
|
||||||
// worked around by callers. Refactor
|
// worked around by callers. Refactor
|
||||||
call,
|
call,
|
||||||
|
logs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -676,6 +696,7 @@ export const eval_modules = (
|
|||||||
modules: result.modules,
|
modules: result.modules,
|
||||||
calltree: assign_code(parse_result.modules, result.calltree),
|
calltree: assign_code(parse_result.modules, result.calltree),
|
||||||
call: result.call,
|
call: result.call,
|
||||||
|
logs: result.logs,
|
||||||
calltree_actions,
|
calltree_actions,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
81
test/test.js
81
test/test.js
@@ -25,6 +25,20 @@ import {
|
|||||||
|
|
||||||
export const tests = [
|
export const tests = [
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
/*
|
||||||
|
test('trace', () => {
|
||||||
|
assert_code_evals_to(`
|
||||||
|
const trace = () => 1;
|
||||||
|
trace()
|
||||||
|
|
||||||
|
`,
|
||||||
|
|
||||||
|
1
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
*/
|
||||||
|
|
||||||
test('invalid token in the beginning', () => {
|
test('invalid token in the beginning', () => {
|
||||||
const result = parse('# import')
|
const result = parse('# import')
|
||||||
assert_equal(result, {
|
assert_equal(result, {
|
||||||
@@ -2697,38 +2711,57 @@ const y = x()`
|
|||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
|
||||||
//test('async/await calltree', async () => {
|
test('async/await calltree', async () => {
|
||||||
// const i = await test_initial_state_async(`
|
const i = await test_initial_state_async(`
|
||||||
// const x = () => 1
|
const x = () => 1
|
||||||
// const delay = async time => {
|
const delay = async time => {
|
||||||
// await 1 //Promise.resolve()
|
await 1 //TODO Promise.resolve()
|
||||||
// x()
|
x()
|
||||||
// }
|
}
|
||||||
// await delay(3)
|
await delay(3)
|
||||||
// /* TODO
|
/* TODO
|
||||||
// await Promise.all([
|
await Promise.all([
|
||||||
// delay(3),
|
delay(3),
|
||||||
// ])
|
])
|
||||||
// */
|
*/
|
||||||
// `)
|
`)
|
||||||
// log(pp_calltree(root_calltree_node(i)))
|
const root = root_calltree_node(i)
|
||||||
// assert_equal(root_calltree_node(i).children.length, 1)
|
assert_equal(root.children.length, 1)
|
||||||
//}),
|
const call_delay = root.children[0]
|
||||||
|
assert_equal(call_delay.fn.name, 'delay')
|
||||||
|
assert_equal(call_delay.fn.name, 'delay')
|
||||||
|
}),
|
||||||
|
|
||||||
|
// TODO
|
||||||
test('async/await logs out of order', async () => {
|
test('async/await logs out of order', async () => {
|
||||||
const i = await test_initial_state_async(`
|
const i = await test_initial_state_async(`
|
||||||
const delay = async time => {
|
const delay = async time => {
|
||||||
await new Promise(res => globalThis.setTimeout(res, time*10))
|
await new Promise(res => globalThis.setTimeout(res, time*10))
|
||||||
console.log(time)
|
console.log(time)
|
||||||
}
|
}
|
||||||
await Promise.all([
|
|
||||||
delay(3),
|
await Promise.all([delay(2), delay(1)])
|
||||||
delay(2),
|
|
||||||
delay(1),
|
|
||||||
])
|
|
||||||
`)
|
`)
|
||||||
const logs = i.logs.logs.map(l => l.args[0])
|
const logs = i.logs.logs.map(l => l.args[0])
|
||||||
assert_equal(logs, [1,2,3])
|
assert_equal(logs, [1, 2])
|
||||||
})
|
}),
|
||||||
|
|
||||||
|
test('async/await logs out of order', async () => {
|
||||||
|
const i = await test_initial_state_async(`
|
||||||
|
// Init promises p1 and p2 that are resolved in different order (p2 then
|
||||||
|
// p1)
|
||||||
|
const p2 = Promise.resolve(2)
|
||||||
|
const p1 = p2.then(() => 1)
|
||||||
|
|
||||||
|
const log = async p => {
|
||||||
|
const v = await p
|
||||||
|
console.log(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all([log(p1), log(p2)])
|
||||||
|
`)
|
||||||
|
const logs = i.logs.logs.map(l => l.args[0])
|
||||||
|
assert_equal(logs, [2, 1])
|
||||||
|
}),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -71,14 +71,13 @@ export const test_initial_state_async = async code => {
|
|||||||
|
|
||||||
export const test_deferred_calls_state = code => {
|
export const test_deferred_calls_state = code => {
|
||||||
const {get_deferred_call, on_deferred_call} = (new Function(`
|
const {get_deferred_call, on_deferred_call} = (new Function(`
|
||||||
let call, calltree_changed_token
|
let args
|
||||||
return {
|
return {
|
||||||
get_deferred_call() {
|
get_deferred_call() {
|
||||||
return [call, calltree_changed_token]
|
return args
|
||||||
},
|
},
|
||||||
on_deferred_call(_call, _calltree_changed_token) {
|
on_deferred_call(..._args) {
|
||||||
call = _call
|
args = _args
|
||||||
calltree_changed_token = _calltree_changed_token
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`))()
|
`))()
|
||||||
|
|||||||
Reference in New Issue
Block a user