diff --git a/Nub.Lang/Nub.Lang/Generation/Generator.cs b/Nub.Lang/Nub.Lang/Generation/Generator.cs index 5be3248..3d890a9 100644 --- a/Nub.Lang/Nub.Lang/Generation/Generator.cs +++ b/Nub.Lang/Nub.Lang/Generation/Generator.cs @@ -13,6 +13,7 @@ public class Generator private readonly Dictionary _strings; private readonly HashSet _externFuncDefinitions; private int _stringIndex; + private int _labelIndex; public Generator(IReadOnlyCollection definitions) { @@ -144,6 +145,9 @@ public class Generator case FuncCallStatementNode funcCallStatement: GenerateFuncCall(funcCallStatement.FuncCall, func); break; + case IfNode ifStatement: + GenerateIf(ifStatement, func); + break; case ReturnNode @return: GenerateReturn(@return, func); break; @@ -161,6 +165,33 @@ public class Generator } } + private void GenerateIf(IfNode ifStatement, LocalFunc func) + { + var endLabel = CreateLabel(); + GenerateIf(ifStatement, endLabel, func); + _builder.AppendLine($"{endLabel}:"); + } + + private void GenerateIf(IfNode ifStatement, string endLabel, LocalFunc func) + { + var nextLabel = CreateLabel(); + GenerateExpression(ifStatement.Condition, func); + _builder.AppendLine(" cmp rax, 0"); + _builder.AppendLine($" je {nextLabel}"); + GenerateBlock(ifStatement.Body, func); + _builder.AppendLine($" jmp {endLabel}"); + _builder.AppendLine($"{nextLabel}:"); + + if (ifStatement.Else.HasValue) + { + ifStatement.Else.Value.Match + ( + elseIfStatement => GenerateIf(elseIfStatement, endLabel, func), + elseStatement => GenerateBlock(elseStatement, func) + ); + } + } + private void GenerateReturn(ReturnNode @return, LocalFunc func) { if (@return.Value.HasValue) @@ -474,4 +505,9 @@ public class Generator _builder.AppendLine(" syscall"); } + + private string CreateLabel() + { + return $"label{++_labelIndex}"; + } } \ No newline at end of file diff --git a/Nub.Lang/Nub.Lang/Generation/SymbolTable.cs b/Nub.Lang/Nub.Lang/Generation/SymbolTable.cs index 61ad307..88615fb 100644 --- a/Nub.Lang/Nub.Lang/Generation/SymbolTable.cs +++ b/Nub.Lang/Nub.Lang/Generation/SymbolTable.cs @@ -21,11 +21,37 @@ public class SymbolTable public void DefineFunc(ExternFuncDefinitionNode externFuncDefinition) { + var existing = _funcDefinitions + .FirstOrDefault(f => f + .SignatureMatches + ( + externFuncDefinition.Name, + externFuncDefinition.Parameters.Select(p => p.Type).ToList() + )); + + if (existing != null) + { + throw new Exception($"Func {existing} is already defined"); + } + _funcDefinitions.Add(new ExternFunc(externFuncDefinition.Name, externFuncDefinition.Name, externFuncDefinition.Parameters, externFuncDefinition.ReturnType)); } public void DefineFunc(LocalFuncDefinitionNode localFuncDefinition) { + var existing = _funcDefinitions + .FirstOrDefault(f => f + .SignatureMatches + ( + localFuncDefinition.Name, + localFuncDefinition.Parameters.Select(p => p.Type).ToList() + )); + + if (existing != null) + { + throw new Exception($"Func {existing} is already defined"); + } + var startLabel = $"func{++_labelIndex}"; var endLabel = $"func_end{_labelIndex}"; _funcDefinitions.Add(new LocalFunc(localFuncDefinition.Name, startLabel, endLabel, localFuncDefinition.Parameters, localFuncDefinition.ReturnType, _globalVariables.Concat(ResolveFuncVariables(localFuncDefinition)).ToList())); @@ -56,7 +82,7 @@ public class SymbolTable public Func ResolveFunc(string name, IReadOnlyCollection parameterTypes) { - var func = _funcDefinitions.FirstOrDefault(f => f.Name == name && f.Parameters.Count == parameterTypes.Count && f.Parameters.Where((p, i) => p.Type == parameterTypes.ElementAt(i)).Count() == parameterTypes.Count); + var func = _funcDefinitions.FirstOrDefault(f => f.SignatureMatches(name, parameterTypes)); if (func == null) { throw new Exception($"Func {name}({string.Join(", ", parameterTypes)}) is not defined"); @@ -115,7 +141,7 @@ public class GlobalVariable(string name, Type type, string identifier) : Variabl public abstract class Func { - public Func(string name, string startLabel, IReadOnlyCollection parameters, Optional returnType) + protected Func(string name, string startLabel, IReadOnlyCollection parameters, Optional returnType) { Name = name; Parameters = parameters; @@ -127,6 +153,13 @@ public abstract class Func public string StartLabel { get; } public IReadOnlyCollection Parameters { get; } public Optional ReturnType { get; } + + public bool SignatureMatches(string name, IReadOnlyCollection parameterTypes) + { + return Name == name + && Parameters.Count == parameterTypes.Count + && Parameters.Where((p, i) => p.Type == parameterTypes.ElementAt(i)).Count() == parameterTypes.Count; + } } public class ExternFunc : Func diff --git a/Nub.Lang/Nub.Lang/Input/program.nub b/Nub.Lang/Nub.Lang/Input/program.nub index 599b0b5..c7a656d 100644 --- a/Nub.Lang/Nub.Lang/Input/program.nub +++ b/Nub.Lang/Nub.Lang/Input/program.nub @@ -4,12 +4,45 @@ let STD_OUT = 1; let STD_ERR = 2; func main() { - print("test\n"); + println("test"); + println(true); + + if true { + println("1"); + } else if false { + println("2"); + } else if true { + println("3"); + } else { + println("4"); + } } func print(msg: String) { syscall(SYS_WRITE, STD_OUT, msg, strlen(msg)); } +func print(value: bool) { + if value { + print("true"); + } else { + print("false"); + } +} + +func println() { + print("\n"); +} + +func println(msg: String) { + print(msg); + println(); +} + +func println(value: bool) { + print(value); + println(); +} + extern func strlen(msg: String): int64; extern func strcmp(a: String, b: String): bool; diff --git a/Nub.Lang/Nub.Lang/Lexing/Lexer.cs b/Nub.Lang/Nub.Lang/Lexing/Lexer.cs index 15befca..dd8065d 100644 --- a/Nub.Lang/Nub.Lang/Lexing/Lexer.cs +++ b/Nub.Lang/Nub.Lang/Lexing/Lexer.cs @@ -10,6 +10,8 @@ public class Lexer ["extern"] = Symbol.Extern, ["return"] = Symbol.Return, ["let"] = Symbol.Let, + ["if"] = Symbol.If, + ["else"] = Symbol.Else, }; private static readonly Dictionary Chians = new() @@ -80,6 +82,11 @@ public class Lexer { return new SymbolToken(keywordSymbol); } + + if (buffer is "true" or "false") + { + return new LiteralToken(new PrimitiveType(PrimitiveTypeKind.Bool), buffer); + } return new IdentifierToken(buffer); } diff --git a/Nub.Lang/Nub.Lang/Lexing/SymbolToken.cs b/Nub.Lang/Nub.Lang/Lexing/SymbolToken.cs index e67c07f..d284b8a 100644 --- a/Nub.Lang/Nub.Lang/Lexing/SymbolToken.cs +++ b/Nub.Lang/Nub.Lang/Lexing/SymbolToken.cs @@ -12,6 +12,8 @@ public enum Symbol Func, Return, Let, + If, + Else, Semicolon, Colon, OpenParen, diff --git a/Nub.Lang/Nub.Lang/Nub.Lang.csproj b/Nub.Lang/Nub.Lang/Nub.Lang.csproj index 31b8260..4832d7a 100644 --- a/Nub.Lang/Nub.Lang/Nub.Lang.csproj +++ b/Nub.Lang/Nub.Lang/Nub.Lang.csproj @@ -8,7 +8,7 @@ - + diff --git a/Nub.Lang/Nub.Lang/Parsing/IfNode.cs b/Nub.Lang/Nub.Lang/Parsing/IfNode.cs new file mode 100644 index 0000000..40d9f03 --- /dev/null +++ b/Nub.Lang/Nub.Lang/Parsing/IfNode.cs @@ -0,0 +1,10 @@ +using Nub.Core; + +namespace Nub.Lang.Parsing; + +public class IfNode(ExpressionNode condition, BlockNode body, Optional> @else) : StatementNode +{ + public ExpressionNode Condition { get; } = condition; + public BlockNode Body { get; } = body; + public Optional> Else { get; } = @else; +} \ 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 0f13196..2145779 100644 --- a/Nub.Lang/Nub.Lang/Parsing/Parser.cs +++ b/Nub.Lang/Nub.Lang/Parsing/Parser.cs @@ -154,22 +154,15 @@ public class Parser { case Symbol.Return: { - var value = Optional.Empty(); - if (!TryExpectSymbol(Symbol.Semicolon)) - { - value = ParseExpression(); - ExpectSymbol(Symbol.Semicolon); - } - - return new ReturnNode(value); + return ParseReturn(); } case Symbol.Let: { - var name = ExpectIdentifier().Value; - ExpectSymbol(Symbol.Assign); - var value = ParseExpression(); - ExpectSymbol(Symbol.Semicolon); - return new VariableAssignmentNode(name, value); + return ParseVariableAssignment(); + } + case Symbol.If: + { + return ParseIf(); } default: { @@ -184,6 +177,44 @@ public class Parser } } + private ReturnNode ParseReturn() + { + var value = Optional.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>.Empty(); + if (TryExpectSymbol(Symbol.Else)) + { + elseStatement = TryExpectSymbol(Symbol.If) + ? (Variant)ParseIf() + : (Variant)ParseBlock(); + } + + return new IfNode(condition, body, elseStatement); + } + private ExpressionNode ParseExpression(int precedence = 0) { var left = ParsePrimaryExpression(); diff --git a/Nub.Lang/Nub.Lang/Typing/ExpressionTyper.cs b/Nub.Lang/Nub.Lang/Typing/ExpressionTyper.cs index cc00042..a2ff298 100644 --- a/Nub.Lang/Nub.Lang/Typing/ExpressionTyper.cs +++ b/Nub.Lang/Nub.Lang/Typing/ExpressionTyper.cs @@ -82,6 +82,9 @@ public class ExpressionTyper case FuncCallStatementNode funcCall: PopulateFuncCallStatement(funcCall); break; + case IfNode ifStatement: + PopulateIf(ifStatement); + break; case ReturnNode returnNode: PopulateReturn(returnNode); break; @@ -107,6 +110,20 @@ public class ExpressionTyper } } + private void PopulateIf(IfNode ifStatement) + { + PopulateExpression(ifStatement.Condition); + PopulateBlock(ifStatement.Body); + if (ifStatement.Else.HasValue) + { + ifStatement.Else.Value.Match + ( + PopulateIf, + PopulateBlock + ); + } + } + private void PopulateSyscallStatement(SyscallStatementNode syscall) { foreach (var parameter in syscall.Syscall.Parameters)