Skip to content

Commit 4e6bec2

Browse files
committed
Add basic label/with parser suport
1 parent d9c0756 commit 4e6bec2

File tree

4 files changed

+72
-3
lines changed

4 files changed

+72
-3
lines changed

feature/loop.js

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Loops: while, do-while, for, for await, break, continue, return
2-
import { expr, skip, space, parse, word, parens, cur, idx, operator, compile } from '../parse.js';
2+
import { expr, skip, space, parse, word, parens, cur, idx, operator, compile, next, seek } from '../parse.js';
33
import { body, keyword } from './block.js';
44
import { destructure } from './destruct.js';
55
import { BREAK, CONTINUE, RETURN } from './control.js';
@@ -34,8 +34,36 @@ keyword('for', STATEMENT + 1, () => {
3434
return ['for', parens(), body()];
3535
});
3636

37-
keyword('break', STATEMENT + 1, () => ['break']);
38-
keyword('continue', STATEMENT + 1, () => ['continue']);
37+
keyword('break', STATEMENT + 1, () => {
38+
parse.asi && (parse.newline = false);
39+
const from = idx;
40+
space();
41+
const c = cur.charCodeAt(idx);
42+
if (!c || c === CBRACE || c === SEMI || parse.newline) return ['break'];
43+
const label = next(parse.id);
44+
if (!label) return ['break'];
45+
// Label must be followed by end/semicolon/newline, not another token
46+
space();
47+
const cc = cur.charCodeAt(idx);
48+
if (!cc || cc === CBRACE || cc === SEMI || parse.newline) return ['break', label];
49+
// Not a valid label - backtrack
50+
seek(from);
51+
return ['break'];
52+
});
53+
keyword('continue', STATEMENT + 1, () => {
54+
parse.asi && (parse.newline = false);
55+
const from = idx;
56+
space();
57+
const c = cur.charCodeAt(idx);
58+
if (!c || c === CBRACE || c === SEMI || parse.newline) return ['continue'];
59+
const label = next(parse.id);
60+
if (!label) return ['continue'];
61+
space();
62+
const cc = cur.charCodeAt(idx);
63+
if (!cc || cc === CBRACE || cc === SEMI || parse.newline) return ['continue', label];
64+
seek(from);
65+
return ['continue'];
66+
});
3967
keyword('return', STATEMENT + 1, () => {
4068
parse.asi && (parse.newline = false);
4169
space();

feature/statement.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Additional JS statements: debugger, with (parse-only)
2+
// debugger → ['debugger']
3+
// with (obj) body → ['with', obj, body]
4+
import { space, parens } from '../parse.js';
5+
import { keyword, body } from './block.js';
6+
7+
const STATEMENT = 5;
8+
9+
// debugger statement
10+
keyword('debugger', STATEMENT + 1, () => ['debugger']);
11+
12+
// with statement
13+
keyword('with', STATEMENT + 1, () => (space(), ['with', parens(), body()]));

jessie.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import './feature/if.js';
1919
import './feature/loop.js';
2020
import './feature/try.js';
2121
import './feature/switch.js';
22+
import './feature/statement.js'; // debugger, with, labeled statements
2223

2324
// Module system
2425
import './feature/module.js';

test/feature/async-class.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,30 @@ test('object: method shorthand', () => {
9797
const obj = compile(parse('{ double(x) { x * 2 } }'))();
9898
is(obj.double(5), 10);
9999
});
100+
101+
// === Additional JS statements ===
102+
103+
test('statement: debugger', () => {
104+
is(parse('debugger'), ['debugger']);
105+
is(parse('debugger;x'), [';', ['debugger'], 'x']);
106+
});
107+
108+
test('statement: with', () => {
109+
is(parse('with (obj) x'), ['with', 'obj', 'x']);
110+
is(parse('with (a.b) { c }'), ['with', ['.', 'a', 'b'], 'c']);
111+
});
112+
113+
test('statement: break/continue label', () => {
114+
is(parse('break'), ['break']);
115+
is(parse('break foo'), ['break', 'foo']);
116+
is(parse('continue'), ['continue']);
117+
is(parse('continue bar'), ['continue', 'bar']);
118+
// break followed by keyword should not consume as label
119+
is(parse('if (x) break else y'), ['if', 'x', ['break'], 'y']);
120+
});
121+
122+
test('statement: label (via colon)', () => {
123+
// Simple labels work via : binary operator
124+
is(parse('foo: x'), [':', 'foo', 'x']);
125+
is(parse('foo: { x }'), [':', 'foo', ['{}', 'x']]);
126+
});

0 commit comments

Comments
 (0)