Skip to content

Commit

Permalink
Added switch conditional implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
vbergeron-ledger committed Nov 23, 2023
1 parent 7eaaa91 commit fbb75b2
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 5 deletions.
16 changes: 16 additions & 0 deletions src/main/scala/sail/main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def show(expr: Expr): String =
s"<container from ${show(from)} - ${showInstr(build)}>"
case Expr.Module(name, source) =>
s"<module ${show(name)} - ${show(source)}"
case Expr.Switch(_, _) => "<switch TODO>"

def render(expr: Expr): String =
expr match
Expand Down Expand Up @@ -195,6 +196,21 @@ def reduce(cmd: Args, env: Env, expr: Expr): (Env, Expr) =
(sym, expr) => sym.copy(module = Some(mod.name.value)) -> expr
(env, Expr.Unit)

case switch: Expr.Switch =>
import BooleanExpr.*
val branch = switch.clauses.find: (cond, _) =>
val reduced = reduce(cmd, env, cond)._2 match
case x: (True.type | False.type) =>
x
case e =>
throw Exception(
s"Expression did not reduce to boolean: $cond (reduced: $e)"
)
reduced == True
(branch.map(_._2) orElse switch.default)
.fold(throw Exception(s"Non exhaustive switch: $switch")): expr =>
reduce(cmd, env, expr)

def reduceFile(cmd: Args, exprs: Seq[Expr]): Env =
exprs.foldLeft(Map.empty[Expr.Sym, Expr])(reduce(cmd, _, _)._1)

Expand Down
2 changes: 2 additions & 0 deletions src/main/scala/sail/model.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ object Expr:
case class FuncCall(name: Sym, args: Seq[Expr]) extends Expr
case class Container(from: Str, build: Instr) extends Expr
case class Scope(bindings: Seq[FuncDef], body: Expr) extends Expr
case class Switch(clauses: Seq[(Expr, Expr)], default: Option[Expr])
extends Expr

case class Module(name: Expr.Sym, sourcePath: Expr.Str) extends Expr

Expand Down
27 changes: 22 additions & 5 deletions src/main/scala/sail/parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def part[$: P]: P[Part] =
capture | content

def expr[$: P]: P[Expr] =
scope | container | module | funcDef | instr | template | str | num | funcCall | sym | boolean.apply
cond.apply | scope | container | module | funcDef | instr | template | str | num | funcCall | sym | boolean.apply

object boolean:
def t[$: P]: P[BooleanExpr.True.type] =
Expand All @@ -28,22 +28,25 @@ object boolean:
P("false").map(_ => BooleanExpr.False)

def and[$: P]: P[BooleanExpr.And] =
P(expr ~ ws ~ "and" ~/ ws ~ expr)
val inner = eq | expr
P(inner ~ ws ~ "and" ~ ws ~ inner)
.map(BooleanExpr.And.apply)

def or[$: P]: P[BooleanExpr.Or] =
P(expr ~ ws ~ "or" ~/ ws ~ expr)
val inner = expr
P(inner ~ ws ~ "or" ~ ws ~ inner)
.map(BooleanExpr.Or.apply)

def not[$: P]: P[BooleanExpr.Not] =
P("not" ~ ws ~ expr).map(BooleanExpr.Not.apply)

def eq[$: P]: P[BooleanExpr.Eq] =
P(expr ~ ws ~ "==" ~/ ws ~ expr)
val inner = expr
P(inner ~ ws ~ "==" ~ ws ~ inner)
.map(BooleanExpr.Eq.apply)

def apply[$: P]: P[BooleanExpr] =
eq | and | or | not | t | f
and | or | eq | not | t | f

def instr[$: P]: P[Instr] =
def run[$: P]: P[Instr.Run] =
Expand All @@ -69,6 +72,20 @@ def instr[$: P]: P[Instr] =

rec

object cond:
def clause[$: P]: P[(Expr, Expr)] =
P("when" ~ ws ~ expr ~ ws ~ "then" ~ ws ~ expr)

def default[$: P]: P[Expr] =
P("else" ~ ws ~ expr)

def apply[$: P]: P[Expr.Switch] =
P(clause.rep ~ ws ~ default.?)
.filter: (clauses, _) =>
clauses.size > 0
.map: (clauses, default) =>
Expr.Switch(clauses, default)

def scope[$: P]: P[Expr.Scope] =
P("let" ~ ws ~ (funcDef ~ ws).rep ~ "in" ~ ws ~ expr).map(Expr.Scope.apply)

Expand Down
24 changes: 24 additions & 0 deletions src/test/scala/parser.test.scala
Original file line number Diff line number Diff line change
Expand Up @@ -107,5 +107,29 @@ class ParserTest extends FunSuite:
passing("Boolean", "false", boolean(_))
passing("Boolean", "bar or bar", boolean(_))
passing("Boolean", "f(x) and g(y)", boolean(_))
// passingT("Boolean", "foo == bar and foo == baz", boolean.and(_)): expr =>
// import BooleanExpr.*
// import Expr.*
// assertEquals(
// expr,
// And(
// Eq(Sym(None, "foo"), Sym(None, "bar")),
// Eq(Sym(None, "foo"), Sym(None, "bar"))
// )
// )

passing("Boolean", "f(x) == g(y)", boolean(_))
passing("Boolean", "not f(x)", boolean(_))

passing("Switch", "when foo then bar", cond.apply)
passing("Switch", "when foo then bar when foobar then barbar", cond.apply)

passing("Switch", "when foo then bar else baz", cond.apply)
passing(
"Switch",
"""when foo then bar
|when foo == bar then barbar
|else baz
|""".stripMargin,
cond.apply
)

0 comments on commit fbb75b2

Please sign in to comment.