* bug#48871: 27.2; Unusably slow in C# mode
2021-06-06 13:50 ` Eli Zaretskii
@ 2021-06-06 14:53 ` jan via Bug reports for GNU Emacs, the Swiss army knife of text editors
2021-06-06 17:00 ` Eli Zaretskii
0 siblings, 1 reply; 11+ messages in thread
From: jan via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-06-06 14:53 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: 48871
[-- Attachment #1: Type: text/plain, Size: 2205 bytes --]
Hi,
I don't recall installing c# but may well have happened.
From package-list-packages:
csharp-mode 20210328.2004 installed C# mode derived mode
Which does not say built-in so likely I did. Looking in the unzipped
emacs 27.2 , no relevant *sharp* file in it, and did find it in the
elpa directory, so I guess must have.
Started with -Q. Did the M-x load-file for csharp-mode.el (FYI also
had to do load-file for csharp-compilation.el before that to make it
happy).
Finally got to load the C# file itself, exactly the same. No faster.
Troublesome C# file attached.
cheers
jan
On 06/06/2021, Eli Zaretskii <eliz@gnu.org> wrote:
>> From: jan <rtm443x@googlemail.com>
>> Date: Sun, 6 Jun 2021 14:34:19 +0100
>> Cc: 48871@debbugs.gnu.org
>>
>> had already tried the -Q option. Emacs started but when I tried "M-x
>> csharp-mode" it didn't recognise it. I tried exactly the same on the
>> normally-started emacs to check I was entering it correctly and that
>> did understand it.
>> I guess the -Q effectively disables some modes? I was surprised.
>>
>> Yep, customisation may well be the issue here.
>
> So let's try to figure out why "emacs -Q" fails to load csharp-mode.
> Since there's no such mode bundled with Emacs, I guess you downloaded
> it from somewhere? Then try this:
>
> emacs -Q
> M-x load-file RET /path/to/csharp-mode.el RE
>
> The last line assumes that the Lisp file which defines the function
> csharp-mode is called csharp-mode.el; if not, change the file name to
> fit the reality. Also, "/path/to/" should be replaced with the actual
> absolute file name of the file on your system.
>
> Then say what you tried:
>
> M-x csharp-mode RET
>
> If that still doesn't work, please show the error messages. Likely
> they will identify packages csharp-mode depends on that you also need
> to load with "M-x load-file".
>
>> I have a file of equivalent size which should demo the issue but can't
>> zip it as gmail blocks anything with a zip attached, I can post as
>> attachment directly but it's 220KBytes, you ok with that on your
>> mailing list?
>
> Yes, it's okay. But let's first try and see whether "emacs -Q"
> exhibits the same problem, okay?
>
[-- Attachment #2: expression.cs --]
[-- Type: text/plain, Size: 224343 bytes --]
// 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<T>
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!
// <https://stackoverflow.com/questions/15345517/is-it-possible-to-disable-implicit-tostring-call>
// Will try <https://stackoverflow.com/questions/54354342/how-to-get-an-error-shown-when-using-non-strings-as-strings-instead-of-an-autom/54355115#54355115)
var msg = "LDBRoot.ToString()";
throw new LDBToStringImplictConversionException(msg);
}
public abstract string toDebugStr();
}
public abstract class Ephemeral : LDBRoot {
// for objects that are not part of the AST
}
public interface IWithLineNUm { // todo - use or remove - todo
// public
}
public interface IHasTableAlias {
// Has a table alias AST obj. May be a ...Missing subtype in which
// it has no alias given by the user, but alias AST is there. More
// accurately a TableSource alias but ok
public abstract TableAlias ta { get; set; }
}
// Base class for all LDB AST objects
public abstract class LDBASTobj : LDBRoot {
// This one is a problem. If I declare the 'parent' property
// abstract I then have to implement it literally hundreds of
// times in the derived classes, and I really don't think it
// would help so just set it to a default which gets overwritten later.
//
// The problem is that those objects were created by the
// parser, which creates them bottom-up, which means you can't
// know the parent when they are created. It should be
// possible to work around this but it does mean reworking the
// parser fundamentally, and I'm not going to do that
// assumeing it's even possible.
// todo - read up on priv/pub/prog accessors I really don't
// get these at all
public LDBASTobj parent { get; private set; }
= noParent;
// Unique ID per object, useful for distinguishing them
// without general equality.
readonly public ulong uid = newUID();
// public abstract int lineNum { get; set; }
// Is validated is a cheapish check to ensure everything gets
// validated
// can do this as auto prop? todo
private bool _validated = false;
protected bool validated {
get => _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<T>() 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<T>() where T : LDBASTobj {
var loc = className + ".reqClimbTo<T>()";
var res = optClimbTo<T>();
hardMust2(!(res is null),
// Thanks SO <https://stackoverflow.com/questions/2581642/how-do-i-get-the-type-name-of-a-generic-type-argument>
// 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<P>() {
hardMust(!(this is NoParent), "Item is NoParent");
return this.parent is P;
}
public bool hasAncestor<A>() {
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<A>();
}
}
public bool notHasAncestor<A>() =>
!hasAncestor<A>();
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<T>
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!
// <https://stackoverflow.com/questions/15345517/is-it-possible-to-disable-implicit-tostring-call>
// Will try <https://stackoverflow.com/questions/54354342/how-to-get-an-error-shown-when-using-non-strings-as-strings-instead-of-an-autom/54355115#54355115)
var msg = "LDBRoot.ToString()";
throw new LDBToStringImplictConversionException(msg);
}
public abstract string toDebugStr();
}
public abstract class Ephemeral : LDBRoot {
// for objects that are not part of the AST
}
public interface IWithLineNUm { // todo - use or remove - todo
// public
}
public interface IHasTableAlias {
// Has a table alias AST obj. May be a ...Missing subtype in which
// it has no alias given by the user, but alias AST is there. More
// accurately a TableSource alias but ok
public abstract TableAlias ta { get; set; }
}
// Base class for all LDB AST objects
public abstract class LDBASTobj : LDBRoot {
// This one is a problem. If I declare the 'parent' property
// abstract I then have to implement it literally hundreds of
// times in the derived classes, and I really don't think it
// would help so just set it to a default which gets overwritten later.
//
// The problem is that those objects were created by the
// parser, which creates them bottom-up, which means you can't
// know the parent when they are created. It should be
// possible to work around this but it does mean reworking the
// parser fundamentally, and I'm not going to do that
// assumeing it's even possible.
// todo - read up on priv/pub/prog accessors I really don't
// get these at all
public LDBASTobj parent { get; private set; }
= noParent;
// Unique ID per object, useful for distinguishing them
// without general equality.
readonly public ulong uid = newUID();
// public abstract int lineNum { get; set; }
// Is validated is a cheapish check to ensure everything gets
// validated
// can do this as auto prop? todo
private bool _validated = false;
protected bool validated {
get => _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<T>() 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<T>() where T : LDBASTobj {
var loc = className + ".reqClimbTo<T>()";
var res = optClimbTo<T>();
hardMust2(!(res is null),
// Thanks SO <https://stackoverflow.com/questions/2581642/how-do-i-get-the-type-name-of-a-generic-type-argument>
// 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<P>() {
hardMust(!(this is NoParent), "Item is NoParent");
return this.parent is P;
}
public bool hasAncestor<A>() {
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<A>();
}
}
public bool notHasAncestor<A>() =>
!hasAncestor<A>();
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<T>
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!
// <https://stackoverflow.com/questions/15345517/is-it-possible-to-disable-implicit-tostring-call>
// Will try <https://stackoverflow.com/questions/54354342/how-to-get-an-error-shown-when-using-non-strings-as-strings-instead-of-an-autom/54355115#54355115)
var msg = "LDBRoot.ToString()";
throw new LDBToStringImplictConversionException(msg);
}
public abstract string toDebugStr();
}
public abstract class Ephemeral : LDBRoot {
// for objects that are not part of the AST
}
public interface IWithLineNUm { // todo - use or remove - todo
// public
}
public interface IHasTableAlias {
// Has a table alias AST obj. May be a ...Missing subtype in which
// it has no alias given by the user, but alias AST is there. More
// accurately a TableSource alias but ok
public abstract TableAlias ta { get; set; }
}
// Base class for all LDB AST objects
public abstract class LDBASTobj : LDBRoot {
// This one is a problem. If I declare the 'parent' property
// abstract I then have to implement it literally hundreds of
// times in the derived classes, and I really don't think it
// would help so just set it to a default which gets overwritten later.
//
// The problem is that those objects were created by the
// parser, which creates them bottom-up, which means you can't
// know the parent when they are created. It should be
// possible to work around this but it does mean reworking the
// parser fundamentally, and I'm not going to do that
// assumeing it's even possible.
// todo - read up on priv/pub/prog accessors I really don't
// get these at all
public LDBASTobj parent { get; private set; }
= noParent;
// Unique ID per object, useful for distinguishing them
// without general equality.
readonly public ulong uid = newUID();
// public abstract int lineNum { get; set; }
// Is validated is a cheapish check to ensure everything gets
// validated
// can do this as auto prop? todo
private bool _validated = false;
protected bool validated {
get => _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<T>() 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<T>() where T : LDBASTobj {
var loc = className + ".reqClimbTo<T>()";
var res = optClimbTo<T>();
hardMust2(!(res is null),
// Thanks SO <https://stackoverflow.com/questions/2581642/how-do-i-get-the-type-name-of-a-generic-type-argument>
// 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<P>() {
hardMust(!(this is NoParent), "Item is NoParent");
return this.parent is P;
}
public bool hasAncestor<A>() {
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<A>();
}
}
public bool notHasAncestor<A>() =>
!hasAncestor<A>();
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<T>
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!
// <https://stackoverflow.com/questions/15345517/is-it-possible-to-disable-implicit-tostring-call>
// Will try <https://stackoverflow.com/questions/54354342/how-to-get-an-error-shown-when-using-non-strings-as-strings-instead-of-an-autom/54355115#54355115)
var msg = "LDBRoot.ToString()";
throw new LDBToStringImplictConversionException(msg);
}
public abstract string toDebugStr();
}
public abstract class Ephemeral : LDBRoot {
// for objects that are not part of the AST
}
public interface IWithLineNUm { // todo - use or remove - todo
// public
}
public interface IHasTableAlias {
// Has a table alias AST obj. May be a ...Missing subtype in which
// it has no alias given by the user, but alias AST is there. More
// accurately a TableSource alias but ok
public abstract TableAlias ta { get; set; }
}
// Base class for all LDB AST objects
public abstract class LDBASTobj : LDBRoot {
// This one is a problem. If I declare the 'parent' property
// abstract I then have to implement it literally hundreds of
// times in the derived classes, and I really don't think it
// would help so just set it to a default which gets overwritten later.
//
// The problem is that those objects were created by the
// parser, which creates them bottom-up, which means you can't
// know the parent when they are created. It should be
// possible to work around this but it does mean reworking the
// parser fundamentally, and I'm not going to do that
// assumeing it's even possible.
// todo - read up on priv/pub/prog accessors I really don't
// get these at all
public LDBASTobj parent { get; private set; }
= noParent;
// Unique ID per object, useful for distinguishing them
// without general equality.
readonly public ulong uid = newUID();
// public abstract int lineNum { get; set; }
// Is validated is a cheapish check to ensure everything gets
// validated
// can do this as auto prop? todo
private bool _validated = false;
protected bool validated {
get => _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<T>() 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<T>() where T : LDBASTobj {
var loc = className + ".reqClimbTo<T>()";
var res = optClimbTo<T>();
hardMust2(!(res is null),
// Thanks SO <https://stackoverflow.com/questions/2581642/how-do-i-get-the-type-name-of-a-generic-type-argument>
// 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<P>() {
hardMust(!(this is NoParent), "Item is NoParent");
return this.parent is P;
}
public bool hasAncestor<A>() {
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<A>();
}
}
public bool notHasAncestor<A>() =>
!hasAncestor<A>();
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<T>
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!
// <https://stackoverflow.com/questions/15345517/is-it-possible-to-disable-implicit-tostring-call>
// Will try <https://stackoverflow.com/questions/54354342/how-to-get-an-error-shown-when-using-non-strings-as-strings-instead-of-an-autom/54355115#54355115)
var msg = "LDBRoot.ToString()";
throw new LDBToStringImplictConversionException(msg);
}
public abstract string toDebugStr();
}
public abstract class Ephemeral : LDBRoot {
// for objects that are not part of the AST
}
public interface IWithLineNUm { // todo - use or remove - todo
// public
}
public interface IHasTableAlias {
// Has a table alias AST obj. May be a ...Missing subtype in which
// it has no alias given by the user, but alias AST is there. More
// accurately a TableSource alias but ok
public abstract TableAlias ta { get; set; }
}
// Base class for all LDB AST objects
public abstract class LDBASTobj : LDBRoot {
// This one is a problem. If I declare the 'parent' property
// abstract I then have to implement it literally hundreds of
// times in the derived classes, and I really don't think it
// would help so just set it to a default which gets overwritten later.
//
// The problem is that those objects were created by the
// parser, which creates them bottom-up, which means you can't
// know the parent when they are created. It should be
// possible to work around this but it does mean reworking the
// parser fundamentally, and I'm not going to do that
// assumeing it's even possible.
// todo - read up on priv/pub/prog accessors I really don't
// get these at all
public LDBASTobj parent { get; private set; }
= noParent;
// Unique ID per object, useful for distinguishing them
// without general equality.
readonly public ulong uid = newUID();
// public abstract int lineNum { get; set; }
// Is validated is a cheapish check to ensure everything gets
// validated
// can do this as auto prop? todo
private bool _validated = false;
protected bool validated {
get => _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<T>() 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<T>() where T : LDBASTobj {
var loc = className + ".reqClimbTo<T>()";
var res = optClimbTo<T>();
hardMust2(!(res is null),
// Thanks SO <https://stackoverflow.com/questions/2581642/how-do-i-get-the-type-name-of-a-generic-type-argument>
// 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<P>() {
hardMust(!(this is NoParent), "Item is NoParent");
return this.parent is P;
}
public bool hasAncestor<A>() {
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<A>();
}
}
public bool notHasAncestor<A>() =>
!hasAncestor<A>();
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<T>
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!
// <https://stackoverflow.com/questions/15345517/is-it-possible-to-disable-implicit-tostring-call>
// Will try <https://stackoverflow.com/questions/54354342/how-to-get-an-error-shown-when-using-non-strings-as-strings-instead-of-an-autom/54355115#54355115)
var msg = "LDBRoot.ToString()";
throw new LDBToStringImplictConversionException(msg);
}
public abstract string toDebugStr();
}
public abstract class Ephemeral : LDBRoot {
// for objects that are not part of the AST
}
public interface IWithLineNUm { // todo - use or remove - todo
// public
}
public interface IHasTableAlias {
// Has a table alias AST obj. May be a ...Missing subtype in which
// it has no alias given by the user, but alias AST is there. More
// accurately a TableSource alias but ok
public abstract TableAlias ta { get; set; }
}
// Base class for all LDB AST objects
public abstract class LDBASTobj : LDBRoot {
// This one is a problem. If I declare the 'parent' property
// abstract I then have to implement it literally hundreds of
// times in the derived classes, and I really don't think it
// would help so just set it to a default which gets overwritten later.
//
// The problem is that those objects were created by the
// parser, which creates them bottom-up, which means you can't
// know the parent when they are created. It should be
// possible to work around this but it does mean reworking the
// parser fundamentally, and I'm not going to do that
// assumeing it's even possible.
// todo - read up on priv/pub/prog accessors I really don't
// get these at all
public LDBASTobj parent { get; private set; }
= noParent;
// Unique ID per object, useful for distinguishing them
// without general equality.
readonly public ulong uid = newUID();
// public abstract int lineNum { get; set; }
// Is validated is a cheapish check to ensure everything gets
// validated
// can do this as auto prop? todo
private bool _validated = false;
protected bool validated {
get => _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<T>() 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<T>() where T : LDBASTobj {
var loc = className + ".reqClimbTo<T>()";
var res = optClimbTo<T>();
hardMust2(!(res is null),
// Thanks SO <https://stackoverflow.com/questions/2581642/how-do-i-get-the-type-name-of-a-generic-type-argument>
// 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<P>() {
hardMust(!(this is NoParent), "Item is NoParent");
return this.parent is P;
}
public bool hasAncestor<A>() {
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<A>();
}
}
public bool notHasAncestor<A>() =>
!hasAncestor<A>();
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<T>
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!
// <https://stackoverflow.com/questions/15345517/is-it-possible-to-disable-implicit-tostring-call>
// Will try <https://stackoverflow.com/questions/54354342/how-to-get-an-error-shown-when-using-non-strings-as-strings-instead-of-an-autom/54355115#54355115)
var msg = "LDBRoot.ToString()";
throw new LDBToStringImplictConversionException(msg);
}
public abstract string toDebugStr();
}
public abstract class Ephemeral : LDBRoot {
// for objects that are not part of the AST
}
public interface IWithLineNUm { // todo - use or remove - todo
// public
}
public interface IHasTableAlias {
// Has a table alias AST obj. May be a ...Missing subtype in which
// it has no alias given by the user, but alias AST is there. More
// accurately a TableSource alias but ok
public abstract TableAlias ta { get; set; }
}
// Base class for all LDB AST objects
public abstract class LDBASTobj : LDBRoot {
// This one is a problem. If I declare the 'parent' property
// abstract I then have to implement it literally hundreds of
// times in the derived classes, and I really don't think it
// would help so just set it to a default which gets overwritten later.
//
// The problem is that those objects were created by the
// parser, which creates them bottom-up, which means you can't
// know the parent when they are created. It should be
// possible to work around this but it does mean reworking the
// parser fundamentally, and I'm not going to do that
// assumeing it's even possible.
// todo - read up on priv/pub/prog accessors I really don't
// get these at all
public LDBASTobj parent { get; private set; }
= noParent;
// Unique ID per object, useful for distinguishing them
// without general equality.
readonly public ulong uid = newUID();
// public abstract int lineNum { get; set; }
// Is validated is a cheapish check to ensure everything gets
// validated
// can do this as auto prop? todo
private bool _validated = false;
protected bool validated {
get => _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<T>() 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<T>() where T : LDBASTobj {
var loc = className + ".reqClimbTo<T>()";
var res = optClimbTo<T>();
hardMust2(!(res is null),
// Thanks SO <https://stackoverflow.com/questions/2581642/how-do-i-get-the-type-name-of-a-generic-type-argument>
// 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<P>() {
hardMust(!(this is NoParent), "Item is NoParent");
return this.parent is P;
}
public bool hasAncestor<A>() {
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<A>();
}
}
public bool notHasAncestor<A>() =>
!hasAncestor<A>();
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<T>
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!
// <https://stackoverflow.com/questions/15345517/is-it-possible-to-disable-implicit-tostring-call>
// Will try <https://stackoverflow.com/questions/54354342/how-to-get-an-error-shown-when-using-non-strings-as-strings-instead-of-an-autom/54355115#54355115)
var msg = "LDBRoot.ToString()";
throw new LDBToStringImplictConversionException(msg);
}
public abstract string toDebugStr();
}
public abstract class Ephemeral : LDBRoot {
// for objects that are not part of the AST
}
public interface IWithLineNUm { // todo - use or remove - todo
// public
}
public interface IHasTableAlias {
// Has a table alias AST obj. May be a ...Missing subtype in which
// it has no alias given by the user, but alias AST is there. More
// accurately a TableSource alias but ok
public abstract TableAlias ta { get; set; }
}
// Base class for all LDB AST objects
public abstract class LDBASTobj : LDBRoot {
// This one is a problem. If I declare the 'parent' property
// abstract I then have to implement it literally hundreds of
// times in the derived classes, and I really don't think it
// would help so just set it to a default which gets overwritten later.
//
// The problem is that those objects were created by the
// parser, which creates them bottom-up, which means you can't
// know the parent when they are created. It should be
// possible to work around this but it does mean reworking the
// parser fundamentally, and I'm not going to do that
// assumeing it's even possible.
// todo - read up on priv/pub/prog accessors I really don't
// get these at all
public LDBASTobj parent { get; private set; }
= noParent;
// Unique ID per object, useful for distinguishing them
// without general equality.
readonly public ulong uid = newUID();
// public abstract int lineNum { get; set; }
// Is validated is a cheapish check to ensure everything gets
// validated
// can do this as auto prop? todo
private bool _validated = false;
protected bool validated {
get => _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<T>() 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<T>() where T : LDBASTobj {
var loc = className + ".reqClimbTo<T>()";
var res = optClimbTo<T>();
hardMust2(!(res is null),
// Thanks SO <https://stackoverflow.com/questions/2581642/how-do-i-get-the-type-name-of-a-generic-type-argument>
// 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<P>() {
hardMust(!(this is NoParent), "Item is NoParent");
return this.parent is P;
}
public bool hasAncestor<A>() {
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<A>();
}
}
public bool notHasAncestor<A>() =>
!hasAncestor<A>();
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<T>
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!
// <https://stackoverflow.com/questions/15345517/is-it-possible-to-disable-implicit-tostring-call>
// Will try <https://stackoverflow.com/questions/54354342/how-to-get-an-error-shown-when-using-non-strings-as-strings-instead-of-an-autom/54355115#54355115)
var msg = "LDBRoot.ToString()";
throw new LDBToStringImplictConversionException(msg);
}
public abstract string toDebugStr();
}
public abstract class Ephemeral : LDBRoot {
// for objects that are not part of the AST
}
public interface IWithLineNUm { // todo - use or remove - todo
// public
}
public interface IHasTableAlias {
// Has a table alias AST obj. May be a ...Missing subtype in which
// it has no alias given by the user, but alias AST is there. More
// accurately a TableSource alias but ok
public abstract TableAlias ta { get; set; }
}
// Base class for all LDB AST objects
public abstract class LDBASTobj : LDBRoot {
// This one is a problem. If I declare the 'parent' property
// abstract I then have to implement it literally hundreds of
// times in the derived classes, and I really don't think it
// would help so just set it to a default which gets overwritten later.
//
// The problem is that those objects were created by the
// parser, which creates them bottom-up, which means you can't
// know the parent when they are created. It should be
// possible to work around this but it does mean reworking the
// parser fundamentally, and I'm not going to do that
// assumeing it's even possible.
// todo - read up on priv/pub/prog accessors I really don't
// get these at all
public LDBASTobj parent { get; private set; }
= noParent;
// Unique ID per object, useful for distinguishing them
// without general equality.
readonly public ulong uid = newUID();
// public abstract int lineNum { get; set; }
// Is validated is a cheapish check to ensure everything gets
// validated
// can do this as auto prop? todo
private bool _validated = false;
protected bool validated {
get => _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<T>() 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<T>() where T : LDBASTobj {
var loc = className + ".reqClimbTo<T>()";
var res = optClimbTo<T>();
hardMust2(!(res is null),
// Thanks SO <https://stackoverflow.com/questions/2581642/how-do-i-get-the-type-name-of-a-generic-type-argument>
// 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<P>() {
hardMust(!(this is NoParent), "Item is NoParent");
return this.parent is P;
}
public bool hasAncestor<A>() {
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<A>();
}
}
public bool notHasAncestor<A>() =>
!hasAncestor<A>();
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<T>
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!
// <https://stackoverflow.com/questions/15345517/is-it-possible-to-disable-implicit-tostring-call>
// Will try <https://stackoverflow.com/questions/54354342/how-to-get-an-error-shown-when-using-non-strings-as-strings-instead-of-an-autom/54355115#54355115)
var msg = "LDBRoot.ToString()";
throw new LDBToStringImplictConversionException(msg);
}
public abstract string toDebugStr();
}
public abstract class Ephemeral : LDBRoot {
// for objects that are not part of the AST
}
public interface IWithLineNUm { // todo - use or remove - todo
// public
}
public interface IHasTableAlias {
// Has a table alias AST obj. May be a ...Missing subtype in which
// it has no alias given by the user, but alias AST is there. More
// accurately a TableSource alias but ok
public abstract TableAlias ta { get; set; }
}
// Base class for all LDB AST objects
public abstract class LDBASTobj : LDBRoot {
// This one is a problem. If I declare the 'parent' property
// abstract I then have to implement it literally hundreds of
// times in the derived classes, and I really don't think it
// would help so just set it to a default which gets overwritten later.
//
// The problem is that those objects were created by the
// parser, which creates them bottom-up, which means you can't
// know the parent when they are created. It should be
// possible to work around this but it does mean reworking the
// parser fundamentally, and I'm not going to do that
// assumeing it's even possible.
// todo - read up on priv/pub/prog accessors I really don't
// get these at all
public LDBASTobj parent { get; private set; }
= noParent;
// Unique ID per object, useful for distinguishing them
// without general equality.
readonly public ulong uid = newUID();
// public abstract int lineNum { get; set; }
// Is validated is a cheapish check to ensure everything gets
// validated
// can do this as auto prop? todo
private bool _validated = false;
protected bool validated {
get => _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<T>() 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<T>() where T : LDBASTobj {
var loc = className + ".reqClimbTo<T>()";
var res = optClimbTo<T>();
hardMust2(!(res is null),
// Thanks SO <https://stackoverflow.com/questions/2581642/how-do-i-get-the-type-name-of-a-generic-type-argument>
// 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<P>() {
hardMust(!(this is NoParent), "Item is NoParent");
return this.parent is P;
}
public bool hasAncestor<A>() {
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<A>();
}
}
public bool notHasAncestor<A>() =>
!hasAncestor<A>();
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<T>
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!
// <https://stackoverflow.com/questions/15345517/is-it-possible-to-disable-implicit-tostring-call>
// Will try <https://stackoverflow.com/questions/54354342/how-to-get-an-error-shown-when-using-non-strings-as-strings-instead-of-an-autom/54355115#54355115)
var msg = "LDBRoot.ToString()";
throw new LDBToStringImplictConversionException(msg);
}
public abstract string toDebugStr();
}
public abstract class Ephemeral : LDBRoot {
// for objects that are not part of the AST
}
public interface IWithLineNUm { // todo - use or remove - todo
// public
}
public interface IHasTableAlias {
// Has a table alias AST obj. May be a ...Missing subtype in which
// it has no alias given by the user, but alias AST is there. More
// accurately a TableSource alias but ok
public abstract TableAlias ta { get; set; }
}
// Base class for all LDB AST objects
public abstract class LDBASTobj : LDBRoot {
// This one is a problem. If I declare the 'parent' property
// abstract I then have to implement it literally hundreds of
// times in the derived classes, and I really don't think it
// would help so just set it to a default which gets overwritten later.
//
// The problem is that those objects were created by the
// parser, which creates them bottom-up, which means you can't
// know the parent when they are created. It should be
// possible to work around this but it does mean reworking the
// parser fundamentally, and I'm not going to do that
// assumeing it's even possible.
// todo - read up on priv/pub/prog accessors I really don't
// get these at all
public LDBASTobj parent { get; private set; }
= noParent;
// Unique ID per object, useful for distinguishing them
// without general equality.
readonly public ulong uid = newUID();
// public abstract int lineNum { get; set; }
// Is validated is a cheapish check to ensure everything gets
// validated
// can do this as auto prop? todo
private bool _validated = false;
protected bool validated {
get => _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<T>() 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<T>() where T : LDBASTobj {
var loc = className + ".reqClimbTo<T>()";
var res = optClimbTo<T>();
hardMust2(!(res is null),
// Thanks SO <https://stackoverflow.com/questions/2581642/how-do-i-get-the-type-name-of-a-generic-type-argument>
// 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<P>() {
hardMust(!(this is NoParent), "Item is NoParent");
return this.parent is P;
}
public bool hasAncestor<A>() {
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<A>();
}
}
public bool notHasAncestor<A>() =>
!hasAncestor<A>();
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<T>
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!
// <https://stackoverflow.com/questions/15345517/is-it-possible-to-disable-implicit-tostring-call>
// Will try <https://stackoverflow.com/questions/54354342/how-to-get-an-error-shown-when-using-non-strings-as-strings-instead-of-an-autom/54355115#54355115)
var msg = "LDBRoot.ToString()";
throw new LDBToStringImplictConversionException(msg);
}
public abstract string toDebugStr();
}
public abstract class Ephemeral : LDBRoot {
// for objects that are not part of the AST
}
public interface IWithLineNUm { // todo - use or remove - todo
// public
}
public interface IHasTableAlias {
// Has a table alias AST obj. May be a ...Missing subtype in which
// it has no alias given by the user, but alias AST is there. More
// accurately a TableSource alias but ok
public abstract TableAlias ta { get; set; }
}
// Base class for all LDB AST objects
public abstract class LDBASTobj : LDBRoot {
// This one is a problem. If I declare the 'parent' property
// abstract I then have to implement it literally hundreds of
// times in the derived classes, and I really don't think it
// would help so just set it to a default which gets overwritten later.
//
// The problem is that those objects were created by the
// parser, which creates them bottom-up, which means you can't
// know the parent when they are created. It should be
// possible to work around this but it does mean reworking the
// parser fundamentally, and I'm not going to do that
// assumeing it's even possible.
// todo - read up on priv/pub/prog accessors I really don't
// get these at all
public LDBASTobj parent { get; private set; }
= noParent;
// Unique ID per object, useful for distinguishing them
// without general equality.
readonly public ulong uid = newUID();
// public abstract int lineNum { get; set; }
// Is validated is a cheapish check to ensure everything gets
// validated
// can do this as auto prop? todo
private bool _validated = false;
protected bool validated {
get => _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<T>() 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<T>() where T : LDBASTobj {
var loc = className + ".reqClimbTo<T>()";
var res = optClimbTo<T>();
hardMust2(!(res is null),
// Thanks SO <https://stackoverflow.com/questions/2581642/how-do-i-get-the-type-name-of-a-generic-type-argument>
// 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<P>() {
hardMust(!(this is NoParent), "Item is NoParent");
return this.parent is P;
}
public bool hasAncestor<A>() {
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<A>();
}
}
public bool notHasAncestor<A>() =>
!hasAncestor<A>();
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<T>
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!
// <https://stackoverflow.com/questions/15345517/is-it-possible-to-disable-implicit-tostring-call>
// Will try <https://stackoverflow.com/questions/54354342/how-to-get-an-error-shown-when-using-non-strings-as-strings-instead-of-an-autom/54355115#54355115)
var msg = "LDBRoot.ToString()";
throw new LDBToStringImplictConversionException(msg);
}
public abstract string toDebugStr();
}
public abstract class Ephemeral : LDBRoot {
// for objects that are not part of the AST
}
public interface IWithLineNUm { // todo - use or remove - todo
// public
}
public interface IHasTableAlias {
// Has a table alias AST obj. May be a ...Missing subtype in which
// it has no alias given by the user, but alias AST is there. More
// accurately a TableSource alias but ok
public abstract TableAlias ta { get; set; }
}
// Base class for all LDB AST objects
public abstract class LDBASTobj : LDBRoot {
// This one is a problem. If I declare the 'parent' property
// abstract I then have to implement it literally hundreds of
// times in the derived classes, and I really don't think it
// would help so just set it to a default which gets overwritten later.
//
// The problem is that those objects were created by the
// parser, which creates them bottom-up, which means you can't
// know the parent when they are created. It should be
// possible to work around this but it does mean reworking the
// parser fundamentally, and I'm not going to do that
// assumeing it's even possible.
// todo - read up on priv/pub/prog accessors I really don't
// get these at all
public LDBASTobj parent { get; private set; }
= noParent;
// Unique ID per object, useful for distinguishing them
// without general equality.
readonly public ulong uid = newUID();
// public abstract int lineNum { get; set; }
// Is validated is a cheapish check to ensure everything gets
// validated
// can do this as auto prop? todo
private bool _validated = false;
protected bool validated {
get => _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<T>() 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<T>() where T : LDBASTobj {
var loc = className + ".reqClimbTo<T>()";
var res = optClimbTo<T>();
hardMust2(!(res is null),
// Thanks SO <https://stackoverflow.com/questions/2581642/how-do-i-get-the-type-name-of-a-generic-type-argument>
// 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<P>() {
hardMust(!(this is NoParent), "Item is NoParent");
return this.parent is P;
}
public bool hasAncestor<A>() {
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<A>();
}
}
public bool notHasAncestor<A>() =>
!hasAncestor<A>();
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<T>
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!
// <https://stackoverflow.com/questions/15345517/is-it-possible-to-disable-implicit-tostring-call>
// Will try <https://stackoverflow.com/questions/54354342/how-to-get-an-error-shown-when-using-non-strings-as-strings-instead-of-an-autom/54355115#54355115)
var msg = "LDBRoot.ToString()";
throw new LDBToStringImplictConversionException(msg);
}
public abstract string toDebugStr();
}
public abstract class Ephemeral : LDBRoot {
// for objects that are not part of the AST
}
public interface IWithLineNUm { // todo - use or remove - todo
// public
}
public interface IHasTableAlias {
// Has a table alias AST obj. May be a ...Missing subtype in which
// it has no alias given by the user, but alias AST is there. More
// accurately a TableSource alias but ok
public abstract TableAlias ta { get; set; }
}
// Base class for all LDB AST objects
public abstract class LDBASTobj : LDBRoot {
// This one is a problem. If I declare the 'parent' property
// abstract I then have to implement it literally hundreds of
// times in the derived classes, and I really don't think it
// would help so just set it to a default which gets overwritten later.
//
// The problem is that those objects were created by the
// parser, which creates them bottom-up, which means you can't
// know the parent when they are created. It should be
// possible to work around this but it does mean reworking the
// parser fundamentally, and I'm not going to do that
// assumeing it's even possible.
// todo - read up on priv/pub/prog accessors I really don't
// get these at all
public LDBASTobj parent { get; private set; }
= noParent;
// Unique ID per object, useful for distinguishing them
// without general equality.
readonly public ulong uid = newUID();
// public abstract int lineNum { get; set; }
// Is validated is a cheapish check to ensure everything gets
// validated
// can do this as auto prop? todo
private bool _validated = false;
protected bool validated {
get => _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<T>() 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<T>() where T : LDBASTobj {
var loc = className + ".reqClimbTo<T>()";
var res = optClimbTo<T>();
hardMust2(!(res is null),
// Thanks SO <https://stackoverflow.com/questions/2581642/how-do-i-get-the-type-name-of-a-generic-type-argument>
// 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<P>() {
hardMust(!(this is NoParent), "Item is NoParent");
return this.parent is P;
}
public bool hasAncestor<A>() {
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<A>();
}
}
public bool notHasAncestor<A>() =>
!hasAncestor<A>();
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<T>
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!
// <https://stackoverflow.com/questions/15345517/is-it-possible-to-disable-implicit-tostring-call>
// Will try <https://stackoverflow.com/questions/54354342/how-to-get-an-error-shown-when-using-non-strings-as-strings-instead-of-an-autom/54355115#54355115)
var msg = "LDBRoot.ToString()";
throw new LDBToStringImplictConversionException(msg);
}
public abstract string toDebugStr();
}
public abstract class Ephemeral : LDBRoot {
// for objects that are not part of the AST
}
public interface IWithLineNUm { // todo - use or remove - todo
// public
}
public interface IHasTableAlias {
// Has a table alias AST obj. May be a ...Missing subtype in which
// it has no alias given by the user, but alias AST is there. More
// accurately a TableSource alias but ok
public abstract TableAlias ta { get; set; }
}
// Base class for all LDB AST objects
public abstract class LDBASTobj : LDBRoot {
// This one is a problem. If I declare the 'parent' property
// abstract I then have to implement it literally hundreds of
// times in the derived classes, and I really don't think it
// would help so just set it to a default which gets overwritten later.
//
// The problem is that those objects were created by the
// parser, which creates them bottom-up, which means you can't
// know the parent when they are created. It should be
// possible to work around this but it does mean reworking the
// parser fundamentally, and I'm not going to do that
// assumeing it's even possible.
// todo - read up on priv/pub/prog accessors I really don't
// get these at all
public LDBASTobj parent { get; private set; }
= noParent;
// Unique ID per object, useful for distinguishing them
// without general equality.
readonly public ulong uid = newUID();
// public abstract int lineNum { get; set; }
// Is validated is a cheapish check to ensure everything gets
// validated
// can do this as auto prop? todo
private bool _validated = false;
protected bool validated {
get => _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<T>() 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<T>() where T : LDBASTobj {
var loc = className + ".reqClimbTo<T>()";
var res = optClimbTo<T>();
hardMust2(!(res is null),
// Thanks SO <https://stackoverflow.com/questions/2581642/how-do-i-get-the-type-name-of-a-generic-type-argument>
// 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<P>() {
hardMust(!(this is NoParent), "Item is NoParent");
return this.parent is P;
}
public bool hasAncestor<A>() {
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<A>();
}
}
public bool notHasAncestor<A>() =>
!hasAncestor<A>();
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<T>
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!
// <https://stackoverflow.com/questions/15345517/is-it-possible-to-disable-implicit-tostring-call>
// Will try <https://stackoverflow.com/questions/54354342/how-to-get-an-error-shown-when-using-non-strings-as-strings-instead-of-an-autom/54355115#54355115)
var msg = "LDBRoot.ToString()";
throw new LDBToStringImplictConversionException(msg);
}
public abstract string toDebugStr();
}
public abstract class Ephemeral : LDBRoot {
// for objects that are not part of the AST
}
public interface IWithLineNUm { // todo - use or remove - todo
// public
}
public interface IHasTableAlias {
// Has a table alias AST obj. May be a ...Missing subtype in which
// it has no alias given by the user, but alias AST is there. More
// accurately a TableSource alias but ok
public abstract TableAlias ta { get; set; }
}
// Base class for all LDB AST objects
public abstract class LDBASTobj : LDBRoot {
// This one is a problem. If I declare the 'parent' property
// abstract I then have to implement it literally hundreds of
// times in the derived classes, and I really don't think it
// would help so just set it to a default which gets overwritten later.
//
// The problem is that those objects were created by the
// parser, which creates them bottom-up, which means you can't
// know the parent when they are created. It should be
// possible to work around this but it does mean reworking the
// parser fundamentally, and I'm not going to do that
// assumeing it's even possible.
// todo - read up on priv/pub/prog accessors I really don't
// get these at all
public LDBASTobj parent { get; private set; }
= noParent;
// Unique ID per object, useful for distinguishing them
// without general equality.
readonly public ulong uid = newUID();
// public abstract int lineNum { get; set; }
// Is validated is a cheapish check to ensure everything gets
// validated
// can do this as auto prop? todo
private bool _validated = false;
protected bool validated {
get => _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<T>() 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<T>() where T : LDBASTobj {
var loc = className + ".reqClimbTo<T>()";
var res = optClimbTo<T>();
hardMust2(!(res is null),
// Thanks SO <https://stackoverflow.com/questions/2581642/how-do-i-get-the-type-name-of-a-generic-type-argument>
// 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<P>() {
hardMust(!(this is NoParent), "Item is NoParent");
return this.parent is P;
}
public bool hasAncestor<A>() {
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<A>();
}
}
public bool notHasAncestor<A>() =>
!hasAncestor<A>();
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
}
^ permalink raw reply [flat|nested] 11+ messages in thread