一、程序功能描述
[实验项目]
以专题1词法分析程序的输出为语法分析的输入,完成以下描述赋值语句
的 LL(1)文法的递归下降分析程序
G[S]: S→V=E
E→TE′
E′→ATE′|ε
T→FT′
T′→MFT′|ε
F→ (E)|i
A→+|-
M→*|/
V→i
[设计说明]
终结符号 i 为用户定义的简单变量,即标识符的定义。
[设计要求]
(1)递归下降语法分析的输入为词法分析的输出二元式序列,即某算术表达式“专题 1”的输出结果,输出为输入串是否为该文法定义的算术表达式的判断结果;
(2)递归下降分析程序应能发现简单的语法错误;
(3)设计至少四个测试用例(尽可能完备,正确和出错),并给出测试结果;
(4)选做:如有可能,考虑如何用文法描述C语言的if 语句,使整个文法仍然为 LL(1)文法,并使得你的递归下降程序可以分析赋值语句和 if 语句。
二、主要数据结构
变量及类型 | 用途 |
TokenType枚举
类型: enum class |
定义词法单元的类型,如标识符、整数、运算符等。 |
Token 结构体
类型: struct |
TokenType type: 词法单元的类型。
std::string value: 词法单元的值。 表示一个词法单元。 |
keywords 常量
类型: std::unordered_map<std::string, TokenType> |
存储关键字及其对应的词法单元类型。 |
Lexer 类:
成员: std::string input: 输入字符串。 size_t position: 当前位置。 char current_char: 当前字符。
|
Lexer(const std::string& input): 构造函数,初始化输入和当前位置。
void advance(): 移动到下一个字符。 void skip_whitespace(): 跳过空白字符。 Token identifier_or_keyword(): 处理标识符或关键字。 Token number(): 处理数字。 Token single_char_token(char ch, TokenType type): 处理单字符词法单元。 Token double_char_token(std::string chars, TokenType type): 处理双字符词法单元。 Token get_next_token(): 获取下一个词法单元。 char peek(): 查看下一个字符。 |
Parser 类:
成员: std::vector<Token> tokens: 存储词法单元的向量。 size_t pos: 当前解析位置。
|
Parser(std::vector<Token>& tokens): 构造函数,初始化词法单元向量和当前位置。
Token consume(TokenType expected): 消费预期的词法单元。 bool match(TokenType type): 匹配预期的词法单元。 void expect(TokenType expected): 预期某个词法单元,否则抛出异常。 void S(), void E(), void E_prime(), void T(), void T_prime(), void F(), void A(), void M(), void V(): 语法分析的方法,分别对应不同的语法规则。 void parse(): 开始解析。 |
main 函数 | 主函数,读取输入文件,创建词法分析器和语法分析器对象,进行解析并输出结果。 |
三、程序结构描述
设计方法
递归下降分析是最常用的自顶向下分析方法。它通过一组递归函数来实现对文法规则的直接翻译。
函数定义及函数间的调用关系
函数名称 | 函数功能描述 |
Parser(std::vector<Token>& tokens) | 构造函数,初始化解析器并设置记号流。 |
void parse() | 主解析函数,启动解析过程。 |
Token consume(TokenType expected) | 消费预期的记号,如果当前记号符合预期,则返回该记号并移动指针;否则抛出异常。 |
bool match(TokenType type) | 匹配预期的记号,如果当前记号符合预期,则移动指针并返回 true;否则返回 false。 |
void expect(TokenType expected) | 期望某个记号,如果当前记号不符合预期,则抛出异常。 |
void array_declaration() | 解析数组声明语句,如 int arr[10]; |
void array_initialization() | 解析数组初始化语句,如 int arr[10] = {1, 2, 3};。 |
void expression_list() | 解析表达式列表,如 {1, 2, 3}。 |
void expression() | 解析单个表达式,如 1 + 2 或 arr[0]。 |
void array_access() | 解析数组访问表达式,如 arr[0]。 |
void parse_error(const std::string& msg) | 抛出解析错误,并附带错误信息。 |
void unexpected_token_error(TokenType expected, TokenType actual) | 处理预期记号与实际记号不匹配的情况,抛出错误信息。 |
void debug_print(const std::string& msg) | 在调试模式下输出调试信息。 |
四、程序测试
测试用例1:
x = 5; y = x + 3; z = y - 2; w = z * 4 / 2; a = (b + c) * d;
程序执行结果:
测试用例2:
int main() { int a = 10; a = a + 1; return 0 }
程序执行结果:
测试用例3:
y = (x + 3;
程序执行结果:
测试用例4:
z = y ++ 2;
程序执行结果:
五、源代码附录
#include #include #include #include #include #include #include enum class TokenType { IDENTIFIER, INTEGER, PLUS, MINUS, TIMES, DIVIDE, ASSIGN, SEMICOLON, LPAREN, RPAREN, EOF_TOKEN, INCREMENT, DECREMENT, LSHIFT, RSHIFT, PLUS_ASSIGN, MINUS_ASSIGN, TIMES_ASSIGN, DIVIDE_ASSIGN, AND, OR, NOT, LBRACE, RBRACE, // 可以继续添加其他类型 }; struct Token { TokenType type; std::string value; Token(TokenType t, std::string v) : type(t), value(v) {} }; std::ostream& operator<<(std::ostream& os, const Token& token) { return os << "(" << static_cast(token.type) << ", " << token.value << ")"; } const std::unordered_map<std::string, TokenType> keywords = { {"void", TokenType::IDENTIFIER}, // 这里假设关键字也作为标识符处理,可以根据需求调整 {"int", TokenType::IDENTIFIER}, {"float", TokenType::IDENTIFIER}, {"double", TokenType::IDENTIFIER}, {"if", TokenType::IDENTIFIER}, {"else", TokenType::IDENTIFIER}, {"for", TokenType::IDENTIFIER}, {"do", TokenType::IDENTIFIER}, {"while", TokenType::IDENTIFIER} }; class Lexer { public: Lexer(const std::string& input) : input(input), position(0), current_char(input[position]) {} Token get_next_token(); private: void advance(); void skip_whitespace(); Token identifier_or_keyword(); Token number(); Token single_char_token(char ch, TokenType type); Token double_char_token(std::string chars, TokenType type); char peek(); std::string input; size_t position; char current_char; }; void Lexer::advance() { position++; if (position < input.length()) { current_char = input[position]; } else { current_char = '\0'; // 表示已到达输入末尾 } } void Lexer::skip_whitespace() { while (current_char != '\0' && isspace(current_char)) { advance(); } } Token Lexer::identifier_or_keyword() { std::string value; while (isalnum(current_char) || current_char == '_') { value += current_char; advance(); } auto it = keywords.find(value); if (it != keywords.end()) { return Token(it->second, value); } else { return Token(TokenType::IDENTIFIER, value); } } Token Lexer::number() { std::string value; while (isdigit(current_char)) { value += current_char; advance(); } if (current_char == '.') { value += current_char; advance(); while (isdigit(current_char)) { value += current_char; advance(); } return Token(TokenType::INTEGER, value); // 假设这里处理的是浮点数 } else { return Token(TokenType::INTEGER, value); } } Token Lexer::single_char_token(char ch, TokenType type) { advance(); return Token(type, std::string(1, ch)); } Token Lexer::double_char_token(std::string chars, TokenType type) { advance(); advance(); return Token(type, chars); } Token Lexer::get_next_token() { while (current_char != '\0') { if (isspace(current_char)) { skip_whitespace(); continue; } if (isalpha(current_char) || current_char == '_') { return identifier_or_keyword(); } if (isdigit(current_char)) { return number(); } if (current_char == '+') { if (peek() == '+') { return double_char_token("++", TokenType::INCREMENT); } else if (peek() == '=') { return double_char_token("+=", TokenType::PLUS_ASSIGN); } else { return single_char_token('+', TokenType::PLUS); } } if (current_char == '-') { if (peek() == '-') { return double_char_token("--", TokenType::DECREMENT); } else if (peek() == '=') { return double_char_token("-=", TokenType::MINUS_ASSIGN); } else { return single_char_token('-', TokenType::MINUS); } } if (current_char == '*') { if (peek() == '=') { return double_char_token("*=", TokenType::TIMES_ASSIGN); } else { return single_char_token('*', TokenType::TIMES); } } if (current_char == '/') { if (peek() == '=') { return double_char_token("/=", TokenType::DIVIDE_ASSIGN); } else { return single_char_token('/', TokenType::DIVIDE); } } if (current_char == '&') { if (peek() == '&') { return double_char_token("&&", TokenType::AND); } } if (current_char == '|') { if (peek() == '|') { return double_char_token("||", TokenType::OR); } } if (current_char == '!') { if (peek() == '=') { return double_char_token("!=", TokenType::NOT); } else { return single_char_token('!', TokenType::NOT); } } if (current_char == '=') { if (peek() == '=') { return double_char_token("==", TokenType::ASSIGN); } else { return single_char_token('=', TokenType::ASSIGN); } } if (current_char == ';') { return single_char_token(';', TokenType::SEMICOLON); } if (current_char == '(') { return single_char_token('(', TokenType::LPAREN); } if (current_char == ')') { return single_char_token(')', TokenType::RPAREN); } if (current_char == '{') { return single_char_token('{', TokenType::LBRACE); } if (current_char == '}') { return single_char_token('}', TokenType::RBRACE); } std::cout << "未识别的字符: '" << current_char << "'" << std::endl; throw std::runtime_error("非法字符"); } return Token(TokenType::EOF_TOKEN, ""); } char Lexer::peek() { if (position + 1 < input.length()) { return input[position + 1]; } else { return '\0'; } } class Parser { public: Parser(std::vector& tokens) : tokens(tokens), pos(0) {} void parse(); private: std::vector tokens; size_t pos; Token consume(TokenType expected); bool match(TokenType type); void expect(TokenType expected); void S(); void E(); void E_prime(); void T(); void T_prime(); void F(); void A(); void M(); void V(); }; Token Parser::consume(TokenType expected) { if (pos < tokens.size() && tokens[pos].type == expected) { return tokens[pos++]; } else { throw std::runtime_error("Unexpected token at position " + std::to_string(pos)); } } bool Parser::match(TokenType type) { if (pos < tokens.size() && tokens[pos].type == type) { pos++; return true; } return false; } void Parser::expect(TokenType expected) { if (!match(expected)) { throw std::runtime_error("Expected token of type " + std::to_string(static_cast(expected)) + " but got " + std::to_string(static_cast(tokens[pos].type))); } } void Parser::S() { V(); expect(TokenType::ASSIGN); E(); expect(TokenType::SEMICOLON); } void Parser::E() { T(); E_prime(); } void Parser::E_prime() { std::cout << "Entering E_prime() at position " << pos << std::endl; if (tokens[pos].type == TokenType::PLUS || tokens[pos].type == TokenType::MINUS) { std::cout << "Matched ADDITIVE OPERATOR, calling T()" << std::endl; A(); T(); E_prime(); // 递归调用 E_prime() 处理后续表达式 } std::cout << "Exiting E_prime() at position " << pos << std::endl; } void Parser::T() { F(); T_prime(); } void Parser::T_prime() { std::cout << "Entering T_prime() at position " << pos << std::endl; if (tokens[pos].type == TokenType::TIMES || tokens[pos].type == TokenType::DIVIDE) { std::cout << "Matched MULTIPLICATIVE OPERATOR, calling F()" << std::endl; M(); F(); T_prime(); // 递归调用 T_prime() 处理后续表达式 } std::cout << "Exiting T_prime() at position " << pos << std::endl; } void Parser::F() { if (tokens[pos].type == TokenType::LPAREN) { std::cout << "Matched LPAREN, calling E()" << std::endl; expect(TokenType::LPAREN); E(); expect(TokenType::RPAREN); } else if (tokens[pos].type == TokenType::IDENTIFIER) { std::cout << "Matched IDENTIFIER: " << tokens[pos].value << std::endl; pos++; } else if (tokens[pos].type == TokenType::INTEGER) { std::cout << "Matched INTEGER: " << tokens[pos].value << std::endl; pos++; } else { std::cerr << "Current token: " << tokens[pos] << std::endl; throw std::runtime_error("Unexpected token in F()"); } } void Parser::A() { if (tokens[pos].type == TokenType::PLUS || tokens[pos].type == TokenType::MINUS) { std::cout << "Matched ADDITIVE OPERATOR: " << tokens[pos].value << std::endl; pos++; // 移动到下一个记号 } else { throw std::runtime_error("Expected '+' or '-'"); } } void Parser::M() { if (tokens[pos].type == TokenType::TIMES || tokens[pos].type == TokenType::DIVIDE) { std::cout << "Matched MULTIPLICATIVE OPERATOR: " << tokens[pos].value << std::endl; pos++; // 移动到下一个记号 } else { throw std::runtime_error("Expected '*' or '/'"); } } void Parser::V() { if (tokens[pos].type != TokenType::IDENTIFIER) { throw std::runtime_error("Expected identifier"); } pos++; } void Parser::parse() { try { while (pos < tokens.size() && tokens[pos].type != TokenType::EOF_TOKEN) { S(); } if (pos < tokens.size()) { throw std::runtime_error("Unexpected token after parsing complete expression"); } } catch (const std::runtime_error& e) { std::cerr << "Parse error: " << e.what() << std::endl; } } int main() { // 从 example.txt 文件中读取内容 std::ifstream input_file("example.txt"); if (!input_file.is_open()) { std::cerr << "无法打开 example.txt 文件" << std::endl; return 1; } std::string input((std::istreambuf_iterator(input_file)), std::istreambuf_iterator()); input_file.close(); // 创建 Lexer 对象 Lexer lexer(input); std::vector tokens; // 获取所有记号 Token token = lexer.get_next_token(); while (token.type != TokenType::EOF_TOKEN) { tokens.push_back(token); token = lexer.get_next_token(); } // 创建 Parser 对象并解析 Parser parser(tokens); parser.parse(); std::cout << "语法分析完成" << std::endl; return 0; }