UNPKG

clvm

Version:

Javascript implementation of chia lisp

280 lines (278 loc) 10.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SExp = exports.to_sexp_type = exports.convert_atom_to_bytes = exports.looks_like_clvm_object = void 0; const __python_types__1 = require("./__python_types__"); const CLVMObject_1 = require("./CLVMObject"); const __type_compatibility__1 = require("./__type_compatibility__"); const casts_1 = require("./casts"); const serialize_1 = require("./serialize"); const as_javascript_1 = require("./as_javascript"); const EvalError_1 = require("./EvalError"); function looks_like_clvm_object(o) { if (!o || (typeof o !== "object" && typeof o !== "function")) { return false; } return Boolean("atom" in o && "pair" in o); } exports.looks_like_clvm_object = looks_like_clvm_object; // this function recognizes some common types and turns them into plain bytes function convert_atom_to_bytes(v) { if ((0, __type_compatibility__1.isBytes)(v)) { return v; } else if (typeof v === "string") { return __type_compatibility__1.Bytes.from(v, "utf8"); } else if (typeof v === "number") { return (0, casts_1.int_to_bytes)(v, { signed: true }); } else if (typeof v === "boolean") { // Tips. In Python, isinstance(True, int) == True. return (0, casts_1.int_to_bytes)(v ? 1 : 0, { signed: true }); } else if (typeof v === "bigint") { return (0, casts_1.bigint_to_bytes)(v, { signed: true }); } else if (v === __python_types__1.None || !v) { return __type_compatibility__1.Bytes.NULL; } else if ((0, __type_compatibility__1.isIterable)(v)) { if (v.length > 0) { throw new Error(`can't cast ${JSON.stringify(v)} to bytes`); } return __type_compatibility__1.Bytes.NULL; } else if (typeof v.serialize === "function") { return __type_compatibility__1.Bytes.from(v, "G1Element"); } else if (typeof v.toBytes === "function") { return v.toBytes(); } try { return __type_compatibility__1.Bytes.from(v); } catch (_) { throw new Error(`can't cast ${JSON.stringify(v)} to bytes`); } } exports.convert_atom_to_bytes = convert_atom_to_bytes; const op_convert = 0; const op_set_left = 1; const op_set_right = 2; const op_prepend_list = 3; function to_sexp_type(value) { let v = value; const stack = [v]; const ops = [(0, __type_compatibility__1.t)(0, __python_types__1.None)]; while (ops.length) { const item = ops.pop(); const op = item[0]; let targetIndex = item[1]; // convert value if (op === op_convert) { if (looks_like_clvm_object(stack[stack.length - 1])) { continue; } v = stack.pop(); if ((0, __type_compatibility__1.isTuple)(v)) { if (v.length !== 2) { throw new Error(`can't cast tuple of size ${v.length}`); } const [left, right] = v; targetIndex = stack.length; stack.push(new CLVMObject_1.CLVMObject((0, __type_compatibility__1.t)(left, right))); if (!looks_like_clvm_object(right)) { stack.push(right); ops.push((0, __type_compatibility__1.t)(2, targetIndex)); // set right ops.push((0, __type_compatibility__1.t)(0, __python_types__1.None)); // convert } if (!looks_like_clvm_object(left)) { stack.push(left); ops.push((0, __type_compatibility__1.t)(1, targetIndex)); ops.push((0, __type_compatibility__1.t)(0, __python_types__1.None)); } continue; } else if (Array.isArray(v) /* && !(v instance of Tuple) */) { targetIndex = stack.length; stack.push(new CLVMObject_1.CLVMObject(__type_compatibility__1.Bytes.NULL)); for (const _ of v) { stack.push(_); ops.push((0, __type_compatibility__1.t)(3, targetIndex)); // prepend list // we only need to convert if it's not already the right type if (!looks_like_clvm_object(_)) { ops.push((0, __type_compatibility__1.t)(0, __python_types__1.None)); // convert } } continue; } stack.push(new CLVMObject_1.CLVMObject(convert_atom_to_bytes(v))); continue; } if (targetIndex === __python_types__1.None) { throw new Error("Invalid target. target is None"); } if (op === op_set_left) { // set left stack[targetIndex] = new CLVMObject_1.CLVMObject((0, __type_compatibility__1.t)(new CLVMObject_1.CLVMObject(stack.pop()), stack[targetIndex].pair[1])); } else if (op === op_set_right) { // set right stack[targetIndex] = new CLVMObject_1.CLVMObject((0, __type_compatibility__1.t)(stack[targetIndex].pair[0], new CLVMObject_1.CLVMObject(stack.pop()))); } else if (op === op_prepend_list) { // prepend list stack[targetIndex] = new CLVMObject_1.CLVMObject((0, __type_compatibility__1.t)(stack.pop(), stack[targetIndex])); } } // there's exactly one item left at this point if (stack.length !== 1) { throw new Error("internal error"); } // stack[0] implements the clvm object protocol and can be wrapped by an SExp return stack[0]; } exports.to_sexp_type = to_sexp_type; /* SExp provides higher level API on top of any object implementing the CLVM object protocol. The tree of values is not a tree of SExp objects, it's a tree of CLVMObject like objects. SExp simply wraps them to provide a uniform view of any underlying conforming tree structure. The CLVM object protocol (concept) exposes two attributes: 1. "atom" which is either None or bytes 2. "pair" which is either None or a tuple of exactly two elements. Both elements implementing the CLVM object protocol. Exactly one of "atom" and "pair" must be None. */ class SExp { get atom() { return this._atom instanceof Uint8Array ? new __type_compatibility__1.Bytes(this._atom) : this._atom; } get pair() { return this._pair; } static to(v) { if (v instanceof SExp) { return v; } if (looks_like_clvm_object(v)) { return new SExp(v); } // this will lazily convert elements return new SExp(to_sexp_type(v)); } static null() { return SExp.__NULL__; } constructor(v) { // When instantiating SExp from `LazyNode` of `clvm_wasm`, _atom will be `Optional<Uint8Array>`. // Otherwise, it will be `Optional<Bytes>` this._atom = __python_types__1.None; this._pair = __python_types__1.None; this._atom = v.atom; this._pair = v.pair; } as_pair() { const pair = this.pair; if (pair === __python_types__1.None) { return pair; } return (0, __type_compatibility__1.t)(new SExp(pair[0]), new SExp(pair[1])); } listp() { return this.pair !== __python_types__1.None; } nullp() { return this.atom !== __python_types__1.None && this.atom.length === 0; } as_int() { return (0, casts_1.int_from_bytes)(this.atom, { signed: true }); } as_bigint() { return (0, casts_1.bigint_from_bytes)(this.atom, { signed: true }); } as_bin() { const f = new __type_compatibility__1.Stream(); (0, serialize_1.sexp_to_stream)(this, f); return f.getValue(); } cons(right) { return SExp.to((0, __type_compatibility__1.t)(this, right)); } first() { const pair = this.pair; if (pair) { return new SExp(pair[0]); } throw new EvalError_1.EvalError("first of non-cons", this); } rest() { const pair = this.pair; if (pair) { return new SExp(pair[1]); } throw new EvalError_1.EvalError("rest of non-cons", this); } *as_iter() { let v = this; while (!v.nullp()) { yield v.first(); v = v.rest(); } } equal_to(other /* CastableType */) { try { other = SExp.to(other); const to_compare_stack = [(0, __type_compatibility__1.t)(this, other)]; while (to_compare_stack.length) { const [s1, s2] = to_compare_stack.pop(); const p1 = s1.as_pair(); if (p1) { const p2 = s2.as_pair(); if (p2) { to_compare_stack.push((0, __type_compatibility__1.t)(p1[0], p2[0])); to_compare_stack.push((0, __type_compatibility__1.t)(p1[1], p2[1])); } else { return false; } } else if (s2.as_pair() || !(s1.atom && s2.atom && s1.atom.equal_to(s2.atom))) { return false; } } return true; } catch (e) { return false; } } list_len() { let v = this; let size = 0; while (v.listp()) { size += 1; v = v.rest(); } return size; } as_javascript() { return (0, as_javascript_1.as_javascript)(this); } toString() { return this.as_bin().hex(); } toJSON() { if (this.pair) { return this.pair; } if (this.atom) { return this.atom.hex(); } throw new EvalError_1.EvalError("Invalid object", this); } __repr__() { return `SExp(${this.as_bin().hex()})`; } } exports.SExp = SExp; SExp.TRUE = new SExp(new CLVMObject_1.CLVMObject(__type_compatibility__1.Bytes.from("0x01", "hex"))); SExp.FALSE = new SExp(new CLVMObject_1.CLVMObject(__type_compatibility__1.Bytes.NULL)); SExp.__NULL__ = new SExp(new CLVMObject_1.CLVMObject(__type_compatibility__1.Bytes.NULL));