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.
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 -lreadlinePortability 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);Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
