const Token = @import("tokenizer.zig").Token; const TokenType = @import("tokenizer.zig").TokenType; const std = @import("std"); const types = @import("types.zig"); const CodeRegion = @import("types.zig").CodeRegion; pub const ChunkNode = struct { block: BlockNode, startRegion: CodeRegion, endRegion: CodeRegion, pub fn dump(self: *const ChunkNode, indent: usize) void { std.debug.print("ChunkNode ({} - {}):\n", .{self.startRegion, self.endRegion}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("block: ", .{}); self.block.dump(indent + 1); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const BlockNode = struct { stats: std.ArrayList(StatNode), retstat: ?RetstatNode, startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const BlockNode, indent: usize) void { std.debug.print("BlockNode:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("stats:\n", .{}); for (0..indent + 1) |_| { std.debug.print("\t", .{}); } std.debug.print("[\n", .{}); for(self.stats.items) |stat| { for (0..indent + 2) |_| { std.debug.print("\t", .{}); } dumpStatNode(stat, indent + 2); } for (0..indent + 1) |_| { std.debug.print("\t", .{}); } std.debug.print("]\n", .{}); for (0..indent + 1) |_| { std.debug.print("\t", .{}); } std.debug.print("retstat: ", .{}); if(self.retstat == null) { std.debug.print("null\n", .{}); } else { self.retstat.?.dump(indent + 1); } for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const RetstatNode = struct { values: ?ExplistNode, startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const RetstatNode, indent: usize) void { std.debug.print("Retstat Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("values: ", .{}); if(self.values == null) { std.debug.print("null\n", .{}); } else { self.values.?.dump(indent + 1); } for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const StatNode = union(enum) { Semicolon, Assignment: AssignmentNode, Functioncall: FunctioncallNode, Label: []u8, Break, Goto: []u8, Do: BlockNode, While: WhileNode, Repeat: RepeatNode, If: IfNode, ForNumerical: ForNumericalNode, ForGeneric: ForGenericNode, Function: FunctionNode, LocalFunction: LocalFunctionNode, Local: LocalNode, }; fn dumpStatNode(stat: StatNode, indent: usize) void { switch(stat) { .Semicolon => std.debug.print("Semicolon\n", .{}), .Assignment => |*node| node.dump(indent), .Functioncall => |*node| node.dump(indent), .Label => |*label| std.debug.print("Label: '{s}'\n", .{label.*}), .Break => std.debug.print("Break\n", .{}), .Goto => |*label| std.debug.print("Goto: '{s}'\n", .{label.*}), .Do => |*node| node.dump(indent), .While => |*node| node.dump(indent), .Repeat => |*node| node.dump(indent), .If => |*node| node.dump(indent), .ForNumerical => |*node| node.dump(indent), .ForGeneric => |*node| node.dump(indent), .Function => |*node| node.dump(indent), .LocalFunction => |*node| node.dump(indent), .Local => |*node| node.dump(indent), } } pub const AssignmentNode = struct { lhs: VarlistNode, rhs: ExplistNode, startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const AssignmentNode, indent: usize) void { std.debug.print("Assignment Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("lhs: ", .{}); self.lhs.dump(indent + 1); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("rhs: ", .{}); self.rhs.dump(indent + 1); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const WhileNode = struct { condition: ExpNode, body: BlockNode, startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const WhileNode, indent: usize) void { std.debug.print("While Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("condition: ", .{}); dumpExpNode(self.condition, indent + 1); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("body: ", .{}); self.body.dump(indent + 1); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const RepeatNode = struct { condition: ExpNode, body: BlockNode, startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const RepeatNode, indent: usize) void { std.debug.print("Repeat Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("condition: ", .{}); dumpExpNode(self.condition, indent + 1); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("body: ", .{}); self.body.dump(indent + 1); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const IfNode = struct { condition: ExpNode, body: BlockNode, elseifs: std.ArrayList(ElseifNode), else_: ?BlockNode, startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const IfNode, indent: usize) void { std.debug.print("If Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("condition: ", .{}); dumpExpNode(self.condition, indent + 1); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("body: ", .{}); self.body.dump(indent + 1); for (0..indent + 1) |_| { std.debug.print("\t", .{}); } std.debug.print("elseifs:\n", .{}); for (0..indent + 1) |_| { std.debug.print("\t", .{}); } std.debug.print("[\n", .{}); for(self.elseifs.items) |elseif| { for (0..indent + 2) |_| { std.debug.print("\t", .{}); } elseif.dump(indent + 2); } for (0..indent + 1) |_| { std.debug.print("\t", .{}); } std.debug.print("]\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("else: ", .{}); if(self.else_ == null) { std.debug.print("null\n", .{}); } else { self.else_.?.dump(indent + 1); } for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const ForNumericalNode = struct { variable: []u8, start: ExpNode, end: ExpNode, change: ?ExpNode, body: BlockNode, startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const ForNumericalNode, indent: usize) void { std.debug.print("For Numerical Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("variable: '{s}'\n", .{self.variable}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("start: ", .{}); dumpExpNode(self.start, indent + 1); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("end: ", .{}); dumpExpNode(self.end, indent + 1); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("change: ", .{}); if(self.change == null) { std.debug.print("null\n", .{}); } else { dumpExpNode(self.start, indent + 1); } for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("body: ", .{}); self.body.dump(indent + 1); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const ForGenericNode = struct { vars: std.ArrayList([]u8), exps: ExplistNode, body: BlockNode, startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const ForGenericNode, indent: usize) void { std.debug.print("For Generic Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("vars:\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("[\n", .{}); for(self.vars.items) |v| { for (0..(indent + 2)) |_| { std.debug.print("\t", .{}); } std.debug.print("'{s}'\n", .{v}); } for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("]\n", .{}); std.debug.print("exps: ", .{}); self.exps.dump(indent + 1); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("body: ", .{}); self.body.dump(indent + 1); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const FunctionNode = struct { name: FuncnameNode, body: FuncbodyNode, startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const FunctionNode, indent: usize) void { std.debug.print("Function Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("name: ", .{}); self.name.dump(indent + 1); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("body: ", .{}); self.body.dump(indent + 1); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const LocalFunctionNode = struct { name: []u8, body: FuncbodyNode, startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const LocalFunctionNode, indent: usize) void { std.debug.print("Local Function Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("name: '{s}'\n", .{self.name}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("body: ", .{}); self.body.dump(indent + 1); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const LocalNode = struct { attnames: AttnamelistNode, values: ?ExplistNode, startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const LocalNode, indent: usize) void { std.debug.print("Local Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("attnames: ", .{}); self.attnames.dump(indent + 1); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("values: ", .{}); if(self.values == null) { std.debug.print("null\n", .{}); } else { self.values.?.dump(indent + 1); } for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const FunctioncallNode = struct { function: SuffixexpNode, objectArg: ?[]u8, args: ArgsNode, startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const FunctioncallNode, indent: usize) void { std.debug.print("Functioncall Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("function: ", .{}); dumpSuffixExpNode(self.function, indent + 1); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("object arg: ", .{}); if(self.objectArg == null) { std.debug.print("null\n", .{}); } else { std.debug.print("'{s}'\n", .{self.objectArg.?}); } for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("args: ", .{}); dumpArgsNode(self.args, indent + 1); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const VarlistNode = struct { vars: std.ArrayList(VarNode), startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const VarlistNode, indent: usize) void { std.debug.print("Varlist Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("vars:\n", .{}); for (0..indent + 1) |_| { std.debug.print("\t", .{}); } std.debug.print("[\n", .{}); for(self.vars.items) |item| { for (0..indent + 2) |_| { std.debug.print("\t", .{}); } dumpVarNode(item, indent + 2); } for (0..indent + 1) |_| { std.debug.print("\t", .{}); } std.debug.print("]\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const ExplistNode = struct { exps: std.ArrayList(ExpNode), startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const ExplistNode, indent: usize) void { std.debug.print("Explist Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..indent + 1) |_| { std.debug.print("\t", .{}); } std.debug.print("exps:\n", .{}); for (0..indent + 1) |_| { std.debug.print("\t", .{}); } std.debug.print("[\n", .{}); for(self.exps.items) |item| { for (0..indent + 2) |_| { std.debug.print("\t", .{}); } dumpExpNode(item, indent + 2); } for (0..indent + 1) |_| { std.debug.print("\t", .{}); } std.debug.print("]\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const ExpNode = union(enum) { Nil, False, True, Numeral: types.Numeral, LiteralString: []u8, Varargs, Functiondef: FuncbodyNode, Suffixexp: *SuffixexpNode, Tableconstructor: TableconstructorNode, Unop: UnopNode, Binop: *BinopNode, }; fn dumpExpNode(expNode: ExpNode, indent: usize) void { switch(expNode) { .Nil => std.debug.print("Nil\n", .{}), .False => std.debug.print("False\n", .{}), .True => std.debug.print("True\n", .{}), .Numeral => |*numeral| std.debug.print("{}\n", .{numeral.*}), .LiteralString => |*string| std.debug.print("LiteralString: '{s}'\n", .{string.*}), .Varargs => std.debug.print("Varargs", .{}), .Functiondef => |*node| node.dump(indent), .Suffixexp => |*node| dumpSuffixExpNode(node.*.*, indent), .Tableconstructor => |*node| node.dump(indent), .Unop => |*node| node.dump(indent), .Binop => |*node| node.*.dump(indent), } } pub const UnopNode = struct { unopType: UnopType, exp: *ExpNode, startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const UnopNode, indent: usize) void { std.debug.print("Unop Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("unop type: {}\n", .{self.unopType}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("exp: ", .{}); dumpExpNode(self.exp.*, indent + 1); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const BinopNode = struct { lhs: ExpNode, op: BinopType, rhs: ExpNode, startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const BinopNode, indent: usize) void { std.debug.print("Binop Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("lhs: ", .{}); dumpExpNode(self.lhs, indent + 1); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("op: {}\n", .{self.op}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("rhs: ", .{}); dumpExpNode(self.rhs, indent + 1); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const ElseifNode = struct { condition: ExpNode, body: BlockNode, startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const ElseifNode, indent: usize) void { std.debug.print("Elseif Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("condition: ", .{}); dumpExpNode(self.condition, indent + 1); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("body: ", .{}); self.body.dump(indent + 1); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const FuncnameNode = struct { name: []u8, dottedNames: std.ArrayList([]u8), firstArg: ?[]u8, startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const FuncnameNode, indent: usize) void { std.debug.print("Funcname Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("name: '{s}'\n", .{self.name}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("dottedNames:\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("[\n", .{}); for(self.dottedNames.items) |dottedName| { for (0..(indent + 2)) |_| { std.debug.print("\t", .{}); } std.debug.print("'{s}'\n", .{dottedName}); } for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("]\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("firstArg: ", .{}); if(self.firstArg == null) { std.debug.print("null\n", .{}); } else { std.debug.print("'{s}'\n", .{self.firstArg.?}); } for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const FuncbodyNode = struct { pars: ?ParlistNode, body: BlockNode, startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const FuncbodyNode, indent: usize) void { std.debug.print("Funcbody Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("pars: ", .{}); if(self.pars == null) { std.debug.print("null\n", .{}); } else { self.pars.?.dump(indent + 1); } for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } self.body.dump(indent + 1); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const AttnamelistNode = struct { attnames: std.ArrayList(AttnameNode), startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const AttnamelistNode, indent: usize) void { std.debug.print("Attnamelist Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("attNames:\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("[\n", .{}); for(self.attnames.items) |attNames| { for (0..(indent + 2)) |_| { std.debug.print("\t", .{}); } attNames.dump(indent + 2); } for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("]\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const AttnameNode = struct { name: []u8, attribute: ?[]u8, startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const AttnameNode, indent: usize) void { std.debug.print("Funcname Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("name: '{s}'\n", .{self.name}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("attribute: ", .{}); if(self.attribute == null) { std.debug.print("null\n", .{}); } else { std.debug.print("'{s}'\n", .{self.attribute.?}); } for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const SuffixexpNode = union(enum) { Normal: NormalSuffixNode, Functioncall: *FunctioncallNode, }; fn dumpSuffixExpNode(suffixexpNode: SuffixexpNode, indent: usize) void { switch(suffixexpNode) { .Normal => |*node| node.dump(indent), .Functioncall => |*node| node.*.dump(indent), } } pub const ArgsNode = union(enum) { Bracketed: ?ExplistNode, Tableconstructor: TableconstructorNode, Literal: []u8, }; fn dumpArgsNode(argsNode: ArgsNode, indent: usize) void { switch(argsNode) { .Bracketed => |*name| { if(name.* == null) { std.debug.print("null\n", .{}); } else { name.*.?.dump(indent); } }, .Tableconstructor => |*node| node.dump(indent), .Literal => |*string| std.debug.print("Literal: '{s}'\n", .{string}), } } pub const VarNode = union(enum) { Name: []u8, Indexed: IndexedVarNode, Member: MemberVarNode, }; fn dumpVarNode(varNode: VarNode, indent: usize) void { switch(varNode) { .Name => |*name| std.debug.print("Name: '{s}'\n", .{name.*}), .Indexed => |*node| node.dump(indent), .Member => |*node| node.dump(indent), } } pub const IndexedVarNode = struct { value: SuffixexpNode, index: ExpNode, startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const IndexedVarNode, indent: usize) void { std.debug.print("Indexed Var Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("value: ", .{}); dumpSuffixExpNode(self.value, indent + 1); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("index: ", .{}); dumpExpNode(self.index, indent + 1); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const MemberVarNode = struct { value: SuffixexpNode, name: []u8, startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const MemberVarNode, indent: usize) void { std.debug.print("Member Var Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("value: ", .{}); dumpSuffixExpNode(self.value, indent + 1); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("name: '{s}'\n", .{self.name}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const TableconstructorNode = struct { exps: ?FieldlistNode, startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const TableconstructorNode, indent: usize) void { std.debug.print("Tableconstructor Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..indent + 1) |_| { std.debug.print("\t", .{}); } std.debug.print("exps: ", .{}); if(self.exps == null) { std.debug.print("null\n", .{}); } else { self.exps.?.dump(indent + 1); } for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const UnopType = enum { Minus, LogicalNot, Length, BinaryNot, }; pub const BinopType = enum { LogicalOr, LocicalAnd, Lt, Gt, LtEquals, GtEquals, NotEquals, Equals, BinaryOr, BinaryNot, BinaryAnd, Shl, Shr, Concat, Add, Sub, Mul, Div, IntDiv, Mod, Exp, }; pub const ParlistNode = struct { names: std.ArrayList([]u8), hasVarargs: bool, startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const ParlistNode, indent: usize) void { std.debug.print("Parlist Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("names:\n", .{}); for (0..indent + 1) |_| { std.debug.print("\t", .{}); } std.debug.print("[\n", .{}); for(self.names.items) |name| { for (0..indent + 2) |_| { std.debug.print("\t", .{}); } std.debug.print("'{s}'\n", .{name}); } for (0..indent + 1) |_| { std.debug.print("\t", .{}); } std.debug.print("]\n", .{}); for (0..indent + 1) |_| { std.debug.print("\t", .{}); } std.debug.print("has Varargs: {}\n", .{self.hasVarargs}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const NormalSuffixNode = struct { firstPart: SuffixexpFirstPart, suffixes: std.ArrayList(SuffixexpSuffix), startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const NormalSuffixNode, indent: usize) void { std.debug.print("Normal Suffix Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("First Part: ", .{}); dumpSuffixExpFirstPart(self.firstPart, indent + 1); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("Suffixes:\n", .{}); for (0..indent + 1) |_| { std.debug.print("\t", .{}); } std.debug.print("[\n", .{}); for(self.suffixes.items) |suffix| { for (0..(indent + 2)) |_| { std.debug.print("\t", .{}); } dumpSuffixSuffix(suffix, indent + 2); } for (0..indent + 1) |_| { std.debug.print("\t", .{}); } std.debug.print("]\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const SuffixexpFirstPart = union(enum) { Name: []u8, BracketedExpr: ExpNode, }; fn dumpSuffixExpFirstPart(suffixexpFirstPart: SuffixexpFirstPart, indent: usize) void { switch(suffixexpFirstPart) { .Name => |*name| std.debug.print("Name: '{s}'\n", .{name.*}), .BracketedExpr => |*node| dumpExpNode(node.*, indent), } } pub const SuffixexpSuffix = union(enum) { Dot: []u8, Indexed: ExpNode, Args: ArgsNode, ArgsFirstArg: ArgsFirstArgNode, }; fn dumpSuffixSuffix(suffixexpSuffix: SuffixexpSuffix, indent: usize) void { switch(suffixexpSuffix) { .Dot => |*name| std.debug.print("Dot: '{s}'\n", .{name.*}), .Indexed => |*node| dumpExpNode(node.*, indent), .Args => |*node| dumpArgsNode(node.*, indent), .ArgsFirstArg => |*node| node.dump(indent), } } pub const ArgsFirstArgNode = struct { name: []u8, rest: ArgsNode, startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const ArgsFirstArgNode, indent: usize) void { std.debug.print("Args First Arg Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("name: '{s}'\n", .{self.name}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("rest: ", .{}); dumpArgsNode(self.rest, indent + 1); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const FieldlistNode = struct { exps: std.ArrayList(FieldNode), startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const FieldlistNode, indent: usize) void { std.debug.print("Fieldlist Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("exps: ", .{}); for (0..indent + 1) |_| { std.debug.print("\t", .{}); } std.debug.print("[\n", .{}); for(self.exps.items) |exp| { for (0..(indent + 2)) |_| { std.debug.print("\t", .{}); } dumpFieldNode(exp, indent + 2); } for (0..indent + 1) |_| { std.debug.print("\t", .{}); } std.debug.print("]\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub const FieldNode = union(enum) { IndexedAssignment: IndexedAssignmentNode, Assignment: FieldAssignmentNode, Exp: ExpNode, }; pub const FieldAssignmentNode = struct { lhs: []u8, rhs: ExpNode, startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const FieldAssignmentNode, indent: usize) void { std.debug.print("Field Assignment Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("lhs: {s}\n", .{self.lhs}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("rhs: ", .{}); dumpExpNode(self.rhs, indent + 1); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; fn dumpFieldNode(fieldNode: FieldNode, indent: usize) void { switch(fieldNode) { .IndexedAssignment => |*node| node.dump(indent), .Assignment => |*node| node.dump(indent), .Exp => |*node| dumpExpNode(node.*, indent), } } pub const IndexedAssignmentNode = struct { index: ExpNode, rhs: ExpNode, startRegion: CodeRegion, endRegion: CodeRegion, fn dump(self: *const IndexedAssignmentNode, indent: usize) void { std.debug.print("Indexed Assignment Node:\n", .{}); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("{{\n", .{}); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("index: ", .{}); dumpExpNode(self.index, indent + 1); for (0..(indent + 1)) |_| { std.debug.print("\t", .{}); } std.debug.print("rhs: ", .{}); dumpExpNode(self.rhs, indent + 1); for (0..indent) |_| { std.debug.print("\t", .{}); } std.debug.print("}}\n", .{}); } }; pub fn parse(tokens: []Token, allocator: *std.heap.ArenaAllocator) !ChunkNode { var i: usize = 0; const maybeParsedChunk = parseChunk(tokens, &i, allocator) catch |err| { //std.debug.print("{any}: data: {any}, type: {any}\n", .{tokens[i].region, tokens[i].tokenData, tokens[i].tokenType}); return err; }; return maybeParsedChunk; } const ParserError = error { NotImplemented, ReachedEOFParsing, ReachedEOFExpectedNameForGoto, ExpectedNameForGoto, MissingEndForDoBlock, MissingDoAfterWhileCondition, MissingEndForWhileBody, ExpectedUntilAfterRepeatBody, ExpectedThenAfterIfCondition, ReachedEOFAfterIfBody, ExpectedThenAfterElseifCondition, ReachedEOFAfterElseifs, ExpectedEndClosingIf, ExpectedNameAfterFor, ExpectedCommaAfterForEqStartValue, ReachedEOFAfterForEqEndValue, ExpectedDoAfterForEqHead, ExpectedAnotherNameInForInNamelist, ReachedEOFAfterNameInForInNamelist, ExpectedInAfterForInNamelist, ExpectedDoAfterForInExplist, ExpectedEndAfterForInBody, ExpectedEndAfterForEqBody, UnexpectedTokenAfterFirstNameInFor, ReachedEOFInLocal, ExpectedLocalFunctionName, ExpectedNameAfterDoubleColonInLabelDeclaration, ExpectedDoubleColonAfterNameInLabelDeclaration, ExpectedFunctioncall, ExpectedEqAfterAssignmentVarList, ReachedEOFInSuffixExp, ExpectedRoundClosedClosingBracketedPrimaryExp, UnexpectedTokenAsFirstPartOfSuffixExp, ExpectedNameInDottedSuffixExp, ExpectedSquareClosedClosingIndexedSuffixExp, ExpectedNameInArgsFirstArgSuffixExp, ExpectedDotOrIndexedSuffixWhenConvertingSuffixExpToVar, ReachedEOFExpectedPrimaryExpression, ReachedEOFInArgs, ReachedEOFInBracketedArgs, ExpectedRoundClosedClosingBracketedArgs, UnexpectedTokenInArgs, NoPrecedenceForOperator, NoBinopTypeForOperator, ExpectednameInAttribName, ExpectedAttributeInAttrib, ExpectedGtInAttrib, ExpectedFuncname, ExpectedNameInDottedFuncname, ExpectedNameOfFirstArgInFuncname, ExpectedRoundOpenStartingFuncbody, ReachedEOFInFuncbodyParlist, ExpectedRoundClosedClosingFuncbodyParlist, ExpectedEndClosingFuncbody, ReachedEOFInParlist, ExpectedNameStartingParlist, ReachedEOFInParlistNameList, UnexpectedTokenInParlistNameList, ExpectedReturnStartingRetstat, ExpectedCurlyOpenOpeningTableconstructor, ExpectedCurlyClosedClosingTableconstructor, ReachedEOFInField, ExpectedSquareClosedClosingIndexedField, ExpectedEqualsInIndexedFieldExpression, OutOfMemory, }; fn parseChunk(tokens: []Token, i: *usize, allocator: *std.heap.ArenaAllocator) !ChunkNode { const block = try parseBlock(tokens, i, allocator); return ChunkNode { .block = block, .startRegion = block.startRegion, .endRegion = block.endRegion }; } fn parseBlock(tokens: []Token, i: *usize, allocator: *std.heap.ArenaAllocator) ParserError!BlockNode { var ret = BlockNode { .stats = std.ArrayList(StatNode).init(allocator.*.allocator()), .retstat = null, .startRegion = tokens[i.*].region, .endRegion = tokens[i.*].region }; while(i.* < tokens.len and tokens[i.*].tokenType != TokenType.Return and tokens[i.*].tokenType != TokenType.End and tokens[i.*].tokenType != TokenType.Elseif and tokens[i.*].tokenType != TokenType.Else ) { try ret.stats.append(try parseStat(tokens, i, allocator)); } if(i.* < tokens.len and tokens[i.*].tokenType == TokenType.Return) { ret.retstat = try parseRetstat(tokens, i, allocator); } ret.endRegion = if(i.* - 1 < tokens.len) tokens[i.* - 1].region else tokens[tokens.len - 1].region; return ret; } fn parseStat(tokens: []Token, i: *usize, allocator: *std.heap.ArenaAllocator) !StatNode { if(i.* >= tokens.len) { return error.ReachedEOFParsing; } switch(tokens[i.*].tokenType) { TokenType.Semicolon => { i.* += 1; return StatNode.Semicolon; }, TokenType.Break => { i.* += 1; return StatNode.Break; }, TokenType.Goto => { i.* += 1; if(i.* >= tokens.len) { return error.ReachedEOFExpectedNameForGoto; } if(tokens[i.*].tokenType == TokenType.Name) { const name = tokens[i.*].tokenData.string; i.* += 1; return StatNode { .Goto = name }; } return error.ExpectedNameForGoto; }, TokenType.Do => { i.* += 1; const body = try parseBlock(tokens, i, allocator); if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.End) { return error.MissingEndForDoBlock; } i.* += 1; return StatNode { .Do = body }; }, TokenType.While => { const startRegion = tokens[i.*].region; i.* += 1; const condition = try parseExp(tokens, i, allocator); if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.Do) { return error.MissingDoAfterWhileCondition; } const body = try parseBlock(tokens, i, allocator); if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.End) { return error.MissingEndForWhileBody; } const endRegion = tokens[i.*].region; i.* += 1; return StatNode { .While = WhileNode { .body = body, .condition = condition, .startRegion = startRegion, .endRegion = endRegion } }; }, TokenType.Repeat => { const startRegion = tokens[i.*].region; i.* += 1; const body = try parseBlock(tokens, i, allocator); if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.Until) { return error.ExpectedUntilAfterRepeatBody; } const endRegion = tokens[i.*].region; i.* += 1; return StatNode { .Repeat = RepeatNode { .body = body, .condition = try parseExp(tokens, i, allocator), .startRegion = startRegion, .endRegion = endRegion } }; }, TokenType.If => { const startRegion = tokens[i.*].region; i.* += 1; const condition = try parseExp(tokens, i, allocator); if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.Then) { return error.ExpectedThenAfterIfCondition; } i.* += 1; const body = try parseBlock(tokens, i, allocator); if(i.* >= tokens.len) { return error.ReachedEOFAfterIfBody; } var ifNode = IfNode { .body = body, .condition = condition, .elseifs = std.ArrayList(ElseifNode).init(allocator.*.allocator()), .else_ = null, .startRegion = startRegion, .endRegion = startRegion }; while(tokens[i.*].tokenType == TokenType.Elseif) { i.* += 1; const elseifCondition = try parseExp(tokens, i, allocator); if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.Then) { return error.ExpectedThenAfterElseifCondition; } const endRegion = tokens[i.*].region; i.* += 1; try ifNode.elseifs.append(ElseifNode { .body = try parseBlock(tokens, i, allocator), .condition = elseifCondition, .startRegion = startRegion, .endRegion = endRegion }); } if(i.* >= tokens.len) { return error.ReachedEOFAfterElseifs; } if(tokens[i.*].tokenType == TokenType.Else) { i.* += 1; ifNode.else_ = try parseBlock(tokens, i, allocator); } if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.End) { return error.ExpectedEndClosingIf; } ifNode.endRegion = tokens[i.*].region; i.* += 1; return StatNode { .If = ifNode }; }, TokenType.For => { const startRegion = tokens[i.*].region; i.* += 1; if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.Name) { return error.ExpectedNameAfterFor; } const variable = tokens[i.*].tokenData.string; i.* += 1; switch(tokens[i.*].tokenType) { TokenType.Equals => { i.* += 1; const start = try parseExp(tokens, i, allocator); if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.Comma) { return error.ExpectedCommaAfterForEqStartValue; } i.* += 1; const end = try parseExp(tokens, i, allocator); if(i.* >= tokens.len) { return error.ReachedEOFAfterForEqEndValue; } var change: ?ExpNode = null; if(tokens[i.*].tokenType == TokenType.Comma) { i.* += 1; change = try parseExp(tokens, i, allocator); } if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.Do) { return error.ExpectedDoAfterForEqHead; } i.* += 1; const body = try parseBlock(tokens, i, allocator); if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.End) { return error.ExpectedEndAfterForEqBody; } const endRegion = tokens[i.*].region; i.* += 1; return StatNode { .ForNumerical = ForNumericalNode { .variable = variable, .start = start, .end = end, .change = change, .body = body, .startRegion = startRegion, .endRegion = endRegion } }; }, TokenType.Comma => { var names = std.ArrayList([]u8).init(allocator.*.allocator()); try names.append(variable); while(tokens[i.*].tokenType == TokenType.Comma) { i.* += 1; if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.Name) { return error.ExpectedAnotherNameInForInNamelist; } try names.append(tokens[i.*].tokenData.string); i.* += 1; if(i.* >= tokens.len) { return error.ReachedEOFAfterNameInForInNamelist; } } if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.In) { return error.ExpectedInAfterForInNamelist; } i.* += 1; const exps = try parseExplist(tokens, i, allocator); if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.Do) { return error.ExpectedDoAfterForInExplist; } i.* += 1; const body = try parseBlock(tokens, i, allocator); if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.End) { return error.ExpectedEndAfterForInBody; } const endRegion = tokens[i.*].region; i.* += 1; return StatNode { .ForGeneric = ForGenericNode { .vars = names, .exps = exps, .body = body, .startRegion = startRegion, .endRegion = endRegion } }; }, TokenType.In => { i.* += 1; const exps = try parseExplist(tokens, i, allocator); if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.Do) { return error.ExpectedDoAfterForInExplist; } i.* += 1; const body = try parseBlock(tokens, i, allocator); if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.End) { return error.ExpectedEndAfterForInBody; } const endRegion = tokens[i.*].region; i.* += 1; var names = try std.ArrayList([]u8).initCapacity(allocator.allocator(), 1); try names.insert(0, variable); return StatNode { .ForGeneric = ForGenericNode { .vars = names, .exps = exps, .body = body, .startRegion = startRegion, .endRegion = endRegion } }; }, else => return error.UnexpectedTokenAfterFirstNameInFor, } }, TokenType.Function => { const startRegion = tokens[i.*].region; i.* += 1; const name = try parseFuncname(tokens, i, allocator); const body = try parseFuncbody(tokens, i, allocator); return StatNode { .Function = FunctionNode { .name = name, .body = body, .startRegion = startRegion, .endRegion = body.endRegion } }; }, TokenType.Local => { const startRegion = tokens[i.*].region; i.* += 1; if(i.* >= tokens.len) { return error.ReachedEOFInLocal; } if(tokens[i.*].tokenType == TokenType.Function) { i.* += 1; if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.Name) { return error.ExpectedLocalFunctionName; } const name = tokens[i.*].tokenData.string; const endRegion = tokens[i.*].region; i.* += 1; return StatNode { .LocalFunction = LocalFunctionNode { .name = name, .body = try parseFuncbody(tokens, i, allocator), .startRegion = startRegion, .endRegion = endRegion } }; } else { var ret = LocalNode { .attnames = try parseAttnamelist(tokens, i, allocator), .values = null, .startRegion = startRegion, .endRegion = startRegion }; if(i.* < tokens.len and tokens[i.*].tokenType == TokenType.Equals) { i.* += 1; ret.values = try parseExplist(tokens, i, allocator); ret.endRegion = ret.values.?.endRegion; } return StatNode { .Local = ret }; } }, TokenType.ColonColon => { i.* += 1; if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.Name) { return error.ExpectedNameAfterDoubleColonInLabelDeclaration; } const name = tokens[i.*].tokenData.string; i.* += 1; if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.ColonColon) { return error.ExpectedDoubleColonAfterNameInLabelDeclaration; } i.* += 1; return StatNode { .Label = name }; }, TokenType.Name, TokenType.RoundOpen => { const startRegion = tokens[i.*].region; const suffixExp = try parseSuffixExp(tokens, i, allocator); if(i.* >= tokens.len) { switch(suffixExp) { .Normal => return error.ExpectedFunctioncall, .Functioncall => |functioncall| return StatNode { .Functioncall = functioncall.* }, } } else { switch(tokens[i.*].tokenType) { TokenType.Equals => { const endRegion = tokens[i.*].region; i.* += 1; var lhs = std.ArrayList(VarNode).init(allocator.allocator()); try lhs.append(try suffixExpToVar(suffixExp, startRegion, endRegion)); const rhs = try parseExplist(tokens, i, allocator); return StatNode { .Assignment = AssignmentNode { .lhs = VarlistNode { .vars = lhs, .startRegion = endRegion, .endRegion = tokens[@min(i.*, tokens.len) - 1].region }, .rhs = rhs, .startRegion = startRegion, .endRegion = rhs.endRegion } }; }, TokenType.Comma => { var varlistNode = VarlistNode { .vars = std.ArrayList(VarNode).init(allocator.allocator()), .startRegion = startRegion, .endRegion = startRegion }; try varlistNode.vars.append(try suffixExpToVar(suffixExp, startRegion, tokens[@min(i.*, tokens.len) - 1].region)); while(tokens[i.*].tokenType == TokenType.Comma) { i.* += 1; try varlistNode.vars.append(try parseVar(tokens, i, allocator)); } if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.Equals) { return error.ExpectedEqAfterAssignmentVarList; } varlistNode.endRegion = tokens[i.*].region; i.* += 1; const rhs = try parseExplist(tokens, i, allocator); return StatNode { .Assignment = AssignmentNode { .lhs = varlistNode, .rhs = rhs, .startRegion = startRegion, .endRegion = rhs.endRegion } }; }, else => { switch(suffixExp) { .Normal => return error.ExpectedFunctioncall, .Functioncall => |functioncall| return StatNode { .Functioncall = functioncall.* }, } } } } }, else => { std.debug.print("{}\n", .{tokens[i.*]}); return error.NotImplemented; } } } fn parseRetstat(tokens: []Token, i: *usize, allocator: *std.heap.ArenaAllocator) !RetstatNode { if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.Return) { return error.ExpectedReturnStartingRetstat; } const startRegion = tokens[i.*].region; i.* += 1; if(i.* >= tokens.len or tokens[i.*].tokenType == TokenType.Semicolon or tokens[i.*].tokenType == TokenType.Else or tokens[i.*].tokenType == TokenType.Elseif or tokens[i.*].tokenType == TokenType.End) { if(i.* < tokens.len and tokens[i.*].tokenType == TokenType.Semicolon) { i.* += 1; } return RetstatNode { .values = null, .startRegion = startRegion, .endRegion = tokens[@min(i.*, tokens.len) - 1].region }; } const values = try parseExplist(tokens, i, allocator); var endRegion = values.endRegion; if(i.* < tokens.len and tokens[i.*].tokenType == TokenType.Semicolon) { endRegion = tokens[i.*].region; i.* += 1; } return RetstatNode { .values = values, .startRegion = startRegion, .endRegion = endRegion }; } fn parseExp(tokens: []Token, i: *usize, allocator: *std.heap.ArenaAllocator) ParserError!ExpNode { const lhs = try parseExpPrimary(tokens, i, allocator); return parseExpPrecedence(tokens, i, allocator, lhs, 0); } fn parseExpPrimary(tokens: []Token, i: *usize, allocator: *std.heap.ArenaAllocator) !ExpNode { if(i.* >= tokens.len) { return error.ReachedEOFExpectedPrimaryExpression; } const startRegion = tokens[i.*].region; switch(tokens[i.*].tokenType) { TokenType.Nil => { i.* += 1; return ExpNode.Nil; }, TokenType.True => { i.* += 1; return ExpNode.True; }, TokenType.False => { i.* += 1; return ExpNode.False; }, TokenType.Numeral => { const numeral = tokens[i.*].tokenData.numeral; i.* += 1; return ExpNode { .Numeral = numeral }; }, TokenType.StringLiteral => { const string = tokens[i.*].tokenData.string; i.* += 1; return ExpNode { .LiteralString = string }; }, TokenType.DotDotDot => { i.* += 1; return ExpNode.Varargs; }, TokenType.Function => { i.* += 1; return ExpNode { .Functiondef = try parseFuncbody(tokens, i, allocator) }; }, TokenType.CurlyOpen => return ExpNode { .Tableconstructor = try parseTableconstructor(tokens, i, allocator) }, TokenType.Minus => { i.* += 1; const unop = try allocator.allocator().create(ExpNode); unop.* = try parseExp(tokens, i, allocator); const endRegion = tokens[@min(i.*, tokens.len) - 1].region; return ExpNode { .Unop = UnopNode { .unopType = UnopType.Minus, .exp = unop, .startRegion = startRegion, .endRegion = endRegion } }; }, TokenType.Hash => { i.* += 1; const unop = try allocator.allocator().create(ExpNode); unop.* = try parseExp(tokens, i, allocator); const endRegion = tokens[@min(i.*, tokens.len) - 1].region; return ExpNode { .Unop = UnopNode { .unopType = UnopType.Length, .exp = unop, .startRegion = startRegion, .endRegion = endRegion } }; }, TokenType.Not => { i.* += 1; const unop = try allocator.allocator().create(ExpNode); unop.* = try parseExp(tokens, i, allocator); const endRegion = tokens[@min(i.*, tokens.len) - 1].region; return ExpNode { .Unop = UnopNode { .unopType = UnopType.LogicalNot, .exp = unop, .startRegion = startRegion, .endRegion = endRegion } }; }, TokenType.Tilde => { i.* += 1; const unop = try allocator.allocator().create(ExpNode); unop.* = try parseExp(tokens, i, allocator); const endRegion = tokens[@min(i.*, tokens.len) - 1].region; return ExpNode { .Unop = UnopNode { .unopType = UnopType.BinaryNot, .exp = unop, .startRegion = startRegion, .endRegion = endRegion } }; }, else => { const suffixexp = try allocator.allocator().create(SuffixexpNode); suffixexp.* = try parseSuffixExp(tokens, i, allocator); return ExpNode { .Suffixexp = suffixexp }; } } } fn parseExpPrecedence(tokens: []Token, i: *usize, allocator: *std.heap.ArenaAllocator, lhs: ExpNode, minPrecedence: u8) !ExpNode { var currentLhs = lhs; while(i.* < tokens.len and isBinop(tokens[i.*])) { const startRegion = tokens[i.*].region; const precedence = try getPrecedence(tokens[i.*]); if(precedence < minPrecedence) { break; } const op = try getBinopType(tokens[i.*]); i.* += 1; var rhs = try parseExpPrimary(tokens, i, allocator); while(i.* < tokens.len and isBinop(tokens[i.*]) and (try getPrecedence(tokens[i.*]) > precedence or (try getPrecedence(tokens[i.*]) == precedence and isRightAssociative(tokens[i.*])))) { const associativityBoost: u8 = if(try getPrecedence(tokens[i.*]) == precedence) 0 else 1; rhs = try parseExpPrecedence(tokens, i, allocator, rhs, precedence + associativityBoost); } const binop = try allocator.allocator().create(BinopNode); binop.* = BinopNode { .lhs = currentLhs, .op = op, .rhs = rhs, .startRegion = startRegion, .endRegion = tokens[@min(i.*, tokens.len) - 1].region }; currentLhs = ExpNode { .Binop = binop }; } return currentLhs; } fn isRightAssociative(token: Token) bool { return token.tokenType == TokenType.DotDot or token.tokenType == TokenType.Caret; } fn getBinopType(token: Token) !BinopType { return switch(token.tokenType) { TokenType.Or => BinopType.LogicalOr, TokenType.And => BinopType.LocicalAnd, TokenType.Lt => BinopType.Lt, TokenType.Gt => BinopType.Gt, TokenType.LtEquals => BinopType.LtEquals, TokenType.GtEquals => BinopType.GtEquals, TokenType.LtLt => BinopType.Shl, TokenType.GtGt=> BinopType.Shr, TokenType.TildeEquals => BinopType.NotEquals, TokenType.EqualsEquals => BinopType.Equals, TokenType.Pipe => BinopType.BinaryOr, TokenType.Tilde => BinopType.BinaryNot, TokenType.Ampersand => BinopType.BinaryAnd, TokenType.DotDot => BinopType.Concat, TokenType.Plus => BinopType.Add, TokenType.Minus => BinopType.Sub, TokenType.Star => BinopType.Mul, TokenType.Slash => BinopType.Div, TokenType.SlashSlash => BinopType.IntDiv, TokenType.Percent => BinopType.Mod, TokenType.Caret => BinopType.Exp, else => error.NoBinopTypeForOperator, }; } fn getPrecedence(token: Token) !u8 { return switch(token.tokenType) { TokenType.Or => 2, TokenType.And => 4, TokenType.Lt, TokenType.Gt, TokenType.LtEquals, TokenType.GtEquals, TokenType.TildeEquals, TokenType.EqualsEquals => 6, TokenType.Pipe => 8, TokenType.Tilde => 10, TokenType.Ampersand => 12, TokenType.LtLt, TokenType.GtGt => 14, TokenType.DotDot => 16, TokenType.Plus, TokenType.Minus => 18, TokenType.Star, TokenType.Slash, TokenType.SlashSlash, TokenType.Percent => 20, TokenType.Caret => 22, else => error.NoPrecedenceForOperator, }; } fn isBinop(token: Token) bool { return switch(token.tokenType) { TokenType.Or, TokenType.And, TokenType.Lt, TokenType.Gt, TokenType.LtEquals, TokenType.GtEquals, TokenType.TildeEquals, TokenType.EqualsEquals, TokenType.Pipe, TokenType.Tilde, TokenType.Ampersand, TokenType.LtLt, TokenType.GtGt, TokenType.DotDot, TokenType.Plus, TokenType.Minus, TokenType.Star, TokenType.Slash, TokenType.SlashSlash, TokenType.Percent, TokenType.Caret => true, else => false }; } fn parseExplist(tokens: []Token, i: *usize, allocator: *std.heap.ArenaAllocator) !ExplistNode { const startRegion = tokens[@min(i.*, tokens.len) - 1].region; var ret = ExplistNode { .exps = std.ArrayList(ExpNode).init(allocator.allocator()), .startRegion = startRegion, .endRegion = startRegion }; try ret.exps.append(try parseExp(tokens, i, allocator)); while(i.* < tokens.len and tokens[i.*].tokenType == TokenType.Comma) { i.* += 1; try ret.exps.append(try parseExp(tokens, i, allocator)); } ret.endRegion = tokens[@min(i.*, tokens.len) - 1].region; return ret; } fn parseFuncname(tokens: []Token, i: *usize, allocator: *std.heap.ArenaAllocator) !FuncnameNode { if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.Name) { return error.ExpectedFuncname; } const startRange = tokens[i.*].region; var ret = FuncnameNode { .name = tokens[i.*].tokenData.string, .dottedNames = std.ArrayList([]u8).init(allocator.allocator()), .firstArg = null, .startRegion = startRange, .endRegion = startRange }; i.* += 1; while(i.* < tokens.len and tokens[i.*].tokenType == TokenType.Dot) { i.* += 1; if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.Name) { return error.ExpectedNameInDottedFuncname; } try ret.dottedNames.append(tokens[i.*].tokenData.string); } if(i.* < tokens.len and tokens[i.*].tokenType == TokenType.Colon) { i.* += 1; if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.Name) { return error.ExpectedNameOfFirstArgInFuncname; } ret.firstArg = tokens[i.*].tokenData.string; i.* += 1; } ret.endRegion = tokens[@min(i.*, tokens.len) - 1].region; return ret; } fn parseFuncbody(tokens: []Token, i: *usize, allocator: *std.heap.ArenaAllocator) !FuncbodyNode { if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.RoundOpen) { return error.ExpectedRoundOpenStartingFuncbody; } const startRegion = tokens[i.*].region; i.* += 1; if(i.* >= tokens.len) { return error.ReachedEOFInFuncbodyParlist; } var pars: ?ParlistNode = null; if(tokens[i.*].tokenType == TokenType.RoundClosed) { i.* += 1; } else { pars = try parseParlist(tokens, i, allocator); if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.RoundClosed) { return error.ExpectedRoundClosedClosingFuncbodyParlist; } i.* += 1; } const ret = FuncbodyNode { .body = try parseBlock(tokens, i, allocator), .pars = pars, .startRegion = startRegion, .endRegion = tokens[i.*].region }; if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.End) { return error.ExpectedEndClosingFuncbody; } i.* += 1; return ret; } fn parseParlist(tokens: []Token, i: *usize, allocator: *std.heap.ArenaAllocator) !ParlistNode { if(i.* >= tokens.len) { return error.ReachedEOFInParlist; } const startRegion = tokens[i.*].region; if(tokens[i.*].tokenType == TokenType.DotDotDot) { const endRegion = tokens[i.*].region; i.* += 1; return ParlistNode { .names = std.ArrayList([]u8).init(allocator.allocator()), .hasVarargs = true, .startRegion = startRegion, .endRegion = endRegion }; } if(tokens[i.*].tokenType != TokenType.Name) { return error.ExpectedNameStartingParlist; } var ret = ParlistNode { .names = std.ArrayList([]u8).init(allocator.allocator()), .hasVarargs = false, .startRegion = startRegion, .endRegion = startRegion }; try ret.names.append(tokens[i.*].tokenData.string); i.* += 1; while(i.* < tokens.len and tokens[i.*].tokenType == TokenType.Comma) { i.* += 1; if(i.* >= tokens.len) { return error.ReachedEOFInParlistNameList; } switch(tokens[i.*].tokenType) { TokenType.Name => { try ret.names.append(tokens[i.*].tokenData.string); i.* += 1; }, TokenType.DotDotDot => { i.* += 1; ret.hasVarargs = true; break; }, else => return error.UnexpectedTokenInParlistNameList, } } ret.endRegion = tokens[@min(i.*, tokens.len) - 1].region; return ret; } fn parseAttnamelist(tokens: []Token, i: *usize, allocator: *std.heap.ArenaAllocator) !AttnamelistNode { // TODO: What happens if this is reaches EOF? var ret = AttnamelistNode { .attnames = std.ArrayList(AttnameNode).init(allocator.allocator()), .startRegion = tokens[i.*].region, .endRegion = tokens[i.*].region }; try ret.attnames.append(try parseAttname(tokens, i)); while(i.* < tokens.len and tokens[i.*].tokenType == TokenType.Comma) { i.* += 1; try ret.attnames.append(try parseAttname(tokens, i)); } ret.endRegion = tokens[@min(i.*, tokens.len) - 1].region; return ret; } fn parseSuffixExp(tokens: []Token, i: *usize, allocator: *std.heap.ArenaAllocator) !SuffixexpNode { // primaryexp { '.' 'Name' | '[' exp']' | ':' 'Name' args | args } if(i.* >= tokens.len) { return error.ReachedEOFInSuffixExp; } const startRegion = tokens[i.*].region; const firstPart = try switch(tokens[i.*].tokenType) { TokenType.Name => nameBlock: { const name = tokens[i.*].tokenData.string; i.* += 1; break :nameBlock SuffixexpFirstPart { .Name = name }; }, TokenType.RoundOpen => roundOpenBlock: { i.* += 1; const ret = SuffixexpFirstPart { .BracketedExpr = try parseExp(tokens, i, allocator) }; if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.RoundClosed) { return error.ExpectedRoundClosedClosingBracketedPrimaryExp; } i.* += 1; break :roundOpenBlock ret; }, else => error.UnexpectedTokenAsFirstPartOfSuffixExp, }; var suffixes = std.ArrayList(SuffixexpSuffix).init(allocator.allocator()); while(i.* < tokens.len) { switch(tokens[i.*].tokenType) { TokenType.Dot => { i.* += 1; if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.Name) { return error.ExpectedNameInDottedSuffixExp; } const name = tokens[i.*].tokenData.string; i.* += 1; try suffixes.append(SuffixexpSuffix { .Dot = name }); }, TokenType.SquareOpen => { i.* += 1; try suffixes.append(SuffixexpSuffix { .Indexed = try parseExp(tokens, i, allocator) }); if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.SquareClosed) { return error.ExpectedSquareClosedClosingIndexedSuffixExp; } i.* += 1; }, TokenType.Colon => { const argsFirstArgStartRegion = tokens[i.*].region; i.* += 1; if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.Name) { return error.ExpectedNameInArgsFirstArgSuffixExp; } const name = tokens[i.*].tokenData.string; const argsFirstArgEndRegion = tokens[i.*].region; i.* += 1; try suffixes.append(SuffixexpSuffix { .ArgsFirstArg = ArgsFirstArgNode { .name = name, .rest = try parseArgs(tokens, i, allocator), .startRegion = argsFirstArgStartRegion, .endRegion = argsFirstArgEndRegion } }); }, TokenType.RoundOpen, TokenType.CurlyOpen, TokenType.StringLiteral => { try suffixes.append(SuffixexpSuffix { .Args = try parseArgs(tokens, i, allocator) }); }, else => break, } } const endRegion = tokens[@min(i.*, tokens.len) - 1].region; const last = suffixes.getLastOrNull(); if(last != null) { switch(last.?) { SuffixexpSuffix.Args => |*args| { _ = suffixes.pop(); const functioncall = try allocator.allocator().create(FunctioncallNode); functioncall.* = FunctioncallNode { .function = SuffixexpNode { .Normal = NormalSuffixNode { .firstPart = firstPart, .suffixes = suffixes, .startRegion = startRegion, .endRegion = endRegion } }, .args = args.*, .objectArg = null, .startRegion = startRegion, .endRegion = endRegion, }; return SuffixexpNode { .Functioncall = functioncall, }; }, SuffixexpSuffix.ArgsFirstArg => |*node| { _ = suffixes.pop(); const functioncall = try allocator.allocator().create(FunctioncallNode); functioncall.* = FunctioncallNode { .function = SuffixexpNode { .Normal = NormalSuffixNode { .firstPart = firstPart, .suffixes = suffixes, .startRegion = startRegion, .endRegion = endRegion } }, .args = node.rest, .objectArg = node.name, .startRegion = startRegion, .endRegion = endRegion, }; return SuffixexpNode { .Functioncall = functioncall, }; }, else => {} } } return SuffixexpNode { .Normal = NormalSuffixNode { .firstPart = firstPart, .suffixes = suffixes, .startRegion = startRegion, .endRegion = endRegion } }; } fn parseVar(tokens: []Token, i: *usize, allocator: *std.heap.ArenaAllocator) !VarNode { const startRegion = tokens[i.*].region; return suffixExpToVar(try parseSuffixExp(tokens, i, allocator), startRegion, tokens[@min(i.*, tokens.len) - 1].region); } fn parseAttname(tokens: []Token, i: *usize) !AttnameNode { if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.Name) { return error.ExpectednameInAttribName; } const name = tokens[i.*].tokenData.string; const startRegion = tokens[i.*].region; i.* += 1; var ret = AttnameNode { .name = name, .attribute = null, .startRegion = startRegion, .endRegion = startRegion }; if(i.* < tokens.len and tokens[i.*].tokenType == TokenType.Lt) { ret.attribute = tokens[i.*].tokenData.string; i.* += 1; if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.Name) { return error.ExpectedAttributeInAttrib; } ret.endRegion = tokens[i.*].region; i.* += 1; if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.Gt) { return error.ExpectedGtInAttrib; } } return ret; } fn parseArgs(tokens: []Token, i: *usize, allocator: *std.heap.ArenaAllocator) !ArgsNode { if(i.* >= tokens.len) { return error.ReachedEOFInArgs; } switch(tokens[i.*].tokenType) { TokenType.RoundOpen => { i.* += 1; if(i.* >= tokens.len) { return error.ReachedEOFInBracketedArgs; } if(tokens[i.*].tokenType == TokenType.RoundClosed) { i.* += 1; return ArgsNode { .Bracketed = null }; } const exps = try parseExplist(tokens, i, allocator); if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.RoundClosed) { return error.ExpectedRoundClosedClosingBracketedArgs; } i.* += 1; return ArgsNode { .Bracketed = exps }; }, TokenType.CurlyOpen => return ArgsNode { .Tableconstructor = try parseTableconstructor(tokens, i, allocator) }, TokenType.StringLiteral => { const value = tokens[i.*].tokenData.string; i.* += 1; return ArgsNode { .Literal = value }; }, else => return error.UnexpectedTokenInArgs, } } fn parseTableconstructor(tokens: []Token, i: *usize, allocator: *std.heap.ArenaAllocator) !TableconstructorNode { if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.CurlyOpen) { return error.ExpectedCurlyOpenOpeningTableconstructor; } const startRegion = tokens[i.*].region; i.* += 1; if(i.* < tokens.len and tokens[i.*].tokenType == TokenType.CurlyClosed) { const endRegion = tokens[i.*].region; i.* += 1; return TableconstructorNode { .exps = null, .startRegion = startRegion, .endRegion = endRegion }; } var ret = TableconstructorNode { .exps = try parseFieldlist(tokens, i, allocator), .startRegion = startRegion, .endRegion = startRegion }; if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.CurlyClosed) { return error.ExpectedCurlyClosedClosingTableconstructor; } ret.endRegion = tokens[i.*].region; i.* += 1; return ret; } fn parseFieldlist(tokens: []Token, i: *usize, allocator: *std.heap.ArenaAllocator) !FieldlistNode { const startRegion = tokens[@min(i.*, tokens.len) - 1].region; var ret = FieldlistNode { .exps = std.ArrayList(FieldNode).init(allocator.allocator()), .startRegion = startRegion, .endRegion = startRegion }; try ret.exps.append(try parseField(tokens, i, allocator)); while(i.* < tokens.len and isFieldsep(tokens[i.*])) { i.* += 1; try ret.exps.append(try parseField(tokens, i, allocator)); } if(i.* < tokens.len and isFieldsep(tokens[i.*])) { i.* += 1; } ret.endRegion = tokens[@min(i.*, tokens.len) - 1].region; return ret; } fn isFieldsep(token: Token) bool { return token.tokenType == TokenType.Comma or token.tokenType == TokenType.Semicolon; } fn parseField(tokens: []Token, i: *usize, allocator: *std.heap.ArenaAllocator) !FieldNode { if(i.* >= tokens.len) { return error.ReachedEOFInField; } const startRegion = tokens[i.*].region; switch(tokens[i.*].tokenType) { TokenType.SquareOpen => { i.* += 1; const index = try parseExp(tokens, i, allocator); if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.SquareClosed) { return error.ExpectedSquareClosedClosingIndexedField; } i.* += 1; if(i.* >= tokens.len or tokens[i.*].tokenType != TokenType.Equals) { return error.ExpectedEqualsInIndexedFieldExpression; } const endRegion = tokens[i.*].region; i.* += 1; return FieldNode { .IndexedAssignment = IndexedAssignmentNode { .index = index, .rhs = try parseExp(tokens, i, allocator), .startRegion = startRegion, .endRegion = endRegion } }; }, TokenType.Name => { if(i.* + 1 < tokens.len and tokens[i.* + 1].tokenType == TokenType.Equals) { const name = tokens[i.*].tokenData.string; i.* += 2; return FieldNode { .Assignment = FieldAssignmentNode { .lhs = name, .rhs = try parseExp(tokens, i, allocator), .startRegion = startRegion, .endRegion = tokens[i.* - 1].region } }; } return FieldNode { .Exp = try parseExp(tokens, i, allocator) }; }, else => return FieldNode { .Exp = try parseExp(tokens, i, allocator) }, } } fn suffixExpToVar(suffixexp: SuffixexpNode, startRegion: CodeRegion, endRegion: CodeRegion) !VarNode { var exp = suffixexp.Normal; if(exp.suffixes.items.len == 0) { return VarNode { .Name = exp.firstPart.Name }; } const last = exp.suffixes.pop(); return switch(last) { SuffixexpSuffix.Dot => |*name| VarNode { .Member = MemberVarNode { .name = name.*, .value = SuffixexpNode { .Normal = exp }, .startRegion = startRegion, .endRegion = endRegion } }, SuffixexpSuffix.Indexed => |*index| VarNode { .Indexed = IndexedVarNode { .index = index.*, .value = SuffixexpNode { .Normal = exp }, .startRegion = startRegion, .endRegion = endRegion } }, else => error.ExpectedDotOrIndexedSuffixWhenConvertingSuffixExpToVar, }; }