Build a Cross‑Platform C REPL with GNU Readline and MPC Parser

This guide walks through building a cross‑platform interactive C REPL, covering basic input/output loops, integrating GNU Readline for line editing and history, handling portability with preprocessor directives, and using the MPC library to create a simple Doge language parser.

AI Cyberspace
AI Cyberspace
AI Cyberspace
Build a Cross‑Platform C REPL with GNU Readline and MPC Parser

Interactive REPL

Parsing‑oriented programming languages require a parser to translate source code into executable instructions. Implementing such a language in C involves four main parts: designing syntax and semantics, implementing the parser, implementing the executor, and providing utility libraries.

Design syntax and semantics : This tutorial focuses on a Lisp‑like syntax called Doge .

Implement the parser : We first use a simple loop with fgets and printf, then replace them with GNU Readline for line editing and history.

Implement the executor : The C program itself executes the input.

Provide utility libraries : Additional helper functions can be added as needed.

Basic REPL Implementation

# mkdir lispy
$ cd lispy
$ code . // use VS Code IDE
#include <stdio.h>
#define INPUT_BUFF_SIZE 2048 // input buffer size
static char input[INPUT_BUFF_SIZE];
int main(int argc, char *argv[]) {
    puts("Lispy Version 0.1");
    puts("Press Ctrl+c to Exit
");
    while (1) {
        fputs("lispy> ", stdout);
        fgets(input, INPUT_BUFF_SIZE, stdin);
        printf("You're a %s", input);
    }
    return 0;
}

Running the program yields basic input/output functionality, but arrow keys produce escape sequences like ^[[D on Unix systems.

Using GNU Readline

GNU Readline adds line editing and history support.

#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
");
    while (1) {
        char *input = readline("lispy> ");
        add_history(input);
        printf("You're a %s
", input);
        free(input);
    }
    return 0;
}

Compile with:

gcc -std=c99 -Wall main.c -o main -lreadline

Portability with Preprocessor Directives

To make the code compile on Windows, Linux, and macOS, we use conditional compilation to provide fake readline and add_history functions on Windows and include the real library on Unix‑like systems.

#include <stdio.h>
#include <stdlib.h>
#ifdef _WIN32
#include <string.h>
static char buffer[2048];
char *readline(char *prompt) {
    fputs(prompt, stdout);
    fgets(buffer, 2048, stdin);
    char *cpy = malloc(strlen(buffer) + 1);
    strcpy(cpy, buffer);
    cpy[strlen(cpy) - 1] = '\0';
    return cpy;
}
void add_history(char *unused) {}
#else
#ifdef __linux__
#include <readline/readline.h>
#include <readline/history.h>
#endif
#ifdef __MACH__
#include <readline/readline.h>
#endif
#endif
int main(int argc, char *argv[]) {
    puts("Lispy Version 0.1");
    puts("Press Ctrl+c to Exit
");
    while (1) {
        char *input = NULL;
        input = readline("lispy> ");
        add_history(input);
        printf("You're a %s
", input);
        free(input);
    }
    return 0;
}

Using the MPC Library to Build a Parser

MPC (Micro Parser Combinators) simplifies creating parsers for custom languages.

/* Build a parser 'Adjective' */
mpc_parser_t *Adjective = mpc_or(4,
    mpc_sym("wow"), mpc_sym("many"),
    mpc_sym("so"),  mpc_sym("such")
);
/* Build a parser 'Noun' */
mpc_parser_t *Noun = mpc_or(5,
    mpc_sym("lisp"), mpc_sym("language"),
    mpc_sym("book"), mpc_sym("build"),
    mpc_sym("c")
);
/* Phrase = Adjective followed by Noun */
mpc_parser_t *Phrase = mpc_and(2, mpcf_strfold, Adjective, Noun, free);
/* Doge = zero or more Phrases */
mpc_parser_t *Doge = mpc_many(mpcf_strfold, Phrase);

Example Doge sentences:

"wow book such language many lisp"
"so c such build such language"
"many build wow c"
""
"wow lisp wow c many language"
"so c"

A more concise definition uses mpca_lang with a grammar string:

mpc_parser_t *Adjective = mpc_new("adjective");
mpc_parser_t *Noun      = mpc_new("noun");
mpc_parser_t *Phrase    = mpc_new("phrase");
mpc_parser_t *Doge      = mpc_new("doge");
mpca_lang(MPCA_LANG_DEFAULT,
  "adjective : \"wow\" | \"many\" | \"so\" | \"such\";
"
  "noun      : \"lisp\" | \"language\" | \"book\" | \"build\" | \"c\";
"
  "phrase    : <adjective> <noun>;
"
  "doge      : <phrase>*;",
  Adjective, Noun, Phrase, Doge);
mpc_cleanup(4, Adjective, Noun, Phrase, Doge);
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

CMPCreplGNU Readline
AI Cyberspace
Written by

AI Cyberspace

AI, big data, cloud computing, and networking.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.