LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 2198|回复: 3

[编译原理]词法分析器(请教一个问题)

[复制链接]
发表于 2005-12-9 08:39:50 | 显示全部楼层 |阅读模式
最近在学编译原理,用的编译原理与实践这本书,为了能更好的学习,所以把书中tiny compiler的词法分析器给分出来了,编成一个独立的程序,在改的时候出现了一个问题,就是如果把scan.c中一些函数用到的变量
int lineno=0;
FILE *source,*listing,*fp;
放在scan.h中,就编译不了,出现变量未定义的错误,而放在
scan.c中就可以编译通过。

  一直不能理解这是为什么,难道在预处理时,不是把scan.h与scan.c合在一起的吗?


  1. /****************************************************/
  2. /* File: scan.h                                     */
  3. /* The scanner interface for the TINY compiler      */
  4. /* Compiler Construction: Principles and Practice   */
  5. /* Kenneth C. Louden                                */
  6. /****************************************************/

  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <ctype.h>
  10. #include <string.h>

  11. /* MAXTOKENLEN is the maximum size of a token */
  12. #define MAXTOKENLEN 40
  13. #define FALSE 0
  14. #define TRUE 1
  15. #define MAXRESERVED 8


  16. typedef enum
  17.     /* book-keeping tokens */
  18.    {ENDFILE,ERROR,
  19.     /* reserved words */
  20.     IF,THEN,ELSE,END,REPEAT,UNTIL,READ,WRITE,
  21.     /* multicharacter tokens */
  22.     ID,NUM,
  23.     /* special symbols */
  24.     ASSIGN,EQ,LT,PLUS,MINUS,TIMES,OVER,LPAREN,RPAREN,SEMI
  25.    } TokenType;

  26. /* states in scanner DFA */
  27. typedef enum
  28.    { START,INASSIGN,INCOMMENT,INNUM,INID,DONE }
  29.    StateType;

  30. /* lexeme of identifier or reserved word */
  31. char tokenString[MAXTOKENLEN+1];

  32. /* BUFLEN = length of the input buffer for
  33.    source code lines */
  34. #define BUFLEN 256

  35. static char lineBuf[BUFLEN]; /* holds the current line */
  36. static int linepos = 0; /* current position in LineBuf */
  37. static int bufsize = 0; /* current size of buffer string */
  38. static int EOF_flag = FALSE; /* corrects ungetNextChar behavior on EOF


  39. /* functions */

  40. TokenType getToken(void);
  41. static int getNextChar(void);
  42. static void ungetNextChar(void);
  43. static TokenType reservedLookup (char * s);
  44. void printToken( TokenType token, const char* tokenString ,FILE *listing);

复制代码




  1. /****************************************************/
  2. /* File: scan.c                                     */
  3. /* The scanner implementation for the TINY compiler */
  4. /* Compiler Construction: Principles and Practice   */
  5. /* Kenneth C. Louden                                */
  6. /****************************************************/


  7. #include "scan.h"
  8. FILE *source,*listing,*fp;
  9. int lineno=0;

  10. /* getNextChar fetches the next non-blank character
  11.    from lineBuf, reading in a new line if lineBuf is
  12.    exhausted */
  13. static int getNextChar(void)
  14. { if (!(linepos < bufsize))
  15.   { lineno++;
  16.     if (fgets(lineBuf,BUFLEN-1,source))
  17.     {
  18.       bufsize = strlen(lineBuf);
  19.       linepos = 0;
  20.       return lineBuf[linepos++];
  21.     }
  22.     else
  23.     { EOF_flag = TRUE;
  24.       return EOF;
  25.     }
  26.   }
  27.   else return lineBuf[linepos++];
  28. }


  29. /* ungetNextChar backtracks one character
  30.    in lineBuf */
  31. static void ungetNextChar(void)
  32. { if (!EOF_flag) linepos-- ;}

  33. /* lookup table of reserved words */
  34. static struct
  35.     { char* str;
  36.       TokenType tok;
  37.     } reservedWords[MAXRESERVED]
  38.    = {{"if",IF},{"then",THEN},{"else",ELSE},{"end",END},
  39.       {"repeat",REPEAT},{"until",UNTIL},{"read",READ},
  40.       {"write",WRITE}};

  41. /* lookup an identifier to see if it is a reserved word */
  42. /* uses linear search */

  43. static TokenType reservedLookup (char * s)
  44. { int i;
  45.   for (i=0;i<MAXRESERVED;i++)
  46.     if (!strcmp(s,reservedWords[i].str))
  47.       return reservedWords[i].tok;
  48.   return ID;
  49. }

  50. /*print tokens and save them in a file */
  51. void printToken( TokenType token, const char* tokenString ,FILE *listing)
  52. { switch (token)
  53.   { case IF:
  54.     case THEN:
  55.     case ELSE:
  56.     case END:
  57.     case REPEAT:
  58.     case UNTIL:
  59.     case READ:
  60.     case WRITE:
  61.       fprintf(listing,
  62.          "reserved word: %s\n",tokenString);
  63.       break;
  64.     case ASSIGN: fprintf(listing,":=\n"); break;
  65.     case LT: fprintf(listing,"<\n"); break;
  66.     case EQ: fprintf(listing,"=\n"); break;
  67.     case LPAREN: fprintf(listing,"(\n"); break;
  68.     case RPAREN: fprintf(listing,")\n"); break;
  69.     case SEMI: fprintf(listing,";\n"); break;
  70.     case PLUS: fprintf(listing,"+\n"); break;
  71.     case MINUS: fprintf(listing,"-\n"); break;
  72.     case TIMES: fprintf(listing,"*\n"); break;
  73.     case OVER: fprintf(listing,"/\n"); break;
  74.     case ENDFILE: fprintf(listing,"EOF\n"); break;
  75.     case NUM:
  76.       fprintf(listing,
  77.           "NUM, val= %s\n",tokenString);
  78.       break;
  79.     case ID:
  80.       fprintf(listing,
  81.           "ID, name= %s\n",tokenString);
  82.       break;
  83.     case ERROR:
  84.       fprintf(listing,
  85.           "ERROR: %s\n",tokenString);
  86.       break;
  87.     default: /* should never happen */
  88.       fprintf(listing,"Unknown token: %d\n",token);
  89.   }
  90. }

  91. /****************************************/
  92. /* the primary function of the scanner  */
  93. /****************************************/
  94. /* function getToken returns the
  95. * next token in source file
  96. */
  97. TokenType getToken(void)
  98. {  /* index for storing into tokenString */
  99.    int tokenStringIndex = 0;
  100.    /* holds current token to be returned */
  101.    TokenType currentToken;
  102.    /* current state - always begins at START */
  103.    StateType state = START;
  104.    /* flag to indicate save to tokenString */
  105.    int save;
  106.    while (state != DONE)
  107.    { int c = getNextChar();
  108.      save = TRUE;
  109.      switch (state)
  110.      { case START:
  111.          if (isdigit(c))
  112.            state = INNUM;
  113.          else if (isalpha(c))
  114.            state = INID;
  115.          else if (c == ':')
  116.            state = INASSIGN;
  117.          else if ((c == ' ') || (c == '\t') || (c == '\n'))
  118.            save = FALSE;
  119.          else if (c == '{')
  120.          { save = FALSE;
  121.            state = INCOMMENT;
  122.          }
  123.          else
  124.          { state = DONE;
  125.            switch (c)
  126.            { case EOF:
  127.                save = FALSE;
  128.                currentToken = ENDFILE;
  129.                break;
  130.              case '=':
  131.                currentToken = EQ;
  132.                break;
  133.              case '<':
  134.                currentToken = LT;
  135.                break;
  136.              case '+':
  137.                currentToken = PLUS;
  138.                break;
  139.              case '-':
  140.                currentToken = MINUS;
  141.                break;
  142.              case '*':
  143.                currentToken = TIMES;
  144.                break;
  145.              case '/':
  146.                currentToken = OVER;
  147.                break;
  148.              case '(':
  149.                currentToken = LPAREN;
  150.                break;
  151.              case ')':
  152.                currentToken = RPAREN;
  153.                break;
  154.              case ';':
  155.                currentToken = SEMI;
  156.                break;
  157.              default:
  158.                currentToken = ERROR;
  159.                break;
  160.            }
  161.          }
  162.          break;
  163.        case INCOMMENT:
  164.          save = FALSE;
  165.          if (c == EOF)
  166.          { state = DONE;
  167.            currentToken = ENDFILE;
  168.          }
  169.          else if (c == '}') state = START;
  170.          break;
  171.        case INASSIGN:
  172.          state = DONE;
  173.          if (c == '=')
  174.            currentToken = ASSIGN;
  175.          else
  176.          { /* backup in the input */
  177.            ungetNextChar();
  178.            save = FALSE;
  179.            currentToken = ERROR;
  180.          }
  181.          break;
  182.        case INNUM:
  183.          if (!isdigit(c))
  184.          { /* backup in the input */
  185.            ungetNextChar();
  186.            save = FALSE;
  187.            state = DONE;
  188.            currentToken = NUM;
  189.          }
  190.          break;
  191.        case INID:
  192.          if (!isalpha(c))
  193.          { /* backup in the input */
  194.            ungetNextChar();
  195.            save = FALSE;
  196.            state = DONE;
  197.            currentToken = ID;
  198.          }
  199.          break;
  200.        case DONE:
  201.        default: /* should never happen */
  202.          fprintf(listing,"Scanner Bug: state= %d\n",state);
  203.          state = DONE;
  204.          currentToken = ERROR;
  205.          break;
  206.      }
  207.      if ((save) && (tokenStringIndex <= MAXTOKENLEN))
  208.        tokenString[tokenStringIndex++] = (char) c;
  209.      if (state == DONE)
  210.      { tokenString[tokenStringIndex] = '\0';
  211.        if (currentToken == ID)
  212.          currentToken = reservedLookup(tokenString);
  213.      }
  214.    }

  215.      fprintf(listing,"\t%d: ",lineno);
  216.      printToken(currentToken,tokenString,listing);
  217.      printToken(currentToken,tokenString,fp);

  218.    return currentToken;
  219. } /* end getToken */



  220. main( int argc, char * argv[] )
  221. {
  222.   char pgm[120]; /* source code file name */
  223.   if (argc != 2)
  224.     { fprintf(stderr,"usage: %s <filename>\n",argv[0]);
  225.       exit(1);
  226.     }
  227.   strcpy(pgm,argv[1]) ;
  228.   if (strchr (pgm, '.') == NULL)
  229.      strcat(pgm,".tny");
  230.   source = fopen(pgm,"r");
  231.   if (source==NULL)
  232.   { fprintf(stderr,"File %s not found\n",pgm);
  233.     exit(1);
  234.   }
  235.   listing = stdout; /* send listing to screen */
  236.   fprintf(listing,"\nTINY COMPILATION: %s\n",pgm);

  237. if((fp=fopen("string.txt","wt"))==NULL)
  238. {
  239. printf("Cannot open file strike any key exit!");
  240. getch();
  241. exit(1);
  242. }


  243. fprintf(fp,"\nTINY COMPILATION: %s\n",pgm);

  244.    while (getToken()!=ENDFILE);
  245.   }
复制代码
发表于 2005-12-9 09:38:42 | 显示全部楼层
放在scan.h时要用extern修饰吧?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-12-9 10:13:34 | 显示全部楼层
thanks
以前还真没注意这个关键字。

----------------------------------


C语言之extern声明辨析-
                                          
1 基本解释

  extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。

  另外,extern也可用来进行链接指定。


  2 问题:extern 变量

  在一个源文件里定义了一个数组:

char a[6];

  在另外一个文件里用下列语句进行了声明:

extern char *a;


  请问,这样可以吗?

  答案与分析:

  1)、不可以,程序运行时会告诉你非法访问。原因在于,指向类型T的指针并不等价于类型T的数组。extern char *a声明的是一个指针变量而不是字符数组,因此与实际的定义不同,从而造成运行时非法访问。应该将声明改为extern char a[ ]。

  2)、例子分析如下,如果a[] = "abcd",则外部变量a=0x61626364 (abcd的ASCII码值),*a显然没有意义,如下图:


  显然a指向的空间(0x61626364)没有意义,易出现非法内存访问。

  3)、这提示我们,在使用extern时候要严格对应声明时的格式,在实际编程中,这样的错误屡见不鲜。

  4)、extern用在变量声明中常常有这样一个作用,你在*.c文件中声明了一个全局的变量,这个全局的变量如果要被引用,就放在*.h中并用extern来声明。

  3 问题:extern 函数1

  常常见extern放在函数的前面成为函数声明的一部分,那么,C语言的关键字extern在函数的声明中起什么作用?

  答案与分析:

  如果函数的声明中带有关键字extern,仅仅是暗示这个函数可能在别的源文件里定义,没有其它作用。即下述两个函数声明没有明显的区别:



extern int f(); 和int f();



  当然,这样的用处还是有的,就是在程序中取代include “*.h”来声明函数,在一些复杂的项目中,我比较习惯在所有的函数声明前添加extern修饰。

  4 问题:extern 函数2

  当函数提供方单方面修改函数原型时,如果使用方不知情继续沿用原来的extern申明,这样编译时编译器不会报错。但是在运行过程中,因为少了或者多了输入参数,往往会照成系统错误,这种情况应该如何解决?

  答案与分析:

  目前业界针对这种情况的处理没有一个很完美的方案,通常的做法是提供方在自己的xxx_pub.h中提供对外部接口的声明,然后调用方include该头文件,从而省去extern这一步。以避免这种错误。

  宝剑有双锋,对extern的应用,不同的场合应该选择不同的做法。

  5 问题:extern “C”

  在C++环境下使用C函数的时候,常常会出现编译器无法找到obj模块中的C函数定义,从而导致链接失败的情况,应该如何解决这种情况呢?

  答案与分析:

  C++语言在编译的时候为了解决函数的多态问题,会将函数名和参数联合起来生成一个中间的函数名称,而C语言则不会,因此会造成链接时找不到对应函数的情况,此时C函数就需要用extern “C”进行链接指定,这告诉编译器,请保持我的名称,不要给我生成用于链接的中间函数名。

  下面是一个标准的写法:



//在.h文件的头上
#ifdef __cplusplus
#if __cplusplus
extern "C"{
 #endif
 #endif /* __cplusplus */
 …
 …
 //.h文件结束的地方
 #ifdef __cplusplus
 #if __cplusplus
}
#endif
#endif /* __cplusplus */
回复 支持 反对

使用道具 举报

发表于 2005-12-9 15:51:30 | 显示全部楼层
哦,tiny, 当初我们学编译原理也是用这本书
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表