Starting point for a collaboratively developed golfing language interpreted in Python

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
8
down vote

favorite
1












Over on Code Golf, we've decided, for once, to write some readable code! However, we're obviously not very good at that, so we need your help to make it even better.



I've proposed this challenge on PPCG, which requires an interpreter for a new golfing language, written in Python 3.4. I've finished the base file, but, as this will be collaborated on by many users, I need to make sure it's readable, understandable and, well, not golfed.



The basic interpreter included a collection of different memory models, as to not restrict modifications, such as a tape (à la brainfuck), or a stack, similar to ><>, and also includes the basis of commands needed to perform the tasks in the linked challenge.



Unfortunately, it is sparse when it comes to comments, and in addition to feedback on how the code is written, I'd appreciate feedback or suggestions on the level of commenting needed, ranging from I can barely understand what this is doing, fill it all with comments to Pepper a few throughout, near the more complicated parts. The code is as follows:





import argparse
import collections
import sys

# === Constants === #

inf = float('inf')
nan = float('nan')

digits = str.maketrans('⁰¹²³⁴⁵⁶⁷⁸⁹₀₁₂₃₄₅₆₇₈₉', '01234567890123456789')

code_page = '''ÀÁÂÄÆÃÅĀĄtnĆČÇĎÐÈÉÊËĒĖĚĘÌÍÎÏĪĮĹĽ'''
code_page += ''' !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz~¶'''
code_page += '''ŁŃŇÑŊÒÓÔÖŒÕØŌŔŘŚŠŤŦÙÚÛÜŮŪŴÝŶŸŹŽŻàáâäæãåāąćčçďðèéêëēėěęìíîïīįĺľłńňñŋòóôöœøōõŕřßśšťŧùúûüůūŵýŷÿźžż◊'''
code_page += '''ΑΆΒΓΔΕΈΖΗΉΘΙΊΚΛΜΝΞΟΌΠΡΣΤΥΎΦΧΨΩΏ'''

code_page += '''αάβγδεέζηήθιίΐκλμνξοόπσςτυύΰφχψωώ'''
code_page += '''ǴḰḾṔẂǵḱḿṕẃḂḞĠḢṀȮṖṠṪẊḃḟġḣṁȯṗṡṫẋ§ĂĞĬŎŬĴăğĭŏŭĵªº‹›'''
code_page += '''ƁƇƊƑƓƘⱮƝƤƬƲȤɓƈɗƒɠɦƙɱɲƥʠɼʂƭʋȥ©®ıȷ'''
code_page += '''ЉЊЕРТЗУИОПШАСДФГХЈКЛЧЋЅЏЦВБНМЂЖљњертзуиопшасдфгхјклчћѕџцвбнмђжÞþ'''
code_page += '''†∂∆≈≠√∈∉∌∋∩∪¬∧∨⊕¤₽¥£¢€₩‰¿¡⁇⁈‼⁉‽⸘…°•”“„’‘≤«·»≥ᴇ∞¦×⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾÷₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎'''

# === Memory models === #

class Model:
def is_tape(self):
return self.__class__.__name__ == 'Tape'

def is_stack(self):
return self.__class__.__name__ == 'Stack'

def is_grid(self):
return self.__class__.__name__ == 'Grid'

def is_register(self):
return self.__class__.__name__ == 'Register'

def is_deque(self):
return self.__class__.__name__ == 'Deque'

class Stack(Model):
def __init__(self, *values):
self.stack = list(values)

def push(self, *values):
for value in values:
self.stack.append(value)

def pop(self, index = -1):
return self.stack.pop(index)

def peek(self, index = -1):
return self.stack[index % len(self.stack)]

def __repr__(self):
return 'Stack()'.format(self.stack)

def __str__(self):
return str(self.stack)

def __iter__(self):
return iter(self.stack)

def __getitem__(self, index):
return self.stack[index]

def __setitem__(self, index, value):
self.stack[index] = value

class Tape(Model):
def __init__(self, size = inf, wrap = True):
self.size = size
self.wrap = wrap
self.tape = [0]
self.index = 0
if size != inf:
self.tape = [0] * size

def __repr__(self):
if self.size == inf:
return 'Tape([ … ])'

final = 'Tape(['
for i, val in enumerate(self.tape):
val = str(val)
if i == self.index:
final += '' + val + ''
else:
final += val
final += ' '
final += '])'
return final

def __str__(self):
if self.size == inf:
if len(self.tape):
template = '[ … … ]'
final = ''
for i, val in enumerate(self.tape):
val = str(val)
if i == self.index:
final += '' + val + ''
else:
final += val
final += ' '
return template.format(final[:-1])

else:
return '[ … ]'
else:
final = '['
for i, val in enumerate(self.tape):
val = str(val)
if i == self.index:
final += '' + val + ''
else:
final += val
final += ' '
final = final.strip() + ']'
return final

def __iter__(self):
return iter(self.tape)

@property
def cell(self):
return self.tape[self.index]

def write_cell(self, value):
self.tape[self.index] = value

def move_right(self):
self.index += 1
if self.size != inf:
if self.wrap:
self.index %= self.size
else:
while self.index >= len(self.tape):
self.tape.append(0)

def move_left(self):
self.index -= 1
if self.size != inf:
if self.wrap and self.index < 0:
self.index = self.size - 1
elif not self.wrap and self.index < 0:
raise IndexError

else:
if self.index < 0:
self.tape.insert(0, 0)
self.index = 0

class Grid(Model):
def __init__(self, xlen = inf, ylen = inf, xwrap = True, ywrap = True):
self.xlen = xlen
self.ylen = ylen
self.xwrap = xwrap
self.ywrap = ywrap
self.xindex = 0
self.yindex = 0
self.grid =
self.pointer_value = 0

if xlen != inf:
if ylen != inf:
for _ in range(ylen):
self.grid.append([0] * xlen)
else:
self.grid.append([0] * xlen)
else:
if ylen != inf:
for _ in range(ylen):
self.grid.append([0])
else:
for _ in range(10):
self.grid.append([0] * 10)

def __repr__(self):
final = ''

if self.xlen == inf:
if self.ylen == inf:
final = '[ … …n ⋮ ⋮ ]'
else:
final = ''
for line in self.grid:
final += ' ' + str(line)
.replace(',', '')
.replace('[', '')
.replace(']', '') + ' …n'
final = '[' + final[1:-1] + ']'
else:
if self.ylen == inf:
final = '[' + ('⋮ ' * int(len(self.grid[0]) * 4/5)) + 'n'
for line in self.grid:
final += ' ' + str(line)
.replace(',', '')
.replace('[', '')
.replace(']', '') + 'n'
final += ' ' + ('⋮ ' * int(len(self.grid[0]) * 4/5)) + ']'
else:
final = ''
for line in self.grid:
final += ' ' + str(line)
.replace(',', '')
.replace('[', '')
.replace(']', '') + 'n'
final = '[' + final[1:-1] + ']'

return final

def __str__(self):
array = list(map(lambda a: list(map(str, a)), self.grid))
max_row_length = max(map(len, array))

for index, row in enumerate(array):
while len(row) < max_row_length:
row.append('0')
array[index] = row

pad = max(max(map(len, a)) for a in array) + 1
final = ''
for row in array:
for element in row:
element = element.rjust(pad)
final += element
final += 'n'
return final + '<> <> '.format(self.xindex, self.yindex)

def __iter__(self):
return iter(strip(flatten(self.grid), 0))

@property
def cell(self):
return self.grid[self.yindex][self.xindex]

def write_cell(self, value):
self.grid[self.yindex][self.xindex] = value

def write_pointer(self):
self.pointer_value = self.cell

def move_right(self):
self.xindex += 1
if self.xlen == inf:
self.grid[self.yindex].append(0)
else:
if self.xwrap:
self.xindex %= self.xlen

def move_left(self):
self.xindex -= 1
if self.xlen == inf and self.xindex < 0:
self.grid[self.yindex].insert(0, 0)
self.xindex = 0
else:
if self.xindex < 0 and self.xwrap:
self.xindex = self.xlen - abs(self.xindex)
elif self.xindex < 0 and not self.xwrap:
self.xindex = self.xlen + 1

def move_down(self):
self.yindex += 1
if self.ylen == inf:
if self.xlen != inf:
self.grid.append([0] * self.xlen)
else:
self.grid.append([0])
else:
if self.ywrap:
self.yindex %= self.ylen

def move_up(self):
self.yindex -= 1
if self.ylen == inf and self.yindex < 0:
self.grid[self.xindex].insert(0, 0)
self.yindex = 0
else:
if self.yindex < 0 and self.ywrap:
self.yindex = self.ylen - abs(self.yindex)
elif self.yindex < 0 and not self.ywrap:
self.yindex = self.ylen + 1

class Register(Model, int):
def __init__(self, value = None):
self.value = value

def __repr__(self):
return 'Register()'.format(self.value)

def __str__(self):
return str(self.value)

class Deque(Model, collections.deque):
def __repr__(self):
rep = super().__repr__()[5:]
return 'Deque' + rep

def __str__(self):
return super().__repr__()[6:-1]

push = collections.deque.append

def current_value(model):
if model.is_tape() or model.is_grid():
return model.cell
if model.is_stack() or model.is_deque():
return model.pop()
if model.is_register():
return model.value
return False

def identity(value):
return value

def overload(stack_cmd = identity, tape_cmd = identity, grid_cmd = identity,
register_cmd = identity, deque_cmd = identity, all_cmd = None):

def inner(model):
if all_cmd is not None:
return all_cmd(model)

if model.is_stack():
return stack_cmd(model)

if model.is_tape():
return tape_cmd(model)

if model.is_grid():
return grid_cmd(model)

if model.is_register():
return register_cmd(model)

return deque_cmd(model)

return inner

def make_nilad(value, model):
value = eval(value)

if model.is_tape() or model.is_grid():
model.write_cell(value)

if model.is_stack():
model.push(value)

if model.is_deque():
model.append(value)

if model.is_register():
model.value = value

# === Functions === #

# = Stack Commands = #

def add(stack):
stack.push(stack.pop() + stack.pop())

def subtract(stack):
stack.push(stack.pop() - stack.pop())

def multiply(stack):
stack.push(stack.pop() * stack.pop())

def divide(stack):
stack.push(stack.pop() / stack.pop())

def modulo(stack):
stack.push(stack.pop() % stack.pop())

def swap(array):
array[-2], array[-1] = array[-1], array[-2]

def reverse(stack):
if hasattr(stack.peek(), '__iter__'):
stack.push(stack.pop()[::-1])
else:
stack.stack = stack.stack[::-1]

def product(array):
total = 1
for element in array:
total *= element
return total

def flatten(array):
flat =
if type(array) == list:
for item in array:
flat += flatten(item)
else:
flat.append(array)
return flat

def strip(array, trim):
final =
for value in array:
if value != trim or final:
final.append(value)

array = final[::-1]
final =

for value in array:
if value != trim or final:
final.append(value)

return final[::-1]

def rotate(stack):
a = stack.pop()
b = stack.pop()
c = stack.pop()
stack.push(a, c, b)

# = Tape / Grid Commands = #

def increment_cell(tape_grid):
tape_grid.write_cell(tape_grid.cell + 1)

def decrement_cell(tape_grid):
tape_grid.write_cell(tape_grid.cell - 1)

def write_grid_pointer(grid):
grid.pointer_value

def getchar():
ret = sys.stdin.read(1)
if ret:
return ret
return chr(0)

def getinput():
try:
inputted = input()
except:
return ''
try:
return eval(inputted)
except:
return inputted

# === Operators === #

def while_loop(code):
def while_inner(model, used):
while current_value(model):
model = interpret(code, model, used)
return while_inner

def if_statement(code):
def if_inner(model, used):
else_, if_ = list(map(lambda a: a[::-1], code[::-1].split('}', 1)))
if current_value(model):
model = interpret(if_, model, used)
else:
model = interpret(else_, model, used)
return if_inner

# === Command lookups === #

memories =

'#': (0, Tape),
'$': (0, Stack),
'G': (0, Grid),
'D': (0, Deque),
'S': (1, Register),

'À': (2, lambda size, wrap: Tape(size, wrap)),
'Á': (1, lambda size: Tape(size, wrap = True)),
'Â': (1, lambda size: Tape(size, wrap = False)),
'Ä': (1, lambda wrap: Tape(wrap = wrap)),

'Ç´': (4, lambda xlen, ylen, xwrap, ywrap: Grid(xlen, ylen, xwrap, ywrap)),



functions =

'+': overload(
stack_cmd = add,
deque_cmd = add,
tape_cmd = increment_cell,
grid_cmd = increment_cell,
),

'-': overload(
stack_cmd = subtract,
deque_cmd = subtract,
tape_cmd = decrement_cell,
grid_cmd = decrement_cell,
),

'%': overload(
stack_cmd = modulo,
deque_cmd = modulo,
),

':': overload(
stack_cmd = lambda stack: stack.push(stack.peek()),
deque_cmd = lambda deque: deque.append(deque[-1]),
),

';': overload(
stack_cmd = lambda stack: stack.pop(),
deque_cmd = lambda deque: deque.pop(),
tape_cmd = lambda tape: tape.write_cell(0),
grid_cmd = lambda grid: grid.write_cell(0),
),

'<': overload(
tape_cmd = Tape.move_left,
grid_cmd = Grid.move_left,
),

'=': overload(
stack_cmd = lambda stack: stack.push(stack.pop() == stack.pop()),
grid_cmd = Grid.move_down,
),

'>': overload(
tape_cmd = Tape.move_right,
grid_cmd = Grid.move_right,
),

'?': overload(
stack_cmd = lambda stack: stack.push(getinput()),
deque_cmd = lambda deque: deque.append(getinput()),
tape_cmd = lambda tape: tape.write_cell(ord(getchar())),
grid_cmd = lambda grid: grid.write_cell(ord(getchar())),
),

'O': overload(
all_cmd = lambda model: print(end = str(current_value(model))),
),

'Z': overload(
stack_cmd = lambda stack: stack.push(stack.pop() > 0),
),

'R': overload(
stack_cmd = reverse,
),

'^': overload(
grid_cmd = Grid.move_up,
),

'h': overload(
all_cmd = print,
),

'o': overload(
all_cmd = lambda model: print(end = chr(current_value(model))),
),

'r': overload(
stack_cmd = rotate,
deque_cmd = lambda deque: deque.rotate(deque.pop()),
),

's': overload(
stack_cmd = swap,
deque_cmd = swap,
),

'Π': overload(
stack_cmd = lambda stack: stack.push(product(stack)),
tape_cmd = lambda tape: tape.write_cell(product(tape)),
grid_cmd = lambda grid: grid.write_cell(product(flatten(list(grid)))),
),

'Σ': overload(
stack_cmd = lambda stack: stack.push(sum(stack)),
tape_cmd = lambda tape: tape.write_cell(sum(tape)),
grid_cmd = lambda grid: grid.write_cell(sum(flatten(list(grid)))),
),

'×': overload(
stack_cmd = multiply,
grid_cmd = lambda grid: grid.write_cell(grid.pointer_value),
),

'÷': overload(
stack_cmd = divide,
grid_cmd = Grid.write_pointer,
),



operators =  












up vote
8
down vote

favorite
1












Over on Code Golf, we've decided, for once, to write some readable code! However, we're obviously not very good at that, so we need your help to make it even better.



I've proposed this challenge on PPCG, which requires an interpreter for a new golfing language, written in Python 3.4. I've finished the base file, but, as this will be collaborated on by many users, I need to make sure it's readable, understandable and, well, not golfed.



The basic interpreter included a collection of different memory models, as to not restrict modifications, such as a tape (à la brainfuck), or a stack, similar to ><>, and also includes the basis of commands needed to perform the tasks in the linked challenge.



Unfortunately, it is sparse when it comes to comments, and in addition to feedback on how the code is written, I'd appreciate feedback or suggestions on the level of commenting needed, ranging from I can barely understand what this is doing, fill it all with comments to Pepper a few throughout, near the more complicated parts. The code is as follows:





import argparse
import collections
import sys

# === Constants === #

inf = float('inf')
nan = float('nan')

digits = str.maketrans('⁰¹²³⁴⁵⁶⁷⁸⁹₀₁₂₃₄₅₆₇₈₉', '01234567890123456789')

code_page = '''ÀÁÂÄÆÃÅĀĄtnĆČÇĎÐÈÉÊËĒĖĚĘÌÍÎÏĪĮĹĽ'''
code_page += ''' !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz~¶'''
code_page += '''ŁŃŇÑŊÒÓÔÖŒÕØŌŔŘŚŠŤŦÙÚÛÜŮŪŴÝŶŸŹŽŻàáâäæãåāąćčçďðèéêëēėěęìíîïīįĺľłńňñŋòóôöœøōõŕřßśšťŧùúûüůūŵýŷÿźžż◊'''
code_page += '''ΑΆΒΓΔΕΈΖΗΉΘΙΊΚΛΜΝΞΟΌΠΡΣΤΥΎΦΧΨΩΏ'''

code_page += '''αάβγδεέζηήθιίΐκλμνξοόπσςτυύΰφχψωώ'''
code_page += '''ǴḰḾṔẂǵḱḿṕẃḂḞĠḢṀȮṖṠṪẊḃḟġḣṁȯṗṡṫẋ§ĂĞĬŎŬĴăğĭŏŭĵªº‹›'''
code_page += '''ƁƇƊƑƓƘⱮƝƤƬƲȤɓƈɗƒɠɦƙɱɲƥʠɼʂƭʋȥ©®ıȷ'''
code_page += '''ЉЊЕРТЗУИОПШАСДФГХЈКЛЧЋЅЏЦВБНМЂЖљњертзуиопшасдфгхјклчћѕџцвбнмђжÞþ'''
code_page += '''†∂∆≈≠√∈∉∌∋∩∪¬∧∨⊕¤₽¥£¢€₩‰¿¡⁇⁈‼⁉‽⸘…°•”“„’‘≤«·»≥ᴇ∞¦×⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾÷₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎'''

# === Memory models === #

class Model:
def is_tape(self):
return self.__class__.__name__ == 'Tape'

def is_stack(self):
return self.__class__.__name__ == 'Stack'

def is_grid(self):
return self.__class__.__name__ == 'Grid'

def is_register(self):
return self.__class__.__name__ == 'Register'

def is_deque(self):
return self.__class__.__name__ == 'Deque'

class Stack(Model):
def __init__(self, *values):
self.stack = list(values)

def push(self, *values):
for value in values:
self.stack.append(value)

def pop(self, index = -1):
return self.stack.pop(index)

def peek(self, index = -1):
return self.stack[index % len(self.stack)]

def __repr__(self):
return 'Stack()'.format(self.stack)

def __str__(self):
return str(self.stack)

def __iter__(self):
return iter(self.stack)

def __getitem__(self, index):
return self.stack[index]

def __setitem__(self, index, value):
self.stack[index] = value

class Tape(Model):
def __init__(self, size = inf, wrap = True):
self.size = size
self.wrap = wrap
self.tape = [0]
self.index = 0
if size != inf:
self.tape = [0] * size

def __repr__(self):
if self.size == inf:
return 'Tape([ … ])'

final = 'Tape(['
for i, val in enumerate(self.tape):
val = str(val)
if i == self.index:
final += '' + val + ''
else:
final += val
final += ' '
final += '])'
return final

def __str__(self):
if self.size == inf:
if len(self.tape):
template = '[ … … ]'
final = ''
for i, val in enumerate(self.tape):
val = str(val)
if i == self.index:
final += '' + val + ''
else:
final += val
final += ' '
return template.format(final[:-1])

else:
return '[ … ]'
else:
final = '['
for i, val in enumerate(self.tape):
val = str(val)
if i == self.index:
final += '' + val + ''
else:
final += val
final += ' '
final = final.strip() + ']'
return final

def __iter__(self):
return iter(self.tape)

@property
def cell(self):
return self.tape[self.index]

def write_cell(self, value):
self.tape[self.index] = value

def move_right(self):
self.index += 1
if self.size != inf:
if self.wrap:
self.index %= self.size
else:
while self.index >= len(self.tape):
self.tape.append(0)

def move_left(self):
self.index -= 1
if self.size != inf:
if self.wrap and self.index < 0:
self.index = self.size - 1
elif not self.wrap and self.index < 0:
raise IndexError

else:
if self.index < 0:
self.tape.insert(0, 0)
self.index = 0

class Grid(Model):
def __init__(self, xlen = inf, ylen = inf, xwrap = True, ywrap = True):
self.xlen = xlen
self.ylen = ylen
self.xwrap = xwrap
self.ywrap = ywrap
self.xindex = 0
self.yindex = 0
self.grid =
self.pointer_value = 0

if xlen != inf:
if ylen != inf:
for _ in range(ylen):
self.grid.append([0] * xlen)
else:
self.grid.append([0] * xlen)
else:
if ylen != inf:
for _ in range(ylen):
self.grid.append([0])
else:
for _ in range(10):
self.grid.append([0] * 10)

def __repr__(self):
final = ''

if self.xlen == inf:
if self.ylen == inf:
final = '[ … …n ⋮ ⋮ ]'
else:
final = ''
for line in self.grid:
final += ' ' + str(line)
.replace(',', '')
.replace('[', '')
.replace(']', '') + ' …n'
final = '[' + final[1:-1] + ']'
else:
if self.ylen == inf:
final = '[' + ('⋮ ' * int(len(self.grid[0]) * 4/5)) + 'n'
for line in self.grid:
final += ' ' + str(line)
.replace(',', '')
.replace('[', '')
.replace(']', '') + 'n'
final += ' ' + ('⋮ ' * int(len(self.grid[0]) * 4/5)) + ']'
else:
final = ''
for line in self.grid:
final += ' ' + str(line)
.replace(',', '')
.replace('[', '')
.replace(']', '') + 'n'
final = '[' + final[1:-1] + ']'

return final

def __str__(self):
array = list(map(lambda a: list(map(str, a)), self.grid))
max_row_length = max(map(len, array))

for index, row in enumerate(array):
while len(row) < max_row_length:
row.append('0')
array[index] = row

pad = max(max(map(len, a)) for a in array) + 1
final = ''
for row in array:
for element in row:
element = element.rjust(pad)
final += element
final += 'n'
return final + '<> <> '.format(self.xindex, self.yindex)

def __iter__(self):
return iter(strip(flatten(self.grid), 0))

@property
def cell(self):
return self.grid[self.yindex][self.xindex]

def write_cell(self, value):
self.grid[self.yindex][self.xindex] = value

def write_pointer(self):
self.pointer_value = self.cell

def move_right(self):
self.xindex += 1
if self.xlen == inf:
self.grid[self.yindex].append(0)
else:
if self.xwrap:
self.xindex %= self.xlen

def move_left(self):
self.xindex -= 1
if self.xlen == inf and self.xindex < 0:
self.grid[self.yindex].insert(0, 0)
self.xindex = 0
else:
if self.xindex < 0 and self.xwrap:
self.xindex = self.xlen - abs(self.xindex)
elif self.xindex < 0 and not self.xwrap:
self.xindex = self.xlen + 1

def move_down(self):
self.yindex += 1
if self.ylen == inf:
if self.xlen != inf:
self.grid.append([0] * self.xlen)
else:
self.grid.append([0])
else:
if self.ywrap:
self.yindex %= self.ylen

def move_up(self):
self.yindex -= 1
if self.ylen == inf and self.yindex < 0:
self.grid[self.xindex].insert(0, 0)
self.yindex = 0
else:
if self.yindex < 0 and self.ywrap:
self.yindex = self.ylen - abs(self.yindex)
elif self.yindex < 0 and not self.ywrap:
self.yindex = self.ylen + 1

class Register(Model, int):
def __init__(self, value = None):
self.value = value

def __repr__(self):
return 'Register()'.format(self.value)

def __str__(self):
return str(self.value)

class Deque(Model, collections.deque):
def __repr__(self):
rep = super().__repr__()[5:]
return 'Deque' + rep

def __str__(self):
return super().__repr__()[6:-1]

push = collections.deque.append

def current_value(model):
if model.is_tape() or model.is_grid():
return model.cell
if model.is_stack() or model.is_deque():
return model.pop()
if model.is_register():
return model.value
return False

def identity(value):
return value

def overload(stack_cmd = identity, tape_cmd = identity, grid_cmd = identity,
register_cmd = identity, deque_cmd = identity, all_cmd = None):

def inner(model):
if all_cmd is not None:
return all_cmd(model)

if model.is_stack():
return stack_cmd(model)

if model.is_tape():
return tape_cmd(model)

if model.is_grid():
return grid_cmd(model)

if model.is_register():
return register_cmd(model)

return deque_cmd(model)

return inner

def make_nilad(value, model):
value = eval(value)

if model.is_tape() or model.is_grid():
model.write_cell(value)

if model.is_stack():
model.push(value)

if model.is_deque():
model.append(value)

if model.is_register():
model.value = value

# === Functions === #

# = Stack Commands = #

def add(stack):
stack.push(stack.pop() + stack.pop())

def subtract(stack):
stack.push(stack.pop() - stack.pop())

def multiply(stack):
stack.push(stack.pop() * stack.pop())

def divide(stack):
stack.push(stack.pop() / stack.pop())

def modulo(stack):
stack.push(stack.pop() % stack.pop())

def swap(array):
array[-2], array[-1] = array[-1], array[-2]

def reverse(stack):
if hasattr(stack.peek(), '__iter__'):
stack.push(stack.pop()[::-1])
else:
stack.stack = stack.stack[::-1]

def product(array):
total = 1
for element in array:
total *= element
return total

def flatten(array):
flat =
if type(array) == list:
for item in array:
flat += flatten(item)
else:
flat.append(array)
return flat

def strip(array, trim):
final =
for value in array:
if value != trim or final:
final.append(value)

array = final[::-1]
final =

for value in array:
if value != trim or final:
final.append(value)

return final[::-1]

def rotate(stack):
a = stack.pop()
b = stack.pop()
c = stack.pop()
stack.push(a, c, b)

# = Tape / Grid Commands = #

def increment_cell(tape_grid):
tape_grid.write_cell(tape_grid.cell + 1)

def decrement_cell(tape_grid):
tape_grid.write_cell(tape_grid.cell - 1)

def write_grid_pointer(grid):
grid.pointer_value

def getchar():
ret = sys.stdin.read(1)
if ret:
return ret
return chr(0)

def getinput():
try:
inputted = input()
except:
return ''
try:
return eval(inputted)
except:
return inputted

# === Operators === #

def while_loop(code):
def while_inner(model, used):
while current_value(model):
model = interpret(code, model, used)
return while_inner

def if_statement(code):
def if_inner(model, used):
else_, if_ = list(map(lambda a: a[::-1], code[::-1].split('', 1)))
if current_value(model):
model = interpret(if_, model, used)
else:
model = interpret(else_, model, used)
return if_inner

# === Command lookups === #

memories =

'#': (0, Tape),
'$': (0, Stack),
'G': (0, Grid),
'D': (0, Deque),
'S': (1, Register),

'À': (2, lambda size, wrap: Tape(size, wrap)),
'Á': (1, lambda size: Tape(size, wrap = True)),
'Â': (1, lambda size: Tape(size, wrap = False)),
'Ä': (1, lambda wrap: Tape(wrap = wrap)),

'Ç´': (4, lambda xlen, ylen, xwrap, ywrap: Grid(xlen, ylen, xwrap, ywrap)),



functions =

'+': overload(
stack_cmd = add,
deque_cmd = add,
tape_cmd = increment_cell,
grid_cmd = increment_cell,
),

'-': overload(
stack_cmd = subtract,
deque_cmd = subtract,
tape_cmd = decrement_cell,
grid_cmd = decrement_cell,
),

'%': overload(
stack_cmd = modulo,
deque_cmd = modulo,
),

':': overload(
stack_cmd = lambda stack: stack.push(stack.peek()),
deque_cmd = lambda deque: deque.append(deque[-1]),
),

';': overload(
stack_cmd = lambda stack: stack.pop(),
deque_cmd = lambda deque: deque.pop(),
tape_cmd = lambda tape: tape.write_cell(0),
grid_cmd = lambda grid: grid.write_cell(0),
),

'<': overload(
tape_cmd = Tape.move_left,
grid_cmd = Grid.move_left,
),

'=': overload(
stack_cmd = lambda stack: stack.push(stack.pop() == stack.pop()),
grid_cmd = Grid.move_down,
),

'>': overload(
tape_cmd = Tape.move_right,
grid_cmd = Grid.move_right,
),

'?': overload(
stack_cmd = lambda stack: stack.push(getinput()),
deque_cmd = lambda deque: deque.append(getinput()),
tape_cmd = lambda tape: tape.write_cell(ord(getchar())),
grid_cmd = lambda grid: grid.write_cell(ord(getchar())),
),

'O': overload(
all_cmd = lambda model: print(end = str(current_value(model))),
),

'Z': overload(
stack_cmd = lambda stack: stack.push(stack.pop() > 0),
),

'R': overload(
stack_cmd = reverse,
),

'^': overload(
grid_cmd = Grid.move_up,
),

'h': overload(
all_cmd = print,
),

'o': overload(
all_cmd = lambda model: print(end = chr(current_value(model))),
),

'r': overload(
stack_cmd = rotate,
deque_cmd = lambda deque: deque.rotate(deque.pop()),
),

's': overload(
stack_cmd = swap,
deque_cmd = swap,
),

'Π': overload(
stack_cmd = lambda stack: stack.push(product(stack)),
tape_cmd = lambda tape: tape.write_cell(product(tape)),
grid_cmd = lambda grid: grid.write_cell(product(flatten(list(grid)))),
),

'Σ': overload(
stack_cmd = lambda stack: stack.push(sum(stack)),
tape_cmd = lambda tape: tape.write_cell(sum(tape)),
grid_cmd = lambda grid: grid.write_cell(sum(flatten(list(grid)))),
),

'×': overload(
stack_cmd = multiply,
grid_cmd = lambda grid: grid.write_cell(grid.pointer_value),
),

'÷': overload(
stack_cmd = divide,
grid_cmd = Grid.write_pointer,
),



operators =

'[': while_loop,
'': if_statement,



def decode(bytestring):
decoded = ''
continue_byte = False

for index, byte in enumerate(bytestring):
if continue_byte:
continue_byte = False
continue

if byte == 0xff:
continue_byte = True
byte += bytestring[index + 1] + 1

try:
decoded += code_page[byte]
except:
raise UnicodeDecodeError('Unknown byte value: '.format(byte))

return decoded

def next_index(string, start, char):
index = start
depth = 1
while depth and index < len(string) - 1:
index += 1
if string[index] in operators.keys():
depth += 1
if string[index] == char:
depth -= 1
return index

def parse(code):
tokens =
index = 0
comment = False

while index < len(code):
char = code[index]

if char == '◊':
comment = not comment

if comment:
index += 1
continue

if char.isdigit() or char == '_':
tokens.append(char)
index += 1
if index < len(code):
char = code[index]
while char.isdigit() and index < len(code):
tokens[-1] += char
index += 1
char = code[index]
tokens[-1] = tokens[-1].replace('_', '-')

if char in operators.keys():
ret = next_index(code, index, ']')
tokens.append(operators[char](code[index + 1 : ret]))
index = ret
else:
tokens.append(char)

index += 1
return tokens

def interpret(code, memory = None, used = None):
tokens = parse(code)
operable = (memory is not None)
if used is None:
used =

for tkn in tokens:

if callable(tkn) and operable:
tkn(memory, used)

elif tkn in '₀₁₂₃₄₅₆₇₈₉' and operable:
tkn = int(tkn.translate(digits))
memory = used[tkn]

elif tkn.isdigit() or (tkn[0] in '-' and tkn[1:].isdigit()):
make_nilad(tkn, memory)

elif tkn in memories.keys():
arity, mem_type = memories[tkn]

memory = mem_type(*[current_value(memory) for _ in range(arity)])
used.append(memory)

operable = True

elif tkn == '@' and operable:
used.append(memory)
memory = used.pop(-2)

elif tkn in functions.keys() and operable:
functions[tkn](memory)

else:
continue

return memory

if __name__ == '__main__':

argparser = argparse.ArgumentParser()

a = 'store_true'

getcode = argparser.add_mutually_exclusive_group()
getcode.add_argument('-f', '--file', help = 'Specifies that code be read from a file', action = a)
getcode.add_argument('-c', '--cmd', '--cmdline', help = 'Specifies that code be read from the command line', action = a)

argparser.add_argument('-u', '--unicode', help = 'Use utf-8 encoding for files', action = a)

argparser.add_argument('program')
argparser.add_argument('argv', nargs = '*')

settings = argparser.parse_args()

if settings.file:
with open(settings.program, mode = 'rb') as file:
contents = file.read()

if settings.unicode:
contents = ''.join([char for char in contents.decode('utf-8') if char in code_page])
else:
contents = decode(contents)

if settings.cmd:
contents = bytes(settings.program, 'utf-8')

if settings.unicode:
contents = ''.join([char for char in contents.decode('utf-8') if char in code_page])
else:
contents = decode(contents)

interpret(contents)


Try it online!



For those interested, the GitHub repository contains 18 examples, all responses to the challenges listed in the question linked in the second paragraph







share', 1)))
if current_value(model):
model = interpret(if_, model, used)
else:
model = interpret(else_, model, used)
return if_inner

# === Command lookups === #

memories =

'#': (0, Tape),
'$': (0, Stack),
'G': (0, Grid),
'D': (0, Deque),
'S': (1, Register),

'À': (2, lambda size, wrap: Tape(size, wrap)),
'Á': (1, lambda size: Tape(size, wrap = True)),
'Â': (1, lambda size: Tape(size, wrap = False)),
'Ä': (1, lambda wrap: Tape(wrap = wrap)),

'Ç´': (4, lambda xlen, ylen, xwrap, ywrap: Grid(xlen, ylen, xwrap, ywrap)),



functions =

'+': overload(
stack_cmd = add,
deque_cmd = add,
tape_cmd = increment_cell,
grid_cmd = increment_cell,
),

'-': overload(
stack_cmd = subtract,
deque_cmd = subtract,
tape_cmd = decrement_cell,
grid_cmd = decrement_cell,
),

'%': overload(
stack_cmd = modulo,
deque_cmd = modulo,
),

':': overload(
stack_cmd = lambda stack: stack.push(stack.peek()),
deque_cmd = lambda deque: deque.append(deque[-1]),
),

';': overload(
stack_cmd = lambda stack: stack.pop(),
deque_cmd = lambda deque: deque.pop(),
tape_cmd = lambda tape: tape.write_cell(0),
grid_cmd = lambda grid: grid.write_cell(0),
),

'<': overload(
tape_cmd = Tape.move_left,
grid_cmd = Grid.move_left,
),

'=': overload(
stack_cmd = lambda stack: stack.push(stack.pop() == stack.pop()),
grid_cmd = Grid.move_down,
),

'>': overload(
tape_cmd = Tape.move_right,
grid_cmd = Grid.move_right,
),

'?': overload(
stack_cmd = lambda stack: stack.push(getinput()),
deque_cmd = lambda deque: deque.append(getinput()),
tape_cmd = lambda tape: tape.write_cell(ord(getchar())),
grid_cmd = lambda grid: grid.write_cell(ord(getchar())),
),

'O': overload(
all_cmd = lambda model: print(end = str(current_value(model))),
),

'Z': overload(
stack_cmd = lambda stack: stack.push(stack.pop() > 0),
),

'R': overload(
stack_cmd = reverse,
),

'^': overload(
grid_cmd = Grid.move_up,
),

'h': overload(
all_cmd = print,
),

'o': overload(
all_cmd = lambda model: print(end = chr(current_value(model))),
),

'r': overload(
stack_cmd = rotate,
deque_cmd = lambda deque: deque.rotate(deque.pop()),
),

's': overload(
stack_cmd = swap,
deque_cmd = swap,
),

'Π': overload(
stack_cmd = lambda stack: stack.push(product(stack)),
tape_cmd = lambda tape: tape.write_cell(product(tape)),
grid_cmd = lambda grid: grid.write_cell(product(flatten(list(grid)))),
),

'Σ': overload(
stack_cmd = lambda stack: stack.push(sum(stack)),
tape_cmd = lambda tape: tape.write_cell(sum(tape)),
grid_cmd = lambda grid: grid.write_cell(sum(flatten(list(grid)))),
),

'×': overload(
stack_cmd = multiply,
grid_cmd = lambda grid: grid.write_cell(grid.pointer_value),
),

'÷': overload(
stack_cmd = divide,
grid_cmd = Grid.write_pointer,
),



operators =
improve this question













Over on Code Golf, we've decided, for once, to write some readable code! However, we're obviously not very good at that, so we need your help to make it even better.



I've proposed this challenge on PPCG, which requires an interpreter for a new golfing language, written in Python 3.4. I've finished the base file, but, as this will be collaborated on by many users, I need to make sure it's readable, understandable and, well, not golfed.



The basic interpreter included a collection of different memory models, as to not restrict modifications, such as a tape (à la brainfuck), or a stack, similar to ><>, and also includes the basis of commands needed to perform the tasks in the linked challenge.



Unfortunately, it is sparse when it comes to comments, and in addition to feedback on how the code is written, I'd appreciate feedback or suggestions on the level of commenting needed, ranging from I can barely understand what this is doing, fill it all with comments to Pepper a few throughout, near the more complicated parts. The code is as follows:





import argparse
import collections
import sys

# === Constants === #

inf = float('inf')
nan = float('nan')

digits = str.maketrans('⁰¹²³⁴⁵⁶⁷⁸⁹₀₁₂₃₄₅₆₇₈₉', '01234567890123456789')

code_page = '''ÀÁÂÄÆÃÅĀĄtnĆČÇĎÐÈÉÊËĒĖĚĘÌÍÎÏĪĮĹĽ'''
code_page += ''' !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz~¶'''
code_page += '''ŁŃŇÑŊÒÓÔÖŒÕØŌŔŘŚŠŤŦÙÚÛÜŮŪŴÝŶŸŹŽŻàáâäæãåāąćčçďðèéêëēėěęìíîïīįĺľłńňñŋòóôöœøōõŕřßśšťŧùúûüůūŵýŷÿźžż◊'''
code_page += '''ΑΆΒΓΔΕΈΖΗΉΘΙΊΚΛΜΝΞΟΌΠΡΣΤΥΎΦΧΨΩΏ'''

code_page += '''αάβγδεέζηήθιίΐκλμνξοόπσςτυύΰφχψωώ'''
code_page += '''ǴḰḾṔẂǵḱḿṕẃḂḞĠḢṀȮṖṠṪẊḃḟġḣṁȯṗṡṫẋ§ĂĞĬŎŬĴăğĭŏŭĵªº‹›'''
code_page += '''ƁƇƊƑƓƘⱮƝƤƬƲȤɓƈɗƒɠɦƙɱɲƥʠɼʂƭʋȥ©®ıȷ'''
code_page += '''ЉЊЕРТЗУИОПШАСДФГХЈКЛЧЋЅЏЦВБНМЂЖљњертзуиопшасдфгхјклчћѕџцвбнмђжÞþ'''
code_page += '''†∂∆≈≠√∈∉∌∋∩∪¬∧∨⊕¤₽¥£¢€₩‰¿¡⁇⁈‼⁉‽⸘…°•”“„’‘≤«·»≥ᴇ∞¦×⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾÷₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎'''

# === Memory models === #

class Model:
def is_tape(self):
return self.__class__.__name__ == 'Tape'

def is_stack(self):
return self.__class__.__name__ == 'Stack'

def is_grid(self):
return self.__class__.__name__ == 'Grid'

def is_register(self):
return self.__class__.__name__ == 'Register'

def is_deque(self):
return self.__class__.__name__ == 'Deque'

class Stack(Model):
def __init__(self, *values):
self.stack = list(values)

def push(self, *values):
for value in values:
self.stack.append(value)

def pop(self, index = -1):
return self.stack.pop(index)

def peek(self, index = -1):
return self.stack[index % len(self.stack)]

def __repr__(self):
return 'Stack()'.format(self.stack)

def __str__(self):
return str(self.stack)

def __iter__(self):
return iter(self.stack)

def __getitem__(self, index):
return self.stack[index]

def __setitem__(self, index, value):
self.stack[index] = value

class Tape(Model):
def __init__(self, size = inf, wrap = True):
self.size = size
self.wrap = wrap
self.tape = [0]
self.index = 0
if size != inf:
self.tape = [0] * size

def __repr__(self):
if self.size == inf:
return 'Tape([ … ])'

final = 'Tape(['
for i, val in enumerate(self.tape):
val = str(val)
if i == self.index:
final += '' + val + ''
else:
final += val
final += ' '
final += '])'
return final

def __str__(self):
if self.size == inf:
if len(self.tape):
template = '[ … … ]'
final = ''
for i, val in enumerate(self.tape):
val = str(val)
if i == self.index:
final += '' + val + ''
else:
final += val
final += ' '
return template.format(final[:-1])

else:
return '[ … ]'
else:
final = '['
for i, val in enumerate(self.tape):
val = str(val)
if i == self.index:
final += '' + val + ''
else:
final += val
final += ' '
final = final.strip() + ']'
return final

def __iter__(self):
return iter(self.tape)

@property
def cell(self):
return self.tape[self.index]

def write_cell(self, value):
self.tape[self.index] = value

def move_right(self):
self.index += 1
if self.size != inf:
if self.wrap:
self.index %= self.size
else:
while self.index >= len(self.tape):
self.tape.append(0)

def move_left(self):
self.index -= 1
if self.size != inf:
if self.wrap and self.index < 0:
self.index = self.size - 1
elif not self.wrap and self.index < 0:
raise IndexError

else:
if self.index < 0:
self.tape.insert(0, 0)
self.index = 0

class Grid(Model):
def __init__(self, xlen = inf, ylen = inf, xwrap = True, ywrap = True):
self.xlen = xlen
self.ylen = ylen
self.xwrap = xwrap
self.ywrap = ywrap
self.xindex = 0
self.yindex = 0
self.grid =
self.pointer_value = 0

if xlen != inf:
if ylen != inf:
for _ in range(ylen):
self.grid.append([0] * xlen)
else:
self.grid.append([0] * xlen)
else:
if ylen != inf:
for _ in range(ylen):
self.grid.append([0])
else:
for _ in range(10):
self.grid.append([0] * 10)

def __repr__(self):
final = ''

if self.xlen == inf:
if self.ylen == inf:
final = '[ … …n ⋮ ⋮ ]'
else:
final = ''
for line in self.grid:
final += ' ' + str(line)
.replace(',', '')
.replace('[', '')
.replace(']', '') + ' …n'
final = '[' + final[1:-1] + ']'
else:
if self.ylen == inf:
final = '[' + ('⋮ ' * int(len(self.grid[0]) * 4/5)) + 'n'
for line in self.grid:
final += ' ' + str(line)
.replace(',', '')
.replace('[', '')
.replace(']', '') + 'n'
final += ' ' + ('⋮ ' * int(len(self.grid[0]) * 4/5)) + ']'
else:
final = ''
for line in self.grid:
final += ' ' + str(line)
.replace(',', '')
.replace('[', '')
.replace(']', '') + 'n'
final = '[' + final[1:-1] + ']'

return final

def __str__(self):
array = list(map(lambda a: list(map(str, a)), self.grid))
max_row_length = max(map(len, array))

for index, row in enumerate(array):
while len(row) < max_row_length:
row.append('0')
array[index] = row

pad = max(max(map(len, a)) for a in array) + 1
final = ''
for row in array:
for element in row:
element = element.rjust(pad)
final += element
final += 'n'
return final + '<> <> '.format(self.xindex, self.yindex)

def __iter__(self):
return iter(strip(flatten(self.grid), 0))

@property
def cell(self):
return self.grid[self.yindex][self.xindex]

def write_cell(self, value):
self.grid[self.yindex][self.xindex] = value

def write_pointer(self):
self.pointer_value = self.cell

def move_right(self):
self.xindex += 1
if self.xlen == inf:
self.grid[self.yindex].append(0)
else:
if self.xwrap:
self.xindex %= self.xlen

def move_left(self):
self.xindex -= 1
if self.xlen == inf and self.xindex < 0:
self.grid[self.yindex].insert(0, 0)
self.xindex = 0
else:
if self.xindex < 0 and self.xwrap:
self.xindex = self.xlen - abs(self.xindex)
elif self.xindex < 0 and not self.xwrap:
self.xindex = self.xlen + 1

def move_down(self):
self.yindex += 1
if self.ylen == inf:
if self.xlen != inf:
self.grid.append([0] * self.xlen)
else:
self.grid.append([0])
else:
if self.ywrap:
self.yindex %= self.ylen

def move_up(self):
self.yindex -= 1
if self.ylen == inf and self.yindex < 0:
self.grid[self.xindex].insert(0, 0)
self.yindex = 0
else:
if self.yindex < 0 and self.ywrap:
self.yindex = self.ylen - abs(self.yindex)
elif self.yindex < 0 and not self.ywrap:
self.yindex = self.ylen + 1

class Register(Model, int):
def __init__(self, value = None):
self.value = value

def __repr__(self):
return 'Register()'.format(self.value)

def __str__(self):
return str(self.value)

class Deque(Model, collections.deque):
def __repr__(self):
rep = super().__repr__()[5:]
return 'Deque' + rep

def __str__(self):
return super().__repr__()[6:-1]

push = collections.deque.append

def current_value(model):
if model.is_tape() or model.is_grid():
return model.cell
if model.is_stack() or model.is_deque():
return model.pop()
if model.is_register():
return model.value
return False

def identity(value):
return value

def overload(stack_cmd = identity, tape_cmd = identity, grid_cmd = identity,
register_cmd = identity, deque_cmd = identity, all_cmd = None):

def inner(model):
if all_cmd is not None:
return all_cmd(model)

if model.is_stack():
return stack_cmd(model)

if model.is_tape():
return tape_cmd(model)

if model.is_grid():
return grid_cmd(model)

if model.is_register():
return register_cmd(model)

return deque_cmd(model)

return inner

def make_nilad(value, model):
value = eval(value)

if model.is_tape() or model.is_grid():
model.write_cell(value)

if model.is_stack():
model.push(value)

if model.is_deque():
model.append(value)

if model.is_register():
model.value = value

# === Functions === #

# = Stack Commands = #

def add(stack):
stack.push(stack.pop() + stack.pop())

def subtract(stack):
stack.push(stack.pop() - stack.pop())

def multiply(stack):
stack.push(stack.pop() * stack.pop())

def divide(stack):
stack.push(stack.pop() / stack.pop())

def modulo(stack):
stack.push(stack.pop() % stack.pop())

def swap(array):
array[-2], array[-1] = array[-1], array[-2]

def reverse(stack):
if hasattr(stack.peek(), '__iter__'):
stack.push(stack.pop()[::-1])
else:
stack.stack = stack.stack[::-1]

def product(array):
total = 1
for element in array:
total *= element
return total

def flatten(array):
flat =
if type(array) == list:
for item in array:
flat += flatten(item)
else:
flat.append(array)
return flat

def strip(array, trim):
final =
for value in array:
if value != trim or final:
final.append(value)

array = final[::-1]
final =

for value in array:
if value != trim or final:
final.append(value)

return final[::-1]

def rotate(stack):
a = stack.pop()
b = stack.pop()
c = stack.pop()
stack.push(a, c, b)

# = Tape / Grid Commands = #

def increment_cell(tape_grid):
tape_grid.write_cell(tape_grid.cell + 1)

def decrement_cell(tape_grid):
tape_grid.write_cell(tape_grid.cell - 1)

def write_grid_pointer(grid):
grid.pointer_value

def getchar():
ret = sys.stdin.read(1)
if ret:
return ret
return chr(0)

def getinput():
try:
inputted = input()
except:
return ''
try:
return eval(inputted)
except:
return inputted

# === Operators === #

def while_loop(code):
def while_inner(model, used):
while current_value(model):
model = interpret(code, model, used)
return while_inner

def if_statement(code):
def if_inner(model, used):
else_, if_ = list(map(lambda a: a[::-1], code[::-1].split('', 1)))
if current_value(model):
model = interpret(if_, model, used)
else:
model = interpret(else_, model, used)
return if_inner

# === Command lookups === #

memories =

'#': (0, Tape),
'$': (0, Stack),
'G': (0, Grid),
'D': (0, Deque),
'S': (1, Register),

'À': (2, lambda size, wrap: Tape(size, wrap)),
'Á': (1, lambda size: Tape(size, wrap = True)),
'Â': (1, lambda size: Tape(size, wrap = False)),
'Ä': (1, lambda wrap: Tape(wrap = wrap)),

'Ç´': (4, lambda xlen, ylen, xwrap, ywrap: Grid(xlen, ylen, xwrap, ywrap)),



functions =

'+': overload(
stack_cmd = add,
deque_cmd = add,
tape_cmd = increment_cell,
grid_cmd = increment_cell,
),

'-': overload(
stack_cmd = subtract,
deque_cmd = subtract,
tape_cmd = decrement_cell,
grid_cmd = decrement_cell,
),

'%': overload(
stack_cmd = modulo,
deque_cmd = modulo,
),

':': overload(
stack_cmd = lambda stack: stack.push(stack.peek()),
deque_cmd = lambda deque: deque.append(deque[-1]),
),

';': overload(
stack_cmd = lambda stack: stack.pop(),
deque_cmd = lambda deque: deque.pop(),
tape_cmd = lambda tape: tape.write_cell(0),
grid_cmd = lambda grid: grid.write_cell(0),
),

'<': overload(
tape_cmd = Tape.move_left,
grid_cmd = Grid.move_left,
),

'=': overload(
stack_cmd = lambda stack: stack.push(stack.pop() == stack.pop()),
grid_cmd = Grid.move_down,
),

'>': overload(
tape_cmd = Tape.move_right,
grid_cmd = Grid.move_right,
),

'?': overload(
stack_cmd = lambda stack: stack.push(getinput()),
deque_cmd = lambda deque: deque.append(getinput()),
tape_cmd = lambda tape: tape.write_cell(ord(getchar())),
grid_cmd = lambda grid: grid.write_cell(ord(getchar())),
),

'O': overload(
all_cmd = lambda model: print(end = str(current_value(model))),
),

'Z': overload(
stack_cmd = lambda stack: stack.push(stack.pop() > 0),
),

'R': overload(
stack_cmd = reverse,
),

'^': overload(
grid_cmd = Grid.move_up,
),

'h': overload(
all_cmd = print,
),

'o': overload(
all_cmd = lambda model: print(end = chr(current_value(model))),
),

'r': overload(
stack_cmd = rotate,
deque_cmd = lambda deque: deque.rotate(deque.pop()),
),

's': overload(
stack_cmd = swap,
deque_cmd = swap,
),

'Π': overload(
stack_cmd = lambda stack: stack.push(product(stack)),
tape_cmd = lambda tape: tape.write_cell(product(tape)),
grid_cmd = lambda grid: grid.write_cell(product(flatten(list(grid)))),
),

'Σ': overload(
stack_cmd = lambda stack: stack.push(sum(stack)),
tape_cmd = lambda tape: tape.write_cell(sum(tape)),
grid_cmd = lambda grid: grid.write_cell(sum(flatten(list(grid)))),
),

'×': overload(
stack_cmd = multiply,
grid_cmd = lambda grid: grid.write_cell(grid.pointer_value),
),

'÷': overload(
stack_cmd = divide,
grid_cmd = Grid.write_pointer,
),



operators = {

'[': while_loop,
'': if_statement,



def decode(bytestring):
decoded = ''
continue_byte = False

for index, byte in enumerate(bytestring):
if continue_byte:
continue_byte = False
continue

if byte == 0xff:
continue_byte = True
byte += bytestring[index + 1] + 1

try:
decoded += code_page[byte]
except:
raise UnicodeDecodeError('Unknown byte value: '.format(byte))

return decoded

def next_index(string, start, char):
index = start
depth = 1
while depth and index < len(string) - 1:
index += 1
if string[index] in operators.keys():
depth += 1
if string[index] == char:
depth -= 1
return index

def parse(code):
tokens =
index = 0
comment = False

while index < len(code):
char = code[index]

if char == '◊':
comment = not comment

if comment:
index += 1
continue

if char.isdigit() or char == '_':
tokens.append(char)
index += 1
if index < len(code):
char = code[index]
while char.isdigit() and index < len(code):
tokens[-1] += char
index += 1
char = code[index]
tokens[-1] = tokens[-1].replace('_', '-')

if char in operators.keys():
ret = next_index(code, index, ']')
tokens.append(operators[char](code[index + 1 : ret]))
index = ret
else:
tokens.append(char)

index += 1
return tokens

def interpret(code, memory = None, used = None):
tokens = parse(code)
operable = (memory is not None)
if used is None:
used =

for tkn in tokens:

if callable(tkn) and operable:
tkn(memory, used)

elif tkn in '₀₁₂₃₄₅₆₇₈₉' and operable:
tkn = int(tkn.translate(digits))
memory = used[tkn]

elif tkn.isdigit() or (tkn[0] in '-' and tkn[1:].isdigit()):
make_nilad(tkn, memory)

elif tkn in memories.keys():
arity, mem_type = memories[tkn]

memory = mem_type(*[current_value(memory) for _ in range(arity)])
used.append(memory)

operable = True

elif tkn == '@' and operable:
used.append(memory)
memory = used.pop(-2)

elif tkn in functions.keys() and operable:
functions[tkn](memory)

else:
continue

return memory

if __name__ == '__main__':

argparser = argparse.ArgumentParser()

a = 'store_true'

getcode = argparser.add_mutually_exclusive_group()
getcode.add_argument('-f', '--file', help = 'Specifies that code be read from a file', action = a)
getcode.add_argument('-c', '--cmd', '--cmdline', help = 'Specifies that code be read from the command line', action = a)

argparser.add_argument('-u', '--unicode', help = 'Use utf-8 encoding for files', action = a)

argparser.add_argument('program')
argparser.add_argument('argv', nargs = '*')

settings = argparser.parse_args()

if settings.file:
with open(settings.program, mode = 'rb') as file:
contents = file.read()

if settings.unicode:
contents = ''.join([char for char in contents.decode('utf-8') if char in code_page])
else:
contents = decode(contents)

if settings.cmd:
contents = bytes(settings.program, 'utf-8')

if settings.unicode:
contents = ''.join([char for char in contents.decode('utf-8') if char in code_page])
else:
contents = decode(contents)

interpret(contents)


Try it online!



For those interested, the GitHub repository contains 18 examples, all responses to the challenges listed in the question linked in the second paragraph









share|improve this question












share|improve this question




share|improve this question








edited Apr 5 at 16:39









200_success

123k14142399




123k14142399









asked Apr 5 at 8:08









caird coinheringaahing

24529




24529











  • If I understand correctly, at least one of file or cmd CLI argument is required. Am I right ? I am experiencing issue trying to run a simple example from your Github project.
    – Josay
    Apr 5 at 9:09











  • @Josay Correct. In order to read and run a file, you must provide the --file/-f flag followed by the filename, and the same with the --cmdline/-c flag for CLI
    – caird coinheringaahing
    Apr 5 at 9:15
















  • If I understand correctly, at least one of file or cmd CLI argument is required. Am I right ? I am experiencing issue trying to run a simple example from your Github project.
    – Josay
    Apr 5 at 9:09











  • @Josay Correct. In order to read and run a file, you must provide the --file/-f flag followed by the filename, and the same with the --cmdline/-c flag for CLI
    – caird coinheringaahing
    Apr 5 at 9:15















If I understand correctly, at least one of file or cmd CLI argument is required. Am I right ? I am experiencing issue trying to run a simple example from your Github project.
– Josay
Apr 5 at 9:09





If I understand correctly, at least one of file or cmd CLI argument is required. Am I right ? I am experiencing issue trying to run a simple example from your Github project.
– Josay
Apr 5 at 9:09













@Josay Correct. In order to read and run a file, you must provide the --file/-f flag followed by the filename, and the same with the --cmdline/-c flag for CLI
– caird coinheringaahing
Apr 5 at 9:15




@Josay Correct. In order to read and run a file, you must provide the --file/-f flag followed by the filename, and the same with the --cmdline/-c flag for CLI
– caird coinheringaahing
Apr 5 at 9:15










2 Answers
2






active

oldest

votes

















up vote
6
down vote













Object programming



Y'ouve split your code into various classes. Yet, it seems like the way you use them could be improved.



Indeed, the various "is_SOMETHING" method are very suspicious, both in the fact that it exists and the way it is implemented.



The implementation relies on the name of the class. This is fragile but also wrong if you actually plan to use inheritance. You could have an instance of a subclass of "Deque": its class would not be called "Deque" but it would actually be a Deque.



Thus, isinstance is what you should be using.
For instance:



def is_tape(self):
return isinstance(self, Tape)

def is_stack(self):
return isinstance(self, Stack)

def is_grid(self):
return isinstance(self, Grid)

def is_register(self):
return isinstance(self, Register)

def is_deque(self):
return isinstance(self, Deque)


This is better but as I said previously, even having these methods in a first place seems to be against the basic principle of object programming.



You should not need to care about the type of an object, the behavior of the object itself should be defined properly. For instance, instead of having a current_value function checking the model class, you could have a abstract method, overriden in each subclass to have the behavior you need. This applies to make_nilad.



This needs quite a lot of changes, at this stage, the code looks like http://termbin.com/q8jc .



Then, the same principle applies to overload but it is trickier - work in progress in http://termbin.com/7x7cd .



Index out of bound error



I had a feeling that the boundary check in ithe while char.isdigit()... loop was weird and indeed, giving an input ending with 2 (or more) digits, en end up going too far in the input and throwing an exception.



I guess a better (but far from perfect) solution would be:



def parse(code):
tokens =
index = 0
comment = False

while index < len(code):
char = code[index]
if char == '◊':
comment = not comment
elif not comment:
if char in operators.keys():
ret = next_index(code, index, ']')
token = operators[char](code[index + 1 : ret])
index = ret
elif char.isdigit() or char == '_':
#######################################
token = char.replace('_', '-')
index += 1
while index < len(code):
char = code[index]
if not char.isdigit():
break
token += char
index += 1
#######################################
else:
token = char
tokens.append(token)

index += 1
return tokens





share|improve this answer






























    up vote
    5
    down vote













    There's a lot of code here, so I'm going to go through it very sketchily. If you need a point explaining in more detail, ask.



    1. There's no documentation. What is the specification of the language being interpreted? The idea of the challenge to extend and modify the language and its interpreter. The documentation will need to be extended and modified at the same time. How is this going to be done?


    2. There are no docstrings. How are people supposed to modify this code if there's no specification for what the classes mean and the methods do?


    3. PEP8 recommends UPPER_CASE for module-level constants.


    4. Instead of digits, use unicodedata.normalize.


    5. Python concatenates adjacent string constants, so you can create code_page without the need for +=.


    6. code_page[32:128] looks as if it is supposed to be the same as the corresponding code points in ASCII so why not make use of that fact and avoid the risk of typos?


    7. There ought to be an encode function to correspond to decode otherwise how are people going to write their programs? Better still, make it a codec so that it can be passed to open.


    8. What is the purpose of the Model class? It doesn't provide any common behaviour.



    9. The design doesn't make use of object orientation. Consider a function like:



      def current_value(model):
      if model.is_tape() or model.is_grid():
      return model.cell
      if model.is_stack() or model.is_deque():
      return model.pop()
      if model.is_register():
      return model.value
      return False


      In an object-oriented program this function would be a method on the Model class and its subclasses. In the Model class this would be an abstract implementation:



      @abstractmethod
      @property
      def current_value(self):
      """Docstring here."""


      and then the various subclasses would override this method with concrete implementations.



    10. It seems wrong for current_value to modify the model in some cases (stack and deque) but not in others (tape, grid and register).


    11. The Stack model seems to be a fairly thin wrapper around a list, so it could be implemented more simply by inheriting from list (as the Deque model inherits from collections.deque).


    12. The Register model inherits from int but does not make use of this fact. Instead it uses a superfluous attribute value.


    13. What is a "nilad"? Google says that it is like a monad, but taking no arguments. This does not seem to correspond to anything in the make_nilad function. Could you choose a better name?


    14. Commands that operate on a Stack object (add, subtract etc.) ought to be methods on the Stack class. Similarly for tape and grid commands.


    15. The arguments to the overload function all end with _cmd; this shared suffix is unnecessary.


    16. I think it would be clearer if the Model subclasses did their own command dispatch, instead of doing this via the functions mapping and the overload function.


    17. Input is delivered to the model via getinput and getchar, which read from standard input. This makes it difficult to write unit tests because you have to make a file or a pipe in order to deliver the input for the test case. It would be better to take input in some other way, for example from an buffer object passed to interpret.


    18. There are lots of opportunities for η-conversion. For example lambda size, wrap: Tape(size, wrap) could be just Tape and lambda xlen, ylen, xwrap, ywrap: Grid(xlen, ylen, xwrap, ywrap) could be just Grid.


    19. It seems strange that an if expression starts with but ends with ]. I would have expected it to end with .


    20. next_index goes wrong if ] appears in a comment. (Maybe ] is not allowed to appear in a comment but if so I think this would need to be checked in parse.)


    21. while_loop re-parses the body of the loop each time round the loop. Is this deliberate? This seems like a waste of time compared to the normal approach of parsing just once.


    22. I recommend splitting up the tasks of tokenization, parsing and interpretation, instead of mixing them as here. (See these answers.)


    23. There are no unit tests.






    share|improve this answer























    • I got the term nilad from this golfing language I use. However, when I googled "nilad", all the results were relating to a plant. What did you search?
      – caird coinheringaahing
      Apr 6 at 17:57










    • Also, what do you mean by η-conversion? I'm not familiar with the term
      – caird coinheringaahing
      Apr 6 at 18:12










    • η-conversion is a term from the λ-calculus — it refers to the operation that transforms $λx.yx$ into $y$.
      – Gareth Rees
      Apr 6 at 18:33











    Your Answer




    StackExchange.ifUsing("editor", function ()
    return StackExchange.using("mathjaxEditing", function ()
    StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
    StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
    );
    );
    , "mathjax-editing");

    StackExchange.ifUsing("editor", function ()
    StackExchange.using("externalEditor", function ()
    StackExchange.using("snippets", function ()
    StackExchange.snippets.init();
    );
    );
    , "code-snippets");

    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "196"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    convertImagesToLinks: false,
    noModals: false,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );








     

    draft saved


    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f191311%2fstarting-point-for-a-collaboratively-developed-golfing-language-interpreted-in-p%23new-answer', 'question_page');

    );

    Post as a guest






























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    6
    down vote













    Object programming



    Y'ouve split your code into various classes. Yet, it seems like the way you use them could be improved.



    Indeed, the various "is_SOMETHING" method are very suspicious, both in the fact that it exists and the way it is implemented.



    The implementation relies on the name of the class. This is fragile but also wrong if you actually plan to use inheritance. You could have an instance of a subclass of "Deque": its class would not be called "Deque" but it would actually be a Deque.



    Thus, isinstance is what you should be using.
    For instance:



    def is_tape(self):
    return isinstance(self, Tape)

    def is_stack(self):
    return isinstance(self, Stack)

    def is_grid(self):
    return isinstance(self, Grid)

    def is_register(self):
    return isinstance(self, Register)

    def is_deque(self):
    return isinstance(self, Deque)


    This is better but as I said previously, even having these methods in a first place seems to be against the basic principle of object programming.



    You should not need to care about the type of an object, the behavior of the object itself should be defined properly. For instance, instead of having a current_value function checking the model class, you could have a abstract method, overriden in each subclass to have the behavior you need. This applies to make_nilad.



    This needs quite a lot of changes, at this stage, the code looks like http://termbin.com/q8jc .



    Then, the same principle applies to overload but it is trickier - work in progress in http://termbin.com/7x7cd .



    Index out of bound error



    I had a feeling that the boundary check in ithe while char.isdigit()... loop was weird and indeed, giving an input ending with 2 (or more) digits, en end up going too far in the input and throwing an exception.



    I guess a better (but far from perfect) solution would be:



    def parse(code):
    tokens =
    index = 0
    comment = False

    while index < len(code):
    char = code[index]
    if char == '◊':
    comment = not comment
    elif not comment:
    if char in operators.keys():
    ret = next_index(code, index, ']')
    token = operators[char](code[index + 1 : ret])
    index = ret
    elif char.isdigit() or char == '_':
    #######################################
    token = char.replace('_', '-')
    index += 1
    while index < len(code):
    char = code[index]
    if not char.isdigit():
    break
    token += char
    index += 1
    #######################################
    else:
    token = char
    tokens.append(token)

    index += 1
    return tokens





    share|improve this answer



























      up vote
      6
      down vote













      Object programming



      Y'ouve split your code into various classes. Yet, it seems like the way you use them could be improved.



      Indeed, the various "is_SOMETHING" method are very suspicious, both in the fact that it exists and the way it is implemented.



      The implementation relies on the name of the class. This is fragile but also wrong if you actually plan to use inheritance. You could have an instance of a subclass of "Deque": its class would not be called "Deque" but it would actually be a Deque.



      Thus, isinstance is what you should be using.
      For instance:



      def is_tape(self):
      return isinstance(self, Tape)

      def is_stack(self):
      return isinstance(self, Stack)

      def is_grid(self):
      return isinstance(self, Grid)

      def is_register(self):
      return isinstance(self, Register)

      def is_deque(self):
      return isinstance(self, Deque)


      This is better but as I said previously, even having these methods in a first place seems to be against the basic principle of object programming.



      You should not need to care about the type of an object, the behavior of the object itself should be defined properly. For instance, instead of having a current_value function checking the model class, you could have a abstract method, overriden in each subclass to have the behavior you need. This applies to make_nilad.



      This needs quite a lot of changes, at this stage, the code looks like http://termbin.com/q8jc .



      Then, the same principle applies to overload but it is trickier - work in progress in http://termbin.com/7x7cd .



      Index out of bound error



      I had a feeling that the boundary check in ithe while char.isdigit()... loop was weird and indeed, giving an input ending with 2 (or more) digits, en end up going too far in the input and throwing an exception.



      I guess a better (but far from perfect) solution would be:



      def parse(code):
      tokens =
      index = 0
      comment = False

      while index < len(code):
      char = code[index]
      if char == '◊':
      comment = not comment
      elif not comment:
      if char in operators.keys():
      ret = next_index(code, index, ']')
      token = operators[char](code[index + 1 : ret])
      index = ret
      elif char.isdigit() or char == '_':
      #######################################
      token = char.replace('_', '-')
      index += 1
      while index < len(code):
      char = code[index]
      if not char.isdigit():
      break
      token += char
      index += 1
      #######################################
      else:
      token = char
      tokens.append(token)

      index += 1
      return tokens





      share|improve this answer

























        up vote
        6
        down vote










        up vote
        6
        down vote









        Object programming



        Y'ouve split your code into various classes. Yet, it seems like the way you use them could be improved.



        Indeed, the various "is_SOMETHING" method are very suspicious, both in the fact that it exists and the way it is implemented.



        The implementation relies on the name of the class. This is fragile but also wrong if you actually plan to use inheritance. You could have an instance of a subclass of "Deque": its class would not be called "Deque" but it would actually be a Deque.



        Thus, isinstance is what you should be using.
        For instance:



        def is_tape(self):
        return isinstance(self, Tape)

        def is_stack(self):
        return isinstance(self, Stack)

        def is_grid(self):
        return isinstance(self, Grid)

        def is_register(self):
        return isinstance(self, Register)

        def is_deque(self):
        return isinstance(self, Deque)


        This is better but as I said previously, even having these methods in a first place seems to be against the basic principle of object programming.



        You should not need to care about the type of an object, the behavior of the object itself should be defined properly. For instance, instead of having a current_value function checking the model class, you could have a abstract method, overriden in each subclass to have the behavior you need. This applies to make_nilad.



        This needs quite a lot of changes, at this stage, the code looks like http://termbin.com/q8jc .



        Then, the same principle applies to overload but it is trickier - work in progress in http://termbin.com/7x7cd .



        Index out of bound error



        I had a feeling that the boundary check in ithe while char.isdigit()... loop was weird and indeed, giving an input ending with 2 (or more) digits, en end up going too far in the input and throwing an exception.



        I guess a better (but far from perfect) solution would be:



        def parse(code):
        tokens =
        index = 0
        comment = False

        while index < len(code):
        char = code[index]
        if char == '◊':
        comment = not comment
        elif not comment:
        if char in operators.keys():
        ret = next_index(code, index, ']')
        token = operators[char](code[index + 1 : ret])
        index = ret
        elif char.isdigit() or char == '_':
        #######################################
        token = char.replace('_', '-')
        index += 1
        while index < len(code):
        char = code[index]
        if not char.isdigit():
        break
        token += char
        index += 1
        #######################################
        else:
        token = char
        tokens.append(token)

        index += 1
        return tokens





        share|improve this answer















        Object programming



        Y'ouve split your code into various classes. Yet, it seems like the way you use them could be improved.



        Indeed, the various "is_SOMETHING" method are very suspicious, both in the fact that it exists and the way it is implemented.



        The implementation relies on the name of the class. This is fragile but also wrong if you actually plan to use inheritance. You could have an instance of a subclass of "Deque": its class would not be called "Deque" but it would actually be a Deque.



        Thus, isinstance is what you should be using.
        For instance:



        def is_tape(self):
        return isinstance(self, Tape)

        def is_stack(self):
        return isinstance(self, Stack)

        def is_grid(self):
        return isinstance(self, Grid)

        def is_register(self):
        return isinstance(self, Register)

        def is_deque(self):
        return isinstance(self, Deque)


        This is better but as I said previously, even having these methods in a first place seems to be against the basic principle of object programming.



        You should not need to care about the type of an object, the behavior of the object itself should be defined properly. For instance, instead of having a current_value function checking the model class, you could have a abstract method, overriden in each subclass to have the behavior you need. This applies to make_nilad.



        This needs quite a lot of changes, at this stage, the code looks like http://termbin.com/q8jc .



        Then, the same principle applies to overload but it is trickier - work in progress in http://termbin.com/7x7cd .



        Index out of bound error



        I had a feeling that the boundary check in ithe while char.isdigit()... loop was weird and indeed, giving an input ending with 2 (or more) digits, en end up going too far in the input and throwing an exception.



        I guess a better (but far from perfect) solution would be:



        def parse(code):
        tokens =
        index = 0
        comment = False

        while index < len(code):
        char = code[index]
        if char == '◊':
        comment = not comment
        elif not comment:
        if char in operators.keys():
        ret = next_index(code, index, ']')
        token = operators[char](code[index + 1 : ret])
        index = ret
        elif char.isdigit() or char == '_':
        #######################################
        token = char.replace('_', '-')
        index += 1
        while index < len(code):
        char = code[index]
        if not char.isdigit():
        break
        token += char
        index += 1
        #######################################
        else:
        token = char
        tokens.append(token)

        index += 1
        return tokens






        share|improve this answer















        share|improve this answer



        share|improve this answer








        edited Apr 6 at 14:24


























        answered Apr 5 at 11:47









        Josay

        23.8k13580




        23.8k13580






















            up vote
            5
            down vote













            There's a lot of code here, so I'm going to go through it very sketchily. If you need a point explaining in more detail, ask.



            1. There's no documentation. What is the specification of the language being interpreted? The idea of the challenge to extend and modify the language and its interpreter. The documentation will need to be extended and modified at the same time. How is this going to be done?


            2. There are no docstrings. How are people supposed to modify this code if there's no specification for what the classes mean and the methods do?


            3. PEP8 recommends UPPER_CASE for module-level constants.


            4. Instead of digits, use unicodedata.normalize.


            5. Python concatenates adjacent string constants, so you can create code_page without the need for +=.


            6. code_page[32:128] looks as if it is supposed to be the same as the corresponding code points in ASCII so why not make use of that fact and avoid the risk of typos?


            7. There ought to be an encode function to correspond to decode otherwise how are people going to write their programs? Better still, make it a codec so that it can be passed to open.


            8. What is the purpose of the Model class? It doesn't provide any common behaviour.



            9. The design doesn't make use of object orientation. Consider a function like:



              def current_value(model):
              if model.is_tape() or model.is_grid():
              return model.cell
              if model.is_stack() or model.is_deque():
              return model.pop()
              if model.is_register():
              return model.value
              return False


              In an object-oriented program this function would be a method on the Model class and its subclasses. In the Model class this would be an abstract implementation:



              @abstractmethod
              @property
              def current_value(self):
              """Docstring here."""


              and then the various subclasses would override this method with concrete implementations.



            10. It seems wrong for current_value to modify the model in some cases (stack and deque) but not in others (tape, grid and register).


            11. The Stack model seems to be a fairly thin wrapper around a list, so it could be implemented more simply by inheriting from list (as the Deque model inherits from collections.deque).


            12. The Register model inherits from int but does not make use of this fact. Instead it uses a superfluous attribute value.


            13. What is a "nilad"? Google says that it is like a monad, but taking no arguments. This does not seem to correspond to anything in the make_nilad function. Could you choose a better name?


            14. Commands that operate on a Stack object (add, subtract etc.) ought to be methods on the Stack class. Similarly for tape and grid commands.


            15. The arguments to the overload function all end with _cmd; this shared suffix is unnecessary.


            16. I think it would be clearer if the Model subclasses did their own command dispatch, instead of doing this via the functions mapping and the overload function.


            17. Input is delivered to the model via getinput and getchar, which read from standard input. This makes it difficult to write unit tests because you have to make a file or a pipe in order to deliver the input for the test case. It would be better to take input in some other way, for example from an buffer object passed to interpret.


            18. There are lots of opportunities for η-conversion. For example lambda size, wrap: Tape(size, wrap) could be just Tape and lambda xlen, ylen, xwrap, ywrap: Grid(xlen, ylen, xwrap, ywrap) could be just Grid.


            19. It seems strange that an if expression starts with but ends with ]. I would have expected it to end with .


            20. next_index goes wrong if ] appears in a comment. (Maybe ] is not allowed to appear in a comment but if so I think this would need to be checked in parse.)


            21. while_loop re-parses the body of the loop each time round the loop. Is this deliberate? This seems like a waste of time compared to the normal approach of parsing just once.


            22. I recommend splitting up the tasks of tokenization, parsing and interpretation, instead of mixing them as here. (See these answers.)


            23. There are no unit tests.






            share|improve this answer























            • I got the term nilad from this golfing language I use. However, when I googled "nilad", all the results were relating to a plant. What did you search?
              – caird coinheringaahing
              Apr 6 at 17:57










            • Also, what do you mean by η-conversion? I'm not familiar with the term
              – caird coinheringaahing
              Apr 6 at 18:12










            • η-conversion is a term from the λ-calculus — it refers to the operation that transforms $λx.yx$ into $y$.
              – Gareth Rees
              Apr 6 at 18:33















            up vote
            5
            down vote













            There's a lot of code here, so I'm going to go through it very sketchily. If you need a point explaining in more detail, ask.



            1. There's no documentation. What is the specification of the language being interpreted? The idea of the challenge to extend and modify the language and its interpreter. The documentation will need to be extended and modified at the same time. How is this going to be done?


            2. There are no docstrings. How are people supposed to modify this code if there's no specification for what the classes mean and the methods do?


            3. PEP8 recommends UPPER_CASE for module-level constants.


            4. Instead of digits, use unicodedata.normalize.


            5. Python concatenates adjacent string constants, so you can create code_page without the need for +=.


            6. code_page[32:128] looks as if it is supposed to be the same as the corresponding code points in ASCII so why not make use of that fact and avoid the risk of typos?


            7. There ought to be an encode function to correspond to decode otherwise how are people going to write their programs? Better still, make it a codec so that it can be passed to open.


            8. What is the purpose of the Model class? It doesn't provide any common behaviour.



            9. The design doesn't make use of object orientation. Consider a function like:



              def current_value(model):
              if model.is_tape() or model.is_grid():
              return model.cell
              if model.is_stack() or model.is_deque():
              return model.pop()
              if model.is_register():
              return model.value
              return False


              In an object-oriented program this function would be a method on the Model class and its subclasses. In the Model class this would be an abstract implementation:



              @abstractmethod
              @property
              def current_value(self):
              """Docstring here."""


              and then the various subclasses would override this method with concrete implementations.



            10. It seems wrong for current_value to modify the model in some cases (stack and deque) but not in others (tape, grid and register).


            11. The Stack model seems to be a fairly thin wrapper around a list, so it could be implemented more simply by inheriting from list (as the Deque model inherits from collections.deque).


            12. The Register model inherits from int but does not make use of this fact. Instead it uses a superfluous attribute value.


            13. What is a "nilad"? Google says that it is like a monad, but taking no arguments. This does not seem to correspond to anything in the make_nilad function. Could you choose a better name?


            14. Commands that operate on a Stack object (add, subtract etc.) ought to be methods on the Stack class. Similarly for tape and grid commands.


            15. The arguments to the overload function all end with _cmd; this shared suffix is unnecessary.


            16. I think it would be clearer if the Model subclasses did their own command dispatch, instead of doing this via the functions mapping and the overload function.


            17. Input is delivered to the model via getinput and getchar, which read from standard input. This makes it difficult to write unit tests because you have to make a file or a pipe in order to deliver the input for the test case. It would be better to take input in some other way, for example from an buffer object passed to interpret.


            18. There are lots of opportunities for η-conversion. For example lambda size, wrap: Tape(size, wrap) could be just Tape and lambda xlen, ylen, xwrap, ywrap: Grid(xlen, ylen, xwrap, ywrap) could be just Grid.


            19. It seems strange that an if expression starts with but ends with ]. I would have expected it to end with .


            20. next_index goes wrong if ] appears in a comment. (Maybe ] is not allowed to appear in a comment but if so I think this would need to be checked in parse.)


            21. while_loop re-parses the body of the loop each time round the loop. Is this deliberate? This seems like a waste of time compared to the normal approach of parsing just once.


            22. I recommend splitting up the tasks of tokenization, parsing and interpretation, instead of mixing them as here. (See these answers.)


            23. There are no unit tests.






            share|improve this answer























            • I got the term nilad from this golfing language I use. However, when I googled "nilad", all the results were relating to a plant. What did you search?
              – caird coinheringaahing
              Apr 6 at 17:57










            • Also, what do you mean by η-conversion? I'm not familiar with the term
              – caird coinheringaahing
              Apr 6 at 18:12










            • η-conversion is a term from the λ-calculus — it refers to the operation that transforms $λx.yx$ into $y$.
              – Gareth Rees
              Apr 6 at 18:33













            up vote
            5
            down vote










            up vote
            5
            down vote









            There's a lot of code here, so I'm going to go through it very sketchily. If you need a point explaining in more detail, ask.



            1. There's no documentation. What is the specification of the language being interpreted? The idea of the challenge to extend and modify the language and its interpreter. The documentation will need to be extended and modified at the same time. How is this going to be done?


            2. There are no docstrings. How are people supposed to modify this code if there's no specification for what the classes mean and the methods do?


            3. PEP8 recommends UPPER_CASE for module-level constants.


            4. Instead of digits, use unicodedata.normalize.


            5. Python concatenates adjacent string constants, so you can create code_page without the need for +=.


            6. code_page[32:128] looks as if it is supposed to be the same as the corresponding code points in ASCII so why not make use of that fact and avoid the risk of typos?


            7. There ought to be an encode function to correspond to decode otherwise how are people going to write their programs? Better still, make it a codec so that it can be passed to open.


            8. What is the purpose of the Model class? It doesn't provide any common behaviour.



            9. The design doesn't make use of object orientation. Consider a function like:



              def current_value(model):
              if model.is_tape() or model.is_grid():
              return model.cell
              if model.is_stack() or model.is_deque():
              return model.pop()
              if model.is_register():
              return model.value
              return False


              In an object-oriented program this function would be a method on the Model class and its subclasses. In the Model class this would be an abstract implementation:



              @abstractmethod
              @property
              def current_value(self):
              """Docstring here."""


              and then the various subclasses would override this method with concrete implementations.



            10. It seems wrong for current_value to modify the model in some cases (stack and deque) but not in others (tape, grid and register).


            11. The Stack model seems to be a fairly thin wrapper around a list, so it could be implemented more simply by inheriting from list (as the Deque model inherits from collections.deque).


            12. The Register model inherits from int but does not make use of this fact. Instead it uses a superfluous attribute value.


            13. What is a "nilad"? Google says that it is like a monad, but taking no arguments. This does not seem to correspond to anything in the make_nilad function. Could you choose a better name?


            14. Commands that operate on a Stack object (add, subtract etc.) ought to be methods on the Stack class. Similarly for tape and grid commands.


            15. The arguments to the overload function all end with _cmd; this shared suffix is unnecessary.


            16. I think it would be clearer if the Model subclasses did their own command dispatch, instead of doing this via the functions mapping and the overload function.


            17. Input is delivered to the model via getinput and getchar, which read from standard input. This makes it difficult to write unit tests because you have to make a file or a pipe in order to deliver the input for the test case. It would be better to take input in some other way, for example from an buffer object passed to interpret.


            18. There are lots of opportunities for η-conversion. For example lambda size, wrap: Tape(size, wrap) could be just Tape and lambda xlen, ylen, xwrap, ywrap: Grid(xlen, ylen, xwrap, ywrap) could be just Grid.


            19. It seems strange that an if expression starts with but ends with ]. I would have expected it to end with .


            20. next_index goes wrong if ] appears in a comment. (Maybe ] is not allowed to appear in a comment but if so I think this would need to be checked in parse.)


            21. while_loop re-parses the body of the loop each time round the loop. Is this deliberate? This seems like a waste of time compared to the normal approach of parsing just once.


            22. I recommend splitting up the tasks of tokenization, parsing and interpretation, instead of mixing them as here. (See these answers.)


            23. There are no unit tests.






            share|improve this answer















            There's a lot of code here, so I'm going to go through it very sketchily. If you need a point explaining in more detail, ask.



            1. There's no documentation. What is the specification of the language being interpreted? The idea of the challenge to extend and modify the language and its interpreter. The documentation will need to be extended and modified at the same time. How is this going to be done?


            2. There are no docstrings. How are people supposed to modify this code if there's no specification for what the classes mean and the methods do?


            3. PEP8 recommends UPPER_CASE for module-level constants.


            4. Instead of digits, use unicodedata.normalize.


            5. Python concatenates adjacent string constants, so you can create code_page without the need for +=.


            6. code_page[32:128] looks as if it is supposed to be the same as the corresponding code points in ASCII so why not make use of that fact and avoid the risk of typos?


            7. There ought to be an encode function to correspond to decode otherwise how are people going to write their programs? Better still, make it a codec so that it can be passed to open.


            8. What is the purpose of the Model class? It doesn't provide any common behaviour.



            9. The design doesn't make use of object orientation. Consider a function like:



              def current_value(model):
              if model.is_tape() or model.is_grid():
              return model.cell
              if model.is_stack() or model.is_deque():
              return model.pop()
              if model.is_register():
              return model.value
              return False


              In an object-oriented program this function would be a method on the Model class and its subclasses. In the Model class this would be an abstract implementation:



              @abstractmethod
              @property
              def current_value(self):
              """Docstring here."""


              and then the various subclasses would override this method with concrete implementations.



            10. It seems wrong for current_value to modify the model in some cases (stack and deque) but not in others (tape, grid and register).


            11. The Stack model seems to be a fairly thin wrapper around a list, so it could be implemented more simply by inheriting from list (as the Deque model inherits from collections.deque).


            12. The Register model inherits from int but does not make use of this fact. Instead it uses a superfluous attribute value.


            13. What is a "nilad"? Google says that it is like a monad, but taking no arguments. This does not seem to correspond to anything in the make_nilad function. Could you choose a better name?


            14. Commands that operate on a Stack object (add, subtract etc.) ought to be methods on the Stack class. Similarly for tape and grid commands.


            15. The arguments to the overload function all end with _cmd; this shared suffix is unnecessary.


            16. I think it would be clearer if the Model subclasses did their own command dispatch, instead of doing this via the functions mapping and the overload function.


            17. Input is delivered to the model via getinput and getchar, which read from standard input. This makes it difficult to write unit tests because you have to make a file or a pipe in order to deliver the input for the test case. It would be better to take input in some other way, for example from an buffer object passed to interpret.


            18. There are lots of opportunities for η-conversion. For example lambda size, wrap: Tape(size, wrap) could be just Tape and lambda xlen, ylen, xwrap, ywrap: Grid(xlen, ylen, xwrap, ywrap) could be just Grid.


            19. It seems strange that an if expression starts with but ends with ]. I would have expected it to end with .


            20. next_index goes wrong if ] appears in a comment. (Maybe ] is not allowed to appear in a comment but if so I think this would need to be checked in parse.)


            21. while_loop re-parses the body of the loop each time round the loop. Is this deliberate? This seems like a waste of time compared to the normal approach of parsing just once.


            22. I recommend splitting up the tasks of tokenization, parsing and interpretation, instead of mixing them as here. (See these answers.)


            23. There are no unit tests.







            share|improve this answer















            share|improve this answer



            share|improve this answer








            edited Apr 6 at 14:06


























            answered Apr 6 at 14:00









            Gareth Rees

            41.1k394166




            41.1k394166











            • I got the term nilad from this golfing language I use. However, when I googled "nilad", all the results were relating to a plant. What did you search?
              – caird coinheringaahing
              Apr 6 at 17:57










            • Also, what do you mean by η-conversion? I'm not familiar with the term
              – caird coinheringaahing
              Apr 6 at 18:12










            • η-conversion is a term from the λ-calculus — it refers to the operation that transforms $λx.yx$ into $y$.
              – Gareth Rees
              Apr 6 at 18:33

















            • I got the term nilad from this golfing language I use. However, when I googled "nilad", all the results were relating to a plant. What did you search?
              – caird coinheringaahing
              Apr 6 at 17:57










            • Also, what do you mean by η-conversion? I'm not familiar with the term
              – caird coinheringaahing
              Apr 6 at 18:12










            • η-conversion is a term from the λ-calculus — it refers to the operation that transforms $λx.yx$ into $y$.
              – Gareth Rees
              Apr 6 at 18:33
















            I got the term nilad from this golfing language I use. However, when I googled "nilad", all the results were relating to a plant. What did you search?
            – caird coinheringaahing
            Apr 6 at 17:57




            I got the term nilad from this golfing language I use. However, when I googled "nilad", all the results were relating to a plant. What did you search?
            – caird coinheringaahing
            Apr 6 at 17:57












            Also, what do you mean by η-conversion? I'm not familiar with the term
            – caird coinheringaahing
            Apr 6 at 18:12




            Also, what do you mean by η-conversion? I'm not familiar with the term
            – caird coinheringaahing
            Apr 6 at 18:12












            η-conversion is a term from the λ-calculus — it refers to the operation that transforms $λx.yx$ into $y$.
            – Gareth Rees
            Apr 6 at 18:33





            η-conversion is a term from the λ-calculus — it refers to the operation that transforms $λx.yx$ into $y$.
            – Gareth Rees
            Apr 6 at 18:33













             

            draft saved


            draft discarded


























             


            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f191311%2fstarting-point-for-a-collaboratively-developed-golfing-language-interpreted-in-p%23new-answer', 'question_page');

            );

            Post as a guest













































































            Popular posts from this blog

            Chat program with C++ and SFML

            Function to Return a JSON Like Objects Using VBA Collections and Arrays

            Will my employers contract hold up in court?