PL/0语言词法分析器
前言:关于词法分析的基础知识的介绍可以看一下这篇博客,我再累述估计也不会有这篇讲的清楚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语言词法分析器的更多相关文章
- PL/0语言编译器的设计与实现
一.设计任务 1.1程序实现要求 PL/0语言可以看成PASCAL语言的子集,它的编译程序是一个编译解释执行系统.PL/0的目标程序为假想栈式计算机的汇编语言,与具体计算机无关. PL/0的编译程序和 ...
- 北航 编译实践 PL/0文法
编译实践-PL\0编译系统实现 姓名: 专业: 计算机科学与技术 学院: 软件学院 提交时间: 2013年12月25日 北京航空航天大学·软件学院 编译实践-PL\0编译系统实现 实验要求 以个人 ...
- PL/0编译程序
Pl/0语言文法的BNF表示: 〈程序〉→〈分程序>. 〈分程序〉→ [<常量说明部分>][<变量说明部分>][<过程说明部分>]〈语句〉 <常量说明部 ...
- PL/0 词法分析器
PL/0 词法分析器 #include<stdio.h> #include <ctype.h> #include <stdlib.h> #include <s ...
- 编译原理--05 用C++手撕PL/0
前言 目录 01 文法和语言.词法分析复习 02 自顶向下.自底向上的LR分析复习 03 语法制导翻译和中间代码生成复习 04 符号表.运行时存储组织和代码优化复习 05 用C++手撕PL/0 在之前 ...
- PL/0编译器实践---后记
花了几天时间,把清华版的<编译原理>一书中的PL/0编译器实践了一遍.颇有收获,记录如下: 理解代码的技巧,如何理解一份代码,比如这个程序,其逻辑相对于一般程序就比较复杂了,如何翻译,虚拟 ...
- PL/0编译器(java version)–Praser.java
1: package compiler; 2: 3: import java.io.IOException; 4: import java.util.BitSet; 5: 6: /** 7: ...
- PL/0与Pascal-S编译器程序详细注释
学校编译课的作业之一,要求阅读两个较为简单的编译器的代码并做注释, 个人感觉是一次挺有意义的锻炼, 将自己的心得分享出来与一同在进步的同学们分享. 今后有时间再做进一步的更新和总结,其中可能有不少错误 ...
- Java编写的C语言词法分析器
Java编写的C语言词法分析器 这是java编写的C语言词法分析器,我也是参考很多代码,然后核心代码整理起来,放在QQ空间和博客上,目的是互相学习借鉴,希望可以得到高手改进.这个词法分析器实现的功能有 ...
随机推荐
- 调试json
console.log("======================") // 转对象 //var obj = eval('(' + data + ')'); // 转对象 // ...
- redis学习二 排序
文章转载自:http://www.cnblogs.com/redcreen/archive/2011/02/15/1955226.html redis支持对list,set和sorted set元素的 ...
- Solaris11 How-To
允许root用户使用ftp - 修改/etc/ftpd/ftpusers文件,移除或注释掉"root" - 修改/etc/proftpd.conf文件,"RootLogi ...
- 通过在Oracle子表外键上建立索引提高性能
根据我的经验,导致死锁的头号原因是外键未加索引(第二号原因是表上的位图索引遭到并发更新).在以下两种情况下,Oracle在修改父表后会对子表加一个全表锁: 1)如果更新了父表的主键(倘若遵循关系数据库 ...
- JDBC批处理数据
JDBC3.0 的增强支持BLOB,CLOB,ARRAY,REF数据类型.的ResultSet对象UPDATEBLOB(),updateCLOB(),updateArray()和updateRef( ...
- css水平居中(一)
第一种方法:使用text-align属性. 看到一篇博客,也不知道是不是我理解的问题,博客上说text-align可以是内联元素水平居中,我感觉这样的说法是不是有些不准确. text-align属性规 ...
- JS的Prototype属性
转载至: http://blog.sina.com.cn/s/blog_7045cb9e0100rtoh.html 函数:原型 每一个构造函数都有一个属性叫做原型(prototype,下面都不再翻译, ...
- Struts2框架05 result标签的类型、拦截器
1 result标签是干什么的 就是结果,服务器处理完返回给浏览器的结果:是一个输出结果数据的组件 2 什么时候需要指定result标签的类型 把要输出的结果数据按照我们指定的数据类型进行处理 3 常 ...
- 项目一:在线下单(补充) activeMQ使用(重点) 重构客户注册功能,发短信功能分离
1 课程计划 1.在线下单(补充) 2.activeMQ使用(重点) n 简介和安装 n activeMQ入门案例 n spring整合activeMQ应用 3.重构客户注册功能,发短信功能分离 n ...
- Python 网络爬虫 004 (编程) 如何编写一个网络爬虫,来下载(或叫:爬取)一个站点里的所有网页
爬取目标站点里所有的网页 使用的系统:Windows 10 64位 Python语言版本:Python 3.5.0 V 使用的编程Python的集成开发环境:PyCharm 2016 04 一 . 首 ...