编程语言原理与编译
1. 项目自评等级:(1-5) 请根据自己项目情况填写下表
1.1 解释器
功能 | 完善程度 | 自评 | 备注 |
---|---|---|---|
bool 类型 | 5 | 1 | |
float 类型 | 5 | 2 | |
char 类型 | 5 | 2 | |
For 循环 | 5 | 4 | |
DoWhile 循环 | 5 | 4 | |
DoUntil 循环 | 5 | 4 | |
switch-case | 5 | 3 | |
模式匹配 match | 5 | 3 | |
自增自减 ++、– | 5 | 3 | 可以识别 i++ 和 ++i |
三目运算符?: | 5 | 3 | |
+= -= *= /=语法糖 | 5 | 3 | |
位运算 << >> | 5 | 3 | |
占位符 pass | 5 | 2 | |
break | 5 | 4 | |
Continue | 5 | 4 | |
print 重写 | 5 | 3 |
1.2 编译器
功能 | 完善程度 | 自评 | 备注 |
---|---|---|---|
bool 类型 | 5 | 1 | |
float 类型 | 5 | 2 | |
char 类型 | 5 | 2 | |
For 循环 | 5 | 4 | |
DoWhile 循环 | 5 | 4 | |
DoUntil 循环 | 5 | 4 | |
switch-case | 5 | 3 | |
模式匹配 match | 5 | 3 | |
自增自减 ++、– | 5 | 3 | 可以识别 i++ 和 ++i |
三目运算符?: | 5 | 3 | |
+= -= *= /=语法糖 | 5 | 3 | |
位运算 << >> | 5 | 3 | |
占位符 pass | 5 | 2 | |
break | 5 | 4 | |
Continue | 5 | 4 | |
print 重写 | 5 | 3 | |
Java 虚拟机 | 5 | 4 | 新增了 11 条指令及新 print 和新类型的实现 |
1.3 优化编译器
功能 | 完善程度 | 自评 | 备注 |
---|---|---|---|
bool 类型 | 5 | 1 | |
float 类型 | 5 | 2 | |
char 类型 | 5 | 2 | |
For 循环 | 5 | 4 | |
DoWhile 循环 | 5 | 4 | |
DoUntil 循环 | 5 | 4 | |
switch-case | 5 | 3 | |
模式匹配 match | 5 | 3 | |
自增自减 ++、– | 5 | 3 | 可以识别 i++ 和 ++i |
三目运算符?: | 5 | 3 | |
+= -= *= /=语法糖 | 5 | 3 | |
位运算 << >> | 5 | 3 | |
占位符 pass | 5 | 2 | |
break | 5 | 4 | |
Continue | 5 | 4 | |
print 重写 | 5 | 3 | |
Java 虚拟机 | 5 | 4 | 新增了 11 条指令及新 print 和新类型的实现 |
2. 项目说明
2.1 文件目录结构
- src - Java 虚拟机
- test/testinterpc - 解释器测试样例
- test/testmicroc- 编译器测试样例
- test/testMicroCC- 优化编译器测试样例
- Absyn.fs - 抽象语法
- CLex.fsl - fslex 词法定义
- CPar.fsy - fsyacc 语法定义
- Parse.fs - 语法解析器
- Interp.fs - 解释器
- interpc.fsproj - 解释器项目文件
- Comp.fs - 编译器
- microc.fsproj - 优化编译器项目文件
- Contcomp.fs - 优化编译器
- microcc.fsproj - 优化编译器项目文件
- Machine.fs - 机器指令定义
2.2 项目运行
解释器
//编译解释器 interpc.exe 命令行程序
dotnet restore interpc.fsproj //可选
dotnet clean interpc.fsproj //可选
dotnet build -v n interpc.fsproj //构建,-v n查看详细生成过程
//执行解释器
./bin/Debug/net5.0/interpc.exe 测试文件 参数
dotnet run -p interpc.fsproj 测试文件 参数
dotnet run -p interpc.fsproj -g 测试文件 参数 //显示token AST 等调试信息
编译器
//构建 microc.exe 编译器程序
dotnet restore microc.fsproj //可选
dotnet clean microc.fsproj //可选
dotnet build microc.fsproj //构建
dotnet run -p microc.fsproj 测试文件 // 执行编译器,编译测试文件,并输出.out 文件
dotnet run -p microc.fsproj -g 测试文件 // -g 查看调试信息
./bin/Debug/net5.0/microc.exe -g 测试文件 // 直接执行构建的.exe文件,同上效果
优化编译器
dotnet restore microcc.fsproj
dotnet clean microcc.fsproj
dotnet build microcc.fsproj //构建编译器
dotnet run -p microcc.fsproj 测试文件 //执行编译器
./bin/Debug/net5.0/microcc.exe 测试文件 //直接执行
Java 虚拟机
javac Machine.java
java Machine .out文件 参数
javac Machinetrace.java
java Machinetrace .out文件 参数
java Machinetrace .out文件 参数
2.3 功能实现(具体说明仅在解释器中,编译器和优化编译器不再重复)
解释器
解释器基于原 microc 改进并在其上新增了上表所列的功能并对部分功能进行了改进。
-
bool 类型
通过 bool 关键词定义,只支持赋值为
true
和false
,可以通过print
的'b'
类型来输出。void main(){ bool x; bool k; x=true; k=false; print 'b' : x; print 'b' : k; }
-
char 类型
通过
char
关键词定义,支持赋值使用' '
包裹的一个字符,可以通过print
的'c'
类型来输出。void main(){ char a; a='a'; print 'c' : a; }
-
float 类型
通过
float
关键词定义,支持浮点数赋值,支持向下数据类型转换,可以通过print
的'f'
类型来输出。void main(){ float a; a=1.1111; print 'f' : a; }
-
For 循环
for 循环的语句结构为:
for(表达式 1; 表达式 2; 表达式 3){
语句块
}运行过程为:
- 先执行“表达式 1”
- 再执行“表达式 2”,如果它的值为真(非 0),则执行循环体,否则结束循环。
- 执行完循环体后再执行“表达式 3”。
- 重复执行步骤 2) 和 3),直到“表达式 2”的值为假,就结束循环。
上面的步骤中,2) 和 3) 是一次循环,会重复执行,for 语句的主要作用就是不断执行步骤 2) 和 3)。
void main(int n){ int i; for (i=0;i<n;i=i+1){ print 'd' : i; } print 'd' : n; }
-
DoWhile
dowhile 循环的语句结构为:
do
{
语句块;
}
while (表达式);进入循环时,程序会先执行一次循环体,然后会对表达式进行判断,若表达式满足条件,继续执行,反之退出循环
void main(int n){ int i; i=0; do{ i=i+2; print 'd' : i; }while(i<n); print 'd' : n; }
-
DoUntil
DoUntil 循环的语句结构为:
do
{
语句块;
}
until(表达式);进入循环时,程序会先执行一次循环体,然后会对表达式进行判断,若表达式不满足条件,继续执行,反之退出循环
void main(int n){ int i; i=0; do{ print 'd' : i; i=i+1; }until(i>n); print 'd' : n; }
-
三目运算符
三目运算符的格式为:
<表达式1> ? <表达式2> : <表达式3>;
返回值:先求表达式 1 的值,如果为真,则执行表达式 2,并返回表达式 2 的结果;如果表达式 1 的值为假,则执行表达式 3,并返回表达式 3 的结果。
void main(int n){ int x; x=n>6 ? 0:1; print 'd' : x; }
-
位运算 << >>
>>
右移操作会将运算数的二进制向右移动n
位,<<
左移操作会将运算数的二进制向左移动n
位int main(){ int n; n=30; print 'd' : n; print 'd' :n<<3; print 'd' :n>>2; }
-
switch-case
多分支选择的 switch 语句的格式为
switch(表达式){
case 常量表达式 1: 语句 1;
case 常量表达式 2: 语句 2;
…
case 常量表达式 n: 语句 n;
default: 语句 n+1;
}计算表达式的值, 并逐个与其后的常量表达式值相比较,当表达式的值与某个常量表达式的值相等时, 即执行其后的语句,然后不再进行判断,继续执行后面所有 case 后的语句。如表达式的值与所有 case 后的常量表达式均不相同时,则执行 default 后的语句。
void main(int n){ switch(n){ case 1: {print 'd' : 1;break;} case 2: {print 'd' :0;break;} default: print 'd' : 233; } print 'd' : n; }
-
模式匹配 match
match 语句的格式为
match ecp with:
| 条件 1 -> 语句块 1
…
| _ -> 语句块 n
逐个比较判断体 ecp 与下面哪个条件一致则执行对应语句块,若都不一致,则执行
_
后的语句块 nvoid main(int ecp){ match ecp with | 1 -> {print 'd' : ecp;break;} | 2 -> {print 'd' : ecp+1;break;} | _ -> print 'd' : 0; print 'd' : ecp; }
-
自增自减 ++ –
++
和–可以实现参数的+1
,如I++
等价于I=I+1
其中
I++
表示返回I
的值,然后 I+1,++I
表示先I+1
然后返回 I 的值void main(int n){ print 'd' : n; n=n++; print 'd' : n; n=++n; print 'd' : n; n=n--; print 'd' : n; n=--n; print 'd' : n; }
-
+= -= *= /= 语法糖
I+=n
等价于I=I+n
、I-=n
等价于I=I-n
、I*=n
等价于I=I*n
、I/=n
等价于I=I/n
void main(int n){ int a; a=1; a+=n; print 'd' :a; int b; b=2; b-=n; print 'd':b; int c; c=4; c*=n; print 'd':c; int d; d=6; d/=n; print 'd':d; }
-
占位符 PASS
占位符思想来源于
python
,用于占位,没有实际含义。int main(){ int i; for (i = 0; i < 10; i++) { pass; } print 'c':'1'; }
-
continue
跳过本次循环剩下语句,直接进入下一次循环
void main(){ int i; i=0; do{ i=i+1; if (i%2==0) continue; print 'd' : i; } while(i<100); }
-
print
为了适应新的数据类型,所以我们重写了
print
,在此处我们定义了三种类型符:c-char,d-int,f-floatvoid main(){ print 'd' :1; }