编译原理实验 递归下降子程序的编写
一、实验目的
通过本实验,了解递归下降预测分析的原理和过程以及可能存在的回溯问题,探讨解决方法,为预测分析表方法的学习奠定基础。分析递归下降子程序的优缺点。
二、实验准备
1.预习自上而下语法分析小节的内容;2.学生自己考虑使用的开发环境,如VC++,熟悉开发环境。
三、实验内容
下列文法中选做一题:
1.针对算术表达式文法:
E → TE'
E' → +TE'| ε
T → FT'
T' → *FT' | ε
F → (E) | i
为其编写递归下降子程序,判定某个算术表达式是否正确:如j+k*m,j*k+m
输入:其输入数据应该为词法分析器输出的记号形式:i+i*i,i*i+i
输出:分析结果:算术表达式结构正确或结构错误。
显然这个文法属于LL(1)形文法,可以直接编写代码实现
2.给定文法(PASCAL语言标识符定义文法)(选做)
type → simple | id | array [ simple ] of type
simple → integer | char | num dotdot num
其中:dotdot 表示 ..
编写递归下降子程序,判定一个句子结构是否正确:
array [num dotdot num] of integer
输出:分析结果
显然这个文法也属于LL(1)形文法,可以直接编写代码实现
四、实验要求
1.编写程序调试运行;考虑如果将你的程序改为识别其他的文法,你的递归下降子程序可否通用,考虑递归下降子程序方法的优缺点。
2.撰写实验报告:实验名称、实验目的、实验内容、实验结果、结果分析
1
#include <iostream>
#include <string>
#include <vector>
using namespace std;
/*
递归下降语法分析器
最左推导,自顶向下
*/
/// 枚举定义了我们支持的 Token 类型
enum class TokenType {
Identifier, // i
Plus, // +
Star, // *
LParen, // (
RParen, // )
End // 结束符 #
};
/// Token 类代表词法分析器输出的一个单词
struct Token {
TokenType type;
string text;
// move 引用拷贝并解除原引用
Token(TokenType type, string text) : type(type), text(move(text)) {}
};
/// 词法分析器:将原始字符串拆分成 Token 序列
class Lexer {
public:
Lexer(const string& input) : input_(input), pos_(0) {}
/// 执行词法分析,返回 Token 列表
vector<Token> tokenize() {
vector<Token> tokens;
while (pos_ < input_.size()) {
char ch = input_[pos_];
if (isspace(ch)) {
++pos_; // 忽略空格
} else if (ch == 'i') {
tokens.emplace_back(TokenType::Identifier, "i");
++pos_;
} else if (ch == '+') {
tokens.emplace_back(TokenType::Plus, "+");
++pos_;
} else if (ch == '*') {
tokens.emplace_back(TokenType::Star, "*");
++pos_;
} else if (ch == '(') {
tokens.emplace_back(TokenType::LParen, "(");
++pos_;
} else if (ch == ')') {
tokens.emplace_back(TokenType::RParen, ")");
++pos_;
} else {
cerr << "未知字符: " << ch << endl;
++pos_;
}
}
tokens.emplace_back(TokenType::End, "#"); // 表示输入结束
return tokens;
}
private:
string input_;
size_t pos_;
};
/// 语法分析器:根据 Token 序列判断语法是否正确
class Parser {
public:
explicit Parser(const vector<Token>& tokens) : tokens_(tokens), pos_(0) {}
/// 启动分析器,从文法的起点 E 开始
bool parse() {
return E() && match(TokenType::End);
}
private:
const vector<Token>& tokens_;
size_t pos_;
/// 获取当前 Token
const Token& current() const {
return tokens_[pos_];
}
/// 匹配当前 Token 是否为指定类型,并前进
bool match(TokenType expected) {
if (current().type == expected) {
++pos_;
return true;
}
return false;
}
/// E → T E'
bool E() {
// cout << "Enter E" << endl;
return T() && EPrime();
}
/// E' → + T E' | ε
bool EPrime() {
if (current().type == TokenType::Plus) {
match(TokenType::Plus);
return T() && EPrime();
}
return true; // 空产生式
}
/// T → F T'
bool T() {
return F() && TPrime();
}
/// T' → * F T' | ε
bool TPrime() {
if (current().type == TokenType::Star) {
match(TokenType::Star);
return F() && TPrime();
}
return true; // 空产生式
}
/// F → ( E ) | i
bool F() {
if (match(TokenType::Identifier)) {
return true;
} else if (match(TokenType::LParen)) {
// 匹配左括号后必须是一个表达式,再跟右括号
if (E() && match(TokenType::RParen)) {
return true;
} else {
return false;
}
}
return false; // 不符合任何形式
}
};
int main() {
// 测试表达式列表
vector<string> tests = {
"i+i*i", // ✅
"i*i+i", // ✅
"i+*i", // ❌ 错误:+ 后不能跟 *
"(i+i)*i", // ✅
"((i))", // ✅
"i+" // ❌ 错误:+ 后缺少操作数
};
for (const auto& expr : tests) {
Lexer lexer(expr);
auto tokens = lexer.tokenize();
Parser parser(tokens);
cout << "测试表达式: " << expr << endl;
if (parser.parse()) {
cout << "✅ 表达式结构正确" << endl;
} else {
cout << "❌ 表达式结构错误" << endl;
}
cout << "-----------------------" << endl;
}
return 0;
}
2
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// ======================
// Token 类型定义
// ======================
/// 支持的词法单元(Token)类型
enum class TokenType {
Identifier, ///< 标识符(如变量名)
Number, ///< 数字常量
Integer, ///< 关键字 "integer"
Char, ///< 关键字 "char"
Array, ///< 关键字 "array"
Of, ///< 关键字 "of"
LBracket, ///< 左中括号 [
RBracket, ///< 右中括号 ]
DotDot, ///< 范围符号 ".."
End ///< 输入结束符 #
};
/// Token 结构:表示一个词法单元
struct Token {
TokenType type; ///< Token 类型
string text; ///< 原始文本
Token(TokenType type, string text)
: type(type), text(move(text)) {} // 使用移动构造提高性能
};
// ======================
// 词法分析器:Lexer
// ======================
/// 将字符串输入转换为 Token 列表
class Lexer {
public:
explicit Lexer(const string& input) : input_(input), pos_(0) {}
/// 进行词法分析,返回 Token 序列
vector<Token> tokenize() {
vector<Token> tokens;
while (pos_ < input_.size()) {
char ch = input_[pos_];
if (isspace(ch)) {
++pos_; // 跳过空白字符
} else if (isalpha(ch)) {
// 解析关键字或标识符
string ident;
while (isalnum(input_[pos_])) ident += input_[pos_++];
if (ident == "array") tokens.emplace_back(TokenType::Array, ident);
else if (ident == "of") tokens.emplace_back(TokenType::Of, ident);
else if (ident == "integer") tokens.emplace_back(TokenType::Integer, ident);
else if (ident == "char") tokens.emplace_back(TokenType::Char, ident);
else tokens.emplace_back(TokenType::Identifier, ident);
} else if (isdigit(ch)) {
// 解析数字
string num;
while (isdigit(input_[pos_])) num += input_[pos_++];
tokens.emplace_back(TokenType::Number, num);
} else if (ch == '.' && peek() == '.') {
// 范围符号 ..
tokens.emplace_back(TokenType::DotDot, "..");
pos_ += 2;
} else if (ch == '[') {
tokens.emplace_back(TokenType::LBracket, "[");
++pos_;
} else if (ch == ']') {
tokens.emplace_back(TokenType::RBracket, "]");
++pos_;
} else {
cerr << "非法字符: " << ch << endl;
++pos_;
}
}
tokens.emplace_back(TokenType::End, "#");
return tokens;
}
private:
string input_;
size_t pos_;
/// 预览下一个字符
char peek() const {
return (pos_ + 1 < input_.size()) ? input_[pos_ + 1] : '\0';
}
};
// ======================
// 语法分析器:递归下降 Parser
// ======================
/// 解析 Pascal 类型定义文法
class Parser {
public:
explicit Parser(const vector<Token>& tokens) : tokens_(tokens), pos_(0) {}
/// 启动解析,从 type 开始
bool parse() {
return parseType() && match(TokenType::End);
}
private:
const vector<Token>& tokens_;
size_t pos_;
/// 获取当前 Token
const Token& current() const {
return tokens_[pos_];
}
/// 匹配并消费一个特定类型的 Token
bool match(TokenType expected) {
if (current().type == expected) {
++pos_;
return true;
}
return false;
}
/// type → simple | id | array [ simple ] of type
bool parseType() {
if (match(TokenType::Integer) || match(TokenType::Char) || match(TokenType::Identifier)) {
return true;
}
if (match(TokenType::Array)) {
if (!match(TokenType::LBracket)) return false;
if (!parseSimple()) return false;
if (!match(TokenType::RBracket)) return false;
if (!match(TokenType::Of)) return false;
return parseType(); // 递归调用 type
}
return false;
}
/// simple → integer | char | num dotdot num
bool parseSimple() {
if (match(TokenType::Integer) || match(TokenType::Char)) {
return true;
}
if (match(TokenType::Number)) {
if (!match(TokenType::DotDot)) return false;
if (!match(TokenType::Number)) return false;
return true;
}
return false;
}
};
// ======================
// 测试程序入口
// ======================
int main() {
vector<string> tests = {
"array[3..5] of integer", // ✅ 合法
"array [ integer ] of char", // ✅ 合法
"char", // ✅ 合法
"3..5", // ❌ 错误:不是完整的 type
"array[3..x] of integer" // ❌ 错误:x 不是 num
};
for (const auto& input : tests) {
Lexer lexer(input);
auto tokens = lexer.tokenize();
Parser parser(tokens);
cout << "测试: " << input << endl;
if (parser.parse()) {
cout << "✅ 语法结构正确\n";
} else {
cout << "❌ 语法结构错误\n";
}
cout << "------------------------\n";
}
return 0;
}

浙公网安备 33010602011771号