Mercurial > repo
view bin/gs2.py @ 10190:ca23294c6863
<oerjan> addquote <Phantom_Hoover> but i said at the start \'i have good reasons for this, i\'m not crazy\'!
author | HackBot |
---|---|
date | Sun, 29 Jan 2017 10:31:51 +0000 |
parents | c989a1669243 |
children |
line wrap: on
line source
# gs2 interpreter (version 0.2) # (c) nooodl 2014 import copy import inspect import itertools as it import math import operator import os import random import re import string import struct import sys import traceback from collections import namedtuple from fractions import gcd Block = namedtuple('Block', 'code') STRING_ENDS = '\x05\x06' + ''.join(map(chr, range(0x9b, 0xa0))) DEBUG = False def log(x): if not DEBUG: return line, name = inspect.stack()[1][2:4] sys.stderr.write('\x1b[36m%s:%d\x1b[34m: %r\x1b[0m\n' % (name, line, x)) def lcm(a, b): if (a, b) == (0, 0): return 0 return abs(a * b) // gcd(a, b) def product(xs): p = 1 for x in xs: p *= x return p def split(a, b, clean=False): res = [[]] lb = len(b) i = 0 while i < len(a): if a[i:i + lb] == b: res.append([]) i += lb else: res[-1].append(a[i]) i += 1 return filter(None, res) if clean else res def join(a, b): res = [] for i, x in enumerate(a): if i > 0: res.extend(b) res.extend(x if is_list(x) else [x]) return res def set_diff(a, b): res = [] for i in a: if i not in b: res.append(i) return res def set_and(a, b): res = [] for i in a: if i in b: res.append(i) return res def set_or(a, b): return a + set_diff(b, a) def set_xor(a, b): return set_diff(a, b) + set_diff(b, a) # prime number functions prime_list = [] sieved = 2 composite = set([1]) def sieve(limit): global prime_list global sieved global composite if limit <= sieved: return prime_list = [] for i in range(2, limit): if i in composite: continue for j in range(i*i, limit, i): composite.add(j) prime_list.append(i) sieved = limit sieve(1000) def is_prime(n): global prime_list sieve(n+1) return n not in composite def nth_prime(n): global prime_list sieve(int(math.log(n) * n) + 100) return prime_list[n-1] def n_primes(n): global prime_list sieve(int(math.log(n) * n) + 100) return prime_list[:n] def primes_below(n): global prime_list sieve(n+1) return list(it.takewhile(lambda x: x < n, prime_list)) def next_prime(n): n += 1 while not is_prime(n): n += 1 return n def totient(n): count = 0 for i in xrange(1, n+1): if gcd(n, i) == 1: count += 1 return count def factor(n, exps=False): if is_num(n): p = 2 res = [] while n > 1: while n % p == 0: res.append(p) n //= p p = next_prime(p) if exps: res = [[k, len(list(g))] for k, g in it.groupby(res)] return res elif is_list(n): if n and is_num(n[0]): n = zip(n[0::2], n[1::2]) p = 1 for b, e in n: p *= b ** e return p else: raise TypeError('factor') def chunks(x, y): # chunks(range(12), 3) ==> [[0, 1, 2], [3, 4, 5], ...] while x: yield x[:y] x = x[y:] def tokenize(prog): # string hack cs = STRING_ENDS if re.match('^[^\x04]*[%s]' % cs, prog): prog = '\x04' + prog mode = None if prog[0] in '\x30\x31\x32': # set mode mode = prog[0] prog = prog[1:] token_re = [ '\x04[^%s]*[%s]' % (cs, cs), # string (array) '\x01.', # unsigned byte '\x02..', # signed short '\x03....', # signed long '\x07.', # 1 char string '.', # regular token ] tokens = re.findall('|'.join(token_re), prog, re.DOTALL) final = [] blocks = [Block([])] i = 0 while i < len(tokens): t = tokens[i] log(tokens[i:]) if t == '\x08': #= { blocks.append(Block([])) final.append('\x00') elif t == '\x09': #= } blocks[-2].code.append(blocks.pop()) blocks[-1].code.append(final.pop()) elif '\xe0' <= t <= '\xff' and ord(t) & 7 < 6: # quick block # 0b111XXYYY -- Y+1 is number of tokens, X is end token: # 0 = nop (0x00) 2 = filter (0x35) # 1 = map (0x34) 3 = both (0x38) # but 0xfe and 0xff are special (see below.) num = (ord(t) & 7) + 1 ts = blocks[-1].code[-num:] del blocks[-1].code[-num:] blocks[-1].code.append(Block(ts)) blocks[-1].code.append('\x00\x34\x35\x38'[(ord(t) >> 3) & 3]) elif t in '\xee\xef': #= z1 zipwith1, z2 zipwith2 # zipwith (1/2 tokens) num = (ord(t) & 1) + 1 ts = blocks[-1].code[-num:] del blocks[-1].code[-num:] blocks[-1].code.append(Block(ts)) blocks[-1].code.append('\xb1') elif t in '\xf6\xf7': #= dm1 dump-map1, df1 dump-filter1 # like m1/f1 with dump prepended to block # useful with transpose, pairwise, cartesian-product, etc. f = {'\xf6': '\x34', '\xf7': '\x35'}[t] x = blocks[-1].code.pop() blocks[-1].code.extend([Block(['\x0e', x]), f]) elif t == '\xfe': #= m: blocks.append(Block([])) final.append('\x34') elif t == '\xff': #= f: blocks.append(Block([])) final.append('\x35') else: blocks[-1].code.append(t) i += 1 while final: blocks[-2].code.append(blocks.pop()) blocks[-1].code.append(final.pop()) assert len(blocks) == 1 main = blocks[0] if mode == '\x30': #= line-mode main = Block(['\x2a', main, '\x34', '\x54']) elif mode == '\x31': #= word-mode main = Block(['\x2c', main, '\x34', '\x55']) elif mode == '\x32': #= line-mode-skip-first main = Block(['\x2a', '\x22', main, '\x34', '\x54']) main.code.extend(final) return main is_num = lambda v: isinstance(v, (int, long)) is_list = lambda v: isinstance(v, list) is_block = lambda v: isinstance(v, Block) def to_gs(ps): return map(ord, ps) def to_ps(gs): if is_list(gs): return ''.join(map(chr, gs)) else: return chr(gs) def regex_count(pattern): c = 0 if pattern[0] == ']': c = 1 pattern = pattern[1:] elif pattern[0] == '}': c = ord(pattern[1]) pattern = pattern[2:] return (c, pattern) def show(value, nest=False): if is_list(value): return ''.join(show(x, nest=True) for x in value) elif nest and is_num(value): return chr(value) else: return str(value) class Stack(list): def __init__(self, *args): list.__init__(self, *args) self.junk = [] def pop(self, i=-1, junk=True): x = list.pop(self, i) if junk: self.junk.append(x) return x class GS2(object): def __init__(self, code, stdin=''): self.code = code self.stdin = to_gs(stdin) self.stack = Stack([self.stdin]) self.regs = { 0: to_gs(stdin), # A 1: len(stdin), # B 2: to_gs(code), # C 3: random.randint(0, 2), # D } self.counter = 1 def run(self): try: self.evaluate(tokenize(self.code)) sys.stdout.write(''.join(map(show, self.stack))) except Exception: # If the code fails, print something meaningful to stderr, # but quine on stdout: this allows GS2 to good at simple # "print this string" programs -- just upload a plaintext # file, it's unlikely to be valid GS2 code. traceback.print_exc() if not DEBUG: sys.stdout.write(self.code) def evaluate(self, block): log(block) for t in block.code: if is_block(t): self.stack.append(t) elif t[0] == '\x00': #= nop pass elif t[0] == '\x01': # push unsigned byte self.stack.append(struct.unpack('<B', t[1:])[0]) elif t[0] == '\x02': # push signed short self.stack.append(struct.unpack('<h', t[1:])[0]) elif t[0] == '\x03': # push signed long self.stack.append(struct.unpack('<l', t[1:])[0]) elif t[0] == '\x04': # string assert len(t) >= 2 assert t[-1] in STRING_ENDS strings = t[1:-1].split('\x07') strings = map(to_gs, strings) if t[-1] == '\x05': # regular self.stack += strings elif t[-1] == '\x06': # array self.stack.append(strings) elif t[-1] == '\x9b': # printf f = to_ps(strings.pop()) n = f.count('%') - f.count('%%') * 2 x = tuple(map(to_ps, self.stack[-n:])) del self.stack[-n:] self.stack.append(to_gs(f % x)) elif t[-1] == '\x9c': # regex match pattern = to_ps(strings.pop()) c, pattern = regex_count(pattern) s = to_ps(self.stack.pop()) f = re.match if c else re.search self.stack.append(1 if f(pattern, s) else 0) elif t[-1] == '\x9d': # regex sub repl = to_ps(strings.pop()) pattern = to_ps(strings.pop()) c, pattern = regex_count(pattern) s = to_ps(self.stack.pop()) m = re.sub(pattern, repl, s, count=c) self.stack.append(to_gs(m)) elif t[-1] == '\x9e': # regex find pattern = to_ps(strings.pop()) c, pattern = regex_count(pattern) s = to_ps(self.stack.pop()) ms = re.findall(pattern, s) if c > 0: ms = ms[0] if ms else [] self.stack.append(map(to_gs, ms)) elif t[-1] == '\x9f': # regex split pattern = to_ps(strings.pop()) c, pattern = regex_count(pattern) s = to_ps(self.stack.pop()) m = re.split(pattern, s, maxsplit=c) self.stack.append(map(to_gs, m)) elif t[0] == '\x07': # single char string self.stack.append([ord(t[1])]) # \x08 and \x09 are block syntax elif t == '\x0a': #= new-line self.stack.append([ord('\n')]) elif t == '\x0b': #= empty-list self.stack.append([]) elif t == '\x0c': #= empty-block self.stack.append(Block([])) elif t == '\x0d': #= space self.stack.append([ord(' ')]) elif t == '\x0e': #= make-array extract-array dump x = self.stack.pop() if is_num(x): self.stack[-x:] = [self.stack[-x:]] elif is_list(x): for i in x: self.stack.append(i) else: raise TypeError('make-array / extract-array') elif t == '\x0f': #= exit break elif 0x10 <= ord(t[0]) <= 0x1a: # push small number self.stack.append(ord(t[0]) - 0x10) elif t == '\x1b': self.stack.append(100) elif t == '\x1c': self.stack.append(1000) elif t == '\x1d': self.stack.append(16) elif t == '\x1e': self.stack.append(64) elif t == '\x1f': self.stack.append(256) elif t == '\x20': #= negate reverse eval x = self.stack.pop() if is_num(x): self.stack.append(-x) elif is_list(x): self.stack.append(x[::-1]) elif is_block(x): self.evaluate(x) else: raise TypeError('negate / reverse') elif t == '\x21': #= bnot head x = self.stack.pop() if is_num(x): self.stack.append(~x) elif is_list(x): self.stack.append(x[0]) else: raise TypeError('bitwise not / head') elif t == '\x22': #= not tail x = self.stack.pop() if is_num(x): self.stack.append(0 if x else 1) elif is_list(x): self.stack.append(x[1:]) else: raise TypeError('not / tail') elif t == '\x23': #= abs init x = self.stack.pop() if is_num(x): self.stack.append(abs(x)) elif is_list(x): self.stack.append(x[:-1]) else: raise TypeError('abs / init') elif t == '\x24': #= digits last x = self.stack.pop() if is_num(x): self.stack.append(map(int, str(abs(x)))) elif is_list(x): self.stack.append(x[-1]) else: raise ValueError('digits / last') elif t == '\x25': #= random x = self.stack.pop() if is_num(x): self.stack.append(random.randrange(x)) elif is_list(x): self.stack.append(random.choice(x)) else: raise TypeError('random') elif t == '\x26': #= dec left-uncons x = self.stack.pop() if is_num(x): self.stack.append(x - 1) elif is_list(x): self.stack.append(x[1:]) self.stack.append(x[0]) else: raise TypeError('deincrement / left uncons') elif t == '\x27': #= inc right-uncons x = self.stack.pop() if is_num(x): self.stack.append(x + 1) elif is_list(x): self.stack.append(x[:-1]) self.stack.append(x[-1]) else: raise TypeError('increment / right uncons') elif t == '\x28': #= sign min x = self.stack.pop() if is_num(x): self.stack.append(cmp(x, 0)) elif is_list(x): self.stack.append(min(x)) else: raise TypeError('sign / min') elif t == '\x29': #= thousand max x = self.stack.pop() if is_num(x): self.stack.append(x * 1000) elif is_list(x): self.stack.append(max(x)) else: raise TypeError('thousand / max') elif t == '\x2a': #= double lines x = self.stack.pop() if is_num(x): self.stack.append(x * 2) elif is_list(x): if x and x[-1] == ord('\n'): x.pop() self.stack.append(split(x, to_gs('\n'))) else: raise TypeError('double / line') elif t == '\x2b': #= half unlines x = self.stack.pop() if is_num(x): self.stack.append(x // 2) elif is_list(x): x = [to_gs(show(i)) for i in x] self.stack.append(join(x, to_gs('\n'))) else: raise TypeError('half / unlines') elif t == '\x2c': #= square words x = self.stack.pop() if is_num(x): self.stack.append(x * x) elif is_list(x): self.stack.append(map(to_gs, to_ps(x).split())) else: raise TypeError('square / words') elif t == '\x2d': #= sqrt unwords x = self.stack.pop() if is_num(x): self.stack.append(int(math.sqrt(x))) elif is_list(x): x = [to_gs(show(i)) for i in x] self.stack.append(join(x, to_gs(' '))) else: raise TypeError('sqrt / unwords') elif t == '\x2e': #= range length x = self.stack.pop() if is_num(x): self.stack.append(range(x)) elif is_list(x): self.stack.append(len(x)) else: raise TypeError('range / length') elif t == '\x2f': #= range1 sort x = self.stack.pop() if is_num(x): self.stack.append(range(1, x + 1)) elif is_list(x): self.stack.append(list(sorted(x))) elif is_block(x): l = self.stack.pop() def f(z): self.stack.append(z) self.evaluate(x) return self.stack.pop(junk=False) self.stack.append(list(sorted(l, key=f))) else: raise TypeError('range1 / sort') elif t == '\x30': #= + add catenate y = self.stack.pop() x = self.stack.pop() if is_num(x) and is_num(y): self.stack.append(x + y) elif is_list(x) and is_list(y): self.stack.append(x + y) elif is_block(x) and is_block(y): self.stack.append(Block(x.code + y.code)) elif is_list(x) and not is_list(y): self.stack.append(x + [y]) elif not is_list(x) and is_list(y): self.stack.append([x] + y) else: raise TypeError('add / catenate') elif t == '\x31': #= - sub diff y = self.stack.pop() x = self.stack.pop() if is_num(x) and is_num(y): self.stack.append(x - y) elif is_list(x) and is_list(y): self.stack.append(set_diff(x, y)) elif is_list(x) and not is_list(y): self.stack.append(set_diff(x, [y])) elif not is_list(x) and is_list(y): self.stack.append(set_diff(y, [x])) else: raise TypeError('subtract / set diff') elif t == '\x32': #= * mul join times fold y = self.stack.pop() x = self.stack.pop() if is_num(x) and (is_block(y) or is_list(y)): x, y = y, x if is_block(x) and is_list(y): x, y = y, x if is_num(x) and is_num(y): self.stack.append(x * y) elif is_list(x) and is_list(y): self.stack.append(join(x, y)) elif is_list(x) and is_num(y): self.stack.append(x * y) elif is_block(x) and is_num(y): for i in xrange(y): self.evaluate(x) elif is_list(x) and is_block(y): self.stack.append(x[0]) for i in x[1:]: self.stack.append(i) self.evaluate(y) else: raise TypeError('multiply / join / times / fold') elif t == '\x33': #= / div chunks split each y = self.stack.pop() x = self.stack.pop() if not is_list(x) and is_list(y): x, y = y, x if is_num(x) and is_num(y): self.stack.append(x // y) elif is_list(x) and is_num(y): self.stack.append(list(chunks(x, y))) elif is_list(x) and is_list(y): self.stack.append(split(x, y)) elif is_list(x) and is_block(y): for i in x: self.stack.append(i) self.evaluate(y) else: raise TypeError('divide / chunks / split / each') elif t == '\x34': #= % mod step clean-split map y = self.stack.pop() x = self.stack.pop() if not is_list(x) and is_list(y): x, y = y, x if is_num(x) and is_num(y): self.stack.append(x % y) elif is_list(x) and is_num(y): self.stack.append(x[::y]) elif is_list(x) and is_list(y): self.stack.append(split(x, y, clean=True)) elif is_list(x) and is_block(y): self.eval_map(y, x) else: raise TypeError('modulo / step / split\' / map') elif t == '\x35': #= & and get when filter y = self.stack.pop() x = self.stack.pop() if is_block(x) and is_num(y): x, y = y, x if is_num(x) and is_list(y): x, y = y, x if is_block(x) and is_list(y): x, y = y, x if is_num(x) and is_num(y): self.stack.append(x & y) elif is_list(x) and is_list(y): self.stack.append(set_and(x, y)) elif is_list(x) and is_num(y): self.stack.append(x[y]) elif is_num(x) and is_block(y): if x: self.evaluate(y) elif is_list(x) and is_block(y): self.eval_filter(y, x) else: raise TypeError('and / get / when / filter') elif t == '\x36': #= | or unless y = self.stack.pop() x = self.stack.pop() if is_block(x) and is_num(y): x, y = y, x if is_num(x) and is_num(y): self.stack.append(x | y) elif is_list(x) and is_list(y): self.stack.append(set_or(x, y)) elif is_num(x) and is_block(y): if not x: self.evaluate(y) else: raise TypeError('bor / unless') elif t == '\x37': #= ^ xor concatmap y = self.stack.pop() x = self.stack.pop() if is_block(x) and is_list(y): x, y = y, x if is_num(x) and is_num(y): self.stack.append(x ^ y) elif is_list(x) and is_list(y): self.stack.append(set_xor(x, y)) elif is_list(x) and is_block(y): res = [] for i in x: self.stack.append(i) self.evaluate(y) res.extend(self.stack.pop(junk=False)) self.stack.append(res) else: raise TypeError('xor / concatmap') elif t == '\x38': #= smallest both y = self.stack.pop() if is_block(y): x = self.stack.pop() self.evaluate(y) self.stack.append(x) self.evaluate(y) else: x = self.stack.pop() self.stack.append(min(x, y)) elif t == '\x39': #= biggest y = self.stack.pop() x = self.stack.pop() self.stack.append(max(x, y)) elif t == '\x3a': #= clamp z = self.stack.pop() y = self.stack.pop() x = self.stack.pop() self.stack.append(min(max(x, y), z)) elif t == '\x3c': #= gcd take y = self.stack.pop() x = self.stack.pop() if is_num(x) and is_list(y): x, y = y, x if is_num(x) and is_num(y): self.stack.append(gcd(x, y)) elif is_list(x) and is_num(y): self.stack.append(x[:y]) else: raise TypeError('gcd / take') elif t == '\x3d': #= lcm drop y = self.stack.pop() x = self.stack.pop() if is_num(x) and is_list(y): x, y = y, x if is_num(x) and is_num(y): self.stack.append(lcm(x, y)) elif is_list(x) and is_num(y): self.stack.append(x[y:]) else: raise TypeError('lcm / drop') elif t == '\x3e': #= pow index y = self.stack.pop() x = self.stack.pop() if is_num(x) and is_list(y): x, y = y, x if is_num(x) and is_num(y): self.stack.append(x ** y) elif is_list(x) and is_num(y): self.stack.append(x.index(y) if y in x else -1) else: raise TypeError('power / index') elif t == '\x3f': #= log member y = self.stack.pop() x = self.stack.pop() if is_list(y): x, y = y, x if is_num(x) and is_num(y): self.stack.append(int(math.log(x, y))) elif is_list(x): self.stack.append(1 if y in x else 0) else: raise TypeError('log / member') elif t == '\x40': #= dup self.stack.append(self.stack[-1]) elif t == '\x41': #= dup2 self.stack.append(self.stack[-1]) self.stack.append(self.stack[-1]) elif t == '\x42': #= swap self.stack.append(self.stack.pop(-2)) elif t == '\x43': #= rot self.stack.append(self.stack.pop(-3)) elif t == '\x44': #= rrot self.stack.append(self.stack.pop(-3)) self.stack.append(self.stack.pop(-3)) elif t == '\x45': #= over self.stack.append(self.stack[-2]) elif t == '\x46': #= nip self.stack.pop(-2) elif t == '\x47': #= tuck self.stack.insert(-2, self.stack[-1]) elif t == '\x48': #= 2dup self.stack.append(self.stack[-2]) self.stack.append(self.stack[-2]) elif t == '\x49': #= pick n = self.stack.pop() self.stack.append(self.stack[-n]) elif t == '\x4a': #= roll n = self.stack.pop() self.stack.append(self.stack.pop(-n)) elif t == '\x4b': #= wrap-stack self.stack = [copy.deepcopy(self.stack)] elif t == '\x4c': #= leave-top del self.stack[:-1] elif t == '\x4d': #= itemize self.stack.append([self.stack.pop()]) elif t == '\x4e': #= rrange x = self.stack.pop() self.stack.append(range(x)[::-1]) elif t == '\x4f': #= crange y = self.stack.pop() x = self.stack.pop() if x > y: x, y = y, x self.stack.append(range(x, y)) elif t == '\x50': #= pop self.stack.pop() elif t == '\x51': #= pop2 self.stack.pop() self.stack.pop() elif t == '\x52': #= show x = self.stack.pop() self.stack.append(to_gs(show(x))) elif t == '\x53': #= map-show x = self.stack.pop() self.stack.append(map(to_gs, map(show, x))) elif t == '\x54': #= show-lines x = self.stack.pop() self.stack.append(to_gs('\n'.join(map(show, x)))) elif t == '\x55': #= show-words x = self.stack.pop() self.stack.append(to_gs(' '.join(map(show, x)))) elif t in '\x56\x57': #= read-num, read-nums x = to_ps(self.stack.pop()) nums = map(int, re.findall(r'-?\d+', x)) self.stack.append(nums[0] if t == '\x56' else nums) elif t == '\x58': #= show-line x = self.stack.pop() self.stack.append(to_gs(show(x) + '\n')) elif t == '\x59': #= show-space x = self.stack.pop() self.stack.append(to_gs(show(x) + ' ')) elif t == '\x5a': #= show-comma x = self.stack.pop() self.stack.append(to_gs(', '.join(map(show, x)))) elif t == '\x5b': #= show-python x = self.stack.pop() self.stack.append(to_gs(', '.join(map(show, x)).join('[]'))) elif t in '\x5c\x5d\x5e': #= ljust, center, rjust fill = ' ' if is_num(self.stack[-2]): fill = chr(self.stack.pop()) width = self.stack.pop() s = self.stack.pop() if t == '\x5c': g = show(s).ljust(width, fill) if t == '\x5d': g = show(s).center(width, fill) if t == '\x5e': g = show(s).rjust(width, fill) self.stack.append(to_gs(g)) elif t == '\x5f': #= inspect self.stack.append(to_gs(repr(self.stack.pop()))) elif t == '\x60': #= logical-and y = self.stack.pop() x = self.stack.pop() self.stack.append(x and y) elif t == '\x61': #= logical-or y = self.stack.pop() x = self.stack.pop() self.stack.append(x or y) elif t == '\x62': #= divides left-cons y = self.stack.pop() x = self.stack.pop() if is_num(x): self.stack.append(0 if x % y else 1) elif is_list(x): self.stack.append([y] + x) else: raise TypeError('divides / left-cons') elif t == '\x63': #= divmod group y = self.stack.pop() if is_num(y): x = self.stack.pop() self.stack.append(x // y) self.stack.append(x % y) elif is_list(y): gb = [list(g) for k, g in it.groupby(y)] self.stack.append(list(gb)) else: raise TypeError('divmod / group') elif t == '\x64': #= sum even x = self.stack.pop() if is_num(x): self.stack.append(1 if x % 2 == 0 else 0) elif is_list(x): self.stack.append(sum(x)) elif t == '\x65': #= product odd x = self.stack.pop() if is_num(x): self.stack.append(1 if x % 2 == 1 else 0) elif is_list(x): self.stack.append(product(x)) elif t == '\x66': #= fizzbuzz fizzbuzz = [] for i in range(1, 101): s = ("Fizz" if i % 3 == 0 else "") + \ ("Buzz" if i % 5 == 0 else "") fizzbuzz.append(s or str(i)) self.stack.append(to_gs('\n'.join(fizzbuzz))) elif t == '\x67': #= popcnt right-cons x = self.stack.pop() if is_num(x): x = abs(x) p = 0 while x: p += (x & 1) x >>= 1 self.stack.append(p) elif is_list(x): y = self.stack.pop() self.stack.append(x + [y]) elif t == '\x68': #= hello x = 0 if len(self.stack) >= 1 and is_num(self.stack[-1]): x = self.stack.pop() x = (range(0, 11) + [100, 1000, 16, 64, 256]).index(x) s1 = 'h' if x & 1 else 'H' s2 = 'W' if x & 2 else 'w' s3 = ['!', '', '.', '...'][((x & 4) >> 2) | ((x & 16) >> 3)] s4 = '' if x & 8 else ',' f = '%sello%s %sorld%s' % (s1, s4, s2, s3) self.stack.append(to_gs(f)) elif t in '\x69\x6a': #= base, binary b = 2 if t == '\x6a' else self.stack.pop() x = self.stack.pop() if is_num(x): x = abs(x) res = [] while x: res.append(x % b) x //= b self.stack.append(res[::-1]) elif is_list(x): res = 0 for i in x: res = res * b + i self.stack.append(res) else: raise TypeError('base / binary') elif t == '\x6b': #= is-prime x = self.stack.pop() if is_num(x): self.stack.append(1 if is_prime(x) else 0) elif is_list(x): self.stack.append(filter(is_prime, x)) else: raise TypeError('is-prime') elif t == '\x6c': #= primes op = self.stack.pop() x = self.stack.pop() if op == 0: self.stack.append(n_primes(x)) elif op == 1: self.stack.append(primes_below(x)) elif op == 2: self.stack.append(next_prime(x)) elif op == 3: self.stack.append(totient(x)) elif op == 4: self.stack.append(factor(x, exps=False)) elif op == 5: self.stack.append(factor(x, exps=True)) elif t == '\x6d': #= scan f = self.stack.pop() def call_f(x, y): self.stack.append(x) self.stack.append(y) self.evaluate(f) return self.stack.pop() xs = self.stack.pop() res = [xs.pop(0)] while xs: res.append(call_f(res[-1], xs.pop(0))) self.stack.append(res) elif t in '\x70\x71\x72\x73\x74\x75': #= lt <, eq =, gt >, ge >=, ne !=, le <= y = self.stack.pop() x = self.stack.pop() ops = { '\x70': operator.lt, '\x71': operator.eq, '\x72': operator.gt, '\x73': operator.ge, '\x74': operator.ne, '\x75': operator.le, } self.stack.append(1 if ops[t](x, y) else 0) elif t == '\x76': #= cmp y = self.stack.pop() x = self.stack.pop() self.stack.append(cmp(x, y)) elif t == '\x77': #= is-sorted x = self.stack.pop() if is_list(x): self.stack.append(1 if x == list(sorted(x)) else 0) elif is_block(x): l = self.stack.pop() def f(z): self.stack.append(z) self.evaluate(x) return self.stack.pop() sorted_l = list(sorted(l, key=f)) self.stack.append(1 if l == sorted_l else 0) else: raise TypeError('sorted') elif t == '\x78': #= shift-left inits y = self.stack.pop() if is_list(y): inits = [] for i in xrange(len(y) + 1): inits.append(y[:i]) self.stack.append(inits) else: x = self.stack.pop() self.stack.append(x << y) elif t == '\x79': #= shift-right tails y = self.stack.pop() if is_list(y): tails = [] for i in xrange(len(y) + 1): tails.append(y[len(y)-i:]) self.stack.append(tails) else: x = self.stack.pop() self.stack.append(x >> y) elif t == '\x7a': #= digit-left enumerate y = self.stack.pop() if is_list(y): self.stack.append(list(map(list, enumerate(y)))) else: x = self.stack.pop() self.stack.append(x * (10 ** y)) elif t == '\x7b': #= digit-right y = self.stack.pop() x = self.stack.pop() self.stack.append(x // (10 ** y)) elif t == '\x7c': #= power-of-2 self.stack.append(2 ** self.stack.pop()) elif t == '\x7d': #= power-of-10 self.stack.append(10 ** self.stack.pop()) elif t == '\x7e': #= sub-power-of-2 self.stack.append(2 ** self.stack.pop() - 1) elif t == '\x7f': #= sub-power-of-10 self.stack.append(10 ** self.stack.pop() - 1) elif t == '\x80': #= pair y = self.stack.pop() x = self.stack.pop() self.stack.append([x, y]) elif t == '\x81': #= copies n = self.stack.pop() x = self.stack.pop() self.stack.append([x for _ in xrange(n)]) elif t == '\x82': #= take-end y = self.stack.pop() x = self.stack.pop() if is_num(x) and is_list(y): x, y = y, x self.stack.append(x[-y:]) elif t == '\x83': #= cartesian-product y = self.stack.pop() x = self.stack.pop() p = it.product(x, y) self.stack.append(list(map(list, p))) elif t == '\x84': #= uppercase-alphabet self.stack.append(range(ord('A'), ord('Z') + 1)) elif t == '\x85': #= lowercase-alphabet self.stack.append(range(ord('a'), ord('z') + 1)) elif t == '\x86': #= ascii-digits self.stack.append(range(ord('0'), ord('9') + 1)) elif t == '\x87': #= printable-ascii self.stack.append(range(32, 127)) elif t in '\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f': #= is-alnum, is-alpha, is-digit, is-lower, is-space, is-upper, is-printable, is-hexdigit m = [str.isalnum, str.isalpha, str.isdigit, str.islower, str.isspace, str.isupper, lambda x: all(32 <= ord(c) <= 126 for c in x), lambda x: x in '0123456789abcdefABCDEF'] p = m[ord(t) - 0x88] x = to_ps(self.stack.pop()) self.stack.append(1 if p(x) else 0) elif t == '\x90': #= uniq nub xs = self.stack.pop() uniq = [] for x in xs: if x not in uniq: uniq.append(x) self.stack.append(uniq) elif t == '\x91': #= compress ns = self.stack.pop() xs = self.stack.pop() new = [] for n, x in zip(ns, xs): new += [x for _ in xrange(n)] self.stack.append(new) elif t == '\x92': #= select xs = self.stack.pop() iis = self.stack.pop() new = [] for i in iis: new.append(xs[i]) self.stack.append(new) elif t == '\x93': #= permutations xs = self.stack.pop() if is_num(xs): n = xs xs = self.stack.pop() else: n = None ps = list(map(list, it.permutations(xs, n))) self.stack.append(ps) elif t == '\x94': #= fold-product xss = self.stack.pop() ys = list(map(list, it.product(*xss))) self.stack.append(ys) elif t == '\x95': #= repeat-product n = self.stack.pop() xs = self.stack.pop() ys = list(map(list, it.product(xs, repeat=n))) self.stack.append(ys) elif t == '\x96': #= combinations n = self.stack.pop() xs = self.stack.pop() ys = list(map(list, it.combinations(xs, n))) self.stack.append(ys) elif t == '\x97': #= combinations-with-replacement n = self.stack.pop() xs = self.stack.pop() ys = list(map(list, it.combinations_with_replacement(xs, n))) self.stack.append(ys) elif t == '\x98': #= pairwise xs = self.stack.pop() ys = map(list, zip(xs, xs[1:])) self.stack.append(ys) elif t == '\x99': #= flatten def flatten(xs): acc = [] for x in xs: if is_list(x): acc += flatten(x) else: acc.append(x) return acc xs = self.stack.pop() self.stack.append(flatten(xs)) elif t == '\x9a': #= transpose xs = self.stack.pop() self.stack.append(map(list, zip(*xs))) elif '\xa0' <= t <= '\xaf': # junk (recently popped items) self.stack.append(self.stack.junk[-1 - (ord(t) & 15)]) elif t == '\xb0': #= zip ys = self.stack.pop() xs = self.stack.pop() self.stack.append(map(list, zip(xs, ys))) elif t == '\xb1': #= zipwith f = self.stack.pop() ys = self.stack.pop() xs = self.stack.pop() l0 = len(self.stack) for x, y in zip(xs, ys): self.stack.append(x) self.stack.append(y) self.evaluate(f) self.stack[l0:] = [self.stack[l0:]] elif t == '\xb2': #= counter self.stack.append(self.counter) self.counter += 1 elif '\xc8' <= t <= '\xcb': # save self.regs[ord(t) & 3] = self.stack[-1] elif '\xcc' <= t <= '\xcf': # put self.regs[ord(t) & 3] = self.stack.pop() elif '\xd0' <= t <= '\xd3': # get self.stack.append(self.regs[ord(t) & 3]) elif '\xd4' <= t <= '\xd7': # nip self.regs[ord(t) & 3] = self.stack.pop(-2) elif '\xd8' <= t <= '\xdb': # tuck self.stack.insert(-1, self.regs[ord(t) & 3]) elif '\xdc' <= t <= '\xdf': # show self.stack.append(to_gs(show(self.regs[ord(t) & 3]))) else: raise ValueError('invalid token %r' % t) def eval_map(self, f, x): l0 = len(self.stack) for i in x: self.stack.append(i) self.evaluate(f) self.stack[l0:] = [self.stack[l0:]] def eval_filter(self, f, x): l0 = len(self.stack) for i in x: self.stack.append(i) self.evaluate(f) if self.stack.pop(): self.stack.append(i) self.stack[l0:] = [self.stack[l0:]] if __name__ == '__main__': if len(sys.argv) <= 1: print >> sys.stderr, 'usage: python %s [-d] <code file>' % sys.argv[0] sys.exit(1) if sys.argv[1] == '-d': DEBUG = True sys.argv.pop(1) code = open(sys.argv[1], 'rb').read() stdin = '' if sys.stdin.isatty() else sys.stdin.read() GS2(code, stdin).run()