antlr-ng
Version:
Next generation ANTLR Tool
207 lines (206 loc) • 6.98 kB
JavaScript
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
};