编译原理-实现一个函数绘图语言的解释器(1)实现词法分析器


 

要实现一个解释器,主要要经过三个步骤,分别是构造:

1、词法分析器:用于识别一条语句中的关键词是否符合预先定义的规则。

2、语法分析器:用来确定一条语句是否满足语法规则。

3、解释器:用来确定满足语法规则的句子,在意思上是否符合要求。

 程序的主要架构是:

1、词法分析器:scanner.h scanner.cpp

2、语法分析器:parser.h parser.cpp

3、解释器:semantic.h semantic.cpp main.cpp

 

首先介绍一下函数绘图语言:

函数绘图源程序举例:

画出的图形举例:

 

 

下面给出词法分析器的源码:

scanner.h:

 1 #ifndef SCANNER_H
 2 #define SCANNER_H
 3 
 4 #include <string.h>
 5 #include <stdio.h>
 6 #include <stdlib.h>
 7 #include <ctype.h>
 8 #include <stdarg.h>
 9 #include <math.h>
10 
11 enum Token_Type                           //记号种类
12 {
13     ORIGIN, SCALE, ROT, IS, TO,               //保留字
14     STEP, DRAW, FOR, FROM,                   //保留字
15     T,                                    //参数
16     SEMICO, L_BRACKET, R_BRACKET, COMMA,     //分隔符号
17     PLUS, MINUS, MUL, DIV, POWER,             //运算符
18     FUNC,                                 //函数
19     CONST_ID,                             //常数
20     NONTOKEN,                             //空记号
21     ERRTOKEN                              //出错记号
22 };
23 
24 typedef double(*MathFuncPtr)(double);
25 
26 struct Token                              //记号与符号表结构
27 {
28     Token_Type type;                      //记号的类型
29     char *lexeme;                         //构成记号的字符串
30     double value;                         //若为常数,则是常数的值
31     double(*FuncPtr)(double);            //若为函数,则是函数的指针
32 };
33 
34 static Token TokenTab[] =                  //符号表内容
35 {
36     { CONST_ID,"PI",3.1415926,NULL },
37     { CONST_ID,"E",2.71828,NULL },
38     { T,"T",0.0,NULL },
39     { FUNC,"SIN",0.0,sin },
40     { FUNC,"COS",0.0,cos },
41     { FUNC,"TAN",0.0,tan },
42     { FUNC,"LN",0.0,log },
43     { FUNC,"EXP",0.0,exp },
44     { FUNC,"SQRT",0.0,sqrt },
45     { ORIGIN,"ORIGIN",0.0,NULL },
46     { SCALE,"SCALE",0.0,NULL },
47     { ROT,"ROT",0.0,NULL },
48     { IS,"IS",0.0,NULL },
49     { FOR,"FOR",0.0,NULL },
50     { FROM,"FROM",0.0,NULL },
51     { TO,"TO",0.0,NULL },
52     { STEP,"STEP",0.0,NULL },
53     { DRAW,"DRAW",0.0,NULL },
54 };
55 //extern 表示变量或者函数的定义在别的函数中
56 extern unsigned int LineNo;                //跟踪记号所在源文件行号
57 extern int InitScanner(const char*);       //初始化词法分析器
58 extern Token GetToken(void);               //获取记号函数
59 extern void CloseScanner(void);            //关闭词法分析器
60 
61 #endif // SCANNER_H

scanner.cpp:

  1 #include "scanner.h"
  2 
  3 #define TOKEN_LEN 100                            //记号最大长度
  4 
  5 unsigned int LineNo;                             //跟踪源文件行号
  6                                                  //static FILE *InFile;                             //输入文件流
  7 static char TokenBuffer[TOKEN_LEN];              //记号字符缓冲
  8 FILE* pFile;
  9 errno_t err = 0; //用来记录文件是否打开成功
 10                  //初始化词法分析器
 11 extern int InitScanner(const char *FileName)//如果成功打开文件,则返回值1
 12 {
 13     LineNo = 1;
 14     err = fopen_s(&pFile, FileName, "r");
 15     if (err == 0) return 1;
 16     else        return 0;
 17 }
 18 
 19 //关闭词法分析器
 20 extern void CloseScanner(void)
 21 {
 22     if (err == 0) fclose(pFile);
 23 }
 24 
 25 //从输入源程序中读入一个字符
 26 static char GetChar(void)
 27 {
 28     int Char = getc(pFile);
 29     return toupper(Char); //将Char变成大写字母
 30 }
 31 
 32 //把预读的字符退回到输入源程序中
 33 static void BackChar(char Char)
 34 {
 35     if (Char != EOF) ungetc(Char, pFile);
 36 }
 37 
 38 //加入字符到记号缓冲区
 39 static void AddCharTokenString(char Char)
 40 {
 41     int TokenLength = strlen(TokenBuffer);
 42     if (TokenLength + 1 >= sizeof(TokenBuffer)) return;//发生数组越界
 43     TokenBuffer[TokenLength] = Char;
 44     TokenBuffer[TokenLength + 1] = '\0';//字符串以'\0'结尾,c语言的字符串都是以/0来结尾的
 45 }
 46 
 47 //清空记号缓冲区
 48 static void EmptyTokenString()
 49 {
 50     memset(TokenBuffer, 0, TOKEN_LEN);
 51 }
 52 
 53 //判断所给的字符串是否在符号表中
 54 static Token JudgeKeyToken(const char *IDString)
 55 {
 56     int loop;            //sizeof(TokenTab)L:表示这个数组一共占了多少字节数;sizeof(TokenTab[0]):表示一个元素所占的字节数,
 57                          //两者相除,表述数组中一共有多少个元素
 58     for (loop = 0; loop<sizeof(TokenTab) / sizeof(TokenTab[0]); loop++) //TokenTab是一个数组,里面存储的是常量、函数、关键字的信息
 59     {
 60         //遍历TokenTab表
 61         if (strcmp(TokenTab[loop].lexeme, IDString) == 0)  return TokenTab[loop]; //判断所给的字符与TokenTab中的所有项,是否存在匹配
 62     }
 63     Token errortoken;
 64     memset(&errortoken, 0, sizeof(Token));
 65     errortoken.type = ERRTOKEN;
 66     return errortoken;
 67 }
 68 
 69 //获取一个记号
 70 extern Token GetToken(void)
 71 {
 72     Token token;
 73     int Char;
 74 
 75     memset(&token, 0, sizeof(Token));
 76     EmptyTokenString();
 77     token.lexeme = TokenBuffer;
 78     for (;;)                                      //过滤源程序中的空格、TAB、回车等,遇到文件结束符返回空记号
 79     {
 80         Char = GetChar();
 81         if (Char == EOF)
 82         {
 83             token.type = NONTOKEN;
 84             return token;
 85         }
 86         if (Char == '\n') LineNo++;
 87         if (!isspace(Char)) break;// 是空白字符,返回非0值 如果Char不是空白字符,跳出for循环
 88     }
 89     AddCharTokenString(Char);
 90 
 91     //若不是空格、TAB、回车、文件结束符等,则先加入到记号的字符缓冲区中
 92     if (isalpha(Char))         //判断Char是不是英文字母                   //若char是A-Za-z,则一定是函数、关键字、PI、E等。
 93     {
 94         for (;;)
 95         {
 96             Char = GetChar();
 97             if (isalnum(Char))    //判断字符变量Char是否为字母或者数字,是则返回非0,否则返回0;
 98                 AddCharTokenString(Char); //Char是字母或者数字
 99             else break;
100         }
101         BackChar(Char);
102         token = JudgeKeyToken(TokenBuffer);
103         token.lexeme = TokenBuffer;
104         return token;
105     }
106     else if (isdigit(Char))                       //若是一个数字,则一定是常量
107     {
108         for (;;)
109         {
110             Char = GetChar();
111             if (isdigit(Char)) AddCharTokenString(Char);
112             else break;
113         }
114         if (Char == '.')
115         {
116             AddCharTokenString(Char);
117             for (;;)
118             {
119                 Char = GetChar();
120                 if (isdigit(Char)) AddCharTokenString(Char);
121                 else break;
122             }
123         }
124         BackChar(Char); //把预读的字符退回到输入源程序中
125         token.type = CONST_ID;
126         token.value = atof(TokenBuffer); //把字符串转换成浮点数
127         return token;
128     }
129     else                                         //不是字母和数字,则一定是运算符或分隔符
130     {
131         switch (Char)
132         {
133         case ';':token.type = SEMICO; break;
134         case '(':token.type = L_BRACKET; break;
135         case ')':token.type = R_BRACKET; break;
136         case ',':token.type = COMMA; break;
137         case '+':token.type = PLUS; break;
138         case '-':
139             Char = GetChar();
140             if (Char == '-')
141             {
142                 while (Char != '\n' && Char != EOF) Char = GetChar();
143                 BackChar(Char);
144                 return GetToken();
145             }
146             else
147             {
148                 BackChar(Char);
149                 token.type = MINUS;
150                 break;
151             }
152         case '/':
153             Char = GetChar();
154             if (Char == '/')
155             {
156                 while (Char != '\n' && Char != EOF) Char = GetChar();
157                 BackChar(Char);
158                 return GetToken();
159             }
160             else
161             {
162                 BackChar(Char);
163                 token.type = DIV;
164                 break;
165             }
166         case '*':
167             Char = GetChar();
168             if (Char == '*')
169             {
170                 token.type = POWER;
171                 break;
172             }
173             else
174             {
175                 BackChar(Char);
176                 token.type = MUL;
177                 break;
178             }
179         default:
180             token.type = ERRTOKEN;
181             break;
182         }// end of switch
183     }//end of else(不是字母和数字,则一定是符号)
184     return token;
185 }//end of GetToken

scannermain.cpp 测试主程序:(在最后构造解释器的时候,不需要,主要作用是用来测试词法分析器的正常运行)

#include "scanner.h"

int main(int argc, char *argv[])
{
    Token token;
    if(argc < 2)
    {
        printf("please input Source File !\n"); return 0;
    }
    if(!InitScanner(argv[1]))
    {
        printf("Open Source File Error!\n"); return 0;
    }
    printf("记号类型    字符串     常数值     函数指针\n");
    printf("------------------------------------------\n");
    while(1)
    {
        token = GetToken();
        if(token.type != NONTOKEN)
            printf("%4d,%12s,%12f,%12x\n",token.type, token.lexeme, token.value, token.FuncPtr);
        else break;
    }
    printf("------------------------------------------\n");
    CloseScanner();
}

环境说明:在单独测试词法分析器的时候,要进入到scanner生成的可执行文件的目录下,通过windows的控制台,执行scanner的可执行文件,加上函数绘图语言的源程序文件来测试。

如: scanner.exe   test.txt

测试结果:

至此,成功构造该解释器的词法分析器。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM