词法分析

编译原理的词法分析,主要是根据自己的规则,识别出相对应的规则,根据自己的需求,输出你想要的内容

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>

//记号类型序号
enum Token_Type {
    ORIGIN, SCALE, ROT, IS, TO,                     //保留字
    STEP, DRAW, FOR, FROM,                              //保留字
    T,                                                                      //参数
    ID,                                                                     //标识符(已没用了)

    SEMICO, L_BRACKET, R_BRACKET, COMMA,    //分隔符
    PLUS, MINUS, MUL, DIV, POWER,               //运算符

    FUNC,                                                               //函数

    CONST_ID,                                                       //数值常数
    ERRTOKEN,                                                           //错误单词
    NONTOKEN                                                            //表示词法分析结束的专用记号
};

//记号变量类型
struct Token {
    Token_Type type;                        //记号类别号
    char *lexeme;                               //标识符的字符串
    double value;                               //数值常数的值
    double (*FuncPtr)(double);  //函数的指针
};

// 预定义单词表
#define NUMKEYWORDS 18
struct Token TokenTab[NUMKEYWORDS] = {
    //语言中使用的符号常数
    {CONST_ID,  "PI",       3.1415926,  NULL},
    {CONST_ID,  "E",        2.71828,    NULL},
    //语言中惟一的变量
    {T,             "T",            0.0,        NULL},
    //语言允许用的数学函数名(函数原型放在 math.h 中)
    {FUNC,      "SIN",      0.0,        sin},
    {FUNC,      "COS",      0.0,        cos},
    {FUNC,      "TAN",      0.0,        tan},
    {FUNC,      "LN",           0.0,        log},
    {FUNC,      "EXP",      0.0,        exp},
    {FUNC,      "SQRT",     0.0,        sqrt},
    //语句关键字
    {ORIGIN,    "ORIGIN",   0.0,        NULL},
    {SCALE,     "SCALE",    0.0,        NULL},
    {ROT,           "ROT",      0.0,        NULL},
    {IS,            "IS",           0.0,        NULL},
    {FOR,           "FOR",      0.0,        NULL},
    {FROM,      "FROM",     0.0,        NULL},
    {TO,            "TO",           0.0,        NULL},
    {STEP,      "STEP",     0.0,        NULL},
    {DRAW,      "DRAW",     0.0,        NULL}
};

Token iskeywords(const char* id);//判断标识符是否语言的预留单词

void main(int argc, char *argv[]) {
FILE *InFile;   //源文件
int Char;           //当前输入字符

Token token;    //记号变量

int c;              //临时变量(暂存已读入的输入字符)
char *bufc,*bufval; //临时变量(以字符串形式暂存读入的单字符,暂存标识符、数值常量的当前串)

    if (argc < 2) { //程序执行时有 1 个命令行参数(即需要进行词法分析的文件名)
        printf("请输入源文件名!\n" );
        return;
    }
    InFile = fopen (argv[1], "r");  //以只读方式,打开源文件

    printf("记号类别    字符串    常数值      函数指针\n");
    printf("__________________________________________\n");

    while(1) {
        //初始化记号变量
        //token.type=NONTOKEN;//记号类别号放在后面处理
        token.lexeme="";
        token.value=0;
        token.FuncPtr=NULL;

        Char = getc (InFile);       // 从源文件读入一个字符
        if (Char == EOF) {
            token.type=NONTOKEN;
            break;  //读到文件终止符,词法分析结束
        }

        //消除注释内容
        if(Char=='-' || Char=='/' ) {   //当前输入字符是 - 或 /,说明可能遇到注释引导符了
            c=Char;                     //暂存已读的输入字符
            Char = getc (InFile);   //向前多读 1 个输入字符
            if (Char == c) {    //多读的输入字符与暂存字符相同,说明是注释引导符 -- 或 //

                    //逐一读入注释文本(以换行符或文件终止符为结束),不作任何处理,即跳过了这些注释内容
                    while (Char != '\n' && Char != EOF) 
                        Char = getc (InFile);

            } else {            //多读的字符不是 - 或 /,说明遇到的不是注释引导符
                ungetc (Char, InFile);  //将多读的这个字符退回输入流
                Char=c;                                 //当前输入字符恢复为暂存字符(暂存的字符 - 或 / 是运算符,需作为记号处理)
            }
        }

        if(isspace(Char)) continue; //过滤空白符(空格、制表符、换行符)

        if(Char=='-') {         //减号
            token.type=MINUS;
            token.lexeme="-";

        } else if(Char=='/' ) {         //除号
            token.type=DIV;
            token.lexeme="/";

        } else if(Char=='*' ) {         //当前输入字符是 *,说明已读出乘号或遇到乘方算符的首字母
            c=Char;                             //暂存已读的 *
            Char = getc (InFile);   //向前多读 1 个输入字符

            if (Char == c) {                    //多读的字符也是 *,说明已读出了乘方算符
                token.type=POWER;
                token.lexeme="**";
            } else {                                    //多读的字符不是 *,说明是乘号
                ungetc (Char, InFile);  //将多读的字符退回输入流

                token.type=MUL;
                token.lexeme="*";
            }

        } else if(Char==',' ) {         //逗号
            token.type=COMMA;
            token.lexeme=",";
        } else if(Char==';' ) {         //分号
            token.type=SEMICO;
            token.lexeme=";";
        } else if(Char=='(' ) {         //左括号
            token.type=L_BRACKET;
            token.lexeme="(";
        } else if(Char=='+' ) {         //加号
            token.type=PLUS;
            token.lexeme="+";       
        } else if(Char==')' ) {         //右括号
            token.type=R_BRACKET;
            token.lexeme=")";

        } else if(isalpha(Char)) {  //当前输入字符是字母,说明遇到了标识符的首字母
            //将首字母以字符串的形式,保存到临时串
            bufc=(char*)malloc(2);
            bufc[0]=toupper(Char);
            bufc[1]='\0';
            bufval=bufc;            
            //循环读取后续的输入字符
            for(;;){                
                Char = getc (InFile);   //读入 1 个输入字符
                if (isalnum(Char)) {    //当前输入字符仍然是字母或数字,说明还在标识符中
                    //将读入的输入字符,以字符串的形式,连接到临时串后
                    bufc=(char*)malloc(2);
                    bufc[0]=toupper(Char);
                    bufc[1]='\0';
                    strcat(bufval,bufc);//将字符串bufval追加到bufc后面

                } else                              //当前输入字符不再是字母或数字,说明标识符已经读完,并且多读了一个不属于标识符的输入
                    break;
            }
            ungetc (Char, InFile);//将多读的字符退回到输入流中

            //token.type=ID;                //记号类别  = 标识符
            //token.lexeme = bufval;//记号值       = 临时串值
            token=iskeywords(bufval); //判定是否语言的关键字,结果以记号变量形式返回

        } else if(isdigit(Char)) {  //当前输入字符是数字,说明遇到了数值常量的首个数字字母
            bufc=(char*)malloc(2);
            bufc[0]=Char;
            bufc[1]='\0';
            bufval=bufc;
            //逐个字符处理数值常量的整数部分
            for (;;) {
                Char = getc (InFile);
                if (isdigit(Char)) {
                    bufc=(char*)malloc(2);
                    bufc[0]=Char;
                    bufc[1]='\0';
                    strcat(bufval,bufc);
                } else 
                    break;
            }
            if (Char == '.') {      //当前输入字符是 .,说明遇到了数值常量的小数部分
                bufc=(char*)malloc(2);
                bufc[0]=Char;
                bufc[1]='\0';
                strcat(bufval,bufc);
                //逐个字符处理数值常量的小数部分
                for (;;) {                  
                    Char = getc (InFile);
                    if (isdigit(Char)) {
                        bufc=(char*)malloc(2);
                        bufc[0]=Char;
                        bufc[1]='\0';
                        strcat(bufval,bufc);
                    } else 
                        break;
                }
            }
            ungetc (Char, InFile);//将多读的、不属于数值常量的字符,退回输入流

            token.type=CONST_ID;            //记号类别  = 常数
            token.value=atof(bufval);   //记号值       =   临时串转换成的浮点数

        } else if (Char == EOF ){   //文件终止符(处理源文件最后一行是注释的情况)
            token.type=NONTOKEN;
            break;

        } else {                                    //当前输入字符不是以上情况,说明该字符不在本语言的字母表中,出现词法错误
            token.type=ERRTOKEN;
        }       

        //putc(Char,stdout);//逐个字符地显示处理后的源文件

        //输出记号信息
        if(token.type==CONST_ID)
            printf(" %4d%12s%12f\n",token.type,"",token.value);
        else if(token.type==FUNC)
            printf(" %4d%12s%12f%12x\n",token.type,"",token.value,token.FuncPtr);
        else
            printf(" %4d%12s\n",token.type, token.lexeme);

    };
    printf(" %4d%12s\n",token.type, token.lexeme);//显示分析结束记号

    printf("______________________________\n");

    fclose (InFile);    //关闭源文件(即关闭词法分析器)

}

//判断标识符是否语言的预留单词
Token iskeywords(const char* id){
int i;
Token token;

    token.lexeme="";
    token.value=0;
    token.FuncPtr=NULL;

    for(i=0;i<NUMKEYWORDS;i++)  if( strcmp(TokenTab[i].lexeme, id)==0) break;

    if(i<NUMKEYWORDS) token=TokenTab[i]; else token.type = ERRTOKEN;

    return token;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值