diff --git a/.clang-tidy b/.clang-tidy index 62a8b405..3828ba0b 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,5 +1,5 @@ Checks: - - 'performance*,portability*,bugprone*,clang-analyzer*,cert*,concurrency*,readability-identifier-naming' + - 'performance*,portability*,bugprone*,clang-analyzer*,cert*,concurrency*,readability*' - '-bugprone-easily-swappable-parameters' - '-performance-no-int-to-ptr' CheckOptions: @@ -21,4 +21,7 @@ CheckOptions: - { key: readability-identifier-naming.GlobalConstantPrefix, value: k } - { key: readability-identifier-naming.GlobalVariableCase, value: CamelCase } - { key: readability-identifier-naming.GlobalVariablePrefix, value: g } -HeaderFilterRegex: '' + - { key: readability-identifier-naming.ScopedEnumConstantCase, value: CamelCase } + - { key: readability-identifier-naming.ScopedEnumConstantPrefix, value: '' } +HeaderFileExtensions: ['h'] +ImplementationFileExtensions: ['c'] diff --git a/src/common/os/include/os/impl/posix.h b/src/common/os/include/os/impl/posix.h index 8787a814..6d20d7dd 100644 --- a/src/common/os/include/os/impl/posix.h +++ b/src/common/os/include/os/impl/posix.h @@ -26,6 +26,8 @@ enum { #define CT_OS_INVALID_FILE NULL #define CT_OS_INVALID_LIBRARY NULL + +// TODO: this may actually be UINTPTR_MAX #define CT_OS_INVALID_MAPPING NULL #define CT_OS_INVALID_ITER NULL diff --git a/src/common/os/include/os/os.h b/src/common/os/include/os/os.h index d52c3676..e40d0b46 100644 --- a/src/common/os/include/os/os.h +++ b/src/common/os/include/os/os.h @@ -303,7 +303,7 @@ CT_OS_API os_error_t os_file_tell(INOUT_NOTNULL os_file_t *file, OUT_NOTNULL siz /// /// @return an error if the operation could not be performed RET_INSPECT -CT_OS_API os_error_t os_file_expand(INOUT_NOTNULL os_file_t *file, size_t size); +CT_OS_API os_error_t os_file_resize(INOUT_NOTNULL os_file_t *file, size_t size); /// @brief map a file into memory /// @@ -332,7 +332,7 @@ CT_OS_API os_error_t os_unmap(INOUT_NOTNULL os_mapping_t *mapping); /// /// @return the data of the mapping CT_NODISCARD -CT_OS_API void *os_mapping_data(INOUT_NOTNULL os_mapping_t *mapping); +CT_OS_API void *os_mapping_data(IN_NOTNULL os_mapping_t *mapping); /// @brief get the size of a file mapping /// @@ -340,7 +340,7 @@ CT_OS_API void *os_mapping_data(INOUT_NOTNULL os_mapping_t *mapping); /// /// @return the size of the mapping CT_NODISCARD -CT_OS_API size_t os_mapping_size(INOUT_NOTNULL const os_mapping_t *mapping); +CT_OS_API size_t os_mapping_size(IN_NOTNULL const os_mapping_t *mapping); /// @brief does the mapping object contain a valid mapping /// checks if the mapping data exists, not for the validity of the mapping @@ -349,7 +349,7 @@ CT_OS_API size_t os_mapping_size(INOUT_NOTNULL const os_mapping_t *mapping); /// /// @return true if the mapping is valid CT_NODISCARD -CT_OS_API bool os_mapping_active(INOUT_NOTNULL const os_mapping_t *mapping); +CT_OS_API bool os_mapping_active(IN_NOTNULL const os_mapping_t *mapping); /// @brief get the name of a file /// @@ -357,7 +357,7 @@ CT_OS_API bool os_mapping_active(INOUT_NOTNULL const os_mapping_t *mapping); /// /// @return the name of the file CT_NODISCARD -CT_OS_API const char *os_file_name(INOUT_NOTNULL const os_file_t *file); +CT_OS_API const char *os_file_name(IN_NOTNULL const os_file_t *file); /// @} diff --git a/src/common/os/src/posix/file.c b/src/common/os/src/posix/file.c index d618abcc..c05527f2 100644 --- a/src/common/os/src/posix/file.c +++ b/src/common/os/src/posix/file.c @@ -142,7 +142,7 @@ os_error_t os_file_size(os_file_t *file, size_t *actual) } USE_DECL -os_error_t os_file_expand(os_file_t *file, size_t size) +os_error_t os_file_resize(os_file_t *file, size_t size) { CTASSERT(file != NULL); diff --git a/src/common/os/src/windows/error.c b/src/common/os/src/windows/error.c index e1f2518b..ebf921fa 100644 --- a/src/common/os/src/windows/error.c +++ b/src/common/os/src/windows/error.c @@ -38,7 +38,7 @@ size_t os_error_get_string(os_error_t error, char *buffer, size_t size) // TODO: is there a way of asking for the size of the buffer at all? // this is what .NET core does, but its not clear if this is correct. - return 0x1000 * sizeof(TCHAR); + return 512 * sizeof(TCHAR); } CTASSERT(buffer != NULL); diff --git a/src/common/os/src/windows/file.c b/src/common/os/src/windows/file.c index a844e777..2c4a0bfc 100644 --- a/src/common/os/src/windows/file.c +++ b/src/common/os/src/windows/file.c @@ -213,7 +213,7 @@ os_error_t os_file_tell(os_file_t *file, size_t *actual) } USE_DECL -os_error_t os_file_expand(os_file_t *file, size_t size) +os_error_t os_file_resize(os_file_t *file, size_t size) { CTASSERT(file != NULL); diff --git a/src/support/json/include/json/json.h b/src/support/json/include/json/json.h index f501ea30..3cf6b81b 100644 --- a/src/support/json/include/json/json.h +++ b/src/support/json/include/json/json.h @@ -92,6 +92,16 @@ CT_JSON_API json_t *json_map_get(IN_NOTNULL const json_t *json, IN_NOTNULL const RET_INSPECT CT_JSON_API json_t *json_array_get(IN_NOTNULL const json_t *json, size_t index); +/// @brief query a json object +/// +/// @param json the object to query +/// @param query the query to perform +/// @param arena the arena to use +/// +/// @return the result of the query +RET_INSPECT +CT_JSON_API json_t *json_query(IN_NOTNULL json_t *json, IN_NOTNULL const char *query, IN_NOTNULL arena_t *arena); + /// @brief scan an io into a json value /// scan the contents of an io object into a json value /// @note if the scan fails, the logger will contain error information diff --git a/src/support/json/meson.build b/src/support/json/meson.build index 5f62d3b5..624d1636 100644 --- a/src/support/json/meson.build +++ b/src/support/json/meson.build @@ -5,7 +5,7 @@ json_api = configure_file( ) json_include = include_directories('.', 'include') -json_impl_include = include_directories('src') +json_impl_include = include_directories('src', 'src/query') json_args = [ '-DCT_JSON_BUILD=1', generated_args ] src = [ @@ -13,7 +13,13 @@ src = [ 'src/ast.c', 'src/scan.c', lex.process('src/json.l'), - parse.process('src/json.y') + parse.process('src/json.y'), + + 'src/query/query.c', + 'src/query/query_ast.c', + 'src/query/query_scan.c', + lex.process('src/query/query.l'), + parse.process('src/query/query.y'), ] if has_cpp @@ -25,7 +31,7 @@ libjson = library('json', src, install : not meson.is_subproject(), c_args : json_args, cpp_args : json_args, - dependencies : [ core, base, scan, interop, gmp, std, notify, events, util ], + dependencies : [ core, base, scan, interop, gmp, std, notify, events, util, io ], include_directories : [ json_include, json_impl_include ], override_options : [ 'unity=off' ] ) diff --git a/src/support/json/src/query/query.c b/src/support/json/src/query/query.c new file mode 100644 index 00000000..2ea70dc8 --- /dev/null +++ b/src/support/json/src/query/query.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +#include "json/json.h" + +#include "arena/arena.h" +#include "interop/compile.h" + +#include "io/io.h" +#include "notify/notify.h" +#include "base/panic.h" + +#include "query_scan.h" + +#include "query_bison.h" // IWYU pragma: keep +#include "query_flex.h" // IWYU pragma: keep +#include "std/typed/vector.h" +#include "std/vector.h" + +CT_CALLBACKS(kQueryCallbacks, query); + +static json_t *eval_query(json_t *json, const query_ast_t *query, arena_t *arena) +{ + if (json == NULL) + return NULL; + + switch (query->kind) + { + case eQueryObject: + return json; + case eQueryField: + { + json_t *object = eval_query(json, query->object, arena); + if (!object) + return NULL; + + if (object->kind != eJsonObject) + return NULL; + + return json_map_get(object, arena_strndup(query->field.text, query->field.length, arena)); + } + case eQueryIndex: + { + json_t *object = eval_query(json, query->object, arena); + if (!object) + return NULL; + + if (object->kind != eJsonArray) + return NULL; + + int idx = mpz_get_si(query->index); + if (idx < 0) + return NULL; + + if (idx >= vector_len(object->array)) + return NULL; + + return vector_get(object->array, idx); + } + case eQueryMap: + { + json_t *object = eval_query(json, query->object, arena); + if (!object) + return NULL; + + if (object->kind != eJsonObject) + return NULL; + + return json_map_get(object, arena_strndup(query->field.text, query->field.length, arena)); + } + + default: + CT_NEVER("invalid query kind %d", query->kind); + } +} + +USE_DECL +json_t *json_query(json_t *json, const char *query, arena_t *arena) +{ + CTASSERT(json != NULL); + CTASSERT(query != NULL); + CTASSERT(arena != NULL); + + io_t *io = io_string("query", query, arena); + + logger_t *logger = logger_new(arena); + + query_scan_t ctx = { + .reports = logger, + }; + + scan_t *scan = scan_io("json query", io, arena); + scan_set_context(scan, &ctx); + + parse_result_t result = scan_buffer(scan, &kQueryCallbacks); + + if (result.result != eParseOk) + { + typevec_t *messages = logger_get_events(logger); + for (size_t i = 0; i < typevec_len(messages); i++) + { + event_t *event = typevec_offset(messages, i); + printf("error: %s\n", event->message); + } + return NULL; + } + + return eval_query(json, result.tree, arena); +} diff --git a/src/support/json/src/query/query.l b/src/support/json/src/query/query.l new file mode 100644 index 00000000..4568849f --- /dev/null +++ b/src/support/json/src/query/query.l @@ -0,0 +1,42 @@ +%option extra-type="scan_t*" +%option 8bit nodefault +%option noyywrap noinput nounput +%option noyyalloc noyyrealloc noyyfree +%option reentrant bison-bridge bison-locations +%option never-interactive batch +%option prefix="query" + +%{ +#include "query_bison.h" +#include "interop/flex.h" +#include "interop/memory.h" +#include "cthulhu/events/events.h" +%} + +WS [ \t\r\v\n\f] +ESCAPES (\\(['"\?\\abfnrtv]|[0-7]{1,3}|x[a-fA-F0-9]+)) + +%% + +{WS}+ ; + +"[" { return LBRACKET; } +"]" { return RBRACKET; } +"." { return DOT; } + +[-]"0"[0-7]* { query_parse_integer(yylval->integer, yyextra, *yylloc, yytext, 8); return INTEGER; } +[-]"0"[bB][01]+ { query_parse_integer(yylval->integer, yyextra, *yylloc, yytext + 2, 2); return INTEGER; } +[-]"0"[xX][0-9a-fA-F]+ { query_parse_integer(yylval->integer, yyextra, *yylloc, yytext + 2, 16); return INTEGER; } +[-]?[0-9]+ { query_parse_integer(yylval->integer, yyextra, *yylloc, yytext, 10); return INTEGER; } + +[a-zA-Z_][a-zA-Z0-9_]* { query_parse_string(&yylval->string, yyextra, *yylloc, yytext, yyleng); return IDENT; } +\"([^"\\\n]|{ESCAPES})*\" { query_parse_string(&yylval->string, yyextra, *yylloc, yytext + 1, yyleng - 2); return STRING; } + +. { + query_scan_t *scan = query_scan_context(yyextra); + evt_scan_unknown(scan->reports, node_new(yyextra, *yylloc), yytext); +} + +%% + +FLEX_MEMORY(query) diff --git a/src/support/json/src/query/query.y b/src/support/json/src/query/query.y new file mode 100644 index 00000000..8d598017 --- /dev/null +++ b/src/support/json/src/query/query.y @@ -0,0 +1,66 @@ +%define parse.error verbose +%define api.pure full +%lex-param { void *scan } +%parse-param { void *scan } { scan_t *x } +%locations +%expect 0 +%define api.prefix {query} + +%code top { + #include "interop/flex.h" + #include "interop/bison.h" + + #include "std/typed/vector.h" + #include "std/vector.h" +} + +%code requires { + #include "query_ast.h" + #include "query_scan.h" + #define YYSTYPE QUERYSTYPE + #define YYLTYPE QUERYLTYPE +} + +%{ +int querylex(void *lval, void *loc, scan_t *scan); +void queryerror(where_t *where, void *state, scan_t *scan, const char *msg); +%} + +%union { + mpz_t integer; + text_t string; + + query_ast_t *ast; +} + +%token + LBRACKET "[" + RBRACKET "]" + DOT "." + +%token + STRING + +%token + INTEGER + +%token + IDENT + +%type + root expr + +%start root + +%% + +root: expr { scan_set(x, $1); } + ; + +expr: IDENT { $$ = query_ast_object(x, $1); } + | expr DOT IDENT { $$ = query_ast_field(x, $1, $3); } + | expr LBRACKET INTEGER RBRACKET { $$ = query_ast_index(x, $1, $3); } + | expr LBRACKET STRING RBRACKET { $$ = query_ast_map(x, $1, $3); } + ; + +%% diff --git a/src/support/json/src/query/query_ast.c b/src/support/json/src/query/query_ast.c new file mode 100644 index 00000000..b15c07e5 --- /dev/null +++ b/src/support/json/src/query/query_ast.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +#include "query_ast.h" + +#include "arena/arena.h" +#include "base/panic.h" +#include "scan/scan.h" + +static query_ast_t *query_ast_new(scan_t *scan, query_ast_type_t type) +{ + CTASSERT(scan != NULL); + + arena_t *arena = scan_get_arena(scan); + query_ast_t *ast = ARENA_MALLOC(sizeof(query_ast_t), "query", scan, arena); + ast->kind = type; + + return ast; +} + +query_ast_t *query_ast_object(scan_t *scan, text_t name) +{ + query_ast_t *ast = query_ast_new(scan, eQueryObject); + ast->name = name; + return ast; +} + +query_ast_t *query_ast_field(scan_t *scan, query_ast_t *object, text_t field) +{ + query_ast_t *ast = query_ast_new(scan, eQueryField); + ast->object = object; + ast->field = field; + return ast; +} + +query_ast_t *query_ast_index(scan_t *scan, query_ast_t *object, mpz_t index) +{ + query_ast_t *ast = query_ast_new(scan, eQueryIndex); + ast->object = object; + mpz_init_set(ast->index, index); + return ast; +} + +query_ast_t *query_ast_map(scan_t *scan, query_ast_t *object, text_t field) +{ + query_ast_t *ast = query_ast_new(scan, eQueryMap); + ast->object = object; + ast->field = field; + return ast; +} diff --git a/src/support/json/src/query/query_ast.h b/src/support/json/src/query/query_ast.h new file mode 100644 index 00000000..0b821d6c --- /dev/null +++ b/src/support/json/src/query/query_ast.h @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once +#include + +#include "core/text.h" + +#include + +typedef struct scan_t scan_t; +typedef struct node_t node_t; +typedef struct query_ast_t query_ast_t; + +typedef enum query_ast_type_t +{ + eQueryObject, // object + eQueryField, // field.name + eQueryMap, // field["name"] + eQueryIndex, // field[0] + + eQueryCount +} query_ast_type_t; + +typedef struct query_ast_t +{ + query_ast_type_t kind; + + union { + struct { + query_ast_t *object; + union { + /* eQueryField, eQueryMap */ + text_t field; + + /* eQueryIndex */ + mpz_t index; + }; + }; + + /* eQueryObject */ + text_t name; + }; +} query_ast_t; + +CT_BEGIN_API + +query_ast_t *query_ast_object(scan_t *scan, text_t name); +query_ast_t *query_ast_field(scan_t *scan, query_ast_t *object, text_t field); +query_ast_t *query_ast_map(scan_t *scan, query_ast_t *object, text_t field); +query_ast_t *query_ast_index(scan_t *scan, query_ast_t *object, mpz_t index); diff --git a/src/support/json/src/query/query_scan.c b/src/support/json/src/query/query_scan.c new file mode 100644 index 00000000..b33a8ac7 --- /dev/null +++ b/src/support/json/src/query/query_scan.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +#include "query_scan.h" + +#include "core/macros.h" +#include "cthulhu/events/events.h" +#include "cthulhu/util/text.h" + +query_scan_t *query_scan_context(scan_t *scan) +{ + return (query_scan_t *)scan_get_context(scan); +} + +void query_parse_integer(mpz_t integer, scan_t *scan, where_t where, const char *text, int base) +{ + int result = mpz_init_set_str(integer, text, base); + if (result != 0) + { + query_scan_t *ctx = query_scan_context(scan); + msg_notify(ctx->reports, &kEvent_InvalidIntegerLiteral, node_new(scan, where), "invalid integer literal"); + } +} + +void query_parse_string(text_t *string, scan_t *scan, where_t where, const char *text, size_t length) +{ + query_scan_t *ctx = query_scan_context(scan); + arena_t *arena = scan_get_arena(scan); + *string = util_text_escape(ctx->reports, node_new(scan, where), text, length, arena); +} + +void queryerror(where_t *where, void *state, scan_t *scan, const char *msg) +{ + CT_UNUSED(state); + + query_scan_t *ctx = query_scan_context(scan); + + evt_scan_error(ctx->reports, node_new(scan, *where), msg); +} diff --git a/src/support/json/src/query/query_scan.h b/src/support/json/src/query/query_scan.h new file mode 100644 index 00000000..e14a9c63 --- /dev/null +++ b/src/support/json/src/query/query_scan.h @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +#pragma once + +#include "scan/node.h" // IWYU pragma: export + +#include + +#define QUERYLTYPE where_t + +typedef struct logger_t logger_t; + +typedef struct query_scan_t +{ + logger_t *reports; +} query_scan_t; + +query_scan_t *query_scan_context(scan_t *scan); + +void query_parse_integer(mpz_t integer, scan_t *scan, where_t where, const char *text, int base); +void query_parse_string(text_t *string, scan_t *scan, where_t where, const char *text, size_t length); diff --git a/src/target/cfamily/meson.build b/src/target/cfamily/meson.build index 3af3451f..7bb2a945 100644 --- a/src/target/cfamily/meson.build +++ b/src/target/cfamily/meson.build @@ -7,12 +7,15 @@ libtarget_cfamily = static_library('target_cfamily', src, build_by_default : not meson.is_subproject(), install : not meson.is_subproject(), include_directories : 'include', - dependencies : [ target, fs, io, events ] + dependencies : [ target, fs, io, events, std, tree ] ) targets += { 'cfamily': { 'dep': declare_dependency(link_with : libtarget_cfamily), - 'mod': 'kTargetC' + 'mod': 'kTargetC', + + 'static': libtarget_cfamily, + 'module': 'kTargetC' } } diff --git a/src/target/cfamily/src/tree.c b/src/target/cfamily/src/tree.c index a6e8c165..5a9adf5a 100644 --- a/src/target/cfamily/src/tree.c +++ b/src/target/cfamily/src/tree.c @@ -3,35 +3,78 @@ #include "core/macros.h" #include "cthulhu/broker/broker.h" +#include "cthulhu/tree/query.h" #include "fs/fs.h" #include "io/io.h" #include "os/os.h" #include "base/panic.h" #include "cthulhu/events/events.h" +#include "std/map.h" +#include "std/vector.h" + typedef struct cfamily_emit_t { - vector_t *names; + vector_t *stack; + + map_t *names; // map of tree_t* -> mangled name + + io_t *src; + io_t *hdr; } cfamily_emit_t; -static void cfamily_pair(target_runtime_t *runtime, const tree_t *tree, fs_t *fs) +static void push_namespace(cfamily_emit_t *emit, const tree_t *tree) +{ + vector_push(&emit->stack, (void*)tree_get_name(tree)); +} + +static void pop_namespace(cfamily_emit_t *emit) +{ + vector_drop(emit->stack); +} + +static void forward_decl(cfamily_emit_t *emit, const tree_t *tree) +{ + switch (tree_get_kind(tree)) + { + case eTreeDeclModule: + { + push_namespace(emit, tree); + + map_t *vars = tree_module_tag(tree, eSemaTypes); + map_iter_t iter = map_iter(vars); + while (map_has_next(&iter)) + { + map_next(&iter); + } + + pop_namespace(emit); + break; + } + + default: + CT_NEVER("unsupported tree kind %s", tree_kind_to_string(tree_get_kind(tree))); + } +} + +static void cfamily_pair(target_runtime_t *runtime, const tree_t *tree, fs_t *dst) { CT_UNUSED(tree); os_error_t err = eOsSuccess; - io_t *src = fs_open(fs, "tree.c", eOsAccessWrite | eOsAccessTruncate); - io_t *hdr = fs_open(fs, "tree.h", eOsAccessWrite | eOsAccessTruncate); + io_t *src = fs_open(dst, "tree.c", eOsAccessWrite | eOsAccessTruncate); + io_t *hdr = fs_open(dst, "tree.h", eOsAccessWrite | eOsAccessTruncate); if ((err = io_error(src)) != eOsSuccess) { evt_os_error(runtime->logger, &kEvent_FailedToCreateOutputFile, err, "failed to create tree.c"); - return; + goto cleanup; } if ((err = io_error(hdr)) != eOsSuccess) { evt_os_error(runtime->logger, &kEvent_FailedToCreateOutputFile, err, "failed to create tree.h"); - return; + goto cleanup; } io_printf(src, "/* This file was generated, do not edit */\n\n"); @@ -40,6 +83,16 @@ static void cfamily_pair(target_runtime_t *runtime, const tree_t *tree, fs_t *fs io_printf(hdr, "/* This file was generated, do not edit */\n\n"); io_printf(hdr, "#pragma once\n\n"); + cfamily_emit_t emit = { + .stack = vector_new(4, runtime->arena), + .names = map_optimal(1024, kTypeInfoPtr, runtime->arena), // TODO: calculate optimal size + .src = src, + .hdr = hdr, + }; + + forward_decl(&emit, tree); + +cleanup: io_close(src); io_close(hdr); } diff --git a/src/target/debug/include/debug/target.h b/src/target/debug/include/debug/target.h index cb12d8c8..153b65e0 100644 --- a/src/target/debug/include/debug/target.h +++ b/src/target/debug/include/debug/target.h @@ -7,9 +7,11 @@ typedef struct target_runtime_t target_runtime_t; typedef struct ssa_result_t ssa_result_t; typedef struct target_emit_t target_emit_t; +typedef struct tree_t tree_t; CT_BEGIN_API void debug_ssa(target_runtime_t *runtime, const ssa_result_t *ssa, target_emit_t *emit); +void debug_tree(target_runtime_t *runtime, const tree_t *tree, target_emit_t *emit); CT_END_API diff --git a/src/target/debug/meson.build b/src/target/debug/meson.build index 84d836ba..445e0889 100644 --- a/src/target/debug/meson.build +++ b/src/target/debug/meson.build @@ -1,11 +1,11 @@ -src = [ 'src/target.c', 'src/ssa.c', 'src/common.c' ] +src = [ 'src/target.c', 'src/ssa.c', 'src/tree.c', 'src/common.c' ] deps = [ driver, events, std, broker, ssa, io, fs ] # TODO: once we can load targets as shared libraries # we need to do the same shared_module/static_library split # that languages do -debug_target = static_library('target_debug', src, +libtarget_debug = static_library('target_debug', src, build_by_default : not meson.is_subproject(), install : not meson.is_subproject(), include_directories : 'include', @@ -14,10 +14,10 @@ debug_target = static_library('target_debug', src, targets += { 'debug': { - 'dep': declare_dependency(link_with : debug_target), + 'dep': declare_dependency(link_with : libtarget_debug), 'mod': 'kTargetDebug', - 'static': debug_target, + 'static': libtarget_debug, 'module': 'kTargetDebug' } } diff --git a/src/target/debug/src/ssa.c b/src/target/debug/src/ssa.c index a5cc8c49..6d57a8a7 100644 --- a/src/target/debug/src/ssa.c +++ b/src/target/debug/src/ssa.c @@ -380,10 +380,6 @@ void debug_ssa(target_runtime_t *runtime, const ssa_result_t *ssa, target_emit_t CT_UNUSED(ssa); CT_UNUSED(config); - const node_t *node = broker_get_node(runtime->broker); - - msg_notify(runtime->logger, &kEvent_Unimplemented, node, "debug ssa not implemented"); - arena_t *arena = runtime->arena; ssa_emit_t emit = { .emit = { diff --git a/src/target/debug/src/target.c b/src/target/debug/src/target.c index b1142802..c48f59f8 100644 --- a/src/target/debug/src/target.c +++ b/src/target/debug/src/target.c @@ -12,11 +12,12 @@ CT_DRIVER_API const target_t kTargetDebug = { .license = "LGPLv3", .author = "Elliot Haisley", .desc = "Debug code generation target", - .version = CT_NEW_VERSION(0, 0, 1), + .version = CT_NEW_VERSION(0, 0, 2), }, }, - .fn_ssa = debug_ssa + .fn_tree = debug_tree, + .fn_ssa = debug_ssa, }; CT_TARGET_EXPORT(kTargetDebug) diff --git a/src/target/debug/src/tree.c b/src/target/debug/src/tree.c new file mode 100644 index 00000000..c5d357b4 --- /dev/null +++ b/src/target/debug/src/tree.c @@ -0,0 +1,147 @@ +#include "base/panic.h" +#include "cthulhu/events/events.h" +#include "cthulhu/tree/query.h" +#include "debug/target.h" + +#include "cthulhu/broker/broker.h" +#include "fs/fs.h" +#include "os/os.h" +#include "io/io.h" +#include "std/map.h" + +typedef struct dbg_emit_t +{ + io_t *io; + size_t indent; +} dbg_emit_t; + +static void dbg_emit(dbg_emit_t *emit, const char *fmt, ...) +{ + for (size_t i = 0; i < emit->indent; ++i) + io_write(emit->io, " ", 2); + + va_list args; + va_start(args, fmt); + io_vprintf(emit->io, fmt, args); + va_end(args); + + io_write(emit->io, "\n", 1); +} + +static void dbg_emit_tree(dbg_emit_t *emit, const tree_t *tree, const char *name) +{ + dbg_emit(emit, "%s: %s (%p)", name, tree_kind_to_string(tree_get_kind(tree)), tree); +} + +static void dbg_indent(dbg_emit_t *emit) +{ + emit->indent += 1; +} + +static void dbg_dedent(dbg_emit_t *emit) +{ + CTASSERTF(emit->indent > 0, "cannot dedent past 0"); + emit->indent -= 1; +} + +static void emit_tree(dbg_emit_t *dbg, const tree_t *tree); + +static void emit_storage(dbg_emit_t *dbg, tree_storage_t storage) +{ + dbg_emit(dbg, "type: %s", tree_to_string(storage.storage)); + dbg_emit(dbg, "length: %zu", storage.length); + dbg_emit(dbg, "qualifiers: %s", quals_name(storage.quals)); +} + +static void emit_section(dbg_emit_t *dbg, const char *name, map_t *section) +{ + dbg_emit(dbg, "%s:", name); + dbg_indent(dbg); + + map_iter_t iter = map_iter(section); + while (map_has_next(&iter)) + { + map_entry_t entry = map_next(&iter); + const tree_t *tree = entry.key; + emit_tree(dbg, tree); + } + + dbg_dedent(dbg); +} + +static void emit_module(dbg_emit_t *dbg, const tree_t *tree) +{ + dbg_emit_tree(dbg, tree, tree_get_name(tree)); + dbg_indent(dbg); + + emit_section(dbg, "types", tree_module_tag(tree, eSemaTypes)); + emit_section(dbg, "values", tree_module_tag(tree, eSemaValues)); + emit_section(dbg, "functions", tree_module_tag(tree, eSemaProcs)); + emit_section(dbg, "modules", tree_module_tag(tree, eSemaModules)); + + dbg_dedent(dbg); +} + +static void emit_global(dbg_emit_t *dbg, const tree_t *tree) +{ + dbg_emit_tree(dbg, tree, tree_get_name(tree)); + dbg_indent(dbg); + dbg_emit(dbg, "type:"); + dbg_indent(dbg); + emit_tree(dbg, tree_get_type(tree)); + dbg_dedent(dbg); + dbg_emit(dbg, "storage:"); + dbg_indent(dbg); + emit_storage(dbg, tree_get_storage(tree)); + dbg_dedent(dbg); + + dbg_emit(dbg, "value:"); + dbg_indent(dbg); + emit_tree(dbg, tree->initial); + dbg_dedent(dbg); + + dbg_dedent(dbg); +} + +static void emit_tree(dbg_emit_t *dbg, const tree_t *tree) +{ + if (tree == NULL) + { + dbg_emit(dbg, "NULL"); + return; + } + + tree_kind_t kind = tree_get_kind(tree); + switch (tree_get_kind(tree)) + { + case eTreeDeclModule: + emit_module(dbg, tree); + break; + case eTreeDeclGlobal: + emit_global(dbg, tree); + break; + default: + dbg_emit(dbg, "%s unimplemented (%p)", tree_kind_to_string(kind), tree); + break; + } +} + +void debug_tree(target_runtime_t *runtime, const tree_t *tree, target_emit_t *emit) +{ + io_t *io = fs_open(emit->fs, "debug.tree", eOsAccessWrite | eOsAccessTruncate); + os_error_t err = eOsSuccess; + if ((err = io_error(io)) != eOsSuccess) + { + evt_os_error(runtime->logger, &kEvent_FailedToCreateOutputFile, err, "failed to create debug.tree"); + return; + } + + dbg_emit_t dbg = { + .io = io, + .indent = 0, + }; + + emit_tree(&dbg, tree); + + io_close(io); +} diff --git a/src/target/hlsl/meson.build b/src/target/hlsl/meson.build index 493c9ede..d05ef8cc 100644 --- a/src/target/hlsl/meson.build +++ b/src/target/hlsl/meson.build @@ -10,6 +10,9 @@ libtarget_hlsl = static_library('target_hlsl', src, targets += { 'hlsl': { 'dep': declare_dependency(link_with : libtarget_hlsl), - 'mod': 'kTargetHlsl' + 'mod': 'kTargetHlsl', + + 'static': libtarget_hlsl, + 'module': 'kTargetHlsl' } } diff --git a/tests/unit/json/json.c b/tests/unit/json/json.c index 0ec09fc5..9f644165 100644 --- a/tests/unit/json/json.c +++ b/tests/unit/json/json.c @@ -58,5 +58,24 @@ int main(void) GROUP_EXPECT_PASS(group, "dependency_2", json_array_get(dependencies, 2)->kind == eJsonString); } + { + test_group_t group = test_group(&suite, "query"); + logger_t *logger = logger_new(arena); + io_t *io = io_string("test.json", kTestJson, arena); + json_t *json = json_scan(io, logger, arena); + + GROUP_EXPECT_PASS(group, "json", json != NULL); + + json_t *name = json_query(json, "root.name", arena); + GROUP_EXPECT_PASS(group, "name", name != NULL); + GROUP_EXPECT_PASS(group, "name_kind", name->kind == eJsonString); + GROUP_EXPECT_PASS(group, "name_value", ctu_strncmp(name->string.text, "ctu", name->string.length) == 0); + + json_t *dep1 = json_query(json, "root.dependencies[1]", arena); + GROUP_EXPECT_PASS(group, "dep1", dep1 != NULL); + GROUP_EXPECT_PASS(group, "dep_kind", dep1->kind == eJsonString); + GROUP_EXPECT_PASS(group, "dep_value", ctu_strncmp(dep1->string.text, "std", dep1->string.length) == 0); + } + return test_suite_finish(&suite); }