From 716b0395c6d89121b7b0d98807a152bb7c347562 Mon Sep 17 00:00:00 2001 From: Nicolas Voirol <voirol.nicolas@gmail.com> Date: Mon, 3 Oct 2016 15:25:06 +0200 Subject: [PATCH] More solver tests (+fixes) and start on TIP test --- src/it/scala/inox/InoxTestSuite.scala | 1 - src/it/scala/inox/ResourceUtils.scala | 30 ++ .../unrolling/InductiveUnrollingSuite.scala | 41 ++- src/it/scala/inox/tip/TIPTestSuite.scala | 37 +++ src/main/scala/inox/parsers/TIPParser.scala | 309 +++++++++--------- .../scala/inox/solvers/smtlib/Z3Target.scala | 33 +- 6 files changed, 285 insertions(+), 166 deletions(-) create mode 100644 src/it/scala/inox/ResourceUtils.scala create mode 100644 src/it/scala/inox/tip/TIPTestSuite.scala diff --git a/src/it/scala/inox/InoxTestSuite.scala b/src/it/scala/inox/InoxTestSuite.scala index 9abb984f9..89b1d0649 100644 --- a/src/it/scala/inox/InoxTestSuite.scala +++ b/src/it/scala/inox/InoxTestSuite.scala @@ -8,7 +8,6 @@ import org.scalatest.concurrent._ import utils._ trait InoxTestSuite extends FunSuite with Matchers with Timeouts { - type FixtureParam = InoxContext val configurations: Seq[Seq[InoxOption[Any]]] = Seq(Seq.empty) diff --git a/src/it/scala/inox/ResourceUtils.scala b/src/it/scala/inox/ResourceUtils.scala new file mode 100644 index 000000000..3297b44f6 --- /dev/null +++ b/src/it/scala/inox/ResourceUtils.scala @@ -0,0 +1,30 @@ +/* Copyright 2009-2016 EPFL, Lausanne */ + +package inox + +import org.scalatest._ +import org.scalatest.concurrent._ + +import java.io.File + +import utils._ + +trait ResourceUtils { + + val resourcesDir = "src/it/resources" + + def resourceFiles(dir: String, filter: String => Boolean = (s: String) => true, recursive: Boolean = false): Seq[File] = { + val baseDir = new File(getClass.getResource(s"/$dir").getPath) + + def rec(f: File): Seq[File] = Option(f.listFiles()).getOrElse(Array()).flatMap { f => + if (f.isDirectory) { + if (recursive) rec(f) + else Nil + } else { + List(f) + } + } + + rec(baseDir).filter(f => filter(f.getPath)).toSeq.sortBy(_.getPath) + } +} diff --git a/src/it/scala/inox/solvers/unrolling/InductiveUnrollingSuite.scala b/src/it/scala/inox/solvers/unrolling/InductiveUnrollingSuite.scala index 50182a30f..70fcc7b3a 100644 --- a/src/it/scala/inox/solvers/unrolling/InductiveUnrollingSuite.scala +++ b/src/it/scala/inox/solvers/unrolling/InductiveUnrollingSuite.scala @@ -22,6 +22,15 @@ class InductiveUnrollingSuite extends SolvingTestSuite { } val sizeID = FreshIdentifier("size") + val appendID = FreshIdentifier("append") + val flatMapID = FreshIdentifier("flatMap") + val assocID = FreshIdentifier("associative_lemma") + + val forallID = FreshIdentifier("forall") + val contentID = FreshIdentifier("content") + val partitionID = FreshIdentifier("partition") + val sortID = FreshIdentifier("sort") + val sizeFd = mkFunDef(sizeID)("A") { case Seq(aT) => ( Seq("l" :: T(listID)(aT)), IntegerType, { case Seq(l) => if_ (l.isInstOf(T(consID)(aT))) { @@ -34,20 +43,18 @@ class InductiveUnrollingSuite extends SolvingTestSuite { }) } - val appendID = FreshIdentifier("append") val append = mkFunDef(appendID)("A") { case Seq(aT) => ( Seq("l1" :: T(listID)(aT), "l2" :: T(listID)(aT)), T(listID)(aT), { case Seq(l1, l2) => - if_ (l1.isInstOf(T(consID)(aT))) { + let("res" :: T(listID)(aT), if_ (l1.isInstOf(T(consID)(aT))) { let("c" :: T(consID)(aT), l1.asInstOf(T(consID)(aT))) { c => T(consID)(aT)(c.getField(head), E(appendID)(aT)(c.getField(tail), l2)) } } else_ { l2 - } + }) { res => Assume(E(contentID)(aT)(res) === E(contentID)(aT)(l1) ++ E(contentID)(aT)(l2), res) } }) } - val flatMapID = FreshIdentifier("flatMap") val flatMap = mkFunDef(flatMapID)("A","B") { case Seq(aT, bT) => ( Seq("l" :: T(listID)(aT), "f" :: (aT =>: T(listID)(bT))), T(listID)(bT), { case Seq(l, f) => if_ (l.isInstOf(T(consID)(aT))) { @@ -60,7 +67,6 @@ class InductiveUnrollingSuite extends SolvingTestSuite { }) } - val assocID = FreshIdentifier("associative_lemma") val associative = mkFunDef(assocID)("A","B","C") { case Seq(aT, bT, cT) => ( Seq("l1" :: T(listID)(aT), "l2" :: T(listID)(bT), "l3" :: T(listID)(cT), "f" :: (aT =>: T(listID)(bT)), "g" :: (bT =>: T(listID)(cT))), BooleanType, @@ -88,7 +94,6 @@ class InductiveUnrollingSuite extends SolvingTestSuite { }) } - val forallID = FreshIdentifier("forall") val forall = mkFunDef(forallID)("A") { case Seq(aT) => ( Seq("l" :: T(listID)(aT), "p" :: (aT =>: BooleanType)), BooleanType, { case Seq(l, p) => if_ (l.isInstOf(T(consID)(aT))) { @@ -101,7 +106,6 @@ class InductiveUnrollingSuite extends SolvingTestSuite { }) } - val contentID = FreshIdentifier("content") val content = mkFunDef(contentID)("A") { case Seq(aT) => ( Seq("l" :: T(listID)(aT)), SetType(aT), { case Seq(l) => if_ (l.isInstOf(T(consID)(aT))) { @@ -114,7 +118,6 @@ class InductiveUnrollingSuite extends SolvingTestSuite { }) } - val partitionID = FreshIdentifier("partition") val partition = mkFunDef(partitionID)("A") { case Seq(aT) => ( Seq("l" :: T(listID)(aT), "p" :: (aT =>: BooleanType)), T(T(listID)(aT), T(listID)(aT)), { case Seq(l, p) => let("res" :: T(T(listID)(aT), T(listID)(aT)), if_ (l.isInstOf(T(consID)(aT))) { @@ -139,7 +142,6 @@ class InductiveUnrollingSuite extends SolvingTestSuite { }) } - val sortID = FreshIdentifier("sort") val sort = mkFunDef(sortID)("A") { case Seq(aT) => ( Seq("l" :: T(listID)(aT), "lt" :: ((aT, aT) =>: BooleanType)), T(listID)(aT), { case Seq(l, lt) => let("res" :: T(listID)(aT), if_ (l.isInstOf(T(consID)(aT))) { @@ -148,13 +150,12 @@ class InductiveUnrollingSuite extends SolvingTestSuite { E(partitionID)(aT)(cons.getField(tail), \("x" :: aT)(x => lt(x, cons.getField(head))))) { part => let("less" :: T(listID)(aT), E(sortID)(aT)(part._1, lt)) { less => let("more" :: T(listID)(aT), E(sortID)(aT)(part._2, lt)) { more => - let("res" :: T(listID)(aT), E(appendID)(aT)(less, T(consID)(aT)(cons.getField(head), more))) { res => Assume(E(forallID)(aT)(part._1, \("x" :: aT)(x => lt(x, cons.getField(head)))), - Assume(E(contentID)(aT)(less) === E(contentID)(aT)(part._1), + Assume(E(contentID)(aT)(part._1) === E(contentID)(aT)(less), Assume(E(forallID)(aT)(less, \("x" :: aT)(x => lt(x, cons.getField(head)))), - res + E(appendID)(aT)(less, T(consID)(aT)(cons.getField(head), more)) ))) - }}}}} + }}}} } else_ { l }) { res => @@ -190,7 +191,7 @@ class InductiveUnrollingSuite extends SolvingTestSuite { assert(SimpleSolverAPI(SolverFactory.default(program)).solveSAT(Not(associative.fullBody)).isUNSAT) } - test("sort preserves content") { ctx => + test("sort preserves content 1") { ctx => val program = InoxProgram(ctx, symbols) val (l,p) = ("l" :: T(listID)(IntegerType), "p" :: ((IntegerType, IntegerType) =>: BooleanType)) val clause = E(contentID)(IntegerType)(E(sortID)(IntegerType)(l.toVariable, p.toVariable)) === @@ -198,4 +199,16 @@ class InductiveUnrollingSuite extends SolvingTestSuite { assert(SimpleSolverAPI(SolverFactory.default(program)).solveSAT(Not(clause)).isUNSAT) } + test("sort preserves content 2") { ctx => + val program = InoxProgram(ctx, symbols) + import program._ + + val clause = sort.fullBody match { + case Let(res, body, Assume(pred, resVar)) if res.toVariable == resVar => + Let(res, body, pred) + case e => fail("Unexpected fullBody of `sort`: " + e.asString) + } + assert(SimpleSolverAPI(SolverFactory.default(program)).solveSAT(Not(clause)).isUNSAT) + } + } diff --git a/src/it/scala/inox/tip/TIPTestSuite.scala b/src/it/scala/inox/tip/TIPTestSuite.scala new file mode 100644 index 000000000..19f91fe85 --- /dev/null +++ b/src/it/scala/inox/tip/TIPTestSuite.scala @@ -0,0 +1,37 @@ +/* Copyright 2009-2016 EPFL, Lausanne */ + +package inox +package tip + +import org.scalatest._ +import org.scalatest.concurrent._ + +import solvers._ +import utils._ + +trait TIPTestSuite extends InoxTestSuite with ResourceUtils { + + val tipDir = "tip-benchmarks" + + def makeTests(base: String) = { + val files = resourceFiles(s"$tipDir/$base") + + if (files.isEmpty) { + ignore(s"tip-benchmarks: $base directory not found (missing git submodule?") {_ => ()} + } else { + for (file <- files) { + test(s"Verifying tip-benchmarks/$base") { ctx => + val (syms, clause) = parsers.TIPParser.parse(file) + val program = InoxProgram(ctx, syms) + + assert(SimpleSolverAPI(SolverFactory.default(program)).solveSAT(clause).isSAT) + } + } + } + } + + makeTests("grammars") + makeTests("isaplanner") + makeTests("prod") + makeTests("tip2015") +} diff --git a/src/main/scala/inox/parsers/TIPParser.scala b/src/main/scala/inox/parsers/TIPParser.scala index 6f0897b17..8af28f8f1 100644 --- a/src/main/scala/inox/parsers/TIPParser.scala +++ b/src/main/scala/inox/parsers/TIPParser.scala @@ -18,7 +18,20 @@ import utils._ import scala.language.implicitConversions trait TIPParser { - import inox.trees._ + val trees: ast.Trees + import trees._ + + def parse(file: File): (Symbols, Expr) = { + val pos = new PositionProvider(new java.io.BufferedReader(new java.io.FileReader(file)), Some(file)) + val parser = new Parser(new Lexer(pos.reader), pos) + parser.parseTIPScript + } + + def parse(reader: Reader): (Symbols, Expr) = { + val pos = new PositionProvider(reader, None) + val parser = new Parser(new Lexer(pos.reader), pos) + parser.parseTIPScript + } private class PositionProvider(_reader: Reader, _file: Option[File]) { val (reader, file): (Reader, File) = _file match { @@ -48,13 +61,13 @@ trait TIPParser { class MissformedTIPException(reason: String, pos: Position) extends Exception("Missfomed TIP source @" + pos + ":\n" + reason) - private class Parser(lex: Lexer, positions: PositionProvider) extends SMTParser(lex) { + protected class Parser(lex: Lexer, positions: PositionProvider) extends SMTParser(lex) { implicit def smtlibPositionToPosition(pos: _root_.smtlib.common.Position): Position = { positions.get(pos.line, pos.col) } - private class Locals private( + protected class Locals ( funs: Map[SSymbol, Identifier], adts: Map[SSymbol, Identifier], selectors: Map[SSymbol, Identifier], @@ -109,176 +122,168 @@ trait TIPParser { new Locals(funs, adts, selectors, vars, tps, symbols.withADTs(defs)) } - private object Locals { - def empty = new Locals(Map.empty, Map.empty, Map.empty, Map.empty, Map.empty, NoSymbols) - } + protected val NoLocals: Locals = new Locals( + Map.empty, Map.empty, Map.empty, Map.empty, Map.empty, NoSymbols) - private def getIdentifier(sym: SSymbol): Identifier = { + protected def getIdentifier(sym: SSymbol): Identifier = { // TODO: check keywords! FreshIdentifier(sym.name) } - def parseTIPScript: (InoxProgram, Expr) = { + protected def parseTIPCommand(token: Tokens.Token) + (implicit locals: Locals): (Option[Expr], Locals) = token match { + case Tokens.SymbolLit("assert-not") => + (Some(Not(extractExpr(parseTerm))), locals) - val ctx: InoxContext = InoxContext.empty - var assertions: Seq[Expr] = Seq.empty - implicit var locals: Locals = Locals.empty + case Tokens.Token(Tokens.Assert) => + (Some(extractExpr(parseTerm)), locals) - while (peekToken != null) { - eat(Tokens.OParen) - - peekToken match { - case Tokens.SymbolLit("assert-not") => - nextToken // eat the "assert-not" token - assertions :+= Not(extractExpr(parseTerm)) - - case Tokens.Token(Tokens.Assert) => - nextToken // eat the "assert" token - assertions :+= extractExpr(parseTerm) - - case Tokens.Token(Tokens.DefineFun) | Tokens.Token(Tokens.DefineFunRec) => - // eats the "define-fun" or "define-fun-rec" token - val isRec = nextToken == Tokens.Token(Tokens.DefineFunRec) - val (tps, funDef) = getPeekToken.kind match { - case Tokens.OParen => - eat(Tokens.OParen) - eat(Tokens.Par) - val tps = parseMany(parseSymbol _) - val res = parseWithin(Tokens.OParen, Tokens.CParen)(parseFunDef _) - eat(Tokens.CParen) - (tps, res) - - case _ => - (Seq.empty[SSymbol], parseFunDef) - } + case Tokens.Token(Tokens.DefineFun) | Tokens.Token(Tokens.DefineFunRec) => + val isRec = token == Tokens.Token(Tokens.DefineFunRec) + val (tps, funDef) = getPeekToken.kind match { + case Tokens.OParen => + eat(Tokens.OParen) + eat(Tokens.Par) + val tps = parseMany(parseSymbol _) + val res = parseWithin(Tokens.OParen, Tokens.CParen)(parseFunDef _) + eat(Tokens.CParen) + (tps, res) - val tpsLocals = locals.withGenerics(tps.map(s => s -> TypeParameter.fresh(s.name).setPos(s.getPos))) - val fdsLocals = if (!isRec) tpsLocals else { - tpsLocals.withFunction(funDef.name, extractSignature(funDef, tps)(tpsLocals)) - } - val fd = extractFunction(funDef, tps)(fdsLocals) - locals = locals.withFunction(funDef.name, fd) - - case Tokens.Token(Tokens.DefineFunsRec) => - nextToken // eat the "define-funs-rec" token - val (funDec, funDecs) = parseOneOrMore(() => { - eat(Tokens.OParen) - val (tps, funDec) = getPeekToken.kind match { - case Tokens.Par => - eat(Tokens.Par) - val tps = parseMany(parseSymbol _) - val funDec = parseWithin(Tokens.OParen, Tokens.CParen)(parseFunDec _) - (tps -> funDec) - - case _ => - (Seq.empty[SSymbol], parseFunDec) - } - eat(Tokens.CParen) - (tps, funDec) - }) + case _ => + (Seq.empty[SSymbol], parseFunDef) + } - val (body, bodies) = parseOneOrMore(parseTerm _) - assert(funDecs.size == bodies.size) + val tpsLocals = locals.withGenerics(tps.map(s => s -> TypeParameter.fresh(s.name).setPos(s.getPos))) + val fdsLocals = if (!isRec) tpsLocals else { + tpsLocals.withFunction(funDef.name, extractSignature(funDef, tps)(tpsLocals)) + } + val fd = extractFunction(funDef, tps)(fdsLocals) + (None, locals.withFunction(funDef.name, fd)) + + case Tokens.Token(Tokens.DefineFunsRec) => + val (funDec, funDecs) = parseOneOrMore(() => { + eat(Tokens.OParen) + val (tps, funDec) = getPeekToken.kind match { + case Tokens.Par => + eat(Tokens.Par) + val tps = parseMany(parseSymbol _) + val funDec = parseWithin(Tokens.OParen, Tokens.CParen)(parseFunDec _) + (tps -> funDec) + + case _ => + (Seq.empty[SSymbol], parseFunDec) + } + eat(Tokens.CParen) + (tps, funDec) + }) - val funDefs = ((funDec -> body) +: (funDecs zip bodies)).map { - case ((tps, FunDec(name, params, returnSort)), body) => - tps -> SMTFunDef(name, params, returnSort, body) - } + val (body, bodies) = parseOneOrMore(parseTerm _) + assert(funDecs.size == bodies.size) - val bodyLocals = locals.withFunctions(for ((tps, funDef) <- funDefs) yield { - val tpsLocals = locals.withGenerics(tps.map(s => s -> TypeParameter.fresh(s.name).setPos(s.getPos))) - funDef.name -> extractSignature(funDef, tps)(tpsLocals) - }) + val funDefs = ((funDec -> body) +: (funDecs zip bodies)).map { + case ((tps, FunDec(name, params, returnSort)), body) => + tps -> SMTFunDef(name, params, returnSort, body) + } - locals = locals.withFunctions(for ((tps, funDef) <- funDefs) yield { - val tpsLocals = bodyLocals.withGenerics(tps.map(s => s -> TypeParameter.fresh(s.name).setPos(s.getPos))) - funDef.name -> extractFunction(funDef, tps)(tpsLocals) - }) + val bodyLocals = locals.withFunctions(for ((tps, funDef) <- funDefs) yield { + val tpsLocals = locals.withGenerics(tps.map(s => s -> TypeParameter.fresh(s.name).setPos(s.getPos))) + funDef.name -> extractSignature(funDef, tps)(tpsLocals) + }) + + (None, locals.withFunctions(for ((tps, funDef) <- funDefs) yield { + val tpsLocals = bodyLocals.withGenerics(tps.map(s => s -> TypeParameter.fresh(s.name).setPos(s.getPos))) + funDef.name -> extractFunction(funDef, tps)(tpsLocals) + })) + + case Tokens.Token(Tokens.DeclareDatatypes) => + val tps = parseMany(parseSymbol _) + val datatypes = parseMany(parseDatatypes _) + + var locs = locals.withADTs(datatypes + .flatMap { case (sym, conss) => sym +: conss.map(_.sym) } + .map(sym => sym -> getIdentifier(sym))) + + val adtLocals = locs.withGenerics(tps.map(s => s -> TypeParameter.fresh(s.name).setPos(s.getPos))) + for ((sym, conss) <- datatypes) { + val children = for (Constructor(sym, fields) <- conss) yield { + val id = locs.getADT(sym) + val vds = fields.map { case (s, sort) => + ValDef(getIdentifier(s), extractType(sort)(adtLocals)).setPos(s.getPos) + } - case Tokens.Token(Tokens.DeclareDatatypes) => - nextToken // eat the "declare-datatypes" token - val tps = parseMany(parseSymbol _) - val datatypes = parseMany(parseDatatypes _) + (id, vds) + } - locals = locals.withADTs(datatypes - .flatMap { case (sym, conss) => sym +: conss.map(_.sym) } - .map(sym => sym -> getIdentifier(sym))) + val allVds: Set[ValDef] = children.flatMap(_._2).toSet + val allTparams: Set[TypeParameter] = children.flatMap(_._2).toSet.flatMap { + (vd: ValDef) => locs.symbols.typeParamsOf(vd.tpe): Set[TypeParameter] + } - val adtLocals = locals.withGenerics(tps.map(s => s -> TypeParameter.fresh(s.name).setPos(s.getPos))) - for ((sym, conss) <- datatypes) { - val children = for (Constructor(sym, fields) <- conss) yield { - val id = locals.getADT(sym) - val vds = fields.map { case (s, sort) => - ValDef(getIdentifier(s), extractType(sort)(adtLocals)).setPos(s.getPos) - } + val tparams: Seq[TypeParameterDef] = tps.flatMap { sym => + val tp = adtLocals.getGeneric(sym) + if (allTparams(tp)) Some(TypeParameterDef(tp).setPos(sym.getPos)) else None + } - (id, vds) - } + val parent = if (children.size > 1) { + val id = adtLocals.getADT(sym) + locs = locs.registerADT( + new ADTSort(id, tparams, children.map(_._1), Set.empty).setPos(sym.getPos)) + Some(id) + } else { + None + } - val allVds: Set[ValDef] = children.flatMap(_._2).toSet - val allTparams: Set[TypeParameter] = children.flatMap(_._2).toSet.flatMap { - (vd: ValDef) => locals.symbols.typeParamsOf(vd.tpe): Set[TypeParameter] - } + locs = locals.registerADTs((conss zip children).map { case (cons, (cid, vds)) => + new ADTConstructor(cid, tparams, parent, vds, Set.empty).setPos(cons.sym.getPos) + }).withADTSelectors((conss zip children).flatMap { case (Constructor(_, fields), (_, vds)) => + (fields zip vds).map(p => p._1._1 -> p._2.id) + }) + } - val tparams: Seq[TypeParameterDef] = tps.flatMap { sym => - val tp = adtLocals.getGeneric(sym) - if (allTparams(tp)) Some(TypeParameterDef(tp).setPos(sym.getPos)) else None - } + (None, locs) + + case Tokens.Token(Tokens.DeclareConst) => + val sym = parseSymbol + val sort = parseSort + (None, locals.withVariable(sym, + Variable(getIdentifier(sym), extractType(sort)).setPos(sym.getPos))) + + case Tokens.Token(Tokens.DeclareSort) => + val sym = parseSymbol + val arity = parseNumeral.value.toInt + val id = getIdentifier(sym) + (None, locals.withADT(sym, id).registerADT { + val tparams = List.range(0, arity).map { + i => TypeParameterDef(TypeParameter.fresh("A" + i).setPos(sym.getPos)).setPos(sym.getPos) + } + val field = ValDef(FreshIdentifier("val"), IntegerType).setPos(sym.getPos) - val parent = if (children.size > 1) { - val id = adtLocals.getADT(sym) - locals = locals.registerADT( - new ADTSort(id, tparams, children.map(_._1), Set.empty).setPos(sym.getPos)) - Some(id) - } else { - None - } + new ADTConstructor(id, tparams, None, Seq(field), Set.empty) + }) - locals = locals.registerADTs((conss zip children).map { case (cons, (cid, vds)) => - new ADTConstructor(cid, tparams, parent, vds, Set.empty).setPos(cons.sym.getPos) - }).withADTSelectors((conss zip children).flatMap { case (Constructor(_, fields), (_, vds)) => - (fields zip vds).map(p => p._1._1 -> p._2.id) - }) - } + case Tokens.Token(Tokens.CheckSat) => + // TODO: what do I do with this?? + (None, locals) - case Tokens.Token(Tokens.DeclareConst) => - nextToken // eat the "declare-const" token - val sym = parseSymbol - val sort = parseSort - locals = locals.withVariable(sym, - Variable(getIdentifier(sym), extractType(sort)).setPos(sym.getPos)) - - case Tokens.Token(Tokens.DeclareSort) => - nextToken // eat the "declare-const" token - val sym = parseSymbol - val arity = parseNumeral.value.toInt - locals = if (arity == 0) { - locals.withGeneric(sym, TypeParameter.fresh(sym.name)) - } else { - val id = getIdentifier(sym) - locals.withADT(sym, id).registerADT { - val tparams = List.range(0, arity).map { - i => TypeParameterDef(TypeParameter.fresh("A" + i).setPos(sym.getPos)).setPos(sym.getPos) - } - val field = ValDef(FreshIdentifier("val"), IntegerType).setPos(sym.getPos) - - new ADTConstructor(id, tparams, None, Seq(field), Set.empty) - } - } + case token => + throw new MissformedTIPException("unknown TIP command " + token, token.getPos) + } - case Tokens.Token(Tokens.CheckSat) => - // TODO: what do I do with this?? + def parseTIPScript: (Symbols, Expr) = { - case token => - throw new MissformedTIPException("unknown TIP command " + token, token.getPos) - } + var assertions: Seq[Expr] = Seq.empty + implicit var locals: Locals = NoLocals + while (peekToken != null) { + eat(Tokens.OParen) + val (newAssertions, newLocals) = parseTIPCommand(nextToken) + assertions ++= newAssertions + locals = newLocals eat(Tokens.CParen) } val expr: Expr = locals.symbols.andJoin(assertions) - val program = InoxProgram(ctx, locals.symbols) - (program, expr) + (locals.symbols, expr) } override protected def parseTermWithoutParens: Term = getPeekToken match { @@ -372,7 +377,7 @@ trait TIPParser { } } - private def extractExpr(term: Term)(implicit locals: Locals): Expr = (term match { + protected def extractExpr(term: Term)(implicit locals: Locals): Expr = (term match { case FunctionApplication(QualifiedIdentifier(SimpleIdentifier(SSymbol("assume")), None), Seq(pred, body)) => Assume(extractExpr(pred), extractExpr(body)) @@ -594,7 +599,7 @@ trait TIPParser { }).setPos(term.getPos) - private def extractType(sort: Sort)(implicit locals: Locals): Type = (sort match { + protected def extractType(sort: Sort)(implicit locals: Locals): Type = (sort match { case Sort(SMTIdentifier(SSymbol("bitvector"), Seq(SNumeral(n))), Seq()) => BVType(n.toInt) case Sort(SimpleIdentifier(SSymbol("Bool")), Seq()) => BooleanType case Sort(SimpleIdentifier(SSymbol("Int")), Seq()) => IntegerType @@ -621,3 +626,13 @@ trait TIPParser { }).setPos(sort.id.symbol.getPos) } } + +object TIPParser { + def parse(file: File): (inox.trees.Symbols, inox.trees.Expr) = new TIPParser { + val trees: inox.trees.type = inox.trees + }.parse(file) + + def parse(reader: Reader): (inox.trees.Symbols, inox.trees.Expr) = new TIPParser { + val trees: inox.trees.type = inox.trees + }.parse(reader) +} diff --git a/src/main/scala/inox/solvers/smtlib/Z3Target.scala b/src/main/scala/inox/solvers/smtlib/Z3Target.scala index ccff47697..5d8e30703 100644 --- a/src/main/scala/inox/solvers/smtlib/Z3Target.scala +++ b/src/main/scala/inox/solvers/smtlib/Z3Target.scala @@ -66,9 +66,8 @@ trait Z3Target extends SMTLIBTarget { override protected def fromSMT(t: Term, otpe: Option[Type] = None) (implicit lets: Map[SSymbol, Term], letDefs: Map[SSymbol, DefineFun]): Expr = { (t, otpe) match { - case (QualifiedIdentifier(ExtendedIdentifier(SSymbol("as-array"), k: SSymbol), _), Some(tpe)) => + case (QualifiedIdentifier(ExtendedIdentifier(SSymbol("as-array"), k: SSymbol), _), Some(tpe @ MapType(keyType, valueType))) => if (letDefs contains k) { - val MapType(keyType, valueType) = tpe val DefineFun(SMTFunDef(a, Seq(SortedVar(arg, akind)), rkind, body)) = letDefs(k) def extractCases(e: Term): (Map[Expr, Expr], Expr) = e match { @@ -85,13 +84,39 @@ trait Z3Target extends SMTLIBTarget { throw FatalError("Array on non-function or unknown symbol "+k) } + case (QualifiedIdentifier(ExtendedIdentifier(SSymbol("as-array"), k: SSymbol), _), Some(tpe @ SetType(base))) => + val fm @ FiniteMap(cases, dflt, _) = fromSMT(t, Some(MapType(base, BooleanType))) + if (dflt != BooleanLiteral(false)) unsupported(fm, "Solver returned a co-finite set which is not supported") + FiniteSet(cases.collect { case (k, BooleanLiteral(true)) => k }, base) + + case (QualifiedIdentifier(ExtendedIdentifier(SSymbol("as-array"), k: SSymbol), _), Some(tpe @ BagType(base))) => + val fm @ FiniteMap(cases, dflt, _) = fromSMT(t, Some(MapType(base, IntegerType))) + if (dflt != IntegerLiteral(0)) unsupported(fm, "Solver returned a co-finite bag which is not supported") + FiniteBag(cases.filter(_._2 != IntegerLiteral(BigInt(0))), base) + case (FunctionApplication( QualifiedIdentifier(SMTIdentifier(SSymbol("const"), _), Some(ArraysEx.ArraySort(k, v))), Seq(defV) - ), Some(tpe)) => + ), Some(tpe: MapType)) => val ktpe = sorts.fromB(k) val vtpe = sorts.fromB(v) - FiniteMap(Seq(), fromSMT(defV), ktpe) + FiniteMap(Seq(), fromSMT(defV, Some(vtpe)), ktpe) + + case (FunctionApplication( + QualifiedIdentifier(SMTIdentifier(SSymbol("const"), _), Some(ArraysEx.ArraySort(k, v))), + Seq(defV) + ), Some(tpe @ SetType(base))) => + val dflt = fromSMT(defV, Some(BooleanType)) + if (dflt != BooleanLiteral(false)) unsupported(dflt, "Solver returned a co-finite set which is not supported") + FiniteSet(Seq.empty, base) + + case (FunctionApplication( + QualifiedIdentifier(SMTIdentifier(SSymbol("const"), _), Some(ArraysEx.ArraySort(k, v))), + Seq(defV) + ), Some(tpe @ BagType(base))) => + val dflt = fromSMT(defV, Some(IntegerType)) + if (dflt != IntegerLiteral(BigInt(0))) unsupported(dflt, "Solver returned a co-finite bag which is not supported") + FiniteBag(Seq.empty, base) case _ => super.fromSMT(t, otpe) -- GitLab