UNPKG

speech-rule-engine

Version:

A standalone speech rule engine for XML structures, based on the original engine from ChromeVox.

180 lines 6.61 kB
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