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空间和博客上,目的是互相学习借鉴,希望可以得到高手改进.这个词法分析器实现的功能有 ...
随机推荐
- CentOS7安装wget 及配置
yum -y install wget yum -y install setup yum -y install perl
- DATAX动态参数数据传递
实例:ORACLE到ORACLE的数据传递 编写job.xml文件,添加变量参数 执行datax.py文件时记得带参数 格式:./datax.py –p"-Ddbname=*** -Di ...
- HDU1560(迭代加深搜索)
DNA sequence Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Tot ...
- the virtual machine is configured for 64-bit guest operating systems
Security--Virtualization--Inter(R) Virtualization Technolog 设置为enable 本机安装的是WIN 7 ,详细版本是:Windows 7 U ...
- Java-API-Package:org.springframework.web.bind.annotation
ylbtech-Java-API-Package:org.springframework.web.bind.annotation 1.返回顶部 1. @NonNullApi @NonNullField ...
- Java 的三个注释
单行注释 // 这是名为 a 的类 class a{ } 多行注释 /* 这是多行注释 可以注释多行 */ class a{ } 文档注释 /** 这是文档注释 可以注释多行 */ class a{ ...
- 【SymmetricDS】实现新的数据库方言
2018-04-20 by 安静的下雪天 http://www.cnblogs.com/quiet-snowy-day/p/8890785.html 本文翻译自SymmetricDS官方文档 I ...
- docker 笔记(3)第一个dockerfile
#vim Dockerfile FROM ubuntu RUN apt-get update && apt-get install -y vim #docker build -t ub ...
- java中常用的时间操作
最近项目设计时间的转换和计算,长时间没用时间操作了,感觉手有点生,所以在这里记录一下: Date 常用的方法: getTime() .setTime(): SimpleDateFormate 常用的方 ...
- oracle数据库部分技巧
由于笔者在操作数据库时,遇到几个以前不太常见的操作,感觉有必要记录一下,如下: 1.查被锁表 SELECT object_name, machine, s.sid, s.serial# FROM ...