Files
Lake/src/parser.lua
2026-04-25 16:59:45 +02:00

1374 lines
34 KiB
Lua

---@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