diff --git a/common/space_lua/ast.ts b/common/space_lua/ast.ts index ef5f3ccc..c5bfe9a9 100644 --- a/common/space_lua/ast.ts +++ b/common/space_lua/ast.ts @@ -1,252 +1,252 @@ type ASTPosition = { - from?: number; - to?: number; + from?: number; + to?: number; }; export type LuaBlock = { - type: "Block"; - statements: LuaStatement[]; + type: "Block"; + statements: LuaStatement[]; } & ASTPosition; // STATEMENTS export type LuaReturnStatement = { - type: "Return"; - expressions: LuaExpression[]; + type: "Return"; + expressions: LuaExpression[]; } & ASTPosition; export type LuaStatement = - | LuaSemicolonStatement - | LuaLabelStatement - | LuaBreakStatement - | LuaGotoStatement - | LuaReturnStatement - | LuaBlock - | LuaWhileStatement - | LuaRepeatStatement - | LuaIfStatement - | LuaForStatement - | LuaForInStatement - | LuaFunctionStatement - | LuaLocalFunctionStatement - | LuaAssignmentStatement - | LuaLocalStatement - | LuaFunctionCallStatement; + | LuaSemicolonStatement + | LuaLabelStatement + | LuaBreakStatement + | LuaGotoStatement + | LuaReturnStatement + | LuaBlock + | LuaWhileStatement + | LuaRepeatStatement + | LuaIfStatement + | LuaForStatement + | LuaForInStatement + | LuaFunctionStatement + | LuaLocalFunctionStatement + | LuaAssignmentStatement + | LuaLocalStatement + | LuaFunctionCallStatement; export type LuaSemicolonStatement = { - type: "Semicolon"; + type: "Semicolon"; } & ASTPosition; export type LuaLabelStatement = { - type: "Label"; - name: string; + type: "Label"; + name: string; } & ASTPosition; export type LuaBreakStatement = { - type: "Break"; + type: "Break"; } & ASTPosition; export type LuaGotoStatement = { - type: "Goto"; - name: string; + type: "Goto"; + name: string; } & ASTPosition; export type LuaWhileStatement = { - type: "While"; - condition: LuaExpression; - block: LuaBlock; + type: "While"; + condition: LuaExpression; + block: LuaBlock; } & ASTPosition; export type LuaRepeatStatement = { - type: "Repeat"; - block: LuaBlock; - condition: LuaExpression; + type: "Repeat"; + block: LuaBlock; + condition: LuaExpression; } & ASTPosition; export type LuaIfStatement = { - type: "If"; - conditions: { condition: LuaExpression; block: LuaBlock }[]; - elseBlock?: LuaBlock; + type: "If"; + conditions: { condition: LuaExpression; block: LuaBlock }[]; + elseBlock?: LuaBlock; } & ASTPosition; export type LuaForStatement = { - type: "For"; - name: string; - start: LuaExpression; - end: LuaExpression; - step?: LuaExpression; - block: LuaBlock; + type: "For"; + name: string; + start: LuaExpression; + end: LuaExpression; + step?: LuaExpression; + block: LuaBlock; } & ASTPosition; export type LuaForInStatement = { - type: "ForIn"; - names: string[]; - expressions: LuaExpression[]; - block: LuaBlock; + type: "ForIn"; + names: string[]; + expressions: LuaExpression[]; + block: LuaBlock; } & ASTPosition; export type LuaFunctionStatement = { - type: "Function"; - name: LuaFunctionName; - body: LuaFunctionBody; + type: "Function"; + name: LuaFunctionName; + body: LuaFunctionBody; } & ASTPosition; export type LuaLocalFunctionStatement = { - type: "LocalFunction"; - name: string; - body: LuaFunctionBody; + type: "LocalFunction"; + name: string; + body: LuaFunctionBody; } & ASTPosition; export type LuaFunctionName = { - type: "FunctionName"; - propNames: string[]; - colonName?: string; + type: "FunctionName"; + propNames: string[]; + colonName?: string; } & ASTPosition; export type LuaFunctionBody = { - type: "FunctionBody"; - parameters: string[]; - block: LuaBlock; + type: "FunctionBody"; + parameters: string[]; + block: LuaBlock; } & ASTPosition; export type LuaAssignmentStatement = { - type: "Assignment"; - variables: LuaLValue[]; - expressions: LuaExpression[]; + type: "Assignment"; + variables: LuaLValue[]; + expressions: LuaExpression[]; } & ASTPosition; export type LuaLValue = - | LuaVariable - | LuaPropertyAccessExpression - | LuaTableAccessExpression; + | LuaVariable + | LuaPropertyAccessExpression + | LuaTableAccessExpression; export type LuaLocalStatement = { - type: "Local"; - names: LuaAttName[]; - expressions?: LuaExpression[]; + type: "Local"; + names: LuaAttName[]; + expressions?: LuaExpression[]; } & ASTPosition; export type LuaAttName = { - type: "AttName"; - name: string; - attribute?: string; + type: "AttName"; + name: string; + attribute?: string; } & ASTPosition; export type LuaFunctionCallStatement = { - type: "FunctionCallStatement"; - call: LuaFunctionCallExpression; + type: "FunctionCallStatement"; + call: LuaFunctionCallExpression; } & ASTPosition; // EXPRESSIONS export type LuaExpression = - | LuaNilLiteral - | LuaBooleanLiteral - | LuaNumberLiteral - | LuaStringLiteral - | LuaPrefixExpression - | LuaBinaryExpression - | LuaUnaryExpression - | LuaTableConstructor - | LuaFunctionDefinition; + | LuaNilLiteral + | LuaBooleanLiteral + | LuaNumberLiteral + | LuaStringLiteral + | LuaPrefixExpression + | LuaBinaryExpression + | LuaUnaryExpression + | LuaTableConstructor + | LuaFunctionDefinition; export type LuaNilLiteral = { - type: "Nil"; + type: "Nil"; } & ASTPosition; export type LuaBooleanLiteral = { - type: "Boolean"; - value: boolean; + type: "Boolean"; + value: boolean; } & ASTPosition; export type LuaNumberLiteral = { - type: "Number"; - value: number; + type: "Number"; + value: number; } & ASTPosition; export type LuaStringLiteral = { - type: "String"; - value: string; + type: "String"; + value: string; } & ASTPosition; export type LuaPrefixExpression = - | LuaVariableExpression - | LuaParenthesizedExpression - | LuaFunctionCallExpression; + | LuaVariableExpression + | LuaParenthesizedExpression + | LuaFunctionCallExpression; export type LuaParenthesizedExpression = { - type: "Parenthesized"; - expression: LuaExpression; + type: "Parenthesized"; + expression: LuaExpression; } & ASTPosition; export type LuaVariableExpression = - | LuaVariable - | LuaPropertyAccessExpression - | LuaTableAccessExpression; + | LuaVariable + | LuaPropertyAccessExpression + | LuaTableAccessExpression; export type LuaVariable = { - type: "Variable"; - name: string; + type: "Variable"; + name: string; } & ASTPosition; export type LuaPropertyAccessExpression = { - type: "PropertyAccess"; - object: LuaPrefixExpression; - property: string; + type: "PropertyAccess"; + object: LuaPrefixExpression; + property: string; } & ASTPosition; export type LuaTableAccessExpression = { - type: "TableAccess"; - object: LuaPrefixExpression; - key: LuaExpression; + type: "TableAccess"; + object: LuaPrefixExpression; + key: LuaExpression; } & ASTPosition; export type LuaFunctionCallExpression = { - type: "FunctionCall"; - prefix: LuaPrefixExpression; - name?: string; - args: LuaExpression[]; + type: "FunctionCall"; + prefix: LuaPrefixExpression; + name?: string; + args: LuaExpression[]; } & ASTPosition; export type LuaBinaryExpression = { - type: "Binary"; - operator: string; - left: LuaExpression; - right: LuaExpression; + type: "Binary"; + operator: string; + left: LuaExpression; + right: LuaExpression; } & ASTPosition; export type LuaUnaryExpression = { - type: "Unary"; - operator: string; - argument: LuaExpression; + type: "Unary"; + operator: string; + argument: LuaExpression; } & ASTPosition; export type LuaTableConstructor = { - type: "TableConstructor"; - fields: LuaTableField[]; + type: "TableConstructor"; + fields: LuaTableField[]; } & ASTPosition; export type LuaTableField = - | LuaDynamicField - | LuaPropField - | LuaExpressionField; + | LuaDynamicField + | LuaPropField + | LuaExpressionField; export type LuaDynamicField = { - type: "DynamicField"; - key: LuaExpression; - value: LuaExpression; + type: "DynamicField"; + key: LuaExpression; + value: LuaExpression; } & ASTPosition; export type LuaPropField = { - type: "PropField"; - key: string; - value: LuaExpression; + type: "PropField"; + key: string; + value: LuaExpression; } & ASTPosition; export type LuaExpressionField = { - type: "ExpressionField"; - value: LuaExpression; + type: "ExpressionField"; + value: LuaExpression; } & ASTPosition; export type LuaFunctionDefinition = { - type: "FunctionDefinition"; - body: LuaFunctionBody; + type: "FunctionDefinition"; + body: LuaFunctionBody; } & ASTPosition; diff --git a/common/space_lua/eval.test.ts b/common/space_lua/eval.test.ts index 5c1bde79..76783f1a 100644 --- a/common/space_lua/eval.test.ts +++ b/common/space_lua/eval.test.ts @@ -5,133 +5,133 @@ import type { LuaBlock, LuaFunctionCallStatement } from "./ast.ts"; import { evalExpression, evalStatement } from "./eval.ts"; function evalExpr(s: string, e = new LuaEnv()): any { - return evalExpression( - (parse(`e(${s})`).statements[0] as LuaFunctionCallStatement).call - .args[0], - e, - ); + return evalExpression( + (parse(`e(${s})`).statements[0] as LuaFunctionCallStatement).call + .args[0], + e, + ); } function evalBlock(s: string, e = new LuaEnv()): Promise { - return evalStatement(parse(s) as LuaBlock, e); + return evalStatement(parse(s) as LuaBlock, e); } Deno.test("Evaluator test", async () => { - const env = new LuaEnv(); - env.set("test", new LuaNativeJSFunction((n) => n)); - env.set("asyncTest", new LuaNativeJSFunction((n) => Promise.resolve(n))); + const env = new LuaEnv(); + env.set("test", new LuaNativeJSFunction((n) => n)); + env.set("asyncTest", new LuaNativeJSFunction((n) => Promise.resolve(n))); - // Basic arithmetic - assertEquals(evalExpr(`1 + 2 + 3 - 3`), 3); - assertEquals(evalExpr(`4 // 3`), 1); - assertEquals(evalExpr(`4 % 3`), 1); + // Basic arithmetic + assertEquals(evalExpr(`1 + 2 + 3 - 3`), 3); + assertEquals(evalExpr(`4 // 3`), 1); + assertEquals(evalExpr(`4 % 3`), 1); - // Strings - assertEquals(evalExpr(`"a" .. "b"`), "ab"); + // Strings + assertEquals(evalExpr(`"a" .. "b"`), "ab"); - // Logic - assertEquals(evalExpr(`true and false`), false); - assertEquals(evalExpr(`true or false`), true); - assertEquals(evalExpr(`not true`), false); + // Logic + assertEquals(evalExpr(`true and false`), false); + assertEquals(evalExpr(`true or false`), true); + assertEquals(evalExpr(`not true`), false); - // Tables - const tbl = evalExpr(`{3, 1, 2}`); - assertEquals(tbl.get(1), 3); - assertEquals(tbl.get(2), 1); - assertEquals(tbl.get(3), 2); - assertEquals(tbl.toArray(), [3, 1, 2]); + // Tables + const tbl = evalExpr(`{3, 1, 2}`); + assertEquals(tbl.get(1), 3); + assertEquals(tbl.get(2), 1); + assertEquals(tbl.get(3), 2); + assertEquals(tbl.toArray(), [3, 1, 2]); - assertEquals(evalExpr(`{name=test("Zef"), age=100}`, env).toObject(), { - name: "Zef", - age: 100, - }); + assertEquals(evalExpr(`{name=test("Zef"), age=100}`, env).toObject(), { + name: "Zef", + age: 100, + }); - assertEquals( - (await evalExpr(`{name="Zef", age=asyncTest(100)}`, env)).toObject(), - { - name: "Zef", - age: 100, - }, - ); + assertEquals( + (await evalExpr(`{name="Zef", age=asyncTest(100)}`, env)).toObject(), + { + name: "Zef", + age: 100, + }, + ); - assertEquals(evalExpr(`{[3+2]=1, ["a".."b"]=2}`).toObject(), { - 5: 1, - ab: 2, - }); + assertEquals(evalExpr(`{[3+2]=1, ["a".."b"]=2}`).toObject(), { + 5: 1, + ab: 2, + }); - assertEquals(evalExpr(`#{}`), 0); - assertEquals(evalExpr(`#{1, 2, 3}`), 3); + assertEquals(evalExpr(`#{}`), 0); + assertEquals(evalExpr(`#{1, 2, 3}`), 3); - // Unary operators - assertEquals(await evalExpr(`-asyncTest(3)`, env), -3); + // Unary operators + assertEquals(await evalExpr(`-asyncTest(3)`, env), -3); - // Function calls - assertEquals(singleResult(evalExpr(`test(3)`, env)), 3); - assertEquals(singleResult(await evalExpr(`asyncTest(3) + 1`, env)), 4); + // Function calls + assertEquals(singleResult(evalExpr(`test(3)`, env)), 3); + assertEquals(singleResult(await evalExpr(`asyncTest(3) + 1`, env)), 4); }); Deno.test("Statement evaluation", async () => { - const env = new LuaEnv(); - env.set("test", new LuaNativeJSFunction((n) => n)); - env.set("asyncTest", new LuaNativeJSFunction((n) => Promise.resolve(n))); + const env = new LuaEnv(); + env.set("test", new LuaNativeJSFunction((n) => n)); + env.set("asyncTest", new LuaNativeJSFunction((n) => Promise.resolve(n))); - assertEquals(undefined, await evalBlock(`a = 3`, env)); - assertEquals(env.get("a"), 3); - assertEquals(undefined, await evalBlock(`b = test(3)`, env)); - assertEquals(env.get("b"), 3); + assertEquals(undefined, await evalBlock(`a = 3`, env)); + assertEquals(env.get("a"), 3); + assertEquals(undefined, await evalBlock(`b = test(3)`, env)); + assertEquals(env.get("b"), 3); - await evalBlock(`c = asyncTest(3)`, env); - assertEquals(env.get("c"), 3); + await evalBlock(`c = asyncTest(3)`, env); + assertEquals(env.get("c"), 3); - // Multiple assignments - const env2 = new LuaEnv(); - assertEquals(undefined, await evalBlock(`a, b = 1, 2`, env2)); - assertEquals(env2.get("a"), 1); - assertEquals(env2.get("b"), 2); + // Multiple assignments + const env2 = new LuaEnv(); + assertEquals(undefined, await evalBlock(`a, b = 1, 2`, env2)); + assertEquals(env2.get("a"), 1); + assertEquals(env2.get("b"), 2); - // Other lvalues - const env3 = new LuaEnv(); - await evalBlock(`tbl = {1, 2, 3}`, env3); - await evalBlock(`tbl[1] = 3`, env3); - assertEquals(env3.get("tbl").toArray(), [3, 2, 3]); - await evalBlock("tbl.name = 'Zef'", env3); - assertEquals(env3.get("tbl").get("name"), "Zef"); - await evalBlock(`tbl[2] = {age=10}`, env3); - await evalBlock(`tbl[2].age = 20`, env3); - assertEquals(env3.get("tbl").get(2).get("age"), 20); + // Other lvalues + const env3 = new LuaEnv(); + await evalBlock(`tbl = {1, 2, 3}`, env3); + await evalBlock(`tbl[1] = 3`, env3); + assertEquals(env3.get("tbl").toArray(), [3, 2, 3]); + await evalBlock("tbl.name = 'Zef'", env3); + assertEquals(env3.get("tbl").get("name"), "Zef"); + await evalBlock(`tbl[2] = {age=10}`, env3); + await evalBlock(`tbl[2].age = 20`, env3); + assertEquals(env3.get("tbl").get(2).get("age"), 20); - // Blocks and scopes - const env4 = new LuaEnv(); - env4.set("print", new LuaNativeJSFunction(console.log)); - await evalBlock( - ` + // Blocks and scopes + const env4 = new LuaEnv(); + env4.set("print", new LuaNativeJSFunction(console.log)); + await evalBlock( + ` a = 1 do -- sets global a to 3 a = 3 print("The number is: " .. a) end`, - env4, - ); - assertEquals(env4.get("a"), 3); + env4, + ); + assertEquals(env4.get("a"), 3); - const env5 = new LuaEnv(); - env5.set("print", new LuaNativeJSFunction(console.log)); + const env5 = new LuaEnv(); + env5.set("print", new LuaNativeJSFunction(console.log)); - await evalBlock( - ` + await evalBlock( + ` a = 1 if a > 0 then a = 3 else a = 0 end`, - env5, - ); - assertEquals(env5.get("a"), 3); + env5, + ); + assertEquals(env5.get("a"), 3); - await evalBlock( - ` + await evalBlock( + ` if a < 0 then a = -1 elseif a > 0 then @@ -139,25 +139,25 @@ Deno.test("Statement evaluation", async () => { else a = 0 end`, - env5, - ); - assertEquals(env5.get("a"), 1); + env5, + ); + assertEquals(env5.get("a"), 1); - await evalBlock( - ` + await evalBlock( + ` var = 1 do local var var = 2 end`, - env5, - ); - assertEquals(env5.get("var"), 1); + env5, + ); + assertEquals(env5.get("var"), 1); - // While loop - const env6 = new LuaEnv(); - await evalBlock( - ` + // While loop + const env6 = new LuaEnv(); + await evalBlock( + ` c = 0 while true do c = c + 1 @@ -166,14 +166,14 @@ Deno.test("Statement evaluation", async () => { end end `, - env6, - ); - assertEquals(env6.get("c"), 3); + env6, + ); + assertEquals(env6.get("c"), 3); - // Repeat loop - const env7 = new LuaEnv(); - await evalBlock( - ` + // Repeat loop + const env7 = new LuaEnv(); + await evalBlock( + ` c = 0 repeat c = c + 1 @@ -182,20 +182,20 @@ Deno.test("Statement evaluation", async () => { end until false `, - env7, - ); - assertEquals(env7.get("c"), 3); + env7, + ); + assertEquals(env7.get("c"), 3); - // Function definition and calling - const env8 = new LuaEnv(); - env8.set("print", new LuaNativeJSFunction(console.log)); - await evalBlock( - ` + // Function definition and calling + const env8 = new LuaEnv(); + env8.set("print", new LuaNativeJSFunction(console.log)); + await evalBlock( + ` function test(a) return a + 1 end print("3 + 1 = " .. test(3)) `, - env8, - ); + env8, + ); }); diff --git a/common/space_lua/parse.test.ts b/common/space_lua/parse.test.ts index 4b6832bb..319bfa87 100644 --- a/common/space_lua/parse.test.ts +++ b/common/space_lua/parse.test.ts @@ -1,86 +1,85 @@ import { parse } from "$common/space_lua/parse.ts"; - Deno.test("Test Lua parser", () => { - // Basic block test - parse(` + // Basic block test + parse(` print("Hello, World!") print(10) `); - parse(""); - // Expression tests - parse( - `e(1, 1.2, -3.8, +4, #lst, true, false, nil, "string", "Hello there \x00", ...)`, - ); + parse(""); + // Expression tests + parse( + `e(1, 1.2, -3.8, +4, #lst, true, false, nil, "string", "Hello there \x00", ...)`, + ); - parse(`e(10 << 10, 10 >> 10, 10 & 10, 10 | 10, 10 ~ 10)`); + parse(`e(10 << 10, 10 >> 10, 10 & 10, 10 | 10, 10 ~ 10)`); - 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))`); + 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"})`); + // 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 calls + parse(`e(func(), func(1, 2, 3), a.b(), a.b.c:hello(), (a.b)(7))`); - // Function expression - parse(`e(function(a, b) test() end)`); - parse(`e(function(a, b, ...) end)`); + // Function expression + parse(`e(function(a, b) test() end)`); + parse(`e(function(a, b, ...) end)`); - // Statements - parse(`do end`); - parse(`do print() end`); - parse(`::hello:: + // Statements + parse(`do end`); + parse(`do print() end`); + parse(`::hello:: goto hello`); - parse(`while true do print() end`); - parse(`repeat print() until false`); - parse( - `if 1 == 2 then print() elseif 1 < 2 then print2() else print3() end`, - ); - parse(`if true then print() end`); - parse(`if true then print() else print2() end`); - parse(`if true then print() elseif false then print2() end`); + parse(`while true do print() end`); + parse(`repeat print() until false`); + parse( + `if 1 == 2 then print() elseif 1 < 2 then print2() else print3() end`, + ); + parse(`if true then print() end`); + parse(`if true then print() else print2() end`); + parse(`if true then print() elseif false then print2() end`); - // For loops - parse(`for i = 1, 10, 1 do print(i) end`); - parse(`for i = 1, 10 do print(i) end`); - parse(`for el in each({1, 2, 3}) do print(i) end`); - parse(`for i, l in 1, pairs() do print(i) end`); + // For loops + parse(`for i = 1, 10, 1 do print(i) end`); + parse(`for i = 1, 10 do print(i) end`); + parse(`for el in each({1, 2, 3}) do print(i) end`); + parse(`for i, l in 1, pairs() do print(i) end`); - // Function statements - parse(`function a() end`); - parse(`function a:b() end`); - parse(`function a.b.c:d() end`); - parse(`function a.b.c() end`); - parse(`function hello(a, b) end`); - parse(`function hello(a, b, ...) end`); - parse(`local function hello() end`); + // Function statements + parse(`function a() end`); + parse(`function a:b() end`); + parse(`function a.b.c:d() end`); + parse(`function a.b.c() end`); + parse(`function hello(a, b) end`); + parse(`function hello(a, b, ...) end`); + parse(`local function hello() end`); - // Assignments, local variables etc. - parse(`a = 1`); - parse(`a, b = 1, 2`); - parse(`a.b.c = 1`); - parse(`a["name"] = 1`); - parse(`local a, b`); - parse(`local a = 1`); - parse(`local a = 4`); - parse(`local a, b = 1, 2`); + // Assignments, local variables etc. + parse(`a = 1`); + parse(`a, b = 1, 2`); + parse(`a.b.c = 1`); + parse(`a["name"] = 1`); + parse(`local a, b`); + parse(`local a = 1`); + parse(`local a = 4`); + parse(`local a, b = 1, 2`); - // Function calls - parse(`a(1, 2, 3)`); - parse(`print "Sup"`); - parse(`e(1 + print "8")`); + // Function calls + parse(`a(1, 2, 3)`); + parse(`print "Sup"`); + parse(`e(1 + print "8")`); - // Return statements - parse(`return`); - parse(`return 1`); - parse(`return 1, 2, 3`); - // return; + // Return statements + parse(`return`); + parse(`return 1`); + parse(`return 1, 2, 3`); + // return; }); diff --git a/common/space_lua/parse.ts b/common/space_lua/parse.ts index c4bac872..a878a4f5 100644 --- a/common/space_lua/parse.ts +++ b/common/space_lua/parse.ts @@ -1,519 +1,515 @@ import { lezerToParseTree } from "$common/markdown_parser/parse_tree.ts"; import { - cleanTree, - type ParseTree, + cleanTree, + type ParseTree, } from "@silverbulletmd/silverbullet/lib/tree"; import { parser } from "./parse-lua.js"; import { styleTags } from "@lezer/highlight"; import type { - LuaAttName, - LuaBlock, - LuaExpression, - LuaFunctionBody, - LuaFunctionCallExpression, - LuaFunctionName, - LuaLValue, - LuaPrefixExpression, - LuaStatement, - LuaTableField, + LuaAttName, + LuaBlock, + LuaExpression, + LuaFunctionBody, + LuaFunctionCallExpression, + LuaFunctionName, + LuaLValue, + LuaPrefixExpression, + LuaStatement, + LuaTableField, } from "./ast.ts"; 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, + // 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, - ], + props: [ + luaStyleTags, + ], }); function parseChunk(t: ParseTree): LuaBlock { - if (t.type !== "Chunk") { - throw new Error(`Expected Chunk, got ${t.type}`); - } - return parseBlock(t.children![0]); + if (t.type !== "Chunk") { + throw new Error(`Expected Chunk, got ${t.type}`); + } + return parseBlock(t.children![0]); } function parseBlock(t: ParseTree): LuaBlock { - if (t.type !== "Block") { - throw new Error(`Expected Block, got ${t.type}`); - } - const statements = t.children!.map(parseStatement); - return { type: "Block", statements, from: t.from, to: t.to }; + if (t.type !== "Block") { + throw new Error(`Expected Block, got ${t.type}`); + } + const statements = t.children!.map(parseStatement); + return { type: "Block", statements, from: t.from, to: t.to }; } function parseStatement(t: ParseTree): LuaStatement { - switch (t.type) { - case "Block": - return parseChunk(t.children![0]); - case "Semicolon": - return { type: "Semicolon", from: t.from, to: t.to }; - case "Label": - return { - type: "Label", - name: t.children![1].children![0].text!, - from: t.from, - to: t.to, - }; - case "Break": - return { type: "Break", from: t.from, to: t.to }; - case "Goto": - return { - type: "Goto", - name: t.children![1].children![0].text!, - from: t.from, - to: t.to, - }; - case "Scope": - return parseBlock(t.children![1]); - case ";": - return { type: "Semicolon", from: t.from, to: t.to }; - case "WhileStatement": - return { - type: "While", - condition: parseExpression(t.children![1]), - block: parseBlock(t.children![3]), - }; - case "RepeatStatement": - return { - type: "Repeat", - block: parseBlock(t.children![1]), - condition: parseExpression(t.children![3]), - }; - case "IfStatement": { - const conditions: { - condition: LuaExpression; - block: LuaBlock; - from?: number; - to?: number; - }[] = []; - let elseBlock: LuaBlock | undefined = undefined; - for (let i = 0; i < t.children!.length; i += 4) { - console.log("Looking at", t.children![i]); - const child = t.children![i]; - if ( - child.children![0].text === "if" || - child.children![0].text === "elseif" - ) { - conditions.push({ - condition: parseExpression(t.children![i + 1]), - block: parseBlock(t.children![i + 3]), - from: child.from, - to: child.to, - }); - } else if (child.children![0].text === "else") { - elseBlock = parseBlock(t.children![i + 1]); - } else if (child.children![0].text === "end") { - break; - } else { - throw new Error( - `Unknown if clause type: ${child.children![0].text}`, - ); - } - } - return { - type: "If", - conditions, - elseBlock, - from: t.from, - to: t.to, - }; + switch (t.type) { + case "Block": + return parseChunk(t.children![0]); + case "Semicolon": + return { type: "Semicolon", from: t.from, to: t.to }; + case "Label": + return { + type: "Label", + name: t.children![1].children![0].text!, + from: t.from, + to: t.to, + }; + case "Break": + return { type: "Break", from: t.from, to: t.to }; + case "Goto": + return { + type: "Goto", + name: t.children![1].children![0].text!, + from: t.from, + to: t.to, + }; + case "Scope": + return parseBlock(t.children![1]); + case ";": + return { type: "Semicolon", from: t.from, to: t.to }; + case "WhileStatement": + return { + type: "While", + condition: parseExpression(t.children![1]), + block: parseBlock(t.children![3]), + }; + case "RepeatStatement": + return { + type: "Repeat", + block: parseBlock(t.children![1]), + condition: parseExpression(t.children![3]), + }; + case "IfStatement": { + const conditions: { + condition: LuaExpression; + block: LuaBlock; + from?: number; + to?: number; + }[] = []; + let elseBlock: LuaBlock | undefined = undefined; + for (let i = 0; i < t.children!.length; i += 4) { + console.log("Looking at", t.children![i]); + const child = t.children![i]; + if ( + child.children![0].text === "if" || + child.children![0].text === "elseif" + ) { + conditions.push({ + condition: parseExpression(t.children![i + 1]), + block: parseBlock(t.children![i + 3]), + from: child.from, + to: child.to, + }); + } else if (child.children![0].text === "else") { + elseBlock = parseBlock(t.children![i + 1]); + } else if (child.children![0].text === "end") { + break; + } else { + throw new Error( + `Unknown if clause type: ${child.children![0].text}`, + ); } - case "ForStatement": - if (t.children![1].type === "ForNumeric") { - const forNumeric = t.children![1]; - return { - type: "For", - name: forNumeric.children![0].children![0].text!, - start: parseExpression(forNumeric.children![2]), - end: parseExpression(forNumeric.children![4]), - step: forNumeric.children![5] - ? parseExpression(forNumeric.children![6]) - : undefined, - block: parseBlock(t.children![3]), - from: t.from, - to: t.to, - }; - } else { - const forGeneric = t.children![1]; - return { - type: "ForIn", - names: parseNameList(forGeneric.children![0]), - expressions: parseExpList(forGeneric.children![2]), - block: parseBlock(t.children![3]), - from: t.from, - to: t.to, - }; - } - case "Function": - return { - type: "Function", - name: parseFunctionName(t.children![1]), - body: parseFunctionBody(t.children![2]), - from: t.from, - to: t.to, - }; - case "LocalFunction": - return { - type: "LocalFunction", - name: t.children![2].children![0].text!, - body: parseFunctionBody(t.children![3]), - }; - case "FunctionCall": - return { - type: "FunctionCallStatement", - call: parseExpression( - { - type: "FunctionCall", - children: t.children!, - from: t.from, - to: t.to, - }, - ) as LuaFunctionCallExpression, - }; - case "Assign": - return { - type: "Assignment", - variables: t.children![0].children!.filter((t) => - t.type !== "," - ).map( - parseLValue, - ), - expressions: parseExpList(t.children![2]), - from: t.from, - to: t.to, - }; - case "Local": - return { - type: "Local", - names: parseAttNames(t.children![1]), - expressions: t.children![3] ? parseExpList(t.children![3]) : [], - from: t.from, - to: t.to, - }; - case "ReturnStatement": { - const expressions = t.children![1] - ? parseExpList(t.children![1]) - : []; - return { type: "Return", expressions, from: t.from, to: t.to }; - } - case "break": - return { type: "Break", from: t.from, to: t.to }; - default: - console.error(t); - throw new Error(`Unknown statement type: ${t.children![0].text}`); + } + return { + type: "If", + conditions, + elseBlock, + from: t.from, + to: t.to, + }; } + case "ForStatement": + if (t.children![1].type === "ForNumeric") { + const forNumeric = t.children![1]; + return { + type: "For", + name: forNumeric.children![0].children![0].text!, + start: parseExpression(forNumeric.children![2]), + end: parseExpression(forNumeric.children![4]), + step: forNumeric.children![5] + ? parseExpression(forNumeric.children![6]) + : undefined, + block: parseBlock(t.children![3]), + from: t.from, + to: t.to, + }; + } else { + const forGeneric = t.children![1]; + return { + type: "ForIn", + names: parseNameList(forGeneric.children![0]), + expressions: parseExpList(forGeneric.children![2]), + block: parseBlock(t.children![3]), + from: t.from, + to: t.to, + }; + } + case "Function": + return { + type: "Function", + name: parseFunctionName(t.children![1]), + body: parseFunctionBody(t.children![2]), + from: t.from, + to: t.to, + }; + case "LocalFunction": + return { + type: "LocalFunction", + name: t.children![2].children![0].text!, + body: parseFunctionBody(t.children![3]), + }; + case "FunctionCall": + return { + type: "FunctionCallStatement", + call: parseExpression( + { + type: "FunctionCall", + children: t.children!, + from: t.from, + to: t.to, + }, + ) as LuaFunctionCallExpression, + }; + case "Assign": + return { + type: "Assignment", + variables: t.children![0].children!.filter((t) => t.type !== ",").map( + parseLValue, + ), + expressions: parseExpList(t.children![2]), + from: t.from, + to: t.to, + }; + case "Local": + return { + type: "Local", + names: parseAttNames(t.children![1]), + expressions: t.children![3] ? parseExpList(t.children![3]) : [], + from: t.from, + to: t.to, + }; + case "ReturnStatement": { + const expressions = t.children![1] ? parseExpList(t.children![1]) : []; + return { type: "Return", expressions, from: t.from, to: t.to }; + } + case "break": + return { type: "Break", from: t.from, to: t.to }; + default: + console.error(t); + throw new Error(`Unknown statement type: ${t.children![0].text}`); + } } function parseAttNames(t: ParseTree): LuaAttName[] { - if (t.type !== "AttNameList") { - throw new Error(`Expected AttNameList, got ${t.type}`); - } - return t.children!.filter((t) => t.type !== ",").map(parseAttName); + if (t.type !== "AttNameList") { + throw new Error(`Expected AttNameList, got ${t.type}`); + } + return t.children!.filter((t) => t.type !== ",").map(parseAttName); } function parseAttName(t: ParseTree): LuaAttName { - if (t.type !== "AttName") { - throw new Error(`Expected AttName, got ${t.type}`); - } - return { - type: "AttName", - name: t.children![0].children![0].text!, - attribute: t.children![1].children![1] - ? t.children![1].children![1].children![0].text! - : undefined, - from: t.from, - to: t.to, - }; + if (t.type !== "AttName") { + throw new Error(`Expected AttName, got ${t.type}`); + } + return { + type: "AttName", + name: t.children![0].children![0].text!, + attribute: t.children![1].children![1] + ? t.children![1].children![1].children![0].text! + : undefined, + from: t.from, + to: t.to, + }; } function parseLValue(t: ParseTree): LuaLValue { - switch (t.type) { - case "Name": - return { - type: "Variable", - name: t.children![0].text!, - from: t.from, - to: t.to, - }; - case "Property": - return { - type: "PropertyAccess", - object: parsePrefixExpression(t.children![0]), - property: t.children![2].children![0].text!, - from: t.from, - to: t.to, - }; - case "MemberExpression": - return { - type: "TableAccess", - object: parsePrefixExpression(t.children![0]), - key: parseExpression(t.children![2]), - from: t.from, - to: t.to, - }; - default: - console.error(t); - throw new Error(`Unknown lvalue type: ${t.type}`); - } + switch (t.type) { + case "Name": + return { + type: "Variable", + name: t.children![0].text!, + from: t.from, + to: t.to, + }; + case "Property": + return { + type: "PropertyAccess", + object: parsePrefixExpression(t.children![0]), + property: t.children![2].children![0].text!, + from: t.from, + to: t.to, + }; + case "MemberExpression": + return { + type: "TableAccess", + object: parsePrefixExpression(t.children![0]), + key: parseExpression(t.children![2]), + from: t.from, + to: t.to, + }; + default: + console.error(t); + throw new Error(`Unknown lvalue type: ${t.type}`); + } } function parseFunctionName(t: ParseTree): LuaFunctionName { - if (t.type !== "FuncName") { - throw new Error(`Expected FunctionName, got ${t.type}`); + if (t.type !== "FuncName") { + throw new Error(`Expected FunctionName, got ${t.type}`); + } + const propNames: string[] = []; + let colonName: string | undefined = undefined; + for (let i = 0; i < t.children!.length; i += 2) { + const prop = t.children![i]; + propNames.push(prop.children![0].text!); + if (t.children![i + 1] && t.children![i + 1].type === ":") { + colonName = t.children![i + 2].children![0].text!; + break; } - const propNames: string[] = []; - let colonName: string | undefined = undefined; - for (let i = 0; i < t.children!.length; i += 2) { - const prop = t.children![i]; - propNames.push(prop.children![0].text!); - if (t.children![i + 1] && t.children![i + 1].type === ":") { - colonName = t.children![i + 2].children![0].text!; - break; - } - } - return { - type: "FunctionName", - propNames, - colonName, - from: t.from, - to: t.to, - }; + } + return { + type: "FunctionName", + propNames, + colonName, + from: t.from, + to: t.to, + }; } function parseNameList(t: ParseTree): string[] { - if (t.type !== "NameList") { - throw new Error(`Expected NameList, got ${t.type}`); - } - return t.children!.filter((t) => t.type === "Name").map((t) => - t.children![0].text! - ); + if (t.type !== "NameList") { + throw new Error(`Expected NameList, got ${t.type}`); + } + return t.children!.filter((t) => t.type === "Name").map((t) => + t.children![0].text! + ); } function parseExpList(t: ParseTree): LuaExpression[] { - if (t.type !== "ExpList") { - throw new Error(`Expected ExpList, got ${t.type}`); - } - return t.children!.filter((t) => t.type !== ",").map(parseExpression); + if (t.type !== "ExpList") { + throw new Error(`Expected ExpList, got ${t.type}`); + } + return t.children!.filter((t) => t.type !== ",").map(parseExpression); } function parseExpression(t: ParseTree): LuaExpression { - switch (t.type) { - case "LiteralString": { - let cleanString = t.children![0].text!; - // Remove quotes etc - cleanString = cleanString.slice(1, -1); - return { - type: "String", - value: cleanString, - from: t.from, - to: t.to, - }; - } - case "Number": - return { - type: "Number", - value: parseFloat(t.children![0].text!), - from: t.from, - to: t.to, - }; - case "BinaryExpression": - return { - type: "Binary", - operator: t.children![1].children![0].text!, - left: parseExpression(t.children![0]), - right: parseExpression(t.children![2]), - from: t.from, - to: t.to, - }; - case "UnaryExpression": - return { - type: "Unary", - operator: t.children![0].children![0].text!, - argument: parseExpression(t.children![1]), - from: t.from, - to: t.to, - }; - case "Property": - return { - type: "PropertyAccess", - object: parsePrefixExpression(t.children![0]), - property: t.children![2].children![0].text!, - from: t.from, - to: t.to, - }; - - case "Parens": - return parseExpression(t.children![1]); - case "FunctionCall": { - if (t.children![1].type === ":") { - return { - type: "FunctionCall", - prefix: parsePrefixExpression(t.children![0]), - name: t.children![2].children![0].text!, - args: parseFunctionArgs(t.children!.slice(3)), - from: t.from, - to: t.to, - }; - } - return { - type: "FunctionCall", - prefix: parsePrefixExpression(t.children![0]), - args: parseFunctionArgs(t.children!.slice(1)), - from: t.from, - to: t.to, - }; - } - case "FunctionDef": { - const body = parseFunctionBody(t.children![1]); - return { - type: "FunctionDefinition", - body, - from: t.from, - to: t.to, - }; - } - case "Name": - return { - type: "Variable", - name: t.children![0].text!, - from: t.from, - to: t.to, - }; - case "Ellipsis": - return { type: "Variable", name: "...", from: t.from, to: t.to }; - case "true": - return { type: "Boolean", value: true, from: t.from, to: t.to }; - case "false": - return { type: "Boolean", value: false, from: t.from, to: t.to }; - case "TableConstructor": - return { - type: "TableConstructor", - fields: t.children!.slice(1, -1).filter((t) => - ["FieldExp", "FieldProp", "FieldDynamic"].includes(t.type!) - ).map(parseTableField), - from: t.from, - to: t.to, - }; - case "nil": - return { type: "Nil", from: t.from, to: t.to }; - default: - console.error(t); - throw new Error(`Unknown expression type: ${t.type}`); + switch (t.type) { + case "LiteralString": { + let cleanString = t.children![0].text!; + // Remove quotes etc + cleanString = cleanString.slice(1, -1); + return { + type: "String", + value: cleanString, + from: t.from, + to: t.to, + }; } + case "Number": + return { + type: "Number", + value: parseFloat(t.children![0].text!), + from: t.from, + to: t.to, + }; + case "BinaryExpression": + return { + type: "Binary", + operator: t.children![1].children![0].text!, + left: parseExpression(t.children![0]), + right: parseExpression(t.children![2]), + from: t.from, + to: t.to, + }; + case "UnaryExpression": + return { + type: "Unary", + operator: t.children![0].children![0].text!, + argument: parseExpression(t.children![1]), + from: t.from, + to: t.to, + }; + case "Property": + return { + type: "PropertyAccess", + object: parsePrefixExpression(t.children![0]), + property: t.children![2].children![0].text!, + from: t.from, + to: t.to, + }; + + case "Parens": + return parseExpression(t.children![1]); + case "FunctionCall": { + if (t.children![1].type === ":") { + return { + type: "FunctionCall", + prefix: parsePrefixExpression(t.children![0]), + name: t.children![2].children![0].text!, + args: parseFunctionArgs(t.children!.slice(3)), + from: t.from, + to: t.to, + }; + } + return { + type: "FunctionCall", + prefix: parsePrefixExpression(t.children![0]), + args: parseFunctionArgs(t.children!.slice(1)), + from: t.from, + to: t.to, + }; + } + case "FunctionDef": { + const body = parseFunctionBody(t.children![1]); + return { + type: "FunctionDefinition", + body, + from: t.from, + to: t.to, + }; + } + case "Name": + return { + type: "Variable", + name: t.children![0].text!, + from: t.from, + to: t.to, + }; + case "Ellipsis": + return { type: "Variable", name: "...", from: t.from, to: t.to }; + case "true": + return { type: "Boolean", value: true, from: t.from, to: t.to }; + case "false": + return { type: "Boolean", value: false, from: t.from, to: t.to }; + case "TableConstructor": + return { + type: "TableConstructor", + fields: t.children!.slice(1, -1).filter((t) => + ["FieldExp", "FieldProp", "FieldDynamic"].includes(t.type!) + ).map(parseTableField), + from: t.from, + to: t.to, + }; + case "nil": + return { type: "Nil", from: t.from, to: t.to }; + default: + console.error(t); + throw new Error(`Unknown expression type: ${t.type}`); + } } function parseFunctionArgs(ts: ParseTree[]): LuaExpression[] { - console.log("Parsing function args", JSON.stringify(ts, null, 2)); - return ts.filter((t) => ![",", "(", ")"].includes(t.type!)).map( - parseExpression, - ); + console.log("Parsing function args", JSON.stringify(ts, null, 2)); + return ts.filter((t) => ![",", "(", ")"].includes(t.type!)).map( + parseExpression, + ); } function parseFunctionBody(t: ParseTree): LuaFunctionBody { - if (t.type !== "FuncBody") { - throw new Error(`Expected FunctionBody, got ${t.type}`); - } - return { - type: "FunctionBody", - parameters: t.children![1].children!.filter((t) => - ["Name", "Ellipsis"].includes(t.type!) - ) - .map((t) => t.children![0].text!), - block: parseBlock(t.children![3]), - from: t.from, - to: t.to, - }; + if (t.type !== "FuncBody") { + throw new Error(`Expected FunctionBody, got ${t.type}`); + } + return { + type: "FunctionBody", + parameters: t.children![1].children!.filter((t) => + ["Name", "Ellipsis"].includes(t.type!) + ) + .map((t) => t.children![0].text!), + block: parseBlock(t.children![3]), + from: t.from, + to: t.to, + }; } function parsePrefixExpression(t: ParseTree): LuaPrefixExpression { - switch (t.type) { - case "Name": - return { - type: "Variable", - name: t.children![0].text!, - from: t.from, - to: t.to, - }; - case "Property": - return { - type: "PropertyAccess", - object: parsePrefixExpression(t.children![0]), - property: t.children![2].children![0].text!, - from: t.from, - to: t.to, - }; - case "MemberExpression": - return { - type: "TableAccess", - object: parsePrefixExpression(t.children![0]), - key: parseExpression(t.children![2]), - from: t.from, - to: t.to, - }; - case "Parens": - return { - type: "Parenthesized", - expression: parseExpression(t.children![1]), - from: t.from, - to: t.to, - }; - default: - console.error(t); - throw new Error(`Unknown prefix expression type: ${t.type}`); - } + switch (t.type) { + case "Name": + return { + type: "Variable", + name: t.children![0].text!, + from: t.from, + to: t.to, + }; + case "Property": + return { + type: "PropertyAccess", + object: parsePrefixExpression(t.children![0]), + property: t.children![2].children![0].text!, + from: t.from, + to: t.to, + }; + case "MemberExpression": + return { + type: "TableAccess", + object: parsePrefixExpression(t.children![0]), + key: parseExpression(t.children![2]), + from: t.from, + to: t.to, + }; + case "Parens": + return { + type: "Parenthesized", + expression: parseExpression(t.children![1]), + from: t.from, + to: t.to, + }; + default: + console.error(t); + throw new Error(`Unknown prefix expression type: ${t.type}`); + } } function parseTableField(t: ParseTree): LuaTableField { - switch (t.type) { - case "FieldExp": - return { - type: "ExpressionField", - value: parseExpression(t.children![0]), - from: t.from, - to: t.to, - }; - case "FieldProp": - return { - type: "PropField", - key: t.children![0].children![0].text!, - value: parseExpression(t.children![2]), - from: t.from, - to: t.to, - }; - case "FieldDynamic": - return { - type: "DynamicField", - key: parseExpression(t.children![1]), - value: parseExpression(t.children![4]), - from: t.from, - to: t.to, - }; - default: - console.error(t); - throw new Error(`Unknown table field type: ${t.type}`); - } + switch (t.type) { + case "FieldExp": + return { + type: "ExpressionField", + value: parseExpression(t.children![0]), + from: t.from, + to: t.to, + }; + case "FieldProp": + return { + type: "PropField", + key: t.children![0].children![0].text!, + value: parseExpression(t.children![2]), + from: t.from, + to: t.to, + }; + case "FieldDynamic": + return { + type: "DynamicField", + key: parseExpression(t.children![1]), + value: parseExpression(t.children![4]), + from: t.from, + to: t.to, + }; + default: + console.error(t); + throw new Error(`Unknown table field type: ${t.type}`); + } } export function parse(s: string): LuaBlock { - const t = parseToCrudeAST(s); - console.log("Clean tree", JSON.stringify(t, null, 2)); - const result = parseChunk(t); - console.log("Parsed AST", JSON.stringify(result, null, 2)); - return result; + const t = parseToCrudeAST(s); + console.log("Clean tree", JSON.stringify(t, null, 2)); + const result = parseChunk(t); + console.log("Parsed AST", JSON.stringify(result, null, 2)); + return result; } export function parseToCrudeAST(t: string): ParseTree { - return cleanTree(lezerToParseTree(t, parser.parse(t).topNode), true); + return cleanTree(lezerToParseTree(t, parser.parse(t).topNode), true); } diff --git a/common/space_lua/runtime.ts b/common/space_lua/runtime.ts index 20ead596..18e17810 100644 --- a/common/space_lua/runtime.ts +++ b/common/space_lua/runtime.ts @@ -2,52 +2,52 @@ import type { LuaFunctionBody } from "./ast.ts"; import { evalStatement } from "$common/space_lua/eval.ts"; export class LuaEnv implements ILuaSettable, ILuaGettable { - variables = new Map(); + variables = new Map(); - constructor(readonly parent?: LuaEnv) { - } + constructor(readonly parent?: LuaEnv) { + } - setLocal(name: string, value: LuaValue) { - this.variables.set(name, value); - } + setLocal(name: string, value: LuaValue) { + this.variables.set(name, value); + } - set(key: string, value: LuaValue): void { - if (this.variables.has(key) || !this.parent) { - this.variables.set(key, value); - } else { - this.parent.set(key, value); - } + set(key: string, value: LuaValue): void { + if (this.variables.has(key) || !this.parent) { + this.variables.set(key, value); + } else { + this.parent.set(key, value); } + } - get(name: string): LuaValue | undefined { - if (this.variables.has(name)) { - return this.variables.get(name); - } - if (this.parent) { - return this.parent.get(name); - } - return undefined; + get(name: string): LuaValue | undefined { + if (this.variables.has(name)) { + return this.variables.get(name); } + if (this.parent) { + return this.parent.get(name); + } + return undefined; + } } export class LuaMultiRes { - constructor(readonly values: any[]) { - } + constructor(readonly values: any[]) { + } - unwrap(): any { - if (this.values.length !== 1) { - throw new Error("Cannot unwrap multiple values"); - } - return this.values[0]; + unwrap(): any { + if (this.values.length !== 1) { + throw new Error("Cannot unwrap multiple values"); } + return this.values[0]; + } } export function singleResult(value: any): any { - if (value instanceof LuaMultiRes) { - return value.unwrap(); - } else { - return value; - } + if (value instanceof LuaMultiRes) { + return value.unwrap(); + } else { + return value; + } } // These types are for documentation only @@ -55,207 +55,207 @@ export type LuaValue = any; export type JSValue = any; export interface ILuaFunction { - call(...args: LuaValue[]): Promise | LuaValue; + call(...args: LuaValue[]): Promise | LuaValue; } export interface ILuaSettable { - set(key: LuaValue, value: LuaValue): void; + set(key: LuaValue, value: LuaValue): void; } export interface ILuaGettable { - get(key: LuaValue): LuaValue | undefined; + get(key: LuaValue): LuaValue | undefined; } export class LuaFunction implements ILuaFunction { - constructor(private body: LuaFunctionBody, private closure: LuaEnv) { - } + constructor(private body: LuaFunctionBody, private closure: LuaEnv) { + } - call(...args: LuaValue[]): Promise | LuaValue { - // Create a new environment for this function call - const env = new LuaEnv(this.closure); - // Assign the passed arguments to the parameters - for (let i = 0; i < this.body.parameters.length; i++) { - let arg = args[i]; - if (arg === undefined) { - arg = null; - } - env.set(this.body.parameters[i], arg); - } - return evalStatement(this.body.block, env).catch((e: any) => { - if (e instanceof LuaReturn) { - if (e.values.length === 0) { - return; - } else if (e.values.length === 1) { - return e.values[0]; - } else { - return new LuaMultiRes(e.values); - } - } else { - throw e; - } - }); + call(...args: LuaValue[]): Promise | LuaValue { + // Create a new environment for this function call + const env = new LuaEnv(this.closure); + // Assign the passed arguments to the parameters + for (let i = 0; i < this.body.parameters.length; i++) { + let arg = args[i]; + if (arg === undefined) { + arg = null; + } + env.set(this.body.parameters[i], arg); } + return evalStatement(this.body.block, env).catch((e: any) => { + if (e instanceof LuaReturn) { + if (e.values.length === 0) { + return; + } else if (e.values.length === 1) { + return e.values[0]; + } else { + return new LuaMultiRes(e.values); + } + } else { + throw e; + } + }); + } } export class LuaNativeJSFunction implements ILuaFunction { - constructor(readonly fn: (...args: JSValue[]) => JSValue) { - } + constructor(readonly fn: (...args: JSValue[]) => JSValue) { + } - call(...args: LuaValue[]): Promise | LuaValue { - const result = this.fn(...args.map(luaValueToJS)); - if (result instanceof Promise) { - return result.then(jsToLuaValue); - } else { - return jsToLuaValue(result); - } + call(...args: LuaValue[]): Promise | LuaValue { + const result = this.fn(...args.map(luaValueToJS)); + if (result instanceof Promise) { + return result.then(jsToLuaValue); + } else { + return jsToLuaValue(result); } + } } export class LuaTable implements ILuaSettable, ILuaGettable { - // To optimize the table implementation we use a combination of different data structures - // When tables are used as maps, the common case is that they are string keys, so we use a simple object for that - private stringKeys: Record; - // Other keys we can support using a Map as a fallback - private otherKeys: Map | null; - // When tables are used as arrays, we use a native JavaScript array for that - private arrayPart: any[]; + // To optimize the table implementation we use a combination of different data structures + // When tables are used as maps, the common case is that they are string keys, so we use a simple object for that + private stringKeys: Record; + // Other keys we can support using a Map as a fallback + private otherKeys: Map | null; + // When tables are used as arrays, we use a native JavaScript array for that + private arrayPart: any[]; - // TODO: Actually implement metatables - private metatable: LuaTable | null; + // TODO: Actually implement metatables + private metatable: LuaTable | null; - constructor() { - // For efficiency and performance reasons we pre-allocate these (modern JS engines are very good at optimizing this) - this.stringKeys = {}; - this.arrayPart = []; - this.otherKeys = null; // Only create this when needed - this.metatable = null; + constructor() { + // For efficiency and performance reasons we pre-allocate these (modern JS engines are very good at optimizing this) + this.stringKeys = {}; + this.arrayPart = []; + this.otherKeys = null; // Only create this when needed + this.metatable = null; + } + + get length(): number { + return this.arrayPart.length; + } + + set(key: LuaValue, value: LuaValue) { + if (typeof key === "string") { + this.stringKeys[key] = value; + } else if (Number.isInteger(key) && key >= 1) { + this.arrayPart[key - 1] = value; + } else { + if (!this.otherKeys) { + this.otherKeys = new Map(); + } + this.otherKeys.set(key, value); } + } - get length(): number { - return this.arrayPart.length; + get(key: LuaValue): LuaValue | undefined { + if (typeof key === "string") { + return this.stringKeys[key]; + } else if (Number.isInteger(key) && key >= 1) { + return this.arrayPart[key - 1]; + } else if (this.otherKeys) { + return this.otherKeys.get(key); } + return undefined; + } - set(key: LuaValue, value: LuaValue) { - if (typeof key === "string") { - this.stringKeys[key] = value; - } else if (Number.isInteger(key) && key >= 1) { - this.arrayPart[key - 1] = value; - } else { - if (!this.otherKeys) { - this.otherKeys = new Map(); - } - this.otherKeys.set(key, value); - } - } + toArray(): JSValue[] { + return this.arrayPart; + } - get(key: LuaValue): LuaValue | undefined { - if (typeof key === "string") { - return this.stringKeys[key]; - } else if (Number.isInteger(key) && key >= 1) { - return this.arrayPart[key - 1]; - } else if (this.otherKeys) { - return this.otherKeys.get(key); - } - return undefined; + toObject(): Record { + const result = { ...this.stringKeys }; + for (const i in this.arrayPart) { + result[parseInt(i) + 1] = this.arrayPart[i]; } + return result; + } - toArray(): JSValue[] { - return this.arrayPart; + static fromArray(arr: JSValue[]): LuaTable { + const table = new LuaTable(); + for (let i = 0; i < arr.length; i++) { + table.set(i + 1, arr[i]); } + return table; + } - toObject(): Record { - const result = { ...this.stringKeys }; - for (const i in this.arrayPart) { - result[parseInt(i) + 1] = this.arrayPart[i]; - } - return result; - } - - static fromArray(arr: JSValue[]): LuaTable { - const table = new LuaTable(); - for (let i = 0; i < arr.length; i++) { - table.set(i + 1, arr[i]); - } - return table; - } - - static fromObject(obj: Record): LuaTable { - const table = new LuaTable(); - for (const key in obj) { - table.set(key, obj[key]); - } - return table; + static fromObject(obj: Record): LuaTable { + const table = new LuaTable(); + for (const key in obj) { + table.set(key, obj[key]); } + return table; + } } export type LuaLValueContainer = { env: ILuaSettable; key: LuaValue }; export function luaSet(obj: any, key: any, value: any) { - if (obj instanceof LuaTable) { - obj.set(key, value); - } else { - obj[key] = value; - } + if (obj instanceof LuaTable) { + obj.set(key, value); + } else { + obj[key] = value; + } } export function luaGet(obj: any, key: any): any { - if (obj instanceof LuaTable) { - return obj.get(key); - } else { - return obj[key]; - } + if (obj instanceof LuaTable) { + return obj.get(key); + } else { + return obj[key]; + } } export function luaLen(obj: any): number { - if (obj instanceof LuaTable) { - return obj.toArray().length; - } else if (Array.isArray(obj)) { - return obj.length; - } else { - return 0; - } + if (obj instanceof LuaTable) { + return obj.toArray().length; + } else if (Array.isArray(obj)) { + return obj.length; + } else { + return 0; + } } export class LuaBreak extends Error { } export class LuaReturn extends Error { - constructor(readonly values: LuaValue[]) { - super(); - } + constructor(readonly values: LuaValue[]) { + super(); + } } export function luaTruthy(value: any): boolean { - if (value === undefined || value === null || value === false) { - return false; - } - if (value instanceof LuaTable) { - return value.length > 0; - } - return true; + if (value === undefined || value === null || value === false) { + return false; + } + if (value instanceof LuaTable) { + return value.length > 0; + } + return true; } export function jsToLuaValue(value: any): any { - if (value instanceof LuaTable) { - return value; - } else if (Array.isArray(value)) { - return LuaTable.fromArray(value.map(jsToLuaValue)); - } else if (typeof value === "object") { - return LuaTable.fromObject(value); - } else { - return value; - } + if (value instanceof LuaTable) { + return value; + } else if (Array.isArray(value)) { + return LuaTable.fromArray(value.map(jsToLuaValue)); + } else if (typeof value === "object") { + return LuaTable.fromObject(value); + } else { + return value; + } } export function luaValueToJS(value: any): any { - if (value instanceof LuaTable) { - // This is a heuristic: if this table is used as an array, we return an array - if (value.length > 0) { - return value.toArray(); - } else { - return value.toObject(); - } + if (value instanceof LuaTable) { + // This is a heuristic: if this table is used as an array, we return an array + if (value.length > 0) { + return value.toArray(); } else { - return value; + return value.toObject(); } + } else { + return value; + } } diff --git a/common/space_lua/util.test.ts b/common/space_lua/util.test.ts index 8854bdc6..1281c6c4 100644 --- a/common/space_lua/util.test.ts +++ b/common/space_lua/util.test.ts @@ -3,19 +3,19 @@ import { assertEquals } from "@std/assert/equals"; import { assert } from "@std/assert"; Deno.test("Test promise helpers", async () => { - const r = evalPromiseValues([1, 2, 3]); - // should return the same array not as a promise - assertEquals(r, [1, 2, 3]); - const asyncR = evalPromiseValues([ - new Promise((resolve) => { - setTimeout(() => { - resolve(1); - }, 5); - }), - Promise.resolve(2), - 3, - ]); - // should return a promise - assert(asyncR instanceof Promise); - assertEquals(await asyncR, [1, 2, 3]); + const r = evalPromiseValues([1, 2, 3]); + // should return the same array not as a promise + assertEquals(r, [1, 2, 3]); + const asyncR = evalPromiseValues([ + new Promise((resolve) => { + setTimeout(() => { + resolve(1); + }, 5); + }), + Promise.resolve(2), + 3, + ]); + // should return a promise + assert(asyncR instanceof Promise); + assertEquals(await asyncR, [1, 2, 3]); }); diff --git a/common/space_lua/util.ts b/common/space_lua/util.ts index dfd628ef..bf5f2a46 100644 --- a/common/space_lua/util.ts +++ b/common/space_lua/util.ts @@ -1,16 +1,16 @@ export function evalPromiseValues(vals: any[]): Promise | any[] { - const promises = []; - const promiseResults = new Array(vals.length); - for (let i = 0; i < vals.length; i++) { - if (vals[i] instanceof Promise) { - promises.push(vals[i].then((v: any) => promiseResults[i] = v)); - } else { - promiseResults[i] = vals[i]; - } - } - if (promises.length === 0) { - return promiseResults; + const promises = []; + const promiseResults = new Array(vals.length); + for (let i = 0; i < vals.length; i++) { + if (vals[i] instanceof Promise) { + promises.push(vals[i].then((v: any) => promiseResults[i] = v)); } else { - return Promise.all(promises).then(() => promiseResults); + promiseResults[i] = vals[i]; } + } + if (promises.length === 0) { + return promiseResults; + } else { + return Promise.all(promises).then(() => promiseResults); + } } diff --git a/lib/dates.test.ts b/lib/dates.test.ts index 51ee7b28..b465ff13 100644 --- a/lib/dates.test.ts +++ b/lib/dates.test.ts @@ -1,5 +1,5 @@ import { localDateString } from "$lib/dates.ts"; Deno.test("Dates", () => { - console.log("Local date string", localDateString(new Date())); + console.log("Local date string", localDateString(new Date())); }); diff --git a/plugs/index/attributes.test.ts b/plugs/index/attributes.test.ts index 7614b968..3f8c7e41 100644 --- a/plugs/index/attributes.test.ts +++ b/plugs/index/attributes.test.ts @@ -2,63 +2,63 @@ import { assertEquals } from "@std/assert/equals"; import { determineType, jsonTypeToString } from "./attributes.ts"; Deno.test("JSON Determine type", () => { - // Determine type tests - assertEquals(determineType(null), { type: "null" }); - assertEquals(determineType(undefined), { type: "null" }); - assertEquals(determineType("hello"), { type: "string" }); - assertEquals(determineType(10), { type: "number" }); - assertEquals(determineType(true), { type: "boolean" }); - assertEquals(determineType({}), { type: "object", properties: {} }); - assertEquals(determineType([]), { type: "array" }); - assertEquals(determineType([1]), { - type: "array", - items: { type: "number" }, - }); - assertEquals( - determineType({ name: "Pete", age: 10, siblings: ["Sarah"] }), - { - type: "object", - properties: { - name: { type: "string" }, - age: { type: "number" }, - siblings: { type: "array", items: { type: "string" } }, - }, - }, - ); + // Determine type tests + assertEquals(determineType(null), { type: "null" }); + assertEquals(determineType(undefined), { type: "null" }); + assertEquals(determineType("hello"), { type: "string" }); + assertEquals(determineType(10), { type: "number" }); + assertEquals(determineType(true), { type: "boolean" }); + assertEquals(determineType({}), { type: "object", properties: {} }); + assertEquals(determineType([]), { type: "array" }); + assertEquals(determineType([1]), { + type: "array", + items: { type: "number" }, + }); + assertEquals( + determineType({ name: "Pete", age: 10, siblings: ["Sarah"] }), + { + type: "object", + properties: { + name: { type: "string" }, + age: { type: "number" }, + siblings: { type: "array", items: { type: "string" } }, + }, + }, + ); }); Deno.test("Serialize JSON Type to string", () => { - assertEquals(jsonTypeToString({ type: "string" }), "string"); - assertEquals(jsonTypeToString({ type: "null" }), "null"); - assertEquals(jsonTypeToString({ type: "number" }), "number"); - assertEquals(jsonTypeToString({ type: "boolean" }), "boolean"); - assertEquals(jsonTypeToString({ type: "array" }), "any[]"); - assertEquals( - jsonTypeToString({ type: "array", items: { type: "number" } }), - "number[]", - ); - assertEquals( - jsonTypeToString({ type: "object", properties: {} }), - "{}", - ); - assertEquals( - jsonTypeToString({ - type: "object", - properties: { - name: { type: "string" }, - age: { type: "number" }, - }, - }), - "{name: string; age: number;}", - ); - assertEquals( - jsonTypeToString({ - anyOf: [ - { type: "string" }, - { type: "number" }, - { type: "boolean" }, - ], - }), - "string | number | boolean", - ); + assertEquals(jsonTypeToString({ type: "string" }), "string"); + assertEquals(jsonTypeToString({ type: "null" }), "null"); + assertEquals(jsonTypeToString({ type: "number" }), "number"); + assertEquals(jsonTypeToString({ type: "boolean" }), "boolean"); + assertEquals(jsonTypeToString({ type: "array" }), "any[]"); + assertEquals( + jsonTypeToString({ type: "array", items: { type: "number" } }), + "number[]", + ); + assertEquals( + jsonTypeToString({ type: "object", properties: {} }), + "{}", + ); + assertEquals( + jsonTypeToString({ + type: "object", + properties: { + name: { type: "string" }, + age: { type: "number" }, + }, + }), + "{name: string; age: number;}", + ); + assertEquals( + jsonTypeToString({ + anyOf: [ + { type: "string" }, + { type: "number" }, + { type: "boolean" }, + ], + }), + "string | number | boolean", + ); }); diff --git a/plugs/index/item.test.ts b/plugs/index/item.test.ts index 08b5a3eb..d01d57bf 100644 --- a/plugs/index/item.test.ts +++ b/plugs/index/item.test.ts @@ -10,24 +10,24 @@ const itemsMd = ` `; Deno.test("Test item extraction", async () => { - const t = parseMarkdown(itemsMd); - const items = await extractItems("test", t); + const t = parseMarkdown(itemsMd); + const items = await extractItems("test", t); - assertEquals(items[0].name, "Item 1"); - assertEquals(items[0].age, 100); - assertEquals(items[0].page, "test"); - assertEquals(items[0].parent, undefined); - assertEquals(items[0].text, "Item 1 #tag1 #tag2 [age: 100]"); - assertEquals(new Set(items[0].tags), new Set(["tag1", "tag2"])); - assertEquals(new Set(items[0].itags), new Set(["item", "tag1", "tag2"])); + assertEquals(items[0].name, "Item 1"); + assertEquals(items[0].age, 100); + assertEquals(items[0].page, "test"); + assertEquals(items[0].parent, undefined); + assertEquals(items[0].text, "Item 1 #tag1 #tag2 [age: 100]"); + assertEquals(new Set(items[0].tags), new Set(["tag1", "tag2"])); + assertEquals(new Set(items[0].itags), new Set(["item", "tag1", "tag2"])); - assertEquals(items[1].name, "Item 1.1"); - assertEquals(new Set(items[1].tags), new Set(["tag3", "tag1"])); - assertEquals( - new Set(items[1].itags), - new Set(["tag3", "tag2", "tag1", "item"]), - ); - assertEquals(items[1].parent, items[0].ref); + assertEquals(items[1].name, "Item 1.1"); + assertEquals(new Set(items[1].tags), new Set(["tag3", "tag1"])); + assertEquals( + new Set(items[1].itags), + new Set(["tag3", "tag2", "tag1", "item"]), + ); + assertEquals(items[1].parent, items[0].ref); - assertEquals(items[2].parent, items[1].ref); + assertEquals(items[2].parent, items[1].ref); }); diff --git a/plugs/tasks/task.test.ts b/plugs/tasks/task.test.ts index 07e49f26..442ed7f8 100644 --- a/plugs/tasks/task.test.ts +++ b/plugs/tasks/task.test.ts @@ -11,34 +11,34 @@ const itemsMd = ` `; Deno.test("Test task extraction", async () => { - const t = parseMarkdown(itemsMd); - const tasks = await extractTasks("test", t); + const t = parseMarkdown(itemsMd); + const tasks = await extractTasks("test", t); - assertEquals(tasks.length, 3); - assertEquals(tasks[0].name, "Task 1"); - assertEquals(tasks[0].age, 200); - assertEquals(tasks[0].page, "test"); - assertEquals(tasks[0].text, "Task 1 [age: 200]"); - assertEquals(new Set(tasks[0].itags), new Set(["tag1", "tag2", "task"])); - assertEquals(tasks[0].parent, "test@1"); - assertEquals(tasks[1].name, "Task 2"); - // Don't inherit attributes - assertEquals(tasks[1].age, undefined); - // But inherit tags through itags, not tags - assertEquals( - new Set(tasks[1].tags), - new Set(["tag1", "tag3"]), - ); - assertEquals( - new Set(tasks[1].itags), - new Set(["tag1", "tag3", "task", "tag2"]), - ); - assertEquals(tasks[1].parent, "test@1"); - // Deeply - assertEquals(tasks[2].name, "Task 2.1"); - assertEquals(tasks[2].tags, []); - assertEquals( - new Set(tasks[2].itags), - new Set(["tag1", "tag3", "task", "tag2"]), - ); + assertEquals(tasks.length, 3); + assertEquals(tasks[0].name, "Task 1"); + assertEquals(tasks[0].age, 200); + assertEquals(tasks[0].page, "test"); + assertEquals(tasks[0].text, "Task 1 [age: 200]"); + assertEquals(new Set(tasks[0].itags), new Set(["tag1", "tag2", "task"])); + assertEquals(tasks[0].parent, "test@1"); + assertEquals(tasks[1].name, "Task 2"); + // Don't inherit attributes + assertEquals(tasks[1].age, undefined); + // But inherit tags through itags, not tags + assertEquals( + new Set(tasks[1].tags), + new Set(["tag1", "tag3"]), + ); + assertEquals( + new Set(tasks[1].itags), + new Set(["tag1", "tag3", "task", "tag2"]), + ); + assertEquals(tasks[1].parent, "test@1"); + // Deeply + assertEquals(tasks[2].name, "Task 2.1"); + assertEquals(tasks[2].tags, []); + assertEquals( + new Set(tasks[2].itags), + new Set(["tag1", "tag3", "task", "tag2"]), + ); });