# HG changeset patch # User HackBot # Date 1465186213 0 # Node ID 81911da6b984d0ab6982fa8a09250052792ffc32 # Parent a4ea1b3ea7130f8241e0e1c974e766e26b2581e4 ` mv gs2.py bin/gs2.py diff -r a4ea1b3ea713 -r 81911da6b984 bin/gs2.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bin/gs2.py Mon Jun 06 04:10:13 2016 +0000 @@ -0,0 +1,1224 @@ +# 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('= 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] ' % 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() diff -r a4ea1b3ea713 -r 81911da6b984 gs2.py --- a/gs2.py Mon Jun 06 04:09:34 2016 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1224 +0,0 @@ -# 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('= 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] ' % 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()