Skip to content

Commit

Permalink
Trying to remove polymorphic recursion where possible
Browse files Browse the repository at this point in the history
  • Loading branch information
b-studios committed Oct 20, 2023
1 parent d810c54 commit 7b56e3d
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 173 deletions.
22 changes: 11 additions & 11 deletions effekt/shared/src/main/scala/effekt/lifted/Monomorphize.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,17 @@ object Monomorphize extends Phase[CoreLifted, CoreLifted] {

val binders = analysis.binders.collect { case (id: Id, ftpe: FlowType.Function) => s"${id.name}: ${ftpe.evidences.show}" }.toList.sorted.mkString("\n")

// println(s"""|
// |Constraints:
// |-----------
// |${constrs})
// |""".stripMargin)
//
// println(s"""|
// |
// |Binders:
// |${binders}
// |""".stripMargin)
println(s"""|
|Constraints:
|-----------
|${constrs})
|""".stripMargin)

println(s"""|
|
|Binders:
|${binders}
|""".stripMargin)

val (solved, cls) = solve(analysis.constraints)

Expand Down
11 changes: 6 additions & 5 deletions examples/casestudies/lexer.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ def example1() = {
## Handling the Lexer Effect with a given List
A dummy lexer reading lexemes from a given list can be implemented as a handler for the `Lexer` effect. The definition uses the effect `LexerError` to signal the end of the input stream:
```
effect LexerError[A](msg: String, pos: Position): A
effect LexerError(msg: String, pos: Position): Unit
def absurd[A](unit: Unit): A = panic("should not happen")
def dummyPosition() = Position(0, 0, 0)
def lexerFromList[R](l: List[Token]) { program: => R / Lexer }: R / LexerError = {
Expand All @@ -85,7 +86,7 @@ def lexerFromList[R](l: List[Token]) { program: => R / Lexer }: R / LexerError =
case Cons(tok, _) => resume(Some(tok))
}
def next() = in match {
case Nil() => do LexerError("Unexpected end of input", dummyPosition())
case Nil() => do LexerError("Unexpected end of input", dummyPosition()).absurd
case Cons(tok, _) => resume(tok)
}
}
Expand All @@ -94,7 +95,7 @@ def lexerFromList[R](l: List[Token]) { program: => R / Lexer }: R / LexerError =
We define a separate handler to report lexer errors to the console:
```
def report { prog: => Unit / LexerError }: Unit =
try { prog() } with LexerError[A] { (msg, pos) =>
try { prog() } with LexerError { (msg, pos) =>
println(pos.line.show ++ ":" ++ pos.col.show ++ " " ++ msg)
}
```
Expand Down Expand Up @@ -192,10 +193,10 @@ the input, or not.
def peek() = resume(tryMatchAll(tokenDesriptors()))
def next() =
if (eos())
do LexerError("Unexpected EOS", position())
do LexerError("Unexpected EOS", position()).absurd
else {
val tok = tryMatchAll(tokenDesriptors()).getOrElse {
do LexerError("Cannot tokenize input", position())
do LexerError("Cannot tokenize input", position()).absurd
}
consume(tok.text)
resume(tok)
Expand Down
89 changes: 44 additions & 45 deletions examples/casestudies/parser.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Parsers can be expressed by using the lexer effect and process the token stream.
```
effect Nondet {
def alt(): Boolean
def fail[A](msg: String): A
def fail(msg: String): Unit
}
effect Parser = { Nondet, Lexer }
Expand All @@ -40,7 +40,7 @@ input stream and fails, if it does not match.
def accept { p: Token => Boolean } : Token / Parser = {
val got = do next();
if (p(got)) got
else do fail("Unexpected token " ++ show(got))
else do fail("Unexpected token " ++ show(got)).absurd
}
```

Expand All @@ -53,12 +53,12 @@ def number() = accept(Number()).text
def punct(p: String) = {
val tok = accept(Punct())
if (tok.text == p) ()
else do fail("Expected " ++ p ++ " but got " ++ tok.text)
else do fail("Expected " ++ p ++ " but got " ++ tok.text).absurd
}
def kw(exp: String): Unit / Parser = {
val got = ident();
if (got == exp) ()
else do fail("Expected keyword " ++ exp ++ " but got " ++ got)
else do fail("Expected keyword " ++ exp ++ " but got " ++ got).absurd
}
```
Using the effect for non-deterministic choice `alt`, we can model alternatives, optional matches and various repetitions:
Expand Down Expand Up @@ -100,7 +100,7 @@ Let us start by defining the parser for numeric literals.
def parseNum(): Tree / Parser = {
val numText = number()
val num = toInt(numText).getOrElse {
do fail("Expected number, but cannot convert input to integer: " ++ numText)
do fail("Expected number, but cannot convert input to integer: " ++ numText).absurd
}
Lit(num)
}
Expand All @@ -122,41 +122,44 @@ semantics of effects.

Similarly, we can write parsers for let bindings, by sequentially composing
our existing parsers:
```
def parseLet(): Tree / Parser = {
kw("let");
val name = ident();
punct("=");
val binding = parseExpr();
kw("in");
val body = parseExpr();
Let(name, binding, body)
}
```


Again, note how naturally the result can be composed from the individual results, much like
manually writing a recursive descent parser. Compared to handcrafted parsers, the imperative
parser combinators presented here offer a similar flexibility. At the same time, the semantics
of `alt` and `fail` is still left open, offering flexibility in the implementation of the actual underlying parsing algorithm.

We proceed to implement the remaining parsers for our expression language:
```
def parseGroup() = or { parseAtom() } {
punct("(");
val res = parseExpr();
punct(")");
res
}
def parseExpr(): Tree / Parser = {
def parseLet(): Tree / {} = {
kw("let");
val name = ident();
punct("=");
val binding = parseExpr();
kw("in");
val body = parseExpr();
Let(name, binding, body)
}
def parseApp(): Tree / Parser = {
val funName = ident();
punct("(");
val arg = parseExpr();
punct(")");
App(funName, arg)
}
def parseGroup(): Tree / {} = or { parseAtom() } {
punct("(");
val res = parseExpr();
punct(")");
res
}
def parseApp(): Tree / {} = {
val funName = ident();
punct("(");
val arg = parseExpr();
punct(")");
App(funName, arg)
}
def parseExpr(): Tree / Parser =
or { parseLet() } { or { parseApp() } { parseGroup() } }
}
```

## Example: Combining Parsers and Local Mutable State
Expand All @@ -176,11 +179,7 @@ def parseCalls(): Int / Parser = {
or { number(); 1 } {
ident();
punct("(");
val count = parseCalls() +
sum(many {
punct(",");
parseCalls()
});
val count = parseCalls() + sum(Nil());
punct(")");
count
}
Expand Down Expand Up @@ -213,14 +212,14 @@ The parsing algorithm is simply implemented as a handler for `Parser`.

```
def parse[R](input: String) { p: => R / Parser }: ParseResult[R] = try {
lexer(input) { skipWhitespace { Success(p()) } }
lexer(input) { Success(p()) }
} with Nondet {
def alt() = resume(true) match {
case Failure(msg) => resume(false)
case Success(res) => Success(res)
}
def fail[A](msg) = Failure(msg)
} with LexerError[A] { (msg, pos) =>
def fail(msg) = Failure(msg)
} with LexerError { (msg, pos) =>
Failure(msg)
}
```
Expand All @@ -246,12 +245,12 @@ def main() = {
println(parse("foo(1, 2, bar(4, 5))") { parseCalls() })
println(parse("foo(1, 2,\nbar(4, 5))") { parseCalls() })
// println(parse("}42") { parseExpr() })
// println(parse("42") { parseExpr() })
// println(parse("let x = 4 in 42") { parseExpr() })
// println(parse("let x = let y = 2 in 1 in 42") { parseExpr() })
// println(parse("let x = (let y = 2 in 1) in 42") { parseExpr() })
// println(parse("let x = (let y = f(42) in 1) in 42") { parseExpr() })
// println(parse("let x = (let y = f(let z = 1 in z) in 1) in 42") { parseExpr() })
println(parse("}42") { parseExpr() })
println(parse("42") { parseExpr() })
println(parse("let x = 4 in 42") { parseExpr() })
println(parse("let x = let y = 2 in 1 in 42") { parseExpr() })
println(parse("let x = (let y = 2 in 1) in 42") { parseExpr() })
println(parse("let x = (let y = f(42) in 1) in 42") { parseExpr() })
println(parse("let x = (let y = f(let z = 1 in z) in 1) in 42") { parseExpr() })
}
```
Loading

0 comments on commit 7b56e3d

Please sign in to comment.