Where can these be posted besides the Python Cookbook?

Posted by Noctis Skytower on Stack Overflow See other posts from Stack Overflow or by Noctis Skytower
Published on 2010-03-14T22:32:22Z Indexed on 2010/03/14 22:35 UTC
Read the original article Hit count: 483

Filed under:
|
|

Whitespace Assembler

#! /usr/bin/env python
"""Assembler.py

Compiles a program from "Assembly" folder into "Program" folder.
Can be executed directly by double-click or on the command line.
Give name of *.WSA file without extension (example: stack_calc)."""

################################################################################

__author__ = 'Stephen "Zero" Chappell <[email protected]>'
__date__ = '14 March 2010'
__version__ = '$Revision: 3 $'

################################################################################

import string
from Interpreter import INS, MNEMONIC

################################################################################

def parse(code):
    program = []
    process_virtual(program, code)
    process_control(program)
    return tuple(program)

def process_virtual(program, code):
    for line, text in enumerate(code.split('\n')):
        if not text or text[0] == '#':
            continue
        if text.startswith('part '):
            parse_part(program, line, text[5:])
        elif text.startswith('     '):
            parse_code(program, line, text[5:])
        else:
            syntax_error(line)

def syntax_error(line):
    raise SyntaxError('Line ' + str(line + 1))

################################################################################

def process_control(program):
    parts = get_parts(program)
    names = dict(pair for pair in zip(parts, generate_index()))
    correct_control(program, names)

def get_parts(program):
    parts = []
    for ins in program:
        if isinstance(ins, tuple):
            ins, arg = ins
            if ins == INS.PART:
                if arg in parts:
                    raise NameError('Part definition was found twice: ' + arg)
                parts.append(arg)
    return parts

def generate_index():
    index = 1
    while True:
        yield index
        index *= -1
        if index > 0:
            index += 1

def correct_control(program, names):
    for index, ins in enumerate(program):
        if isinstance(ins, tuple):
            ins, arg = ins
            if ins in HAS_LABEL:
                if arg not in names:
                    raise NameError('Part definition was never found: ' + arg)
                program[index] = (ins, names[arg])

################################################################################

def parse_part(program, line, text):
    if not valid_label(text):
        syntax_error(line)
    program.append((INS.PART, text))

def valid_label(text):
    if not between_quotes(text):
        return False
    label = text[1:-1]
    if not valid_name(label):
        return False
    return True

def between_quotes(text):
    if len(text) < 3:
        return False
    if text.count('"') != 2:
        return False
    if text[0] != '"' or text[-1] != '"':
        return False
    return True

def valid_name(label):
    valid_characters = string.ascii_letters + string.digits + '_'
    valid_set = frozenset(valid_characters)
    label_set = frozenset(label)
    if len(label_set - valid_set) != 0:
        return False
    return True

################################################################################

from Interpreter import HAS_LABEL, Program

NO_ARGS = Program.NO_ARGS
HAS_ARG = Program.HAS_ARG
TWO_WAY = tuple(set(NO_ARGS) & set(HAS_ARG))

################################################################################

def parse_code(program, line, text):
    for ins, word in enumerate(MNEMONIC):
        if text.startswith(word):
            check_code(program, line, text[len(word):], ins)
            break
    else:
        syntax_error(line)

def check_code(program, line, text, ins):
    if ins in TWO_WAY:
        if text:
            number = parse_number(line, text)
            program.append((ins, number))
        else:
            program.append(ins)
    elif ins in HAS_LABEL:
        text = parse_label(line, text)
        program.append((ins, text))
    elif ins in HAS_ARG:
        number = parse_number(line, text)
        program.append((ins, number))
    elif ins in NO_ARGS:
        if text:
            syntax_error(line)
        program.append(ins)
    else:
        syntax_error(line)


def parse_label(line, text):
    if not text or text[0] != ' ':
        syntax_error(line)
    text = text[1:]
    if not valid_label(text):
        syntax_error(line)
    return text

################################################################################

def parse_number(line, text):
    if not valid_number(text):
        syntax_error(line)
    return int(text)

def valid_number(text):
    if len(text) < 2:
        return False
    if text[0] != ' ':
        return False
    text = text[1:]
    if '+' in text and '-' in text:
        return False
    if '+' in text:
        if text.count('+') != 1:
            return False
        if text[0] != '+':
            return False
        text = text[1:]
        if not text:
            return False
    if '-' in text:
        if text.count('-') != 1:
            return False
        if text[0] != '-':
            return False
        text = text[1:]
        if not text:
            return False
    valid_set = frozenset(string.digits)
    value_set = frozenset(text)
    if len(value_set - valid_set) != 0:
        return False
    return True

################################################################################
################################################################################

from Interpreter import partition_number

VMC_2_TRI = {
    (INS.PUSH, True):  (0, 0),
    (INS.COPY, False): (0, 2, 0),
    (INS.COPY, True):  (0, 1, 0),
    (INS.SWAP, False): (0, 2, 1),
    (INS.AWAY, False): (0, 2, 2),
    (INS.AWAY, True):  (0, 1, 2),
    (INS.ADD, False):  (1, 0, 0, 0),
    (INS.SUB, False):  (1, 0, 0, 1),
    (INS.MUL, False):  (1, 0, 0, 2),
    (INS.DIV, False):  (1, 0, 1, 0),
    (INS.MOD, False):  (1, 0, 1, 1),
    (INS.SET, False):  (1, 1, 0),
    (INS.GET, False):  (1, 1, 1),
    (INS.PART, True):  (2, 0, 0),
    (INS.CALL, True):  (2, 0, 1),
    (INS.GOTO, True):  (2, 0, 2),
    (INS.ZERO, True):  (2, 1, 0),
    (INS.LESS, True):  (2, 1, 1),
    (INS.BACK, False): (2, 1, 2),
    (INS.EXIT, False): (2, 2, 2),
    (INS.OCHR, False): (1, 2, 0, 0),
    (INS.OINT, False): (1, 2, 0, 1),
    (INS.ICHR, False): (1, 2, 1, 0),
    (INS.IINT, False): (1, 2, 1, 1)
    }

################################################################################

def to_trinary(program):
    trinary_code = []
    for ins in program:
        if isinstance(ins, tuple):
            ins, arg = ins
            trinary_code.extend(VMC_2_TRI[(ins, True)])
            trinary_code.extend(from_number(arg))
        else:
            trinary_code.extend(VMC_2_TRI[(ins, False)])
    return tuple(trinary_code)

def from_number(arg):
    code = [int(arg < 0)]
    if arg:
        for bit in reversed(list(partition_number(abs(arg), 2))):
            code.append(bit)
        return code + [2]
    return code + [0, 2]

to_ws = lambda trinary: ''.join(' \t\n'[index] for index in trinary)

def compile_wsa(source):
    program = parse(source)
    trinary = to_trinary(program)
    ws_code = to_ws(trinary)
    return ws_code

################################################################################
################################################################################

import os
import sys
import time
import traceback

def main():
    name, source, command_line, error = get_source()
    if not error:
        start = time.clock()
        try:
            ws_code = compile_wsa(source)
        except:
            print('ERROR: File could not be compiled.\n')
            traceback.print_exc()
            error = True
        else:
            path = os.path.join('Programs', name + '.ws')
            try:
                open(path, 'w').write(ws_code)
            except IOError as err:
                print(err)
                error = True
            else:
                div, mod = divmod((time.clock() - start) * 1000, 1)
                args = int(div), '{:.3}'.format(mod)[1:]
                print('DONE: Comipled in {}{} ms'.format(*args))
    handle_close(error, command_line)

def get_source():
    if len(sys.argv) > 1:
        command_line = True
        name = sys.argv[1]
    else:
        command_line = False
        try:
            name = input('Source File: ')
        except:
            return None, None, False, True
    print()
    path = os.path.join('Assembly', name + '.wsa')
    try:
        return name, open(path).read(), command_line, False
    except IOError as err:
        print(err)
        return None, None, command_line, True

def handle_close(error, command_line):
    if error:
        usage = 'Usage: {} <assembly>'.format(os.path.basename(sys.argv[0]))
        print('\n{}\n{}'.format('-' * len(usage), usage))
    if not command_line:
        time.sleep(10)

################################################################################

if __name__ == '__main__':
    main()

Whitespace Helpers

#! /usr/bin/env python
"""Helpers.py

Includes a function to encode Python strings into my WSA format.
Has a "PRINT_LINE" function that can be copied to a WSA program.
Contains a "PRINT" function and documentation as an explanation."""

################################################################################

__author__ = 'Stephen "Zero" Chappell <[email protected]>'
__date__ = '14 March 2010'
__version__ = '$Revision: 1 $'

################################################################################

def encode_string(string, addr):
    print('     push', addr)
    print('     push', len(string))
    print('     set')
    addr += 1
    for offset, character in enumerate(string):
        print('     push', addr + offset)
        print('     push', ord(character))
        print('     set')

################################################################################

# Prints a string with newline.
# push addr
# call "PRINT_LINE"

"""
part "PRINT_LINE"
     call "PRINT"
     push 10
     ochr
     back
"""

################################################################################

# def print(array):
#     if len(array) <= 0:
#         return
#     offset = 1
#     while len(array) - offset >= 0:
#          ptr = array.ptr + offset
#          putch(array[ptr])
#          offset += 1

"""
part "PRINT"
# Line 1-2
     copy
     get
     less "__PRINT_RET_1"
     copy
     get
     zero "__PRINT_RET_1"
# Line 3
     push 1
# Line 4
part "__PRINT_LOOP"
     copy
     copy 2
     get
     swap
     sub
     less "__PRINT_RET_2"
# Line 5
     copy 1
     copy 1
     add
# Line 6
     get
     ochr
# Line 7
     push 1
     add
     goto "__PRINT_LOOP"
part "__PRINT_RET_2"
     away
part "__PRINT_RET_1"
     away
     back
"""

Whitespace Interpreter

#! /usr/bin/env python
"""Interpreter.py

Runs programs in "Programs" and creates *.WSO files when needed.
Can be executed directly by double-click or on the command line.
If run on command line, add "ASM" flag to dump program assembly."""

################################################################################

__author__ = 'Stephen "Zero" Chappell <[email protected]>'
__date__ = '14 March 2010'
__version__ = '$Revision: 4 $'

################################################################################

def test_file(path):
    disassemble(parse(trinary(load(path))), True)

################################################################################

load = lambda ws: ''.join(c for r in open(ws) for c in r if c in ' \t\n')

trinary = lambda ws: tuple(' \t\n'.index(c) for c in ws)

################################################################################

def enum(names):
    names = names.replace(',', ' ').split()
    space = dict((reversed(pair) for pair in enumerate(names)), __slots__=())
    return type('enum', (object,), space)()

INS = enum('''\
PUSH, COPY, SWAP, AWAY, \
ADD, SUB, MUL, DIV, MOD, \
SET, GET, \
PART, CALL, GOTO, ZERO, LESS, BACK, EXIT, \
OCHR, OINT, ICHR, IINT''')

################################################################################

def parse(code):
    ins = iter(code).__next__
    program = []
    while True:
        try:
            imp = ins()
        except StopIteration:
            return tuple(program)
        if imp == 0:
            # [Space]
            parse_stack(ins, program)
        elif imp == 1:
            # [Tab]
            imp = ins()
            if imp == 0:
                # [Tab][Space]
                parse_math(ins, program)
            elif imp == 1:
                # [Tab][Tab]
                parse_heap(ins, program)
            else:
                # [Tab][Line]
                parse_io(ins, program)
        else:
            # [Line]
            parse_flow(ins, program)

def parse_number(ins):
    sign = ins()
    if sign == 2:
        raise StopIteration()
    buffer = ''
    code = ins()
    if code == 2:
        raise StopIteration()
    while code != 2:
        buffer += str(code)
        code = ins()
    if sign == 1:
        return int(buffer, 2) * -1
    return int(buffer, 2)

################################################################################

def parse_stack(ins, program):
    code = ins()
    if code == 0:
        # [Space]
        number = parse_number(ins)
        program.append((INS.PUSH, number))
    elif code == 1:
        # [Tab]
        code = ins()
        number = parse_number(ins)
        if code == 0:
            # [Tab][Space]
            program.append((INS.COPY, number))
        elif code == 1:
            # [Tab][Tab]
            raise StopIteration()
        else:
            # [Tab][Line]
            program.append((INS.AWAY, number))
    else:
        # [Line]
        code = ins()
        if code == 0:
            # [Line][Space]
            program.append(INS.COPY)
        elif code == 1:
            # [Line][Tab]
            program.append(INS.SWAP)
        else:
            # [Line][Line]
            program.append(INS.AWAY)

def parse_math(ins, program):
    code = ins()
    if code == 0:
        # [Space]
        code = ins()
        if code == 0:
            # [Space][Space]
            program.append(INS.ADD)
        elif code == 1:
            # [Space][Tab]
            program.append(INS.SUB)
        else:
            # [Space][Line]
            program.append(INS.MUL)
    elif code == 1:
        # [Tab]
        code = ins()
        if code == 0:
            # [Tab][Space]
            program.append(INS.DIV)
        elif code == 1:
            # [Tab][Tab]
            program.append(INS.MOD)
        else:
            # [Tab][Line]
            raise StopIteration()
    else:
        # [Line]
        raise StopIteration()

def parse_heap(ins, program):
    code = ins()
    if code == 0:
        # [Space]
        program.append(INS.SET)
    elif code == 1:
        # [Tab]
        program.append(INS.GET)
    else:
        # [Line]
        raise StopIteration()

def parse_io(ins, program):
    code = ins()
    if code == 0:
        # [Space]
        code = ins()
        if code == 0:
            # [Space][Space]
            program.append(INS.OCHR)
        elif code == 1:
            # [Space][Tab]
            program.append(INS.OINT)
        else:
            # [Space][Line]
            raise StopIteration()
    elif code == 1:
        # [Tab]
        code = ins()
        if code == 0:
            # [Tab][Space]
            program.append(INS.ICHR)
        elif code == 1:
            # [Tab][Tab]
            program.append(INS.IINT)
        else:
            # [Tab][Line]
            raise StopIteration()
    else:
        # [Line]
        raise StopIteration()

def parse_flow(ins, program):
    code = ins()
    if code == 0:
        # [Space]
        code = ins()
        label = parse_number(ins)
        if code == 0:
            # [Space][Space]
            program.append((INS.PART, label))
        elif code == 1:
            # [Space][Tab]
            program.append((INS.CALL, label))
        else:
            # [Space][Line]
            program.append((INS.GOTO, label))
    elif code == 1:
        # [Tab]
        code = ins()
        if code == 0:
            # [Tab][Space]
            label = parse_number(ins)
            program.append((INS.ZERO, label))
        elif code == 1:
            # [Tab][Tab]
            label = parse_number(ins)
            program.append((INS.LESS, label))
        else:
            # [Tab][Line]
            program.append(INS.BACK)
    else:
        # [Line]
        code = ins()
        if code == 2:
            # [Line][Line]
            program.append(INS.EXIT)
        else:
            # [Line][Space] or [Line][Tab]
            raise StopIteration()

################################################################################

MNEMONIC = '\
push copy swap away add sub mul div mod set get part \
call goto zero less back exit ochr oint ichr iint'.split()

HAS_ARG = [getattr(INS, name) for name in
           'PUSH COPY AWAY PART CALL GOTO ZERO LESS'.split()]

HAS_LABEL = [getattr(INS, name) for name in
             'PART CALL GOTO ZERO LESS'.split()]

def disassemble(program, names=False):
    if names:
        names = create_names(program)
    for ins in program:
        if isinstance(ins, tuple):
            ins, arg = ins
            assert ins in HAS_ARG
            has_arg = True
        else:
            assert INS.PUSH <= ins <= INS.IINT
            has_arg = False
        if ins == INS.PART:
            if names:
                print(MNEMONIC[ins], '"' + names[arg] + '"')
            else:
                print(MNEMONIC[ins], arg)
        elif has_arg and ins in HAS_ARG:
            if ins in HAS_LABEL and names:
                assert arg in names
                print('     ' + MNEMONIC[ins], '"' + names[arg] + '"')
            else:
                print('     ' + MNEMONIC[ins], arg)
        else:
            print('     ' + MNEMONIC[ins])

################################################################################

def create_names(program):
    names = {}
    number = 1
    for ins in program:
        if isinstance(ins, tuple) and ins[0] == INS.PART:
            label = ins[1]
            assert label not in names
            names[label] = number_to_name(number)
            number += 1
    return names

def number_to_name(number):
    name = ''
    for offset in reversed(list(partition_number(number, 27))):
        if offset:
            name += chr(ord('A') + offset - 1)
        else:
            name += '_'
    return name

def partition_number(number, base):
    div, mod = divmod(number, base)
    yield mod
    while div:
        div, mod = divmod(div, base)
        yield mod

################################################################################

CODE = ('   \t\n',
        ' \n ',
        ' \t  \t\n',
        ' \n\t',
        ' \n\n',
        ' \t\n \t\n',
        '\t   ',
        '\t  \t',
        '\t  \n',
        '\t \t ',
        '\t \t\t',
        '\t\t ',
        '\t\t\t',
        '\n   \t\n',
        '\n \t \t\n',
        '\n \n \t\n',
        '\n\t  \t\n',
        '\n\t\t \t\n',
        '\n\t\n',
        '\n\n\n',
        '\t\n  ',
        '\t\n \t',
        '\t\n\t ',
        '\t\n\t\t')

EXAMPLE = ''.join(CODE)

################################################################################

NOTES = '''\
STACK
=====
  push number
  copy
  copy number
  swap
  away
  away number

MATH
====
  add
  sub
  mul
  div
  mod

HEAP
====
  set
  get

FLOW
====
  part label
  call label
  goto label
  zero label
  less label
  back
  exit

I/O
===
  ochr
  oint
  ichr
  iint'''

################################################################################
################################################################################

class Stack:

    def __init__(self):
        self.__data = []

    # Stack Operators

    def push(self, number):
        self.__data.append(number)

    def copy(self, number=None):
        if number is None:
            self.__data.append(self.__data[-1])
        else:
            size = len(self.__data)
            index = size - number - 1
            assert 0 <= index < size
            self.__data.append(self.__data[index])

    def swap(self):
        self.__data[-2], self.__data[-1] = self.__data[-1], self.__data[-2]

    def away(self, number=None):
        if number is None:
            self.__data.pop()
        else:
            size = len(self.__data)
            index = size - number - 1
            assert 0 <= index < size
            del self.__data[index:-1]

    # Math Operators

    def add(self):
        suffix = self.__data.pop()
        prefix = self.__data.pop()
        self.__data.append(prefix + suffix)

    def sub(self):
        suffix = self.__data.pop()
        prefix = self.__data.pop()
        self.__data.append(prefix - suffix)

    def mul(self):
        suffix = self.__data.pop()
        prefix = self.__data.pop()
        self.__data.append(prefix * suffix)

    def div(self):
        suffix = self.__data.pop()
        prefix = self.__data.pop()
        self.__data.append(prefix // suffix)

    def mod(self):
        suffix = self.__data.pop()
        prefix = self.__data.pop()
        self.__data.append(prefix % suffix)

    # Program Operator

    def pop(self):
        return self.__data.pop()

################################################################################

class Heap:

    def __init__(self):
        self.__data = {}

    def set_(self, addr, item):
        if item:
            self.__data[addr] = item
        elif addr in self.__data:
            del self.__data[addr]

    def get_(self, addr):
        return self.__data.get(addr, 0)

################################################################################

import os
import zlib
import msvcrt
import pickle
import string

class CleanExit(Exception): pass

NOP = lambda arg: None

DEBUG_WHITESPACE = False

################################################################################

class Program:

    NO_ARGS = INS.COPY, INS.SWAP, INS.AWAY, INS.ADD, \
              INS.SUB, INS.MUL, INS.DIV, INS.MOD, \
              INS.SET, INS.GET, INS.BACK, INS.EXIT, \
              INS.OCHR, INS.OINT, INS.ICHR, INS.IINT
    HAS_ARG = INS.PUSH, INS.COPY, INS.AWAY, INS.PART, \
              INS.CALL, INS.GOTO, INS.ZERO, INS.LESS

    def __init__(self, code):
        self.__data = code
        self.__validate()
        self.__build_jump()
        self.__check_jump()
        self.__setup_exec()

    def __setup_exec(self):
        self.__iptr = 0
        self.__stck = stack = Stack()
        self.__heap = Heap()
        self.__cast = []
        self.__meth = (stack.push, stack.copy, stack.swap, stack.away,
                       stack.add, stack.sub, stack.mul, stack.div, stack.mod,
                       self.__set, self.__get,
                       NOP, self.__call, self.__goto, self.__zero,
                            self.__less, self.__back, self.__exit,
                       self.__ochr, self.__oint, self.__ichr, self.__iint)

    def step(self):
        ins = self.__data[self.__iptr]
        self.__iptr += 1
        if isinstance(ins, tuple):
            self.__meth[ins[0]](ins[1])
        else:
            self.__meth[ins]()

    def run(self):
        while True:
            ins = self.__data[self.__iptr]
            self.__iptr += 1
            if isinstance(ins, tuple):
                self.__meth[ins[0]](ins[1])
            else:
                self.__meth[ins]()

    def __oint(self):
        for digit in str(self.__stck.pop()):
            msvcrt.putwch(digit)

    def __ichr(self):
        addr = self.__stck.pop()
        # Input Routine
        while msvcrt.kbhit():
            msvcrt.getwch()
        while True:
            char = msvcrt.getwch()
            if char in '\x00\xE0':
                msvcrt.getwch()
            elif char in string.printable:
                char = char.replace('\r', '\n')
                msvcrt.putwch(char)
                break
        item = ord(char)
        # Storing Number
        self.__heap.set_(addr, item)

    def __iint(self):
        addr = self.__stck.pop()
        # Input Routine
        while msvcrt.kbhit():
            msvcrt.getwch()
        buff = ''
        char = msvcrt.getwch()
        while char != '\r' or not buff:
            if char in '\x00\xE0':
                msvcrt.getwch()
            elif char in '+-' and not buff:
                msvcrt.putwch(char)
                buff += char
            elif '0' <= char <= '9':
                msvcrt.putwch(char)
                buff += char
            elif char == '\b':
                if buff:
                    buff = buff[:-1]
                    msvcrt.putwch(char)
                    msvcrt.putwch(' ')
                    msvcrt.putwch(char)
            char = msvcrt.getwch()
        msvcrt.putwch(char)
        msvcrt.putwch('\n')
        item = int(buff)
        # Storing Number
        self.__heap.set_(addr, item)

    def __goto(self, label):
        self.__iptr = self.__jump[label]

    def __zero(self, label):
        if self.__stck.pop() == 0:
            self.__iptr = self.__jump[label]

    def __less(self, label):
        if self.__stck.pop() < 0:
            self.__iptr = self.__jump[label]

    def __exit(self):
        self.__setup_exec()
        raise CleanExit()

    def __set(self):
        item = self.__stck.pop()
        addr = self.__stck.po

© Stack Overflow or respective owner

Related posts about whitespace

Related posts about python