UNPKG

@predictive-text-studio/lexical-model-compiler

Version:
136 lines 5.69 kB
"use strict"; /* lexical-model-compiler.ts: base file for lexical model compiler. */ Object.defineProperty(exports, "__esModule", { value: true }); /// <reference path="./model-info-file.ts" /> const build_trie_1 = require("./build-trie"); const join_word_breaker_decorator_1 = require("./join-word-breaker-decorator"); const script_overrides_decorator_1 = require("./script-overrides-decorator"); const parse_wordlist_1 = require("./parse-wordlist"); class DefaultLexicalModelCompiler { constructor() { this.snippets = []; } emit(code) { this.snippets.push(code); } emitLine(code) { this.emit((code + "\n")); } get func() { return this.snippets.join(''); } /** * Returns the generated code for the model that will ultimately be loaded by * the LMLayer worker. This code contains all model parameters, and specifies * word breakers and auxilary functions that may be required. * * @param model_id The model ID. TODO: not sure if this is actually required! * @param modelSource A specification of the model to compile * @param sourcePath Where to find auxilary sources files */ compile(modelSource, getWordList) { this.emitLine(`(function() {`); this.emitLine(`'use strict';`); // TODO: add metadata in comment switch (modelSource.format) { case "custom-1.0": case "fst-foma-1.0": throw new ModelSourceError(`Unimplemented model format: ${modelSource.format}`); case "trie-1.0": let wordlist = modelSource.sources.reduce((wl, source) => { let wordlistSource = typeof source === "string" ? getWordList(source) : source; parse_wordlist_1.parseWordList(wl, wordlistSource); return wl; }, {}); // Use the default search term to key function, if left unspecified. let searchTermToKey = modelSource.searchTermToKey || build_trie_1.defaultSearchTermToKey; this.emit(`LMLayerWorker.loadModel(new models.TrieModel(${build_trie_1.compileTrieFromWordlist(wordlist, searchTermToKey)}, {\n`); let wordBreakerSourceCode = compileWordBreaker(normalizeWordBreakerSpec(modelSource.wordBreaker)); this.emit(` wordBreaker: ${wordBreakerSourceCode},\n`); this.emit(` searchTermToKey: ${searchTermToKey.toString()},\n`); if (modelSource.punctuation) { this.emit(` punctuation: ${JSON.stringify(modelSource.punctuation)},\n`); } this.emit(`}));\n`); break; default: throw new ModelSourceError(`Unknown model format: ${modelSource.format}`); } this.emit(`})();`); return this.func; } } exports.DefaultLexicalModelCompiler = DefaultLexicalModelCompiler; ; class ModelSourceError extends Error { } exports.ModelSourceError = ModelSourceError; /** * Returns a JavaScript expression (as a string) that can serve as a word * breaking function. */ function compileWordBreaker(spec) { let wordBreakerCode = compileInnerWordBreaker(spec.use); if (spec.joinWordsAt) { wordBreakerCode = compileJoinDecorator(spec, wordBreakerCode); } if (spec.overrideScriptDefaults) { wordBreakerCode = compileScriptOverrides(spec, wordBreakerCode); } return wordBreakerCode; } function compileJoinDecorator(spec, existingWordBreakerCode) { // Bundle the source of the join decorator, as an IIFE, // like this: (function join(breaker, joiners) {/*...*/}(breaker, joiners)) // The decorator will run IMMEDIATELY when the model is loaded, // by the LMLayer returning the decorated word breaker to the // LMLayer model. let joinerExpr = JSON.stringify(spec.joinWordsAt); return `(${join_word_breaker_decorator_1.decorateWithJoin.toString()}(${existingWordBreakerCode}, ${joinerExpr}))`; } function compileScriptOverrides(spec, existingWordBreakerCode) { return `(${script_overrides_decorator_1.decorateWithScriptOverrides.toString()}(${existingWordBreakerCode}, '${spec.overrideScriptDefaults}'))`; } /** * Compiles the base word breaker, that may be decorated later. * Returns the source code of a JavaScript expression. */ function compileInnerWordBreaker(spec) { if (typeof spec === "string") { // It must be a builtin word breaker, so just instantiate it. return `wordBreakers['${spec}']`; } else { // It must be a function: return spec.toString() // Note: the .toString() might just be the property name, but we want a // plain function: .replace(/^wordBreak(ing|er)\b/, 'function'); } } /** * Given a word breaker specification in any of the messy ways, * normalizes it to a common form that the compiler can deal with. */ function normalizeWordBreakerSpec(wordBreakerSpec) { if (wordBreakerSpec == undefined) { // Use the default word breaker when it's unspecified return { use: 'default' }; } else if (isSimpleWordBreaker(wordBreakerSpec)) { // The word breaker was passed as a literal function; use its source code. return { use: wordBreakerSpec }; } else if (wordBreakerSpec.use) { return wordBreakerSpec; } else { throw new Error(`Unknown word breaker: ${wordBreakerSpec}`); } } function isSimpleWordBreaker(spec) { return typeof spec === "function" || spec === "default" || spec === "ascii"; } //# sourceMappingURL=lexical-model-compiler.js.map