解释器和编译器的作用和区别_java解释器和编译器的区别

解释器和编译器的作用和区别_java解释器和编译器的区别编程语言原理与编译简介这是一个名为MicroC的编译原理大作业,主要基于microC和Yuby完成,通过对解释器和编译器的代码改进和开发,实现了部分C语言的语法(并改进了部分)

编程语言原理与编译

简介

这是一个名为MicroC的编译原理大作业,主要基于microC和Yuby完成,通过对解释器和编译器的代码改进和开发,实现了部分C语言的语法(并改进了部分)。主要完成功能如下:

项目说明

结构

  • CLex.fsl生成的CLex.fs词法分析器。

    • CLex 中定义了基本的关键字、标识符、常量、使用大写字母表示

      程序读到这个符号就会转换为我们定义的大写字母,然后就给 CPar 处理

  • CPar.fsy生成的CPar.fs语法分析器。

    • CPar 文件分为两部分,每个部分之间通过 %% 分隔

    • 第一部分声明需要使用的变量(词元),声明变量后还需要声明优先级

    • 第二部分定义语法规则(文法)

      包括 : statement ,expression ,function ,main ,vardeclare variabledescirbe ,type ,const这些基本元素

      表示识别到前面定义的这些大写字母组成的符号串后,怎么处理这些规则

  • Absyn.fs 定义了抽象语法树

    定义了变量描述、函数和类型的构造方法

  • Comp.fs将抽象语法树转化为栈式虚拟机

  • interp.fs对抽象语法树进行语义分析

  • Machine.fs 虚拟机指令定义

  • Machine.java 执行虚拟机指令

  • Machine.java生成Machine.class虚拟机与Machinetrace.class堆栈追踪

测试集:测试程序放在example文件夹内

使用方法

编译器

生成汇编指令数字文件 .out (任选以下两文件之一)

dotnet restore microc.fsproj // 可选 dotnet clean microc.fsproj // 可选 dotnet build microc.fsproj // 构建 ./bin/Debug/net5.0/microc.exe dotnet run -p microc.fsproj example/ex1.c // 执行编译器,编译 ex1.c,并输出 ex1.out 文件 dotnet run -p microc.fsproj -g example/ex1.c // -g 查看调试信息 

虚拟机的构建与运行

javac -encoding UTF-8 Machine.java java Machine ex9.out 3//直接显示结果  java Machinetrace ex9.out 0//查看栈式虚拟机每一步的细节 

解释器和编译器的作用和区别_java解释器和编译器的区别

在实际使用中发现,如果不加-encoding UTF-8,会造成编辑提示错误。

解释器

**运行编译解释器 interpc.exe **

dotnet restore interpc.fsproj // 可选 dotnet clean interpc.fsproj // 可选 dotnet build -v n interpc.fsproj // 构建 ./bin/Debug/net5.0/interpc.exe ```http://www.biyezuopin.vip **执行解释器** 

./bin/Debug/net5.0/interpc.exe ex_all.c
dotnet run -p interpc.fsproj ex_all.c
dotnet run -p interpc.fsproj ex_all.c //-g 显示token AST 等调试信息

 ## 功能实现 #### 1.变量初始化 - 简介: 变量声明后同时可以初始化变量的值 - 语法分析: ```F# | Vardec ASSIGN Expr SEMI StmtOrDecSeq { DecAndAssign (fst $1, snd $1, $3) :: $5 } 
  • 抽象语法树:

    | DecAndAssign of typ * string * expr 
  • 语义分析(解释器):

    and stmtordec stmtordec locEnv gloEnv store = match stmtordec with | Stmt stmt -> (locEnv, exec stmt locEnv gloEnv store) | Dec (typ, x) -> allocate (typ, x, None) locEnv store | DecAndAssign (typ, name, expr) -> allocate (typ, name, Some(fst (eval expr locEnv gloEnv store))) locEnv store 
  • 编译器:

    and cStmtOrDec stmtOrDec (varEnv: VarEnv) (funEnv: FunEnv) : VarEnv * instr list = match stmtOrDec with | Stmt stmt -> (varEnv, cStmt stmt varEnv funEnv) | Dec (typ, x) -> allocateWithMsg Locvar (typ, x) varEnv | DecAndAssign (typ, x, e) -> //定义时赋值 let (varEnv, code) = allocate Locvar (typ, x) varEnv (varEnv, code @ (cExpr (Assign((AccVar x), e)) varEnv funEnv) @ [ INCSP -1 ]) 
  • 例子:

    void main(){ 
          int a = 3; print a; } 
  • 堆栈图

    解释器和编译器的作用和区别_java解释器和编译器的区别

2.float|double类型(解释器)
  • 简介:

    float:单精度浮点型,识别格式为’数字’+’.’+‘数字’+‘f(F)’,在栈中占一个地址单位

    double:双精度浮点型,识别格式为’数字’+’.’+‘数字’,通过iee754转化为64位二进制,拆分成两个32位二进制数后再转化为两个十进制,在栈中占两个地址单位

  • 语法分析:
    http://www.biyezuopin.cc

    | ConstFloat { CstF $1 } | ConstDouble { CstD $1 } ConstFloat: CSTFLOAT { $1 } | MINUS CSTFLOAT { - $2 } ; ConstDouble: CSTDOUBLE { $1 } | MINUS CSTDOUBLE { - $2 } ; | FLOAT { TypF } | DOUBLE { TypD } 
  • 抽象语法树:

    | TypF | TypD | CstF of float | CstD of double 
  • 语义分析(解释器):

    // memData | FLOAT of float | DOUBLE of double member this.float = match this with | FLOAT i -> i | INT i -> float i | DOUBLE i -> float i | STRING i -> float i | _ -> failwith ("wrong float") member this.double = match this with | DOUBLE i -> i | INT i -> double i | _ -> failwith ("wrong double") | CstF i -> (FLOAT(i), store) | CstD i -> (DOUBLE(i), store) 
  • 例子:

    void main() { 
          double i = 2.21; print i; float j = 3.21; print j; } 
3.float|double|long|char(编译器)
  • 简介:

    float:单精度浮点型,识别格式为’数字’+’.’+‘数字’+‘f(F)’,通过IEEER32.24转化为32位二进制,在栈中占一个地址单位

    double:双精度浮点型,识别格式为’数字’+’.’+‘数字’,通过IEEE R64.53转化为64位二进制,拆分成两个32位二进制数后再转化为两个十进制,在栈中占两个地址单位

    long:长整型,识别格式为’数字’+‘l(L)’,通过字节运算拆分成两个32位,交给虚拟机处理,在栈中占两个地址单位

    char:字符型,识别格式为’’’+‘字符’+’’,在栈中占一个地址单位

  • 例子:

    //ex4.c void main() { 
          long a = L; print(a); float b = 2.55F; print(b); char c = 's'; print(c); double d = 3.666; print(d); } 

    (参照int类型改进)

    添加一个新类型要在Machine.fs中添加对应的汇编指令、机器码、获得标签在机器码中的地址、得到标签所在地址的函数、反编译等内容。

    在java虚拟机中,在Instruction.java中添加对应指令。

    在Machine.java中根据指令类型,对数据类型进行压栈、加减乘除、判断大小等内容的补充。

  • 堆栈图

    解释器和编译器的作用和区别_java解释器和编译器的区别

    float:,转化为二进制就是2.55的IEEER32.24(IEEE 754)二进制码

    double: ,前一个数和后一个数分别转化为32位二进制,拼接起来就是2.55的IEEE R64.53二进制码

4.boolean(编译器)
  • 简介:boolean类型,true为1,false为0

  • 词法定义:

    | "boolean" -> BOOLEAN | "true" -> CSTBOOL 1 | "false" -> CSTBOOL 0 
  • 语法分析:

    | BOOLEAN { TypB } //= 
  • 抽象语法树:

    | TypB (*boolean*) Const: CSTINT { $1 } | CSTBOOL { $1 } 
  • 例子

    void main() { 
          boolean flag=true; if (flag) { 
          print(1); } if (!flag) { 
          print(0); } } 
  • 堆栈图

    解释器和编译器的作用和区别_java解释器和编译器的区别

5.string类型(解释器)
  • 简介: 字符串类型, 本项目中将其限定为64位字符串

  • 语法分析:

    | ConstString { CstS $1 } ConstString: CSTSTRING { $1 } ; | STRING { TypS } 
  • 抽象语法树:

    | TypS | CstS of string 
  • 语义分析(解释器)::

    // memData | STRING of string member this.string = match this with | STRING i -> i | INT i -> string i | CHAR i -> string i | POINTER i -> string i | FLOAT i -> string i | DOUBLE i -> string i | CstS i -> (STRING(i), store) 
  • 例子:

    void main() { 
          string i = "编译原理"; print i; } 
6.print
  • 简介: 拓展了 print 的功能,可以输出新增的三种类型(string float double)

  • 语义分析:

    | Prim1 (ope, e1) -> let (i1, store1) = eval e1 locEnv gloEnv store let res = match ope with | "!" -> if i1.int = 0 then INT(1) else INT(0) | "printi" -> if i1 = STRING(i1.string) then printf "%s " i1.string i1 else if i1.float = float(i1.int) then printf "%d " i1.int i1 else printf "%.2f " i1.float i1 | "printc" -> printf "%c " i1.char i1 | _ -> failwith ("unknown primitive " + ope) 
  • 例子:

    同上方 float double string 的例子 
7.for
  • 简介: 类似于 C 的 for 循环

  • 语法分析:

    | FOR Access IN RANGE LPAR Expr COMMA Expr COMMA Expr RPAR StmtM {ForIn($2, $6, $8, $10, $12)} 
  • 抽象语法树:

    | For of expr * expr * expr * stmt 
  • 语义分析(解释器):

    | For(e1,e2,e3,body) -> let (v, store1) = eval e1 locEnv gloEnv store let rec loop store1 = let (v,store2) = eval e2 locEnv gloEnv store1 if v<>INT(0) then loop(snd(eval e3 locEnv gloEnv (exec body locEnv gloEnv store2))) else store2 loop store1 
  • 语义分析(编译器):

    先编译e1赋值,赋值后释放计算时占用的空间,然后进行条件判断e2,若IFNZRO=1执行body语句,相反结束for循环。

    | For(e1, e2, e3, body) -> let labbegin = newLabel() let labtest = newLabel() cExpr e1 varEnv funEnv @ [INCSP -1] @ [GOTO labtest; Label labbegin] @ cStmt body varEnv funEnv @ cExpr e3 varEnv funEnv @ [INCSP -1] @ [Label labtest] @ cExpr e2 varEnv funEnv @ [IFNZRO labbegin] 
  • 例子:

    void main() { 
          int i; for (i = 0; i < 5; i = i + 1) { 
          print i; } } 
  • 运行时堆栈:

    解释器和编译器的作用和区别_java解释器和编译器的区别

8.do while|do until
  • 简介:

    dowhile:先执行一次body中的语句, 不符合条件跳出循环。

    dountil:先执行一次body中的语句, 符合条件跳出循环.

  • 语法分析:

    | DO StmtM WHILE LPAR Expr RPAR SEMI { DoWhile($2, $5) } | DO StmtM UNTIL LPAR Expr RPAR SEMI { DoUntil($2, $5) } 
  • 抽象语法树:

    | DoWhile of stmt * expr | DoUntil of stmt * expr 
  • 语义分析(解释器):

    | DoWhile (body, e) -> let rec loop store1 = let (v, store2) = eval e locEnv gloEnv store1 if v <> INT(0) then loop (exec body locEnv gloEnv store2) else store2 loop (exec body locEnv gloEnv store) | DoUntil (body, e) -> let rec loop store1 = let (v, store2) = eval e locEnv gloEnv store1 if v = INT(0) then loop (exec body locEnv gloEnv store2) else store2 loop (exec body locEnv gloEnv store) 
  • 语义分析(编译器):

    dowhile:与while相似,只需要将body语句第一次执行一遍后即可

    dountil:与dowhile的区别就是dowhile是条件为1则继续进行,而dountil是条件为1则退出循环

    | DoWhile (body, e) -> let labbegin = newLabel () let labtest = newLabel () cStmt body varEnv funEnv @[ GOTO labtest] @[Label labbegin ] @ cStmt body varEnv funEnv @ [ Label labtest ] @ cExpr e varEnv funEnv @ [ IFNZRO labbegin ] | DoUntil (body, e) -> let labbegin = newLabel () let labtest = newLabel () cStmt body varEnv funEnv @[ GOTO labtest] @[Label labbegin ] @ cStmt body varEnv funEnv @ [ Label labtest ] @ cExpr e varEnv funEnv @ [ IFZERO labbegin ] 
  • 例子:

    //ex7.c void main() { 
          int i=3; do{ 
          i+=1; }while(i<3); print(i); do{ 
          i-=1; }until(i<3); print(i); } 
  • 运行时堆栈:

    解释器和编译器的作用和区别_java解释器和编译器的区别

    解释器和编译器的作用和区别_java解释器和编译器的区别

9.三目运算符 :
  • 简介: 类似于 C 的三目运算符:a > b ? a : b

  • 语法分析:

    | Expr QUEST Expr COLON Expr { Prim3($1,$3,$5) } 
  • 抽象语法树:

    | Prim3 of expr * expr * expr 
  • 语义分析(解释器):

    | Prim3(e1, e2, e3) -> let (v, store1) = eval e1 locEnv gloEnv store if v <> INT(0) then eval e2 locEnv gloEnv store1 else eval e3 locEnv gloEnv store1 
  • 语义分析(编译器):

    先编译右值表达式e,判断条件,如果为0,执行e2;如果为1,执行e1。

    | Prim3 (e, e1, e2) -> let labelse = newLabel () let labend = newLabel () cExpr e varEnv funEnv @ [ IFZERO labelse ] @ cExpr e1 varEnv funEnv @ [ GOTO labend ] @ [ Label labelse ] @ cExpr e2 varEnv funEnv @ [ Label labend ] 
  • 例子:

    void main() { 
          int i=3; i > 5 ? i=0 : i=5; print(i); } 
  • 运行时堆栈:

    解释器和编译器的作用和区别_java解释器和编译器的区别

10.+= -= *= /= %=
  • 简介: 类似于C. 变量对自身作四则运算.

  • 语法分析:

    | Access PLUSASSIGN Expr { AssignPrim("+=", $1, $3) } | Access MINUSASSIGN Expr { AssignPrim("-=", $1, $3) } | Access TIMESASSIGN Expr { AssignPrim("*=", $1, $3) } | Access DIVASSIGN Expr { AssignPrim("/=", $1, $3) } | Access MODASSIGN Expr { AssignPrim("%=", $1, $3) } 
  • 抽象语法树:

    | AssignPrim of string * access * expr 
  • 语义分析(解释器):

    | AssignPrim(ope, acc, e) -> let (loc,store1) = access acc locEnv gloEnv store let tmp = getSto store1 loc.pointer let (res,store2) = eval e locEnv gloEnv store1 let num = match ope with | "+=" -> match (tmp) with | INT i -> INT(tmp.int + res.int) | FLOAT i -> FLOAT(tmp.float + res.float) | _ -> failwith ("wrong calu") | "-=" -> match (tmp) with | INT i -> INT(tmp.int - res.int) | FLOAT i -> FLOAT(tmp.float - res.float) | _ -> failwith ("wrong calu") | "*=" -> match (tmp) with | INT i -> INT(tmp.int / res.int) | FLOAT i -> FLOAT(tmp.float / res.float) | _ -> failwith ("wrong calu") | "/=" -> match (tmp) with | INT i -> INT(tmp.int / res.int) | FLOAT i -> FLOAT(tmp.float / res.float) | _ -> failwith ("wrong calu") | "%=" -> match (tmp) with | INT i -> INT(tmp.int % res.int) | FLOAT i -> FLOAT(tmp.float % res.float) | _ -> failwith ("wrong calu") | _ -> failwith("unkown primitive " + ope) (num, setSto store2 loc.pointer num) 
  • 语义分析(编译器):

    首先取得变量e1的位置,DUP一份后取值,再编译右值表达式e2,最后match操作进行加减乘除后赋值。

    | AssignPrim (ope, e1, e2) -> cAccess e1 varEnv funEnv @[DUP;LDI] @ cExpr e2 varEnv funEnv @ (match ope with | "+=" -> [ ADD;STI ] | "-=" -> [ SUB;STI ] | "*=" -> [ MUL;STI ] | "/=" -> [ DIV;STI ] | _ -> raise (Failure "unknown AssignPrim")) 
  • 例子:

    void main() { 
          int i = 3; i += 1; print(i); i -= 1; print(i); i *= 2; print(i); i = i/2; print(i); } 
  • 运行时堆栈:

    解释器和编译器的作用和区别_java解释器和编译器的区别

    解释器和编译器的作用和区别_java解释器和编译器的区别

11.switch-case-default
  • 简介: 类似于C. 根据switch中的变量表达式, 找到相同的case值, 若全都不符合, 则执行default中的表达式, 且default表达式必须写在最后.

  • 语法分析:

    | FOR LPAR Expr SEMI Expr SEMI Expr RPAR StmtU { For($3, $5, $7, $9) } CaseList: { [] } | CaseDec { [$1] } | CaseDec CaseList { $1 :: $2 } | DEFAULT COLON StmtM { [Default($3)] } CaseDec: CASE Expr COLON Stmt { Case($2,$4) } 
  • 抽象语法树:

    | Switch of expr * stmt list | Case of expr * stmt | Default of stmt 
  • 语义分析(解释器):

     | Switch(e, body) -> let (v, store0) = eval e locEnv gloEnv store let rec carry list = match list with | Case(e1, body1) :: next -> let (v1, store1) = eval e1 locEnv gloEnv store0 if v1 = v then exec body1 locEnv gloEnv store1 else carry next | Default(body) :: over -> exec body locEnv gloEnv store0 | [] -> store0 | _ -> store0 (carry body) | Case (e, body) -> exec body locEnv gloEnv store | Default(body) -> exec body locEnv gloEnv store 
  • 语义分析(编译器):

    定义递归函数serchcases,用来匹配cases这个list,分空集,Case和Default三种情况处理。Case需要进行条件判断,EQ相等才执行body语句,否则再次调用函数寻找下一个。Default则直接执行语句后结束。

    | Switch (e, cases) -> let rec searchcases c = match c with | Case (e, body) :: tail -> let labend = newLabel () let labfin = newLabel () [DUP] @ cExpr e varEnv funEnv @ [EQ] @ [ IFZERO labend ] @ cStmt body varEnv funEnv @ [ GOTO labfin ] @ [ Label labend ] @ searchcases tail @ [ Label labfin ] | Default body :: [] -> cStmt body varEnv funEnv | [] -> [] cExpr e varEnv funEnv @ searchcases cases @[INCSP -1] 
  • 例子:

    void main() { 
          switch(5){ 
          case 1:print(1); case 2:print(2); case 3:print(3); default:print(4); } } 
  • 运行时堆栈:

    解释器和编译器的作用和区别_java解释器和编译器的区别

12.自增 自减
  • 语法分析:

    | Access PREINC { Prim4("I++", $1) } | Access PREDEC { Prim4("I--", $1) } | PREINC Access { Prim4("++I", $2) } | PREDEC Access { Prim4("--I", $2) } 
  • 抽象语法树:

    | Prim4 of string * access 
  • 语义分析(解释器):

    | Prim4(ope, acc) -> let (loc, store1) = access acc locEnv gloEnv store let (i1) = getSto store1 loc.pointer match ope with | "I++" -> match (i1) with | INT i -> let res = INT(i1.int + 1) (i1, setSto store1 loc.pointer res) | FLOAT i -> let res = FLOAT(i1.float + 1.0) (i1, setSto store1 loc.pointer res) | _ -> failwith ("wrong calu") | "I--" -> match (i1) with | INT i -> let res = INT(i1.int - 1) (i1, setSto store1 loc.pointer res) | FLOAT i -> let res = FLOAT(i1.float - 1.0) (i1, setSto store1 loc.pointer res) | _ -> failwith ("wrong calu") | "++I"-> match (i1) with | INT i -> let res = INT(i1.int + 1) (res, setSto store1 loc.pointer res) | FLOAT i -> let res = FLOAT(i1.float + 1.0) (res, setSto store1 loc.pointer res) | _ -> failwith ("wrong calu") | "--I"-> match (i1) with | INT i -> let res = INT(i1.int - 1) (res, setSto store1 loc.pointer res) | FLOAT i -> let res = FLOAT(i1.float - 1.0) (res, setSto store1 loc.pointer res) | _ -> failwith ("wrong calu") | _ -> failwith ("unknown primitive " + ope) 
  • 语义分析(编译器):

    I++:先找到变量地址,复制一份后取值,将原值与地址交换(保存一下原值),之后同++i,最后赋值后清除计算结果省空间

    ++i:找到变量地址后dup一份,取值,加常量1,add操作,将计算结果赋值

    | Prim4 (ope, e1) -> (match ope with | "I++" -> cAccess e1 varEnv funEnv @[ DUP;LDI;SWAP;DUP;LDI;CSTI 1; ADD;STI;INCSP -1 ] | "I--" -> cAccess e1 varEnv funEnv @ [ DUP;LDI;SWAP;DUP;LDI;CSTI -1; ADD;STI;INCSP -1 ] | "++I" -> cAccess e1 varEnv funEnv @[ DUP;LDI;CSTI 1; ADD;STI ] | "--I" -> cAccess (e1) varEnv funEnv @ [ DUP;LDI;CSTI -1; ADD;STI ] | _ -> raise (Failure "unknown primitive 4")) 
  • 例子:

    void main(){ 
          int i = 1; print i++; print i; print i--; print i; print ++i; print i; print --i; print i; } 
  • 运行时堆栈:

    解释器和编译器的作用和区别_java解释器和编译器的区别

    解释器和编译器的作用和区别_java解释器和编译器的区别

    ex2中a为1,8个print输出结果应为1 2 2 1 2 2 1 1,栈显示操作如图。

    举i++为例,首先找到i的位置(GETBP+CSTI 0+ADD),DUP一份后LDI取值,SWAP将原值保存。之后DUP一份换到栈顶的i的地址用于取值,再加入常量1,相加后STI赋值,INCSP -1将计算后的值出栈,留下备份的i值用于print。

    [ 4 -999 1 1 2 1 1 ]{27: ADD} i的值 备份的i值 i的地址 i的值 常量1 相 加 
13.for-in-range
  • 简介: 类似于C. 根据switch中的变量表达式, 找到相同的case值, 若全都不符合, 则执行default中的表达式, 且default表达式必须写在最后.

  • 语法分析:

    | FOR Access IN RANGE LPAR Expr COMMA Expr COMMA Expr RPAR StmtM {ForIn($2, $6, $8, $10, $12)} 
  • 抽象语法树:

    | ForIn of access * expr * expr * expr * stmt 
  • 语义分析(解释器):

    | ForIn (var, e1, e2, e3, body) -> let (local_var, store1) = access var locEnv gloEnv store let (start_num, store2) = eval e1 locEnv gloEnv store1 let (end_num, store3) = eval e2 locEnv gloEnv store2 let (step, store4) = eval e3 locEnv gloEnv store3 let rec loop temp store5 = let store_local = exec body locEnv gloEnv (setSto store5 local_var.pointer temp) if temp.int + step.int < end_num.int then let nextValue = INT(temp.int + step.int) loop nextValue store_local else store_local if start_num.int < end_num.int then let intValue = INT(start_num.int) loop intValue store4 else store4 
  • 例子:

    void main() { 
          int i; for i in range (2,10,3) { 
          print i; } } 

技术评价

  • example

    功能 对应测试文件
    变量初始化 Assign.c
    自增、自减 Pre.c
    for循环 for.c
    三目运算符 prim3.c
    switch-case switch.c
    float、double、char、long类型(编译器) type.c
    dowhile、dountil dowhileuntil.c
    += -= *= /= %= Add.c
    boolean boolean.c

参考文档

http://sigcc.gitee.io/plc2021/#/07/microc.compiler 学习指令

解释器和编译器的作用和区别_java解释器和编译器的区别

今天的文章
解释器和编译器的作用和区别_java解释器和编译器的区别分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/60266.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注