import { MTypeDef } from '@cafetextual/util/dist/src/manifest/MTypeDef'; import GTypeDescriptor from "./GTypeDescriptor"; import SeepParserDefs from "../SeepParserDefs"; import IGrammarProcessor from "../postprocess/IGrammarProcessor"; import { Hash, UIDBase, ITypeManifest } from "@cafetextual/util"; import Assert from "@cafetextual/util/dist/src/assert/Assert"; import G from "@cafetextual/util/dist/src/type/G"; import GRuleToken from "./GRuleToken"; import ISeepGrammar from "../ISeepGrammar"; /** * Abstract rule definitions that generate grammar rules indirectly. */ export interface IGRuleGenerator { name:string createGenerated(tokens:Hash):GRule; } // interface /** * Defines an import statement */ export class GImport { constructor(r:GRule = null) { this.grule = r; } static ALL:string = "*" grammar:ISeepGrammar; suri:string; ruleNames:Array grule:GRule; // actual rule rule:string; asRule:string; targetSwitchName:string; targetSwitchValue:string; multiplicity:string; isline:boolean = false; lineIndent:boolean = false; asTypeNode:boolean = false; /** * factory method * * @param g grammar * @param rule name of rule to import * @param asRule as rule as opposed to as opposed to full grammar */ static create(g:ISeepGrammar, rule:string, asRule:string = null):GImport { var o:GImport = new GImport; o.grammar = g; o.rule = rule; o.asRule = asRule; return o; } static createSwitch(target:string, value:string, rule:GRule, asTypeNode:boolean = false, lineIndent:boolean = false):GImport { var imp:GImport = new GImport; imp.grule = rule; imp.targetSwitchName = target; imp.targetSwitchValue = value; imp.asTypeNode = asTypeNode; imp.lineIndent = lineIndent; return imp; } } // class /* import com.cafetextual.util.UIDBase; import org.subalternproductions.seepResource.dsl.manifest.ITypeManifest; import org.subalternproductions.seepResource.dsl.parser.metaparser.GImport; import org.subalternproductions.seepResource.dsl.parser.metaparser.IGRuleGenerator; import org.subalternproductions.seepResource.dsl.parser.postprocess.IGrammarProcessor; import org.subalternproductions.util.assert.Assert; */ /** * Describes a single parser rule (ie "line: PAREN(var_name)") of a SeepGrammar */ export default class GRule extends UIDBase { constructor() { super(); } isDefault:boolean = false; // the context of a parent suffin which this rule has been cloned suffixContext:Array; /** * Generated grammar elements must reference that actual non-generated object from which they are generated */ derivedSourceObject:Object; generatedFrom:IGRuleGenerator; clonedFrom:GRule; importDef:GImport; name:string = null; typeName:string = null; /** * child line rules may optionally have names. This is referenable by stylesheets only. */ lineRuleName:string = null; private _dataType:MTypeDef = null; elements:Array; comment:string = null; ruleString:string = null; isOR:boolean = false; /** * For a named rule, StyleID is unique to a grammar. */ styleID:number = -1; /** * Index of this rule */ // [Transient] index:Number; private _lineRules:Array; /** * Elements containing rules for indended lines */ get lineRules():Array { return this._lineRules ? this._lineRules : this.clonedFrom ? this.clonedFrom.lineRules : null; } set lineRules(v:Array) { this._lineRules = v; } // lineRules typeDefs:GTypeDescriptor; isValid():boolean { if (!this.elements || this.elements.length == 0) { return false; } var element:GElement for (element of this.elements) { if (element.hasError() || (element.childRule && (!element.ruleIsReference) && !(element.childRule.isValid()))) { return false; } if (this.lineRules) { var lr:GRule for (lr of this.lineRules) { if (this.clonedFrom == null && !lr.isValid()) { return false; } } } } return true; } // isValid /** * true when we have a "<--" no-indent construct */ lineNoIndent:boolean = false; /** * indicates that an element container a cutpoint (non recursive) * * a simple caching, optimisation only, does not refer to child */ hasCutpoint:boolean; /** * sets presfix and suffix tokens * */ setTokens(prefixTokens:any, suffixTokens:any, filters:any, contentTokens:any):void { // {, parentSuffixTokens:Array, parentPrefixTokens:Array):void { // 1. for each element set the actual values of tokens var e:GElement = null; for (e of this.elements) { // set tokens and token values if (contentTokens && contentTokens.hasOwnProperty(e.tokenName)) { e.contentTokens = contentTokens[e.tokenName]; } if (e.tokenName != null) { if (!prefixTokens.hasOwnProperty(e.tokenName)) { Assert.assert(!suffixTokens.hasOwnProperty(e.tokenName)); // this error should be caught elsewhere if (!contentTokens.hasOwnProperty(e.tokenName) && !filters.hasOwnProperty(e.tokenName) && !contentTokens.hasOwnProperty(e.tokenName) ) { e.unknownToken = true; e.addError(GElement.ERROR_UNKNOWN_TOKEN, "tokenName = \"" + e.tokenName + "\""); // Assert.fail(' unknown token ' + e.tokenName); // todo - error handling earlier that this break; } } e.prefixTokenValue = prefixTokens[e.tokenName]; e.suffixTokenValue = suffixTokens.hasOwnProperty(e.tokenName) ? suffixTokens[e.tokenName] : null; e.filterType = filters.hasOwnProperty(e.tokenName) ? filters[e.tokenName] : null; } // recurse on child rules. At the point at which this is called, only annmyous and cloned rules will have been added. if (e.childRule) { e.childRule.setTokens(prefixTokens, suffixTokens, filters, contentTokens) } } // iteration over elements } lineChild(index:number):GRule { let { lineRules } = this; return (lineRules && (lineRules.length > index)) ? G.aas(lineRules[index], GRule) : null; } // lineChile /** * returns element, child/ grandchild elements - for debug */ child(elementIndex:number, childElementIndex:number = -1, grandChildIndex:number = -1, greatGrandChildIndex:number = -1):GElement { let {elements} = this; if (elementIndex < elements.length) { var e:GElement = elements[elementIndex] if (childElementIndex >= 0) { var child:GElement = e.childRule && (childElementIndex < e.childRule.elements.length) ? e.childRule.elements[childElementIndex] : null if (grandChildIndex >= 0) { var grandChild:GElement = (child && child.childRule && child.childRule.elements.length > grandChildIndex) ? child.childRule.elements[grandChildIndex] :null if (greatGrandChildIndex >= 0) { return (grandChild && grandChild.childRule && grandChild.childRule.elements.length > greatGrandChildIndex) ? grandChild.childRule.elements[greatGrandChildIndex] :null } return grandChild; } return child; } return e; } return null } private _elementTypes:{[index:string]:string} = {}; private _elementProcessors:{[index:string]:IGrammarProcessor} = {}; setPropertyDataType(name:string, type:any, processor:IGrammarProcessor = null):void { if (type) { this._elementTypes[name] = type; } if (processor) { this._elementProcessors[name] = processor; } } // setPropertyDataType getTypeProcessor(name:string):IGrammarProcessor { let {clonedFrom, _elementProcessors } = this; return clonedFrom ? clonedFrom.getTypeProcessor(name) :_elementProcessors[name] as IGrammarProcessor } getPropertyDataType(name:string):string { let {clonedFrom, _elementTypes } = this; return clonedFrom ? clonedFrom.getPropertyDataType(name) : (name ? _elementTypes[name] : null); } isIndeterminate():boolean { let {elements} = this; for (var i:number = (elements.length)-1; i >= 0; i--) { var e:GElement = elements[i]; if (e.isIndeterminate()) { // last element is inteterminate, therefore rule is indeterminate return true; } if (!(e.allowNone)) { // element is determinate and not optional return false; } // element is determinate but optional - need to keep iterating } return true; } // isIndeterminate /** * aggregated next Element tokens for the rule as a whole */ ruleNextElementTokens:Array; /** * Used for an OR rule, all possible child tokens. * *

Might reasonably be extended as an optimisation mechanism that the parser can use to determine if a next token is found within * a child rule.

*/ ruleORChildTokens:Array; getNextRuleTokens( fromIndex:number, v:Array):Array { var baseElement:GElement = fromIndex >= 0 ? this.elements[fromIndex] : null; if (baseElement.prefixTokenValue != null) { return null; } v = v ? v : new Array(); if (baseElement.childRule) { // recurse } for (var i:number = (fromIndex + 1); i < this.elements.length; i++) { var e:GElement = this.elements[i]; if (e.prefixTokenValue) { v.push(e.prefixTokenValue); } else if (e.childRule) { var childTokens:Array = e.nextElementTokens = childTokens; } if (!(e.allowNone)) { // only continue pase return v.length > 0 ? v : null; } } return v.length> 0 ? v: null; } // getNextRuleTokens get lineAllowNone():boolean { var e:GElement = this.elements[0]; // TODO - cache return e.lineAllowNone; } get lineAllowMany():boolean { var e:GElement = this.elements[0]; return e.lineAllowMany; } // no dependancy on this instance of rule, algotithm to be encapsulated in a seperate class private appendElementsForRule(r:GRule, possibleNextTokens:Array):void { } /** * Recursively populate tokens */ private appendElementsByToken(possibleNextTokens:Array, element:GElement):boolean { return false; } get dataType():MTypeDef { return this._dataType ? this._dataType : this.clonedFrom ? this.clonedFrom.dataType : null; } private _manifest:ITypeManifest set manifest(v:ITypeManifest) { if (this.clonedFrom) { this.clonedFrom.manifest = v; } else { this._manifest = v; } } get manifest():ITypeManifest { return this.clonedFrom ? this.clonedFrom.manifest : this._manifest } setDataType(v:MTypeDef):void { this._dataType = v; } toString():string { var out:string = ""; if (this.name) out += this.name + ": "; var e:GElement for (e of this.elements) { if (e.childRule && !e.anonmyousChildRule) { out += e.toString(); } else if (e.childRule) { out += "child rule " + e.childRule.name; } } return out; } // toString /** * clone this rule. * *

Assumes that the datastructure marks recursive rules with ruleIsRecursive * * * @param cloneNamedChildRules false only when building the rule graph, otherwise set this true to propery clone the rule * */ clone(depth:number = 0):GRule { if (this.name) { //console.log(' cloning rule ' + this.name + ' depth = ' + depth ); } if (depth > 200) { console.log(' too deep'); } var out:GRule = new GRule; out.name = this.name; out.isOR = this.isOR; out.setDataType(this._dataType); // this would allow one to set a new data type on a cloned rule. (as opposed to just looking it up on clonedFrom) out.clonedFrom = this; out.importDef = null // import def is *not* cloned . It only applies to the item that is origionally cloned out.styleID = this.styleID; out.lineRuleName = this.lineRuleName; // by reference out.typeDefs = this.typeDefs; out.hasCutpoint = this.hasCutpoint; //out.comment = this.comment; out.ruleString = this.ruleString; out.elements = []; // new Vector.; // clone elements for (var i:number = 0; i < this.elements.length; i++) { var e:GElement = this.elements[i].clone(depth + 1); out.elements.push(e); } // clone child line rules /* line rules are referenced, not cloned if (this._lineRules) { out._lineRules = []; for each (var lineRule:PRule in lineRules) { out.lineRules.push(lineRule.clone()); } } */ return out; } // clone tr(firstItemOnly:boolean = false):string { var out:string = "["; if (firstItemOnly) { var e0:GElement = this.elements[0]; out += e0.tr(); out += (this.elements.length > 1) ? "...]" : "]"; return out; } var element:GElement for (element of this.elements) { out = out + element.tr() + " "; } if (this.lineRules && this.lineRules.length > 0) { var lr:GRule for (lr of this.lineRules) { out += lr.lineNoIndent ? " <--" : " -->"; out += lr.child(0).tr(); } } out = out + "]"; return out; } // tr } // ----------------------------------------------------------- /** * An parse element of a PRule */ export class GElement extends UIDBase { constructor() { super() } /** * secondary rule -> second pass parsing * * Mechanism to * a) preserve source directly in the ast * b) decouple "container" grammar from "content" grammar * c) simplify incremental updates. * * s * ie. * * rule: X ... * -->*, (source) ==> (parsedSource):rule2 * * */ secondaryRule:GRule; /** /** * This element is parsed as a line rule. */ lineRuleName:string; /** * styleID relates to mechanism for efficiently applying styles. *

For an element, it refers to the id of the token.

*/ styleID:number = -1; /** * Error that occurs when an element is indeterminate because the previous item is optional and lacks a token */ static ERROR_INDETERMINATE_ELEMENT:number = 1; /** * Type Name (if this object is a treeNode) */ typeName:string = null; /** * Error that occurs when an element is indeterminate owing to values of previous tokens * *

ie. TOKEN1(var) TOKEN2(var)
* if the value of TOKEN1 and TOKEN2 are the same, then */ static ERROR_INDETERMINATE_TOKEN:number = 2; /** * Generated grammar elements must reference that actual non-generated object from which they are generated */ derivedSourceObject:Object; /** * FIX_THIS - wrong kind of error */ static ERROR_EXPECTING_CHAR:number = 3; /** * nested elements require WS or suffix token for simplicity */ static ERROR_UNSUPPORTED_LL_INDETERMINACY :number = 4; // WS or suffix token static ERROR_UNKNOWN_CHILD_RULE:number = 5; static ERROR_UNKNOWN_TOKEN:number = 6; static ERROR_VAR_NAME_REQUIRED_FOR_COLLECTIONS:number = 7; static ERROR_INVALID_RULE_RECURSION:number = 8; // cannot reference a previous rule w/o suffix & prefix static ERROR_TREE_NODE_WITH_COLLECTION:number = 9; static ERROR_INCONSISTENT_ORS:number = 10; static ERROR_OPTION_ELEMENT_WITH_OR:number = 11; static ERROR_UNSUPPORTED_RULE_RECURSION:number = 12; // cannot reference a previous rule w/o suffix & prefix static ERROR_OR_WITH_MULTIPLICITY:number = 13; static ERROR_TYPE_NODE_WITH_COLLECTION:number = 14 static ERROR_SUFFIX_WITHOUT_PREFIX:number = 15; static ERROR_INVALID_REFERENCE_VAR:number = 16; // -- node token related errors --- static ERROR_NODE_TOKEN_REQUIRES_PARENT_TREE_NODE:number = 17; static ERROR_NODE_TOKEN_MUST_BE_NAMED:number = 18; static ERROR_MAX_RULE_DEPTH_EXCEEDED:number = 19; static ERROR_ONLY_TREE_NODES_MAY_BE_TYPED:number = 20; static ERROR_UNKNOWN_TYPE:number = 21; static ERROR_ELEMENT_HAS_SUFFIX_WITH_LINE_RULES:number = 22; static ERROR_ELEMENT_WITH_CHILD_LINE_RULES_MUST_BE_LAST:number = 23; static ERROR_ELEMENT_TYPE_OVERWRITE:number = 24; static ERROR_BAD_SKIP_TOKEN:number = 25; static ERROR_OR_WITH_WS:number = 26; static ERROR_CONTENT_TOKENS_WITH_CHILD_RULE:number = 27; errors:Object private _errorIndex:Array; private errorIndexToString(i:number):string { if (!this._errorIndex) { this._errorIndex = []; var e = this._errorIndex; e[GElement.ERROR_INDETERMINATE_ELEMENT] = "indeterminate element"; e[GElement.ERROR_INDETERMINATE_TOKEN] = "indeterminate token"; e[GElement.ERROR_EXPECTING_CHAR] = "expecting character"; e[GElement.ERROR_UNSUPPORTED_LL_INDETERMINACY] = "unsupported LL indeterminacy"; e[GElement.ERROR_UNKNOWN_CHILD_RULE] = "unknown child rule"; e[GElement.ERROR_UNKNOWN_TOKEN] = "unknown token"; e[GElement.ERROR_VAR_NAME_REQUIRED_FOR_COLLECTIONS] = "collections require name"; e[GElement.ERROR_INVALID_RULE_RECURSION] = "rule may not be used in this kind of LL recursion - rule requires a determinate element "; e[GElement.ERROR_TREE_NODE_WITH_COLLECTION] = "tree node with collection"; e[GElement.ERROR_INCONSISTENT_ORS] = "inconsistent OR (|) tokens (ie all element or no elements should be ORed)"; e[GElement.ERROR_OPTION_ELEMENT_WITH_OR] = "ORed elements may not be optional"; e[GElement.ERROR_UNSUPPORTED_RULE_RECURSION] = "this kind of recursion not yet supported"; e[GElement.ERROR_SUFFIX_WITHOUT_PREFIX] = "a token may not define a suffix value w/o a prefix"; e[GElement.ERROR_OR_WITH_MULTIPLICITY] = "cannont specify multiplicity with an OR" // could combine /w ERROR_OPTION_ELEMENT_WITH_OR e[GElement.ERROR_TYPE_NODE_WITH_COLLECTION] = "cannot have a type node /w a collection: ^^(var)*"; e[GElement.ERROR_SUFFIX_WITHOUT_PREFIX] = "a token may not define a suffix value w/o a prefix"; e[GElement.ERROR_INVALID_REFERENCE_VAR] = "referece name value requires a leaf tree node, cannot be a type/host node - ie ^(%varName)" e[GElement.ERROR_NODE_TOKEN_REQUIRES_PARENT_TREE_NODE] = "a node token requires a parent tree node"; e[GElement.ERROR_NODE_TOKEN_MUST_BE_NAMED] = "the parent tree node of an object currently requires a name (even a temprary one line ^^^(_node))"; e[GElement.ERROR_MAX_RULE_DEPTH_EXCEEDED] = "rule depth is to great"; e[GElement.ERROR_ONLY_TREE_NODES_MAY_BE_TYPED] = "only treeNodes may be typed ie ^(myVar:MyType) or ^^^(_myProperty:MyType)" e[GElement.ERROR_UNKNOWN_TYPE] = "unknown type"; e[GElement.ERROR_ELEMENT_HAS_SUFFIX_WITH_LINE_RULES] = "if an element's child rule has line rules, cannot have a suffix"; e[GElement.ERROR_ELEMENT_WITH_CHILD_LINE_RULES_MUST_BE_LAST] = "If an element has line rule, it must be the last element in the rule (or in an or)" e[GElement.ERROR_ELEMENT_TYPE_OVERWRITE] = "overwriting element type (probably duplicate name, ie ^^^(_:Op) ... ^^^(_:App)" e[GElement.ERROR_BAD_SKIP_TOKEN] = "`(var) is not currently supported in an OR construct"; } // errorIndexString return this._errorIndex[i]; } childRuleName:string; tokenName:string; tokenRef:GRuleToken; asTree:boolean = false; // ^ construcrt asTreeType:boolean = false; // ^^ construct TODO --> rename as hoist node, or typeNode varName:string /** * */ nameIsRef:boolean = false; childRuleStr:string; isOR:boolean = false; /** * FIX_THIS - the parser incorrectly sets asTree true when asTreeType is what we actually want, * this method mitigates that */ asTreeNode():boolean { return this.asTree; // && ! asTreeType; } skipToToken:boolean = false; skip2:boolean = false; wsPolicy:string = null; // '.' , ',', '~' '$' multiplicity:string = null; // "+", "?", "*" /** * Modifier for a line element ie -->(var)? , vlaue is ? * + only */ lineModifier:string = null; get isCutpoint():boolean { return this.cutpointMsg != null; } cutpointMsg:string = null; anonmyousChildRule:boolean = false; // true when childRule Str is true comment:string = null; // indicies // --- set by a parent rule during pre-process -- prefixTokenValue:string = null; suffixTokenValue:string = null; contentTokens:Array = null; filterType:string; // ------------- unclonable elements ---- /** * Possible next tokens that can occur anywhere within an item * *

* ie. (var)? DASH(var2) --> " " DASH does not appear because "a-" will match (var)

* *

*/ possibleNextTokens:Array; /** * Differs from possibleNextTokens in that these are tokens, if found at the beginning of a var, skip to next rule. * *

* ie (var)?, DASH(var2) --> "-" "-x" with match var2, but "x-" with match var1.

* * WS prevents DASH at beginning of line being matched( ie "-var2-value", but not in the middle of var (ie "myText-withdashInIt") *

*/ nextElementTokens:Array; hasElementToken(v:string):boolean { return this.nextElementTokens && this.nextElementTokens.indexOf(v) >= 0; } /** * When a child rule appears in a OR construct contains tokens that would match * * ie [(var1?) X] | Y | Z * * the "[(var1) X]" element will have {Y, Z} nextORElementTokens * * * */ nextORElementTokens:Array; // validation state unknownToken:boolean = false; // ------------------------ /** * If this is the first element in a line rule, whether leading WS is permitted */ lineAllowWS:boolean = false; // derived rules, some required get allowNone():boolean { let {multiplicity} = this; return (multiplicity && (multiplicity.indexOf("?") >= 0 || multiplicity.indexOf("*") >= 0)); } get allowMany():boolean { let {multiplicity} = this; return multiplicity && (multiplicity.indexOf("*") >= 0 || multiplicity.indexOf("+") >= 0) } consumeAll():boolean { let {wsPolicy} = this; return (wsPolicy && wsPolicy.indexOf("$") >= 0); } /** * Allow WS, invoked by a "." modifier, and overriding implicit requireWS */ get allowWS():boolean { // also true if wsPolicy == ",", but no need to be explicit var allow:boolean = !this.wsPolicy // allowed by default || ((this.wsPolicy.indexOf('.') < 0) && !this.consumeAll()); // forbidden by "$" return allow; } get lineAllowNone():boolean { return (this.lineModifier == "?" || this.lineModifier == "*") } get lineAllowMany():boolean { return (this.lineModifier == "+" || this.lineModifier == "*") } explicitRequireWS:boolean = false; /** * Child rule. */ childRule:GRule; /** * Flag used to prevent recursion through refererenced rules */ ruleIsReference:boolean = false; /** * require ws (when grammar element is succeeded by whitespace and it's not explicitly forbidden by the "." modifier") */ get requireWS():boolean { let {wsPolicy, isOR, allowWS, explicitRequireWS} = this; var requireWSToken:boolean = (wsPolicy && wsPolicy.indexOf("~") >= 0); var optionalWS:boolean = (wsPolicy && wsPolicy.indexOf(",") >= 0); return !isOR && !optionalWS && !this.consumeAll() && allowWS && (explicitRequireWS || requireWSToken); } private _errors:Array; private _errorMessages:Array; setErrors(v:Array):void { this.errors = v; } toErrorString(indent:string):string { var out:string = ""; var i:number for (i of this._errors) { out += indent + this.errorIndexToString(i) + "\n" } return out; } addError(value:number, msg:string = ""):void { if (!this._errors) { this._errors = new Array(); } if (!this._errorMessages) { this._errorMessages = new Array() } this._errors.push(value); this._errorMessages.push(msg); } // addEror hasError(v:number = -1):boolean { let {_errors} = this; return (v < 0) ? (_errors && _errors.length > 0) : (_errors && _errors.indexOf(v) >= 0); } /** * True if the element does not require any further possible next tokens */ isIndeterminate():boolean { if (this.childRule) { return !( (this.suffixTokenValue || this.requireWS) || !this.childRule.isIndeterminate() ); } return !(this.suffixTokenValue || this.requireWS || (this.varName == null) || this.filterExists) } // isIndeterminate toString():string { var out:string = ""; if (this.asTree) out += "^"; if (this.asTreeType) out += "^" if (this.tokenName) out += this.tokenName; if (this.varName) out += "{" + this.varName + "}"; if (this.childRuleName) out += ":" + (this.childRuleName ? this.childRuleName : "anonomous child rule: " ); if (this.childRule && this.anonmyousChildRule) { out += '[' + this.childRuleStr + ']'; } if (this.suffixTokenValue) { out += this.suffixTokenValue; } if (this.multiplicity) out += this.multiplicity; if (this.wsPolicy) out += this.wsPolicy; if (this.requireWS) out += " "; return out; } // toString // temporary filter get filterExists():boolean { return (this.filterType == SeepParserDefs.EXISTS_FILTER); } get filterList():boolean { return this.filterType == SeepParserDefs.LIST_FILTER; } get filterRawText():boolean { return this.filterType == SeepParserDefs.RAW_TEXT; } /** * context in which recursive rules are managed */ stackContext:string = null; clonedFrom:GElement = null; private cloneErrors():Array { var out:Array = null; if (this._errors) { out = new Array(); var v:number for (v of this._errors) { out.push(v); } } return out; } elementNL:boolean = false; // found <-- elementNLIndent:boolean = false; // found --> elementPermitsNL():boolean { return this.elementNL || this.elementNLIndent; } clone( depth:number = 0):GElement { var out:GElement = new GElement; out.clonedFrom = this; // TODO - refactor most of this to a def reference out.typeName = out.typeName; out.childRuleName = this.childRuleName; out.lineRuleName = this.lineRuleName; out.tokenName = this.tokenName; out.tokenRef = this.tokenRef; out.varName = this.varName; out.skipToToken = this.skipToToken; out.skip2 = this.skip2; out.nameIsRef = this.nameIsRef; out.childRuleStr = this.childRuleStr; out.multiplicity = this.multiplicity; out.wsPolicy = this.wsPolicy; out.anonmyousChildRule = this.anonmyousChildRule; out.asTree = this.asTree; out.asTreeType = this.asTreeType; out.lineModifier = this.lineModifier; out.isOR = this.isOR; out.styleID = this.styleID; out.elementNL = this.elementNL; out.elementNLIndent = this.elementNLIndent; // clone child rule childRule if (this.childRule) { if (this.ruleIsReference) { out.ruleIsReference = true; out.childRule = this.childRule } else { out.childRule = this.childRule.clone( depth +1); } } // injected from parsing // another option would be to just grab these values from the cloned object via a getter out.prefixTokenValue = this.prefixTokenValue; out.suffixTokenValue = this.suffixTokenValue; out.filterType = this.filterType; out.contentTokens = this.contentTokens // shallow copy out.unknownToken = this.unknownToken; out.setErrors(this.cloneErrors()); //Assert.assert(possibleNextTokens == null); // only clone before, these are context dependent calculations //Assert.assert(nextElementTokens == null); // only clone before return out; } // clone /** * Constucts a string the traces the elment, iterating down through anonmyous nodes * * Helpful in debug when we need to identify an anonmyous element in a complex rule */ tr(childIndex:number = -1):string { var out:string = ""; if (this.skip2) out += "`" if (this.skipToToken) out += "`" if (childIndex >= 0) { return out + this.childRule.child(childIndex).tr(); } else { out = out + (this.tokenName ? this.tokenName : ""); out = out + (this.varName != null? ('(' + this.varName + ')') : "") if (this.childRuleName) { out = out + ":" + this.childRuleName } else if (this.childRule) { out += this.childRule.tr(true); } } return out; } // } // class export class GRuleMultiline extends GRule { constructor() { super(); this.isOR = true; } }