'use strict;';
const errorName = 'apgex: replace(): ';
const synError = function synError(result, chars, phraseIndex, data) {
if (data.isMatch(result.state)) {
const value = data.charsToString(chars, phraseIndex, result.phraseLength);
data.items.push({ type: 'error', index: phraseIndex, length: result.phraseLength, error: value });
data.errors += 1;
data.count += 1;
}
};
const synEscape = function synExcape(result, chars, phraseIndex, data) {
if (data.isMatch(result.state)) {
data.items.push({ type: 'escape', index: phraseIndex, length: result.phraseLength });
data.escapes += 1;
data.count += 1;
}
};
const synMatch = function synMatch(result, chars, phraseIndex, data) {
if (data.isMatch(result.state)) {
data.items.push({ type: 'match', index: phraseIndex, length: result.phraseLength });
data.matches += 1;
data.count += 1;
}
};
const synPrefix = function synPrefix(result, chars, phraseIndex, data) {
if (data.isMatch(result.state)) {
data.items.push({ type: 'prefix', index: phraseIndex, length: result.phraseLength });
data.prefixes += 1;
data.count += 1;
}
};
const synSuffix = function synSuffix(result, chars, phraseIndex, data) {
if (data.isMatch(result.state)) {
data.items.push({ type: 'suffix', index: phraseIndex, length: result.phraseLength });
data.suffixes += 1;
data.count += 1;
}
};
const synXName = function synXName(result, chars, phraseIndex, data) {
if (data.isMatch(result.state)) {
data.items.push({ type: 'name', index: phraseIndex, length: result.phraseLength, name: data.name });
data.names += 1;
data.count += 1;
}
};
const synName = function synName(result, chars, phraseIndex, data) {
if (data.isMatch(result.state)) {
const nameStr = data.charsToString(chars, phraseIndex, result.phraseLength);
const nameChars = chars.slice(phraseIndex, phraseIndex + result.phraseLength);
data.name = { nameString: nameStr, nameChars };
}
};
module.exports = function parseReplacement(p, str) {
const grammar = new (require('./replace-grammar'))();
const apglib = require('../apg-lib/node-exports');
const parser = new apglib.parser();
const data = {
name: '',
count: 0,
errors: 0,
escapes: 0,
prefixes: 0,
matches: 0,
suffixes: 0,
names: 0,
isMatch: p.match,
charsToString: apglib.utils.charsToString,
items: [],
};
parser.callbacks.error = synError;
parser.callbacks.escape = synEscape;
parser.callbacks.prefix = synPrefix;
parser.callbacks.match = synMatch;
parser.callbacks.suffix = synSuffix;
parser.callbacks.xname = synXName;
parser.callbacks.name = synName;
const chars = apglib.utils.stringToChars(str);
const result = parser.parse(grammar, 0, chars, data);
if (!result.success) {
throw new Error(`${errorName}unexpected error parsing replacement string`);
}
const ret = data.items;
if (data.errors > 0) {
let msg = '[';
let i = 0;
let e = 0;
for (; i < data.items.length; i += 1) {
const item = data.items[i];
if (item.type === 'error') {
if (e > 0) {
msg += `, ${item.error}`;
} else {
msg += item.error;
}
e += 1;
}
}
msg += ']';
throw new Error(`${errorName}special character sequences ($...) errors: ${msg}`);
}
if (data.names > 0) {
const badNames = [];
let i = 0;
for (; i < data.items.length; i += 1) {
const item = data.items[i];
if (item.type === 'name') {
const name = item.name.nameString;
const lower = name.toLowerCase();
if (!p.parser.ast.callbacks[lower]) {
badNames.push(name);
}
item.name.nameString = lower;
}
}
if (badNames.length > 0) {
let msg = '[';
for (let ii = 0; ii < badNames.length; ii += 1) {
if (ii > 0) {
msg += `, ${badNames[ii]}`;
} else {
msg += badNames[ii];
}
}
msg += ']';
throw new Error(`${errorName}special character sequences \${name}: names not found: ${msg}`);
}
}
return ret;
};