595 lines
18 KiB
C#
595 lines
18 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 = ExpectIdentifier();
|
|
imports.Add(name.Value);
|
|
}
|
|
else
|
|
{
|
|
definitions.Add(ParseDefinition());
|
|
}
|
|
}
|
|
|
|
return new ModuleNode(path, imports, definitions);
|
|
}
|
|
|
|
private DefinitionNode ParseDefinition()
|
|
{
|
|
List<Modifier> modifiers = [];
|
|
|
|
List<string> documentationParts = [];
|
|
while (_index < _tokens.Count && _tokens[_index] is DocumentationToken commentToken)
|
|
{
|
|
documentationParts.Add(commentToken.Documentation);
|
|
_index++;
|
|
}
|
|
|
|
var documentation = documentationParts.Count == 0 ? null : string.Join('\n', documentationParts);
|
|
|
|
while (TryExpectModifier(out var modifier))
|
|
{
|
|
modifiers.Add(modifier);
|
|
}
|
|
|
|
var keyword = ExpectSymbol();
|
|
return keyword.Symbol switch
|
|
{
|
|
Symbol.Func => ParseFuncDefinition(modifiers, Optional.OfNullable(documentation)),
|
|
Symbol.Struct => ParseStruct(modifiers, Optional.OfNullable(documentation)),
|
|
_ => throw new Exception("Unexpected symbol: " + keyword.Symbol)
|
|
};
|
|
}
|
|
|
|
private DefinitionNode ParseFuncDefinition(List<Modifier> modifiers, Optional<string> documentation)
|
|
{
|
|
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 = ParseType();
|
|
}
|
|
|
|
if (modifiers.Remove(Modifier.Extern))
|
|
{
|
|
if (modifiers.Count != 0)
|
|
{
|
|
throw new Exception($"Modifiers: {string.Join(", ", modifiers)} is not valid for an extern function");
|
|
}
|
|
|
|
return new ExternFuncDefinitionNode(name.Value, parameters, returnType, documentation);
|
|
}
|
|
|
|
var body = ParseBlock();
|
|
var global = modifiers.Remove(Modifier.Global);
|
|
|
|
if (modifiers.Count != 0)
|
|
{
|
|
throw new Exception($"Modifiers: {string.Join(", ", modifiers)} is not valid for a local function");
|
|
}
|
|
|
|
return new LocalFuncDefinitionNode(name.Value, parameters, body, returnType, global, documentation);
|
|
}
|
|
|
|
private StructDefinitionNode ParseStruct(List<Modifier> _, Optional<string> documentation)
|
|
{
|
|
var name = ExpectIdentifier().Value;
|
|
|
|
ExpectSymbol(Symbol.OpenBrace);
|
|
|
|
List<StructField> variables = [];
|
|
|
|
while (!TryExpectSymbol(Symbol.CloseBrace))
|
|
{
|
|
var variableName = ExpectIdentifier().Value;
|
|
ExpectSymbol(Symbol.Colon);
|
|
var variableType = ParseType();
|
|
|
|
var variableValue = Optional<ExpressionNode>.Empty();
|
|
|
|
if (TryExpectSymbol(Symbol.Assign))
|
|
{
|
|
variableValue = ParseExpression();
|
|
}
|
|
|
|
variables.Add(new StructField(variableName, variableType, variableValue));
|
|
}
|
|
|
|
return new StructDefinitionNode(name, variables, documentation);
|
|
}
|
|
|
|
private FuncParameter ParseFuncParameter()
|
|
{
|
|
var variadic = false;
|
|
if (TryExpectSymbol(Symbol.Period))
|
|
{
|
|
ExpectSymbol(Symbol.Period);
|
|
ExpectSymbol(Symbol.Period);
|
|
variadic = true;
|
|
}
|
|
|
|
var name = ExpectIdentifier();
|
|
ExpectSymbol(Symbol.Colon);
|
|
var type = ParseType();
|
|
|
|
return new FuncParameter(name.Value, type, variadic);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
return new FuncCallStatementNode(new FuncCall(identifier.Value, parameters));
|
|
}
|
|
case Symbol.Assign:
|
|
{
|
|
var value = ParseExpression();
|
|
return new VariableAssignmentNode(identifier.Value, Optional<NubType>.Empty(), value);
|
|
}
|
|
case Symbol.Colon:
|
|
{
|
|
var type = ParseType();
|
|
ExpectSymbol(Symbol.Assign);
|
|
var value = ParseExpression();
|
|
return new VariableAssignmentNode(identifier.Value, type, value);
|
|
}
|
|
default:
|
|
{
|
|
throw new Exception($"Unexpected symbol {symbol.Symbol}");
|
|
}
|
|
}
|
|
}
|
|
case SymbolToken symbol:
|
|
{
|
|
return symbol.Symbol switch
|
|
{
|
|
Symbol.Return => ParseReturn(),
|
|
Symbol.If => ParseIf(),
|
|
Symbol.While => ParseWhile(),
|
|
Symbol.Break => new BreakNode(),
|
|
Symbol.Continue => new ContinueNode(),
|
|
_ => 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();
|
|
}
|
|
|
|
return new ReturnNode(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 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()
|
|
{
|
|
ExpressionNode expr;
|
|
|
|
var token = ExpectToken();
|
|
switch (token)
|
|
{
|
|
case LiteralToken literal:
|
|
{
|
|
expr = new LiteralNode(literal.Value, literal.Type);
|
|
break;
|
|
}
|
|
case IdentifierToken identifier:
|
|
{
|
|
var next = Peek();
|
|
switch (next.Value)
|
|
{
|
|
case SymbolToken { Symbol: Symbol.OpenParen }:
|
|
{
|
|
Next();
|
|
List<ExpressionNode> parameters = [];
|
|
while (!TryExpectSymbol(Symbol.CloseParen))
|
|
{
|
|
parameters.Add(ParseExpression());
|
|
TryExpectSymbol(Symbol.Comma);
|
|
}
|
|
|
|
expr = new FuncCallExpressionNode(new FuncCall(identifier.Value, parameters));
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
expr = new IdentifierNode(identifier.Value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case SymbolToken symbolToken:
|
|
{
|
|
switch (symbolToken.Symbol)
|
|
{
|
|
case Symbol.OpenParen:
|
|
{
|
|
var expression = ParseExpression();
|
|
ExpectSymbol(Symbol.CloseParen);
|
|
expr = expression;
|
|
break;
|
|
}
|
|
case Symbol.LessThan:
|
|
{
|
|
var type = ParseType();
|
|
ExpectSymbol(Symbol.GreaterThan);
|
|
ExpectSymbol(Symbol.OpenParen);
|
|
var expressionToCast = ParseExpression();
|
|
ExpectSymbol(Symbol.CloseParen);
|
|
expr = new CastNode(type, expressionToCast);
|
|
break;
|
|
}
|
|
case Symbol.New:
|
|
{
|
|
var type = ParseType();
|
|
Dictionary<string, ExpressionNode> initializers = [];
|
|
ExpectSymbol(Symbol.OpenBrace);
|
|
while (!TryExpectSymbol(Symbol.CloseBrace))
|
|
{
|
|
var name = ExpectIdentifier().Value;
|
|
ExpectSymbol(Symbol.Assign);
|
|
var value = ParseExpression();
|
|
initializers.Add(name, value);
|
|
}
|
|
|
|
expr = new StructInitializerNode(type, initializers);
|
|
break;
|
|
}
|
|
case Symbol.Ampersand:
|
|
{
|
|
var expression = ParsePrimaryExpression();
|
|
expr = new AddressOfNode(expression);
|
|
break;
|
|
}
|
|
case Symbol.Minus:
|
|
{
|
|
var expression = ParsePrimaryExpression();
|
|
expr = new UnaryExpressionNode(UnaryExpressionOperator.Negate, expression);
|
|
break;
|
|
}
|
|
case Symbol.Bang:
|
|
{
|
|
var expression = ParsePrimaryExpression();
|
|
expr = new UnaryExpressionNode(UnaryExpressionOperator.Invert, expression);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
throw new Exception($"Unknown symbol: {symbolToken.Symbol}");
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
throw new Exception($"Unexpected token type {token.GetType().Name}");
|
|
}
|
|
}
|
|
|
|
return ParsePostfixOperators(expr);
|
|
}
|
|
|
|
private ExpressionNode ParsePostfixOperators(ExpressionNode expr)
|
|
{
|
|
while (true)
|
|
{
|
|
if (TryExpectSymbol(Symbol.Caret))
|
|
{
|
|
expr = new DereferenceNode(expr);
|
|
continue;
|
|
}
|
|
|
|
if (TryExpectSymbol(Symbol.Period))
|
|
{
|
|
var structMember = ExpectIdentifier().Value;
|
|
expr = new MemberAccessNode(expr, structMember);
|
|
continue;
|
|
}
|
|
|
|
if (TryExpectSymbol(Symbol.OpenBracket))
|
|
{
|
|
var index = ParseExpression();
|
|
ExpectSymbol(Symbol.CloseBracket);
|
|
expr = new ArrayIndexNode(expr, index);
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
private BlockNode ParseBlock()
|
|
{
|
|
ExpectSymbol(Symbol.OpenBrace);
|
|
List<StatementNode> statements = [];
|
|
while (!TryExpectSymbol(Symbol.CloseBrace))
|
|
{
|
|
statements.Add(ParseStatement());
|
|
}
|
|
|
|
return new BlockNode(statements);
|
|
}
|
|
|
|
private NubType ParseType()
|
|
{
|
|
if (TryExpectIdentifier(out var name))
|
|
{
|
|
return NubType.Parse(name);
|
|
}
|
|
|
|
if (TryExpectSymbol(Symbol.Caret))
|
|
{
|
|
var baseType = ParseType();
|
|
return new NubPointerType(baseType);
|
|
}
|
|
|
|
if (TryExpectSymbol(Symbol.OpenBracket))
|
|
{
|
|
ExpectSymbol(Symbol.CloseBracket);
|
|
var baseType = ParseType();
|
|
return new NubArrayType(baseType);
|
|
}
|
|
|
|
throw new Exception($"Unexpected token {Peek()} when parsing type");
|
|
}
|
|
|
|
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 bool TryExpectModifier(out Modifier modifier)
|
|
{
|
|
if (Peek() is { HasValue: true, Value: ModifierToken modifierToken })
|
|
{
|
|
modifier = modifierToken.Modifier;
|
|
Next();
|
|
return true;
|
|
}
|
|
|
|
modifier = default;
|
|
return false;
|
|
}
|
|
|
|
private bool TryExpectIdentifier([NotNullWhen(true)] out string? identifier)
|
|
{
|
|
if (Peek() is { HasValue: true, Value: IdentifierToken identifierToken })
|
|
{
|
|
identifier = identifierToken.Value;
|
|
Next();
|
|
return true;
|
|
}
|
|
|
|
identifier = null;
|
|
return false;
|
|
}
|
|
|
|
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 Optional<Token> Peek()
|
|
{
|
|
var peekIndex = _index;
|
|
while (peekIndex < _tokens.Count && _tokens[peekIndex] is DocumentationToken)
|
|
{
|
|
peekIndex++;
|
|
}
|
|
|
|
if (peekIndex < _tokens.Count)
|
|
{
|
|
return _tokens[peekIndex];
|
|
}
|
|
|
|
return Optional<Token>.Empty();
|
|
}
|
|
|
|
private void Next()
|
|
{
|
|
while (_index < _tokens.Count && _tokens[_index] is DocumentationToken)
|
|
{
|
|
_index++;
|
|
}
|
|
|
|
_index++;
|
|
}
|
|
} |