編譯原理-實現一個函數繪圖語言的解釋器(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