speech-rule-engine
Version:
A standalone speech rule engine for XML structures, based on the original engine from ChromeVox.
180 lines • 6.61 kB
JavaScript
import { Span } from '../audio/span.js';
import * as DomUtil from '../common/dom_util.js';
import * as XpathUtil from '../common/xpath_util.js';
import { LOCALE } from '../l10n/locale.js';
import { vulgarFractionSmall } from '../l10n/transformers.js';
import { Grammar } from '../rule_engine/grammar.js';
import * as StoreUtil from '../rule_engine/store_util.js';
import { isMatchingFence } from '../semantic_tree/semantic_attr.js';
import { SemanticRole, SemanticType } from '../semantic_tree/semantic_meaning.js';
import { lookupCategory } from '../rule_engine/math_compound_store.js';
export function nodeCounter(nodes, context) {
const split = context.split('-');
const func = StoreUtil.nodeCounter(nodes, split[0] || '');
const sep = split[1] || '';
const init = split[2] || '';
let first = true;
return function () {
const result = func();
if (first) {
first = false;
return init + result + sep;
}
else {
return result + sep;
}
};
}
function simpleNode(node) {
if (!node.hasAttribute('annotation')) {
return false;
}
const annotation = node.getAttribute('annotation');
return !!/clearspeak:simple$|clearspeak:simple;/.exec(annotation);
}
function simpleCell_(node) {
if (simpleNode(node)) {
return true;
}
if (node.tagName !== SemanticType.SUBSCRIPT) {
return false;
}
const children = node.childNodes[0].childNodes;
const index = children[1];
return (children[0].tagName === SemanticType.IDENTIFIER &&
(isInteger_(index) ||
(index.tagName === SemanticType.INFIXOP &&
index.hasAttribute('role') &&
index.getAttribute('role') === SemanticRole.IMPLICIT &&
allIndices_(index))));
}
function isInteger_(node) {
return (node.tagName === SemanticType.NUMBER &&
node.hasAttribute('role') &&
node.getAttribute('role') === SemanticRole.INTEGER);
}
function allIndices_(node) {
const nodes = XpathUtil.evalXPath('children/*', node);
return nodes.every((x) => isInteger_(x) || x.tagName === SemanticType.IDENTIFIER);
}
export function allCellsSimple(node) {
const xpath = node.tagName === SemanticType.MATRIX
? 'children/row/children/cell/children/*'
: 'children/line/children/*';
const nodes = XpathUtil.evalXPath(xpath, node);
const result = nodes.every(simpleCell_);
return result ? [node] : [];
}
export function isSmallVulgarFraction(node) {
return vulgarFractionSmall(node, 20, 11) ? [node] : [];
}
export function ordinalExponent(node) {
const num = parseInt(node.textContent, 10);
return [
Span.stringEmpty(isNaN(num)
? node.textContent
: num > 10
? LOCALE.NUMBERS.numericOrdinal(num)
: LOCALE.NUMBERS.wordOrdinal(num))
];
}
let NESTING_DEPTH = null;
export function nestingDepth(node) {
let count = 0;
const fence = node.textContent;
const index = node.getAttribute('role') === 'open' ? 0 : 1;
let parent = node.parentNode;
while (parent) {
if (parent.tagName === SemanticType.FENCED &&
parent.childNodes[0].childNodes[index].textContent === fence) {
count++;
}
parent = parent.parentNode;
}
NESTING_DEPTH = count > 1 ? LOCALE.NUMBERS.wordOrdinal(count) : '';
return [Span.stringEmpty(NESTING_DEPTH)];
}
export function matchingFences(node) {
const sibling = node.previousSibling;
let left, right;
if (sibling) {
left = sibling;
right = node;
}
else {
left = node;
right = node.nextSibling;
}
if (!right) {
return [];
}
return isMatchingFence(left.textContent, right.textContent) ? [node] : [];
}
function insertNesting(text, correction) {
if (!correction || !text) {
return text;
}
const start = text.match(/^(open|close) /);
if (!start) {
return correction + ' ' + text;
}
return start[0] + correction + ' ' + text.substring(start[0].length);
}
Grammar.getInstance().setCorrection('insertNesting', insertNesting);
export function fencedArguments(node) {
const content = DomUtil.toArray(node.parentNode.childNodes);
const children = XpathUtil.evalXPath('../../children/*', node);
const index = content.indexOf(node);
return fencedFactor_(children[index]) || fencedFactor_(children[index + 1])
? [node]
: [];
}
export function simpleArguments(node) {
const content = DomUtil.toArray(node.parentNode.childNodes);
const children = XpathUtil.evalXPath('../../children/*', node);
const index = content.indexOf(node);
return simpleFactor_(children[index]) &&
children[index + 1] &&
(simpleFactor_(children[index + 1]) ||
children[index + 1].tagName === SemanticType.ROOT ||
children[index + 1].tagName === SemanticType.SQRT ||
(children[index + 1].tagName === SemanticType.SUPERSCRIPT &&
children[index + 1].childNodes[0].childNodes[0] &&
(children[index + 1].childNodes[0].childNodes[0]
.tagName === SemanticType.NUMBER ||
children[index + 1].childNodes[0].childNodes[0]
.tagName === SemanticType.IDENTIFIER) &&
(children[index + 1].childNodes[0].childNodes[1].textContent === '2' ||
children[index + 1].childNodes[0].childNodes[1].textContent === '3')))
? [node]
: [];
}
function simpleFactor_(node) {
return (!!node &&
(node.tagName === SemanticType.NUMBER ||
node.tagName === SemanticType.IDENTIFIER ||
node.tagName === SemanticType.FUNCTION ||
node.tagName === SemanticType.APPL ||
node.tagName === SemanticType.FRACTION));
}
function fencedFactor_(node) {
return (node &&
(node.tagName === SemanticType.FENCED ||
(node.hasAttribute('role') &&
node.getAttribute('role') === SemanticRole.LEFTRIGHT) ||
layoutFactor_(node)));
}
function layoutFactor_(node) {
return (!!node &&
(node.tagName === SemanticType.MATRIX ||
node.tagName === SemanticType.VECTOR));
}
export function wordOrdinal(node) {
return [
Span.stringEmpty(LOCALE.NUMBERS.wordOrdinal(parseInt(node.textContent, 10)))
];
}
export function isUnit(node) {
return lookupCategory(node.textContent + ':unit') ? [node] : [];
}
//# sourceMappingURL=clearspeak_util.js.map