Blog 2020/1/10
<- previous | index | next ->
Let's learn how to write a Lisp interpreter in C!
In part 3, we implement support for reading,
evaluating, and printing C double
floating-point numbers.
This post doesn't cover any new ground (it is nearly identical to part 2), but will set up some interesting type promotion issues for us to tackle in later posts!
We add a new struct
to represent C double
floating-point numbers:
struct CDouble_ {
FormType type;
double value;
};
typedef struct CDouble_ CDouble;
int new_cdouble(CDouble** cdpp, double d);
bool is_cdouble(Form* formp);
and add an entry to our FormType enum:
TypeSymbol = 10,
TypeCLong = 20,
+ TypeCDouble = 30,
};
typedef enum FormType_ FormType;
We add a function which attempts to parse a floating-point number:
/* Tries to parse a double from buffp intp dp.
Returns true or false. */
static bool try_parse_double(const char* buffp, double* dp) {
char* endptr;
double d = strtod(buffp, &endptr);
if (errno != 0) {
errno = 0;
return false;
} else if (endptr == buffp || *endptr != '\0') {
return false;
} else {
*dp = d;
return true;
}
}
and stitch it into read_form()
:
reader.c (error handling elided):
return 0;
}
}
+
+ /* a floating-point literal. */
+ double d;
+ success = try_parse_double(buffp, &d);
+ if (success) {
+ CDouble* cdp;
+ new_cdouble(&cdp, d);
+ *formpp = (Form*)cdp;
+ return 0;
+ }
/* assume anything else is a symbol. */
Symbol* symp;
Returns 0. */
int eval_form(Form* formp, Form** resultpp) {
/* for now, all forms evaluate to themselves. */
- if (is_symbol(formp) || is_clong(formp)) {
+ if (is_symbol(formp) || is_clong(formp) || is_cdouble(formp)) {
*resultpp = formp;
return 0;
We implement support for printing CDouble
objects:
printer.c: (error handling elided):
/* Prints the CDouble in dp into fp.
Returns 0 or errno. */
static int print_cdouble(CDouble* dp, FILE* fp) {
int err = fprintf(fp, "CDouble: %f", dp->value);
return 0;
}
and stitch it into print_form()
:
} else if (is_clong(formp)) {
CLong* lp = (CLong*)formp;
return print_clong(lp, fp);
+ } else if (is_cdouble(formp)) {
+ CDouble* dp = (CDouble*)formp;
+ return print_cdouble(dp, fp);
} else {
assert(false);
}
Our interpreter understands floating-point now!
$ echo foo 42 3.14 | ./lisp
Symbol: foo
CLong: 42
CDouble: 3.140000
In part 4 we'll add support for strings!