luaaaaah/src/parser.zig

2573 lines
65 KiB
Zig

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,
};
}