Отправной точкой для совместно разработанная языку гольф интерпретируется в Python


На гольф-кода, мы решили, для начала, написать читабельный код! Однако, мы, очевидно, не очень хорош в этом, поэтому нам нужна ваша помощь, чтобы сделать его еще лучше.

Я предложил этот вызов на PPCG, который требует переводчика для нового гольф языке, написанная в Python 3.4. Я закончила базовый файл, но, как это будет сотрудничал с многими пользователями, мне нужно убедиться, что они читаемы, понятны и, ну, не играл.

Интерпретатор BASIC входит набор различных моделей памяти, а не ограничить их, например, лентой (а-ля на Brainfuck), или стек, аналогично ><>, а также включает в основе команды, необходимые для выполнения задач в связанной проблемой.

К сожалению, это редкие, когда дело доходит до комментариев, и в дополнение к информации о том, как код написан, я бы высоко ценим обратную связь и предложения по уровню комментирования необходима, начиная от я могу едва понять, что это делаешь, заливаем все комментарии , чтобы перец немного на протяжении, рядом с более сложными деталями. Код выглядит следующим образом:

import argparse
import collections
import sys

# === Constants === #

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

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

code_page  = '''ÀÁÂÄÆÃÅĀĄ\t\nĆČÇĎÐÈÉÊËĒĖĚĘÌÍÎÏĪĮĹĽ'''
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)

Попробуйте его онлайн!

Для тех, кто заинтересован в GitHub репозиторий содержит 18 примеров, ответов на вызовы, перечисленных в вопрос, связан во втором пункте



Комментарии
2 ответа

Объектное Программирование

Г'ouve разделить ваш код в различных классах. Но, похоже, пути их использования могут быть улучшены.

Действительно, различные "is_что-то" способ очень подозрительны, как в том, что она существует и каким образом она будет реализована.

Реализация опирается на имя класса. Это хрупкая, но и не так, если вы действительно планируете использовать наследование. Вы могли бы иметь экземпляр подкласса "Дека": его класс не назвали бы "Дека", но это будет на самом деле быть в начале очереди.

Таким образом, isinstance Это то, что вы должны использовать.
Например:

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)

Это лучше, но как я уже сказал ранее, даже имея эти методы в первую очередь, кажется, против основной принцип объектно-ориентированного подхода.

Вам не нужно заботиться о типе объекта, поведение объекта должны быть определены правильно. Например, вместо того, current_value функция проверки модели класса, вы можете иметь абстрактный метод переопределен в каждом подклассе, чтобы иметь поведение вам нужно. Это касается make_nilad.

Для этого потребуется достаточно много изменений, на данном этапе, код выглядит следующим образом http://termbin.com/q8jc .

Затем, тот же принцип применяется к overload но это сложнее - работа в http://termbin.com/7x7cd .

Индекс выходит за границы погрешности

У меня было ощущение, что граница проверить в этоон while char.isdigit()... цикл был странный и действительно, давая входной конец с 2 (или больше) цифр, собственная итоге заходит слишком далеко во входном и исключения.

Я думаю, лучше (но далеко не идеальное) решение:

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

6
ответ дан 5 апреля 2018 в 11:04 Источник Поделиться

Там много кода, так что я собираюсь пройти через это очень эскизно. Если вам нужно объяснять более подробно, спрашивайте.


  1. Нет никакой документации. Что спецификация языка интерпретируется? Идея бросить вызов, чтобы расширить и изменить язык и его интерпретатор. В документации должны быть расширены и изменены в то же время. Как это будет сделано?

  2. Нет комментарии. Как люди могут изменить этот код, если нет спецификации, что классы и методы?

  3. PEP8 рекомендует UPPER_CASE для модуля на уровне констант.

  4. Вместо digitsиспользуйте unicodedata.normalize.

  5. Питон объединяет соседние строковые константы, так что вы можете создать code_page без необходимости +=.

  6. code_page[32:128] выглядит так, как будто она должна быть такой же, как соответствующего кода в ASCII так почему бы не использовать этот факт и избежать опасности ошибок?

  7. Там должно быть encode функция соответствует decode иначе как люди будут писать свои программы? Лучше все-таки сделать это кодек, так что он может быть передан open.

  8. Какова цель Model класса? Это не дает никакой общей поведения.

  9. Конструкция не использовать объектную ориентацию. Рассмотрим функцию как:

    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

    В объектно-ориентированной программе эта функция будет метод Model класса и его подклассов. В Model класс это будет абстрактная реализация:

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

    а затем различные подклассы могут переопределить этот метод с конкретными реализациями.


  10. Это кажется неправильным для current_value чтобы изменить модель в некоторых случаях (стек и очередь), но не в других (ленты, сетки и зарегистрировать).

  11. В Stack модель, кажется, довольно тонкая обертка вокруг списка, поэтому он может быть реализован более простым путем наследования list (как Deque модель наследует от collections.deque).

  12. В Register модель наследует от int но не использовать этот факт. Вместо этого он использует лишним атрибутом value.

  13. Что такое "nilad"? Google говорит, что это как монада, но не принимая никаких аргументов. Похоже, что это не соответствует ничему в make_nilad функция. Вы не могли бы выбрать лучшее название?

  14. Команды, которые работают на Stack объект (add, subtract и т. д.) должны быть методы Stack класс. Аналогично для ленты и сетки команд.

  15. Аргументы overload функции все закончиться _cmd; этот общий суффикс ненужно.

  16. Я думаю, что было бы понятнее, если бы Model подклассы сделали свою собственную команду отправки, а не делать это через functions сопоставление и overload функция.

  17. Вход доставляется в модель через getinput и getchar, которые читаются из стандартного ввода. Это усложняет написание модульных тестов, потому что вы должны сделать файл или канал для того, чтобы доставить материалы для тестового случая. Было бы лучше сделать вход в какой-то другой путь, например, из буфера объект, переданный interpret.

  18. Есть много возможностей для η-конверсии. Например lambda size, wrap: Tape(size, wrap) может быть просто Tape и lambda xlen, ylen, xwrap, ywrap: Grid(xlen, ylen, xwrap, ywrap) может быть просто Grid.

  19. Это кажется странным, что если выражение начинается с { но заканчивается ]. Я ожидал бы, чтобы это закончилось }.

  20. next_index пойдет не так, если ] появляется в комментарии. (Возможно ] не разрешается появляться в комментариях, но если это так я думаю, что это должны быть проверены в parse.)

  21. while_loop повторно разбирает теле цикла каждый раз вокруг петли. Это умышленное? Это выглядит как пустая трата времени по сравнению с нормальным подходом парсинга только один раз.

  22. Я рекомендую разделить задачи лексического анализа, синтаксического анализа и интерпретации, вместо того, чтобы смешивать их здесь. (См. эти ответы.)

  23. Нет модульных тестов.

5
ответ дан 6 апреля 2018 в 02:04 Источник Поделиться