Skip to content

Commit

Permalink
Turn on type checking for merge-character-classes, and use ellipsis t…
Browse files Browse the repository at this point in the history
…o clone
  • Loading branch information
markw65 committed Aug 29, 2023
1 parent cbc8f8d commit 88df0a4
Showing 1 changed file with 45 additions and 18 deletions.
63 changes: 45 additions & 18 deletions lib/compiler/passes/merge-character-classes.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
// @ts-check
"use strict";

/**
* @typedef {import("../../peg")} PEG
*/

/** @type {PEG.compiler.visitor} */
const visitor = require("../visitor");

/**
* @param {unknown} target
* @param {unknown} source
*/
function cloneOver(target, source) {
Object.keys(target).forEach(key => delete target[key]);
Object.keys(source).forEach(key => { target[key] = source[key]; });
const t = /** @type {Record<string,unknown>} */ (target);
const s = /** @type {Record<string,unknown>} */ (source);
Object.keys(t).forEach(key => delete t[key]);
Object.keys(s).forEach(key => { t[key] = s[key]; });
}

// Clean up the parts array of a `class` node, by sorting,
// then removing "contained" ranges, and merging overlapping
// or adjacent ranges.
/**
* Clean up the parts array of a `class` node, by sorting,
* then removing "contained" ranges, and merging overlapping
* or adjacent ranges.
*
* @param {PEG.ast.CharacterClass["parts"]} parts
*/
function cleanParts(parts) {
// Sort parts on increasing start, and then decreasing end.
parts.sort((a, b) => {
Expand Down Expand Up @@ -48,28 +64,36 @@ function cleanParts(parts) {
return parts;
}

// Merges a choice character classes into a character class
/**
* Merges a choice character classes into a character class
* @param {PEG.ast.Grammar} ast
*/
function mergeCharacterClasses(ast) {
// Build a map from rule names to rules for quick lookup of
// ref_fules.
// ref_rules.
const rules = Object.create(null);
ast.rules.forEach(rule => (rules[rule.name] = rule.expression));
// Keep a map of which rules have been processed, so that when
// we find a ref_rule, we can make sure its processed, before we
// try to use it.
const processedRules = Object.create(null);
const [asClass, merge] = [
/**
* Determine whether a node can be represented as a simple character class,
* and return that class if so.
*
* @param {PEG.ast.Expression} node - the node to inspect
* @param {boolean} [clone] - if true, always return a new node that
* can be modified by the caller
* @returns {PEG.ast.CharacterClass | null}
*/
(node, clone) => {
if (node.type === "class" && !node.inverted) {
return clone
? {
type: node.type,
parts: node.parts.slice(),
inverted: node.inverted,
ignoreCase: node.ignoreCase,
location: node.location,
}
: node;
if (clone) {
node = { ...node };
node.parts = [...node.parts];
}
return node;
}
if (node.type === "literal" && node.value.length === 1) {
return {
Expand Down Expand Up @@ -98,6 +122,7 @@ function mergeCharacterClasses(ast) {
},
visitor.build({
choice(node) {
/** @type {PEG.ast.CharacterClass | null} */
let prev = null;
let changed = false;
node.alternatives.forEach((alt, i) => {
Expand All @@ -109,7 +134,7 @@ function mergeCharacterClasses(ast) {
}
if (prev && prev.ignoreCase === cls.ignoreCase) {
prev.parts.push(...cls.parts);
node.alternatives[i - 1] = null;
node.alternatives[i - 1] = prev;
node.alternatives[i] = prev;
prev.location = {
source: prev.location.source,
Expand All @@ -122,7 +147,9 @@ function mergeCharacterClasses(ast) {
}
});
if (changed) {
node.alternatives = node.alternatives.filter(alt => alt !== null);
node.alternatives = node.alternatives.filter(
(alt, i, arr) => !i || alt !== arr[i - 1]
);
node.alternatives.forEach((alt, i) => {
if (alt.type === "class") {
alt.parts = cleanParts(alt.parts);
Expand Down

0 comments on commit 88df0a4

Please sign in to comment.