Omnis Clocha Clochabilis in Clocherio Clochando Clochans Clochativo Clochare facit Clochabiliter Clochantes/refimpl

From Esolang
Jump to navigation Jump to search
Back to Omnis Clocha Clochabilis in Clocherio Clochando Clochans Clochativo Clochare facit Clochabiliter Clochantes
Note: there may be errors in the reference implementation. It's still young.
#!/bin/python3                                                                  

import sys, re, string, os.path as op

_regex = re.compile('[' + re.escape(string.punctuation) + ']')

bf2oc9 = {
    '>': 'clochans',
    '<': 'clochare',
    '+': 'clochando',
    '-': 'clocherio',
    ',': 'clochantes',
    '.': 'clochativo',
    '[': 'clochabilis',
    ']': 'clochabiliter',
}
oc92bf = {v: n for n, v in bf2oc9.items()}

class ClochaClochareClochansError(Exception): pass

usage_msg = '''Usage: {}  PROG1  [ PROG2, ... ]                                 
Executes one or more programs written in Omnis Clocha Clochabilis in Clocherio  
Clochando Clochans Clochativo Clochare facit Clochabiliter Clochantes.          
'''



def remove_punctuation(s):
    return _regex.sub(' ', s).lower()


def brainfuck_to_OC9(bf):
    'You have to add the `clocha` yourself.'
    return ' '.join(bf2oc9[c] for c in bf if c in bf2oc9)


def OC9_to_brainfuck(oc9):
    oc9 = remove_punctuation(oc9).split()
    since_last_clocha = 0
    ret = []
    for s in oc9:
        if not s: continue
        since_last_clocha += 1
        if s == 'clocha':
            since_last_clocha = 0
        elif s in oc92bf:
            ret.append(oc92bf[s])
        if since_last_clocha >= 10:
            raise ClochaClochareClochansError('Needs more clocha!')
    return ''.join(ret)


def _exec_bf(code, tape = None):
    if tape is None: tape = {}
    call_stack = []
    head = 0
    i = 0
    while i < len(code):
        c = code[i]
        i += 1
        if c == '>':
            head += 1
        elif c == '<':
            head -= 1
        elif c == '+':
            tape[head] = tape.get(head, 0) + 1
        elif c == '-':
            tape[head] = tape.get(head, 0) - 1
        elif c == ',':
            tape[head] = ord(sys.stdin.read(1))
        elif c == '.':
            sys.stdout.write(chr(tape.get(head, 0)))
        elif c == '[':
            call_stack.append(i)
        elif c == ']':
            if tape.get(head, 0): i = call_stack[-1]
            else: call_stack.pop()


def exec_file(path):
    with open(path, 'r') as f:
        code = f.read()
    exec_code(code)


def exec_code(code):
    try:
        bf = OC9_to_brainfuck(code)
    except ClochaClochareClochansError as e:
        sys.stderr.write(e + '\n')
    _exec_bf(bf)


def usage(prog_name):
    sys.stdout.write(usage_msg.format(prog_name))


def main():
    if len(sys.argv) <= 1 or '--help' in sys.argv:
        usage(sys.argv[0])
        return
    for arg in sys.argv[1:]:
        if op.exists(arg):
            exec_file(arg)
        else:
            sys.stderr.write('File \'{}\' does not exist\n'.format(arg))


if __name__ == '__main__': main()