Browse Source

Begin extending grammar and abstract syntax for action language

Joeri Exelmans 5 years ago
parent
commit
895807ec6e
2 changed files with 79 additions and 9 deletions
  1. 49 6
      src/sccd/runtime/expression.py
  2. 30 3
      src/sccd/schema/grammar.g

+ 49 - 6
src/sccd/runtime/expression.py

@@ -10,20 +10,26 @@ class DataModel:
     def __init__(self, names: Dict[str, Variable]):
         self.names = names
 
-@dataclass
 class Expression(ABC):
-    pass
-
     @abstractmethod
     def eval(self, events, datamodel):
         pass
 
+class LHS(Expression):
+    @abstractmethod
+    def lhs(self, events, datamodel) -> Variable:
+        pass
+
+    # LHS types are expressions too!
+    def eval(self, events, datamodel):
+        return self.lhs(events, datamodel).value
+
 @dataclass
-class Identifier(Expression):
+class Identifier(LHS):
     identifier: str
 
-    def eval(self, events, datamodel):
-        return datamodel.names[self.identifier].value
+    def lhs(self, events, datamodel):
+        return datamodel.names[self.identifier]
 
     def render(self):
         return self.identifier
@@ -60,3 +66,40 @@ class Array(Expression):
 
     def render(self):
         return '['+','.join([e.render() for e in self.elements])+']'
+
+@dataclass
+class BinaryExpression(Expression):
+    lhs: Expression
+    operator: str # the operator token value from the grammar.
+    rhs: Expression
+
+    def eval(self, events, datamodel):
+        return {
+            "and": lambda x,y: x and y,
+            "or": lambda x,y: x or y,
+            "==": lambda x,y: x == y,
+            "!=": lambda x,y: x != y,
+            ">": lambda x,y: x > y,
+            ">=": lambda x,y: x >= y,
+            "<": lambda x,y: x < y,
+            "<=": lambda x,y: x <= y,
+            "+": lambda x,y: x + y,
+            "-": lambda x,y: x - y,
+            "*": lambda x,y: x * y,
+            "/": lambda x,y: x / y,
+            "//": lambda x,y: x // y,
+            "%": lambda x,y: x % y,
+        }[self.operator](self.lhs.eval(events, datamodel), self.rhs.eval(events, datamodel))
+
+class Statement(ABC):
+    @abstractmethod
+    def exec(self, events, datamodel):
+        pass
+
+@dataclass
+class Assignment(Statement):
+    lhs: LHS
+    rhs: Expression
+
+    def exec(self, events, datamodel):
+        self.lhs.lhs(events, datamodel).value = rhs.eval(events, datamodel)

+ 30 - 3
src/sccd/schema/grammar.g

@@ -22,18 +22,38 @@ relative_path: _path_sequence
 _path_sequence: (CURRENT_NODE | PARENT_NODE | IDENTIFIER) (_PATH_SEP _path_sequence)?
 
 
+// We use the same operators and operator precedence rules as Python
 
+COMPARE_OPERATOR: "==" | "!=" | ">" | ">=" | "<" | "<="
+ADD_OPERATOR: "+" | "-"
+MULT_OPERATOR: "*" | "/" | "//" | "%"
 
 ?expr: or_expr
 
 ?or_expr: and_expr
-        | or_expr "||" and_expr -> or
+        | or_expr "or" and_expr    -> or
 
 ?and_expr: atom
-         | and_expr "&&" atom   -> and
+         | and_expr "and" not_expr  -> and
+
+?not_expr: comp_expr
+         | "not" comp_expr           -> not
+
+?comp_expr: add_expr
+          | comp_expr COMPARE_OPERATOR add_expr -> compare
+
+
+?add_expr: mult_expr
+         | add_expr ADD_OPERATOR mult_expr -> add
+
+
+?mult_expr: unary
+          | mult_expr MULT_OPERATOR unary -> mult
+
+?unary: atom
+      | "-" atom  -> neg
 
 ?atom: IDENTIFIER               -> identifier
-     | "-" atom                 -> neg
      | "(" expr ")"             -> group
      | literal
      | func_call
@@ -52,3 +72,10 @@ param_list: ( expr ("," expr)* )?  -> params
 
 TRUE: "true"
 FALSE: "false"
+
+
+?stmt: assignment
+
+assignment: lhs "=" expr
+
+?lhs: IDENTIFIER