编译原理:求产生式的FIRST集合

还是先把代码给贴出来,回头再找时间写篇文章总结一下,分析一下数据结构和算法



#pragma once
#include <iostream>
#include <algorithm>
#include <fstream>
#include <map>
#include <set>
#include <string>
#include <stack>

using namespace std;

/*----------------------------------------全局变量存放终结符和非终结符---------------------------------------------------*/
set<string> non_terminal;                                                       //存放非终结符
set<string> productions;                                                        //存放产生式
std::map<string, string> match_map;                                             //存放非终结符和其对应的产生式的文法的键值对
std::map<string, set<string>> first;                                            //string:非终结符;set<string>:非终结符所对应的first集合
std::map<string, set<string>> follow;                                           //string:非终结符;set<string>:非终结符所对应的follow集合
bool is_not_changing = false;


void divide_words(string grammar, map<string, string>& match_map) {
    for (int i = 0; i < (int)grammar.length(); ++i) {
        if (grammar[i] == '-' && grammar[i + 1] == '>') {
            /* code */
            string left = grammar.substr(0, i);                                 //一句文法的左边即非终结符
            string right = grammar.substr(i + 2, grammar.length() - 1);         //一句文法的右边即非终结符对应的产生式
            non_terminal.insert(left);                                          //插入非终结符集合里
            productions.insert(right);                                          //插入产生式集合里
            match_map.insert(make_pair(left, right));                           //将一句文法里的非终结符和其对应的产生式作为键值对插入到匹配map里
            break;
        }
    }
}
/*将被'|'隔开的产生式拆分成对应多个的单词*/
void divide_right(string grammar_right, set<string>& small_right) {
    /*或许可以用grammar.find_first_of一个一个找|,然后用substr分开子串,最后再insert到small_right中去*/
    size_t found = grammar_right.find('|');
    if (found != string::npos) {
        int i = 0;
        string temp = "\0";
        while ((size_t)i < grammar_right.length()) {
            if (grammar_right[i] != '|') {
                temp += grammar_right[i];
                i = i + 1;
            }
            else {
                i = i + 1;
                small_right.insert(temp);
                temp = "\0";
            }
            if (i == grammar_right.length()) {
                small_right.insert(temp);
                temp = "\0";
            }
        }
    }
    else {
        small_right.insert(grammar_right);
    }
}
/*对每个非终结符non_term寻找它的非终结符集合first*/
void find_first(string non_term, set<string>& first) {
    set<string> or_words;                                               //存放产生式中被'|'隔开的单词
    auto search = match_map.find(non_term);
    if (search != match_map.end()) {
        divide_right(search->second, or_words);
        //匹配非终结符是否在or_words的开头
        for (set<string>::iterator i = or_words.begin(); i != or_words.end(); i++) {
            for (set<string>::iterator j = non_terminal.begin(); j != non_terminal.end(); j++) {
                if ((*i).find(*j) == 0) {                               //在or_words[i]的开头找到了一个非终结符
                                                                        //递归寻找非终结符j的first集合
                    find_first((*j), first);
                }
                else {                                                  //在or_words[i]的开头如果没有找到非终结符,即终结符
                    if ((*i)[0] >= 'a' && (*i)[0] <= 'z') {
                        first.insert(*i);
                    }
                    switch ((*i)[0]) {
                    case '(':
                        first.insert(string("("));
                        break;
                    case ')':
                        first.insert(string(")"));
                        break;
                    case '+':
                        first.insert(string("+"));
                        break;
                    case '*':
                        first.insert(string("*"));
                        break;
                    case '#':
                        first.insert(string("#"));
                        break;
                    default:                                            //如果没有匹配到符号的话就把这个单词插入到first集合中
                                                                        //first.insert(*i);
                        break;
                    }
                    continue;                                           //找到之后跳出循环,避免进行多余的遍历浪费时间
                }
            }
        }
    }
}

//对非终结符的follow集进行初始化,开始符号的follow集初始化成{$},其余的初始化成空集
void initial_follow() {
    for (set<string>::iterator i = non_terminal.begin(); i != non_terminal.end(); i++) {
        if (i == non_terminal.begin()) {
            set<string> startFollow;
            startFollow.insert("$");
            auto pair = make_pair(*i, startFollow);
            follow.insert(pair);
        }
        set<string> temp_follow;
        auto pair = make_pair(*i, temp_follow);
        follow.insert(pair);
    }
}
//判断一个非终结符的first集合中是不是含有空串#
bool first_contains_null(set<string> &first) {
    auto find = first.find("#");
    if (find != first.end()) {
        return true;
    }
    return false;
}
//判断一个字符串str是否是非终结符,如果是返回true,否则返回false
bool is_non_terminal(string str) {
    auto find = non_terminal.find(str);
    if (find != non_terminal.end()) {
        return true;
    }
    return false;
}

bool is_letter(char a) {                                                        //是否是小写字母
    if (a >= 'a' && a <= 'z') {
        return true;
    }
    return false;
}

bool is_cap_letter(char a) {
    return (a >= 'A' && a <= 'Z') ? true : false;
}

//返回一个产生式的右部str的最后一个终结符或者非终结符
string find_last(string &str) {

    if ("\0" == str) {
        return "\0";
    }
    if ('\'' == str.at(str.size() - 1)) {
        string s = str.substr(str.size() - 2, 2);
        str = str.substr(0, str.size() - 2);
        return s;
    }
    else if (is_letter( str.at(str.size() - 1) ) && is_letter( str.at( str.size() - 2) ) ) {
        string s = str.substr(str.size() - 2, 2);
        str = str.substr(0, str.size() - 2);
        return s;
    }
    else {
        string s = str.substr(str.size() - 1, 1);
        str = str.substr(0, str.size() - 1);
        return s;
    }
}



int cal_follow_total_size() {                                                   //计算所有follow集合的总size
    int total_size = 0;
    for (map<string, set<string>>::iterator i = follow.begin(); i != follow.end(); i++) {
        total_size += i->second.size();
    }
    return total_size;
}

void find_follow(std::map<string, set<string>>& Follow) {
    while (!is_not_changing) {
        int fomer_size = cal_follow_total_size();
        for (std::map<string, string>::iterator i = match_map.begin(); i != match_map.end(); i++) {//对每一个产生式进行遍历
            set<string> or_words;
            string left = (*i).first;                                       //左边的非终结符A
            string right = (*i).second;                                     //右边的产生式A->b1b2b3B...
            divide_right(right, or_words);
            for (set<string>::iterator j = or_words.begin(); j != or_words.end(); j++) {
                set<string> temp = Follow.find(left)->second;
                string str;
                string word = *j;
                for (; word != "\0"; ) {
                    str = find_last(word);
                    if (!is_non_terminal(str)) {                        //是终结符
                        temp.clear();
                        temp.insert(str);
                    }
                    else {
                        for (set<string>::iterator k = temp.begin(); k != temp.end(); k++) {
                            if ("#" != (*k)) {
                                (Follow.find(str)->second).insert(*k);
                            }
                        }
                        if (!first_contains_null(first.find(str)->second)) {
                            temp = first.find(str)->second;
                        }
                        else {
                            for (set<string>::iterator m = first.find(str)->second.begin(); m != first.find(str)->second.end(); m++) {
                                temp.insert(*m);
                            }
                        }
                    }
                }
            }
        }
        //判断是否发生变化
        int latter_size = cal_follow_total_size();
        is_not_changing = fomer_size == latter_size ? true : false;
    }
}
set<string> find_first_s(string left, string production) {
    set<string> FIRST_S;

    for (size_t i = 0; i < production.size(); i++) {
        if (is_cap_letter(production.at(i))) {
            string M;
            if ('\'' == production.at(i + 1)) {
                M = production.substr(i, 2);
            }
            else {
                ///M = "" + production.at(i);
                M = production.at(i);
            }
            set<string> M_FIRST;
            find_first(M, M_FIRST);
            for (set<string>::iterator j = M_FIRST.begin(); j != M_FIRST.end(); j++) {
                FIRST_S.insert(*j);
            }
            if (!first_contains_null(M_FIRST)) {
                return FIRST_S;
            }
        }
        else {
            if (is_letter(production.at(i))) {
                if (is_letter(production.at(i + 1))) {
                    FIRST_S.insert(production.substr(i, 2));
                }
                else {
                    FIRST_S.insert(production.substr(i, 1));
                }
            }
            else {
                string _temp_;
                _temp_ = production.at(i);
                FIRST_S.insert(_temp_);
            }
            return FIRST_S;
        }
    }
    FIRST_S = follow.find(left)->second;
    return FIRST_S;
}

int main() {
    /*读取文法文件*/
    const char* filename = "wenfa.txt";
    ifstream inFile(filename);
    if (!inFile) {
        cout << "\nFiled to open file " << filename;
        return -1;
    }
    string st = "\0";
    char buf[100];
    while (!inFile.eof()) {
        inFile.getline(buf, 20);
        st = buf;
        if (strlen(buf) == 0 || st == "end") {
            break;
        }
        divide_words(st, match_map);                                        //对每一行文法进行分析找出非终结符和对应的产生式
    }
    inFile.close();
    /*遍历非终结符集合,为每个非终结符寻找first集合*/
    for (set<string>::iterator i = non_terminal.begin(); i != non_terminal.end(); ++i) {
        set<string> the_first;                                              //当前非终结符的first集合
        find_first(*i, the_first);
        first.insert(make_pair(*i, the_first));
    }
    cout << "非终结符" << "\t" << "First集合" << endl;
    for (map<string, set<string>>::iterator i = first.begin(); i != first.end(); i++) {
        cout << "-------------------------" << endl;
        cout << i->first << "\t|\t";
        cout << "{ ";
        //倒序输出first集合中的元素与文法中出现的顺序保持一致
        for (set<string>::reverse_iterator j = (i->second).rbegin(); j != (i->second).rend(); j++) {
            cout << *j << ", ";
        }
        cout << "\b\b }";
        cout << endl;
    }
    cout << endl;
    initial_follow();
    find_follow(follow);
    cout << "非终结符" << "\t" << "Follow集合" << endl;
    for (map<string, set<string>>::iterator i = follow.begin(); i != follow.end(); i++) {
        cout << "------------------------------" << endl;
        cout << i->first << "\t|\t";
        cout << "{ ";
        //倒序输出first集合中的元素与文法中出现的顺序保持一致
        for (set<string>::reverse_iterator j = (i->second).rbegin(); j != (i->second).rend(); j++) {
            cout << *j << ", ";
        }
        cout << "\b\b }";
        cout << endl;
    }

    return 0;

    /********输出FIRST_S集合,这个函数我没写但是经验证结果正确-_-懒……********/
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目:First和Follow生成算法模拟 【问题描述】 设计一个由正规文法生成First和Follow并进行简化的算法动态模拟。(算法参见教材) 【基本要】 动态模拟算法的基本功能是: (1) 输入一个文法G; (2) 输出由文法G构造FIRST的算法; (3) 输出First; (4) 输出由文法G构造FOLLOW的算法; (5) 输出FOLLOW。 【测试数据】 输入文法: E->TE’ E’->+TE’|ε T->FT’ T’->*FT’|εF->(E)|i 【实现提示】 用数据库存储多行文法,用LIST控件显示算法,用GRID类依据算法进行作图。并实现算法与生成过程的关联。 三、设计思路 该分析器主要包括三大部分:FIRSTFOLLOWSELECT。下面主要介绍计算FIRST和FOLLOW算法思想。 FIRST的算法思想:主要有三个函数 First(), First_getvn(), First_getvt。函数 First()调用First_getvn(),First_getvn()调用First_getvt 这里主要把产生分成的两种: 一:产生只能推出空,形如:S->$;此时S的FIRST为{$} 二:产生右部包含非终结符和终结符,形如:S->aA, S->AB. 这里定义了两个比较重要字符串,分别是first 即FIRST;另一个是first_vn,把它定义非终结符的FIRST。当产生右边的第一个字符为非终结符时,把该VN加入 srt first_vn中,后再调用函数first_getvt出FIRST。若产生右边的第一个字符为终结符时,则直接把该VT加入str first,得到FISRT。 流程图: 略
经过一个月的努力终于开发出来,大家来祝贺喔! 是我们java初学者一大帮助,可以告别可爱的记事本了! 这个工具是一个Java开发编辑工具,当然比记事本要好,主要给java初学者和一般的个人开发者使用,使他们更快学好JAVA技术.它适合学习java用途. 目前主要功能有java工程,包,类的创建和保存,java代码和jsp页面的编辑,会有方便的辅助对话框,显示相关的函数和属性.jsp页面的编辑辅助对话框可以少输入很多代码,Tomcat工程的自动部署和启动.加入两个实用功能,可以减少很多工作量. 1.Hibernate配置向导: 数据库的sql脚本到Hibernate配置文件和java文件自动产生. 2.Struts action向导:根据jsp文件中的表单内容自动生成的form类和action类的代码. 并修改struts-config.xml配置文件. 这个FirstJava2开发工具编辑辅助方比Eclipse和Jbuild更高效,使简单, 自己使用Eclipse和Jbuild后发现 尺码太大, 功能太强(大多数用不到), 对初学者来说,使用方法过于复杂,如果你编译200个java文件,编译速度太慢,需要好长时间. Eclipse和Jbuild编辑辅助方,显示的内容太多了,眼都看花了. 当你使用FirstJava2你会发现,它速度很快,功能简单,管用,反应敏捷,编辑辅助方高效,好用! 现在我开发jsp网站也用自己的开发工具,好用. 一.编辑辅助方主要有: 1.当你输入"import java."时就会显示编辑辅助对话框中,来显示java API目录中java包下所有类和包, 列表框中双击某一个包名或类名,包名和类名自动输入代码编辑区. 2.当是输入某一个"变量名."时就会显示编辑辅助对话框中,来显示变量类型的所有相关公共的函数和属性 列表框中双击某一个函数名,代码编辑区将会自动输入"函数名()",如果这个函数有参数光标定位于 括号中,否则定位到括号外. 3.输入"this."时,会显示当前类和超类的所有相关函数和属性. 4.输入"new "时,会显示当前类所有相关构造函数, 输入类名点,会显示所有相关静态函数和属性. 5.输入"{"时,代码编辑区将会自动输入另一个"}",当时插入一个空行,光标定位于这个空行,自动缩进三个空格. 6.每一行会根据{,来计算自动缩进空格数,并对齐. 7.输入"for("时,代码编辑区将会自动输入"for(){}",插入一个空行,光标定位于括号中; 8.输入"while("时,代码编辑区将会自动输入"while(){}",插入一个空行,光标定位于括号中; 9.输入"switch("时,代码编辑区将会自动输入"switch(){case 0: break; default: }",插入相应的空行和自动缩进, 光标定位于括号中; 10.在编辑jsp页面时,输入"<td>"时,自动插入"</td>"; 11.在编辑jsp页面时,输入"<"时,就会显示编辑辅助对话框,你可以选择"table",它将自动 输入"<table width=800 border=0 cellspacing=0 cellpadding=0 >"; 如果选择"input type=text",它将自动输入"<input type="text" name="" size="23"/>"; 如果选择"select"时,它将自动输入"<select name="" > </select>"; 如果选择"option"时,它将自动输入"<option value="" > </option>"; 12.在编辑jsp页面时,在<% %>之间,输入"out.","session.","application.", 就会显示编辑辅助对话框中,来显示类型的所有相关公共的函数和属性; 二.Eclipse和Jbuild开发的工程很容易移到这个工具下,只需直接复制src,来代替FirstJava2 创建的Tomcat工程下src, web文件需直接复制到web目录,再打开这工程,就全部OK了. 三.在工具条上点"部署到Tomcat"按钮,会新创建classes目录,Build目录和deploy目录, 自动编译工程的所有类到classes目录, 复制相关文件到build目录下,并把build目录下的所 有内容打包成deploy目录下webtest1.war再复制到Tomcat的webapps目录下. 在工具条上点"运行Tomcat 服务器",就会自动运行Tomcat 服务器. 并自动运行IE浏览器,并自动打开http://localhost:8080/test1 网页. 你还将学会直接用javac命令来编译代码,java运行代码方法,ANT方编译和运行代码, 创建自己的更高效的Build.xml文件,实现DIY自已的工程, Eclipse和Jbuild编译和运行代码方,和这差不多,把他们隐藏起来了. 请输入关健词:FirstJava2 搜索一下 就可下载
编译原理中,First集合和Follow集合是两个重要的概念,用于描述文法规则的语言特征和语法结构,下面是它们的法: 1. First集合法: 对于一个非终结符号,它的First集合包括所有以该符号作为起始符号的产生的首字符的终结符号。 对于一个终结符号,它的First集合就是本身。 对于一个产生,如果它的右部第一个符号是终结符号,则将该符号加入First集合。 如果它的右部第一个符号是非终结符号,则将该符号的First集合中的所有符号加入到该产生的First集合中,如果该符号的First集合中包含ε,则还需要将下一个符号的First集合中的符号加入到该产生的First集合中,直到没有ε为止。 2. Follow集合法: 对于文法的开始符号,将#(结束符号)加入到该符号的Follow集合中。 对于每个非终结符号,遍历所有产生,如果该符号出现在产生右部,则将该符号后面的符号的First集合中的所有符号(除ε)加入到该符号的Follow集合中,如果该符号后面的符号可以推导出ε,则将该产生左部符号的Follow集合中的所有符号加入到该符号的Follow集合中。 对于每一个右部可以推导出ε的产生,将该产生左部符号的Follow集合中的所有符号加入到该产生右部最后一个符号的Follow集合中。 以上就是编译原理中First集合和Follow集合法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值