//============================================================================== // // 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; } PARSER_BEGIN(PrismParser) package parser; import java.io.*; import java.util.ArrayList; import parser.ast.*; import prism.PrismLangException; public class PrismParser { // The modules file associated with properties file being parsed private static ModulesFile modulesFile; // List of keyword strings private static ArrayList keywordList = new ArrayList(); //----------------------------------------------------------------------------------- // 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.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: " + Expression.getTypeString(expr.getType())); System.out.println("Eval: " + expr.evaluate(null, null)); } 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); // Build a list of strings for keywords keywordList.clear(); for (int i = PrismParserConstants.COMMENT+1; i < PrismParserConstants.NOT; i++) { keywordList.add(PrismParserConstants.tokenImage[i].replaceAll("\"", "")); } } // Parse modules file public ModulesFile parseModulesFile(InputStream str) throws PrismLangException { return parseModulesFile(str, 0); } public ModulesFile parseModulesFile(InputStream str, int 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 != 0) { mf.setType(typeOverride); } return mf; } // Parse properties file (pass ModulesFile in to get at its constants) public PropertiesFile parsePropertiesFile(ModulesFile mf, InputStream str) throws PrismLangException { return parsePropertiesFile(mf, str, false); } public PropertiesFile parsePropertiesFile(ModulesFile mf, InputStream str, boolean strict) throws PrismLangException { PropertiesFile pf = null; // (Re)start parser ReInit(str); modulesFile = mf; // 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 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 (including white space) // 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) { // trace back thru special tokens t = t.specialToken; while (t.specialToken != null) t = t.specialToken; // ignore initial white space while (t != null && t.kind == PrismParserConstants.WHITESPACE) t = t.next; // concatenate 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); } /** * 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(0); } /** * Split a single argument ExpressionFunc object into two expressions. */ protected ExpressionPair splitExpressionFunc(ExpressionFunc func) { ExpressionPair expr = new ExpressionPair(); return expr; } /** * Generate a syntax error (PrismLangException) from a ParseException. */ protected PrismLangException generateSyntaxError(ParseException e) { Token t; if (e == null || e.currentToken == null) return new PrismLangException("Syntax error"); else if (e.currentToken.next == null) { ExpressionIdent tmp = new ExpressionIdent(e.currentToken.image); tmp.setPosition(e.currentToken); return new PrismLangException("Syntax error", tmp); } else { ExpressionIdent tmp = new ExpressionIdent(e.currentToken.next.image); tmp.setPosition(e.currentToken.next); return new PrismLangException("Syntax error", tmp); } } //----------------------------------------------------------------------------------- // A few classes for temporary storage of bits of the AST //----------------------------------------------------------------------------------- static class TimeBound { public Expression lBound = null; public Expression uBound = null; } 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 < BOOL: "bool" > | < CONST: "const" > | < CTMC: "ctmc" > | < C: "C" > | < DOUBLE: "double" > | < DTMC: "dtmc" > | < ENDINIT: "endinit" > | < ENDMODULE: "endmodule" > | < ENDREWARDS: "endrewards" > | < ENDSYSTEM: "endsystem" > | < FALSE: "false" > | < FORMULA: "formula" > | < FUNC: "func" > | < F: "F" > | < GLOBAL: "global" > | < G: "G" > | < INIT: "init" > | < I: "I" > | < INT: "int" > | < LABEL: "label" > | < MAX: "max" > | < MDP: "mdp" > | < MIN: "min" > | < MODULE: "module" > | < X: "X" > | < NONDETERMINISTIC: "nondeterministic" > | < PMAX: "Pmax" > | < PMIN: "Pmin" > | < P: "P" > | < PROBABILISTIC: "probabilistic" > | < PROB: "prob" > | < 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: "=>" > | < RARROW: "->" > | < COLON: ":" > | < SEMICOLON: ";" > | < COMMA: "," > | < DOTS: ".." > | < LPARENTH: "(" > | < RPARENTH: ")" > | < LBRACKET: "[" > | < RBRACKET: "]" > | < LBRACE: "{" > | < RBRACE: "}" > | < EQ: "=" > | < NE: "!=" > | < LT: "<" > | < GT: ">" > | < LE: "<=" > | < GE: ">=" > | < PLUS: "+" > | < MINUS: "-" > | < TIMES: "*" > | < DIVIDE: "/" > | < PRIME: "'" > | < RENAME: "<-" > | < QMARK: "?" > | < DQUOTE: "\"" > // 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"])* > | < 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 : { int type = 0; int typeCount = 0; Token typeDupe = null; Declaration global; Module m = null; RenamedModule rm = null; RewardStruct rs = null; Expression init = null; int initCount = 0; Expression initDupe = null; SystemDefn sys = null; int sysCount = 0; SystemDefn sysDupe = 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; } | // System definition ("system...endsystem" construct) sys = SystemEndsystem() { mf.setSystemDefn(sys); sysCount++; if (sysCount == 2) sysDupe = sys; } )* { // 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); } if (sysDupe != null) { throw new PrismLangException("There were multiple system...endsystem constructs", sysDupe); } // Set type (default is MDP) switch (type) { case PROBABILISTIC: case DTMC: mf.setType(ModulesFile.PROBABILISTIC); break; case NONDETERMINISTIC: case MDP: mf.setType(ModulesFile.NONDETERMINISTIC); break; case STOCHASTIC: case CTMC: mf.setType(ModulesFile.STOCHASTIC); break; default : mf.setType(ModulesFile.NONDETERMINISTIC); break; } // 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(modulesFile); Expression expr; Token begin = null, t = null; } { { begin = getToken(1); } ( // Semi-colon terminated property ( { t = getToken(1); } expr = Property() { pf.addProperty(expr, getPrecedingCommentBlock(t)); } ()+ ) | // 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(modulesFile); Expression expr; Token begin = null, t = null; } { { begin = getToken(1); } ( // Semi-colon terminated property ( { t = getToken(1); } expr = Property() ()* { pf.addProperty(expr, getPrecedingCommentBlock(t)); } ) | // Label/constant definition ( LabelDef(pf.getLabelList()) ) | ( ConstantDef(pf.getConstantList()) ) )* { pf.setPosition(begin, getToken(0)); return pf; } } // Property expression (used above) Expression Property() : { Expression expr; } { // 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) expr = ExpressionITE(true, false) { return expr; } } // A single expression Expression SingleExpression() : { Expression ret; } { ( ret = Expression(false, false) ) { return ret; } } //----------------------------------------------------------------------------------- // Modules file stuff (a few bits of which are reused for property files) //----------------------------------------------------------------------------------- // Keyword denoting model type int ModulesFileType() : { } { ( | | | | | ) { return getToken(0).kind; } } // 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(