🧠 Feature/Meta: Cloneable (round 1) (#21)

* 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`
This commit is contained in:
Tristan B. Velloza Kildaire 2023-07-17 16:16:18 +02:00 committed by GitHub
parent f1aaaf1088
commit 8a11eabd96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 233 additions and 11 deletions

View File

@ -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
}

View File

@ -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;

View File

@ -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