diff --git a/lib/compiler.c b/lib/compiler.c index bc209f3..95099ba 100644 --- a/lib/compiler.c +++ b/lib/compiler.c @@ -121,6 +121,17 @@ void compiler_run(compiler_t* compiler, node_t* node) { op = OP_USUB; } + else if (node->type == NODE_ADD + && node_find_first(node, NODE_STR)) + { + op = OP_STRCAT; + } + else if (node->type == NODE_MUL + && node_find_first(node, NODE_STR) + && node_find_first(node, NODE_NUM)) + { + op = OP_STRDUP; + } else { switch (node->type) diff --git a/lib/node.c b/lib/node.c index 2190479..66aed2b 100644 --- a/lib/node.c +++ b/lib/node.c @@ -97,3 +97,25 @@ size_t node_str(node_t* node, char* buffer, size_t size) return sz; } + +node_t* node_find_first(node_t* node, NodeType type) +{ + assert(node); + + if (node->type == type) + { + return node; + } + + for (size_t i=0; ichildren.size; i++) + { + node_t* child = node_find_first((node_t*) node->children.data[i], type); + + if (child) + { + return child; + } + } + + return NULL; +} diff --git a/lib/node.h b/lib/node.h index 6b0efc8..f045312 100644 --- a/lib/node.h +++ b/lib/node.h @@ -34,5 +34,6 @@ void node_add_new_child(node_t* node, node_t* child); node_t* node_child(node_t* node, size_t index); size_t node_str(node_t* node, char* buffer, size_t size); +node_t* node_find_first(node_t* node, NodeType type); #endif diff --git a/lib/opcodes.h b/lib/opcodes.h index a83c2e2..62c0c99 100644 --- a/lib/opcodes.h +++ b/lib/opcodes.h @@ -11,7 +11,8 @@ G(OP_ADD), G(OP_SUB), G(OP_MUL), G(OP_DIV), \ G(OP_MODULO), G(OP_POW), G(OP_USUB), \ G(OP_AND), G(OP_OR), G(OP_NOT), \ - G(OP_BRF), G(OP_BRT), G(OP_BR) + G(OP_BRF), G(OP_BRT), G(OP_BR), \ + G(OP_STRCAT), G(OP_STRDUP) RZ_ENUM_H(Opcode, OPCODES); diff --git a/lib/vm.c b/lib/vm.c index 5c6ecae..fb4f0e0 100644 --- a/lib/vm.c +++ b/lib/vm.c @@ -21,7 +21,7 @@ void vm_free(vm_t* vm) assert(vm); } -void vm_push_value(vm_t* vm, mod_t* mod, value_t* value) +void vm_push_new_value(vm_t* vm, mod_t* mod, value_t* value) { assert(vm); assert(value); @@ -146,7 +146,7 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param) : (!same_type || !eq)), rhs->line); - vm_push_value(vm, mod, res); + vm_push_new_value(vm, mod, res); vm->pc++; } break; @@ -176,7 +176,7 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param) } value_t* res = tysy_new_bool(vm->tysy, val, lhs->line); - vm_push_value(vm, mod, res); + vm_push_new_value(vm, mod, res); vm->pc++; } break; @@ -210,7 +210,63 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param) } value_t* res = tysy_new_num(vm->tysy, val, lhs->line); - vm_push_value(vm, mod, res); + vm_push_new_value(vm, mod, res); + + vm->pc++; + } break; + + case OP_STRCAT: { + int r = vm_pop(vm); + int l = vm_pop(vm); + + value_t* lhs = mod->values.data[l]; + value_t* rhs = mod->values.data[r]; + + vm_ensure_type(vm, lhs, TYPE_STR); + vm_ensure_type(vm, rhs, TYPE_STR); + + str_t res; + str_init(&res); + str_append(&res, lhs->value.str); + str_append(&res, rhs->value.str); + + value_t* value = tysy_new_str(vm->tysy, res.data, lhs->line); + str_free(&res); + + vm_push_new_value(vm, mod, value); + + vm->pc++; + } break; + + case OP_STRDUP: { + int r = vm_pop(vm); + int l = vm_pop(vm); + + value_t* lhs = mod->values.data[l]; + value_t* rhs = mod->values.data[r]; + + if (lhs->type->kind != TYPE_NUM) + { + value_t* tmp = lhs; + lhs = rhs; + rhs = tmp; + } + + vm_ensure_type(vm, lhs, TYPE_NUM); + vm_ensure_type(vm, rhs, TYPE_STR); + + str_t res; + str_init(&res); + + for (int i=0; i<(int) lhs->value.num; i++) + { + str_append(&res, rhs->value.str); + } + + value_t* value = tysy_new_str(vm->tysy, res.data, lhs->line); + str_free(&res); + + vm_push_new_value(vm, mod, value); vm->pc++; } break; @@ -236,7 +292,7 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param) } value_t* res = tysy_new_bool(vm->tysy, val, lhs->line); - vm_push_value(vm, mod, res); + vm_push_new_value(vm, mod, res); vm->pc++; } break; @@ -251,7 +307,7 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param) double val = -lhs->value.num; value_t* res = tysy_new_num(vm->tysy, val, lhs->line); - vm_push_value(vm, mod, res); + vm_push_new_value(vm, mod, res); vm->pc++; } break; @@ -264,7 +320,7 @@ int vm_exec_instr(vm_t* vm, mod_t* mod, Opcode op, param_t param) int val = !lhs->value.bool; value_t* res = tysy_new_bool(vm->tysy, val, lhs->line); - vm_push_value(vm, mod, res); + vm_push_new_value(vm, mod, res); vm->pc++; } break; @@ -320,5 +376,6 @@ void vm_ensure_type(vm_t* vm, value_t* value, TypeKind want) TypeKindStr[got] + strlen("TYPE_")); err_fatal(vm->err, msg, line); + err_dump(vm->err); } } diff --git a/lib/vm.h b/lib/vm.h index 9745e9a..6292b39 100644 --- a/lib/vm.h +++ b/lib/vm.h @@ -20,7 +20,7 @@ typedef struct { void vm_init(vm_t* vm, tysy_t* tysy, err_t* err); void vm_free(vm_t* vm); -void vm_push_value(vm_t* vm, mod_t* mod, value_t* value); +void vm_push_new_value(vm_t* vm, mod_t* mod, value_t* value); void vm_push(vm_t* vm, param_t param); param_t vm_pop(vm_t* vm); diff --git a/tests/acceptances/str.roza b/tests/acceptances/str.roza new file mode 100644 index 0000000..213d273 --- /dev/null +++ b/tests/acceptances/str.roza @@ -0,0 +1,14 @@ +# STRING OPERATIONS +# ================= + +# concatenation with + +assert "hel" + "lo" == "hello" +assert "hello" + " " + "world" == "hello world" + +# duplication with * +assert "a" * 3 == "aaa" +assert 4 * "!" == "!!!!" + +# bonus +assert ("a" * 2) + "b" == "aab" +assert 4 * ("a" + "b") == "abababab" \ No newline at end of file diff --git a/tests/units/lexer.c b/tests/units/lexer.c index e4f8f13..9065688 100644 --- a/tests/units/lexer.c +++ b/tests/units/lexer.c @@ -152,5 +152,4 @@ Test(lexer, bool_arith) { "AND", "OR", "NOT"); - }