mirror of
https://github.com/leporello-js/leporello-js
synced 2026-01-13 21:14:28 -08:00
WIP
This commit is contained in:
19
src/cmd.js
19
src/cmd.js
@@ -220,15 +220,6 @@ const eval_modules_finished = (state, result, node, toplevel) => {
|
|||||||
if(toplevel) {
|
if(toplevel) {
|
||||||
if(node == state.parse_result.modules[root_calltree_module(next)]) {
|
if(node == state.parse_result.modules[root_calltree_module(next)]) {
|
||||||
active_calltree_node = root_calltree_node(next)
|
active_calltree_node = root_calltree_node(next)
|
||||||
return add_frame(
|
|
||||||
default_expand_path(
|
|
||||||
expand_path(
|
|
||||||
next,
|
|
||||||
active_calltree_node
|
|
||||||
)
|
|
||||||
),
|
|
||||||
active_calltree_node,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
active_calltree_node = null
|
active_calltree_node = null
|
||||||
}
|
}
|
||||||
@@ -244,11 +235,13 @@ const eval_modules_finished = (state, result, node, toplevel) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let result_state
|
||||||
|
|
||||||
if(active_calltree_node == null) {
|
if(active_calltree_node == null) {
|
||||||
const {node, state: next2} = initial_calltree_node(next)
|
const {node, state: next2} = initial_calltree_node(next)
|
||||||
return set_active_calltree_node(next2, null, node)
|
result_state = set_active_calltree_node(next2, null, node)
|
||||||
} else {
|
} else {
|
||||||
return add_frame(
|
result_state = add_frame(
|
||||||
default_expand_path(
|
default_expand_path(
|
||||||
expand_path(
|
expand_path(
|
||||||
next,
|
next,
|
||||||
@@ -258,6 +251,10 @@ const eval_modules_finished = (state, result, node, toplevel) => {
|
|||||||
active_calltree_node,
|
active_calltree_node,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result_state.eval_modules_state == null
|
||||||
|
? result_state
|
||||||
|
: {...result_state, eval_modules_state: null}
|
||||||
}
|
}
|
||||||
|
|
||||||
const input = (state, code, index) => {
|
const input = (state, code, index) => {
|
||||||
|
|||||||
1
src/effects.js
vendored
1
src/effects.js
vendored
@@ -8,6 +8,7 @@ import {
|
|||||||
import {current_cursor_position} from './calltree.js'
|
import {current_cursor_position} from './calltree.js'
|
||||||
import {FLAGS} from './feature_flags.js'
|
import {FLAGS} from './feature_flags.js'
|
||||||
import {exec, FILES_ROOT} from './index.js'
|
import {exec, FILES_ROOT} from './index.js'
|
||||||
|
// TODO remove
|
||||||
import {unwrap_settled_promises} from './unwrap_promises.js'
|
import {unwrap_settled_promises} from './unwrap_promises.js'
|
||||||
|
|
||||||
// Imports in the context of `run_window`, so global variables in loaded
|
// Imports in the context of `run_window`, so global variables in loaded
|
||||||
|
|||||||
34
src/eval.js
34
src/eval.js
@@ -14,6 +14,9 @@ import {
|
|||||||
|
|
||||||
import {has_toplevel_await} from './find_definitions.js'
|
import {has_toplevel_await} from './find_definitions.js'
|
||||||
|
|
||||||
|
// external
|
||||||
|
import {patch_promise} from './patch_promise.js'
|
||||||
|
|
||||||
// TODO: fix error messages. For example, "__fn is not a function"
|
// TODO: fix error messages. For example, "__fn is not a function"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -278,6 +281,13 @@ export const eval_modules = (
|
|||||||
|
|
||||||
// TODO bug if module imported twice, once as external and as regular
|
// TODO bug if module imported twice, once as external and as regular
|
||||||
|
|
||||||
|
patch_promise(
|
||||||
|
globalThis.run_window
|
||||||
|
??
|
||||||
|
// Code executed in test env
|
||||||
|
globalThis
|
||||||
|
)
|
||||||
|
|
||||||
const is_async = has_toplevel_await(parse_result.modules)
|
const is_async = has_toplevel_await(parse_result.modules)
|
||||||
|
|
||||||
const codestring =
|
const codestring =
|
||||||
@@ -937,21 +947,35 @@ const do_eval_frame_expr = (node, scope, callsleft) => {
|
|||||||
return {ok: false, children, calls}
|
return {ok: false, children, calls}
|
||||||
} else {
|
} else {
|
||||||
const expr = children[0]
|
const expr = children[0]
|
||||||
let value
|
let ok, value, error
|
||||||
if(node.operator == '!') {
|
if(node.operator == '!') {
|
||||||
|
ok = true
|
||||||
value = !expr.result.value
|
value = !expr.result.value
|
||||||
} else if(node.operator == 'typeof') {
|
} else if(node.operator == 'typeof') {
|
||||||
|
ok = true
|
||||||
value = typeof(expr.result.value)
|
value = typeof(expr.result.value)
|
||||||
} else if(node.operator == '-') {
|
} else if(node.operator == '-') {
|
||||||
value = - expr.result.value
|
value = - expr.result.value
|
||||||
} else if(node.operator == 'await') {
|
} else if(node.operator == 'await') {
|
||||||
log('expr', expr.result.value.status)
|
const run_window = globalThis.run_window ?? globalThis
|
||||||
value = expr.result.value
|
if(expr.result.value instanceof run_window.Promise.Original) {
|
||||||
//throw new Error('not implemented')
|
const status = expr.result.value.status
|
||||||
|
if(status == null) {
|
||||||
|
// Promise must be already resolved
|
||||||
|
throw new Error('illegal state')
|
||||||
|
} else {
|
||||||
|
ok = status.ok
|
||||||
|
error = status.error
|
||||||
|
value = status.value
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ok = true
|
||||||
|
value = expr.result.value
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error('unknown op')
|
throw new Error('unknown op')
|
||||||
}
|
}
|
||||||
return {ok: true, children, calls, value}
|
return {ok, children, calls, value, error}
|
||||||
}
|
}
|
||||||
} else if(node.type == 'binary' && !['&&', '||', '??'].includes(node.operator)){
|
} else if(node.type == 'binary' && !['&&', '||', '??'].includes(node.operator)){
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export const globals = new Set([
|
export const globals = new Set([
|
||||||
'globalThis',
|
'globalThis',
|
||||||
// TODO Promise,
|
'Promise',
|
||||||
// TODO Symbol
|
// TODO Symbol
|
||||||
'URL',
|
'URL',
|
||||||
'Set',
|
'Set',
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
export const patch_promise = window => {
|
export const patch_promise = window => {
|
||||||
|
|
||||||
// TODO check that it is not already patched
|
|
||||||
if(window.Promise.Original != null) {
|
if(window.Promise.Original != null) {
|
||||||
throw new Error('already patched')
|
// already patched
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
class PromiseWithStatus extends Promise {
|
class PromiseWithStatus extends Promise {
|
||||||
|
|||||||
91
test/test.js
91
test/test.js
@@ -15,10 +15,10 @@ import {
|
|||||||
test_only,
|
test_only,
|
||||||
assert_equal,
|
assert_equal,
|
||||||
stringify,
|
stringify,
|
||||||
assert_code_evals_to,
|
assert_code_evals_to, assert_code_evals_to_async,
|
||||||
assert_code_error,
|
assert_code_error, assert_code_error_async,
|
||||||
parse_modules,
|
parse_modules,
|
||||||
test_initial_state,
|
test_initial_state, test_initial_state_async,
|
||||||
test_deferred_calls_state,
|
test_deferred_calls_state,
|
||||||
print_debug_ct_node,
|
print_debug_ct_node,
|
||||||
} from './utils.js'
|
} from './utils.js'
|
||||||
@@ -2589,15 +2589,80 @@ const y = x()`
|
|||||||
assert_equal(get_deferred_calls(result), null)
|
assert_equal(get_deferred_calls(result), null)
|
||||||
}),
|
}),
|
||||||
|
|
||||||
test('async/await', () => {
|
test('async/await return from async function', async () => {
|
||||||
const code = `
|
await assert_code_evals_to_async(
|
||||||
const x = async () => 123
|
`
|
||||||
const y = async () => await x()
|
const x = async () => 123
|
||||||
await y()
|
const y = async () => await x()
|
||||||
`
|
await y()
|
||||||
const s = test_initial_state(code)
|
`,
|
||||||
const move = COMMANDS.move_cursor(s, code.indexOf('await x()')).state
|
123
|
||||||
log('m', root_calltree_node(move).children[0].children[0].value)
|
)
|
||||||
//log(s.parse_result.modules[''])
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
test('async/await await Promise', async () => {
|
||||||
|
await assert_code_evals_to_async(
|
||||||
|
`
|
||||||
|
await Promise.resolve(123)
|
||||||
|
`,
|
||||||
|
123
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
|
test('async/await await Promise returned from async function', async () => {
|
||||||
|
await assert_code_evals_to_async(
|
||||||
|
`
|
||||||
|
const x = async () => {
|
||||||
|
return Promise.resolve(123)
|
||||||
|
}
|
||||||
|
await x()
|
||||||
|
`,
|
||||||
|
123
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
|
test('async/await throw from async function', async () => {
|
||||||
|
await assert_code_error_async(
|
||||||
|
`
|
||||||
|
const x = async () => { throw 'boom' }
|
||||||
|
await x()
|
||||||
|
`,
|
||||||
|
'boom'
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
|
test('async/await await rejected Promise', async () => {
|
||||||
|
await assert_code_error_async(
|
||||||
|
`
|
||||||
|
await Promise.reject('boom')
|
||||||
|
`,
|
||||||
|
'boom'
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
|
test('async/await await rejected Promise', async () => {
|
||||||
|
await assert_code_error_async(
|
||||||
|
`
|
||||||
|
await Promise.reject('boom')
|
||||||
|
`,
|
||||||
|
'boom'
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
|
test('async/await await rejected Promise returned from async', async () => {
|
||||||
|
await assert_code_error_async(
|
||||||
|
`
|
||||||
|
const x = async () => Promise.reject('boom')
|
||||||
|
await x()
|
||||||
|
`,
|
||||||
|
'boom'
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
//assert_equal('s', active_frame
|
||||||
|
//const result = await s.eval_modules_state
|
||||||
|
//const move = COMMANDS.move_cursor(s, code.indexOf('await x()')).state
|
||||||
|
//log('m', root_calltree_node(move).children[0].children[0].value)
|
||||||
|
//log(s.parse_result.modules[''])
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
import {parse, print_debug_node, load_modules} from '../src/parse_js.js'
|
import {parse, print_debug_node, load_modules} from '../src/parse_js.js'
|
||||||
import {eval_tree, eval_frame} from '../src/eval.js'
|
import {eval_tree, eval_frame} from '../src/eval.js'
|
||||||
|
import {active_frame} from '../src/calltree.js'
|
||||||
import {COMMANDS} from '../src/cmd.js'
|
import {COMMANDS} from '../src/cmd.js'
|
||||||
|
|
||||||
// external
|
|
||||||
import {patch_promise} from '../src/patch_promise.js'
|
|
||||||
|
|
||||||
patch_promise(globalThis)
|
|
||||||
|
|
||||||
Object.assign(globalThis, {log: console.log})
|
Object.assign(globalThis, {log: console.log})
|
||||||
|
|
||||||
export const parse_modules = (entry, modules) =>
|
export const parse_modules = (entry, modules) =>
|
||||||
@@ -32,6 +28,22 @@ export const assert_code_error = (codestring, error) => {
|
|||||||
assert_equal(result.error, error)
|
assert_equal(result.error, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const assert_code_evals_to_async = async (codestring, expected) => {
|
||||||
|
const s = await test_initial_state_async(codestring)
|
||||||
|
const frame = active_frame(s)
|
||||||
|
const result = frame.children[frame.children.length - 1].result
|
||||||
|
assert_equal(result.ok, true)
|
||||||
|
assert_equal(result.value, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const assert_code_error_async = async (codestring, error) => {
|
||||||
|
const s = await test_initial_state_async(codestring)
|
||||||
|
const frame = active_frame(s)
|
||||||
|
const result = frame.children[frame.children.length - 1].result
|
||||||
|
assert_equal(result.ok, false)
|
||||||
|
assert_equal(result.error, error)
|
||||||
|
}
|
||||||
|
|
||||||
export const test_initial_state = (code, state) => {
|
export const test_initial_state = (code, state) => {
|
||||||
return COMMANDS.open_run_window(
|
return COMMANDS.open_run_window(
|
||||||
COMMANDS.get_initial_state(
|
COMMANDS.get_initial_state(
|
||||||
@@ -45,6 +57,18 @@ export const test_initial_state = (code, state) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const test_initial_state_async = async code => {
|
||||||
|
const s = test_initial_state(code)
|
||||||
|
assert_equal(s.eval_modules_state != null, true)
|
||||||
|
const result = await s.eval_modules_state.promise
|
||||||
|
return COMMANDS.eval_modules_finished(
|
||||||
|
s,
|
||||||
|
result,
|
||||||
|
s.eval_modules_state.node,
|
||||||
|
s.eval_modules_state.toplevel
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
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 call, calltree_changed_token
|
||||||
@@ -119,10 +143,13 @@ export const test_only = (message, t) => test(message, t, true)
|
|||||||
// Wrap to Function constructor to hide from calltree view
|
// Wrap to Function constructor to hide from calltree view
|
||||||
// TODO in calltree view, hide fn which has special flag set (see
|
// TODO in calltree view, hide fn which has special flag set (see
|
||||||
// filter_calltree)
|
// filter_calltree)
|
||||||
export const run = Object.defineProperty(new Function('tests', `
|
|
||||||
const run_test = t => {
|
const AsyncFunction = new Function(`return (async () => {}).constructor`)()
|
||||||
|
|
||||||
|
export const run = Object.defineProperty(new AsyncFunction('tests', `
|
||||||
|
const run_test = async t => {
|
||||||
try {
|
try {
|
||||||
t.test()
|
await t.test()
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
if(globalThis.process != null) {
|
if(globalThis.process != null) {
|
||||||
// In node.js runner, fail fast
|
// In node.js runner, fail fast
|
||||||
@@ -142,12 +169,13 @@ export const run = Object.defineProperty(new Function('tests', `
|
|||||||
const only = tests.find(t => t.only)
|
const only = tests.find(t => t.only)
|
||||||
const tests_to_run = only == null ? tests : [only]
|
const tests_to_run = only == null ? tests : [only]
|
||||||
|
|
||||||
// Exec each test. After all tests are done, we rethrow first failer if
|
// Exec each test. After all tests are done, we rethrow first error if
|
||||||
// any. So we will mark root calltree node if one of tests failed
|
// any. So we will mark root calltree node if one of tests failed
|
||||||
const failure = tests_to_run.reduce(
|
const failure = await tests_to_run.reduce(
|
||||||
(failure, t) => {
|
async (failureP, t) => {
|
||||||
const next_failure = run_test(t)
|
const failure = await failureP
|
||||||
return failure ?? next_failure
|
const next_failure = await run_test(t)
|
||||||
|
return (await failure) ?? next_failure
|
||||||
},
|
},
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
@@ -165,7 +193,7 @@ export const run = Object.defineProperty(new Function('tests', `
|
|||||||
if(test == null) {
|
if(test == null) {
|
||||||
throw new Error('test not found')
|
throw new Error('test not found')
|
||||||
} else {
|
} else {
|
||||||
run_test(test)
|
await run_test(test)
|
||||||
if(globalThis.process != null) {
|
if(globalThis.process != null) {
|
||||||
console.log('Ok')
|
console.log('Ok')
|
||||||
}
|
}
|
||||||
|
|||||||
20
x.js
20
x.js
@@ -1,17 +1,31 @@
|
|||||||
/*
|
const delay = () => new Promise(resolve => {
|
||||||
const delay = new Promise(resolve => {
|
|
||||||
setTimeout(resolve, 1000)
|
setTimeout(resolve, 1000)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await [1,2,3,4,5].reduce(
|
||||||
|
async (acc, x) => {
|
||||||
|
console.log('wait')
|
||||||
|
await acc
|
||||||
|
await delay()
|
||||||
|
console.log('wait finish')
|
||||||
|
},
|
||||||
|
Promise.resolve(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
await delay
|
await delay
|
||||||
console.log('x')
|
console.log('x')
|
||||||
export const x = 1
|
export const x = 1
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
const p = {then: y => y(3)}
|
const p = {then: y => y(3)}
|
||||||
|
|
||||||
async function test() {
|
async function test() {
|
||||||
return await p
|
return await p
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// TODO remove
|
// TODO remove
|
||||||
//const x = new Promise((resolve, reject) => resolve(10))
|
//const x = new Promise((resolve, reject) => resolve(10))
|
||||||
@@ -26,4 +40,4 @@ async function test() {
|
|||||||
//x.then(() => {
|
//x.then(() => {
|
||||||
// console.log('x', x.status)
|
// console.log('x', x.status)
|
||||||
//})
|
//})
|
||||||
console.log(await test())
|
//console.log(await test())
|
||||||
|
|||||||
Reference in New Issue
Block a user