1、制作一个简单的交互式解释器(REPL)

什么是交互式解释器(REPL)

简单解释就是:“读取值-求值-输出结果”循环(Read-Eval-Print Loop,简称REPL)1

创建简单的REPL程序

新建个db_tutorial目录,用vs code打开改目录,在目录下创建一个db.c文件。
完整代码如下:

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct 
{
    char* buffer;
    size_t buffer_length;
    ssize_t input_length;
} InputBuffer;

/*
 * 功能:从指定文件流stream中读取一行数据,保存到lineptr指向的缓冲区中
 * 参数:lineptr:指向缓冲区的指针,用于保存读取出来的一行数据
 *      n:指向缓冲区大小的指针(入参时为缓冲区大小,出参时为实际读取到的字节数)
 *      stream:指定的文件流
 * 返回值:成功返回读取到的字节数,失败返回-1
 */
ssize_t getline(char **lineptr, size_t *n, FILE *stream) 
{
    size_t size;            // 当前缓冲区大小
    size_t index;           // 当前缓冲区中已经读取的字节数
    int c;                  // 从文件流中读取的字节
    char *buf;              // 缓冲区

    if(!lineptr || !n || !stream)    // 参数检查
        return -1;

    buf = *lineptr;         // 获取缓冲区
    size = *n;              // 获取缓冲区大小

    index = 0;              // 缓冲区已经读取的字节数置0
    while((c = fgetc(stream)) != EOF)    // 逐个字节读取数据
    {
        /* 当前缓冲区已满,需要扩容 */
        if(size <= index + 1)
        {
            size += 128;               // 扩容128字节
            buf = (char *)realloc(buf, size);    // 重新分配内存
            if(!buf)                    // 内存分配失败
                return -1;
            *lineptr = buf;            // 更新缓冲区指针
            *n = size;                 // 更新缓冲区大小
        }

        buf[index++] = c;              // 将字节写入缓冲区
        if(c == '\n')                  // 读取到换行符,一行数据读取完毕
            break;
    }

    buf[index] = '\0';                 // 写入字符串结束符

    // 返回实际读取到的字节数
    return index > 0 ? (ssize_t)index : -1;
}

InputBuffer* new_input_buffer() 
{
    InputBuffer* input_buffer = (InputBuffer*)malloc(sizeof(InputBuffer));
    input_buffer->buffer = NULL;
    input_buffer->buffer_length = 0;
    input_buffer->input_length = 0;

    return input_buffer;
}

void print_prompt() 
{
    printf("db > ");
}

void read_input(InputBuffer* input_buffer) 
{
    ssize_t bytes_read = getline(&(input_buffer->buffer), &(input_buffer->buffer_length), stdin);
    if(bytes_read <= 0) {
        printf("Errorr reading input\n");
        exit(EXIT_FAILURE);
    }

    input_buffer->input_length = bytes_read - 1;
    input_buffer->buffer[bytes_read - 1] = 0;
}

void close_input_buffer(InputBuffer* input_buffer) 
{
    free(input_buffer->buffer);
    free(input_buffer);
}

int main(int argc, char* argv[]) {
    InputBuffer* input_buffer = new_input_buffer();
    while (true)
    {
        print_prompt();
        read_input(input_buffer);
        if(strcmp(input_buffer->buffer, ".exit") == 0)
        {
            close_input_buffer(input_buffer);
            exit(EXIT_SUCCESS);
        }
        else
        {
            printf("Unrecognized command '%s'.\n", input_buffer->buffer);
        }
    }
}

编译运行效果
run

代码解释

首先我们的需求是用户运行程序后在命令窗口输入命令,程序读取命令然后识别命令并做出相应的反馈。

缓存读取的输入

我们需要个连续内存,需要个字段记录这个内存大小,需要个字段记录内存中有用数据的长度。详见InputBuffer结构体内容。

读取输入

详细代码见getline函数。
第一个参数是用来存放输入内容,用指针的指针这个类型,是因为lineptr指向的指针可能为空或者lineptr指向的指针的内存长度不够,这两种情况都需要扩充内存。**lineptr是通过&(input_buffer->buffer)得到,input_buffer->bufferchar*类型。我们需要知道,buffer里面存放的是指针,即内存地址。buffer字段本身也是在内存中有个地址。当需要扩容时,我们需要改变的就是buffer自身的地址。
第二个参数是表示第一个参数中*lineptr指向的内存块的大小。这样在读取输入时,如果输入数据的长度大于内存块的长度,那么可以对改内存块进行扩容。
第三个参数表示输入流,这里传入的为stdinstdin表示指向键盘的输入。这里配合fgetcstdin中一个个读取字符。

命令识别

因为输入是字符,这里用strcmp函数对输入进行简单的判断。在main函数中strcmp(input_buffer->buffer, ".exit") == 0,只对.exit命令进行了识别。
在实际中,用户不可能一次只输入一个单词,肯定是一连串的字符。这个会在后面的内容进行实现。


  1. 读取-求值-输出循环 ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shangdibaozi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值