- 前言
- 之前的语言设计
- 新语言风格
- 自我提问
- 确定风格
- 操作符及其功能
- 关键字
- 字面量
- 标识符
- 注释
- 数据类型
- 数学运算
- 字符串表达式
- 正则表达式
- 判断表达式
- 逻辑表达式
- 位运算
- 变量定义
- 基本语句介绍
- 循环语句
- switch语句
- 函数定义
- 函数调用
- 普通函数调用
- 自然语言顺序的函数调用
- 普通语句块
- 枚举
- 类——封装,继承,多态,作用域限定,接口
- 数组
- 字典
- 范围
- 命名空间
- 模块的构造与析构语句块
- built-in 内置函数
- BNF文法
- 安全or方便
前言
编译器我是一定要做的。
将编译器作为兴趣的契机在复习一个C++数据结构学习时,做的是个中缀表达式转后缀表达式,然后用栈对后缀表达式进行求解。应该很多人都会和我有同样的想法,能不能用这种逆波兰表达式来制作编程语言呢?毕竟逆波兰表达式可以用来计算表达式,甚至更进一步可以引入数学函数功能 ,
刚开始的suatin解释器项目,我就启动了两次。第三次制作的是zhangqi编译器,在操作符优先级上,使用了递归,但是还是无法理解如何将BNF语法实现为对应的代码。第四次制作的是i think of you编译器,主要工作在预编译和词法分析阶段,语法分析前面的内容对不同语言来说都是一样的。
所有的项目都没有考虑过局部变量、全局变量、常量、栈、符号表这些内容该如何实现。。。我实在是乐色。
现在第五次制作编译器,名字叫suatin,但是和之前的没有关系。用C而不是用C++做,C++太臃肿。当做之所以用C++是因为C++有regex文件,用正则可以让我省去制作详细的词法分析器的功夫——当时第一本书是《两周制作脚本语言》(垃圾书)。
不是靠热情,也不是为了坚持什么的,现在纯粹是为了装逼!我一定要做出来,吊着一口气第三四次重学编译原理。反复学习自己懂了一半的知识很难受,就像吃明明很难吃但是必须要吃的早餐一样。
之前的语言设计
lang:使用BNF范式设计一个文法
lang:总结9种编程语言的语法来设计自己的编程语言Suatin-lang
【Suatin】不学编译原理就制作语言--------谈谈自制编程语言1
【Suatin】不学编译原理就制作语言2——Concrete Syntax Tree
自制编程语言应该按编译原理来吗?
下一版本编程语言Dem应该具有的功能---------又一次胡思乱想
【语言定义】《自己动手制作编译器、链接器》中的simple c语言定义
[Julia]野心是不是要更大一点……又制定新的编程语言计划
【zhangqi-lang】制作新语言的需求
【zhangqi-lang】【1】词法分析器
【zhangqi-lang】【9】文法设计
再谈编程语言设计和实现
【zhangqi-lang】【13】重学编译原理!新语言大改版!
【i think of you】【1】语法设计,懒得写BNF,只有例子
【fun】【1】超级精简的编程语言定义
;
新语言风格 自我提问- 条件判断中的条件语句需要用小括号扩着吗?
- 语句块用花括号包着吗?
- 设计的动态语言,需要可以设定类型吗?
- 有了类需要结构体吗?
- 如何设计自带的多线程、协程、触发结构(中断结构)、正则?
- 需要匿名函数、lambda表达式吗?
- 类的设计中,所有成员都公开吗?如果可以不公开,需要protected吗?
- 为了支持更多的数学运算,可以使用哪些操作符?
- 自己做的语言,在哪个功能上可以显得特别?而且要么很有用,要么有象征意义!
- 标识符除了字母、数字、下划线,还能添加什么符号?
- 数字开头的标识符为什么不能是有效的?
- 需要重载符号的功能吗?
- 什么叫编程语言是给人看的,不是给机器看的?
;
确定风格 操作符及其功能. 属性
.. ?
... 可变参数
, 表达式分隔符号
: 语句块开始符号或范围符号或字典符号
:: ?
:= ?
; 语句分隔符号
( 表达式符号或函数符号
) 表达式符号或函数符号
[ 数组符号
] 数组符号
{ 字典符号
} 字典符号
# ?
#{x} 字符串内替换为实际值
$ 符合自然语言顺序逻辑的函数调用符号
@ 确定变量类型
! 表达式的非
!= 值不等于
!== 值或类型不等于
~ 位异或
| 位或
|| 表达式的或
|x| 表达式x的绝对值或模
& 位与
&& 表达式的与
+ 加
+= 叠加
- 减
-= 叠减
-> ?
* 乘
** ?
*= 叠乘
/ 除
/= 叠除
/d+/gim 正则表达式,g全局匹配,i忽略大小写,m多行匹配
% 求余
%= 叠求余
^ 乘方
^= 叠乘方
= 左赋值
== 值等于
=> 右赋值
=== 值和类型都等于
< 小于
<- ?
<= 小于等于
<< 位左移
<<< ?
> 大于
>= 大于等于
>> 位右移
>>> ?
x?y:z 三目运算
? ?
'x' 单引号字符串
"x" 双引号字符串
'''x''' 三单引号字符串
"""x""" 三双引号字符串
`x` 特殊标识符
转义符或多行连接符
;
关键字import export from as def public private class enum switch case ok fin using namespace do while for if elif else in or not and break continue redo next true false Nil NaN Undefined Inf BEGIN END new del let const static yield when im var int float double string array dict func
;
字面量字面量由变量、数字、字符串、true和false关键字、数组本身、字典本身组成。
;
标识符普通标识符以字母或下划线开头,以字母、下划线、数字的混合做内容。
特殊标识符用重音符号包裹,内容可以是任何符号——也许会有问题,用不同编辑器打开后,可能其中的符号被编辑器篡改。。。
`一箭穿@->心` `δΓΞÄÅÍㅙ` `ㅝ㈀㈁НСЬ⒋⒙ⅲ` `ⅲ` _49say _00
;
注释//xxx 单行注释
;
数据类型int float double string bool range func enum class array dict
;
数学运算1 + 2 * 3 / 4 - (23 ^ 12 % 1)
;
字符串表达式'hel' + 'lo' + ",w" + '''orld'''+"""!SuAtin"""
;
正则表达式/d+w+(W)[a-z]*/i
;
判断表达式a >= c <= d + e === func() != h
;
逻辑表达式a and b && c || d or e and not w and ! q
;
位运算a <<= (d >> 8) ~ g
;
变量定义let a let a,b=12 let a,b@int,c let const a = "hello" let static a = 1 // let a=1,b=2 let c=a+b
;
基本语句介绍 循环语句let a = 1 while a < 100 : ++a else: a = 1 ok // do a += 1 while a >= 100 ok //for ① for i = 1..2.10 do if i == 7 : break elif i == 3 : a += 2 else a += 1 ok ok //for ② for i=0;i<100;i+=1 do ok //for ③ for i in [1,2,3] do ok
;
switch语句switch a: case 1 => a=2 others => a=0 case 2 => a=3 case 4 | 5 => a = 6 case 7:10 => a = 11 case 10:2:20 => 21+a=>a ok
;
函数定义- 无匿名函数和lambda表达式
- 同名函数根据确定的形参个数区分,不看是否有可变参数
class Sprite: def fun() = 0 //接口 ok // def fun(a): print(a) ok // def fun(a=23,b): //提供默认值的形参放形参列表的前面 ok // def fun(a,...): //可变参数,这个函数违法,与前面的fun(a)无法区分 ok // def fun(a,b@int,c@string)@bool : //确定类型 ok
;
函数调用 普通函数调用属性用点分隔,在执行属性前,前调用前面的函数部分。
函数部分依次放入栈,然后逆顺序执行,将函数一个个弹出栈。
然后函数返回结果调用后面的属性,属性碰巧也是函数调用。
函数调用的返回结果调用后面的属性。。。
//普通的单个函数调用
fun(1,2,"hello")
//普通的多个函数调用
fun(say(input('please:')),hello('你好'))
//普通的多个属性和多个函数混合调用
fun(isbegin()).run().play(key(screen_handle))
自然语言顺序的函数调用
$ 前面必须要有参数,作为后面函数的第一个输入参数。如果函数有多个形参,则之后的参数依次放入函数括号内:1$fun(2,3)
也可以将所有参数放入$ 前面的括号内:(1,2)$fun(3)
or
(1,2,3)$fun
在点和美元符号混合的语句中,先按照从前往后的规则执行!!!
第二点,如果点后面的属性很长,其中有$符号,那么先处理后面的属性!!!
比如下面第三个例子:
isbegin调用结果输入fun,fun调用结果的run属性也是函数调用,
run调用结果的属性是 screen_handle$key$play ,先处理这个属性,
即将screen_handle输入key,key调用结果输入play,play是这个属性最后一个函数,
和前面的run结果相呼应,确定这个play是run结果的play,再执行run结果的play函数!!!
//自然语言顺序的单个函数调用
1$fun(2,"hello")
或
(1,2)$fun("hello")
或
(1,2,"hello")$fun
//自然语言顺序的多个函数调用
'please'$input$say$fun('你好'$hello)
或
('please'$input$say,'你好'$hello)$fun
//自然语言顺序的的多个属性和多个函数混合调用
isbegin()$fun.run().screen_handle$key$play
;
普通语句块let i = 0 do print(i) let i = 12 print(i) ok print(i) Out[1]:0120
;
枚举enum StateType: sa,sb, //枚举元素没有值 sc, sd ok let a = StateType.sa //定义枚举类型变量 a = 12 //还是能改值和类型 let a@StateType = StateType.sa //固定类型 a = 12 违法 let a = sa //也可以直接写枚举元素
;
类——封装,继承,多态,作用域限定,接口class Object:
def exec()=0 //默认public
ok
class Engine(Object):
private:
let a,b
let c@bool = true
public:
def exec():
print('runn')
ok
ok
;
数组let a = [1,2,3,'hello'] let a@array = [3,4] let a[3][3]@array = [ [1,2,3], [4,5,6], [7,8,9] ] b = a[1:2:45] c = a[:1] //没有负数,只能从前至后,边界0和size-1
;
字典let a = {
a : fun,
'2':3
false:true
}
;
范围let a = 1..100 //1.2.3.4.5 .... 100 let a = 1..2..100 //1.3.5.7.
;
命名空间namespace E3D: ... fin // using E3D E3D.fun()
;
模块的构造与析构语句块一个文件中可以有多个构造块或析构块,他们的并列关系,不能包含,也不能放在命名空间里面!!!
他们之间的执行顺序是从前至后!!!
BEGIN:
printf('program start1')
ok
END:
printf('program close1')
ok
BEGIN:
printf('program start2')
ok
END:
printf('program close2')
ok
Out[2]:program start1program start2program close1program close2
;
built-in 内置函数typeof() sizeof() intanceof() input() print() printf() log() write() size() //与关键字类型重名的函数也是被允许的 //定义关键字重名的变量也被允许 int() float() double() string() bool() // valueof() indexof() lastindexof() trim() substr() split() replace() match() uppercase() lowercase() sort() ...
;
;
BNF文法终结符用单引号包裹着!
中括号表示出现0或1次!
花括号表示出现至少0次!
program := chuck { chuck }
chuck := block | statement
statement := ((id_def_stat | assign_stat | expression | funcall |
special_funcall | return_stat | del_stat
)end_stat)
| 'break' | 'next' | 'continue' | 'redo'
end_stat := ';' | 'n' | 'ok' | 'fin'
id := (_|a-z|A-Z) { _ | a-z | A-Z | 0-9 } |
'`' any chars '`'
del_stat := 'del' id
type := 'int'|'float'|'double'|'string'|'bool'|
'array'|'range'|'dict'|'func'|'class'|'enum'
type_def := '@' type
id_def_stat := 'let' ['const'|'static'] id
[type_def] '=' assign_stat end_stat
assign_stat := id { '=' id } [
('=' | '+=' | '-=' | '*=' | '/=' | '%=' | '^=' )
( expression | array_def | dict_def)
]
expression := ['+'|'-'] exp_unit { biop exp_unit }
exp_unit := NUM | STR | 'true' | 'false' | id |
'(' ['+'|'-'] exp_unit ')' |
funcall |
monop exp_unit
monop := '!' | 'not'
biop := '+'|'-'|'*'|'/'|'%'|'^'|'|'|'&'|'~'|'<<'|'>>'|
'and'|'or'
NUM := positive_number
positive_number := num_unit { num_unit2 } num_unit
[ '.' num_unit { num_unit2 } num_unit ]
[ 'e' | 'E' ['+'|'-']
num_unit { num_unit2 } num_unit ]
num_unit := '0'-'9'
num_unit2 := '0'-'9'|'_'
STR := OMIT THE DEFINE
id_list := id { ',' id }
exp_list := expression { ',' expression }
return_stat := 'return' exp_list
funcall := id '(' [exp_list] ')' |
( id | '(' exp_list ')' ) '$' id [ '(' exp_list ')' ]
array_def
;
安全or方便条件表达式的位置可以用赋值表达式代替吗?
默认的静态类型转换需要被禁止吗?
为了像JS一样,几乎怎么执行都没错,我还是选择方便!
——C++写类的时候,学的越多,我写的C++代码就越丑陋,太多东西需要考虑了——
解决JS关于空的问题Maybe
0 == "" false 0的值是0,""值是Nil "" == [] true []值是Nil 0 == [] false "0" == [] false "0"的值是0 "0" == "" false "0" == 0 true



