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

237 lines (193 loc) 5.79 kB
import { makeReferenceError } from "@helios-lang/compiler-utils" import { ToIRContext, applyTypeParameters, collectAllUsed, injectMutualRecursions, wrapWithDefs } from "../codegen/index.js" import { ModuleScope, TopScope } from "../scopes/index.js" import { ConstStatement, Statement } from "../statements/index.js" import { MainModule } from "./MainModule.js" import { Module } from "./Module.js" /** * @typedef {import("@helios-lang/ir").SourceMappedStringI} SourceMappedStringI * @typedef {import("../codegen/index.js").Definitions} Definitions */ export class ModuleCollection { /** * @readonly * @type {Module[]} */ modules /** * @param {Module[]} modules */ constructor(modules) { if (modules.length == 0) { throw new Error("expected at least 1 module") } this.modules = modules } /** * @type {[Statement, boolean][]} - boolean value marks if statement is import or not */ get allStatements() { /** * @type {[Statement, boolean][]} */ let statements = [] for (let i = 0; i < this.modules.length; i++) { let m = this.modules[i] // MainModule or last Module => isImport == false let isImport = !( m instanceof MainModule || i == this.modules.length - 1 ) statements = statements.concat( m.statements.map((s) => [s, isImport]) ) } return statements } /** * @type {Module} */ get lastModule() { return this.modules[this.modules.length - 1] } /** * @type {MainModule} */ get mainModule() { for (let m of this.modules) { if (m instanceof MainModule) { return m } } throw new Error("MainModule not found") } /** * @type {Module[]} */ get nonMainModules() { /** @type {Module[]} */ let ms = [] for (let m of this.modules) { if (m instanceof MainModule) { break } else { ms.push(m) } } return ms } /** * @private * @param {SourceMappedStringI} ir * @param {Definitions} definitions * @returns {Definitions} */ eliminateUnused(ir, definitions) { const used = collectAllUsed(ir, definitions) // eliminate all definitions that are not in set /** * @type {Definitions} */ const result = new Map() for (let [k, ir] of definitions) { if (used.has(k)) { result.set(k, ir) } } // Loop internal const statemtsn this.loopConstStatements((name, cs) => { const path = cs.path if (used.has(path) && !definitions.has(cs.path)) { throw makeReferenceError( cs.site, `used unset const '${name}' (hint: use program.parameters['${name}'] = ...)` ) } }) return result } /** * @param {TopScope} topScope */ evalTypes(topScope) { for (let i = 0; i < this.modules.length; i++) { const m = this.modules[i] // reuse main ModuleScope for post module const moduleScope = new ModuleScope(topScope) m.evalTypes(moduleScope) if (m instanceof MainModule) { topScope.setStrict(false) } topScope.setScope(m.name, moduleScope) } } /** * Loops over all statements, until endCond == true (includes the matches statement) * Then applies type parameters * @param {ToIRContext} ctx * @param {SourceMappedStringI} ir * @param {(s: Statement, isImport: boolean) => boolean} endCond * @param {Definitions | undefined} extra * @returns {Definitions} */ fetchDefinitions(ctx, ir, endCond, extra = undefined) { let map = this.statementsToIR(ctx, endCond) map = applyTypeParameters(ctx, ir, map) if (extra) { map = new Map( Array.from(extra.entries()).concat(Array.from(map.entries())) ) } return map } /** * @param {(name: string, statement: ConstStatement) => void} callback */ loopConstStatements(callback) { this.modules.forEach((m) => m.loopConstStatements(callback)) } /** * @param {ToIRContext} ctx * @param {(s: Statement, isImport: boolean) => boolean} endCond * @returns {Definitions} */ statementsToIR(ctx, endCond) { /** * @type {Definitions} */ const map = new Map() for (let [statement, isImport] of this.allStatements) { statement.toIR(ctx, map) if (endCond(statement, isImport)) { break } } return map } /** * @returns {string} */ toString() { return this.modules.map((m) => m.toString()).join("\n") } /** * @param {ToIRContext} ctx * @param {SourceMappedStringI} ir * @param {Definitions} definitions * @returns {SourceMappedStringI} */ wrap(ctx, ir, definitions) { ir = injectMutualRecursions(ir, definitions) definitions = this.eliminateUnused(ir, definitions) ir = wrapWithDefs(ir, definitions) // add builtins as late as possible, to make sure we catch as many dependencies as possible const builtins = ctx.fetchRawFunctions(ir, definitions) ir = wrapWithDefs(ir, builtins) return ir } }