From 02aab2e59055f199e2be451007fa368b6c778bac Mon Sep 17 00:00:00 2001 From: 0x4261756D <38735823+0x4261756D@users.noreply.github.com> Date: Thu, 1 Feb 2024 02:20:22 +0100 Subject: [PATCH] Start working on the parser --- Parser.cs | 904 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 904 insertions(+) create mode 100644 Parser.cs diff --git a/Parser.cs b/Parser.cs new file mode 100644 index 0000000..b33dbae --- /dev/null +++ b/Parser.cs @@ -0,0 +1,904 @@ +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 startRegion, CodeRegion endRegion) : ExpNode(startRegion, endRegion) + { + public INumeral value = value; + } + public class LiteralString(string value, CodeRegion startRegion, CodeRegion endRegion) : ExpNode(startRegion, endRegion) + { + 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) : SuffixexpFirstPart(startRegion, endRegion) + { + public string name = name; + } + public class Indexed(ExpNode node, CodeRegion startRegion, CodeRegion endRegion) : SuffixexpFirstPart(startRegion, endRegion) + { + public ExpNode node = node; + } + public class Args(ArgsNode node, CodeRegion startRegion, CodeRegion endRegion) : SuffixexpFirstPart(startRegion, endRegion) + { + public ArgsNode node = node; + } + public class ArgsFirstArg(ArgsFirstArgNode node, CodeRegion startRegion, CodeRegion endRegion) : SuffixexpFirstPart(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(); + } +}