Skip to content

Commit

Permalink
Supported return types of abstract algorithms (#57)
Browse files Browse the repository at this point in the history
  • Loading branch information
jhnaldo committed Feb 25, 2022
1 parent 1c44bb4 commit 3d5683b
Show file tree
Hide file tree
Showing 16 changed files with 97 additions and 68 deletions.
4 changes: 2 additions & 2 deletions src/main/scala/esmeta/cfg/util/Stringifier.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ class Stringifier(detail: Boolean, location: Boolean) {

// functions
given funcRule: Rule[Func] = (app, func) =>
val IRFunc(main, kind, name, params, _, _) = func.irFunc
val IRFunc(main, kind, name, params, retTy, _, _) = func.irFunc
given Rule[Iterable[IRFunc.Param]] = iterableRule("(", ", ", ")")
app >> func.id >> ": "
app >> (if (main) "@main " else "") >> "def " >> kind
app >> name >> params >> " "
app >> name >> params >> ": " >> retTy >> " "
app.wrap {
given Ordering[Node] = Ordering.by(_.id)
for (node <- func.nodes.toList.sorted) app :> node
Expand Down
1 change: 1 addition & 0 deletions src/main/scala/esmeta/ir/Func.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ case class Func(
kind: Func.Kind,
name: String,
params: List[Func.Param],
retTy: Type,
body: Inst,
algo: Option[Algorithm] = None,
) extends IRElem {
Expand Down
1 change: 1 addition & 0 deletions src/main/scala/esmeta/ir/Type.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ import esmeta.ir.util.Parser
// TODO ir types
case class Type(name: String) extends IRElem
object Type extends Parser.From[Type]
val AnyType = Type("Any")
7 changes: 5 additions & 2 deletions src/main/scala/esmeta/ir/util/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ trait Parsers extends BasicParsers {

// functions
given func: Parser[Func] = {
(main <~ "def") ~ funcKind ~ "[\\w|:\\.]+".r ~ params ~ inst ^^ {
case m ~ k ~ n ~ ps ~ b => Func(m, k, n, ps, b)
(main <~ "def") ~ funcKind ~ "[\\w|:\\.]+".r ~ params ~ retTy ~ inst ^^ {
case m ~ k ~ n ~ ps ~ rty ~ b => Func(m, k, n, ps, rty, b)
}
}.named("ir.Func")

Expand All @@ -47,6 +47,9 @@ trait Parsers extends BasicParsers {
}
}.named("ir.Func.Param")

// return types
lazy val retTy: Parser[Type] = opt(":" ~> ty) ^^ { _.getOrElse(AnyType) }

// instructions
given inst: Parser[Inst] = withLoc {
"{" ~> rep(inst) <~ "}" ^^ {
Expand Down
4 changes: 2 additions & 2 deletions src/main/scala/esmeta/ir/util/Stringifier.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ class Stringifier(detail: Boolean, location: Boolean) {

// functions
given funcRule: Rule[Func] = (app, func) =>
val Func(main, kind, name, params, body, _) = func
val Func(main, kind, name, params, retTy, body, _) = func
given Rule[Iterable[Func.Param]] = iterableRule("(", ", ", ")")
app >> (if (main) "@main " else "") >> "def " >> kind
app >> name >> params >> " " >> body
app >> name >> params >> ": " >> retTy >> " " >> body

// function kinds
given funcKindRule: Rule[Func.Kind] = (app, kind) =>
Expand Down
6 changes: 3 additions & 3 deletions src/main/scala/esmeta/ir/util/UnitWalker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ trait UnitWalker extends BasicUnitWalker {

// functions
def walk(func: Func): Unit =
val Func(main, kind, name, ps, body, _) = func
walk(main); walk(kind); walk(name); walkList(ps, walk)
walk(func.body)
val Func(main, kind, name, ps, rty, body, _) = func
walk(main); walk(kind); walk(name);
walkList(ps, walk); walk(rty); walk(func.body)

// function kinds
def walk(kind: Func.Kind): Unit = {}
Expand Down
3 changes: 2 additions & 1 deletion src/main/scala/esmeta/ir/util/Walker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@ trait Walker extends BasicWalker {

// functions
def walk(func: Func): Func =
val Func(main, kind, name, ps, body, algo) = func
val Func(main, kind, name, ps, rty, body, algo) = func
Func(
walk(main),
walk(kind),
walk(name),
walkList(ps, walk),
walk(rty),
walk(func.body),
algo,
)
Expand Down
5 changes: 4 additions & 1 deletion src/main/scala/esmeta/spec/Algorithm.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package esmeta.spec

import esmeta.lang.*
import esmeta.lang.{Step, YetStep}
import esmeta.lang.util.*
import esmeta.spec.util.*
import org.jsoup.nodes.Element
Expand All @@ -16,6 +16,9 @@ case class Algorithm(
/** check whether it is incomplete */
lazy val complete: Boolean = incompleteSteps.isEmpty

/** return types */
lazy val retTy: Type = head.retTy

/** get all steps */
lazy val steps: List[Step] = StepCollector(body)

Expand Down
12 changes: 9 additions & 3 deletions src/main/scala/esmeta/spec/Head.scala
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
package esmeta.spec

/** algorithm heads */
sealed trait Head extends SpecElem
sealed trait Head extends SpecElem { val retTy: Type }

/** abstract operation (AO) heads */
case class AbstractOperationHead(
isHostDefined: Boolean,
name: String,
params: List[Param],
isHostDefined: Boolean,
retTy: Type,
) extends Head

/** numeric method heads */
case class NumericMethodHead(
ty: Type,
name: String,
params: List[Param],
retTy: Type,
) extends Head

/** syntax-directed operation (SDO) heads */
case class SyntaxDirectedOperationHead(
target: Option[SyntaxDirectedOperationHead.Target],
target: Option[SdoHeadTarget],
methodName: String,
isStatic: Boolean,
withParams: List[Param],
retTy: Type,
) extends Head
object SyntaxDirectedOperationHead:
case class Target(
Expand All @@ -38,19 +41,22 @@ case class ConcreteMethodHead(
methodName: String,
receiverParam: Param,
params: List[Param],
retTy: Type,
) extends Head

/** internal method heads */
case class InternalMethodHead(
methodName: String,
receiverParam: Param,
params: List[Param],
retTy: Type,
) extends Head

/** buil-in heads */
case class BuiltinHead(
ref: BuiltinHead.Ref,
params: List[Param],
retTy: Type,
) extends Head
object BuiltinHead:
enum Ref extends SpecElem:
Expand Down
12 changes: 8 additions & 4 deletions src/main/scala/esmeta/spec/util/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,20 @@ class Compiler(val spec: Spec) {
kind: Func.Kind,
name: String,
params: List[Func.Param],
retTy: IRType,
body: Step,
algo: Algorithm,
) {
// get an IR function as the result of compilation of an algorithm
lazy val result: Func =
val inst = compileWithScope(this, body)
val func = Func(false, kind, name, params, inst, Some(algo))
val func = Func(false, kind, name, params, retTy, inst, Some(algo))
funcs += func
func

// production context
var ntList: List[(String, Expr, Int)] = algo.head match
case SyntaxDirectedOperationHead(Some(target), _, _, _) =>
case SyntaxDirectedOperationHead(Some(target), _, _, _, _) =>
target.rhsParams.zipWithIndex.map {
case (param, idx) => (param.name, ENAME_THIS, idx)
}
Expand Down Expand Up @@ -194,6 +195,7 @@ class Compiler(val spec: Spec) {
getKind(algo.head),
getName(algo.head),
getParams(algo.head),
compile(algo.retTy),
algo.body,
algo,
).result
Expand Down Expand Up @@ -456,7 +458,8 @@ class Compiler(val spec: Spec) {
val ps =
params.map(x => Func.Param(compile(x), false, IRType("any")))
val cloName = fb.nextClosureName
val newFb = FuncBuilder(Func.Kind.Clo, cloName, ps, body, fb.algo)
val newFb =
FuncBuilder(Func.Kind.Clo, cloName, ps, AnyType, body, fb.algo)
newFb.result
EClo(cloName, captured.map(compile))
case lit: Literal => compile(fb, lit)
Expand Down Expand Up @@ -591,7 +594,8 @@ class Compiler(val spec: Spec) {
// compile types
// TODO handle type properly
private def compile(ty: Type): IRType =
IRType(ty.name.replace(" ", "").replace("|", ""))
if (ty == UnknownType) AnyType
else IRType(ty.name.replace(" ", "").replace("|", ""))

// handle short circuiting
private def compileShortCircuit(
Expand Down
38 changes: 19 additions & 19 deletions src/main/scala/esmeta/spec/util/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ object Parser extends Parsers {
elem: Element,
): List[Head] = elem.getPrevText match
case thisValuePattern(name, param) =>
List(AbstractOperationHead(name, List(Param(param)), false))
List(AbstractOperationHead(false, name, List(Param(param)), UnknownType))
case _ => parseBuiltinHead(parent, elem)
}

Expand Down Expand Up @@ -381,6 +381,8 @@ trait Parsers extends BasicParsers {
// ...
given ty: Parser[Type] = "([^_,:]|, )+".r ^^ { Type(_) }

lazy val retTy: Parser[Type] = opt(":" ~> ty) ^^ { _.getOrElse(UnknownType) }

lazy val ref: Parser[BuiltinHead.Ref] =
import BuiltinHead.Ref
import BuiltinHead.Ref.*
Expand All @@ -404,16 +406,14 @@ trait Parsers extends BasicParsers {

// abstract opration (AO) heads
lazy val absOpHeadGen: Parser[Boolean => AbstractOperationHead] =
opt(semanticsKind) ~> name ~ params ^^ {
case name ~ params =>
(isHostDefined: Boolean) =>
AbstractOperationHead(name, params, isHostDefined)
opt(semanticsKind) ~> name ~ params ~ retTy ^^ {
case name ~ params ~ rty => AbstractOperationHead(_, name, params, rty)
}

// numeric method heads
lazy val numMethodHead: Parser[NumericMethodHead] =
(ty <~ "::") ~ name ~ params ^^ {
case t ~ x ~ ps => NumericMethodHead(t, x, ps)
(ty <~ "::") ~ name ~ params ~ retTy ^^ {
case t ~ x ~ ps ~ rty => NumericMethodHead(t, x, ps, rty)
}

// algorithm parameters
Expand Down Expand Up @@ -454,28 +454,28 @@ trait Parsers extends BasicParsers {
lazy val sdoHeadGen: Parser[
Option[SyntaxDirectedOperationHead.Target] => SyntaxDirectedOperationHead,
] =
semanticsKind ~ name ~ params ^^ {
case isStatic ~ x ~ params =>
targetOpt => SyntaxDirectedOperationHead(targetOpt, x, isStatic, params)
semanticsKind ~ name ~ params ~ retTy ^^ {
case isStatic ~ x ~ params ~ rty =>
SyntaxDirectedOperationHead(_, x, isStatic, params, rty)
}

// concrete method head generator
lazy val concMethodHeadGen: Parser[Param => ConcreteMethodHead] =
name ~ params ^^ {
case name ~ params =>
(receiverParam: Param) =>
ConcreteMethodHead(name, receiverParam, params)
name ~ params ~ retTy ^^ {
case name ~ params ~ rty =>
ConcreteMethodHead(name, _, params, rty)
}

// internal method head generator
lazy val inMethodHeadGen: Parser[Param => InternalMethodHead] =
("[[" ~> name <~ "]]") ~ params ^^ {
case name ~ params =>
(receiverParam: Param) =>
InternalMethodHead(name, receiverParam, params)
("[[" ~> name <~ "]]") ~ params ~ retTy ^^ {
case name ~ params ~ rty =>
InternalMethodHead(name, _, params, rty)
}

// built-in heads
lazy val builtinHead: Parser[BuiltinHead] =
ref ~ params ^^ { case name ~ params => BuiltinHead(name, params) }
ref ~ params ~ retTy ^^ {
case name ~ params ~ rty => BuiltinHead(name, params, rty)
}
}
24 changes: 13 additions & 11 deletions src/main/scala/esmeta/spec/util/Stringifier.scala
Original file line number Diff line number Diff line change
Expand Up @@ -172,28 +172,30 @@ object Stringifier {
given headRule: Rule[Head] = (app, head) =>
given Rule[List[Param]] = iterableRule("(", ", ", ")")
head match {
case AbstractOperationHead(name, params, isHostDefined) =>
app >> name >> params
case NumericMethodHead(ty, name, params) =>
app >> ty >> "::" >> name >> params
case AbstractOperationHead(isHostDefined, name, params, rty) =>
app >> name >> params >> ": " >> rty
case NumericMethodHead(ty, name, params, rty) =>
app >> ty >> "::" >> name >> params >> ": " >> rty
case SyntaxDirectedOperationHead(
target,
methodName,
isStatic,
withParams,
rty,
) =>
given Rule[Option[SdoHeadTarget]] = optionRule("<DEFAULT>")
app >> "[SYNTAX] " >> target >> "." >> methodName
if (isStatic) app >> "[" >> "S" >> "]"
else app >> "[" >> "R" >> "]"
app >> withParams
case ConcreteMethodHead(methodName, receiverParam, params) =>
app >> "[METHOD] " >> methodName >> "(" >> receiverParam.name >> ")" >> params
case InternalMethodHead(methodName, receiverParam, params) =>
app >> withParams >> ": " >> rty
case ConcreteMethodHead(methodName, receiverParam, params, rty) =>
app >> "[METHOD] " >> methodName >> "(" >> receiverParam.name >> ")"
app >> params
case BuiltinHead(ref, params) =>
app >> "[BUILTIN] " >> ref >> params
app >> params >> ": " >> rty
case InternalMethodHead(methodName, receiverParam, params, rty) =>
app >> "[METHOD] " >> methodName >> "(" >> receiverParam.name >> ")"
app >> params >> ": " >> rty
case BuiltinHead(ref, params, rty) =>
app >> "[BUILTIN] " >> ref >> params >> ": " >> rty
}

given builtinHeadRefRule: Rule[BuiltinHead.Ref] = (app, ref) =>
Expand Down
12 changes: 6 additions & 6 deletions src/test/scala/esmeta/cfg/StringifyTinyTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class StringifyTinyTest extends CFGTest {
lazy val cfg = CFG(List(mainFunc, func(1)))
// tests
checkStringify("CFG")(
cfg -> """0: @main def f(x: T, y?: T) {
cfg -> """0: @main def f(x: T, y?: T): T {
| 0: let x = ~empty~ -> 1
| 1: if x then 2 else 3
| 2: {
Expand All @@ -29,7 +29,7 @@ class StringifyTinyTest extends CFGTest {
| }
| 3: call %42 = x(x, y)
|}
|1: def f(x: T, y?: T) {
|1: def f(x: T, y?: T): T {
| 4: let x = ~empty~ -> 5
| 5: if x then 6 else 7
| 6: {
Expand Down Expand Up @@ -61,7 +61,7 @@ class StringifyTinyTest extends CFGTest {

// tests
checkStringify("Func")(
mainFunc -> """0: @main def f(x: T, y?: T) {
mainFunc -> """0: @main def f(x: T, y?: T): T {
| 0: let x = ~empty~ -> 1
| 1: if x then 2 else 3
| 2: {
Expand All @@ -71,7 +71,7 @@ class StringifyTinyTest extends CFGTest {
| }
| 3: call %42 = x(x, y)
|}""".stripMargin,
func(0) -> """0: def f(x: T, y?: T) {
func(0) -> """0: def f(x: T, y?: T): T {
| 4: let x = ~empty~ -> 5
| 5: if x then 6 else 7
| 6: {
Expand Down Expand Up @@ -107,8 +107,8 @@ class StringifyTinyTest extends CFGTest {
// -------------------------------------------------------------------------
// IR elements
// -------------------------------------------------------------------------
lazy val irMainFunc = IRFunc(true, IRFunc.Kind.AbsOp, "f", params, seq)
lazy val irFunc = IRFunc(false, IRFunc.Kind.AbsOp, "f", params, seq)
lazy val irMainFunc = IRFunc(true, IRFunc.Kind.AbsOp, "f", params, ty, seq)
lazy val irFunc = IRFunc(false, IRFunc.Kind.AbsOp, "f", params, ty, seq)
lazy val seq = ISeq(List(let, del, ret))
lazy val params = List(xParam, yParam)
lazy val xParam = IRFunc.Param(x, false, ty)
Expand Down
2 changes: 1 addition & 1 deletion src/test/scala/esmeta/interp/StringifyTinyTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ class StringifyTinyTest extends InterpTest {
lazy val namedAddr = NamedAddr("Global")
lazy val addr = DynamicAddr(42)
lazy val func =
Func(0, IRFunc(true, IRFunc.Kind.AbsOp, "f", Nil, INop()), None)
Func(0, IRFunc(true, IRFunc.Kind.AbsOp, "f", Nil, AnyType, INop()), None)
lazy val clo = Clo(func, Map())
lazy val cloCaptured = Clo(func, Map(Name("x") -> Str("abc")))
lazy val cont = Cont(func, Map(), Nil)
Expand Down
Loading

0 comments on commit 3d5683b

Please sign in to comment.