WIP lua
parent
f7fe7cadbc
commit
8725655b9d
|
@ -0,0 +1,171 @@
|
||||||
|
/* Based on: https://github.com/R167/lezer-lua */
|
||||||
|
@precedence {
|
||||||
|
call,
|
||||||
|
power @right,
|
||||||
|
prefix,
|
||||||
|
times @left,
|
||||||
|
plus @left,
|
||||||
|
concat @right,
|
||||||
|
shift @left,
|
||||||
|
bitand @left,
|
||||||
|
xor @left,
|
||||||
|
bitor @left,
|
||||||
|
compare @left,
|
||||||
|
and @left,
|
||||||
|
or @left
|
||||||
|
}
|
||||||
|
|
||||||
|
@top Chunk { Block }
|
||||||
|
|
||||||
|
Block { statement* ReturnStatement? }
|
||||||
|
|
||||||
|
ReturnStatement { kw<"return"> exp? ";"?}
|
||||||
|
|
||||||
|
@skip { newline | space | Comment }
|
||||||
|
|
||||||
|
statement[@isGroup=Statement] {
|
||||||
|
";" |
|
||||||
|
Label |
|
||||||
|
kw<"break"> |
|
||||||
|
Goto{ kw<"goto"> Name } |
|
||||||
|
Scope { kw<"do"> Block kw<"end"> } |
|
||||||
|
WhileStatement { kw<"while"> exp kw<"do"> Block kw<"end"> } |
|
||||||
|
RepeatStatement { kw<"repeat"> Block kw<"until"> exp } |
|
||||||
|
IfStatement |
|
||||||
|
ForStatement |
|
||||||
|
Function { kw<"function"> FuncName FuncBody } |
|
||||||
|
LocalFunction { kw<"local"> kw<"function"> Name FuncBody } |
|
||||||
|
Assign { VarList "=" ExpList } |
|
||||||
|
Local { kw<"local"> AttNameList ("=" ExpList)? } |
|
||||||
|
FunctionCall ~fcall
|
||||||
|
}
|
||||||
|
|
||||||
|
IfStatement {
|
||||||
|
kw<"if"> exp kw<"then"> Block
|
||||||
|
(kw<"elseif"> exp kw<"then"> Block)*
|
||||||
|
(kw<"else"> Block)
|
||||||
|
kw<"end">
|
||||||
|
}
|
||||||
|
|
||||||
|
ForNumeric { Name "=" exp "," exp ("," exp)? }
|
||||||
|
|
||||||
|
ForGeneric { NameList kw<"in"> ExpList }
|
||||||
|
|
||||||
|
ForStatement {
|
||||||
|
kw<"for"> (ForNumeric | ForGeneric) kw<"do"> Block kw<"end">
|
||||||
|
}
|
||||||
|
|
||||||
|
FuncName { Name ("." Name)* (":" Name)? }
|
||||||
|
FuncBody { "(" ArgList ")" Block kw<"end"> }
|
||||||
|
|
||||||
|
list<term> { term ("," term)* }
|
||||||
|
|
||||||
|
NameList { list<Name> }
|
||||||
|
ExpList { list<exp> }
|
||||||
|
VarList { list<var> }
|
||||||
|
ArgList { list<var | "..."> }
|
||||||
|
|
||||||
|
AttNameList { list<Name Attrib> }
|
||||||
|
Attrib { ( "<" Name ">" )? }
|
||||||
|
|
||||||
|
exp {
|
||||||
|
kw<"nil"> | kw<"true"> | kw<"false"> | "..." |
|
||||||
|
Number |
|
||||||
|
LiteralString |
|
||||||
|
prefixexp |
|
||||||
|
BinaryExpression |
|
||||||
|
UnaryExpression |
|
||||||
|
TableConstructor |
|
||||||
|
FunctionDef { kw<"function"> FuncBody }
|
||||||
|
}
|
||||||
|
|
||||||
|
field[@isGroup=Field] {
|
||||||
|
FieldDynamic { "[" exp "]" "=" exp } |
|
||||||
|
FieldProp { Name "=" exp } |
|
||||||
|
FieldExp { exp }
|
||||||
|
}
|
||||||
|
|
||||||
|
prefixexp {
|
||||||
|
var |
|
||||||
|
Parens { "(" exp ")" ~parens } |
|
||||||
|
FunctionCall ~fcall
|
||||||
|
}
|
||||||
|
FunctionCall { prefixexp (":" Name)? !call args }
|
||||||
|
args {
|
||||||
|
LiteralString |
|
||||||
|
TableConstructor |
|
||||||
|
funcParams[@dynamicPrecedence=1] { "(" list<exp>? ")" ~parens }
|
||||||
|
}
|
||||||
|
|
||||||
|
var {
|
||||||
|
Name | Property { (prefixexp "." Name) } | MemberExpression { (prefixexp "[" exp "]") }
|
||||||
|
}
|
||||||
|
|
||||||
|
kw<term> { @specialize[@name={term}]<identifier, term> }
|
||||||
|
|
||||||
|
Name { identifier }
|
||||||
|
Label { "::" Name "::" }
|
||||||
|
LiteralString { simpleString }
|
||||||
|
|
||||||
|
BinaryExpression {
|
||||||
|
exp !or kw<"or"> exp |
|
||||||
|
exp !and kw<"and"> exp |
|
||||||
|
exp !compare CompareOp exp |
|
||||||
|
exp !bitor BitOp{"|"} exp |
|
||||||
|
exp !bitand BitOp{"&"} exp |
|
||||||
|
exp !xor BitOp{"~"} exp |
|
||||||
|
exp !shift BitOp{"<<" | ">>"} exp |
|
||||||
|
exp !concat ".." exp |
|
||||||
|
exp !plus ArithOp{"+" | minus} exp |
|
||||||
|
exp !times ArithOp{"*" | "/" | "%" | "//"} exp |
|
||||||
|
exp !power ArithOp{"^"} exp
|
||||||
|
}
|
||||||
|
|
||||||
|
UnaryExpression {
|
||||||
|
!prefix kw<"not"> exp |
|
||||||
|
!prefix (ArithOp{"+" | minus} | BitOp{"~"}) exp
|
||||||
|
}
|
||||||
|
|
||||||
|
TableConstructor { "{" (field (fieldsep field)* fieldsep?)? "}" }
|
||||||
|
|
||||||
|
@tokens {
|
||||||
|
CompareOp { "<" | ">" | $[<>=~/] "=" }
|
||||||
|
|
||||||
|
word { std.asciiLetter (std.digit | std.asciiLetter)* }
|
||||||
|
|
||||||
|
identifier { word }
|
||||||
|
|
||||||
|
stringEscape {
|
||||||
|
"\\" ($[abfnz"'\\] | digit digit? digit?) |
|
||||||
|
"\\x" hex hex |
|
||||||
|
// NOTE: this should really be /[0-7]hex{5}/ at max, but that's annoying to write
|
||||||
|
"\\u{" hex+ "}"
|
||||||
|
}
|
||||||
|
|
||||||
|
simpleString { "'" (stringEscape | ![\r\n\\'])+ "'" | '"' (stringEscape | ![\r\n\\"])+ '"'}
|
||||||
|
|
||||||
|
hex { $[0-9a-fA-F] }
|
||||||
|
digit { std.digit }
|
||||||
|
|
||||||
|
Number {
|
||||||
|
digit+ ("." digit+)? ($[eE] $[+\-] digit+)? |
|
||||||
|
"0" $[xX] hex+ ("." hex+)? ($[pP] $[+/-] digit+)?
|
||||||
|
}
|
||||||
|
|
||||||
|
Comment { "--" ![\n\r]* }
|
||||||
|
|
||||||
|
space { ($[ \t\f] | "\\" $[\n\r])+ }
|
||||||
|
newline { $[\n\r] | "\n\r" | "\r\n" }
|
||||||
|
|
||||||
|
"..."[@name=Ellipsis]
|
||||||
|
".."[@name=Concat]
|
||||||
|
|
||||||
|
@precedence { Comment, minus }
|
||||||
|
|
||||||
|
minus {"-"}
|
||||||
|
fieldsep { $[,;] }
|
||||||
|
|
||||||
|
"(" ")" "[" "]" "{" "}"
|
||||||
|
|
||||||
|
"." "," ";" ":" "::"
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { parse } from "$common/space_lua/parse.ts";
|
||||||
|
import { assertEquals } from "@std/assert/equals";
|
||||||
|
|
||||||
|
Deno.test("Test Lua parser", () => {
|
||||||
|
// Basic block test
|
||||||
|
parse(`
|
||||||
|
print("Hello, World!")
|
||||||
|
print(10)
|
||||||
|
`);
|
||||||
|
parse("");
|
||||||
|
// Expression tests
|
||||||
|
parse(
|
||||||
|
`e(1, 1.2, -3.8, +4, true, false, nil, "string", "Hello there \x00", ...)`,
|
||||||
|
);
|
||||||
|
parse(`e(10 << 10, 10 >> 10, 10 & 10, 10 | 10, 10 ~ 10)`);
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
parse(`e(1 + 2 - 3 * 4 / 4)`),
|
||||||
|
parse(`e(1 + 2 - ((3 * 4) / 4))`),
|
||||||
|
);
|
||||||
|
parse(`e(true and false or true)`);
|
||||||
|
parse(`e(a < 3 and b > 4 or b == 5 or c <= 6 and d >= 7 or a /= 8)`);
|
||||||
|
parse(`e(a.b.c)`);
|
||||||
|
parse(`e((1+2))`);
|
||||||
|
|
||||||
|
// Table expressions
|
||||||
|
parse(`e({})`);
|
||||||
|
parse(`e({1, 2, 3, })`);
|
||||||
|
parse(`e({1 ; 2 ; 3})`);
|
||||||
|
parse(`e({a = 1, b = 2, c = 3})`);
|
||||||
|
parse(`e({[3] = 1, [10 * 10] = "sup"})`);
|
||||||
|
|
||||||
|
// Function calls
|
||||||
|
parse(`e(func(), func(1, 2, 3), a.b(), a.b.c:hello(), (a.b)(7))`);
|
||||||
|
|
||||||
|
// Function expression
|
||||||
|
parse(`function sayHi()
|
||||||
|
print("Hi")
|
||||||
|
end`);
|
||||||
|
parse(`e(function(a, b) end)`);
|
||||||
|
parse(`e(function(a, b, ...) end)`);
|
||||||
|
});
|
|
@ -0,0 +1,443 @@
|
||||||
|
import { lezerToParseTree } from "$common/markdown_parser/parse_tree.ts";
|
||||||
|
import {
|
||||||
|
type AST as CrudeAST,
|
||||||
|
parseTreeToAST,
|
||||||
|
} from "@silverbulletmd/silverbullet/lib/tree";
|
||||||
|
import { parser } from "./parse-lua.js";
|
||||||
|
import { styleTags } from "@lezer/highlight";
|
||||||
|
|
||||||
|
const luaStyleTags = styleTags({
|
||||||
|
// Identifier: t.variableName,
|
||||||
|
// TagIdentifier: t.variableName,
|
||||||
|
// GlobalIdentifier: t.variableName,
|
||||||
|
// String: t.string,
|
||||||
|
// Number: t.number,
|
||||||
|
// PageRef: ct.WikiLinkTag,
|
||||||
|
// BinExpression: t.operator,
|
||||||
|
// TernaryExpression: t.operator,
|
||||||
|
// Regex: t.regexp,
|
||||||
|
// "where limit select render Order OrderKW and or null as InKW NotKW BooleanKW each all":
|
||||||
|
// t.keyword,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const highlightingQueryParser = parser.configure({
|
||||||
|
props: [
|
||||||
|
luaStyleTags,
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export type LuaBlock = {
|
||||||
|
type: "Block";
|
||||||
|
statements: LuaStatement[];
|
||||||
|
};
|
||||||
|
|
||||||
|
// STATEMENTS
|
||||||
|
export type LuaReturnStatement = {
|
||||||
|
type: "Return";
|
||||||
|
expressions: LuaExpression[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaStatement =
|
||||||
|
| LuaSemicolonStatement
|
||||||
|
| LuaLabelStatement
|
||||||
|
| LuaBreakStatement
|
||||||
|
| LuaGotoStatement
|
||||||
|
| LuaReturnStatement
|
||||||
|
| LuaBlock
|
||||||
|
| LuaWhileStatement
|
||||||
|
| LuaRepeatStatement
|
||||||
|
| LuaIfStatement
|
||||||
|
| LuaForStatement
|
||||||
|
| LuaForInStatement
|
||||||
|
| LuaFunctionStatement
|
||||||
|
| LuaLocalFunctionStatement
|
||||||
|
| LuaAssignmentStatement
|
||||||
|
| LuaLocalAssignmentStatement
|
||||||
|
| LuaFunctionCallStatement;
|
||||||
|
|
||||||
|
export type LuaSemicolonStatement = {
|
||||||
|
type: "Semicolon";
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaLabelStatement = {
|
||||||
|
type: "Label";
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaBreakStatement = {
|
||||||
|
type: "Break";
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaGotoStatement = {
|
||||||
|
type: "Goto";
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaWhileStatement = {
|
||||||
|
type: "While";
|
||||||
|
condition: LuaExpression;
|
||||||
|
block: LuaBlock;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaRepeatStatement = {
|
||||||
|
type: "Repeat";
|
||||||
|
block: LuaBlock;
|
||||||
|
condition: LuaExpression;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaIfStatement = {
|
||||||
|
type: "If";
|
||||||
|
conditions: { condition: LuaExpression; block: LuaBlock }[];
|
||||||
|
elseBlock?: LuaBlock;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaForStatement = {
|
||||||
|
type: "For";
|
||||||
|
name: string;
|
||||||
|
start: LuaExpression;
|
||||||
|
end: LuaExpression;
|
||||||
|
step?: LuaExpression;
|
||||||
|
block: LuaBlock;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaForInStatement = {
|
||||||
|
type: "ForIn";
|
||||||
|
names: string[];
|
||||||
|
expressions: LuaExpression[];
|
||||||
|
block: LuaBlock;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaFunctionStatement = {
|
||||||
|
type: "Function";
|
||||||
|
name: LuaFunctionName;
|
||||||
|
body: LuaFunctionBody;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaLocalFunctionStatement = {
|
||||||
|
type: "LocalFunction";
|
||||||
|
name: string;
|
||||||
|
body: LuaFunctionBody;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaFunctionName = {
|
||||||
|
type: "FunctionName";
|
||||||
|
name: string;
|
||||||
|
propNames?: string[];
|
||||||
|
colonName?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaFunctionBody = {
|
||||||
|
type: "FunctionBody";
|
||||||
|
parameters: string[];
|
||||||
|
block: LuaBlock;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaAssignmentStatement = {
|
||||||
|
type: "Assignment";
|
||||||
|
variables: LuaExpression[];
|
||||||
|
expressions: LuaExpression[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaLocalAssignmentStatement = {
|
||||||
|
type: "LocalAssignment";
|
||||||
|
names: string[];
|
||||||
|
expressions: LuaExpression[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaFunctionCallStatement = {
|
||||||
|
type: "FunctionCallStatement";
|
||||||
|
name: string;
|
||||||
|
args: LuaExpression[];
|
||||||
|
};
|
||||||
|
|
||||||
|
// EXPRESSIONS
|
||||||
|
export type LuaExpression =
|
||||||
|
| LuaNilLiteral
|
||||||
|
| LuaBooleanLiteral
|
||||||
|
| LuaNumberLiteral
|
||||||
|
| LuaStringLiteral
|
||||||
|
| LuaPrefixExpression
|
||||||
|
| LuaBinaryExpression
|
||||||
|
| LuaUnaryExpression
|
||||||
|
| LuaTableConstructor
|
||||||
|
| LuaFunctionDefinition;
|
||||||
|
|
||||||
|
export type LuaNilLiteral = {
|
||||||
|
type: "Nil";
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaBooleanLiteral = {
|
||||||
|
type: "Boolean";
|
||||||
|
value: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaNumberLiteral = {
|
||||||
|
type: "Number";
|
||||||
|
value: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaStringLiteral = {
|
||||||
|
type: "String";
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaPrefixExpression =
|
||||||
|
| LuaVariableExpression
|
||||||
|
| LuaParenthesizedExpression
|
||||||
|
| LuaFunctionCallExpression;
|
||||||
|
|
||||||
|
export type LuaParenthesizedExpression = {
|
||||||
|
type: "Parenthesized";
|
||||||
|
expression: LuaExpression;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaVariableExpression =
|
||||||
|
| LuaVariable
|
||||||
|
| LuaPropertyAccessExpression
|
||||||
|
| LuaTableAccessExpression;
|
||||||
|
|
||||||
|
export type LuaVariable = {
|
||||||
|
type: "Variable";
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaPropertyAccessExpression = {
|
||||||
|
type: "PropertyAccess";
|
||||||
|
object: LuaPrefixExpression;
|
||||||
|
property: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaTableAccessExpression = {
|
||||||
|
type: "TableAccess";
|
||||||
|
object: LuaPrefixExpression;
|
||||||
|
key: LuaExpression;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaFunctionCallExpression = {
|
||||||
|
type: "FunctionCall";
|
||||||
|
prefix: LuaPrefixExpression;
|
||||||
|
name?: string;
|
||||||
|
args: LuaExpression[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaBinaryExpression = {
|
||||||
|
type: "Binary";
|
||||||
|
operator: string;
|
||||||
|
left: LuaExpression;
|
||||||
|
right: LuaExpression;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaUnaryExpression = {
|
||||||
|
type: "Unary";
|
||||||
|
operator: string;
|
||||||
|
argument: LuaExpression;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaTableConstructor = {
|
||||||
|
type: "TableConstructor";
|
||||||
|
fields: LuaTableField[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaTableField =
|
||||||
|
| LuaDynamicField
|
||||||
|
| LuaPropField
|
||||||
|
| LuaExpressionField;
|
||||||
|
|
||||||
|
export type LuaDynamicField = {
|
||||||
|
type: "DynamicField";
|
||||||
|
key: LuaExpression;
|
||||||
|
value: LuaExpression;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaPropField = {
|
||||||
|
type: "PropField";
|
||||||
|
key: string;
|
||||||
|
value: LuaExpression;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaExpressionField = {
|
||||||
|
type: "ExpressionField";
|
||||||
|
value: LuaExpression;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type LuaFunctionDefinition = {
|
||||||
|
type: "FunctionDefinition";
|
||||||
|
body: LuaFunctionBody;
|
||||||
|
};
|
||||||
|
|
||||||
|
function parseChunk(n: CrudeAST): LuaBlock {
|
||||||
|
const t = n as [string, ...CrudeAST[]];
|
||||||
|
if (t[0] !== "Chunk") {
|
||||||
|
throw new Error(`Expected Chunk, got ${t[0]}`);
|
||||||
|
}
|
||||||
|
return parseBlock(t[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseBlock(n: CrudeAST): LuaBlock {
|
||||||
|
const t = n as [string, ...CrudeAST[]];
|
||||||
|
if (t[0] !== "Block") {
|
||||||
|
throw new Error(`Expected Block, got ${t[0]}`);
|
||||||
|
}
|
||||||
|
const statements = t.slice(1).map(parseStatement);
|
||||||
|
return { type: "Block", statements };
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseStatement(n: CrudeAST): LuaStatement {
|
||||||
|
const t = n as [string, ...CrudeAST[]];
|
||||||
|
switch (t[0]) {
|
||||||
|
case "Block":
|
||||||
|
return parseChunk(t[1]);
|
||||||
|
case "Semicolon":
|
||||||
|
return { type: "Semicolon" };
|
||||||
|
case "Label":
|
||||||
|
return { type: "Label", name: t[1] as string };
|
||||||
|
case "Break":
|
||||||
|
return { type: "Break" };
|
||||||
|
case "Goto":
|
||||||
|
return { type: "Goto", name: t[1] as string };
|
||||||
|
case "FunctionCall":
|
||||||
|
return {
|
||||||
|
type: "FunctionCallStatement",
|
||||||
|
name: t[1][1] as string,
|
||||||
|
args: t.slice(2, -1).filter((t) =>
|
||||||
|
![",", "(", ")"].includes(t[1] as string)
|
||||||
|
).map(parseExpression),
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
console.error(t);
|
||||||
|
throw new Error(`Unknown statement type: ${t[0]}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseExpression(n: CrudeAST): LuaExpression {
|
||||||
|
const t = n as [string, ...CrudeAST[]];
|
||||||
|
switch (t[0]) {
|
||||||
|
case "LiteralString": {
|
||||||
|
let cleanString = t[1] as string;
|
||||||
|
// Remove quotes etc
|
||||||
|
cleanString = cleanString.slice(1, -1);
|
||||||
|
return { type: "String", value: cleanString };
|
||||||
|
}
|
||||||
|
case "Number":
|
||||||
|
return { type: "Number", value: parseFloat(t[1] as string) };
|
||||||
|
case "BinaryExpression":
|
||||||
|
return {
|
||||||
|
type: "Binary",
|
||||||
|
operator: t[2][1] as string,
|
||||||
|
left: parseExpression(t[1]),
|
||||||
|
right: parseExpression(t[3]),
|
||||||
|
};
|
||||||
|
case "UnaryExpression":
|
||||||
|
return {
|
||||||
|
type: "Unary",
|
||||||
|
operator: t[1][1] as string,
|
||||||
|
argument: parseExpression(t[2]),
|
||||||
|
};
|
||||||
|
case "Property":
|
||||||
|
return {
|
||||||
|
type: "PropertyAccess",
|
||||||
|
object: parsePrefixExpression(t[1]),
|
||||||
|
property: t[3][1] as string,
|
||||||
|
};
|
||||||
|
|
||||||
|
case "Parens":
|
||||||
|
return parseExpression(t[2]);
|
||||||
|
case "FunctionCall": {
|
||||||
|
if (t[2][0] === ":") {
|
||||||
|
return {
|
||||||
|
type: "FunctionCall",
|
||||||
|
prefix: parsePrefixExpression(t[1]),
|
||||||
|
name: t[3][1] as string,
|
||||||
|
args: t.slice(4, -1).filter((t) =>
|
||||||
|
![",", "(", ")"].includes(t[1] as string)
|
||||||
|
).map(parseExpression),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: "FunctionCall",
|
||||||
|
prefix: parsePrefixExpression(t[1]),
|
||||||
|
args: t.slice(3, -1).filter((t) =>
|
||||||
|
![",", "(", ")"].includes(t[1] as string)
|
||||||
|
).map(parseExpression),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "Name":
|
||||||
|
return { type: "Variable", name: t[1] as string };
|
||||||
|
case "Ellipsis":
|
||||||
|
return { type: "Variable", name: "..." };
|
||||||
|
case "true":
|
||||||
|
return { type: "Boolean", value: true };
|
||||||
|
case "false":
|
||||||
|
return { type: "Boolean", value: false };
|
||||||
|
case "TableConstructor":
|
||||||
|
return {
|
||||||
|
type: "TableConstructor",
|
||||||
|
fields: t.slice(2, -1).filter((t) =>
|
||||||
|
!(typeof t === "string" ||
|
||||||
|
["{", "}"].includes(t[1] as string))
|
||||||
|
).map(parseTableField),
|
||||||
|
};
|
||||||
|
case "nil":
|
||||||
|
return { type: "Nil" };
|
||||||
|
default:
|
||||||
|
console.error(t);
|
||||||
|
throw new Error(`Unknown expression type: ${t[0]}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parsePrefixExpression(n: CrudeAST): LuaPrefixExpression {
|
||||||
|
const t = n as [string, ...CrudeAST[]];
|
||||||
|
switch (t[0]) {
|
||||||
|
case "Name":
|
||||||
|
return { type: "Variable", name: t[1] as string };
|
||||||
|
case "Property":
|
||||||
|
return {
|
||||||
|
type: "PropertyAccess",
|
||||||
|
object: parsePrefixExpression(t[1]),
|
||||||
|
property: t[3][1] as string,
|
||||||
|
};
|
||||||
|
case "Parens":
|
||||||
|
return { type: "Parenthesized", expression: parseExpression(t[2]) };
|
||||||
|
default:
|
||||||
|
console.error(t);
|
||||||
|
throw new Error(`Unknown prefix expression type: ${t[0]}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseTableField(n: CrudeAST): LuaTableField {
|
||||||
|
const t = n as [string, ...CrudeAST[]];
|
||||||
|
switch (t[0]) {
|
||||||
|
case "FieldExp":
|
||||||
|
return {
|
||||||
|
type: "ExpressionField",
|
||||||
|
value: parseExpression(t[1]),
|
||||||
|
};
|
||||||
|
case "FieldProp":
|
||||||
|
return {
|
||||||
|
type: "PropField",
|
||||||
|
key: t[1][1] as string,
|
||||||
|
value: parseExpression(t[3]),
|
||||||
|
};
|
||||||
|
case "FieldDynamic":
|
||||||
|
return {
|
||||||
|
type: "DynamicField",
|
||||||
|
key: parseExpression(t[2]),
|
||||||
|
value: parseExpression(t[5]),
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
console.error(t);
|
||||||
|
throw new Error(`Unknown table field type: ${t[0]}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parse(t: string): LuaBlock {
|
||||||
|
const crudeAst = parseToCrudeAST(t);
|
||||||
|
console.log("Crude AST", JSON.stringify(crudeAst, null, 2));
|
||||||
|
const result = parseChunk(crudeAst);
|
||||||
|
console.log("Parsed AST", JSON.stringify(result, null, 2));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseToCrudeAST(t: string): CrudeAST {
|
||||||
|
return parseTreeToAST(lezerToParseTree(t, parser.parse(t).topNode), true);
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
QUERY_GRAMMAR=common/markdown_parser/query.grammar
|
QUERY_GRAMMAR=common/markdown_parser/query.grammar
|
||||||
EXPRESSION_GRAMMAR=common/markdown_parser/expression.grammar.generated
|
EXPRESSION_GRAMMAR=common/markdown_parser/expression.grammar.generated
|
||||||
TEMPLATE_GRAMMAR=common/template/template.grammar
|
TEMPLATE_GRAMMAR=common/template/template.grammar
|
||||||
|
LUA_GRAMMAR=common/space_lua/lua.grammar
|
||||||
LEZER_GENERATOR_VERSION=1.5.1
|
LEZER_GENERATOR_VERSION=1.5.1
|
||||||
|
|
||||||
# Generate a patched grammer for just expressions
|
# Generate a patched grammer for just expressions
|
||||||
|
@ -12,3 +13,4 @@ tail -n +2 $QUERY_GRAMMAR >> $EXPRESSION_GRAMMAR
|
||||||
deno run -A npm:@lezer/generator@$LEZER_GENERATOR_VERSION $QUERY_GRAMMAR -o common/markdown_parser/parse-query.js
|
deno run -A npm:@lezer/generator@$LEZER_GENERATOR_VERSION $QUERY_GRAMMAR -o common/markdown_parser/parse-query.js
|
||||||
deno run -A npm:@lezer/generator@$LEZER_GENERATOR_VERSION $EXPRESSION_GRAMMAR -o common/markdown_parser/parse-expression.js
|
deno run -A npm:@lezer/generator@$LEZER_GENERATOR_VERSION $EXPRESSION_GRAMMAR -o common/markdown_parser/parse-expression.js
|
||||||
deno run -A npm:@lezer/generator@$LEZER_GENERATOR_VERSION $TEMPLATE_GRAMMAR -o common/template/parse-template.js
|
deno run -A npm:@lezer/generator@$LEZER_GENERATOR_VERSION $TEMPLATE_GRAMMAR -o common/template/parse-template.js
|
||||||
|
deno run -A npm:@lezer/generator@$LEZER_GENERATOR_VERSION $LUA_GRAMMAR -o common/space_lua/parse-lua.js
|
Loading…
Reference in New Issue