mirror of
https://github.com/leporello-js/leporello-js
synced 2026-01-13 13:04:30 -08:00
mutable closures (let variables)
This commit is contained in:
611
test/test.js
611
test/test.js
@@ -105,6 +105,14 @@ export const tests = [
|
||||
)
|
||||
}),
|
||||
|
||||
// TODO
|
||||
// test('backtick_string let vars', () => {
|
||||
// assert_code_evals_to(
|
||||
// 'let x = `b`; `a${x}a`',
|
||||
// 'aba',
|
||||
// )
|
||||
// }),
|
||||
|
||||
test('Simple expression', () => {
|
||||
return assert_code_evals_to('1+1;', 2)
|
||||
}),
|
||||
@@ -320,6 +328,7 @@ export const tests = [
|
||||
)
|
||||
}),
|
||||
|
||||
/*
|
||||
test('let variable', () => {
|
||||
const code = `
|
||||
let x, y = 2, unused, [z,q] = [3,4]
|
||||
@@ -328,6 +337,7 @@ export const tests = [
|
||||
const i = test_initial_state(code, code.indexOf('x'))
|
||||
assert_equal(i.value_explorer.result.value, {y: 2, z: 3, q: 4})
|
||||
}),
|
||||
*/
|
||||
|
||||
test('let variable not initialized bug', () => {
|
||||
const code = `
|
||||
@@ -352,11 +362,6 @@ export const tests = [
|
||||
};
|
||||
x
|
||||
`
|
||||
const parse_result = do_parse(code)
|
||||
const assignment = find_leaf(
|
||||
parse_result.node,
|
||||
code.indexOf('x = 0')
|
||||
)
|
||||
assert_code_evals_to(
|
||||
code,
|
||||
1
|
||||
@@ -1476,6 +1481,18 @@ export const tests = [
|
||||
}),
|
||||
|
||||
test('multiple assignments', () => {
|
||||
assert_code_evals_to(
|
||||
`
|
||||
let x, y
|
||||
x = 1, y = 2
|
||||
{x,y}
|
||||
`,
|
||||
{x: 1, y: 2}
|
||||
)
|
||||
}),
|
||||
|
||||
/* TODO assignments destructuring
|
||||
test('multiple assignments destructuring', () => {
|
||||
assert_code_evals_to(
|
||||
`
|
||||
let x, y
|
||||
@@ -1485,6 +1502,7 @@ export const tests = [
|
||||
{x: 1, y: 2}
|
||||
)
|
||||
}),
|
||||
*/
|
||||
|
||||
test('assigments value explorer', () => {
|
||||
const code = `
|
||||
@@ -1504,7 +1522,8 @@ export const tests = [
|
||||
assert_equal(i.value_explorer.result.value, {x: 1, y: 2})
|
||||
}),
|
||||
|
||||
test('assigments destructuring value explorer', () => {
|
||||
/* TODO
|
||||
test('assignments destructuring value explorer', () => {
|
||||
const code = `
|
||||
let x, y
|
||||
x = 1, {y} = {y:2}
|
||||
@@ -1512,6 +1531,7 @@ export const tests = [
|
||||
const i = test_initial_state(code, code.indexOf('x = 1'))
|
||||
assert_equal(i.value_explorer.result.value, {x: 1, y: 2})
|
||||
}),
|
||||
*/
|
||||
|
||||
test('assigments error', () => {
|
||||
const code = `
|
||||
@@ -1522,6 +1542,19 @@ export const tests = [
|
||||
assert_equal(i.value_explorer.result.ok, false)
|
||||
}),
|
||||
|
||||
test('block scoping const', () => {
|
||||
assert_code_evals_to(
|
||||
`
|
||||
const x = 0
|
||||
if(true) {
|
||||
const x = 1
|
||||
}
|
||||
x
|
||||
`,
|
||||
0
|
||||
)
|
||||
}),
|
||||
|
||||
test('block scoping', () => {
|
||||
assert_code_evals_to(
|
||||
`
|
||||
@@ -4079,4 +4112,570 @@ const y = x()`
|
||||
'Map {foo: "bar", baz: "qux"}'
|
||||
)
|
||||
}),
|
||||
|
||||
test('let_versions find_versioned_lets toplevel', () => {
|
||||
const result = do_parse(`
|
||||
let x
|
||||
x = 1
|
||||
function foo() {
|
||||
x
|
||||
}
|
||||
`)
|
||||
assert_equal(result.node.has_versioned_let_vars, true)
|
||||
}),
|
||||
|
||||
test('let_versions find_versioned_lets', () => {
|
||||
function assert_is_versioned_let(code, is_versioned) {
|
||||
const result = do_parse(code)
|
||||
const root = find_node(result.node,
|
||||
n => n.name == 'root' && n.type == 'function_expr'
|
||||
)
|
||||
assert_equal(root.has_versioned_let_vars, is_versioned)
|
||||
const node = find_node(result.node, n => n.index == code.indexOf('x'))
|
||||
assert_equal(!(!node.is_versioned_let_var), is_versioned)
|
||||
}
|
||||
|
||||
assert_is_versioned_let(
|
||||
`
|
||||
function root() {
|
||||
let x
|
||||
x = 1
|
||||
function foo() {
|
||||
x
|
||||
}
|
||||
}
|
||||
`,
|
||||
true
|
||||
)
|
||||
|
||||
// closed but constant
|
||||
assert_is_versioned_let(
|
||||
`
|
||||
function root() {
|
||||
let x
|
||||
function foo() {
|
||||
x
|
||||
}
|
||||
}
|
||||
`,
|
||||
false
|
||||
)
|
||||
|
||||
// assigned but not closed
|
||||
assert_is_versioned_let(
|
||||
`
|
||||
function root() {
|
||||
let x
|
||||
x = 1
|
||||
}
|
||||
`,
|
||||
false
|
||||
)
|
||||
|
||||
// not closed, var has the same name
|
||||
assert_is_versioned_let(
|
||||
`
|
||||
function root() {
|
||||
let x
|
||||
x = 1
|
||||
function foo() {
|
||||
let x
|
||||
x
|
||||
}
|
||||
}
|
||||
`,
|
||||
false
|
||||
)
|
||||
|
||||
// not closed, var has the same name
|
||||
assert_is_versioned_let(
|
||||
`
|
||||
function root() {
|
||||
let x
|
||||
x = 1
|
||||
if(true) {
|
||||
let x
|
||||
function foo() {
|
||||
x
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
false
|
||||
)
|
||||
}),
|
||||
|
||||
test('let_versions', () => {
|
||||
const code = `
|
||||
let x
|
||||
[1,2].forEach(y => {
|
||||
x /*x*/
|
||||
x = y
|
||||
})
|
||||
`
|
||||
const x_pos = code.indexOf('x /*x*/')
|
||||
const i = test_initial_state(code, x_pos)
|
||||
const second_iter = COMMANDS.calltree.arrow_down(i)
|
||||
const select_x = COMMANDS.move_cursor(second_iter, x_pos)
|
||||
assert_equal(select_x.value_explorer.result.value, 1)
|
||||
}),
|
||||
|
||||
test('let_versions close let var bug', () => {
|
||||
const code = `
|
||||
let x
|
||||
x = 1
|
||||
function y() {
|
||||
return {x}
|
||||
}
|
||||
y() /*y()*/
|
||||
`
|
||||
const i = test_initial_state(code, code.indexOf('y() /*y()*/'))
|
||||
assert_equal(i.value_explorer.result.value, {x: 1})
|
||||
}),
|
||||
|
||||
test('let_versions initial let value', () => {
|
||||
const code = `
|
||||
let x
|
||||
function y() {
|
||||
x /*x*/
|
||||
}
|
||||
y()
|
||||
`
|
||||
const x_pos = code.indexOf('x /*x*/')
|
||||
const i = test_initial_state(code, x_pos)
|
||||
assert_equal(i.value_explorer.result, {ok: true, value: undefined})
|
||||
}),
|
||||
|
||||
test('let_versions save version bug', () => {
|
||||
const code = `
|
||||
let x = 0
|
||||
|
||||
function set_x(value) {
|
||||
x = value
|
||||
}
|
||||
|
||||
function get_x() {
|
||||
x /* result */
|
||||
}
|
||||
|
||||
get_x()
|
||||
|
||||
set_x(10)
|
||||
x = 10
|
||||
set_x(10)
|
||||
x = 10
|
||||
`
|
||||
const i = test_initial_state(code, code.indexOf('x /* result */'))
|
||||
assert_equal(i.value_explorer.result.value, 0)
|
||||
}),
|
||||
|
||||
test('let_versions expand_calltree_node', () => {
|
||||
const code = `
|
||||
let y
|
||||
|
||||
function foo(x) {
|
||||
y /*y*/
|
||||
bar(y)
|
||||
}
|
||||
|
||||
function bar(arg) {
|
||||
}
|
||||
|
||||
foo(0)
|
||||
y = 11
|
||||
foo(0)
|
||||
y = 12
|
||||
`
|
||||
const i = test_initial_state(code)
|
||||
const second_foo_call = root_calltree_node(i).children[1]
|
||||
assert_equal(second_foo_call.has_more_children, true)
|
||||
const expanded = COMMANDS.calltree.click(i, second_foo_call.id)
|
||||
const bar_call = root_calltree_node(expanded).children[1].children[0]
|
||||
assert_equal(bar_call.fn.name, 'bar')
|
||||
assert_equal(bar_call.args, [11])
|
||||
const moved = COMMANDS.move_cursor(expanded, code.indexOf('y /*y*/'))
|
||||
assert_equal(moved.value_explorer.result.value, 11)
|
||||
}),
|
||||
|
||||
test('let_versions expand_calltree_node 2', () => {
|
||||
const code = `
|
||||
let y
|
||||
|
||||
function deep(x) {
|
||||
if(x < 10) {
|
||||
y /*y*/
|
||||
y = x
|
||||
deep(x + 1)
|
||||
}
|
||||
}
|
||||
|
||||
deep(0)
|
||||
y = 11
|
||||
deep(0)
|
||||
y = 12
|
||||
`
|
||||
const i = test_initial_state(code)
|
||||
const second_deep_call = root_calltree_node(i).children[1]
|
||||
assert_equal(second_deep_call.has_more_children, true)
|
||||
const expanded = COMMANDS.calltree.click(i, second_deep_call.id)
|
||||
const moved = COMMANDS.move_cursor(expanded, code.indexOf('y /*y*/'))
|
||||
assert_equal(moved.value_explorer.result.value, 11)
|
||||
}),
|
||||
|
||||
test('let_versions create multiversion within expand_calltree_node', () => {
|
||||
const code = `
|
||||
function x() {
|
||||
let y
|
||||
function set(value) {
|
||||
y = value
|
||||
}
|
||||
set(1)
|
||||
y /*result*/
|
||||
set(2)
|
||||
}
|
||||
|
||||
x()
|
||||
x()
|
||||
|
||||
`
|
||||
const i = test_initial_state(code)
|
||||
const second_x_call = root_calltree_node(i).children[1]
|
||||
assert_equal(second_x_call.has_more_children, true)
|
||||
const expanded = COMMANDS.calltree.click(i, second_x_call.id)
|
||||
const moved = COMMANDS.move_cursor(expanded, code.indexOf('y /*result*/'))
|
||||
assert_equal(moved.value_explorer.result.value, 1)
|
||||
}),
|
||||
|
||||
test('let_versions mutable closure', () => {
|
||||
const code = `
|
||||
const holder = (function() {
|
||||
let value
|
||||
return {
|
||||
get: () => value,
|
||||
set: (v) => {
|
||||
value /*value*/
|
||||
value = v
|
||||
}
|
||||
}
|
||||
})()
|
||||
Array.from({length: 10}).map((_, i) => {
|
||||
holder.set(i)
|
||||
})
|
||||
holder.get()
|
||||
`
|
||||
const i = test_initial_state(code, code.indexOf('holder.get'))
|
||||
assert_equal(i.value_explorer.result.value, 9)
|
||||
|
||||
const map_expanded = COMMANDS.calltree.click(
|
||||
i,
|
||||
root_calltree_node(i).children[2].id
|
||||
)
|
||||
const expanded = COMMANDS.calltree.click(
|
||||
map_expanded,
|
||||
root_calltree_node(map_expanded).children[2].children[5].id
|
||||
)
|
||||
const set_call = COMMANDS.calltree.arrow_right(
|
||||
COMMANDS.calltree.arrow_right(
|
||||
expanded
|
||||
)
|
||||
)
|
||||
assert_equal(
|
||||
set_call.active_calltree_node.code.index,
|
||||
code.indexOf('(v) =>')
|
||||
)
|
||||
const moved = COMMANDS.move_cursor(set_call, code.indexOf('value /*value*/'))
|
||||
assert_equal(moved.value_explorer.result.value, 4)
|
||||
}),
|
||||
|
||||
test('let_versions forEach', () => {
|
||||
const code = `
|
||||
let sum = 0
|
||||
[1,2,3].forEach(v => {
|
||||
sum = sum + v
|
||||
})
|
||||
sum /*first*/
|
||||
[1,2,3].forEach(v => {
|
||||
sum = sum + v
|
||||
})
|
||||
sum /*second*/
|
||||
`
|
||||
const i = test_initial_state(code, code.indexOf('sum /*first*/'))
|
||||
assert_equal(i.value_explorer.result.value, 6)
|
||||
const second = COMMANDS.move_cursor(i, code.indexOf('sum /*second*/'))
|
||||
assert_equal(second.value_explorer.result.value, 12)
|
||||
}),
|
||||
|
||||
test('let_versions scope', () => {
|
||||
assert_code_evals_to(`
|
||||
let x = 1
|
||||
let y = 1
|
||||
function change_x() {
|
||||
x = 2
|
||||
}
|
||||
function change_y() {
|
||||
y = 2
|
||||
}
|
||||
function unused() {
|
||||
return {}
|
||||
}
|
||||
if(false) {
|
||||
} else {
|
||||
if((change_y() || true) ? true : null) {
|
||||
const a = [...[{...{
|
||||
y: unused()[!(1 + (true ? {y: [change_x()]} : null))]
|
||||
}}]]
|
||||
}
|
||||
}
|
||||
{x,y} /*result*/
|
||||
`,
|
||||
{x: 2, y: 2}
|
||||
)
|
||||
}),
|
||||
|
||||
test('let_versions expr', () => {
|
||||
assert_code_evals_to(`
|
||||
let x = 0
|
||||
function inc() {
|
||||
x = x + 1
|
||||
return 0
|
||||
}
|
||||
x + inc() + x + inc() + x
|
||||
`,
|
||||
3
|
||||
)
|
||||
}),
|
||||
|
||||
test('let_versions update in assignment', () => {
|
||||
assert_code_evals_to(`
|
||||
let x
|
||||
function set(value) {
|
||||
x = 1
|
||||
return 0
|
||||
}
|
||||
x = set()
|
||||
x
|
||||
`,
|
||||
0
|
||||
)
|
||||
}),
|
||||
|
||||
test('let_versions update in assignment closed', () => {
|
||||
const code = `
|
||||
function test() {
|
||||
let x
|
||||
function set(value) {
|
||||
x = 1
|
||||
return 0
|
||||
}
|
||||
x = set()
|
||||
return x
|
||||
}
|
||||
test()
|
||||
`
|
||||
const i = test_initial_state(code, code.indexOf('return x'))
|
||||
assert_equal(i.value_explorer.result.value, 0)
|
||||
}),
|
||||
|
||||
test('let_versions multiple vars with same name', () => {
|
||||
const code = `
|
||||
let x
|
||||
function x_1() {
|
||||
x = 1
|
||||
}
|
||||
if(true) {
|
||||
let x = 0
|
||||
function x_2() {
|
||||
x = 2
|
||||
}
|
||||
x /* result 0 */
|
||||
x_1()
|
||||
x /* result 1 */
|
||||
x_2()
|
||||
x /* result 2 */
|
||||
}
|
||||
`
|
||||
const i = test_initial_state(code, code.indexOf('x /* result 0 */'))
|
||||
const frame = active_frame(i)
|
||||
const result_0 = find_node(frame, n => n.index == code.indexOf('x /* result 0 */')).result
|
||||
assert_equal(result_0.value, 0)
|
||||
const result_1 = find_node(frame, n => n.index == code.indexOf('x /* result 1 */')).result
|
||||
assert_equal(result_1.value, 0)
|
||||
const result_2 = find_node(frame, n => n.index == code.indexOf('x /* result 2 */')).result
|
||||
assert_equal(result_2.value, 2)
|
||||
}),
|
||||
|
||||
test('let_versions closed let vars bug', () => {
|
||||
const code = `
|
||||
let x = 0
|
||||
function inc() {
|
||||
x = x + 1
|
||||
}
|
||||
function test() {
|
||||
inc()
|
||||
x /*x*/
|
||||
}
|
||||
test()
|
||||
`
|
||||
const i = test_initial_state(code, code.indexOf('x /*x*/'))
|
||||
assert_equal(i.value_explorer.result.value, 1)
|
||||
}),
|
||||
|
||||
test('let_versions assign and read variable multiple times within call', () => {
|
||||
const code = `
|
||||
let x;
|
||||
(() => {
|
||||
x = 1
|
||||
console.log(x)
|
||||
x = 2
|
||||
console.log(x)
|
||||
})()
|
||||
`
|
||||
}),
|
||||
|
||||
test('let_versions let assigned undefined bug', () => {
|
||||
const code = `
|
||||
let x = 1
|
||||
function set(value) {
|
||||
x = value
|
||||
}
|
||||
set(2)
|
||||
set(undefined)
|
||||
x /*x*/
|
||||
`
|
||||
const i = test_initial_state(code, code.indexOf('x /*x*/'))
|
||||
assert_equal(i.value_explorer.result.value, undefined)
|
||||
}),
|
||||
|
||||
// TODO function args should have multiple versions same as let vars
|
||||
|
||||
// test('let_versions function args closure', () => {
|
||||
// const code = `
|
||||
// (function(x) {
|
||||
// function y() {
|
||||
// x /*x*/
|
||||
// }
|
||||
// y()
|
||||
// x = 1
|
||||
// y()
|
||||
// })(0)
|
||||
// `
|
||||
// const i = test_initial_state(code)
|
||||
// const second_y_call = root_calltree_node(i).children[0].children[1]
|
||||
// const selected = COMMANDS.calltree.click(i, second_y_call.id)
|
||||
// const moved = COMMANDS.move_cursor(selected, code.indexOf('x /*x*/'))
|
||||
// assert_equal(moved.value_explorer.result.value, 1)
|
||||
// }),
|
||||
|
||||
test('let_versions async/await', async () => {
|
||||
const code = `
|
||||
let x
|
||||
function set(value) {
|
||||
x = value
|
||||
}
|
||||
await set(1)
|
||||
x /*x*/
|
||||
`
|
||||
const i = await test_initial_state_async(code, code.indexOf('x /*x*/'))
|
||||
assert_equal(i.value_explorer.result.value, 1)
|
||||
}),
|
||||
|
||||
test('let_versions async/await 2', async () => {
|
||||
const code = `
|
||||
let x
|
||||
function set(value) {
|
||||
x = value
|
||||
Promise.resolve().then(() => {
|
||||
x = 10
|
||||
})
|
||||
}
|
||||
await set(1)
|
||||
x /*x*/
|
||||
`
|
||||
const i = await test_initial_state_async(code, code.indexOf('x /*x*/'))
|
||||
assert_equal(i.value_explorer.result.value, 10)
|
||||
}),
|
||||
|
||||
// Test that expand_calltree_node produces correct id for expanded nodes
|
||||
test('let_versions native call', () => {
|
||||
const code = `
|
||||
function x() {}
|
||||
[1,2].map(x)
|
||||
[1,2].map(x)
|
||||
`
|
||||
const i = test_initial_state(code)
|
||||
const second_map_call = i.calltree.children[0].children[1]
|
||||
assert_equal(second_map_call.has_more_children, true)
|
||||
const expanded = COMMANDS.calltree.click(i, second_map_call.id)
|
||||
const second_map_call_exp = expanded.calltree.children[0].children[1]
|
||||
assert_equal(second_map_call.id == second_map_call_exp.id, true)
|
||||
assert_equal(second_map_call_exp.children[0].id == second_map_call_exp.id + 1, true)
|
||||
}),
|
||||
|
||||
test('let_versions expand twice', () => {
|
||||
const code = `
|
||||
function test() {
|
||||
let x = 0
|
||||
function test2() {
|
||||
function foo() {
|
||||
x /*x*/
|
||||
}
|
||||
x = x + 1
|
||||
foo()
|
||||
}
|
||||
test2()
|
||||
}
|
||||
test()
|
||||
test()
|
||||
`
|
||||
const i = test_initial_state(code)
|
||||
const test_call = root_calltree_node(i).children[1]
|
||||
assert_equal(test_call.has_more_children , true)
|
||||
|
||||
const expanded = COMMANDS.calltree.click(i, test_call.id)
|
||||
const test2_call = root_calltree_node(expanded).children[1].children[0]
|
||||
assert_equal(test2_call.has_more_children, true)
|
||||
|
||||
const expanded2 = COMMANDS.calltree.click(expanded, test2_call.id)
|
||||
const foo_call = root_calltree_node(expanded2).children[1].children[0].children[0]
|
||||
|
||||
const expanded3 = COMMANDS.calltree.click(expanded2, foo_call.id)
|
||||
|
||||
const moved = COMMANDS.move_cursor(expanded3, code.indexOf('x /*x*/'))
|
||||
assert_equal(moved.value_explorer.result.value, 1)
|
||||
}),
|
||||
|
||||
test('let_versions deferred calls', () => {
|
||||
const code = `
|
||||
let x = 0
|
||||
export const inc = () => {
|
||||
return do_inc()
|
||||
}
|
||||
const do_inc = () => {
|
||||
x = x + 1
|
||||
return x
|
||||
}
|
||||
inc()
|
||||
`
|
||||
|
||||
const {state: i, on_deferred_call} = test_deferred_calls_state(code)
|
||||
|
||||
// Make deferred call
|
||||
i.modules[''].inc()
|
||||
|
||||
const state = on_deferred_call(i)
|
||||
const call = get_deferred_calls(state)[0]
|
||||
assert_equal(call.has_more_children, true)
|
||||
assert_equal(call.value, 2)
|
||||
|
||||
// Expand call
|
||||
// first arrow rights selects do_inc call, second steps into it
|
||||
const expanded = COMMANDS.calltree.arrow_right(
|
||||
COMMANDS.calltree.arrow_right(
|
||||
COMMANDS.calltree.click(state, call.id)
|
||||
)
|
||||
)
|
||||
// Move cursor
|
||||
const moved = COMMANDS.move_cursor(expanded, code.indexOf('return x'))
|
||||
assert_equal(moved.value_explorer.result.value, 2)
|
||||
}),
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user