6

私は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()
4

5 に答える 5

7

プログラムは非常に混乱しており、関数の定義をサポートするように変更する前に修正する必要があります。これをいくつかのステップで行い、それらを完了すると、それらを回答に追加します。この答えはかなり長くなります。

また、言語定義がどうあるべきかを明らかに決めていません。あなたは自分の言語定義を自分の実装テクニックに従うことに決めましたが、これは一種の壊れたものであり、多くの苦痛をもたらします。

まず、Simp関数の定義が本当に壊れています。すべてがスタックから正確に 2 つの値を取得し、正確に 1 つの値を元に戻す必要があります。これは壊れています。階乗関数はこのようには機能せず、フィボナッチ関数も機能しないため、使用されていない「ダミー」引数を使用する必要があります。また、グローバル リストやディクショナリの要素への代入などには、値をスタックにプッシュする理由がないため、'ok' をプッシュしたままになります。これは壊れており、修正が必要です。

この問題を修正したバージョンがこちらです。その目的をより正確に反映するために、名前を に変更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モジュールを解析に使用しています。そして、文字のグループ全体 (スペースなどを含む) が 1 つのエンティティの一部であると言う方法があります。これにより、関数を実装するための迅速かつ簡単な方法が得られます。次のようなことができます。

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()

スタックベースの言語では、2 つの非常に便利な組み込み演算子がdupswapです。dup一番上のスタック要素を取得して複製します。swap上の 2 つのスタック要素を交換します。

持っている場合は、次のような関数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)

この新しいバージョンでは、ifandifelseステートメントがサポートされています。this and 関数呼び出しを使用すると、fibandfact関数を言語で実装できます。後でこれらを定義する方法を追加します。

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の 2 つの可能なパスの関数で使用されます。ifelse

このパーサーはとてつもなく単純です。そしてshlex、この単純な言語のレクサーとして十分強力です。他のプロジェクトでは、リストから個々のアイテムを取得します。以前のリストの一部のみで構成される新しいリストを作成します。スタック上の単一のトークンを「リスト化」します。whileプリミティブの実装。整数を操作する数値演算子 (実際の Forte では、数値操作はデフォルトで整数を操作する+.ため、浮動小数点バージョンを取得するには何かを指定する必要があります)。また、文字列操作を可能にするシンボル トークンに対するいくつかの操作。おそらく、トークンを文字の個々のトークンのリストに変換するか、リストを単一のトークンに結合する「分割」および「結合」操作で十分でしょう。

于 2011-06-14T19:38:25.397 に答える
2

正しい答えは、何を心配しているかによって異なります。言語の複雑さが増すようなスケーラブルなソリューションを用意することに不安がある場合は、おそらくパーサー モジュールの 1 つを学習/使用することから始める必要があります。一部のモジュールは、手動で簡単に生成できるものよりも最適化されている可能性が高いため、パフォーマンスが心配な場合は、これが答えになる可能性があります。

一方、学習に興味がある場合は、分水車のアルゴリズムを調べてください。おそらく、次の行に沿った操作を使用して、関数の辞書を作成できます (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
于 2011-06-14T23:01:02.347 に答える
1

変数を格納し、それらを関数名に関連付ける辞書を作成できます。

たとえば、コードを1行ずつ読んでいるとします。

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

変数(a、b、c)を見つけて、それらをaaリストに格納し、そのリストをスコープ内に配置すると、これは次のようなグローバルスコープになる可能性があります。

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. レクサーを作成します(入力をトークンに分割します)
  2. 解析(トークンが言語ルールに従っているかどうかを検証します)
  3. AST(入力をウォークするため)
  4. プログラムシンボル(変数や関数など)を特定する
  5. インタプリタを作成します。

これがお役に立てば幸いです

于 2011-06-15T00:00:35.333 に答える
1

必要なのは、一連の記号 (数値、数値の演算、括弧) を、一連の記号によって表現される計算を表すツリー構造に変換することです。このようなことは、まさに「パーサー」の仕事です。次のような単純なパーサーを見たいと思うかもしれませんhttp://en.wikipedia.org/wiki/LL_parser これらはコーディングが簡単で、鉛筆と紙でテーブルを解析します。

于 2011-06-14T02:32:54.733 に答える
1

Python でこのForthのようなものを書こうとしているようです。

于 2011-06-14T02:40:22.083 に答える