Permalink
Browse files

Merge pull request #135 from laerreal/bugfixes

Bugfixes for C preprocessor
  • Loading branch information...
2 parents 149a11c + 069239b commit 6860652be4069eaac8be88af1400f69a5197284f @dabeaz committed Dec 2, 2017
Showing with 111 additions and 2 deletions.
  1. +9 −2 ply/cpp.py
  2. +1 −0 test/README
  3. +101 −0 test/testcpp.py
View
@@ -411,10 +411,11 @@ def macro_prescan(self,macro):
elif (i > 0 and macro.value[i-1].value == '##'):
macro.patch.append(('c',argnum,i-1))
del macro.value[i-1]
+ i -= 1
continue
elif ((i+1) < len(macro.value) and macro.value[i+1].value == '##'):
macro.patch.append(('c',argnum,i))
- i += 1
+ del macro.value[i + 1]
continue
# Standard expansion
else:
@@ -509,7 +510,7 @@ def expand_macros(self,tokens,expanded=None):
j = i + 1
while j < len(tokens) and tokens[j].type in self.t_WS:
j += 1
- if tokens[j].value == '(':
+ if j < len(tokens) and tokens[j].value == '(':
tokcount,args,positions = self.collect_args(tokens[j:])
if not m.variadic and len(args) != len(m.arglist):
self.error(self.source,t.lineno,"Macro %s requires %d arguments" % (t.value,len(m.arglist)))
@@ -535,6 +536,12 @@ def expand_macros(self,tokens,expanded=None):
r.lineno = t.lineno
tokens[i:j+tokcount] = rep
i += len(rep)
+ else:
+ # This is not a macro. It is just a word which
+ # equals to name of the macro. Hence, go to the
+ # next token.
+ i += 1
+
del expanded[t.value]
continue
elif t.value == '__LINE__':
View
@@ -3,5 +3,6 @@ conditions. To run:
$ python testlex.py
$ python testyacc.py
+ $ python testcpp.py
The script 'cleanup.sh' cleans up this directory to its original state.
View
@@ -0,0 +1,101 @@
+from unittest import TestCase, main
+
+from multiprocessing import Process, Queue
+from six.moves.queue import Empty
+
+import sys
+
+if ".." not in sys.path:
+ sys.path.insert(0, "..")
+
+from ply.lex import lex
+from ply.cpp import *
+
+
+def preprocessing(in_, out_queue):
+ out = None
+
+ try:
+ p = Preprocessor(lex())
+ p.parse(in_)
+ tokens = [t.value for t in p.parser]
+ out = "".join(tokens)
+ finally:
+ out_queue.put(out)
+
+class CPPTests(TestCase):
+ "Tests related to ANSI-C style lexical preprocessor."
+
+ def __test_preprocessing(self, in_, expected, time_limit = 1.0):
+ out_queue = Queue()
+
+ preprocessor = Process(
+ name = "PLY`s C preprocessor",
+ target = preprocessing,
+ args = (in_, out_queue)
+ )
+
+ preprocessor.start()
+
+ try:
+ out = out_queue.get(timeout = time_limit)
+ except Empty:
+ preprocessor.terminate()
+ raise RuntimeError("Time limit exceeded!")
+ else:
+ self.assertMultiLineEqual(out, expected)
+
+ def test_concatenation(self):
+ self.__test_preprocessing("""\
+#define a(x) x##_
+#define b(x) _##x
+#define c(x) _##x##_
+#define d(x,y) _##x##y##_
+
+a(i)
+b(j)
+c(k)
+d(q,s)"""
+ , """\
+
+
+
+
+
+i_
+_j
+_k_
+_qs_"""
+ )
+
+ def test_deadloop_macro(self):
+ # If there is a word which equals to name of a parametrized macro, then
+ # attempt to expand such word as a macro manages the parser to fall
+ # into an infinite loop.
+
+ self.__test_preprocessing("""\
+#define a(x) x
+
+a;"""
+ , """\
+
+
+a;"""
+ )
+
+ def test_index_error(self):
+ # If there are no tokens after a word ("a") which equals to name of
+ # a parameterized macro, then attempt to expand this word leads to
+ # IndexError.
+
+ self.__test_preprocessing("""\
+#define a(x) x
+
+a"""
+ , """\
+
+
+a"""
+ )
+
+main()

0 comments on commit 6860652

Please sign in to comment.