|
|
|
@ -1,8 +1,8 @@ |
|
|
|
//============================================================================== |
|
|
|
// |
|
|
|
// Copyright (c) 2002- |
|
|
|
// Copyright (c) 2019- |
|
|
|
// Authors: |
|
|
|
// * Dave Parker <david.parker@comlab.ox.ac.uk> (University of Oxford) |
|
|
|
// * Dave Parker <d.a.parker@cs.bham.ac.uk> (University of Birmingham) |
|
|
|
// |
|
|
|
//------------------------------------------------------------------------------ |
|
|
|
// |
|
|
|
@ -24,44 +24,48 @@ |
|
|
|
// |
|
|
|
//============================================================================== |
|
|
|
|
|
|
|
package parser; |
|
|
|
package prism; |
|
|
|
|
|
|
|
import java.io.BufferedReader; |
|
|
|
import java.io.File; |
|
|
|
import java.io.FileReader; |
|
|
|
import java.io.IOException; |
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.Arrays; |
|
|
|
import java.util.Collections; |
|
|
|
import java.util.List; |
|
|
|
import java.util.regex.Matcher; |
|
|
|
import java.util.regex.Pattern; |
|
|
|
|
|
|
|
import parser.VarList; |
|
|
|
import parser.ast.Declaration; |
|
|
|
import parser.ast.DeclarationBool; |
|
|
|
import parser.ast.DeclarationInt; |
|
|
|
import parser.ast.DeclarationType; |
|
|
|
import parser.ast.Expression; |
|
|
|
import parser.ast.ExpressionIdent; |
|
|
|
import parser.ast.ExpressionLiteral; |
|
|
|
import parser.ast.LabelList; |
|
|
|
import parser.ast.Module; |
|
|
|
import parser.ast.ModulesFile; |
|
|
|
import parser.ast.RewardStruct; |
|
|
|
import parser.type.Type; |
|
|
|
import parser.type.TypeBool; |
|
|
|
import parser.type.TypeInt; |
|
|
|
import prism.ModelType; |
|
|
|
import prism.PrismComponent; |
|
|
|
import prism.PrismException; |
|
|
|
|
|
|
|
/** |
|
|
|
* Class to build a (partial) ModulesFile corresponding to imported explicit-state file storage of a model. |
|
|
|
* Basically, the ModulesFile just stores the model type and variable info. |
|
|
|
* Class to build a ModelInfo object corresponding to imported explicit-state file storage of a model. |
|
|
|
* Basically, just stores the model type and variable info. |
|
|
|
* The number of states in the model is also extracted. |
|
|
|
*/ |
|
|
|
public class ExplicitFiles2ModulesFile extends PrismComponent |
|
|
|
public class ExplicitFiles2ModelInfo extends PrismComponent |
|
|
|
{ |
|
|
|
// Info extracted from files for ModelInfo object |
|
|
|
private int numVars; |
|
|
|
private List<String> varNames; |
|
|
|
private List<Type> varTypes; |
|
|
|
private VarList varList; |
|
|
|
private List<String> labelNames; |
|
|
|
|
|
|
|
// Num states |
|
|
|
private int numStates = 0; |
|
|
|
|
|
|
|
public ExplicitFiles2ModulesFile(PrismComponent parent) |
|
|
|
public ExplicitFiles2ModelInfo(PrismComponent parent) |
|
|
|
{ |
|
|
|
super(parent); |
|
|
|
} |
|
|
|
@ -76,66 +80,109 @@ public class ExplicitFiles2ModulesFile extends PrismComponent |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Build a ModulesFile corresponding to the passed in states/transitions/labels files. |
|
|
|
* Build a ModelInfo object corresponding to the passed in states/transitions/labels files. |
|
|
|
* If {@code typeOverride} is null, we assume model is an MDP. |
|
|
|
* |
|
|
|
* @param statesFile states file (may be {@code null}) |
|
|
|
* @param transFile transitions file |
|
|
|
* @param labelsFile labels file (may be {@code null}) |
|
|
|
*/ |
|
|
|
public ModulesFile buildModulesFile(File statesFile, File transFile, File labelsFile, File stateRewardsFile, ModelType typeOverride) throws PrismException |
|
|
|
public ModelInfo buildModelInfo(File statesFile, File transFile, File labelsFile, ModelType typeOverride) throws PrismException |
|
|
|
{ |
|
|
|
ModulesFile modulesFile; |
|
|
|
ModelType modelType; |
|
|
|
|
|
|
|
// Generate ModulesFile from states or transitions file, depending what is available |
|
|
|
// Extract variable info from states or transitions file, depending on what is available |
|
|
|
if (statesFile != null) { |
|
|
|
modulesFile = createVarInfoFromStatesFile(statesFile); |
|
|
|
extractVarInfoFromStatesFile(statesFile); |
|
|
|
} else { |
|
|
|
modulesFile = createVarInfoFromTransFile(transFile); |
|
|
|
extractVarInfoFromTransFile(transFile); |
|
|
|
} |
|
|
|
|
|
|
|
// Generate and store a label list from the labelsFile, if available. |
|
|
|
// Generate and store label names from the labels file, if available. |
|
|
|
// This way, expressions can refer to the labels later on. |
|
|
|
if (labelsFile != null) { |
|
|
|
modulesFile.setLabelList(createLabelListFromLabelsFile(labelsFile)); |
|
|
|
extractLabelNamesFromLabelsFile(labelsFile); |
|
|
|
} |
|
|
|
|
|
|
|
// Add a new (unnamed) reward structure if there is a state rewards file |
|
|
|
if (stateRewardsFile != null) { |
|
|
|
modulesFile.addRewardStruct(new RewardStruct()); |
|
|
|
} |
|
|
|
|
|
|
|
// Set model type: if no preference stated, assume default of MDP |
|
|
|
modelType = (typeOverride == null) ? ModelType.MDP : typeOverride; |
|
|
|
modulesFile.setModelType(modelType); |
|
|
|
ModelType modelType = (typeOverride == null) ? ModelType.MDP : typeOverride; |
|
|
|
|
|
|
|
return modulesFile; |
|
|
|
// Create and return ModelInfo object with above info |
|
|
|
ModelInfo modelInfo = new ModelInfo() |
|
|
|
{ |
|
|
|
@Override |
|
|
|
public ModelType getModelType() |
|
|
|
{ |
|
|
|
return modelType; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public List<String> getVarNames() |
|
|
|
{ |
|
|
|
return varNames; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public List<Type> getVarTypes() |
|
|
|
{ |
|
|
|
return varTypes; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public VarList createVarList() throws PrismException |
|
|
|
{ |
|
|
|
return varList; |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public List<String> getLabelNames() |
|
|
|
{ |
|
|
|
return labelNames; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
return modelInfo; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Build a ModulesFile corresponding to a states file. |
|
|
|
* Build a "dummy" RewardGenerator object corresponding to the passed in rewards file. |
|
|
|
* Provides access to reward struct names, but not the rewards themselves. |
|
|
|
* @param stateRewardsFile state rewards file (may be {@code null}) |
|
|
|
*/ |
|
|
|
private ModulesFile createVarInfoFromStatesFile(File statesFile) throws PrismException |
|
|
|
public RewardGenerator buildRewardInfo(File stateRewardsFile) throws PrismException |
|
|
|
{ |
|
|
|
// Very simple for now: either 1 unnamed reward struct or none |
|
|
|
if (stateRewardsFile != null) { |
|
|
|
return new RewardGenerator() |
|
|
|
{ |
|
|
|
@Override |
|
|
|
public List<String> getRewardStructNames() |
|
|
|
{ |
|
|
|
return Collections.singletonList(""); |
|
|
|
} |
|
|
|
}; |
|
|
|
} else { |
|
|
|
return new RewardGenerator() |
|
|
|
{ |
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Extract variable info from a states file. |
|
|
|
*/ |
|
|
|
private void extractVarInfoFromStatesFile(File statesFile) throws PrismException |
|
|
|
{ |
|
|
|
String s, ss[]; |
|
|
|
int i, j, lineNum = 0; |
|
|
|
Module m; |
|
|
|
Declaration d; |
|
|
|
DeclarationType dt; |
|
|
|
// Var info |
|
|
|
int numVars; |
|
|
|
String varNames[]; |
|
|
|
int varMins[]; |
|
|
|
int varMaxs[]; |
|
|
|
int varRanges[]; |
|
|
|
Type varTypes[]; |
|
|
|
ModulesFile modulesFile; |
|
|
|
|
|
|
|
// open file for reading, automatic close when done |
|
|
|
try (BufferedReader in = new BufferedReader(new FileReader(statesFile))) { |
|
|
|
// read first line and extract var names |
|
|
|
s = in.readLine(); |
|
|
|
String s = in.readLine(); |
|
|
|
lineNum = 1; |
|
|
|
if (s == null) |
|
|
|
throw new PrismException("empty states file"); |
|
|
|
@ -143,13 +190,13 @@ public class ExplicitFiles2ModulesFile extends PrismComponent |
|
|
|
if (s.charAt(0) != '(' || s.charAt(s.length() - 1) != ')') |
|
|
|
throw new PrismException("badly formatted state"); |
|
|
|
s = s.substring(1, s.length() - 1); |
|
|
|
varNames = s.split(","); |
|
|
|
numVars = varNames.length; |
|
|
|
varNames = new ArrayList<String>(Arrays.asList(s.split(","))); |
|
|
|
numVars = varNames.size(); |
|
|
|
// create arrays to store info about vars |
|
|
|
varMins = new int[numVars]; |
|
|
|
varMaxs = new int[numVars]; |
|
|
|
varRanges = new int[numVars]; |
|
|
|
varTypes = new Type[numVars]; |
|
|
|
varTypes = new ArrayList<Type>(); |
|
|
|
// read remaining lines |
|
|
|
s = in.readLine(); |
|
|
|
lineNum++; |
|
|
|
@ -162,20 +209,21 @@ public class ExplicitFiles2ModulesFile extends PrismComponent |
|
|
|
numStates++; |
|
|
|
// split string |
|
|
|
s = s.substring(s.indexOf('(') + 1, s.indexOf(')')); |
|
|
|
ss = s.split(","); |
|
|
|
String[] ss = s.split(","); |
|
|
|
if (ss.length != numVars) |
|
|
|
throw new PrismException("wrong number of variables"); |
|
|
|
// for each variable... |
|
|
|
for (i = 0; i < numVars; i++) { |
|
|
|
// if this is the first state, establish variable type |
|
|
|
if (numStates == 1) { |
|
|
|
if (ss[i].equals("true") || ss[i].equals("false")) |
|
|
|
varTypes[i] = TypeBool.getInstance(); |
|
|
|
else |
|
|
|
varTypes[i] = TypeInt.getInstance(); |
|
|
|
if (ss[i].equals("true") || ss[i].equals("false")) { |
|
|
|
varTypes.add(TypeBool.getInstance()); |
|
|
|
} else { |
|
|
|
varTypes.add(TypeInt.getInstance()); |
|
|
|
} |
|
|
|
} |
|
|
|
// check for new min/max values (ints only) |
|
|
|
if (varTypes[i] instanceof TypeInt) { |
|
|
|
if (varTypes.get(i) instanceof TypeInt) { |
|
|
|
j = Integer.parseInt(ss[i]); |
|
|
|
if (numStates == 1) { |
|
|
|
varMins[i] = varMaxs[i] = j; |
|
|
|
@ -194,7 +242,7 @@ public class ExplicitFiles2ModulesFile extends PrismComponent |
|
|
|
} |
|
|
|
// compute variable ranges |
|
|
|
for (i = 0; i < numVars; i++) { |
|
|
|
if (varTypes[i] instanceof TypeInt) { |
|
|
|
if (varTypes.get(i) instanceof TypeInt) { |
|
|
|
varRanges[i] = varMaxs[i] - varMins[i]; |
|
|
|
// if range = 0, increment maximum - we don't allow zero-range variables |
|
|
|
if (varRanges[i] == 0) |
|
|
|
@ -208,115 +256,83 @@ public class ExplicitFiles2ModulesFile extends PrismComponent |
|
|
|
} catch (PrismException e) { |
|
|
|
throw new PrismException("Error detected (" + e.getMessage() + ") at line " + lineNum + " of states file \"" + statesFile + "\""); |
|
|
|
} |
|
|
|
// create modules file |
|
|
|
modulesFile = new ModulesFile(); |
|
|
|
m = new Module("M"); |
|
|
|
|
|
|
|
varList = new VarList(); |
|
|
|
for (i = 0; i < numVars; i++) { |
|
|
|
if (varTypes[i] instanceof TypeInt) { |
|
|
|
if (varTypes.get(i) instanceof TypeInt) { |
|
|
|
dt = new DeclarationInt(Expression.Int(varMins[i]), Expression.Int(varMaxs[i])); |
|
|
|
d = new Declaration(varNames[i], dt); |
|
|
|
d = new Declaration(varNames.get(i), dt); |
|
|
|
d.setStart(Expression.Int(varMins[i])); |
|
|
|
} else { |
|
|
|
dt = new DeclarationBool(); |
|
|
|
d = new Declaration(varNames[i], dt); |
|
|
|
d = new Declaration(varNames.get(i), dt); |
|
|
|
d.setStart(Expression.False()); |
|
|
|
} |
|
|
|
m.addDeclaration(d); |
|
|
|
varList.addVar(d, -1, null); |
|
|
|
} |
|
|
|
modulesFile.addModule(m); |
|
|
|
modulesFile.tidyUp(); |
|
|
|
|
|
|
|
return modulesFile; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Build a ModulesFile corresponding to a transitions file. |
|
|
|
* Extract variable info from a transitions file. |
|
|
|
*/ |
|
|
|
private ModulesFile createVarInfoFromTransFile(File transFile) throws PrismException |
|
|
|
private void extractVarInfoFromTransFile(File transFile) throws PrismException |
|
|
|
{ |
|
|
|
String s, ss[]; |
|
|
|
int lineNum = 0; |
|
|
|
Module m; |
|
|
|
Declaration d; |
|
|
|
DeclarationType dt; |
|
|
|
ModulesFile modulesFile; |
|
|
|
|
|
|
|
// open file for reading, automatic close when done |
|
|
|
// Open file for reading, automatic close when done |
|
|
|
try (BufferedReader in = new BufferedReader(new FileReader(transFile))) { |
|
|
|
// read first line and extract num states |
|
|
|
s = in.readLine(); |
|
|
|
lineNum = 1; |
|
|
|
// Read first line and extract num states |
|
|
|
String s = in.readLine(); |
|
|
|
if (s == null) |
|
|
|
throw new PrismException("empty transitions file"); |
|
|
|
s = s.trim(); |
|
|
|
ss = s.split(" "); |
|
|
|
String[] ss = s.split(" "); |
|
|
|
if (ss.length < 2) |
|
|
|
throw new PrismException(""); |
|
|
|
numStates = Integer.parseInt(ss[0]); |
|
|
|
} catch (IOException e) { |
|
|
|
throw new PrismException("File I/O error reading from \"" + transFile + "\""); |
|
|
|
} catch (NumberFormatException e) { |
|
|
|
throw new PrismException("Error detected at line " + lineNum + " of transition matrix file \"" + transFile + "\""); |
|
|
|
} catch (PrismException e) { |
|
|
|
throw new PrismException("Error detected (" + e.getMessage() + ") at line " + lineNum + " of transition matrix file \"" + transFile + "\""); |
|
|
|
throw new PrismException("Error detected at line 1 of transition matrix file \"" + transFile + "\""); |
|
|
|
} |
|
|
|
// create modules file |
|
|
|
modulesFile = new ModulesFile(); |
|
|
|
m = new Module("M"); |
|
|
|
dt = new DeclarationInt(Expression.Int(0), Expression.Int(numStates - 1)); |
|
|
|
d = new Declaration("x", dt); |
|
|
|
|
|
|
|
varList = new VarList(); |
|
|
|
DeclarationType dt = new DeclarationInt(Expression.Int(0), Expression.Int(numStates - 1)); |
|
|
|
Declaration d = new Declaration("x", dt); |
|
|
|
d.setStart(Expression.Int(0)); |
|
|
|
m.addDeclaration(d); |
|
|
|
modulesFile.addModule(m); |
|
|
|
modulesFile.tidyUp(); |
|
|
|
|
|
|
|
return modulesFile; |
|
|
|
varList.addVar(d, -1, null); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Create a LabelList from a labels file, i.e., a label definition per |
|
|
|
* label that is encountered in the file. |
|
|
|
* <br> |
|
|
|
* In the label definitions, the label expression is set to "false", |
|
|
|
* the actual information about the states belonging to the label |
|
|
|
* is later on directly stored in the model. |
|
|
|
* <br> |
|
|
|
* Extract names of labels from the labels file. |
|
|
|
* The "init" and "deadlock" labels are skipped, as they have special |
|
|
|
* meaning and are implicitly defined for all models. |
|
|
|
*/ |
|
|
|
private LabelList createLabelListFromLabelsFile(File labelsFile) throws PrismException |
|
|
|
private void extractLabelNamesFromLabelsFile(File labelsFile) throws PrismException |
|
|
|
{ |
|
|
|
LabelList list = new LabelList(); |
|
|
|
|
|
|
|
try (BufferedReader in = new BufferedReader(new FileReader(labelsFile))) { |
|
|
|
// read first line (label names) |
|
|
|
String labelNames = in.readLine(); |
|
|
|
// split |
|
|
|
// Read/parse first line (label names) |
|
|
|
// Looks like, e.g.: 0="init" 1="deadlock" 2="heads" 3="tails" 4="end" |
|
|
|
String labelsString = in.readLine(); |
|
|
|
Pattern label = Pattern.compile("(\\d+)=\"([^\"]+)\"\\s*"); |
|
|
|
Matcher matcher = label.matcher(labelNames); |
|
|
|
Matcher matcher = label.matcher(labelsString); |
|
|
|
labelNames = new ArrayList<>(); |
|
|
|
while (matcher.find()) { |
|
|
|
String labelName = matcher.group(2); |
|
|
|
|
|
|
|
// Skip built-in labels |
|
|
|
if (labelName.equals("init") || labelName.equals("deadlock")) { |
|
|
|
// "init" and "deadlock" are special labels that exist |
|
|
|
// in every model, so we don't need to add them to the ModulesFile |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
// TODO: Check that label name is valid identifier |
|
|
|
|
|
|
|
if (list.getLabelIndex(labelName) != -1) { |
|
|
|
throw new PrismException("duplicate label \"" + labelName + "\""); |
|
|
|
// Check legal and non-dupe |
|
|
|
if (!ExpressionIdent.isLegalIdentifierName(labelName)) { |
|
|
|
throw new PrismException("Illegal label name \"" + labelName + "\""); |
|
|
|
} |
|
|
|
|
|
|
|
Expression falseExpression = new ExpressionLiteral(TypeBool.getInstance(), false); |
|
|
|
list.addLabel(new ExpressionIdent(labelName), falseExpression); |
|
|
|
if (labelNames.contains(labelName)) { |
|
|
|
throw new PrismException("Duplicate label \"" + labelName + "\""); |
|
|
|
} |
|
|
|
labelNames.add(labelName); |
|
|
|
} |
|
|
|
} catch (IOException e) { |
|
|
|
throw new PrismException("File I/O error reading from \"" + labelsFile + "\""); |
|
|
|
} |
|
|
|
return list; |
|
|
|
} |
|
|
|
|
|
|
|
} |