rooklang 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -7
- package/README.ru.md +32 -7
- package/dist/ast/declarations.d.ts +20 -1
- package/dist/ast/expressions.d.ts +31 -1
- package/dist/ast/markup.d.ts +6 -1
- package/dist/ast/statements.d.ts +25 -1
- package/dist/auto-import.d.ts +11 -0
- package/dist/auto-import.js +81 -0
- package/dist/breaks.d.ts +6 -0
- package/dist/breaks.js +36 -0
- package/dist/build.d.ts +7 -0
- package/dist/build.js +23 -2
- package/dist/checker/array-literals.d.ts +6 -0
- package/dist/checker/array-literals.js +59 -0
- package/dist/checker/assignments.d.ts +7 -0
- package/dist/checker/assignments.js +48 -8
- package/dist/checker/base.d.ts +2 -1
- package/dist/checker/base.js +6 -12
- package/dist/checker/builtins.js +15 -5
- package/dist/checker/bytes.d.ts +2 -2
- package/dist/checker/bytes.js +2 -2
- package/dist/checker/calls.js +3 -0
- package/dist/checker/channel-objects.d.ts +3 -0
- package/dist/checker/channel-objects.js +40 -0
- package/dist/checker/channels.d.ts +1 -0
- package/dist/checker/channels.js +16 -2
- package/dist/checker/checker.d.ts +1 -1
- package/dist/checker/checker.js +19 -0
- package/dist/checker/class-members.d.ts +22 -0
- package/dist/checker/class-members.js +105 -0
- package/dist/checker/classes.d.ts +5 -4
- package/dist/checker/classes.js +41 -12
- package/dist/checker/closing.d.ts +21 -0
- package/dist/checker/closing.js +164 -0
- package/dist/checker/completion.d.ts +2 -2
- package/dist/checker/completion.js +2 -2
- package/dist/checker/context.d.ts +19 -0
- package/dist/checker/control-flow.d.ts +6 -0
- package/dist/checker/control-flow.js +34 -0
- package/dist/checker/declarations.js +1 -0
- package/dist/checker/decode.d.ts +8 -0
- package/dist/checker/decode.js +32 -0
- package/dist/checker/error-results.d.ts +6 -0
- package/dist/checker/error-results.js +31 -0
- package/dist/checker/expressions.d.ts +5 -1
- package/dist/checker/expressions.js +13 -34
- package/dist/checker/flow.js +8 -4
- package/dist/checker/helpers.d.ts +1 -1
- package/dist/checker/helpers.js +9 -2
- package/dist/checker/iterables.d.ts +3 -1
- package/dist/checker/iterables.js +13 -0
- package/dist/checker/iterators.d.ts +4 -1
- package/dist/checker/iterators.js +12 -4
- package/dist/checker/json.d.ts +5 -0
- package/dist/checker/json.js +92 -0
- package/dist/checker/loops.js +23 -3
- package/dist/checker/members.js +7 -0
- package/dist/checker/operators.d.ts +2 -2
- package/dist/checker/operators.js +7 -5
- package/dist/checker/queues.d.ts +2 -2
- package/dist/checker/queues.js +2 -2
- package/dist/checker/resolution.js +5 -3
- package/dist/checker/signals.d.ts +8 -0
- package/dist/checker/signals.js +48 -0
- package/dist/checker/statements.d.ts +5 -0
- package/dist/checker/statements.js +19 -0
- package/dist/checker/topics.d.ts +6 -0
- package/dist/checker/topics.js +17 -0
- package/dist/checker/transfer.d.ts +3 -1
- package/dist/checker/transfer.js +25 -5
- package/dist/checker/try.d.ts +6 -0
- package/dist/checker/try.js +19 -0
- package/dist/checker/types/constructors.d.ts +1 -1
- package/dist/checker/types/constructors.js +2 -2
- package/dist/checker/types/generics.js +1 -1
- package/dist/checker/types/iterables.d.ts +13 -0
- package/dist/checker/types/iterables.js +52 -0
- package/dist/checker/types/model.d.ts +25 -0
- package/dist/checker/types/params.js +22 -3
- package/dist/checker/types/printing.js +4 -3
- package/dist/checker/types/relations.js +19 -25
- package/dist/checker/types/topic.d.ts +6 -0
- package/dist/checker/types/topic.js +23 -0
- package/dist/checker/types.d.ts +2 -0
- package/dist/checker/types.js +2 -0
- package/dist/cli.js +18 -36
- package/dist/codegen/calls.d.ts +23 -0
- package/dist/codegen/calls.js +88 -7
- package/dist/codegen/control-statements.d.ts +19 -3
- package/dist/codegen/control-statements.js +105 -20
- package/dist/codegen/decode.d.ts +14 -0
- package/dist/codegen/decode.js +93 -0
- package/dist/codegen/emitter.d.ts +10 -41
- package/dist/codegen/emitter.js +9 -4
- package/dist/codegen/expressions.d.ts +9 -0
- package/dist/codegen/expressions.js +15 -24
- package/dist/codegen/js.d.ts +2 -2
- package/dist/codegen/js.js +5 -2
- package/dist/codegen/options.d.ts +40 -0
- package/dist/codegen/options.js +1 -0
- package/dist/codegen/runtime.d.ts +7 -5
- package/dist/codegen/runtime.js +28 -20
- package/dist/codegen/simple-statements.d.ts +10 -4
- package/dist/codegen/simple-statements.js +40 -15
- package/dist/codegen/statements.js +64 -4
- package/dist/codegen/try.d.ts +5 -0
- package/dist/codegen/try.js +40 -0
- package/dist/compile.d.ts +9 -0
- package/dist/compile.js +19 -6
- package/dist/completion.d.ts +4 -2
- package/dist/completion.js +10 -5
- package/dist/declarations.js +32 -6
- package/dist/decorator-copy.js +5 -2
- package/dist/goroutines.d.ts +1 -1
- package/dist/goroutines.js +5 -4
- package/dist/index.d.ts +6 -2
- package/dist/index.js +14 -5
- package/dist/lexer/base.d.ts +2 -0
- package/dist/lexer/base.js +6 -1
- package/dist/lexer/lexer.js +3 -0
- package/dist/lexer/literals.d.ts +5 -0
- package/dist/lexer/literals.js +51 -1
- package/dist/lexer/markup.js +3 -4
- package/dist/lexer/tables.js +2 -0
- package/dist/lexer/token.d.ts +4 -3
- package/dist/lexer/token.js +0 -2
- package/dist/loader.js +40 -13
- package/dist/parser/base.d.ts +4 -0
- package/dist/parser/base.js +2 -1
- package/dist/parser/classes.d.ts +2 -0
- package/dist/parser/classes.js +88 -23
- package/dist/parser/control-flow.d.ts +3 -1
- package/dist/parser/control-flow.js +21 -24
- package/dist/parser/declarations.d.ts +7 -4
- package/dist/parser/declarations.js +45 -6
- package/dist/parser/expressions.js +27 -3
- package/dist/parser/jumps.d.ts +11 -0
- package/dist/parser/jumps.js +77 -0
- package/dist/parser/literals.d.ts +9 -0
- package/dist/parser/literals.js +45 -6
- package/dist/parser/markup.js +1 -1
- package/dist/parser/simple-statements.d.ts +1 -1
- package/dist/parser/simple-statements.js +16 -9
- package/dist/parser/statements.d.ts +0 -3
- package/dist/parser/statements.js +22 -25
- package/dist/parser/syntax.d.ts +11 -1
- package/dist/parser/syntax.js +16 -2
- package/dist/parser/types.js +8 -3
- package/dist/project-modules.d.ts +12 -0
- package/dist/project-modules.js +106 -0
- package/dist/run-file.d.ts +6 -0
- package/dist/run-file.js +111 -0
- package/dist/runtime/sources.js +11 -5
- package/dist/runtime-module.d.ts +1 -0
- package/dist/runtime-module.js +3 -0
- package/dist/source-map.d.ts +17 -0
- package/dist/source-map.js +75 -0
- package/dist/ts-types.d.ts +4 -0
- package/dist/ts-types.js +20 -3
- package/dist/typescript/convert-objects.d.ts +2 -1
- package/dist/typescript/convert-objects.js +18 -9
- package/dist/vite.js +13 -5
- package/dist/walk-types.js +1 -0
- package/dist/walk.d.ts +0 -2
- package/dist/walk.js +11 -28
- package/package.json +1 -1
- package/src/runtime/chan.js +15 -5
- package/src/runtime/channel.js +28 -0
- package/src/runtime/classes.js +32 -0
- package/src/runtime/core.js +10 -1
- package/src/runtime/decode.js +190 -0
- package/src/runtime/go.js +14 -5
- package/src/runtime/index.js +6 -0
- package/src/runtime/select.js +14 -9
- package/src/runtime/signals.js +59 -0
- package/src/runtime/threads.js +32 -5
- package/src/runtime/topic.js +118 -0
- package/src/runtime/try.js +45 -0
package/dist/parser/classes.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { isKeyword } from "../lexer/token.js";
|
|
2
2
|
import { DecoratorParser } from "./decorators.js";
|
|
3
|
-
import { MODIFIERS } from "./syntax.js";
|
|
3
|
+
import { MODIFIERS, VISIBILITIES } from "./syntax.js";
|
|
4
4
|
// Classes and their members, interfaces and `type` aliases.
|
|
5
5
|
export class ClassParser extends DecoratorParser {
|
|
6
6
|
parseClass(start, exported) {
|
|
7
|
+
const abstract = this.acceptWord('abstract');
|
|
8
|
+
const data = this.acceptWord('data');
|
|
7
9
|
this.expect('class');
|
|
8
10
|
const name = this.parseIdentifier('class name');
|
|
9
11
|
const typeParams = this.parseTypeParams();
|
|
@@ -34,6 +36,8 @@ export class ClassParser extends DecoratorParser {
|
|
|
34
36
|
return {
|
|
35
37
|
kind: 'ClassDeclaration',
|
|
36
38
|
exported,
|
|
39
|
+
data,
|
|
40
|
+
...(abstract ? { abstract } : {}),
|
|
37
41
|
name,
|
|
38
42
|
typeParams,
|
|
39
43
|
superClass,
|
|
@@ -45,24 +49,26 @@ export class ClassParser extends DecoratorParser {
|
|
|
45
49
|
parseClassMember() {
|
|
46
50
|
const start = this.peek().start;
|
|
47
51
|
let visibility = null;
|
|
48
|
-
|
|
52
|
+
const modifiers = new Map();
|
|
49
53
|
// Modifiers are contextual: `static` is a modifier only when a member name follows it.
|
|
50
54
|
while (this.peek().kind === 'Identifier' && MODIFIERS.has(this.peek().text)) {
|
|
51
55
|
const following = this.peek(1);
|
|
52
56
|
if (following.kind !== 'Identifier' && !isKeyword(following.kind))
|
|
53
57
|
break;
|
|
54
58
|
const modifier = this.next();
|
|
55
|
-
if (modifier.text
|
|
56
|
-
if (isStatic)
|
|
57
|
-
this.error('duplicate "static" modifier', modifier.start, modifier.end);
|
|
58
|
-
isStatic = true;
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
59
|
+
if (VISIBILITIES.has(modifier.text)) {
|
|
61
60
|
if (visibility)
|
|
62
61
|
this.error('duplicate visibility modifier', modifier.start, modifier.end);
|
|
63
62
|
visibility = modifier.text;
|
|
64
63
|
}
|
|
64
|
+
else {
|
|
65
|
+
if (modifiers.has(modifier.text)) {
|
|
66
|
+
this.error(`duplicate "${modifier.text}" modifier`, modifier.start, modifier.end);
|
|
67
|
+
}
|
|
68
|
+
modifiers.set(modifier.text, modifier);
|
|
69
|
+
}
|
|
65
70
|
}
|
|
71
|
+
const isStatic = modifiers.has('static');
|
|
66
72
|
// `async load()`; a method named `async` is `async()`.
|
|
67
73
|
const asyncToken = this.check('async') && this.peek(1).kind !== '(' ? this.next() : null;
|
|
68
74
|
const async = asyncToken !== null;
|
|
@@ -72,7 +78,12 @@ export class ClassParser extends DecoratorParser {
|
|
|
72
78
|
this.error('constructors cannot be static', start, this.span(start).end);
|
|
73
79
|
if (async)
|
|
74
80
|
this.error('a constructor cannot be async', start, this.span(start).end);
|
|
75
|
-
const
|
|
81
|
+
for (const word of ['readonly', 'abstract']) {
|
|
82
|
+
const modifier = modifiers.get(word);
|
|
83
|
+
if (modifier)
|
|
84
|
+
this.error(`a constructor cannot be ${word}`, modifier.start, modifier.end);
|
|
85
|
+
}
|
|
86
|
+
const params = this.parseParams(false, false, false, true);
|
|
76
87
|
const resultsStart = this.peek().start;
|
|
77
88
|
if (this.parseResults().length > 0) {
|
|
78
89
|
this.error('constructors cannot have result types', resultsStart, this.span(start).end);
|
|
@@ -88,27 +99,24 @@ export class ClassParser extends DecoratorParser {
|
|
|
88
99
|
},
|
|
89
100
|
];
|
|
90
101
|
}
|
|
102
|
+
// `get area()`; a method named `get` is `get()`, a field `get string`.
|
|
103
|
+
const accessorToken = (this.checkWord('get') || this.checkWord('set')) &&
|
|
104
|
+
isName(this.peek(1)) &&
|
|
105
|
+
this.peek(2).kind === '('
|
|
106
|
+
? this.next()
|
|
107
|
+
: null;
|
|
91
108
|
const name = this.parsePropertyName('member name');
|
|
92
109
|
if (this.check('(')) {
|
|
93
|
-
const params = this.parseParams(false);
|
|
94
|
-
const results = this.parseResults();
|
|
95
|
-
const body = this.parseFunctionBody(async);
|
|
96
110
|
return [
|
|
97
|
-
|
|
98
|
-
kind: 'MethodDeclaration',
|
|
99
|
-
visibility: visibility ?? 'public',
|
|
100
|
-
isStatic,
|
|
101
|
-
...(async ? { async } : {}),
|
|
102
|
-
name,
|
|
103
|
-
params,
|
|
104
|
-
results,
|
|
105
|
-
body,
|
|
106
|
-
...this.span(start),
|
|
107
|
-
},
|
|
111
|
+
this.parseMethod(start, name, visibility ?? 'public', modifiers, asyncToken, accessorToken),
|
|
108
112
|
];
|
|
109
113
|
}
|
|
110
114
|
if (asyncToken)
|
|
111
115
|
this.error('only methods can be async', asyncToken.start, asyncToken.end);
|
|
116
|
+
const abstract = modifiers.get('abstract');
|
|
117
|
+
if (abstract) {
|
|
118
|
+
this.error('only methods can be abstract: for a field, declare an abstract getter, e.g. abstract get name() string', abstract.start, abstract.end);
|
|
119
|
+
}
|
|
112
120
|
// Fields: `name string`, `count = 0`, `x, y number`.
|
|
113
121
|
const names = [name];
|
|
114
122
|
while (this.accept(','))
|
|
@@ -127,12 +135,65 @@ export class ClassParser extends DecoratorParser {
|
|
|
127
135
|
kind: 'FieldDeclaration',
|
|
128
136
|
visibility: visibility ?? 'public',
|
|
129
137
|
isStatic,
|
|
138
|
+
...(modifiers.has('readonly') ? { readonly: true } : {}),
|
|
130
139
|
name: fieldName,
|
|
131
140
|
type,
|
|
132
141
|
value,
|
|
133
142
|
...span,
|
|
134
143
|
}));
|
|
135
144
|
}
|
|
145
|
+
/** A method, a getter or a setter, after its name; an abstract one has no body. */
|
|
146
|
+
parseMethod(start, name, visibility, modifiers, asyncToken, accessorToken) {
|
|
147
|
+
const isStatic = modifiers.has('static');
|
|
148
|
+
const readonly = modifiers.get('readonly');
|
|
149
|
+
const abstract = modifiers.get('abstract');
|
|
150
|
+
const accessor = accessorToken?.text;
|
|
151
|
+
const async = asyncToken !== null;
|
|
152
|
+
if (readonly) {
|
|
153
|
+
this.error('only fields can be readonly: a method cannot be assigned anyway', readonly.start, readonly.end);
|
|
154
|
+
}
|
|
155
|
+
if (abstract && isStatic) {
|
|
156
|
+
this.error('a static method cannot be abstract', abstract.start, abstract.end);
|
|
157
|
+
}
|
|
158
|
+
else if (abstract && visibility === 'private') {
|
|
159
|
+
this.error('an abstract method cannot be private: the subclasses that implement it could not see it', abstract.start, abstract.end);
|
|
160
|
+
}
|
|
161
|
+
if (accessor && asyncToken) {
|
|
162
|
+
this.error(`a ${accessor}ter cannot be async`, asyncToken.start, asyncToken.end);
|
|
163
|
+
}
|
|
164
|
+
const params = this.parseParams(false);
|
|
165
|
+
const results = this.parseResults();
|
|
166
|
+
const header = this.span(start);
|
|
167
|
+
if (accessor === 'get' && (params.length > 0 || results.length !== 1)) {
|
|
168
|
+
this.error('a getter takes nothing and returns one value, e.g. get area() number', header.start, header.end);
|
|
169
|
+
}
|
|
170
|
+
else if (accessor === 'set' && (params.length !== 1 || results.length > 0)) {
|
|
171
|
+
this.error('a setter takes one value and returns nothing, e.g. set area(v number)', header.start, header.end);
|
|
172
|
+
}
|
|
173
|
+
let body = null;
|
|
174
|
+
if (!abstract)
|
|
175
|
+
body = this.parseFunctionBody(async);
|
|
176
|
+
else if (this.check('{')) {
|
|
177
|
+
const block = this.parseFunctionBody(async);
|
|
178
|
+
this.error('an abstract method has no body: the subclasses implement it', block.start, block.end);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
this.endStatement();
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
kind: 'MethodDeclaration',
|
|
185
|
+
visibility,
|
|
186
|
+
isStatic,
|
|
187
|
+
...(async ? { async } : {}),
|
|
188
|
+
...(accessor ? { accessor } : {}),
|
|
189
|
+
...(abstract ? { abstract: true } : {}),
|
|
190
|
+
name,
|
|
191
|
+
params,
|
|
192
|
+
results,
|
|
193
|
+
body,
|
|
194
|
+
...this.span(start),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
136
197
|
parseInterface(start, exported) {
|
|
137
198
|
this.expect('interface');
|
|
138
199
|
const name = this.parseIdentifier('interface name');
|
|
@@ -162,3 +223,7 @@ export class ClassParser extends DecoratorParser {
|
|
|
162
223
|
};
|
|
163
224
|
}
|
|
164
225
|
}
|
|
226
|
+
/** A member name: keywords can be names after `.` and in classes. */
|
|
227
|
+
function isName(token) {
|
|
228
|
+
return token.kind === 'Identifier' || isKeyword(token.kind);
|
|
229
|
+
}
|
|
@@ -2,6 +2,9 @@ import type * as ast from '../ast.ts';
|
|
|
2
2
|
import { StatementParser } from './statements.ts';
|
|
3
3
|
export declare abstract class ControlFlowParser extends StatementParser {
|
|
4
4
|
protected parseIf(): ast.IfStatement;
|
|
5
|
+
protected parseHeaderExpression(): ast.Expression;
|
|
6
|
+
/** `const v, err = f();` before the condition of `if` or the value of `switch`. */
|
|
7
|
+
private parseHeaderInit;
|
|
5
8
|
protected parseFor(): ast.ForStatement | ast.ForInStatement;
|
|
6
9
|
/** The rest of a `for` statement that starts at `start`, after the keyword. */
|
|
7
10
|
private parseForAfter;
|
|
@@ -22,7 +25,6 @@ export declare abstract class ControlFlowParser extends StatementParser {
|
|
|
22
25
|
protected parseSelect(): ast.SelectStatement;
|
|
23
26
|
/** `v = <-ch`, `v, ok = <-ch`, `<-ch` or `ch <- v`; the names are new variables of the case. */
|
|
24
27
|
private parseSelectOperation;
|
|
25
|
-
protected parseBreakOrContinue(): ast.BreakStatement | ast.ContinueStatement;
|
|
26
28
|
protected parseThrow(): ast.ThrowStatement;
|
|
27
29
|
protected parseTry(): ast.TryStatement;
|
|
28
30
|
protected parseDefer(): ast.DeferStatement;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { StatementParser } from "./statements.js";
|
|
2
|
-
// if, for, switch,
|
|
2
|
+
// if, for, switch, select, throw, try and defer.
|
|
3
3
|
export class ControlFlowParser extends StatementParser {
|
|
4
4
|
parseIf() {
|
|
5
5
|
const start = this.next().start;
|
|
6
|
+
const init = this.parseHeaderInit('if');
|
|
6
7
|
const condition = this.parseHeaderExpression();
|
|
7
8
|
const consequent = this.parseBlock();
|
|
8
9
|
let alternate = null;
|
|
@@ -10,7 +11,21 @@ export class ControlFlowParser extends StatementParser {
|
|
|
10
11
|
if (this.accept('else')) {
|
|
11
12
|
alternate = this.check('if') ? this.parseIf() : this.parseBlock();
|
|
12
13
|
}
|
|
13
|
-
return { kind: 'IfStatement', condition, consequent, alternate, ...this.span(start) };
|
|
14
|
+
return { kind: 'IfStatement', init, condition, consequent, alternate, ...this.span(start) };
|
|
15
|
+
}
|
|
16
|
+
parseHeaderExpression() {
|
|
17
|
+
return this.withContext({ noObjectLiteral: true }, () => this.parseExpression());
|
|
18
|
+
}
|
|
19
|
+
/** `const v, err = f();` before the condition of `if` or the value of `switch`. */
|
|
20
|
+
parseHeaderInit(keyword) {
|
|
21
|
+
if (!this.check('const') && !this.check('let'))
|
|
22
|
+
return null;
|
|
23
|
+
const init = this.withContext({ noObjectLiteral: true }, () => this.parseVariableDeclaration(this.peek().start, false));
|
|
24
|
+
if (this.context.inMarkup) {
|
|
25
|
+
this.error(`the ${keyword} of markup has no initializer: declare the variable before the markup`, init.start, init.end);
|
|
26
|
+
}
|
|
27
|
+
this.expect(';', `";" after the initializer of ${keyword}`);
|
|
28
|
+
return init;
|
|
14
29
|
}
|
|
15
30
|
parseFor() {
|
|
16
31
|
return this.parseForAfter(this.next().start);
|
|
@@ -59,7 +74,7 @@ export class ControlFlowParser extends StatementParser {
|
|
|
59
74
|
return { kind: 'ForInStatement', key, value, iterable, body, ...this.span(start) };
|
|
60
75
|
}
|
|
61
76
|
// The parts run at once in workers: nothing can leave the loop but `continue`.
|
|
62
|
-
const body = this.withContext({ inLoop: true, inBreakable: false, inGoFor: true }, () => this.parseBlock());
|
|
77
|
+
const body = this.withContext({ inLoop: true, inBreakable: false, inGoFor: true, labels: [] }, () => this.parseBlock());
|
|
63
78
|
return { kind: 'ForInStatement', key, value, iterable, body, go, ...this.span(start) };
|
|
64
79
|
}
|
|
65
80
|
/** `go for i, v in items { }`: the parts of the array run as goroutines, and the loop waits. */
|
|
@@ -77,6 +92,7 @@ export class ControlFlowParser extends StatementParser {
|
|
|
77
92
|
}
|
|
78
93
|
parseSwitch() {
|
|
79
94
|
const start = this.next().start;
|
|
95
|
+
const init = this.parseHeaderInit('switch');
|
|
80
96
|
const discriminant = this.check('{') ? null : this.parseHeaderExpression();
|
|
81
97
|
this.expect('{');
|
|
82
98
|
const cases = [];
|
|
@@ -101,7 +117,7 @@ export class ControlFlowParser extends StatementParser {
|
|
|
101
117
|
cases.push({ kind: 'SwitchCase', tests, body, ...this.span(caseStart) });
|
|
102
118
|
}
|
|
103
119
|
this.expect('}');
|
|
104
|
-
return { kind: 'SwitchStatement', discriminant, cases, ...this.span(start) };
|
|
120
|
+
return { kind: 'SwitchStatement', init, discriminant, cases, ...this.span(start) };
|
|
105
121
|
}
|
|
106
122
|
/** `select { case v = <-ch: ... case ch <- v: ... default: ... }`; it waits, as `await` does. */
|
|
107
123
|
parseSelect() {
|
|
@@ -157,25 +173,6 @@ export class ControlFlowParser extends StatementParser {
|
|
|
157
173
|
}
|
|
158
174
|
return [names, first];
|
|
159
175
|
}
|
|
160
|
-
parseBreakOrContinue() {
|
|
161
|
-
const token = this.next();
|
|
162
|
-
let statement;
|
|
163
|
-
if (token.kind === 'break') {
|
|
164
|
-
if (!this.context.inBreakable) {
|
|
165
|
-
this.error(this.context.inGoFor
|
|
166
|
-
? '"break" cannot stop "go for": its parts run at once in workers; skip an element with "continue"'
|
|
167
|
-
: '"break" outside of a loop or switch', token.start, token.end);
|
|
168
|
-
}
|
|
169
|
-
statement = { kind: 'BreakStatement', start: token.start, end: token.end };
|
|
170
|
-
}
|
|
171
|
-
else {
|
|
172
|
-
if (!this.context.inLoop)
|
|
173
|
-
this.error('"continue" outside of a loop', token.start, token.end);
|
|
174
|
-
statement = { kind: 'ContinueStatement', start: token.start, end: token.end };
|
|
175
|
-
}
|
|
176
|
-
this.endStatement();
|
|
177
|
-
return statement;
|
|
178
|
-
}
|
|
179
176
|
parseThrow() {
|
|
180
177
|
const start = this.next().start;
|
|
181
178
|
const argument = this.parseExpression();
|
|
@@ -214,7 +211,7 @@ export class ControlFlowParser extends StatementParser {
|
|
|
214
211
|
this.error('"defer" cannot be used in the body of "go for": it runs in workers, apart from the function; defer it before the loop', start, this.span(start).end);
|
|
215
212
|
}
|
|
216
213
|
if (this.check('{')) {
|
|
217
|
-
const body = this.withContext({ inDefer: true, inAsync: false, inLoop: false, inBreakable: false }, () => this.parseBlock());
|
|
214
|
+
const body = this.withContext({ inDefer: true, inAsync: false, inLoop: false, inBreakable: false, labels: [] }, () => this.parseBlock());
|
|
218
215
|
return { kind: 'DeferStatement', body, ...this.span(start) };
|
|
219
216
|
}
|
|
220
217
|
const call = this.withContext({ inDefer: true, inAsync: false }, () => this.parseExpression());
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type * as ast from '../ast.ts';
|
|
2
|
-
import {
|
|
3
|
-
export declare abstract class DeclarationParser extends
|
|
2
|
+
import { JumpParser } from './jumps.ts';
|
|
3
|
+
export declare abstract class DeclarationParser extends JumpParser {
|
|
4
4
|
protected parseImport(): ast.ImportDeclaration;
|
|
5
5
|
protected parseImportSpecifier(): ast.ImportSpecifier;
|
|
6
6
|
protected parseExport(): ast.Statement;
|
|
@@ -11,9 +11,12 @@ export declare abstract class DeclarationParser extends ControlFlowParser {
|
|
|
11
11
|
protected parseTypeParams(): ast.TypeParameter[];
|
|
12
12
|
/**
|
|
13
13
|
* `(a, b number, c string)`. Names without a type take the type of the next typed name, as in
|
|
14
|
-
* Go; only arrow functions may leave types out entirely.
|
|
14
|
+
* Go; only arrow functions may leave types out entirely. The parameters of a constructor can be
|
|
15
|
+
* properties: `(private name string)`.
|
|
15
16
|
*/
|
|
16
|
-
protected parseParams(typesOptional: boolean, allowDefaults?: boolean, allowRest?: boolean): ast.Parameter[];
|
|
17
|
+
protected parseParams(typesOptional: boolean, allowDefaults?: boolean, allowRest?: boolean, allowProperties?: boolean): ast.Parameter[];
|
|
18
|
+
/** `private`, `readonly` or both before a parameter that is a property of its class. */
|
|
19
|
+
private parsePropertyModifiers;
|
|
17
20
|
/** `...attrs`: the attributes that a component passes on to one of its elements. */
|
|
18
21
|
private parseRestParam;
|
|
19
22
|
/** Result types after a parameter list: nothing, `T`, or `(T1, T2)`. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { describe, FUNCTION_BODY } from "./syntax.js";
|
|
1
|
+
import { JumpParser } from "./jumps.js";
|
|
2
|
+
import { describe, FUNCTION_BODY, VISIBILITIES } from "./syntax.js";
|
|
3
3
|
// Imports and exports, functions and components with their parameters and results.
|
|
4
|
-
export class DeclarationParser extends
|
|
4
|
+
export class DeclarationParser extends JumpParser {
|
|
5
5
|
parseImport() {
|
|
6
6
|
const start = this.next().start;
|
|
7
7
|
if (!this.context.topLevel) {
|
|
@@ -111,9 +111,10 @@ export class DeclarationParser extends ControlFlowParser {
|
|
|
111
111
|
}
|
|
112
112
|
/**
|
|
113
113
|
* `(a, b number, c string)`. Names without a type take the type of the next typed name, as in
|
|
114
|
-
* Go; only arrow functions may leave types out entirely.
|
|
114
|
+
* Go; only arrow functions may leave types out entirely. The parameters of a constructor can be
|
|
115
|
+
* properties: `(private name string)`.
|
|
115
116
|
*/
|
|
116
|
-
parseParams(typesOptional, allowDefaults = false, allowRest = false) {
|
|
117
|
+
parseParams(typesOptional, allowDefaults = false, allowRest = false, allowProperties = false) {
|
|
117
118
|
this.expect('(');
|
|
118
119
|
const params = [];
|
|
119
120
|
let untyped = 0;
|
|
@@ -122,10 +123,19 @@ export class DeclarationParser extends ControlFlowParser {
|
|
|
122
123
|
params.push(this.parseRestParam(allowRest));
|
|
123
124
|
break;
|
|
124
125
|
}
|
|
126
|
+
const paramStart = this.peek().start;
|
|
127
|
+
const property = this.parsePropertyModifiers(allowProperties);
|
|
125
128
|
const name = this.parseIdentifier('parameter name');
|
|
126
129
|
const type = this.canStartType() ? this.parseType() : null;
|
|
127
130
|
const defaultValue = allowDefaults && this.accept('=') ? this.parseExpression() : null;
|
|
128
|
-
params.push({
|
|
131
|
+
params.push({
|
|
132
|
+
kind: 'Parameter',
|
|
133
|
+
name,
|
|
134
|
+
type,
|
|
135
|
+
defaultValue,
|
|
136
|
+
...(property ? { property } : {}),
|
|
137
|
+
...this.span(paramStart),
|
|
138
|
+
});
|
|
129
139
|
if (type === null) {
|
|
130
140
|
untyped++;
|
|
131
141
|
}
|
|
@@ -144,6 +154,35 @@ export class DeclarationParser extends ControlFlowParser {
|
|
|
144
154
|
}
|
|
145
155
|
return params;
|
|
146
156
|
}
|
|
157
|
+
/** `private`, `readonly` or both before a parameter that is a property of its class. */
|
|
158
|
+
parsePropertyModifiers(allowed) {
|
|
159
|
+
let visibility = null;
|
|
160
|
+
let readonly = false;
|
|
161
|
+
const start = this.peek().start;
|
|
162
|
+
// Elsewhere `readonly number` is a parameter named readonly, and `private x number` an error.
|
|
163
|
+
while (this.check('Identifier') &&
|
|
164
|
+
(VISIBILITIES.has(this.peek().text) || this.peek().text === 'readonly') &&
|
|
165
|
+
this.peek(1).kind === 'Identifier' &&
|
|
166
|
+
(allowed || this.peek(2).kind === 'Identifier')) {
|
|
167
|
+
const modifier = this.next();
|
|
168
|
+
if (modifier.text === 'readonly') {
|
|
169
|
+
if (readonly)
|
|
170
|
+
this.error('duplicate "readonly" modifier', modifier.start, modifier.end);
|
|
171
|
+
readonly = true;
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
if (visibility)
|
|
175
|
+
this.error('duplicate visibility modifier', modifier.start, modifier.end);
|
|
176
|
+
visibility = modifier.text;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (visibility === null && !readonly)
|
|
180
|
+
return null;
|
|
181
|
+
if (!allowed) {
|
|
182
|
+
this.error('only the parameters of a constructor can be properties, e.g. constructor(private name string)', start, this.span(start).end);
|
|
183
|
+
}
|
|
184
|
+
return { visibility: visibility ?? 'public', ...(readonly ? { readonly: true } : {}) };
|
|
185
|
+
}
|
|
147
186
|
/** `...attrs`: the attributes that a component passes on to one of its elements. */
|
|
148
187
|
parseRestParam(allowed) {
|
|
149
188
|
const start = this.next().start;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BINARY_PRECEDENCE, isBinaryOperator, isUnaryOperator,
|
|
1
|
+
import { BINARY_PRECEDENCE, isBinaryOperator, isUnaryOperator, describe } from "./syntax.js";
|
|
2
2
|
import { TypeParser } from "./types.js";
|
|
3
3
|
// Expressions: operators with their precedence, member access, calls and indexing.
|
|
4
4
|
export class ExpressionParser extends TypeParser {
|
|
@@ -71,6 +71,12 @@ export class ExpressionParser extends TypeParser {
|
|
|
71
71
|
if (token.kind === '++' || token.kind === '--') {
|
|
72
72
|
this.fail(`"${token.kind}" must follow the variable: "i${token.kind}"`);
|
|
73
73
|
}
|
|
74
|
+
// `try` takes the whole expression after it, as `try { }` takes its block.
|
|
75
|
+
if (token.kind === 'try') {
|
|
76
|
+
this.next();
|
|
77
|
+
const argument = this.parseExpression();
|
|
78
|
+
return { kind: 'TryExpression', argument, start: token.start, end: argument.end };
|
|
79
|
+
}
|
|
74
80
|
if (token.kind === 'go') {
|
|
75
81
|
this.next();
|
|
76
82
|
if (this.check('for')) {
|
|
@@ -181,6 +187,16 @@ export class ExpressionParser extends TypeParser {
|
|
|
181
187
|
if (queue)
|
|
182
188
|
return queue;
|
|
183
189
|
}
|
|
190
|
+
if (this.checkWord('topic') && this.peek(1).kind === '<') {
|
|
191
|
+
const topic = this.parseTopic();
|
|
192
|
+
if (topic)
|
|
193
|
+
return topic;
|
|
194
|
+
}
|
|
195
|
+
if (this.checkWord('decode') && this.peek(1).kind === '<') {
|
|
196
|
+
const decode = this.parseDecode();
|
|
197
|
+
if (decode)
|
|
198
|
+
return decode;
|
|
199
|
+
}
|
|
184
200
|
this.next();
|
|
185
201
|
return { kind: 'Identifier', name: token.text, start, end };
|
|
186
202
|
case 'Number':
|
|
@@ -191,6 +207,16 @@ export class ExpressionParser extends TypeParser {
|
|
|
191
207
|
return { kind: 'BigIntLiteral', raw: token.text, start, end };
|
|
192
208
|
case 'String':
|
|
193
209
|
return this.parseStringLiteral();
|
|
210
|
+
case 'RegExp': {
|
|
211
|
+
const { value } = this.next();
|
|
212
|
+
return {
|
|
213
|
+
kind: 'RegExpLiteral',
|
|
214
|
+
pattern: value,
|
|
215
|
+
flags: token.text.slice(value.length + 2),
|
|
216
|
+
start,
|
|
217
|
+
end,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
194
220
|
case 'Template':
|
|
195
221
|
case 'TemplateHead':
|
|
196
222
|
return this.parseTemplate(token);
|
|
@@ -254,8 +280,6 @@ export class ExpressionParser extends TypeParser {
|
|
|
254
280
|
case '@':
|
|
255
281
|
return this.parseDecoratorMember();
|
|
256
282
|
default:
|
|
257
|
-
if (RESERVED.has(token.kind))
|
|
258
|
-
this.fail(`"${token.text}" is reserved for future use`);
|
|
259
283
|
return this.fail(`expected expression, found ${describe(token)}`);
|
|
260
284
|
}
|
|
261
285
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type * as ast from '../ast.ts';
|
|
2
|
+
import { ControlFlowParser } from './control-flow.ts';
|
|
3
|
+
export declare abstract class JumpParser extends ControlFlowParser {
|
|
4
|
+
/** `outer: for`, `outer: switch` or `outer: select`: the statements that break can leave. */
|
|
5
|
+
protected parseLabeled(): ast.Statement;
|
|
6
|
+
protected parseReturn(): ast.ReturnStatement;
|
|
7
|
+
/** `}` + newline + `else`: the newline ends the `if`, so explain the error instead of failing. */
|
|
8
|
+
protected parseBreakOrContinue(): ast.BreakStatement | ast.ContinueStatement;
|
|
9
|
+
/** `break outer` leaves an enclosing statement with that label; `continue outer`, a loop. */
|
|
10
|
+
private checkLabel;
|
|
11
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { ControlFlowParser } from "./control-flow.js";
|
|
2
|
+
// The statements that jump, `return`, `break` and `continue`, and the labels that `break` and
|
|
3
|
+
// `continue` name: `outer: for ... { for ... { continue outer } }`.
|
|
4
|
+
export class JumpParser extends ControlFlowParser {
|
|
5
|
+
/** `outer: for`, `outer: switch` or `outer: select`: the statements that break can leave. */
|
|
6
|
+
parseLabeled() {
|
|
7
|
+
const label = this.parseIdentifier('label');
|
|
8
|
+
this.expect(':');
|
|
9
|
+
const goFor = this.check('go') && this.peek(1).kind === 'for';
|
|
10
|
+
const loop = this.check('for');
|
|
11
|
+
if (goFor || (!loop && !this.check('switch') && !this.checkWord('select'))) {
|
|
12
|
+
this.error(goFor
|
|
13
|
+
? '"go for" cannot have a label: its parts run at once in workers, which break and continue cannot leave'
|
|
14
|
+
: 'a label goes before for, switch or select: only they can be left with break, e.g. outer: for ...', label.start, label.end);
|
|
15
|
+
return this.parseStatement();
|
|
16
|
+
}
|
|
17
|
+
if (this.context.labels.some((each) => each.name === label.name)) {
|
|
18
|
+
this.error(`label "${label.name}" is already used by an enclosing statement`, label.start, label.end);
|
|
19
|
+
}
|
|
20
|
+
const entry = { name: label.name, loop, used: false };
|
|
21
|
+
const statement = this.withContext({ labels: [...this.context.labels, entry] }, () => this.parseStatement());
|
|
22
|
+
if (!entry.used) {
|
|
23
|
+
this.error(`label "${label.name}" is not used: leave the statement with "break ${label.name}" or remove the label`, label.start, label.end);
|
|
24
|
+
}
|
|
25
|
+
return { ...statement, label, start: label.start };
|
|
26
|
+
}
|
|
27
|
+
parseReturn() {
|
|
28
|
+
const start = this.next().start;
|
|
29
|
+
if (this.context.inGoFor) {
|
|
30
|
+
this.error('"return" cannot leave "go for": its body runs in workers; skip an element with "continue"', start, this.span(start).end);
|
|
31
|
+
}
|
|
32
|
+
else if (!this.context.inFunction) {
|
|
33
|
+
this.error('"return" outside of a function', start, this.span(start).end);
|
|
34
|
+
}
|
|
35
|
+
else if (this.context.inDefer) {
|
|
36
|
+
this.error('"return" is not allowed inside "defer"', start, this.span(start).end);
|
|
37
|
+
}
|
|
38
|
+
const values = this.check(';') || this.check('}') || this.check('EOF') ? [] : this.parseExpressionList();
|
|
39
|
+
const statement = { kind: 'ReturnStatement', values, ...this.span(start) };
|
|
40
|
+
this.endStatement();
|
|
41
|
+
return statement;
|
|
42
|
+
}
|
|
43
|
+
/** `}` + newline + `else`: the newline ends the `if`, so explain the error instead of failing. */
|
|
44
|
+
parseBreakOrContinue() {
|
|
45
|
+
const token = this.next();
|
|
46
|
+
const word = token.kind === 'break' ? 'break' : 'continue';
|
|
47
|
+
const label = this.check('Identifier') ? this.parseIdentifier('label') : null;
|
|
48
|
+
const span = { start: token.start, end: label?.end ?? token.end };
|
|
49
|
+
if (label)
|
|
50
|
+
this.checkLabel(word, label);
|
|
51
|
+
else if (word === 'continue' && !this.context.inLoop) {
|
|
52
|
+
this.error('"continue" outside of a loop', token.start, token.end);
|
|
53
|
+
}
|
|
54
|
+
else if (word === 'break' && !this.context.inBreakable) {
|
|
55
|
+
this.error(this.context.inGoFor
|
|
56
|
+
? '"break" cannot stop "go for": its parts run at once in workers; skip an element with "continue"'
|
|
57
|
+
: '"break" outside of a loop or switch', token.start, token.end);
|
|
58
|
+
}
|
|
59
|
+
this.endStatement();
|
|
60
|
+
const kind = word === 'break' ? 'BreakStatement' : 'ContinueStatement';
|
|
61
|
+
return { kind, ...(label ? { label } : {}), ...span };
|
|
62
|
+
}
|
|
63
|
+
/** `break outer` leaves an enclosing statement with that label; `continue outer`, a loop. */
|
|
64
|
+
checkLabel(word, label) {
|
|
65
|
+
const entry = this.context.labels.findLast((each) => each.name === label.name);
|
|
66
|
+
if (!entry) {
|
|
67
|
+
this.error(this.context.inGoFor
|
|
68
|
+
? `"${word} ${label.name}" cannot leave "go for": its parts run at once in workers`
|
|
69
|
+
: `label "${label.name}" is not defined: put it before an enclosing loop, e.g. ${label.name}: for ...`, label.start, label.end);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
entry.used = true;
|
|
73
|
+
if (word === 'continue' && !entry.loop) {
|
|
74
|
+
this.error(`"continue ${label.name}" needs a loop, and ${label.name} labels a switch or select: leave it with "break ${label.name}"`, label.start, label.end);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -19,6 +19,15 @@ export declare abstract class LiteralParser extends ExpressionParser {
|
|
|
19
19
|
* that has no type and `(` after it is a comparison: then this gives null and reads nothing.
|
|
20
20
|
*/
|
|
21
21
|
protected parseQueue(): ast.QueueExpression | null;
|
|
22
|
+
/** `topic<CartEvent>("cart")`; `topic < n` is a comparison, as `queue < n` is. */
|
|
23
|
+
protected parseTopic(): ast.TopicExpression | null;
|
|
24
|
+
/** `decode<User>(body)`; `decode < n` is a comparison, as `topic < n` is. */
|
|
25
|
+
protected parseDecode(): ast.DecodeExpression | null;
|
|
26
|
+
/**
|
|
27
|
+
* `<T>(` after `queue`, `topic` or `decode`: the type, with `(` read. Without `(` after the
|
|
28
|
+
* type, gives null and reads nothing, so that `queue < n` stays a comparison.
|
|
29
|
+
*/
|
|
30
|
+
private parseMadeType;
|
|
22
31
|
/** `chan(stream)` or `chan(stream, size)`, after `chan`. */
|
|
23
32
|
protected parseStreamChan(start: number): ast.StreamChanExpression;
|
|
24
33
|
/** `[]byte("text")`: a type, then its value in parentheses. */
|
package/dist/parser/literals.js
CHANGED
|
@@ -133,18 +133,60 @@ export class LiteralParser extends ExpressionParser {
|
|
|
133
133
|
* that has no type and `(` after it is a comparison: then this gives null and reads nothing.
|
|
134
134
|
*/
|
|
135
135
|
parseQueue() {
|
|
136
|
+
const start = this.peek().start;
|
|
137
|
+
const element = this.parseMadeType();
|
|
138
|
+
if (!element)
|
|
139
|
+
return null;
|
|
140
|
+
const items = this.check(')') ? null : this.nested(() => this.parseExpression());
|
|
141
|
+
this.accept(',');
|
|
142
|
+
this.expect(')', '")": a queue takes its first values as one array, e.g. queue<number>([1, 2])');
|
|
143
|
+
return { kind: 'QueueExpression', element, items, ...this.span(start) };
|
|
144
|
+
}
|
|
145
|
+
/** `topic<CartEvent>("cart")`; `topic < n` is a comparison, as `queue < n` is. */
|
|
146
|
+
parseTopic() {
|
|
147
|
+
const start = this.peek().start;
|
|
148
|
+
const element = this.parseMadeType();
|
|
149
|
+
if (!element)
|
|
150
|
+
return null;
|
|
151
|
+
if (this.check(')')) {
|
|
152
|
+
this.fail('a topic takes its name, the same in every tab, e.g. topic<CartEvent>("cart")');
|
|
153
|
+
}
|
|
154
|
+
const name = this.nested(() => this.parseExpression());
|
|
155
|
+
this.accept(',');
|
|
156
|
+
this.expect(')', '")": a topic takes only its name, e.g. topic<CartEvent>("cart")');
|
|
157
|
+
return { kind: 'TopicExpression', element, name, ...this.span(start) };
|
|
158
|
+
}
|
|
159
|
+
/** `decode<User>(body)`; `decode < n` is a comparison, as `topic < n` is. */
|
|
160
|
+
parseDecode() {
|
|
161
|
+
const start = this.peek().start;
|
|
162
|
+
const type = this.parseMadeType();
|
|
163
|
+
if (!type)
|
|
164
|
+
return null;
|
|
165
|
+
if (this.check(')')) {
|
|
166
|
+
this.fail('decode takes JSON text or a value from JS, e.g. decode<User>(body)');
|
|
167
|
+
}
|
|
168
|
+
const argument = this.nested(() => this.parseExpression());
|
|
169
|
+
this.accept(',');
|
|
170
|
+
this.expect(')', '")": decode takes one argument, e.g. decode<User>(body)');
|
|
171
|
+
return { kind: 'DecodeExpression', type, argument, ...this.span(start) };
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* `<T>(` after `queue`, `topic` or `decode`: the type, with `(` read. Without `(` after the
|
|
175
|
+
* type, gives null and reads nothing, so that `queue < n` stays a comparison.
|
|
176
|
+
*/
|
|
177
|
+
parseMadeType() {
|
|
136
178
|
const { pos } = this;
|
|
137
179
|
// Closing the type may split a `>>` token, and errors are reported as the type is read.
|
|
138
180
|
const tokens = this.tokens.slice(pos);
|
|
139
181
|
const reported = this.diagnostics.length;
|
|
140
|
-
const start = this.next().start;
|
|
141
182
|
let element;
|
|
183
|
+
this.next();
|
|
142
184
|
try {
|
|
143
185
|
this.expect('<');
|
|
144
186
|
element = this.parseType();
|
|
145
187
|
this.expectTypeClose('">" after the type of the values');
|
|
146
188
|
if (!this.check('('))
|
|
147
|
-
throw new ParseError('not a
|
|
189
|
+
throw new ParseError('not a call');
|
|
148
190
|
}
|
|
149
191
|
catch (error) {
|
|
150
192
|
if (!(error instanceof ParseError))
|
|
@@ -155,10 +197,7 @@ export class LiteralParser extends ExpressionParser {
|
|
|
155
197
|
return null;
|
|
156
198
|
}
|
|
157
199
|
this.next();
|
|
158
|
-
|
|
159
|
-
this.accept(',');
|
|
160
|
-
this.expect(')', '")": a queue takes its first values as one array, e.g. queue<number>([1, 2])');
|
|
161
|
-
return { kind: 'QueueExpression', element, items, ...this.span(start) };
|
|
200
|
+
return element;
|
|
162
201
|
}
|
|
163
202
|
/** `chan(stream)` or `chan(stream, size)`, after `chan`. */
|
|
164
203
|
parseStreamChan(start) {
|
package/dist/parser/markup.js
CHANGED
|
@@ -136,7 +136,7 @@ export class MarkupParser extends LiteralParser {
|
|
|
136
136
|
parseEventHandler() {
|
|
137
137
|
const start = this.peek().start;
|
|
138
138
|
const body = [];
|
|
139
|
-
this.withContext({ noObjectLiteral: false, inFunction: true, inLoop: false, inBreakable: false }, () => {
|
|
139
|
+
this.withContext({ noObjectLiteral: false, inFunction: true, inLoop: false, inBreakable: false, labels: [] }, () => {
|
|
140
140
|
do {
|
|
141
141
|
if (this.check('}'))
|
|
142
142
|
break;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type * as ast from '../ast.ts';
|
|
2
2
|
import { ParserBase } from './base.ts';
|
|
3
3
|
export declare abstract class SimpleStatementParser extends ParserBase {
|
|
4
|
-
/** `await`, `<-ch` and `
|
|
4
|
+
/** `await`, `<-ch` and `select` wait: only in async functions and at the top level. */
|
|
5
5
|
protected requireAsync(what: string, start: number, end: number): void;
|
|
6
6
|
/** `x := 1` is a common habit from Go. */
|
|
7
7
|
protected rejectColonEquals(): void;
|