UNPKG

@helios-lang/compiler

Version:

Helios is a Domain Specific Language that compiles to Plutus-Core (i.e. Cardano on-chain validator scripts). Helios is a non-Haskell alternative to Plutus. With this library you can compile Helios scripts and build Cardano transactions, all you need to bu

274 lines (244 loc) 7.07 kB
import { makeReferenceError, makeWord } from "@helios-lang/compiler-utils" import { AddressType, AnyTypeClass, AssertFunc, AssetClassType, BoolType, ByteArrayType, Cip67Namespace, DatumHashType, DCertType, DurationType, ErrorFunc, IntType, MintingPolicyHashType, MixedArgsType, PrintFunc, PubKeyType, PubKeyHashType, RatioType, RawDataType, RealType, scriptHashType, ScriptContextNamespace, ScriptPurposeType, ScriptsType, SpendingCredentialType, StakingCredentialType, StakingHashType, StakingPurposeType, StakingValidatorHashType, StringType, TimeType, TimeRangeType, TxType, TxIdType, TxInputType, TxOutputType, TxOutputIdType, TxOutputDatumType, ValidatorHashType, ValueType, ValuableTypeClass, NamedNamespace } from "../typecheck/index.js" /** * @import { ErrorCollector, Site, Word } from "@helios-lang/compiler-utils" * @typedef {import("../typecheck/index.js").DataType} DataType * @typedef {import("../typecheck/index.js").EvalEntity} EvalEntity * @typedef {import("../typecheck/index.js").Func} Func * @typedef {import("../typecheck/index.js").MultiValidatorInfo} MultiValidatorInfo * @typedef {import("../typecheck/index.js").Named} Named * @typedef {import("../typecheck/index.js").Namespace} Namespace * @typedef {import("../typecheck/index.js").Parametric} Parametric * @typedef {import("../typecheck/index.js").Type} Type * @typedef {import("../typecheck/index.js").ScriptTypes} ScriptTypes */ /** * Setting information allows making the scope multi-validator aware * @typedef {{ * currentScript?: string * scriptTypes?: ScriptTypes * }} GlobalScopeConfig */ /** * @type {{[name: string]: (info: MultiValidatorInfo) => NamedNamespace}} */ export const builtinNamespaces = { Cip67: Cip67Namespace, ScriptContext: ScriptContextNamespace } /** * @type {{[name: string]: DataType}} */ export const builtinTypes = { Address: AddressType, AssetClass: AssetClassType, Bool: BoolType, ByteArray: ByteArrayType, DCert: DCertType, DatumHash: DatumHashType, Data: RawDataType, Duration: DurationType, Int: IntType, MintingPolicyHash: MintingPolicyHashType, MixedArgs: MixedArgsType, PubKey: PubKeyType, PubKeyHash: PubKeyHashType, Ratio: RatioType, Real: RealType, ScriptHash: scriptHashType, ScriptPurpose: ScriptPurposeType, SpendingCredential: SpendingCredentialType, StakingCredential: StakingCredentialType, StakingHash: StakingHashType, StakingPurpose: StakingPurposeType, StakingValidatorHash: StakingValidatorHashType, String: StringType, Time: TimeType, TimeRange: TimeRangeType, Tx: TxType, TxId: TxIdType, TxInput: TxInputType, TxOutput: TxOutputType, TxOutputDatum: TxOutputDatumType, TxOutputId: TxOutputIdType, ValidatorHash: ValidatorHashType, Value: ValueType } /** * GlobalScope sits above the top-level scope and contains references to all the builtin Values and Types */ export class GlobalScope { /** * @private * @readonly * @type {[Word, EvalEntity][]} */ _values /** * * @param {ErrorCollector | undefined} [errorCollector] */ constructor(errorCollector = undefined) { this._values = [] this.errorCollector = errorCollector } /** * @private * @param {Site} site * @param {string} msg */ addReferenceError(site, msg) { if (this.errorCollector) { this.errorCollector.reference(site, msg) } else { throw makeReferenceError(site, msg) } } /** * Checks if scope contains a name * @param {Word} name * @returns {boolean} */ has(name) { for (let pair of this._values) { if (pair[0].toString() == name.toString()) { return true } } return false } /** * Sets a global name, doesn't check for uniqueness * Called when initializing GlobalScope * @param {string | Word} name * @param {EvalEntity} value */ set(name, value) { /** @type {Word} */ let nameWord = typeof name == "string" ? makeWord({ value: name }) : name this._values.push([nameWord, value]) } /** * Gets a named value from the scope. * Throws an error if not found. * @param {Word} name * @returns {EvalEntity | undefined} */ get(name) { for (let pair of this._values) { if (pair[0].toString() == name.toString()) { return pair[1] } } this.addReferenceError(name.site, `'${name.toString()}' undefined`) return undefined } /** * @param {Word} name * @returns {(Named & Namespace) | undefined} */ getBuiltinNamespace(name) { if (name.value in builtinNamespaces) { const nameEntity = this._values.find( ([n, _entity]) => n.value == name.value ) if (nameEntity) { const entity = nameEntity[1] if (entity.asNamed && entity.asNamespace) { return /** @type {any} */ (entity) } else { return undefined } } } this.addReferenceError(name.site, `namespace ${name.value} not found`) return undefined } /** * @returns {boolean} */ isStrict() { throw new Error("should've been returned be TopScope") } /** * Initialize the GlobalScope with all the builtins * @param {MultiValidatorInfo} info * @param {ErrorCollector | undefined} [errorCollector] * @returns {GlobalScope} */ static new(info, errorCollector = undefined) { let scope = new GlobalScope(errorCollector) // List (aka '[]'), Option, and Map types are accessed through special expressions // fill the global scope with builtin types and namespaces for (let name in builtinNamespaces) { scope.set(name, builtinNamespaces[name](info)) } for (let name in builtinTypes) { scope.set(name, builtinTypes[name]) } scope.set("Any", new AnyTypeClass()) scope.set("Valuable", new ValuableTypeClass()) if (info.scriptTypes && Object.keys(info.scriptTypes).length > 0) { scope.set("Scripts", new ScriptsType(info.scriptTypes)) } // builtin functions scope.set("assert", AssertFunc) scope.set("error", ErrorFunc) scope.set("print", PrintFunc) return scope } /** * @param {(name: string, type: Type) => void} callback */ loopTypes(callback) { for (let [k, v] of this._values) { if (v.asType) { callback(k.value, v.asType) } } } }