1666 lines
64 KiB
C#
1666 lines
64 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text.Json.Serialization;
|
|
|
|
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<StatNode> stats, CodeRegion startRegion, CodeRegion endRegion)
|
|
{
|
|
public List<StatNode> stats = stats;
|
|
public RetstatNode? retstat;
|
|
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
|
}
|
|
[JsonDerivedType(typeof(Semicolon), typeDiscriminator: "st Semicolon")]
|
|
[JsonDerivedType(typeof(Assignment), typeDiscriminator: "st Assignment")]
|
|
[JsonDerivedType(typeof(Functioncall), typeDiscriminator: "st Functioncall")]
|
|
[JsonDerivedType(typeof(Label), typeDiscriminator: "st Label")]
|
|
[JsonDerivedType(typeof(Break), typeDiscriminator: "st Break")]
|
|
[JsonDerivedType(typeof(Goto), typeDiscriminator: "st Goto")]
|
|
[JsonDerivedType(typeof(Do), typeDiscriminator: "st Do")]
|
|
[JsonDerivedType(typeof(While), typeDiscriminator: "st While")]
|
|
[JsonDerivedType(typeof(Repeat), typeDiscriminator: "st Repeat")]
|
|
[JsonDerivedType(typeof(If), typeDiscriminator: "st If")]
|
|
[JsonDerivedType(typeof(ForNumerical), typeDiscriminator: "st ForNum")]
|
|
[JsonDerivedType(typeof(ForGeneric), typeDiscriminator: "st ForGen")]
|
|
[JsonDerivedType(typeof(Function), typeDiscriminator: "st Function")]
|
|
[JsonDerivedType(typeof(LocalFunction), typeDiscriminator: "st LocalFunction")]
|
|
[JsonDerivedType(typeof(Local), typeDiscriminator: "st Local")]
|
|
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(ExplistNode? values, CodeRegion startRegion, CodeRegion endRegion)
|
|
{
|
|
public ExplistNode? values = 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<ElseifNode> elseifs, CodeRegion startRegion, CodeRegion endRegion)
|
|
{
|
|
public ExpNode condition = condition;
|
|
public BlockNode body = body;
|
|
public List<ElseifNode> 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<string> vars, ExplistNode exps, BlockNode body, CodeRegion startRegion, CodeRegion endRegion)
|
|
{
|
|
public List<string> 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<ExpNode> exps, CodeRegion startRegion, CodeRegion endRegion)
|
|
{
|
|
public List<ExpNode> exps = exps;
|
|
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
|
}
|
|
public class VarlistNode(List<VarNode> vars, CodeRegion startRegion, CodeRegion endRegion)
|
|
{
|
|
public List<VarNode> vars = vars;
|
|
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
|
}
|
|
[JsonDerivedType(typeof(Normal), typeDiscriminator: "s Normal")]
|
|
[JsonDerivedType(typeof(Functioncall), typeDiscriminator: "s Functioncall")]
|
|
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;
|
|
}
|
|
}
|
|
[JsonDerivedType(typeof(Bracketed), typeDiscriminator: "a Bracketed")]
|
|
[JsonDerivedType(typeof(Tableconstructor), typeDiscriminator: "a Tableconstructor")]
|
|
[JsonDerivedType(typeof(Literal), typeDiscriminator: "a Literal")]
|
|
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;
|
|
}
|
|
}
|
|
[JsonDerivedType(typeof(Nil), typeDiscriminator: "e Nil")]
|
|
[JsonDerivedType(typeof(False), typeDiscriminator: "e True")]
|
|
[JsonDerivedType(typeof(True), typeDiscriminator: "e False")]
|
|
[JsonDerivedType(typeof(Numeral), typeDiscriminator: "e Numeral")]
|
|
[JsonDerivedType(typeof(LiteralString), typeDiscriminator: "e Literal")]
|
|
[JsonDerivedType(typeof(Varargs), typeDiscriminator: "e Varargs")]
|
|
[JsonDerivedType(typeof(Functiondef), typeDiscriminator: "e Functiondef")]
|
|
[JsonDerivedType(typeof(Suffixexp), typeDiscriminator: "e Suffixexp")]
|
|
[JsonDerivedType(typeof(Tableconstructor), typeDiscriminator: "e Tableconstructor")]
|
|
[JsonDerivedType(typeof(Unop), typeDiscriminator: "e Unop")]
|
|
[JsonDerivedType(typeof(Binop), typeDiscriminator: "e Binop")]
|
|
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<string> dottedNames, string? firstArg, CodeRegion startRegion, CodeRegion endRegion)
|
|
{
|
|
public string name = name;
|
|
public List<string> 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<AttnameNode> attnames, CodeRegion startRegion, CodeRegion endRegion)
|
|
{
|
|
public List<AttnameNode> attnames = attnames;
|
|
public CodeRegion startRegion = startRegion, endRegion = endRegion;
|
|
}
|
|
[JsonDerivedType(typeof(Name), typeDiscriminator: "v Name")]
|
|
[JsonDerivedType(typeof(Indexed), typeDiscriminator: "v Indexed")]
|
|
[JsonDerivedType(typeof(Member), typeDiscriminator: "v Member")]
|
|
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<SuffixexpSuffix> suffixes, CodeRegion startRegion, CodeRegion endRegion)
|
|
{
|
|
public SuffixexpFirstPart firstPart = firstPart;
|
|
public List<SuffixexpSuffix> 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<string> names, bool hasVarargs, CodeRegion startRegion, CodeRegion endRegion)
|
|
{
|
|
public List<string> 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;
|
|
}
|
|
[JsonDerivedType(typeof(Name), typeDiscriminator: "sfp Name")]
|
|
[JsonDerivedType(typeof(BracketedExp), typeDiscriminator: "sfp BracketedExp")]
|
|
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;
|
|
}
|
|
}
|
|
[JsonDerivedType(typeof(Dot))]
|
|
[JsonDerivedType(typeof(Indexed))]
|
|
[JsonDerivedType(typeof(Args))]
|
|
[JsonDerivedType(typeof(ArgsFirstArg))]
|
|
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<FieldNode> exps, CodeRegion startRegion, CodeRegion endRegion)
|
|
{
|
|
public List<FieldNode> 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;
|
|
}
|
|
[JsonDerivedType(typeof(IndexedAssignment))]
|
|
[JsonDerivedType(typeof(Assignment))]
|
|
[JsonDerivedType(typeof(Exp))]
|
|
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<StatNode> 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 &&
|
|
tokens[index].type != TokenType.Until)
|
|
{
|
|
stats.Add(ParseStat(tokens));
|
|
}
|
|
BlockNode ret = new(stats: stats, startRegion: startRegion, endRegion: (stats.Count == 0) ? startRegion : 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<ElseifNode> 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.Else)
|
|
{
|
|
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<string> 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.ColonColon)
|
|
{
|
|
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<VarNode> 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<VarNode> 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)
|
|
{
|
|
return SuffixExpToVar(ParseSuffixExp(tokens));
|
|
}
|
|
|
|
private static VarNode SuffixExpToVar(SuffixexpNode suffixExp)
|
|
{
|
|
if(suffixExp is not SuffixexpNode.Normal normal)
|
|
{
|
|
throw new Exception($"Expected a normal suffix expression to convert to var at {suffixExp.startRegion}-{suffixExp.endRegion}");
|
|
}
|
|
if(normal.node.suffixes.Count == 0)
|
|
{
|
|
if(normal.node.firstPart is not SuffixexpFirstPart.Name name)
|
|
{
|
|
throw new Exception($"Expected a name as first part of suffix expression to convert to var at {normal.node.firstPart.startRegion}-{normal.node.firstPart.endRegion}");
|
|
}
|
|
return new VarNode.Name(name: name.name, startRegion: suffixExp.startRegion, endRegion: suffixExp.endRegion);
|
|
}
|
|
SuffixexpSuffix last = normal.node.suffixes[^1];
|
|
return last switch
|
|
{
|
|
SuffixexpSuffix.Dot dot => new VarNode.Member(node: new(name: dot.name, value: normal, startRegion: suffixExp.startRegion, endRegion: suffixExp.endRegion), startRegion: suffixExp.startRegion, endRegion: dot.endRegion),
|
|
SuffixexpSuffix.Indexed indexed => new VarNode.Indexed(node: new(index: indexed.node, value: normal, startRegion: suffixExp.startRegion, endRegion: suffixExp.endRegion), startRegion: suffixExp.startRegion, endRegion: indexed.endRegion),
|
|
_ => throw new Exception($"Expected dot or indexed suffix expression to convert to var at {last.startRegion}-{last.endRegion}")
|
|
};
|
|
}
|
|
|
|
private SuffixexpNode ParseSuffixExp(Token[] tokens)
|
|
{
|
|
// primaryexp { '.' 'Name' | '[' exp']' | ':' 'Name' args | args }
|
|
if(index >= tokens.Length)
|
|
{
|
|
throw new Exception($"Index {index} out of bounds of {tokens.Length}");
|
|
}
|
|
CodeRegion startRegion = tokens[index].region;
|
|
SuffixexpFirstPart firstPart;
|
|
switch(tokens[index].type)
|
|
{
|
|
case TokenType.Name:
|
|
{
|
|
string name = ((Token.StringData)tokens[index].data!).data;
|
|
index += 1;
|
|
firstPart = new SuffixexpFirstPart.Name(name, startRegion, startRegion);
|
|
}
|
|
break;
|
|
case TokenType.RoundOpen:
|
|
{
|
|
index += 1;
|
|
ExpNode inner = ParseExp(tokens);
|
|
if(index >= tokens.Length)
|
|
{
|
|
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `)` to close bracketed expression starting at {startRegion}");
|
|
}
|
|
if(tokens[index].type != TokenType.RoundClosed)
|
|
{
|
|
throw new Exception($"{tokens[index].region}: Expected `)` to close bracketed expression at {startRegion}, got {tokens[index].type}");
|
|
}
|
|
firstPart = new SuffixexpFirstPart.BracketedExp(node: inner, startRegion: startRegion, endRegion: tokens[index].region);
|
|
index += 1;
|
|
}
|
|
break;
|
|
default:
|
|
throw new Exception($"{startRegion}: Expected either `)` or name as first part of suffix-expression, got {tokens[index].type}");
|
|
}
|
|
List<SuffixexpSuffix> suffixes = [];
|
|
bool shouldContinue = true;
|
|
while(shouldContinue && index < tokens.Length)
|
|
{
|
|
CodeRegion suffixStartRegion = tokens[index].region;
|
|
switch(tokens[index].type)
|
|
{
|
|
case TokenType.Dot:
|
|
{
|
|
index += 1;
|
|
if(index >= tokens.Length)
|
|
{
|
|
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected name in dotted suffix of suffix expression starting at {startRegion}");
|
|
}
|
|
if(tokens[index].type != TokenType.Name)
|
|
{
|
|
throw new Exception($"{tokens[index].region}: Expected name in dotted suffix of suffix expression at {startRegion}, got {tokens[index].type}");
|
|
}
|
|
CodeRegion suffixEndRegion = tokens[index].region;
|
|
string name = ((Token.StringData)tokens[index].data!).data;
|
|
index += 1;
|
|
suffixes.Add(new SuffixexpSuffix.Dot(name, startRegion: suffixStartRegion, endRegion: suffixEndRegion));
|
|
}
|
|
break;
|
|
case TokenType.SquareOpen:
|
|
{
|
|
index += 1;
|
|
ExpNode inner = ParseExp(tokens);
|
|
suffixes.Add(new SuffixexpSuffix.Indexed(node: inner, startRegion: suffixStartRegion, endRegion: inner.endRegion));
|
|
if(index >= tokens.Length)
|
|
{
|
|
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `]` to close indexed suffix of suffix-expression starting at {suffixStartRegion}");
|
|
}
|
|
if(tokens[index].type != TokenType.SquareClosed)
|
|
{
|
|
throw new Exception($"{tokens[index].region}: Expected `]` to close indexed suffix of suffix-expression at {suffixStartRegion}, got {tokens[index].type}");
|
|
}
|
|
index += 1;
|
|
}
|
|
break;
|
|
case TokenType.Colon:
|
|
{
|
|
index += 1;
|
|
if(index >= tokens.Length)
|
|
{
|
|
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected name as first arg after `:` in args suffix in suffix-expression starting at {suffixStartRegion}");
|
|
}
|
|
if(tokens[index].type != TokenType.RoundClosed)
|
|
{
|
|
throw new Exception($"{tokens[index].region}: Expected name as first arg after `:` in args suffix in suffix-expression at {suffixStartRegion}, got {tokens[index].type}");
|
|
}
|
|
string name = ((Token.StringData)tokens[index].data!).data;
|
|
index += 1;
|
|
ArgsNode args = ParseArgs(tokens);
|
|
suffixes.Add(new SuffixexpSuffix.ArgsFirstArg(new(name, rest: args, startRegion: suffixStartRegion, endRegion: args.endRegion), startRegion: suffixStartRegion, endRegion: args.endRegion));
|
|
}
|
|
break;
|
|
case TokenType.RoundOpen:
|
|
case TokenType.CurlyOpen:
|
|
case TokenType.StringLiteral:
|
|
{
|
|
ArgsNode args = ParseArgs(tokens);
|
|
suffixes.Add(new SuffixexpSuffix.Args(node: args, startRegion: suffixStartRegion, endRegion: args.endRegion));
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
shouldContinue = false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
CodeRegion endRegion;
|
|
if(suffixes.Count > 0)
|
|
{
|
|
endRegion = suffixes[^1].endRegion;
|
|
SuffixexpNode? ret = suffixes[^1] switch
|
|
{
|
|
SuffixexpSuffix.Args args => new SuffixexpNode.Functioncall(
|
|
node: new(
|
|
function: new SuffixexpNode.Normal(
|
|
node: new NormalSuffixNode(firstPart, suffixes[..^1], startRegion, args.endRegion),
|
|
startRegion: startRegion,
|
|
endRegion: args.endRegion
|
|
),
|
|
args: args.node,
|
|
objectArg: null,
|
|
startRegion: startRegion,
|
|
endRegion: args.endRegion
|
|
),
|
|
startRegion: startRegion,
|
|
endRegion: args.endRegion
|
|
),
|
|
SuffixexpSuffix.ArgsFirstArg node => new SuffixexpNode.Functioncall(
|
|
node: new(
|
|
function: new SuffixexpNode.Normal(
|
|
node: new NormalSuffixNode(firstPart: firstPart, suffixes: suffixes[..^1], startRegion, node.endRegion),
|
|
startRegion: startRegion,
|
|
endRegion: node.endRegion
|
|
),
|
|
objectArg: node.node.name,
|
|
args: node.node.rest,
|
|
startRegion: startRegion,
|
|
endRegion: node.endRegion
|
|
),
|
|
startRegion: startRegion,
|
|
endRegion: node.endRegion
|
|
),
|
|
_ => null,
|
|
};
|
|
if(ret is not null)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
endRegion = firstPart.endRegion;
|
|
}
|
|
|
|
return new SuffixexpNode.Normal(
|
|
node: new(firstPart: firstPart, suffixes: suffixes, startRegion: startRegion, endRegion: endRegion),
|
|
startRegion: startRegion,
|
|
endRegion: endRegion
|
|
);
|
|
}
|
|
|
|
private ArgsNode ParseArgs(Token[] tokens)
|
|
{
|
|
if(index >= tokens.Length)
|
|
{
|
|
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `(`, `{{` or string to start args");
|
|
}
|
|
CodeRegion startRegion = tokens[index].region;
|
|
switch(tokens[index].type)
|
|
{
|
|
case TokenType.RoundOpen:
|
|
{
|
|
index += 1;
|
|
if(index >= tokens.Length)
|
|
{
|
|
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected explist or `)` to continue args starting at {startRegion}");
|
|
}
|
|
if(tokens[index].type == TokenType.RoundClosed)
|
|
{
|
|
CodeRegion endRegion = tokens[index].region;
|
|
index += 1;
|
|
return new ArgsNode.Bracketed(null, startRegion: startRegion, endRegion: endRegion);
|
|
}
|
|
ExplistNode exps = ParseExplist(tokens);
|
|
if(index >= tokens.Length)
|
|
{
|
|
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `)` to close args starting at {startRegion}");
|
|
}
|
|
if(tokens[index].type != TokenType.RoundClosed)
|
|
{
|
|
throw new Exception($"{tokens[index].region}: Expected `)` to close args starting at {startRegion}, got {tokens[index].type}");
|
|
}
|
|
index += 1;
|
|
return new ArgsNode.Bracketed(node: exps, startRegion: startRegion, endRegion: exps.endRegion);
|
|
}
|
|
case TokenType.CurlyOpen:
|
|
{
|
|
TableconstructorNode node = ParseTableconstructor(tokens);
|
|
return new ArgsNode.Tableconstructor(node: node, startRegion: startRegion, endRegion: node.endRegion);
|
|
}
|
|
case TokenType.StringLiteral:
|
|
{
|
|
string value = ((Token.StringData)tokens[index].data!).data;
|
|
index += 1;
|
|
return new ArgsNode.Literal(name: value, startRegion: startRegion, endRegion: startRegion);
|
|
}
|
|
default:
|
|
throw new Exception($"{tokens[index].region}: Expected explist or `)` to continue args starting at {startRegion}");
|
|
}
|
|
}
|
|
|
|
private TableconstructorNode ParseTableconstructor(Token[] tokens)
|
|
{
|
|
if(index >= tokens.Length)
|
|
{
|
|
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `{{` to start tableconstructor");
|
|
}
|
|
CodeRegion startRegion = tokens[index].region;
|
|
if(tokens[index].type != TokenType.CurlyOpen)
|
|
{
|
|
throw new Exception($"{startRegion}: Expected `{{` to start tableconstructor, got {tokens[index].type}");
|
|
}
|
|
index += 1;
|
|
if(index < tokens.Length && tokens[index].type == TokenType.CurlyClosed)
|
|
{
|
|
CodeRegion emptyEndRegion = tokens[index].region;
|
|
index += 1;
|
|
return new TableconstructorNode(exps: null, startRegion: startRegion, endRegion: emptyEndRegion);
|
|
}
|
|
FieldlistNode fields = ParseFieldlist(tokens);
|
|
if(index >= tokens.Length)
|
|
{
|
|
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `}}` to close tableconstructor starting at {startRegion}");
|
|
}
|
|
if(tokens[index].type != TokenType.CurlyClosed)
|
|
{
|
|
throw new Exception($"{tokens[index].region}: Expected `}}` to close tableconstructor starting at {startRegion}, got {tokens[index].type}");
|
|
}
|
|
CodeRegion endRegion = tokens[index].region;
|
|
index += 1;
|
|
return new TableconstructorNode(exps: fields, startRegion: startRegion, endRegion: endRegion);
|
|
}
|
|
|
|
private FieldlistNode ParseFieldlist(Token[] tokens)
|
|
{
|
|
List<FieldNode> fields = [ParseField(tokens)];
|
|
while(index < tokens.Length && IsFieldsep(tokens[index]))
|
|
{
|
|
index += 1;
|
|
fields.Add(ParseField(tokens));
|
|
}
|
|
if(index < tokens.Length && IsFieldsep(tokens[index]))
|
|
{
|
|
index += 1;
|
|
}
|
|
// NOTE: Since at least 1 field is parsed the list accesses are safe
|
|
return new FieldlistNode(exps: fields, startRegion: fields[0].startRegion, endRegion: fields[^1].endRegion);
|
|
}
|
|
|
|
private static bool IsFieldsep(Token token) => token.type is TokenType.Comma or TokenType.Semicolon;
|
|
|
|
private FieldNode ParseField(Token[] tokens)
|
|
{
|
|
if(index >= tokens.Length)
|
|
{
|
|
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `[` or name to start field");
|
|
}
|
|
CodeRegion startRegion = tokens[index].region;
|
|
switch(tokens[index].type)
|
|
{
|
|
case TokenType.SquareOpen:
|
|
{
|
|
index += 1;
|
|
ExpNode indexNode = ParseExp(tokens);
|
|
if(index >= tokens.Length)
|
|
{
|
|
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `]` to close indexed field in indexed field assignment starting at {startRegion}");
|
|
}
|
|
if(tokens[index].type != TokenType.SquareClosed)
|
|
{
|
|
throw new Exception($"{tokens[index].region}: Expected `]` to close indexed field starting in indexed field assignment at {startRegion}, got {tokens[index].type}");
|
|
}
|
|
index += 1;
|
|
if(index >= tokens.Length)
|
|
{
|
|
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `=` to continue indexed field assignment starting at {startRegion}");
|
|
}
|
|
if(tokens[index].type != TokenType.Equals)
|
|
{
|
|
throw new Exception($"{tokens[index].region}: Expected `=` to continue indexed field assignment starting at {startRegion}, got {tokens[index].type}");
|
|
}
|
|
index += 1;
|
|
ExpNode rhs = ParseExp(tokens);
|
|
return new FieldNode.IndexedAssignment(node: new(index: indexNode, rhs: rhs, startRegion: startRegion, endRegion: rhs.endRegion), startRegion: startRegion, endRegion: rhs.endRegion);
|
|
}
|
|
case TokenType.Name:
|
|
{
|
|
if(index + 1 < tokens.Length && tokens[index + 1].type == TokenType.Equals)
|
|
{
|
|
string name = ((Token.StringData)tokens[index].data!).data;
|
|
index += 2;
|
|
ExpNode rhs = ParseExp(tokens);
|
|
return new FieldNode.Assignment(node: new(lhs: name, rhs: rhs, startRegion: startRegion, endRegion: rhs.endRegion), startRegion: startRegion, endRegion: rhs.endRegion);
|
|
}
|
|
ExpNode exp = ParseExp(tokens);
|
|
return new FieldNode.Exp(node: exp, startRegion: startRegion, endRegion: exp.endRegion);
|
|
}
|
|
default:
|
|
{
|
|
ExpNode exp = ParseExp(tokens);
|
|
return new FieldNode.Exp(node: exp, startRegion: startRegion, endRegion: exp.endRegion);
|
|
}
|
|
}
|
|
}
|
|
|
|
private AttnamelistNode ParseAttnamelist(Token[] tokens)
|
|
{
|
|
List<AttnameNode> attnames = [ParseAttname(tokens)];
|
|
while(index < tokens.Length && tokens[index].type == TokenType.Comma)
|
|
{
|
|
index += 1;
|
|
attnames.Add(ParseAttname(tokens));
|
|
}
|
|
// NOTE: Since at least 1 attname is parsed the list accesses are safe
|
|
return new(attnames: attnames, startRegion: attnames[0].startRegion, endRegion: attnames[^1].endRegion);
|
|
}
|
|
|
|
private AttnameNode ParseAttname(Token[] tokens)
|
|
{
|
|
if(index >= tokens.Length)
|
|
{
|
|
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected name to start attname");
|
|
}
|
|
CodeRegion startRegion = tokens[index].region;
|
|
if(tokens[index].type != TokenType.Name)
|
|
{
|
|
throw new Exception($"{tokens[index].region}: Expected name to start attname at {startRegion}, got {tokens[index].type}");
|
|
}
|
|
string name = ((Token.StringData)tokens[index].data!).data;
|
|
index += 1;
|
|
if(index < tokens.Length && tokens[index].type == TokenType.Lt)
|
|
{
|
|
index += 1;
|
|
if(index >= tokens.Length)
|
|
{
|
|
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected attribute name of attname starting at {startRegion}");
|
|
}
|
|
if(tokens[index].type != TokenType.Name)
|
|
{
|
|
throw new Exception($"{tokens[index].region}: Expected attribute name of attname at {startRegion}, got {tokens[index].type}");
|
|
}
|
|
string attribute = ((Token.StringData)tokens[index].data!).data;
|
|
index += 1;
|
|
if(index >= tokens.Length)
|
|
{
|
|
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `>` to close attribute of attname starting at {startRegion}");
|
|
}
|
|
if(tokens[index].type != TokenType.Gt)
|
|
{
|
|
throw new Exception($"{tokens[index].region}: Expected `>` to close attribute of attname starting at {startRegion}, got {tokens[index].type}");
|
|
}
|
|
CodeRegion endRegion = tokens[index].region;
|
|
index += 1;
|
|
return new AttnameNode(name: name, attribute: attribute, startRegion: startRegion, endRegion: endRegion);
|
|
}
|
|
return new AttnameNode(name: name, attribute: null, startRegion: startRegion, endRegion: startRegion);
|
|
}
|
|
|
|
private FuncbodyNode ParseFuncbody(Token[] tokens)
|
|
{
|
|
if(index >= tokens.Length)
|
|
{
|
|
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `(` to start funcbody");
|
|
}
|
|
CodeRegion startRegion = tokens[index].region;
|
|
if(tokens[index].type != TokenType.RoundOpen)
|
|
{
|
|
throw new Exception($"{tokens[index].region}: Expected `(` to start funcbody at {startRegion}, got {tokens[index].type}");
|
|
}
|
|
index += 1;
|
|
ParlistNode? pars;
|
|
if(index < tokens.Length && tokens[index].type == TokenType.RoundClosed)
|
|
{
|
|
index += 1;
|
|
pars = null;
|
|
}
|
|
else
|
|
{
|
|
pars = ParseParlist(tokens);
|
|
if(index >= tokens.Length)
|
|
{
|
|
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `)` to close parlist of funcbody starting at {startRegion}");
|
|
}
|
|
if(tokens[index].type != TokenType.RoundClosed)
|
|
{
|
|
throw new Exception($"{tokens[index].region}: Expected `)` to close parlist of funcbody 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 funcbody starting at {startRegion}");
|
|
}
|
|
if(tokens[index].type != TokenType.End)
|
|
{
|
|
throw new Exception($"{tokens[index].region}: Expected `end` to close funcbody starting at {startRegion}, got {tokens[index].type}");
|
|
}
|
|
CodeRegion endRegion = tokens[index].region;
|
|
index += 1;
|
|
return new FuncbodyNode(pars: pars, body: body, startRegion: startRegion, endRegion: endRegion);
|
|
}
|
|
|
|
private ParlistNode ParseParlist(Token[] tokens)
|
|
{
|
|
if(index >= tokens.Length)
|
|
{
|
|
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `...` or name to start parlist");
|
|
}
|
|
CodeRegion startRegion = tokens[index].region;
|
|
if(tokens[index].type == TokenType.DotDotDot)
|
|
{
|
|
return new ParlistNode(names: [], hasVarargs: true, startRegion: startRegion, endRegion: startRegion);
|
|
}
|
|
if(tokens[index].type != TokenType.Name)
|
|
{
|
|
throw new Exception($"{startRegion}: Expected `...` or name to start parlist, got {tokens[index].type}");
|
|
}
|
|
List<string> names = [((Token.StringData)tokens[index].data!).data];
|
|
index += 1;
|
|
while(index < tokens.Length && tokens[index].type == TokenType.Comma)
|
|
{
|
|
index += 1;
|
|
if(index >= tokens.Length)
|
|
{
|
|
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `...` or name to continue parlist starting at {startRegion}");
|
|
}
|
|
switch(tokens[index].type)
|
|
{
|
|
case TokenType.Name:
|
|
{
|
|
names.Add(((Token.StringData)tokens[index].data!).data);
|
|
index += 1;
|
|
}
|
|
break;
|
|
case TokenType.DotDotDot:
|
|
{
|
|
CodeRegion endRegion = tokens[index].region;
|
|
index += 1;
|
|
return new ParlistNode(names: names, hasVarargs: true, startRegion: startRegion, endRegion: endRegion);
|
|
};
|
|
default:
|
|
{
|
|
throw new Exception($"{tokens[index].region}: Expected `...` or name to continue parlist starting at {startRegion}, got {tokens[index].type}");
|
|
}
|
|
}
|
|
}
|
|
return new ParlistNode(names: names, hasVarargs: false, startRegion: startRegion, endRegion: tokens[index - 1].region);
|
|
}
|
|
|
|
private FuncnameNode ParseFuncname(Token[] tokens)
|
|
{
|
|
if(index >= tokens.Length)
|
|
{
|
|
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected name to start funcname");
|
|
}
|
|
CodeRegion startRegion = tokens[index].region;
|
|
if(tokens[index].type != TokenType.Name)
|
|
{
|
|
throw new Exception($"{startRegion}: Expected name to start funcname, got {tokens[index].type}");
|
|
}
|
|
string name = ((Token.StringData)tokens[index].data!).data;
|
|
index += 1;
|
|
List<string> dottedNames = [];
|
|
while(index < tokens.Length && tokens[index].type == TokenType.Dot)
|
|
{
|
|
index += 1;
|
|
if(index >= tokens.Length)
|
|
{
|
|
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected name in dotted funcname starting at {startRegion}");
|
|
}
|
|
if(tokens[index].type != TokenType.Name)
|
|
{
|
|
throw new Exception($"{tokens[index].region}: Expected name in dotted funcname starting at {startRegion}, got {tokens[index].type}");
|
|
}
|
|
dottedNames.Add(((Token.StringData)tokens[index].data!).data);
|
|
}
|
|
if(index < tokens.Length && tokens[index].type == TokenType.Colon)
|
|
{
|
|
index += 1;
|
|
if(index >= tokens.Length)
|
|
{
|
|
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected name as first arg name after `:` in funcname starting at {startRegion}");
|
|
}
|
|
if(tokens[index].type != TokenType.Name)
|
|
{
|
|
throw new Exception($"{tokens[index].region}: Expected name as first arg name after `:` in funcname starting at {startRegion}, got {tokens[index].type}");
|
|
}
|
|
string firstArg = ((Token.StringData)tokens[index].data!).data;
|
|
CodeRegion endRegion = tokens[index].region;
|
|
index += 1;
|
|
return new FuncnameNode(name: name, dottedNames: dottedNames, firstArg: firstArg, startRegion: startRegion, endRegion: endRegion);
|
|
}
|
|
return new FuncnameNode(name: name, dottedNames: dottedNames, firstArg: null, startRegion: startRegion, endRegion: tokens[index - 1].region);
|
|
}
|
|
|
|
private ExplistNode ParseExplist(Token[] tokens)
|
|
{
|
|
List<ExpNode> exps = [ParseExp(tokens)];
|
|
while(index < tokens.Length && tokens[index].type == TokenType.Comma)
|
|
{
|
|
index += 1;
|
|
exps.Add(ParseExp(tokens));
|
|
}
|
|
return new ExplistNode(exps: exps, startRegion: exps[0].startRegion, endRegion: exps[^1].endRegion);
|
|
}
|
|
|
|
private ExpNode ParseExp(Token[] tokens)
|
|
{
|
|
ExpNode lhs = ParseExpPrimary(tokens);
|
|
return ParseExpPrecedence(tokens, lhs, 0);
|
|
}
|
|
|
|
private ExpNode ParseExpPrecedence(Token[] tokens, ExpNode lhs, int minPrecedence)
|
|
{
|
|
ExpNode currentLhs = lhs;
|
|
while(index < tokens.Length && IsBinop(tokens[index]))
|
|
{
|
|
CodeRegion startRegion = tokens[index].region;
|
|
int precedence = GetPrecedence(tokens[index]);
|
|
if(precedence < minPrecedence)
|
|
{
|
|
break;
|
|
}
|
|
BinopType op = GetBinopType(tokens[index]);
|
|
index += 1;
|
|
ExpNode rhs = ParseExpPrimary(tokens);
|
|
while(index < tokens.Length && IsBinop(tokens[index]) && (GetPrecedence(tokens[index]) > precedence || (GetPrecedence(tokens[index]) == precedence && IsRightAssociative(tokens[index]))))
|
|
{
|
|
int associativityBoost = (GetPrecedence(tokens[index]) == precedence) ? 0 : 1;
|
|
rhs = ParseExpPrecedence(tokens, lhs: rhs, minPrecedence: precedence + associativityBoost);
|
|
}
|
|
currentLhs = new ExpNode.Binop(node: new(lhs: currentLhs, type: op, rhs: rhs, startRegion: startRegion, endRegion: rhs.endRegion), startRegion: startRegion, endRegion: rhs.endRegion);
|
|
}
|
|
return currentLhs;
|
|
}
|
|
|
|
private static bool IsRightAssociative(Token token) => token.type is TokenType.DotDot or TokenType.Caret;
|
|
|
|
private static BinopType GetBinopType(Token token) => token.type switch
|
|
{
|
|
TokenType.Or => BinopType.LogicalOr,
|
|
TokenType.And => BinopType.LogicalAnd,
|
|
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,
|
|
_ => throw new Exception($"{token.region}: Expected binary operator with precedence, got {token.type}"),
|
|
};
|
|
|
|
private static int GetPrecedence(Token token) => token.type switch
|
|
{
|
|
TokenType.Or => 2,
|
|
TokenType.And => 4,
|
|
TokenType.Lt or TokenType.Gt or TokenType.LtEquals or TokenType.GtEquals or TokenType.TildeEquals or TokenType.EqualsEquals => 6,
|
|
TokenType.Pipe => 8,
|
|
TokenType.Tilde => 10,
|
|
TokenType.Ampersand => 12,
|
|
TokenType.LtLt or TokenType.GtGt => 14,
|
|
TokenType.DotDot => 16,
|
|
TokenType.Plus or TokenType.Minus => 18,
|
|
TokenType.Star or TokenType.Slash or TokenType.SlashSlash or TokenType.Percent => 20,
|
|
TokenType.Caret => 22,
|
|
_ => throw new Exception($"{token.region}: Expected binary operator with precedence, got {token.type}"),
|
|
};
|
|
|
|
private static bool IsBinop(Token token) => token.type switch
|
|
{
|
|
TokenType.Or or TokenType.And or TokenType.Lt or TokenType.Gt or TokenType.LtEquals or TokenType.GtEquals or TokenType.TildeEquals or TokenType.EqualsEquals or
|
|
TokenType.Pipe or TokenType.Tilde or TokenType.Ampersand or TokenType.LtLt or TokenType.GtGt or TokenType.DotDot or TokenType.Plus or TokenType.Minus or
|
|
TokenType.Star or TokenType.Slash or TokenType.SlashSlash or TokenType.Percent or TokenType.Caret => true,
|
|
_ => false
|
|
};
|
|
|
|
private ExpNode ParseExpPrimary(Token[] tokens)
|
|
{
|
|
if(index >= tokens.Length)
|
|
{
|
|
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected primary expression (`nil`, `true`, `false`, numeral, string, `...`, `function`, `{{`, `#`, `not`, `~`)");
|
|
}
|
|
CodeRegion startRegion = tokens[index].region;
|
|
switch(tokens[index].type)
|
|
{
|
|
case TokenType.Nil:
|
|
{
|
|
index += 1;
|
|
return new ExpNode.Nil(region: startRegion);
|
|
}
|
|
case TokenType.True:
|
|
{
|
|
index += 1;
|
|
return new ExpNode.True(region: startRegion);
|
|
}
|
|
case TokenType.False:
|
|
{
|
|
index += 1;
|
|
return new ExpNode.False(region: startRegion);
|
|
}
|
|
case TokenType.Numeral:
|
|
{
|
|
INumeral numeral = ((Token.NumeralData)tokens[index].data!).numeral;
|
|
index += 1;
|
|
return new ExpNode.Numeral(value: numeral, region: startRegion);
|
|
}
|
|
case TokenType.StringLiteral:
|
|
{
|
|
string value = ((Token.StringData)tokens[index].data!).data;
|
|
index += 1;
|
|
return new ExpNode.LiteralString(value: value, region: startRegion);
|
|
}
|
|
case TokenType.DotDotDot:
|
|
{
|
|
index += 1;
|
|
return new ExpNode.Varargs(region: startRegion);
|
|
}
|
|
case TokenType.CurlyOpen:
|
|
{
|
|
TableconstructorNode inner = ParseTableconstructor(tokens);
|
|
return new ExpNode.Tableconstructor(node: inner, startRegion: inner.startRegion, endRegion: inner.endRegion);
|
|
}
|
|
case TokenType.Function:
|
|
{
|
|
index += 1;
|
|
FuncbodyNode body = ParseFuncbody(tokens);
|
|
return new ExpNode.Functiondef(node: body, startRegion: startRegion, endRegion: body.endRegion);
|
|
}
|
|
case TokenType.Minus:
|
|
{
|
|
index += 1;
|
|
ExpNode unop = ParseExp(tokens);
|
|
return new ExpNode.Unop(node: new(type: UnopType.Minus, exp: unop, startRegion: startRegion, endRegion: unop.endRegion), startRegion: startRegion, endRegion: unop.endRegion);
|
|
}
|
|
case TokenType.Hash:
|
|
{
|
|
index += 1;
|
|
ExpNode unop = ParseExp(tokens);
|
|
return new ExpNode.Unop(node: new(type: UnopType.Length, exp: unop, startRegion: startRegion, endRegion: unop.endRegion), startRegion: startRegion, endRegion: unop.endRegion);
|
|
}
|
|
case TokenType.Not:
|
|
{
|
|
index += 1;
|
|
ExpNode unop = ParseExp(tokens);
|
|
return new ExpNode.Unop(node: new(type: UnopType.LogicalNot, exp: unop, startRegion: startRegion, endRegion: unop.endRegion), startRegion: startRegion, endRegion: unop.endRegion);
|
|
}
|
|
case TokenType.Tilde:
|
|
{
|
|
index += 1;
|
|
ExpNode unop = ParseExp(tokens);
|
|
return new ExpNode.Unop(node: new(type: UnopType.BinaryNot, exp: unop, startRegion: startRegion, endRegion: unop.endRegion), startRegion: startRegion, endRegion: unop.endRegion);
|
|
}
|
|
default:
|
|
{
|
|
SuffixexpNode suffixexp = ParseSuffixExp(tokens);
|
|
return new ExpNode.Suffixexp(node: suffixexp, startRegion: suffixexp.startRegion, endRegion: suffixexp.endRegion);
|
|
}
|
|
}
|
|
}
|
|
|
|
private RetstatNode ParseRetstat(Token[] tokens)
|
|
{
|
|
if(index >= tokens.Length)
|
|
{
|
|
throw new Exception($"Index {index} out of bounds of {tokens.Length}, expected `return` to start retstat");
|
|
}
|
|
CodeRegion startRegion = tokens[index].region;
|
|
if(tokens[index].type != TokenType.Return)
|
|
{
|
|
throw new Exception($"{startRegion}: Expected `return` to start retstat, got {tokens[index].type}");
|
|
}
|
|
index += 1;
|
|
if(index >= tokens.Length)
|
|
{
|
|
return new RetstatNode(values: null, startRegion: startRegion, endRegion: startRegion);
|
|
}
|
|
if(tokens[index].type is TokenType.Semicolon or TokenType.Else or TokenType.Elseif or TokenType.End)
|
|
{
|
|
CodeRegion emptyEndRegion;
|
|
if(tokens[index].type == TokenType.Semicolon)
|
|
{
|
|
emptyEndRegion = tokens[index].region;
|
|
index += 1;
|
|
}
|
|
else
|
|
{
|
|
emptyEndRegion = startRegion;
|
|
}
|
|
return new RetstatNode(values: null, startRegion: startRegion, endRegion: emptyEndRegion);
|
|
}
|
|
ExplistNode values = ParseExplist(tokens);
|
|
CodeRegion endRegion;
|
|
if(index < tokens.Length && tokens[index].type == TokenType.Semicolon)
|
|
{
|
|
endRegion = tokens[index].region;
|
|
index += 1;
|
|
}
|
|
else
|
|
{
|
|
endRegion = values.endRegion;
|
|
}
|
|
return new RetstatNode(values: values, startRegion: startRegion, endRegion: endRegion);
|
|
}
|
|
}
|