关于编译原理——递归下降分析器的设计

学习目标:根据文法编制递归下降分析程序,以便对输入的符号串进行语法分析。通过编写递归下降分析程序掌握递归下降分析法的基本原理以及递归下降分析程序的构造方法。


1.分析原理

①E->TE'

②E'->+TE' | ---TE' |ε

③T->FT'

④T'->*FT' | /FT' |ε

⑤F->(E) | id | num

注:文法中id表示标识符(此处标识符的定义与实验一中标识符的定义相同),num表示数字(简单处理可以认为是整数)

2.具体案例分析

输入一个字符串,输出该字符串是否为正确的句子。

例如:输入a+xyz*10+(c/d),输出"正确的表达式"

输入a+*xyz,输出"错误的表达式"

3.设计思路

可以将整个系统分为主函数、解析函数以及辅助函数三个模块:

①主函数模块:main,负责读取输入表达式并调用解析函数。

②解析函数模块:parse_expression、parse_E、parse_E_prime、parse_T、parse_T_prime、parse_F,分别对应文法中的非终结符号。

③辅助函数模块:skip_whitespace、match、is_id_char、is_num_char,用于处理空白字符、字符匹配、判断标识符字符和数字字符。

4.模块关系简图

5.函数接口

①主函数:int main(void)

功能:读取输入表达式,调用解析函数,输出解析结果。

②解析函数:bool parse_F(void)、bool parse_T_prime(void)、bool parse_T(void)、bool parse_E_prime(void)、bool parse_E(void)、bool parse_expression(void)

参数:无

返回值:解析成功返回true,失败返回false。

③辅助函数:void skip_whitespace(void)、bool match(char expected)、bool is_id_char(char c)、bool is_num_char(char c)

参数:根据函数功能而定。

返回值:根据函数功能而定。

6.测试方案及结果

①测试方案:样例表达式a+xyz*10+(c/d)

②测试方案:样例表达式a+*xyz

7.优缺点分析

优点:实现简单直观,易于理解和调试,不需要复杂的表格结构;对语法错误的处理比较灵活。

缺点:只能处理LL(k)文法,特别是LL(1)文法;需要手动处理左递归和左公因子;递归调用可能导致栈溢出等等。

8.学习小结

在本次设计中,我通过编写递归下降分析程序,掌握递归下降分析法的基本原理以及递归下降分析程序的构造方法,不仅提升了我的编程和调试能力,让我掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单词的词法分析方法,体现了编译原理中"分治"和"递归"的核心思想,也让我对编译器的工作原理有了更深入的认识,为后续学习打下了坚实基础。

附录:主要方法的源代码

复制代码
#include <stdio.h>
#include <stdbool.h>
#include <ctype.h>
#include <string.h>
 
 // 函数原型声明
bool parse_F(void);
bool parse_T_prime(void);
bool parse_T(void);
bool parse_E_prime(void);
bool parse_E(void);
bool parse_expression(void);

// 定义输入字符串的最大长度
#define MAX_INPUT_LENGTH 1024
 
// 当前解析的索引
int current_index = 0;
 
// 输入字符串
char input_str[MAX_INPUT_LENGTH];
 
// 判断是否为标识符(简单处理,只包含字母和下划线,以字母开头)
bool is_id_char(char c) {
    return isalpha(c) || c == '_';
}
 
// 判断是否为数字(简单处理,只处理整数)
bool is_num_char(char c) {
    return isdigit(c);
}
 
// 跳过空白字符
void skip_whitespace() {
    while (current_index < strlen(input_str) && isspace(input_str[current_index])) {
        current_index++;
    }
}
 
// 匹配并消耗一个字符
bool match(char expected) {
    if (current_index < strlen(input_str) && input_str[current_index] == expected) {
        current_index++;
        return true;
    }
    return false;
}
 
// 解析因子 F
bool parse_F() {
    skip_whitespace();
    if (match('(')) {
        if (!parse_E()) {
            return false;
        }
        if (!match(')')) {
            fprintf(stderr, "表达式错误:缺少闭合括号 ')' 在位置 %d\n", current_index);
            return false;
        }
        return true;
    } else if (is_id_char(input_str[current_index])) {
        while (current_index < strlen(input_str) && (is_id_char(input_str[current_index]) || isdigit(input_str[current_index]))) {
            current_index++;
        }
        return true;
    } else if (is_num_char(input_str[current_index])) {
        while (current_index < strlen(input_str) && is_num_char(input_str[current_index])) {
            current_index++;
        }
         //可以扩展为支持浮点数,这里仅处理整数
         if (match('.')) {
             while (current_index < strlen(input_str) && is_num_char(input_str[current_index])) {
                 current_index++;
             }
         }
        return true;
    } else {
        fprintf(stderr, "表达式错误:无效的因子 在位置 %d\n", current_index);
        return false;
    }
}
 
// 解析 T'
bool parse_T_prime() {
    skip_whitespace();
    if (match('*')) {
        if (!parse_F()) {
            return false;
        }
        return parse_T_prime() || true; // 递归或 ε
    } else if (match('/')) {
        if (!parse_F()) {
            return false;
        }
        return parse_T_prime() || true; // 递归或 ε
    } else {
        return true; // ε
    }
}
 
// 解析 T
bool parse_T() {
    if (!parse_F()) {
        return false;
    }
    return parse_T_prime();
}
 
// 解析 E'
bool parse_E_prime() {
    skip_whitespace();
    if (match('+')) {
        if (!parse_T()) {
            return false;
        }
        return parse_E_prime() || true; // 递归或 ε
    } else if (match('-')) {
        if (!parse_T()) {
            return false;
        }
        return parse_E_prime() || true; // 递归或 ε
    } else {
        return true; // ε
    }
}
 
// 解析 E
bool parse_E() {0
    if (!parse_T()) {
        return false;
    }
    return parse_E_prime();
}
 
// 主解析函数
bool parse_expression() {
    current_index = 0;
    return parse_E();
}
 
int main() {
    printf("请输入算术表达式:");
    fgets(input_str, MAX_INPUT_LENGTH, stdin);
    
    // 移除换行符
    input_str[strcspn(input_str, "\n")] = 0;
    
    if (parse_expression()) {
        printf("正确的表达式\n");
    } else {
        printf("错误的表达式\n");
    }
    
    return 0;
}
相关推荐
玩代码40 分钟前
CompletableFuture 详解
java·开发语言·高并发·线程
剪一朵云爱着1 小时前
力扣2438. 二的幂数组中查询范围内的乘积
算法·leetcode
hz_zhangrl1 小时前
CCF-GESP 等级考试 2025年6月认证C++三级真题解析
开发语言·c++·青少年编程·gesp·gesp2025年6月·c++三级
人生在勤,不索何获-白大侠2 小时前
day21——特殊文件:XML、Properties、以及日志框架
xml·java·开发语言
肥猪猪爸3 小时前
BP神经网络对时序数据进行分类
人工智能·深度学习·神经网络·算法·机器学习·分类·时序数据
dongzhenmao4 小时前
P1484 种树,特殊情形下的 WQS 二分转化。
数据结构·c++·windows·线性代数·算法·数学建模·动态规划
Dxy12393102164 小时前
Python PDFplumber详解:从入门到精通的PDF处理指南
开发语言·python·pdf
Dcs4 小时前
用不到 1000 行 Go 实现 BaaS,Pennybase 是怎么做到的?
java
EutoCool5 小时前
Qt:布局管理器Layout
开发语言·c++·windows·嵌入式硬件·qt·前端框架
Cyanto6 小时前
Spring注解IoC与JUnit整合实战
java·开发语言·spring·mybatis