summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Beazley <dave@dabeaz.com>2015-08-25 14:42:31 -0500
committerDavid Beazley <dave@dabeaz.com>2015-08-25 14:42:31 -0500
commit50e6b9df7b5e1680136fc795ad15d08f80e534b6 (patch)
treebc63565f23bb376a16ed15de3aec2650f0f47771
parenta843b294852a1283ec6184e392a614eecf0cefa7 (diff)
parent8692bdcc20e941ad4fa5f6ac2f91c3f74cabc634 (diff)
downloadply-50e6b9df7b5e1680136fc795ad15d08f80e534b6.tar.gz
Merge pull request #71 from moses-palmer/fixup-module-sort-order
Corrected ParserReflect.get_pfunctions on Python 3
-rw-r--r--ply/yacc.py10
-rw-r--r--test/pkg_test6/__init__.py9
-rw-r--r--test/pkg_test6/parsing/__init__.py0
-rw-r--r--test/pkg_test6/parsing/calclex.py48
-rw-r--r--test/pkg_test6/parsing/calcparse.py33
-rw-r--r--test/pkg_test6/parsing/expression.py31
-rw-r--r--test/pkg_test6/parsing/statement.py9
-rw-r--r--test/testyacc.py8
8 files changed, 146 insertions, 2 deletions
diff --git a/ply/yacc.py b/ply/yacc.py
index 429ad85..33064d4 100644
--- a/ply/yacc.py
+++ b/ply/yacc.py
@@ -3111,8 +3111,14 @@ class ParserReflect(object):
module = inspect.getmodule(item)
p_functions.append((line, module, name, item.__doc__))
- # Sort all of the actions by line number
- p_functions.sort()
+ # Sort all of the actions by line number; make sure to stringify
+ # modules to make them sortable, since `line` may not uniquely sort all
+ # p functions
+ p_functions.sort(key=lambda p_function: (
+ p_function[0],
+ str(p_function[1]),
+ p_function[2],
+ p_function[3]))
self.pfuncs = p_functions
# Validate all of the p_functions
diff --git a/test/pkg_test6/__init__.py b/test/pkg_test6/__init__.py
new file mode 100644
index 0000000..5dbe0cb
--- /dev/null
+++ b/test/pkg_test6/__init__.py
@@ -0,0 +1,9 @@
+# Tests proper sorting of modules in yacc.ParserReflect.get_pfunctions
+
+# Here for testing purposes
+import sys
+if '..' not in sys.path:
+ sys.path.insert(0, '..')
+
+from .parsing.calcparse import parser
+
diff --git a/test/pkg_test6/parsing/__init__.py b/test/pkg_test6/parsing/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/pkg_test6/parsing/__init__.py
diff --git a/test/pkg_test6/parsing/calclex.py b/test/pkg_test6/parsing/calclex.py
new file mode 100644
index 0000000..e8759b6
--- /dev/null
+++ b/test/pkg_test6/parsing/calclex.py
@@ -0,0 +1,48 @@
+# -----------------------------------------------------------------------------
+# calclex.py
+# -----------------------------------------------------------------------------
+
+import ply.lex as lex
+
+tokens = (
+ 'NAME','NUMBER',
+ 'PLUS','MINUS','TIMES','DIVIDE','EQUALS',
+ 'LPAREN','RPAREN',
+ )
+
+# Tokens
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_TIMES = r'\*'
+t_DIVIDE = r'/'
+t_EQUALS = r'='
+t_LPAREN = r'\('
+t_RPAREN = r'\)'
+t_NAME = r'[a-zA-Z_][a-zA-Z0-9_]*'
+
+def t_NUMBER(t):
+ r'\d+'
+ try:
+ t.value = int(t.value)
+ except ValueError:
+ print("Integer value too large %s" % t.value)
+ t.value = 0
+ return t
+
+t_ignore = " \t"
+
+def t_newline(t):
+ r'\n+'
+ t.lexer.lineno += t.value.count("\n")
+
+def t_error(t):
+ print("Illegal character '%s'" % t.value[0])
+ t.lexer.skip(1)
+
+# Build the lexer
+import os.path
+lexer = lex.lex(optimize=True, outputdir=os.path.dirname(__file__))
+
+
+
diff --git a/test/pkg_test6/parsing/calcparse.py b/test/pkg_test6/parsing/calcparse.py
new file mode 100644
index 0000000..6defaf9
--- /dev/null
+++ b/test/pkg_test6/parsing/calcparse.py
@@ -0,0 +1,33 @@
+# -----------------------------------------------------------------------------
+# yacc_simple.py
+#
+# A simple, properly specifier grammar
+# -----------------------------------------------------------------------------
+
+from .calclex import tokens
+from ply import yacc
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+from .statement import *
+
+from .expression import *
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+import os.path
+parser = yacc.yacc(outputdir=os.path.dirname(__file__))
+
+
+
+
+
diff --git a/test/pkg_test6/parsing/expression.py b/test/pkg_test6/parsing/expression.py
new file mode 100644
index 0000000..028f662
--- /dev/null
+++ b/test/pkg_test6/parsing/expression.py
@@ -0,0 +1,31 @@
+# This file contains definitions of expression grammar
+
+def p_expression_binop(t):
+ '''expression : expression PLUS expression
+ | expression MINUS expression
+ | expression TIMES expression
+ | expression DIVIDE expression'''
+ if t[2] == '+' : t[0] = t[1] + t[3]
+ elif t[2] == '-': t[0] = t[1] - t[3]
+ elif t[2] == '*': t[0] = t[1] * t[3]
+ elif t[2] == '/': t[0] = t[1] / t[3]
+
+def p_expression_uminus(t):
+ 'expression : MINUS expression %prec UMINUS'
+ t[0] = -t[2]
+
+def p_expression_group(t):
+ 'expression : LPAREN expression RPAREN'
+ t[0] = t[2]
+
+def p_expression_number(t):
+ 'expression : NUMBER'
+ t[0] = t[1]
+
+def p_expression_name(t):
+ 'expression : NAME'
+ try:
+ t[0] = names[t[1]]
+ except LookupError:
+ print("Undefined name '%s'" % t[1])
+ t[0] = 0
diff --git a/test/pkg_test6/parsing/statement.py b/test/pkg_test6/parsing/statement.py
new file mode 100644
index 0000000..ef7dc55
--- /dev/null
+++ b/test/pkg_test6/parsing/statement.py
@@ -0,0 +1,9 @@
+# This file contains definitions of statement grammar
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ t[0] = t[1]
diff --git a/test/testyacc.py b/test/testyacc.py
index 7620c38..7e69f09 100644
--- a/test/testyacc.py
+++ b/test/testyacc.py
@@ -441,4 +441,12 @@ class YaccErrorWarningTests(unittest.TestCase):
r = parser.parse('3+4+5')
self.assertEqual(r, 12)
+ def test_pkg_test6(self):
+ from pkg_test6 import parser
+ self.assertTrue(os.path.exists('pkg_test6/parsing/parsetab.py'))
+ self.assertTrue(os.path.exists('pkg_test6/parsing/lextab.py'))
+ self.assertTrue(os.path.exists('pkg_test6/parsing/parser.out'))
+ r = parser.parse('3+4+5')
+ self.assertEqual(r, 12)
+
unittest.main()