UNPKG

clvm_tools

Version:

Javascript implementation of clvm_tools

196 lines (191 loc) 8.59 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.make_do_com = exports.do_com_prog = exports.lower_quote = exports.COMPILE_BINDINGS = exports.compile_symbols = exports.compile_macros = exports.compile_qq = exports.PASS_THROUGH_OPERATORS = exports.CONS_ATOM = exports.APPLY_ATOM = exports.QUOTE_ATOM = void 0; const clvm_1 = require("clvm"); const binutils_1 = require("../../clvm_tools/binutils"); const NodePath_1 = require("../../clvm_tools/NodePath"); const defaults_1 = require("./defaults"); const helpers_1 = require("./helpers"); const mod_1 = require("./mod"); exports.QUOTE_ATOM = clvm_1.KEYWORD_TO_ATOM["q"]; exports.APPLY_ATOM = clvm_1.KEYWORD_TO_ATOM["a"]; exports.CONS_ATOM = clvm_1.KEYWORD_TO_ATOM["c"]; exports.PASS_THROUGH_OPERATORS = new Set(Object.values(clvm_1.KEYWORD_TO_ATOM)); for (const _ of ["com", "opt"]) { exports.PASS_THROUGH_OPERATORS.add(clvm_1.b(_).hex()); } function compile_qq(args, macro_lookup, symbol_table, run_program, level = 1) { /* (qq ATOM) => (q . ATOM) (qq (unquote X)) => X (qq (a . B)) => (c (qq a) (qq B)) */ const com = function com(sexp) { return do_com_prog(sexp, macro_lookup, symbol_table, run_program); }; const sexp = args.first(); if (!sexp.listp() || sexp.nullp()) { // (qq ATOM) => (q . ATOM) return clvm_1.SExp.to(helpers_1.quote(sexp)); } if (sexp.listp() && !sexp.first().listp()) { const op = sexp.first().atom; if (op.equal_to(clvm_1.b("qq"))) { const subexp = compile_qq(sexp.rest(), macro_lookup, symbol_table, run_program, level + 1); return com(clvm_1.SExp.to([clvm_1.h(exports.CONS_ATOM), op, [clvm_1.h(exports.CONS_ATOM), subexp, helpers_1.quote(0)]])); } else if (op.equal_to(clvm_1.b("unquote"))) { if (level === 1) { // (qq (unquote X)) => X return com(sexp.rest().first()); } const subexp = compile_qq(sexp.rest(), macro_lookup, symbol_table, run_program, level - 1); return com(clvm_1.SExp.to([clvm_1.h(exports.CONS_ATOM), op, [clvm_1.h(exports.CONS_ATOM), subexp, helpers_1.quote(0)]])); } } // (qq (a . B)) => (c (qq a) (qq B)) const A = com(clvm_1.SExp.to([clvm_1.b("qq"), sexp.first()])); const B = com(clvm_1.SExp.to([clvm_1.b("qq"), sexp.rest()])); return clvm_1.SExp.to([clvm_1.h(exports.CONS_ATOM), A, B]); } exports.compile_qq = compile_qq; function compile_macros(args, macro_lookup, symbol_table, run_program) { return clvm_1.SExp.to(helpers_1.quote(macro_lookup)); } exports.compile_macros = compile_macros; function compile_symbols(args, macro_lookup, symbol_table, run_program) { return clvm_1.SExp.to(helpers_1.quote(symbol_table)); } exports.compile_symbols = compile_symbols; exports.COMPILE_BINDINGS = { [clvm_1.b("qq").hex()]: compile_qq, [clvm_1.b("macros").hex()]: compile_macros, [clvm_1.b("symbols").hex()]: compile_symbols, [clvm_1.b("lambda").hex()]: mod_1.compile_mod, [clvm_1.b("mod").hex()]: mod_1.compile_mod, }; // # Transform "quote" to "q" everywhere. Note that quote will not be compiled if behind qq. // # Overrides symbol table defns. function lower_quote(prog, macro_lookup = clvm_1.None, symbol_table = clvm_1.None, run_program = clvm_1.None) { if (prog.nullp()) { return prog; } if (prog.listp()) { if (clvm_1.b("quote").equal_to(prog.first().atom)) { // Note: quote should have exactly one arg, so the length of // quoted list should be 2: "(quote arg)" if (!prog.rest().rest().nullp()) { const errMsg = `Compilation error while compiling [${binutils_1.disassemble(prog)}]. quote takes exactly one argument.`; // printError(`SyntaxError: ${errMsg}`); throw new SyntaxError(errMsg); } return clvm_1.SExp.to(helpers_1.quote(lower_quote(prog.rest().first()))); } else { return clvm_1.SExp.to(clvm_1.t(lower_quote(prog.first()), lower_quote(prog.rest()))); } } else { return prog; } } exports.lower_quote = lower_quote; function do_com_prog(prog, macro_lookup, symbol_table, run_program) { /* Turn the given program `prog` into a clvm program using the macros to do transformation. prog is an uncompiled s-expression. Return a new expanded s-expression PROG_EXP that is equivalent by rewriting based upon the operator, where "equivalent" means (a (com (q PROG) (MACROS)) ARGS) == (a (q PROG_EXP) ARGS) for all ARGS. Also, (opt (com (q PROG) (MACROS))) == (opt (com (q PROG_EXP) (MACROS))) */ // lower "quote" to "q" prog = lower_quote(prog, macro_lookup, symbol_table, run_program); // quote atoms if (prog.nullp() || !prog.listp()) { const atom = prog.atom; if (clvm_1.b("@").equal_to(atom)) { return clvm_1.SExp.to(NodePath_1.TOP.as_path()); } for (const pair of symbol_table.as_iter()) { const [symbol, value] = [pair.first(), pair.rest().first()]; if (symbol.equal_to(atom)) { return clvm_1.SExp.to(value); } } return clvm_1.SExp.to(helpers_1.quote(prog)); } const operator = prog.first(); if (operator.listp()) { // (com ((OP) . RIGHT)) => (a (com (q OP)) 1) const inner_exp = helpers_1.evaluate(clvm_1.SExp.to([clvm_1.b("com"), helpers_1.quote(operator), helpers_1.quote(macro_lookup), helpers_1.quote(symbol_table)]), NodePath_1.TOP.as_path()); return clvm_1.SExp.to([inner_exp]); } const atom = operator.atom; for (const macro_pair of macro_lookup.as_iter()) { const macro_name = macro_pair.first().atom; if (atom.equal_to(macro_name)) { const macro_code = macro_pair.rest().first(); const post_prog = helpers_1.brun(macro_code, prog.rest()); return helpers_1.evaluate(clvm_1.SExp.to([clvm_1.b("com"), post_prog, helpers_1.quote(macro_lookup), helpers_1.quote(symbol_table)]), NodePath_1.TOP.as_short_path()); } } if (atom.hex() in exports.COMPILE_BINDINGS) { const f = exports.COMPILE_BINDINGS[atom.hex()]; const post_prog = f(prog.rest(), macro_lookup, symbol_table, run_program); return helpers_1.evaluate(clvm_1.SExp.to(helpers_1.quote(post_prog)), NodePath_1.TOP.as_path()); } if (operator.equal_to(clvm_1.h(exports.QUOTE_ATOM))) { return prog; } const compiled_args = []; for (const _ of prog.rest().as_iter()) { compiled_args.push(do_com_prog(_, macro_lookup, symbol_table, run_program)); } let r = clvm_1.SExp.to([operator].concat(compiled_args)); if (exports.PASS_THROUGH_OPERATORS.has(atom.hex()) || atom.startswith(clvm_1.b("_"))) { return r; } for (const [symbol, value] of symbol_table.as_javascript()) { if (clvm_1.b("*").equal_to(symbol)) { return r; } if (atom.equal_to(symbol)) { const list = []; for (const _ of prog.rest().as_iter()) { list.push(_); } const new_args = helpers_1.evaluate(clvm_1.SExp.to([clvm_1.b("opt"), [clvm_1.b("com"), helpers_1.quote([clvm_1.b("list")].concat(list)), helpers_1.quote(macro_lookup), helpers_1.quote(symbol_table)]]), NodePath_1.TOP.as_path()); r = clvm_1.SExp.to([clvm_1.h(exports.APPLY_ATOM), value, [clvm_1.h(exports.CONS_ATOM), NodePath_1.LEFT.as_path(), new_args]]); return r; } } const errMsg = `can't compile ${binutils_1.disassemble(prog)}, unknown operator`; // printError(`SyntaxError: ${errMsg}`); throw new SyntaxError(errMsg); } exports.do_com_prog = do_com_prog; function make_do_com(run_program) { return function do_com(sexp) { const prog = sexp.first(); let symbol_table = clvm_1.SExp.null(); let macro_lookup; if (!sexp.rest().nullp()) { macro_lookup = sexp.rest().first(); if (!sexp.rest().rest().nullp()) { symbol_table = sexp.rest().rest().first(); } } else { macro_lookup = defaults_1.default_macro_lookup(run_program); } return clvm_1.t(1, do_com_prog(prog, macro_lookup, symbol_table, run_program)); }; } exports.make_do_com = make_do_com;