## Creature Project ## CSC323 ## 4/17/10 ## Parser.py: An S-expression parser ## @Author: SamR import unittest from Predicate import * 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].isalnum(): 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, ''] def genpred(tree): '''build some predicates out of the parse tree''' cases = [ ('OR', lambda:OrPredicate([genpred(t) for t in tree[1:]])), ('AND', lambda:AndPredicate([genpred(t) for t in tree[1:]])), ('NOT', lambda:NotPredicate(genpred(tree[1]))), ('DIRECTION', lambda:DirectionalTestPredicate(tree[1], tree[2])), ('QUANTITY', lambda:QuantityPredicate(tree[1], tree[2])), ] for cond, func in cases: if tree[0] == cond: return func() raise Exception("Syntax Error!") def gen(tree): '''parse the mini-programming language of species files''' cases = [ ('define', lambda:(tree[1], gen(tree[2]))), ('instruction', lambda:(tree[1], gen(tree[2]))), ('predicate', lambda:genpred(tree[1:])), ('species', lambda:(tree[1], tree[2], genrule(tree[3:]))), ] 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']]) class genpred_UnitTests(unittest.TestCase): def direction(self): st = '(DIRECTION NE BLUE)' pred = genpred(parse(st)) self.assertTrue(isinstance(pred, DirectionalTestPredicate)) def quantity(self): st = '(QUANTITY 2 BLUE)' pred = genpred(parse(st)) self.assertTrue(isinstance(pred, QuantityPredicate)) def not_test(self): st = '(NOT (QUANTITY 2 BLUE))' pred = genpred(parse(st)) self.assertTrue(isinstance(pred, NotPredicate)) def or_test(self): st = '(OR (QUANTITY 2 BLUE))' pred = genpred(parse(st)) self.assertTrue(isinstance(pred, OrPredicate)) def and_test(self): st = '(AND (QUANTITY 2 BLUE))' pred = genpred(parse(st)) self.assertTrue(isinstance(pred, AndPredicate)) def genpredTestSuite(): suite = unittest.TestSuite() suite.addTest(genpred_UnitTests("direction")) suite.addTest(genpred_UnitTests("quantity")) suite.addTest(genpred_UnitTests("not_test")) suite.addTest(genpred_UnitTests("or_test")) suite.addTest(genpred_UnitTests("and_test")) return suite 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()) alltests.addTest(genpredTestSuite()) runner = unittest.TextTestRunner() runner.run(alltests) if __name__ == '__main__': runParseTests()