全局定义:

#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会在解析过程中打印状态信息

词法分析部分:
Tiny编译器总体回顾分析-词法分析

/* 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");

相关文章: