From 9b4608a259891ddefdcd47a875c04f25fcb75a29 Mon Sep 17 00:00:00 2001 From: nub31 Date: Sun, 26 Jan 2025 23:20:16 +0100 Subject: [PATCH] Add basic binary expression support Current implementation is placeholder. Assembly is generated by mr. chat and is currently unchecked. Also only 64 bit ints are supported --- Nub.Lang/Nub.Lang/Generation/Generator.cs | 84 +++++++++++++++++++++ Nub.Lang/Nub.Lang/Input/program.nub | 5 +- Nub.Lang/Nub.Lang/Lexing/SymbolToken.cs | 2 +- Nub.Lang/Nub.Lang/Parsing/Parser.cs | 82 +++++++++++++++++++- Nub.Lang/Nub.Lang/Typing/ExpressionTyper.cs | 35 +++++++++ 5 files changed, 201 insertions(+), 7 deletions(-) diff --git a/Nub.Lang/Nub.Lang/Generation/Generator.cs b/Nub.Lang/Nub.Lang/Generation/Generator.cs index 5dbe862..0faf88f 100644 --- a/Nub.Lang/Nub.Lang/Generation/Generator.cs +++ b/Nub.Lang/Nub.Lang/Generation/Generator.cs @@ -190,6 +190,9 @@ public class Generator { switch (expression) { + case BinaryExpressionNode binaryExpression: + GenerateBinaryExpression(binaryExpression, func); + break; case FuncCallExpressionNode funcCallExpression: GenerateFuncCall(funcCallExpression.FuncCall, func); break; @@ -210,6 +213,87 @@ public class Generator } } + private void GenerateBinaryExpression(BinaryExpressionNode binaryExpression, Func func) + { + GenerateExpression(binaryExpression.Left, func); + _builder.AppendLine(" push rax"); + + GenerateExpression(binaryExpression.Right, func); + _builder.AppendLine(" pop rbx"); + + switch (binaryExpression.Operator) + { + + case BinaryExpressionOperator.Plus: + { + _builder.AppendLine(" add rax, rbx"); + break; + } + case BinaryExpressionOperator.Minus: + { + _builder.AppendLine(" sub rax, rbx"); + break; + } + case BinaryExpressionOperator.Multiply: + { + _builder.AppendLine(" imul rax, rbx"); + break; + } + case BinaryExpressionOperator.Divide: + { + _builder.AppendLine(" xor rdx, rdx"); + _builder.AppendLine(" div rbx"); + break; + } + case BinaryExpressionOperator.Equal: + { + _builder.AppendLine(" cmp rax, rbx"); + _builder.AppendLine(" sete al"); + _builder.AppendLine(" movzx rax, al"); + break; + } + case BinaryExpressionOperator.NotEqual: + { + _builder.AppendLine(" cmp rax, rbx"); + _builder.AppendLine(" setne al"); + _builder.AppendLine(" movzx rax, al"); + break; + } + case BinaryExpressionOperator.GreaterThan: + { + _builder.AppendLine(" cmp rax, rbx"); + _builder.AppendLine(" setg al"); + _builder.AppendLine(" movzx rax, al"); + break; + } + case BinaryExpressionOperator.GreaterThanOrEqual: + { + _builder.AppendLine(" cmp rax, rbx"); + _builder.AppendLine(" setge al"); + _builder.AppendLine(" movzx rax, al"); + break; + } + case BinaryExpressionOperator.LessThan: + { + _builder.AppendLine(" cmp rax, rbx"); + _builder.AppendLine(" setl al"); + _builder.AppendLine(" movzx rax, al"); + break; + } + case BinaryExpressionOperator.LessThanOrEqual: + { + _builder.AppendLine(" cmp rax, rbx"); + _builder.AppendLine(" setle al"); + _builder.AppendLine(" movzx rax, al"); + break; + } + default: + { + throw new ArgumentOutOfRangeException(nameof(binaryExpression.Operator), binaryExpression.Operator, null); + } + } + } + private void GenerateIdentifier(IdentifierNode identifier, Func func) { var variable = func.ResolveVariable(identifier.Identifier); diff --git a/Nub.Lang/Nub.Lang/Input/program.nub b/Nub.Lang/Nub.Lang/Input/program.nub index 9e7616a..87ebb73 100644 --- a/Nub.Lang/Nub.Lang/Input/program.nub +++ b/Nub.Lang/Nub.Lang/Input/program.nub @@ -4,10 +4,7 @@ let STD_OUT = 1; let STD_ERR = 2; func main() { - let x = "test\n"; - write(x); - x = "uwu\n"; - write(x); + let x = 5 * 5 + 5; } func write(msg: String) { diff --git a/Nub.Lang/Nub.Lang/Lexing/SymbolToken.cs b/Nub.Lang/Nub.Lang/Lexing/SymbolToken.cs index 29f8ce6..2910f2f 100644 --- a/Nub.Lang/Nub.Lang/Lexing/SymbolToken.cs +++ b/Nub.Lang/Nub.Lang/Lexing/SymbolToken.cs @@ -22,8 +22,8 @@ public enum Symbol Comma, Period, Assign, - Equal, Bang, + Equal, NotEqual, LessThan, LessThanOrEqual, diff --git a/Nub.Lang/Nub.Lang/Parsing/Parser.cs b/Nub.Lang/Nub.Lang/Parsing/Parser.cs index 332be87..99b4632 100644 --- a/Nub.Lang/Nub.Lang/Parsing/Parser.cs +++ b/Nub.Lang/Nub.Lang/Parsing/Parser.cs @@ -1,4 +1,5 @@ -using Nub.Lang.Lexing; +using System.Diagnostics.CodeAnalysis; +using Nub.Lang.Lexing; using Nub.Lib; namespace Nub.Lang.Parsing; @@ -156,7 +157,84 @@ public class Parser } } - private ExpressionNode ParseExpression() + 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(); return token switch diff --git a/Nub.Lang/Nub.Lang/Typing/ExpressionTyper.cs b/Nub.Lang/Nub.Lang/Typing/ExpressionTyper.cs index 1e2644c..c5617ec 100644 --- a/Nub.Lang/Nub.Lang/Typing/ExpressionTyper.cs +++ b/Nub.Lang/Nub.Lang/Typing/ExpressionTyper.cs @@ -115,6 +115,9 @@ public class ExpressionTyper { switch (expression) { + case BinaryExpressionNode binaryExpression: + PopulateBinaryExpression(binaryExpression); + break; case FuncCallExpressionNode funcCall: PopulateFuncCallExpression(funcCall); break; @@ -135,6 +138,38 @@ public class ExpressionTyper } } + private void PopulateBinaryExpression(BinaryExpressionNode binaryExpression) + { + PopulateExpression(binaryExpression.Left); + PopulateExpression(binaryExpression.Right); + switch (binaryExpression.Operator) + { + case BinaryExpressionOperator.Equal: + case BinaryExpressionOperator.NotEqual: + case BinaryExpressionOperator.GreaterThan: + case BinaryExpressionOperator.GreaterThanOrEqual: + case BinaryExpressionOperator.LessThan: + case BinaryExpressionOperator.LessThanOrEqual: + { + binaryExpression.Type = new PrimitiveType(PrimitiveTypeKind.Bool); + break; + } + case BinaryExpressionOperator.Plus: + case BinaryExpressionOperator.Minus: + case BinaryExpressionOperator.Multiply: + case BinaryExpressionOperator.Divide: + { + // TODO: Add change if int8, int16, int32, float or double + binaryExpression.Type = new PrimitiveType(PrimitiveTypeKind.Int64); + break; + } + default: + { + throw new ArgumentOutOfRangeException(nameof(binaryExpression.Operator)); + } + } + } + private void PopulateFuncCallExpression(FuncCallExpressionNode funcCall) { foreach (var parameter in funcCall.FuncCall.Parameters)