math_store.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import * as BaseUtil from '../common/base_util.js';
  2. import { LOCALE } from '../l10n/locale.js';
  3. import { activate } from '../semantic_tree/semantic_annotations.js';
  4. import { BaseRuleStore } from './base_rule_store.js';
  5. import { Action, OutputError, SpeechRule } from './speech_rule.js';
  6. export class MathStore extends BaseRuleStore {
  7. constructor() {
  8. super();
  9. this.annotators = [];
  10. this.parseMethods['Alias'] = this.defineAlias;
  11. this.parseMethods['SpecializedRule'] = this.defineSpecializedRule;
  12. this.parseMethods['Specialized'] = this.defineSpecialized;
  13. }
  14. initialize() {
  15. if (this.initialized) {
  16. return;
  17. }
  18. this.annotations();
  19. this.initialized = true;
  20. }
  21. annotations() {
  22. for (let i = 0, annotator; (annotator = this.annotators[i]); i++) {
  23. activate(this.domain, annotator);
  24. }
  25. }
  26. defineAlias(name, prec, ...args) {
  27. const fullPrec = this.parsePrecondition(prec, args);
  28. if (!fullPrec) {
  29. console.error(`Precondition Error: ${prec} ${args}`);
  30. return;
  31. }
  32. const condition = this.preconditions.get(name);
  33. if (!condition) {
  34. console.error(`Alias Error: No precondition by the name of ${name}`);
  35. return;
  36. }
  37. condition.addFullCondition(fullPrec);
  38. }
  39. defineRulesAlias(name, query, ...args) {
  40. const rules = this.findAllRules(function (rule) {
  41. return rule.name === name;
  42. });
  43. if (rules.length === 0) {
  44. throw new OutputError('Rule with name ' + name + ' does not exist.');
  45. }
  46. const keep = [];
  47. const findKeep = (rule) => {
  48. const cstr = rule.dynamicCstr.toString();
  49. const action = rule.action.toString();
  50. for (let i = 0, k; (k = keep[i]); i++) {
  51. if (k.action === action && k.cstr === cstr) {
  52. return false;
  53. }
  54. }
  55. keep.push({ cstr: cstr, action: action });
  56. return true;
  57. };
  58. rules.forEach((rule) => {
  59. if (findKeep(rule)) {
  60. this.addAlias_(rule, query, args);
  61. }
  62. });
  63. }
  64. defineSpecializedRule(name, oldDynamic, newDynamic, opt_action) {
  65. const dynamicCstr = this.parseCstr(oldDynamic);
  66. const rule = this.findRule((rule) => rule.name === name && dynamicCstr.equal(rule.dynamicCstr));
  67. const newCstr = this.parseCstr(newDynamic);
  68. if (!rule && opt_action) {
  69. throw new OutputError('Rule named ' + name + ' with style ' + oldDynamic + ' does not exist.');
  70. }
  71. const action = opt_action ? Action.fromString(opt_action) : rule.action;
  72. const newRule = new SpeechRule(rule.name, newCstr, rule.precondition, action);
  73. this.addRule(newRule);
  74. }
  75. defineSpecialized(name, _old, dynamic) {
  76. const cstr = this.parseCstr(dynamic);
  77. if (!cstr) {
  78. console.error(`Dynamic Constraint Error: ${dynamic}`);
  79. return;
  80. }
  81. const condition = this.preconditions.get(name);
  82. if (!condition) {
  83. console.error(`Alias Error: No precondition by the name of ${name}`);
  84. return;
  85. }
  86. condition.addConstraint(cstr);
  87. }
  88. evaluateString(str) {
  89. const descs = [];
  90. if (str.match(/^\s+$/)) {
  91. return descs;
  92. }
  93. let num = this.matchNumber(str);
  94. if (num && num.length === str.length) {
  95. descs.push(this.evaluateCharacter(num.number));
  96. return descs;
  97. }
  98. const split = BaseUtil.removeEmpty(str.replace(/\s/g, ' ').split(' '));
  99. for (let i = 0, s; (s = split[i]); i++) {
  100. if (s.length === 1) {
  101. descs.push(this.evaluateCharacter(s));
  102. }
  103. else if (s.match(new RegExp('^[' + LOCALE.MESSAGES.regexp.TEXT + ']+$'))) {
  104. descs.push(this.evaluateCharacter(s));
  105. }
  106. else {
  107. let rest = s;
  108. while (rest) {
  109. num = this.matchNumber(rest);
  110. const alpha = rest.match(new RegExp('^[' + LOCALE.MESSAGES.regexp.TEXT + ']+'));
  111. if (num) {
  112. descs.push(this.evaluateCharacter(num.number));
  113. rest = rest.substring(num.length);
  114. }
  115. else if (alpha) {
  116. descs.push(this.evaluateCharacter(alpha[0]));
  117. rest = rest.substring(alpha[0].length);
  118. }
  119. else {
  120. const chars = Array.from(rest);
  121. const chr = chars[0];
  122. descs.push(this.evaluateCharacter(chr));
  123. rest = chars.slice(1).join('');
  124. }
  125. }
  126. }
  127. }
  128. return descs;
  129. }
  130. parse(ruleSet) {
  131. super.parse(ruleSet);
  132. this.annotators = ruleSet['annotators'] || [];
  133. }
  134. addAlias_(rule, query, cstrList) {
  135. const prec = this.parsePrecondition(query, cstrList);
  136. const newRule = new SpeechRule(rule.name, rule.dynamicCstr, prec, rule.action);
  137. newRule.name = rule.name;
  138. this.addRule(newRule);
  139. }
  140. matchNumber(str) {
  141. const locNum = str.match(new RegExp('^' + LOCALE.MESSAGES.regexp.NUMBER));
  142. const enNum = str.match(new RegExp('^' + MathStore.regexp.NUMBER));
  143. if (!locNum && !enNum) {
  144. return null;
  145. }
  146. const isEn = enNum && enNum[0] === str;
  147. const isLoc = (locNum && locNum[0] === str) || !isEn;
  148. if (isLoc) {
  149. return locNum ? { number: locNum[0], length: locNum[0].length } : null;
  150. }
  151. const num = enNum[0]
  152. .replace(new RegExp(MathStore.regexp.DIGIT_GROUP, 'g'), 'X')
  153. .replace(new RegExp(MathStore.regexp.DECIMAL_MARK, 'g'), LOCALE.MESSAGES.regexp.DECIMAL_MARK)
  154. .replace(/X/g, LOCALE.MESSAGES.regexp.DIGIT_GROUP.replace(/\\/g, ''));
  155. return { number: num, length: enNum[0].length };
  156. }
  157. }
  158. MathStore.regexp = {
  159. NUMBER: '((\\d{1,3})(?=(,| ))((,| )\\d{3})*(\\.\\d+)?)|^\\d*\\.\\d+|^\\d+',
  160. DECIMAL_MARK: '\\.',
  161. DIGIT_GROUP: ','
  162. };