Explorar o código

Merge branch 'master' of kompiler.org:trs

Sander Mathijs van Veen %!s(int64=14) %!d(string=hai) anos
pai
achega
361a0f1b87
Modificáronse 8 ficheiros con 68 adicións e 30 borrados
  1. 5 0
      TODO
  2. 1 1
      external/graph_drawing
  3. 7 7
      src/node.py
  4. 25 14
      src/parser.py
  5. 14 2
      src/rules/goniometry.py
  6. 0 1
      src/rules/numerics.py
  7. 15 2
      tests/test_parser.py
  8. 1 3
      tests/test_rules_goniometry.py

+ 5 - 0
TODO

@@ -48,3 +48,8 @@ Division of 0 by 1 reduces to 0.
 Division of 0 by 1 reduces to 0.
 
  - Fractions constant rewrite rules.
+
+ - >>> (sin x) ^ 2 + (cos x) ^ 2
+   sin(x) ^ 2 + cos(x) ^ 2
+   >>> sin(x) ^ 2 + cos(x) ^ 2
+   sin(x ^ 2) + cos(x ^ 2)

+ 1 - 1
external/graph_drawing

@@ -1 +1 @@
-Subproject commit 1911c3fe7965c7505a26111c653472cc94d79f12
+Subproject commit 21f0710c80184b6ea18827808ec40af924f0c8a8

+ 7 - 7
src/node.py

@@ -79,18 +79,18 @@ TOKEN_MAP = {
         OP_MUL: 'TIMES',
         OP_DIV: 'DIVIDE',
         OP_POW: 'POW',
-        OP_SQRT: 'SQRT',
-        OP_SIN: 'SIN',
-        OP_COS: 'COS',
-        OP_TAN: 'TAN',
-        OP_INT: 'INT',
-        OP_SOLVE: 'SOLVE',
+        OP_SQRT: 'FUNCTION',
+        OP_SIN: 'FUNCTION',
+        OP_COS: 'FUNCTION',
+        OP_TAN: 'FUNCTION',
+        OP_INT: 'FUNCTION',
+        OP_SOLVE: 'FUNCTION',
         OP_EQ: 'EQ',
         OP_POSSIBILITIES: 'POSSIBILITIES',
         OP_HINT: 'HINT',
         OP_REWRITE_ALL: 'REWRITE_ALL',
         OP_REWRITE: 'REWRITE',
-}
+        }
 
 
 def to_expression(obj):

+ 25 - 14
src/parser.py

@@ -43,6 +43,10 @@ class Parser(BisonParser):
     docstrings. Scanner rules are in the 'lexscript' attribute.
     """
 
+    # Words to be ignored by preprocessor
+    words = zip(*filter(lambda (s, op): TOKEN_MAP[op] == 'FUNCTION', \
+                        OP_MAP.iteritems()))[0] + ('raise', 'graph')
+
     # Output directory of generated pybison files, including a trailing slash.
     buildDirectory = PYBISON_BUILD + '/'
 
@@ -51,8 +55,9 @@ class Parser(BisonParser):
     # ----------------------------------------------------------------
     # TODO: add a runtime check to verify that this token list match the list
     # of tokens of the lex script.
-    tokens =  ['NUMBER', 'IDENTIFIER', 'NEWLINE', 'QUIT', 'RAISE', 'GRAPH', \
-               'LPAREN', 'RPAREN'] + TOKEN_MAP.values()
+    tokens = ['NUMBER', 'IDENTIFIER', 'NEWLINE', 'QUIT', 'RAISE', 'GRAPH',
+              'LPAREN', 'RPAREN', 'FUNCTION'] \
+             + filter(lambda t: t != 'FUNCTION', TOKEN_MAP.values())
 
     # ------------------------------
     # precedences
@@ -63,8 +68,9 @@ class Parser(BisonParser):
         ('left', ('TIMES', 'DIVIDE')),
         ('left', ('EQ', )),
         ('left', ('NEG', )),
-        ('right', ('SIN', 'COS', 'TAN', 'SOLVE', 'INT', 'SQRT')),
         ('right', ('POW', )),
+        ('right', ('FUNCTION', )),
+        #('right', ('SIN', 'COS', 'TAN', 'SOLVE', 'INT', 'SQRT')),
         )
 
     interactive = 0
@@ -156,7 +162,7 @@ class Parser(BisonParser):
             left, right = filter(None, match.groups())
 
             # Filter words (otherwise they will be preprocessed as well)
-            if (left + right).upper() in self.tokens:
+            if left + right in Parser.words:
                 return left + right
 
             # If all characters on the right are numbers. e.g. "a4", the
@@ -336,12 +342,7 @@ class Parser(BisonParser):
     def on_unary(self, target, option, names, values):
         """
         unary : MINUS exp %prec NEG
-              | SIN exp
-              | COS exp
-              | TAN exp
-              | INT exp
-              | SOLVE exp
-              | SQRT exp
+              | FUNCTION exp
         """
 
         if option == 0:  # rule: NEG exp
@@ -354,9 +355,10 @@ class Parser(BisonParser):
 
             return values[1]
 
-        if option < 7:  # rule: SIN exp | COS exp | TAN exp | INT exp
-            if values[1].type == TYPE_OPERATOR and values[1].op == OP_COMMA:
+        if option == 1:  # rule: FUNCTION exp
+            if values[1].is_op(OP_COMMA):
                 return Node(values[0], *values[1])
+
             return Node(*values)
 
         raise BisonSyntaxError('Unsupported option %d in target "%s".'
@@ -408,10 +410,19 @@ class Parser(BisonParser):
     # operator tokens
     # -----------------------------------------
     operators = ''
+    functions = []
 
     for op_str, op in OP_MAP.iteritems():
-        operators += '"%s"%s{ returntoken(%s); }\n' \
-                     % (op_str, ' ' * (8 - len(op_str)), TOKEN_MAP[op])
+        if TOKEN_MAP[op] == 'FUNCTION':
+            functions.append(op_str)
+        else:
+            operators += '"%s"%s{ returntoken(%s); }\n' \
+                         % (op_str, ' ' * (8 - len(op_str)), TOKEN_MAP[op])
+
+    # Put all functions in a single regex
+    if functions:
+        operators += '("%s") { returntoken(FUNCTION); }\n' \
+                     % '"|"'.join(functions)
 
     # -----------------------------------------
     # raw lex script, verbatim here

+ 14 - 2
src/rules/goniometry.py

@@ -1,9 +1,21 @@
-from ..node import ExpressionLeaf as L, Scope, OP_ADD, OP_POW, OP_MUL, \
-        OP_SIN, OP_COS, OP_TAN
+from ..node import ExpressionNode as N, ExpressionLeaf as L, Scope, OP_ADD, \
+        OP_POW, OP_MUL, OP_SIN, OP_COS, OP_TAN
 from ..possibilities import Possibility as P, MESSAGES
 from ..translate import _
 
 
+def sin(*args):
+    return N('sin', *args)
+
+
+def cos(*args):
+    return N('cos', *args)
+
+
+def tan(*args):
+    return N('tan', *args)
+
+
 def match_add_quadrants(node):
     """
     sin(x) ^ 2 + cos(x) ^ 2  ->  1

+ 0 - 1
src/rules/numerics.py

@@ -214,7 +214,6 @@ def multiply_numerics(root, args):
     """
     scope, c0, c1 = args
 
-
     # Replace the left node with the new expression
     substitution = Leaf(c0.value * c1.value).negate(c0.negated + c1.negated)
     scope.replace(c0, substitution)

+ 15 - 2
tests/test_parser.py

@@ -4,6 +4,8 @@ import unittest
 from src.parser import Parser
 from src.node import ExpressionNode as Node, ExpressionLeaf as Leaf
 from tests.parser import ParserWrapper, run_expressions, line, graph
+from tests.rulestestcase import tree
+from src.rules.goniometry import sin, cos
 
 
 class TestParser(unittest.TestCase):
@@ -15,11 +17,11 @@ class TestParser(unittest.TestCase):
         run_expressions(Parser, [('a', Leaf('a'))])
 
     def test_graph(self):
-        assert graph(Parser, '4a') == ("""
+        self.assertEqual(graph(Parser, '4a'), ("""
          *
         ╭┴╮
         4 a
-        """).replace('\n        ', '\n')[1:-1]
+        """).replace('\n        ', '\n')[1:-1])
 
     def test_line(self):
         self.assertEqual(line(Parser, '4-a'), '4 - a')
@@ -35,3 +37,14 @@ class TestParser(unittest.TestCase):
         self.assertNotEqual(possibilities2, [])
 
         self.assertNotEqual(possibilities1, possibilities2)
+
+    def test_functions(self):
+        root, x = tree('sin x, x')
+
+        self.assertEqual(root, sin(x))
+        self.assertEqual(tree('sin x ^ 2'), sin(x) ** 2)
+        self.assertEqual(tree('sin(x) ^ 2'), sin(x) ** 2)
+        self.assertEqual(tree('sin (x) ^ 2'), sin(x) ** 2)
+        self.assertEqual(tree('sin(x ^ 2)'), sin(x ** 2))
+        self.assertEqual(tree('sin cos x'), sin(cos(x)))
+        self.assertEqual(tree('sin cos x ^ 2'), sin(cos(x)) ** 2)

+ 1 - 3
tests/test_rules_goniometry.py

@@ -6,11 +6,9 @@ from tests.rulestestcase import RulesTestCase, tree
 class TestRulesGoniometry(RulesTestCase):
 
     def test_match_add_quadrants(self):
-        return
-        root = tree('sin(x) ^ 2 + cos(x) ^ 2')
+        root = tree('sin x ^ 2 + cos x ^ 2')
         possibilities = match_add_quadrants(root)
         self.assertEqualPos(possibilities, [P(root, add_quadrants, ())])
 
     def test_add_quadrants(self):
-        return
         self.assertEqual(add_quadrants(None, ()), 1)