一、程序功能描述
[实验项目]
以专题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;
}
	
