Variable declarations seperate from assignment
This commit is contained in:
@@ -3,9 +3,9 @@ namespace main
|
|||||||
/// # Documentation
|
/// # Documentation
|
||||||
/// ## Documentation subtitle
|
/// ## Documentation subtitle
|
||||||
export func main(args: []string) {
|
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)
|
c::printf("%d\n", args.count)
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ using Nub.Lang.Frontend.Typing;
|
|||||||
|
|
||||||
if (args.Length != 1)
|
if (args.Length != 1)
|
||||||
{
|
{
|
||||||
Console.Error.WriteLine("Usage: nub <input-dir>");
|
Console.Error.WriteLine("Usage: nub <input-dir>");
|
||||||
Console.Error.WriteLine("Example: nub src");
|
Console.Error.WriteLine("Example: nub src");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -296,8 +296,6 @@ public class Generator
|
|||||||
_builder.AppendLine($"({string.Join(", ", parameterStrings)}) {{");
|
_builder.AppendLine($"({string.Join(", ", parameterStrings)}) {{");
|
||||||
_builder.AppendLine("@start");
|
_builder.AppendLine("@start");
|
||||||
|
|
||||||
_builder.AppendLine(" # Variable allocation");
|
|
||||||
|
|
||||||
foreach (var parameter in node.Parameters)
|
foreach (var parameter in node.Parameters)
|
||||||
{
|
{
|
||||||
var parameterName = parameter.Name;
|
var parameterName = parameter.Name;
|
||||||
@@ -334,9 +332,6 @@ public class Generator
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_builder.AppendLine(" # End variable allocation");
|
|
||||||
_builder.AppendLine();
|
|
||||||
|
|
||||||
GenerateBlock(node.Body);
|
GenerateBlock(node.Body);
|
||||||
|
|
||||||
if (node.Body.Statements.LastOrDefault() is not ReturnNode)
|
if (node.Body.Statements.LastOrDefault() is not ReturnNode)
|
||||||
@@ -386,6 +381,9 @@ public class Generator
|
|||||||
case VariableAssignmentNode variableAssignment:
|
case VariableAssignmentNode variableAssignment:
|
||||||
GenerateVariableAssignment(variableAssignment);
|
GenerateVariableAssignment(variableAssignment);
|
||||||
break;
|
break;
|
||||||
|
case VariableDeclarationNode variableDeclaration:
|
||||||
|
GenerateVariableDeclaration(variableDeclaration);
|
||||||
|
break;
|
||||||
case WhileNode whileStatement:
|
case WhileNode whileStatement:
|
||||||
GenerateWhile(whileStatement);
|
GenerateWhile(whileStatement);
|
||||||
break;
|
break;
|
||||||
@@ -505,23 +503,32 @@ 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}");
|
||||||
|
}
|
||||||
|
|
||||||
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
|
else
|
||||||
{
|
{
|
||||||
var pointerName = GenVarName();
|
_builder.AppendLine($" storel 0, %{pointerName}");
|
||||||
_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
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_variables[variableDeclaration.Name] = new Variable
|
||||||
|
{
|
||||||
|
Pointer = $"%{pointerName}",
|
||||||
|
Type = type
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateWhile(WhileNode whileStatement)
|
private void GenerateWhile(WhileNode whileStatement)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ public class Lexer
|
|||||||
["return"] = Symbol.Return,
|
["return"] = Symbol.Return,
|
||||||
["new"] = Symbol.New,
|
["new"] = Symbol.New,
|
||||||
["struct"] = Symbol.Struct,
|
["struct"] = Symbol.Struct,
|
||||||
|
["let"] = Symbol.Let,
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly Dictionary<string, Modifier> Modifiers = new()
|
private static readonly Dictionary<string, Modifier> Modifiers = new()
|
||||||
|
|||||||
@@ -44,4 +44,5 @@ public enum Symbol
|
|||||||
Ampersand,
|
Ampersand,
|
||||||
DoubleColon,
|
DoubleColon,
|
||||||
Namespace,
|
Namespace,
|
||||||
|
Let
|
||||||
}
|
}
|
||||||
@@ -192,11 +192,12 @@ public class Parser
|
|||||||
Symbol.Return => ParseReturn(startIndex),
|
Symbol.Return => ParseReturn(startIndex),
|
||||||
Symbol.If => ParseIf(startIndex),
|
Symbol.If => ParseIf(startIndex),
|
||||||
Symbol.While => ParseWhile(startIndex),
|
Symbol.While => ParseWhile(startIndex),
|
||||||
|
Symbol.Let => ParseVariableDeclaration(startIndex),
|
||||||
Symbol.Break => new BreakNode(GetTokensForNode(startIndex)),
|
Symbol.Break => new BreakNode(GetTokensForNode(startIndex)),
|
||||||
Symbol.Continue => new ContinueNode(GetTokensForNode(startIndex)),
|
Symbol.Continue => new ContinueNode(GetTokensForNode(startIndex)),
|
||||||
_ => throw new ParseException(Diagnostic
|
_ => throw new ParseException(Diagnostic
|
||||||
.Error($"Unexpected symbol '{symbol.Symbol}' at start of statement")
|
.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)
|
.At(symbol)
|
||||||
.Build())
|
.Build())
|
||||||
};
|
};
|
||||||
@@ -258,14 +259,7 @@ public class Parser
|
|||||||
case Symbol.Assign:
|
case Symbol.Assign:
|
||||||
{
|
{
|
||||||
var value = ParseExpression();
|
var value = ParseExpression();
|
||||||
return new VariableAssignmentNode(GetTokensForNode(startIndex), identifier.Value, Optional<NubType>.Empty(), value);
|
return new VariableAssignmentNode(GetTokensForNode(startIndex), identifier.Value, value);
|
||||||
}
|
|
||||||
case Symbol.Colon:
|
|
||||||
{
|
|
||||||
var type = ParseType();
|
|
||||||
ExpectSymbol(Symbol.Assign);
|
|
||||||
var value = ParseExpression();
|
|
||||||
return new VariableAssignmentNode(GetTokensForNode(startIndex), identifier.Value, type, value);
|
|
||||||
}
|
}
|
||||||
default:
|
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)
|
private ReturnNode ParseReturn(int startIndex)
|
||||||
{
|
{
|
||||||
var value = Optional<ExpressionNode>.Empty();
|
var value = Optional<ExpressionNode>.Empty();
|
||||||
|
|||||||
@@ -3,9 +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, 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 string Name { get; } = name;
|
||||||
public Optional<NubType> ExplicitType { get; } = explicitType;
|
|
||||||
public ExpressionNode Value { get; } = value;
|
public ExpressionNode Value { get; } = value;
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -28,7 +28,7 @@ public class TypeChecker
|
|||||||
{
|
{
|
||||||
ReportError($"Extern function '{funcName}' has been declared more than once", funcName);
|
ReportError($"Extern function '{funcName}' has been declared more than once", funcName);
|
||||||
}
|
}
|
||||||
|
|
||||||
var exportedLocalFuncDefinitions = _sourceFiles
|
var exportedLocalFuncDefinitions = _sourceFiles
|
||||||
.SelectMany(f => f.Definitions)
|
.SelectMany(f => f.Definitions)
|
||||||
.OfType<LocalFuncDefinitionNode>()
|
.OfType<LocalFuncDefinitionNode>()
|
||||||
@@ -39,7 +39,7 @@ public class TypeChecker
|
|||||||
{
|
{
|
||||||
ReportError($"Exported function '{funcName}' has been declared more than once", funcName);
|
ReportError($"Exported function '{funcName}' has been declared more than once", funcName);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var structDef in _sourceFiles.SelectMany(f => f.Definitions).OfType<StructDefinitionNode>())
|
foreach (var structDef in _sourceFiles.SelectMany(f => f.Definitions).OfType<StructDefinitionNode>())
|
||||||
{
|
{
|
||||||
TypeCheckStructDef(structDef);
|
TypeCheckStructDef(structDef);
|
||||||
@@ -49,7 +49,7 @@ public class TypeChecker
|
|||||||
{
|
{
|
||||||
TypeCheckFuncDef(funcDef);
|
TypeCheckFuncDef(funcDef);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DiagnosticsResult(_diagnostics);
|
return new DiagnosticsResult(_diagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,8 +108,11 @@ public class TypeChecker
|
|||||||
{
|
{
|
||||||
switch (statement)
|
switch (statement)
|
||||||
{
|
{
|
||||||
case VariableAssignmentNode varAssign:
|
case VariableAssignmentNode variableAssignment:
|
||||||
TypeCheckVariableAssignment(varAssign);
|
TypeCheckVariableAssignment(variableAssignment);
|
||||||
|
break;
|
||||||
|
case VariableDeclarationNode variableDeclaration:
|
||||||
|
TypeCheckVariableVariableDeclaration(variableDeclaration);
|
||||||
break;
|
break;
|
||||||
case FuncCallStatementNode funcCall:
|
case FuncCallStatementNode funcCall:
|
||||||
TypeCheckFuncCall(funcCall.FuncCall, 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 (valueType == null) return;
|
||||||
|
|
||||||
if (_variables.TryGetValue(varAssign.Name, out var existingVariable))
|
if (!_variables.TryGetValue(variableAssignment.Name, out var existingVariable))
|
||||||
{
|
{
|
||||||
if (varAssign.ExplicitType.HasValue)
|
ReportError($"Variable '{variableAssignment.Name}' is not declared", variableAssignment);
|
||||||
{
|
return;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!NubType.IsCompatibleWith(valueType, existingVariable))
|
|
||||||
{
|
|
||||||
ReportError($"Cannot assign expression of type '{valueType}' to variable '{varAssign.Name}' of type '{existingVariable}'", varAssign);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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;
|
if (!NubType.IsCompatibleWith(variableAssignment.Value.Type, existingVariable))
|
||||||
}
|
{
|
||||||
else
|
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))
|
||||||
{
|
{
|
||||||
_variables[varAssign.Name] = valueType;
|
ReportError($"Cannot assign expression of type '{variableDeclaration.Value.Value.Type}' to variable '{variableDeclaration.Name}' with type '{variableDeclaration.ExplicitType.Value}'", variableDeclaration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type == null)
|
||||||
|
{
|
||||||
|
ReportError($"Cannot implicitly get type of variable '{variableDeclaration.Name}'", variableDeclaration);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_variables[variableDeclaration.Name] = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
private NubType? TypeCheckDereference(DereferenceNode dereference)
|
private NubType? TypeCheckDereference(DereferenceNode dereference)
|
||||||
@@ -196,7 +213,7 @@ public class TypeChecker
|
|||||||
if (parameterType == null) return null;
|
if (parameterType == null) return null;
|
||||||
parameterTypes.Add(parameterType);
|
parameterTypes.Add(parameterType);
|
||||||
}
|
}
|
||||||
|
|
||||||
var funcDefinition = LookupFuncSignature(funcCall.Namespace, funcCall.Name, parameterTypes);
|
var funcDefinition = LookupFuncSignature(funcCall.Namespace, funcCall.Name, parameterTypes);
|
||||||
if (funcDefinition == null)
|
if (funcDefinition == null)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user