Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Strata/Backends/CBMC/GOTO/LambdaToCProverGOTO.lean
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ handle these correctly.

**Integer**: (none — all integer operators are now handled)
**Real**: (none — all real operators are now handled)
**String**: `Str.Concat`, `Str.Length`, `Str.Substr`, `Str.InRegEx`, `Str.ToRegEx`
**String**: `Str.Concat`, `Str.Length`, `Str.Substr`, `Str.ToLower`, `Str.ToUpper`,
`Str.InRegEx`, `Str.ToRegEx`
(intentionally left as function applications for the CBMC string solver patch)
**Bitvector**: `Bv{8,16,32,64}.Extract_{hi}_{lo}` maps to GOTO `extractbits`; the
lower bit index is parsed from the operator name and emitted as a constant operand.
Expand Down
6 changes: 4 additions & 2 deletions Strata/DL/Lambda/IntBoolFactory.lean
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ def unaryOp (n : T.Identifier)
[hIn : LambdaLeanType inTy InValTy] [hOut : LambdaLeanType outTy OutValTy]
(op : InValTy → OutValTy)
(hInTy : inTy.freeVars = [] := by decide)
(hOutTy : outTy.freeVars = [] := by decide) : WFLFunc T :=
(hOutTy : outTy.freeVars = [] := by decide)
(axioms : List (LExpr T.mono) := []) : WFLFunc T :=
let mkConst := LambdaLeanType.mkConst (ValTy := OutValTy) T
let cevalInTy := LambdaLeanType.cevalTy (ValTy := InValTy) T
⟨{ name := n,
Expand All @@ -156,7 +157,8 @@ def unaryOp (n : T.Identifier)
| [x] => match cevalInTy x with
| some a => .some (mkConst md (op a))
| _ => .none
| _ => none) }, {
| _ => none),
axioms := axioms }, {
arg_nodup := by simp
body_freevars := by intro b hb; simp at hb
concreteEval_argmatch := by
Expand Down
2 changes: 2 additions & 0 deletions Strata/Languages/Core/DDMTransform/ASTtoCST.lean
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,8 @@ def handleUnaryOps {M} [Inhabited M] (name : String) (arg : CoreDDM.Expr M)
| "Bool.Not" => pure (.not default arg)
-- Strings and regexes
| "Str.Length" => pure (.str_len default arg)
| "Str.ToLower" => pure (.str_tolower default arg)
| "Str.ToUpper" => pure (.str_toupper default arg)
| "Str.ToRegEx" => pure (.str_toregex default arg)
| "Re.Star" => pure (.re_star default arg)
| "Re.Plus" => pure (.re_plus default arg)
Expand Down
2 changes: 2 additions & 0 deletions Strata/Languages/Core/DDMTransform/Grammar.lean
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ fn seq_drop (A : Type, s : Sequence A, n : int) : Sequence A =>
fn str_len (a : string) : int => "str.len" "(" a ")";
fn str_concat (a : string, b : string) : string => "str.concat" "(" a ", " b ")";
fn str_substr (a : string, i : int, n : int) : string => "str.substr" "(" a ", " i ", " n ")";
fn str_tolower (a : string) : string => "str.tolower" "(" a ")";
fn str_toupper (a : string) : string => "str.toupper" "(" a ")";
fn str_toregex (a : string) : regex => "str.to.re" "(" a ")";
fn str_inregex (s : string, a : regex) : bool => "str.in.re" "(" s ", " a ")";
fn re_allchar () : regex => "re.allchar" "(" ")";
Expand Down
4 changes: 4 additions & 0 deletions Strata/Languages/Core/DDMTransform/Translate.lean
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,8 @@ def translateFn (ty? : Option LMonoTy) (q : QualifiedIdent) : TransM Core.Expres
| _, q`Core.str_len => return Core.strLengthOp
| _, q`Core.str_concat => return Core.strConcatOp
| _, q`Core.str_substr => return Core.strSubstrOp
| _, q`Core.str_tolower => return Core.strToLowerOp
| _, q`Core.str_toupper => return Core.strToUpperOp
| _, q`Core.str_toregex => return Core.strToRegexOp
| _, q`Core.str_inregex => return Core.strInRegexOp
| _, q`Core.re_all => return Core.reAllOp
Expand Down Expand Up @@ -806,6 +808,8 @@ partial def translateExpr (p : Program) (bindings : TransBindings) (arg : Arg) :
| q`Core.bvextract_15_0_64
| q`Core.bvextract_31_0_64
| q`Core.str_len
| q`Core.str_tolower
| q`Core.str_toupper
| q`Core.str_toregex
| q`Core.re_star
| q`Core.re_plus
Expand Down
50 changes: 50 additions & 0 deletions Strata/Languages/Core/Factory.lean
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,52 @@ def strSubstrFunc : WFLFunc CoreLParams :=
polyUneval "Str.Substr" []
[("x", mty[string]), ("i", mty[int]), ("n", mty[int])] mty[string]

def strToLowerFunc : WFLFunc CoreLParams :=
unaryOp "Str.ToLower" String.toLower (axioms := [
-- Idempotence: `toLower(toLower(s)) == toLower(s)`
esM[∀ (string): -- %0 s
{((~Str.ToLower : string → string) ((~Str.ToLower : string → string) %0))}
((~Str.ToLower : string → string) ((~Str.ToLower : string → string) %0))
== ((~Str.ToLower : string → string) %0)],
-- Length preservation: `length(toLower(s)) == length(s)`
esM[∀ (string): -- %0 s
{((~Str.ToLower : string → string) %0)}
((~Str.Length : string → int) ((~Str.ToLower : string → string) %0))
== ((~Str.Length : string → int) %0)],
-- Concat distributivity: `toLower(s1 ++ s2) == toLower(s1) ++ toLower(s2)`
-- `str.concat` is a built-in SMT op so we elide any trigger here.
esM[∀ (string): -- %1 s1
(∀ (string): -- %0 s2
((~Str.ToLower : string → string)
(((~Str.Concat : string → string → string) %1) %0))
== (((~Str.Concat : string → string → string)
((~Str.ToLower : string → string) %1))
((~Str.ToLower : string → string) %0)))]
])

def strToUpperFunc : WFLFunc CoreLParams :=
unaryOp "Str.ToUpper" String.toUpper (axioms := [
-- Idempotence: `toUpper(toUpper(s)) == toUpper(s)`
esM[∀ (string): -- %0 s
{((~Str.ToUpper : string → string) ((~Str.ToUpper : string → string) %0))}
((~Str.ToUpper : string → string) ((~Str.ToUpper : string → string) %0))
== ((~Str.ToUpper : string → string) %0)],
-- Length preservation: `length(toUpper(s)) == length(s)`
esM[∀ (string): -- %0 s
{((~Str.ToUpper : string → string) %0)}
((~Str.Length : string → int) ((~Str.ToUpper : string → string) %0))
== ((~Str.Length : string → int) %0)],
-- Concat distributivity: `toUpper(s1 ++ s2) == toUpper(s1) ++ toUpper(s2)`
-- `str.concat` is a built-in SMT op so we elide any trigger here.
esM[∀ (string): -- %1 s1
(∀ (string): -- %0 s2
((~Str.ToUpper : string → string)
(((~Str.Concat : string → string → string) %1) %0))
== (((~Str.Concat : string → string → string)
((~Str.ToUpper : string → string) %1))
((~Str.ToUpper : string → string) %0)))]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed that all these axioms and their ToLower versions are indeed valid, using CVC5.
I heard ß uppercases to SS, but probably ToUpper only deals with English alphabets.

])

def strToRegexFunc : WFLFunc CoreLParams :=
unaryFuncUneval "Str.ToRegEx" mty[string] mty[regex]

Expand Down Expand Up @@ -663,6 +709,8 @@ def WFFactory : Lambda.WFLFactory CoreLParams :=
strLengthFunc,
strConcatFunc,
strSubstrFunc,
strToLowerFunc,
strToUpperFunc,
strToRegexFunc,
strInRegexFunc,
reAllFunc,
Expand Down Expand Up @@ -790,6 +838,8 @@ def boolNotOp : Expression.Expr := (@boolNotFunc CoreLParams _).opExpr
def strLengthOp : Expression.Expr := strLengthFunc.opExpr
def strConcatOp : Expression.Expr := strConcatFunc.opExpr
def strSubstrOp : Expression.Expr := strSubstrFunc.opExpr
def strToLowerOp : Expression.Expr := strToLowerFunc.opExpr
def strToUpperOp : Expression.Expr := strToUpperFunc.opExpr
def strToRegexOp : Expression.Expr := strToRegexFunc.opExpr
def strInRegexOp : Expression.Expr := strInRegexFunc.opExpr
def reAllOp : Expression.Expr := reAllFunc.opExpr
Expand Down
24 changes: 21 additions & 3 deletions StrataTest/Languages/Core/Tests/ExprEvalTest.lean
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,22 @@ def checkValid (e:LExpr CoreLParams.mono): IO Bool := do
[] smt_term ctx true false
match ans with
| .ok (.sat _, _, _) => return true
| _ =>
IO.println s!"Test failed on {e}"
| .ok (smt_ans, _, _) =>
IO.println s!"Test failed on {instCoreExprFormat.format e}"
IO.println s!"The query: {repr smt_term}"
throw (IO.userError "- failed"))
IO.println s!"SMT solver returned {repr smt_ans}."
throw (IO.userError "- failed")
| .error err =>
-- Benefit of the doubt when we have a solver timeout
-- (which can happen if we have uninterpreted functions.)
if (toString err).contains "timeout" then
IO.println s!"Test TIMED OUT on {instCoreExprFormat.format e}"
IO.println s!"The query: {repr smt_term}"
IO.println s!"Ignoring and moving on."
return true
else
IO.println s!"ERROR: {err}."
throw (IO.userError "- failed"))

/--
If a randomly chosen value is <= odd / 10, pick from interesting vals,
Expand Down Expand Up @@ -145,6 +157,12 @@ def checkFactoryOps (verbose:Bool): IO Unit := do
if ¬ e.typeArgs.isEmpty then
print "- Has non-empty type arguments, skipping..."
continue
else if ¬ e.axioms.isEmpty then
-- These tests make sense for native SMTLib operations.
-- We'll trivially get a SAT (or worse, a timeout) for UFs + axioms, so
-- let's just skip these.
print "- Has axioms (UF with symbolic constraints), skipping..."
continue
else
let cnt := 5
let mut unsupported := false
Expand Down
2 changes: 2 additions & 0 deletions StrataTest/Languages/Core/Tests/ProcedureEvalTests.lean
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ func Bool.Not : ((x : bool)) → bool;
func Str.Length : ((x : string)) → int;
func Str.Concat : ((x : string) (y : string)) → string;
func Str.Substr : ((x : string) (i : int) (n : int)) → string;
func Str.ToLower : ((x : string)) → string;
func Str.ToUpper : ((x : string)) → string;
func Str.ToRegEx : ((x : string)) → regex;
func Str.InRegEx : ((x : string) (y : regex)) → bool;
func Re.All : () → regex;
Expand Down
2 changes: 2 additions & 0 deletions StrataTest/Languages/Core/Tests/ProgramTypeTests.lean
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ info: ok: [(type Foo (a : Type, b : Type);
func Str.Length : ((x : string)) → int;
func Str.Concat : ((x : string) (y : string)) → string;
func Str.Substr : ((x : string) (i : int) (n : int)) → string;
func Str.ToLower : ((x : string)) → string;
func Str.ToUpper : ((x : string)) → string;
func Str.ToRegEx : ((x : string)) → regex;
func Str.InRegEx : ((x : string) (y : regex)) → bool;
func Re.All : () → regex;
Expand Down
8 changes: 8 additions & 0 deletions StrataTest/Languages/Core/Tests/SMTEncoderTests.lean
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,14 @@ info: "; x\n(declare-const x Real)\n(define-fun t0 () Real x)\n; y\n(declare-con
}
}})

/--
info: "; Str.ToLower\n(declare-fun Str.ToLower (String) String)\n(define-fun t0 () String (Str.ToLower \"a\"))\n"
-/
#guard_msgs in
#eval toSMTTermString $
LExpr.app () (LExpr.op () { name := "Str.ToLower", metadata := () } (.some (.arrow .string .string)))
(LExpr.const () (.strConst "a"))

end ArrayTheory

/-! ## Test that built-in types do not produce declare-sort -/
Expand Down
Loading
Loading