//============================================================================== // // Copyright (c) 2002- // Authors: // * Dave Parker (University of Oxford, formerly University of Birmingham) // //------------------------------------------------------------------------------ // // This file is part of PRISM. // // PRISM 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 2 of the License, or // (at your option) any later version. // // PRISM 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 PRISM; if not, write to the Free Software Foundation, // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // //============================================================================== options { LOOKAHEAD = 1; UNICODE_INPUT = true; } PARSER_BEGIN(PrismParser) package parser; import java.io.*; import java.util.List; import java.util.ArrayList; import jltl2ba.SimpleLTL; import parser.BooleanUtils; import parser.ast.*; import parser.type.*; import prism.ModelInfo; import parser.visitor.*; import prism.ModelType; import prism.PrismLangException; @SuppressWarnings({"unused", "static-access", "serial"}) public class PrismParser { // The model associated with the properties file being parsed private static ModelInfo modelInfo; // List of keyword strings private static ArrayList keywordList = new ArrayList(); static { keywordList.clear(); for (int i = PrismParserConstants.COMMENT+1; i < PrismParserConstants.NOT; i++) { keywordList.add(PrismParserConstants.tokenImage[i].replaceAll("\"", "")); } } //----------------------------------------------------------------------------------- // Main method for testing purposes //----------------------------------------------------------------------------------- public static void main(String[] args) { PrismParser p = null; InputStream str = null; String src = null; try { if (args.length == 0) { System.out.println("Usage: java parser.PrismParser []"); System.out.println("Where: = -modulesfile or -mf"); System.out.println(" -propertiesfile or -pf"); System.out.println(" -expression or -e"); System.out.println(" -ltl or -l"); System.exit(1); } p = new PrismParser(); str = (args.length > 1) ? new FileInputStream(args[1]) : System.in; src = (args.length > 1) ? "file "+args[1] : "stdin"; System.out.println("Reading from "+src+"...\n"); if (args[0].equals("-modulesfile") || args[0].equals("-mf")) { ModulesFile mf = p.parseModulesFile(str); System.out.print("Modules file:\n=============\n\n" + mf); System.out.print("\nTree:\n=====\n" + mf.toTreeString()); mf.tidyUp(); System.out.print("\nAnd after expansion:\n====================\n\n" +mf); } else if (args[0].equals("-propertiesfile") || args[0].equals("-pf")) { PropertiesFile pf = p.parsePropertiesFile(new ModulesFile(), str); System.out.print("Properties file:\n================\n\n" + pf); System.out.print("\nTree:\n=====\n" + pf.toTreeString()); pf.tidyUp(); System.out.print("\nAnd after expansion:\n====================\n\n" + pf); } else if (args[0].equals("-expression") || args[0].equals("-e")) { Expression expr = p.parseSingleExpression(str); System.out.println("Expression: " + expr.toString()); System.out.print("Tree:\n=====\n" + expr.toTreeString()); expr.typeCheck(); expr.semanticCheck(); System.out.println("Type: " + expr.getType().getTypeString()); System.out.println("Eval: " + expr.evaluate()); } else if (args[0].equals("-ltl") || args[0].equals("-l")) { Expression expr = p.parseSingleLTLFormula(str); expr = (Expression) expr.accept(new ASTTraverseModify() { public Object visit(ExpressionIdent e) throws PrismLangException { return new parser.ast.ExpressionVar(e.getName(), TypeBool.getInstance()); } }); System.out.println("LTL formula: " + expr.toString()); System.out.print("Tree:\n=====\n" + expr.toTreeString()); expr.typeCheck(); //expr.semanticCheck(); System.out.println("Type: " + expr.getType().getTypeString()); boolean pnf = Expression.isPositiveNormalFormLTL(expr); System.out.println("Positive normal form: " + pnf); System.out.println("Syntactically co-safe: " + Expression.isCoSafeLTLSyntactic(expr)); if (!pnf) { Expression exprPnf = BooleanUtils.convertLTLToPositiveNormalForm(expr.deepCopy()); System.out.println("Positive normal form conversion: " + exprPnf); System.out.println("Syntactically co-safe: " + Expression.isCoSafeLTLSyntactic(exprPnf)); } Expression expr2 = (Expression) expr.deepCopy().accept(new ASTTraverseModify() { public Object visit(ExpressionVar e) throws PrismLangException { return new parser.ast.ExpressionLabel(e.getName()); } }); SimpleLTL sltl = expr2.convertForJltl2ba(); System.out.println("LBT: " + sltl.toStringLBT()); System.out.println("Spot: " + sltl.toStringSpot()); System.out.println("Spin: " + sltl.toStringSpin()); } else { System.out.println("Unknown switch"); System.exit(1); } } catch (PrismLangException e) { System.out.println("Error in "+src+": " + e.getMessage()+"."); System.exit(1); } catch (FileNotFoundException e) { System.out.println(e); System.exit(1); } } //----------------------------------------------------------------------------------- // Methods called by Prism //----------------------------------------------------------------------------------- // Constructor public PrismParser() { // Call default constructor this(System.in); } // Parse modules file public ModulesFile parseModulesFile(InputStream str) throws PrismLangException { return parseModulesFile(str, null); } public ModulesFile parseModulesFile(InputStream str, ModelType typeOverride) throws PrismLangException { ModulesFile mf = null; // (Re)start parser ReInit(str); // Parse try { mf = ModulesFile(); } catch (ParseException e) { throw generateSyntaxError(e); } // Override type of model if requested if (typeOverride != null) { mf.setModelTypeInFile(typeOverride); } return mf; } // Parse properties file (pass ModulesFile in to get at its constants) public PropertiesFile parsePropertiesFile(ModelInfo modelInfo, InputStream str) throws PrismLangException { return parsePropertiesFile(modelInfo, str, false); } public PropertiesFile parsePropertiesFile(ModelInfo modelInfo, InputStream str, boolean strict) throws PrismLangException { PropertiesFile pf = null; // (Re)start parser ReInit(str); this.modelInfo = modelInfo; // Parse try { pf = strict ? PropertiesFile() : PropertiesFileSemicolonless(); } catch (ParseException e) { throw generateSyntaxError(e); } return pf; } // Parse a single expression public Expression parseSingleExpression(InputStream str) throws PrismLangException { Expression expr = null; // (Re)start parser ReInit(str); // Parse try { expr = SingleExpression(); } catch (ParseException e) { throw generateSyntaxError(e); } return expr; } // Parse a single LTL formula public Expression parseSingleLTLFormula(InputStream str) throws PrismLangException { Expression expr = null; // (Re)start parser ReInit(str); // Parse try { expr = SingleLTLFormula(); } catch (ParseException e) { throw generateSyntaxError(e); } return expr; } // Parse a for loop public ForLoop parseForLoop(InputStream str) throws PrismLangException { ForLoop fl = null; // (Re)start parser ReInit(str); // Parse try { fl = ForLoop(); } catch (ParseException e) { throw generateSyntaxError(e); } return fl; } //----------------------------------------------------------------------------------- // Some utility methods //----------------------------------------------------------------------------------- /** * Get comment block directly preceding a token and remove "//" characters */ public static String getPrecedingCommentBlock(Token firstToken) { String comment = "", s; Token t = firstToken; // extract any comment from the previous lines of the file if (t.specialToken != null && !(t.specialToken.kind == PrismParserConstants.WHITESPACE && t.specialToken.image.matches("[\\n\\r]*"))) { // trace back thru special tokens that are comments t = t.specialToken; while (t.specialToken != null && !(t.specialToken.kind == PrismParserConstants.WHITESPACE && t.specialToken.image.matches("[\\n\\r]*"))) t = t.specialToken; // concatenate comment special tokens while (t != null) { s = t.image; // strip any nasty carriage returns s = s.replaceAll("\r", ""); // remove "//" and preceding/subsequent spaces/tabs from comments if (t.kind == PrismParserConstants.COMMENT) { while (comment.length() > 0 && (""+comment.charAt(comment.length()-1)).matches("[ \t]")) comment = comment.substring(0,comment.length()-1); s = s.substring(2); s = s.replaceFirst("[ \t]*", ""); } comment += s; t = t.next; } } // remove final new line (if present) if (comment.length() > 0 && (comment.charAt(comment.length()-1) == '\n')) comment = comment.substring(0,comment.length()-1); return comment; } // Add "//"s into comment block public static String slashCommentBlock(String comment) { int i; String s, res = ""; // break into lines while ((i = comment.indexOf("\n")) != -1) { s = comment.substring(0, i); comment = comment.substring(i+1); // add "//" to non-empty lines if (s.trim().length()>0) res += "// " + s; res += "\n"; } // deal with any trailing characters (with no new line ending them) if (comment.trim().length()>0) res += "// " + comment + "\n"; return res; } /** * Test a string to see if it is a PRISM language keyword. */ public static boolean isKeyword(String s) { return keywordList.contains(s); } /** * Get access to the list of all PRISM language keywords. */ public static List getListOfKeywords() { return keywordList; } /** * Set the tab size used by the lexer/parser. */ public void setTabSize(int size) { SimpleCharStream.setTabSize(size); } /** * Get the tab size used by the lexer/parser. */ public int getTabSize() { return SimpleCharStream.getTabSize(); } /** * Generate a syntax error (PrismLangException) from a ParseException. */ protected PrismLangException generateSyntaxError(ParseException e) { if (e == null) return new PrismLangException("Syntax error"); // No token: was prob created manually so use message if present if (e.currentToken == null) { String msg = e.getMessage(); String exMsg = "Syntax error"; if (msg != null && msg.length() > 0) exMsg += ": " + msg; return new PrismLangException(exMsg); } // Use current/next token to find location of error // But don't use error message if too long (esp if generated by JavaCC) else { ExpressionIdent tmp; if (e.currentToken.next == null) { tmp = new ExpressionIdent(e.currentToken.image); tmp.setPosition(e.currentToken); } else { tmp = new ExpressionIdent(e.currentToken.next.image); tmp.setPosition(e.currentToken.next); } String msg = e.getMessage(); String exMsg = "Syntax error"; if (msg != null && msg.length() > 0 && msg.length() < 20) exMsg += ": " + msg; return new PrismLangException(exMsg, tmp); } } //----------------------------------------------------------------------------------- // A few classes for temporary storage of bits of the AST //----------------------------------------------------------------------------------- static class ExpressionPair { public Expression expr1 = null; public Expression expr2 = null; } } //----------------------------------------------------------------------------------- PARSER_END(PrismParser) // Token definitions follow // Note that PrismSyntaxHighlighter makes assumptions about the ordering of these // Skip (but store) all other white space SPECIAL_TOKEN : { } // Skip (but store) comments SPECIAL_TOKEN : { } // Tokens TOKEN : { // Keywords < A: "A" > //| < ARRAY: "array" > | < BOOL: "bool" > | < CLOCK: "clock" > | < CONST: "const" > | < CTMC: "ctmc" > | < C: "C" > | < DOUBLE: "double" > | < DTMC: "dtmc" > | < E: "E" > | < ENDINIT: "endinit" > | < ENDINVARIANT: "endinvariant" > | < ENDMODULE: "endmodule" > | < ENDOBSERVABLES: "endobservables" > | < ENDREWARDS: "endrewards" > | < ENDSYSTEM: "endsystem" > | < FALSE: "false" > | < FORMULA: "formula" > | < FILTER: "filter" > | < FUNC: "func" > | < F: "F" > | < GLOBAL: "global" > | < G: "G" > | < INIT: "init" > | < INVARIANT: "invariant" > | < I: "I" > | < INT: "int" > | < LABEL: "label" > | < MAX: "max" > | < MDP: "mdp" > | < MIN: "min" > | < MODULE: "module" > | < X: "X" > | < NONDETERMINISTIC: "nondeterministic" > | < OBSERVABLE: "observable" > | < OBSERVABLES: "observables" > //| < OF: "of" > | < PMAX: "Pmax" > | < PMIN: "Pmin" > | < P: "P" > | < POMDP: "pomdp" > | < POPTA: "popta" > | < PROBABILISTIC: "probabilistic" > | < PROB: "prob" > | < PTA: "pta" > | < RATE: "rate" > | < REWARDS: "rewards" > | < RMAX: "Rmax" > | < RMIN: "Rmin" > | < R: "R" > | < S: "S" > | < STOCHASTIC: "stochastic" > | < SYSTEM: "system" > | < TRUE: "true" > | < U: "U" > | < W: "W" > // Punctuation, etc. // Note that "NOT" must be the first item of punctuation in this list // (PrismSyntaxHighlighter relies on this fact) | < NOT: "!" > | < AND: "&" > | < OR: "|" > | < IMPLIES: "=>" > | < IFF: "<=>" > | < RARROW: "->" > | < COLON: ":" > | < SEMICOLON: ";" > | < COMMA: "," > | < DOTS: ".." > | < LPARENTH: "(" > | < RPARENTH: ")" > | < LBRACKET: "[" > | < RBRACKET: "]" > | < DLBRACKET: "[[" > | < DRBRACKET: "]]" > | < LBRACE: "{" > | < RBRACE: "}" > | < EQ: "=" > | < NE: "!=" > | < LT: "<" > | < GT: ">" > | < DLT: "<<" > | < DGT: ">>" > | < LE: "<=" > | < GE: ">=" > | < PLUS: "+" > | < MINUS: "-" > | < TIMES: "*" > | < DIVIDE: "/" > | < PRIME: "'" > | < RENAME: "<-" > | < QMARK: "?" > // Regular expressions | < REG_INT: (["1"-"9"](["0"-"9"])*)|("0") > | < REG_DOUBLE: (["0"-"9"])*(".")?(["0"-"9"])+(["e","E"](["-","+"])?(["0"-"9"])+)? > | < REG_IDENTPRIME: ["_","a"-"z","A"-"Z"](["_","a"-"z","A"-"Z","0"-"9"])*"'" > | < REG_IDENT: ["_","a"-"z","A"-"Z"](["_","a"-"z","A"-"Z","0"-"9"])* > | < REG_QUOTED_IDENT: "\"" (["_","a"-"z","A"-"Z"](["_","a"-"z","A"-"Z","0"-"9"])*) "\"" > | < PREPROC: "#"(~["#"])*"#" > // Special catch-all token for lexical errors // (this allows us to throw our usual exceptions in this case) | < LEXICAL_ERROR: ~[] > } //----------------------------------------------------------------------------------- // Top-level productions //----------------------------------------------------------------------------------- // Modules file ModulesFile ModulesFile() throws PrismLangException : { ModelType type = null; int typeCount = 0; Token typeDupe = null; Declaration global; parser.ast.Module m = null; RenamedModule rm = null; RewardStruct rs = null; Expression init = null; ObservableVars obsVars = null; Observable obs = null; int initCount = 0; Expression initDupe = null; ModulesFile mf = new ModulesFile(); Token begin = null; } { ( { begin = getToken(1); } // Model type ( type=ModulesFileType() { typeCount++; if (typeCount == 2) typeDupe = getToken(0); } ) | // Formula/label/constant definition FormulaDef(mf.getFormulaList()) | LabelDef(mf.getLabelList()) | ConstantDef(mf.getConstantList()) | // Global variable global = GlobalDecl() { mf.addGlobal(global); } | // Renamed module (lookahead to distinguish from normal module) LOOKAHEAD( Identifier() ) rm = RenamedModule() { mf.addRenamedModule(rm); } | // Module m = Module() { mf.addModule(m); } | // Reward structure rs = RewardStruct() { mf.addRewardStruct(rs); } | // Initial states ("init...endinit" construct) init = Init() { mf.setInitialStates(init); initCount++; if (initCount == 2) initDupe = init; } | // Observables obsVars = ObservableVars() { mf.addObservableVarList(obsVars); } | obs = Observable() {mf.addObservableDefinition(obs); } | // System definition ("system...endsystem" construct) SystemEndsystem(mf) )* { // Check for multiple instances of some items if (typeDupe != null) { ExpressionIdent tmp = new ExpressionIdent(typeDupe.image); tmp.setPosition(typeDupe); throw new PrismLangException("There were multiple model type declarations", tmp); } if (initDupe != null) { throw new PrismLangException("There were multiple init...endinit constructs", initDupe); } // Set model type (might be null, i.e., unspecified) mf.setModelTypeInFile(type); // Return completed ModulesFile object mf.setPosition(begin != null? begin: getToken(0), getToken(0)); return mf; } } // Properties file PropertiesFile PropertiesFile() throws PrismLangException : { PropertiesFile pf = new PropertiesFile(modelInfo); Property prop; Token begin = null; } { { begin = getToken(1); } ( // Semi-colon terminated property ( prop = Property() { pf.addProperty(prop); } ()+ ) | // Label/constant definition ( LabelDef(pf.getLabelList()) ) | ( ConstantDef(pf.getConstantList()) ) )* { pf.setPosition(begin, getToken(0)); return pf; } } // Properties file with optional semicolons - beware of potential ambiguities PropertiesFile PropertiesFileSemicolonless() throws PrismLangException : { PropertiesFile pf = new PropertiesFile(modelInfo); Property prop; Token begin = null; } { { begin = getToken(1); } ( // Semi-colon terminated property ( prop = Property() ()* { pf.addProperty(prop); } ) | // Label/constant definition ( LabelDef(pf.getLabelList()) ) | ( ConstantDef(pf.getConstantList()) ) )* { pf.setPosition(begin, getToken(0)); return pf; } } // Property - expression, with optional name/comment Property Property() : { String name = null; Expression expr; Property prop; Token begin = null; } { // Note that we jump in a few levels down in the Expression hierarchy // (more precisely, we skip the temporal operators, which can't occur at the top-level) // (this avoids some common parsing errors for semicolon-less files) // Note also use of lookahead (to colon) to distinguish (optional) name from label reference ( { begin = getToken(1); } ( LOOKAHEAD(QuotedIdentifier() ) name = QuotedIdentifier() )? expr = ExpressionITE(true, false) { prop = new Property(expr, name, getPrecedingCommentBlock(begin)); } ) { prop.setPosition(begin, getToken(0)); return prop; } } // A single expression Expression SingleExpression() : { Expression ret; } { ( ret = Expression(false, false) ) { return ret; } } // A single LTL formula Expression SingleLTLFormula() : { Expression ret; } { ( ret = Expression(true, true) ) { return ret; } } //----------------------------------------------------------------------------------- // Modules file stuff (a few bits of which are reused for property files) //----------------------------------------------------------------------------------- // Keyword denoting model type ModelType ModulesFileType() : { ModelType modelType = null; } { ( (|) { modelType=ModelType.DTMC; } | (|) { modelType=ModelType.MDP; } | (|) { modelType=ModelType.CTMC; } | { modelType=ModelType.PTA; } | { modelType=ModelType.POMDP; } | { modelType=ModelType.POPTA; } ) { return modelType; } } // Formula definition void FormulaDef(FormulaList formulaList) : { ExpressionIdent name = null; Expression expr = null; } { ( name = IdentifierExpression() expr = Expression(false, false) ) { formulaList.addFormula(name, expr); } } // Label definition void LabelDef(LabelList labelList) throws PrismLangException : { ExpressionIdent name = null; Expression expr = null; } { // Lookahead required because of the error handling clause below LOOKAHEAD(