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
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { JumpParser } from "./jumps.js";
|
|
2
|
+
import { VISIBILITIES } from "./syntax.js";
|
|
3
|
+
// The parameters of functions, constructors and components, with their modifiers, and the results
|
|
4
|
+
// of functions.
|
|
5
|
+
export class ParamParser extends JumpParser {
|
|
6
|
+
/**
|
|
7
|
+
* `(a, b number, c string)`. Names without a type take the type of the next typed name, as in
|
|
8
|
+
* Go; only arrow functions may leave types out entirely. The parameters of a constructor can be
|
|
9
|
+
* properties: `(private name string)`.
|
|
10
|
+
*/
|
|
11
|
+
parseParams(typesOptional, allowDefaults = false, allowRest = false, allowProperties = false) {
|
|
12
|
+
this.expect('(');
|
|
13
|
+
const params = [];
|
|
14
|
+
let untyped = 0;
|
|
15
|
+
while (!this.check(')')) {
|
|
16
|
+
if (this.check('...')) {
|
|
17
|
+
params.push(this.parseRestParam(allowRest));
|
|
18
|
+
break;
|
|
19
|
+
}
|
|
20
|
+
const paramStart = this.peek().start;
|
|
21
|
+
const property = this.parsePropertyModifiers(allowProperties);
|
|
22
|
+
const bind = this.parseBindModifier(allowRest);
|
|
23
|
+
const name = this.parseIdentifier('parameter name');
|
|
24
|
+
const type = this.canStartType() ? this.parseType() : null;
|
|
25
|
+
const defaultValue = allowDefaults && this.accept('=') ? this.parseExpression() : null;
|
|
26
|
+
if (bind && name.name === 'children') {
|
|
27
|
+
this.error('children cannot be bound: it is the content between the tags', paramStart, name.end);
|
|
28
|
+
}
|
|
29
|
+
params.push({
|
|
30
|
+
kind: 'Parameter',
|
|
31
|
+
name,
|
|
32
|
+
type,
|
|
33
|
+
defaultValue,
|
|
34
|
+
...(property ? { property } : {}),
|
|
35
|
+
...(bind ? { bind: true } : {}),
|
|
36
|
+
...this.span(paramStart),
|
|
37
|
+
});
|
|
38
|
+
if (type === null) {
|
|
39
|
+
untyped++;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
for (const param of params.slice(params.length - 1 - untyped))
|
|
43
|
+
param.type = type;
|
|
44
|
+
untyped = 0;
|
|
45
|
+
}
|
|
46
|
+
if (!this.accept(','))
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
this.expect(')');
|
|
50
|
+
if (untyped > 0 && !typesOptional) {
|
|
51
|
+
const param = params[params.length - untyped];
|
|
52
|
+
this.error(`missing type for parameter "${param.name.name}"`, param.start, param.end);
|
|
53
|
+
}
|
|
54
|
+
return params;
|
|
55
|
+
}
|
|
56
|
+
/** `private`, `readonly` or both before a parameter that is a property of its class. */
|
|
57
|
+
parsePropertyModifiers(allowed) {
|
|
58
|
+
let visibility = null;
|
|
59
|
+
let readonly = false;
|
|
60
|
+
const start = this.peek().start;
|
|
61
|
+
// Elsewhere `readonly number` is a parameter named readonly, and `private x number` an error.
|
|
62
|
+
while (this.check('Identifier') &&
|
|
63
|
+
(VISIBILITIES.has(this.peek().text) || this.peek().text === 'readonly') &&
|
|
64
|
+
this.peek(1).kind === 'Identifier' &&
|
|
65
|
+
(allowed || this.peek(2).kind === 'Identifier')) {
|
|
66
|
+
const modifier = this.next();
|
|
67
|
+
if (modifier.text === 'readonly') {
|
|
68
|
+
if (readonly)
|
|
69
|
+
this.error('duplicate "readonly" modifier', modifier.start, modifier.end);
|
|
70
|
+
readonly = true;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
if (visibility)
|
|
74
|
+
this.error('duplicate visibility modifier', modifier.start, modifier.end);
|
|
75
|
+
visibility = modifier.text;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (visibility === null && !readonly)
|
|
79
|
+
return null;
|
|
80
|
+
if (!allowed) {
|
|
81
|
+
this.error('only the parameters of a constructor can be properties, e.g. constructor(private name string)', start, this.span(start).end);
|
|
82
|
+
}
|
|
83
|
+
return { visibility: visibility ?? 'public', ...(readonly ? { readonly: true } : {}) };
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* `bind` before a property of a component, `bind active string`. Followed by a name and then
|
|
87
|
+
* not by `,` or `)`, so a parameter named bind, `(bind Mode)`, stays one.
|
|
88
|
+
*/
|
|
89
|
+
parseBindModifier(allowed) {
|
|
90
|
+
const after = this.peek(2).kind;
|
|
91
|
+
if (!this.checkWord('bind') ||
|
|
92
|
+
this.peek(1).kind !== 'Identifier' ||
|
|
93
|
+
after === ',' ||
|
|
94
|
+
after === ')') {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
const modifier = this.next();
|
|
98
|
+
if (!allowed) {
|
|
99
|
+
this.error('only the properties of components can be bound, e.g. comp Tabs(bind active string)', modifier.start, modifier.end);
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
/** `...attrs`: the attributes that a component passes on to one of its elements. */
|
|
104
|
+
parseRestParam(allowed) {
|
|
105
|
+
const start = this.next().start;
|
|
106
|
+
const name = this.parseIdentifier('parameter name');
|
|
107
|
+
if (!allowed) {
|
|
108
|
+
this.error(`...${name.name} is only for components: it takes the attributes of an element`, start, name.end);
|
|
109
|
+
}
|
|
110
|
+
if (this.canStartType() || this.check('=')) {
|
|
111
|
+
const at = this.peek().start;
|
|
112
|
+
// After the error, the type and the default are read, so they are not reported again.
|
|
113
|
+
if (this.canStartType())
|
|
114
|
+
this.parseType();
|
|
115
|
+
if (this.accept('='))
|
|
116
|
+
this.parseExpression();
|
|
117
|
+
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);
|
|
118
|
+
}
|
|
119
|
+
// After the error, the rest of the list is skipped up to the `)`.
|
|
120
|
+
if (this.check(',')) {
|
|
121
|
+
this.error(`...${name.name} is the last property of a component`, start, name.end);
|
|
122
|
+
while (!this.check(')') && !this.check('EOF'))
|
|
123
|
+
this.next();
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
kind: 'Parameter',
|
|
127
|
+
name,
|
|
128
|
+
type: null,
|
|
129
|
+
defaultValue: null,
|
|
130
|
+
rest: true,
|
|
131
|
+
...this.span(start),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/** Result types after a parameter list: nothing, `T`, or `(T1, T2)`. */
|
|
135
|
+
parseResults() {
|
|
136
|
+
if (this.accept('('))
|
|
137
|
+
return this.parseList(')', () => this.parseType());
|
|
138
|
+
// `{` after the parameters starts the body, so an object result type needs parentheses.
|
|
139
|
+
if (this.canStartType() && !this.check('{'))
|
|
140
|
+
return [this.parseType()];
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -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;
|
|
@@ -18,5 +18,6 @@ export declare abstract class SimpleStatementParser extends ParserBase {
|
|
|
18
18
|
protected parseExpressionList(): ast.Expression[];
|
|
19
19
|
/** `ref input` is one name, and the markup gives its element. */
|
|
20
20
|
private checkRef;
|
|
21
|
+
private parseDerivedBlock;
|
|
21
22
|
protected parseVariableDeclaration(start: number, exported: boolean): ast.VariableDeclaration;
|
|
22
23
|
}
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { ParserBase } from "./base.js";
|
|
2
|
-
import { isAssignmentOperator, describe, plural } from "./syntax.js";
|
|
2
|
+
import { cannotWait, isAssignmentOperator, describe, plural } from "./syntax.js";
|
|
3
3
|
// Simple statements, which `for` headers allow too: declarations of variables, assignments, `i++`
|
|
4
4
|
// and calls.
|
|
5
5
|
export class SimpleStatementParser extends ParserBase {
|
|
6
|
-
/** `await`, `<-ch` and `
|
|
6
|
+
/** `await`, `<-ch` and `select` wait: only in async functions and at the top level. */
|
|
7
7
|
requireAsync(what, start, end) {
|
|
8
8
|
if (this.context.inAsync)
|
|
9
9
|
return;
|
|
10
|
-
this.error(this.context.inDefer
|
|
11
|
-
? `"${what}" is not allowed inside "defer": deferred code runs when the function ends`
|
|
12
|
-
: `"${what}" is allowed only in async functions and at the top level of a module; mark the function, e.g. "async func load() { ... }"`, start, end);
|
|
10
|
+
this.error(cannotWait(what, this.context.inDefer), start, end);
|
|
13
11
|
}
|
|
14
12
|
/** `x := 1` is a common habit from Go. */
|
|
15
13
|
rejectColonEquals() {
|
|
@@ -49,12 +47,15 @@ export class SimpleStatementParser extends ParserBase {
|
|
|
49
47
|
this.checkArity(targets.length, values, start);
|
|
50
48
|
return { kind: 'AssignmentStatement', operator, targets, values, ...this.span(start) };
|
|
51
49
|
}
|
|
52
|
-
// `ch <- value`: the value goes into the channel.
|
|
50
|
+
// `ch <- value`: the value goes into the channel. A send to a channel waits, and one to a
|
|
51
|
+
// topic does not, so whether it may be here is for the checker, which knows the type.
|
|
53
52
|
if (this.check('<-')) {
|
|
54
|
-
const
|
|
55
|
-
this.requireAsync('<-', arrow.start, arrow.end);
|
|
53
|
+
const { start: from, end } = this.next();
|
|
56
54
|
const value = this.parseExpression();
|
|
57
|
-
|
|
55
|
+
const cannotWait = this.context.inAsync
|
|
56
|
+
? {}
|
|
57
|
+
: { cannotWait: { inDefer: this.context.inDefer, start: from, end } };
|
|
58
|
+
return { kind: 'SendStatement', channel: first, value, ...cannotWait, ...this.span(start) };
|
|
58
59
|
}
|
|
59
60
|
if (this.check('++') || this.check('--')) {
|
|
60
61
|
const operator = this.next().kind === '++' ? '++' : '--';
|
|
@@ -66,7 +67,6 @@ export class SimpleStatementParser extends ParserBase {
|
|
|
66
67
|
checkAssignable(target) {
|
|
67
68
|
// Members of decorators are read-only outside them: the checker explains it.
|
|
68
69
|
const assignable = target.kind === 'Identifier' ||
|
|
69
|
-
target.kind === 'DecoratorGet' ||
|
|
70
70
|
target.kind === 'DecoratorMember' ||
|
|
71
71
|
((target.kind === 'MemberExpression' || target.kind === 'IndexExpression') &&
|
|
72
72
|
!target.optional);
|
|
@@ -90,6 +90,12 @@ export class SimpleStatementParser extends ParserBase {
|
|
|
90
90
|
// `chunks, failed = chan(res.body)`: the channel of a stream and that of its error.
|
|
91
91
|
if (values.length === 1 && value?.kind === 'StreamChanExpression')
|
|
92
92
|
return;
|
|
93
|
+
// `user, err = decode<User>(body)`: the value and the error.
|
|
94
|
+
if (values.length === 1 && value?.kind === 'DecodeExpression')
|
|
95
|
+
return;
|
|
96
|
+
// `data, err = try JSON.parse(text)`: the value and the error, as from a call.
|
|
97
|
+
if (values.length === 1 && value?.kind === 'TryExpression')
|
|
98
|
+
return;
|
|
93
99
|
this.error(`assignment mismatch: ${plural(targets, 'variable')} but ${plural(values.length, 'value')}`, start, this.span(start).end);
|
|
94
100
|
}
|
|
95
101
|
/** `delete scores[name]`: removes a field, so the target is one. */
|
|
@@ -118,6 +124,17 @@ export class SimpleStatementParser extends ParserBase {
|
|
|
118
124
|
this.error(`ref declares one name, e.g. ref ${name}`, start, end);
|
|
119
125
|
}
|
|
120
126
|
}
|
|
127
|
+
parseDerivedBlock() {
|
|
128
|
+
const body = this.parseFunctionBody();
|
|
129
|
+
return {
|
|
130
|
+
kind: 'FuncExpression',
|
|
131
|
+
params: [],
|
|
132
|
+
results: [],
|
|
133
|
+
body,
|
|
134
|
+
start: body.start,
|
|
135
|
+
end: body.end,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
121
138
|
parseVariableDeclaration(start, exported) {
|
|
122
139
|
const token = this.next();
|
|
123
140
|
const keyword = token.kind === 'const'
|
|
@@ -128,16 +145,21 @@ export class SimpleStatementParser extends ParserBase {
|
|
|
128
145
|
const names = [this.parseIdentifier('variable name')];
|
|
129
146
|
while (this.accept(','))
|
|
130
147
|
names.push(this.parseIdentifier('variable name'));
|
|
131
|
-
|
|
132
|
-
|
|
148
|
+
// `derived summary {` starts the block that computes it, as `{` after the parameters of a
|
|
149
|
+
// function starts its body: an object type goes in parentheses.
|
|
150
|
+
const block = () => keyword === 'derived' && this.check('{');
|
|
151
|
+
const type = !block() && this.canStartType() ? this.parseType() : null;
|
|
152
|
+
const body = block() ? this.parseDerivedBlock() : null;
|
|
153
|
+
const values = body === null && this.accept('=') ? this.parseExpressionList() : [];
|
|
133
154
|
const { end } = this.span(start);
|
|
134
155
|
if (keyword === 'ref') {
|
|
135
156
|
this.checkRef(names, values.length > 0, start, end);
|
|
136
157
|
}
|
|
137
|
-
else if (keyword === 'derived' &&
|
|
138
|
-
|
|
158
|
+
else if (keyword === 'derived' &&
|
|
159
|
+
(names.length !== 1 || values.length + Number(!!body) !== 1)) {
|
|
160
|
+
this.error('derived is one name and the expression it is computed from, e.g. derived double = count * 2, or a block that returns it: derived total number { ... return sum }', start, end);
|
|
139
161
|
}
|
|
140
|
-
else if (values.length === 0 && type === null) {
|
|
162
|
+
else if (values.length === 0 && type === null && body === null) {
|
|
141
163
|
this.error(`"${names[0].name}" needs a type or a value`, start, end);
|
|
142
164
|
}
|
|
143
165
|
else if (values.length === 0 && keyword === 'const') {
|
|
@@ -152,6 +174,7 @@ export class SimpleStatementParser extends ParserBase {
|
|
|
152
174
|
names,
|
|
153
175
|
type,
|
|
154
176
|
values,
|
|
177
|
+
body,
|
|
155
178
|
start,
|
|
156
179
|
end,
|
|
157
180
|
};
|
|
@@ -21,17 +21,17 @@ export declare abstract class StatementParser extends SimpleStatementParser {
|
|
|
21
21
|
protected parseStyle(): ast.StyleStatement;
|
|
22
22
|
/** `effect {`: like `state`, a keyword only at the start of a statement in a component. */
|
|
23
23
|
protected isEffectDeclaration(): boolean;
|
|
24
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* `mount {`: like `state`, a keyword only at the start of a statement in a component. The old
|
|
26
|
+
* form `mount() {` is recognized too, so that parseMount can say how to write it now.
|
|
27
|
+
*/
|
|
25
28
|
protected isMountDeclaration(): boolean;
|
|
26
|
-
/** `mount
|
|
29
|
+
/** `mount { ... }`; its body is a function that runs later, so `return` is its own. */
|
|
27
30
|
protected parseMount(): ast.MountStatement;
|
|
28
31
|
/** `effect { ... }`: its body runs again later, so `return` and `defer` are its own. */
|
|
29
32
|
protected parseEffect(): ast.EffectStatement;
|
|
30
33
|
protected parseBlock(): ast.BlockStatement;
|
|
31
34
|
protected parseFunctionBody(async?: boolean): ast.BlockStatement;
|
|
32
35
|
/** An if/for/switch header, where `{` starts the block rather than an object literal. */
|
|
33
|
-
protected parseHeaderExpression(): ast.Expression;
|
|
34
|
-
protected parseReturn(): ast.ReturnStatement;
|
|
35
|
-
/** `}` + newline + `else`: the newline ends the `if`, so explain the error instead of failing. */
|
|
36
36
|
protected checkSameLine(keyword: 'else' | 'catch' | 'finally'): void;
|
|
37
37
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SimpleStatementParser } from "./simple-statements.js";
|
|
2
2
|
import { FOREIGN_KEYWORDS, FUNCTION_BODY, describe } from "./syntax.js";
|
|
3
|
-
// Statements: blocks, `
|
|
3
|
+
// Statements: blocks, `mount`, and which statement a token starts.
|
|
4
4
|
export class StatementParser extends SimpleStatementParser {
|
|
5
5
|
parseStatements(isEnd) {
|
|
6
6
|
const body = [];
|
|
@@ -62,13 +62,20 @@ export class StatementParser extends SimpleStatementParser {
|
|
|
62
62
|
return this.parseBreakOrContinue();
|
|
63
63
|
case 'throw':
|
|
64
64
|
return this.parseThrow();
|
|
65
|
+
// `try f()` without a block is an expression.
|
|
65
66
|
case 'try':
|
|
66
|
-
|
|
67
|
+
if (this.peek(1).kind === '{')
|
|
68
|
+
return this.parseTry();
|
|
69
|
+
break;
|
|
67
70
|
case 'defer':
|
|
68
71
|
return this.parseDefer();
|
|
69
72
|
default:
|
|
70
73
|
break;
|
|
71
74
|
}
|
|
75
|
+
// `outer: for`; `x := 1` is reported as Go's declaration.
|
|
76
|
+
if (token.kind === 'Identifier' && this.peek(1).kind === ':' && this.peek(2).kind !== '=') {
|
|
77
|
+
return this.parseLabeled();
|
|
78
|
+
}
|
|
72
79
|
const hint = token.kind === 'Identifier' ? FOREIGN_KEYWORDS.get(token.text) : undefined;
|
|
73
80
|
if (hint !== undefined && this.peek(1).kind === 'Identifier') {
|
|
74
81
|
this.fail(`"${token.text}" is not a Rook keyword: ${hint}`);
|
|
@@ -88,14 +95,16 @@ export class StatementParser extends SimpleStatementParser {
|
|
|
88
95
|
const misplaced = this.isStateDeclaration() || this.isRefDeclaration()
|
|
89
96
|
? this.peek().text
|
|
90
97
|
: this.isMountDeclaration()
|
|
91
|
-
? 'mount
|
|
98
|
+
? 'mount'
|
|
92
99
|
: this.isEffectDeclaration()
|
|
93
100
|
? 'effect'
|
|
94
101
|
: null;
|
|
95
102
|
if (misplaced !== null) {
|
|
96
103
|
this.fail(this.context.inComponent
|
|
97
104
|
? `${misplaced} is declared at the top level of a component, not inside blocks or functions`
|
|
98
|
-
:
|
|
105
|
+
: misplaced === 'state'
|
|
106
|
+
? 'state can only be declared inside a component or at the top level of a module'
|
|
107
|
+
: `${misplaced} can only be declared inside a component`);
|
|
99
108
|
}
|
|
100
109
|
const statement = this.parseSimpleStatement();
|
|
101
110
|
if (statement.kind === 'ExpressionStatement') {
|
|
@@ -106,7 +115,10 @@ export class StatementParser extends SimpleStatementParser {
|
|
|
106
115
|
const call = expression.kind === 'CallExpression' ||
|
|
107
116
|
expression.kind === 'NewExpression' ||
|
|
108
117
|
expression.kind === 'GoExpression';
|
|
109
|
-
if (
|
|
118
|
+
if (expression.kind === 'TryExpression') {
|
|
119
|
+
this.error('try gives the error, which is dropped here: keep it, e.g. "const err = try save()", or catch it with try { } catch e { }', expression.start, expression.end);
|
|
120
|
+
}
|
|
121
|
+
else if (!call && !awaited) {
|
|
110
122
|
this.error('this expression does nothing: only function calls can be used as statements', expression.start, expression.end);
|
|
111
123
|
}
|
|
112
124
|
}
|
|
@@ -123,7 +135,7 @@ export class StatementParser extends SimpleStatementParser {
|
|
|
123
135
|
return this.parseFuncDeclaration(start, exported);
|
|
124
136
|
case 'async':
|
|
125
137
|
if (this.peek(1).kind === 'comp') {
|
|
126
|
-
this.fail('a component cannot be async: load the data in mount
|
|
138
|
+
this.fail('a component cannot be async: load the data in mount or in an async func');
|
|
127
139
|
}
|
|
128
140
|
if (this.peek(1).kind !== 'func' || this.peek(2).kind !== 'Identifier')
|
|
129
141
|
return undefined;
|
|
@@ -151,10 +163,17 @@ export class StatementParser extends SimpleStatementParser {
|
|
|
151
163
|
this.endStatement();
|
|
152
164
|
return declaration;
|
|
153
165
|
}
|
|
154
|
-
default:
|
|
166
|
+
default: {
|
|
155
167
|
if (this.isDecoratorDeclaration())
|
|
156
168
|
return this.parseDecoratorDeclaration(start, exported);
|
|
157
|
-
|
|
169
|
+
// `data` and `abstract` are keywords only before `class`: elsewhere they are names.
|
|
170
|
+
let words = this.checkWord('abstract') ? 1 : 0;
|
|
171
|
+
if (this.peek(words).text === 'data')
|
|
172
|
+
words++;
|
|
173
|
+
return words > 0 && this.peek(words).kind === 'class'
|
|
174
|
+
? this.parseClass(start, exported)
|
|
175
|
+
: undefined;
|
|
176
|
+
}
|
|
158
177
|
}
|
|
159
178
|
}
|
|
160
179
|
/** A statement ends with a newline or `;`, or right before the `}` that closes its block. */
|
|
@@ -206,18 +225,23 @@ export class StatementParser extends SimpleStatementParser {
|
|
|
206
225
|
isEffectDeclaration() {
|
|
207
226
|
return this.checkWord('effect') && this.peek(1).kind === '{';
|
|
208
227
|
}
|
|
209
|
-
/**
|
|
228
|
+
/**
|
|
229
|
+
* `mount {`: like `state`, a keyword only at the start of a statement in a component. The old
|
|
230
|
+
* form `mount() {` is recognized too, so that parseMount can say how to write it now.
|
|
231
|
+
*/
|
|
210
232
|
isMountDeclaration() {
|
|
211
233
|
return (this.checkWord('mount') &&
|
|
212
|
-
this.peek(1).kind === '
|
|
213
|
-
|
|
214
|
-
this.peek(3).kind === '{');
|
|
234
|
+
(this.peek(1).kind === '{' ||
|
|
235
|
+
(this.peek(1).kind === '(' && this.peek(2).kind === ')' && this.peek(3).kind === '{')));
|
|
215
236
|
}
|
|
216
|
-
/** `mount
|
|
237
|
+
/** `mount { ... }`; its body is a function that runs later, so `return` is its own. */
|
|
217
238
|
parseMount() {
|
|
218
239
|
const { start } = this.next();
|
|
219
|
-
this.
|
|
220
|
-
|
|
240
|
+
if (this.check('(')) {
|
|
241
|
+
const open = this.next();
|
|
242
|
+
const close = this.next();
|
|
243
|
+
this.error('mount has no parentheses, like effect: write mount { ... }', open.start, close.end);
|
|
244
|
+
}
|
|
221
245
|
const body = this.withContext({ ...FUNCTION_BODY, inComponent: true }, () => this.parseBlock());
|
|
222
246
|
return { kind: 'MountStatement', body, ...this.span(start) };
|
|
223
247
|
}
|
|
@@ -237,26 +261,6 @@ export class StatementParser extends SimpleStatementParser {
|
|
|
237
261
|
return this.withContext({ ...FUNCTION_BODY, inAsync: async }, () => this.parseBlock());
|
|
238
262
|
}
|
|
239
263
|
/** An if/for/switch header, where `{` starts the block rather than an object literal. */
|
|
240
|
-
parseHeaderExpression() {
|
|
241
|
-
return this.withContext({ noObjectLiteral: true }, () => this.parseExpression());
|
|
242
|
-
}
|
|
243
|
-
parseReturn() {
|
|
244
|
-
const start = this.next().start;
|
|
245
|
-
if (this.context.inGoFor) {
|
|
246
|
-
this.error('"return" cannot leave "go for": its body runs in workers; skip an element with "continue"', start, this.span(start).end);
|
|
247
|
-
}
|
|
248
|
-
else if (!this.context.inFunction) {
|
|
249
|
-
this.error('"return" outside of a function', start, this.span(start).end);
|
|
250
|
-
}
|
|
251
|
-
else if (this.context.inDefer) {
|
|
252
|
-
this.error('"return" is not allowed inside "defer"', start, this.span(start).end);
|
|
253
|
-
}
|
|
254
|
-
const values = this.check(';') || this.check('}') || this.check('EOF') ? [] : this.parseExpressionList();
|
|
255
|
-
const statement = { kind: 'ReturnStatement', values, ...this.span(start) };
|
|
256
|
-
this.endStatement();
|
|
257
|
-
return statement;
|
|
258
|
-
}
|
|
259
|
-
/** `}` + newline + `else`: the newline ends the `if`, so explain the error instead of failing. */
|
|
260
264
|
checkSameLine(keyword) {
|
|
261
265
|
if (this.isImplicitSemicolon() && this.peek(1).kind === keyword) {
|
|
262
266
|
const token = this.peek(1);
|
package/dist/parser/syntax.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export declare const ASSIGNMENT_OPERATORS: ReadonlySet<string>;
|
|
|
7
7
|
export declare function isBinaryOperator(kind: TokenKind): kind is ast.BinaryOperator;
|
|
8
8
|
export declare function isUnaryOperator(kind: TokenKind): kind is ast.UnaryOperator;
|
|
9
9
|
export declare function isAssignmentOperator(kind: TokenKind): kind is ast.AssignmentOperator;
|
|
10
|
-
export declare const
|
|
10
|
+
export declare const VISIBILITIES: ReadonlySet<string>;
|
|
11
11
|
export declare const MODIFIERS: ReadonlySet<string>;
|
|
12
12
|
/** Words from JS that Rook spells differently. */
|
|
13
13
|
export declare const FOREIGN_KEYWORDS: ReadonlyMap<string, string>;
|
|
@@ -33,6 +33,14 @@ export interface Context {
|
|
|
33
33
|
inComponent: boolean;
|
|
34
34
|
/** In the blocks of `{if ...}` and `{for ...}` inside markup, where elements are statements. */
|
|
35
35
|
inMarkup: boolean;
|
|
36
|
+
/** The labels of the enclosing statements, innermost last, which `break` and `continue` name. */
|
|
37
|
+
labels: readonly Label[];
|
|
38
|
+
}
|
|
39
|
+
/** `outer: for`: `loop` tells `continue outer` from a label of a switch or select. */
|
|
40
|
+
export interface Label {
|
|
41
|
+
name: string;
|
|
42
|
+
loop: boolean;
|
|
43
|
+
used: boolean;
|
|
36
44
|
}
|
|
37
45
|
export declare const FUNCTION_BODY: Partial<Context>;
|
|
38
46
|
/** Thrown to abandon the current statement after its syntax error has been reported. */
|
|
@@ -49,3 +57,5 @@ export declare function cleanJsxText(text: string): string;
|
|
|
49
57
|
/** In `on*` attributes, these are the handler itself rather than code to run. */
|
|
50
58
|
export declare function isHandlerReference(node: ast.Expression): boolean;
|
|
51
59
|
export declare function templateElement(token: StringToken): ast.TemplateElement;
|
|
60
|
+
/** Why `what`, which waits, is not allowed where it is: not in an async function or in `defer`. */
|
|
61
|
+
export declare function cannotWait(what: string, inDefer: boolean): string;
|
package/dist/parser/syntax.js
CHANGED
|
@@ -60,8 +60,13 @@ export function isUnaryOperator(kind) {
|
|
|
60
60
|
export function isAssignmentOperator(kind) {
|
|
61
61
|
return ASSIGNMENT_OPERATORS.has(kind);
|
|
62
62
|
}
|
|
63
|
-
export const
|
|
64
|
-
export const MODIFIERS = new Set([
|
|
63
|
+
export const VISIBILITIES = new Set(['public', 'protected', 'private']);
|
|
64
|
+
export const MODIFIERS = new Set([
|
|
65
|
+
...VISIBILITIES,
|
|
66
|
+
'static',
|
|
67
|
+
'readonly',
|
|
68
|
+
'abstract',
|
|
69
|
+
]);
|
|
65
70
|
/** Words from JS that Rook spells differently. */
|
|
66
71
|
export const FOREIGN_KEYWORDS = new Map([
|
|
67
72
|
['function', 'declare functions with "func"'],
|
|
@@ -106,6 +111,7 @@ export const FUNCTION_BODY = {
|
|
|
106
111
|
inAsync: false,
|
|
107
112
|
noObjectLiteral: false,
|
|
108
113
|
inMarkup: false,
|
|
114
|
+
labels: [],
|
|
109
115
|
};
|
|
110
116
|
/** Thrown to abandon the current statement after its syntax error has been reported. */
|
|
111
117
|
export class ParseError extends Error {
|
|
@@ -118,6 +124,8 @@ export function describe(token) {
|
|
|
118
124
|
return token.text === '' ? 'newline' : '";"';
|
|
119
125
|
case 'String':
|
|
120
126
|
return 'string';
|
|
127
|
+
case 'RegExp':
|
|
128
|
+
return 'regular expression';
|
|
121
129
|
case 'Number':
|
|
122
130
|
return `number ${token.text}`;
|
|
123
131
|
case 'BigInt':
|
|
@@ -171,3 +179,9 @@ export function templateElement(token) {
|
|
|
171
179
|
end: token.end,
|
|
172
180
|
};
|
|
173
181
|
}
|
|
182
|
+
/** Why `what`, which waits, is not allowed where it is: not in an async function or in `defer`. */
|
|
183
|
+
export function cannotWait(what, inDefer) {
|
|
184
|
+
return inDefer
|
|
185
|
+
? `"${what}" is not allowed inside "defer": deferred code runs when the function ends`
|
|
186
|
+
: `"${what}" is allowed only in async functions and at the top level of a module; mark the function, e.g. "async func load() { ... }"`;
|
|
187
|
+
}
|
package/dist/parser/types.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ClassParser } from "./classes.js";
|
|
2
2
|
import { describe } from "./syntax.js";
|
|
3
|
-
// Types: `?T`, `[]T`, `queue T`, unions, literal types, function, component and object types, type arguments.
|
|
3
|
+
// Types: `?T`, `[]T`, `queue T`, `topic T`, unions, literal types, function, component and object types, type arguments.
|
|
4
4
|
/** `iterator: true` for the parameters of an iterator: one, named `yield`. */
|
|
5
5
|
function iterator(names) {
|
|
6
6
|
return names.length === 1 && names[0] === 'yield' ? { iterator: true } : {};
|
|
@@ -106,14 +106,19 @@ export class TypeParser extends ClassParser {
|
|
|
106
106
|
const members = this.parseTypeMembers();
|
|
107
107
|
return { kind: 'ObjectType', members, ...this.span(start) };
|
|
108
108
|
}
|
|
109
|
-
// `queue number`: `queue`
|
|
110
|
-
//
|
|
109
|
+
// `queue number` and `topic number`: `queue` and `topic` are types of their own only before
|
|
110
|
+
// the type of their values, so that they stay names elsewhere.
|
|
111
111
|
default: {
|
|
112
112
|
if (this.checkWord('queue') && this.canStartType(1)) {
|
|
113
113
|
this.next();
|
|
114
114
|
const element = this.parsePrimaryType();
|
|
115
115
|
return { kind: 'QueueType', element, ...this.span(start) };
|
|
116
116
|
}
|
|
117
|
+
if (this.checkWord('topic') && this.canStartType(1)) {
|
|
118
|
+
this.next();
|
|
119
|
+
const element = this.parsePrimaryType();
|
|
120
|
+
return { kind: 'TopicType', element, ...this.span(start) };
|
|
121
|
+
}
|
|
117
122
|
const name = this.parseIdentifier('type');
|
|
118
123
|
const typeArgs = this.parseTypeArgs();
|
|
119
124
|
return { kind: 'TypeReference', name, typeArgs, ...this.span(start) };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Completion } from './checker/checker.ts';
|
|
2
|
+
/** What a name is used as, and so where it is offered: decorators have a namespace of their own. */
|
|
3
|
+
export type ExportSpace = 'value' | 'type' | 'tag' | 'decorator';
|
|
4
|
+
export interface ExportedName {
|
|
5
|
+
name: string;
|
|
6
|
+
kind: Completion['kind'];
|
|
7
|
+
spaces: ExportSpace[];
|
|
8
|
+
/** The absolute path of the module. */
|
|
9
|
+
path: string;
|
|
10
|
+
}
|
|
11
|
+
/** The names exported by the other `.rk` modules of the package of `filename`. */
|
|
12
|
+
export declare function projectExports(filename: string): ExportedName[];
|