First implementation Toy Forth interpreter
This commit is contained in:
23
Makefile
Normal file
23
Makefile
Normal file
@@ -0,0 +1,23 @@
|
||||
##
|
||||
# Toy Forth
|
||||
#
|
||||
# @file
|
||||
# @version 0.1
|
||||
|
||||
CC=gcc
|
||||
CFLAGS=-W -Wall -O2
|
||||
SRC=toyforth.c
|
||||
OUT=toyforth
|
||||
|
||||
all: $(OUT)
|
||||
|
||||
$(OUT): $(SRC)
|
||||
$(CC) $(CFLAGS) $(SRC) -o $@
|
||||
|
||||
run: $(OUT)
|
||||
./$(OUT) program.tf
|
||||
|
||||
clean:
|
||||
rm -f $(OUT)
|
||||
|
||||
# end
|
||||
1
program.tf
Normal file
1
program.tf
Normal file
@@ -0,0 +1 @@
|
||||
5 10 20
|
||||
223
toyforth.c
Normal file
223
toyforth.c
Normal file
@@ -0,0 +1,223 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#define TFOBJ_TYPE_INT 0
|
||||
#define TFOBJ_TYPE_STR 1
|
||||
#define TFOBJ_TYPE_BOOL 2
|
||||
#define TFOBJ_TYPE_LIST 3
|
||||
#define TFOBJ_TYPE_SYMBOL 4
|
||||
|
||||
/* ############################# Data Structures ########################################## */
|
||||
typedef struct tfobj {
|
||||
int refcount;
|
||||
int type; // TFOBJ_TYPE_*
|
||||
union {
|
||||
int i;
|
||||
struct {
|
||||
char *ptr;
|
||||
size_t len;
|
||||
} str;
|
||||
struct {
|
||||
struct tfobj **ele;
|
||||
size_t len;
|
||||
} list;
|
||||
};
|
||||
} tfobj;
|
||||
|
||||
typedef struct tfparser {
|
||||
char *prg; // The program to compile into a list.
|
||||
char *p; // Next token to parse.
|
||||
} tfparser;
|
||||
|
||||
typedef struct tfctx {
|
||||
tfobj *stack;
|
||||
} tfctx;
|
||||
|
||||
/* ############################# Allocations wrappers ################################### */
|
||||
|
||||
void *xmalloc(size_t size)
|
||||
{
|
||||
void *ptr = malloc(size);
|
||||
if (ptr == NULL) {
|
||||
fprintf(stderr, "Out of memory allocating %zu bytes", size);
|
||||
exit(1);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/* ######################### Object related functions ###################################
|
||||
* The following functions allocate Toy Forth objects of different types.
|
||||
*/
|
||||
|
||||
/*Allocate and initialize a new Toy Forth object. */
|
||||
tfobj *createObject(int type)
|
||||
{
|
||||
tfobj *o = xmalloc(sizeof(tfobj));
|
||||
o->type = type;
|
||||
o->refcount = 1;
|
||||
return o;
|
||||
}
|
||||
|
||||
tfobj *createStringObject(char *s, size_t len)
|
||||
{
|
||||
tfobj *obj = createObject(TFOBJ_TYPE_STR);
|
||||
obj->str.ptr = s;
|
||||
obj->str.len = len;
|
||||
return obj;
|
||||
}
|
||||
|
||||
tfobj *createIntObject(int i)
|
||||
{
|
||||
tfobj *obj = createObject(TFOBJ_TYPE_INT);
|
||||
obj->i = i;
|
||||
return obj;
|
||||
}
|
||||
|
||||
tfobj *createBoolObject(int b)
|
||||
{
|
||||
tfobj *obj = createObject(TFOBJ_TYPE_BOOL);
|
||||
obj->i = b;
|
||||
return obj;
|
||||
}
|
||||
|
||||
tfobj *createSymbolObject(char *s, size_t len)
|
||||
{
|
||||
tfobj *obj = createStringObject(s, len);
|
||||
obj->type = TFOBJ_TYPE_SYMBOL;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/* ############################ List Object ############################################ */
|
||||
|
||||
tfobj *createListObject(void)
|
||||
{
|
||||
tfobj *obj = createObject(TFOBJ_TYPE_LIST);
|
||||
obj->list.ele = NULL;
|
||||
obj->list.len = 0;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/* Add the new element at the end of the 'list'.
|
||||
* It is up to the caller to increment the reference count of the
|
||||
* element added to the list, if needed. */
|
||||
void listPush(tfobj *l, tfobj *ele)
|
||||
{
|
||||
l->list.ele = realloc(l->list.ele, sizeof(tfobj*) * (l->list.len+1));
|
||||
l->list.ele[l->list.len] = ele;
|
||||
l->list.len++;
|
||||
}
|
||||
|
||||
/* ####################### Turn program into toy forth list ############################ */
|
||||
|
||||
void parseSpaces(tfparser *parser)
|
||||
{
|
||||
while (isspace(parser->p[0])) parser->p++;
|
||||
}
|
||||
|
||||
#define MAX_NUM_LEN 128
|
||||
|
||||
tfobj *parseNumber(tfparser *parser)
|
||||
{
|
||||
char buf[MAX_NUM_LEN];
|
||||
char *start = parser->p;
|
||||
char *end;
|
||||
|
||||
if (parser->p[0] == '-') parser->p++;
|
||||
while (parser->p[0] && isdigit(parser->p[0])) parser->p++;
|
||||
end = parser->p;
|
||||
int numLen = end - start;
|
||||
if (numLen >= MAX_NUM_LEN) return NULL;
|
||||
|
||||
memcpy(buf, start, numLen);
|
||||
buf[numLen] = 0;
|
||||
|
||||
tfobj *obj = createIntObject(atoi(buf));
|
||||
return obj;
|
||||
}
|
||||
|
||||
tfobj *compile(char *prg)
|
||||
{
|
||||
tfparser parser;
|
||||
parser.prg = prg;
|
||||
parser.p = prg;
|
||||
|
||||
tfobj *parsed = createListObject();
|
||||
|
||||
while (parser.p) {
|
||||
tfobj *obj;
|
||||
char *token_start = parser.p;
|
||||
|
||||
parseSpaces(&parser);
|
||||
if (parser.p[0] == 0) break; // End of program reached.
|
||||
|
||||
if (isdigit(parser.p[0]) || parser.p[0] == '-') {
|
||||
obj = parseNumber(&parser);
|
||||
} else {
|
||||
obj = NULL;
|
||||
}
|
||||
|
||||
// Check if the current token produced a parsing error.
|
||||
if (obj == NULL) {
|
||||
// FIXME: release parsed here.
|
||||
printf("Syntax error near: %32s ...\n", token_start);
|
||||
return NULL;
|
||||
} else {
|
||||
listPush(parsed, obj);
|
||||
}
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
/* ############################ Execute the program ##################################### */
|
||||
|
||||
void exec(tfobj *obj) {
|
||||
// TODO: unimplmented function
|
||||
for (size_t j = 0; j < obj->list.len; j++) {
|
||||
tfobj *o = obj->list.ele[j];
|
||||
switch (o->type) {
|
||||
case TFOBJ_TYPE_INT:
|
||||
printf("%d", o->i);
|
||||
break;
|
||||
default:
|
||||
printf("?");
|
||||
break;
|
||||
}
|
||||
printf(" ");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/* ############################### Main ################################################# */
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Read the program in memory, for later parsing.
|
||||
FILE *fp = fopen(argv[1], "r");
|
||||
if (fp == NULL) {
|
||||
perror("Opening Toy Forth program");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fseek(fp, 0, SEEK_END);
|
||||
long file_size = ftell(fp);
|
||||
char *prgtext = xmalloc(file_size+1);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
fread(prgtext, file_size, 1, fp);
|
||||
prgtext[file_size] = 0;
|
||||
fclose(fp);
|
||||
|
||||
//printf("Program text: \"%s\"\n", prgtext);
|
||||
|
||||
tfobj *prg = compile(prgtext);
|
||||
exec(prg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user