From 1db42c1e8ea4581df7bda91ba409b3d1773d854c Mon Sep 17 00:00:00 2001 From: samsonium Date: Thu, 5 Nov 2026 21:48:25 +0300 Subject: [PATCH] wip: working on extern in VM - Feat: add writer externs writing fns - Feat: add VM reader functions --- CMakeLists.txt | 13 +++ include/vm/fzl_vm_reader.h | 43 +++++++ include/vm/internals/reader.h | 22 ++++ src/compiler/fzl_writer.c | 8 +- src/compiler/writer/wr_consts.c | 11 +- src/compiler/writer/wr_extern_fns.c | 1 + src/compiler/writer/wr_extern_types.c | 14 ++- src/vm/fzl_vm.c | 2 +- src/vm/fzl_vm_reader.c | 147 ++++++++++++++++++++++++ src/vm/reader/utility.c | 32 ++++++ src/vm/reader/vm_rd_consts.c | 52 +++++++++ src/vm/reader/vm_rd_extern_fns.c | 20 ++++ src/vm/reader/vm_rd_extern_libs.c | 49 ++++++++ src/vm/reader/vm_rd_extern_types.c | 156 ++++++++++++++++++++++++++ src/vm/reader/vm_rd_header.c | 24 ++++ src/vm/reader/vm_rd_sections_sz.c | 29 +++++ tests/libs/math.c | 1 + 17 files changed, 608 insertions(+), 16 deletions(-) create mode 100644 include/vm/fzl_vm_reader.h create mode 100644 include/vm/internals/reader.h create mode 100644 src/vm/fzl_vm_reader.c create mode 100644 src/vm/reader/utility.c create mode 100644 src/vm/reader/vm_rd_consts.c create mode 100644 src/vm/reader/vm_rd_extern_fns.c create mode 100644 src/vm/reader/vm_rd_extern_libs.c create mode 100644 src/vm/reader/vm_rd_extern_types.c create mode 100644 src/vm/reader/vm_rd_header.c create mode 100644 src/vm/reader/vm_rd_sections_sz.c create mode 100644 tests/libs/math.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 79901be..ba91cd0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -159,6 +159,13 @@ if (NOT FZL_SKIP_VM) src/vm/handlers/group_str.c src/vm/handlers/group_strcmp.c src/vm/handlers/master.c + src/vm/reader/utility.c + src/vm/reader/vm_rd_consts.c + src/vm/reader/vm_rd_extern_fns.c + src/vm/reader/vm_rd_extern_libs.c + src/vm/reader/vm_rd_extern_types.c + src/vm/reader/vm_rd_header.c + src/vm/reader/vm_rd_sections_sz.c src/vm/fzl_vm.c src/vm/fzl_vm_env.c src/vm/fzl_vm_debug.c @@ -186,6 +193,12 @@ endif () if (FZL_TESTS) enable_testing() + file(GLOB TEST_LIBS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "tests/libs/*.c") + foreach (TEST_LIB IN LISTS TEST_LIBS) + get_filename_component(LIB_NAME "${TEST_LIB}" NAME_WE) + add_library("${LIB_NAME}" ${TEST_LIB}) + endforeach () + file(GLOB TEST_PROGS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "tests/programs/*.fzs") foreach (TEST_FILE IN LISTS TEST_PROGS) get_filename_component(TEST_NAME "${TEST_FILE}" NAME_WE) diff --git a/include/vm/fzl_vm_reader.h b/include/vm/fzl_vm_reader.h new file mode 100644 index 0000000..fbe5316 --- /dev/null +++ b/include/vm/fzl_vm_reader.h @@ -0,0 +1,43 @@ +#ifndef FZL_VM_READER_H +#define FZL_VM_READER_H + +#include +#include +#include +#include +#include + +// Binary file reader descriptor +typedef struct +{ + char *filepath; + + fzl_value_t *consts; + uint16_t consts_count; + + size_t *ops; + size_t ops_count; + + char **extern_libs; + uint16_t extern_libs_count; + + fzl_extern_type_t *extern_types; + uint16_t extern_types_count; + + fzl_extern_fn_t *extern_fns; + uint16_t extern_fns_count; +} fzl_vm_reader_t; + +// Allocate binary file reader +fzl_vm_reader_t *fzl_vm_mkreader(char *filepath); + +// Read the file +bool fzl_vm_read(fzl_vm_reader_t *reader); + +// Get last error message from the reader +char *fzl_vm_getreadererror(); + +// Free file reader +void fzl_vm_freereader(fzl_vm_reader_t *reader); + +#endif // FZL_VM_READER_H diff --git a/include/vm/internals/reader.h b/include/vm/internals/reader.h new file mode 100644 index 0000000..8764cd7 --- /dev/null +++ b/include/vm/internals/reader.h @@ -0,0 +1,22 @@ +#ifndef INTERNALS_VM_READER_H +#define INTERNALS_VM_READER_H + +#include +#include +#include +#include +#include + +bool vm_rd_char(FILE *fd, size_t count, char *outbuf); +bool vm_rd_u16(FILE *fd, size_t count, uint16_t *outbuf); +bool vm_rd_word(FILE *fd, size_t count, size_t *outbuf); + +bool vm_rd_header(fzl_vm_reader_t *reader, FILE *fd); +bool vm_rd_sections_sz(fzl_vm_reader_t *reader, FILE *fd); +bool vm_rd_consts(fzl_vm_reader_t *reader, FILE *fd); +bool vm_rd_extern_libs(fzl_vm_reader_t *reader, FILE *fd); +bool vm_rd_extern_types(fzl_vm_reader_t *reader, FILE *fd); +bool vm_rd_extern_fns(fzl_vm_reader_t *reader, FILE *fd); +bool vm_rd_ops(fzl_vm_reader_t *reader, FILE *fd); + +#endif // INTERNALS_VM_READER_H diff --git a/src/compiler/fzl_writer.c b/src/compiler/fzl_writer.c index 59013ae..38e4e48 100644 --- a/src/compiler/fzl_writer.c +++ b/src/compiler/fzl_writer.c @@ -40,10 +40,10 @@ int fzl_write(fzl_writer_t *writer, fzl_gen_t *gen) const char hdr[5] = {'F', 'Z', 'L', 0x0, 0x2}; fwrite(hdr, sizeof(char), 5, fd); - fwrite(&gen->consts->size, sizeof(size_t), 1, fd); - fwrite(&gen->extern_libs_count, sizeof(size_t), 1, fd); - fwrite(&gen->extern_types_count, sizeof(size_t), 1, fd); - fwrite(&gen->extern_fns_count, sizeof(size_t), 1, fd); + fwrite(&gen->consts->size, sizeof(uint16_t), 1, fd); + fwrite(&gen->extern_libs_count, sizeof(uint16_t), 1, fd); + fwrite(&gen->extern_types_count, sizeof(uint16_t), 1, fd); + fwrite(&gen->extern_fns_count, sizeof(uint16_t), 1, fd); fwrite(&gen->ops_count, sizeof(size_t), 1, fd); bool result = wr_consts(fd, gen->consts); diff --git a/src/compiler/writer/wr_consts.c b/src/compiler/writer/wr_consts.c index 7de72a6..4f18724 100644 --- a/src/compiler/writer/wr_consts.c +++ b/src/compiler/writer/wr_consts.c @@ -11,14 +11,9 @@ bool wr_consts(FILE *fd, fzl_const_hash_pool_t *consts) for (size_t i = 0; i < consts->size; i++) { fzl_const_t c = consts->array[i]; - fwrite(&c.type, sizeof(fzl_consttype_t), 1, fd); - - if (c.type == FZL_CONSTTYPE_STRING) - { - size_t strlen = c.as.string.length; - fwrite(&c.as.string.length, sizeof(uint16_t), 1, fd); - fwrite(c.as.string.chars, sizeof(char), strlen, fd); - } + size_t strlen = c.as.string.length; + fwrite(&c.as.string.length, sizeof(uint16_t), 1, fd); + fwrite(c.as.string.chars, sizeof(char), strlen, fd); } return true; diff --git a/src/compiler/writer/wr_extern_fns.c b/src/compiler/writer/wr_extern_fns.c index ace030a..368b333 100644 --- a/src/compiler/writer/wr_extern_fns.c +++ b/src/compiler/writer/wr_extern_fns.c @@ -32,6 +32,7 @@ bool wr_extern_fns(FILE *fd, fzl_extern_fn_t *fns, size_t fns_count) for (size_t i = 0; i < fns_count; i++) { fzl_extern_fn_t *fn = &fns[i]; + fwrite(&fn->lib_slot, sizeof(uint16_t), 1, fd); size_t used_name_sz = strlen(fn->used_name); bool has_symbol_name = fn->symbol_name != NULL; diff --git a/src/compiler/writer/wr_extern_types.c b/src/compiler/writer/wr_extern_types.c index 9a615aa..d181b42 100644 --- a/src/compiler/writer/wr_extern_types.c +++ b/src/compiler/writer/wr_extern_types.c @@ -46,9 +46,10 @@ bool wr_extern_types(FILE *fd, fzl_extern_type_t *types, size_t types_count) for (size_t i = 0; i < types_count; i++) { fzl_extern_type_t *type = &types[i]; + fwrite(&type->lib_slot, sizeof(uint16_t), 1, fd); fwrite(&type->kind, sizeof(uint8_t), 1, fd); - size_t name_sz = strlen(type->name); + uint16_t name_sz = strlen(type->name); fwrite(&name_sz, sizeof(uint16_t), 1, fd); fwrite(type->name, sizeof(char), name_sz, fd); @@ -57,8 +58,7 @@ bool wr_extern_types(FILE *fd, fzl_extern_type_t *types, size_t types_count) if (!write_struct(fd, type)) return false; } - else if (type->kind == FZL_EXTERN_KIND_REF || - type->kind == FZL_EXTERN_KIND_ALIAS) + else if (type->kind == FZL_EXTERN_KIND_REF) { fwrite(&type->ref_target.is_primitive, sizeof(uint8_t), 1, fd); if (type->ref_target.is_primitive) @@ -66,6 +66,14 @@ bool wr_extern_types(FILE *fd, fzl_extern_type_t *types, size_t types_count) else fwrite(&type->ref_target.type_slot, sizeof(uint16_t), 1, fd); } + else if (type->kind == FZL_EXTERN_KIND_ALIAS) + { + fwrite(&type->alias_target.is_primitive, sizeof(uint8_t), 1, fd); + if (type->alias_target.is_primitive) + fwrite(&type->alias_target.primitive, sizeof(uint8_t), 1, fd); + else + fwrite(&type->alias_target.type_slot, sizeof(uint16_t), 1, fd); + } } return true; diff --git a/src/vm/fzl_vm.c b/src/vm/fzl_vm.c index 5bcf0d5..e7fe3c4 100644 --- a/src/vm/fzl_vm.c +++ b/src/vm/fzl_vm.c @@ -18,7 +18,7 @@ char last_err[256]; size_t last_jmp_ip = 0; size_t total_jmp_on_same = 0; -static char *expect_hdr = "FZL\0\1"; +static char *expect_hdr = "FZL\0\2"; fzl_vm_t *fzl_mkvm(const char *filepath) { diff --git a/src/vm/fzl_vm_reader.c b/src/vm/fzl_vm_reader.c new file mode 100644 index 0000000..b093b2c --- /dev/null +++ b/src/vm/fzl_vm_reader.c @@ -0,0 +1,147 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char reader_err[256]; + +fzl_vm_reader_t *fzl_vm_mkreader(char *filepath) +{ + if (!filepath) + { + snprintf(reader_err, sizeof(reader_err), + "init error - filepath is not defined"); + return NULL; + } + + fzl_vm_reader_t *reader = malloc(sizeof(fzl_vm_reader_t)); + if (!reader) + { + snprintf(reader_err, sizeof(reader_err), + "init error - couldn't allocate memory for reader"); + return NULL; + } + + memset(reader, 0, sizeof(fzl_vm_reader_t)); + reader->filepath = filepath; + + return reader; +} + +void wr_validation_err(char *section) +{ + snprintf(reader_err, sizeof(reader_err), + "init error - section '%s' is not valid", section); +} + +bool fzl_vm_read(fzl_vm_reader_t *reader) +{ + if (!reader) + { + snprintf(reader_err, sizeof(reader_err), + "init error - reader is not provided"); + return false; + } + + FILE *fd = fopen(reader->filepath, "rb"); + if (!fd) + { + snprintf(reader_err, sizeof(reader_err), + "init error - couldn't open file '%s'", reader->filepath); + return false; + } + + bool result = vm_rd_header(reader, fd); + if (!result) + { + wr_validation_err("header"); + fclose(fd); + return false; + } + + result = vm_rd_sections_sz(reader, fd); + if (!result) + { + wr_validation_err("sizes"); + fclose(fd); + return false; + } + + result = vm_rd_consts(reader, fd); + if (!result) + { + wr_validation_err("constants table"); + fclose(fd); + return false; + } + + result = vm_rd_extern_libs(reader, fd); + if (!result) + { + wr_validation_err("extern libs"); + fclose(fd); + return false; + } + + result = vm_rd_extern_types(reader, fd); + if (!result) + { + wr_validation_err("extern types"); + fclose(fd); + return false; + } + + result = vm_rd_extern_fns(reader, fd); + if (!result) + { + wr_validation_err("extern functions"); + fclose(fd); + return false; + } + + result = vm_rd_ops(reader, fd); + if (!result) + { + wr_validation_err("opcodes"); + fclose(fd); + return false; + } + + return true; +} + +char *fzl_vm_getreadererror() { return reader_err; } + +void fzl_vm_freereader(fzl_vm_reader_t *reader) +{ + if (!reader) + return; + + if (reader->consts) + { + for (uint16_t i = 0; i < reader->consts_count; i++) + if (reader->consts[i].as.s.value) + free((void *)reader->consts[i].as.s.value); + + free(reader->consts); + } + + if (reader->extern_libs) + free(reader->extern_libs); + + if (reader->extern_types) + free(reader->extern_types); + + if (reader->extern_fns) + free(reader->extern_fns); + + if (reader->ops) + free(reader->ops); + + free(reader); +} diff --git a/src/vm/reader/utility.c b/src/vm/reader/utility.c new file mode 100644 index 0000000..0054bac --- /dev/null +++ b/src/vm/reader/utility.c @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include + +// Read characters with red bytes check +bool vm_rd_char(FILE *fd, size_t count, char *o) +{ + if (!fd || !o) + return false; + + return fread(o, sizeof(char), count, fd) == (sizeof(char) * count); +} + +// Read 16-byte uints with red bytes check +bool vm_rd_u16(FILE *fd, size_t count, uint16_t *o) +{ + if (!fd || !o) + return false; + + return fread(o, sizeof(uint16_t), count, fd) == (sizeof(uint16_t) * count); +} + +// Read machine word-size uints with red bytes check +bool vm_rd_word(FILE *fd, size_t count, size_t *o) +{ + if (!fd || !o) + return false; + + return fread(o, sizeof(size_t), count, fd) == (sizeof(size_t) * count); +} diff --git a/src/vm/reader/vm_rd_consts.c b/src/vm/reader/vm_rd_consts.c new file mode 100644 index 0000000..593f7e5 --- /dev/null +++ b/src/vm/reader/vm_rd_consts.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +// Read constants table +bool vm_rd_consts(fzl_vm_reader_t *reader, FILE *fd) +{ + if (!reader || !fd) + return false; + + size_t consts_count = reader->consts_count; + if (!consts_count) + return true; + + reader->consts = malloc(sizeof(fzl_value_t) * consts_count); + if (!reader->consts) + return false; + + memset(reader->consts, 0, sizeof(fzl_value_t) * consts_count); + + bool ok = false; + uint16_t const_size; + char contents[512]; + for (uint16_t i = 0; i < reader->consts_count; i++) + { + memset(contents, 0, sizeof(contents)); + + ok = vm_rd_u16(fd, 1, &const_size); + if (!ok || const_size < 1) + return false; + + ok = vm_rd_char(fd, const_size, contents); + if (!ok) + return false; + + char *val = malloc(sizeof(char) * const_size); + if (!val) + return false; + + memset(val, 0, sizeof(char) * const_size); + memcpy(val, contents, sizeof(char) * const_size); + reader->consts[i] = (fzl_value_t){ + .type = FZL_TYPE_STRING, .as.s.value = val, .as.s.length = const_size}; + } + + return true; +} diff --git a/src/vm/reader/vm_rd_extern_fns.c b/src/vm/reader/vm_rd_extern_fns.c new file mode 100644 index 0000000..1975eec --- /dev/null +++ b/src/vm/reader/vm_rd_extern_fns.c @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include + +// Read external functions definitions +bool vm_rd_extern_fns(fzl_vm_reader_t *reader, FILE *fd) +{ + if (!reader || !fd) + return false; + + uint16_t fns_count = reader->extern_fns_count; + if (!fns_count) + return true; + + // TODO: fuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuck + + return true; +} diff --git a/src/vm/reader/vm_rd_extern_libs.c b/src/vm/reader/vm_rd_extern_libs.c new file mode 100644 index 0000000..d924c39 --- /dev/null +++ b/src/vm/reader/vm_rd_extern_libs.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include +#include + +// Read external lib names +bool vm_rd_extern_libs(fzl_vm_reader_t *reader, FILE *fd) +{ + if (!reader || !fd) + return false; + + uint16_t libs_count = reader->extern_libs_count; + if (!libs_count) + return true; + + reader->extern_libs = malloc(sizeof(char *) * libs_count); + if (!reader->extern_libs) + return false; + + memset(reader->extern_libs, 0, sizeof(char *) * libs_count); + + bool ok = false; + uint16_t name_sz = 0; + char name[256]; + for (uint16_t i = 0; i < libs_count; i++) + { + memset(name, 0, sizeof(name)); + + ok = vm_rd_u16(fd, 1, &name_sz); + if (!ok || name_sz < 1) + return false; + + ok = vm_rd_char(fd, name_sz, name); + if (!ok) + return false; + + char *val = malloc(sizeof(char) * name_sz); + if (!val) + return false; + + memset(val, 0, sizeof(char) * name_sz); + memcpy(val, name, sizeof(char) * name_sz); + reader->extern_libs[i] = val; + } + + return true; +} diff --git a/src/vm/reader/vm_rd_extern_types.c b/src/vm/reader/vm_rd_extern_types.c new file mode 100644 index 0000000..ca644a7 --- /dev/null +++ b/src/vm/reader/vm_rd_extern_types.c @@ -0,0 +1,156 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Read external types +bool vm_rd_extern_types(fzl_vm_reader_t *reader, FILE *fd) +{ + if (!reader || !fd) + return false; + + uint16_t types_count = reader->extern_types_count; + if (!types_count) + return true; + + reader->extern_types = malloc(sizeof(fzl_extern_type_t) * types_count); + if (!reader->extern_types) + return false; + + memset(reader->extern_types, 0, sizeof(fzl_extern_type_t) * types_count); + + char namebuf[256]; + for (uint16_t i = 0; i < types_count; i++) + { + fzl_extern_type_t *type = &reader->extern_types[i]; + + uint16_t lib_slot; + if (!vm_rd_u16(fd, 1, &lib_slot)) + return false; + type->lib_slot = lib_slot; + + char kind; + if (!vm_rd_char(fd, 1, &kind)) + return false; + type->kind = (fzl_extern_kind_t)kind; + + uint16_t name_sz; + if (!vm_rd_u16(fd, 1, &name_sz) || name_sz < 1) + return false; + + if (!vm_rd_char(fd, name_sz, namebuf)) + return false; + + char *val = malloc(sizeof(char) * name_sz); + if (!val) + return false; + type->name = val; + + memset(val, 0, sizeof(char) * name_sz); + memcpy(val, namebuf, sizeof(char) * name_sz); + + if (type->kind == FZL_EXTERN_KIND_HANDLE) + continue; + + if (type->kind == FZL_EXTERN_KIND_ALIAS || + type->kind == FZL_EXTERN_KIND_REF) + { + fzl_extern_type_ref_t *typeref = type->kind == FZL_EXTERN_KIND_ALIAS + ? &type->alias_target + : &type->ref_target; + + char is_primitive; + if (!vm_rd_char(fd, 1, &is_primitive)) + return false; + typeref->is_primitive = (bool)is_primitive; + + if ((bool)is_primitive) + { + char primitive; + if (!vm_rd_char(fd, 1, &primitive)) + return false; + typeref->primitive = (fzl_type_t)primitive; + } + else + { + uint16_t slot; + if (!vm_rd_u16(fd, 1, &slot)) + return false; + typeref->type_slot = slot; + } + + continue; + } + + if (type->kind != FZL_EXTERN_KIND_STRUCT) + return false; + + uint16_t fields_count; + if (!vm_rd_u16(fd, 1, &fields_count)) + return false; + + fzl_extern_struct_field_t *fields = + malloc(sizeof(fzl_extern_struct_field_t) * fields_count); + if (!fields) + return false; + + memset(fields, 0, sizeof(fzl_extern_struct_field_t) * fields_count); + type->structure.fields = fields; + type->structure.fields_count = fields_count; + + for (uint16_t j = 0; j < fields_count; j++) + { + fzl_extern_struct_field_t *f = &fields[i]; + + char has_name; + if (!vm_rd_char(fd, 1, &has_name)) + return false; + + if (has_name) + { + memset(namebuf, 0, sizeof(namebuf)); + if (!vm_rd_u16(fd, 1, &name_sz)) + return false; + + if (!vm_rd_char(fd, name_sz, namebuf)) + return false; + + char *fval = malloc(sizeof(char) * name_sz); + if (!fval) + return false; + + memset(fval, 0, sizeof(char) * name_sz); + memcpy(fval, namebuf, sizeof(char) * name_sz); + + f->name = fval; + } + + char is_primitive; + if (!vm_rd_char(fd, 1, &is_primitive)) + return false; + f->type.is_primitive = (bool)is_primitive; + + if (f->type.is_primitive) + { + char ftype; + if (!vm_rd_char(fd, 1, &ftype)) + return false; + f->type.primitive = (fzl_type_t)ftype; + } + else + { + uint16_t fslot; + if (!vm_rd_u16(fd, 1, &fslot)) + return false; + f->type.type_slot = fslot; + } + } + } + + return true; +} diff --git a/src/vm/reader/vm_rd_header.c b/src/vm/reader/vm_rd_header.c new file mode 100644 index 0000000..bf14fe7 --- /dev/null +++ b/src/vm/reader/vm_rd_header.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include + +size_t hdr_size = 5; +char hdr[5] = {'F', 'Z', 'L', 0, 2}; + +// Reader file header +bool vm_rd_header(fzl_vm_reader_t *reader, FILE *fd) +{ + if (!reader || !fd) + return false; + + char chkbuf[5]; + bool ok = vm_rd_char(fd, 5, chkbuf); + if (!ok) + return false; + + if (memcmp(chkbuf, hdr, hdr_size) != 0) + return false; + + return true; +} diff --git a/src/vm/reader/vm_rd_sections_sz.c b/src/vm/reader/vm_rd_sections_sz.c new file mode 100644 index 0000000..5fbe407 --- /dev/null +++ b/src/vm/reader/vm_rd_sections_sz.c @@ -0,0 +1,29 @@ +#include +#include +#include + +// Reader sizes section +bool vm_rd_sections_sz(fzl_vm_reader_t *reader, FILE *fd) +{ + if (!reader || !fd) + return false; + + bool ok = vm_rd_u16(fd, 1, (uint16_t *)&reader->consts_count); + if (!ok) + return false; + + ok = vm_rd_u16(fd, 1, &reader->extern_libs_count); + if (!ok) + return false; + + ok = vm_rd_u16(fd, 1, &reader->extern_types_count); + if (!ok) + return false; + + ok = vm_rd_u16(fd, 1, &reader->extern_fns_count); + if (!ok) + return false; + + ok = vm_rd_word(fd, 1, &reader->ops_count); + return ok; +} diff --git a/tests/libs/math.c b/tests/libs/math.c new file mode 100644 index 0000000..da318ea --- /dev/null +++ b/tests/libs/math.c @@ -0,0 +1 @@ +int double_num(int num) { return num * 2; }