This commit is contained in:
Dmitry Vasilev
2023-11-23 14:47:28 +08:00
parent 3f6d0fa902
commit 9b92302d88

View File

@@ -595,8 +595,8 @@ const get_args_scope = (fn_node, args, closure) => {
} }
} }
const eval_binary_expr = (node, scope, callsleft, context) => { const eval_binary_expr = (node, scope, callsleft, frame_cxt) => {
const {ok, children, calls, scope: nextscope} = eval_children(node, scope, callsleft, context) const {ok, children, calls, scope: nextscope} = eval_children(node, scope, callsleft, frame_cxt)
if(!ok) { if(!ok) {
return {ok, children, calls, scope: nextscope} return {ok, children, calls, scope: nextscope}
} }
@@ -624,17 +624,17 @@ const symbol_for_closed_let_var = name =>
different names. Prepend '!' symbol because it is not a valid js identifier, different names. Prepend '!' symbol because it is not a valid js identifier,
so it will not be mixed with other variables so it will not be mixed with other variables
*/ */
const symbol_for_identifier = (node, context) => { const symbol_for_identifier = (node, frame_cxt) => {
if(node.definition == 'global') { if(node.definition == 'global') {
return node.value return node.value
} }
const index = node.definition == 'self' ? node.index : node.definition.index const index = node.definition == 'self' ? node.index : node.definition.index
const declaration = find_declaration(context.calltree_node.code, index) const declaration = find_declaration(frame_cxt.calltree_node.code, index)
const variable = node.definition == 'self' const variable = node.definition == 'self'
? node ? node
: find_leaf(context.calltree_node.code, node.definition.index) : find_leaf(frame_cxt.calltree_node.code, node.definition.index)
if(declaration == null) { if(declaration == null) {
/* /*
Variable was declared in outer scope. Since there can be only one variable Variable was declared in outer scope. Since there can be only one variable
@@ -649,13 +649,13 @@ const symbol_for_identifier = (node, context) => {
} }
} }
const do_eval_frame_expr = (node, scope, callsleft, context) => { const do_eval_frame_expr = (node, scope, callsleft, frame_cxt) => {
if(node.type == 'identifier') { if(node.type == 'identifier') {
let value let value
if(node.definition == 'global') { if(node.definition == 'global') {
value = globalThis.app_window[node.value] value = globalThis.app_window[node.value]
} else { } else {
value = scope[symbol_for_identifier(node, context)] value = scope[symbol_for_identifier(node, frame_cxt)]
} }
return { return {
ok: true, ok: true,
@@ -673,7 +673,7 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
// TODO for string literal and number, do not use eval // TODO for string literal and number, do not use eval
return {...eval_codestring(node.value, scope), calls: callsleft, scope} return {...eval_codestring(node.value, scope), calls: callsleft, scope}
} else if(node.type == 'array_spread') { } else if(node.type == 'array_spread') {
const result = eval_children(node, scope, callsleft, context) const result = eval_children(node, scope, callsleft, frame_cxt)
if(!result.ok) { if(!result.ok) {
return result return result
} }
@@ -695,9 +695,9 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
'key_value_pair', 'key_value_pair',
'computed_property' 'computed_property'
].includes(node.type)) { ].includes(node.type)) {
return eval_children(node, scope, callsleft, context) return eval_children(node, scope, callsleft, frame_cxt)
} else if(node.type == 'array_literal' || node.type == 'call_args'){ } else if(node.type == 'array_literal' || node.type == 'call_args'){
const {ok, children, calls, scope: nextscope} = eval_children(node, scope, callsleft, context) const {ok, children, calls, scope: nextscope} = eval_children(node, scope, callsleft, frame_cxt)
if(!ok) { if(!ok) {
return {ok, children, calls, scope: nextscope} return {ok, children, calls, scope: nextscope}
} }
@@ -713,7 +713,7 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
) )
return {ok, children, calls, value, scope: nextscope} return {ok, children, calls, value, scope: nextscope}
} else if(node.type == 'object_literal'){ } else if(node.type == 'object_literal'){
const {ok, children, calls, scope: nextscope} = eval_children(node, scope, callsleft, context) const {ok, children, calls, scope: nextscope} = eval_children(node, scope, callsleft, frame_cxt)
if(!ok) { if(!ok) {
return {ok, children, calls, scope: nextscope} return {ok, children, calls, scope: nextscope}
} }
@@ -744,14 +744,14 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
) )
return {ok, children, value, calls, scope: nextscope} return {ok, children, value, calls, scope: nextscope}
} else if(node.type == 'function_call' || node.type == 'new'){ } else if(node.type == 'function_call' || node.type == 'new'){
const {ok, children, calls, scope: nextscope} = eval_children(node, scope, callsleft, context) const {ok, children, calls, scope: nextscope} = eval_children(node, scope, callsleft, frame_cxt)
if(!ok) { if(!ok) {
return {ok: false, children, calls, scope: nextscope} return {ok: false, children, calls, scope: nextscope}
} else { } else {
if(typeof(children[0].result.value) != 'function') { if(typeof(children[0].result.value) != 'function') {
return { return {
ok: false, ok: false,
error: context.calltree_node.error, error: frame_cxt.calltree_node.error,
is_error_origin: true, is_error_origin: true,
children, children,
calls, calls,
@@ -763,7 +763,7 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
throw new Error('illegal state') throw new Error('illegal state')
} }
const closure = context.calltree_node.fn?.__closure const closure = frame_cxt.calltree_node.fn?.__closure
const closure_let_vars = closure == null const closure_let_vars = closure == null
? null ? null
: Object.fromEntries( : Object.fromEntries(
@@ -773,7 +773,7 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
) )
const let_vars = { const let_vars = {
...context.calltree_node.let_vars, ...frame_cxt.calltree_node.let_vars,
...closure_let_vars, ...closure_let_vars,
} }
const changed_vars = filter_object(let_vars, (name, v) => const changed_vars = filter_object(let_vars, (name, v) =>
@@ -781,7 +781,7 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
) )
const next_id = next_calls.length == 0 const next_id = next_calls.length == 0
? context.calltree_node.next_id ? frame_cxt.calltree_node.next_id
: next_calls[0].id : next_calls[0].id
const updated_let_scope = map_object(changed_vars, (name, v) => const updated_let_scope = map_object(changed_vars, (name, v) =>
@@ -823,7 +823,7 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
node.cond, node.cond,
scope, scope,
callsleft, callsleft,
context frame_cxt
) )
const {ok, value} = cond_evaled.result const {ok, value} = cond_evaled.result
const branches = node.branches const branches = node.branches
@@ -840,7 +840,7 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
branches[value ? 0 : 1], branches[value ? 0 : 1],
scope_after_cond, scope_after_cond,
calls_after_cond, calls_after_cond,
context frame_cxt
) )
const children = value const children = value
? [cond_evaled, branch_evaled, branches[1]] ? [cond_evaled, branch_evaled, branches[1]]
@@ -854,7 +854,7 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
} }
} }
} else if(node.type == 'member_access'){ } else if(node.type == 'member_access'){
const {ok, children, calls, scope: nextscope} = eval_children(node, scope, callsleft, context) const {ok, children, calls, scope: nextscope} = eval_children(node, scope, callsleft, frame_cxt)
if(!ok) { if(!ok) {
return {ok: false, children, calls, scope: nextscope} return {ok: false, children, calls, scope: nextscope}
} }
@@ -875,7 +875,7 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
} }
} else if(node.type == 'unary') { } else if(node.type == 'unary') {
const {ok, children, calls, scope: nextscope} = eval_children(node, scope, callsleft, context) const {ok, children, calls, scope: nextscope} = eval_children(node, scope, callsleft, frame_cxt)
if(!ok) { if(!ok) {
return {ok: false, children, calls, scope: nextscope} return {ok: false, children, calls, scope: nextscope}
} else { } else {
@@ -913,14 +913,14 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
} }
} else if(node.type == 'binary' && !['&&', '||', '??'].includes(node.operator)){ } else if(node.type == 'binary' && !['&&', '||', '??'].includes(node.operator)){
return eval_binary_expr(node, scope, callsleft, context) return eval_binary_expr(node, scope, callsleft, frame_cxt)
} else if(node.type == 'binary' && ['&&', '||', '??'].includes(node.operator)){ } else if(node.type == 'binary' && ['&&', '||', '??'].includes(node.operator)){
const {node: left_evaled, calls, scope: nextscope} = eval_frame_expr( const {node: left_evaled, calls, scope: nextscope} = eval_frame_expr(
node.children[0], node.children[0],
scope, scope,
callsleft, callsleft,
context frame_cxt
) )
const {ok, value} = left_evaled.result const {ok, value} = left_evaled.result
@@ -941,11 +941,11 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
scope: nextscope, scope: nextscope,
} }
} else { } else {
return eval_binary_expr(node, scope, callsleft, context) return eval_binary_expr(node, scope, callsleft, frame_cxt)
} }
} else if(node.type == 'grouping'){ } else if(node.type == 'grouping'){
const {ok, children, calls, scope: nextscope} = eval_children(node, scope, callsleft, context) const {ok, children, calls, scope: nextscope} = eval_children(node, scope, callsleft, frame_cxt)
if(!ok) { if(!ok) {
return {ok, children, calls, scope: nextscope} return {ok, children, calls, scope: nextscope}
} else { } else {
@@ -957,7 +957,7 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
} }
} }
const eval_children = (node, scope, calls, context) => { const eval_children = (node, scope, calls, frame_cxt) => {
return node.children.reduce( return node.children.reduce(
({ok, children, calls, scope}, child) => { ({ok, children, calls, scope}, child) => {
let next_child, next_ok, next_calls, next_scope let next_child, next_ok, next_calls, next_scope
@@ -967,7 +967,7 @@ const eval_children = (node, scope, calls, context) => {
next_calls = calls next_calls = calls
next_scope = scope next_scope = scope
} else { } else {
const result = eval_frame_expr(child, scope, calls, context) const result = eval_frame_expr(child, scope, calls, frame_cxt)
next_child = result.node next_child = result.node
next_calls = result.calls next_calls = result.calls
next_ok = next_child.result.ok next_ok = next_child.result.ok
@@ -984,9 +984,9 @@ const eval_children = (node, scope, calls, context) => {
) )
} }
const eval_frame_expr = (node, scope, callsleft, context) => { const eval_frame_expr = (node, scope, callsleft, frame_cxt) => {
const {ok, error, is_error_origin, value, call, children, calls, scope: nextscope} const {ok, error, is_error_origin, value, call, children, calls, scope: nextscope}
= do_eval_frame_expr(node, scope, callsleft, context) = do_eval_frame_expr(node, scope, callsleft, frame_cxt)
if(callsleft != null && calls == null) { if(callsleft != null && calls == null) {
// TODO remove it, just for debug // TODO remove it, just for debug
console.error('node', node) console.error('node', node)
@@ -1004,14 +1004,14 @@ const eval_frame_expr = (node, scope, callsleft, context) => {
} }
} }
const eval_decl_pair = (s, scope, calls, context) => { const eval_decl_pair = (s, scope, calls, frame_cxt) => {
if(s.type != 'decl_pair') { if(s.type != 'decl_pair') {
throw new Error('illegal state') throw new Error('illegal state')
} }
// TODO default values for destructuring can be function calls // TODO default values for destructuring can be function calls
const {node, calls: next_calls, scope: scope_after_expr} const {node, calls: next_calls, scope: scope_after_expr}
= eval_frame_expr(s.expr, scope, calls, context) = eval_frame_expr(s.expr, scope, calls, frame_cxt)
const s_expr_evaled = {...s, children: [s.name_node, node]} const s_expr_evaled = {...s, children: [s.name_node, node]}
if(!node.result.ok) { if(!node.result.ok) {
return { return {
@@ -1040,7 +1040,7 @@ const eval_decl_pair = (s, scope, calls, context) => {
) )
const next_scope = Object.fromEntries(name_nodes.map(n => const next_scope = Object.fromEntries(name_nodes.map(n =>
[symbol_for_identifier(n, context), values[n.value]] [symbol_for_identifier(n, frame_cxt), values[n.value]]
)) ))
// TODO fine-grained destructuring error, only for identifiers that failed // TODO fine-grained destructuring error, only for identifiers that failed
@@ -1052,7 +1052,7 @@ const eval_decl_pair = (s, scope, calls, context) => {
result: { result: {
ok, ok,
error: ok ? null : error, error: ok ? null : error,
value: !ok ? null : next_scope[symbol_for_identifier(node, context)], value: !ok ? null : next_scope[symbol_for_identifier(node, frame_cxt)],
} }
}) })
), ),
@@ -1085,14 +1085,14 @@ const eval_decl_pair = (s, scope, calls, context) => {
} }
const eval_statement = (s, scope, calls, context) => { const eval_statement = (s, scope, calls, frame_cxt) => {
if(s.type == 'do') { if(s.type == 'do') {
const stmt = s const stmt = s
// hoist function decls to the top // hoist function decls to the top
const function_decls = s.children const function_decls = s.children
.filter(s => s.type == 'function_decl') .filter(s => s.type == 'function_decl')
.map(s => { .map(s => {
const {ok, children, calls: next_calls} = eval_children(s, scope, calls, context) const {ok, children, calls: next_calls} = eval_children(s, scope, calls, frame_cxt)
if(!ok) { if(!ok) {
// Function decl can never fail // Function decl can never fail
throw new Error('illegal state') throw new Error('illegal state')
@@ -1132,7 +1132,7 @@ const eval_statement = (s, scope, calls, context) => {
node, node,
scope: nextscope, scope: nextscope,
calls: next_calls, calls: next_calls,
} = eval_statement(s, scope, calls, context) } = eval_statement(s, scope, calls, frame_cxt)
return { return {
ok, ok,
returned, returned,
@@ -1169,7 +1169,7 @@ const eval_statement = (s, scope, calls, context) => {
return { return {
ok, ok,
children: [...children, node], children: [...children, node],
scope: {...scope, [symbol_for_identifier(s, context)]: undefined}, scope: {...scope, [symbol_for_identifier(s, frame_cxt)]: undefined},
calls calls
} }
} }
@@ -1178,7 +1178,7 @@ const eval_statement = (s, scope, calls, context) => {
node, node,
scope: nextscope, scope: nextscope,
calls: next_calls, calls: next_calls,
} = eval_decl_pair(s, scope, calls, context) } = eval_decl_pair(s, scope, calls, frame_cxt)
return { return {
ok: next_ok, ok: next_ok,
scope: nextscope, scope: nextscope,
@@ -1197,7 +1197,7 @@ const eval_statement = (s, scope, calls, context) => {
} else if(s.type == 'return') { } else if(s.type == 'return') {
const {node, calls: next_calls, scope: nextscope} = const {node, calls: next_calls, scope: nextscope} =
eval_frame_expr(s.expr, scope, calls, context) eval_frame_expr(s.expr, scope, calls, frame_cxt)
return { return {
ok: node.result.ok, ok: node.result.ok,
@@ -1209,7 +1209,7 @@ const eval_statement = (s, scope, calls, context) => {
} else if(s.type == 'export') { } else if(s.type == 'export') {
const {ok, scope: nextscope, calls: next_calls, node} const {ok, scope: nextscope, calls: next_calls, node}
= eval_statement(s.binding, scope, calls, context) = eval_statement(s.binding, scope, calls, frame_cxt)
return { return {
ok, ok,
scope: nextscope, scope: nextscope,
@@ -1217,7 +1217,7 @@ const eval_statement = (s, scope, calls, context) => {
node: {...s, children: [node], result: {ok: node.result.ok}} node: {...s, children: [node], result: {ok: node.result.ok}}
} }
} else if(s.type == 'import') { } else if(s.type == 'import') {
const module = context.modules[s.full_import_path] const module = frame_cxt.modules[s.full_import_path]
const children = s.children.map((imp, i) => ( const children = s.children.map((imp, i) => (
{...imp, {...imp,
result: { result: {
@@ -1240,7 +1240,7 @@ const eval_statement = (s, scope, calls, context) => {
} else if(s.type == 'if') { } else if(s.type == 'if') {
const {node, calls: next_calls, scope: scope_after_cond} = const {node, calls: next_calls, scope: scope_after_cond} =
eval_frame_expr(s.cond, scope, calls, context) eval_frame_expr(s.cond, scope, calls, frame_cxt)
if(!node.result.ok) { if(!node.result.ok) {
return { return {
@@ -1265,7 +1265,7 @@ const eval_statement = (s, scope, calls, context) => {
s.branches[0], s.branches[0],
scope_after_cond, scope_after_cond,
next_calls, next_calls,
context frame_cxt
) )
return { return {
ok: evaled_branch.result.ok, ok: evaled_branch.result.ok,
@@ -1299,7 +1299,7 @@ const eval_statement = (s, scope, calls, context) => {
active_branch, active_branch,
scope_after_cond, scope_after_cond,
next_calls, next_calls,
context, frame_cxt,
) )
const children = node.result.value const children = node.result.value
@@ -1318,7 +1318,7 @@ const eval_statement = (s, scope, calls, context) => {
} else if(s.type == 'throw') { } else if(s.type == 'throw') {
const {node, calls: next_calls, scope: next_scope} = const {node, calls: next_calls, scope: next_scope} =
eval_frame_expr(s.expr, scope, calls, context) eval_frame_expr(s.expr, scope, calls, frame_cxt)
return { return {
ok: false, ok: false,
@@ -1336,7 +1336,7 @@ const eval_statement = (s, scope, calls, context) => {
} else { } else {
// stmt type is expression // stmt type is expression
const {node, calls: next_calls, scope: next_scope} = eval_frame_expr(s, scope, calls, context) const {node, calls: next_calls, scope: next_scope} = eval_frame_expr(s, scope, calls, frame_cxt)
return { return {
ok: node.result.ok, ok: node.result.ok,
node, node,
@@ -1351,14 +1351,14 @@ export const eval_frame = (calltree_node, modules) => {
throw new Error('illegal state') throw new Error('illegal state')
} }
const node = calltree_node.code const node = calltree_node.code
const context = {calltree_node, modules} const frame_cxt = {calltree_node, modules}
if(node.type == 'do') { if(node.type == 'do') {
// eval module toplevel // eval module toplevel
return eval_statement( return eval_statement(
node, node,
{}, {},
calltree_node.children, calltree_node.children,
context, frame_cxt,
).node ).node
} else { } else {
// TODO default values for destructuring can be function calls // TODO default values for destructuring can be function calls
@@ -1426,10 +1426,10 @@ export const eval_frame = (calltree_node, modules) => {
body, body,
scope, scope,
calltree_node.children, calltree_node.children,
context, frame_cxt,
).node ).node
} else { } else {
nextbody = eval_frame_expr(body, scope, calltree_node.children, context) nextbody = eval_frame_expr(body, scope, calltree_node.children, frame_cxt)
.node .node
} }