cli.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
  2. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  3. return new (P || (P = Promise))(function (resolve, reject) {
  4. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  5. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  6. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  7. step((generator = generator.apply(thisArg, _arguments || [])).next());
  8. });
  9. };
  10. import { Axis, DynamicCstr } from '../rule_engine/dynamic_cstr.js';
  11. import * as MathCompoundStore from '../rule_engine/math_compound_store.js';
  12. import { SpeechRuleEngine } from '../rule_engine/speech_rule_engine.js';
  13. import { ClearspeakPreferences } from '../speech_rules/clearspeak_preferences.js';
  14. import { Debugger } from './debugger.js';
  15. import { EnginePromise, SREError } from './engine.js';
  16. import * as EngineConst from './engine_const.js';
  17. import * as ProcessorFactory from './processor_factory.js';
  18. import * as System from './system.js';
  19. import { SystemExternal } from './system_external.js';
  20. import { Variables } from './variables.js';
  21. export class Cli {
  22. constructor() {
  23. this.setup = {
  24. mode: EngineConst.Mode.SYNC
  25. };
  26. this.processors = [];
  27. this.output = Cli.process.stdout;
  28. this.dp = new SystemExternal.xmldom.DOMParser({
  29. onError: (_key, _msg) => {
  30. throw new SREError('XML DOM error!');
  31. }
  32. });
  33. }
  34. set(arg, value, _def) {
  35. this.setup[arg] = typeof value === 'undefined' ? true : value;
  36. }
  37. processor(processor) {
  38. this.processors.push(processor);
  39. }
  40. loadLocales() {
  41. return __awaiter(this, void 0, void 0, function* () {
  42. for (const loc of Variables.LOCALES.keys()) {
  43. yield System.setupEngine({ locale: loc });
  44. }
  45. });
  46. }
  47. enumerate() {
  48. return __awaiter(this, arguments, void 0, function* (all = false) {
  49. const promise = System.setupEngine(this.setup);
  50. const order = DynamicCstr.DEFAULT_ORDER.slice(0, -1);
  51. return (all ? this.loadLocales() : promise).then(() => EnginePromise.getall().then(() => {
  52. const length = order.map((x) => x.length);
  53. const maxLength = (obj, index) => {
  54. length[index] = Math.max.apply(null, Object.keys(obj)
  55. .map((x) => x.length)
  56. .concat(length[index]));
  57. };
  58. const compStr = (str, length) => str + new Array(length - str.length + 1).join(' ');
  59. let dynamic = SpeechRuleEngine.getInstance().enumerate();
  60. dynamic = MathCompoundStore.enumerate(dynamic);
  61. const table = [];
  62. maxLength(dynamic, 0);
  63. for (const [ax1, dyna1] of Object.entries(dynamic)) {
  64. let clear1 = true;
  65. maxLength(dyna1, 1);
  66. for (const [ax2, dyna2] of Object.entries(dyna1)) {
  67. let clear2 = true;
  68. maxLength(dyna2, 2);
  69. for (const [ax3, dyna3] of Object.entries(dyna2)) {
  70. const styles = Object.keys(dyna3).sort();
  71. if (ax3 === 'clearspeak') {
  72. let clear3 = true;
  73. const prefs = ClearspeakPreferences.getLocalePreferences(dynamic)[ax1];
  74. if (!prefs) {
  75. continue;
  76. }
  77. for (const dyna4 of Object.values(prefs)) {
  78. table.push([
  79. compStr(clear1 ? ax1 : '', length[0]),
  80. compStr(clear2 ? ax2 : '', length[1]),
  81. compStr(clear3 ? ax3 : '', length[2]),
  82. dyna4.join(', ')
  83. ]);
  84. clear1 = false;
  85. clear2 = false;
  86. clear3 = false;
  87. }
  88. }
  89. else {
  90. table.push([
  91. compStr(clear1 ? ax1 : '', length[0]),
  92. compStr(clear2 ? ax2 : '', length[1]),
  93. compStr(ax3, length[2]),
  94. styles.join(', ')
  95. ]);
  96. }
  97. clear1 = false;
  98. clear2 = false;
  99. }
  100. }
  101. }
  102. let i = 0;
  103. const header = order.map((x) => compStr(x, length[i++]));
  104. const markdown = Cli.commander.opts().pprint;
  105. const separator = length.map((x) => new Array(x + 1).join(markdown ? '-' : '='));
  106. if (!markdown) {
  107. separator[i - 1] = separator[i - 1] + '========================';
  108. }
  109. table.unshift(separator);
  110. table.unshift(header);
  111. let output = table.map((x) => x.join(' | '));
  112. if (markdown) {
  113. output = output.map((x) => `| ${x} |`);
  114. output.unshift(`# Options SRE v${System.version}\n`);
  115. }
  116. console.info(output.join('\n'));
  117. }));
  118. });
  119. }
  120. execute(input) {
  121. EnginePromise.getall().then(() => {
  122. this.runProcessors_((proc, file) => this.output.write(System.processFile(proc, file) + '\n'), input);
  123. });
  124. }
  125. readline() {
  126. Cli.process.stdin.setEncoding('utf8');
  127. const inter = SystemExternal.extRequire('readline').createInterface({
  128. input: Cli.process.stdin,
  129. output: this.output
  130. });
  131. let input = '';
  132. inter.on('line', ((expr) => {
  133. input += expr;
  134. if (this.readExpression_(input)) {
  135. inter.close();
  136. }
  137. }).bind(this));
  138. inter.on('close', (() => {
  139. this.runProcessors_((proc, expr) => {
  140. inter.output.write(ProcessorFactory.output(proc, expr) + '\n');
  141. }, input);
  142. System.engineReady().then(() => Debugger.getInstance().exit(() => System.exit(0)));
  143. }).bind(this));
  144. }
  145. commandLine() {
  146. return __awaiter(this, void 0, void 0, function* () {
  147. const commander = Cli.commander;
  148. const system = System;
  149. const set = ((key) => {
  150. return (val, def) => this.set(key, val, def);
  151. }).bind(this);
  152. const processor = this.processor.bind(this);
  153. commander
  154. .version(system.version)
  155. .usage('[options] <file ...>')
  156. .option('-i, --input [name]', 'Input file [name]. (Deprecated)')
  157. .option('-o, --output [name]', 'Output file [name]. Defaults to stdout.')
  158. .option('-d, --domain [name]', 'Speech rule set [name]. See --options' + ' for details.', set(Axis.DOMAIN), DynamicCstr.DEFAULT_VALUES[Axis.DOMAIN])
  159. .option('-s, --style [name]', 'Speech style [name]. See --options' + ' for details.', set(Axis.STYLE), DynamicCstr.DEFAULT_VALUES[Axis.STYLE])
  160. .option('-c, --locale [code]', 'Locale [code].', set(Axis.LOCALE), DynamicCstr.DEFAULT_VALUES[Axis.LOCALE])
  161. .option('-b, --modality [name]', 'Modality [name].', set(Axis.MODALITY), DynamicCstr.DEFAULT_VALUES[Axis.MODALITY])
  162. .option('-k, --markup [name]', 'Generate speech output with markup tags.', set('markup'), 'none')
  163. .option('-e, --automark', 'Automatically set marks for external reference.', set('automark'))
  164. .option('-L, --linebreaks', 'Linebreak marking in 2D output.', set('linebreaks'))
  165. .option('-r, --rate [value]', 'Base rate [value] for tagged speech' + ' output.', set('rate'), '100')
  166. .option('-p, --speech', 'Generate speech output (default).', () => processor('speech'))
  167. .option('-a, --audit', 'Generate auditory descriptions (JSON format).', () => processor('description'))
  168. .option('-j, --json', 'Generate JSON of semantic tree.', () => processor('json'))
  169. .option('-x, --xml', 'Generate XML of semantic tree.', () => processor('semantic'))
  170. .option('-m, --mathml', 'Generate enriched MathML.', () => processor('enriched'))
  171. .option('-u, --rebuild', 'Rebuild semantic tree from enriched MathML.', () => processor('rebuild'))
  172. .option('-t, --latex', 'Accepts LaTeX input for certain locale/modality combinations.', () => processor('latex'))
  173. .option('-g, --generate <depth>', 'Include generated speech in enriched' +
  174. ' MathML (with -m option only).', set('speech'), 'none')
  175. .option('-w, --structure', 'Include structure attribute in enriched' +
  176. ' MathML (with -m option only).', set('structure'))
  177. .option('-A, --aria', 'Include aria tree annotations' +
  178. ' MathML (with -m and -w option only).', set('aria'))
  179. .option('-P, --pprint', 'Pretty print output whenever possible.', set('pprint'))
  180. .option('-f, --rules [name]', 'Loads a local rule file [name].', set('rules'))
  181. .option('-C, --subiso [name]', 'Supplementary country code (or similar) for the given locale.', set('subiso'))
  182. .option('-N, --number', 'Translate number to word.', () => processor('number'))
  183. .option('-O, --ordinal', 'Translate number to ordinal.', () => processor('ordinal'), 'ordinal')
  184. .option('-S, --numeric', 'Translate number to numeric ordinal.', () => processor('numericOrdinal'))
  185. .option('-F, --vulgar', 'Translate vulgar fraction to word. Provide vulgar fraction as slash seperated numbers.', () => processor('vulgar'))
  186. .option('-v, --verbose', 'Verbose mode.')
  187. .option('-l, --log [name]', 'Log file [name].')
  188. .option('--opt', 'List engine setup options. Output as markdown with -P option.')
  189. .option('--opt-all', 'List engine setup options for all available locales. Output as markdown with -P option.')
  190. .on('option:opt', () => {
  191. this.enumerate().then(() => System.exit(0));
  192. })
  193. .on('option:opt-all', () => {
  194. this.enumerate(true).then(() => System.exit(0));
  195. })
  196. .parse(Cli.process.argv);
  197. yield System.engineReady().then(() => System.setupEngine(this.setup));
  198. const options = Cli.commander.opts();
  199. if (options.output) {
  200. this.output = SystemExternal.fs.createWriteStream(options.output);
  201. }
  202. if (options.verbose) {
  203. yield Debugger.getInstance().init(options.log);
  204. }
  205. if (options.input) {
  206. this.execute(options.input);
  207. }
  208. if (Cli.commander.args.length) {
  209. Cli.commander.args.forEach(this.execute.bind(this));
  210. System.engineReady().then(() => Debugger.getInstance().exit(() => System.exit(0)));
  211. }
  212. else {
  213. this.readline();
  214. }
  215. });
  216. }
  217. runProcessors_(processor, input) {
  218. try {
  219. if (!this.processors.length) {
  220. this.processors.push('speech');
  221. }
  222. if (input) {
  223. this.processors.forEach((proc) => processor(proc, input));
  224. }
  225. }
  226. catch (err) {
  227. console.error(err.name + ': ' + err.message);
  228. Debugger.getInstance().exit(() => Cli.process.exit(1));
  229. }
  230. }
  231. readExpression_(input) {
  232. try {
  233. const testInput = input.replace(/(&|#|;)/g, '');
  234. this.dp.parseFromString(testInput, 'text/xml');
  235. }
  236. catch (_err) {
  237. return false;
  238. }
  239. return true;
  240. }
  241. }
  242. Cli.process = SystemExternal.extRequire('process');
  243. Cli.commander = SystemExternal.documentSupported
  244. ? null
  245. : SystemExternal.extRequire('commander').program;