You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
472 lines
12 KiB
472 lines
12 KiB
//==============================================================================
|
|
//
|
|
// Copyright (c) 2002-
|
|
// Authors:
|
|
// * Dave Parker <david.parker@comlab.ox.ac.uk> (University of Oxford)
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
//
|
|
// 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(PTAParser)
|
|
|
|
package pta.parser;
|
|
|
|
import java.io.*;
|
|
import java.util.*;
|
|
|
|
import pta.*;
|
|
import prism.PrismLangException;
|
|
|
|
public class PTAParser
|
|
{
|
|
//-----------------------------------------------------------------------------------
|
|
// Main method for testing purposes
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
public static void main(String[] args)
|
|
{
|
|
PTAParser p = null;
|
|
InputStream str = null;
|
|
String src = null;
|
|
|
|
try {
|
|
p = new PTAParser();
|
|
str = (args.length > 0) ? new FileInputStream(args[0]) : System.in;
|
|
src = (args.length > 0) ? "file "+args[0] : "stdin";
|
|
System.out.println("Reading from "+src+"...\n");
|
|
|
|
PTA pta = p.parsePTA(str);
|
|
System.out.print(pta);
|
|
}
|
|
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 PTAParser()
|
|
{
|
|
// Call default constructor
|
|
this(System.in);
|
|
}
|
|
|
|
// Parse PTA
|
|
|
|
public PTA parsePTA(InputStream str) throws PrismLangException
|
|
{
|
|
astPTA pta = null;
|
|
|
|
// (Re)start parser
|
|
ReInit(str);
|
|
// Parse
|
|
try {
|
|
pta = PTA();
|
|
}
|
|
catch (ParseException e) {
|
|
throw new PrismLangException(e.getMessage());
|
|
}
|
|
|
|
return pta.createDataStructures();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Abstract syntax tree classes
|
|
//------------------------------------------------------------------------------
|
|
|
|
// Classes used to build AST representing PTA.
|
|
// Note: locations are indexed by name here, not integer as in the normal PTA class
|
|
// (this is the main reason for needing separate AST classes here).
|
|
// For clocks, this approach is not needed: we just take the ordering of the clocks
|
|
// to be as they appear in the file (unlike locations, which have an explicit ordering
|
|
// combined with possible forward references).
|
|
|
|
static class astPTA
|
|
{
|
|
// Data
|
|
public ArrayList<String> clockNames;
|
|
public ArrayList<String> locationNames;
|
|
public HashMap<String,LinkedHashSet<Constraint>> invariants;
|
|
public HashMap<String,ArrayList<astTransition>> transitions;
|
|
// Methods
|
|
public astPTA() {
|
|
clockNames = new ArrayList<String>();
|
|
locationNames = new ArrayList<String>();
|
|
invariants = new HashMap<String,LinkedHashSet<Constraint>>();
|
|
transitions = new HashMap<String,ArrayList<astTransition>>();
|
|
}
|
|
public int getOrAddClock(String name) {
|
|
int i = clockNames.indexOf(name);
|
|
if (i == -1) { clockNames.add(name); return clockNames.size(); }
|
|
else return i+1;
|
|
}
|
|
public void addLocation(String name) {
|
|
locationNames.add(name);
|
|
invariants.put(name, new LinkedHashSet<Constraint>());
|
|
transitions.put(name, new ArrayList<astTransition>());
|
|
}
|
|
public void addInvariantCondition(String locName, Constraint c) {
|
|
invariants.get(locName).add(c);
|
|
}
|
|
public void setInvariantConditions(String locName, LinkedHashSet<Constraint> cs) {
|
|
invariants.put(locName, cs);
|
|
}
|
|
public astTransition addTransition(String locName) {
|
|
astTransition t = new astTransition();
|
|
transitions.get(locName).add(t);
|
|
return t;
|
|
}
|
|
public int getLocationIndex(String name) {
|
|
return locationNames.indexOf(name);
|
|
}
|
|
// Conversion to pta classes
|
|
public PTA createDataStructures()
|
|
{
|
|
int i, n;
|
|
String name;
|
|
PTA pta;
|
|
Transition trans;
|
|
LinkedHashSet <String> alphabet = new LinkedHashSet<String>();
|
|
// Find alphabet
|
|
n = locationNames.size();
|
|
for (i = 0; i < n; i++) {
|
|
ArrayList<astTransition> tt = transitions.get(locationNames.get(i));
|
|
if (tt == null || tt.isEmpty()) continue;
|
|
for (astTransition t : tt) {
|
|
if (t.action != null && !t.action.equals("")) {
|
|
alphabet.add(t.action);
|
|
}
|
|
}
|
|
}
|
|
// Create new PTA
|
|
pta = new PTA(new ArrayList<String> (alphabet));
|
|
// Add all clocks
|
|
n = clockNames.size();
|
|
for (i = 0; i < n; i++)
|
|
pta.addClock(clockNames.get(i));
|
|
// Add all locations
|
|
n = locationNames.size();
|
|
for (i = 0; i < n; i++)
|
|
pta.addLocation(locationNames.get(i));
|
|
// Add invariants/transitions to locations
|
|
n = locationNames.size();
|
|
for (i = 0; i < n; i++) {
|
|
name = locationNames.get(i);
|
|
pta.setInvariantConditions(i, invariants.get(name));
|
|
ArrayList<astTransition> tt = transitions.get(name);
|
|
if (tt == null || tt.isEmpty()) continue;
|
|
for (astTransition t : tt) {
|
|
if (!(t.edges.isEmpty())) {
|
|
trans = pta.addTransition(i, t.action);
|
|
t.createDataStructures(this, trans);
|
|
}
|
|
}
|
|
}
|
|
return pta;
|
|
}
|
|
}
|
|
|
|
static class astTransition
|
|
{
|
|
// Data
|
|
private String action = null;
|
|
private ArrayList<Constraint> guard;
|
|
public ArrayList<astEdge> edges;
|
|
// Methods
|
|
public astTransition() { guard = new ArrayList<Constraint>(); edges = new ArrayList<astEdge>(); }
|
|
public void setAction(String action) { this.action = action; }
|
|
public void addGuardConstraint(Constraint c) { guard.add(c); }
|
|
public astEdge addEdge(double prob, String dest) { astEdge e = new astEdge(prob, dest); edges.add(e); return e; }
|
|
// Conversion to pta classes
|
|
public void createDataStructures(astPTA pta, Transition trans)
|
|
{
|
|
for (Constraint c : guard)
|
|
trans.addGuardConstraint(c);
|
|
for (astEdge e : edges)
|
|
e.createDataStructures(pta, trans);
|
|
}
|
|
}
|
|
|
|
static class astEdge
|
|
{
|
|
// Data
|
|
public double prob;
|
|
public String dest;
|
|
public HashMap<Integer,Integer> resets;
|
|
// Methods
|
|
public astEdge(double prob, String dest) { this.prob = prob; this.dest = dest; resets = new HashMap<Integer,Integer>(); }
|
|
public void addReset(int clock, int val) { resets.put(clock, val); }
|
|
// Conversion to pta classes
|
|
public void createDataStructures(astPTA pta, Transition trans)
|
|
{
|
|
int d = pta.getLocationIndex(dest);
|
|
if (d == -1) { System.err.println("Error: Location \""+dest+"\" does not exist"); System.exit(1); }
|
|
Edge edge = trans.addEdge(prob, d);
|
|
for (Map.Entry<Integer,Integer> e : resets.entrySet()) edge.addReset(e.getKey(), e.getValue());
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
PARSER_END(PTAParser)
|
|
|
|
// Skip (but store) all other white space
|
|
|
|
SPECIAL_TOKEN :
|
|
{
|
|
<WHITESPACE: (" "|"\t"|"\n"|"\r")>
|
|
}
|
|
|
|
// Skip (but store) comments
|
|
|
|
SPECIAL_TOKEN :
|
|
{
|
|
<COMMENT: "#" (~["\n","\r"])* ("\n"|"\r"|"\r\n")>
|
|
}
|
|
|
|
// Tokens
|
|
|
|
TOKEN :
|
|
{
|
|
// Keywords
|
|
< INIT: "init" >
|
|
| < NODE: "node" >
|
|
| < NULL: "null" >
|
|
| < TRAN: "tran" >
|
|
| < TRUE: "true" >
|
|
// Punctuation, etc.
|
|
| < 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"])* >
|
|
// Special catch-all token for lexical errors
|
|
// (this allows us to throw our usual exceptions in this case)
|
|
| < LEXICAL_ERROR: ~[] >
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
// Top-level production
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
// PTA
|
|
|
|
astPTA PTA() :
|
|
{
|
|
astPTA pta = new astPTA();
|
|
}
|
|
{
|
|
( (Location(pta))* <EOF> )
|
|
{
|
|
return pta;
|
|
}
|
|
}
|
|
|
|
void Location(astPTA pta) :
|
|
{
|
|
String name;
|
|
LinkedHashSet<Constraint> constrs;
|
|
}
|
|
{
|
|
<LBRACE>
|
|
(<INIT>)?
|
|
<NODE> name=Identifier() { pta.addLocation(name); }
|
|
<SEMICOLON> constrs=ConstraintList(pta) { pta.setInvariantConditions(name, constrs); }
|
|
(Transition(pta, name))*
|
|
(<RBRACE>|<TIMES>)
|
|
}
|
|
|
|
void Transition(astPTA pta, String locName) :
|
|
{
|
|
astTransition tr;
|
|
}
|
|
{
|
|
<LBRACKET> { tr = pta.addTransition(locName); }
|
|
(Edge(pta, tr))*
|
|
<RBRACKET>
|
|
}
|
|
|
|
void Edge(astPTA pta, astTransition tr) :
|
|
{
|
|
LinkedHashSet<Constraint> constrs;
|
|
String action = null, dest;
|
|
double p;
|
|
astEdge edge;
|
|
HashMap<Integer,Integer> resets;
|
|
}
|
|
{
|
|
(action=Identifier() <OR><OR>)? <TRAN> dest=Identifier() <SEMICOLON> constrs=ConstraintList(pta) <SEMICOLON> resets=Resets(pta) <SEMICOLON> p=Probability()
|
|
{
|
|
tr.setAction(action);
|
|
edge = tr.addEdge(p, dest);
|
|
for (Map.Entry<Integer,Integer> e : resets.entrySet()) edge.addReset(e.getKey(), e.getValue());
|
|
for (Constraint c : constrs) tr.addGuardConstraint(c);
|
|
}
|
|
}
|
|
|
|
LinkedHashSet<Constraint> ConstraintList(astPTA pta) :
|
|
{
|
|
LinkedHashSet<Constraint> constrs = new LinkedHashSet<Constraint>();
|
|
}
|
|
{
|
|
((Constraint(pta, constrs) ( <COMMA> Constraint(pta, constrs) )* )
|
|
|
|
|
(<TRUE>))
|
|
{ return constrs; }
|
|
}
|
|
|
|
void Constraint(astPTA pta, LinkedHashSet<Constraint> constrs) :
|
|
{
|
|
String clock1Name, clock2Name;
|
|
int clock1, clock2, val;
|
|
Token t;
|
|
}
|
|
{
|
|
(clock1Name=Identifier() (t=<LT>|t=<LE>|t=<GT>|t=<GE>|t=<EQ>) (val=Integer()
|
|
{
|
|
clock1 = pta.getOrAddClock(clock1Name);
|
|
switch (t.kind) {
|
|
case PTAParserConstants.LT:
|
|
constrs.add(Constraint.buildLt(clock1, val)); break;
|
|
case PTAParserConstants.LE:
|
|
constrs.add(Constraint.buildLeq(clock1, val)); break;
|
|
case PTAParserConstants.GT:
|
|
constrs.add(Constraint.buildGt(clock1, val)); break;
|
|
case PTAParserConstants.GE:
|
|
constrs.add(Constraint.buildGeq(clock1, val)); break;
|
|
case PTAParserConstants.EQ:
|
|
constrs.add(Constraint.buildLeq(clock1, val));
|
|
constrs.add(Constraint.buildGeq(clock1, val)); break;
|
|
}
|
|
}
|
|
| clock2Name=Identifier()
|
|
{
|
|
clock1 = pta.getOrAddClock(clock1Name);
|
|
clock2 = pta.getOrAddClock(clock2Name);
|
|
switch (t.kind) {
|
|
case PTAParserConstants.LT:
|
|
constrs.add(Constraint.buildLt(clock1, clock2)); break;
|
|
default:
|
|
System.err.println("Error: Unsupported constraint type"); System.exit(1);
|
|
}
|
|
}
|
|
))
|
|
}
|
|
|
|
HashMap<Integer,Integer> Resets(astPTA pta) :
|
|
{
|
|
HashMap<Integer,Integer> resets = new HashMap<Integer,Integer>();
|
|
}
|
|
{
|
|
((Reset(pta, resets) (<COMMA> Reset(pta, resets))*)
|
|
|
|
|
(<NULL>))
|
|
{ return resets; }
|
|
}
|
|
|
|
void Reset(astPTA pta, HashMap<Integer,Integer> resets) :
|
|
{
|
|
String clockName;
|
|
int clock;
|
|
int val;
|
|
}
|
|
{
|
|
(clockName=Identifier() <EQ> val=Integer())
|
|
{
|
|
clock = pta.getOrAddClock(clockName);
|
|
resets.put(clock, val);
|
|
}
|
|
}
|
|
|
|
double Probability() :
|
|
{
|
|
Token t;
|
|
double d;
|
|
}
|
|
{
|
|
(t=<REG_DOUBLE> | t=<REG_INT>)
|
|
{ return Double.parseDouble(t.image); }
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
// Miscellaneous stuff
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
// Identifier (returns String)
|
|
|
|
String Identifier() :
|
|
{
|
|
}
|
|
{
|
|
<REG_IDENT> { return getToken(0).image; }
|
|
}
|
|
|
|
// Integer
|
|
|
|
int Integer() :
|
|
{
|
|
}
|
|
{
|
|
<REG_INT> { return Integer.parseInt(getToken(0).image); }
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|