From 577e38f36bab334a50c4526a77a22e53acb4d0eb Mon Sep 17 00:00:00 2001 From: Fabio Scotto di Santolo Date: Wed, 19 Nov 2025 18:10:08 +0100 Subject: [PATCH] First implementation Toy Forth interpreter --- Makefile | 23 ++++++ program.tf | 1 + toyforth.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 247 insertions(+) create mode 100644 Makefile create mode 100644 program.tf create mode 100644 toyforth.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..30d1abd --- /dev/null +++ b/Makefile @@ -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 diff --git a/program.tf b/program.tf new file mode 100644 index 0000000..d1f0134 --- /dev/null +++ b/program.tf @@ -0,0 +1 @@ +5 10 20 diff --git a/toyforth.c b/toyforth.c new file mode 100644 index 0000000..43d1032 --- /dev/null +++ b/toyforth.c @@ -0,0 +1,223 @@ +#include +#include +#include +#include + +#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 \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; +}