import ParserState from "./ParserState";
import IRuleState from "./IRuleState";
import { PRuleResult, PElementResult } from "../parser/PObjectMap";
import GRule, { GElement } from "../parser/grammar/GRule";
import SeepParserDefs from "../parser/SeepParserDefs";
import Assert from "@cafetextual/util/dist/src/assert/Assert";
/**
* Persists a stack element while parsing.
*
* A note on line rule results:
* a) A line rule result is seperately persistsed as a line rule result
* b) However, the 0th element is appended to the parent rule.
*
* This reflects the way child rules are mapped to objects - a child rule is seen as an element appended to to the rule's elements.
*
*/
export default class RuleState extends ParserState implements IRuleState
{
isElementState():boolean {
return false
}
ruleInit(currentLineIndex:number):void {
var r:PRuleResult = this.ruleResult
// init element
this.elementIndex = 0; // default, to remove
this.element = this.rule.elements[0];
this.elementCollectionIndex = this.element.allowMany ? 0 : -1;
this.line1 = currentLineIndex;
this.compositeResult = null;
} // rule_init
clone(fromCurrentElementState:any/*ElementState*/ = null, fromChildRuleState:any/*RuleState*/ = null, fromParentRuleState:any/*RuleState*/ = null,
fromParentElementState:any/*ElementState*/=null, stack:Array = null):ParserState {
if (this.rule) {
//console.log("cloning " + rule.tr());
}
if (stack.indexOf(this) >= 0) {
console.log("bug");
}
stack.push(this);
var r:RuleState = new RuleState; // create( p, rule, asChild, indent, asLineRule );
// ----- from superclass -----
r.rulePos0 = this.rulePos0;
r.line0 = this.line0;
r.pos0 = this.pos0;
r.p = this.p;
r.breakpoint = this.breakpoint;
r.continueFn = this.continueFn;
// ----- from superclass ----
if (fromParentElementState) {
r.parentElementState = fromParentElementState;
} else if (this.parentElementState) {
var clonedElementState:any = this.parentElementState.clone(null,r, null, null, stack) //as ElementState;
r.parentElementState = clonedElementState
}
r.rule = this.rule; // redundant
r.indent = this.indent;
r.asChild = this.asChild;
r.line1 = this.line1;
r.lineRuleLine0 = this.lineRuleLine0;
r.lineRuleLineX = this.lineRuleLineX;
r.lastElementMatchedWS = this.lastElementMatchedWS;
r.elementIndex = this.elementIndex;
r.elementCollectionIndex = this.elementCollectionIndex;
r.parentLineCollectionIndex = this.parentLineCollectionIndex;
r.element = this.element
r._lastSecondaryResult = this._lastSecondaryResult;
r.elementMatchIsZeroLength = this.elementMatchIsZeroLength;
r.incrementPending == this.incrementPending
r.consumeUntilEOF = this.consumeUntilEOF;
r.lineRuleIndex = this.lineRuleIndex;
r.currentLineIndent = this.currentLineIndent;
r.currentLineRule = this.currentLineRule;
r.lineAllowMany = this.lineAllowMany;
r.lineAllowNone = this.lineAllowNone;
r.lineRuleResult = this.lineRuleResult;
r.compositeLineRuleResult = this.compositeLineRuleResult;
r.foundOneLineRule = this.foundOneLineRule;
r.lineMatchFailed = this.lineMatchFailed;
r.lineCompositeResults = this.lineCompositeResults;
r.parentToAddResultTo = this.parentToAddResultTo;
if (fromCurrentElementState) {
r.currentElementState = fromCurrentElementState;
} else if (this.currentElementState) {
clonedElementState = this.currentElementState.clone(null, null, r, null, stack) // as ElementState;
r.currentElementState = clonedElementState
}
r.currentLineRuleState = this.currentLineRuleState;
r.elementResult = this.elementResult;
r.compositeResult = this.compositeResult;
r.posX = this.posX;
r.lineX = this.lineX;
if (this.parentRuleState) {
r.parentRuleState = this.parentRuleState.clone(null, null,null, null ,stack) as RuleState;
}
r.asLineRule = this.asLineRule;
r.ruleResult = this.ruleResult;
return r;
}
parentElementState:any // ElementState; // represntable as an id
// top down level variables
rule:GRule; // representableas via unique styleID
indent:string; // currently derivable from grammar
asChild:boolean; // TODO - derivable
/**
* line1 is the startLineIndex after secondary grammar's preResults (is secondary grammar) - will differ from line0 only
* for a parent line preceeded by ws/ comments / whatever the secondary grammar allows.
*
*
* ie
* line0, pos0 - is the absolute first line when rule begics
* line1 - is the start after possible secondary grammar extracted for a top rule only
* (remove this once top rule concept deprecated, line1 reverts to line0)
* lineX, posX - confirmed consumed position. updated once an element has matched, or line has matched
*
*
*/
line1:number = -1;
lineRuleLine0:number = -1;
lineRuleLineX:number = -1;
// TODO - make derivable - but this is a signnificant contextual variable relating to state
lastElementMatchedWS:boolean = false;
// element iteration state
elementIndex:number = -1; // current element to iterate on - used in switching during recursio
elementCollectionIndex:number = -1;
/**
* Refers to the index in the result
*/
parentLineCollectionIndex:number = -1;
// cached
element:GElement; // derivable from rule & etc
/**
* Append a simple element
*/
appendPreResult(elementResult:PElementResult):void {
var r:Array;
if (this.ruleResult.preResults == null) {
this.ruleResult.preResults = [];
}
r = this.ruleResult.preResults;
// and the moment, there's only a single
var rr:PRuleResult = new PRuleResult;
rr.parentLineResult = this.ruleResult; //
rr.startLineIndex = elementResult.startLineIndex;
r.push(rr);
rr.preResultIndex = (r.length -1)
rr.addResult_low(elementResult);
} // appendPreResult
_lastSecondaryResult:PRuleResult;
getLastSecondaryResult():PRuleResult {
return this._lastSecondaryResult;
}
appendLineSecondaryResult(elementResult:PElementResult):void {
var targetRuleResult:PRuleResult;
if (this.lineRuleResult == null) {
Assert.assert(this.lineRuleIndex < 0); // only invoked
// 1. not called from a line rule, simply append to ruleResult
targetRuleResult = this.ruleResult;
} else if (this.lineCompositeResults) {
// 2. we have a secondary line result
var lastElement:PElementResult = this.lineCompositeResults.children[this.lineCompositeResults.children.length -1];
targetRuleResult = lastElement.parentRuleResult; // almost certainly wrong
} else {
// 3. simple line result
Assert.assert(this.lineRuleResult != null);
targetRuleResult = this.lineRuleResult;
}
Assert.assert(targetRuleResult != null);
if (targetRuleResult.secondaryResults == null) {
targetRuleResult.secondaryResults = [];
}
var secondaryRule:PRuleResult = this.secondaryElementResultToRuleResult(elementResult
, targetRuleResult.secondaryResults.length // secondaryResultIndex
);
targetRuleResult.secondaryResults.push(secondaryRule);
this._lastSecondaryResult = secondaryRule;
} // appendLineSecondaryResult2
/**
* Wrap an element result from a secondary grammar in a rule result with appropriate indexing
*
* @param elementResult raw element result
* @param secondaryResultIndex index of number of lines of secondary grammar matched (ie between primary grammar rules)
*/
private secondaryElementResultToRuleResult(elementResult:PElementResult, secondaryResultIndex:number):PRuleResult {
var rr:PRuleResult = new PRuleResult;
rr.parentLineResult = this.ruleResult;
rr.startLineIndex = elementResult.startLineIndex;
rr.secondaryResultIndex = secondaryResultIndex;
rr.addResult_low(elementResult);
return rr;
}
private unappendCompositeResult(ruleResult:PRuleResult, elementResult:PElementResult):void {
var e:PElementResult = this.compositeResult.children.pop();
Assert.assert(e == elementResult);
if (this.compositeResult.children.length == 0) {
ruleResult.removeResult_low(this.compositeResult);
this.compositeResult = null; // TO_OPTIMIZE -- will cause (in some instances)
}
this.elementCollectionIndex--;
} // appendCompositeResult
/**
* Append an element result for an element /w multiplicity ie ":my-rule*"
*/
private appendCompositeResult(ruleResult:PRuleResult, elementResult:PElementResult):void {
Assert.assert(ruleResult == this.ruleResult) // TODO - remove from api
Assert.assert(this.elementCollectionIndex >= 0);
if (!this.compositeResult) {
this.compositeResult = RuleState.createCompositeResult(this.element, this);
this.compositeResult.parentRuleResult = this.ruleResult;
this.compositeResult.parentElementIndex = this.elementIndex;
ruleResult.addResult_low(this.compositeResult);
}
elementResult.parentRuleResult = this.ruleResult;
elementResult.parentElementIndex = this.elementIndex
elementResult.parentCollectionIndex = this.elementCollectionIndex;
this.compositeResult.children.push(elementResult);
this.elementCollectionIndex++;
}
isFirst():boolean {
return this.elementCollectionIndex == 1;
}
unappendCompositeLineResult(lineRuleResult:PRuleResult):void {
Assert.assert(lineRuleResult == this.lineRuleResult);
var e:PElementResult = lineRuleResult.results[0];
if (!this.lineCompositeResults || this.lineCompositeResults.children == null || this.lineCompositeResults.children.length == 0) {
Assert.fail();
}
var last:PElementResult = this.lineCompositeResults.children.pop();
Assert.assert(last == e);
if (this.lineCompositeResults.children.length == 0) {
this.upappendLineChild(this.compositeLineRuleResult);
}
// OPTIMISE_THIS - this is a potential inefficieny.
// for line rule that doesnt match, we will have createed, appended and then unappended and destroyed
if (this.lineCompositeResults) {
this.lineCompositeResults.release()
}
this.lineCompositeResults = null;
}
/**
* Append a line result /w multiplicity ie -->* X :: [etc]
*
* Note
* - we're assuming that the the form of the line rule result to have a single element, possibly with a child rule,
* ie "-->* TOP_ELEMENT_TOKEN(vars) :: child rule here".
*
* - A single PRuleResult will be added to the parent line rule's lineChildren.
*
* - The individual line results will be in that PRuleResult's results.
*
*/
appendCompositeLineResult(lineRuleResult:PRuleResult):void {
Assert.assert(lineRuleResult == this.lineRuleResult);
var e:PElementResult = lineRuleResult.results[0];
if (!this.lineCompositeResults) {
var gElement:GElement = lineRuleResult.child(0).element; // NOTE - here we hardcode the assumption (enforced by the grammar) that a line rule has a
// single elemment ie -->* PARENT_TOKEN :: :child-rule
// this is the composite PElementResult
this.lineCompositeResults = RuleState.createCompositeResult(gElement, this.currentLineRuleState);
this.lineCompositeResults.parentRuleResult = null;
this.lineCompositeResults.parentLineRuleResult = lineRuleResult; // Q: what is this??
this.lineCompositeResults.parentElement = this.element;
this.lineCompositeResults.parentElementIndex = this.elementIndex;
this.lineCompositeResults.parentCollectionIndex = -1; //this.elementCollectionIndex;
// duplicates funtionlity in appendLineChild(lineRuleResult);
if (this.ruleResult.lineChildren ==null) {
this.ruleResult.lineChildren = [];
}
Assert.assert(this.compositeLineRuleResult == null);
this.compositeLineRuleResult = new PRuleResult;
this.compositeLineRuleResult.matchedRule = this.currentLineRule;
this.compositeLineRuleResult.addResult_low(this.lineCompositeResults);
this.appendLineChild(this.compositeLineRuleResult);
} // create composite line rule container
// ---- note: here we have no less that three types of parent line rules. (a sure sign of overcomlexity) ----
// i) the parent line rule. ie
// rule: X
// --> (childLineOne) :: >child> // effective prent is "rule:" as this "line" is appended as an element to line
e.effectiveParentResult = this.parentToAddResultTo;
// ii) the actual lineresult that the parser uses - kind of a dummy wrapper. But the literal parentRule rsult in the eyes of the parser
// Q: can we not just throw this away?
e.parentRuleResult = lineRuleResult; // line rules are (confusingly) specified as element, not rules. This is something of a dummy lineRule
// iii)
e.parentLineRuleResult = this.ruleResult; // distinct form the parentLineRuleResult
// e.lineMatchIndex will be equal to the e.parentLineRuleResult - however this may not have been calculated at this stage
e.parentElement = this.element;
e.parentElementIndex = -1;
e.parentCollectionIndex = -1;
e.parentLineCollectionIndex = this.lineCompositeResults.children.length; //this.lineCollectionElementIndex; // index of repeated line rule (ie 0 if first match, 1 if 2nd match)
this.parentLineCollectionIndex = e.parentLineCollectionIndex; // state need to preserve this.
this.lineCompositeResults.children.push(e);
// if (! (e.parentLineCollectionIndex == lineCompositeResults.children.length -1) ) {
// Assert.assert(e.parentLineCollectionIndex == lineCompositeResults.children.length -1);
// }
// notes: retreiving element from the parent rule
// 1. how to retrieve the result context of this element
//Assert.assert(e.parentLineRuleResult.lineChildren[e.lineMatchIndex()] == compositeLineRuleResult);
Assert.assert(this.compositeLineRuleResult.results[0] == this.lineCompositeResults);
Assert.assert(this.lineCompositeResults.children[e.parentLineCollectionIndex] == e);
//Assert.assert(e.parentLineRuleResult.lineChild(e.lineMatchIndex()).results[0].children[e.parentLineCollectionIndex] == e);
} // appendCompositeLineResult
unappendElementResult(elementResult:PElementResult):void {
if (this.element.allowMany) {
this.unappendCompositeResult(this.ruleResult, elementResult);
} else {
this.unappendResult(elementResult);
}
} // upppendElementResult
appendElementResult(elementResult:PElementResult):void {
if (elementResult.element == null) {
Assert.fail(); // debug only
}
if (this.element.allowMany) {
this.appendCompositeResult(this.ruleResult , elementResult );
} else { // TODO - if OR is false, may need to add an error if the last element fails
// record the result if it's valid, or even if it's not valid, but non optional
var allowNone:boolean = this.element.allowNone || this.rule.isOR
Assert.assert( elementResult.valid || !allowNone );
this.addResult(elementResult);
}
elementResult = null;
}
elementMatchIsZeroLength:boolean = false; // resent after each element
incrementPending:boolean = false; // Q: when's the latest that can be calculated (cached for efficiency, but shouldn't need to be)
// TODO - remove this mechanism.
consumeUntilEOF:boolean = false; // whether to add errors if the entire source docuemnt is consumed
// line rule iteration
/**
* index of the line rule in the parent rule (this is the index of the rule in the grammar, not the results)
*
*/
lineRuleIndex:number = -1;
//[sID] lineCollectionElementIndex:number = -1; no longer used
lineIncrementNotPending():boolean {
return !this.lineMatchFailed && (!this.foundOneLineRule || this.lineAllowMany);
}
currentLineIndent:string = "";
// cached, but derivable
currentLineRule:GRule; // duplicates lineRuleIndex
lineAllowMany:boolean = false; // TODO - derivable
lineAllowNone:boolean = false; // TODO - derivable
// results
lineRuleResult:PRuleResult = null; // this is always the single rule result returned by the child result
compositeLineRuleResult:PRuleResult = null; // this is the parent line rule
foundOneLineRule:boolean = false; // TODO - derivable from previous results
// transient
lineMatchFailed:boolean = false;
/**
*
*/
lineCompositeResults:PElementResult; // 0th element of lineRuleResult
// mechanism by which child line element result are appended to the parent rule's element results
parentToAddResultTo:PRuleResult;
currentElementState:any // ElementState; // child elements
currentLineRuleState:RuleState;
// transient result - when iterating over the same element
elementResult:PElementResult;
compositeResult:PElementResult; // a composite collection of element results , ie when we have (var)*
// last confirmed matched position of a rule
posX:number = -1; // must derive
lineX:number = -1;
// used when iterating on a line rule
/**
* when iterating on a line rule (as opposed to a a child element rule), parentRuleState is the parent
*/
parentRuleState:RuleState;
asLineRule:boolean; // TODO - derive from parent
setButDontAppendRuleError(errCode:number, state:ParserState, element:GElement, valid:boolean = true, err:string = null, childResult:PRuleResult = null):void {
Assert.assert(state == this);
this.populateElement(this.elementResult, errCode, state, element, valid, err, childResult )
}
/**
* Append an error on a rule. Only call this if the rule errors out encountering an actual element
*/
appendError(errCode:number, state:RuleState, element:GElement, valid:boolean = true, err:string = null, childResult:PRuleResult = null):void {
Assert.assert(state == this);
this.elementResult = new PElementResult
this.populateElement(this.elementResult, errCode, state, element, valid, err, childResult )
this.addResult(this.elementResult);
if (state.asLineRule) {
//console.log(' --- line rule ---');
if (!state.parentRuleState.currentLineIsOptional()) {
state.parentRuleState.appendLine(this.ruleResult);
} else {
//console.log(' throwing away line error');
}
}
//var failElement:PElementResult = PElementResult.create();
//___deprecated___createElementResult(GErr.RULE_FAILED, state, state.currentLineRule.child(0), false,
// 'failed to match rule' + state.currentLineRule.ruleString, state.lineRuleResult );
} // appendElementResult
// result
ruleResult:PRuleResult;
appendLine(lineRuleResult:PRuleResult):void {
if (lineRuleResult != this.lineRuleResult) {
Assert.assert(lineRuleResult == this.lineRuleResult);
}
if (!this.lineAllowMany) {
this.appendLineChild(lineRuleResult);
} else {
this.appendCompositeLineResult(lineRuleResult);
}
}
unappendLine(lineRuleResult:PRuleResult):void {
this.unappendLine_low(lineRuleResult, this.lineAllowMany);
}
unappendLine_low(lineRuleResult:PRuleResult, many:boolean):void {
if (!many) {
this.upappendLineChild(lineRuleResult);
} else {
this.parentLineCollectionIndex--;
this.unappendCompositeLineResult(lineRuleResult);
}
} // unappendLine
upappendLineChild(lineRuleResult:PRuleResult):void {
var e:PElementResult = lineRuleResult.results[0];
this.parentToAddResultTo.removeResult_low(e);
var last:PRuleResult = this.ruleResult.lineChildren.pop();
Assert.assert(lineRuleResult == last);
}
// append the ruleRusult as a line rule result, and then append the element to the parent rule
appendLineChild(lineRuleResult:PRuleResult):void {
var e:PElementResult = lineRuleResult.results[0];
this.parentToAddResultTo.addResult_low(e);
if (this.ruleResult.lineChildren ==null) {
this.ruleResult.lineChildren = [];
}
// lineRuleResult.parentElementResult will be null
lineRuleResult.parentLineResult = this.ruleResult;
// recall that in this case the -->* (var) :: .. the '*' referes to the line, not the
e.parentRuleResult = null;
e.parentLineRuleResult = lineRuleResult;
e.parentElementIndex = 0;
e.parentCollectionIndex = -1;
lineRuleResult.lineMatchIndex = this.ruleResult.lineChildren.length;
this.ruleResult.lineChildren.push(lineRuleResult);
} // appendLineChild
private unappendResult(e:PElementResult):void {
this.ruleResult.removeResult_low(e);
}
private addResult(e:PElementResult):void {
// need to keep track of whether any child elements have matched
// slightly awkward, the complexity here arises in part from the way elements are also used to represent errors (motivated by the need to do error hilighting on errors)
e.parentRuleResult = this.ruleResult;
e.parentElement = this.element;
e.parentElementIndex = this.elementIndex;
e.parentCollectionIndex = this.elementCollectionIndex;
if (!this.ruleResult) {
console.log(' bug');
}
this.ruleResult.addResult_low(e);
} // addResult
/**
* true if this rule has previously hit a cutpoint.
*
* Should be called only when an element has failed
*/
cutpointHit():boolean {
// TODO - cache this value, as this very close to innermost loop
return this.rule.hasCutpoint && this.getCutpointElement() != null;
} // cutpointHit
private getCutpointElement():PElementResult {
var results:Array = this.ruleResult.results;
for (var i:number = results.length -1; i>=0; i--) {
var er:PElementResult = results[i]
if (er.element && er.element.isCutpoint && er != this.elementResult) {
return er;
}
}
return null;
}
markAsCutpointInvalid():void {
if (this.elementResult.isValid()) {
Assert.assert(this.elementResult.isValid() == false);
}
var cutElement:PElementResult = this.getCutpointElement();
Assert.assert(cutElement && cutElement != this.elementResult);
this.elementResult.cutpointFailElement = cutElement;
this.elementResult.valid = false;
} // markAsCutpointInvalid
incrementElement():void {
this.compositeResult = null;
this.elementIndex++;
this.elementMatchIsZeroLength = false;
this.incrementPending = false;
this.elementResult = null;
if (this.elementIndex < this.rule.elements.length) {
this.element = this.rule.elements[this.elementIndex];
if (this.element.allowMany) {
this.elementCollectionIndex = 0;
}
} else {
this.element = null;
}
}
foundAtLeastOneValidElement():boolean {
// FIX_THIS - may break if 0th child is a zero length element
// note: we need to check that the item is valid b/c this is called when a subsequent, potentially failed
// current element is still appended to the results.
return this.compositeResult && this.compositeResult.children && (this.compositeResult.children.length > 0) && this.compositeResult.children[0].isValid();
} // foundOneElements
incrementLineRule():void {
this.lineRuleIndex++;
this.currentLineRule = this.lineRuleIndex < (this.rule.lineRules.length) ? this.rule.lineRules[this.lineRuleIndex] : null
//lineCollectionElementIndex = -1;
if (this.currentLineRule) {
this.lineAllowMany = this.currentLineRule.lineAllowMany;
this.lineAllowNone = this.currentLineRule.lineAllowNone;
this.currentLineIndent = this.indent + (this.currentLineRule.lineNoIndent ? "" : SeepParserDefs.INDENT);
}
this._lastSecondaryResult = null;
this.foundOneLineRule = false;
this.lineCompositeResults = null; // very important to reset copo
this.lineMatchFailed = false;
this.lineCompositeResults = null;
this.lineRuleResult = null;
this.compositeLineRuleResult = null;
this.parentLineCollectionIndex = -1;
} // incrementLineRule
static createCompositeResult(element:GElement, state:RuleState):PElementResult {
var out:PElementResult = new PElementResult(element);
out.startLineIndex = state.line0;
//out.startIndex = state.pos0; // no need to persist this, we're only going to care about this at the leaf level
out.children = new Array()
return out;
}
// --------- static helper methods ---------
hasResult():boolean {
return (this.ruleResult.isValid());
} // hasResult
static createFromParentElement(p:any/*ElementState*/):RuleState {
var o:RuleState = new RuleState();
o.ruleResult = new PRuleResult;
o.ruleResult.parentElementResult = p.elementResult;
// core state in graph
o.parentElementState = p;
o.rule = p.element.childRule;
// propagated from higher up the tree
o.asChild = true; // is necessarily a child
o.indent = p.parentRuleState.indent;
// reference to parser
o.p = p.p; // (reference to the parser, probably deprecate)
o.line0 = o.p.getCurrentLineIndex();
o.pos0 = o.p.getPos();
o.rulePos0 = o.pos0;
return o;
} // createFromElementState
static create(p:any /*SeepParser*/, rule:GRule, asChild:boolean, indent:string, asLineRule:boolean ):RuleState {
var o:RuleState = new RuleState; // seriously going to need to cache and resuse objects
o.rule = rule;
o.indent = indent;
o.asChild = asChild;
o.asLineRule = asLineRule;
// copy from parser
o.p = p;
o.pos0 = o.p.getPos();
o.line0 = o.p.getCurrentLineIndex();
o.rulePos0 = o.pos0;
return o;
} // create
static createForLineRuleFromParent(parentState:RuleState, lineRule:GRule):RuleState {
var o:RuleState = new RuleState
Assert.assert(parentState.currentLineRule == lineRule);
Assert.assert(parentState.rule.lineChild(parentState.lineRuleIndex) == lineRule)
parentState.currentLineRuleState = o;
o.rule = lineRule;
o.parentRuleState = parentState;
o.p = parentState.p;
o.asChild = false;
o.asLineRule = true;
o.lineRuleIndex = -1;
o.indent = parentState.currentLineIndent;
o.ruleResult = PRuleResult.create();
parentState.parentToAddResultTo = o.getParentToAppendLineResultTo(o); // artifact of refactoring, much cleaner ways to do this
// this should perhaps be done more transparently
o.line0 = o.p.getCurrentLineIndex();
o.pos0 = o.p.getPos();
parentState.parentToAddResultTo = o.getParentToAppendLineResultTo(o);
return o;
}
// --- util ---
/**
* Call this on the state of a child line rule, to exactly which PElementResult to add the PRuleResult to.
*
*
*
* This is a bit awkward:
* If the parent is the line elenet ie from "rule: e1 e2 ..." then child result is simply appended to the results
* However, if the parent is another line rule, then we want to append it the the results of the implicit child
* this allows nesting of variables in child line rules of names child rules
* ie rule0 X: e1 e2 ... e(n)
* --> (var1) :: ee1 ... (var2) // (var1)[...] is appended, effectively as e(n+1)
* --> (var3) :: eee1 // (var3) is a child of (var1)[...]
*
* - by leaving the (var1) rule unnamed, you could have var3 appended to the child, and therefore the parent rule0
* - - there's no way (var1) to nest in a variable of rule0:
*
*
*
*/
private getParentToAppendLineResultTo(state:RuleState):PRuleResult {
var out:PRuleResult =
(state.parentRuleState.asLineRule)
? (state.parentRuleState.ruleResult.results[0] as PElementResult).childResult // parent is a line rule, so append to the child rule rules of the 0th element
: state.parentRuleState.ruleResult; // parent is a normal line rule, so just add to the final element
return out;
} // getParentToAppendLineResultTo
eq(s:RuleState):boolean {
var ok:boolean = true;
ok = ok && (this.rule == s.rule);
ok = ok && (this.elementIndex == s.elementIndex)
ok = ok && (this.elementCollectionIndex == s.elementCollectionIndex);
ok = ok && (this.asChild == s.asChild);
ok = ok && (this.asLineRule == s.asLineRule);
ok = ok && (this.indent == s.indent);
if (this.parentElementState) {
ok = ok && this.parentElementState.eq(s.parentElementState);
} else {
ok = ok && (s.parentElementState == null);
}
ok = ok && (this.lastElementMatchedWS == s.lastElementMatchedWS);
// state of child line rules
ok = ok && (this.lineRuleIndex == s.lineRuleIndex); // index of line rule
//ok = ok && (lineCollectionElementIndex == s.lineCollectionElementIndex); // when a line rule's multiplicity is > 0
ok = ok && (this.foundOneLineRule == s.foundOneLineRule);
ok = ok && (this.currentLineIndent == s.currentLineIndent);
ok = ok && (this.currentLineRule == s.currentLineRule); // derivable
ok = ok && (this.lineAllowMany == s.lineAllowMany); // derivable
ok = ok && (this.lineAllowNone == s.lineAllowNone); // derivable
if (this.parentRuleState) {
Assert.assert(this.parentElementState == null);
ok = this.parentRuleState.eq(s.parentRuleState);
} else {
ok = ok && (s.parentRuleState == null);
}
return ok;
} // eq
hasLineRules():boolean {
return this.rule.lineRules && (this.rule.lineRules.length > 0);
}
currentLineIsOptional():boolean {
var lineRuleElement:GElement = this.currentLineRule.elements[0];
return (lineRuleElement.lineAllowNone || (lineRuleElement.lineAllowMany && this.foundOneLineRule) )
}
toTraceString():string {
var out:string = " " + (this.rule.name ? this.rule.name : "");
return out;
}
/**
* Trim is called on upon an incremental invalidation to remove all items after a particular line
*
* @param invalidatedLine The invalidate line, all results on this and subsequent lines are to be trimmed
*/
trim(invalidatedLine:number):void {
var removed:Array = [];
// if this is a simple
var i:number;
// if this
var trimLineRules:boolean = this.ruleResult && this.ruleResult.lineChildren && this.ruleResult.lineChildren.length >0
// && (ruleResult.lineChildren.length >= lineRuleIndex); <-- bug
// current item of this state is a line rule. Should ensure that anything we trim is also from a line rule
var currentLineGRule:GRule = this.lineRuleIndex >= 0 ? this.ruleResult.matchedRule.lineRules[this.lineRuleIndex] : null;
// x. additionally, there may be error messages that need to be trimmed in non-line rules
// cf. the mechanism where errors on
if (this.ruleResult.results) {
// backwards iteration over elements (recall that later elements could be line rules)
for (i = this.ruleResult.results.length -1; i >=0; i--) {
var el:PElementResult = this.ruleResult.results[i];
if (el.parentLineRuleResult == null) { // top level, simply pop element out of results
if (!this.p.isBefore(el.startLineIndex, invalidatedLine)) {
removed.push(this.ruleResult.results.pop());
}
} else if (trimLineRules && (this.ruleResult.lineChildren.length > 0) ) {
// this hinges on the face that the current element will be replicated in the lineRuleResult
// ( wrapped in a PRuleResults )
var numLineRules:number = this.ruleResult.lineChildren.length;
var lastLineRule:PRuleResult = this.ruleResult.lineChildren[numLineRules -1];
//
var en:PElementResult = this.ruleResult.lineMainElement(numLineRules - 1, lastLineRule); // little more that an assertion
if (en.children) {
// TODO - extract
var elementChildren:Array = en.children;
for (var j:number = elementChildren.length-1; j >= 0; j--) { // recall that the cutoff is the line before the line that we're trimming
var childEl:PElementResult = elementChildren[j];
if (this.p.isBefore(childEl.startLineIndex, invalidatedLine) ) {
console.log('not trimming ' + childEl.tr() + ' i= ' + childEl.startLineIndex ) ;
break;
} else {
console.log('trimming ' + childEl.tr() + ' i= ' + childEl.startLineIndex) ;
removed.push( elementChildren.pop() );
}
}
if (j < 0 ) {
this.removeLineChild(removed);
continue;
} else {
break; // we've hit a next line
}
} else {
if (this.p.isBefore(en.startLineIndex, invalidatedLine)) {
break;
}
this.removeLineChild(removed);
continue;
}
/*
if (this.p.isBefore(el.startLineIndex, invalidatedLine)) { //
console.log('not trimming ' + el.tr() + ' i= ' + el.startLineIndex) ;
break;
} else {
this.removeLineChild(removed);
}
*/
}
}
}
// pre line results
if (this.ruleResult && this.ruleResult.preResultsLength() > 0) {
var preResults:Array = this.ruleResult.preResults;
// iterate backwards on pre results removing anything that is equal or greater to the current line
for (i = preResults.length -1; i >=0; i--) {
var pr:PRuleResult = preResults[i];
if (! this.p.isBefore(pr.startLineIndex, invalidatedLine) ){
removed.push(preResults.pop())
} else {
break;
}
}
}
// secondary lines
if (this.ruleResult && this.ruleResult.secondaryResults && this.ruleResult.secondaryResults.length >0) {
var seconaryResults:Array = this.ruleResult.secondaryResults;
for (i = seconaryResults.length -1; i >=0; i--) {
var sr:PRuleResult = seconaryResults[i];
if (! this.p.isBefore(sr.startLineIndex, invalidatedLine) ){
removed.push(seconaryResults.pop());
} else {
break;
}
}
}
if (this.element && this.element.allowMany) {
Assert.fail(' not implemented');
}
// if this is a line rule it will have a parent rule state
if (this.parentRuleState) {
this.parentRuleState.trim(invalidatedLine);
}
if (this.parentElementState && this.parentElementState.parentRuleState) {
this.parentElementState.parentRuleState.trim(invalidatedLine);
}
} // trim
private removeLineChild(removed:Array):void {
var ruleResultRemoved:PRuleResult = this.ruleResult.lineChildren.pop();
var elementRemoved:PElementResult;
if (!this.ruleResult.isLineTopRule()) { // this is a child line
elementRemoved = this.ruleResult.child(0).childResult.results.pop(); // ie --> X :: [e0 ... e_n] poping element e_n
Assert.assert(ruleResultRemoved.child(0) == elementRemoved);
removed.push(ruleResultRemoved);
//unappendLine_low(ruleResult.lineChildren[ruleResult.lineChildren.length-1], true);
} else {
// function popLineResult(ruleResult);
var resultElement:Array = this.ruleResult.results;
elementRemoved = this.ruleResult.results.pop();
removed.push(ruleResultRemoved);
if (ruleResultRemoved.child(0) != elementRemoved) {
Assert.assert(ruleResultRemoved.child(0) == elementRemoved);
}
}
}
parent():ParserState {
return this.parentElementState;
}
showItem():string {
return this.rule ? ( (this.rule.name ? (this.rule.name + ":") : "" ) + this.rule.tr() ) : "";
}
} // class