// For reproducing emacs issue of slow response in C# mode. // Ignore any swearing. using Antlr4.Runtime.Tree; // faq - need this for ParseTreeProperty using System; using System.Collections.Generic; using System.Text; using static LDB.LDButils; using static LDB.TypesBroadly; using static LDB.InstFullyOrPartly; using static LDB.errorHandling; using static LDB.SymTab; using System.Linq; using System.Diagnostics.CodeAnalysis; // where from? why? - todo using static LDB.globals; // Caused trouble but was generally worth it; caught a few bugs and // will probably catch a few more. Yep, definitely worth it! // // // Certain methods/properties (e.g. the name property) were declared // in abstract superclasses but when I added '#nullable enable' they // started reporting nullability errors. I could either disable those // with "= null!", totally defeating the point, or deal with them // properly by pulling those methods/properties down into the concrete // subclasses, which was more verbose and in some sense less // encapsulated. In the main, I chose to do that to get static // guarantees. #nullable enable /* This module is called expressions because that's where I started, and SQL at heart is an expression language, however there will be non-expression constructs such as if and loops, and they go here as well. */ namespace LDB { public interface IhandlePrefixes where T : LDBRoot { public bool isPrefixed { get; } public bool isExcessPrefixed { get; } // The T in addPrefix/rePrefix/withoutPrefix is because we may be // returning eg. a list of items with prefixes // thus modified, not a single PMQIdent public T addPrefix(PMQIdent pfx); public T rePrefix(PMQIdent newpfx); // public T stripPrefix(PMQIdent id); } // todo - move this public abstract class LDBRoot { // Root of everything LDB-ish public bool Equals([AllowNull] LDBRoot other) { throw new LDBShouldNotBeImplementedException( "called Equals on LDBRoot"); } public override bool Equals([AllowNull] Object other) { throw new LDBShouldNotBeImplementedException( "called Equals in Object"); } public override int GetHashCode() { throw new LDBShouldNotBeImplementedException( "called GetHashCode in LDBRoot"); } public static bool operator ==(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called == in LDBRoot"); } public static bool operator !=(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called != in LDBRoot"); } public virtual string className { // todo - this needs checking it works as I want // also rename to ldbclassname -- todo get => this.GetType().FullName ?? "(unknown)"; } public virtual string classNameQ => "'" + className + "'"; public virtual string classNamePlus(string plus) => className + "." + plus; public override string ToString() { // This implicit to-string conversion crap cost me a few hours, so at least // do this & blow up at runtime. Would prefer compile time but it seems // not possible. This turns out to have been a good idea, // saved me quite a bit more debugging! // // Will try _validated; set { // hardMust(!validated, "... already validated... "); - no // It's hard to avoid validating a thing twice, so for now allow it // Look into it later, really shouldn't happen _validated = true; } } public LDBStructsChildren kids { get; private set; } = new LDBStructsChildren(noParent); // just easier public LDBASTobj() { //////////////////////////////////////////////////////////////// todo dead? // VStudio thinks this has zero references, which is true explicitly, but it's // called implicitly on every obj creation - or should do todo- check if (recordObjectsCreated) { AllCreatedItems.Add(this); } } public virtual void validate() { if (reportAlsoIfValidationOK) { debugmsg("validating..." + className + " (" + asNameOrIdentContents() + ")"); } mhp_mhk(); kids.validate(); // validated = true; -- No, get each subclass to set // validated. That forces an explicit call to base() which // is more code but ok. } public bool isValidated { get => validated; } // todo just => validated; public virtual bool isTopLevelItemSeq => false; public virtual void mhp_mhk() { // don't think this need be virtual - todo? // = must have parent, must have kids. The kids may be the // empty set and the parent may be noParent but they must // exist. // Why not just inline this into validate()? // Because a few classes need bespoke validation, but still must check this. hardMustParentIsPresent(); kids.eachKidParentMustBe(this); } public string asNameOrIdentContents() { // for very crude debugging var optName = this is IisNamedItem ni ? ni.name.toRawStr() : ""; optName = (optName == "" && this is PMQIdent) ? this.toRawStr() : ""; var optName2 = optName + " (uid:" + uid.ToString() + ") " + getFirstFewCharsOf(toRawStr()); return optName2; } public virtual void getAllUIDs(UIDset uids) { uids.Add(this); kids.ForEach(namedKid => namedKid.kid.getAllUIDs(uids)); } public bool isStubParent { get => (this is NoParent) || (this is FakeParent); } public void validateSpecificItems(params LDBASTobj[] items) { Array.ForEach(items, item => item.validate()); } public T? optClimbTo() where T : LDBASTobj { var resItem = this; while (!(resItem is T)) { if (resItem.parent.isStubParent) { return null; } resItem = resItem.parent; } var res = resItem as T; return res; } public T reqClimbTo() where T : LDBASTobj { var loc = className + ".reqClimbTo()"; var res = optClimbTo(); hardMust2(!(res is null), // Thanks SO // Todo - use this elsewhere, poss ExpectException? todo () => loc + nl + ", climbed to top, didn't find type " + typeof(T).FullName + nl + "Hierarchy is: " + nl + getItemWithParents(this)); if (res is null) { throw new NNQC(); } return res; } public void hardMustParentIsPresent() { hardMust2(!(parent is null), () => "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust2(!(this.parent is NoParent), () => "Parent is NoParent on object of type\n" + $"'{this.className}'"); } public void hardMustParentIsAbsent() { // For checking cloning - parent should be NoParent immediately // after, before it's set by parent, and never null hardMust(!(parent is null), "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust((this.parent is NoParent), "Parent should be NoParent on object of type\n" + $"'{this.className}'"); } public bool hasParent

() { hardMust(!(this is NoParent), "Item is NoParent"); return this.parent is P; } public bool hasAncestor() { hardMust(!this.isStubParent, "Item is NoParent"); // how to do with a switch expr? if (this.parent.isStubParent /* is NoParent || this.parent is FakeParent*/ ) { // todo do as x.hasFudgedParent() - todo return false; } else if (this.parent is A) { return true; } else { return this.parent.hasAncestor(); } } public bool notHasAncestor() => !hasAncestor(); public void setMyParentTo(LDBASTobj prnt) { // An object may only set its parent if its current parent is // the dummy 'noParent', which almost every object's parent is // set to on creation (see LDBASTobj parent property def). // The test for null is peculiar given this. Because // noParent is created statically, setting the parent of // noParent is difficult, perhaps impossible, to do statically // and reliably.Despite the declaration '#nullable enable', it // gets created with a null parrent (quite reasonably), hence // this check.The actual setting of the parent has to be done // at runtime i.e.after the static creation of 'noParent'. hardMust2((this.parent is NoParent) || (this.parent is null), () => "tried to re-set parent on object of type: " + nl + this.classNameQ + nl + "Original parent type is: " + (this?.parent?.classNameQ == null ? "null" : this.parent.classNameQ) + nl + "and new parent type is: " + prnt.classNameQ + nl + "Item having parent set on it is: " // because object may not be fully constructed yet, check howver TODO this is a major hack and must be fixed sometime. // + nl + (this is null ? "(null)" : this.toRawStr())); + nl + this.toRawStr() ); parent = prnt; } public void forceNewParentForRootObject() { // Every object must have some parent, including the // root object of the parse, which is LDBitems // (a sequence of LDB items), so this forces one. // It is only for that purpose! // // LDBitems obj exists for sub objs such as the statements // in a while loop, in a proc, in an if... etc., for // which their parent is the while/proc/if etc. hardMustParentIsAbsent(); this.parent = fakeParent; } //public void resetMyParentTo(LDBASTobj prnt) { ////////////////////////////////// destroy!!!!!! todo // parent = prnt; //} //////////////////////// xxxxxxxxxxxxx delete - todo //private void setParentOnKid(LDBASTobj kid) => // todo needed? // // check parente doesn't exist - todo // kid.parent = this; public virtual void setParentOnKidsAndMakeKidsDict( // todo - and add to kids dict // this mixes up setting parent and making child dict. Not good. - todo params (string kidName, LDBASTobj kid)[] kidsIn) { var loc = classNamePlus("setParentOnKidsAndMakeKidsDict"); var kidsToAdd = new LDBStructsChildren(this, kidsIn); hardMust2(kids.isEmpty, () => $"setting kids on parent '{className}'" + "when kids is not empty, " + $"kids is ({kids.Count} kids):" + nl + kids.toDebugStr() + nl + $"Trying to add\n{kidsToAdd.toDebugStr()}\n" + $"in {className}"); // if there is an LDBStructsChildren and another child, // this will blow up - will need to do kids.Add or // something - in fact, kids needs to be created in one // go, which is what happens. kids = kidsToAdd; ///////////////////////////////////////////////////////// why this? todo Array.ForEach(kidsIn, xkid => hardMust(!(xkid.kid is null), loc, "null kid")); Array.ForEach(kidsIn, xkid => xkid.kid.setMyParentTo(this)); } public virtual void setParentOnKidsAndAddToKidsDict( params (string kidName, LDBASTobj kid)[] kidsIn) { var kidsToAdd_justForErrReporting = new LDBStructsChildren(this, kidsIn); hardMust2(!kids.isEmpty, // may disable this later () => "adding to kids when kids is empty, " + $"kids is\n{kids.toDebugStr()}\n" + $"Trying to add\n{kidsToAdd_justForErrReporting.toDebugStr()}\n" + $"in {className}"); Array.ForEach(kidsIn, xkid => { // setParentOnKid(xkid.kid); xkid.kid.setMyParentTo(this); kids.Add(xkid); }); } public virtual IdentsUsed getAllIDs() { /* This is overridden only in TemplatedAgg, and that's only to deliberately fail. There's something wrong there, todo - un-virtualise this and remove the one override. - todo */ var iu = new IdentsUsed(); this._getAllIDs(iu); return iu; } public virtual void _getAllIDs(IdentsUsed iu) { // Gets all the identifiers explicitly used (read or written) ----- todo update this // anywhere in the AST. Does not get idents which are // declarations ie. parameters, or var/const declarations // unless these are combined with an assignment. // // Implied idents such as those from tbl.* are implicit and ignored. // // Root caller supplies new, empty iu // need usage here - todo docs// kids._getAllIDs(iu); } public abstract bool canGetAllIdentsUsed { // This seems to be true on most classes so I could set true // as a default here and override it where it's not wanted, saving // a chunk of code, but I'd rather make it explicit to force me to consider each case. A bit more C&P for less chance of bugs. get; } // Produces a string that should be identical to (excepting // whitespaces) the input ie. emit LDB code not valid SQL // // ToString was causing too many weird problems (due to my // inexperience with c#) so just used toRawStr() public abstract string toRawStr(); public override string toDebugStr() => toRawStr(); } // end LDBASTobj public interface IhandlePrefixes where T : LDBRoot { public bool isPrefixed { get; } public bool isExcessPrefixed { get; } // The T in addPrefix/rePrefix/withoutPrefix is because we may be // returning eg. a list of items with prefixes // thus modified, not a single PMQIdent public T addPrefix(PMQIdent pfx); public T rePrefix(PMQIdent newpfx); // public T stripPrefix(PMQIdent id); } // todo - move this public abstract class LDBRoot { // Root of everything LDB-ish public bool Equals([AllowNull] LDBRoot other) { throw new LDBShouldNotBeImplementedException( "called Equals on LDBRoot"); } public override bool Equals([AllowNull] Object other) { throw new LDBShouldNotBeImplementedException( "called Equals in Object"); } public override int GetHashCode() { throw new LDBShouldNotBeImplementedException( "called GetHashCode in LDBRoot"); } public static bool operator ==(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called == in LDBRoot"); } public static bool operator !=(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called != in LDBRoot"); } public virtual string className { // todo - this needs checking it works as I want // also rename to ldbclassname -- todo get => this.GetType().FullName ?? "(unknown)"; } public virtual string classNameQ => "'" + className + "'"; public virtual string classNamePlus(string plus) => className + "." + plus; public override string ToString() { // This implicit to-string conversion crap cost me a few hours, so at least // do this & blow up at runtime. Would prefer compile time but it seems // not possible. This turns out to have been a good idea, // saved me quite a bit more debugging! // // Will try _validated; set { // hardMust(!validated, "... already validated... "); - no // It's hard to avoid validating a thing twice, so for now allow it // Look into it later, really shouldn't happen _validated = true; } } public LDBStructsChildren kids { get; private set; } = new LDBStructsChildren(noParent); // just easier public LDBASTobj() { //////////////////////////////////////////////////////////////// todo dead? // VStudio thinks this has zero references, which is true explicitly, but it's // called implicitly on every obj creation - or should do todo- check if (recordObjectsCreated) { AllCreatedItems.Add(this); } } public virtual void validate() { if (reportAlsoIfValidationOK) { debugmsg("validating..." + className + " (" + asNameOrIdentContents() + ")"); } mhp_mhk(); kids.validate(); // validated = true; -- No, get each subclass to set // validated. That forces an explicit call to base() which // is more code but ok. } public bool isValidated { get => validated; } // todo just => validated; public virtual bool isTopLevelItemSeq => false; public virtual void mhp_mhk() { // don't think this need be virtual - todo? // = must have parent, must have kids. The kids may be the // empty set and the parent may be noParent but they must // exist. // Why not just inline this into validate()? // Because a few classes need bespoke validation, but still must check this. hardMustParentIsPresent(); kids.eachKidParentMustBe(this); } public string asNameOrIdentContents() { // for very crude debugging var optName = this is IisNamedItem ni ? ni.name.toRawStr() : ""; optName = (optName == "" && this is PMQIdent) ? this.toRawStr() : ""; var optName2 = optName + " (uid:" + uid.ToString() + ") " + getFirstFewCharsOf(toRawStr()); return optName2; } public virtual void getAllUIDs(UIDset uids) { uids.Add(this); kids.ForEach(namedKid => namedKid.kid.getAllUIDs(uids)); } public bool isStubParent { get => (this is NoParent) || (this is FakeParent); } public void validateSpecificItems(params LDBASTobj[] items) { Array.ForEach(items, item => item.validate()); } public T? optClimbTo() where T : LDBASTobj { var resItem = this; while (!(resItem is T)) { if (resItem.parent.isStubParent) { return null; } resItem = resItem.parent; } var res = resItem as T; return res; } public T reqClimbTo() where T : LDBASTobj { var loc = className + ".reqClimbTo()"; var res = optClimbTo(); hardMust2(!(res is null), // Thanks SO // Todo - use this elsewhere, poss ExpectException? todo () => loc + nl + ", climbed to top, didn't find type " + typeof(T).FullName + nl + "Hierarchy is: " + nl + getItemWithParents(this)); if (res is null) { throw new NNQC(); } return res; } public void hardMustParentIsPresent() { hardMust2(!(parent is null), () => "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust2(!(this.parent is NoParent), () => "Parent is NoParent on object of type\n" + $"'{this.className}'"); } public void hardMustParentIsAbsent() { // For checking cloning - parent should be NoParent immediately // after, before it's set by parent, and never null hardMust(!(parent is null), "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust((this.parent is NoParent), "Parent should be NoParent on object of type\n" + $"'{this.className}'"); } public bool hasParent

() { hardMust(!(this is NoParent), "Item is NoParent"); return this.parent is P; } public bool hasAncestor() { hardMust(!this.isStubParent, "Item is NoParent"); // how to do with a switch expr? if (this.parent.isStubParent /* is NoParent || this.parent is FakeParent*/ ) { // todo do as x.hasFudgedParent() - todo return false; } else if (this.parent is A) { return true; } else { return this.parent.hasAncestor(); } } public bool notHasAncestor() => !hasAncestor(); public void setMyParentTo(LDBASTobj prnt) { // An object may only set its parent if its current parent is // the dummy 'noParent', which almost every object's parent is // set to on creation (see LDBASTobj parent property def). // The test for null is peculiar given this. Because // noParent is created statically, setting the parent of // noParent is difficult, perhaps impossible, to do statically // and reliably.Despite the declaration '#nullable enable', it // gets created with a null parrent (quite reasonably), hence // this check.The actual setting of the parent has to be done // at runtime i.e.after the static creation of 'noParent'. hardMust2((this.parent is NoParent) || (this.parent is null), () => "tried to re-set parent on object of type: " + nl + this.classNameQ + nl + "Original parent type is: " + (this?.parent?.classNameQ == null ? "null" : this.parent.classNameQ) + nl + "and new parent type is: " + prnt.classNameQ + nl + "Item having parent set on it is: " // because object may not be fully constructed yet, check howver TODO this is a major hack and must be fixed sometime. // + nl + (this is null ? "(null)" : this.toRawStr())); + nl + this.toRawStr() ); parent = prnt; } public void forceNewParentForRootObject() { // Every object must have some parent, including the // root object of the parse, which is LDBitems // (a sequence of LDB items), so this forces one. // It is only for that purpose! // // LDBitems obj exists for sub objs such as the statements // in a while loop, in a proc, in an if... etc., for // which their parent is the while/proc/if etc. hardMustParentIsAbsent(); this.parent = fakeParent; } //public void resetMyParentTo(LDBASTobj prnt) { ////////////////////////////////// destroy!!!!!! todo // parent = prnt; //} //////////////////////// xxxxxxxxxxxxx delete - todo //private void setParentOnKid(LDBASTobj kid) => // todo needed? // // check parente doesn't exist - todo // kid.parent = this; public virtual void setParentOnKidsAndMakeKidsDict( // todo - and add to kids dict // this mixes up setting parent and making child dict. Not good. - todo params (string kidName, LDBASTobj kid)[] kidsIn) { var loc = classNamePlus("setParentOnKidsAndMakeKidsDict"); var kidsToAdd = new LDBStructsChildren(this, kidsIn); hardMust2(kids.isEmpty, () => $"setting kids on parent '{className}'" + "when kids is not empty, " + $"kids is ({kids.Count} kids):" + nl + kids.toDebugStr() + nl + $"Trying to add\n{kidsToAdd.toDebugStr()}\n" + $"in {className}"); // if there is an LDBStructsChildren and another child, // this will blow up - will need to do kids.Add or // something - in fact, kids needs to be created in one // go, which is what happens. kids = kidsToAdd; ///////////////////////////////////////////////////////// why this? todo Array.ForEach(kidsIn, xkid => hardMust(!(xkid.kid is null), loc, "null kid")); Array.ForEach(kidsIn, xkid => xkid.kid.setMyParentTo(this)); } public virtual void setParentOnKidsAndAddToKidsDict( params (string kidName, LDBASTobj kid)[] kidsIn) { var kidsToAdd_justForErrReporting = new LDBStructsChildren(this, kidsIn); hardMust2(!kids.isEmpty, // may disable this later () => "adding to kids when kids is empty, " + $"kids is\n{kids.toDebugStr()}\n" + $"Trying to add\n{kidsToAdd_justForErrReporting.toDebugStr()}\n" + $"in {className}"); Array.ForEach(kidsIn, xkid => { // setParentOnKid(xkid.kid); xkid.kid.setMyParentTo(this); kids.Add(xkid); }); } public virtual IdentsUsed getAllIDs() { /* This is overridden only in TemplatedAgg, and that's only to deliberately fail. There's something wrong there, todo - un-virtualise this and remove the one override. - todo */ var iu = new IdentsUsed(); this._getAllIDs(iu); return iu; } public virtual void _getAllIDs(IdentsUsed iu) { // Gets all the identifiers explicitly used (read or written) ----- todo update this // anywhere in the AST. Does not get idents which are // declarations ie. parameters, or var/const declarations // unless these are combined with an assignment. // // Implied idents such as those from tbl.* are implicit and ignored. // // Root caller supplies new, empty iu // need usage here - todo docs// kids._getAllIDs(iu); } public abstract bool canGetAllIdentsUsed { // This seems to be true on most classes so I could set true // as a default here and override it where it's not wanted, saving // a chunk of code, but I'd rather make it explicit to force me to consider each case. A bit more C&P for less chance of bugs. get; } // Produces a string that should be identical to (excepting // whitespaces) the input ie. emit LDB code not valid SQL // // ToString was causing too many weird problems (due to my // inexperience with c#) so just used toRawStr() public abstract string toRawStr(); public override string toDebugStr() => toRawStr(); } // end LDBASTobj public interface IhandlePrefixes where T : LDBRoot { public bool isPrefixed { get; } public bool isExcessPrefixed { get; } // The T in addPrefix/rePrefix/withoutPrefix is because we may be // returning eg. a list of items with prefixes // thus modified, not a single PMQIdent public T addPrefix(PMQIdent pfx); public T rePrefix(PMQIdent newpfx); // public T stripPrefix(PMQIdent id); } // todo - move this public abstract class LDBRoot { // Root of everything LDB-ish public bool Equals([AllowNull] LDBRoot other) { throw new LDBShouldNotBeImplementedException( "called Equals on LDBRoot"); } public override bool Equals([AllowNull] Object other) { throw new LDBShouldNotBeImplementedException( "called Equals in Object"); } public override int GetHashCode() { throw new LDBShouldNotBeImplementedException( "called GetHashCode in LDBRoot"); } public static bool operator ==(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called == in LDBRoot"); } public static bool operator !=(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called != in LDBRoot"); } public virtual string className { // todo - this needs checking it works as I want // also rename to ldbclassname -- todo get => this.GetType().FullName ?? "(unknown)"; } public virtual string classNameQ => "'" + className + "'"; public virtual string classNamePlus(string plus) => className + "." + plus; public override string ToString() { // This implicit to-string conversion crap cost me a few hours, so at least // do this & blow up at runtime. Would prefer compile time but it seems // not possible. This turns out to have been a good idea, // saved me quite a bit more debugging! // // Will try _validated; set { // hardMust(!validated, "... already validated... "); - no // It's hard to avoid validating a thing twice, so for now allow it // Look into it later, really shouldn't happen _validated = true; } } public LDBStructsChildren kids { get; private set; } = new LDBStructsChildren(noParent); // just easier public LDBASTobj() { //////////////////////////////////////////////////////////////// todo dead? // VStudio thinks this has zero references, which is true explicitly, but it's // called implicitly on every obj creation - or should do todo- check if (recordObjectsCreated) { AllCreatedItems.Add(this); } } public virtual void validate() { if (reportAlsoIfValidationOK) { debugmsg("validating..." + className + " (" + asNameOrIdentContents() + ")"); } mhp_mhk(); kids.validate(); // validated = true; -- No, get each subclass to set // validated. That forces an explicit call to base() which // is more code but ok. } public bool isValidated { get => validated; } // todo just => validated; public virtual bool isTopLevelItemSeq => false; public virtual void mhp_mhk() { // don't think this need be virtual - todo? // = must have parent, must have kids. The kids may be the // empty set and the parent may be noParent but they must // exist. // Why not just inline this into validate()? // Because a few classes need bespoke validation, but still must check this. hardMustParentIsPresent(); kids.eachKidParentMustBe(this); } public string asNameOrIdentContents() { // for very crude debugging var optName = this is IisNamedItem ni ? ni.name.toRawStr() : ""; optName = (optName == "" && this is PMQIdent) ? this.toRawStr() : ""; var optName2 = optName + " (uid:" + uid.ToString() + ") " + getFirstFewCharsOf(toRawStr()); return optName2; } public virtual void getAllUIDs(UIDset uids) { uids.Add(this); kids.ForEach(namedKid => namedKid.kid.getAllUIDs(uids)); } public bool isStubParent { get => (this is NoParent) || (this is FakeParent); } public void validateSpecificItems(params LDBASTobj[] items) { Array.ForEach(items, item => item.validate()); } public T? optClimbTo() where T : LDBASTobj { var resItem = this; while (!(resItem is T)) { if (resItem.parent.isStubParent) { return null; } resItem = resItem.parent; } var res = resItem as T; return res; } public T reqClimbTo() where T : LDBASTobj { var loc = className + ".reqClimbTo()"; var res = optClimbTo(); hardMust2(!(res is null), // Thanks SO // Todo - use this elsewhere, poss ExpectException? todo () => loc + nl + ", climbed to top, didn't find type " + typeof(T).FullName + nl + "Hierarchy is: " + nl + getItemWithParents(this)); if (res is null) { throw new NNQC(); } return res; } public void hardMustParentIsPresent() { hardMust2(!(parent is null), () => "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust2(!(this.parent is NoParent), () => "Parent is NoParent on object of type\n" + $"'{this.className}'"); } public void hardMustParentIsAbsent() { // For checking cloning - parent should be NoParent immediately // after, before it's set by parent, and never null hardMust(!(parent is null), "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust((this.parent is NoParent), "Parent should be NoParent on object of type\n" + $"'{this.className}'"); } public bool hasParent

() { hardMust(!(this is NoParent), "Item is NoParent"); return this.parent is P; } public bool hasAncestor() { hardMust(!this.isStubParent, "Item is NoParent"); // how to do with a switch expr? if (this.parent.isStubParent /* is NoParent || this.parent is FakeParent*/ ) { // todo do as x.hasFudgedParent() - todo return false; } else if (this.parent is A) { return true; } else { return this.parent.hasAncestor(); } } public bool notHasAncestor() => !hasAncestor(); public void setMyParentTo(LDBASTobj prnt) { // An object may only set its parent if its current parent is // the dummy 'noParent', which almost every object's parent is // set to on creation (see LDBASTobj parent property def). // The test for null is peculiar given this. Because // noParent is created statically, setting the parent of // noParent is difficult, perhaps impossible, to do statically // and reliably.Despite the declaration '#nullable enable', it // gets created with a null parrent (quite reasonably), hence // this check.The actual setting of the parent has to be done // at runtime i.e.after the static creation of 'noParent'. hardMust2((this.parent is NoParent) || (this.parent is null), () => "tried to re-set parent on object of type: " + nl + this.classNameQ + nl + "Original parent type is: " + (this?.parent?.classNameQ == null ? "null" : this.parent.classNameQ) + nl + "and new parent type is: " + prnt.classNameQ + nl + "Item having parent set on it is: " // because object may not be fully constructed yet, check howver TODO this is a major hack and must be fixed sometime. // + nl + (this is null ? "(null)" : this.toRawStr())); + nl + this.toRawStr() ); parent = prnt; } public void forceNewParentForRootObject() { // Every object must have some parent, including the // root object of the parse, which is LDBitems // (a sequence of LDB items), so this forces one. // It is only for that purpose! // // LDBitems obj exists for sub objs such as the statements // in a while loop, in a proc, in an if... etc., for // which their parent is the while/proc/if etc. hardMustParentIsAbsent(); this.parent = fakeParent; } //public void resetMyParentTo(LDBASTobj prnt) { ////////////////////////////////// destroy!!!!!! todo // parent = prnt; //} //////////////////////// xxxxxxxxxxxxx delete - todo //private void setParentOnKid(LDBASTobj kid) => // todo needed? // // check parente doesn't exist - todo // kid.parent = this; public virtual void setParentOnKidsAndMakeKidsDict( // todo - and add to kids dict // this mixes up setting parent and making child dict. Not good. - todo params (string kidName, LDBASTobj kid)[] kidsIn) { var loc = classNamePlus("setParentOnKidsAndMakeKidsDict"); var kidsToAdd = new LDBStructsChildren(this, kidsIn); hardMust2(kids.isEmpty, () => $"setting kids on parent '{className}'" + "when kids is not empty, " + $"kids is ({kids.Count} kids):" + nl + kids.toDebugStr() + nl + $"Trying to add\n{kidsToAdd.toDebugStr()}\n" + $"in {className}"); // if there is an LDBStructsChildren and another child, // this will blow up - will need to do kids.Add or // something - in fact, kids needs to be created in one // go, which is what happens. kids = kidsToAdd; ///////////////////////////////////////////////////////// why this? todo Array.ForEach(kidsIn, xkid => hardMust(!(xkid.kid is null), loc, "null kid")); Array.ForEach(kidsIn, xkid => xkid.kid.setMyParentTo(this)); } public virtual void setParentOnKidsAndAddToKidsDict( params (string kidName, LDBASTobj kid)[] kidsIn) { var kidsToAdd_justForErrReporting = new LDBStructsChildren(this, kidsIn); hardMust2(!kids.isEmpty, // may disable this later () => "adding to kids when kids is empty, " + $"kids is\n{kids.toDebugStr()}\n" + $"Trying to add\n{kidsToAdd_justForErrReporting.toDebugStr()}\n" + $"in {className}"); Array.ForEach(kidsIn, xkid => { // setParentOnKid(xkid.kid); xkid.kid.setMyParentTo(this); kids.Add(xkid); }); } public virtual IdentsUsed getAllIDs() { /* This is overridden only in TemplatedAgg, and that's only to deliberately fail. There's something wrong there, todo - un-virtualise this and remove the one override. - todo */ var iu = new IdentsUsed(); this._getAllIDs(iu); return iu; } public virtual void _getAllIDs(IdentsUsed iu) { // Gets all the identifiers explicitly used (read or written) ----- todo update this // anywhere in the AST. Does not get idents which are // declarations ie. parameters, or var/const declarations // unless these are combined with an assignment. // // Implied idents such as those from tbl.* are implicit and ignored. // // Root caller supplies new, empty iu // need usage here - todo docs// kids._getAllIDs(iu); } public abstract bool canGetAllIdentsUsed { // This seems to be true on most classes so I could set true // as a default here and override it where it's not wanted, saving // a chunk of code, but I'd rather make it explicit to force me to consider each case. A bit more C&P for less chance of bugs. get; } // Produces a string that should be identical to (excepting // whitespaces) the input ie. emit LDB code not valid SQL // // ToString was causing too many weird problems (due to my // inexperience with c#) so just used toRawStr() public abstract string toRawStr(); public override string toDebugStr() => toRawStr(); } // end LDBASTobj public interface IhandlePrefixes where T : LDBRoot { public bool isPrefixed { get; } public bool isExcessPrefixed { get; } // The T in addPrefix/rePrefix/withoutPrefix is because we may be // returning eg. a list of items with prefixes // thus modified, not a single PMQIdent public T addPrefix(PMQIdent pfx); public T rePrefix(PMQIdent newpfx); // public T stripPrefix(PMQIdent id); } // todo - move this public abstract class LDBRoot { // Root of everything LDB-ish public bool Equals([AllowNull] LDBRoot other) { throw new LDBShouldNotBeImplementedException( "called Equals on LDBRoot"); } public override bool Equals([AllowNull] Object other) { throw new LDBShouldNotBeImplementedException( "called Equals in Object"); } public override int GetHashCode() { throw new LDBShouldNotBeImplementedException( "called GetHashCode in LDBRoot"); } public static bool operator ==(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called == in LDBRoot"); } public static bool operator !=(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called != in LDBRoot"); } public virtual string className { // todo - this needs checking it works as I want // also rename to ldbclassname -- todo get => this.GetType().FullName ?? "(unknown)"; } public virtual string classNameQ => "'" + className + "'"; public virtual string classNamePlus(string plus) => className + "." + plus; public override string ToString() { // This implicit to-string conversion crap cost me a few hours, so at least // do this & blow up at runtime. Would prefer compile time but it seems // not possible. This turns out to have been a good idea, // saved me quite a bit more debugging! // // Will try _validated; set { // hardMust(!validated, "... already validated... "); - no // It's hard to avoid validating a thing twice, so for now allow it // Look into it later, really shouldn't happen _validated = true; } } public LDBStructsChildren kids { get; private set; } = new LDBStructsChildren(noParent); // just easier public LDBASTobj() { //////////////////////////////////////////////////////////////// todo dead? // VStudio thinks this has zero references, which is true explicitly, but it's // called implicitly on every obj creation - or should do todo- check if (recordObjectsCreated) { AllCreatedItems.Add(this); } } public virtual void validate() { if (reportAlsoIfValidationOK) { debugmsg("validating..." + className + " (" + asNameOrIdentContents() + ")"); } mhp_mhk(); kids.validate(); // validated = true; -- No, get each subclass to set // validated. That forces an explicit call to base() which // is more code but ok. } public bool isValidated { get => validated; } // todo just => validated; public virtual bool isTopLevelItemSeq => false; public virtual void mhp_mhk() { // don't think this need be virtual - todo? // = must have parent, must have kids. The kids may be the // empty set and the parent may be noParent but they must // exist. // Why not just inline this into validate()? // Because a few classes need bespoke validation, but still must check this. hardMustParentIsPresent(); kids.eachKidParentMustBe(this); } public string asNameOrIdentContents() { // for very crude debugging var optName = this is IisNamedItem ni ? ni.name.toRawStr() : ""; optName = (optName == "" && this is PMQIdent) ? this.toRawStr() : ""; var optName2 = optName + " (uid:" + uid.ToString() + ") " + getFirstFewCharsOf(toRawStr()); return optName2; } public virtual void getAllUIDs(UIDset uids) { uids.Add(this); kids.ForEach(namedKid => namedKid.kid.getAllUIDs(uids)); } public bool isStubParent { get => (this is NoParent) || (this is FakeParent); } public void validateSpecificItems(params LDBASTobj[] items) { Array.ForEach(items, item => item.validate()); } public T? optClimbTo() where T : LDBASTobj { var resItem = this; while (!(resItem is T)) { if (resItem.parent.isStubParent) { return null; } resItem = resItem.parent; } var res = resItem as T; return res; } public T reqClimbTo() where T : LDBASTobj { var loc = className + ".reqClimbTo()"; var res = optClimbTo(); hardMust2(!(res is null), // Thanks SO // Todo - use this elsewhere, poss ExpectException? todo () => loc + nl + ", climbed to top, didn't find type " + typeof(T).FullName + nl + "Hierarchy is: " + nl + getItemWithParents(this)); if (res is null) { throw new NNQC(); } return res; } public void hardMustParentIsPresent() { hardMust2(!(parent is null), () => "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust2(!(this.parent is NoParent), () => "Parent is NoParent on object of type\n" + $"'{this.className}'"); } public void hardMustParentIsAbsent() { // For checking cloning - parent should be NoParent immediately // after, before it's set by parent, and never null hardMust(!(parent is null), "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust((this.parent is NoParent), "Parent should be NoParent on object of type\n" + $"'{this.className}'"); } public bool hasParent

() { hardMust(!(this is NoParent), "Item is NoParent"); return this.parent is P; } public bool hasAncestor() { hardMust(!this.isStubParent, "Item is NoParent"); // how to do with a switch expr? if (this.parent.isStubParent /* is NoParent || this.parent is FakeParent*/ ) { // todo do as x.hasFudgedParent() - todo return false; } else if (this.parent is A) { return true; } else { return this.parent.hasAncestor(); } } public bool notHasAncestor() => !hasAncestor(); public void setMyParentTo(LDBASTobj prnt) { // An object may only set its parent if its current parent is // the dummy 'noParent', which almost every object's parent is // set to on creation (see LDBASTobj parent property def). // The test for null is peculiar given this. Because // noParent is created statically, setting the parent of // noParent is difficult, perhaps impossible, to do statically // and reliably.Despite the declaration '#nullable enable', it // gets created with a null parrent (quite reasonably), hence // this check.The actual setting of the parent has to be done // at runtime i.e.after the static creation of 'noParent'. hardMust2((this.parent is NoParent) || (this.parent is null), () => "tried to re-set parent on object of type: " + nl + this.classNameQ + nl + "Original parent type is: " + (this?.parent?.classNameQ == null ? "null" : this.parent.classNameQ) + nl + "and new parent type is: " + prnt.classNameQ + nl + "Item having parent set on it is: " // because object may not be fully constructed yet, check howver TODO this is a major hack and must be fixed sometime. // + nl + (this is null ? "(null)" : this.toRawStr())); + nl + this.toRawStr() ); parent = prnt; } public void forceNewParentForRootObject() { // Every object must have some parent, including the // root object of the parse, which is LDBitems // (a sequence of LDB items), so this forces one. // It is only for that purpose! // // LDBitems obj exists for sub objs such as the statements // in a while loop, in a proc, in an if... etc., for // which their parent is the while/proc/if etc. hardMustParentIsAbsent(); this.parent = fakeParent; } //public void resetMyParentTo(LDBASTobj prnt) { ////////////////////////////////// destroy!!!!!! todo // parent = prnt; //} //////////////////////// xxxxxxxxxxxxx delete - todo //private void setParentOnKid(LDBASTobj kid) => // todo needed? // // check parente doesn't exist - todo // kid.parent = this; public virtual void setParentOnKidsAndMakeKidsDict( // todo - and add to kids dict // this mixes up setting parent and making child dict. Not good. - todo params (string kidName, LDBASTobj kid)[] kidsIn) { var loc = classNamePlus("setParentOnKidsAndMakeKidsDict"); var kidsToAdd = new LDBStructsChildren(this, kidsIn); hardMust2(kids.isEmpty, () => $"setting kids on parent '{className}'" + "when kids is not empty, " + $"kids is ({kids.Count} kids):" + nl + kids.toDebugStr() + nl + $"Trying to add\n{kidsToAdd.toDebugStr()}\n" + $"in {className}"); // if there is an LDBStructsChildren and another child, // this will blow up - will need to do kids.Add or // something - in fact, kids needs to be created in one // go, which is what happens. kids = kidsToAdd; ///////////////////////////////////////////////////////// why this? todo Array.ForEach(kidsIn, xkid => hardMust(!(xkid.kid is null), loc, "null kid")); Array.ForEach(kidsIn, xkid => xkid.kid.setMyParentTo(this)); } public virtual void setParentOnKidsAndAddToKidsDict( params (string kidName, LDBASTobj kid)[] kidsIn) { var kidsToAdd_justForErrReporting = new LDBStructsChildren(this, kidsIn); hardMust2(!kids.isEmpty, // may disable this later () => "adding to kids when kids is empty, " + $"kids is\n{kids.toDebugStr()}\n" + $"Trying to add\n{kidsToAdd_justForErrReporting.toDebugStr()}\n" + $"in {className}"); Array.ForEach(kidsIn, xkid => { // setParentOnKid(xkid.kid); xkid.kid.setMyParentTo(this); kids.Add(xkid); }); } public virtual IdentsUsed getAllIDs() { /* This is overridden only in TemplatedAgg, and that's only to deliberately fail. There's something wrong there, todo - un-virtualise this and remove the one override. - todo */ var iu = new IdentsUsed(); this._getAllIDs(iu); return iu; } public virtual void _getAllIDs(IdentsUsed iu) { // Gets all the identifiers explicitly used (read or written) ----- todo update this // anywhere in the AST. Does not get idents which are // declarations ie. parameters, or var/const declarations // unless these are combined with an assignment. // // Implied idents such as those from tbl.* are implicit and ignored. // // Root caller supplies new, empty iu // need usage here - todo docs// kids._getAllIDs(iu); } public abstract bool canGetAllIdentsUsed { // This seems to be true on most classes so I could set true // as a default here and override it where it's not wanted, saving // a chunk of code, but I'd rather make it explicit to force me to consider each case. A bit more C&P for less chance of bugs. get; } // Produces a string that should be identical to (excepting // whitespaces) the input ie. emit LDB code not valid SQL // // ToString was causing too many weird problems (due to my // inexperience with c#) so just used toRawStr() public abstract string toRawStr(); public override string toDebugStr() => toRawStr(); } // end LDBASTobj public interface IhandlePrefixes where T : LDBRoot { public bool isPrefixed { get; } public bool isExcessPrefixed { get; } // The T in addPrefix/rePrefix/withoutPrefix is because we may be // returning eg. a list of items with prefixes // thus modified, not a single PMQIdent public T addPrefix(PMQIdent pfx); public T rePrefix(PMQIdent newpfx); // public T stripPrefix(PMQIdent id); } // todo - move this public abstract class LDBRoot { // Root of everything LDB-ish public bool Equals([AllowNull] LDBRoot other) { throw new LDBShouldNotBeImplementedException( "called Equals on LDBRoot"); } public override bool Equals([AllowNull] Object other) { throw new LDBShouldNotBeImplementedException( "called Equals in Object"); } public override int GetHashCode() { throw new LDBShouldNotBeImplementedException( "called GetHashCode in LDBRoot"); } public static bool operator ==(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called == in LDBRoot"); } public static bool operator !=(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called != in LDBRoot"); } public virtual string className { // todo - this needs checking it works as I want // also rename to ldbclassname -- todo get => this.GetType().FullName ?? "(unknown)"; } public virtual string classNameQ => "'" + className + "'"; public virtual string classNamePlus(string plus) => className + "." + plus; public override string ToString() { // This implicit to-string conversion crap cost me a few hours, so at least // do this & blow up at runtime. Would prefer compile time but it seems // not possible. This turns out to have been a good idea, // saved me quite a bit more debugging! // // Will try _validated; set { // hardMust(!validated, "... already validated... "); - no // It's hard to avoid validating a thing twice, so for now allow it // Look into it later, really shouldn't happen _validated = true; } } public LDBStructsChildren kids { get; private set; } = new LDBStructsChildren(noParent); // just easier public LDBASTobj() { //////////////////////////////////////////////////////////////// todo dead? // VStudio thinks this has zero references, which is true explicitly, but it's // called implicitly on every obj creation - or should do todo- check if (recordObjectsCreated) { AllCreatedItems.Add(this); } } public virtual void validate() { if (reportAlsoIfValidationOK) { debugmsg("validating..." + className + " (" + asNameOrIdentContents() + ")"); } mhp_mhk(); kids.validate(); // validated = true; -- No, get each subclass to set // validated. That forces an explicit call to base() which // is more code but ok. } public bool isValidated { get => validated; } // todo just => validated; public virtual bool isTopLevelItemSeq => false; public virtual void mhp_mhk() { // don't think this need be virtual - todo? // = must have parent, must have kids. The kids may be the // empty set and the parent may be noParent but they must // exist. // Why not just inline this into validate()? // Because a few classes need bespoke validation, but still must check this. hardMustParentIsPresent(); kids.eachKidParentMustBe(this); } public string asNameOrIdentContents() { // for very crude debugging var optName = this is IisNamedItem ni ? ni.name.toRawStr() : ""; optName = (optName == "" && this is PMQIdent) ? this.toRawStr() : ""; var optName2 = optName + " (uid:" + uid.ToString() + ") " + getFirstFewCharsOf(toRawStr()); return optName2; } public virtual void getAllUIDs(UIDset uids) { uids.Add(this); kids.ForEach(namedKid => namedKid.kid.getAllUIDs(uids)); } public bool isStubParent { get => (this is NoParent) || (this is FakeParent); } public void validateSpecificItems(params LDBASTobj[] items) { Array.ForEach(items, item => item.validate()); } public T? optClimbTo() where T : LDBASTobj { var resItem = this; while (!(resItem is T)) { if (resItem.parent.isStubParent) { return null; } resItem = resItem.parent; } var res = resItem as T; return res; } public T reqClimbTo() where T : LDBASTobj { var loc = className + ".reqClimbTo()"; var res = optClimbTo(); hardMust2(!(res is null), // Thanks SO // Todo - use this elsewhere, poss ExpectException? todo () => loc + nl + ", climbed to top, didn't find type " + typeof(T).FullName + nl + "Hierarchy is: " + nl + getItemWithParents(this)); if (res is null) { throw new NNQC(); } return res; } public void hardMustParentIsPresent() { hardMust2(!(parent is null), () => "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust2(!(this.parent is NoParent), () => "Parent is NoParent on object of type\n" + $"'{this.className}'"); } public void hardMustParentIsAbsent() { // For checking cloning - parent should be NoParent immediately // after, before it's set by parent, and never null hardMust(!(parent is null), "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust((this.parent is NoParent), "Parent should be NoParent on object of type\n" + $"'{this.className}'"); } public bool hasParent

() { hardMust(!(this is NoParent), "Item is NoParent"); return this.parent is P; } public bool hasAncestor() { hardMust(!this.isStubParent, "Item is NoParent"); // how to do with a switch expr? if (this.parent.isStubParent /* is NoParent || this.parent is FakeParent*/ ) { // todo do as x.hasFudgedParent() - todo return false; } else if (this.parent is A) { return true; } else { return this.parent.hasAncestor(); } } public bool notHasAncestor() => !hasAncestor(); public void setMyParentTo(LDBASTobj prnt) { // An object may only set its parent if its current parent is // the dummy 'noParent', which almost every object's parent is // set to on creation (see LDBASTobj parent property def). // The test for null is peculiar given this. Because // noParent is created statically, setting the parent of // noParent is difficult, perhaps impossible, to do statically // and reliably.Despite the declaration '#nullable enable', it // gets created with a null parrent (quite reasonably), hence // this check.The actual setting of the parent has to be done // at runtime i.e.after the static creation of 'noParent'. hardMust2((this.parent is NoParent) || (this.parent is null), () => "tried to re-set parent on object of type: " + nl + this.classNameQ + nl + "Original parent type is: " + (this?.parent?.classNameQ == null ? "null" : this.parent.classNameQ) + nl + "and new parent type is: " + prnt.classNameQ + nl + "Item having parent set on it is: " // because object may not be fully constructed yet, check howver TODO this is a major hack and must be fixed sometime. // + nl + (this is null ? "(null)" : this.toRawStr())); + nl + this.toRawStr() ); parent = prnt; } public void forceNewParentForRootObject() { // Every object must have some parent, including the // root object of the parse, which is LDBitems // (a sequence of LDB items), so this forces one. // It is only for that purpose! // // LDBitems obj exists for sub objs such as the statements // in a while loop, in a proc, in an if... etc., for // which their parent is the while/proc/if etc. hardMustParentIsAbsent(); this.parent = fakeParent; } //public void resetMyParentTo(LDBASTobj prnt) { ////////////////////////////////// destroy!!!!!! todo // parent = prnt; //} //////////////////////// xxxxxxxxxxxxx delete - todo //private void setParentOnKid(LDBASTobj kid) => // todo needed? // // check parente doesn't exist - todo // kid.parent = this; public virtual void setParentOnKidsAndMakeKidsDict( // todo - and add to kids dict // this mixes up setting parent and making child dict. Not good. - todo params (string kidName, LDBASTobj kid)[] kidsIn) { var loc = classNamePlus("setParentOnKidsAndMakeKidsDict"); var kidsToAdd = new LDBStructsChildren(this, kidsIn); hardMust2(kids.isEmpty, () => $"setting kids on parent '{className}'" + "when kids is not empty, " + $"kids is ({kids.Count} kids):" + nl + kids.toDebugStr() + nl + $"Trying to add\n{kidsToAdd.toDebugStr()}\n" + $"in {className}"); // if there is an LDBStructsChildren and another child, // this will blow up - will need to do kids.Add or // something - in fact, kids needs to be created in one // go, which is what happens. kids = kidsToAdd; ///////////////////////////////////////////////////////// why this? todo Array.ForEach(kidsIn, xkid => hardMust(!(xkid.kid is null), loc, "null kid")); Array.ForEach(kidsIn, xkid => xkid.kid.setMyParentTo(this)); } public virtual void setParentOnKidsAndAddToKidsDict( params (string kidName, LDBASTobj kid)[] kidsIn) { var kidsToAdd_justForErrReporting = new LDBStructsChildren(this, kidsIn); hardMust2(!kids.isEmpty, // may disable this later () => "adding to kids when kids is empty, " + $"kids is\n{kids.toDebugStr()}\n" + $"Trying to add\n{kidsToAdd_justForErrReporting.toDebugStr()}\n" + $"in {className}"); Array.ForEach(kidsIn, xkid => { // setParentOnKid(xkid.kid); xkid.kid.setMyParentTo(this); kids.Add(xkid); }); } public virtual IdentsUsed getAllIDs() { /* This is overridden only in TemplatedAgg, and that's only to deliberately fail. There's something wrong there, todo - un-virtualise this and remove the one override. - todo */ var iu = new IdentsUsed(); this._getAllIDs(iu); return iu; } public virtual void _getAllIDs(IdentsUsed iu) { // Gets all the identifiers explicitly used (read or written) ----- todo update this // anywhere in the AST. Does not get idents which are // declarations ie. parameters, or var/const declarations // unless these are combined with an assignment. // // Implied idents such as those from tbl.* are implicit and ignored. // // Root caller supplies new, empty iu // need usage here - todo docs// kids._getAllIDs(iu); } public abstract bool canGetAllIdentsUsed { // This seems to be true on most classes so I could set true // as a default here and override it where it's not wanted, saving // a chunk of code, but I'd rather make it explicit to force me to consider each case. A bit more C&P for less chance of bugs. get; } // Produces a string that should be identical to (excepting // whitespaces) the input ie. emit LDB code not valid SQL // // ToString was causing too many weird problems (due to my // inexperience with c#) so just used toRawStr() public abstract string toRawStr(); public override string toDebugStr() => toRawStr(); } // end LDBASTobj public interface IhandlePrefixes where T : LDBRoot { public bool isPrefixed { get; } public bool isExcessPrefixed { get; } // The T in addPrefix/rePrefix/withoutPrefix is because we may be // returning eg. a list of items with prefixes // thus modified, not a single PMQIdent public T addPrefix(PMQIdent pfx); public T rePrefix(PMQIdent newpfx); // public T stripPrefix(PMQIdent id); } // todo - move this public abstract class LDBRoot { // Root of everything LDB-ish public bool Equals([AllowNull] LDBRoot other) { throw new LDBShouldNotBeImplementedException( "called Equals on LDBRoot"); } public override bool Equals([AllowNull] Object other) { throw new LDBShouldNotBeImplementedException( "called Equals in Object"); } public override int GetHashCode() { throw new LDBShouldNotBeImplementedException( "called GetHashCode in LDBRoot"); } public static bool operator ==(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called == in LDBRoot"); } public static bool operator !=(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called != in LDBRoot"); } public virtual string className { // todo - this needs checking it works as I want // also rename to ldbclassname -- todo get => this.GetType().FullName ?? "(unknown)"; } public virtual string classNameQ => "'" + className + "'"; public virtual string classNamePlus(string plus) => className + "." + plus; public override string ToString() { // This implicit to-string conversion crap cost me a few hours, so at least // do this & blow up at runtime. Would prefer compile time but it seems // not possible. This turns out to have been a good idea, // saved me quite a bit more debugging! // // Will try _validated; set { // hardMust(!validated, "... already validated... "); - no // It's hard to avoid validating a thing twice, so for now allow it // Look into it later, really shouldn't happen _validated = true; } } public LDBStructsChildren kids { get; private set; } = new LDBStructsChildren(noParent); // just easier public LDBASTobj() { //////////////////////////////////////////////////////////////// todo dead? // VStudio thinks this has zero references, which is true explicitly, but it's // called implicitly on every obj creation - or should do todo- check if (recordObjectsCreated) { AllCreatedItems.Add(this); } } public virtual void validate() { if (reportAlsoIfValidationOK) { debugmsg("validating..." + className + " (" + asNameOrIdentContents() + ")"); } mhp_mhk(); kids.validate(); // validated = true; -- No, get each subclass to set // validated. That forces an explicit call to base() which // is more code but ok. } public bool isValidated { get => validated; } // todo just => validated; public virtual bool isTopLevelItemSeq => false; public virtual void mhp_mhk() { // don't think this need be virtual - todo? // = must have parent, must have kids. The kids may be the // empty set and the parent may be noParent but they must // exist. // Why not just inline this into validate()? // Because a few classes need bespoke validation, but still must check this. hardMustParentIsPresent(); kids.eachKidParentMustBe(this); } public string asNameOrIdentContents() { // for very crude debugging var optName = this is IisNamedItem ni ? ni.name.toRawStr() : ""; optName = (optName == "" && this is PMQIdent) ? this.toRawStr() : ""; var optName2 = optName + " (uid:" + uid.ToString() + ") " + getFirstFewCharsOf(toRawStr()); return optName2; } public virtual void getAllUIDs(UIDset uids) { uids.Add(this); kids.ForEach(namedKid => namedKid.kid.getAllUIDs(uids)); } public bool isStubParent { get => (this is NoParent) || (this is FakeParent); } public void validateSpecificItems(params LDBASTobj[] items) { Array.ForEach(items, item => item.validate()); } public T? optClimbTo() where T : LDBASTobj { var resItem = this; while (!(resItem is T)) { if (resItem.parent.isStubParent) { return null; } resItem = resItem.parent; } var res = resItem as T; return res; } public T reqClimbTo() where T : LDBASTobj { var loc = className + ".reqClimbTo()"; var res = optClimbTo(); hardMust2(!(res is null), // Thanks SO // Todo - use this elsewhere, poss ExpectException? todo () => loc + nl + ", climbed to top, didn't find type " + typeof(T).FullName + nl + "Hierarchy is: " + nl + getItemWithParents(this)); if (res is null) { throw new NNQC(); } return res; } public void hardMustParentIsPresent() { hardMust2(!(parent is null), () => "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust2(!(this.parent is NoParent), () => "Parent is NoParent on object of type\n" + $"'{this.className}'"); } public void hardMustParentIsAbsent() { // For checking cloning - parent should be NoParent immediately // after, before it's set by parent, and never null hardMust(!(parent is null), "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust((this.parent is NoParent), "Parent should be NoParent on object of type\n" + $"'{this.className}'"); } public bool hasParent

() { hardMust(!(this is NoParent), "Item is NoParent"); return this.parent is P; } public bool hasAncestor() { hardMust(!this.isStubParent, "Item is NoParent"); // how to do with a switch expr? if (this.parent.isStubParent /* is NoParent || this.parent is FakeParent*/ ) { // todo do as x.hasFudgedParent() - todo return false; } else if (this.parent is A) { return true; } else { return this.parent.hasAncestor(); } } public bool notHasAncestor() => !hasAncestor(); public void setMyParentTo(LDBASTobj prnt) { // An object may only set its parent if its current parent is // the dummy 'noParent', which almost every object's parent is // set to on creation (see LDBASTobj parent property def). // The test for null is peculiar given this. Because // noParent is created statically, setting the parent of // noParent is difficult, perhaps impossible, to do statically // and reliably.Despite the declaration '#nullable enable', it // gets created with a null parrent (quite reasonably), hence // this check.The actual setting of the parent has to be done // at runtime i.e.after the static creation of 'noParent'. hardMust2((this.parent is NoParent) || (this.parent is null), () => "tried to re-set parent on object of type: " + nl + this.classNameQ + nl + "Original parent type is: " + (this?.parent?.classNameQ == null ? "null" : this.parent.classNameQ) + nl + "and new parent type is: " + prnt.classNameQ + nl + "Item having parent set on it is: " // because object may not be fully constructed yet, check howver TODO this is a major hack and must be fixed sometime. // + nl + (this is null ? "(null)" : this.toRawStr())); + nl + this.toRawStr() ); parent = prnt; } public void forceNewParentForRootObject() { // Every object must have some parent, including the // root object of the parse, which is LDBitems // (a sequence of LDB items), so this forces one. // It is only for that purpose! // // LDBitems obj exists for sub objs such as the statements // in a while loop, in a proc, in an if... etc., for // which their parent is the while/proc/if etc. hardMustParentIsAbsent(); this.parent = fakeParent; } //public void resetMyParentTo(LDBASTobj prnt) { ////////////////////////////////// destroy!!!!!! todo // parent = prnt; //} //////////////////////// xxxxxxxxxxxxx delete - todo //private void setParentOnKid(LDBASTobj kid) => // todo needed? // // check parente doesn't exist - todo // kid.parent = this; public virtual void setParentOnKidsAndMakeKidsDict( // todo - and add to kids dict // this mixes up setting parent and making child dict. Not good. - todo params (string kidName, LDBASTobj kid)[] kidsIn) { var loc = classNamePlus("setParentOnKidsAndMakeKidsDict"); var kidsToAdd = new LDBStructsChildren(this, kidsIn); hardMust2(kids.isEmpty, () => $"setting kids on parent '{className}'" + "when kids is not empty, " + $"kids is ({kids.Count} kids):" + nl + kids.toDebugStr() + nl + $"Trying to add\n{kidsToAdd.toDebugStr()}\n" + $"in {className}"); // if there is an LDBStructsChildren and another child, // this will blow up - will need to do kids.Add or // something - in fact, kids needs to be created in one // go, which is what happens. kids = kidsToAdd; ///////////////////////////////////////////////////////// why this? todo Array.ForEach(kidsIn, xkid => hardMust(!(xkid.kid is null), loc, "null kid")); Array.ForEach(kidsIn, xkid => xkid.kid.setMyParentTo(this)); } public virtual void setParentOnKidsAndAddToKidsDict( params (string kidName, LDBASTobj kid)[] kidsIn) { var kidsToAdd_justForErrReporting = new LDBStructsChildren(this, kidsIn); hardMust2(!kids.isEmpty, // may disable this later () => "adding to kids when kids is empty, " + $"kids is\n{kids.toDebugStr()}\n" + $"Trying to add\n{kidsToAdd_justForErrReporting.toDebugStr()}\n" + $"in {className}"); Array.ForEach(kidsIn, xkid => { // setParentOnKid(xkid.kid); xkid.kid.setMyParentTo(this); kids.Add(xkid); }); } public virtual IdentsUsed getAllIDs() { /* This is overridden only in TemplatedAgg, and that's only to deliberately fail. There's something wrong there, todo - un-virtualise this and remove the one override. - todo */ var iu = new IdentsUsed(); this._getAllIDs(iu); return iu; } public virtual void _getAllIDs(IdentsUsed iu) { // Gets all the identifiers explicitly used (read or written) ----- todo update this // anywhere in the AST. Does not get idents which are // declarations ie. parameters, or var/const declarations // unless these are combined with an assignment. // // Implied idents such as those from tbl.* are implicit and ignored. // // Root caller supplies new, empty iu // need usage here - todo docs// kids._getAllIDs(iu); } public abstract bool canGetAllIdentsUsed { // This seems to be true on most classes so I could set true // as a default here and override it where it's not wanted, saving // a chunk of code, but I'd rather make it explicit to force me to consider each case. A bit more C&P for less chance of bugs. get; } // Produces a string that should be identical to (excepting // whitespaces) the input ie. emit LDB code not valid SQL // // ToString was causing too many weird problems (due to my // inexperience with c#) so just used toRawStr() public abstract string toRawStr(); public override string toDebugStr() => toRawStr(); } // end LDBASTobj public interface IhandlePrefixes where T : LDBRoot { public bool isPrefixed { get; } public bool isExcessPrefixed { get; } // The T in addPrefix/rePrefix/withoutPrefix is because we may be // returning eg. a list of items with prefixes // thus modified, not a single PMQIdent public T addPrefix(PMQIdent pfx); public T rePrefix(PMQIdent newpfx); // public T stripPrefix(PMQIdent id); } // todo - move this public abstract class LDBRoot { // Root of everything LDB-ish public bool Equals([AllowNull] LDBRoot other) { throw new LDBShouldNotBeImplementedException( "called Equals on LDBRoot"); } public override bool Equals([AllowNull] Object other) { throw new LDBShouldNotBeImplementedException( "called Equals in Object"); } public override int GetHashCode() { throw new LDBShouldNotBeImplementedException( "called GetHashCode in LDBRoot"); } public static bool operator ==(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called == in LDBRoot"); } public static bool operator !=(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called != in LDBRoot"); } public virtual string className { // todo - this needs checking it works as I want // also rename to ldbclassname -- todo get => this.GetType().FullName ?? "(unknown)"; } public virtual string classNameQ => "'" + className + "'"; public virtual string classNamePlus(string plus) => className + "." + plus; public override string ToString() { // This implicit to-string conversion crap cost me a few hours, so at least // do this & blow up at runtime. Would prefer compile time but it seems // not possible. This turns out to have been a good idea, // saved me quite a bit more debugging! // // Will try _validated; set { // hardMust(!validated, "... already validated... "); - no // It's hard to avoid validating a thing twice, so for now allow it // Look into it later, really shouldn't happen _validated = true; } } public LDBStructsChildren kids { get; private set; } = new LDBStructsChildren(noParent); // just easier public LDBASTobj() { //////////////////////////////////////////////////////////////// todo dead? // VStudio thinks this has zero references, which is true explicitly, but it's // called implicitly on every obj creation - or should do todo- check if (recordObjectsCreated) { AllCreatedItems.Add(this); } } public virtual void validate() { if (reportAlsoIfValidationOK) { debugmsg("validating..." + className + " (" + asNameOrIdentContents() + ")"); } mhp_mhk(); kids.validate(); // validated = true; -- No, get each subclass to set // validated. That forces an explicit call to base() which // is more code but ok. } public bool isValidated { get => validated; } // todo just => validated; public virtual bool isTopLevelItemSeq => false; public virtual void mhp_mhk() { // don't think this need be virtual - todo? // = must have parent, must have kids. The kids may be the // empty set and the parent may be noParent but they must // exist. // Why not just inline this into validate()? // Because a few classes need bespoke validation, but still must check this. hardMustParentIsPresent(); kids.eachKidParentMustBe(this); } public string asNameOrIdentContents() { // for very crude debugging var optName = this is IisNamedItem ni ? ni.name.toRawStr() : ""; optName = (optName == "" && this is PMQIdent) ? this.toRawStr() : ""; var optName2 = optName + " (uid:" + uid.ToString() + ") " + getFirstFewCharsOf(toRawStr()); return optName2; } public virtual void getAllUIDs(UIDset uids) { uids.Add(this); kids.ForEach(namedKid => namedKid.kid.getAllUIDs(uids)); } public bool isStubParent { get => (this is NoParent) || (this is FakeParent); } public void validateSpecificItems(params LDBASTobj[] items) { Array.ForEach(items, item => item.validate()); } public T? optClimbTo() where T : LDBASTobj { var resItem = this; while (!(resItem is T)) { if (resItem.parent.isStubParent) { return null; } resItem = resItem.parent; } var res = resItem as T; return res; } public T reqClimbTo() where T : LDBASTobj { var loc = className + ".reqClimbTo()"; var res = optClimbTo(); hardMust2(!(res is null), // Thanks SO // Todo - use this elsewhere, poss ExpectException? todo () => loc + nl + ", climbed to top, didn't find type " + typeof(T).FullName + nl + "Hierarchy is: " + nl + getItemWithParents(this)); if (res is null) { throw new NNQC(); } return res; } public void hardMustParentIsPresent() { hardMust2(!(parent is null), () => "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust2(!(this.parent is NoParent), () => "Parent is NoParent on object of type\n" + $"'{this.className}'"); } public void hardMustParentIsAbsent() { // For checking cloning - parent should be NoParent immediately // after, before it's set by parent, and never null hardMust(!(parent is null), "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust((this.parent is NoParent), "Parent should be NoParent on object of type\n" + $"'{this.className}'"); } public bool hasParent

() { hardMust(!(this is NoParent), "Item is NoParent"); return this.parent is P; } public bool hasAncestor() { hardMust(!this.isStubParent, "Item is NoParent"); // how to do with a switch expr? if (this.parent.isStubParent /* is NoParent || this.parent is FakeParent*/ ) { // todo do as x.hasFudgedParent() - todo return false; } else if (this.parent is A) { return true; } else { return this.parent.hasAncestor(); } } public bool notHasAncestor() => !hasAncestor(); public void setMyParentTo(LDBASTobj prnt) { // An object may only set its parent if its current parent is // the dummy 'noParent', which almost every object's parent is // set to on creation (see LDBASTobj parent property def). // The test for null is peculiar given this. Because // noParent is created statically, setting the parent of // noParent is difficult, perhaps impossible, to do statically // and reliably.Despite the declaration '#nullable enable', it // gets created with a null parrent (quite reasonably), hence // this check.The actual setting of the parent has to be done // at runtime i.e.after the static creation of 'noParent'. hardMust2((this.parent is NoParent) || (this.parent is null), () => "tried to re-set parent on object of type: " + nl + this.classNameQ + nl + "Original parent type is: " + (this?.parent?.classNameQ == null ? "null" : this.parent.classNameQ) + nl + "and new parent type is: " + prnt.classNameQ + nl + "Item having parent set on it is: " // because object may not be fully constructed yet, check howver TODO this is a major hack and must be fixed sometime. // + nl + (this is null ? "(null)" : this.toRawStr())); + nl + this.toRawStr() ); parent = prnt; } public void forceNewParentForRootObject() { // Every object must have some parent, including the // root object of the parse, which is LDBitems // (a sequence of LDB items), so this forces one. // It is only for that purpose! // // LDBitems obj exists for sub objs such as the statements // in a while loop, in a proc, in an if... etc., for // which their parent is the while/proc/if etc. hardMustParentIsAbsent(); this.parent = fakeParent; } //public void resetMyParentTo(LDBASTobj prnt) { ////////////////////////////////// destroy!!!!!! todo // parent = prnt; //} //////////////////////// xxxxxxxxxxxxx delete - todo //private void setParentOnKid(LDBASTobj kid) => // todo needed? // // check parente doesn't exist - todo // kid.parent = this; public virtual void setParentOnKidsAndMakeKidsDict( // todo - and add to kids dict // this mixes up setting parent and making child dict. Not good. - todo params (string kidName, LDBASTobj kid)[] kidsIn) { var loc = classNamePlus("setParentOnKidsAndMakeKidsDict"); var kidsToAdd = new LDBStructsChildren(this, kidsIn); hardMust2(kids.isEmpty, () => $"setting kids on parent '{className}'" + "when kids is not empty, " + $"kids is ({kids.Count} kids):" + nl + kids.toDebugStr() + nl + $"Trying to add\n{kidsToAdd.toDebugStr()}\n" + $"in {className}"); // if there is an LDBStructsChildren and another child, // this will blow up - will need to do kids.Add or // something - in fact, kids needs to be created in one // go, which is what happens. kids = kidsToAdd; ///////////////////////////////////////////////////////// why this? todo Array.ForEach(kidsIn, xkid => hardMust(!(xkid.kid is null), loc, "null kid")); Array.ForEach(kidsIn, xkid => xkid.kid.setMyParentTo(this)); } public virtual void setParentOnKidsAndAddToKidsDict( params (string kidName, LDBASTobj kid)[] kidsIn) { var kidsToAdd_justForErrReporting = new LDBStructsChildren(this, kidsIn); hardMust2(!kids.isEmpty, // may disable this later () => "adding to kids when kids is empty, " + $"kids is\n{kids.toDebugStr()}\n" + $"Trying to add\n{kidsToAdd_justForErrReporting.toDebugStr()}\n" + $"in {className}"); Array.ForEach(kidsIn, xkid => { // setParentOnKid(xkid.kid); xkid.kid.setMyParentTo(this); kids.Add(xkid); }); } public virtual IdentsUsed getAllIDs() { /* This is overridden only in TemplatedAgg, and that's only to deliberately fail. There's something wrong there, todo - un-virtualise this and remove the one override. - todo */ var iu = new IdentsUsed(); this._getAllIDs(iu); return iu; } public virtual void _getAllIDs(IdentsUsed iu) { // Gets all the identifiers explicitly used (read or written) ----- todo update this // anywhere in the AST. Does not get idents which are // declarations ie. parameters, or var/const declarations // unless these are combined with an assignment. // // Implied idents such as those from tbl.* are implicit and ignored. // // Root caller supplies new, empty iu // need usage here - todo docs// kids._getAllIDs(iu); } public abstract bool canGetAllIdentsUsed { // This seems to be true on most classes so I could set true // as a default here and override it where it's not wanted, saving // a chunk of code, but I'd rather make it explicit to force me to consider each case. A bit more C&P for less chance of bugs. get; } // Produces a string that should be identical to (excepting // whitespaces) the input ie. emit LDB code not valid SQL // // ToString was causing too many weird problems (due to my // inexperience with c#) so just used toRawStr() public abstract string toRawStr(); public override string toDebugStr() => toRawStr(); } // end LDBASTobj public interface IhandlePrefixes where T : LDBRoot { public bool isPrefixed { get; } public bool isExcessPrefixed { get; } // The T in addPrefix/rePrefix/withoutPrefix is because we may be // returning eg. a list of items with prefixes // thus modified, not a single PMQIdent public T addPrefix(PMQIdent pfx); public T rePrefix(PMQIdent newpfx); // public T stripPrefix(PMQIdent id); } // todo - move this public abstract class LDBRoot { // Root of everything LDB-ish public bool Equals([AllowNull] LDBRoot other) { throw new LDBShouldNotBeImplementedException( "called Equals on LDBRoot"); } public override bool Equals([AllowNull] Object other) { throw new LDBShouldNotBeImplementedException( "called Equals in Object"); } public override int GetHashCode() { throw new LDBShouldNotBeImplementedException( "called GetHashCode in LDBRoot"); } public static bool operator ==(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called == in LDBRoot"); } public static bool operator !=(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called != in LDBRoot"); } public virtual string className { // todo - this needs checking it works as I want // also rename to ldbclassname -- todo get => this.GetType().FullName ?? "(unknown)"; } public virtual string classNameQ => "'" + className + "'"; public virtual string classNamePlus(string plus) => className + "." + plus; public override string ToString() { // This implicit to-string conversion crap cost me a few hours, so at least // do this & blow up at runtime. Would prefer compile time but it seems // not possible. This turns out to have been a good idea, // saved me quite a bit more debugging! // // Will try _validated; set { // hardMust(!validated, "... already validated... "); - no // It's hard to avoid validating a thing twice, so for now allow it // Look into it later, really shouldn't happen _validated = true; } } public LDBStructsChildren kids { get; private set; } = new LDBStructsChildren(noParent); // just easier public LDBASTobj() { //////////////////////////////////////////////////////////////// todo dead? // VStudio thinks this has zero references, which is true explicitly, but it's // called implicitly on every obj creation - or should do todo- check if (recordObjectsCreated) { AllCreatedItems.Add(this); } } public virtual void validate() { if (reportAlsoIfValidationOK) { debugmsg("validating..." + className + " (" + asNameOrIdentContents() + ")"); } mhp_mhk(); kids.validate(); // validated = true; -- No, get each subclass to set // validated. That forces an explicit call to base() which // is more code but ok. } public bool isValidated { get => validated; } // todo just => validated; public virtual bool isTopLevelItemSeq => false; public virtual void mhp_mhk() { // don't think this need be virtual - todo? // = must have parent, must have kids. The kids may be the // empty set and the parent may be noParent but they must // exist. // Why not just inline this into validate()? // Because a few classes need bespoke validation, but still must check this. hardMustParentIsPresent(); kids.eachKidParentMustBe(this); } public string asNameOrIdentContents() { // for very crude debugging var optName = this is IisNamedItem ni ? ni.name.toRawStr() : ""; optName = (optName == "" && this is PMQIdent) ? this.toRawStr() : ""; var optName2 = optName + " (uid:" + uid.ToString() + ") " + getFirstFewCharsOf(toRawStr()); return optName2; } public virtual void getAllUIDs(UIDset uids) { uids.Add(this); kids.ForEach(namedKid => namedKid.kid.getAllUIDs(uids)); } public bool isStubParent { get => (this is NoParent) || (this is FakeParent); } public void validateSpecificItems(params LDBASTobj[] items) { Array.ForEach(items, item => item.validate()); } public T? optClimbTo() where T : LDBASTobj { var resItem = this; while (!(resItem is T)) { if (resItem.parent.isStubParent) { return null; } resItem = resItem.parent; } var res = resItem as T; return res; } public T reqClimbTo() where T : LDBASTobj { var loc = className + ".reqClimbTo()"; var res = optClimbTo(); hardMust2(!(res is null), // Thanks SO // Todo - use this elsewhere, poss ExpectException? todo () => loc + nl + ", climbed to top, didn't find type " + typeof(T).FullName + nl + "Hierarchy is: " + nl + getItemWithParents(this)); if (res is null) { throw new NNQC(); } return res; } public void hardMustParentIsPresent() { hardMust2(!(parent is null), () => "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust2(!(this.parent is NoParent), () => "Parent is NoParent on object of type\n" + $"'{this.className}'"); } public void hardMustParentIsAbsent() { // For checking cloning - parent should be NoParent immediately // after, before it's set by parent, and never null hardMust(!(parent is null), "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust((this.parent is NoParent), "Parent should be NoParent on object of type\n" + $"'{this.className}'"); } public bool hasParent

() { hardMust(!(this is NoParent), "Item is NoParent"); return this.parent is P; } public bool hasAncestor() { hardMust(!this.isStubParent, "Item is NoParent"); // how to do with a switch expr? if (this.parent.isStubParent /* is NoParent || this.parent is FakeParent*/ ) { // todo do as x.hasFudgedParent() - todo return false; } else if (this.parent is A) { return true; } else { return this.parent.hasAncestor(); } } public bool notHasAncestor() => !hasAncestor(); public void setMyParentTo(LDBASTobj prnt) { // An object may only set its parent if its current parent is // the dummy 'noParent', which almost every object's parent is // set to on creation (see LDBASTobj parent property def). // The test for null is peculiar given this. Because // noParent is created statically, setting the parent of // noParent is difficult, perhaps impossible, to do statically // and reliably.Despite the declaration '#nullable enable', it // gets created with a null parrent (quite reasonably), hence // this check.The actual setting of the parent has to be done // at runtime i.e.after the static creation of 'noParent'. hardMust2((this.parent is NoParent) || (this.parent is null), () => "tried to re-set parent on object of type: " + nl + this.classNameQ + nl + "Original parent type is: " + (this?.parent?.classNameQ == null ? "null" : this.parent.classNameQ) + nl + "and new parent type is: " + prnt.classNameQ + nl + "Item having parent set on it is: " // because object may not be fully constructed yet, check howver TODO this is a major hack and must be fixed sometime. // + nl + (this is null ? "(null)" : this.toRawStr())); + nl + this.toRawStr() ); parent = prnt; } public void forceNewParentForRootObject() { // Every object must have some parent, including the // root object of the parse, which is LDBitems // (a sequence of LDB items), so this forces one. // It is only for that purpose! // // LDBitems obj exists for sub objs such as the statements // in a while loop, in a proc, in an if... etc., for // which their parent is the while/proc/if etc. hardMustParentIsAbsent(); this.parent = fakeParent; } //public void resetMyParentTo(LDBASTobj prnt) { ////////////////////////////////// destroy!!!!!! todo // parent = prnt; //} //////////////////////// xxxxxxxxxxxxx delete - todo //private void setParentOnKid(LDBASTobj kid) => // todo needed? // // check parente doesn't exist - todo // kid.parent = this; public virtual void setParentOnKidsAndMakeKidsDict( // todo - and add to kids dict // this mixes up setting parent and making child dict. Not good. - todo params (string kidName, LDBASTobj kid)[] kidsIn) { var loc = classNamePlus("setParentOnKidsAndMakeKidsDict"); var kidsToAdd = new LDBStructsChildren(this, kidsIn); hardMust2(kids.isEmpty, () => $"setting kids on parent '{className}'" + "when kids is not empty, " + $"kids is ({kids.Count} kids):" + nl + kids.toDebugStr() + nl + $"Trying to add\n{kidsToAdd.toDebugStr()}\n" + $"in {className}"); // if there is an LDBStructsChildren and another child, // this will blow up - will need to do kids.Add or // something - in fact, kids needs to be created in one // go, which is what happens. kids = kidsToAdd; ///////////////////////////////////////////////////////// why this? todo Array.ForEach(kidsIn, xkid => hardMust(!(xkid.kid is null), loc, "null kid")); Array.ForEach(kidsIn, xkid => xkid.kid.setMyParentTo(this)); } public virtual void setParentOnKidsAndAddToKidsDict( params (string kidName, LDBASTobj kid)[] kidsIn) { var kidsToAdd_justForErrReporting = new LDBStructsChildren(this, kidsIn); hardMust2(!kids.isEmpty, // may disable this later () => "adding to kids when kids is empty, " + $"kids is\n{kids.toDebugStr()}\n" + $"Trying to add\n{kidsToAdd_justForErrReporting.toDebugStr()}\n" + $"in {className}"); Array.ForEach(kidsIn, xkid => { // setParentOnKid(xkid.kid); xkid.kid.setMyParentTo(this); kids.Add(xkid); }); } public virtual IdentsUsed getAllIDs() { /* This is overridden only in TemplatedAgg, and that's only to deliberately fail. There's something wrong there, todo - un-virtualise this and remove the one override. - todo */ var iu = new IdentsUsed(); this._getAllIDs(iu); return iu; } public virtual void _getAllIDs(IdentsUsed iu) { // Gets all the identifiers explicitly used (read or written) ----- todo update this // anywhere in the AST. Does not get idents which are // declarations ie. parameters, or var/const declarations // unless these are combined with an assignment. // // Implied idents such as those from tbl.* are implicit and ignored. // // Root caller supplies new, empty iu // need usage here - todo docs// kids._getAllIDs(iu); } public abstract bool canGetAllIdentsUsed { // This seems to be true on most classes so I could set true // as a default here and override it where it's not wanted, saving // a chunk of code, but I'd rather make it explicit to force me to consider each case. A bit more C&P for less chance of bugs. get; } // Produces a string that should be identical to (excepting // whitespaces) the input ie. emit LDB code not valid SQL // // ToString was causing too many weird problems (due to my // inexperience with c#) so just used toRawStr() public abstract string toRawStr(); public override string toDebugStr() => toRawStr(); } // end LDBASTobj public interface IhandlePrefixes where T : LDBRoot { public bool isPrefixed { get; } public bool isExcessPrefixed { get; } // The T in addPrefix/rePrefix/withoutPrefix is because we may be // returning eg. a list of items with prefixes // thus modified, not a single PMQIdent public T addPrefix(PMQIdent pfx); public T rePrefix(PMQIdent newpfx); // public T stripPrefix(PMQIdent id); } // todo - move this public abstract class LDBRoot { // Root of everything LDB-ish public bool Equals([AllowNull] LDBRoot other) { throw new LDBShouldNotBeImplementedException( "called Equals on LDBRoot"); } public override bool Equals([AllowNull] Object other) { throw new LDBShouldNotBeImplementedException( "called Equals in Object"); } public override int GetHashCode() { throw new LDBShouldNotBeImplementedException( "called GetHashCode in LDBRoot"); } public static bool operator ==(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called == in LDBRoot"); } public static bool operator !=(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called != in LDBRoot"); } public virtual string className { // todo - this needs checking it works as I want // also rename to ldbclassname -- todo get => this.GetType().FullName ?? "(unknown)"; } public virtual string classNameQ => "'" + className + "'"; public virtual string classNamePlus(string plus) => className + "." + plus; public override string ToString() { // This implicit to-string conversion crap cost me a few hours, so at least // do this & blow up at runtime. Would prefer compile time but it seems // not possible. This turns out to have been a good idea, // saved me quite a bit more debugging! // // Will try _validated; set { // hardMust(!validated, "... already validated... "); - no // It's hard to avoid validating a thing twice, so for now allow it // Look into it later, really shouldn't happen _validated = true; } } public LDBStructsChildren kids { get; private set; } = new LDBStructsChildren(noParent); // just easier public LDBASTobj() { //////////////////////////////////////////////////////////////// todo dead? // VStudio thinks this has zero references, which is true explicitly, but it's // called implicitly on every obj creation - or should do todo- check if (recordObjectsCreated) { AllCreatedItems.Add(this); } } public virtual void validate() { if (reportAlsoIfValidationOK) { debugmsg("validating..." + className + " (" + asNameOrIdentContents() + ")"); } mhp_mhk(); kids.validate(); // validated = true; -- No, get each subclass to set // validated. That forces an explicit call to base() which // is more code but ok. } public bool isValidated { get => validated; } // todo just => validated; public virtual bool isTopLevelItemSeq => false; public virtual void mhp_mhk() { // don't think this need be virtual - todo? // = must have parent, must have kids. The kids may be the // empty set and the parent may be noParent but they must // exist. // Why not just inline this into validate()? // Because a few classes need bespoke validation, but still must check this. hardMustParentIsPresent(); kids.eachKidParentMustBe(this); } public string asNameOrIdentContents() { // for very crude debugging var optName = this is IisNamedItem ni ? ni.name.toRawStr() : ""; optName = (optName == "" && this is PMQIdent) ? this.toRawStr() : ""; var optName2 = optName + " (uid:" + uid.ToString() + ") " + getFirstFewCharsOf(toRawStr()); return optName2; } public virtual void getAllUIDs(UIDset uids) { uids.Add(this); kids.ForEach(namedKid => namedKid.kid.getAllUIDs(uids)); } public bool isStubParent { get => (this is NoParent) || (this is FakeParent); } public void validateSpecificItems(params LDBASTobj[] items) { Array.ForEach(items, item => item.validate()); } public T? optClimbTo() where T : LDBASTobj { var resItem = this; while (!(resItem is T)) { if (resItem.parent.isStubParent) { return null; } resItem = resItem.parent; } var res = resItem as T; return res; } public T reqClimbTo() where T : LDBASTobj { var loc = className + ".reqClimbTo()"; var res = optClimbTo(); hardMust2(!(res is null), // Thanks SO // Todo - use this elsewhere, poss ExpectException? todo () => loc + nl + ", climbed to top, didn't find type " + typeof(T).FullName + nl + "Hierarchy is: " + nl + getItemWithParents(this)); if (res is null) { throw new NNQC(); } return res; } public void hardMustParentIsPresent() { hardMust2(!(parent is null), () => "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust2(!(this.parent is NoParent), () => "Parent is NoParent on object of type\n" + $"'{this.className}'"); } public void hardMustParentIsAbsent() { // For checking cloning - parent should be NoParent immediately // after, before it's set by parent, and never null hardMust(!(parent is null), "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust((this.parent is NoParent), "Parent should be NoParent on object of type\n" + $"'{this.className}'"); } public bool hasParent

() { hardMust(!(this is NoParent), "Item is NoParent"); return this.parent is P; } public bool hasAncestor() { hardMust(!this.isStubParent, "Item is NoParent"); // how to do with a switch expr? if (this.parent.isStubParent /* is NoParent || this.parent is FakeParent*/ ) { // todo do as x.hasFudgedParent() - todo return false; } else if (this.parent is A) { return true; } else { return this.parent.hasAncestor(); } } public bool notHasAncestor() => !hasAncestor(); public void setMyParentTo(LDBASTobj prnt) { // An object may only set its parent if its current parent is // the dummy 'noParent', which almost every object's parent is // set to on creation (see LDBASTobj parent property def). // The test for null is peculiar given this. Because // noParent is created statically, setting the parent of // noParent is difficult, perhaps impossible, to do statically // and reliably.Despite the declaration '#nullable enable', it // gets created with a null parrent (quite reasonably), hence // this check.The actual setting of the parent has to be done // at runtime i.e.after the static creation of 'noParent'. hardMust2((this.parent is NoParent) || (this.parent is null), () => "tried to re-set parent on object of type: " + nl + this.classNameQ + nl + "Original parent type is: " + (this?.parent?.classNameQ == null ? "null" : this.parent.classNameQ) + nl + "and new parent type is: " + prnt.classNameQ + nl + "Item having parent set on it is: " // because object may not be fully constructed yet, check howver TODO this is a major hack and must be fixed sometime. // + nl + (this is null ? "(null)" : this.toRawStr())); + nl + this.toRawStr() ); parent = prnt; } public void forceNewParentForRootObject() { // Every object must have some parent, including the // root object of the parse, which is LDBitems // (a sequence of LDB items), so this forces one. // It is only for that purpose! // // LDBitems obj exists for sub objs such as the statements // in a while loop, in a proc, in an if... etc., for // which their parent is the while/proc/if etc. hardMustParentIsAbsent(); this.parent = fakeParent; } //public void resetMyParentTo(LDBASTobj prnt) { ////////////////////////////////// destroy!!!!!! todo // parent = prnt; //} //////////////////////// xxxxxxxxxxxxx delete - todo //private void setParentOnKid(LDBASTobj kid) => // todo needed? // // check parente doesn't exist - todo // kid.parent = this; public virtual void setParentOnKidsAndMakeKidsDict( // todo - and add to kids dict // this mixes up setting parent and making child dict. Not good. - todo params (string kidName, LDBASTobj kid)[] kidsIn) { var loc = classNamePlus("setParentOnKidsAndMakeKidsDict"); var kidsToAdd = new LDBStructsChildren(this, kidsIn); hardMust2(kids.isEmpty, () => $"setting kids on parent '{className}'" + "when kids is not empty, " + $"kids is ({kids.Count} kids):" + nl + kids.toDebugStr() + nl + $"Trying to add\n{kidsToAdd.toDebugStr()}\n" + $"in {className}"); // if there is an LDBStructsChildren and another child, // this will blow up - will need to do kids.Add or // something - in fact, kids needs to be created in one // go, which is what happens. kids = kidsToAdd; ///////////////////////////////////////////////////////// why this? todo Array.ForEach(kidsIn, xkid => hardMust(!(xkid.kid is null), loc, "null kid")); Array.ForEach(kidsIn, xkid => xkid.kid.setMyParentTo(this)); } public virtual void setParentOnKidsAndAddToKidsDict( params (string kidName, LDBASTobj kid)[] kidsIn) { var kidsToAdd_justForErrReporting = new LDBStructsChildren(this, kidsIn); hardMust2(!kids.isEmpty, // may disable this later () => "adding to kids when kids is empty, " + $"kids is\n{kids.toDebugStr()}\n" + $"Trying to add\n{kidsToAdd_justForErrReporting.toDebugStr()}\n" + $"in {className}"); Array.ForEach(kidsIn, xkid => { // setParentOnKid(xkid.kid); xkid.kid.setMyParentTo(this); kids.Add(xkid); }); } public virtual IdentsUsed getAllIDs() { /* This is overridden only in TemplatedAgg, and that's only to deliberately fail. There's something wrong there, todo - un-virtualise this and remove the one override. - todo */ var iu = new IdentsUsed(); this._getAllIDs(iu); return iu; } public virtual void _getAllIDs(IdentsUsed iu) { // Gets all the identifiers explicitly used (read or written) ----- todo update this // anywhere in the AST. Does not get idents which are // declarations ie. parameters, or var/const declarations // unless these are combined with an assignment. // // Implied idents such as those from tbl.* are implicit and ignored. // // Root caller supplies new, empty iu // need usage here - todo docs// kids._getAllIDs(iu); } public abstract bool canGetAllIdentsUsed { // This seems to be true on most classes so I could set true // as a default here and override it where it's not wanted, saving // a chunk of code, but I'd rather make it explicit to force me to consider each case. A bit more C&P for less chance of bugs. get; } // Produces a string that should be identical to (excepting // whitespaces) the input ie. emit LDB code not valid SQL // // ToString was causing too many weird problems (due to my // inexperience with c#) so just used toRawStr() public abstract string toRawStr(); public override string toDebugStr() => toRawStr(); } // end LDBASTobj public interface IhandlePrefixes where T : LDBRoot { public bool isPrefixed { get; } public bool isExcessPrefixed { get; } // The T in addPrefix/rePrefix/withoutPrefix is because we may be // returning eg. a list of items with prefixes // thus modified, not a single PMQIdent public T addPrefix(PMQIdent pfx); public T rePrefix(PMQIdent newpfx); // public T stripPrefix(PMQIdent id); } // todo - move this public abstract class LDBRoot { // Root of everything LDB-ish public bool Equals([AllowNull] LDBRoot other) { throw new LDBShouldNotBeImplementedException( "called Equals on LDBRoot"); } public override bool Equals([AllowNull] Object other) { throw new LDBShouldNotBeImplementedException( "called Equals in Object"); } public override int GetHashCode() { throw new LDBShouldNotBeImplementedException( "called GetHashCode in LDBRoot"); } public static bool operator ==(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called == in LDBRoot"); } public static bool operator !=(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called != in LDBRoot"); } public virtual string className { // todo - this needs checking it works as I want // also rename to ldbclassname -- todo get => this.GetType().FullName ?? "(unknown)"; } public virtual string classNameQ => "'" + className + "'"; public virtual string classNamePlus(string plus) => className + "." + plus; public override string ToString() { // This implicit to-string conversion crap cost me a few hours, so at least // do this & blow up at runtime. Would prefer compile time but it seems // not possible. This turns out to have been a good idea, // saved me quite a bit more debugging! // // Will try _validated; set { // hardMust(!validated, "... already validated... "); - no // It's hard to avoid validating a thing twice, so for now allow it // Look into it later, really shouldn't happen _validated = true; } } public LDBStructsChildren kids { get; private set; } = new LDBStructsChildren(noParent); // just easier public LDBASTobj() { //////////////////////////////////////////////////////////////// todo dead? // VStudio thinks this has zero references, which is true explicitly, but it's // called implicitly on every obj creation - or should do todo- check if (recordObjectsCreated) { AllCreatedItems.Add(this); } } public virtual void validate() { if (reportAlsoIfValidationOK) { debugmsg("validating..." + className + " (" + asNameOrIdentContents() + ")"); } mhp_mhk(); kids.validate(); // validated = true; -- No, get each subclass to set // validated. That forces an explicit call to base() which // is more code but ok. } public bool isValidated { get => validated; } // todo just => validated; public virtual bool isTopLevelItemSeq => false; public virtual void mhp_mhk() { // don't think this need be virtual - todo? // = must have parent, must have kids. The kids may be the // empty set and the parent may be noParent but they must // exist. // Why not just inline this into validate()? // Because a few classes need bespoke validation, but still must check this. hardMustParentIsPresent(); kids.eachKidParentMustBe(this); } public string asNameOrIdentContents() { // for very crude debugging var optName = this is IisNamedItem ni ? ni.name.toRawStr() : ""; optName = (optName == "" && this is PMQIdent) ? this.toRawStr() : ""; var optName2 = optName + " (uid:" + uid.ToString() + ") " + getFirstFewCharsOf(toRawStr()); return optName2; } public virtual void getAllUIDs(UIDset uids) { uids.Add(this); kids.ForEach(namedKid => namedKid.kid.getAllUIDs(uids)); } public bool isStubParent { get => (this is NoParent) || (this is FakeParent); } public void validateSpecificItems(params LDBASTobj[] items) { Array.ForEach(items, item => item.validate()); } public T? optClimbTo() where T : LDBASTobj { var resItem = this; while (!(resItem is T)) { if (resItem.parent.isStubParent) { return null; } resItem = resItem.parent; } var res = resItem as T; return res; } public T reqClimbTo() where T : LDBASTobj { var loc = className + ".reqClimbTo()"; var res = optClimbTo(); hardMust2(!(res is null), // Thanks SO // Todo - use this elsewhere, poss ExpectException? todo () => loc + nl + ", climbed to top, didn't find type " + typeof(T).FullName + nl + "Hierarchy is: " + nl + getItemWithParents(this)); if (res is null) { throw new NNQC(); } return res; } public void hardMustParentIsPresent() { hardMust2(!(parent is null), () => "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust2(!(this.parent is NoParent), () => "Parent is NoParent on object of type\n" + $"'{this.className}'"); } public void hardMustParentIsAbsent() { // For checking cloning - parent should be NoParent immediately // after, before it's set by parent, and never null hardMust(!(parent is null), "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust((this.parent is NoParent), "Parent should be NoParent on object of type\n" + $"'{this.className}'"); } public bool hasParent

() { hardMust(!(this is NoParent), "Item is NoParent"); return this.parent is P; } public bool hasAncestor() { hardMust(!this.isStubParent, "Item is NoParent"); // how to do with a switch expr? if (this.parent.isStubParent /* is NoParent || this.parent is FakeParent*/ ) { // todo do as x.hasFudgedParent() - todo return false; } else if (this.parent is A) { return true; } else { return this.parent.hasAncestor(); } } public bool notHasAncestor() => !hasAncestor(); public void setMyParentTo(LDBASTobj prnt) { // An object may only set its parent if its current parent is // the dummy 'noParent', which almost every object's parent is // set to on creation (see LDBASTobj parent property def). // The test for null is peculiar given this. Because // noParent is created statically, setting the parent of // noParent is difficult, perhaps impossible, to do statically // and reliably.Despite the declaration '#nullable enable', it // gets created with a null parrent (quite reasonably), hence // this check.The actual setting of the parent has to be done // at runtime i.e.after the static creation of 'noParent'. hardMust2((this.parent is NoParent) || (this.parent is null), () => "tried to re-set parent on object of type: " + nl + this.classNameQ + nl + "Original parent type is: " + (this?.parent?.classNameQ == null ? "null" : this.parent.classNameQ) + nl + "and new parent type is: " + prnt.classNameQ + nl + "Item having parent set on it is: " // because object may not be fully constructed yet, check howver TODO this is a major hack and must be fixed sometime. // + nl + (this is null ? "(null)" : this.toRawStr())); + nl + this.toRawStr() ); parent = prnt; } public void forceNewParentForRootObject() { // Every object must have some parent, including the // root object of the parse, which is LDBitems // (a sequence of LDB items), so this forces one. // It is only for that purpose! // // LDBitems obj exists for sub objs such as the statements // in a while loop, in a proc, in an if... etc., for // which their parent is the while/proc/if etc. hardMustParentIsAbsent(); this.parent = fakeParent; } //public void resetMyParentTo(LDBASTobj prnt) { ////////////////////////////////// destroy!!!!!! todo // parent = prnt; //} //////////////////////// xxxxxxxxxxxxx delete - todo //private void setParentOnKid(LDBASTobj kid) => // todo needed? // // check parente doesn't exist - todo // kid.parent = this; public virtual void setParentOnKidsAndMakeKidsDict( // todo - and add to kids dict // this mixes up setting parent and making child dict. Not good. - todo params (string kidName, LDBASTobj kid)[] kidsIn) { var loc = classNamePlus("setParentOnKidsAndMakeKidsDict"); var kidsToAdd = new LDBStructsChildren(this, kidsIn); hardMust2(kids.isEmpty, () => $"setting kids on parent '{className}'" + "when kids is not empty, " + $"kids is ({kids.Count} kids):" + nl + kids.toDebugStr() + nl + $"Trying to add\n{kidsToAdd.toDebugStr()}\n" + $"in {className}"); // if there is an LDBStructsChildren and another child, // this will blow up - will need to do kids.Add or // something - in fact, kids needs to be created in one // go, which is what happens. kids = kidsToAdd; ///////////////////////////////////////////////////////// why this? todo Array.ForEach(kidsIn, xkid => hardMust(!(xkid.kid is null), loc, "null kid")); Array.ForEach(kidsIn, xkid => xkid.kid.setMyParentTo(this)); } public virtual void setParentOnKidsAndAddToKidsDict( params (string kidName, LDBASTobj kid)[] kidsIn) { var kidsToAdd_justForErrReporting = new LDBStructsChildren(this, kidsIn); hardMust2(!kids.isEmpty, // may disable this later () => "adding to kids when kids is empty, " + $"kids is\n{kids.toDebugStr()}\n" + $"Trying to add\n{kidsToAdd_justForErrReporting.toDebugStr()}\n" + $"in {className}"); Array.ForEach(kidsIn, xkid => { // setParentOnKid(xkid.kid); xkid.kid.setMyParentTo(this); kids.Add(xkid); }); } public virtual IdentsUsed getAllIDs() { /* This is overridden only in TemplatedAgg, and that's only to deliberately fail. There's something wrong there, todo - un-virtualise this and remove the one override. - todo */ var iu = new IdentsUsed(); this._getAllIDs(iu); return iu; } public virtual void _getAllIDs(IdentsUsed iu) { // Gets all the identifiers explicitly used (read or written) ----- todo update this // anywhere in the AST. Does not get idents which are // declarations ie. parameters, or var/const declarations // unless these are combined with an assignment. // // Implied idents such as those from tbl.* are implicit and ignored. // // Root caller supplies new, empty iu // need usage here - todo docs// kids._getAllIDs(iu); } public abstract bool canGetAllIdentsUsed { // This seems to be true on most classes so I could set true // as a default here and override it where it's not wanted, saving // a chunk of code, but I'd rather make it explicit to force me to consider each case. A bit more C&P for less chance of bugs. get; } // Produces a string that should be identical to (excepting // whitespaces) the input ie. emit LDB code not valid SQL // // ToString was causing too many weird problems (due to my // inexperience with c#) so just used toRawStr() public abstract string toRawStr(); public override string toDebugStr() => toRawStr(); } // end LDBASTobj public interface IhandlePrefixes where T : LDBRoot { public bool isPrefixed { get; } public bool isExcessPrefixed { get; } // The T in addPrefix/rePrefix/withoutPrefix is because we may be // returning eg. a list of items with prefixes // thus modified, not a single PMQIdent public T addPrefix(PMQIdent pfx); public T rePrefix(PMQIdent newpfx); // public T stripPrefix(PMQIdent id); } // todo - move this public abstract class LDBRoot { // Root of everything LDB-ish public bool Equals([AllowNull] LDBRoot other) { throw new LDBShouldNotBeImplementedException( "called Equals on LDBRoot"); } public override bool Equals([AllowNull] Object other) { throw new LDBShouldNotBeImplementedException( "called Equals in Object"); } public override int GetHashCode() { throw new LDBShouldNotBeImplementedException( "called GetHashCode in LDBRoot"); } public static bool operator ==(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called == in LDBRoot"); } public static bool operator !=(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called != in LDBRoot"); } public virtual string className { // todo - this needs checking it works as I want // also rename to ldbclassname -- todo get => this.GetType().FullName ?? "(unknown)"; } public virtual string classNameQ => "'" + className + "'"; public virtual string classNamePlus(string plus) => className + "." + plus; public override string ToString() { // This implicit to-string conversion crap cost me a few hours, so at least // do this & blow up at runtime. Would prefer compile time but it seems // not possible. This turns out to have been a good idea, // saved me quite a bit more debugging! // // Will try _validated; set { // hardMust(!validated, "... already validated... "); - no // It's hard to avoid validating a thing twice, so for now allow it // Look into it later, really shouldn't happen _validated = true; } } public LDBStructsChildren kids { get; private set; } = new LDBStructsChildren(noParent); // just easier public LDBASTobj() { //////////////////////////////////////////////////////////////// todo dead? // VStudio thinks this has zero references, which is true explicitly, but it's // called implicitly on every obj creation - or should do todo- check if (recordObjectsCreated) { AllCreatedItems.Add(this); } } public virtual void validate() { if (reportAlsoIfValidationOK) { debugmsg("validating..." + className + " (" + asNameOrIdentContents() + ")"); } mhp_mhk(); kids.validate(); // validated = true; -- No, get each subclass to set // validated. That forces an explicit call to base() which // is more code but ok. } public bool isValidated { get => validated; } // todo just => validated; public virtual bool isTopLevelItemSeq => false; public virtual void mhp_mhk() { // don't think this need be virtual - todo? // = must have parent, must have kids. The kids may be the // empty set and the parent may be noParent but they must // exist. // Why not just inline this into validate()? // Because a few classes need bespoke validation, but still must check this. hardMustParentIsPresent(); kids.eachKidParentMustBe(this); } public string asNameOrIdentContents() { // for very crude debugging var optName = this is IisNamedItem ni ? ni.name.toRawStr() : ""; optName = (optName == "" && this is PMQIdent) ? this.toRawStr() : ""; var optName2 = optName + " (uid:" + uid.ToString() + ") " + getFirstFewCharsOf(toRawStr()); return optName2; } public virtual void getAllUIDs(UIDset uids) { uids.Add(this); kids.ForEach(namedKid => namedKid.kid.getAllUIDs(uids)); } public bool isStubParent { get => (this is NoParent) || (this is FakeParent); } public void validateSpecificItems(params LDBASTobj[] items) { Array.ForEach(items, item => item.validate()); } public T? optClimbTo() where T : LDBASTobj { var resItem = this; while (!(resItem is T)) { if (resItem.parent.isStubParent) { return null; } resItem = resItem.parent; } var res = resItem as T; return res; } public T reqClimbTo() where T : LDBASTobj { var loc = className + ".reqClimbTo()"; var res = optClimbTo(); hardMust2(!(res is null), // Thanks SO // Todo - use this elsewhere, poss ExpectException? todo () => loc + nl + ", climbed to top, didn't find type " + typeof(T).FullName + nl + "Hierarchy is: " + nl + getItemWithParents(this)); if (res is null) { throw new NNQC(); } return res; } public void hardMustParentIsPresent() { hardMust2(!(parent is null), () => "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust2(!(this.parent is NoParent), () => "Parent is NoParent on object of type\n" + $"'{this.className}'"); } public void hardMustParentIsAbsent() { // For checking cloning - parent should be NoParent immediately // after, before it's set by parent, and never null hardMust(!(parent is null), "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust((this.parent is NoParent), "Parent should be NoParent on object of type\n" + $"'{this.className}'"); } public bool hasParent

() { hardMust(!(this is NoParent), "Item is NoParent"); return this.parent is P; } public bool hasAncestor() { hardMust(!this.isStubParent, "Item is NoParent"); // how to do with a switch expr? if (this.parent.isStubParent /* is NoParent || this.parent is FakeParent*/ ) { // todo do as x.hasFudgedParent() - todo return false; } else if (this.parent is A) { return true; } else { return this.parent.hasAncestor(); } } public bool notHasAncestor() => !hasAncestor(); public void setMyParentTo(LDBASTobj prnt) { // An object may only set its parent if its current parent is // the dummy 'noParent', which almost every object's parent is // set to on creation (see LDBASTobj parent property def). // The test for null is peculiar given this. Because // noParent is created statically, setting the parent of // noParent is difficult, perhaps impossible, to do statically // and reliably.Despite the declaration '#nullable enable', it // gets created with a null parrent (quite reasonably), hence // this check.The actual setting of the parent has to be done // at runtime i.e.after the static creation of 'noParent'. hardMust2((this.parent is NoParent) || (this.parent is null), () => "tried to re-set parent on object of type: " + nl + this.classNameQ + nl + "Original parent type is: " + (this?.parent?.classNameQ == null ? "null" : this.parent.classNameQ) + nl + "and new parent type is: " + prnt.classNameQ + nl + "Item having parent set on it is: " // because object may not be fully constructed yet, check howver TODO this is a major hack and must be fixed sometime. // + nl + (this is null ? "(null)" : this.toRawStr())); + nl + this.toRawStr() ); parent = prnt; } public void forceNewParentForRootObject() { // Every object must have some parent, including the // root object of the parse, which is LDBitems // (a sequence of LDB items), so this forces one. // It is only for that purpose! // // LDBitems obj exists for sub objs such as the statements // in a while loop, in a proc, in an if... etc., for // which their parent is the while/proc/if etc. hardMustParentIsAbsent(); this.parent = fakeParent; } //public void resetMyParentTo(LDBASTobj prnt) { ////////////////////////////////// destroy!!!!!! todo // parent = prnt; //} //////////////////////// xxxxxxxxxxxxx delete - todo //private void setParentOnKid(LDBASTobj kid) => // todo needed? // // check parente doesn't exist - todo // kid.parent = this; public virtual void setParentOnKidsAndMakeKidsDict( // todo - and add to kids dict // this mixes up setting parent and making child dict. Not good. - todo params (string kidName, LDBASTobj kid)[] kidsIn) { var loc = classNamePlus("setParentOnKidsAndMakeKidsDict"); var kidsToAdd = new LDBStructsChildren(this, kidsIn); hardMust2(kids.isEmpty, () => $"setting kids on parent '{className}'" + "when kids is not empty, " + $"kids is ({kids.Count} kids):" + nl + kids.toDebugStr() + nl + $"Trying to add\n{kidsToAdd.toDebugStr()}\n" + $"in {className}"); // if there is an LDBStructsChildren and another child, // this will blow up - will need to do kids.Add or // something - in fact, kids needs to be created in one // go, which is what happens. kids = kidsToAdd; ///////////////////////////////////////////////////////// why this? todo Array.ForEach(kidsIn, xkid => hardMust(!(xkid.kid is null), loc, "null kid")); Array.ForEach(kidsIn, xkid => xkid.kid.setMyParentTo(this)); } public virtual void setParentOnKidsAndAddToKidsDict( params (string kidName, LDBASTobj kid)[] kidsIn) { var kidsToAdd_justForErrReporting = new LDBStructsChildren(this, kidsIn); hardMust2(!kids.isEmpty, // may disable this later () => "adding to kids when kids is empty, " + $"kids is\n{kids.toDebugStr()}\n" + $"Trying to add\n{kidsToAdd_justForErrReporting.toDebugStr()}\n" + $"in {className}"); Array.ForEach(kidsIn, xkid => { // setParentOnKid(xkid.kid); xkid.kid.setMyParentTo(this); kids.Add(xkid); }); } public virtual IdentsUsed getAllIDs() { /* This is overridden only in TemplatedAgg, and that's only to deliberately fail. There's something wrong there, todo - un-virtualise this and remove the one override. - todo */ var iu = new IdentsUsed(); this._getAllIDs(iu); return iu; } public virtual void _getAllIDs(IdentsUsed iu) { // Gets all the identifiers explicitly used (read or written) ----- todo update this // anywhere in the AST. Does not get idents which are // declarations ie. parameters, or var/const declarations // unless these are combined with an assignment. // // Implied idents such as those from tbl.* are implicit and ignored. // // Root caller supplies new, empty iu // need usage here - todo docs// kids._getAllIDs(iu); } public abstract bool canGetAllIdentsUsed { // This seems to be true on most classes so I could set true // as a default here and override it where it's not wanted, saving // a chunk of code, but I'd rather make it explicit to force me to consider each case. A bit more C&P for less chance of bugs. get; } // Produces a string that should be identical to (excepting // whitespaces) the input ie. emit LDB code not valid SQL // // ToString was causing too many weird problems (due to my // inexperience with c#) so just used toRawStr() public abstract string toRawStr(); public override string toDebugStr() => toRawStr(); } // end LDBASTobj public interface IhandlePrefixes where T : LDBRoot { public bool isPrefixed { get; } public bool isExcessPrefixed { get; } // The T in addPrefix/rePrefix/withoutPrefix is because we may be // returning eg. a list of items with prefixes // thus modified, not a single PMQIdent public T addPrefix(PMQIdent pfx); public T rePrefix(PMQIdent newpfx); // public T stripPrefix(PMQIdent id); } // todo - move this public abstract class LDBRoot { // Root of everything LDB-ish public bool Equals([AllowNull] LDBRoot other) { throw new LDBShouldNotBeImplementedException( "called Equals on LDBRoot"); } public override bool Equals([AllowNull] Object other) { throw new LDBShouldNotBeImplementedException( "called Equals in Object"); } public override int GetHashCode() { throw new LDBShouldNotBeImplementedException( "called GetHashCode in LDBRoot"); } public static bool operator ==(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called == in LDBRoot"); } public static bool operator !=(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called != in LDBRoot"); } public virtual string className { // todo - this needs checking it works as I want // also rename to ldbclassname -- todo get => this.GetType().FullName ?? "(unknown)"; } public virtual string classNameQ => "'" + className + "'"; public virtual string classNamePlus(string plus) => className + "." + plus; public override string ToString() { // This implicit to-string conversion crap cost me a few hours, so at least // do this & blow up at runtime. Would prefer compile time but it seems // not possible. This turns out to have been a good idea, // saved me quite a bit more debugging! // // Will try _validated; set { // hardMust(!validated, "... already validated... "); - no // It's hard to avoid validating a thing twice, so for now allow it // Look into it later, really shouldn't happen _validated = true; } } public LDBStructsChildren kids { get; private set; } = new LDBStructsChildren(noParent); // just easier public LDBASTobj() { //////////////////////////////////////////////////////////////// todo dead? // VStudio thinks this has zero references, which is true explicitly, but it's // called implicitly on every obj creation - or should do todo- check if (recordObjectsCreated) { AllCreatedItems.Add(this); } } public virtual void validate() { if (reportAlsoIfValidationOK) { debugmsg("validating..." + className + " (" + asNameOrIdentContents() + ")"); } mhp_mhk(); kids.validate(); // validated = true; -- No, get each subclass to set // validated. That forces an explicit call to base() which // is more code but ok. } public bool isValidated { get => validated; } // todo just => validated; public virtual bool isTopLevelItemSeq => false; public virtual void mhp_mhk() { // don't think this need be virtual - todo? // = must have parent, must have kids. The kids may be the // empty set and the parent may be noParent but they must // exist. // Why not just inline this into validate()? // Because a few classes need bespoke validation, but still must check this. hardMustParentIsPresent(); kids.eachKidParentMustBe(this); } public string asNameOrIdentContents() { // for very crude debugging var optName = this is IisNamedItem ni ? ni.name.toRawStr() : ""; optName = (optName == "" && this is PMQIdent) ? this.toRawStr() : ""; var optName2 = optName + " (uid:" + uid.ToString() + ") " + getFirstFewCharsOf(toRawStr()); return optName2; } public virtual void getAllUIDs(UIDset uids) { uids.Add(this); kids.ForEach(namedKid => namedKid.kid.getAllUIDs(uids)); } public bool isStubParent { get => (this is NoParent) || (this is FakeParent); } public void validateSpecificItems(params LDBASTobj[] items) { Array.ForEach(items, item => item.validate()); } public T? optClimbTo() where T : LDBASTobj { var resItem = this; while (!(resItem is T)) { if (resItem.parent.isStubParent) { return null; } resItem = resItem.parent; } var res = resItem as T; return res; } public T reqClimbTo() where T : LDBASTobj { var loc = className + ".reqClimbTo()"; var res = optClimbTo(); hardMust2(!(res is null), // Thanks SO // Todo - use this elsewhere, poss ExpectException? todo () => loc + nl + ", climbed to top, didn't find type " + typeof(T).FullName + nl + "Hierarchy is: " + nl + getItemWithParents(this)); if (res is null) { throw new NNQC(); } return res; } public void hardMustParentIsPresent() { hardMust2(!(parent is null), () => "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust2(!(this.parent is NoParent), () => "Parent is NoParent on object of type\n" + $"'{this.className}'"); } public void hardMustParentIsAbsent() { // For checking cloning - parent should be NoParent immediately // after, before it's set by parent, and never null hardMust(!(parent is null), "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust((this.parent is NoParent), "Parent should be NoParent on object of type\n" + $"'{this.className}'"); } public bool hasParent

() { hardMust(!(this is NoParent), "Item is NoParent"); return this.parent is P; } public bool hasAncestor() { hardMust(!this.isStubParent, "Item is NoParent"); // how to do with a switch expr? if (this.parent.isStubParent /* is NoParent || this.parent is FakeParent*/ ) { // todo do as x.hasFudgedParent() - todo return false; } else if (this.parent is A) { return true; } else { return this.parent.hasAncestor(); } } public bool notHasAncestor() => !hasAncestor(); public void setMyParentTo(LDBASTobj prnt) { // An object may only set its parent if its current parent is // the dummy 'noParent', which almost every object's parent is // set to on creation (see LDBASTobj parent property def). // The test for null is peculiar given this. Because // noParent is created statically, setting the parent of // noParent is difficult, perhaps impossible, to do statically // and reliably.Despite the declaration '#nullable enable', it // gets created with a null parrent (quite reasonably), hence // this check.The actual setting of the parent has to be done // at runtime i.e.after the static creation of 'noParent'. hardMust2((this.parent is NoParent) || (this.parent is null), () => "tried to re-set parent on object of type: " + nl + this.classNameQ + nl + "Original parent type is: " + (this?.parent?.classNameQ == null ? "null" : this.parent.classNameQ) + nl + "and new parent type is: " + prnt.classNameQ + nl + "Item having parent set on it is: " // because object may not be fully constructed yet, check howver TODO this is a major hack and must be fixed sometime. // + nl + (this is null ? "(null)" : this.toRawStr())); + nl + this.toRawStr() ); parent = prnt; } public void forceNewParentForRootObject() { // Every object must have some parent, including the // root object of the parse, which is LDBitems // (a sequence of LDB items), so this forces one. // It is only for that purpose! // // LDBitems obj exists for sub objs such as the statements // in a while loop, in a proc, in an if... etc., for // which their parent is the while/proc/if etc. hardMustParentIsAbsent(); this.parent = fakeParent; } //public void resetMyParentTo(LDBASTobj prnt) { ////////////////////////////////// destroy!!!!!! todo // parent = prnt; //} //////////////////////// xxxxxxxxxxxxx delete - todo //private void setParentOnKid(LDBASTobj kid) => // todo needed? // // check parente doesn't exist - todo // kid.parent = this; public virtual void setParentOnKidsAndMakeKidsDict( // todo - and add to kids dict // this mixes up setting parent and making child dict. Not good. - todo params (string kidName, LDBASTobj kid)[] kidsIn) { var loc = classNamePlus("setParentOnKidsAndMakeKidsDict"); var kidsToAdd = new LDBStructsChildren(this, kidsIn); hardMust2(kids.isEmpty, () => $"setting kids on parent '{className}'" + "when kids is not empty, " + $"kids is ({kids.Count} kids):" + nl + kids.toDebugStr() + nl + $"Trying to add\n{kidsToAdd.toDebugStr()}\n" + $"in {className}"); // if there is an LDBStructsChildren and another child, // this will blow up - will need to do kids.Add or // something - in fact, kids needs to be created in one // go, which is what happens. kids = kidsToAdd; ///////////////////////////////////////////////////////// why this? todo Array.ForEach(kidsIn, xkid => hardMust(!(xkid.kid is null), loc, "null kid")); Array.ForEach(kidsIn, xkid => xkid.kid.setMyParentTo(this)); } public virtual void setParentOnKidsAndAddToKidsDict( params (string kidName, LDBASTobj kid)[] kidsIn) { var kidsToAdd_justForErrReporting = new LDBStructsChildren(this, kidsIn); hardMust2(!kids.isEmpty, // may disable this later () => "adding to kids when kids is empty, " + $"kids is\n{kids.toDebugStr()}\n" + $"Trying to add\n{kidsToAdd_justForErrReporting.toDebugStr()}\n" + $"in {className}"); Array.ForEach(kidsIn, xkid => { // setParentOnKid(xkid.kid); xkid.kid.setMyParentTo(this); kids.Add(xkid); }); } public virtual IdentsUsed getAllIDs() { /* This is overridden only in TemplatedAgg, and that's only to deliberately fail. There's something wrong there, todo - un-virtualise this and remove the one override. - todo */ var iu = new IdentsUsed(); this._getAllIDs(iu); return iu; } public virtual void _getAllIDs(IdentsUsed iu) { // Gets all the identifiers explicitly used (read or written) ----- todo update this // anywhere in the AST. Does not get idents which are // declarations ie. parameters, or var/const declarations // unless these are combined with an assignment. // // Implied idents such as those from tbl.* are implicit and ignored. // // Root caller supplies new, empty iu // need usage here - todo docs// kids._getAllIDs(iu); } public abstract bool canGetAllIdentsUsed { // This seems to be true on most classes so I could set true // as a default here and override it where it's not wanted, saving // a chunk of code, but I'd rather make it explicit to force me to consider each case. A bit more C&P for less chance of bugs. get; } // Produces a string that should be identical to (excepting // whitespaces) the input ie. emit LDB code not valid SQL // // ToString was causing too many weird problems (due to my // inexperience with c#) so just used toRawStr() public abstract string toRawStr(); public override string toDebugStr() => toRawStr(); } // end LDBASTobj public interface IhandlePrefixes where T : LDBRoot { public bool isPrefixed { get; } public bool isExcessPrefixed { get; } // The T in addPrefix/rePrefix/withoutPrefix is because we may be // returning eg. a list of items with prefixes // thus modified, not a single PMQIdent public T addPrefix(PMQIdent pfx); public T rePrefix(PMQIdent newpfx); // public T stripPrefix(PMQIdent id); } // todo - move this public abstract class LDBRoot { // Root of everything LDB-ish public bool Equals([AllowNull] LDBRoot other) { throw new LDBShouldNotBeImplementedException( "called Equals on LDBRoot"); } public override bool Equals([AllowNull] Object other) { throw new LDBShouldNotBeImplementedException( "called Equals in Object"); } public override int GetHashCode() { throw new LDBShouldNotBeImplementedException( "called GetHashCode in LDBRoot"); } public static bool operator ==(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called == in LDBRoot"); } public static bool operator !=(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called != in LDBRoot"); } public virtual string className { // todo - this needs checking it works as I want // also rename to ldbclassname -- todo get => this.GetType().FullName ?? "(unknown)"; } public virtual string classNameQ => "'" + className + "'"; public virtual string classNamePlus(string plus) => className + "." + plus; public override string ToString() { // This implicit to-string conversion crap cost me a few hours, so at least // do this & blow up at runtime. Would prefer compile time but it seems // not possible. This turns out to have been a good idea, // saved me quite a bit more debugging! // // Will try _validated; set { // hardMust(!validated, "... already validated... "); - no // It's hard to avoid validating a thing twice, so for now allow it // Look into it later, really shouldn't happen _validated = true; } } public LDBStructsChildren kids { get; private set; } = new LDBStructsChildren(noParent); // just easier public LDBASTobj() { //////////////////////////////////////////////////////////////// todo dead? // VStudio thinks this has zero references, which is true explicitly, but it's // called implicitly on every obj creation - or should do todo- check if (recordObjectsCreated) { AllCreatedItems.Add(this); } } public virtual void validate() { if (reportAlsoIfValidationOK) { debugmsg("validating..." + className + " (" + asNameOrIdentContents() + ")"); } mhp_mhk(); kids.validate(); // validated = true; -- No, get each subclass to set // validated. That forces an explicit call to base() which // is more code but ok. } public bool isValidated { get => validated; } // todo just => validated; public virtual bool isTopLevelItemSeq => false; public virtual void mhp_mhk() { // don't think this need be virtual - todo? // = must have parent, must have kids. The kids may be the // empty set and the parent may be noParent but they must // exist. // Why not just inline this into validate()? // Because a few classes need bespoke validation, but still must check this. hardMustParentIsPresent(); kids.eachKidParentMustBe(this); } public string asNameOrIdentContents() { // for very crude debugging var optName = this is IisNamedItem ni ? ni.name.toRawStr() : ""; optName = (optName == "" && this is PMQIdent) ? this.toRawStr() : ""; var optName2 = optName + " (uid:" + uid.ToString() + ") " + getFirstFewCharsOf(toRawStr()); return optName2; } public virtual void getAllUIDs(UIDset uids) { uids.Add(this); kids.ForEach(namedKid => namedKid.kid.getAllUIDs(uids)); } public bool isStubParent { get => (this is NoParent) || (this is FakeParent); } public void validateSpecificItems(params LDBASTobj[] items) { Array.ForEach(items, item => item.validate()); } public T? optClimbTo() where T : LDBASTobj { var resItem = this; while (!(resItem is T)) { if (resItem.parent.isStubParent) { return null; } resItem = resItem.parent; } var res = resItem as T; return res; } public T reqClimbTo() where T : LDBASTobj { var loc = className + ".reqClimbTo()"; var res = optClimbTo(); hardMust2(!(res is null), // Thanks SO // Todo - use this elsewhere, poss ExpectException? todo () => loc + nl + ", climbed to top, didn't find type " + typeof(T).FullName + nl + "Hierarchy is: " + nl + getItemWithParents(this)); if (res is null) { throw new NNQC(); } return res; } public void hardMustParentIsPresent() { hardMust2(!(parent is null), () => "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust2(!(this.parent is NoParent), () => "Parent is NoParent on object of type\n" + $"'{this.className}'"); } public void hardMustParentIsAbsent() { // For checking cloning - parent should be NoParent immediately // after, before it's set by parent, and never null hardMust(!(parent is null), "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust((this.parent is NoParent), "Parent should be NoParent on object of type\n" + $"'{this.className}'"); } public bool hasParent

() { hardMust(!(this is NoParent), "Item is NoParent"); return this.parent is P; } public bool hasAncestor() { hardMust(!this.isStubParent, "Item is NoParent"); // how to do with a switch expr? if (this.parent.isStubParent /* is NoParent || this.parent is FakeParent*/ ) { // todo do as x.hasFudgedParent() - todo return false; } else if (this.parent is A) { return true; } else { return this.parent.hasAncestor(); } } public bool notHasAncestor() => !hasAncestor(); public void setMyParentTo(LDBASTobj prnt) { // An object may only set its parent if its current parent is // the dummy 'noParent', which almost every object's parent is // set to on creation (see LDBASTobj parent property def). // The test for null is peculiar given this. Because // noParent is created statically, setting the parent of // noParent is difficult, perhaps impossible, to do statically // and reliably.Despite the declaration '#nullable enable', it // gets created with a null parrent (quite reasonably), hence // this check.The actual setting of the parent has to be done // at runtime i.e.after the static creation of 'noParent'. hardMust2((this.parent is NoParent) || (this.parent is null), () => "tried to re-set parent on object of type: " + nl + this.classNameQ + nl + "Original parent type is: " + (this?.parent?.classNameQ == null ? "null" : this.parent.classNameQ) + nl + "and new parent type is: " + prnt.classNameQ + nl + "Item having parent set on it is: " // because object may not be fully constructed yet, check howver TODO this is a major hack and must be fixed sometime. // + nl + (this is null ? "(null)" : this.toRawStr())); + nl + this.toRawStr() ); parent = prnt; } public void forceNewParentForRootObject() { // Every object must have some parent, including the // root object of the parse, which is LDBitems // (a sequence of LDB items), so this forces one. // It is only for that purpose! // // LDBitems obj exists for sub objs such as the statements // in a while loop, in a proc, in an if... etc., for // which their parent is the while/proc/if etc. hardMustParentIsAbsent(); this.parent = fakeParent; } //public void resetMyParentTo(LDBASTobj prnt) { ////////////////////////////////// destroy!!!!!! todo // parent = prnt; //} //////////////////////// xxxxxxxxxxxxx delete - todo //private void setParentOnKid(LDBASTobj kid) => // todo needed? // // check parente doesn't exist - todo // kid.parent = this; public virtual void setParentOnKidsAndMakeKidsDict( // todo - and add to kids dict // this mixes up setting parent and making child dict. Not good. - todo params (string kidName, LDBASTobj kid)[] kidsIn) { var loc = classNamePlus("setParentOnKidsAndMakeKidsDict"); var kidsToAdd = new LDBStructsChildren(this, kidsIn); hardMust2(kids.isEmpty, () => $"setting kids on parent '{className}'" + "when kids is not empty, " + $"kids is ({kids.Count} kids):" + nl + kids.toDebugStr() + nl + $"Trying to add\n{kidsToAdd.toDebugStr()}\n" + $"in {className}"); // if there is an LDBStructsChildren and another child, // this will blow up - will need to do kids.Add or // something - in fact, kids needs to be created in one // go, which is what happens. kids = kidsToAdd; ///////////////////////////////////////////////////////// why this? todo Array.ForEach(kidsIn, xkid => hardMust(!(xkid.kid is null), loc, "null kid")); Array.ForEach(kidsIn, xkid => xkid.kid.setMyParentTo(this)); } public virtual void setParentOnKidsAndAddToKidsDict( params (string kidName, LDBASTobj kid)[] kidsIn) { var kidsToAdd_justForErrReporting = new LDBStructsChildren(this, kidsIn); hardMust2(!kids.isEmpty, // may disable this later () => "adding to kids when kids is empty, " + $"kids is\n{kids.toDebugStr()}\n" + $"Trying to add\n{kidsToAdd_justForErrReporting.toDebugStr()}\n" + $"in {className}"); Array.ForEach(kidsIn, xkid => { // setParentOnKid(xkid.kid); xkid.kid.setMyParentTo(this); kids.Add(xkid); }); } public virtual IdentsUsed getAllIDs() { /* This is overridden only in TemplatedAgg, and that's only to deliberately fail. There's something wrong there, todo - un-virtualise this and remove the one override. - todo */ var iu = new IdentsUsed(); this._getAllIDs(iu); return iu; } public virtual void _getAllIDs(IdentsUsed iu) { // Gets all the identifiers explicitly used (read or written) ----- todo update this // anywhere in the AST. Does not get idents which are // declarations ie. parameters, or var/const declarations // unless these are combined with an assignment. // // Implied idents such as those from tbl.* are implicit and ignored. // // Root caller supplies new, empty iu // need usage here - todo docs// kids._getAllIDs(iu); } public abstract bool canGetAllIdentsUsed { // This seems to be true on most classes so I could set true // as a default here and override it where it's not wanted, saving // a chunk of code, but I'd rather make it explicit to force me to consider each case. A bit more C&P for less chance of bugs. get; } // Produces a string that should be identical to (excepting // whitespaces) the input ie. emit LDB code not valid SQL // // ToString was causing too many weird problems (due to my // inexperience with c#) so just used toRawStr() public abstract string toRawStr(); public override string toDebugStr() => toRawStr(); } // end LDBASTobj public interface IhandlePrefixes where T : LDBRoot { public bool isPrefixed { get; } public bool isExcessPrefixed { get; } // The T in addPrefix/rePrefix/withoutPrefix is because we may be // returning eg. a list of items with prefixes // thus modified, not a single PMQIdent public T addPrefix(PMQIdent pfx); public T rePrefix(PMQIdent newpfx); // public T stripPrefix(PMQIdent id); } // todo - move this public abstract class LDBRoot { // Root of everything LDB-ish public bool Equals([AllowNull] LDBRoot other) { throw new LDBShouldNotBeImplementedException( "called Equals on LDBRoot"); } public override bool Equals([AllowNull] Object other) { throw new LDBShouldNotBeImplementedException( "called Equals in Object"); } public override int GetHashCode() { throw new LDBShouldNotBeImplementedException( "called GetHashCode in LDBRoot"); } public static bool operator ==(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called == in LDBRoot"); } public static bool operator !=(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called != in LDBRoot"); } public virtual string className { // todo - this needs checking it works as I want // also rename to ldbclassname -- todo get => this.GetType().FullName ?? "(unknown)"; } public virtual string classNameQ => "'" + className + "'"; public virtual string classNamePlus(string plus) => className + "." + plus; public override string ToString() { // This implicit to-string conversion crap cost me a few hours, so at least // do this & blow up at runtime. Would prefer compile time but it seems // not possible. This turns out to have been a good idea, // saved me quite a bit more debugging! // // Will try _validated; set { // hardMust(!validated, "... already validated... "); - no // It's hard to avoid validating a thing twice, so for now allow it // Look into it later, really shouldn't happen _validated = true; } } public LDBStructsChildren kids { get; private set; } = new LDBStructsChildren(noParent); // just easier public LDBASTobj() { //////////////////////////////////////////////////////////////// todo dead? // VStudio thinks this has zero references, which is true explicitly, but it's // called implicitly on every obj creation - or should do todo- check if (recordObjectsCreated) { AllCreatedItems.Add(this); } } public virtual void validate() { if (reportAlsoIfValidationOK) { debugmsg("validating..." + className + " (" + asNameOrIdentContents() + ")"); } mhp_mhk(); kids.validate(); // validated = true; -- No, get each subclass to set // validated. That forces an explicit call to base() which // is more code but ok. } public bool isValidated { get => validated; } // todo just => validated; public virtual bool isTopLevelItemSeq => false; public virtual void mhp_mhk() { // don't think this need be virtual - todo? // = must have parent, must have kids. The kids may be the // empty set and the parent may be noParent but they must // exist. // Why not just inline this into validate()? // Because a few classes need bespoke validation, but still must check this. hardMustParentIsPresent(); kids.eachKidParentMustBe(this); } public string asNameOrIdentContents() { // for very crude debugging var optName = this is IisNamedItem ni ? ni.name.toRawStr() : ""; optName = (optName == "" && this is PMQIdent) ? this.toRawStr() : ""; var optName2 = optName + " (uid:" + uid.ToString() + ") " + getFirstFewCharsOf(toRawStr()); return optName2; } public virtual void getAllUIDs(UIDset uids) { uids.Add(this); kids.ForEach(namedKid => namedKid.kid.getAllUIDs(uids)); } public bool isStubParent { get => (this is NoParent) || (this is FakeParent); } public void validateSpecificItems(params LDBASTobj[] items) { Array.ForEach(items, item => item.validate()); } public T? optClimbTo() where T : LDBASTobj { var resItem = this; while (!(resItem is T)) { if (resItem.parent.isStubParent) { return null; } resItem = resItem.parent; } var res = resItem as T; return res; } public T reqClimbTo() where T : LDBASTobj { var loc = className + ".reqClimbTo()"; var res = optClimbTo(); hardMust2(!(res is null), // Thanks SO // Todo - use this elsewhere, poss ExpectException? todo () => loc + nl + ", climbed to top, didn't find type " + typeof(T).FullName + nl + "Hierarchy is: " + nl + getItemWithParents(this)); if (res is null) { throw new NNQC(); } return res; } public void hardMustParentIsPresent() { hardMust2(!(parent is null), () => "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust2(!(this.parent is NoParent), () => "Parent is NoParent on object of type\n" + $"'{this.className}'"); } public void hardMustParentIsAbsent() { // For checking cloning - parent should be NoParent immediately // after, before it's set by parent, and never null hardMust(!(parent is null), "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust((this.parent is NoParent), "Parent should be NoParent on object of type\n" + $"'{this.className}'"); } public bool hasParent

() { hardMust(!(this is NoParent), "Item is NoParent"); return this.parent is P; } public bool hasAncestor() { hardMust(!this.isStubParent, "Item is NoParent"); // how to do with a switch expr? if (this.parent.isStubParent /* is NoParent || this.parent is FakeParent*/ ) { // todo do as x.hasFudgedParent() - todo return false; } else if (this.parent is A) { return true; } else { return this.parent.hasAncestor(); } } public bool notHasAncestor() => !hasAncestor(); public void setMyParentTo(LDBASTobj prnt) { // An object may only set its parent if its current parent is // the dummy 'noParent', which almost every object's parent is // set to on creation (see LDBASTobj parent property def). // The test for null is peculiar given this. Because // noParent is created statically, setting the parent of // noParent is difficult, perhaps impossible, to do statically // and reliably.Despite the declaration '#nullable enable', it // gets created with a null parrent (quite reasonably), hence // this check.The actual setting of the parent has to be done // at runtime i.e.after the static creation of 'noParent'. hardMust2((this.parent is NoParent) || (this.parent is null), () => "tried to re-set parent on object of type: " + nl + this.classNameQ + nl + "Original parent type is: " + (this?.parent?.classNameQ == null ? "null" : this.parent.classNameQ) + nl + "and new parent type is: " + prnt.classNameQ + nl + "Item having parent set on it is: " // because object may not be fully constructed yet, check howver TODO this is a major hack and must be fixed sometime. // + nl + (this is null ? "(null)" : this.toRawStr())); + nl + this.toRawStr() ); parent = prnt; } public void forceNewParentForRootObject() { // Every object must have some parent, including the // root object of the parse, which is LDBitems // (a sequence of LDB items), so this forces one. // It is only for that purpose! // // LDBitems obj exists for sub objs such as the statements // in a while loop, in a proc, in an if... etc., for // which their parent is the while/proc/if etc. hardMustParentIsAbsent(); this.parent = fakeParent; } //public void resetMyParentTo(LDBASTobj prnt) { ////////////////////////////////// destroy!!!!!! todo // parent = prnt; //} //////////////////////// xxxxxxxxxxxxx delete - todo //private void setParentOnKid(LDBASTobj kid) => // todo needed? // // check parente doesn't exist - todo // kid.parent = this; public virtual void setParentOnKidsAndMakeKidsDict( // todo - and add to kids dict // this mixes up setting parent and making child dict. Not good. - todo params (string kidName, LDBASTobj kid)[] kidsIn) { var loc = classNamePlus("setParentOnKidsAndMakeKidsDict"); var kidsToAdd = new LDBStructsChildren(this, kidsIn); hardMust2(kids.isEmpty, () => $"setting kids on parent '{className}'" + "when kids is not empty, " + $"kids is ({kids.Count} kids):" + nl + kids.toDebugStr() + nl + $"Trying to add\n{kidsToAdd.toDebugStr()}\n" + $"in {className}"); // if there is an LDBStructsChildren and another child, // this will blow up - will need to do kids.Add or // something - in fact, kids needs to be created in one // go, which is what happens. kids = kidsToAdd; ///////////////////////////////////////////////////////// why this? todo Array.ForEach(kidsIn, xkid => hardMust(!(xkid.kid is null), loc, "null kid")); Array.ForEach(kidsIn, xkid => xkid.kid.setMyParentTo(this)); } public virtual void setParentOnKidsAndAddToKidsDict( params (string kidName, LDBASTobj kid)[] kidsIn) { var kidsToAdd_justForErrReporting = new LDBStructsChildren(this, kidsIn); hardMust2(!kids.isEmpty, // may disable this later () => "adding to kids when kids is empty, " + $"kids is\n{kids.toDebugStr()}\n" + $"Trying to add\n{kidsToAdd_justForErrReporting.toDebugStr()}\n" + $"in {className}"); Array.ForEach(kidsIn, xkid => { // setParentOnKid(xkid.kid); xkid.kid.setMyParentTo(this); kids.Add(xkid); }); } public virtual IdentsUsed getAllIDs() { /* This is overridden only in TemplatedAgg, and that's only to deliberately fail. There's something wrong there, todo - un-virtualise this and remove the one override. - todo */ var iu = new IdentsUsed(); this._getAllIDs(iu); return iu; } public virtual void _getAllIDs(IdentsUsed iu) { // Gets all the identifiers explicitly used (read or written) ----- todo update this // anywhere in the AST. Does not get idents which are // declarations ie. parameters, or var/const declarations // unless these are combined with an assignment. // // Implied idents such as those from tbl.* are implicit and ignored. // // Root caller supplies new, empty iu // need usage here - todo docs// kids._getAllIDs(iu); } public abstract bool canGetAllIdentsUsed { // This seems to be true on most classes so I could set true // as a default here and override it where it's not wanted, saving // a chunk of code, but I'd rather make it explicit to force me to consider each case. A bit more C&P for less chance of bugs. get; } // Produces a string that should be identical to (excepting // whitespaces) the input ie. emit LDB code not valid SQL // // ToString was causing too many weird problems (due to my // inexperience with c#) so just used toRawStr() public abstract string toRawStr(); public override string toDebugStr() => toRawStr(); } // end LDBASTobj public interface IhandlePrefixes where T : LDBRoot { public bool isPrefixed { get; } public bool isExcessPrefixed { get; } // The T in addPrefix/rePrefix/withoutPrefix is because we may be // returning eg. a list of items with prefixes // thus modified, not a single PMQIdent public T addPrefix(PMQIdent pfx); public T rePrefix(PMQIdent newpfx); // public T stripPrefix(PMQIdent id); } // todo - move this public abstract class LDBRoot { // Root of everything LDB-ish public bool Equals([AllowNull] LDBRoot other) { throw new LDBShouldNotBeImplementedException( "called Equals on LDBRoot"); } public override bool Equals([AllowNull] Object other) { throw new LDBShouldNotBeImplementedException( "called Equals in Object"); } public override int GetHashCode() { throw new LDBShouldNotBeImplementedException( "called GetHashCode in LDBRoot"); } public static bool operator ==(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called == in LDBRoot"); } public static bool operator !=(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called != in LDBRoot"); } public virtual string className { // todo - this needs checking it works as I want // also rename to ldbclassname -- todo get => this.GetType().FullName ?? "(unknown)"; } public virtual string classNameQ => "'" + className + "'"; public virtual string classNamePlus(string plus) => className + "." + plus; public override string ToString() { // This implicit to-string conversion crap cost me a few hours, so at least // do this & blow up at runtime. Would prefer compile time but it seems // not possible. This turns out to have been a good idea, // saved me quite a bit more debugging! // // Will try _validated; set { // hardMust(!validated, "... already validated... "); - no // It's hard to avoid validating a thing twice, so for now allow it // Look into it later, really shouldn't happen _validated = true; } } public LDBStructsChildren kids { get; private set; } = new LDBStructsChildren(noParent); // just easier public LDBASTobj() { //////////////////////////////////////////////////////////////// todo dead? // VStudio thinks this has zero references, which is true explicitly, but it's // called implicitly on every obj creation - or should do todo- check if (recordObjectsCreated) { AllCreatedItems.Add(this); } } public virtual void validate() { if (reportAlsoIfValidationOK) { debugmsg("validating..." + className + " (" + asNameOrIdentContents() + ")"); } mhp_mhk(); kids.validate(); // validated = true; -- No, get each subclass to set // validated. That forces an explicit call to base() which // is more code but ok. } public bool isValidated { get => validated; } // todo just => validated; public virtual bool isTopLevelItemSeq => false; public virtual void mhp_mhk() { // don't think this need be virtual - todo? // = must have parent, must have kids. The kids may be the // empty set and the parent may be noParent but they must // exist. // Why not just inline this into validate()? // Because a few classes need bespoke validation, but still must check this. hardMustParentIsPresent(); kids.eachKidParentMustBe(this); } public string asNameOrIdentContents() { // for very crude debugging var optName = this is IisNamedItem ni ? ni.name.toRawStr() : ""; optName = (optName == "" && this is PMQIdent) ? this.toRawStr() : ""; var optName2 = optName + " (uid:" + uid.ToString() + ") " + getFirstFewCharsOf(toRawStr()); return optName2; } public virtual void getAllUIDs(UIDset uids) { uids.Add(this); kids.ForEach(namedKid => namedKid.kid.getAllUIDs(uids)); } public bool isStubParent { get => (this is NoParent) || (this is FakeParent); } public void validateSpecificItems(params LDBASTobj[] items) { Array.ForEach(items, item => item.validate()); } public T? optClimbTo() where T : LDBASTobj { var resItem = this; while (!(resItem is T)) { if (resItem.parent.isStubParent) { return null; } resItem = resItem.parent; } var res = resItem as T; return res; } public T reqClimbTo() where T : LDBASTobj { var loc = className + ".reqClimbTo()"; var res = optClimbTo(); hardMust2(!(res is null), // Thanks SO // Todo - use this elsewhere, poss ExpectException? todo () => loc + nl + ", climbed to top, didn't find type " + typeof(T).FullName + nl + "Hierarchy is: " + nl + getItemWithParents(this)); if (res is null) { throw new NNQC(); } return res; } public void hardMustParentIsPresent() { hardMust2(!(parent is null), () => "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust2(!(this.parent is NoParent), () => "Parent is NoParent on object of type\n" + $"'{this.className}'"); } public void hardMustParentIsAbsent() { // For checking cloning - parent should be NoParent immediately // after, before it's set by parent, and never null hardMust(!(parent is null), "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust((this.parent is NoParent), "Parent should be NoParent on object of type\n" + $"'{this.className}'"); } public bool hasParent

() { hardMust(!(this is NoParent), "Item is NoParent"); return this.parent is P; } public bool hasAncestor() { hardMust(!this.isStubParent, "Item is NoParent"); // how to do with a switch expr? if (this.parent.isStubParent /* is NoParent || this.parent is FakeParent*/ ) { // todo do as x.hasFudgedParent() - todo return false; } else if (this.parent is A) { return true; } else { return this.parent.hasAncestor(); } } public bool notHasAncestor() => !hasAncestor(); public void setMyParentTo(LDBASTobj prnt) { // An object may only set its parent if its current parent is // the dummy 'noParent', which almost every object's parent is // set to on creation (see LDBASTobj parent property def). // The test for null is peculiar given this. Because // noParent is created statically, setting the parent of // noParent is difficult, perhaps impossible, to do statically // and reliably.Despite the declaration '#nullable enable', it // gets created with a null parrent (quite reasonably), hence // this check.The actual setting of the parent has to be done // at runtime i.e.after the static creation of 'noParent'. hardMust2((this.parent is NoParent) || (this.parent is null), () => "tried to re-set parent on object of type: " + nl + this.classNameQ + nl + "Original parent type is: " + (this?.parent?.classNameQ == null ? "null" : this.parent.classNameQ) + nl + "and new parent type is: " + prnt.classNameQ + nl + "Item having parent set on it is: " // because object may not be fully constructed yet, check howver TODO this is a major hack and must be fixed sometime. // + nl + (this is null ? "(null)" : this.toRawStr())); + nl + this.toRawStr() ); parent = prnt; } public void forceNewParentForRootObject() { // Every object must have some parent, including the // root object of the parse, which is LDBitems // (a sequence of LDB items), so this forces one. // It is only for that purpose! // // LDBitems obj exists for sub objs such as the statements // in a while loop, in a proc, in an if... etc., for // which their parent is the while/proc/if etc. hardMustParentIsAbsent(); this.parent = fakeParent; } //public void resetMyParentTo(LDBASTobj prnt) { ////////////////////////////////// destroy!!!!!! todo // parent = prnt; //} //////////////////////// xxxxxxxxxxxxx delete - todo //private void setParentOnKid(LDBASTobj kid) => // todo needed? // // check parente doesn't exist - todo // kid.parent = this; public virtual void setParentOnKidsAndMakeKidsDict( // todo - and add to kids dict // this mixes up setting parent and making child dict. Not good. - todo params (string kidName, LDBASTobj kid)[] kidsIn) { var loc = classNamePlus("setParentOnKidsAndMakeKidsDict"); var kidsToAdd = new LDBStructsChildren(this, kidsIn); hardMust2(kids.isEmpty, () => $"setting kids on parent '{className}'" + "when kids is not empty, " + $"kids is ({kids.Count} kids):" + nl + kids.toDebugStr() + nl + $"Trying to add\n{kidsToAdd.toDebugStr()}\n" + $"in {className}"); // if there is an LDBStructsChildren and another child, // this will blow up - will need to do kids.Add or // something - in fact, kids needs to be created in one // go, which is what happens. kids = kidsToAdd; ///////////////////////////////////////////////////////// why this? todo Array.ForEach(kidsIn, xkid => hardMust(!(xkid.kid is null), loc, "null kid")); Array.ForEach(kidsIn, xkid => xkid.kid.setMyParentTo(this)); } public virtual void setParentOnKidsAndAddToKidsDict( params (string kidName, LDBASTobj kid)[] kidsIn) { var kidsToAdd_justForErrReporting = new LDBStructsChildren(this, kidsIn); hardMust2(!kids.isEmpty, // may disable this later () => "adding to kids when kids is empty, " + $"kids is\n{kids.toDebugStr()}\n" + $"Trying to add\n{kidsToAdd_justForErrReporting.toDebugStr()}\n" + $"in {className}"); Array.ForEach(kidsIn, xkid => { // setParentOnKid(xkid.kid); xkid.kid.setMyParentTo(this); kids.Add(xkid); }); } public virtual IdentsUsed getAllIDs() { /* This is overridden only in TemplatedAgg, and that's only to deliberately fail. There's something wrong there, todo - un-virtualise this and remove the one override. - todo */ var iu = new IdentsUsed(); this._getAllIDs(iu); return iu; } public virtual void _getAllIDs(IdentsUsed iu) { // Gets all the identifiers explicitly used (read or written) ----- todo update this // anywhere in the AST. Does not get idents which are // declarations ie. parameters, or var/const declarations // unless these are combined with an assignment. // // Implied idents such as those from tbl.* are implicit and ignored. // // Root caller supplies new, empty iu // need usage here - todo docs// kids._getAllIDs(iu); } public abstract bool canGetAllIdentsUsed { // This seems to be true on most classes so I could set true // as a default here and override it where it's not wanted, saving // a chunk of code, but I'd rather make it explicit to force me to consider each case. A bit more C&P for less chance of bugs. get; } // Produces a string that should be identical to (excepting // whitespaces) the input ie. emit LDB code not valid SQL // // ToString was causing too many weird problems (due to my // inexperience with c#) so just used toRawStr() public abstract string toRawStr(); public override string toDebugStr() => toRawStr(); } // end LDBASTobj public interface IhandlePrefixes where T : LDBRoot { public bool isPrefixed { get; } public bool isExcessPrefixed { get; } // The T in addPrefix/rePrefix/withoutPrefix is because we may be // returning eg. a list of items with prefixes // thus modified, not a single PMQIdent public T addPrefix(PMQIdent pfx); public T rePrefix(PMQIdent newpfx); // public T stripPrefix(PMQIdent id); } // todo - move this public abstract class LDBRoot { // Root of everything LDB-ish public bool Equals([AllowNull] LDBRoot other) { throw new LDBShouldNotBeImplementedException( "called Equals on LDBRoot"); } public override bool Equals([AllowNull] Object other) { throw new LDBShouldNotBeImplementedException( "called Equals in Object"); } public override int GetHashCode() { throw new LDBShouldNotBeImplementedException( "called GetHashCode in LDBRoot"); } public static bool operator ==(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called == in LDBRoot"); } public static bool operator !=(LDBRoot lhs, LDBRoot rhs) { throw new LDBShouldNotBeImplementedException( "called != in LDBRoot"); } public virtual string className { // todo - this needs checking it works as I want // also rename to ldbclassname -- todo get => this.GetType().FullName ?? "(unknown)"; } public virtual string classNameQ => "'" + className + "'"; public virtual string classNamePlus(string plus) => className + "." + plus; public override string ToString() { // This implicit to-string conversion crap cost me a few hours, so at least // do this & blow up at runtime. Would prefer compile time but it seems // not possible. This turns out to have been a good idea, // saved me quite a bit more debugging! // // Will try _validated; set { // hardMust(!validated, "... already validated... "); - no // It's hard to avoid validating a thing twice, so for now allow it // Look into it later, really shouldn't happen _validated = true; } } public LDBStructsChildren kids { get; private set; } = new LDBStructsChildren(noParent); // just easier public LDBASTobj() { //////////////////////////////////////////////////////////////// todo dead? // VStudio thinks this has zero references, which is true explicitly, but it's // called implicitly on every obj creation - or should do todo- check if (recordObjectsCreated) { AllCreatedItems.Add(this); } } public virtual void validate() { if (reportAlsoIfValidationOK) { debugmsg("validating..." + className + " (" + asNameOrIdentContents() + ")"); } mhp_mhk(); kids.validate(); // validated = true; -- No, get each subclass to set // validated. That forces an explicit call to base() which // is more code but ok. } public bool isValidated { get => validated; } // todo just => validated; public virtual bool isTopLevelItemSeq => false; public virtual void mhp_mhk() { // don't think this need be virtual - todo? // = must have parent, must have kids. The kids may be the // empty set and the parent may be noParent but they must // exist. // Why not just inline this into validate()? // Because a few classes need bespoke validation, but still must check this. hardMustParentIsPresent(); kids.eachKidParentMustBe(this); } public string asNameOrIdentContents() { // for very crude debugging var optName = this is IisNamedItem ni ? ni.name.toRawStr() : ""; optName = (optName == "" && this is PMQIdent) ? this.toRawStr() : ""; var optName2 = optName + " (uid:" + uid.ToString() + ") " + getFirstFewCharsOf(toRawStr()); return optName2; } public virtual void getAllUIDs(UIDset uids) { uids.Add(this); kids.ForEach(namedKid => namedKid.kid.getAllUIDs(uids)); } public bool isStubParent { get => (this is NoParent) || (this is FakeParent); } public void validateSpecificItems(params LDBASTobj[] items) { Array.ForEach(items, item => item.validate()); } public T? optClimbTo() where T : LDBASTobj { var resItem = this; while (!(resItem is T)) { if (resItem.parent.isStubParent) { return null; } resItem = resItem.parent; } var res = resItem as T; return res; } public T reqClimbTo() where T : LDBASTobj { var loc = className + ".reqClimbTo()"; var res = optClimbTo(); hardMust2(!(res is null), // Thanks SO // Todo - use this elsewhere, poss ExpectException? todo () => loc + nl + ", climbed to top, didn't find type " + typeof(T).FullName + nl + "Hierarchy is: " + nl + getItemWithParents(this)); if (res is null) { throw new NNQC(); } return res; } public void hardMustParentIsPresent() { hardMust2(!(parent is null), () => "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust2(!(this.parent is NoParent), () => "Parent is NoParent on object of type\n" + $"'{this.className}'"); } public void hardMustParentIsAbsent() { // For checking cloning - parent should be NoParent immediately // after, before it's set by parent, and never null hardMust(!(parent is null), "NULL parent for item of type\n" + $"{GetType()}\n."); hardMust((this.parent is NoParent), "Parent should be NoParent on object of type\n" + $"'{this.className}'"); } public bool hasParent

() { hardMust(!(this is NoParent), "Item is NoParent"); return this.parent is P; } public bool hasAncestor() { hardMust(!this.isStubParent, "Item is NoParent"); // how to do with a switch expr? if (this.parent.isStubParent /* is NoParent || this.parent is FakeParent*/ ) { // todo do as x.hasFudgedParent() - todo return false; } else if (this.parent is A) { return true; } else { return this.parent.hasAncestor(); } } public bool notHasAncestor() => !hasAncestor(); public void setMyParentTo(LDBASTobj prnt) { // An object may only set its parent if its current parent is // the dummy 'noParent', which almost every object's parent is // set to on creation (see LDBASTobj parent property def). // The test for null is peculiar given this. Because // noParent is created statically, setting the parent of // noParent is difficult, perhaps impossible, to do statically // and reliably.Despite the declaration '#nullable enable', it // gets created with a null parrent (quite reasonably), hence // this check.The actual setting of the parent has to be done // at runtime i.e.after the static creation of 'noParent'. hardMust2((this.parent is NoParent) || (this.parent is null), () => "tried to re-set parent on object of type: " + nl + this.classNameQ + nl + "Original parent type is: " + (this?.parent?.classNameQ == null ? "null" : this.parent.classNameQ) + nl + "and new parent type is: " + prnt.classNameQ + nl + "Item having parent set on it is: " // because object may not be fully constructed yet, check howver TODO this is a major hack and must be fixed sometime. // + nl + (this is null ? "(null)" : this.toRawStr())); + nl + this.toRawStr() ); parent = prnt; } public void forceNewParentForRootObject() { // Every object must have some parent, including the // root object of the parse, which is LDBitems // (a sequence of LDB items), so this forces one. // It is only for that purpose! // // LDBitems obj exists for sub objs such as the statements // in a while loop, in a proc, in an if... etc., for // which their parent is the while/proc/if etc. hardMustParentIsAbsent(); this.parent = fakeParent; } //public void resetMyParentTo(LDBASTobj prnt) { ////////////////////////////////// destroy!!!!!! todo // parent = prnt; //} //////////////////////// xxxxxxxxxxxxx delete - todo //private void setParentOnKid(LDBASTobj kid) => // todo needed? // // check parente doesn't exist - todo // kid.parent = this; public virtual void setParentOnKidsAndMakeKidsDict( // todo - and add to kids dict // this mixes up setting parent and making child dict. Not good. - todo params (string kidName, LDBASTobj kid)[] kidsIn) { var loc = classNamePlus("setParentOnKidsAndMakeKidsDict"); var kidsToAdd = new LDBStructsChildren(this, kidsIn); hardMust2(kids.isEmpty, () => $"setting kids on parent '{className}'" + "when kids is not empty, " + $"kids is ({kids.Count} kids):" + nl + kids.toDebugStr() + nl + $"Trying to add\n{kidsToAdd.toDebugStr()}\n" + $"in {className}"); // if there is an LDBStructsChildren and another child, // this will blow up - will need to do kids.Add or // something - in fact, kids needs to be created in one // go, which is what happens. kids = kidsToAdd; ///////////////////////////////////////////////////////// why this? todo Array.ForEach(kidsIn, xkid => hardMust(!(xkid.kid is null), loc, "null kid")); Array.ForEach(kidsIn, xkid => xkid.kid.setMyParentTo(this)); } public virtual void setParentOnKidsAndAddToKidsDict( params (string kidName, LDBASTobj kid)[] kidsIn) { var kidsToAdd_justForErrReporting = new LDBStructsChildren(this, kidsIn); hardMust2(!kids.isEmpty, // may disable this later () => "adding to kids when kids is empty, " + $"kids is\n{kids.toDebugStr()}\n" + $"Trying to add\n{kidsToAdd_justForErrReporting.toDebugStr()}\n" + $"in {className}"); Array.ForEach(kidsIn, xkid => { // setParentOnKid(xkid.kid); xkid.kid.setMyParentTo(this); kids.Add(xkid); }); } public virtual IdentsUsed getAllIDs() { /* This is overridden only in TemplatedAgg, and that's only to deliberately fail. There's something wrong there, todo - un-virtualise this and remove the one override. - todo */ var iu = new IdentsUsed(); this._getAllIDs(iu); return iu; } public virtual void _getAllIDs(IdentsUsed iu) { // Gets all the identifiers explicitly used (read or written) ----- todo update this // anywhere in the AST. Does not get idents which are // declarations ie. parameters, or var/const declarations // unless these are combined with an assignment. // // Implied idents such as those from tbl.* are implicit and ignored. // // Root caller supplies new, empty iu // need usage here - todo docs// kids._getAllIDs(iu); } public abstract bool canGetAllIdentsUsed { // This seems to be true on most classes so I could set true // as a default here and override it where it's not wanted, saving // a chunk of code, but I'd rather make it explicit to force me to consider each case. A bit more C&P for less chance of bugs. get; } // Produces a string that should be identical to (excepting // whitespaces) the input ie. emit LDB code not valid SQL // // ToString was causing too many weird problems (due to my // inexperience with c#) so just used toRawStr() public abstract string toRawStr(); public override string toDebugStr() => toRawStr(); } // end LDBASTobj }