前言
部分变量的定义请自行处理
03
读取
我们思考下,如何让解释器读取用户编写的源代码呢?
我们可以使用文件来存储用户编写的源代码,然后使用with open打开源代码文件,再用for循环读取每一行内容
注意:为了确保复用性,我们将其封装为函数,我们让这个函数接收3个参数,content(用户编写的源代码文件路径),env(Environment对象),line_num(起始读取行,默认1)
def stcode(content, env, line_num):
然后我们定义一个局部变量,这个局部变量的值为env对象的variables字典
global_vars = env.variables
但是停一下,我们不能确定后面会使用到多少次global来获取访问和修改global_vars,所以我们需要创建一个函数,使得允许在函数内部通过这些函数来间接访问和修改全局变量
这个函数应接收一个参数name,并让其返回global_vars字典中对应键的值(键为name参数)
同时在内部应再定义一个函数,让它接收两个参数,name以及value,让其能重新将字典里键为参数name的值重新赋值为value,但若此键不存在,就让其创建一个新键值对
最后我们让它用globals函数返回一个字典,再让它将globals_get
和 globals_set两个函数分别作为get_variable
和 set_variable
加到全局符表中
def globals_get(name):
return global_vars.get(name)
def globals_set(name, value):
global_vars[name] = value
globals().update({
'get_variable': globals_get,
'set_variable': globals_set
})
接着就是读取源代码文件,并for循环读取每一行内容
with open(content, 'r',encoding="utf-8") as file:
filepathdpc2 = content
line_num = 1
for line_num, line in enumerate(file, start=line_num):
line = line.strip()
04
执行
在解释器能正常读取源代码文件后,我们就要让解释器每读取一行内容就解析一行内容;
我们可以设置关键字/词,当解释器在解析时若遇到关键字/词则执行对应的操作
我的思路是一个关键词为一个函数,外加上简单的映射处理,在部分情况下需要考虑用到正则表达式,比如注释符的位置...
由于需要去考虑控制流结构,所以传统的if,elif...依次判断来作为一级执行肯定不行
这些判断最多只能作为二级执行,而一级执行应考虑的是设置一个关键变量判断,在遇到控制流结构时,将这个变量设置为是否触发的关键标准
它将作为权衡一二级执行+平衡各控制流结构之间的中心;
但还要注意的是各个关键词的上下顺序,哪个放在关键变量判断之前,哪些放在关键变量判断内
以及执行完控制流结构后对关键变量的处理
所以根据上面描述,我们可以敲定:
控制流结构触发关键词放在关键变量判断前,非控制流结构函数触发关键词放在关键变量判断内部
所以我们定义一个全局变量:doom,并在stcode函数内部global它
我的思路是doom为0就可以进入二级执行,反之doom-1
global doom
#控制流结构关键词部分
if doom == 0:
#非控制流结构关键词部分
else:
doom-=1
在这里作为一个示例,我只会讲两个,实际上许多函数的实现都是直接套模板就行,拿参数,再调用python内置函数就行了;
后面讲的就是控制流结构部分和GUI部分的了
注释符
在这里注释符我以//为标准
首先判断解释器当前读取的这一行内容有没有其触发的关键字/词
if 'xxx' in line:
在这里由于是注释符,所以我就写为
if '//' in line:
当if成立时该做什么呢?既然是注释,所以注释的内容是不参与其执行过程的,所以我们就可以跳过这一行内容
continue
但是这就完了吗?肯定不是,如果你去测试,你就会发现,当一行内容里只要有//出现这一行就会被注释掉,所以我们要限制它,我们可以使用正则表达式,来确保//只在一行内容最前时才生效
我们要让这个正则表达式匹配以 #
或 //
开头的行,如果匹配则返回true,反之false
def zsfxjh_match(linecon):
comment_pattern = re.compile(r'^[ ]*#|^[ ]*//.*')
if comment_pattern.match(linecon):
return True
else:
return False
if zsfxjh_match(line):
continue
赋值
若不是我在上面写了取两个示例,或许我都忘了这一茬
赋值实际上很简单,我们先要判断是否存在关键字等号
if '=' in line:
如果存在我们就可以用split将等号左右分割,这样就可以得到变量和其要赋予的值
但是不要忘了检查哟,避免用户coding错误
然后拿到name和expr值,记得去除空白字符
but,还要再判断一手,在值那(expr)是callback和直接赋值
如果是callback那就是判断其有左括号,反之为直接赋值
但是我们还得思考一下如果是callback该怎么做
这时我们就可以考虑去再执行一次stcode函数
什么意思呢?也就是获取expr内容,并将其写入临时文件,再调用stcode来执行这个临时文件
但是这是两个不同的执行,那怎么获取其返回值呢?
先定义一个全局变量ycc_bl,然后新增一个函数,函数接收三个参数:变量名称,执行代码,env对象
在函数内,global刚定义的全局变量ycc_bl,然后我们调用另外一个函数(不必多说),这个函数的参数将接收参数执行代码的值,最后返回临时文件路径;我们调用stcode函数,最后使用env对象的set_variable方法来赋值即可
def temp_while2(temp_content):
with tempfile.NamedTemporaryFile(mode='w+', delete=False, encoding='utf-8') as temp_file:
temp_file.write(temp_content)
temp_file.seek(0)
temp = temp_file.name
return temp
def open_fz(blname,code_line,env):
global ycc_bl
file_pathbn = temp_while2(code_line)
stcode(file_pathbn,env,1)
env.set_variable(blname, ycc_bl)
parts = line.split('=', 1)
if len(parts) == 2:
name, expr = parts
name = name.strip()
if '(' in expr:
open_fz(name,expr,env)
else:
env.set_variable(name, expr)
最重要的一点:
比如说我设置了一个函数'MD5'
那么在函数md5实现内部,我就应该加入ycc_bl = xxx
来确保这个函数可以返回值
if 'md5' in line:
xxxx...
xxxx...
a = xxx...
ycc_bl = a #实现callback
End
只要不是控制流结构关键词的判断都放在doom判断内