前端学习记录
  • 前言及目录
  • 前端基础
    • HTML
    • CSS
      • CSS学习之布局
    • JavaScript
      • 跟着月影学JavaScript
      • JavaScript之对象、原型链及继承
      • JavaScript中的类
      • onclick与addEventListener区别
      • JS手撕题
    • HTTP与浏览器
      • HTTP实用指南
      • Web开发的安全之旅
    • 通用知识
      • 前端必须知道的开发调试知识
      • 前端设计模式应用
      • Web 标准与前端开发
  • 数据结构及算法
    • 数据结构
      • 1、线性表(List)
      • 2、堆栈(Stack)
      • 3、队列(Queue)
      • 4、二叉树(Binary Tree)
      • 5、二叉搜索树与平衡二叉树(BST & AVL)
      • 6、堆(Stack)& 哈夫曼树 & 并查集
      • 7、图(Graph)
        • 图论——解决最小生成树问题(Kruskal算法&Prim算法)
      • 8、排序(sort)
      • 9、散列表(hash)
      • 数据结构习题
        • 第一周:最大子列和算法、二分查找
        • 第二周:线性结构
        • 第三周:栽树(二叉树等)
        • 第四周:二叉搜索树&二叉平衡树
        • 第五周:堆&哈夫曼树&并查集
        • 第六周:图(上)连通集 、DFS&BFS
        • 第七周:图(中)Floyd算法求最短路
        • 第八周:图(下)
        • 第九周:排序(上)归并&堆排序
        • 第十周:排序(下)
        • 第十一周:散列查找 & KMP
    • CS基础
      • 编译原理 实验一 词法分析器设计
      • 编译原理 实验二 LL(1)分析法程序
    • LeetCode
      • 冲刺春招-精选笔面试 66 题大通关
        • day1:21. 合并两个有序链表、146. LRU 缓存、25. K 个一组翻转链表
        • day2:14. 最长公共前缀、3. 无重复字符的最长子串、124. 二叉树中的最大路径和
        • day3:206. 反转链表、199. 二叉树的右视图、bytedance-016最短移动距离
        • day4:1. 两数之和、15. 三数之和、42. 接雨水
        • day5:7. 整数反转、215. 数组中的第K个最大元素、23. 合并K个升序链表
        • day6:33. 搜索旋转排序数组、54. 螺旋矩阵、bytedance-006. 夏季特惠
        • day7:53. 最大子数组和、152. 乘积最大子数组、41. 缺失的第一个正数
        • day8:20. 有效的括号、200. 岛屿数量、76. 最小覆盖子串
        • day9:105. 从前序与中序遍历序列构造二叉树、103. 二叉树的锯齿形层序遍历、bytedance-010. 数组组成最大数
        • day10:94. 二叉树的中序遍历、102. 二叉树的层序遍历、394. 字符串解码
        • day11:415. 字符串相加、5. 最长回文子串、72. 编辑距离
        • day12:64. 最小路径和、300. 最长递增子序列、bytedance-004. 机器人跳跃问题
        • day13:88. 合并两个有序数组、31. 下一个排列、4. 寻找两个正序数组的中位数
        • day14:121. 买卖股票的最佳时机、56. 合并区间、135. 分发糖果
        • day15:232. 用栈实现队列、22. 括号生成、128. 最长连续序列
        • day16:bytedance-007. 化学公式解析、129. 求根节点到叶节点数字之和、239. 滑动窗口最大值
        • day17:141. 环形链表、236. 二叉树的最近公共祖先、92. 反转链表 II
        • day18:322. 零钱兑换、198. 打家劫舍、 bytedance-003. 古生物血缘远近判定
        • day19:160. 相交链表、143. 重排链表、142. 环形链表 II
        • day20:704. 二分查找、43. 字符串相乘、bytedance-002. 发下午茶
        • day21题目:69. x 的平方根、912. 排序数组、887. 鸡蛋掉落
        • day22:151. 颠倒字符串中的单词、46. 全排列、2. 两数相加
      • 剑指 Offer
        • 剑指offer day1 栈与队列(简单)
        • 剑指offer day2 链表(简单)
        • 剑指offer day3 字符串(简单)
        • 剑指offer day4 查找算法(简单)
        • 剑指offer day5 查找算法(中等)
        • 剑指offer day6 搜索与回溯算法(简单)
        • 剑指offer day7 搜索与回溯算法(简单)
        • 剑指offer day8 动态规划(简单)
        • 剑指offer day9 动态规划(中等)
        • 剑指offer day10 动态规划(中等)
        • 剑指offer day11 双指针(简单)
        • 剑指offer day12 双指针(简单)
        • 剑指offer day13 双指针(简单)
        • 剑指offer day14 搜索与回溯算法(中等)
        • 剑指offer day15 搜索与回溯算法(中等)
        • 剑指offer day16 排序(简单)
        • 剑指offer day17 排序(中等)
      • 剑指 Offer 专项突击版
  • 前端进阶
    • React
      • 响应式系统与 React
      • React学习小记
      • Redux学习之Redux三原则、createSore原理及实现
    • Vue
    • TypeScript
      • TypeScript入门
      • TypeScript 类型体操练习
        • Easy题(13/13)
        • Middle(20/72)
    • 前端工程化
      • Webpack知识体系
    • Node
    • 前端动画与绘图
      • WebGL基础
      • 前端动画简介
      • Floating UI 使用经验分享 - Popover
      • Floating UI 使用经验分享 - Dialog
      • Three.js 学习
        • 学习记录
        • 资源记录
    • 前端性能优化
    • 跨端
      • RN 学习小记之使用 Expo 创建项目
    • 开源
    • SEO 优化
      • 搜索引擎优化 (SEO) 新手指南笔记
  • 笔面试记录
    • 面经集锦
      • 2022春暑期实习MetaApp一二面面经
      • 2022春暑期实习字节前端一面凉经
    • 笔试复盘
      • 2022春暑期实习-美团前端-笔试
      • 2022春暑期实习-360前端-笔试(AK)
      • 2022春暑期实习-京东前端-笔试
      • 2022春暑期实习-网易雷火前端-笔试(AK)
      • 2022春暑期实习-网易互联网前端-暑期实习笔试
由 GitBook 提供支持
在本页
  • 一. 实验目的
  • 二. 实验内容及要求
  • 三. 实验过程
  • 1、采用的数据结构
  • 2、头文件声明和全局变量定义
  • 4、函数汇总
  • 5、实验结果
  • 完整代码

这有帮助吗?

在GitHub上编辑
导出为 PDF
  1. 数据结构及算法
  2. CS基础

编译原理 实验二 LL(1)分析法程序

上一页编译原理 实验一 词法分析器设计下一页LeetCode

最后更新于3年前

这有帮助吗?

源代码仓库:

一. 实验目的

  1. 掌握LL(1)分析法的基本原理

  2. 掌握LL(1)分析表的构造方法

  3. 掌握LL(1)驱动程序的构造方法

二. 实验内容及要求

编写识别单词的词法分析程序。

根据某一文法编制调试LL(1)分析程序,以便对任意输入的符号串进行分析。本次实验的目的主要是加深对预测分析LL(1)分析法的理解。

例:对下列文法,用LL(1)分析法对任意输入的符号串进行分析:

(1)E->TG

(2)G->+TG|—TG

(3)G->ε

(4)T->FS

(5)S->*FS|/FS

(6)S->ε

(7)F->(E)

(8)F->i

输出的格式如下:

(1) LL(1)分析程序,编制人:姓名,学号,班级

(2) 输入一以#结束的符号串(包括 +*()i#):i*(i+i)+(i*i)#

(3) 输出过程步骤如下:

步骤

分析栈

剩余输入串

所用产生式

1

E

i+i*i#

E->TG

(4)输入符号串为非法符号串(或者为合法符号串)

备注:

(1) 在“ 所用产生式 ”一列中如果对应有推导则写出所用产生式;如果为匹配终结符则写明匹配的终结符;如分析异常出错则写为“分析出错”;若成功结束则写为“分析成功”。

(2)在此位置输入符号串为用户自行输入的符号串。

(3)上述描述的输出过程只是其中一部分的。

注意:1.表达式中允许使用运算符(+-*/)、分割符(括号)、字符i,结束符#;

2.如果遇到错误的表达式,应输出错误提示信息(该信息越详细越好);

3.对学有余力的同学,测试用的表达式事先放在文本文件中,一行存放一个表达式,同时以分号分割。同时将预期的输出结果写在另一个文本文件中,以便和输出进行对照;

4.可采用的其它的文法

三. 实验过程

1、采用的数据结构

产生式类型定义为type,左侧为origin,大写字符,右侧产生的字符

struct type { /*产生式类型定义      */
    char origin;   /*产生式左侧字符 大写字符  */
    string array; /*产生式右边字符 */
    int length;    /*字符个数      */
    type():origin('N'), array(""), length(0) {}
    void init(char a, string b) {
        origin = a;
        array = b;
        length = array.length();
    }
};

初始化数据结构如下:

e.init('E', "TG"), t.init('T', "FS");
g.init('G', "+TG"), g1.init('G', "^");
s.init('S', "*FS"), s1.init('S', "^");
f.init('F', "(E)"), f1.init('F', "i");

2、头文件声明和全局变量定义

如下

#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <cstring>
using namespace std;
const string ExpFileName = "./exp.txt";
char analyeStack[20];                           /*分析栈*/
char restStack[20];                             /*剩余栈*/
const string v1 = "i+*()#"; /*终结符 */
const string v2 = "EGTSF";      /*非终结符   */
const string acceptStr = "i+*()#";   // 接受的字符串
int top, ridx, len; /*len为输入串长度 */
struct type { /*产生式类型定义      */
    char origin;   /*产生式左侧字符 大写字符  */
    string array; /*产生式右边字符 */
    int length;    /*字符个数      */
    type():origin('N'), array(""), length(0) {}
    void init(char a, string b) {
        origin = a;
        array = b;
        length = array.length();
    }
};
type e, t, g, g1, s, s1, f, f1; /* 产生式结构体变量 */
type C[10][10];                 /* 预测分析表 */

4、函数汇总

(1)函数汇总表

函数名称
功能简述

readFile

读取文件函数,返回一个string动态数组,以行数分割

init

初始化函数,在该函数中进行分析栈、剩余栈的初始化

print

输出当前分析栈和剩余栈

isTerminator

判断当前字符c是否是终结符

analyze

分析字符串s,输出其分析步骤

main

主程序入口,从此进入,填充初始分析表及产生式初始化,调用读取文件函数开始分析

(2)函数的调用关系

5、实验结果

输入

文件exp.txt

i*(i+i)+(i*i)#
i*i-i/1#
i+i*i+i*(i+i*i)#
i+*i(i)+i(i+i*i)#
i+i(i)#code.txt

输出

完整代码

/*
 * @Author: cos
 * @Date: 2022-04-12 23:03:36
 * @LastEditTime: 2022-04-13 01:32:58
 * @LastEditors: cos
 * @Description: 
 * @FilePath: \CS\experiment_2\main.cpp
 */
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <cstring>
using namespace std;
const string ExpFileName = "./exp.txt";
char analyeStack[20];                           /*分析栈*/
char restStack[20];                             /*剩余栈*/
const string v1 = "i+*()#"; /*终结符 */
const string v2 = "EGTSF";      /*非终结符   */
int top, ridx, len; /*len为输入串长度 */
struct type { /*产生式类型定义      */
    char origin;   /*产生式左侧字符 大写字符  */
    string array; /*产生式右边字符 */
    int length;    /*字符个数      */
    type():origin('N'), array(""), length(0) {}
    void init(char a, string b) {
        origin = a;
        array = b;
        length = array.length();
    }
};
type e, t, g, g1, s, s1, f, f1; /* 产生式结构体变量 */
type C[10][10];                 /* 预测分析表 */
void print() {/*输出分析栈和剩余栈 */
    for(int i = 0; i <= top + 1; ++i)   /*输出分析栈  */
        cout << analyeStack[i];
    cout << "\t\t";

    for(int i = 0; i < ridx; ++i) /*输出对齐符*/
        cout << ' ';
    for(int i = ridx; i <= len; ++i)   /*输出剩余串*/
        cout << restStack[i];
    cout << "\t\t\t";
}
// 读文件
vector<string> readFile(string fileName) {
    vector<string> res;
    try {
        ifstream fin;
        fin.open(fileName);
        string temp;
        while (getline(fin, temp))
            res.push_back(temp);
        return res;
    } catch(const exception& e) {
        cerr << e.what() << '\n';
        return res;
    }
}
bool isTerminator(char c) { // 判断是否是终结符
    return v1.find(c) != string::npos;
}
void init(string exp) {
    top = ridx = 0;
    len = exp.length();     /*分析串长度*/
    for(int i = 0; i < len; ++i)
        restStack[i] = exp[i];
}
void analyze(string exp) {  // 分析一个文法
    init(exp);
    int k = 0;
    analyeStack[top] = '#';
    analyeStack[++top] = 'E'; /*'#','E'进栈*/
    cout << "步骤\t\t分析栈 \t\t剩余字符 \t\t所用产生式 " << endl;
    while(true) {
        char ch = restStack[ridx];
        char x = analyeStack[top--]; /*x为当前栈顶字符*/
        cout << ++k << "\t\t";
        if(x == '#') {
            cout << "分析成功!AC!\n" << endl; /*接受 */
            return;
        }
        if(isTerminator(x)) {
            if (x == ch) {  // 匹配上了
                print();
                cout << ch << "匹配" << endl;
                ch = restStack[++ridx]; /*下一个输入字符*/
            } else {             /*出错处理*/
                print();
                cout << "分析出错,错误终结符为" << ch << endl; /*输出出错终结符*/
                return;
            }
        } else {    /*非终结符处理*/
            int m, n;   // 非终结符下标, 终结符下标
            v2.find(x) != string::npos ? m = v2.find(x) : -1;   // m为-1则说明找不到该非终结符,出错
            v1.find(ch) != string::npos ? n = v1.find(ch) : -1; // n为-1则说明找不到该终结符,出错
            if(m == -1 || n == -1) { /*出错处理*/
                print();
                cout << "分析出错,错误非终结符为" << x << endl; /*输出出错非终结符*/
                return;
            }
            type nowType = C[m][n];/*用来接受C[m][n]*/
            if(nowType.origin != 'N') {/*判断是否为空*/
                print();
                cout << nowType.origin << "->" << nowType.array << endl; /*输出产生式*/
                for (int j = (nowType.length - 1); j >= 0; --j) /*产生式逆序入栈*/
                    analyeStack[++top] = nowType.array[j];
                if (analyeStack[top] == '^') /*为空则不进栈*/
                    top--;
            } else { /*出错处理*/
                print();
                cout << "分析出错,错误非终结符为" << x << endl; /*输出出错非终结符*/
                return;
            }
        }
    }
}
int main() {
    e.init('E', "TG"), t.init('T', "FS");
    g.init('G', "+TG"), g1.init('G', "^");
    s.init('S', "*FS"), s1.init('S', "^");
    f.init('F', "(E)"), f1.init('F', "i"); /* 结构体变量 */
    /*填充分析表*/
    C[0][0] = C[0][3] = e;
    C[1][1] = g; 
    C[1][4] = C[1][5] = g1;
    C[2][0] = C[2][3] = t;
    C[3][2] = s;
    C[3][4] = C[3][5] = C[3][1] = s1;
    C[4][0] = f1; C[4][3] = f;
    cout << "LL(1)分析程序分析程序,编制人:xxx xxx 计科xxxx班" << endl;
    cout << "提示:本程序只能对由'i','+','*','(',')'构成的以'#'结束的字符串进行分析,每行一个读入的字符串" << endl;
    cout << "读取的文件名为:" << ExpFileName << endl; 
    vector<string> exps = readFile(ExpFileName);
    int len = exps.size();
    for(int i = 0; i < len; i++) {
        string exp = exps[i];
        cout << "------------------待分析字符串" << i+1 << ":"<< exp << "--------------------" << endl;
        bool flag = true;
        for(int j = 0; j < exp.length(); j++) {
            if(!isTerminator(exp[j])) {
                cout << "第"<< i+1 << "行输入的字符串不合法,请重新输入" << endl;
                flag = false;
                break;
            }
        }
        if(flag) {
            cout << "字符串" << i+1 << ":" << exp << endl;
            analyze(exp);
        }
    }
    return 0;
}
function
实验结果
在这里插入图片描述
CompilePrincipleLearning/experiment_2 · yusixian/CompilePrincipleLearning (github.com)