mirror of
https://github.com/leporello-js/leporello-js
synced 2026-01-13 21:14:28 -08:00
fix error origin
This commit is contained in:
@@ -145,33 +145,27 @@ export const find_node = (node, pred) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO refactor, at eval.js we have explicit information if node is error
|
|
||||||
// origin, without guessing. See also color.js
|
|
||||||
// TODO result is ok, but value is rejected promise
|
// TODO result is ok, but value is rejected promise
|
||||||
// TODO check if return result is null and throw early
|
export const find_error_origin_node = (node, is_root = true) => {
|
||||||
export const find_error_origin_node = node =>
|
if(node.result == null) {
|
||||||
find_node(
|
return null
|
||||||
// TODO do not go inside function_expr
|
}
|
||||||
node,
|
if(node.type == 'function_expr' && !is_root) {
|
||||||
n => n.result != null && !n.result.ok && (
|
return null
|
||||||
n.result.error != null
|
}
|
||||||
||
|
if(node.result.is_error_origin) {
|
||||||
// node has no error, but its children also have no error, so this node
|
return node
|
||||||
// is error origin
|
}
|
||||||
n.children.find(c => find_error_origin_node(c) != null) == null
|
if(node.children == null) {
|
||||||
&&
|
return null
|
||||||
(
|
}
|
||||||
// In case if throw null or throw undefined
|
return node
|
||||||
n.type == 'throw'
|
.children
|
||||||
||
|
.reduce(
|
||||||
// await can also throw null
|
(result, c) => result ?? find_error_origin_node(c, false),
|
||||||
n.type == 'unary' && n.operator == 'await'
|
null
|
||||||
// or function call throwing null or undefined
|
|
||||||
||
|
|
||||||
n.type == 'function_call'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/* Maps tree nodes, discarding mapped children, so maps only node contents, not
|
/* Maps tree nodes, discarding mapped children, so maps only node contents, not
|
||||||
* allowing to modify structure */
|
* allowing to modify structure */
|
||||||
|
|||||||
14
src/color.js
14
src/color.js
@@ -20,7 +20,7 @@ const is_result_eq = (a,b) => a.result == null
|
|||||||
? b.result == null
|
? b.result == null
|
||||||
: b.result != null
|
: b.result != null
|
||||||
&& a.result.ok == b.result.ok
|
&& a.result.ok == b.result.ok
|
||||||
&& a.result.error_origin == b.result.error_origin
|
&& !!a.result.is_error_origin == !!b.result.is_error_origin
|
||||||
|
|
||||||
const node_to_color = node => ({
|
const node_to_color = node => ({
|
||||||
index: node.index,
|
index: node.index,
|
||||||
@@ -31,11 +31,7 @@ const node_to_color = node => ({
|
|||||||
? null
|
? null
|
||||||
: node.result.ok
|
: node.result.ok
|
||||||
? {ok: true}
|
? {ok: true}
|
||||||
// node.result.error may be null, for example if throw null
|
: {ok: false, is_error_origin: !!node.result.is_error_origin}
|
||||||
// See find_error_origin_node
|
|
||||||
: node.result.error == null
|
|
||||||
? {ok: false, error_origin: false}
|
|
||||||
: {ok: false, error_origin: true}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const is_short_circuit = node =>
|
const is_short_circuit = node =>
|
||||||
@@ -175,7 +171,7 @@ const do_color = (node, is_root = false) => {
|
|||||||
return [node_to_color(node)]
|
return [node_to_color(node)]
|
||||||
}
|
}
|
||||||
|
|
||||||
if(node.result?.error != null) {
|
if(node.result?.is_error_origin) {
|
||||||
const color = node_to_color(node)
|
const color = node_to_color(node)
|
||||||
const exprs = collect_function_exprs(node)
|
const exprs = collect_function_exprs(node)
|
||||||
if(exprs.length == 0) {
|
if(exprs.length == 0) {
|
||||||
@@ -208,7 +204,7 @@ const do_color = (node, is_root = false) => {
|
|||||||
const result = color_children(node, is_root)
|
const result = color_children(node, is_root)
|
||||||
return node.result != null && !node.result.ok
|
return node.result != null && !node.result.ok
|
||||||
? result.map(c => c.result == null
|
? result.map(c => c.result == null
|
||||||
? {...c, result: {ok: false, error_origin: false}}
|
? {...c, result: {ok: false, is_error_origin: false}}
|
||||||
: c
|
: c
|
||||||
)
|
)
|
||||||
: result
|
: result
|
||||||
@@ -222,7 +218,7 @@ export const color = frame => {
|
|||||||
c.result != null
|
c.result != null
|
||||||
&&
|
&&
|
||||||
// Parts that were not error origins
|
// Parts that were not error origins
|
||||||
(c.result.ok || c.result.error_origin)
|
(c.result.ok || c.result.is_error_origin)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Sanity-check result
|
// Sanity-check result
|
||||||
|
|||||||
20
src/eval.js
20
src/eval.js
@@ -452,7 +452,7 @@ export const eval_codestring = (codestring, scope) =>
|
|||||||
try {
|
try {
|
||||||
return {ok: true, value: eval('with({...scope}){' + codestring + '}')}
|
return {ok: true, value: eval('with({...scope}){' + codestring + '}')}
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
return {ok: false, error}
|
return {ok: false, error, is_error_origin: true}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
))(codestring, scope)
|
))(codestring, scope)
|
||||||
@@ -481,7 +481,7 @@ const get_args_scope = (fn_node, args, closure) => {
|
|||||||
|
|
||||||
if(!ok) {
|
if(!ok) {
|
||||||
// TODO show exact destructuring error
|
// TODO show exact destructuring error
|
||||||
return {ok, error}
|
return {ok, error, is_error_origin: true}
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
ok,
|
ok,
|
||||||
@@ -534,6 +534,7 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
|
|||||||
children: result.children,
|
children: result.children,
|
||||||
calls: result.calls,
|
calls: result.calls,
|
||||||
error: new TypeError(child.string + ' is not iterable'),
|
error: new TypeError(child.string + ' is not iterable'),
|
||||||
|
is_error_origin: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if([
|
} else if([
|
||||||
@@ -598,6 +599,7 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
|
|||||||
return {
|
return {
|
||||||
ok: false,
|
ok: false,
|
||||||
error: context.calltree_node.error,
|
error: context.calltree_node.error,
|
||||||
|
is_error_origin: true,
|
||||||
children,
|
children,
|
||||||
calls,
|
calls,
|
||||||
}
|
}
|
||||||
@@ -611,6 +613,7 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
|
|||||||
call: c,
|
call: c,
|
||||||
value: c.value,
|
value: c.value,
|
||||||
error: c.error,
|
error: c.error,
|
||||||
|
is_error_origin: !c.ok,
|
||||||
children,
|
children,
|
||||||
calls: calls.slice(1)
|
calls: calls.slice(1)
|
||||||
}
|
}
|
||||||
@@ -687,7 +690,7 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
|
|||||||
return {ok: false, children, calls}
|
return {ok: false, children, calls}
|
||||||
} else {
|
} else {
|
||||||
const expr = children[0]
|
const expr = children[0]
|
||||||
let ok, value, error
|
let ok, value, error, is_error_origin
|
||||||
if(node.operator == '!') {
|
if(node.operator == '!') {
|
||||||
ok = true
|
ok = true
|
||||||
value = !expr.result.value
|
value = !expr.result.value
|
||||||
@@ -707,6 +710,7 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
|
|||||||
ok = status.ok
|
ok = status.ok
|
||||||
error = status.error
|
error = status.error
|
||||||
value = status.value
|
value = status.value
|
||||||
|
is_error_origin = !ok
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ok = true
|
ok = true
|
||||||
@@ -715,7 +719,7 @@ const do_eval_frame_expr = (node, scope, callsleft, context) => {
|
|||||||
} else {
|
} else {
|
||||||
throw new Error('unknown op')
|
throw new Error('unknown op')
|
||||||
}
|
}
|
||||||
return {ok, children, calls, value, error}
|
return {ok, children, calls, value, error, is_error_origin}
|
||||||
}
|
}
|
||||||
} else if(node.type == 'binary' && !['&&', '||', '??'].includes(node.operator)){
|
} else if(node.type == 'binary' && !['&&', '||', '??'].includes(node.operator)){
|
||||||
|
|
||||||
@@ -783,7 +787,7 @@ const eval_children = (node, scope, calls, context) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const eval_frame_expr = (node, scope, callsleft, context) => {
|
const eval_frame_expr = (node, scope, callsleft, context) => {
|
||||||
const {ok, error, value, call, children, calls}
|
const {ok, error, is_error_origin, value, call, children, calls}
|
||||||
= do_eval_frame_expr(node, scope, callsleft, context)
|
= do_eval_frame_expr(node, scope, callsleft, context)
|
||||||
if(callsleft != null && calls == null) {
|
if(callsleft != null && calls == null) {
|
||||||
// TODO remove it, just for debug
|
// TODO remove it, just for debug
|
||||||
@@ -795,7 +799,7 @@ const eval_frame_expr = (node, scope, callsleft, context) => {
|
|||||||
...node,
|
...node,
|
||||||
children,
|
children,
|
||||||
// Add `call` for step_into
|
// Add `call` for step_into
|
||||||
result: {ok, error, value, call}
|
result: {ok, error, value, call, is_error_origin}
|
||||||
},
|
},
|
||||||
calls,
|
calls,
|
||||||
}
|
}
|
||||||
@@ -974,7 +978,7 @@ const eval_statement = (s, scope, calls, context) => {
|
|||||||
return {
|
return {
|
||||||
ok: false,
|
ok: false,
|
||||||
// TODO assign error to node where destructuring failed, not to every node
|
// TODO assign error to node where destructuring failed, not to every node
|
||||||
node: {...s_evaled, result: {ok, error}},
|
node: {...s_evaled, result: {ok, error, is_error_origin: true}},
|
||||||
scope,
|
scope,
|
||||||
calls,
|
calls,
|
||||||
}
|
}
|
||||||
@@ -1135,6 +1139,7 @@ const eval_statement = (s, scope, calls, context) => {
|
|||||||
children: [node],
|
children: [node],
|
||||||
result: {
|
result: {
|
||||||
ok: false,
|
ok: false,
|
||||||
|
is_error_origin: node.result.ok,
|
||||||
error: node.result.ok ? node.result.value : null,
|
error: node.result.ok ? node.result.value : null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1189,6 +1194,7 @@ export const eval_frame = (calltree_node, modules) => {
|
|||||||
result: {
|
result: {
|
||||||
ok: args_scope_result.ok,
|
ok: args_scope_result.ok,
|
||||||
error: args_scope_result.ok ? null : args_scope_result.error,
|
error: args_scope_result.ok ? null : args_scope_result.error,
|
||||||
|
is_error_origin: !args_scope_result.ok,
|
||||||
value: !args_scope_result.ok ? null : args_scope_result.value[a.value],
|
value: !args_scope_result.ok ? null : args_scope_result.value[a.value],
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
12
test/test.js
12
test/test.js
@@ -1504,7 +1504,7 @@ export const tests = [
|
|||||||
{
|
{
|
||||||
index: code.indexOf('x()'),
|
index: code.indexOf('x()'),
|
||||||
length: 'x()'.length,
|
length: 'x()'.length,
|
||||||
result: { ok: false, error_origin: true }
|
result: { ok: false, is_error_origin: true }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@@ -1527,12 +1527,12 @@ export const tests = [
|
|||||||
{
|
{
|
||||||
index: code.indexOf('throw'),
|
index: code.indexOf('throw'),
|
||||||
length: 'throw new Error()'.length,
|
length: 'throw new Error()'.length,
|
||||||
result: { ok: false, error_origin: true }
|
result: { ok: false, is_error_origin: true }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
index: code.indexOf('x()'),
|
index: code.indexOf('x()'),
|
||||||
length: "x()".length,
|
length: "x()".length,
|
||||||
result: { ok: false, error_origin: true }
|
result: { ok: false, is_error_origin: true }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@@ -1544,7 +1544,7 @@ export const tests = [
|
|||||||
// Color only index access, not grouping braces
|
// Color only index access, not grouping braces
|
||||||
assert_equal(
|
assert_equal(
|
||||||
color_file(initial, ''),
|
color_file(initial, ''),
|
||||||
[ { index: 1, length: 7, result: { ok: false, error_origin: true } } ],
|
[ { index: 1, length: 7, result: { ok: false, is_error_origin: true } } ],
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
|
||||||
@@ -1603,7 +1603,7 @@ export const tests = [
|
|||||||
{
|
{
|
||||||
index: 0,
|
index: 0,
|
||||||
length: code.length,
|
length: code.length,
|
||||||
result: { ok: false, error_origin: true }
|
result: { ok: false, is_error_origin: true }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@@ -1667,7 +1667,7 @@ const y = x()`
|
|||||||
const i = test_initial_state(code)
|
const i = test_initial_state(code)
|
||||||
const coloring = color_file(i, '')
|
const coloring = color_file(i, '')
|
||||||
|
|
||||||
const result = {ok: false, error_origin: true}
|
const result = {ok: false, is_error_origin: true}
|
||||||
assert_equal(
|
assert_equal(
|
||||||
coloring,
|
coloring,
|
||||||
[
|
[
|
||||||
|
|||||||
Reference in New Issue
Block a user