diff --git a/.vs/launch.vs.json b/.vs/launch.vs.json index e884e0734..73972d739 100644 --- a/.vs/launch.vs.json +++ b/.vs/launch.vs.json @@ -7,42 +7,58 @@ "project": "CMakeLists.txt", "projectTarget": "goalc-test.exe (bin\\goalc-test.exe)", "name": "Tests - Unit-Tests - Summary", - "args": ["--gtest_brief=1"] + "args": [ + "--gtest_brief=1" + ] }, { "type": "default", "project": "CMakeLists.txt", "projectTarget": "goalc-test.exe (bin\\goalc-test.exe)", "name": "Tests - Unit-Tests - Verbose", - "args": ["--gtest_brief=0"] + "args": [ + "--gtest_brief=0" + ] }, { "type": "default", "project": "CMakeLists.txt", "projectTarget": "goalc-test.exe (bin\\goalc-test.exe)", "name": "Tests - Draft Tests - Verbose", - "args": ["--gtest_brief=0", "--gtest_filter=\"*Draft*\""] + "args": [ + "--gtest_brief=0", + "--gtest_filter=\"*Draft*\"" + ] }, { "type": "default", "project": "CMakeLists.txt", "projectTarget": "goalc-test.exe (bin\\goalc-test.exe)", "name": "Tests - TypeConsistency - Verbose", - "args": ["--gtest_brief=0", "--gtest_filter=\"*TypeConsistency*\""] + "args": [ + "--gtest_brief=0", + "--gtest_filter=\"*TypeConsistency*\"" + ] }, { "type": "default", "project": "CMakeLists.txt", "projectTarget": "goalc-test.exe (bin\\goalc-test.exe)", "name": "Tests - TypeConsistency - Jak 2 - Verbose", - "args": ["--gtest_brief=0", "--gtest_filter=\"*Jak2TypeConsistency*\""] + "args": [ + "--gtest_brief=0", + "--gtest_filter=\"*Jak2TypeConsistency*\"" + ] }, { "type": "default", "project": "CMakeLists.txt", "projectTarget": "goalc-test.exe (bin\\goalc-test.exe)", "name": "Tests - WithGameTests - Verbose", - "args": ["--gtest_brief=0", "--gtest_filter=\"*WithGameTests*\""] + "args": [ + "--gtest_brief=0", + "--gtest_filter=\"*WithGameTests*\"" + ] }, { "type": "default", @@ -113,56 +129,109 @@ "project": "CMakeLists.txt", "projectTarget": "gk.exe (bin\\gk.exe)", "name": "Game - Jak 1 - Runtime", - "args": ["-v", "--game", "jak1", "--", "-fakeiso", "-debug"] + "args": [ + "-v", + "--game", + "jak1", + "--", + "-fakeiso", + "-debug" + ] }, { "type": "default", "project": "CMakeLists.txt", "projectTarget": "gk.exe (bin\\gk.exe)", "name": "Game - Jak 1 - Runtime (boot)", - "args": ["-v", "--game", "jak1", "--", "-boot", "-fakeiso", "-debug"] + "args": [ + "-v", + "--game", + "jak1", + "--", + "-boot", + "-fakeiso", + "-debug" + ] }, { "type": "default", "project": "CMakeLists.txt", "projectTarget": "gk.exe (bin\\gk.exe)", "name": "Game - Jak 2 - Runtime (no boot)", - "args": ["-v", "--game", "jak2", "--", "-fakeiso", "-debug"] + "args": [ + "-v", + "--game", + "jak2", + "--", + "-fakeiso", + "-debug" + ] }, { "type": "default", "project": "CMakeLists.txt", "projectTarget": "gk.exe (bin\\gk.exe)", "name": "Game - Jak 2 - Runtime (boot)", - "args": ["-v", "--game", "jak2", "--", "-boot", "-fakeiso", "-debug"] + "args": [ + "-v", + "--game", + "jak2", + "--", + "-boot", + "-fakeiso", + "-debug" + ] }, { "type": "default", "project": "CMakeLists.txt", "projectTarget": "gk.exe (bin\\gk.exe)", "name": "Game - Jak 2 - Runtime (release)", - "args": ["-v", "--game", "jak2", "--", "-boot", "-fakeiso"] + "args": [ + "-v", + "--game", + "jak2", + "--", + "-boot", + "-fakeiso" + ] }, { "type": "default", "project": "CMakeLists.txt", "projectTarget": "gk.exe (bin\\gk.exe)", "name": "Game - Jak 3 - Runtime (boot)", - "args": ["-v", "--game", "jak3", "--", "-boot", "-fakeiso", "-debug"] + "args": [ + "-v", + "--game", + "jak3", + "--", + "-boot", + "-fakeiso", + "-debug" + ] }, { "type": "default", "project": "CMakeLists.txt", "projectTarget": "goalc.exe (bin\\goalc.exe)", "name": "REPL - Jak 1", - "args": ["--user-auto", "--game", "jak1"] + "args": [ + "--user-auto", + "--game", + "jak1" + ] }, { "type": "default", "project": "CMakeLists.txt", "projectTarget": "goalc.exe (bin\\goalc.exe)", "name": "REPL - Jak 2", - "args": ["--user-auto", "--game", "jak2"] + "args": [ + "--user-auto", + "--game", + "jak2" + ] }, { "type": "default", @@ -262,14 +331,26 @@ "project": "CMakeLists.txt", "projectTarget": "memory_dump_tool.exe (bin\\memory_dump_tool.exe)", "name": "Tools - EE Memory Analyze - Jak 1", - "args": ["\"${workspaceRoot}/eeMemory.bin\"", "--output-path", "\"${workspaceRoot}\"", "--game", "jak1"] + "args": [ + "\"${workspaceRoot}/eeMemory.bin\"", + "--output-path", + "\"${workspaceRoot}\"", + "--game", + "jak1" + ] }, { "type": "default", "project": "CMakeLists.txt", "projectTarget": "memory_dump_tool.exe (bin\\memory_dump_tool.exe)", "name": "Tools - EE Memory Analyze - Jak 2", - "args": ["\"${workspaceRoot}/eeMemory.bin\"", "--output-path", "\"${workspaceRoot}\"", "--game", "jak2"] + "args": [ + "\"${workspaceRoot}/eeMemory.bin\"", + "--output-path", + "\"${workspaceRoot}\"", + "--game", + "jak2" + ] }, { "type": "default", @@ -286,35 +367,53 @@ "project": "CMakeLists.txt", "projectTarget": "extractor.exe (bin\\extractor.exe)", "name": "Tools - Extractor - Full", - "args": ["\"E:\\ISOs\\Jak\\Jak 1.iso\""] + "args": [ + "\"E:\\ISOs\\Jak\\Jak 1.iso\"" + ] }, { - "type" : "default", - "project" : "CMakeLists.txt", - "projectTarget" : "lsp.exe (bin\\lsp.exe)", - "name" : "Tools - LSP", - "args" : [] + "type": "default", + "project": "CMakeLists.txt", + "projectTarget": "lsp.exe (bin\\lsp.exe)", + "name": "Tools - LSP", + "args": [] }, { - "type" : "default", - "project" : "CMakeLists.txt", - "projectTarget" : "type_searcher.exe (bin\\type_searcher.exe)", - "name" : "Tools - Type Searcher", - "args" : ["--game", "jak2", "--output-path", "./search-results.json", "--size", 255, "--fields", "[{\\\"type\\\":\\\"quaternion\\\",\\\"offset\\\":48}]"] + "type": "default", + "project": "CMakeLists.txt", + "projectTarget": "type_searcher.exe (bin\\type_searcher.exe)", + "name": "Tools - Type Searcher", + "args": [ + "--game", + "jak2", + "--output-path", + "./search-results.json", + "--size", + 255, + "--fields", + "[{\\\"type\\\":\\\"quaternion\\\",\\\"offset\\\":48}]" + ] }, { - "type" : "default", - "project" : "CMakeLists.txt", - "projectTarget" : "formatter.exe (bin\\formatter.exe)", - "name" : "Tools - Formatter - Inplace", - "args" : ["--write", "--file", "C:\\Users\\xtvas\\Repos\\opengoal\\jak-project\\decompiler_out\\jak3\\mood-h_disasm.gc"] + "type": "default", + "project": "CMakeLists.txt", + "projectTarget": "formatter.exe (bin\\formatter.exe)", + "name": "Tools - Formatter - Inplace", + "args": [ + "--write", + "--file", + "C:\\Users\\xtvas\\Repositories\\opengoal\\jak-project\\goal_src\\jak1\\engine\\anim\\joint.gc" + ] }, { "type": "default", "project": "CMakeLists.txt", "projectTarget": "goalc-test.exe (bin\\goalc-test.exe)", "name": "Tests - Formatter", - "args": ["--gtest_brief=0", "--gtest_filter=\"*FormatterTests*\""] + "args": [ + "--gtest_brief=0", + "--gtest_filter=\"*FormatterTests*\"" + ] } ] -} +} \ No newline at end of file diff --git a/common/formatter/formatter.cpp b/common/formatter/formatter.cpp index 5aa0205b7..d8e7a392e 100644 --- a/common/formatter/formatter.cpp +++ b/common/formatter/formatter.cpp @@ -33,14 +33,16 @@ int hang_indentation_width(const FormatterTreeNode& curr_node) { return 1 + hang_indentation_width(first_elt); } -// TODO - this doesn't account for paren's width contribution! int get_total_form_inlined_width(const FormatterTreeNode& curr_node) { if (curr_node.token) { return curr_node.token->length(); } int width = 1; - for (const auto& ref : curr_node.refs) { - width += get_total_form_inlined_width(ref); + for (int i = 0; i < curr_node.refs.size(); i++) { + width += get_total_form_inlined_width(curr_node.refs.at(i)); + if (i != curr_node.refs.size() - 1) { + width += 1; // add the space between elements + } } return width + 1; } @@ -104,7 +106,7 @@ void apply_formatting_config( if (curr_node.formatting_config.has_constant_pairs) { for (int i = 0; i < (int)curr_node.refs.size(); i++) { auto& child_ref = curr_node.refs.at(i); - const auto type = child_ref.metadata.node_type; + const auto& type = child_ref.metadata.node_type; if (constant_types.find(type) == constant_types.end() && constant_pairs::is_element_second_in_constant_pair(curr_node, child_ref, i)) { child_ref.formatting_config.parent_mutable_extra_indent = 2; @@ -151,10 +153,26 @@ void apply_formatting_config( max_columns = field.refs.size(); } } + // if only one field has a value in the max col position, it looks weird for it to be indented + bool ignore_final_column_width = true; + int fields_with_atleast_max_col = 0; + for (const auto& field : curr_node.refs) { + if ((int)field.refs.size() == max_columns) { + fields_with_atleast_max_col++; + if (fields_with_atleast_max_col > 1) { + ignore_final_column_width = false; + break; + } + } + } // Now find the column max widths std::vector column_max_widths = {}; for (int col = 0; col < max_columns; col++) { column_max_widths.push_back(0); + // -2 because its the indentation before the final column that we want to skip + if (ignore_final_column_width && col == max_columns - 2) { + continue; + } for (const auto& field : curr_node.refs) { if ((int)field.refs.size() > col) { const auto width = get_total_form_inlined_width(field.refs.at(col)); @@ -264,13 +282,15 @@ std::vector apply_formatting(const FormatterTreeNode& curr_node, // Add new line entry if (ref.token) { // Cleanup block-comments - std::string val = ref.token_str(); if (ref.metadata.node_type == "block_comment") { - // TODO - change this sanitization to return a list of lines instead of a single new-lined - // line - val = comments::format_block_comment(ref.token_str()); + const auto comment_lines = comments::format_block_comment(ref.token_str()); + for (const auto& line : comment_lines) { + form_lines.push_back(line); + } + } else { + form_lines.push_back(ref.token_str()); } - form_lines.push_back(val); + if (!curr_node.metadata.is_top_level && i == (int)curr_node.refs.size() - 1 && (ref.metadata.is_comment)) { // if there's an inline comment at the end of a form, we have to force the paren to the next @@ -306,6 +326,13 @@ std::vector apply_formatting(const FormatterTreeNode& curr_node, if (next_ref.token) { form_lines.at(form_lines.size() - 1) += fmt::format(" {}", next_ref.token.value()); i++; + // We have to handle hang-consolidation here or else it will never be reached above! + if (i == (int)curr_node.refs.size() - 1 && form_lines.size() > 1 && + (curr_node.formatting_config.hang_forms || + curr_node.formatting_config.combine_first_two_lines)) { + form_lines.at(0) += fmt::format(" {}", form_lines.at(1)); + form_lines.erase(form_lines.begin() + 1); + } } else if (can_node_be_inlined(next_ref, cursor_pos)) { const auto& lines = apply_formatting(next_ref, {}, cursor_pos); // TODO - cursor pos for (const auto& line : lines) { @@ -379,7 +406,14 @@ std::vector apply_formatting(const FormatterTreeNode& curr_node, curr_form += str_util::repeat(curr_node.formatting_config.parent_mutable_extra_indent, " "); } if (inline_form) { - form_lines = {fmt::format("{}", fmt::join(form_lines, " "))}; + // NOTE - not sure about this, if we are inlining a form, it always makes sense to eliminate + // trailing whitespace the only issue i can foresee is related to strings that span multiple + // lines. + std::vector new_form_lines = {}; + for (const auto& form_line : form_lines) { + new_form_lines.push_back(str_util::ltrim(form_line)); + } + form_lines = {fmt::format("{}", fmt::join(new_form_lines, " "))}; } else { for (int i = 0; i < (int)form_lines.size(); i++) { if (i > 0) { diff --git a/common/formatter/rules/formatting_rules.cpp b/common/formatter/rules/formatting_rules.cpp index b999fe16f..18a80040f 100644 --- a/common/formatter/rules/formatting_rules.cpp +++ b/common/formatter/rules/formatting_rules.cpp @@ -36,21 +36,28 @@ bool should_insert_blank_line(const FormatterTreeNode& containing_node, if (node.metadata.is_comment && node.metadata.num_blank_lines_following == 0) { return false; } - // If the next form is a comment and is inline, don't insert a comment + // If the next form is a comment and is inline, don't insert a new line if ((index + 1) < (int)containing_node.refs.size() && containing_node.refs.at(index + 1).metadata.is_comment && containing_node.refs.at(index + 1).metadata.is_inline) { return false; } - // TODO - only if the form doesn't fit on a single line + if (node.formatting_config.elide_top_level_newline) { + if ((index + 1) < (int)containing_node.refs.size() && + containing_node.refs.at(index + 1).metadata.is_comment) { + return true; + } + return false; + } + return true; } } // namespace blank_lines namespace comments { -std::string format_block_comment(const std::string& comment) { +std::vector format_block_comment(const std::string& comment) { // Normalize block comments, remove any trailing or leading whitespace // Only allow annotations on the first line, like #|@file // Don't mess with internal indentation as the user might intend it to be a certain way. @@ -71,12 +78,18 @@ std::string format_block_comment(const std::string& comment) { // Remove trailing whitespace comment_contents = str_util::rtrim(comment_contents); // remove |# - // TODO - check suffix - comment_contents.pop_back(); - comment_contents.pop_back(); + if (str_util::ends_with(comment_contents, "|#")) { + comment_contents.pop_back(); + comment_contents.pop_back(); + } comment_contents = str_util::rtrim(comment_contents); - new_comment += fmt::format("\n{}\n|#", comment_contents); - return new_comment; + std::vector lines = {new_comment}; + const auto contents_as_lines = str_util::split_string(comment_contents, "\n"); + for (const auto& line : contents_as_lines) { + lines.push_back(line); + } + lines.push_back("|#"); + return lines; } } // namespace comments diff --git a/common/formatter/rules/formatting_rules.h b/common/formatter/rules/formatting_rules.h index 248f74e68..14f331e8a 100644 --- a/common/formatter/rules/formatting_rules.h +++ b/common/formatter/rules/formatting_rules.h @@ -36,7 +36,7 @@ bool should_insert_blank_line(const FormatterTreeNode& containing_node, // // Reference - https://github.com/kkinnear/zprint/blob/main/doc/options/comments.md namespace comments { -std::string format_block_comment(const std::string& comment); +std::vector format_block_comment(const std::string& comment); } // Paired elements in a list will be kept in-line rather than the default new-line indentation diff --git a/common/formatter/rules/rule_config.cpp b/common/formatter/rules/rule_config.cpp index 9fff2f5c0..0e7535fcc 100644 --- a/common/formatter/rules/rule_config.cpp +++ b/common/formatter/rules/rule_config.cpp @@ -3,22 +3,103 @@ namespace formatter_rules { namespace config { +static FormFormattingConfig new_inlinable_simple_flow_rule() { + return {.config_set = true, .hang_forms = false}; +} + static FormFormattingConfig new_permissive_flow_rule() { return {.config_set = true, .hang_forms = false, .combine_first_two_lines = true}; } -static FormFormattingConfig new_flow_rule(int start_index) { +static FormFormattingConfig new_flow_rule(int start_index, bool has_constant_pairs = false) { return {.config_set = true, .hang_forms = false, - .inline_until_index = [start_index](const std::vector& /*curr_lines*/) { - return start_index; - }}; + .inline_until_index = + [start_index](const std::vector& /*curr_lines*/) { return start_index; }, + .has_constant_pairs = has_constant_pairs}; +} + +static FormFormattingConfig new_inlineable_flow_rule(int start_index, + bool has_constant_pairs = false) { + return {.config_set = true, + .hang_forms = false, + .inline_until_index = + [start_index](const std::vector& curr_lines) { + int total_width = 0; + for (const auto& line : curr_lines) { + total_width += line.length(); + // an empty line implies a new-line was forced, this is bleeding implementation + // details, but fine for now + if (line.empty()) { + return start_index; + } + } + if (total_width <= 120) { + return (int)curr_lines.size(); + } + return start_index; + }, + .has_constant_pairs = has_constant_pairs}; +} + +static FormFormattingConfig new_defstate_rule(int start_index, bool has_constant_pairs = false) { + FormFormattingConfig cfg = { + .config_set = true, + .hang_forms = false, + .inline_until_index = + [start_index](const std::vector& /*curr_lines*/) { return start_index; }, + .has_constant_pairs = has_constant_pairs}; + // TODO - might be nice to have a function that returns a config based on a given index, instead + // of hardcoding them! + std::vector state_handler_indexes = {4, 6, 8, 10, + 12}; // NOTE - not all of these have to be defined + for (const auto& index : state_handler_indexes) { + auto temp_config = std::make_shared(); + temp_config->config_set = true; + temp_config->prevent_inlining = true; + temp_config->hang_forms = false; + temp_config->inline_until_index = [](const std::vector& /*curr_lines*/) { + return 2; + }; + temp_config->parent_mutable_extra_indent = 2; + cfg.index_configs.emplace(index, temp_config); + } + return cfg; +} + +static FormFormattingConfig new_defmethod_rule(int start_index, bool has_constant_pairs = false) { + return {.config_set = true, + .hang_forms = false, + .inline_until_index = + [start_index](const std::vector& curr_lines) { + if (curr_lines.size() >= 2 && curr_lines.at(1) == "new") { + // defmethod was changed to omit the type name for everything except the `new` + // method, so special case. + return start_index + 1; + } + return start_index; + }, + .has_constant_pairs = has_constant_pairs}; +} + +static FormFormattingConfig new_defnum_rule() { + auto temp_list_config = std::make_shared(); + temp_list_config->force_inline = true; + temp_list_config->hang_forms = false; + return { + .config_set = true, + .hang_forms = false, + .inline_until_index = [](const std::vector& /*curr_lines*/) { return 2; }, + .has_constant_pairs = true, + .default_index_config = temp_list_config, + }; } static FormFormattingConfig new_deftype_rule( int start_index, const std::vector& inlining_preventation_indices) { FormFormattingConfig cfg; + cfg.has_constant_pairs = true; cfg.config_set = true; cfg.hang_forms = false; cfg.inline_until_index = [start_index](std::vector curr_lines) { @@ -88,23 +169,39 @@ static FormFormattingConfig new_pair_rule(bool combine_first_two_expr) { return cfg; } +static FormFormattingConfig new_top_level_inline_form(bool elide_new_line) { + return {.force_inline = true, .elide_top_level_newline = elide_new_line}; +} + const std::unordered_map opengoal_form_config = { {"case", new_pair_rule(true)}, {"cond", new_pair_rule(false)}, - {"defmethod", new_flow_rule(3)}, + {"in-package", new_top_level_inline_form(true)}, + {"bundles", new_top_level_inline_form(true)}, + {"require", new_top_level_inline_form(true)}, + {"defenum", new_defnum_rule()}, + {"defmethod", new_defmethod_rule(3)}, {"deftype", new_deftype_rule(3, {3, 4, 5, 6})}, {"defun", new_flow_rule(3)}, {"defun-debug", new_flow_rule(3)}, {"defbehavior", new_flow_rule(4)}, - {"if", new_permissive_flow_rule()}, + {"if", new_inlineable_flow_rule(2)}, {"define", new_permissive_flow_rule()}, {"define-extern", new_permissive_flow_rule()}, {"defmacro", new_flow_rule(3)}, + {"defstate", new_defstate_rule(3, true)}, + {"behavior", new_flow_rule(2)}, {"dotimes", new_flow_rule(2)}, {"let", new_binding_rule(4)}, + {"let*", new_binding_rule(5)}, {"rlet", new_binding_rule(5)}, {"when", new_flow_rule(2)}, + {"countdown", new_flow_rule(2)}, + {"until", new_flow_rule(2)}, + {"while", new_flow_rule(2)}, {"begin", new_flow_rule(0)}, + {"with-pp", new_flow_rule(0)}, + {"local-vars", new_inlinable_simple_flow_rule()}, {"with-dma-buffer-add-bucket", new_flow_rule(2)}}; } // namespace config } // namespace formatter_rules diff --git a/common/formatter/rules/rule_config.h b/common/formatter/rules/rule_config.h index 0bea72d81..1d6a4ab6f 100644 --- a/common/formatter/rules/rule_config.h +++ b/common/formatter/rules/rule_config.h @@ -7,7 +7,6 @@ #include #include -// TODO - some way to apply a config to all list elements (index configs with -1?) namespace formatter_rules { namespace config { struct FormFormattingConfig { @@ -36,6 +35,8 @@ struct FormFormattingConfig { bool determine_column_widths_for_list_elements = false; std::vector list_element_column_widths = {}; + + bool elide_top_level_newline = false; }; extern const std::unordered_map opengoal_form_config; diff --git a/test/common/formatter/corpus/blank-lines.test.gc b/test/common/formatter/corpus/blank-lines.test.gc index 25ed606a8..57af46b6d 100644 --- a/test/common/formatter/corpus/blank-lines.test.gc +++ b/test/common/formatter/corpus/blank-lines.test.gc @@ -11,4 +11,40 @@ Separate Top Level (println "test") -(println "test") \ No newline at end of file +(println "test") + +=== +Consolidate Top Level +=== + +(in-package goal) +(bundles "ENGINE.CGO" "GAME.CGO") +(require "engine/draw/drawable-h.gc") +(require "kernel/gstate.gc") + +--- + +(in-package goal) +(bundles "ENGINE.CGO" "GAME.CGO") +(require "engine/draw/drawable-h.gc") +(require "kernel/gstate.gc") + +=== +Consolidate Top Level - Ignore and space out comment +=== + +(in-package goal) +(bundles "ENGINE.CGO" "GAME.CGO") +(require "engine/draw/drawable-h.gc") +(require "kernel/gstate.gc") + +;; Some comment + +--- + +(in-package goal) +(bundles "ENGINE.CGO" "GAME.CGO") +(require "engine/draw/drawable-h.gc") +(require "kernel/gstate.gc") + +;; Some comment \ No newline at end of file diff --git a/test/common/formatter/corpus/comments.test.gc b/test/common/formatter/corpus/comments.test.gc index 54de0a077..2a69f045a 100644 --- a/test/common/formatter/corpus/comments.test.gc +++ b/test/common/formatter/corpus/comments.test.gc @@ -105,7 +105,7 @@ Block Comment - Allow Annotations (println "test") === -Block Comment - In Form - TODO Improve +Block Comment - In Form === (println @@ -118,9 +118,9 @@ Block Comment - In Form - TODO Improve (println #| - block comment - test -|# + block comment + test + |# "test") === @@ -139,7 +139,7 @@ At the end of a form ) === -Block at the end of a form - TODO Improve +Block at the end of a form === (println @@ -151,12 +151,12 @@ Block at the end of a form - TODO Improve (println "hello world" #| - wow look at that block comment -|# + wow look at that block comment + |# ) === -Inline at the end of a form - TODO-A handle hanging in this instance better +Inline at the end of a single element form === (println @@ -165,7 +165,6 @@ Inline at the end of a form - TODO-A handle hanging in this instance better --- -(println - "hello world" ;; this is a comment +(println "hello world" ;; this is a comment ) diff --git a/test/common/formatter/corpus/conditions.test.gc b/test/common/formatter/corpus/conditions.test.gc index 31c462aaa..c3bde25ea 100644 --- a/test/common/formatter/corpus/conditions.test.gc +++ b/test/common/formatter/corpus/conditions.test.gc @@ -42,3 +42,36 @@ Multiline condition hang (or (and (-> this opened-with-start?) (not (cpad-hold? 0 start)) (not (cpad-hold? 0 start))) (and (not (-> this opened-with-start?)) (not (cpad-hold? 0 select))))) (set! (-> this ignore-menu-toggle?) #f)) + +=== +Consolidation of if +=== + +(if (and + (-> this data s2-0) ;; entry is populated + (= (-> this data s2-0 type) arg1) ;; type is right + (or (name= arg0 (-> this data s2-0 name)) (string-charp= arg0 (&-> (-> this data s2-0 name) data s3-0))) ;; name is right. also seek past ag name, and try again. + ) + (return (the-as joint (-> this data s2-0)))) + +--- + +(if (and (-> this data s2-0) ;; entry is populated + (= (-> this data s2-0 type) arg1) ;; type is right + (or (name= arg0 (-> this data s2-0 name)) (string-charp= arg0 (&-> (-> this data s2-0 name) data s3-0))) ;; name is right. also seek past ag name, and try again. + ) + (return (the-as joint (-> this data s2-0)))) + +=== +Consolidation of if 2 +=== + +(if (or (not s5-1) (= (-> s5-1 name) 'default)) + (login this) ;; not part of level load, just normal login. + ) + +--- + +(if (or (not s5-1) (= (-> s5-1 name) 'default)) + (login this) ;; not part of level load, just normal login. + ) \ No newline at end of file diff --git a/test/common/formatter/corpus/enums.test.gc b/test/common/formatter/corpus/enums.test.gc new file mode 100644 index 000000000..849500006 --- /dev/null +++ b/test/common/formatter/corpus/enums.test.gc @@ -0,0 +1,86 @@ +=== +With Bitfield and Type Info +=== + +(defenum + process-mask + :bitfield + #t + :type + uint32 + (execute 0) ;; 1 + (draw 1) ;; 2 + (pause 2) ;; 4 + (menu 3) ;; 8 + (progress 4) ;; 16 + (actor-pause 5) ;; 32 + (sleep 6) ;; 64 + (sleep-code 7) ;; 128 + (process-tree 8) ;; 256 not an actual process, just a "tree node" for organization + (heap-shrunk 9) ;; 512 + (going 10) ;; 1024 + (movie 11) ;; 2048 + (movie-subject 12) ;; 4096 + (target 13) ;; 8192 + (sidekick 14) ;; 16384 + (crate 15) ;; 32768 + (collectable 16) ;; 65536 + (enemy 17) ;; 131072 + (camera 18) ;; 262144 + (platform 19) ;; 524288 + (ambient 20) ;; 1048576 + (entity 21) ;; 2097152 + (projectile 22) ;; 4194304 + (attackable 23) ;; 8388608 + (death 24) ;; 16777216 + ) + +--- + +(defenum process-mask + :bitfield #t + :type uint32 + (execute 0) ;; 1 + (draw 1) ;; 2 + (pause 2) ;; 4 + (menu 3) ;; 8 + (progress 4) ;; 16 + (actor-pause 5) ;; 32 + (sleep 6) ;; 64 + (sleep-code 7) ;; 128 + (process-tree 8) ;; 256 not an actual process, just a "tree node" for organization + (heap-shrunk 9) ;; 512 + (going 10) ;; 1024 + (movie 11) ;; 2048 + (movie-subject 12) ;; 4096 + (target 13) ;; 8192 + (sidekick 14) ;; 16384 + (crate 15) ;; 32768 + (collectable 16) ;; 65536 + (enemy 17) ;; 131072 + (camera 18) ;; 262144 + (platform 19) ;; 524288 + (ambient 20) ;; 1048576 + (entity 21) ;; 2097152 + (projectile 22) ;; 4194304 + (attackable 23) ;; 8388608 + (death 24) ;; 16777216 + ) + + +=== +Form heads that overlap with built-in forms +=== + +(defenum pc-prof-event + (begin + 0) + (end 1) + (instant 2)) + +--- + +(defenum pc-prof-event + (begin 0) + (end 1) + (instant 2)) \ No newline at end of file diff --git a/test/common/formatter/corpus/functions.test.gc b/test/common/formatter/corpus/functions.test.gc index 3a0a98a9a..c8ca1dabe 100644 --- a/test/common/formatter/corpus/functions.test.gc +++ b/test/common/formatter/corpus/functions.test.gc @@ -82,3 +82,18 @@ Basic behavior (quaternion-copy! (-> gp-0 quat) (-> arg0 quat)) (vector-identity! (-> gp-0 scale)) (the-as symbol (send-event *camera* 'teleport-to-transformq gp-0)))) + +=== +Methods - new +=== + +(defmethod new align-control + ((allocation symbol) (type-to-make type) (proc process-drawable)) + "Create a new align-control." + (+ 1 1)) + +--- + +(defmethod new align-control ((allocation symbol) (type-to-make type) (proc process-drawable)) + "Create a new align-control." + (+ 1 1)) \ No newline at end of file diff --git a/test/common/formatter/corpus/indent.test.gc b/test/common/formatter/corpus/indent.test.gc index 8c54a1718..2e23faa1a 100644 --- a/test/common/formatter/corpus/indent.test.gc +++ b/test/common/formatter/corpus/indent.test.gc @@ -81,3 +81,70 @@ begin (begin "hello" (println "world")) + +=== +Inlining flowed form +=== + +(or something (begin (+ 1 2) (= 2 2))) + +--- + +(or something (begin (+ 1 2) (= 2 2))) + +=== +Long array creation +=== + +(let ((arr (new + 'static + 'array + uint32 + 16 + #x7b2191b ;; = 6 ^ 129112349 + #x7b21914 ;; = 9 ^ 129112349 + #x7b21916 ;; = 11 ^ 129112349 + #x7b21913 ;; = 14 ^ 129112349 + #x7b21909 ;; = 20 ^ 129112349 + #x7b21908 ;; = 21 ^ 129112349 + #x7b2190a ;; = 23 ^ 129112349 + #x7b21907 ;; = 26 ^ 129112349 + #x7b2191b ;; = 6 ^ 129112349 + #x7b21917 ;; = 10 ^ 129112349 + #x7b21916 ;; = 11 ^ 129112349 + #x7b2190c ;; = 17 ^ 129112349 + #x7b21909 ;; = 20 ^ 129112349 + #x7b2190b ;; = 22 ^ 129112349 + #x7b2190a ;; = 23 ^ 129112349 + #x7b21900 ;; = 29 ^ 129112349 + ))) + (dotimes (i 16) + (set! (-> (scratchpad-object terrain-context) work foreground joint-work frm-jmp-table i) + (the-as (function none) (-> arr i))))) + +--- + +(let ((arr (new 'static + 'array + uint32 + 16 + #x7b2191b ;; = 6 ^ 129112349 + #x7b21914 ;; = 9 ^ 129112349 + #x7b21916 ;; = 11 ^ 129112349 + #x7b21913 ;; = 14 ^ 129112349 + #x7b21909 ;; = 20 ^ 129112349 + #x7b21908 ;; = 21 ^ 129112349 + #x7b2190a ;; = 23 ^ 129112349 + #x7b21907 ;; = 26 ^ 129112349 + #x7b2191b ;; = 6 ^ 129112349 + #x7b21917 ;; = 10 ^ 129112349 + #x7b21916 ;; = 11 ^ 129112349 + #x7b2190c ;; = 17 ^ 129112349 + #x7b21909 ;; = 20 ^ 129112349 + #x7b2190b ;; = 22 ^ 129112349 + #x7b2190a ;; = 23 ^ 129112349 + #x7b21900 ;; = 29 ^ 129112349 + ))) + (dotimes (i 16) + (set! (-> (scratchpad-object terrain-context) work foreground joint-work frm-jmp-table i) + (the-as (function none) (-> arr i))))) \ No newline at end of file diff --git a/test/common/formatter/corpus/states.test.gc b/test/common/formatter/corpus/states.test.gc index 59de4a87f..6834177bd 100644 --- a/test/common/formatter/corpus/states.test.gc +++ b/test/common/formatter/corpus/states.test.gc @@ -1,52 +1,92 @@ === -TODO - Basic State +Basic State === -(defstate active (enemy) - :virtual #t - :event enemy-event-handler - :enter (behavior () +(defstate joint-exploder-shatter (joint-exploder) + :enter + (behavior () + (set-time! (-> self state-time))) + :trans + (behavior () + (let* ((f1-0 (the float (- (current-time) (-> self state-time)))) + (f0-2 (- 1.0 (/ f1-0 (the float (-> self tuning duration))))) + (f1-2 (- 1.0 (/ f1-0 (* 0.75 (the float (-> self tuning duration))))))) + (if (< f1-2 0.0) (set! f1-2 0.0)) + (set-vector! (-> self scale-vector) f0-2 f1-2 f0-2 1.0)) + (dotimes (v1-8 5) + (set! (-> self lists v1-8 pre-moved?) #f)) + (dotimes (gp-0 5) + (let ((s5-0 (-> self lists gp-0))) + (when (>= (-> s5-0 head) 0) + (when (not (-> s5-0 pre-moved?)) + (joint-exploder-method-25 self s5-0) + (if (nonzero? gp-0) (joint-exploder-method-28 self s5-0)))))) + (let ((gp-1 (new 'stack-no-clear 'bounding-box))) + (let ((v1-26 (-> self root trans))) (set! (-> gp-1 min quad) (-> v1-26 quad)) (set! (-> gp-1 max quad) (-> v1-26 quad))) + (dotimes (s5-1 5) + (let ((s4-0 (-> self lists s5-1))) + (if (-> s4-0 bbox-valid?) (add-box! gp-1 (-> s4-0 bbox))) + (if (nonzero? s5-1) (joint-exploder-method-22 self s4-0)))) + (let ((s5-2 (-> self draw bounds))) + (set-vector! s5-2 + (* 0.5 (+ (-> gp-1 min x) (-> gp-1 max x))) + (* 0.5 (+ (-> gp-1 min y) (-> gp-1 max y))) + (* 0.5 (+ (-> gp-1 min z) (-> gp-1 max z))) + 1.0) + (let ((f0-12 (+ 16384.0 (vector-vector-distance s5-2 (-> gp-1 max))))) + (vector-! s5-2 s5-2 (-> self root trans)) + (set! (-> s5-2 w) f0-12)))) + 0) + :code + (behavior () (set-time! (-> self state-time)) - (logclear! (-> self enemy-flags) (enemy-flag cam-attack-mode)) - (when (logtest? (-> self enemy-flags) (enemy-flag enable-on-active)) - (logclear! (-> self enemy-flags) (enemy-flag enable-on-active)) - (let ((gp-0 (-> self on-active))) - (if gp-0 - (script-eval gp-0 :vector (-> self root trans)) - ) - ) - ) - (when (not (logtest? (enemy-flag chase-startup) (-> self enemy-flags))) - (if (logtest? (-> self enemy-flags) (enemy-flag actor-pause-backup)) - (logior! (-> self mask) (process-mask actor-pause)) - (logclear! (-> self mask) (process-mask actor-pause)) - ) - ) - ) - :trans (behavior () - (when (time-elapsed? (-> self state-time) (seconds 0.1)) - (let ((v1-3 (-> self focus aware))) - (cond - ((< (the-as int v1-3) 1) - (go-virtual idle) - ) - ((< 1 (the-as int v1-3)) - (go-virtual notice) - ) - ) - ) - ) - ) - :code (behavior () - (ja-channel-push! 1 (seconds 0.1)) - (sleep-code) - ) - :post (behavior () - (idle-control-method-10 (-> self idle-anim-player) self) - (enemy-simple-post) - ) - ) + (until (time-elapsed? (-> self state-time) (-> self tuning duration)) + (suspend) + (ja :num! (loop!)))) + :post + ja-post) --- -TODO +(defstate joint-exploder-shatter (joint-exploder) + :enter + (behavior () + (set-time! (-> self state-time))) + :trans + (behavior () + (let* ((f1-0 (the float (- (current-time) (-> self state-time)))) + (f0-2 (- 1.0 (/ f1-0 (the float (-> self tuning duration))))) + (f1-2 (- 1.0 (/ f1-0 (* 0.75 (the float (-> self tuning duration))))))) + (if (< f1-2 0.0) (set! f1-2 0.0)) + (set-vector! (-> self scale-vector) f0-2 f1-2 f0-2 1.0)) + (dotimes (v1-8 5) + (set! (-> self lists v1-8 pre-moved?) #f)) + (dotimes (gp-0 5) + (let ((s5-0 (-> self lists gp-0))) + (when (>= (-> s5-0 head) 0) + (when (not (-> s5-0 pre-moved?)) + (joint-exploder-method-25 self s5-0) + (if (nonzero? gp-0) (joint-exploder-method-28 self s5-0)))))) + (let ((gp-1 (new 'stack-no-clear 'bounding-box))) + (let ((v1-26 (-> self root trans))) (set! (-> gp-1 min quad) (-> v1-26 quad)) (set! (-> gp-1 max quad) (-> v1-26 quad))) + (dotimes (s5-1 5) + (let ((s4-0 (-> self lists s5-1))) + (if (-> s4-0 bbox-valid?) (add-box! gp-1 (-> s4-0 bbox))) + (if (nonzero? s5-1) (joint-exploder-method-22 self s4-0)))) + (let ((s5-2 (-> self draw bounds))) + (set-vector! s5-2 + (* 0.5 (+ (-> gp-1 min x) (-> gp-1 max x))) + (* 0.5 (+ (-> gp-1 min y) (-> gp-1 max y))) + (* 0.5 (+ (-> gp-1 min z) (-> gp-1 max z))) + 1.0) + (let ((f0-12 (+ 16384.0 (vector-vector-distance s5-2 (-> gp-1 max))))) + (vector-! s5-2 s5-2 (-> self root trans)) + (set! (-> s5-2 w) f0-12)))) + 0) + :code + (behavior () + (set-time! (-> self state-time)) + (until (time-elapsed? (-> self state-time) (-> self tuning duration)) + (suspend) + (ja :num! (loop!)))) + :post ja-post) diff --git a/test/common/formatter/corpus/types.test.gc b/test/common/formatter/corpus/types.test.gc index a980f73a0..0e3df88a3 100644 --- a/test/common/formatter/corpus/types.test.gc +++ b/test/common/formatter/corpus/types.test.gc @@ -105,3 +105,91 @@ Types - With Methods and States pov-camera-playing pov-camera-start-playing pov-camera-startup)) + +=== +With flags +=== + +(deftype process-tree (basic) + ((name basic :offset-assert 4) + (mask process-mask :offset-assert 8) + (parent (pointer process-tree) :offset-assert 12) + (brother (pointer process-tree) :offset-assert 16) + (child (pointer process-tree) :offset-assert 20) + (ppointer (pointer process) :offset-assert 24) + (self process-tree :offset-assert 28)) + (:methods + (new (symbol type basic) _type_) + (activate (_type_ process-tree basic pointer) process-tree) ;; 9 + (deactivate (_type_) none) ;; 10 + (init-from-entity! (_type_ entity-actor) none) ;; 11 + (run-logic? (_type_) symbol) ;; 12 + (process-tree-method-13 () none) ;; 13 + ) + :size-assert + #x20 + :method-count-assert + 14 + :no-runtime-type ;; already defined by kscheme. Don't do it again. + ) + +--- + +(deftype process-tree (basic) + ((name basic :offset-assert 4) + (mask process-mask :offset-assert 8) + (parent (pointer process-tree) :offset-assert 12) + (brother (pointer process-tree) :offset-assert 16) + (child (pointer process-tree) :offset-assert 20) + (ppointer (pointer process) :offset-assert 24) + (self process-tree :offset-assert 28)) + (:methods + (new (symbol type basic) _type_) + (activate (_type_ process-tree basic pointer) process-tree) ;; 9 + (deactivate (_type_) none) ;; 10 + (init-from-entity! (_type_ entity-actor) none) ;; 11 + (run-logic? (_type_) symbol) ;; 12 + (process-tree-method-13 () none) ;; 13 + ) + :size-assert #x20 + :method-count-assert 14 + :no-runtime-type ;; already defined by kscheme. Don't do it again. + ) + +=== +Single value column +=== + +(deftype joint-control-channel (structure) + ((parent joint-control) + (command symbol) + (frame-interp float) + (frame-group art-joint-anim) + (frame-num float) + (num-func (function joint-control-channel float float float)) + (param float 2) + (group-sub-index int16) + (group-size int16) + (dist meters) + (eval-time uint32) + (inspector-amount float)) + (:methods + (debug-print-frames (_type_) _type_))) + +--- + +(deftype joint-control-channel (structure) + ((parent joint-control) + (command symbol) + (frame-interp float) + (frame-group art-joint-anim) + (frame-num float) + (num-func (function joint-control-channel float float float)) + (param float 2) + (group-sub-index int16) + (group-size int16) + (dist meters) + (eval-time uint32) + (inspector-amount float)) + (:methods + (debug-print-frames (_type_) _type_))) diff --git a/test/common/formatter/test_formatter.cpp b/test/common/formatter/test_formatter.cpp index dc06e541e..42f4ecf11 100644 --- a/test/common/formatter/test_formatter.cpp +++ b/test/common/formatter/test_formatter.cpp @@ -56,7 +56,7 @@ std::vector get_test_definitions(const fs::path& file_path) { if (!curr_test.name.empty() && line.empty()) { i++; while (true) { - if (contents.at(i) == "---") { + if (str_util::trim(contents.at(i)) == "---") { i++; curr_test.input = str_util::trim(curr_test.input); break; diff --git a/third-party/tree-sitter/tree-sitter-opengoal/grammar.js b/third-party/tree-sitter/tree-sitter-opengoal/grammar.js index d9c136543..ef32fb81b 100644 --- a/third-party/tree-sitter/tree-sitter-opengoal/grammar.js +++ b/third-party/tree-sitter/tree-sitter-opengoal/grammar.js @@ -205,7 +205,7 @@ module.exports = grammar({ seq(field('numberOfArgs', $._format_token), '*'), '?', "Newline", - seq(repeat(choice($._format_token, ',')), /[$mrRbBdDgGxXeEoOsStTfHhJjKkLlNnVwWyYzZ]/), + seq(repeat(choice($._format_token, ',')), /[$mrRbBdDgGxXeEoOsStTfFHhJjKkLlNnVwWyYzZ]/), ), format_specifier: $ => prec.left(seq( diff --git a/third-party/tree-sitter/tree-sitter-opengoal/grammar.json b/third-party/tree-sitter/tree-sitter-opengoal/grammar.json index 28e204339..32c7eecd3 100644 --- a/third-party/tree-sitter/tree-sitter-opengoal/grammar.json +++ b/third-party/tree-sitter/tree-sitter-opengoal/grammar.json @@ -646,7 +646,7 @@ }, { "type": "PATTERN", - "value": "[$mrRbBdDgGxXeEoOsStTfHhJjKkLlNnVwWyYzZ]" + "value": "[$mrRbBdDgGxXeEoOsStTfFHhJjKkLlNnVwWyYzZ]" } ] } diff --git a/third-party/tree-sitter/tree-sitter-opengoal/parser.c b/third-party/tree-sitter/tree-sitter-opengoal/parser.c index 8543b2f3f..7f6b31a99 100644 --- a/third-party/tree-sitter/tree-sitter-opengoal/parser.c +++ b/third-party/tree-sitter/tree-sitter-opengoal/parser.c @@ -722,46 +722,18 @@ static inline bool sym_kwd_lit_character_set_2(int32_t c) { } static inline bool aux_sym_format_directive_type_token11_character_set_1(int32_t c) { - return (c < 'R' - ? (c < 'G' + return (c < 'V' + ? (c < 'J' ? (c < 'B' ? c == '$' - : c <= 'E') - : (c <= 'L' || c == 'O')) - : (c <= 'T' || (c < 'r' - ? (c < 'b' - ? (c >= 'X' && c <= 'Z') - : c <= 'o') - : (c <= 't' || (c >= 'x' && c <= 'z'))))); -} - -static inline bool aux_sym_format_directive_type_token11_character_set_2(int32_t c) { - return (c < 'R' - ? (c < 'G' - ? (c < 'B' - ? c == '$' - : c <= 'E') - : (c <= 'L' || c == 'O')) - : (c <= 'T' || (c < 'r' - ? (c < 'b' - ? (c >= 'V' && c <= 'Z') - : c <= 'o') - : (c <= 't' || (c >= 'x' && c <= 'z'))))); -} - -static inline bool aux_sym_format_directive_type_token11_character_set_3(int32_t c) { - return (c < 'R' - ? (c < 'G' - ? (c < 'B' - ? c == '$' - : (c <= 'B' || (c >= 'D' && c <= 'E'))) - : (c <= 'H' || (c < 'N' - ? (c >= 'J' && c <= 'L') - : c <= 'O'))) - : (c <= 'T' || (c < 'j' - ? (c < 'b' - ? (c >= 'V' && c <= 'Z') - : (c <= 'b' || (c >= 'd' && c <= 'h'))) + : (c <= 'B' || (c >= 'D' && c <= 'H'))) + : (c <= 'L' || (c < 'R' + ? (c >= 'N' && c <= 'O') + : c <= 'T'))) + : (c <= 'Z' || (c < 'j' + ? (c < 'd' + ? c == 'b' + : c <= 'h') : (c <= 'o' || (c < 'w' ? (c >= 'r' && c <= 't') : c <= 'z'))))); @@ -864,8 +836,7 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (('[' <= lookahead && lookahead <= ']')) ADVANCE(68); if (('{' <= lookahead && lookahead <= '}')) ADVANCE(68); if (lookahead == '$' || - ('B' <= lookahead && lookahead <= 'E') || - ('G' <= lookahead && lookahead <= 'L') || + ('B' <= lookahead && lookahead <= 'L') || lookahead == 'N' || lookahead == 'O' || ('R' <= lookahead && lookahead <= 'T') || @@ -914,7 +885,14 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == '(' || lookahead == ')') ADVANCE(56); if (('0' <= lookahead && lookahead <= '9')) ADVANCE(27); - if (aux_sym_format_directive_type_token11_character_set_1(lookahead)) ADVANCE(65); + if (lookahead == '$' || + ('B' <= lookahead && lookahead <= 'L') || + lookahead == 'O' || + ('R' <= lookahead && lookahead <= 'T') || + ('X' <= lookahead && lookahead <= 'Z') || + ('b' <= lookahead && lookahead <= 'o') || + ('r' <= lookahead && lookahead <= 't') || + ('x' <= lookahead && lookahead <= 'z')) ADVANCE(65); END_STATE(); case 2: if (lookahead == '\n') ADVANCE(49); @@ -953,7 +931,14 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == '(' || lookahead == ')') ADVANCE(56); if (('0' <= lookahead && lookahead <= '9')) ADVANCE(27); - if (aux_sym_format_directive_type_token11_character_set_2(lookahead)) ADVANCE(65); + if (lookahead == '$' || + ('B' <= lookahead && lookahead <= 'L') || + lookahead == 'O' || + ('R' <= lookahead && lookahead <= 'T') || + ('V' <= lookahead && lookahead <= 'Z') || + ('b' <= lookahead && lookahead <= 'o') || + ('r' <= lookahead && lookahead <= 't') || + ('x' <= lookahead && lookahead <= 'z')) ADVANCE(65); END_STATE(); case 3: if (lookahead == '"') ADVANCE(67); @@ -983,7 +968,7 @@ static bool ts_lex(TSLexer *lexer, TSStateId state) { if (lookahead == '~') ADVANCE(43); if (('+' <= lookahead && lookahead <= '-')) ADVANCE(7); if (('0' <= lookahead && lookahead <= '9')) ADVANCE(27); - if (aux_sym_format_directive_type_token11_character_set_3(lookahead)) ADVANCE(65); + if (aux_sym_format_directive_type_token11_character_set_1(lookahead)) ADVANCE(65); END_STATE(); case 7: if (lookahead == '#') ADVANCE(9); @@ -3147,7 +3132,9 @@ static const TSParseActionEntry ts_parse_actions[] = { #ifdef __cplusplus extern "C" { #endif -#ifdef _WIN32 +#ifdef TREE_SITTER_HIDE_SYMBOLS +#define TS_PUBLIC +#elif defined(_WIN32) #define TS_PUBLIC __declspec(dllexport) #else #define TS_PUBLIC __attribute__((visibility("default"))) diff --git a/third-party/tree-sitter/tree-sitter-opengoal/tree_sitter/array.h b/third-party/tree-sitter/tree-sitter-opengoal/tree_sitter/array.h index 186ba6739..15a3b233b 100644 --- a/third-party/tree-sitter/tree-sitter-opengoal/tree_sitter/array.h +++ b/third-party/tree-sitter/tree-sitter-opengoal/tree_sitter/array.h @@ -66,9 +66,12 @@ extern "C" { /// Increase the array's size by `count` elements. /// New elements are zero-initialized. #define array_grow_by(self, count) \ - (_array__grow((Array *)(self), count, array_elem_size(self)), \ - memset((self)->contents + (self)->size, 0, (count) * array_elem_size(self)), \ - (self)->size += (count)) + do { \ + if ((count) == 0) break; \ + _array__grow((Array *)(self), count, array_elem_size(self)); \ + memset((self)->contents + (self)->size, 0, (count) * array_elem_size(self)); \ + (self)->size += (count); \ + } while (0) /// Append all elements from one array to the end of another. #define array_push_all(self, other) \