From 8a11eabd9661767ac3879378fc48b6e4f115f855 Mon Sep 17 00:00:00 2001 From: "Tristan B. Velloza Kildaire" Date: Mon, 17 Jul 2023 16:16:18 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=A7=A0=20Feature/Meta:=20Cloneable=20(rou?= =?UTF-8?q?nd=201)=20(#21)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Data - Moved AST manipulation imports to the top - Made `VariableAssignment` cloneable - Made `Variable` cloneable * Data - When trying to `clone()` an `Expression`, do a runtime type check to check if we can (else `null` is used) * Expressions - Moved AST-manipulation related import to the top * Expressions - `BinaryOperatorExpression` now overrides `clone()` from `MCloneable` * Expressions - `IntegerLiteral` now implements `MCloneable` * Expressions - `CastedExpression` now implements `clone()` for `MCloneable` * Containers - Moved all AST maniuplation-related imports to the top * Containers - Made `Struct` support `MCloneable`'s `clone()` method * Data - Only clone the `VariableAssignment` if the `Variable` has one * Containers (unit test) - Added test which tests the `clone()` on a `Struct` --- source/tlang/compiler/symbols/containers.d | 90 ++++++++++++++++++++- source/tlang/compiler/symbols/data.d | 73 ++++++++++++++++- source/tlang/compiler/symbols/expressions.d | 81 +++++++++++++++++-- 3 files changed, 233 insertions(+), 11 deletions(-) diff --git a/source/tlang/compiler/symbols/containers.d b/source/tlang/compiler/symbols/containers.d index 00d2125..9d264b9 100644 --- a/source/tlang/compiler/symbols/containers.d +++ b/source/tlang/compiler/symbols/containers.d @@ -4,6 +4,9 @@ import tlang.compiler.symbols.data; import std.conv : to; import tlang.compiler.symbols.typing.core; +// AST manipulation interfaces +import tlang.compiler.symbols.mcro : MStatementSearchable, MStatementReplaceable, MCloneable; + /** * Used so often that we may as well * declare it once @@ -30,7 +33,7 @@ public Statement[] weightReorder(Statement[] statements) } // TODO: Honestly all contains should be a kind-of `MStatementSearchable` and `MStatementReplaceable` -import tlang.compiler.symbols.mcro : MStatementSearchable, MStatementReplaceable; +// AND MCloneable public interface Container : MStatementSearchable, MStatementReplaceable { public void addStatement(Statement statement); @@ -147,7 +150,7 @@ public class Module : Entity, Container * that are Variables (TODO: Enforce in parser) * TODO: Possibly enforce here too */ -public class Struct : Type, Container +public class Struct : Type, Container, MCloneable { private Statement[] statements; @@ -243,6 +246,41 @@ public class Struct : Type, Container return false; } } + + /** + * Clones this struct recursively returning a + * fresh copy of all its members and the struct + * itself. + * + * Returns: the cloned `Statement` + */ + public override Statement clone() + { + Struct clonedStruct = new Struct(this.name); + + /** + * Clone all the statements and re-parent them + * to the clone + */ + Statement[] clonedStatements; + foreach(Statement curStmt; this.getStatements()) + { + Statement clonedStmt; + if(cast(MCloneable)curStmt) + { + MCloneable cloneableCurStmt = cast(MCloneable)curStmt; + clonedStmt = cloneableCurStmt.clone(); + } + + // Re-parent to the cloned struct + clonedStmt.parentTo(clonedStruct); + + // Add it to the cloned struct's body + clonedStruct.addStatement(clonedStmt); + } + + return clonedStruct; + } } public class Clazz : Type, Container @@ -362,4 +400,52 @@ public class Clazz : Type, Container } } +} + + +/** + * Test the `MCloneable`-ity support of `Struct` + * which has two `Variable` members (therefore + * also testing the `clone()` on `Variable`) + */ +unittest +{ + Struct original = new Struct("User"); + Variable originalVar_Name = new Variable("byte*", "name"); + Variable originalVar_Age = new Variable("int", "age"); + originalVar_Name.parentTo(original); + originalVar_Age.parentTo(original); + original.addStatement(originalVar_Name); + original.addStatement(originalVar_Age); + + // Now clone it + Struct cloned = cast(Struct)original.clone(); + + // Cloned version should differ + assert(cloned !is original); + + // Cloned statements versus original statements + Statement[] clonedStmts = cloned.getStatements(); + Statement[] originalStmts = original.getStatements(); + assert(clonedStmts[0] !is originalStmts[0]); + assert(clonedStmts[1] !is originalStmts[1]); + + // Compare the variables (members) of both + Variable origStruct_MemberOne = cast(Variable)originalStmts[0]; + Variable origStruct_MemberTwo = cast(Variable)originalStmts[1]; + Variable clonedStruct_MemberOne = cast(Variable)clonedStmts[0]; + Variable clonedStruct_MemberTwo = cast(Variable)clonedStmts[1]; + assert(origStruct_MemberOne !is clonedStruct_MemberOne); + assert(origStruct_MemberTwo !is clonedStruct_MemberTwo); + assert(originalVar_Name.getName() == clonedStruct_MemberOne.getName()); // Names should match + assert(origStruct_MemberTwo.getName() == clonedStruct_MemberTwo.getName()); // Names should match + + // Ensure re-parenting is correct + assert(origStruct_MemberOne.parentOf() == original); + assert(origStruct_MemberTwo.parentOf() == original); + assert(clonedStruct_MemberOne.parentOf() == cloned); + assert(clonedStruct_MemberTwo.parentOf() == cloned); + + // TODO: Make this more deeper this test as a few + // ... more things were left out that can be checked } \ No newline at end of file diff --git a/source/tlang/compiler/symbols/data.d b/source/tlang/compiler/symbols/data.d index 6987ec6..7bcd655 100644 --- a/source/tlang/compiler/symbols/data.d +++ b/source/tlang/compiler/symbols/data.d @@ -4,6 +4,11 @@ public import tlang.compiler.symbols.check; import std.conv : to; import tlang.compiler.typecheck.dependency.core : Context; +// For debug printing +import gogga; + +// AST manipulation interfaces +import tlang.compiler.symbols.mcro : MStatementSearchable, MStatementReplaceable, MCloneable; /** * TODO: Implement the blow and use them @@ -438,7 +443,7 @@ public class Function : TypedEntity, Container } } -public class Variable : TypedEntity, MStatementSearchable, MStatementReplaceable +public class Variable : TypedEntity, MStatementSearchable, MStatementReplaceable, MCloneable { /* TODO: Just make this an Expression */ private VariableAssignment assignment; @@ -509,6 +514,39 @@ public class Variable : TypedEntity, MStatementSearchable, MStatementReplaceable return assignment.replace(thiz, that); } } + + /** + * Clones this variable declaration recursively + * including its assigned value (`VariableAssignment`) + * if any. + * + * Returns: the cloned `Statement` + */ + public override Statement clone() + { + Variable clonedVarDec; + + // If there's an assignment, then clone it + VariableAssignment clonedVarAss = null; + if(this.assignment) + { + // Clone the assignment + clonedVarAss = cast(VariableAssignment)this.assignment.clone(); + } + + + // Create new variable with same name and identifier + clonedVarDec = new Variable(this.type, this.name); + + // Copy all properties across (TODO: Make sure we didn't miss any) + clonedVarDec.accessorType = this.accessorType; + clonedVarDec.isExternalEntity = this.isExternalEntity; + clonedVarDec.assignment = clonedVarAss; + clonedVarDec.container = this.container; + + + return clonedVarDec; + } } @@ -521,7 +559,7 @@ public import tlang.compiler.symbols.expressions; /** * TODO: Rename to `VariableDeclarationAssignment` */ -public class VariableAssignment : Statement, MStatementSearchable, MStatementReplaceable +public class VariableAssignment : Statement, MStatementSearchable, MStatementReplaceable, MCloneable { private Expression expression; private Variable variable; @@ -600,6 +638,35 @@ public class VariableAssignment : Statement, MStatementSearchable, MStatementRep return false; } } + + /** + * Clones this variable assignment by recursively cloning + * the fields within (TODO: finish description) + * + * Returns: the cloned `Statement` + */ + public override Statement clone() + { + // FIXME: Investigate if `Variable`? Must be cloned + // ... would cuase infinite recursion and it isn't + // ... reaslly a part of the AST (just a helper) + // ... hence I do not believe it needs to be cloned + // (If for some reason the association eneds to be) + // ... updted then `Variable`'s `clone()' can call + /// ... `setvariable(clonedVarDec)` (with itself) + + // Clone the expression (if supported, TODO: throw an error if not) + Expression clonedExpression = null; + if(cast(MCloneable)this.expression) + { + MCloneable cloneableExpression = cast(MCloneable)this.expression; + clonedExpression = cast(Expression)cloneableExpression.clone(); + } + + VariableAssignment clonedVarAss = new VariableAssignment(clonedExpression); + + return clonedVarAss; + } } /** @@ -1508,8 +1575,6 @@ public final class Branch : Entity, Container } } -import tlang.compiler.symbols.mcro : MStatementSearchable, MStatementReplaceable; - public final class DiscardStatement : Statement, MStatementSearchable, MStatementReplaceable { private Expression expression; diff --git a/source/tlang/compiler/symbols/expressions.d b/source/tlang/compiler/symbols/expressions.d index 64f6c2f..b403798 100644 --- a/source/tlang/compiler/symbols/expressions.d +++ b/source/tlang/compiler/symbols/expressions.d @@ -3,6 +3,9 @@ module tlang.compiler.symbols.expressions; import tlang.compiler.symbols.data; import std.conv : to; +// AST manipulation interfaces +import tlang.compiler.symbols.mcro : MStatementSearchable, MStatementReplaceable, MCloneable; + /* TODO: Look into arrays later */ public class StringExpression : Expression { @@ -56,9 +59,7 @@ public class UnaryOperatorExpression : OperatorExpression } } -import tlang.compiler.symbols.mcro : MStatementSearchable, MStatementReplaceable; - -public class BinaryOperatorExpression : OperatorExpression, MStatementSearchable, MStatementReplaceable +public class BinaryOperatorExpression : OperatorExpression, MStatementSearchable, MStatementReplaceable, MCloneable { private Expression lhs, rhs; @@ -151,6 +152,39 @@ public class BinaryOperatorExpression : OperatorExpression, MStatementSearchable return false; } } + + /** + * Clones this binery operator expression recursively + * returning a fresh new copy of itself and its + * left and right operands + * + * Returns: the cloned `Statement` + */ + public override Statement clone() + { + BinaryOperatorExpression clonedBinaryOp; + + // Clone the left-hand operand expression (if supported, TODO: throw an error if not) + Expression clonedLeftOperandExpression = null; + if(cast(MCloneable)this.lhs) + { + MCloneable cloneableExpression = cast(MCloneable)this.lhs; + clonedLeftOperandExpression = cast(Expression)cloneableExpression.clone(); + } + + // Clone the left-hand operand expression (if supported, TODO: throw an error if not) + Expression clonedRightOperandExpression = null; + if(cast(MCloneable)this.rhs) + { + MCloneable cloneableExpression = cast(MCloneable)this.rhs; + clonedRightOperandExpression = cast(Expression)cloneableExpression.clone(); + } + + // Clone ourselves + clonedBinaryOp = new BinaryOperatorExpression(this.operator, clonedLeftOperandExpression, clonedRightOperandExpression); + + return clonedBinaryOp; + } } public enum IntegerLiteralEncoding @@ -161,7 +195,7 @@ public enum IntegerLiteralEncoding UNSIGNED_LONG } -public class IntegerLiteral : NumberLiteral +public class IntegerLiteral : NumberLiteral, MCloneable { private IntegerLiteralEncoding encoding; @@ -180,6 +214,20 @@ public class IntegerLiteral : NumberLiteral { return "[integerLiteral: "~numberLiteral~" ("~to!(string)(encoding)~")]"; } + + /** + * Clones this integer literal + * + * Returns: the cloned `Statement` + */ + public override Statement clone() + { + IntegerLiteral clonedIntegerLiteral; + + clonedIntegerLiteral = new IntegerLiteral(this.numberLiteral, this.encoding); + + return clonedIntegerLiteral; + } } //TODO: Work on floating point literal encodings @@ -238,7 +286,7 @@ public final class NewExpression : Expression } } -public final class CastedExpression : Expression +public final class CastedExpression : Expression, MCloneable { private Expression uncastedExpression; private string toType; @@ -258,6 +306,29 @@ public final class CastedExpression : Expression { return uncastedExpression; } + + /** + * Clones this casted expression recursively + * and returns a fresh copy of it + * + * Returns: the cloned `Statement` + */ + public override Statement clone() + { + CastedExpression clonedCastedExpression; + + // Clone the uncasted expression (if supported, TODO: throw an error if not) + Expression clonedUncastedExpression = null; + if(cast(MCloneable)this.uncastedExpression) + { + MCloneable cloneableExpression = cast(MCloneable)this.uncastedExpression; + clonedUncastedExpression = cast(Expression)cloneableExpression.clone(); + } + + clonedCastedExpression = new CastedExpression(this.toType, clonedUncastedExpression); + + return clonedCastedExpression; + } } public final class ArrayIndex : Expression