Skip to content
Browse files

more reader tests, doc update, #= and @foo syntax handled

  • Loading branch information...
1 parent b81338d commit d523fbde4c91adf2457311436250da148f627c6b @stirfoo stirfoo committed with aschoerk Mar 22, 2012
Showing with 147 additions and 19 deletions.
  1. +30 −2 clojure/lang/lispreader.py
  2. +2 −2 doc/reader.rst
  3. +115 −15 tests/reader-tests.py
View
32 clojure/lang/lispreader.py
@@ -601,7 +601,7 @@ def readDelimitedList(delim, rdr, isRecursive):
def readNamedUnicodeChar(rdr):
"""Read \N{foo} syntax, starting at the {.
- rdr -- a read/unread
+ rdr -- a read/unread-able object
May raise ReaderException. Return the unicode character named by foo."""
buf = []
@@ -872,6 +872,12 @@ def registerArg(arg):
def fnReader(rdr, lparen):
+ """Read an anonymous function #() from reader
+
+ rdr -- a read/unread-able object
+ lparen -- ignored
+
+ Return an IPersistentList"""
if ARG_ENV.deref() is not None:
raise IllegalStateException("Nested #()s are not allowed")
pushThreadBindings(RT.map(ARG_ENV, EMPTY_MAP))
@@ -1007,6 +1013,25 @@ def garg(n):
str(RT.nextID()) + "#")
+def derefNotImplemented(rdr, _):
+ """Unconditionally raise ReaderException.
+
+ The deref syntax @foo is not currently implemented. @foo will pass through
+ silently as a symbol unless it's caught here, as it should be."""
+ raise ReaderException("Deref syntax @foo not currently implemented.",
+ rdr)
+
+
+def evalReaderNotImplemented(rdr, _):
+ """Unconditionally raise ReaderException.
+
+ The eval syntax #= not currently implemented and should be caught by the
+ #reader. This message is more informative than the `no dispatch macro'
+ message."""
+ raise ReaderException("Eval syntax #= not currently implemented.",
+ rdr)
+
+
macros = {'\"': stringReader,
"\'": wrappingReader(_QUOTE_),
"(": listReader,
@@ -1021,7 +1046,9 @@ def garg(n):
"%": argReader,
"`": SyntaxQuoteReader(),
"~": unquoteReader,
- "\\": characterReader}
+ "\\": characterReader,
+ "@": derefNotImplemented, # temporary?
+ }
dispatchMacros = {"\"": regexReader,
"{": setReader,
@@ -1032,4 +1059,5 @@ def garg(n):
"^": metaReader,
# Use Python raw string syntax as #r"foo"
"r": rawRegexReader,
+ "=": evalReaderNotImplemented, # temporary?
}
View
4 doc/reader.rst
@@ -373,7 +373,7 @@ return the list::
Clojure Conformance
-------------------
-Same syntax.
+None. This is not currently implemented. The reader will raise an exception.
^
===
@@ -448,7 +448,7 @@ list.
Clojure Conformance
-------------------
-Same syntax.
+None. This is not currently implemented. The reader will raise an exception.
#_
===
View
130 tests/reader-tests.py
@@ -3,36 +3,46 @@
"""reader-tests.py
-<stirfoo@gmail.com>
Friday, March 16 2012
"""
-import unittest, string, re
+import re
+import string
+import unittest
+
from random import choice
from fractions import Fraction
-
-from clojure.lang.lispreader import read
-from clojure.lang.character import character
+from clojure.lang.lispreader import read, readDelimitedList
+from clojure.lang.symbol import Symbol
+from clojure.lang.character import character, Character
+from clojure.lang.ipersistentlist import IPersistentList
+from clojure.lang.persistentlist import PersistentList
+from clojure.lang.persistentlist import EmptyList
+from clojure.lang.persistentvector import PersistentVector
+from clojure.lang.persistenthashmap import PersistentHashMap
+from clojure.lang.persistenthashset import PersistentHashSet
from clojure.lang.fileseq import StringReader
from clojure.lang.cljexceptions import ReaderException
+regexType = type(re.compile(""))
+
class TestReader(unittest.TestCase):
# literal integers
def testIntegerReader_PASS(self):
# base 8
- for k,v in base8IntegerMap_PASS.items():
+ for k, v in base8IntegerMap_PASS.items():
r = StringReader(k)
self.assertEqual(read(r, False, None, False), v)
# base 10
- for k,v in base10IntegerMap_PASS.items():
+ for k, v in base10IntegerMap_PASS.items():
r = StringReader(k)
self.assertEqual(read(r, False, None, False), v)
# base 16
- for k,v in base16IntegerMap_PASS.items():
+ for k, v in base16IntegerMap_PASS.items():
r = StringReader(k)
self.assertEqual(read(r, False, None, False), v)
# base N
- for k,v in baseNIntegerMap_PASS.items():
+ for k, v in baseNIntegerMap_PASS.items():
r = StringReader(k)
self.assertEqual(read(r, False, None, False), v)
def testIntegerReader_FAIL(self):
@@ -41,7 +51,7 @@ def testIntegerReader_FAIL(self):
self.assertRaises(ReaderException, read, r, False, None, False)
# literal floating point
def testFloatingPointReader_PASS(self):
- for k,v in floatingPointMap_PASS.items():
+ for k, v in floatingPointMap_PASS.items():
r = StringReader(k)
self.assertEqual(read(r, False, None, False), v)
def testFloatingPointReader_FAIL(self):
@@ -50,7 +60,7 @@ def testFloatingPointReader_FAIL(self):
self.assertRaises(ReaderException, read, r, False, None, False)
# literal ratios
def testRationalReader_PASS(self):
- for k,v in rationalMap_PASS.items():
+ for k, v in rationalMap_PASS.items():
r = StringReader(k)
self.assertEqual(read(r, False, None, False), v)
def testRationalReader_FAIL(self):
@@ -59,7 +69,7 @@ def testRationalReader_FAIL(self):
self.assertRaises(ReaderException, read, r, False, None, False)
# literal characters
def testCharacterReader_PASS(self):
- for k,v in literalCharacterMap_PASS.items():
+ for k, v in literalCharacterMap_PASS.items():
r = StringReader(k)
self.assertEqual(read(r, False, None, False), v)
def testCharacterReader_FAIL(self):
@@ -68,7 +78,7 @@ def testCharacterReader_FAIL(self):
self.assertRaises(ReaderException, read, r, False, None, False)
# literal strings
def testStringReader_PASS(self):
- for k,v in literalStringMap_PASS.items():
+ for k, v in literalStringMap_PASS.items():
r = StringReader('"' + k + '"')
self.assertEqual(read(r, False, None, False), v)
def testStringReader_FAIL(self):
@@ -80,7 +90,7 @@ def testStringReader_FAIL(self):
self.assertRaises(ReaderException, read, r, False, None, False)
# literal regex pattern strings
def testRegexPattern_PASS(self):
- for k,v in regexPatternMap_PASS.items():
+ for k, v in regexPatternMap_PASS.items():
r = StringReader(k)
self.assertEqual(read(r, False, None, False).pattern, v.pattern)
def testRegexPattern_FAIL(self):
@@ -89,13 +99,31 @@ def testRegexPattern_FAIL(self):
self.assertRaises(ReaderException, read, r, False, None, False)
# literal raw regex pattern strings
def testRawRegexPattern_PASS(self):
- for k,v in rawRegexPatternMap_PASS.items():
+ for k, v in rawRegexPatternMap_PASS.items():
r = StringReader(k)
self.assertEqual(read(r, False, None, False).pattern, v.pattern)
def testRawRegexPattern_FAIL(self):
for s in rawRegexPattern_FAIL:
r = StringReader(s)
self.assertRaises(ReaderException, read, r, False, None, False)
+ # delimited lists
+ def testDelimitedLists_PASS(self):
+ # length test
+ for k, v in delimitedListLength_PASS.items():
+ r = StringReader(k)
+ delim = k[-1]
+ self.assertEqual(readDelimitedList(delim, r, False), v)
+ # returned type tests
+ def testReturnedType_PASS(self):
+ for k, v in returnedType_PASS.items():
+ r = StringReader(k)
+ self.assertEqual(type(read(r, False, None, False)), v)
+ # miscellaneous failures
+ def testMiscellaneous_FAIL(self):
+ for s in miscellaneous_FAIL:
+ r = StringReader(s)
+ self.assertRaises(ReaderException, read, r, False, None, False)
+
# ======================================================================
# Literal Integer Cases
@@ -494,3 +522,75 @@ def gen_baseNIntegerMap_PASS():
'#r"\U"', '#r"\U1"', '#r"\U12"', '#r"\U123"', '#r"\U1234"', '#r"\U12345"',
'#r"\U123456"', '#r"\U1234567"',
]
+
+# ======================================================================
+# Literal Delimited Lists
+# ======================================================================
+
+# The keys define the clojure syntax of any object that would result in a call
+# to lispreader.readDelimitedList() (minus the leading macro character(s)).
+# Some objects like map and set have the same terminating character `}'. So
+# there is only one entry for both.
+#
+# The value is a the expected contents of the Python list returned from
+# readDelimitedList(). Integers are used because I don't care what type the
+# items are. There are separate tests for that.
+delimitedListLength_PASS = {
+ "]" : [],
+ "}" : [],
+ ")" : [],
+ "0]" : [0],
+ "0)" : [0],
+ "0}" : [0],
+ "0 0]" : [0, 0],
+ "0 0)" : [0, 0],
+ "0 0}" : [0, 0],
+ "0 0]" : [0, 0],
+ "0 0)" : [0, 0],
+ "0 0}" : [0, 0],
+ }
+
+# ======================================================================
+# Returned Type
+# ======================================================================
+returnedType_PASS = {
+ "\\x" : Character,
+ "%foo" : Symbol, # not in an anonymous function #()
+ "[]" : PersistentVector,
+ "()" : EmptyList,
+ "{}" : PersistentHashMap,
+ '"foo"' : str,
+ '#"foo"' : regexType,
+ '#r"foo"' : regexType,
+ "#()" : PersistentList,
+ "#{}" : PersistentHashSet,
+ "'foo" : PersistentList,
+ "~foo" : PersistentList,
+ "~@(foo)" : PersistentList,
+ "#^:foo()" : EmptyList,
+ "^:foo()" : EmptyList,
+ "; comment" : type(None),
+ "#_ foo" : type(None),
+ "0" : int,
+ "0x0" : int,
+ "041" : int,
+ "2r10" : int,
+ "2.2" : float,
+ "2e-3" : float,
+ "1/2" : Fraction,
+ "foo" : Symbol
+ }
+
+# ======================================================================
+# Miscellaneous Failures
+# Any type of random failures should go here
+# ======================================================================
+
+miscellaneous_FAIL = [
+ # always raises
+ "#<unreadable object>",
+ # deref not implemented (yet)
+ "@foo",
+ # reader eval not implemented (yet)
+ "#=foo",
+ ]

0 comments on commit d523fbd

Please sign in to comment.
Something went wrong with that request. Please try again.