git @ Cat's Eye Technologies Yolk / master src / yolk.py
master

Tree @master (Download .tar.gz)

yolk.py @masterraw · history · blame

#!/usr/bin/env python


class Sexpr(object):
    def is_atom(self, s):
       return False


class Atom(Sexpr):
    def __init__(self, s):
       assert s in ('ifeq', 'quote', 'arg', 'head', 'tail', 'self', 'cons'), s
       self.s = s

    def __str__(self):
       return self.s

    def is_atom(self, s):
       return self.s == s

    def __eq__(self, other):
       return isinstance(other, Atom) and other.s == self.s


class List(Sexpr):
    def __init__(self, sexprs):
       for s in sexprs:
           assert isinstance(s, Sexpr)
       self.sexprs = sexprs

    def __str__(self):
       return "(%s)" % ' '.join([s.__str__() for s in self.sexprs])

    def head(self):
       return self.sexprs[0]

    def tail(self):
       return List(self.sexprs[1:])

    def __eq__(self, other):
       return isinstance(other, List) and other.sexprs == self.sexprs


class Scanner(object):
    def __init__(self, s):
        self.s = s

    def scan(self):
        while self.s and self.s[0].isspace():
            self.s = self.s[1:]

        if not self.s:
            return None

        if self.s[0] in ('(', ')'):
            token = self.s[0]
            self.s = self.s[1:]
            return token

        if self.s[0].isalpha():
            token = ''
            while self.s and self.s[0].isalpha():
                token += self.s[0]
                self.s = self.s[1:]
            return token

        raise ValueError('illegal character: ' + self.s[0])


class Parser(object):

    def __init__(self, s):
        self.scanner = Scanner(s)

    def sexpr(self):
        token = self.scanner.scan()
        if token == ')':
            return None
        if token == '(':
            l = []
            sub = self.sexpr()
            while sub is not None:
                l.append(sub)
                sub = self.sexpr()
            return List(l)
        else:
            return Atom(token)


def cons(a, b):
    return List([a] + b.sexprs)


def eval_yolk(full, prog, arg):
    if prog.is_atom('arg'):
        return arg
    if prog.head().is_atom('head'):
        return eval_yolk(full, prog.tail().head(), arg).head()
    if prog.head().is_atom('tail'):
        return eval_yolk(full, prog.tail().head(), arg).tail()
    if prog.head().is_atom('cons'):
        return cons(eval_yolk(full, prog.tail().head(), arg), eval_yolk(full, prog.tail().tail().head(), arg))
    if prog.head().is_atom('quote'):
        return prog.tail().head()
    if prog.head().is_atom('ifeq'):
        if eval_yolk(full, prog.tail().head(), arg) == eval_yolk(full, prog.tail().tail().head(), arg):
            return eval_yolk(full, prog.tail().tail().tail().head(), arg)
        else:
            return eval_yolk(full, prog.tail().tail().tail().tail().head(), arg)
    if prog.head().is_atom('self'):
        return eval_yolk(full, full, eval_yolk(full, prog.tail().head(), arg))
    raise ValueError("Cannot evaluate %r" % prog)


def run(ptext, atext):
    p = Parser(ptext).sexpr()
    a = Parser(atext).sexpr()
    r = eval_yolk(p, p, a)
    return r


def main():
    import sys
    with open(sys.argv[1], 'r') as f:
        prog = f.read()
    if len(sys.argv) >= 4 and sys.argv[2] == '-i':
        with open(sys.argv[3], 'r') as f:
            inp = f.read()
    else:
        inp = sys.stdin.read()
    if not inp:
        inp = 'ifeq'
    result = run(prog, inp)
    print(result)


def target(*args):
    import os
    
    def rpython_load(filename):
        fd = os.open(filename, os.O_RDONLY, 0o644)
        text = ''
        chunk = os.read(fd, 1024)
        text += chunk
        while len(chunk) == 1024:
            chunk = os.read(fd, 1024)
            text += chunk
        os.close(fd)
        return text

    def rpython_input():
        accum = ''
        done = False
        while not done:
            s = os.read(1, 1)
            if not s:
                done = True
            accum += s
        return accum

    def rpython_main(argv):
        program = rpython_load(argv[1])
        if len(argv) >= 4 and argv[2] == '-i':
            inp = rpython_load(argv[3])
        else:
            inp = rpython_input()
        if not inp:
            inp = 'ifeq'
        result = run(program, inp)
        print(result.__str__())
        return 0

    return rpython_main, None


if __name__ == '__main__':
    main()