diff --git a/core/src/main/scala/org/bykn/bosatsu/Matchless.scala b/core/src/main/scala/org/bykn/bosatsu/Matchless.scala index f91867482..3fea102ba 100644 --- a/core/src/main/scala/org/bykn/bosatsu/Matchless.scala +++ b/core/src/main/scala/org/bykn/bosatsu/Matchless.scala @@ -120,7 +120,8 @@ object Matchless { case class MatchString( arg: CheapExpr, parts: List[StrPart], - binds: List[LocalAnonMut] + binds: List[LocalAnonMut], + mustMatch: Boolean ) extends BoolExpr // set the mutable variable to the given expr and return true case class SetMut(target: LocalAnonMut, expr: Expr) extends BoolExpr @@ -132,7 +133,7 @@ object Matchless { case TrueConst | CheckVariant(_, _, _, _) | EqualsLit(_, _) | EqualsNat(_, _) => false - case MatchString(_, _, b) => b.nonEmpty + case MatchString(_, _, b, _) => b.nonEmpty case And(b1, b2) => hasSideEffect(b1) || hasSideEffect(b2) case SearchList(_, _, b, l) => l.nonEmpty || hasSideEffect(b) @@ -485,7 +486,7 @@ object Matchless { .map { binds => val ms = binds.map(_._2) - NonEmptyList.one((ms, MatchString(arg, pat, ms), binds)) + NonEmptyList.one((ms, MatchString(arg, pat, ms, mustMatch), binds)) } } case lp @ Pattern.ListPat(_) => diff --git a/core/src/main/scala/org/bykn/bosatsu/MatchlessToValue.scala b/core/src/main/scala/org/bykn/bosatsu/MatchlessToValue.scala index 19da5db6b..5c5b40078 100644 --- a/core/src/main/scala/org/bykn/bosatsu/MatchlessToValue.scala +++ b/core/src/main/scala/org/bykn/bosatsu/MatchlessToValue.scala @@ -201,7 +201,7 @@ object MatchlessToValue { scope.updateMut(mut, exprF(scope)) true } - case MatchString(str, pat, binds) => + case MatchString(str, pat, binds, _) => // do this before we evaluate the string binds match { case Nil => diff --git a/core/src/main/scala/org/bykn/bosatsu/codegen/clang/ClangGen.scala b/core/src/main/scala/org/bykn/bosatsu/codegen/clang/ClangGen.scala index 6b4064b1b..c3f8949dd 100644 --- a/core/src/main/scala/org/bykn/bosatsu/codegen/clang/ClangGen.scala +++ b/core/src/main/scala/org/bykn/bosatsu/codegen/clang/ClangGen.scala @@ -324,7 +324,7 @@ object ClangGen { .flatMapN { (condV, initV) => searchList(lst, initV, condV, leftAcc) } - case ms @ MatchString(arg, parts, binds) => + case ms @ MatchString(arg, parts, binds, mustMatch) => // TODO: ??? println(s"TODO: implement boolToValue($ms) returning false") pv(Code.FalseLit) diff --git a/core/src/main/scala/org/bykn/bosatsu/codegen/python/Code.scala b/core/src/main/scala/org/bykn/bosatsu/codegen/python/Code.scala index dbcad533a..9cb9236be 100644 --- a/core/src/main/scala/org/bykn/bosatsu/codegen/python/Code.scala +++ b/core/src/main/scala/org/bykn/bosatsu/codegen/python/Code.scala @@ -515,7 +515,14 @@ object Code { case PyInt(i) => if (i != BigInteger.ZERO) ifTrue.simplify else ifFalse.simplify case notStatic => - Ternary(ifTrue.simplify, notStatic, ifFalse.simplify) + + (ifTrue.simplify, ifFalse.simplify) match { + case (Const.One | Const.True, Const.Zero | Const.False) => + // this is just the condition + notStatic + case (st, sf) => + Ternary(st, notStatic, sf) + } } } case class MakeTuple(args: List[Expression]) extends Expression { diff --git a/core/src/main/scala/org/bykn/bosatsu/codegen/python/PythonGen.scala b/core/src/main/scala/org/bykn/bosatsu/codegen/python/PythonGen.scala index 35e3871fa..ffecba802 100644 --- a/core/src/main/scala/org/bykn/bosatsu/codegen/python/PythonGen.scala +++ b/core/src/main/scala/org/bykn/bosatsu/codegen/python/PythonGen.scala @@ -330,28 +330,21 @@ object PythonGen { def andCode(c1: ValueLike, c2: ValueLike): Env[ValueLike] = (c1, c2) match { - case (t: Expression, c2) if t.simplify == Code.Const.True => - Env.pure(c2) + case (e1: Expression, c2) => + // and(x, y) == if x: y else: False + Env.pure(c2 match { + case _ if e1.simplify == Code.Const.True => c2 + case e2: Expression => + // both are expressions + // e2 if e1 else False + Code.Ternary(e2, e1, Code.Const.False) + case _ => + Code.IfElse(NonEmptyList.one((e1, c2)), Code.Const.False) + }) case (_, x2: Expression) => onLast(c1)(_.evalAnd(x2)) case _ => - // we know that c2 is not a simple expression - // res = False - // if c1: - // res = c2 - Env.onLastM(c1) { x1 => - for { - res <- Env.newAssignableVar - ifstmt <- ifElseS(x1, res := c2, Code.Pass) - } yield { - Code - .block( - res := Code.Const.False, - ifstmt - ) - .withValue(res) - } - } + Env.onLastM(c1) { andCode(_, c2) } } def makeDef( @@ -1257,12 +1250,12 @@ object PythonGen { (ident := resx).withValue(Code.Const.True) } }.flatten - case MatchString(str, pat, binds) => + case MatchString(str, pat, binds, mustMatch) => ( loop(str, slotName), binds.traverse { case LocalAnonMut(m) => Env.nameForAnon(m) } ).mapN { (strVL, binds) => - Env.onLastM(strVL)(matchString(_, pat, binds)) + Env.onLastM(strVL)(matchString(_, pat, binds, mustMatch)) }.flatten case SearchList(locMut, init, check, optLeft) => // check to see if we can find a non-empty @@ -1276,7 +1269,8 @@ object PythonGen { def matchString( strEx: Expression, pat: List[StrPart], - binds: List[Code.Ident] + binds: List[Code.Ident], + mustMatch: Boolean ): Env[ValueLike] = { import StrPart.{LitStr, Glob, CharPart} val bindArray = binds.toArray @@ -1285,18 +1279,21 @@ object PythonGen { def loop( offsetIdent: Code.Ident, pat: List[StrPart], - next: Int + next: Int, + mustMatch: Boolean ): Env[ValueLike] = pat match { case Nil => // offset == str.length - Env.pure(offsetIdent =:= strEx.len()) + if (mustMatch) Env.pure(Code.Const.True) + else Env.pure(offsetIdent =:= strEx.len()) case LitStr(expect) :: tail => // val len = expect.length // str.regionMatches(offset, expect, 0, len) && loop(offset + len, tail, next) // // strEx.startswith(expect, offsetIdent) - loop(offsetIdent, tail, next) + // note: a literal string can never be a total match, so mustMatch is false + loop(offsetIdent, tail, next, mustMatch = false) .flatMap { loopRes => val regionMatches = strEx.dot(Code.Ident("startswith"))(expect, offsetIdent) @@ -1311,7 +1308,9 @@ object PythonGen { Env.andCode(regionMatches, rest) } case (c: CharPart) :: tail => - val matches = offsetIdent :< strEx.len() + val matches = + if (mustMatch) Code.Const.True + else offsetIdent :< strEx.len() val n1 = if (c.capture) (next + 1) else next val stmt = if (c.capture) { @@ -1324,7 +1323,7 @@ object PythonGen { .withValue(true) } else (offsetIdent := offsetIdent + 1).withValue(true) for { - tailRes <- loop(offsetIdent, tail, n1) + tailRes <- loop(offsetIdent, tail, n1, mustMatch) and2 <- Env.andCode(stmt, tailRes) and1 <- Env.andCode(matches, and2) } yield and1 @@ -1385,7 +1384,8 @@ object PythonGen { Env.newAssignableVar, Env.newAssignableVar ).mapN { (start, result, candidate, candOffset) => - val searchEnv = loop(candOffset, tail2, next1) + // note, a literal prefix can never be a total match + val searchEnv = loop(candOffset, tail2, next1, mustMatch = false) def onSearch(search: ValueLike): Env[Statement] = Env.ifElseS( @@ -1451,7 +1451,7 @@ object PythonGen { for { matched <- Env.newAssignableVar off1 <- Env.newAssignableVar - tailMatched <- loop(off1, tail, next1) + tailMatched <- loop(off1, tail, next1, mustMatch) matchStmt = Code .block( @@ -1488,7 +1488,7 @@ object PythonGen { for { offsetIdent <- Env.newAssignableVar - res <- loop(offsetIdent, pat, 0) + res <- loop(offsetIdent, pat, 0, mustMatch) } yield (offsetIdent := 0).withValue(res) }