rooklang 0.3.3 → 0.4.0
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 +34 -7
- package/README.ru.md +34 -7
- package/dist/ast/declarations.d.ts +39 -4
- package/dist/ast/expressions.d.ts +34 -8
- package/dist/ast/markup.d.ts +6 -1
- package/dist/ast/statements.d.ts +28 -2
- 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 +4 -0
- package/dist/checker/assignments.js +23 -3
- package/dist/checker/base.d.ts +8 -2
- package/dist/checker/base.js +7 -7
- package/dist/checker/builtins.js +22 -8
- 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 +17 -4
- 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 +48 -13
- package/dist/checker/closing.d.ts +21 -0
- package/dist/checker/closing.js +164 -0
- package/dist/checker/completion.d.ts +3 -3
- package/dist/checker/completion.js +4 -3
- package/dist/checker/component-bindings.d.ts +7 -0
- package/dist/checker/component-bindings.js +45 -0
- package/dist/checker/component-values.js +10 -7
- package/dist/checker/components.d.ts +2 -2
- package/dist/checker/components.js +8 -3
- package/dist/checker/context.d.ts +41 -5
- package/dist/checker/context.js +23 -0
- package/dist/checker/control-flow.d.ts +11 -5
- package/dist/checker/control-flow.js +65 -40
- package/dist/checker/declarations.d.ts +2 -2
- package/dist/checker/declarations.js +4 -2
- package/dist/checker/decode.d.ts +8 -0
- package/dist/checker/decode.js +32 -0
- package/dist/checker/decorator-exports.js +4 -0
- package/dist/checker/decorator-includes.d.ts +9 -0
- package/dist/checker/decorator-includes.js +71 -0
- package/dist/checker/decorator-members.d.ts +8 -10
- package/dist/checker/decorator-members.js +28 -31
- package/dist/checker/decorator-wrappers.d.ts +1 -1
- package/dist/checker/decorator-wrappers.js +3 -2
- package/dist/checker/decorators.d.ts +14 -5
- package/dist/checker/decorators.js +52 -25
- package/dist/checker/expressions.d.ts +6 -2
- package/dist/checker/expressions.js +20 -40
- package/dist/checker/flow.d.ts +25 -3
- package/dist/checker/flow.js +56 -12
- package/dist/checker/functions.d.ts +2 -2
- package/dist/checker/functions.js +9 -5
- package/dist/checker/helpers.d.ts +5 -2
- package/dist/checker/helpers.js +16 -4
- package/dist/checker/imports.js +5 -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.d.ts +2 -0
- package/dist/checker/loops.js +50 -5
- package/dist/checker/markup.js +14 -2
- package/dist/checker/members.js +10 -1
- 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/shared-state.d.ts +11 -0
- package/dist/checker/shared-state.js +37 -0
- package/dist/checker/signals.d.ts +8 -0
- package/dist/checker/signals.js +48 -0
- package/dist/checker/statements.d.ts +5 -2
- package/dist/checker/statements.js +51 -22
- package/dist/checker/tagged-unions.d.ts +37 -0
- package/dist/checker/tagged-unions.js +132 -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/checker/untyped-globals.d.ts +1 -0
- package/dist/checker/untyped-globals.js +3 -1
- package/dist/cli.js +18 -36
- package/dist/codegen/attributes.d.ts +4 -0
- package/dist/codegen/attributes.js +24 -2
- package/dist/codegen/calls.d.ts +23 -0
- package/dist/codegen/calls.js +88 -7
- package/dist/codegen/component-helpers.d.ts +6 -1
- package/dist/codegen/component-helpers.js +6 -1
- package/dist/codegen/component-props.d.ts +11 -2
- package/dist/codegen/component-props.js +50 -5
- package/dist/codegen/components.d.ts +6 -5
- package/dist/codegen/components.js +44 -21
- package/dist/codegen/control-flow.d.ts +2 -0
- package/dist/codegen/control-flow.js +23 -9
- package/dist/codegen/control-statements.d.ts +10 -2
- package/dist/codegen/control-statements.js +56 -19
- package/dist/codegen/creation.js +5 -2
- package/dist/codegen/decode.d.ts +14 -0
- package/dist/codegen/decode.js +93 -0
- package/dist/codegen/early-returns.d.ts +4 -3
- package/dist/codegen/early-returns.js +17 -5
- package/dist/codegen/emitter.d.ts +13 -41
- package/dist/codegen/emitter.js +27 -8
- package/dist/codegen/expressions.d.ts +9 -0
- package/dist/codegen/expressions.js +15 -26
- package/dist/codegen/functions.d.ts +11 -1
- package/dist/codegen/functions.js +31 -3
- package/dist/codegen/js.d.ts +2 -2
- package/dist/codegen/js.js +5 -2
- package/dist/codegen/live.d.ts +60 -0
- package/dist/codegen/live.js +29 -0
- package/dist/codegen/mutations.d.ts +7 -0
- package/dist/codegen/mutations.js +43 -2
- package/dist/codegen/options.d.ts +42 -0
- package/dist/codegen/options.js +1 -0
- package/dist/codegen/reactive.d.ts +4 -59
- package/dist/codegen/reactive.js +34 -72
- package/dist/codegen/runtime.d.ts +7 -3
- package/dist/codegen/runtime.js +33 -14
- package/dist/codegen/shared.d.ts +22 -0
- package/dist/codegen/shared.js +93 -0
- package/dist/codegen/simple-statements.d.ts +11 -5
- package/dist/codegen/simple-statements.js +40 -15
- package/dist/codegen/statements.d.ts +5 -0
- package/dist/codegen/statements.js +99 -4
- package/dist/codegen/try.d.ts +5 -0
- package/dist/codegen/try.js +40 -0
- package/dist/codegen/updates.d.ts +11 -3
- package/dist/codegen/updates.js +52 -4
- package/dist/codegen/web-components.d.ts +2 -2
- package/dist/codegen/web-components.js +2 -2
- package/dist/compile.d.ts +9 -0
- package/dist/compile.js +25 -8
- package/dist/completion.d.ts +4 -2
- package/dist/completion.js +10 -5
- package/dist/declarations.js +32 -6
- package/dist/decorator-copy.d.ts +1 -1
- package/dist/decorator-copy.js +6 -2
- package/dist/decorator-names.d.ts +14 -0
- package/dist/decorator-names.js +56 -0
- package/dist/decorators.d.ts +2 -3
- package/dist/decorators.js +36 -14
- 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/inline-packages.d.ts +2 -1
- package/dist/inline-packages.js +9 -4
- 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 +30 -8
- package/dist/names.d.ts +4 -0
- package/dist/names.js +29 -0
- package/dist/parser/base.d.ts +5 -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 +8 -1
- package/dist/parser/control-flow.js +49 -26
- package/dist/parser/declarations.d.ts +5 -12
- package/dist/parser/declarations.js +14 -79
- package/dist/parser/decorators.d.ts +6 -2
- package/dist/parser/decorators.js +43 -19
- 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/params.d.ts +21 -0
- package/dist/parser/params.js +143 -0
- package/dist/parser/simple-statements.d.ts +2 -1
- package/dist/parser/simple-statements.js +38 -15
- package/dist/parser/statements.d.ts +5 -5
- package/dist/parser/statements.js +39 -35
- 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 +13 -6
- package/dist/source-map.d.ts +17 -0
- package/dist/source-map.js +75 -0
- package/dist/ts-types.js +4 -1
- package/dist/typescript/convert-objects.d.ts +2 -1
- package/dist/typescript/convert-objects.js +18 -9
- package/dist/vite.js +12 -4
- package/dist/walk-types.js +1 -0
- package/dist/walk.d.ts +0 -2
- package/dist/walk.js +14 -31
- package/package.json +2 -2
- 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 +221 -0
- package/src/runtime/go.js +3 -0
- package/src/runtime/index.js +7 -0
- package/src/runtime/markup.js +27 -17
- package/src/runtime/select.js +14 -9
- package/src/runtime/shared.js +17 -0
- 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
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ParamParser } from "./params.js";
|
|
2
2
|
import { describe, FUNCTION_BODY } from "./syntax.js";
|
|
3
|
-
// Imports and exports, functions and components
|
|
4
|
-
export class DeclarationParser extends
|
|
3
|
+
// Imports and exports, functions and components.
|
|
4
|
+
export class DeclarationParser extends ParamParser {
|
|
5
5
|
parseImport() {
|
|
6
6
|
const start = this.next().start;
|
|
7
7
|
if (!this.context.topLevel) {
|
|
@@ -50,6 +50,16 @@ export class DeclarationParser extends ControlFlowParser {
|
|
|
50
50
|
}
|
|
51
51
|
return { kind: 'ImportSpecifier', imported, local, ...this.span(token.start) };
|
|
52
52
|
}
|
|
53
|
+
/** `state items []Item = []` at the top level of a module: state its components share. */
|
|
54
|
+
parseDeclaration(start, exported) {
|
|
55
|
+
if (!this.context.topLevel || !this.checkWord('state') || this.peek(1).kind !== 'Identifier') {
|
|
56
|
+
return super.parseDeclaration(start, exported);
|
|
57
|
+
}
|
|
58
|
+
this.requireMarkup('state of a module is');
|
|
59
|
+
const declaration = this.parseVariableDeclaration(start, exported);
|
|
60
|
+
this.endStatement();
|
|
61
|
+
return declaration;
|
|
62
|
+
}
|
|
53
63
|
parseExport() {
|
|
54
64
|
const start = this.next().start;
|
|
55
65
|
if (!this.context.topLevel) {
|
|
@@ -109,81 +119,6 @@ export class DeclarationParser extends ControlFlowParser {
|
|
|
109
119
|
this.expectTypeClose('">" after the type parameters');
|
|
110
120
|
return params;
|
|
111
121
|
}
|
|
112
|
-
/**
|
|
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.
|
|
115
|
-
*/
|
|
116
|
-
parseParams(typesOptional, allowDefaults = false, allowRest = false) {
|
|
117
|
-
this.expect('(');
|
|
118
|
-
const params = [];
|
|
119
|
-
let untyped = 0;
|
|
120
|
-
while (!this.check(')')) {
|
|
121
|
-
if (this.check('...')) {
|
|
122
|
-
params.push(this.parseRestParam(allowRest));
|
|
123
|
-
break;
|
|
124
|
-
}
|
|
125
|
-
const name = this.parseIdentifier('parameter name');
|
|
126
|
-
const type = this.canStartType() ? this.parseType() : null;
|
|
127
|
-
const defaultValue = allowDefaults && this.accept('=') ? this.parseExpression() : null;
|
|
128
|
-
params.push({ kind: 'Parameter', name, type, defaultValue, ...this.span(name.start) });
|
|
129
|
-
if (type === null) {
|
|
130
|
-
untyped++;
|
|
131
|
-
}
|
|
132
|
-
else {
|
|
133
|
-
for (const param of params.slice(params.length - 1 - untyped))
|
|
134
|
-
param.type = type;
|
|
135
|
-
untyped = 0;
|
|
136
|
-
}
|
|
137
|
-
if (!this.accept(','))
|
|
138
|
-
break;
|
|
139
|
-
}
|
|
140
|
-
this.expect(')');
|
|
141
|
-
if (untyped > 0 && !typesOptional) {
|
|
142
|
-
const param = params[params.length - untyped];
|
|
143
|
-
this.error(`missing type for parameter "${param.name.name}"`, param.start, param.end);
|
|
144
|
-
}
|
|
145
|
-
return params;
|
|
146
|
-
}
|
|
147
|
-
/** `...attrs`: the attributes that a component passes on to one of its elements. */
|
|
148
|
-
parseRestParam(allowed) {
|
|
149
|
-
const start = this.next().start;
|
|
150
|
-
const name = this.parseIdentifier('parameter name');
|
|
151
|
-
if (!allowed) {
|
|
152
|
-
this.error(`...${name.name} is only for components: it takes the attributes of an element`, start, name.end);
|
|
153
|
-
}
|
|
154
|
-
if (this.canStartType() || this.check('=')) {
|
|
155
|
-
const at = this.peek().start;
|
|
156
|
-
// After the error, the type and the default are read, so they are not reported again.
|
|
157
|
-
if (this.canStartType())
|
|
158
|
-
this.parseType();
|
|
159
|
-
if (this.accept('='))
|
|
160
|
-
this.parseExpression();
|
|
161
|
-
this.error(`...${name.name} has no type and no default: it gets the type of the element it is spread on`, at, this.span(at).end);
|
|
162
|
-
}
|
|
163
|
-
// After the error, the rest of the list is skipped up to the `)`.
|
|
164
|
-
if (this.check(',')) {
|
|
165
|
-
this.error(`...${name.name} is the last property of a component`, start, name.end);
|
|
166
|
-
while (!this.check(')') && !this.check('EOF'))
|
|
167
|
-
this.next();
|
|
168
|
-
}
|
|
169
|
-
return {
|
|
170
|
-
kind: 'Parameter',
|
|
171
|
-
name,
|
|
172
|
-
type: null,
|
|
173
|
-
defaultValue: null,
|
|
174
|
-
rest: true,
|
|
175
|
-
...this.span(start),
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
/** Result types after a parameter list: nothing, `T`, or `(T1, T2)`. */
|
|
179
|
-
parseResults() {
|
|
180
|
-
if (this.accept('('))
|
|
181
|
-
return this.parseList(')', () => this.parseType());
|
|
182
|
-
// `{` after the parameters starts the body, so an object result type needs parentheses.
|
|
183
|
-
if (this.canStartType() && !this.check('{'))
|
|
184
|
-
return [this.parseType()];
|
|
185
|
-
return [];
|
|
186
|
-
}
|
|
187
122
|
/** `comp Name(properties) { ...; return <markup> }`, maybe after decorators. */
|
|
188
123
|
parseComponent(start, exported, htmlTag = null, decorators = []) {
|
|
189
124
|
this.requireMarkup('components are');
|
|
@@ -209,7 +144,7 @@ export class DeclarationParser extends ControlFlowParser {
|
|
|
209
144
|
...this.span(start),
|
|
210
145
|
};
|
|
211
146
|
}
|
|
212
|
-
/** Like a function body, but its own statements may also declare `state`, `ref` and `mount
|
|
147
|
+
/** Like a function body, but its own statements may also declare `state`, `ref` and `mount`. */
|
|
213
148
|
parseComponentBody() {
|
|
214
149
|
const start = this.expect('{').start;
|
|
215
150
|
const body = [];
|
|
@@ -6,6 +6,10 @@ export declare abstract class DecoratorParser extends DeclarationParser {
|
|
|
6
6
|
protected parseDecoratorDeclaration(start: number, exported: boolean): ast.DecoratorDeclaration;
|
|
7
7
|
/** `needs cart, auth` after the parameters: `needs` is a keyword only here. */
|
|
8
8
|
protected parseNeeds(): ast.Identifier[];
|
|
9
|
+
/** `includes visible(margin), cart` after `needs`: `includes` is a keyword only here. */
|
|
10
|
+
protected parseIncludes(): ast.DecoratorUse[];
|
|
11
|
+
/** `visible(margin)` or `cart`: like a decorator before `comp`, without the `@`. */
|
|
12
|
+
protected parseIncluded(): ast.DecoratorUse;
|
|
9
13
|
/** Like the body of a component, but `state`, `const` and `func` may be `public`. */
|
|
10
14
|
protected parseDecoratorBody(): ast.BlockStatement;
|
|
11
15
|
/** `public` followed by a declaration; `public = 1` assigns a variable named `public`. */
|
|
@@ -24,7 +28,7 @@ export declare abstract class DecoratorParser extends DeclarationParser {
|
|
|
24
28
|
/** `@visible.show`: a member of a decorator; a statement may start with it. */
|
|
25
29
|
protected isDecoratorMember(): boolean;
|
|
26
30
|
protected parseDecoratorMember(): ast.DecoratorMember;
|
|
27
|
-
/** `get(@visible.shown)
|
|
31
|
+
/** `get(@visible.shown)`, the old way to read a member: reported, and read as the member. */
|
|
28
32
|
protected isDecoratorGet(): boolean;
|
|
29
|
-
protected parseDecoratorGet(): ast.
|
|
33
|
+
protected parseDecoratorGet(): ast.DecoratorMember;
|
|
30
34
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { DeclarationParser } from "./declarations.js";
|
|
2
2
|
import { describe, FUNCTION_BODY } from "./syntax.js";
|
|
3
3
|
// Decorators: their declarations `dec name(...) { ... }`, the decorators written before `comp`
|
|
4
|
-
// (`@html-tag` and `@visible("300px")`), and the access to their members,
|
|
5
|
-
//
|
|
4
|
+
// (`@html-tag` and `@visible("300px")`), and the access to their members, `@visible.shown` and
|
|
5
|
+
// `@visible.show()`.
|
|
6
6
|
export class DecoratorParser extends DeclarationParser {
|
|
7
7
|
/** `dec visible(margin string = "0px") { ... }`: `dec` is a keyword only here. */
|
|
8
8
|
isDecoratorDeclaration() {
|
|
@@ -17,6 +17,7 @@ export class DecoratorParser extends DeclarationParser {
|
|
|
17
17
|
const name = this.parseIdentifier('decorator name');
|
|
18
18
|
const params = this.parseParams(false, true);
|
|
19
19
|
const needs = this.parseNeeds();
|
|
20
|
+
const includes = this.parseIncludes();
|
|
20
21
|
const body = this.withContext({ ...FUNCTION_BODY, inComponent: true }, () => this.parseDecoratorBody());
|
|
21
22
|
return {
|
|
22
23
|
kind: 'DecoratorDeclaration',
|
|
@@ -24,6 +25,7 @@ export class DecoratorParser extends DeclarationParser {
|
|
|
24
25
|
name,
|
|
25
26
|
params,
|
|
26
27
|
needs,
|
|
28
|
+
includes,
|
|
27
29
|
body,
|
|
28
30
|
...this.span(start),
|
|
29
31
|
};
|
|
@@ -41,6 +43,34 @@ export class DecoratorParser extends DeclarationParser {
|
|
|
41
43
|
needs.push(this.parseIdentifier('decorator name'));
|
|
42
44
|
return needs;
|
|
43
45
|
}
|
|
46
|
+
/** `includes visible(margin), cart` after `needs`: `includes` is a keyword only here. */
|
|
47
|
+
parseIncludes() {
|
|
48
|
+
if (!this.checkWord('includes'))
|
|
49
|
+
return [];
|
|
50
|
+
if (this.peek(1).kind !== 'Identifier') {
|
|
51
|
+
this.fail('"includes" is followed by the decorators this one applies, e.g. includes cart');
|
|
52
|
+
}
|
|
53
|
+
this.next();
|
|
54
|
+
const includes = [this.parseIncluded()];
|
|
55
|
+
while (this.accept(','))
|
|
56
|
+
includes.push(this.parseIncluded());
|
|
57
|
+
return includes;
|
|
58
|
+
}
|
|
59
|
+
/** `visible(margin)` or `cart`: like a decorator before `comp`, without the `@`. */
|
|
60
|
+
parseIncluded() {
|
|
61
|
+
const name = this.parseIdentifier('decorator name');
|
|
62
|
+
const args = this.check('(') ? this.parseArguments() : [];
|
|
63
|
+
const spread = args.find((arg) => arg.kind === 'SpreadElement');
|
|
64
|
+
if (spread) {
|
|
65
|
+
this.error('arguments of a decorator cannot be spread', spread.start, spread.end);
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
kind: 'DecoratorUse',
|
|
69
|
+
name,
|
|
70
|
+
arguments: args.filter((arg) => arg.kind !== 'SpreadElement'),
|
|
71
|
+
...this.span(name.start),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
44
74
|
/** Like the body of a component, but `state`, `const` and `func` may be `public`. */
|
|
45
75
|
parseDecoratorBody() {
|
|
46
76
|
const start = this.expect('{').start;
|
|
@@ -71,8 +101,11 @@ export class DecoratorParser extends DeclarationParser {
|
|
|
71
101
|
declaration.isPublic = true;
|
|
72
102
|
return declaration;
|
|
73
103
|
}
|
|
74
|
-
if (!this.check('const') &&
|
|
75
|
-
this.
|
|
104
|
+
if (!this.check('const') &&
|
|
105
|
+
!this.check('let') &&
|
|
106
|
+
!this.isStateDeclaration() &&
|
|
107
|
+
!this.isRefDeclaration()) {
|
|
108
|
+
this.fail(`only state, derived, const, ref and func can be public, not ${describe(this.peek())}`);
|
|
76
109
|
}
|
|
77
110
|
const declaration = this.parseVariableDeclaration(modifier.start, false);
|
|
78
111
|
this.endStatement();
|
|
@@ -137,18 +170,7 @@ export class DecoratorParser extends DeclarationParser {
|
|
|
137
170
|
/** `@visible("300px")` or `@visible`: the arguments are positional, as in a call. */
|
|
138
171
|
parseDecoratorUse() {
|
|
139
172
|
const at = this.expect('@');
|
|
140
|
-
|
|
141
|
-
const args = this.check('(') ? this.parseArguments() : [];
|
|
142
|
-
const spread = args.find((arg) => arg.kind === 'SpreadElement');
|
|
143
|
-
if (spread) {
|
|
144
|
-
this.error('arguments of a decorator cannot be spread', spread.start, spread.end);
|
|
145
|
-
}
|
|
146
|
-
return {
|
|
147
|
-
kind: 'DecoratorUse',
|
|
148
|
-
name,
|
|
149
|
-
arguments: args.filter((arg) => arg.kind !== 'SpreadElement'),
|
|
150
|
-
...this.span(at.start),
|
|
151
|
-
};
|
|
173
|
+
return { ...this.parseIncluded(), ...this.span(at.start) };
|
|
152
174
|
}
|
|
153
175
|
/** `@visible.show`: a member of a decorator; a statement may start with it. */
|
|
154
176
|
isDecoratorMember() {
|
|
@@ -162,7 +184,7 @@ export class DecoratorParser extends DeclarationParser {
|
|
|
162
184
|
const member = this.parsePropertyName('member name');
|
|
163
185
|
return { kind: 'DecoratorMember', decorator, member, ...this.span(at.start) };
|
|
164
186
|
}
|
|
165
|
-
/** `get(@visible.shown)
|
|
187
|
+
/** `get(@visible.shown)`, the old way to read a member: reported, and read as the member. */
|
|
166
188
|
isDecoratorGet() {
|
|
167
189
|
return this.checkWord('get') && this.peek(1).kind === '(' && this.peek(2).kind === '@';
|
|
168
190
|
}
|
|
@@ -170,7 +192,9 @@ export class DecoratorParser extends DeclarationParser {
|
|
|
170
192
|
const start = this.next().start;
|
|
171
193
|
this.expect('(');
|
|
172
194
|
const target = this.parseDecoratorMember();
|
|
173
|
-
|
|
174
|
-
|
|
195
|
+
const { decorator, member } = target;
|
|
196
|
+
this.expect(')');
|
|
197
|
+
this.error(`read a member of a decorator without get: @${decorator.name}.${member.name}`, start, this.span(start).end);
|
|
198
|
+
return target;
|
|
175
199
|
}
|
|
176
200
|
}
|
|
@@ -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;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type * as ast from '../ast.ts';
|
|
2
|
+
import { JumpParser } from './jumps.ts';
|
|
3
|
+
export declare abstract class ParamParser extends JumpParser {
|
|
4
|
+
/**
|
|
5
|
+
* `(a, b number, c string)`. Names without a type take the type of the next typed name, as in
|
|
6
|
+
* Go; only arrow functions may leave types out entirely. The parameters of a constructor can be
|
|
7
|
+
* properties: `(private name string)`.
|
|
8
|
+
*/
|
|
9
|
+
protected parseParams(typesOptional: boolean, allowDefaults?: boolean, allowRest?: boolean, allowProperties?: boolean): ast.Parameter[];
|
|
10
|
+
/** `private`, `readonly` or both before a parameter that is a property of its class. */
|
|
11
|
+
private parsePropertyModifiers;
|
|
12
|
+
/**
|
|
13
|
+
* `bind` before a property of a component, `bind active string`. Followed by a name and then
|
|
14
|
+
* not by `,` or `)`, so a parameter named bind, `(bind Mode)`, stays one.
|
|
15
|
+
*/
|
|
16
|
+
private parseBindModifier;
|
|
17
|
+
/** `...attrs`: the attributes that a component passes on to one of its elements. */
|
|
18
|
+
private parseRestParam;
|
|
19
|
+
/** Result types after a parameter list: nothing, `T`, or `(T1, T2)`. */
|
|
20
|
+
protected parseResults(): ast.TypeNode[];
|
|
21
|
+
}
|