Created
August 23, 2010 09:11
-
-
Save dittos/545110 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# encoding: utf-8 | |
""" | |
TODO | |
---- | |
* boolean, 조건문 | |
* typing | |
* 음수, 소수, 분수 | |
* 단어 정의할 때 덮어쓰기? | |
""" | |
import string | |
import itertools | |
from StringIO import StringIO | |
class Token(object): | |
def __init__(self, value): | |
self.value = value | |
self.suffix = None | |
def __repr__(self): | |
return '%r (%s)' % (self.value, type(self).__name__) | |
class Number(Token): pass | |
class String(Token): pass | |
class Word(Token): pass | |
class Reader(object): | |
""" | |
>>> from StringIO import StringIO | |
>>> reader = Reader(StringIO("ab")) | |
>>> reader.peek() | |
'a' | |
>>> reader.next() | |
'a' | |
>>> reader.next() | |
'b' | |
>>> reader.peek() | |
'' | |
>>> reader.next() | |
'' | |
""" | |
def __init__(self, stream): | |
self.stream = stream | |
self.lookahead = None | |
def peek(self): | |
if not self.lookahead: | |
self.lookahead = self.next() | |
return self.lookahead | |
def next(self): | |
if self.lookahead: | |
_, self.lookahead = self.lookahead, None | |
return _ | |
else: | |
return self.stream.read(1) | |
class StringReader(Reader): | |
def __init__(self, string): | |
super(StringReader, self).__init__(StringIO(string)) | |
def scanwhile(reader, predicate): | |
""" | |
>>> reader = StringReader('aaaaab') | |
>>> scanwhile(reader, lambda ch: ch == 'a') | |
'aaaaa' | |
>>> reader.next() | |
'b' | |
""" | |
buf = '' | |
while True: | |
ch = reader.peek() | |
if not ch: | |
break | |
if predicate(ch): | |
buf += ch | |
reader.next() | |
else: | |
break | |
return buf | |
def isspace(ch): | |
return ch in string.whitespace | |
def scan(reader): | |
""" | |
>>> for token in scan(StringReader('123 "hello" + .')): | |
... print token | |
123 (Number) | |
'hello' (String) | |
'+' (Word) | |
'.' (Word) | |
""" | |
while True: | |
ch = reader.peek() | |
if not ch: | |
raise StopIteration | |
if ch == '"': | |
reader.next() # skip quote | |
token = String(scanwhile(reader, lambda ch: ch != '"')) | |
reader.next() # skip quote | |
token.suffix = scanwhile(reader, lambda ch: not isspace(ch)) | |
yield token | |
elif ch == '(': | |
reader.next() # skip ( | |
scanwhile(reader, lambda ch: ch != ')') | |
reader.next() # skip ) | |
elif ch in string.digits: | |
token = Number(int(scanwhile(reader, lambda ch: ch in string.digits))) | |
token.suffix = scanwhile(reader, lambda ch: not isspace(ch)) | |
yield token | |
elif not isspace(ch): | |
yield Word(scanwhile(reader, lambda ch: not isspace(ch))) | |
else: | |
reader.next() # ignore whitespaces | |
class Vocabulary(object): | |
def __init__(self): | |
self.words = {} | |
def word(self, *names): | |
def _inner(func): | |
for name in names: | |
self.register(name, func) | |
return func | |
return _inner | |
def register(self, name, func): | |
self.words[name] = func | |
def get(self, name): | |
return self.words[name] | |
std = Vocabulary() | |
@std.word(u'버려') # ( a -- ) | |
def discard(env): | |
env.pop() | |
@std.word(u'ㄱㄱ', u'ㄲ') # ( a -- a a ) | |
def dup(env): | |
a = env.pop() | |
env.push(a) | |
env.push(a) | |
@std.word(u'ㄴㄱ') # ( a b -- b a ) | |
def swap(env): | |
b = env.pop() | |
a = env.pop() | |
env.push(b) | |
env.push(a) | |
@std.word(u'더해') # ( a b -- a+b ) | |
def add(env): | |
env.push(env.pop() + env.pop()) | |
@std.word(u'실행') # ( f -- ) | |
def call(env): | |
env.execute(env.pop()) | |
@std.word(u'반복') # ( f n -- ) | |
def repeat(env): | |
n = env.pop() | |
f = env.pop() | |
for x in xrange(n): | |
env.execute(f) | |
@std.word(u'출력') # ( a -- ) | |
def prn(env): | |
print env.pop() | |
@std.word(u'종료') # ( -- ) | |
def quit(env): | |
raise SystemExit | |
@std.word(u'참') | |
def t(env): | |
env.push(True) | |
@std.word(u'거짓') | |
def f(env): | |
env.push(False) | |
class SyntaxError(Exception): pass | |
class Environment(object): | |
def __init__(self, vocab): | |
self.stack = [] | |
self.vocab = vocab | |
self.in_def = False | |
self.in_block = False | |
def call(self, word): | |
self.vocab.get(word)(self) | |
def push(self, value): | |
self.stack.append(value) | |
def pop(self): | |
return self.stack.pop() | |
def execute(self, token_iter): | |
for token in token_iter: | |
if type(token) == Word: | |
if token.value == ':': | |
if self.in_def: | |
raise SyntaxError("Cannot nest word definitions") | |
self.in_def = True | |
self.def_buf = [] | |
elif token.value == ';': | |
if not self.in_def: | |
raise SyntaxError("Cannot use ';' outside word definition") | |
self.in_def = False | |
self.vocab.register(self.def_buf[0].value, lambda env: env.execute(self.def_buf[1:])) | |
elif token.value == '[': | |
if not self.in_block: | |
self.in_block = True | |
self.block_buf = [] | |
self.block_depth = 0 | |
else: | |
self.block_buf.append(token) | |
self.block_depth += 1 | |
elif token.value == ']': | |
if not self.in_block: | |
raise SyntaxError("Cannot use ']' here") | |
if self.block_depth == 0: | |
self.in_block = False | |
self.push(self.block_buf) | |
else: | |
self.block_buf.append(token) | |
self.block_depth -= 1 | |
else: | |
if self.in_def: | |
self.def_buf.append(token) | |
elif self.in_block: | |
self.block_buf.append(token) | |
else: | |
self.call(token.value) | |
else: # value types | |
if self.in_def: | |
self.def_buf.append(token) | |
elif self.in_block: | |
self.block_buf.append(token) | |
else: | |
self.push(token.value) | |
def repl(): | |
import sys | |
import traceback | |
env = Environment(std) | |
while True: | |
prompt = '_ ' if env.in_def else '> ' | |
code = raw_input(prompt).decode(sys.stdin.encoding) | |
try: | |
env.execute(scan(StringReader(code))) | |
if not env.in_def and len(env.stack) > 0: | |
print 'Stack: ', | |
for content in env.stack: | |
print repr(content), | |
except SystemExit: | |
break | |
except: | |
traceback.print_exc(file=sys.stdout) | |
if __name__ == '__main__': | |
repl() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment