From 343d515f3742af297b3724896b52ab29a642e7da Mon Sep 17 00:00:00 2001 From: nub31 Date: Mon, 8 Sep 2025 20:22:44 +0200 Subject: [PATCH] Simplify addressing --- example/src/main.nub | 2 +- .../NubLang/Generation/QBE/QBEGenerator.cs | 156 +++++++++--------- .../TypeChecking/Node/ExpressionNode.cs | 39 +++-- .../TypeChecking/Node/StatementNode.cs | 2 +- .../NubLang/TypeChecking/TypeChecker.cs | 54 ++++-- 5 files changed, 143 insertions(+), 110 deletions(-) diff --git a/example/src/main.nub b/example/src/main.nub index 84e3a9e..1cb3710 100644 --- a/example/src/main.nub +++ b/example/src/main.nub @@ -19,7 +19,7 @@ struct Human : Printable func main(args: []cstring): i64 { - let x: Printable = struct Human { + let x = struct Human { name = "Oliver" age = 23 } diff --git a/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs b/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs index 46ab1d0..ca3fa99 100644 --- a/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs +++ b/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs @@ -230,7 +230,7 @@ public class QBEGenerator return; } - var value = EmitUnwrap(EmitExpression(source)); + var value = EmitExpression(source); if (source.Type.IsSimpleType(out var simpleType, out var complexType)) { @@ -269,7 +269,7 @@ public class QBEGenerator case InterfaceInitializerNode: case LiteralNode { Kind: LiteralKind.String }: { - destination = EmitUnwrap(EmitExpression(source)); + destination = EmitExpression(source); return true; } } @@ -286,7 +286,7 @@ public class QBEGenerator return uncopiedValue; } - var value = EmitUnwrap(EmitExpression(source)); + var value = EmitExpression(source); if (source.Type.IsSimpleType(out _, out var complexType)) { @@ -508,13 +508,7 @@ public class QBEGenerator private void EmitAssignment(AssignmentNode assignment) { - if (!assignment.Target.IsLValue) - { - throw new UnreachableException("Destination of assignment must be an lvalue. This should have been caught in the type checker"); - } - - var destination = EmitExpression(assignment.Target); - EmitCopyIntoOrInitialize(assignment.Value, destination.Name); + EmitCopyIntoOrInitialize(assignment.Value, EmitAddressOfLValue(assignment.Target)); } private void EmitBreak() @@ -535,7 +529,7 @@ public class QBEGenerator var falseLabel = LabelName(); var endLabel = LabelName(); - var result = EmitUnwrap(EmitExpression(ifStatement.Condition)); + var result = EmitExpression(ifStatement.Condition); _writer.Indented($"jnz {result}, {trueLabel}, {falseLabel}"); _writer.WriteLine(trueLabel); EmitBlock(ifStatement.Body); @@ -543,11 +537,7 @@ public class QBEGenerator _writer.WriteLine(falseLabel); if (ifStatement.Else.HasValue) { - ifStatement.Else.Value.Match - ( - elseIfNode => EmitIf(elseIfNode), - elseNode => EmitBlock(elseNode) - ); + ifStatement.Else.Value.Match(EmitIf, EmitBlock); } _writer.WriteLine(endLabel); @@ -557,7 +547,7 @@ public class QBEGenerator { if (@return.Value.HasValue) { - var result = EmitUnwrap(EmitExpression(@return.Value.Value)); + var result = EmitExpression(@return.Value.Value); _writer.Indented($"ret {result}"); } else @@ -591,7 +581,7 @@ public class QBEGenerator _writer.WriteLine(iterationLabel); EmitBlock(whileStatement.Body); _writer.WriteLine(conditionLabel); - var result = EmitUnwrap(EmitExpression(whileStatement.Condition)); + var result = EmitExpression(whileStatement.Condition); _writer.Indented($"jnz {result}, {iterationLabel}, {endLabel}"); _writer.WriteLine(endLabel); @@ -599,21 +589,22 @@ public class QBEGenerator _breakLabels.Pop(); } - private Val EmitExpression(ExpressionNode expression) + private string EmitExpression(ExpressionNode expression) { - var value = expression switch + return expression switch { ArrayInitializerNode arrayInitializer => EmitArrayInitializer(arrayInitializer), StructInitializerNode structInitializer => EmitStructInitializer(structInitializer), AddressOfNode addressOf => EmitAddressOf(addressOf), DereferenceNode dereference => EmitDereference(dereference), - BinaryExpressionNode binaryExpression => EmitBinaryExpression(binaryExpression), - FuncCallNode funcCallExpression => EmitFuncCall(funcCallExpression), + BinaryExpressionNode binary => EmitBinaryExpression(binary), + FuncCallNode funcCall => EmitFuncCall(funcCall), InterfaceFuncCallNode interfaceFuncCall => EmitInterfaceFuncCall(interfaceFuncCall), InterfaceInitializerNode interfaceInitializer => EmitInterfaceInitializer(interfaceInitializer), ExternFuncIdentNode externFuncIdent => EmitExternFuncIdent(externFuncIdent), LocalFuncIdentNode localFuncIdent => EmitLocalFuncIdent(localFuncIdent), VariableIdentNode variableIdent => EmitVariableIdent(variableIdent), + FuncParameterIdentNode funcParameterIdent => EmitFuncParameterIdent(funcParameterIdent), LiteralNode literal => EmitLiteral(literal), UnaryExpressionNode unaryExpression => EmitUnaryExpression(unaryExpression), StructFieldAccessNode structFieldAccess => EmitStructFieldAccess(structFieldAccess), @@ -621,27 +612,14 @@ public class QBEGenerator ArrayIndexAccessNode arrayIndex => EmitArrayIndexAccess(arrayIndex), _ => throw new ArgumentOutOfRangeException(nameof(expression)) }; - - return new Val(value, expression.Type, expression.IsLValue); } private string EmitArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccess) { - var array = EmitUnwrap(EmitExpression(arrayIndexAccess.Target)); - var index = EmitUnwrap(EmitExpression(arrayIndexAccess.Index)); - - EmitArraysCheck(array, index); - - var elementType = ((ArrayTypeNode)arrayIndexAccess.Target.Type).ElementType; - - var pointer = TmpName(); - _writer.Indented($"{pointer} =l mul {index}, {SizeOf(elementType)}"); - _writer.Indented($"{pointer} =l add {pointer}, 8"); - _writer.Indented($"{pointer} =l add {array}, {pointer}"); - return pointer; + return EmitLoad(arrayIndexAccess.Type, EmitAddressOfArrayIndexAccess(arrayIndexAccess)); } - private void EmitArraysCheck(string array, string index) + private void EmitArrayBoundsCheck(string array, string index) { var count = TmpName(); _writer.Indented($"{count} =l loadl {array}"); @@ -667,7 +645,7 @@ public class QBEGenerator private string EmitArrayInitializer(ArrayInitializerNode arrayInitializer) { - var capacity = EmitUnwrap(EmitExpression(arrayInitializer.Capacity)); + var capacity = EmitExpression(arrayInitializer.Capacity); var elementSize = SizeOf(arrayInitializer.ElementType); var capacityInBytes = TmpName(); @@ -688,24 +666,65 @@ public class QBEGenerator private string EmitDereference(DereferenceNode dereference) { - return EmitLoad(dereference.Type, EmitUnwrap(EmitExpression(dereference.Expression))); + return EmitLoad(dereference.Type, EmitExpression(dereference.Expression)); } private string EmitAddressOf(AddressOfNode addressOf) { - var value = EmitExpression(addressOf.Expression); - if (!value.IsLValue) - { - throw new UnreachableException("Tried to take address of rvalue. This should have been caught in the type checker"); - } + return EmitAddressOfLValue(addressOf.LValue); + } - return value.Name; + private string EmitAddressOfLValue(LValueExpressionNode addressOf) + { + return addressOf switch + { + ArrayIndexAccessNode arrayIndexAccess => EmitAddressOfArrayIndexAccess(arrayIndexAccess), + ArrayInitializerNode arrayInitializer => EmitArrayInitializer(arrayInitializer), + InterfaceInitializerNode interfaceInitializer => EmitInterfaceInitializer(interfaceInitializer), + StructFieldAccessNode structFieldAccess => EmitAddressOfStructFieldAccess(structFieldAccess), + StructInitializerNode structInitializer => EmitStructInitializer(structInitializer), + VariableIdentNode variableIdent => EmitAddressOfVariableIdent(variableIdent), + _ => throw new ArgumentOutOfRangeException(nameof(addressOf)) + }; + } + + private string EmitAddressOfArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccess) + { + var array = EmitExpression(arrayIndexAccess.Target); + var index = EmitExpression(arrayIndexAccess.Index); + + EmitArrayBoundsCheck(array, index); + + var elementType = ((ArrayTypeNode)arrayIndexAccess.Target.Type).ElementType; + + var address = TmpName(); + _writer.Indented($"{address} =l mul {index}, {SizeOf(elementType)}"); + _writer.Indented($"{address} =l add {address}, 8"); + _writer.Indented($"{address} =l add {array}, {address}"); + return address; + } + + private string EmitAddressOfStructFieldAccess(StructFieldAccessNode structFieldAccess) + { + var target = EmitExpression(structFieldAccess.Target); + + var structDef = _definitionTable.LookupStruct(structFieldAccess.StructType.Name); + var offset = OffsetOf(structDef, structFieldAccess.Field); + + var address = TmpName(); + _writer.Indented($"{address} =l add {target}, {offset}"); + return address; + } + + private string EmitAddressOfVariableIdent(VariableIdentNode variableIdent) + { + return "%" + variableIdent.Name; } private string EmitBinaryExpression(BinaryExpressionNode binaryExpression) { - var left = EmitUnwrap(EmitExpression(binaryExpression.Left)); - var right = EmitUnwrap(EmitExpression(binaryExpression.Right)); + var left = EmitExpression(binaryExpression.Left); + var right = EmitExpression(binaryExpression.Right); var outputName = TmpName(); @@ -811,19 +830,22 @@ public class QBEGenerator private string EmitExternFuncIdent(ExternFuncIdentNode externFuncIdent) { - var func = _definitionTable.LookupExternFunc(externFuncIdent.Name); - return ExternFuncName(func); + return ExternFuncName(_definitionTable.LookupExternFunc(externFuncIdent.Name)); } private string EmitLocalFuncIdent(LocalFuncIdentNode localFuncIdent) { - var func = _definitionTable.LookupLocalFunc(localFuncIdent.Name); - return LocalFuncName(func); + return LocalFuncName(_definitionTable.LookupLocalFunc(localFuncIdent.Name)); } private string EmitVariableIdent(VariableIdentNode variableIdent) { - return variableIdent.Name; + return EmitLoad(variableIdent.Type, EmitAddressOfVariableIdent(variableIdent)); + } + + private string EmitFuncParameterIdent(FuncParameterIdentNode funcParameterIdent) + { + return "%" + funcParameterIdent.Name; } private string EmitLiteral(LiteralNode literal) @@ -941,7 +963,7 @@ public class QBEGenerator private string EmitUnaryExpression(UnaryExpressionNode unaryExpression) { - var operand = EmitUnwrap(EmitExpression(unaryExpression.Operand)); + var operand = EmitExpression(unaryExpression.Operand); var outputName = TmpName(); switch (unaryExpression.Operator) @@ -988,15 +1010,7 @@ public class QBEGenerator private string EmitStructFieldAccess(StructFieldAccessNode structFieldAccess) { - var target = EmitUnwrap(EmitExpression(structFieldAccess.Target)); - - var structDef = _definitionTable.LookupStruct(structFieldAccess.StructType.Name); - var offset = OffsetOf(structDef, structFieldAccess.Field); - - var output = TmpName(); - _writer.Indented($"{output} =l add {target}, {offset}"); - - return output; + return EmitLoad(structFieldAccess.Type, EmitAddressOfStructFieldAccess(structFieldAccess)); } private string EmitStructFuncCall(StructFuncCallNode structFuncCall) @@ -1004,7 +1018,7 @@ public class QBEGenerator var structDef = _definitionTable.LookupStruct(structFuncCall.StructType.Name); var func = StructFuncName(structDef.Name, structFuncCall.Name); - var thisParameter = EmitUnwrap(EmitExpression(structFuncCall.StructExpression)); + var thisParameter = EmitExpression(structFuncCall.StructExpression); List parameterStrings = [$"l {thisParameter}"]; @@ -1029,7 +1043,7 @@ public class QBEGenerator private string EmitInterfaceFuncCall(InterfaceFuncCallNode interfaceFuncCall) { - var target = EmitUnwrap(EmitExpression(interfaceFuncCall.InterfaceExpression)); + var target = EmitExpression(interfaceFuncCall.InterfaceExpression); var interfaceDef = _definitionTable.LookupInterface(interfaceFuncCall.InterfaceType.Name); var functionIndex = interfaceDef.Functions.ToList().FindIndex(x => x.Name == interfaceFuncCall.Name); @@ -1070,7 +1084,7 @@ public class QBEGenerator private string EmitInterfaceInitializer(InterfaceInitializerNode interfaceInitializer, string? destination = null) { - var implementation = EmitUnwrap(EmitExpression(interfaceInitializer.Implementation)); + var implementation = EmitExpression(interfaceInitializer.Implementation); var vtableOffset = 0; foreach (var interfaceImplementation in interfaceInitializer.StructType.InterfaceImplementations) @@ -1102,7 +1116,7 @@ public class QBEGenerator private string EmitFuncCall(FuncCallNode funcCall) { - var expression = EmitExpression(funcCall.Expression); + var funcPointer = EmitExpression(funcCall.Expression); var parameterStrings = new List(); @@ -1112,7 +1126,6 @@ public class QBEGenerator parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {copy}"); } - var funcPointer = EmitUnwrap(expression); if (funcCall.Type is VoidTypeNode) { _writer.Indented($"call {funcPointer}({string.Join(", ", parameterStrings)})"); @@ -1126,11 +1139,6 @@ public class QBEGenerator } } - private string EmitUnwrap(Val val) - { - return val.IsLValue ? EmitLoad(val.Type, val.Name) : val.Name; - } - private static int SizeOf(TypeNode type) { return type switch @@ -1293,6 +1301,4 @@ public class CStringLiteral(string value, string name) { public string Value { get; } = value; public string Name { get; } = name; -} - -public record Val(string Name, TypeNode Type, bool IsLValue); \ No newline at end of file +} \ No newline at end of file diff --git a/src/compiler/NubLang/TypeChecking/Node/ExpressionNode.cs b/src/compiler/NubLang/TypeChecking/Node/ExpressionNode.cs index 8978f5d..03f629c 100644 --- a/src/compiler/NubLang/TypeChecking/Node/ExpressionNode.cs +++ b/src/compiler/NubLang/TypeChecking/Node/ExpressionNode.cs @@ -22,36 +22,41 @@ public enum BinaryOperator Divide } -public abstract record ExpressionNode(TypeNode Type, bool IsLValue) : Node; +public abstract record ExpressionNode(TypeNode Type) : Node; -public record BinaryExpressionNode(TypeNode Type, ExpressionNode Left, BinaryOperator Operator, ExpressionNode Right) : ExpressionNode(Type, false); +public abstract record LValueExpressionNode(TypeNode Type) : RValueExpressionNode(Type); +public abstract record RValueExpressionNode(TypeNode Type) : ExpressionNode(Type); -public record UnaryExpressionNode(TypeNode Type, UnaryOperator Operator, ExpressionNode Operand) : ExpressionNode(Type, false); +public record BinaryExpressionNode(TypeNode Type, ExpressionNode Left, BinaryOperator Operator, ExpressionNode Right) : RValueExpressionNode(Type); -public record FuncCallNode(TypeNode Type, ExpressionNode Expression, IReadOnlyList Parameters) : ExpressionNode(Type, false); +public record UnaryExpressionNode(TypeNode Type, UnaryOperator Operator, ExpressionNode Operand) : RValueExpressionNode(Type); -public record StructFuncCallNode(TypeNode Type, string Name, StructTypeNode StructType, ExpressionNode StructExpression, IReadOnlyList Parameters) : ExpressionNode(Type, false); +public record FuncCallNode(TypeNode Type, ExpressionNode Expression, IReadOnlyList Parameters) : RValueExpressionNode(Type); -public record InterfaceFuncCallNode(TypeNode Type, string Name, InterfaceTypeNode InterfaceType, ExpressionNode InterfaceExpression, IReadOnlyList Parameters) : ExpressionNode(Type, false); +public record StructFuncCallNode(TypeNode Type, string Name, StructTypeNode StructType, ExpressionNode StructExpression, IReadOnlyList Parameters) : RValueExpressionNode(Type); -public record VariableIdentNode(TypeNode Type, string Name) : ExpressionNode(Type, true); +public record InterfaceFuncCallNode(TypeNode Type, string Name, InterfaceTypeNode InterfaceType, ExpressionNode InterfaceExpression, IReadOnlyList Parameters) : RValueExpressionNode(Type); -public record LocalFuncIdentNode(TypeNode Type, string Name) : ExpressionNode(Type, false); +public record VariableIdentNode(TypeNode Type, string Name) : LValueExpressionNode(Type); -public record ExternFuncIdentNode(TypeNode Type, string Name) : ExpressionNode(Type, false); +public record FuncParameterIdentNode(TypeNode Type, string Name) : RValueExpressionNode(Type); -public record ArrayInitializerNode(TypeNode Type, ExpressionNode Capacity, TypeNode ElementType) : ExpressionNode(Type, true); +public record LocalFuncIdentNode(TypeNode Type, string Name) : RValueExpressionNode(Type); -public record ArrayIndexAccessNode(TypeNode Type, ExpressionNode Target, ExpressionNode Index) : ExpressionNode(Type, true); +public record ExternFuncIdentNode(TypeNode Type, string Name) : RValueExpressionNode(Type); -public record AddressOfNode(TypeNode Type, ExpressionNode Expression) : ExpressionNode(Type, false); +public record ArrayInitializerNode(TypeNode Type, ExpressionNode Capacity, TypeNode ElementType) : LValueExpressionNode(Type); -public record LiteralNode(TypeNode Type, string Value, LiteralKind Kind) : ExpressionNode(Type, false); +public record ArrayIndexAccessNode(TypeNode Type, ExpressionNode Target, ExpressionNode Index) : LValueExpressionNode(Type); -public record StructFieldAccessNode(TypeNode Type, StructTypeNode StructType, ExpressionNode Target, string Field) : ExpressionNode(Type, true); +public record AddressOfNode(TypeNode Type, LValueExpressionNode LValue) : RValueExpressionNode(Type); -public record StructInitializerNode(StructTypeNode StructType, Dictionary Initializers) : ExpressionNode(StructType, true); +public record LiteralNode(TypeNode Type, string Value, LiteralKind Kind) : RValueExpressionNode(Type); -public record DereferenceNode(TypeNode Type, ExpressionNode Expression) : ExpressionNode(Type, false); +public record StructFieldAccessNode(TypeNode Type, StructTypeNode StructType, ExpressionNode Target, string Field) : LValueExpressionNode(Type); -public record InterfaceInitializerNode(TypeNode Type, InterfaceTypeNode InterfaceType, StructTypeNode StructType, ExpressionNode Implementation) : ExpressionNode(Type, true); \ No newline at end of file +public record StructInitializerNode(StructTypeNode StructType, Dictionary Initializers) : LValueExpressionNode(StructType); + +public record DereferenceNode(TypeNode Type, ExpressionNode Expression) : RValueExpressionNode(Type); + +public record InterfaceInitializerNode(TypeNode Type, InterfaceTypeNode InterfaceType, StructTypeNode StructType, ExpressionNode Implementation) : LValueExpressionNode(Type); \ No newline at end of file diff --git a/src/compiler/NubLang/TypeChecking/Node/StatementNode.cs b/src/compiler/NubLang/TypeChecking/Node/StatementNode.cs index 6bf764a..bd58462 100644 --- a/src/compiler/NubLang/TypeChecking/Node/StatementNode.cs +++ b/src/compiler/NubLang/TypeChecking/Node/StatementNode.cs @@ -6,7 +6,7 @@ public record StatementExpressionNode(ExpressionNode Expression) : StatementNode public record ReturnNode(Optional Value) : StatementNode; -public record AssignmentNode(ExpressionNode Target, ExpressionNode Value) : StatementNode; +public record AssignmentNode(LValueExpressionNode Target, ExpressionNode Value) : StatementNode; public record IfNode(ExpressionNode Condition, BlockNode Body, Optional> Else) : StatementNode; diff --git a/src/compiler/NubLang/TypeChecking/TypeChecker.cs b/src/compiler/NubLang/TypeChecking/TypeChecker.cs index af4baee..5f3adea 100644 --- a/src/compiler/NubLang/TypeChecking/TypeChecker.cs +++ b/src/compiler/NubLang/TypeChecking/TypeChecker.cs @@ -94,10 +94,10 @@ public sealed class TypeChecker { var scope = new Scope(); - scope.Declare(new Variable("this", GetStructType(node))); + scope.Declare(new Identifier("this", GetStructType(node), IdentifierKind.FunctionParameter)); foreach (var parameter in func.Signature.Parameters) { - scope.Declare(new Variable(parameter.Name, CheckType(parameter.Type))); + scope.Declare(new Identifier(parameter.Name, CheckType(parameter.Type), IdentifierKind.FunctionParameter)); } _funcReturnTypes.Push(CheckType(func.Signature.ReturnType)); @@ -150,7 +150,7 @@ public sealed class TypeChecker var scope = new Scope(); foreach (var parameter in signature.Parameters) { - scope.Declare(new Variable(parameter.Name, parameter.Type)); + scope.Declare(new Identifier(parameter.Name, parameter.Type, IdentifierKind.FunctionParameter)); } _funcReturnTypes.Push(signature.ReturnType); @@ -178,9 +178,14 @@ public sealed class TypeChecker private StatementNode CheckAssignment(AssignmentSyntax statement) { - var expression = CheckExpression(statement.Target); - var value = CheckExpression(statement.Value, expression.Type); - return new AssignmentNode(expression, value); + var target = CheckExpression(statement.Target); + if (target is not LValueExpressionNode targetLValue) + { + throw new TypeCheckerException(Diagnostic.Error("Cannot assign to rvalue").Build()); + } + + var value = CheckExpression(statement.Value, target.Type); + return new AssignmentNode(targetLValue, value); } private IfNode CheckIf(IfSyntax statement) @@ -252,7 +257,7 @@ public sealed class TypeChecker throw new TypeCheckerException(Diagnostic.Error($"Unknown type of variable {statement.Name}").Build()); } - Scope.Declare(new Variable(statement.Name, type)); + Scope.Declare(new Identifier(statement.Name, type, IdentifierKind.Variable)); return new VariableDeclarationNode(statement.Name, assignment, type); } @@ -306,7 +311,13 @@ public sealed class TypeChecker private AddressOfNode CheckAddressOf(AddressOfSyntax expression) { var inner = CheckExpression(expression.Expression); - return new AddressOfNode(new PointerTypeNode(inner.Type), inner); + + if (inner is not LValueExpressionNode lValueInner) + { + throw new TypeCheckerException(Diagnostic.Error("Cannot take address of rvalue").Build()); + } + + return new AddressOfNode(new PointerTypeNode(inner.Type), lValueInner); } private ArrayIndexAccessNode CheckArrayIndexAccess(ArrayIndexAccessSyntax expression) @@ -434,10 +445,15 @@ public sealed class TypeChecker private ExpressionNode CheckIdentifier(IdentifierSyntax expression) { - var variable = Scope.Lookup(expression.Name); - if (variable != null) + var identifier = Scope.Lookup(expression.Name); + if (identifier != null) { - return new VariableIdentNode(variable.Type, variable.Name); + return identifier.Kind switch + { + IdentifierKind.Variable => new VariableIdentNode(identifier.Type, identifier.Name), + IdentifierKind.FunctionParameter => new FuncParameterIdentNode(identifier.Type, identifier.Name), + _ => throw new ArgumentOutOfRangeException() + }; } var localFuncs = _definitionTable.LookupLocalFunc(expression.Name).ToArray(); @@ -768,13 +784,19 @@ public sealed class TypeChecker } } -public record Variable(string Name, TypeNode Type); +public enum IdentifierKind +{ + Variable, + FunctionParameter +} + +public record Identifier(string Name, TypeNode Type, IdentifierKind Kind); public class Scope(Scope? parent = null) { - private readonly List _variables = []; + private readonly List _variables = []; - public Variable? Lookup(string name) + public Identifier? Lookup(string name) { var variable = _variables.FirstOrDefault(x => x.Name == name); if (variable != null) @@ -785,9 +807,9 @@ public class Scope(Scope? parent = null) return parent?.Lookup(name); } - public void Declare(Variable variable) + public void Declare(Identifier identifier) { - _variables.Add(variable); + _variables.Add(identifier); } public Scope SubScope()