UNPKG

@taquito/michel-codec

Version:

Michelson parser, validator, and formatter for Taquito.

1,369 lines (1,360 loc) 220 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@taquito/core')) : typeof define === 'function' && define.amd ? define(['exports', '@taquito/core'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.taquitoMichelCodec = {}, global.core)); })(this, (function (exports, core) { 'use strict'; // Michelson abstract syntax tree types https://tezos.gitlab.io/whitedoc/michelson.html#concrete-syntax const sourceReference = Symbol('source_reference'); /** * @category Error * Error that indicates a failure when performing the scan step when parsing Michelson */ class ScanError extends core.TaquitoError { constructor(src, idx, message) { super(); this.src = src; this.idx = idx; this.message = message; this.name = 'ScanError'; } } var Literal; (function (Literal) { Literal[Literal["Comment"] = 0] = "Comment"; Literal[Literal["Number"] = 1] = "Number"; Literal[Literal["String"] = 2] = "String"; Literal[Literal["Bytes"] = 3] = "Bytes"; Literal[Literal["Ident"] = 4] = "Ident"; })(Literal || (Literal = {})); const isSpace = new RegExp('\\s'); const isIdentStart = new RegExp('[:@%_A-Za-z]'); const isIdent = new RegExp('[@%_\\.A-Za-z0-9]'); const isDigit = new RegExp('[0-9]'); const isHex = new RegExp('[0-9a-fA-F]'); function* scan(src, scanComments = false) { let i = 0; while (i < src.length) { // Skip space while (i < src.length && isSpace.test(src[i])) { i++; } if (i === src.length) { return; } const s = src[i]; const start = i; if (isIdentStart.test(s)) { // Identifier i++; while (i < src.length && isIdent.test(src[i])) { i++; } yield { t: Literal.Ident, v: src.slice(start, i), first: start, last: i }; } else if (src.length - i > 1 && src.substring(i, i + 2) === '0x') { // Bytes i += 2; while (i < src.length && isHex.test(src[i])) { i++; } if (((i - start) & 1) !== 0) { throw new ScanError(src, i, 'Bytes literal length is expected to be power of two'); } yield { t: Literal.Bytes, v: src.slice(start, i), first: start, last: i }; } else if (isDigit.test(s) || s === '-') { // Number if (s === '-') { i++; } const ii = i; while (i < src.length && isDigit.test(src[i])) { i++; } if (ii === i) { throw new ScanError(src, i, 'Number literal is too short'); } yield { t: Literal.Number, v: src.slice(start, i), first: start, last: i }; } else if (s === '"') { // String i++; let esc = false; for (; i < src.length && (esc || src[i] !== '"'); i++) { if (!esc && src[i] === '\\') { esc = true; } else { esc = false; } } if (i === src.length) { throw new ScanError(src, i, 'Unterminated string literal'); } i++; yield { t: Literal.String, v: src.slice(start, i), first: start, last: i }; } else if (s === '#') { // Comment i++; while (i < src.length && src[i] !== '\n') { i++; } if (scanComments) { yield { t: Literal.Comment, v: src.slice(start, i), first: start, last: i }; } } else if (src.length - i > 1 && src.substring(i, i + 2) === '/*') { // C style comment i += 2; while (i < src.length && !(src.length - i > 1 && src.substring(i, i + 2) === '*/')) { i++; } if (i === src.length) { throw new ScanError(src, i, 'Unterminated C style comment'); } i += 2; if (scanComments) { yield { t: Literal.Comment, v: src.slice(start, i), first: start, last: i }; } } else if (s === '(' || s === ')' || s === '{' || s === '}' || s === ';') { i++; yield { t: s, v: s, first: start, last: i }; } else { throw new ScanError(src, i, `Invalid character at offset ${i}: \`${s}'`); } } } // Michelson types const refContract = Symbol('ref_contract'); exports.Protocol = void 0; (function (Protocol) { Protocol["Ps9mPmXa"] = "Ps9mPmXaRzmzk35gbAYNCAw6UXdE2qoABTHbN2oEEc1qM7CwT9P"; Protocol["PtCJ7pwo"] = "PtCJ7pwoxe8JasnHY8YonnLYjcVHmhiARPJvqcC6VfHT5s8k8sY"; Protocol["PsYLVpVv"] = "PsYLVpVvgbLhAhoqAkMFUo6gudkJ9weNXhUYCiLDzcUpFpkk8Wt"; Protocol["PsddFKi3"] = "PsddFKi32cMJ2qPjf43Qv5GDWLDPZb3T3bF6fLKiF5HtvHNU7aP"; Protocol["Pt24m4xi"] = "Pt24m4xiPbLDhVgVfABUjirbmda3yohdN82Sp9FeuAXJ4eV9otd"; Protocol["PsBABY5H"] = "PsBABY5HQTSkA4297zNHfsZNKtxULfL18y95qb3m53QJiXGmrbU"; Protocol["PsBabyM1"] = "PsBabyM1eUXZseaJdmXFApDSBqj8YBfwELoxZHHW77EMcAbbwAS"; Protocol["PsCARTHA"] = "PsCARTHAGazKbHtnKfLzQg3kms52kSRpgnDY982a9oYsSXRLQEb"; Protocol["PsDELPH1"] = "PsDELPH1Kxsxt8f9eWbxQeRxkjfbxoqM52jvs5Y5fBxWWh4ifpo"; Protocol["PtEdoTez"] = "PtEdoTezd3RHSC31mpxxo1npxFjoWWcFgQtxapi51Z8TLu6v6Uq"; Protocol["PtEdo2Zk"] = "PtEdo2ZkT9oKpimTah6x2embF25oss54njMuPzkJTEi5RqfdZFA"; Protocol["PsFLorena"] = "PsFLorenaUUuikDWvMDr6fGBRG8kt3e3D3fHoXK1j1BFRxeSH4i"; Protocol["PtGRANADs"] = "PtGRANADsDU8R9daYKAgWnQYAJ64omN1o3KMGVCykShA97vQbvV"; Protocol["PtHangzH"] = "PtHangzHogokSuiMHemCuowEavgYTP8J5qQ9fQS793MHYFpCY3r"; Protocol["PtHangz2"] = "PtHangz2aRngywmSRGGvrcTyMbbdpWdpFKuS4uMWxg2RaH9i1qx"; Protocol["PsiThaCa"] = "PsiThaCaT47Zboaw71QWScM8sXeMM7bbQFncK9FLqYc6EKdpjVP"; Protocol["Psithaca2"] = "Psithaca2MLRFYargivpo7YvUr7wUDqyxrdhC5CQq78mRvimz6A"; Protocol["PtJakarta"] = "PtJakartaiDz69SfDDLXJSiuZqTSeSKRDbKVZC8MNzJnvRjvnGw"; Protocol["PtJakart2"] = "PtJakart2xVj7pYXJBXrqHgd82rdkLey5ZeeGwDgPp9rhQUbSqY"; Protocol["PtKathman"] = "PtKathmankSpLLDALzWw7CGD2j2MtyveTwboEYokqUCP4a1LxMg"; Protocol["PtLimaPtL"] = "PtLimaPtLMwfNinJi9rCfDPWea8dFgTZ1MeJ9f1m2SRic6ayiwW"; Protocol["PtMumbaii"] = "PtMumbaiiFFEGbew1rRjzSPyzRbA51Tm3RVZL5suHPxSZYDhCEc"; Protocol["PtMumbai2"] = "PtMumbai2TmsJHNGRkD8v8YDbtao7BLUC3wjASn1inAKLFCjaH1"; Protocol["PtNairobi"] = "PtNairobiyssHuh87hEhfVBGCVrK3WnS8Z2FT4ymB5tAa4r1nQf"; Protocol["ProxfordY"] = "ProxfordYmVfjWnRcgjWH36fW6PArwqykTFzotUxRs6gmTcZDuH"; Protocol["PtParisBx"] = "PtParisBxoLz5gzMmn3d9WBQNoPSZakgnkMC2VNuQ3KXfUtUQeZ"; Protocol["PsParisCZ"] = "PsParisCZo7KAh1Z1smVd9ZMZ1HHn5gkzbM94V3PLCpknFWhUAi"; Protocol["PsQuebecn"] = "PsQuebecnLByd3JwTiGadoG4nGWi3HYiLXUjkibeFV8dCFeVMUg"; Protocol["PsRiotuma"] = "PsRiotumaAMotcRoDWW1bysEhQy2n1M5fy8JgRp8jjRfHGmfeA7"; Protocol["PtSeouLou"] = "PtSeouLouXkxhg39oWzjxDWaCydNfR3RxCUrNe4Q9Ro8BTehcbh"; Protocol["PtTALLiNt"] = "PtTALLiNtPec7mE7yY4m3k26J8Qukef3E3ehzhfXgFZKGtDdAXu"; Protocol["PsUshuai9"] = "PsUshuai9QapM5TGj1JpuVGkdxz5GykdnEvS6Rh8SUVrARvZLCY"; Protocol["ProtoALpha"] = "ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK"; })(exports.Protocol || (exports.Protocol = {})); const DefaultProtocol = exports.Protocol.PsUshuai9; const protoLevel = { Ps9mPmXaRzmzk35gbAYNCAw6UXdE2qoABTHbN2oEEc1qM7CwT9P: 0, PtCJ7pwoxe8JasnHY8YonnLYjcVHmhiARPJvqcC6VfHT5s8k8sY: 1, PsYLVpVvgbLhAhoqAkMFUo6gudkJ9weNXhUYCiLDzcUpFpkk8Wt: 2, PsddFKi32cMJ2qPjf43Qv5GDWLDPZb3T3bF6fLKiF5HtvHNU7aP: 3, Pt24m4xiPbLDhVgVfABUjirbmda3yohdN82Sp9FeuAXJ4eV9otd: 4, PsBABY5HQTSkA4297zNHfsZNKtxULfL18y95qb3m53QJiXGmrbU: 5, PsBabyM1eUXZseaJdmXFApDSBqj8YBfwELoxZHHW77EMcAbbwAS: 5, PsCARTHAGazKbHtnKfLzQg3kms52kSRpgnDY982a9oYsSXRLQEb: 6, PsDELPH1Kxsxt8f9eWbxQeRxkjfbxoqM52jvs5Y5fBxWWh4ifpo: 7, PtEdoTezd3RHSC31mpxxo1npxFjoWWcFgQtxapi51Z8TLu6v6Uq: 8, PtEdo2ZkT9oKpimTah6x2embF25oss54njMuPzkJTEi5RqfdZFA: 8, PsFLorenaUUuikDWvMDr6fGBRG8kt3e3D3fHoXK1j1BFRxeSH4i: 9, PtGRANADsDU8R9daYKAgWnQYAJ64omN1o3KMGVCykShA97vQbvV: 10, PtHangzHogokSuiMHemCuowEavgYTP8J5qQ9fQS793MHYFpCY3r: 11, PtHangz2aRngywmSRGGvrcTyMbbdpWdpFKuS4uMWxg2RaH9i1qx: 11, PsiThaCaT47Zboaw71QWScM8sXeMM7bbQFncK9FLqYc6EKdpjVP: 12, Psithaca2MLRFYargivpo7YvUr7wUDqyxrdhC5CQq78mRvimz6A: 12, PtJakartaiDz69SfDDLXJSiuZqTSeSKRDbKVZC8MNzJnvRjvnGw: 13, PtJakart2xVj7pYXJBXrqHgd82rdkLey5ZeeGwDgPp9rhQUbSqY: 13, PtKathmankSpLLDALzWw7CGD2j2MtyveTwboEYokqUCP4a1LxMg: 14, PtLimaPtLMwfNinJi9rCfDPWea8dFgTZ1MeJ9f1m2SRic6ayiwW: 15, PtMumbaiiFFEGbew1rRjzSPyzRbA51Tm3RVZL5suHPxSZYDhCEc: 16, PtMumbai2TmsJHNGRkD8v8YDbtao7BLUC3wjASn1inAKLFCjaH1: 16, PtNairobiyssHuh87hEhfVBGCVrK3WnS8Z2FT4ymB5tAa4r1nQf: 17, ProxfordYmVfjWnRcgjWH36fW6PArwqykTFzotUxRs6gmTcZDuH: 19, PtParisBxoLz5gzMmn3d9WBQNoPSZakgnkMC2VNuQ3KXfUtUQeZ: 20, PsParisCZo7KAh1Z1smVd9ZMZ1HHn5gkzbM94V3PLCpknFWhUAi: 20, PsQuebecnLByd3JwTiGadoG4nGWi3HYiLXUjkibeFV8dCFeVMUg: 21, PsRiotumaAMotcRoDWW1bysEhQy2n1M5fy8JgRp8jjRfHGmfeA7: 22, PtSeouLouXkxhg39oWzjxDWaCydNfR3RxCUrNe4Q9Ro8BTehcbh: 23, PtTALLiNtPec7mE7yY4m3k26J8Qukef3E3ehzhfXgFZKGtDdAXu: 24, PsUshuai9QapM5TGj1JpuVGkdxz5GykdnEvS6Rh8SUVrARvZLCY: 25, ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK: 26, }; function ProtoGreaterOrEqual(a, b) { return protoLevel[a] >= protoLevel[b]; } function ProtoInferiorTo(a, b) { return protoLevel[a] < protoLevel[b]; } /** * @category Error * Error that indicates macros failed to be expanded */ class MacroError extends core.TaquitoError { constructor(prim, message) { super(); this.prim = prim; this.message = message; this.name = 'MacroError'; } } function assertArgs$1(ex, n) { if ((n === 0 && ex.args === undefined) || ex.args?.length === n) { return true; } throw new MacroError(ex, `macro ${ex.prim} expects ${n} arguments, was given ${ex.args?.length}`); } function assertNoAnnots(ex) { if (ex.annots === undefined) { return true; } throw new MacroError(ex, `unexpected annotation on macro ${ex.prim}: ${ex.annots}`); } function assertIntArg(ex, arg) { if ('int' in arg) { return true; } throw new MacroError(ex, `macro ${ex.prim} expects int argument`); } function parsePairUnpairExpr(p, expr, annotations, agg) { let i = 0; let ai = 0; const ann = [null, null]; // Left expression let lexpr; if (i === expr.length) { throw new MacroError(p, `unexpected end: ${p.prim}`); } let c = expr[i++]; switch (c) { case 'P': { const { r, n, an } = parsePairUnpairExpr(p, expr.slice(i), annotations.slice(ai), agg); lexpr = r; i += n; ai += an; break; } case 'A': if (ai !== annotations.length) { ann[0] = annotations[ai++]; } break; default: throw new MacroError(p, `${p.prim}: unexpected character: ${c}`); } // Right expression let rexpr; if (i === expr.length) { throw new MacroError(p, `unexpected end: ${p.prim}`); } c = expr[i++]; switch (c) { case 'P': { const { r, n, an } = parsePairUnpairExpr(p, expr.slice(i), annotations.slice(ai), agg); rexpr = r.map(([v, a]) => [v + 1, a]); i += n; ai += an; break; } case 'I': if (ai !== annotations.length) { ann[1] = annotations[ai++]; } break; default: throw new MacroError(p, `${p.prim}: unexpected character: ${c}`); } return { r: agg(lexpr, rexpr, [0, ann]), n: i, an: ai }; } function parseSetMapCadr(p, expr, vann, term) { const c = expr[0]; switch (c) { case 'A': return expr.length > 1 ? [ { prim: 'DUP' }, { prim: 'DIP', args: [ [{ prim: 'CAR', annots: ['@%%'] }, parseSetMapCadr(p, expr.slice(1), [], term)], ], }, { prim: 'CDR', annots: ['@%%'] }, { prim: 'SWAP' }, { prim: 'PAIR', annots: ['%@', '%@', ...vann] }, ] : term.a; case 'D': return expr.length > 1 ? [ { prim: 'DUP' }, { prim: 'DIP', args: [ [{ prim: 'CDR', annots: ['@%%'] }, parseSetMapCadr(p, expr.slice(1), [], term)], ], }, { prim: 'CAR', annots: ['@%%'] }, { prim: 'PAIR', annots: ['%@', '%@', ...vann] }, ] : term.d; default: throw new MacroError(p, `${p.prim}: unexpected character: ${c}`); } } function trimLast(a, v) { let l = a.length; while (l > 0 && a[l - 1] === v) { l--; } return a.slice(0, l); } function filterAnnotations(a) { const fields = []; const rest = []; if (a !== undefined) { for (const v of a) { (v.length !== 0 && v[0] === '%' ? fields : rest).push(v); } } return { fields, rest }; } function mkPrim({ prim, annots, args }) { return { prim, ...(annots && { annots }), ...(args && { args }), }; } const pairRe = /^P[PAI]{3,}R$/; const unpairRe = /^UNP[PAI]{2,}R$/; const cadrRe = /^C[AD]{2,}R$/; const setCadrRe = /^SET_C[AD]+R$/; const mapCadrRe = /^MAP_C[AD]+R$/; const diipRe = /^DI{2,}P$/; const duupRe = /^DU+P$/; function expandMacros(ex, opt) { const proto = opt?.protocol || DefaultProtocol; function mayRename(annots) { return annots !== undefined ? [{ prim: 'RENAME', annots }] : []; } switch (ex.prim) { // Compare case 'CMPEQ': case 'CMPNEQ': case 'CMPLT': case 'CMPGT': case 'CMPLE': case 'CMPGE': if (assertArgs$1(ex, 0)) { return [{ prim: 'COMPARE' }, mkPrim({ prim: ex.prim.slice(3), annots: ex.annots })]; } break; case 'IFEQ': case 'IFNEQ': case 'IFLT': case 'IFGT': case 'IFLE': case 'IFGE': if (assertArgs$1(ex, 2)) { return [ { prim: ex.prim.slice(2) }, mkPrim({ prim: 'IF', annots: ex.annots, args: ex.args }), ]; } break; case 'IFCMPEQ': case 'IFCMPNEQ': case 'IFCMPLT': case 'IFCMPGT': case 'IFCMPLE': case 'IFCMPGE': if (assertArgs$1(ex, 2)) { return [ { prim: 'COMPARE' }, { prim: ex.prim.slice(5) }, mkPrim({ prim: 'IF', annots: ex.annots, args: ex.args }), ]; } break; // Fail case 'FAIL': if (assertArgs$1(ex, 0) && assertNoAnnots(ex)) { return [{ prim: 'UNIT' }, { prim: 'FAILWITH' }]; } break; // Assertion macros case 'ASSERT': if (assertArgs$1(ex, 0) && assertNoAnnots(ex)) { return [ { prim: 'IF', args: [[], [[{ prim: 'UNIT' }, { prim: 'FAILWITH' }]]], }, ]; } break; case 'ASSERT_EQ': case 'ASSERT_NEQ': case 'ASSERT_LT': case 'ASSERT_GT': case 'ASSERT_LE': case 'ASSERT_GE': if (assertArgs$1(ex, 0) && assertNoAnnots(ex)) { return [ { prim: ex.prim.slice(7) }, { prim: 'IF', args: [[], [[{ prim: 'UNIT' }, { prim: 'FAILWITH' }]]], }, ]; } break; case 'ASSERT_CMPEQ': case 'ASSERT_CMPNEQ': case 'ASSERT_CMPLT': case 'ASSERT_CMPGT': case 'ASSERT_CMPLE': case 'ASSERT_CMPGE': if (assertArgs$1(ex, 0) && assertNoAnnots(ex)) { return [ [{ prim: 'COMPARE' }, { prim: ex.prim.slice(10) }], { prim: 'IF', args: [[], [[{ prim: 'UNIT' }, { prim: 'FAILWITH' }]]], }, ]; } break; case 'ASSERT_NONE': if (assertArgs$1(ex, 0) && assertNoAnnots(ex)) { return [ { prim: 'IF_NONE', args: [[], [[{ prim: 'UNIT' }, { prim: 'FAILWITH' }]]], }, ]; } break; case 'ASSERT_SOME': if (assertArgs$1(ex, 0)) { return [ { prim: 'IF_NONE', args: [[[{ prim: 'UNIT' }, { prim: 'FAILWITH' }]], mayRename(ex.annots)], }, ]; } break; case 'ASSERT_LEFT': if (assertArgs$1(ex, 0)) { return [ { prim: 'IF_LEFT', args: [mayRename(ex.annots), [[{ prim: 'UNIT' }, { prim: 'FAILWITH' }]]], }, ]; } break; case 'ASSERT_RIGHT': if (assertArgs$1(ex, 0)) { return [ { prim: 'IF_LEFT', args: [[[{ prim: 'UNIT' }, { prim: 'FAILWITH' }]], mayRename(ex.annots)], }, ]; } break; // Syntactic conveniences case 'IF_SOME': if (assertArgs$1(ex, 2)) { return [mkPrim({ prim: 'IF_NONE', annots: ex.annots, args: [ex.args[1], ex.args[0]] })]; } break; case 'IF_RIGHT': if (assertArgs$1(ex, 2)) { return [mkPrim({ prim: 'IF_LEFT', annots: ex.annots, args: [ex.args[1], ex.args[0]] })]; } break; // CAR/CDR n case 'CAR': case 'CDR': if (ex.args !== undefined) { if (assertArgs$1(ex, 1) && assertIntArg(ex, ex.args[0])) { const n = parseInt(ex.args[0].int, 10); return mkPrim({ prim: 'GET', args: [{ int: ex.prim === 'CAR' ? String(n * 2 + 1) : String(n * 2) }], annots: ex.annots, }); } } else { return ex; } } // More syntactic conveniences // PAPPAIIR macro if (pairRe.test(ex.prim)) { if (assertArgs$1(ex, 0)) { const { fields, rest } = filterAnnotations(ex.annots); const { r } = parsePairUnpairExpr(ex, ex.prim.slice(1), fields, (l, r, top) => [ ...(l || []), ...(r || []), top, ]); return r.map(([v, a], i) => { const ann = [ ...trimLast(a, null).map((v) => (v === null ? '%' : v)), ...(v === 0 && i === r.length - 1 ? rest : []), ]; const leaf = mkPrim({ prim: 'PAIR', annots: ann.length !== 0 ? ann : undefined }); return v === 0 ? leaf : { prim: 'DIP', args: v === 1 ? [[leaf]] : [{ int: String(v) }, [leaf]], }; }); } } // UNPAPPAIIR macro if (unpairRe.test(ex.prim)) { if (ProtoInferiorTo(proto, exports.Protocol.PtEdo2Zk) && assertArgs$1(ex, 0)) { const { r } = parsePairUnpairExpr(ex, ex.prim.slice(3), ex.annots || [], (l, r, top) => [ top, ...(r || []), ...(l || []), ]); return r.map(([v, a]) => { const leaf = [ { prim: 'DUP' }, mkPrim({ prim: 'CAR', annots: a[0] !== null ? [a[0]] : undefined }), { prim: 'DIP', args: [[mkPrim({ prim: 'CDR', annots: a[1] !== null ? [a[1]] : undefined })]], }, ]; return v === 0 ? leaf : { prim: 'DIP', args: v === 1 ? [[leaf]] : [{ int: String(v) }, [leaf]], }; }); } else { if (ex.prim === 'UNPAIR') { return ex; } if (assertArgs$1(ex, 0)) { // 008_edo: annotations are deprecated const { r } = parsePairUnpairExpr(ex, ex.prim.slice(3), [], (l, r, top) => [ top, ...(r || []), ...(l || []), ]); return r.map(([v]) => { const leaf = mkPrim({ prim: 'UNPAIR', }); return v === 0 ? leaf : { prim: 'DIP', args: v === 1 ? [[leaf]] : [{ int: String(v) }, [leaf]], }; }); } } } // C[AD]+R macro if (cadrRe.test(ex.prim)) { if (assertArgs$1(ex, 0)) { const ch = [...ex.prim.slice(1, ex.prim.length - 1)]; return ch.map((c, i) => { const ann = i === ch.length - 1 ? ex.annots : undefined; switch (c) { case 'A': return mkPrim({ prim: 'CAR', annots: ann }); case 'D': return mkPrim({ prim: 'CDR', annots: ann }); default: throw new MacroError(ex, `unexpected character: ${c}`); } }); } } // SET_C[AD]+R macro if (setCadrRe.test(ex.prim)) { if (assertArgs$1(ex, 0)) { const { fields, rest } = filterAnnotations(ex.annots); if (fields.length > 1) { throw new MacroError(ex, `unexpected annotation on macro ${ex.prim}: ${fields}`); } const term = fields.length !== 0 ? { a: [ { prim: 'DUP' }, { prim: 'CAR', annots: fields }, { prim: 'DROP' }, { prim: 'CDR', annots: ['@%%'] }, { prim: 'SWAP' }, { prim: 'PAIR', annots: [fields[0], '%@'] }, ], d: [ { prim: 'DUP' }, { prim: 'CDR', annots: fields }, { prim: 'DROP' }, { prim: 'CAR', annots: ['@%%'] }, { prim: 'PAIR', annots: ['%@', fields[0]] }, ], } : { a: [ { prim: 'CDR', annots: ['@%%'] }, { prim: 'SWAP' }, { prim: 'PAIR', annots: ['%', '%@'] }, ], d: [ { prim: 'CAR', annots: ['@%%'] }, { prim: 'PAIR', annots: ['%@', '%'] }, ], }; return parseSetMapCadr(ex, ex.prim.slice(5, ex.prim.length - 1), rest, term); } } // MAP_C[AD]+R macro if (mapCadrRe.test(ex.prim)) { if (assertArgs$1(ex, 1)) { const { fields } = filterAnnotations(ex.annots); if (fields.length > 1) { throw new MacroError(ex, `unexpected annotation on macro ${ex.prim}: ${fields}`); } const term = { a: [ { prim: 'DUP' }, { prim: 'CDR', annots: ['@%%'] }, { prim: 'DIP', args: [ [ mkPrim({ prim: 'CAR', annots: fields.length !== 0 ? ['@' + fields[0].slice(1)] : undefined, }), ex.args[0], ], ], }, { prim: 'SWAP' }, { prim: 'PAIR', annots: [fields.length !== 0 ? fields[0] : '%', '%@'] }, ], d: [ { prim: 'DUP' }, mkPrim({ prim: 'CDR', annots: fields.length !== 0 ? ['@' + fields[0].slice(1)] : undefined, }), ex.args[0], { prim: 'SWAP' }, { prim: 'CAR', annots: ['@%%'] }, { prim: 'PAIR', annots: ['%@', fields.length !== 0 ? fields[0] : '%'] }, ], }; return parseSetMapCadr(ex, ex.prim.slice(5, ex.prim.length - 1), [], term); } } // Expand deprecated DI...IP to [DIP n] if (diipRe.test(ex.prim)) { if (assertArgs$1(ex, 1)) { let n = 0; while (ex.prim[1 + n] === 'I') { n++; } return mkPrim({ prim: 'DIP', args: [{ int: String(n) }, ex.args[0]] }); } } // Expand DU...UP and DUP n if (duupRe.test(ex.prim)) { let n = 0; while (ex.prim[1 + n] === 'U') { n++; } if (ProtoInferiorTo(proto, exports.Protocol.PtEdo2Zk)) { if (n === 1) { if (ex.args === undefined) { return ex; // skip } if (assertArgs$1(ex, 1) && assertIntArg(ex, ex.args[0])) { n = parseInt(ex.args[0].int, 10); } } else { assertArgs$1(ex, 0); } if (n === 1) { return [mkPrim({ prim: 'DUP', annots: ex.annots })]; } else if (n === 2) { return [ { prim: 'DIP', args: [[mkPrim({ prim: 'DUP', annots: ex.annots })]], }, { prim: 'SWAP' }, ]; } else { return [ { prim: 'DIP', args: [{ int: String(n - 1) }, [mkPrim({ prim: 'DUP', annots: ex.annots })]], }, { prim: 'DIG', args: [{ int: String(n) }], }, ]; } } else { if (n === 1) { return ex; } if (assertArgs$1(ex, 0)) { return mkPrim({ prim: 'DUP', args: [{ int: String(n) }], annots: ex.annots }); } } } return ex; } function expandGlobalConstants(ex, hashAndValue) { if (ex.args !== undefined && ex.args.length === 1 && 'string' in ex.args[0] && ex.args[0].string in hashAndValue) { return hashAndValue[ex.args[0].string]; } return ex; } /** * @category Error * Error that indicates a failure when parsing Micheline expressions */ class MichelineParseError extends core.TaquitoError { /** * @param token A token caused the error * @param message An error message */ constructor(token, message) { super(); this.token = token; this.message = message; this.name = 'MichelineParseError'; } } /** * @category Error * Error indicates a failure when parsing Micheline JSON */ class JSONParseError extends core.TaquitoError { /** * @param node A node caused the error * @param message An error message */ constructor(node, message) { super(); this.node = node; this.message = message; this.name = 'JSONParseError'; } } const errEOF = new MichelineParseError(null, 'Unexpected EOF'); function isAnnotation(tok) { return tok.t === Literal.Ident && (tok.v[0] === '@' || tok.v[0] === '%' || tok.v[0] === ':'); } const intRe = new RegExp('^-?[0-9]+$'); const bytesRe = new RegExp('^([0-9a-fA-F]{2})*$'); /** * Converts and validates Michelson expressions between JSON-based Michelson and Micheline * * Pretty Print a Michelson Smart Contract: * ``` * const contract = await Tezos.contract.at("KT1Vsw3kh9638gqWoHTjvHCoHLPKvCbMVbCg"); * const p = new Parser(); * * const michelsonCode = p.parseJSON(contract.script.code); * const storage = p.parseJSON(contract.script.storage); * * console.log("Pretty print Michelson smart contract:"); * console.log(emitMicheline(michelsonCode, {indent:" ", newline: "\n",})); * * console.log("Pretty print Storage:"); * console.log(emitMicheline(storage, {indent:" ", newline: "\n",})); * ``` * * Encode a Michelson expression for initial storage of a smart contract * ``` * const src = `(Pair (Pair { Elt 1 * (Pair (Pair "tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx") * 0x0501000000026869) } * 10000000) * (Pair 2 333))`; * * const p = new Parser(); * * const exp = p.parseMichelineExpression(src); * console.log(JSON.stringify(exp)); * ``` */ class Parser { constructor(opt) { this.opt = opt; } expand(ex) { if (this.opt?.expandGlobalConstant !== undefined && ex.prim === 'constant') { const ret = expandGlobalConstants(ex, this.opt.expandGlobalConstant); if (ret !== ex) { ret[sourceReference] = { ...(ex[sourceReference] || { first: 0, last: 0 }), globalConstant: ex, }; } return ret; } if (this.opt?.expandMacros !== undefined ? this.opt?.expandMacros : true) { const ret = expandMacros(ex, this.opt); if (ret !== ex) { ret[sourceReference] = { ...(ex[sourceReference] || { first: 0, last: 0 }), macro: ex }; } return ret; } else { return ex; } } parseListExpr(scanner, start) { const ref = { first: start.first, last: start.last, }; const expectBracket = start.t === '('; let tok; if (expectBracket) { tok = scanner.next(); if (tok.done) { throw errEOF; } ref.last = tok.value.last; } else { tok = { value: start }; } if (tok.value.t !== Literal.Ident) { throw new MichelineParseError(tok.value, `not an identifier: ${tok.value.v}`); } const ret = { prim: tok.value.v, [sourceReference]: ref, }; for (;;) { const tok = scanner.next(); if (tok.done) { if (expectBracket) { throw errEOF; } break; } else if (tok.value.t === ')') { if (!expectBracket) { throw new MichelineParseError(tok.value, 'unexpected closing bracket'); } ref.last = tok.value.last; break; } else if (isAnnotation(tok.value)) { ret.annots = ret.annots || []; ret.annots.push(tok.value.v); ref.last = tok.value.last; } else { ret.args = ret.args || []; const arg = this.parseExpr(scanner, tok.value); ref.last = arg[sourceReference]?.last || ref.last; ret.args.push(arg); } } return this.expand(ret); } parseArgs(scanner, start) { // Identifier with arguments const ref = { first: start.first, last: start.last, }; const p = { prim: start.v, [sourceReference]: ref, }; for (;;) { const t = scanner.next(); if (t.done || t.value.t === '}' || t.value.t === ';') { return [p, t]; } if (isAnnotation(t.value)) { ref.last = t.value.last; p.annots = p.annots || []; p.annots.push(t.value.v); } else { const arg = this.parseExpr(scanner, t.value); ref.last = arg[sourceReference]?.last || ref.last; p.args = p.args || []; p.args.push(arg); } } } parseSequenceExpr(scanner, start) { const ref = { first: start.first, last: start.last, }; const seq = []; seq[sourceReference] = ref; const expectBracket = start.t === '{'; let tok = start.t === '{' ? null : { value: start }; for (;;) { if (tok === null) { tok = scanner.next(); if (!tok.done) { ref.last = tok.value.last; } } if (tok.done) { if (expectBracket) { throw errEOF; } else { return seq; } } if (tok.value.t === '}') { if (!expectBracket) { throw new MichelineParseError(tok.value, 'unexpected closing bracket'); } else { return seq; } } else if (tok.value.t === Literal.Ident) { // Identifier with arguments const [itm, n] = this.parseArgs(scanner, tok.value); ref.last = itm[sourceReference]?.last || ref.last; seq.push(this.expand(itm)); tok = n; } else { // Other const ex = this.parseExpr(scanner, tok.value); ref.last = ex[sourceReference]?.last || ref.last; seq.push(ex); tok = null; } if (tok === null) { tok = scanner.next(); if (!tok.done) { ref.last = tok.value.last; } } if (!tok.done && tok.value.t === ';') { tok = null; } } } parseExpr(scanner, tok) { switch (tok.t) { case Literal.Ident: return this.expand({ prim: tok.v, [sourceReference]: { first: tok.first, last: tok.last }, }); case Literal.Number: return { int: tok.v, [sourceReference]: { first: tok.first, last: tok.last } }; case Literal.String: return { string: JSON.parse(tok.v), [sourceReference]: { first: tok.first, last: tok.last }, }; case Literal.Bytes: return { bytes: tok.v.slice(2), [sourceReference]: { first: tok.first, last: tok.last } }; case '{': return this.parseSequenceExpr(scanner, tok); default: return this.parseListExpr(scanner, tok); } } /** * Parses a Micheline sequence expression, such as smart contract source. Enclosing curly brackets may be omitted. * @param src A Micheline sequence `{parameter ...; storage int; code { DUP ; ...};}` or `parameter ...; storage int; code { DUP ; ...};` */ parseSequence(src) { if (typeof src !== 'string') { throw new TypeError(`string type was expected, got ${typeof src} instead`); } const scanner = scan(src); const tok = scanner.next(); if (tok.done) { return null; } return this.parseSequenceExpr(scanner, tok.value); } /** * Parse a Micheline sequence expression. Enclosing curly brackets may be omitted. * @param src A Michelson list expression such as `(Pair {Elt "0" 0} 0)` or `Pair {Elt "0" 0} 0` * @returns An AST node or null for empty document. */ parseList(src) { if (typeof src !== 'string') { throw new TypeError(`string type was expected, got ${typeof src} instead`); } const scanner = scan(src); const tok = scanner.next(); if (tok.done) { return null; } return this.parseListExpr(scanner, tok.value); } /** * Parse any Michelson expression * @param src A Michelson expression such as `(Pair {Elt "0" 0} 0)` or `{parameter ...; storage int; code { DUP ; ...};}` * @returns An AST node or null for empty document. */ parseMichelineExpression(src) { if (typeof src !== 'string') { throw new TypeError(`string type was expected, got ${typeof src} instead`); } const scanner = scan(src); const tok = scanner.next(); if (tok.done) { return null; } return this.parseExpr(scanner, tok.value); } /** * Parse a Micheline sequence expression, such as smart contract source. Enclosing curly brackets may be omitted. * An alias for `parseSequence` * @param src A Micheline sequence `{parameter ...; storage int; code { DUP ; ...};}` or `parameter ...; storage int; code { DUP ; ...};` */ parseScript(src) { return this.parseSequence(src); } /** * Parse a Micheline sequence expression. Enclosing curly brackets may be omitted. * An alias for `parseList` * @param src A Michelson list expression such as `(Pair {Elt "0" 0} 0)` or `Pair {Elt "0" 0} 0` * @returns An AST node or null for empty document. */ parseData(src) { return this.parseList(src); } /** * Takes a JSON-encoded Michelson, validates it, strips away unneeded properties and optionally expands macros (See {@link ParserOptions}). * @param src An object containing JSON-encoded Michelson, usually returned by `JSON.parse()` */ parseJSON(src) { if (typeof src !== 'object') { throw new TypeError(`object type was expected, got ${typeof src} instead`); } if (Array.isArray(src)) { const ret = []; for (const n of src) { if (n === null || typeof n !== 'object') { throw new JSONParseError(n, `unexpected sequence element: ${n}`); } ret.push(this.parseJSON(n)); } return ret; } else if ('prim' in src) { const p = src; if (typeof p.prim === 'string' && (p.annots === undefined || Array.isArray(p.annots)) && (p.args === undefined || Array.isArray(p.args))) { const ret = { prim: p.prim, }; if (p.annots !== undefined) { for (const a of p.annots) { if (typeof a !== 'string') { throw new JSONParseError(a, `string expected: ${a}`); } } ret.annots = p.annots; } if (p.args !== undefined) { ret.args = []; for (const a of p.args) { if (a === null || typeof a !== 'object') { throw new JSONParseError(a, `unexpected argument: ${a}`); } ret.args.push(this.parseJSON(a)); } } return this.expand(ret); } throw new JSONParseError(src, `malformed prim expression: ${src}`); } else if ('string' in src) { if (typeof src.string === 'string') { return { string: src.string }; } throw new JSONParseError(src, `malformed string literal: ${src}`); } else if ('int' in src) { if (typeof src.int === 'string' && intRe.test(src.int)) { return { int: src.int }; } throw new JSONParseError(src, `malformed int literal: ${src}`); } else if ('bytes' in src) { if (typeof src.bytes === 'string' && bytesRe.test(src.bytes)) { return { bytes: src.bytes }; } throw new JSONParseError(src, `malformed bytes literal: ${src}`); } else { throw new JSONParseError(src, `unexpected object: ${src}`); } } } class Formatter { constructor(opt, lev = 0) { this.opt = opt; this.lev = lev; } indent(n = 0) { let ret = ''; if (this.opt?.indent !== undefined) { for (let i = this.lev + n; i > 0; i--) { ret += this.opt.indent; } } return ret; } get lf() { return this.opt?.newline || ''; } get lfsp() { return this.opt?.newline || ' '; } down(n) { return new Formatter(this.opt, this.lev + n); } } function hasArgs(node) { return ('prim' in node && ((node.annots !== undefined && node.annots.length !== 0) || (node.args !== undefined && node.args.length !== 0))); } function isMultiline(node) { if (node.args !== undefined) { for (const a of node.args) { if (Array.isArray(a) || hasArgs(a)) { return true; } } } return false; } function emitExpr(node, f, foldMacros) { const macro = node[sourceReference]?.macro; if (foldMacros && macro) { return emitExpr(macro, f, foldMacros); } if (Array.isArray(node)) { return emitSeq(node, f, foldMacros); } else if ('string' in node) { return JSON.stringify(node.string); } else if ('int' in node) { return node.int; } else if ('bytes' in node) { return '0x' + node.bytes; } else { if ((node.annots === undefined || node.annots.length === 0) && (node.args === undefined || node.args.length === 0)) { return node.prim; } let ret = '(' + node.prim; if (node.annots !== undefined) { for (const a of node.annots) { ret += ' ' + a; } } if (node.args !== undefined) { const multiline = isMultiline(node); for (const a of node.args) { if (multiline) { ret += f.lfsp + f.indent(1) + emitExpr(a, f.down(1), foldMacros); } else { ret += ' ' + emitExpr(a, f, foldMacros); } } } return ret + ')'; } } function emitSeq(node, f, foldMacros) { let ret = '{' + f.lf; let i = node.length; for (const el of node) { ret += f.indent(1); if ('prim' in el) { ret += el.prim; if (el.annots !== undefined) { for (const a of el.annots) { ret += ' ' + a; } } if (el.args !== undefined) { const multiline = isMultiline(el); for (const a of el.args) { if (multiline) { ret += f.lfsp + f.indent(2) + emitExpr(a, f.down(2), foldMacros); } else { ret += ' ' + emitExpr(a, f, foldMacros); } } } } else { ret += emitExpr(el, f.down(1), foldMacros); } ret += i > 1 ? ';' + f.lfsp : f.lf; i--; } return ret + f.indent() + '}'; } /** * Formats Micheline expression * @param expr An AST node * @param opt Options */ function emitMicheline(expr, opt, foldMacros = false) { if (typeof expr !== 'object') { throw new TypeError(`object type was expected, got ${typeof expr} instead`); } return emitExpr(expr, new Formatter(opt), foldMacros); } const H = [ 0x6a09e667 | 0, 0xbb67ae85 | 0, 0x3c6ef372 | 0, 0xa54ff53a | 0, 0x510e527f | 0, 0x9b05688c | 0, 0x1f83d9ab | 0, 0x5be0cd19 | 0, ]; const K = [ 0x428a2f98 | 0, 0x71374491 | 0, 0xb5c0fbcf | 0, 0xe9b5dba5 | 0, 0x3956c25b | 0, 0x59f111f1 | 0, 0x923f82a4 | 0, 0xab1c5ed5 | 0, 0xd807aa98 | 0, 0x12835b01 | 0, 0x243185be | 0, 0x550c7dc3 | 0, 0x72be5d74 | 0, 0x80deb1fe | 0, 0x9bdc06a7 | 0, 0xc19bf174 | 0, 0xe49b69c1 | 0, 0xefbe4786 | 0, 0x0fc19dc6 | 0, 0x240ca1cc | 0, 0x2de92c6f | 0, 0x4a7484aa | 0, 0x5cb0a9dc | 0, 0x76f988da | 0, 0x983e5152 | 0, 0xa831c66d | 0, 0xb00327c8 | 0, 0xbf597fc7 | 0, 0xc6e00bf3 | 0, 0xd5a79147 | 0, 0x06ca6351 | 0, 0x14292967 | 0, 0x27b70a85 | 0, 0x2e1b2138 | 0, 0x4d2c6dfc | 0, 0x53380d13 | 0, 0x650a7354 | 0, 0x766a0abb | 0, 0x81c2c92e | 0, 0x92722c85 | 0, 0xa2bfe8a1 | 0, 0xa81a664b | 0, 0xc24b8b70 | 0, 0xc76c51a3 | 0, 0xd192e819 | 0, 0xd6990624 | 0, 0xf40e3585 | 0, 0x106aa070 | 0, 0x19a4c116 | 0, 0x1e376c08 | 0, 0x2748774c | 0, 0x34b0bcb5 | 0, 0x391c0cb3 | 0, 0x4ed8aa4a | 0, 0x5b9cca4f | 0, 0x682e6ff3 | 0, 0x748f82ee | 0, 0x78a5636f | 0, 0x84c87814 | 0, 0x8cc70208 | 0, 0x90befffa | 0, 0xa4506ceb | 0, 0xbef9a3f7 | 0, 0xc67178f2 | 0, ]; /** * @category Error * Error that indicates a failure when decoding a base58 encoding */ class Base58DecodingError extends core.TaquitoError { constructor(message) { super();