commit b28889d61364fba929d4c9124b6ca3600cfc7a30
parent 64c941a864332e2fa9813345b8885636ff124b5f
Author: citbl <citbl@citbl.org>
Date: Tue, 26 May 2026 22:37:45 +1000
more parsing
Diffstat:
3 files changed, 188 insertions(+), 18 deletions(-)
diff --git a/mtcl/ast.lua b/mtcl/ast.lua
@@ -14,6 +14,22 @@ function AST:use(lib)
}
end
+function AST:postfix(op, operand)
+ return {
+ op = op,
+ operand = operand,
+ is_postfix = true,
+ }
+end
+
+function AST:unary(op, operand)
+ return {
+ op = op,
+ operand = operand,
+ is_postfix = false,
+ }
+end
+
function AST:binary(op, lhs, rhs)
return {
op = op,
diff --git a/mtcl/parser.lua b/mtcl/parser.lua
@@ -1,5 +1,6 @@
-local parser = { pos = 1, nodes = {} }
+local parser = { pos = 1 }
local ast = require("ast")
+local utils = require("utils")
function parser:peek()
return self.tokens[self.pos] or 0
@@ -29,13 +30,114 @@ function parser:match_ident()
end
return current
end
+-----------------------
-function parser:add(ast)
- print("(+) adding node: " .. ast.kind)
- self.nodes[#self.nodes + 1] = ast
+function parser:parse_postfix()
+ local node = parse_primary()
+
+ while true do
+ if parser:peek().kind == TK.PLUSPLUS then
+ return ast.postfix(OPER.POSTINCR, node)
+ elseif parser:peek().kind == TK.MINUSMINUS then
+ return ast.postfix(OPER.POSTDECR, node)
+ elseif parser:peek().kind == TK.L_BRACKET then -- parse inside brackets[]
+ local index = parser:parse_expression()
+ parser:expect(TK.R_BRACKET)
+ return ast.subscript(node, index)
+ elseif parser:peek().kind == TK.L_PAREN then
+ -- arguments
+ local args = parser:parse_arguments()
+ return ast.call(node, args)
+ elseif parser:peek().kind == TK.L_BRACE then -- parse name{} we don't hold grammar for this...
+ else
+ break
+ -- todo .member and potentially arrow operators here
+ end
+ end
+end
+
+function parser:parse_unary()
+ local inner = 0
+
+ if parser:peek().kind == TK.MINUS then
+ parser:advance()
+ return ast.unary(OPER.MINUS, parser:parse_unary())
+ elseif parser:peek().kind == TK.MINUSMINUS then
+ parser:advance()
+ return ast.unary(OPER.PREDECR, parser:parse_unary())
+ elseif parser:peek().kind == TK.BANG then
+ parser:advance()
+ return ast.unary(OPER.BANG, parser:parse_unary())
+ -- TODO add others '~a' '$a' '*a' '^a' '@a' '&a' ?
+ elseif parser:peek().kind == TK.PLUSPLUS then
+ parser:advance()
+ return ast.unary(OPER.PREINCR, parser:parse_unary())
+ elseif parser:peek().kind == TK.PLUS then
+ parser:advance()
+ return parser:parse_unary()
+ else
+ return parser:parse_postfix()
+ end
end
-function parser:parse_relational() end
+function parser:parse_term()
+ return parser:parse_unary()
+end
+
+function parser:parse_multiplicative()
+ local node = parse_term()
+
+ while true do
+ if parser:peek().kind == TK.STAR then
+ local rhs = parse_unary()
+ node = ast.binary(OP_MUL, node, rhs)
+ elseif parser:peek().kind == TK.SLASH then
+ local rhs = parse_unary()
+ node = ast.binary(OP_DIV, node, rhs)
+ elseif parser:peek().kind == TK.PERCENT then
+ local rhs = parse_unary()
+ node = ast.binary(OP_MOD, node, rhs)
+ else
+ break
+ end
+ end
+ return node
+end
+
+function parser:parse_additive()
+ local node = parse_multiplicative()
+
+ while true do
+ if parser:peek().kind == TK.PLUS then
+ local rhs = parse_multiplicative()
+ node = ast.binary(OP.PLUS, node, rhs)
+ elseif parser:peek().kind == TK.MINUS then
+ local rhs = parse_multiplicative()
+ node = ast.binary(OP.MINUS, node, rhs)
+ else
+ break
+ end
+ end
+ return node
+end
+
+function parser:parse_relational()
+ local node = parse_additive()
+
+ while true do
+ if parse:peek().kind == TK.LT then
+ local rhs = parser:parse_additive()
+ node = ast.binary(OP.LT, node, rhs)
+ elseif parse:peek().kind == TK.LTEQ then
+ local rhs = parser:parse_additive()
+ node = ast.binary(OP.LT_EQ, node, rhs)
+ -- todo more relationals
+ else
+ break
+ end
+ end
+ return node
+end
function parser:parse_equality()
local node = parser:parse_relational()
@@ -84,10 +186,8 @@ end
function parser:parse_as_cast()
print("todo parse_as_cast")
end
-function parser:parse_call()
- -- parse ident, parse parameters
- local callee = parser:expect(TK.IDENT)
- print("parse_call")
+
+function parser:parse_arguments()
parser:expect(TK.L_PAREN) -- swallow (
local args = {}
while parser:peek().kind ~= TK.R_PAREN do -- call parameters
@@ -98,7 +198,18 @@ function parser:parse_call()
else
break
end
+ parser:advance()
end
+ print(parser:peek().kind)
+ parser:expect(TK.R_PAREN)
+ return args
+end
+
+function parser:parse_call()
+ -- parse ident, parse parameters
+ local callee = parser:expect(TK.IDENT)
+ print("parse_call")
+ local args = parser:parse_arguments()
return ast.call(callee, args)
end
@@ -107,6 +218,7 @@ function parser:parse_statement()
local second = parser:nextpeek().kind
if first == TK.IDENT then
if second == TK.L_PAREN then
+ print("parsing a call statement")
return parser:parse_call()
elseif second == TK.IDENT then
return parser:parse_var()
@@ -127,6 +239,7 @@ function parser:parse_statement()
print("parse_statement unhandled keyword yet: " .. kw.lexeme)
end
end
+ return { name = "idk" }
end
function parser:parse_function(is_exported)
@@ -161,13 +274,20 @@ function parser:parse_function(is_exported)
parser:expect(TK.COLONCOLON, "no rbrace start of function")
while parser:peek().lexeme ~= "end" do
- -- function body statements
+ ::continue::
+ local tok = parser:peek()
+ if tok.kind == TK.EOS then
+ parser:advance()
+ goto continue
+ end
+ -- function body statements'
+ print("parsing a body statement " .. parser:peek().lexeme)
local stmt = parser:parse_statement()
statements[#statements + 1] = stmt
parser:advance()
end
local node = ast.func(name, parameters, ret, statements)
- parser:add(node)
+ return node
end
function parser:parse_use()
@@ -177,13 +297,14 @@ function parser:parse_use()
self:advance()
self:expect(TK.EOS, "use statement found without a end statement \\n|;")
local node = ast.use(lib)
- parser:add(node)
+ return node
end
function unhandled(token, extramsg)
print(
- token.file
- .. " at L"
+ "\27[33mat "
+ .. token.file
+ .. " L"
.. token.line
.. ":"
.. token.col
@@ -192,9 +313,11 @@ function unhandled(token, extramsg)
.. "\t "
.. (extramsg or "")
)
+ print(" |> " .. debug.traceback() .. "\27[0m")
end
function parser:parse_program()
+ local nodes = {}
while self:peek() ~= 0 do
local token = self:peek()
local kind = token.kind
@@ -203,17 +326,18 @@ function parser:parse_program()
if kind == TK.EOS then
-- nothing, advance
elseif kind == TK.KEYWORD and word == "use" then
- self:parse_use()
+ nodes[#nodes + 1] = self:parse_use()
elseif kind == TK.KEYWORD and word == "fx" then
- self:parse_function(true)
+ nodes[#nodes + 1] = self:parse_function(true)
elseif kind == TK.KEYWORD and word == "fn" then
- self:parse_function(false)
+ nodes[#nodes + 1] = self:parse_function(false)
else
unhandled(token)
break
end
self:advance()
end
+ return nodes
end
function parser:parse(file, src, tokens)
@@ -223,7 +347,9 @@ function parser:parse(file, src, tokens)
self.pos = 1
local i = 1
- self:parse_program()
+ local nodes = self:parse_program()
+
+ utils.print_table(nodes)
-- while i <= #tokens do
-- local t = tokens[i]
diff --git a/mtcl/utils.lua b/mtcl/utils.lua
@@ -0,0 +1,28 @@
+local utils = {}
+
+-- adapted from https://gist.github.com/ripter/4270799
+function utils.print_table(tbl, indent)
+ if not tbl then
+ return
+ end
+ if not indent then
+ indent = 0
+ end
+ for k, v in pairs(tbl) do
+ if k == "line" or k == "col" or k == "file" then
+ goto pass
+ end
+ formatting = string.rep(" ", indent) .. k .. ": "
+ if type(v) == "table" then
+ print(formatting)
+ utils.print_table(v, indent + 1)
+ elseif type(v) == "boolean" then
+ print(formatting .. tostring(v))
+ else
+ print(formatting .. v)
+ end
+ ::pass::
+ end
+end
+
+return utils