Refactor statements

This commit is contained in:
nub31
2025-06-02 15:27:19 +02:00
parent 9f7bc4d2d5
commit 96ad3d2677
36 changed files with 354 additions and 230 deletions

View File

@@ -7,6 +7,6 @@ export func main(args: []^string) {
while i < args.count {
c::printf("%s\n", args[i])
i += 1
i = i + 1
}
}

View File

@@ -8,7 +8,7 @@ namespace Nub.Lang.Backend;
public class Generator
{
private const string OutOfBoundsMessage = "Index is out of bounds\\n";
private List<SourceFile> _sourceFiles = [];
private StringBuilder _builder = new();
private Dictionary<string, Variable> _variables = [];
@@ -34,13 +34,13 @@ public class Generator
_funcIndex = 0;
_labelIndex = 0;
_codeIsReachable = true;
foreach (var structDef in _sourceFiles.SelectMany(f => f.Definitions).OfType<StructDefinitionNode>())
{
GenerateStructDefinition(structDef);
_builder.AppendLine();
}
foreach (var funcSignature in _sourceFiles.SelectMany(f => f.Definitions).OfType<IFuncSignature>())
{
switch (funcSignature)
@@ -58,6 +58,7 @@ public class Generator
var funcName = GenFuncName();
_funcNames[funcSignature] = funcName;
}
break;
default:
throw new ArgumentOutOfRangeException(nameof(funcSignature));
@@ -77,7 +78,7 @@ public class Generator
var str = _strings[i];
_builder.AppendLine($"data $str{i + 1} = {{ b \"{str}\", b 0 }}");
}
return _builder.ToString();
}
@@ -367,21 +368,27 @@ public class Generator
{
switch (statement)
{
case ArrayIndexAssignmentNode arrayIndexAssignment:
throw new NotImplementedException();
break;
case BreakNode:
GenerateBreak();
break;
case ContinueNode:
GenerateContinue();
break;
case FuncCallStatementNode funcCallStatement:
GenerateStatementFuncCall(funcCallStatement);
break;
case IfNode ifStatement:
GenerateIf(ifStatement);
break;
case MemberAssignmentNode memberAssignment:
throw new NotImplementedException();
break;
case ReturnNode @return:
GenerateReturn(@return);
break;
case StatementExpressionNode statementExpression:
GenerateExpression(statementExpression.Expression);
break;
case VariableAssignmentNode variableAssignment:
GenerateVariableAssignment(variableAssignment);
break;
@@ -396,49 +403,6 @@ public class Generator
}
}
private string GenerateFuncCall(FuncCall funcCall)
{
var funcDefinition = LookupFuncSignature(funcCall.Namespace, funcCall.Name, funcCall.Parameters.Select(p => p.Type).ToList());
if (funcDefinition == null)
{
throw new Exception($"Unknown function {funcCall}");
}
var parameterStrings = new List<string>();
for (var i = 0; i < funcCall.Parameters.Count; i++)
{
if (i < funcDefinition.Parameters.Count && funcDefinition.Parameters[i].Variadic)
{
parameterStrings.Add("...");
}
NubType expectedType;
if (i < funcDefinition.Parameters.Count)
{
expectedType = funcDefinition.Parameters[i].Type;
}
else if (funcDefinition.Parameters[^1].Variadic)
{
expectedType = funcDefinition.Parameters[^1].Type;
}
else
{
throw new Exception($"Parameters for func {funcCall} does not not match");
}
var parameter = funcCall.Parameters[i];
var result = GenerateExpression(parameter);
var qbeParameterType = SQT(expectedType.Equals(NubPrimitiveType.Any) ? parameter.Type : expectedType);
parameterStrings.Add($"{qbeParameterType} {result}");
}
var funcName = _funcNames[funcDefinition];
return $"call ${funcName}({string.Join(", ", parameterStrings)})";
}
private void GenerateBlock(BlockNode block)
{
foreach (var statement in block.Statements.Where(_ => _codeIsReachable))
@@ -461,12 +425,6 @@ public class Generator
_codeIsReachable = false;
}
private void GenerateStatementFuncCall(FuncCallStatementNode funcCall)
{
var call = GenerateFuncCall(funcCall.FuncCall);
_builder.AppendLine($" {call}");
}
private void GenerateIf(IfNode ifStatement)
{
var trueLabel = GenLabelName();
@@ -507,7 +465,7 @@ public class Generator
private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment)
{
var result = GenerateExpression(variableAssignment.Value);
_builder.AppendLine($" storel {result}, {_variables[variableAssignment.Name].Pointer}");
_builder.AppendLine($" storel {result}, {_variables[variableAssignment.Identifier.Identifier].Pointer}");
}
private void GenerateVariableDeclaration(VariableDeclarationNode variableDeclaration)
@@ -515,7 +473,7 @@ public class Generator
var pointerName = GenVarName();
var type = variableDeclaration.ExplicitType.Value ?? variableDeclaration.Value.Value?.Type!;
_builder.AppendLine($" %{pointerName} ={SQT(type)} alloc8 {QbeTypeSize(type)}");
if (variableDeclaration.Value.HasValue)
@@ -527,7 +485,7 @@ public class Generator
{
_builder.AppendLine($" storel 0, %{pointerName}");
}
_variables[variableDeclaration.Name] = new Variable
{
Pointer = $"%{pointerName}",
@@ -561,12 +519,12 @@ public class Generator
return expression switch
{
AddressOfNode addressOf => GenerateAddressOf(addressOf),
ArrayIndexNode arrayIndex => GenerateArrayIndex(arrayIndex),
ArrayIndexAccessNode arrayIndex => GenerateArrayIndex(arrayIndex),
ArrayInitializerNode arrayInitializer => GenerateArrayInitializer(arrayInitializer),
BinaryExpressionNode binaryExpression => GenerateBinaryExpression(binaryExpression),
CastNode cast => GenerateCast(cast),
DereferenceNode dereference => GenerateDereference(dereference),
FuncCallExpressionNode funcCallExpression => GenerateExpressionFuncCall(funcCallExpression),
FuncCallNode funcCallExpression => GenerateFuncCall(funcCallExpression),
IdentifierNode identifier => GenerateIdentifier(identifier),
LiteralNode literal => GenerateLiteral(literal),
StructInitializerNode structInitializer => GenerateStructInitializer(structInitializer),
@@ -576,34 +534,34 @@ public class Generator
};
}
private string GenerateArrayIndex(ArrayIndexNode arrayIndex)
private string GenerateArrayIndex(ArrayIndexAccessNode arrayIndexAccess)
{
var array = GenerateExpression(arrayIndex.Expression);
var index = GenerateExpression(arrayIndex.Index);
var array = GenerateExpression(arrayIndexAccess.Array);
var index = GenerateExpression(arrayIndexAccess.Index);
var arrayBaseType = ((NubArrayType)arrayIndexAccess.Array.Type).BaseType;
var arrayBaseType = ((NubArrayType)arrayIndex.Expression.Type).BaseType;
var countName = GenVarName();
_builder.AppendLine($" %{countName} =l loadl {array}");
var isNegativeName = GenVarName();
_builder.AppendLine($" %{isNegativeName} =w csltl {index}, 0");
var isOobName = GenVarName();
_builder.AppendLine($" %{isOobName} =w csgel {index}, %{countName}");
var anyOobName = GenVarName();
_builder.AppendLine($" %{anyOobName} =w or %{isNegativeName}, %{isOobName}");
var oobLabel = GenLabelName();
var notOobLabel = GenLabelName();
_builder.AppendLine($" jnz %{anyOobName}, @{oobLabel}, @{notOobLabel}");
_builder.AppendLine($"@{oobLabel}");
_builder.AppendLine($" call $nub_panic(l $oob_message, l {OutOfBoundsMessage.Length})");
_builder.AppendLine($"@{notOobLabel}");
// Calculate element address
var firstItemPointerName = GenVarName();
_builder.AppendLine($" %{firstItemPointerName} =l add {array}, 8");
@@ -611,7 +569,7 @@ public class Generator
_builder.AppendLine($" %{offsetPointerName} =l mul {index}, {QbeTypeSize(arrayBaseType)}");
var resultPointerName = GenVarName();
_builder.AppendLine($" %{resultPointerName} =l add %{firstItemPointerName}, %{offsetPointerName}");
// Load the value
var outputName = GenVarName();
_builder.AppendLine($" %{outputName} ={SQT(arrayBaseType)} load{SQT(arrayBaseType)} %{resultPointerName}");
@@ -628,11 +586,11 @@ public class Generator
var outputName = GenVarName();
_builder.AppendLine($" %{outputName} =l alloc8 %{totalArraySize}");
_builder.AppendLine($" storel {capacity}, %{outputName}");
var dataPtr = GenVarName();
_builder.AppendLine($" %{dataPtr} =l add %{outputName}, 8");
_builder.AppendLine($" call $nub_memset(l %{dataPtr}, w 0, l %{capacityInBytes})");
return $"%{outputName}";
}
@@ -1490,7 +1448,7 @@ public class Generator
{
throw new Exception($"Struct {structType.Name} is not defined");
}
var fieldIndex = -1;
for (var i = 0; i < structDefinition.Fields.Count; i++)
{
@@ -1515,14 +1473,54 @@ public class Generator
return $"%{outputName}";
}
}
throw new ArgumentOutOfRangeException(nameof(memberAccess.Expression.Type));
}
private string GenerateExpressionFuncCall(FuncCallExpressionNode funcCall)
private string GenerateFuncCall(FuncCallNode funcCall)
{
var outputName = GenVarName();
var call = GenerateFuncCall(funcCall.FuncCall);
var funcDefinition = LookupFuncSignature(funcCall.Namespace, funcCall.Name, funcCall.Parameters.Select(p => p.Type).ToList());
if (funcDefinition == null)
{
throw new Exception($"Unknown function {funcCall}");
}
var parameterStrings = new List<string>();
for (var i = 0; i < funcCall.Parameters.Count; i++)
{
if (i < funcDefinition.Parameters.Count && funcDefinition.Parameters[i].Variadic)
{
parameterStrings.Add("...");
}
NubType expectedType;
if (i < funcDefinition.Parameters.Count)
{
expectedType = funcDefinition.Parameters[i].Type;
}
else if (funcDefinition.Parameters[^1].Variadic)
{
expectedType = funcDefinition.Parameters[^1].Type;
}
else
{
throw new Exception($"Parameters for func {funcCall} does not not match");
}
var parameter = funcCall.Parameters[i];
var result = GenerateExpression(parameter);
var qbeParameterType = SQT(expectedType.Equals(NubPrimitiveType.Any) ? parameter.Type : expectedType);
parameterStrings.Add($"{qbeParameterType} {result}");
}
var funcName = _funcNames[funcDefinition];
var call = $"call ${funcName}({string.Join(", ", parameterStrings)})";
_builder.AppendLine($" %{outputName} ={SQT(funcCall.Type)} {call}");
return $"%{outputName}";
}
@@ -1531,12 +1529,12 @@ public class Generator
{
return $"v{++_variableIndex}";
}
private string GenFuncName()
{
return $"f{++_funcIndex}";
}
private string GenLabelName()
{
return $"l{++_labelIndex}";

View File

@@ -1,9 +0,0 @@
using Nub.Lang.Frontend.Lexing;
namespace Nub.Lang.Frontend.Parsing;
public class ArrayIndexNode(IReadOnlyList<Token> tokens, ExpressionNode expression, ExpressionNode index) : ExpressionNode(tokens)
{
public ExpressionNode Expression { get; } = expression;
public ExpressionNode Index { get; } = index;
}

View File

@@ -0,0 +1,9 @@
using Nub.Lang.Frontend.Lexing;
namespace Nub.Lang.Frontend.Parsing;
public class ArrayIndexAccessNode(IReadOnlyList<Token> tokens, ExpressionNode array, ExpressionNode index) : ExpressionNode(tokens)
{
public ExpressionNode Array { get; } = array;
public ExpressionNode Index { get; } = index;
}

View File

@@ -0,0 +1,12 @@
using Nub.Lang.Frontend.Lexing;
namespace Nub.Lang.Frontend.Parsing;
public class FuncCallNode(IReadOnlyList<Token> tokens, string @namespace, string name, List<ExpressionNode> parameters) : ExpressionNode(tokens)
{
public string Namespace { get; } = @namespace;
public string Name { get; } = name;
public List<ExpressionNode> Parameters { get; } = parameters;
public override string ToString() => $"{Name}()";
}

View File

@@ -1,10 +0,0 @@
namespace Nub.Lang.Frontend.Parsing;
public class FuncCall(string @namespace, string name, List<ExpressionNode> parameters)
{
public string Namespace { get; } = @namespace;
public string Name { get; } = name;
public List<ExpressionNode> Parameters { get; } = parameters;
public override string ToString() => $"{Name}()";
}

View File

@@ -1,10 +0,0 @@
using Nub.Lang.Frontend.Lexing;
namespace Nub.Lang.Frontend.Parsing;
public class FuncCallExpressionNode(IReadOnlyList<Token> tokens, FuncCall funcCall) : ExpressionNode(tokens)
{
public FuncCall FuncCall { get; } = funcCall;
public override string ToString() => FuncCall.ToString();
}

View File

@@ -1,10 +0,0 @@
using Nub.Lang.Frontend.Lexing;
namespace Nub.Lang.Frontend.Parsing;
public class FuncCallStatementNode(IReadOnlyList<Token> tokens, FuncCall funcCall) : StatementNode(tokens)
{
public FuncCall FuncCall { get; } = funcCall;
public override string ToString() => FuncCall.ToString();
}

View File

@@ -178,113 +178,183 @@ public class Parser
private StatementNode ParseStatement()
{
var startIndex = _index;
var token = ExpectToken();
switch (token)
if (!Peek().TryGetValue(out var token))
{
case IdentifierToken identifier:
throw new ParseException(Diagnostic
.Error("Unexpected end of file while parsing statement")
.At(_tokens.Last())
.Build());
}
if (token is SymbolToken symbol)
{
switch (symbol.Symbol)
{
return ParseStatementIdentifier(startIndex, identifier);
}
case SymbolToken symbol:
{
return symbol.Symbol switch
{
Symbol.Return => ParseReturn(startIndex),
Symbol.If => ParseIf(startIndex),
Symbol.While => ParseWhile(startIndex),
Symbol.Let => ParseVariableDeclaration(startIndex),
Symbol.Break => new BreakNode(GetTokensForNode(startIndex)),
Symbol.Continue => new ContinueNode(GetTokensForNode(startIndex)),
_ => throw new ParseException(Diagnostic
.Error($"Unexpected symbol '{symbol.Symbol}' at start of statement")
.WithHelp("Expected identifier, 'let', 'return', 'if', 'while', 'break', or 'continue'")
.At(symbol)
.Build())
};
}
default:
{
throw new ParseException(Diagnostic
.Error($"Unexpected token '{token.GetType().Name}' at start of statement")
.WithHelp("Statements must start with an identifier or keyword")
.At(token)
.Build());
case Symbol.Return:
return ParseReturn(startIndex);
case Symbol.If:
return ParseIf(startIndex);
case Symbol.While:
return ParseWhile(startIndex);
case Symbol.Let:
return ParseVariableDeclaration(startIndex);
case Symbol.Break:
return ParseBreak(startIndex);
case Symbol.Continue:
return ParseContinue(startIndex);
}
}
return ParseStatementExpression(startIndex);
}
private StatementNode ParseStatementIdentifier(int startIndex, IdentifierToken identifier)
private StatementNode ParseStatementExpression(int startIndex)
{
var symbol = ExpectSymbol();
var expr = ParseExpression();
if (TryGetBinaryOperator(symbol.Symbol, out var binaryOperator) && Peek().TryGetValue(out var next) && next is SymbolToken { Symbol: Symbol.Assign })
if (Peek().TryGetValue(out var token))
{
Next();
var left = new IdentifierNode(GetTokensForNode(startIndex), identifier.Value);
var right = ParseExpression();
var binOp = new BinaryExpressionNode(GetTokensForNode(startIndex), left, binaryOperator.Value, right);
return new VariableAssignmentNode(GetTokensForNode(startIndex), identifier.Value, binOp);
}
switch (symbol.Symbol)
{
case Symbol.DoubleColon:
if (token is SymbolToken symbol)
{
var name = ExpectIdentifier();
ExpectSymbol(Symbol.OpenParen);
var parameters = new List<ExpressionNode>();
while (!TryExpectSymbol(Symbol.CloseParen))
switch (symbol.Symbol)
{
parameters.Add(ParseExpression());
if (!TryExpectSymbol(Symbol.Comma) && Peek().TryGetValue(out var nextToken) && nextToken is not SymbolToken { Symbol: Symbol.CloseParen })
case Symbol.Assign:
{
_diagnostics.Add(Diagnostic
.Warning("Missing comma between function arguments")
.WithHelp("Add a ',' to separate arguments")
.At(nextToken)
.Build());
Next();
switch (expr)
{
case MemberAccessNode memberAccess:
{
var value = ParseExpression();
return new MemberAssignmentNode(GetTokensForNode(startIndex), memberAccess, value);
}
case ArrayIndexAccessNode arrayIndexAccess:
{
var value = ParseExpression();
return new ArrayIndexAssignmentNode(GetTokensForNode(startIndex), arrayIndexAccess, value);
}
case IdentifierNode identifier:
{
var value = ParseExpression();
return new VariableAssignmentNode(GetTokensForNode(startIndex), identifier, value);
}
}
break;
}
}
return new FuncCallStatementNode(GetTokensForNode(startIndex), new FuncCall(identifier.Value, name.Value, parameters));
}
case Symbol.OpenParen:
{
var parameters = new List<ExpressionNode>();
while (!TryExpectSymbol(Symbol.CloseParen))
{
parameters.Add(ParseExpression());
if (!TryExpectSymbol(Symbol.Comma) && Peek().TryGetValue(out var nextToken) && nextToken is not SymbolToken { Symbol: Symbol.CloseParen })
{
_diagnostics.Add(Diagnostic
.Warning("Missing comma between function arguments")
.WithHelp("Add a ',' to separate arguments")
.At(nextToken)
.Build());
}
}
return new FuncCallStatementNode(GetTokensForNode(startIndex), new FuncCall(_namespace, identifier.Value, parameters));
}
case Symbol.Assign:
{
var value = ParseExpression();
return new VariableAssignmentNode(GetTokensForNode(startIndex), identifier.Value, value);
}
default:
{
throw new ParseException(Diagnostic
.Error($"Unexpected symbol '{symbol.Symbol}' after identifier")
.WithHelp("Expected '(', '=', or '::' after identifier")
.At(symbol)
.Build());
}
}
return new StatementExpressionNode(GetTokensForNode(startIndex), expr);
}
// private StatementNode ParseStatementIdentifier(int startIndex)
// {
// var leftExpr = ParsePrimaryExpression();
//
// var symbol = ExpectSymbol();
//
// switch (symbol.Symbol)
// {
// case Symbol.DoubleColon:
// {
// if (leftExpr is not IdentifierNode namespaceNode)
// {
// throw new ParseException(Diagnostic
// .Error("Invalid syntax before '::'")
// .WithHelp("Only identifiers can be used before '::' for namespace resolution")
// .At(symbol)
// .Build());
// }
//
// var name = ExpectIdentifier();
// ExpectSymbol(Symbol.OpenParen);
// var parameters = new List<ExpressionNode>();
// while (!TryExpectSymbol(Symbol.CloseParen))
// {
// parameters.Add(ParseExpression());
// if (!TryExpectSymbol(Symbol.Comma) && Peek().TryGetValue(out var nextToken) && nextToken is not SymbolToken { Symbol: Symbol.CloseParen })
// {
// _diagnostics.Add(Diagnostic
// .Warning("Missing comma between function arguments")
// .WithHelp("Add a ',' to separate arguments")
// .At(nextToken)
// .Build());
// }
// }
//
// return new FuncCallStatementNode(GetTokensForNode(startIndex), new FuncCall(namespaceNode.Identifier, name.Value, parameters));
// }
// case Symbol.OpenParen:
// {
// if (leftExpr is not IdentifierNode funcNode)
// {
// throw new ParseException(Diagnostic
// .Error("Invalid syntax before '('")
// .WithHelp("Only identifiers can be called as functions")
// .At(symbol)
// .Build());
// }
//
// var parameters = new List<ExpressionNode>();
// while (!TryExpectSymbol(Symbol.CloseParen))
// {
// parameters.Add(ParseExpression());
// if (!TryExpectSymbol(Symbol.Comma) && Peek().TryGetValue(out var nextToken) && nextToken is not SymbolToken { Symbol: Symbol.CloseParen })
// {
// _diagnostics.Add(Diagnostic
// .Warning("Missing comma between function arguments")
// .WithHelp("Add a ',' to separate arguments")
// .At(nextToken)
// .Build());
// }
// }
//
// return new FuncCallStatementNode(GetTokensForNode(startIndex), new FuncCall(_namespace, funcNode.Identifier, parameters));
// }
// case Symbol.Assign:
// {
// var value = ParseExpression();
//
// switch (leftExpr)
// {
// case IdentifierNode identifierNode:
// {
// return new VariableAssignmentNode(GetTokensForNode(startIndex), identifierNode.Identifier, value);
// }
// case ArrayIndexNode arrayIndexNode:
// {
// return new ArrayIndexAssignmentNode(GetTokensForNode(startIndex), arrayIndexNode.Expression, arrayIndexNode.Index, value);
// }
// case MemberAccessNode memberAccessNode:
// {
// return new MemberAssignmentNode(GetTokensForNode(startIndex), memberAccessNode.Expression, memberAccessNode.Member, value);
// }
// default:
// {
// throw new ParseException(Diagnostic
// .Error("Invalid left-hand side in assignment")
// .WithHelp("Left side must be a variable, array element, or struct member")
// .At(symbol)
// .Build());
// }
// }
// }
// default:
// {
// throw new ParseException(Diagnostic
// .Error($"Unexpected symbol '{symbol.Symbol}' after identifier")
// .WithHelp("Expected '(', '=', or '::' after identifier")
// .At(symbol)
// .Build());
// }
// }
// }
private VariableDeclarationNode ParseVariableDeclaration(int startIndex)
{
ExpectSymbol(Symbol.Let);
var name = ExpectIdentifier().Value;
var type = Optional<NubType>.Empty();
if (TryExpectSymbol(Symbol.Colon))
@@ -301,8 +371,22 @@ public class Parser
return new VariableDeclarationNode(GetTokensForNode(startIndex), name, type, value);
}
private StatementNode ParseBreak(int startIndex)
{
ExpectSymbol(Symbol.Break);
Next();
return new BreakNode(GetTokensForNode(startIndex));
}
private StatementNode ParseContinue(int startIndex)
{
ExpectSymbol(Symbol.Continue);
return new ContinueNode(GetTokensForNode(startIndex));
}
private ReturnNode ParseReturn(int startIndex)
{
ExpectSymbol(Symbol.Return);
var value = Optional<ExpressionNode>.Empty();
if (!TryExpectSymbol(Symbol.Semicolon))
{
@@ -314,6 +398,7 @@ public class Parser
private IfNode ParseIf(int startIndex)
{
ExpectSymbol(Symbol.If);
var condition = ParseExpression();
var body = ParseBlock();
@@ -331,6 +416,7 @@ public class Parser
private WhileNode ParseWhile(int startIndex)
{
ExpectSymbol(Symbol.While);
var condition = ParseExpression();
var body = ParseBlock();
return new WhileNode(GetTokensForNode(startIndex), condition, body);
@@ -454,7 +540,7 @@ public class Parser
}
}
expr = new FuncCallExpressionNode(GetTokensForNode(startIndex), new FuncCall(identifier.Value, name.Value, parameters));
expr = new FuncCallNode(GetTokensForNode(startIndex), identifier.Value, name.Value, parameters);
break;
}
case SymbolToken { Symbol: Symbol.OpenParen }:
@@ -474,7 +560,7 @@ public class Parser
}
}
expr = new FuncCallExpressionNode(GetTokensForNode(startIndex), new FuncCall(_namespace, identifier.Value, parameters));
expr = new FuncCallNode(GetTokensForNode(startIndex), _namespace, identifier.Value, parameters);
break;
}
default:
@@ -554,7 +640,7 @@ public class Parser
.Build());
}
expr = new StructInitializerNode(GetTokensForNode(startIndex), structType, initializers);
expr = new StructInitializerNode(GetTokensForNode(startIndex), structType, initializers);
break;
}
default:
@@ -603,7 +689,7 @@ public class Parser
{
var index = ParseExpression();
ExpectSymbol(Symbol.CloseBracket);
expr = new ArrayIndexNode(GetTokensForNode(startIndex), expr, index);
expr = new ArrayIndexAccessNode(GetTokensForNode(startIndex), expr, index);
continue;
}
@@ -798,7 +884,7 @@ public class Parser
{
if (token is SymbolToken { Symbol: Symbol.CloseBrace } or IdentifierToken or SymbolToken
{
Symbol: Symbol.Return or Symbol.If or Symbol.While or Symbol.Break or Symbol.Continue
Symbol: Symbol.Return or Symbol.If or Symbol.While or Symbol.Let or Symbol.Break or Symbol.Continue
})
{
break;

View File

@@ -0,0 +1,9 @@
using Nub.Lang.Frontend.Lexing;
namespace Nub.Lang.Frontend.Parsing;
public class ArrayIndexAssignmentNode(IReadOnlyList<Token> tokens, ArrayIndexAccessNode arrayIndexAccess, ExpressionNode value) : StatementNode(tokens)
{
public ArrayIndexAccessNode ArrayIndexAccess { get; } = arrayIndexAccess;
public ExpressionNode Value { get; } = value;
}

View File

@@ -0,0 +1,9 @@
using Nub.Lang.Frontend.Lexing;
namespace Nub.Lang.Frontend.Parsing;
public class MemberAssignmentNode(IReadOnlyList<Token> tokens, MemberAccessNode expression, ExpressionNode value) : StatementNode(tokens)
{
public MemberAccessNode MemberAccess { get; } = expression;
public ExpressionNode Value { get; } = value;
}

View File

@@ -0,0 +1,8 @@
using Nub.Lang.Frontend.Lexing;
namespace Nub.Lang.Frontend.Parsing;
public class StatementExpressionNode(IReadOnlyList<Token> tokens, ExpressionNode expression) : StatementNode(tokens)
{
public ExpressionNode Expression { get; } = expression;
}

View File

@@ -3,8 +3,8 @@ using Nub.Lang.Frontend.Typing;
namespace Nub.Lang.Frontend.Parsing;
public class VariableAssignmentNode(IReadOnlyList<Token> tokens, string name, ExpressionNode value) : StatementNode(tokens)
public class VariableAssignmentNode(IReadOnlyList<Token> tokens, IdentifierNode identifier, ExpressionNode value) : StatementNode(tokens)
{
public string Name { get; } = name;
public IdentifierNode Identifier { get; } = identifier;
public ExpressionNode Value { get; } = value;
}

View File

@@ -108,24 +108,30 @@ public class TypeChecker
{
switch (statement)
{
case ArrayIndexAssignmentNode arrayIndexAssignment:
TypeCheckArrayIndexAssignment(arrayIndexAssignment);
break;
case VariableAssignmentNode variableAssignment:
TypeCheckVariableAssignment(variableAssignment);
break;
case VariableDeclarationNode variableDeclaration:
TypeCheckVariableVariableDeclaration(variableDeclaration);
break;
case FuncCallStatementNode funcCall:
TypeCheckFuncCall(funcCall.FuncCall, funcCall);
break;
case IfNode ifNode:
TypeCheckIf(ifNode);
break;
case MemberAssignmentNode memberAssignment:
TypeCheckMemberAssignment(memberAssignment);
break;
case WhileNode whileNode:
TypeCheckWhile(whileNode);
break;
case ReturnNode returnNode:
TypeCheckReturn(returnNode);
break;
case StatementExpressionNode statementExpression:
TypeCheckExpression(statementExpression.Expression);
break;
case BreakNode:
case ContinueNode:
break;
@@ -135,20 +141,46 @@ public class TypeChecker
}
}
private void TypeCheckMemberAssignment(MemberAssignmentNode memberAssignment)
{
var memberType = TypeCheckExpression(memberAssignment.MemberAccess);
if (memberType == null) return;
var valueType = TypeCheckExpression(memberAssignment.Value);
if (valueType == null) return;
if (!NubType.IsCompatibleWith(memberType, valueType))
{
ReportError($"'{valueType}' is not assignable to member of type '{memberType}'", memberAssignment);
}
}
private void TypeCheckArrayIndexAssignment(ArrayIndexAssignmentNode arrayIndexAssignment)
{
var itemType = TypeCheckExpression(arrayIndexAssignment.ArrayIndexAccess);
if (itemType == null) return;
var valueType = TypeCheckExpression(arrayIndexAssignment.Value);
if (valueType == null) return;
if (!NubType.IsCompatibleWith(itemType, valueType))
{
ReportError($"'{valueType}' is not assignable to array of type '{itemType}'", arrayIndexAssignment);
}
}
private void TypeCheckVariableAssignment(VariableAssignmentNode variableAssignment)
{
var valueType = TypeCheckExpression(variableAssignment.Value);
if (valueType == null) return;
if (!_variables.TryGetValue(variableAssignment.Name, out var existingVariable))
if (!_variables.TryGetValue(variableAssignment.Identifier.Identifier, out var existingVariable))
{
ReportError($"Variable '{variableAssignment.Name}' is not declared", variableAssignment);
ReportError($"Variable '{variableAssignment.Identifier}' is not declared", variableAssignment);
return;
}
if (!NubType.IsCompatibleWith(variableAssignment.Value.Type, existingVariable))
{
ReportError($"Cannot assign expression of type '{variableAssignment.Value.Type}' to variable '{variableAssignment.Name}' with type '{existingVariable}'", variableAssignment);
ReportError($"Cannot assign expression of type '{variableAssignment.Value.Type}' to variable '{variableAssignment.Identifier}' with type '{existingVariable}'", variableAssignment);
}
}
@@ -204,7 +236,7 @@ public class TypeChecker
return nubPointerType.BaseType;
}
private NubType? TypeCheckFuncCall(FuncCall funcCall, Node node)
private NubType? TypeCheckFuncCall(FuncCallNode funcCall, Node node)
{
List<NubType> parameterTypes = [];
foreach (var funcCallParameter in funcCall.Parameters)
@@ -314,14 +346,14 @@ public class TypeChecker
var resultType = expression switch
{
AddressOfNode addressOf => TypeCheckAddressOf(addressOf),
ArrayIndexNode arrayIndex => TypeCheckArrayIndex(arrayIndex),
ArrayIndexAccessNode arrayIndex => TypeCheckArrayIndex(arrayIndex),
ArrayInitializerNode arrayInitializer => TypeCheckArrayInitializer(arrayInitializer),
LiteralNode literal => literal.LiteralType,
IdentifierNode identifier => TypeCheckIdentifier(identifier),
BinaryExpressionNode binaryExpr => TypeCheckBinaryExpression(binaryExpr),
CastNode cast => TypeCheckCast(cast),
DereferenceNode dereference => TypeCheckDereference(dereference),
FuncCallExpressionNode funcCallExpr => TypeCheckFuncCall(funcCallExpr.FuncCall, funcCallExpr),
FuncCallNode funcCallExpr => TypeCheckFuncCall(funcCallExpr, funcCallExpr),
StructInitializerNode structInit => TypeCheckStructInitializer(structInit),
UnaryExpressionNode unaryExpression => TypeCheckUnaryExpression(unaryExpression),
MemberAccessNode memberAccess => TypeCheckMemberAccess(memberAccess),
@@ -342,21 +374,21 @@ public class TypeChecker
return null;
}
private NubType? TypeCheckArrayIndex(ArrayIndexNode arrayIndex)
private NubType? TypeCheckArrayIndex(ArrayIndexAccessNode arrayIndexAccess)
{
var expressionType = TypeCheckExpression(arrayIndex.Expression);
var expressionType = TypeCheckExpression(arrayIndexAccess.Array);
if (expressionType == null) return null;
if (expressionType is not NubArrayType arrayType)
{
ReportError($"Cannot access index of non-array type {expressionType}", arrayIndex.Expression);
ReportError($"Cannot access index of non-array type {expressionType}", arrayIndexAccess.Array);
return null;
}
var indexType = TypeCheckExpression(arrayIndex.Index);
var indexType = TypeCheckExpression(arrayIndexAccess.Index);
if (indexType != null && !IsInteger(indexType))
{
ReportError("Array index type must be an integer", arrayIndex.Index);
ReportError("Array index type must be an integer", arrayIndexAccess.Index);
}
return arrayType.BaseType;