herb 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Rakefile +20 -2
- data/ext/herb/extension.c +46 -14
- data/ext/herb/extension.h +9 -0
- data/ext/herb/extension_helpers.c +9 -27
- data/ext/herb/nodes.c +104 -1
- data/herb.gemspec +2 -1
- data/lib/herb/ast/node.rb +40 -4
- data/lib/herb/ast/nodes.rb +892 -140
- data/lib/herb/ast.rb +1 -0
- data/lib/herb/cli.rb +4 -1
- data/lib/herb/errors.rb +84 -33
- data/lib/herb/lex_result.rb +4 -1
- data/lib/herb/libherb/array.rb +3 -0
- data/lib/herb/libherb/ast_node.rb +3 -0
- data/lib/herb/libherb/buffer.rb +3 -0
- data/lib/herb/libherb/extract_result.rb +3 -0
- data/lib/herb/libherb/lex_result.rb +3 -0
- data/lib/herb/libherb/libherb.rb +3 -0
- data/lib/herb/libherb/parse_result.rb +3 -0
- data/lib/herb/libherb/token.rb +3 -0
- data/lib/herb/libherb.rb +3 -0
- data/lib/herb/location.rb +15 -6
- data/lib/herb/parse_result.rb +10 -1
- data/lib/herb/position.rb +17 -8
- data/lib/herb/project.rb +13 -4
- data/lib/herb/range.rb +18 -8
- data/lib/herb/result.rb +7 -1
- data/lib/herb/token.rb +14 -4
- data/lib/herb/token_list.rb +10 -1
- data/lib/herb/version.rb +2 -1
- data/lib/herb/visitor.rb +175 -0
- data/lib/herb/warnings.rb +43 -0
- data/lib/herb.rb +6 -1
- data/sig/herb/ast/node.rbs +48 -0
- data/sig/herb/ast/nodes.rbs +941 -0
- data/sig/herb/ast.rbs +6 -0
- data/sig/herb/errors.rbs +193 -0
- data/sig/herb/lex_result.rbs +16 -0
- data/sig/herb/location.rbs +30 -0
- data/sig/herb/parse_result.rbs +22 -0
- data/sig/herb/position.rbs +30 -0
- data/sig/herb/range.rbs +33 -0
- data/sig/herb/result.rbs +20 -0
- data/sig/herb/token.rbs +31 -0
- data/sig/herb/token_list.rbs +13 -0
- data/sig/herb/version.rbs +5 -0
- data/sig/herb/visitor.rbs +104 -0
- data/sig/herb/warnings.rbs +28 -0
- data/sig/herb.rbs +4 -0
- data/sig/serialized.rbs +9 -0
- data/sig/serialized_ast_errors.rbs +53 -0
- data/sig/serialized_ast_nodes.rbs +221 -0
- data/src/analyze.c +138 -43
- data/src/analyze_helpers.c +44 -1
- data/src/analyzed_ruby.c +10 -1
- data/src/ast_nodes.c +103 -1
- data/src/ast_pretty_print.c +60 -0
- data/src/buffer.c +60 -27
- data/src/extract.c +57 -20
- data/src/include/analyze.h +3 -0
- data/src/include/analyze_helpers.h +6 -0
- data/src/include/analyzed_ruby.h +3 -0
- data/src/include/ast_nodes.h +32 -0
- data/src/include/buffer.h +5 -2
- data/src/include/lexer_peek_helpers.h +2 -2
- data/src/include/macros.h +2 -2
- data/src/include/version.h +1 -1
- data/src/lexer.c +1 -1
- data/src/parser.c +17 -7
- data/src/token.c +1 -1
- data/src/util.c +3 -1
- data/src/visitor.c +36 -0
- metadata +23 -2
data/src/analyzed_ruby.c
CHANGED
@@ -18,7 +18,9 @@ analyzed_ruby_T* init_analyzed_ruby(char* source) {
|
|
18
18
|
analyzed->has_block_node = false;
|
19
19
|
analyzed->has_block_closing = false;
|
20
20
|
analyzed->has_case_node = false;
|
21
|
+
analyzed->has_case_match_node = false;
|
21
22
|
analyzed->has_when_node = false;
|
23
|
+
analyzed->has_in_node = false;
|
22
24
|
analyzed->has_for_node = false;
|
23
25
|
analyzed->has_while_node = false;
|
24
26
|
analyzed->has_until_node = false;
|
@@ -26,10 +28,17 @@ analyzed_ruby_T* init_analyzed_ruby(char* source) {
|
|
26
28
|
analyzed->has_rescue_node = false;
|
27
29
|
analyzed->has_ensure_node = false;
|
28
30
|
analyzed->has_unless_node = false;
|
31
|
+
analyzed->has_yield_node = false;
|
29
32
|
|
30
33
|
return analyzed;
|
31
34
|
}
|
32
35
|
|
33
36
|
void free_analyzed_ruby(analyzed_ruby_T* analyzed) {
|
34
|
-
|
37
|
+
if (!analyzed) { return; }
|
38
|
+
|
39
|
+
if (analyzed->parsed && analyzed->root != NULL) { pm_node_destroy(&analyzed->parser, analyzed->root); }
|
40
|
+
|
41
|
+
pm_parser_free(&analyzed->parser);
|
42
|
+
|
43
|
+
free(analyzed);
|
35
44
|
}
|
data/src/ast_nodes.c
CHANGED
@@ -266,6 +266,22 @@ AST_ERB_CASE_NODE_T* ast_erb_case_node_init(token_T* tag_opening, token_T* conte
|
|
266
266
|
return erb_case_node;
|
267
267
|
}
|
268
268
|
|
269
|
+
AST_ERB_CASE_MATCH_NODE_T* ast_erb_case_match_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, array_T* children, array_T* conditions, struct AST_ERB_ELSE_NODE_STRUCT* else_clause, struct AST_ERB_END_NODE_STRUCT* end_node, position_T* start_position, position_T* end_position, array_T* errors) {
|
270
|
+
AST_ERB_CASE_MATCH_NODE_T* erb_case_match_node = malloc(sizeof(AST_ERB_CASE_MATCH_NODE_T));
|
271
|
+
|
272
|
+
ast_node_init(&erb_case_match_node->base, AST_ERB_CASE_MATCH_NODE, start_position, end_position, errors);
|
273
|
+
|
274
|
+
erb_case_match_node->tag_opening = token_copy(tag_opening);
|
275
|
+
erb_case_match_node->content = token_copy(content);
|
276
|
+
erb_case_match_node->tag_closing = token_copy(tag_closing);
|
277
|
+
erb_case_match_node->children = children;
|
278
|
+
erb_case_match_node->conditions = conditions;
|
279
|
+
erb_case_match_node->else_clause = else_clause;
|
280
|
+
erb_case_match_node->end_node = end_node;
|
281
|
+
|
282
|
+
return erb_case_match_node;
|
283
|
+
}
|
284
|
+
|
269
285
|
AST_ERB_WHILE_NODE_T* ast_erb_while_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, array_T* statements, struct AST_ERB_END_NODE_STRUCT* end_node, position_T* start_position, position_T* end_position, array_T* errors) {
|
270
286
|
AST_ERB_WHILE_NODE_T* erb_while_node = malloc(sizeof(AST_ERB_WHILE_NODE_T));
|
271
287
|
|
@@ -367,6 +383,31 @@ AST_ERB_UNLESS_NODE_T* ast_erb_unless_node_init(token_T* tag_opening, token_T* c
|
|
367
383
|
return erb_unless_node;
|
368
384
|
}
|
369
385
|
|
386
|
+
AST_ERB_YIELD_NODE_T* ast_erb_yield_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, position_T* start_position, position_T* end_position, array_T* errors) {
|
387
|
+
AST_ERB_YIELD_NODE_T* erb_yield_node = malloc(sizeof(AST_ERB_YIELD_NODE_T));
|
388
|
+
|
389
|
+
ast_node_init(&erb_yield_node->base, AST_ERB_YIELD_NODE, start_position, end_position, errors);
|
390
|
+
|
391
|
+
erb_yield_node->tag_opening = token_copy(tag_opening);
|
392
|
+
erb_yield_node->content = token_copy(content);
|
393
|
+
erb_yield_node->tag_closing = token_copy(tag_closing);
|
394
|
+
|
395
|
+
return erb_yield_node;
|
396
|
+
}
|
397
|
+
|
398
|
+
AST_ERB_IN_NODE_T* ast_erb_in_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, array_T* statements, position_T* start_position, position_T* end_position, array_T* errors) {
|
399
|
+
AST_ERB_IN_NODE_T* erb_in_node = malloc(sizeof(AST_ERB_IN_NODE_T));
|
400
|
+
|
401
|
+
ast_node_init(&erb_in_node->base, AST_ERB_IN_NODE, start_position, end_position, errors);
|
402
|
+
|
403
|
+
erb_in_node->tag_opening = token_copy(tag_opening);
|
404
|
+
erb_in_node->content = token_copy(content);
|
405
|
+
erb_in_node->tag_closing = token_copy(tag_closing);
|
406
|
+
erb_in_node->statements = statements;
|
407
|
+
|
408
|
+
return erb_in_node;
|
409
|
+
}
|
410
|
+
|
370
411
|
const char* ast_node_type_to_string(AST_NODE_T* node) {
|
371
412
|
switch (node->type) {
|
372
413
|
case AST_DOCUMENT_NODE: return "AST_DOCUMENT_NODE";
|
@@ -389,6 +430,7 @@ const char* ast_node_type_to_string(AST_NODE_T* node) {
|
|
389
430
|
case AST_ERB_BLOCK_NODE: return "AST_ERB_BLOCK_NODE";
|
390
431
|
case AST_ERB_WHEN_NODE: return "AST_ERB_WHEN_NODE";
|
391
432
|
case AST_ERB_CASE_NODE: return "AST_ERB_CASE_NODE";
|
433
|
+
case AST_ERB_CASE_MATCH_NODE: return "AST_ERB_CASE_MATCH_NODE";
|
392
434
|
case AST_ERB_WHILE_NODE: return "AST_ERB_WHILE_NODE";
|
393
435
|
case AST_ERB_UNTIL_NODE: return "AST_ERB_UNTIL_NODE";
|
394
436
|
case AST_ERB_FOR_NODE: return "AST_ERB_FOR_NODE";
|
@@ -396,6 +438,8 @@ const char* ast_node_type_to_string(AST_NODE_T* node) {
|
|
396
438
|
case AST_ERB_ENSURE_NODE: return "AST_ERB_ENSURE_NODE";
|
397
439
|
case AST_ERB_BEGIN_NODE: return "AST_ERB_BEGIN_NODE";
|
398
440
|
case AST_ERB_UNLESS_NODE: return "AST_ERB_UNLESS_NODE";
|
441
|
+
case AST_ERB_YIELD_NODE: return "AST_ERB_YIELD_NODE";
|
442
|
+
case AST_ERB_IN_NODE: return "AST_ERB_IN_NODE";
|
399
443
|
}
|
400
444
|
|
401
445
|
return "Unknown ast_node_type_T";
|
@@ -423,6 +467,7 @@ const char* ast_node_human_type(AST_NODE_T* node) {
|
|
423
467
|
case AST_ERB_BLOCK_NODE: return "ERBBlockNode";
|
424
468
|
case AST_ERB_WHEN_NODE: return "ERBWhenNode";
|
425
469
|
case AST_ERB_CASE_NODE: return "ERBCaseNode";
|
470
|
+
case AST_ERB_CASE_MATCH_NODE: return "ERBCaseMatchNode";
|
426
471
|
case AST_ERB_WHILE_NODE: return "ERBWhileNode";
|
427
472
|
case AST_ERB_UNTIL_NODE: return "ERBUntilNode";
|
428
473
|
case AST_ERB_FOR_NODE: return "ERBForNode";
|
@@ -430,6 +475,8 @@ const char* ast_node_human_type(AST_NODE_T* node) {
|
|
430
475
|
case AST_ERB_ENSURE_NODE: return "ERBEnsureNode";
|
431
476
|
case AST_ERB_BEGIN_NODE: return "ERBBeginNode";
|
432
477
|
case AST_ERB_UNLESS_NODE: return "ERBUnlessNode";
|
478
|
+
case AST_ERB_YIELD_NODE: return "ERBYieldNode";
|
479
|
+
case AST_ERB_IN_NODE: return "ERBInNode";
|
433
480
|
}
|
434
481
|
|
435
482
|
return "Unknown ast_node_type_T";
|
@@ -603,7 +650,9 @@ static void ast_free_erb_content_node(AST_ERB_CONTENT_NODE_T* erb_content_node)
|
|
603
650
|
if (erb_content_node->tag_opening != NULL) { token_free(erb_content_node->tag_opening); }
|
604
651
|
if (erb_content_node->content != NULL) { token_free(erb_content_node->content); }
|
605
652
|
if (erb_content_node->tag_closing != NULL) { token_free(erb_content_node->tag_closing); }
|
606
|
-
|
653
|
+
if (erb_content_node->analyzed_ruby != NULL) {
|
654
|
+
free_analyzed_ruby(erb_content_node->analyzed_ruby);
|
655
|
+
}
|
607
656
|
|
608
657
|
ast_free_base_node(&erb_content_node->base);
|
609
658
|
}
|
@@ -709,6 +758,32 @@ static void ast_free_erb_case_node(AST_ERB_CASE_NODE_T* erb_case_node) {
|
|
709
758
|
ast_free_base_node(&erb_case_node->base);
|
710
759
|
}
|
711
760
|
|
761
|
+
static void ast_free_erb_case_match_node(AST_ERB_CASE_MATCH_NODE_T* erb_case_match_node) {
|
762
|
+
if (erb_case_match_node->tag_opening != NULL) { token_free(erb_case_match_node->tag_opening); }
|
763
|
+
if (erb_case_match_node->content != NULL) { token_free(erb_case_match_node->content); }
|
764
|
+
if (erb_case_match_node->tag_closing != NULL) { token_free(erb_case_match_node->tag_closing); }
|
765
|
+
if (erb_case_match_node->children != NULL) {
|
766
|
+
for (size_t i = 0; i < array_size(erb_case_match_node->children); i++) {
|
767
|
+
AST_NODE_T* child = array_get(erb_case_match_node->children, i);
|
768
|
+
if (child) { ast_node_free(child); }
|
769
|
+
}
|
770
|
+
|
771
|
+
array_free(&erb_case_match_node->children);
|
772
|
+
}
|
773
|
+
if (erb_case_match_node->conditions != NULL) {
|
774
|
+
for (size_t i = 0; i < array_size(erb_case_match_node->conditions); i++) {
|
775
|
+
AST_NODE_T* child = array_get(erb_case_match_node->conditions, i);
|
776
|
+
if (child) { ast_node_free(child); }
|
777
|
+
}
|
778
|
+
|
779
|
+
array_free(&erb_case_match_node->conditions);
|
780
|
+
}
|
781
|
+
ast_node_free((AST_NODE_T*) erb_case_match_node->else_clause);
|
782
|
+
ast_node_free((AST_NODE_T*) erb_case_match_node->end_node);
|
783
|
+
|
784
|
+
ast_free_base_node(&erb_case_match_node->base);
|
785
|
+
}
|
786
|
+
|
712
787
|
static void ast_free_erb_while_node(AST_ERB_WHILE_NODE_T* erb_while_node) {
|
713
788
|
if (erb_while_node->tag_opening != NULL) { token_free(erb_while_node->tag_opening); }
|
714
789
|
if (erb_while_node->content != NULL) { token_free(erb_while_node->content); }
|
@@ -831,6 +906,30 @@ static void ast_free_erb_unless_node(AST_ERB_UNLESS_NODE_T* erb_unless_node) {
|
|
831
906
|
ast_free_base_node(&erb_unless_node->base);
|
832
907
|
}
|
833
908
|
|
909
|
+
static void ast_free_erb_yield_node(AST_ERB_YIELD_NODE_T* erb_yield_node) {
|
910
|
+
if (erb_yield_node->tag_opening != NULL) { token_free(erb_yield_node->tag_opening); }
|
911
|
+
if (erb_yield_node->content != NULL) { token_free(erb_yield_node->content); }
|
912
|
+
if (erb_yield_node->tag_closing != NULL) { token_free(erb_yield_node->tag_closing); }
|
913
|
+
|
914
|
+
ast_free_base_node(&erb_yield_node->base);
|
915
|
+
}
|
916
|
+
|
917
|
+
static void ast_free_erb_in_node(AST_ERB_IN_NODE_T* erb_in_node) {
|
918
|
+
if (erb_in_node->tag_opening != NULL) { token_free(erb_in_node->tag_opening); }
|
919
|
+
if (erb_in_node->content != NULL) { token_free(erb_in_node->content); }
|
920
|
+
if (erb_in_node->tag_closing != NULL) { token_free(erb_in_node->tag_closing); }
|
921
|
+
if (erb_in_node->statements != NULL) {
|
922
|
+
for (size_t i = 0; i < array_size(erb_in_node->statements); i++) {
|
923
|
+
AST_NODE_T* child = array_get(erb_in_node->statements, i);
|
924
|
+
if (child) { ast_node_free(child); }
|
925
|
+
}
|
926
|
+
|
927
|
+
array_free(&erb_in_node->statements);
|
928
|
+
}
|
929
|
+
|
930
|
+
ast_free_base_node(&erb_in_node->base);
|
931
|
+
}
|
932
|
+
|
834
933
|
void ast_node_free(AST_NODE_T* node) {
|
835
934
|
if (!node) { return; }
|
836
935
|
|
@@ -855,6 +954,7 @@ void ast_node_free(AST_NODE_T* node) {
|
|
855
954
|
case AST_ERB_BLOCK_NODE: ast_free_erb_block_node((AST_ERB_BLOCK_NODE_T*) node); break;
|
856
955
|
case AST_ERB_WHEN_NODE: ast_free_erb_when_node((AST_ERB_WHEN_NODE_T*) node); break;
|
857
956
|
case AST_ERB_CASE_NODE: ast_free_erb_case_node((AST_ERB_CASE_NODE_T*) node); break;
|
957
|
+
case AST_ERB_CASE_MATCH_NODE: ast_free_erb_case_match_node((AST_ERB_CASE_MATCH_NODE_T*) node); break;
|
858
958
|
case AST_ERB_WHILE_NODE: ast_free_erb_while_node((AST_ERB_WHILE_NODE_T*) node); break;
|
859
959
|
case AST_ERB_UNTIL_NODE: ast_free_erb_until_node((AST_ERB_UNTIL_NODE_T*) node); break;
|
860
960
|
case AST_ERB_FOR_NODE: ast_free_erb_for_node((AST_ERB_FOR_NODE_T*) node); break;
|
@@ -862,5 +962,7 @@ void ast_node_free(AST_NODE_T* node) {
|
|
862
962
|
case AST_ERB_ENSURE_NODE: ast_free_erb_ensure_node((AST_ERB_ENSURE_NODE_T*) node); break;
|
863
963
|
case AST_ERB_BEGIN_NODE: ast_free_erb_begin_node((AST_ERB_BEGIN_NODE_T*) node); break;
|
864
964
|
case AST_ERB_UNLESS_NODE: ast_free_erb_unless_node((AST_ERB_UNLESS_NODE_T*) node); break;
|
965
|
+
case AST_ERB_YIELD_NODE: ast_free_erb_yield_node((AST_ERB_YIELD_NODE_T*) node); break;
|
966
|
+
case AST_ERB_IN_NODE: ast_free_erb_in_node((AST_ERB_IN_NODE_T*) node); break;
|
865
967
|
}
|
866
968
|
}
|
data/src/ast_pretty_print.c
CHANGED
@@ -364,6 +364,47 @@ void ast_pretty_print_node(AST_NODE_T* node, const size_t indent, const size_t r
|
|
364
364
|
|
365
365
|
} break;
|
366
366
|
|
367
|
+
case AST_ERB_CASE_MATCH_NODE: {
|
368
|
+
const AST_ERB_CASE_MATCH_NODE_T* erb_case_match_node = (AST_ERB_CASE_MATCH_NODE_T*) node;
|
369
|
+
|
370
|
+
pretty_print_errors(node, indent, relative_indent, false, buffer);
|
371
|
+
pretty_print_token_property(erb_case_match_node->tag_opening, "tag_opening", indent, relative_indent, false, buffer);
|
372
|
+
pretty_print_token_property(erb_case_match_node->content, "content", indent, relative_indent, false, buffer);
|
373
|
+
pretty_print_token_property(erb_case_match_node->tag_closing, "tag_closing", indent, relative_indent, false, buffer);
|
374
|
+
pretty_print_array("children", erb_case_match_node->children, indent, relative_indent, false, buffer);
|
375
|
+
pretty_print_array("conditions", erb_case_match_node->conditions, indent, relative_indent, false, buffer);
|
376
|
+
|
377
|
+
pretty_print_label("else_clause", indent, relative_indent, false, buffer);
|
378
|
+
|
379
|
+
if (erb_case_match_node->else_clause) {
|
380
|
+
buffer_append(buffer, "\n");
|
381
|
+
pretty_print_indent(buffer, indent);
|
382
|
+
pretty_print_indent(buffer, relative_indent + 1);
|
383
|
+
|
384
|
+
buffer_append(buffer, "└── ");
|
385
|
+
ast_pretty_print_node((AST_NODE_T*) erb_case_match_node->else_clause, indent, relative_indent + 2, buffer);
|
386
|
+
} else {
|
387
|
+
buffer_append(buffer, " ∅\n");
|
388
|
+
}
|
389
|
+
buffer_append(buffer, "\n");
|
390
|
+
|
391
|
+
|
392
|
+
pretty_print_label("end_node", indent, relative_indent, true, buffer);
|
393
|
+
|
394
|
+
if (erb_case_match_node->end_node) {
|
395
|
+
buffer_append(buffer, "\n");
|
396
|
+
pretty_print_indent(buffer, indent);
|
397
|
+
pretty_print_indent(buffer, relative_indent + 1);
|
398
|
+
|
399
|
+
buffer_append(buffer, "└── ");
|
400
|
+
ast_pretty_print_node((AST_NODE_T*) erb_case_match_node->end_node, indent, relative_indent + 2, buffer);
|
401
|
+
} else {
|
402
|
+
buffer_append(buffer, " ∅\n");
|
403
|
+
}
|
404
|
+
buffer_append(buffer, "\n");
|
405
|
+
|
406
|
+
} break;
|
407
|
+
|
367
408
|
case AST_ERB_WHILE_NODE: {
|
368
409
|
const AST_ERB_WHILE_NODE_T* erb_while_node = (AST_ERB_WHILE_NODE_T*) node;
|
369
410
|
|
@@ -584,5 +625,24 @@ void ast_pretty_print_node(AST_NODE_T* node, const size_t indent, const size_t r
|
|
584
625
|
|
585
626
|
} break;
|
586
627
|
|
628
|
+
case AST_ERB_YIELD_NODE: {
|
629
|
+
const AST_ERB_YIELD_NODE_T* erb_yield_node = (AST_ERB_YIELD_NODE_T*) node;
|
630
|
+
|
631
|
+
pretty_print_errors(node, indent, relative_indent, false, buffer);
|
632
|
+
pretty_print_token_property(erb_yield_node->tag_opening, "tag_opening", indent, relative_indent, false, buffer);
|
633
|
+
pretty_print_token_property(erb_yield_node->content, "content", indent, relative_indent, false, buffer);
|
634
|
+
pretty_print_token_property(erb_yield_node->tag_closing, "tag_closing", indent, relative_indent, true, buffer);
|
635
|
+
} break;
|
636
|
+
|
637
|
+
case AST_ERB_IN_NODE: {
|
638
|
+
const AST_ERB_IN_NODE_T* erb_in_node = (AST_ERB_IN_NODE_T*) node;
|
639
|
+
|
640
|
+
pretty_print_errors(node, indent, relative_indent, false, buffer);
|
641
|
+
pretty_print_token_property(erb_in_node->tag_opening, "tag_opening", indent, relative_indent, false, buffer);
|
642
|
+
pretty_print_token_property(erb_in_node->content, "content", indent, relative_indent, false, buffer);
|
643
|
+
pretty_print_token_property(erb_in_node->tag_closing, "tag_closing", indent, relative_indent, false, buffer);
|
644
|
+
pretty_print_array("statements", erb_in_node->statements, indent, relative_indent, true, buffer);
|
645
|
+
} break;
|
646
|
+
|
587
647
|
}
|
588
648
|
}
|
data/src/buffer.c
CHANGED
@@ -10,7 +10,7 @@
|
|
10
10
|
bool buffer_init(buffer_T* buffer) {
|
11
11
|
buffer->capacity = 1024;
|
12
12
|
buffer->length = 0;
|
13
|
-
buffer->value = nullable_safe_malloc(buffer->capacity * sizeof(char));
|
13
|
+
buffer->value = nullable_safe_malloc((buffer->capacity + 1) * sizeof(char));
|
14
14
|
|
15
15
|
if (!buffer->value) {
|
16
16
|
fprintf(stderr, "Error: Failed to initialize buffer with capacity of %zu.\n", buffer->capacity);
|
@@ -50,32 +50,39 @@ size_t buffer_sizeof(void) {
|
|
50
50
|
* or null termination.
|
51
51
|
*
|
52
52
|
* @param buffer The buffer to increase capacity for
|
53
|
-
* @param
|
54
|
-
* @return true if capacity
|
55
|
-
* false if reallocation failed
|
53
|
+
* @param additional_capacity The additional length needed beyond current buffer capacity
|
54
|
+
* @return true if capacity was increased, false if reallocation failed
|
56
55
|
*/
|
57
|
-
bool buffer_increase_capacity(buffer_T* buffer, const size_t
|
58
|
-
if (
|
56
|
+
bool buffer_increase_capacity(buffer_T* buffer, const size_t additional_capacity) {
|
57
|
+
if (additional_capacity + 1 >= SIZE_MAX) {
|
59
58
|
fprintf(stderr, "Error: Buffer capacity would overflow system limits.\n");
|
60
|
-
|
59
|
+
exit(1);
|
61
60
|
}
|
62
61
|
|
63
|
-
const size_t
|
64
|
-
|
65
|
-
if (buffer->capacity >= required_capacity) { return true; }
|
62
|
+
const size_t new_capacity = buffer->capacity + additional_capacity;
|
66
63
|
|
67
|
-
|
68
|
-
|
69
|
-
new_capacity = required_capacity + 1024;
|
64
|
+
return buffer_resize(buffer, new_capacity);
|
65
|
+
}
|
70
66
|
|
71
|
-
|
72
|
-
|
73
|
-
|
67
|
+
/**
|
68
|
+
* Resizes the capacity of the buffer to the specified new capacity.
|
69
|
+
*
|
70
|
+
* @param buffer The buffer to resize
|
71
|
+
* @param new_capacity The new capacity to resize the buffer to
|
72
|
+
* @return true if capacity was resized, false if reallocation failed
|
73
|
+
*/
|
74
|
+
bool buffer_resize(buffer_T* buffer, const size_t new_capacity) {
|
75
|
+
if (new_capacity + 1 >= SIZE_MAX) {
|
76
|
+
fprintf(stderr, "Error: Buffer capacity would overflow system limits.\n");
|
77
|
+
exit(1);
|
74
78
|
}
|
75
79
|
|
76
|
-
char* new_value =
|
80
|
+
char* new_value = nullable_safe_realloc(buffer->value, new_capacity + 1);
|
77
81
|
|
78
|
-
if (unlikely(new_value == NULL)) {
|
82
|
+
if (unlikely(new_value == NULL)) {
|
83
|
+
fprintf(stderr, "Error: Failed to resize buffer to %zu.\n", new_capacity);
|
84
|
+
exit(1);
|
85
|
+
}
|
79
86
|
|
80
87
|
buffer->value = new_value;
|
81
88
|
buffer->capacity = new_capacity;
|
@@ -83,6 +90,33 @@ bool buffer_increase_capacity(buffer_T* buffer, const size_t required_length) {
|
|
83
90
|
return true;
|
84
91
|
}
|
85
92
|
|
93
|
+
/**
|
94
|
+
* Expands the capacity of the buffer by doubling its current capacity.
|
95
|
+
* This function is a convenience function that calls buffer_increase_capacity
|
96
|
+
* with a factor of 2.
|
97
|
+
*
|
98
|
+
* @param buffer The buffer to expand capacity for
|
99
|
+
* @return true if capacity was increased, false if reallocation failed
|
100
|
+
*/
|
101
|
+
bool buffer_expand_capacity(buffer_T* buffer) {
|
102
|
+
return buffer_resize(buffer, buffer->capacity * 2);
|
103
|
+
}
|
104
|
+
|
105
|
+
/**
|
106
|
+
* Expands the capacity of the buffer if needed to accommodate additional content.
|
107
|
+
* This function is a convenience function that calls buffer_has_capacity and
|
108
|
+
* buffer_expand_capacity.
|
109
|
+
*
|
110
|
+
* @param buffer The buffer to expand capacity for
|
111
|
+
* @param required_length The additional length needed beyond current buffer capacity
|
112
|
+
* @return true if capacity was increased, false if reallocation failed
|
113
|
+
*/
|
114
|
+
bool buffer_expand_if_needed(buffer_T* buffer, const size_t required_length) {
|
115
|
+
if (buffer_has_capacity(buffer, required_length)) { return true; }
|
116
|
+
|
117
|
+
return buffer_resize(buffer, buffer->capacity + (required_length * 2));
|
118
|
+
}
|
119
|
+
|
86
120
|
/**
|
87
121
|
* Appends a null-terminated string to the buffer.
|
88
122
|
* @note This function requires that 'text' is a properly null-terminated string.
|
@@ -99,7 +133,7 @@ void buffer_append(buffer_T* buffer, const char* text) {
|
|
99
133
|
|
100
134
|
size_t text_length = strlen(text);
|
101
135
|
|
102
|
-
if (!
|
136
|
+
if (!buffer_expand_if_needed(buffer, text_length)) { return; }
|
103
137
|
|
104
138
|
memcpy(buffer->value + buffer->length, text, text_length);
|
105
139
|
buffer->length += text_length;
|
@@ -120,7 +154,7 @@ void buffer_append(buffer_T* buffer, const char* text) {
|
|
120
154
|
*/
|
121
155
|
void buffer_append_with_length(buffer_T* buffer, const char* text, const size_t length) {
|
122
156
|
if (!buffer || !text || length == 0) { return; }
|
123
|
-
if (!
|
157
|
+
if (!buffer_expand_if_needed(buffer, length)) { return; }
|
124
158
|
|
125
159
|
memcpy(buffer->value + buffer->length, text, length);
|
126
160
|
|
@@ -161,7 +195,7 @@ void buffer_prepend(buffer_T* buffer, const char* text) {
|
|
161
195
|
|
162
196
|
size_t text_length = strlen(text);
|
163
197
|
|
164
|
-
if (!
|
198
|
+
if (!buffer_expand_if_needed(buffer, text_length)) { return; }
|
165
199
|
|
166
200
|
memmove(buffer->value + text_length, buffer->value, buffer->length + 1);
|
167
201
|
memcpy(buffer->value, text, text_length);
|
@@ -171,17 +205,16 @@ void buffer_prepend(buffer_T* buffer, const char* text) {
|
|
171
205
|
|
172
206
|
void buffer_concat(buffer_T* destination, buffer_T* source) {
|
173
207
|
if (source->length == 0) { return; }
|
174
|
-
if (!
|
208
|
+
if (!buffer_expand_if_needed(destination, source->length)) { return; }
|
175
209
|
|
176
210
|
memcpy(destination->value + destination->length, source->value, source->length);
|
211
|
+
|
177
212
|
destination->length += source->length;
|
178
213
|
destination->value[destination->length] = '\0';
|
179
214
|
}
|
180
215
|
|
181
|
-
bool
|
182
|
-
|
183
|
-
|
184
|
-
return buffer_increase_capacity(buffer, required_length);
|
216
|
+
bool buffer_has_capacity(buffer_T* buffer, const size_t required_length) {
|
217
|
+
return (buffer->length + required_length <= buffer->capacity);
|
185
218
|
}
|
186
219
|
|
187
220
|
void buffer_clear(buffer_T* buffer) {
|
@@ -192,7 +225,7 @@ void buffer_clear(buffer_T* buffer) {
|
|
192
225
|
void buffer_free(buffer_T* buffer) {
|
193
226
|
if (!buffer) { return; }
|
194
227
|
|
195
|
-
free(buffer->value);
|
228
|
+
if (buffer->value != NULL) { free(buffer->value); }
|
196
229
|
|
197
230
|
buffer->value = NULL;
|
198
231
|
buffer->length = buffer->capacity = 0;
|
data/src/extract.c
CHANGED
@@ -7,25 +7,28 @@
|
|
7
7
|
#include <stdlib.h>
|
8
8
|
|
9
9
|
void herb_extract_ruby_to_buffer_with_semicolons(const char* source, buffer_T* output) {
|
10
|
-
|
10
|
+
array_T* tokens = herb_lex(source);
|
11
|
+
bool skip_erb_content = false;
|
11
12
|
|
12
13
|
for (size_t i = 0; i < array_size(tokens); i++) {
|
13
14
|
const token_T* token = array_get(tokens, i);
|
14
15
|
|
15
16
|
switch (token->type) {
|
16
|
-
case TOKEN_NEWLINE:
|
17
|
-
|
18
|
-
case TOKEN_ERB_END: {
|
19
|
-
buffer_append_char(output, ';');
|
20
|
-
buffer_append_whitespace(output, range_length(token->range) - 1);
|
17
|
+
case TOKEN_NEWLINE: {
|
18
|
+
buffer_append(output, token->value);
|
21
19
|
break;
|
22
20
|
}
|
23
21
|
|
24
22
|
case TOKEN_ERB_START: {
|
25
|
-
if (strcmp(token->value, "<%#") == 0) {
|
26
|
-
|
27
|
-
|
28
|
-
|
23
|
+
if (strcmp(token->value, "<%#") == 0) { skip_erb_content = true; }
|
24
|
+
|
25
|
+
buffer_append_whitespace(output, range_length(token->range));
|
26
|
+
break;
|
27
|
+
}
|
28
|
+
|
29
|
+
case TOKEN_ERB_CONTENT: {
|
30
|
+
if (skip_erb_content == false) {
|
31
|
+
buffer_append(output, token->value);
|
29
32
|
} else {
|
30
33
|
buffer_append_whitespace(output, range_length(token->range));
|
31
34
|
}
|
@@ -33,25 +36,46 @@ void herb_extract_ruby_to_buffer_with_semicolons(const char* source, buffer_T* o
|
|
33
36
|
break;
|
34
37
|
}
|
35
38
|
|
36
|
-
|
39
|
+
case TOKEN_ERB_END: {
|
40
|
+
skip_erb_content = false;
|
41
|
+
|
42
|
+
buffer_append_char(output, ';');
|
43
|
+
buffer_append_whitespace(output, range_length(token->range) - 1);
|
44
|
+
break;
|
45
|
+
}
|
46
|
+
|
47
|
+
default: {
|
48
|
+
buffer_append_whitespace(output, range_length(token->range));
|
49
|
+
}
|
37
50
|
}
|
38
51
|
}
|
52
|
+
|
53
|
+
herb_free_tokens(&tokens);
|
39
54
|
}
|
40
55
|
|
41
56
|
void herb_extract_ruby_to_buffer(const char* source, buffer_T* output) {
|
42
|
-
|
57
|
+
array_T* tokens = herb_lex(source);
|
58
|
+
bool skip_erb_content = false;
|
43
59
|
|
44
60
|
for (size_t i = 0; i < array_size(tokens); i++) {
|
45
61
|
const token_T* token = array_get(tokens, i);
|
46
62
|
|
47
63
|
switch (token->type) {
|
48
|
-
case TOKEN_NEWLINE:
|
49
|
-
|
64
|
+
case TOKEN_NEWLINE: {
|
65
|
+
buffer_append(output, token->value);
|
66
|
+
break;
|
67
|
+
}
|
68
|
+
|
50
69
|
case TOKEN_ERB_START: {
|
51
|
-
if (strcmp(token->value, "<%#") == 0) {
|
52
|
-
|
53
|
-
|
54
|
-
|
70
|
+
if (strcmp(token->value, "<%#") == 0) { skip_erb_content = true; }
|
71
|
+
|
72
|
+
buffer_append_whitespace(output, range_length(token->range));
|
73
|
+
break;
|
74
|
+
}
|
75
|
+
|
76
|
+
case TOKEN_ERB_CONTENT: {
|
77
|
+
if (skip_erb_content == false) {
|
78
|
+
buffer_append(output, token->value);
|
55
79
|
} else {
|
56
80
|
buffer_append_whitespace(output, range_length(token->range));
|
57
81
|
}
|
@@ -59,13 +83,24 @@ void herb_extract_ruby_to_buffer(const char* source, buffer_T* output) {
|
|
59
83
|
break;
|
60
84
|
}
|
61
85
|
|
62
|
-
|
86
|
+
case TOKEN_ERB_END: {
|
87
|
+
skip_erb_content = false;
|
88
|
+
|
89
|
+
buffer_append_whitespace(output, range_length(token->range));
|
90
|
+
break;
|
91
|
+
}
|
92
|
+
|
93
|
+
default: {
|
94
|
+
buffer_append_whitespace(output, range_length(token->range));
|
95
|
+
}
|
63
96
|
}
|
64
97
|
}
|
98
|
+
|
99
|
+
herb_free_tokens(&tokens);
|
65
100
|
}
|
66
101
|
|
67
102
|
void herb_extract_html_to_buffer(const char* source, buffer_T* output) {
|
68
|
-
|
103
|
+
array_T* tokens = herb_lex(source);
|
69
104
|
|
70
105
|
for (size_t i = 0; i < array_size(tokens); i++) {
|
71
106
|
const token_T* token = array_get(tokens, i);
|
@@ -77,6 +112,8 @@ void herb_extract_html_to_buffer(const char* source, buffer_T* output) {
|
|
77
112
|
default: buffer_append(output, token->value);
|
78
113
|
}
|
79
114
|
}
|
115
|
+
|
116
|
+
herb_free_tokens(&tokens);
|
80
117
|
}
|
81
118
|
|
82
119
|
char* herb_extract_ruby_with_semicolons(const char* source) {
|
data/src/include/analyze.h
CHANGED
@@ -17,7 +17,9 @@ typedef enum {
|
|
17
17
|
CONTROL_TYPE_ELSE,
|
18
18
|
CONTROL_TYPE_END,
|
19
19
|
CONTROL_TYPE_CASE,
|
20
|
+
CONTROL_TYPE_CASE_MATCH,
|
20
21
|
CONTROL_TYPE_WHEN,
|
22
|
+
CONTROL_TYPE_IN,
|
21
23
|
CONTROL_TYPE_BEGIN,
|
22
24
|
CONTROL_TYPE_RESCUE,
|
23
25
|
CONTROL_TYPE_ENSURE,
|
@@ -27,6 +29,7 @@ typedef enum {
|
|
27
29
|
CONTROL_TYPE_FOR,
|
28
30
|
CONTROL_TYPE_BLOCK,
|
29
31
|
CONTROL_TYPE_BLOCK_CLOSE,
|
32
|
+
CONTROL_TYPE_YIELD,
|
30
33
|
CONTROL_TYPE_UNKNOWN
|
31
34
|
} control_type_t;
|
32
35
|
|
@@ -13,7 +13,9 @@ bool has_end(analyzed_ruby_T* analyzed);
|
|
13
13
|
bool has_block_node(analyzed_ruby_T* analyzed);
|
14
14
|
bool has_block_closing(analyzed_ruby_T* analyzed);
|
15
15
|
bool has_case_node(analyzed_ruby_T* analyzed);
|
16
|
+
bool has_case_match_node(analyzed_ruby_T* analyzed);
|
16
17
|
bool has_when_node(analyzed_ruby_T* analyzed);
|
18
|
+
bool has_in_node(analyzed_ruby_T* analyzed);
|
17
19
|
bool has_for_node(analyzed_ruby_T* analyzed);
|
18
20
|
bool has_while_node(analyzed_ruby_T* analyzed);
|
19
21
|
bool has_until_node(analyzed_ruby_T* analyzed);
|
@@ -21,12 +23,14 @@ bool has_begin_node(analyzed_ruby_T* analyzed);
|
|
21
23
|
bool has_rescue_node(analyzed_ruby_T* analyzed);
|
22
24
|
bool has_ensure_node(analyzed_ruby_T* analyzed);
|
23
25
|
bool has_unless_node(analyzed_ruby_T* analyzed);
|
26
|
+
bool has_yield_node(analyzed_ruby_T* analyzed);
|
24
27
|
|
25
28
|
bool has_error_message(analyzed_ruby_T* anlayzed, const char* message);
|
26
29
|
|
27
30
|
bool search_if_nodes(const pm_node_t* node, void* data);
|
28
31
|
bool search_block_nodes(const pm_node_t* node, void* data);
|
29
32
|
bool search_case_nodes(const pm_node_t* node, void* data);
|
33
|
+
bool search_case_match_nodes(const pm_node_t* node, void* data);
|
30
34
|
bool search_while_nodes(const pm_node_t* node, void* data);
|
31
35
|
bool search_for_nodes(const pm_node_t* node, void* data);
|
32
36
|
bool search_until_nodes(const pm_node_t* node, void* data);
|
@@ -37,7 +41,9 @@ bool search_else_nodes(analyzed_ruby_T* analyzed);
|
|
37
41
|
bool search_end_nodes(analyzed_ruby_T* analyzed);
|
38
42
|
bool search_block_closing_nodes(analyzed_ruby_T* analyzed);
|
39
43
|
bool search_when_nodes(analyzed_ruby_T* analyzed);
|
44
|
+
bool search_in_nodes(analyzed_ruby_T* analyzed);
|
40
45
|
bool search_rescue_nodes(analyzed_ruby_T* analyzed);
|
41
46
|
bool search_ensure_nodes(analyzed_ruby_T* analyzed);
|
47
|
+
bool search_yield_nodes(analyzed_ruby_T* analyzed);
|
42
48
|
|
43
49
|
#endif
|
data/src/include/analyzed_ruby.h
CHANGED
@@ -17,7 +17,9 @@ typedef struct ANALYZED_RUBY_STRUCT {
|
|
17
17
|
bool has_block_closing;
|
18
18
|
bool has_block_node;
|
19
19
|
bool has_case_node;
|
20
|
+
bool has_case_match_node;
|
20
21
|
bool has_when_node;
|
22
|
+
bool has_in_node;
|
21
23
|
bool has_for_node;
|
22
24
|
bool has_while_node;
|
23
25
|
bool has_until_node;
|
@@ -25,6 +27,7 @@ typedef struct ANALYZED_RUBY_STRUCT {
|
|
25
27
|
bool has_rescue_node;
|
26
28
|
bool has_ensure_node;
|
27
29
|
bool has_unless_node;
|
30
|
+
bool has_yield_node;
|
28
31
|
} analyzed_ruby_T;
|
29
32
|
|
30
33
|
analyzed_ruby_T* init_analyzed_ruby(char* source);
|