ff4e0dfb |
from __future__ import print_function
import sys
import inspect
|
5267dfda |
import contextlib
@contextlib.contextmanager
def redir_stdout(new):
old = sys.stdout
sys.stdout = new
yield
sys.stdout = old
|
ff4e0dfb |
SPACE = None
class _RUN: pass
def read_word(fil):
char = ' '
buffer = []
delim = SPACE
spaces = {' ', '\n', '\t'}
while char != '':
char = fil.read(1)
if char == '':
yield ''.join(buffer).strip()
break
buffer.append(char)
if delim != SPACE and buffer[-len(delim):] == delim:
delim = yield ''.join(buffer[:-len(delim)])
buffer = []
elif char in spaces:
delim = yield ''.join(buffer[:-1]).strip()
if char == '\n':
yield _RUN
buffer = []
def skip_run(reader):
skipped = False
r = reader.next()
while r == _RUN:
skipped = skipped or r == _RUN
#print(r, end=':')
r = reader.next()
#print(r, end=' ')
#print('out')
return r, skipped
import collections
import traceback
class SymbolDict(collections.MutableMapping):
def __init__(self, *args, **kwargs):
self.ctr = 0
self.items_ = set()
self.dct = dict(*args, **kwargs)
def __getitem__(self, key):
if self.dct[key] != []:
return self.dct[key][-1][1]
else:
raise KeyError("No such key '%s'" % key)
def __setitem__(self, key, value):
self.ctr += 1
self.items_.add(key)
self.dct.setdefault(key, []).append((self.ctr, value))
def __delitem__(self, key):
state = self.dct[key][-1][0]
for a in self.dct.keys():
while self.dct[a] and self.dct[a][-1][0] >= state:
self.dct[a].pop()
if self.dct[a] == []: self.dct.pop(a)
forget = __delitem__
def __iter__(self):
return (x for x in self.dct.keys())
def __len__(self):
return len(self.dct)
import random
class Reader(object):
symbols = SymbolDict()
def rev_pctr(self, stack, rstack):
num = stack.pop()
self.program_ctr -= num
def adv_pctr(self, stack, rstack):
num = stack.pop()
self.program_ctr += num
def adv_pctr0(self, stack, rstack):
#print('adv0 -->', stack, rstack)
num = stack.pop()
cond = stack.pop()
if cond == 0:
stack.append(num)
self.adv_pctr(stack, rstack)
def save_program_ctr(self):
self.pcs.append(self.program_ctr)
def restore_program_ctr(self):
self.program_ctr = self.pcs.pop()
def rollback_program(self):
self.restore_program_ctr()
del self.program[self.program_ctr:]
def splice_program(self, words):
begin = self.peek_saved_program_ctr()
end = self.program_ctr + 1
self.program[begin:end] = words
def peek_saved_program_ctr(self):
return self.pcs[-1]
def drop_saved_program_ctr(self):
return self.pcs.pop()
def if_(self, stack, rstack=None):
def if_reader(gen):
counter = 0
if_clause = []
else_clause = []
cur_clause = if_clause
for word in gen:
if word == 'if':
cur_clause.extend(if_reader(gen))
continue
elif word == 'else':
cur_clause = else_clause
continue
elif word == 'then':
else_clause.extend(['nop'])
break
cur_clause.extend(self.expand_word(word))
if_clause.extend(['%d' % len(else_clause), 'adv'])
if_clause.insert(0, '%d' % len(if_clause))
if_clause.insert(1, 'adv0')
result = if_clause + else_clause
return result
## Below we replace the if statement with a call to lower-level primitives
## i.e. if is implemented as a kind of macro.
self.save_program_ctr()
self.program_ctr += 1 # since the program counter doesn't update until after the yield
gen = self.read_word()
self.splice_program(if_reader(gen))
self.restore_program_ctr()
self.program_ctr -= 1 # in order to pick up the beginning of the if statement
def words(self, stack, rstack=None):
words = sorted(self.symbols.keys())
mlen = max(len(x) for x in words)
c = 80 // (mlen + 2)
while words:
head, words = words[:c], words[c:]
|
5267dfda |
print(' '.join(x.center(mlen) for x in head), file=self.out)
|
ff4e0dfb |
def forget(self, stack, rstack=None):
self.program_ctr += 1 # since the program counter doesn't update until after the yield
name, skipped = skip_run(self.read_word())
self.symbols.forget(name)
if skipped: self.inp.append([_RUN])
def colon(self, stack, rstack=None):
self.program_ctr += 1 # since the program counter doesn't update until after the yield
gen = self.read_word()
name = gen.next()
word = ' '
conts = []
while word != ';':
word = gen.next()
if word == _RUN: continue
conts.append(word)
conts.pop() # pop off the semicolon
self.compile(name, filter(None,conts))
#print(' >name', name, ' >symbols["%s"]' % name, self.symbols[name.lower()])
def expand_word(self, word):
if word in self.sym_words:
outp = [self.expand_word(w) for w in self.sym_words[word][:]]
return sum(outp, [])
else:
return [word]
def compile(self, name, words):
if hasattr(words, 'split'): words = words.split()
self.sym_words[name] = words[:]
@self.add_symbol(name)
def newfun(stack, rstack=None):
self.save_program_ctr()
self.splice_program(words[:])
self.program_ctr -= 1
newfun.__name__ = '_forth_%s' % name
def prog_print(self, stack, rstack=None):
|
5267dfda |
print('<%d>' % len(self.program), end=' ', file=self.out)
|
ff4e0dfb |
for n in self.program:
|
5267dfda |
print(n, end=' ', file=self.out)
print(file=self.out)
|
ff4e0dfb |
def set_pctr(self, v):
self.program_ctr = v
def begin_until(self, stack, rstack):
self.program_ctr += 1 # since the program counter doesn't update until after the yield
mark = self.program_ctr
word = ' '
loop_count = 0
words = []
while True:
word = self.read_word()
if word == 'begin': loop_count += 1
elif word == 'until':
if loop_count == 0: break
else: loop_count -= 1
words.append(word)
def mark_r(self, stack, rstack):
self.jmpr.append(self.program_ctr-1)
#print('markr', self.jmpr)
def jmp_r(self, stack, rstack):
dst = self.jmpr.pop()
self.set_pctr(dst)
#print('jmpr', dst, self.jmpr)
def jmp_clr(self, stack, rstack):
self.jmpr.pop()
#print('clrjmp', self.jmpr)
def mark(self, stack, rstack):
self.stack.append(self.program_ctr-1)
def jmp(self, stack, rstack):
dst = stack.pop()
self.set_pctr(dst)
|
5267dfda |
def __init__(self, out=sys.stdout):
|
ff4e0dfb |
self.sym_words = SymbolDict()
self.stack = []
self.rstack = []
self.program = []
self.jmpr = []
self.pcs = []
self.mode = 0; # 0 - Normal; 1 - string reading
self.program_ctr = 0
|
5267dfda |
self.out = out
|
ff4e0dfb |
self.add_symbol('words')(self.words)
self.add_symbol('.p')(self.prog_print)
self.add_symbol(':')(self.colon)
self.add_symbol('forget')(self.forget)
self.add_symbol('if')(self.if_)
self.add_symbol('jmp')(self.jmp)
self.add_symbol('rev')(self.rev_pctr)
self.add_symbol('adv')(self.adv_pctr)
self.add_symbol('adv0')(self.adv_pctr0)
self.add_symbol('mark')(self.mark)
self.add_symbol('markr')(self.mark_r)
self.add_symbol('jmpr')(self.jmp_r)
self.add_symbol('clrjmp')(self.jmp_clr)
def __getword(self):
while self.inp[-1] == []: self.inp.pop()
if hasattr(self.inp[-1], 'read'):
self.inp[-1] = read_word(self.inp[-1])
if hasattr(self.inp[-1], 'next'):
result = self.inp[-1].next()
else:
result = self.inp[-1].pop(0)
self.program.append(result)
def read_word(self):
while True:
while self.program_ctr >= len(self.program):
self.__getword()
yield self.program[self.program_ctr]
self.program_ctr += 1
def read(self, inp, silent=False):
self.inp = [inp]
delim = SPACE
|
5267dfda |
if not silent: print(self.program_ctr, end='? ', file=self.out)
|
ff4e0dfb |
for word in self.read_word():
#print('<%s>' % word, end=' ')
if word == _RUN:
if not silent:
|
5267dfda |
print(' ok', file=self.out)
print(self.program_ctr, end='? ', file=self.out)
|
ff4e0dfb |
continue
elif word == '."':
out = []
word = self.__getword()
while word != '"':
out.append(self.__getword)
word = self.__getword()
|
5267dfda |
print(' '.join(out), file=self.out)
|
ff4e0dfb |
continue
elif word == '': continue
self.stack.append(word)
self.lookup()
def lookup(self, sym=None):
if sym is None:
sym = self.stack.pop()
result = None
if sym.lower() in self.symbols:
|
5267dfda |
with redir_stdout(self.out):
result = self.symbols[sym.lower()](self.stack, self.rstack)
|
ff4e0dfb |
else:
result = self.numberp(sym)
if result is None: raise ValueError("Symbol %r not found" % sym)
else:
self.stack.append(result)
return result
|
5267dfda |
|
ff4e0dfb |
def numberp(self, sym):
is_valid = -1 # -1 = undecided, 0 = False, 1 = True
typ = int
dot_acceptable = False
if sym == '': is_valid = 0
elif not (sym.startswith('-') or sym[0].isdigit()): is_valid = 0
if is_valid == 0: return None
else:
for char in sym[1:]:
if char.isdigit(): dot_acceptable = True
elif dot_acceptable and char == '.': typ = float
elif dot_acceptable and char == 'e': typ = float
else:
return None
else:
return typ(sym)
@classmethod
def add_symbol(cls, name):
def _i1(func):
cls.symbols[name.lower()] = func
return func
return _i1
@Reader.add_symbol('J')
def J(stack, rstack):
stack.append(rstack[-3])
@Reader.add_symbol('R@')
def rAt(stack, rstack):
stack.append(rstack[-1])
@Reader.add_symbol('I')
def I(stack, rstack):
stack.append(rstack[-1])
@Reader.add_symbol('R>')
def movP(stack, rstack):
stack.append(rstack.pop())
@Reader.add_symbol('>R')
def movR(stack, rstack):
rstack.append(stack.pop())
@Reader.add_symbol('2over')
def _2over(stack, rstack=None):
d, c, b, a = [stack.pop() for _ in range(4)]
stack.append(a)
stack.append(b)
stack.append(c)
stack.append(d)
stack.append(a)
stack.append(b)
@Reader.add_symbol('2dup')
def _2dup(stack, rstack=None):
b, a = [stack.pop() for _ in range(2)]
stack.append(a)
stack.append(b)
stack.append(a)
stack.append(b)
@Reader.add_symbol('2swap')
def _2swap(stack, rstack=None):
d, c, b, a = [stack.pop() for _ in range(4)]
stack.append(c)
stack.append(d)
stack.append(a)
stack.append(b)
@Reader.add_symbol('.r')
def stack_print(stack, rstack=None):
print('<%d>' % len(rstack), end=' ')
for n in rstack:
print(n, end=' ')
print()
@Reader.add_symbol('.s')
def stack_print(stack, rstack=None):
print('<%d>' % len(stack), end=' ')
for n in stack:
print(n, end=' ')
print()
@Reader.add_symbol('drop')
def drop(stack, rstack=None):
stack.pop()
@Reader.add_symbol('rot')
def rot(stack, rstack=None):
n1, n2, n3 = stack.pop(), stack.pop(), stack.pop()
#a, b, c
stack.extend([n2, n1, n3])
@Reader.add_symbol('dup')
def dup(stack, rstack=None):
a = stack.pop()
stack.append(a)
stack.append(a)
@Reader.add_symbol('over')
def over(stack, rstack=None):
b, a = stack.pop(), stack.pop()
stack.append(a)
stack.append(b)
stack.append(a)
@Reader.add_symbol('swap')
def swap(stack, rstack=None):
b, a = stack.pop(), stack.pop()
stack.append(b)
stack.append(a)
@Reader.add_symbol('%')
def mod(stack, rstack=None):
b, a = stack.pop(), stack.pop()
stack.append(a % b)
@Reader.add_symbol('/')
def div(stack, rstack=None):
b, a = stack.pop(), stack.pop()
stack.append(a / b)
@Reader.add_symbol('*')
def mul(stack, rstack=None):
b, a = stack.pop(), stack.pop()
stack.append(a * b)
@Reader.add_symbol('-')
def min_(stack, rstack=None):
b, a = stack.pop(), stack.pop()
stack.append(a - b)
@Reader.add_symbol('+')
def add(stack, rstack=None):
b, a = stack.pop(), stack.pop()
stack.append(a + b)
@Reader.add_symbol('and')
def and_(stack, rstack=None):
b, a = stack.pop(), stack.pop()
stack.append(a and b)
@Reader.add_symbol('or')
def or_(stack, rstack=None):
b, a = stack.pop(), stack.pop()
stack.append(a or b)
@Reader.add_symbol('.')
def period(stack, rstack=None):
print(stack.pop(), end=' ')
@Reader.add_symbol('emit')
def emit(stack, rstack=None):
print(chr(stack.pop()), end='')
@Reader.add_symbol('=')
def eql(stack, rstack=None):
b, a = stack.pop(), stack.pop()
stack.append(int(a == b))
@Reader.add_symbol('<')
def lt(stack, rstack=None):
b, a = stack.pop(), stack.pop()
stack.append(int(a < b))
@Reader.add_symbol('>')
def gt(stack, rstack=None):
b, a = stack.pop(), stack.pop()
stack.append(int(a > b))
@Reader.add_symbol('nop')
def nop(*_):
pass
@Reader.add_symbol('clear')
def clear(stack, rstack):
del stack[:]
del rstack[:]
|
5267dfda |
def init_reader():
|
ff4e0dfb |
reader = Reader()
reader.compile(*'0= 0 ='.split(None,1))
reader.compile(*'0> 0 >'.split(None,1))
reader.compile(*'0< 0 <'.split(None,1))
reader.compile('cr', ['10', 'emit'])
reader.compile('2drop', ['drop', 'drop'])
reader.compile('false', ['0'])
reader.compile('true', ['-1'])
reader.compile('invert', ['0=', 'if', 'true', 'else', 'false', 'then'])
reader.compile('?dup', 'dup if dup then'.split())
reader.compile('1+', '1 +'.split())
reader.compile('1-', '1 -'.split())
reader.compile('2+', '2 +'.split())
reader.compile('2-', '2 -'.split())
reader.compile('2*', '2 *'.split())
reader.compile('2/', '2 /'.split())
reader.compile('abs', 'dup 0< if -1 * then'.split())
reader.compile('negate', '-1 *'.split())
reader.compile('min', '2dup - 0> if swap then drop')
reader.compile('max', '2dup - 0< if swap then drop')
reader.compile('*/', 'rot rot * swap /')
reader.compile('*/mod', 'rot rot * swap /mod')
reader.compile('/mod1', '2dup % dup 2swap / rot drop')
reader.compile('mark>r', 'mark >r')
reader.compile('arrow', '45 emit 45 emit 62 emit')
reader.compile('loop0', '0= arrow .s if 98 emit cr else 97 emit cr .s r> .s jmp then')
reader.compile(*'spaces 0 do 32 emit loop'.split(None, 1))
reader.compile(*'do swap >r >r begin'.split(None, 1))
reader.compile('rdrop', 'r> drop')
reader.compile(*'loop r> 1+ r> 2dup >r >r < invert until rdrop rdrop'.split(None, 1))
reader.compile('begin', 'markr')
reader.compile('until', 'invert if jmpr else clrjmp then')
|
5267dfda |
return reader
if __name__ == '__main__':
import sys
reader = init_reader()
|
ff4e0dfb |
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('file', nargs='?')
args = parser.parse_args()
if args.file == '-' or args.file == None:
fil = sys.stdin
reader.read(sys.stdin)
else:
with open(args.file) as f:
reader.read(f, silent=True)
|