プログラムは非常に混乱しており、関数の定義をサポートするように変更する前に修正する必要があります。これをいくつかのステップで行い、それらを完了すると、それらを回答に追加します。この答えはかなり長くなります。
また、言語定義がどうあるべきかを明らかに決めていません。あなたは自分の言語定義を自分の実装テクニックに従うことに決めましたが、これは一種の壊れたものであり、多くの苦痛をもたらします。
まず、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 つの非常に便利な組み込み演算子がdup
とswap
です。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)
この新しいバージョンでは、if
andifelse
ステートメントがサポートされています。this and 関数呼び出しを使用すると、fib
andfact
関数を言語で実装できます。後でこれらを定義する方法を追加します。
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 では、数値操作はデフォルトで整数を操作する+.
ため、浮動小数点バージョンを取得するには何かを指定する必要があります)。また、文字列操作を可能にするシンボル トークンに対するいくつかの操作。おそらく、トークンを文字の個々のトークンのリストに変換するか、リストを単一のトークンに結合する「分割」および「結合」操作で十分でしょう。