import GRule, { GElement } from "./GRule"; import Assert from "@cafetextual/util/dist/src/assert/Assert"; export default class RuleSuffixContextCache { private _cache:{[index:string]:Array>} = {}; // innermost would better be typed as //type ruleCache:[Array, Array, boolean, string] private _names:Array = []; show():string { var out:string = ""; out += "-------- rules in grammar context ----------- \n" var ruleName:string for (ruleName of this._names) { out+= "--" + ruleName + ":\n" var items:Array> = this._cache[ruleName]; var item:Array for (item of items) { var itemElementTokens:Array = item[0]; var itemSuffixTokens:Array = item[1]; out += " tokens: [" + this.toTokenString(itemElementTokens) + "] suffix: [ " + this.toTokenString(itemSuffixTokens) + " ]\n" } } return out; } static tokenEq(v1:Array, v2:Array):Boolean { if (v1 == v2) { return true; } if (!v1 || !v2) { return false; } if (v1.length != v2.length) { return false; } for (var i:number = 0; i < v1.length; i++) { if (v1[i] != v2[i]) { return false; } } return true; } toTokenString(tokens:Array):string { if (!tokens) { return ""; } return tokens.join(",") } dispose():void { this._cache = null; } cacheRule(rule:GRule, elementTokens:Array, suffixTokens:Array, isSkip:Boolean):void { Assert.assert(rule.name != null); var previouslyCached:GRule = this.getCachedChildRule(rule.name, elementTokens, suffixTokens, isSkip); if (previouslyCached) { Assert.fail(); // should only cache intentionally } if (this._cache.hasOwnProperty(rule.name)) { var items:Array> = this._cache[rule.name]; } else { items = []; this._names.push(rule.name); this._cache[rule.name] = items; } items.push([this.cloneTokens(elementTokens), this.cloneTokens(suffixTokens), isSkip, rule]); } private cloneTokens(v:Array):Array { if (v) { var out:Array = v.concat(); return out; } return null; } // cloneTokens getCachedChildRule(name:string, elementTokens:Array, suffixTokens:Array, isSkip:Boolean):GRule { var cachedByRuleName:Array> = this._cache[name]; if (cachedByRuleName) { var item:Array; for (item of cachedByRuleName) { var itemElementTokens:Array = item[0]; var itemSuffixTokens:Array = item[1]; var itemIsSkip:Boolean = item[2]; var itemRule:GRule = item[3]; if ( RuleSuffixContextCache.tokenEq(elementTokens, itemElementTokens) && RuleSuffixContextCache.tokenEq(suffixTokens, itemSuffixTokens) ) { return itemRule; } } } return null; } // getCached /** * A temporary hack - only require context if we have a `(var) construct. * * note that this is a rough and ready hack, a stronger test could be written based on further consideration of context of element * */ private requiresSuffixContext(r:GRule):Boolean { if (!r.name) { return false; } if (r.name.indexOf("_") == 0) { return false; // FIX_THIS - an egregious hack } var e:GElement for (e of r.elements) { if (e.skipToToken) { return true; } else if (!e.ruleIsReference && e.childRule) { if (this.requiresSuffixContext(e.childRule)) { return true; } } } return false; } } // class