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

210 lines (178 loc) 6 kB
import { anyWord, group, symbol, word } from "@helios-lang/compiler-utils" import { AnyTypeExpr, Expr, FuncArgTypeExpr, FuncTypeExpr, IteratorTypeExpr, ListTypeExpr, MapTypeExpr, OptionTypeExpr, ParametricExpr, PathExpr, TupleTypeExpr, TypeRefExpr, VoidTypeExpr } from "../expressions/index.js" import { ParseContext } from "./ParseContext.js" /** * @import { GenericGroup, TokenReader, Word } from "@helios-lang/compiler-utils" */ /** * @param {ParseContext} ctx * @returns {Expr} */ export function parseTypeExpr(ctx) { /** * @type {Expr | undefined} */ let typeExpr = undefined const r = ctx.reader let m while (!r.isEof()) { if (!typeExpr) { if ((m = r.matches(group("[", { length: 0 })))) { const itemTypeExpr = parseTypeExpr(ctx) typeExpr = new ListTypeExpr(m.site, itemTypeExpr) } else if ( (m = r.matches(word("Map"), group("[", { length: 1 }))) ) { const [kw, g] = m const keyTypeExpr = parseTypeExpr( ctx.atSite(g.site).withReader(g.fields[0]) ) const valueTypeExpr = parseTypeExpr(ctx.atSite(kw.site)) typeExpr = new MapTypeExpr(kw.site, keyTypeExpr, valueTypeExpr) } else if ( (m = r.matches(word("Option"), group("[", { length: 1 }))) ) { const [kw, g] = m const someTypeExpr = parseTypeExpr( ctx.atSite(g.site).withReader(g.fields[0]) ) typeExpr = new OptionTypeExpr(kw.site, someTypeExpr) } else if ( (m = r.matches(word("Iterator"), group("[", { minLength: 1 }))) ) { const [kw, g] = m const itemTypeExprs = g.fields.map((f) => parseTypeExpr(ctx.atSite(kw.site).withReader(f)) ) typeExpr = new IteratorTypeExpr(kw.site, itemTypeExprs) } else if ( (m = r.matches( group("[", { minLength: 1 }), group("("), symbol("->") )) ) { const [pg, ag, arrow] = m const paramExprs = pg.fields.map((f) => parseTypeExpr(ctx.atSite(pg.site).withReader(f)) ) const argExprs = parseFuncArgTypes(ctx, ag) const retTypeExpr = parseTypeExpr(ctx.atSite(arrow.site)) typeExpr = new ParametricExpr( pg.site, new FuncTypeExpr(arrow.site, argExprs, retTypeExpr), paramExprs ) r.end() } else if ((m = r.matches(group("("), symbol("->")))) { const [ag, arrow] = m const argExprs = parseFuncArgTypes(ctx, ag) const retTypeExpr = parseTypeExpr(ctx.atSite(arrow.site)) typeExpr = new FuncTypeExpr(arrow.site, argExprs, retTypeExpr) r.end() } else if ((m = r.matches(group("(", { minLength: 2 })))) { const entryExprs = m.fields.map((f) => parseTypeExpr(ctx.atSite(m.site).withReader(f)) ) typeExpr = new TupleTypeExpr(m.site, entryExprs) r.end() } else if ((m = r.matches(group("(", { length: 0 })))) { typeExpr = new VoidTypeExpr(m.site) r.end() } else if ((m = r.matches(anyWord))) { typeExpr = new TypeRefExpr(m) } else { r.endMatch() r.end() } } else { if ((m = r.matches(symbol("::"), anyWord))) { const [dcolon, w] = m typeExpr = new PathExpr(dcolon.site, typeExpr, w) } else if ((m = r.matches(group("[", { minLength: 1 })))) { const paramExprs = m.fields.map((f) => parseTypeExpr(ctx.atSite(m.site).withReader(f)) ) typeExpr = new ParametricExpr(m.site, typeExpr, paramExprs) } else { r.endMatch() r.end() } } } return typeExpr ?? new AnyTypeExpr(ctx.currentSite) } /** * @param {ParseContext} ctx * @param {GenericGroup<TokenReader>} ag * @returns {FuncArgTypeExpr[]} */ function parseFuncArgTypes(ctx, ag) { const argExprs = ag.fields.map((f) => parseFuncArgType(ctx.atSite(ag.site).withReader(f)) ) if ( argExprs.some((a) => a.isNamed()) && argExprs.some((a) => !a.isNamed()) ) { ctx.errors.syntax( ag.site, "can't mix named and unnamed args in a function type" ) } if ( argExprs.some( (a, i) => a.isOptional() && i < argExprs.length - 1 && !argExprs[i + 1].isOptional() ) ) { ctx.errors.syntax(ag.site, "optional arg not at end") } return argExprs } /** * @param {ParseContext} ctx * @returns {FuncArgTypeExpr} */ function parseFuncArgType(ctx) { const r = ctx.reader /** * @type {Word | undefined} */ let name = undefined /** * @type {Expr} */ let typeExpr let optional = false let m if ((m = r.matches(anyWord, symbol(":"), symbol("?")))) { const [n, colon] = m name = n typeExpr = parseTypeExpr(ctx.atSite(colon.site)) optional = true } else if ((m = r.matches(anyWord, symbol(":")))) { const [n, colon] = m name = n typeExpr = parseTypeExpr(ctx.atSite(colon.site)) } else { typeExpr = parseTypeExpr(ctx) } return new FuncArgTypeExpr(ctx.currentSite, name, typeExpr, optional) }