diff options
| -rw-r--r-- | ext/bg/js/template-renderer.js | 127 | 
1 files changed, 125 insertions, 2 deletions
| diff --git a/ext/bg/js/template-renderer.js b/ext/bg/js/template-renderer.js index f6c4374b..ef05cbd8 100644 --- a/ext/bg/js/template-renderer.js +++ b/ext/bg/js/template-renderer.js @@ -25,6 +25,7 @@ class TemplateRenderer {          this._cache = new Map();          this._cacheMaxSize = 5;          this._helpersRegistered = false; +        this._stateStack = null;      }      async render(template, data) { @@ -41,7 +42,12 @@ class TemplateRenderer {              cache.set(template, instance);          } -        return instance(data).trim(); +        try { +            this._stateStack = [new Map()]; +            return instance(data).trim(); +        } finally { +            this._stateStack = null; +        }      }      // Private @@ -70,7 +76,13 @@ class TemplateRenderer {              ['regexReplace',     this._regexReplace.bind(this)],              ['regexMatch',       this._regexMatch.bind(this)],              ['mergeTags',        this._mergeTags.bind(this)], -            ['eachUpTo',         this._eachUpTo.bind(this)] +            ['eachUpTo',         this._eachUpTo.bind(this)], +            ['spread',           this._spread.bind(this)], +            ['op',               this._op.bind(this)], +            ['get',              this._get.bind(this)], +            ['set',              this._set.bind(this)], +            ['scope',            this._scope.bind(this)], +            ['isMoraPitchHigh',  this._isMoraPitchHigh.bind(this)]          ];          for (const [name, helper] of helpers) { @@ -224,4 +236,115 @@ class TemplateRenderer {          }          return options.inverse(context);      } + +    _spread(context, ...args) { +        const result = []; +        for (let i = 0, ii = args.length - 1; i < ii; ++i) { +            try { +                result.push(...args[i]); +            } catch (e) { +                // NOP +            } +        } +        return result; +    } + +    _op(context, ...args) { +        switch (args.length) { +            case 3: return this._evaluateUnaryExpression(args[0], args[1]); +            case 4: return this._evaluateBinaryExpression(args[0], args[1], args[2]); +            case 5: return this._evaluateTernaryExpression(args[0], args[1], args[2], args[3]); +            default: return void 0; +        } +    } + +    _evaluateUnaryExpression(operator, operand1) { +        switch (operator) { +            case '+': return +operand1; +            case '-': return -operand1; +            case '~': return ~operand1; +            case '!': return !operand1; +            default: return void 0; +        } +    } + +    _evaluateBinaryExpression(operator, operand1, operand2) { +        switch (operator) { +            case '+': return operand1 + operand2; +            case '-': return operand1 - operand2; +            case '/': return operand1 / operand2; +            case '*': return operand1 * operand2; +            case '%': return operand1 % operand2; +            case '**': return operand1 ** operand2; +            case '==': return operand1 == operand2; // eslint-disable-line eqeqeq +            case '!=': return operand1 != operand2; // eslint-disable-line eqeqeq +            case '===': return operand1 === operand2; +            case '!==': return operand1 !== operand2; +            case '<':  return operand1 < operand2; +            case '<=': return operand1 <= operand2; +            case '>':  return operand1 > operand2; +            case '>=': return operand1 >= operand2; +            case '<<': return operand1 << operand2; +            case '>>': return operand1 >> operand2; +            case '>>>': return operand1 >>> operand2; +            case '&': return operand1 & operand2; +            case '|': return operand1 | operand2; +            case '^': return operand1 ^ operand2; +            case '&&': return operand1 && operand2; +            case '||': return operand1 || operand2; +            default: return void 0; +        } +    } + +    _evaluateTernaryExpression(operator, operand1, operand2, operand3) { +        switch (operator) { +            case '?:': return operand1 ? operand2 : operand3; +            default: return void 0; +        } +    } + +    _get(context, key) { +        for (let i = this._stateStack.length; --i >= 0;) { +            const map = this._stateStack[i]; +            if (map.has(key)) { +                return map.get(key); +            } +        } +        return void 0; +    } + +    _set(context, ...args) { +        switch (args.length) { +            case 2: +            { +                const [key, options] = args; +                const value = options.fn(context); +                this._stateStack[this._stateStack.length - 1].set(key, value); +                return value; +            } +            case 3: +            { +                const [key, value] = args; +                this._stateStack[this._stateStack.length - 1].set(key, value); +                return value; +            } +            default: +                return void 0; +        } +    } + +    _scope(context, options) { +        try { +            this._stateStack.push(new Map()); +            return options.fn(context); +        } finally { +            if (this._stateStack.length > 1) { +                this._stateStack.pop(); +            } +        } +    } + +    _isMoraPitchHigh(context, position, index) { +        return jp.isMoraPitchHigh(index, position); +    }  } |