Variable declarations seperate from assignment

This commit is contained in:
nub31
2025-05-27 11:31:28 +02:00
parent 98c42f6021
commit 0541e56224
9 changed files with 115 additions and 67 deletions

View File

@@ -3,9 +3,9 @@ namespace main
/// # Documentation
/// ## Documentation subtitle
export func main(args: []string) {
i = 0
let i: i64
x = math::add(1, 1)
let x = math::add(1, 1)
c::printf("%d\n", args.count)

View File

@@ -6,8 +6,8 @@ using Nub.Lang.Frontend.Typing;
if (args.Length != 1)
{
Console.Error.WriteLine("Usage: nub <input-dir>");
Console.Error.WriteLine("Example: nub src");
Console.Error.WriteLine("Usage: nub <input-dir>");
Console.Error.WriteLine("Example: nub src");
return 1;
}

View File

@@ -296,8 +296,6 @@ public class Generator
_builder.AppendLine($"({string.Join(", ", parameterStrings)}) {{");
_builder.AppendLine("@start");
_builder.AppendLine(" # Variable allocation");
foreach (var parameter in node.Parameters)
{
var parameterName = parameter.Name;
@@ -334,9 +332,6 @@ public class Generator
};
}
_builder.AppendLine(" # End variable allocation");
_builder.AppendLine();
GenerateBlock(node.Body);
if (node.Body.Statements.LastOrDefault() is not ReturnNode)
@@ -386,6 +381,9 @@ public class Generator
case VariableAssignmentNode variableAssignment:
GenerateVariableAssignment(variableAssignment);
break;
case VariableDeclarationNode variableDeclaration:
GenerateVariableDeclaration(variableDeclaration);
break;
case WhileNode whileStatement:
GenerateWhile(whileStatement);
break;
@@ -505,23 +503,32 @@ public class Generator
private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment)
{
var result = GenerateExpression(variableAssignment.Value);
_builder.AppendLine($" storel {result}, {_variables[variableAssignment.Name].Pointer}");
}
if (_variables.TryGetValue(variableAssignment.Name, out var existingVariable))
private void GenerateVariableDeclaration(VariableDeclarationNode variableDeclaration)
{
var pointerName = GenVarName();
var type = variableDeclaration.ExplicitType.Value ?? variableDeclaration.Value.Value?.Type!;
_builder.AppendLine($" %{pointerName} ={SQT(type)} alloc8 {QbeTypeSize(type)}");
if (variableDeclaration.Value.HasValue)
{
_builder.AppendLine($" storel {result}, {existingVariable.Pointer}");
var result = GenerateExpression(variableDeclaration.Value.Value);
_builder.AppendLine($" storel {result}, %{pointerName}");
}
else
{
var pointerName = GenVarName();
_builder.AppendLine($" %{pointerName} ={SQT(variableAssignment.Value.Type)} alloc8 {QbeTypeSize(variableAssignment.Value.Type)}");
_builder.AppendLine($" storel {result}, %{pointerName}");
_variables[variableAssignment.Name] = new Variable
{
Pointer = $"%{pointerName}",
Type = variableAssignment.Value.Type
};
_builder.AppendLine($" storel 0, %{pointerName}");
}
_variables[variableDeclaration.Name] = new Variable
{
Pointer = $"%{pointerName}",
Type = type
};
}
private void GenerateWhile(WhileNode whileStatement)

View File

@@ -17,6 +17,7 @@ public class Lexer
["return"] = Symbol.Return,
["new"] = Symbol.New,
["struct"] = Symbol.Struct,
["let"] = Symbol.Let,
};
private static readonly Dictionary<string, Modifier> Modifiers = new()

View File

@@ -44,4 +44,5 @@ public enum Symbol
Ampersand,
DoubleColon,
Namespace,
Let
}

View File

@@ -192,11 +192,12 @@ public class Parser
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, 'return', 'if', 'while', 'break', or 'continue'")
.WithHelp("Expected identifier, 'let', 'return', 'if', 'while', 'break', or 'continue'")
.At(symbol)
.Build())
};
@@ -258,14 +259,7 @@ public class Parser
case Symbol.Assign:
{
var value = ParseExpression();
return new VariableAssignmentNode(GetTokensForNode(startIndex), identifier.Value, Optional<NubType>.Empty(), value);
}
case Symbol.Colon:
{
var type = ParseType();
ExpectSymbol(Symbol.Assign);
var value = ParseExpression();
return new VariableAssignmentNode(GetTokensForNode(startIndex), identifier.Value, type, value);
return new VariableAssignmentNode(GetTokensForNode(startIndex), identifier.Value, value);
}
default:
{
@@ -278,6 +272,24 @@ public class Parser
}
}
private VariableDeclarationNode ParseVariableDeclaration(int startIndex)
{
var name = ExpectIdentifier().Value;
var type = Optional<NubType>.Empty();
if (TryExpectSymbol(Symbol.Colon))
{
type = ParseType();
}
var value = Optional<ExpressionNode>.Empty();
if (TryExpectSymbol(Symbol.Assign))
{
value = ParseExpression();
}
return new VariableDeclarationNode(GetTokensForNode(startIndex), name, type, value);
}
private ReturnNode ParseReturn(int startIndex)
{
var value = Optional<ExpressionNode>.Empty();

View File

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

View File

@@ -0,0 +1,11 @@
using Nub.Lang.Frontend.Lexing;
using Nub.Lang.Frontend.Typing;
namespace Nub.Lang.Frontend.Parsing;
public class VariableDeclarationNode(IReadOnlyList<Token> tokens, string name, Optional<NubType> explicitType, Optional<ExpressionNode> value) : StatementNode(tokens)
{
public string Name { get; } = name;
public Optional<NubType> ExplicitType { get; } = explicitType;
public Optional<ExpressionNode> Value { get; } = value;
}

View File

@@ -108,8 +108,11 @@ public class TypeChecker
{
switch (statement)
{
case VariableAssignmentNode varAssign:
TypeCheckVariableAssignment(varAssign);
case VariableAssignmentNode variableAssignment:
TypeCheckVariableAssignment(variableAssignment);
break;
case VariableDeclarationNode variableDeclaration:
TypeCheckVariableVariableDeclaration(variableDeclaration);
break;
case FuncCallStatementNode funcCall:
TypeCheckFuncCall(funcCall.FuncCall, funcCall);
@@ -132,45 +135,59 @@ public class TypeChecker
}
}
private void TypeCheckVariableAssignment(VariableAssignmentNode varAssign)
private void TypeCheckVariableAssignment(VariableAssignmentNode variableAssignment)
{
var valueType = TypeCheckExpression(varAssign.Value);
var valueType = TypeCheckExpression(variableAssignment.Value);
if (valueType == null) return;
if (_variables.TryGetValue(varAssign.Name, out var existingVariable))
if (!_variables.TryGetValue(variableAssignment.Name, out var existingVariable))
{
if (varAssign.ExplicitType.HasValue)
{
if (!NubType.IsCompatibleWith(existingVariable, varAssign.ExplicitType.Value))
{
ReportError($"Explicit type '{varAssign.ExplicitType.Value}' on variable '{varAssign.Name}' is not compatible with declared type '{existingVariable}'", varAssign);
return;
}
}
ReportError($"Variable '{variableAssignment.Name}' is not declared", variableAssignment);
return;
}
if (!NubType.IsCompatibleWith(valueType, 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);
}
}
private void TypeCheckVariableVariableDeclaration(VariableDeclarationNode variableDeclaration)
{
NubType? type = null;
if (_variables.TryGetValue(variableDeclaration.Name, out var existingVariable))
{
ReportError($"Cannot redeclare variable '{existingVariable}'", variableDeclaration);
}
if (variableDeclaration.Value.HasValue)
{
var valueType = TypeCheckExpression(variableDeclaration.Value.Value);
if (valueType == null) return;
type = valueType;
}
if (variableDeclaration.ExplicitType.HasValue)
{
type = variableDeclaration.ExplicitType.Value;
}
if (variableDeclaration.ExplicitType.HasValue && variableDeclaration.Value.HasValue)
{
if (!NubType.IsCompatibleWith(variableDeclaration.ExplicitType.Value, variableDeclaration.Value.Value.Type))
{
ReportError($"Cannot assign expression of type '{valueType}' to variable '{varAssign.Name}' of type '{existingVariable}'", varAssign);
ReportError($"Cannot assign expression of type '{variableDeclaration.Value.Value.Type}' to variable '{variableDeclaration.Name}' with type '{variableDeclaration.ExplicitType.Value}'", variableDeclaration);
}
}
else
{
if (varAssign.ExplicitType.HasValue)
{
var explicitType = varAssign.ExplicitType.Value;
if (!NubType.IsCompatibleWith(valueType, explicitType))
{
ReportError($"Cannot assign expression of type '{valueType}' to variable '{varAssign.Name}' of type '{explicitType}'", varAssign);
return;
}
_variables[varAssign.Name] = explicitType;
}
else
{
_variables[varAssign.Name] = valueType;
}
if (type == null)
{
ReportError($"Cannot implicitly get type of variable '{variableDeclaration.Name}'", variableDeclaration);
return;
}
_variables[variableDeclaration.Name] = type;
}
private NubType? TypeCheckDereference(DereferenceNode dereference)