语法分析器的两个重要函数 FIRST和FOLLOW

FIRST的定义

FIRST(α),可从α推导得到的串的首符号的集合

1.如果X是一个终结符,那么FIRST(X) = X

2.如果X是一个非终结符,且X -> Y1Y2...Yk 是一个产生式,

1)如果Y1..Yj-1=>ε 那么 FIRST(Yj) 添加到 FIRST(X)

2)如果Y1...Yk=>ε,那么ε添加到FIRST(X)

3.如果X->ε是一个产生式,那么ε添加到FIRST(X)

代码实现

1.符号类

import java.util.Objects;

/**
* 符号
*/
public class Symbol { protected String name; @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Symbol symbol = (Symbol) o;
return Objects.equals(name, symbol.name);
} @Override
public int hashCode() {
return Objects.hash(name);
}
}

2.终结符

public class Terminal extends Symbol {

    public static Terminal of(String name) {
Terminal result = new Terminal();
result.name = name;
return result;
}
}

 

3.非终结符

/**
* 非终结符
*/
public class Nonterminal extends Symbol { public static Nonterminal of(String name) {
Nonterminal result = new Nonterminal();
result.name = name;
return result;
}
}

  

4.产生式

import java.util.List;
import java.util.Objects; /**
* 产生式
*/
public class Production { /** 产生式头 */
private Nonterminal head;
/** 产生式体 */
private List<Symbol> body; public Nonterminal getHead() {
return head;
} public List<Symbol> getBody() {
return body;
} @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Production that = (Production) o;
return Objects.equals(head, that.head) && Objects.equals(body, that.body);
} @Override
public int hashCode() {
return Objects.hash(head, body);
}
}

  

5.文法

package com.kashin.grammar.model;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set; /**
* 文法
*/
public class Grammar { /** 开始符号 */
private Nonterminal start;
/** 产生式 */
private List<Production> productions; }

  

6.FIRST函数

/**
* P140 FIRST(X)集合
* @param token
* @return
*/
public Set<Terminal> first(Symbol token) { Set<Terminal> result = new HashSet<>(); // X 是一个终结符号
// 1) if X is a terminal, then FIRST(X) = {X}
if (token instanceof Terminal) {
result.add((Terminal) token);
return result;
} List<Production> productionList = getProduction((Nonterminal) token);
for (Production production : productionList) { // 如果 X->ε是一个产生式,那么将ε加入到FIRST(X)
if (production.getBody().size() == 1 && production.getBody().get(0).equals(Terminal.of("ε"))) {
result.add(Terminal.of("ε"));
} else {
Set<Terminal> firstSet = first(production.getBody());
result.addAll(firstSet);
}
} return result;
}

  

/**
* P140 FIRST(X)集合
* @param tokens
* @return
*/
public Set<Terminal> first(List<Symbol> tokens) { // 是否全部包含ε
boolean allContainsEpsilon = true;
Set<Terminal> result = new HashSet<>();
for (Symbol token : tokens) { Set<Terminal> firstSet = first(token);
for (Terminal terminal : firstSet) {
if (terminal.equals(Terminal.of("ε"))) {
continue;
}
result.add(terminal);
} if (!firstSet.contains(Terminal.of("ε"))) {
allContainsEpsilon = false;
break;
}
} // 如果对于所有的j=1,2,...,k,ε在FIRST(Yj)中,那么将ε加入到FIRST(X)
if (allContainsEpsilon) {
result.add(Terminal.of("ε"));
} return result;
}

  

编译器-FIRST集合的更多相关文章

  1. Java集合框架——容器的快速报错机制 fail-fast 是什么?

    前言:最近看 java 集合方面的源码,了解到集合使用了 fail-fast 的机制,这里就记录一下这个机制是什么,有什么用,如何实现的. 一.fail-fast 简介 fail-fast 机制,即快 ...

  2. g++编译器的使用

    关于g++ g++  是GNU组织开发出的编译器软件集合(GCC)下的一个C++编译器.它是Unix 和 Linux  系统下标配的 基于命令行的 C++编译器.如果你的系统是Windows,可以按照 ...

  3. g++编译器的使用(转载)

    关于g++ g++  是GNU组织开发出的编译器软件集合(GCC)下的一个C++编译器.它是Unix 和 Linux  系统下标配的 基于命令行的 C++编译器.如果你的系统是Windows,可以按照 ...

  4. cc 和gcc编译器

    从名字上看,老的unix系统的CC程式叫做C Compiler.但GCC这个名字按GNU的说法叫做Gnu Compiler Collection.因为gcc包含非常多编译器(C, C++, Objec ...

  5. Android.mk 文件语法详解

    0. Android.mk简介: Android.mk文件用来告知NDK Build 系统关于Source的信息. Android.mk将是GNU Makefile的一部分,且将被Build Syst ...

  6. GCC的gcc和g++区别

    看的Linux公社的一篇文章,觉得不错,内容复制过来了. 其实在这之前,我一直以为gcc和g++是一个东西,只是有两个不同的名字而已,今天在linux下编译一个c代码时出现了错误才找了一下gcc和g+ ...

  7. Android.mk 文件语法详解 转:http://blog.sina.com.cn/s/blog_602f8770010148ce.html

    0. Android.mk简介: Android.mk文件用来告知NDK Build 系统关于Source的信息. Android.mk将是GNU Makefile的一部分,且将被Build Syst ...

  8. Android NDK开发之Android.mk文件

    Android NDK开发指南---Android.mk文件 博客分类: Android NDK开发指南   Android.mk文件语法详述 介绍: ------------ 这篇文档是用来描述你的 ...

  9. Android.mk文件语法规范及使用模板

    Android.mk文件语法详述 介绍:------------这篇文档是用来描述你的C或C++源文件中Android.mk编译文件的语法的,为了理解她们我们需要您先看完docs/OVERVIEW.h ...

  10. 【转】configure/make/make install的使用说明

    这些都是典型的使用GNU的AUTOCONF和AUTOMAKE产生的程序的安装步骤. ./configure是用来检测你的安装平台的目标特征的.比如它会检测你是不是有CC或GCC,并不是需要CC或GCC ...

随机推荐

  1. Task2 - IDA学习【进度 - 第二课】

    学习目标: - 无名侠的课,看二进制培训(第二集和第三集)(https://space.bilibili.com/7761039/video) - 会反汇编 - 会字符串搜索(f12) - 会简单异或 ...

  2. .Net 依赖注入深入探索,做一个DI拓展,实现一个简易灵活的 自动依赖注入框架

    一.依赖注入相关知识 1.1.依赖注入的原理和优点 依赖注入(DI),是IOC控制反转思想 的实现.由一个DI容器,去统一管理所有的服务生命周期,服务的创建.销毁.获取,都是由DI容器去处理的. 依赖 ...

  3. 随心所动,厂商的CPU核管理策略介绍

    一.引文 随着CPU架构的发展,工艺的升级,带来性能提升,能效的提升(同性能下).但是由于极限性能的增加,也带来了peak功耗的增加(大部分情况下,能效比的提升无法抵消这部分),CPU功耗优化一直是广 ...

  4. /proc/sys/vm 使用

    这些参数主要是用来调整virtual memory子系统的行为以及数据的写出(从RAM到ROM). 这些节点(参数)的默认值和初始化的过程大部分都可以在mm/swap.c中找到. 目前,/proc/s ...

  5. 深度解析Spring AI:请求与响应机制的核心逻辑

    我们在前面的两个章节中基本上对Spring Boot 3版本的新变化进行了全面的回顾,以确保在接下来研究Spring AI时能够避免任何潜在的问题.今天,我们终于可以直接进入主题:Spring AI是 ...

  6. 对于ChannelNet的一点理解

    主要是为了个人理解,做个笔记 1.Pytorch代码 2.论文出处 3.详细介绍 这篇论文在2018年发出来,而ShuffleNet是从2017年由旷视发出来.起初了解shufflenet的提出,主要 ...

  7. ts 的 declare 用途

    declare namespace API { /** 新增数据集合 */ type CreateDataSet = { createdAt: string; dname: string; headI ...

  8. docker发布ASPNETCore项目 yum

    docker手动发布ASP.NET Core7 一.环境准备环节 1.准备Linux系统 Linux系统-CentOS7---基于虚拟机来安装 IP:192.168.1.97 2.安装docker环境 ...

  9. 怎么根据token的有⽆去控制路由的跳转?进度条跳转 - 白名单是否有token - 单独封装文件permission .js

    vue这边的路由⾃带了路由前置守卫,我们可以在前置守卫⾥拿到token数据,然后根据需求做分⽀判 断,要是token存在就使⽤next⽅法正常放⾏跳转,否则可以强制跳回到登录,让⽤户去获取token ...

  10. 盘点.NET支持的 处理器架构

    在一个会议上,中国招投标协会的技术负责人居然当着很多领导的面说.NET不能在国产服务器上运行,可以说这个技术负责人非蠢即坏.国产服务器的处理器架构主要包括x86.ARM.LoongArch.risc- ...