Skip to content

Instantly share code, notes, and snippets.

@dittos
Created August 23, 2010 09:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dittos/545110 to your computer and use it in GitHub Desktop.
Save dittos/545110 to your computer and use it in GitHub Desktop.
# 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),
print
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