编译原理之LR0语法分析器的设计与实现

一、实验目的

理解LR(0)语法分析方法的原理,掌握LR(0)分析表的构造,设计相关数据结构和程序结构,加深对自下而上语法分析方法的理解。

二、实验内容

需要实现的功能:

1)使用LR(0)分析方法构造识别活前缀的DFA;

2)构造文法的分析表(Action表和Goto表);

3)输入文法:文法描述存储在文本文件中,文件名作为命令行参数输入;

4)输出文法的项目集簇(标准输出设备);

5)输出识别活前缀的DFA(标准输出设备);

6)输出文法的Action表和Goto表(输出到创建的指定LR分析表文件,文件名与文法描述文件同名,扩展名为lrtbl);

7)输出文法是否是LR(0)文法的判断结果(标准输出设备);

8)构造LR语法分析器的总控程序;

9)对待分析符号串,输出其是否该文法正确句子的判断,并输出文本形式的分析过程(标准输出设备)。

三、实验过程

1.核心概念

(1)LR(0) 项目

一个 LR(0) 项目是一个产生式加上一个点(·),表示分析过程中的某个状态。例如,对于产生式 A → αβ,其 LR(0) 项目可以是 A → α·β。

点(·)表示当前分析的位置,左侧是已经识别的部分,右侧是待识别的部分。

(2)项目集规范簇

项目集规范簇是 LR(0) 分析器的核心数据结构,表示所有可能的分析状态。

每个项目集是一个 LR(0) 项目的集合,通过闭包(Closure)和转移(Goto)操作生成。

(3)ACTION 表和 GOTO 表

ACTION 表:根据当前状态和输入符号,决定下一步动作(移进、规约、接受或报错)。

GOTO 表:根据当前状态和非终结符,决定转移到的下一个状态。

2.实验步骤

(1)数据结构定义

map<char, vector<string>> grammar; // 存储文法规则,键是非终结符,值是产生式列表
set<char> ter; // 终结符集合
set<char> nonter; // 非终结符集合
char start; // 开始符号
string input; // 输入串

// LR(0) 项目表示结构
struct Item {
    char left;    // 产生式左部
    string right; // 产生式右部
    int dot;      // 点的位置,表示当前分析位置
    
    // 定义比较运算符以支持在集合中存储
    bool operator<(const Item& other) const {
        if (left != other.left) return left < other.left;
        if (right != other.right) return right < other.right;
        return dot < other.dot;
    }
    
    bool operator==(const Item& other) const {
        return left == other.left && right == other.right && dot == other.dot;
    }
};

// LR(0) 规范族
vector<set<Item>> C; 
// ACTION 表:存储移进 (s)、规约 (r)、接受 (a)、错误 (e) 操作
map<pair<int, char>, pair<char, string>> actionTable; 
// GOTO 表
map<pair<int, char>, int> gotoTable; 

(2)求解闭包函数算法

(3)求解闭包函数代码

set<Item> closure(set<Item> I){
	
	stack<Item> stk;
	for(auto i : I){
		stk.push(i);
	}
	
	set<Item> result = I;
	
	while(!stk.empty()){
		
		Item item = stk.top();
		stk.pop();
		
		if(item.dot<item.right.size()){
			char next = item.right[item.dot];
			if(nonter.count(next)){
				for(auto formula : grammar[next]){
					Item newItem = {next, formula, 0};
					if(!result.count(newItem)){
						result.insert(newItem);
						stk.push(newItem);
					}
				}
			}
		}
	}
	
	return result;
}

(4)状态转换函数GO(I,X)

(5)求解状态转换函数GO(I,X)代码

set<Item> Goto(set<Item> I, char X){
	
	set<Item> J;
	
	for(auto item : I){
		if(item.dot<item.right.size()&&item.right[item.dot]==X){
			J.insert({item.left, item.right, item.dot+1});
		}
	}
	
	return closure(J);
}

(6) 构建项目集规范簇算法

(7)构建项目集规范簇代码

void constructItems(){
	
	set<Item> startItem = closure({{'S', string(1, start), 0}});
	
	start='S';
	
	C.push_back(startItem);
	
	bool changed = true;
	
	while(changed){
		
		changed=false;
		
		for(auto I: C){
			for(char t : ter){
				set<Item> J = Goto(I, t);
					
				if(!J.empty()){
					
					bool sign=false;
					
					for(auto c : C){
						if(c==J){
							sign=true;
							break;
						}
					}
					
					if(sign==false){
						C.push_back(J);
						changed=true;
					}
				}
			}
			
			for(char n : nonter){
				set<Item> J = Goto(I, n);
				if(!J.empty()){
					
					bool sign=false;
					
					for(auto c : C){
						if(c==J){
							sign=true;
							break;
						}
					}
					
					if(sign==false){
						C.push_back(J);
						changed=true;
					}
				}
			}
		}
	}
}

(8)求解Action表和GoTo表算法

(9)求解Action表和GoTo表实现代码

void constructActionGoto(){
	
	for(int i=0;i<C.size();i++){
		for(auto item : C[i]){
			if(item.dot == item.right.size()){
				
				if(item.left==start){
					actionTable[{i, '$'}]={'a', "accept"};
				}else{
					for(auto t : ter){
						actionTable[{i, t}] = {'r', string(1, item.left)+"->"+item.right};
					}
					
					actionTable[{i,'$'}] = {'r', string(1, item.left)+"->"+item.right};					
				}
			}else{
				
				char next=item.right[item.dot];
				if(ter.count(next)){
					set<Item> nextItem = Goto(C[i], next);
					for(int j = 0;j<C.size();j++){
						if(nextItem==C[j]){
							actionTable[{i, next}]={'s', to_string(j)};
							break;
						}
					}
				}else{
					set<Item> nextItems = Goto(C[i], next);
                    for (int j = 0; j < C.size(); ++j) {
                        if (nextItems == C[j]) {
                            gotoTable[{i,next}]=j;
                            break;
                        }
                    }
					
				}
			}
		}
	}
	
    for (int i = 0; i < C.size(); i++) {
	    for (char t : ter) {
	        if (actionTable.find({i, t}) == actionTable.end()) {
	            actionTable[{i, t}] = {'e', "error"};  // 未定义项填充 "error"
	        }
	    }
	    for (char nt : nonter) {
	        if (gotoTable.find({i, nt}) == gotoTable.end()) {
	            gotoTable[{i, nt}] = -1;  // -1 代表 GOTO 错误
	        }
	    }
	}
}

(10)构建预测分析程序算法

(11)构建预测分析程序代码

void predict() {
    stack<int> stateStack;  // 状态栈
    stack<char> symbolStack; // 符号栈
    stateStack.push(0);      // 初始状态
    symbolStack.push('$');   // 栈底符号

    input += '$';            // 输入串末尾添加 $
    int idx = 0;             // 输入指针

    cout << "预测分析过程" << endl;
    cout << "------------+--------+----+-------------------------------------+-----------" << endl;
    cout << "栈顶\t\t输入\t查表\t动作\t\t\t\t注释" << endl;
    cout << "------------+--------+----+-------------------------------------+-----------" << endl;

    while (true) {

        int state = stateStack.top(); // 当前状态
        char current = input[idx];   // 当前输入符号
        
        cout<<state<<'\t'<<symbolStack.top()<<'\t'<<current<<'\t';

        // 查找 ACTION 表
        if (actionTable.find({state, current}) != actionTable.end()) {
            auto action = actionTable[{state, current}];
            char actionType = action.first;
            string actionValue = action.second;

            if (actionType == 's') { // 移进动作
                int nextState = stoi(actionValue);
                stateStack.push(nextState);
                symbolStack.push(current);
                idx++;
                cout << "s" << nextState << "\t移进\t进栈 " << " " << current << endl;
            } else if (actionType == 'r') { // 规约动作
                char left = actionValue[0];
                string right = actionValue.substr(3);
                int count = right.size();

                // 弹出栈顶的符号和状态
                for (int i = 0; i < count; i++) {
                    stateStack.pop();
                    symbolStack.pop();
                }

                // 获取规约后的状态
                int newState = stateStack.top();
                if (gotoTable.find({newState, left}) != gotoTable.end()) {
                    int gotoState = gotoTable[{newState, left}];
                    stateStack.push(gotoState);
                    symbolStack.push(left);
                    cout << "r" << gotoState << "\t规约\t出栈 " << count << " 个符号和状态,进栈 " << " " << left << "\t" << left << " -> " << right << endl;
                } else {
                    cout << "error\t错误\tGOTO 表未定义" << endl;
                    return;
                }
            } else if (actionType == 'a') { // 接受动作
                cout << "acc\t接受\t成功接收!" << endl;
                return;
            } else { // 其他动作
                cout << actionValue << "\t未知动作" << endl;
                return;
            }
        } else {
            cout << "error\t错误\tACTION 表未定义" << endl;
            return;
        }
    }
}

四、实验结果

1.奉上全部代码

#include <bits/stdc++.h>

using namespace std;

map<char, vector<string>> grammar;
set<char> ter;                          
set<char> nonter;                        
char start;                              
string input;                           

struct Item{
	char left;
	string right;
	int dot;
	
    bool operator<(const Item& other) const {
	    if (left != other.left) return left < other.left;
	    if (right != other.right) return right < other.right;
	    return dot < other.dot;
	}
	
	bool operator==(const Item& other) const {
        return left == other.left && right == other.right && dot == other.dot;
    }
};

vector<set<Item>> C;
map<pair<int,char>, pair<char, string>> actionTable;
map<pair<int,char>, int> gotoTable;

// 从文件中读取文法规则和其他信息
void ReadFile() {
    ifstream file("C:\\Users\\24775\\Desktop\\test.txt"); // 打开文件
    if (!file) {
        cout << "文件读取错误!" << endl;
        return;
    }

    string line;

    // 读取非终结符
    getline(file, line);
    for (char c : line) {
        if (c != ' ') nonter.insert(c);
    }

    // 读取终结符
    getline(file, line);
    for (char c : line) {
        if (c != ' ') ter.insert(c);
    }

    // 读取文法规则数量
    int numGrammar;
    file >> numGrammar;
    file.ignore(); // 忽略换行符

    // 读取每条文法规则
    for (int i = 0; i < numGrammar; i++) {
        getline(file, line);
        char left = line[0]; // 产生式左部
        string right;         // 产生式右部

        // 去掉空格,提取右部
        for (int j = 4; j < line.size(); j++) {
            if (line[j] != ' ') right += line[j];
        }

        grammar[left].push_back(right); // 存储文法规则
    }

    // 读取开始符号
    file >> start;
    file.ignore();

    // 读取输入串
    getline(file, input);

    file.close(); // 关闭文件
}

set<Item> closure(set<Item> I){
	
	stack<Item> stk;
	for(auto i : I){
		stk.push(i);
	}
	
	set<Item> result = I;
	
	while(!stk.empty()){
		
		Item item = stk.top();
		stk.pop();
		
		if(item.dot<item.right.size()){
			char next = item.right[item.dot];
			if(nonter.count(next)){
				for(auto formula : grammar[next]){
					Item newItem = {next, formula, 0};
					if(!result.count(newItem)){
						result.insert(newItem);
						stk.push(newItem);
					}
				}
			}
		}
	}
	
	return result;
}

set<Item> Goto(set<Item> I, char X){
	
	set<Item> J;
	
	for(auto item : I){
		if(item.dot<item.right.size()&&item.right[item.dot]==X){
			J.insert({item.left, item.right, item.dot+1});
		}
	}
	
	return closure(J);
}

void constructItems(){
	
	set<Item> startItem = closure({{'S', string(1, start), 0}});
	
	start='S';
	
	C.push_back(startItem);
	
	bool changed = true;
	
	while(changed){
		
		changed=false;
		
		for(auto I: C){
			for(char t : ter){
				set<Item> J = Goto(I, t);
					
				if(!J.empty()){
					
					bool sign=false;
					
					for(auto c : C){
						if(c==J){
							sign=true;
							break;
						}
					}
					
					if(sign==false){
						C.push_back(J);
						changed=true;
					}
				}
			}
			
			for(char n : nonter){
				set<Item> J = Goto(I, n);
				if(!J.empty()){
					
					bool sign=false;
					
					for(auto c : C){
						if(c==J){
							sign=true;
							break;
						}
					}
					
					if(sign==false){
						C.push_back(J);
						changed=true;
					}
				}
			}
		}
	}
}

void constructActionGoto(){
	
	for(int i=0;i<C.size();i++){
		for(auto item : C[i]){
			if(item.dot == item.right.size()){
				
				if(item.left==start){
					actionTable[{i, '$'}]={'a', "accept"};
				}else{
					for(auto t : ter){
						actionTable[{i, t}] = {'r', string(1, item.left)+"->"+item.right};
					}
					
					actionTable[{i,'$'}] = {'r', string(1, item.left)+"->"+item.right};					
				}
			}else{
				
				char next=item.right[item.dot];
				if(ter.count(next)){
					set<Item> nextItem = Goto(C[i], next);
					for(int j = 0;j<C.size();j++){
						if(nextItem==C[j]){
							actionTable[{i, next}]={'s', to_string(j)};
							break;
						}
					}
				}else{
					set<Item> nextItems = Goto(C[i], next);
                    for (int j = 0; j < C.size(); ++j) {
                        if (nextItems == C[j]) {
                            gotoTable[{i,next}]=j;
                            break;
                        }
                    }
					
				}
			}
		}
	}
	
    for (int i = 0; i < C.size(); i++) {
	    for (char t : ter) {
	        if (actionTable.find({i, t}) == actionTable.end()) {
	            actionTable[{i, t}] = {'e', "error"};  // 未定义项填充 "error"
	        }
	    }
	    for (char nt : nonter) {
	        if (gotoTable.find({i, nt}) == gotoTable.end()) {
	            gotoTable[{i, nt}] = -1;  // -1 代表 GOTO 错误
	        }
	    }
	}
}

void predict() {
    stack<int> stateStack;  // 状态栈
    stack<char> symbolStack; // 符号栈
    stateStack.push(0);      // 初始状态
    symbolStack.push('$');   // 栈底符号

    input += '$';            // 输入串末尾添加 $
    int idx = 0;             // 输入指针

    cout << "预测分析过程" << endl;
    cout << "------------+--------+----+-------------------------------------+-----------" << endl;
    cout << "栈顶\t\t输入\t查表\t动作\t\t\t\t注释" << endl;
    cout << "------------+--------+----+-------------------------------------+-----------" << endl;

    while (true) {

        int state = stateStack.top(); // 当前状态
        char current = input[idx];   // 当前输入符号
        
        cout<<state<<'\t'<<symbolStack.top()<<'\t'<<current<<'\t';

        // 查找 ACTION 表
        if (actionTable.find({state, current}) != actionTable.end()) {
            auto action = actionTable[{state, current}];
            char actionType = action.first;
            string actionValue = action.second;

            if (actionType == 's') { // 移进动作
                int nextState = stoi(actionValue);
                stateStack.push(nextState);
                symbolStack.push(current);
                idx++;
                cout << "s" << nextState << "\t移进\t进栈 " << " " << current << endl;
            } else if (actionType == 'r') { // 规约动作
                char left = actionValue[0];
                string right = actionValue.substr(3);
                int count = right.size();

                // 弹出栈顶的符号和状态
                for (int i = 0; i < count; i++) {
                    stateStack.pop();
                    symbolStack.pop();
                }

                // 获取规约后的状态
                int newState = stateStack.top();
                if (gotoTable.find({newState, left}) != gotoTable.end()) {
                    int gotoState = gotoTable[{newState, left}];
                    stateStack.push(gotoState);
                    symbolStack.push(left);
                    cout << "r" << gotoState << "\t规约\t出栈 " << count << " 个符号和状态,进栈 " << " " << left << "\t" << left << " -> " << right << endl;
                } else {
                    cout << "error\t错误\tGOTO 表未定义" << endl;
                    return;
                }
            } else if (actionType == 'a') { // 接受动作
                cout << "acc\t接受\t成功接收!" << endl;
                return;
            } else { // 其他动作
                cout << actionValue << "\t未知动作" << endl;
                return;
            }
        } else {
            cout << "error\t错误\tACTION 表未定义" << endl;
            return;
        }
    }
}


int main() {
	
    ReadFile(); 
    
    constructItems();
    
    constructActionGoto();

	cout << "非终结符: ";
    for (const auto &nt : nonter) {
        cout << nt << " ";
    }
    cout << endl;
 
    cout << "终结符: ";
    for (const auto &t : ter) {
        cout << t << " ";
    }
    cout << endl;
 
    cout << "文法规则:" << endl;
    for (auto entry : grammar) {
        char lhs = entry.first;
        cout << lhs << " -> ";
        for (auto rhs : entry.second) {
            cout << rhs << " | ";
        }
        cout << endl;
    }
 
    cout << "起始符号: " << start << endl;
    
        // 打印项目集
    cout << "LR(0) 规范簇:" << endl;
    for (int i = 0; i < C.size(); i++) {
        cout << "I" << i << ":\n";
        for (const auto& item : C[i]) {
            cout << "  " << item.left << " -> ";
			cout<<item.right<<" "<<item.dot<<endl;
        }

    }
    
    cout << "\nACTION 表:\n";
    cout << "状态\t";
    for (char t : ter) cout << t << "\t";
    cout << "$\n";

    for (int i = 0; i < C.size(); i++) {
        cout << i << "\t";
        for (char t : ter) {
            if (actionTable[{i, t}].first != 'e') {
                cout << actionTable[{i, t}].first << actionTable[{i, t}].second << "\t";
            } else {
                cout << "error\t";
            }
        }
        if (actionTable.find({i, '$'}) != actionTable.end()) {
            cout << actionTable[{i, '$'}].first << actionTable[{i, '$'}].second;
        } else {
            cout << "error";
        }
        cout << "\n";
    }

    cout << "\nGOTO 表:\n";
    cout << "状态\t";
    for (char nt : nonter) cout << nt << "\t";
    cout << "\n";

    for (int i = 0; i < C.size(); i++) {
        cout << i << "\t";
        for (char nt : nonter) {
            if (gotoTable[{i, nt}] != -1) {
                cout << gotoTable[{i, nt}] << "\t";
            } else {
                cout << "error\t";
            }
        }
        cout << "\n";
    }

	predict();

    return 0;
}

2.测试数据

3.测试结果

首先,让我们来了解一下LR(0)文法和活前缀DFA。 LR(0)文法是一种用于语法分析的形式文法。它的特点是可以根据输入符号串的任意前缀在不需要回溯的情况下进行推导,即从左到右扫描输入符号串,同时从栈顶到栈底扫描LR(0)项集,直到找到一个可以进行规约的项集,或者发现输入符号串不符合文法。 活前缀DFA是一种用于确定LR(0)自动机状态的算法。它的特点是可以根据当前LR(0)项集中的活前缀(即尚未匹配完毕的输入符号)和规约符号(即待规约的非终结符)来确定下一个状态的编号。 接下来,我们可以开始编写Java代码了。首先,我们需要定义一个类来表示LR(0)项: ``` class LR0Item { private String left; // 产生式左部 private String right; // 产生式右部 private int dot; // 点的位置 public LR0Item(String left, String right, int dot) { this.left = left; this.right = right; this.dot = dot; } public String getLeft() { return left; } public String getRight() { return right; } public int getDot() { return dot; } public boolean isReduceItem() { return dot == right.length(); } public String getNextSymbol() { return right.substring(dot, dot + 1); } public LR0Item advanceDot() { return new LR0Item(left, right, dot + 1); } @Override public boolean equals(Object obj) { if (obj instanceof LR0Item) { LR0Item item = (LR0Item) obj; return left.equals(item.left) && right.equals(item.right) && dot == item.dot; } else { return false; } } @Override public int hashCode() { return left.hashCode() * 31 + right.hashCode() * 17 + dot; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append(left).append(" -> "); for (int i = 0; i < right.length(); i++) { if (i == dot) { builder.append("·"); } builder.append(right.charAt(i)); } if (dot == right.length()) { builder.append("·"); } return builder.toString(); } } ``` LR0Item类包含了一个产生式的左部、右部和点的位置。它还提供了一些方便的方法,如判断是否为规约项、获取下一个符号、移动点等。equals()、hashCode()和toString()方法用于比较、哈希和输出。 接下来,我们需要定义一个类来表示LR(0)项集: ``` class LR0ItemSet { private Set<LR0Item> items = new HashSet<>(); public void addItem(LR0Item item) { items.add(item); } public Set<LR0Item> getItems() { return items; } public Set<String> getLookaheads() { Set<String> lookaheads = new HashSet<>(); for (LR0Item item : items) { if (item.isReduceItem()) { lookaheads.addAll(getLookaheads(item.getLeft())); } } return lookaheads; } private Set<String> getLookaheads(String symbol) { Set<String> lookaheads = new HashSet<>(); for (LR0Item item : items) { if (item.getLeft().equals(symbol)) { lookaheads.add("$"); String lookahead = item.getNextSymbol(); if (!lookahead.equals("") && !lookahead.equals("$")) { lookaheads.add(lookahead); } else { lookaheads.addAll(getLookaheads(item.getLeft())); } } } return lookaheads; } public boolean containsItem(LR0Item item) { return items.contains(item); } public LR0ItemSet closure(Map<String, List<String>> grammar) { LR0ItemSet closure = new LR0ItemSet(); closure.items.addAll(items); Queue<LR0Item> queue = new LinkedList<>(items); while (!queue.isEmpty()) { LR0Item item = queue.poll(); String symbol = item.getNextSymbol(); if (!symbol.equals("") && !item.isReduceItem()) { for (String right : grammar.getOrDefault(symbol, Collections.emptyList())) { LR0Item newItem = new LR0Item(symbol, right, 0); if (!closure.containsItem(newItem)) { closure.addItem(newItem); queue.offer(newItem); } } } } return closure; } public LR0ItemSet goTo(String symbol, Map<String, List<String>> grammar) { LR0ItemSet next = new LR0ItemSet(); for (LR0Item item : items) { if (!item.isReduceItem() && item.getNextSymbol().equals(symbol)) { next.addItem(item.advanceDot()); } } return next.closure(grammar); } @Override public boolean equals(Object obj) { if (obj instanceof LR0ItemSet) { LR0ItemSet set = (LR0ItemSet) obj; return items.equals(set.items); } else { return false; } } @Override public int hashCode() { return items.hashCode(); } @Override public String toString() { StringBuilder builder = new StringBuilder(); for (LR0Item item : items) { builder.append(item).append("\n"); } return builder.toString(); } } ``` LR0ItemSet类包含了一个LR(0)项的集合。它提供了一些方便的方法,如获取向前看符号、计算闭包、计算转移等。equals()、hashCode()和toString()方法用于比较、哈希和输出。 接下来,我们需要定义一个类来表示活前缀DFA: ``` class LR0DFA { private Map<LR0ItemSet, Integer> states = new HashMap<>(); private Map<Integer, Map<String, Integer>> transitions = new HashMap<>(); public void addState(LR0ItemSet state) { if (!states.containsKey(state)) { states.put(state, states.size()); } } public int getStateCount() { return states.size(); } public int getStateId(LR0ItemSet state) { return states.get(state); } public LR0ItemSet getState(int id) { for (Map.Entry<LR0ItemSet, Integer> entry : states.entrySet()) { if (entry.getValue() == id) { return entry.getKey(); } } return null; } public void addTransition(int from, String symbol, int to) { transitions.computeIfAbsent(from, k -> new HashMap<>()).put(symbol, to); } public int getTransition(int from, String symbol) { return transitions.getOrDefault(from, Collections.emptyMap()).getOrDefault(symbol, -1); } @Override public String toString() { StringBuilder builder = new StringBuilder(); for (Map.Entry<LR0ItemSet, Integer> entry : states.entrySet()) { builder.append("State ").append(entry.getValue()).append(":\n"); builder.append(entry.getKey()).append("\n"); for (Map.Entry<String, Integer> transition : transitions.getOrDefault(entry.getValue(), Collections.emptyMap()).entrySet()) { builder.append(" ").append(transition.getKey()).append(" -> ").append(transition.getValue()).append("\n"); } } return builder.toString(); } } ``` LR0DFA类包含了一个状态编号到LR(0)项集的映射和一个转移函数。它提供了一些方便的方法,如添加状态、获取状态数量、获取状态编号、添加转移、获取转移等。toString()方法用于输出。 最后,我们可以编写主程序来实现控制台输入LR(0)文法并分析程序识别活前缀DFA构造并输出: ``` import java.util.*; public class LR0Parser { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); Map<String, List<String>> grammar = new HashMap<>(); while (true) { String line = scanner.nextLine().trim(); if (line.isEmpty()) { break; } String[] parts = line.split("\\s*->\\s*"); String left = parts[0]; String[] right = parts[1].split("\\s*\\|\\s*"); grammar.computeIfAbsent(left, k -> new ArrayList<>()).addAll(Arrays.asList(right)); } LR0DFA dfa = buildLR0DFA(grammar); System.out.println(dfa); } private static LR0DFA buildLR0DFA(Map<String, List<String>> grammar) { LR0ItemSet initial = new LR0ItemSet(); initial.addItem(new LR0Item("_S", "S", 0)); LR0DFA dfa = new LR0DFA(); dfa.addState(initial); Queue<Integer> queue = new LinkedList<>(); queue.offer(dfa.getStateId(initial)); while (!queue.isEmpty()) { int from = queue.poll(); LR0ItemSet fromSet = dfa.getState(from); Set<String> symbols = new HashSet<>(); for (LR0Item item : fromSet.getItems()) { symbols.add(item.getNextSymbol()); } for (String symbol : symbols) { LR0ItemSet toSet = fromSet.goTo(symbol, grammar); if (!toSet.getItems().isEmpty()) { dfa.addState(toSet); int to = dfa.getStateId(toSet); dfa.addTransition(from, symbol, to); if (!queue.contains(to)) { queue.offer(to); } } } } return dfa; } } ``` 主程序首先从标准输入读取LR(0)文法,并将其转换为产生式的左部和右部。接着,它调用buildLR0DFA()方法来构造LR(0)自动机的状态和转移函数,并输出结果。 buildLR0DFA()方法使用广度优先搜索算法来遍历所有可能的LR(0)项集。它首先创建一个初始项集,然后加入自动机的状态中。接着,它从状态队列中取出一个状态,并根据每个可能的终结符计算下一个状态。如果下一个状态非空且还未加入自动机的状态中,则将其加入自动机的状态中,并记录转移函数。最后,它将下一个状态加入状态队列中,以便继续搜索。 这样,我们就成功地用Java实现了控制台输入LR(0)文法并分析程序识别活前缀DFA构造并输出。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值