UNPKG

skynovel

Version:
268 lines 11.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const P = require("parsimmon"); const m_xregexp = require("xregexp"); const CmnLib_1 = require("./CmnLib"); class PropParser { constructor(val) { this.val = val; this.parser = null; this.hFnc = { '!num!': a => a.shift(), '!str!': a => this.procEmbedVar(a.shift()), '!bool!': a => a.shift(), '!': a => { const b = a.shift(); return (b[0] == '!bool!') ? !Boolean(b[1]) : !(String(this.calc(b)) == 'true'); }, '~': a => ~Number(this.calc(a.shift())), '**': a => Number(this.calc(a.shift())) ** Number(this.calc(a.shift())), '*': a => Number(this.calc(a.shift())) * Number(this.calc(a.shift())), '/': a => Number(this.calc(a.shift())) / Number(this.calc(a.shift())), '¥': a => Math.floor(this.hFnc['/'](a)), '%': a => Number(this.calc(a.shift())) % Number(this.calc(a.shift())), '+': a => { const b = this.calc(a.shift()); const c = this.calc(a.shift()); if (Object.prototype.toString.call(b) == '[object String]' || Object.prototype.toString.call(c) == '[object String]') { return String(b) + String(c); } return Number(b) + Number(c); }, '-': a => Number(this.calc(a.shift())) - Number(this.calc(a.shift())), 'int': a => CmnLib_1.int(this.fncSub_ChkNum(a.shift())), 'parseInt': a => CmnLib_1.int(this.hFnc['Number'](a)), 'Number': a => { const b = this.calc(a.shift()); if (Object.prototype.toString.call(b) != '[object String]') return Number(b); return this.fncSub_ChkNum(this.parser.parse(String(b)).value); }, 'ceil': a => Math.ceil(this.fncSub_ChkNum(a.shift())), 'floor': a => Math.floor(this.fncSub_ChkNum(a.shift())), 'round': a => Math.round(this.fncSub_ChkNum(a.shift())), '<<': a => Number(this.calc(a.shift())) << Number(this.calc(a.shift())), '>>': a => Number(this.calc(a.shift())) >> Number(this.calc(a.shift())), '>>>': a => Number(this.calc(a.shift())) >>> Number(this.calc(a.shift())), '<': a => Number(this.calc(a.shift())) < Number(this.calc(a.shift())), '<=': a => Number(this.calc(a.shift())) <= Number(this.calc(a.shift())), '>': a => Number(this.calc(a.shift())) > Number(this.calc(a.shift())), '>=': a => Number(this.calc(a.shift())) >= Number(this.calc(a.shift())), '==': a => { const b = this.calc(a.shift()); const c = this.calc(a.shift()); if ((b == null) && (c == null) && (!b || !c)) return (b == c); return String(b) == String(c); }, '!=': a => !this.hFnc['=='](a), '===': a => { const b = this.calc(a.shift()); const c = this.calc(a.shift()); if (Object.prototype.toString.call(b) != Object.prototype.toString.call(c)) return false; return String(b) == String(c); }, '!==': a => !this.hFnc['==='](a), '&': a => Number(this.calc(a.shift())) & Number(this.calc(a.shift())), '^': a => Number(this.calc(a.shift())) ^ Number(this.calc(a.shift())), '|': a => Number(this.calc(a.shift())) | Number(this.calc(a.shift())), '&&': a => (String(this.calc(a.shift())) == 'true') && (String(this.calc(a.shift())) == 'true'), '||': a => (String(this.calc(a.shift())) == 'true') || (String(this.calc(a.shift())) == 'true'), '?': a => { const b = a.shift(); let cond = false; if (b[0] == '!bool!') { cond = Boolean(b[1]); } else { const cond2 = String(this.calc(b)); cond = (cond2 != 'true' && cond2 != 'false') ? (CmnLib_1.int(cond2) != 0) : (cond2 == 'true'); } const elm2 = a.shift(); if (elm2[0] != ':') throw Error('(PropParser)三項演算子の文法エラーです。: が見つかりません'); return this.calc(elm2[cond ? 1 : 2]); }, ':': () => { throw Error('(PropParser)三項演算子の文法エラーです。? が見つかりません'); }, }; this.REG_EMBEDVAR = /(\$((tmp|sys|save|mp):)?[^\s!--\/:-@[-^`{-~]+|\#\{[^\}]+})/g; this.getValAmpersand = (val) => (val.charAt(0) == '&') ? String(this.parse(val.substr(1))) : val; function ope(a) { const ps = []; for (const v of a) ps.push(((v instanceof RegExp) ? P.regex(v) : P.string(v)) .trim(P.optWhitespace)); return P.alt.apply(null, ps); } function PREFIX(operatorsParser, nextParser) { const parser = P.lazy(() => { return P.seq(operatorsParser, parser).or(nextParser); }); return parser; } function BINARY_RIGHT(operatorsParser, nextParser) { let parser = P.lazy(() => nextParser.chain((next) => P.seq(operatorsParser, P.of(next), parser).or(P.of(next)))); return parser; } function BINARY_LEFT(operatorsParser, nextParser) { return P.seqMap(nextParser, P.seq(operatorsParser, nextParser).many(), (first, rest) => { return rest.reduce((acc, ch) => { return [ch[0], acc, ch[1]]; }, first); }); } const Num = P.alt(P.alt(P.regex(/-?(0|[1-9][0-9]*)\.[0-9]+/), P.regex(/0x[0-9a-fA-F]+/)).map(Number), P.alt(P.regex(/-?(0|[1-9][0-9]*)/)).map(n => CmnLib_1.int(n))) .map(str => ['!num!', str]) .desc('number'); const NullLiteral = P.string('null') .map(() => ['!str!', null]); const BooleanLiteral = P.regex(/(true|false)/) .map(b => ['!bool!', b == 'true']) .desc('boolean'); const StringLiteral = P .regex(/("|'|#).*?\1/) .map(b => ['!str!', b.slice(1, -1)]) .desc('string'); const REG_BRACKETS = /\[[^\]]+\]/g; const VarLiteral = P .regex(/\-?((tmp|sys|save|mp):)?[^\s!-\/:-@[-^`{-~]+(\.[^\s!-\/:-@[-^`{-~]+|\[[^\]]+\])*(@str)?/) .map(b => { const s = String(b).replace(REG_BRACKETS, v => '.' + this.parse(v.slice(1, -1))); if (s.charAt(0) == '-') { const val = this.val.getVal(s.slice(1)); if (val == null || String(val) == 'null') throw Error('(PropParser)数値以外に-符号がついています'); return ['!num!', -Number(val)]; } const val = this.val.getVal(s); if (val == null) return ['!str!', val]; if (typeof val == 'boolean') return ['!bool!', val]; return (Object.prototype.toString.call(val) == '[object String]') ? ['!str!', String(val)] : ['!num!', Number(val)]; }) .desc('string'); const Basic = P.lazy(() => P .string('(').then(this.parser).skip(P.string(')')) .or(Num) .or(NullLiteral) .or(BooleanLiteral) .or(StringLiteral) .or(VarLiteral)); const table = [ { type: PREFIX, ops: ope([/[A-Za-z_][A-Za-z0-9_]*(?=\()/]) }, { type: PREFIX, ops: ope([/(!(?!=)|~)/]) }, { type: BINARY_RIGHT, ops: ope(['**']) }, { type: BINARY_LEFT, ops: ope(['*', '/', '¥', '%']) }, { type: BINARY_LEFT, ops: ope(['+', '-']) }, { type: BINARY_LEFT, ops: ope([/(>>>|<<|>>)/]) }, { type: BINARY_LEFT, ops: ope([/(<=|<|>=|>)/]) }, { type: BINARY_LEFT, ops: ope([/(===|!==|==|!=)/]) }, { type: BINARY_LEFT, ops: ope([/&(?!&)/]) }, { type: BINARY_LEFT, ops: ope(['^']) }, { type: BINARY_LEFT, ops: ope([/\|(?!\|)/]) }, { type: BINARY_LEFT, ops: ope(['&&']) }, { type: BINARY_LEFT, ops: ope(['||']) }, { type: BINARY_RIGHT, ops: ope([':']) }, { type: BINARY_RIGHT, ops: ope(['?']) }, ]; const tableParser = table.reduce((acc, level) => level.type(level.ops, acc), Basic); this.parser = tableParser.trim(P.optWhitespace); } parse(s) { const p = this.parser.parse(s); if (!p.status) throw Error('(PropParser)文法エラー【' + s + '】'); const a = p.value; if (a[0] == '!str!') return this.procEmbedVar(a[1]); return this.calc(a); } calc(a) { const elm = a.shift(); if (elm instanceof Array) return this.calc(elm); const fnc = this.hFnc[elm]; return (fnc) ? fnc(a) : Object(null); } fncSub_ChkNum(v) { const b = this.calc(v); if (Object.prototype.toString.call(b) != '[object Number]') throw Error('(PropParser)引数【' + b + '】が数値ではありません'); return Number(b); } procEmbedVar(b) { if (b == null) return b; return String(b).replace(this.REG_EMBEDVAR, v => { return (v.charAt(0) == '$') ? this.val.getVal(v.slice(1)) : this.parse(v.slice(2, -1)); }); } static getValName(arg_name) { var _a; const a = m_xregexp.exec(CmnLib_1.trim(arg_name), this.REG_VAL); if (!a) return undefined; return { scope: a.scope || 'tmp', name: PropParser.getValName_B2D(a.name), at: (_a = a.at) !== null && _a !== void 0 ? _a : '', }; } static getValName_B2D(str) { let i = 0, e = 0; while (true) { i = str.indexOf('["'); if (i < 0) { i = str.indexOf("['"); if (i < 0) break; e = str.indexOf("']", i + 2); } else { e = str.indexOf('"]', i + 2); } if (e < 0) break; str = str.slice(0, i) + '.' + str.slice(i + 2, e) + str.slice(e + 2); i = e - 2; } return str; } } exports.PropParser = PropParser; PropParser.REG_VAL = m_xregexp('^((?<scope>\\w+?):)?(?<name>[^\\s :@]+)(?<at>\\@str)?$'); //# sourceMappingURL=PropParser.js.map