---@class Parser ---@field lexer Lexer Parser = {} function Parser:new(lexer) return setmetatable({ lexer = lexer }, {__index = self}) end function Parser:parse() local parts = {} local start while true do local tok, err = self.lexer:peek() if not tok then return nil, err end if not start then start = tok.location:clone() end if tok.kind == "eof" then break end local node, nerr = self:parseStatement() if not node then return nil, nerr end parts[#parts+1] = node end return Node:new("Program", parts, start) end function Parser:parseBlockOrStatement() local checktok, checkerr = self.lexer:peek() if not checktok then return nil, checkerr end if checktok.kind == "symbol" and checktok.content == "{" then local block, berr = self:parseBlock() if not block then return nil, berr end return block end local stmt, sterr = self:parseStatement() if not stmt then return nil, sterr end return stmt; end function Parser:parseBlock() local starttok, sterr = self.lexer:peek() if not starttok then return nil, sterr end if starttok.kind ~= "symbol" or starttok.content ~= "{" then return nil, Error:new("expected { to start block", "parser", starttok.location:clone()) end self.lexer:consume() local parts = {} while true do local detect, deterr = self.lexer:peek() if not detect then return nil, deterr end if detect.kind == "symbol" and detect.content == "}" then break; end if detect.kind == "eof" and detect.content == "eof" then return nil, Error:new("unexpected eof in a block", "parser", detect.location:clone()) end local stmt, stmterr = self:parseStatement() if not stmt then return nil, stmterr end parts[#parts+1] = stmt end local check, cherr = self.lexer:peek() if not check then return nil, cherr end if check.kind ~= "symbol" or check.content ~= "}" then return nil, Error:new("block ended in an invalid way", "parser", check.location:clone()) end self.lexer:consume() return Node:new("Block", parts, starttok.location:clone()) end function Parser:parseFunctionName() local tok,terr = self.lexer:peek() if not tok then return nil, terr end if tok.kind ~= "identifier" then return nil, Error:new("expected function name to start with identifier", "parser", tok.location:clone()) end self.lexer:consume() local node = Node:new('Variable', tok.content, tok.location:clone()) while true do -- woohoo more shit local check, checkerr = self.lexer:peek() if not check then return nil, checkerr end if check.kind == "symbol" and check.content == "." then self.lexer:consume() local idx,idxerr = self.lexer:peek() if not idx then return nil, idxerr end if idx.kind ~= "identifier" then return nil, Error:new("expected identifier after . in function name", 'parser', idx.location:clone()) end self.lexer:consume() node = Node:new('Field', {node, idx.content}, check.location:clone()); elseif check.kind == "symbol" and check.content == "[" then self.lexer:consume() local expr,err = self:parseExpression() if not expr then return nil, err end local endcheck, endcheckerr = self.lexer:peek() if not endcheck then return nil, endcheckerr end if endcheck.kind ~= "symbol" or endcheck.content ~= "]" then return nil, Error:new("expected ] to close indexing in function name", 'parser', endcheck.location:clone()) end self.lexer:consume() node = Node:new('Index', {node, expr}, check.location:clone()) elseif check.kind == "symbol" and check.content == ":" then self.lexer:consume() local methodname, methoderr = self.lexer:peek() if not methodname then return nil, methoderr end if methodname.kind ~= "identifier" then return nil, Error:new("expected identifier for method name in function declaration", 'parser', methodname.location:clone()) end self.lexer:consume() node = Node:new("Method", {node, methodname.content}, check.location:clone()) break; -- can't have more afterward else break; end end return node; end function Parser:parseStatement() local tok, err = self.lexer:peek() if not tok then return nil, err end if tok.kind == "keyword" and tok.content == "var" then -- var decl -- get rid of keyw self.lexer:consume() local checker,checkererr = self.lexer:peek() if not checker then return nil, checkererr end if checker.kind == "keyword" and checker.content == "fn" then self.lexer:consume() local fnname, fnnerr = self.lexer:peek() if not fnname then return nil, fnnerr end if fnname.kind ~= "identifier" then return nil, Error:new("expected identifier function name after var fn", "parser", fnname.location:clone()); end self.lexer:consume() local paren, parerr = self.lexer:peek() if not paren then return nil, parerr end if paren.kind ~= "symbol" or paren.content ~= "(" then return nil, Error:new("expected ( to open parameter list for local function") end self.lexer:consume() local params = {} while true do local checktok, checkerr = self.lexer:peek() if not checktok then return nil, checkerr end if checktok.kind == "symbol" and checktok.content == ")" then break; elseif checktok.kind == "identifier" then params[#params+1] = checktok.content self.lexer:consume() elseif checktok.kind == "eof" and checktok.content == "eof" then break; end local nexttok, nexterr = self.lexer:peek() if not nexttok then return nil, nexterr end if nexttok.kind ~= "symbol" or nexttok.content ~= "," then break; end self.lexer:consume() end local clostok, closerr = self.lexer:peek() if not clostok then return nil, closerr end if clostok.kind ~= "symbol" or clostok.content ~= ")" then return nil, Error:new("no closing parenthesis after local function parameters", "parser", clostok.location:clone()) end self.lexer:consume() local block, berr = self:parseBlockOrStatement() if not block then return nil, berr end return Node:new("LocalFunctionDeclaration", {fnname, params, block}, tok.location:clone()) end -- varnames local varnames = {} while true do local vntok, vnerr = self.lexer:peek() if not vntok then return nil, vnerr end if vntok.kind == "identifier" then self.lexer:consume() varnames[#varnames+1] = {vntok.content, vntok.location:clone()} else return nil, Error:new("not an identifier in varname list in declaration", "parser", vntok.location:clone()) end local commatok, commaerr = self.lexer:peek() if not commatok then return nil, commaerr end if commatok.kind == "symbol" and commatok.content == "," then self.lexer:consume() else break end end local splittok, spliterr = self.lexer:peek() if not splittok then return nil, spliterr end if splittok.kind ~= "symbol" then return nil, Error:new("expected = or ; after variable names in declaration", "parser", splittok.location:clone()) end if splittok.content == ";" then -- get rid self.lexer:consume() return Node:new("VariableDeclaration", {varnames, {}}, tok.location:clone()) end if splittok.content == "=" then -- get rid self.lexer:consume() local values = {} while true do local expr, exerr = self:parseExpression() if not expr then return nil, exerr end values[#values+1] = expr local commatok, commaerr = self.lexer:peek() if not commatok then return nil, commaerr end if commatok.kind == "symbol" and commatok.content == "," then self.lexer:consume() else break end end local smc, smcerr = self.lexer:peek() if not smc then return nil, smcerr end if smc.kind ~= "symbol" or smc.content ~= ";" then return nil, Error:new("expected semicolon after variable declaration", "parser", smc.location:clone()) end self.lexer:consume() return Node:new("VariableDeclaration", {varnames, values}, tok.location:clone()) end return nil, Error:new("expected = or ; after variable names in declaration", "parser", splittok.location:clone()) elseif tok.kind == "keyword" and tok.content == "if" then self.lexer:consume() local checks = {} local partok, parerr = self.lexer:peek() if not partok then return nil,parerr end if partok.kind ~= "symbol" or partok.content ~= "(" then return nil, Error:new("expected ( after if keyword", "parser", partok.location:clone()) end self.lexer:consume() local condition,conderr = self:parseExpression() if not condition then return nil, conderr end local closepart, closeparerr = self.lexer:peek() if not closepart then return nil, closeparerr end if closepart.kind ~= "symbol" or closepart.content ~= ")" then return nil, Error:new("expected ) to close if statement condition", 'parser', closepart.location:clone()) end self.lexer:consume() local body, bodyerr = self:parseBlockOrStatement() if not body then return nil, bodyerr end checks[#checks+1] = {condition,body} -- elseifs while true do local checkt, checkerr = self.lexer:peek() if not checkt then return nil, checkerr end if checkt.kind == "keyword" and checkt.content == "elseif" then self.lexer:consume() local partok2, parerr2 = self.lexer:peek() if not partok2 then return nil,parerr2 end if partok2.kind ~= "symbol" or partok2.content ~= "(" then return nil, Error:new("expected ( after elseif keyword", "parser", partok2.location:clone()) end self.lexer:consume() local condition2,conderr2 = self:parseExpression() if not condition2 then return nil, conderr2 end local closepart2, closeparerr2 = self.lexer:peek() if not closepart2 then return nil, closeparerr2 end if closepart2.kind ~= "symbol" or closepart2.content ~= ")" then return nil, Error:new("expected ) to close elseif statement condition", closepart2.location:clone()) end self.lexer:consume() local block,berr = self:parseBlockOrStatement(); if not block then return nil, berr end checks[#checks+1] = {condition2, block} else break; end end local elset, elseerr = self.lexer:peek() if not elset then return nil, elseerr end local elseblock; if elset.kind == "keyword" and elset.content == "else" then self.lexer:consume() local elseb, elseberr = self:parseBlockOrStatement(); if not elseb then return nil, elseberr end elseblock = elseb end return Node:new("IfStatement", {checks, elseblock}, tok.location:clone()); elseif tok.kind == "keyword" and tok.content == "while" then self.lexer:consume() local paren, perr = self.lexer:peek() if not paren then return nil, perr end if paren.kind ~= "symbol" or paren.content ~= "(" then return nil, Error:new("expected ( after start of while", "parser", paren.location:clone()) end self.lexer:consume() local cond, conderr = self:parseExpression() if not cond then return nil, conderr end local closep, closeperr = self.lexer:peek() if not closep then return nil, closeperr end if closep.kind ~= "symbol" or closep.content ~= ")" then return nil, Error:new("expected ) to close condition in while loop", 'parser', closep.location:clone()) end self.lexer:consume() local block,berr = self:parseBlockOrStatement() if not block then return nil, berr end return Node:new("While", {cond, block}, tok.location:clone()) elseif tok.kind == "keyword" and tok.content == "for" then self.lexer:consume() local paren, perr = self.lexer:peek() if not paren then return nil, perr end if paren.kind ~= "symbol" or paren.content ~= "(" then return nil, Error:new("expected ( after start of while", "parser", paren.location:clone()) end self.lexer:consume() local decl, declerr = self:parseStatement(); -- auto removes semi!! if not decl then return nil, declerr end local check, checkerr = self:parseExpression(); if not check then return nil, checkerr end local semi, semierr = self.lexer:peek() if not semi then return nil, semierr end if semi.kind ~= "symbol" or semi.content ~= ";" then return nil, Error:new('expected semicolon after condition in for loop', 'parser', semi.location:clone()) end self.lexer:consume() local modifier, moderr = self:parseStatement(); -- also removes a semi -> third semi is required! if not modifier then return nil, moderr end local close, closeerr = self.lexer:peek(); if not close then return nil, closeerr end if close.kind ~= "symbol" or close.content ~= ")" then return nil, Error:new("expected ) to close condition part of for loop", 'parser', close.location:clone()) end self.lexer:consume() local block, berr = self:parseBlockOrStatement() if not block then return nil, berr end return Node:new("For", {decl, check, modifier, block}, tok.location:clone()) elseif tok.kind == "keyword" and tok.content == "fn" then self.lexer:consume() local fnname, fnnerr = self:parseFunctionName() if not fnname then return nil, fnnerr end local paren, parerr = self.lexer:peek() if not paren then return nil, parerr end if paren.kind ~= "symbol" or paren.content ~= "(" then return nil, Error:new("expected ( to open parameter list for function", 'lexer', paren.location:clone()) end self.lexer:consume() local params = {} while true do local checktok, checkerr = self.lexer:peek() if not checktok then return nil, checkerr end if checktok.kind == "symbol" and checktok.content == ")" then break; elseif checktok.kind == "identifier" then params[#params+1] = checktok.content self.lexer:consume() elseif checktok.kind == "eof" and checktok.content == "eof" then break; end local nexttok, nexterr = self.lexer:peek() if not nexttok then return nil, nexterr end if nexttok.kind ~= "symbol" or nexttok.content ~= "," then break; end self.lexer:consume() end local clostok, closerr = self.lexer:peek() if not clostok then return nil, closerr end if clostok.kind ~= "symbol" or clostok.content ~= ")" then return nil, Error:new("no closing parenthesis after function parameters", "parser", clostok.location:clone()) end self.lexer:consume() local block, berr = self:parseBlockOrStatement() if not block then return nil, berr end return Node:new("FunctionDeclaration", {fnname, params, block}, tok.location:clone()) elseif tok.kind == "keyword" and tok.content == "return" then self.lexer:consume() local parts = {} while true do local chtok, cherr = self.lexer:peek() if not chtok then return nil, cherr end if chtok.kind == "symbol" and chtok.content == ";" then break; end local expr, exprerr = self:parseExpression() if not expr then return nil, exprerr end parts[#parts+1] = expr local checkt, checkerr = self.lexer:peek() if not checkt then return nil, checkerr end if checkt.kind ~= "symbol" or checkt.content ~= "," then break; end self.lexer:consume() end local semi, semierr = self.lexer:peek() if not semi then return nil, semierr end if semi.kind ~= 'symbol' or semi.content ~= ';' then return nil, Error:new("expected ; after return statement", "parser", semi.location:clone()) end self.lexer:consume() return Node:new('Return', parts, tok.location:clone()); elseif tok.kind == "keyword" and tok.content == "do" then self.lexer:consume() local block, berr = self:parseBlockOrStatement() if not block then return nil, berr end if block.kind == "Block" then return block; else return Node:new("Block", {block}, tok.location:clone()) end elseif (tok.kind == "symbol" and tok.content == "(") or tok.kind == "identifier" then local expr, exerr = self:parseExpression() if not expr then return nil, exerr end local nextok, nexerr = self.lexer:peek() if not nextok then return nil, nexerr end if nextok.kind == "symbol" and (nextok.content == "," or nextok.content == "=") then -- assignment local vars = {expr} while true do local tkn, tknerr = self.lexer:peek() if not tkn then return nil, tknerr end if tkn.kind == "symbol" and tkn.content == "," then self.lexer:consume() local checkt, checkerr = self.lexer:peek() if not checkt then return nil, checkerr end if (checkt.kind == "symbol" and checkt.content == "(") or checkt.kind == "identifier" then local nexpr, nerr = self:parseExpression() if not nexpr then return nil, nerr end vars[#vars+1] = nexpr else return nil, Error:new("expected expression starting with ( or an identifier after comma in assigment!", 'parser', checkt.location:clone()) end else break; end end local equaltok, equalerr = self.lexer:peek() if not equaltok then return nil, equalerr end if equaltok.kind ~= "symbol" or equaltok.content ~= "=" then return nil, Error:new("expected = in assignment", 'parser', equaltok.location:clone()) end self.lexer:consume() -- get rid of = local values = {} while true do local assexpr, asserr = self:parseExpression() if not assexpr then return nil, asserr end values[#values+1] = assexpr; local commatok, commaerr = self.lexer:peek() if not commatok then return nil, commaerr end if commatok.kind == "symbol" and commatok.content == "," then self.lexer:consume() else break; end end local semi,semierr = self.lexer:peek() if not semi then return nil, semierr end if semi.kind ~= "symbol" or semi.content ~= ";" then return nil, Error:new("expected semicolon after assignment", 'parser', semi.location:clone()) end self.lexer:consume() return Node:new('Assignment', {vars, values}, equaltok.location:clone()); elseif nextok.kind == "symbol" and (nextok.content == '&&=' or nextok.content == "||=" or nextok.content == "+=" or nextok.content == "-=" or nextok.content == "*=" or nextok.content == "/=" or nextok.content == "%=" or nextok.content == "^=" or nextok.content == "..=") then self.lexer:consume() local operator = nextok.content:sub(1,-2) -- just the thing but without the =, whould work fine for all these cases if expr.kind ~= "Variable" and expr.kind ~= "Field" and expr.kind ~= "Index" then return nil, Error:new("can only use operator assignment on variables, fields or indexes", "parser", expr.location:clone()) end local rhs,rhserr = self:parseExpression() if not rhs then return nil, rhserr end local semi,semierr = self.lexer:peek() if not semi then return nil, semierr end if semi.kind ~= "symbol" or semi.content ~= ";" then return nil, Error:new("expected semicolon after operator assignment", 'parser', semi.location:clone()) end self.lexer:consume() return Node:new("OperatorAssignment", {expr, operator, rhs}, nextok.location:clone()) end if expr.kind ~= "Call" and expr.kind ~= "MethodCall" then return nil, Error:new("loose expression must be a call or method call", 'parser', expr.location:clone()) end local semi, semierr = self.lexer:peek() if not semi then return nil, semierr end if semi.kind ~= "symbol" or semi.content ~= ";" then return nil, Error:new("expected semicolon after call or method call", 'parser', semi.location:clone()) end self.lexer:consume() return expr elseif tok.kind == "keyword" and tok.content == "break" then self.lexer:consume() local semi,semierr = self.lexer:peek() if not semi then return nil, semierr end if semi.kind ~= "symbol" or semi.content ~= ";" then return nil, Error:new("expected semicolon after break", 'parser', semi.location:clone()) end self.lexer:consume() return Node:new("Break", {}, tok.location:clone()) elseif tok.kind == "keyword" and tok.content == "foreach" then self.lexer:consume() local openpar,operr = self.lexer:peek() if not openpar then return nil, operr end if openpar.kind ~= "symbol" or openpar.content ~= "(" then return nil, Error:new("expected ( after foreach keyword", 'parser', openpar.location:clone()); end self.lexer:consume() local varnames = {} while true do local vname,vnamerr = self.lexer:peek() if not vname then return nil, vnamerr end if vname.kind ~= "identifier" then return nil, Error:new("expected identifier in varname list in foreach loop", 'parser', vname.location:clone()) end self.lexer:consume() varnames[#varnames+1] = vname.content; local comma,commaerr = self.lexer:peek() if not comma then return nil, commaerr end if comma.kind ~= "symbol" or comma.content ~= "," then break end self.lexer:consume() end local eqtok, eqerr = self.lexer:peek() if not eqtok then return nil, eqerr end if eqtok.kind ~= "symbol" or eqtok.content ~= "=" then return nil, Error:new("expected = after varname list in foreach loop", 'parser', eqtok.location:clone()) end self.lexer:consume() local exprs = {} while true do local expr, exerr = self:parseExpression() if not expr then return nil, exerr end exprs[#exprs+1] = expr local commatok, commaerr = self.lexer:peek() if not commatok then return nil, commaerr end if commatok.kind ~= "symbol" or commatok.content ~= "," then break; end self.lexer:consume() end local closepar, clperr = self.lexer:peek() if not closepar then return nil, clperr end if closepar.kind ~= "symbol" or closepar.content ~= ")" then return nil, Error:new("expected ) to close expression list for foreach loop", 'parser', closepar.location:clone()) end self.lexer:consume() local block, berr = self:parseBlockOrStatement(); if not block then return nil, berr end return Node:new("Foreach", {varnames, exprs, block}, tok.location:clone()) end return nil, Error:new('invalid statement', 'parser', tok.location:clone()); end function Parser:parseExpression() return self:parseOperatorExpression() end ---@param token Token ---@return number? function Parser:prefixOperatorBindingPower(token) if token.kind == "symbol" then if token.content == "-" then return 45 elseif token.content == "#" then return 45 elseif token.content == "!" then return 45 end end return nil end ---@param token Token ---@return number?, number? function Parser:infixOperatorBindingPower(token) if token.kind == "symbol" then if token.content == "+" or token.content == "-" then return 10, 20 elseif token.content == "*" or token.content == "/" or token.content == "%" then return 30, 40 elseif token.content == "^" then return 60, 50 elseif token.content == ".." then return 0, -10 elseif token.content == "<" or token.content == ">" or token.content == "<=" or token.content == ">=" or token.content == "!=" or token.content == "==" then return -30, -20 elseif token.content == "&&" then return -50, -40 elseif token.content == "||" then return -70, -60 end end end function Parser:parseOperatorExpression(min_bp, predlhs) min_bp = min_bp or -math.huge local tok, err = self.lexer:peek() if not tok then return nil, err end if tok.kind == "eof" then return nil, Error:new("expected expression, but found EOF", "parser", tok.location:clone()) end local lhs = predlhs if (lhs == nil) and self:prefixOperatorBindingPower(tok) then self.lexer:consume() -- eat the operator local prefix_bp = self:prefixOperatorBindingPower(tok) local rightthing, righterr = self:parseOperatorExpression(prefix_bp) if not rightthing then return nil, righterr end lhs = Node:new("UnaryOperator", {tok.content, rightthing}, tok.location:clone()) end if lhs == nil then local newlhs, newerr = self:parseRawExpression() if not newlhs then return nil, newerr end lhs = newlhs end while true do local newtok, lexErr = self.lexer:peek() if not newtok then return nil, lexErr end local left_bp, right_bp = self:infixOperatorBindingPower(newtok) if not left_bp or not right_bp then break end -- ain't an operator!! if left_bp < min_bp then -- we don't break end self.lexer:consume() local rhs, rhserr = self:parseOperatorExpression(right_bp) if not rhs then return nil, rhserr end lhs = Node:new("BinaryOperator", {newtok.content, lhs, rhs}, newtok.location:clone()) end return lhs end function Parser:parseTableLiteral() local tok, tokerr = self.lexer:peek() if not tok then return nil, tokerr end if tok.kind ~= "symbol" or tok.content ~= "{" then return nil, Error:new("expected { to start table literal", "parser", tok.location:clone()) end self.lexer:consume() local list_part = {} local table_entries = {} while true do local check, checkerr = self.lexer:peek() if not check then return nil, checkerr end if check.kind == "symbol" and check.content == "}" then break; end -- it hasn't ended, let us continue. local expr, exerr = self:parseExpression() if not expr then return nil, exerr end local check2, check2err = self.lexer:peek() if not check2 then return nil, check2err end if check2.kind == "symbol" and check2.content == '=' then -- table entry! self.lexer:consume() local set,seterr = self:parseExpression() if not set then return nil, seterr end table_entries[#table_entries+1] = {expr, set} else list_part[#list_part+1] = expr end local commatok, commaerr = self.lexer:peek() if not commatok then return nil, commaerr end if commatok.kind ~= "symbol" or commatok.content ~= "," then break; end self.lexer:consume() -- remove comma end local closetok, closeerr = self.lexer:peek() if not closetok then return nil, closeerr; end if closetok.kind ~= "symbol" or closetok.content ~= "}" then return nil, Error:new('expected } to close table literal', 'parser', closetok.location:clone()) end self.lexer:consume() return Node:new("TableLiteral", {list_part, table_entries}, tok.location:clone()) end function Parser:parseLambdaFunction() local fn,fnerr = self.lexer:peek() if not fn then return nil, fnerr end if fn.kind ~= 'keyword' or fn.content ~= 'fn' then return nil, Error:new('expected fn to start lambda function', 'parser', fn.location:clone()) end self.lexer:consume() local paren, parerr = self.lexer:peek() if not paren then return nil, parerr end if paren.kind ~= "symbol" or paren.content ~= "(" then return nil, Error:new("expected ( to open parameter list for lambda function", 'parser', paren.location:clone()) end self.lexer:consume() local params = {} while true do local checktok, checkerr = self.lexer:peek() if not checktok then return nil, checkerr end if checktok.kind == "symbol" and checktok.content == ")" then break; elseif checktok.kind == "identifier" then params[#params+1] = checktok.content self.lexer:consume() elseif checktok.kind == "eof" and checktok.content == "eof" then break; end local nexttok, nexterr = self.lexer:peek() if not nexttok then return nil, nexterr end if nexttok.kind ~= "symbol" or nexttok.content ~= "," then break; end self.lexer:consume() end local clostok, closerr = self.lexer:peek() if not clostok then return nil, closerr end if clostok.kind ~= "symbol" or clostok.content ~= ")" then return nil, Error:new("no closing parenthesis after lambda function parameters", "parser", clostok.location:clone()) end self.lexer:consume() local block, berr = self:parseBlockOrStatement() if not block then return nil, berr end return Node:new("LambdaFunctionLiteral", {params, block}, fn.location:clone()) end function Parser:parseRawExpression() local tok, err = self.lexer:peek() if not tok then return nil, err end if tok.kind == "string" then self.lexer:consume() return Node:new("StringLiteral", tok.content, tok.location:clone()) elseif tok.kind == "number" then self.lexer:consume() return Node:new("NumberLiteral", tok.content, tok.location:clone()) elseif tok.kind == "identifier" then self.lexer:consume() local node = Node:new("Variable", tok.content, tok.location:clone()) return self:parseComplexExpression(node) elseif tok.kind == "keyword" and tok.content == "true" then self.lexer:consume() return Node:new("BooleanLiteral", "true", tok.location:clone()) elseif tok.kind == "keyword" and tok.content == "false" then self.lexer:consume() return Node:new("BooleanLiteral", "false", tok.location:clone()) elseif tok.kind == "keyword" and tok.content == "null" then self.lexer:consume() return Node:new("NullLiteral", nil, tok.location:clone()) elseif tok.kind == "symbol" and tok.content == "{" then return self:parseTableLiteral(); elseif tok.kind == "keyword" and tok.content == "fn" then return self:parseLambdaFunction(); elseif tok.kind == "symbol" and tok.content == "(" then self.lexer:consume() local expr, exerr = self:parseExpression() if not expr then return nil, exerr end local closet, closerr = self.lexer:peek() if not closet then return nil, closerr end if closet.kind ~= "symbol" or closet.content ~= ")" then return nil, Error:new('expected ) to close parenthesized expression', 'parser', closet.location:clone()) end self.lexer:consume() local node = Node:new('ParenthesizedExpression', expr, tok.location:clone()) local newnode, nodeerr = self:parseComplexExpression(node) if nodeerr then return nil, nodeerr end return newnode else return nil, Error:new('invalid expression', 'parser', tok.location:clone()) end end function Parser:parseComplexExpression(node) while true do local tok, err = self.lexer:peek() if not tok then return nil, err end if tok.kind == "symbol" and tok.content == "." then self.lexer:consume() -- bye . local field, ferr = self.lexer:peek() if not field then return nil, ferr end if field.kind ~= "identifier" then return nil, Error:new("expected identifier after . in complex expression", "parser", field.location:clone()) end self.lexer:consume() node = Node:new("Field", {node, field.content}, tok.location:clone()) elseif tok.kind == "symbol" and tok.content == "[" then self.lexer:consume() -- now we have *some* expression local expr, exerr = self:parseExpression() if not expr then return nil, exerr end local close,closerr = self.lexer:peek() if not close then return nil, closerr end if close.kind ~= "symbol" or close.content ~= "]" then return nil, Error:new("expected ] to close indexing in complex expression", "parser", close.location:clone()) end self.lexer:consume() node = Node:new("Index", {node, expr}, tok.location:clone()) elseif tok.kind == "symbol" and tok.content == ":" then self.lexer:consume() local methodname, methoderr = self.lexer:peek() if not methodname then return nil, methoderr end if methodname.kind ~= "identifier" then return nil, Error:new('expected identifier for method name', 'parser', methodname.location:clone()) end self.lexer:consume() local parentok, parenerr = self.lexer:peek() if not parentok then return nil, parenerr end if parentok.kind ~= "symbol" or parentok.content ~= '(' then return nil, Error:new("expected ( to open method call argument list", 'parser', parentok.location:clone()) end self.lexer:consume() local parts = {} while true do local chtok, cherr = self.lexer:peek() if not chtok then return nil, cherr end if chtok.kind == "symbol" and chtok.content == ")" then -- self.lexer:consume() break; end local expr, experr = self:parseExpression() if not expr then return nil, experr end parts[#parts+1] = expr; local ntok, nerr = self.lexer:peek() if not ntok then return nil, nerr end if ntok.kind == "symbol" and ntok.content == ")" then break; elseif ntok.kind == "symbol" and ntok.content == "," then self.lexer:consume() -- let it go!1!! else return nil, Error:new("expected ) or , after argument in method call", 'parser', ntok.location:clone()) end end local closeparen, closeparenerr = self.lexer:peek() if not closeparen then return nil, closeparenerr end if closeparen.kind ~= "symbol" or closeparen.content ~= ")" then return nil, Error:new("expected ) to close arument list for method call", 'parser', closeparen.location:clone()) end self.lexer:consume() node = Node:new("MethodCall", {node, methodname.content, parts}, tok.location:clone()) elseif tok.kind == "symbol" and tok.content == "(" then self.lexer:consume() local parts = {} while true do local chtok, cherr = self.lexer:peek() if not chtok then return nil, cherr end if chtok.kind == "symbol" and chtok.content == ")" then -- self.lexer:consume() break; end local expr, experr = self:parseExpression() if not expr then return nil, experr end parts[#parts+1] = expr; local ntok, nerr = self.lexer:peek() if not ntok then return nil, nerr end if ntok.kind == "symbol" and ntok.content == ")" then break; elseif ntok.kind == "symbol" and ntok.content == "," then self.lexer:consume() -- let it go!1!! else return nil, Error:new("expected ) or , after argument in call", 'parser', ntok.location:clone()) end end local paren, parenerr = self.lexer:peek() if not paren then return nil, parenerr end if paren.kind ~= "symbol" or paren.content ~= ")" then return nil, Error:new("expected ) to close arument list for call", 'parser', paren.location:clone()) end self.lexer:consume() node = Node:new("Call", {node, parts}, tok.location:clone()); else return node end end end