@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
239 lines (209 loc) • 5.63 kB
JavaScript
import { ToIRContext } from "../codegen/index.js"
import { FuncArg, FuncLiteralExpr } from "../expressions/index.js"
import { Scope } from "../scopes/index.js"
import { FuncType, NamedEntity, ParametricFunc } from "../typecheck/index.js"
import { Statement } from "./Statement.js"
import { TypeParameters } from "./TypeParameters.js"
/**
* @import { Site, Word } from "@helios-lang/compiler-utils"
* @import { SourceMappedStringI } from "@helios-lang/ir"
* @import { Definitions, TypeCheckContext } from "../index.js"
* @typedef {import("../typecheck/index.js").EvalEntity} EvalEntity
* @typedef {import("../typecheck/index.js").Type} Type
*/
/**
* Function statement
* (basically just a named FuncLiteralExpr)
* @internal
*/
export class FuncStatement extends Statement {
/**
* @private
* @readonly
* @type {TypeParameters}
*/
_parameters
/**
* @private
* @readonly
* @type {FuncLiteralExpr}
*/
_funcExpr
/**
* @param {Site} site
* @param {Word} name
* @param {TypeParameters} parameters
* @param {FuncLiteralExpr} funcExpr
*/
constructor(site, name, parameters, funcExpr) {
super(site, name)
this._parameters = parameters
this._funcExpr = funcExpr
}
/**
* @type {TypeParameters}
*/
get typeParameters() {
return this._parameters
}
/**
* @type {string}
*/
get path() {
return this._parameters.genFuncPath(super.path)
}
/**
* @type {number}
*/
get nArgs() {
return this._funcExpr.nArgs
}
/**
* @type {FuncArg[]}
*/
get args() {
return this._funcExpr.args
}
/**
* @type {string[]}
*/
get argNames() {
return this._funcExpr.argNames
}
/**
* @type {Type[]}
*/
get argTypes() {
return this._funcExpr.argTypes
}
/**
* @type {string[]}
*/
get argTypeNames() {
return this._funcExpr.argTypeNames
}
/**
* @type {FuncLiteralExpr}
*/
get funcExpr() {
return this._funcExpr
}
/**
* @type {Type}
*/
get retType() {
return this._funcExpr.retType
}
/**
* @type {Site}
*/
get retSite() {
return this._funcExpr.retExpr.site
}
/**
* @returns {string}
*/
toString() {
return `func ${this.name.toString()}${this._parameters.toString()}${this._funcExpr.toString()}`
}
/**
* Evaluates a function and returns a func value
* @param {TypeCheckContext} ctx
* @param {Scope} scope
* @param {boolean} isMember functions that are members of structs or enums aren't added to their own internal scope as they are always accessed through member access
* @returns {EvalEntity}
*/
evalInternal(ctx, scope, isMember = false) {
const typed = this._parameters.evalParametricFunc(
ctx,
scope,
(subScope) => {
const type = this._funcExpr.evalType(ctx, subScope)
if (isMember) {
void this._funcExpr.evalInternal(ctx, subScope)
} else {
const implScope = new Scope(subScope)
// recursive calls expect func value, not func type
implScope.set(
this.name,
new NamedEntity(
this.name.value,
super.path,
type.toTyped()
)
)
void this._funcExpr.evalInternal(ctx, implScope)
}
return type
}
)
return typed
}
/**
* Evaluates type of a funtion.
* Separate from evalInternal so we can use this function recursively inside evalInternal
* @param {TypeCheckContext} ctx
* @param {Scope} scope
* @returns {ParametricFunc | FuncType}
*/
evalType(ctx, scope) {
return this._parameters.evalParametricFuncType(
ctx,
scope,
(subScope) => {
return this._funcExpr.evalType(ctx, subScope)
}
)
}
/**
* @param {TypeCheckContext} ctx
* @param {Scope} scope
*/
eval(ctx, scope) {
const typed = this.evalInternal(ctx, scope)
if (typed) {
if (!!typed.asType) {
throw new Error("unexpected")
}
scope.set(
this.name,
new NamedEntity(this.name.value, super.path, typed)
)
}
}
/**
* Returns IR of function
* @param {ToIRContext} ctx
* @returns {SourceMappedStringI}
*/
toIRInternal(ctx) {
return this._funcExpr.toIR(ctx)
}
/**
* @param {ToIRContext} ctx
* @param {Definitions} map
*/
toIR(ctx, map) {
const alias = ctx.aliasNamespace
? `${ctx.aliasNamespace}::${this.name.value}`
: this.name.value
const keySite = this.name.site.withDescription(alias)
map.set(this.path, {
content: this.toIRInternal(
ctx.appendAliasNamespace(this.name.value)
),
keySite
})
}
/**
* @param {Statement} s
* @returns {boolean}
*/
static isMethod(s) {
if (s instanceof FuncStatement) {
return s._funcExpr.isMethod()
} else {
return false
}
}
}