From 79ec5f9b0f32930aa0d1c76ceef662c2e80e11cf Mon Sep 17 00:00:00 2001 From: nub31 Date: Tue, 10 Jun 2025 17:49:44 +0200 Subject: [PATCH] ... --- .../Frontend/Generation/QBEGenerator.cs | 123 +++++++----------- .../Frontend/Generation/SymbolTable.cs | 103 +++++++++++++++ .../Parsing/Expressions/IdentifierNode.cs | 4 +- src/lang/Nub.Lang/Frontend/Parsing/Parser.cs | 2 +- .../Nub.Lang/Frontend/Typing/TypeChecker.cs | 20 +-- 5 files changed, 167 insertions(+), 85 deletions(-) create mode 100644 src/lang/Nub.Lang/Frontend/Generation/SymbolTable.cs diff --git a/src/lang/Nub.Lang/Frontend/Generation/QBEGenerator.cs b/src/lang/Nub.Lang/Frontend/Generation/QBEGenerator.cs index e863f6f..87698e8 100644 --- a/src/lang/Nub.Lang/Frontend/Generation/QBEGenerator.cs +++ b/src/lang/Nub.Lang/Frontend/Generation/QBEGenerator.cs @@ -16,24 +16,21 @@ public class QBEGenerator private List _sourceFiles = []; private StringBuilder _builder = new(); - private Dictionary _variables = []; private List _strings = []; private Stack _breakLabels = []; private Stack _continueLabels = []; private int _variableIndex; private int _labelIndex; private bool _codeIsReachable = true; - private Dictionary _funcNames = []; private Dictionary _anonymousFunctions = []; private int _anonymousFuncIndex; + private SymbolTable _symbolTable = new([]); public string Generate(List sourceFiles) { _sourceFiles = sourceFiles; _builder = new StringBuilder(); - _variables = new Dictionary(); _strings = []; - _funcNames = []; _breakLabels = []; _continueLabels = []; _anonymousFunctions = []; @@ -41,6 +38,7 @@ public class QBEGenerator _labelIndex = 0; _anonymousFuncIndex = 0; _codeIsReachable = true; + _symbolTable = new SymbolTable(_sourceFiles.SelectMany(f => f.Definitions.OfType())); foreach (var structDef in _sourceFiles.SelectMany(f => f.Definitions).OfType()) { @@ -48,32 +46,10 @@ public class QBEGenerator _builder.AppendLine(); } - foreach (var funcSignature in _sourceFiles.SelectMany(f => f.Definitions).OfType()) - { - switch (funcSignature) - { - case ExternFuncDefinitionNode externFuncDefinitionNode: - _funcNames[funcSignature] = "$" + externFuncDefinitionNode.CallName; - break; - case LocalFuncDefinitionNode localFuncDefinitionNode: - if (localFuncDefinitionNode.Exported) - { - _funcNames[funcSignature] = "$" + localFuncDefinitionNode.Name; - } - else - { - _funcNames[funcSignature] = "$" + localFuncDefinitionNode.Namespace + "_" + localFuncDefinitionNode.Name; - } - - break; - default: - throw new ArgumentOutOfRangeException(nameof(funcSignature)); - } - } - foreach (var funcDef in _sourceFiles.SelectMany(f => f.Definitions).OfType()) { - GenerateFuncDefinition(_funcNames[funcDef], funcDef.Parameters, funcDef.ReturnType, funcDef.Body, funcDef.Exported); + var symbol = _symbolTable.LookupFunc(funcDef.Namespace, funcDef.Name); + GenerateFuncDefinition(symbol.GeneratedName, funcDef.Parameters, funcDef.ReturnType, funcDef.Body, funcDef.Exported); _builder.AppendLine(); } @@ -316,7 +292,7 @@ public class QBEGenerator private void GenerateFuncDefinition(string name, List parameters, NubType returnType, BlockNode body, bool exported) { - _variables.Clear(); + _symbolTable.Reset(); if (exported) { @@ -410,7 +386,7 @@ public class QBEGenerator } } - _variables[parameter.Name] = parameterName; + _symbolTable.DeclareVariable(parameter.Name, parameterName); } GenerateBlock(body); @@ -548,10 +524,12 @@ public class QBEGenerator private void GenerateBlock(BlockNode block) { + _symbolTable.StartScope(); foreach (var statement in block.Statements.Where(_ => _codeIsReachable)) { GenerateStatement(statement); } + _symbolTable.EndScope(); _codeIsReachable = true; } @@ -629,13 +607,6 @@ public class QBEGenerator _builder.AppendLine(" ret"); } } - - private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment) - { - var value = GenerateExpression(variableAssignment.Value); - var pointer = _variables[variableAssignment.Identifier.Name]; - GenerateCopy(variableAssignment.Value.Type, value, pointer); - } private void GenerateVariableDeclaration(VariableDeclarationNode variableDeclaration) { @@ -651,10 +622,17 @@ public class QBEGenerator else { var pointerName = GenVarName(); - _variables[variableDeclaration.Name] = pointerName; + _symbolTable.DeclareVariable(variableDeclaration.Name, pointerName); } - _variables[variableDeclaration.Name] = pointer; + _symbolTable.DeclareVariable(variableDeclaration.Name, pointer); + } + + private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment) + { + var value = GenerateExpression(variableAssignment.Value); + var variable = _symbolTable.LookupVariable(variableAssignment.Identifier.Name); + GenerateCopy(variableAssignment.Value.Type, value, variable.Pointer); } private void GenerateWhile(WhileNode whileStatement) @@ -820,7 +798,11 @@ public class QBEGenerator case DereferenceNode dereference: return GenerateExpression(dereference.Expression); case IdentifierNode identifier: - return _variables[identifier.Name]; + if (identifier.Namespace != null) + { + throw new NotSupportedException("There is nothing to address in another namespace"); + } + return _symbolTable.LookupVariable(identifier.Name).Pointer; case MemberAccessNode memberAccess: return GenerateMemberAccessPointer(memberAccess); default: @@ -1056,26 +1038,19 @@ public class QBEGenerator private string GenerateIdentifier(IdentifierNode identifier) { - if (_variables.TryGetValue(identifier.Name, out var value)) + var symbol = _symbolTable.Lookup(identifier.Namespace, identifier.Name); + + switch (symbol) { - if (IsLargeType(identifier.Type)) - { - return value; - } - else - { - var result = GenVarName(); - _builder.AppendLine($" {result} {QBEAssign(identifier.Type)} {QBELoad(identifier.Type)} {value}"); - return result; - } - } - else - { - var funcName = _funcNames[LookupFuncSignature(identifier.Namespace, identifier.Name)]; - var pointer = GenVarName(); - _builder.AppendLine($" {pointer} =l alloc8 8"); - _builder.AppendLine($" storel {funcName}, {pointer}"); - return pointer; + case SymbolTable.Func func: + var pointer = GenVarName(); + _builder.AppendLine($" {pointer} =l alloc8 8"); + _builder.AppendLine($" storel {func.GeneratedName}, {pointer}"); + return pointer; + case SymbolTable.Variable variable: + return GenerateDereference(identifier.Type, variable.Pointer); + default: + throw new ArgumentOutOfRangeException(nameof(symbol)); } } @@ -1333,6 +1308,20 @@ public class QBEGenerator _builder.AppendLine($" {QBEStore(type)} {value}, {destinationPointer}"); } } + + private string GenerateDereference(NubType type, string pointer) + { + if (IsLargeType(type)) + { + return pointer; + } + else + { + var result = GenVarName(); + _builder.AppendLine($" {result} {QBEAssign(type)} {QBELoad(type)} {pointer}"); + return result; + } + } private string GenVarName() { @@ -1344,15 +1333,6 @@ public class QBEGenerator return $"@l{++_labelIndex}"; } - private IFuncSignature LookupFuncSignature(string @namespace, string name) - { - return _sourceFiles - .Where(f => f.Namespace == @namespace) - .SelectMany(f => f.Definitions) - .OfType() - .Single(f => f.Name == name); - } - private StructDefinitionNode LookupStructDefinition(string @namespace, string name) { return _sourceFiles @@ -1364,7 +1344,7 @@ public class QBEGenerator private int LookupMemberOffset(StructDefinitionNode structDefinition, string member) { - int offset = 0; + var offset = 0; foreach (var field in structDefinition.Fields) { @@ -1373,12 +1353,9 @@ public class QBEGenerator return offset; } - int fieldAlignment = AlignmentOf(field.Type); + var fieldAlignment = AlignmentOf(field.Type); - // Align offset for this field offset = AlignTo(offset, fieldAlignment); - - // Add field size offset += SizeOf(field.Type); } diff --git a/src/lang/Nub.Lang/Frontend/Generation/SymbolTable.cs b/src/lang/Nub.Lang/Frontend/Generation/SymbolTable.cs new file mode 100644 index 0000000..ea284ba --- /dev/null +++ b/src/lang/Nub.Lang/Frontend/Generation/SymbolTable.cs @@ -0,0 +1,103 @@ +using Nub.Lang.Frontend.Parsing.Definitions; + +namespace Nub.Lang.Frontend.Generation; + +public class SymbolTable +{ + private readonly List _functions = []; + private readonly Stack _variables = []; + private readonly Stack _scopes = []; + + public SymbolTable(IEnumerable functions) + { + foreach (var func in functions) + { + string name; + switch (func) + { + case ExternFuncDefinitionNode externFuncDefinitionNode: + { + name = "$" + externFuncDefinitionNode.CallName; + break; + } + case LocalFuncDefinitionNode localFuncDefinitionNode: + { + if (localFuncDefinitionNode.Exported) + { + name = "$" + localFuncDefinitionNode.Name; + } + else + { + name = "$" + localFuncDefinitionNode.Namespace + "_" + localFuncDefinitionNode.Name; + } + break; + } + default: + { + throw new ArgumentOutOfRangeException(nameof(func)); + } + } + _functions.Add(new Func(func.Namespace, func.Name, name)); + } + } + + public void Reset() + { + _variables.Clear(); + } + + public void StartScope() + { + _scopes.Push(_variables.Count); + } + + public void EndScope() + { + var count = _scopes.Pop(); + while (count > _variables.Count) + { + _variables.Pop(); + } + } + + public Symbol Lookup(string? @namespace, string name) + { + if (@namespace == null) + { + return LookupVariable(name); + } + + return LookupFunc(@namespace, name); + } + + public Func LookupFunc(string @namespace, string name) + { + return _functions.Single(x => x.Name == name); + } + + public Variable LookupVariable(string name) + { + return _variables.Single(x => x.Name == name); + } + + public void DeclareVariable(string name, string pointer) + { + _variables.Push(new Variable(name, pointer)); + } + + public abstract class Symbol(string name) + { + public string Name { get; } = name; + } + + public class Variable(string name, string pointer) : Symbol(name) + { + public string Pointer { get; set; } = pointer; + } + + public class Func(string @namespace, string name, string generatedName) : Symbol(name) + { + public string Namespace { get; } = @namespace; + public string GeneratedName { get; } = generatedName; + } +} \ No newline at end of file diff --git a/src/lang/Nub.Lang/Frontend/Parsing/Expressions/IdentifierNode.cs b/src/lang/Nub.Lang/Frontend/Parsing/Expressions/IdentifierNode.cs index 5ccc392..cb51da0 100644 --- a/src/lang/Nub.Lang/Frontend/Parsing/Expressions/IdentifierNode.cs +++ b/src/lang/Nub.Lang/Frontend/Parsing/Expressions/IdentifierNode.cs @@ -2,9 +2,9 @@ namespace Nub.Lang.Frontend.Parsing.Expressions; -public class IdentifierNode(IReadOnlyList tokens, string @namespace, string name) : LValueNode(tokens) +public class IdentifierNode(IReadOnlyList tokens, string? @namespace, string name) : LValueNode(tokens) { - public string Namespace { get; } = @namespace; + public string? Namespace { get; } = @namespace; public string Name { get; } = name; public override string ToString() => Name; diff --git a/src/lang/Nub.Lang/Frontend/Parsing/Parser.cs b/src/lang/Nub.Lang/Frontend/Parsing/Parser.cs index 1912b45..c7e4d91 100644 --- a/src/lang/Nub.Lang/Frontend/Parsing/Parser.cs +++ b/src/lang/Nub.Lang/Frontend/Parsing/Parser.cs @@ -422,7 +422,7 @@ public class Parser } case IdentifierToken identifier: { - var @namespace = _namespace; + string? @namespace = null; var name = identifier.Value; if (TryExpectSymbol(Symbol.DoubleColon)) { diff --git a/src/lang/Nub.Lang/Frontend/Typing/TypeChecker.cs b/src/lang/Nub.Lang/Frontend/Typing/TypeChecker.cs index 07f5822..f370490 100644 --- a/src/lang/Nub.Lang/Frontend/Typing/TypeChecker.cs +++ b/src/lang/Nub.Lang/Frontend/Typing/TypeChecker.cs @@ -447,24 +447,26 @@ public class TypeChecker private NubType? TypeCheckIdentifier(IdentifierNode identifier) { - var result = _variables.GetValueOrDefault(identifier.Name); - - if (result == null) + if (identifier.Namespace == null) { - var func = LookupFuncSignature(identifier.Namespace, identifier.Name); - if (func != null) + var result = _variables.GetValueOrDefault(identifier.Name); + if (result == null) { - result = new NubFuncType(func.ReturnType, func.Parameters.Select(p => p.Type).ToList()); + ReportError($"Variable '{identifier.Name}' is not defined", identifier); + return null; } + + return result; } - if (result == null) + var func = LookupFuncSignature(identifier.Namespace, identifier.Name); + if (func == null) { ReportError($"Identifier '{identifier.Name}' is not defined", identifier); return null; } - - return result; + + return new NubFuncType(func.ReturnType, func.Parameters.Select(p => p.Type).ToList()); } private NubType? TypeCheckAddressOf(AddressOfNode addressOf)