/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package edu.mit.csail.sdg.alloy4whole;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import edu.mit.csail.sdg.alloy4.A4Reporter;
import edu.mit.csail.sdg.alloy4.ErrorWarning;
import edu.mit.csail.sdg.alloy4.Pair;
import edu.mit.csail.sdg.alloy4.Util;
import edu.mit.csail.sdg.alloy4.Version;
import edu.mit.csail.sdg.alloy4.XMLNode;
import edu.mit.csail.sdg.alloy4compiler.ast.Command;
import edu.mit.csail.sdg.alloy4compiler.ast.Decl;
import edu.mit.csail.sdg.alloy4compiler.ast.Expr;
import edu.mit.csail.sdg.alloy4compiler.ast.ExprHasName;
import edu.mit.csail.sdg.alloy4compiler.ast.Func;
import edu.mit.csail.sdg.alloy4compiler.ast.Sig;
import edu.mit.csail.sdg.alloy4compiler.ast.Module;
import edu.mit.csail.sdg.alloy4compiler.parser.CompUtil;
import edu.mit.csail.sdg.alloy4compiler.translator.A4Options;
import edu.mit.csail.sdg.alloy4compiler.translator.A4Solution;
import edu.mit.csail.sdg.alloy4compiler.translator.A4SolutionReader;
import edu.mit.csail.sdg.alloy4compiler.translator.A4SolutionWriter;
import edu.mit.csail.sdg.alloy4compiler.translator.TranslateAlloyToKodkod;
import edu.mit.csail.sdg.alloy4compiler.translator.A4Options.SatSolver;
import edu.mit.csail.sdg.alloy4viz.StaticInstanceReader;
/** This class is used by the Alloy developers to drive the regression test suite.
* For a more detailed guide on how to use Alloy API, please see "ExampleUsingTheCompiler.java"
*/
public final class SimpleCLI {
private static final class SimpleReporter extends A4Reporter {
private final StringBuilder sb = new StringBuilder();
private final List<ErrorWarning> warnings = new ArrayList<ErrorWarning>();
private final RandomAccessFile os;
public SimpleReporter() throws IOException {
os = new RandomAccessFile(".alloy.tmp","rw");
os.setLength(0);
}
public void flush() throws IOException {
if (sb.length()>65536) {
os.write(sb.toString().getBytes("UTF-8"));
sb.delete(0, sb.length());
}
}
public void close() throws IOException {
if (sb.length()>0) {
os.write(sb.toString().getBytes("UTF-8"));
sb.delete(0, sb.length());
}
os.close();
}
@Override public void debug(String msg) { sb.append(msg); }
@Override public void parse(String msg) { sb.append(msg); }
@Override public void typecheck(String msg) { sb.append(msg); }
@Override public void warning(ErrorWarning msg) { warnings.add(msg); }
@Override public void scope(String msg) { sb.append(" "); sb.append(msg); }
@Override public void bound(String msg) { sb.append(" "); sb.append(msg); }
@Override public void translate(String solver, int bitwidth, int maxseq, int skolemDepth, int symmetry) {
sb.append(" Solver="+solver+" Bitwidth="+bitwidth+" MaxSeq="+maxseq+" Symmetry="+(symmetry>0 ? (""+symmetry) : "OFF")+"\n");
}
@Override public void solve(int primaryVars, int totalVars, int clauses) {
if (db) db(" "+totalVars+" vars. "+primaryVars+" primary vars. "+clauses+" clauses.\n");
sb.append(" "+totalVars+" vars. "+primaryVars+" primary vars. "+clauses+" clauses. 12345ms.\n");
}
@Override public void resultCNF(String filename) {}
@Override public void resultSAT(Object command, long solvingTime, Object solution) {
if (db) db(" SAT!\n");
if (!(command instanceof Command)) return;
Command cmd = (Command)command;
sb.append(cmd.check ? " Counterexample found. " : " Instance found. ");
if (cmd.check) sb.append("Assertion is invalid"); else sb.append("Predicate is consistent");
if (cmd.expects==0) sb.append(", contrary to expectation"); else if (cmd.expects==1) sb.append(", as expected");
sb.append(". "+solvingTime+"ms.\n\n");
}
@Override public void resultUNSAT(Object command, long solvingTime, Object solution) {
if (db) db(" UNSAT!\n");
if (!(command instanceof Command)) return;
Command cmd = (Command)command;
sb.append(cmd.check ? " No counterexample found." : " No instance found.");
if (cmd.check) sb.append(" Assertion may be valid"); else sb.append(" Predicate may be inconsistent");
if (cmd.expects==1) sb.append(", contrary to expectation"); else if (cmd.expects==0) sb.append(", as expected");
sb.append(". "+solvingTime+"ms.\n\n");
}
}
private static boolean db=true;
private static void db(String msg) { System.out.print(msg); System.out.flush(); }
private SimpleCLI() { }
private static void validate(A4Solution sol) throws Exception {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
sol.writeXML(pw, null, null);
pw.flush();
sw.flush();
String txt = sw.toString();
A4SolutionReader.read(new ArrayList<Sig>(), new XMLNode(new StringReader(txt))).toString();
StaticInstanceReader.parseInstance(new StringReader(txt));
}
public static void main(String[] args) throws Exception {
final boolean sat4j = "yes".equals(System.getProperty("sat4j"));
final boolean minisat = "yes".equals(System.getProperty("minisat"));
SatSolver solver = A4Options.SatSolver.make("mem", "mem", "/zweb/sat/mem");
final SimpleReporter rep = new SimpleReporter();
final StringBuilder sb = rep.sb;
for(String filename:args) {
try {
// Parse+Typecheck
rep.sb.append("\n\nMain file = "+filename+"\n");
if (db) db("Parsing+Typechecking...");
Module world = CompUtil.parseEverything_fromFile(rep, null, filename);
if (db) db(" ok\n");
List<Command> cmds=world.getAllCommands();
for(ErrorWarning msg: rep.warnings) rep.sb.append("Relevance Warning:\n" + (msg.toString().trim()) + "\n\n");
rep.warnings.clear();
// Do a detailed dump if we will not be executing the commands
if (args.length!=1) {
for(Module m:world.getAllReachableModules()) {
for(Sig x:m.getAllSigs()) {
sb.append("\nSig ").append(x.label).append(" at position ").append(x.pos).append("\n");
for(Decl d:x.getFieldDecls()) for(ExprHasName f:d.names) {
sb.append("\nField ").append(f.label).append(" with type ").append(f.type()).append("\n");
d.expr.toString(sb, 2);
}
rep.flush();
}
for(Func x:m.getAllFunc()) {
sb.append("\nFun/pred ").append(x.label).append(" at position ").append(x.pos).append("\n");
for(Decl d:x.decls) for(ExprHasName v:d.names) { v.toString(sb, 2); d.expr.toString(sb, 4); }
x.returnDecl.toString(sb, 2);
x.getBody().toString(sb, 4);
rep.flush();
}
for(Pair<String,Expr> x:m.getAllFacts()) {
sb.append("\nFact ").append(x.a).append("\n"); x.b.toString(sb, 4);
rep.flush();
}
for(Pair<String,Expr> x:m.getAllAssertions()) {
sb.append("\nAssertion ").append(x.a).append("\n"); x.b.toString(sb, 4);
rep.flush();
}
if (m==world) for(Command x:m.getAllCommands()) {
sb.append("\nCommand ").append(x.label).append("\n");
x.formula.toString(sb, 4);
rep.flush();
}
}
continue;
}
// Validate the metamodel generation code
StringWriter metasb = new StringWriter();
PrintWriter meta = new PrintWriter(metasb);
Util.encodeXMLs(meta, "\n<alloy builddate=\"", Version.buildDate(), "\">\n\n");
A4SolutionWriter.writeMetamodel(world.getAllReachableSigs(), filename, meta);
Util.encodeXMLs(meta, "\n</alloy>");
meta.flush();
metasb.flush();
String metaxml = metasb.toString();
A4SolutionReader.read(new ArrayList<Sig>(), new XMLNode(new StringReader(metaxml)));
StaticInstanceReader.parseInstance(new StringReader(metaxml));
// Okay, now solve the commands
A4Options options = new A4Options();
options.originalFilename = filename;
options.solverDirectory = "/zweb/zweb/tmp/alloy4/x86-freebsd";
options.solver = sat4j ? A4Options.SatSolver.SAT4J : (minisat ? A4Options.SatSolver.MiniSatJNI : solver);
for (int i=0; i<cmds.size(); i++) {
Command c = cmds.get(i);
if (db) {
String cc = c.toString();
if (cc.length()>60) cc=cc.substring(0,55);
db("Executing "+cc+"...\n");
}
rep.sb.append("Executing \""+c+"\"\n");
options.skolemDepth=0;
A4Solution s = TranslateAlloyToKodkod.execute_commandFromBook(rep, world.getAllReachableSigs(), c, options);
if (s.satisfiable()) { validate(s); if (s.isIncremental()) { s=s.next(); if (s.satisfiable()) validate(s); } }
options.skolemDepth=2;
s = TranslateAlloyToKodkod.execute_commandFromBook(rep, world.getAllReachableSigs(), c, options);
if (s.satisfiable()) { validate(s); if (s.isIncremental()) { s=s.next(); if (s.satisfiable()) validate(s); } }
}
} catch(Throwable ex) {
rep.sb.append("\n\nException: "+ex);
}
if (db) { if (args.length!=1) db(" ERROR!\n"); else db("\n\n"); }
}
rep.close();
}
}