浏览代码

Action language: Added float literals, subtractions between durations and fixed bug in binary expression type check.

Joeri Exelmans 5 年之前
父节点
当前提交
829cb29aa4

+ 8 - 6
src/sccd/action_lang/parser/action_lang.g

@@ -55,10 +55,11 @@ TYPE_ANNOT: "int" | "str" | "dur" | "float"
 
 array: "[" (expr ("," expr)*)? "]"
 
-?literal: ESCAPED_STRING -> string
-        | INT -> int
-        | bool_literal -> bool
-        | duration -> duration_literal
+?literal: ESCAPED_STRING -> string_literal
+        | INT -> int_literal
+        | FLOAT -> float_literal
+        | bool_literal
+        | duration_literal
 
 ?compare_operator: EQ | NEQ | GT | GEQ | LT | LEQ
 ?add_operator: PLUS | MINUS
@@ -81,14 +82,15 @@ MOD: "%"
 EXP: "**"
 NOT: "not"
 
-?bool_literal: TRUE | FALSE
+bool_literal: TRUE | FALSE
 
 TRUE: "True"
 FALSE: "False"
 
 INT: /[0-9]+/
+FLOAT: /[0-9]+\.[0-9]*/
 
-duration: (INT duration_unit)+
+duration_literal: (INT duration_unit)+
 
 ?duration_unit: TIME_H | TIME_M | TIME_S | TIME_MS | TIME_US | TIME_NS | TIME_PS | TIME_FS | TIME_D
 

+ 30 - 30
src/sccd/action_lang/parser/text.py

@@ -15,12 +15,40 @@ class ExpressionTransformer(Transformer):
 
   block = Block
 
-  def string(self, node):
+  def string_literal(self, node):
     return StringLiteral(node[0][1:-1])
 
-  def int(self, node):
+  def int_literal(self, node):
     return IntLiteral(int(node[0].value))
 
+  def float_literal(self, node):
+    return FloatLiteral(float(node[0].value))
+
+  def bool_literal(self, node):
+    return BoolLiteral({
+      "True": True,
+      "False": False,
+      }[node[0].value])
+
+  def duration_literal(self, node):
+    val = int(node[0])
+    suffix = node[1]
+
+    unit = {
+      "d": None, # 'd' stands for "duration", the non-unit for all zero-durations.
+                 # need this to parse zero-duration as a duration instead of int.
+      "fs": FemtoSecond,
+      "ps": PicoSecond,
+      "ns": Nanosecond,
+      "us": Microsecond,
+      "ms": Millisecond,
+      "s": Second,
+      "m": Minute,
+      "h": Hour
+    }[suffix]
+
+    return DurationLiteral(duration(val, unit))
+
   def func_call(self, node):
     return FunctionCall(node[0], node[1].children)
 
@@ -34,12 +62,6 @@ class ExpressionTransformer(Transformer):
   def unary_expr(self, node):
     return UnaryExpression(node[0].value, node[1])
 
-  def bool(self, node):
-    return BoolLiteral({
-      "True": True,
-      "False": False,
-      }[node[0].value])
-
   def group(self, node):
     return Group(node[0])
 
@@ -52,28 +74,6 @@ class ExpressionTransformer(Transformer):
       bin_operator = {"+=": "+", "-=": "-", "*=": "*", "/=": "/", "//=": "//"}[operator]
       return Assignment(node[0], BinaryExpression(node[0], bin_operator, node[2]))
 
-  def duration_literal(self, node):
-    return DurationLiteral(node[0])
-
-  def duration(self, node):
-    val = int(node[0])
-    suffix = node[1]
-
-    unit = {
-      "d": None, # 'd' stands for "duration", the non-unit for all zero-durations.
-                 # need this to parse zero-duration as a duration instead of int.
-      "fs": FemtoSecond,
-      "ps": PicoSecond,
-      "ns": Nanosecond,
-      "us": Microsecond,
-      "ms": Millisecond,
-      "s": Second,
-      "m": Minute,
-      "h": Hour
-    }[suffix]
-
-    return duration(val, unit)
-
   def expression_stmt(self, node):
     return ExpressionStatement(node[0])
 

+ 58 - 36
src/sccd/action_lang/static/expression.py

@@ -190,6 +190,19 @@ class IntLiteral(Expression):
     def render(self):
         return str(self.i)
 
+@dataclass
+class FloatLiteral(Expression):
+    f: float
+
+    def init_expr(self, scope: Scope) -> SCCDType:
+        return SCCDFloat
+
+    def eval(self, memory: MemoryInterface):
+        return self.f
+
+    def render(self):
+        return str(self.f)
+
 @dataclass
 class BoolLiteral(Expression):
     b: bool 
@@ -268,9 +281,14 @@ class BinaryExpression(Expression):
 
         def same_type():
             if lhs_t != rhs_t:
-                raise StaticTypeError("Mixed LHS and RHS types in '%s' expression: %s and %s" % (self.operator, str(lhs_t), str(rhs_t)))
+                raise StaticTypeError("Mixed LHS and RHS types in binary '%s'-expression: %s and %s" % (self.operator, str(lhs_t), str(rhs_t)))
             return lhs_t
 
+        def sum_type():
+            if lhs_t != SCCDInt and lhs_t != SCCDFloat and lhs_t != SCCDDuration:
+                raise StaticTypeError("Invalid type '%s' for binary '%s'-expresion" % (lhs_t, self.operator))
+            return same_type()
+
         def mult_type():
             if lhs_t == rhs_t:
                 if lhs_t == Duration:
@@ -283,42 +301,42 @@ class BinaryExpression(Expression):
             return largest_type
 
         return {
-            "and": SCCDBool,
-            "or":  SCCDBool,
-            "==":  comparison_type(),
-            "!=":  comparison_type(),
-            ">":   comparison_type(),
-            ">=":  comparison_type(),
-            "<":   comparison_type(),
-            "<=":  comparison_type(),
-            "+":   same_type(),
-            "-":   same_type(),
-            "*":   mult_type(),
-            "/":   SCCDFloat,
-            "//":  same_type(),
-            "%":   same_type(),
-            "**":  same_type(),
-        }[self.operator]
+            "and": lambda: SCCDBool,
+            "or":  lambda: SCCDBool,
+            "==":  comparison_type,
+            "!=":  comparison_type,
+            ">":   comparison_type,
+            ">=":  comparison_type,
+            "<":   comparison_type,
+            "<=":  comparison_type,
+            "+":   sum_type,
+            "-":   sum_type,
+            "*":   mult_type,
+            "/":   lambda: SCCDFloat,
+            "//":  same_type,
+            "%":   same_type,
+            "**":  same_type,
+        }[self.operator]()
 
     def eval(self, memory: MemoryInterface):
         
         return {
-            "and": lambda x,y: x.eval(memory) and y.eval(memory),
-            "or": lambda x,y: x.eval(memory) or y.eval(memory),
-            "==": lambda x,y: x.eval(memory) == y.eval(memory),
-            "!=": lambda x,y: x.eval(memory) != y.eval(memory),
-            ">": lambda x,y: x.eval(memory) > y.eval(memory),
-            ">=": lambda x,y: x.eval(memory) >= y.eval(memory),
-            "<": lambda x,y: x.eval(memory) < y.eval(memory),
-            "<=": lambda x,y: x.eval(memory) <= y.eval(memory),
-            "+": lambda x,y: x.eval(memory) + y.eval(memory),
-            "-": lambda x,y: x.eval(memory) - y.eval(memory),
-            "*": lambda x,y: x.eval(memory) * y.eval(memory),
-            "/": lambda x,y: x.eval(memory) / y.eval(memory),
-            "//": lambda x,y: x.eval(memory) // y.eval(memory),
-            "%": lambda x,y: x.eval(memory) % y.eval(memory),
-            "**": lambda x,y: x.eval(memory) ** y.eval(memory),
-        }[self.operator](self.lhs, self.rhs) # Borrow Python's lazy evaluation
+            "and": lambda x,y: x and y.eval(memory),
+            "or": lambda x,y: x or y.eval(memory),
+            "==": lambda x,y: x == y.eval(memory),
+            "!=": lambda x,y: x != y.eval(memory),
+            ">": lambda x,y: x > y.eval(memory),
+            ">=": lambda x,y: x >= y.eval(memory),
+            "<": lambda x,y: x < y.eval(memory),
+            "<=": lambda x,y: x <= y.eval(memory),
+            "+": lambda x,y: x + y.eval(memory),
+            "-": lambda x,y: x - y.eval(memory),
+            "*": lambda x,y: x * y.eval(memory),
+            "/": lambda x,y: x / y.eval(memory),
+            "//": lambda x,y: x // y.eval(memory),
+            "%": lambda x,y: x % y.eval(memory),
+            "**": lambda x,y: x ** y.eval(memory),
+        }[self.operator](self.lhs.eval(memory), self.rhs) # Borrow Python's lazy evaluation
 
     def render(self):
         return self.lhs.render() + ' ' + self.operator + ' ' + self.rhs.render()
@@ -330,10 +348,14 @@ class UnaryExpression(Expression):
 
     def init_expr(self, scope: Scope) -> SCCDType:
         expr_type = self.expr.init_expr(scope)
+        def num_type():
+            if expr_type != SCCDInt and expr_type != SCCDFloat:
+                raise StaticTypeError("Invalid type '%s' for unary '%s'-expresion" % (expr_type, self.operator))
+            return expr_type
         return {
-            "not": SCCDBool,
-            "-":   expr_type,
-        }[self.operator]
+            "not": lambda: SCCDBool,
+            "-":   num_type,
+        }[self.operator]()
 
     def eval(self, memory: MemoryInterface):
         return {

+ 26 - 2
src/sccd/util/duration.py

@@ -52,11 +52,19 @@ class Duration(ABC):
     pass
 
   @abstractmethod
-  def __eq__(self):
+  def __eq__(self, other):
+    pass
+
+  @abstractmethod
+  def __add__(self, other):
+    pass
+
+  @abstractmethod
+  def __sub__(self, other):
     pass
 
   @abstractmethod
-  def __add__(self):
+  def __neg__(self):
     pass
 
   @abstractmethod
@@ -94,6 +102,13 @@ class _ZeroDuration(Duration):
   def __add__(self, other):
     return other
 
+  def __sub__(self, other):
+    return duration(-other.val, other.unit)
+
+  def __neg__(self):
+    return self
+
+
   def __mul__(self, other: int) -> Duration:
     return self
 
@@ -149,6 +164,15 @@ class _NonZeroDuration(Duration):
     self_val, other_val, unit = _same_unit(self, other)
     return duration(self_val + other_val, unit)
 
+  def __sub__(self, other):
+    if other is _zero:
+      return duration(-self.val, self.unit)
+    self_val, other_val, unit = _same_unit(self, other)
+    return duration(self_val - other_val, unit)
+
+  def __neg__(self):
+    return duration(-self.val, self.unit)
+
   def __mul__(self, other: int) -> Duration:
     if other == 0:
       return _zero