用 C 语言开发一门编程语言 — 交互式解释器
目录
前言
通过开发一门类 Lisp 的编程语言来理解编程语言的设计思想,本实践来自著名的《Build Your Own Lisp》。
环境
- 操作系统:CentOS7
- 编辑器:VIM
- C 语言标准:C99
编译型 vs 解释型
语言主要有两种类型:编译型和解释型。技术上,任何语言都可以被编译或解释,但是一种或另一种语言通常对于特定语言更有意义。一般来说,解释往往更加灵活,而编译往往具有更高的性能。
当我们希望开发一种解释型语言,那么建议使用编译语言(e.g. C/C ++ 或 Swift)进行开发,否则我们开发的解释型语言的性能损失以及其自身的解释器将会更加复杂。
实现交互式解释器
交互式解释器,这种系统也被叫做 REPL(Read-Evaluate-Print Loop,读取-求值-输出-循环),这种技术被广泛地应用在各种编程语言的解释器中,例如 Python 的 Shell。我们称这种模式为交互提示。
在编写一个完整的 REPL 之前,我们先实现一个简单的程序:读取用户的输入,在程序内部进行处理,然后返回一些信息给用户。
- 使用一个永循环等待用户输入并打印信息。
- 使用 stdio.h 中的 fgets 函数获取用户输入的内容,这个函数可以一直读取直到遇到换行符为止。
- 声明一个固定大小的数组缓冲区来存储用户的输入。
- 一旦获取到用户输入的字符串,就可以使用 printf 将它打印到命令行中。
创建一个 parsing.c 源文件,意为解释器:
#include <stdio.h>
/* Declare a buffer for user input of size 2048.
* 定义了一个拥有 2048 个字符长度的全局数组。这个数组中存储的数据可以在程序的任何地方获取到。
* 我们会把用户在命令中输入的语句保存到这里面来。
* static 关键字标明这个数组仅在本文件中可见。
*/
static char input[2048]; // 2Kb
int main(int argc, char *argv[]) {
/* 把一个字符串写入到标准输出 stdout,直到空字符,但不包括空字符。换行符会被追加到输出中。 */
puts("Lispy Version 0.1");
puts("Press Ctrl+c to Exit\n");
/* In a never ending loop */
while(1) {
/* Output our prompt.
* 把字符串写入到指定的 stream 中,但不包括空字符。
* 与 puts 函数区别是 fputs 不会在末尾自动加换行符。
*/
fputs("lispy> ", stdout);
/* Read a line of user input of maximum size 2048.
* 使用 fgets 函数来获取用户在命令行中输入的字符串。
* 这两个函数都需要指定写入或读取的文件。在这里,我们使用 stdin 和 stdout 作为输入和输出。
* 这两个变量都是在 <stdio.h> 中定义的,用来表示向命令行进行输入和输出。
* 当我们把 stdin 传给 fgets 后,它就会等待用户输入一串字符,并按下回车键。
* 如果得到了字串,就会把字串连同换行符存放到 input 数组中。
* 为了不让获取到的数据太大数组装不下,我们还要指定一下可以获取的最大长度为 2048。
*/
fgets(input, 2048, stdin);
/* Echo input back to user */
printf("You're a %s", input);
}
return 0;
}
运行:
$ ./parsing
Lispy Version 0.1
Press Ctrl+c to Exit
lispy> fanguiju
You're a fanguiju
lispy> ^C
使用 GNU Readline 函数库
上述完成了最基本的交互式功能(输入、输出),如果你用的是 Mac 或 Linux,当你用左右箭头键编辑在程序中的输入时,你会遇到一个奇怪的问题:使用箭头键不会前后移动输入的光标,而是会产生像 ^[[D 或 ^[[C 这种奇怪的字符。如下所示。
Lispy Version 0.0.0.0.3
Press Ctrl+c to Exit
lispy> hel^[[D^[[C
注:在 Windows 上则不会有这个现象。
所以,我们还需要完成 Shell 具有的一些特性,例如:使用左右箭头对指令行进行编辑、使用上下箭头来获取历史输入。这里,我们使用 GNU Readline 库来进行改造,把 fputs 和 fgets 替换为这个库提供的相同功能的函数。
安装 GNU Readline 函数库:
yum install readline-devel -y
查看:
$ ll /usr/include | grep readline
drwxr-xr-x 2 root root 143 4月 7 18:06 readline
GNU Readline 函数库提供的两个函数:readline 和 add_history。
- readline 函数:和 fgets 一样,从命令行读取一行输入,并且允许用户使用左右箭头对指令行进行编辑。
- add_history 函数:可以纪录下我们之前输入过的命令,并运行用户使用上下箭头来获取历史输入。
#include <stdio.h>
#include <stdlib.h>
#include <readline/readline.h>
#include <readline/history.h>
int main(int argc, char *argv[]) {
puts("Lispy Version 0.1");
puts("Press Ctrl+c to Exit\n");
while(1) {
char *input = NULL;
/* Output our prompt and get input.
* 使用 readline 读取用户输入。
* 与 fgets 不同的是,readline 并不在结尾添加换行符。
* 所以我们在 printf 函数中添加了一个换行符。
*/
input = readline("lispy> ");
/* Add input to history.
* 使用 add_history 将该输入添加到历史纪录当中。
*/
add_history(input);
printf("You're a %s\n", input);
/* Free retrieved input.
* 还需要使用 free 函数手动释放 readline 函数返回给我们的缓冲区 input。
* 因为 readline 不同于 fgets 函数,后者使用已经存在的空间,而前者会申请一块新的内存,所以需要手动释放。
*/
free(input);
}
return 0;
}
编译:
gcc -std=c99 -Wall parsing.c -o parsing -lreadline
-l<lib_name>:指定程序要链接的库。- -std:指定 C 语言标准。
运行:
$ ./parsing
Lispy Version 0.1
Press Ctrl+c to Exit
lispy> fanguiju
You're a fanguiju
lispy> fanguiju # 使用上箭头获取历史输入记录
You're a fanguiju
lispy>
用 C 语言开发一门编程语言 — 交互式解释器的更多相关文章
- python语言开发环境配置
原作者:龙行天下-super 地址:https://www.cnblogs.com/longxingtianxia/p/10181901.html 要点:IDLE是一个轻量级python语言开发环境, ...
- Mac OSX下Go语言开发环境的搭建与配置--使用InteliJ IDEA 13
折腾了一上午终于把go语言的ide配置好了. 其实GO语言的语法和特性早在去年的时候就学习了一遍.结果后来一直没机会进行开发,结果还是个GO小白.感叹一下,要学好一门编程语言唯一的途径就是多写代码.. ...
- Go语言开发环境的搭建(Goland和VSCode)
教程首发于:微信公众号<Go编程时光>,欢迎你一起来学习 1. 下载安装 Go语言 下载地址:https://golang.google.cn/dl/ 下载完成后,直接双击 msi 文件进 ...
- Go语言开发
Go语言圣经(中文版) Go编程语言规范 搭建Go开发及调试环境(LiteIDE + GoClipse) -- Windows篇 Go开发工具 Go命令行操作命令详细介绍 ...
- Go语言开发第一个Hello,World
在网上看到go语言的各种评价,也是闻名已久,但是没有自己实践过,也不知道它的好,它的坏,今天就来试试第一个小程序 第一步.如何下载 1)下载go安装程序 下载地址:https://golang.org ...
- Go语言开发环境配置
一.我为什么要学习go语言 当今已经是移动和云计算时代,Go出现在了工业向云计算转型的时刻,简单.高效.内 置并发原语和现代的标准库让Go语言尤其适合云端软件开发(毕竟它就是为此而设计的).到2014 ...
- 利用Scala语言开发Spark应用程序
Spark内核是由Scala语言开发的,因此使用Scala语言开发Spark应用程序是自然而然的事情.如果你对Scala语言还不太熟悉,可 以阅读网络教程A Scala Tutorial for Ja ...
- (转载)Go语言开发环境配置
一.我为什么要学习go语言 当今已经是移动和云计算时代,Go出现在了工业向云计算转型的时刻,简单.高效.内 置并发原语和现代的标准库让Go语言尤其适合云端软件开发(毕竟它就是为此而设计的).到2014 ...
- Go语言开发环境安装
Go是Google开发的一种编译型,並發型,并具有垃圾回收功能的编程语言. 去http://golang.org/doc/install#download 下载相应的版本. 1.安装go语言:2.将g ...
- 第一行代码:以太坊(2)-使用Solidity语言开发和测试智能合约
智能合约是以太坊的核心之一,用户可以利用智能合约实现更灵活的代币以及其他DApp.不过在深入讲解如何开发智能合约之前,需要先介绍一下以太坊中用于开发智能合约的Solidity语言,以及相关的开发和测试 ...
随机推荐
- Jetty的threadpool模块
Jetty提供的线程池相关的模块,如下: threadpool threadpool-virtual,使用JDK 21提供的virtual threads. threadpool-virtual-pr ...
- Java处理关键字进行脱敏操作
1.通过表头获取需要处理的下标列 注:此处导出表格时对关键字进行脱敏处理 /** * . * 对表头进行过滤判断 * * @param headers 表头 * @return 对应的下标列及方法名 ...
- Java List集合去重、过滤、分组、获取数据、求最值、合并、排序、跳数据和遍历
前言 请各大网友尊重本人原创知识分享,谨记本人博客:南国以南i. 准备工作:现有一个User类.Student 类和Ticket类,加入相关依赖 @Data public class User { / ...
- 如何在HarmonyOS对数据库进行备份,恢复与加密
数据库备份与恢复 场景介绍 当应用在处理一项重要的操作,显然是不能被打断的.例如:写入多个表关联的事务.此时,每个表的写入都是单独的,但是表与表之间的事务关联性不能被分割. 如果操作的过程中出现问题, ...
- 超强阵容!HarmonyOS极客马拉松2023专家评审团来袭!
数十位重量级专家现身决赛现场,为参赛者提供多角度专业点评.12支队伍,46位选手,齐聚东莞·松山湖,围绕HarmonyOS技术特性,共同挑战36小时极限编程,谁将问鼎决赛之巅,8.3日-5日,我们拭 ...
- 什么是报表工具?和 EXCEL 有什么区别?
报表是什么? 带数据的表格和图表就都是报表,像工资表,考勤表,成绩表,资产负载表等等都是报表. 那报表工具,顾名思义就是用来做报表的工具,那 Excel 是不是也算报表工具?广义上讲当然也算.但 IT ...
- nginx重新整理——————http请求的11个阶段中的access阶段[十五]
前言 简单介绍一下access 阶段. 正文 限制某些ip地址的访问权限: syntax: allow address| CIDR|unix:|all; defuat: - context: http ...
- sql 语句系列(更新系列)[八百章之第六章]
使用另一个表更新记录 有时候我们的数据不会立即去更新,而是存在另外一张表中等待更新,这是在日常开发中常见的操作. update e set e.SAL=ns.SAL+e.SAL, e.COMM=ns. ...
- 重新点亮linux 命令树————网络配置的查看[十一三]
前言 简单整理一下网络配置. 正文 通过ifconfig 查看. 这个就是ip地址. 网卡mac地址. 还有一块信息非常重要: 这个io开头的信息,这里面就是我们127.0.0.1的信息. 那么就来演 ...
- jenkins 持续集成和交付 —— git hook(七)
前言 这个hook的意思叫做钩子哈,前端听得多. 正文 好吧,这个git hook 有什么用呢? 前面说了一个轮询SCM这个东西呢,我是真的觉得这东西没啥用,经常扫描仓储算怎么回事呢? 但是如果主动通 ...