面向中文的自然语言编程:L脚本语言
l 为什么要设计一种新的编程语言?
在编程的过程中我总在想,计算机编程这件事需要那么长时间的学习过程,能不能让这份工作更简单一些呢?有那么多的编程语言,每一种都有自己不同的语法规则和使用的局限,那能不能自己实现一个编程语言呢。
如果我们有一个语法简单,并且还支持中文的编程语言是不是能够让编程更简单这样还能够让编程更加符合一般的语言文字表达习惯。
为此,我开始从头设计实现这样的一种脚本编程语言,因为相对于c,c++这样的静态编译语言来说,脚本语言实现起来更简单。这种脚本语言命名为L语言
l L脚本语言的设计目标是:支持中文自然语言编程
也就是说L脚本语言希望更加更加接近我们平时说话写文章的语言,而不是计算机才懂的机器语言,而且它还是建立在中文基础上的,不需要精通英语就可以使用了
l L脚本语言的语法为 动作:对象,参数
我们先来看看一段C语言代码及其文字表述的含义
C语言代码 |
解释 |
|
int num ; num =10; num=num+1; printf(“%d”,num); |
定义一个整数num; 给整数num赋值为10; 将num的值加1重新赋值给num; 打印输出num; |
为什么我们需要写出左边那样的代码才能让计算机运行,为什么不能写出右边的描述就能让计算机去工作呢?大部分计算机软件开发人员的回答都是:因为右边的自然语言不容易让计算机识别。
然而,真的是这样吗?
我一直在思考,是不是能够让计算机来识别自然语言呢?
我们来观察一下,在计算机编程中,每一行代码都是一个命令,而每一个命令的核心都是一个动词,其它的信息都可以理解为参数。
而自然语言中,不仅有动词,还有名词,形容词,量词等等。
它们的共同点都是有一个核心动词。
那如果我们把我们平时所写的自然语言重新调整一下格式,是不是计算机就容易识别了呢?
为了验证这样的想法,我做了一个假设,假设编程语言只有命令+参数这一种格式
,这样的语法,即接近自然语言,又容易让计算机识别
l 在L脚本语言中,只有动作,对象和字符串
我们用 L脚本语言来实现上面的那段c语言代码
L脚本语言代码1 |
L脚本语言代码2 |
定义:整数,num,10 计算:算式,exp1,num=num+1 显示:num |
定义:整数,num,10 计算:num=num+1 显示:num |
看一下,是不是很简单,我想,不需要有编程经验都可以看得懂上面这段L脚本语言的代码
而且,这样简单的语法结构很容易让计算机来识别
编程是不是很简单呢?
当然,前面这样的一段代码没有太多现实意义,我们的L脚本语言需要更强的功能。
为此,我们需要定义更多的语言特性。
l L脚本语言的语法说明
在L脚本语言的代码中,一行代码是一个文本串,一个文本串有可能是命令,也有可能是对象,还有可能是原始的文本字符串
看起来有点混乱,但也有明确的规则
在一行代码中,行的起始必然是动作:对象
所以,一行的起始位置到":"之间的就是动作名,":"后面跟着的是对象名
对象名后面可以跟参数,也可以不跟
如果后面跟着参数,那么用","分隔开
分隔符:或:前面是命令或动作,后面是对象
分割符,或,用来把参数区分开
参数可以是字符串,也可以是对象
这样够简单吧!L脚本语言中没有太多标识符元素,而且只有两个分隔符,逗号和冒号,这样的语法很容易记忆吧。
什么是对象?对于没有接触过编程的朋友,这个需要解释一下,这个对象不是“谈对象”的“对象”对象一词是从英文 object翻译过来的,英文词典上object的含义解释是:物体、物件。其实这样很容易理解了,就是说我们在编程思考问题的时候不是考虑每一条计算指令,而是考虑在一个物体上进行什么动作。
L脚本语言也是一门面向对象的编程语言!
l 其它语言特性
L脚本语言中内置了一些全局对象,但也允许用户定义自己的对象实例 ,L脚本语言的解释引擎会自己识别参数的类型到底是什么
我们也可以把动作理解为函数
L脚本语言中内置了一个“结果”对象,这个"结果"对象中保存的是上一条语句的运行结果,这个"结果"可以是任何对象,但它是容易丢失的,如果我们想要把“结果”中保存的对象保留下来,可以通过使用“捕获”命令
L脚本语言中内置了对条件语句的支持,在这里,条件语句实际上也是一个动作,叫做"如果"语句
L脚本语言中也内置了对循环语句的支持,在这里,循环语句也是一个动作,叫做"当"语句
好了,光说不做假把式。我们看看L脚本语言都有哪些功能吧。
l L脚本语言目前支持的功能:
支持字符串,支持字符串比较 连接取子串 赋值 查找子串
支持文件,支持文件的打开,读取,写入,获得文件大小等操作
支持数值运算,包括整数和浮点数的运算,包括算术运算、逻辑运算、位运算、移位运算
支持“表”,表其实是借鉴了LUA语言的概念,可以用它来实现动态数组,表的元素可以是任意对象,可以是函数,当然也可以是表,表的大小是可变的
支持正则表达式,可以通过内置的正则表达式实现字符串的匹配
支持函数,函数可以有任意多个参数,参数可以是任意对象,当然也可以是函数
支持“内存”对象,支持内存比较
调试器,支持单步执行,支持断点,支持对象查看,支持源码编辑
支持调用操作系统C语言API
支持库以及库的导入机制,通过L脚本语言的导入库机制,可以实现语言自身的扩展,因为可以调用操作系统提供的C语言API,所以可以由用户实现各种增强的功能
支持网络通讯,支持TCP,UDP协议,可以使用L脚本语言开发简单的网络程序,当然可以是服务器、也可以是客户端
支持时间、日期,时间比较
支持目录管理
支持简单的交互输入框
支持随机数的生成
支持注册表编辑
支持自定义类,以及类的实例化
哇!看来我们的L脚本语言还真能干,有这么多功能呢!
l 我们来看看L脚本语言都能做些什么吧
对字符串的支持:
因为L脚本语言中:和,是分隔符 所以如果希望在字符串中使用:和, 就需要用" "把字符串括起来
但是动作和对象名不可以用""括起来
为什么?这只不过是因为这样实现更加简单,所以就这样规定了
对函数的支持:
对于一个函数,首先要进行定义(声明),然后才能进行调用
定义函数有着明确的格式:
函数名可以是任意字符串,形式参数也可以是任意字符串,形式参数的个数可以是任意多个
函数体可以是任意合法的L脚本语言脚本语句
函数体以 开始:函数,函数名 的格式开始
以 结束:函数,函数名 的格式结束
调用函数时需要传入与定义函数时数量匹配的函数参数
函数的参数必须是对象
函数的参数是对象,而函数自身也是对象,所以函数的参数也可以是函数
这不就可以实现递归调用了吗?
在L脚本语言中,函数的参数是引用方式传递,也就是说,函数内部的形式参数只是实际参数的别名,在结束函数调用并退出函数体的时候,解释器引擎负责解除别名映射,所以,函数内部对形式参数的修改也会反映到实际参数之上。因此,在设计L脚本语言的函数时需要考虑这一点,避免意外修改实际参数的值。
l 例子程序
介绍了这么多,好像还是不清楚,到底应该怎样编程呢?
不用担心,我已经实现了一个L脚本语言的解释引擎和一个简单的调试器,我们只要有一个“记事本”或者任何一种文本编辑软件,就可以实现L脚本语言的编程了,嗯!这个解释器和调试器是用C++语言开发的。
我们来看看编程的例子吧,一边看例子,一边解释程序的语法结构,这样更容易理解
在L脚本语言中,进行数学运算的命令格式为:
计算:算式,算式的名字,算式的内容
或者
计算:算式的内容
以#开头的行是注释,所有注释会被解释器忽略
代码的开头一行以#scp开始
代码的扩展名为.scp ,其实可以是任意的扩展名,这只不过是一个约定而已
代码文件必须保存为ANSI编码格式,对了,还是为了实现更简单
例子1:数值运算
#scp #L脚本语言中支持的算术运算符号 + - * / % #L脚本语言中支持的逻辑运算符号 < <= == > >= ! != #L脚本语言支持赋值运算符 = += -= *= /= %= #数值也是对象,在L脚本语言中,数值是整数或者浮点数 #算式是包含数学计算表达式及其计算结果的对象 #不支持-- ++ 这样的自增运算 #目前只支持简单的算式 #对于运算符的优先级没有进行深入处理 #所以现在书写的表达式必须加括号 #很绕吗?其实一点也不复杂 定义:整数,我是一个整数,1001 定义:整数,我是另一个整数,1 定义:整数,我是计算结果,0 #逻辑“非”运算 计算:算式,formula17,!我是计算结果 显示:formula17 #逻辑“不等于” 计算:算式,formula16,我是计算结果!=我是一个整数 显示:formula16 #位运算“按位与” 计算:算式,算式1,我是一个整数&我是另一个整数 显示:算式1 #位运算”按位或” 计算:算式,算式2,我是一个整数|我是另一个整数 显示:算式2 #位运算”按位异或” 计算:算式,算式3,我是一个整数^我是另一个整数 显示:算式3 #逻辑“与“ 计算:算式,算式4,我是计算结果&&我是另一个整数 显示:算式4 #逻辑“或“ 计算:算式,算式5,我是计算结果||我是另一个整数 显示:算式5 #按位左移 计算:算式,算式6,我是一个整数<<我是另一个整数 显示:算式6 #按位右移 计算:算式,算式7,我是一个整数>>我是另一个整数 显示:算式7 #赋值 计算:算式,formula10,我是计算结果=我是一个整数 显示:formula10 #逻辑“等于“ 计算:算式,formula11,我是计算结果==我是一个整数 显示:formula11 #”小于” 计算:算式,formula12,我是计算结果<我是一个整数 显示:formula12 #”小于等于” 计算:算式,formula13,我是计算结果<=我是一个整数 显示:formula13 “大于” 计算:算式,formula14,我是计算结果>我是一个整数 显示:formula14 “大于等于” 计算:算式,formula15,我是计算结果>=我是一个整数 显示:formula15 #复杂一点的整数四则运算 计算:算式,formula9,我是计算结果=(我是一个整数*(-2)*(-2)-1-9+6)/100 显示:formula9 定义:整数,number1,8 定义:整数,number2,9 定义:整数,number3,100 #各种加减乘除运算 计算:算式,formula1,number1*(number2+number3)-100 计算:算式,formula2,(number1*(number2+number3))-(100*102) #计算:算式,formula2,100*number4 计算:算式,formula3,number1+number2*number1 计算:算式,formula4,number1-number2-365 计算:算式,formula5,number1*number2 计算:算式,formula6,number1/number2 计算:算式,formula7,number1%number2 计算:算式,formula8,number1+number2*number2-number1*number3 #一些简单的函数以及函数调用 定义:函数,打印并减一,参数1 开始:函数,打印并减一 显示:参数1 计算:算式,exp1,参数1=参数1-1 结束:函数,打印并减一 定义:整数,变化因子,100 当:变化因子>0,调用:函数,打印并减一,变化因子 定义:整数,rid,100 定义:函数,求圆的周长,半径 开始:函数,求圆的周长 计算:算式,ret,2*3.14*半径 显示:ret 结束:函数,求圆的周长 显示:"现在求圆的周长" 调用:函数,求圆的周长,rid 定义:整数,rid2,10 定义:函数,求圆的面积,半径 开始:函数,求圆的面积 计算:算式,ret,3.14*(半径*半径) 显示:ret 结束:函数,求圆的面积 显示:"现在求圆的面积" 调用:函数,求圆的面积,rid2 定义:浮点数,float1,1.5 定义:浮点数,float2,155 计算:算式,formula111,float1*float2 显示:formula111 定义:函数,乘方,num1 开始:函数,乘方 计算:算式,ret,num1*num1 显示:ret 结束:函数,乘方 定义:函数,平方,num1 开始:函数,平方 计算:算式,ret,num1*num1 显示:ret 结束:函数,平方 定义:函数,三次方,num1 开始:函数,三次方 计算:算式,ret,num1*num1*num1 显示:ret 结束:函数,三次方 定义:函数,求余数,num1,num2 开始:函数,求余数 计算:算式,ret,num1%num2 显示:ret 结束:函数,求余数 #定义:数值,anynum1,65535 #定义:数值,anynum2,3.14159 显示:number1 显示:number2 显示:float1 显示:float2 显示:formula1 显示:formula2 显示:formula3 显示:formula4 显示:formula5 显示:formula6 显示:formula7 显示:formula8 调用:函数,乘方,number1 调用:函数,求余数,number2,number1 调用:函数,三次方,number1 #一些赋值运算 计算:我是一个整数+=我是另一个整数 显示:我是一个整数 计算:我是一个整数-=我是另一个整数 显示:我是一个整数 计算:我是一个整数*=我是另一个整数 显示:我是一个整数 计算:我是一个整数/=我是另一个整数 显示:我是一个整数 计算:我是一个整数%=我是另一个整数 显示:我是一个整数 |
在前面的例子里,我介绍了如何使用L脚本语言编程实现数值运算,以及简单函数的编写
现在我们来看看如何使用L脚本语言实现字符串的处理和字符串运算
例子2:字符串处理
#scp 定义:函数,function1,参数1,参数2 开始:函数,function1 弹出:消息框,参数1,参数2 结束:函数,function1 定义:字符串,str1,"这是一个字符串" 定义:字符串,str2,"标题" 定义:字符串,str3," 这是另一个字符串" 取子串:str4,str1,1,3; 显示:str4 调用:函数,function1,str1,str2 调用:函数,function1,str3,str2 连接:字符串,str1,str3 调用:函数,function1,str1,str2 定义:函数,function2 开始:函数,function2 显示:helloworld 弹出:消息框,helloworld 结束:函数,function2 调用:函数,function2 定义:函数,function3,参数1 开始:函数,function3 定义:字符串,str4,"函数调用函数" 定义:字符串,str5,"消息" 调用:函数,参数1,str4,str5 结束:函数,function3 调用:函数,function3,function1 定义:函数,function4,参数1,参数2 开始:函数,function4 打开:文件,file1,参数1 写入:文件,file1,参数2 结束:函数,function4 定义:字符串,filename,"c:\222.txt" 调用:函数,function4,filename,str1 显示:filename 清空:字符串,filename 显示:filename |
好了,通过前面的例子,我们知道了如何进行字符串的初始化以及各种字符串操作函数,而且我们还通过内置的函数,实现了使操作系统弹出一个消息窗口,是不是很简单呢?
嗯,字符串的操作不只是有连接、取子串、清空,还可以进行查找和替换,并且,L脚本内置了对正则表达式的支持,我们也可以通过正则表达式在字符串的内部查找匹配字串
例子3:字符串查找替换以及正则表达式的使用
#scp 定义:正则表达式,regexp1,"\d\d\d" 定义:字符串,str2,"abc" 定义:字符串,str1,123 匹配:正则表达式,regexp1,str1 捕获:result1 显示:result1 如果:result1==匹配,弹出:消息框,正则表达式匹配到字符串,提示 如果:result1==不匹配,弹出:消息框,正则表达式没有匹配到字符串,提示 匹配:正则表达式,regexp1,str2 捕获:result2 显示:result2 如果:result2==匹配,弹出:消息框,正则表达式匹配到字符串,提示 如果:result2==不匹配,弹出:消息框,正则表达式没有匹配到字符串,提示 定义:字符串,string1,"123456abcdefg" 定义:字符串,string2,"abc" 查找:字符串,string1,string2 捕获:result3 显示:result3 如果:result3==找到,弹出:消息框,找到字符串,提示 如果:result3==没找到,弹出:消息框,没找到字符串,提示 替换:字符串,string1,string2,"xyz" 捕获:result4 显示:result4 显示:字符串,string1 如果:result4==找到,弹出:消息框,找到并替换字符串,提示 如果:result4==没找到,弹出:消息框,没找到字符串,提示 |
在L脚本语言中,每一行代码执行的结果都会赋值给一个“结果”对象,这个”结果”对象的内容可以是任何对象,但是它是容易丢失的,如果我们不把它保存起来,那么下一行代码会把上一行的执行结果给替换掉,所以,在我们需要保留代码执行结果的时候,可以通过一条”捕获“语句,实现对代码执行结果的保存
上面的例子里还出现了“如果”语句,“如果”语句的语法是:
如果:条件表达式,条件表达式为真时运行的代码
很容易理解吧。
但是我们没有像C语言那样的if else结构,那如果我们想要同时编写条件为真和条件为假的时候应该执行的代码该怎么办呢?只需要再写一条“如果”语句就行啦。让第二条”如果”语句的条件与第一条的相反,就可以了。
我知道这样看起来有点傻,不过为了实现起来简单,初步就这样设计了。
好了,经过这几个例子程序,我们应该能够看懂L脚本语言的其它代码了,因为代码都是中文,并且语法格式都是一致的,所以相信大家理解起来并不难。
除了数值运算和字符串处理,我们还能不能访问存储在磁盘上的文件呢?
例子4:文件处理以及内存处理
#scp 打开:文件,file1,c:\1.txt 显示:文件,file1 复制:文件,c:\1.txt,c:\456.txt 复制:文件,c:\456.txt,c:\789.txt 移动:文件,c:\789.txt,c:\777.txt 删除:文件,c:\777.txt 定义:整数,size1,0 取大小:文件,file1,size1 显示:size1 申请:内存,mem1,1024 申请:内存,mem2,1024 复制:内存,mem1,mem2 写入:内存,mem1,number1 定义:字符串,str1,这是一个测试字符串 写入:文件,file1,mem1 写入:文件,file1,str1 释放:内存,mem1 定义:整数,size2,0 取大小:内存,mem2,size2 显示:size2 释放:内存,mem2 |
嗯!我们已经能够操作文件和申请并使用内存了,但是只是这样的工作是不是有些枯燥呢?能不能让我们和计算机进行一些交互呢?
例子5:用户交互输入----简单的计算器
#scp #我们用scp来开发一个计算器吧! 定义:字符串,string1 定义:函数,func1 开始:函数,func1 等待:用户输入,string1 计算:算式,formula1,string1 显示:formula1 结束:函数,func1 当:1,调用:函数,func1 |
哈哈!原来实现一个计算器就这么几行代码啊!
如果只能计算一些数值表达式、处理一些简单的字符串和进行简单的文件访问好像还是没什么意思啊!如果能够通过计算机网络来与别人进行通讯会不会更有意思呢?
当然啦,L脚本语言也可以实现基本的网络通讯
那现在我们来自己实现网络通讯吧!
例子6:TCP回显服务器 以及 TCP客户端
#scp #这是一个TCP回显服务器的例子 #它不断监听一个网络连接 #如果有网络连接到来 #就通过新建立的网络连接接收一个字符串,打印并发送回客户端 定义:字符串,string1,"hello" 定义:字符串,string2,"" 定义:地址,addr2,127.0.0.1,27015 定义:整数,字节数,0 定义:网络连接,conn2,TCP 定义:函数,routine 开始:函数,routine 监听:conn2,addr2 接收:conn2,string2 取大小:字符串,string2,字节数 如果:字节数>0,显示:string2 如果:字节数>0,发送:conn2,string2 结束:函数,routine 当:1,调用:函数,routine |
#scp #这是一个通过TCP协议向服务器发送字符串的例子 #它先发起对一个网络上的服务器的连接 #如果连接建立,就发送一个字符串给服务器 #如果服务器返回消息 #就将消息打印输出 #这个过程重复100次 定义:字符串,string1,"hello" 定义:字符串,string2,"" 定义:地址,addr1,127.0.0.1,27015 定义:整数,字节数,0 定义:整数, count,100 定义:网络连接,conn1,TCP 连接:conn1,addr1 定义:函数,function1 开始:函数,function1 发送:conn1,string1 接收:conn1,string2 取大小:字符串,string2,字节数 如果:字节数>0,显示:string2 计算:算式,exp1,count=count-1 结束:函数,function1 当:count>0,调用:函数,function1 |
原来网络通信也这么简单啊!看来我们自己实现一个简单的聊天工具也不太难!只需要结合用户输入就可以实现实时的聊天啦!大家可以自己试一下!
L脚本语言从语法设计到实现上都包含了很多缺陷,而且这些功能的实现只用了很短的时间,我希望L脚本语言能够对希望学习编程的人有一些帮助,编程高手们也可以用它来完成一些简单的重复性工作。
L脚本语言支持调用C语言接口的操作系统API函数,并且支持库以及库的导入机制,因此L脚本语言是可以不断扩展的,希望有兴趣的人能够参与L脚本语言的库开发。因为希望先让零基础的朋友学习L脚本,而库的开发需要一些C语言编程和操作系统的知识,在这里我先不进行L脚本库的深入介绍了。
对于L脚本引擎中可能存在的各种错误和缺陷,希望有兴趣的朋友能够给我提出修改的意见,当然如果有更好的语法结构设计和功能点也可以告诉我。
有兴趣的朋友可以下载L脚本语言解释引擎和调试器安装包进行尝试,安装包包含了命令行的解释引擎和一个简单的调试器,一份语法手册和一些简单的脚本例子。
CSDN资源下载地址 http://download.csdn.net/detail/itmes/8656133
希望大家编程愉快!