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

185 lines (161 loc) 5.1 kB
import { makeTypeError } from "@helios-lang/compiler-utils" import { $ } from "@helios-lang/ir" import { expectDefined, isDefined } from "@helios-lang/type-utils" import { ToIRContext, PARAM_IR_MACRO } from "../codegen/index.js" import { Expr, LiteralDataExpr } from "../expressions/index.js" import { Scope, TopScope } from "../scopes/index.js" import { DataEntity, NamedEntity } from "../typecheck/index.js" import { Statement } from "./Statement.js" /** * @import { Site, Word } from "@helios-lang/compiler-utils" * @import { SourceMappedStringI } from "@helios-lang/ir" * @import { UplcData } from "@helios-lang/uplc" * @import { Definitions, TypeCheckContext } from "../index.js" * @typedef {import("../typecheck/index.js").DataType} DataType * @typedef {import("../typecheck/index.js").EvalEntity} EvalEntity */ /** * Const value statement */ export class ConstStatement extends Statement { /** * @private * @readonly * @type {Expr | undefined} */ _typeExpr /** * @private * @type {Expr | undefined} */ _valueExpr /** * @param {Site} site * @param {Word} name * @param {Expr | undefined} typeExpr - can be null in case of type inference * @param {Expr | undefined} valueExpr */ constructor(site, name, typeExpr, valueExpr) { super(site, name) this._typeExpr = typeExpr this._valueExpr = valueExpr } /** * @type {"ConstStatement"} */ get kind() { return "ConstStatement" } /** * @type {DataType} */ get type() { return expectDefined( this._typeExpr?.cache?.asDataType ?? this._valueExpr?.cache?.asTyped?.type?.asDataType, this._typeExpr?.cache?.toString() ?? this._typeExpr?.toString() ?? this._valueExpr?.toString() ?? "Any" ) } /** * @returns {boolean} */ isSet() { return isDefined(this._valueExpr) } /** * Use this to change a value of something that is already typechecked. * @param {UplcData} data */ changeValueSafe(data) { const type = this.type const site = this._valueExpr ? this._valueExpr.site : this.site this._valueExpr = new LiteralDataExpr(site, type, data) } /** * @returns {string} */ toString() { return `const ${this.name.toString()}${this._typeExpr ? `: ${this._typeExpr.toString()}` : ""}${this._valueExpr ? ` = ${this._valueExpr.toString()}` : ""};` } /** * @param {TypeCheckContext} ctx * @param {Scope} scope * @returns {DataType} */ evalType(ctx, scope) { if (this._typeExpr) { return this._typeExpr.evalAsDataType(ctx, scope) } else if (this._valueExpr) { return this._valueExpr.evalAsDataType(ctx, scope) } else { throw new Error("unexpected") } } /** * @param {TypeCheckContext} ctx * @param {Scope} scope * @returns {EvalEntity} */ evalInternal(ctx, scope) { let type = this._typeExpr?.evalAsDataType(ctx, scope) if (this._valueExpr) { const value = this._valueExpr.evalAsTyped(ctx, scope) if (type) { if (!type.isBaseOf(value.type)) { ctx.errors.type(this._valueExpr.site, "wrong type") } } else { type = value.type.asDataType ?? undefined } } const data = new DataEntity(expectDefined(type)) const res = new NamedEntity(this.name.value, this.path, data) return res } /** * Evaluates rhs and adds to scope * @param {TypeCheckContext} ctx * @param {TopScope} scope */ eval(ctx, scope) { const res = this.evalInternal(ctx, scope) scope.set(this.name, res) } /** * @param {ToIRContext} ctx * @returns {SourceMappedStringI} */ toIRInternal(ctx) { let ir = expectDefined(this._valueExpr).toIR(ctx) if (this._valueExpr instanceof LiteralDataExpr) { ir = $`${this._valueExpr.type.path}__from_data${$("(", this.site)}${ir})` } // if this._valueExpr is None, and paramsSubstitutable is true -> param macro with single argument if (ctx.paramsSubsitutable) { return $`${PARAM_IR_MACRO}("${this.path}", ${ir})` } else { return ir } } /** * @param {ToIRContext} ctx * @param {Definitions} map */ toIR(ctx, map) { if (this._valueExpr) { const description = ctx.aliasNamespace ? `${ctx.aliasNamespace}::${this.name.value}` : this.name.value const keySite = this.name.site.withDescription(description) map.set(this.path, { content: this.toIRInternal( ctx.appendAliasNamespace(this.name.value) ), keySite }) } } }