From e8a036bb16931cef1d30755bde8864b6d328b564 Mon Sep 17 00:00:00 2001 From: Loic Nageleisen Date: Thu, 3 Oct 2013 19:22:54 +0200 Subject: [PATCH] let's get more serious (splat that mess) --- .gitignore | 1 + parser.py | 96 ----------------------------------------- requirements.txt | 1 + test.py | 39 +++++++++++++++++ wasp/__init__.py | 0 wasp/parser/__init__.py | 54 +++++++++++++++++++++++ wasp/parser/box.py | 37 ++++++++++++++++ 7 files changed, 132 insertions(+), 96 deletions(-) create mode 100644 .gitignore delete mode 100644 parser.py create mode 100644 requirements.txt create mode 100644 test.py create mode 100644 wasp/__init__.py create mode 100644 wasp/parser/__init__.py create mode 100644 wasp/parser/box.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/parser.py b/parser.py deleted file mode 100644 index 8f3e5e4..0000000 --- a/parser.py +++ /dev/null @@ -1,96 +0,0 @@ -from __future__ import print_function -from rply import ParserGenerator, LexerGenerator -from rply.token import BaseBox - -lg = LexerGenerator() -lg.add("LPAREN", r"\(") -lg.add("RPAREN", r"\)") -lg.add("QUOTE", r"'") -lg.add("ATOM", r"[^\s()]+") -lg.ignore(r"\s+") - -pg = ParserGenerator(["QUOTE", "LPAREN", "RPAREN", "ATOM"]) - - -@pg.error -def error_handler(token): - type = token.gettokentype() - pos = token.getsourcepos() - raise ValueError("unexpected %s at (%s, %s)" % - (type, pos.lineno, pos.colno)) - - -@pg.production("main : sexpr") -def main(p): - return p[0] - - -@pg.production("sexpr : ATOM") -@pg.production("sexpr : QUOTE sexpr") -@pg.production("sexpr : LPAREN list RPAREN") -def sexpr(p): - if p[0].gettokentype() == "ATOM": - return BoxAtom(p[0].getstr()) - elif p[0].gettokentype() == "QUOTE": - return BoxQuote(p[1]) - else: - return p[1] - - -@pg.production("list : sexpr") -@pg.production("list : sexpr list") -def list(p): - if len(p) == 1: - return BoxPair(p[0]) - else: - return BoxPair(p[0], p[1]) - -lexer = lg.build() -parser = pg.build() - - -class BoxQuote(BaseBox): - def __init__(self, sexpr): - self.sexpr = sexpr - - def sexpr(self): - return self.sexpr - - def __str__(self): - return "' %s" % self.sexpr - - -class BoxPair(BaseBox): - def __init__(self, x, y=None): - self.x = x - self.y = y - - def __str__(self): - if self.y is None: - y = "NIL" - else: - y = self.y - return "(%s . %s)" % (self.x, y) - - -class BoxAtom(BaseBox): - def __init__(self, atom): - self.atom = atom - - def atom(self): - return self.atom - - def __str__(self): - return "%s" % (self.atom) - - -print(parser.parse(lexer.lex("1"))) -print(parser.parse(lexer.lex("'1"))) -print(parser.parse(lexer.lex("+"))) -print(parser.parse(lexer.lex("'+"))) -print(parser.parse(lexer.lex("(+ 1 2)"))) -print(parser.parse(lexer.lex("'(+ 1 2)"))) -print(parser.parse(lexer.lex("(+ 1 (* 3 2))"))) -print(parser.parse(lexer.lex("'(+ 1 (* 3 2))"))) -print(parser.parse(lexer.lex("(+ (* 4 5) (* 3 2))"))) -print(parser.parse(lexer.lex("(+ '(* 4 5) (* 3 2))"))) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f676474 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +rply==0.6.1 diff --git a/test.py b/test.py new file mode 100644 index 0000000..08a1533 --- /dev/null +++ b/test.py @@ -0,0 +1,39 @@ +from unittest import TestCase +import wasp.parser as parser + +class TestParser(TestCase): + def test_atom(self): + self.assertEqual(str(parser.parse("1")), "1") + + def test_quoted_atom(self): + self.assertEqual(str(parser.parse("'1")), "'1") + + def test_nonalpha_symbol(self): + self.assertEqual(str(parser.parse("+")), "+") + + def test_quoted_nonalpha_symbol(self): + self.assertEqual(str(parser.parse("'+")), "'+") + + def test_list(self): + self.assertEqual(str(parser.parse("(+ 1 2)")), + "(+ . (1 . (2 . NIL)))") + + def test_quoted_list(self): + self.assertEqual(str(parser.parse("'(+ 1 2)")), + "'(+ . (1 . (2 . NIL)))") + + def test_list_in_list(self): + self.assertEqual(str(parser.parse("(+ 1 (* 3 2))")), + "(+ . (1 . ((* . (3 . (2 . NIL))) . NIL)))") + + def test_lists_in_list(self): + self.assertEqual(str(parser.parse("(+ (* 4 5) (* 3 2))")), + "(+ . ((* . (4 . (5 . NIL))) . ((* . (3 . (2 . NIL))) . NIL)))") + + def test_quote_in_list(self): + self.assertEqual(str(parser.parse("(+ '1 (* 3 2))")), + "(+ . ('1 . ((* . (3 . (2 . NIL))) . NIL)))") + +if __name__ == '__main__': + import unittest + unittest.main() diff --git a/wasp/__init__.py b/wasp/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/wasp/parser/__init__.py b/wasp/parser/__init__.py new file mode 100644 index 0000000..a1c1dda --- /dev/null +++ b/wasp/parser/__init__.py @@ -0,0 +1,54 @@ +from rply import ParserGenerator, LexerGenerator +import box + + +lg = LexerGenerator() +lg.add("LPAREN", r"\(") +lg.add("RPAREN", r"\)") +lg.add("QUOTE", r"'") +lg.add("ATOM", r"[^\s()]+") +lg.ignore(r"\s+") + +pg = ParserGenerator(["QUOTE", "LPAREN", "RPAREN", "ATOM"], + precedence=[], + cache_id="wasp") + + +@pg.error +def error_handler(token): + type = token.gettokentype() + pos = token.getsourcepos() + raise ValueError("unexpected %s at (%s, %s)" % + (type, pos.lineno, pos.colno)) + + +@pg.production("main : sexpr") +def main(p): + return p[0] + + +@pg.production("sexpr : ATOM") +@pg.production("sexpr : QUOTE sexpr") +@pg.production("sexpr : LPAREN list RPAREN") +def sexpr(p): + if p[0].gettokentype() == "ATOM": + return box.Atom(p[0].getstr()) + elif p[0].gettokentype() == "QUOTE": + return box.Quote(p[1]) + else: + return p[1] + + +@pg.production("list : sexpr") +@pg.production("list : sexpr list") +def list(p): + if len(p) == 1: + return box.Pair(p[0]) + else: + return box.Pair(p[0], p[1]) + +lexer = lg.build() +parser = pg.build() + +def parse(string): + return parser.parse(lexer.lex(string)) diff --git a/wasp/parser/box.py b/wasp/parser/box.py new file mode 100644 index 0000000..45317f8 --- /dev/null +++ b/wasp/parser/box.py @@ -0,0 +1,37 @@ +from rply.token import BaseBox + +class Quote(BaseBox): + def __init__(self, sexpr): + self.sexpr = sexpr + + def sexpr(self): + return self.sexpr + + def __str__(self): + return "'%s" % self.sexpr + + +class Pair(BaseBox): + def __init__(self, x, y=None): + self.x = x + self.y = y + + def __str__(self): + if self.y is None: + y = "NIL" + else: + y = self.y + return "(%s . %s)" % (self.x, y) + + +class Atom(BaseBox): + def __init__(self, atom): + self.atom = atom + + def atom(self): + return self.atom + + def __str__(self): + return "%s" % (self.atom) + +