import { int } from '@cafetextual/nlist/dist/src/ntree/types'; import Position from "../serialize/Position"; import PValidationError from "./PValidationError"; import GRule, { GElement } from "./grammar/GRule"; import GErr from './GErr'; import IObjectMap from "./IObjectMap"; import { ISourceList, UIDBase, IContentList, SimpleTextContent } from "@cafetextual/util"; import UID from "@cafetextual/util/dist/src/uid/UID"; import Assert from "@cafetextual/util/dist/src/assert/Assert"; import SourceLocation from "@cafetextual/util/dist/src/source/SourceLocation"; import G from "@cafetextual/util/dist/src/type/G"; import GRuleToken from "./grammar/GRuleToken"; /** * Top level construct that maps objects to results, validation errors etc */ export default class PObjectMap implements IObjectMap { constructor() { this._objectMap = {} this._objectPropertiesMap = {} this._objectCollectionMap = {} this._objectPropertiesList = {} } content:ISourceList private _objectMap:Object = {} private _objectPropertiesMap:Object = {} private _objectCollectionMap:Object = {} private _objectPropertiesList:Object = {} // _objectPropertiesMap[parent] --> pref( _objectPropertiesMap , parent ) // _objectPropertiesMap[parent] = properties --> pset(_objectProperties, parent, propertes) private ensureReferenceable(o:any):void { if (o === null || o === undefined) { //Assert.fail() } if (! (o instanceof UIDBase) ){ if (! (o.__uid > 0) ) { o.__uid = UID.createInt(); } } } pset(o:any, ref:any, v:any):void { if (typeof ref == 'string' || typeof ref == 'boolean') { Assert.fail(); } this.ensureReferenceable(ref) if (!(o instanceof UIDBase)) { o.__uid = UID.createInt(); } if (ref instanceof UIDBase) { o[ref.getUid()] = v; } else { o[ref.__uid] = v } } // pset private pref(o:any, ref:any):any { var out:any this.ensureReferenceable(ref) if (ref instanceof UIDBase) { out = o[ref.getUid()] } else { out = o[ref.__uid] } return out } static itemIsNonReferenceable(item:any):boolean { return (typeof item == 'string') || (typeof item == 'boolean'); // (recalling that we have no mechanism for any other kind of literal) } mapObject(element:IPResult, o:any, parent:any, propertyName:string, isTopOrCollection:boolean = false):void { if ( PObjectMap.itemIsNonReferenceable(o) ) { Assert.assert(propertyName != null); var properties:any = this.pref( this._objectPropertiesMap , parent ) if (!properties) { properties = {}; this.pset( this._objectPropertiesMap, parent, properties ) } properties[propertyName] = element } else { this.pset( this._objectMap, o , element) } if (!isTopOrCollection) { if (!parent) { Assert.fail(); } if (!propertyName) { Assert.fail(); } var propertiesList:Array = this.pref( this._objectPropertiesList , parent ) if (!propertiesList) { propertiesList = []; this.pset( this._objectPropertiesList, parent, propertiesList) } propertiesList.push(propertyName); } } // map object mapCollectionObject(element:PElementResult, collection:Array, index:number, o:any):void { if (!PObjectMap.itemIsNonReferenceable(o)) { this.mapObject(element, o, null, null, true); } var collectionMap:Array = this.pref( this._objectCollectionMap , collection ); if (!collectionMap) { collectionMap = []; this.pset( this._objectCollectionMap, collection, collectionMap) ; } collectionMap[index] = element } // mapCollectionObjectP /** * o - parent object (if childObject is non referenceable, ie a String) * childObject - use this * propertyName - only needed when we have a (non referenceable childObject) * index - when the parent object is an array * */ objectToLoc(o:any, includeToken:boolean = false, propertyName:string = null, index:number = -1, childObject:any = null):SourceLocation { var p:IPResult = this.toResult(o, propertyName, index, childObject) if (p) { return this.toLoc(p, includeToken) } return null } objectToGStack(o:any, includeToken:boolean = false, propertyName:string = null, index:number = -1, childObject:any = null):Array { var p:IPResult = this.toResult(o, propertyName, index, childObject) var el:PElementResult = p instanceof PElementResult ? el as PElementResult : null if (p) { var out:Array = [] } var out:Array = [] return out } toResult(o:any, propertyName:string = null, index:number = -1, childObject:any = null):IPResult { Assert.assert(o != null); if (index >= 0) { Assert.assert(propertyName == null); var collectionMap:Array = this.pref( this._objectCollectionMap, o ) return collectionMap && collectionMap.length > index ? collectionMap[index] : null; } else if (childObject) { if (PObjectMap.itemIsNonReferenceable(childObject) ) { return this.toResult(o, propertyName); } else { return this.toResult(childObject); } } else if (propertyName) { var obx:any = this.pref( this._objectMap, o ) Assert.assert(index == -1); var properties:any = this.pref( this._objectPropertiesMap , o ) var p:PElementResult = properties ? properties[propertyName] : null; return p; } return this.pref( this._objectMap, o ) } toLoc(r:IPResult, includeToken:boolean = false):SourceLocation { return PObjectMap.resultToLocation(r, includeToken) } static resultToLocation(r:IPResult, includeToken:boolean = true):SourceLocation { var el1:PElementResult = r.firstElement(); var el2:PElementResult = r.lastElement(); if (includeToken) { if (r instanceof PRuleResult) { startIndex = (r as PRuleResult).startIndex; } else { // Probably what we need to do is iterate down until we get a non negative start index startIndex = ((r as PElementResult).startIndex >=0) ? (r as PElementResult).startIndex : el1.startIndex; // same as below. Need to do this //startIndex = (r as PElementResult).startIndex } } else { var startIndex:number = el1.startIndex; } if (startIndex < 0) { Assert.assert(startIndex >= 0); } var endIndex:number = el2.endIndex; //element(GElement): :element-indent?, [SKIP_TOKEN(skipToToken)?.SKIP_TOKEN(skip2)?.TYPE_TREE(asTreeType)?.AS_TREE(asTree)?.(tokenName)?.PAREN[REFERENCE_TOKEN(nameIsRef)?.(varName).[COLON.(typeName).]?.]?.[COLON.(childRuleName).]?.(childRule):anonmyous-rule?].MULTIPLICITY(multiplicity)?.WS_POLICY(wsPolicy)?.WS(explicitRequireWS)?, CUTPOINT(cutpointMsg)?$ OR(isOR)?, if (!includeToken) { var t1:GRuleToken = el1.element.tokenRef; if (t1) { startIndex += (t1.prefix ? t1.prefix.length : 0); } var t2:GRuleToken = el2.element.tokenRef; if (t2) { endIndex -= (t2.suffix ? t2.suffix.length : 0); } } else { if (r instanceof PElementResult) { var endIndex2:number = (r as PElementResult).endIndex; if (endIndex != endIndex2) { endIndex = Math.max(endIndex, endIndex2); } } } return new SourceLocation( new Position(el1.startLineIndex, startIndex), new Position(el2.startLineIndex, endIndex )); } // assertSourceMap // ----- validation ------------- private _validationHash:{[index:string]:Array} = {} private _validationErrors:Array = []; logValidationError(err:PValidationError):void { var ruid:string = err.result.getUid() var current:Array = this._validationHash[ruid]; if (!current) { current = []; this._validationHash[ruid] = current; } current.push(err); this._validationHash[ruid] = current; this._validationErrors.push(err); } // logValidationError get validationErrors():Array { return this._validationErrors; } validationErrorsByElement(e:IPResult):Array { return this._validationHash[e.getUid()]; } // validationErrorsBeElemnt hasError(element:IPResult = null, type:string = null, err:string= null):boolean { if (!element) { return this._validationErrors && this._validationErrors.length > 0; } var errs:Array = this.validationErrorsByElement(element); if (errs) { var e:PValidationError for (e of errs) { if ((!type || e.type == type) && (!err || e.localType == err)) { return true; } } } return false; } // hasError toErrString(content:IContentList = null):string { var out:string = ""; var ve:PValidationError for (ve of this._validationErrors) { out += ve.line.toString() + ":" + ve.index.toString() + " " if (ve.element && ve.element.varStr) { out += "(" + ve.element.varStr + ") "; } if (ve.localType) { out += ve.localType +": " } out += "[" + ve.type + "] " + ve.message + "\n"; if (content) { out += " \"" + (ve.line >= 0 ? content.indexToObject(ve.line).toString() : "" ) + "\"\n"; } } return out; } // toErrString /** * * iterates mappings over the following callbacks * */ eachProperty(o:any, depth:number, it:IObjectTreeFactoryIterator, topName:string = "", includeTop:boolean = true):void { // 1. --- basic object if (includeTop) { var pr:IPResult = this.pref( this._objectMap, o ) Assert.assert(pr != null); var loc:SourceLocation = this.toLoc(pr, true); it.objectF(o, depth, topName, pr, loc); } var propertiesList:Array = this.pref( this._objectPropertiesList , o ) ; // list of properties on this object ["p1", "p2" ...] var childLoc:SourceLocation if (propertiesList) { var leafPropertiesMap:{[index:string]:IPResult } = this.pref( this._objectPropertiesMap, o); // list of leaf properties ie ["p1" ] if p1 is a String but p2 is an object // --- 2. iterate over all child properties for (var i:number = 0; i < propertiesList.length; i++) { var propertyName:string = propertiesList[i]; var isLeaf:boolean = (leafPropertiesMap && leafPropertiesMap.hasOwnProperty(propertyName)); if (isLeaf) { // --- 3. child leaf --- var value:any = o[propertyName]; Assert.assert(PObjectMap.itemIsNonReferenceable(value)); var childPr:IPResult = leafPropertiesMap[propertyName] Assert.assert(childPr != null); childLoc = this.toLoc(childPr, true); it.leafF(o, depth+1, propertyName, value, childPr, childLoc) } else { // --- 4. recurse on child object ---- var childObject:Object = o[propertyName] var collectionMap:Array = this.pref( this._objectCollectionMap , childObject ); if (collectionMap) { var childArray:Array = childObject as Array; Assert.assert(childArray.length == collectionMap.length); childPr = this.pref( this._objectMap, childArray) loc = this.toLoc(childPr, true); it.arrayF(childArray, depth+1, propertyName, childPr, loc); for (var j:number = 0; j < collectionMap.length; j++ ) { value = childArray[j]; childPr = collectionMap[j]; childLoc = this.toLoc(childPr, true); var elementIsLeaf:boolean = PObjectMap.itemIsNonReferenceable(value); if (elementIsLeaf) { it.arrayLeafF(j, depth+2,value, childPr, childLoc) } else { it.arrayItemF(j, depth+2, value, childPr, childLoc) this.eachProperty(value, depth+2, it, null, false); } } } else { this.eachProperty(childObject, depth+1, it, propertyName); } } } } } // eachProperty clear():void { this._validationErrors = null; var o:UIDBase Assert.fail(" not implemented ") /* for (o of this._validationHash) { delete this._validationHash[o.getUid()]; } */ } static showLoc(src:SimpleTextContent, loc:SourceLocation):string { return "x" } // showLoc /** * To a babel-friendly ast /w extracting complete source location information for objects. * * Note: * * - we're mapping class to Node.type * rule(RuleType): ... <--- RuleType is the Node.type * * - properties named "type", "loc" , "__cls" ,"uid" will break this * * rule(MyType): X(type):etc <--- conflict breaks data integrity * * o straightforward to create a more general mechanism * o might reasonably put a validation error on the grammar's property names * when such an ast is targeted * * * - similarly, grammar types named: "StringLiteral" , "BooleanLiteral" "ArrayLiteral" * will cause similar problems * * ie myRule(StringLiteral): property_that_inst_value(etc) <--- will confict /w this ast mechanism * * - All leaf properties mapped to StringLiteral, BooleanLiteral, or ArrayNode * * rule(MyType): X(v) <-- maps to: * * { * type: "MyType", * loc: ..., * v: {type: StringLiteral, value: ...} * } * * * - this is meant to run on plain objects, not instances of classes * ie out = { __cls, uid/__uid v:"SomeResult"} * * * * (although we do have the data to do this by looking up all the registered values) * * * - grammar parser tree * each item has a parent rule state [RuleResult , ElementResult .... RuleResult] * mapping to grammar rules/elements. * gstack:[list of uids] * * https://github.com/babel/babylon/blob/master/ast/spec.md * * interface Node { * type: string; * loc: SourceLocation | null; * } * * interface SourceLocation { * source: string | null; * start: Position; * end: Position; * } * * interface Position { * line: number; // >= 1 * column: number; // >= 0 * } * */ toAST(topObject:Object):Object { return this.objectToAST(topObject); } private static IGNORE:Array = ["__cls", "__uid"] private objectToAST(o:any, includeGrammarStack:boolean = false):any { var out:any = {}; var loc:SourceLocation = this.objectToLoc(o) Assert.assert(loc != null); var type:string var cls:string = o.__cls var map:PObjectMap = this; if (cls) { type = G.stripClassPrefix(cls) } else { type = "Object" } out.type = type; out.loc = loc.toPlainObject(); var key:string for (key in o) { if (PObjectMap.IGNORE.indexOf(key) >= 0) { continue; } // Option 2 - iterate over the object var value:any = o[key] var astObject:Object = this.itemToAST(o, value, key, -1 ); out[key] = astObject; } // iteration over object keys this.addUID(out) return out } // toAST private itemToAST(o:any, value:any, key:string, index:number):any { var childType:string var childLoc:SourceLocation var gstack:Array var childValue:any var astObject:any var astLeaf:any = this.toLeafAST(value) if (astLeaf) { astObject = astLeaf; var includeTokenInLocation:boolean = G.isBool(value); childLoc = this.objectToLoc(o, includeTokenInLocation, key, index, value); // <--- do this elsewhere astObject.loc = childLoc.toPlainObject(); Assert.assert(childLoc != null); this.addUID(astObject) return astObject } if (value instanceof Array) { var arr:Array = value as Array; var childArrayValues:Array = []; for (var i:number = 0; i < arr.length; i++) { var childItem:any = arr[i]; var astItemObject:Object = this.itemToAST(arr, childItem, null, i) childArrayValues.push(astItemObject); } childLoc = this.objectToLoc(arr, false); if (!childLoc) { this.objectToLoc(arr, false); } Assert.assert(childLoc != null); astObject = { type:"ArrayLiteral", value: childArrayValues, loc: childLoc.toPlainObject() } } else if ( G.isObj(value) ) { astObject = this.objectToAST(value); // recurse childLoc = this.objectToLoc(value, false, key); childValue = null } else { Assert.fail() } if (!astObject) { astObject = { type: childType, loc: ( childLoc.toPlainObject()) } } if (childValue) { astObject["value"] = childValue; } this.addUID(astObject) return astObject; } private addUID(o:any):void { o.uid = UID.createInt().toString() } private toLeafAST(value:any):Object { var isStr:boolean = G.isStr(value); var isBool:boolean = G.isBool(value); var astObject:any = null; if (isStr || isBool) { astObject = {}; astObject["type"] = isStr ? "StringLiteral" :"BooleanLiteral"; astObject["value"] = isStr ? value : true //childLoc = objectToLoc(o, true, key, -1, value); // <--- do this elsewhere //Assert.assert(childLoc != null); } return astObject } } // class export interface IPResult { isValid(parentMap:PObjectMap, lineID:number):boolean; firstElement():PElementResult; firstRule():PRuleResult; lastElement():PElementResult; show():string; // debug only getUid():string; } // interface export class PElementResult extends UIDBase implements IPResult { /** * Result of a single element match. * */ constructor(element:GElement = null, valid:boolean = true, err:string = null, childResult:PRuleResult = null, prefixStart:number = -1, contentStartIndex:number = -1, contentEndIndex:number = -1, suffixStart:number = -1, endIndex:number = -1) { super(); this.element = element; this.valid = valid; this.err = err; this.childResult = childResult this.tokenPrefixIndex = prefixStart; this.tokenSuffixIndex = suffixStart; this.contentIndex = contentStartIndex; this.contentEndIndex = contentEndIndex; this.endIndex = endIndex; } // element:GElement; varStr:string = null; //modifier:string = null; /** * result when we have a composite rule X[Y(var1) Z(var2)] */ childResult:PRuleResult; valid:boolean = false; err:string = null; errCode:number; /** * May have children when we have a multiply occuring values */ children:Array; /// array of PRuleResult // indicies startLineIndex:number = -1; tokenPrefixIndex:number = -1; startIndex:number = -1; contentIndex:number = -1; contentEndIndex:number = -1; tokenSuffixIndex:number = -1; endIndex:number = -1; // -- state - allows upward traversal of tree ---- parentRuleResult:PRuleResult; /** * This is the the parent line result * * Although when this is the first element in a line child * a) This is parsed as a direct child of a (more or less throw away rule result) * b) .. it is added as a child of an effective parent * c) it's immediate parent is a child (direct or part of a collection) of the line rule result * */ parentLineRuleResult:PRuleResult; parentLineCollectionIndex:number = -1; effectiveParentResult:PRuleResult; // the rule to which a parent is added parentElementIndex:number; parentElement:GElement; parentCollectionIndex:number; cutpointFailElement:PElementResult; // ------------------------------------------------------- /** * return the match index of the parent line. * * Not necessarily equal to the index of the grammar child line element (unless all child line rules * are non-optional). */ lineMatchIndex():number { // Assert.assert(parentLineRuleResult) -- just call hasSecondaryResult first /* var out:number = -1; for (var i:number = 0; i < parentLineRuleResult.parentLineResult.lineChildren.length; i++) { var r:PRuleResult = parentLineRuleResult.parentLineResult.lineChildren[i]; if (r.child(0) == this) { out = i; break; } } Assert.assert(parentLineRuleResult.lineMatchIndex == out); */ return this.parentLineRuleResult.lineMatchIndex; //return parentLineRuleResult.lineMatchIndex; } isLineChild():boolean { return this.parentLineRuleResult != null; } child(index:number):PElementResult { return this.childResult ? this.childResult.child(index) : null } toStringLow(prefix:string):string { var out:string; var elementStr:string let {element, childResult} = this; if (this.children) { if (this.children.length > 0) { out = prefix + "[ (" + element.toString() + ") \n"; var e:PElementResult for (e of this.children) { out += e.toStringLow(prefix + " -"); } out += prefix + "]\n"; } else { out += prefix + "[ ] \n"; } } else if (childResult) { return childResult.toStringLow(prefix); } else if (this.err) { elementStr = element ? element.toString() : "" out = prefix + "o [ " + elementStr + " ] error: " + this.err + ' \n'; } else { elementStr = element ? element.toString() : "" out = prefix + 'o [ ' + elementStr+ " ] "; if (element.prefixTokenValue) out += element.prefixTokenValue + " "; // FIX_THIS - will incorrectly output the prefix token of the first element of a list if (element.suffixTokenValue) out += element.suffixTokenValue; if (element.multiplicity) out += element.multiplicity; if (element.wsPolicy) out += element.wsPolicy; if (this.varStr) out += ' value = "' + this.varStr + '"'; out += ' \n'; } return out; } // toStringLow isValid(parentMap:PObjectMap = null, lineID:number = -1):boolean { if (this.childResult) { return this.childResult.isValid(parentMap, lineID); } else if (this.children) { var child:PElementResult for ( child of this.children) { if (!child.isValid(null, lineID)) { return false; } } } return this.valid; } static create():PElementResult { return new PElementResult; } release():void { // to implement } tr(childIndex:number = -1):string { return this.element ? this.element.tr() : ""; } show():string { return this.tr(); } /** * result trace - for debug, useful for quickly identifying an element * * Iterates over actually present element results, as opposed to tr() which iterates over all grammar constructs */ rtr():string { let {element, varStr} = this; if (element) { var out:string = ""; out = element.tokenName ? element.tokenName : ""; out = out + (element.varName != null? ('(' + element.varName + (varStr ? '="' + varStr + '"' : "") + ')') : "") if (element.childRuleName) { out = out + ":" + element.childRuleName; } if (this.childResult) { out += this.childResult.rtr(); } else if (this.children) { out += "|" for (var i:number = 0; i < this.children.length; i++) { var child:PElementResult = this.children[i]; out += "#" + i.toString() + "[" + child.rtr() + "]"; out += " "; } out += "|"; } return out; } return ""; } private lastChild():PElementResult { return this.children[this.children.length -1]; } private firstChild():PElementResult { return this.children[0]; } /** * * */ firstRule():PRuleResult { if (this.childResult) { return this.childResult; } return null; } firstElement():PElementResult { if (this.childResult) { return this.childResult.firstElement(); } else if (this.children) { return this.firstChild().firstElement() } return this; } lastElement():PElementResult { if (this.childResult) { return this.childResult.lastElement(); } else if (this.children) { return this.lastChild().lastElement(); } return this; } // lastElement } // class //- -------------------- /** * Details of a match parser rule */ export class PRuleResult extends UIDBase implements IPResult { constructor(source:string = null, r:GRule = null, element:PElementResult = null) { super(); this.source = source; this.matchedRule = r; if (element) { this.results.push(element); } } source:string; /** * Array of secondary grammar (ie managing blank lines, maybe comment etc) */ preResults:Array; preResultsLength():number { return this.preResults ? this.preResults.length : 0; } secondaryResults:Array; /** * Used in incremental parsing/tokenising/debugging when a parsing may pause in the middle of a rule * * (this could also be inferred form the call stack) */ lineIterationInProgress:boolean = false; /** * Reference to the rule that was matched */ matchedRule:GRule; /** * details of the match. * *

Includes resul elements that document errors

*/ results:Array = new Array() /** * True if the last matched element has matched WS */ matchedWS:boolean = false; /** * Extracted values of variables */ data:any startIndex:number = -1; // cached only, for convenience endIndex:number = -1; /** * result starts on this line in the content. */ startLineIndex:number = -1; /** * Index of match, not the index of the rule in the grammar. */ lineMatchIndex:number = -1; lineChildren:Array; lineChild(index:number, collectionIndex:number = -1):PRuleResult { let {lineChildren} = this; var lchild:PRuleResult = lineChildren && lineChildren.length > index ? lineChildren[index] : null; if (!lchild) { return null; } if (collectionIndex < 0) { Assert.assert(lchild.child(0).element.allowMany == false); return lchild; } else { // parentRuleResult - is PRuleResult used by the parser, but not that effectiveParentRestult the parent Line ... if ((lchild.child(0).children.length -1) < collectionIndex) { return null } return lchild.child(0).children[collectionIndex].parentRuleResult; } } // lineChild /** * Convenience method - return the element of a line child, */ lineElement(index:number, collectionIndex:number = -1):PElementResult { let {lineChildren} = this; var lineChild:PRuleResult = lineChildren && lineChildren.length ? lineChildren[index] : null; if (!lineChild) { return null; } if (collectionIndex < 0) { return lineChild.child(0); } Assert.assert(lineChild.results.length == 1); // child rules by definiton by a single element return lineChild.child(0).children[collectionIndex]; } // lineElement firstRule():PRuleResult { return this; } firstElement():PElementResult { var el:PElementResult = this.results[0]; return el.firstElement(); } // firstElement lastElement():PElementResult { let {lineChildren, results} = this; if (lineChildren && lineChildren.length > 0) { var lastLine:PRuleResult = lineChildren[lineChildren.length -1] return lastLine.lastElement(); } if (results.length == 0) { Assert.fail(); } var lastEl:PElementResult = results[results.length-1] return lastEl.lastElement(); } // ------- context ------- parentElementResult:PElementResult; // parent element when this is a child rule (ie X[ ... ]) parentLineResult:PRuleResult = null; // parent line rule preResultIndex:number = -1; // for top level rules only. secondaryResultIndex:number = -1; // secondary items (ie /** * should only be called by rule state * @private */ addResult_low(v: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) this.results.push(v); if (v.childResult != null) { this._matchedElements += v.childResult.matchedElements; } else if (v.valid) { this._matchedElements++; } } // addResult removeResult_low(v:PElementResult):void { var last:PElementResult = this.results.pop(); Assert.assert(v == last); if (v.childResult != null) { this._matchedElements -= v.childResult.matchedElements; } else if (v.valid) { this._matchedElements--; } } private _matchedElements:number = 0 get matchedElements():number { return this._matchedElements; } quickErrorStr():string { var out:string = ""; if (this.results) { var e:PElementResult for (e of this.results) { if (e.err != null) { out += ("err: " + e.err); } } } return out; } // quickErrorStr /** * Recursively determine if this rule is valid. * * Note - this explicitly recurses, and is not an efficient way to determine valiidity. * * @param includeLines recurse on child lines only if true */ isValid(parentMap:PObjectMap = null, lineID:number = -1):boolean { if (this.map && this.map.hasError()) { console.log(this.map.toErrString()); return false; } var m:PObjectMap = parentMap ? parentMap : this.map; if (m && m.validationErrorsByElement(this)) { //trace(m.toErrString()); return false; } var restrictToLine:boolean = (lineID >= 0); var result:PElementResult for (result of this.results) { if (m) { if (m.validationErrorsByElement(result) ) { //trace(m.toErrString()); return false; } } if (restrictToLine && result.startLineIndex != lineID) { // FIX_THIS - prex /w pre-alpha continue; } if (result.childResult) { // if child line is on a new line, if ( !(result.valid && result.childResult.isValid(m, lineID) ) ) { return false; } } else if (!result.isValid()) { //trace(' error ' + GErr.ERR_STR[result.errCode]); return false; } else if (!(result.errCode == GErr.NOT_AN_ERROR ) && (result.element.allowMany || result.element.lineAllowMany) ) { var e:PElementResult for (e of result.children) { if (m && m.validationErrorsByElement(e)) { return false; } if (e.childResult && !e.childResult.isValid(m, lineID) ) { return false } if (!e.valid) { return false; } } } if (!(result.valid)) { return false; } } /* this is unnecessary if (lineChildren && lineChildren.length > 0) { for each (var lc:PRuleResult in lineChildren) { if (!lc.isValid(m)) { return false; } } } */ return ((this.results.length > 0) || this.preResults != null || this.secondaryResults != null); // not valid if we have 0 results } contentString():string { if (this.startIndex >= 0 && this.endIndex >= 0 && this.source) { return this.source.slice(this.startIndex, this.endIndex+1); } return null; } // contentString child(index:number):PElementResult { return this.results.length > index ? this.results[index] : null; } /** * */ toString():string { return (this.matchedRule ? this.matchedRule.ruleString : " " ) + " \n" + this.toStringLow(" "); } toStringLow(prefix:string):string { if (!this.results || this.results.length == 0) { return prefix + "\n"; } var out:string = ""; var r:PElementResult for (r of this.results) { out += r.toStringLow(prefix + "|"); } return out; } // toStringLow getSecondaryResult(index:number):PRuleResult { return this.secondaryResults && this.secondaryResults.length > index ? this.secondaryResults[index] : null; } getResult(index:number):PElementResult { return index < this.results.length ? this.results[index] : null; } map:PObjectMap; static create():PRuleResult { return new PRuleResult; } tr():string { if (this.matchedRule) { return this.matchedRule.tr(); } return "[]"; } show():string { return this.tr(); } rtr(elementIndex:number = -1):string { var out:string = ""; if (this.matchedRule) { if (this.results && this.results.length > 0) { out += "["; var er:PElementResult for (er of this.results) { out += " " + er.rtr(); } out += "]" } return out; } return "[]"; } /** * Distinguishes between * rule1: ==> isLineTopRule = true * --> X :: ... * * and * * rule2: * --> X :: ... ==> isLineTopRule = false * --> Y :: ... * * note that this includes * * rule1: ... :rule2 ... * */ isLineTopRule():boolean { return this.matchedRule.name && (this.parentElementResult == null || this.parentLineResult == null); } /** * Documents the relattion of child line rules (as maintained in lineRules) and the elements as they are kept * in a parent rules's results property. */ lineMainElement(lineResultIndex:number, expectedRuleResult:PRuleResult):PElementResult { if (!this.lineChildren || this.lineChildren.length < lineResultIndex) { return null; } // asssumptions inherent in this: // a) this has a line rule, in the form X :: [e0 ... eN ... e_line0 ... e_lineN] // b) there is a 1:1 connespondence between the [e_line0 .. e_lineN] elements and lineChildren var childElements:Array if (!this.isLineTopRule()) { // this is the crux of the complexity. A nested rule: // --> [X [ e1 ... eN ... e_lineN ]] ruleResult has a single line element. // --> Y [ ... ] // e_lineN represents this child rule. childElements = this.child(0).childResult.results; } else { childElements = this.results; // no parent rule } var offset:number = (this.lineChildren.length - 1) - lineResultIndex; var eLine:PElementResult = childElements[childElements.length - offset -1] // resolve e_LineN var lr:PRuleResult = this.lineChildren[lineResultIndex]; // its parenet line [Y [...]], which Assert.assert(lr == expectedRuleResult); // Assert.assert(lr.child(0) == eLine); Assert.assert(eLine.parentLineRuleResult == lr); return eLine; } eachElement(f:Function):void { var result:PElementResult for (result of this.results) { if (result.children) { var e:PElementResult for (e of result.children) { f(e); if (e.childResult) { e.childResult.eachElement(f); } } } else { f(result); if (result.childResult) { result.childResult.eachElement(f); } } } if (this.lineChildren) { var lineResult:PRuleResult for (lineResult of this.lineChildren) { lineResult.eachElement(f); } } } } // class export interface IObjectTreeFactoryIterator { objectF(o:any, depth:number, propertyName:string, pr:IPResult, loc:SourceLocation):void; leafF(o:any, depth:number, propertyName:string, value:any, pr:IPResult, loc:SourceLocation):void arrayF(array:Array, depth:number, propertyName:string, pr:IPResult, loc:SourceLocation):void; arrayLeafF(index:number, depth:number, value:any, pr:IPResult, loc:SourceLocation):void; arrayItemF(index:number, depth:number, o:Object, p:IPResult, loc:SourceLocation):void; } // class{