1
0

add initial marp implementation with sample content and build configuration

This commit is contained in:
2025-09-13 18:13:22 +02:00
parent dcacc9b409
commit e5f219507f
10319 changed files with 1402023 additions and 0 deletions

View File

@@ -0,0 +1,80 @@
import { AuditoryDescription } from '../audio/auditory_description.js';
import { AxisOrder, DynamicCstr, DynamicCstrParser } from './dynamic_cstr.js';
import { Action, Precondition, SpeechRule } from './speech_rule.js';
import { SpeechRuleContext } from './speech_rule_context.js';
import { SpeechRuleEvaluator } from './speech_rule_evaluator.js';
import { SpeechRuleFunction } from './speech_rule_functions.js';
import { SpeechRuleStore } from './speech_rule_store.js';
export declare abstract class BaseRuleStore implements SpeechRuleEvaluator, SpeechRuleStore {
context: SpeechRuleContext;
parseOrder: AxisOrder;
parser: DynamicCstrParser;
locale: string;
modality: string;
domain: string;
parseMethods: any;
initialized: boolean;
inherits: BaseRuleStore;
kind: string;
customTranscriptions: {
[key: string]: string;
};
protected preconditions: Map<string, Condition>;
private speechRules_;
private rank;
private static compareStaticConstraints_;
private static comparePreconditions_;
constructor();
defineRule(name: string, dynamic: string, action: string, prec: string, ...args: string[]): SpeechRule;
addRule(rule: SpeechRule): void;
deleteRule(rule: SpeechRule): void;
findRule(pred: (rule: SpeechRule) => boolean): SpeechRule;
findAllRules(pred: (rule: SpeechRule) => boolean): SpeechRule[];
evaluateDefault(node: Node): AuditoryDescription[];
evaluateWhitespace(_str: string): AuditoryDescription[];
evaluateCustom(str: string): AuditoryDescription;
evaluateCharacter(str: string): AuditoryDescription;
abstract evaluateString(str: string): AuditoryDescription[];
abstract initialize(): void;
removeDuplicates(rule: SpeechRule): void;
getSpeechRules(): SpeechRule[];
setSpeechRules(rules: SpeechRule[]): void;
getPreconditions(): Map<string, Condition>;
parseCstr(cstr: string): DynamicCstr;
parsePrecondition(query: string, rest: string[]): Precondition;
parseAction(action: string): Action;
parse(ruleSet: RulesJson): void;
parseRules(rules: string[][]): void;
generateRules(generator: string): void;
defineAction(name: string, action: string): void;
getFullPreconditions(name: string): Condition;
definePrecondition(name: string, dynamic: string, prec: string, ...args: string[]): void;
inheritRules(): void;
ignoreRules(name: string, ...cstrs: string[]): void;
private parsePrecondition_;
}
declare class Condition {
private base;
private _conditions;
private constraints;
private allCstr;
constructor(base: DynamicCstr, condition: Precondition);
get conditions(): [DynamicCstr, Precondition][];
addConstraint(dynamic: DynamicCstr): void;
addBaseCondition(cond: Precondition): void;
addFullCondition(cond: Precondition): void;
private addCondition;
}
export interface RulesJson {
modality?: string;
domain?: string;
locale?: string;
kind?: string;
inherits?: string;
functions?: {
[key: string]: SpeechRuleFunction;
};
rules?: any[];
annotators?: any[];
}
export {};

View File

@@ -0,0 +1,316 @@
import { AuditoryDescription } from '../audio/auditory_description.js';
import { Axis, DynamicCstr, DynamicCstrParser } from './dynamic_cstr.js';
import { Action, Precondition, SpeechRule } from './speech_rule.js';
import { SpeechRuleContext } from './speech_rule_context.js';
export class BaseRuleStore {
static compareStaticConstraints_(cstr1, cstr2) {
if (cstr1.length !== cstr2.length) {
return false;
}
for (let i = 0, cstr; (cstr = cstr1[i]); i++) {
if (cstr2.indexOf(cstr) === -1) {
return false;
}
}
return true;
}
static comparePreconditions_(rule1, rule2) {
const prec1 = rule1.precondition;
const prec2 = rule2.precondition;
if (prec1.query !== prec2.query) {
return false;
}
return BaseRuleStore.compareStaticConstraints_(prec1.constraints, prec2.constraints);
}
constructor() {
this.context = new SpeechRuleContext();
this.parseOrder = DynamicCstr.DEFAULT_ORDER;
this.parser = new DynamicCstrParser(this.parseOrder);
this.locale = DynamicCstr.DEFAULT_VALUES[Axis.LOCALE];
this.modality = DynamicCstr.DEFAULT_VALUES[Axis.MODALITY];
this.domain = '';
this.initialized = false;
this.inherits = null;
this.kind = 'standard';
this.customTranscriptions = {};
this.preconditions = new Map();
this.speechRules_ = [];
this.rank = 0;
this.parseMethods = {
Rule: this.defineRule,
Generator: this.generateRules,
Action: this.defineAction,
Precondition: this.definePrecondition,
Ignore: this.ignoreRules
};
}
defineRule(name, dynamic, action, prec, ...args) {
const postc = this.parseAction(action);
const fullPrec = this.parsePrecondition(prec, args);
const dynamicCstr = this.parseCstr(dynamic);
if (!(postc && fullPrec && dynamicCstr)) {
console.error(`Rule Error: ${prec}, (${dynamic}): ${action}`);
return null;
}
const rule = new SpeechRule(name, dynamicCstr, fullPrec, postc);
rule.precondition.rank = this.rank++;
this.addRule(rule);
return rule;
}
addRule(rule) {
rule.context = this.context;
this.speechRules_.unshift(rule);
}
deleteRule(rule) {
const index = this.speechRules_.indexOf(rule);
if (index !== -1) {
this.speechRules_.splice(index, 1);
}
}
findRule(pred) {
for (let i = 0, rule; (rule = this.speechRules_[i]); i++) {
if (pred(rule)) {
return rule;
}
}
return null;
}
findAllRules(pred) {
return this.speechRules_.filter(pred);
}
evaluateDefault(node) {
const rest = node.textContent.slice(0);
if (rest.match(/^\s+$/)) {
return this.evaluateWhitespace(rest);
}
return this.evaluateString(rest);
}
evaluateWhitespace(_str) {
return [];
}
evaluateCustom(str) {
const trans = this.customTranscriptions[str];
return trans !== undefined
? AuditoryDescription.create({ text: trans }, { adjust: true, translate: false })
: null;
}
evaluateCharacter(str) {
return (this.evaluateCustom(str) ||
AuditoryDescription.create({ text: str }, { adjust: true, translate: true }));
}
removeDuplicates(rule) {
for (let i = this.speechRules_.length - 1, oldRule; (oldRule = this.speechRules_[i]); i--) {
if (oldRule !== rule &&
rule.dynamicCstr.equal(oldRule.dynamicCstr) &&
BaseRuleStore.comparePreconditions_(oldRule, rule)) {
this.speechRules_.splice(i, 1);
}
}
}
getSpeechRules() {
return this.speechRules_;
}
setSpeechRules(rules) {
this.speechRules_ = rules;
}
getPreconditions() {
return this.preconditions;
}
parseCstr(cstr) {
try {
return this.parser.parse(this.locale +
'.' +
this.modality +
(this.domain ? '.' + this.domain : '') +
'.' +
cstr);
}
catch (err) {
if (err.name === 'RuleError') {
console.error('Rule Error ', `Illegal Dynamic Constraint: ${cstr}.`, err.message);
return null;
}
else {
throw err;
}
}
}
parsePrecondition(query, rest) {
try {
const queryCstr = this.parsePrecondition_(query);
query = queryCstr[0];
let restCstr = queryCstr.slice(1);
for (const cstr of rest) {
restCstr = restCstr.concat(this.parsePrecondition_(cstr));
}
return new Precondition(query, ...restCstr);
}
catch (err) {
if (err.name === 'RuleError') {
console.error('Rule Error ', `Illegal preconditions: ${query}, ${rest}.`, err.message);
return null;
}
else {
throw err;
}
}
}
parseAction(action) {
try {
return Action.fromString(action);
}
catch (err) {
if (err.name === 'RuleError') {
console.error('Rule Error ', `Illegal action: ${action}.`, err.message);
return null;
}
else {
throw err;
}
}
}
parse(ruleSet) {
this.modality = ruleSet.modality || this.modality;
this.locale = ruleSet.locale || this.locale;
this.domain = ruleSet.domain || this.domain;
this.context.parse(ruleSet.functions || []);
if (ruleSet.kind !== 'actions') {
this.kind = ruleSet.kind || this.kind;
this.inheritRules();
}
this.parseRules(ruleSet.rules || []);
}
parseRules(rules) {
for (let i = 0, rule; (rule = rules[i]); i++) {
const type = rule[0];
const method = this.parseMethods[type];
if (type && method) {
method.apply(this, rule.slice(1));
}
}
}
generateRules(generator) {
const method = this.context.customGenerators.lookup(generator);
if (method) {
method(this);
}
}
defineAction(name, action) {
let postc;
try {
postc = Action.fromString(action);
}
catch (err) {
if (err.name === 'RuleError') {
console.error('Action Error ', action, err.message);
return;
}
else {
throw err;
}
}
const prec = this.getFullPreconditions(name);
if (!prec) {
console.error(`Action Error: No precondition for action ${name}`);
return;
}
this.ignoreRules(name);
const regexp = new RegExp('^\\w+\\.\\w+\\.' + (this.domain ? '\\w+\\.' : ''));
prec.conditions.forEach(([dynamic, prec]) => {
const newDynamic = this.parseCstr(dynamic.toString().replace(regexp, ''));
this.addRule(new SpeechRule(name, newDynamic, prec, postc));
});
}
getFullPreconditions(name) {
const prec = this.preconditions.get(name);
if (prec || !this.inherits) {
return prec;
}
return this.inherits.getFullPreconditions(name);
}
definePrecondition(name, dynamic, prec, ...args) {
const fullPrec = this.parsePrecondition(prec, args);
const dynamicCstr = this.parseCstr(dynamic);
if (!(fullPrec && dynamicCstr)) {
console.error(`Precondition Error: ${prec}, (${dynamic})`);
return;
}
fullPrec.rank = this.rank++;
this.preconditions.set(name, new Condition(dynamicCstr, fullPrec));
}
inheritRules() {
if (!this.inherits || !this.inherits.getSpeechRules().length) {
return;
}
const regexp = new RegExp('^\\w+\\.\\w+\\.' + (this.domain ? '\\w+\\.' : ''));
this.inherits.getSpeechRules().forEach((rule) => {
const newDynamic = this.parseCstr(rule.dynamicCstr.toString().replace(regexp, ''));
this.addRule(new SpeechRule(rule.name, newDynamic, rule.precondition, rule.action));
});
}
ignoreRules(name, ...cstrs) {
let rules = this.findAllRules((r) => r.name === name);
if (!cstrs.length) {
rules.forEach(this.deleteRule.bind(this));
return;
}
let rest = [];
for (const cstr of cstrs) {
const dynamic = this.parseCstr(cstr);
for (const rule of rules) {
if (dynamic.equal(rule.dynamicCstr)) {
this.deleteRule(rule);
}
else {
rest.push(rule);
}
}
rules = rest;
rest = [];
}
}
parsePrecondition_(cstr) {
const generator = this.context.customGenerators.lookup(cstr);
return generator ? generator() : [cstr];
}
}
class Condition {
constructor(base, condition) {
this.base = base;
this._conditions = [];
this.constraints = [];
this.allCstr = {};
this.constraints.push(base);
this.addCondition(base, condition);
}
get conditions() {
return this._conditions;
}
addConstraint(dynamic) {
if (this.constraints.filter((cstr) => cstr.equal(dynamic)).length) {
return;
}
this.constraints.push(dynamic);
const newConds = [];
for (const [dyn, pre] of this.conditions) {
if (this.base.equal(dyn)) {
newConds.push([dynamic, pre]);
}
}
this._conditions = this._conditions.concat(newConds);
}
addBaseCondition(cond) {
this.addCondition(this.base, cond);
}
addFullCondition(cond) {
this.constraints.forEach((cstr) => this.addCondition(cstr, cond));
}
addCondition(dynamic, cond) {
const condStr = dynamic.toString() + ' ' + cond.toString();
if (this.allCstr.condStr) {
return;
}
this.allCstr[condStr] = true;
this._conditions.push([dynamic, cond]);
}
}

View File

@@ -0,0 +1,22 @@
import { MathStore } from './math_store.js';
import { AuditoryDescription } from '../audio/auditory_description.js';
export declare class BrailleStore extends MathStore {
modality: string;
customTranscriptions: {
[key: string]: string;
};
evaluateString(str: string): AuditoryDescription[];
annotations(): void;
}
export declare class EuroStore extends BrailleStore {
locale: string;
customTranscriptions: {};
customCommands: {
[key: string]: string;
};
evaluateString(str: string): AuditoryDescription[];
protected cleanup(commands: string[]): string;
private lastSpecial;
private specialChars;
private addSpace;
}

View File

@@ -0,0 +1,109 @@
import { MathStore } from './math_store.js';
import { activate } from '../semantic_tree/semantic_annotations.js';
import { SemanticMap } from '../semantic_tree/semantic_attr.js';
import { SemanticType, SemanticRole } from '../semantic_tree/semantic_meaning.js';
export class BrailleStore extends MathStore {
constructor() {
super(...arguments);
this.modality = 'braille';
this.customTranscriptions = {
'\u22ca': '⠈⠡⠳'
};
}
evaluateString(str) {
const descs = [];
const text = Array.from(str);
for (let i = 0; i < text.length; i++) {
descs.push(this.evaluateCharacter(text[i]));
}
return descs;
}
annotations() {
for (let i = 0, annotator; (annotator = this.annotators[i]); i++) {
activate(this.locale, annotator);
}
}
}
export class EuroStore extends BrailleStore {
constructor() {
super(...arguments);
this.locale = 'euro';
this.customTranscriptions = {};
this.customCommands = {
'\\cdot': '*',
'\\lt': '<',
'\\gt': '>'
};
this.lastSpecial = false;
this.specialChars = ['^', '_', '{', '}'];
}
evaluateString(str) {
const regexp = /(\\[a-z]+|\\{|\\}|\\\\)/i;
const split = str.split(regexp);
const cleaned = this.cleanup(split);
return super.evaluateString(cleaned);
}
cleanup(commands) {
const cleaned = [];
let intext = false;
let lastcom = null;
for (let command of commands) {
if (command.match(/^\\/)) {
if (command === '\\text') {
intext = true;
}
if (this.addSpace(SemanticMap.LatexCommands.get(command))) {
cleaned.push(' ');
}
command = this.customCommands[command] || command;
const newcom = command.match(/^\\/);
if (newcom && command.match(/^\\[a-zA-Z]+$/) && lastcom) {
cleaned.push(' ');
}
lastcom = newcom ? command : null;
cleaned.push(command);
continue;
}
const rest = command.split('');
for (const char of rest) {
if (intext) {
cleaned.push(char);
intext = char !== '}';
lastcom = null;
continue;
}
if (char.match(/[a-z]/i) && lastcom) {
lastcom = null;
cleaned.push(' ');
cleaned.push(char);
continue;
}
if (char.match(/\s/))
continue;
if (this.addSpace(char)) {
cleaned.push(' ');
}
cleaned.push(char);
lastcom = null;
}
}
return cleaned.join('');
}
addSpace(char) {
if (!char)
return false;
if (this.specialChars.indexOf(char) !== -1) {
this.lastSpecial = true;
return false;
}
if (this.lastSpecial) {
this.lastSpecial = false;
return false;
}
const meaning = SemanticMap.Meaning.get(char);
return (meaning.type === SemanticType.OPERATOR ||
meaning.type === SemanticType.RELATION ||
(meaning.type === SemanticType.PUNCTUATION &&
meaning.role === SemanticRole.COLON));
}
}

View File

@@ -0,0 +1,66 @@
export declare enum Axis {
DOMAIN = "domain",
STYLE = "style",
LOCALE = "locale",
TOPIC = "topic",
MODALITY = "modality"
}
export type AxisProperties = {
[key: string]: string[];
};
export type AxisOrder = Axis[];
export type AxisMap = {
[key: string]: string;
};
export declare class DynamicProperties {
private properties;
protected order: AxisOrder;
static createProp(...cstrList: string[][]): DynamicProperties;
constructor(properties: AxisProperties, order?: AxisOrder);
getProperties(): AxisProperties;
getOrder(): AxisOrder;
getAxes(): AxisOrder;
getProperty(key: Axis): string[];
updateProperties(props: AxisProperties): void;
allProperties(): string[][];
toString(): string;
}
export declare class DynamicCstr extends DynamicProperties {
static DEFAULT_ORDER: AxisOrder;
static BASE_LOCALE: string;
static DEFAULT_VALUE: string;
static DEFAULT_VALUES: AxisMap;
private components;
static createCstr(...cstrList: string[]): DynamicCstr;
static defaultCstr(): DynamicCstr;
static validOrder(order: AxisOrder): boolean;
constructor(components_: AxisMap, order?: AxisOrder);
getComponents(): AxisMap;
getValue(key: Axis): string;
getValues(): string[];
allProperties(): string[][];
toString(): string;
equal(cstr: DynamicCstr): boolean;
}
export declare class DynamicCstrParser {
private order;
constructor(order: AxisOrder);
parse(str: string): DynamicCstr;
}
export interface Comparator {
getReference(): DynamicCstr;
setReference(cstr: DynamicCstr, opt_props?: DynamicProperties): void;
match(cstr: DynamicCstr): boolean;
compare(cstr1: DynamicCstr, cstr2: DynamicCstr): number;
}
export declare class DefaultComparator implements Comparator {
private reference;
private fallback;
private order;
constructor(reference: DynamicCstr, fallback?: DynamicProperties);
getReference(): DynamicCstr;
setReference(cstr: DynamicCstr, props?: DynamicProperties): void;
match(cstr: DynamicCstr): boolean;
compare(cstr1: DynamicCstr, cstr2: DynamicCstr): 0 | 1 | -1;
toString(): string;
}

View File

@@ -0,0 +1,206 @@
export var Axis;
(function (Axis) {
Axis["DOMAIN"] = "domain";
Axis["STYLE"] = "style";
Axis["LOCALE"] = "locale";
Axis["TOPIC"] = "topic";
Axis["MODALITY"] = "modality";
})(Axis || (Axis = {}));
export class DynamicProperties {
static createProp(...cstrList) {
const axes = DynamicCstr.DEFAULT_ORDER;
const dynamicCstr = {};
for (let i = 0, l = cstrList.length, k = axes.length; i < l && i < k; i++) {
dynamicCstr[axes[i]] = cstrList[i];
}
return new DynamicProperties(dynamicCstr);
}
constructor(properties, order = Object.keys(properties)) {
this.properties = properties;
this.order = order;
}
getProperties() {
return this.properties;
}
getOrder() {
return this.order;
}
getAxes() {
return this.order;
}
getProperty(key) {
return this.properties[key];
}
updateProperties(props) {
this.properties = props;
}
allProperties() {
const propLists = [];
this.order.forEach((key) => propLists.push(this.getProperty(key).slice()));
return propLists;
}
toString() {
const cstrStrings = [];
this.order.forEach((key) => cstrStrings.push(key + ': ' + this.getProperty(key).toString()));
return cstrStrings.join('\n');
}
}
export class DynamicCstr extends DynamicProperties {
static createCstr(...cstrList) {
const axes = DynamicCstr.DEFAULT_ORDER;
const dynamicCstr = {};
for (let i = 0, l = cstrList.length, k = axes.length; i < l && i < k; i++) {
dynamicCstr[axes[i]] = cstrList[i];
}
return new DynamicCstr(dynamicCstr);
}
static defaultCstr() {
return DynamicCstr.createCstr.apply(null, DynamicCstr.DEFAULT_ORDER.map(function (x) {
return DynamicCstr.DEFAULT_VALUES[x];
}));
}
static validOrder(order) {
const axes = DynamicCstr.DEFAULT_ORDER.slice();
return order.every((x) => {
const index = axes.indexOf(x);
return index !== -1 && axes.splice(index, 1);
});
}
constructor(components_, order) {
const properties = {};
for (const [key, value] of Object.entries(components_)) {
properties[key] = [value];
}
super(properties, order);
this.components = components_;
}
getComponents() {
return this.components;
}
getValue(key) {
return this.components[key];
}
getValues() {
return this.order.map((key) => this.getValue(key));
}
allProperties() {
const propLists = super.allProperties();
for (let i = 0, props, key; (props = propLists[i]), (key = this.order[i]); i++) {
const value = this.getValue(key);
if (props.indexOf(value) === -1) {
props.unshift(value);
}
}
return propLists;
}
toString() {
return this.getValues().join('.');
}
equal(cstr) {
const keys1 = cstr.getAxes();
if (this.order.length !== keys1.length) {
return false;
}
for (let j = 0, key; (key = keys1[j]); j++) {
const comp2 = this.getValue(key);
if (!comp2 || cstr.getValue(key) !== comp2) {
return false;
}
}
return true;
}
}
DynamicCstr.DEFAULT_ORDER = [
Axis.LOCALE,
Axis.MODALITY,
Axis.DOMAIN,
Axis.STYLE,
Axis.TOPIC
];
DynamicCstr.BASE_LOCALE = 'base';
DynamicCstr.DEFAULT_VALUE = 'default';
DynamicCstr.DEFAULT_VALUES = {
[Axis.LOCALE]: 'en',
[Axis.DOMAIN]: DynamicCstr.DEFAULT_VALUE,
[Axis.STYLE]: DynamicCstr.DEFAULT_VALUE,
[Axis.TOPIC]: DynamicCstr.DEFAULT_VALUE,
[Axis.MODALITY]: 'speech'
};
export class DynamicCstrParser {
constructor(order) {
this.order = order;
}
parse(str) {
const order = str.split('.');
const cstr = {};
if (order.length > this.order.length) {
throw new Error('Invalid dynamic constraint: ' + cstr);
}
let j = 0;
for (let i = 0, key; (key = this.order[i]), order.length; i++, j++) {
const value = order.shift();
cstr[key] = value;
}
return new DynamicCstr(cstr, this.order.slice(0, j));
}
}
export class DefaultComparator {
constructor(reference, fallback = new DynamicProperties(reference.getProperties(), reference.getOrder())) {
this.reference = reference;
this.fallback = fallback;
this.order = this.reference.getOrder();
}
getReference() {
return this.reference;
}
setReference(cstr, props) {
this.reference = cstr;
this.fallback =
props || new DynamicProperties(cstr.getProperties(), cstr.getOrder());
this.order = this.reference.getOrder();
}
match(cstr) {
const keys1 = cstr.getAxes();
return (keys1.length === this.reference.getAxes().length &&
keys1.every((key) => {
const value = cstr.getValue(key);
return (value === this.reference.getValue(key) ||
this.fallback.getProperty(key).indexOf(value) !== -1);
}));
}
compare(cstr1, cstr2) {
let ignore = false;
for (let i = 0, key; (key = this.order[i]); i++) {
const value1 = cstr1.getValue(key);
const value2 = cstr2.getValue(key);
if (!ignore) {
const ref = this.reference.getValue(key);
if (ref === value1 && ref !== value2) {
return -1;
}
if (ref === value2 && ref !== value1) {
return 1;
}
if (ref === value1 && ref === value2) {
continue;
}
if (ref !== value1 && ref !== value2) {
ignore = true;
}
}
const prop = this.fallback.getProperty(key);
const index1 = prop.indexOf(value1);
const index2 = prop.indexOf(value2);
if (index1 < index2) {
return -1;
}
if (index2 < index1) {
return 1;
}
}
return 0;
}
toString() {
return this.reference.toString() + '\n' + this.fallback.toString();
}
}

View File

@@ -0,0 +1,48 @@
type Value = boolean | string;
export type State = {
[key: string]: Value;
};
interface Flags {
adjust?: boolean;
preprocess?: boolean;
correct?: boolean;
translate?: boolean;
}
type Correction = (text: string, parameter?: Value) => string;
export declare const ATTRIBUTE = "grammar";
export declare class Grammar {
private static instance;
currentFlags: Flags;
private parameters_;
private corrections_;
private preprocessors_;
private stateStack_;
private singles;
static getInstance(): Grammar;
static parseInput(grammar: string): State;
static parseState(stateStr: string): State;
private static translateString;
private static translateUnit;
private static prepareUnit;
private static cleanUnit;
clear(): void;
setParameter(parameter: string, value: Value): Value;
getParameter(parameter: string): Value;
setCorrection(correction: string, func: Correction): void;
setPreprocessor(preprocessor: string, func: Correction): void;
getCorrection(correction: string): Correction;
getState(): string;
processSingles(): void;
pushState(assignment: {
[key: string]: Value;
}): void;
popState(): void;
setAttribute(node: Element): void;
preprocess(text: string): string;
correct(text: string): string;
apply(text: string, opt_flags?: Flags): string;
private runProcessors;
private constructor();
}
export declare function correctFont(text: string, correction: string): string;
export {};

View File

@@ -0,0 +1,230 @@
import * as DomUtil from '../common/dom_util.js';
import { Engine } from '../common/engine.js';
import * as LocaleUtil from '../l10n/locale_util.js';
import { LOCALE } from '../l10n/locale.js';
export const ATTRIBUTE = 'grammar';
export class Grammar {
static getInstance() {
Grammar.instance = Grammar.instance || new Grammar();
return Grammar.instance;
}
static parseInput(grammar) {
const attributes = {};
const components = grammar.split(':');
for (const component of components) {
const comp = component.split('=');
const key = comp[0].trim();
if (comp[1]) {
attributes[key] = comp[1].trim();
continue;
}
key.match(/^!/)
? (attributes[key.slice(1)] = false)
: (attributes[key] = true);
}
return attributes;
}
static parseState(stateStr) {
const state = {};
const corrections = stateStr.split(' ');
for (const correction of corrections) {
const corr = correction.split(':');
const key = corr[0];
const value = corr[1];
state[key] = value ? value : true;
}
return state;
}
static translateString(text) {
if (text.match(/:unit$/)) {
return Grammar.translateUnit(text);
}
const engine = Engine.getInstance();
let result = engine.evaluator(text, engine.dynamicCstr);
result = result === null ? text : result;
if (Grammar.getInstance().getParameter('plural')) {
result = LOCALE.FUNCTIONS.plural(result);
}
return result;
}
static translateUnit(text) {
text = Grammar.prepareUnit(text);
const engine = Engine.getInstance();
const plural = Grammar.getInstance().getParameter('plural');
const strict = engine.strict;
const baseCstr = `${engine.locale}.${engine.modality}.default`;
engine.strict = true;
let cstr;
let result;
if (plural) {
cstr = engine.defaultParser.parse(baseCstr + '.plural');
result = engine.evaluator(text, cstr);
}
if (result) {
engine.strict = strict;
return result;
}
cstr = engine.defaultParser.parse(baseCstr + '.default');
result = engine.evaluator(text, cstr);
engine.strict = strict;
if (!result) {
return Grammar.cleanUnit(text);
}
if (plural) {
result = LOCALE.FUNCTIONS.plural(result);
}
return result;
}
static prepareUnit(text) {
const match = text.match(/:unit$/);
return match
? text.slice(0, match.index).replace(/\s+/g, ' ') +
text.slice(match.index)
: text;
}
static cleanUnit(text) {
if (text.match(/:unit$/)) {
return text.replace(/:unit$/, '');
}
return text;
}
clear() {
this.parameters_ = {};
this.stateStack_ = [];
}
setParameter(parameter, value) {
const oldValue = this.parameters_[parameter];
value
? (this.parameters_[parameter] = value)
: delete this.parameters_[parameter];
return oldValue;
}
getParameter(parameter) {
return this.parameters_[parameter];
}
setCorrection(correction, func) {
this.corrections_[correction] = func;
}
setPreprocessor(preprocessor, func) {
this.preprocessors_[preprocessor] = func;
}
getCorrection(correction) {
return this.corrections_[correction];
}
getState() {
const pairs = [];
for (const [key, val] of Object.entries(this.parameters_)) {
pairs.push(typeof val === 'string' ? key + ':' + val : key);
}
return pairs.join(' ');
}
processSingles() {
const assignment = {};
for (const single of this.singles) {
assignment[single] = false;
}
this.singles = [];
this.pushState(assignment);
}
pushState(assignment) {
for (let [key, value] of Object.entries(assignment)) {
if (key.match(/^\?/)) {
delete assignment[key];
key = key.slice(1);
this.singles.push(key);
}
assignment[key] = this.setParameter(key, value);
}
this.stateStack_.push(assignment);
}
popState() {
const assignment = this.stateStack_.pop();
for (const [key, val] of Object.entries(assignment)) {
this.setParameter(key, val);
}
}
setAttribute(node) {
if (node && node.nodeType === DomUtil.NodeType.ELEMENT_NODE) {
const state = this.getState();
if (state) {
node.setAttribute(ATTRIBUTE, state);
}
}
}
preprocess(text) {
return this.runProcessors(text, this.preprocessors_);
}
correct(text) {
return this.runProcessors(text, this.corrections_);
}
apply(text, opt_flags) {
this.currentFlags = opt_flags || {};
text =
this.currentFlags.adjust || this.currentFlags.preprocess
? Grammar.getInstance().preprocess(text)
: text;
if (this.parameters_['translate'] || this.currentFlags.translate) {
text = Grammar.translateString(text);
}
text =
this.currentFlags.adjust || this.currentFlags.correct
? Grammar.getInstance().correct(text)
: text;
this.currentFlags = {};
return text;
}
runProcessors(text, funcs) {
for (const [key, val] of Object.entries(this.parameters_)) {
const func = funcs[key];
if (!func) {
continue;
}
text = val === true ? func(text) : func(text, val);
}
return text;
}
constructor() {
this.currentFlags = {};
this.parameters_ = {};
this.corrections_ = {};
this.preprocessors_ = {};
this.stateStack_ = [];
this.singles = [];
}
}
export function correctFont(text, correction) {
if (!correction || !text) {
return text;
}
const regexp = LOCALE.FUNCTIONS.fontRegexp(LocaleUtil.localFont(correction));
return text.replace(regexp, '');
}
function correctCaps(text) {
let cap = LOCALE.ALPHABETS.capPrefix[Engine.getInstance().domain];
if (typeof cap === 'undefined') {
cap = LOCALE.ALPHABETS.capPrefix['default'];
}
return correctFont(text, cap);
}
function addAnnotation(text, annotation) {
return text + ':' + annotation;
}
function numbersToAlpha(text) {
return text.match(/\d+/)
? LOCALE.NUMBERS.numberToWords(parseInt(text, 10))
: text;
}
function noTranslateText(text) {
if (text.match(new RegExp('^[' + LOCALE.MESSAGES.regexp.TEXT + ']+$'))) {
Grammar.getInstance().currentFlags['translate'] = false;
}
return text;
}
Grammar.getInstance().setCorrection('localFont', LocaleUtil.localFont);
Grammar.getInstance().setCorrection('localRole', LocaleUtil.localRole);
Grammar.getInstance().setCorrection('localEnclose', LocaleUtil.localEnclose);
Grammar.getInstance().setCorrection('ignoreFont', correctFont);
Grammar.getInstance().setPreprocessor('annotation', addAnnotation);
Grammar.getInstance().setPreprocessor('noTranslateText', noTranslateText);
Grammar.getInstance().setCorrection('ignoreCaps', correctCaps);
Grammar.getInstance().setPreprocessor('numbers2alpha', numbersToAlpha);

View File

@@ -0,0 +1,21 @@
import { BaseJson, MathSimpleStore, SiJson, MappingsJson, SimpleRule, UnicodeJson } from './math_simple_store.js';
import { DynamicCstr } from './dynamic_cstr.js';
export declare function changeLocale(json: UnicodeJson): boolean;
export declare function setSiPrefixes(prefixes: SiJson): void;
export declare const subStores: Map<string, MathSimpleStore>;
export declare const baseStores: Map<string, BaseJson>;
export declare function defineRules(base: string, str: string, mappings: MappingsJson): void;
export declare function defineRule(domain: string, style: string, str: string, content: string): void;
export declare function addSymbolRules(json: UnicodeJson[]): void;
export declare const addCharacterRules: (json: UnicodeJson[]) => void;
export declare function addFunctionRules(json: UnicodeJson[]): void;
export declare function addUnitRules(json: UnicodeJson[]): void;
export declare function lookupRule(node: string, dynamic: DynamicCstr): SimpleRule;
export declare function lookupCategory(character: string): string;
export declare function lookupString(text: string, dynamic: DynamicCstr): string;
export declare function enumerate(info?: {
[key: string]: any;
}): {
[key: string]: any;
};
export declare function reset(): void;

View File

@@ -0,0 +1,168 @@
import { Engine } from '../common/engine.js';
import { locales } from '../l10n/l10n.js';
import { addFunctionSemantic } from '../semantic_tree/semantic_attr.js';
import { MathSimpleStore } from './math_simple_store.js';
import { Axis, DynamicCstr } from './dynamic_cstr.js';
let locale = DynamicCstr.DEFAULT_VALUES[Axis.LOCALE];
let modality = DynamicCstr.DEFAULT_VALUES[Axis.MODALITY];
export function changeLocale(json) {
if (!json['locale'] && !json['modality']) {
return false;
}
locale = json['locale'] || locale;
modality = json['modality'] || modality;
return true;
}
let siPrefixes = {};
export function setSiPrefixes(prefixes) {
siPrefixes = prefixes;
}
export const subStores = new Map();
export const baseStores = new Map();
function getSubStore(base, key) {
let store = subStores.get(key);
if (store) {
return store;
}
store = new MathSimpleStore();
store.base = baseStores.get(base);
subStores.set(key, store);
return store;
}
function completeWithBase(json) {
const base = baseStores.get(json.key);
if (!base) {
return;
}
const names = json.names;
Object.assign(json, base);
if (names && base.names) {
json.names = json.names.concat(names);
}
}
export function defineRules(base, str, mappings) {
const store = getSubStore(base, str);
store.defineRulesFromMappings(locale, modality, mappings);
}
export function defineRule(domain, style, str, content) {
const store = getSubStore(str, str);
store.defineRuleFromStrings(locale, modality, domain, style, content);
}
export function addSymbolRules(json) {
for (const rule of json) {
if (changeLocale(rule)) {
continue;
}
const key = MathSimpleStore.parseUnicode(rule['key']);
if (locale === 'base') {
baseStores.set(key, rule);
continue;
}
defineRules(key, key, rule['mappings']);
}
}
function addCharacterRule(json) {
if (changeLocale(json)) {
return;
}
for (const [key, value] of Object.entries(json)) {
defineRule('default', 'default', key, value);
}
}
export const addCharacterRules = (json) => json.forEach(addCharacterRule);
function addFunctionRule(json) {
for (let j = 0, name; (name = json.names[j]); j++) {
defineRules(json.key, name, json.mappings);
}
}
export function addFunctionRules(json) {
for (const rule of json) {
if (changeLocale(rule)) {
continue;
}
addFunctionSemantic(rule.key, rule.names || []);
if (locale === 'base') {
baseStores.set(rule.key, rule);
continue;
}
completeWithBase(rule);
addFunctionRule(rule);
}
}
export function addUnitRules(json) {
for (const rule of json) {
if (changeLocale(rule)) {
continue;
}
rule.key += ':unit';
if (locale === 'base') {
baseStores.set(rule.key, rule);
continue;
}
completeWithBase(rule);
if (rule.names) {
rule.names = rule.names.map(function (name) {
return name + ':unit';
});
}
if (rule.si) {
addSiUnitRule(rule);
}
addFunctionRule(rule);
}
}
function addSiUnitRule(json) {
for (const key of Object.keys(siPrefixes)) {
const newJson = Object.assign({}, json);
newJson.mappings = {};
const prefix = siPrefixes[key];
newJson['names'] = newJson['names'].map(function (name) {
return key + name;
});
for (const domain of Object.keys(json['mappings'])) {
newJson.mappings[domain] = {};
for (const style of Object.keys(json['mappings'][domain])) {
newJson['mappings'][domain][style] = locales[locale]().FUNCTIONS.si(prefix, json['mappings'][domain][style]);
}
}
addFunctionRule(newJson);
}
}
export function lookupRule(node, dynamic) {
const store = subStores.get(node);
return store ? store.lookupRule(null, dynamic) : null;
}
export function lookupCategory(character) {
const store = subStores.get(character);
return (store === null || store === void 0 ? void 0 : store.base) ? store.base.category : '';
}
export function lookupString(text, dynamic) {
const rule = lookupRule(text, dynamic);
if (!rule) {
return null;
}
return rule.action;
}
Engine.getInstance().evaluator = lookupString;
export function enumerate(info = {}) {
for (const store of subStores.values()) {
for (const [, rules] of store.rules.entries()) {
for (const { cstr: dynamic } of rules) {
info = enumerate_(dynamic.getValues(), info);
}
}
}
return info;
}
function enumerate_(dynamic, info) {
info = info || {};
if (!dynamic.length) {
return info;
}
info[dynamic[0]] = enumerate_(dynamic.slice(1), info[dynamic[0]]);
return info;
}
export function reset() {
locale = DynamicCstr.DEFAULT_VALUES[Axis.LOCALE];
modality = DynamicCstr.DEFAULT_VALUES[Axis.MODALITY];
}

View File

@@ -0,0 +1,38 @@
import { DynamicCstr } from './dynamic_cstr.js';
export interface MappingsJson {
default: {
[key: string]: string;
};
[domainName: string]: {
[key: string]: string;
};
}
export interface BaseJson {
key: string;
category: string;
names?: string[];
si?: boolean;
}
export interface UnicodeJson extends BaseJson {
mappings: MappingsJson;
modality?: string;
locale?: string;
domain?: string;
}
export interface SiJson {
[key: string]: string;
}
export interface SimpleRule {
cstr: DynamicCstr;
action: string;
}
export declare class MathSimpleStore {
base: BaseJson;
rules: Map<string, SimpleRule[]>;
static parseUnicode(num: string): string;
private static testDynamicConstraints_;
defineRulesFromMappings(locale: string, modality: string, mapping: MappingsJson): void;
getRules(key: string): SimpleRule[];
defineRuleFromStrings(locale: string, modality: string, domain: string, style: string, content: string): void;
lookupRule(_node: Node, dynamic: DynamicCstr): SimpleRule;
}

View File

@@ -0,0 +1,60 @@
import { Engine } from '../common/engine.js';
import { Axis } from './dynamic_cstr.js';
export class MathSimpleStore {
constructor() {
this.rules = new Map();
}
static parseUnicode(num) {
const keyValue = parseInt(num, 16);
return String.fromCodePoint(keyValue);
}
static testDynamicConstraints_(dynamic, rule) {
if (Engine.getInstance().strict) {
return rule.cstr.equal(dynamic);
}
return Engine.getInstance().comparator.match(rule.cstr);
}
defineRulesFromMappings(locale, modality, mapping) {
for (const [domain, styles] of Object.entries(mapping)) {
for (const [style, content] of Object.entries(styles)) {
this.defineRuleFromStrings(locale, modality, domain, style, content);
}
}
}
getRules(key) {
let store = this.rules.get(key);
if (!store) {
store = [];
this.rules.set(key, store);
}
return store;
}
defineRuleFromStrings(locale, modality, domain, style, content) {
let store = this.getRules(locale);
const parser = Engine.getInstance().parsers[domain] ||
Engine.getInstance().defaultParser;
const comp = Engine.getInstance().comparators[domain];
const cstr = `${locale}.${modality}.${domain}.${style}`;
const dynamic = parser.parse(cstr);
const comparator = comp ? comp() : Engine.getInstance().comparator;
const oldCstr = comparator.getReference();
comparator.setReference(dynamic);
const rule = { cstr: dynamic, action: content };
store = store.filter((r) => !dynamic.equal(r.cstr));
store.push(rule);
this.rules.set(locale, store);
comparator.setReference(oldCstr);
}
lookupRule(_node, dynamic) {
let rules = this.getRules(dynamic.getValue(Axis.LOCALE));
rules = rules.filter(function (rule) {
return MathSimpleStore.testDynamicConstraints_(dynamic, rule);
});
if (rules.length === 1) {
return rules[0];
}
return rules.length
? rules.sort((r1, r2) => Engine.getInstance().comparator.compare(r1.cstr, r2.cstr))[0]
: null;
}
}

View File

@@ -0,0 +1,24 @@
import { AuditoryDescription } from '../audio/auditory_description.js';
import { BaseRuleStore, RulesJson } from './base_rule_store.js';
export declare class MathStore extends BaseRuleStore {
annotators: string[];
constructor();
initialize(): void;
annotations(): void;
defineAlias(name: string, prec: string, ...args: string[]): void;
defineRulesAlias(name: string, query: string, ...args: string[]): void;
defineSpecializedRule(name: string, oldDynamic: string, newDynamic: string, opt_action?: string): void;
defineSpecialized(name: string, _old: string, dynamic: string): void;
evaluateString(str: string): AuditoryDescription[];
parse(ruleSet: RulesJson): void;
private addAlias_;
static regexp: {
NUMBER: string;
DECIMAL_MARK: string;
DIGIT_GROUP: string;
};
protected matchNumber(str: string): {
number: string;
length: number;
} | null;
}

View File

@@ -0,0 +1,162 @@
import * as BaseUtil from '../common/base_util.js';
import { LOCALE } from '../l10n/locale.js';
import { activate } from '../semantic_tree/semantic_annotations.js';
import { BaseRuleStore } from './base_rule_store.js';
import { Action, OutputError, SpeechRule } from './speech_rule.js';
export class MathStore extends BaseRuleStore {
constructor() {
super();
this.annotators = [];
this.parseMethods['Alias'] = this.defineAlias;
this.parseMethods['SpecializedRule'] = this.defineSpecializedRule;
this.parseMethods['Specialized'] = this.defineSpecialized;
}
initialize() {
if (this.initialized) {
return;
}
this.annotations();
this.initialized = true;
}
annotations() {
for (let i = 0, annotator; (annotator = this.annotators[i]); i++) {
activate(this.domain, annotator);
}
}
defineAlias(name, prec, ...args) {
const fullPrec = this.parsePrecondition(prec, args);
if (!fullPrec) {
console.error(`Precondition Error: ${prec} ${args}`);
return;
}
const condition = this.preconditions.get(name);
if (!condition) {
console.error(`Alias Error: No precondition by the name of ${name}`);
return;
}
condition.addFullCondition(fullPrec);
}
defineRulesAlias(name, query, ...args) {
const rules = this.findAllRules(function (rule) {
return rule.name === name;
});
if (rules.length === 0) {
throw new OutputError('Rule with name ' + name + ' does not exist.');
}
const keep = [];
const findKeep = (rule) => {
const cstr = rule.dynamicCstr.toString();
const action = rule.action.toString();
for (let i = 0, k; (k = keep[i]); i++) {
if (k.action === action && k.cstr === cstr) {
return false;
}
}
keep.push({ cstr: cstr, action: action });
return true;
};
rules.forEach((rule) => {
if (findKeep(rule)) {
this.addAlias_(rule, query, args);
}
});
}
defineSpecializedRule(name, oldDynamic, newDynamic, opt_action) {
const dynamicCstr = this.parseCstr(oldDynamic);
const rule = this.findRule((rule) => rule.name === name && dynamicCstr.equal(rule.dynamicCstr));
const newCstr = this.parseCstr(newDynamic);
if (!rule && opt_action) {
throw new OutputError('Rule named ' + name + ' with style ' + oldDynamic + ' does not exist.');
}
const action = opt_action ? Action.fromString(opt_action) : rule.action;
const newRule = new SpeechRule(rule.name, newCstr, rule.precondition, action);
this.addRule(newRule);
}
defineSpecialized(name, _old, dynamic) {
const cstr = this.parseCstr(dynamic);
if (!cstr) {
console.error(`Dynamic Constraint Error: ${dynamic}`);
return;
}
const condition = this.preconditions.get(name);
if (!condition) {
console.error(`Alias Error: No precondition by the name of ${name}`);
return;
}
condition.addConstraint(cstr);
}
evaluateString(str) {
const descs = [];
if (str.match(/^\s+$/)) {
return descs;
}
let num = this.matchNumber(str);
if (num && num.length === str.length) {
descs.push(this.evaluateCharacter(num.number));
return descs;
}
const split = BaseUtil.removeEmpty(str.replace(/\s/g, ' ').split(' '));
for (let i = 0, s; (s = split[i]); i++) {
if (s.length === 1) {
descs.push(this.evaluateCharacter(s));
}
else if (s.match(new RegExp('^[' + LOCALE.MESSAGES.regexp.TEXT + ']+$'))) {
descs.push(this.evaluateCharacter(s));
}
else {
let rest = s;
while (rest) {
num = this.matchNumber(rest);
const alpha = rest.match(new RegExp('^[' + LOCALE.MESSAGES.regexp.TEXT + ']+'));
if (num) {
descs.push(this.evaluateCharacter(num.number));
rest = rest.substring(num.length);
}
else if (alpha) {
descs.push(this.evaluateCharacter(alpha[0]));
rest = rest.substring(alpha[0].length);
}
else {
const chars = Array.from(rest);
const chr = chars[0];
descs.push(this.evaluateCharacter(chr));
rest = chars.slice(1).join('');
}
}
}
}
return descs;
}
parse(ruleSet) {
super.parse(ruleSet);
this.annotators = ruleSet['annotators'] || [];
}
addAlias_(rule, query, cstrList) {
const prec = this.parsePrecondition(query, cstrList);
const newRule = new SpeechRule(rule.name, rule.dynamicCstr, prec, rule.action);
newRule.name = rule.name;
this.addRule(newRule);
}
matchNumber(str) {
const locNum = str.match(new RegExp('^' + LOCALE.MESSAGES.regexp.NUMBER));
const enNum = str.match(new RegExp('^' + MathStore.regexp.NUMBER));
if (!locNum && !enNum) {
return null;
}
const isEn = enNum && enNum[0] === str;
const isLoc = (locNum && locNum[0] === str) || !isEn;
if (isLoc) {
return locNum ? { number: locNum[0], length: locNum[0].length } : null;
}
const num = enNum[0]
.replace(new RegExp(MathStore.regexp.DIGIT_GROUP, 'g'), 'X')
.replace(new RegExp(MathStore.regexp.DECIMAL_MARK, 'g'), LOCALE.MESSAGES.regexp.DECIMAL_MARK)
.replace(/X/g, LOCALE.MESSAGES.regexp.DIGIT_GROUP.replace(/\\/g, ''));
return { number: num, length: enNum[0].length };
}
}
MathStore.regexp = {
NUMBER: '((\\d{1,3})(?=(,| ))((,| )\\d{3})*(\\.\\d+)?)|^\\d*\\.\\d+|^\\d+',
DECIMAL_MARK: '\\.',
DIGIT_GROUP: ','
};

View File

@@ -0,0 +1,71 @@
import { SREError } from '../common/engine.js';
import { DynamicCstr } from './dynamic_cstr.js';
import * as Grammar from './grammar.js';
import { SpeechRuleContext } from './speech_rule_context.js';
export declare class SpeechRule {
name: string;
dynamicCstr: DynamicCstr;
precondition: Precondition;
action: Action;
context: SpeechRuleContext;
constructor(name: string, dynamicCstr: DynamicCstr, precondition: Precondition, action: Action);
toString(): string;
}
export declare enum ActionType {
NODE = "NODE",
MULTI = "MULTI",
TEXT = "TEXT",
PERSONALITY = "PERSONALITY"
}
interface ComponentType {
type: ActionType;
content?: string;
attributes?: Attributes;
grammar?: Grammar.State;
}
export declare class Component {
type: ActionType;
content: string;
attributes: Attributes;
grammar: Grammar.State;
static grammarFromString(grammar: string): Grammar.State;
static fromString(input: string): Component;
static attributesFromString(attrs: string): {
[key: string]: string | Grammar.State;
};
constructor({ type, content, attributes, grammar }: ComponentType);
toString(): string;
grammarToString(): string;
getGrammar(): string[];
attributesToString(): string;
getAttributes(): string[];
}
type Attributes = {
[key: string]: string;
};
export declare class Action {
components: Component[];
static fromString(input: string): Action;
private static naiveSpan;
private static addNaiveSpan;
constructor(components: Component[]);
toString(): string;
}
export declare class Precondition {
query: string;
private static queryPriorities;
private static attributePriorities;
constraints: string[];
priority: number;
rank: number;
private static constraintValue;
toString(): string;
constructor(query: string, ...cstr: string[]);
private calculatePriority;
private presetPriority;
}
export declare class OutputError extends SREError {
name: string;
constructor(msg: string);
}
export {};

View File

@@ -0,0 +1,330 @@
import { SREError } from '../common/engine.js';
import * as Grammar from './grammar.js';
export class SpeechRule {
constructor(name, dynamicCstr, precondition, action) {
this.name = name;
this.dynamicCstr = dynamicCstr;
this.precondition = precondition;
this.action = action;
this.context = null;
}
toString() {
return (this.name +
' | ' +
this.dynamicCstr.toString() +
' | ' +
this.precondition.toString() +
' ==> ' +
this.action.toString());
}
}
export var ActionType;
(function (ActionType) {
ActionType["NODE"] = "NODE";
ActionType["MULTI"] = "MULTI";
ActionType["TEXT"] = "TEXT";
ActionType["PERSONALITY"] = "PERSONALITY";
})(ActionType || (ActionType = {}));
function actionFromString(str) {
switch (str) {
case '[n]':
return ActionType.NODE;
case '[m]':
return ActionType.MULTI;
case '[t]':
return ActionType.TEXT;
case '[p]':
return ActionType.PERSONALITY;
default:
throw 'Parse error: ' + str;
}
}
function actionToString(speechType) {
switch (speechType) {
case ActionType.NODE:
return '[n]';
case ActionType.MULTI:
return '[m]';
case ActionType.TEXT:
return '[t]';
case ActionType.PERSONALITY:
return '[p]';
default:
throw 'Unknown type error: ' + speechType;
}
}
export class Component {
static grammarFromString(grammar) {
return Grammar.Grammar.parseInput(grammar);
}
static fromString(input) {
const output = {
type: actionFromString(input.substring(0, 3))
};
let rest = input.slice(3).trim();
if (!rest) {
throw new OutputError('Missing content.');
}
switch (output.type) {
case ActionType.TEXT:
if (rest[0] === '"') {
const quotedString = splitString(rest, '\\(')[0].trim();
if (quotedString.slice(-1) !== '"') {
throw new OutputError('Invalid string syntax.');
}
output.content = quotedString;
rest = rest.slice(quotedString.length).trim();
if (rest.indexOf('(') === -1) {
rest = '';
}
break;
}
case ActionType.NODE:
case ActionType.MULTI:
{
const bracket = rest.indexOf(' (');
if (bracket === -1) {
output.content = rest.trim();
rest = '';
break;
}
output.content = rest.substring(0, bracket).trim();
rest = rest.slice(bracket).trim();
}
break;
}
if (rest) {
const attributes = Component.attributesFromString(rest);
if (attributes.grammar) {
output.grammar = attributes.grammar;
delete attributes.grammar;
}
if (Object.keys(attributes).length) {
output.attributes = attributes;
}
}
return new Component(output);
}
static attributesFromString(attrs) {
if (attrs[0] !== '(' || attrs.slice(-1) !== ')') {
throw new OutputError('Invalid attribute expression: ' + attrs);
}
const attributes = {};
const attribs = splitString(attrs.slice(1, -1), ',');
for (const attr of attribs) {
const colon = attr.indexOf(':');
if (colon === -1) {
attributes[attr.trim()] = 'true';
}
else {
const key = attr.substring(0, colon).trim();
const value = attr.slice(colon + 1).trim();
attributes[key] =
key === Grammar.ATTRIBUTE
? Component.grammarFromString(value)
: value;
}
}
return attributes;
}
constructor({ type, content, attributes, grammar }) {
this.type = type;
this.content = content;
this.attributes = attributes;
this.grammar = grammar;
}
toString() {
let strs = '';
strs += actionToString(this.type);
strs += this.content ? ' ' + this.content : '';
const attrs = this.attributesToString();
strs += attrs ? ' ' + attrs : '';
return strs;
}
grammarToString() {
return this.getGrammar().join(':');
}
getGrammar() {
if (!this.grammar) {
return [];
}
const attribs = [];
for (const [key, val] of Object.entries(this.grammar)) {
attribs.push(val === true ? key : val === false ? `!${key}` : `${key}=${val}`);
}
return attribs;
}
attributesToString() {
const attribs = this.getAttributes();
const grammar = this.grammarToString();
if (grammar) {
attribs.push('grammar:' + grammar);
}
return attribs.length > 0 ? '(' + attribs.join(', ') + ')' : '';
}
getAttributes() {
if (!this.attributes) {
return [];
}
const attribs = [];
for (const [key, val] of Object.entries(this.attributes)) {
attribs.push(val === 'true' ? key : `${key}:${val}`);
}
return attribs;
}
}
export class Action {
static fromString(input) {
const comps = splitString(input, ';')
.filter(function (x) {
return x.match(/\S/);
})
.map(function (x) {
return x.trim();
});
const newComps = [];
for (let i = 0, m = comps.length; i < m; i++) {
const comp = Component.fromString(comps[i]);
if (comp) {
newComps.push(comp);
}
}
Action.naiveSpan(newComps);
return new Action(newComps);
}
static naiveSpan(comps) {
var _a;
let first = false;
for (let i = 0, comp; (comp = comps[i]); i++) {
if (first &&
(comp.type !== ActionType.TEXT ||
(comp.content[0] !== '"' && !comp.content.match(/^CSF/))))
continue;
if (!first && comp.type === ActionType.PERSONALITY)
continue;
if (!first) {
first = true;
continue;
}
if ((_a = comp.attributes) === null || _a === void 0 ? void 0 : _a.span)
continue;
const next = comps[i + 1];
if (next && next.type !== ActionType.NODE)
continue;
Action.addNaiveSpan(comp, next ? next.content : 'LAST');
}
}
static addNaiveSpan(comp, span) {
if (!comp.attributes) {
comp.attributes = {};
}
comp.attributes['span'] = span;
}
constructor(components) {
this.components = components;
}
toString() {
const comps = this.components.map(function (c) {
return c.toString();
});
return comps.join('; ');
}
}
export class Precondition {
static constraintValue(constr, priorities) {
for (let i = 0, regexp; (regexp = priorities[i]); i++) {
if (constr.match(regexp)) {
return ++i;
}
}
return 0;
}
toString() {
const constrs = this.constraints.join(', ');
return `${this.query}, ${constrs} (${this.priority}, ${this.rank})`;
}
constructor(query, ...cstr) {
this.query = query;
this.constraints = cstr;
const [exists, user] = this.presetPriority();
this.priority = exists ? user : this.calculatePriority();
}
calculatePriority() {
const query = Precondition.constraintValue(this.query, Precondition.queryPriorities);
if (!query) {
return 0;
}
const match = this.query.match(/^self::.+\[(.+)\]/);
let attr = 0;
if ((match === null || match === void 0 ? void 0 : match.length) && match[1]) {
const inner = match[1];
attr = Precondition.constraintValue(inner, Precondition.attributePriorities);
}
return query * 100 + attr * 10;
}
presetPriority() {
if (!this.constraints.length) {
return [false, 0];
}
const last = this.constraints[this.constraints.length - 1].match(/^priority=(.*$)/);
if (!last) {
return [false, 0];
}
this.constraints.pop();
const numb = parseFloat(last[1]);
return [true, isNaN(numb) ? 0 : numb];
}
}
Precondition.queryPriorities = [
/^self::\*$/,
/^self::[\w-]+$/,
/^self::\*\[.+\]$/,
/^self::[\w-]+\[.+\]$/
];
Precondition.attributePriorities = [
/^@[\w-]+$/,
/^@[\w-]+!=".+"$/,
/^not\(contains\(@[\w-]+,\s*".+"\)\)$/,
/^contains\(@[\w-]+,".+"\)$/,
/^@[\w-]+=".+"$/
];
export class OutputError extends SREError {
constructor(msg) {
super(msg);
this.name = 'RuleError';
}
}
function splitString(str, sep) {
const strList = [];
let prefix = '';
while (str !== '') {
const sepPos = str.search(sep);
if (sepPos === -1) {
if ((str.match(/"/g) || []).length % 2 !== 0) {
throw new OutputError('Invalid string in expression: ' + str);
}
strList.push(prefix + str);
prefix = '';
str = '';
}
else if ((str.substring(0, sepPos).match(/"/g) || []).length % 2 === 0) {
strList.push(prefix + str.substring(0, sepPos));
prefix = '';
str = str.substring(sepPos + 1);
}
else {
const nextQuot = str.substring(sepPos).search('"');
if (nextQuot === -1) {
throw new OutputError('Invalid string in expression: ' + str);
}
else {
prefix = prefix + str.substring(0, sepPos + nextQuot + 1);
str = str.substring(sepPos + nextQuot + 1);
}
}
}
if (prefix) {
strList.push(prefix);
}
return strList;
}

View File

@@ -0,0 +1,18 @@
import { Span, SpanAttrs } from '../audio/span.js';
import * as srf from './speech_rule_functions.js';
export declare class SpeechRuleContext {
customQueries: srf.CustomQueries;
customStrings: srf.CustomStrings;
contextFunctions: srf.ContextFunctions;
customGenerators: srf.CustomGenerators;
applyCustomQuery(node: Element, funcName: string): Element[];
applySelector(node: Element, expr: string): Element[];
applyQuery(node: Element, expr: string): Element;
applyConstraint(node: Element, expr: string): boolean;
constructString(node: Element, expr: string): string;
constructSpan(node: Element, expr: string, def: SpanAttrs): Span[];
private constructString_;
parse(functions: [string, srf.SpeechRuleFunction][] | {
[key: string]: srf.SpeechRuleFunction;
}): void;
}

View File

@@ -0,0 +1,84 @@
import { Span } from '../audio/span.js';
import * as XpathUtil from '../common/xpath_util.js';
import * as srf from './speech_rule_functions.js';
export class SpeechRuleContext {
constructor() {
this.customQueries = new srf.CustomQueries();
this.customStrings = new srf.CustomStrings();
this.contextFunctions = new srf.ContextFunctions();
this.customGenerators = new srf.CustomGenerators();
}
applyCustomQuery(node, funcName) {
const func = this.customQueries.lookup(funcName);
return func ? func(node) : null;
}
applySelector(node, expr) {
const result = this.applyCustomQuery(node, expr);
return result || XpathUtil.evalXPath(expr, node);
}
applyQuery(node, expr) {
const results = this.applySelector(node, expr);
if (results.length > 0) {
return results[0];
}
return null;
}
applyConstraint(node, expr) {
const result = this.applyQuery(node, expr);
return !!result || XpathUtil.evaluateBoolean(expr, node);
}
constructString(node, expr) {
const result = this.constructString_(node, expr);
return Array.isArray(result)
? result.map((x) => x.speech).join('')
: result;
}
constructSpan(node, expr, def) {
const result = this.constructString_(node, expr);
if (Array.isArray(result)) {
const last = result[result.length - 1];
last.attributes = Object.assign({}, def, last.attributes);
return result;
}
else {
return [Span.node(result, node, def)];
}
}
constructString_(node, expr) {
if (!expr) {
return '';
}
if (expr.charAt(0) === '"') {
return expr.slice(1, -1);
}
const func = this.customStrings.lookup(expr);
if (func) {
return func(node);
}
return XpathUtil.evaluateString(expr, node);
}
parse(functions) {
const functs = Array.isArray(functions)
? functions
: Object.entries(functions);
for (const func of functs) {
const kind = func[0].slice(0, 3);
switch (kind) {
case 'CQF':
this.customQueries.add(func[0], func[1]);
break;
case 'CSF':
this.customStrings.add(func[0], func[1]);
break;
case 'CTF':
this.contextFunctions.add(func[0], func[1]);
break;
case 'CGF':
this.customGenerators.add(func[0], func[1]);
break;
default:
console.error('FunctionError: Invalid function name ' + func[0]);
}
}
}
}

View File

@@ -0,0 +1,43 @@
import { AuditoryDescription } from '../audio/auditory_description.js';
import { BaseRuleStore } from './base_rule_store.js';
import { RulesJson } from './base_rule_store.js';
import { DynamicCstr } from './dynamic_cstr.js';
import { State as GrammarState } from './grammar.js';
import { SpeechRule } from './speech_rule.js';
import { SpeechRuleContext } from './speech_rule_context.js';
import { Trie } from '../indexing/trie.js';
export declare class SpeechRuleEngine {
private static instance;
trie: Trie;
private evaluators_;
static getInstance(): SpeechRuleEngine;
static debugSpeechRule(rule: SpeechRule, node: Element): void;
static debugNamedSpeechRule(name: string, node: Element): void;
evaluateNode(node: Element): AuditoryDescription[];
toString(): string;
runInSetting(settings: {
[feature: string]: string | boolean;
}, callback: () => AuditoryDescription[]): AuditoryDescription[];
static addStore(set: RulesJson): void;
processGrammar(context: SpeechRuleContext, node: Element, grammar: GrammarState): void;
addEvaluator(store: BaseRuleStore): void;
getEvaluator(locale: string, modality: string): (p1: Element) => AuditoryDescription[];
enumerate(opt_info?: {
[key: string]: any;
}): {
[key: string]: any;
};
private constructor();
private evaluateNode_;
private evaluateTree_;
private evaluateNodeList_;
private addLayout;
private addPersonality_;
private addExternalAttributes_;
private addRelativePersonality_;
private updateConstraint_;
private makeSet_;
lookupRule(node: Element, dynamic: DynamicCstr): SpeechRule;
lookupRules(node: Element, dynamic: DynamicCstr): SpeechRule[];
private pickMostConstraint_;
}

View File

@@ -0,0 +1,474 @@
import { AuditoryDescription, AuditoryList } from '../audio/auditory_description.js';
import { Span } from '../audio/span.js';
import { Debugger } from '../common/debugger.js';
import * as DomUtil from '../common/dom_util.js';
import { Engine } from '../common/engine.js';
import * as EngineConst from '../common/engine_const.js';
import { evalXPath, updateEvaluator } from '../common/xpath_util.js';
import * as SpeechRules from '../speech_rules/speech_rules.js';
import * as SpeechRuleStores from '../speech_rules/speech_rule_stores.js';
import { BrailleStore, EuroStore } from './braille_store.js';
import { Axis, DynamicCstr } from './dynamic_cstr.js';
import { Grammar } from './grammar.js';
import { MathStore } from './math_store.js';
import { ActionType } from './speech_rule.js';
import { Trie } from '../indexing/trie.js';
export class SpeechRuleEngine {
static getInstance() {
SpeechRuleEngine.instance =
SpeechRuleEngine.instance || new SpeechRuleEngine();
return SpeechRuleEngine.instance;
}
static debugSpeechRule(rule, node) {
const prec = rule.precondition;
const queryResult = rule.context.applyQuery(node, prec.query);
Debugger.getInstance().output(prec.query, queryResult ? queryResult.toString() : queryResult);
prec.constraints.forEach((cstr) => Debugger.getInstance().output(cstr, rule.context.applyConstraint(node, cstr)));
}
static debugNamedSpeechRule(name, node) {
const rules = SpeechRuleEngine.getInstance().trie.collectRules();
const allRules = rules.filter((rule) => rule.name == name);
for (let i = 0, rule; (rule = allRules[i]); i++) {
Debugger.getInstance().output('Rule', name, 'DynamicCstr:', rule.dynamicCstr.toString(), 'number', i);
SpeechRuleEngine.debugSpeechRule(rule, node);
}
}
evaluateNode(node) {
updateEvaluator(node);
const timeIn = new Date().getTime();
let result = [];
try {
result = this.evaluateNode_(node);
}
catch (err) {
console.log(err);
console.error('Something went wrong computing speech.');
Debugger.getInstance().output(err);
}
const timeOut = new Date().getTime();
Debugger.getInstance().output('Time:', timeOut - timeIn);
return result;
}
toString() {
const allRules = this.trie.collectRules();
return allRules.map((rule) => rule.toString()).join('\n');
}
runInSetting(settings, callback) {
const engine = Engine.getInstance();
const save = {};
for (const [key, val] of Object.entries(settings)) {
save[key] = engine[key];
engine[key] = val;
}
engine.setDynamicCstr();
const result = callback();
for (const [key, val] of Object.entries(save)) {
engine[key] = val;
}
engine.setDynamicCstr();
return result;
}
static addStore(set) {
const store = storeFactory(set);
if (store.kind !== 'abstract') {
store
.getSpeechRules()
.forEach((x) => SpeechRuleEngine.getInstance().trie.addRule(x));
}
SpeechRuleEngine.getInstance().addEvaluator(store);
}
processGrammar(context, node, grammar) {
const assignment = {};
for (const [key, val] of Object.entries(grammar)) {
assignment[key] =
typeof val === 'string' ? context.constructString(node, val) : val;
}
Grammar.getInstance().pushState(assignment);
}
addEvaluator(store) {
const fun = store.evaluateDefault.bind(store);
const loc = this.evaluators_[store.locale];
if (loc) {
loc[store.modality] = fun;
return;
}
const mod = {};
mod[store.modality] = fun;
this.evaluators_[store.locale] = mod;
}
getEvaluator(locale, modality) {
const loc = this.evaluators_[locale] ||
this.evaluators_[DynamicCstr.DEFAULT_VALUES[Axis.LOCALE]];
return loc[modality] || loc[DynamicCstr.DEFAULT_VALUES[Axis.MODALITY]];
}
enumerate(opt_info) {
return this.trie.enumerate(opt_info);
}
constructor() {
this.trie = null;
this.evaluators_ = {};
this.trie = new Trie();
}
evaluateNode_(node) {
if (!node) {
return [];
}
this.updateConstraint_();
let result = this.evaluateTree_(node);
result = processAnnotations(result);
return result;
}
evaluateTree_(node) {
const engine = Engine.getInstance();
let result;
Debugger.getInstance().output(engine.mode !== EngineConst.Mode.HTTP ? node.toString() : node);
Grammar.getInstance().setAttribute(node);
const rule = this.lookupRule(node, engine.dynamicCstr);
if (!rule) {
if (engine.strict) {
return [];
}
result = this.getEvaluator(engine.locale, engine.modality)(node);
if (node.attributes) {
this.addPersonality_(result, {}, false, node);
}
return result;
}
Debugger.getInstance().generateOutput(() => [
'Apply Rule:',
rule.name,
rule.dynamicCstr.toString(),
engine.mode === EngineConst.Mode.HTTP
? DomUtil.serializeXml(node)
: node.toString()
]);
Grammar.getInstance().processSingles();
const context = rule.context;
const components = rule.action.components;
result = [];
for (let i = 0, component; (component = components[i]); i++) {
let descrs = [];
const content = component.content || '';
const attributes = component.attributes || {};
let multi = false;
if (component.grammar) {
this.processGrammar(context, node, component.grammar);
}
let saveEngine = null;
if (attributes.engine) {
saveEngine = Engine.getInstance().dynamicCstr.getComponents();
const features = Object.assign({}, saveEngine, Grammar.parseInput(attributes.engine));
Engine.getInstance().setDynamicCstr(features);
this.updateConstraint_();
}
switch (component.type) {
case ActionType.NODE:
{
const selected = context.applyQuery(node, content);
if (selected) {
descrs = this.evaluateTree_(selected);
}
}
break;
case ActionType.MULTI:
{
multi = true;
const selects = context.applySelector(node, content);
if (selects.length > 0) {
descrs = this.evaluateNodeList_(context, selects, attributes['sepFunc'], context.constructString(node, attributes['separator']), attributes['ctxtFunc'], context.constructString(node, attributes['context']));
}
}
break;
case ActionType.TEXT:
{
const xpath = attributes['span'];
let attrs = {};
if (xpath) {
const nodes = evalXPath(xpath, node);
attrs = nodes.length
? Span.getAttributes(nodes[0])
: { kind: xpath };
}
const str = context.constructSpan(node, content, attrs);
descrs = str.map(function (span) {
return AuditoryDescription.create({ text: span.speech, attributes: span.attributes }, { adjust: true });
});
}
break;
case ActionType.PERSONALITY:
default:
descrs = [AuditoryDescription.create({ text: content })];
}
if (descrs[0] && !multi) {
if (attributes['context']) {
descrs[0]['context'] =
context.constructString(node, attributes['context']) +
(descrs[0]['context'] || '');
}
if (attributes['annotation']) {
descrs[0]['annotation'] = attributes['annotation'];
}
}
this.addLayout(descrs, attributes, multi);
if (component.grammar) {
Grammar.getInstance().popState();
}
result = result.concat(this.addPersonality_(descrs, attributes, multi, node));
if (saveEngine) {
Engine.getInstance().setDynamicCstr(saveEngine);
this.updateConstraint_();
}
}
Grammar.getInstance().popState();
return result;
}
evaluateNodeList_(context, nodes, sepFunc, sepStr, ctxtFunc, ctxtStr) {
if (!nodes.length) {
return [];
}
const sep = sepStr || '';
const cont = ctxtStr || '';
const cFunc = context.contextFunctions.lookup(ctxtFunc);
const ctxtClosure = cFunc
? cFunc(nodes, cont)
: function () {
return cont;
};
const sFunc = context.contextFunctions.lookup(sepFunc);
const sepClosure = sFunc
? sFunc(nodes, sep)
: function () {
return [
AuditoryDescription.create({ text: sep }, { translate: true })
];
};
let result = [];
for (let i = 0, node; (node = nodes[i]); i++) {
const descrs = this.evaluateTree_(node);
if (descrs.length > 0) {
descrs[0]['context'] = ctxtClosure() + (descrs[0]['context'] || '');
result = result.concat(descrs);
if (i < nodes.length - 1) {
const text = sepClosure();
result = result.concat(text);
}
}
}
return result;
}
addLayout(descrs, props, _multi) {
const layout = props.layout;
if (!layout) {
return;
}
if (layout.match(/^begin/)) {
descrs.unshift(new AuditoryDescription({ text: '', layout: layout }));
return;
}
if (layout.match(/^end/)) {
descrs.push(new AuditoryDescription({ text: '', layout: layout }));
return;
}
descrs.unshift(new AuditoryDescription({ text: '', layout: `begin${layout}` }));
descrs.push(new AuditoryDescription({ text: '', layout: `end${layout}` }));
}
addPersonality_(descrs, props, multi, node) {
const personality = {};
let pause = null;
for (const key of EngineConst.personalityPropList) {
const value = props[key];
if (typeof value === 'undefined') {
continue;
}
const numeral = parseFloat(value);
const realValue = isNaN(numeral)
? value.charAt(0) === '"'
? value.slice(1, -1)
: value
: numeral;
if (key === EngineConst.personalityProps.PAUSE) {
pause = realValue;
}
else {
personality[key] = realValue;
}
}
for (let i = 0, descr; (descr = descrs[i]); i++) {
this.addRelativePersonality_(descr, personality);
this.addExternalAttributes_(descr, node);
}
if (multi && descrs.length) {
delete descrs[descrs.length - 1].personality[EngineConst.personalityProps.JOIN];
}
if (pause && descrs.length) {
const last = descrs[descrs.length - 1];
if (last.text || Object.keys(last.personality).length) {
descrs.push(AuditoryDescription.create({
text: '',
personality: { pause: pause }
}));
}
else {
last.personality[EngineConst.personalityProps.PAUSE] = pause;
}
}
return descrs;
}
addExternalAttributes_(descr, node) {
if (descr.attributes['id'] === undefined) {
descr.attributes['id'] = node.getAttribute('id');
}
if (node.hasAttributes()) {
const attrs = node.attributes;
for (let i = attrs.length - 1; i >= 0; i--) {
const key = attrs[i].name;
if (!descr.attributes[key] && key.match(/^ext/)) {
descr.attributes[key] = attrs[i].value;
}
}
}
}
addRelativePersonality_(descr, personality) {
if (!descr['personality']) {
descr['personality'] = personality;
return descr;
}
const descrPersonality = descr['personality'];
for (const [key, val] of Object.entries(personality)) {
if (descrPersonality[key] &&
typeof descrPersonality[key] == 'number' &&
typeof val == 'number') {
descrPersonality[key] = (descrPersonality[key] + val).toString();
}
else if (!descrPersonality[key]) {
descrPersonality[key] = val;
}
}
return descr;
}
updateConstraint_() {
const dynamic = Engine.getInstance().dynamicCstr;
const strict = Engine.getInstance().strict;
const trie = this.trie;
const props = {};
let locale = dynamic.getValue(Axis.LOCALE);
let modality = dynamic.getValue(Axis.MODALITY);
let domain = dynamic.getValue(Axis.DOMAIN);
if (!trie.hasSubtrie([locale, modality, domain])) {
domain = DynamicCstr.DEFAULT_VALUES[Axis.DOMAIN];
if (!trie.hasSubtrie([locale, modality, domain])) {
modality = DynamicCstr.DEFAULT_VALUES[Axis.MODALITY];
if (!trie.hasSubtrie([locale, modality, domain])) {
locale = DynamicCstr.DEFAULT_VALUES[Axis.LOCALE];
}
}
}
props[Axis.LOCALE] = [locale];
props[Axis.MODALITY] = [
modality !== 'summary'
? modality
: DynamicCstr.DEFAULT_VALUES[Axis.MODALITY]
];
props[Axis.DOMAIN] = [
modality !== 'speech' ? DynamicCstr.DEFAULT_VALUES[Axis.DOMAIN] : domain
];
const order = dynamic.getOrder();
for (let i = 0, axis; (axis = order[i]); i++) {
if (!props[axis]) {
const value = dynamic.getValue(axis);
const valueSet = this.makeSet_(value, dynamic.preference);
const def = DynamicCstr.DEFAULT_VALUES[axis];
if (!strict && value !== def) {
valueSet.push(def);
}
props[axis] = valueSet;
}
}
dynamic.updateProperties(props);
}
makeSet_(value, preferences) {
if (!preferences || !Object.keys(preferences).length) {
return [value];
}
return value.split(':');
}
lookupRule(node, dynamic) {
if (!node ||
(node.nodeType !== DomUtil.NodeType.ELEMENT_NODE &&
node.nodeType !== DomUtil.NodeType.TEXT_NODE)) {
return null;
}
const matchingRules = this.lookupRules(node, dynamic);
return matchingRules.length > 0
? this.pickMostConstraint_(dynamic, matchingRules)
: null;
}
lookupRules(node, dynamic) {
return this.trie.lookupRules(node, dynamic.allProperties());
}
pickMostConstraint_(_dynamic, rules) {
const comparator = Engine.getInstance().comparator;
rules.sort(function (r1, r2) {
return (comparator.compare(r1.dynamicCstr, r2.dynamicCstr) ||
r2.precondition.priority - r1.precondition.priority ||
r2.precondition.constraints.length -
r1.precondition.constraints.length ||
r2.precondition.rank - r1.precondition.rank);
});
Debugger.getInstance().generateOutput((() => {
return rules.map((x) => x.name + '(' + x.dynamicCstr.toString() + ')');
}).bind(this));
return rules[0];
}
}
const stores = new Map();
function getStore(locale, modality) {
if (modality === 'braille' && locale === 'euro') {
return new EuroStore();
}
if (modality === 'braille') {
return new BrailleStore();
}
return new MathStore();
}
function storeFactory(set) {
const name = `${set.locale}.${set.modality}.${set.domain}`;
if (set.kind === 'actions') {
const store = stores.get(name);
store.parse(set);
return store;
}
SpeechRuleStores.init();
if (set && !set.functions) {
set.functions = SpeechRules.getStore(set.locale, set.modality, set.domain);
}
const store = getStore(set.locale, set.modality);
stores.set(name, store);
if (set.inherits) {
store.inherits = stores.get(`${set.inherits}.${set.modality}.${set.domain}`);
}
store.parse(set);
store.initialize();
return store;
}
Engine.nodeEvaluator = SpeechRuleEngine.getInstance().evaluateNode.bind(SpeechRuleEngine.getInstance());
const punctuationMarks = ['⠆', '⠒', '⠲', '⠦', '⠴', '⠄'];
function processAnnotations(descrs) {
const alist = new AuditoryList(descrs);
for (const item of alist.annotations) {
const descr = item.data;
if (descr.annotation === 'punctuation') {
const prev = alist.prevText(item);
if (!prev)
continue;
const last = prev.data;
if (last.annotation !== 'punctuation' &&
last.text !== '' &&
descr.text.length === 1 &&
punctuationMarks.indexOf(descr.text) !== -1) {
descr.text = '⠸' + descr.text;
}
}
}
return alist.toList();
}

View File

@@ -0,0 +1,8 @@
import { AuditoryDescription } from '../audio/auditory_description.js';
export interface SpeechRuleEvaluator {
evaluateDefault(node: Node): void;
evaluateWhitespace(str: string): AuditoryDescription[];
evaluateString(str: string): AuditoryDescription[];
evaluateCustom(str: string): AuditoryDescription;
evaluateCharacter(str: string): AuditoryDescription;
}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,31 @@
import { AuditoryDescription } from '../audio/auditory_description.js';
import { Span } from '../audio/span.js';
declare abstract class FunctionsStore<S> {
private prefix;
private store;
protected constructor(prefix: string, store: {
[key: string]: S;
});
add(name: string, func: S): void;
addStore(store: FunctionsStore<S>): void;
lookup(name: string): S;
private checkCustomFunctionSyntax_;
}
export type CustomQuery = (p1: Element) => Element[];
export declare class CustomQueries extends FunctionsStore<CustomQuery> {
constructor();
}
export type CustomString = (p1: Element) => Span[];
export declare class CustomStrings extends FunctionsStore<CustomString> {
constructor();
}
export type ContextFunction = (p1: Element[] | Element, p2: string | null) => () => string | AuditoryDescription[];
export declare class ContextFunctions extends FunctionsStore<ContextFunction> {
constructor();
}
export type CustomGenerator = (store?: any, flag?: boolean) => string[] | void;
export declare class CustomGenerators extends FunctionsStore<CustomGenerator> {
constructor();
}
export type SpeechRuleFunction = CustomQuery | CustomString | ContextFunction | CustomGenerator;
export {};

View File

@@ -0,0 +1,52 @@
class FunctionsStore {
constructor(prefix, store) {
this.prefix = prefix;
this.store = store;
}
add(name, func) {
if (this.checkCustomFunctionSyntax_(name)) {
this.store[name] = func;
}
}
addStore(store) {
const keys = Object.keys(store.store);
for (let i = 0, key; (key = keys[i]); i++) {
this.add(key, store.store[key]);
}
}
lookup(name) {
return this.store[name];
}
checkCustomFunctionSyntax_(name) {
const reg = new RegExp('^' + this.prefix);
if (!name.match(reg)) {
console.error('FunctionError: Invalid function name. Expected prefix ' + this.prefix);
return false;
}
return true;
}
}
export class CustomQueries extends FunctionsStore {
constructor() {
const store = {};
super('CQF', store);
}
}
export class CustomStrings extends FunctionsStore {
constructor() {
const store = {};
super('CSF', store);
}
}
export class ContextFunctions extends FunctionsStore {
constructor() {
const store = {};
super('CTF', store);
}
}
export class CustomGenerators extends FunctionsStore {
constructor() {
const store = {};
super('CGF', store);
}
}

View File

@@ -0,0 +1,10 @@
import { SpeechRule } from './speech_rule.js';
import { SpeechRuleContext } from './speech_rule_context.js';
export interface SpeechRuleStore {
context: SpeechRuleContext;
addRule(rule: SpeechRule): void;
deleteRule(rule: SpeechRule): void;
findRule(pred: (rule: SpeechRule) => boolean): SpeechRule;
findAllRules(pred: (rule: SpeechRule) => boolean): SpeechRule[];
defineRule(name: string, dynamic: string, action: string, pre: string, ...args: string[]): SpeechRule;
}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,4 @@
import { AuditoryDescription } from '../audio/auditory_description.js';
export declare function nodeCounter(nodes: Element[], context: string | null): () => string;
export declare function pauseSeparator(_nodes: Element[], context: string): () => AuditoryDescription[];
export declare function contentIterator(nodes: Element[], context: string): () => AuditoryDescription[];

View File

@@ -0,0 +1,49 @@
import { AuditoryDescription } from '../audio/auditory_description.js';
import * as XpathUtil from '../common/xpath_util.js';
import { Engine } from '../common/engine.js';
export function nodeCounter(nodes, context) {
const localLength = nodes.length;
let localCounter = 0;
let localContext = context;
if (!context) {
localContext = '';
}
return function () {
if (localCounter < localLength) {
localCounter += 1;
}
return localContext + ' ' + localCounter;
};
}
export function pauseSeparator(_nodes, context) {
const numeral = parseFloat(context);
const value = isNaN(numeral) ? context : numeral;
return function () {
return [
AuditoryDescription.create({
text: '',
personality: { pause: value }
})
];
};
}
export function contentIterator(nodes, context) {
let contentNodes;
if (nodes.length > 0) {
contentNodes = XpathUtil.evalXPath('../../content/*', nodes[0]);
}
else {
contentNodes = [];
}
return function () {
const content = contentNodes.shift();
const contextDescr = context
? [AuditoryDescription.create({ text: context }, { translate: true })]
: [];
if (!content) {
return contextDescr;
}
const descrs = Engine.evaluateNode(content);
return contextDescr.concat(descrs);
};
}