@harmoniclabs/plu-ts-onchain
Version:
An embedded DSL for Cardano smart contracts creation coupled with a library for Cardano transactions, all in Typescript
240 lines (238 loc) • 11.7 kB
JavaScript
;
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.pstruct = void 0;
var PData_1 = require("../PData/PData.js");
var addUtilityForType_1 = require("../../lib/std/UtilityTerms/addUtilityForType.js");
var utils_1 = require("../../type_system/utils.js");
var types_1 = require("../../type_system/types.js");
var type_system_1 = require("../../type_system/index.js");
var Term_1 = require("../../Term/index.js");
var punsafeConvertType_1 = require("../../lib/punsafeConvertType/index.js");
var obj_utils_1 = require("@harmoniclabs/obj-utils");
var assert_1 = require("../../../utils/assert.js");
var plutus_data_1 = require("@harmoniclabs/plutus-data");
var plutus_machine_1 = require("@harmoniclabs/plutus-machine");
var IRHoisted_1 = require("../../../IR/IRNodes/IRHoisted.js");
var IRConst_1 = require("../../../IR/IRNodes/IRConst.js");
var const_1 = require("../../lib/std/list/const/index.js");
var IRNative_1 = require("../../../IR/IRNodes/IRNative/index.js");
var IRApp_1 = require("../../../IR/IRNodes/IRApp.js");
/**
* intermediate class useful to reconise structs form primitives
*/
var _PStruct = /** @class */ (function (_super) {
__extends(_PStruct, _super);
function _PStruct() {
return _super.call(this) || this;
}
return _PStruct;
}(PData_1.PData));
function isStructInstanceOfDefinition(structInstance, definition) {
var jsStructFieldsNames = Object.keys(structInstance);
var defKeys = Object.keys(definition);
return (jsStructFieldsNames.length === defKeys.length &&
defKeys.every(function (defFieldName) { return jsStructFieldsNames.includes(defFieldName); }) &&
jsStructFieldsNames.every(function (fieldKey) {
return definition[fieldKey] !== undefined &&
// every field's value is a Term
structInstance[fieldKey] instanceof Term_1.Term /*thisCtorDef[fieldKey]*/ &&
(0, type_system_1.typeExtends)(structInstance[fieldKey].type, (0, types_1.asData)(definition[fieldKey]));
}));
}
var RESERVED_STRUCT_KEYS = Object.freeze([
"eq",
"peq",
"extract",
"in",
"raw"
]);
/**
*
* @param {StructDef} def data-type definition of the struct
*
* each property of the object is a possible constructor for the struct;
*
* each constructor is defined by specifiying the fields that constructor expects and relative types
*
* @example
* ```ts
* const Shape = pstruct({
* Circle: {
* radius: int
* },
* Rectangle: {
* fstSide: int,
* sndSide: int
* }
* });
* ```
*
* @param {( self_t: StructT<StructDef,{}> ) => Methods} getMethods (optional) function to implement arbitrary methods on a given struct.
*
* the function takes as first argument the type of this same struct and expects an object with various methods to be implemented on a struct instance
*
* @example
* ```ts
* const Shape = pstruct({
* Circle: {
* radius: int
* },
* Rectangle: {
* fstSide: int,
* sndSide: int
* }
* }, ( self_t ) => {
*
* return {
* largestSide: pfn([ self_t ], int )
* ( self =>
* pmatch( self )
* .onCircle(({ radius }) => radius )
* .onRectangle({ fstSide, sndSide } =>
* pif( int ).$( fstSide.gt( sndSide ) )
* .then( fstSide )
* .else( sndSide )
* )
* )
* };
* });
*
* const isLargeShape = pfn([ Shape.type ], int )
* ( shape => shape.largestSide.gtEq( 100 ) )
* ```
*/
function pstruct(def, getMethods) {
(0, assert_1.assert)((0, type_system_1.isStructDefinition)(def), "cannot construct 'PStruct' type; struct definition is not constant: " +
(0, utils_1.structDefToString)(def));
getMethods = typeof getMethods === "function" ? getMethods : function (_self_t) { return {}; };
var PStructExt = /** @class */ (function (_super) {
__extends(PStructExt, _super);
// private constructors are not a thing at js runtime
// in any case constructing an instance is useless
// private allows the typescript LSP to rise errors (not runtime) whet trying to extend the class
function PStructExt() {
return _super.call(this) || this;
}
PStructExt._isPType = true;
return PStructExt;
}(_PStruct));
var noMethodsType = (0, types_1.struct)(def);
var methods = getMethods(noMethodsType);
var thisStructType = (0, types_1.struct)(def, methods);
(0, obj_utils_1.defineReadOnlyProperty)(PStructExt, "type", thisStructType);
(0, obj_utils_1.defineReadOnlyProperty)(PStructExt, "termType", thisStructType);
(0, obj_utils_1.defineReadOnlyProperty)(PStructExt, "fromData", function (dataTerm /* | PappArg<PData> */) {
// dataTerm = dataTerm instanceof Term ? dataTerm : pappArgToTerm( dataTerm, data ) as Term<PData>;
(0, assert_1.assert)((0, type_system_1.typeExtends)(dataTerm.type, types_1.data), "trying to construct a Struct using static method 'fromData'; but the `Data` argument is not a `Data.Constr`");
// basically only mocking typescript here; still data
return new Term_1.Term(thisStructType, dataTerm.toIR, dataTerm.isConstant);
});
(0, obj_utils_1.defineReadOnlyProperty)(PStructExt, "toData", function (struct) {
(0, assert_1.assert)((0, type_system_1.typeExtends)(struct.type, thisStructType), "trying to conver a struct using the wrong 'toData', perhaps you ment to call the 'toData' method of an other struct?");
return (0, punsafeConvertType_1.punsafeConvertType)(struct, (0, types_1.asData)(struct.type));
});
var constructors = Object.keys(def);
(0, assert_1.assert)(constructors.length >= 1, "struct definition requires at least 1 constructor");
var _loop_1 = function (i) {
var ctorName = constructors[i];
(0, obj_utils_1.defineReadOnlyProperty)(PStructExt.prototype, ctorName, function (jsStruct) {
var e_1, _a;
(0, assert_1.assert)((0, obj_utils_1.isObject)(jsStruct), "cannot build a plu-ts structure if the input is not an object with named fields");
var thisCtorDef = def[ctorName];
// const jsStructFieldsNames = Object.keys( jsStruct );
// order of fields in the 'jsStruct' migth be different than the order of the definiton
// to preserve the order we need to use the keys got form the ctor definition
var ctorDefFieldsNames = Object.keys(thisCtorDef);
try {
for (var ctorDefFieldsNames_1 = (e_1 = void 0, __values(ctorDefFieldsNames)), ctorDefFieldsNames_1_1 = ctorDefFieldsNames_1.next(); !ctorDefFieldsNames_1_1.done; ctorDefFieldsNames_1_1 = ctorDefFieldsNames_1.next()) {
var fieldName = ctorDefFieldsNames_1_1.value;
if (RESERVED_STRUCT_KEYS.includes(fieldName)) {
throw new Error("\"".concat(fieldName, "\" is a reserved struct key; it can't be used as custom struct property."));
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (ctorDefFieldsNames_1_1 && !ctorDefFieldsNames_1_1.done && (_a = ctorDefFieldsNames_1.return)) _a.call(ctorDefFieldsNames_1);
}
finally { if (e_1) throw e_1.error; }
}
if (ctorDefFieldsNames.length === 0) {
return (0, addUtilityForType_1.addUtilityForType)(thisStructType)(new Term_1.Term(thisStructType, function (_dbn) { return new IRHoisted_1.IRHoisted(IRConst_1.IRConst.data(new plutus_data_1.DataConstr(i, []))); }, true));
}
// still we must be sure that the jsStruct has at least all the fields
(0, assert_1.assert)(isStructInstanceOfDefinition(jsStruct, thisCtorDef), "the fields passed do not match the struct definition for constructor: " + ctorName);
var dataReprTerm;
if (ctorDefFieldsNames.every(function (fieldKey) {
return jsStruct[fieldKey].isConstant;
})) {
dataReprTerm = new Term_1.Term(thisStructType, function (cfg, _dbn) {
return IRConst_1.IRConst.data(new plutus_data_1.DataConstr(i, ctorDefFieldsNames.map(function (fieldKey) {
var _term = jsStruct[fieldKey];
// toData_minimal( thisCtorDef[ fieldKey ] )
// ( jsStruct[ fieldKey ] );
var res = (plutus_machine_1.Machine.evalSimple(_term));
if (!(res instanceof plutus_machine_1.CEKConst && (0, plutus_data_1.isData)(res.value))) {
console.log("--------------------------------");
console.log(ctorDefFieldsNames);
console.log(fieldKey, (0, utils_1.termTypeToString)(thisCtorDef[fieldKey]));
console.log(res);
// console.log( showUPLC( _term.toIR( cfg, _dbn ) ) )
throw res;
}
return res.value;
})));
}, true // isConstant
);
}
else {
dataReprTerm = new Term_1.Term(thisStructType, function (cfg, dbn) {
return new IRApp_1.IRApp(new IRApp_1.IRApp(IRNative_1.IRNative.constrData, IRConst_1.IRConst.int(i)), (0, const_1.pList)(types_1.data)(ctorDefFieldsNames.map(function (fieldKey) {
var res = jsStruct[fieldKey];
// toData_minimal( thisCtorDef[ fieldKey ] )( jsStruct[ fieldKey ] );
return res;
})).toIR(cfg, dbn));
});
}
return (0, addUtilityForType_1.addUtilityForType)(thisStructType)(dataReprTerm);
});
(0, obj_utils_1.defineReadOnlyProperty)(PStructExt, ctorName, PStructExt.prototype[ctorName]);
};
// define constructors
for (var i = 0; i < constructors.length; i++) {
_loop_1(i);
}
/*
Type 'typeof PStructExt' is not assignable to type 'PStruct<StructDef>'
Why is this?
*/
return PStructExt;
}
exports.pstruct = pstruct;