【任务介绍】根据给定的上下文无关文法,分析任意一个算术表达式的语法结构。
【输入】任意的算术表达式。
【输出】与输入对应的一颗语法树或者错误。
【题目】设计一个程序,根据给定的上下文无关文法,构造一颗语法树来表达任意一个算术表达式的语法结构。要求:
1. 基础文法:
<Expr>-><Term><Expr1>
<Expr1>-><AddOp><Term><Expr1>|empty
<Term>-><Factor><Term1>
<Term1>-><MulOp><Factor><Term1>|empty
<Factor>->id|number|(<Expr>)
<AddOp>->+|-
<MulOp>->*|/
2. 语法分析方法采用递归子程序法。
3. 输入:形如 a+b*2/4-(b+c)*3 的算术表达式,有+、-、*、/ 四种运算符,运算符的优先级、结合规则和括号的用法遵循惯例,有变量、整数两种运算对象。为简化问题,变量和整数均为只含有1个字符的单词,忽略空格等非必要的字符。
4. 输出:输入正确时,输出其对应的语法树,树根标记为<Expr>;输入错误时,输出error。
【任务介绍】根据给定的上下文无关文法,对高级程序设计语言中常见的几种执行语句进行语法分析。
【输入】一串执行语句,其中包括:赋值语句、选择语句和循环语句。
【输出】与输入对应的一颗完整的语法树或者错误。
【题目】设计一个程序,根据给定的上下文无关文法,对于输入的一串源程序语句,构造其对应的语法树或者报告错误。要求:
1) 基础文法以<Block>为开始符号:
<Block> → { <Decls> <Statements> }
<Decls> → <Decls> <Decl> | empty
<Decl> → <Type> <NameList> ;
<NameList> → <NameList> , <Name> | <Name>
<Type> → int
<Name> → id
<Statements>的定义参照附录A中的文法定义;
2)语法分析方法采用递归子程序法。
3)输入:一串(3∽5句)执行语句,其中包括:赋值语句、选择语句和循环语句。
4)输出:输入正确时,输出其对应的语法树,树根标记为< Block>;输入错误时,输出error。
5)赋值语句:左部为1个简单变量(假设都定义为整型),右部为1个算术表达式;可以调用之前的程序来完成对这个算术表达式的分析。
6)选择语句:包含if-then单分支和if-then-else双分支两种结构,可以只考虑分支判定条件为1个简单的关系运算表达式的情况,暂不处理逻辑运算。
7)循环语句:包含while-do、do-while 和 for-each 三种结构中的任一种。
【任务介绍】递归下降的语法分析。
【输入】一个完整的源程序。
【输出】语法树或者错误。
【题目】设计一个程序,输入字符串形式的源程序,输出该程序的语法分析树,有错误时报告错误。要求:
1)源语言及其语法规则:可以参照附录A,也可以自定义。
2)输入为字符串形式的源程序,因此,需要调用前面实验做过的词法分析器,为语法分析器提供单词符号。
3)应该指出错误的具体位置,如:在xx单词之后/之前发现错误,分析中止。
实验思路:本实验的目标是实现一个语法分析器。通过递归下降解析。按照layer类的数据结构,按照文法规则递归解析
layer类:树结构类,包含构造函数, addNext-添加子节点,removeLayer-移除子节点,toString-树形输出
package Grammatical;
import java.util.ArrayList;
import java.util.List;
// 语法树类
public class layer {
final String content; // 节点内容
private final String content_length; // 控制节点内容缩进的空格字符串
final List<layer> next; // 子节点列表动态索引来访问和操作列表中的元素
// 构造函数
public layer(String content,int depth) {
this.content = content;
//深度
this.content_length = " ".repeat(depth*2); // 根据内容长度生成相应数量的空格字符串
this.next = new ArrayList<>(); // 初始化子节点列表
}
// 添加子节点
public void addNext(layer layer) {
this.next.add(layer);
}
// 移除子节点
public void removeLayer(layer layer) {
this.next.remove(layer);
}
// 内部方法,用于生成树结构字符串
private void buildString(StringBuilder builder, String prefix, String childrenPrefix) {
builder.append(prefix);
builder.append(content);
builder.append('\n');
for (int i = 0; i < next.size(); i++) {
layer nextLayer = next.get(i);
if (i < next.size() - 1) {
nextLayer.buildString(builder, childrenPrefix + "├── ", childrenPrefix + "│ ");
} else {
nextLayer.buildString(builder, childrenPrefix + "└── ", childrenPrefix + " ");
}
}
}
// 输出
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
buildString(builder, "", "");
return builder.toString();
}
}
ArithmeticParser类:解析文法。包含各种工具类(isDigit-检查字符是否为数字, isVariable-检查字符是否为变量,match-匹配字符,skipBeforeEqual,skipAfterEqual-更新 line 为等号前、后的内容) 。对算术表达式的递归下降分析。
package Grammatical;
import java.util.Scanner;
public class ArithmeticParser {
static String line; // 存储用户输入的表达式
static int index; // 用于追踪当前解析的位置
static int depth; // 树的深度
// 工具类
// ********************************************************************************************************
// 检查字符是否为数字
public static boolean isDigit(char c) {
return Character.isDigit(c);
}
// 检查字符是否为变量
public static boolean isVariable(char c) {
return Character.isLetter(c);
}
// 匹配字符
public static boolean match(char expected) {
if (index < line.length() && line.charAt(index) == expected) {
index++; // 移动解析位置到下一个字符
return true; // 返回匹配成功
}
return false; // 返回匹配失败
}
// 跳过op之前的内容并将索引移动到op处,然后更新 line 为op后的内容
public static String skipBeforeEqual(String op, String line) {
if (line != null && line.contains(op)) {
index = line.indexOf(op) + 1; // 将索引移动到op后的位置
line = line.substring(index).trim(); // 更新 line 为op后的内容
index = 0; // 重置索引为 0,因为 line 已经被更新为op后的内容
}
return line;
}
// 跳过op之前的内容并将索引移动到op处,然后更新 line 为op前的内容
public static String skipAfterEqual(String op, String line) {
if (line != null && line.contains(op)) {
index = line.indexOf(op) + 1; // 将索引移动到op后的位置
line = line.substring(0, index-1).trim(); // 更新 line 为op前的内容
index = 0; // 重置索引为 0,因为 line 已经被更新为op前的内容
}
return line;
}
// *************************************************************************************************************
// <Expr> -> <Term> <Expr1>
public static boolean expr(layer parent, int depth) {
layer exprNode = new layer("<Expr>", depth + 1); // 创建<Expr>节点
parent.addNext(exprNode); // 将<Expr>节点添加到父节点
line = skipAfterEqual(";",line);
line = skipBeforeEqual("=", line);
if (term(exprNode, depth + 2) && expr1(exprNode, depth + 2)) { // 解析<Expr>的子节点<Term>和<Expr1>
return true; // 返回解析成功
} else {
parent.removeLayer(exprNode); // 移除<Expr>节点
return false; // 返回解析失败
}
}
// <Expr1> -> <AddOp> <Term> <Expr1> | empty
public static boolean expr1(layer parent, int depth) {
layer expr1Node = new layer("<Expr1>", depth + 1); // 创建<Expr1>节点
parent.addNext(expr1Node); // 将<Expr1>节点添加到父节点
if (index >= line.length()) {
return true; // 到达末尾,返回空
}
if (match('+') || match('-')) { // 匹配加号或减号
expr1Node.addNext(new layer(String.valueOf(line.charAt(index - 1)), depth + 2)); // 添加操作符节点
if (term(expr1Node, depth + 2) && expr1(expr1Node, depth + 2)) { // 解析<Expr1>的子节点<Term>和<Expr1>
return true; // 返回解析成功
} else {
parent.removeLayer(expr1Node); // 移除<Expr1>节点
return false; // 返回解析失败
}
} else {
parent.removeLayer(expr1Node); // 移除<Expr1>节点
return true; // 返回空
}
}
// <Term> -> <Factor> <Term1>
public static boolean term(layer parent, int depth) {
layer termNode = new layer("<Term>", depth + 1); // 创建<Term>节点
parent.addNext(termNode); // 将<Term>节点添加到父节点
if (factor(termNode, depth + 2) && term1(termNode, depth + 2)) { // 解析<Term>的子节点<Factor>和<Term1>
return true; // 返回解析成功
} else {
parent.removeLayer(termNode); // 移除<Term>节点
return false; // 返回解析失败
}
}
// <Term1> -> <MulOp> <Factor> <Term1> | empty
public static boolean term1(layer parent, int depth) {
layer term1Node = new layer("<Term1>", depth + 1); // 创建<Term1>节点
parent.addNext(term1Node); // 将<Term1>节点添加到父节点
if (index >= line.length()) {
return true; // 到达末尾,返回空
}
if (match('*') || match('/')) { // 匹配乘号或除号
term1Node.addNext(new layer(String.valueOf(line.charAt(index - 1)), depth + 2)); // 添加操作符节点
if (factor(term1Node, depth + 2) && term1(term1Node, depth + 2)) { // 解析<Term1>的子节点<Factor>和<Term1>
return true; // 返回解析成功
} else {
parent.removeLayer(term1Node); // 移除<Term1>节点
return false; // 返回解析失败
}
} else {
parent.removeLayer(term1Node); // 移除<Term1>节点
return true; // 返回空
}
}
// <Factor> -> id | number | '(' <Expr> ')'
public static boolean factor(layer parent, int depth) {
layer factorNode = new layer("<Factor>", depth + 1); // 创建<Factor>节点
parent.addNext(factorNode); // 将<Factor>节点添加到父节点
if (index < line.length()) {
char currentChar = line.charAt(index);
if (isVariable(currentChar) || isDigit(currentChar)) { // 匹配变量或数字
factorNode.addNext(new layer(String.valueOf(currentChar), depth + 1)); // 添加变量或数字节点
index++; // 移动解析位置到下一个字符
return true; // 返回解析成功
} else if (match('(')) { // 匹配左括号
factorNode.addNext(new layer("(", depth + 2)); // 添加左括号节点
if (expr(factorNode, depth + 2) && match(')')) { // 解析括号内的表达式
factorNode.addNext(new layer(")", depth + 2)); // 添加右括号节点
return true; // 返回解析成功
}
}
}
parent.removeLayer(factorNode); // 移除<Factor>节点
return false; // 返回解析失败
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入算术表达式:");
line = scanner.nextLine().trim();
scanner.close();
index = 0; // 初始化解析位置
depth = 0;
if (!line.isEmpty()) {
layer root = new layer(line, depth); // 创建根节点
if (expr(root, depth + 1) && index == line.length()) { // 如果整个表达式被成功解析
System.out.println("输入正确,语法树如下:");
System.out.println(root); // 输出构造的语法树
} else {
// 输入错误,输出错误信息
System.out.println("输入错误,无法构造语法树。");
if (index < line.length()) {
System.out.println("错误位置前的内容:" + line.substring(0, index));
System.out.println("错误位置:" + line.charAt(index));
System.out.println("错误原因:不符合语法规则。");
} else {
System.out.println("错误原因:输入不完整或存在未匹配的语法结构。");
}
}
} else {
System.out.println("语法错误:无内容。");
}
}
}
SyntaxTreeGenerator类:解析文法。生成树。
package Grammatical;
import Lexer.Lexer;
import java.io.*;
import java.util.*;
import static Grammatical.ArithmeticParser.*;
import static Lexer.Identifier.isKeyword_short;
public class SyntaxTreeGenerator {
private static final List<String> lines = new ArrayList<>(); // 创建程序对象
private static String line; // 存储当前的表达式
private static List<String> words;//存储当前的表达式分割单词
private static int index; // 用于追踪当前解析的位置
// 提取括号中的条件
private static String extractCondition(char op1,char op2) {
int start = line.indexOf(op1);
int end = line.indexOf(op2, start);
if (start != -1 && end != -1) {
return line.substring(start + 1, end);
}
return "";
}
//<Block> → { <Decls> <STMTS> }
private static boolean block(layer parent,int depth){
if (!isKeyword_short(words.get(index))) {
return decls(parent, depth+2); // 返回解析成功
}else {
return stmts(parent, depth+2);
}
}
//<Decls> → <Decls> <Decl> | empty // <Decl> → <Type> <NameList> ; --> <Decls> → <Type> <NameList> ; <Decls> | empty
private static boolean decls(layer parent, int depth) {
layer declsNode = new layer("<Decls>", depth+1);
parent.addNext(declsNode); // 将节点添加到父节点
// 尝试解析声明
if (type(declsNode, depth + 2) && namelist(declsNode, depth + 2) && words.get(index).equals(" ;")) {
// 如果成功解析了声明,尝试解析下一个声明
if (decls(declsNode, depth + 2)) {
return true; // 返回解析成功
} else {
parent.removeLayer(declsNode); // 移除节点
return false; // 返回解析失败
}
} else {
return true;
}
}
//<Type> → int
private static boolean type(layer parent,int depth) {
layer typeNode = new layer("<Type>",depth+1);
parent.addNext(typeNode); // 将节点添加到父节点
if (!isKeyword_short(words.get(index))) {
layer intnode = new layer(words.get(index),depth+2);
typeNode.addNext(intnode);
index++;
return true; // 返回解析成功
} else {
parent.removeLayer(typeNode); // 移除节点
return false; // 返回解析失败
}
}
// <NameList> → <Name> <NameList1>
private static boolean namelist(layer parent, int depth) {
layer nameListNode = new layer("<NameList>", depth + 1);
parent.addNext(nameListNode); // 将节点添加到父节点
if (!name(nameListNode, depth + 1)) {
return false; // 如果无法解析第一个变量名,返回解析失败
}
// 尝试解析<NameList1>
return namelist1(nameListNode, depth + 1); // 返回解析成功
}
// <NameList1> → , <Name> <NameList1> | empty
private static boolean namelist1(layer parent, int depth) {
// 终止条件:索引超出范围或没有逗号,不再继续解析
if (index >= words.size() || !words.get(index).equals(",")) {
return true; // 返回解析成功
}
layer nameListNode1 = new layer("<NameList1>", depth + 1);
parent.addNext(nameListNode1); // 将节点添加到父节点
index++;
// 如果成功匹配逗号,则继续解析下一个变量名
if (!name(nameListNode1, depth + 2)) {
return false; // 如果无法解析变量名,返回解析失败
}
// 继续解析下一个<NameList1>
return namelist1(nameListNode1, depth + 2);
}
// <Name> → id
private static boolean name(layer parent, int depth) {
if (index >= words.size() || words.get(index).equals(",") || words.get(index).equals("=")) {
return false; // 如果索引超出范围或者当前字符是逗号,返回解析失败
}
layer nameNode = new layer("<Name>", depth+1);
parent.addNext(nameNode); // 将节点添加到父节点
layer idNode = new layer(words.get(index), depth +2);
nameNode.addNext(idNode); // 添加变量名节点
index++; // 移动索引到下一个位置
return true; // 返回解析成功
}
private static boolean stmts(layer parent, int depth) {
// 终止条件:当前索引已经超出了
if (index >= words.size()) {
return true; // 返回解析成功
}
// 如果 parent 节点没有子节点或最后一个子节点不是 <STMTS>,创建新的 <STMTS> 节点
if (parent.next.isEmpty() || !parent.next.get(parent.next.size() - 1).content.equals("<STMTS>")) {
layer stmtsNode = new layer("<STMTS>", depth + 1);
parent.addNext(stmtsNode); // 将节点添加到父节点
}
// 获取 parent 的最后一个子节点(应该是 <STMTS> 节点)
layer stmtsNode = parent.next.get(parent.next.size() - 1);
// 解析当前行的语句
if (stmt(stmtsNode, depth + 2)) {
// 如果成功解析了当前行的语句,尝试解析下一行的语句
return stmts(stmtsNode, depth + 2);
} else {
parent.removeLayer(stmtsNode);
return false; // 如果无法解析当前行的语句,返回解析失败
}
}
// <STMT> → other ;
private static boolean stmt(layer parent, int depth) {
// 检查当前行是否为空或已解析完毕
if (index >= words.size() || words.get(index).equals(";")) {
return false; // 返回解析失败
}
// 创建 <STMT> 节点
layer stmtNode = new layer("<STMT>", depth + 1);
parent.addNext(stmtNode);
// 解析 if 语句
if (words.get(index).startsWith("if") || words.get(index).startsWith("else")) {
return ifStatement(stmtNode, depth + 2); // 解析 if 语句
}
// 解析 while 语句
else if (words.get(index).startsWith("while")) {
return whileStatement(stmtNode, depth + 2); // 解析 while 语句
}
// 解析赋值语句
else if (line.contains("=")) {
return assignment(stmtNode, depth + 2); // 解析赋值语句
}
// 如果不匹配任何语句形式,返回解析失败
else {
parent.removeLayer(stmtNode);
return false;
}
}
// <STMT> → <Name> = <Expr> ;
private static boolean assignment(layer parent, int depth) {
// 解析 <Name>
if (!name(parent, depth+1)) {
// 如果无法解析 <Name>,直接返回 false
return false;
}
// 确认赋值符号为 =
String equalSign = words.get(index);
if (!equalSign.equals("=")) {
// 如果不是等号,返回 false
return false;
}
// 创建赋值符号节点
layer equalNode = new layer("=", depth+1);
parent.addNext(equalNode);
ArithmeticParser.line = line;
ArithmeticParser.index = index;
// 解析 <Expr>
if (!expr(parent, depth+1)) {
// 如果无法解析 <Expr>,返回 false
return false;
}
index = index + ArithmeticParser.index+2;
// 解析成功,返回 true
return true;
}
//<STMT> → if ( BOOL ) <STMT>
//<STMT> → if ( BOOL ) <STMT> else <STMT>
private static boolean ifStatement(layer parent, int depth) {
layer ifNode = new layer("<IF>", depth + 1); // 创建 if 节点
parent.addNext(ifNode); // 将 if 节点添加到父节点
if (line.contains("if")) {
String bool_line = extractCondition('(',')'); // 提取条件
layer boolsNode = new layer("<Bool>", depth + 2); // 创建 bool 节点
ifNode.addNext(boolsNode); // 将 bool 节点添加到 if 节点
layer boolNode = new layer(bool_line, depth + 3); // 创建 bool 节点
boolsNode.addNext(boolNode); // 将 bool 节点添加到 if 节点
index = words.indexOf(")") + 1;
if(words.get(index).equals("{")) {
// 查找 if 语句的主体部分,即 if 语句的代码块
int start = line.indexOf('{');
if (start != -1) {
String ifBody = extractCondition('{','}'); // 获取代码块内容
// 创建 if 语句代码块节点
layer ifBodyNode = new layer("<IF_BODY>", depth + 2);
ifNode.addNext(ifBodyNode); // 将 if 语句代码块节点添加到 if 节点
String[] bodyLines = ifBody.split(";");
for (String bodyLine : bodyLines) {
index = 0; // 初始化
line = bodyLine;
words = Lexer.splitIntoWords(line);//分割字符串为单词
line = line.replace(" ", "");
stmt(ifBodyNode,depth+3);
}
}
}
return true; // 解析成功
} else if (line.contains("else")) {
layer elseNode = new layer("else", depth + 1); // 创建 else 节点
ifNode.addNext(elseNode); // 将 else 节点添加到父节点
// 查找 else 语句的主体部分,即 else 语句的代码块
int start = line.indexOf('{');
if (start != -1) {
String elseBody = extractCondition('{','}'); // 获取代码块内容
// 创建 if 语句代码块节点
layer elseBodyNode = new layer("<ELSE_BODY>", depth + 2);
elseNode.addNext(elseBodyNode); // 将 else语句代码块节点添加到 if 节点
// 添加 if 语句代码块内容到节点中
String[] bodyLines = elseBody.split(";");
for (String bodyLine : bodyLines) {
index = 0; // 初始化
line = bodyLine;
words = Lexer.splitIntoWords(line);//分割字符串为单词
line = line.replace(" ", "");
stmt(elseBodyNode,depth+3);
}
}
index++; // 移动到下一个单词
return true; // 解析成功
} else {
parent.removeLayer(ifNode);
return false;
}
}
//<STMT> → while ( BOOL ) <STMT>
private static boolean whileStatement(layer parent, int depth) {
layer whileNode = new layer("<WHILE>", depth + 1); // 创建 while 节点
parent.addNext(whileNode); // 将 while 节点添加到父节点
if ((line.contains("while"))) {
String while_line = extractCondition('(',')'); // 提取条件
layer boolsNode = new layer("<Bool>", depth + 2); // 创建 bool 节点
whileNode.addNext(boolsNode); // 将 bool 节点添加到 if 节点
layer boolNode = new layer(while_line, depth + 3); // 创建 bool 节点
boolsNode.addNext(boolNode); // 将 bool 节点添加到 if 节点
// 查找 while 语句的主体部分,即 while 语句的代码块
int start = line.indexOf('{');
if (start != -1) {
// 创建 while 语句代码块节点
String whileBody = extractCondition('{','}'); // 获取代码块内容
layer whileBodyNode = new layer("<WHILE_BODY>", depth + 2);
whileNode.addNext(whileBodyNode); // 将 while 语句代码块节点添加到 while 节点
// 添加 while 语句代码块内容到节点中
String[] bodyLines = whileBody.split(";");
for (String bodyLine : bodyLines) {
index = 0; // 初始化
line = bodyLine;
words = Lexer.splitIntoWords(line);//分割字符串为单词
stmt(whileBodyNode,depth+3);
}
}
return true;
} else {
parent.removeLayer(whileNode);
return false;
}
}
public static void main(String[] args) throws Exception {
BufferedReader reader = new BufferedReader(new FileReader("Output_Lexer.txt"));
BufferedWriter writer = new BufferedWriter(new FileWriter("Output_SystanTree.txt"));
//树的深度
int depth = 0;
// 逐行读取文件内容
while ((line = reader.readLine()) != null){
lines.add(line);
}
if(!lines.isEmpty()) {
layer root = new layer("<Block>", depth); // 创建根节点
if (!lines.get(0).startsWith("{") && !lines.get(lines.size() - 1).endsWith("}")) {
System.out.println("Block_语法错误:无{}");
}
reader.close();
for (int i = 1; i < lines.size() - 1; i++) {
index = 0; // 初始化
line = null;
words = null;
line = lines.get(i);
words = Lexer.splitIntoWords(line);//分割字符串为单词
if (!block(root, depth)) {
// 输入错误,输出错误信息
System.out.println("输入错误,无法构造语法树。");
int errorIndex = index; // 记录出错位置
// 输出出错位置前的内容
if (errorIndex > 0) {
System.out.println("错误位置前的内容:" + line.substring(0, errorIndex));
}
// 输出出错位置及错误信息
System.out.println("错误位置:line ="+ i +" "+line.charAt(errorIndex));
System.out.println("错误原因:不符合语法规则。");
}
}
writer.write(root.toString()); // 将构造的语法树写入文件
writer.close(); // 关闭写入流
}else {
System.out.println("语法错误:无内容");
}
}
}
结构: 结果截图: