全局定义:
#ifndef _GLOBALS_H_
#define _GLOBALS_H_
#endif
代表只载入一次_GLOBALS_H_
考虑分析中用到的Tokentype,由各种值。所有的token取值都在里面了
typedef enum
/* book-keeping tokens */
{ENDFILE,ERROR,
/* reserved words */
IF,THEN,ELSE,END,REPEAT,UNTIL,READ,WRITE,
/* multicharacter tokens 多字符tokens分为两类ID,NUM。比如a=3 a是ID 3是NUM类型的值*/
ID,NUM,
/* special symbols 比如a=3,=就是ASSIGN,操作符*/
ASSIGN,EQ,LT,PLUS,MINUS,TIMES,OVER,LPAREN,RPAREN,SEMI
} TokenType;
语法分析部分。
树的结点种类NodeKind分为StmtK和ExpK两类,两类又有各自的子类。
在语法树中标明种类有利于代码生成,当遍历语法树检测到特定类型时就可以进行特定的处理。
treeNode结构为指向孩子和兄弟的节点。
语句通过同属域而不是子域来排序,即由父亲到他的孩子的唯一物理连接是到最左孩子的。孩子则在一个标准连接表中自左向右连接到一起,这种连接称作同属连接(网上查了查,没有这个概念。。),用于区别父子连接。
typedef enum {StmtK,ExpK} NodeKind;
typedef enum {IfK,RepeatK,AssignK,ReadK,WriteK} StmtKind;
typedef enum {OpK,ConstK,IdK} ExpKind;
/* ExpType is used for type checking */
typedef enum {Void,Integer,Boolean} ExpType;
#define MAXCHILDREN 3
typedef struct treeNode
{ struct treeNode * child[MAXCHILDREN];
struct treeNode * sibling;
int lineno; /* source line number for listing */
NodeKind nodekind;
union { StmtKind stmt; ExpKind exp;} kind; #两个变量指定表达式种类
union { TokenType op;
int val;
char * name; } attr; #值
ExpType type; /* for type checking of exps */
} TreeNode;
一些其他变量:
/* MAXRESERVED = the number of reserved words */
#define MAXRESERVED 8
extern FILE* source; /* source code text file */
extern FILE* listing; /* listing output text file */
extern FILE* code; /* code text file for TM simulator */
extern int lineno; /* source line number for listing */
最后还有for tracing的部分,设置对应变量为True会在解析过程中打印状态信息
词法分析部分:
/* states in scanner DFA */
typedef enum
{ START,INASSIGN,INCOMMENT,INNUM,INID,DONE }
StateType;
既然是状态机,首先要记录状态,StateType就是用来记录状态机的状态,根据当前不同的状态和获取的字符选择下一步操作。图中六个状态正好对应enum的六个值。
这里要注意,每识别一个用空格隔开的单词就重复从START到DONE的步骤。
/* lexeme of identifier or reserved word */
char tokenString[MAXTOKENLEN+1];
应该是识别identifier用
/* index for storing into tokenString */
int tokenStringIndex = 0; #tokenString下标,分析是一个一个字符分析的,总要有个容器(tokenString)临时存放这个半成品的token
/* holds current token to be returned */
TokenType currentToken; #记录当前token的值
/* current state - always begins at START */
StateType state = START; #记录当前状态的值
/* flag to indicate save to tokenString */
int save; #判断当前字符是否记录进tokenString,如果是空白等无意义字符就不记录
图上省略了一些对操作符±<>*的判断,直接在other中没有体现出来。
这些操作符也有单独的token表示
switch (state)
{ case START:
if (isdigit(c))
state = INNUM;
else if (isalpha(c))
state = INID;
else if (c == ':')
state = INASSIGN; #如果是:,就进入赋值状态(赋值语句)比如语句fact := 1;
else if ((c == ' ') || (c == '\t') || (c == '\n'))
save = FALSE;
else if (c == '{')
{ save = FALSE;
state = INCOMMENT;
}
else
{ state = DONE;
switch (c)
{ case EOF:
save = FALSE;
currentToken = ENDFILE;
break;
case '=':
currentToken = EQ;
break;
case '<':
currentToken = LT;
break;
。。。
}
}
break;
当判断出token结束时,执行ungetNextChar函数。
/* ungetNextChar backtracks one character
in lineBuf */
static void ungetNextChar(void)
{ if (!EOF_flag) linepos-- ;}
case INNUM:
if (!isdigit(c))
{ /* backup in the input */
ungetNextChar(); #理论上来说数字必须是全部是digit,一旦出现不是数字了,就默认该token已经结束。这时这个字符就要unget一下。
save = FALSE;
state = DONE;
currentToken = NUM;
}
break;
符号表:
记录了这些信息
fprintf(listing,"Variable Name Location Line Numbers\n");
fprintf(listing,"------------- -------- ------------\n");