Add arrays

This commit is contained in:
nub31
2025-01-30 16:33:06 +01:00
parent 8a50278f0f
commit a4404a4fe2
15 changed files with 545 additions and 34 deletions

View File

@@ -6,7 +6,8 @@ namespace Nub.Lang.Backend.Custom;
public class Generator
{
private const string Entrypoint = "main";
private const bool ZeroBasedIndexing = false;
private readonly List<DefinitionNode> _definitions;
private readonly SymbolTable _symbolTable;
private readonly StringBuilder _builder;
@@ -69,6 +70,10 @@ public class Generator
}
_builder.AppendLine("""
array_out_of_bounds:
mov rax, 60
mov rdi, 69
syscall
strcmp:
xor rdx, rdx
@@ -204,6 +209,9 @@ public class Generator
{
switch (statement)
{
case ArrayIndexAssignmentNode arrayIndexAssignment:
GenerateArrayIndexAssignment(arrayIndexAssignment, func);
break;
case FuncCallStatementNode funcCallStatement:
GenerateFuncCall(funcCallStatement.FuncCall, func);
break;
@@ -230,6 +238,15 @@ public class Generator
}
}
private void GenerateArrayIndexAssignment(ArrayIndexAssignmentNode arrayIndexAssignment, LocalFunc func)
{
GenerateExpression(arrayIndexAssignment.Value, func);
_builder.AppendLine(" push rax");
GenerateArrayIndexPointerAccess(arrayIndexAssignment.Identifier, arrayIndexAssignment.Index, func);
_builder.AppendLine(" pop rdx");
_builder.AppendLine(" mov [rax], rdx");
}
private void GenerateIf(IfNode ifStatement, LocalFunc func)
{
var endLabel = _labelFactory.Create();
@@ -298,6 +315,12 @@ public class Generator
{
switch (expression)
{
case ArrayIndexAccessNode arrayIndexAccess:
GenerateArrayIndexAccess(arrayIndexAccess, func);
break;
case ArrayInitializerNode arrayInitializer:
GenerateArrayInitializer(arrayInitializer, func);
break;
case BinaryExpressionNode binaryExpression:
GenerateBinaryExpression(binaryExpression, func);
break;
@@ -318,6 +341,28 @@ public class Generator
}
}
private void GenerateArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccess, LocalFunc func)
{
GenerateArrayIndexPointerAccess(arrayIndexAccess.Identifier, arrayIndexAccess.Index, func);
_builder.AppendLine(" mov rax, [rax]");
}
private void GenerateArrayInitializer(ArrayInitializerNode arrayInitializer, LocalFunc func)
{
_builder.AppendLine($"""
mov rax, 9
mov rdi, 0
mov rsi, {8 + arrayInitializer.Length * 8}
mov rdx, 3
mov r10, 34
mov r8, -1
mov r9, 0
syscall
mov QWORD [rax], {arrayInitializer.Length}
""");
}
private void GenerateBinaryExpression(BinaryExpressionNode binaryExpression, LocalFunc func)
{
GenerateExpression(binaryExpression.Left, func);
@@ -576,4 +621,31 @@ public class Generator
_builder.AppendLine(" syscall");
}
private void GenerateArrayIndexPointerAccess(IdentifierNode identifier, ExpressionNode index, LocalFunc func)
{
GenerateExpression(index, func);
_builder.AppendLine(" push rax");
GenerateIdentifier(identifier, func);
_builder.AppendLine(" pop rbx");
// rcx now holds the length of the array which we can use to check bounds
_builder.AppendLine(" mov rcx, [rax]");
_builder.AppendLine(" cmp rbx, rcx");
if (ZeroBasedIndexing)
{
_builder.AppendLine(" jge array_out_of_bounds");
_builder.AppendLine(" cmp rbx, 0");
}
else
{
_builder.AppendLine(" jg array_out_of_bounds");
_builder.AppendLine(" cmp rbx, 1");
}
_builder.AppendLine(" jl array_out_of_bounds");
_builder.AppendLine(" inc rbx");
_builder.AppendLine(" shl rbx, 3");
_builder.AppendLine(" add rax, rbx");
}
}

View File

@@ -7,7 +7,7 @@ public class SymbolTable
{
private readonly List<Func> _funcDefinitions = [];
private readonly List<GlobalVariable> _globalVariables = [];
private LabelFactory _labelFactory;
private readonly LabelFactory _labelFactory;
public readonly Dictionary<string, string> Strings = [];

View File

@@ -14,6 +14,7 @@ public class Lexer
["else"] = Symbol.Else,
["while"] = Symbol.While,
["return"] = Symbol.Return,
["new"] = Symbol.New,
};
private static readonly Dictionary<char[], Symbol> Chians = new()

View File

@@ -38,4 +38,5 @@ public enum Symbol
Minus,
Star,
ForwardSlash,
New,
}

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

@@ -4,8 +4,5 @@ public class IdentifierNode(string identifier) : ExpressionNode
{
public string Identifier { get; } = identifier;
public override string ToString()
{
return Identifier;
}
public override string ToString() => Identifier;
}

View File

@@ -149,6 +149,15 @@ public class Parser
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();
@@ -310,10 +319,32 @@ public class Parser
return new LiteralNode(literal.Value, literal.Type);
case IdentifierToken identifier:
return ParseExpressionIdentifier(identifier);
case SymbolToken { Symbol: Symbol.OpenParen }:
var expression = ParseExpression();
ExpectSymbol(Symbol.CloseParen);
return expression;
case SymbolToken symbolToken:
{
switch (symbolToken.Symbol)
{
case Symbol.OpenParen:
{
var expression = ParseExpression();
ExpectSymbol(Symbol.CloseParen);
return expression;
}
case Symbol.New:
{
var type = ParseType();
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);
}
default:
throw new Exception($"Unknown symbol: {symbolToken.Symbol}");
}
}
default:
throw new Exception($"Unexpected token type {token.GetType().Name}");
}
@@ -321,6 +352,13 @@ public class Parser
private ExpressionNode ParseExpressionIdentifier(IdentifierToken identifier)
{
if (TryExpectSymbol(Symbol.OpenBracket))
{
var index = ParseExpression();
ExpectSymbol(Symbol.CloseBracket);
return new ArrayIndexAccessNode(new IdentifierNode(identifier.Value), index);
}
if (TryExpectSymbol(Symbol.OpenParen))
{
List<ExpressionNode> parameters = [];
@@ -356,30 +394,42 @@ public class Parser
private Type ParseType()
{
var name = ExpectIdentifier().Value;
if (name == "Func")
switch (name)
{
List<Type> typeArguments = [];
if (TryExpectSymbol(Symbol.LessThan))
case "Func":
{
while (!TryExpectSymbol(Symbol.GreaterThan))
List<Type> typeArguments = [];
if (TryExpectSymbol(Symbol.LessThan))
{
var type = ParseType();
typeArguments.Add(type);
TryExpectSymbol(Symbol.Comma);
while (!TryExpectSymbol(Symbol.GreaterThan))
{
var type = ParseType();
typeArguments.Add(type);
TryExpectSymbol(Symbol.Comma);
}
}
var returnType = Optional<Type>.OfNullable(typeArguments.LastOrDefault());
return new DelegateType(typeArguments.Take(typeArguments.Count - 1).ToList(), returnType);
}
case "String":
{
return new StringType();
}
case "Array":
{
ExpectSymbol(Symbol.LessThan);
var innerType = ParseType();
ExpectSymbol(Symbol.GreaterThan);
return new ArrayType(innerType);
}
default:
{
return PrimitiveType.Parse(name);
}
var returnType = Optional<Type>.OfNullable(typeArguments.LastOrDefault());
return new DelegateType(typeArguments.Take(typeArguments.Count - 1).ToList(), returnType);
}
if (name == "String")
{
return new StringType();
}
return PrimitiveType.Parse(name);
}
private Token ExpectToken()

View File

@@ -83,6 +83,9 @@ public class ExpressionTyper
{
switch (statement)
{
case ArrayIndexAssignmentNode arrayIndexAssignment:
PopulateArrayIndexAssignment(arrayIndexAssignment);
break;
case FuncCallStatementNode funcCall:
PopulateFuncCallStatement(funcCall);
break;
@@ -109,6 +112,13 @@ public class ExpressionTyper
}
}
private void PopulateArrayIndexAssignment(ArrayIndexAssignmentNode arrayIndexAssignment)
{
PopulateIdentifier(arrayIndexAssignment.Identifier);
PopulateExpression(arrayIndexAssignment.Index);
PopulateExpression(arrayIndexAssignment.Value);
}
private void PopulateFuncCallStatement(FuncCallStatementNode funcCall)
{
foreach (var parameter in funcCall.FuncCall.Parameters)
@@ -168,6 +178,12 @@ public class ExpressionTyper
{
switch (expression)
{
case ArrayIndexAccessNode arrayIndexAccess:
PopulateArrayIndexAccess(arrayIndexAccess);
break;
case ArrayInitializerNode arrayInitializer:
PopulateArrayInitializer(arrayInitializer);
break;
case BinaryExpressionNode binaryExpression:
PopulateBinaryExpression(binaryExpression);
break;
@@ -188,6 +204,30 @@ public class ExpressionTyper
}
}
private void PopulateArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccess)
{
PopulateExpression(arrayIndexAccess.Index);
PopulateIdentifier(arrayIndexAccess.Identifier);
var variable = _variables.FirstOrDefault(v => v.Name == arrayIndexAccess.Identifier.Identifier);
if (variable == null)
{
throw new Exception($"Variable {arrayIndexAccess.Identifier} is not defined");
}
if (variable.Type is not ArrayType arrayType)
{
throw new Exception($"Variable {arrayIndexAccess.Identifier} is not an array type");
}
arrayIndexAccess.Type = arrayType.InnerType;
}
private void PopulateArrayInitializer(ArrayInitializerNode arrayInitializer)
{
arrayInitializer.Type = arrayInitializer.InnerType;
}
private void PopulateBinaryExpression(BinaryExpressionNode binaryExpression)
{
PopulateExpression(binaryExpression.Left);

View File

@@ -41,6 +41,18 @@ public record StringType : Type
public override string ToString() => "String";
}
public record ArrayType : Type
{
public ArrayType(Type innerType)
{
InnerType = innerType;
}
public Type InnerType { get; }
public override string ToString() => $"{InnerType}";
}
public record DelegateType : Type
{
public DelegateType(List<Type> parameters, Optional<Type> returnType)