diff --git a/eraser-base/src/main/jastadd/Expression.relast b/eraser-base/src/main/jastadd/Expression.relast
index 591e112a21b904c88a450e3a2cf92c982a6a4c03..3f09a660c0e0cc460cef2a0504245235443d7702 100644
--- a/eraser-base/src/main/jastadd/Expression.relast
+++ b/eraser-base/src/main/jastadd/Expression.relast
@@ -23,3 +23,5 @@ ComparingExpression : LogicalExpression ::= LeftOperand:NumberExpression RightOp
 abstract BinaryLogicalExpression : LogicalExpression ::= LeftOperand:LogicalExpression RightOperand:LogicalExpression ;
 AndExpression : BinaryLogicalExpression ;
 OrExpression : BinaryLogicalExpression ;
+
+ComparatorBox ::= <It:ComparatorType> ;
diff --git a/eraser-base/src/main/jastadd/Printing.jrag b/eraser-base/src/main/jastadd/Printing.jrag
index 60e77e3a00a122f60495df3f66216a08accfbdb2..b1761f17cdf003877f1f6b4ccb9b4314bfd51c8c 100644
--- a/eraser-base/src/main/jastadd/Printing.jrag
+++ b/eraser-base/src/main/jastadd/Printing.jrag
@@ -8,6 +8,9 @@ aspect Printing {
   eq Root.prettyPrint() {
     StringBuilder sb = new StringBuilder();
     sb.append(getSmartHomeEntityModel().prettyPrint());
+    for (Rule r : getRules()) {
+      sb.append(r.prettyPrint());
+    }
     sb.append(getMqttRoot().prettyPrint());
     sb.append(getInfluxRoot().prettyPrint());
     sb.append(getMachineLearningRoot().prettyPrint());
@@ -66,6 +69,7 @@ aspect Printing {
         .addOptional("category", hasCategory(), () -> getCategory().getName())
         .addOptional("topic", hasTopic(), () -> getTopic().getTopicString())
         .addOptionalPrettyPrint(getMetaData())
+        .addOptional("performance", hasFrequencySetting(), () -> getFrequencySetting().getID())
         .build();
   }
 
@@ -241,30 +245,92 @@ aspect Printing {
   syn String ParenthesizedLogicalExpression.prettyPrint()  = "(" + getOperand().prettyPrint() + ")";
   syn String NotExpression.prettyPrint()  = "!" + getOperand().prettyPrint();
   eq ComparingExpression.prettyPrint() {
-    switch (getComparator()) {
-      case NotEquals: return "(" + getLeftOperand().prettyPrint() + " != " + getRightOperand().prettyPrint() + ")";
-      case Equals: return "(" + getLeftOperand().prettyPrint() + " == " + getRightOperand().prettyPrint() + ")";
-      case LessThan: return "(" + getLeftOperand().prettyPrint() + " < " + getRightOperand().prettyPrint() + ")";
-      case GreaterThan: return "(" + getLeftOperand().prettyPrint() + " > " + getRightOperand().prettyPrint() + ")";
-      case LessOrEqualThan: return "(" + getLeftOperand().prettyPrint() + " <= " + getRightOperand().prettyPrint() + ")";
-      case GreaterOrEqualThan: return "(" + getLeftOperand().prettyPrint() + " >= " + getRightOperand().prettyPrint() + ")";
-      default: throw new IllegalArgumentException("Unknown compartor type: " + getComparator());
-    }
+    return "(" + getLeftOperand().prettyPrint() + Condition.operatorAsString(getComparator()) + getRightOperand().prettyPrint() + ")";
   }
   syn String AndExpression.prettyPrint() = "(" + getLeftOperand().prettyPrint() + " & " + getRightOperand().prettyPrint() + ")";
   syn String OrExpression.prettyPrint() = "(" + getLeftOperand().prettyPrint() + " | " + getRightOperand().prettyPrint() + ")";
   syn String Designator.prettyPrint() = getItem().getID();
+  
+  public static String Condition.operatorAsString(ComparatorType comparatorType) {
+    switch (comparatorType) {
+      case NotEquals: return "!=";
+      case Equals: return "==";
+      case LessThan: return "<";
+      case GreaterThan: return ">";
+      case LessOrEqualThan: return "<=";
+      case GreaterOrEqualThan: return ">=";
+      default: throw new IllegalArgumentException("Unknown comparator type: " + comparatorType);
+    }
+  }
 
   // Rules
   eq Rule.prettyPrint() {
     return new MemberPrinter("Rule")
-        .addIds("TriggeringItems", getObserverList().size(), getObserverList(),
-                  io -> io.observedItem().getID())
-        .addNodes("Condition", getNumCondition(), getConditionList(),
-                  Condition::toString)
-        .addNodes("Action", getNumAction(), getActionList(),
-                  Action::toString)
+        .addRequired("name", getID())
+        .addCustomIterable("items", getObserverList().size(), getObserverList(),
+                  io -> io.observedItem().getID(),true)
+        .addIterable(new JastAddList<>().addAll(getConditionList()).addAll(getActionList()))
         .build();
   }
 
+  //ItemStateCheck
+  eq ItemStateCheckCondition.prettyPrint() {
+    return "Condition: ItemStateCheck " + getItemStateCheck().prettyPrint();
+  }
+
+  //ItemStateNumberCheck
+  eq ItemStateNumberCheck.prettyPrint() {
+      return Condition.operatorAsString(getComparator()) + " " + getValue();
+  }
+
+  //ItemStateStringCheck
+  eq ItemStateStringCheck.prettyPrint() {
+      return Condition.operatorAsString(getComparator()) + " " + "\""+getValue()+"\"";
+  }
+
+  //ItemStateChangeCondition
+  eq ItemStateChangeCondition.prettyPrint() {
+      return "Condition: ItemStateChange " + getItem().getID();
+  }
+
+  //ExpressionCondition
+  eq ExpressionCondition.prettyPrint() {
+      return "Condition: Expression " + getLogicalExpression().prettyPrint();
+  }
+
+
+  //TriggerRuleAction
+  eq TriggerRuleAction.prettyPrint() {
+    return "Action: TriggerRule " + getRule().getID();
+  }
+
+  //NoopAction
+  eq NoopAction.prettyPrint() {
+      return "Action: Noop";
+  }
+
+  //SetStateFromConstantStringAction
+  eq SetStateFromConstantStringAction.prettyPrint() {
+      return "Action: SetStateFromConstant " + getAffectedItem().getID() + " \"" + getNewState()+ "\"";
+  }
+
+  //SetStateFromTriggeringItemAction
+  eq SetStateFromTriggeringItemAction.prettyPrint() {
+      return "Action: SetStateFromTrigger " + getAffectedItem().getID();
+  }
+  
+  //MultiplyDoubleToStateAction
+  eq MultiplyDoubleToStateAction.prettyPrint() {
+      return "Action: SetStateFromTrigger " + getAffectedItem().getID() + " " + getMultiplier();
+  }
+
+  //SetStateFromExpressionAction
+  eq SetStateFromExpressionAction.prettyPrint() {
+      return "Action: StateFromExpression " + getNumberExpression().prettyPrint() + " " + getAffectedItem().getID();
+  }
+
+  
+  
+
+  
 }
diff --git a/eraser-base/src/main/jastadd/Resolving.jrag b/eraser-base/src/main/jastadd/Resolving.jrag
index 7d6656121956a1c3014d1fed3dbc14832b1556d1..5a1a8ca939e32054970efc9b032d305ba43798ab 100644
--- a/eraser-base/src/main/jastadd/Resolving.jrag
+++ b/eraser-base/src/main/jastadd/Resolving.jrag
@@ -88,6 +88,16 @@ aspect Resolving {
     return java.util.Optional.empty();
   }
 
+    //--- resolveRule ---
+  syn java.util.Optional<Rule> Root.resolveRule(String ruleName) {
+    for (Rule rule : getRules()) {
+      if (rule.getID().equals(ruleName)) {
+        return java.util.Optional.of(rule);
+      }
+    }
+    return java.util.Optional.empty();
+  }
+
   //--- resolveActivity ---
   syn java.util.Optional<Activity> Root.resolveActivity(int identifier) {
     for (Activity activity : getMachineLearningRoot().getActivityList()) {
@@ -136,6 +146,11 @@ aspect Resolving {
     return getRoot().getSmartHomeEntityModel().resolveFrequencySetting(id).orElseThrow(() -> new RuntimeException("FrequencySetting '" + id + "' not found!"));
   }
 
+  // _._ -> Rule
+  refine RefResolverStubs eq ASTNode.globallyResolveRuleByToken(String id) {
+    return getRoot().resolveRule(id).orElseThrow(() -> new RuntimeException("Rule '" + id + "' not found!"));
+  }
+
   // Thing.Channel* -> Channel
   refine RefResolverStubs eq Thing.resolveChannelByToken(String id, int position) {
     return containingSmartHomeEntityModel().resolveChannel(id).orElseThrow(() -> new RuntimeException("Channel '" + id + "' not found!"));
diff --git a/eraser-base/src/main/jastadd/Rules.jrag b/eraser-base/src/main/jastadd/Rules.jrag
index e2acfca41c585dedfc71bd56c0772abcef36d247..1036024413db67e7b6d7adb0fe4d405868d8442f 100644
--- a/eraser-base/src/main/jastadd/Rules.jrag
+++ b/eraser-base/src/main/jastadd/Rules.jrag
@@ -76,21 +76,16 @@ aspect Rules {
   public void NoopAction.applyFor(Item item) {
     // empty by design
   }
-  public void LambdaAction.applyFor(Item item) {
-    getLambda().accept(item);
-  }
+
   public void TriggerRuleAction.applyFor(Item item) {
     getRule().trigger(item);
   }
-  public void SetStateFromExpression.applyFor(Item item) {
+  public void SetStateFromExpressionAction.applyFor(Item item) {
     getAffectedItem().setStateFromDouble(getNumberExpression().eval());
   }
   public void SetStateFromConstantStringAction.applyFor(Item item) {
     getAffectedItem().setStateFromString(getNewState());
   }
-  public void SetStateFromLambdaAction.applyFor(Item item) {
-    getAffectedItem().setStateFromString(getNewStateProvider().get());
-  }
   public void SetStateFromTriggeringItemAction.applyFor(Item item) {
     Item target = getAffectedItem();
     if (target==item) {
@@ -98,9 +93,6 @@ aspect Rules {
     }
     item.copyStateTo(target);
   }
-  public void SetStateFromItemsAction.applyFor(Item item) {
-    getAffectedItem().setStateFromString(getCombinator().apply(getSourceItems()));
-  }
   public void AddDoubleToStateAction.applyFor(Item item) {
     getAffectedItem().setStateFromDouble(getAffectedItem().getStateAsDouble() + getIncrement());
   }
@@ -116,7 +108,7 @@ aspect StateSyncGroup {
       Rule rule = new Rule();
 
       for (Item item : getTargetItemList()) {
-        rule.addAction(new SetStateFromTriggeringItemAction(item));
+        rule.addAction(new SetStateFromTriggeringItemAction().setAffectedItem(item));
         rule.addObserver(item.getItemObserver());
       }   
 
diff --git a/eraser-base/src/main/jastadd/Rules.relast b/eraser-base/src/main/jastadd/Rules.relast
index e6c3ab0138742149407d3a6279a958c2cc551ee4..b78d52a079f206a31064f1f98c03672a51c666c8 100644
--- a/eraser-base/src/main/jastadd/Rules.relast
+++ b/eraser-base/src/main/jastadd/Rules.relast
@@ -1,5 +1,5 @@
 // --- New ECA rules ---
-Rule ::= Condition* Action* ;
+Rule : ModelElement ::= Condition* Action* ;
 abstract Condition ;
 ItemStateCheckCondition : Condition ::= ItemStateCheck ;
 ItemStateChangeCondition : Condition ;
@@ -7,7 +7,6 @@ rel ItemStateChangeCondition.Item -> Item;
 ExpressionCondition : Condition ::= LogicalExpression ;
 abstract Action ;
 NoopAction : Action ;
-LambdaAction : Action ::= <Lambda:Action2EditConsumer> ;
 
 TriggerRuleAction : Action ;
 rel TriggerRuleAction.Rule -> Rule ;
@@ -15,14 +14,11 @@ rel TriggerRuleAction.Rule -> Rule ;
 abstract SetStateAction : Action ;
 rel SetStateAction.AffectedItem -> Item ;
 
-SetStateFromExpression : SetStateAction ::= NumberExpression ;
+SetStateFromExpressionAction : SetStateAction ::= NumberExpression ;
 
 SetStateFromConstantStringAction : SetStateAction ::= <NewState:String> ;
-SetStateFromLambdaAction : SetStateAction ::= <NewStateProvider:NewStateProvider> ;
 SetStateFromTriggeringItemAction : SetStateAction ::= ;
 
-SetStateFromItemsAction : SetStateAction ::= <Combinator:ItemsToStringFunction> ;
-rel SetStateFromItemsAction.SourceItem* -> Item ;
 
 AddDoubleToStateAction : SetStateAction ::= <Increment:double> ;
 MultiplyDoubleToStateAction : SetStateAction ::= <Multiplier:double> ;
@@ -33,3 +29,7 @@ rel ItemObserver.TriggeredRule* <-> Rule.Observer* ;
 FrequencySetting : LabelledModelElement ::= <EventProcessingFrequency:double> ;
 StateSyncGroup : Rule ;
 rel StateSyncGroup.TargetItem* -> Item;
+
+ConditionActionPack ::= ;
+rel ConditionActionPack.Condition* -> Condition;
+rel ConditionActionPack.Action* -> Action; 
\ No newline at end of file
diff --git a/eraser-base/src/main/jastadd/eraser.flex b/eraser-base/src/main/jastadd/eraser.flex
index 54dff35b310e573c8efe700596f57b36b276235a..c1c6b52ebbc7c591f22b87d384e0d20f1026dcbe 100644
--- a/eraser-base/src/main/jastadd/eraser.flex
+++ b/eraser-base/src/main/jastadd/eraser.flex
@@ -71,12 +71,23 @@ Comment = "//" [^\n\r]+
 "RollerShutter"  { return sym(Terminals.ROLLER_SHUTTER); }
 "String"       { return sym(Terminals.STRING); }
 "Switch"       { return sym(Terminals.SWITCH); }
+// actions and conditions
+"ItemStateCheck"       { return sym(Terminals.ITEM_STATE_CHECK); }
+"ItemStateChange"       { return sym(Terminals.ITEM_STATE_CHANGE); }
+"Expression"       { return sym(Terminals.EXPRESSION); }
+"Noop"       { return sym(Terminals.NOOP_ACTION); }
+"TriggerRule"       { return sym(Terminals.TRIGGER_RULE_ACTION); }
+"SetStateFromConstant"       { return sym(Terminals.STATE_FROM_CONST_ACTION); }
+"SetStateFromTrigger"       { return sym(Terminals.STATE_FROM_TRIGGER_ACTION); }
+"StateFromExpression"       { return sym(Terminals.STATE_FROM_EXPRESSION_ACTION); }
 // within specification
+"Action"   { return sym(Terminals.ACTION); }
 "activities"   { return sym(Terminals.ACTIVITIES); }
 "aggregation"  { return sym(Terminals.AGGREGATION); }
 "category"     { return sym(Terminals.CATEGORY); }
 "channels"     { return sym(Terminals.CHANNELS); }
 "channelTypes" { return sym(Terminals.CHANNEL_TYPES); }
+"Condition"   { return sym(Terminals.CONDITION); }
 "context"      { return sym(Terminals.CONTEXT); }
 "dbName"       { return sym(Terminals.DB_NAME); }
 "default"      { return sym(Terminals.DEFAULT); }
@@ -84,6 +95,7 @@ Comment = "//" [^\n\r]+
 "groups"       { return sym(Terminals.GROUPS); }
 "host"         { return sym(Terminals.HOST); }
 "id"           { return sym(Terminals.ID); }
+"name"           { return sym(Terminals.LABELNAME); }
 "incoming"     { return sym(Terminals.INCOMING); }
 "items"        { return sym(Terminals.ITEMS); }
 "itemType"     { return sym(Terminals.ITEM_TYPE); }
diff --git a/eraser-base/src/main/jastadd/eraser.parser b/eraser-base/src/main/jastadd/eraser.parser
index 0da0601d2136e5ed105eddd81751da9d38e848c8..456027b12235de42421b539218f7d82fca904871 100644
--- a/eraser-base/src/main/jastadd/eraser.parser
+++ b/eraser-base/src/main/jastadd/eraser.parser
@@ -95,7 +95,7 @@ import java.util.HashMap;
         rule.addCondition(condition);
         break;
       case AltGoals.number_expression:
-        SetStateFromExpression action = new SetStateFromExpression();
+        SetStateFromExpressionAction action = new SetStateFromExpressionAction();
         action.setNumberExpression(exp.asNumberExpression());
         rule.addAction(action);
         break;
@@ -127,7 +127,7 @@ Root root =
   |  influx_root.ir root.r              {: r.setInfluxRoot(ir); return r; :}
   |  machine_learning_root.ml root.r    {: r.setMachineLearningRoot(ml); return r; :}
   |  rule.rule root.r                   {: r.addRule(rule); return r; :}
-  |  frequency_setting.ip root.r        {: insertZero(r.getSmartHomeEntityModel().getFrequencySettingList(), ip); return r; :}
+  |  frequency_setting.fs root.r        {: insertZero(r.getSmartHomeEntityModel().getFrequencySettingList(), fs); return r; :}
   |                                     {: return Root.createEmptyRoot(); :}
   ;
 
@@ -157,12 +157,7 @@ LogicalExpression logical_expression =
   | LB_ROUND logical_expression.a OR    logical_expression.b RB_ROUND    {: return new OrExpression(a, b); :}
   | EXCLAMATION logical_expression.e                                     {: return new NotExpression(e); :}
   | LB_ROUND logical_expression.e RB_ROUND                               {: return new ParenthesizedLogicalExpression(e); :}
-  | LB_ROUND number_expression.a LT    number_expression.b RB_ROUND      {: return new ComparingExpression(a, b, ComparatorType.LessThan); :}
-  | LB_ROUND number_expression.a LE    number_expression.b RB_ROUND      {: return new ComparingExpression(a, b, ComparatorType.LessOrEqualThan); :}
-  | LB_ROUND number_expression.a EQ    number_expression.b RB_ROUND      {: return new ComparingExpression(a, b, ComparatorType.Equals); :}
-  | LB_ROUND number_expression.a NE    number_expression.b RB_ROUND      {: return new ComparingExpression(a, b, ComparatorType.NotEquals); :}
-  | LB_ROUND number_expression.a GE    number_expression.b RB_ROUND      {: return new ComparingExpression(a, b, ComparatorType.GreaterOrEqualThan); :}
-  | LB_ROUND number_expression.a GT    number_expression.b RB_ROUND      {: return new ComparingExpression(a, b, ComparatorType.GreaterThan); :}
+  | LB_ROUND number_expression.a comparator.cp    number_expression.b RB_ROUND      {: return new ComparingExpression(a, b, cp.getIt()); :}
   ;
 
 NumberLiteralExpression literal_expression =
@@ -372,21 +367,71 @@ MachineLearningRoot machine_learning_root_body =
 
 // Rule: condition=condition action=a1 action=a2;
 // (only allow one condition and action for now)
+//Rule rule =
+//    RULE COLON CONDITION EQUALS condition.c action.a SEMICOLON    {: Rule result = new Rule();
+//                                                                     result.addCondition(c);
+//                                                                     result.addAction(a);
+//                                                                     return result; :}
+//  ;
+
+
+
 Rule rule =
-    RULE COLON CONDITION EQUALS condition.c action.a SEMICOLON    {: Rule result = new Rule();
-                                                                     result.addCondition(c);
-                                                                     result.addAction(a);
-                                                                     return result; :}
+     RULE COLON rule_body.rb SEMICOLON    {: return rb; :}
   ;
 
-Condition condition =
-    logical_expression.be    {: return new ExpressionCondition(be); :}
+
+//Rule: name="testRue1" items=["ITEM_ID","ITEM_ID"] conditions=["CONDITION", "CONDITION"] action=["ACTION", "ACTION"]
+// (only allow one condition and action for now)
+Rule rule_body =
+    LABELNAME EQUALS TEXT.n rule_body.r                              {: return r.setID(n); :}
+  | ITEMS EQUALS string_list.items rule_body.r                       {: items.forEach(i -> r.activateFor(createItemPlaceHolder(i))); return r; :}
+  | LB_SQUARE capack.cap RB_SQUARE rule_body.r                       {: return r.setActionList(new JastAddList().addAll(cap.getActions())).setConditionList(new JastAddList().addAll(cap.getConditions())); :}
+  |                                                                  {: return new Rule(); :}
   ;
 
-// TODO implement action cases
-Action action = {: return new NoopAction(); :}
+
+ConditionActionPack capack = 
+      COMMA capack.cap {: return cap; :}
+    | CONDITION COLON condition.c capack.cap {: cap.addCondition(c); return cap; :}
+    | ACTION COLON action.a capack.cap {: cap.addAction(a); return cap; :}
+    |                     {: return new ConditionActionPack(); :}
+    ;
+
+Condition condition = 
+      ITEM_STATE_CHECK isccondition.iscc {: return iscc; :}
+    | ITEM_STATE_CHANGE NAME.n {: return new ItemStateChangeCondition(Item.createRef(n)); :}
+    | EXPRESSION logical_expression.le {: return new ExpressionCondition(le); :}
+    
   ;
 
+ItemStateCheckCondition isccondition = 
+    comparator.cp REAL.r {: return new ItemStateCheckCondition().setItemStateCheck(new ItemStateNumberCheck().setValue(Double.parseDouble(r)).setComparator(cp.getIt()));  :}
+  | comparator.cp TEXT.n {: return new ItemStateCheckCondition().setItemStateCheck(new ItemStateStringCheck().setValue(n).setComparator(cp.getIt()));  :}
+
+ComparatorBox comparator = 
+    NE {: return new ComparatorBox(ComparatorType.NotEquals); :}
+  | EQ {: return new ComparatorBox(ComparatorType.Equals); :}
+  | LT {: return new ComparatorBox(ComparatorType.LessThan); :}
+  | GT {: return new ComparatorBox(ComparatorType.GreaterThan); :}
+  | LE {: return new ComparatorBox(ComparatorType.LessOrEqualThan); :}
+  | GE {: return new ComparatorBox(ComparatorType.GreaterOrEqualThan); :}
+  ;
+  
+
+   
+Action action = 
+  TRIGGER_RULE_ACTION NAME.n {: return new TriggerRuleAction().setRule(Rule.createRef(n)); :}
+  | NOOP_ACTION {:return new NoopAction(); :}
+  | STATE_FROM_CONST_ACTION  NAME.i TEXT.s {: return new SetStateFromConstantStringAction().setNewState(s).setAffectedItem(Item.createRef(i)); :}
+  | STATE_FROM_TRIGGER_ACTION  NAME.i {: return new SetStateFromTriggeringItemAction().setAffectedItem(Item.createRef(i)); :}
+  | STATE_FROM_TRIGGER_ACTION  NAME.i REAL.r {: return new MultiplyDoubleToStateAction().setMultiplier(Double.parseDouble(r)).setAffectedItem(Item.createRef(i)); :}
+  | STATE_FROM_EXPRESSION_ACTION  number_expression.ne NAME.i {: return new SetStateFromExpressionAction().setNumberExpression(ne).setAffectedItem(Item.createRef(i)); :}
+  ;
+
+
+
+
 // Util: StringList, StringKeyMap, IntegerKeyMap
 StringList string_list =
      LB_SQUARE string_list_body.slb RB_SQUARE         {: return slb; :}
diff --git a/eraser-base/src/main/jastadd/shem.jrag b/eraser-base/src/main/jastadd/shem.jrag
index 1eb0dfa753a45b36fe7047e662a6577b8b27eea5..bed14719588736d3c819f94d5886a2cdd0f17ae7 100644
--- a/eraser-base/src/main/jastadd/shem.jrag
+++ b/eraser-base/src/main/jastadd/shem.jrag
@@ -28,6 +28,7 @@ aspect SmartHomeEntityModel {
       result.setID(this.getID());
       result.setLabel(this.getLabel());
       result.setMetaData(this.getMetaData());
+      result.setFrequencySetting(this.getFrequencySetting());
       if (this.hasTopic()) {
         result.setTopic(this.getTopic());
       }
diff --git a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/util/MemberPrinter.java b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/util/MemberPrinter.java
index 53acf15253fcfc3a91067ca332ea8e2f2cdf7642..da6b04f969d8f48e7eabb187ea83798e0a1d5ef6 100644
--- a/eraser-base/src/main/java/de/tudresden/inf/st/eraser/util/MemberPrinter.java
+++ b/eraser-base/src/main/java/de/tudresden/inf/st/eraser/util/MemberPrinter.java
@@ -5,9 +5,13 @@ import de.tudresden.inf.st.eraser.jastadd.model.ModelElement;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.function.Function;
 import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 /**
  * Fluent API to pretty-print ASTNodes in a line-based fashion.
@@ -91,7 +95,7 @@ public class MemberPrinter {
    * @param listOfNodes the list of nodes
    * @param mapping a function to map a node to a String
    */
-  private <T extends ASTNode<?>> void concatNodes(Iterable<T> listOfNodes,
+  private <T> void concatNodes(Iterable<T> listOfNodes,
                                                Function<T, String> mapping,
                                                boolean quote) {
     boolean first = true;
@@ -129,9 +133,25 @@ public class MemberPrinter {
    * @return this
    */
   public MemberPrinter addIds(String name, int count, Iterable<? extends ModelElement> listOfElements) {
+    return addCustomIterable(name,count,listOfElements,ModelElement::getID,true);
+  }
+
+  /**
+   * Appends model elements to the builder, if count is positive.
+   * @param name           The name of the member
+   * @param count          The number of items in the listOfElements
+   * @param listOfElements The list of model elements
+   * @param printPart      The function that is called on the elements of the list
+   * @return this
+   */
+  public <T> MemberPrinter addCustomIterable(String name, int count, Iterable<T> listOfElements, Function<T,String> printPart,boolean quote) {
     if (count > 0) {
-      sb.append(' ').append(name).append("=[");
-      concatIds(listOfElements);
+      sb.append(' ');
+      if (name!=null && !name.isEmpty()) {
+        sb.append(name).append("=");
+      }
+      sb.append("[");
+      concatNodes(listOfElements,printPart,quote);
       sb.append("]");
       this.empty = false;
     }
@@ -139,13 +159,37 @@ public class MemberPrinter {
   }
 
   /**
-   * Appends nodes to the builder, if count is positive.
-   * @param name        The name of the member
-   * @param count       The number of items in the listOfNodes
-   * @param listOfNodes The list of nodes
-   * @param mapping     A function to map a node to a ModelElement
+   * Appends model elements to the builder, if count is positive. "Name=..." won't appear
+   * @param count          The number of items in the listOfElements
+   * @param listOfElements The list of model elements
+   * @param printPart      The function that is called on the elements of the list
    * @return this
    */
+  public <T> MemberPrinter addCustomIterable(int count, Iterable<T> listOfElements, Function<T,String> printPart,boolean quote) {
+    return addCustomIterable(null,count,listOfElements,printPart,quote);
+  }
+  public <T> MemberPrinter addCustomIterable(String name, Iterable<T> listOfElements, Function<T,String> printPart,boolean quote) {
+    List<T> list = StreamSupport.stream(listOfElements.spliterator(), false)
+            .collect(Collectors.toList());
+    return addCustomIterable(name,list.size(),list,printPart,quote);
+  }
+  public <T extends ASTNode<?>> MemberPrinter addIterable(Iterable<T> listOfElements) {
+    return addIterable(null,listOfElements);
+  }
+
+  public <T extends ASTNode<?>> MemberPrinter addIterable(String name,Iterable<T> listOfElements) {
+    return addCustomIterable(name,listOfElements,ASTNode::prettyPrint,false);
+  }
+
+
+    /**
+     * Appends nodes to the builder, if count is positive.
+     * @param name        The name of the member
+     * @param count       The number of items in the listOfNodes
+     * @param listOfNodes The list of nodes
+     * @param mapping     A function to map a node to a ModelElement
+     * @return this
+     */
   public <T extends ASTNode<?>> MemberPrinter addIds(
       String name, int count, Iterable<T> listOfNodes,
       Function<T, String> mapping) {
@@ -254,7 +298,7 @@ public class MemberPrinter {
    * @return this
    */
   public MemberPrinter addNonDefault(String name, Object actualValue, Object defaultValue) {
-    if (!actualValue.equals(defaultValue)) {
+    if (actualValue==null || !actualValue.equals(defaultValue)) {
       this.empty = false;
       return add(name, actualValue);
     }
diff --git a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/RulesTest.java b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/RulesTest.java
index a8c1f727a599a1f529173428dee1d11fa5974156..fa91036013bab2f16cf0e4c1428b1b5c7e318e60 100644
--- a/eraser-base/src/test/java/de/tudresden/inf/st/eraser/RulesTest.java
+++ b/eraser-base/src/test/java/de/tudresden/inf/st/eraser/RulesTest.java
@@ -225,8 +225,8 @@ public class RulesTest {
     Rule rule = new Rule();
     ItemStateNumberCheck check1 = new ItemStateNumberCheck(ComparatorType.GreaterOrEqualThan, 4);
     ItemStateNumberCheck check2 = new ItemStateNumberCheck(ComparatorType.LessThan, 6);
-    rule.addCondition(new ItemStateCheckCondition(check1));
-    rule.addCondition(new ItemStateCheckCondition(check2));
+    rule.addCondition(new ItemStateCheckCondition().setItemStateCheck(check1));
+    rule.addCondition(new ItemStateCheckCondition().setItemStateCheck(check2));
     CountingAction counter = new CountingAction();
     rule.addAction(counter);
     root.addRule(rule);
@@ -540,7 +540,7 @@ public class RulesTest {
     CountingAction counter2 = new CountingAction();
     ruleB.addAction(counter2);
 
-    ruleA.addAction(new TriggerRuleAction(ruleB));
+    ruleA.addAction(new TriggerRuleAction().setRule(ruleB));
 
     root.addRule(ruleA);
     root.addRule(ruleB);
@@ -576,7 +576,7 @@ public class RulesTest {
     NumberItem item2 = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 4, true);
 
     Rule rule = new Rule();
-    rule.addAction(new SetStateFromConstantStringAction(item2, "5"));
+    rule.addAction(new SetStateFromConstantStringAction().setNewState("5").setAffectedItem(item2));
     CountingAction counter = new CountingAction();
     rule.addAction(counter);
     root.addRule(rule);
@@ -597,44 +597,6 @@ public class RulesTest {
     }
   }
 
-  @Test
-  public void testSetStateFromLambdaAction() {
-    ValuedStateProvider provider = new ValuedStateProvider();
-    TestUtils.ModelAndItem modelAndItem = createModelAndItem(0);
-    Root root = modelAndItem.model.getRoot();
-    NumberItem item = modelAndItem.item;
-    NumberItem item2 = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 3, true);
-
-    Rule rule = new Rule();
-    rule.addAction(new SetStateFromLambdaAction(item2, provider));
-    CountingAction counter = new CountingAction();
-    rule.addAction(counter);
-    root.addRule(rule);
-    rule.activateFor(item);
-
-    assertEquals(3, item2.asItemWithDoubleState().getState(), DELTA, "Affected item not initialized correctly");
-
-    provider.value = 4;
-    setState(item, 1);
-    assertEquals(1, counter.get(item), "Change of item state should trigger the rule");
-    assertEquals(4, item2.asItemWithDoubleState().getState(), DELTA, "Change of item state should set the state of the affected item");
-
-    provider.value = 4;
-    setState(item, 2);
-    assertEquals(2, counter.get(item), "Change of item state should trigger the rule");
-    assertEquals(4, item2.asItemWithDoubleState().getState(), DELTA, "Change of item state should set the state of the affected item");
-
-    provider.value = 5;
-    setState(item, 2);
-    assertEquals(2, counter.get(item), "Change of item to same state should not trigger the rule");
-    assertEquals(4, item2.asItemWithDoubleState().getState(), DELTA, "Change of item to same state should not set the state of the affected item");
-
-    provider.value = 5;
-    setState(item, 3);
-    assertEquals(3, counter.get(item), "Change of item state should trigger the rule");
-    assertEquals(5, item2.asItemWithDoubleState().getState(), DELTA, "Change of item state should set the state of the affected item");
-  }
-
   @Test
   public void testSetStateFromTriggeringItemAction() {
     TestUtils.ModelAndItem modelAndItem = createModelAndItem(3);
@@ -644,7 +606,7 @@ public class RulesTest {
 
     Rule rule = new Rule();
     CountingAction counter = new CountingAction();
-    rule.addAction(new SetStateFromTriggeringItemAction(item2));
+    rule.addAction(new SetStateFromTriggeringItemAction().setAffectedItem(item2));
     rule.addAction(counter);
     root.addRule(rule);
     rule.activateFor(item);
@@ -665,65 +627,6 @@ public class RulesTest {
     assertEquals("7.0", item2.getState(), "Change of item state should set the state of the affected item");
   }
 
-  @Test
-  public void testSetStateFromItemsAction() {
-    TestUtils.ModelAndItem modelAndItem = createModelAndItem(3);
-    Root root = modelAndItem.model.getRoot();
-    NumberItem item = modelAndItem.item;
-    NumberItem item2 = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 4, true);
-    StringItem affectedItem = addStringItem(root.getSmartHomeEntityModel(), "1");
-
-    Rule rule = new Rule();
-    SetStateFromItemsAction action = new SetStateFromItemsAction(items ->
-        Long.toString(StreamSupport.stream(items.spliterator(), false)
-            .mapToLong(inner -> (long) inner.asItemWithDoubleState().getState())
-            .sum()));
-    action.addSourceItem(item);
-    action.addSourceItem(item2);
-    action.setAffectedItem(affectedItem);
-    rule.addAction(action);
-    CountingAction counter = new CountingAction();
-    rule.addAction(counter);
-    root.addRule(rule);
-    rule.activateFor(item);
-
-    assertEquals(0, counter.get(item), "Counter not initialized correctly");
-    assertEquals("1", affectedItem.getState(), "Affected item not initialized correctly");
-
-    // 5 + 4 = 9
-    setState(item, 5);
-    assertEquals(1, counter.get(item), "Change of item state should trigger the rule");
-    assertEquals("9", affectedItem.getState(), "Change of item state should set the state of the affected item");
-
-    // still 5 + 4 = 9, as rule does not trigger for item2
-    setState(item2, 5);
-    assertEquals(1, counter.get(item), "Change of item2 state should not trigger the rule");
-    assertEquals("9", affectedItem.getState(), "Change of item2 state should not set the state of the affected item");
-
-    // still 5 + 4 = 9, as rule should not trigger
-    setState(item, 5);
-    assertEquals(1, counter.get(item), "Change of item to same state should not trigger the rule");
-    assertEquals("9", affectedItem.getState(), "Change of item to same state should not set the state of the affected item");
-
-    // 7 + 5 = 12
-    setState(item, 7);
-    assertEquals(2, counter.get(item), "Change of item state should trigger the rule");
-    assertEquals("12", affectedItem.getState(), "Change of item state should set the state of the affected item");
-
-    // add new item to sum
-    NumberItem item3 = TestUtils.addItemTo(root.getSmartHomeEntityModel(), -4, true);
-    action.addSourceItem(item3);
-
-    // still 7 + 5 = 12, as rule should not trigger
-    setState(item, 7);
-    assertEquals(2, counter.get(item), "Change of item to same state should not trigger the rule");
-    assertEquals("12", affectedItem.getState(), "Change of item to same state should not set the state of the affected item");
-
-    // 8 + 5 - 4 = 9
-    setState(item, 8);
-    assertEquals(3, counter.get(item), "Change of item state should trigger the rule");
-    assertEquals("9", affectedItem.getState(), "Change of item state should set the state of the affected item");
-  }
 
   @Test
   public void testAddDoubleToStateAction() {
@@ -733,7 +636,7 @@ public class RulesTest {
     NumberItem affectedItem = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 4, true);
 
     Rule rule = new Rule();
-    rule.addAction(new AddDoubleToStateAction(affectedItem, 2));
+    rule.addAction(new AddDoubleToStateAction().setIncrement(2).setAffectedItem(affectedItem));
     CountingAction counter = new CountingAction();
     rule.addAction(counter);
     root.addRule(rule);
@@ -766,7 +669,7 @@ public class RulesTest {
     NumberItem affectedItem = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 4, true);
 
     Rule rule = new Rule();
-    rule.addAction(new MultiplyDoubleToStateAction(affectedItem, 2));
+    rule.addAction(new MultiplyDoubleToStateAction().setMultiplier(2).setAffectedItem(affectedItem));
     CountingAction counter = new CountingAction();
     rule.addAction(counter);
     root.addRule(rule);
@@ -800,16 +703,16 @@ public class RulesTest {
     NumberItem affectedItem = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 5, true);
 
     Rule ruleA = new Rule();
-    ruleA.addAction(new AddDoubleToStateAction(affectedItem, 2));
+    ruleA.addAction(new AddDoubleToStateAction().setIncrement(2).setAffectedItem(affectedItem));
     CountingAction counterA = new CountingAction();
     ruleA.addAction(counterA);
 
     Rule ruleB = new Rule();
-    ruleB.addAction(new MultiplyDoubleToStateAction(affectedItem, 3));
+    ruleB.addAction(new MultiplyDoubleToStateAction().setMultiplier(3).setAffectedItem(affectedItem));
     CountingAction counterB = new CountingAction();
     ruleB.addAction(counterB);
 
-    ruleA.addAction(new TriggerRuleAction(ruleB));
+    ruleA.addAction(new TriggerRuleAction().setRule(ruleB));
 
     root.addRule(ruleA);
     root.addRule(ruleB);
@@ -851,7 +754,7 @@ public class RulesTest {
     NumberItem affectedItem = TestUtils.addItemTo(root.getSmartHomeEntityModel(), 5, true);
 
     Rule rule = new Rule();
-    SetStateFromExpression action = new SetStateFromExpression();
+    SetStateFromExpressionAction action = new SetStateFromExpressionAction();
     // TODO item1 should be referred to as triggering item
     action.setNumberExpression(ParserUtils.parseNumberExpression("(" + item1.getID() + " + " + item2.getID() + ")", root));
     action.setAffectedItem(affectedItem);
diff --git a/eraser-base/src/test/resources/tests/ppc2/input.eraser b/eraser-base/src/test/resources/tests/ppc2/input.eraser
index 36fddab19dc562bbed3fe437c147016f64ef7eee..a2793872f590861be7a06194c8b1f1e0de424d0c 100644
--- a/eraser-base/src/test/resources/tests/ppc2/input.eraser
+++ b/eraser-base/src/test/resources/tests/ppc2/input.eraser
@@ -1,5 +1,5 @@
 Mqtt: incoming="ppc2/" outgoing="oh2/in" host="localhost" ;
-Item: id="iris1_item" label="Iris 1" state="121,88,68" topic="iris1_item/state";
+Item: id="iris1_item" label="Iris 1" state="121,88,68" topic="iris1_item/state" performance="ip1" ;
 Group: id="my-group" items=["iris1_item"] ;
 ThingType: id="skywriter-hat" label="SkyWriterHAT" description="SkyWriterHAT Gesture Recognition" parameters=["brokername"] channelTypes=["flick-type"] ;
 ChannelType: id="flick-type" itemType="String" label="Last Flick" description="Last Flick detected (and its direction)" category="Motion" readOnly ;
diff --git a/eraser-base/src/test/resources/tests/ppc2/output.eraser b/eraser-base/src/test/resources/tests/ppc2/output.eraser
index 9fec0c1fd2611dd05a1e7872899aecd21936ff89..30855cfc04eb3dab0d42e419cb159959550d37e3 100644
--- a/eraser-base/src/test/resources/tests/ppc2/output.eraser
+++ b/eraser-base/src/test/resources/tests/ppc2/output.eraser
@@ -1,4 +1,4 @@
-Item: id="iris1_item" label="Iris 1" state="121,88,68" topic="iris1_item/state" ;
+Item: id="iris1_item" label="Iris 1" state="121,88,68" topic="iris1_item/state" performance="ip1" ;
 Group: id="my-group" items=["iris1_item"] ;
 ThingType: id="skywriter-hat" label="SkyWriterHAT" description="SkyWriterHAT Gesture Recognition" parameters=["brokername"] channelTypes=["flick-type"] ;
 ChannelType: id="flick-type" label="Last Flick" description="Last Flick detected (and its direction)" itemType="String" category="Motion" readOnly ;
diff --git a/eraser-base/src/test/resources/tests/ppc6/Test.properties b/eraser-base/src/test/resources/tests/ppc6/Test.properties
new file mode 100644
index 0000000000000000000000000000000000000000..616a3deddd4decffd5d0e6e3db7bb8ce3873562a
--- /dev/null
+++ b/eraser-base/src/test/resources/tests/ppc6/Test.properties
@@ -0,0 +1 @@
+result=OUTPUT_PASS
diff --git a/eraser-base/src/test/resources/tests/ppc6/description b/eraser-base/src/test/resources/tests/ppc6/description
new file mode 100644
index 0000000000000000000000000000000000000000..4546caf78b69eb510a37a3856ebebcbfc0da3f15
--- /dev/null
+++ b/eraser-base/src/test/resources/tests/ppc6/description
@@ -0,0 +1 @@
+Test of different actions and conditions in rules
\ No newline at end of file
diff --git a/eraser-base/src/test/resources/tests/ppc6/input.eraser b/eraser-base/src/test/resources/tests/ppc6/input.eraser
new file mode 100644
index 0000000000000000000000000000000000000000..4debf7eb64563c1b54687e118e9c629126307e22
--- /dev/null
+++ b/eraser-base/src/test/resources/tests/ppc6/input.eraser
@@ -0,0 +1,15 @@
+Color Item: id="iris1_item" label="Iris 1" state="121,88,68" topic="iris1_item/state" ;
+Color Item: id="iris2_item" label="Iris 2" state="121,88,68" topic="iris1_item/state" ;
+Group: id="Unknown" items=["iris1_item", "iris2_item"] ;
+Rule: name="testRule2" items=["iris1_item"] [Condition: ItemStateCheck < 6.0] ;
+Rule: name="eqfff" items=["iris1_item"] [Condition: ItemStateCheck < "Yeah" ] ;
+Rule: name="testRule1" items=["iris1_item"] [Condition: ItemStateCheck < 4.0, Action: TriggerRule testRule2] ;
+Rule: name="testRule416" items=["iris1_item"] [Condition: ItemStateChange iris1_item] ;
+Rule: name="testRule456" items=["iris1_item"] [Action: Noop] ;
+Rule: name="testRule46" items=["iris1_item"] [Action: SetStateFromConstant iris1_item "K"] ;
+Rule: name="testRule8" items=["iris1_item"] [Action: SetStateFromTrigger iris2_item] ;
+Rule: name="testRul118" items=["iris1_item"] [Action: SetStateFromTrigger iris2_item 2.0] ;
+Rule: name="testR8" items=["iris1_item"] [Condition: Expression (2.0<3.0)] ;
+Rule: name="testR8" items=["iris1_item"] [Condition: Expression (2.0<iris1_item)] ;
+Rule: name="testR8556" items=["iris1_item"] [Action: StateFromExpression (2.0 + 3.0) iris1_item] ;
+Rule: name="testR8556" items=["iris1_item"] [Action: StateFromExpression iris1_item iris1_item] ;
diff --git a/eraser-base/src/test/resources/tests/ppc6/output.eraser b/eraser-base/src/test/resources/tests/ppc6/output.eraser
new file mode 100644
index 0000000000000000000000000000000000000000..ab7f5ee68c4dbf169d6538756744c8aac741a87f
--- /dev/null
+++ b/eraser-base/src/test/resources/tests/ppc6/output.eraser
@@ -0,0 +1,15 @@
+Color Item: id="iris1_item" label="Iris 1" state="121,88,68" topic="iris1_item/state" ;
+Color Item: id="iris2_item" label="Iris 2" state="121,88,68" topic="iris1_item/state" ;
+Group: id="Unknown" items=["iris1_item", "iris2_item"] ;
+Rule: name="testR8556" items=["iris1_item"] [Action: StateFromExpression iris1_item iris1_item] ;
+Rule: name="testR8556" items=["iris1_item"] [Action: StateFromExpression (2.0 + 3.0) iris1_item] ;
+Rule: name="testR8" items=["iris1_item"] [Condition: Expression (2.0<iris1_item)] ;
+Rule: name="testR8" items=["iris1_item"] [Condition: Expression (2.0<3.0)] ;
+Rule: name="testRul118" items=["iris1_item"] [Action: SetStateFromTrigger iris2_item 2.0] ;
+Rule: name="testRule8" items=["iris1_item"] [Action: SetStateFromTrigger iris2_item] ;
+Rule: name="testRule46" items=["iris1_item"] [Action: SetStateFromConstant iris1_item "K"] ;
+Rule: name="testRule456" items=["iris1_item"] [Action: Noop] ;
+Rule: name="testRule416" items=["iris1_item"] [Condition: ItemStateChange iris1_item] ;
+Rule: name="testRule1" items=["iris1_item"] [Condition: ItemStateCheck < 4.0, Action: TriggerRule testRule2] ;
+Rule: name="eqfff" items=["iris1_item"] [Condition: ItemStateCheck < "Yeah"] ;
+Rule: name="testRule2" items=["iris1_item"] [Condition: ItemStateCheck < 6.0] ;
diff --git a/pages/docs/DSL.md b/pages/docs/DSL.md
index 7ac5c9f9b6c5dde3768c40a68e804be9fa5c6d50..b8f12e6a6c6dde69012955204ee63556cdb38275 100644
--- a/pages/docs/DSL.md
+++ b/pages/docs/DSL.md
@@ -98,3 +98,40 @@ Influx: user="" password="" dbName="" host="" ;
 - `user` and `password` define connection credentials
 - `dbName` defines the name of the database to use
 - `host` defines the URL of the host running an InfluxDB. The port will be set to `8086` and cannot be changed.
+
+
+## Rule
+
+```
+Rule: name="" items=["ITEM_ID", "ITEM_ID"] [Condition: CONDITION_CONSTRUCTOR, Condition: CONDITION_CONSTRUCTOR, Action: ACTION_CONSTRUCTOR, Action: ACTION_CONSTRUCTOR] ;
+```
+
+- if the state of an included item (from attribute `items`) changes the rule gets triggered
+- when a rule is triggered each conditions receives the triggering item for check
+- conditions checks are aggregated with `AND` operator
+- if every condition returns true when checking each action will run using the triggering item
+
+### Constructors
+#### Conditions
+
+| Name                   | Description                                      | Structure                           | Example                  |
+| ---------------------- |--------------------------------------------------| ------------------------------------| -------------------------|
+| ItemStateNumberCheck   | compares the state with NUMBER using the OPERATOR | ItemStateCheck OPERATOR NUMBER     | ItemStateCheck < 5       |
+| ItemStateTextCheck   | compares the state with TEXT using the OPERATOR   | ItemStateCheck OPERATOR TEXT           | ItemStateCheck < "abc"   |
+| ItemStateChange   | triggers if specified ITEM changed its value  | ItemStateChange ITEM ItemStateChange         | ItemStateChange item1   |
+| ExpressionCondition   | triggers if the LOGICAL_EXPRESSION is true  | Expression LOGICAL_EXPRESSION         | Expression (2<3)   |
+
+
+
+#### Actions
+
+| Name                 | Description                                                                                  | Structure                      | Example                       |
+| -------------------- |----------------------------------------------------------------------------------------------| -------------------------------| ------------------------------|
+| TriggerRuleAction    | triggers the rule with with specified RULE_NAME (conditions of RULE are still being checked) | TriggerRuleAction RULE_NAME    | TriggerRuleAction testRule    |
+| NoopAction    | no operation (just for testing) | NoopAction    | NoopAction    |
+| SetStateFromConstantStringAction    | apply specified VALUE to the ITEM | SetStateFromConstant ITEM VALUE    | SetStateFromConstant item1  "LMNOP"     |
+| SetStateFromTriggeringItemAction    | copies state from triggering device to specified ITEM | SetStateFromTrigger ITEM   | SetStateFromTrigger item1  |
+| MultiplyDoubleToStateAction    | copies state multiplied by MULTIPLIER from triggering item to specified ITEM  | SetStateFromTrigger ITEM MULTIPLIER  | SetStateFromTrigger item1 2.0 |
+| StateFromExpressionAction    | apply the evaluated NUMBER_EXPRESSION as state to ITEM   | StateFromExpression NUMBER_EXPRESSION ITEM  | StateFromExpression (2.0 + 3.0) item2 |
+
+