mirror of
https://github.com/lloeki/wasp.git
synced 2025-12-06 10:44:39 +01:00
let's get more serious (splat that mess)
This commit is contained in:
parent
91020ddfe6
commit
e8a036bb16
7 changed files with 132 additions and 96 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
*.pyc
|
||||||
96
parser.py
96
parser.py
|
|
@ -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
1
requirements.txt
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
rply==0.6.1
|
||||||
39
test.py
Normal file
39
test.py
Normal 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
0
wasp/__init__.py
Normal file
54
wasp/parser/__init__.py
Normal file
54
wasp/parser/__init__.py
Normal 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
37
wasp/parser/box.py
Normal 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)
|
||||||
|
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue