Refactor statements

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

View File

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

View File

@@ -8,7 +8,7 @@ namespace Nub.Lang.Backend;
public class Generator public class Generator
{ {
private const string OutOfBoundsMessage = "Index is out of bounds\\n"; private const string OutOfBoundsMessage = "Index is out of bounds\\n";
private List<SourceFile> _sourceFiles = []; private List<SourceFile> _sourceFiles = [];
private StringBuilder _builder = new(); private StringBuilder _builder = new();
private Dictionary<string, Variable> _variables = []; private Dictionary<string, Variable> _variables = [];
@@ -34,13 +34,13 @@ public class Generator
_funcIndex = 0; _funcIndex = 0;
_labelIndex = 0; _labelIndex = 0;
_codeIsReachable = true; _codeIsReachable = true;
foreach (var structDef in _sourceFiles.SelectMany(f => f.Definitions).OfType<StructDefinitionNode>()) foreach (var structDef in _sourceFiles.SelectMany(f => f.Definitions).OfType<StructDefinitionNode>())
{ {
GenerateStructDefinition(structDef); GenerateStructDefinition(structDef);
_builder.AppendLine(); _builder.AppendLine();
} }
foreach (var funcSignature in _sourceFiles.SelectMany(f => f.Definitions).OfType<IFuncSignature>()) foreach (var funcSignature in _sourceFiles.SelectMany(f => f.Definitions).OfType<IFuncSignature>())
{ {
switch (funcSignature) switch (funcSignature)
@@ -58,6 +58,7 @@ public class Generator
var funcName = GenFuncName(); var funcName = GenFuncName();
_funcNames[funcSignature] = funcName; _funcNames[funcSignature] = funcName;
} }
break; break;
default: default:
throw new ArgumentOutOfRangeException(nameof(funcSignature)); throw new ArgumentOutOfRangeException(nameof(funcSignature));
@@ -77,7 +78,7 @@ public class Generator
var str = _strings[i]; var str = _strings[i];
_builder.AppendLine($"data $str{i + 1} = {{ b \"{str}\", b 0 }}"); _builder.AppendLine($"data $str{i + 1} = {{ b \"{str}\", b 0 }}");
} }
return _builder.ToString(); return _builder.ToString();
} }
@@ -367,21 +368,27 @@ public class Generator
{ {
switch (statement) switch (statement)
{ {
case ArrayIndexAssignmentNode arrayIndexAssignment:
throw new NotImplementedException();
break;
case BreakNode: case BreakNode:
GenerateBreak(); GenerateBreak();
break; break;
case ContinueNode: case ContinueNode:
GenerateContinue(); GenerateContinue();
break; break;
case FuncCallStatementNode funcCallStatement:
GenerateStatementFuncCall(funcCallStatement);
break;
case IfNode ifStatement: case IfNode ifStatement:
GenerateIf(ifStatement); GenerateIf(ifStatement);
break; break;
case MemberAssignmentNode memberAssignment:
throw new NotImplementedException();
break;
case ReturnNode @return: case ReturnNode @return:
GenerateReturn(@return); GenerateReturn(@return);
break; break;
case StatementExpressionNode statementExpression:
GenerateExpression(statementExpression.Expression);
break;
case VariableAssignmentNode variableAssignment: case VariableAssignmentNode variableAssignment:
GenerateVariableAssignment(variableAssignment); GenerateVariableAssignment(variableAssignment);
break; 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) private void GenerateBlock(BlockNode block)
{ {
foreach (var statement in block.Statements.Where(_ => _codeIsReachable)) foreach (var statement in block.Statements.Where(_ => _codeIsReachable))
@@ -461,12 +425,6 @@ public class Generator
_codeIsReachable = false; _codeIsReachable = false;
} }
private void GenerateStatementFuncCall(FuncCallStatementNode funcCall)
{
var call = GenerateFuncCall(funcCall.FuncCall);
_builder.AppendLine($" {call}");
}
private void GenerateIf(IfNode ifStatement) private void GenerateIf(IfNode ifStatement)
{ {
var trueLabel = GenLabelName(); var trueLabel = GenLabelName();
@@ -507,7 +465,7 @@ public class Generator
private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment) private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment)
{ {
var result = GenerateExpression(variableAssignment.Value); 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) private void GenerateVariableDeclaration(VariableDeclarationNode variableDeclaration)
@@ -515,7 +473,7 @@ public class Generator
var pointerName = GenVarName(); var pointerName = GenVarName();
var type = variableDeclaration.ExplicitType.Value ?? variableDeclaration.Value.Value?.Type!; var type = variableDeclaration.ExplicitType.Value ?? variableDeclaration.Value.Value?.Type!;
_builder.AppendLine($" %{pointerName} ={SQT(type)} alloc8 {QbeTypeSize(type)}"); _builder.AppendLine($" %{pointerName} ={SQT(type)} alloc8 {QbeTypeSize(type)}");
if (variableDeclaration.Value.HasValue) if (variableDeclaration.Value.HasValue)
@@ -527,7 +485,7 @@ public class Generator
{ {
_builder.AppendLine($" storel 0, %{pointerName}"); _builder.AppendLine($" storel 0, %{pointerName}");
} }
_variables[variableDeclaration.Name] = new Variable _variables[variableDeclaration.Name] = new Variable
{ {
Pointer = $"%{pointerName}", Pointer = $"%{pointerName}",
@@ -561,12 +519,12 @@ public class Generator
return expression switch return expression switch
{ {
AddressOfNode addressOf => GenerateAddressOf(addressOf), AddressOfNode addressOf => GenerateAddressOf(addressOf),
ArrayIndexNode arrayIndex => GenerateArrayIndex(arrayIndex), ArrayIndexAccessNode arrayIndex => GenerateArrayIndex(arrayIndex),
ArrayInitializerNode arrayInitializer => GenerateArrayInitializer(arrayInitializer), ArrayInitializerNode arrayInitializer => GenerateArrayInitializer(arrayInitializer),
BinaryExpressionNode binaryExpression => GenerateBinaryExpression(binaryExpression), BinaryExpressionNode binaryExpression => GenerateBinaryExpression(binaryExpression),
CastNode cast => GenerateCast(cast), CastNode cast => GenerateCast(cast),
DereferenceNode dereference => GenerateDereference(dereference), DereferenceNode dereference => GenerateDereference(dereference),
FuncCallExpressionNode funcCallExpression => GenerateExpressionFuncCall(funcCallExpression), FuncCallNode funcCallExpression => GenerateFuncCall(funcCallExpression),
IdentifierNode identifier => GenerateIdentifier(identifier), IdentifierNode identifier => GenerateIdentifier(identifier),
LiteralNode literal => GenerateLiteral(literal), LiteralNode literal => GenerateLiteral(literal),
StructInitializerNode structInitializer => GenerateStructInitializer(structInitializer), 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 array = GenerateExpression(arrayIndexAccess.Array);
var index = GenerateExpression(arrayIndex.Index); var index = GenerateExpression(arrayIndexAccess.Index);
var arrayBaseType = ((NubArrayType)arrayIndexAccess.Array.Type).BaseType;
var arrayBaseType = ((NubArrayType)arrayIndex.Expression.Type).BaseType;
var countName = GenVarName(); var countName = GenVarName();
_builder.AppendLine($" %{countName} =l loadl {array}"); _builder.AppendLine($" %{countName} =l loadl {array}");
var isNegativeName = GenVarName(); var isNegativeName = GenVarName();
_builder.AppendLine($" %{isNegativeName} =w csltl {index}, 0"); _builder.AppendLine($" %{isNegativeName} =w csltl {index}, 0");
var isOobName = GenVarName(); var isOobName = GenVarName();
_builder.AppendLine($" %{isOobName} =w csgel {index}, %{countName}"); _builder.AppendLine($" %{isOobName} =w csgel {index}, %{countName}");
var anyOobName = GenVarName(); var anyOobName = GenVarName();
_builder.AppendLine($" %{anyOobName} =w or %{isNegativeName}, %{isOobName}"); _builder.AppendLine($" %{anyOobName} =w or %{isNegativeName}, %{isOobName}");
var oobLabel = GenLabelName(); var oobLabel = GenLabelName();
var notOobLabel = GenLabelName(); var notOobLabel = GenLabelName();
_builder.AppendLine($" jnz %{anyOobName}, @{oobLabel}, @{notOobLabel}"); _builder.AppendLine($" jnz %{anyOobName}, @{oobLabel}, @{notOobLabel}");
_builder.AppendLine($"@{oobLabel}"); _builder.AppendLine($"@{oobLabel}");
_builder.AppendLine($" call $nub_panic(l $oob_message, l {OutOfBoundsMessage.Length})"); _builder.AppendLine($" call $nub_panic(l $oob_message, l {OutOfBoundsMessage.Length})");
_builder.AppendLine($"@{notOobLabel}"); _builder.AppendLine($"@{notOobLabel}");
// Calculate element address // Calculate element address
var firstItemPointerName = GenVarName(); var firstItemPointerName = GenVarName();
_builder.AppendLine($" %{firstItemPointerName} =l add {array}, 8"); _builder.AppendLine($" %{firstItemPointerName} =l add {array}, 8");
@@ -611,7 +569,7 @@ public class Generator
_builder.AppendLine($" %{offsetPointerName} =l mul {index}, {QbeTypeSize(arrayBaseType)}"); _builder.AppendLine($" %{offsetPointerName} =l mul {index}, {QbeTypeSize(arrayBaseType)}");
var resultPointerName = GenVarName(); var resultPointerName = GenVarName();
_builder.AppendLine($" %{resultPointerName} =l add %{firstItemPointerName}, %{offsetPointerName}"); _builder.AppendLine($" %{resultPointerName} =l add %{firstItemPointerName}, %{offsetPointerName}");
// Load the value // Load the value
var outputName = GenVarName(); var outputName = GenVarName();
_builder.AppendLine($" %{outputName} ={SQT(arrayBaseType)} load{SQT(arrayBaseType)} %{resultPointerName}"); _builder.AppendLine($" %{outputName} ={SQT(arrayBaseType)} load{SQT(arrayBaseType)} %{resultPointerName}");
@@ -628,11 +586,11 @@ public class Generator
var outputName = GenVarName(); var outputName = GenVarName();
_builder.AppendLine($" %{outputName} =l alloc8 %{totalArraySize}"); _builder.AppendLine($" %{outputName} =l alloc8 %{totalArraySize}");
_builder.AppendLine($" storel {capacity}, %{outputName}"); _builder.AppendLine($" storel {capacity}, %{outputName}");
var dataPtr = GenVarName(); var dataPtr = GenVarName();
_builder.AppendLine($" %{dataPtr} =l add %{outputName}, 8"); _builder.AppendLine($" %{dataPtr} =l add %{outputName}, 8");
_builder.AppendLine($" call $nub_memset(l %{dataPtr}, w 0, l %{capacityInBytes})"); _builder.AppendLine($" call $nub_memset(l %{dataPtr}, w 0, l %{capacityInBytes})");
return $"%{outputName}"; return $"%{outputName}";
} }
@@ -1490,7 +1448,7 @@ public class Generator
{ {
throw new Exception($"Struct {structType.Name} is not defined"); throw new Exception($"Struct {structType.Name} is not defined");
} }
var fieldIndex = -1; var fieldIndex = -1;
for (var i = 0; i < structDefinition.Fields.Count; i++) for (var i = 0; i < structDefinition.Fields.Count; i++)
{ {
@@ -1515,14 +1473,54 @@ public class Generator
return $"%{outputName}"; return $"%{outputName}";
} }
} }
throw new ArgumentOutOfRangeException(nameof(memberAccess.Expression.Type)); throw new ArgumentOutOfRangeException(nameof(memberAccess.Expression.Type));
} }
private string GenerateExpressionFuncCall(FuncCallExpressionNode funcCall) private string GenerateFuncCall(FuncCallNode funcCall)
{ {
var outputName = GenVarName(); 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}"); _builder.AppendLine($" %{outputName} ={SQT(funcCall.Type)} {call}");
return $"%{outputName}"; return $"%{outputName}";
} }
@@ -1531,12 +1529,12 @@ public class Generator
{ {
return $"v{++_variableIndex}"; return $"v{++_variableIndex}";
} }
private string GenFuncName() private string GenFuncName()
{ {
return $"f{++_funcIndex}"; return $"f{++_funcIndex}";
} }
private string GenLabelName() private string GenLabelName()
{ {
return $"l{++_labelIndex}"; 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() private StatementNode ParseStatement()
{ {
var startIndex = _index; var startIndex = _index;
var token = ExpectToken(); if (!Peek().TryGetValue(out var token))
switch (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 Symbol.Return:
} return ParseReturn(startIndex);
case SymbolToken symbol: case Symbol.If:
{ return ParseIf(startIndex);
return symbol.Symbol switch case Symbol.While:
{ return ParseWhile(startIndex);
Symbol.Return => ParseReturn(startIndex), case Symbol.Let:
Symbol.If => ParseIf(startIndex), return ParseVariableDeclaration(startIndex);
Symbol.While => ParseWhile(startIndex), case Symbol.Break:
Symbol.Let => ParseVariableDeclaration(startIndex), return ParseBreak(startIndex);
Symbol.Break => new BreakNode(GetTokensForNode(startIndex)), case Symbol.Continue:
Symbol.Continue => new ContinueNode(GetTokensForNode(startIndex)), return ParseContinue(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());
} }
} }
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(); if (token is SymbolToken symbol)
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:
{ {
var name = ExpectIdentifier(); switch (symbol.Symbol)
ExpectSymbol(Symbol.OpenParen);
var parameters = new List<ExpressionNode>();
while (!TryExpectSymbol(Symbol.CloseParen))
{ {
parameters.Add(ParseExpression()); case Symbol.Assign:
if (!TryExpectSymbol(Symbol.Comma) && Peek().TryGetValue(out var nextToken) && nextToken is not SymbolToken { Symbol: Symbol.CloseParen })
{ {
_diagnostics.Add(Diagnostic Next();
.Warning("Missing comma between function arguments") switch (expr)
.WithHelp("Add a ',' to separate arguments") {
.At(nextToken) case MemberAccessNode memberAccess:
.Build()); {
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) private VariableDeclarationNode ParseVariableDeclaration(int startIndex)
{ {
ExpectSymbol(Symbol.Let);
var name = ExpectIdentifier().Value; var name = ExpectIdentifier().Value;
var type = Optional<NubType>.Empty(); var type = Optional<NubType>.Empty();
if (TryExpectSymbol(Symbol.Colon)) if (TryExpectSymbol(Symbol.Colon))
@@ -301,8 +371,22 @@ public class Parser
return new VariableDeclarationNode(GetTokensForNode(startIndex), name, type, value); 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) private ReturnNode ParseReturn(int startIndex)
{ {
ExpectSymbol(Symbol.Return);
var value = Optional<ExpressionNode>.Empty(); var value = Optional<ExpressionNode>.Empty();
if (!TryExpectSymbol(Symbol.Semicolon)) if (!TryExpectSymbol(Symbol.Semicolon))
{ {
@@ -314,6 +398,7 @@ public class Parser
private IfNode ParseIf(int startIndex) private IfNode ParseIf(int startIndex)
{ {
ExpectSymbol(Symbol.If);
var condition = ParseExpression(); var condition = ParseExpression();
var body = ParseBlock(); var body = ParseBlock();
@@ -331,6 +416,7 @@ public class Parser
private WhileNode ParseWhile(int startIndex) private WhileNode ParseWhile(int startIndex)
{ {
ExpectSymbol(Symbol.While);
var condition = ParseExpression(); var condition = ParseExpression();
var body = ParseBlock(); var body = ParseBlock();
return new WhileNode(GetTokensForNode(startIndex), condition, body); 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; break;
} }
case SymbolToken { Symbol: Symbol.OpenParen }: 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; break;
} }
default: default:
@@ -554,7 +640,7 @@ public class Parser
.Build()); .Build());
} }
expr = new StructInitializerNode(GetTokensForNode(startIndex), structType, initializers); expr = new StructInitializerNode(GetTokensForNode(startIndex), structType, initializers);
break; break;
} }
default: default:
@@ -603,7 +689,7 @@ public class Parser
{ {
var index = ParseExpression(); var index = ParseExpression();
ExpectSymbol(Symbol.CloseBracket); ExpectSymbol(Symbol.CloseBracket);
expr = new ArrayIndexNode(GetTokensForNode(startIndex), expr, index); expr = new ArrayIndexAccessNode(GetTokensForNode(startIndex), expr, index);
continue; continue;
} }
@@ -798,7 +884,7 @@ public class Parser
{ {
if (token is SymbolToken { Symbol: Symbol.CloseBrace } or IdentifierToken or SymbolToken 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; 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; 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; public ExpressionNode Value { get; } = value;
} }

View File

@@ -108,24 +108,30 @@ public class TypeChecker
{ {
switch (statement) switch (statement)
{ {
case ArrayIndexAssignmentNode arrayIndexAssignment:
TypeCheckArrayIndexAssignment(arrayIndexAssignment);
break;
case VariableAssignmentNode variableAssignment: case VariableAssignmentNode variableAssignment:
TypeCheckVariableAssignment(variableAssignment); TypeCheckVariableAssignment(variableAssignment);
break; break;
case VariableDeclarationNode variableDeclaration: case VariableDeclarationNode variableDeclaration:
TypeCheckVariableVariableDeclaration(variableDeclaration); TypeCheckVariableVariableDeclaration(variableDeclaration);
break; break;
case FuncCallStatementNode funcCall:
TypeCheckFuncCall(funcCall.FuncCall, funcCall);
break;
case IfNode ifNode: case IfNode ifNode:
TypeCheckIf(ifNode); TypeCheckIf(ifNode);
break; break;
case MemberAssignmentNode memberAssignment:
TypeCheckMemberAssignment(memberAssignment);
break;
case WhileNode whileNode: case WhileNode whileNode:
TypeCheckWhile(whileNode); TypeCheckWhile(whileNode);
break; break;
case ReturnNode returnNode: case ReturnNode returnNode:
TypeCheckReturn(returnNode); TypeCheckReturn(returnNode);
break; break;
case StatementExpressionNode statementExpression:
TypeCheckExpression(statementExpression.Expression);
break;
case BreakNode: case BreakNode:
case ContinueNode: case ContinueNode:
break; 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) private void TypeCheckVariableAssignment(VariableAssignmentNode variableAssignment)
{ {
var valueType = TypeCheckExpression(variableAssignment.Value); var valueType = TypeCheckExpression(variableAssignment.Value);
if (valueType == null) return; 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; return;
} }
if (!NubType.IsCompatibleWith(variableAssignment.Value.Type, existingVariable)) 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; return nubPointerType.BaseType;
} }
private NubType? TypeCheckFuncCall(FuncCall funcCall, Node node) private NubType? TypeCheckFuncCall(FuncCallNode funcCall, Node node)
{ {
List<NubType> parameterTypes = []; List<NubType> parameterTypes = [];
foreach (var funcCallParameter in funcCall.Parameters) foreach (var funcCallParameter in funcCall.Parameters)
@@ -314,14 +346,14 @@ public class TypeChecker
var resultType = expression switch var resultType = expression switch
{ {
AddressOfNode addressOf => TypeCheckAddressOf(addressOf), AddressOfNode addressOf => TypeCheckAddressOf(addressOf),
ArrayIndexNode arrayIndex => TypeCheckArrayIndex(arrayIndex), ArrayIndexAccessNode arrayIndex => TypeCheckArrayIndex(arrayIndex),
ArrayInitializerNode arrayInitializer => TypeCheckArrayInitializer(arrayInitializer), ArrayInitializerNode arrayInitializer => TypeCheckArrayInitializer(arrayInitializer),
LiteralNode literal => literal.LiteralType, LiteralNode literal => literal.LiteralType,
IdentifierNode identifier => TypeCheckIdentifier(identifier), IdentifierNode identifier => TypeCheckIdentifier(identifier),
BinaryExpressionNode binaryExpr => TypeCheckBinaryExpression(binaryExpr), BinaryExpressionNode binaryExpr => TypeCheckBinaryExpression(binaryExpr),
CastNode cast => TypeCheckCast(cast), CastNode cast => TypeCheckCast(cast),
DereferenceNode dereference => TypeCheckDereference(dereference), DereferenceNode dereference => TypeCheckDereference(dereference),
FuncCallExpressionNode funcCallExpr => TypeCheckFuncCall(funcCallExpr.FuncCall, funcCallExpr), FuncCallNode funcCallExpr => TypeCheckFuncCall(funcCallExpr, funcCallExpr),
StructInitializerNode structInit => TypeCheckStructInitializer(structInit), StructInitializerNode structInit => TypeCheckStructInitializer(structInit),
UnaryExpressionNode unaryExpression => TypeCheckUnaryExpression(unaryExpression), UnaryExpressionNode unaryExpression => TypeCheckUnaryExpression(unaryExpression),
MemberAccessNode memberAccess => TypeCheckMemberAccess(memberAccess), MemberAccessNode memberAccess => TypeCheckMemberAccess(memberAccess),
@@ -342,21 +374,21 @@ public class TypeChecker
return null; 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 == null) return null;
if (expressionType is not NubArrayType arrayType) 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; return null;
} }
var indexType = TypeCheckExpression(arrayIndex.Index); var indexType = TypeCheckExpression(arrayIndexAccess.Index);
if (indexType != null && !IsInteger(indexType)) 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; return arrayType.BaseType;