我一直在用Python制作一个解释器,但遇到了一个问题,它识别字符串、变量、数字和表达式。在测试
.lang
文件时,我注意到它输出了 {'$var': 'EQUALS'}
而不是变量的字符串或数字。
它完美地输出 "print"、"num"、"expr" 值,但不是变量,首先我尝试重新评估代码并将 symbols[varname[4:]] = varvalue
更改为 symbols[varname[4:]] = varvalue[6:]
,这让我得到 {'$var': ' '}
。
它将“变量”识别为与
$var
关联的字符串,但输出 {'$var': 'EQUALS'}
,如字符串、num 或 expr 之前的 equal 一样。我希望它将“变量”存储为变量的字符串,如{'$var': 'STRING: "variable"'}
中所示。
我似乎没有意识到某些事情,但我认为问题可能出在我的 parse 或 doASSIGN 函数中,有人可以告诉我或给我一个提示我可能做错了什么吗?
OUTPUT:
PS C:\Users\<user>\Desktop\spl> python basic.py test.lang
hello world
55
48
{'$var': 'EQUALS'}
test.lang:
print "hello world"
print 55
print (10 + 2) * 4
$var = "variable"
from sys import *
tokens = []
num_stack = []
symbols = {}
def open_file(filename):
data = open(filename, 'r').read()
data += "<EOF>"
return data
def lex(filecontents):
tok = ""
state = 0
varstarted = 0
var = ""
string = ""
expr = ""
n = ""
isexpr = 0
for char in filecontents:
tok += char
if tok == " ":
if state == 0:
tok = ""
else:
tok = " "
elif tok == "\n" or tok =="<EOF>":
if expr != "" and isexpr == 1:
tokens.append("EXPR:" + expr)
expr = ""
elif expr != "" and isexpr == 0:
tokens.append("NUM:" + expr)
expr = ""
elif var != "":
tokens.append("VAR:" + var)
var = ""
varstarted = 0
tok = ""
elif tok == "=" and state == 0:
if var != "":
tokens.append("VAR:" + var)
var = ""
varstarted = 0
tokens.append("EQUALS")
tok = ""
elif tok == "$" and state == 0:
varstarted = 1
var += tok
tok = ""
elif varstarted == 1:
var += tok
tok = ""
elif tok == "PRINT" or tok == "print":
tokens.append("PRINT")
tok = ""
elif tok == "0" or tok == "1" or tok == "2" or tok == "3" or tok == "4" or tok == "5" or tok == "6" or tok == "7" or tok == "8" or tok == "9":
expr += tok
tok = ""
elif tok == "+" or tok == "-" or tok == "*" or tok == "/" or tok == "(" or tok == ")":
isexpr = 1
expr += tok
tok = ""
elif tok == "\"":
if state == 0:
state = 1
elif state == 1:
tokens.append("STRING:" + string + "\"")
string = ""
state = 0
tok = ""
elif state == 1:
string += tok
tok = ""
#print(tokens)
#return ''
return tokens
def evalExpression(expr):
return eval(expr)
def doPRINT(toPRINT):
if(toPRINT[0:6] == "STRING"):
toPRINT = toPRINT[8:]
toPRINT = toPRINT[:-1]
elif(toPRINT[0:3] == "NUM"):
toPRINT = toPRINT[4:]
elif(toPRINT[0:4] == "EXPR"):
toPRINT = evalExpression(toPRINT[5:])
print(toPRINT)
def doASSIGN(varname, varvalue):
symbols[varname[4:]] = varvalue
def parse(toks):
i = 0
while(i < len(toks) - 1):
if toks[i] + " " + toks[i+1][0:6] == "PRINT STRING" or toks[i] + " " + toks[i+1][0:3] == "PRINT NUM" or toks[i] + " " + toks[i+1][0:4] == "PRINT EXPR":
if toks[i+1][0:6] == "STRING":
doPRINT(toks[i+1])
elif toks[i+1][0:3] == "NUM":
doPRINT(toks[i+1])
elif toks[i+1][0:4] == "EXPR":
doPRINT(toks[i+1])
i+= 2
if toks[i][0:3] + " " + toks[i+1] + " " + toks[i+2][0:6] == "VAR EQUALS STRING" or toks[i][0:3] + " " + toks[i+1] + " " + toks[i+2][0:3] == "VAR EQUALS NUM" or toks[i][0:3] + " " + toks[i+1] + " " + toks[i+2][0:4] == "VAR EQUALS EXPR":
if toks[i+2][0:6] == "STRING":
doASSIGN(toks[i],toks[i+1])
elif toks[i+2][0:3] == "NUM":
doASSIGN(toks[i],toks[i+1])
elif toks[i+2][0:4] == "EXPR":
doASSIGN(evalExpression(toks[i+2][5:]))
i += 3
print(symbols)
def run():
data = open_file(argv[1])
toks = lex(data)
parse(toks)
run()
我们实际上可以通过仅更改解析器中的一个字符来解决问题!正如有人已经指出的那样,使用以下代码片段,每次在源程序中的某个位置找到
EQUALS
时,您都会附加 =
:
...
elif tok == "=" and state == 0:
if var != "":
tokens.append("VAR:" + var)
var = ""
varstarted = 0
tokens.append("EQUALS")
tok = ""
...
这是因为您的程序会迭代源文件 (
test.lang
) 并尝试查找与含义上的相应标记匹配的任何符号。一旦您的词法分析器找到 =
,EQUALS
就会被附加到标记列表中,无论上下文如何。为 EQUALS
代币实现上下文是一项具有挑战性但并非完全不可能的任务。
因此,虽然有一种方法可以通过对词法分析器进行一些更改来解决此问题,但如果语言本身没有变得比现在更复杂,则没有必要。我的意思是声明变量的结构不会比
$var = "string"
更复杂。一旦复杂性超过这个值,对词法分析器的更改就足够了(复杂,我的意思是类型声明之类的东西:$var str = "string"
)。
无论如何,让我们看一个简单的修复方法:
if toks[i][0:3] + " " + toks[i+1] + " " + toks[i+2][0:6] == "VAR EQUALS STRING" or ...:
if toks[i+2][0:6] == "STRING":
doASSIGN(toks[i],toks[i+2]) #: line I changed
我没有使用
doASSIGN(toks[I], toks[i+1]
,而是使用 doASSIGN(toks[i],toks[i+2])
,因为 toks[i+1]
会直接落在 EQUALS
而不是 STRING
上,因此导致 {'$var', 'EQUALS'}
。在试用该程序一段时间后,我发现没有理由不实现这一点,因为包含 EQUALS
只是一种形式,如果您决定实现程序优化,您可以在技术上包含它。
现在,这是最漂亮、最佳实践的解决方案吗?可能不是,但坦率地说,这是最简单、最快的一种。如果您需要在某处记录
doASSIGN
的存在,您始终可以通过向 EQUALS
函数添加第三个可选参数来创建后备变量。
但是,这个修复程序确实可以实现与所有其他修复程序相同的效果,无需付出太多努力,几乎不需要任何重写。或者,您也可以尝试使用
itertools
来实现前瞻技术。这也是一种非常流行的对源文件进行词法分析的方法。
所有其他解决方案都需要重写您的程序的大部分内容,这是我想避免做的事情,因为它应该仍然是您自己的工作。尽管如此,我建议您更深入地研究解释器设计并查看一些最佳实践!此外,您可能想查看用于构建解释器和编译器的流行库! :)