--- /dev/null
+function gettoken(tokenlist)
+ local token = table.remove(tokenlist, 1)
+ return token
+end
+
+function peektoken(tokenlist, n)
+ if not n then
+ n = 1
+ end
+ local token = tokenlist[n]
+ return token
+end
+
+function expecttoken(token, expected)
+ if not token then
+ error("unexpected EOF", 1)
+ end
+ for k,v in pairs(expected) do
+ if token.type == v then
+ return token
+ end
+ end
+ error("unexpected token "..token.type,1)
+end
+
+function parser_global(tokenlist, parsetree)
+ -- GLOBAL = (VARIABLE | FUNCTION)
+ local token = peektoken(tokenlist)
+ while token do
+ if token.type == "int" then
+ local subtoken
+ expecttoken(peektoken(tokenlist, 2), {"identifier"})
+ subtoken = peektoken(tokenlist, 3)
+ if not subtoken then
+ error("unexpected EOF")
+ end
+ if subtoken.type == "(" then
+ table.insert(parsetree, parser_function(tokenlist))
+ elseif subtoken.type == ";" or subtoken.type == "=" then
+ table.insert(parsetree, parser_variable(tokenlist))
+ else
+ error("unexpected token "..subtoken.type.." following 'int <identifier>'")
+ end
+ else
+ error("unexpected token "..token.type)
+ end
+ token = peektoken(tokenlist)
+ end
+end
+
+function parser_function(tokenlist)
+ -- FUNCTION = TYPE IDENTIFIER ARGSLIST STMTLIST
+ local myfunction = {}
+
+ myfunction.type = "function"
+ myfunction.returntype = parser_type(tokenlist)
+ local token = expecttoken(gettoken(tokenlist), {"identifier"})
+ myfunction.name = token.value
+ myfunction.args = parser_argslist(tokenlist)
+ myfunction.body = parser_stmtlist(tokenlist)
+ return myfunction
+end
+
+function parser_type(tokenlist)
+ -- TYPE = INT
+ return expecttoken(gettoken(tokenlist), { "int" }).type
+end
+
+function parser_istype(tokenlist)
+ if peektoken(tokenlist).type == "int" then
+ return true
+ end
+ return false
+end
+
+function parser_argslist(tokenlist)
+ -- ARGSLIST = ( [TYPE IDENTIFIER [, TYPE IDENTIFIER]*] )
+ local token
+ local argslist = {}
+ local firstarg = true
+ expecttoken(gettoken(tokenlist), { "(" })
+ while true do
+ local type, ident
+ token = peektoken(tokenlist)
+ if token.type == ")" then
+ gettoken(tokenlist)
+ return argslist
+ end
+ if not firstarg then
+ expecttoken(gettoken(tokenlist), { "," })
+ token = peektoken(tokenlist)
+ end
+ firstarg = false
+ type = parser_type(tokenlist)
+ token = expecttoken(gettoken(tokenlist), { "identifier" })
+ ident = token.value
+ table.insert(argslist, { type = type, name = ident })
+ end
+end
+
+function parser_stmtlist(tokenlist)
+ -- STMTLIST = { [( VARIABLE | STATEMENT | STMTLIST)]* }
+ local token
+ local stmtlist = {}
+
+ stmtlist.type = "stmtlist"
+ stmtlist.body = {}
+ expecttoken(gettoken(tokenlist), {"{"})
+ while true do
+ token = peektoken(tokenlist)
+ if token.type == "}" then
+ gettoken(tokenlist)
+ return stmtlist
+ end
+ if parser_istype(tokenlist) then
+ table.insert(stmtlist.body, parser_variable(tokenlist))
+ elseif token.type == "{" then
+ table.insert(stmtlist.body, parser_stmtlist(tokenlist))
+ else
+ table.insert(stmtlist.body, parser_statement(tokenlist))
+ end
+ end
+end
+
+function parser_variable(tokenlist)
+ -- VARIABLE = TYPE IDENTIFIER [ = TYPEINITIALIZER ] ;
+ local variable = {}
+ variable.type = "variable"
+ variable.vtype = parser_type(tokenlist)
+ variable.name = expecttoken(gettoken(tokenlist), { "identifier" }).value
+ expecttoken(peektoken(tokenlist), { ";", "=" })
+ local token = gettoken(tokenlist)
+ if token.type == ";" then
+ return variable
+ end
+ if token.type == '=' then
+ if variable.vtype == "int" then
+ variable.initializer = parser_getinitializer_int(tokenlist)
+ else
+ error("internal error")
+ end
+ expecttoken(gettoken(tokenlist), { ";" })
+ return variable
+ end
+ error("internal error")
+end
+
+function parser_getinitializer_int(tokenlist)
+ return expecttoken(gettoken(tokenlist), { "number" }).value
+end
+
+function parser_statement(tokenlist)
+ -- STATEMENT = ; | EXPRESSION ; | (WHILE | IF)
+ local token = peektoken(tokenlist)
+ if token.type == ";" then
+ return nil
+ elseif token.type == "while" then
+ return parser_while(tokenlist)
+ elseif token.type == "if" then
+ return parser_if(tokenlist)
+ elseif token.type == "return" then
+ return parser_return(tokenlist)
+ else
+ local result = parser_expression(tokenlist)
+ expecttoken(gettoken(tokenlist), { ";" })
+ return result
+ end
+end
+
+function parser_while(tokenlist)
+ -- WHILE = WHILE ( EXPRESSION ) CHUNK
+ local result = {}
+ result.type = "while"
+ expecttoken(gettoken(tokenlist), { "while" })
+ expecttoken(gettoken(tokenlist), { "(" })
+ result.expression = parser_expression(tokenlist)
+ expecttoken(gettoken(tokenlist), { ")" })
+ result.chunk = parser_chunk(tokenlist)
+ return result
+end
+
+function parser_if(tokenlist)
+ -- IF = IF ( EXPRESSION ) CHUNK
+ local result = {}
+ result.type = "if"
+ expecttoken(gettoken(tokenlist), { "if" })
+ expecttoken(gettoken(tokenlist), { "(" })
+ result.expression = parser_expression(tokenlist)
+ expecttoken(gettoken(tokenlist), { ")" })
+ result.chunk = parser_chunk(tokenlist)
+ return result
+end
+
+function parser_return(tokenlist)
+ -- RETURN [EXPRESSION] ;
+ local result = {}
+ result.type = "return"
+ expecttoken(gettoken(tokenlist), { "return" })
+ if peektoken(tokenlist).type == ";" then
+ gettoken(tokenlist)
+ return result
+ end
+ result.expression = parser_expression(tokenlist)
+ expecttoken(gettoken(tokenlist), { ";" })
+ return result
+end
+
+function parser_chunk(tokenlist)
+ local token = peektoken(tokenlist)
+ if token.type == "{" then
+ return parser_stmtlist(tokenlist)
+ else
+ return parser_statement(tokenlist)
+ end
+end
+
+function parser_expression(tokenlist)
+ -- we support:
+ -- ( ) + - < > == =
+ -- of those, ( ) has precedence 0
+ -- + - has precedence 3
+ -- < > has precedence 5
+ -- == has precedence 6
+ -- = has precedence 13
+
+ local operators = {
+ ["*"] = { precedence = 2, associative = "left" },
+ ["/"] = { precedence = 2, associative = "left" },
+ ["+"] = { precedence = 3, associative = "left" },
+ ["-"] = { precedence = 3, associative = "left" },
+ ["<"] = { precedence = 5, associative = "left" },
+ [">"] = { precedence = 5, associative = "left" },
+ ["=="] ={ precedence = 6, associative = "left" },
+ ["="] = { precedence = 13, associative = "left" }
+ }
+
+ -- Shunting yard algorithm
+ local outstack = {}
+ local opstack = {}
+
+ local addopertooutstack = function(oper)
+ assert(table.getn(outstack) >= 2, "attempted to add operator "..oper.value.." to stack containing "..table.getn(outstack).." elements")
+ oper.right = table.remove(outstack, table.getn(outstack))
+ oper.left = table.remove(outstack, table.getn(outstack))
+ table.insert(outstack, oper)
+ end
+
+ while true do
+ local token = peektoken(tokenlist)
+ if token.type == "identifier" or token.type == "number" then
+ gettoken(tokenlist)
+
+ if peektoken(tokenlist).type == "(" then -- function call guyz!
+ gettoken(tokenlist)
+ local outp = { type = "funccall", name = token.value, args = {} }
+ local token = peektoken(tokenlist)
+ while token.type ~= ")" do
+ table.insert(outp.args, parser_expression(tokenlist))
+ token = expecttoken(peektoken(tokenlist), { ")", "," })
+ if token.type == "," then
+ gettoken(tokenlist)
+ end
+ end
+ gettoken(tokenlist)
+
+ table.insert(outstack, outp)
+ else
+ table.insert(outstack, { type = token.type, value = token.value })
+ end
+ elseif token.type == "(" then
+ gettoken(tokenlist)
+
+ local outp = parser_expression(tokenlist)
+ expecttoken(gettoken(tokenlist), { ")" })
+ table.insert(outstack, outp)
+ elseif operators[token.type] then
+ gettoken(tokenlist)
+
+ while table.getn(opstack) > 0 and
+ ((operators[token.type].associative == "left" and
+ operators[token.type].precedence >= operators[opstack[table.getn(opstack)].value].precedence) or
+ (operators[token.type].associative == "right" and
+ operators[token.type].precedence > operators[opstack[table.getn(opstack)].value].precedence)) do
+ addopertooutstack(table.remove(opstack, table.getn(opstack)))
+ end
+ table.insert(opstack, { type = "operator", value = token.type })
+ elseif token.type == ")" or token.type == ";" or token.type == "," then
+ break
+ end
+ end
+
+ while table.getn(opstack) > 0 do
+ addopertooutstack(table.remove(opstack, table.getn(opstack)))
+ end
+ assert(table.getn(outstack) == 1, "wrong number of objects ("..table.getn(outstack)..") on output stack after expression parse")
+ return { type = "expression", value = outstack[1] }
+end
+
+function parse(tokenlist)
+ local parsetree = {}
+ parser_global(tokenlist, parsetree)
+ return parsetree
+end