This commit is contained in:
nub31
2025-05-24 21:29:47 +02:00
parent 5f5cf7126a
commit 830bf95308
28 changed files with 154 additions and 86 deletions

View File

@@ -1,6 +1,8 @@
using Nub.Lang.Frontend.Lexing;
namespace Nub.Lang.Frontend.Parsing;
public class AddressOfNode(ExpressionNode expression) : ExpressionNode
public class AddressOfNode(IReadOnlyList<Token> tokens, ExpressionNode expression) : ExpressionNode(tokens)
{
public ExpressionNode Expression { get; } = expression;
}

View File

@@ -1,6 +1,8 @@
using Nub.Lang.Frontend.Lexing;
namespace Nub.Lang.Frontend.Parsing;
public class ArrayIndexNode(ExpressionNode expression, ExpressionNode index) : ExpressionNode
public class ArrayIndexNode(IReadOnlyList<Token> tokens, ExpressionNode expression, ExpressionNode index) : ExpressionNode(tokens)
{
public ExpressionNode Expression { get; } = expression;
public ExpressionNode Index { get; } = index;

View File

@@ -1,6 +1,8 @@
namespace Nub.Lang.Frontend.Parsing;
using Nub.Lang.Frontend.Lexing;
public class BinaryExpressionNode(ExpressionNode left, BinaryExpressionOperator @operator, ExpressionNode right) : ExpressionNode
namespace Nub.Lang.Frontend.Parsing;
public class BinaryExpressionNode(IReadOnlyList<Token> tokens, ExpressionNode left, BinaryExpressionOperator @operator, ExpressionNode right) : ExpressionNode(tokens)
{
public ExpressionNode Left { get; } = left;
public BinaryExpressionOperator Operator { get; } = @operator;

View File

@@ -1,6 +1,8 @@
namespace Nub.Lang.Frontend.Parsing;
using Nub.Lang.Frontend.Lexing;
public class BlockNode(List<StatementNode> statements) : Node
namespace Nub.Lang.Frontend.Parsing;
public class BlockNode(IReadOnlyList<Token> tokens, List<StatementNode> statements) : Node(tokens)
{
public List<StatementNode> Statements { get; } = statements;
}

View File

@@ -1,3 +1,5 @@
using Nub.Lang.Frontend.Lexing;
namespace Nub.Lang.Frontend.Parsing;
public class BreakNode : StatementNode;
public class BreakNode(IReadOnlyList<Token> tokens) : StatementNode(tokens);

View File

@@ -1,6 +1,8 @@
using Nub.Lang.Frontend.Lexing;
namespace Nub.Lang.Frontend.Parsing;
public class CastNode(NubType targetType, ExpressionNode expression) : ExpressionNode
public class CastNode(IReadOnlyList<Token> tokens, NubType targetType, ExpressionNode expression) : ExpressionNode(tokens)
{
public NubType TargetType { get; } = targetType;
public ExpressionNode Expression { get; } = expression;

View File

@@ -1,3 +1,5 @@
using Nub.Lang.Frontend.Lexing;
namespace Nub.Lang.Frontend.Parsing;
public class ContinueNode : StatementNode;
public class ContinueNode(IReadOnlyList<Token> tokens) : StatementNode(tokens);

View File

@@ -1,6 +1,8 @@
namespace Nub.Lang.Frontend.Parsing;
using Nub.Lang.Frontend.Lexing;
public abstract class DefinitionNode(Optional<string> documentation) : Node
namespace Nub.Lang.Frontend.Parsing;
public abstract class DefinitionNode(IReadOnlyList<Token> tokens, Optional<string> documentation) : Node(tokens)
{
public Optional<string> Documentation { get; set; } = documentation;
}

View File

@@ -1,6 +1,8 @@
using Nub.Lang.Frontend.Lexing;
namespace Nub.Lang.Frontend.Parsing;
public class DereferenceNode(ExpressionNode expression) : ExpressionNode
public class DereferenceNode(IReadOnlyList<Token> tokens, ExpressionNode expression) : ExpressionNode(tokens)
{
public ExpressionNode Expression { get; } = expression;
}

View File

@@ -1,6 +1,8 @@
namespace Nub.Lang.Frontend.Parsing;
using Nub.Lang.Frontend.Lexing;
public abstract class ExpressionNode : Node
namespace Nub.Lang.Frontend.Parsing;
public abstract class ExpressionNode(IReadOnlyList<Token> tokens) : Node(tokens)
{
private NubType? _type;
public NubType Type

View File

@@ -1,6 +1,8 @@
namespace Nub.Lang.Frontend.Parsing;
using Nub.Lang.Frontend.Lexing;
public class ExternFuncDefinitionNode(string name, List<FuncParameter> parameters, Optional<NubType> returnType, Optional<string> documentation) : DefinitionNode(documentation)
namespace Nub.Lang.Frontend.Parsing;
public class ExternFuncDefinitionNode(IReadOnlyList<Token> tokens, Optional<string> documentation, string name, List<FuncParameter> parameters, Optional<NubType> returnType) : DefinitionNode(tokens, documentation)
{
public string Name { get; } = name;
public List<FuncParameter> Parameters { get; } = parameters;

View File

@@ -1,6 +1,8 @@
namespace Nub.Lang.Frontend.Parsing;
using Nub.Lang.Frontend.Lexing;
public class FuncCallExpressionNode(FuncCall funcCall) : ExpressionNode
namespace Nub.Lang.Frontend.Parsing;
public class FuncCallExpressionNode(IReadOnlyList<Token> tokens, FuncCall funcCall) : ExpressionNode(tokens)
{
public FuncCall FuncCall { get; } = funcCall;

View File

@@ -1,6 +1,8 @@
namespace Nub.Lang.Frontend.Parsing;
using Nub.Lang.Frontend.Lexing;
public class FuncCallStatementNode(FuncCall funcCall) : StatementNode
namespace Nub.Lang.Frontend.Parsing;
public class FuncCallStatementNode(IReadOnlyList<Token> tokens, FuncCall funcCall) : StatementNode(tokens)
{
public FuncCall FuncCall { get; } = funcCall;

View File

@@ -1,6 +1,8 @@
namespace Nub.Lang.Frontend.Parsing;
using Nub.Lang.Frontend.Lexing;
public class IdentifierNode(string identifier) : ExpressionNode
namespace Nub.Lang.Frontend.Parsing;
public class IdentifierNode(IReadOnlyList<Token> tokens, string identifier) : ExpressionNode(tokens)
{
public string Identifier { get; } = identifier;

View File

@@ -1,6 +1,8 @@
namespace Nub.Lang.Frontend.Parsing;
using Nub.Lang.Frontend.Lexing;
public class IfNode(ExpressionNode condition, BlockNode body, Optional<Variant<IfNode, BlockNode>> @else) : StatementNode
namespace Nub.Lang.Frontend.Parsing;
public class IfNode(IReadOnlyList<Token> tokens, ExpressionNode condition, BlockNode body, Optional<Variant<IfNode, BlockNode>> @else) : StatementNode(tokens)
{
public ExpressionNode Condition { get; } = condition;
public BlockNode Body { get; } = body;

View File

@@ -1,7 +1,9 @@
namespace Nub.Lang.Frontend.Parsing;
using Nub.Lang.Frontend.Lexing;
public class LiteralNode(string literal, NubType type) : ExpressionNode
namespace Nub.Lang.Frontend.Parsing;
public class LiteralNode(IReadOnlyList<Token> tokens, string literal, NubType literalType) : ExpressionNode(tokens)
{
public string Literal { get; } = literal;
public NubType LiteralType { get; } = type;
public NubType LiteralType { get; } = literalType;
}

View File

@@ -1,6 +1,8 @@
namespace Nub.Lang.Frontend.Parsing;
using Nub.Lang.Frontend.Lexing;
public class LocalFuncDefinitionNode(string name, List<FuncParameter> parameters, BlockNode body, Optional<NubType> returnType, bool global, Optional<string> documentation) : DefinitionNode(documentation)
namespace Nub.Lang.Frontend.Parsing;
public class LocalFuncDefinitionNode(IReadOnlyList<Token> tokens, Optional<string> documentation, string name, List<FuncParameter> parameters, BlockNode body, Optional<NubType> returnType, bool global) : DefinitionNode(tokens, documentation)
{
public string Name { get; } = name;
public List<FuncParameter> Parameters { get; } = parameters;

View File

@@ -1,6 +1,8 @@
namespace Nub.Lang.Frontend.Parsing;
using Nub.Lang.Frontend.Lexing;
public class MemberAccessNode(ExpressionNode expression, string member) : ExpressionNode
namespace Nub.Lang.Frontend.Parsing;
public class MemberAccessNode(IReadOnlyList<Token> tokens, ExpressionNode expression, string member) : ExpressionNode(tokens)
{
public ExpressionNode Expression { get; } = expression;
public string Member { get; } = member;

View File

@@ -1,6 +1,8 @@
namespace Nub.Lang.Frontend.Parsing;
using Nub.Lang.Frontend.Lexing;
public class ModuleNode(string path, List<string> imports, List<DefinitionNode> definitions) : Node
namespace Nub.Lang.Frontend.Parsing;
public class ModuleNode(IReadOnlyList<Token> tokens, string path, List<string> imports, List<DefinitionNode> definitions) : Node(tokens)
{
public string Path { get; } = path;
public List<string> Imports { get; } = imports;

View File

@@ -1,3 +1,8 @@
namespace Nub.Lang.Frontend.Parsing;
using Nub.Lang.Frontend.Lexing;
public abstract class Node;
namespace Nub.Lang.Frontend.Parsing;
public abstract class Node(IReadOnlyList<Token> tokens)
{
public IReadOnlyList<Token> Tokens { get; set; } = tokens;
}

View File

@@ -29,11 +29,12 @@ public class Parser
}
}
return new ModuleNode(rootFilePath, imports, definitions);
return new ModuleNode(GetTokensForNode(0), rootFilePath, imports, definitions);
}
private DefinitionNode ParseDefinition()
{
var startIndex = _index;
List<Modifier> modifiers = [];
List<string> documentationParts = [];
@@ -53,13 +54,13 @@ public class Parser
var keyword = ExpectSymbol();
return keyword.Symbol switch
{
Symbol.Func => ParseFuncDefinition(modifiers, Optional.OfNullable(documentation)),
Symbol.Struct => ParseStruct(modifiers, Optional.OfNullable(documentation)),
Symbol.Func => ParseFuncDefinition(startIndex, modifiers, Optional.OfNullable(documentation)),
Symbol.Struct => ParseStruct(startIndex, modifiers, Optional.OfNullable(documentation)),
_ => throw new Exception("Unexpected symbol: " + keyword.Symbol)
};
}
private DefinitionNode ParseFuncDefinition(List<Modifier> modifiers, Optional<string> documentation)
private DefinitionNode ParseFuncDefinition(int startIndex, List<Modifier> modifiers, Optional<string> documentation)
{
var name = ExpectIdentifier();
List<FuncParameter> parameters = [];
@@ -86,7 +87,7 @@ public class Parser
throw new Exception($"Modifiers: {string.Join(", ", modifiers)} is not valid for an extern function");
}
return new ExternFuncDefinitionNode(name.Value, parameters, returnType, documentation);
return new ExternFuncDefinitionNode(GetTokensForNode(startIndex), documentation, name.Value, parameters, returnType);
}
var body = ParseBlock();
@@ -97,10 +98,10 @@ public class Parser
throw new Exception($"Modifiers: {string.Join(", ", modifiers)} is not valid for a local function");
}
return new LocalFuncDefinitionNode(name.Value, parameters, body, returnType, global, documentation);
return new LocalFuncDefinitionNode(GetTokensForNode(startIndex), documentation, name.Value, parameters, body, returnType, global);
}
private StructDefinitionNode ParseStruct(List<Modifier> _, Optional<string> documentation)
private StructDefinitionNode ParseStruct(int startIndex, List<Modifier> _, Optional<string> documentation)
{
var name = ExpectIdentifier().Value;
@@ -124,7 +125,7 @@ public class Parser
variables.Add(new StructField(variableName, variableType, variableValue));
}
return new StructDefinitionNode(name, variables, documentation);
return new StructDefinitionNode(GetTokensForNode(startIndex), documentation, name, variables);
}
private FuncParameter ParseFuncParameter()
@@ -146,6 +147,7 @@ public class Parser
private StatementNode ParseStatement()
{
var startIndex = _index;
var token = ExpectToken();
switch (token)
{
@@ -163,19 +165,19 @@ public class Parser
TryExpectSymbol(Symbol.Comma);
}
return new FuncCallStatementNode(new FuncCall(identifier.Value, parameters));
return new FuncCallStatementNode(GetTokensForNode(startIndex), new FuncCall(identifier.Value, parameters));
}
case Symbol.Assign:
{
var value = ParseExpression();
return new VariableAssignmentNode(identifier.Value, Optional<NubType>.Empty(), value);
return new VariableAssignmentNode(GetTokensForNode(startIndex), 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);
return new VariableAssignmentNode(GetTokensForNode(startIndex), identifier.Value, type, value);
}
default:
{
@@ -187,11 +189,11 @@ public class Parser
{
return symbol.Symbol switch
{
Symbol.Return => ParseReturn(),
Symbol.If => ParseIf(),
Symbol.While => ParseWhile(),
Symbol.Break => new BreakNode(),
Symbol.Continue => new ContinueNode(),
Symbol.Return => ParseReturn(startIndex),
Symbol.If => ParseIf(startIndex),
Symbol.While => ParseWhile(startIndex),
Symbol.Break => new BreakNode(GetTokensForNode(startIndex)),
Symbol.Continue => new ContinueNode(GetTokensForNode(startIndex)),
_ => throw new Exception($"Unexpected symbol {symbol.Symbol}")
};
}
@@ -202,7 +204,7 @@ public class Parser
}
}
private ReturnNode ParseReturn()
private ReturnNode ParseReturn(int startIndex)
{
var value = Optional<ExpressionNode>.Empty();
if (!TryExpectSymbol(Symbol.Semicolon))
@@ -210,10 +212,10 @@ public class Parser
value = ParseExpression();
}
return new ReturnNode(value);
return new ReturnNode(GetTokensForNode(startIndex), value);
}
private IfNode ParseIf()
private IfNode ParseIf(int startIndex)
{
var condition = ParseExpression();
var body = ParseBlock();
@@ -221,23 +223,25 @@ public class Parser
var elseStatement = Optional<Variant<IfNode, BlockNode>>.Empty();
if (TryExpectSymbol(Symbol.Else))
{
var newStartIndex = _index;
elseStatement = TryExpectSymbol(Symbol.If)
? (Variant<IfNode, BlockNode>)ParseIf()
? (Variant<IfNode, BlockNode>)ParseIf(newStartIndex)
: (Variant<IfNode, BlockNode>)ParseBlock();
}
return new IfNode(condition, body, elseStatement);
return new IfNode(GetTokensForNode(startIndex), condition, body, elseStatement);
}
private WhileNode ParseWhile()
private WhileNode ParseWhile(int startIndex)
{
var condition = ParseExpression();
var body = ParseBlock();
return new WhileNode(condition, body);
return new WhileNode(GetTokensForNode(startIndex), condition, body);
}
private ExpressionNode ParseExpression(int precedence = 0)
{
var startIndex = _index;
var left = ParsePrimaryExpression();
while (true)
@@ -250,7 +254,7 @@ public class Parser
Next();
var right = ParseExpression(GetBinaryOperatorPrecedence(op.Value) + 1);
left = new BinaryExpressionNode(left, op.Value, right);
left = new BinaryExpressionNode(GetTokensForNode(startIndex), left, op.Value, right);
}
return left;
@@ -316,6 +320,7 @@ public class Parser
private ExpressionNode ParsePrimaryExpression()
{
var startIndex = _index;
ExpressionNode expr;
var token = ExpectToken();
@@ -323,7 +328,7 @@ public class Parser
{
case LiteralToken literal:
{
expr = new LiteralNode(literal.Value, literal.Type);
expr = new LiteralNode(GetTokensForNode(startIndex), literal.Value, literal.Type);
break;
}
case IdentifierToken identifier:
@@ -341,12 +346,12 @@ public class Parser
TryExpectSymbol(Symbol.Comma);
}
expr = new FuncCallExpressionNode(new FuncCall(identifier.Value, parameters));
expr = new FuncCallExpressionNode(GetTokensForNode(startIndex), new FuncCall(identifier.Value, parameters));
break;
}
default:
{
expr = new IdentifierNode(identifier.Value);
expr = new IdentifierNode(GetTokensForNode(startIndex), identifier.Value);
break;
}
}
@@ -371,7 +376,7 @@ public class Parser
ExpectSymbol(Symbol.OpenParen);
var expressionToCast = ParseExpression();
ExpectSymbol(Symbol.CloseParen);
expr = new CastNode(type, expressionToCast);
expr = new CastNode(GetTokensForNode(startIndex), type, expressionToCast);
break;
}
case Symbol.New:
@@ -387,25 +392,25 @@ public class Parser
initializers.Add(name, value);
}
expr = new StructInitializerNode(type, initializers);
expr = new StructInitializerNode(GetTokensForNode(startIndex), type, initializers);
break;
}
case Symbol.Ampersand:
{
var expression = ParsePrimaryExpression();
expr = new AddressOfNode(expression);
expr = new AddressOfNode(GetTokensForNode(startIndex), expression);
break;
}
case Symbol.Minus:
{
var expression = ParsePrimaryExpression();
expr = new UnaryExpressionNode(UnaryExpressionOperator.Negate, expression);
expr = new UnaryExpressionNode(GetTokensForNode(startIndex), UnaryExpressionOperator.Negate, expression);
break;
}
case Symbol.Bang:
{
var expression = ParsePrimaryExpression();
expr = new UnaryExpressionNode(UnaryExpressionOperator.Invert, expression);
expr = new UnaryExpressionNode(GetTokensForNode(startIndex), UnaryExpressionOperator.Invert, expression);
break;
}
default:
@@ -422,23 +427,23 @@ public class Parser
}
}
return ParsePostfixOperators(expr);
return ParsePostfixOperators(startIndex, expr);
}
private ExpressionNode ParsePostfixOperators(ExpressionNode expr)
private ExpressionNode ParsePostfixOperators(int startIndex, ExpressionNode expr)
{
while (true)
{
if (TryExpectSymbol(Symbol.Caret))
{
expr = new DereferenceNode(expr);
expr = new DereferenceNode(GetTokensForNode(startIndex), expr);
continue;
}
if (TryExpectSymbol(Symbol.Period))
{
var structMember = ExpectIdentifier().Value;
expr = new MemberAccessNode(expr, structMember);
expr = new MemberAccessNode(GetTokensForNode(startIndex), expr, structMember);
continue;
}
@@ -446,7 +451,7 @@ public class Parser
{
var index = ParseExpression();
ExpectSymbol(Symbol.CloseBracket);
expr = new ArrayIndexNode(expr, index);
expr = new ArrayIndexNode(GetTokensForNode(startIndex), expr, index);
continue;
}
@@ -458,6 +463,7 @@ public class Parser
private BlockNode ParseBlock()
{
var startIndex = _index;
ExpectSymbol(Symbol.OpenBrace);
List<StatementNode> statements = [];
while (!TryExpectSymbol(Symbol.CloseBrace))
@@ -465,7 +471,7 @@ public class Parser
statements.Add(ParseStatement());
}
return new BlockNode(statements);
return new BlockNode(GetTokensForNode(startIndex), statements);
}
private NubType ParseType()
@@ -592,4 +598,9 @@ public class Parser
_index++;
}
private IReadOnlyList<Token> GetTokensForNode(int startIndex)
{
return _tokens[startIndex..Math.Min(_index, _tokens.Count - 1)];
}
}

View File

@@ -1,6 +1,8 @@
namespace Nub.Lang.Frontend.Parsing;
using Nub.Lang.Frontend.Lexing;
public class ReturnNode(Optional<ExpressionNode> value) : StatementNode
namespace Nub.Lang.Frontend.Parsing;
public class ReturnNode(IReadOnlyList<Token> tokens, Optional<ExpressionNode> value) : StatementNode(tokens)
{
public Optional<ExpressionNode> Value { get; } = value;
}

View File

@@ -1,3 +1,5 @@
namespace Nub.Lang.Frontend.Parsing;
using Nub.Lang.Frontend.Lexing;
public abstract class StatementNode : Node;
namespace Nub.Lang.Frontend.Parsing;
public abstract class StatementNode(IReadOnlyList<Token> tokens) : Node(tokens);

View File

@@ -1,6 +1,8 @@
namespace Nub.Lang.Frontend.Parsing;
using Nub.Lang.Frontend.Lexing;
public class StructDefinitionNode(string name, List<StructField> fields, Optional<string> documentation) : DefinitionNode(documentation)
namespace Nub.Lang.Frontend.Parsing;
public class StructDefinitionNode(IReadOnlyList<Token> tokens, Optional<string> documentation, string name, List<StructField> fields) : DefinitionNode(tokens, documentation)
{
public string Name { get; } = name;
public List<StructField> Fields { get; } = fields;

View File

@@ -1,6 +1,8 @@
namespace Nub.Lang.Frontend.Parsing;
using Nub.Lang.Frontend.Lexing;
public class StructInitializerNode(NubType structType, Dictionary<string, ExpressionNode> initializers) : ExpressionNode
namespace Nub.Lang.Frontend.Parsing;
public class StructInitializerNode(IReadOnlyList<Token> tokens, NubType structType, Dictionary<string, ExpressionNode> initializers) : ExpressionNode(tokens)
{
public NubType StructType { get; } = structType;
public Dictionary<string, ExpressionNode> Initializers { get; } = initializers;

View File

@@ -1,6 +1,8 @@
using Nub.Lang.Frontend.Lexing;
namespace Nub.Lang.Frontend.Parsing;
public class UnaryExpressionNode(UnaryExpressionOperator @operator, ExpressionNode operand) : ExpressionNode
public class UnaryExpressionNode(IReadOnlyList<Token> tokens, UnaryExpressionOperator @operator, ExpressionNode operand) : ExpressionNode(tokens)
{
public UnaryExpressionOperator Operator { get; } = @operator;
public ExpressionNode Operand { get; } = operand;

View File

@@ -1,6 +1,8 @@
namespace Nub.Lang.Frontend.Parsing;
using Nub.Lang.Frontend.Lexing;
public class VariableAssignmentNode(string name, Optional<NubType> explicitType, ExpressionNode value) : StatementNode
namespace Nub.Lang.Frontend.Parsing;
public class VariableAssignmentNode(IReadOnlyList<Token> tokens, string name, Optional<NubType> explicitType, ExpressionNode value) : StatementNode(tokens)
{
public string Name { get; } = name;
public Optional<NubType> ExplicitType { get; } = explicitType;

View File

@@ -1,6 +1,8 @@
using Nub.Lang.Frontend.Lexing;
namespace Nub.Lang.Frontend.Parsing;
public class WhileNode(ExpressionNode condition, BlockNode body) : StatementNode
public class WhileNode(IReadOnlyList<Token> tokens, ExpressionNode condition, BlockNode body) : StatementNode(tokens)
{
public ExpressionNode Condition { get; } = condition;
public BlockNode Body { get; } = body;