@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
145 lines (123 loc) • 4.12 kB
JavaScript
import { makeTypeError } from "@helios-lang/compiler-utils"
import { $ } from "@helios-lang/ir"
import { expectDefined } from "@helios-lang/type-utils"
import { TAB, ToIRContext } from "../codegen/index.js"
import { GlobalScope } from "../scopes/index.js"
import { isDataType, VoidType } from "../typecheck/index.js"
import { EntryPointImpl } from "./EntryPoint.js"
import { ModuleCollection } from "./ModuleCollection.js"
/**
* @import { SourceMappedStringI } from "@helios-lang/ir"
* @import { Definitions, TypeCheckContext } from "../index.js"
* @typedef {import("../typecheck/index.js").DataType} DataType
* @typedef {import("../typecheck/index.js").ScriptTypes} ScriptTypes
* @typedef {import("../typecheck/index.js").Type} Type
* @typedef {import("./EntryPoint.js").EntryPoint} EntryPoint
*/
/**
* @implements {EntryPoint}
*/
export class GenericEntryPoint extends EntryPointImpl {
/**
* @readonly
* @type {string}
*/
purpose
/**
* @param {string} purpose
* @param {ModuleCollection} modules
*/
constructor(purpose, modules) {
super(modules)
this.purpose = purpose
}
/**
* @type {Set<string>}
*/
get requiredParams() {
const ctx = new ToIRContext({ optimize: false, isTestnet: false })
const ir = this.toIRInternal(ctx)
return this.getRequiredParametersInternal(ctx, ir)
}
/**
* @param {TypeCheckContext} ctx
* @param {ScriptTypes} scriptTypes
*/
evalTypes(ctx, scriptTypes) {
const scope = GlobalScope.new(
{ scriptTypes, currentScript: this.name },
ctx.errors
)
super.evalTypesInternal(ctx, scope)
// check the 'main' function
const main = this.mainFunc
const argTypeNames = main.argTypeNames
const argTypes = main.argTypes
const retType = main.retType
argTypeNames.forEach((argTypeName, i) => {
if (argTypeName != "" && !isDataType(argTypes[i])) {
ctx.errors.type(
main.site,
`illegal argument type in main: '${argTypes[i].toString()}${!isDataType(argTypes[i]) ? " (not a data type)" : ""}`
)
}
})
if (!isDataType(retType) && !new VoidType().isBaseOf(retType)) {
ctx.errors.type(
main.site,
`illegal return type for main: '${retType.toString()}'`
)
}
}
/**
* @param {ToIRContext} ctx
* @param {Definitions | undefined} extra
* @returns {SourceMappedStringI}
*/
toIR(ctx, extra = undefined) {
const ir = this.toIRInternal(ctx)
return this.wrapEntryPoint(ctx, ir, extra)
}
/**
* @returns {string}
*/
toString() {
return `${this.purpose} ${this.name}\n${super.toString()}`
}
/**
* @protected
* @param {ToIRContext} ctx
* @returns {SourceMappedStringI}
*/
toIRInternal(ctx) {
const argTypeNames = this.mainFunc.argTypeNames
const innerArgs = this.mainArgTypes.map((t, i) => {
// empty path
if (argTypeNames[i] != "") {
return $([
$(`${t.path}__from_data`),
$("("),
$(`arg${i}`),
$(")")
])
} else {
// unused arg, 0 is easier to optimize
return $("0")
}
})
let ir = $([$(`${this.mainPath}(`), $(innerArgs).join(", "), $(")")])
if (!new VoidType().isBaseOf(this.mainFunc.retType)) {
const retType = expectDefined(this.mainFunc.retType.asDataType)
ir = $([$(`${retType.path}____to_data`), $("("), ir, $(")")])
}
const outerArgs = this.mainFunc.argTypes.map((_, i) => $(`arg${i}`))
ir = $([
$(`${TAB}/*entry point*/\n${TAB}(`),
$(outerArgs).join(", "),
$(`) -> {\n${TAB}${TAB}`),
ir,
$(`\n${TAB}}`)
])
return ir
}
}