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)[...]
*

* *

*/ 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