Blog 2020/1/9
<- previous | index | next ->
Let's learn how to write a Lisp interpreter in C!
In part 2, we implement support for reading,
evaluating, and printing C long
integers.
This post demonstrates how to extend our Lisp with additional types.
We add a new struct
to represent C long
integers:
struct CLong_ {
FormType type;
long value;
};
typedef struct CLong_ CLong;
int new_clong(CLong** clpp, long l);
bool is_clong(Form* formp);
and add an entry to our FormType
enum:
_Type_UNINITIALIZED = 0,
TypeSymbol = 10,
+ TypeCLong = 20,
};
typedef enum FormType_ FormType;
We add a function which attempts to parse a token as a long
integer:
/* Tries to parse a long from buffp into lp.
Returns true or false. */
static bool try_parse_long(const char* buffp, long* lp) {
char* endptr;
long l = strtol(buffp, &endptr, 10);
if (errno != 0) {
errno = 0;
return false;
} else if (endptr == buffp || *endptr != '\0') {
return false;
} else {
*lp = l;
return true;
}
}
and stitch it into read_form()
:
reader.c (error handling elided):
if (ch1 == '\0') {
return EOF;
} else {
- /* assume every token is a symbol. */
+ bool success;
+
+ /* an integer literal. */
+ long l;
+ success = try_parse_long(buffp, &l);
+ if (success) {
+ CLong* clp;
+ new_clong(&clp, l);
+ *formpp = (Form*)clp;
+ return 0;
+ }
+
+ /* assume anything else is a symbol. */
Symbol* symp;
err = new_symbol(&symp, buffp);
if (err) {
int eval_form(Form* formp, Form** resultpp) {
/* for now, all forms evaluate to themselves. */
- if (is_symbol(formp)) {
+ if (is_symbol(formp) || is_clong(formp)) {
*resultpp = formp;
return 0;
We implement support for printing CLong
objects:
printer.c (error handling elided):
/* Prints the CLong in lp into fp.
Returns 0 or errno. */
static int print_clong(CLong* lp, FILE* fp) {
fprintf(fp, "CLong: %li", lp->value);
return 0;
}
and stitch it into print_form()
:
int print_form(Form* formp, FILE* fp) {
if (is_symbol(formp)) {
Symbol* symp = (Symbol*)formp;
return print_symbol(symp, fp);
+ } else if (is_clong(formp)) {
+ CLong* lp = (CLong*)formp;
+ return print_clong(lp, fp);
} else {
assert(false);
}
We can see that our evaluator understands integers now!
$ echo foo 1 42 | ./lisp
Symbol: foo
CLong: 1
CLong: 42
However, because our tokenizer doesn't understand lists, we aren't quite 100% there yet:
$ echo "(+ 1 2)" | ./lisp
Symbol: (+
CLong: 1
Symbol: 2)
In part 3
we will add support for C double
floating-point numbers!