-- This Script is Part of the Prometheus Obfuscator by levno-710 -- -- parser.lua -- Overview: -- This Script provides a class for parsing of lua code. -- This Parser is Capable of parsing LuaU and Lua5.1 -- -- Note that when parsing LuaU "continue" is treated as a Keyword, so no variable may be named "continue" even though this would be valid in LuaU -- -- Settings Object: -- luaVersion : The LuaVersion of the Script - Currently Supported : Lua51 and LuaU -- local Tokenizer = require("prometheus.tokenizer"); local Enums = require("prometheus.enums"); local util = require("prometheus.util"); local Ast = require("prometheus.ast"); local Scope = require("prometheus.scope"); local logger = require("logger"); local AstKind = Ast.AstKind; local LuaVersion = Enums.LuaVersion; local lookupify = util.lookupify; local TokenKind = Tokenizer.TokenKind; local Parser = {}; local ASSIGNMENT_NO_WARN_LOOKUP = lookupify{ AstKind.NilExpression, AstKind.FunctionCallExpression, AstKind.PassSelfFunctionCallExpression, AstKind.VarargExpression }; local CALLABLE_PREFIX_EXPRESSION_LOOKUP = lookupify{ AstKind.VariableExpression, AstKind.IndexExpression, AstKind.FunctionCallExpression, AstKind.PassSelfFunctionCallExpression }; local function generateError(self, message) local token; if(self.index > self.length) then token = self.tokens[self.length]; elseif(self.index < 1) then return "Parsing Error at Position 0:0, " .. message; else token = self.tokens[self.index]; end return "Parsing Error at Position " .. tostring(token.line) .. ":" .. tostring(token.linePos) .. ", " .. message; end local function generateWarning(token, message) return "Warning at Position " .. tostring(token.line) .. ":" .. tostring(token.linePos) .. ", " .. message; end function Parser:new(settings) local luaVersion = (settings and (settings.luaVersion or settings.LuaVersion)) or LuaVersion.LuaU; local parser = { luaVersion = luaVersion, tokenizer = Tokenizer:new({ luaVersion = luaVersion }), tokens = {}; length = 0; index = 0; }; setmetatable(parser, self); self.__index = self; return parser; end -- Function to peek the n'th token local function peek(self, n) n = n or 0; local i = self.index + n + 1; if i > self.length then return Tokenizer.EOF_TOKEN; end return self.tokens[i]; end -- Function to get the next Token local function get(self) local i = self.index + 1; if i > self.length then error(generateError(self, "Unexpected end of Input")); end self.index = self.index + 1; local tk = self.tokens[i]; return tk; end local function is(self, kind, sourceOrN, n) local token = peek(self, n); local source = nil; if(type(sourceOrN) == "string") then source = sourceOrN; else n = sourceOrN; end n = n or 0; if(token.kind == kind) then if(source == nil or token.source == source) then return true; end end return false; end local function consume(self, kind, source) if(is(self, kind, source)) then self.index = self.index + 1; return true; end return false; end local function expect(self, kind, source) if(is(self, kind, source, 0)) then return get(self); end local token = peek(self); if self.disableLog then error() end if(source) then logger:error(generateError(self, string.format("unexpected token <%s> \"%s\", expected <%s> \"%s\"", token.kind, token.source, kind, source))); else logger:error(generateError(self, string.format("unexpected token <%s> \"%s\", expected <%s>", token.kind, token.source, kind))); end end -- Parse the given code to an Abstract Syntax Tree function Parser:parse(code) self.tokenizer:append(code); self.tokens = self.tokenizer:scanAll(); self.length = #self.tokens; -- Create Global Variable Scope local globalScope = Scope:newGlobal(); local ast = Ast.TopNode(self:block(globalScope, false), globalScope); -- File Must be Over when Top Node is Fully Parsed expect(self, TokenKind.Eof); logger:debug("Cleaning up Parser for next Use ...") -- Clean Up self.tokenizer:reset(); self.tokens = {}; self.index = 0; self.length = 0; logger:debug("Cleanup Done") return ast; end -- Parse a Code Block function Parser:block(parentScope, currentLoop, scope) scope = scope or Scope:new(parentScope); local statements = {}; repeat local statement, isTerminatingStatement = self:statement(scope, currentLoop); table.insert(statements, statement); until isTerminatingStatement or not statement -- Consume Eventual Semicolon after terminating return, break or continue consume(self, TokenKind.Symbol, ";"); return Ast.Block(statements, scope); end function Parser:statement(scope, currentLoop) -- Skip all semicolons before next real statement -- NOP statements are therefore ignored while(consume(self, TokenKind.Symbol, ";")) do end -- Break Statement - only valid inside of Loops if(consume(self, TokenKind.Keyword, "break")) then if(not currentLoop) then if self.disableLog then error() end; logger:error(generateError(self, "the break Statement is only valid inside of loops")); end -- Return true as Second value because break must be the last Statement in a block return Ast.BreakStatement(currentLoop, scope), true; end -- Continue Statement - only valid inside of Loops - only valid in LuaU if(self.luaVersion == LuaVersion.LuaU and consume(self, TokenKind.Keyword, "continue")) then if(not currentLoop) then if self.disableLog then error() end; logger:error(generateError(self, "the continue Statement is only valid inside of loops")); end -- Return true as Second value because continue must be the last Statement in a block return Ast.ContinueStatement(currentLoop, scope), true; end -- do ... end Statement if(consume(self, TokenKind.Keyword, "do")) then local body = self:block(scope, currentLoop); expect(self, TokenKind.Keyword, "end"); return Ast.DoStatement(body); end -- While Statement if(consume(self, TokenKind.Keyword, "while")) then local condition = self:expression(scope); expect(self, TokenKind.Keyword, "do"); local stat = Ast.WhileStatement(nil, condition, scope); stat.body = self:block(scope, stat); expect(self, TokenKind.Keyword, "end"); return stat; end -- Repeat Statement if(consume(self, TokenKind.Keyword, "repeat")) then local repeatScope = Scope:new(scope); local stat = Ast.RepeatStatement(nil, nil, scope); stat.body = self:block(nil, stat, repeatScope); expect(self, TokenKind.Keyword, "until"); stat.condition = self:expression(repeatScope); return stat; end -- Return Statement if(consume(self, TokenKind.Keyword, "return")) then local args = {}; if(not is(self, TokenKind.Keyword, "end") and not is(self, TokenKind.Keyword, "elseif") and not is(self, TokenKind.Keyword, "else") and not is(self, TokenKind.Symbol, ";") and not is(self, TokenKind.Eof)) then args = self:exprList(scope); end -- Return true as Second value because return must be the last Statement in a block return Ast.ReturnStatement(args), true; end -- If Statement if(consume(self, TokenKind.Keyword, "if")) then local condition = self:expression(scope); expect(self, TokenKind.Keyword, "then"); local body = self:block(scope, currentLoop); local elseifs = {}; -- Elseifs while(consume(self, TokenKind.Keyword, "elseif")) do local condition = self:expression(scope); expect(self, TokenKind.Keyword, "then"); local body = self:block(scope, currentLoop); table.insert(elseifs, { condition = condition, body = body, }); end local elsebody = nil; -- Else if(consume(self, TokenKind.Keyword, "else")) then elsebody = self:block(scope, currentLoop); end expect(self, TokenKind.Keyword, "end"); return Ast.IfStatement(condition, body, elseifs, elsebody); end -- Function Declaration if(consume(self, TokenKind.Keyword, "function")) then -- TODO: Parse Function Declaration Name local obj = self:funcName(scope); local baseScope = obj.scope; local baseId = obj.id; local indices = obj.indices; local funcScope = Scope:new(scope); expect(self, TokenKind.Symbol, "("); local args = self:functionArgList(funcScope); expect(self, TokenKind.Symbol, ")"); if(obj.passSelf) then local id = funcScope:addVariable("self", obj.token); table.insert(args, 1, Ast.VariableExpression(funcScope, id)); end local body = self:block(nil, false, funcScope); expect(self, TokenKind.Keyword, "end"); return Ast.FunctionDeclaration(baseScope, baseId, indices, args, body); end -- Local Function or Variable Declaration if(consume(self, TokenKind.Keyword, "local")) then -- Local Function Declaration if(consume(self, TokenKind.Keyword, "function")) then local ident = expect(self, TokenKind.Ident); local name = ident.value; local id = scope:addVariable(name, ident); local funcScope = Scope:new(scope); expect(self, TokenKind.Symbol, "("); local args = self:functionArgList(funcScope); expect(self, TokenKind.Symbol, ")"); local body = self:block(nil, false, funcScope); expect(self, TokenKind.Keyword, "end"); return Ast.LocalFunctionDeclaration(scope, id, args, body); end -- Local Variable Declaration local ids = self:nameList(scope); local expressions = {}; if(consume(self, TokenKind.Symbol, "=")) then expressions = self:exprList(scope); end -- Variables can only be reffered to in the next statement, so the id's are enabled after the expressions have been parsed self:enableNameList(scope, ids); if(#expressions > #ids) then logger:warn(generateWarning(peek(self, -1), string.format("assigning %d values to %d variable" .. ((#ids > 1 and "s") or ""), #expressions, #ids))); elseif(#ids > #expressions and #expressions > 0 and not ASSIGNMENT_NO_WARN_LOOKUP[expressions[#expressions].kind]) then logger:warn(generateWarning(peek(self, -1), string.format("assigning %d value" .. ((#expressions > 1 and "s") or "") .. " to %d variables initializes extra variables with nil, add a nil value to silence", #expressions, #ids))); end return Ast.LocalVariableDeclaration(scope, ids, expressions); end -- For Statement if(consume(self, TokenKind.Keyword, "for")) then -- Normal for Statement if(is(self, TokenKind.Symbol, "=", 1)) then local forScope = Scope:new(scope); local ident = expect(self, TokenKind.Ident); local varId = forScope:addDisabledVariable(ident.value, ident); expect(self, TokenKind.Symbol, "="); local initialValue = self:expression(scope); expect(self, TokenKind.Symbol, ","); local finalValue = self:expression(scope); local incrementBy = Ast.NumberExpression(1); if(consume(self, TokenKind.Symbol, ",")) then incrementBy = self:expression(scope); end local stat = Ast.ForStatement(forScope, varId, initialValue, finalValue, incrementBy, nil, scope); forScope:enableVariable(varId); expect(self, TokenKind.Keyword, "do"); stat.body = self:block(nil, stat, forScope); expect(self, TokenKind.Keyword, "end"); return stat; end -- For ... in ... statement local forScope = Scope:new(scope); local ids = self:nameList(forScope); expect(self, TokenKind.Keyword, "in"); local expressions = self:exprList(scope); -- Enable Ids after Expression Parsing so that code like this works: -- local z = {10,20} -- for y,z in ipairs(z) do -- print(y, z); -- end self:enableNameList(forScope, ids); expect(self, TokenKind.Keyword, "do"); local stat = Ast.ForInStatement(forScope, ids, expressions, nil, scope); stat.body = self:block(nil, stat, forScope); expect(self, TokenKind.Keyword, "end"); return stat; end local expr = self:primaryExpression(scope); -- Variable Assignment or Function Call if expr then -- Function Call Statement if(expr.kind == AstKind.FunctionCallExpression) then return Ast.FunctionCallStatement(expr.base, expr.args); end -- Function Call Statement passing self if(expr.kind == AstKind.PassSelfFunctionCallExpression) then return Ast.PassSelfFunctionCallStatement(expr.base, expr.passSelfFunctionName, expr.args); end -- Variable Assignment if(expr.kind == AstKind.IndexExpression or expr.kind == AstKind.VariableExpression) then if(expr.kind == AstKind.IndexExpression) then expr.kind = AstKind.AssignmentIndexing end if(expr.kind == AstKind.VariableExpression) then expr.kind = AstKind.AssignmentVariable end if(self.luaVersion == LuaVersion.LuaU) then -- LuaU Compound Assignment if(consume(self, TokenKind.Symbol, "+=")) then local rhs = self:expression(scope); return Ast.CompoundAddStatement(expr, rhs); end if(consume(self, TokenKind.Symbol, "-=")) then local rhs = self:expression(scope); return Ast.CompoundSubStatement(expr, rhs); end if(consume(self, TokenKind.Symbol, "*=")) then local rhs = self:expression(scope); return Ast.CompoundMulStatement(expr, rhs); end if(consume(self, TokenKind.Symbol, "/=")) then local rhs = self:expression(scope); return Ast.CompoundDivStatement(expr, rhs); end if(consume(self, TokenKind.Symbol, "%=")) then local rhs = self:expression(scope); return Ast.CompoundModStatement(expr, rhs); end if(consume(self, TokenKind.Symbol, "^=")) then local rhs = self:expression(scope); return Ast.CompoundPowStatement(expr, rhs); end if(consume(self, TokenKind.Symbol, "..=")) then local rhs = self:expression(scope); return Ast.CompoundConcatStatement(expr, rhs); end end local lhs = { expr } while consume(self, TokenKind.Symbol, ",") do expr = self:primaryExpression(scope); if(not expr) then if self.disableLog then error() end; logger:error(generateError(self, string.format("expected a valid assignment statement lhs part but got nil"))); end if(expr.kind == AstKind.IndexExpression or expr.kind == AstKind.VariableExpression) then if(expr.kind == AstKind.IndexExpression) then expr.kind = AstKind.AssignmentIndexing end if(expr.kind == AstKind.VariableExpression) then expr.kind = AstKind.AssignmentVariable end table.insert(lhs, expr); else if self.disableLog then error() end; logger:error(generateError(self, string.format("expected a valid assignment statement lhs part but got <%s>", expr.kind))); end end expect(self, TokenKind.Symbol, "="); local rhs = self:exprList(scope); return Ast.AssignmentStatement(lhs, rhs); end if self.disableLog then error() end; logger:error(generateError(self, "expressions are not valid statements!")); end return nil; end function Parser:primaryExpression(scope) local i = self.index; local s = self; self.disableLog = true; local status, val = pcall(self.expressionFunctionCall, self, scope); self.disableLog = false; if(status) then return val; else self.index = i; return nil; end end -- List of expressions Seperated by a comma function Parser:exprList(scope) local expressions = { self:expression(scope) }; while(consume(self, TokenKind.Symbol, ",")) do table.insert(expressions, self:expression(scope)); end return expressions; end -- list of local variable names function Parser:nameList(scope) local ids = {}; local ident = expect(self, TokenKind.Ident); local id = scope:addDisabledVariable(ident.value, ident); table.insert(ids, id); while(consume(self, TokenKind.Symbol, ",")) do ident = expect(self, TokenKind.Ident); id = scope:addDisabledVariable(ident.value, ident); table.insert(ids, id); end return ids; end function Parser:enableNameList(scope, list) for i, id in ipairs(list) do scope:enableVariable(id); end end -- function name function Parser:funcName(scope) local ident = expect(self, TokenKind.Ident); local baseName = ident.value; local baseScope, baseId = scope:resolve(baseName); local indices = {}; local passSelf = false; while(consume(self, TokenKind.Symbol, ".")) do table.insert(indices, expect(self, TokenKind.Ident).value); end if(consume(self, TokenKind.Symbol, ":")) then table.insert(indices, expect(self, TokenKind.Ident).value); passSelf = true; end return { scope = baseScope, id = baseId, indices = indices, passSelf = passSelf, token = ident, }; end -- Expression function Parser:expression(scope) return self:expressionOr(scope); end function Parser:expressionOr(scope) local lhs = self:expressionAnd(scope); if(consume(self, TokenKind.Keyword, "or")) then local rhs = self:expressionOr(scope); return Ast.OrExpression(lhs, rhs, true); end return lhs; end function Parser:expressionAnd(scope) local lhs = self:expressionComparision(scope); if(consume(self, TokenKind.Keyword, "and")) then local rhs = self:expressionAnd(scope); return Ast.AndExpression(lhs, rhs, true); end return lhs; end function Parser:expressionComparision(scope) local curr = self:expressionStrCat(scope); repeat local found = false; if(consume(self, TokenKind.Symbol, "<")) then local rhs = self:expressionStrCat(scope); curr = Ast.LessThanExpression(curr, rhs, true); found = true; end if(consume(self, TokenKind.Symbol, ">")) then local rhs = self:expressionStrCat(scope); curr = Ast.GreaterThanExpression(curr, rhs, true); found = true; end if(consume(self, TokenKind.Symbol, "<=")) then local rhs = self:expressionStrCat(scope); curr = Ast.LessThanOrEqualsExpression(curr, rhs, true); found = true; end if(consume(self, TokenKind.Symbol, ">=")) then local rhs = self:expressionStrCat(scope); curr = Ast.GreaterThanOrEqualsExpression(curr, rhs, true); found = true; end if(consume(self, TokenKind.Symbol, "~=")) then local rhs = self:expressionStrCat(scope); curr = Ast.NotEqualsExpression(curr, rhs, true); found = true; end if(consume(self, TokenKind.Symbol, "==")) then local rhs = self:expressionStrCat(scope); curr = Ast.EqualsExpression(curr, rhs, true); found = true; end until not found; return curr; end function Parser:expressionStrCat(scope) local lhs = self:expressionAddSub(scope); if(consume(self, TokenKind.Symbol, "..")) then local rhs = self:expressionStrCat(scope); return Ast.StrCatExpression(lhs, rhs, true); end return lhs; end function Parser:expressionAddSub(scope) local curr = self:expressionMulDivMod(scope); repeat local found = false; if(consume(self, TokenKind.Symbol, "+")) then local rhs = self:expressionMulDivMod(scope); curr = Ast.AddExpression(curr, rhs, true); found = true; end if(consume(self, TokenKind.Symbol, "-")) then local rhs = self:expressionMulDivMod(scope); curr = Ast.SubExpression(curr, rhs, true); found = true; end until not found; return curr; end function Parser:expressionMulDivMod(scope) local curr = self:expressionUnary(scope); repeat local found = false; if(consume(self, TokenKind.Symbol, "*")) then local rhs = self:expressionUnary(scope); curr = Ast.MulExpression(curr, rhs, true); found = true; end if(consume(self, TokenKind.Symbol, "/")) then local rhs = self:expressionUnary(scope); curr = Ast.DivExpression(curr, rhs, true); found = true; end if(consume(self, TokenKind.Symbol, "%")) then local rhs = self:expressionUnary(scope); curr = Ast.ModExpression(curr, rhs, true); found = true; end until not found; return curr; end function Parser:expressionUnary(scope) if(consume(self, TokenKind.Keyword, "not")) then local rhs = self:expressionUnary(scope); return Ast.NotExpression(rhs, true); end if(consume(self, TokenKind.Symbol, "#")) then local rhs = self:expressionUnary(scope); return Ast.LenExpression(rhs, true); end if(consume(self, TokenKind.Symbol, "-")) then local rhs = self:expressionUnary(scope); return Ast.NegateExpression(rhs, true); end return self:expressionPow(scope); end function Parser:expressionPow(scope) local lhs = self:tableOrFunctionLiteral(scope); if(consume(self, TokenKind.Symbol, "^")) then -- Allow unary operators on the rhs (e.g. 2 ^ #x, 2 ^ -x) while preserving right-associativity. ~ SpinnySpiwal local rhs = self:expressionUnary(scope); return Ast.PowExpression(lhs, rhs, true); end return lhs; end -- Table Literals and Function Literals cannot directly be called or indexed function Parser:tableOrFunctionLiteral(scope) if(is(self, TokenKind.Symbol, "{")) then return self:tableConstructor(scope); end if(is(self, TokenKind.Keyword, "function")) then return self:expressionFunctionLiteral(scope); end return self:expressionFunctionCall(scope); end function Parser:expressionFunctionLiteral(parentScope) local scope = Scope:new(parentScope); expect(self, TokenKind.Keyword, "function"); expect(self, TokenKind.Symbol, "("); local args = self:functionArgList(scope); expect(self, TokenKind.Symbol, ")"); local body = self:block(nil, false, scope); expect(self, TokenKind.Keyword, "end"); return Ast.FunctionLiteralExpression(args, body); end function Parser:functionArgList(scope) local args = {}; if(consume(self, TokenKind.Symbol, "...")) then table.insert(args, Ast.VarargExpression()); return args; end if(is(self, TokenKind.Ident)) then local ident = get(self); local name = ident.value; local id = scope:addVariable(name, ident); table.insert(args, Ast.VariableExpression(scope, id)); while(consume(self, TokenKind.Symbol, ",")) do if(consume(self, TokenKind.Symbol, "...")) then table.insert(args, Ast.VarargExpression()); return args; end ident = get(self); name = ident.value; id = scope:addVariable(name, ident); table.insert(args, Ast.VariableExpression(scope, id)); end end return args; end function Parser:expressionFunctionCall(scope, base) base = base or self:expressionIndex(scope); if(not (base and (CALLABLE_PREFIX_EXPRESSION_LOOKUP[base.kind] or base.isParenthesizedExpression))) then return base; end -- Normal Function Call local args = {}; if(is(self, TokenKind.String)) then args = { Ast.StringExpression(get(self).value), }; elseif(is(self, TokenKind.Symbol, "{")) then args = { self:tableConstructor(scope), }; elseif(consume(self, TokenKind.Symbol, "(")) then if(not is(self, TokenKind.Symbol, ")")) then args = self:exprList(scope); end expect(self, TokenKind.Symbol, ")"); else return base; end local node = Ast.FunctionCallExpression(base, args); -- the result of a function call can be indexed if(is(self, TokenKind.Symbol, ".") or is(self, TokenKind.Symbol, "[") or is(self, TokenKind.Symbol, ":")) then return self:expressionIndex(scope, node); end -- The result of a function call can be a function that is again called if(is(self, TokenKind.Symbol, "(") or is(self, TokenKind.Symbol, "{") or is(self, TokenKind.String)) then return self:expressionFunctionCall(scope, node); end return node; end function Parser:expressionIndex(scope, base) base = base or self:expressionLiteral(scope); -- Parse Indexing Expressions while(consume(self, TokenKind.Symbol, "[")) do local expr = self:expression(scope); expect(self, TokenKind.Symbol, "]"); base = Ast.IndexExpression(base, expr); end -- Parse Indexing Expressions while consume(self, TokenKind.Symbol, ".") do local ident = expect(self, TokenKind.Ident); base = Ast.IndexExpression(base, Ast.StringExpression(ident.value)); while(consume(self, TokenKind.Symbol, "[")) do local expr = self:expression(scope); expect(self, TokenKind.Symbol, "]"); base = Ast.IndexExpression(base, expr); end end -- Function Passing self if(consume(self, TokenKind.Symbol, ":")) then local passSelfFunctionName = expect(self, TokenKind.Ident).value; local args = {}; if(is(self, TokenKind.String)) then args = { Ast.StringExpression(get(self).value), }; elseif(is(self, TokenKind.Symbol, "{")) then args = { self:tableConstructor(scope), }; else expect(self, TokenKind.Symbol, "("); if(not is(self, TokenKind.Symbol, ")")) then args = self:exprList(scope); end expect(self, TokenKind.Symbol, ")"); end local node = Ast.PassSelfFunctionCallExpression(base, passSelfFunctionName, args); -- the result of a function call can be indexed if(is(self, TokenKind.Symbol, ".") or is(self, TokenKind.Symbol, "[") or is(self, TokenKind.Symbol, ":")) then return self:expressionIndex(scope, node); end -- The result of a function call can be a function that is again called if(is(self, TokenKind.Symbol, "(") or is(self, TokenKind.Symbol, "{") or is(self, TokenKind.String)) then return self:expressionFunctionCall(scope, node); end return node end -- The result of a function call can be a function that is again called if(is(self, TokenKind.Symbol, "(") or is(self, TokenKind.Symbol, "{") or is(self, TokenKind.String)) then return self:expressionFunctionCall(scope, base); end return base; end function Parser:expressionLiteral(scope) -- () expression if(consume(self, TokenKind.Symbol, "(")) then local expr = self:expression(scope); expect(self, TokenKind.Symbol, ")"); if expr then expr.isParenthesizedExpression = true; end return expr; end -- String Literal if(is(self, TokenKind.String)) then return Ast.StringExpression(get(self).value); end -- Number Literal if(is(self, TokenKind.Number)) then return Ast.NumberExpression(get(self).value); end -- True Literal if(consume(self, TokenKind.Keyword, "true")) then return Ast.BooleanExpression(true); end -- False Literal if(consume(self, TokenKind.Keyword, "false")) then return Ast.BooleanExpression(false); end -- Nil Literal if(consume(self, TokenKind.Keyword, "nil")) then return Ast.NilExpression(); end -- Vararg Literal if(consume(self, TokenKind.Symbol, "...")) then return Ast.VarargExpression(); end -- Variable if(is(self, TokenKind.Ident)) then local ident = get(self); local name = ident.value; local scope, id = scope:resolve(name); return Ast.VariableExpression(scope, id); end -- IfElse if(LuaVersion.LuaU) then if(consume(self, TokenKind.Keyword, "if")) then local condition = self:expression(scope); expect(self, TokenKind.Keyword, "then"); local true_value = self:expression(scope); local elseifs = {} while(consume(self, TokenKind.Keyword, "elseif")) do local elseif_condition = self:expression(scope); expect(self, TokenKind.Keyword, "then"); local elseif_value = self:expression(scope); table.insert(elseifs, { condition = elseif_condition, value = elseif_value }); end expect(self, TokenKind.Keyword, "else"); local false_value = self:expression(scope); return Ast.IfElseExpression(condition, true_value, elseifs, false_value); end end if(self.disableLog) then error() end logger:error(generateError(self, "Unexpected Token \"" .. peek(self).source .. "\". Expected a Expression!")) end function Parser:tableConstructor(scope) -- TODO: Parse Table Literals local entries = {}; expect(self, TokenKind.Symbol, "{"); while (not consume(self, TokenKind.Symbol, "}")) do if(consume(self, TokenKind.Symbol, "[")) then local key = self:expression(scope); expect(self, TokenKind.Symbol, "]"); expect(self, TokenKind.Symbol, "="); local value = self:expression(scope); table.insert(entries, Ast.KeyedTableEntry(key, value)); elseif(is(self, TokenKind.Ident, 0) and is(self, TokenKind.Symbol, "=", 1)) then local key = Ast.StringExpression(get(self).value); expect(self, TokenKind.Symbol, "="); local value = self:expression(scope); table.insert(entries, Ast.KeyedTableEntry(key, value)); else local value = self:expression(scope); table.insert(entries, Ast.TableEntry(value)); end if (not consume(self, TokenKind.Symbol, ";") and not consume(self, TokenKind.Symbol, ",") and not is(self, TokenKind.Symbol, "}")) then if self.disableLog then error() end logger:error(generateError(self, "expected a \";\" or a \",\"")); end end return Ast.TableConstructorExpression(entries); end return Parser