用C++编写简单绘图语言的词法分析器——程序清单

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

  1/********************************* scanner.cpp *********************************/
  2
  3#include "scanner.h"
  4
  5#define TOKEN_LEN 100                                        // 记号最大长度
  6
  7unsigned int LineNo;                                        // 跟踪源文件行号
  8static FILE *InFile;                                        // 输入文件流
  9static char TokenBuffer[TOKEN_LEN];                            // 记号字符缓冲
 10
 11/* 初始化词法分析器 */
 12extern int InitScanner(const char *FileName)
 13{
 14    LineNo = 1;
 15    InFile = fopen(FileName, "r");
 16    return ((InFile != 0? 1 : 0);
 17}

 18
 19/* 关闭词法分析器 */
 20extern void CloseScanner()
 21{
 22    if(InFile != 0) fclose(InFile);
 23}

 24
 25/* 从输入源程序读入一个字符 */
 26static char GetChar()
 27{
 28    int Char = getc(InFile);
 29    return toupper(Char);
 30}

 31
 32/* 把预读的字符退回到输入源程序中 */
 33static void BackChar(char Char)
 34{
 35    if(Char != EOF) ungetc(Char, InFile);
 36}

 37
 38/* 加入字符到记号缓冲区 */
 39static void AddCharTokenString(char Char)
 40{
 41    int TokenLength = (int)strlen(TokenBuffer);
 42    if(TokenLength + 1 >= sizeof(TokenBuffer)) return;
 43    TokenBuffer[TokenLength] = Char;
 44    TokenBuffer[TokenLength + 1= '\0';
 45}

 46
 47/* 清空记号缓冲区 */
 48static void EmptyTokenString()
 49{
 50    memset(TokenBuffer, 0, TOKEN_LEN);
 51}

 52
 53/* 判断所给的字符串是否在符号表中 */
 54static Token JudgeKeyToken(const char *IDString)
 55{
 56    for(int loop = 0; loop < sizeof(TokenTab) / sizeof(sizeof(TokenTab[0])) / 6++loop)
 57                                /* 注意:需要" / 6",否则 TokenTab 中的元素个数扩大了 6 倍 */
 58    {
 59        if(strcmp(TokenTab[loop].lexeme, IDString) == 0return TokenTab[loop];
 60    }

 61    Token errortoken;
 62    memset(&errortoken, 0sizeof(Token));
 63    errortoken.type = ERRTOKEN;
 64    return errortoken;
 65}

 66
 67/* 获取一个记号 */
 68extern Token GetToken()
 69{
 70    Token token;
 71    int Char;
 72
 73    memset(&token, 0sizeof(Token));
 74    EmptyTokenString();
 75    token.lexeme = TokenBuffer;
 76    
 77    for(;;)                                                    // 过滤源程序中的空格、TAB、回车等,遇到文件结束符返回空记号
 78    {
 79        Char = GetChar();
 80        if(Char == EOF)
 81        {
 82            token.type = NONTOKEN;
 83            return token;
 84        }

 85        if(Char == '\n') LineNo++;
 86        if(!isspace(Char)) break;
 87    }

 88
 89    AddCharTokenString(Char);                                // 若不是空格、TAB、回车、文件结束符等,则先加入到记号的字符缓冲区中
 90
 91    if(isalpha(Char))                                        // 若Char是A-Za-z,则一定是函数、关键字、PI、E等
 92    {
 93        for(;;)
 94        {
 95            Char = GetChar();
 96            if(isalnum(Char)) AddCharTokenString(Char);
 97            else break;
 98        }

 99        BackChar(Char);
100        token = JudgeKeyToken(TokenBuffer);
101        token.lexeme = TokenBuffer;
102        return token;
103    }

104    else if(isdigit(Char))                                    // 若是一个数字,则一定是常量
105    {
106        for(;;)
107        {
108            Char = GetChar();
109            if(isdigit(Char)) AddCharTokenString(Char);
110            else break;
111        }

112        if(Char == '.')
113        {
114            AddCharTokenString(Char);
115            for(;;)
116            {
117                Char = GetChar();
118                if(isdigit(Char)) AddCharTokenString(Char);
119                else break;
120            }

121        }

122        BackChar(Char);
123        token.type = CONST_ID;
124        token.value = atof(TokenBuffer);
125        
126        return token;
127    }

128    else
129    {
130        switch(Char)
131        {
132        case ';' : token.type = SEMICO; break;
133        case '(' : token.type = L_BRACKET; break;
134        case ')' : token.type = R_BRACKET; break;
135        case ',' : token.type = COMMA; break;
136        case '+' : token.type = PLUS; break;
137        case '-' : 
138            Char = GetChar();
139            if(Char == '-')
140            {
141                while(Char != '\n' && Char != EOF) Char = GetChar();
142                BackChar(Char);
143                return GetToken();
144            }

145            else
146            {
147                BackChar(Char);
148                token.type = MINUS;
149                break;
150            }

151        case '/' : 
152            Char = GetChar();
153            if(Char == '/')
154            {
155                while(Char != '\n' && Char != EOF) Char = GetChar();
156                BackChar(Char);
157                return GetToken();
158            }

159            else
160            {
161                BackChar(Char);
162                token.type = DIV;
163                break;
164            }

165        case '*' :
166            Char = GetChar();
167            if(Char == '*')
168            {
169                token.type = POWER;
170                AddCharTokenString(Char);                    
171                break;
172            }

173            else
174            {
175                BackChar(Char);
176                token.type = MUL;
177                break;
178            }

179        default  : token.type = ERRTOKEN; break;
180        }

181    }

182    return token;
183}

 1/********************************* scannermain.cpp *********************************/
 2
 3#include "scanner.h"
 4
 5int main(int argc, char *argv[])
 6{
 7    Token token;
 8    if(!InitScanner("test.txt"))
 9    {
10        printf("Open Source File Error !\n");
11        return -1;
12    }

13    printf("记号类别      字符串     常数值         函数指针\n");
14    printf("________________________________________________\n");
15    while(1)
16    {
17        token = GetToken();
18        if(token.type != NONTOKEN)
19            printf("%4d, %12s, %12f, %12x\n", token.type, token.lexeme, token.value, token.FuncPtr);
20        else break;
21    }
;
22    printf("________________________________________________\n");
23    CloseScanner();
24    return 0;
25}
原文地址:https://www.cnblogs.com/suyang/p/1123123.html