要實現一個解釋器,主要要經過三個步驟,分別是構造:
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
測試結果:


至此,成功構造該解釋器的詞法分析器。
