用 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语言,以及相关的开发和测试 ...
随机推荐
- #点分树#洛谷 6626 [省选联考 2020 B 卷] 消息传递
题目 多组数据多组询问,对于一个点 \(x\) 和 树上的距离 \(k\),问 \(\sum_{i=1}^n[Dis(x,i)==k]\) 分析 卡了一页的常,发现两个 \(\log\) 过不去,有一 ...
- 使用OHOS SDK构建freetype
参照OHOS IDE和SDK的安装方法配置好开发环境. 从github下载源码. 执行如下命令: git clone https://github.com/freetype/freetype.git ...
- 在nginx中使用proxy protocol协议
目录 简介 proxy protocol在nginx中应用 在nginx中配置使用proxy protocol 在nginx中启用proxy protocol 使用Real‑IP modules 请求 ...
- OpenAtom OpenHarmony分论坛圆满举办,生态与产业发展迈向新征程
7月27日,2022开放原子全球开源峰会OpenAtom OpenHarmony分论坛在北京成功举办.本次论坛以"万物互联,使能千行百业"为主题,OpenHarmony共建单位.生 ...
- 手撸jdk源码分析类加载机制
我们一般写的java文件jvm是识别不了的,因此需要编译,编译后会变成.class文件,而要执行代码,jvm首先会去加载.class文件到内存中,那么他的流程是什么样的呢: 1.首先肯定创建java虚 ...
- Python设置电脑桌面壁纸
其实 Python 设置电脑桌面壁纸是很简单的,主要是调用 win32gui 这个库来实现的 代码如下: import win32api import win32con import win32gu ...
- 如何保存/同步多架构容器 Docker 镜像
前言 随着容器.芯片技术的进一步发展,以及绿色.节能.信创等方面的要求,多 CPU 架构的场景越来越常见.典型的应用场景包括: 信创:x86 服务器 + 鲲鹏 ARM 等信创服务器: 个人电脑:苹果 ...
- 最最最简单使用Docker部署Wordpress
普通Docker部署 这种方式我用过,但是总体来说是比较麻烦的.但是可以简单说一下流程,总体流程如下: 安装Docker环境 拉取Wordpress镜像,运行镜像 拉取MySql镜像,运行镜像 Wor ...
- git合并某分支上的单次提交(cherry-pick)
1. 查找提交对应的hash值 git log 查看 d 查看下一页,q退出 vscode通过gitlens插件查看 2.合并提交 git cherry-pick hahs值
- 大厂面试题:ReentrantLock 与 synchronized异同点对比
写在开头 在过去的博文中我们学习了ReentrantLock 与 synchronized这两种Java并发使用频率最高的同步锁,在很多大厂面试题中有个经典考题: ReentrantLock 与 sy ...