Просмотр исходного кода

Merge branch 'master' of kompiler.org:trs

Sander Mathijs van Veen 14 лет назад
Родитель
Сommit
361a0f1b87
8 измененных файлов с 68 добавлено и 30 удалено
  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.
 Division of 0 by 1 reduces to 0.
 
 
  - Fractions constant rewrite rules.
  - 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_MUL: 'TIMES',
         OP_DIV: 'DIVIDE',
         OP_DIV: 'DIVIDE',
         OP_POW: 'POW',
         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_EQ: 'EQ',
         OP_POSSIBILITIES: 'POSSIBILITIES',
         OP_POSSIBILITIES: 'POSSIBILITIES',
         OP_HINT: 'HINT',
         OP_HINT: 'HINT',
         OP_REWRITE_ALL: 'REWRITE_ALL',
         OP_REWRITE_ALL: 'REWRITE_ALL',
         OP_REWRITE: 'REWRITE',
         OP_REWRITE: 'REWRITE',
-}
+        }
 
 
 
 
 def to_expression(obj):
 def to_expression(obj):

+ 25 - 14
src/parser.py

@@ -43,6 +43,10 @@ class Parser(BisonParser):
     docstrings. Scanner rules are in the 'lexscript' attribute.
     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.
     # Output directory of generated pybison files, including a trailing slash.
     buildDirectory = PYBISON_BUILD + '/'
     buildDirectory = PYBISON_BUILD + '/'
 
 
@@ -51,8 +55,9 @@ class Parser(BisonParser):
     # ----------------------------------------------------------------
     # ----------------------------------------------------------------
     # TODO: add a runtime check to verify that this token list match the list
     # TODO: add a runtime check to verify that this token list match the list
     # of tokens of the lex script.
     # 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
     # precedences
@@ -63,8 +68,9 @@ class Parser(BisonParser):
         ('left', ('TIMES', 'DIVIDE')),
         ('left', ('TIMES', 'DIVIDE')),
         ('left', ('EQ', )),
         ('left', ('EQ', )),
         ('left', ('NEG', )),
         ('left', ('NEG', )),
-        ('right', ('SIN', 'COS', 'TAN', 'SOLVE', 'INT', 'SQRT')),
         ('right', ('POW', )),
         ('right', ('POW', )),
+        ('right', ('FUNCTION', )),
+        #('right', ('SIN', 'COS', 'TAN', 'SOLVE', 'INT', 'SQRT')),
         )
         )
 
 
     interactive = 0
     interactive = 0
@@ -156,7 +162,7 @@ class Parser(BisonParser):
             left, right = filter(None, match.groups())
             left, right = filter(None, match.groups())
 
 
             # Filter words (otherwise they will be preprocessed as well)
             # 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
                 return left + right
 
 
             # If all characters on the right are numbers. e.g. "a4", the
             # 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):
     def on_unary(self, target, option, names, values):
         """
         """
         unary : MINUS exp %prec NEG
         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
         if option == 0:  # rule: NEG exp
@@ -354,9 +355,10 @@ class Parser(BisonParser):
 
 
             return values[1]
             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[0], *values[1])
+
             return Node(*values)
             return Node(*values)
 
 
         raise BisonSyntaxError('Unsupported option %d in target "%s".'
         raise BisonSyntaxError('Unsupported option %d in target "%s".'
@@ -408,10 +410,19 @@ class Parser(BisonParser):
     # operator tokens
     # operator tokens
     # -----------------------------------------
     # -----------------------------------------
     operators = ''
     operators = ''
+    functions = []
 
 
     for op_str, op in OP_MAP.iteritems():
     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
     # 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 ..possibilities import Possibility as P, MESSAGES
 from ..translate import _
 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):
 def match_add_quadrants(node):
     """
     """
     sin(x) ^ 2 + cos(x) ^ 2  ->  1
     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
     scope, c0, c1 = args
 
 
-
     # Replace the left node with the new expression
     # Replace the left node with the new expression
     substitution = Leaf(c0.value * c1.value).negate(c0.negated + c1.negated)
     substitution = Leaf(c0.value * c1.value).negate(c0.negated + c1.negated)
     scope.replace(c0, substitution)
     scope.replace(c0, substitution)

+ 15 - 2
tests/test_parser.py

@@ -4,6 +4,8 @@ import unittest
 from src.parser import Parser
 from src.parser import Parser
 from src.node import ExpressionNode as Node, ExpressionLeaf as Leaf
 from src.node import ExpressionNode as Node, ExpressionLeaf as Leaf
 from tests.parser import ParserWrapper, run_expressions, line, graph
 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):
 class TestParser(unittest.TestCase):
@@ -15,11 +17,11 @@ class TestParser(unittest.TestCase):
         run_expressions(Parser, [('a', Leaf('a'))])
         run_expressions(Parser, [('a', Leaf('a'))])
 
 
     def test_graph(self):
     def test_graph(self):
-        assert graph(Parser, '4a') == ("""
+        self.assertEqual(graph(Parser, '4a'), ("""
          *
          *
         ╭┴╮
         ╭┴╮
         4 a
         4 a
-        """).replace('\n        ', '\n')[1:-1]
+        """).replace('\n        ', '\n')[1:-1])
 
 
     def test_line(self):
     def test_line(self):
         self.assertEqual(line(Parser, '4-a'), '4 - a')
         self.assertEqual(line(Parser, '4-a'), '4 - a')
@@ -35,3 +37,14 @@ class TestParser(unittest.TestCase):
         self.assertNotEqual(possibilities2, [])
         self.assertNotEqual(possibilities2, [])
 
 
         self.assertNotEqual(possibilities1, 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):
 class TestRulesGoniometry(RulesTestCase):
 
 
     def test_match_add_quadrants(self):
     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)
         possibilities = match_add_quadrants(root)
         self.assertEqualPos(possibilities, [P(root, add_quadrants, ())])
         self.assertEqualPos(possibilities, [P(root, add_quadrants, ())])
 
 
     def test_add_quadrants(self):
     def test_add_quadrants(self):
-        return
         self.assertEqual(add_quadrants(None, ()), 1)
         self.assertEqual(add_quadrants(None, ()), 1)