Реализация функций с очень простыми сценариями - PullRequest
17 голосов
/ 17 июня 2011

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

Как здесь реализовать функции?Утверждение goto слишком просто, я хотел бы попробовать что-то более сложное.(редактировать) В конце концов я хочу иметь возможность:

f0(x, y, z):=ax^by^cz

... в оболочке, которая запускает скрипт, который запускает этот модуль (глупо, а?)

# notes: separate addresses from data lest the loop of doom cometh

class Interpreter:

  def __init__(self):
    self.memory = { }
    self.dictionary = {"mov" : self.mov,
                       "put" : self.put,
                       "add" : self.add,
                       "sub" : self.sub,
                       "clr" : self.clr,
                       "cpy" : self.cpy,
                       "ref" : self.ref }
    self.hooks = {self.val("0") : self.out }

  def interpret(self, line):
    x = line.split(" ")
    vals = tuple(self.val(y) for y in x[1:])
    dereferenced = []
    keys_only = tuple(key for key in self.memory)
    for val in vals:
      while val in self.memory: val = self.memory[val]
      dereferenced.append(val)
    vals = tuple(y for y in dereferenced)
    self.dictionary[x[0]](vals)

  def val(self, x):
    return tuple(int(y) for y in str(x).split("."))

  def mov(self, value):
    self.ptr = value[0]

  def put(self, value):
    self.memory[self.ptr] = value[0]

  def clr(self, value):
    if self.ptr in self.hooks and self.ptr in self.memory:
      x = self.hooks[self.ptr]
      y = self.memory[self.ptr]
      for z in y: x(z)
    del self.memory[self.ptr]

  def add(self, values):
    self.put(self.mat(values, lambda x, y: x + y))

  def sub(self, values):
    self.put(self.mat(values, lambda x, y: x - y))

  def mat(self, values, op):
    a, b = self.memory[values[0]], self.memory[values[1]]
    if len(a) > len(b): a, b = b, a
    c = [op(a[x], b[x]) for x in xrange(len(b))] + [x for x in a[len(a):]]
    return [tuple(x for x in c)]

  def cpy(self, value):
    self.put(value)

  def out(self, x):
    print chr(x),

  def ref(self, x):
    self.put(x)

interp = Interpreter()
for x in file(__file__.split('/')[-1].split(".")[-2] + ".why"):
  interp.interpret(x.strip())

пример сценария:

mov 1
put 104.101.108.108.111.10
mov 0
ref 1
clr 0

(РЕДАКТИРОВАТЬ) Я принял решение использовать эту попытку в качестве вдохновения и начать с нуля в этом проекте.(Надеюсь, я найду немного реального времени, чтобы сесть и написать код, прежде чем занятия начнутся снова.) Я собираюсь наградить лучший ответ через несколько дней .Я надеюсь, что эта информация не сможет отговорить потенциальных участников представить что-либо, что, по их мнению, будет полезно для такого рода проблем с кодированием.

Ответы [ 4 ]

3 голосов
/ 27 июня 2011

Я изо всех сил пытаюсь понять, что вы спрашиваете.Где будет дано определение вашей функции?В обработчике сценария или в сценарии?

Если он находится в обработчике сценария, очевидным решением будет использование выражения lambda.Используя пример, который вы использовали в вопросе f0(x, y, z):=x^2, можно перевести в:

>>> f0 = lambda x, y, z : x**2
>>> f0(2,3,4)
4

Если определения функций должны быть помещены в сам скрипт, вы можете избежать сочетания lambda и eval выражения.Вот краткий пример, который я просто собрал вместе, чтобы проиллюстрировать идею.

class ScriptParser(object):

    # See 'to_python' to check out what this does
    mapping = {'^':'**', '!':' not ', '&':' and '}

    def to_python(self, calc):
        '''
        Parse the calculation syntax from the script grammar to the python one.
        This could be grown to a more complex parser, if needed. For now it will
        simply assume any operator as defined in the grammar used for the script
        has an equivalent in python.
        '''
        for k, v in self.mapping.items():
            calc = calc.replace(k, v)
        return calc

    def feed(self, lfs):
        '''
        Parse a line of the script containing a function defintion
        '''
        signature, calc = lfs.split(':=')
        funcname, variables = [s.strip() for s in signature.split('(')]
        # as we stripped the strings, it's now safe to do...'
        variables = variables[:-1]
        setattr(self, funcname,
                eval('lambda ' + variables + ' : ' + self.to_python(calc)))

def main():
    lines = ['f0(x, y, z) := x^2',
             'f1(x) := x**2 + x**3 + x*1000']
    sp = ScriptParser()
    for line in lines:
        sp.feed(line)
        print('Script definition  : %s' % line)
    for i in range(5):
        res0 = sp.f0(i, None, None)
        res1 = sp.f1(i)
        print('f0(%d) = %d' % (i, res0))
        print('f1(%d) = %d' % (i, res1))
        print('--------')

if __name__ == '__main__':
    main()

Запуск этой программы выводит:

Script definition  : f0(x, y, z) := x^2
Script definition  : f1(x) := x**2 + x**3 + x*1000
f0(0) = 0
f1(0) = 0
--------
f0(1) = 1
f1(1) = 1002
--------
f0(2) = 4
f1(2) = 2012
--------
f0(3) = 9
f1(3) = 3036
--------
f0(4) = 16
f1(4) = 4080
--------

Имейте в виду, что:

  1. Использование eval имеет последствия для безопасности, о которых вы должны знать.
  2. Написание собственного синтаксического анализатора грамматики - это действительно крутой опыт обучения !!:)

HTH, Mac.

2 голосов
/ 24 июня 2011

Не уверен, правильно ли я вас понимаю, но если ваша цель - определить функцию с помощью f0(x):=mov x и других сложных синтаксисов, то мне кажется, что большие компоненты, которые вам не хватаетсвоего рода лексический анализ и грамматический парсер.Как только вы отойдете от понятия «первый символ в строке определяет, что делает линия», тогда ваш метод line.split(" ") больше не будет достаточным.Это довольно сложные инструменты, и каждый язык, более сложный, чем ассемблер, требует этих инструментов (хотя они могут быть собраны вручную, в зависимости от языка и компилятора / интерпретатора).

Большинство анализирует свои входные данные в два основных этапа:

1) Лексический анализ - этот шаг принимает «x + 1/5» и переводит его в значимые символы, такие как «НОМЕР ОПЕРАТОРА ПЕРЕМЕННОГО ОПЕРАТОРА».Выходные данные этого шага используются в качестве входных данных для синтаксического анализатора грамматики

2) Разбор грамматики - это более сложный процесс, и существует множество теоретических представлений о лучших способах анализа грамматики.Это примет вышеприведенный ввод и проанализирует его в дереве, которое может быть оценено.Например:

Operator+ 
|     |
|     ----Variable x 
Operator/
|    |
1    5

У меня нет опыта работы с этими инструментами в Python.В C ++ единственные инструменты, которые я использовал, называются flex и bison.Я уверен, что кто-то еще использовал подобные инструменты в Python и мог указать вам на некоторые ссылки.Похоже, у этого вопроса есть кое-что: Эффективный синтаксический анализатор грамматики без контекста, предпочтительно дружественный к Python

Я попытался найти несколько учебных пособий для вас по понятиям, но ничего не получилось.По какой-то причине мои навыки поиска в Google не включаются сегодня вечером.

2 голосов
/ 17 июня 2011

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

Таким образом, вы используете стек для суммирования переменных для каждого вызова функции, и да, используйте goto, чтобы перейти к адресу функции. Затем используйте свой стек для получения адреса возврата функции и состояния переменных при вызове функции. Вот и все.

Удачи!

1 голос
/ 27 июня 2011

Попробуйте использовать pyparsing для определения грамматики.В его вики много примеров , таких как интерактивный калькулятор .

...