基于Appel-Tiger编译器前端的简易语法分析器
说明:
本例修改自Appel的Tiger编译器前端。
本例给出某文法的一个简单的语法分析器。它是在parser0的基础上添加语义动作而得到的。
输入:用该文法所表示的语言写的源程序文件。
输出:分析树的数据结构描述,可以显示在屏幕上,也可以写入指定的文件中。
该语言的文法为:(与parser0文法相同)
program--> PROGRAM ID; vardec BEGIN stmts END.
vardec--> VAR declist
declist--> ID:INTEGER;
stmts--> ID := exp
exp--> INT
该语法分析器的测试例(输入文件)为:testcases/test0.p
该语法分析器的词法分析器由lex编写,本例并未给出lex规则源程序,只给出了flex编译后的c语言程序lex.yy.c。本词法分析器支持以(*开始,以*)结束的注释(不支持注释的嵌套)。
parsetest.c为该语法分析器主控程序,包含main函数。
util.c含有一般工具函数,util.h是其头文件。
errormsg.c含有错误管理函数,errormsg.h是其头文件。
parser.y是语法分析器的文法说明文件。 在该文法说明文件中,我们为产生式添加了语义动 作,语义动作为产生语法分析树。
ast.h文件给出了语法分析器所应生成的语法分析树(抽象语法树)的各部分的数据结构;而ast.c则为这些数据结构提供了构造函数(注意:ast.c提供的构造函数并不完整,需要你进行完善)
prast.c文件含有语法分析树各个数据结构的打印函数。prast.h是其头文件。
testcases文件夹下的文件为语法分析器的测试例,test0.p为当前语法分析器可通过的测试例,
test1.p为P178页上机题所示文法的一个测试例。你写的其他测试例也应该放在该文件夹下。
makefile文件表示了本例的编译方法:
在cygwin下,输入make,将会得到一些中间object文件以及可执行文件parser1.exe;
输入 make clean,会将编译及运行中生成的文件删除(只留下原有文件);
编译完成后,输入make test00,将会以testcases/test0.p为输入源程序运行生成的语法分析器parser0.exe,输出的分析树将显示在屏幕上。
编译完成后,输入make test01,将会以testcases/test0.p为输入源程序运行生成的语法分析器parser0.exe,输出的分析树将写入指定文件test0.out中。
用到的文件截图:
源代码:
(1) ast.c
/* 本文件给出抽象语法树上各数据结构的构造函数,实验8需要修改该文件的一些函数 */
#include "ast.h"
/* make id node */
a_id A_Id(a_pos pos, string val){
a_id ret = checked_malloc(sizeof(*ret));
ret->pos = pos;
ret->val = val;
return ret;
}
/* make exp node from integer */
a_exp A_IntExp(a_pos pos, int ival){
a_exp ret = checked_malloc(sizeof(*ret));
ret->kind = A_intExp;
ret->pos = pos;
ret->exp.ival = ival;
return ret;
}
/* make exp node from real */
a_exp A_RealExp(a_pos pos, double fval){
a_exp ret = checked_malloc(sizeof(*ret));
//请完善该函数
ret->kind = A_realExp;
ret->pos = pos;
ret->exp.fval = fval;
return ret;
}
/* make exp node from id node */
a_exp A_VarExp(a_pos pos, a_id var){
a_exp ret = checked_malloc(sizeof(*ret));
//请完善该函数
ret->kind = A_varExp;
ret->pos = pos;
ret->exp.var = var;
return ret;
}
/* make binary operands exp node */
a_exp A_OpExp(a_pos pos, a_op op, a_exp left, a_exp right){
a_exp ret = checked_malloc(sizeof(*ret));
//请完善该函数
ret->kind = A_opExp;
ret->pos = pos;
ret->exp.biopExp.op=op;
ret->exp.biopExp.left=left;
//ret->exp.biopExp.right=right;
ret->exp.biopExp.right=right;
return ret;
}
/* make boolean exp node */
a_bexp A_BExp(a_pos pos, a_bop bop, a_exp left, a_exp right){
a_bexp ret = checked_malloc(sizeof(*ret));
//请完善该函数
ret->pos = pos;
ret->bexp.bop = bop;
ret->bexp.left = left;
ret->bexp.right = right;
return ret;
}
/* make assign statement */
a_stm A_Assign (a_pos pos, a_id var, a_exp exp){
a_stm ret = checked_malloc(sizeof(*ret));
ret->kind = A_assign;
ret->pos = pos;
ret->stm.assign.var = var;
ret->stm.assign.exp = exp;
return ret;
}
/* make if statement */
a_stm A_If(a_pos pos, a_bexp b, a_stm s1, a_stm s2){
a_stm ret = checked_malloc(sizeof(*ret));
//请完善该函数
ret->kind = A_if;
ret->pos = pos;
ret->stm.iff.b=b;
ret->stm.iff.s1=s1;
ret->stm.iff.s2=s2;
return ret;
}
/* make while statement */
a_stm A_While(a_pos pos, a_bexp b, a_stm s){
a_stm ret = checked_malloc(sizeof(*ret));
//请完善该函数
ret->kind = A_while;
ret->pos = pos;
ret->stm.whilee.b=b;
ret->stm.whilee.s=s;
return ret;
}
/* make sequence statement */
a_stm A_Seq(a_pos pos, a_stm_list sl){
a_stm ret = checked_malloc(sizeof(*ret));
//请完善该函数
ret->kind = A_seq;
ret->pos = pos;
ret->stm.seq=sl;
return ret;
}
/* make statement list */
a_stm_list A_StmList(a_stm s, a_stm_list sl){
a_stm_list ret = checked_malloc(sizeof(*ret));
ret->head = s;
ret->tail = sl;
return ret;
}
/* make var list */
a_var_list A_VarList(a_id v, a_var_list vl){
a_var_list ret = checked_malloc(sizeof(*ret));
ret->head = v;
ret->tail = vl;
return ret;
}
/* make variable declaration node */
a_dec A_VarDec(a_pos pos, a_var_list vl, ttype t){
a_dec ret = checked_malloc(sizeof(*ret));
ret->type = t;
ret->pos = pos;
ret->varlist = vl;
return ret;
}
/* make variable declaration list */
a_dec_list A_DecList(a_dec vd, a_dec_list vdl){
a_dec_list ret = checked_malloc(sizeof(*ret));
ret->head = vd;
ret->tail = vdl;
return ret;
}
/* make program node */
a_prog A_Prog (a_pos pos, char * name, a_dec_list dl, a_stm_list sl){
a_prog ret = checked_malloc(sizeof(*ret));
ret->name = name;
ret->pos =pos;
ret->declist = dl;
ret->stmlist = sl;
return ret;
}
(2) ast.h
/* 定义抽象语法树(分析树)的数据结构 */
#include "util.h"
typedef int a_pos; //a_pos为源文件某记号的位置类型,报错用。
typedef struct a_exp_ * a_exp;
typedef struct a_bexp_ * a_bexp;
typedef struct a_id_ * a_id;
typedef struct a_stm_ * a_stm;
typedef struct a_stm_list_ * a_stm_list;
typedef struct a_dec_ * a_dec;
typedef struct a_dec_list_ * a_dec_list;
typedef struct a_var_list_ * a_var_list;
typedef struct a_prog_ * a_prog;
/* 变量类型有整型T_int和实型T_real两种 */
typedef enum{T_int, T_real} ttype;
/* 二元算术运算有四种:+ - * / */
typedef enum {A_plusOp, A_minusOp, A_timesOp, A_divideOp} a_op;
/* 二元布尔运算六种:=, <>, <, <=, >, >= */
typedef enum {A_eqOp, A_neqOp, A_ltOp, A_leOp, A_gtOp, A_geOp} a_bop;
/* id的数据结构,pos为位置,val为id的名字 */
struct a_id_ {
a_pos pos;
string val;
};
/* 算术表达式数据结构。
* kind为表达式的类型,分为变量、整型数、实型数和二元表达式
*/
struct a_exp_ {
enum {A_varExp, A_intExp, A_realExp, A_opExp} kind;
a_pos pos;
union {
struct {
a_op op;
a_exp left;
a_exp right;
}biopExp; //二元运算表达式:left op right
a_id var; //变量表达式
int ival; //整型数表达式
double fval;//实型数表达式
}exp;
};
/* 布尔表达式数据结构 */
struct a_bexp_{
a_pos pos;
struct {
a_bop bop;
a_exp left;
a_exp right;
} bexp;
};
/* 语句数据结构。
* 类型分为赋值语句、if语句、while语句、顺序语句(语句序列)
*/
struct a_stm_ {
enum {A_assign, A_if, A_while, A_seq} kind;
a_pos pos;
union {
struct a_assign_stm_ {
a_id var;
a_exp exp;
} assign;//赋值语句:var = exp;
struct a_if_stm_ {
a_bexp b;
a_stm s1;
a_stm s2;
} iff;// if语句:if b then s1 else s2;
struct a_while_stm_ {
a_bexp b;
a_stm s;
} whilee;// while语句:while b do s;
a_stm_list seq;//顺序语句:是若干顺序的语句的一个列表。
} stm;
};
/* 语句列表数据结构 */
struct a_stm_list_ {
a_stm head;
a_stm_list tail;
};
/* 变量列表数据结构 */
struct a_var_list_ {
a_id head;
a_var_list tail;
};
/* 变量声明数据结构 。
* 变量类型为type,变量列表为varlist。
* 例:声明var a, b, c : INTEGER;
* 其中a,b,c组成varlist,type为T_int(表示INTEGER)。
*/
struct a_dec_ {
ttype type;
a_pos pos;
a_var_list varlist;
};
/* 变量声明列表数据结构 */
struct a_dec_list_ {
a_dec head;
a_dec_list tail;
};
/* 程序数据结构。
* name为程序名,declist为程序变量声明列表 */
struct a_prog_ {
char * name;
a_pos pos;
a_dec_list declist;
a_stm_list stmlist;
};
/* 下面的函数是各个语法结构的构造函数 */
a_id A_Id(a_pos pos, string val);
a_exp A_IntExp(a_pos pos, int ival);
a_exp A_RealExp(a_pos pos, double fval);
a_exp A_VarExp(a_pos pos, a_id var);
a_exp A_OpExp(a_pos pos, a_op op, a_exp left, a_exp right);
a_bexp A_BExp(a_pos pos, a_bop bop, a_exp left, a_exp right);
a_stm A_Assign (a_pos pos, a_id var, a_exp exp);
a_stm A_If(a_pos pos, a_bexp b, a_stm s1, a_stm s2);
a_stm A_While(a_pos pos, a_bexp b, a_stm s);
a_stm A_Seq(a_pos pos, a_stm_list sl);
a_stm_list A_StmList(a_stm s, a_stm_list sl);
a_var_list A_VarList(a_id v, a_var_list vl);
a_dec A_VarDec(a_pos pos, a_var_list vl, ttype t);
a_dec_list A_DecList(a_dec vd, a_dec_list vdl);
a_prog A_Prog (a_pos pos, char * name, a_dec_list dl, a_stm_list sl);
(3)errormsg.c
/*
* errormsg.c - functions used in all phases of the compiler to give
* error messages about the program.
*
*/
/* 本文件定义各种错误管理操作 */
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "util.h"
#include "errormsg.h"
extern void resetLexState();
bool anyErrors= FALSE;
static string fileName = "";
static int lineNum = 1;//行号,用于报错
int EM_tokPos=0;
extern FILE *yyin;
extern FILE *yyout;
extern int yydebug;//bison中用于查错的标记,yydebug非0时,输出错误信息。
typedef struct intList {int i; struct intList *rest;} *IntList;
static IntList intList(int i, IntList rest)
{IntList l= checked_malloc(sizeof *l);
l->i=i; l->rest=rest;
return l;
}
static IntList linePos=NULL; //行内位置,用于报错。
/* 换行时,处理行号和行内位置的函数。 */
void EM_newline(void)
{lineNum++;
linePos = intList(EM_tokPos, linePos);
}
/* 报错函数,message是错误信息格式串,pos是错误位置。 */
void EM_error(int pos, char *message,...)
{va_list ap;//可变参数列表指针
IntList lines = linePos;
int num=lineNum;
anyErrors=TRUE;
while (lines && lines->i >= pos)
{lines=lines->rest; num--;}
if (fileName) fprintf(stderr,"%s:",fileName);
if (lines) fprintf(stderr,"%d.%d: ", num, pos-lines->i);
va_start(ap,message);//ap指向message这个参数的地址
//格式打印到文件stderr中,格式串为message,所需的参数由ap指针指向
vfprintf(stderr, message, ap);
va_end(ap);//ap指针归零
fprintf(stderr,"\n");
}
/* 重置关于错误管理的一些设置。 */
void EM_reset(string fname, string output)
{
anyErrors=FALSE; fileName=fname; lineNum=1;
linePos=intList(0,NULL);
yyin = fopen(fname,"r");
if (output){
yyout = fopen(output, "w");
}
yyrestart(yyin);//用于重置缓冲区的内容
/* 在flex中,函数 void yyrestart(FILE * newfile) 会将yyin的指针指向newfile的开始处,并且,
* 原lex输入缓冲区中的内容全部抹掉。这可以用来重启yylex()。(因为当文件读取一半时,重新
* 调用yylex仍会接着原文件的位置分析,即使重新制定yyin,缓冲区内的内容也还是原来的内容)
* 该函数不会重置lex的状态为INITIAL。
*/
resetLexState(); //重置lex状态为INTIAL。
if (!yyin) {EM_error(0,"cannot open"); exit(1);}
}
/* 此函数用于设定语法分析查错 */
void EM_yydebug(){
yydebug = 1;
}
(4)errormsg.h
extern bool EM_anyErrors;
void EM_newline(void);
extern int EM_tokPos;
void EM_error(int, string,...);
void EM_reset(string filename, string output);
void EM_yydebug();
(5)lex.yy.c
#line 3 "lex.yy.c"
#define YY_INT_ALIGNED short int
/* A lexical scanner generated by flex */
#define FLEX_SCANNER
#define YY_FLEX_MAJOR_VERSION 2
#define YY_FLEX_MINOR_VERSION 5
#define YY_FLEX_SUBMINOR_VERSION 35
#if YY_FLEX_SUBMINOR_VERSION > 0
#define FLEX_BETA
#endif
/* First, we deal with platform-specific or compiler-specific issues. */
/* begin standard C headers. */
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
/* end standard C headers. */
/* flex integer type definitions */
#ifndef FLEXINT_H
#define FLEXINT_H
/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
* if you want the limit (max/min) macros for int types.
*/
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS 1
#endif
#include <inttypes.h>
typedef int8_t flex_int8_t;
typedef uint8_t flex_uint8_t;
typedef int16_t flex_int16_t;
typedef uint16_t flex_uint16_t;
typedef int32_t flex_int32_t;
typedef uint32_t flex_uint32_t;
#else
typedef signed char flex_int8_t;
typedef short int flex_int16_t;
typedef int flex_int32_t;
typedef unsigned char flex_uint8_t;
typedef unsigned short int flex_uint16_t;
typedef unsigned int flex_uint32_t;
#endif /* ! C99 */
/* Limits of integral types. */
#ifndef INT8_MIN
#define INT8_MIN (-128)
#endif
#ifndef INT16_MIN
#define INT16_MIN (-32767-1)
#endif
#ifndef INT32_MIN
#define INT32_MIN (-2147483647-1)
#endif
#ifndef INT8_MAX
#define INT8_MAX (127)
#endif
#ifndef INT16_MAX
#define INT16_MAX (32767)
#endif
#ifndef INT32_MAX
#define INT32_MAX (2147483647)
#endif
#ifndef UINT8_MAX
#define UINT8_MAX (255U)
#endif
#ifndef UINT16_MAX
#define UINT16_MAX (65535U)
#endif
#ifndef UINT32_MAX
#define UINT32_MAX (4294967295U)
#endif
#endif /* ! FLEXINT_H */
#ifdef __cplusplus
/* The "const" storage-class-modifier is valid. */
#define YY_USE_CONST
#else /* ! __cplusplus */
/* C99 requires __STDC__ to be defined as 1. */
#if defined (__STDC__)
#define YY_USE_CONST
#endif /* defined (__STDC__) */
#endif /* ! __cplusplus */
#ifdef YY_USE_CONST
#define yyconst const
#else
#define yyconst
#endif
/* Returned upon end-of-file. */
#define YY_NULL 0
/* Promotes a possibly negative, possibly signed char to an unsigned
* integer for use as an array index. If the signed char is negative,
* we want to instead treat it as an 8-bit unsigned char, hence the
* double cast.
*/
#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
/* Enter a start condition. This macro really ought to take a parameter,
* but we do it the disgusting crufty way forced on us by the ()-less
* definition of BEGIN.
*/
#define BEGIN (yy_start) = 1 + 2 *
/* Translate the current start state into a value that can be later handed
* to BEGIN to return to the state. The YYSTATE alias is for lex
* compatibility.
*/
#define YY_START (((yy_start) - 1) / 2)
#define YYSTATE YY_START
/* Action number for EOF rule of a given start state. */
#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
/* Special action meaning "start processing a new file". */
#define YY_NEW_FILE yyrestart(yyin )
#define YY_END_OF_BUFFER_CHAR 0
/* Size of default input buffer. */
#ifndef YY_BUF_SIZE
#define YY_BUF_SIZE 16384
#endif
/* The state buf must be large enough to hold one stat