编译原理实践 11.语义分析与代码生成
table数组和enter过程的扩充 命令代码生成过程gen 表达式、项、因子及关系式的翻译 语句的翻译 生成PL/0目标代码程序
1. table数组和enter过程的扩充 lod l,a 将l,a形成的栈地址变量值置入栈顶 sto l,a 将栈顶值保存到l,a形成的栈地址变量 l level级别之差 变量所处的程序段静态级别与 变量被说明时的静态级别之差 a offset
必须将variable和procedure的level保存在合适的 地方 cal l,a 调用子程序 l level级别之差 call语句所处的程序段静态级别与 调用过程被说明时的静态级别之差 a 被调用代码程序段的起始地址 必须将variable和procedure的level保存在合适的 地方
改变符号表 struct tablestruct { char name[al]; /* 名字 */ enum object kind; /* 类型:constant,variable或 procedure */ int val; /* 数值,仅const使用 */ int level; /* 所处层,仅const不使用 */ int adr; /* 地址,仅const不使用 */ int size; /* 需要分配的数据区空间, 仅procedure使用 */ }; 常量:val 变量:静态级别,位移地址 过程:程序地址,静态级别,预留存储单元
void enter(enum object k, int. ptx, int lev, int. pdx) { ( void enter(enum object k, int* ptx, int lev, int* pdx) { (*ptx)++; strcpy(table[(*ptx)].name, id); /*符号表的name域记录标识符的名字*/ table[(*ptx)].kind = k; switch (k) case constant: /* 常量 */ if (num > amax) error(31); /* 常数越界 */ num = 0; } table[(*ptx)].val = num; /* 登记常数的值 */ break; case variable: /* 变量 */ table[(*ptx)].level = lev; table[(*ptx)].adr = (*pdx); (*pdx)++; case procedure: /* 过程 */
2.命令代码生成过程gen 每调用一次过程gen,在程序存储器里产生一条代码指令 void gen(enum fct x, int y, int z ) { if (cx >= cxmax) { printf("Program is too long!\n"); /* 生成的虚拟机代码程序过长 */ exit(1); } if ( z >= amax) { printf("Displacement address is too big!\n"); /* 地址偏移 越界 */ code[cx].f = x; code[cx].l = y; code[cx].a = z; cx++;
3.表达式、项、因子及关系式的翻译 表达式的翻译 中缀表达式与后缀表达式 后缀表达式:操作符总是跟随在它操作数的后边 后缀表达式翻译规则 T(“+”term)=T(term) T(“-”term)= T(term) “-” T(term1”+”term2)= T(term1) T(term2) “+” T(term1”-”term2)= T(term1) T(term2) “-” T(term1”*”term2)= T(term1) T(term2) “*” T(term1”/”term2)= T(term1) T(term2) “/” T(“(”expression”)”)=T(expression)
Example 后缀表达式 xy+zw-/ (x+y)/(z-w) Lod x Lod y Opr 0,2 Lod z Lod w
expression过程的扩充 term过程的扩充 factor过程的扩充 condition过程的扩充
4.语句的翻译 T(expression) Sto lev-level, adr 赋值语句的翻译处理 T(Ident”:=”expression) T(expression) Sto lev-level, adr
T(condition) Jpc, L1 T(statement) L1: … if语句的翻译处理 T(“if” condition “then” statement) T(condition) Jpc, L1 T(statement) L1: …
L1: T(condition) Jpc, L2 T(statement) jmp L1 L2: … while语句的翻译处理 T(“while” condition “do” statement) L1: T(condition) Jpc, L2 T(statement) jmp L1 L2: …
call语句的翻译处理 T(“call” ident) cal, lev-level, adr
5.生成PL/0目标代码程序 执行program6 在SI-NS图上执行program6 直接从PL/0程序翻译写出目标代码程序
对分程序的定义: void block(int lev, int tx, bool* fsys) {…} cx0: 保留本过程目标代码的起始位置
其中: 对分程序体入口的处理: dx = 3; /* 三个空间用于存放静态链SL、动态链DL和返 回地址RA */ tx0 = tx; /* 记录本层标识符的初始位置 */ table[tx].adr = cx; /*记录当前层代码的开始位置 */ gen(jmp, 0, 0); /* 产生跳转指令,跳转位置未 知暂时填0 */ 其中: cx已保留在过程名的adr域,等生成过程体入口的指 令时,再由table[tx].adr中找到 cx将过程体入口返 填到cx中,即( jmp,0,0)的第3区域,同时将过程 体入口填到过程名的table[tx].adr中。
分程序的处理 (0) jmp 0 0 table[tx].adr:=cx; CX:(1 ) jmp 0 0 记录 过程在code的入 口到table中的adr域 CONST A=35,B=49; VAR C,D,E; PROCEDURE P; VAR G tx 名字 类型 层次/值 地址 存储空间
过程体入口时的处理 code[table[tx0].adr].a:=cx; (回填过程入口地址到code的a中) with table[tx0] do begin adr:=cx; (过程的入口填 写在table中) size:=dx; (过程占的空间填 写在table中) end; cx0:=cx; gen(int,0,dx);(生成过程入口指令) 保留过程在code中的入口地址,打印目标代码用
过程体入口时的处理 code[table[tx0].adr].a = cx; /* 把前面生成的 跳转语句的跳转位置改成当前位置 */ table[tx0].adr = cx; /*记录当前过程代码地址*/ table[tx0].size = dx; /* 声明部分中每增加一条 声明都会给dx增加1,声明部分已经结束,dx就是 当前过程数据的size */ cx0 = cx; /* 保留过程在code中的入口地址,打印 目标代码用*/ gen(ini, 0, dx); /* 生成指令,此指令执行时在 数据栈中为被调用的过程开辟dx个单元的数据区 */
过程体入口时的处理 CONST A=35;B=49; VAR C,D,E; PROCEDURE P; VAR G (0) jmp 0 0 code[table[tx0].adr] .a=cx; table[tx0].adr =cx; 填写过程在code和 table中的入口地址 (0) jmp 0 0 (1 ) jmp 0 0 . . . ( cx ) int 0 4 tx0 名字 类型 层次/值 地址 存储空间