klisp

an open source interpreter for the Kernel Programming Language.
git clone http://git.hanabi.in/repos/klisp.git
Log | Files | Refs | README

commit e7f076f7403be64cb516e9b330d388735eebc69d
parent 8bdabb9fb89eb5407b644ff4e2069dea455e3496
Author: Andres Navarro <canavarro82@gmail.com>
Date:   Sat, 19 Feb 2011 21:30:19 -0300

Added a reader (including source info tracking & srfi-38 style shared defs/refs).

Diffstat:
Msrc/Makefile | 5+++--
Msrc/klisp.c | 32++++++++++++++++----------------
Msrc/kpair.c | 2+-
Msrc/kpair.h | 11+++++++----
Asrc/kread.c | 461+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/kread.h | 23+++++++++++++++++++++++
Msrc/kstring.c | 2+-
Msrc/kstring.h | 2+-
Msrc/ksymbol.h | 2+-
Msrc/ktoken.c | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Msrc/ktoken.h | 16++++++++++++++++
11 files changed, 617 insertions(+), 64 deletions(-)

diff --git a/src/Makefile b/src/Makefile @@ -1,5 +1,5 @@ CC=gcc -CFLAGS=-O2 -std=c99 -Wall -m32 $(MYCFLAGS) +CFLAGS=-O2 -g -std=c99 -Wall -m32 $(MYCFLAGS) RM=rm -f LIBS=-lm $(MYLIBS) @@ -7,7 +7,7 @@ MYCFLAGS= MYLDFLAGS= MYLIBS= -CORE_O= kobject.o ktoken.o kpair.o kstring.o ksymbol.o +CORE_O= kobject.o ktoken.o kpair.o kstring.o ksymbol.o kread.o KRN_T= klisp KRN_O= klisp.o @@ -38,3 +38,4 @@ kpair.o: kpair.c kpair.h kobject.h kstring.o: kstring.c kstring.h kobject.h # XXX: kpair.h because of use of list as symbol table ksymbol.o: ksymbol.c ksymbol.h kobject.h kpair.h +kread.o: kread.c kread.h kobject.h ktoken.h kpair.h diff --git a/src/klisp.c b/src/klisp.c @@ -6,28 +6,28 @@ #include <stdio.h> +#include <inttypes.h> +#include <math.h> + #include "kobject.h" -#include "ktoken.h" +#include "kread.h" int main(int argc, char *argv[]) { /* - ** Simple tokenizer loop + ** Simple read loop */ - printf("Tokenizer Type Test\n"); - - ktok_file = stdin; - ktok_init(); - - TValue tok = KNIL; - - while(!ttiseof(tok)) { - tok = ktok_read_token(); - if (ttisnil(tok)) { - /* there was an error */ - break; - } - printf("\nToken Type: %s\n", ttname(tok)); + printf("Read Type Test\n"); + + kread_file = stdin; + kread_filename = "*STDIN*"; + kread_init(); + + TValue obj = KNIL; + + while(!ttiseof(obj)) { + obj = kread(); + printf("\nRead Object Type: %s\n", ttname(obj)); } return 0; diff --git a/src/kpair.c b/src/kpair.c @@ -12,7 +12,7 @@ #include "kobject.h" /* TODO: Out of memory errors */ -/* XXX: for now all pairs are mutable */ +/* TEMP: for now all pairs are mutable */ TValue kcons(TValue car, TValue cdr) { Pair *new_pair = malloc(sizeof(Pair)); diff --git a/src/kpair.h b/src/kpair.h @@ -11,15 +11,18 @@ /* TODO: add type assertions */ /* TODO: add more kc[ad]*r combinations */ -#define kcar(p_) (((Pair *)(p_.tv.v.gc))->car) -#define kcdr(p_) (((Pair *)(p_.tv.v.gc))->cdr) +#define kcar(p_) (tv2pair(p_)->car) +#define kcdr(p_) (tv2pair(p_)->cdr) #define kset_car(p_, v_) (kcar(p_) = v_) #define kset_cdr(p_, v_) (kcdr(p_) = v_) -#define kdummy_cons (kcons(KNIL, KNIL)) +#define kdummy_cons() (kcons(KNIL, KNIL)) -/* XXX: for now all pairs are mutable */ +/* TEMP: for now all pairs are mutable */ TValue kcons(TValue, TValue); +#define kget_source_info(p_) (tv2pair(p_)->si) +#define kset_source_info(p_, si_) (kget_source_info(p_) = si_) + #endif diff --git a/src/kread.c b/src/kread.c @@ -0,0 +1,461 @@ +/* +** kread.c +** Reader for the Kernel Programming Language +** See Copyright Notice in klisp.h +*/ + + +/* +** TODO: +** +** - Read mutable/immutable objects (cons function should be a parameter) +** this is needed because some functions (like load) return immutable objs +** - Decent error handling mechanism +** +*/ + +#include <stdio.h> +/* XXX for malloc */ +#include <stdlib.h> +/* TODO: use a generalized alloc function */ +/* TEMP: for out of mem errors */ +#include <assert.h> + +#include "kread.h" +#include "kobject.h" +#include "kpair.h" +#include "ktoken.h" + +/* TODO: move to the global state */ +/* TODO: replace the list with a hashtable */ +TValue shared_dict = KNIL_; +FILE *kread_file = NULL; +char *kread_filename = NULL; + + +/* +** Stacks for the read FSM +** +** The state stack is never empty while read is in process and +** selects the action to be performed on the next read token. +** +** The data saved in the data stack changes according to state: +** ST_FIRST_LIST: pair representing the first pair of the list +** with source info of the '(' token. +** ST_MIDDLE_LIST, ST_LAST_ILIST: two elements, first below, second on top: +** - a pair with car: first pair of the list (with source info +** corrected to car of list) and cdr: source info of the '(' token that +** started the [i]list. +** - another pair, that is the last pair of the list so far. +** ST_PAST_LAST_ILIST: a pair with car: first pair and cdr: source +** info as above (but no pair with last pair). +** ST_SHARED_DEF: a pair with car: shared def token and cdr: source +** info of the shared def token. +** +*/ + +/* TODO: move to the global state */ +typedef enum { + ST_READ, ST_SHARED_DEF, ST_LAST_ILIST, ST_PAST_LAST_ILIST, + ST_FIRST_LIST, ST_MIDDLE_LIST +} state_t; + +state_t *sstack; +int sstack_size; +int sstack_i; + +TValue *dstack; +int dstack_size; +int dstack_i; + +/* TEMP: for now stacks are fixed size, use asserts to check */ +#define STACK_INIT_SIZE 1024 + +#define push_state(st_) ({ assert(sstack_i < sstack_size); \ + sstack[sstack_i++] = st_; }) +#define pop_state() (--sstack_i) +#define get_state() (sstack[sstack_i-1]) +#define clear_state() (sstack_i = 0) + +#define push_data(data_) ({ assert(dstack_i < dstack_size); \ + dstack[dstack_i++] = data_; }) +#define pop_data() (--dstack_i) +#define get_data() (dstack[dstack_i-1]) +#define clear_data() (dstack_i = 0) + +/* +** Error management +*/ +TValue kread_error(char *str) +{ + /* TODO: Decide on error handling mechanism for reader (& tokenizer) */ + printf("READ ERROR: %s\n", str); + return KEOF; +} + +/* +** Reader initialization +*/ +void kread_init() +{ + assert(kread_file != NULL); + assert(kread_filename != NULL); + + ktok_file = kread_file; + ktok_source_info.filename = kread_filename; + /* XXX: For now just hardcode it to 8 spaces tab-stop */ + ktok_source_info.tab_width = 8; + ktok_init(); + ktok_reset_source_info(); + + /* XXX: for now use a fixed size for stacks */ + sstack_size = STACK_INIT_SIZE; + clear_state(); + sstack = malloc(STACK_INIT_SIZE*sizeof(state_t)); + assert(sstack != NULL); + + dstack_size = STACK_INIT_SIZE; + clear_data(); + dstack = malloc(STACK_INIT_SIZE*sizeof(TValue)); + assert(dstack != NULL); +} + +/* +** Shared Reference Management (srfi-38) +*/ + +/* This is called after kread to clear the shared alist */ +void clear_shared_dict() +{ + shared_dict = KNIL; +} + +TValue try_shared_ref(TValue ref_token) +{ + /* TEMP: for now, only allow fixints in shared tokens */ + int32_t ref_num = ivalue(kcdr(ref_token)); + TValue tail = shared_dict; + while (!ttisnil(tail)) { + TValue head = kcar(tail); + if (ref_num == ivalue(kcar(head))) + return kcdr(head); + tail = kcdr(tail); + } + return kread_error("undefined shared ref found"); +} + +TValue try_shared_def(TValue def_token, TValue value) +{ + /* TEMP: for now, only allow fixints in shared tokens */ + int32_t ref_num = ivalue(kcdr(def_token)); + TValue tail = shared_dict; + while (!ttisnil(tail)) { + TValue head = kcar(tail); + if (ref_num == ivalue(kcar(head))) + return kread_error("duplicate shared def found"); + tail = kcdr(tail); + } + + /* XXX: what happens on out of mem? & gc?(inner cons is not rooted) */ + shared_dict = kcons(kcons(kcdr(def_token), value), + shared_dict); + return KINERT; +} + +/* This overwrites a previouly made def, it is used in '() */ +/* NOTE: the shared def is guaranteed to exist */ +void change_shared_def(TValue def_token, TValue value) +{ + /* TEMP: for now, only allow fixints in shared tokens */ + int32_t ref_num = ivalue(kcdr(def_token)); + TValue tail = shared_dict; + while (!ttisnil(tail)) { + TValue head = kcar(tail); + if (ref_num == ivalue(kcar(head))) { + kset_cdr(head, value); + return; + } + tail = kcdr(tail); + } + /* NOTE: can't really happen */ + return; +} + +/* +** Reader FSM +*/ +/* TEMP: For now we'll use just one big function */ +TValue kread_fsm() +{ + push_state(ST_READ); + + /* read next token or process obj */ + bool read_next_token = true; + /* the obj just read/completed */ + TValue obj; + /* the source code information of that obj */ + TValue obj_si; + + while (!(get_state() == ST_READ && !read_next_token)) { + if (read_next_token) { + TValue tok = ktok_read_token(); + if (ttispair(tok)) { /* special token */ + switch (chvalue(kcar(tok))) { + case '(': { + if (get_state() == ST_PAST_LAST_ILIST) + return kread_error("open paren found after " + "last element of improper list"); + TValue np = kdummy_cons(); + /* + ** NOTE: the source info of the '(' is temporarily saved + ** in np (later it will be replace by the source info + ** of the car of the list + */ + kset_source_info(np, ktok_get_source_info()); + + /* update the shared def to point to the new list */ + /* NOTE: this is necessary for self referrencing lists */ + /* NOTE: the shared def was already checked for errors */ + if (get_state() == ST_SHARED_DEF) + change_shared_def(kcar(get_data()), np); + + /* start reading elements of the new list */ + push_state(ST_FIRST_LIST); + push_data(np); + read_next_token = true; + break; + } + case ')': { + switch(get_state()) { + case ST_FIRST_LIST: { /* empty list */ + /* + ** Discard the pair in sdata but + ** retain the source info + ** Return () for processing + */ + TValue fp_with_old_si = get_data(); + pop_data(); + pop_state(); + obj = KNIL; + obj_si = kget_source_info(fp_with_old_si); + read_next_token = false; + break; + } + case ST_MIDDLE_LIST: /* end of list */ + case ST_PAST_LAST_ILIST: { /* end of ilist */ + /* discard info on last pair */ + pop_data(); + pop_state(); + TValue fp_old_si = get_data(); + pop_data(); + pop_state(); + /* list read ok, process it in next iteration */ + obj = kcar(fp_old_si); + obj_si = kcdr(fp_old_si); + read_next_token = false; + break; + } + case ST_LAST_ILIST: + return kread_error("missing last element in " + "improper list"); + case ST_SHARED_DEF: + return kread_error("unmatched closing paren found " + "in shared def"); + case ST_READ: + return kread_error("unmatched closing paren found"); + default: + /* shouldn't happen */ + assert(0); + } + break; + } + case '.': { + switch(get_state()) { + case (ST_MIDDLE_LIST): + /* tok ok, read next obj for cdr of ilist */ + pop_state(); + push_state(ST_LAST_ILIST); + read_next_token = true; + break; + case ST_FIRST_LIST: + return kread_error("missing first element of " + "improper list"); + case ST_LAST_ILIST: + case ST_PAST_LAST_ILIST: + return kread_error("double dot in improper list"); + case ST_SHARED_DEF: + return kread_error("dot found in shared def"); + case ST_READ: + return kread_error("dot found outside list"); + default: + /* shouldn't happen */ + assert(0); + } + break; + } + case '=': { /* srfi-38 shared def */ + switch (get_state()) { + case ST_SHARED_DEF: + return kread_error("shared def found in " + "shared def"); + case ST_PAST_LAST_ILIST: + return kread_error("shared def found after " + "last element of improper list"); + default: { + TValue res = try_shared_def(tok, KNIL); + /* TEMP: while error returns EOF */ + if (ttiseof(res)) { + return res; + } else { + /* token ok, read defined object */ + push_state(ST_SHARED_DEF); + /* NOTE: save the source info to return it + after the defined object is read */ + push_data(kcons(tok, ktok_get_source_info())); + read_next_token = true; + } + } + } + break; + } + case '#': { /* srfi-38 shared ref */ + switch(get_state()) { + case ST_SHARED_DEF: + return kread_error("shared ref found in " + "shared def"); + case ST_PAST_LAST_ILIST: + return kread_error("shared ref found after " + "last element of improper list"); + default: { + TValue res = try_shared_ref(tok); + /* TEMP: while error returns EOF */ + if (ttiseof(res)) { + return res; + } else { + /* token ok, process it in next iteration */ + obj = tok; + /* NOTE: use source info of ref token */ + obj_si = ktok_get_source_info(); + read_next_token = false; + } + } + } + break; + } + default: + /* shouldn't happen */ + assert(0); + } + } else if (ttiseof(tok)) { + switch (get_state()) { + case ST_READ: + /* will exit in next loop */ + obj = tok; + obj_si = ktok_get_source_info(); + read_next_token = false; + break; + case ST_FIRST_LIST: + case ST_MIDDLE_LIST: + return kread_error("EOF found while reading list"); + case ST_LAST_ILIST: + case ST_PAST_LAST_ILIST: + return kread_error("EOF found while reading " + "improper list"); + case ST_SHARED_DEF: + return kread_error("EOF found in shared def"); + default: + /* shouldn't happen */ + assert(0); + } + } else { /* this can only be a complete token */ + if (get_state() == ST_PAST_LAST_ILIST) { + return kread_error("Non paren found after last " + "element of improper list"); + } else { + /* token ok, process it in next iteration */ + obj = tok; + obj_si = ktok_get_source_info(); + read_next_token = false; + } + } + } else { /* if(read_next_token) */ + /* process the object just read */ + switch(get_state()) { + case ST_FIRST_LIST: { + TValue fp = get_data(); + /* replace source info in fp with the saved one */ + /* NOTE: the old one will be returned when list is complete */ + TValue fp_old_si = kget_source_info(fp); + kset_source_info(fp, obj_si); + kset_car(fp, obj); + + /* continue reading objects of list */ + push_state(ST_MIDDLE_LIST); + pop_data(); + /* save first & last pair of the (still incomplete) list */ + push_data(kcons (fp, fp_old_si)); + push_data(fp); + read_next_token = true; + break; + } + case ST_MIDDLE_LIST: { + TValue np = kcons(obj, KNIL); + kset_source_info(np, obj_si); + kset_cdr(get_data(), np); + /* replace last pair of the (still incomplete) read next obj */ + pop_data(); + push_data(np); + read_next_token = true; + break; + } + case ST_LAST_ILIST: + pop_state(); + push_state(ST_PAST_LAST_ILIST); + read_next_token = true; + break; + case ST_SHARED_DEF: { + /* shared def completed, continue processing obj */ + TValue def_si = get_data(); + pop_state(); + pop_data(); + + change_shared_def(kcar(def_si), obj); + + /* obj = obj; */ + /* the source info returned is the one from the shared def */ + obj_si = kcdr(def_si); + read_next_token = false; + break; + } + case ST_READ: + /* this shouldn't happen, should've exited the while */ + assert(0); + default: + /* shouldn't happen */ + assert(0); + } + } + } + + return obj; +} + +/* +** Reader Main Function +*/ +TValue kread() +{ + TValue obj; + + /* TEMP: for now assume we are in the repl: reset source info */ + ktok_reset_source_info(); + + obj = kread_fsm(); + + /* NOTE: clear after function to allow earlier gc */ + clear_shared_dict(); + clear_state(); + clear_data(); + + return obj; +} diff --git a/src/kread.h b/src/kread.h @@ -0,0 +1,23 @@ +/* +** kread.h +** Reader for the Kernel Programming Language +** See Copyright Notice in klisp.h +*/ + +#ifndef kread_h +#define kread_h + +#include "kobject.h" + +/* +** Reader interface +*/ +void kread_init(); +TValue kread(); + +/* TODO: move this to the global state */ +FILE *kread_file; +char *kread_filename; + +#endif + diff --git a/src/kstring.c b/src/kstring.c @@ -14,7 +14,7 @@ #include "kobject.h" /* TODO: Out of memory errors */ -/* XXX: for now all strings are mutable */ +/* TEMP: for now all strings are mutable */ TValue kstring_new(const char *buf, uint32_t size) { String *new_str = malloc(sizeof(String) + size + 1); diff --git a/src/kstring.h b/src/kstring.h @@ -9,7 +9,7 @@ #include "kobject.h" -/* XXX: for now all strings are mutable */ +/* TEMP: for now all strings are mutable */ TValue kstring_new(const char *, uint32_t); #endif diff --git a/src/ksymbol.h b/src/ksymbol.h @@ -13,7 +13,7 @@ /* TODO: move to global state */ TValue ksymbol_table; -/* XXX: for now all symbols are interned */ +/* TEMP: for now all symbols are interned */ TValue ksymbol_new(const char *); #endif diff --git a/src/ktoken.c b/src/ktoken.c @@ -12,7 +12,6 @@ ** - Support for complete number syntax (exactness, radix, etc) ** - Support for unicode (strings, char and symbols). ** - Error handling -** - Source code tracking ** */ #include <stdio.h> @@ -20,6 +19,9 @@ #include <stdlib.h> /* TODO: use a generalized alloc function */ +/* TEMP: for out of mem errors */ +#include <assert.h> + #include <string.h> #include <ctype.h> #include <stdint.h> @@ -108,28 +110,34 @@ kcharset ktok_delimiter, ktok_extended, ktok_subsequent; ** instead of creating an otherwise useless special token type ** lparen, rparen and dot are represented as a pair with the corresponding ** char in the car and nil in the cdr. -** srfi-38 tokens are represented with a boolean in the indicating if it's a -** defining token and the number in the cdr +** srfi-38 tokens are also represented with a char in the car indicating if +** it's a defining token ('=') or a referring token ('#') and the number in +** the cdr. This way a special token can be easily tested for (with ttispair) +** and easily classified (with switch(chvalue(kcar(tok)))). ** */ TValue ktok_lparen, ktok_rparen, ktok_dot; /* TODO: move this to the global state */ char *ktok_buffer; -/* WORKAROUND: for stdin line buffering & reading of EOF */ -bool ktok_seen_eof; uint32_t ktok_buffer_size; #define KTOK_BUFFER_INITIAL_SIZE 1024 +/* WORKAROUND: for stdin line buffering & reading of EOF */ +bool ktok_seen_eof; void ktok_init() { + assert(ktok_file != NULL); + assert(ktok_source_info.filename != NULL); + /* WORKAROUND: for stdin line buffering & reading of EOF */ ktok_seen_eof = false; /* string buffer */ - /* XXX: for now use a fixed size */ + /* TEMP: for now use a fixed size */ ktok_buffer_size = KTOK_BUFFER_INITIAL_SIZE; - /* TODO: Out of memory errors */ ktok_buffer = malloc(KTOK_BUFFER_INITIAL_SIZE); + /* TEMP: while there is no error handling code */ + assert(ktok_buffer != NULL); /* Character sets */ kcharset_fill(ktok_alphabetic, "ABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -154,28 +162,74 @@ void ktok_init() } /* -** Underlying stream interface +** Underlying stream interface & source code location tracking */ -int ktok_getc(); -int ktok_peekc(); - int ktok_getc() { - /* TODO: add location tracking */ /* WORKAROUND: for stdin line buffering & reading of EOF */ if (ktok_seen_eof) { return EOF; } else { int chi = getc(ktok_file); - ktok_seen_eof = (chi == EOF); - return chi; + if (chi == EOF) { + /* NOTE: eof doesn't change source code location info */ + ktok_seen_eof = true; + return EOF; + } + + /* track source code location before returning the char */ + if (chi == '\t') { + /* align column to next tab stop */ + ktok_source_info.col = + (ktok_source_info.col + ktok_source_info.tab_width) - + (ktok_source_info.col % ktok_source_info.tab_width); + return '\t'; + } else if (chi == '\n') { + ktok_source_info.line++; + ktok_source_info.col = 0; + return '\n'; + } else { + return chi; + } } } int ktok_peekc() { - int chi = ktok_getc(); - if (chi != EOF) - ungetc(chi, ktok_file); - return chi; + /* WORKAROUND: for stdin line buffering & reading of EOF */ + if (ktok_seen_eof) { + return EOF; + } else { + int chi = getc(ktok_file); + if (chi == EOF) + ktok_seen_eof = true; + else + ungetc(chi, ktok_file); + return chi; + } +} + +void ktok_reset_source_info() +{ + /* line is 1-base and col is 0-based */ + ktok_source_info.line = 1; + ktok_source_info.col = 0; +} + +void ktok_save_source_info() +{ + ktok_source_info.saved_filename = ktok_source_info.filename; + ktok_source_info.saved_line = ktok_source_info.line; + ktok_source_info.saved_col = ktok_source_info.col; +} + +TValue ktok_get_source_info() +{ + /* XXX: what happens on gc? (unrooted objects) */ + /* NOTE: the filename doesn't contains embedded '\0's */ + TValue filename_str = kstring_new(ktok_source_info.saved_filename, + strlen(ktok_source_info.saved_filename)); + /* TEMP: for now, lines and column names are fixints */ + return kcons(filename_str, kcons(i2tv(ktok_source_info.saved_line), + i2tv(ktok_source_info.saved_col))); } /* @@ -184,8 +238,9 @@ int ktok_peekc() { TValue ktok_error(char *str) { /* TODO: Decide on error handling mechanism for reader (& tokenizer) */ + /* TEMP: Use eof object */ printf("TOK ERROR: %s\n", str); - return KNIL; + return KEOF; } @@ -213,8 +268,8 @@ TValue ktok_read_token () ** in any case we save the location of the port */ - /* TODO: add location tracking */ - /* SCHEME VERSION (kport-save-loc! kport) */ + /* save the source info of the start of the next token */ + ktok_save_source_info(); int chi = ktok_peekc(); @@ -316,14 +371,12 @@ int ktok_read_until_delimiter() int i = 0; while (!ktok_check_delimiter()) { + /* TODO: allow buffer to grow */ + assert(i + 1 < ktok_buffer_size); + /* NOTE: can't be eof, because eof is a delimiter */ char ch = (char) ktok_getc(); ktok_buffer[i++] = ch; - - if (i + 1 == ktok_buffer_size) { - /* TODO: allow buffer to grow */ - break; - } } ktok_buffer[i] = '\0'; return i; @@ -331,7 +384,7 @@ int ktok_read_until_delimiter() /* ** Numbers -** XXX: for now, only fixints in base 10 +** TEMP: for now, only fixints in base 10 */ TValue ktok_read_number(bool is_pos) { @@ -397,12 +450,10 @@ TValue ktok_read_string() "while reading a string"); } } + /* TODO: allow buffer to grow */ + assert(i+1 < ktok_buffer_size); + ktok_buffer[i++] = ch; - - if (i + 1 == ktok_buffer_size) - /* TODO: allow buffer to grow */ - return ktok_error("Implementation restriction: " - "String too long"); } } return kstring_new(ktok_buffer, i); @@ -505,7 +556,7 @@ TValue ktok_read_special() if (chi == EOF) return ktok_error("EOF found while reading a srfi-38 token"); } - return kcons(b2tv(ch == '='), i2tv(res)); + return kcons(ch2tv(ch), i2tv(res)); } /* TODO: add real with no primary value and undefined */ default: @@ -521,6 +572,9 @@ TValue ktok_read_identifier() int i = 0; while (!ktok_check_delimiter()) { + /* TODO: allow buffer to grow */ + assert(i+1 < ktok_buffer_size); + /* NOTE: can't be eof, because eof is a delimiter */ char ch = (char) ktok_getc(); @@ -529,11 +583,6 @@ TValue ktok_read_identifier() ktok_buffer[i++] = ch; else return ktok_error("Invalid char in identifier"); - - /* TODO: allow buffer to grow */ - if (i + 1 == ktok_buffer_size) { - break; - } } ktok_buffer[i] = '\0'; return ksymbol_new(ktok_buffer); diff --git a/src/ktoken.h b/src/ktoken.h @@ -14,8 +14,24 @@ */ void ktok_init(); TValue ktok_read_token(); +void ktok_reset_source_info(); +TValue ktok_get_source_info(); /* TODO: move this to the global state */ FILE *ktok_file; +/* XXX: for now, lines and column names are fixints */ +typedef struct { + char *filename; + int32_t tab_width; + int32_t line; + int32_t col; + + char *saved_filename; + int32_t saved_line; + int32_t saved_col; +} ksource_info_t; + +ksource_info_t ktok_source_info; + #endif