From 5103593ab4824127f4dd59071cb66b3ceeea950d Mon Sep 17 00:00:00 2001 From: nub31 Date: Sun, 26 Jan 2025 01:55:34 +0100 Subject: [PATCH] Basic parser --- Nub.Lang/Nub.Lang/Input/program.nub | 2 - Nub.Lang/Nub.Lang/Parsing/BlockNode.cs | 2 +- Nub.Lang/Nub.Lang/Parsing/FuncCallNode.cs | 7 + .../Nub.Lang/Parsing/FuncDefinitionNode.cs | 3 +- Nub.Lang/Nub.Lang/Parsing/IdentifierNode.cs | 6 + Nub.Lang/Nub.Lang/Parsing/LiteralNode.cs | 6 + Nub.Lang/Nub.Lang/Parsing/Parser.cs | 141 +++++++++++++++++- Nub.Lang/Nub.Lang/Parsing/SyscallNode.cs | 9 +- Nub.Lang/Nub.Lang/Program.cs | 8 +- 9 files changed, 166 insertions(+), 18 deletions(-) create mode 100644 Nub.Lang/Nub.Lang/Parsing/FuncCallNode.cs create mode 100644 Nub.Lang/Nub.Lang/Parsing/IdentifierNode.cs create mode 100644 Nub.Lang/Nub.Lang/Parsing/LiteralNode.cs diff --git a/Nub.Lang/Nub.Lang/Input/program.nub b/Nub.Lang/Nub.Lang/Input/program.nub index 5d03e25..4a965bd 100644 --- a/Nub.Lang/Nub.Lang/Input/program.nub +++ b/Nub.Lang/Nub.Lang/Input/program.nub @@ -5,8 +5,6 @@ let STD_ERR = 2; func main() { write("test"); - syscall(SYS_WRITE, STD_OUT, msg); - return 12; } func write(msg: void) { diff --git a/Nub.Lang/Nub.Lang/Parsing/BlockNode.cs b/Nub.Lang/Nub.Lang/Parsing/BlockNode.cs index efafc77..8b08ceb 100644 --- a/Nub.Lang/Nub.Lang/Parsing/BlockNode.cs +++ b/Nub.Lang/Nub.Lang/Parsing/BlockNode.cs @@ -1,6 +1,6 @@ namespace Nub.Lang.Parsing; -public class BlockNode(IEnumerable statements) : StatementNode +public class BlockNode(IEnumerable statements) : Node { public IEnumerable Statements { get; } = statements; } \ No newline at end of file diff --git a/Nub.Lang/Nub.Lang/Parsing/FuncCallNode.cs b/Nub.Lang/Nub.Lang/Parsing/FuncCallNode.cs new file mode 100644 index 0000000..66a9d96 --- /dev/null +++ b/Nub.Lang/Nub.Lang/Parsing/FuncCallNode.cs @@ -0,0 +1,7 @@ +namespace Nub.Lang.Parsing; + +public class FuncCallNode(string name, IEnumerable parameters) : StatementNode +{ + public string Name { get; } = name; + public IEnumerable Parameters { get; } = parameters; +} \ No newline at end of file diff --git a/Nub.Lang/Nub.Lang/Parsing/FuncDefinitionNode.cs b/Nub.Lang/Nub.Lang/Parsing/FuncDefinitionNode.cs index 1ac761b..0cc0fd7 100644 --- a/Nub.Lang/Nub.Lang/Parsing/FuncDefinitionNode.cs +++ b/Nub.Lang/Nub.Lang/Parsing/FuncDefinitionNode.cs @@ -1,7 +1,8 @@ namespace Nub.Lang.Parsing; -public class FuncDefinitionNode(string name, IEnumerable parameters) : DefinitionNode +public class FuncDefinitionNode(string name, IEnumerable parameters, BlockNode body) : DefinitionNode { public string Name { get; } = name; public IEnumerable Parameters { get; } = parameters; + public BlockNode Body { get; } = body; } \ No newline at end of file diff --git a/Nub.Lang/Nub.Lang/Parsing/IdentifierNode.cs b/Nub.Lang/Nub.Lang/Parsing/IdentifierNode.cs new file mode 100644 index 0000000..7bb87de --- /dev/null +++ b/Nub.Lang/Nub.Lang/Parsing/IdentifierNode.cs @@ -0,0 +1,6 @@ +namespace Nub.Lang.Parsing; + +public class IdentifierNode(string identifier) : ExpressionNode +{ + public string Identifier { get; } = identifier; +} \ No newline at end of file diff --git a/Nub.Lang/Nub.Lang/Parsing/LiteralNode.cs b/Nub.Lang/Nub.Lang/Parsing/LiteralNode.cs new file mode 100644 index 0000000..892b897 --- /dev/null +++ b/Nub.Lang/Nub.Lang/Parsing/LiteralNode.cs @@ -0,0 +1,6 @@ +namespace Nub.Lang.Parsing; + +public class LiteralNode(string value) : ExpressionNode +{ + public string Value { get; } = value; +} \ No newline at end of file diff --git a/Nub.Lang/Nub.Lang/Parsing/Parser.cs b/Nub.Lang/Nub.Lang/Parsing/Parser.cs index 22e1378..a3063da 100644 --- a/Nub.Lang/Nub.Lang/Parsing/Parser.cs +++ b/Nub.Lang/Nub.Lang/Parsing/Parser.cs @@ -15,17 +15,130 @@ public class Parser public IEnumerable Parse() { + _index = 0; List definitions = []; while (Peek().HasValue) { definitions.Add(ParseDefinition()); } + return definitions; } private DefinitionNode ParseDefinition() { - throw new NotImplementedException(); + var keyword = ExpectSymbol(); + return keyword.Symbol switch + { + Symbol.Let => ParseGlobalVariableDefinition(), + Symbol.Func => ParseFuncDefinition(), + _ => 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 FuncDefinitionNode ParseFuncDefinition() + { + var name = ExpectIdentifier(); + List parameters = []; + ExpectSymbol(Symbol.OpenParen); + if (!TryExpectSymbol(Symbol.CloseParen)) + { + while (!TryExpectSymbol(Symbol.CloseParen)) + { + parameters.Add(ParseFuncParameter()); + } + } + + var body = ParseBlock(); + + return new FuncDefinitionNode(name.Value, parameters, body); + } + + 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(); + while (!TryExpectSymbol(Symbol.CloseParen)) + { + parameters.Add(ParseExpression()); + TryExpectSymbol(Symbol.Comma); + } + + ExpectSymbol(Symbol.Semicolon); + + if (identifier.Value == "syscall") + { + return new SyscallNode(parameters); + } + + return new FuncCallNode(identifier.Value, parameters); + } + case Symbol.Assign: + throw new NotImplementedException(); + default: + throw new Exception($"Unexpected symbol {symbol.Symbol}"); + } + } + default: + throw new Exception($"Unexpected token type {token.GetType().Name}"); + } + } + + private ExpressionNode ParseExpression() + { + var token = ExpectToken(); + return token switch + { + LiteralToken literal => new LiteralNode(literal.Value), + IdentifierToken identifier => new IdentifierNode(identifier.Value), + _ => throw new Exception($"Unexpected token type {token.GetType().Name}") + }; + } + + private BlockNode ParseBlock() + { + ExpectSymbol(Symbol.OpenBrace); + List statements = []; + while (!TryExpectSymbol(Symbol.CloseBrace)) + { + statements.Add(ParseStatement()); + } + + return new BlockNode(statements); + } + + private Type ParseType() + { + var name = ExpectIdentifier(); + return new Type(name.Value); } private Token ExpectToken() @@ -36,6 +149,7 @@ public class Parser throw new Exception("Reached end of tokens"); } + Next(); return token.Value; } @@ -46,9 +160,10 @@ public class Parser { throw new Exception($"Expected {nameof(SymbolToken)} but got {token.GetType().Name}"); } + return symbol; } - + private void ExpectSymbol(Symbol symbol) { var token = ExpectSymbol(); @@ -58,6 +173,13 @@ public class Parser } } + 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(); @@ -65,9 +187,10 @@ public class Parser { throw new Exception($"Expected {nameof(IdentifierToken)} but got {token.GetType().Name}"); } + return identifier; } - + private LiteralToken ExpectLiteral() { var token = ExpectToken(); @@ -75,19 +198,25 @@ public class Parser { throw new Exception($"Expected {nameof(LiteralToken)} but got {token.GetType().Name}"); } + return literal; } - + private Optional Peek() { + while (_index < _tokens.Length && _tokens[_index] is SymbolToken { Symbol: Symbol.Whitespace }) + { + Next(); + } + if (_index < _tokens.Length) { return _tokens[_index]; } - + return Optional.Empty(); } - + private void Next() { _index++; diff --git a/Nub.Lang/Nub.Lang/Parsing/SyscallNode.cs b/Nub.Lang/Nub.Lang/Parsing/SyscallNode.cs index 34223d0..7d7ca5f 100644 --- a/Nub.Lang/Nub.Lang/Parsing/SyscallNode.cs +++ b/Nub.Lang/Nub.Lang/Parsing/SyscallNode.cs @@ -1,11 +1,6 @@ namespace Nub.Lang.Parsing; -public class ESyscallNode(IEnumerable parameters) : ExpressionNode +public class SyscallNode(IEnumerable parameters) : StatementNode { - public IEnumerable Parameters { get; } = parameters; -} - -public class SSyscallNode(IEnumerable parameters) : StatementNode -{ - public IEnumerable Parameters { get; } = parameters; + public IEnumerable Parameters { get; } = parameters; } \ No newline at end of file diff --git a/Nub.Lang/Nub.Lang/Program.cs b/Nub.Lang/Nub.Lang/Program.cs index 35d3617..b200c9c 100644 --- a/Nub.Lang/Nub.Lang/Program.cs +++ b/Nub.Lang/Nub.Lang/Program.cs @@ -1,6 +1,12 @@ using Nub.Lang.Lexing; +using Nub.Lang.Parsing; var src = File.ReadAllText(args[0]); var lexer = new Lexer(src); -var tokens = lexer.Lex(); \ No newline at end of file +var tokens = lexer.Lex(); + +var parser = new Parser(tokens); +var definitions = parser.Parse(); + +Console.Read(); \ No newline at end of file