diff --git a/SYNTAX b/SYNTAX index e0d3299..5887d71 100644 --- a/SYNTAX +++ b/SYNTAX @@ -78,9 +78,17 @@ Labels must be specified at the beginning of a line or on their own line. // 4. PREPROCESSOR /////////////////////////////// ////////////////////////////////////////////////// -TVM has a preprocessor that allows other source files to be included with the directive "%include filename.vm" +TVM's preprocessor works similarly to many C compilers and uses the prefix "%". -During preprocessing, the entire included source file is inserted in place of the %include directive in memory. + // I. Include // + + %include filename + Pastes all of the contents of filename into the source code before interpretting it. + + // II. Define // + + %define identifier value + Define a constant so that all instances of the string "identifier" will be replaced by "value". ////////////////////////////////////////////////// // 5. INSTRUCTION LISTING //////////////////////// diff --git a/include/tvm/tvm_htab.h b/include/tvm/tvm_htab.h index 0adc68e..7838565 100644 --- a/include/tvm/tvm_htab.h +++ b/include/tvm/tvm_htab.h @@ -8,6 +8,7 @@ typedef struct tvm_htab_node_s { char *key; int value; + void *valptr; struct tvm_htab_node_s *next; } tvm_htab_node_t; @@ -22,6 +23,8 @@ tvm_htab_t* htab_create(); void htab_destroy(tvm_htab_t *htab); int htab_add(tvm_htab_t *htab, const char *key, int value); +int htab_add_str(tvm_htab_t *htab, const char *key, const void *valptr, int len); int htab_find(tvm_htab_t *htab, const char *key); +char *htab_find_str(tvm_htab_t *htab, const char *key); #endif diff --git a/include/tvm/tvm_lexer.h b/include/tvm/tvm_lexer.h index 6b28a6b..6724b5a 100644 --- a/include/tvm/tvm_lexer.h +++ b/include/tvm/tvm_lexer.h @@ -4,6 +4,8 @@ #define MAX_ARGS 2 #define MAX_TOKENS 4 +#include "tvm_htab.h" + typedef struct tvm_lexer_s { char **source_lines; @@ -14,6 +16,6 @@ tvm_lexer_t *lexer_create(); void lexer_destroy(tvm_lexer_t *l); /* Tokenize the character array "source" into lines and tokens */ -void lex(tvm_lexer_t *lexer, char *source); +void lex(tvm_lexer_t *lexer, char *source, tvm_htab_t *defines); #endif diff --git a/include/tvm/tvm_preprocessor.h b/include/tvm/tvm_preprocessor.h index e999950..9832c62 100644 --- a/include/tvm/tvm_preprocessor.h +++ b/include/tvm/tvm_preprocessor.h @@ -1,6 +1,8 @@ #ifndef TVM_PREPROCESSOR_H_ #define TVM_PREPROCESSOR_H_ -int tvm_preprocess(char *src, int *src_len); +#include "tvm_htab.h" + +int tvm_preprocess(char *src, int *src_len, tvm_htab_t *defines); #endif diff --git a/include/tvm/tvm_program.h b/include/tvm/tvm_program.h index e5722f1..88a85c4 100644 --- a/include/tvm/tvm_program.h +++ b/include/tvm/tvm_program.h @@ -20,6 +20,8 @@ typedef struct tvm_program_s int **values; int num_values; + tvm_htab_t *defines; + tvm_htab_t *label_htab; } tvm_program_t; diff --git a/libtvm/tvm_htab.c b/libtvm/tvm_htab.c index 84f813b..635d3c3 100644 --- a/libtvm/tvm_htab.c +++ b/libtvm/tvm_htab.c @@ -25,6 +25,8 @@ void htab_destroy(tvm_htab_t *htab) while(node) { next = node->next; + if(node->valptr) + free(node->valptr); free(node->key); free(node); node = next; @@ -65,7 +67,13 @@ static void htab_rehash(tvm_htab_t *orig, unsigned int size) while(node) { next = node->next; - htab_add(new, node->key, node->value); + if (node->valptr) + { + htab_add_str(new, node->key, node->valptr, strlen(node->valptr) + 1); + free(node->valptr); + } + else + htab_add(new, node->key, node->value); free(node->key); free(node); node = next; @@ -113,7 +121,26 @@ int htab_add(tvm_htab_t *htab, const char *k, int v) if((float)++htab->num_nodes / htab->size > HTAB_LOAD_FACTOR) htab_rehash(htab, htab->num_nodes * 2); - return 0; + return hash; +} + +int htab_add_str(tvm_htab_t *htab, const char *key, const void *valptr, int len) +{ + int hash = htab_add(htab, key, 0); + int found = 0; + tvm_htab_node_t *node = htab->nodes[hash]; + + while (node && !found) + { + if (!strcmp(node->key, key)) + found = 1; + else + node = node->next; + } + + node->valptr = calloc(len, sizeof(char)); + memcpy(node->valptr, valptr, len); + return hash; } int htab_find(tvm_htab_t *htab, const char *key) @@ -131,3 +158,17 @@ int htab_find(tvm_htab_t *htab, const char *key) return -1; } +char *htab_find_str(tvm_htab_t *htab, const char *key) +{ + int hash = htab_hash(key, htab->size); + tvm_htab_node_t *node = htab->nodes[hash]; + + while(node) + { + if(!strcmp(node->key, key)) + return node->valptr; + node = node->next; + } + + return NULL; +} diff --git a/libtvm/tvm_lexer.c b/libtvm/tvm_lexer.c index 9b3aa5e..b03d49e 100644 --- a/libtvm/tvm_lexer.c +++ b/libtvm/tvm_lexer.c @@ -26,7 +26,7 @@ void lexer_destroy(tvm_lexer_t *lexer) free(lexer); } -void lex(tvm_lexer_t *lexer, char *source) +void lex(tvm_lexer_t *lexer, char *source, tvm_htab_t *defines) { int i, j; char *pToken, *pLine = strtok(source, "\n"); @@ -60,12 +60,16 @@ void lex(tvm_lexer_t *lexer, char *source) for(j = 0; (pToken && j < MAX_TOKENS); j++) { - lexer->tokens[i][j] = (char *)calloc(1, (strlen(pToken) + 1)); - strcpy(lexer->tokens[i][j], pToken); + char *token = htab_find_str(defines, pToken); + token = token ? token : pToken; + + lexer->tokens[i][j] = (char *)calloc(1, (strlen(token) + 1)); + strcpy(lexer->tokens[i][j], token); pToken = strtok(NULL, " \t,"); } } lexer->tokens[i] = NULL; + htab_destroy(defines); } diff --git a/libtvm/tvm_preprocessor.c b/libtvm/tvm_preprocessor.c index 6718fc4..09d58be 100644 --- a/libtvm/tvm_preprocessor.c +++ b/libtvm/tvm_preprocessor.c @@ -3,7 +3,7 @@ #include -int tvm_preprocess(char *src, int *src_len) +int tvm_preprocess(char *src, int *src_len, tvm_htab_t *defines) { char* pp_directive_delimiter = NULL; if((pp_directive_delimiter = strstr(src, "%include"))) @@ -22,7 +22,7 @@ int tvm_preprocess(char *src, int *src_len) if(!pFile) { printf("Unable to open file \"%s\"\n", filename); - return 0; + return -1; } free(temp_str); @@ -48,6 +48,63 @@ int tvm_preprocess(char *src, int *src_len) *src_len = strlen(src); return 1; } + else if((pp_directive_delimiter = strstr(src, "%define "))) + { + char *begin = pp_directive_delimiter; + char *end = strchr(begin, '\n'); + + if(!end) return 0; + + int offset = strlen("%define "); + + if(begin + offset >= end) + { + printf("Define missing arguments.\n"); + return -1; + } + + int length = (end - (begin + offset)); + char tempstr[length + 1]; + memset(tempstr, 0, length + 1); + memcpy(tempstr, begin + offset, length); + + char *keystr = tempstr; + char *valstr = strchr(tempstr, ' '); + + /* If there is a value, seperate the key and value + with a null character. */ + if(valstr) + { + *valstr = 0; + valstr += 1; + } + + if(!keystr || !valstr) + { + printf("Define missing arguments.\n"); + return -1; + } + + if(htab_find(defines, keystr) < 0) + htab_add_str(defines, keystr, valstr, strlen(valstr) + 1); + else + { + printf("Multiple definitions for %s.\n", keystr); + return -1; + } + + /* Remove the define line so it is not processed again. */ + size_t new_length = *src_len - (end - begin); + size_t first_block_len = begin - src; + size_t second_block_len = (src + *src_len) - end; + + memmove(&src[first_block_len], end, second_block_len); + + src = realloc(src, sizeof(char) * new_length); + *src_len = new_length; + + return 1; + } return 0; diff --git a/libtvm/tvm_program.c b/libtvm/tvm_program.c index b8ee315..ed563be 100644 --- a/libtvm/tvm_program.c +++ b/libtvm/tvm_program.c @@ -8,6 +8,7 @@ tvm_program_t *program_create() { tvm_program_t *p = (tvm_program_t *)calloc(1, sizeof(tvm_program_t)); p->label_htab = htab_create(); + p->defines = htab_create(); return p; } @@ -54,10 +55,15 @@ pi_interpret: tvm_fcopy(source, source_length, pFile); fclose(pFile); - while(tvm_preprocess(source, &source_length)); + int err = 0; + while((err = tvm_preprocess(source, &source_length, p->defines)) > 0); + + /* The preprocessor encountered a problem. */ + if (err < 0) + return 1; tvm_lexer_t *lexer_ctx = lexer_create(); - lex(lexer_ctx, source); + lex(lexer_ctx, source, p->defines); free(source); if(parse_labels(p, (const char ***)lexer_ctx->tokens) != 0) return 1; diff --git a/programs/tinyvm/preprocessor/define.vm b/programs/tinyvm/preprocessor/define.vm new file mode 100644 index 0000000..2ade114 --- /dev/null +++ b/programs/tinyvm/preprocessor/define.vm @@ -0,0 +1,20 @@ +%define ONE 1 +%define ZERO 0 + +start: + mov eax, ONE + mov ebx, ZERO + +loop: add eax, ebx + add ebx, eax + + prn eax + prn ebx + + cmp eax, ZERO + jl end + + cmp ebx, ZERO + jg loop + +end: