небольшой язык в питоне

Я пишу то, что можно даже не называть языком на питоне. У меня сейчас несколько операторов: +, -, *, ^, 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()

person tekknolagi    schedule 14.06.2011    source источник
comment
Почему бы вам не использовать надлежащий модуль синтаксического анализа вместо того, чтобы пытаться изобретать велосипед самостоятельно (в очень плохом смысле)?   -  person Andreas Jung    schedule 14.06.2011
comment
Я просто пытаюсь учиться и хочу сделать это с нуля. О каких модулях парсинга вы говорите?   -  person tekknolagi    schedule 14.06.2011
comment
@tekknolagi pyparsing — стандартный модуль для этого уровня синтаксического анализа.   -  person Kathy Van Stone    schedule 14.06.2011
comment
Если вы действительно хотите учиться, вы можете обратиться к Книге Дракона (en .wikipedia.org/wiki/) для материалов низкого уровня и, возможно, новую книгу по предметно-ориентированному языку Мартина Фаулера.   -  person Kathy Van Stone    schedule 14.06.2011
comment
может кто-нибудь помочь с моим фактическим вопросом, а не предложениями?   -  person tekknolagi    schedule 14.06.2011
comment
@tekknolagi: Вы пишете на очень неидиоматическом Python.   -  person Omnifarious    schedule 14.06.2011
comment
что такое неидиоматический питон? даже так, могу ли я помочь?   -  person tekknolagi    schedule 14.06.2011
comment
@tekknolagi: У каждого языка есть своя культура. Существуют стандарты и способы написания выражений, которые считаются соответствующими возможностям языка. Ваша программа выглядит... странно. Это не похоже на другие программы Python. Он не следует тем же идиомам и негласным правилам, которым следует большинство других программ Python.   -  person Omnifarious    schedule 14.06.2011
comment
@tekknolagi: Да, это так. Эти правила обычно возникают из-за желания сделать программы, написанные на языке, более понятными. Иногда они настолько ясны, что их может увидеть любой. Иногда они просто яснее, потому что делать что-то не так, как все остальные, делает это так, что другие люди, которые являются частью культуры и привыкли к тому, что люди делают что-то стандартным способом, находят вашу программу трудной для понимания. Изучение языка — это не только изучение идиом и культуры, но и изучение явных синтаксических и семантических правил языка.   -  person Omnifarious    schedule 14.06.2011
comment
Могу ли я получить помощь в добавлении пользовательских функций?   -  person tekknolagi    schedule 14.06.2011
comment
@tekknolagi: я займусь этим. Но я хотел объяснить вам, почему вы получили такой вялый отклик и плохие ответы. Мое искушение состоит в том, чтобы полностью переписать ваш код, сделав его идиоматическим Python.   -  person Omnifarious    schedule 14.06.2011
comment
если хочешь, давай! но если вы перепишете его, можете ли вы включить функции как в мою версию, так и в идиоматическую версию?   -  person tekknolagi    schedule 14.06.2011
comment
@tekknolagi Ниже я предлагаю идею, посмотрите, имеет ли она смысл.   -  person OscarRyz    schedule 15.06.2011


Ответы (5)


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

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

Во-первых, определение вашей функции 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,
        '&&': 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 "   => %r\n" % (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 и ifelse. С помощью this и вызовов функций можно реализовать функции 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. Числовые операторы, работающие с целыми числами (в реальном Форте числовые операции по умолчанию работают с целыми числами, и вам нужно указать что-то вроде +., чтобы получить версию с плавающей запятой). И некоторые операции с токенами символов, которые позволяют манипулировать строками. Возможно, было бы достаточно операции «разделить» и «объединить», которая превращает токен в список отдельных токенов для персонажей или объединяет список вместе в один токен.

person Omnifarious    schedule 14.06.2011
comment
вау, это отличный ответ - а как насчет пользовательских функций? - person tekknolagi; 15.06.2011
comment
@tekknolagi: я добавил эту часть сейчас. Как я уже сказал, я работаю над этим в несколько этапов. - person Omnifarious; 15.06.2011
comment
вау... как насчет таких вещей, как x x * square func, которые определяют квадрат как x x *? - person tekknolagi; 15.06.2011
comment
@tekknolagi: вам нужно реализовать встроенную операцию dup, которая дублирует верхнюю запись стека. Тогда square можно определить как "dup *" square func. В качестве альтернативы вы можете реализовать swap, который меняет местами два верхних элемента стека, и сделать это: "square:x swap > square:x @ *" square func. - person Omnifarious; 15.06.2011
comment
@tekknolagi: Обновлено с моей переделкой. - person Omnifarious; 15.06.2011
comment
@tekknolagi: На этом мой ответ закончен. Для полнофункционального языка не требуется сложный парсер. В конце концов, это вариант Форта. - person Omnifarious; 16.06.2011
comment
Ага. Ура Форту. Я пробовал выдумывать примеры и не работал для меня. Я поиграю с ним еще после седьмой игры... Вперед, ВАНКУВЕР! - person Warren P; 16.06.2011
comment
@Warren P: я протестировал и отладил эту функцию, так что она должна работать. - person Omnifarious; 16.06.2011
comment
@Omnifarious Как мне написать один для возведения чисел в квадрат? - person tekknolagi; 16.06.2011
comment
@tekknolagi: вздох Это очень просто. Введите это: square [ dup * ] !. После того, как вы это сделаете, это будет работать 5 square @ call. В первой строке говорится: «Определить квадрат как последовательность фишек dup *». Вторая строка говорит: «Вспомнить определение квадрата и выполнить его с 5 в стеке». Вторая строка в точности эквивалентна фразе 5 dup *. - person Omnifarious; 16.06.2011
comment
@tekknolagi: языковой интерпретатор теперь завершен по Тьюрингу. Любая программа, которую вы могли написать на любом языке, теперь может быть написана на языке, понятном интерпретатору. Это может быть не очень легко или удобно сделать, и это может занять целую вечность, но это можно сделать. В любом случае, если вы чувствуете, что на ваш вопрос ответили, не стесняйтесь принять мой ответ, установив флажок. :-) - person Omnifarious; 16.06.2011
comment
аааа, это прозвучит очень глупо: почему бы и нет, если b 5 ! почему не работает c b @ call 3 * !? - person tekknolagi; 16.06.2011
comment
@tekknolagi: c b @ 3 * ! сработает. call требуется только тогда, когда у вас есть последовательность токенов, которые необходимо выполнить. b 5 ! просто сохранил единственный токен «5» в «b». Если бы вы сделали b [ 5 ] !, ваш код работал бы, потому что [ 5 ] — это последовательность токенов. b 5 ! это как сказать b = 5 в Python. И b [ 5 ] ! это как сказать b = lambda : 5 на Python. В первом случае вы просто скажете b, чтобы получить значение, а в другом вы должны вызвать его как функцию b(). - person Omnifarious; 16.06.2011
comment
Вот это да. Вау вау вау. Вы действительно основательный человек. Я благодарю вас за все это! Могу ли я опубликовать этот язык на github и отдать вам должное? - person tekknolagi; 16.06.2011
comment
@tekknolagi: Конечно. :-) Хотя у меня есть код в репозитории Mercurial. :-) Вот репозиторий Mercurial: hg.omnifarious.org/~hopper/forthlike - person Omnifarious; 16.06.2011
comment
@tekknolagi: Вы можете клонировать этот проект на gitib: github.com/Omnifarious/forthlike - person Omnifarious; 16.06.2011

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

Если, с другой стороны, вас интересует обучение, ознакомьтесь с алгоритмом маневровой станции. Возможно, вы могли бы создать словарь функций (который будет быстрее, чем оператор 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
person Brett Stottlemyer    schedule 14.06.2011
comment
В этом контексте алгоритм Shunting Yard является лучшим ответом, чем грамматика LL, я думаю ^^ - person Monkey; 15.06.2011
comment
@Monkey - язык OP - это постфиксный язык. Это реализация Форта. Ему действительно не нужен парсер какой-либо заметки. - person Omnifarious; 15.06.2011

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

person Monkey    schedule 14.06.2011
comment
единственный вопрос, который у меня был, состоял в том, чтобы выяснить, как определить функции... я знаю, что это как-то связано с lambda - person tekknolagi; 14.06.2011
comment
Не только это, но как вы собираетесь определять аргументы и отображать эти аргументы? Вам нужны функции, которые принимают только 1 аргумент, или переменные аргументы и так далее. - person Warren P; 14.06.2011
comment
ну как-то так x x * square def - person tekknolagi; 14.06.2011
comment
@Warren просто функции, которые принимают один из двух аргументов. - person tekknolagi; 14.06.2011
comment
Я пытался написать вам один, который позволил бы вам написать x x * квадратное определение или квадратное определение %1 %1 *, но я только немного продвинулся, и это стало слишком волосатым. - person Warren P; 16.06.2011

Похоже, вы пытаетесь написать что-то вроде этого Форт в питоне.

person Warren P    schedule 14.06.2011
comment
немного :) я не уверен, что я делаю, хотя - person tekknolagi; 14.06.2011
comment
ну, я попытался расширить ваш пример, и без создания дерева синтаксического анализа, то есть написания синтаксического анализатора, я застрял из-за ограничений вашей простой операции со стеком. Вы намного больше, чем 1 лямбда от парсера. - person Warren P; 15.06.2011

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

person OscarRyz    schedule 15.06.2011