speech-rule-engine
Version:
A standalone speech rule engine for XML structures, based on the original engine from ChromeVox.
193 lines • 7.17 kB
JavaScript
import { Engine } from '../common/engine.js';
import { SemanticRole, SemanticType } from '../semantic_tree/semantic_meaning.js';
import { register, activate } from '../semantic_tree/semantic_annotations.js';
import { SemanticAnnotator, SemanticVisitor } from '../semantic_tree/semantic_annotator.js';
function isSimpleExpression(node) {
return (isSimpleNumber_(node) ||
isSimpleLetters_(node) ||
isSimpleDegree_(node) ||
isSimpleNegative_(node) ||
isSimpleFunction_(node));
}
function isSimpleFunction_(node) {
return (node.type === SemanticType.APPL &&
(node.childNodes[0].role === SemanticRole.PREFIXFUNC ||
node.childNodes[0].role === SemanticRole.SIMPLEFUNC) &&
(isSimple_(node.childNodes[1]) ||
(node.childNodes[1].type === SemanticType.FENCED &&
isSimple_(node.childNodes[1].childNodes[0]))));
}
function isSimpleNegative_(node) {
return (node.type === SemanticType.PREFIXOP &&
node.role === SemanticRole.NEGATIVE &&
isSimple_(node.childNodes[0]) &&
node.childNodes[0].type !== SemanticType.PREFIXOP &&
node.childNodes[0].type !== SemanticType.APPL &&
node.childNodes[0].type !== SemanticType.PUNCTUATED);
}
function isSimpleDegree_(node) {
return (node.type === SemanticType.PUNCTUATED &&
node.role === SemanticRole.ENDPUNCT &&
node.childNodes.length === 2 &&
node.childNodes[1].role === SemanticRole.DEGREE &&
(isLetter_(node.childNodes[0]) ||
isNumber_(node.childNodes[0]) ||
(node.childNodes[0].type === SemanticType.PREFIXOP &&
node.childNodes[0].role === SemanticRole.NEGATIVE &&
(isLetter_(node.childNodes[0].childNodes[0]) ||
isNumber_(node.childNodes[0].childNodes[0])))));
}
function isSimpleLetters_(node) {
return (isLetter_(node) ||
(node.type === SemanticType.INFIXOP &&
node.role === SemanticRole.IMPLICIT &&
((node.childNodes.length === 2 &&
(isLetter_(node.childNodes[0]) ||
isSimpleNumber_(node.childNodes[0])) &&
isLetter_(node.childNodes[1])) ||
(node.childNodes.length === 3 &&
isSimpleNumber_(node.childNodes[0]) &&
isLetter_(node.childNodes[1]) &&
isLetter_(node.childNodes[2])))));
}
function isSimple_(node) {
return node.hasAnnotation('clearspeak', 'simple');
}
function isLetter_(node) {
return (node.type === SemanticType.IDENTIFIER &&
(node.role === SemanticRole.LATINLETTER ||
node.role === SemanticRole.GREEKLETTER ||
node.role === SemanticRole.OTHERLETTER ||
node.role === SemanticRole.SIMPLEFUNC));
}
function isNumber_(node) {
return (node.type === SemanticType.NUMBER &&
(node.role === SemanticRole.INTEGER || node.role === SemanticRole.FLOAT));
}
function isSimpleNumber_(node) {
return isNumber_(node) || isSimpleFraction_(node);
}
function isSimpleFraction_(node) {
if (hasPreference('Fraction_Over') || hasPreference('Fraction_FracOver')) {
return false;
}
if (node.type !== SemanticType.FRACTION ||
node.role !== SemanticRole.VULGAR) {
return false;
}
if (hasPreference('Fraction_Ordinal')) {
return true;
}
const enumerator = parseInt(node.childNodes[0].textContent, 10);
const denominator = parseInt(node.childNodes[1].textContent, 10);
return (enumerator > 0 && enumerator < 20 && denominator > 0 && denominator < 11);
}
function hasPreference(pref) {
return Engine.getInstance().options.style === pref;
}
register(new SemanticAnnotator('clearspeak', 'simple', function (node) {
return isSimpleExpression(node) ? 'simple' : '';
}));
activate('clearspeak', 'simple');
function isUnitExpression(node) {
return ((node.type === SemanticType.TEXT && node.role !== SemanticRole.LABEL) ||
(node.type === SemanticType.PUNCTUATED &&
node.role === SemanticRole.TEXT &&
isNumber_(node.childNodes[0]) &&
allTextLastContent_(node.childNodes.slice(1))) ||
(node.type === SemanticType.IDENTIFIER &&
node.role === SemanticRole.UNIT) ||
(node.type === SemanticType.INFIXOP &&
(node.role === SemanticRole.IMPLICIT || node.role === SemanticRole.UNIT)));
}
function allTextLastContent_(nodes) {
for (let i = 0; i < nodes.length - 1; i++) {
if (!(nodes[i].type === SemanticType.TEXT && nodes[i].textContent === '')) {
return false;
}
}
return nodes[nodes.length - 1].type === SemanticType.TEXT;
}
register(new SemanticAnnotator('clearspeak', 'unit', function (node) {
return isUnitExpression(node) ? 'unit' : '';
}));
activate('clearspeak', 'unit');
const NUMBER_PROPAGATORS = [
SemanticType.MULTIREL,
SemanticType.RELSEQ,
SemanticType.APPL,
SemanticType.ROW,
SemanticType.LINE
];
const NUMBER_INHIBITORS = [
SemanticType.SUBSCRIPT,
SemanticType.SUPERSCRIPT,
SemanticType.OVERSCORE,
SemanticType.UNDERSCORE
];
function checkParent(node, info) {
const parent = node.parent;
if (!parent) {
return false;
}
const type = parent.type;
if (NUMBER_PROPAGATORS.indexOf(type) !== -1 ||
(type === SemanticType.PREFIXOP &&
parent.role === SemanticRole.NEGATIVE &&
!info.script &&
!info.enclosed) ||
(type === SemanticType.PREFIXOP &&
parent.role === SemanticRole.GEOMETRY)) {
return true;
}
if (type === SemanticType.PUNCTUATED) {
if (!info.enclosed || parent.role === SemanticRole.TEXT) {
return true;
}
}
return false;
}
function propagateNumber(node, info) {
if (!node.childNodes.length) {
if (checkParent(node, info)) {
info.number = true;
info.script = false;
info.enclosed = false;
}
return [
info['number'] ? 'number' : '',
{ number: false, enclosed: info.enclosed, script: info.script }
];
}
if (NUMBER_INHIBITORS.indexOf(node.type) !== -1) {
info.script = true;
}
if (node.type === SemanticType.FENCED) {
info.number = false;
info.enclosed = true;
return ['', info];
}
if (node.type === SemanticType.PREFIXOP &&
node.role !== SemanticRole.GEOMETRY &&
node.role !== SemanticRole.NEGATIVE) {
info.number = false;
return ['', info];
}
if (checkParent(node, info)) {
info.number = true;
info.enclosed = false;
}
return ['', info];
}
register(new SemanticVisitor('nemeth', 'number', propagateNumber, { number: true }));
activate('nemeth', 'number');
function annotateDepth(node) {
if (!node.parent) {
return [1];
}
const depth = parseInt(node.parent.annotation['depth'][0]);
return [depth + 1];
}
register(new SemanticVisitor('depth', 'depth', annotateDepth));
activate('depth', 'depth');
//# sourceMappingURL=special_annotators.js.map