rooklang 0.0.0-stage → 0.3.1
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/LICENSE +21 -0
- package/README.md +177 -2
- package/README.ru.md +178 -0
- package/dist/ast/declarations.d.ts +185 -0
- package/dist/ast/declarations.js +1 -0
- package/dist/ast/expressions.d.ts +201 -0
- package/dist/ast/expressions.js +1 -0
- package/dist/ast/markup.d.ts +141 -0
- package/dist/ast/markup.js +1 -0
- package/dist/ast/statements.d.ts +173 -0
- package/dist/ast/statements.js +1 -0
- package/dist/ast.d.ts +18 -0
- package/dist/ast.js +18 -0
- package/dist/attrs.d.ts +8 -0
- package/dist/attrs.js +23 -0
- package/dist/browser.d.ts +30 -0
- package/dist/browser.js +36 -0
- package/dist/build.d.ts +48 -0
- package/dist/build.js +112 -0
- package/dist/checker/assignments.d.ts +14 -0
- package/dist/checker/assignments.js +197 -0
- package/dist/checker/base.d.ts +124 -0
- package/dist/checker/base.js +180 -0
- package/dist/checker/bigint.d.ts +15 -0
- package/dist/checker/bigint.js +85 -0
- package/dist/checker/builtins.d.ts +9 -0
- package/dist/checker/builtins.js +307 -0
- package/dist/checker/bytes.d.ts +11 -0
- package/dist/checker/bytes.js +72 -0
- package/dist/checker/calls.d.ts +23 -0
- package/dist/checker/calls.js +210 -0
- package/dist/checker/channel.d.ts +14 -0
- package/dist/checker/channel.js +44 -0
- package/dist/checker/channels.d.ts +9 -0
- package/dist/checker/channels.js +95 -0
- package/dist/checker/checker.d.ts +4 -0
- package/dist/checker/checker.js +22 -0
- package/dist/checker/classes.d.ts +24 -0
- package/dist/checker/classes.js +223 -0
- package/dist/checker/completion-names.d.ts +11 -0
- package/dist/checker/completion-names.js +100 -0
- package/dist/checker/completion.d.ts +25 -0
- package/dist/checker/completion.js +222 -0
- package/dist/checker/component-values.d.ts +11 -0
- package/dist/checker/component-values.js +88 -0
- package/dist/checker/components.d.ts +37 -0
- package/dist/checker/components.js +287 -0
- package/dist/checker/context.d.ts +196 -0
- package/dist/checker/context.js +35 -0
- package/dist/checker/control-flow.d.ts +24 -0
- package/dist/checker/control-flow.js +237 -0
- package/dist/checker/declarations.d.ts +26 -0
- package/dist/checker/declarations.js +259 -0
- package/dist/checker/decorator-exports.d.ts +5 -0
- package/dist/checker/decorator-exports.js +40 -0
- package/dist/checker/decorator-members.d.ts +28 -0
- package/dist/checker/decorator-members.js +140 -0
- package/dist/checker/decorator-wrappers.d.ts +20 -0
- package/dist/checker/decorator-wrappers.js +132 -0
- package/dist/checker/decorators.d.ts +28 -0
- package/dist/checker/decorators.js +265 -0
- package/dist/checker/dom-svg.d.ts +7 -0
- package/dist/checker/dom-svg.js +19 -0
- package/dist/checker/dom.d.ts +22 -0
- package/dist/checker/dom.js +361 -0
- package/dist/checker/expressions.d.ts +32 -0
- package/dist/checker/expressions.js +244 -0
- package/dist/checker/flow.d.ts +18 -0
- package/dist/checker/flow.js +134 -0
- package/dist/checker/forwarding.d.ts +15 -0
- package/dist/checker/forwarding.js +78 -0
- package/dist/checker/functions.d.ts +24 -0
- package/dist/checker/functions.js +154 -0
- package/dist/checker/go-for.d.ts +12 -0
- package/dist/checker/go-for.js +93 -0
- package/dist/checker/goroutines.d.ts +19 -0
- package/dist/checker/goroutines.js +110 -0
- package/dist/checker/helpers.d.ts +35 -0
- package/dist/checker/helpers.js +162 -0
- package/dist/checker/imports.d.ts +7 -0
- package/dist/checker/imports.js +83 -0
- package/dist/checker/initializers.d.ts +22 -0
- package/dist/checker/initializers.js +104 -0
- package/dist/checker/iterables.d.ts +5 -0
- package/dist/checker/iterables.js +17 -0
- package/dist/checker/iterators.d.ts +13 -0
- package/dist/checker/iterators.js +95 -0
- package/dist/checker/loops.d.ts +10 -0
- package/dist/checker/loops.js +82 -0
- package/dist/checker/markup.d.ts +28 -0
- package/dist/checker/markup.js +220 -0
- package/dist/checker/members.d.ts +29 -0
- package/dist/checker/members.js +233 -0
- package/dist/checker/operators.d.ts +13 -0
- package/dist/checker/operators.js +177 -0
- package/dist/checker/promise.d.ts +17 -0
- package/dist/checker/promise.js +49 -0
- package/dist/checker/refs.d.ts +26 -0
- package/dist/checker/refs.js +136 -0
- package/dist/checker/resolution.d.ts +23 -0
- package/dist/checker/resolution.js +173 -0
- package/dist/checker/statements.d.ts +28 -0
- package/dist/checker/statements.js +212 -0
- package/dist/checker/styles.d.ts +14 -0
- package/dist/checker/styles.js +49 -0
- package/dist/checker/transfer.d.ts +5 -0
- package/dist/checker/transfer.js +83 -0
- package/dist/checker/types/bytes.d.ts +4 -0
- package/dist/checker/types/bytes.js +51 -0
- package/dist/checker/types/classes.d.ts +22 -0
- package/dist/checker/types/classes.js +93 -0
- package/dist/checker/types/constructors.d.ts +27 -0
- package/dist/checker/types/constructors.js +129 -0
- package/dist/checker/types/generics.d.ts +20 -0
- package/dist/checker/types/generics.js +75 -0
- package/dist/checker/types/lazy.d.ts +30 -0
- package/dist/checker/types/lazy.js +92 -0
- package/dist/checker/types/model.d.ts +149 -0
- package/dist/checker/types/model.js +15 -0
- package/dist/checker/types/params.d.ts +7 -0
- package/dist/checker/types/params.js +220 -0
- package/dist/checker/types/printing.d.ts +6 -0
- package/dist/checker/types/printing.js +109 -0
- package/dist/checker/types/relations.d.ts +16 -0
- package/dist/checker/types/relations.js +294 -0
- package/dist/checker/types.d.ts +9 -0
- package/dist/checker/types.js +10 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +195 -0
- package/dist/codegen/analysis.d.ts +18 -0
- package/dist/codegen/analysis.js +144 -0
- package/dist/codegen/attributes.d.ts +40 -0
- package/dist/codegen/attributes.js +227 -0
- package/dist/codegen/calls.d.ts +22 -0
- package/dist/codegen/calls.js +106 -0
- package/dist/codegen/chan-helpers.d.ts +2 -0
- package/dist/codegen/chan-helpers.js +253 -0
- package/dist/codegen/component-helpers.d.ts +33 -0
- package/dist/codegen/component-helpers.js +18 -0
- package/dist/codegen/component-props.d.ts +42 -0
- package/dist/codegen/component-props.js +173 -0
- package/dist/codegen/component-values.d.ts +27 -0
- package/dist/codegen/component-values.js +146 -0
- package/dist/codegen/components.d.ts +53 -0
- package/dist/codegen/components.js +268 -0
- package/dist/codegen/control-flow.d.ts +25 -0
- package/dist/codegen/control-flow.js +152 -0
- package/dist/codegen/control-statements.d.ts +28 -0
- package/dist/codegen/control-statements.js +226 -0
- package/dist/codegen/creation.d.ts +31 -0
- package/dist/codegen/creation.js +113 -0
- package/dist/codegen/early-returns.d.ts +59 -0
- package/dist/codegen/early-returns.js +244 -0
- package/dist/codegen/element-helpers.d.ts +12 -0
- package/dist/codegen/element-helpers.js +54 -0
- package/dist/codegen/elements.d.ts +71 -0
- package/dist/codegen/elements.js +293 -0
- package/dist/codegen/emitter.d.ts +135 -0
- package/dist/codegen/emitter.js +296 -0
- package/dist/codegen/expressions.d.ts +49 -0
- package/dist/codegen/expressions.js +262 -0
- package/dist/codegen/functions.d.ts +28 -0
- package/dist/codegen/functions.js +170 -0
- package/dist/codegen/go-helpers.d.ts +9 -0
- package/dist/codegen/go-helpers.js +307 -0
- package/dist/codegen/helpers.d.ts +10 -0
- package/dist/codegen/helpers.js +356 -0
- package/dist/codegen/imported.d.ts +38 -0
- package/dist/codegen/imported.js +71 -0
- package/dist/codegen/js.d.ts +20 -0
- package/dist/codegen/js.js +113 -0
- package/dist/codegen/mutations.d.ts +10 -0
- package/dist/codegen/mutations.js +102 -0
- package/dist/codegen/parallel.d.ts +7 -0
- package/dist/codegen/parallel.js +63 -0
- package/dist/codegen/reactive.d.ts +95 -0
- package/dist/codegen/reactive.js +280 -0
- package/dist/codegen/refs.d.ts +62 -0
- package/dist/codegen/refs.js +160 -0
- package/dist/codegen/select-helpers.d.ts +2 -0
- package/dist/codegen/select-helpers.js +135 -0
- package/dist/codegen/simple-statements.d.ts +42 -0
- package/dist/codegen/simple-statements.js +253 -0
- package/dist/codegen/statements.d.ts +22 -0
- package/dist/codegen/statements.js +142 -0
- package/dist/codegen/styles.d.ts +54 -0
- package/dist/codegen/styles.js +157 -0
- package/dist/codegen/syntax.d.ts +18 -0
- package/dist/codegen/syntax.js +70 -0
- package/dist/codegen/thread-helpers.d.ts +2 -0
- package/dist/codegen/thread-helpers.js +198 -0
- package/dist/codegen/updates.d.ts +51 -0
- package/dist/codegen/updates.js +201 -0
- package/dist/codegen/web-components.d.ts +20 -0
- package/dist/codegen/web-components.js +149 -0
- package/dist/compile-file.d.ts +11 -0
- package/dist/compile-file.js +24 -0
- package/dist/compile.d.ts +53 -0
- package/dist/compile.js +187 -0
- package/dist/completion.d.ts +10 -0
- package/dist/completion.js +134 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.js +44 -0
- package/dist/css/hash.d.ts +1 -0
- package/dist/css/hash.js +10 -0
- package/dist/css/parse.d.ts +42 -0
- package/dist/css/parse.js +138 -0
- package/dist/css/print.d.ts +9 -0
- package/dist/css/print.js +35 -0
- package/dist/css/selectors.d.ts +29 -0
- package/dist/css/selectors.js +168 -0
- package/dist/declarations.d.ts +3 -0
- package/dist/declarations.js +207 -0
- package/dist/decorator-copy.d.ts +18 -0
- package/dist/decorator-copy.js +158 -0
- package/dist/decorators.d.ts +61 -0
- package/dist/decorators.js +241 -0
- package/dist/diagnostics.d.ts +17 -0
- package/dist/diagnostics.js +22 -0
- package/dist/go-for.d.ts +26 -0
- package/dist/go-for.js +155 -0
- package/dist/goroutines.d.ts +37 -0
- package/dist/goroutines.js +253 -0
- package/dist/html-tag.d.ts +21 -0
- package/dist/html-tag.js +103 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +55 -0
- package/dist/inline-packages.d.ts +32 -0
- package/dist/inline-packages.js +129 -0
- package/dist/iterators.d.ts +10 -0
- package/dist/iterators.js +33 -0
- package/dist/lexer/base.d.ts +24 -0
- package/dist/lexer/base.js +87 -0
- package/dist/lexer/lexer.d.ts +8 -0
- package/dist/lexer/lexer.js +134 -0
- package/dist/lexer/literals.d.ts +18 -0
- package/dist/lexer/literals.js +203 -0
- package/dist/lexer/markup.d.ts +10 -0
- package/dist/lexer/markup.js +93 -0
- package/dist/lexer/style.d.ts +17 -0
- package/dist/lexer/style.js +97 -0
- package/dist/lexer/tables.d.ts +53 -0
- package/dist/lexer/tables.js +101 -0
- package/dist/lexer/token.d.ts +42 -0
- package/dist/lexer/token.js +108 -0
- package/dist/loader.d.ts +1 -0
- package/dist/loader.js +50 -0
- package/dist/methods.d.ts +5 -0
- package/dist/methods.js +81 -0
- package/dist/modules.d.ts +3 -0
- package/dist/modules.js +49 -0
- package/dist/names.d.ts +23 -0
- package/dist/names.js +267 -0
- package/dist/parser/base.d.ts +96 -0
- package/dist/parser/base.js +202 -0
- package/dist/parser/classes.d.ts +8 -0
- package/dist/parser/classes.js +164 -0
- package/dist/parser/control-flow.d.ts +29 -0
- package/dist/parser/control-flow.js +232 -0
- package/dist/parser/declarations.d.ts +27 -0
- package/dist/parser/declarations.js +238 -0
- package/dist/parser/decorators.d.ts +30 -0
- package/dist/parser/decorators.js +176 -0
- package/dist/parser/expressions.d.ts +15 -0
- package/dist/parser/expressions.js +255 -0
- package/dist/parser/literals.d.ts +27 -0
- package/dist/parser/literals.js +206 -0
- package/dist/parser/markup.d.ts +22 -0
- package/dist/parser/markup.js +193 -0
- package/dist/parser/parser.d.ts +14 -0
- package/dist/parser/parser.js +22 -0
- package/dist/parser/simple-statements.d.ts +22 -0
- package/dist/parser/simple-statements.js +156 -0
- package/dist/parser/statements.d.ts +37 -0
- package/dist/parser/statements.js +267 -0
- package/dist/parser/syntax.d.ts +51 -0
- package/dist/parser/syntax.js +173 -0
- package/dist/parser/types.d.ts +23 -0
- package/dist/parser/types.js +218 -0
- package/dist/recursion.d.ts +25 -0
- package/dist/recursion.js +170 -0
- package/dist/refs.d.ts +49 -0
- package/dist/refs.js +210 -0
- package/dist/run.d.ts +1 -0
- package/dist/run.js +21 -0
- package/dist/source.d.ts +15 -0
- package/dist/source.js +34 -0
- package/dist/style-modules.d.ts +17 -0
- package/dist/style-modules.js +49 -0
- package/dist/styles.d.ts +36 -0
- package/dist/styles.js +160 -0
- package/dist/svg.d.ts +8 -0
- package/dist/svg.js +77 -0
- package/dist/ts-types.d.ts +11 -0
- package/dist/ts-types.js +133 -0
- package/dist/typed-arrays.d.ts +8 -0
- package/dist/typed-arrays.js +43 -0
- package/dist/typescript/convert-base.d.ts +42 -0
- package/dist/typescript/convert-base.js +135 -0
- package/dist/typescript/convert-objects.d.ts +28 -0
- package/dist/typescript/convert-objects.js +245 -0
- package/dist/typescript/convert.d.ts +14 -0
- package/dist/typescript/convert.js +67 -0
- package/dist/typescript/importer.d.ts +37 -0
- package/dist/typescript/importer.js +152 -0
- package/dist/typescript/library.d.ts +23 -0
- package/dist/typescript/library.js +81 -0
- package/dist/vite.d.ts +11 -0
- package/dist/vite.js +86 -0
- package/dist/walk-types.d.ts +4 -0
- package/dist/walk-types.js +56 -0
- package/dist/walk.d.ts +5 -0
- package/dist/walk.js +315 -0
- package/package.json +105 -4
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { LiteralParser } from "./literals.js";
|
|
2
|
+
import { describe, cleanJsxText, isHandlerReference } from "./syntax.js";
|
|
3
|
+
// Markup: elements, attributes, event handlers and `{...}` in markup.
|
|
4
|
+
export class MarkupParser extends LiteralParser {
|
|
5
|
+
/** `<tag attributes>children</tag>`, `<tag ... />` or a fragment `<>children</>`. */
|
|
6
|
+
parseJsxElement() {
|
|
7
|
+
// The code in markup runs again in update functions, which are not async.
|
|
8
|
+
return this.withContext({ inAsync: false }, () => this.parseMarkup());
|
|
9
|
+
}
|
|
10
|
+
parseMarkup() {
|
|
11
|
+
this.requireMarkup('markup is');
|
|
12
|
+
const start = this.expect('JsxTagOpen').start;
|
|
13
|
+
const tag = this.check('JsxTagEnd') ? null : this.parseJsxName('tag name');
|
|
14
|
+
const attributes = [];
|
|
15
|
+
while (tag && !this.check('JsxTagEnd') && !this.check('JsxSelfClose')) {
|
|
16
|
+
attributes.push(this.parseJsxAttribute());
|
|
17
|
+
}
|
|
18
|
+
const value = tag && this.tagValue(tag);
|
|
19
|
+
if (tag && this.accept('JsxSelfClose')) {
|
|
20
|
+
return {
|
|
21
|
+
kind: 'ElementExpression',
|
|
22
|
+
tag,
|
|
23
|
+
value,
|
|
24
|
+
attributes,
|
|
25
|
+
children: [],
|
|
26
|
+
...this.span(start),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
this.expect('JsxTagEnd', tag ? '">" or "/>"' : '">"');
|
|
30
|
+
const children = [];
|
|
31
|
+
while (!this.check('JsxCloseTagOpen')) {
|
|
32
|
+
const token = this.peek();
|
|
33
|
+
if (token.kind === 'JsxText') {
|
|
34
|
+
this.next();
|
|
35
|
+
const value = cleanJsxText(token.value);
|
|
36
|
+
if (value !== '')
|
|
37
|
+
children.push({ kind: 'JsxText', value, start: token.start, end: token.end });
|
|
38
|
+
}
|
|
39
|
+
else if (token.kind === 'JsxTagOpen') {
|
|
40
|
+
children.push(this.parseJsxElement());
|
|
41
|
+
}
|
|
42
|
+
else if (token.kind === '{') {
|
|
43
|
+
const container = this.parseJsxExpressionContainer();
|
|
44
|
+
if (container)
|
|
45
|
+
children.push(container);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
this.fail(`expected element content or a closing tag, found ${describe(token)}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// The closing tag must match the opening one.
|
|
52
|
+
const closeStart = this.expect('JsxCloseTagOpen').start;
|
|
53
|
+
const closing = this.check('JsxName') ? this.next().text : null;
|
|
54
|
+
if (closing !== (tag?.name ?? null)) {
|
|
55
|
+
this.failAt(`expected </${tag?.name ?? ''}> to close <${tag?.name ?? ''}>`, closeStart, this.span(closeStart).end);
|
|
56
|
+
}
|
|
57
|
+
this.expect('JsxTagEnd');
|
|
58
|
+
return { kind: 'ElementExpression', tag, value, attributes, children, ...this.span(start) };
|
|
59
|
+
}
|
|
60
|
+
/** `<section.content />`: a tag with a dot is the value `section.content`. */
|
|
61
|
+
tagValue(tag) {
|
|
62
|
+
const parts = tag.name.split('.');
|
|
63
|
+
if (parts.length === 1)
|
|
64
|
+
return null;
|
|
65
|
+
if (!parts.every((part) => /^[\p{ID_Start}$_][\p{ID_Continue}$]*$/u.test(part))) {
|
|
66
|
+
this.error(`"${tag.name}" is not a value: a tag with a dot is written like <section.content />`, tag.start, tag.end);
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
let end = tag.start - 1;
|
|
70
|
+
const identifier = (name) => {
|
|
71
|
+
const start = end + 1;
|
|
72
|
+
end = start + name.length;
|
|
73
|
+
return { kind: 'Identifier', name, start, end };
|
|
74
|
+
};
|
|
75
|
+
let value = identifier(parts[0]);
|
|
76
|
+
for (const part of parts.slice(1)) {
|
|
77
|
+
const property = identifier(part);
|
|
78
|
+
value = {
|
|
79
|
+
kind: 'MemberExpression',
|
|
80
|
+
object: value,
|
|
81
|
+
property,
|
|
82
|
+
optional: false,
|
|
83
|
+
start: tag.start,
|
|
84
|
+
end,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return value;
|
|
88
|
+
}
|
|
89
|
+
parseJsxName(what) {
|
|
90
|
+
const token = this.peek();
|
|
91
|
+
if (token.kind !== 'JsxName')
|
|
92
|
+
this.fail(`expected ${what}, found ${describe(token)}`);
|
|
93
|
+
this.next();
|
|
94
|
+
return { kind: 'Identifier', name: token.text, start: token.start, end: token.end };
|
|
95
|
+
}
|
|
96
|
+
parseJsxAttribute() {
|
|
97
|
+
const start = this.peek().start;
|
|
98
|
+
if (this.accept('{')) {
|
|
99
|
+
this.expect('...', '"..." (attributes in braces are spread: {...attrs})');
|
|
100
|
+
const argument = this.nested(() => this.parseExpression());
|
|
101
|
+
this.expect('}');
|
|
102
|
+
return { kind: 'JsxSpreadAttribute', argument, ...this.span(start) };
|
|
103
|
+
}
|
|
104
|
+
const name = this.parseJsxName('attribute name');
|
|
105
|
+
let value = null;
|
|
106
|
+
if (this.accept('=')) {
|
|
107
|
+
const token = this.peek();
|
|
108
|
+
if (token.kind === 'JsxString') {
|
|
109
|
+
this.next();
|
|
110
|
+
// Attribute strings have no escapes, so the JS literal is built from the value.
|
|
111
|
+
value = {
|
|
112
|
+
kind: 'StringLiteral',
|
|
113
|
+
value: token.value,
|
|
114
|
+
raw: JSON.stringify(token.value),
|
|
115
|
+
start: token.start,
|
|
116
|
+
end: token.end,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
else if (token.kind === '{') {
|
|
120
|
+
this.next();
|
|
121
|
+
value = /^on[A-Z]/.test(name.name)
|
|
122
|
+
? this.parseEventHandler()
|
|
123
|
+
: this.nested(() => this.parseExpression());
|
|
124
|
+
this.expect('}');
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
this.fail(`expected "..." or {...} after "${name.name}=", found ${describe(token)}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return { kind: 'JsxAttribute', name, value, ...this.span(start) };
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* The value of an `on*` attribute: a function is the handler itself; anything else is a list of
|
|
134
|
+
* statements to run on the event, e.g. `{count++}` or `{event.preventDefault(); send()}`.
|
|
135
|
+
*/
|
|
136
|
+
parseEventHandler() {
|
|
137
|
+
const start = this.peek().start;
|
|
138
|
+
const body = [];
|
|
139
|
+
this.withContext({ noObjectLiteral: false, inFunction: true, inLoop: false, inBreakable: false }, () => {
|
|
140
|
+
do {
|
|
141
|
+
if (this.check('}'))
|
|
142
|
+
break;
|
|
143
|
+
body.push(this.parseSimpleStatement());
|
|
144
|
+
} while (this.accept(';'));
|
|
145
|
+
});
|
|
146
|
+
const [only] = body;
|
|
147
|
+
if (body.length === 1 && only?.kind === 'ExpressionStatement') {
|
|
148
|
+
if (isHandlerReference(only.expression))
|
|
149
|
+
return only.expression;
|
|
150
|
+
}
|
|
151
|
+
for (const statement of body) {
|
|
152
|
+
if (statement.kind !== 'ExpressionStatement')
|
|
153
|
+
continue;
|
|
154
|
+
const { expression } = statement;
|
|
155
|
+
if (expression.kind !== 'CallExpression' && expression.kind !== 'NewExpression') {
|
|
156
|
+
this.error('this expression does nothing: only function calls can be used as statements', expression.start, expression.end);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return { kind: 'EventHandler', body, ...this.span(start) };
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* `{expression}` or `{if ...}` / `{for ...}` / `{switch ...}` among element children; `null` for
|
|
163
|
+
* `{}` or `{/* comment *\/}`.
|
|
164
|
+
*/
|
|
165
|
+
parseJsxExpressionContainer() {
|
|
166
|
+
const start = this.expect('{').start;
|
|
167
|
+
if (this.accept('}'))
|
|
168
|
+
return null;
|
|
169
|
+
if (this.check('if') || this.check('for') || this.check('switch')) {
|
|
170
|
+
// `{for todo in todos { <li>{todo.title}</li> }}`: the blocks hold markup.
|
|
171
|
+
const statement = this.withContext({ inMarkup: true, noObjectLiteral: false }, () => this.parseOrdinaryStatement());
|
|
172
|
+
this.accept(';');
|
|
173
|
+
this.expect('}');
|
|
174
|
+
return { kind: 'JsxStatementContainer', statement, ...this.span(start) };
|
|
175
|
+
}
|
|
176
|
+
const expression = this.nested(() => this.parseExpression());
|
|
177
|
+
this.expect('}');
|
|
178
|
+
return { kind: 'JsxExpressionContainer', expression, ...this.span(start) };
|
|
179
|
+
}
|
|
180
|
+
parseStringLiteral(what = 'string') {
|
|
181
|
+
const token = this.peek();
|
|
182
|
+
if (token.kind !== 'String')
|
|
183
|
+
this.fail(`expected ${what}, found ${describe(token)}`);
|
|
184
|
+
this.next();
|
|
185
|
+
return {
|
|
186
|
+
kind: 'StringLiteral',
|
|
187
|
+
value: token.value,
|
|
188
|
+
raw: token.text,
|
|
189
|
+
start: token.start,
|
|
190
|
+
end: token.end,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type * as ast from '../ast.ts';
|
|
2
|
+
import type { Diagnostic } from '../diagnostics.ts';
|
|
3
|
+
export interface ParseResult {
|
|
4
|
+
program: ast.Program;
|
|
5
|
+
/** Lexer and parser errors, sorted by position. */
|
|
6
|
+
diagnostics: Diagnostic[];
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Parses a module. Markup and components are read unless `markup` is false: the entries of the
|
|
10
|
+
* compiler (src/config.ts) turn them off unless the package turns them on.
|
|
11
|
+
*/
|
|
12
|
+
export declare function parse(text: string, options?: {
|
|
13
|
+
markup?: boolean;
|
|
14
|
+
}): ParseResult;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { tokenize } from "../lexer/lexer.js";
|
|
2
|
+
import { MarkupParser } from "./markup.js";
|
|
3
|
+
// The parser of a module; its layers are listed in base.ts.
|
|
4
|
+
class Parser extends MarkupParser {
|
|
5
|
+
parseProgram() {
|
|
6
|
+
const body = this.parseStatements(() => false);
|
|
7
|
+
return { kind: 'Program', body, start: 0, end: this.peek().end };
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Parses a module. Markup and components are read unless `markup` is false: the entries of the
|
|
12
|
+
* compiler (src/config.ts) turn them off unless the package turns them on.
|
|
13
|
+
*/
|
|
14
|
+
export function parse(text, options = {}) {
|
|
15
|
+
const { tokens, diagnostics } = tokenize(text);
|
|
16
|
+
const parser = new Parser(tokens, options.markup ?? true);
|
|
17
|
+
const program = parser.parseProgram();
|
|
18
|
+
return {
|
|
19
|
+
program,
|
|
20
|
+
diagnostics: [...diagnostics, ...parser.diagnostics].sort((a, b) => a.start - b.start),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type * as ast from '../ast.ts';
|
|
2
|
+
import { ParserBase } from './base.ts';
|
|
3
|
+
export declare abstract class SimpleStatementParser extends ParserBase {
|
|
4
|
+
/** `await`, `<-ch` and `ch <- v` wait: only in async functions and at the top level. */
|
|
5
|
+
protected requireAsync(what: string, start: number, end: number): void;
|
|
6
|
+
/** `x := 1` is a common habit from Go. */
|
|
7
|
+
protected rejectColonEquals(): void;
|
|
8
|
+
/** Statements allowed in `for` headers: declarations, assignments, `i++` and expressions. */
|
|
9
|
+
protected parseSimpleStatement(): ast.SimpleStatement;
|
|
10
|
+
protected checkAssignable(target: ast.Expression): void;
|
|
11
|
+
/**
|
|
12
|
+
* `a, b = 1, 2, 3`: the counts must match, unless a single call returns all the values, or
|
|
13
|
+
* `await` of a call or of `go` gives them. `v, ok = <-ch` tells whether the channel is closed.
|
|
14
|
+
*/
|
|
15
|
+
protected checkArity(targets: number, values: ast.Expression[], start: number): void;
|
|
16
|
+
/** `delete scores[name]`: removes a field, so the target is one. */
|
|
17
|
+
private parseDelete;
|
|
18
|
+
protected parseExpressionList(): ast.Expression[];
|
|
19
|
+
/** `ref input` is one name, and the markup gives its element. */
|
|
20
|
+
private checkRef;
|
|
21
|
+
protected parseVariableDeclaration(start: number, exported: boolean): ast.VariableDeclaration;
|
|
22
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { ParserBase } from "./base.js";
|
|
2
|
+
import { isAssignmentOperator, describe, plural } from "./syntax.js";
|
|
3
|
+
// Simple statements, which `for` headers allow too: declarations of variables, assignments, `i++`
|
|
4
|
+
// and calls.
|
|
5
|
+
export class SimpleStatementParser extends ParserBase {
|
|
6
|
+
/** `await`, `<-ch` and `ch <- v` wait: only in async functions and at the top level. */
|
|
7
|
+
requireAsync(what, start, end) {
|
|
8
|
+
if (this.context.inAsync)
|
|
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);
|
|
13
|
+
}
|
|
14
|
+
/** `x := 1` is a common habit from Go. */
|
|
15
|
+
rejectColonEquals() {
|
|
16
|
+
const colon = this.peek();
|
|
17
|
+
const equals = this.peek(1);
|
|
18
|
+
if (colon.kind === ':' && equals.kind === '=' && equals.start === colon.end) {
|
|
19
|
+
this.fail('":=" is not supported: declare variables with "let" or "const"');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** Statements allowed in `for` headers: declarations, assignments, `i++` and expressions. */
|
|
23
|
+
parseSimpleStatement() {
|
|
24
|
+
const start = this.peek().start;
|
|
25
|
+
// `delete` is a keyword only at the start of a statement, followed by what it removes.
|
|
26
|
+
if (this.checkWord('delete') && ['Identifier', 'this'].includes(this.peek(1).kind)) {
|
|
27
|
+
return this.parseDelete();
|
|
28
|
+
}
|
|
29
|
+
if (this.check('let') || this.check('const'))
|
|
30
|
+
return this.parseVariableDeclaration(start, false);
|
|
31
|
+
const first = this.parseExpression();
|
|
32
|
+
this.rejectColonEquals();
|
|
33
|
+
if (this.check(',') || isAssignmentOperator(this.peek().kind)) {
|
|
34
|
+
const targets = [first];
|
|
35
|
+
while (this.accept(','))
|
|
36
|
+
targets.push(this.parseExpression());
|
|
37
|
+
this.rejectColonEquals();
|
|
38
|
+
const operator = this.peek().kind;
|
|
39
|
+
if (!isAssignmentOperator(operator)) {
|
|
40
|
+
return this.fail(`expected "=", found ${describe(this.peek())}`);
|
|
41
|
+
}
|
|
42
|
+
this.next();
|
|
43
|
+
if (operator !== '=' && targets.length > 1) {
|
|
44
|
+
this.error(`"${operator}" works with a single variable`, start, this.span(start).end);
|
|
45
|
+
}
|
|
46
|
+
for (const target of targets)
|
|
47
|
+
this.checkAssignable(target);
|
|
48
|
+
const values = this.parseExpressionList();
|
|
49
|
+
this.checkArity(targets.length, values, start);
|
|
50
|
+
return { kind: 'AssignmentStatement', operator, targets, values, ...this.span(start) };
|
|
51
|
+
}
|
|
52
|
+
// `ch <- value`: the value goes into the channel.
|
|
53
|
+
if (this.check('<-')) {
|
|
54
|
+
const arrow = this.next();
|
|
55
|
+
this.requireAsync('<-', arrow.start, arrow.end);
|
|
56
|
+
const value = this.parseExpression();
|
|
57
|
+
return { kind: 'SendStatement', channel: first, value, ...this.span(start) };
|
|
58
|
+
}
|
|
59
|
+
if (this.check('++') || this.check('--')) {
|
|
60
|
+
const operator = this.next().kind === '++' ? '++' : '--';
|
|
61
|
+
this.checkAssignable(first);
|
|
62
|
+
return { kind: 'IncDecStatement', operator, target: first, ...this.span(start) };
|
|
63
|
+
}
|
|
64
|
+
return { kind: 'ExpressionStatement', expression: first, ...this.span(start) };
|
|
65
|
+
}
|
|
66
|
+
checkAssignable(target) {
|
|
67
|
+
// Members of decorators are read-only outside them: the checker explains it.
|
|
68
|
+
const assignable = target.kind === 'Identifier' ||
|
|
69
|
+
target.kind === 'DecoratorGet' ||
|
|
70
|
+
target.kind === 'DecoratorMember' ||
|
|
71
|
+
((target.kind === 'MemberExpression' || target.kind === 'IndexExpression') &&
|
|
72
|
+
!target.optional);
|
|
73
|
+
if (!assignable)
|
|
74
|
+
this.error('cannot assign to this expression', target.start, target.end);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* `a, b = 1, 2, 3`: the counts must match, unless a single call returns all the values, or
|
|
78
|
+
* `await` of a call or of `go` gives them. `v, ok = <-ch` tells whether the channel is closed.
|
|
79
|
+
*/
|
|
80
|
+
checkArity(targets, values, start) {
|
|
81
|
+
if (values.length === 0 || values.length === targets)
|
|
82
|
+
return;
|
|
83
|
+
const [value] = values;
|
|
84
|
+
if (targets === 2 && value?.kind === 'UnaryExpression' && value.operator === '<-')
|
|
85
|
+
return;
|
|
86
|
+
const awaited = value?.kind === 'UnaryExpression' && value.operator === 'await' ? value.argument : value;
|
|
87
|
+
const call = awaited?.kind === 'GoExpression' ? awaited.call : awaited;
|
|
88
|
+
if (values.length === 1 && call?.kind === 'CallExpression')
|
|
89
|
+
return;
|
|
90
|
+
this.error(`assignment mismatch: ${plural(targets, 'variable')} but ${plural(values.length, 'value')}`, start, this.span(start).end);
|
|
91
|
+
}
|
|
92
|
+
/** `delete scores[name]`: removes a field, so the target is one. */
|
|
93
|
+
parseDelete() {
|
|
94
|
+
const { start } = this.next();
|
|
95
|
+
const target = this.parseExpression();
|
|
96
|
+
const field = (target.kind === 'MemberExpression' || target.kind === 'IndexExpression') && !target.optional;
|
|
97
|
+
if (!field) {
|
|
98
|
+
this.error('delete removes a field: delete obj.key or delete obj[key]', start, target.end);
|
|
99
|
+
}
|
|
100
|
+
return { kind: 'DeleteStatement', target, ...this.span(start) };
|
|
101
|
+
}
|
|
102
|
+
parseExpressionList() {
|
|
103
|
+
const expressions = [this.parseExpression()];
|
|
104
|
+
while (this.accept(','))
|
|
105
|
+
expressions.push(this.parseExpression());
|
|
106
|
+
return expressions;
|
|
107
|
+
}
|
|
108
|
+
/** `ref input` is one name, and the markup gives its element. */
|
|
109
|
+
checkRef(names, valued, start, end) {
|
|
110
|
+
const name = names[0].name;
|
|
111
|
+
if (valued) {
|
|
112
|
+
this.error(`ref "${name}" gets its element from the markup, not from a value: write ref ${name} and <div ref={${name}}>`, start, end);
|
|
113
|
+
}
|
|
114
|
+
else if (names.length > 1) {
|
|
115
|
+
this.error(`ref declares one name, e.g. ref ${name}`, start, end);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
parseVariableDeclaration(start, exported) {
|
|
119
|
+
const token = this.next();
|
|
120
|
+
const keyword = token.kind === 'const'
|
|
121
|
+
? 'const'
|
|
122
|
+
: token.text === 'state' || token.text === 'derived' || token.text === 'ref'
|
|
123
|
+
? token.text
|
|
124
|
+
: 'let';
|
|
125
|
+
const names = [this.parseIdentifier('variable name')];
|
|
126
|
+
while (this.accept(','))
|
|
127
|
+
names.push(this.parseIdentifier('variable name'));
|
|
128
|
+
const type = this.canStartType() ? this.parseType() : null;
|
|
129
|
+
const values = this.accept('=') ? this.parseExpressionList() : [];
|
|
130
|
+
const { end } = this.span(start);
|
|
131
|
+
if (keyword === 'ref') {
|
|
132
|
+
this.checkRef(names, values.length > 0, start, end);
|
|
133
|
+
}
|
|
134
|
+
else if (keyword === 'derived' && (names.length !== 1 || values.length !== 1)) {
|
|
135
|
+
this.error('derived is one name and the expression it is computed from, e.g. derived double = count * 2', start, end);
|
|
136
|
+
}
|
|
137
|
+
else if (values.length === 0 && type === null) {
|
|
138
|
+
this.error(`"${names[0].name}" needs a type or a value`, start, end);
|
|
139
|
+
}
|
|
140
|
+
else if (values.length === 0 && keyword === 'const') {
|
|
141
|
+
this.error('constants need a value', start, end);
|
|
142
|
+
}
|
|
143
|
+
this.checkArity(names.length, values, start);
|
|
144
|
+
return {
|
|
145
|
+
kind: 'VariableDeclaration',
|
|
146
|
+
exported,
|
|
147
|
+
isPublic: false,
|
|
148
|
+
keyword,
|
|
149
|
+
names,
|
|
150
|
+
type,
|
|
151
|
+
values,
|
|
152
|
+
start,
|
|
153
|
+
end,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type * as ast from '../ast.ts';
|
|
2
|
+
import { SimpleStatementParser } from './simple-statements.ts';
|
|
3
|
+
export declare abstract class StatementParser extends SimpleStatementParser {
|
|
4
|
+
protected parseStatements(isEnd: () => boolean): ast.Statement[];
|
|
5
|
+
protected parseStatement(): ast.Statement;
|
|
6
|
+
protected parseOrdinaryStatement(): ast.Statement;
|
|
7
|
+
/** Parses a declaration if the current token starts one, otherwise returns `undefined`. */
|
|
8
|
+
protected parseDeclaration(start: number, exported: boolean): ast.Statement | undefined;
|
|
9
|
+
/** A statement ends with a newline or `;`, or right before the `}` that closes its block. */
|
|
10
|
+
protected endStatement(): void;
|
|
11
|
+
/**
|
|
12
|
+
* `state count = 0` or `derived double = count * 2`: `state` and `derived` are keywords only at
|
|
13
|
+
* the start of a statement in a component.
|
|
14
|
+
*/
|
|
15
|
+
protected isStateDeclaration(): boolean;
|
|
16
|
+
/** `ref input`: like `state`, a keyword only at the start of a statement in a component. */
|
|
17
|
+
protected isRefDeclaration(): boolean;
|
|
18
|
+
/** `style {`: the lexer reads the block as CSS where it starts a statement. */
|
|
19
|
+
protected isStyleBlock(): boolean;
|
|
20
|
+
/** `style { ... }`: CSS text and, in the values of declarations, `{expression}`. */
|
|
21
|
+
protected parseStyle(): ast.StyleStatement;
|
|
22
|
+
/** `effect {`: like `state`, a keyword only at the start of a statement in a component. */
|
|
23
|
+
protected isEffectDeclaration(): boolean;
|
|
24
|
+
/** `mount() {`: like `state`, a keyword only at the start of a statement in a component. */
|
|
25
|
+
protected isMountDeclaration(): boolean;
|
|
26
|
+
/** `mount() { ... }`; its body is a function that runs later, so `return` is its own. */
|
|
27
|
+
protected parseMount(): ast.MountStatement;
|
|
28
|
+
/** `effect { ... }`: its body runs again later, so `return` and `defer` are its own. */
|
|
29
|
+
protected parseEffect(): ast.EffectStatement;
|
|
30
|
+
protected parseBlock(): ast.BlockStatement;
|
|
31
|
+
protected parseFunctionBody(async?: boolean): ast.BlockStatement;
|
|
32
|
+
/** 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
|
+
protected checkSameLine(keyword: 'else' | 'catch' | 'finally'): void;
|
|
37
|
+
}
|