|
|
|
|
@@ -8,10 +8,10 @@ namespace NubLang.Syntax.Parsing;
|
|
|
|
|
|
|
|
|
|
public sealed class Parser
|
|
|
|
|
{
|
|
|
|
|
private IEnumerable<Token> _tokens = null!;
|
|
|
|
|
|
|
|
|
|
private IEnumerator<Token> _tokenEnumerator = null!;
|
|
|
|
|
private readonly List<Diagnostic> _diagnostics = [];
|
|
|
|
|
private int _tokenIndex;
|
|
|
|
|
private Token? _currentToken;
|
|
|
|
|
private bool _hasCurrentToken;
|
|
|
|
|
|
|
|
|
|
public IReadOnlyList<Diagnostic> GetDiagnostics()
|
|
|
|
|
{
|
|
|
|
|
@@ -21,12 +21,13 @@ public sealed class Parser
|
|
|
|
|
public SyntaxTree Parse(IEnumerable<Token> tokens)
|
|
|
|
|
{
|
|
|
|
|
_diagnostics.Clear();
|
|
|
|
|
_tokenIndex = 0;
|
|
|
|
|
_tokens = tokens;
|
|
|
|
|
_tokenEnumerator = tokens.GetEnumerator();
|
|
|
|
|
_hasCurrentToken = _tokenEnumerator.MoveNext();
|
|
|
|
|
_currentToken = _hasCurrentToken ? _tokenEnumerator.Current : null;
|
|
|
|
|
|
|
|
|
|
var definitions = new List<DefinitionSyntax>();
|
|
|
|
|
|
|
|
|
|
while (Peek().HasValue)
|
|
|
|
|
while (_hasCurrentToken)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
@@ -49,10 +50,9 @@ public sealed class Parser
|
|
|
|
|
catch (ParseException ex)
|
|
|
|
|
{
|
|
|
|
|
_diagnostics.Add(ex.Diagnostic);
|
|
|
|
|
while (Peek().HasValue)
|
|
|
|
|
while (_hasCurrentToken)
|
|
|
|
|
{
|
|
|
|
|
var token = Peek().Value;
|
|
|
|
|
if (token is SymbolToken { Symbol: Symbol.Extern or Symbol.Func or Symbol.Struct or Symbol.Interface })
|
|
|
|
|
if (_currentToken is SymbolToken { Symbol: Symbol.Extern or Symbol.Func or Symbol.Struct or Symbol.Interface })
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
@@ -80,7 +80,7 @@ public sealed class Parser
|
|
|
|
|
{
|
|
|
|
|
parameters.Add(ParseFuncParameter());
|
|
|
|
|
|
|
|
|
|
if (!TryExpectSymbol(Symbol.Comma) && Peek().TryGetValue(out var token) && token is not SymbolToken { Symbol: Symbol.CloseParen })
|
|
|
|
|
if (!TryExpectSymbol(Symbol.Comma) && _currentToken is not SymbolToken { Symbol: Symbol.CloseParen })
|
|
|
|
|
{
|
|
|
|
|
_diagnostics.Add(Diagnostic
|
|
|
|
|
.Warning("Missing comma between function parameters")
|
|
|
|
|
@@ -204,14 +204,7 @@ public sealed class Parser
|
|
|
|
|
|
|
|
|
|
private StatementSyntax ParseStatement()
|
|
|
|
|
{
|
|
|
|
|
if (!Peek().TryGetValue(out var token))
|
|
|
|
|
{
|
|
|
|
|
throw new ParseException(Diagnostic
|
|
|
|
|
.Error("Unexpected end of file while parsing statement")
|
|
|
|
|
.Build());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (token is SymbolToken symbol)
|
|
|
|
|
if (_currentToken is SymbolToken symbol)
|
|
|
|
|
{
|
|
|
|
|
switch (symbol.Symbol)
|
|
|
|
|
{
|
|
|
|
|
@@ -269,7 +262,6 @@ public sealed class Parser
|
|
|
|
|
private StatementSyntax ParseBreak()
|
|
|
|
|
{
|
|
|
|
|
ExpectSymbol(Symbol.Break);
|
|
|
|
|
Next();
|
|
|
|
|
return new BreakSyntax();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -322,18 +314,10 @@ public sealed class Parser
|
|
|
|
|
{
|
|
|
|
|
var left = ParsePrimaryExpression();
|
|
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
|
while (_currentToken is SymbolToken symbolToken && TryGetBinaryOperator(symbolToken.Symbol, out var op) && GetBinaryOperatorPrecedence(op.Value) >= precedence)
|
|
|
|
|
{
|
|
|
|
|
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 BinaryExpressionSyntax(left, op.Value, right);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -400,125 +384,99 @@ public sealed class Parser
|
|
|
|
|
|
|
|
|
|
private ExpressionSyntax ParsePrimaryExpression()
|
|
|
|
|
{
|
|
|
|
|
ExpressionSyntax expr;
|
|
|
|
|
|
|
|
|
|
var token = ExpectToken();
|
|
|
|
|
switch (token)
|
|
|
|
|
var expr = token switch
|
|
|
|
|
{
|
|
|
|
|
case LiteralToken literal:
|
|
|
|
|
LiteralToken literal => new LiteralSyntax(literal.Value, literal.Kind),
|
|
|
|
|
IdentifierToken identifier => new IdentifierSyntax(identifier.Value),
|
|
|
|
|
SymbolToken symbolToken => symbolToken.Symbol switch
|
|
|
|
|
{
|
|
|
|
|
expr = new LiteralSyntax(literal.Value, literal.Kind);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case IdentifierToken identifier:
|
|
|
|
|
{
|
|
|
|
|
expr = new IdentifierSyntax(identifier.Value);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SymbolToken symbolToken:
|
|
|
|
|
{
|
|
|
|
|
switch (symbolToken.Symbol)
|
|
|
|
|
{
|
|
|
|
|
case Symbol.Func:
|
|
|
|
|
{
|
|
|
|
|
List<ArrowFuncParameterSyntax> parameters = [];
|
|
|
|
|
ExpectSymbol(Symbol.OpenParen);
|
|
|
|
|
while (!TryExpectSymbol(Symbol.CloseParen))
|
|
|
|
|
{
|
|
|
|
|
var name = ExpectIdentifier();
|
|
|
|
|
parameters.Add(new ArrowFuncParameterSyntax(name.Value));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BlockSyntax body;
|
|
|
|
|
|
|
|
|
|
if (TryExpectSymbol(Symbol.Arrow))
|
|
|
|
|
{
|
|
|
|
|
var returnValue = ParseExpression();
|
|
|
|
|
var arrowExpression = new ReturnSyntax(returnValue);
|
|
|
|
|
body = new BlockSyntax([arrowExpression]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
body = ParseBlock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
expr = new ArrowFuncSyntax(parameters, body);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case Symbol.OpenParen:
|
|
|
|
|
{
|
|
|
|
|
var expression = ParseExpression();
|
|
|
|
|
ExpectSymbol(Symbol.CloseParen);
|
|
|
|
|
expr = expression;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case Symbol.Minus:
|
|
|
|
|
{
|
|
|
|
|
var expression = ParsePrimaryExpression();
|
|
|
|
|
expr = new UnaryExpressionSyntax(UnaryOperator.Negate, expression);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case Symbol.Bang:
|
|
|
|
|
{
|
|
|
|
|
var expression = ParsePrimaryExpression();
|
|
|
|
|
expr = new UnaryExpressionSyntax(UnaryOperator.Invert, expression);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case Symbol.OpenBracket:
|
|
|
|
|
{
|
|
|
|
|
var capacity = ParseExpression();
|
|
|
|
|
ExpectSymbol(Symbol.CloseBracket);
|
|
|
|
|
var type = ParseType();
|
|
|
|
|
expr = new ArrayInitializerSyntax(capacity, type);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case Symbol.Alloc:
|
|
|
|
|
{
|
|
|
|
|
var type = ParseType();
|
|
|
|
|
Dictionary<string, ExpressionSyntax> initializers = [];
|
|
|
|
|
ExpectSymbol(Symbol.OpenBrace);
|
|
|
|
|
while (!TryExpectSymbol(Symbol.CloseBrace))
|
|
|
|
|
{
|
|
|
|
|
var name = ExpectIdentifier().Value;
|
|
|
|
|
ExpectSymbol(Symbol.Assign);
|
|
|
|
|
var value = ParseExpression();
|
|
|
|
|
initializers.Add(name, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
expr = new StructInitializerSyntax(type, initializers);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
throw new ParseException(Diagnostic
|
|
|
|
|
.Error($"Unexpected symbol '{symbolToken.Symbol}' in expression")
|
|
|
|
|
.WithHelp("Expected literal, identifier, or '(' to start expression")
|
|
|
|
|
.Build());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
throw new ParseException(Diagnostic
|
|
|
|
|
.Error($"Unexpected token '{token.GetType().Name}' in expression")
|
|
|
|
|
.WithHelp("Expected literal, identifier, or parenthesized expression")
|
|
|
|
|
.Build());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Symbol.Func => ParseArrowFunction(),
|
|
|
|
|
Symbol.OpenParen => ParseParenthesizedExpression(),
|
|
|
|
|
Symbol.Minus => new UnaryExpressionSyntax(UnaryOperator.Negate, ParsePrimaryExpression()),
|
|
|
|
|
Symbol.Bang => new UnaryExpressionSyntax(UnaryOperator.Invert, ParsePrimaryExpression()),
|
|
|
|
|
Symbol.OpenBracket => ParseArrayInitializer(),
|
|
|
|
|
Symbol.Alloc => ParseStructInitializer(),
|
|
|
|
|
_ => throw new ParseException(Diagnostic
|
|
|
|
|
.Error($"Unexpected symbol '{symbolToken.Symbol}' in expression")
|
|
|
|
|
.WithHelp("Expected literal, identifier, or '(' to start expression")
|
|
|
|
|
.Build())
|
|
|
|
|
},
|
|
|
|
|
_ => throw new ParseException(Diagnostic
|
|
|
|
|
.Error($"Unexpected token '{token.GetType().Name}' in expression")
|
|
|
|
|
.WithHelp("Expected literal, identifier, or parenthesized expression")
|
|
|
|
|
.Build())
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return ParsePostfixOperators(expr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ExpressionSyntax ParseArrowFunction()
|
|
|
|
|
{
|
|
|
|
|
List<ArrowFuncParameterSyntax> parameters = [];
|
|
|
|
|
ExpectSymbol(Symbol.OpenParen);
|
|
|
|
|
while (!TryExpectSymbol(Symbol.CloseParen))
|
|
|
|
|
{
|
|
|
|
|
var name = ExpectIdentifier();
|
|
|
|
|
parameters.Add(new ArrowFuncParameterSyntax(name.Value));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ExpectSymbol(Symbol.Arrow);
|
|
|
|
|
BlockSyntax body;
|
|
|
|
|
|
|
|
|
|
if (_currentToken is SymbolToken { Symbol: Symbol.OpenBrace })
|
|
|
|
|
{
|
|
|
|
|
var returnValue = ParseExpression();
|
|
|
|
|
var arrowExpression = new ReturnSyntax(returnValue);
|
|
|
|
|
body = new BlockSyntax([arrowExpression]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
body = ParseBlock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new ArrowFuncSyntax(parameters, body);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ExpressionSyntax ParseParenthesizedExpression()
|
|
|
|
|
{
|
|
|
|
|
var expression = ParseExpression();
|
|
|
|
|
ExpectSymbol(Symbol.CloseParen);
|
|
|
|
|
return expression;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ExpressionSyntax ParseArrayInitializer()
|
|
|
|
|
{
|
|
|
|
|
var capacity = ParseExpression();
|
|
|
|
|
ExpectSymbol(Symbol.CloseBracket);
|
|
|
|
|
var type = ParseType();
|
|
|
|
|
return new ArrayInitializerSyntax(capacity, type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ExpressionSyntax ParseStructInitializer()
|
|
|
|
|
{
|
|
|
|
|
var type = ParseType();
|
|
|
|
|
Dictionary<string, ExpressionSyntax> initializers = [];
|
|
|
|
|
ExpectSymbol(Symbol.OpenBrace);
|
|
|
|
|
while (!TryExpectSymbol(Symbol.CloseBrace))
|
|
|
|
|
{
|
|
|
|
|
var name = ExpectIdentifier().Value;
|
|
|
|
|
ExpectSymbol(Symbol.Assign);
|
|
|
|
|
var value = ParseExpression();
|
|
|
|
|
initializers.Add(name, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new StructInitializerSyntax(type, initializers);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ExpressionSyntax ParsePostfixOperators(ExpressionSyntax expr)
|
|
|
|
|
{
|
|
|
|
|
while (true)
|
|
|
|
|
while (_hasCurrentToken)
|
|
|
|
|
{
|
|
|
|
|
if (TryExpectSymbol(Symbol.Ampersand))
|
|
|
|
|
{
|
|
|
|
|
expr = new AddressOfSyntax(expr);
|
|
|
|
|
break;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (TryExpectSymbol(Symbol.Caret))
|
|
|
|
|
@@ -548,7 +506,7 @@ public sealed class Parser
|
|
|
|
|
while (!TryExpectSymbol(Symbol.CloseParen))
|
|
|
|
|
{
|
|
|
|
|
parameters.Add(ParseExpression());
|
|
|
|
|
if (!TryExpectSymbol(Symbol.Comma) && Peek().TryGetValue(out var nextToken) && nextToken is not SymbolToken { Symbol: Symbol.CloseParen })
|
|
|
|
|
if (!TryExpectSymbol(Symbol.Comma) && _currentToken is not SymbolToken { Symbol: Symbol.CloseParen })
|
|
|
|
|
{
|
|
|
|
|
_diagnostics.Add(Diagnostic
|
|
|
|
|
.Warning("Missing comma between function arguments")
|
|
|
|
|
@@ -571,7 +529,7 @@ public sealed class Parser
|
|
|
|
|
{
|
|
|
|
|
ExpectSymbol(Symbol.OpenBrace);
|
|
|
|
|
List<StatementSyntax> statements = [];
|
|
|
|
|
while (Peek().HasValue && !TryExpectSymbol(Symbol.CloseBrace))
|
|
|
|
|
while (!TryExpectSymbol(Symbol.CloseBrace))
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
@@ -625,7 +583,7 @@ public sealed class Parser
|
|
|
|
|
{
|
|
|
|
|
var parameter = ParseType();
|
|
|
|
|
parameters.Add(parameter);
|
|
|
|
|
if (!TryExpectSymbol(Symbol.Comma) && Peek().TryGetValue(out var nextToken) && nextToken is not SymbolToken { Symbol: Symbol.CloseParen })
|
|
|
|
|
if (!TryExpectSymbol(Symbol.Comma) && _currentToken is not SymbolToken { Symbol: Symbol.CloseParen })
|
|
|
|
|
{
|
|
|
|
|
_diagnostics.Add(Diagnostic
|
|
|
|
|
.Warning("Missing comma between func type arguments")
|
|
|
|
|
@@ -647,21 +605,22 @@ public sealed class Parser
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
throw new ParseException(Diagnostic
|
|
|
|
|
.Error("Invalid type Syntax")
|
|
|
|
|
.Error("Invalid type syntax")
|
|
|
|
|
.WithHelp("Expected type name, '^' for pointer, or '[]' for array")
|
|
|
|
|
.Build());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Token ExpectToken()
|
|
|
|
|
{
|
|
|
|
|
if (!Peek().TryGetValue(out var token))
|
|
|
|
|
if (!_hasCurrentToken)
|
|
|
|
|
{
|
|
|
|
|
throw new ParseException(Diagnostic
|
|
|
|
|
.Error("Unexpected end of file")
|
|
|
|
|
.WithHelp("Expected more tokens to complete the Syntax")
|
|
|
|
|
.WithHelp("Expected more tokens to complete the syntax")
|
|
|
|
|
.Build());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var token = _currentToken!;
|
|
|
|
|
Next();
|
|
|
|
|
return token;
|
|
|
|
|
}
|
|
|
|
|
@@ -694,7 +653,7 @@ public sealed class Parser
|
|
|
|
|
|
|
|
|
|
private bool TryExpectSymbol(Symbol symbol)
|
|
|
|
|
{
|
|
|
|
|
if (Peek() is { Value: SymbolToken symbolToken } && symbolToken.Symbol == symbol)
|
|
|
|
|
if (_currentToken is SymbolToken symbolToken && symbolToken.Symbol == symbol)
|
|
|
|
|
{
|
|
|
|
|
Next();
|
|
|
|
|
return true;
|
|
|
|
|
@@ -705,7 +664,7 @@ public sealed class Parser
|
|
|
|
|
|
|
|
|
|
private bool TryExpectIdentifier([NotNullWhen(true)] out IdentifierToken? identifier)
|
|
|
|
|
{
|
|
|
|
|
if (Peek() is { Value: IdentifierToken identifierToken })
|
|
|
|
|
if (_currentToken is IdentifierToken identifierToken)
|
|
|
|
|
{
|
|
|
|
|
identifier = identifierToken;
|
|
|
|
|
Next();
|
|
|
|
|
@@ -730,20 +689,10 @@ public sealed class Parser
|
|
|
|
|
return identifier;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Optional<Token> Peek(int offset = 0)
|
|
|
|
|
{
|
|
|
|
|
var peekIndex = _tokenIndex + offset;
|
|
|
|
|
if (peekIndex < _tokens.Count())
|
|
|
|
|
{
|
|
|
|
|
return _tokens.ElementAt(peekIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Optional<Token>.Empty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Next()
|
|
|
|
|
{
|
|
|
|
|
_tokenIndex++;
|
|
|
|
|
_hasCurrentToken = _tokenEnumerator.MoveNext();
|
|
|
|
|
_currentToken = _hasCurrentToken ? _tokenEnumerator.Current : null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|