编译原理实验-PL/0语言 词法分析程序编写

PL/0语言

〈程序〉→〈分程序>.
〈分程序〉→ [<常量说明部分>][<变量说明部分>][<过程说明部分>]〈语句〉
<常量说明部分> → CONST<常量定义>{ ,<常量定义>};
<常量定义> → <标识符>=<无符号整数>
<无符号整数> → <数字>{<数字>}
<变量说明部分> → VAR<标识符>{ ,<标识符>};
<标识符> → <字母>{<字母>|<数字>}
<过和说明部分> → <过程首部><分程序>;{<过程说明部分>}
<过程首部> → procedure<标识符>;
<语句> → <赋值语句>|<条件语句>|<当型循环语句>|<过程调用语句>|<读语句>|<写语句>|<复合语句>|<空>
<赋值语句> → <标识符>:=<表达式>
<复合语句> → begin<语句>{ ;<语句>}
<条件> → <表达式><关系运算符><表达式>|ood<表达式>
<表达式> → [+|-]<项>{<加减运算符><项>}
<项> → <因子>{<乘除运算符><因子>}
<因子> → <标识符>|<无符号整数>|(<表达式>)
<加减运符> → +|-
<乘除运算符> → *|/
<关系运算符> → =|#|<|<=|>|>=
<条件语句> → if<条件>then<语句>
<过程调用语句> → call<标识符>
<当型循环语句> → while<条件>do<语句>
<读语句> → read(<标识符>{ ,<标识符>})
<写语句> → write(<标识符>{,<标识符>})
<字母> → a|b|c…x|y|z
<数字> → 0|1|2…7|8|9

需求:为PL/0语言建立一个词法分程序

首先要理解词法分析的过程,选择合适的数据结构存储值,然后理解PL/0语言的词法,可以选择使用if-else判断结构,也可以自己构造一个DFA进行状态转换。
下面进行思路讲解:

1.首先设置了三个队列来存储相应信息:

queue<string> SYM;
queue<string> ID;
queue<string> NUM;

SYM用来存放每个单词的类别,每个关键字一个类别,比如if我们就存储IFSSYM,var我们就存储VARSYM,标识符的类别是IDENT,然后数字的类别是NUMBER,在我们程序进行识别的过程中,边识别边存储。
ID是存储标识符的具体内容,和SYM的存储同步,当SYM存上IDENT时对应的ID中就存储上标识符具体内容。
NUM与ID类似,用来存储数字的值。
选用队列来存储这三种数据主要是为了使用它先进先出的特性,方便最后输出词法分析器识别的结果。

2.设置了两个map结构,键值都是string类型。其中keywords用来存储关键字以及相应的SYM存储的内容。然后SYM_OUT是存储了包括关键字,标识符,数字以及各种运算符边界符的SYM和符号的对应关系,为了方便最后统一输出词法分析的结果。

map<string,string> Keywords;
map<string,string> SYM_OUT;

Keywords["begin"]="BEGINSYM";
    Keywords["call"]="CALLSYM";
    Keywords["const"]="CONSTSYM";
    Keywords["do"]="DOSYM";
    Keywords["end"]="ENDSYM";
    Keywords["if"]="IFSYM";
    Keywords["odd"]="ODDSYM";
    Keywords["procedure"]="PROCEDURESYM";
    Keywords["read"]="READSYM";
    Keywords["then"]="THENSYM";
    Keywords["var"]="VARSYM";
    Keywords["while"]="WHILESYM";
    Keywords["write"]="WRITESYM";

 SYM_OUT["ADDSYM"]="+";
    SYM_OUT["MINUSSYM"]="-";
    SYM_OUT["MULTIPLYSYM"]="*";
    SYM_OUT["DIVIDESYM"]="/";
    SYM_OUT["EQUALSYM"]="=";
    SYM_OUT["GEQSYM"]=">=";
    SYM_OUT["BECOMESYM"]=":=";
    SYM_OUT["GTRSYM"]=">";
    SYM_OUT["LEQSYM"]="<=";
    SYM_OUT["LSSSYM"]="<";
    SYM_OUT["LEFT_PARENSYM"]="(";
    SYM_OUT["RIGHT_PARENSYM"]=")";
    SYM_OUT["COMMASYM"]=",";
    SYM_OUT["SEMICOLONSYM"]=";";
    SYM_OUT["SPOTSYM"]=".";

3.编写了进行字符类型判断的函数:
主要用于截取的第一个字符的判断,只有知道第一个字符是什么我们才能进一步判断这个字符的类型。
其中包括isNumber用来判断该位置的符号是否是数字,isLatter检测这里的符号是否是字母,大小写都可以检测出来,isOperator检测这里的符号是否是运算符,ifBandSymbol检测字符是否是边界符号,比如括号,逗号,分号等等。

//判断该位置的符号是否是数字
bool isNumber(char ch) {
    if(ch>='0' && ch<='9') {
    return true;
    } else {
        return false;
    }
}
//判断该位置的符号是否是字母
bool isLatter(char ch) {
    if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z')){
        return true;
    }
    return false;
}
//判断该位置是否是运算符的基本单位
bool isOperator(char ch)
{
    if(ch=='+'||ch=='-'||ch=='*'||ch=='/'||ch=='>'||ch=='<'||ch=='='||ch=='#'||ch==':'){
        return true;
    }
    return false;
}
//判断该位置是否是边界符
bool isBandSymbol(char ch)
{
    if(ch=='('||ch==')'||ch==','||ch==';'||ch=='.'){
        return true;
    }
    return false;
}

4.对于连续输入的运算符,进行循环判断,根据字符类型来处理,比如说如果我们判断出这个字符是+,就将对应的ADDSYM类型push进SYM队列,如果是-就push MINUSSYM进入SYM队列,这种没有什么歧义的一元运算符到这里就可以判断完毕。但是有一个产生式运算符比较特殊,就是“:=”,因此对于这个符号,我们就要先识别冒号,然后进行判断,如果冒号后面跟着的是=,那么这个符号合法,将BECOMESYM push进SYM队列,否则输出非法字符。同理还有大于等于和小于等于的处理,也类似这样进行判断。

 else if(str[i]==':')//如果i位置是 ':',i+1位置不是 '=',那么该符号非法
        {
            if((i+1)<a&&str[i+1]=='=')
            {
                SYM.push("BECOMESYM");
                i++;
            } else printf("(%c,非法字符)\n",str[i]);

        }else if(str[i]=='>')
        {
            if((i+1)<a&&str[i+1]=='=')
            {
                SYM.push("GEQSYM");
                i++;
            }else{
                SYM.push("GTRSYM");
            }
             
        }

5.处理完运算符开始处理边界符,使用switch case进行判断,比如(,对于SYM就push相应的类别,其他边界符都是同理。

 case '(':
                SYM.push("LEFT_PARENSYM");
                break;

6.词法分析过程,首先从文件读取对于的字符串,设置了一个flag判断是否存在一个字符串他的长度大于10。设置两个char类型的数组进行存储对应要分析的字符串单词。第一步去掉开头的空格,将对应的标识i++,分析下面的字符,截取两个空格之间的字符串,存到tempStr数组中,对里面的单词进行判断,首先看一下是否超出10个字符的长度。然后使用选择分支进行判读,第一个字符是字母的话,获取都是字母和数字的串,去keyword的map进行迭代器判断,如果是关键字进行SYM中push相应关键字的操作,不是关键字就是标识符了,进行标识符存储的操作。如果以数字开头,首先判断数字后面跟着的是不是字母,是的话输出非法符号,不能出现以数字开头的标识符。
以运算符开头就按照编写的运算符处理函数来处理,边界符同理,按照SYM对应的值进行push就行。
7.最后main函数里面,初始化两个map,定义字符数组存储字符串,然后进行词法分析函数运行,最后统一输出SYM,ID,NUM中的元素。

while (!SYM.empty())
    {
        map<string,string>::iterator sym_iter;
        sym_iter=SYM_OUT.find(string(SYM.front()));
        if(sym_iter!=SYM_OUT.end()){
            cout<<""<<SYM.front()<<"  "<<sym_iter->second<<endl;
        }else{
            if(SYM.front()=="IDENT"){
                if(!ID.empty()){
                    cout<<""<<SYM.front()<<"  "<<ID.front()<<endl;
                    ID.pop();
                }
            }else if(SYM.front()=="NUMBER"){
                if(!NUM.empty()){
                    cout<<""<<SYM.front()<<"  "<<NUM.front()<<endl;
                    NUM.pop();
                }
            }
        }
        SYM.pop();
    }

这里不是完整的代码实现,有需要的同学可以根据思路尝试写一下

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完成对某一种常用高级语言(如Pascal、C语言PL/0语言)的各类单词进行词法分析PL/0语言文法的EBNF描述: 〈程序〉∷= 〈分程序〉。 〈分程序〉∷= [〈常量说明部分〉][〈变量说明部分〉][〈过程说明部分〉]〈语句〉 〈常量说明部分〉∷= CONST〈常量定义〉{。〈常量定义〉}; 〈常量定义〉∷= 〈标志符〉=〈无符号整数〉 〈无符号整数〉∷= 〈数字〉{〈数字〉} 〈变量说明部分〉∷= VAR〈标志符〉{,〈标志符〉}; 〈标志符〉∷= 〈字母〉{〈字母〉|〈数字〉} 〈过程说明部分〉∷= 〈过程首部〉〈分程序〉{;〈过程说明部分〉}; 〈过程首部〉∷= PROCEDURE〈标志符〉; 〈语句〉∷= 〈赋值语句〉|〈条件语句〉|〈当型循环语句〉|〈过程调用语句〉|〈读语句〉|〈写语句〉|〈复合语句〉|〈空〉 〈赋值语句〉∷= 〈标志符〉:=〈表达式〉 〈复合语句〉∷= BEGIN〈语句〉{;〈语句〉}END 〈条件〉∷= 〈表达式〉〈关系运算符〉〈表达式〉|ODD〈表达式〉 〈表达式〉∷= [+|-]〈项〉{〈加法运算符〉〈项〉} 〈项〉∷= 〈因子〉{〈乘法运算符〉〈因子〉} 〈因子〉∷= 〈标志符〉|〈无符号整数〉|‘(’〈表达式〉‘)’ 〈加法运算符〉∷= +|- 〈乘法运算符〉∷= *|/ 〈关系运算符〉∷= =|#|<|>|<=|>= 〈条件语句〉∷= IF〈条件〉THEN〈语句〉 〈过程调用语句〉∷= CALL〈标志符〉 〈当型循环语句〉∷= WHILE〈条件〉DO〈语句〉 〈读语句〉∷= READ‘(’〈标志符〉{,〈标志符〉}‘)’ 〈写语句〉∷= WRITE‘(’〈表达式〉{,〈表达式〉}‘)’ 〈字母〉∷= a|b|…..|X|Y|Z 〈数字〉∷= 0|1|2|…..|8|9
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值