import SimpleTypeManifest from '@cafetextual/util/dist/src/manifest/SimpleTypeManifest'; import PObjectMap, { PRuleResult, PElementResult } from "./PObjectMap"; import GRule from "./grammar/GRule"; import PValidationError from "./PValidationError"; import IGrammarProcessor from "./postprocess/IGrammarProcessor"; import Assert from "@cafetextual/util/dist/src/assert/Assert"; import G from "@cafetextual/util/dist/src/type/G"; import { MTypeDef } from "@cafetextual/util"; /** * Grammar to object mapper. * * */ export default class ObjectMapper { // OBJECT mapping error types static BAD_CHILD_TYPE_NODE:string = "bad child ^^ node"; static UNNAMED_ARRAY:string = "unnamed array"; static CANNOT_SET_PROPERTY:string = "cannot set property"; static UNNAMED_HOIST_WITH_TREE:string = "unnamed hoist /w tree "; static OPTIONAL_RAW_TYPE_NODE:string = "badOptionalTypeNode"; static COLLECTION_ON_TREE_TYPE_NODE:string = "badTreeType"; static INVALID_TREE_NODE:string = "badTreeNode"; mapFrom(result:PRuleResult ):void { // restriction 1: not valid on top level Assert.assert(result && result.parentElementResult != null); // restriction 2: does not support collection Assert.assert(result.parentElementResult.element.allowMany == false); // restriction 3: ensure that the parent is a named: ie (value):link // this is because we need to instantiate an object, which is what the varName (value) indicates Assert.assert(result.parentElementResult.element.varName != null); var rule:GRule = result.matchedRule var data:Object = this.createDataObject(rule, true); // explicitly create data type this.mapValues(result, rule, data, null, true ); // TODO - reuse object map etc for fully incremental parsing } private map:PObjectMap; /** * Maps values of variables matched in the grammar to an object * @param data the parent data object - ie (var0)[(var1) (var2)] * @param collectionData parent collection object - ie ^(vars)* [(var1) (var2)] * @param isRoot indicates that this is the root node. * @return Returns the rule's matched tree node (^) if present, falling back to the type/hoist (^^) node if present, null otherwise. */ mapValues(result:PRuleResult, rule:GRule, data:any = null, collectionData:Array = null, isRoot:boolean = false):PElementResult { if (isRoot) { this.map = new PObjectMap; result.map = this.map; } var origionalData:any = data; var origionalCollectionData:Array = collectionData; var item:PElementResult; var treeNode:PElementResult = null; // the tree node ie ^(op)[ ... ] var typeNode:PElementResult = null // the type node ie ^^(_optionalName):typed-childrule var treeNodeName:string = null; var dataIsSimpleString:boolean = false; var treeData:any = null; // the data object of a tree node ie ^(var) var treeCollection:Array = null; // if the tree is a collection ^(var)[...]* // 1. scan to get the tree node (^ or ^^^) and thh typeNode (^^) // - ^ may not repeat // - valid ^ will trump instances of ^^ if (result.results) { for (item of result.results) { if (!item.valid) { // TODO_RESULT_MAPPING - could still map default objects? continue; // assuming that invalid results are being dealt with elsewhere, so we may safely ignore them here } if (!item || item.element == null) { Assert.fail(); // debug only } if ( item.element.asTreeNode() && item.valid) { Assert.assert(treeNode == null); // limit of one valid tree node math - TODO - add validation error treeNode = item; if (treeNode.element.asTreeType) { typeNode = treeNode; // this may trump any existing tree node ie exp: ^^(first):value ^^^(_op)[OP (second):exp]? } // if (op) matches, ^^ on (first) will be ignored } if (item.element.asTreeType && item.valid && !(item==treeNode) ) { if (typeNode != null) { this.map.logValidationError(new PValidationError(PValidationError.OBJECT_MAPPING, item, "multiple ^^ nodes ", ObjectMapper.INVALID_TREE_NODE ) ); Assert.assert(typeNode == null); // TODO - catch this in element validation } typeNode = item; } } } // 2. no parent object & no typeNode (^^) (or trumped by treeNode ie ^ or ^^^) // so we unless we have a parent object/colletion, we go ahead a create the object from teh treeNode if (!data && !collectionData) { if (!typeNode || ( (typeNode && treeNode) && (typeNode != treeNode) ) ) { // TO_OPTIMIZE - reconsider this mechanism. There are instance where we're creating the object only to be subsequenly thrown away // ie (vars)[ ^^:rule ], data = origionalData = this.createDataObject(rule, true /*force*/ ); } } // 2. we have a typeNode (^^ or ^^^ ), so deal with it here if (typeNode) { // no tree node (^), or it equals typeNode (^^^) if (!treeNode || (treeNode == typeNode)) { // i. if ( (treeNode == typeNode) && typeNode.element.allowMany) { if (!(typeNode.element.childRule == null)) { this.map.logValidationError(new PValidationError(PValidationError.OBJECT_MAPPING, treeNode, "^^^(var)* and ^^^(var):* may not have a child rule", ObjectMapper.COLLECTION_ON_TREE_TYPE_NODE ) ); // Assert.assert( typeNode.element.childRule == null); // TODO - this can be validated at the grammar level return null; } } // x. only create data object from typeNode in the absence of a treeNode. If we have a treeNode, //Assert.assert(!typeNode.element.allowMany); // grammar must error this, behaviour not defined // we're only going to create data for this //data = createTypeNodeDataObject(typeNode, rule); if (typeNode.element.childRule) { // x. attempt to create an only if we have string typing // we do not attempt to instantiate an object locall, as the point of ^^ is that the object // instantiateion //data = createDataObject(typeNode.element.childRule, false); // - don't sent data, as the point is that we're defereing the instantiation of the data object // - don't sent collection: ^^[ ... ] required a single object returned var typeData:any = null; /// ^^^( ):childRule or ^^^(_:Type) if (typeNode == treeNode) { typeData = this.createDataObject( null, false, /* force - we're only going to instantiate if we have a specific type registered for TypeNode */ typeNode, rule); } // ^^(var)[ ... ] requires that we do a depth first traversal to get the data object from the child object // - sibling properties are then added to it. ^^(var)[ ... ] (siblingVar) var childTreeOrTypeNode:PElementResult = this.mapValues(typeNode.childResult, typeNode.element.childRule, typeData/*data*/ , // this is only set if there's an explicit type set on the object null // collectionData - why is this null ); var childData:any = typeNode.childResult.data if (childTreeOrTypeNode && childTreeOrTypeNode.element.asTreeType) { // ie nested rule1: ^^:rule2 // rule2: ^^:rule3 // rule3: X(var1) //console.log(" ^^: " + typeNode.element.childRuleName + "--> ^^: " + childTreeOrTypeNode.element.childRuleName); Assert.assert(!childTreeOrTypeNode.element.nameIsRef); // [ ^^(%name) (value) ] doesn't make any sense here, should be disallowed by grammar if (collectionData) { //console.log(" ==> and we have collection data "); //Assert.assert(collectionData[collectionData.length -1] == item.childResult.data); //map.mapCollectionObject(item, collectionData, collectionData.length -1, item.childResult.data); } } /* TODO - checkForErrors if childTreeOrTypeNode is a typeNode, in conjunection /w a typeNode ^^ can't do this since sibling element may not have been added yet. var processor:IGrammarProcessor = rule.getTypeProcessor(typeNode.element.varName); if (processor) { childData = processor.postProcess(ChildData); } */ if (treeNode != typeNode) { // ^^ but not ^^^ data = childData; if (collectionData) { //collectionData.push(data); this.addCollectionData(typeNode, collectionData, data); } } else { // ^^^(var)[...] // and we'll deal with this wrt collection data when we deal with the treenode treeData = childData; } } else if (typeNode.element.allowNone == false) { // x. typeNode (^^) but no child rule ie ^^(var) Assert.assert(typeNode.element.childRule == null); // x. ^^^ if (treeNode == typeNode) { if (treeNode.element.allowMany) { // ^^^(list)+ // "*" disallowed //treeData = []; // we will instantiate treeCollection below when we deal with the treeNode } else { // ^^^(item) - recall that the purpose of this construct might be to specify a particular type. // (but will default to a regular object) treeData = this.createDataObject(null, true, treeNode, rule); // instantiate object either from an explicitly registered type or default to Object } } else { // x . ^^(var) data = ""; } } else { // x. no child rule and optional ie ^^(var)? // TODO - add validation error. Actually this can be done at the grammar level this.map.logValidationError(new PValidationError(PValidationError.OBJECT_MAPPING, typeNode, "type/hoist node may not be optional without a child rule - ^^(var)?", ObjectMapper.OPTIONAL_RAW_TYPE_NODE)); return null; } dataIsSimpleString = G.isStr(data); // typically when ^^(var) // Note that we're not recursing on a child rule because we've (necessarily) already done a depth first recursion // no tree/type-hoist nodes above } else { // we do have a treeNode and it it not the typeNode. ie ^^(var1) ^(var2) ==> ^ will trump ^^, only need to ensure that ^^ is named // could relax this in the presennce of a collection, ^(vars)* ^^:my-rule if (typeNode.element.varName == null) { this.map.logValidationError(new PValidationError(PValidationError.OBJECT_MAPPING, typeNode, "a ^^ must be named when in the presence of a ^ (or ^^^) node. Note - this can be relaxe it the ^ node is a collection, in which case it shouldn't matter", ObjectMapper.UNNAMED_HOIST_WITH_TREE)); return null;//treeNode; } //Assert.assert(typeNode.element.varName != null); // TODO - error handling . Could enforce this in grammer, but in principle // there could be cases were both nodes will never match simultaneously } } // end if typeNode (^^) // 3. we have a treeNode, all other other element are children on this object var rawText:boolean = treeNode ? treeNode.element.filterRawText : false; var isRef:boolean = treeNode ? treeNode.element.nameIsRef : false; if (treeNode && !rawText ) { if (isRef) { Assert.assert(treeNode && !typeNode); Assert.assert(rawText || !treeNode.childResult); } var treeOK:boolean = (treeNode.element.varName != null) || (treeNode == typeNode) if (!treeOK) { Assert.assert( treeOK ); // undefined behavioud for unnamed nodes ie ^[ ...] . grammar validation should catch this for now. ==> ^^^* is ok } // might relax this for ^^^ (ie treeNode == typeNode) // 2a. create an object for the node if (treeNode != typeNode) { // ^ and not ^^^ if (this.allowMany(treeNode) ) { // ^(vars)+ or ^(vars)[...]* --> simple array Assert.assert(typeNode != treeNode); // ie ^^^( )[...]* not allowed treeCollection = new Array; // ^(varName)* || ^(varName)[...]* } else if (treeNode.element.childRule) { // ^(var)[ ... ] tree node /w child treeData = this.createDataObject(treeNode.element.childRule, true, treeNode, rule); // ^(varName):typedChildRule // would be safe to recurse here to allow things like ^[ ^^:type] or ^(node1)[ ^(node) ] } else if (treeNode.element.nameIsRef) { // ^(%varName) // don't create treeData Assert.assert(treeData == null); } else { // ^(var) --> simplest tree node treeData = this.createDataObject(null, true, treeNode, rule); // ^(varName) ==> simple object } } else { // ^^^ Assert.assert(treeNode == typeNode); if (treeNode.element.allowMany) { // ^^^(_var)* - since this is a typeNode the name will have no impact treeCollection = new Array; } else { // in this case, treeData will have been created above } } // 2b. place new object in the parent object or collection if (collectionData) { if (treeCollection) { // ie ^(vars)* /w a parent collection //collectionData.push(treeCollection); // ^(var)[...]* this.addCollectionData(treeNode , collectionData, treeCollection); collectionData = treeCollection; // ensures that all further data is pushed into the arrauy data = null; } else { // ---> this is being deferred // origionalCollectionData (have been set) = colelctionData //collectionData.push(treeData); // this is now defered to the ed if (!isRef) { Assert.assert(treeData != null); data = treeData; // ensures all further data is added to object collectionData = null; } } } else if (data == null) { // this will happen in the case of a parent ^^ type node Assert.assert(treeNode == typeNode); Assert.assert(treeData != null || treeCollection != null); if (treeCollection) { Assert.assert(collectionData == null); // Q: why? collectionData = treeCollection; } else { data = treeData; } } else { // no parent data or collection data treeNodeName = treeNode.element.varName; if (treeCollection) { if (treeNode != typeNode) { // ^(var)* or ^ this.addData(treeCollection, treeNode, treeNodeName, dataIsSimpleString, data, typeNode, treeNode, treeData, treeCollection, isRef, collectionData); //data[treeNodeName] = treeCollection; Assert.assert(collectionData == null); collectionData = treeCollection; data = null; } else { // ^^^(list)+ collectionData = treeCollection; data = null; } } else { Assert.assert(data != null); // ----> this probably also need to be deferred //data[treeNodeName] = treeData; // addData(treeNode, if (treeNode && isRef) { // don't overwrite parent data } else { data = treeData; } } } // if the tree node has a child rule the need to reurse } // tree node //. of a treenode wants raw text, if (treeNode && rawText) { var rawString:string = result.contentString(); this.addData(rawString, treeNode , treeNode.element.varName, dataIsSimpleString, data, typeNode, treeNode, treeData, treeCollection, isRef, collectionData); result.data = data ? data : collectionData; return treeNode; } // 3. main iterate over this rule's results for (item of result.results) { if (!item.valid) { continue; // assuming that invalid results are being dealt with elsewhere, so we may safely ignore them here } // hit the ^ node, but not not ^^^ if ((item == treeNode) && (treeNode != typeNode) ) { if (treeNode.element.childRule) { if (this.allowMany(treeNode)) { var cr:PElementResult for (cr of treeNode.children) { this.mapValues(cr.childResult, treeNode.element.childRule, data, collectionData); } } else { this.mapValues(treeNode.childResult, treeNode.element.childRule, data, collectionData); // } } continue; // note that we'll register the object to element later } else if ( (item == typeNode) && (!treeNode || (treeNode == typeNode) )) { // /* this will have been done earlier if (typeNode.element.childRule) { mapValues(typeNode.childResult, typeNode.element.childRule, data, collectionData); // } else { Assert.assert(dataIsSimpleString); addData(typeNode.varStr, null); } */ if (typeNode.varStr && (typeNode.element.childRule == null) && (treeNode != typeNode ) ) { this.addData(typeNode.varStr, typeNode, null, dataIsSimpleString, data, typeNode, treeNode, treeData, treeCollection, isRef, collectionData); } // TODO ---> ensure that typeNode is registered here continue; } var varName:string = item.element.varName; // explicit var name present ie. (varName)... if (varName != "" && varName != null) { var value:string; if (this.allowMany(item)) { // (varName)...* var childItems:Array = []; // add and empty array to the output this.addData(childItems, item, varName, dataIsSimpleString, data, typeNode, treeNode, treeData, treeCollection, isRef, collectionData); this.extractToCollection(result, item, childItems, null); } else { // (varName)... single item only if (item.element.filterExists) { // | exists filter this.addData(item.valid, item, varName, dataIsSimpleString, data, typeNode, treeNode, treeData, treeCollection, isRef, collectionData); } else if (item.element.filterRawText) { // | raw-text filter value = this.rawContent(result, item); this.addData(value,item, varName, dataIsSimpleString, data, typeNode, treeNode, treeData, treeCollection, isRef, collectionData); } else { if (item.childResult) { Assert.assert((item != typeNode) || (treeNode != typeNode)); // this is necessarily not a ^^ typenode (or if there is a typeNode, it's overridded by a treeNode, ^(tree) ^^(not_a_type_node) // nested rule TOKEN(var)[ ... ] var newChildData:any = this.createDataObject(item.element.childRule, false, item, rule ); // /* don't force creation if no type found*/); var itemChildTypeNode:PElementResult = this.mapValues(item.childResult, item.element.childRule, newChildData); if (! (newChildData || item.childResult.data)) { console.log(' bug '); //if (map.hasError(item)) { // return null; //} Assert.assert(newChildData || item.childResult.data); } this.addData( item.childResult.data, item, varName , dataIsSimpleString, data, typeNode, treeNode, treeData, treeCollection, isRef, collectionData) } else { // plain var (var) // leaf TOKEN(var) value = item.valid ? item.varStr : null; this.addData(value, item, varName, dataIsSimpleString, data, typeNode, treeNode, treeData, treeCollection, isRef, collectionData); } } } } else { // unnamed element element varName == null if ( ! ( !this.allowMany(item) || collectionData) ) { var err:PValidationError = new PValidationError(PValidationError.OBJECT_MAPPING, item, "unnamed array are not supported (ie [:my-rule]*) except with a parent collection node (ie (vars)* [:my-rule] ) ", ObjectMapper.UNNAMED_ARRAY); this.map.logValidationError(err); continue; } Assert.assert( (this.allowMany(item) == false) // by default anonmyous arrays not supported // || item.element.asTree || (collectionData != null) ); // | exists && | raw-text filters don't mean anything here if (item.childResult) { // unnamed child rule [ ] var childMatchedNode:PElementResult = this.mapValues(item.childResult, item.childResult.matchedRule , data, collectionData); // simple recursion on anonmyous rule if (childMatchedNode && childMatchedNode.element.asTreeType) { if (collectionData) { // [... ^^( ) ... ] ; } else { var msg:string = "unnamed non-type node's child rule returns a type node (consider adding a ^^) " + item.tr(); this.map.logValidationError(new PValidationError(PValidationError.OBJECT_MAPPING, item, msg, ObjectMapper.BAD_CHILD_TYPE_NODE )); } } if (collectionData && treeNode && childMatchedNode && childMatchedNode.element.asTreeType) { // scenario: ^(list)+ [ ^^(vars):etc] // - we have a collection, which means that the child rule may match a tree node // ==> we'll need to explicity register the object if (!childMatchedNode.element.nameIsRef) { if (! (collectionData[collectionData.length -1] == item.childResult.data ) ) { Assert.assert(collectionData[collectionData.length -1] == item.childResult.data); } this.map.mapCollectionObject(item, collectionData, collectionData.length -1, item.childResult.data); } } } else if (this.allowMany(item)) { // TODO: either // a) add a run time error here, or // b) catch this error in grammar validation Assert.assert(collectionData != null); // unnamed collection only valid when we have this.extractToCollection(result, item, null, collectionData); } } } // iteration over matched elements // x. post process // - only implemented for ^^^ constructs // - TODO - relax condition so it may be applied to any element if (treeNode && !treeCollection && !isRef) { Assert.assert(treeData != null); Assert.assert(treeCollection == null); // not dealing with this var processor:IGrammarProcessor = rule.getTypeProcessor(treeNode.element.varName); /// BUG -- if we have exp: ^^(left):leafX ^^^[^^^(_op:Op)[OP(op) (right):exp] | ^^^(_app:App)[(right):exp] ]? . (left) hasn't been add yet if (processor) { Assert.assert(data == treeData); treeData = processor.postProcess(treeData); data = treeData; } if (origionalData) { try { origionalData[treeNode.element.varName] = treeData; } catch (e) { Assert.fail(' TODO - map errors: failed to map property' + treeNode.element.varName + ' on object ' + G._getQualifiedClassName(origionalData) ) } this.map.mapObject(treeNode, treeData, origionalData, treeNode.element.varName); } else if (origionalCollectionData) { origionalCollectionData.push(treeData); this.map.mapCollectionObject(treeNode, origionalCollectionData, origionalCollectionData.length -1, treeData); } } // x. finally, add the data to the result if (typeNode && (!treeNode || treeNode == typeNode) ) { // ^^ but not if trumped by ^, unless ^^^ if (!data) { result.data = treeCollection } else { result.data = data; // may be a string } } else { result.data = origionalData ? origionalData : data; } if (isRoot) { var topObject:any; if (typeNode && (!treeNode || treeNode == typeNode)) { // if ^^(var) or ^^^(var) but not ^^(var1) ^(var2 // TODO - better semantics, this duplicates code if (treeCollection) { // ^^^(list)* Assert.assert(typeNode == treeNode); topObject = treeCollection; } else if (treeData && (typeNode == treeNode)) { // ^^^(vars) topObject = treeData } else { // ^^(var)[...] or ^^(varStr) topObject = data; } } else { topObject = treeNode ? origionalData : data; // FIX_THIS - clear indication that the semantics of "origionalData" are being abused/ } if (topObject == null) { console.log(' bug'); Assert.assert(topObject != null); } if (!dataIsSimpleString) { // ie if top rule ue ^^(varStr) , don't bother to register anything. This is a trival grammar anyway, no need to support this.map.mapObject(result, topObject, null, null, true); } } return treeNode ? treeNode : typeNode; } // mapValuesR private addData(item:any, e:PElementResult, varName:string, dataIsSimpleString:boolean, data:any, typeNode:PElementResult, treeNode:PElementResult, treeData:any, treeCollection:Array, isRef:boolean, collectionData:Array):void { if (dataIsSimpleString) { Assert.assert(G.isStr(item)); Assert.assert(typeNode && !typeNode.element.childRule); // attempting to add data to a simple string ie [^^(var1) (var2)] '); data = item as String; } else if (treeData && !treeCollection) { // TODO - error handling and messaging try { treeData[varName] = item; } catch (error) { var msg:string = "Cannot set property \"" + varName + "\" " + error.message; console.log(" failed to set tree value on " + varName + "\n" + G.getStackTrace(error)); this.map.logValidationError(new PValidationError(PValidationError.OBJECT_MAPPING, e, msg, ObjectMapper.CANNOT_SET_PROPERTY )) } this.map.mapObject(e, item, treeData, varName); Assert.assert(!isRef); // isRef must ensure that treeData is null, and that we may only add } else if (collectionData) { Assert.assert(!treeCollection || (treeCollection == collectionData) ); this.addCollectionData(e, collectionData, item); //collectionData.push(item); //map.mapCollectionObject(e, collectionData, collectionData.length -1, item); } else { //console.log(' data ... ' + varName ); //console.log(' item: ' + item.toString()); if (isRef) { varName = treeNode.varStr; // FIX_THIS - breaks /w etc } try { data[varName] = item; } catch (error) { msg = "Cannot set property \"" + varName + "\": " + error.message; console.log(msg + "\n" + G.getStackTrace(error)); this.map.logValidationError(new PValidationError(PValidationError.OBJECT_MAPPING, e, msg, ObjectMapper.CANNOT_SET_PROPERTY )) } this.map.mapObject(e, item, data, varName); } } private allowMany(item:PElementResult):boolean { return item.element.allowMany || item.element.lineAllowMany; } /** * Iterate over a collection result, ie [...]* * * @result parent rule * @item element result /w multiple children * @childItems The array object into which child result are pushed * @childCollection c.f. parent array, ie ^(vars)* (morevars)* * */ private extractToCollection(result:PRuleResult, item:PElementResult, childItems:Array, childCollection:Array = null):void { // 1. when we've got child items (* or +), iterate over child items var childMatch:PElementResult if (item.children) { for (childMatch of item.children) { var value:any; if (childMatch.childResult) { //2. (var)[...]* childRule Present if (item.element.filterRawText) { // | raw-text filter ==> we don't have an object, just the text value = this.rawContent(result, childMatch); if (childItems) { this.addCollectionData(childMatch, childItems, value); } else { this.addCollectionData(childMatch, childCollection, value); } } else { var childObject:any = null; if (childCollection == null) { // only pre-create if we have explicitly need to, ie // (vars):typed-rule* --> create a child object // (vars)[ [^(objects) (var1)] | ^^:typed-rule2 ] --> let the child create the object childObject = this.createDataObject(childMatch.element.childRule, false); } this.mapValues(childMatch.childResult, childMatch.element.childRule, childObject, childCollection); if (childCollection == null) { this.addCollectionData(childMatch, childItems,childMatch.childResult.data); } } } else { // 3. simple string, no child rule ie (simple_string_list)* value = childMatch.valid ? childMatch.varStr : null; this.addCollectionData( childMatch, childItems, value); } } } } private addCollectionData(childElement:PElementResult, childItems:Array, value:any):void { childItems.push(value); this.map.mapCollectionObject(childElement, childItems, childItems.length -1, value);; } /** * * @param force Create a simple Object even if we don't have a registered class type */ private createDataObject(r:GRule, force:boolean = true, treeNode:PElementResult = null, parentRule:GRule = null):Object { var o:Object = null; try { if (treeNode && parentRule.getPropertyDataType(treeNode.element.varName)) { var cls:any = parentRule.getPropertyDataType(treeNode.element.varName) return this.createType(parentRule,cls); } else if (r && r.dataType) { o = this.createType(r, r.dataType); } else if (force) { o = {} } } catch (e) { console.log(e.stack) console.log("error" + G.getStackTrace(e)); // TODO - validation error Assert.fail(); } return o; } // createDataObject private createType(r:GRule, type:MTypeDef):any { // NOTE - this effectively disables the mechanims of adding the manifest on the grammar if (SimpleTypeManifest.jsonTarget) { return SimpleTypeManifest.createJSONCls(type.typeName) } else { return G.createType(type) } } private rawContent(item:PRuleResult, element:PElementResult):string { if (item.source && element.contentIndex >=0 && element.contentEndIndex >= 0) { return item.source.slice(element.contentIndex, element.contentEndIndex + 1); } return null; } // rawContent incrementalMap(out:PRuleResult):any { Assert.assert(out.map == null); var collection:Array; var oldLineModifier:string var tmp:PElementResult // yet another hack. If this is a line Rule result, it's in a pseudo line rule, and the 0th Result is actually actuall a PElementResult int he children of if (out.results && out.results.length > 0) { tmp = out.results[0] as PElementResult; if (tmp.element.lineAllowMany) { oldLineModifier = tmp.element.lineModifier; tmp.element.lineModifier = null; } } collection = new Array; this.map = new PObjectMap; // FIX_THIS - dreadful hack, but we do need a mechanism t var ret:any = this.mapValues(out, out.matchedRule, null, collection); // , collection); if ( !(out.isValid()) ) { console.log( out.map.toErrString() ); Assert.assert( out.isValid() ); } this.map = null; if (tmp && oldLineModifier != null) { tmp.element.lineModifier = oldLineModifier; } //out.map = null; is there any read return collection[0]; } // incrementalMap } // class