Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import nl.esi.xtext.actions.actions.ActionList;
import nl.esi.xtext.actions.actions.AssignmentAction;
import nl.esi.xtext.actions.actions.ForAction;
import nl.esi.xtext.actions.actions.FunctionCall;
import nl.esi.xtext.actions.actions.IfAction;
import nl.esi.xtext.actions.actions.RecordFieldAssignmentAction;
import nl.esi.xtext.expressions.expression.Expression;
Expand Down Expand Up @@ -273,37 +274,36 @@ static String action(Action action, Function<String, String> variableRename) {
}

private static String action(Action action, Function<String, String> variableRename, String indent) {
if (action instanceof AssignmentAction) {
AssignmentAction a = (AssignmentAction) action;
if (action instanceof AssignmentAction a) {
// String variable = String.format("%s%s", variablePrefix.apply(a.getAssignment().getName()), a.getAssignment().getName());
String variable = String.format("%s", variableRename.apply(a.getAssignment().getName()));
// if(a.isSymbolic()) return String.format("%s = %s%s%s", variable, QUOTE, expression(a.getExp(), variablePrefix).replace("\"", "\\\""), QUOTE);
return String.format("%s = %s", variable, expression(a.getExp(), variableRename));
} else if (action instanceof RecordFieldAssignmentAction) {
RecordFieldAssignmentAction a = (RecordFieldAssignmentAction) action;
} else if (action instanceof RecordFieldAssignmentAction a) {
ExpressionRecordAccess access = (ExpressionRecordAccess) a.getFieldAccess();
String record = expression(access.getRecord(), variableRename);
String field = access.getField().getName();
String value = expression(a.getExp(), variableRename);
return String.format("%s[\"%s\"] = %s", record, field, value);
} else if(action instanceof IfAction) {
} else if(action instanceof IfAction act) {
var txt = new String();
var act = (IfAction) action;
txt += String.format("if %s:\n",expression(act.getGuard(), variableRename));
txt += indentActionList(act.getThenList(), variableRename, indent);
if(act.getElseList()!= null) {
txt += "else:\n";
txt += indentActionList(act.getElseList(), variableRename, indent);
}
return txt.trim();
} else if(action instanceof ForAction) {
} else if(action instanceof ForAction act) {
var txt = new String();
var act = (ForAction) action;
txt += String.format("for %s in %s:\n", act.getVar().getName(), expression(act.getExp(), variableRename));
txt += indentActionList(act.getDoList(), variableRename, indent);
return txt.trim();
}

} else if(action instanceof FunctionCall functionCall) {
var txt = new String();
txt += expression(functionCall.getExp());
return txt.trim();
}
throw new RuntimeException("Not supported");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import static extension nl.esi.xtext.common.lang.utilities.EcoreUtil3.*
import static extension nl.esi.xtext.types.utilities.TypeUtilities.*
import static extension org.eclipse.lsat.common.xtend.Queries.*
import static extension org.eclipse.xtext.EcoreUtil2.*
import nl.esi.xtext.actions.actions.FunctionCall

/**
* This class contains custom validation rules.
Expand Down Expand Up @@ -129,7 +130,7 @@ class ProductValidator extends AbstractProductValidator {
* All variables occurring in a guard expression must have been defined as inputs of an action
*/
@Check
def preventIlligalVariableAccess(Function function) {
def preventIllegalVariableAccess(Function function) {
for (update : function.updates) {
if (update.guard !== null) {
val Set<String> inputs = newHashSet
Expand Down Expand Up @@ -157,60 +158,64 @@ class ProductValidator extends AbstractProductValidator {
* Note that attributes of this variable may be referenced in the LHS expression (record field access)
*/
@Check
def void preventIlligalVariableAccess(Update update) {
def void preventIllegalVariableAccess(Update update) {
/* Variables occurring in the RHS of an update expression must have been defined as inputs of an action. */
for (updateOutVar : update.updateOutputVar.reject[act === null]) {
val inputs = update.fnInp.map[ref].toSet
val outputs = updateOutVar.fnOut.map[ref].toSet
updateOutVar.act.actions.forEach[preventIlligalVariableAccess(inputs, outputs)]
updateOutVar.act.actions.forEach[preventIllegalVariableAccess(inputs, outputs)]
}
}

private dispatch def void preventIlligalVariableAccess(AssignmentAction action, Set<Variable> inputs, Set<Variable> outputs) {
private dispatch def void preventIllegalVariableAccess(AssignmentAction action, Set<Variable> inputs, Set<Variable> outputs) {
if (!outputs.contains(action.assignment)) {
error('''Variable '«action.assignment.name»' is not defined as an output''', action, ActionsPackage.Literals.ASSIGNMENT_ACTION__ASSIGNMENT)
}
action.exp?.preventIlligalExpressionVariableAccess(inputs, Direction::input)
action.exp?.preventIllegalExpressionVariableAccess(inputs, Direction::input)
// As the variable is now assigned, we can also use it as input (imperative programming)
inputs += action.assignment
}

private dispatch def void preventIlligalVariableAccess(RecordFieldAssignmentAction action, Set<Variable> inputs, Set<Variable> outputs) {
action.fieldAccess?.preventIlligalExpressionVariableAccess(outputs, Direction::output)
action.exp?.preventIlligalExpressionVariableAccess(inputs, Direction::input)
private dispatch def void preventIllegalVariableAccess(RecordFieldAssignmentAction action, Set<Variable> inputs, Set<Variable> outputs) {
action.fieldAccess?.preventIllegalExpressionVariableAccess(outputs, Direction::output)
action.exp?.preventIllegalExpressionVariableAccess(inputs, Direction::input)
}

private dispatch def void preventIlligalVariableAccess(ForAction action, Set<Variable> inputs, Set<Variable> outputs) {
action.exp?.preventIlligalExpressionVariableAccess(inputs, Direction::input)
private dispatch def void preventIllegalVariableAccess(ForAction action, Set<Variable> inputs, Set<Variable> outputs) {
action.exp?.preventIllegalExpressionVariableAccess(inputs, Direction::input)
if (action.doList !== null) {
val doInputs = new HashSet(inputs)
doInputs += action.^var
action.doList.actions.forEach[preventIlligalVariableAccess(doInputs, outputs)]
action.doList.actions.forEach[preventIllegalVariableAccess(doInputs, outputs)]
}
}

private dispatch def void preventIlligalVariableAccess(IfAction action, Set<Variable> inputs, Set<Variable> outputs) {
action.guard?.preventIlligalExpressionVariableAccess(inputs, Direction::input)
private dispatch def void preventIllegalVariableAccess(IfAction action, Set<Variable> inputs, Set<Variable> outputs) {
action.guard?.preventIllegalExpressionVariableAccess(inputs, Direction::input)
if (action.thenList !== null) {
action.thenList.actions.forEach[preventIlligalVariableAccess(inputs, outputs)]
action.thenList.actions.forEach[preventIllegalVariableAccess(inputs, outputs)]
}
if (action.thenList !== null) {
action.thenList.actions.forEach[preventIlligalVariableAccess(inputs, outputs)]
action.thenList.actions.forEach[preventIllegalVariableAccess(inputs, outputs)]
}
}

private dispatch def void preventIllegalVariableAccess(FunctionCall action, Set<Variable> inputs, Set<Variable> outputs) {
action.exp?.preventIllegalExpressionVariableAccess(inputs, Direction::input)
}

private enum Direction { input, output }

private dispatch def void preventIlligalExpressionVariableAccess(ExpressionVariable exprVar, Set<Variable> variables, Direction direction) {
private dispatch def void preventIllegalExpressionVariableAccess(ExpressionVariable exprVar, Set<Variable> variables, Direction direction) {
if (!variables.contains(exprVar.variable)) {
error('''Variable '«exprVar.variable.name»' is not defined as an «direction»''', exprVar, ExpressionPackage.Literals.EXPRESSION_VARIABLE__VARIABLE)
}
}

private dispatch def void preventIlligalExpressionVariableAccess(Expression expr, Set<Variable> variables, Direction direction) {
private dispatch def void preventIllegalExpressionVariableAccess(Expression expr, Set<Variable> variables, Direction direction) {
// Finds child expressions, but allows other classes in between (e.g. Record Field)
val subExpressions = #[expr as EObject].walkTree[eContents].findNearest(Expression)
subExpressions.forEach[preventIlligalExpressionVariableAccess(variables, direction)]
subExpressions.forEach[preventIllegalExpressionVariableAccess(variables, direction)]
}

/**
Expand Down Expand Up @@ -305,6 +310,10 @@ class ProductValidator extends AbstractProductValidator {
action.exp.reportWarningOnAccess(RecordFieldKind::SYMBOLIC)[ field | '''Symbolic record field should not be used in «property»''']
}

dispatch def private void doCheckConcreteVariableUpdate(FunctionCall action, String property) {
action.exp.reportWarningOnAccess(RecordFieldKind::SYMBOLIC)[ field | '''Symbolic record field should not be used in «property»''']
}

private def reportWarningOnAccess(Expression expression, RecordFieldKind accessKind, (RecordField)=>String messageProducer) {
Iterators.concat(#[expression].iterator, expression.eAllContents).forEach [ eObject |
switch (it: eObject) {
Expand Down
Loading