mirror of
https://github.com/leporello-js/leporello-js
synced 2026-01-13 13:04:30 -08:00
refactor globals
This commit is contained in:
@@ -77,7 +77,7 @@ const run_code = (s, dirty_files) => {
|
|||||||
return s.files[module]
|
return s.files[module]
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
}, s.globals)
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
...s,
|
...s,
|
||||||
@@ -839,12 +839,15 @@ const create_file = (state, dir, current_module) => {
|
|||||||
return {...load_dir(state, dir), current_module}
|
return {...load_dir(state, dir), current_module}
|
||||||
}
|
}
|
||||||
|
|
||||||
const open_run_window = state => {
|
const open_run_window = (state, globals) => {
|
||||||
// After we reopen run window, we should reload external modules in the
|
// After we reopen run window, we should reload external modules in the
|
||||||
// context of new window. Clear external_imports_cache
|
// context of new window. Clear external_imports_cache
|
||||||
return run_code({
|
return run_code({
|
||||||
...state,
|
...state,
|
||||||
|
globals,
|
||||||
external_imports_cache: null,
|
external_imports_cache: null,
|
||||||
|
// Bust parse result cache because list of globals may change
|
||||||
|
parse_result: null,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
import {set_push, set_diff, set_union, map_object, map_find, uniq} from './utils.js'
|
import {set_push, set_diff, set_union, map_object, map_find, uniq} from './utils.js'
|
||||||
import {collect_destructuring_identifiers, collect_imports, ancestry, find_leaf} from './ast_utils.js'
|
import {collect_destructuring_identifiers, collect_imports, ancestry, find_leaf} from './ast_utils.js'
|
||||||
|
|
||||||
import {globals} from './globals.js'
|
|
||||||
|
|
||||||
const map_find_definitions = (nodes, mapper) => {
|
const map_find_definitions = (nodes, mapper) => {
|
||||||
const result = nodes.map(mapper)
|
const result = nodes.map(mapper)
|
||||||
const undeclared = result.reduce(
|
const undeclared = result.reduce(
|
||||||
@@ -74,7 +72,14 @@ const add_trivial_definition = node => {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// TODO in same pass find already declared
|
// TODO in same pass find already declared
|
||||||
export const find_definitions = (ast, scope = {}, closure_scope = {}, module_name) => {
|
export const find_definitions = (ast, globals, scope = {}, closure_scope = {}, module_name) => {
|
||||||
|
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
if(!globals instanceof Set) {
|
||||||
|
throw new Error('not a set')
|
||||||
|
}
|
||||||
|
|
||||||
if(ast.type == 'identifier'){
|
if(ast.type == 'identifier'){
|
||||||
if(ast.definition != null) {
|
if(ast.definition != null) {
|
||||||
// Definition previously added by add_trivial_definition
|
// Definition previously added by add_trivial_definition
|
||||||
@@ -112,7 +117,7 @@ export const find_definitions = (ast, scope = {}, closure_scope = {}, module_nam
|
|||||||
)
|
)
|
||||||
const local_scope = children_with_scope.scope
|
const local_scope = children_with_scope.scope
|
||||||
const {nodes, undeclared, closed} = map_find_definitions(children_with_scope.children, cs =>
|
const {nodes, undeclared, closed} = map_find_definitions(children_with_scope.children, cs =>
|
||||||
find_definitions(cs.node, {...scope, ...cs.scope}, local_scope, module_name)
|
find_definitions(cs.node, globals, {...scope, ...cs.scope}, local_scope, module_name)
|
||||||
)
|
)
|
||||||
return {
|
return {
|
||||||
node: {...ast, children: nodes},
|
node: {...ast, children: nodes},
|
||||||
@@ -125,7 +130,7 @@ export const find_definitions = (ast, scope = {}, closure_scope = {}, module_nam
|
|||||||
a.value, a
|
a.value, a
|
||||||
]))
|
]))
|
||||||
const {nodes, undeclared, closed} = map_find_definitions(ast.children,
|
const {nodes, undeclared, closed} = map_find_definitions(ast.children,
|
||||||
node => find_definitions(node, {...scope, ...closure_scope, ...args_scope})
|
node => find_definitions(node, globals, {...scope, ...closure_scope, ...args_scope})
|
||||||
)
|
)
|
||||||
const next_closed = set_diff(closed, new Set(args_identifiers.map(a => a.value)))
|
const next_closed = set_diff(closed, new Set(args_identifiers.map(a => a.value)))
|
||||||
return {
|
return {
|
||||||
@@ -153,7 +158,7 @@ export const find_definitions = (ast, scope = {}, closure_scope = {}, module_nam
|
|||||||
}
|
}
|
||||||
|
|
||||||
const {nodes, undeclared, closed} = map_find_definitions(children,
|
const {nodes, undeclared, closed} = map_find_definitions(children,
|
||||||
c => find_definitions(c, scope, closure_scope)
|
c => find_definitions(c, globals, scope, closure_scope)
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
export const globals = new Set(Object.getOwnPropertyNames(globalThis))
|
|
||||||
|
|
||||||
// Not available in node.js, but add to use in tests
|
|
||||||
globals.add('fetch')
|
|
||||||
10
src/index.js
10
src/index.js
@@ -67,7 +67,10 @@ export const open_run_window = state => {
|
|||||||
// event fired. TODO: better register SW explicitly and don't rely on
|
// event fired. TODO: better register SW explicitly and don't rely on
|
||||||
// already registered SW?
|
// already registered SW?
|
||||||
const onload = () => {
|
const onload = () => {
|
||||||
exec('open_run_window')
|
exec(
|
||||||
|
'open_run_window',
|
||||||
|
new Set(Object.getOwnPropertyNames(globalThis.run_window))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const add_load_handler = () => {
|
const add_load_handler = () => {
|
||||||
@@ -170,7 +173,10 @@ export const init = (container, _COMMANDS) => {
|
|||||||
render_initial_state(ui, state)
|
render_initial_state(ui, state)
|
||||||
|
|
||||||
open_run_iframe(state, () => {
|
open_run_iframe(state, () => {
|
||||||
exec('open_run_window')
|
exec(
|
||||||
|
'open_run_window',
|
||||||
|
new Set(Object.getOwnPropertyNames(globalThis.run_window))
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1580,7 +1580,7 @@ const deduce_fn_names = node => {
|
|||||||
return do_deduce_fn_names(node, null)[0]
|
return do_deduce_fn_names(node, null)[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const parse = (str, is_module = false, module_name) => {
|
export const parse = (str, globals, is_module = false, module_name) => {
|
||||||
|
|
||||||
// Add string to node for debugging
|
// Add string to node for debugging
|
||||||
// TODO remove, only need for debug
|
// TODO remove, only need for debug
|
||||||
@@ -1629,6 +1629,7 @@ export const parse = (str, is_module = false, module_name) => {
|
|||||||
} else {
|
} else {
|
||||||
const {node, undeclared} = find_definitions(
|
const {node, undeclared} = find_definitions(
|
||||||
update_children(result.value),
|
update_children(result.value),
|
||||||
|
globals,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
module_name
|
module_name
|
||||||
@@ -1675,7 +1676,7 @@ export const print_debug_node = node => {
|
|||||||
return stringify(do_print_debug_node(node))
|
return stringify(do_print_debug_node(node))
|
||||||
}
|
}
|
||||||
|
|
||||||
const do_load_modules = (module_names, loader, already_loaded) => {
|
const do_load_modules = (module_names, loader, already_loaded, globals) => {
|
||||||
const parsed = module_names
|
const parsed = module_names
|
||||||
.filter(module_name => already_loaded[module_name] == null)
|
.filter(module_name => already_loaded[module_name] == null)
|
||||||
.map(module_name => {
|
.map(module_name => {
|
||||||
@@ -1686,7 +1687,7 @@ const do_load_modules = (module_names, loader, already_loaded) => {
|
|||||||
// Allows cache parse result
|
// Allows cache parse result
|
||||||
return [module_name, m]
|
return [module_name, m]
|
||||||
} else if(typeof(m) == 'string') {
|
} else if(typeof(m) == 'string') {
|
||||||
return [module_name, parse(m, true, module_name)]
|
return [module_name, parse(m, globals, true, module_name)]
|
||||||
} else {
|
} else {
|
||||||
throw new Error('illegal state')
|
throw new Error('illegal state')
|
||||||
}
|
}
|
||||||
@@ -1728,7 +1729,12 @@ const do_load_modules = (module_names, loader, already_loaded) => {
|
|||||||
// TODO when refactor this function to async, do not forget that different
|
// TODO when refactor this function to async, do not forget that different
|
||||||
// deps can be loaded independently. So dont just put `await Promise.all(`
|
// deps can be loaded independently. So dont just put `await Promise.all(`
|
||||||
// here
|
// here
|
||||||
const loaded_deps = do_load_modules(deps, loader, {...already_loaded, ...modules})
|
const loaded_deps = do_load_modules(
|
||||||
|
deps,
|
||||||
|
loader,
|
||||||
|
{...already_loaded, ...modules},
|
||||||
|
globals
|
||||||
|
)
|
||||||
if(loaded_deps.ok) {
|
if(loaded_deps.ok) {
|
||||||
return {
|
return {
|
||||||
ok: true,
|
ok: true,
|
||||||
@@ -1770,11 +1776,11 @@ const do_load_modules = (module_names, loader, already_loaded) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const load_modules = (entry_module, loader) => {
|
export const load_modules = (entry_module, loader, globals) => {
|
||||||
// TODO check_imports. detect cycles while modules are loading, in
|
// TODO check_imports. detect cycles while modules are loading, in
|
||||||
// do_load_modules
|
// do_load_modules
|
||||||
|
|
||||||
const result = do_load_modules([entry_module], loader, {})
|
const result = do_load_modules([entry_module], loader, {}, globals)
|
||||||
if(!result.ok) {
|
if(!result.ok) {
|
||||||
return result
|
return result
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
49
test/test.js
49
test/test.js
@@ -1,5 +1,5 @@
|
|||||||
import {find_leaf, ancestry, find_node} from '../src/ast_utils.js'
|
import {find_leaf, ancestry, find_node} from '../src/ast_utils.js'
|
||||||
import {parse, print_debug_node} from '../src/parse_js.js'
|
import {print_debug_node} from '../src/parse_js.js'
|
||||||
import {eval_frame, eval_modules} from '../src/eval.js'
|
import {eval_frame, eval_modules} from '../src/eval.js'
|
||||||
import {COMMANDS} from '../src/cmd.js'
|
import {COMMANDS} from '../src/cmd.js'
|
||||||
import {
|
import {
|
||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
test_only,
|
test_only,
|
||||||
assert_equal,
|
assert_equal,
|
||||||
stringify,
|
stringify,
|
||||||
|
do_parse,
|
||||||
eval_tree,
|
eval_tree,
|
||||||
assert_code_evals_to, assert_code_evals_to_async,
|
assert_code_evals_to, assert_code_evals_to_async,
|
||||||
assert_code_error, assert_code_error_async,
|
assert_code_error, assert_code_error_async,
|
||||||
@@ -30,7 +31,7 @@ import {
|
|||||||
export const tests = [
|
export const tests = [
|
||||||
|
|
||||||
test('invalid token in the beginning', () => {
|
test('invalid token in the beginning', () => {
|
||||||
const result = parse('# import')
|
const result = do_parse('# import')
|
||||||
assert_equal(result, {
|
assert_equal(result, {
|
||||||
ok: false,
|
ok: false,
|
||||||
problems: [ { message: 'unexpected lexical token', index: 0 } ]
|
problems: [ { message: 'unexpected lexical token', index: 0 } ]
|
||||||
@@ -38,7 +39,7 @@ export const tests = [
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
test('invalid token in the middle', () => {
|
test('invalid token in the middle', () => {
|
||||||
const result = parse(': # import')
|
const result = do_parse(': # import')
|
||||||
assert_equal(result, {
|
assert_equal(result, {
|
||||||
ok: false,
|
ok: false,
|
||||||
problems: [ { message: 'unexpected lexical token', index: 2 } ]
|
problems: [ { message: 'unexpected lexical token', index: 2 } ]
|
||||||
@@ -46,7 +47,7 @@ export const tests = [
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
test('invalid token in the end', () => {
|
test('invalid token in the end', () => {
|
||||||
const result = parse(': ^')
|
const result = do_parse(': ^')
|
||||||
assert_equal(result, {
|
assert_equal(result, {
|
||||||
ok: false,
|
ok: false,
|
||||||
problems: [ { message: 'unexpected lexical token', index: 2 } ]
|
problems: [ { message: 'unexpected lexical token', index: 2 } ]
|
||||||
@@ -61,7 +62,7 @@ export const tests = [
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
test('empty if branch', () => {
|
test('empty if branch', () => {
|
||||||
const r = parse(`
|
const r = do_parse(`
|
||||||
if(true) {
|
if(true) {
|
||||||
} else {
|
} else {
|
||||||
}
|
}
|
||||||
@@ -70,7 +71,7 @@ export const tests = [
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
test('Must be finished by eof', () => {
|
test('Must be finished by eof', () => {
|
||||||
const result = parse('}')
|
const result = do_parse('}')
|
||||||
assert_equal(result.ok, false)
|
assert_equal(result.ok, false)
|
||||||
}),
|
}),
|
||||||
|
|
||||||
@@ -330,7 +331,7 @@ export const tests = [
|
|||||||
};
|
};
|
||||||
x
|
x
|
||||||
`
|
`
|
||||||
const parse_result = parse(code)
|
const parse_result = do_parse(code)
|
||||||
const assignment = find_leaf(
|
const assignment = find_leaf(
|
||||||
parse_result.node,
|
parse_result.node,
|
||||||
code.indexOf('x = 0')
|
code.indexOf('x = 0')
|
||||||
@@ -397,7 +398,7 @@ export const tests = [
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
test('ASI_1', () => {
|
test('ASI_1', () => {
|
||||||
const parse_result = parse(`
|
const parse_result = do_parse(`
|
||||||
1
|
1
|
||||||
const y = 2;
|
const y = 2;
|
||||||
`)
|
`)
|
||||||
@@ -409,7 +410,7 @@ export const tests = [
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
test('ASI_2', () => {
|
test('ASI_2', () => {
|
||||||
const parse_result = parse(`
|
const parse_result = do_parse(`
|
||||||
1
|
1
|
||||||
2
|
2
|
||||||
`)
|
`)
|
||||||
@@ -423,14 +424,14 @@ export const tests = [
|
|||||||
test('ASI_restrited', () => {
|
test('ASI_restrited', () => {
|
||||||
// Currently we forbid bare return statement, TODO
|
// Currently we forbid bare return statement, TODO
|
||||||
assert_equal(
|
assert_equal(
|
||||||
parse(`
|
do_parse(`
|
||||||
return
|
return
|
||||||
1
|
1
|
||||||
`).ok,
|
`).ok,
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
assert_equal(
|
assert_equal(
|
||||||
parse(`
|
do_parse(`
|
||||||
throw
|
throw
|
||||||
1
|
1
|
||||||
`).ok,
|
`).ok,
|
||||||
@@ -921,7 +922,7 @@ export const tests = [
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
test('bug parser pragma external', () => {
|
test('bug parser pragma external', () => {
|
||||||
const result = parse(`
|
const result = do_parse(`
|
||||||
// external
|
// external
|
||||||
`)
|
`)
|
||||||
assert_equal(result.ok, true)
|
assert_equal(result.ok, true)
|
||||||
@@ -1158,7 +1159,7 @@ export const tests = [
|
|||||||
const bar = baz => qux(foo, bar, baz, quux);
|
const bar = baz => qux(foo, bar, baz, quux);
|
||||||
const qux = 3;
|
const qux = 3;
|
||||||
`
|
`
|
||||||
const result = parse(undeclared_test)
|
const result = do_parse(undeclared_test)
|
||||||
assert_equal(result.problems.length, 1)
|
assert_equal(result.problems.length, 1)
|
||||||
assert_equal(result.problems[0].message, 'undeclared identifier: quux')
|
assert_equal(result.problems[0].message, 'undeclared identifier: quux')
|
||||||
}),
|
}),
|
||||||
@@ -1177,7 +1178,7 @@ export const tests = [
|
|||||||
const code = `
|
const code = `
|
||||||
const x = x;
|
const x = x;
|
||||||
`
|
`
|
||||||
return assert_equal(parse(code).problems[0].message, 'undeclared identifier: x')
|
return assert_equal(do_parse(code).problems[0].message, 'undeclared identifier: x')
|
||||||
}),
|
}),
|
||||||
|
|
||||||
test('function hoisting', () => {
|
test('function hoisting', () => {
|
||||||
@@ -1220,6 +1221,18 @@ export const tests = [
|
|||||||
assert_equal(s2.active_calltree_node.value, 5)
|
assert_equal(s2.active_calltree_node.value, 5)
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
/*
|
||||||
|
test('await only in async', () => {
|
||||||
|
const code = `
|
||||||
|
() => {
|
||||||
|
await 1
|
||||||
|
}
|
||||||
|
`
|
||||||
|
console.log(do_parse(code).problems[0])
|
||||||
|
//return assert_equal(do_parse(code).problems[0].message, 'undeclared identifier: x')
|
||||||
|
}),
|
||||||
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO use before assignment
|
TODO use before assignment
|
||||||
test('no use before assignment', () => {
|
test('no use before assignment', () => {
|
||||||
@@ -1227,7 +1240,7 @@ export const tests = [
|
|||||||
let x;
|
let x;
|
||||||
x;
|
x;
|
||||||
`
|
`
|
||||||
return assert_equal(parse(test).problems[0].message, 'undeclared identifier: x')
|
return assert_equal(do_parse(test).problems[0].message, 'undeclared identifier: x')
|
||||||
}),
|
}),
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -1513,7 +1526,7 @@ const y = x()`
|
|||||||
1 2
|
1 2
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
const r = parse(code)
|
const r = do_parse(code)
|
||||||
assert_equal(r.ok, false)
|
assert_equal(r.ok, false)
|
||||||
const p = r.problems[0]
|
const p = r.problems[0]
|
||||||
assert_equal(p.index, code.indexOf('2'))
|
assert_equal(p.index, code.indexOf('2'))
|
||||||
@@ -1527,7 +1540,7 @@ const y = x()`
|
|||||||
,
|
,
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
const r = parse(code)
|
const r = do_parse(code)
|
||||||
assert_equal(r.ok, false)
|
assert_equal(r.ok, false)
|
||||||
const p = r.problems[0]
|
const p = r.problems[0]
|
||||||
assert_equal(p.index, code.indexOf(','))
|
assert_equal(p.index, code.indexOf(','))
|
||||||
@@ -1535,7 +1548,7 @@ const y = x()`
|
|||||||
|
|
||||||
test('better parse errors 3', () => {
|
test('better parse errors 3', () => {
|
||||||
const code = `[() => { , }] `
|
const code = `[() => { , }] `
|
||||||
const r = parse(code)
|
const r = do_parse(code)
|
||||||
const p = r.problems[0]
|
const p = r.problems[0]
|
||||||
assert_equal(p.index, code.indexOf(','))
|
assert_equal(p.index, code.indexOf(','))
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -46,11 +46,16 @@ export const patch_builtin = new Function(`
|
|||||||
|
|
||||||
export const original_setTimeout = globalThis.run_window.__original_setTimeout
|
export const original_setTimeout = globalThis.run_window.__original_setTimeout
|
||||||
|
|
||||||
|
export const do_parse = code => parse(
|
||||||
|
code,
|
||||||
|
new Set(Object.getOwnPropertyNames(globalThis.run_window))
|
||||||
|
)
|
||||||
|
|
||||||
export const parse_modules = (entry, modules) =>
|
export const parse_modules = (entry, modules) =>
|
||||||
load_modules(entry, module_name => modules[module_name])
|
load_modules(entry, module_name => modules[module_name])
|
||||||
|
|
||||||
export const eval_tree = code => {
|
export const eval_tree = code => {
|
||||||
const parse_result = parse(code)
|
const parse_result = do_parse(code)
|
||||||
assert_equal(parse_result.ok, true)
|
assert_equal(parse_result.ok, true)
|
||||||
return eval_modules(
|
return eval_modules(
|
||||||
{
|
{
|
||||||
@@ -102,7 +107,8 @@ export const test_initial_state = (code, state) => {
|
|||||||
current_module: '',
|
current_module: '',
|
||||||
...state,
|
...state,
|
||||||
},
|
},
|
||||||
)
|
),
|
||||||
|
new Set(Object.getOwnPropertyNames(globalThis.run_window))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user