UNPKG

antlr-ng

Version:

Next generation ANTLR Tool

207 lines (206 loc) 6.98 kB
var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); import { GrammarTreeVisitor } from "../../tree/walkers/GrammarTreeVisitor.js"; import { ANTLRv4Parser } from "../../generated/ANTLRv4Parser.js"; import { FrequencySet } from "../../misc/FrequencySet.js"; class ElementFrequenciesVisitor extends GrammarTreeVisitor { static { __name(this, "ElementFrequenciesVisitor"); } /** * This special value means "no set", and is used by {@link minFrequencies} to ensure that {@link combineMin} * doesn't merge an empty set (all zeros) with the results of the first alternative. */ static sentinel = new FrequencySet(); frequencies = [new FrequencySet()]; minFrequencies = [ElementFrequenciesVisitor.sentinel]; /** * Generate a frequency set as the union of two input sets. If an element is contained in both sets, the value * for the output will be the maximum of the two input values. * * @param a The first set. * @param b The second set. * * @returns The union of the two sets, with the maximum value chosen whenever both sets contain the same key. */ static combineMax(a, b) { const result = ElementFrequenciesVisitor.combineAndClip(a, b, 1); for (const [key, value] of a.entries()) { result.set(key, value); } for (const [key, value] of b.entries()) { const slot = result.get(key); result.set(key, slot === void 0 ? value : Math.max(slot, value)); } return result; } /** * Generate a frequency set as the union of two input sets. If an element is contained in both sets, the value * for the output will be the minimum of the two input values. * * @param a The first set. * @param b The second set. If this set is {@link sentinel}, it is treated as though no second set were provided. * * @returns The union of the two sets, with the minimum value chosen whenever both sets contain the same key. */ static combineMin(a, b) { if (b === ElementFrequenciesVisitor.sentinel) { return a; } const result = ElementFrequenciesVisitor.combineAndClip(a, b, Number.MAX_VALUE); for (const [key] of result.entries()) { result.set(key, Math.min(a.count(key), b.count(key))); } return result; } /** * Generate a frequency set as the union of two input sets, with the values clipped to a specified maximum value. * If an element is contained in both sets, the value for the output, prior to clipping, will be the sum of the * two input values. * * @param a The first set. * @param b The second set. * @param clip The maximum value to allow for any output. * * @returns The sum of the two sets, with the individual elements clipped to the maximum value given by `clip`. */ static combineAndClip(a, b, clip) { const result = new FrequencySet(); for (const [key, value] of a.entries()) { for (let i = 0; i < value; i++) { result.add(key); } } for (const [key, value] of b.entries()) { for (let i = 0; i < value; i++) { result.add(key); } } for (const [key, value] of result.entries()) { result.set(key, Math.min(value, clip)); } return result; } tokenRef(ref) { this.frequencies[0].add(ref.getText()); this.minFrequencies[0].add(ref.getText()); } ruleRef(ref, arg) { this.frequencies[0].add(ref.getText()); this.minFrequencies[0].add(ref.getText()); } stringRef(ref) { const tokenName = ref.g.getTokenName(ref.getText()); if (tokenName !== null && !tokenName.startsWith("T__")) { this.frequencies[0].add(tokenName); this.minFrequencies[0].add(tokenName); } } enterAlternative() { this.frequencies.unshift(new FrequencySet()); this.minFrequencies.unshift(new FrequencySet()); } exitAlternative() { this.frequencies.unshift(ElementFrequenciesVisitor.combineMax( this.frequencies.shift(), this.frequencies.shift() )); this.minFrequencies.unshift(ElementFrequenciesVisitor.combineMin( this.minFrequencies.shift(), this.minFrequencies.shift() )); } enterElement() { this.frequencies.unshift(new FrequencySet()); this.minFrequencies.unshift(new FrequencySet()); } exitElement() { this.frequencies.unshift(ElementFrequenciesVisitor.combineAndClip( this.frequencies.shift(), this.frequencies.shift(), 2 )); this.minFrequencies.unshift(ElementFrequenciesVisitor.combineAndClip( this.minFrequencies.shift(), this.minFrequencies.shift(), 2 )); } enterBlockSet() { this.frequencies.unshift(new FrequencySet()); this.minFrequencies.unshift(new FrequencySet()); } exitBlockSet() { for (const key of this.frequencies[0].keys()) { this.frequencies[0].set(key, 1); } if (this.minFrequencies[0].size > 1) { this.minFrequencies[0].clear(); } this.frequencies.unshift(ElementFrequenciesVisitor.combineAndClip( this.frequencies.shift(), this.frequencies.shift(), 2 )); this.minFrequencies.unshift(ElementFrequenciesVisitor.combineAndClip( this.minFrequencies.shift(), this.minFrequencies.shift(), 2 )); } exitSubrule(tree) { if (tree.getType() === ANTLRv4Parser.CLOSURE || tree.getType() === ANTLRv4Parser.POSITIVE_CLOSURE) { const set = this.frequencies[0]; for (const key of set.keys()) { set.set(key, 2); } } if (tree.getType() === ANTLRv4Parser.CLOSURE || tree.getType() === ANTLRv4Parser.OPTIONAL) { this.minFrequencies[0].clear(); } } enterLexerAlternative() { this.frequencies.unshift(new FrequencySet()); this.minFrequencies.unshift(new FrequencySet()); } exitLexerAlternative() { this.frequencies.unshift(ElementFrequenciesVisitor.combineMax( this.frequencies.shift(), this.frequencies.pop() )); this.minFrequencies.unshift(ElementFrequenciesVisitor.combineMin( this.minFrequencies.shift(), this.minFrequencies.pop() )); } enterLexerElement() { this.frequencies.unshift(new FrequencySet()); this.minFrequencies.unshift(new FrequencySet()); } exitLexerElement() { this.frequencies.unshift(ElementFrequenciesVisitor.combineAndClip( this.frequencies.shift(), this.frequencies.shift(), 2 )); this.minFrequencies.unshift(ElementFrequenciesVisitor.combineAndClip( this.minFrequencies.shift(), this.minFrequencies.shift(), 2 )); } exitLexerSubrule(tree) { if (tree.getType() === ANTLRv4Parser.CLOSURE || tree.getType() === ANTLRv4Parser.POSITIVE_CLOSURE) { const set = this.frequencies[0]; for (const key of set.keys()) { set.set(key, 2); } } if (tree.getType() === ANTLRv4Parser.CLOSURE) { this.minFrequencies[0].clear(); } } } export { ElementFrequenciesVisitor };