@aeriajs/compiler 0.0.62 → 0.0.63
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.js +3 -6
- package/dist/codegen/generateContracts.js +15 -19
- package/dist/codegen/generateExports.js +5 -9
- package/dist/codegen/generateJSCollections.js +13 -17
- package/dist/codegen/generateTSCollections.js +13 -17
- package/dist/codegen/index.js +4 -20
- package/dist/codegen/utils.js +25 -37
- package/dist/codegen.js +8 -45
- package/dist/compile.js +19 -58
- package/dist/diagnostic.js +1 -5
- package/dist/guards.js +3 -41
- package/dist/index.js +7 -23
- package/dist/lexer.js +48 -52
- package/dist/parser.js +260 -297
- package/dist/semantic.js +31 -68
- package/dist/token.js +1 -4
- package/dist/types.js +1 -2
- package/dist/utils.js +1 -4
- package/package.json +6 -9
- package/dist/ast.mjs +0 -25
- package/dist/codegen/generateContracts.mjs +0 -85
- package/dist/codegen/generateExports.mjs +0 -42
- package/dist/codegen/generateJSCollections.mjs +0 -103
- package/dist/codegen/generateTSCollections.mjs +0 -114
- package/dist/codegen/index.mjs +0 -5
- package/dist/codegen/utils.mjs +0 -161
- package/dist/codegen.mjs +0 -54
- package/dist/compile.mjs +0 -69
- package/dist/diagnostic.mjs +0 -18
- package/dist/guards.mjs +0 -8
- package/dist/index.mjs +0 -8
- package/dist/lexer.mjs +0 -375
- package/dist/parser.mjs +0 -1304
- package/dist/semantic.mjs +0 -202
- package/dist/token.mjs +0 -24
- package/dist/types.mjs +0 -1
- package/dist/utils.mjs +0 -12
package/dist/codegen/utils.mjs
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
export const PACKAGE_NAME = "aeria";
|
|
3
|
-
export const MIDDLEWARES_RUNTIME_PATH = "../../../dist/middlewares/index.mjs";
|
|
4
|
-
export const UnquotedSymbol = Symbol("unquoted");
|
|
5
|
-
export const ArraySymbol = Symbol("array");
|
|
6
|
-
export const getExposedFunctions = (functionNodes) => {
|
|
7
|
-
return Object.fromEntries(functionNodes.map((node) => [
|
|
8
|
-
node.name,
|
|
9
|
-
node.accessCondition
|
|
10
|
-
]));
|
|
11
|
-
};
|
|
12
|
-
export const makeASTImports = (ast, initialImports = {}, options = {
|
|
13
|
-
includeRuntimeOnlyImports: false
|
|
14
|
-
}) => {
|
|
15
|
-
const modifiedSymbols = {};
|
|
16
|
-
const toImport = ast.reduce((imports, node) => {
|
|
17
|
-
if (node.kind === "collection") {
|
|
18
|
-
if (node.extends) {
|
|
19
|
-
const modifiedSymbol = `${node.extends.packageName}${resizeFirstChar(node.extends.symbolName, true)}`;
|
|
20
|
-
modifiedSymbols[node.extends.symbolName] = modifiedSymbol;
|
|
21
|
-
imports[node.extends.importPath] ??= /* @__PURE__ */ new Set();
|
|
22
|
-
imports[node.extends.importPath].add(`${node.extends.symbolName} as ${modifiedSymbol}`);
|
|
23
|
-
}
|
|
24
|
-
if (node.functions) {
|
|
25
|
-
for (const functionNode of node.functions) {
|
|
26
|
-
if (functionNode.exportSymbol) {
|
|
27
|
-
const { importPath, symbolName } = functionNode.exportSymbol;
|
|
28
|
-
imports[importPath] ??= /* @__PURE__ */ new Set();
|
|
29
|
-
imports[importPath].add(symbolName);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
if (options.includeRuntimeOnlyImports) {
|
|
34
|
-
if (node.middlewares) {
|
|
35
|
-
imports[MIDDLEWARES_RUNTIME_PATH] ??= /* @__PURE__ */ new Set();
|
|
36
|
-
for (const middleware of node.middlewares) {
|
|
37
|
-
imports[MIDDLEWARES_RUNTIME_PATH].add(middleware);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return imports;
|
|
43
|
-
}, initialImports);
|
|
44
|
-
return {
|
|
45
|
-
code: Object.keys(toImport).map((key) => `import { ${Array.from(toImport[key]).join(", ")} } from '${key}'`),
|
|
46
|
-
modifiedSymbols
|
|
47
|
-
};
|
|
48
|
-
};
|
|
49
|
-
export const unwrapNode = (node) => {
|
|
50
|
-
const { kind, ...unwrappedNode } = Object.fromEntries(Object.entries(node).filter(([key]) => typeof key === "string"));
|
|
51
|
-
return unwrappedNode;
|
|
52
|
-
};
|
|
53
|
-
export const unwrapPropertyNode = ({ property, nestedProperties, nestedAdditionalProperties }) => {
|
|
54
|
-
const propertyOrPropertyItems = "items" in property ? property.items : property;
|
|
55
|
-
let unwrappedProperty = propertyOrPropertyItems;
|
|
56
|
-
if ("$ref" in propertyOrPropertyItems) {
|
|
57
|
-
unwrappedProperty = {
|
|
58
|
-
...propertyOrPropertyItems,
|
|
59
|
-
$ref: getCollectionId(propertyOrPropertyItems.$ref)
|
|
60
|
-
};
|
|
61
|
-
} else if ("type" in propertyOrPropertyItems && propertyOrPropertyItems.type === "object") {
|
|
62
|
-
let properties;
|
|
63
|
-
let additionalProperties;
|
|
64
|
-
if (nestedProperties) {
|
|
65
|
-
properties = recursivelyUnwrapPropertyNodes(nestedProperties);
|
|
66
|
-
}
|
|
67
|
-
if (nestedAdditionalProperties) {
|
|
68
|
-
additionalProperties = typeof nestedAdditionalProperties === "boolean" ? nestedAdditionalProperties : unwrapPropertyNode(nestedAdditionalProperties);
|
|
69
|
-
}
|
|
70
|
-
if (properties && additionalProperties) {
|
|
71
|
-
unwrappedProperty = {
|
|
72
|
-
...propertyOrPropertyItems,
|
|
73
|
-
properties,
|
|
74
|
-
additionalProperties
|
|
75
|
-
};
|
|
76
|
-
} else if (properties) {
|
|
77
|
-
unwrappedProperty = {
|
|
78
|
-
...propertyOrPropertyItems,
|
|
79
|
-
properties
|
|
80
|
-
};
|
|
81
|
-
} else if (additionalProperties) {
|
|
82
|
-
unwrappedProperty = {
|
|
83
|
-
...propertyOrPropertyItems,
|
|
84
|
-
additionalProperties
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
if ("items" in property) {
|
|
89
|
-
return {
|
|
90
|
-
...property,
|
|
91
|
-
items: unwrappedProperty
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
return unwrappedProperty;
|
|
95
|
-
};
|
|
96
|
-
export const recursivelyUnwrapPropertyNodes = (properties) => {
|
|
97
|
-
return Object.entries(properties).reduce((acc, [key, value]) => {
|
|
98
|
-
if (Array.isArray(value)) {
|
|
99
|
-
acc[key] = value.map((propertyNode) => unwrapPropertyNode(propertyNode));
|
|
100
|
-
} else {
|
|
101
|
-
acc[key] = unwrapPropertyNode(value);
|
|
102
|
-
}
|
|
103
|
-
return acc;
|
|
104
|
-
}, {});
|
|
105
|
-
};
|
|
106
|
-
const isRecord = (value) => {
|
|
107
|
-
return !!(value && typeof value === "object");
|
|
108
|
-
};
|
|
109
|
-
export const stringify = (value, parents = []) => {
|
|
110
|
-
if (Array.isArray(value)) {
|
|
111
|
-
let arrayString = "[\n";
|
|
112
|
-
value.map((element) => {
|
|
113
|
-
const currentParents = [
|
|
114
|
-
...parents,
|
|
115
|
-
ArraySymbol
|
|
116
|
-
];
|
|
117
|
-
arrayString += " ".repeat(currentParents.length) + checkQuotes(currentParents, element) + ",\n";
|
|
118
|
-
});
|
|
119
|
-
return arrayString + `${" ".repeat(parents.length)}]`;
|
|
120
|
-
}
|
|
121
|
-
if (isRecord(value)) {
|
|
122
|
-
const objectString = Object.keys(value).map((key) => {
|
|
123
|
-
const currentParents = [
|
|
124
|
-
...parents,
|
|
125
|
-
key
|
|
126
|
-
];
|
|
127
|
-
const indentation = " ".repeat(currentParents.length);
|
|
128
|
-
return `${indentation}${key}: ${checkQuotes(currentParents, value[key])}`;
|
|
129
|
-
}).join(",\n");
|
|
130
|
-
return `{
|
|
131
|
-
${objectString}
|
|
132
|
-
${" ".repeat(parents.length)}}`;
|
|
133
|
-
}
|
|
134
|
-
switch (typeof value) {
|
|
135
|
-
case "undefined":
|
|
136
|
-
case "number":
|
|
137
|
-
case "boolean": {
|
|
138
|
-
return String(value);
|
|
139
|
-
}
|
|
140
|
-
default: {
|
|
141
|
-
if (value === null) {
|
|
142
|
-
return String(value);
|
|
143
|
-
}
|
|
144
|
-
return `"${String(value)}"`;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
const checkQuotes = (parents, value) => {
|
|
149
|
-
if (value && typeof value === "object" && UnquotedSymbol in value) {
|
|
150
|
-
return value[UnquotedSymbol];
|
|
151
|
-
}
|
|
152
|
-
return stringify(value, parents);
|
|
153
|
-
};
|
|
154
|
-
export const resizeFirstChar = (text, capitalize) => {
|
|
155
|
-
if (capitalize === true) {
|
|
156
|
-
return text.charAt(0).toUpperCase() + text.slice(1);
|
|
157
|
-
}
|
|
158
|
-
return text.charAt(0).toLowerCase() + text.slice(1);
|
|
159
|
-
};
|
|
160
|
-
export const getCollectionId = (name) => resizeFirstChar(name, false);
|
|
161
|
-
export const getExtendName = (name) => `extend${resizeFirstChar(name, true)}Collection`;
|
package/dist/codegen.mjs
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
import { generateContracts, generateExports, generateJSCollections, generateTSCollections } from "./codegen/index.mjs";
|
|
3
|
-
import * as fsPromises from "node:fs/promises";
|
|
4
|
-
import * as path from "node:path";
|
|
5
|
-
const generateFileMap = async (fileTree, outDir = ".") => {
|
|
6
|
-
const mappedPaths = {};
|
|
7
|
-
const mapPathTree = async (tree, previousPath) => {
|
|
8
|
-
for (const treePath in tree) {
|
|
9
|
-
const currentPath = path.join(previousPath, treePath);
|
|
10
|
-
if (typeof tree[treePath] === "object") {
|
|
11
|
-
await mapPathTree(tree[treePath], currentPath);
|
|
12
|
-
continue;
|
|
13
|
-
}
|
|
14
|
-
await fsPromises.mkdir(previousPath, {
|
|
15
|
-
recursive: true
|
|
16
|
-
});
|
|
17
|
-
mappedPaths[currentPath] = tree[treePath];
|
|
18
|
-
}
|
|
19
|
-
return;
|
|
20
|
-
};
|
|
21
|
-
await mapPathTree(fileTree, outDir);
|
|
22
|
-
return mappedPaths;
|
|
23
|
-
};
|
|
24
|
-
export const generateCode = async (ast, options) => {
|
|
25
|
-
const contracts = generateContracts(ast);
|
|
26
|
-
const exports = generateExports(ast, {
|
|
27
|
-
hasContracts: !!contracts
|
|
28
|
-
});
|
|
29
|
-
const fileTree = {
|
|
30
|
-
["collections"]: {
|
|
31
|
-
["collections.d.ts"]: generateTSCollections(ast),
|
|
32
|
-
["collections.mjs"]: generateJSCollections(ast),
|
|
33
|
-
["index.d.ts"]: exports.collections.dts,
|
|
34
|
-
["index.mjs"]: exports.collections.js
|
|
35
|
-
},
|
|
36
|
-
["index.d.ts"]: exports.index.dts,
|
|
37
|
-
["index.mjs"]: exports.index.js
|
|
38
|
-
};
|
|
39
|
-
if (contracts) {
|
|
40
|
-
fileTree.contracts = {
|
|
41
|
-
["contracts.mjs"]: contracts.js,
|
|
42
|
-
["contracts.d.ts"]: contracts.dts,
|
|
43
|
-
["index.d.ts"]: exports.contracts.dts,
|
|
44
|
-
["index.mjs"]: exports.contracts.js
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
const fileStructure = await generateFileMap(fileTree, options.outDir);
|
|
48
|
-
if (!options.dryRun) {
|
|
49
|
-
for (const path2 in fileStructure) {
|
|
50
|
-
await fsPromises.writeFile(path2, fileStructure[path2]);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return fileStructure;
|
|
54
|
-
};
|
package/dist/compile.mjs
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
import { Diagnostic } from "./diagnostic.mjs";
|
|
3
|
-
import { tokenize } from "./lexer.mjs";
|
|
4
|
-
import { parse, locationMap } from "./parser.mjs";
|
|
5
|
-
import { analyze } from "./semantic.mjs";
|
|
6
|
-
import { generateCode } from "./codegen.mjs";
|
|
7
|
-
import * as fs from "node:fs";
|
|
8
|
-
export const GLOB_PATTERN = "**/*.aeria";
|
|
9
|
-
export const postflight = (ast) => {
|
|
10
|
-
const errors = [];
|
|
11
|
-
for (const node of ast.collections) {
|
|
12
|
-
if (node.functionSets?.length) {
|
|
13
|
-
for (const [functionSetName, locationSymbol] of node.functionSets) {
|
|
14
|
-
const functionSet = ast.functionsets.find(({ name }) => name === functionSetName);
|
|
15
|
-
if (!functionSet) {
|
|
16
|
-
const location = locationMap.get(locationSymbol);
|
|
17
|
-
errors.push(new Diagnostic(`invalid function set "${functionSetName}"`, location));
|
|
18
|
-
continue;
|
|
19
|
-
}
|
|
20
|
-
node.functions.push(...functionSet.functions);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
return {
|
|
25
|
-
errors
|
|
26
|
-
};
|
|
27
|
-
};
|
|
28
|
-
export const parseAndCheck = async (sources, options = {}) => {
|
|
29
|
-
const errors = [];
|
|
30
|
-
const allTokens = [];
|
|
31
|
-
for (const fileName in sources) {
|
|
32
|
-
const { errors: lexerErrors, tokens } = tokenize(sources[fileName], fileName);
|
|
33
|
-
if (lexerErrors.length > 0) {
|
|
34
|
-
errors.push(...lexerErrors);
|
|
35
|
-
}
|
|
36
|
-
allTokens.push(...tokens);
|
|
37
|
-
}
|
|
38
|
-
const { errors: parserErrors, ast } = parse(allTokens);
|
|
39
|
-
const { errors: semanticErrors } = await analyze(ast, options);
|
|
40
|
-
const { errors: postflightErrors } = postflight(ast);
|
|
41
|
-
errors.push(...parserErrors.concat(semanticErrors, postflightErrors));
|
|
42
|
-
return {
|
|
43
|
-
success: errors.length === 0,
|
|
44
|
-
errors,
|
|
45
|
-
errorCount: errors.length,
|
|
46
|
-
ast
|
|
47
|
-
};
|
|
48
|
-
};
|
|
49
|
-
export const compileFromFiles = async (options) => {
|
|
50
|
-
const fileList = await Array.fromAsync(fs.promises.glob(GLOB_PATTERN));
|
|
51
|
-
const sources = {};
|
|
52
|
-
for (const fileName of fileList) {
|
|
53
|
-
sources[fileName] = await fs.promises.readFile(fileName, {
|
|
54
|
-
encoding: "utf-8"
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
const result = await parseAndCheck(sources, options);
|
|
58
|
-
if (!result.ast || result.errorCount > 0) {
|
|
59
|
-
return result;
|
|
60
|
-
}
|
|
61
|
-
if (options.outDir) {
|
|
62
|
-
const emittedFiles = await generateCode(result.ast, options);
|
|
63
|
-
return {
|
|
64
|
-
...result,
|
|
65
|
-
emittedFiles
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
return result;
|
|
69
|
-
};
|
package/dist/diagnostic.mjs
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
const emptyLocation = {
|
|
3
|
-
file: "",
|
|
4
|
-
index: 0,
|
|
5
|
-
line: 0,
|
|
6
|
-
start: 0,
|
|
7
|
-
end: 0
|
|
8
|
-
};
|
|
9
|
-
export class Diagnostic extends Error {
|
|
10
|
-
constructor(message, location = emptyLocation) {
|
|
11
|
-
super();
|
|
12
|
-
this.message = message;
|
|
13
|
-
this.location = location;
|
|
14
|
-
if (false) {
|
|
15
|
-
console.error(message, location);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
package/dist/guards.mjs
DELETED
package/dist/index.mjs
DELETED
package/dist/lexer.mjs
DELETED
|
@@ -1,375 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
import { TokenType } from "./token.mjs";
|
|
3
|
-
import { Diagnostic } from "./diagnostic.mjs";
|
|
4
|
-
export const COLLECTION_KEYWORDS = [
|
|
5
|
-
"actions",
|
|
6
|
-
"additionalProperties",
|
|
7
|
-
"filters",
|
|
8
|
-
"form",
|
|
9
|
-
"formLayout",
|
|
10
|
-
"functions",
|
|
11
|
-
"icon",
|
|
12
|
-
"indexes",
|
|
13
|
-
"individualActions",
|
|
14
|
-
"layout",
|
|
15
|
-
"middlewares",
|
|
16
|
-
"owned",
|
|
17
|
-
"presets",
|
|
18
|
-
"properties",
|
|
19
|
-
"required",
|
|
20
|
-
"search",
|
|
21
|
-
"table",
|
|
22
|
-
"tableMeta",
|
|
23
|
-
"unique",
|
|
24
|
-
"writable"
|
|
25
|
-
];
|
|
26
|
-
export const COLLECTION_ACTIONS_KEYWORDS = [
|
|
27
|
-
"ask",
|
|
28
|
-
"button",
|
|
29
|
-
"clearItem",
|
|
30
|
-
"effect",
|
|
31
|
-
"event",
|
|
32
|
-
"fetchItem",
|
|
33
|
-
"function",
|
|
34
|
-
"icon",
|
|
35
|
-
"label",
|
|
36
|
-
"params",
|
|
37
|
-
"query",
|
|
38
|
-
"requires",
|
|
39
|
-
"roles",
|
|
40
|
-
"route",
|
|
41
|
-
"selection",
|
|
42
|
-
"setItem",
|
|
43
|
-
"translate"
|
|
44
|
-
];
|
|
45
|
-
export const COLLECTION_SEARCH_KEYWORDS = [
|
|
46
|
-
"indexes",
|
|
47
|
-
"placeholder",
|
|
48
|
-
"exactMatches"
|
|
49
|
-
];
|
|
50
|
-
export const COLLECTION_LAYOUT_KEYWORDS = [
|
|
51
|
-
"name",
|
|
52
|
-
"options"
|
|
53
|
-
];
|
|
54
|
-
export const COLLECTION_LAYOUT_OPTIONS_KEYWORDS = [
|
|
55
|
-
"title",
|
|
56
|
-
"picture",
|
|
57
|
-
"badge",
|
|
58
|
-
"information",
|
|
59
|
-
"active",
|
|
60
|
-
"translateBadge"
|
|
61
|
-
];
|
|
62
|
-
export const COLLECTION_FORM_LAYOUT_KEYWORDS = [
|
|
63
|
-
"fields",
|
|
64
|
-
"if",
|
|
65
|
-
"span",
|
|
66
|
-
"verticalSpacing",
|
|
67
|
-
"separator"
|
|
68
|
-
];
|
|
69
|
-
export const CONTRACT_KEYWORDS = [
|
|
70
|
-
"roles",
|
|
71
|
-
"payload",
|
|
72
|
-
"query",
|
|
73
|
-
"response"
|
|
74
|
-
];
|
|
75
|
-
export const TOPLEVEL_KEYWORDS = [
|
|
76
|
-
"collection",
|
|
77
|
-
"contract",
|
|
78
|
-
"functionset"
|
|
79
|
-
];
|
|
80
|
-
export const MISC_KEYWORDS = ["extends"];
|
|
81
|
-
export const KEYWORDS = [].concat(
|
|
82
|
-
COLLECTION_KEYWORDS,
|
|
83
|
-
COLLECTION_ACTIONS_KEYWORDS,
|
|
84
|
-
COLLECTION_SEARCH_KEYWORDS,
|
|
85
|
-
COLLECTION_LAYOUT_KEYWORDS,
|
|
86
|
-
COLLECTION_LAYOUT_OPTIONS_KEYWORDS,
|
|
87
|
-
COLLECTION_FORM_LAYOUT_KEYWORDS,
|
|
88
|
-
CONTRACT_KEYWORDS,
|
|
89
|
-
TOPLEVEL_KEYWORDS,
|
|
90
|
-
MISC_KEYWORDS
|
|
91
|
-
);
|
|
92
|
-
export const FINAL_OPERATORS = [
|
|
93
|
-
"==",
|
|
94
|
-
"in",
|
|
95
|
-
">=",
|
|
96
|
-
"<=",
|
|
97
|
-
">",
|
|
98
|
-
"<",
|
|
99
|
-
"!"
|
|
100
|
-
];
|
|
101
|
-
export const LOGICAL_OPERATORS = [
|
|
102
|
-
"&&",
|
|
103
|
-
"||"
|
|
104
|
-
];
|
|
105
|
-
const keywordsSet = /* @__PURE__ */ new Set();
|
|
106
|
-
for (const keyword of KEYWORDS) {
|
|
107
|
-
keywordsSet.add(keyword);
|
|
108
|
-
}
|
|
109
|
-
const TOKENS = [
|
|
110
|
-
{
|
|
111
|
-
type: null,
|
|
112
|
-
matcher: /\r?[ \t]+/
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
type: TokenType.LineBreak,
|
|
116
|
-
matcher: "\n"
|
|
117
|
-
},
|
|
118
|
-
{
|
|
119
|
-
type: TokenType.Comment,
|
|
120
|
-
matcher: "//"
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
type: TokenType.LeftBracket,
|
|
124
|
-
matcher: "{"
|
|
125
|
-
},
|
|
126
|
-
{
|
|
127
|
-
type: TokenType.RightBracket,
|
|
128
|
-
matcher: "}"
|
|
129
|
-
},
|
|
130
|
-
{
|
|
131
|
-
type: TokenType.LeftParens,
|
|
132
|
-
matcher: "("
|
|
133
|
-
},
|
|
134
|
-
{
|
|
135
|
-
type: TokenType.RightParens,
|
|
136
|
-
matcher: ")"
|
|
137
|
-
},
|
|
138
|
-
{
|
|
139
|
-
type: TokenType.LeftSquareBracket,
|
|
140
|
-
matcher: "["
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
type: TokenType.RightSquareBracket,
|
|
144
|
-
matcher: "]"
|
|
145
|
-
},
|
|
146
|
-
{
|
|
147
|
-
type: TokenType.Operator,
|
|
148
|
-
matcher: [].concat(FINAL_OPERATORS, LOGICAL_OPERATORS)
|
|
149
|
-
},
|
|
150
|
-
{
|
|
151
|
-
type: TokenType.Pipe,
|
|
152
|
-
matcher: "|"
|
|
153
|
-
},
|
|
154
|
-
{
|
|
155
|
-
type: TokenType.Comma,
|
|
156
|
-
matcher: ","
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
type: TokenType.Range,
|
|
160
|
-
matcher: /(\d+\.\.\d*|\d*\.\.\d+)/g,
|
|
161
|
-
valueExtractor: (value) => {
|
|
162
|
-
const [, left, right] = value.match(/(\d*)\.\.(\d*)/);
|
|
163
|
-
return [
|
|
164
|
-
parseInt(left),
|
|
165
|
-
parseInt(right)
|
|
166
|
-
];
|
|
167
|
-
}
|
|
168
|
-
},
|
|
169
|
-
{
|
|
170
|
-
type: TokenType.Dot,
|
|
171
|
-
matcher: "."
|
|
172
|
-
},
|
|
173
|
-
{
|
|
174
|
-
type: TokenType.Number,
|
|
175
|
-
matcher: /[0-9]+(\.[0-9]+)?/,
|
|
176
|
-
construct: Number
|
|
177
|
-
},
|
|
178
|
-
{
|
|
179
|
-
type: TokenType.Boolean,
|
|
180
|
-
matcher: [
|
|
181
|
-
"true",
|
|
182
|
-
"false"
|
|
183
|
-
],
|
|
184
|
-
construct: Boolean
|
|
185
|
-
},
|
|
186
|
-
{
|
|
187
|
-
type: TokenType.Keyword,
|
|
188
|
-
matcher: Array.from(keywordsSet),
|
|
189
|
-
condition: (state, lastToken) => {
|
|
190
|
-
if (state.variableScopeStack.at(-1) || state.variableExpressionStack.at(-1)) {
|
|
191
|
-
return false;
|
|
192
|
-
}
|
|
193
|
-
if (lastToken && lastToken.type === TokenType.Keyword) {
|
|
194
|
-
switch (lastToken.value) {
|
|
195
|
-
case "if":
|
|
196
|
-
case "badge":
|
|
197
|
-
case "title": {
|
|
198
|
-
return false;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
return true;
|
|
203
|
-
}
|
|
204
|
-
},
|
|
205
|
-
{
|
|
206
|
-
type: TokenType.MacroName,
|
|
207
|
-
matcher: /[a-zA-Z]([a-zA-Z0-9]|_)+\(/,
|
|
208
|
-
valueExtractor: (value) => value.slice(0, -1)
|
|
209
|
-
},
|
|
210
|
-
{
|
|
211
|
-
type: TokenType.Identifier,
|
|
212
|
-
matcher: /([a-zA-Z0-9]|_)+/
|
|
213
|
-
},
|
|
214
|
-
{
|
|
215
|
-
type: TokenType.QuotedString,
|
|
216
|
-
matcher: /"([^"]+)"/,
|
|
217
|
-
valueExtractor: (value) => value.slice(1, -1)
|
|
218
|
-
},
|
|
219
|
-
{
|
|
220
|
-
type: TokenType.AttributeName,
|
|
221
|
-
matcher: /@[a-zA-Z0-9]+/,
|
|
222
|
-
valueExtractor: (value) => value.slice(1)
|
|
223
|
-
}
|
|
224
|
-
];
|
|
225
|
-
export const tokenize = function(rawInput, fileLocation) {
|
|
226
|
-
const input = rawInput.replace(/\r\n/g, "\n");
|
|
227
|
-
let index = 0, line = 1, start = 0, end = 0;
|
|
228
|
-
const tokens = [];
|
|
229
|
-
const errors = [];
|
|
230
|
-
const state = {
|
|
231
|
-
variableScopeStack: [],
|
|
232
|
-
variableExpressionStack: []
|
|
233
|
-
};
|
|
234
|
-
while (index < input.length) {
|
|
235
|
-
let hasMatch = false;
|
|
236
|
-
for (const { type, matcher, valueExtractor, construct, condition } of TOKENS) {
|
|
237
|
-
let value;
|
|
238
|
-
let token;
|
|
239
|
-
const lastToken = tokens.at(-1);
|
|
240
|
-
if (condition) {
|
|
241
|
-
if (!condition(state, lastToken)) {
|
|
242
|
-
continue;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
if (typeof matcher === "string") {
|
|
246
|
-
if (input.slice(index).startsWith(matcher)) {
|
|
247
|
-
value = matcher;
|
|
248
|
-
}
|
|
249
|
-
} else if (matcher instanceof RegExp) {
|
|
250
|
-
const currentMatcher = new RegExp(matcher.source, "y");
|
|
251
|
-
currentMatcher.lastIndex = index;
|
|
252
|
-
const matched = currentMatcher.exec(input);
|
|
253
|
-
if (matched) {
|
|
254
|
-
[value] = matched;
|
|
255
|
-
}
|
|
256
|
-
} else {
|
|
257
|
-
const segment = input.slice(index, index + input.slice(index).search(/[ \t\n\{\}\(\)\[\]]/));
|
|
258
|
-
if (segment && matcher.includes(segment)) {
|
|
259
|
-
value = segment;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
if (value) {
|
|
263
|
-
let tokenValue;
|
|
264
|
-
const location = {
|
|
265
|
-
file: fileLocation,
|
|
266
|
-
index: index += value.length,
|
|
267
|
-
line,
|
|
268
|
-
end: end += value.length,
|
|
269
|
-
start: start = end - value.length
|
|
270
|
-
};
|
|
271
|
-
switch (type) {
|
|
272
|
-
case null:
|
|
273
|
-
break;
|
|
274
|
-
case TokenType.LineBreak:
|
|
275
|
-
line++;
|
|
276
|
-
end = 0;
|
|
277
|
-
start = 0;
|
|
278
|
-
break;
|
|
279
|
-
case TokenType.Comment: {
|
|
280
|
-
while (input[index++] !== "\n") {
|
|
281
|
-
}
|
|
282
|
-
line++;
|
|
283
|
-
break;
|
|
284
|
-
}
|
|
285
|
-
default: {
|
|
286
|
-
if (valueExtractor) {
|
|
287
|
-
tokenValue = construct ? construct(valueExtractor(value)) : valueExtractor(value);
|
|
288
|
-
} else {
|
|
289
|
-
tokenValue = construct ? construct(value) : value;
|
|
290
|
-
}
|
|
291
|
-
token = {
|
|
292
|
-
type,
|
|
293
|
-
location,
|
|
294
|
-
value: tokenValue
|
|
295
|
-
};
|
|
296
|
-
switch (type) {
|
|
297
|
-
case TokenType.LeftBracket: {
|
|
298
|
-
let variableScope = false;
|
|
299
|
-
if (lastToken && lastToken.type === TokenType.Keyword) {
|
|
300
|
-
switch (lastToken.value) {
|
|
301
|
-
case "fields":
|
|
302
|
-
case "information":
|
|
303
|
-
case "form":
|
|
304
|
-
case "table":
|
|
305
|
-
case "tableMeta":
|
|
306
|
-
case "indexes":
|
|
307
|
-
case "filters":
|
|
308
|
-
case "writable":
|
|
309
|
-
case "required":
|
|
310
|
-
case "properties": {
|
|
311
|
-
variableScope = true;
|
|
312
|
-
break;
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
state.variableScopeStack.push(variableScope);
|
|
317
|
-
break;
|
|
318
|
-
}
|
|
319
|
-
case TokenType.LeftParens: {
|
|
320
|
-
let variableExpression = false;
|
|
321
|
-
if (lastToken) {
|
|
322
|
-
switch (lastToken.type) {
|
|
323
|
-
case TokenType.Keyword: {
|
|
324
|
-
switch (lastToken.value) {
|
|
325
|
-
case "if": {
|
|
326
|
-
variableExpression = true;
|
|
327
|
-
break;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
break;
|
|
331
|
-
}
|
|
332
|
-
case TokenType.Operator: {
|
|
333
|
-
variableExpression = true;
|
|
334
|
-
break;
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
state.variableExpressionStack.push(variableExpression);
|
|
339
|
-
break;
|
|
340
|
-
}
|
|
341
|
-
case TokenType.RightBracket: {
|
|
342
|
-
if (state.variableScopeStack.length > 0) {
|
|
343
|
-
state.variableScopeStack.pop();
|
|
344
|
-
}
|
|
345
|
-
break;
|
|
346
|
-
}
|
|
347
|
-
case TokenType.RightParens: {
|
|
348
|
-
if (state.variableExpressionStack.length > 0) {
|
|
349
|
-
state.variableExpressionStack.pop();
|
|
350
|
-
}
|
|
351
|
-
break;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
tokens.push(token);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
hasMatch = true;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
if (!hasMatch) {
|
|
361
|
-
index += input.slice(index).search(/[ \t\n\{\}\(\)\[\]]/);
|
|
362
|
-
errors.push(new Diagnostic("unexpected token", {
|
|
363
|
-
file: fileLocation,
|
|
364
|
-
index,
|
|
365
|
-
line,
|
|
366
|
-
start,
|
|
367
|
-
end
|
|
368
|
-
}));
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
return {
|
|
372
|
-
tokens,
|
|
373
|
-
errors
|
|
374
|
-
};
|
|
375
|
-
};
|