Permalink
Browse files

Initial commit.

Right now the only expressions that are recognized are unsigned decimal
integers. Next will be signed integers in a variety of bases, plus some
more error checking.
  • Loading branch information...
ingramj committed Jul 27, 2011
0 parents commit cd83f0250ea334c3af8b261b37343f21b06bee0c
Showing with 294 additions and 0 deletions.
  1. +3 −0 .gitignore
  2. +17 −0 Makefile
  3. +43 −0 README
  4. +37 −0 error.c
  5. +7 −0 eval.c
  6. +77 −0 read.c
  7. +15 −0 rescheme.c
  8. +88 −0 rescheme.h
  9. +7 −0 write.c
@@ -0,0 +1,3 @@
+*~
+*.o
+rescheme
@@ -0,0 +1,17 @@
+CC = clang
+CFLAGS = -std=c99 -pedantic -Wall -Wextra -Werror -DDEBUG
+
+OBJECTS = error.o read.o eval.o write.o rescheme.o
+
+.PHONY: clean cleaner
+
+rescheme: rescheme.h $(OBJECTS)
+ $(CC) -o $@ $(OBJECTS)
+
+.c.o: rescheme.h
+
+clean:
+ rm -f *.o *~
+
+cleaner:
+ rm -f *.o *~ rescheme
43 README
@@ -0,0 +1,43 @@
+ReScheme - A second Scheme implementation
+=========================================
+
+ ReScheme is a "sequel" to my earlier Scheme interpreter, bs. Just as bs was
+written while I was following Peter Michaux's Scheme From Scratch articles,
+ReScheme will be following his upcoming Royal Scheme series.
+ See the LICENSE file for copyright and licensing information.
+
+Compilation and Usage
+=====================
+
+ If you don't have clang installed, change the first line of Makefile to
+"CC = gcc", or whatever is appropriate. There aren't any external dependencies
+so far.
+
+ $ make
+ $ ./rescheme
+ > 123
+ 123
+ > 9876
+ 9876
+ > a
+ rs_read: expected whitespace or digit
+ $
+
+Not much to it yet.
+
+
+Links
+=====
+
+https://github.com/ingramj/bs
+ My previous Scheme interpreter. Buggy, but it was fun to write.
+
+http://peter.michaux.ca/index#Scheme%20from%20Scratch
+https://github.com/petermichaux/bootstrap-scheme
+ Peter's original Scheme from Scratch articles, and the Bootstrap Scheme
+ interpreter. Several people followed this series and wrote their own
+ implementations.
+
+http://peter.michaux.ca/index#Royal%20Scheme
+https://github.com/petermichaux/royal-scheme
+ Peter's Royal Scheme articles (coming soon), and code.
37 error.c
@@ -0,0 +1,37 @@
+#include "rescheme.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#define BUFSIZE 1024
+
+
+void eprintf(int status, char const * const func, char const * const fmt, ...)
+{
+ int olderr = errno;
+ char buf[BUFSIZE];
+ int bytes;
+ va_list argv;
+
+ (void) fflush(stderr);
+
+ bytes = snprintf(buf, BUFSIZE, "%s: ", func);
+
+ va_start(argv, fmt);
+ bytes += vsnprintf(buf + bytes, BUFSIZE - bytes, fmt, argv);
+ va_end(argv);
+
+ if (fmt[0] != '\0' && fmt[strlen(fmt) - 1] == ':') {
+ bytes += snprintf(buf + bytes, BUFSIZE - bytes, " %s",
+ strerror(status));
+ }
+
+ fprintf(stderr, "%s\n", buf);
+ if (bytes >= BUFSIZE - 1) {
+ fprintf(stderr, "warning: error message from %s has been truncated.\n", func);
+ }
+
+ errno = olderr;
+}
7 eval.c
@@ -0,0 +1,7 @@
+#include "rescheme.h"
+
+rs_object rs_eval(rs_object expr)
+{
+ /* For now, just return expr. */
+ return expr;
+}
77 read.c
@@ -0,0 +1,77 @@
+#include "rescheme.h"
+#include <ctype.h>
+
+/* From what I can tell, the normal way to parse Lisp is with a recursive
+ descent parser. I'm taking a different approach, and hand-writing a state
+ machine. This may end up being a bad idea.
+*/
+
+
+#define BUFSIZE 1024
+
+
+/* I don't know if these defines are awesome, or abominations, but they do
+ make the state machine code more concise.
+*/
+
+/* They call it whitespace, but on my screen it's black... */
+#define WS \
+ ' ': case '\t': case '\r': case '\n'
+
+#define SEP WS
+
+#define DIGIT \
+ '0': case '1': case '2': case '3': case '4': \
+case '5': case '6': case '7': case '8': case '9'
+
+
+enum state { ST_START, ST1, ST_END };
+
+
+rs_object rs_read(FILE *in)
+{
+ int c;
+ char buf[BUFSIZE];
+ int off = 0;
+ rs_object obj;
+ enum state cur_state = ST_START;
+
+ while (cur_state != ST_END) {
+ c = getc(in);
+
+ switch (cur_state) {
+ case ST_START:
+ switch (c) {
+ case WS:
+ cur_state = ST_START;
+ break;
+ case DIGIT:
+ buf[off++] = c;
+ cur_state = ST1;
+ break;
+ default:
+ rs_fatal("expected whitespace or digit");
+ }
+ break;
+ case ST1:
+ switch (c) {
+ case DIGIT:
+ buf[off++] = c;
+ cur_state = ST1;
+ break;
+ case SEP:
+ ungetc(c, in);
+ buf[off] = '\0';
+ obj = rs_fixnum_make(strtol(buf, NULL, 10));
+ cur_state = ST_END;
+ break;
+ default:
+ rs_fatal("expected digit or separator");
+ }
+ break;
+ default:
+ rs_fatal("got into an impossible state");
+ }
+ }
+ return obj;
+}
@@ -0,0 +1,15 @@
+#include "rescheme.h"
+
+int main(void)
+{
+ rs_object obj;
+ for (;;) {
+ printf("> ");
+ obj = rs_read(stdin);
+ if (obj == EOF) break;
+ obj = rs_eval(obj);
+ rs_write(stdout, obj);
+ printf("\n");
+ }
+ return 0;
+}
@@ -0,0 +1,88 @@
+#ifndef _RESCHEME_H
+#define _RESCHEME_H
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+
+/**** error.c - error-reporting. ****/
+
+/* The error macros have a printf-like interface. If the format string ends with
+ a ':', then the result of strerror(3) is appened. The "_s" varieties take an
+ integer status code to pass to strerror(3), and the normal varieties use
+ errno.
+*/
+
+/* Display an error message and exit. */
+#define rs_fatal(...) do { \
+ eprintf(errno, __func__, __VA_ARGS__); \
+ exit(EXIT_FAILURE); \
+ } while (0)
+#define rs_fatal_s(status, ...) do { \
+ eprintf(status, __func__, __VA_ARGS__); \
+ exit(EXIT_FAILURE); \
+ } while (0)
+
+/* Display an error message. */
+#define rs_nonfatal(...) eprintf(errno, __func__, __VA_ARGS__)
+#define rs_nonfatal_s(status, ...) eprintf(status, __func__, __VA_ARGS__);
+
+
+/* Debugging messages. */
+#ifndef DEBUG
+# define TRACE(...)
+#else
+# define TRACE(...) rs_nonfatal(__VA_ARGS__)
+#endif
+
+/* This function is used by the preceding macros, and should not be called
+ directly. */
+void eprintf(int status, char const * const func, char const * const fmt, ...);
+
+
+/**** object.c - object model and memory management. ****/
+
+/* Eventually, rs_object will be able to hold every object type. For now, our
+ only objects are fixnums. */
+typedef long rs_object;
+
+/* An integer that is restricted to a fixed range. */
+typedef long rs_fixnum;
+
+/* The minimum and maximum values for fixnums will change as the implementation
+ evolves. Specifically, a few bits will be lost to "type tags". */
+#define rs_fixnum_min LONG_MIN;
+#define rs_fixnum_max LONG_MAX;
+
+/* Convert a fixnum into an object. */
+static inline rs_object rs_fixnum_make(rs_fixnum val) {
+ return (rs_object) val;
+}
+
+/* Convert an object into a fixnum. */
+static inline rs_fixnum rs_fixnum_value(rs_object obj) {
+ return (rs_fixnum) obj;
+}
+
+
+/**** read.c - s-expression parsing. ****/
+
+/* Read an s-expression from a file, and return the resulting object. */
+rs_object rs_read(FILE *in);
+
+
+/**** eval.c - object evaluation. ****/
+
+/* Evaluate expr, and return the result. */
+rs_object rs_eval(rs_object expr);
+
+
+/**** write.c - s-expression output. ****/
+
+/* Write obj's corresponding s-expression to a file, and return the
+ number of bytes written */
+int rs_write(FILE *out, rs_object obj);
+
+
+#endif
@@ -0,0 +1,7 @@
+#include "rescheme.h"
+
+
+int rs_write(FILE *out, rs_object obj)
+{
+ return fprintf(out, "%ld", (long)rs_fixnum_value(obj));
+}

0 comments on commit cd83f02

Please sign in to comment.