summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorDavid Beazley <dave@dabeaz.com>2022-10-27 13:44:12 -0500
committerDavid Beazley <dave@dabeaz.com>2022-10-27 13:44:12 -0500
commitaf80858e888c5f36979da88fcb1080de7b848967 (patch)
tree2f2fb95414baee83e489eac1b936602599ea9eba /tests
parent818ab0684e33f5f513fc839673ff56ea330b6380 (diff)
downloadply-af80858e888c5f36979da88fcb1080de7b848967.tar.gz
Reorganization. Added makefile
Diffstat (limited to 'tests')
-rw-r--r--tests/README8
-rw-r--r--tests/calclex.py46
-rwxr-xr-xtests/cleanup.sh4
-rw-r--r--tests/lex_closure.py51
-rw-r--r--tests/lex_doc1.py23
-rw-r--r--tests/lex_dup1.py26
-rw-r--r--tests/lex_dup2.py30
-rw-r--r--tests/lex_dup3.py28
-rw-r--r--tests/lex_empty.py17
-rw-r--r--tests/lex_error1.py21
-rw-r--r--tests/lex_error2.py23
-rw-r--r--tests/lex_error3.py24
-rw-r--r--tests/lex_error4.py24
-rw-r--r--tests/lex_hedit.py45
-rw-r--r--tests/lex_ignore.py28
-rw-r--r--tests/lex_ignore2.py26
-rw-r--r--tests/lex_literal1.py22
-rw-r--r--tests/lex_literal2.py22
-rw-r--r--tests/lex_literal3.py23
-rw-r--r--tests/lex_many_tokens.py25
-rw-r--r--tests/lex_module.py7
-rw-r--r--tests/lex_module_import.py42
-rw-r--r--tests/lex_object.py53
-rw-r--r--tests/lex_re1.py24
-rw-r--r--tests/lex_re2.py24
-rw-r--r--tests/lex_re3.py26
-rw-r--r--tests/lex_rule1.py24
-rw-r--r--tests/lex_rule2.py26
-rw-r--r--tests/lex_rule3.py24
-rw-r--r--tests/lex_state1.py37
-rw-r--r--tests/lex_state2.py37
-rw-r--r--tests/lex_state3.py39
-rw-r--r--tests/lex_state4.py38
-rw-r--r--tests/lex_state5.py37
-rw-r--r--tests/lex_state_noerror.py36
-rw-r--r--tests/lex_state_norule.py37
-rw-r--r--tests/lex_state_try.py42
-rw-r--r--tests/lex_token1.py16
-rw-r--r--tests/lex_token2.py19
-rw-r--r--tests/lex_token3.py21
-rw-r--r--tests/lex_token4.py23
-rw-r--r--tests/lex_token_dup.py26
-rw-r--r--tests/test_cpp_nonascii.c2
-rwxr-xr-xtests/testlex.py401
-rw-r--r--tests/testyacc.py403
-rw-r--r--tests/yacc_badargs.py68
-rw-r--r--tests/yacc_badid.py74
-rw-r--r--tests/yacc_badprec.py61
-rw-r--r--tests/yacc_badprec2.py65
-rw-r--r--tests/yacc_badprec3.py65
-rw-r--r--tests/yacc_badrule.py65
-rw-r--r--tests/yacc_badtok.py65
-rw-r--r--tests/yacc_dup.py65
-rw-r--r--tests/yacc_error1.py65
-rw-r--r--tests/yacc_error2.py65
-rw-r--r--tests/yacc_error3.py64
-rw-r--r--tests/yacc_error4.py69
-rw-r--r--tests/yacc_error5.py91
-rw-r--r--tests/yacc_error6.py77
-rw-r--r--tests/yacc_error7.py77
-rw-r--r--tests/yacc_inf.py53
-rw-r--r--tests/yacc_literal.py66
-rw-r--r--tests/yacc_misplaced.py65
-rw-r--r--tests/yacc_missing1.py65
-rw-r--r--tests/yacc_nested.py30
-rw-r--r--tests/yacc_nodoc.py64
-rw-r--r--tests/yacc_noerror.py63
-rw-r--r--tests/yacc_nop.py65
-rw-r--r--tests/yacc_notfunc.py63
-rw-r--r--tests/yacc_notok.py63
-rw-r--r--tests/yacc_prec1.py65
-rw-r--r--tests/yacc_rr.py69
-rw-r--r--tests/yacc_rr_unused.py27
-rw-r--r--tests/yacc_simple.py65
-rw-r--r--tests/yacc_sr.py60
-rw-r--r--tests/yacc_term1.py65
-rw-r--r--tests/yacc_unicode_literals.py67
-rw-r--r--tests/yacc_unused.py74
-rw-r--r--tests/yacc_unused_rule.py69
-rw-r--r--tests/yacc_uprec.py60
-rw-r--r--tests/yacc_uprec2.py60
81 files changed, 4294 insertions, 0 deletions
diff --git a/tests/README b/tests/README
new file mode 100644
index 0000000..03b167c
--- /dev/null
+++ b/tests/README
@@ -0,0 +1,8 @@
+This directory mostly contains tests for various types of error
+conditions. To run:
+
+ $ python testlex.py
+ $ python testyacc.py
+ $ python testcpp.py
+
+The script 'cleanup.sh' cleans up this directory to its original state.
diff --git a/tests/calclex.py b/tests/calclex.py
new file mode 100644
index 0000000..4862753
--- /dev/null
+++ b/tests/calclex.py
@@ -0,0 +1,46 @@
+# -----------------------------------------------------------------------------
+# 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
+lexer = lex.lex()
+
+
+
diff --git a/tests/cleanup.sh b/tests/cleanup.sh
new file mode 100755
index 0000000..9374f2c
--- /dev/null
+++ b/tests/cleanup.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+rm -rf *~ *.pyc *.pyo *.dif *.out __pycache__
+
diff --git a/tests/lex_closure.py b/tests/lex_closure.py
new file mode 100644
index 0000000..0b34cff
--- /dev/null
+++ b/tests/lex_closure.py
@@ -0,0 +1,51 @@
+# -----------------------------------------------------------------------------
+# lex_closure.py
+# -----------------------------------------------------------------------------
+import ply.lex as lex
+
+tokens = (
+ 'NAME','NUMBER',
+ 'PLUS','MINUS','TIMES','DIVIDE','EQUALS',
+ 'LPAREN','RPAREN',
+ )
+
+def make_calc():
+
+ # 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.lineno += t.value.count("\n")
+
+ def t_error(t):
+ print("Illegal character '%s'" % t.value[0])
+ t.lexer.skip(1)
+
+ # Build the lexer
+ return lex.lex()
+
+make_calc()
+lex.runmain(data="3+4")
+
+
+
diff --git a/tests/lex_doc1.py b/tests/lex_doc1.py
new file mode 100644
index 0000000..4b62609
--- /dev/null
+++ b/tests/lex_doc1.py
@@ -0,0 +1,23 @@
+# lex_doc1.py
+#
+# Missing documentation string
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ ]
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+def t_NUMBER(t):
+ pass
+
+def t_error(t):
+ pass
+
+lex.lex()
+
+
diff --git a/tests/lex_dup1.py b/tests/lex_dup1.py
new file mode 100644
index 0000000..b9dc3c5
--- /dev/null
+++ b/tests/lex_dup1.py
@@ -0,0 +1,26 @@
+# lex_dup1.py
+#
+# Duplicated rule specifiers
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ ]
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_NUMBER = r'\d+'
+
+t_NUMBER = r'\d+'
+
+def t_error(t):
+ pass
+
+
+
+lex.lex()
+
+
diff --git a/tests/lex_dup2.py b/tests/lex_dup2.py
new file mode 100644
index 0000000..47bf5b5
--- /dev/null
+++ b/tests/lex_dup2.py
@@ -0,0 +1,30 @@
+# lex_dup2.py
+#
+# Duplicated rule specifiers
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ ]
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+def t_NUMBER(t):
+ r'\d+'
+ pass
+
+def t_NUMBER(t):
+ r'\d+'
+ pass
+
+def t_error(t):
+ pass
+
+
+
+lex.lex()
+
+
diff --git a/tests/lex_dup3.py b/tests/lex_dup3.py
new file mode 100644
index 0000000..5398f48
--- /dev/null
+++ b/tests/lex_dup3.py
@@ -0,0 +1,28 @@
+# lex_dup3.py
+#
+# Duplicated rule specifiers
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ ]
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_NUMBER = r'\d+'
+
+def t_NUMBER(t):
+ r'\d+'
+ pass
+
+def t_error(t):
+ pass
+
+
+
+lex.lex()
+
+
diff --git a/tests/lex_empty.py b/tests/lex_empty.py
new file mode 100644
index 0000000..d0baf31
--- /dev/null
+++ b/tests/lex_empty.py
@@ -0,0 +1,17 @@
+# lex_empty.py
+#
+# No rules defined
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ ]
+
+
+
+lex.lex()
+
+
diff --git a/tests/lex_error1.py b/tests/lex_error1.py
new file mode 100644
index 0000000..fec0282
--- /dev/null
+++ b/tests/lex_error1.py
@@ -0,0 +1,21 @@
+# lex_error1.py
+#
+# Missing t_error() rule
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ ]
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_NUMBER = r'\d+'
+
+
+
+lex.lex()
+
+
diff --git a/tests/lex_error2.py b/tests/lex_error2.py
new file mode 100644
index 0000000..8914ab6
--- /dev/null
+++ b/tests/lex_error2.py
@@ -0,0 +1,23 @@
+# lex_error2.py
+#
+# t_error defined, but not function
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ ]
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_NUMBER = r'\d+'
+
+t_error = "foo"
+
+
+
+lex.lex()
+
+
diff --git a/tests/lex_error3.py b/tests/lex_error3.py
new file mode 100644
index 0000000..71ca903
--- /dev/null
+++ b/tests/lex_error3.py
@@ -0,0 +1,24 @@
+# lex_error3.py
+#
+# t_error defined as function, but with wrong # args
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ ]
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_NUMBER = r'\d+'
+
+def t_error():
+ pass
+
+
+
+lex.lex()
+
+
diff --git a/tests/lex_error4.py b/tests/lex_error4.py
new file mode 100644
index 0000000..5387808
--- /dev/null
+++ b/tests/lex_error4.py
@@ -0,0 +1,24 @@
+# lex_error4.py
+#
+# t_error defined as function, but too many args
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ ]
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_NUMBER = r'\d+'
+
+def t_error(t,s):
+ pass
+
+
+
+lex.lex()
+
+
diff --git a/tests/lex_hedit.py b/tests/lex_hedit.py
new file mode 100644
index 0000000..9f724d0
--- /dev/null
+++ b/tests/lex_hedit.py
@@ -0,0 +1,45 @@
+# -----------------------------------------------------------------------------
+# hedit.py
+#
+# Paring of Fortran H Edit descriptions (Contributed by Pearu Peterson)
+#
+# These tokens can't be easily tokenized because they are of the following
+# form:
+#
+# nHc1...cn
+#
+# where n is a positive integer and c1 ... cn are characters.
+#
+# This example shows how to modify the state of the lexer to parse
+# such tokens
+# -----------------------------------------------------------------------------
+
+import ply.lex as lex
+
+tokens = (
+ 'H_EDIT_DESCRIPTOR',
+ )
+
+# Tokens
+t_ignore = " \t\n"
+
+def t_H_EDIT_DESCRIPTOR(t):
+ r"\d+H.*" # This grabs all of the remaining text
+ i = t.value.index('H')
+ n = eval(t.value[:i])
+
+ # Adjust the tokenizing position
+ t.lexer.lexpos -= len(t.value) - (i+1+n)
+ t.value = t.value[i+1:i+1+n]
+ return t
+
+def t_error(t):
+ print("Illegal character '%s'" % t.value[0])
+ t.lexer.skip(1)
+
+# Build the lexer
+lex.lex()
+lex.runmain(data="3Habc 10Habcdefghij 2Hxy")
+
+
+
diff --git a/tests/lex_ignore.py b/tests/lex_ignore.py
new file mode 100644
index 0000000..b31fb39
--- /dev/null
+++ b/tests/lex_ignore.py
@@ -0,0 +1,28 @@
+# lex_ignore.py
+#
+# Improperly specific ignore declaration
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ ]
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_NUMBER = r'\d+'
+
+def t_ignore(t):
+ ' \t'
+ pass
+
+def t_error(t):
+ pass
+
+import sys
+
+lex.lex()
+
+
diff --git a/tests/lex_ignore2.py b/tests/lex_ignore2.py
new file mode 100644
index 0000000..de0958a
--- /dev/null
+++ b/tests/lex_ignore2.py
@@ -0,0 +1,26 @@
+# lex_ignore2.py
+#
+# ignore declaration as a raw string
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ ]
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_NUMBER = r'\d+'
+
+t_ignore = r' \t'
+
+def t_error(t):
+ pass
+
+
+
+lex.lex()
+
+
diff --git a/tests/lex_literal1.py b/tests/lex_literal1.py
new file mode 100644
index 0000000..510b379
--- /dev/null
+++ b/tests/lex_literal1.py
@@ -0,0 +1,22 @@
+# lex_literal1.py
+#
+# Bad literal specification
+
+import ply.lex as lex
+
+tokens = [
+ "NUMBER",
+ ]
+
+literals = ["+","-","**"]
+
+def t_NUMBER(t):
+ r'\d+'
+ return t
+
+def t_error(t):
+ pass
+
+lex.lex()
+
+
diff --git a/tests/lex_literal2.py b/tests/lex_literal2.py
new file mode 100644
index 0000000..a7a2c56
--- /dev/null
+++ b/tests/lex_literal2.py
@@ -0,0 +1,22 @@
+# lex_literal2.py
+#
+# Bad literal specification
+
+import ply.lex as lex
+
+tokens = [
+ "NUMBER",
+ ]
+
+literals = 23
+
+def t_NUMBER(t):
+ r'\d+'
+ return t
+
+def t_error(t):
+ pass
+
+lex.lex()
+
+
diff --git a/tests/lex_literal3.py b/tests/lex_literal3.py
new file mode 100644
index 0000000..9d697c9
--- /dev/null
+++ b/tests/lex_literal3.py
@@ -0,0 +1,23 @@
+# lex_literal3.py
+#
+# An empty literal specification given as a list
+# Issue 8 : Literals empty list causes IndexError
+
+import ply.lex as lex
+
+tokens = [
+ "NUMBER",
+ ]
+
+literals = []
+
+def t_NUMBER(t):
+ r'\d+'
+ return t
+
+def t_error(t):
+ pass
+
+lex.lex()
+
+
diff --git a/tests/lex_many_tokens.py b/tests/lex_many_tokens.py
new file mode 100644
index 0000000..02e3a11
--- /dev/null
+++ b/tests/lex_many_tokens.py
@@ -0,0 +1,25 @@
+# lex_many_tokens.py
+#
+# Test lex's ability to handle a large number of tokens (beyond the
+# 100-group limit of the re module)
+
+import sys
+import ply.lex as lex
+
+tokens = ["TOK%d" % i for i in range(1000)]
+
+for tok in tokens:
+ if sys.version_info[0] < 3:
+ exec("t_%s = '%s:'" % (tok,tok))
+ else:
+ exec("t_%s = '%s:'" % (tok,tok), globals())
+
+t_ignore = " \t"
+
+def t_error(t):
+ pass
+
+lex.lex()
+lex.runmain(data="TOK34: TOK143: TOK269: TOK372: TOK452: TOK561: TOK999:")
+
+
diff --git a/tests/lex_module.py b/tests/lex_module.py
new file mode 100644
index 0000000..0fa8544
--- /dev/null
+++ b/tests/lex_module.py
@@ -0,0 +1,7 @@
+# lex_module.py
+#
+
+import ply.lex as lex
+import lex_module_import
+lex.lex(module=lex_module_import)
+lex.runmain(data="3+4")
diff --git a/tests/lex_module_import.py b/tests/lex_module_import.py
new file mode 100644
index 0000000..df42082
--- /dev/null
+++ b/tests/lex_module_import.py
@@ -0,0 +1,42 @@
+# -----------------------------------------------------------------------------
+# lex_module_import.py
+#
+# A lexer defined in a module, but built in lex_module.py
+# -----------------------------------------------------------------------------
+
+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.lineno += t.value.count("\n")
+
+def t_error(t):
+ print("Illegal character '%s'" % t.value[0])
+ t.lexer.skip(1)
+
diff --git a/tests/lex_object.py b/tests/lex_object.py
new file mode 100644
index 0000000..2fa7537
--- /dev/null
+++ b/tests/lex_object.py
@@ -0,0 +1,53 @@
+# -----------------------------------------------------------------------------
+# lex_object.py
+# -----------------------------------------------------------------------------
+
+import ply.lex as lex
+
+class CalcLexer:
+ 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(self,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(self,t):
+ r'\n+'
+ t.lineno += t.value.count("\n")
+
+ def t_error(self,t):
+ print("Illegal character '%s'" % t.value[0])
+ t.lexer.skip(1)
+
+
+calc = CalcLexer()
+
+# Build the lexer
+lex.lex(object=calc)
+lex.runmain(data="3+4")
+
+
+
+
diff --git a/tests/lex_re1.py b/tests/lex_re1.py
new file mode 100644
index 0000000..d6b7c38
--- /dev/null
+++ b/tests/lex_re1.py
@@ -0,0 +1,24 @@
+# lex_re1.py
+#
+# Bad regular expression in a string
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ ]
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_NUMBER = r'(\d+'
+
+def t_error(t):
+ pass
+
+
+
+lex.lex()
+
+
diff --git a/tests/lex_re2.py b/tests/lex_re2.py
new file mode 100644
index 0000000..f5ce695
--- /dev/null
+++ b/tests/lex_re2.py
@@ -0,0 +1,24 @@
+# lex_re2.py
+#
+# Regular expression rule matches empty string
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ ]
+
+t_PLUS = r'\+?'
+t_MINUS = r'-'
+t_NUMBER = r'(\d+)'
+
+def t_error(t):
+ pass
+
+
+
+lex.lex()
+
+
diff --git a/tests/lex_re3.py b/tests/lex_re3.py
new file mode 100644
index 0000000..8f594f3
--- /dev/null
+++ b/tests/lex_re3.py
@@ -0,0 +1,26 @@
+# lex_re3.py
+#
+# Regular expression rule matches empty string
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ "POUND",
+ ]
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_NUMBER = r'(\d+)'
+t_POUND = r'#'
+
+def t_error(t):
+ pass
+
+
+
+lex.lex()
+
+
diff --git a/tests/lex_rule1.py b/tests/lex_rule1.py
new file mode 100644
index 0000000..ae279e9
--- /dev/null
+++ b/tests/lex_rule1.py
@@ -0,0 +1,24 @@
+# lex_rule1.py
+#
+# Rule function with incorrect number of arguments
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ ]
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_NUMBER = 1
+
+def t_error(t):
+ pass
+
+
+
+lex.lex()
+
+
diff --git a/tests/lex_rule2.py b/tests/lex_rule2.py
new file mode 100644
index 0000000..adefee2
--- /dev/null
+++ b/tests/lex_rule2.py
@@ -0,0 +1,26 @@
+# lex_rule2.py
+#
+# Rule function with incorrect number of arguments
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ ]
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+def t_NUMBER():
+ r'\d+'
+ return t
+
+def t_error(t):
+ pass
+
+
+
+lex.lex()
+
+
diff --git a/tests/lex_rule3.py b/tests/lex_rule3.py
new file mode 100644
index 0000000..bd61040
--- /dev/null
+++ b/tests/lex_rule3.py
@@ -0,0 +1,24 @@
+# lex_rule3.py
+#
+# Rule function with incorrect number of arguments
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ ]
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+def t_NUMBER(t,s):
+ r'\d+'
+ return t
+
+def t_error(t):
+ pass
+
+lex.lex()
+
+
diff --git a/tests/lex_state1.py b/tests/lex_state1.py
new file mode 100644
index 0000000..6fa5e9f
--- /dev/null
+++ b/tests/lex_state1.py
@@ -0,0 +1,37 @@
+# lex_state1.py
+#
+# Bad state declaration
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ ]
+
+states = 'comment'
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_NUMBER = r'\d+'
+
+# Comments
+def t_comment(t):
+ r'/\*'
+ t.lexer.begin('comment')
+ print("Entering comment state")
+
+def t_comment_body_part(t):
+ r'(.|\n)*\*/'
+ print("comment body %s" % t)
+ t.lexer.begin('INITIAL')
+
+def t_error(t):
+ pass
+
+
+
+lex.lex()
+
+
diff --git a/tests/lex_state2.py b/tests/lex_state2.py
new file mode 100644
index 0000000..9300ed7
--- /dev/null
+++ b/tests/lex_state2.py
@@ -0,0 +1,37 @@
+# lex_state2.py
+#
+# Bad state declaration
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ ]
+
+states = ('comment','example')
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_NUMBER = r'\d+'
+
+# Comments
+def t_comment(t):
+ r'/\*'
+ t.lexer.begin('comment')
+ print("Entering comment state")
+
+def t_comment_body_part(t):
+ r'(.|\n)*\*/'
+ print("comment body %s" % t)
+ t.lexer.begin('INITIAL')
+
+def t_error(t):
+ pass
+
+
+
+lex.lex()
+
+
diff --git a/tests/lex_state3.py b/tests/lex_state3.py
new file mode 100644
index 0000000..2e5b7cf
--- /dev/null
+++ b/tests/lex_state3.py
@@ -0,0 +1,39 @@
+# lex_state3.py
+#
+# Bad state declaration
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ ]
+
+comment = 1
+states = ((comment, 'inclusive'),
+ ('example', 'exclusive'))
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_NUMBER = r'\d+'
+
+# Comments
+def t_comment(t):
+ r'/\*'
+ t.lexer.begin('comment')
+ print("Entering comment state")
+
+def t_comment_body_part(t):
+ r'(.|\n)*\*/'
+ print("comment body %s" % t)
+ t.lexer.begin('INITIAL')
+
+def t_error(t):
+ pass
+
+
+
+lex.lex()
+
+
diff --git a/tests/lex_state4.py b/tests/lex_state4.py
new file mode 100644
index 0000000..fb147c8
--- /dev/null
+++ b/tests/lex_state4.py
@@ -0,0 +1,38 @@
+# lex_state4.py
+#
+# Bad state declaration
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ ]
+
+
+states = (('comment', 'exclsive'),)
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_NUMBER = r'\d+'
+
+# Comments
+def t_comment(t):
+ r'/\*'
+ t.lexer.begin('comment')
+ print("Entering comment state")
+
+def t_comment_body_part(t):
+ r'(.|\n)*\*/'
+ print("comment body %s" % t)
+ t.lexer.begin('INITIAL')
+
+def t_error(t):
+ pass
+
+
+
+lex.lex()
+
+
diff --git a/tests/lex_state5.py b/tests/lex_state5.py
new file mode 100644
index 0000000..fb2f3a7
--- /dev/null
+++ b/tests/lex_state5.py
@@ -0,0 +1,37 @@
+# lex_state5.py
+#
+# Bad state declaration
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ ]
+
+states = (('comment', 'exclusive'),
+ ('comment', 'exclusive'))
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_NUMBER = r'\d+'
+
+# Comments
+def t_comment(t):
+ r'/\*'
+ t.lexer.begin('comment')
+ print("Entering comment state")
+
+def t_comment_body_part(t):
+ r'(.|\n)*\*/'
+ print("comment body %s" % t)
+ t.lexer.begin('INITIAL')
+
+def t_error(t):
+ pass
+
+
+lex.lex()
+
+
diff --git a/tests/lex_state_noerror.py b/tests/lex_state_noerror.py
new file mode 100644
index 0000000..54b892e
--- /dev/null
+++ b/tests/lex_state_noerror.py
@@ -0,0 +1,36 @@
+# lex_state_noerror.py
+#
+# Declaration of a state for which no rules are defined
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ ]
+
+states = (('comment', 'exclusive'),)
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_NUMBER = r'\d+'
+
+# Comments
+def t_comment(t):
+ r'/\*'
+ t.lexer.begin('comment')
+ print("Entering comment state")
+
+def t_comment_body_part(t):
+ r'(.|\n)*\*/'
+ print("comment body %s" % t)
+ t.lexer.begin('INITIAL')
+
+def t_error(t):
+ pass
+
+
+lex.lex()
+
+
diff --git a/tests/lex_state_norule.py b/tests/lex_state_norule.py
new file mode 100644
index 0000000..4b979b2
--- /dev/null
+++ b/tests/lex_state_norule.py
@@ -0,0 +1,37 @@
+# lex_state_norule.py
+#
+# Declaration of a state for which no rules are defined
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ ]
+
+states = (('comment', 'exclusive'),
+ ('example', 'exclusive'))
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_NUMBER = r'\d+'
+
+# Comments
+def t_comment(t):
+ r'/\*'
+ t.lexer.begin('comment')
+ print("Entering comment state")
+
+def t_comment_body_part(t):
+ r'(.|\n)*\*/'
+ print("comment body %s" % t)
+ t.lexer.begin('INITIAL')
+
+def t_error(t):
+ pass
+
+
+lex.lex()
+
+
diff --git a/tests/lex_state_try.py b/tests/lex_state_try.py
new file mode 100644
index 0000000..c5f3448
--- /dev/null
+++ b/tests/lex_state_try.py
@@ -0,0 +1,42 @@
+# lex_state_try.py
+#
+# Declaration of a state for which no rules are defined
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ ]
+
+states = (('comment', 'exclusive'),)
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_NUMBER = r'\d+'
+
+t_ignore = " \t"
+
+# Comments
+def t_comment(t):
+ r'/\*'
+ t.lexer.begin('comment')
+ print("Entering comment state")
+
+def t_comment_body_part(t):
+ r'(.|\n)*\*/'
+ print("comment body %s" % t)
+ t.lexer.begin('INITIAL')
+
+def t_error(t):
+ pass
+
+t_comment_error = t_error
+t_comment_ignore = t_ignore
+
+lex.lex()
+
+data = "3 + 4 /* This is a comment */ + 10"
+
+lex.runmain(data=data)
diff --git a/tests/lex_token1.py b/tests/lex_token1.py
new file mode 100644
index 0000000..1001a99
--- /dev/null
+++ b/tests/lex_token1.py
@@ -0,0 +1,16 @@
+# lex_token1.py
+#
+# Tests for absence of tokens variable
+
+import ply.lex as lex
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_NUMBER = r'\d+'
+
+def t_error(t):
+ pass
+
+lex.lex()
+
+
diff --git a/tests/lex_token2.py b/tests/lex_token2.py
new file mode 100644
index 0000000..00e758b
--- /dev/null
+++ b/tests/lex_token2.py
@@ -0,0 +1,19 @@
+# lex_token2.py
+#
+# Tests for tokens of wrong type
+
+import ply.lex as lex
+
+tokens = "PLUS MINUS NUMBER"
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_NUMBER = r'\d+'
+
+def t_error(t):
+ pass
+
+
+lex.lex()
+
+
diff --git a/tests/lex_token3.py b/tests/lex_token3.py
new file mode 100644
index 0000000..3b71b3a
--- /dev/null
+++ b/tests/lex_token3.py
@@ -0,0 +1,21 @@
+# lex_token3.py
+#
+# tokens is right type, but is missing a token for one rule
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "NUMBER",
+ ]
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_NUMBER = r'\d+'
+
+def t_error(t):
+ pass
+
+lex.lex()
+
+
diff --git a/tests/lex_token4.py b/tests/lex_token4.py
new file mode 100644
index 0000000..89bc2e2
--- /dev/null
+++ b/tests/lex_token4.py
@@ -0,0 +1,23 @@
+# lex_token4.py
+#
+# Bad token name
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "-",
+ "NUMBER",
+ ]
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+t_NUMBER = r'\d+'
+
+def t_error(t):
+ pass
+
+lex.lex()
+
+
diff --git a/tests/lex_token_dup.py b/tests/lex_token_dup.py
new file mode 100644
index 0000000..631535f
--- /dev/null
+++ b/tests/lex_token_dup.py
@@ -0,0 +1,26 @@
+# lex_token_dup.py
+#
+# Duplicate token name in tokens
+
+import ply.lex as lex
+
+tokens = [
+ "PLUS",
+ "MINUS",
+ "NUMBER",
+ "MINUS"
+ ]
+
+t_PLUS = r'\+'
+t_MINUS = r'-'
+
+def t_NUMBER(t):
+ r'\d+'
+ return t
+
+def t_error(t):
+ pass
+
+lex.lex()
+
+
diff --git a/tests/test_cpp_nonascii.c b/tests/test_cpp_nonascii.c
new file mode 100644
index 0000000..3e97d81
--- /dev/null
+++ b/tests/test_cpp_nonascii.c
@@ -0,0 +1,2 @@
+/* ë */
+#define x 1 \ No newline at end of file
diff --git a/tests/testlex.py b/tests/testlex.py
new file mode 100755
index 0000000..7f2adad
--- /dev/null
+++ b/tests/testlex.py
@@ -0,0 +1,401 @@
+# testlex.py
+
+import unittest
+try:
+ import StringIO
+except ImportError:
+ import io as StringIO
+
+import sys
+import os
+import warnings
+import platform
+
+sys.tracebacklimit = 0
+
+import ply.lex
+
+try:
+ from importlib.util import cache_from_source
+except ImportError:
+ # Python 2.7, but we don't care.
+ cache_from_source = None
+
+
+def make_pymodule_path(filename, optimization=None):
+ path = os.path.dirname(filename)
+ file = os.path.basename(filename)
+ mod, ext = os.path.splitext(file)
+
+ if sys.hexversion >= 0x3050000:
+ fullpath = cache_from_source(filename, optimization=optimization)
+ elif sys.hexversion >= 0x3040000:
+ fullpath = cache_from_source(filename, ext=='.pyc')
+ elif sys.hexversion >= 0x3020000:
+ import imp
+ modname = mod+"."+imp.get_tag()+ext
+ fullpath = os.path.join(path,'__pycache__',modname)
+ else:
+ fullpath = filename
+ return fullpath
+
+def pymodule_out_exists(filename, optimization=None):
+ return os.path.exists(make_pymodule_path(filename,
+ optimization=optimization))
+
+def pymodule_out_remove(filename, optimization=None):
+ os.remove(make_pymodule_path(filename, optimization=optimization))
+
+def implementation():
+ if platform.system().startswith("Java"):
+ return "Jython"
+ elif hasattr(sys, "pypy_version_info"):
+ return "PyPy"
+ else:
+ return "CPython"
+
+test_pyo = (implementation() == 'CPython')
+
+def check_expected(result, expected, contains=False):
+ if sys.version_info[0] >= 3:
+ if isinstance(result,str):
+ result = result.encode('ascii')
+ if isinstance(expected,str):
+ expected = expected.encode('ascii')
+ resultlines = result.splitlines()
+ expectedlines = expected.splitlines()
+
+ if len(resultlines) != len(expectedlines):
+ return False
+
+ for rline,eline in zip(resultlines,expectedlines):
+ if contains:
+ if eline not in rline:
+ return False
+ else:
+ if not rline.endswith(eline):
+ return False
+ return True
+
+def run_import(module):
+ code = "import "+module
+ exec(code)
+ del sys.modules[module]
+
+# Tests related to errors and warnings when building lexers
+class LexErrorWarningTests(unittest.TestCase):
+ def setUp(self):
+ sys.stderr = StringIO.StringIO()
+ sys.stdout = StringIO.StringIO()
+ if sys.hexversion >= 0x3020000:
+ warnings.filterwarnings('ignore',category=ResourceWarning)
+
+ def tearDown(self):
+ sys.stderr = sys.__stderr__
+ sys.stdout = sys.__stdout__
+ def test_lex_doc1(self):
+ self.assertRaises(SyntaxError,run_import,"lex_doc1")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "lex_doc1.py:15: No regular expression defined for rule 't_NUMBER'\n"))
+ def test_lex_dup1(self):
+ self.assertRaises(SyntaxError,run_import,"lex_dup1")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "lex_dup1.py:17: Rule t_NUMBER redefined. Previously defined on line 15\n" ))
+
+ def test_lex_dup2(self):
+ self.assertRaises(SyntaxError,run_import,"lex_dup2")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "lex_dup2.py:19: Rule t_NUMBER redefined. Previously defined on line 15\n" ))
+
+ def test_lex_dup3(self):
+ self.assertRaises(SyntaxError,run_import,"lex_dup3")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "lex_dup3.py:17: Rule t_NUMBER redefined. Previously defined on line 15\n" ))
+
+ def test_lex_empty(self):
+ self.assertRaises(SyntaxError,run_import,"lex_empty")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "No rules of the form t_rulename are defined\n"
+ "No rules defined for state 'INITIAL'\n"))
+
+ def test_lex_error1(self):
+ run_import("lex_error1")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "No t_error rule is defined\n"))
+
+ def test_lex_error2(self):
+ self.assertRaises(SyntaxError,run_import,"lex_error2")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "Rule 't_error' must be defined as a function\n")
+ )
+
+ def test_lex_error3(self):
+ self.assertRaises(SyntaxError,run_import,"lex_error3")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "lex_error3.py:17: Rule 't_error' requires an argument\n"))
+
+ def test_lex_error4(self):
+ self.assertRaises(SyntaxError,run_import,"lex_error4")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "lex_error4.py:17: Rule 't_error' has too many arguments\n"))
+
+ def test_lex_ignore(self):
+ self.assertRaises(SyntaxError,run_import,"lex_ignore")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "lex_ignore.py:17: Rule 't_ignore' must be defined as a string\n"))
+
+ def test_lex_ignore2(self):
+ run_import("lex_ignore2")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "t_ignore contains a literal backslash '\\'\n"))
+
+
+ def test_lex_re1(self):
+ self.assertRaises(SyntaxError,run_import,"lex_re1")
+ result = sys.stderr.getvalue()
+ if sys.hexversion < 0x3050000:
+ msg = "Invalid regular expression for rule 't_NUMBER'. unbalanced parenthesis\n"
+ else:
+ msg = "Invalid regular expression for rule 't_NUMBER'. missing ), unterminated subpattern at position 0"
+ self.assertTrue(check_expected(result,
+ msg,
+ contains=True))
+
+ def test_lex_re2(self):
+ self.assertRaises(SyntaxError,run_import,"lex_re2")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "Regular expression for rule 't_PLUS' matches empty string\n"))
+
+ def test_lex_re3(self):
+ self.assertRaises(SyntaxError,run_import,"lex_re3")
+ result = sys.stderr.getvalue()
+# self.assertTrue(check_expected(result,
+# "Invalid regular expression for rule 't_POUND'. unbalanced parenthesis\n"
+# "Make sure '#' in rule 't_POUND' is escaped with '\\#'\n"))
+
+ if sys.hexversion < 0x3050000:
+ msg = ("Invalid regular expression for rule 't_POUND'. unbalanced parenthesis\n"
+ "Make sure '#' in rule 't_POUND' is escaped with '\\#'\n")
+ else:
+ msg = ("Invalid regular expression for rule 't_POUND'. missing ), unterminated subpattern at position 0\n"
+ "ERROR: Make sure '#' in rule 't_POUND' is escaped with '\#'")
+ self.assertTrue(check_expected(result,
+ msg,
+ contains=True), result)
+
+ def test_lex_rule1(self):
+ self.assertRaises(SyntaxError,run_import,"lex_rule1")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "t_NUMBER not defined as a function or string\n"))
+
+ def test_lex_rule2(self):
+ self.assertRaises(SyntaxError,run_import,"lex_rule2")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "lex_rule2.py:15: Rule 't_NUMBER' requires an argument\n"))
+
+ def test_lex_rule3(self):
+ self.assertRaises(SyntaxError,run_import,"lex_rule3")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "lex_rule3.py:15: Rule 't_NUMBER' has too many arguments\n"))
+
+
+ def test_lex_state1(self):
+ self.assertRaises(SyntaxError,run_import,"lex_state1")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "states must be defined as a tuple or list\n"))
+
+ def test_lex_state2(self):
+ self.assertRaises(SyntaxError,run_import,"lex_state2")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "Invalid state specifier 'comment'. Must be a tuple (statename,'exclusive|inclusive')\n"
+ "Invalid state specifier 'example'. Must be a tuple (statename,'exclusive|inclusive')\n"))
+
+ def test_lex_state3(self):
+ self.assertRaises(SyntaxError,run_import,"lex_state3")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "State name 1 must be a string\n"
+ "No rules defined for state 'example'\n"))
+
+ def test_lex_state4(self):
+ self.assertRaises(SyntaxError,run_import,"lex_state4")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "State type for state 'comment' must be 'inclusive' or 'exclusive'\n"))
+
+
+ def test_lex_state5(self):
+ self.assertRaises(SyntaxError,run_import,"lex_state5")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "State 'comment' already defined\n"))
+
+ def test_lex_state_noerror(self):
+ run_import("lex_state_noerror")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "No error rule is defined for exclusive state 'comment'\n"))
+
+ def test_lex_state_norule(self):
+ self.assertRaises(SyntaxError,run_import,"lex_state_norule")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "No rules defined for state 'example'\n"))
+
+ def test_lex_token1(self):
+ self.assertRaises(SyntaxError,run_import,"lex_token1")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "No token list is defined\n"
+ "Rule 't_NUMBER' defined for an unspecified token NUMBER\n"
+ "Rule 't_PLUS' defined for an unspecified token PLUS\n"
+ "Rule 't_MINUS' defined for an unspecified token MINUS\n"
+))
+
+ def test_lex_token2(self):
+ self.assertRaises(SyntaxError,run_import,"lex_token2")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "tokens must be a list or tuple\n"
+ "Rule 't_NUMBER' defined for an unspecified token NUMBER\n"
+ "Rule 't_PLUS' defined for an unspecified token PLUS\n"
+ "Rule 't_MINUS' defined for an unspecified token MINUS\n"
+))
+
+ def test_lex_token3(self):
+ self.assertRaises(SyntaxError,run_import,"lex_token3")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "Rule 't_MINUS' defined for an unspecified token MINUS\n"))
+
+
+ def test_lex_token4(self):
+ self.assertRaises(SyntaxError,run_import,"lex_token4")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "Bad token name '-'\n"))
+
+
+ def test_lex_token_dup(self):
+ run_import("lex_token_dup")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "Token 'MINUS' multiply defined\n"))
+
+
+ def test_lex_literal1(self):
+ self.assertRaises(SyntaxError,run_import,"lex_literal1")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "Invalid literal '**'. Must be a single character\n"))
+
+ def test_lex_literal2(self):
+ self.assertRaises(SyntaxError,run_import,"lex_literal2")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "Invalid literals specification. literals must be a sequence of characters\n"))
+
+import os
+import subprocess
+import shutil
+
+# Tests related to various build options associated with lexers
+class LexBuildOptionTests(unittest.TestCase):
+ def setUp(self):
+ sys.stderr = StringIO.StringIO()
+ sys.stdout = StringIO.StringIO()
+ def tearDown(self):
+ sys.stderr = sys.__stderr__
+ sys.stdout = sys.__stdout__
+ try:
+ shutil.rmtree("lexdir")
+ except OSError:
+ pass
+
+ def test_lex_module(self):
+ run_import("lex_module")
+ result = sys.stdout.getvalue()
+ self.assertTrue(check_expected(result,
+ "(NUMBER,3,1,0)\n"
+ "(PLUS,'+',1,1)\n"
+ "(NUMBER,4,1,2)\n"))
+
+ def test_lex_object(self):
+ run_import("lex_object")
+ result = sys.stdout.getvalue()
+ self.assertTrue(check_expected(result,
+ "(NUMBER,3,1,0)\n"
+ "(PLUS,'+',1,1)\n"
+ "(NUMBER,4,1,2)\n"))
+
+ def test_lex_closure(self):
+ run_import("lex_closure")
+ result = sys.stdout.getvalue()
+ self.assertTrue(check_expected(result,
+ "(NUMBER,3,1,0)\n"
+ "(PLUS,'+',1,1)\n"
+ "(NUMBER,4,1,2)\n"))
+
+ def test_lex_many_tokens(self):
+ run_import("lex_many_tokens")
+ result = sys.stdout.getvalue()
+ self.assertTrue(check_expected(result,
+ "(TOK34,'TOK34:',1,0)\n"
+ "(TOK143,'TOK143:',1,7)\n"
+ "(TOK269,'TOK269:',1,15)\n"
+ "(TOK372,'TOK372:',1,23)\n"
+ "(TOK452,'TOK452:',1,31)\n"
+ "(TOK561,'TOK561:',1,39)\n"
+ "(TOK999,'TOK999:',1,47)\n"
+ ))
+
+# Tests related to run-time behavior of lexers
+class LexRunTests(unittest.TestCase):
+ def setUp(self):
+ sys.stderr = StringIO.StringIO()
+ sys.stdout = StringIO.StringIO()
+ def tearDown(self):
+ sys.stderr = sys.__stderr__
+ sys.stdout = sys.__stdout__
+
+ def test_lex_hedit(self):
+ run_import("lex_hedit")
+ result = sys.stdout.getvalue()
+ self.assertTrue(check_expected(result,
+ "(H_EDIT_DESCRIPTOR,'abc',1,0)\n"
+ "(H_EDIT_DESCRIPTOR,'abcdefghij',1,6)\n"
+ "(H_EDIT_DESCRIPTOR,'xy',1,20)\n"))
+
+ def test_lex_state_try(self):
+ run_import("lex_state_try")
+ result = sys.stdout.getvalue()
+ self.assertTrue(check_expected(result,
+ "(NUMBER,'3',1,0)\n"
+ "(PLUS,'+',1,2)\n"
+ "(NUMBER,'4',1,4)\n"
+ "Entering comment state\n"
+ "comment body LexToken(body_part,'This is a comment */',1,9)\n"
+ "(PLUS,'+',1,30)\n"
+ "(NUMBER,'10',1,32)\n"
+ ))
+
+
+
+unittest.main()
diff --git a/tests/testyacc.py b/tests/testyacc.py
new file mode 100644
index 0000000..b488bf7
--- /dev/null
+++ b/tests/testyacc.py
@@ -0,0 +1,403 @@
+# testyacc.py
+
+import unittest
+try:
+ import StringIO
+except ImportError:
+ import io as StringIO
+
+import sys
+import os
+import warnings
+import re
+import platform
+
+sys.tracebacklimit = 0
+
+import ply.yacc
+
+def make_pymodule_path(filename):
+ path = os.path.dirname(filename)
+ file = os.path.basename(filename)
+ mod, ext = os.path.splitext(file)
+
+ if sys.hexversion >= 0x3040000:
+ import importlib.util
+ fullpath = importlib.util.cache_from_source(filename, ext=='.pyc')
+ elif sys.hexversion >= 0x3020000:
+ import imp
+ modname = mod+"."+imp.get_tag()+ext
+ fullpath = os.path.join(path,'__pycache__',modname)
+ else:
+ fullpath = filename
+ return fullpath
+
+def pymodule_out_exists(filename):
+ return os.path.exists(make_pymodule_path(filename))
+
+def pymodule_out_remove(filename):
+ os.remove(make_pymodule_path(filename))
+
+def implementation():
+ if platform.system().startswith("Java"):
+ return "Jython"
+ elif hasattr(sys, "pypy_version_info"):
+ return "PyPy"
+ else:
+ return "CPython"
+
+# Check the output to see if it contains all of a set of expected output lines.
+# This alternate implementation looks weird, but is needed to properly handle
+# some variations in error message order that occurs due to dict hash table
+# randomization that was introduced in Python 3.3
+def check_expected(result, expected):
+ # Normalize 'state n' text to account for randomization effects in Python 3.3
+ expected = re.sub(r' state \d+', 'state <n>', expected)
+ result = re.sub(r' state \d+', 'state <n>', result)
+
+ resultlines = set()
+ for line in result.splitlines():
+ if line.startswith("WARNING: "):
+ line = line[9:]
+ elif line.startswith("ERROR: "):
+ line = line[7:]
+ resultlines.add(line)
+
+ # Selectively remove expected lines from the output
+ for eline in expected.splitlines():
+ resultlines = set(line for line in resultlines if not line.endswith(eline))
+
+ # Return True if no result lines remain
+ return not bool(resultlines)
+
+def run_import(module):
+ code = "import "+module
+ exec(code)
+ del sys.modules[module]
+
+# Tests related to errors and warnings when building parsers
+class YaccErrorWarningTests(unittest.TestCase):
+ def setUp(self):
+ sys.stderr = StringIO.StringIO()
+ sys.stdout = StringIO.StringIO()
+ try:
+ os.remove("parsetab.py")
+ pymodule_out_remove("parsetab.pyc")
+ except OSError:
+ pass
+
+ if sys.hexversion >= 0x3020000:
+ warnings.filterwarnings('ignore', category=ResourceWarning)
+ warnings.filterwarnings('ignore', category=DeprecationWarning)
+
+ def tearDown(self):
+ sys.stderr = sys.__stderr__
+ sys.stdout = sys.__stdout__
+ def test_yacc_badargs(self):
+ self.assertRaises(ply.yacc.YaccError,run_import,"yacc_badargs")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "yacc_badargs.py:23: Rule 'p_statement_assign' has too many arguments\n"
+ "yacc_badargs.py:27: Rule 'p_statement_expr' requires an argument\n"
+ ))
+ def test_yacc_badid(self):
+ self.assertRaises(ply.yacc.YaccError,run_import,"yacc_badid")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "yacc_badid.py:29: Illegal name 'bad&rule' in rule 'statement'\n"
+ "yacc_badid.py:33: Illegal rule name 'bad&rule'\n"
+ ))
+
+ def test_yacc_badprec(self):
+ try:
+ run_import("yacc_badprec")
+ except ply.yacc.YaccError:
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "precedence must be a list or tuple\n"
+ ))
+ def test_yacc_badprec2(self):
+ self.assertRaises(ply.yacc.YaccError,run_import,"yacc_badprec2")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "Bad precedence table\n"
+ ))
+
+ def test_yacc_badprec3(self):
+ run_import("yacc_badprec3")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "Precedence already specified for terminal 'MINUS'\n"
+ "Generating LALR tables\n"
+
+ ))
+
+ def test_yacc_badrule(self):
+ self.assertRaises(ply.yacc.YaccError,run_import,"yacc_badrule")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "yacc_badrule.py:21: Syntax error. Expected ':'\n"
+ "yacc_badrule.py:25: Syntax error in rule 'statement'\n"
+ "yacc_badrule.py:30: Syntax error. Expected ':'\n"
+ "yacc_badrule.py:39: Syntax error. Expected ':'\n"
+ ))
+
+ def test_yacc_badtok(self):
+ try:
+ run_import("yacc_badtok")
+ except ply.yacc.YaccError:
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "tokens must be a list or tuple\n"))
+
+ def test_yacc_dup(self):
+ run_import("yacc_dup")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "yacc_dup.py:24: Function p_statement redefined. Previously defined on line 20\n"
+ "Token 'EQUALS' defined, but not used\n"
+ "There is 1 unused token\n"
+ "Generating LALR tables\n"
+
+ ))
+ def test_yacc_error1(self):
+ try:
+ run_import("yacc_error1")
+ except ply.yacc.YaccError:
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "yacc_error1.py:58: p_error() requires 1 argument\n"))
+
+ def test_yacc_error2(self):
+ try:
+ run_import("yacc_error2")
+ except ply.yacc.YaccError:
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "yacc_error2.py:58: p_error() requires 1 argument\n"))
+
+ def test_yacc_error3(self):
+ try:
+ run_import("yacc_error3")
+ except ply.yacc.YaccError:
+ e = sys.exc_info()[1]
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "'p_error' defined, but is not a function or method\n"))
+
+ def test_yacc_error4(self):
+ self.assertRaises(ply.yacc.YaccError,run_import,"yacc_error4")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "yacc_error4.py:59: Illegal rule name 'error'. Already defined as a token\n"
+ ))
+
+
+ def test_yacc_error5(self):
+ run_import("yacc_error5")
+ result = sys.stdout.getvalue()
+ self.assertTrue(check_expected(result,
+ "Group at 3:10 to 3:12\n"
+ "Undefined name 'a'\n"
+ "Syntax error at 'b'\n"
+ "Syntax error at 4:18 to 4:22\n"
+ "Assignment Error at 2:5 to 5:27\n"
+ "13\n"
+ ))
+
+ def test_yacc_error6(self):
+ run_import("yacc_error6")
+ result = sys.stdout.getvalue()
+ self.assertTrue(check_expected(result,
+ "a=7\n"
+ "Line 3: Syntax error at '*'\n"
+ "c=21\n"
+ ))
+
+ def test_yacc_error7(self):
+ run_import("yacc_error7")
+ result = sys.stdout.getvalue()
+ self.assertTrue(check_expected(result,
+ "a=7\n"
+ "Line 3: Syntax error at '*'\n"
+ "c=21\n"
+ ))
+
+ def test_yacc_inf(self):
+ self.assertRaises(ply.yacc.YaccError,run_import,"yacc_inf")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "Token 'NUMBER' defined, but not used\n"
+ "There is 1 unused token\n"
+ "Infinite recursion detected for symbol 'statement'\n"
+ "Infinite recursion detected for symbol 'expression'\n"
+ ))
+ def test_yacc_literal(self):
+ self.assertRaises(ply.yacc.YaccError,run_import,"yacc_literal")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "yacc_literal.py:33: Literal token '**' in rule 'expression' may only be a single character\n"
+ ))
+ def test_yacc_misplaced(self):
+ self.assertRaises(ply.yacc.YaccError,run_import,"yacc_misplaced")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "yacc_misplaced.py:29: Misplaced '|'\n"
+ ))
+
+ def test_yacc_missing1(self):
+ self.assertRaises(ply.yacc.YaccError,run_import,"yacc_missing1")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "yacc_missing1.py:21: Symbol 'location' used, but not defined as a token or a rule\n"
+ ))
+
+ def test_yacc_nested(self):
+ run_import("yacc_nested")
+ result = sys.stdout.getvalue()
+ self.assertTrue(check_expected(result,
+ "A\n"
+ "A\n"
+ "A\n",
+ ))
+
+ def test_yacc_nodoc(self):
+ run_import("yacc_nodoc")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "yacc_nodoc.py:24: No documentation string specified in function 'p_statement_expr' (ignored)\n"
+ "Generating LALR tables\n"
+ ))
+
+ def test_yacc_noerror(self):
+ run_import("yacc_noerror")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "no p_error() function is defined\n"
+ "Generating LALR tables\n"
+ ))
+
+ def test_yacc_nop(self):
+ run_import("yacc_nop")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "yacc_nop.py:24: Possible grammar rule 'statement_expr' defined without p_ prefix\n"
+ "Generating LALR tables\n"
+ ))
+
+ def test_yacc_notfunc(self):
+ run_import("yacc_notfunc")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "'p_statement_assign' not defined as a function\n"
+ "Token 'EQUALS' defined, but not used\n"
+ "There is 1 unused token\n"
+ "Generating LALR tables\n"
+ ))
+ def test_yacc_notok(self):
+ try:
+ run_import("yacc_notok")
+ except ply.yacc.YaccError:
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "No token list is defined\n"))
+
+ def test_yacc_rr(self):
+ run_import("yacc_rr")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "Generating LALR tables\n"
+ "1 reduce/reduce conflict\n"
+ "reduce/reduce conflict in state 15 resolved using rule (statement -> NAME EQUALS NUMBER)\n"
+ "rejected rule (expression -> NUMBER) in state 15\n"
+
+ ))
+
+ def test_yacc_rr_unused(self):
+ run_import("yacc_rr_unused")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "no p_error() function is defined\n"
+ "Generating LALR tables\n"
+ "3 reduce/reduce conflicts\n"
+ "reduce/reduce conflict in state 1 resolved using rule (rule3 -> A)\n"
+ "rejected rule (rule4 -> A) in state 1\n"
+ "reduce/reduce conflict in state 1 resolved using rule (rule3 -> A)\n"
+ "rejected rule (rule5 -> A) in state 1\n"
+ "reduce/reduce conflict in state 1 resolved using rule (rule4 -> A)\n"
+ "rejected rule (rule5 -> A) in state 1\n"
+ "Rule (rule5 -> A) is never reduced\n"
+ ))
+
+ def test_yacc_simple(self):
+ run_import("yacc_simple")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "Generating LALR tables\n"
+ ))
+
+ def test_yacc_sr(self):
+ run_import("yacc_sr")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "Generating LALR tables\n"
+ "20 shift/reduce conflicts\n"
+ ))
+
+ def test_yacc_term1(self):
+ self.assertRaises(ply.yacc.YaccError,run_import,"yacc_term1")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "yacc_term1.py:21: Illegal rule name 'NUMBER'. Already defined as a token\n"
+ ))
+
+ def test_yacc_unicode_literals(self):
+ run_import("yacc_unicode_literals")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "Generating LALR tables\n"
+ ))
+
+ def test_yacc_unused(self):
+ self.assertRaises(ply.yacc.YaccError,run_import,"yacc_unused")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "yacc_unused.py:59: Symbol 'COMMA' used, but not defined as a token or a rule\n"
+ "Symbol 'COMMA' is unreachable\n"
+ "Symbol 'exprlist' is unreachable\n"
+ ))
+ def test_yacc_unused_rule(self):
+ run_import("yacc_unused_rule")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "yacc_unused_rule.py:59: Rule 'integer' defined, but not used\n"
+ "There is 1 unused rule\n"
+ "Symbol 'integer' is unreachable\n"
+ "Generating LALR tables\n"
+ ))
+
+ def test_yacc_uprec(self):
+ self.assertRaises(ply.yacc.YaccError,run_import,"yacc_uprec")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "yacc_uprec.py:34: Nothing known about the precedence of 'UMINUS'\n"
+ ))
+
+ def test_yacc_uprec2(self):
+ self.assertRaises(ply.yacc.YaccError,run_import,"yacc_uprec2")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "yacc_uprec2.py:34: Syntax error. Nothing follows %prec\n"
+ ))
+
+ def test_yacc_prec1(self):
+ self.assertRaises(ply.yacc.YaccError,run_import,"yacc_prec1")
+ result = sys.stderr.getvalue()
+ self.assertTrue(check_expected(result,
+ "Precedence rule 'left' defined for unknown symbol '+'\n"
+ "Precedence rule 'left' defined for unknown symbol '*'\n"
+ "Precedence rule 'left' defined for unknown symbol '-'\n"
+ "Precedence rule 'left' defined for unknown symbol '/'\n"
+ ))
+
+unittest.main()
diff --git a/tests/yacc_badargs.py b/tests/yacc_badargs.py
new file mode 100644
index 0000000..cd4b1e7
--- /dev/null
+++ b/tests/yacc_badargs.py
@@ -0,0 +1,68 @@
+# -----------------------------------------------------------------------------
+# yacc_badargs.py
+#
+# Rules with wrong # args
+# -----------------------------------------------------------------------------
+import sys
+sys.tracebacklimit = 0
+
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t,s):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr():
+ 'statement : expression'
+ print(t[1])
+
+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
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_badid.py b/tests/yacc_badid.py
new file mode 100644
index 0000000..f624aca
--- /dev/null
+++ b/tests/yacc_badid.py
@@ -0,0 +1,74 @@
+# -----------------------------------------------------------------------------
+# yacc_badid.py
+#
+# Attempt to define a rule with a bad-identifier name
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+def p_statement_expr2(t):
+ 'statement : bad&rule'
+ pass
+
+def p_badrule(t):
+ 'bad&rule : expression'
+ pass
+
+
+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
+
+def p_error(t):
+ pass
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_badprec.py b/tests/yacc_badprec.py
new file mode 100644
index 0000000..95e9db2
--- /dev/null
+++ b/tests/yacc_badprec.py
@@ -0,0 +1,61 @@
+# -----------------------------------------------------------------------------
+# yacc_badprec.py
+#
+# Bad precedence specifier
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = "blah"
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+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
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_badprec2.py b/tests/yacc_badprec2.py
new file mode 100644
index 0000000..c33303d
--- /dev/null
+++ b/tests/yacc_badprec2.py
@@ -0,0 +1,65 @@
+# -----------------------------------------------------------------------------
+# yacc_badprec2.py
+#
+# Bad precedence
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ 42,
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+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
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_badprec3.py b/tests/yacc_badprec3.py
new file mode 100644
index 0000000..e1ce3a6
--- /dev/null
+++ b/tests/yacc_badprec3.py
@@ -0,0 +1,65 @@
+# -----------------------------------------------------------------------------
+# yacc_badprec3.py
+#
+# Bad precedence
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE','MINUS'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+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[3] == '/': 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
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_badrule.py b/tests/yacc_badrule.py
new file mode 100644
index 0000000..898a8c7
--- /dev/null
+++ b/tests/yacc_badrule.py
@@ -0,0 +1,65 @@
+# -----------------------------------------------------------------------------
+# yacc_badrule.py
+#
+# Syntax problems in the rule strings
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement'
+ print(t[1])
+
+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
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_badtok.py b/tests/yacc_badtok.py
new file mode 100644
index 0000000..567aadf
--- /dev/null
+++ b/tests/yacc_badtok.py
@@ -0,0 +1,65 @@
+# -----------------------------------------------------------------------------
+# yacc_badtok.py
+#
+# A grammar, but tokens is a bad datatype
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+tokens = "Hello"
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+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
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_dup.py b/tests/yacc_dup.py
new file mode 100644
index 0000000..94238a4
--- /dev/null
+++ b/tests/yacc_dup.py
@@ -0,0 +1,65 @@
+# -----------------------------------------------------------------------------
+# yacc_dup.py
+#
+# Duplicated rule name
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement(t):
+ 'statement : expression'
+ print(t[1])
+
+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
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_error1.py b/tests/yacc_error1.py
new file mode 100644
index 0000000..39b9124
--- /dev/null
+++ b/tests/yacc_error1.py
@@ -0,0 +1,65 @@
+# -----------------------------------------------------------------------------
+# yacc_error1.py
+#
+# Bad p_error() function
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+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
+
+def p_error(t,s):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_error2.py b/tests/yacc_error2.py
new file mode 100644
index 0000000..2463ee9
--- /dev/null
+++ b/tests/yacc_error2.py
@@ -0,0 +1,65 @@
+# -----------------------------------------------------------------------------
+# yacc_error2.py
+#
+# Bad p_error() function
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+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
+
+def p_error():
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_error3.py b/tests/yacc_error3.py
new file mode 100644
index 0000000..f181253
--- /dev/null
+++ b/tests/yacc_error3.py
@@ -0,0 +1,64 @@
+# -----------------------------------------------------------------------------
+# yacc_error3.py
+#
+# Bad p_error() function
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+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
+
+p_error = "blah"
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_error4.py b/tests/yacc_error4.py
new file mode 100644
index 0000000..f6bef7e
--- /dev/null
+++ b/tests/yacc_error4.py
@@ -0,0 +1,69 @@
+# -----------------------------------------------------------------------------
+# yacc_error4.py
+#
+# Attempt to define a rule named 'error'
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+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
+
+def p_error_handler(t):
+ 'error : NAME'
+ pass
+
+def p_error(t):
+ pass
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_error5.py b/tests/yacc_error5.py
new file mode 100644
index 0000000..5d4b683
--- /dev/null
+++ b/tests/yacc_error5.py
@@ -0,0 +1,91 @@
+# -----------------------------------------------------------------------------
+# yacc_error5.py
+#
+# Lineno and position tracking with error tokens
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_assign_error(t):
+ 'statement : NAME EQUALS error'
+ line_start, line_end = t.linespan(3)
+ pos_start, pos_end = t.lexspan(3)
+ print("Assignment Error at %d:%d to %d:%d" % (line_start,pos_start,line_end,pos_end))
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+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'
+ line_start, line_end = t.linespan(2)
+ pos_start, pos_end = t.lexspan(2)
+ print("Group at %d:%d to %d:%d" % (line_start,pos_start, line_end, pos_end))
+ t[0] = t[2]
+
+def p_expression_group_error(t):
+ 'expression : LPAREN error RPAREN'
+ line_start, line_end = t.linespan(2)
+ pos_start, pos_end = t.lexspan(2)
+ print("Syntax error at %d:%d to %d:%d" % (line_start,pos_start, line_end, pos_end))
+ t[0] = 0
+
+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
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+parser = yacc.yacc()
+import calclex
+calclex.lexer.lineno=1
+parser.parse("""
+a = 3 +
+(4*5) +
+(a b c) +
++ 6 + 7
+""", tracking=True)
+
+
+
+
+
+
diff --git a/tests/yacc_error6.py b/tests/yacc_error6.py
new file mode 100644
index 0000000..fde3c4d
--- /dev/null
+++ b/tests/yacc_error6.py
@@ -0,0 +1,77 @@
+# -----------------------------------------------------------------------------
+# yacc_error6.py
+#
+# Panic mode recovery test
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+def p_statements(t):
+ 'statements : statements statement'
+ pass
+
+def p_statements_1(t):
+ 'statements : statement'
+ pass
+
+def p_statement_assign(p):
+ 'statement : LPAREN NAME EQUALS expression RPAREN'
+ print("%s=%s" % (p[2],p[4]))
+
+def p_statement_expr(t):
+ 'statement : LPAREN expression RPAREN'
+ print(t[1])
+
+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_number(t):
+ 'expression : NUMBER'
+ t[0] = t[1]
+
+def p_error(p):
+ if p:
+ print("Line %d: Syntax error at '%s'" % (p.lineno, p.value))
+ # Scan ahead looking for a name token
+ while True:
+ tok = parser.token()
+ if not tok or tok.type == 'RPAREN':
+ break
+ if tok:
+ parser.restart()
+ return None
+
+parser = yacc.yacc()
+import calclex
+calclex.lexer.lineno=1
+
+parser.parse("""
+(a = 3 + 4)
+(b = 4 + * 5 - 6 + *)
+(c = 10 + 11)
+""")
+
+
+
+
+
+
diff --git a/tests/yacc_error7.py b/tests/yacc_error7.py
new file mode 100644
index 0000000..17cc4f1
--- /dev/null
+++ b/tests/yacc_error7.py
@@ -0,0 +1,77 @@
+# -----------------------------------------------------------------------------
+# yacc_error7.py
+#
+# Panic mode recovery test using deprecated functionality
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+def p_statements(t):
+ 'statements : statements statement'
+ pass
+
+def p_statements_1(t):
+ 'statements : statement'
+ pass
+
+def p_statement_assign(p):
+ 'statement : LPAREN NAME EQUALS expression RPAREN'
+ print("%s=%s" % (p[2],p[4]))
+
+def p_statement_expr(t):
+ 'statement : LPAREN expression RPAREN'
+ print(t[1])
+
+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_number(t):
+ 'expression : NUMBER'
+ t[0] = t[1]
+
+def p_error(p):
+ if p:
+ print("Line %d: Syntax error at '%s'" % (p.lineno, p.value))
+ # Scan ahead looking for a name token
+ while True:
+ tok = parser.token()
+ if not tok or tok.type == 'RPAREN':
+ break
+ if tok:
+ parser.restart()
+ return None
+
+parser = yacc.yacc()
+import calclex
+calclex.lexer.lineno=1
+
+parser.parse("""
+(a = 3 + 4)
+(b = 4 + * 5 - 6 + *)
+(c = 10 + 11)
+""")
+
+
+
+
+
+
diff --git a/tests/yacc_inf.py b/tests/yacc_inf.py
new file mode 100644
index 0000000..f8b4c30
--- /dev/null
+++ b/tests/yacc_inf.py
@@ -0,0 +1,53 @@
+# -----------------------------------------------------------------------------
+# yacc_inf.py
+#
+# Infinite recursion
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+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_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_literal.py b/tests/yacc_literal.py
new file mode 100644
index 0000000..687d916
--- /dev/null
+++ b/tests/yacc_literal.py
@@ -0,0 +1,66 @@
+# -----------------------------------------------------------------------------
+# yacc_literal.py
+#
+# Grammar with bad literal characters
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','+','-'),
+ ('left','*','/'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+def p_expression_binop(t):
+ '''expression : expression '+' expression
+ | expression '-' expression
+ | expression '*' expression
+ | expression '/' expression
+ | expression '**' 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
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_misplaced.py b/tests/yacc_misplaced.py
new file mode 100644
index 0000000..7cc75be
--- /dev/null
+++ b/tests/yacc_misplaced.py
@@ -0,0 +1,65 @@
+# -----------------------------------------------------------------------------
+# yacc_misplaced.py
+#
+# A misplaced | in grammar rules
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+def p_expression_binop(t):
+ ''' | 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
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_missing1.py b/tests/yacc_missing1.py
new file mode 100644
index 0000000..81955be
--- /dev/null
+++ b/tests/yacc_missing1.py
@@ -0,0 +1,65 @@
+# -----------------------------------------------------------------------------
+# yacc_missing1.py
+#
+# Grammar with a missing rule
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : location EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+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
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_nested.py b/tests/yacc_nested.py
new file mode 100644
index 0000000..93ff400
--- /dev/null
+++ b/tests/yacc_nested.py
@@ -0,0 +1,30 @@
+
+from ply import lex, yacc
+
+t_A = 'A'
+t_B = 'B'
+t_C = 'C'
+
+tokens = ('A', 'B', 'C')
+
+the_lexer = lex.lex()
+
+def t_error(t):
+ pass
+
+def p_error(p):
+ pass
+
+def p_start(t):
+ '''start : A nest C'''
+ pass
+
+def p_nest(t):
+ '''nest : B'''
+ print(t[-1])
+
+the_parser = yacc.yacc(debug = False)
+
+the_parser.parse('ABC', the_lexer)
+the_parser.parse('ABC', the_lexer, tracking=True)
+the_parser.parse('ABC', the_lexer, tracking=True, debug=1)
diff --git a/tests/yacc_nodoc.py b/tests/yacc_nodoc.py
new file mode 100644
index 0000000..a2f34dd
--- /dev/null
+++ b/tests/yacc_nodoc.py
@@ -0,0 +1,64 @@
+# -----------------------------------------------------------------------------
+# yacc_nodoc.py
+#
+# Rule with a missing doc-string
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ print(t[1])
+
+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
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_noerror.py b/tests/yacc_noerror.py
new file mode 100644
index 0000000..2262745
--- /dev/null
+++ b/tests/yacc_noerror.py
@@ -0,0 +1,63 @@
+# -----------------------------------------------------------------------------
+# yacc_noerror.py
+#
+# No p_error() rule defined.
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+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
+
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_nop.py b/tests/yacc_nop.py
new file mode 100644
index 0000000..3ff17ac
--- /dev/null
+++ b/tests/yacc_nop.py
@@ -0,0 +1,65 @@
+# -----------------------------------------------------------------------------
+# yacc_nop.py
+#
+# Possible grammar rule defined without p_ prefix
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+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
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_notfunc.py b/tests/yacc_notfunc.py
new file mode 100644
index 0000000..c8bd123
--- /dev/null
+++ b/tests/yacc_notfunc.py
@@ -0,0 +1,63 @@
+# -----------------------------------------------------------------------------
+# yacc_notfunc.py
+#
+# p_rule not defined as a function
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+p_statement_assign = "Blah"
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+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
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_notok.py b/tests/yacc_notok.py
new file mode 100644
index 0000000..6e271a0
--- /dev/null
+++ b/tests/yacc_notok.py
@@ -0,0 +1,63 @@
+# -----------------------------------------------------------------------------
+# yacc_notok.py
+#
+# A grammar, but we forgot to import the tokens list
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+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
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_prec1.py b/tests/yacc_prec1.py
new file mode 100644
index 0000000..d4fad6e
--- /dev/null
+++ b/tests/yacc_prec1.py
@@ -0,0 +1,65 @@
+# -----------------------------------------------------------------------------
+# yacc_prec1.py
+#
+# Tests case where precedence specifier doesn't match up to terminals
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left', '+', '-'),
+ ('left', '*', '/'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+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
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_rr.py b/tests/yacc_rr.py
new file mode 100644
index 0000000..de78a58
--- /dev/null
+++ b/tests/yacc_rr.py
@@ -0,0 +1,69 @@
+# -----------------------------------------------------------------------------
+# yacc_rr.py
+#
+# A grammar with a reduce/reduce conflict
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_assign_2(t):
+ 'statement : NAME EQUALS NUMBER'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+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
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_rr_unused.py b/tests/yacc_rr_unused.py
new file mode 100644
index 0000000..70a8c78
--- /dev/null
+++ b/tests/yacc_rr_unused.py
@@ -0,0 +1,27 @@
+# -----------------------------------------------------------------------------
+# yacc_rr_unused.py
+#
+# A grammar with reduce/reduce conflicts and a rule that never
+# gets reduced.
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+tokens = ('A', 'B', 'C')
+
+def p_grammar(p):
+ '''
+ rule1 : rule2 B
+ | rule2 C
+
+ rule2 : rule3 B
+ | rule4
+ | rule5
+
+ rule3 : A
+
+ rule4 : A
+
+ rule5 : A
+ '''
+
+yacc.yacc()
diff --git a/tests/yacc_simple.py b/tests/yacc_simple.py
new file mode 100644
index 0000000..5261838
--- /dev/null
+++ b/tests/yacc_simple.py
@@ -0,0 +1,65 @@
+# -----------------------------------------------------------------------------
+# yacc_simple.py
+#
+# A simple, properly specifier grammar
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+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
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_sr.py b/tests/yacc_sr.py
new file mode 100644
index 0000000..0f15a50
--- /dev/null
+++ b/tests/yacc_sr.py
@@ -0,0 +1,60 @@
+# -----------------------------------------------------------------------------
+# yacc_sr.py
+#
+# A grammar with shift-reduce conflicts
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+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'
+ 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
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_term1.py b/tests/yacc_term1.py
new file mode 100644
index 0000000..1c1d8c2
--- /dev/null
+++ b/tests/yacc_term1.py
@@ -0,0 +1,65 @@
+# -----------------------------------------------------------------------------
+# yacc_term1.py
+#
+# Terminal used on the left-hand-side
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'NUMBER : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+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
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_unicode_literals.py b/tests/yacc_unicode_literals.py
new file mode 100644
index 0000000..04f6cfd
--- /dev/null
+++ b/tests/yacc_unicode_literals.py
@@ -0,0 +1,67 @@
+# -----------------------------------------------------------------------------
+# yacc_unicode_literals
+#
+# Test for unicode literals on Python 2.x
+# -----------------------------------------------------------------------------
+from __future__ import unicode_literals
+
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+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
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_unused.py b/tests/yacc_unused.py
new file mode 100644
index 0000000..7bc6749
--- /dev/null
+++ b/tests/yacc_unused.py
@@ -0,0 +1,74 @@
+# -----------------------------------------------------------------------------
+# yacc_unused.py
+#
+# A grammar with an unused rule
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+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
+
+def p_expr_list(t):
+ 'exprlist : exprlist COMMA expression'
+ pass
+
+def p_expr_list_2(t):
+ 'exprlist : expression'
+ pass
+
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_unused_rule.py b/tests/yacc_unused_rule.py
new file mode 100644
index 0000000..2e08d6d
--- /dev/null
+++ b/tests/yacc_unused_rule.py
@@ -0,0 +1,69 @@
+# -----------------------------------------------------------------------------
+# yacc_unused_rule.py
+#
+# Grammar with an unused rule
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+precedence = (
+ ('left','PLUS','MINUS'),
+ ('left','TIMES','DIVIDE'),
+ ('right','UMINUS'),
+ )
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+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
+
+def p_integer(t):
+ 'integer : NUMBER'
+ t[0] = t[1]
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_uprec.py b/tests/yacc_uprec.py
new file mode 100644
index 0000000..f173509
--- /dev/null
+++ b/tests/yacc_uprec.py
@@ -0,0 +1,60 @@
+# -----------------------------------------------------------------------------
+# yacc_uprec.py
+#
+# A grammar with a bad %prec specifier
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+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
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+
diff --git a/tests/yacc_uprec2.py b/tests/yacc_uprec2.py
new file mode 100644
index 0000000..85c0f2f
--- /dev/null
+++ b/tests/yacc_uprec2.py
@@ -0,0 +1,60 @@
+# -----------------------------------------------------------------------------
+# yacc_uprec2.py
+#
+# A grammar with a bad %prec specifier
+# -----------------------------------------------------------------------------
+import ply.yacc as yacc
+
+from calclex import tokens
+
+# Parsing rules
+
+# dictionary of names
+names = { }
+
+def p_statement_assign(t):
+ 'statement : NAME EQUALS expression'
+ names[t[1]] = t[3]
+
+def p_statement_expr(t):
+ 'statement : expression'
+ print(t[1])
+
+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'
+ 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
+
+def p_error(t):
+ print("Syntax error at '%s'" % t.value)
+
+yacc.yacc()
+
+
+
+