/******************************************************************************* * * Copyright (C) 2008 Fujitsu Services Ltd. * * Author: Nick Battle * * This file is part of VDMJ. * * VDMJ is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * VDMJ is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with VDMJ. If not, see <http://www.gnu.org/licenses/>. * ******************************************************************************/ package org.overture.interpreter.commands; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.overture.ast.analysis.AnalysisException; import org.overture.ast.expressions.PExp; import org.overture.ast.lex.Dialect; import org.overture.ast.lex.LexIdentifierToken; import org.overture.ast.lex.LexLocation; import org.overture.ast.lex.LexNameToken; import org.overture.ast.lex.LexToken; import org.overture.ast.lex.VDMToken; import org.overture.ast.statements.PStm; import org.overture.config.Settings; import org.overture.interpreter.VDMJ; import org.overture.interpreter.debug.BreakpointManager; import org.overture.interpreter.debug.DBGPReader; import org.overture.interpreter.messages.Console; import org.overture.interpreter.messages.rtlog.RTLogger; import org.overture.interpreter.runtime.Breakpoint; import org.overture.interpreter.runtime.ContextException; import org.overture.interpreter.runtime.DebuggerException; import org.overture.interpreter.runtime.Interpreter; import org.overture.interpreter.runtime.LatexSourceFile; import org.overture.interpreter.runtime.SourceFile; import org.overture.interpreter.traces.TraceReductionType; import org.overture.interpreter.util.ExitStatus; import org.overture.interpreter.values.BooleanValue; import org.overture.interpreter.values.FunctionValue; import org.overture.interpreter.values.OperationValue; import org.overture.interpreter.values.Value; import org.overture.parser.lex.LexTokenReader; import org.overture.parser.messages.VDMErrorsException; import org.overture.parser.syntax.ParserException; import org.overture.pog.obligation.ProofObligationList; import org.overture.pog.pub.IProofObligation; import org.overture.pog.pub.IProofObligationList; /** * A class to read and perform commands from standard input. */ abstract public class CommandReader { /** The interpreter to use for the execution of commands. */ protected final Interpreter interpreter; /** The prompt for the user. */ protected final String prompt; /** The degree of trace reduction. */ private float reduction = 1.0F; /** The type of trace reduction. */ private TraceReductionType reductionType = TraceReductionType.RANDOM; /** The IDE DBGPReader, if any */ private DBGPReader dbgp = null; private final boolean singlePass; /** * Create a command reader with the given interpreter and prompt. * * @param interpreter * The interpreter instance to use. * @param prompt * The user prompt. */ public CommandReader(Interpreter interpreter, String prompt) { this.interpreter = interpreter; this.prompt = prompt; this.singlePass = false; } /** * Create a command reader with the given interpreter and prompt. * * @param interpreter * The interpreter instance to use. * @param prompt * The user prompt. * @param singlePass * True if the reader should read more than one command */ public CommandReader(Interpreter interpreter, String prompt, boolean singlePass) { this.interpreter = interpreter; this.prompt = prompt; this.singlePass = singlePass; } /** * Read and execute commands from standard input. The prompt passed to the constructor is used to prompt the user, * and the interpreter passed is used to execute commands. Each command is directed through the corresponding "do" * method in this class, the defaults for which print that the command is not available from the current context. * Subclasses of CommandReader implement the "do" methods that apply to them. The only methods implemented at this * level are the ones which are globally applicable. * <p> * The "do" methods return a boolean which indicates whether the command reader should loop and read/dispatch more * commands, or exit. * * @param filenames * the files * @return */ public ExitStatus run(List<File> filenames) { String line = ""; String lastline = ""; boolean carryOn = true; long timestamp = System.currentTimeMillis(); while (carryOn) { for (File file : filenames) { if (file.lastModified() > timestamp) { println("File " + file + " has changed"); } } try { print(prompt); line = getStdin().readLine(); if (line == null) { carryOn = doQuit(""); continue; } if (line.equals(".")) { line = lastline; println(prompt + line); } if (line.equals("") || line.startsWith("--")) { continue; } lastline = line; if (line.equals("quit") || line.equals("q")) { carryOn = doQuit(line); } else if (line.startsWith("reload")) { carryOn = doReLoad(line); if (!carryOn) { return ExitStatus.RELOAD; } } else if (line.startsWith("load")) { carryOn = doLoad(line, filenames); if (!carryOn) { return ExitStatus.RELOAD; } } else if (line.startsWith("save")) { carryOn = doSave(line); } else if (line.equals("files")) { carryOn = doFiles(); } else if (line.startsWith("set")) { carryOn = doSet(line); } else if (line.equals("stop")) { carryOn = doStop(line); } else if (line.equals("help") || line.equals("?")) { doHelp(line); } else if (line.startsWith("assert")) { carryOn = doAssert(line); } else if (line.equals("continue") || line.equals("c")) { carryOn = doContinue(line); } else if (line.equals("stack")) { carryOn = doStack(line); } else if (line.equals("up")) { carryOn = doUp(line); } else if (line.equals("down")) { carryOn = doDown(line); } else if (line.equals("step") || line.equals("s")) { carryOn = doStep(line); } else if (line.equals("next") || line.equals("n")) { carryOn = doNext(line); } else if (line.equals("out") || line.equals("o")) { carryOn = doOut(line); } else if (line.startsWith("trace")) { carryOn = doTrace(line); } else if (line.startsWith("break")) { carryOn = doBreak(line); } else if (line.equals("list")) { carryOn = doList(line); } else if (line.equals("threads")) { carryOn = doThreads(line); } else if (line.equals("source")) { carryOn = doSource(line); } else if (line.startsWith("coverage")) { carryOn = doCoverage(line); } else if (line.startsWith("latexdoc")) { carryOn = doLatex(line, true); } else if (line.startsWith("latex")) { carryOn = doLatex(line, false); } else if (line.startsWith("word")) { carryOn = doWord(line); } else if (line.startsWith("remove")) { carryOn = doRemove(line); } else if (line.equals("init")) { carryOn = doInit(line); } else if (line.equals("env")) { carryOn = doEnv(line); } else if (line.equals("state")) { carryOn = doState(line); } else if (line.startsWith("default")) { carryOn = doDefault(line); } else if (line.equals("classes")) { carryOn = doClasses(line); } else if (line.startsWith("create")) { carryOn = doCreate(line); } else if (line.equals("modules")) { carryOn = doModules(line); } else if (line.startsWith("pog")) { carryOn = doPog(line); } else if (line.startsWith("log")) { carryOn = doLog(line); } else if (line.startsWith("print ") || line.startsWith("p ")) { carryOn = doEvaluate(line); } else if (line.startsWith("runtrace ")) { carryOn = doRuntrace(line, false); } else if (line.startsWith("debugtrace ")) { carryOn = doRuntrace(line, true); } else if (line.startsWith("filter")) { carryOn = doFilter(line); } else { println("Bad command. Try 'help'"); } } catch (Exception e) { carryOn = doException(e); } if (singlePass) { break; } } return ExitStatus.EXIT_OK; } protected PrintWriter getStdout() { return Console.out; } protected BufferedReader getStdin() { return Console.in; } public void setDebugReader(DBGPReader dbgp) { this.dbgp = dbgp; } protected boolean doException(Exception e) { println("Exception: " + e.getMessage()); return true; } protected boolean doEvaluate(String line) { line = line.substring(line.indexOf(' ') + 1); try { long before = System.currentTimeMillis(); println("= " + interpreter.execute(line, dbgp)); long after = System.currentTimeMillis(); println("Executed in " + (double) (after - before) / 1000 + " secs. "); if (RTLogger.getLogSize() > 0) { println("Dumped RT events"); RTLogger.dump(false); } } catch (ParserException e) { println("Syntax: " + e.getMessage()); } catch (DebuggerException e) { println("Debug: " + e.getMessage()); } catch (RuntimeException e) { println("Runtime: " + e); } catch (VDMErrorsException e) { println(e.toString()); } catch (Exception e) { println("Error: " + e.getMessage()); } return true; } protected boolean doFilter(String line) { String[] parts = line.split("\\s+"); if (parts.length != 2) { println("Usage: filter %age | RANDOM | SHAPES_NOVARS | SHAPES_VARNAMES | SHAPES_VARVALUES"); } else { try { reduction = Float.parseFloat(parts[1]) / 100.0F; if (reduction > 1 || reduction < 0) { println("Usage: filter %age (1-100)"); } } catch (NumberFormatException e) { try { reductionType = TraceReductionType.valueOf(parts[1].toUpperCase()); } catch (Exception e1) { println("Usage: filter %age | RANDOM | SHAPES_NOVARS | SHAPES_VARNAMES | SHAPES_VARVALUES"); } } } println("Trace filter currently " + reduction * 100 + "% " + reductionType); return true; } protected boolean doRuntrace(String line, boolean debug) { String[] parts = line.split("\\s+"); int testNo = 0; if (parts.length == 3) { try { testNo = Integer.parseInt(parts[2]); } catch (NumberFormatException e) { println(parts[0] + " <name> [test number]"); return true; } } line = parts[1]; try { long before = System.currentTimeMillis(); boolean passed = interpreter.runtrace(line, testNo, debug, reduction, reductionType, 0); long after = System.currentTimeMillis(); println("Executed in " + (double) (after - before) / 1000 + " secs. "); if (passed) { println("All tests passed"); } else { println("Some tests failed or indeterminate"); } if (RTLogger.getLogSize() > 0) { println("Dumped RT events"); RTLogger.dump(false); } } catch (ParserException e) { println("Syntax: " + e.getMessage()); } catch (DebuggerException e) { println("Debug: " + e.getMessage()); } catch (RuntimeException e) { println("Runtime: " + e); } catch (VDMErrorsException e) { println(e.toString()); } catch (Exception e) { println("Error: " + e.getMessage()); } return true; } protected boolean doQuit(String line) { if (RTLogger.getLogSize() > 0) { println("Dumping RT events"); RTLogger.dump(true); } return false; } protected boolean doStop(String line) { return notAvailable(line); } protected boolean doModules(String line) { return notAvailable(line); } protected boolean doClasses(String line) { return notAvailable(line); } protected boolean doFiles() { Set<File> filenames = interpreter.getSourceFiles(); for (File file : filenames) { println(file.getPath()); } return true; } private void isEnabled(String name, boolean flag) { print(name); println(flag ? " are enabled" : " are disabled"); } protected boolean doSet(String line) { if (line.equals("set")) { isEnabled("Preconditions", Settings.prechecks); isEnabled("Postconditions", Settings.postchecks); isEnabled("Invariants", Settings.invchecks); isEnabled("Dynamic type checks", Settings.dynamictypechecks); isEnabled("Measure checks", Settings.measureChecks); } else { String[] parts = line.split("\\s+"); if (parts.length == 3 && (parts[2].equalsIgnoreCase("on") || parts[2].equalsIgnoreCase("off"))) { boolean setting = parts[2].equalsIgnoreCase("on"); if (parts[1].equals("pre")) { Settings.prechecks = setting; } else if (parts[1].equals("post")) { Settings.postchecks = setting; } else if (parts[1].equals("inv")) { Settings.invchecks = setting; } else if (parts[1].equals("dtc")) { // NB. Do both Settings.invchecks = setting; Settings.dynamictypechecks = setting; } else if (parts[1].equals("measures")) { Settings.measureChecks = setting; } else { println("Usage: set [<pre|post|inv|dtc|measures> <on|off>]"); } } else { println("Usage: set [<pre|post|inv|dtc|measures> <on|off>]"); } } return true; } protected boolean doPog(String line) throws AnalysisException { IProofObligationList all = interpreter.getProofObligations(); IProofObligationList list = null; if (line.equals("pog")) { list = all; } else { Pattern p1 = Pattern.compile("^pog (\\w+)$"); Matcher m = p1.matcher(line); if (m.matches()) { list = new ProofObligationList(); String name = m.group(1) + (Settings.dialect == Dialect.VDM_SL ? "" : "("); for (IProofObligation po : all) { if (po.getName().indexOf(name) >= 0) { list.add(po); } } } else { println("Usage: pog [<fn/op name>]"); return true; } } if (list.isEmpty()) { println("No proof obligations generated"); } else { println("Generated " + plural(list.size(), "proof obligation", "s") + ":\n"); print(list.toString()); } return true; } protected boolean doLog(String line) { return notAvailable(line); } /** * @throws Exception */ protected boolean doCreate(String line) throws Exception { return notAvailable(line); } /** * @throws Exception */ protected boolean doDefault(String line) throws Exception { return notAvailable(line); } protected boolean doInit(String line) { LexLocation.clearLocations(); println("Cleared all coverage information"); interpreter.init(dbgp); println("Global context initialized"); return true; } protected boolean doEnv(String line) { print(interpreter.getInitialContext()); return true; } protected boolean doState(String line) { return notAvailable(line); } protected boolean doRemove(String line) { String parts[] = line.split("\\s+"); if (parts.length != 2) { println("Usage: remove <breakpoint#>"); return true; } int bpno = Integer.parseInt(parts[1]); Breakpoint old = interpreter.clearBreakpoint(bpno); if (old != null) { println("Cleared " + old); println(interpreter.getSourceLine(old.location)); } else { println("Breakpoint [" + bpno + "] not set"); } return true; } protected boolean doList(String line) { Map<Integer, Breakpoint> map = interpreter.getBreakpoints(); for (Entry<Integer, Breakpoint> entry : map.entrySet()) { Breakpoint bp = entry.getValue(); println(bp.toString()); println(interpreter.getSourceLine(bp.location)); } return true; } protected boolean doSource(String line) { return notAvailable(line); } protected boolean doCoverage(String line) { try { Set<File> loaded = interpreter.getSourceFiles(); if (line.equals("coverage")) { for (File file : interpreter.getSourceFiles()) { doCoverage(file); } return true; } String[] parts = line.split("\\s+"); if (parts.length == 2 && parts[1].equals("clear")) { LexLocation.clearLocations(); println("Cleared all coverage information"); return true; } if (parts.length == 3 && parts[1].equals("write")) { writeCoverage(new File(parts[2])); return true; } if (parts.length == 3 && parts[1].equals("merge")) { mergeCoverage(new File(parts[2])); return true; } for (int p = 1; p < parts.length; p++) { File f = new File(parts[p]); if (loaded.contains(f)) { doCoverage(f); } else { println(f + " is not loaded - try 'files'"); } } } catch (Exception e) { println("Usage: coverage clear|write <dir>|merge <dir>|<filenames>"); } return true; } protected boolean doCoverage(File file) { try { SourceFile source = interpreter.getSourceFile(file); if (source == null) { println(file + ": file not found"); } else { source.printCoverage(getStdout()); } } catch (Exception e) { println("coverage: " + e.getMessage()); } return true; } protected boolean doWord(String line) { try { Set<File> loaded = interpreter.getSourceFiles(); if (line.equals("word")) { for (File file : interpreter.getSourceFiles()) { doWord(file); } return true; } String[] parts = line.split("\\s+"); for (int p = 1; p < parts.length; p++) { File f = new File(parts[p]); if (loaded.contains(f)) { doWord(f); } else { println(f + " is not loaded - try 'files'"); } } } catch (Exception e) { println("Usage: word [<filenames>]"); } return true; } protected void doWord(File file) { try { SourceFile source = interpreter.getSourceFile(file); if (source == null) { println(file + ": file not found"); } else { File html = new File(source.filename.getPath() + ".doc"); PrintWriter pw = new PrintWriter(html, "UTF-8"); source.printWordCoverage(pw); pw.close(); println("Word HTML coverage written to " + html); } } catch (Exception e) { println("word: " + e.getMessage()); } } protected boolean doSave(String line) { try { Set<File> loaded = interpreter.getSourceFiles(); if (line.equals("save")) { for (File file : interpreter.getSourceFiles()) { doSave(file); } return true; } String[] parts = line.split("\\s+"); for (int p = 1; p < parts.length; p++) { File f = new File(parts[p]); if (loaded.contains(f)) { doSave(f); } else { println(f + " is not loaded - try 'files'"); } } } catch (Exception e) { println("Usage: save [<filenames>]"); } return true; } protected void doSave(File file) { try { String name = file.getName().toLowerCase(); if (name.endsWith(".doc") || name.endsWith(".docx") || name.endsWith(".odt")) { SourceFile source = interpreter.getSourceFile(file); if (source == null) { println(file + ": file not found"); } else { File vdm = new File(source.filename.getPath() + "." + Settings.dialect.getArgstring().substring(1)); PrintWriter spw = new PrintWriter(vdm, "UTF-8"); source.printSource(spw); spw.close(); println("Extracted source written to " + vdm); } } else { println("Not a Word or ODF file: " + file); } } catch (Exception e) { println("save: " + e.getMessage()); } } protected boolean doLatex(String line, boolean headers) { try { Set<File> loaded = interpreter.getSourceFiles(); if (line.equals("latex") || line.equals("latexdoc")) { for (File file : interpreter.getSourceFiles()) { doLatex(file, headers); } return true; } String[] parts = line.split("\\s+"); for (int p = 1; p < parts.length; p++) { File f = new File(parts[p]); if (loaded.contains(f)) { doLatex(f, headers); } else { println(f + " is not loaded - try 'files'"); } } } catch (Exception e) { println("Usage: latex|latexdoc <filenames>"); } return true; } protected boolean doLatex(File file, boolean headers) { try { SourceFile source = interpreter.getSourceFile(file); if (source == null) { println(file + ": file not found"); } else { File tex = new File(source.filename.getPath() + ".tex"); PrintWriter pw = new PrintWriter(tex, "UTF-8"); new LatexSourceFile(source).printCoverage(pw, headers); pw.close(); println("Latex coverage written to " + tex); } } catch (Exception e) { println("latex: " + e.getMessage()); } return true; } protected boolean doBreak(String line) throws Exception { Pattern p1 = Pattern.compile("^break ([\\w._/\\\\]++)?:?(\\d+) ?(.+)?$"); Matcher m = p1.matcher(line); if (m.matches()) { String g1 = m.group(1); File file = g1 == null ? null : new File(g1); setBreakpoint(file, Integer.parseInt(m.group(2)), m.group(3)); } else { Pattern p2 = Pattern.compile("^break ([\\w`$%']+) ?(.+)?$"); m = p2.matcher(line); if (m.matches()) { setBreakpoint(m.group(1), m.group(2)); } else { println("Usage: break [<file>:]<lineno> [<condition>]"); println(" or: break <function/operation> [<condition>]"); } } return true; } protected boolean doTrace(String line) throws Exception { Pattern p1 = Pattern.compile("^trace ([\\w._/\\\\]++)?:?(\\d+) ?(.+)?$"); Matcher m = p1.matcher(line); if (m.matches()) { String g1 = m.group(1); File file = g1 == null ? null : new File(g1); setTracepoint(file, Integer.parseInt(m.group(2)), m.group(3)); } else { Pattern p2 = Pattern.compile("^trace ([\\w`$%']+) ?(.+)?$"); m = p2.matcher(line); if (m.matches()) { setTracepoint(m.group(1), m.group(2)); } else { println("Usage: trace [<file>:]<lineno> [<expression>]"); println(" or: trace <function/operation> [<expression>]"); } } return true; } protected boolean doStep(String line) { return notAvailable(line); } protected boolean doNext(String line) { return notAvailable(line); } protected boolean doOut(String line) { return notAvailable(line); } protected boolean doStack(String line) { return notAvailable(line); } protected boolean doUp(String line) { return notAvailable(line); } protected boolean doDown(String line) { return notAvailable(line); } protected boolean doContinue(String line) { return notAvailable(line); } protected boolean doThreads(String line) { String threads = interpreter.scheduler.getStatus(); if (threads.isEmpty()) { println("No threads running"); } else { println(threads); } return true; } protected boolean doAssert(String line) { File filename = null; try { String[] parts = line.split("\\s+"); filename = new File(parts[1]); if (!filename.canRead()) { println("File '" + filename + "' not accessible"); return true; } } catch (Exception e) { println("Usage: assert <filename>"); return true; } assertFile(filename); return true; } protected boolean doReLoad(String line) { if (!line.equals("reload")) { println("Usage: reload"); return true; } return false; } protected boolean doLoad(String line, List<File> filenames) { if (line.indexOf(' ') < 0) { println("Usage: load <files or dirs>"); return true; } filenames.clear(); for (String s : line.split("\\s+")) { File dir = new File(s); if (dir.isDirectory()) { for (File file : dir.listFiles(Settings.dialect.getFilter())) { if (file.isFile()) { filenames.add(file); } } } else { filenames.add(dir); } } filenames.remove(0); // which is "load" :-) return false; } public boolean assertFile(File filename) { BufferedReader input = null; try { input = new BufferedReader(new InputStreamReader(new FileInputStream(filename), VDMJ.filecharset)); } catch (FileNotFoundException e) { println("File '" + filename + "' not found"); return false; } catch (UnsupportedEncodingException e) { println("File encoding exception: " + e); return false; } int assertErrors = 0; int assertPasses = 0; while (true) { String assertion = null; try { assertion = input.readLine(); if (assertion == null) { break; // EOF } if (assertion.equals("") || assertion.startsWith("--")) { continue; } Value result = interpreter.execute(assertion, dbgp); if (!(result instanceof BooleanValue) || !result.boolValue(null)) { println("FAILED: " + assertion); assertErrors++; } else { assertPasses++; } } catch (ParserException e) { println("FAILED: " + assertion); println("Syntax: " + e); assertErrors++; break; } catch (ContextException e) { println("FAILED: " + assertion); println("Runtime: " + e.getMessage()); e.ctxt.printStackTrace(getStdout(), true); assertErrors++; break; } catch (RuntimeException e) { println("FAILED: " + assertion); println("Runtime: " + e.getMessage()); assertErrors++; break; } catch (Exception e) { println("FAILED: " + assertion); println("Exception: " + e); assertErrors++; break; } } if (assertErrors == 0) { println("PASSED all " + assertPasses + " assertions from " + filename); } else { println("FAILED " + assertErrors + " and passed " + assertPasses + " assertions from " + filename); } try { input.close(); } catch (Exception e) {/* */ } return assertErrors == 0; } protected void doHelp(String line) { println("print <expression> - evaluate expression"); println("runtrace <name> [test number] - run CT trace(s)"); println("debugtrace <name> [test number] - debug CT trace(s)"); println("filter %age | <reduction type> - reduce CT trace(s)"); println("assert <file> - run assertions from a file"); println("init - re-initialize the global environment"); println("env - list the global symbols in the default environment"); println("pog [<function/operation>] - generate proof obligations"); println("break [<file>:]<line#> [<condition>] - create a breakpoint"); println("break <function/operation> [<condition>] - create a breakpoint"); println("trace [<file>:]<line#> [<exp>] - create a tracepoint"); println("trace <function/operation> [<exp>] - create a tracepoint"); println("remove <breakpoint#> - remove a trace/breakpoint"); println("list - list breakpoints"); println("coverage clear|write <dir>|merge <dir>|<filenames> - handle line coverage"); println("latex|latexdoc [<files>] - generate LaTeX line coverage files"); println("word [<files>] - generate Word HTML line coverage files"); println("files - list files in the current specification"); println("set [<pre|post|inv|dtc|measures> <on|off>] - set runtime checks"); println("reload - reload the current specification files"); println("load <files or dirs> - replace current loaded specification files"); println("save [<files>] - generate Word/ODF source extract files"); println("quit - leave the interpreter"); } /** * Callable by "do" methods which want to make the command unavailable. The method just prints * "Command not available in this context" and returns true. */ protected boolean notAvailable(String line) { println("Command not available in this context"); return true; } /** * Set a breakpoint at the given file and line with a condition. * * @param file * The file name * @param line * The line number * @param condition * Any condition for the breakpoint, or null * @throws Exception * Problems parsing condition. */ private void setBreakpoint(File file, int line, String condition) throws Exception { if (file == null) { file = interpreter.getDefaultFile(); } if (file == null || file.getPath().equals("?")) { Set<File> files = interpreter.getSourceFiles(); if (files.size() > 1) { println("Assuming file " + file.getPath()); } else if (files.isEmpty()) { println("No files defined"); return; } file = files.iterator().next(); } PStm stmt = interpreter.findStatement(file, line); if (stmt == null) { PExp exp = interpreter.findExpression(file, line); if (exp == null) { println("No breakable expressions or statements at " + file + ":" + line); } else { interpreter.clearBreakpoint(BreakpointManager.getBreakpoint(exp).number); Breakpoint bp = interpreter.setBreakpoint(exp, condition); println("Created " + bp); println(interpreter.getSourceLine(bp.location)); } } else { interpreter.clearBreakpoint(BreakpointManager.getBreakpoint(stmt).number); Breakpoint bp = interpreter.setBreakpoint(stmt, condition); println("Created " + bp); println(interpreter.getSourceLine(bp.location)); } } /** * Set a breakpoint at the given function or operation name with a condition. * * @param name * The function or operation name. * @param condition * Any condition for the breakpoint, or null. * @throws Exception * Problems parsing condition. */ private void setBreakpoint(String name, String condition) throws Exception { LexTokenReader ltr = new LexTokenReader(name, Dialect.VDM_SL); LexToken token = ltr.nextToken(); ltr.close(); Value v = null; if (token.is(VDMToken.IDENTIFIER)) { LexIdentifierToken id = (LexIdentifierToken) token; LexNameToken lnt = new LexNameToken(interpreter.getDefaultName(), id); v = interpreter.findGlobal(lnt); } else if (token.is(VDMToken.NAME)) { v = interpreter.findGlobal((LexNameToken) token); } if (v instanceof FunctionValue) { FunctionValue fv = (FunctionValue) v; PExp exp = fv.body; interpreter.clearBreakpoint(BreakpointManager.getBreakpoint(exp).number); Breakpoint bp = interpreter.setBreakpoint(exp, condition); println("Created " + bp); println(interpreter.getSourceLine(bp.location)); } else if (v instanceof OperationValue) { OperationValue ov = (OperationValue) v; PStm stmt = ov.body; interpreter.clearBreakpoint(BreakpointManager.getBreakpoint(stmt).number); Breakpoint bp = interpreter.setBreakpoint(stmt, condition); println("Created " + bp); println(interpreter.getSourceLine(bp.location)); } else if (v == null) { println(name + " is not visible or not found"); } else { println(name + " is not a function or operation"); } } /** * Set a tracepoint at the given file and line. Tracepoints without a condition just print "Reached [n]", where [n] * is the breakpoint number. * * @param file * The file name * @param line * The line number * @param trace * Any expression to evaluate at the tracepoint, or null * @throws Exception * Problems parsing condition. */ private void setTracepoint(File file, int line, String trace) throws Exception { if (file == null) { file = interpreter.getDefaultFile(); } if (file == null || file.getPath().equals("?")) { Set<File> files = interpreter.getSourceFiles(); if (files.size() > 1) { println("Assuming file " + file.getPath()); } else if (files.isEmpty()) { println("No files defined"); return; } file = files.iterator().next(); } PStm stmt = interpreter.findStatement(file, line); if (stmt == null) { PExp exp = interpreter.findExpression(file, line); if (exp == null) { println("No breakable expressions or statements at " + file + ":" + line); } else { interpreter.clearBreakpoint(BreakpointManager.getBreakpoint(exp).number); Breakpoint bp = interpreter.setTracepoint(exp, trace); println("Created " + bp); println(interpreter.getSourceLine(bp.location)); } } else { interpreter.clearBreakpoint(BreakpointManager.getBreakpoint(stmt).number); Breakpoint bp = interpreter.setTracepoint(stmt, trace); println("Created " + bp); println(interpreter.getSourceLine(bp.location)); } } /** * Set a tracepoint at the given function or operation name. Tracepoints without a condition just print * "Reached [n]", where [n] is the breakpoint number. * * @param name * The function or operation name. * @param trace * Any trace for the tracepoint * @throws Exception * Problems parsing condition. */ private void setTracepoint(String name, String trace) throws Exception { LexTokenReader ltr = new LexTokenReader(name, Dialect.VDM_SL); LexToken token = ltr.nextToken(); ltr.close(); Value v = null; if (token.is(VDMToken.IDENTIFIER)) { LexIdentifierToken id = (LexIdentifierToken) token; LexNameToken lnt = new LexNameToken(interpreter.getDefaultName(), id); v = interpreter.findGlobal(lnt); } else if (token.is(VDMToken.NAME)) { v = interpreter.findGlobal((LexNameToken) token); } if (v instanceof FunctionValue) { FunctionValue fv = (FunctionValue) v; PExp exp = fv.body; interpreter.clearBreakpoint(BreakpointManager.getBreakpoint(exp).number); Breakpoint bp = interpreter.setTracepoint(exp, trace); println("Created " + bp); println(interpreter.getSourceLine(bp.location)); } else if (v instanceof OperationValue) { OperationValue ov = (OperationValue) v; PStm stmt = ov.body; interpreter.clearBreakpoint(BreakpointManager.getBreakpoint(stmt).number); Breakpoint bp = interpreter.setTracepoint(stmt, trace); println("Created " + bp); println(interpreter.getSourceLine(bp.location)); } else { println(name + " is not a function or operation"); } } protected void print(String m) { getStdout().print(m); getStdout().flush(); // As it's not a complete line } protected void println(String m) { getStdout().println(m); } protected String plural(int n, String s, String pl) { return n + " " + (n != 1 ? s + pl : s); } private void writeCoverage(File dir) throws IOException { for (File f : interpreter.getSourceFiles()) { SourceFile source = interpreter.getSourceFile(f); File cov = new File(dir.getPath() + File.separator + f.getName() + ".cov"); PrintWriter pw = new PrintWriter(cov); source.writeCoverage(pw); pw.close(); println("Written coverage for " + f); } } private void mergeCoverage(File dir) throws IOException { for (File f : interpreter.getSourceFiles()) { File cov = new File(dir.getPath() + File.separator + f.getName() + ".cov"); LexLocation.mergeHits(f, cov); println("Merged coverage for " + f); } } }