用c语言手搓一个500+行的类c语言解释器: 给编程初学者的编译器教程(1)- 目标和前言

简介: 这一系列教程希望面向初学者,使用c语言手工实现一个简单的解释器来玩,不需要您掌握除了c语言以外的其他前置知识,也不需要您学习过编译原理的相关知识(当然如果能对简单的数据结构有所了解的话会更好,比如树、栈等)。> 写一个能执行代码的解释器不仅是一件很有(zhuang)趣(bi)的事情,大概也可以作为刚学习完c语言的一个练手的小项目啦不同于大部分常见的其他只支持四则运算的所谓”手工解释器“教程,我们希望在代码结构尽量清晰的600行代码中,手工(不借助lex/yacc等工具)完成一个脚本语言“try”,实现以下功能:

用c语言手搓一个500+行的类c语言解释器: 给编程初学者的编译器教程(1)- 目标和前言

项目github地址及源码:
https://github.com/yunwei37/tryC

一个小目标

这一系列教程希望面向初学者,使用c语言手工实现一个简单的解释器来玩,不需要您掌握除了c语言以外的其他前置知识,也不需要您学习过编译原理的相关知识(当然如果能对简单的数据结构有所了解的话会更好,比如树、栈等)。

写一个能执行代码的解释器不仅是一件很有(zhuang)趣(bi)的事情,大概也可以作为刚学习完c语言的一个练手的小项目啦

不同于大部分常见的其他只支持四则运算的所谓”手工解释器“教程,我们希望在代码结构尽量清晰的600行代码中,手工(不借助lex/yacc等工具)完成一个脚本语言“try”,实现以下功能:

  • 选择和循环的流程控制语句
  • 支持的数据类型:双精度浮点数、字符型、字符串、浮点数数组
  • 支持函数和变量的定义、函数的递归调用、嵌套作用域

(如果看不懂下面这段也没关系,可以略过啦)

这个小玩意采用递归下降法进行语法分析,同时不显式构建语法树,不生成中间代码或目标代码,在语法分析的同时进行解释执行;

解释器可运行的代码示例

递归计算文波那契数列 1 - 15,将结果存入数组中,并打印:

# Fibonacci sequence
func fun{ 
    if(x <= 2){  
        return(1);  
    }
    y = 0;
    x = x - 1;
    y = fun(x);
    x = x - 1;
    return(y + fun(x));
};

# save the Fibonacci sequence of 1 to 15 in an array 
array arr(15);    
x = 1;
while( x <= 15 ){
    arr[x - 1] = fun(x);
    x = x + 1;
}

puts("Fibonacci sequence:");
# print the Fibonacci sequence of 1 to 15
i = 0;
while(i < 15){            
    print(arr[i]);
    i=i+1;
}

(起名困难x)这个小玩意我们就随便叫它tryC吧,当做是一个小的尝试。

本人水平有限,如有疏漏之处,还请多多指教。

部分语言规则:

  • 注释在一行内,以‘#’开头;
  • 语句以‘;’结尾
  • 赋值语句类型:

    x = 123.4;
    x = 'c';
    x = "hello world!";
  • 循环语句:

    while( bool ){
        statements
    }
  • 选择语句:

    if( bool ){
        statements
    }
    
    if( bool ){
        statements
    }else{
        statements
    }
  • 定义函数:函数参数在定义中不出现,在调用中获取;返回值为double

    func function_name{
        ...
        return(expression);
    }
    
  • 定义数组:

    array array_name(array_length);        
    
  • 输入输出:

    puts(string);
    print(num);
    read(num);

写在前面

关于写这玩意的缘由(写的很乱可以不看系列)

之前大一学c语言的时候,老师要求实现一个四则运算的计算器,于是我想...要是能给计算器加上函数和变量的定义就好啦...那大概能算一个简单的解释器?我应该怎样去实现它呢?就去查了不少资料七拼八凑加上自己脑补搓了一个出来...虽然能跑起来但是代码混乱不堪一塌糊涂,不过也挺好玩的。

这里的部分是过了一年之后大二学编译原理的时候,把当时的代码用相对比较规范完善的方式重写了一遍,也因此希望把它整理成一个简单的教程,让c语言的初学者也可以愉快地搓一个解释器玩;或者让学过编译原理的同学,能够把理论和实践联系起来,(不要像我一样被一大堆的理论迷惑住或吓跑),对于如何构造一个解释器有个直观感性的认识,并且发现它并不像想象的那么困难。

需要了解的前置知识

  • c语言的指针、函数指针、结构体等
  • 递归的思想

心理准备

  • 写一个600行的解释器虽然不算什么大工程,但相关的原理还是稍微有些复杂的,可能需要多花一些时间理解程序的运行过程;
  • 代码可能难以调试,尤其在没有生成中间代码的情况下;

参考资料

目录
相关文章
|
11月前
|
编译器 C语言
C语言编译器为什么能够用C语言编写?
C语言编译器为什么能够用C语言编写?
71 9
|
7月前
|
监控 Linux PHP
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
179 20
|
12月前
|
存储 编译器 C语言
C语言存储类详解
在 C 语言中,存储类定义了变量的生命周期、作用域和可见性。主要包括:`auto`(默认存储类,块级作用域),`register`(建议存储在寄存器中,作用域同 `auto`,不可取地址),`static`(生命周期贯穿整个程序,局部静态变量在函数间保持值,全局静态变量限于本文件),`extern`(声明变量在其他文件中定义,允许跨文件访问)。此外,`typedef` 用于定义新数据类型名称,提升代码可读性。 示例代码展示了不同存储类变量的使用方式,通过两次调用 `function()` 函数,观察静态变量 `b` 的变化。合理选择存储类可以优化程序性能和内存使用。
290 82
|
9月前
|
存储 编译器 C语言
【C语言】数据类型全解析:编程效率提升的秘诀
在C语言中,合理选择和使用数据类型是编程的关键。通过深入理解基本数据类型和派生数据类型,掌握类型限定符和扩展技巧,可以编写出高效、稳定、可维护的代码。无论是在普通应用还是嵌入式系统中,数据类型的合理使用都能显著提升程序的性能和可靠性。
357 8
|
10月前
|
C语言 开发者
C语言中的模块化编程思想,介绍了模块化编程的概念、实现方式及其优势,强调了合理划分模块、明确接口、保持独立性和内聚性的实践技巧
本文深入探讨了C语言中的模块化编程思想,介绍了模块化编程的概念、实现方式及其优势,强调了合理划分模块、明确接口、保持独立性和内聚性的实践技巧,并通过案例分析展示了其应用,展望了未来的发展趋势,旨在帮助读者提升程序质量和开发效率。
358 5
|
10月前
|
C语言
C语言编程中,错误处理至关重要,能提升程序的健壮性和可靠性
C语言编程中,错误处理至关重要,能提升程序的健壮性和可靠性。本文探讨了C语言中的错误类型(如语法错误、运行时错误)、基本处理方法(如返回值、全局变量、自定义异常处理)、常见策略(如检查返回值、设置标志位、记录错误信息)及错误处理函数(如perror、strerror)。强调了不忽略错误、保持处理一致性及避免过度处理的重要性,并通过文件操作和网络编程实例展示了错误处理的应用。
264 4
|
11月前
|
NoSQL C语言 索引
十二个C语言新手编程时常犯的错误及解决方式
C语言初学者常遇错误包括语法错误、未初始化变量、数组越界、指针错误、函数声明与定义不匹配、忘记包含头文件、格式化字符串错误、忘记返回值、内存泄漏、逻辑错误、字符串未正确终止及递归无退出条件。解决方法涉及仔细检查代码、初始化变量、确保索引有效、正确使用指针与格式化字符串、包含必要头文件、使用调试工具跟踪逻辑、避免内存泄漏及确保递归有基准情况。利用调试器、编写注释及查阅资料也有助于提高编程效率。避免这些错误可使代码更稳定、高效。
1506 12
|
11月前
|
C语言 开发者
C语言实现猜数字小游戏(详细教程)
C语言实现猜数字小游戏(详细教程)
|
11月前
|
编译器 C语言 C++
VSCode安装配置C语言(保姆级教程)
VSCode安装配置C语言(保姆级教程)
|
Linux C语言
C语言 多进程编程(七)信号量
本文档详细介绍了进程间通信中的信号量机制。首先解释了资源竞争、临界资源和临界区的概念,并重点阐述了信号量如何解决这些问题。信号量作为一种协调共享资源访问的机制,包括互斥和同步两方面。文档还详细描述了无名信号量的初始化、等待、释放及销毁等操作,并提供了相应的 C 语言示例代码。此外,还介绍了如何创建信号量集合、初始化信号量以及信号量的操作方法。最后,通过实际示例展示了信号量在进程互斥和同步中的应用,包括如何使用信号量避免资源竞争,并实现了父子进程间的同步输出。附带的 `sem.h` 和 `sem.c` 文件提供了信号量操作的具体实现。