编译原理-词法分析
Posted 修心_666
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了编译原理-词法分析相关的知识,希望对你有一定的参考价值。
实验一 词法分析
一、词法分析的任务
词法分析的核心任务是从左到右读入源程序的字符流,识别出一个个的单词。所识别的每一个单词,是下一个有意义的词法元素,如标识符或整数。在识别出下一单词,同时也验证了其词法正确性之后,词法分析程序就会产生一个单词记录,传递给后续阶段使用。
词法分析程序所产生的单词记录通常由两部分信息组成:一个是单词符号(token),对应某个特定意义的词法单元,如标识符、整常数等;另一部分是单词的属性值(attribute)。
程序设计语言中有各种类别的单词,常见的如:
•保留字,也称关键字,如 C 语言中的 if、while、struct、int、typedef 等。
•标识符,用来表示各种名字,如常量名、变量名、函数/过程名、类名等。
•各种类型的常数,如整数 25,浮点数 3.1415,串常数“ABC”等。
•运算符,如 +,,<= 等。
•界符,如逗点,分号,括号等。
例如某语言构词规则 如下:
<无符号整数> ::= <数字> {<数字>}
<标识符> ::= <字母> {<字母><数字>}
<字母> ::= a | b | … | X | Y | Z
<数字> ::= 0 1 2 … 8 9
<保留字> ::= constvarprocedurbeginendoddifthencallwhiledoreadwrite
<运算符> ::= + - / = # < <= > >= :=
<界符> ::= ( ) , ;
二、实验内容
对于以下 Decaf 程序片断:
class Main {
static void main() {
Print(“hello world”);
}
}
设计中还要考虑的一点就是对于单词记录中的属性值的处理,比如可能需要将标识符的
名字、类型等信息存放于符号表中。词法分析结果如下:
直接上代码,但是这个代码适用性不高:
#include <iostream>
#include <fstream>
#include <cassert>
#include <string>
#include <vector>
#include <map>
#include <iosfwd>
#include <algorithm>
using namespace std;
//以键值对的形式存储分析结果
vector<pair<string, string>> res;
//语法构词规则
//标识符:用来表示各种名字,如常量名、变量名、函数/过程名、类名等
// 字母+字母|数字
//无符号整数:数字+数字的集合
//字符串常量
//字母
vector<char> chars;
//数字
vector<int> nums = { 0,1,2,3,4,5,6,7,8,9 };
//保留字
vector<string> keywords = { "static","void","Print","const","var","procedure","begin","end","odd","if","then","call","while","do","read","write","class" };
//运算符
vector<string> operators = { "+","-","*","/","=","#","<","<=",">",">=",":=" };
//分隔符
//vector<char> delimiters = { '(',')',',',';','{','}' };
vector<string> delimiters = { "(",")",",",";","{","}" };
//初始化字母数组
void Initialize() {
char tmp = 'A';
for (int i = 0; i < 26; i++,tmp++) {
chars.push_back(tmp);
}
char t = 'a';
for (int i = 0; i < 26; i++, t++) {
chars.push_back(t);
}
}
//判断是否是保留字
bool IsKeyword(string tmp,vector<string> keywords) {
for (int i = 0; i < keywords.size(); i++) {
if (tmp == keywords[i]) {
return true;
}
}
return false;
}
//判断是否是运算符
bool IsOperator(string tmp) {
if (find(operators.begin(), operators.end(), tmp) != operators.end()) {
return true;
}
return false;
}
//判断是否是分隔符
bool IsDelimiter(string tmp) {
if (find(delimiters.begin(), delimiters.end(), tmp) != delimiters.end()) {
return true;
}
return false;
}
//判断字符串中是否包含分隔符
bool HaveDelimiter(string tmp) {
for (char i : tmp) {
string c;
c.push_back(i);
if (find(delimiters.begin(), delimiters.end(), c) != delimiters.end()) {
return true;
}
c = "";
}
return false;
}
//判断字符串中是否包含""
bool Haveshuang(string tmp) {
for (char i : tmp) {
if (i == '"') {
return true;
}
}
return false;
}
//判断是否是标识符
bool IsIdentifier(string tmp) {
if ( find(chars.begin(),chars.end(),tmp[0])!= chars.end() && !IsKeyword(tmp,keywords)
&& !HaveDelimiter(tmp) ){
return true;
}
return false;
}
//分析单个字符串的属性
void Analyze(string tmp) {
if (IsKeyword(tmp, keywords)) {
res.push_back(pair<string, string>("保留字", tmp));
}
else if (IsIdentifier(tmp)){
res.push_back(pair<string, string>("标识符", tmp));
}
else if (IsOperator(tmp)) {
res.push_back(pair<string, string>("运算符", tmp));
}
else if (IsDelimiter(tmp)){
res.push_back(pair<string, string>("分隔符", tmp));
}
else if (HaveDelimiter(tmp)) {
//分隔符第一次出现的下标
int index = 0;
for (int i = 0; i < tmp.size(); i++)
{
string c;
c.push_back(tmp[i]);
if (find(delimiters.begin(), delimiters.end(),c) != delimiters.end()) {
index = i;
break;
}
c = "";
}
//处理分隔前面的字符串
string a = tmp.substr(0, index);
if (IsKeyword(a, keywords)) {
res.push_back(pair<string, string>("保留字", a));
}
else if (IsIdentifier(a)) {
res.push_back(pair<string, string>("标识符", a));
}
else if (IsOperator(a)) {
res.push_back(pair<string, string>("运算符", a));
}
else if (IsDelimiter(a)) {
res.push_back(pair<string, string>("分隔符", a));
}
string x;
x.push_back(tmp[index]);
res.push_back(pair<string, string>("分隔符", x));
//找到最后一个分隔符
int lastindex = 0;
for (int i = index; i < tmp.size(); i++) {
string b = "";
b.push_back(tmp[i]);
if (IsDelimiter(b)) {
lastindex = i;
}
b = "";
}
if (Haveshuang(tmp))
{
//处理分隔符()中间的字符串
string c = "";
for (int i = index+1; i < lastindex-1; i++) {
c += tmp[i];
}
res.push_back(pair<string, string>("字符串常量", c));
}
for (int i = index+1; i < lastindex; i++) {
string d;
d.push_back(tmp[i]);
if (IsDelimiter(d)) {
res.push_back(pair<string, string>("分隔符", d));
}
d = "";
}
string y;
y.push_back(tmp[lastindex]);
res.push_back(pair<string, string>("分隔符", y));
}
}
//扫描整个字符串 class Main { static void main() { Print("hello world"); } }
void Saomiao(string buff) {
//先计算输入文件流字符串的长度
int bufflength = buff.size();
int spaceindex = 0;
//标志读入字符串常量的状态
bool state = false;
int count = 0;
string tmp = "";
for (int i = 0; i < bufflength; i++) {
if (buff[i] == '"')
{
count++;
state = count % 2;
}
if (buff[i] != ' ') {
tmp += buff[i];
}else if (buff[i] == ' ' && state == true)
{
tmp += buff[i];
}
else if (buff[i] == ' ' && state == false) {
spaceindex = i;
//cout << tmp << endl;
Analyze(tmp);
tmp = "";
}
}
}
int main() {
ifstream infile;
infile.open("exp1.txt", ios::in);
if (!infile.is_open())
{
cout << "打开文件失败" << endl;
return 0;
}
//第一种方式打开
// char buf[1024] = { 0 };
// while (infile >> buf)
// {
// cout << buf << endl;
// }
//打开文件 , 将数据存储到buf中
string buff = "";
string tmp = "";
while (getline(infile, tmp))
{
//cout << buff << endl;
buff += tmp;
}
//第三种
// string buff;
// char c;
// while ((c = infile.get()) != EOF)
// {
// buff += c;
// }
// cout << buff << endl;
// class Main { static void main() { Print("hello world"); } }
Initialize();
Saomiao(buff);
ofstream outFile;
outFile.open("out.txt");//保存的文件名
cout << "分析结果为:" << endl;
for (auto i : res)
{
outFile << i.first << " " << i.second << endl;
cout << i.first << " " << i.second << endl;
}
//cout << res.size() << endl;
cout << "结果输出到同目录下的out.txt文件中" << endl;
outFile.close();//关闭文件写入流
infile.close();
return 0;
}
如果觉得本文对你有帮助的话,不妨关注作者一波,小小的关注其实对我很重要。更多高质量内容与资料请访问:个人主页:修心的小屋
以上是关于编译原理-词法分析的主要内容,如果未能解决你的问题,请参考以下文章