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,24 @@
|
|
|
1
|
+
import type { Diagnostic } from '../diagnostics.ts';
|
|
2
|
+
import { type Mode } from './tables.ts';
|
|
3
|
+
import type { StringKind, Token, TokenKind } from './token.ts';
|
|
4
|
+
export declare abstract class LexerBase {
|
|
5
|
+
protected readonly text: string;
|
|
6
|
+
protected pos: number;
|
|
7
|
+
protected readonly tokens: Token[];
|
|
8
|
+
protected readonly diagnostics: Diagnostic[];
|
|
9
|
+
protected readonly modes: Mode[];
|
|
10
|
+
constructor(text: string);
|
|
11
|
+
/** Skips whitespace and comments; returns whether a line break was skipped. */
|
|
12
|
+
protected skipTrivia(): boolean;
|
|
13
|
+
protected lastEndsStatement(): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Unlike Go, a line starting with `.` / `?.` continues a method chain, and a line starting with
|
|
16
|
+
* a closing bracket ends a list, so no semicolon is needed before it.
|
|
17
|
+
*/
|
|
18
|
+
protected nextLineContinues(): boolean;
|
|
19
|
+
protected insertSemicolon(): void;
|
|
20
|
+
protected top(): Mode | undefined;
|
|
21
|
+
protected braces(): number;
|
|
22
|
+
protected push(kind: Exclude<TokenKind, 'Number' | StringKind>, start: number, end: number): void;
|
|
23
|
+
protected error(message: string, start: number, end: number): void;
|
|
24
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { ENDS_STATEMENT, isDigit } from "./tables.js";
|
|
2
|
+
// The lexer is a chain of layers: base → literals → markup → style → Lexer (lexer.ts). This layer
|
|
3
|
+
// keeps the position and the modes (code, markup tags, markup content, CSS), skips spaces and
|
|
4
|
+
// comments, and inserts semicolons at the ends of lines.
|
|
5
|
+
export class LexerBase {
|
|
6
|
+
text;
|
|
7
|
+
pos = 0;
|
|
8
|
+
tokens = [];
|
|
9
|
+
diagnostics = [];
|
|
10
|
+
modes = [];
|
|
11
|
+
constructor(text) {
|
|
12
|
+
this.text = text;
|
|
13
|
+
}
|
|
14
|
+
/** Skips whitespace and comments; returns whether a line break was skipped. */
|
|
15
|
+
skipTrivia() {
|
|
16
|
+
const t = this.text;
|
|
17
|
+
let sawNewline = false;
|
|
18
|
+
while (this.pos < t.length) {
|
|
19
|
+
const c = t[this.pos];
|
|
20
|
+
if (c === '\n') {
|
|
21
|
+
sawNewline = true;
|
|
22
|
+
this.pos++;
|
|
23
|
+
}
|
|
24
|
+
else if (c === ' ' || c === '\t' || c === '\r' || c === '\v' || c === '\f') {
|
|
25
|
+
this.pos++;
|
|
26
|
+
}
|
|
27
|
+
else if (t.startsWith('//', this.pos)) {
|
|
28
|
+
const end = t.indexOf('\n', this.pos);
|
|
29
|
+
this.pos = end === -1 ? t.length : end;
|
|
30
|
+
}
|
|
31
|
+
else if (t.startsWith('/*', this.pos)) {
|
|
32
|
+
const end = t.indexOf('*/', this.pos + 2);
|
|
33
|
+
if (end === -1) {
|
|
34
|
+
this.error('unterminated block comment', this.pos, this.pos + 2);
|
|
35
|
+
this.pos = t.length;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
// A comment spanning several lines counts as a line break.
|
|
39
|
+
const newline = t.indexOf('\n', this.pos);
|
|
40
|
+
if (newline !== -1 && newline < end)
|
|
41
|
+
sawNewline = true;
|
|
42
|
+
this.pos = end + 2;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return sawNewline;
|
|
50
|
+
}
|
|
51
|
+
lastEndsStatement() {
|
|
52
|
+
const last = this.tokens.at(-1);
|
|
53
|
+
return last !== undefined && ENDS_STATEMENT.has(last.kind);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Unlike Go, a line starting with `.` / `?.` continues a method chain, and a line starting with
|
|
57
|
+
* a closing bracket ends a list, so no semicolon is needed before it.
|
|
58
|
+
*/
|
|
59
|
+
nextLineContinues() {
|
|
60
|
+
const t = this.text;
|
|
61
|
+
const c = t[this.pos];
|
|
62
|
+
const next = t[this.pos + 1];
|
|
63
|
+
if (c === ')' || c === ']' || c === '}')
|
|
64
|
+
return true;
|
|
65
|
+
if (c === '.')
|
|
66
|
+
return next !== '.' && !isDigit(next);
|
|
67
|
+
return c === '?' && next === '.' && !isDigit(t[this.pos + 2]);
|
|
68
|
+
}
|
|
69
|
+
insertSemicolon() {
|
|
70
|
+
const end = this.tokens.at(-1)?.end ?? 0;
|
|
71
|
+
this.tokens.push({ kind: ';', text: '', start: end, end });
|
|
72
|
+
}
|
|
73
|
+
top() {
|
|
74
|
+
return this.modes.at(-1);
|
|
75
|
+
}
|
|
76
|
+
braces() {
|
|
77
|
+
const top = this.top();
|
|
78
|
+
return top?.kind === 'template' || top?.kind === 'expression' ? top.braces : 0;
|
|
79
|
+
}
|
|
80
|
+
push(kind, start, end) {
|
|
81
|
+
this.tokens.push({ kind, text: this.text.slice(start, end), start, end });
|
|
82
|
+
this.pos = end;
|
|
83
|
+
}
|
|
84
|
+
error(message, start, end) {
|
|
85
|
+
this.diagnostics.push({ message, start, end });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Diagnostic } from '../diagnostics.ts';
|
|
2
|
+
import { type Token } from './token.ts';
|
|
3
|
+
export interface LexResult {
|
|
4
|
+
/** Always ends with an EOF token. */
|
|
5
|
+
tokens: Token[];
|
|
6
|
+
diagnostics: Diagnostic[];
|
|
7
|
+
}
|
|
8
|
+
export declare function tokenize(text: string): LexResult;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { StyleLexer } from "./style.js";
|
|
2
|
+
import { IDENTIFIER, isDigit, isMarkup } from "./tables.js";
|
|
3
|
+
import { isKeyword, isPunctuator } from "./token.js";
|
|
4
|
+
// The lexer of a module; its layers are listed in base.ts.
|
|
5
|
+
class Lexer extends StyleLexer {
|
|
6
|
+
run() {
|
|
7
|
+
if (this.text.startsWith(''))
|
|
8
|
+
this.pos = 1;
|
|
9
|
+
while (true) {
|
|
10
|
+
const mode = this.modes.at(-1);
|
|
11
|
+
if (isMarkup(mode)) {
|
|
12
|
+
// Markup has its own rules for whitespace and no automatic semicolons.
|
|
13
|
+
if (this.pos >= this.text.length)
|
|
14
|
+
break;
|
|
15
|
+
if (mode.kind === 'content')
|
|
16
|
+
this.scanContent(mode);
|
|
17
|
+
else
|
|
18
|
+
this.scanTag(mode);
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (mode?.kind === 'css') {
|
|
22
|
+
if (this.pos >= this.text.length)
|
|
23
|
+
break;
|
|
24
|
+
this.scanCss(mode);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const sawNewline = this.skipTrivia();
|
|
28
|
+
if (sawNewline && this.lastEndsStatement() && !this.nextLineContinues()) {
|
|
29
|
+
this.insertSemicolon();
|
|
30
|
+
}
|
|
31
|
+
if (this.pos >= this.text.length)
|
|
32
|
+
break;
|
|
33
|
+
this.scanToken();
|
|
34
|
+
}
|
|
35
|
+
if (this.lastEndsStatement())
|
|
36
|
+
this.insertSemicolon();
|
|
37
|
+
for (const mode of this.modes) {
|
|
38
|
+
if (mode.kind === 'template') {
|
|
39
|
+
this.error('unterminated template literal', mode.start, mode.start + 1);
|
|
40
|
+
}
|
|
41
|
+
else if (mode.kind === 'css') {
|
|
42
|
+
this.error('unterminated style block: close it with "}"', mode.start, mode.start + 5);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Nested elements are inside the first one, so it is enough to report that.
|
|
46
|
+
const element = this.modes.find(isMarkup);
|
|
47
|
+
if (element)
|
|
48
|
+
this.error('unterminated element', element.start, element.start + 1);
|
|
49
|
+
this.tokens.push({ kind: 'EOF', text: '', start: this.pos, end: this.pos });
|
|
50
|
+
this.diagnostics.sort((a, b) => a.start - b.start);
|
|
51
|
+
return { tokens: this.tokens, diagnostics: this.diagnostics };
|
|
52
|
+
}
|
|
53
|
+
scanToken() {
|
|
54
|
+
const t = this.text;
|
|
55
|
+
const start = this.pos;
|
|
56
|
+
const c = t[start];
|
|
57
|
+
if (isDigit(c) || (c === '.' && isDigit(t[start + 1]))) {
|
|
58
|
+
this.scanNumber(start);
|
|
59
|
+
}
|
|
60
|
+
else if (c === '"' || c === "'") {
|
|
61
|
+
this.scanString(start, c);
|
|
62
|
+
}
|
|
63
|
+
else if (c === '`') {
|
|
64
|
+
this.scanTemplate(start, false);
|
|
65
|
+
}
|
|
66
|
+
else if (c === '}' && this.top()?.kind === 'template' && this.braces() === 0) {
|
|
67
|
+
this.scanTemplate(start, true);
|
|
68
|
+
}
|
|
69
|
+
else if (c === '}' && this.top()?.kind === 'expression' && this.braces() === 0) {
|
|
70
|
+
// The end of `{ ... }` inside markup: back to the tag or the element content.
|
|
71
|
+
this.push('}', start, start + 1);
|
|
72
|
+
this.modes.pop();
|
|
73
|
+
}
|
|
74
|
+
else if (c === '<' && this.startsElement()) {
|
|
75
|
+
this.push('JsxTagOpen', start, start + 1);
|
|
76
|
+
this.modes.push({ kind: 'tag', start });
|
|
77
|
+
}
|
|
78
|
+
else if (!this.scanWord(start) && !this.scanPunctuator(start)) {
|
|
79
|
+
const char = String.fromCodePoint(t.codePointAt(start) ?? 0);
|
|
80
|
+
this.error(`unexpected character "${char}"`, start, start + char.length);
|
|
81
|
+
this.pos += char.length;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
scanWord(start) {
|
|
85
|
+
IDENTIFIER.lastIndex = start;
|
|
86
|
+
const word = IDENTIFIER.exec(this.text)?.[0];
|
|
87
|
+
if (word === undefined)
|
|
88
|
+
return false;
|
|
89
|
+
this.pos = start + word.length;
|
|
90
|
+
if (word.startsWith('$$')) {
|
|
91
|
+
this.error('names starting with "$$" are reserved for generated code', start, this.pos);
|
|
92
|
+
}
|
|
93
|
+
this.tokens.push({
|
|
94
|
+
kind: isKeyword(word) ? word : 'Identifier',
|
|
95
|
+
text: word,
|
|
96
|
+
start,
|
|
97
|
+
end: this.pos,
|
|
98
|
+
});
|
|
99
|
+
if (word === 'style')
|
|
100
|
+
this.startStyle(start);
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
scanPunctuator(start) {
|
|
104
|
+
const t = this.text;
|
|
105
|
+
const three = t.slice(start, start + 3);
|
|
106
|
+
if (three === '===' || three === '!==') {
|
|
107
|
+
const kind = three === '===' ? '==' : '!=';
|
|
108
|
+
this.error(`"${three}" is not needed: "${kind}" is already strict`, start, start + 3);
|
|
109
|
+
this.pos = start + 3;
|
|
110
|
+
this.tokens.push({ kind, text: three, start, end: this.pos });
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
for (let length = 4; length > 0; length--) {
|
|
114
|
+
const candidate = t.slice(start, start + length);
|
|
115
|
+
// `a ?.5 : b` is a conditional expression, not optional chaining (same rule as in JS).
|
|
116
|
+
if (!isPunctuator(candidate) || (candidate === '?.' && isDigit(t[start + 2])))
|
|
117
|
+
continue;
|
|
118
|
+
this.pos = start + length;
|
|
119
|
+
this.tokens.push({ kind: candidate, text: candidate, start, end: this.pos });
|
|
120
|
+
const top = this.top();
|
|
121
|
+
if (top?.kind === 'template' || top?.kind === 'expression') {
|
|
122
|
+
if (candidate === '{')
|
|
123
|
+
top.braces++;
|
|
124
|
+
if (candidate === '}')
|
|
125
|
+
top.braces--;
|
|
126
|
+
}
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
export function tokenize(text) {
|
|
133
|
+
return new Lexer(text).run();
|
|
134
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { LexerBase } from './base.ts';
|
|
2
|
+
import { type CharTest } from './tables.ts';
|
|
3
|
+
export declare abstract class LiteralLexer extends LexerBase {
|
|
4
|
+
protected scanNumber(start: number): void;
|
|
5
|
+
/** Consumes digits and `_` separators starting at `pos`; returns the end offset. */
|
|
6
|
+
protected scanDigits(pos: number, isValid: CharTest): number;
|
|
7
|
+
protected scanString(start: number, quote: '"' | "'"): void;
|
|
8
|
+
/**
|
|
9
|
+
* Scans template text up to the closing backtick or the next `${`. Starts either at the opening
|
|
10
|
+
* backtick or, when `continued`, at the `}` that closes a substitution.
|
|
11
|
+
*/
|
|
12
|
+
protected scanTemplate(start: number, continued: boolean): void;
|
|
13
|
+
/** Decodes the escape sequence whose backslash is at `pos`. */
|
|
14
|
+
protected scanEscape(pos: number): {
|
|
15
|
+
value: string;
|
|
16
|
+
end: number;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { LexerBase } from "./base.js";
|
|
2
|
+
import { IDENTIFIER, SIMPLE_ESCAPES, isDigit, isHexDigit, isOctalDigit, isBinaryDigit, } from "./tables.js";
|
|
3
|
+
// Numbers, strings, template strings and escapes.
|
|
4
|
+
export class LiteralLexer extends LexerBase {
|
|
5
|
+
scanNumber(start) {
|
|
6
|
+
const t = this.text;
|
|
7
|
+
let pos = start;
|
|
8
|
+
const prefix = t[pos] === '0' ? t[pos + 1]?.toLowerCase() : undefined;
|
|
9
|
+
const radixDigit = prefix === 'x'
|
|
10
|
+
? isHexDigit
|
|
11
|
+
: prefix === 'b'
|
|
12
|
+
? isBinaryDigit
|
|
13
|
+
: prefix === 'o'
|
|
14
|
+
? isOctalDigit
|
|
15
|
+
: undefined;
|
|
16
|
+
if (radixDigit) {
|
|
17
|
+
pos = this.scanDigits(pos + 2, radixDigit);
|
|
18
|
+
if (pos === start + 2) {
|
|
19
|
+
this.error(`expected digits after "${t.slice(start, pos)}"`, start, pos);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
pos = this.scanDigits(pos, isDigit);
|
|
24
|
+
if (/^0[0-9_]/.test(t.slice(start, pos))) {
|
|
25
|
+
this.error('leading zeros are not allowed (use "0o" for octal numbers)', start, pos);
|
|
26
|
+
}
|
|
27
|
+
if (t[pos] === '.' && isDigit(t[pos + 1])) {
|
|
28
|
+
pos = this.scanDigits(pos + 1, isDigit);
|
|
29
|
+
}
|
|
30
|
+
if (t[pos] === 'e' || t[pos] === 'E') {
|
|
31
|
+
const exponent = pos++;
|
|
32
|
+
if (t[pos] === '+' || t[pos] === '-')
|
|
33
|
+
pos++;
|
|
34
|
+
if (isDigit(t[pos]))
|
|
35
|
+
pos = this.scanDigits(pos, isDigit);
|
|
36
|
+
else
|
|
37
|
+
this.error('expected digits in exponent', exponent, pos);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// `123abc` is almost certainly a typo, not a number followed by a name.
|
|
41
|
+
IDENTIFIER.lastIndex = pos;
|
|
42
|
+
const suffix = IDENTIFIER.exec(t)?.[0];
|
|
43
|
+
// `10n`, `0xffn`: a bigint, a whole number of any size.
|
|
44
|
+
if (suffix === 'n') {
|
|
45
|
+
const text = t.slice(start, pos + 1);
|
|
46
|
+
if (/^\d[\d_]*[.eE]/.test(text)) {
|
|
47
|
+
this.error(`a bigint is a whole number, without a fraction or an exponent: e.g. ${text.replace(/[.eE].*/, '')}n`, start, pos + 1);
|
|
48
|
+
}
|
|
49
|
+
this.pos = pos + 1;
|
|
50
|
+
this.tokens.push({ kind: 'BigInt', text, start, end: pos + 1 });
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (suffix !== undefined) {
|
|
54
|
+
this.error('identifier cannot start immediately after a number', pos, pos + suffix.length);
|
|
55
|
+
pos += suffix.length;
|
|
56
|
+
}
|
|
57
|
+
this.pos = pos;
|
|
58
|
+
const text = t.slice(start, pos);
|
|
59
|
+
this.tokens.push({
|
|
60
|
+
kind: 'Number',
|
|
61
|
+
value: Number(text.replaceAll('_', '')),
|
|
62
|
+
text,
|
|
63
|
+
start,
|
|
64
|
+
end: pos,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/** Consumes digits and `_` separators starting at `pos`; returns the end offset. */
|
|
68
|
+
scanDigits(pos, isValid) {
|
|
69
|
+
const t = this.text;
|
|
70
|
+
while (true) {
|
|
71
|
+
if (isValid(t[pos])) {
|
|
72
|
+
pos++;
|
|
73
|
+
}
|
|
74
|
+
else if (t[pos] === '_') {
|
|
75
|
+
const run = pos;
|
|
76
|
+
while (t[pos] === '_')
|
|
77
|
+
pos++;
|
|
78
|
+
if (pos - run > 1 || !isValid(t[run - 1]) || !isValid(t[pos])) {
|
|
79
|
+
this.error('"_" can only appear between digits', run, pos);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
return pos;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
scanString(start, quote) {
|
|
88
|
+
const t = this.text;
|
|
89
|
+
let pos = start + 1;
|
|
90
|
+
let value = '';
|
|
91
|
+
while (true) {
|
|
92
|
+
const c = t[pos];
|
|
93
|
+
if (c === undefined || c === '\n' || c === '\r') {
|
|
94
|
+
this.error('unterminated string literal', start, pos);
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
if (c === quote) {
|
|
98
|
+
pos++;
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
if (c === '\\') {
|
|
102
|
+
const escape = this.scanEscape(pos);
|
|
103
|
+
value += escape.value;
|
|
104
|
+
pos = escape.end;
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
value += c;
|
|
108
|
+
pos++;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
this.pos = pos;
|
|
112
|
+
this.tokens.push({ kind: 'String', value, text: t.slice(start, pos), start, end: pos });
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Scans template text up to the closing backtick or the next `${`. Starts either at the opening
|
|
116
|
+
* backtick or, when `continued`, at the `}` that closes a substitution.
|
|
117
|
+
*/
|
|
118
|
+
scanTemplate(start, continued) {
|
|
119
|
+
const t = this.text;
|
|
120
|
+
let pos = start + 1;
|
|
121
|
+
let value = '';
|
|
122
|
+
let kind;
|
|
123
|
+
while (true) {
|
|
124
|
+
const c = t[pos];
|
|
125
|
+
if (c === undefined) {
|
|
126
|
+
const template = continued ? this.modes.pop() : undefined;
|
|
127
|
+
const templateStart = template?.kind === 'template' ? template.start : start;
|
|
128
|
+
this.error('unterminated template literal', templateStart, templateStart + 1);
|
|
129
|
+
kind = continued ? 'TemplateTail' : 'Template';
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
if (c === '`') {
|
|
133
|
+
pos++;
|
|
134
|
+
if (continued)
|
|
135
|
+
this.modes.pop();
|
|
136
|
+
kind = continued ? 'TemplateTail' : 'Template';
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
if (c === '$' && t[pos + 1] === '{') {
|
|
140
|
+
pos += 2;
|
|
141
|
+
if (!continued)
|
|
142
|
+
this.modes.push({ kind: 'template', start, braces: 0 });
|
|
143
|
+
kind = continued ? 'TemplateMiddle' : 'TemplateHead';
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
if (c === '\\') {
|
|
147
|
+
const escape = this.scanEscape(pos);
|
|
148
|
+
value += escape.value;
|
|
149
|
+
pos = escape.end;
|
|
150
|
+
}
|
|
151
|
+
else if (c === '\r') {
|
|
152
|
+
// Line breaks inside templates are normalized to \n, as in JS.
|
|
153
|
+
value += '\n';
|
|
154
|
+
pos += t[pos + 1] === '\n' ? 2 : 1;
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
value += c;
|
|
158
|
+
pos++;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
this.pos = pos;
|
|
162
|
+
this.tokens.push({ kind, value, text: t.slice(start, pos), start, end: pos });
|
|
163
|
+
}
|
|
164
|
+
/** Decodes the escape sequence whose backslash is at `pos`. */
|
|
165
|
+
scanEscape(pos) {
|
|
166
|
+
const t = this.text;
|
|
167
|
+
const c = t[pos + 1];
|
|
168
|
+
// A backslash at the end of a line or input: the caller reports the unterminated literal.
|
|
169
|
+
if (c === undefined || c === '\n' || c === '\r')
|
|
170
|
+
return { value: '', end: pos + 1 };
|
|
171
|
+
const simple = SIMPLE_ESCAPES[c];
|
|
172
|
+
if (simple !== undefined)
|
|
173
|
+
return { value: simple, end: pos + 2 };
|
|
174
|
+
if (c === 'x') {
|
|
175
|
+
const hex = t.slice(pos + 2, pos + 4);
|
|
176
|
+
if (/^[0-9a-fA-F]{2}$/.test(hex)) {
|
|
177
|
+
return { value: String.fromCharCode(parseInt(hex, 16)), end: pos + 4 };
|
|
178
|
+
}
|
|
179
|
+
this.error('expected two hex digits after "\\x"', pos, pos + 2);
|
|
180
|
+
return { value: '', end: pos + 2 };
|
|
181
|
+
}
|
|
182
|
+
if (c === 'u') {
|
|
183
|
+
if (t[pos + 2] === '{') {
|
|
184
|
+
const close = t.indexOf('}', pos + 3);
|
|
185
|
+
const hex = close === -1 ? '' : t.slice(pos + 3, close);
|
|
186
|
+
if (/^[0-9a-fA-F]{1,6}$/.test(hex) && parseInt(hex, 16) <= 0x10ffff) {
|
|
187
|
+
return { value: String.fromCodePoint(parseInt(hex, 16)), end: close + 1 };
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
const hex = t.slice(pos + 2, pos + 6);
|
|
192
|
+
if (/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
193
|
+
return { value: String.fromCharCode(parseInt(hex, 16)), end: pos + 6 };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
this.error('expected "\\uXXXX" or "\\u{X...}" with a valid code point', pos, pos + 2);
|
|
197
|
+
return { value: '', end: pos + 2 };
|
|
198
|
+
}
|
|
199
|
+
const char = String.fromCodePoint(t.codePointAt(pos + 1) ?? 0);
|
|
200
|
+
this.error(`unknown escape sequence "\\${char}"`, pos, pos + 1 + char.length);
|
|
201
|
+
return { value: char, end: pos + 1 + char.length };
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { LiteralLexer } from './literals.ts';
|
|
2
|
+
import { type MarkupMode } from './tables.ts';
|
|
3
|
+
export declare abstract class MarkupLexer extends LiteralLexer {
|
|
4
|
+
/** `<` starts markup where a value is expected and is "less than" after a value. */
|
|
5
|
+
protected startsElement(): boolean;
|
|
6
|
+
/** Inside `<tag ...>` or `</tag>`: names, `=`, attribute values, `{`, `>` and `/>`. */
|
|
7
|
+
protected scanTag(mode: MarkupMode): void;
|
|
8
|
+
/** Element content: text, `{expression}`, child elements and the closing tag. */
|
|
9
|
+
protected scanContent(mode: MarkupMode): void;
|
|
10
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { LiteralLexer } from "./literals.js";
|
|
2
|
+
import { ENDS_VALUE, JSX_NAME, decodeEntities } from "./tables.js";
|
|
3
|
+
// Markup: where an element starts, tags with their attributes, and text content.
|
|
4
|
+
export class MarkupLexer extends LiteralLexer {
|
|
5
|
+
/** `<` starts markup where a value is expected and is "less than" after a value. */
|
|
6
|
+
startsElement() {
|
|
7
|
+
const last = this.tokens.at(-1);
|
|
8
|
+
if (last && ENDS_VALUE.has(last.kind))
|
|
9
|
+
return false;
|
|
10
|
+
// `chan<number>(10)` makes a channel: its `<` opens the type of the values.
|
|
11
|
+
if (last?.kind === 'chan')
|
|
12
|
+
return false;
|
|
13
|
+
const next = this.text[this.pos + 1];
|
|
14
|
+
return next === '>' || (next !== undefined && /^[\p{ID_Start}$_]$/u.test(next));
|
|
15
|
+
}
|
|
16
|
+
/** Inside `<tag ...>` or `</tag>`: names, `=`, attribute values, `{`, `>` and `/>`. */
|
|
17
|
+
scanTag(mode) {
|
|
18
|
+
const t = this.text;
|
|
19
|
+
while (/^\s$/.test(t[this.pos] ?? ''))
|
|
20
|
+
this.pos++;
|
|
21
|
+
const start = this.pos;
|
|
22
|
+
const c = t[start];
|
|
23
|
+
if (c === undefined)
|
|
24
|
+
return;
|
|
25
|
+
const opening = mode.kind === 'tag';
|
|
26
|
+
if (c === '>') {
|
|
27
|
+
this.push('JsxTagEnd', start, start + 1);
|
|
28
|
+
// After an opening tag comes the content; a closing tag ends the element.
|
|
29
|
+
if (opening)
|
|
30
|
+
this.modes[this.modes.length - 1] = { kind: 'content', start: mode.start };
|
|
31
|
+
else
|
|
32
|
+
this.modes.pop();
|
|
33
|
+
}
|
|
34
|
+
else if (opening && c === '/' && t[start + 1] === '>') {
|
|
35
|
+
this.push('JsxSelfClose', start, start + 2);
|
|
36
|
+
this.modes.pop();
|
|
37
|
+
}
|
|
38
|
+
else if (opening && c === '{') {
|
|
39
|
+
this.push('{', start, start + 1);
|
|
40
|
+
this.modes.push({ kind: 'expression', braces: 0 });
|
|
41
|
+
}
|
|
42
|
+
else if (opening && c === '=') {
|
|
43
|
+
this.push('=', start, start + 1);
|
|
44
|
+
}
|
|
45
|
+
else if (opening && (c === '"' || c === "'")) {
|
|
46
|
+
// Attribute values are taken as written, as in HTML: no backslash escapes.
|
|
47
|
+
const close = t.indexOf(c, start + 1);
|
|
48
|
+
const end = close === -1 ? t.length : close + 1;
|
|
49
|
+
if (close === -1)
|
|
50
|
+
this.error('unterminated attribute value', start, end);
|
|
51
|
+
const text = t.slice(start, end);
|
|
52
|
+
const raw = text.slice(1, close === -1 ? undefined : -1);
|
|
53
|
+
this.tokens.push({ kind: 'JsxString', value: decodeEntities(raw), text, start, end });
|
|
54
|
+
this.pos = end;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
JSX_NAME.lastIndex = start;
|
|
58
|
+
const name = JSX_NAME.exec(t)?.[0];
|
|
59
|
+
if (name !== undefined) {
|
|
60
|
+
this.push('JsxName', start, start + name.length);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
this.error(`unexpected character "${c}" in a tag`, start, start + 1);
|
|
64
|
+
this.pos++;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** Element content: text, `{expression}`, child elements and the closing tag. */
|
|
69
|
+
scanContent(mode) {
|
|
70
|
+
const t = this.text;
|
|
71
|
+
const start = this.pos;
|
|
72
|
+
if (t.startsWith('</', start)) {
|
|
73
|
+
this.push('JsxCloseTagOpen', start, start + 2);
|
|
74
|
+
this.modes[this.modes.length - 1] = { kind: 'closingTag', start: mode.start };
|
|
75
|
+
}
|
|
76
|
+
else if (t[start] === '<') {
|
|
77
|
+
this.push('JsxTagOpen', start, start + 1);
|
|
78
|
+
this.modes.push({ kind: 'tag', start });
|
|
79
|
+
}
|
|
80
|
+
else if (t[start] === '{') {
|
|
81
|
+
this.push('{', start, start + 1);
|
|
82
|
+
this.modes.push({ kind: 'expression', braces: 0 });
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
let end = start;
|
|
86
|
+
while (end < t.length && t[end] !== '<' && t[end] !== '{')
|
|
87
|
+
end++;
|
|
88
|
+
const text = t.slice(start, end);
|
|
89
|
+
this.tokens.push({ kind: 'JsxText', value: decodeEntities(text), text, start, end });
|
|
90
|
+
this.pos = end;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { MarkupLexer } from './markup.ts';
|
|
2
|
+
import type { Mode } from './tables.ts';
|
|
3
|
+
type CssMode = Extract<Mode, {
|
|
4
|
+
kind: 'css';
|
|
5
|
+
}>;
|
|
6
|
+
export declare abstract class StyleLexer extends MarkupLexer {
|
|
7
|
+
/**
|
|
8
|
+
* `style {` at the start of a statement starts CSS: the identifier `style` has just been read at
|
|
9
|
+
* `start`. Elsewhere `style` is an ordinary name.
|
|
10
|
+
*/
|
|
11
|
+
protected startStyle(start: number): void;
|
|
12
|
+
/** CSS text up to a value from code or to the `}` that closes the block. */
|
|
13
|
+
protected scanCss(mode: CssMode): void;
|
|
14
|
+
/** The end of a CSS string that starts at `start`, after its closing quote. */
|
|
15
|
+
private cssStringEnd;
|
|
16
|
+
}
|
|
17
|
+
export {};
|