speech_rule.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. import { SREError } from '../common/engine.js';
  2. import * as Grammar from './grammar.js';
  3. export class SpeechRule {
  4. constructor(name, dynamicCstr, precondition, action) {
  5. this.name = name;
  6. this.dynamicCstr = dynamicCstr;
  7. this.precondition = precondition;
  8. this.action = action;
  9. this.context = null;
  10. }
  11. toString() {
  12. return (this.name +
  13. ' | ' +
  14. this.dynamicCstr.toString() +
  15. ' | ' +
  16. this.precondition.toString() +
  17. ' ==> ' +
  18. this.action.toString());
  19. }
  20. }
  21. export var ActionType;
  22. (function (ActionType) {
  23. ActionType["NODE"] = "NODE";
  24. ActionType["MULTI"] = "MULTI";
  25. ActionType["TEXT"] = "TEXT";
  26. ActionType["PERSONALITY"] = "PERSONALITY";
  27. })(ActionType || (ActionType = {}));
  28. function actionFromString(str) {
  29. switch (str) {
  30. case '[n]':
  31. return ActionType.NODE;
  32. case '[m]':
  33. return ActionType.MULTI;
  34. case '[t]':
  35. return ActionType.TEXT;
  36. case '[p]':
  37. return ActionType.PERSONALITY;
  38. default:
  39. throw 'Parse error: ' + str;
  40. }
  41. }
  42. function actionToString(speechType) {
  43. switch (speechType) {
  44. case ActionType.NODE:
  45. return '[n]';
  46. case ActionType.MULTI:
  47. return '[m]';
  48. case ActionType.TEXT:
  49. return '[t]';
  50. case ActionType.PERSONALITY:
  51. return '[p]';
  52. default:
  53. throw 'Unknown type error: ' + speechType;
  54. }
  55. }
  56. export class Component {
  57. static grammarFromString(grammar) {
  58. return Grammar.Grammar.parseInput(grammar);
  59. }
  60. static fromString(input) {
  61. const output = {
  62. type: actionFromString(input.substring(0, 3))
  63. };
  64. let rest = input.slice(3).trim();
  65. if (!rest) {
  66. throw new OutputError('Missing content.');
  67. }
  68. switch (output.type) {
  69. case ActionType.TEXT:
  70. if (rest[0] === '"') {
  71. const quotedString = splitString(rest, '\\(')[0].trim();
  72. if (quotedString.slice(-1) !== '"') {
  73. throw new OutputError('Invalid string syntax.');
  74. }
  75. output.content = quotedString;
  76. rest = rest.slice(quotedString.length).trim();
  77. if (rest.indexOf('(') === -1) {
  78. rest = '';
  79. }
  80. break;
  81. }
  82. case ActionType.NODE:
  83. case ActionType.MULTI:
  84. {
  85. const bracket = rest.indexOf(' (');
  86. if (bracket === -1) {
  87. output.content = rest.trim();
  88. rest = '';
  89. break;
  90. }
  91. output.content = rest.substring(0, bracket).trim();
  92. rest = rest.slice(bracket).trim();
  93. }
  94. break;
  95. }
  96. if (rest) {
  97. const attributes = Component.attributesFromString(rest);
  98. if (attributes.grammar) {
  99. output.grammar = attributes.grammar;
  100. delete attributes.grammar;
  101. }
  102. if (Object.keys(attributes).length) {
  103. output.attributes = attributes;
  104. }
  105. }
  106. return new Component(output);
  107. }
  108. static attributesFromString(attrs) {
  109. if (attrs[0] !== '(' || attrs.slice(-1) !== ')') {
  110. throw new OutputError('Invalid attribute expression: ' + attrs);
  111. }
  112. const attributes = {};
  113. const attribs = splitString(attrs.slice(1, -1), ',');
  114. for (const attr of attribs) {
  115. const colon = attr.indexOf(':');
  116. if (colon === -1) {
  117. attributes[attr.trim()] = 'true';
  118. }
  119. else {
  120. const key = attr.substring(0, colon).trim();
  121. const value = attr.slice(colon + 1).trim();
  122. attributes[key] =
  123. key === Grammar.ATTRIBUTE
  124. ? Component.grammarFromString(value)
  125. : value;
  126. }
  127. }
  128. return attributes;
  129. }
  130. constructor({ type, content, attributes, grammar }) {
  131. this.type = type;
  132. this.content = content;
  133. this.attributes = attributes;
  134. this.grammar = grammar;
  135. }
  136. toString() {
  137. let strs = '';
  138. strs += actionToString(this.type);
  139. strs += this.content ? ' ' + this.content : '';
  140. const attrs = this.attributesToString();
  141. strs += attrs ? ' ' + attrs : '';
  142. return strs;
  143. }
  144. grammarToString() {
  145. return this.getGrammar().join(':');
  146. }
  147. getGrammar() {
  148. if (!this.grammar) {
  149. return [];
  150. }
  151. const attribs = [];
  152. for (const [key, val] of Object.entries(this.grammar)) {
  153. attribs.push(val === true ? key : val === false ? `!${key}` : `${key}=${val}`);
  154. }
  155. return attribs;
  156. }
  157. attributesToString() {
  158. const attribs = this.getAttributes();
  159. const grammar = this.grammarToString();
  160. if (grammar) {
  161. attribs.push('grammar:' + grammar);
  162. }
  163. return attribs.length > 0 ? '(' + attribs.join(', ') + ')' : '';
  164. }
  165. getAttributes() {
  166. if (!this.attributes) {
  167. return [];
  168. }
  169. const attribs = [];
  170. for (const [key, val] of Object.entries(this.attributes)) {
  171. attribs.push(val === 'true' ? key : `${key}:${val}`);
  172. }
  173. return attribs;
  174. }
  175. }
  176. export class Action {
  177. static fromString(input) {
  178. const comps = splitString(input, ';')
  179. .filter(function (x) {
  180. return x.match(/\S/);
  181. })
  182. .map(function (x) {
  183. return x.trim();
  184. });
  185. const newComps = [];
  186. for (let i = 0, m = comps.length; i < m; i++) {
  187. const comp = Component.fromString(comps[i]);
  188. if (comp) {
  189. newComps.push(comp);
  190. }
  191. }
  192. Action.naiveSpan(newComps);
  193. return new Action(newComps);
  194. }
  195. static naiveSpan(comps) {
  196. var _a;
  197. let first = false;
  198. for (let i = 0, comp; (comp = comps[i]); i++) {
  199. if (first &&
  200. (comp.type !== ActionType.TEXT ||
  201. (comp.content[0] !== '"' && !comp.content.match(/^CSF/))))
  202. continue;
  203. if (!first && comp.type === ActionType.PERSONALITY)
  204. continue;
  205. if (!first) {
  206. first = true;
  207. continue;
  208. }
  209. if ((_a = comp.attributes) === null || _a === void 0 ? void 0 : _a.span)
  210. continue;
  211. const next = comps[i + 1];
  212. if (next && next.type !== ActionType.NODE)
  213. continue;
  214. Action.addNaiveSpan(comp, next ? next.content : 'LAST');
  215. }
  216. }
  217. static addNaiveSpan(comp, span) {
  218. if (!comp.attributes) {
  219. comp.attributes = {};
  220. }
  221. comp.attributes['span'] = span;
  222. }
  223. constructor(components) {
  224. this.components = components;
  225. }
  226. toString() {
  227. const comps = this.components.map(function (c) {
  228. return c.toString();
  229. });
  230. return comps.join('; ');
  231. }
  232. }
  233. export class Precondition {
  234. static constraintValue(constr, priorities) {
  235. for (let i = 0, regexp; (regexp = priorities[i]); i++) {
  236. if (constr.match(regexp)) {
  237. return ++i;
  238. }
  239. }
  240. return 0;
  241. }
  242. toString() {
  243. const constrs = this.constraints.join(', ');
  244. return `${this.query}, ${constrs} (${this.priority}, ${this.rank})`;
  245. }
  246. constructor(query, ...cstr) {
  247. this.query = query;
  248. this.constraints = cstr;
  249. const [exists, user] = this.presetPriority();
  250. this.priority = exists ? user : this.calculatePriority();
  251. }
  252. calculatePriority() {
  253. const query = Precondition.constraintValue(this.query, Precondition.queryPriorities);
  254. if (!query) {
  255. return 0;
  256. }
  257. const match = this.query.match(/^self::.+\[(.+)\]/);
  258. let attr = 0;
  259. if ((match === null || match === void 0 ? void 0 : match.length) && match[1]) {
  260. const inner = match[1];
  261. attr = Precondition.constraintValue(inner, Precondition.attributePriorities);
  262. }
  263. return query * 100 + attr * 10;
  264. }
  265. presetPriority() {
  266. if (!this.constraints.length) {
  267. return [false, 0];
  268. }
  269. const last = this.constraints[this.constraints.length - 1].match(/^priority=(.*$)/);
  270. if (!last) {
  271. return [false, 0];
  272. }
  273. this.constraints.pop();
  274. const numb = parseFloat(last[1]);
  275. return [true, isNaN(numb) ? 0 : numb];
  276. }
  277. }
  278. Precondition.queryPriorities = [
  279. /^self::\*$/,
  280. /^self::[\w-]+$/,
  281. /^self::\*\[.+\]$/,
  282. /^self::[\w-]+\[.+\]$/
  283. ];
  284. Precondition.attributePriorities = [
  285. /^@[\w-]+$/,
  286. /^@[\w-]+!=".+"$/,
  287. /^not\(contains\(@[\w-]+,\s*".+"\)\)$/,
  288. /^contains\(@[\w-]+,".+"\)$/,
  289. /^@[\w-]+=".+"$/
  290. ];
  291. export class OutputError extends SREError {
  292. constructor(msg) {
  293. super(msg);
  294. this.name = 'RuleError';
  295. }
  296. }
  297. function splitString(str, sep) {
  298. const strList = [];
  299. let prefix = '';
  300. while (str !== '') {
  301. const sepPos = str.search(sep);
  302. if (sepPos === -1) {
  303. if ((str.match(/"/g) || []).length % 2 !== 0) {
  304. throw new OutputError('Invalid string in expression: ' + str);
  305. }
  306. strList.push(prefix + str);
  307. prefix = '';
  308. str = '';
  309. }
  310. else if ((str.substring(0, sepPos).match(/"/g) || []).length % 2 === 0) {
  311. strList.push(prefix + str.substring(0, sepPos));
  312. prefix = '';
  313. str = str.substring(sepPos + 1);
  314. }
  315. else {
  316. const nextQuot = str.substring(sepPos).search('"');
  317. if (nextQuot === -1) {
  318. throw new OutputError('Invalid string in expression: ' + str);
  319. }
  320. else {
  321. prefix = prefix + str.substring(0, sepPos + nextQuot + 1);
  322. str = str.substring(sepPos + nextQuot + 1);
  323. }
  324. }
  325. }
  326. if (prefix) {
  327. strList.push(prefix);
  328. }
  329. return strList;
  330. }