restructure fs

This commit is contained in:
nub31
2025-05-03 16:55:06 +02:00
parent bfc3aad516
commit 3b142b2453
75 changed files with 38 additions and 387 deletions

View File

@@ -0,0 +1,9 @@
namespace Nub.Lang.Frontend.Parsing;
public class ArrayIndexAccessNode(IdentifierNode identifier, ExpressionNode index) : ExpressionNode
{
public IdentifierNode Identifier { get; } = identifier;
public ExpressionNode Index { get; } = index;
public override string ToString() => $"{Identifier}[{Index}]";
}

View File

@@ -0,0 +1,8 @@
namespace Nub.Lang.Frontend.Parsing;
public class ArrayIndexAssignmentNode(IdentifierNode identifier, ExpressionNode index, ExpressionNode value) : StatementNode
{
public IdentifierNode Identifier { get; } = identifier;
public ExpressionNode Index { get; } = index;
public ExpressionNode Value { get; } = value;
}

View File

@@ -0,0 +1,7 @@
namespace Nub.Lang.Frontend.Parsing;
public class ArrayInitializerNode(long length, Type innerType) : ExpressionNode
{
public long Length { get; } = length;
public Type InnerType { get; } = innerType;
}

View File

@@ -0,0 +1,22 @@
namespace Nub.Lang.Frontend.Parsing;
public class BinaryExpressionNode(ExpressionNode left, BinaryExpressionOperator @operator, ExpressionNode right) : ExpressionNode
{
public ExpressionNode Left { get; } = left;
public BinaryExpressionOperator Operator { get; } = @operator;
public ExpressionNode Right { get; } = right;
}
public enum BinaryExpressionOperator
{
Equal,
NotEqual,
GreaterThan,
GreaterThanOrEqual,
LessThan,
LessThanOrEqual,
Plus,
Minus,
Multiply,
Divide
}

View File

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

View File

@@ -0,0 +1,3 @@
namespace Nub.Lang.Frontend.Parsing;
public class BreakNode : StatementNode;

View File

@@ -0,0 +1,3 @@
namespace Nub.Lang.Frontend.Parsing;
public class ContinueNode : StatementNode;

View File

@@ -0,0 +1,3 @@
namespace Nub.Lang.Frontend.Parsing;
public abstract class DefinitionNode : Node;

View File

@@ -0,0 +1,11 @@
namespace Nub.Lang.Frontend.Parsing;
public abstract class ExpressionNode : Node
{
private Type? _type;
public Type Type
{
get => _type ?? throw new Exception("Tried to access expression type before type was populated");
set => _type = value;
}
}

View File

@@ -0,0 +1,10 @@
namespace Nub.Lang.Frontend.Parsing;
public class ExternFuncDefinitionNode(string name, List<FuncParameter> parameters, Optional<Type> returnType) : DefinitionNode
{
public string Name { get; } = name;
public List<FuncParameter> Parameters { get; } = parameters;
public Optional<Type> ReturnType { get; } = returnType;
public override string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}";
}

View File

@@ -0,0 +1,9 @@
namespace Nub.Lang.Frontend.Parsing;
public class FuncCall(string name, List<ExpressionNode> parameters)
{
public string Name { get; } = name;
public List<ExpressionNode> Parameters { get; } = parameters;
public override string ToString() => $"{Name}()";
}

View File

@@ -0,0 +1,8 @@
namespace Nub.Lang.Frontend.Parsing;
public class FuncCallExpressionNode(FuncCall funcCall) : ExpressionNode
{
public FuncCall FuncCall { get; } = funcCall;
public override string ToString() => FuncCall.ToString();
}

View File

@@ -0,0 +1,8 @@
namespace Nub.Lang.Frontend.Parsing;
public class FuncCallStatementNode(FuncCall funcCall) : StatementNode
{
public FuncCall FuncCall { get; } = funcCall;
public override string ToString() => FuncCall.ToString();
}

View File

@@ -0,0 +1,7 @@
namespace Nub.Lang.Frontend.Parsing;
public class GlobalVariableDefinitionNode(string name, ExpressionNode value) : DefinitionNode
{
public string Name { get; } = name;
public ExpressionNode Value { get; } = value;
}

View File

@@ -0,0 +1,8 @@
namespace Nub.Lang.Frontend.Parsing;
public class IdentifierNode(string identifier) : ExpressionNode
{
public string Identifier { get; } = identifier;
public override string ToString() => Identifier;
}

View File

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

View File

@@ -0,0 +1,7 @@
namespace Nub.Lang.Frontend.Parsing;
public class LiteralNode(string literal, Type type) : ExpressionNode
{
public string Literal { get; } = literal;
public Type LiteralType { get; } = type;
}

View File

@@ -0,0 +1,11 @@
namespace Nub.Lang.Frontend.Parsing;
public class LocalFuncDefinitionNode(string name, List<FuncParameter> parameters, BlockNode body, Optional<Type> returnType) : DefinitionNode
{
public string Name { get; } = name;
public List<FuncParameter> Parameters { get; } = parameters;
public BlockNode Body { get; } = body;
public Optional<Type> ReturnType { get; } = returnType;
public override string ToString() => $"{Name}({string.Join(", ", Parameters.Select(p => p.ToString()))}){(ReturnType.HasValue ? ": " + ReturnType.Value : "")}";
}

View File

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

View File

@@ -0,0 +1,3 @@
namespace Nub.Lang.Frontend.Parsing;
public abstract class Node;

View File

@@ -0,0 +1,607 @@
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 is not StringType)
{
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.Let => ParseGlobalVariableDefinition(),
Symbol.Func => ParseFuncDefinition(),
Symbol.Extern => ParseExternFuncDefinition(),
Symbol.Struct => ParseStruct(),
_ => throw new Exception("Unexpected symbol: " + keyword.Symbol)
};
}
private GlobalVariableDefinitionNode ParseGlobalVariableDefinition()
{
var name = ExpectIdentifier();
ExpectSymbol(Symbol.Assign);
var value = ParseExpression();
ExpectSymbol(Symbol.Semicolon);
return new GlobalVariableDefinitionNode(name.Value, value);
}
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<Type>.Empty();
if (TryExpectSymbol(Symbol.Colon))
{
returnType = ParseType();
}
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<Type>.Empty();
if (TryExpectSymbol(Symbol.Colon))
{
returnType = ParseType();
}
ExpectSymbol(Symbol.Semicolon);
return new ExternFuncDefinitionNode(name.Value, parameters, returnType);
}
private StructDefinitionNode ParseStruct()
{
var name = ExpectIdentifier().Value;
ExpectSymbol(Symbol.OpenBrace);
List<StructMember> variables = [];
while (!TryExpectSymbol(Symbol.CloseBrace))
{
ExpectSymbol(Symbol.Let);
var variableName = ExpectIdentifier().Value;
ExpectSymbol(Symbol.Colon);
var variableType = ParseType();
var variableValue = Optional<ExpressionNode>.Empty();
if (TryExpectSymbol(Symbol.Assign))
{
variableValue = ParseExpression();
}
ExpectSymbol(Symbol.Semicolon);
variables.Add(new StructMember(variableName, variableType, variableValue));
}
return new StructDefinitionNode(name, variables);
}
private FuncParameter ParseFuncParameter()
{
var name = ExpectIdentifier();
ExpectSymbol(Symbol.Colon);
var type = ParseType();
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);
if (identifier.Value == "syscall")
{
return new SyscallStatementNode(new Syscall(parameters));
}
return new FuncCallStatementNode(new FuncCall(identifier.Value, parameters));
}
case Symbol.OpenBracket:
{
var index = ParseExpression();
ExpectSymbol(Symbol.CloseBracket);
ExpectSymbol(Symbol.Assign);
var value = ParseExpression();
ExpectSymbol(Symbol.Semicolon);
return new ArrayIndexAssignmentNode(new IdentifierNode(identifier.Value), index, value);
}
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 = ParseType();
switch (type)
{
case ArrayType:
{
ExpectSymbol(Symbol.OpenParen);
var size = ExpectLiteral();
if (size.Type is not PrimitiveType { Kind: PrimitiveTypeKind.Int64 })
{
throw new Exception($"Array initializer size must be an {PrimitiveTypeKind.Int64}");
}
ExpectSymbol(Symbol.CloseParen);
return new ArrayInitializerNode(long.Parse(size.Value), type);
}
case StructType structType:
{
Dictionary<string, ExpressionNode> initializers = [];
ExpectSymbol(Symbol.OpenBrace);
while (!TryExpectSymbol(Symbol.CloseBrace))
{
var name = ExpectIdentifier().Value;
ExpectSymbol(Symbol.Assign);
var value = ParseExpression();
TryExpectSymbol(Symbol.Comma);
initializers.Add(name, value);
}
return new StructInitializerNode(structType, initializers);
}
default:
throw new Exception($"Type {type} cannot be initialized with the new keyword");
}
}
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.OpenBracket:
{
Next();
var index = ParseExpression();
ExpectSymbol(Symbol.CloseBracket);
return new ArrayIndexAccessNode(new IdentifierNode(identifier.Value), index);
}
case Symbol.OpenParen:
{
Next();
List<ExpressionNode> parameters = [];
while (!TryExpectSymbol(Symbol.CloseParen))
{
parameters.Add(ParseExpression());
TryExpectSymbol(Symbol.Comma);
}
if (identifier.Value == "syscall")
{
return new SyscallExpressionNode(new Syscall(parameters));
}
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 Type ParseType()
{
var name = ExpectIdentifier().Value;
switch (name)
{
case "String":
{
return new StringType();
}
case "Array":
{
ExpectSymbol(Symbol.LessThan);
var innerType = ParseType();
ExpectSymbol(Symbol.GreaterThan);
return new ArrayType(innerType);
}
case "Any":
{
return new AnyType();
}
default:
{
if (PrimitiveType.TryParse(name, out var primitiveType))
{
return primitiveType;
}
return new StructType(name);
}
}
}
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++;
}
}

View File

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

View File

@@ -0,0 +1,3 @@
namespace Nub.Lang.Frontend.Parsing;
public abstract class StatementNode : Node;

View File

@@ -0,0 +1,7 @@
namespace Nub.Lang.Frontend.Parsing;
public class StructDefinitionNode(string name, List<StructMember> members) : DefinitionNode
{
public string Name { get; } = name;
public List<StructMember> Members { get; } = members;
}

View File

@@ -0,0 +1,7 @@
namespace Nub.Lang.Frontend.Parsing;
public class StructInitializerNode(StructType structType, Dictionary<string, ExpressionNode> initializers) : ExpressionNode
{
public StructType StructType { get; } = structType;
public Dictionary<string, ExpressionNode> Initializers { get; } = initializers;
}

View File

@@ -0,0 +1,6 @@
namespace Nub.Lang.Frontend.Parsing;
public class StructMemberAccessorNode(List<string> members) : ExpressionNode
{
public List<string> Members { get; } = members;
}

View File

@@ -0,0 +1,6 @@
namespace Nub.Lang.Frontend.Parsing;
public class Syscall(List<ExpressionNode> parameters)
{
public List<ExpressionNode> Parameters { get; } = parameters;
}

View File

@@ -0,0 +1,6 @@
namespace Nub.Lang.Frontend.Parsing;
public class SyscallExpressionNode(Syscall syscall) : ExpressionNode
{
public Syscall Syscall { get; } = syscall;
}

View File

@@ -0,0 +1,6 @@
namespace Nub.Lang.Frontend.Parsing;
public class SyscallStatementNode(Syscall syscall) : StatementNode
{
public Syscall Syscall { get; } = syscall;
}

View File

@@ -0,0 +1,7 @@
namespace Nub.Lang.Frontend.Parsing;
public class VariableAssignmentNode(string name, ExpressionNode value) : StatementNode
{
public string Name { get; } = name;
public ExpressionNode Value { get; } = value;
}

View File

@@ -0,0 +1,7 @@
namespace Nub.Lang.Frontend.Parsing;
public class VariableReassignmentNode(string name, ExpressionNode value) : StatementNode
{
public string Name { get; } = name;
public ExpressionNode Value { get; } = value;
}

View File

@@ -0,0 +1,7 @@
namespace Nub.Lang.Frontend.Parsing;
public class WhileNode(ExpressionNode condition, BlockNode body) : StatementNode
{
public ExpressionNode Condition { get; } = condition;
public BlockNode Body { get; } = body;
}