# A simple sample parser for S-expressions by SamR import unittest def parse_file(fname): """Parse an S expression like thingy out of a file.""" f = open(fname, 'r') result = parse(read(f)) close(f) return result def drop_whitespace(str): """Remove the initial whitespace from str""" if len(str) == 0: return str i = 0 while (i < len(str) and str[i].isspace()): i = i+1 return(str[i:]) def parse(stuff): """Parse a string and return the resulting s-expression. Only works on correctly formatted strings.""" return parse_kernel(stuff)[0] def parse_kernel(stuff): """Parse a string and return a pair of what we've parsed and the remainder of the string. Only works correctly on correctly formatted strings.""" # Skip over whitespace. Needs improvement stuff = drop_whitespace(stuff) # Special case: Nothing left if (len(stuff) == 0): return ("EMPTY","") # Handle nested/parenthesized stuff elif (stuff[0] == '('): contents = [] # drop the open paren stuff = drop_whitespace(stuff[1:]) while (len(stuff) > 0) and (stuff[0] != ')'): (value, rest) = parse_kernel(stuff) contents.append(value) stuff = drop_whitespace(rest) # Drop the close paren stuff = stuff[1:] return (contents,stuff) # Unlikely special case elif (stuff[0] == ')'): return ("RIGHT",stuff) # Handle simple symbols/strings elif stuff[0].isalpha(): i = 1 while i < len(stuff) and stuff[i].isalpha(): i = i + 1 return (stuff[0:i], stuff[i:]) # Something's wrong else: return ["ERROR: " + stuff, ''] class Parse_UnitTests(unittest.TestCase): def null_tests(self): self.assertEqual(parse('()'), []) self.assertEqual(parse(' ()'), []) self.assertEqual(parse('() '), []) self.assertEqual(parse('( )'), []) def symbol_tests(self): self.assertEqual('hello', 'hello') self.assertEqual('goodbye', 'goodbye') def simple_nesting_tests(self): self.assertEqual(parse('(())'), [[]]) self.assertEqual(parse('(a)'), ['a']) self.assertEqual(parse('(a b)'), ['a','b']) def deep_nesting_tests(self): self.assertEqual(parse('((a))'), [['a']]) def ParseTestSuite(): suite = unittest.TestSuite() suite.addTest(Parse_UnitTests("null_tests")) suite.addTest(Parse_UnitTests("symbol_tests")) suite.addTest(Parse_UnitTests("simple_nesting_tests")) suite.addTest(Parse_UnitTests("deep_nesting_tests")) return suite def runParseTests(): """Run the various parse tests.""" alltests = unittest.TestSuite(ParseTestSuite()) runner = unittest.TextTestRunner() runner.run(alltests)