using System; using System.Collections.Generic; namespace luaaaaah; class Parser { public class ChunkNode(BlockNode block, CodeRegion startRegion, CodeRegion endRegion) { public BlockNode block = block; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public class BlockNode(List stats, CodeRegion startRegion, CodeRegion endRegion) { public List stats = stats; public RetstatNode? retstat; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public abstract class StatNode(CodeRegion startRegion, CodeRegion endRegion) { public CodeRegion startRegion = startRegion, endRegion = endRegion; public class Semicolon(CodeRegion region) : StatNode(region, region) { } public class Assignment(AssignmentNode node, CodeRegion startRegion, CodeRegion endRegion) : StatNode(startRegion, endRegion) { public AssignmentNode node = node; } public class Functioncall(FunctioncallNode node, CodeRegion startRegion, CodeRegion endRegion) : StatNode(startRegion, endRegion) { public FunctioncallNode node = node; } public class Label(string label, CodeRegion startRegion, CodeRegion endRegion) : StatNode(startRegion, endRegion) { public string label = label; } public class Break(CodeRegion region) : StatNode(region, region) { } public class Goto(string label, CodeRegion startRegion, CodeRegion endRegion) : StatNode(startRegion, endRegion) { public string label = label; } public class Do(BlockNode node, CodeRegion startRegion, CodeRegion endRegion) : StatNode(startRegion, endRegion) { public BlockNode node = node; } public class While(WhileNode node, CodeRegion startRegion, CodeRegion endRegion) : StatNode(startRegion, endRegion) { public WhileNode node = node; } public class Repeat(RepeatNode node, CodeRegion startRegion, CodeRegion endRegion) : StatNode(startRegion, endRegion) { public RepeatNode node = node; } public class If(IfNode node, CodeRegion startRegion, CodeRegion endRegion) : StatNode(startRegion, endRegion) { public IfNode node = node; } public class ForNumerical(ForNumericalNode node, CodeRegion startRegion, CodeRegion endRegion) : StatNode(startRegion, endRegion) { public ForNumericalNode node = node; } public class ForGeneric(ForGenericNode node, CodeRegion startRegion, CodeRegion endRegion) : StatNode(startRegion, endRegion) { public ForGenericNode node = node; } public class Function(FunctionNode node, CodeRegion startRegion, CodeRegion endRegion) : StatNode(startRegion, endRegion) { public FunctionNode node = node; } public class LocalFunction(LocalFunctionNode node, CodeRegion startRegion, CodeRegion endRegion) : StatNode(startRegion, endRegion) { public LocalFunctionNode node = node; } public class Local(LocalNode node, CodeRegion startRegion, CodeRegion endRegion) : StatNode(startRegion, endRegion) { public LocalNode node = node; } } public class RetstatNode(CodeRegion startRegion, CodeRegion endRegion) { public ExplistNode? values; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public class AssignmentNode(VarlistNode lhs, ExplistNode rhs, CodeRegion startRegion, CodeRegion endRegion) { public VarlistNode lhs = lhs; public ExplistNode rhs = rhs; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public class FunctioncallNode(SuffixexpNode function, string? objectArg, ArgsNode args, CodeRegion startRegion, CodeRegion endRegion) { public SuffixexpNode function = function; public string? objectArg = objectArg; public ArgsNode args = args; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public class WhileNode(ExpNode condition, BlockNode body, CodeRegion startRegion, CodeRegion endRegion) { public ExpNode condition = condition; public BlockNode body = body; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public class RepeatNode(ExpNode condition, BlockNode body, CodeRegion startRegion, CodeRegion endRegion) { public ExpNode condition = condition; public BlockNode body = body; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public class IfNode(ExpNode condition, BlockNode body, List elseifs, CodeRegion startRegion, CodeRegion endRegion) { public ExpNode condition = condition; public BlockNode body = body; public List elseifs = elseifs; public BlockNode? else_; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public class ForNumericalNode(string variable, ExpNode start, ExpNode end, ExpNode? change, BlockNode body, CodeRegion startRegion, CodeRegion endRegion) { public string variable = variable; public ExpNode start = start; public ExpNode end = end; public ExpNode? change = change; public BlockNode body = body; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public class ForGenericNode(List vars, ExplistNode exps, BlockNode body, CodeRegion startRegion, CodeRegion endRegion) { public List vars = vars; public ExplistNode exps = exps; public BlockNode body = body; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public class FunctionNode(FuncnameNode name, FuncbodyNode body, CodeRegion startRegion, CodeRegion endRegion) { public FuncnameNode name = name; public FuncbodyNode body = body; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public class LocalFunctionNode(string name, FuncbodyNode body, CodeRegion startRegion, CodeRegion endRegion) { public string name = name; public FuncbodyNode body = body; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public class LocalNode(AttnamelistNode attnames, ExplistNode? values, CodeRegion startRegion, CodeRegion endRegion) { public AttnamelistNode attnames = attnames; public ExplistNode? values = values; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public class ExplistNode(List exps, CodeRegion startRegion, CodeRegion endRegion) { public List exps = exps; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public class VarlistNode(List vars, CodeRegion startRegion, CodeRegion endRegion) { public List vars = vars; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public abstract class SuffixexpNode(CodeRegion startRegion, CodeRegion endRegion) { public CodeRegion startRegion = startRegion, endRegion = endRegion; public class Normal(NormalSuffixNode node, CodeRegion startRegion, CodeRegion endRegion) : SuffixexpNode(startRegion, endRegion) { public NormalSuffixNode node = node; } public class Functioncall(FunctioncallNode node, CodeRegion startRegion, CodeRegion endRegion) : SuffixexpNode(startRegion, endRegion) { public FunctioncallNode node = node; } } public abstract class ArgsNode(CodeRegion startRegion, CodeRegion endRegion) { public CodeRegion startRegion = startRegion, endRegion = endRegion; public class Bracketed(ExplistNode? node, CodeRegion startRegion, CodeRegion endRegion) : ArgsNode(startRegion, endRegion) { public ExplistNode? node = node; } public class Tableconstructor(TableconstructorNode node, CodeRegion startRegion, CodeRegion endRegion) : ArgsNode(startRegion, endRegion) { public TableconstructorNode node = node; } public class Literal(string name, CodeRegion startRegion, CodeRegion endRegion) : ArgsNode(startRegion, endRegion) { public string name = name; } } public abstract class ExpNode(CodeRegion startRegion, CodeRegion endRegion) { public CodeRegion startRegion = startRegion, endRegion = endRegion; public class Nil(CodeRegion region) : ExpNode(region, region) { } public class False(CodeRegion region) : ExpNode(region, region) { } public class True(CodeRegion region) : ExpNode(region, region) { } public class Numeral(INumeral value, CodeRegion region) : ExpNode(region, region) { public INumeral value = value; } public class LiteralString(string value, CodeRegion region) : ExpNode(region, region) { public string value = value; } public class Varargs(CodeRegion region) : ExpNode(region, region) { } public class Functiondef(FuncbodyNode node, CodeRegion startRegion, CodeRegion endRegion) : ExpNode(startRegion, endRegion) { public FuncbodyNode node = node; } public class Suffixexp(SuffixexpNode node, CodeRegion startRegion, CodeRegion endRegion) : ExpNode(startRegion, endRegion) { public SuffixexpNode node = node; } public class Tableconstructor(TableconstructorNode node, CodeRegion startRegion, CodeRegion endRegion) : ExpNode(startRegion, endRegion) { public TableconstructorNode node = node; } public class Unop(UnopNode node, CodeRegion startRegion, CodeRegion endRegion) : ExpNode(startRegion, endRegion) { public UnopNode node = node; } public class Binop(BinopNode node, CodeRegion startRegion, CodeRegion endRegion) : ExpNode(startRegion, endRegion) { public BinopNode node = node; } } public class ElseifNode(ExpNode condition, BlockNode body, CodeRegion startRegion, CodeRegion endRegion) { public ExpNode condition = condition; public BlockNode body = body; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public class FuncnameNode(string name, List dottedNames, string? firstArg, CodeRegion startRegion, CodeRegion endRegion) { public string name = name; public List dottedNames = dottedNames; public string? firstArg = firstArg; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public class FuncbodyNode(ParlistNode? pars, BlockNode body, CodeRegion startRegion, CodeRegion endRegion) { public ParlistNode? pars = pars; public BlockNode body = body; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public class AttnamelistNode(List attnames, CodeRegion startRegion, CodeRegion endRegion) { public List attnames = attnames; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public abstract class VarNode(CodeRegion startRegion, CodeRegion endRegion) { public CodeRegion startRegion = startRegion, endRegion = endRegion; public class Name(string name, CodeRegion startRegion, CodeRegion endRegion) : VarNode(startRegion, endRegion) { public string name = name; } public class Indexed(IndexedVarNode node, CodeRegion startRegion, CodeRegion endRegion) : VarNode(startRegion, endRegion) { public IndexedVarNode node = node; } public class Member(MemberVarNode node, CodeRegion startRegion, CodeRegion endRegion) : VarNode(startRegion, endRegion) { public MemberVarNode node = node; } } public class NormalSuffixNode(SuffixexpFirstPart firstPart, List suffixes, CodeRegion startRegion, CodeRegion endRegion) { public SuffixexpFirstPart firstPart = firstPart; public List suffixes = suffixes; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public class TableconstructorNode(FieldlistNode? exps, CodeRegion startRegion, CodeRegion endRegion) { public FieldlistNode? exps = exps; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public class UnopNode(UnopType type, ExpNode exp, CodeRegion startRegion, CodeRegion endRegion) { public UnopType type = type; public ExpNode exp = exp; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public enum UnopType { Minus, LogicalNot, Length, BinaryNot, } public class BinopNode(ExpNode lhs, BinopType type, ExpNode rhs, CodeRegion startRegion, CodeRegion endRegion) { public ExpNode lhs = lhs; public BinopType type = type; public ExpNode rhs = rhs; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public enum BinopType { LogicalOr, LogicalAnd, Lt, Gt, LtEquals, GtEquals, NotEquals, Equals, BinaryOr, BinaryNot, BinaryAnd, Shl, Shr, Concat, Add, Sub, Mul, Div, IntDiv, Mod, Exp, } public class ParlistNode(List names, bool hasVarargs, CodeRegion startRegion, CodeRegion endRegion) { public List names = names; public bool hasVarargs = hasVarargs; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public class AttnameNode(string name, string? attribute, CodeRegion startRegion, CodeRegion endRegion) { public string name = name; public string? attribute = attribute; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public class IndexedVarNode(SuffixexpNode value, ExpNode index, CodeRegion startRegion, CodeRegion endRegion) { public SuffixexpNode value = value; public ExpNode index = index; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public class MemberVarNode(SuffixexpNode value, string name, CodeRegion startRegion, CodeRegion endRegion) { public SuffixexpNode value = value; public string name = name; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public abstract class SuffixexpFirstPart(CodeRegion startRegion, CodeRegion endRegion) { public CodeRegion startRegion = startRegion, endRegion = endRegion; public class Name(string name, CodeRegion startRegion, CodeRegion endRegion) : SuffixexpFirstPart(startRegion, endRegion) { public string name = name; } public class BracketedExp(ExpNode node, CodeRegion startRegion, CodeRegion endRegion) : SuffixexpFirstPart(startRegion, endRegion) { public ExpNode node = node; } } public abstract class SuffixexpSuffix(CodeRegion startRegion, CodeRegion endRegion) { public CodeRegion startRegion = startRegion, endRegion = endRegion; public class Dot(string name, CodeRegion startRegion, CodeRegion endRegion) : SuffixexpSuffix(startRegion, endRegion) { public string name = name; } public class Indexed(ExpNode node, CodeRegion startRegion, CodeRegion endRegion) : SuffixexpSuffix(startRegion, endRegion) { public ExpNode node = node; } public class Args(ArgsNode node, CodeRegion startRegion, CodeRegion endRegion) : SuffixexpSuffix(startRegion, endRegion) { public ArgsNode node = node; } public class ArgsFirstArg(ArgsFirstArgNode node, CodeRegion startRegion, CodeRegion endRegion) : SuffixexpSuffix(startRegion, endRegion) { public ArgsFirstArgNode node = node; } } public class FieldlistNode(List exps, CodeRegion startRegion, CodeRegion endRegion) { public List exps = exps; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public class ArgsFirstArgNode(string name, ArgsNode rest, CodeRegion startRegion, CodeRegion endRegion) { public string name = name; public ArgsNode rest = rest; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public abstract class FieldNode(CodeRegion startRegion, CodeRegion endRegion) { public CodeRegion startRegion = startRegion, endRegion = endRegion; public class IndexedAssignment(IndexedAssignmentNode node, CodeRegion startRegion, CodeRegion endRegion) : FieldNode(startRegion, endRegion) { public IndexedAssignmentNode node = node; } public class Assignment(FieldAssignmentNode node, CodeRegion startRegion, CodeRegion endRegion) : FieldNode(startRegion, endRegion) { public FieldAssignmentNode node = node; } public class Exp(ExpNode node, CodeRegion startRegion, CodeRegion endRegion) : FieldNode(startRegion, endRegion) { public ExpNode node = node; } } public class IndexedAssignmentNode(ExpNode index, ExpNode rhs, CodeRegion startRegion, CodeRegion endRegion) { public ExpNode index = index; public ExpNode rhs = rhs; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public class FieldAssignmentNode(string lhs, ExpNode rhs, CodeRegion startRegion, CodeRegion endRegion) { public string lhs = lhs; public ExpNode rhs = rhs; public CodeRegion startRegion = startRegion, endRegion = endRegion; } public int index; public ChunkNode Parse(Token[] tokens) { return ParseChunk(tokens); } public ChunkNode ParseChunk(Token[] tokens) { BlockNode body = ParseBlock(tokens); return new ChunkNode(block: body, startRegion: body.startRegion, endRegion: body.endRegion); } public BlockNode ParseBlock(Token[] tokens) { CodeRegion startRegion = tokens[index].region; List stats = []; while(index < tokens.Length && tokens[index].type != TokenType.Return && tokens[index].type != TokenType.End && tokens[index].type != TokenType.Elseif && tokens[index].type != TokenType.Else) { stats.Add(ParseStat(tokens)); } BlockNode ret = new(stats: stats, startRegion: startRegion, endRegion: stats[^1].endRegion); if(index < tokens.Length && tokens[index].type == TokenType.Return) { ret.retstat = ParseRetstat(tokens); } return ret; } public StatNode ParseStat(Token[] tokens) { if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}"); } CodeRegion startRegion = tokens[index].region; switch(tokens[index].type) { case TokenType.Semicolon: { index += 1; return new StatNode.Semicolon(startRegion); } case TokenType.Break: { index += 1; return new StatNode.Break(startRegion); } case TokenType.Goto: { index += 1; if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected name for goto at {startRegion}"); } if(tokens[index].type != TokenType.Name) { throw new Exception($"{tokens[index].region}: Expected name for goto, got {tokens[index].type}"); } StatNode.Goto ret = new(label: ((Token.StringData)tokens[index].data!).data, startRegion: startRegion, endRegion: tokens[index].region); index += 1; return ret; } case TokenType.Do: { index += 1; BlockNode body = ParseBlock(tokens); if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected end for `do` at {startRegion}"); } if(tokens[index].type != TokenType.End) { throw new Exception($"{tokens[index].region}: Expected `end` to close `do` at {startRegion}, got {tokens[index].type}"); } StatNode.Do ret = new(node: body, startRegion: startRegion, endRegion: tokens[index].region); index += 1; return ret; } case TokenType.While: { index += 1; ExpNode condition = ParseExp(tokens); if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `do` after condition of while loop starting at {startRegion}"); } if(tokens[index].type != TokenType.Do) { throw new Exception($"{tokens[index].region}: Expected `do` after condition of while starting at {startRegion}, got {tokens[index].type}"); } index += 1; BlockNode body = ParseBlock(tokens); if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `end` after body of while loop starting at {startRegion}"); } CodeRegion endRegion = tokens[index].region; index += 1; return new StatNode.While(new(condition: condition, body: body, startRegion: startRegion, endRegion: endRegion), startRegion: startRegion, endRegion: endRegion); } case TokenType.Repeat: { index += 1; BlockNode body = ParseBlock(tokens); if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `until` after body of until loop starting at {startRegion}"); } if(tokens[index].type != TokenType.Until) { throw new Exception($"{tokens[index].region}: Expected `until` after block of until loop starting at {startRegion}, got {tokens[index].type}"); } index += 1; ExpNode conditon = ParseExp(tokens); return new StatNode.Repeat(new(condition: conditon, body: body, startRegion: startRegion, endRegion: conditon.endRegion), startRegion: startRegion, endRegion: conditon.endRegion); } case TokenType.If: { index += 1; ExpNode condition = ParseExp(tokens); if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `then` after condition of if starting at {startRegion}"); } if(tokens[index].type != TokenType.Then) { throw new Exception($"{tokens[index].region}: Expected `then` after condition of if starting at {startRegion}, got {tokens[index].type}"); } index += 1; BlockNode body = ParseBlock(tokens); if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `end` after body of if starting at {startRegion}"); } List elseifs = []; while(tokens[index].type == TokenType.Elseif) { CodeRegion elseifStartRegion = tokens[index].region; index += 1; ExpNode elseifCondition = ParseExp(tokens); if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `then` after condition of elseif starting at {elseifStartRegion}"); } if(tokens[index].type != TokenType.Then) { throw new Exception($"{tokens[index].region}: Expected `then` after condition of elseif starting at {elseifStartRegion}, got {tokens[index].type}"); } index += 1; BlockNode elseifBody = ParseBlock(tokens); elseifs.Add(new(condition: elseifCondition, body: elseifBody, startRegion: elseifStartRegion, endRegion: elseifBody.endRegion)); } if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `end` after else-ifs of if starting at {startRegion}"); } IfNode ret = new(condition: condition, body: body, elseifs: elseifs, startRegion: startRegion, endRegion: tokens[index - 1].region); if(tokens[index].type == TokenType.Then) { index += 1; ret.else_ = ParseBlock(tokens); } if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `end` to close if starting at {startRegion}"); } if(tokens[index].type != TokenType.End) { throw new Exception($"{tokens[index].region}: Expected `end` to close if starting at {startRegion}, got {tokens[index].type}"); } ret.endRegion = tokens[index].region; index += 1; return new StatNode.If(node: ret, startRegion: startRegion, endRegion: ret.endRegion); } case TokenType.For: { index += 1; if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected name after for at {startRegion}"); } if(tokens[index].type != TokenType.Name) { throw new Exception($"{tokens[index].region}: Expected name after for at {startRegion}, got {tokens[index].type}"); } string variable = ((Token.StringData)tokens[index].data!).data; index += 1; switch(tokens[index].type) { case TokenType.Equals: { index += 1; ExpNode start = ParseExp(tokens); if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `,` after start value of numerical for loop starting at {startRegion}"); } if(tokens[index].type != TokenType.Comma) { throw new Exception($"{tokens[index].region}: Expected `,` after start value of for loop starting at {startRegion}, got {tokens[index].type}"); } index += 1; ExpNode end = ParseExp(tokens); if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `do` or `,` after end value of numerical for loop starting at {startRegion}"); } ExpNode? change = null; if(tokens[index].type == TokenType.Comma) { index += 1; change = ParseExp(tokens); } if(index >= tokens.Length) { string t = (change == null) ? "`do` or `,` after end value" : "`do` after change value"; throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected {t} of numerical for loop starting at {startRegion}"); } if(tokens[index].type != TokenType.Do) { string t = (change == null) ? "`do` or `,` after end value" : "`do` after change value"; throw new Exception($"{tokens[index].region}: Expected {t} of numerical for loop starting at {startRegion}, got {tokens[index].type}"); } index += 1; BlockNode body = ParseBlock(tokens); if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `end` to close numerical for loop starting at {startRegion}"); } if(tokens[index].type != TokenType.End) { throw new Exception($"{tokens[index].region}: Expected `end` to close numerical for loop starting at {startRegion}, got {tokens[index].type}"); } CodeRegion endRegion = tokens[index].region; index += 1; return new StatNode.ForNumerical(new(variable: variable, start: start, end: end, change: change, body: body, startRegion: startRegion, endRegion: endRegion), startRegion: startRegion, endRegion: endRegion); } case TokenType.Comma: { List names = [variable]; while(tokens[index].type == TokenType.Comma) { index += 1; if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected another name in name list of for-in loop starting at {startRegion}"); } if(tokens[index].type != TokenType.Name) { throw new Exception($"{tokens[index].region}: Expected another name in name list of for-in loop starting at {startRegion}, got {tokens[index].type}"); } names.Add(((Token.StringData)tokens[index].data!).data); index += 1; if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `,` or `in` in for-in loop starting at {startRegion}"); } } if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `in` after name list of for-in loop starting at {startRegion}"); } if(tokens[index].type != TokenType.In) { throw new Exception($"{tokens[index].region}: Expected `in` after name list of for-in loop starting at {startRegion}, got {tokens[index].type}"); } index += 1; ExplistNode exps = ParseExplist(tokens); if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `do` after exp list of for-in loop starting at {startRegion}"); } if(tokens[index].type != TokenType.Do) { throw new Exception($"{tokens[index].region}: Expected `do` after exp list of for-in loop starting at {startRegion}, got {tokens[index].type}"); } index += 1; BlockNode body = ParseBlock(tokens); if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `end` to close for-in loop starting at {startRegion}"); } if(tokens[index].type != TokenType.End) { throw new Exception($"{tokens[index].region}: Expected `end` to close for-in loop starting at {startRegion}, got {tokens[index].type}"); } CodeRegion endRegion = tokens[index].region; index += 1; return new StatNode.ForGeneric(new(vars: names, exps: exps, body: body, startRegion: startRegion, endRegion: endRegion), startRegion: startRegion, endRegion: endRegion); } case TokenType.In: { index += 1; ExplistNode exps = ParseExplist(tokens); if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `do` after exp list of for-in loop starting at {startRegion}"); } if(tokens[index].type != TokenType.Do) { throw new Exception($"{tokens[index].region}: Expected `do` after exp list of for-in loop starting at {startRegion}, got {tokens[index].type}"); } index += 1; BlockNode body = ParseBlock(tokens); if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `end` to close for-in loop starting at {startRegion}"); } if(tokens[index].type != TokenType.End) { throw new Exception($"{tokens[index].region}: Expected `end` to close for-in loop starting at {startRegion}, got {tokens[index].type}"); } CodeRegion endRegion = tokens[index].region; index += 1; return new StatNode.ForGeneric(new(vars: [variable], exps: exps, body: body, startRegion: startRegion, endRegion: endRegion), startRegion: startRegion, endRegion: endRegion); } default: { throw new Exception($"{tokens[index].type}: Expected either `=`, `,` or `in` after first name of for loop {startRegion}, got {tokens[index].type}"); } } } case TokenType.Function: { index += 1; FuncnameNode name = ParseFuncname(tokens); FuncbodyNode body = ParseFuncbody(tokens); return new StatNode.Function(new(name: name, body: body, startRegion: startRegion, endRegion: body.endRegion), startRegion: startRegion, endRegion: body.endRegion); } case TokenType.Local: { index += 1; if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length} after `local` at {startRegion}"); } if(tokens[index].type == TokenType.Function) { index += 1; if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected name of local function starting at {startRegion}"); } if(tokens[index].type != TokenType.Name) { throw new Exception($"{tokens[index].region}: Expected name of local function starting at {startRegion}, got {tokens[index].type}"); } string name = ((Token.StringData)tokens[index].data!).data; index += 1; FuncbodyNode body = ParseFuncbody(tokens); return new StatNode.LocalFunction(new(name: name, body: body, startRegion: startRegion, endRegion: body.endRegion), startRegion: startRegion, endRegion: body.endRegion); } else { AttnamelistNode attnames = ParseAttnamelist(tokens); LocalNode ret = new(attnames: attnames, values: null, startRegion: startRegion, endRegion: attnames.endRegion); if(index < tokens.Length && tokens[index].type == TokenType.Equals) { index += 1; ret.values = ParseExplist(tokens); ret.endRegion = ret.values.endRegion; } return new StatNode.Local(ret, startRegion: startRegion, endRegion: ret.endRegion); } } case TokenType.ColonColon: { index += 1; if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected name of label starting at {startRegion}"); } if(tokens[index].type != TokenType.Name) { throw new Exception($"{tokens[index].region}: Expected name of label starting at {startRegion}, got {tokens[index].type}"); } string name = ((Token.StringData)tokens[index].data!).data; index += 1; if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `::` after label name starting at {startRegion}"); } if(tokens[index].type != TokenType.Name) { throw new Exception($"{tokens[index].region}: Expected `::` after label name starting at {startRegion}, got {tokens[index].type}"); } CodeRegion endRegion = tokens[index].region; index += 1; return new StatNode.Label(label: name, startRegion: startRegion, endRegion: endRegion); } case TokenType.Name: case TokenType.RoundOpen: { SuffixexpNode suffixExp = ParseSuffixExp(tokens); if(index >= tokens.Length) { if(suffixExp is SuffixexpNode.Normal) { throw new Exception($"{startRegion}: Expected function call, got normal suffix expression"); } if(suffixExp is SuffixexpNode.Functioncall functioncall) { return new StatNode.Functioncall(node: functioncall.node, startRegion: functioncall.startRegion, endRegion: functioncall.endRegion); } } else { switch(tokens[index].type) { case TokenType.Equals: { index += 1; List lhs = [SuffixExpToVar(suffixExp)]; ExplistNode rhs = ParseExplist(tokens); return new StatNode.Assignment(new(lhs: new(vars: lhs, startRegion: startRegion, endRegion: suffixExp.endRegion), rhs: rhs, startRegion: startRegion, endRegion: rhs.endRegion), startRegion: startRegion, endRegion: rhs.endRegion); } case TokenType.Comma: { List vars = [SuffixExpToVar(suffixExp)]; while(index < tokens.Length && tokens[index].type == TokenType.Comma) { index += 1; vars.Add(ParseVar(tokens)); } if(index >= tokens.Length) { throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `=` for assignment starting at {startRegion}"); } if(tokens[index].type != TokenType.Equals) { throw new Exception($"{tokens[index].region}: Expected `=` for assignment starting at {startRegion}, got {tokens[index].type}"); } index += 1; VarlistNode varlistNode = new(vars: vars, startRegion: startRegion, endRegion: vars[^1].endRegion); ExplistNode rhs = ParseExplist(tokens); return new StatNode.Assignment(new(lhs: varlistNode, rhs: rhs, startRegion: startRegion, endRegion: rhs.endRegion), startRegion: startRegion, endRegion: rhs.endRegion); } } if(suffixExp is SuffixexpNode.Normal) { throw new Exception($"{startRegion}: Expected function call, got normal suffix expression"); } if(suffixExp is SuffixexpNode.Functioncall functioncall) { return new StatNode.Functioncall(node: functioncall.node, startRegion: functioncall.startRegion, endRegion: functioncall.endRegion); } } } break; default: { throw new Exception($"Unexpected token {tokens[index]} at {startRegion}"); } } throw new NotImplementedException(); } private VarNode ParseVar(Token[] tokens) { throw new NotImplementedException(); } private VarNode SuffixExpToVar(SuffixexpNode suffixExp) { throw new NotImplementedException(); } private SuffixexpNode ParseSuffixExp(Token[] tokens) { throw new NotImplementedException(); } private AttnamelistNode ParseAttnamelist(Token[] tokens) { throw new NotImplementedException(); } private FuncbodyNode ParseFuncbody(Token[] tokens) { throw new NotImplementedException(); } private FuncnameNode ParseFuncname(Token[] tokens) { throw new NotImplementedException(); } private ExplistNode ParseExplist(Token[] tokens) { throw new NotImplementedException(); } private ExpNode ParseExp(Token[] tokens) { throw new NotImplementedException(); } private RetstatNode ParseRetstat(Token[] tokens) { throw new NotImplementedException(); } }