编译原理:词法分析的简单代码实现

词法分析:字符流转换为记号流

问题描述:对循环语句和条件判断语句编写词法分析编译程序,只能通过一遍扫描完成。

要求:
(1)关键字: for if then else while do until int input output,所有关键字都是小写。
(2)运算符和分隔符: : = + - * / < > <= <> >= ; ( ) #
(3)其他标识符(ID)和整型常数(NUM),通过以下模式定义:
ID=letter(letter | digit)*
NUM=digit digit*
(4)空格由空白、制表符和换行符组成。空格一般用来分隔ID、NUM、运算符、分隔符和关键字,词法分析阶段通常被忽略。

词法单元和词法记号的对应关系
这里写图片描述

程序功能:
输入:源程序
输出:二元组(词法记号,属性值/其在符号表中的位置)构成的序列。

样例:
输入:int x:=5;
输出:(30, int)(10,’x’)(18, :=) (11,5) (26, ;)

状态转换图:
这里写图片描述
注:0为初态,2,3,4,6,11,12,13为非确定终态,1,5,7,8,9,10为确定终态
非确定终态:这里表示当前子串还不能确定作为一个词法单元
确定终态:这里表示当前终态可以确定作为一个词法单元
非确定终态的其它未标明输入,皆指向1

#include<iostream>
using namespace std;
int main()
{
    void fun(char*a);
    char a[100]={};//保存用户输入,
    //接收输入,不要超过100个字符,否则getline会设置失效位,
    //(如果有的话)接下来的输入将被阻断,需要的话,可以调用cin.clear()恢复输入
    cin.getline(a,100);//遇见回车表示输入结束
    fun(a);
    cout<<endl<<endl<<endl;
    return 0;
}
void fun(char* a)//功能函数
{
    int mov(int im,char c);
    void output(int i,string s);
    string s=a;//将用户输入转换为string类型,因为string.substr比较好用,接下来就会看到
    string keyWord[]={"for","if","then","else","while","do","until","int","input","output"};//关键字
    int kwLenth=sizeof(keyWord)/sizeof(string);//keyWord数组的元素个数
    int tokenKw[]={1,2,3,4,5,6,29,30,31,32};//关键字对应的记号
    int last=0;//待操作终止位,包括这一位
    int start=0;//待操作起始位
    int im=0;//move函数的输入,input_of_move的缩写
    int om=1;//move函数的输出,output_of_move的缩写
    string sub="";//保存当前有效字串
    for(last=0;last<s.size();++last)//扫描输入的源程序
    {
        if(int(s[last])==32||int(s[last])==9)//遇见空格或者水平制表符
        {//(程序中一般不会用水平制表符作为分隔符,为程序完整起见,这里加上了对水平制表符的判断)
            start++;
            //im=0;
            continue;
        }
        sub=s.substr(start,last+1-start);//第一个参数是起始位置,第二个参数是偏移量
        om=mov(im,s[last]);//调用mov函数
        int kw=0;
        switch(om)
        {
        case 1://应该用im对应的终态,即上一个终态
            sub=s.substr(start,last-start);//第一个参数是起始位置,第二个参数是偏移量
            output(im,sub);//输出记号流
            start=last;//移动待操作起始位
            last--;//使得下一轮循环重新读取当前字符
            im=0;//对im置0,因为将要寻找下一个词法单元
            break;
        case 5://单个运算符或分隔符对应的终态
            output(5,sub);
            start=last+1;
            im=0;
            break;
        case 7://<>对应的终态
            output(7,sub);
            start=last+1;
            im=0;
            break;
        case 8://<=对应的终态
            output(8,sub);
            start=last+1;
            im=0;
            break;
        case 9://>=对应的终态
            output(9,sub);
            start=last+1;
            im=0;
            break;
        case 10://:=对应的终态
            output(10,sub);
            start=last+1;
            im=0;
            break;
        case 12://可能是关键字
            for(kw=0;kw<kwLenth;++kw)
            {
                if(sub==keyWord[kw])
                    break;
            }
            if(kw<kwLenth)//匹配关键字成功
            {
                cout<<" ("<<tokenKw[kw]<<","<<sub<<")";
                start=last+1;
                im=0;
            }
            else
                im=om;//不确定终态,还未找到词法单元
            break;
        default://其它值不能确定是终态,除非当前是最后一个字符
            if(last==s.size()-1)
            {
                output(om,sub);
            }
            else
                im=om;//不确定终态,还未找到词法单元
        }
    }
}
int mov(int im,char c)//move函数,需要结合状态转换图理解
{
    int asc=(int)c;//获取当前字符的ASCLL码
    int om=0;//保存mov函数的输出,将作为返回值
    switch(im)
    {
    case 0://从0开始
        if(asc>=48&&asc<=57)//遇见数字
            om=6;
        else if(asc>=65&&asc<=90||asc>=97&&asc<=122)//遇见字母
            om=11;
        else//遇见运算符或分隔符
        {
            if(c=='<')
                om=2;
            else if(c=='>')
                om=3;
            else if(c==':')
                om=4;
            else//遇见其它运算符或分隔符
                om=5;
        }
        break;
    case 2:
        if(c=='>')//表示<>
            om=7;
        else if(c=='=')//表示<=
            om=8;
        else
            om=1;//表示im
        break;
    case 3:
        if(c=='=')//表示>=
            om=9;
        else
            om=1;
        break;
    case 4:
        if(c=='=')//表示:=
            om=10;
        else
            om=1;
        break;
    case 6:
        if(asc>=48&&asc<=57)//遇见数字
            om=6;
        else
            om=1;
        break;
    case 11:
        if(asc>=65&&asc<=90||asc>=97&&asc<=122)//遇见字母
            om=12;
        else if(asc>=48&&asc<=57)//遇见数字
            om=13;
        else
            om=1;
        break;
    case 12:
        if(asc>=65&&asc<=90||asc>=97&&asc<=122)//遇见字母
            om=12;
        else if(asc>=48&&asc<=57)//遇见数字
            om=13;
        else
            om=1;
        break;
    case 13:
        if(asc>=48&&asc<=57)//遇见数字
            om=13;
        else
            om=1;
    default:
        ;
    }
    return om;
}
void output(int i,string s)//输入一个数字和当前有效子串,输出其对应的词法单元的记号流
{
    int trans(char c);
    switch(i)
    {
    case 2://表示<,是单个字符
        ;
    case 3://表示>,是单个字符
        ;
    case 4://表示:,是单个字符
        ;
    case 5://表示单个的运算符或分隔符
        cout<<" ("<<trans(s[0])<<","<<s<<")";
        break;
    case 6://表示数字
        cout<<" (11,"<<s<<")";
        break;
    case 7://表示<>
        cout<<" (21,<>)";
        break;
    case 8://表示<=
        cout<<" (22,<)";
        break;
    case 9://表示>=
        cout<<" (24,<)";
        break;
    case 10://表示:=
        cout<<" (18,:=)";
        break;
    case 11://表示id
       ;
    case 12://表示id
        ;
    case 13://表示id
        cout<<" (10,"<<s<<")";
        break;
    default:
        ;
    }
}
int trans(char c)//输入一个单运算符或分隔符,返回对应的词法记号,需要结合表格理解
{
    int token=0;
    switch(c)
    {
    case ':':
        token=17;
        break;
    case '=':
        token=25;
        break;
    case '+':
        token=13;
        break;
    case '-':
        token=14;
        break;
    case '*':
        token=15;
        break;
    case '/':
        token=16;
        break;
    case '<':
        token=20;
        break;
    case '>':
        token=23;
        break;
    case ';':
        token=26;
        break;
    case '(':
        token=27;
        break;
    case ')':
        token=28;
        break;
    case '#':
        token=0;
        break;
    default:
        ;
    }
    return token;
}

运行结果:
这里写图片描述

  • 4
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值