Commits

ebo committed c024faa

Added logical and/or operators with short circuit evaluation

  • Participants
  • Parent commits 30172b0

Comments (0)

Files changed (5)

File codeanalysis.py

         absolute_jumps = [
             "POP_JUMP_IF_TRUE",
             "POP_JUMP_IF_FALSE",
+            "JUMP_IF_TRUE_OR_POP",
+            "JUMP_IF_FALSE_OR_POP",
         ]
 
         codesize = 0
 precedence = (
     ('left', 'LETPREC'),
     ('nonassoc', 'IF', 'FUN', 'LET', 'MATCH', 'THEN'),
+    ('left', 'LOR'),
+    ('left', 'LAND'),
     ('nonassoc', 'LT', 'LE', 'GT', 'GE', 'EQ', 'NEQ'),
     ('right', 'LIST'),
     ('left', 'PLUS', 'MINUS'),
     p[0] = vmast.BinaryOp(p[1], p[2], p[3])
 
 
+def p_logicalops(p):
+    """expression : expression LOR expression
+                  | expression LAND expression
+    """
+    p[0] = vmast.BinaryOp(p[1], p[2], p[3])
+
+
 def p_ifexpression(p):
     """expression : IF expression THEN expression ELSE expression %prec LETPREC"""
     p[0] = vmast.IfOp(p[2], p[4], p[6])
    'GT', 'GE',
    'EQ', 'NEQ',
 
+    "LOR", "LAND",
+
    'LPAREN',
    'RPAREN',
    'LBRACKET',
 t_EQ      = r'=='
 t_NEQ     = r'!='
 
+t_LOR     = r'\|\|'
+t_LAND    = r'&&'
 
 t_LPAREN  = r'\('
 t_RPAREN  = r'\)'

File test_pyvm.py

         func = topcompiler.compile("1 < 2")
         self.assert_(func())
 
+    def test_binary_shortcircuit(self):
+        func = topcompiler.compile("1 && 0")
+        self.assert_(not func())
+
+        func = topcompiler.compile("1 || 0")
+        self.assert_(func())
+
+        func = topcompiler.compile("0 && unset_variable")
+        self.assert_(not func())
+
+        func = topcompiler.compile("1 || unset_variable")
+        self.assert_(func())
+
+        try:
+            func = topcompiler.compile("1 && unset_variable")
+            func()
+            self.fail("Logical Op did not throw NameError")
+        except NameError:
+            pass
+
+        try:
+            func = topcompiler.compile("0 || unset_variable")
+            func()
+            self.fail("Logical Op did not throw NameError")
+        except NameError:
+            pass
+
     def test_if(self):
         func = topcompiler.compile("if 2 then 0 else 1")
         self.assertEqual(func(), 0)

File topcompiler.py

 
     @code.when(vmast.BinaryOp)
     def code(self, node, rho, kp):
+        if node.op in ['&&', '||']:
+            self.code_shortcircuit_binop(node, rho, kp)
+        else:
+            self.code_std_binop(node, rho, kp)
+
+    def code_shortcircuit_binop(self, node, rho, kp):
+        label = self.next_label()
+
+        ops = {'&&' : 'FALSE',
+               '||' : 'TRUE'}
+
+        self.code(node.e1, rho, kp)
+        if PY_VERSION == 6:
+            self.dest.append(("JUMP_IF_%s" % ops[node.op], label))
+            self.dest.append(chr(opmap["POP_TOP"]))
+        else:
+            self.dest.append(("JUMP_IF_%s_OR_POP" % ops[node.op], label))
+        self.code(node.e2, rho, kp)
+        self.dest.append(("LABEL", label))
+
+    def code_std_binop(self, node, rho, kp):
         self.code(node.e1, rho, kp)
         self.code(node.e2, rho, kp + 1)
         self.dest.append(operators[node.op])