маленький язык в python

#python #parsing #programming-languages #operators #postfix-notation

#python #синтаксический анализ #программирование-языки #операторы #постфиксная нотация

Вопрос:

Я пишу то, что на python даже нельзя назвать языком. В настоящее время у меня есть несколько операторов: , - , * , ^ fac , @ !! fac . @ вычисляет факториал, !! возвращает значение переменной, ,, устанавливает переменную. Код приведен ниже. Как бы мне написать способ определения функций на этом простом языке?

РЕДАКТИРОВАТЬ: я обновил код!

 import sys, shlex, readline, os, string
List, assign, call, add, sub, div, Pow, mul, mod, fac, duf, read,
kill, clr, STO, RET, fib, curs = {}, "set", "get", " ", "-", "/", "^", "*",
"%", "fact", "func", "read", "kill", "clear", ">", "@", "fib", "vars"
def fact(num):
    if num == 1: return 1
    else: return num*fact(num-1)
def Simp(op, num2, num1):
    global List
    try: num1, num2 = float(num1), float(num2)
    except:
        try: num1 = float(num1)
        except:
            try: num2 = float(num2)
            except: pass
    if op == mul: return num1*num2
    elif op == div: return num1/num2
    elif op == sub: return num1-num2
    elif op == add: return num1 num2
    elif op == Pow: return num1**num2
    elif op == assign: List[num1] = num2; return "ok"
    elif op == call: return List[num1]
    elif op == fac: return fact(num1)
    elif op == duf: return "%s %s %s"%(duf, num1, num2)
    elif op == mod: return num1%num2
    elif op == kill: del List[num1]; return "ok"
    elif op == clr: os.system("clear")
    elif op == STO: List[num2] = num1; return "ok"
    elif op == RET: return List[num1]
    elif op == curs: return List
    elif op == read: List[num1] = Eval(raw_input("%s "%num1)); return "ok"
def Eval(expr):
    ops = "%s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s"%(mul, add, sub, div, Pow, assign, call, fac, duf, mod, read, kill, clr, STO, RET, curs)
    stack, expr, ops = [], shlex.split(string.lower(expr)), ops.split()
    for i in expr:
        if i[0] != ';':
            if i not in ops: stack.append(i)
            elif i in ops: stack.append(Simp(i, stack.pop(), stack.pop()))
        else: stack.append("ok")
    return stack[0]
def shell():
    try:
        x = ""
        while x != "quit":
            x = raw_input("star>   ")
            try: l = Eval(x)
            except KeyError: l = "does not exist"
            except: l = "parse error!"
            if l != None: print "   =>",l,"n"
    except (EOFError, KeyboardInterrupt): print
if len(sys.argv) > 1:
    x = open(sys.argv[1], 'r'); l = x.readlines(); x.close()
    for i in l:
        if i[0] != ";":
            i = ' '.join(i.split())
            x = Eval(i)
            if x != None: print i,"n   =>",x,"n"
        else: pass
    shell()
else: shell()
  

Комментарии:

1. Почему бы вам не использовать надлежащий модуль синтаксического анализа вместо того, чтобы пытаться изобретать колеса самостоятельно (в очень плохом смысле)?

2. @tekknolagi pyparsing является стандартным модулем для этого уровня синтаксического анализа

3. не мог бы кто-нибудь, пожалуйста, помочь с моим актуальным вопросом, а не предложениями?

4. @tekknolagi: У каждого языка есть своя культура. Существуют стандарты и способы написания выражений, которые считаются соответствующими возможностям языка. Ваша программа выглядит… странно. Это не похоже на другие программы на Python. Он не следует тем же идиомам и невысказанным правилам, которым следует большинство других программ на Python.

5. @tekknolagi: Я доберусь до этого. Но я хотел объяснить вам, почему вы получили такой теплый ответ и плохие ответы. Мое искушение — полностью переписать ваш код на идиоматический Python.

Ответ №1:

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

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

Во-первых, определение вашей Simp функции действительно нарушено. Это требует, чтобы все извлекало ровно два значения из стека и помещало ровно одно значение обратно. Это не работает. Факториальная функция не работает таким образом, как и функция Фибоначчи, поэтому вы вынуждены использовать «фиктивный» аргумент, который никогда не используется. Кроме того, такие вещи, как присвоение элементу вашего глобального списка или словаря, не имеют причин помещать значения в стек, поэтому вам остается нажать «ок». Это неисправно и требует исправления.

Вот версия с исправленной этой проблемой. Обратите внимание, что я переименовал Simp в builtin_op , чтобы более точно отразить его назначение:

 import sys, shlex, readline, os, string
List, assign, call, add, sub, div, Pow, mul, mod, fac, duf, read,
kill, clr, STO, RET, fib, curs = {}, "set", "get", " ", "-", "/", "^", "*",
"%", "fact", "func", "read", "kill", "clear", ">", "@", "fib", "vars"
def fact(num):
    if num == 1: return 1
    else: return num*fact(num-1)
def builtin_op(op, stack):
    global List
    if op == mul: stack.append(float(stack.pop())*float(stack.pop()))
    elif op == div: stack.append(float(stack.pop())/float(stack.pop()))
    elif op == sub: stack.append(float(stack.pop())-float(stack.pop()))
    elif op == add: stack.append(float(stack.pop()) float(stack.pop()))
    elif op == Pow: stack.append(float(stack.pop())**float(stack.pop()))
    elif op == assign: val = List[stack.pop()] = stack.pop(); stack.append(val)
    elif op == call: stack.append(List[stack.pop()])
    elif op == fac: stack.append(fact(stack.pop()))
    elif op == duf: stack.append("%s %s %s" % (duf, stack.pop(), stack.pop()))
    elif op == mod: stack.append(float(stack.pop())%float(stack.pop()))
    elif op == kill: del List[stack.pop()]
    elif op == clr: os.system("clear")
    elif op == STO: val = List[stack.pop()] = stack.pop(); stack.append(val)
    elif op == RET: stack.append(List[stack.pop()])
    elif op == curs: stack.append(List)
    elif op == read: prompt = stack.pop(); List[prompt] = Eval(raw_input("%s "%prompt)); stack.append(List[prompt])
def Eval(expr):
    ops = "%s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s"%(mul, add, sub, div, Pow, assign, call, fac, duf, mod, read, kill, clr, STO, RET, curs)
    stack, expr, ops = [], shlex.split(string.lower(expr)), ops.split()
    for i in expr:
        if i[0] != ';':
            if i not in ops: stack.append(i)
            elif i in ops: builtin_op(i, stack)
        else: stack.append("ok")
    return stack[0]
def shell():
    try:
        x = ""
        while x != "quit":
            x = raw_input("star>   ")
            try: l = Eval(x)
            except KeyError: l = "does not exist"
            except: l = "parse error!"
            if l != None: print "   =>",l,"n"
    except (EOFError, KeyboardInterrupt): print
if len(sys.argv) > 1:
    x = open(sys.argv[1], 'r'); l = x.readlines(); x.close()
    for i in l:
        if i[0] != ";":
            i = ' '.join(i.split())
            x = Eval(i)
            if x != None: print i,"n   =>",x,"n"
        else: pass
    shell()
else: shell()
  

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

Определение функций — интересная проблема. На вашем языке оценка выполняется немедленно. У вас нет механизма для отсрочки вычисления на более поздний срок. Но вы используете shlex модуль для синтаксического анализа. И у него есть способ сказать, что целая группа символов (включая пробелы и тому подобное) является частью одной сущности. Это дает нам быстрый и простой способ реализации функций. Вы можете сделать что-то вроде этого:

 star>   "3  " add3 func
  

чтобы создать свою функцию, и:

 star>   2 add3 get
  

чтобы вызвать его. Я использовал, get потому что это то, что вы назначили call в своей программе.

Единственная проблема заключается в том, что для работы функции потребуется доступ к текущему состоянию стека. Вы можете легко вернуть строку для функции обратно в Eval , но Eval при каждом ее вызове всегда создается совершенно новый стек. Для реализации функций это необходимо исправить. Итак, я добавил в stack функцию Eval аргумент по умолчанию. Если этот аргумент оставить со значением по умолчанию, Eval все равно будет создан новый стек, как и раньше. Но если будет передан существующий стек, Eval вместо него будет использоваться он.

Вот измененный код:

 import sys, shlex, readline, os, string
List, assign, call, add, sub, div, Pow, mul, mod, fac, duf, read,
kill, clr, STO, RET, fib, curs = {}, "set", "get", " ", "-", "/", "^", "*",
"%", "fact", "func", "read", "kill", "clear", ">", "@", "fib", "vars"
funcdict = {}
def fact(num):
    if num == 1: return 1
    else: return num*fact(num-1)
def builtin_op(op, stack):
    global List
    global funcdict
    if op == mul: stack.append(float(stack.pop())*float(stack.pop()))
    elif op == div: stack.append(float(stack.pop())/float(stack.pop()))
    elif op == sub: stack.append(float(stack.pop())-float(stack.pop()))
    elif op == add: stack.append(float(stack.pop()) float(stack.pop()))
    elif op == Pow: stack.append(float(stack.pop())**float(stack.pop()))
    elif op == assign: val = List[stack.pop()] = stack.pop(); stack.append(val)
    elif op == call: Eval(funcdict[stack.pop()], stack)
    elif op == fac: stack.append(fact(stack.pop()))
    elif op == duf: name = stack.pop(); funcdict[name] = stack.pop(); stack.append(name)
    elif op == mod: stack.append(float(stack.pop())%float(stack.pop()))
    elif op == kill: del List[stack.pop()]
    elif op == clr: os.system("clear")
    elif op == STO: val = List[stack.pop()] = stack.pop(); stack.append(val)
    elif op == RET: stack.append(List[stack.pop()])
    elif op == curs: stack.append(List)
    elif op == read: prompt = stack.pop(); List[prompt] = Eval(raw_input("%s "%prompt)); stack.append(List[prompt])
def Eval(expr, stack=None):
    ops = "%s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s"%(mul, add, sub, div, Pow, assign, call, fac, duf, mod, read, kill, clr, STO, RET, curs)
    if stack is None:
        stack = []
    expr, ops = shlex.split(string.lower(expr)), ops.split()
    for i in expr:
        if i[0] != ';':
            if i not in ops: stack.append(i)
            elif i in ops: builtin_op(i, stack)
        else: stack.append("ok")
    return stack[0]
def shell():
    try:
        x = ""
        while x != "quit":
            x = raw_input("star>   ")
            try: l = Eval(x)
            except KeyError: l = "does not exist"
            except: l = "parse error!"
            if l != None: print "   =>",l,"n"
    except (EOFError, KeyboardInterrupt): print
if len(sys.argv) > 1:
    x = open(sys.argv[1], 'r'); l = x.readlines(); x.close()
    for i in l:
        if i[0] != ";":
            i = ' '.join(i.split())
            x = Eval(i)
            if x != None: print i,"n   =>",x,"n"
        else: pass
    shell()
else: shell()
  

В языках, основанных на стеке, двумя очень полезными встроенными операторами являются dup и swap . dup берет верхний элемент стека и дублирует его. swap меняет местами два верхних элемента стека.

Если у вас есть, dup вы можете реализовать square функцию следующим образом:

 star>   "dup *" square func
  

Вот ваша программа с dup и swap реализованной:

 import sys, shlex, readline, os, string
List, assign, call, add, sub, div, Pow, mul, mod, fac, duf, read,
kill, clr, STO, RET, fib, curs, dup, swap = {}, "set", "get", " ", "-", "/", "^", "*",
"%", "fact", "func", "read", "kill", "clear", ">", "@", "fib", "vars", "dup", "swap"
funcdict = {}
def fact(num):
    if num == 1: return 1
    else: return num*fact(num-1)
def builtin_op(op, stack):
    global List
    global funcdict
    if op == mul: stack.append(float(stack.pop())*float(stack.pop()))
    elif op == div: stack.append(float(stack.pop())/float(stack.pop()))
    elif op == sub: stack.append(float(stack.pop())-float(stack.pop()))
    elif op == add: stack.append(float(stack.pop()) float(stack.pop()))
    elif op == Pow: stack.append(float(stack.pop())**float(stack.pop()))
    elif op == assign: val = List[stack.pop()] = stack.pop(); stack.append(val)
    elif op == call: Eval(funcdict[stack.pop()], stack)
    elif op == fac: stack.append(fact(stack.pop()))
    elif op == duf: name = stack.pop(); funcdict[name] = stack.pop(); stack.append(name)
    elif op == mod: stack.append(float(stack.pop())%float(stack.pop()))
    elif op == kill: del List[stack.pop()]
    elif op == clr: os.system("clear")
    elif op == STO: val = List[stack.pop()] = stack.pop(); stack.append(val)
    elif op == RET: stack.append(List[stack.pop()])
    elif op == curs: stack.append(List)
    elif op == dup: val = stack.pop(); stack.append(val); stack.append(val)
    elif op == swap: val1 = stack.pop(); val2 = stack.pop(); stack.append(val1); stack.append(val2)
    elif op == read: prompt = stack.pop(); List[prompt] = Eval(raw_input("%s "%prompt)); stack.append(List[prompt])
def Eval(expr, stack=None):
    ops = "%s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s"%(mul, add, sub, div, Pow, assign, call, fac, duf, mod, read, kill, clr, STO, RET, curs, dup, swap)
    if stack is None:
        stack = []
    expr, ops = shlex.split(string.lower(expr)), ops.split()
    for i in expr:
        if i[0] != ';':
            if i not in ops: stack.append(i)
            elif i in ops: builtin_op(i, stack)
        else: stack.append("ok")
    return stack[0]
def shell():
    try:
        x = ""
        while x != "quit":
            x = raw_input("star>   ")
            try: l = Eval(x)
            except KeyError: l = "does not exist"
            except: l = "parse error!"
            if l != None: print "   =>",l,"n"
    except (EOFError, KeyboardInterrupt): print
if len(sys.argv) > 1:
    x = open(sys.argv[1], 'r'); l = x.readlines(); x.close()
    for i in l:
        if i[0] != ";":
            i = ' '.join(i.split())
            x = Eval(i)
            if x != None: print i,"n   =>",x,"n"
        else: pass
    shell()
else: shell()
  

Наконец, вот моя версия на Python, которая намного понятнее (по крайней мере, на мой взгляд), чем написанный вами Python:

 import shlex, functools, sys, StringIO

def bin_numeric_op(func):
    @functools.wraps(func)
    def execute(self):
        n2, n1 = self._stack.pop(), self._stack.pop()
        n1 = float(n1)
        n2 = float(n2)
        self._stack.append(func(n1, n2))
    return execute

def relational_op(func):
    @functools.wraps(func)
    def execute(self):
        n2, n1 = self._stack.pop(), self._stack.pop()
        self._stack.append(bool(func(n1, n2)))
    return execute

def bin_bool_op(func):
    @functools.wraps(func)
    def execute(self):
        n2, n1 = self._stack.pop(), self._stack.pop()
        n1 = bool(n1)
        n2 = bool(n2)
        self._stack.append(bool(func(n1, n2)))
    return execute

class Interpreter(object):
    def __init__(self):
        self._stack = []
        self._vars = {}
        self._squarestack = []

    def processToken(self, token):
        if token == '[':
            self._squarestack.append(len(self._stack))
        # Currently inside square brackets, don't execute
        elif len(self._squarestack) > 0:
            if token == ']':
                startlist = self._squarestack.pop()
                lst = self._stack[startlist:]
                self._stack[startlist:] = [tuple(lst)]
            else:
                self._stack.append(token)
        # Not current inside list and close square token, something's wrong.
        elif token == ']':
            raise ValueError("Unmatched ']'")
        elif token in self.builtin_ops:
            self.builtin_ops[token](self)
        else:
            self._stack.append(token)
    def get_stack(self):
        return self._stack
    def get_vars(self):
        return self._vars
    @bin_numeric_op
    def add(n1, n2):
        return n1   n2
    @bin_numeric_op
    def mul(n1, n2):
        return n1 * n2
    @bin_numeric_op
    def div(n1, n2):
        return n1 / n2
    @bin_numeric_op
    def sub(n1, n2):
        return n1 - n2
    @bin_numeric_op
    def mod(n1, n2):
        return n1 % n2
    @bin_numeric_op
    def Pow(n1, n2):
        return n1**n2
    @relational_op
    def less(v1, v2):
        return v1 < v2
    @relational_op
    def lesseq(v1, v2):
        return v1 <= v2
    @relational_op
    def greater(v1, v2):
        return v1 > v2
    @relational_op
    def greatereq(v1, v2):
        return v1 > v2
    @relational_op
    def isequal(v1, v2):
        return v1 == v2
    @relational_op
    def isnotequal(v1, v2):
        return v1 != v2
    @bin_bool_op
    def bool_and(v1, v2):
        return v1 and v2
    @bin_bool_op
    def bool_or(v1, v2):
        return v1 or v2
    def bool_not(self):
        stack = self._stack
        v1 = stack.pop()
        stack.append(not v1)
    def if_func(self):
        stack = self._stack
        pred = stack.pop()
        code = stack.pop()
        if pred:
            self.run(code)
    def ifelse_func(self):
        stack = self._stack
        pred = stack.pop()
        nocode = stack.pop()
        yescode = stack.pop()
        code = yescode if pred else nocode
        self.run(code)
    def store(self):
        stack = self._stack
        value = stack.pop()
        varname = stack.pop()
        self._vars[varname] = value
    def fetch(self):
        stack = self._stack
        varname = stack.pop()
        stack.append(self._vars[varname])
    def remove(self):
        varname = self._stack.pop()
        del self._vars[varname]
    # The default argument is because this is used internally as well.
    def run(self, code=None):
        if code is None:
            code = self._stack.pop()
        for tok in code:
            self.processToken(tok)
    def dup(self):
        self._stack.append(self._stack[-1])
    def swap(self):
        self._stack[-2:] = self._stack[-1:-3:-1]
    def pop(self):
        self._stack.pop()
    def showstack(self):
        print"%r" % (self._stack,)
    def showvars(self):
        print "%r" % (self._vars,)
    builtin_ops = {
        ' ': add,
        '*': mul,
        '/': div,
        '-': sub,
        '%': mod,
        '^': Pow,
        '<': less,
        '<=': lesseq,
        '>': greater,
        '>=': greatereq,
        '==': isequal,
        '!=': isnotequal,
        'amp;amp;': bool_and,
        '||': bool_or,
        'not': bool_not,
        'if': if_func,
        'ifelse': ifelse_func,
        '!': store,
        '@': fetch,
        'del': remove,
        'call': run,
        'dup': dup,
        'swap': swap,
        'pop': pop,
        'stack': showstack,
        'vars': showvars
        }

def shell(interp):
    try:
        while True:
            x = raw_input("star>   ")
            msg = None
            try:
                interp.run(shlex.split(x))
            except KeyError:
                msg = "does not exist"
            except:
                sys.excepthook(*sys.exc_info())
                msg = "parse error!"
            if msg != None:
                print "   =>",msg,"n"
            else:
                print "   => %rn" % (interp.get_stack(),)
    except (EOFError, KeyboardInterrupt):
        print

interp = Interpreter()
if len(sys.argv) > 1:
    lex = shlex.shlex(open(sys.argv[1], 'r'), sys.argv[1])
    tok = shlex.get_token()
    while tok is not None:
        interp.processToken(tok)
        tok = lex.get_token()
shell(interp)
  

Эта новая версия поддерживает оператор if and ifelse . С помощью вызовов this и function можно реализовать функции fib и fact на языке. Я добавлю, как вы определили бы их позже.

Вот как вы могли бы определить fib функцию:

 star>   fib [ dup [ pop 1 0   ] swap [ dup 1 - fib @ call swap 2 - fib @ call   ] swap 0   2 0   < ifelse ] !
   => []

star>   15 fib @ call
   => [987.0]
  

0 2 0 Последовательность перед < заключается в том, чтобы заставить сравнение быть числовым сравнением.

Также обратите внимание, что [ и ] одиночные символы являются операторами кавычек. Они приводят к тому, что все между ними не выполняется, а вместо этого сохраняется в стеке в виде единого списка элементов. Это ключ к определению функций. Функции представляют собой последовательность токенов, которые вы можете выполнить с помощью call оператора. Они также могут использоваться для «анонимных блоков», которые представляют собой нечто среднее между lambda выражениями и стандартным блоком Python. Они используются в fib функции для двух возможных путей ifelse инструкции.

Анализатор для этого смехотворно прост. И shlex достаточно мощный как лексер для этого простого языка. Другие проекты будут извлекать отдельные элементы из списка. Создание нового списка, который состоит только из части предыдущего списка. «Перечисление» одного токена в стеке. Реализация while примитива. Числовые операторы, которые работают с целыми числами (в действительности Forth числовые операции по умолчанию работают с целыми числами, и вам нужно указать что-то вроде . , чтобы получить версию с плавающей запятой). И некоторые операции над символьными маркерами, которые позволяют манипулировать строками. Возможно, было бы достаточно операции ‘split’ и ‘join’, которая превращает токен в список отдельных токенов для символов или объединяет список вместе в один токен.

Комментарии:

1. @tekknolagi: Я добавил эту часть сейчас. Как я уже сказал, я работаю над этим в несколько этапов.

2. @tekknolagi: Вам нужно реализовать dup встроенную операцию, которая дублирует верхнюю запись стека. Тогда square может быть определен как "dup *" square func . В качестве альтернативы, вы могли бы выполнить реализацию, swap которая меняет местами два верхних элемента стека и делает это: "square:x swap > square:x @ *" square func .

3. @tekknolagi: Обновлено моей версией.

4. @tekknolagi: На этом мой ответ закончен. Для полнофункционального языка не требуется никакого сложного анализатора. В конце концов, это четвертый вариант.

5. Ага. Ура Forth. Я попробовал примеры fib, и у меня ничего не получилось. Я еще немного поиграю с этим после игры 7….. Отправляйтесь в ВАНКУВЕР!

Ответ №2:

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

Если, с другой стороны, вас интересует обучение, ознакомьтесь с алгоритмом маневровой площадки. Вероятно, вы могли бы создать словарь функций (который будет быстрее, чем ваш оператор if) с операциями в соответствии с:

 funcs = {}
funcs[" "] = lambda x, y: x   y
funcs["*"] = lambda x, y: y * y
  

Затем в вашей Simp-функции вы можете вызвать

 func = funcs.get[Op]
if func is not None:
    func[Op](num1,num2)
else:
    #whatever you want to do here
  

Комментарии:

1. Я думаю, в этом контексте алгоритм маневрового двора является лучшим ответом, чем грамматика LL ^^

2. @Monkey — Язык OPs является постфиксным языком. Это реализация Forth. На самом деле ему не нужен какой-либо заметный синтаксический анализатор.

Ответ №3:

Что вам нужно, так это преобразовать последовательность символов (числа, операции над числами, круглые скобки) в древовидную структуру, которая представляет вычисления, выраженные вашей последовательностью символов. Такая вещь в точности соответствует задаче «синтаксического анализатора», возможно, вы захотите взглянуть на простые синтаксические анализаторы, подобные этому http://en.wikipedia.org/wiki/LL_parser Они просты в программировании, и вы можете вычислить таблицы синтаксического анализа с помощью карандаша и бумаги.

Комментарии:

1. единственный вопрос, который у меня был, состоял в том, чтобы выяснить, как определять функции… я знаю, что это как-то связано с lambda

2. Не только это, но и как вы собираетесь определять аргументы и сопоставлять эти аргументы? Вам нужны функции, которые принимают только 1 аргумент, или переменные аргументы, и так далее.

3. ну, что-то вроде этого x x * square def

4. @Warren просто функции, которые принимают один из двух аргументов.

5. Я пытался написать для вас такой, который позволял бы вам писать «x x * square def» или «def square %1% 1 *», но я продвинулся лишь немного, и это стало слишком запутанным.

Ответ №4:

Похоже, вы пытаетесь написать что-то подобное Forth на python.

Комментарии:

1. совсем немного 🙂 я не уверен, что я делаю, хотя

2. ну, я попытался расширить ваш пример, и без создания дерева синтаксического анализа, то есть написания синтаксического анализатора, я застрял на ограничениях вашей простой операции со стеком. Вы находитесь намного дальше, чем в 1 лямбде от синтаксического анализатора.

Ответ №5:

У вас мог бы быть словарь, в котором можно хранить переменные и связывать их с именем функции.

Например, предположим, что вы читаете построчно свой код:

 a = 1
b = 2
c = a   b
function x() 
   d = 4
   e = 5
   f = d   e 
end
  

Когда вы определяете переменные ( a, b, c), вы сохраняете их в списке, и этот список находится в области видимости, это может быть глобальная область видимости, что-то вроде:

 variables = scopes["global"]
variables.append( "a" ) 
  

У вас могла бы быть похожая структура данных для функций, поэтому, когда вы обнаруживаете функцию, вы добавляете ее в эту структуру:

 fs = functions["global"]
fs.append("x")
  

И вы также добавляете новую «область» в словаре области

 scopes["x"] = [] // the function x doesn't have any var 
  

Когда вы находите новый var и если вы находитесь внутри определения функции, вы сохраняете этот новый var в этой «области видимости»

 variables = scopes["x"]
variables.append("d")
  

Имеет ли это смысл?

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

http://pragprog.com/titles/tpdsl/language-implementation-patterns

Несмотря на то, что он написан с использованием Java в качестве примера, он предоставит вам надежные основы языковых приложений и его очень легко читать.

Тогда у вас должны быть инструменты для :

  1. Создайте лексер (разделите входные данные на токены)
  2. Синтаксический анализ (проверьте, соответствуют ли токены правилам вашего языка)
  3. AST (для обхода вашего ввода)
  4. Определение программных символов (таких как переменные и функции)
  5. Создайте интерпретатор.

Я надеюсь, что это поможет