From 98e35971a63cdd9bab5dd63a941c57d0bd8de91e Mon Sep 17 00:00:00 2001 From: Alexander Pickering Date: Tue, 19 Feb 2019 16:02:41 -0500 Subject: Added break statement Added a break statement that can break out of loops. --- src/ctemplates.c | 241 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- src/ctemplates.h | 13 ++- t/test_2.c | 47 +++++++++++ 3 files changed, 287 insertions(+), 14 deletions(-) create mode 100644 t/test_2.c diff --git a/src/ctemplates.c b/src/ctemplates.c index 8138f7c..2129ec2 100644 --- a/src/ctemplates.c +++ b/src/ctemplates.c @@ -87,6 +87,18 @@ struct TMPL_tagnode* parse_variable( struct TMPL_token* head, struct TMPL_buf* errbuf ); +struct TMPL_tagnode* parse_continue( + struct TMPL_token* head, + struct TMPL_buf* errbuf + ); +struct TMPL_tagnode* parse_break( + struct TMPL_token* head, + struct TMPL_buf* errbuf + ); +struct TMPL_tagnode* parse_end( + struct TMPL_token* head, + struct TMPL_buf* errbuf + ); struct TMPL_varlist* TMPL_alloc_varlist(void); int TMPL_free_hashmapitems(any_t a, any_t b); void TMPL_free_varlist(struct TMPL_varlist* vl); @@ -133,6 +145,16 @@ int render_loop( struct TMPL_tagnode* node, struct TMPL_varlist* varlist ); +int render_continue( + struct TMPL_templates* t, + struct TMPL_tagnode* node, + struct TMPL_varlist* varlist + ); +int render_break( + struct TMPL_templates* t, + struct TMPL_tagnode* node, + struct TMPL_varlist* varlist + ); int render_any( struct TMPL_templates* t, struct TMPL_tagnode* node, @@ -233,8 +255,7 @@ starts_with_token(const char* str, size_t strlen){ && *(str + 6) == 'R' && *(str + 7) == 'E' && *(str + 8) == 'A' - && *(str + 9) == 'K' - && *(str + 10) == ' '){ + && *(str + 9) == 'K'){ return tag_break; }else if(strlen > TAG_CONTINUE_LENGTH && *(str + 5) == 'C' @@ -244,8 +265,7 @@ starts_with_token(const char* str, size_t strlen){ && *(str + 9) == 'I' && *(str + 10) == 'N' && *(str + 11) == 'U' - && *(str + 12) == 'E' - && *(str + 13) == ' '){ + && *(str + 12) == 'E'){ return tag_continue; }else{ return tag_null; @@ -363,10 +383,13 @@ TMPL_add_var_to_varlist(struct TMPL_varlist* t, const char* varname, const char* void print_ast_helper(struct TMPL_tagnode* cursor, int level){ + /*printf("Helper called, level %d\n",level);*/ int i; + /*printf("Cursor is: %p\n", (void*)cursor);*/ for(i = 0; i < level; i++){ printf("|"); } + /*printf("type is %d\n",cursor->type);*/ printf("} %d ->",(int)cursor->type); switch(cursor->type){ case tag_null: @@ -411,10 +434,10 @@ print_ast_helper(struct TMPL_tagnode* cursor, int level){ print_ast_helper(cursor->TMPL_tag.loop.body,level+1); break; case tag_break: - printf("TAG Break\n"); + printf("TAG Break (%d)\n",cursor->TMPL_tag.breakcont.level); break; case tag_continue: - printf("TAG Continue\n"); + printf("TAG Continue (%d)\n",cursor->TMPL_tag.breakcont.level); break; } if(cursor->next != NULL){ @@ -423,6 +446,7 @@ print_ast_helper(struct TMPL_tagnode* cursor, int level){ } void print_ast(struct TMPL_templates* t){ + printf("Printing template %p\nRoottag: %p\n",(void*)t,(void*)t->roottag); print_ast_helper(t->roottag,0); } @@ -497,9 +521,37 @@ is_whitespace(char c){ return (c == ' ' || c == '\t' || c == '\n') ? 1 : 0; } +const char* tokentype_to_str(struct TMPL_token *head){ + switch(head->type){ + case tag_null: + return "NULL"; + case tag_text: + return "TEXT"; + case tag_var: + return "VAR"; + case tag_if: + return "IF"; + case tag_elseif: + return "ELSEIF"; + case tag_else: + return "ELSE"; + case tag_end: + return "END"; + case tag_loop: + return "LOOP"; + case tag_break: + return "BREAK"; + case tag_continue: + return "CONTINUE"; + default: + return "UNKNOWN"; + } +} + struct TMPL_tagnode* parse_text(struct TMPL_token* head, struct TMPL_buf* errbuf){ struct TMPL_tagnode* t = alloc_tagnode(); + head->into = t; t->TMPL_tag.text.start = head->start; t->TMPL_tag.text.len = head->length; t->type = head->type; @@ -517,6 +569,7 @@ parse_else(struct TMPL_token* head, struct TMPL_buf* errbuf){ struct TMPL_tagnode* parse_elseif(struct TMPL_token* head, struct TMPL_buf* errbuf){ struct TMPL_tagnode* t = alloc_tagnode(); + head->into = t; const char* start_of_attribs = head->start + TAG_ELSEIF_LENGTH; int name_offset = kmp(start_of_attribs,head->length, ATTRIBUTE_VARNAME,ATTRIBUTE_VARNAME_LENGTH); const char* start_of_name = start_of_attribs + name_offset + ATTRIBUTE_VARNAME_LENGTH; @@ -593,7 +646,6 @@ parse_elseif(struct TMPL_token* head, struct TMPL_buf* errbuf){ t->TMPL_tag.ifelse.tbranch = parse(head->next,errbuf); t->next = parse(cursor->next,errbuf); - t->type = tag_elseif; return t; @@ -602,6 +654,7 @@ parse_elseif(struct TMPL_token* head, struct TMPL_buf* errbuf){ struct TMPL_tagnode* parse_if(struct TMPL_token* head, struct TMPL_buf* errbuf){ struct TMPL_tagnode* t = alloc_tagnode(); + head->into = t; const char* start_of_attribs = head->start + TAG_IF_LENGTH; /*Find the name of the varialbe*/ int name_offset = kmp(start_of_attribs,head->length,ATTRIBUTE_VARNAME,ATTRIBUTE_VARNAME_LENGTH); @@ -681,7 +734,6 @@ parse_if(struct TMPL_token* head, struct TMPL_buf* errbuf){ t->TMPL_tag.ifelse.tbranch = parse(head->next,errbuf); /*Walk the tbranch and try to find if we have elseifs*/ t->next = parse(cursor->next,errbuf); - t->type = tag_if; return t; } @@ -690,6 +742,7 @@ parse_if(struct TMPL_token* head, struct TMPL_buf* errbuf){ struct TMPL_tagnode* parse_loop(struct TMPL_token* head, struct TMPL_buf* errbuf){ struct TMPL_tagnode* t = alloc_tagnode(); + head->into = t; const char* loop_start = head->start + TAG_LOOP_LENGTH; int loop_length = head->length - TAG_LOOP_LENGTH; int name_offset = kmp(loop_start,loop_length, ATTRIBUTE_VARNAME, ATTRIBUTE_VARNAME_LENGTH); @@ -736,13 +789,27 @@ parse_loop(struct TMPL_token* head, struct TMPL_buf* errbuf){ } cursor = cursor->next; } + printf("When parsing loop, cursor for continue is %p\n",(void*)cursor); if(cursor == NULL){ bputs(errbuf,"Parse error: Tried parsing loop and hit end of stream near\n"); bputsn(errbuf,head->start,ERRBUF_HINTLEN); return NULL; } - t->TMPL_tag.loop.body = parse(head->next,errbuf); + printf("When parsing loop, cursor was %p and cursor->next was %p\n",(void*)cursor,(void*)cursor->next); t->next = parse(cursor->next,errbuf); + printf("Setting loop %p's next to %p\n",(void*)t,(void*)t->next); + t->TMPL_tag.loop.body = parse(head->next,errbuf); + return t; +} + +const char end_str[] = ""; +struct TMPL_tagnode* parse_end(struct TMPL_token* head, struct TMPL_buf* errbuf){ + struct TMPL_tagnode* t = alloc_tagnode(); + head->into = t; + t->TMPL_tag.text.start = end_str; + t->TMPL_tag.text.len = 0; + t->type = tag_text; + t->next = NULL; return t; } @@ -750,6 +817,7 @@ parse_loop(struct TMPL_token* head, struct TMPL_buf* errbuf){ struct TMPL_tagnode* parse_variable(struct TMPL_token* head, struct TMPL_buf* errbuf){ struct TMPL_tagnode* t = alloc_tagnode(); + head->into =t; /* ^*/ const char* start_of_attribs = head->start + TAG_VAR_LENGTH; @@ -813,6 +881,110 @@ parse_variable(struct TMPL_token* head, struct TMPL_buf* errbuf){ return t; } +struct TMPL_tagnode* +parse_continue(struct TMPL_token* head, struct TMPL_buf* errbuf){ + printf("Praseing continue\n"); + struct TMPL_tagnode* t = alloc_tagnode(); + head->into = t; + const char* start_of_attribs = head->start + TAG_CONTINUE_LENGTH; + unsigned int level = 1; //how many loops to continue + /*We might have multiple loops to continue through*/ + int level_offset = kmp(start_of_attribs,head->length,ATTRIBUTE_LEVEL, ATTRIBUTE_LEVEL_LENGTH); + if(level_offset != -1){//level found + const char* start_of_level = start_of_attribs + level_offset + ATTRIBUTE_LEVEL_LENGTH; + while(is_whitespace(*start_of_level)) + start_of_level++; + if(*start_of_level != '='){ + printf("Start of level was %c\n",*start_of_level); + if(*start_of_level == '>'){ + level = 1; + }else{ + bputs(errbuf,"Parsing error: Expected \"=\" in near "); + bputsn(errbuf,head->start,ERR_MSG_LEN); + bputs(errbuf,"\n"); + } + }else{ + start_of_level++; + while(is_whitespace(*start_of_level)){ + start_of_level++; + } + sscanf(start_of_level,"%u",&level); + } + } + printf("Found continue %u levels\n",level); + int y = level; + struct TMPL_token* cursor = head; + while(y > 0){ + //Scan backwards until we find the loop we're in + while(cursor != NULL && cursor->type != tag_loop){ + cursor = cursor->last; + } + y--; + if(y > 0 && cursor == NULL){ + size_t msglen = snprintf(NULL,0,"Parsing error: Continue was %d levels, but was only nested %d levels deep\n",level, level - y); + char errmsg[msglen]; + sprintf(errmsg, "Parsing error: Continue was %d levels, but was only nested %d levels deep\n",level, level - y); + bputs(errbuf,errmsg); + } + } + printf("Turning continue's next into %5s's into(%p)\n",cursor->start,(void*)cursor->into->next); + t->TMPL_tag.breakcont.level = level; + t->next = parse(head->next,errbuf); + t->type = tag_continue; + return t; +} + +struct TMPL_tagnode* +parse_break(struct TMPL_token* head, struct TMPL_buf* errbuf){ + struct TMPL_tagnode* t = alloc_tagnode(); + head->into = t; + const char* start_of_attribs = head->start + TAG_CONTINUE_LENGTH; + unsigned int level = 1; //how many loops to continue + /*We might have multiple loops to continue through*/ + int level_offset = kmp(start_of_attribs,head->length,ATTRIBUTE_LEVEL, ATTRIBUTE_LEVEL_LENGTH); + if(level_offset != -1){ //level found + const char* start_of_level = start_of_attribs + level_offset + ATTRIBUTE_LEVEL_LENGTH; + while(is_whitespace(*start_of_level)) + start_of_level++; + if(*start_of_level != '='){ + bputs(errbuf,"Parsing error: Expected \"=\" in near "); + bputsn(errbuf,head->start,ERR_MSG_LEN); + bputs(errbuf,"\n"); + }else{ + start_of_level++; + while(is_whitespace(*start_of_level)){ + start_of_level++; + } + sscanf(start_of_level,"%u",&level); + } + } + t->TMPL_tag.breakcont.level = level; + int y = level; + struct TMPL_token* cursor = head; + while(y > 0){ + //Scan backwards until we find the loop we're in + while(cursor != NULL && cursor->type != tag_loop){ + cursor = cursor->last; + } + y--; + if(y > 0 && cursor == NULL){ + printf("Failed 927\n"); + exit(-1); + size_t msglen = snprintf(NULL,0,"Parsing error: Break was %d levels, but was only nested %d levels deep\n",level,level - y); + char errmsg[msglen]; + sprintf(errmsg,"Parsing error: Break was %d levels, but was only nested %d levels deep\n",level,level - y); + bputs(errbuf,errmsg); + } + } + printf("Turning break's next into %5s's into (%p)\n",cursor->start,(void*)cursor->into); + /*t->next = cursor->into;*/ + t->next = NULL; + printf("At time of prasing, cursor is %p and cursor->into is %p\n",(void*)cursor,(void*)cursor->into); + t->TMPL_tag.breakcont.into = cursor->into; + t->type = tag_break; + return t; +} + struct TMPL_varlist* TMPL_alloc_varlist(){ struct TMPL_varlist* ret = (struct TMPL_varlist*)malloc(sizeof(struct TMPL_varlist)); @@ -891,6 +1063,7 @@ parse(struct TMPL_token* head, struct TMPL_buf* errbuf){ if(head == NULL){ return NULL; } + printf("Parse called... on type (%d:%s)\n",head->type,tokentype_to_str(head)); switch(head->type){ case tag_text: root = parse_text(head,errbuf); @@ -902,7 +1075,8 @@ parse(struct TMPL_token* head, struct TMPL_buf* errbuf){ root = parse_loop(head,errbuf); break; case tag_end: - root = NULL; + //A 0-sized string to make break fall out right + root = parse_end(head,errbuf); break; case tag_if: root = parse_if(head,errbuf); @@ -913,13 +1087,25 @@ parse(struct TMPL_token* head, struct TMPL_buf* errbuf){ case tag_else: root = NULL; break; + case tag_continue: + printf("Parsing continue\n"); + root = parse_continue(head,errbuf); + break; + case tag_break: + printf("Parsing break\n"); + root = parse_break(head,errbuf); + break; default: exit(-1); break; } if(errbuf->total_len > 0){ + printf("Error parsing %p\n",(void*)head); + size_t dummy; + printf("Error: %s\n",bstringify(errbuf,&dummy)); return NULL; } + printf("parse returning %p\n",(void*)root); return root; } @@ -936,17 +1122,22 @@ compile(const char* tmplstr){ ret->out = alloc_tmpl_buf(); ret->errout = alloc_tmpl_buf(); struct TMPL_token* tokens = TMPL_tokenize(tmplstr,slen); + printf("After tokenizing, tokens are:\n"); + print_tokens(tokens); struct TMPL_tagnode* ast = parse(tokens,ret->errout); + size_t dummy; + printf("errbuf:%s\n",bstringify(ret->errout,&dummy)); + printf("After parsing, syntax tree is:\n"); if(ast == NULL){ //size_t dummy; //printf("error: %s\n",bstringify(ret->errout,&dummy)); }else{ ret->roottag = ast; } + print_ast(ret); return ret; } - int render_variable(struct TMPL_templates* t, struct TMPL_tagnode* node, struct TMPL_varlist* varlist){ struct TMPL_varitem* vi; @@ -1021,7 +1212,6 @@ print_varlist_helper(struct TMPL_varlist* vl, int indent){ hashmap_iterate(vl->map, print_pair,&indent); } - void print_varlist(struct TMPL_varlist* vl){ print_varlist_helper(vl,0); @@ -1079,6 +1269,28 @@ resolve_name(struct TMPL_varlist* varlist, char* name,struct TMPL_varitem** item */ } +int +render_break(struct TMPL_templates* t, struct TMPL_tagnode* node, struct TMPL_varlist* varlist){ + printf("Rendering break\n"); + printf("node is:%p\n",(void*)node); + printf("node->next:%p\n",(void*)node->TMPL_tag.breakcont.into->next); + printf("varlist->parent:%p\n",(void*)varlist->parent); + printf("varlist->parent->parent:%p\n",(void*)varlist->parent->parent); + printf("node into:%p\n",(void*)node->TMPL_tag.breakcont.into); + printf("node into->next:%p\n",(void*)node->TMPL_tag.breakcont.into->next); + /*struct TMPL_templates* nt = (struct TMPL_templates)malloc(sizeof(struct TMPL_templates));*/ + /*nt->out = t->out;*/ + /*nt->errout = t->errout;*/ + /*nt->roottag = node->TMPL_tag.breakcont.*/ + return render_any(t,node->TMPL_tag.breakcont.into->next,varlist->parent->parent); +} + +int +render_continue(struct TMPL_templates* t, struct TMPL_tagnode* node, struct TMPL_varlist* varlist){ + printf("Rendering continue\n"); + return render_any(t,node->next,varlist->parent->next->varlist); +} + int render_loop(struct TMPL_templates* t, struct TMPL_tagnode* node, struct TMPL_varlist* varlist){ char* loopname = node->TMPL_tag.loop.loopname; @@ -1132,7 +1344,11 @@ render_any(struct TMPL_templates* t, struct TMPL_tagnode* node, struct TMPL_varl if(err < 0){ } break; + case tag_break: + err = render_break(t,node,varlist); + break; default: + printf("Failed to find node type: %d\n",node->type); exit(-1); break; } @@ -1141,6 +1357,7 @@ render_any(struct TMPL_templates* t, struct TMPL_tagnode* node, struct TMPL_varl void TMPL_render_helper(struct TMPL_templates* t, struct TMPL_varlist* varlist){ + printf("Render helper\n"); struct TMPL_tagnode* cursor = t->roottag; while(cursor != NULL){ render_any(t,cursor,varlist); diff --git a/src/ctemplates.h b/src/ctemplates.h index eafabac..8bf567d 100644 --- a/src/ctemplates.h +++ b/src/ctemplates.h @@ -35,6 +35,7 @@ typedef struct TMPL_fmtlists TMPL_fmtlists; #define ATTRIBUTE_VARNAME "name" #define ATTRIBUTE_DEFAULT "default" #define ATTRIBUTE_VALUE "value" +#define ATTRIBUTE_LEVEL "level" #define ERRBUF_HINTLEN 50 @@ -55,6 +56,7 @@ typedef struct TMPL_fmtlists TMPL_fmtlists; #define ATTRIBUTE_VARNAME_LENGTH SIZEOF(ATTRIBUTE_VARNAME) - 1 #define ATTRIBUTE_DEFAULT_LENGTH SIZEOF(ATTRIBUTE_DEFAULT) - 1 #define ATTRIBUTE_VALUE_LENGTH SIZEOF(ATTRIBUTE_VALUE) - 1 +#define ATTRIBUTE_LEVEL_LENGTH SIZEOF(ATTRIBUTE_LEVEL) - 1 //Define to 0 for slight speedup improvements, no errors #define DEBUGGING 1 @@ -86,17 +88,21 @@ enum TMPL_vartype{ vartype_var }; +struct TMPL_varlist TMPL_varlist; +struct TMPL_loop TMPL_loop; struct TMPL_varlist{ map_t map; + struct TMPL_loop* parent; }TMPL_varlist; struct TMPL_loop{ struct TMPL_varlist* varlist; + struct TMPL_varlist* parent; size_t loop_len; struct TMPL_loop* next; struct TMPL_loop* tail; -}; +}TMPL_loop; struct TMPL_varitem{ enum TMPL_vartype type; @@ -110,6 +116,7 @@ struct TMPL_varitem{ struct TMPL_tagnode{ enum TMPL_tagtype type; struct TMPL_tagnode* next; + struct TMPL_tagnode* parent; unsigned int line; unsigned int character; union{ @@ -142,7 +149,8 @@ struct TMPL_tagnode{ /*Break and Continue*/ struct{ - int level; + int level;//Just use next as the next value + struct TMPL_tagnode* into; }breakcont; }TMPL_tag; }TMPL_tagnode; @@ -178,6 +186,7 @@ struct TMPL_token{ struct TMPL_token* last;//The last token unsigned int line;// Not used currently unsigned int character;// Not used currently + struct TMPL_tagnode* into;//The tag node this item became }TMPL_token; int TMPL_add_var_to_varlist(struct TMPL_varlist* vl, const char* name, const char* value); diff --git a/t/test_2.c b/t/test_2.c new file mode 100644 index 0000000..b67cca2 --- /dev/null +++ b/t/test_2.c @@ -0,0 +1,47 @@ + +#include + +#include + +char t_1[] = "This is a TextText2!"; +char c_1_1[] = "This is a Text!"; + +#define log(x) printf(x) + +int main(){ + log("Running tests2\n"); + struct TMPL_templates* t; + struct TMPL_varlist *vl,*vl1,*vl2; + struct TMPL_loop *l1,*l2; + char* ret; + size_t dummy; + + /*Test 1: Variable*/ + t = TMPL_alloc_template(t_1); + vl = TMPL_alloc_varlist(); + TMPL_add_var_to_varlist(vl,"what","template"); + l1 = TMPL_alloc_loop(); + vl1 = TMPL_alloc_varlist(); + + l2 = TMPL_alloc_loop(); + vl2 = TMPL_alloc_varlist(); + TMPL_add_varlist_to_loop(l2,vl2); + TMPL_add_loop_to_varlist(vl1,"loop2",l2); + + TMPL_add_varlist_to_loop(l1,vl1); + TMPL_add_loop_to_varlist(vl,"loop1",l1); + + ret = TMPL_render(t,vl,&dummy); + if(strcmp(ret,c_1_1) != 0){ + fprintf(stderr,"Error in test file 2, test 1\n"); + printf("Result should have been '%s'\n was '%s'\n",c_1_1,ret); + if(t->error) + printf(TMPL_err(t, NULL)); + return -1; + } + TMPL_free_template(t); + TMPL_free_varlist(vl); + log("Test 1 complete\n"); + + return 0; +} -- cgit v1.2.3-70-g09d2