我做这个东西为了解决自己的一些小项目(顺便找找趣味),方便自己写东西,所以在兼容python语法加一些东西
我们先来写出这个语言的第一个代码段:
#hello.py
def search(self, nums: list, target: int) -> int:
left, right = 0, len(nums) - 1
while left <= right:
mid = (right - left) // 2 + left
num = nums[mid]
if num == target:
return mid
elif num > target:
right = mid - 1
else:
left = mid + 1
return -1
# 作者:LeetCode-Solution
# 链接:https://leetcode.cn/problems/binary-search/solution/er-fen-cha-zhao-by-leetcode-solution-f0xw/
# 来源:力扣(LeetCode)
# 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
<!--deon.kt-->
<%
import hello
import KamitaTomoe.output as output
def main():
output.echo('<h1>Hello World!</h1>')
output.echo("我们输出了Hello World!,它将在网页上显示")
output.echo(f'<h2>我们二分查找了[1,2,3,7,21,10,32,22,543,111]中的7</h2>,结果\
下标:{hello.search(nums=[1,2,3,7,21,10,32,22,543,111],target=7)}')
%>
<html>
<h1>它可以正确输出Hello World!吗,请拭目以待</h1><br>
<%
main()
%>
</html>
因为py来写web,我觉得不方便(不够php),但是php又太难看了,功能也不如py,所以,我就打算自制一个兼容py语法的KamitaTomoe(本身就是用py写的,所以当然要兼容)
前期,我打算用一行一行解释运行的方式来运行代码,但是因为py的特殊,需要用缩进代替掉大括号的作用,就感觉不舒服,于是就改变想法,直接使用一段代码来进行解释,抛弃掉一行一行的解释,解释成我们熟悉的python代码来运行,我们就使用exec函数来运行代码
首先要支持的肯定是解释器的调用,我这里在采用命令行传参的调用方式,例子:
python KamitaTomoe.py -f D:\Tsuki Hikari\Kamita_Tomoe\deon.kt
目前只支持绝对路径
首先我使用下面的代码来进行接收参数
arg = getopt.getopt(sys.argv[1:],'-f',['file'])
然后我也需要挑出文件的格式和文件所在路径
Extension = file.split('.')[-1] #获取文件格式
file = ''.join(arg[1])
这样子整合起来就是:
file = ''.join(getopt.getopt(sys.argv[1:],'-f',['file'])[1])
Extension = file.split('.')[-1]
然后要切换python的工作路径
os.chdir('/'.join(file.split('/')[0:-1])) #更换运行路径
然后我们定义一个类,代码的运行均在这个类里面完成
class move:
def __init__(self):
pass
def parse(self,code:str): #语法分析并且运行
'''代码'''
for i in code.split('\n'):
i = i.split(' ')
if(i == ''):
continue
if(i.startswith('#')):
continue
if(i.startswith('import')):
continue
if(i.startswith('class')):
continue
于是我们就可以在切换工作路径之前加载一些语言默认加载的类库,比如output库,用来输出内容到网页
Class_library = '\n'.join([f'import KamitaTomoe.{i} as {i}' for i in ['output','html']]) #需要提前加载的类库
'''['output','html'] -> import KamitaTomoe.output as output \n import KamitaTomoe.html as html'''
self.exec_move(self.Class_library) #提前加载类库
接下来我们代码的运行靠着move类就可以了
首先我们要学会运行一个文件,首先就要把文件读出来
这个很简单,直接使用open方法读取即可
self.file_content = open(file,mode='r',encoding='utf-8').read() #读取文件
我记得我之前定义了一个成员方法parse,用于传入代码,进行语法分析转换成python代码并且运行,现在就可以书写这个方法
首先我们要根据<%开始,%>结束的字符来判断什么是python代码,什么是html代码,并且把html当作正常输出
那么,根据这个需求,我们当然是用正则表达式最简单的字符串切割:
我们先使用print(self.file_content.split(‘<%’)) 切割并且输出内容(self.file_content是代码文件的内容):
>>>print(self.file_content.split('<%'))
PS D:\Downloads\TsukiHikari> & C:/Users/Administrator/AppData/Local/Programs/Python/Python310/python.exe d:/Downloads/TsukiHikari/Kamita_Tomoe/KamitaTomoe.py -f D:\Downloads\TsukiHikari\Kamita_Tomoe\deon.kt
['', '\nimport time\nimport hello\nimport KamitaTomoe.output as output\n\ndef main():\n output.echo(\'<h1>Hello
World!</h1>\')\n print("我们输出了Hello World!,它将在网页上显示")\n output.echo(f\'<h2>我们二分查找了[1,2,3,7,21,10,32,22,543,111]中的7</h2>,结果\\\n 下标:{hello.search(nums=[1,2,3,7,21,10,32,22,543,111],target=7)}\')\n
output.Web_output()\n\n%>\n\n<html>\n<h1>它可以正确输出Hello World!吗,请拭目以待</h1><br>\n', ' main() %>\n</html>']
可以看见这是一个列表,然后我们遍历这个列表,并且对里面每个元素再次切割’%>’
>>>print([i.split('%>') for i in self.file_content.split('<%')][1:])
[['\nimport time\nimport hello\nimport KamitaTomoe.output as output\n\ndef main():\n output.echo(\'<h1>Hello Wor1,10,32,22,543,111]中的7</h2>,结果\\\n 下标:{hello.search(nums=[1,2,3,7,21,10,32,22,543,111],target=7)}\')\n
output.Web_output()\n\n', '\n\n<html>\n<h1>它可以正确输出Hello World!吗,请拭目以待</h1><br>\n'], [' main() ', '\n</html>']]
于是我们可以发现,这个输出的列表一维每个元素代表每一个代码段,二维的第一个元素代表kt的程序代码,第二个元素代表html代码,注意:这个非常重要,决定了代码运行的顺序
code = [i.split('%>') for i in self.file_content.split('<%')][1:] #list[list['程序代码','html代码']]
但是二维数组看起来太费力了,所以我们可以转换一下,减慢一下运行速度,变成一维数组(呸,是列表,这样子就可以多执行一个for循环)
code = [ x for y in [i.split('%>') for i in self.file_content.split('<%')][1:] for x in y] #list[list['程序代码','html代码']]
''' vec = [[1,2,3], [4,5,6], [7,8,9]]
[num for elem in vec for num in elem],更加详细的例子:https://zhuanlan.zhihu.com/p/344114093'''
还需要解决如何运行代码的问题,因为exec函数的特殊性,所以exec函数的调用均在__init__成员方法内调用,其他的成员方法就负责修改解释出来的代码即可,到时候运行时直接把所有代码运行
在切换工作目录之前呢,我们可以提前加载一些该语言的类库,方便写代码的时候调用,但是这个不是必须的(可以开发的时候手动导入)
exec('\n'.join([f'import KamitaTomoe.{i} as {i}' for i in ['output','html']])) #提前加载类库
'''['output','html'] -> import KamitaTomoe.output as output \n import KamitaTomoe.html as html'''
我这里提前加载了output和html两个类库
然后在类里面创建一个成员变量,存放类方法解释出来可以运行的python代码
OK,我们开发的基本架构就完成了,我们已经可以运行代码了,也可以轻松的在这个基础上更改解释器的代码
解释器核心源码:
import sys
import getopt
import os
class move:
Final_code = '' #最后生成的代码
def __init__(self):
self.file_content = open(file,mode='r',encoding='utf-8').read() #读取文件
luj = '\\'.join(file.split('\\')[0:-1]) #更换运行路径
self.parse() #语法分析
self.Final_code = '\n'.join([f'import KamitaTomoe.{i} as {i}' for i in ['output','html']])+'\n' \
+f'import os \nos.chdir(\'{luj}\')' \
+self.Final_code.replace('import KamitaTomoe.output as output','').replace('import KamitaTomoe.html as html','')
self.Final_code += '\noutput.Web_output()' #返回生成的HTML
print(self.Final_code)
def Program_parse(self,code:str): #程序代码部分语法分析
self.Final_code += (code+'\n')
def parse(self): #语法分析
'''语法解析'''
code =[i.split('%>') for i in self.file_content.split('<%')][1:] #list[list['程序代码','html代码']]
for i in code:
for i1 in range(2):
if(i1 == 0): #执行程序代码代码
self.Program_parse(i[0])
else:
self.Final_code += f'output.echo(\'\'\'{i[1]}\'\'\')\n'
if __name__ == '__main__':
file = ''.join(getopt.getopt(sys.argv[1:],'-f',['file'])[1])
Extension = file.split('.')[-1]
if(Extension != 'py' and Extension != 'kt'): #判断扩展名
print('Illegal file format')
else:
try:
moves = move()
exec(moves.Final_code) #运行代码
except Exception as e:
print('Exception:', e)
except: #如果不传入文件,那就使用交互式
print('已经对交互式编程停止支持')
生成的python代码:
import KamitaTomoe.output as output
import KamitaTomoe.html as html
import os
os.chdir('D:\Downloads\TsukiHikari\Kamita_Tomoe')
import time
import hello
def main():
output.echo('<h1>Hello World!</h1>')
print("我们输出了Hello World!,它将在网页上显示")
output.echo(f'<h2>我们二分查找了[1,2,3,7,21,10,32,22,543,111]中的7</h2>,结果\
下标:{hello.search(nums=[1,2,3,7,21,10,32,22,543,111],target=7)}')
output.echo('''
<html>
<h1>它可以正确输出Hello World!吗,请拭目以待</h1><br>
''')
main()
output.echo('''
</html>''')
output.Web_output()
注意:output.echo方法用来给网页输出东西(类似于php的echo函数),在最后调用output.Web_output()用来整合echo的东西,给最后生成html
说实话这个不算一个难的东西,只要肯学,就只是一个语法分析而已,毕竟没有用到编译之类的
页尾该2023年了