module tlang.compiler.symbols.containers; 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 * * TODO: Check if we could do it with interfaces? */ public Statement[] weightReorder(Statement[] statements) { import std.algorithm.sorting : sort; import std.algorithm.mutation : SwapStrategy; /* Re-ordered by lowest wieght first */ Statement[] stmntsRed; /* Comparator for Statement objects */ bool wCmp(Statement lhs, Statement rhs) { return lhs.weight < rhs.weight; } stmntsRed = sort!(wCmp, SwapStrategy.stable)(statements).release; return stmntsRed; } // TODO: Honestly all contains should be a kind-of `MStatementSearchable` and `MStatementReplaceable` // AND MCloneable public interface Container : MStatementSearchable, MStatementReplaceable { public void addStatement(Statement statement); public void addStatements(Statement[] statements); public Statement[] getStatements(); } public class Module : Entity, Container { this(string moduleName) { super(moduleName); } private Statement[] statements; public void addStatement(Statement statement) { this.statements ~= statement; } public void addStatements(Statement[] statements) { this.statements ~= statements; } public Statement[] getStatements() { return weightReorder(statements); } public override Statement[] search(TypeInfo_Class clazzType) { /* List of returned matches */ Statement[] matches; /* Are we (ourselves) of this type? */ if(clazzType.isBaseOf(this.classinfo)) { matches ~= [this]; } /* Recurse on each `Statement` making up our body */ // NOTE: Using weight-reordered? Is that fine? foreach(Statement curStmt; getStatements()) { MStatementSearchable curStmtCasted = cast(MStatementSearchable)curStmt; if(curStmtCasted) { matches ~= curStmtCasted.search(clazzType); } } return matches; } public override bool replace(Statement thiz, Statement that) { /* If we (`this`) are `thiz`, then we cannot replace */ if(this == thiz) { return false; } /* If not ourself, then check the body statements */ else { /** * First check each `Statement` that make sup our * body and see if we can replace that, else see * if we can recurse on each of the body statements * and apply replacement therein */ // NOTE: Using weight-reordered? Is that fine? Statement[] bodyStmts = getStatements(); for(ulong idx = 0; idx < bodyStmts.length; idx++) { Statement curBodyStmt = bodyStmts[idx]; /* Should we directly replace the Statement in the body? */ if(curBodyStmt == thiz) { // Replace the statement in the body statements[idx] = that; // Re-parent `that` to us that.parentTo(this); return true; } /* If we cannot, then recurse (try) on it */ else if(cast(MStatementReplaceable)curBodyStmt) { MStatementReplaceable curBodyStmtRepl = cast(MStatementReplaceable)curBodyStmt; if(curBodyStmtRepl.replace(thiz, that)) { return true; } } } return false; } } } /** * Struct * * A Struct can only contain Entity's * that are Variables (TODO: Enforce in parser) * TODO: Possibly enforce here too */ public class Struct : Type, Container, MCloneable { private Statement[] statements; public void addStatement(Statement statement) { this.statements ~= statement; } public void addStatements(Statement[] statements) { this.statements ~= statements; } public Statement[] getStatements() { return weightReorder(statements); } this(string name) { super(name); } public override Statement[] search(TypeInfo_Class clazzType) { /* List of returned matches */ Statement[] matches; /* Are we (ourselves) of this type? */ if(clazzType.isBaseOf(this.classinfo)) { matches ~= [this]; } /* Recurse on each `Statement` making up our body */ // NOTE: Using weight-reordered? Is that fine? foreach(Statement curStmt; getStatements()) { MStatementSearchable curStmtCasted = cast(MStatementSearchable)curStmt; if(curStmtCasted) { matches ~= curStmtCasted.search(clazzType); } } return matches; } public override bool replace(Statement thiz, Statement that) { /* If we (`this`) are `thiz`, then we cannot replace */ if(this == thiz) { return false; } /* If not ourself, then check the body statements */ else { /** * First check each `Statement` that make sup our * body and see if we can replace that, else see * if we can recurse on each of the body statements * and apply replacement therein */ // NOTE: Using weight-reordered? Is that fine? Statement[] bodyStmts = getStatements(); for(ulong idx = 0; idx < bodyStmts.length; idx++) { Statement curBodyStmt = bodyStmts[idx]; /* Should we directly replace the Statement in the body? */ if(curBodyStmt == thiz) { // Replace the statement in the body statements[idx] = that; // Re-parent `that` to us that.parentTo(this); return true; } /* If we cannot, then recurse (try) on it */ else if(cast(MStatementReplaceable)curBodyStmt) { MStatementReplaceable curBodyStmtRepl = cast(MStatementReplaceable)curBodyStmt; if(curBodyStmtRepl.replace(thiz, that)) { return true; } } } return false; } } /** * Clones this struct recursively returning a * fresh copy of all its members and the struct * itself. * * Param: * newParent = the `Container` to re-parent the * cloned `Statement`'s self to * * Returns: the cloned `Statement` */ public override Statement clone(Container newParent = null) { 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); } // Parent ourselves to the given parent clonedStruct.parentTo(newParent); return clonedStruct; } } public class Clazz : Type, Container { private Statement[] statements; private string[] interfacesClasses; this(string name) { super(name); /* Weighted as 0 */ weight = 0; } public void addInherit(string[] l) { interfacesClasses ~= l; } public string[] getInherit() { return interfacesClasses; } public override string toString() { return "Class (Name: "~name~", Parents (Class/Interfaces): "~to!(string)(interfacesClasses)~")"; } public void addStatement(Statement statement) { this.statements ~= statement; } public void addStatements(Statement[] statements) { this.statements ~= statements; } public Statement[] getStatements() { return weightReorder(statements); } public override Statement[] search(TypeInfo_Class clazzType) { /* List of returned matches */ Statement[] matches; /* Are we (ourselves) of this type? */ if(clazzType.isBaseOf(this.classinfo)) { matches ~= [this]; } /* Recurse on each `Statement` making up our body */ // NOTE: Using weight-reordered? Is that fine? foreach(Statement curStmt; getStatements()) { MStatementSearchable curStmtCasted = cast(MStatementSearchable)curStmt; if(curStmtCasted) { matches ~= curStmtCasted.search(clazzType); } } return matches; } public override bool replace(Statement thiz, Statement that) { /* If we (`this`) are `thiz`, then we cannot replace */ if(this == thiz) { return false; } /* If not ourself, then check the body statements */ else { /** * First check each `Statement` that make sup our * body and see if we can replace that, else see * if we can recurse on each of the body statements * and apply replacement therein */ // NOTE: Using weight-reordered? Is that fine? Statement[] bodyStmts = getStatements(); for(ulong idx = 0; idx < bodyStmts.length; idx++) { Statement curBodyStmt = bodyStmts[idx]; /* Should we directly replace the Statement in the body? */ if(curBodyStmt == thiz) { // Replace the statement in the body statements[idx] = that; // Re-parent `that` to us that.parentTo(this); return true; } /* If we cannot, then recurse (try) on it */ else if(cast(MStatementReplaceable)curBodyStmt) { MStatementReplaceable curBodyStmtRepl = cast(MStatementReplaceable)curBodyStmt; if(curBodyStmtRepl.replace(thiz, that)) { return true; } } } return false; } } } /** * 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 }