let's get more serious (splat that mess)

This commit is contained in:
Loic Nageleisen 2013-10-03 19:22:54 +02:00
parent 91020ddfe6
commit e8a036bb16
7 changed files with 132 additions and 96 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
*.pyc

View file

@ -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))")))

1
requirements.txt Normal file
View file

@ -0,0 +1 @@
rply==0.6.1

39
test.py Normal file
View file

@ -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()

0
wasp/__init__.py Normal file
View file

54
wasp/parser/__init__.py Normal file
View file

@ -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))

37
wasp/parser/box.py Normal file
View file

@ -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)