Refactor statements
This commit is contained in:
@@ -7,6 +7,6 @@ export func main(args: []^string) {
|
||||
|
||||
while i < args.count {
|
||||
c::printf("%s\n", args[i])
|
||||
i += 1
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
@@ -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}";
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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}()";
|
||||
}
|
||||
@@ -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}()";
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user