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,60 @@
export declare enum Font {
BOLD = "bold",
BOLDFRAKTUR = "bold-fraktur",
BOLDITALIC = "bold-italic",
BOLDSCRIPT = "bold-script",
DOUBLESTRUCK = "double-struck",
DOUBLESTRUCKITALIC = "double-struck-italic",
FULLWIDTH = "fullwidth",
FRAKTUR = "fraktur",
ITALIC = "italic",
MONOSPACE = "monospace",
NORMAL = "normal",
SCRIPT = "script",
SANSSERIF = "sans-serif",
SANSSERIFITALIC = "sans-serif-italic",
SANSSERIFBOLD = "sans-serif-bold",
SANSSERIFBOLDITALIC = "sans-serif-bold-italic"
}
export declare enum Embellish {
SUPER = "super",
SUB = "sub",
CIRCLED = "circled",
PARENTHESIZED = "parenthesized",
PERIOD = "period",
NEGATIVECIRCLED = "negative-circled",
DOUBLECIRCLED = "double-circled",
CIRCLEDSANSSERIF = "circled-sans-serif",
NEGATIVECIRCLEDSANSSERIF = "negative-circled-sans-serif",
COMMA = "comma",
SQUARED = "squared",
NEGATIVESQUARED = "negative-squared"
}
export declare enum Base {
LATINCAP = "latinCap",
LATINSMALL = "latinSmall",
GREEKCAP = "greekCap",
GREEKSMALL = "greekSmall",
DIGIT = "digit"
}
export declare function makeInterval([a, b]: [string, string], subst: {
[key: string]: string | boolean;
}): string[];
export declare function makeMultiInterval(ints: (string | [string, string])[]): string[];
export declare function makeCodeInterval(ints: (string | [string, string])[]): number[];
export declare interface ProtoAlphabet {
interval: [string, string];
base: Base;
subst: {
[key: string]: string | boolean;
};
category: string;
font: Font | Embellish;
capital?: boolean;
offset?: number;
}
export declare interface Alphabet extends ProtoAlphabet {
unicode: string[];
}
export declare const INTERVALS: Map<string, Alphabet>;
export declare function alphabetName(base: string, font: string): string;

View File

@@ -0,0 +1,692 @@
export var Font;
(function (Font) {
Font["BOLD"] = "bold";
Font["BOLDFRAKTUR"] = "bold-fraktur";
Font["BOLDITALIC"] = "bold-italic";
Font["BOLDSCRIPT"] = "bold-script";
Font["DOUBLESTRUCK"] = "double-struck";
Font["DOUBLESTRUCKITALIC"] = "double-struck-italic";
Font["FULLWIDTH"] = "fullwidth";
Font["FRAKTUR"] = "fraktur";
Font["ITALIC"] = "italic";
Font["MONOSPACE"] = "monospace";
Font["NORMAL"] = "normal";
Font["SCRIPT"] = "script";
Font["SANSSERIF"] = "sans-serif";
Font["SANSSERIFITALIC"] = "sans-serif-italic";
Font["SANSSERIFBOLD"] = "sans-serif-bold";
Font["SANSSERIFBOLDITALIC"] = "sans-serif-bold-italic";
})(Font || (Font = {}));
export var Embellish;
(function (Embellish) {
Embellish["SUPER"] = "super";
Embellish["SUB"] = "sub";
Embellish["CIRCLED"] = "circled";
Embellish["PARENTHESIZED"] = "parenthesized";
Embellish["PERIOD"] = "period";
Embellish["NEGATIVECIRCLED"] = "negative-circled";
Embellish["DOUBLECIRCLED"] = "double-circled";
Embellish["CIRCLEDSANSSERIF"] = "circled-sans-serif";
Embellish["NEGATIVECIRCLEDSANSSERIF"] = "negative-circled-sans-serif";
Embellish["COMMA"] = "comma";
Embellish["SQUARED"] = "squared";
Embellish["NEGATIVESQUARED"] = "negative-squared";
})(Embellish || (Embellish = {}));
export var Base;
(function (Base) {
Base["LATINCAP"] = "latinCap";
Base["LATINSMALL"] = "latinSmall";
Base["GREEKCAP"] = "greekCap";
Base["GREEKSMALL"] = "greekSmall";
Base["DIGIT"] = "digit";
})(Base || (Base = {}));
function num2str(num) {
const str = num.toString(16).toUpperCase();
return str.length > 3 ? str : ('000' + str).slice(-4);
}
export function makeInterval([a, b], subst) {
const start = parseInt(a, 16);
const end = parseInt(b, 16);
const result = [];
for (let i = start; i <= end; i++) {
let key = num2str(i);
const sub = subst[key];
if (sub === false) {
continue;
}
key = subst[key] || key;
result.push(key);
}
return result;
}
function makeCharInterval(int, subst = {}) {
return makeInterval(int, subst).map((x) => String.fromCodePoint(parseInt(x, 16)));
}
export function makeMultiInterval(ints) {
let result = [];
for (const int of ints) {
if (Array.isArray(int)) {
result = result.concat(makeCharInterval(int));
continue;
}
result.push(String.fromCodePoint(parseInt(int, 16)));
}
return result;
}
export function makeCodeInterval(ints) {
let result = [];
for (const int of ints) {
if (Array.isArray(int)) {
result = result.concat(makeInterval(int, {}).map((x) => parseInt(x, 16)));
continue;
}
result.push(parseInt(int, 16));
}
return result;
}
const PROTO_INTERVALS = [
{
interval: ['1D400', '1D419'],
base: Base.LATINCAP,
subst: {},
capital: true,
category: 'Lu',
font: Font.BOLD
},
{
interval: ['1D41A', '1D433'],
base: Base.LATINSMALL,
subst: {},
capital: false,
category: 'Ll',
font: Font.BOLD
},
{
interval: ['1D56C', '1D585'],
base: Base.LATINCAP,
subst: {},
capital: true,
category: 'Lu',
font: Font.BOLDFRAKTUR
},
{
interval: ['1D586', '1D59F'],
base: Base.LATINSMALL,
subst: {},
capital: false,
category: 'Ll',
font: Font.BOLDFRAKTUR
},
{
interval: ['1D468', '1D481'],
base: Base.LATINCAP,
subst: {},
capital: true,
category: 'Lu',
font: Font.BOLDITALIC
},
{
interval: ['1D482', '1D49B'],
base: Base.LATINSMALL,
subst: {},
capital: false,
category: 'Ll',
font: Font.BOLDITALIC
},
{
interval: ['1D4D0', '1D4E9'],
base: Base.LATINCAP,
subst: {},
capital: true,
category: 'Lu',
font: Font.BOLDSCRIPT
},
{
interval: ['1D4EA', '1D503'],
base: Base.LATINSMALL,
subst: {},
capital: false,
category: 'Ll',
font: Font.BOLDSCRIPT
},
{
interval: ['1D538', '1D551'],
base: Base.LATINCAP,
subst: {
'1D53A': '2102',
'1D53F': '210D',
'1D545': '2115',
'1D547': '2119',
'1D548': '211A',
'1D549': '211D',
'1D551': '2124'
},
capital: true,
category: 'Lu',
font: Font.DOUBLESTRUCK
},
{
interval: ['1D552', '1D56B'],
base: Base.LATINSMALL,
subst: {},
capital: false,
category: 'Ll',
font: Font.DOUBLESTRUCK
},
{
interval: ['1D504', '1D51D'],
base: Base.LATINCAP,
subst: {
'1D506': '212D',
'1D50B': '210C',
'1D50C': '2111',
'1D515': '211C',
'1D51D': '2128'
},
capital: true,
category: 'Lu',
font: Font.FRAKTUR
},
{
interval: ['1D51E', '1D537'],
base: Base.LATINSMALL,
subst: {},
capital: false,
category: 'Ll',
font: Font.FRAKTUR
},
{
interval: ['FF21', 'FF3A'],
base: Base.LATINCAP,
subst: {},
capital: true,
category: 'Lu',
font: Font.FULLWIDTH
},
{
interval: ['FF41', 'FF5A'],
base: Base.LATINSMALL,
subst: {},
capital: false,
category: 'Ll',
font: Font.FULLWIDTH
},
{
interval: ['1D434', '1D44D'],
base: Base.LATINCAP,
subst: {},
capital: true,
category: 'Lu',
font: Font.ITALIC
},
{
interval: ['1D44E', '1D467'],
base: Base.LATINSMALL,
subst: { '1D455': '210E' },
capital: false,
category: 'Ll',
font: Font.ITALIC
},
{
interval: ['1D670', '1D689'],
base: Base.LATINCAP,
subst: {},
capital: true,
category: 'Lu',
font: Font.MONOSPACE
},
{
interval: ['1D68A', '1D6A3'],
base: Base.LATINSMALL,
subst: {},
capital: false,
category: 'Ll',
font: Font.MONOSPACE
},
{
interval: ['0041', '005A'],
base: Base.LATINCAP,
subst: {},
capital: true,
category: 'Lu',
font: Font.NORMAL
},
{
interval: ['0061', '007A'],
base: Base.LATINSMALL,
subst: {},
capital: false,
category: 'Ll',
font: Font.NORMAL
},
{
interval: ['1D49C', '1D4B5'],
base: Base.LATINCAP,
subst: {
'1D49D': '212C',
'1D4A0': '2130',
'1D4A1': '2131',
'1D4A3': '210B',
'1D4A4': '2110',
'1D4A7': '2112',
'1D4A8': '2133',
'1D4AD': '211B'
},
capital: true,
category: 'Lu',
font: Font.SCRIPT
},
{
interval: ['1D4B6', '1D4CF'],
base: Base.LATINSMALL,
subst: { '1D4BA': '212F', '1D4BC': '210A', '1D4C4': '2134' },
capital: false,
category: 'Ll',
font: Font.SCRIPT
},
{
interval: ['1D5A0', '1D5B9'],
base: Base.LATINCAP,
subst: {},
capital: true,
category: 'Lu',
font: Font.SANSSERIF
},
{
interval: ['1D5BA', '1D5D3'],
base: Base.LATINSMALL,
subst: {},
capital: false,
category: 'Ll',
font: Font.SANSSERIF
},
{
interval: ['1D608', '1D621'],
base: Base.LATINCAP,
subst: {},
capital: true,
category: 'Lu',
font: Font.SANSSERIFITALIC
},
{
interval: ['1D622', '1D63B'],
base: Base.LATINSMALL,
subst: {},
capital: false,
category: 'Ll',
font: Font.SANSSERIFITALIC
},
{
interval: ['1D5D4', '1D5ED'],
base: Base.LATINCAP,
subst: {},
capital: true,
category: 'Lu',
font: Font.SANSSERIFBOLD
},
{
interval: ['1D5EE', '1D607'],
base: Base.LATINSMALL,
subst: {},
capital: false,
category: 'Ll',
font: Font.SANSSERIFBOLD
},
{
interval: ['1D63C', '1D655'],
base: Base.LATINCAP,
subst: {},
capital: true,
category: 'Lu',
font: Font.SANSSERIFBOLDITALIC
},
{
interval: ['1D656', '1D66F'],
base: Base.LATINSMALL,
subst: {},
capital: false,
category: 'Ll',
font: Font.SANSSERIFBOLDITALIC
},
{
interval: ['0391', '03A9'],
base: Base.GREEKCAP,
subst: { '03A2': '03F4' },
capital: true,
category: 'Lu',
font: Font.NORMAL
},
{
interval: ['03B0', '03D0'],
base: Base.GREEKSMALL,
subst: {
'03B0': '2207',
'03CA': '2202',
'03CB': '03F5',
'03CC': '03D1',
'03CD': '03F0',
'03CE': '03D5',
'03CF': '03F1',
'03D0': '03D6'
},
capital: false,
category: 'Ll',
font: Font.NORMAL
},
{
interval: ['1D6A8', '1D6C0'],
base: Base.GREEKCAP,
subst: {},
capital: true,
category: 'Lu',
font: Font.BOLD
},
{
interval: ['1D6C1', '1D6E1'],
base: Base.GREEKSMALL,
subst: {},
capital: false,
category: 'Ll',
font: Font.BOLD
},
{
interval: ['1D6E2', '1D6FA'],
base: Base.GREEKCAP,
subst: {},
capital: true,
category: 'Lu',
font: Font.ITALIC
},
{
interval: ['1D6FB', '1D71B'],
base: Base.GREEKSMALL,
subst: {},
capital: false,
category: 'Ll',
font: Font.ITALIC
},
{
interval: ['1D71C', '1D734'],
base: Base.GREEKCAP,
subst: {},
capital: true,
category: 'Lu',
font: Font.BOLDITALIC
},
{
interval: ['1D735', '1D755'],
base: Base.GREEKSMALL,
subst: {},
capital: false,
category: 'Ll',
font: Font.BOLDITALIC
},
{
interval: ['1D756', '1D76E'],
base: Base.GREEKCAP,
subst: {},
capital: true,
category: 'Lu',
font: Font.SANSSERIFBOLD
},
{
interval: ['1D76F', '1D78F'],
base: Base.GREEKSMALL,
subst: {},
capital: false,
category: 'Ll',
font: Font.SANSSERIFBOLD
},
{
interval: ['1D790', '1D7A8'],
base: Base.GREEKCAP,
subst: {},
capital: true,
category: 'Lu',
font: Font.SANSSERIFBOLDITALIC
},
{
interval: ['1D7A9', '1D7C9'],
base: Base.GREEKSMALL,
subst: {},
capital: false,
category: 'Ll',
font: Font.SANSSERIFBOLDITALIC
},
{
interval: ['0030', '0039'],
base: Base.DIGIT,
subst: {},
offset: 0,
category: 'Nd',
font: Font.NORMAL
},
{
interval: ['2070', '2079'],
base: Base.DIGIT,
subst: { 2071: '00B9', 2072: '00B2', 2073: '00B3' },
offset: 0,
category: 'No',
font: Embellish.SUPER
},
{
interval: ['2080', '2089'],
base: Base.DIGIT,
subst: {},
offset: 0,
category: 'No',
font: Embellish.SUB
},
{
interval: ['245F', '2473'],
base: Base.DIGIT,
subst: { '245F': '24EA' },
offset: 0,
category: 'No',
font: Embellish.CIRCLED
},
{
interval: ['3251', '325F'],
base: Base.DIGIT,
subst: {},
offset: 21,
category: 'No',
font: Embellish.CIRCLED
},
{
interval: ['32B1', '32BF'],
base: Base.DIGIT,
subst: {},
offset: 36,
category: 'No',
font: Embellish.CIRCLED
},
{
interval: ['2474', '2487'],
base: Base.DIGIT,
subst: {},
offset: 1,
category: 'No',
font: Embellish.PARENTHESIZED
},
{
interval: ['2487', '249B'],
base: Base.DIGIT,
subst: { 2487: '1F100' },
offset: 0,
category: 'No',
font: Embellish.PERIOD
},
{
interval: ['2775', '277F'],
base: Base.DIGIT,
subst: { 2775: '24FF' },
offset: 0,
category: 'No',
font: Embellish.NEGATIVECIRCLED
},
{
interval: ['24EB', '24F4'],
base: Base.DIGIT,
subst: {},
offset: 11,
category: 'No',
font: Embellish.NEGATIVECIRCLED
},
{
interval: ['24F5', '24FE'],
base: Base.DIGIT,
subst: {},
offset: 1,
category: 'No',
font: Embellish.DOUBLECIRCLED
},
{
interval: ['277F', '2789'],
base: Base.DIGIT,
subst: { '277F': '1F10B' },
offset: 0,
category: 'No',
font: Embellish.CIRCLEDSANSSERIF
},
{
interval: ['2789', '2793'],
base: Base.DIGIT,
subst: { 2789: '1F10C' },
offset: 0,
category: 'No',
font: Embellish.NEGATIVECIRCLEDSANSSERIF
},
{
interval: ['FF10', 'FF19'],
base: Base.DIGIT,
subst: {},
offset: 0,
category: 'Nd',
font: Font.FULLWIDTH
},
{
interval: ['1D7CE', '1D7D7'],
base: Base.DIGIT,
subst: {},
offset: 0,
category: 'Nd',
font: Font.BOLD
},
{
interval: ['1D7D8', '1D7E1'],
base: Base.DIGIT,
subst: {},
offset: 0,
category: 'Nd',
font: Font.DOUBLESTRUCK
},
{
interval: ['1D7E2', '1D7EB'],
base: Base.DIGIT,
subst: {},
offset: 0,
category: 'Nd',
font: Font.SANSSERIF
},
{
interval: ['1D7EC', '1D7F5'],
base: Base.DIGIT,
subst: {},
offset: 0,
category: 'Nd',
font: Font.SANSSERIFBOLD
},
{
interval: ['1D7F6', '1D7FF'],
base: Base.DIGIT,
subst: {},
offset: 0,
category: 'Nd',
font: Font.MONOSPACE
},
{
interval: ['1F101', '1F10A'],
base: Base.DIGIT,
subst: {},
offset: 0,
category: 'No',
font: Embellish.COMMA
},
{
interval: ['24B6', '24CF'],
base: Base.LATINCAP,
subst: {},
capital: true,
category: 'So',
font: Embellish.CIRCLED
},
{
interval: ['24D0', '24E9'],
base: Base.LATINSMALL,
subst: {},
capital: false,
category: 'So',
font: Embellish.CIRCLED
},
{
interval: ['1F110', '1F129'],
base: Base.LATINCAP,
subst: {},
capital: true,
category: 'So',
font: Embellish.PARENTHESIZED
},
{
interval: ['249C', '24B5'],
base: Base.LATINSMALL,
subst: {},
capital: false,
category: 'So',
font: Embellish.PARENTHESIZED
},
{
interval: ['1F130', '1F149'],
base: Base.LATINCAP,
subst: {},
capital: true,
category: 'So',
font: Embellish.SQUARED
},
{
interval: ['1F170', '1F189'],
base: Base.LATINCAP,
subst: {},
capital: true,
category: 'So',
font: Embellish.NEGATIVESQUARED
},
{
interval: ['1F150', '1F169'],
base: Base.LATINCAP,
subst: {},
capital: true,
category: 'So',
font: Embellish.NEGATIVECIRCLED
}
];
export const INTERVALS = new Map();
export function alphabetName(base, font) {
const capFont = font
.split('-')
.map((x) => x[0].toUpperCase() + x.slice(1))
.join('');
return base + capFont;
}
for (const proto of PROTO_INTERVALS) {
const key = alphabetName(proto.base, proto.font);
const interval = makeCharInterval(proto.interval, proto.subst);
let alphabet = INTERVALS.get(key);
if (alphabet) {
alphabet.unicode = alphabet.unicode.concat(interval);
continue;
}
alphabet = proto;
alphabet.unicode = interval;
INTERVALS.set(key, alphabet);
}

View File

@@ -0,0 +1,2 @@
export declare function generateBase(): void;
export declare function generate(locale: string): void;

View File

@@ -0,0 +1,84 @@
import * as Alphabet from './alphabet.js';
import { Engine } from '../common/engine.js';
import * as L10n from '../l10n/l10n.js';
import { LOCALE } from '../l10n/locale.js';
import { localeFontCombiner } from '../l10n/locale_util.js';
import * as MathCompoundStore from '../rule_engine/math_compound_store.js';
const Domains = {
small: ['default'],
capital: ['default'],
digit: ['default']
};
function makeDomains() {
const alph = LOCALE.ALPHABETS;
const combineKeys = (obj1, obj2) => {
const result = {};
Object.keys(obj1).forEach((k) => (result[k] = true));
Object.keys(obj2).forEach((k) => (result[k] = true));
return Object.keys(result);
};
Domains.small = combineKeys(alph.smallPrefix, alph.letterTrans);
Domains.capital = combineKeys(alph.capPrefix, alph.letterTrans);
Domains.digit = combineKeys(alph.digitPrefix, alph.digitTrans);
}
export function generateBase() {
for (const int of Alphabet.INTERVALS.values()) {
const letters = int.unicode;
for (const letter of letters) {
MathCompoundStore.baseStores.set(letter, {
key: letter,
category: int.category
});
}
}
}
export function generate(locale) {
const oldLocale = Engine.getInstance().locale;
Engine.getInstance().locale = locale;
L10n.setLocale();
MathCompoundStore.changeLocale({ locale: locale });
makeDomains();
for (const int of Alphabet.INTERVALS.values()) {
const letters = int.unicode;
if ('offset' in int) {
numberRules(letters, int.font, int.offset || 0);
}
else {
const alphabet = LOCALE.ALPHABETS[int.base];
alphabetRules(letters, alphabet, int.font, !!int.capital);
}
}
Engine.getInstance().locale = oldLocale;
L10n.setLocale();
}
function getFont(font) {
const realFont = font === 'normal' || font === 'fullwidth'
? ''
: LOCALE.MESSAGES.font[font] || LOCALE.MESSAGES.embellish[font] || '';
return localeFontCombiner(realFont);
}
function alphabetRules(unicodes, letters, font, cap) {
const realFont = getFont(font);
for (let i = 0, unicode, letter; (unicode = unicodes[i]), (letter = letters[i]); i++) {
const prefixes = cap
? LOCALE.ALPHABETS.capPrefix
: LOCALE.ALPHABETS.smallPrefix;
const domains = cap ? Domains.capital : Domains.small;
makeLetter(realFont.combiner, unicode, letter, realFont.font, prefixes, LOCALE.ALPHABETS.letterTrans, domains);
}
}
function numberRules(unicodes, font, offset) {
const realFont = getFont(font);
for (let i = 0, unicode; (unicode = unicodes[i]); i++) {
const prefixes = LOCALE.ALPHABETS.digitPrefix;
const num = i + offset;
makeLetter(realFont.combiner, unicode, num, realFont.font, prefixes, LOCALE.ALPHABETS.digitTrans, Domains.digit);
}
}
function makeLetter(combiner, unicode, letter, font, prefixes, transformers, domains) {
for (let i = 0, domain; (domain = domains[i]); i++) {
const transformer = domain in transformers ? transformers[domain] : transformers['default'];
const prefix = domain in prefixes ? prefixes[domain] : prefixes['default'];
MathCompoundStore.defineRule(domain, 'default', unicode, combiner(transformer(letter), font, prefix));
}
}

View File

@@ -0,0 +1,33 @@
import { DynamicCstr } from '../rule_engine/dynamic_cstr.js';
import { AxisMap, AxisProperties, DefaultComparator, DynamicProperties } from '../rule_engine/dynamic_cstr.js';
import { SemanticNode } from '../semantic_tree/semantic_node.js';
export declare class ClearspeakPreferences extends DynamicCstr {
preference: {
[key: string]: string;
};
private static AUTO;
static comparator(): Comparator;
static fromPreference(pref: string): AxisMap;
static toPreference(pref: AxisMap): string;
static getLocalePreferences(opt_dynamic?: {
[key: string]: AxisProperties;
}): {
[key: string]: AxisProperties;
};
static currentPreference(): string;
static relevantPreferences(node: SemanticNode): string;
static findPreference(prefs: string, kind: string): string;
static addPreference(prefs: string, kind: string, value: string): string;
private static getLocalePreferences_;
constructor(cstr: AxisMap, preference: {
[key: string]: string;
});
equal(cstr: ClearspeakPreferences): boolean;
}
declare class Comparator extends DefaultComparator {
preference: AxisMap;
constructor(cstr: DynamicCstr, props: DynamicProperties);
match(cstr: DynamicCstr): boolean;
compare(cstr1: DynamicCstr, cstr2: DynamicCstr): 0 | 1 | -1;
}
export {};

View File

@@ -0,0 +1,319 @@
import { Engine } from '../common/engine.js';
import * as EngineConst from '../common/engine_const.js';
import { DynamicCstr } from '../rule_engine/dynamic_cstr.js';
import { Axis, DefaultComparator, DynamicCstrParser, DynamicProperties } from '../rule_engine/dynamic_cstr.js';
import * as MathCompoundStore from '../rule_engine/math_compound_store.js';
import { SpeechRuleEngine } from '../rule_engine/speech_rule_engine.js';
import { SemanticRole, SemanticType } from '../semantic_tree/semantic_meaning.js';
export class ClearspeakPreferences extends DynamicCstr {
static comparator() {
return new Comparator(Engine.getInstance().dynamicCstr, DynamicProperties.createProp([DynamicCstr.DEFAULT_VALUES[Axis.LOCALE]], [DynamicCstr.DEFAULT_VALUES[Axis.MODALITY]], [DynamicCstr.DEFAULT_VALUES[Axis.DOMAIN]], [DynamicCstr.DEFAULT_VALUES[Axis.STYLE]]));
}
static fromPreference(pref) {
const pairs = pref.split(':');
const preferences = {};
const properties = PREFERENCES.getProperties();
const validKeys = Object.keys(properties);
for (let i = 0, key; (key = pairs[i]); i++) {
const pair = key.split('_');
if (validKeys.indexOf(pair[0]) === -1) {
continue;
}
const value = pair[1];
if (value &&
value !== ClearspeakPreferences.AUTO &&
properties[pair[0]].indexOf(value) !== -1) {
preferences[pair[0]] = pair[1];
}
}
return preferences;
}
static toPreference(pref) {
const keys = Object.keys(pref);
const str = [];
for (let i = 0; i < keys.length; i++) {
str.push(keys[i] + '_' + pref[keys[i]]);
}
return str.length ? str.join(':') : DynamicCstr.DEFAULT_VALUE;
}
static getLocalePreferences(opt_dynamic) {
const dynamic = opt_dynamic ||
MathCompoundStore.enumerate(SpeechRuleEngine.getInstance().enumerate());
return ClearspeakPreferences.getLocalePreferences_(dynamic);
}
static currentPreference() {
return EngineConst.DOMAIN_TO_STYLES['clearspeak'];
}
static relevantPreferences(node) {
const roles = SEMANTIC_MAPPING_[node.type];
if (!roles) {
return 'ImpliedTimes';
}
return roles[node.role] || roles[''] || 'ImpliedTimes';
}
static findPreference(prefs, kind) {
if (prefs === 'default') {
return ClearspeakPreferences.AUTO;
}
const parsed = ClearspeakPreferences.fromPreference(prefs);
return parsed[kind] || ClearspeakPreferences.AUTO;
}
static addPreference(prefs, kind, value) {
if (prefs === 'default') {
return kind + '_' + value;
}
const parsed = ClearspeakPreferences.fromPreference(prefs);
parsed[kind] = value;
return ClearspeakPreferences.toPreference(parsed);
}
static getLocalePreferences_(dynamic) {
const result = {};
for (const locale of Object.keys(dynamic)) {
if (!dynamic[locale]['speech'] ||
!dynamic[locale]['speech']['clearspeak']) {
continue;
}
const locPrefs = Object.keys(dynamic[locale]['speech']['clearspeak']);
if (locPrefs.length < 3)
continue;
const prefs = (result[locale] = {});
for (const axis in PREFERENCES.getProperties()) {
const allSty = PREFERENCES.getProperties()[axis];
const values = [axis + '_Auto'];
if (allSty) {
for (const sty of allSty) {
if (locPrefs.indexOf(axis + '_' + sty) !== -1) {
values.push(axis + '_' + sty);
}
}
}
prefs[axis] = values;
}
}
return result;
}
constructor(cstr, preference) {
super(cstr);
this.preference = preference;
}
equal(cstr) {
const top = super.equal(cstr);
if (!top) {
return false;
}
const keys = Object.keys(this.preference);
const preference = cstr.preference;
if (keys.length !== Object.keys(preference).length) {
return false;
}
for (let i = 0, key; (key = keys[i]); i++) {
if (this.preference[key] !== preference[key]) {
return false;
}
}
return true;
}
}
ClearspeakPreferences.AUTO = 'Auto';
const PREFERENCES = new DynamicProperties({
AbsoluteValue: ['Auto', 'AbsEnd', 'Cardinality', 'Determinant'],
Bar: ['Auto', 'Conjugate'],
Caps: ['Auto', 'SayCaps'],
CombinationPermutation: ['Auto', 'ChoosePermute'],
Currency: ['Auto', 'Position', 'Prefix'],
Ellipses: ['Auto', 'AndSoOn'],
Enclosed: ['Auto', 'EndEnclose'],
Exponent: [
'Auto',
'AfterPower',
'Ordinal',
'OrdinalPower',
'Exponent'
],
Fraction: [
'Auto',
'EndFrac',
'FracOver',
'General',
'GeneralEndFrac',
'Ordinal',
'Over',
'OverEndFrac',
'Per'
],
Functions: [
'Auto',
'None',
'Reciprocal'
],
ImpliedTimes: ['Auto', 'MoreImpliedTimes', 'None'],
Log: ['Auto', 'LnAsNaturalLog'],
Matrix: [
'Auto',
'Combinatoric',
'EndMatrix',
'EndVector',
'SilentColNum',
'SpeakColNum',
'Vector'
],
MultiLineLabel: [
'Auto',
'Case',
'Constraint',
'Equation',
'Line',
'None',
'Row',
'Step'
],
MultiLineOverview: ['Auto', 'None'],
MultiLinePausesBetweenColumns: ['Auto', 'Long', 'Short'],
MultsymbolDot: ['Auto', 'Dot'],
MultsymbolX: ['Auto', 'By', 'Cross'],
Paren: [
'Auto',
'CoordPoint',
'Interval',
'Silent',
'Speak',
'SpeakNestingLevel'
],
Prime: ['Auto', 'Angle', 'Length'],
Roots: ['Auto', 'PosNegSqRoot', 'PosNegSqRootEnd', 'RootEnd'],
SetMemberSymbol: ['Auto', 'Belongs', 'Element', 'Member', 'In'],
Sets: ['Auto', 'SilentBracket', 'woAll'],
TriangleSymbol: ['Auto', 'Delta'],
Trig: [
'Auto',
'ArcTrig',
'TrigInverse',
'Reciprocal'
],
VerticalLine: ['Auto', 'Divides', 'Given', 'SuchThat']
});
class Comparator extends DefaultComparator {
constructor(cstr, props) {
super(cstr, props);
this.preference =
cstr instanceof ClearspeakPreferences ? cstr.preference : {};
}
match(cstr) {
if (!(cstr instanceof ClearspeakPreferences)) {
return super.match(cstr);
}
if (cstr.getComponents()[Axis.STYLE] === 'default') {
return true;
}
const keys = Object.keys(cstr.preference);
for (let i = 0, key; (key = keys[i]); i++) {
if (this.preference[key] !== cstr.preference[key]) {
return false;
}
}
return true;
}
compare(cstr1, cstr2) {
const top = super.compare(cstr1, cstr2);
if (top !== 0) {
return top;
}
const pref1 = cstr1 instanceof ClearspeakPreferences;
const pref2 = cstr2 instanceof ClearspeakPreferences;
if (!pref1 && pref2) {
return 1;
}
if (pref1 && !pref2) {
return -1;
}
if (!pref1 && !pref2) {
return 0;
}
const length1 = Object.keys(cstr1.preference).length;
const length2 = Object.keys(cstr2.preference).length;
return length1 > length2 ? -1 : length1 < length2 ? 1 : 0;
}
}
class Parser extends DynamicCstrParser {
constructor() {
super([Axis.LOCALE, Axis.MODALITY, Axis.DOMAIN, Axis.STYLE]);
}
parse(str) {
const initial = super.parse(str);
let style = initial.getValue(Axis.STYLE);
const locale = initial.getValue(Axis.LOCALE);
const modality = initial.getValue(Axis.MODALITY);
let pref = {};
if (style !== DynamicCstr.DEFAULT_VALUE) {
pref = this.fromPreference(style);
style = this.toPreference(pref);
}
return new ClearspeakPreferences({
locale: locale,
modality: modality,
domain: 'clearspeak',
style: style
}, pref);
}
fromPreference(pref) {
return ClearspeakPreferences.fromPreference(pref);
}
toPreference(pref) {
return ClearspeakPreferences.toPreference(pref);
}
}
const REVERSE_MAPPING = [
[
'AbsoluteValue',
SemanticType.FENCED,
SemanticRole.NEUTRAL,
SemanticRole.METRIC
],
['Bar', SemanticType.OVERSCORE, SemanticRole.OVERACCENT],
['Caps', SemanticType.IDENTIFIER, SemanticRole.LATINLETTER],
['CombinationPermutation', SemanticType.APPL, SemanticRole.UNKNOWN],
['Ellipses', SemanticType.PUNCTUATION, SemanticRole.ELLIPSIS],
['Exponent', SemanticType.SUPERSCRIPT, ''],
['Fraction', SemanticType.FRACTION, ''],
['Functions', SemanticType.APPL, SemanticRole.SIMPLEFUNC],
['ImpliedTimes', SemanticType.OPERATOR, SemanticRole.IMPLICIT],
['Log', SemanticType.APPL, SemanticRole.PREFIXFUNC],
['Matrix', SemanticType.MATRIX, ''],
['Matrix', SemanticType.VECTOR, ''],
['MultiLineLabel', SemanticType.MULTILINE, SemanticRole.LABEL],
['MultiLineOverview', SemanticType.MULTILINE, SemanticRole.TABLE],
['MultiLinePausesBetweenColumns', SemanticType.MULTILINE, SemanticRole.TABLE],
['MultiLineLabel', SemanticType.TABLE, SemanticRole.LABEL],
['MultiLineOverview', SemanticType.TABLE, SemanticRole.TABLE],
['MultiLinePausesBetweenColumns', SemanticType.TABLE, SemanticRole.TABLE],
['MultiLineLabel', SemanticType.CASES, SemanticRole.LABEL],
['MultiLineOverview', SemanticType.CASES, SemanticRole.TABLE],
['MultiLinePausesBetweenColumns', SemanticType.CASES, SemanticRole.TABLE],
['MultsymbolDot', SemanticType.OPERATOR, SemanticRole.MULTIPLICATION],
['MultsymbolX', SemanticType.OPERATOR, SemanticRole.MULTIPLICATION],
['Paren', SemanticType.FENCED, SemanticRole.LEFTRIGHT],
['Prime', SemanticType.SUPERSCRIPT, SemanticRole.PRIME],
['Roots', SemanticType.ROOT, ''],
['Roots', SemanticType.SQRT, ''],
['SetMemberSymbol', SemanticType.RELATION, SemanticRole.ELEMENT],
['Sets', SemanticType.FENCED, SemanticRole.SETEXT],
['TriangleSymbol', SemanticType.IDENTIFIER, SemanticRole.GREEKLETTER],
['Trig', SemanticType.APPL, SemanticRole.PREFIXFUNC],
['VerticalLine', SemanticType.PUNCTUATED, SemanticRole.VBAR]
];
const SEMANTIC_MAPPING_ = (function () {
const result = {};
for (let i = 0, triple; (triple = REVERSE_MAPPING[i]); i++) {
const pref = triple[0];
let role = result[triple[1]];
if (!role) {
role = {};
result[triple[1]] = role;
}
role[triple[2]] = pref;
}
return result;
})();
Engine.getInstance().comparators['clearspeak'] =
ClearspeakPreferences.comparator;
Engine.getInstance().parsers['clearspeak'] = new Parser();

View File

@@ -0,0 +1 @@
export declare function ClearspeakRules(): void;

View File

@@ -0,0 +1,23 @@
import { DynamicCstr } from '../rule_engine/dynamic_cstr.js';
import * as StoreUtil from '../rule_engine/store_util.js';
import * as ClearspeakUtil from './clearspeak_util.js';
import * as MathspeakUtil from './mathspeak_util.js';
import * as NumbersUtil from './numbers_util.js';
import * as SpeechRules from './speech_rules.js';
export function ClearspeakRules() {
SpeechRules.addStore(DynamicCstr.BASE_LOCALE + '.speech.clearspeak', '', {
CTFpauseSeparator: StoreUtil.pauseSeparator,
CTFnodeCounter: ClearspeakUtil.nodeCounter,
CTFcontentIterator: StoreUtil.contentIterator,
CSFvulgarFraction: NumbersUtil.vulgarFraction,
CQFvulgarFractionSmall: ClearspeakUtil.isSmallVulgarFraction,
CQFcellsSimple: ClearspeakUtil.allCellsSimple,
CSFordinalExponent: ClearspeakUtil.ordinalExponent,
CSFwordOrdinal: ClearspeakUtil.wordOrdinal,
CQFmatchingFences: ClearspeakUtil.matchingFences,
CSFnestingDepth: ClearspeakUtil.nestingDepth,
CQFfencedArguments: ClearspeakUtil.fencedArguments,
CQFsimpleArguments: ClearspeakUtil.simpleArguments,
CQFspaceoutNumber: MathspeakUtil.spaceoutNumber
});
}

View File

@@ -0,0 +1,10 @@
import { Span } from '../audio/span.js';
export declare function nodeCounter(nodes: Element[], context: string | null): () => string;
export declare function allCellsSimple(node: Element): Element[];
export declare function isSmallVulgarFraction(node: Element): Element[];
export declare function ordinalExponent(node: Element): Span[];
export declare function nestingDepth(node: Element): Span[];
export declare function matchingFences(node: Element): Element[];
export declare function fencedArguments(node: Element): Element[];
export declare function simpleArguments(node: Element): Element[];
export declare function wordOrdinal(node: Element): Span[];

View File

@@ -0,0 +1,286 @@
import { Span } from '../audio/span.js';
import * as DomUtil from '../common/dom_util.js';
import { Engine } from '../common/engine.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 { register } from '../semantic_tree/semantic_annotations.js';
import { SemanticAnnotator } from '../semantic_tree/semantic_annotator.js';
import { isMatchingFence } from '../semantic_tree/semantic_attr.js';
import { SemanticRole, SemanticType } from '../semantic_tree/semantic_meaning.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 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().style === pref;
}
register(new SemanticAnnotator('clearspeak', 'simple', function (node) {
return isSimpleExpression(node) ? 'simple' : '';
}));
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] : [];
}
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' : '';
}));
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)))
];
}

View File

@@ -0,0 +1,4 @@
export declare function loadLocale(locale?: string): Promise<string>;
export declare function standardLoader(): typeof loadFile;
declare function loadFile(locale: string): Promise<string>;
export {};

View File

@@ -0,0 +1,172 @@
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import * as BrowserUtil from '../common/browser_util.js';
import { Engine, EnginePromise } from '../common/engine.js';
import * as EngineConst from '../common/engine_const.js';
import * as FileUtil from '../common/file_util.js';
import { SystemExternal } from '../common/system_external.js';
import { DynamicCstr } from '../rule_engine/dynamic_cstr.js';
import * as MathCompoundStore from '../rule_engine/math_compound_store.js';
import { SpeechRuleEngine } from '../rule_engine/speech_rule_engine.js';
import { completeLocale } from '../l10n/l10n.js';
import * as AlphabetGenerator from './alphabet_generator.js';
const addSymbols = {
functions: MathCompoundStore.addFunctionRules,
symbols: MathCompoundStore.addSymbolRules,
units: MathCompoundStore.addUnitRules,
si: (x) => x.forEach(MathCompoundStore.setSiPrefixes),
messages: completeLocale,
rules: SpeechRuleEngine.addStore,
characters: MathCompoundStore.addCharacterRules
};
let _init = false;
export function loadLocale() {
return __awaiter(this, arguments, void 0, function* (locale = Engine.getInstance().locale) {
if (!_init) {
AlphabetGenerator.generateBase();
_loadLocale(DynamicCstr.BASE_LOCALE);
_init = true;
}
return EnginePromise.promises[DynamicCstr.BASE_LOCALE].then(() => __awaiter(this, void 0, void 0, function* () {
const defLoc = Engine.getInstance().defaultLocale;
if (defLoc) {
_loadLocale(defLoc);
return EnginePromise.promises[defLoc].then(() => __awaiter(this, void 0, void 0, function* () {
_loadLocale(locale);
return EnginePromise.promises[locale];
}));
}
_loadLocale(locale);
return EnginePromise.promises[locale];
}));
});
}
function _loadLocale(locale = Engine.getInstance().locale) {
if (!EnginePromise.loaded[locale]) {
EnginePromise.loaded[locale] = [false, false];
MathCompoundStore.reset();
retrieveMaps(locale);
}
}
function loadMethod() {
if (Engine.getInstance().customLoader) {
return Engine.getInstance().customLoader;
}
return standardLoader();
}
export function standardLoader() {
switch (Engine.getInstance().mode) {
case EngineConst.Mode.ASYNC:
return loadFile;
case EngineConst.Mode.HTTP:
return loadAjax;
case EngineConst.Mode.SYNC:
default:
return loadFileSync;
}
}
function retrieveFiles(locale) {
const loader = loadMethod();
const promise = new Promise((res) => {
const inner = loader(locale);
inner.then((str) => {
parseMaps(str);
EnginePromise.loaded[locale] = [true, true];
res(locale);
}, (_err) => {
EnginePromise.loaded[locale] = [true, false];
console.error(`Unable to load locale: ${locale}`);
Engine.getInstance().locale = Engine.getInstance().defaultLocale;
res(locale);
});
});
EnginePromise.promises[locale] = promise;
}
function parseMaps(json) {
const js = typeof json === 'string'
? JSON.parse(json)
: json;
addMaps(js);
}
function addMaps(json, opt_locale) {
let generate = true;
for (let i = 0, key; (key = Object.keys(json)[i]); i++) {
const info = key.split('/');
if (opt_locale && opt_locale !== info[0]) {
continue;
}
if (generate && info[1] === 'symbols' && info[0] !== 'base') {
AlphabetGenerator.generate(info[0]);
generate = false;
}
addSymbols[info[1]](json[key]);
}
}
function retrieveMaps(locale) {
if (Engine.getInstance().isIE &&
Engine.getInstance().mode === EngineConst.Mode.HTTP) {
getJsonIE_(locale);
return;
}
retrieveFiles(locale);
}
function getJsonIE_(locale, opt_count) {
let count = opt_count || 1;
if (!BrowserUtil.mapsForIE) {
if (count <= 5) {
setTimeout((() => getJsonIE_(locale, count++)).bind(this), 300);
}
return;
}
addMaps(BrowserUtil.mapsForIE, locale);
}
function loadFile(locale) {
const file = FileUtil.localePath(locale);
return new Promise((res, rej) => {
SystemExternal.fs.readFile(file, 'utf8', (err, json) => {
if (err) {
return rej(err);
}
res(json);
});
});
}
function loadFileSync(locale) {
const file = FileUtil.localePath(locale);
return new Promise((res, rej) => {
let str = '{}';
try {
str = SystemExternal.fs.readFileSync(file, 'utf8');
}
catch (err) {
return rej(err);
}
res(str);
});
}
function loadAjax(locale) {
const file = FileUtil.localePath(locale);
const httpRequest = new XMLHttpRequest();
return new Promise((res, rej) => {
httpRequest.onreadystatechange = function () {
if (httpRequest.readyState === 4) {
const status = httpRequest.status;
if (status === 0 || (status >= 200 && status < 400)) {
res(httpRequest.responseText);
}
else {
rej(status);
}
}
};
httpRequest.open('GET', file, true);
httpRequest.send();
});
}

View File

@@ -0,0 +1,7 @@
import { Span } from '../audio/span.js';
export declare function baselineVerbose(node: Element): Span[];
export declare function baselineBrief(node: Element): Span[];
export declare function leftSuperscriptVerbose(node: Element): Span[];
export declare function leftSubscriptVerbose(node: Element): Span[];
export declare function leftSuperscriptBrief(node: Element): Span[];
export declare function leftSubscriptBrief(node: Element): Span[];

View File

@@ -0,0 +1,31 @@
import * as MathspeakUtil from './mathspeak_util.js';
export function baselineVerbose(node) {
const baseline = MathspeakUtil.baselineVerbose(node);
baseline[0].speech = baseline[0].speech.replace(/-$/, '');
return baseline;
}
export function baselineBrief(node) {
const baseline = MathspeakUtil.baselineBrief(node);
baseline[0].speech = baseline[0].speech.replace(/-$/, '');
return baseline;
}
export function leftSuperscriptVerbose(node) {
const leftIndex = MathspeakUtil.superscriptVerbose(node);
leftIndex[0].speech = leftIndex[0].speech.replace(/^exposant/, 'exposant gauche');
return leftIndex;
}
export function leftSubscriptVerbose(node) {
const leftIndex = MathspeakUtil.subscriptVerbose(node);
leftIndex[0].speech = leftIndex[0].speech.replace(/^indice/, 'indice gauche');
return leftIndex;
}
export function leftSuperscriptBrief(node) {
const leftIndex = MathspeakUtil.superscriptBrief(node);
leftIndex[0].speech = leftIndex[0].speech.replace(/^sup/, 'sup gauche');
return leftIndex;
}
export function leftSubscriptBrief(node) {
const leftIndex = MathspeakUtil.subscriptBrief(node);
leftIndex[0].speech = leftIndex[0].speech.replace(/^sub/, 'sub gauche');
return leftIndex;
}

View File

@@ -0,0 +1,26 @@
import { Span } from '../audio/span.js';
export declare function nestedFraction(node: Element, expr: string, opt_end?: string): string;
export declare function openingFractionVerbose(node: Element): Span[];
export declare function closingFractionVerbose(node: Element): Span[];
export declare function openingFractionBrief(node: Element): Span[];
export declare function closingFractionBrief(node: Element): Span[];
export declare function openingFractionSbrief(node: Element): Span[];
export declare function closingFractionSbrief(node: Element): Span[];
export declare function overFractionSbrief(node: Element): Span[];
export declare function isSimpleIndex(node: Element): Element[];
export declare function nestedRadical(node: Element, prefix: string, postfix: string): string;
export declare function openingRadicalVerbose(node: Element): Span[];
export declare function closingRadicalVerbose(node: Element): Span[];
export declare function openingRadicalBrief(node: Element): Span[];
export declare function closingRadicalBrief(node: Element): Span[];
export declare function openingRadicalSbrief(node: Element): Span[];
export declare function getRootIndex(node: Element): string;
export declare function indexRadical(node: Element, postfix: string): string;
export declare function indexRadicalVerbose(node: Element): Span[];
export declare function indexRadicalBrief(node: Element): Span[];
export declare function indexRadicalSbrief(node: Element): Span[];
export declare function ordinalConversion(node: Element): Span[];
export declare function decreasedOrdinalConversion(node: Element): Span[];
export declare function listOrdinalConversion(node: Element): Span[];
export declare function checkDepth(node: Element): Element[];
export declare function getDepthValue(node: Element, roleList: string[]): number;

View File

@@ -0,0 +1,124 @@
import { Span } from '../audio/span.js';
import * as MathspeakUtil from './mathspeak_util.js';
import { LOCALE } from '../l10n/locale.js';
import * as XpathUtil from '../common/xpath_util.js';
export function nestedFraction(node, expr, opt_end) {
const depth = MathspeakUtil.fractionNestingDepth(node);
const annotation = [...Array(depth)].map((_x) => expr);
if (opt_end) {
annotation.unshift(opt_end);
}
return annotation.join(LOCALE.MESSAGES.regexp.JOINER_FRAC);
}
export function openingFractionVerbose(node) {
return Span.singleton(nestedFraction(node, LOCALE.MESSAGES.MS.START, LOCALE.MESSAGES.MS.FRAC_V));
}
export function closingFractionVerbose(node) {
return Span.singleton(nestedFraction(node, LOCALE.MESSAGES.MS.END, LOCALE.MESSAGES.MS.FRAC_V));
}
export function openingFractionBrief(node) {
return Span.singleton(nestedFraction(node, LOCALE.MESSAGES.MS.START, LOCALE.MESSAGES.MS.FRAC_B));
}
export function closingFractionBrief(node) {
return Span.singleton(nestedFraction(node, LOCALE.MESSAGES.MS.END, LOCALE.MESSAGES.MS.FRAC_B));
}
export function openingFractionSbrief(node) {
const depth = MathspeakUtil.fractionNestingDepth(node);
if (depth === 1) {
return Span.singleton(LOCALE.MESSAGES.MS.FRAC_S);
}
return Span.singleton(LOCALE.FUNCTIONS.combineNestedFraction(LOCALE.FUNCTIONS.radicalNestDepth(depth - 1), LOCALE.MESSAGES.MS.NEST_FRAC, LOCALE.MESSAGES.MS.FRAC_S));
}
export function closingFractionSbrief(node) {
const depth = MathspeakUtil.fractionNestingDepth(node);
if (depth === 1) {
return Span.singleton(LOCALE.MESSAGES.MS.ENDFRAC);
}
return Span.singleton(LOCALE.FUNCTIONS.combineNestedFraction(LOCALE.FUNCTIONS.radicalNestDepth(depth - 1), LOCALE.MESSAGES.MS.NEST_FRAC, LOCALE.MESSAGES.MS.ENDFRAC));
}
export function overFractionSbrief(node) {
const depth = MathspeakUtil.fractionNestingDepth(node);
if (depth === 1) {
return Span.singleton(LOCALE.MESSAGES.MS.FRAC_OVER);
}
return Span.singleton(LOCALE.FUNCTIONS.combineNestedFraction(LOCALE.FUNCTIONS.radicalNestDepth(depth - 1), LOCALE.MESSAGES.MS.NEST_FRAC, LOCALE.MESSAGES.MS.FRAC_OVER));
}
export function isSimpleIndex(node) {
const index = XpathUtil.evalXPath('children/*[1]', node)[0]
.toString()
.match(/[^>>]+<\/[^>]*>/g);
return index.length === 1 ? [node] : [];
}
export function nestedRadical(node, prefix, postfix) {
const depth = MathspeakUtil.radicalNestingDepth(node);
if (depth === 1)
return postfix;
return LOCALE.FUNCTIONS.combineNestedRadical(LOCALE.FUNCTIONS.radicalNestDepth(depth - 1), prefix, postfix);
}
export function openingRadicalVerbose(node) {
return Span.singleton(nestedRadical(node, LOCALE.MESSAGES.MS.NESTED, LOCALE.MESSAGES.MS.STARTROOT));
}
export function closingRadicalVerbose(node) {
return Span.singleton(nestedRadical(node, LOCALE.MESSAGES.MS.NESTED, LOCALE.MESSAGES.MS.ENDROOT));
}
export function openingRadicalBrief(node) {
return Span.singleton(nestedRadical(node, LOCALE.MESSAGES.MS.NEST_ROOT, LOCALE.MESSAGES.MS.STARTROOT));
}
export function closingRadicalBrief(node) {
return Span.singleton(nestedRadical(node, LOCALE.MESSAGES.MS.NEST_ROOT, LOCALE.MESSAGES.MS.ENDROOT));
}
export function openingRadicalSbrief(node) {
return Span.singleton(nestedRadical(node, LOCALE.MESSAGES.MS.NEST_ROOT, LOCALE.MESSAGES.MS.ROOT));
}
export function getRootIndex(node) {
const content = XpathUtil.evalXPath('children/*[1]', node)[0].textContent.trim();
return LOCALE.MESSAGES.MSroots[content] || content + '제곱근';
}
export function indexRadical(node, postfix) {
const index = getRootIndex(node);
return index ? index : postfix;
}
export function indexRadicalVerbose(node) {
return Span.singleton(indexRadical(node, LOCALE.MESSAGES.MS.ROOTINDEX));
}
export function indexRadicalBrief(node) {
return Span.singleton(indexRadical(node, LOCALE.MESSAGES.MS.ROOTINDEX));
}
export function indexRadicalSbrief(node) {
return Span.singleton(indexRadical(node, LOCALE.MESSAGES.MS.INDEX));
}
export function ordinalConversion(node) {
const children = XpathUtil.evalXPath('children/*', node);
return Span.singleton(LOCALE.NUMBERS.wordOrdinal(children.length));
}
export function decreasedOrdinalConversion(node) {
const children = XpathUtil.evalXPath('children/*', node);
return Span.singleton(LOCALE.NUMBERS.wordOrdinal(children.length - 1));
}
export function listOrdinalConversion(node) {
const children = XpathUtil.evalXPath('children/*', node);
const content = XpathUtil.evalXPath('content/*', node);
return Span.singleton(LOCALE.NUMBERS.wordOrdinal(children.length - content.length));
}
export function checkDepth(node) {
const roleList = [];
const depth = getDepthValue(node, roleList);
return depth > 3 ? [] : [node];
}
export function getDepthValue(node, roleList) {
const role = node.getAttribute('role');
const index = roleList.indexOf(role) > -1;
if (!index) {
roleList.push(role);
}
const children = XpathUtil.evalXPath('children/*', node);
let max = 0, cur = 0;
if (children.length) {
children.forEach((child) => {
cur = getDepthValue(child, roleList);
max = cur > max ? cur : max;
});
return max + 1;
}
return 0;
}

View File

@@ -0,0 +1 @@
export declare function MathspeakRules(): void;

View File

@@ -0,0 +1,89 @@
import { DynamicCstr } from '../rule_engine/dynamic_cstr.js';
import * as StoreUtil from '../rule_engine/store_util.js';
import * as MathspeakFrenchUtil from './mathspeak_french_util.js';
import * as MathspeakKoreanUtil from './mathspeak_korean_util.js';
import * as MathspeakUtil from './mathspeak_util.js';
import * as NumbersUtil from './numbers_util.js';
import * as SpeechRules from './speech_rules.js';
import * as UnitUtil from './unit_util.js';
export function MathspeakRules() {
SpeechRules.addStore(DynamicCstr.BASE_LOCALE + '.speech.mathspeak', '', {
CQFspaceoutNumber: MathspeakUtil.spaceoutNumber,
CQFspaceoutIdentifier: MathspeakUtil.spaceoutIdentifier,
CSFspaceoutText: MathspeakUtil.spaceoutText,
CSFopenFracVerbose: MathspeakUtil.openingFractionVerbose,
CSFcloseFracVerbose: MathspeakUtil.closingFractionVerbose,
CSFoverFracVerbose: MathspeakUtil.overFractionVerbose,
CSFopenFracBrief: MathspeakUtil.openingFractionBrief,
CSFcloseFracBrief: MathspeakUtil.closingFractionBrief,
CSFopenFracSbrief: MathspeakUtil.openingFractionSbrief,
CSFcloseFracSbrief: MathspeakUtil.closingFractionSbrief,
CSFoverFracSbrief: MathspeakUtil.overFractionSbrief,
CSFvulgarFraction: NumbersUtil.vulgarFraction,
CQFvulgarFractionSmall: MathspeakUtil.isSmallVulgarFraction,
CSFopenRadicalVerbose: MathspeakUtil.openingRadicalVerbose,
CSFcloseRadicalVerbose: MathspeakUtil.closingRadicalVerbose,
CSFindexRadicalVerbose: MathspeakUtil.indexRadicalVerbose,
CSFopenRadicalBrief: MathspeakUtil.openingRadicalBrief,
CSFcloseRadicalBrief: MathspeakUtil.closingRadicalBrief,
CSFindexRadicalBrief: MathspeakUtil.indexRadicalBrief,
CSFopenRadicalSbrief: MathspeakUtil.openingRadicalSbrief,
CSFindexRadicalSbrief: MathspeakUtil.indexRadicalSbrief,
CQFisSmallRoot: MathspeakUtil.smallRoot,
CSFsuperscriptVerbose: MathspeakUtil.superscriptVerbose,
CSFsuperscriptBrief: MathspeakUtil.superscriptBrief,
CSFsubscriptVerbose: MathspeakUtil.subscriptVerbose,
CSFsubscriptBrief: MathspeakUtil.subscriptBrief,
CSFbaselineVerbose: MathspeakUtil.baselineVerbose,
CSFbaselineBrief: MathspeakUtil.baselineBrief,
CSFleftsuperscriptVerbose: MathspeakUtil.superscriptVerbose,
CSFleftsubscriptVerbose: MathspeakUtil.subscriptVerbose,
CSFrightsuperscriptVerbose: MathspeakUtil.superscriptVerbose,
CSFrightsubscriptVerbose: MathspeakUtil.subscriptVerbose,
CSFleftsuperscriptBrief: MathspeakUtil.superscriptBrief,
CSFleftsubscriptBrief: MathspeakUtil.subscriptBrief,
CSFrightsuperscriptBrief: MathspeakUtil.superscriptBrief,
CSFrightsubscriptBrief: MathspeakUtil.subscriptBrief,
CSFunderscript: MathspeakUtil.nestedUnderscript,
CSFoverscript: MathspeakUtil.nestedOverscript,
CSFendscripts: MathspeakUtil.endscripts,
CTFordinalCounter: NumbersUtil.ordinalCounter,
CTFwordCounter: NumbersUtil.wordCounter,
CTFcontentIterator: StoreUtil.contentIterator,
CQFdetIsSimple: MathspeakUtil.determinantIsSimple,
CSFRemoveParens: MathspeakUtil.removeParens,
CQFresetNesting: MathspeakUtil.resetNestingDepth,
CGFbaselineConstraint: MathspeakUtil.generateBaselineConstraint,
CGFtensorRules: MathspeakUtil.generateTensorRules
});
SpeechRules.addStore('es.speech.mathspeak', DynamicCstr.BASE_LOCALE + '.speech.mathspeak', {
CTFunitMultipliers: UnitUtil.unitMultipliers,
CQFoneLeft: UnitUtil.oneLeft
});
SpeechRules.addStore('fr.speech.mathspeak', DynamicCstr.BASE_LOCALE + '.speech.mathspeak', {
CSFbaselineVerbose: MathspeakFrenchUtil.baselineVerbose,
CSFbaselineBrief: MathspeakFrenchUtil.baselineBrief,
CSFleftsuperscriptVerbose: MathspeakFrenchUtil.leftSuperscriptVerbose,
CSFleftsubscriptVerbose: MathspeakFrenchUtil.leftSubscriptVerbose,
CSFleftsuperscriptBrief: MathspeakFrenchUtil.leftSuperscriptBrief,
CSFleftsubscriptBrief: MathspeakFrenchUtil.leftSubscriptBrief
});
SpeechRules.addStore('ko.speech.mathspeak', DynamicCstr.BASE_LOCALE + '.speech.mathspeak', {
CSFopenFracVerbose: MathspeakKoreanUtil.openingFractionVerbose,
CSFcloseFracVerbose: MathspeakKoreanUtil.closingFractionVerbose,
CSFopenFracBrief: MathspeakKoreanUtil.openingFractionBrief,
CSFcloseFracBrief: MathspeakKoreanUtil.closingFractionBrief,
CSFopenFracSbrief: MathspeakKoreanUtil.openingFractionSbrief,
CSFoverFracSbrief: MathspeakKoreanUtil.overFractionSbrief,
CSFcloseFracSbrief: MathspeakKoreanUtil.closingFractionSbrief,
CQFisSimpleIndex: MathspeakKoreanUtil.isSimpleIndex,
CSFindexRadicalVerbose: MathspeakKoreanUtil.indexRadicalVerbose,
CSFindexRadicalBrief: MathspeakKoreanUtil.indexRadicalBrief,
CSFindexRadicalSbrief: MathspeakKoreanUtil.indexRadicalSbrief,
CSFopenRadicalVerbose: MathspeakKoreanUtil.openingRadicalVerbose,
CSFcloseRadicalVerbose: MathspeakKoreanUtil.closingRadicalVerbose,
CSFopenRadicalBrief: MathspeakKoreanUtil.openingRadicalBrief,
CSFcloseRadicalBrief: MathspeakKoreanUtil.closingRadicalBrief,
CSFopenRadicalSbrief: MathspeakKoreanUtil.openingRadicalSbrief
});
}

View File

@@ -0,0 +1,43 @@
import { Span } from '../audio/span.js';
import { SpeechRuleStore } from '../rule_engine/speech_rule_store.js';
export declare function spaceoutText(node: Element): Span[];
export declare function spaceoutNumber(node: Element): Element[];
export declare function spaceoutIdentifier(node: Element): Element[];
export declare function resetNestingDepth(node: Element): Element[];
export declare function fractionNestingDepth(node: Element): number;
export declare function openingFractionVerbose(node: Element): Span[];
export declare function closingFractionVerbose(node: Element): Span[];
export declare function overFractionVerbose(node: Element): Span[];
export declare function openingFractionBrief(node: Element): Span[];
export declare function closingFractionBrief(node: Element): Span[];
export declare function openingFractionSbrief(node: Element): Span[];
export declare function closingFractionSbrief(node: Element): Span[];
export declare function overFractionSbrief(node: Element): Span[];
export declare function isSmallVulgarFraction(node: Element): Element[];
export declare function nestedSubSuper(node: Element, init: string, replace: {
sup: string;
sub: string;
}): string;
export declare function subscriptVerbose(node: Element): Span[];
export declare function subscriptBrief(node: Element): Span[];
export declare function superscriptVerbose(node: Element): Span[];
export declare function superscriptBrief(node: Element): Span[];
export declare function baselineVerbose(node: Element): Span[];
export declare function baselineBrief(node: Element): Span[];
export declare function radicalNestingDepth(node: Element): number;
export declare function openingRadicalVerbose(node: Element): Span[];
export declare function closingRadicalVerbose(node: Element): Span[];
export declare function indexRadicalVerbose(node: Element): Span[];
export declare function openingRadicalBrief(node: Element): Span[];
export declare function closingRadicalBrief(node: Element): Span[];
export declare function indexRadicalBrief(node: Element): Span[];
export declare function openingRadicalSbrief(node: Element): Span[];
export declare function indexRadicalSbrief(node: Element): Span[];
export declare function nestedUnderscript(node: Element): Span[];
export declare function endscripts(_node: Element): Span[];
export declare function nestedOverscript(node: Element): Span[];
export declare function determinantIsSimple(node: Element): Element[];
export declare function generateBaselineConstraint(): string[];
export declare function removeParens(node: Element): Span[];
export declare function generateTensorRules(store: SpeechRuleStore, brief?: boolean): void;
export declare function smallRoot(node: Element): Element[];

View File

@@ -0,0 +1,442 @@
import { Span } from '../audio/span.js';
import * as BaseUtil from '../common/base_util.js';
import * as DomUtil from '../common/dom_util.js';
import * as XpathUtil from '../common/xpath_util.js';
import { LOCALE } from '../l10n/locale.js';
import { SemanticFont, SemanticRole, SemanticType } from '../semantic_tree/semantic_meaning.js';
import { SemanticProcessor } from '../semantic_tree/semantic_processor.js';
let nestingDepth = {};
export function spaceoutText(node) {
return Array.from(node.textContent).map(Span.stringEmpty);
}
function spaceoutNodes(node, correction) {
const content = Array.from(node.textContent);
const result = [];
const processor = SemanticProcessor.getInstance();
const doc = node.ownerDocument;
for (let i = 0, chr; (chr = content[i]); i++) {
const leaf = processor
.getNodeFactory()
.makeLeafNode(chr, SemanticFont.UNKNOWN);
const sn = processor.identifierNode(leaf, SemanticFont.UNKNOWN, '');
correction(sn);
result.push(sn.xml(doc));
}
return result;
}
export function spaceoutNumber(node) {
return spaceoutNodes(node, function (sn) {
if (!sn.textContent.match(/\W/)) {
sn.type = SemanticType.NUMBER;
}
});
}
export function spaceoutIdentifier(node) {
return spaceoutNodes(node, function (sn) {
sn.font = SemanticFont.UNKNOWN;
sn.type = SemanticType.IDENTIFIER;
});
}
const nestingBarriers = [
SemanticType.CASES,
SemanticType.CELL,
SemanticType.INTEGRAL,
SemanticType.LINE,
SemanticType.MATRIX,
SemanticType.MULTILINE,
SemanticType.OVERSCORE,
SemanticType.ROOT,
SemanticType.ROW,
SemanticType.SQRT,
SemanticType.SUBSCRIPT,
SemanticType.SUPERSCRIPT,
SemanticType.TABLE,
SemanticType.UNDERSCORE,
SemanticType.VECTOR
];
export function resetNestingDepth(node) {
nestingDepth = {};
return [node];
}
function getNestingDepth(type, node, tags, opt_barrierTags, opt_barrierAttrs, opt_func) {
opt_barrierTags = opt_barrierTags || nestingBarriers;
opt_barrierAttrs = opt_barrierAttrs || {};
opt_func =
opt_func ||
function (_node) {
return false;
};
const xmlText = DomUtil.serializeXml(node);
if (!nestingDepth[type]) {
nestingDepth[type] = {};
}
if (nestingDepth[type][xmlText]) {
return nestingDepth[type][xmlText];
}
if (opt_func(node) || tags.indexOf(node.tagName) < 0) {
return 0;
}
const depth = computeNestingDepth_(node, tags, BaseUtil.setdifference(opt_barrierTags, tags), opt_barrierAttrs, opt_func, 0);
nestingDepth[type][xmlText] = depth;
return depth;
}
function containsAttr(node, attrs) {
if (!node.attributes) {
return false;
}
const attributes = DomUtil.toArray(node.attributes);
for (let i = 0, attr; (attr = attributes[i]); i++) {
if (attrs[attr.nodeName] === attr.nodeValue) {
return true;
}
}
return false;
}
function computeNestingDepth_(node, tags, barriers, attrs, func, depth) {
if (func(node) ||
barriers.indexOf(node.tagName) > -1 ||
containsAttr(node, attrs)) {
return depth;
}
if (tags.indexOf(node.tagName) > -1) {
depth++;
}
if (!node.childNodes || node.childNodes.length === 0) {
return depth;
}
const children = DomUtil.toArray(node.childNodes);
return Math.max.apply(null, children.map(function (subNode) {
return computeNestingDepth_(subNode, tags, barriers, attrs, func, depth);
}));
}
export function fractionNestingDepth(node) {
return getNestingDepth('fraction', node, ['fraction'], nestingBarriers, {}, LOCALE.FUNCTIONS.fracNestDepth);
}
function nestedFraction(node, expr, opt_end) {
const depth = fractionNestingDepth(node);
const annotation = Array(depth).fill(expr);
if (opt_end) {
annotation.push(opt_end);
}
return annotation.join(LOCALE.MESSAGES.regexp.JOINER_FRAC);
}
export function openingFractionVerbose(node) {
return Span.singleton(nestedFraction(node, LOCALE.MESSAGES.MS.START, LOCALE.MESSAGES.MS.FRAC_V));
}
export function closingFractionVerbose(node) {
return Span.singleton(nestedFraction(node, LOCALE.MESSAGES.MS.END, LOCALE.MESSAGES.MS.FRAC_V), { kind: 'LAST' });
}
export function overFractionVerbose(node) {
return Span.singleton(nestedFraction(node, LOCALE.MESSAGES.MS.FRAC_OVER), {});
}
export function openingFractionBrief(node) {
return Span.singleton(nestedFraction(node, LOCALE.MESSAGES.MS.START, LOCALE.MESSAGES.MS.FRAC_B));
}
export function closingFractionBrief(node) {
return Span.singleton(nestedFraction(node, LOCALE.MESSAGES.MS.END, LOCALE.MESSAGES.MS.FRAC_B), { kind: 'LAST' });
}
export function openingFractionSbrief(node) {
const depth = fractionNestingDepth(node);
return Span.singleton(depth === 1
? LOCALE.MESSAGES.MS.FRAC_S
: LOCALE.FUNCTIONS.combineNestedFraction(LOCALE.MESSAGES.MS.NEST_FRAC, LOCALE.FUNCTIONS.radicalNestDepth(depth - 1), LOCALE.MESSAGES.MS.FRAC_S));
}
export function closingFractionSbrief(node) {
const depth = fractionNestingDepth(node);
return Span.singleton(depth === 1
? LOCALE.MESSAGES.MS.ENDFRAC
: LOCALE.FUNCTIONS.combineNestedFraction(LOCALE.MESSAGES.MS.NEST_FRAC, LOCALE.FUNCTIONS.radicalNestDepth(depth - 1), LOCALE.MESSAGES.MS.ENDFRAC), { kind: 'LAST' });
}
export function overFractionSbrief(node) {
const depth = fractionNestingDepth(node);
return Span.singleton(depth === 1
? LOCALE.MESSAGES.MS.FRAC_OVER
: LOCALE.FUNCTIONS.combineNestedFraction(LOCALE.MESSAGES.MS.NEST_FRAC, LOCALE.FUNCTIONS.radicalNestDepth(depth - 1), LOCALE.MESSAGES.MS.FRAC_OVER));
}
export function isSmallVulgarFraction(node) {
return LOCALE.FUNCTIONS.fracNestDepth(node) ? [node] : [];
}
export function nestedSubSuper(node, init, replace) {
while (node.parentNode) {
const children = node.parentNode;
const parent = children.parentNode;
if (!parent) {
break;
}
const nodeRole = node.getAttribute && node.getAttribute('role');
if ((parent.tagName === SemanticType.SUBSCRIPT &&
node === children.childNodes[1]) ||
(parent.tagName === SemanticType.TENSOR &&
nodeRole &&
(nodeRole === SemanticRole.LEFTSUB ||
nodeRole === SemanticRole.RIGHTSUB))) {
init = replace.sub + LOCALE.MESSAGES.regexp.JOINER_SUBSUPER + init;
}
if ((parent.tagName === SemanticType.SUPERSCRIPT &&
node === children.childNodes[1]) ||
(parent.tagName === SemanticType.TENSOR &&
nodeRole &&
(nodeRole === SemanticRole.LEFTSUPER ||
nodeRole === SemanticRole.RIGHTSUPER))) {
init = replace.sup + LOCALE.MESSAGES.regexp.JOINER_SUBSUPER + init;
}
node = parent;
}
return init.trim();
}
export function subscriptVerbose(node) {
return Span.singleton(nestedSubSuper(node, LOCALE.MESSAGES.MS.SUBSCRIPT, {
sup: LOCALE.MESSAGES.MS.SUPER,
sub: LOCALE.MESSAGES.MS.SUB
}));
}
export function subscriptBrief(node) {
return Span.singleton(nestedSubSuper(node, LOCALE.MESSAGES.MS.SUB, {
sup: LOCALE.MESSAGES.MS.SUP,
sub: LOCALE.MESSAGES.MS.SUB
}));
}
export function superscriptVerbose(node) {
return Span.singleton(nestedSubSuper(node, LOCALE.MESSAGES.MS.SUPERSCRIPT, {
sup: LOCALE.MESSAGES.MS.SUPER,
sub: LOCALE.MESSAGES.MS.SUB
}));
}
export function superscriptBrief(node) {
return Span.singleton(nestedSubSuper(node, LOCALE.MESSAGES.MS.SUP, {
sup: LOCALE.MESSAGES.MS.SUP,
sub: LOCALE.MESSAGES.MS.SUB
}));
}
export function baselineVerbose(node) {
const baseline = nestedSubSuper(node, '', {
sup: LOCALE.MESSAGES.MS.SUPER,
sub: LOCALE.MESSAGES.MS.SUB
});
return Span.singleton(!baseline
? LOCALE.MESSAGES.MS.BASELINE
: baseline
.replace(new RegExp(LOCALE.MESSAGES.MS.SUB + '$'), LOCALE.MESSAGES.MS.SUBSCRIPT)
.replace(new RegExp(LOCALE.MESSAGES.MS.SUPER + '$'), LOCALE.MESSAGES.MS.SUPERSCRIPT));
}
export function baselineBrief(node) {
const baseline = nestedSubSuper(node, '', {
sup: LOCALE.MESSAGES.MS.SUP,
sub: LOCALE.MESSAGES.MS.SUB
});
return Span.singleton(baseline || LOCALE.MESSAGES.MS.BASE);
}
export function radicalNestingDepth(node) {
return getNestingDepth('radical', node, ['sqrt', 'root'], nestingBarriers, {});
}
function nestedRadical(node, prefix, postfix) {
const depth = radicalNestingDepth(node);
const index = getRootIndex(node);
postfix = index ? LOCALE.FUNCTIONS.combineRootIndex(postfix, index) : postfix;
return depth === 1
? postfix
: LOCALE.FUNCTIONS.combineNestedRadical(prefix, LOCALE.FUNCTIONS.radicalNestDepth(depth - 1), postfix);
}
function getRootIndex(node) {
const content = node.tagName === 'sqrt'
? '2'
:
XpathUtil.evalXPath('children/*[1]', node)[0].textContent.trim();
return LOCALE.MESSAGES.MSroots[content] || '';
}
export function openingRadicalVerbose(node) {
return Span.singleton(nestedRadical(node, LOCALE.MESSAGES.MS.NESTED, LOCALE.MESSAGES.MS.STARTROOT));
}
export function closingRadicalVerbose(node) {
return Span.singleton(nestedRadical(node, LOCALE.MESSAGES.MS.NESTED, LOCALE.MESSAGES.MS.ENDROOT));
}
export function indexRadicalVerbose(node) {
return Span.singleton(nestedRadical(node, LOCALE.MESSAGES.MS.NESTED, LOCALE.MESSAGES.MS.ROOTINDEX));
}
export function openingRadicalBrief(node) {
return Span.singleton(nestedRadical(node, LOCALE.MESSAGES.MS.NEST_ROOT, LOCALE.MESSAGES.MS.STARTROOT));
}
export function closingRadicalBrief(node) {
return Span.singleton(nestedRadical(node, LOCALE.MESSAGES.MS.NEST_ROOT, LOCALE.MESSAGES.MS.ENDROOT));
}
export function indexRadicalBrief(node) {
return Span.singleton(nestedRadical(node, LOCALE.MESSAGES.MS.NEST_ROOT, LOCALE.MESSAGES.MS.ROOTINDEX));
}
export function openingRadicalSbrief(node) {
return Span.singleton(nestedRadical(node, LOCALE.MESSAGES.MS.NEST_ROOT, LOCALE.MESSAGES.MS.ROOT));
}
export function indexRadicalSbrief(node) {
return Span.singleton(nestedRadical(node, LOCALE.MESSAGES.MS.NEST_ROOT, LOCALE.MESSAGES.MS.INDEX));
}
function underscoreNestingDepth(node) {
return getNestingDepth('underscore', node, ['underscore'], nestingBarriers, {}, function (node) {
return (node.tagName &&
node.tagName === SemanticType.UNDERSCORE &&
node.childNodes[0].childNodes[1].getAttribute('role') ===
SemanticRole.UNDERACCENT);
});
}
export function nestedUnderscript(node) {
const depth = underscoreNestingDepth(node);
return Span.singleton(Array(depth).join(LOCALE.MESSAGES.MS.UNDER) + LOCALE.MESSAGES.MS.UNDERSCRIPT);
}
function overscoreNestingDepth(node) {
return getNestingDepth('overscore', node, ['overscore'], nestingBarriers, {}, function (node) {
return (node.tagName &&
node.tagName === SemanticType.OVERSCORE &&
node.childNodes[0].childNodes[1].getAttribute('role') ===
SemanticRole.OVERACCENT);
});
}
export function endscripts(_node) {
return Span.singleton(LOCALE.MESSAGES.MS.ENDSCRIPTS);
}
export function nestedOverscript(node) {
const depth = overscoreNestingDepth(node);
return Span.singleton(Array(depth).join(LOCALE.MESSAGES.MS.OVER) + LOCALE.MESSAGES.MS.OVERSCRIPT);
}
export function determinantIsSimple(node) {
if (node.tagName !== SemanticType.MATRIX ||
node.getAttribute('role') !== SemanticRole.DETERMINANT) {
return [];
}
const cells = XpathUtil.evalXPath('children/row/children/cell/children/*', node);
for (let i = 0, cell; (cell = cells[i]); i++) {
if (cell.tagName === SemanticType.NUMBER) {
continue;
}
if (cell.tagName === SemanticType.IDENTIFIER) {
const role = cell.getAttribute('role');
if (role === SemanticRole.LATINLETTER ||
role === SemanticRole.GREEKLETTER ||
role === SemanticRole.OTHERLETTER) {
continue;
}
}
return [];
}
return [node];
}
export function generateBaselineConstraint() {
const ignoreElems = ['subscript', 'superscript', 'tensor'];
const mainElems = ['relseq', 'multrel'];
const breakElems = ['fraction', 'punctuation', 'fenced', 'sqrt', 'root'];
const ancestrify = (elemList) => elemList.map((elem) => 'ancestor::' + elem);
const notify = (elem) => 'not(' + elem + ')';
const prefix = 'ancestor::*/following-sibling::*';
const middle = notify(ancestrify(ignoreElems).join(' or '));
const mainList = ancestrify(mainElems);
const breakList = ancestrify(breakElems);
let breakCstrs = [];
for (let i = 0, brk; (brk = breakList[i]); i++) {
breakCstrs = breakCstrs.concat(mainList.map(function (elem) {
return brk + '/' + elem;
}));
}
const postfix = notify(breakCstrs.join(' | '));
return [[prefix, middle, postfix].join(' and ')];
}
export function removeParens(node) {
if (!node.childNodes.length ||
!node.childNodes[0].childNodes.length ||
!node.childNodes[0].childNodes[0].childNodes.length) {
return Span.singleton('');
}
const content = node.childNodes[0].childNodes[0].childNodes[0].textContent;
return Span.singleton(content.match(/^\(.+\)$/) ? content.slice(1, -1) : content);
}
const componentString = new Map([
[3, 'CSFleftsuperscript'],
[4, 'CSFleftsubscript'],
[2, 'CSFbaseline'],
[1, 'CSFrightsubscript'],
[0, 'CSFrightsuperscript']
]);
const childNumber = new Map([
[4, 2],
[3, 3],
[2, 1],
[1, 4],
[0, 5]
]);
function generateTensorRuleStrings_(constellation) {
const constraints = [];
let verbString = '';
let briefString = '';
let constel = parseInt(constellation, 2);
for (let i = 0; i < 5; i++) {
const childString = 'children/*[' + childNumber.get(i) + ']';
if (constel & 1) {
const compString = componentString.get(i % 5);
verbString =
'[t] ' + compString + 'Verbose; [n] ' + childString + ';' + verbString;
briefString =
'[t] ' + compString + 'Brief; [n] ' + childString + ';' + briefString;
}
else {
constraints.unshift('name(' + childString + ')="empty"');
}
constel >>= 1;
}
return [constraints, verbString, briefString];
}
export function generateTensorRules(store, brief = true) {
const constellations = [
'11111',
'11110',
'11101',
'11100',
'10111',
'10110',
'10101',
'10100',
'01111',
'01110',
'01101',
'01100'
];
for (const constel of constellations) {
let name = 'tensor' + constel;
let [components, verbStr, briefStr] = generateTensorRuleStrings_(constel);
store.defineRule(name, 'default', verbStr, 'self::tensor', ...components);
if (brief) {
store.defineRule(name, 'brief', briefStr, 'self::tensor', ...components);
store.defineRule(name, 'sbrief', briefStr, 'self::tensor', ...components);
}
if (!(parseInt(constel, 2) & 3)) {
continue;
}
const baselineStr = componentString.get(2);
verbStr += '; [t]' + baselineStr + 'Verbose';
briefStr += '; [t]' + baselineStr + 'Brief';
name = name + '-baseline';
const cstr = '((.//*[not(*)])[last()]/@id)!=(((.//ancestor::fraction|' +
'ancestor::root|ancestor::sqrt|ancestor::cell|ancestor::line|' +
'ancestor::stree)[1]//*[not(*)])[last()]/@id)';
store.defineRule(name, 'default', verbStr, 'self::tensor', cstr, ...components);
if (brief) {
store.defineRule(name, 'brief', briefStr, 'self::tensor', cstr, ...components);
store.defineRule(name, 'sbrief', briefStr, 'self::tensor', cstr, ...components);
}
}
}
export function smallRoot(node) {
let max = Object.keys(LOCALE.MESSAGES.MSroots).length;
if (!max) {
return [];
}
else {
max++;
}
if (!node.childNodes ||
node.childNodes.length === 0 ||
!node.childNodes[0].childNodes) {
return [];
}
const index = node.childNodes[0].childNodes[0].textContent;
if (!/^\d+$/.test(index)) {
return [];
}
const num = parseInt(index, 10);
return num > 1 && num <= max ? [node] : [];
}

View File

@@ -0,0 +1,13 @@
import { AuditoryDescription } from '../audio/auditory_description.js';
import { Span } from '../audio/span.js';
export declare function openingFraction(node: Element): Span[];
export declare function closingFraction(node: Element): Span[];
export declare function overFraction(node: Element): Span[];
export declare function overBevelledFraction(node: Element): Span[];
export declare function hyperFractionBoundary(node: Element): Element[];
export declare function openingRadical(node: Element): Span[];
export declare function closingRadical(node: Element): Span[];
export declare function indexRadical(node: Element): Span[];
export declare function relationIterator(nodes: Element[], context: string): () => AuditoryDescription[];
export declare function implicitIterator(nodes: Element[], context: string): () => AuditoryDescription[];
export declare function contentIterator(nodes: Element[], context: string): () => AuditoryDescription[];

View File

@@ -0,0 +1,252 @@
import { AuditoryDescription } from '../audio/auditory_description.js';
import { Span } from '../audio/span.js';
import * as DomUtil from '../common/dom_util.js';
import * as XpathUtil from '../common/xpath_util.js';
import { Grammar, correctFont } from '../rule_engine/grammar.js';
import { Engine } from '../common/engine.js';
import { register, activate } from '../semantic_tree/semantic_annotations.js';
import { SemanticVisitor } from '../semantic_tree/semantic_annotator.js';
import { SemanticRole, SemanticType } from '../semantic_tree/semantic_meaning.js';
import { LOCALE } from '../l10n/locale.js';
import * as MathspeakUtil from './mathspeak_util.js';
import { contentIterator as suCI } from '../rule_engine/store_util.js';
export function openingFraction(node) {
const depth = MathspeakUtil.fractionNestingDepth(node);
return Span.singleton(new Array(depth).join(LOCALE.MESSAGES.MS.FRACTION_REPEAT) +
LOCALE.MESSAGES.MS.FRACTION_START);
}
export function closingFraction(node) {
const depth = MathspeakUtil.fractionNestingDepth(node);
return Span.singleton(new Array(depth).join(LOCALE.MESSAGES.MS.FRACTION_REPEAT) +
LOCALE.MESSAGES.MS.FRACTION_END);
}
export function overFraction(node) {
const depth = MathspeakUtil.fractionNestingDepth(node);
return Span.singleton(new Array(depth).join(LOCALE.MESSAGES.MS.FRACTION_REPEAT) +
LOCALE.MESSAGES.MS.FRACTION_OVER);
}
export function overBevelledFraction(node) {
const depth = MathspeakUtil.fractionNestingDepth(node);
return Span.singleton(new Array(depth).join(LOCALE.MESSAGES.MS.FRACTION_REPEAT) +
'⠸' +
LOCALE.MESSAGES.MS.FRACTION_OVER);
}
export function hyperFractionBoundary(node) {
return LOCALE.MESSAGES.regexp.HYPER ===
MathspeakUtil.fractionNestingDepth(node).toString()
? [node]
: [];
}
function nestedRadical(node, postfix) {
const depth = radicalNestingDepth(node);
return Span.singleton(depth === 1
? postfix
: new Array(depth).join(LOCALE.MESSAGES.MS.NESTED) + postfix);
}
function radicalNestingDepth(node, opt_depth) {
const depth = opt_depth || 0;
if (!node.parentNode) {
return depth;
}
return radicalNestingDepth(node.parentNode, node.tagName === 'root' || node.tagName === 'sqrt' ? depth + 1 : depth);
}
export function openingRadical(node) {
return nestedRadical(node, LOCALE.MESSAGES.MS.STARTROOT);
}
export function closingRadical(node) {
return nestedRadical(node, LOCALE.MESSAGES.MS.ENDROOT);
}
export function indexRadical(node) {
return nestedRadical(node, LOCALE.MESSAGES.MS.ROOTINDEX);
}
function enlargeFence(text) {
const start = '⠠';
if (text.length === 1) {
return start + text;
}
const neut = '⠳';
const split = text.split('');
if (split.every(function (x) {
return x === neut;
})) {
return start + split.join(start);
}
return text.slice(0, -1) + start + text.slice(-1);
}
Grammar.getInstance().setCorrection('enlargeFence', enlargeFence);
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 }));
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');
export function relationIterator(nodes, context) {
var _a;
const childNodes = nodes.slice(0);
let first = true;
const parentNode = nodes[0].parentNode.parentNode;
const match = (_a = parentNode.getAttribute('annotation')) === null || _a === void 0 ? void 0 : _a.match(/depth:(\d+)/);
const depth = match ? match[1] : '';
let contentNodes;
if (nodes.length > 0) {
contentNodes = XpathUtil.evalXPath('./content/*', parentNode);
}
else {
contentNodes = [];
}
return function () {
const content = contentNodes.shift();
const leftChild = childNodes.shift();
const rightChild = childNodes[0];
const contextDescr = context
? [AuditoryDescription.create({ text: context }, { translate: true })]
: [];
if (!content) {
return contextDescr;
}
const base = leftChild
? MathspeakUtil.nestedSubSuper(leftChild, '', {
sup: LOCALE.MESSAGES.MS.SUPER,
sub: LOCALE.MESSAGES.MS.SUB
})
: '';
const left = (leftChild && DomUtil.tagName(leftChild) !== 'EMPTY') ||
(first && parentNode && parentNode.previousSibling)
? [
AuditoryDescription.create({ text: LOCALE.MESSAGES.regexp.SPACE + base }, {})
]
: [];
const right = (rightChild && DomUtil.tagName(rightChild) !== 'EMPTY') ||
(!contentNodes.length && parentNode && parentNode.nextSibling)
? [
AuditoryDescription.create({ text: LOCALE.MESSAGES.regexp.SPACE }, {})
]
: [];
const descrs = Engine.evaluateNode(content);
descrs.unshift(new AuditoryDescription({ text: '', layout: `beginrel${depth}` }));
descrs.push(new AuditoryDescription({ text: '', layout: `endrel${depth}` }));
first = false;
return contextDescr.concat(left, descrs, right);
};
}
export function implicitIterator(nodes, context) {
const childNodes = nodes.slice(0);
let contentNodes;
if (nodes.length > 0) {
contentNodes = XpathUtil.evalXPath('../../content/*', nodes[0]);
}
else {
contentNodes = [];
}
return function () {
const leftChild = childNodes.shift();
const rightChild = childNodes[0];
const content = contentNodes.shift();
const contextDescr = context
? [AuditoryDescription.create({ text: context }, { translate: true })]
: [];
if (!content) {
return contextDescr;
}
const left = leftChild && DomUtil.tagName(leftChild) === 'NUMBER';
const right = rightChild && DomUtil.tagName(rightChild) === 'NUMBER';
return contextDescr.concat(left && right && content.getAttribute('role') === SemanticRole.SPACE
? [
AuditoryDescription.create({ text: LOCALE.MESSAGES.regexp.SPACE }, {})
]
: []);
};
}
function ignoreEnglish(text) {
return correctFont(text, LOCALE.ALPHABETS.languagePrefix.english);
}
Grammar.getInstance().setCorrection('ignoreEnglish', ignoreEnglish);
export function contentIterator(nodes, context) {
var _a;
const func = suCI(nodes, context);
const parentNode = nodes[0].parentNode.parentNode;
const match = (_a = parentNode.getAttribute('annotation')) === null || _a === void 0 ? void 0 : _a.match(/depth:(\d+)/);
const depth = match ? match[1] : '';
return function () {
const descrs = func();
descrs.unshift(new AuditoryDescription({ text: '', layout: `beginrel${depth}` }));
descrs.push(new AuditoryDescription({ text: '', layout: `endrel${depth}` }));
return descrs;
};
}
function literal(text) {
const evalStr = (e) => Engine.getInstance().evaluator(e, Engine.getInstance().dynamicCstr);
return Array.from(text).map(evalStr).join('');
}
Grammar.getInstance().setCorrection('literal', literal);

View File

@@ -0,0 +1,5 @@
import { Span } from '../audio/span.js';
export declare function ordinalCounter(_node: Element, context: string): () => string;
export declare function wordCounter(_node: Element, context: string): () => string;
export declare function vulgarFraction(node: Element): Span[];
export declare function ordinalPosition(node: Element): Span[];

View File

@@ -0,0 +1,33 @@
import { Span } from '../audio/span.js';
import * as DomUtil from '../common/dom_util.js';
import { LOCALE } from '../l10n/locale.js';
import { convertVulgarFraction } from '../l10n/transformers.js';
export function ordinalCounter(_node, context) {
let counter = 0;
return function () {
return LOCALE.NUMBERS.numericOrdinal(++counter) + ' ' + context;
};
}
export function wordCounter(_node, context) {
let counter = 0;
return function () {
return LOCALE.NUMBERS.numberToOrdinal(++counter, false) + ' ' + context;
};
}
export function vulgarFraction(node) {
const conversion = convertVulgarFraction(node, LOCALE.MESSAGES.MS.FRAC_OVER);
if (conversion.convertible &&
conversion.enumerator &&
conversion.denominator) {
return [
Span.node(LOCALE.NUMBERS.numberToWords(conversion.enumerator), node.childNodes[0].childNodes[0], { separator: '' }),
Span.stringAttr(LOCALE.NUMBERS.vulgarSep, { separator: '' }),
Span.node(LOCALE.NUMBERS.numberToOrdinal(conversion.denominator, conversion.enumerator !== 1), node.childNodes[0].childNodes[1])
];
}
return [Span.node(conversion.content || '', node)];
}
export function ordinalPosition(node) {
const children = DomUtil.toArray(node.parentNode.childNodes);
return Span.singleton(LOCALE.NUMBERS.numericOrdinal(children.indexOf(node) + 1).toString());
}

View File

@@ -0,0 +1,3 @@
export declare function PrefixRules(): void;
export declare function OtherRules(): void;
export declare function BrailleRules(): void;

View File

@@ -0,0 +1,47 @@
import { DynamicCstr } from '../rule_engine/dynamic_cstr.js';
import * as StoreUtil from '../rule_engine/store_util.js';
import * as MathspeakKoreanUtil from './mathspeak_korean_util.js';
import * as MathspeakUtil from './mathspeak_util.js';
import * as NemethUtil from './nemeth_util.js';
import * as NumbersUtil from './numbers_util.js';
import * as SpeechRules from './speech_rules.js';
export function PrefixRules() {
SpeechRules.addStore('en.prefix.default', '', {
CSFordinalPosition: NumbersUtil.ordinalPosition
});
}
export function OtherRules() {
SpeechRules.addStore('en.speech.chromevox', '', {
CTFnodeCounter: StoreUtil.nodeCounter,
CTFcontentIterator: StoreUtil.contentIterator
});
SpeechRules.addStore('en.speech.emacspeak', 'en.speech.chromevox', {
CQFvulgarFractionSmall: MathspeakUtil.isSmallVulgarFraction,
CSFvulgarFraction: NumbersUtil.vulgarFraction
});
SpeechRules.addStore('ko.summary.', 'ko.speech.mathspeak', {
CSFordinalConversion: MathspeakKoreanUtil.ordinalConversion,
CSFdecreasedOrdinalConversion: MathspeakKoreanUtil.decreasedOrdinalConversion,
CSFlistOrdinalConversion: MathspeakKoreanUtil.listOrdinalConversion
});
}
export function BrailleRules() {
SpeechRules.addStore('nemeth.braille.default', DynamicCstr.BASE_LOCALE + '.speech.mathspeak', {
CSFopenFraction: NemethUtil.openingFraction,
CSFcloseFraction: NemethUtil.closingFraction,
CSFoverFraction: NemethUtil.overFraction,
CSFoverBevFraction: NemethUtil.overBevelledFraction,
CQFhyperFraction: NemethUtil.hyperFractionBoundary,
CSFopenRadical: NemethUtil.openingRadical,
CSFcloseRadical: NemethUtil.closingRadical,
CSFindexRadical: NemethUtil.indexRadical,
CSFsubscript: MathspeakUtil.subscriptVerbose,
CSFsuperscript: MathspeakUtil.superscriptVerbose,
CSFbaseline: MathspeakUtil.baselineVerbose,
CGFtensorRules: (st) => MathspeakUtil.generateTensorRules(st, false),
CTFcontentIterator: NemethUtil.contentIterator,
CTFrelationIterator: NemethUtil.relationIterator,
CTFimplicitIterator: NemethUtil.implicitIterator
});
SpeechRules.addStore('euro.braille.default', 'nemeth.braille.default', {});
}

View File

@@ -0,0 +1 @@
export declare function init(): void;

View File

@@ -0,0 +1,15 @@
import { ClearspeakRules } from './clearspeak_rules.js';
import { MathspeakRules } from './mathspeak_rules.js';
import { BrailleRules, OtherRules, PrefixRules } from './other_rules.js';
let INIT = false;
export function init() {
if (INIT) {
return;
}
MathspeakRules();
ClearspeakRules();
PrefixRules();
OtherRules();
BrailleRules();
INIT = true;
}

View File

@@ -0,0 +1,7 @@
import { SpeechRuleFunction } from '../rule_engine/speech_rule_functions.js';
export declare function addStore(constr: string, inherit: string, store: {
[key: string]: SpeechRuleFunction;
}): void;
export declare function getStore(locale: string, modality: string, domain: string): {
[key: string]: SpeechRuleFunction;
};

View File

@@ -0,0 +1,16 @@
import { Axis, DynamicCstr } from '../rule_engine/dynamic_cstr.js';
const funcStore = new Map();
export function addStore(constr, inherit, store) {
const values = {};
if (inherit) {
const inherits = funcStore.get(inherit) || {};
Object.assign(values, inherits);
}
funcStore.set(constr, Object.assign(values, store));
}
export function getStore(locale, modality, domain) {
return (funcStore.get([locale, modality, domain].join('.')) ||
funcStore.get([DynamicCstr.DEFAULT_VALUES[Axis.LOCALE], modality, domain].join('.')) ||
funcStore.get([DynamicCstr.BASE_LOCALE, modality, domain].join('.')) ||
{});
}

View File

@@ -0,0 +1,3 @@
import { AuditoryDescription } from '../audio/auditory_description.js';
export declare function unitMultipliers(nodes: Element[], _context: string): () => AuditoryDescription[];
export declare function oneLeft(node: Element): Element[];

View File

@@ -0,0 +1,61 @@
import { AuditoryDescription } from '../audio/auditory_description.js';
import * as XpathUtil from '../common/xpath_util.js';
import { LOCALE } from '../l10n/locale.js';
import { SemanticType } from '../semantic_tree/semantic_meaning.js';
export function unitMultipliers(nodes, _context) {
const children = nodes;
let counter = 0;
return function () {
const descr = AuditoryDescription.create({
text: rightMostUnit(children[counter]) &&
leftMostUnit(children[counter + 1])
? LOCALE.MESSAGES.unitTimes
: ''
}, {});
counter++;
return [descr];
};
}
const SCRIPT_ELEMENTS = [
SemanticType.SUPERSCRIPT,
SemanticType.SUBSCRIPT,
SemanticType.OVERSCORE,
SemanticType.UNDERSCORE
];
function rightMostUnit(node) {
while (node) {
if (node.getAttribute('role') === 'unit') {
return true;
}
const tag = node.tagName;
const children = XpathUtil.evalXPath('children/*', node);
node = (SCRIPT_ELEMENTS.indexOf(tag) !== -1
? children[0]
: children[children.length - 1]);
}
return false;
}
function leftMostUnit(node) {
while (node) {
if (node.getAttribute('role') === 'unit') {
return true;
}
const children = XpathUtil.evalXPath('children/*', node);
node = children[0];
}
return false;
}
export function oneLeft(node) {
while (node) {
if (node.tagName === 'number' && node.textContent === '1') {
return [node];
}
if (node.tagName !== 'infixop' ||
(node.getAttribute('role') !== 'multiplication' &&
node.getAttribute('role') !== 'implicit')) {
return [];
}
node = XpathUtil.evalXPath('children/*', node)[0];
}
return [];
}