Skip to content

Commit

Permalink
REPL and integers
Browse files Browse the repository at this point in the history
  • Loading branch information
petermichaux committed Jul 23, 2011
1 parent 7ac9545 commit 6c6a718
Show file tree
Hide file tree
Showing 11 changed files with 359 additions and 622 deletions.
3 changes: 3 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
0.1.0 - July 22, 2011 - Peter Michaux
- initial REPL
- decimal integers
652 changes: 33 additions & 619 deletions LICENSE

Large diffs are not rendered by default.

26 changes: 26 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.PHONY: clean cleaner

scm: scm.h util.o model.o read.o eval.o print.o repl.c
cc -Wall -ansi -o scm util.o model.o read.o eval.o \
print.o repl.c

util.o: scm.h util.c
cc -Wall -ansi -c util.c

model.o: scm.h model.c
cc -Wall -ansi -c model.c

read.o: scm.h read.c
cc -Wall -ansi -c read.c

eval.o: scm.h eval.c
cc -Wall -ansi -c eval.c

print.o: scm.h print.c
cc -Wall -ansi -c print.c

clean:
rm -f *.o

cleaner: clean
rm -f scm
44 changes: 41 additions & 3 deletions README
Original file line number Diff line number Diff line change
@@ -1,10 +1,48 @@
A Scheme interpreter based on a byte code virtual machine. Implemented in ANSI C.
Royal Scheme will eventually grow to be a real, usable,
embeddable Scheme implemented in ANSI C.

A primary goal of Royal Scheme is documenting the
development of a real language implementation to show
that mortals can accomplish the task.

Thanks to Abdulaziz Ghuloum for his paper "An Incremental
Approach to Compiler Construction". The pedagogy of his
paper which inspired the incremental development of
Royal Scheme.

Thanks to Alex Shinn for making his Chibi-Scheme
implementation open source. Chibi-Scheme has inspired
much of the code in Royal Scheme.

----

Example build and use.

$ cd royal-scheme
$ make
cc -Wall -ansi -c util.c
cc -Wall -ansi -c model.c
cc -Wall -ansi -c read.c
cc -Wall -ansi -c eval.c
cc -Wall -ansi -c print.c
cc -Wall -ansi -o scm util.o model.o read.o eval.o \
print.o repl.c
$ ./scm
Welcome to Royal Scheme. Ctrl-c to exit.
> 123
123
> +123
123
> -123
-123
> a
error: scm_read: unexpected char '\141'

----

For more information see
For more information see:

http://peter.michaux.ca/index#Royal%20Scheme
http://peter.michaux.ca/index#Royal%20Scheme
http://github.com/petermichaux/royal-scheme

----
Expand Down
9 changes: 9 additions & 0 deletions eval.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include "scm.h"

/* Until we have lists and symbols, just echo.
* The result of this echoing is that the repl
* application is a simple pretty printer.
*/
scm_object scm_eval(scm_object expression) {
return expression;
}
3 changes: 3 additions & 0 deletions model.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#include "scm.h"

/* nothing here yet */
17 changes: 17 additions & 0 deletions print.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include <stdio.h>
#include "scm.h"

/* if an error occurs returns negative */
int scm_write(FILE *out, scm_object object) {
int result;

/* Cast the fixnum's value to long because we need to
* specify the format string yet we don't know the size
* of a fixnum's value here. Casting to long cannot
* loose information so the cast is safe (i.e. won't
* print an incorrect value.)
*/
result = fprintf(out, "%ld",
(long)scm_fixnum_value(object));
return result;
}
120 changes: 120 additions & 0 deletions read.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#include <stdio.h>
#include <ctype.h>
#include "scm.h"

int scm_is_delimiter(int c) {
return c == '\t' || c == '\n' ||
c == '\r' || c == ' ' ||
c == ')' || c == ';' ||
c == EOF;
}

int scm_digit_value(int c) {
return c - '0';
}

scm_object scm_read_number(FILE *in) {
char is_negative = 0;
int c;
scm_int num = 0, tmp;

c = getc(in);
if (c == '-') {
is_negative = 1;
c = getc(in);
}
if (!isdigit(c)) {
scm_fatal("scm_read_number: "
"number must start with a digit");
}
while (isdigit(c)) {
tmp = num * 10 + scm_digit_value(c);
/* The next line assumes that if adding c in the
* line causes tmp to overflow, the overflow will
* mean tmp is less than num. This will be true
* if the base of the number being read is small
* in comparison to the size of tmp.
*/
if ((tmp < num) || (tmp > scm_fixnum_max)) {
scm_fatal("scm_read_number: number too large");
}
num = tmp;
c = getc(in);
}
if (!scm_is_delimiter(c)) {
scm_fatal("scm_read_number: "
"number not followed by a delimiter");
}
if (c != EOF) {
c = ungetc(c, in);
if (c == EOF) {
scm_fatal("scm_read_number: could not ungetc");
}
}
/* The next line assumes that any value of num, which
* is not greater than scm_fixnum_max, can be negated
* and still fit into a fixnum. This is true because
* of assumption of two's complement hardware.
*/
return scm_fixnum_make(is_negative ? -num : num);
}

scm_object scm_read(FILE *in) {
int c0, c1;
scm_object result;

loop:
switch (c0 = getc(in)) {
case ' ':
case '\t':
case '\r':
case '\n':
goto loop;
case ';':
/* comments continue to the end of the line */
while ((c0 = getc(in)) != EOF) {
if (c0 == '\n') {
break;
}
}
goto loop;
case '+':
case '-':
c1 = getc(in);
if (isdigit(c1)) {
c1 = ungetc(c1, in);
if (c1 == EOF) {
scm_fatal("scm_read: could not ungetc");
}
result = scm_read_number(in);
if (c0 == '-') {
/* We know scm_read_number returns a
* positive result in this case and
* so we can negate with our assumption
* of two's complement architecture.
*/
result = scm_fixnum_make(
- scm_fixnum_value(result));
}
}
else {
/* TODO Add symbol support for the symbols
* '+', '-' and those starting with '+' '-'.
*/
scm_fatal("scm_read: symbols not supported");
}
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
c0 = ungetc(c0, in);
if (c0 == EOF) {
scm_fatal("scm_read: could not ungetc");
}
result = scm_read_number(in);
break;
default:
scm_fatal("scm_read: unexpected char '\\%o'", c0);
}

return result;
}
38 changes: 38 additions & 0 deletions repl.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include <stdio.h>
#include "scm.h"

void repl(void) {
int p;
scm_object object;

while (1) {
p = printf("> ");
if (p < 0) {
scm_fatal("repl: print failed");
}
/* TODO Currently scm_read and scm_eval exit in the
* case of failure. Eventually scm_read and
* scm_eval will return Scheme error objects in the
* case of failure. For example, scm_read may
* return a syntax error for bad user input. Check
* the return value for errors and report them to
* the user.
*/
object = scm_read(stdin);
object = scm_eval(object);
p = scm_write(stdout, object);
if (p < 0) {
scm_fatal("repl: print failed");
}
p = printf("\n");
if (p < 0) {
scm_fatal("repl: print failed");
}
}
}

int main(void) {
printf("Welcome to Royal Scheme. Ctrl-c to exit.\n");
repl();
return 0;
}
47 changes: 47 additions & 0 deletions scm.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#ifndef SCM_H
#define SCM_H

#include <stdio.h>
#include <limits.h>

/************************* util.c *************************/

void scm_fatal(char *fmt, ...);

/************************ model.c *************************/

/* Assume scm_int is large enough to hold any
* scm_object fixnum.
*/
typedef long scm_int;

/* A scm_object can be any of Scheme's many types:
* the empty list, boolean, number, symbol,
* pair, vector, string, port, etc. For now we only have
* fixnums which will fit in a scm_int.
*/
typedef scm_int scm_object;

/* Trival conversions to and from fixnum since they are
* currently the only type.
*/
#define scm_fixnum_make(val) ((scm_object)(val))
#define scm_fixnum_value(object) ((scm_int)(object))
/* assume a two's complement hardware representation */
#define scm_fixnum_min \
(((scm_int)1)<<(8*sizeof(scm_object)-1))
#define scm_fixnum_max (-(scm_fixnum_min+1))

/************************* read.c *************************/

scm_object scm_read(FILE *in);

/************************* eval.c *************************/

scm_object scm_eval(scm_object expression);

/************************ print.c *************************/

int scm_write(FILE *out, scm_object object);

#endif
22 changes: 22 additions & 0 deletions util.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include "scm.h"

/* scm_fatal is borrowed from K&R2e page 174 where it is
* called "error".
*
* Purposely ignoring return values of fprintf and
* vfprintf because we want to continue to the exit
* even if there is an error printing.
*/
void scm_fatal(char *fmt, ...) {
va_list args;

va_start(args, fmt);
fprintf(stderr, "error: ");
vfprintf(stderr, fmt, args);
fprintf(stderr, "\n");
va_end(args);
exit(1);
}

0 comments on commit 6c6a718

Please sign in to comment.