前言:关于词法分析的基础知识的介绍可以看一下这篇博客,我再累述估计也不会有这篇讲的清楚QAQ。 https://www.cnblogs.com/yanlingyin/archive/2012/04/17/2451717.html    默认大家已经对词法分析有了基本的了解了。

一:下面讨论PL/0语言的词法分析器的单词结构

1、关键字

  关键字(共11个):空格分隔列表如下

  begin  end  if  then  while  do  const  var  call  procedure  odd

2、运算符和界符

  算符和界符(14个):空格分隔列表如下

  +  -  *  /  =  #  <  >  :=  (  )  ,  .  ;

3、标示符

PL/0语言中标示符的定义为:开头可以是下划线或字母,后面可以是下划线、字母或数字。

4、常数

  整形数和浮点数

5、空白符

  PL/0语言中的空白符有:空格符、制表符、换行符,这些空白符在词法分析阶段可以被忽略。

6、注释

PL/0语言中的注释形式为//,可以多行注释(*…*)。

二:PL/0的语言的词法分析器将要完成以下工作

(1) 跳过分隔符(如空格,回车,制表符);

(2) 识别诸如begin,end,if,while等关键字;

(3) 识别非关键字的一般标识符。

(4) 识别常数数字序列。

(5) 识别前面列出的单字符操作符和:=双字符特殊符号。

(6)词法分析器的输出形式(种别,属性值)其中:种别在“2、单词的种别”中进行了定义;

属性值:若单词种别只代表唯一单词,属性值为空;

若单词种别是标识符,为该单词在标识符表中的位置;

若单词种别是常数,属性值为对应常数值。

三:代码实现

测试程序如下(我放置的路径为D:/b.txt,根据自己情况自行修改)

 // PL/0 语法示例程序 

 (*
计算1~10的阶乘
多行注释
*) var n, f;
begin
n := ;
f := ;
while n # do
begin
n := n + ;
f := f * n;
end;
call print;// 用于输出结果,假设预先声明
end.

种别码如下(我放置的路径为D:/a.txt,根据自己情况自行修改)

 begin
end
if
then
while
do
const
var
call
procedure
odd
+
-
*
/
=
#
<
>
:=
(
)
,
.
;
(*多行注释*)
//单行注释 27
常数
标识符

实现代码如下

 // pL/0语言词法分析器
#include<bits/stdc++.h>
using namespace std;
struct _2tup
{
string token;
int id;
};
bool is_blank(char ch)
{
return ch == ' ' || ch == ' ';//空格或控制字符
}
bool gofor(char& ch, string::size_type& pos, const string& prog)//返回指定位置的字符
{
++pos;
if (pos >= prog.size())
{
return false;
}
else
{
ch = prog[pos];
return true;
}
} _2tup scanner(const string& prog, string::size_type& pos, const map<string, int>& keys, int& row)
{
/*
if
标示符
else if
数字
else
符号
*/
_2tup ret;
string token;
int id = ; char ch;
ch = prog[pos]; while(is_blank(ch))
{
++pos;
ch = prog[pos];
}
// 判断标示符、关键字
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_')
{
//保证读取一个单词
while((ch >= '' && ch <= '') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_')
{
token += ch;//追加标示符、关键字
if (!gofor(ch, pos, prog))
{
break;
}
}
// 这里先看做都是其他标示符
id = keys.size(); // 验证是否是关键字
map<string, int>::const_iterator cit = keys.find(token);//根据string类型的token返回int类型的id赋值给cit
if (cit != keys.end())
{
id = cit->second;//此时是关键字,记录他的id
}
}
// 识别常数
else if ((ch >= '' && ch <= '') || ch == '.')
{
while (ch >= '' && ch <= '' || ch == '.')
{
token += ch;
if (!gofor(ch, pos, prog))
{
break;
}
}
id = keys.size() - ;
int dot_num = ;
for (string::size_type i = ; i != token.size(); ++i)
{
if (token[i] == '.')
{
++dot_num;
}
}
if (dot_num > )
{
id = -;
}
}
else
{
map<string, int>::const_iterator cit;
switch (ch)
{
case '-': // - 操作符
token += ch;
if (gofor(ch, pos, prog))
{
if (ch == '-' || ch == '=' || ch == '>') // -- 操作符
{
token += ch;
gofor(ch, pos, prog);
}
}
cit = keys.find(token);
if (cit != keys.end())
{
id = cit->second;
}
break;
case ':':
token += ch;
if (gofor(ch, pos, prog))
{
if (ch == '=') // -- 操作符
{
token += ch;
gofor(ch, pos, prog);
}
}
cit = keys.find(token);
if (cit != keys.end())
{
id = cit->second;
}
break; case '=':
token += ch;
if (gofor(ch, pos, prog))
{
if (ch == '=') // !% %= 操作符
{
token += ch;
gofor(ch, pos, prog);
}
}
cit = keys.find(token);
if (cit != keys.end())
{
id = cit->second;
}
break; case '/': // / 操作符
token += ch;
if (gofor(ch, pos, prog))
{
if (ch == '=') // /= 操作符
{
token += ch;
gofor(ch, pos, prog);
}
else if (ch == '/') // 单行注释
{
token += ch;
++pos;
while (pos < prog.size())
{
ch = prog[pos];
if (ch == '\n')
{
break;
}
token += ch;
++pos;
}
if (pos >= prog.size())
{
;
}
else
{
;
}
id = keys.size() - ;
break;
}
else if (ch == '*') // 注释
{
token += ch;
if (!gofor(ch, pos, prog))
{
token += "\n!!!注释错误!!!";
id = -;
break;
}
if (pos + >= prog.size())
{
token += ch;
token += "\n!!!注释错误!!!";
id = -;
break;
}
char xh = prog[pos + ];
while (ch != '*' || xh != '/')
{
token += ch;
if (ch == '\n')
{
++row;
}
//++pos;
if (!gofor(ch, pos, prog))
{
token += "\n!!!注释错误!!!";
id = -;
ret.token = token;
ret.id = id;
return ret;
}
//ch = prog[pos];
if (pos + >= prog.size())
{
token += ch;
token += "\n!!!注释错误!!!";
id = -;
ret.token = token;
ret.id = id;
return ret;
}
xh = prog[pos + ];
}
token += ch;
token += xh;
pos += ;
ch = prog[pos];
id = keys.size() - ;
break;
}
}
cit = keys.find(token);
if (cit != keys.end())
{
id = cit->second;
}
break;
case '+':
token += ch;
cit = keys.find(token);
if (cit != keys.end())
{
id = cit->second;
}
gofor(ch, pos, prog);
break; case '<':
token += ch;
if (gofor(ch, pos, prog))
{
if (ch == '<')
{
token += ch;
if (gofor(ch, pos, prog))
{
if (ch == '=')
{
token += ch;
gofor(ch, pos, prog);
}
}
}
else if (ch == '=')
{
token += ch;
gofor(ch, pos, prog);
}
}
cit = keys.find(token);
if (cit != keys.end())
{
id = cit->second;
}
break; case '>':
token += ch;
if (gofor(ch, pos, prog))
{
if (ch == '>')
{
token += ch;
if (gofor(ch, pos, prog))
{
if (ch == '=')
{
token += ch;
gofor(ch, pos, prog);
}
}
}
else if (ch == '=')
{
token += ch;
gofor(ch, pos, prog);
}
}
cit = keys.find(token);
if (cit != keys.end())
{
id = cit->second;
}
break;
case '(': // / 操作符
token += ch;
if (gofor(ch, pos, prog)) {
if (ch == '*') // 注释
{
token += ch;
if (!gofor(ch, pos, prog))
{
token += "\n!!!注释错误!!!";
id = -;
break;
}
if (pos + >= prog.size())
{
token += ch;
token += "\n!!!注释错误!!!";
id = -;
break;
}
char xh = prog[pos + ];
while (ch != '*' || xh != ')')
{
token += ch;
if (ch == '\n')
{
++row;
}
//++pos;
if (!gofor(ch, pos, prog))
{
token += "\n!!!注释错误!!!";
id = -;
ret.token = token;
ret.id = id;
return ret;
}
//ch = prog[pos];
if (pos + >= prog.size())
{
token += ch;
token += "\n!!!注释错误!!!";
id = -;
ret.token = token;
ret.id = id;
return ret;
}
xh = prog[pos + ];
}
token += ch;
token += xh;
pos += ;
ch = prog[pos];
id = keys.size() - ;
break;
}
}
cit = keys.find(token);
if (cit != keys.end())
{
id = cit->second;
}
break; case '*':
token += ch;
cit = keys.find(token);
if (cit != keys.end())
{
id = cit->second;
}
gofor(ch, pos, prog);
break; case ',':
case '#':
case '.':
case ';':
token += ch;
gofor(ch, pos, prog);
//++pos;
//ch = prog[pos];
cit = keys.find(token);
if (cit != keys.end())
{
id = cit->second;
}
break; case '\n':
token += "换行";
++pos;
ch = prog[pos];
id = -;
break;
default:
token += "错误";
++pos;
ch = prog[pos];
id = -;
break;
}
}
ret.token = token;
ret.id = id; return ret;
} void init_keys(const string& file, map<string, int>& keys)//读取单词符号和种别码
{
ifstream fin(file.c_str());//.c_str返回的是当前字符串的首地址
if (!fin)
{
cerr << file << " doesn't exist!" << endl;//cerr不经过缓冲而直接输出,一般用于迅速输出出错信息
// exit(1);
}
keys.clear();//清空map对象里面的内容
string line;
string key;
int id;
while (getline(fin, line))//这个函数接收两个参数:一个输入流对象和一个string对象,getline函数从输入流的下一行读取,并保存读取的内容到string中
{
istringstream sin(line);//istringstream sin(s);定义一个字符串输入流的对象sin,并调用sin的复制构造函数,将line中所包含的字符串放入sin 对象中!
sin >> key >> id;//读取里面的字符串每一行一个key id
keys[key] = id;
}
} void read_prog(const string& file, string& prog){//读取代码,并追加到prog上
ifstream fin(file.c_str());
if (!fin)
{
cerr << file << " error!" << endl;
// exit(2);
}
prog.clear();
string line;
while (getline(fin, line))
{
prog += line + '\n';
}
}
int main()
{
map<string, int> keys;
init_keys("D:/Test/a.txt", keys); string prog;
read_prog("D:/Test/b.txt", prog); vector< _2tup > tups;
string token, id; string::size_type pos = ;//size_type属于string标准库,作用可看做相当于unsigned·int
int row = ; _2tup tu;
cout << "------------------" << "要分析的代码如下"<< "---------------" << endl;
cout << prog << endl << endl; // prog += "#"; // 标识终止,其实可以检测 pos 来判别是否终止 int no = ;
cout << "------------------" << "分析的结果如下如下"<< "---------------" << endl;
do
{
tu = scanner(prog, pos, keys, row); switch (tu.id)
{
case -://返回的是错误
++no;
cout << no << ": ";
cout << "Error in row" << row << "!" << '<' << tu.token<< "," << tu.id << '>' << endl;
tups.push_back(tu);
break;
case -:
++row;
// cout << '<' << tu.token<< "," << tu.id << '>' << endl;
break;
default:
++no;
cout << no << ": ";
if(tu.id== || tu.id==){
cout << '<' << tu.id<< "," << tu.token << '>' << endl;
}
else{
cout << '<' << tu.id<< "," << "-" << '>' << endl;
}
tups.push_back(tu);
break;
}
} while (pos < prog.size()); cout << endl << "--------------------------------结果总行数------------"<<tups.size() << endl;
return ;
}

四:运行截图

看懂代码之后,便可以很容易扩展写出其他语言的词法分析器。

PL/0语言词法分析器的更多相关文章

  1. PL/0语言编译器的设计与实现

    一.设计任务 1.1程序实现要求 PL/0语言可以看成PASCAL语言的子集,它的编译程序是一个编译解释执行系统.PL/0的目标程序为假想栈式计算机的汇编语言,与具体计算机无关. PL/0的编译程序和 ...

  2. 北航 编译实践 PL/0文法

    编译实践-PL\0编译系统实现 姓名:   专业: 计算机科学与技术 学院: 软件学院 提交时间: 2013年12月25日 北京航空航天大学·软件学院 编译实践-PL\0编译系统实现 实验要求 以个人 ...

  3. PL/0编译程序

    Pl/0语言文法的BNF表示: 〈程序〉→〈分程序>. 〈分程序〉→ [<常量说明部分>][<变量说明部分>][<过程说明部分>]〈语句〉 <常量说明部 ...

  4. PL/0 词法分析器

    PL/0 词法分析器 #include<stdio.h> #include <ctype.h> #include <stdlib.h> #include <s ...

  5. 编译原理--05 用C++手撕PL/0

    前言 目录 01 文法和语言.词法分析复习 02 自顶向下.自底向上的LR分析复习 03 语法制导翻译和中间代码生成复习 04 符号表.运行时存储组织和代码优化复习 05 用C++手撕PL/0 在之前 ...

  6. PL/0编译器实践---后记

    花了几天时间,把清华版的<编译原理>一书中的PL/0编译器实践了一遍.颇有收获,记录如下: 理解代码的技巧,如何理解一份代码,比如这个程序,其逻辑相对于一般程序就比较复杂了,如何翻译,虚拟 ...

  7. PL/0编译器(java version)–Praser.java

    1: package compiler; 2:   3: import java.io.IOException; 4: import java.util.BitSet; 5:   6: /** 7: ...

  8. PL/0与Pascal-S编译器程序详细注释

    学校编译课的作业之一,要求阅读两个较为简单的编译器的代码并做注释, 个人感觉是一次挺有意义的锻炼, 将自己的心得分享出来与一同在进步的同学们分享. 今后有时间再做进一步的更新和总结,其中可能有不少错误 ...

  9. Java编写的C语言词法分析器

    Java编写的C语言词法分析器 这是java编写的C语言词法分析器,我也是参考很多代码,然后核心代码整理起来,放在QQ空间和博客上,目的是互相学习借鉴,希望可以得到高手改进.这个词法分析器实现的功能有 ...

随机推荐

  1. What makes an inferred latch? how To avoid creating inferred latches? when do you know you need latches?

    What makes an inferred latch?For combinatorial logic, the output of the circuit is a function of inp ...

  2. LeetCode第一题:Two Sum

    Given an array of integers, return indices of the two numbers such that they add up to a specific ta ...

  3. Asp.net工作流workflow实战之给书签命名(四)

    之前我们的书签名字是通过手动录入的方式,在实际开发中要在流程设计的时候定义好: namespace EazyBPMS.WorkFlow { public sealed class SetStepAct ...

  4. HDU1576(扩展欧几里得)

    A/B Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submiss ...

  5. 机器学习:评价分类结果(ROC 曲线)

    一.基础理解 1)定义 ROC(Receiver Operation Characteristic Curve) 定义:描述 TPR 和 FPR 之间的关系: 功能:应用于比较两个模型的优劣: 模型不 ...

  6. java代码继承super

    总结:多态 :. 当重写父类的方法的时,子类对象名可以调用父类的方法,以及不带参的构造方法 package com.addd; public class rr { int a, b; String c ...

  7. 断路器之一:Hystrix 使用与分析

    一:为什么需要Hystrix? 在大中型分布式系统中,通常系统很多依赖(HTTP,hession,Netty,Dubbo等),如下图: 在高并发访问下,这些依赖的稳定性与否对系统的影响非常大,但是依赖 ...

  8. bash姿势-没有管道符执行结果相同于管道符

    听起来比较别口: 直接看代码: shell如下: [root@sevck_linux ~]# </etc/passwd grep root root:x:::root:/root:/bin/ba ...

  9. react过渡动画效果的实现,react-transition-group

    本文介绍react相关的过渡动画效果的实现 有点类似vue的transition组件,主要用于组件mount和unmount之前切换时应用动画效果 安装 cnpm install react-tran ...

  10. delphi 安卓配置教程

    本教程以 delphi 10.2.2.2004 为例,演示 delphi 安卓配置步骤 1.打开 Android Tools 2. 选择合适的版本.比如:我的小米4 LTE 是 andorid 6.0 ...