@vune-ui/compiler 0.1.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/dist/ast.d.ts +85 -0
- package/dist/ast.d.ts.map +1 -0
- package/dist/ast.js +531 -0
- package/dist/ast.js.map +1 -0
- package/dist/diagnostics.d.ts +3 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +178 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/pipeline.d.ts +3 -0
- package/dist/pipeline.d.ts.map +1 -0
- package/dist/pipeline.js +1316 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/scanner.d.ts +54 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +678 -0
- package/dist/scanner.js.map +1 -0
- package/dist/semantic.d.ts +100 -0
- package/dist/semantic.d.ts.map +1 -0
- package/dist/semantic.js +692 -0
- package/dist/semantic.js.map +1 -0
- package/dist/source-map.d.ts +26 -0
- package/dist/source-map.d.ts.map +1 -0
- package/dist/source-map.js +196 -0
- package/dist/source-map.js.map +1 -0
- package/dist/specialization.d.ts +4 -0
- package/dist/specialization.d.ts.map +1 -0
- package/dist/specialization.js +233 -0
- package/dist/specialization.js.map +1 -0
- package/dist/types.d.ts +50 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/vite.d.ts +17 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +96 -0
- package/dist/vite.js.map +1 -0
- package/package.json +29 -0
package/dist/ast.d.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/** Half-open offsets in the original Vune source. */
|
|
2
|
+
export interface VuneSourceRange {
|
|
3
|
+
readonly start: number;
|
|
4
|
+
readonly end: number;
|
|
5
|
+
}
|
|
6
|
+
export interface VuneRawExpression {
|
|
7
|
+
readonly kind: "raw";
|
|
8
|
+
readonly source: string;
|
|
9
|
+
readonly range: VuneSourceRange;
|
|
10
|
+
}
|
|
11
|
+
export interface VuneClosureExpression {
|
|
12
|
+
readonly kind: "closure";
|
|
13
|
+
readonly parameter?: string;
|
|
14
|
+
readonly bodySource: string;
|
|
15
|
+
readonly body: VuneBuilderProgram;
|
|
16
|
+
readonly range: VuneSourceRange;
|
|
17
|
+
}
|
|
18
|
+
export interface VuneArgument {
|
|
19
|
+
readonly label?: string;
|
|
20
|
+
readonly value: VuneRawExpression | VuneClosureExpression;
|
|
21
|
+
readonly range: VuneSourceRange;
|
|
22
|
+
}
|
|
23
|
+
export interface VuneCallExpression {
|
|
24
|
+
readonly kind: "call";
|
|
25
|
+
readonly callee: string;
|
|
26
|
+
readonly arguments: readonly VuneArgument[];
|
|
27
|
+
readonly trailing?: VuneClosureExpression;
|
|
28
|
+
readonly range: VuneSourceRange;
|
|
29
|
+
}
|
|
30
|
+
export interface VuneConditionalExpression {
|
|
31
|
+
readonly kind: "conditional";
|
|
32
|
+
readonly condition: VuneRawExpression;
|
|
33
|
+
readonly then: VuneBuilderProgram;
|
|
34
|
+
readonly otherwise?: VuneBuilderProgram | VuneConditionalExpression;
|
|
35
|
+
readonly range: VuneSourceRange;
|
|
36
|
+
}
|
|
37
|
+
export type VuneBuilderNode = VuneRawExpression | VuneCallExpression | VuneConditionalExpression;
|
|
38
|
+
export interface VuneBuilderProgram {
|
|
39
|
+
readonly kind: "program";
|
|
40
|
+
readonly source: string;
|
|
41
|
+
readonly range: VuneSourceRange;
|
|
42
|
+
readonly statements: readonly VuneBuilderNode[];
|
|
43
|
+
}
|
|
44
|
+
export declare function parseVuneBuilder(source: string, baseOffset?: number): VuneBuilderProgram;
|
|
45
|
+
export interface VuneStructDeclaration {
|
|
46
|
+
readonly kind: "struct";
|
|
47
|
+
readonly name: string;
|
|
48
|
+
readonly genericParameters?: string;
|
|
49
|
+
readonly source: string;
|
|
50
|
+
readonly bodySource: string;
|
|
51
|
+
readonly bodyExpressionSource: string;
|
|
52
|
+
readonly range: VuneSourceRange;
|
|
53
|
+
readonly bodyRange: VuneSourceRange;
|
|
54
|
+
readonly bodyExpressionRange: VuneSourceRange;
|
|
55
|
+
readonly fields: readonly VuneStructField[];
|
|
56
|
+
readonly initializers: readonly VuneStructInitializer[];
|
|
57
|
+
readonly nested?: readonly VuneStructDeclaration[];
|
|
58
|
+
}
|
|
59
|
+
export interface VuneStructField {
|
|
60
|
+
readonly name: string;
|
|
61
|
+
readonly kind: "stored" | "state" | "binding";
|
|
62
|
+
readonly type?: string;
|
|
63
|
+
readonly initializer?: string;
|
|
64
|
+
readonly range: VuneSourceRange;
|
|
65
|
+
}
|
|
66
|
+
export interface VuneStructInitializer {
|
|
67
|
+
readonly parametersSource: string;
|
|
68
|
+
readonly bodySource: string;
|
|
69
|
+
readonly range: VuneSourceRange;
|
|
70
|
+
readonly parametersRange: VuneSourceRange;
|
|
71
|
+
readonly bodyRange: VuneSourceRange;
|
|
72
|
+
}
|
|
73
|
+
export declare function parseVuneStructs(source: string, baseOffset?: number): readonly VuneStructDeclaration[];
|
|
74
|
+
export interface VuneAstLowering {
|
|
75
|
+
readonly transformRaw: (source: string) => string;
|
|
76
|
+
readonly transformArgument?: (source: string, call: VuneCallExpression, argumentIndex: number, label?: string) => string;
|
|
77
|
+
readonly closure: (bodySource: string, parameter?: string, role?: "value" | "viewBuilder" | "action") => string;
|
|
78
|
+
readonly closureRole?: (call: VuneCallExpression, context: {
|
|
79
|
+
readonly position: "argument" | "trailing";
|
|
80
|
+
readonly argumentIndex?: number;
|
|
81
|
+
readonly label?: string;
|
|
82
|
+
}) => "value" | "viewBuilder" | "action" | undefined;
|
|
83
|
+
}
|
|
84
|
+
export declare function lowerVuneBuilderAst(program: VuneBuilderProgram, lowering: VuneAstLowering): string[];
|
|
85
|
+
//# sourceMappingURL=ast.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAA;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAA;CAChC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAA;IACjC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAA;CAChC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,iBAAiB,GAAG,qBAAqB,CAAA;IACzD,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAA;CAChC;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,SAAS,EAAE,SAAS,YAAY,EAAE,CAAA;IAC3C,QAAQ,CAAC,QAAQ,CAAC,EAAE,qBAAqB,CAAA;IACzC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAA;CAChC;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;IAC5B,QAAQ,CAAC,SAAS,EAAE,iBAAiB,CAAA;IACrC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAA;IACjC,QAAQ,CAAC,SAAS,CAAC,EAAE,kBAAkB,GAAG,yBAAyB,CAAA;IACnE,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAA;CAChC;AAED,MAAM,MAAM,eAAe,GAAG,iBAAiB,GAAG,kBAAkB,GAAG,yBAAyB,CAAA;AAEhG,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAA;IAC/B,QAAQ,CAAC,UAAU,EAAE,SAAS,eAAe,EAAE,CAAA;CAChD;AAuPD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,SAAI,GAAG,kBAAkB,CAEnF;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAA;IACrC,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAA;IAC/B,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAA;IACnC,QAAQ,CAAC,mBAAmB,EAAE,eAAe,CAAA;IAC7C,QAAQ,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,CAAA;IAC3C,QAAQ,CAAC,YAAY,EAAE,SAAS,qBAAqB,EAAE,CAAA;IACvD,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAA;CACnD;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAA;IAC7C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAA;CAChC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAA;IAC/B,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAA;IACzC,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAA;CACpC;AAiID,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,SAAI,GAAG,SAAS,qBAAqB,EAAE,CAiCjG;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAA;IACjD,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IACxH,QAAQ,CAAC,OAAO,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,aAAa,GAAG,QAAQ,KAAK,MAAM,CAAA;IAC/G,QAAQ,CAAC,WAAW,CAAC,EAAE,CACrB,IAAI,EAAE,kBAAkB,EACxB,OAAO,EAAE;QAAE,QAAQ,CAAC,QAAQ,EAAE,UAAU,GAAG,UAAU,CAAC;QAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,KAC9G,OAAO,GAAG,aAAa,GAAG,QAAQ,GAAG,SAAS,CAAA;CACpD;AAiCD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,eAAe,GAAG,MAAM,EAAE,CAEpG"}
|
package/dist/ast.js
ADDED
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
const closing = { "(": ")", "{": "}", "[": "]" };
|
|
2
|
+
function syntaxError(message, offset) {
|
|
3
|
+
const error = new SyntaxError(message);
|
|
4
|
+
error.offset = offset;
|
|
5
|
+
return error;
|
|
6
|
+
}
|
|
7
|
+
function identifierPart(value) {
|
|
8
|
+
return !!value && /[A-Za-z0-9_$]/.test(value);
|
|
9
|
+
}
|
|
10
|
+
function skipQuoted(source, index) {
|
|
11
|
+
const quote = source[index];
|
|
12
|
+
for (let cursor = index + 1; cursor < source.length; cursor += 1) {
|
|
13
|
+
if (source[cursor] === "\\") {
|
|
14
|
+
cursor += 1;
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
if (source[cursor] === quote)
|
|
18
|
+
return cursor + 1;
|
|
19
|
+
}
|
|
20
|
+
throw syntaxError(`Unclosed ${quote} string in Vune source`, index);
|
|
21
|
+
}
|
|
22
|
+
function skipTemplate(source, index) {
|
|
23
|
+
for (let cursor = index + 1; cursor < source.length; cursor += 1) {
|
|
24
|
+
if (source[cursor] === "\\") {
|
|
25
|
+
cursor += 1;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (source[cursor] === "`")
|
|
29
|
+
return cursor + 1;
|
|
30
|
+
if (source[cursor] !== "$" || source[cursor + 1] !== "{")
|
|
31
|
+
continue;
|
|
32
|
+
const close = findMatching(source, cursor + 1, "{");
|
|
33
|
+
cursor = close;
|
|
34
|
+
}
|
|
35
|
+
throw syntaxError("Unclosed template literal in Vune source", index);
|
|
36
|
+
}
|
|
37
|
+
function skipTrivia(source, index) {
|
|
38
|
+
let cursor = index;
|
|
39
|
+
while (cursor < source.length) {
|
|
40
|
+
if (/\s/.test(source[cursor])) {
|
|
41
|
+
cursor += 1;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (source.startsWith("//", cursor) || source.startsWith("/*", cursor)) {
|
|
45
|
+
cursor = skipComment(source, cursor);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
return cursor;
|
|
51
|
+
}
|
|
52
|
+
function skipComment(source, index) {
|
|
53
|
+
if (source.startsWith("//", index)) {
|
|
54
|
+
const end = source.indexOf("\n", index + 2);
|
|
55
|
+
return end < 0 ? source.length : end;
|
|
56
|
+
}
|
|
57
|
+
const end = source.indexOf("*/", index + 2);
|
|
58
|
+
if (end < 0)
|
|
59
|
+
throw syntaxError("Unclosed block comment in Vune source", index);
|
|
60
|
+
return end + 2;
|
|
61
|
+
}
|
|
62
|
+
function regexCanStart(source, index) {
|
|
63
|
+
for (let cursor = index - 1; cursor >= 0; cursor -= 1) {
|
|
64
|
+
if (/\s/.test(source[cursor]))
|
|
65
|
+
continue;
|
|
66
|
+
return "([{=,:;!?&|+-*%^~<>".includes(source[cursor]);
|
|
67
|
+
}
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
function skipRegex(source, index) {
|
|
71
|
+
let inClass = false;
|
|
72
|
+
for (let cursor = index + 1; cursor < source.length; cursor += 1) {
|
|
73
|
+
if (source[cursor] === "\\") {
|
|
74
|
+
cursor += 1;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (source[cursor] === "[")
|
|
78
|
+
inClass = true;
|
|
79
|
+
if (source[cursor] === "]")
|
|
80
|
+
inClass = false;
|
|
81
|
+
if (source[cursor] === "/" && !inClass) {
|
|
82
|
+
cursor += 1;
|
|
83
|
+
while (/[A-Za-z]/.test(source[cursor] ?? ""))
|
|
84
|
+
cursor += 1;
|
|
85
|
+
return cursor;
|
|
86
|
+
}
|
|
87
|
+
if (source[cursor] === "\n")
|
|
88
|
+
return index + 1;
|
|
89
|
+
}
|
|
90
|
+
return index + 1;
|
|
91
|
+
}
|
|
92
|
+
function findMatching(source, openIndex, open) {
|
|
93
|
+
const stack = [open];
|
|
94
|
+
for (let cursor = openIndex + 1; cursor < source.length; cursor += 1) {
|
|
95
|
+
const character = source[cursor];
|
|
96
|
+
const next = source[cursor + 1];
|
|
97
|
+
if (character === "\"" || character === "'") {
|
|
98
|
+
cursor = skipQuoted(source, cursor) - 1;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (character === "`") {
|
|
102
|
+
cursor = skipTemplate(source, cursor) - 1;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (character === "/" && (next === "/" || next === "*")) {
|
|
106
|
+
cursor = skipComment(source, cursor) - 1;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (character === "/" && regexCanStart(source, cursor)) {
|
|
110
|
+
cursor = skipRegex(source, cursor) - 1;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (character === "(" || character === "{" || character === "[") {
|
|
114
|
+
stack.push(character);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (character === ")" || character === "}" || character === "]") {
|
|
118
|
+
const expected = closing[stack[stack.length - 1]];
|
|
119
|
+
if (character !== expected)
|
|
120
|
+
throw syntaxError(`Unexpected ${character} in Vune source`, cursor);
|
|
121
|
+
stack.pop();
|
|
122
|
+
if (stack.length === 0)
|
|
123
|
+
return cursor;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
throw syntaxError(`Unclosed ${open} block in Vune source`, openIndex);
|
|
127
|
+
}
|
|
128
|
+
function lineBreakBoundary(source, index) {
|
|
129
|
+
const previous = source.slice(0, index).trimEnd().at(-1);
|
|
130
|
+
const nextIndex = skipTrivia(source, index + 1);
|
|
131
|
+
const next = source[nextIndex];
|
|
132
|
+
if (!previous || !next)
|
|
133
|
+
return true;
|
|
134
|
+
if (source.slice(nextIndex, nextIndex + 4) === "else")
|
|
135
|
+
return false;
|
|
136
|
+
if (",([{.=:+-*/%!&|?<>".includes(previous))
|
|
137
|
+
return false;
|
|
138
|
+
if (".),]}:;".includes(next))
|
|
139
|
+
return false;
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
function splitTopLevel(source, separators, baseOffset) {
|
|
143
|
+
const parts = [];
|
|
144
|
+
let start = 0;
|
|
145
|
+
const stack = [];
|
|
146
|
+
for (let cursor = 0; cursor < source.length; cursor += 1) {
|
|
147
|
+
const character = source[cursor];
|
|
148
|
+
const next = source[cursor + 1];
|
|
149
|
+
if (character === "\"" || character === "'") {
|
|
150
|
+
cursor = skipQuoted(source, cursor) - 1;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (character === "`") {
|
|
154
|
+
cursor = skipTemplate(source, cursor) - 1;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (character === "/" && (next === "/" || next === "*")) {
|
|
158
|
+
cursor = skipComment(source, cursor) - 1;
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (character === "/" && regexCanStart(source, cursor)) {
|
|
162
|
+
cursor = skipRegex(source, cursor) - 1;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (character === "(" || character === "{" || character === "[")
|
|
166
|
+
stack.push(character);
|
|
167
|
+
else if (character === ")" || character === "}" || character === "]")
|
|
168
|
+
stack.pop();
|
|
169
|
+
const boundary = stack.length === 0 && (separators.includes(character) || (character === "\n" && lineBreakBoundary(source, cursor)));
|
|
170
|
+
if (boundary) {
|
|
171
|
+
parts.push({ source: source.slice(start, cursor), start: baseOffset + start, end: baseOffset + cursor });
|
|
172
|
+
start = cursor + 1;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
parts.push({ source: source.slice(start), start: baseOffset + start, end: baseOffset + source.length });
|
|
176
|
+
return parts.filter(part => part.source.trim().length > 0);
|
|
177
|
+
}
|
|
178
|
+
function trimSlice(slice) {
|
|
179
|
+
const leading = slice.source.search(/\S|$/);
|
|
180
|
+
const trimmed = slice.source.trimEnd();
|
|
181
|
+
return { source: trimmed.slice(leading), start: slice.start + leading, end: slice.start + trimmed.length };
|
|
182
|
+
}
|
|
183
|
+
function raw(slice) {
|
|
184
|
+
const value = trimSlice(slice);
|
|
185
|
+
return { kind: "raw", source: value.source, range: { start: value.start, end: value.end } };
|
|
186
|
+
}
|
|
187
|
+
function parseClosure(slice) {
|
|
188
|
+
const value = trimSlice(slice);
|
|
189
|
+
if (value.source[0] !== "{")
|
|
190
|
+
return undefined;
|
|
191
|
+
const close = findMatching(value.source, 0, "{");
|
|
192
|
+
if (close !== value.source.length - 1)
|
|
193
|
+
return undefined;
|
|
194
|
+
const inner = value.source.slice(1, close);
|
|
195
|
+
const parameter = /^\s*([A-Za-z_$][A-Za-z0-9_$]*)\s+in\s+/.exec(inner);
|
|
196
|
+
// A direct object argument is not a Vune closure. Check the first member
|
|
197
|
+
// rather than any colon: closure bodies legitimately contain ternaries.
|
|
198
|
+
if (!parameter && /^(?:\s*(?:[A-Za-z_$][A-Za-z0-9_$]*|["'][^"']*["']|-?\d+(?:\.\d+)?)\s*:)/.test(inner))
|
|
199
|
+
return undefined;
|
|
200
|
+
const bodySource = parameter ? inner.slice(parameter[0].length) : inner;
|
|
201
|
+
const bodyOffset = value.start + 1 + (parameter ? parameter[0].length : 0);
|
|
202
|
+
return {
|
|
203
|
+
kind: "closure",
|
|
204
|
+
parameter: parameter?.[1],
|
|
205
|
+
bodySource,
|
|
206
|
+
body: parseVuneBuilder(bodySource, bodyOffset),
|
|
207
|
+
range: { start: value.start, end: value.end },
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function topLevelColon(source) {
|
|
211
|
+
const slices = splitTopLevel(source, ":", 0);
|
|
212
|
+
return slices.length > 1 ? slices[0].source.length : -1;
|
|
213
|
+
}
|
|
214
|
+
function parseArgument(slice) {
|
|
215
|
+
const value = trimSlice(slice);
|
|
216
|
+
const colon = topLevelColon(value.source);
|
|
217
|
+
const labelStart = skipTrivia(value.source, 0);
|
|
218
|
+
const label = colon < 0 ? undefined : value.source.slice(labelStart, colon).trim();
|
|
219
|
+
if (label && !/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(label))
|
|
220
|
+
return { value: raw(value), range: { start: value.start, end: value.end } };
|
|
221
|
+
const valueSlice = colon < 0
|
|
222
|
+
? value
|
|
223
|
+
: { source: value.source.slice(colon + 1), start: value.start + colon + 1, end: value.end };
|
|
224
|
+
return { label, value: parseClosure(valueSlice) ?? raw(valueSlice), range: { start: value.start, end: value.end } };
|
|
225
|
+
}
|
|
226
|
+
function parseCall(slice) {
|
|
227
|
+
const value = trimSlice(slice);
|
|
228
|
+
const identifier = /^([A-Za-z_$][A-Za-z0-9_$]*)/.exec(value.source);
|
|
229
|
+
if (!identifier)
|
|
230
|
+
return undefined;
|
|
231
|
+
const open = skipTrivia(value.source, identifier[0].length);
|
|
232
|
+
if (value.source[open] !== "(")
|
|
233
|
+
return undefined;
|
|
234
|
+
const close = findMatching(value.source, open, "(");
|
|
235
|
+
const afterClose = skipTrivia(value.source, close + 1);
|
|
236
|
+
let trailing;
|
|
237
|
+
let end = close + 1;
|
|
238
|
+
if (value.source[afterClose] === "{") {
|
|
239
|
+
const trailingClose = findMatching(value.source, afterClose, "{");
|
|
240
|
+
if (skipTrivia(value.source, trailingClose + 1) !== value.source.length)
|
|
241
|
+
return undefined;
|
|
242
|
+
trailing = parseClosure({ source: value.source.slice(afterClose, trailingClose + 1), start: value.start + afterClose, end: value.start + trailingClose + 1 });
|
|
243
|
+
end = trailingClose + 1;
|
|
244
|
+
}
|
|
245
|
+
if (skipTrivia(value.source, end) !== value.source.length)
|
|
246
|
+
return undefined;
|
|
247
|
+
return {
|
|
248
|
+
kind: "call",
|
|
249
|
+
callee: identifier[1],
|
|
250
|
+
arguments: splitTopLevel(value.source.slice(open + 1, close), ",", value.start + open + 1).map(parseArgument),
|
|
251
|
+
trailing,
|
|
252
|
+
range: { start: value.start, end: value.end },
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
function parseConditional(slice) {
|
|
256
|
+
const value = trimSlice(slice);
|
|
257
|
+
if (!/^if\b/.test(value.source))
|
|
258
|
+
return undefined;
|
|
259
|
+
const open = value.source.indexOf("(");
|
|
260
|
+
if (open < 0)
|
|
261
|
+
return undefined;
|
|
262
|
+
const close = findMatching(value.source, open, "(");
|
|
263
|
+
const thenOpen = skipTrivia(value.source, close + 1);
|
|
264
|
+
if (value.source[thenOpen] !== "{")
|
|
265
|
+
return undefined;
|
|
266
|
+
const thenClose = findMatching(value.source, thenOpen, "{");
|
|
267
|
+
const afterThen = skipTrivia(value.source, thenClose + 1);
|
|
268
|
+
const condition = raw({ source: value.source.slice(open + 1, close), start: value.start + open + 1, end: value.start + close });
|
|
269
|
+
let otherwise;
|
|
270
|
+
if (value.source.slice(afterThen, afterThen + 4) === "else") {
|
|
271
|
+
const elseStart = skipTrivia(value.source, afterThen + 4);
|
|
272
|
+
const rest = { source: value.source.slice(elseStart), start: value.start + elseStart, end: value.end };
|
|
273
|
+
if (/^if\b/.test(rest.source))
|
|
274
|
+
otherwise = parseConditional(rest);
|
|
275
|
+
else if (rest.source[0] === "{") {
|
|
276
|
+
const elseClose = findMatching(rest.source, 0, "{");
|
|
277
|
+
if (skipTrivia(rest.source, elseClose + 1) !== rest.source.length)
|
|
278
|
+
return undefined;
|
|
279
|
+
otherwise = parseVuneBuilder(rest.source.slice(1, elseClose), rest.start + 1);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
kind: "conditional",
|
|
284
|
+
condition,
|
|
285
|
+
then: parseVuneBuilder(value.source.slice(thenOpen + 1, thenClose), value.start + thenOpen + 1),
|
|
286
|
+
otherwise,
|
|
287
|
+
range: { start: value.start, end: value.end },
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
function parseNode(slice) {
|
|
291
|
+
return parseConditional(slice) ?? parseCall(slice) ?? raw(slice);
|
|
292
|
+
}
|
|
293
|
+
export function parseVuneBuilder(source, baseOffset = 0) {
|
|
294
|
+
return { kind: "program", source, range: { start: baseOffset, end: baseOffset + source.length }, statements: splitTopLevel(source, ",;", baseOffset).map(parseNode) };
|
|
295
|
+
}
|
|
296
|
+
function findStructs(source, start) {
|
|
297
|
+
for (let cursor = start; cursor < source.length; cursor += 1) {
|
|
298
|
+
const character = source[cursor];
|
|
299
|
+
const next = source[cursor + 1];
|
|
300
|
+
if (character === "\"" || character === "'") {
|
|
301
|
+
cursor = skipQuoted(source, cursor) - 1;
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
if (character === "`") {
|
|
305
|
+
cursor = skipTemplate(source, cursor) - 1;
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
if (character === "/" && (next === "/" || next === "*")) {
|
|
309
|
+
cursor = skipComment(source, cursor) - 1;
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
if (character === "/" && regexCanStart(source, cursor)) {
|
|
313
|
+
cursor = skipRegex(source, cursor) - 1;
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
if (!source.startsWith("struct", cursor))
|
|
317
|
+
continue;
|
|
318
|
+
if (identifierPart(source[cursor - 1]) || identifierPart(source[cursor + 6]))
|
|
319
|
+
continue;
|
|
320
|
+
return cursor;
|
|
321
|
+
}
|
|
322
|
+
return -1;
|
|
323
|
+
}
|
|
324
|
+
function findTopLevelBodyExpression(source) {
|
|
325
|
+
let braces = 0;
|
|
326
|
+
for (let cursor = 0; cursor < source.length; cursor += 1) {
|
|
327
|
+
const character = source[cursor];
|
|
328
|
+
const next = source[cursor + 1];
|
|
329
|
+
if (character === "\"" || character === "'") {
|
|
330
|
+
cursor = skipQuoted(source, cursor) - 1;
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
if (character === "`") {
|
|
334
|
+
cursor = skipTemplate(source, cursor) - 1;
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
if (character === "/" && (next === "/" || next === "*")) {
|
|
338
|
+
cursor = skipComment(source, cursor) - 1;
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
if (character === "/" && regexCanStart(source, cursor)) {
|
|
342
|
+
cursor = skipRegex(source, cursor) - 1;
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
if (braces === 0 && source.startsWith("var", cursor) && !identifierPart(source[cursor - 1]) && !identifierPart(source[cursor + 3])) {
|
|
346
|
+
const match = /^var\s+body\s*:[^{]*\{/.exec(source.slice(cursor));
|
|
347
|
+
if (match) {
|
|
348
|
+
const open = cursor + match[0].lastIndexOf("{");
|
|
349
|
+
return { declarationStart: cursor, open, close: findMatching(source, open, "{") };
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
if (character === "{")
|
|
353
|
+
braces += 1;
|
|
354
|
+
else if (character === "}")
|
|
355
|
+
braces -= 1;
|
|
356
|
+
}
|
|
357
|
+
return undefined;
|
|
358
|
+
}
|
|
359
|
+
function maskNestedStructs(source) {
|
|
360
|
+
const characters = [...source];
|
|
361
|
+
let cursor = 0;
|
|
362
|
+
while (cursor < source.length) {
|
|
363
|
+
const index = findStructs(source, cursor);
|
|
364
|
+
if (index < 0)
|
|
365
|
+
break;
|
|
366
|
+
const header = /^struct\s+([A-Za-z_$][A-Za-z0-9_$]*)(?:\s*<([^>{}]*)>)?\s*:\s*View\b/.exec(source.slice(index));
|
|
367
|
+
if (!header) {
|
|
368
|
+
cursor = index + 6;
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
const brace = source.indexOf("{", index + header[0].length);
|
|
372
|
+
if (brace < 0)
|
|
373
|
+
break;
|
|
374
|
+
const close = findMatching(source, brace, "{");
|
|
375
|
+
for (let position = index; position <= close; position += 1) {
|
|
376
|
+
if (characters[position] !== "\n" && characters[position] !== "\r")
|
|
377
|
+
characters[position] = " ";
|
|
378
|
+
}
|
|
379
|
+
cursor = close + 1;
|
|
380
|
+
}
|
|
381
|
+
return characters.join("");
|
|
382
|
+
}
|
|
383
|
+
function maskInitializerBodies(source) {
|
|
384
|
+
const masked = [...source];
|
|
385
|
+
let cursor = 0;
|
|
386
|
+
while (true) {
|
|
387
|
+
const initializer = findInitializer(source, cursor);
|
|
388
|
+
if (initializer < 0)
|
|
389
|
+
break;
|
|
390
|
+
const open = skipTrivia(source, initializer + 4);
|
|
391
|
+
const close = findMatching(source, open, "(");
|
|
392
|
+
const blockOpen = skipTrivia(source, close + 1);
|
|
393
|
+
if (source[blockOpen] !== "{") {
|
|
394
|
+
cursor = initializer + 4;
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
const blockClose = findMatching(source, blockOpen, "{");
|
|
398
|
+
for (let index = initializer; index <= blockClose; index += 1) {
|
|
399
|
+
if (masked[index] !== "\n" && masked[index] !== "\r")
|
|
400
|
+
masked[index] = " ";
|
|
401
|
+
}
|
|
402
|
+
cursor = blockClose + 1;
|
|
403
|
+
}
|
|
404
|
+
return masked.join("");
|
|
405
|
+
}
|
|
406
|
+
function findInitializer(source, start) {
|
|
407
|
+
for (let cursor = start; cursor < source.length; cursor += 1) {
|
|
408
|
+
const character = source[cursor];
|
|
409
|
+
const next = source[cursor + 1];
|
|
410
|
+
if (character === "\"" || character === "'") {
|
|
411
|
+
cursor = skipQuoted(source, cursor) - 1;
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
if (character === "`") {
|
|
415
|
+
cursor = skipTemplate(source, cursor) - 1;
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
if (character === "/" && (next === "/" || next === "*")) {
|
|
419
|
+
cursor = skipComment(source, cursor) - 1;
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
if (character === "/" && regexCanStart(source, cursor)) {
|
|
423
|
+
cursor = skipRegex(source, cursor) - 1;
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
if (!source.startsWith("init", cursor) || identifierPart(source[cursor - 1]) || identifierPart(source[cursor + 4]))
|
|
427
|
+
continue;
|
|
428
|
+
if (source[skipTrivia(source, cursor + 4)] === "(")
|
|
429
|
+
return cursor;
|
|
430
|
+
}
|
|
431
|
+
return -1;
|
|
432
|
+
}
|
|
433
|
+
function parseStructMembers(body, baseOffset) {
|
|
434
|
+
const fields = [];
|
|
435
|
+
const initializers = [];
|
|
436
|
+
const nestedMaskedBody = maskNestedStructs(body);
|
|
437
|
+
const maskedBody = maskInitializerBodies(nestedMaskedBody);
|
|
438
|
+
const fieldPattern = /(?:^|[;\n])\s*(?:(@State|@Binding)\s+)?(?:let|var)\s+([A-Za-z_$][A-Za-z0-9_$]*)(?:\s*:\s*([^=\n;]+))?(?:\s*=\s*([^\n;]+))?/g;
|
|
439
|
+
for (const match of maskedBody.matchAll(fieldPattern)) {
|
|
440
|
+
if (match[2] === "body")
|
|
441
|
+
continue;
|
|
442
|
+
const start = baseOffset + (match.index ?? 0) + match[0].indexOf(match[2]);
|
|
443
|
+
fields.push({ name: match[2], kind: match[1] === "@State" ? "state" : match[1] === "@Binding" ? "binding" : "stored", type: match[3]?.trim(), initializer: match[4]?.trim(), range: { start, end: start + match[2].length } });
|
|
444
|
+
}
|
|
445
|
+
let cursor = 0;
|
|
446
|
+
while (true) {
|
|
447
|
+
const initializer = findInitializer(nestedMaskedBody, cursor);
|
|
448
|
+
if (initializer < 0)
|
|
449
|
+
break;
|
|
450
|
+
const open = skipTrivia(nestedMaskedBody, initializer + 4);
|
|
451
|
+
const close = findMatching(nestedMaskedBody, open, "(");
|
|
452
|
+
const blockOpen = skipTrivia(nestedMaskedBody, close + 1);
|
|
453
|
+
if (nestedMaskedBody[blockOpen] !== "{") {
|
|
454
|
+
cursor = initializer + 4;
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
const blockClose = findMatching(nestedMaskedBody, blockOpen, "{");
|
|
458
|
+
initializers.push({
|
|
459
|
+
parametersSource: body.slice(open + 1, close),
|
|
460
|
+
bodySource: body.slice(blockOpen + 1, blockClose),
|
|
461
|
+
range: { start: baseOffset + initializer, end: baseOffset + blockClose + 1 },
|
|
462
|
+
parametersRange: { start: baseOffset + open + 1, end: baseOffset + close },
|
|
463
|
+
bodyRange: { start: baseOffset + blockOpen + 1, end: baseOffset + blockClose },
|
|
464
|
+
});
|
|
465
|
+
cursor = blockClose + 1;
|
|
466
|
+
}
|
|
467
|
+
return { fields, initializers };
|
|
468
|
+
}
|
|
469
|
+
export function parseVuneStructs(source, baseOffset = 0) {
|
|
470
|
+
const declarations = [];
|
|
471
|
+
let cursor = 0;
|
|
472
|
+
while (cursor < source.length) {
|
|
473
|
+
const index = findStructs(source, cursor);
|
|
474
|
+
if (index < 0)
|
|
475
|
+
break;
|
|
476
|
+
const header = /^struct\s+([A-Za-z_$][A-Za-z0-9_$]*)(?:\s*<([^>{}]*)>)?\s*:\s*View\b/.exec(source.slice(index));
|
|
477
|
+
if (!header) {
|
|
478
|
+
cursor = index + 6;
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
const brace = source.indexOf("{", index + header[0].length);
|
|
482
|
+
if (brace < 0)
|
|
483
|
+
throw syntaxError(`Missing body for struct ${header[1]}`, baseOffset + index);
|
|
484
|
+
const close = findMatching(source, brace, "{");
|
|
485
|
+
const bodySource = source.slice(brace + 1, close);
|
|
486
|
+
const bodyExpression = findTopLevelBodyExpression(bodySource);
|
|
487
|
+
if (!bodyExpression)
|
|
488
|
+
throw syntaxError(`struct ${header[1]} must declare var body`, baseOffset + index);
|
|
489
|
+
const members = parseStructMembers(bodySource.slice(0, bodyExpression.declarationStart), baseOffset + brace + 1);
|
|
490
|
+
const nested = parseVuneStructs(bodySource, baseOffset + brace + 1);
|
|
491
|
+
declarations.push({
|
|
492
|
+
kind: "struct",
|
|
493
|
+
name: header[1],
|
|
494
|
+
genericParameters: header[2]?.trim(),
|
|
495
|
+
source: source.slice(index, close + 1),
|
|
496
|
+
bodySource,
|
|
497
|
+
bodyExpressionSource: bodySource.slice(bodyExpression.open + 1, bodyExpression.close),
|
|
498
|
+
range: { start: baseOffset + index, end: baseOffset + close + 1 },
|
|
499
|
+
bodyRange: { start: baseOffset + brace + 1, end: baseOffset + close },
|
|
500
|
+
bodyExpressionRange: { start: baseOffset + brace + 1 + bodyExpression.open + 1, end: baseOffset + brace + 1 + bodyExpression.close },
|
|
501
|
+
fields: members.fields,
|
|
502
|
+
initializers: members.initializers,
|
|
503
|
+
nested,
|
|
504
|
+
});
|
|
505
|
+
cursor = close + 1;
|
|
506
|
+
}
|
|
507
|
+
return declarations;
|
|
508
|
+
}
|
|
509
|
+
function lowerProgram(program, lowering) {
|
|
510
|
+
return program.statements.map(node => {
|
|
511
|
+
if (node.kind === "raw")
|
|
512
|
+
return lowering.transformRaw(node.source);
|
|
513
|
+
if (node.kind === "call") {
|
|
514
|
+
const positional = node.arguments.filter(argument => !argument.label).map((argument, argumentIndex) => argument.value.kind === "closure"
|
|
515
|
+
? lowering.closure(argument.value.bodySource, argument.value.parameter, lowering.closureRole?.(node, { position: "argument", argumentIndex, label: argument.label }))
|
|
516
|
+
: lowering.transformArgument?.(argument.value.source, node, argumentIndex, argument.label) ?? lowering.transformRaw(argument.value.source));
|
|
517
|
+
const named = node.arguments.filter(argument => argument.label).map((argument, argumentIndex) => `${argument.label}: ${argument.value.kind === "closure"
|
|
518
|
+
? lowering.closure(argument.value.bodySource, argument.value.parameter, lowering.closureRole?.(node, { position: "argument", argumentIndex, label: argument.label }))
|
|
519
|
+
: lowering.transformArgument?.(argument.value.source, node, argumentIndex, argument.label) ?? lowering.transformRaw(argument.value.source)}`);
|
|
520
|
+
const args = [...positional, ...(named.length ? [`namedArguments({ ${named.join(", ")} })`] : []), ...(node.trailing ? [lowering.closure(node.trailing.bodySource, node.trailing.parameter, lowering.closureRole?.(node, { position: "trailing" }))] : [])];
|
|
521
|
+
return `${node.callee}(${args.join(", ")})`;
|
|
522
|
+
}
|
|
523
|
+
const thenBranch = lowerProgram(node.then, lowering).join(", ");
|
|
524
|
+
const otherwise = !node.otherwise ? "[]" : node.otherwise.kind === "conditional" ? lowerProgram({ ...node.otherwise.then, statements: [node.otherwise] }, lowering)[0] : `[${lowerProgram(node.otherwise, lowering).join(", ")}]`;
|
|
525
|
+
return `(${lowering.transformRaw(node.condition.source)} ? [${thenBranch}] : ${otherwise})`;
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
export function lowerVuneBuilderAst(program, lowering) {
|
|
529
|
+
return lowerProgram(program, lowering);
|
|
530
|
+
}
|
|
531
|
+
//# sourceMappingURL=ast.js.map
|