First implementation Toy Forth interpreter

This commit is contained in:
Fabio Scotto di Santolo
2025-11-19 18:10:08 +01:00
commit 577e38f36b
3 changed files with 247 additions and 0 deletions

23
Makefile Normal file
View 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
View File

@@ -0,0 +1 @@
5 10 20

223
toyforth.c Normal file
View 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;
}