herb 0.9.1-aarch64-linux-gnu → 0.9.3-aarch64-linux-gnu

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.
Files changed (171) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -0
  3. data/config.yml +125 -0
  4. data/ext/herb/error_helpers.c +172 -2
  5. data/ext/herb/extconf.rb +6 -0
  6. data/ext/herb/extension.c +16 -2
  7. data/ext/herb/extension_helpers.c +6 -5
  8. data/ext/herb/extension_helpers.h +4 -4
  9. data/ext/herb/nodes.c +89 -3
  10. data/lib/herb/3.0/herb.so +0 -0
  11. data/lib/herb/3.1/herb.so +0 -0
  12. data/lib/herb/3.2/herb.so +0 -0
  13. data/lib/herb/3.3/herb.so +0 -0
  14. data/lib/herb/3.4/herb.so +0 -0
  15. data/lib/herb/4.0/herb.so +0 -0
  16. data/lib/herb/ast/erb_content_node.rb +32 -0
  17. data/lib/herb/ast/nodes.rb +244 -3
  18. data/lib/herb/cli.rb +12 -2
  19. data/lib/herb/engine/compiler.rb +136 -97
  20. data/lib/herb/engine/validators/security_validator.rb +40 -0
  21. data/lib/herb/engine.rb +21 -0
  22. data/lib/herb/errors.rb +268 -0
  23. data/lib/herb/parser_options.rb +7 -2
  24. data/lib/herb/version.rb +1 -1
  25. data/lib/herb/visitor.rb +82 -0
  26. data/lib/herb.rb +1 -0
  27. data/sig/herb/ast/erb_content_node.rbs +13 -0
  28. data/sig/herb/ast/nodes.rbs +98 -2
  29. data/sig/herb/engine/compiler.rbs +18 -3
  30. data/sig/herb/engine/validators/security_validator.rbs +4 -0
  31. data/sig/herb/engine.rbs +4 -0
  32. data/sig/herb/errors.rbs +122 -0
  33. data/sig/herb/parser_options.rbs +6 -2
  34. data/sig/herb/visitor.rbs +12 -0
  35. data/sig/serialized_ast_errors.rbs +29 -0
  36. data/sig/serialized_ast_nodes.rbs +19 -0
  37. data/src/analyze/action_view/attribute_extraction_helpers.c +425 -87
  38. data/src/analyze/action_view/image_tag.c +87 -0
  39. data/src/analyze/action_view/javascript_include_tag.c +102 -0
  40. data/src/analyze/action_view/javascript_tag.c +55 -0
  41. data/src/analyze/action_view/registry.c +10 -3
  42. data/src/analyze/action_view/tag.c +19 -2
  43. data/src/analyze/action_view/tag_helper_node_builders.c +119 -37
  44. data/src/analyze/action_view/tag_helpers.c +1033 -32
  45. data/src/analyze/analyze.c +165 -10
  46. data/src/analyze/{helpers.c → analyze_helpers.c} +1 -1
  47. data/src/analyze/analyzed_ruby.c +1 -1
  48. data/src/analyze/builders.c +11 -8
  49. data/src/analyze/conditional_elements.c +6 -7
  50. data/src/analyze/conditional_open_tags.c +6 -7
  51. data/src/analyze/control_type.c +4 -2
  52. data/src/analyze/invalid_structures.c +5 -5
  53. data/src/analyze/missing_end.c +2 -2
  54. data/src/analyze/parse_errors.c +5 -5
  55. data/src/analyze/prism_annotate.c +7 -7
  56. data/src/analyze/render_nodes.c +6 -26
  57. data/src/analyze/strict_locals.c +637 -0
  58. data/src/analyze/transform.c +7 -0
  59. data/src/{ast_node.c → ast/ast_node.c} +8 -8
  60. data/src/{ast_nodes.c → ast/ast_nodes.c} +82 -11
  61. data/src/{ast_pretty_print.c → ast/ast_pretty_print.c} +113 -9
  62. data/src/{pretty_print.c → ast/pretty_print.c} +9 -9
  63. data/src/errors.c +398 -8
  64. data/src/extract.c +5 -5
  65. data/src/herb.c +15 -5
  66. data/src/include/analyze/action_view/attribute_extraction_helpers.h +3 -1
  67. data/src/include/analyze/action_view/tag_helper_handler.h +3 -3
  68. data/src/include/analyze/action_view/tag_helper_node_builders.h +34 -5
  69. data/src/include/analyze/action_view/tag_helpers.h +4 -3
  70. data/src/include/analyze/analyze.h +6 -4
  71. data/src/include/analyze/analyzed_ruby.h +2 -2
  72. data/src/include/analyze/builders.h +4 -4
  73. data/src/include/analyze/conditional_elements.h +2 -2
  74. data/src/include/analyze/conditional_open_tags.h +2 -2
  75. data/src/include/analyze/control_type.h +1 -1
  76. data/src/include/analyze/helpers.h +2 -2
  77. data/src/include/analyze/invalid_structures.h +1 -1
  78. data/src/include/analyze/prism_annotate.h +2 -2
  79. data/src/include/analyze/render_nodes.h +1 -1
  80. data/src/include/analyze/strict_locals.h +11 -0
  81. data/src/include/{ast_node.h → ast/ast_node.h} +4 -4
  82. data/src/include/{ast_nodes.h → ast/ast_nodes.h} +38 -14
  83. data/src/include/{ast_pretty_print.h → ast/ast_pretty_print.h} +3 -3
  84. data/src/include/{pretty_print.h → ast/pretty_print.h} +4 -4
  85. data/src/include/errors.h +65 -7
  86. data/src/include/extract.h +2 -2
  87. data/src/include/herb.h +5 -5
  88. data/src/include/{lex_helpers.h → lexer/lex_helpers.h} +5 -5
  89. data/src/include/{lexer.h → lexer/lexer.h} +1 -1
  90. data/src/include/{lexer_peek_helpers.h → lexer/lexer_peek_helpers.h} +2 -2
  91. data/src/include/{lexer_struct.h → lexer/lexer_struct.h} +2 -2
  92. data/src/include/{token.h → lexer/token.h} +3 -3
  93. data/src/include/{token_matchers.h → lexer/token_matchers.h} +1 -1
  94. data/src/include/{token_struct.h → lexer/token_struct.h} +3 -3
  95. data/src/include/{util → lib}/hb_foreach.h +1 -1
  96. data/src/include/{util → lib}/hb_string.h +5 -1
  97. data/src/include/{location.h → location/location.h} +1 -1
  98. data/src/include/parser/dot_notation.h +12 -0
  99. data/src/include/{parser.h → parser/parser.h} +11 -4
  100. data/src/include/{parser_helpers.h → parser/parser_helpers.h} +6 -6
  101. data/src/include/{prism_context.h → prism/prism_context.h} +2 -2
  102. data/src/include/{prism_helpers.h → prism/prism_helpers.h} +6 -6
  103. data/src/include/{html_util.h → util/html_util.h} +2 -1
  104. data/src/include/util/ruby_util.h +9 -0
  105. data/src/include/{utf8.h → util/utf8.h} +1 -1
  106. data/src/include/{util.h → util/util.h} +1 -1
  107. data/src/include/version.h +1 -1
  108. data/src/include/visitor.h +3 -3
  109. data/src/{lexer_peek_helpers.c → lexer/lexer_peek_helpers.c} +3 -3
  110. data/src/{token.c → lexer/token.c} +8 -8
  111. data/src/{token_matchers.c → lexer/token_matchers.c} +2 -2
  112. data/src/lexer.c +6 -6
  113. data/src/{util → lib}/hb_allocator.c +2 -2
  114. data/src/{util → lib}/hb_arena.c +4 -8
  115. data/src/{util → lib}/hb_arena_debug.c +2 -2
  116. data/src/{util → lib}/hb_array.c +2 -2
  117. data/src/{util → lib}/hb_buffer.c +2 -2
  118. data/src/{util → lib}/hb_narray.c +1 -1
  119. data/src/{util → lib}/hb_string.c +2 -2
  120. data/src/{location.c → location/location.c} +2 -2
  121. data/src/{position.c → location/position.c} +2 -2
  122. data/src/{range.c → location/range.c} +1 -1
  123. data/src/main.c +11 -11
  124. data/src/parser/dot_notation.c +100 -0
  125. data/src/{parser_match_tags.c → parser/match_tags.c} +34 -5
  126. data/src/{parser_helpers.c → parser/parser_helpers.c} +10 -10
  127. data/src/parser.c +68 -32
  128. data/src/{prism_helpers.c → prism/prism_helpers.c} +7 -7
  129. data/src/{ruby_parser.c → prism/ruby_parser.c} +1 -1
  130. data/src/{html_util.c → util/html_util.c} +54 -4
  131. data/src/{io.c → util/io.c} +3 -3
  132. data/src/util/ruby_util.c +42 -0
  133. data/src/{utf8.c → util/utf8.c} +2 -2
  134. data/src/{util.c → util/util.c} +4 -4
  135. data/src/visitor.c +35 -3
  136. data/templates/ext/herb/error_helpers.c.erb +2 -2
  137. data/templates/ext/herb/nodes.c.erb +1 -1
  138. data/templates/java/error_helpers.c.erb +1 -1
  139. data/templates/java/error_helpers.h.erb +2 -2
  140. data/templates/java/nodes.c.erb +4 -4
  141. data/templates/java/nodes.h.erb +1 -1
  142. data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +4 -4
  143. data/templates/javascript/packages/node/extension/nodes.cpp.erb +4 -4
  144. data/templates/lib/herb/visitor.rb.erb +14 -0
  145. data/templates/src/analyze/missing_end.c.erb +2 -2
  146. data/templates/src/{ast_nodes.c.erb → ast/ast_nodes.c.erb} +9 -9
  147. data/templates/src/{ast_pretty_print.c.erb → ast/ast_pretty_print.c.erb} +8 -8
  148. data/templates/src/errors.c.erb +8 -8
  149. data/templates/src/include/{ast_nodes.h.erb → ast/ast_nodes.h.erb} +11 -12
  150. data/templates/src/include/{ast_pretty_print.h.erb → ast/ast_pretty_print.h.erb} +2 -2
  151. data/templates/src/include/errors.h.erb +7 -7
  152. data/templates/src/{parser_match_tags.c.erb → parser/match_tags.c.erb} +4 -4
  153. data/templates/src/visitor.c.erb +3 -3
  154. data/templates/wasm/error_helpers.cpp.erb +4 -4
  155. data/templates/wasm/nodes.cpp.erb +5 -5
  156. metadata +78 -68
  157. data/src/include/element_source.h +0 -10
  158. /data/src/include/{util → lib}/hb_allocator.h +0 -0
  159. /data/src/include/{util → lib}/hb_arena.h +0 -0
  160. /data/src/include/{util → lib}/hb_arena_debug.h +0 -0
  161. /data/src/include/{util → lib}/hb_array.h +0 -0
  162. /data/src/include/{util → lib}/hb_buffer.h +0 -0
  163. /data/src/include/{util → lib}/hb_narray.h +0 -0
  164. /data/src/include/{util → lib}/string.h +0 -0
  165. /data/src/include/{position.h → location/position.h} +0 -0
  166. /data/src/include/{range.h → location/range.h} +0 -0
  167. /data/src/include/{herb_prism_node.h → prism/herb_prism_node.h} +0 -0
  168. /data/src/include/{prism_serialized.h → prism/prism_serialized.h} +0 -0
  169. /data/src/include/{ruby_parser.h → prism/ruby_parser.h} +0 -0
  170. /data/src/include/{io.h → util/io.h} +0 -0
  171. /data/templates/src/include/{util → lib}/hb_foreach.h.erb +0 -0
@@ -0,0 +1,102 @@
1
+ #include "../../include/analyze/action_view/tag_helper_handler.h"
2
+ #include "../../include/lib/hb_buffer.h"
3
+
4
+ #include <prism.h>
5
+ #include <stdbool.h>
6
+ #include <stdlib.h>
7
+ #include <string.h>
8
+
9
+ bool detect_javascript_include_tag(pm_call_node_t* call_node, pm_parser_t* parser) {
10
+ if (!call_node || !call_node->name) { return false; }
11
+
12
+ pm_constant_t* constant = pm_constant_pool_id_to_constant(&parser->constant_pool, call_node->name);
13
+ return constant && constant->length == 22
14
+ && strncmp((const char*) constant->start, "javascript_include_tag", 22) == 0;
15
+ }
16
+
17
+ char* extract_javascript_include_tag_name(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
18
+ (void) call_node;
19
+ (void) parser;
20
+
21
+ return hb_allocator_strdup(allocator, "script");
22
+ }
23
+
24
+ char* extract_javascript_include_tag_content(
25
+ pm_call_node_t* call_node,
26
+ pm_parser_t* parser,
27
+ hb_allocator_T* allocator
28
+ ) {
29
+ (void) call_node;
30
+ (void) parser;
31
+ (void) allocator;
32
+
33
+ return NULL;
34
+ }
35
+
36
+ char* extract_javascript_include_tag_src(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
37
+ (void) parser;
38
+
39
+ if (!call_node || !call_node->arguments) { return NULL; }
40
+
41
+ pm_arguments_node_t* arguments = call_node->arguments;
42
+ if (!arguments->arguments.size) { return NULL; }
43
+
44
+ pm_node_t* first_argument = arguments->arguments.nodes[0];
45
+
46
+ if (first_argument->type == PM_STRING_NODE) {
47
+ pm_string_node_t* string_node = (pm_string_node_t*) first_argument;
48
+ size_t length = pm_string_length(&string_node->unescaped);
49
+
50
+ return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
51
+ }
52
+
53
+ size_t source_length = first_argument->location.end - first_argument->location.start;
54
+ return hb_allocator_strndup(allocator, (const char*) first_argument->location.start, source_length);
55
+ }
56
+
57
+ bool javascript_include_tag_source_is_url(const char* source, size_t length) {
58
+ if (!source || length == 0) { return false; }
59
+
60
+ if (length >= 2 && source[0] == '/' && source[1] == '/') { return true; }
61
+ if (strstr(source, "://") != NULL) { return true; }
62
+
63
+ return false;
64
+ }
65
+
66
+ char* wrap_in_javascript_path(
67
+ const char* source,
68
+ size_t source_length,
69
+ const char* path_options,
70
+ hb_allocator_T* allocator
71
+ ) {
72
+ hb_buffer_T buffer;
73
+ hb_buffer_init(&buffer, source_length + 32, allocator);
74
+
75
+ hb_buffer_append(&buffer, "javascript_path(");
76
+ hb_buffer_append_with_length(&buffer, source, source_length);
77
+
78
+ if (path_options && strlen(path_options) > 0) {
79
+ hb_buffer_append(&buffer, ", ");
80
+ hb_buffer_append(&buffer, path_options);
81
+ }
82
+
83
+ hb_buffer_append(&buffer, ")");
84
+
85
+ char* result = hb_allocator_strdup(allocator, hb_buffer_value(&buffer));
86
+ hb_buffer_free(&buffer);
87
+
88
+ return result;
89
+ }
90
+
91
+ bool javascript_include_tag_supports_block(void) {
92
+ return false;
93
+ }
94
+
95
+ const tag_helper_handler_T javascript_include_tag_handler = {
96
+ .name = "javascript_include_tag",
97
+ .source = HB_STRING_LITERAL("ActionView::Helpers::AssetTagHelper#javascript_include_tag"),
98
+ .detect = detect_javascript_include_tag,
99
+ .extract_tag_name = extract_javascript_include_tag_name,
100
+ .extract_content = extract_javascript_include_tag_content,
101
+ .supports_block = javascript_include_tag_supports_block
102
+ };
@@ -0,0 +1,55 @@
1
+ #include "../../include/analyze/action_view/tag_helper_handler.h"
2
+
3
+ #include <prism.h>
4
+ #include <stdbool.h>
5
+ #include <stdlib.h>
6
+ #include <string.h>
7
+
8
+ bool detect_javascript_tag(pm_call_node_t* call_node, pm_parser_t* parser) {
9
+ if (!call_node || !call_node->name) { return false; }
10
+
11
+ pm_constant_t* constant = pm_constant_pool_id_to_constant(&parser->constant_pool, call_node->name);
12
+ return constant && constant->length == 14 && strncmp((const char*) constant->start, "javascript_tag", 14) == 0;
13
+ }
14
+
15
+ char* extract_javascript_tag_name(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
16
+ (void) call_node;
17
+ (void) parser;
18
+
19
+ return hb_allocator_strdup(allocator, "script");
20
+ }
21
+
22
+ char* extract_javascript_tag_content(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
23
+ (void) parser;
24
+
25
+ if (!call_node || !call_node->arguments) { return NULL; }
26
+
27
+ pm_arguments_node_t* arguments = call_node->arguments;
28
+ if (!arguments->arguments.size) { return NULL; }
29
+
30
+ pm_node_t* first_argument = arguments->arguments.nodes[0];
31
+
32
+ if (first_argument->type == PM_KEYWORD_HASH_NODE) { return NULL; }
33
+
34
+ if (first_argument->type == PM_STRING_NODE) {
35
+ pm_string_node_t* string_node = (pm_string_node_t*) first_argument;
36
+ size_t length = pm_string_length(&string_node->unescaped);
37
+ return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
38
+ }
39
+
40
+ size_t source_length = first_argument->location.end - first_argument->location.start;
41
+ return hb_allocator_strndup(allocator, (const char*) first_argument->location.start, source_length);
42
+ }
43
+
44
+ bool javascript_tag_supports_block(void) {
45
+ return true;
46
+ }
47
+
48
+ const tag_helper_handler_T javascript_tag_handler = {
49
+ .name = "javascript_tag",
50
+ .source = HB_STRING_LITERAL("ActionView::Helpers::JavaScriptHelper#javascript_tag"),
51
+ .detect = detect_javascript_tag,
52
+ .extract_tag_name = extract_javascript_tag_name,
53
+ .extract_content = extract_javascript_tag_content,
54
+ .supports_block = javascript_tag_supports_block
55
+ };
@@ -1,5 +1,5 @@
1
1
  #include "../../include/analyze/action_view/tag_helper_handler.h"
2
- #include "../../include/util/hb_allocator.h"
2
+ #include "../../include/lib/hb_allocator.h"
3
3
 
4
4
  #include <stdbool.h>
5
5
  #include <stdlib.h>
@@ -8,8 +8,11 @@ extern const tag_helper_handler_T content_tag_handler;
8
8
  extern const tag_helper_handler_T tag_dot_handler;
9
9
  extern const tag_helper_handler_T link_to_handler;
10
10
  extern const tag_helper_handler_T turbo_frame_tag_handler;
11
+ extern const tag_helper_handler_T javascript_tag_handler;
12
+ extern const tag_helper_handler_T javascript_include_tag_handler;
13
+ extern const tag_helper_handler_T image_tag_handler;
11
14
 
12
- static size_t handlers_count = 4;
15
+ static size_t handlers_count = 7;
13
16
 
14
17
  tag_helper_info_T* tag_helper_info_init(hb_allocator_T* allocator) {
15
18
  tag_helper_info_T* info = hb_allocator_alloc(allocator, sizeof(tag_helper_info_T));
@@ -41,7 +44,7 @@ void tag_helper_info_free(tag_helper_info_T** info) {
41
44
  }
42
45
 
43
46
  tag_helper_handler_T* get_tag_helper_handlers(void) {
44
- static tag_helper_handler_T static_handlers[4];
47
+ static tag_helper_handler_T static_handlers[7];
45
48
  static bool initialized = false;
46
49
 
47
50
  if (!initialized) {
@@ -49,6 +52,10 @@ tag_helper_handler_T* get_tag_helper_handlers(void) {
49
52
  static_handlers[1] = tag_dot_handler;
50
53
  static_handlers[2] = link_to_handler;
51
54
  static_handlers[3] = turbo_frame_tag_handler;
55
+ static_handlers[4] = javascript_tag_handler;
56
+ static_handlers[5] = javascript_include_tag_handler;
57
+ static_handlers[6] = image_tag_handler;
58
+
52
59
  initialized = true;
53
60
  }
54
61
 
@@ -1,4 +1,5 @@
1
1
  #include "../../include/analyze/action_view/tag_helper_handler.h"
2
+ #include "../../include/util/ruby_util.h"
2
3
 
3
4
  #include <prism.h>
4
5
  #include <stdbool.h>
@@ -23,6 +24,10 @@ char* extract_tag_dot_name(pm_call_node_t* call_node, pm_parser_t* parser, hb_al
23
24
  pm_constant_t* constant = pm_constant_pool_id_to_constant(&parser->constant_pool, call_node->name);
24
25
  if (!constant) { return NULL; }
25
26
 
27
+ if (is_ruby_introspection_method(hb_string_from_data((const char*) constant->start, constant->length))) {
28
+ return NULL;
29
+ }
30
+
26
31
  char* name = hb_allocator_strndup(allocator, (const char*) constant->start, constant->length);
27
32
 
28
33
  for (size_t i = 0; i < constant->length && name[i] != '\0'; i++) {
@@ -34,10 +39,17 @@ char* extract_tag_dot_name(pm_call_node_t* call_node, pm_parser_t* parser, hb_al
34
39
 
35
40
  // TODO: this should probably be an array of nodes
36
41
  char* extract_tag_dot_content(pm_call_node_t* call_node, pm_parser_t* parser, hb_allocator_T* allocator) {
37
- (void) parser;
38
-
39
42
  if (!call_node) { return NULL; }
40
43
 
44
+ if (call_node->name) {
45
+ pm_constant_t* constant = pm_constant_pool_id_to_constant(&parser->constant_pool, call_node->name);
46
+
47
+ if (constant
48
+ && is_ruby_introspection_method(hb_string_from_data((const char*) constant->start, constant->length))) {
49
+ return NULL;
50
+ }
51
+ }
52
+
41
53
  char* block_content = extract_inline_block_content(call_node, allocator);
42
54
  if (block_content) { return block_content; }
43
55
 
@@ -47,11 +59,16 @@ char* extract_tag_dot_content(pm_call_node_t* call_node, pm_parser_t* parser, hb
47
59
  if (arguments->arguments.size) {
48
60
  pm_node_t* first_argument = arguments->arguments.nodes[0];
49
61
 
62
+ if (first_argument->type == PM_KEYWORD_HASH_NODE) { return NULL; }
63
+
50
64
  if (first_argument->type == PM_STRING_NODE) {
51
65
  pm_string_node_t* string_node = (pm_string_node_t*) first_argument;
52
66
  size_t length = pm_string_length(&string_node->unescaped);
53
67
  return hb_allocator_strndup(allocator, (const char*) pm_string_source(&string_node->unescaped), length);
54
68
  }
69
+
70
+ size_t source_length = first_argument->location.end - first_argument->location.start;
71
+ return hb_allocator_strndup(allocator, (const char*) first_argument->location.start, source_length);
55
72
  }
56
73
  }
57
74
 
@@ -1,12 +1,12 @@
1
1
  #include "../../include/analyze/action_view/tag_helper_node_builders.h"
2
- #include "../../include/ast_nodes.h"
3
- #include "../../include/location.h"
4
- #include "../../include/position.h"
5
- #include "../../include/range.h"
6
- #include "../../include/token_struct.h"
7
- #include "../../include/util/hb_allocator.h"
8
- #include "../../include/util/hb_array.h"
9
- #include "../../include/util/hb_string.h"
2
+ #include "../../include/ast/ast_nodes.h"
3
+ #include "../../include/lexer/token_struct.h"
4
+ #include "../../include/lib/hb_allocator.h"
5
+ #include "../../include/lib/hb_array.h"
6
+ #include "../../include/lib/hb_string.h"
7
+ #include "../../include/location/location.h"
8
+ #include "../../include/location/position.h"
9
+ #include "../../include/location/range.h"
10
10
 
11
11
  #include <prism.h>
12
12
  #include <stdbool.h>
@@ -26,7 +26,7 @@ token_T* create_synthetic_token(
26
26
  if (value) {
27
27
  size_t length = strlen(value);
28
28
  char* copied = hb_allocator_strndup(allocator, value, length);
29
- token->value = (hb_string_T) { .data = copied, .length = (uint32_t) length };
29
+ token->value = hb_string_from_data(copied, length);
30
30
  } else {
31
31
  token->value = HB_STRING_EMPTY;
32
32
  }
@@ -76,32 +76,47 @@ hb_array_T* prepend_attribute(hb_array_T* attributes, AST_NODE_T* attribute, hb_
76
76
  return new_attributes;
77
77
  }
78
78
 
79
- AST_HTML_ATTRIBUTE_NODE_T* create_html_attribute_node(
79
+ AST_HTML_ATTRIBUTE_NODE_T* create_html_attribute_node_precise(
80
80
  const char* name_string,
81
81
  const char* value_string,
82
- position_T start_position,
83
- position_T end_position,
82
+ attribute_positions_T* positions,
84
83
  hb_allocator_T* allocator
85
84
  ) {
86
85
  if (!name_string) { return NULL; }
87
86
 
88
87
  AST_HTML_ATTRIBUTE_NAME_NODE_T* name_node =
89
- create_attribute_name_node(name_string, start_position, end_position, allocator);
88
+ create_attribute_name_node(name_string, positions->name_start, positions->name_end, allocator);
89
+
90
+ token_T* equals_token = value_string ? create_synthetic_token(
91
+ allocator,
92
+ positions->separator_string,
93
+ positions->separator_type,
94
+ positions->separator_start,
95
+ positions->separator_end
96
+ )
97
+ : NULL;
90
98
 
91
- token_T* equals_token = create_synthetic_token(allocator, "=", TOKEN_EQUALS, start_position, end_position);
92
99
  AST_HTML_ATTRIBUTE_VALUE_NODE_T* value_node = NULL;
93
100
 
94
101
  if (value_string) {
95
- token_T* open_quote = create_synthetic_token(allocator, "\"", TOKEN_QUOTE, start_position, end_position);
96
- token_T* close_quote = create_synthetic_token(allocator, "\"", TOKEN_QUOTE, end_position, end_position);
102
+ token_T* open_quote =
103
+ positions->quoted
104
+ ? create_synthetic_token(allocator, "\"", TOKEN_QUOTE, positions->value_start, positions->content_start)
105
+ : NULL;
106
+
107
+ token_T* close_quote =
108
+ positions->quoted
109
+ ? create_synthetic_token(allocator, "\"", TOKEN_QUOTE, positions->content_end, positions->value_end)
110
+ : NULL;
97
111
 
98
112
  AST_LITERAL_NODE_T* value_literal = ast_literal_node_init(
99
113
  hb_string_from_c_string(value_string),
100
- start_position,
101
- end_position,
114
+ positions->content_start,
115
+ positions->content_end,
102
116
  hb_array_init(0, allocator),
103
117
  allocator
104
118
  );
119
+
105
120
  hb_array_T* value_children = hb_array_init(1, allocator);
106
121
  hb_array_append(value_children, (AST_NODE_T*) value_literal);
107
122
 
@@ -109,43 +124,50 @@ AST_HTML_ATTRIBUTE_NODE_T* create_html_attribute_node(
109
124
  open_quote,
110
125
  value_children,
111
126
  close_quote,
112
- true,
113
- start_position,
114
- end_position,
127
+ positions->quoted,
128
+ positions->value_start,
129
+ positions->value_end,
115
130
  hb_array_init(0, allocator),
116
131
  allocator
117
132
  );
118
133
  }
119
134
 
135
+ position_T attribute_end = value_string ? positions->value_end : positions->name_end;
136
+
120
137
  return ast_html_attribute_node_init(
121
138
  name_node,
122
139
  equals_token,
123
140
  value_node,
124
- start_position,
125
- end_position,
141
+ positions->name_start,
142
+ attribute_end,
126
143
  hb_array_init(0, allocator),
127
144
  allocator
128
145
  );
129
146
  }
130
147
 
131
- AST_HTML_ATTRIBUTE_NODE_T* create_html_attribute_with_ruby_literal(
148
+ AST_HTML_ATTRIBUTE_NODE_T* create_html_attribute_with_ruby_literal_precise(
132
149
  const char* name_string,
133
150
  const char* ruby_content,
134
- position_T start_position,
135
- position_T end_position,
151
+ attribute_positions_T* positions,
136
152
  hb_allocator_T* allocator
137
153
  ) {
138
154
  if (!name_string || !ruby_content) { return NULL; }
139
155
 
140
156
  AST_HTML_ATTRIBUTE_NAME_NODE_T* name_node =
141
- create_attribute_name_node(name_string, start_position, end_position, allocator);
142
-
143
- token_T* equals_token = create_synthetic_token(allocator, ":", TOKEN_COLON, start_position, end_position);
157
+ create_attribute_name_node(name_string, positions->name_start, positions->name_end, allocator);
158
+
159
+ token_T* equals_token = create_synthetic_token(
160
+ allocator,
161
+ positions->separator_string,
162
+ positions->separator_type,
163
+ positions->separator_start,
164
+ positions->separator_end
165
+ );
144
166
 
145
167
  AST_RUBY_LITERAL_NODE_T* ruby_node = ast_ruby_literal_node_init(
146
168
  hb_string_from_c_string(ruby_content),
147
- start_position,
148
- end_position,
169
+ positions->content_start,
170
+ positions->content_end,
149
171
  hb_array_init(0, allocator),
150
172
  allocator
151
173
  );
@@ -158,8 +180,8 @@ AST_HTML_ATTRIBUTE_NODE_T* create_html_attribute_with_ruby_literal(
158
180
  value_children,
159
181
  NULL,
160
182
  false,
161
- start_position,
162
- end_position,
183
+ positions->content_start,
184
+ positions->content_end,
163
185
  hb_array_init(0, allocator),
164
186
  allocator
165
187
  );
@@ -168,13 +190,61 @@ AST_HTML_ATTRIBUTE_NODE_T* create_html_attribute_with_ruby_literal(
168
190
  name_node,
169
191
  equals_token,
170
192
  value_node,
171
- start_position,
172
- end_position,
193
+ positions->name_start,
194
+ positions->content_end,
173
195
  hb_array_init(0, allocator),
174
196
  allocator
175
197
  );
176
198
  }
177
199
 
200
+ AST_HTML_ATTRIBUTE_NODE_T* create_html_attribute_node(
201
+ const char* name_string,
202
+ const char* value_string,
203
+ position_T start_position,
204
+ position_T end_position,
205
+ hb_allocator_T* allocator
206
+ ) {
207
+ attribute_positions_T positions = {
208
+ .name_start = start_position,
209
+ .name_end = end_position,
210
+ .separator_string = "=",
211
+ .separator_type = TOKEN_EQUALS,
212
+ .separator_start = start_position,
213
+ .separator_end = end_position,
214
+ .value_start = start_position,
215
+ .value_end = end_position,
216
+ .content_start = start_position,
217
+ .content_end = end_position,
218
+ .quoted = true,
219
+ };
220
+
221
+ return create_html_attribute_node_precise(name_string, value_string, &positions, allocator);
222
+ }
223
+
224
+ AST_HTML_ATTRIBUTE_NODE_T* create_html_attribute_with_ruby_literal(
225
+ const char* name_string,
226
+ const char* ruby_content,
227
+ position_T start_position,
228
+ position_T end_position,
229
+ hb_allocator_T* allocator
230
+ ) {
231
+ attribute_positions_T positions = {
232
+ .name_start = start_position,
233
+ .name_end = end_position,
234
+ .separator_string = ":",
235
+ .separator_type = TOKEN_COLON,
236
+ .separator_start = start_position,
237
+ .separator_end = end_position,
238
+ .value_start = start_position,
239
+ .value_end = end_position,
240
+ .content_start = start_position,
241
+ .content_end = end_position,
242
+ .quoted = false,
243
+ };
244
+
245
+ return create_html_attribute_with_ruby_literal_precise(name_string, ruby_content, &positions, allocator);
246
+ }
247
+
178
248
  static AST_HTML_ATTRIBUTE_VALUE_NODE_T* create_interpolated_attribute_value(
179
249
  pm_interpolated_string_node_t* interpolated_node,
180
250
  position_T start_position,
@@ -210,8 +280,20 @@ static AST_HTML_ATTRIBUTE_VALUE_NODE_T* create_interpolated_attribute_value(
210
280
  }
211
281
  }
212
282
  } else if (part->type == PM_EMBEDDED_STATEMENTS_NODE) {
213
- size_t ruby_length = part->location.end - part->location.start;
214
- char* ruby_content = hb_allocator_strndup(allocator, (const char*) part->location.start, ruby_length);
283
+ pm_embedded_statements_node_t* embedded = (pm_embedded_statements_node_t*) part;
284
+ const uint8_t* content_start;
285
+ const uint8_t* content_end;
286
+
287
+ if (embedded->statements) {
288
+ content_start = embedded->statements->base.location.start;
289
+ content_end = embedded->statements->base.location.end;
290
+ } else {
291
+ content_start = part->location.start;
292
+ content_end = part->location.end;
293
+ }
294
+
295
+ size_t ruby_length = content_end - content_start;
296
+ char* ruby_content = hb_allocator_strndup(allocator, (const char*) content_start, ruby_length);
215
297
 
216
298
  if (ruby_content) {
217
299
  AST_RUBY_LITERAL_NODE_T* ruby_node = ast_ruby_literal_node_init(