Blog 2020/1/10
<- previous | index | next ->
A Lisp interpreter in C, part 3: floats
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!
Lisp forms
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;
The reader
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;
The evaluator
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;
The printer
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);
}
Try it out
Our interpreter understands floating-point now!
$ echo foo 42 3.14 | ./lisp
Symbol: foo
CLong: 42
CDouble: 3.140000
Next time
In part 4 we'll add support for strings!