diff --git a/doc/grammar.bnf b/doc/grammar.bnf index 485fc5f..a8f6865 100644 --- a/doc/grammar.bnf +++ b/doc/grammar.bnf @@ -7,8 +7,10 @@ EXPR ::= | BEGIN | IF end | WHILE +| FOR | continue | break +FOR ::= for EXPR in EXPR BLOCK end WHILE ::= while EXPR BLOCK end IF ::= | if EXPR BLOCK (else (IF | BLOCK))? diff --git a/lib/bytecode.h b/lib/bytecode.h index 7b6759b..25f0375 100644 --- a/lib/bytecode.h +++ b/lib/bytecode.h @@ -10,7 +10,7 @@ G(OP_DIV), G(OP_POW), G(OP_MOD), G(OP_MK_TUPLE), \ G(OP_ASSERT_EQ), G(OP_ASSERT_NE), G(OP_BRF), G(OP_BR), \ G(OP_NOT), G(OP_IN), G(OP_INDEX), G(OP_EQ), G(OP_LT), \ G(OP_GT), G(OP_MK_ARRAY), G(OP_LOCAL_STORE), \ -G(OP_LOCAL_LOAD), G(OP_ASTORE) +G(OP_LOCAL_LOAD), G(OP_ASTORE), G(OP_LEN) CCM_ENUM_H(Opcode, OPCODES); diff --git a/lib/ccm.c b/lib/ccm.c index 38dfbc3..adb28b2 100644 --- a/lib/ccm.c +++ b/lib/ccm.c @@ -266,10 +266,9 @@ char* ccm_from_str(ccm_t* self, CCM value) return ccm_find_value(self, value)->data.str; } -CCM ccm_to_str(ccm_t* self, char* value, int line) +CCM ccm_to_str(ccm_t* self, char const* value, int line) { assert(self); - assert(value); value_t* val = malloc(sizeof(value_t)); value_init_str(val, value, line); diff --git a/lib/ccm.h b/lib/ccm.h index 702101c..279bcc7 100644 --- a/lib/ccm.h +++ b/lib/ccm.h @@ -51,7 +51,7 @@ CCM ccm_to_boolean(ccm_t* self, int value, int line); int ccm_is_str(ccm_t* self, CCM value); char* ccm_from_str(ccm_t* self, CCM value); -CCM ccm_to_str(ccm_t* self, char* value, int line); +CCM ccm_to_str(ccm_t* self, char const* value, int line); int ccm_is_array(ccm_t* self, CCM value); vec_t* ccm_from_array(ccm_t* self, CCM value); diff --git a/lib/compiler.c b/lib/compiler.c index ddb734e..54be198 100644 --- a/lib/compiler.c +++ b/lib/compiler.c @@ -1,4 +1,5 @@ #include "compiler.h" +#include "prog.h" void compiler_init(compiler_t* self, module_t* module) { @@ -59,6 +60,99 @@ void compiler_compile(compiler_t* self, vec_push(&info->to_end, (void*) point); } break; + + case NODE_FOR: { + node_t const* ident = node->children.data[0]; + node_t* expr = node->children.data[1]; + node_t* block = node->children.data[2]; + + + sym_open_scope(sym); + sym_declare(sym, "__iter", + self->loc_counter++, 0); + sym_declare(sym, ident->value, + self->loc_counter++, 0); + + int var_id = + sym_try_get_value(sym, ident->value)->id; + + int iter_id = + sym_try_get_value(sym, "__iter")->id; + + // initialize iteration counter + value_t* value = malloc(sizeof(value_t)); + value_init_num(value, -1, node->line); + + prog_add_instr(prog, OP_PUSH, prog_add_new_constant( + prog, + ccm_from_value(ccm, value) + )); + + prog_add_instr(prog, OP_LOCAL_STORE, iter_id); + + int start = prog->instrs.size; + loop_info_t* info = malloc(sizeof(loop_info_t)); + info->start_point = start; + vec_init(&info->to_end); + vec_push(&self->loop_infos, info); + + // incr iteration counter + prog_add_instr(prog, OP_LOCAL_LOAD, iter_id); + + value_free(value); free(value); + value = malloc(sizeof(value_t)); + value_init_num(value, 1, node->line); + + prog_add_instr(prog, OP_PUSH, prog_add_new_constant( + prog, + ccm_from_value(ccm, value) + )); + + prog_add_instr(prog, OP_ADD, CCM_NO_PARAM); + prog_add_instr(prog, OP_LOCAL_STORE, iter_id); + + // test if end is reached + prog_add_instr(prog, OP_LOCAL_LOAD, iter_id); + + compiler_compile(self, expr, prog); + prog_add_instr(prog, OP_LEN, CCM_NO_PARAM); + + + prog_add_instr(prog, OP_LT, CCM_NO_PARAM); + int cond_point = prog_add_instr(prog, OP_BRF, 0); + + // get current element + prog_add_instr(prog, OP_LOCAL_LOAD, iter_id); + compiler_compile(self, expr, prog); + prog_add_instr(prog, OP_INDEX, 1); + prog_add_instr(prog, OP_LOCAL_STORE, var_id); + + // compile body + compiler_compile(self, block, prog); + + // next iteration + prog_add_instr(prog, OP_BR, start); + + value_free(value); free(value); + + int end_point = prog->instrs.size; + ((instr_t*)prog->instrs.data[cond_point])->param + = end_point; + + for (size_t i=0; ito_end.size; i++) + { + int addr = (size_t)info->to_end.data[i]; + instr_t* instr = prog->instrs.data[addr]; + instr->param = end_point; + } + + sym_close_scope(sym); + + info = vec_pop(&self->loop_infos); + vec_free(&info->to_end); + free(info); + } break; + case NODE_WHILE: { node_t* expr = node->children.data[0]; node_t* block = node->children.data[1]; diff --git a/lib/exec.c b/lib/exec.c index d407025..1d3e09d 100644 --- a/lib/exec.c +++ b/lib/exec.c @@ -130,6 +130,31 @@ void exec_instr(exec_t* self, self->pc++; } break; + case OP_LEN: { + CCM ccm_value = ccm_pop(ccm); + value_t* value = ccm_to_value(ccm, ccm_value); + CCM ccm_res = 0; + if (value->type == TYPE_ARRAY) + { + ccm_res = ccm_to_num( + ccm, + value->data.array->size, + value->line + ); + } + else if (value->type == TYPE_STR) + { + ccm_res = ccm_to_num( + ccm, + strlen(value->data.str), + value->line + ); + } + + ccm_push(ccm, ccm_res); + + self->pc++; + } break; case OP_INDEX: { CCM ccm_target = ccm_pop(ccm); @@ -169,7 +194,7 @@ void exec_instr(exec_t* self, return; } - char buf[2] = {target[idx], '\0'}; + char const buf[2] = {target[idx], '\0'}; ccm_push(ccm, ccm_to_str(ccm, buf, line)); } else if (ccm_is_tuple(ccm, ccm_target) diff --git a/lib/lexer.c b/lib/lexer.c index 40aaf74..f009dda 100644 --- a/lib/lexer.c +++ b/lib/lexer.c @@ -451,7 +451,13 @@ node_t* lexer_try_new_str(lexer_t* self) cursor++; self->cursor = cursor; node_t* node = malloc(sizeof(node_t)); - node_init(node, NODE_STR, value.value, self->line); + + node_init( + node, + NODE_STR, + value.size == 0 ? "" : value.value, + self->line + ); str_free(&value); diff --git a/lib/parser.c b/lib/parser.c index a9a819c..8ba3e69 100644 --- a/lib/parser.c +++ b/lib/parser.c @@ -168,6 +168,11 @@ node_t* parser_try_new_expr(parser_t* self) return CCM_TRY(parser_try_new_while); } + if (lexer_peek_kind(self->lexer, NODE_FOR, 0)) + { + return CCM_TRY(parser_try_new_for); + } + if (lexer_peek_kind(self->lexer, NODE_CONTINUE, 0) || lexer_peek_kind(self->lexer, NODE_BREAK, 0)) { @@ -216,6 +221,55 @@ node_t* parser_try_new_while(parser_t* self) return node; } +node_t* parser_try_new_for(parser_t* self) +{ + assert(self); + + if (!parser_consume(self, NODE_FOR)) + { + return NULL; + } + + node_t* ident = NULL; + + if (lexer_peek_kind(self->lexer, NODE_IDENT, 0)) + { + ident = lexer_try_new_next(self->lexer); + } + + if (!ident) { return NULL; } + + if (!parser_consume(self, NODE_IN)) + { + node_free(ident); free(ident); + return NULL; + } + + node_t* target = CCM_TRY(parser_try_new_expr); + + if (!target) + { + node_free(ident); free(ident); + return NULL; + } + + node_t* block = CCM_TRY(parser_try_new_block); + + if (!block || !parser_consume(self, NODE_END)) + { + node_free(ident); free(ident); + return NULL; + } + + node_t* node = malloc(sizeof(node_t)); + node_init(node, NODE_FOR, "", ident->line); + node_push_new_child(node, ident); + node_push_new_child(node, target); + node_push_new_child(node, block); + + return node; +} + node_t* parser_try_new_if(parser_t* self) { assert(self); diff --git a/lib/parser.h b/lib/parser.h index 0ba2b23..525fce2 100644 --- a/lib/parser.h +++ b/lib/parser.h @@ -26,6 +26,7 @@ int parser_ensure(parser_t* self, node_t* node, NodeKind kind); node_t* parser_try_new_module(parser_t* self); node_t* parser_try_new_expr(parser_t* self); +node_t* parser_try_new_for(parser_t* self); node_t* parser_try_new_while(parser_t* self); node_t* parser_try_new_if(parser_t* self); node_t* parser_try_new_begin(parser_t* self); diff --git a/lib/str.c b/lib/str.c index b1b8876..e4ce292 100644 --- a/lib/str.c +++ b/lib/str.c @@ -60,7 +60,11 @@ void str_push(str_t* self, char c) void str_push_cstr(str_t* self, char const* rhs) { assert(self); - assert(rhs); + + if (!rhs) + { + return; + } for (size_t i=0; idata.str = strdup(value); self->type = TYPE_STR; self->line = line; @@ -115,7 +113,6 @@ void value_free(value_t* self) { if (self->type == TYPE_ARRAY) { - //vec_free_elements(self->data.array, (void*) value_free); vec_free(self->data.array); free(self->data.array); } @@ -127,7 +124,7 @@ void value_free(value_t* self) free(self->data.tuple); } - if (self->type == TYPE_STR) + if (self->type == TYPE_STR && self->data.str) { free(self->data.str); } diff --git a/tests/loop.ccm b/tests/loop.ccm index 719bd55..91cd695 100644 --- a/tests/loop.ccm +++ b/tests/loop.ccm @@ -1,3 +1,5 @@ +# WHILE LOOP +# ========== var a = 0 while a < 100 @@ -63,3 +65,43 @@ while g < 100 end assert_eq (21, i) + +# FOR LOOP +# ======== +var j = 0 +for k in [1, 2, 3] + j = j + k +end + +assert_eq (6, j) + +var l = 0 +var m = [7, 3, 1] + +for n in m + l = l + n +end + +assert_eq (11, l) + +var o = 0 +for p in [1, 2, 3, 4, 5, 6, 7] + if p > 5 + break + end + + if p % 2 == 0 + continue + end + + o = o + p +end + +assert_eq (9, o) + +var q = "" +for r in "hello" + q = r + q +end + +assert_eq ("olleh", q)