541 lines
16 KiB
C#
541 lines
16 KiB
C#
using System.Diagnostics.CodeAnalysis;
|
|
using Nub.Lang.Frontend.Lexing;
|
|
|
|
namespace Nub.Lang.Frontend.Parsing;
|
|
|
|
public class Parser
|
|
{
|
|
private List<Token> _tokens = [];
|
|
private int _index;
|
|
|
|
public ModuleNode ParseModule(List<Token> tokens, string path)
|
|
{
|
|
_index = 0;
|
|
_tokens = tokens;
|
|
|
|
List<DefinitionNode> definitions = [];
|
|
List<string> imports = [];
|
|
|
|
while (Peek().HasValue)
|
|
{
|
|
if (TryExpectSymbol(Symbol.Import))
|
|
{
|
|
var name = ExpectLiteral();
|
|
if (!name.Type.Equals(NubType.String))
|
|
{
|
|
throw new Exception("Import statements must have a string literal value");
|
|
}
|
|
|
|
TryExpectSymbol(Symbol.Semicolon);
|
|
imports.Add(name.Value);
|
|
}
|
|
else
|
|
{
|
|
definitions.Add(ParseDefinition());
|
|
}
|
|
}
|
|
|
|
return new ModuleNode(path, imports, definitions);
|
|
}
|
|
|
|
private DefinitionNode ParseDefinition()
|
|
{
|
|
var keyword = ExpectSymbol();
|
|
return keyword.Symbol switch
|
|
{
|
|
Symbol.Func => ParseFuncDefinition(),
|
|
Symbol.Extern => ParseExternFuncDefinition(),
|
|
Symbol.Struct => ParseStruct(),
|
|
_ => throw new Exception("Unexpected symbol: " + keyword.Symbol)
|
|
};
|
|
}
|
|
|
|
private LocalFuncDefinitionNode ParseFuncDefinition()
|
|
{
|
|
var name = ExpectIdentifier();
|
|
List<FuncParameter> parameters = [];
|
|
ExpectSymbol(Symbol.OpenParen);
|
|
if (!TryExpectSymbol(Symbol.CloseParen))
|
|
{
|
|
while (!TryExpectSymbol(Symbol.CloseParen))
|
|
{
|
|
parameters.Add(ParseFuncParameter());
|
|
TryExpectSymbol(Symbol.Comma);
|
|
}
|
|
}
|
|
|
|
var returnType = Optional<NubType>.Empty();
|
|
if (TryExpectSymbol(Symbol.Colon))
|
|
{
|
|
returnType = ParseTypeInstance();
|
|
}
|
|
|
|
var body = ParseBlock();
|
|
|
|
return new LocalFuncDefinitionNode(name.Value, parameters, body, returnType);
|
|
}
|
|
|
|
private ExternFuncDefinitionNode ParseExternFuncDefinition()
|
|
{
|
|
ExpectSymbol(Symbol.Func);
|
|
var name = ExpectIdentifier();
|
|
List<FuncParameter> parameters = [];
|
|
ExpectSymbol(Symbol.OpenParen);
|
|
if (!TryExpectSymbol(Symbol.CloseParen))
|
|
{
|
|
while (!TryExpectSymbol(Symbol.CloseParen))
|
|
{
|
|
parameters.Add(ParseFuncParameter());
|
|
TryExpectSymbol(Symbol.Comma);
|
|
}
|
|
}
|
|
|
|
var returnType = Optional<NubType>.Empty();
|
|
if (TryExpectSymbol(Symbol.Colon))
|
|
{
|
|
returnType = ParseTypeInstance();
|
|
}
|
|
|
|
ExpectSymbol(Symbol.Semicolon);
|
|
|
|
return new ExternFuncDefinitionNode(name.Value, parameters, returnType);
|
|
}
|
|
|
|
private StructDefinitionNode ParseStruct()
|
|
{
|
|
var name = ExpectIdentifier().Value;
|
|
|
|
ExpectSymbol(Symbol.OpenBrace);
|
|
|
|
List<StructField> variables = [];
|
|
|
|
while (!TryExpectSymbol(Symbol.CloseBrace))
|
|
{
|
|
var variableName = ExpectIdentifier().Value;
|
|
ExpectSymbol(Symbol.Colon);
|
|
var variableType = ParseTypeInstance();
|
|
|
|
var variableValue = Optional<ExpressionNode>.Empty();
|
|
|
|
if (TryExpectSymbol(Symbol.Assign))
|
|
{
|
|
variableValue = ParseExpression();
|
|
}
|
|
|
|
ExpectSymbol(Symbol.Semicolon);
|
|
|
|
variables.Add(new StructField(variableName, variableType, variableValue));
|
|
}
|
|
|
|
return new StructDefinitionNode(name, variables);
|
|
}
|
|
|
|
private FuncParameter ParseFuncParameter()
|
|
{
|
|
var name = ExpectIdentifier();
|
|
ExpectSymbol(Symbol.Colon);
|
|
var type = ParseTypeInstance();
|
|
|
|
return new FuncParameter(name.Value, type);
|
|
}
|
|
|
|
private StatementNode ParseStatement()
|
|
{
|
|
var token = ExpectToken();
|
|
switch (token)
|
|
{
|
|
case IdentifierToken identifier:
|
|
{
|
|
var symbol = ExpectSymbol();
|
|
switch (symbol.Symbol)
|
|
{
|
|
case Symbol.OpenParen:
|
|
{
|
|
var parameters = new List<ExpressionNode>();
|
|
while (!TryExpectSymbol(Symbol.CloseParen))
|
|
{
|
|
parameters.Add(ParseExpression());
|
|
TryExpectSymbol(Symbol.Comma);
|
|
}
|
|
|
|
ExpectSymbol(Symbol.Semicolon);
|
|
|
|
return new FuncCallStatementNode(new FuncCall(identifier.Value, parameters));
|
|
}
|
|
case Symbol.Assign:
|
|
{
|
|
var value = ParseExpression();
|
|
ExpectSymbol(Symbol.Semicolon);
|
|
return new VariableReassignmentNode(identifier.Value, value);
|
|
}
|
|
default:
|
|
{
|
|
throw new Exception($"Unexpected symbol {symbol.Symbol}");
|
|
}
|
|
}
|
|
}
|
|
case SymbolToken symbol:
|
|
{
|
|
return symbol.Symbol switch
|
|
{
|
|
Symbol.Return => ParseReturn(),
|
|
Symbol.Let => ParseVariableAssignment(),
|
|
Symbol.If => ParseIf(),
|
|
Symbol.While => ParseWhile(),
|
|
Symbol.Break => ParseBreak(),
|
|
Symbol.Continue => ParseContinue(),
|
|
_ => throw new Exception($"Unexpected symbol {symbol.Symbol}")
|
|
};
|
|
}
|
|
default:
|
|
{
|
|
throw new Exception($"Unexpected token type {token.GetType().Name}");
|
|
}
|
|
}
|
|
}
|
|
|
|
private ReturnNode ParseReturn()
|
|
{
|
|
var value = Optional<ExpressionNode>.Empty();
|
|
if (!TryExpectSymbol(Symbol.Semicolon))
|
|
{
|
|
value = ParseExpression();
|
|
ExpectSymbol(Symbol.Semicolon);
|
|
}
|
|
|
|
return new ReturnNode(value);
|
|
}
|
|
|
|
private VariableAssignmentNode ParseVariableAssignment()
|
|
{
|
|
var name = ExpectIdentifier().Value;
|
|
ExpectSymbol(Symbol.Assign);
|
|
var value = ParseExpression();
|
|
ExpectSymbol(Symbol.Semicolon);
|
|
|
|
return new VariableAssignmentNode(name, value);
|
|
}
|
|
|
|
private IfNode ParseIf()
|
|
{
|
|
var condition = ParseExpression();
|
|
var body = ParseBlock();
|
|
|
|
var elseStatement = Optional<Variant<IfNode, BlockNode>>.Empty();
|
|
if (TryExpectSymbol(Symbol.Else))
|
|
{
|
|
elseStatement = TryExpectSymbol(Symbol.If)
|
|
? (Variant<IfNode, BlockNode>)ParseIf()
|
|
: (Variant<IfNode, BlockNode>)ParseBlock();
|
|
}
|
|
|
|
return new IfNode(condition, body, elseStatement);
|
|
}
|
|
|
|
private WhileNode ParseWhile()
|
|
{
|
|
var condition = ParseExpression();
|
|
var body = ParseBlock();
|
|
return new WhileNode(condition, body);
|
|
}
|
|
|
|
private BreakNode ParseBreak()
|
|
{
|
|
ExpectSymbol(Symbol.Semicolon);
|
|
return new BreakNode();
|
|
}
|
|
|
|
private ContinueNode ParseContinue()
|
|
{
|
|
ExpectSymbol(Symbol.Semicolon);
|
|
return new ContinueNode();
|
|
}
|
|
|
|
private ExpressionNode ParseExpression(int precedence = 0)
|
|
{
|
|
var left = ParsePrimaryExpression();
|
|
|
|
while (true)
|
|
{
|
|
var token = Peek();
|
|
if (!token.HasValue || token.Value is not SymbolToken symbolToken || !TryGetBinaryOperator(symbolToken.Symbol, out var op) ||
|
|
GetBinaryOperatorPrecedence(op.Value) < precedence)
|
|
break;
|
|
|
|
Next();
|
|
var right = ParseExpression(GetBinaryOperatorPrecedence(op.Value) + 1);
|
|
|
|
left = new BinaryExpressionNode(left, op.Value, right);
|
|
}
|
|
|
|
return left;
|
|
}
|
|
|
|
private static int GetBinaryOperatorPrecedence(BinaryExpressionOperator binaryExpressionOperator)
|
|
{
|
|
return binaryExpressionOperator switch
|
|
{
|
|
BinaryExpressionOperator.Multiply => 3,
|
|
BinaryExpressionOperator.Divide => 3,
|
|
BinaryExpressionOperator.Plus => 2,
|
|
BinaryExpressionOperator.Minus => 2,
|
|
BinaryExpressionOperator.GreaterThan => 1,
|
|
BinaryExpressionOperator.GreaterThanOrEqual => 1,
|
|
BinaryExpressionOperator.LessThan => 1,
|
|
BinaryExpressionOperator.LessThanOrEqual => 1,
|
|
BinaryExpressionOperator.Equal => 0,
|
|
BinaryExpressionOperator.NotEqual => 0,
|
|
_ => throw new ArgumentOutOfRangeException(nameof(binaryExpressionOperator), binaryExpressionOperator, null)
|
|
};
|
|
}
|
|
|
|
private static bool TryGetBinaryOperator(Symbol symbol, [NotNullWhen(true)] out BinaryExpressionOperator? binaryExpressionOperator)
|
|
{
|
|
switch (symbol)
|
|
{
|
|
case Symbol.Equal:
|
|
binaryExpressionOperator = BinaryExpressionOperator.Equal;
|
|
return true;
|
|
case Symbol.NotEqual:
|
|
binaryExpressionOperator = BinaryExpressionOperator.NotEqual;
|
|
return true;
|
|
case Symbol.LessThan:
|
|
binaryExpressionOperator = BinaryExpressionOperator.LessThan;
|
|
return true;
|
|
case Symbol.LessThanOrEqual:
|
|
binaryExpressionOperator = BinaryExpressionOperator.LessThanOrEqual;
|
|
return true;
|
|
case Symbol.GreaterThan:
|
|
binaryExpressionOperator = BinaryExpressionOperator.GreaterThan;
|
|
return true;
|
|
case Symbol.GreaterThanOrEqual:
|
|
binaryExpressionOperator = BinaryExpressionOperator.GreaterThanOrEqual;
|
|
return true;
|
|
case Symbol.Plus:
|
|
binaryExpressionOperator = BinaryExpressionOperator.Plus;
|
|
return true;
|
|
case Symbol.Minus:
|
|
binaryExpressionOperator = BinaryExpressionOperator.Minus;
|
|
return true;
|
|
case Symbol.Star:
|
|
binaryExpressionOperator = BinaryExpressionOperator.Multiply;
|
|
return true;
|
|
case Symbol.ForwardSlash:
|
|
binaryExpressionOperator = BinaryExpressionOperator.Divide;
|
|
return true;
|
|
default:
|
|
binaryExpressionOperator = null;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private ExpressionNode ParsePrimaryExpression()
|
|
{
|
|
var token = ExpectToken();
|
|
switch (token)
|
|
{
|
|
case LiteralToken literal:
|
|
{
|
|
return new LiteralNode(literal.Value, literal.Type);
|
|
}
|
|
case IdentifierToken identifier:
|
|
{
|
|
return ParseExpressionIdentifier(identifier);
|
|
}
|
|
case SymbolToken symbolToken:
|
|
{
|
|
switch (symbolToken.Symbol)
|
|
{
|
|
case Symbol.OpenParen:
|
|
{
|
|
var expression = ParseExpression();
|
|
ExpectSymbol(Symbol.CloseParen);
|
|
return expression;
|
|
}
|
|
case Symbol.New:
|
|
{
|
|
var type = ParseTypeInstance();
|
|
Dictionary<string, ExpressionNode> initializers = [];
|
|
ExpectSymbol(Symbol.OpenBrace);
|
|
while (!TryExpectSymbol(Symbol.CloseBrace))
|
|
{
|
|
var name = ExpectIdentifier().Value;
|
|
ExpectSymbol(Symbol.Assign);
|
|
var value = ParseExpression();
|
|
TryExpectSymbol(Symbol.Semicolon);
|
|
initializers.Add(name, value);
|
|
}
|
|
|
|
return new StructInitializerNode(type, initializers);
|
|
}
|
|
default:
|
|
throw new Exception($"Unknown symbol: {symbolToken.Symbol}");
|
|
}
|
|
}
|
|
default:
|
|
throw new Exception($"Unexpected token type {token.GetType().Name}");
|
|
}
|
|
}
|
|
|
|
private ExpressionNode ParseExpressionIdentifier(IdentifierToken identifier)
|
|
{
|
|
var token = Peek();
|
|
if (!token.HasValue)
|
|
{
|
|
return new IdentifierNode(identifier.Value);
|
|
}
|
|
|
|
switch (token.Value)
|
|
{
|
|
case SymbolToken symbolToken:
|
|
{
|
|
switch (symbolToken.Symbol)
|
|
{
|
|
case Symbol.Period:
|
|
{
|
|
Next();
|
|
List<string> members =
|
|
[
|
|
identifier.Value,
|
|
ExpectIdentifier().Value
|
|
];
|
|
|
|
while (TryExpectSymbol(Symbol.Period))
|
|
{
|
|
members.Add(ExpectIdentifier().Value);
|
|
}
|
|
|
|
return new StructMemberAccessorNode(members);
|
|
}
|
|
case Symbol.OpenParen:
|
|
{
|
|
Next();
|
|
List<ExpressionNode> parameters = [];
|
|
while (!TryExpectSymbol(Symbol.CloseParen))
|
|
{
|
|
parameters.Add(ParseExpression());
|
|
TryExpectSymbol(Symbol.Comma);
|
|
}
|
|
|
|
return new FuncCallExpressionNode(new FuncCall(identifier.Value, parameters));
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return new IdentifierNode(identifier.Value);
|
|
}
|
|
|
|
private BlockNode ParseBlock()
|
|
{
|
|
ExpectSymbol(Symbol.OpenBrace);
|
|
List<StatementNode> statements = [];
|
|
while (!TryExpectSymbol(Symbol.CloseBrace))
|
|
{
|
|
statements.Add(ParseStatement());
|
|
}
|
|
|
|
return new BlockNode(statements);
|
|
}
|
|
|
|
private NubType ParseTypeInstance()
|
|
{
|
|
var parameters = new List<NubType>();
|
|
var name = ExpectIdentifier().Value;
|
|
|
|
if (TryExpectSymbol(Symbol.LessThan))
|
|
{
|
|
do
|
|
{
|
|
parameters.Add(ParseTypeInstance());
|
|
} while (TryExpectSymbol(Symbol.Comma));
|
|
|
|
ExpectSymbol(Symbol.GreaterThan);
|
|
}
|
|
|
|
return new NubType(name, parameters.ToArray());
|
|
}
|
|
|
|
private Token ExpectToken()
|
|
{
|
|
var token = Peek();
|
|
if (!token.HasValue)
|
|
{
|
|
throw new Exception("Reached end of tokens");
|
|
}
|
|
|
|
Next();
|
|
return token.Value;
|
|
}
|
|
|
|
private SymbolToken ExpectSymbol()
|
|
{
|
|
var token = ExpectToken();
|
|
if (token is not SymbolToken symbol)
|
|
{
|
|
throw new Exception($"Expected {nameof(SymbolToken)} but got {token.GetType().Name}");
|
|
}
|
|
|
|
return symbol;
|
|
}
|
|
|
|
private void ExpectSymbol(Symbol symbol)
|
|
{
|
|
var token = ExpectSymbol();
|
|
if (token.Symbol != symbol)
|
|
{
|
|
throw new Exception($"Expected symbol {symbol} but got {token.Symbol}");
|
|
}
|
|
}
|
|
|
|
private bool TryExpectSymbol(Symbol symbol)
|
|
{
|
|
var result = Peek() is { HasValue: true, Value: SymbolToken symbolToken } && symbolToken.Symbol == symbol;
|
|
if (result) Next();
|
|
return result;
|
|
}
|
|
|
|
private IdentifierToken ExpectIdentifier()
|
|
{
|
|
var token = ExpectToken();
|
|
if (token is not IdentifierToken identifier)
|
|
{
|
|
throw new Exception($"Expected {nameof(IdentifierToken)} but got {token.GetType().Name}");
|
|
}
|
|
|
|
return identifier;
|
|
}
|
|
|
|
private LiteralToken ExpectLiteral()
|
|
{
|
|
var token = ExpectToken();
|
|
if (token is not LiteralToken literal)
|
|
{
|
|
throw new Exception($"Expected {nameof(LiteralToken)} but got {token.GetType().Name}");
|
|
}
|
|
|
|
return literal;
|
|
}
|
|
|
|
private Optional<Token> Peek()
|
|
{
|
|
while (_index < _tokens.Count && _tokens.ElementAt(_index) is SymbolToken { Symbol: Symbol.Whitespace })
|
|
{
|
|
Next();
|
|
}
|
|
|
|
if (_index < _tokens.Count)
|
|
{
|
|
return _tokens.ElementAt(_index);
|
|
}
|
|
|
|
return Optional<Token>.Empty();
|
|
}
|
|
|
|
private void Next()
|
|
{
|
|
_index++;
|
|
}
|
|
} |