herb 0.8.10-arm-linux-gnu → 0.9.1-arm-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 (212) hide show
  1. checksums.yaml +4 -4
  2. data/Makefile +11 -3
  3. data/README.md +64 -34
  4. data/Rakefile +48 -40
  5. data/config.yml +473 -34
  6. data/ext/herb/error_helpers.c +535 -140
  7. data/ext/herb/error_helpers.h +1 -0
  8. data/ext/herb/extconf.rb +67 -28
  9. data/ext/herb/extension.c +321 -51
  10. data/ext/herb/extension.h +1 -0
  11. data/ext/herb/extension_helpers.c +24 -14
  12. data/ext/herb/extension_helpers.h +2 -2
  13. data/ext/herb/nodes.c +647 -270
  14. data/ext/herb/nodes.h +1 -0
  15. data/herb.gemspec +3 -2
  16. data/lib/herb/3.0/herb.so +0 -0
  17. data/lib/herb/3.1/herb.so +0 -0
  18. data/lib/herb/3.2/herb.so +0 -0
  19. data/lib/herb/3.3/herb.so +0 -0
  20. data/lib/herb/3.4/herb.so +0 -0
  21. data/lib/herb/4.0/herb.so +0 -0
  22. data/lib/herb/ast/helpers.rb +3 -3
  23. data/lib/herb/ast/node.rb +15 -2
  24. data/lib/herb/ast/nodes.rb +1530 -179
  25. data/lib/herb/bootstrap.rb +87 -0
  26. data/lib/herb/cli.rb +341 -31
  27. data/lib/herb/configuration.rb +248 -0
  28. data/lib/herb/defaults.yml +32 -0
  29. data/lib/herb/engine/compiler.rb +78 -11
  30. data/lib/herb/engine/debug_visitor.rb +13 -3
  31. data/lib/herb/engine/error_formatter.rb +13 -9
  32. data/lib/herb/engine/parser_error_overlay.rb +10 -6
  33. data/lib/herb/engine/validator.rb +8 -3
  34. data/lib/herb/engine/validators/nesting_validator.rb +2 -2
  35. data/lib/herb/engine.rb +119 -43
  36. data/lib/herb/errors.rb +808 -88
  37. data/lib/herb/lex_result.rb +1 -0
  38. data/lib/herb/location.rb +7 -3
  39. data/lib/herb/parse_result.rb +12 -2
  40. data/lib/herb/parser_options.rb +62 -0
  41. data/lib/herb/position.rb +1 -0
  42. data/lib/herb/prism_inspect.rb +120 -0
  43. data/lib/herb/project.rb +923 -331
  44. data/lib/herb/range.rb +1 -0
  45. data/lib/herb/token.rb +7 -1
  46. data/lib/herb/version.rb +1 -1
  47. data/lib/herb/visitor.rb +47 -2
  48. data/lib/herb/warnings.rb +6 -1
  49. data/lib/herb.rb +35 -3
  50. data/sig/herb/ast/helpers.rbs +2 -2
  51. data/sig/herb/ast/node.rbs +12 -2
  52. data/sig/herb/ast/nodes.rbs +773 -128
  53. data/sig/herb/bootstrap.rbs +31 -0
  54. data/sig/herb/configuration.rbs +89 -0
  55. data/sig/herb/engine/compiler.rbs +9 -1
  56. data/sig/herb/engine/debug_visitor.rbs +2 -0
  57. data/sig/herb/engine/validator.rbs +5 -1
  58. data/sig/herb/engine.rbs +21 -3
  59. data/sig/herb/errors.rbs +372 -63
  60. data/sig/herb/location.rbs +4 -0
  61. data/sig/herb/parse_result.rbs +4 -2
  62. data/sig/herb/parser_options.rbs +46 -0
  63. data/sig/herb/position.rbs +1 -0
  64. data/sig/herb/prism_inspect.rbs +28 -0
  65. data/sig/herb/range.rbs +1 -0
  66. data/sig/herb/token.rbs +6 -0
  67. data/sig/herb/visitor.rbs +31 -4
  68. data/sig/herb/warnings.rbs +6 -1
  69. data/sig/herb.rbs +14 -0
  70. data/sig/herb_c_extension.rbs +5 -2
  71. data/sig/rubyvm.rbs +5 -0
  72. data/sig/serialized_ast_errors.rbs +82 -6
  73. data/sig/serialized_ast_nodes.rbs +91 -6
  74. data/src/analyze/action_view/attribute_extraction_helpers.c +303 -0
  75. data/src/analyze/action_view/content_tag.c +78 -0
  76. data/src/analyze/action_view/link_to.c +167 -0
  77. data/src/analyze/action_view/registry.c +83 -0
  78. data/src/analyze/action_view/tag.c +70 -0
  79. data/src/analyze/action_view/tag_helper_node_builders.c +305 -0
  80. data/src/analyze/action_view/tag_helpers.c +815 -0
  81. data/src/analyze/action_view/turbo_frame_tag.c +88 -0
  82. data/src/analyze/analyze.c +885 -0
  83. data/src/{analyzed_ruby.c → analyze/analyzed_ruby.c} +13 -11
  84. data/src/analyze/builders.c +343 -0
  85. data/src/analyze/conditional_elements.c +594 -0
  86. data/src/analyze/conditional_open_tags.c +640 -0
  87. data/src/analyze/control_type.c +250 -0
  88. data/src/{analyze_helpers.c → analyze/helpers.c} +48 -23
  89. data/src/analyze/invalid_structures.c +193 -0
  90. data/src/{analyze_missing_end.c → analyze/missing_end.c} +33 -22
  91. data/src/analyze/parse_errors.c +84 -0
  92. data/src/analyze/prism_annotate.c +399 -0
  93. data/src/analyze/render_nodes.c +761 -0
  94. data/src/{analyze_transform.c → analyze/transform.c} +24 -3
  95. data/src/ast_node.c +17 -7
  96. data/src/ast_nodes.c +759 -387
  97. data/src/ast_pretty_print.c +264 -6
  98. data/src/errors.c +1454 -519
  99. data/src/extract.c +145 -49
  100. data/src/herb.c +52 -34
  101. data/src/html_util.c +241 -12
  102. data/src/include/analyze/action_view/attribute_extraction_helpers.h +36 -0
  103. data/src/include/analyze/action_view/tag_helper_handler.h +43 -0
  104. data/src/include/analyze/action_view/tag_helper_node_builders.h +70 -0
  105. data/src/include/analyze/action_view/tag_helpers.h +38 -0
  106. data/src/include/{analyze.h → analyze/analyze.h} +14 -4
  107. data/src/include/{analyzed_ruby.h → analyze/analyzed_ruby.h} +3 -3
  108. data/src/include/analyze/builders.h +27 -0
  109. data/src/include/analyze/conditional_elements.h +9 -0
  110. data/src/include/analyze/conditional_open_tags.h +9 -0
  111. data/src/include/analyze/control_type.h +14 -0
  112. data/src/include/{analyze_helpers.h → analyze/helpers.h} +4 -2
  113. data/src/include/analyze/invalid_structures.h +11 -0
  114. data/src/include/analyze/prism_annotate.h +16 -0
  115. data/src/include/analyze/render_nodes.h +11 -0
  116. data/src/include/ast_node.h +11 -5
  117. data/src/include/ast_nodes.h +154 -38
  118. data/src/include/ast_pretty_print.h +5 -0
  119. data/src/include/element_source.h +3 -8
  120. data/src/include/errors.h +206 -55
  121. data/src/include/extract.h +21 -5
  122. data/src/include/herb.h +18 -6
  123. data/src/include/herb_prism_node.h +13 -0
  124. data/src/include/html_util.h +7 -2
  125. data/src/include/io.h +3 -1
  126. data/src/include/lex_helpers.h +29 -0
  127. data/src/include/lexer.h +1 -1
  128. data/src/include/lexer_peek_helpers.h +87 -13
  129. data/src/include/lexer_struct.h +2 -0
  130. data/src/include/location.h +2 -1
  131. data/src/include/parser.h +28 -2
  132. data/src/include/parser_helpers.h +19 -3
  133. data/src/include/pretty_print.h +10 -5
  134. data/src/include/prism_context.h +45 -0
  135. data/src/include/prism_helpers.h +10 -7
  136. data/src/include/prism_serialized.h +12 -0
  137. data/src/include/token.h +16 -4
  138. data/src/include/token_struct.h +10 -3
  139. data/src/include/utf8.h +2 -1
  140. data/src/include/util/hb_allocator.h +78 -0
  141. data/src/include/util/hb_arena.h +6 -1
  142. data/src/include/util/hb_arena_debug.h +12 -1
  143. data/src/include/util/hb_array.h +7 -3
  144. data/src/include/util/hb_buffer.h +6 -4
  145. data/src/include/util/hb_foreach.h +79 -0
  146. data/src/include/util/hb_narray.h +8 -4
  147. data/src/include/util/hb_string.h +56 -9
  148. data/src/include/util.h +6 -3
  149. data/src/include/version.h +1 -1
  150. data/src/io.c +3 -2
  151. data/src/lexer.c +42 -30
  152. data/src/lexer_peek_helpers.c +12 -74
  153. data/src/location.c +2 -2
  154. data/src/main.c +53 -28
  155. data/src/parser.c +784 -247
  156. data/src/parser_helpers.c +110 -23
  157. data/src/parser_match_tags.c +129 -48
  158. data/src/pretty_print.c +29 -24
  159. data/src/prism_helpers.c +30 -27
  160. data/src/ruby_parser.c +2 -0
  161. data/src/token.c +151 -66
  162. data/src/token_matchers.c +0 -1
  163. data/src/utf8.c +7 -6
  164. data/src/util/hb_allocator.c +341 -0
  165. data/src/util/hb_arena.c +81 -56
  166. data/src/util/hb_arena_debug.c +32 -17
  167. data/src/util/hb_array.c +30 -15
  168. data/src/util/hb_buffer.c +17 -21
  169. data/src/util/hb_narray.c +22 -7
  170. data/src/util/hb_string.c +49 -35
  171. data/src/util.c +21 -11
  172. data/src/visitor.c +67 -0
  173. data/templates/ext/herb/error_helpers.c.erb +24 -11
  174. data/templates/ext/herb/error_helpers.h.erb +1 -0
  175. data/templates/ext/herb/nodes.c.erb +50 -16
  176. data/templates/ext/herb/nodes.h.erb +1 -0
  177. data/templates/java/error_helpers.c.erb +1 -1
  178. data/templates/java/nodes.c.erb +30 -8
  179. data/templates/java/org/herb/ast/Errors.java.erb +24 -1
  180. data/templates/java/org/herb/ast/Nodes.java.erb +80 -21
  181. data/templates/javascript/packages/core/src/errors.ts.erb +16 -3
  182. data/templates/javascript/packages/core/src/node-type-guards.ts.erb +3 -1
  183. data/templates/javascript/packages/core/src/nodes.ts.erb +109 -32
  184. data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +13 -4
  185. data/templates/javascript/packages/node/extension/nodes.cpp.erb +43 -4
  186. data/templates/lib/herb/ast/nodes.rb.erb +95 -32
  187. data/templates/lib/herb/errors.rb.erb +15 -3
  188. data/templates/lib/herb/visitor.rb.erb +2 -2
  189. data/templates/rust/src/ast/nodes.rs.erb +97 -44
  190. data/templates/rust/src/errors.rs.erb +2 -1
  191. data/templates/rust/src/nodes.rs.erb +168 -16
  192. data/templates/rust/src/union_types.rs.erb +60 -0
  193. data/templates/rust/src/visitor.rs.erb +81 -0
  194. data/templates/src/{analyze_missing_end.c.erb → analyze/missing_end.c.erb} +9 -6
  195. data/templates/src/{analyze_transform.c.erb → analyze/transform.c.erb} +2 -2
  196. data/templates/src/ast_nodes.c.erb +34 -26
  197. data/templates/src/ast_pretty_print.c.erb +24 -5
  198. data/templates/src/errors.c.erb +60 -54
  199. data/templates/src/include/ast_nodes.h.erb +6 -2
  200. data/templates/src/include/ast_pretty_print.h.erb +5 -0
  201. data/templates/src/include/errors.h.erb +15 -11
  202. data/templates/src/include/util/hb_foreach.h.erb +20 -0
  203. data/templates/src/parser_match_tags.c.erb +10 -4
  204. data/templates/src/visitor.c.erb +2 -2
  205. data/templates/template.rb +204 -29
  206. data/templates/wasm/error_helpers.cpp.erb +9 -5
  207. data/templates/wasm/nodes.cpp.erb +41 -4
  208. metadata +60 -16
  209. data/src/analyze.c +0 -1608
  210. data/src/element_source.c +0 -12
  211. data/src/include/util/hb_system.h +0 -9
  212. data/src/util/hb_system.c +0 -30
@@ -9,6 +9,7 @@
9
9
 
10
10
  #include <ruby.h>
11
11
 
12
+ void rb_init_error_classes(void);
12
13
  VALUE rb_error_from_c_struct(ERROR_T* error);
13
14
  VALUE rb_errors_array_from_c_array(hb_array_T* array);
14
15
 
data/ext/herb/extconf.rb CHANGED
@@ -1,9 +1,42 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "mkmf"
4
+ require_relative "../../lib/herb/bootstrap"
4
5
 
5
6
  extension_name = "herb"
6
7
 
8
+ if Herb::Bootstrap.git_source? && !Herb::Bootstrap.templates_generated?
9
+ puts "Building from source — running bootstrap..."
10
+ Herb::Bootstrap.generate_templates
11
+
12
+ unless Herb::Bootstrap.prism_vendored?
13
+ prism_path = Herb::Bootstrap.find_prism_gem_path
14
+
15
+ abort <<~MSG unless prism_path
16
+ ERROR: Could not find Prism C source files.
17
+
18
+ When installing Herb from a git source, a git-sourced Prism is required
19
+ (the released gem does not include C source files).
20
+
21
+ Add it to your Gemfile before the herb git reference:
22
+
23
+ gem "prism", github: "ruby/prism", tag: "v1.9.0"
24
+ gem "herb", github: "...", branch: "..."
25
+
26
+ Then run `bundle install` again.
27
+ MSG
28
+
29
+ puts "Vendoring Prism from #{prism_path}..."
30
+ Herb::Bootstrap.vendor_prism(prism_gem_path: prism_path)
31
+ end
32
+
33
+ root_path = Herb::Bootstrap::ROOT_PATH
34
+ sha = `git -C #{root_path} rev-parse --short HEAD 2>/dev/null`.strip
35
+
36
+ $CFLAGS << " -DHERB_GIT_BUILD"
37
+ $CFLAGS << " -DHERB_GIT_SHA=\\\"#{sha}\\\"" unless sha.empty?
38
+ end
39
+
7
40
  include_path = File.expand_path("../../src/include", __dir__)
8
41
  prism_path = File.expand_path("../../vendor/prism", __dir__)
9
42
 
@@ -11,6 +44,8 @@ prism_src_path = "#{prism_path}/src"
11
44
  prism_include_path = "#{prism_path}/include"
12
45
 
13
46
  $VPATH << "$(srcdir)/../../src"
47
+ $VPATH << "$(srcdir)/../../src/analyze"
48
+ $VPATH << "$(srcdir)/../../src/analyze/action_view"
14
49
  $VPATH << "$(srcdir)/../../src/util"
15
50
  $VPATH << prism_src_path
16
51
  $VPATH << "#{prism_src_path}/util"
@@ -21,41 +56,45 @@ $INCFLAGS << " -I#{prism_src_path}"
21
56
  $INCFLAGS << " -I#{prism_src_path}/util"
22
57
 
23
58
  $CFLAGS << " -fvisibility=hidden"
59
+ $CFLAGS << " -DHERB_EXCLUDE_PRETTYPRINT"
60
+ $CFLAGS << " -DPRISM_EXCLUDE_PRETTYPRINT"
61
+ $CFLAGS << " -DPRISM_EXCLUDE_JSON"
62
+ $CFLAGS << " -DPRISM_EXCLUDE_PACK"
24
63
 
25
64
  herb_src_files = Dir.glob("#{$srcdir}/../../src/**/*.c").map { |file| file.delete_prefix("../../../../ext/herb/") }.sort
26
65
 
27
- prism_main_files = %w[
28
- diagnostic.c
29
- encoding.c
30
- node.c
31
- options.c
32
- pack.c
33
- prettyprint.c
34
- prism.c
35
- regexp.c
36
- serialize.c
37
- static_literals.c
38
- token_type.c
66
+ prism_main_files = [
67
+ "diagnostic.c",
68
+ "encoding.c",
69
+ "node.c",
70
+ "options.c",
71
+ "pack.c",
72
+ "prettyprint.c",
73
+ "prism.c",
74
+ "regexp.c",
75
+ "serialize.c",
76
+ "static_literals.c",
77
+ "token_type.c"
39
78
  ]
40
79
 
41
- prism_util_files = %w[
42
- pm_buffer.c
43
- pm_char.c
44
- pm_constant_pool.c
45
- pm_integer.c
46
- pm_list.c
47
- pm_memchr.c
48
- pm_newline_list.c
49
- pm_string.c
50
- pm_strncasecmp.c
51
- pm_strpbrk.c
80
+ prism_util_files = [
81
+ "pm_buffer.c",
82
+ "pm_char.c",
83
+ "pm_constant_pool.c",
84
+ "pm_integer.c",
85
+ "pm_list.c",
86
+ "pm_memchr.c",
87
+ "pm_newline_list.c",
88
+ "pm_string.c",
89
+ "pm_strncasecmp.c",
90
+ "pm_strpbrk.c"
52
91
  ]
53
92
 
54
- core_src_files = %w[
55
- extension.c
56
- nodes.c
57
- error_helpers.c
58
- extension_helpers.c
93
+ core_src_files = [
94
+ "extension.c",
95
+ "nodes.c",
96
+ "error_helpers.c",
97
+ "extension_helpers.c"
59
98
  ]
60
99
 
61
100
  $srcs = core_src_files + herb_src_files + prism_main_files + prism_util_files
data/ext/herb/extension.c CHANGED
@@ -1,12 +1,13 @@
1
1
  #include <ruby.h>
2
2
 
3
+ #include "../../src/include/util/hb_allocator.h"
4
+ #include "../../src/include/util/hb_arena_debug.h"
5
+
3
6
  #include "error_helpers.h"
4
7
  #include "extension.h"
5
8
  #include "extension_helpers.h"
6
9
  #include "nodes.h"
7
10
 
8
- #include "../../src/include/analyze.h"
9
-
10
11
  VALUE mHerb;
11
12
  VALUE cPosition;
12
13
  VALUE cLocation;
@@ -15,29 +16,96 @@ VALUE cToken;
15
16
  VALUE cResult;
16
17
  VALUE cLexResult;
17
18
  VALUE cParseResult;
19
+ VALUE cParserOptions;
20
+
21
+ typedef struct {
22
+ AST_DOCUMENT_NODE_T* root;
23
+ VALUE source;
24
+ const parser_options_T* parser_options;
25
+ hb_allocator_T allocator;
26
+ } parse_args_T;
27
+
28
+ typedef struct {
29
+ hb_array_T* tokens;
30
+ VALUE source;
31
+ hb_allocator_T allocator;
32
+ } lex_args_T;
33
+
34
+ typedef struct {
35
+ char* buffer_value;
36
+ hb_allocator_T allocator;
37
+ } buffer_args_T;
38
+
39
+ static VALUE parse_convert_body(VALUE arg) {
40
+ parse_args_T* args = (parse_args_T*) arg;
41
+
42
+ return create_parse_result(args->root, args->source, args->parser_options);
43
+ }
18
44
 
19
- static VALUE Herb_lex(VALUE self, VALUE source) {
20
- char* string = (char*) check_string(source);
45
+ static VALUE parse_cleanup(VALUE arg) {
46
+ parse_args_T* args = (parse_args_T*) arg;
21
47
 
22
- hb_array_T* tokens = herb_lex(string);
48
+ if (args->root != NULL) { ast_node_free((AST_NODE_T*) args->root, &args->allocator); }
23
49
 
24
- VALUE result = create_lex_result(tokens, source);
50
+ hb_allocator_destroy(&args->allocator);
25
51
 
26
- herb_free_tokens(&tokens);
52
+ return Qnil;
53
+ }
27
54
 
28
- return result;
55
+ static VALUE lex_convert_body(VALUE arg) {
56
+ lex_args_T* args = (lex_args_T*) arg;
57
+
58
+ return create_lex_result(args->tokens, args->source);
29
59
  }
30
60
 
31
- static VALUE Herb_lex_file(VALUE self, VALUE path) {
32
- char* file_path = (char*) check_string(path);
33
- hb_array_T* tokens = herb_lex_file(file_path);
61
+ static VALUE lex_cleanup(VALUE arg) {
62
+ lex_args_T* args = (lex_args_T*) arg;
34
63
 
35
- VALUE source_value = read_file_to_ruby_string(file_path);
36
- VALUE result = create_lex_result(tokens, source_value);
64
+ if (args->tokens != NULL) { herb_free_tokens(&args->tokens, &args->allocator); }
37
65
 
38
- herb_free_tokens(&tokens);
66
+ hb_allocator_destroy(&args->allocator);
39
67
 
40
- return result;
68
+ return Qnil;
69
+ }
70
+
71
+ static VALUE buffer_to_string_body(VALUE arg) {
72
+ buffer_args_T* args = (buffer_args_T*) arg;
73
+
74
+ return rb_utf8_str_new_cstr(args->buffer_value);
75
+ }
76
+
77
+ static VALUE buffer_cleanup(VALUE arg) {
78
+ buffer_args_T* args = (buffer_args_T*) arg;
79
+
80
+ hb_allocator_dealloc(&args->allocator, args->buffer_value);
81
+ hb_allocator_destroy(&args->allocator);
82
+
83
+ return Qnil;
84
+ }
85
+
86
+ static VALUE Herb_lex(int argc, VALUE* argv, VALUE self) {
87
+ VALUE source, options;
88
+ rb_scan_args(argc, argv, "1:", &source, &options);
89
+
90
+ char* string = (char*) check_string(source);
91
+ bool print_arena_stats = false;
92
+
93
+ if (!NIL_P(options)) {
94
+ VALUE arena_stats = rb_hash_lookup(options, rb_utf8_str_new_cstr("arena_stats"));
95
+ if (NIL_P(arena_stats)) { arena_stats = rb_hash_lookup(options, ID2SYM(rb_intern("arena_stats"))); }
96
+ if (!NIL_P(arena_stats) && RTEST(arena_stats)) { print_arena_stats = true; }
97
+ }
98
+
99
+ lex_args_T args = { 0 };
100
+ args.source = source;
101
+
102
+ if (!hb_allocator_init(&args.allocator, HB_ALLOCATOR_ARENA)) { return Qnil; }
103
+
104
+ args.tokens = herb_lex(string, &args.allocator);
105
+
106
+ if (print_arena_stats) { hb_arena_print_stats((hb_arena_T*) args.allocator.context); }
107
+
108
+ return rb_ensure(lex_convert_body, (VALUE) &args, lex_cleanup, (VALUE) &args);
41
109
  }
42
110
 
43
111
  static VALUE Herb_parse(int argc, VALUE* argv, VALUE self) {
@@ -45,72 +113,248 @@ static VALUE Herb_parse(int argc, VALUE* argv, VALUE self) {
45
113
  rb_scan_args(argc, argv, "1:", &source, &options);
46
114
 
47
115
  char* string = (char*) check_string(source);
116
+ bool print_arena_stats = false;
48
117
 
49
- parser_options_T* parser_options = NULL;
50
- parser_options_T opts = { 0 };
118
+ parser_options_T parser_options = HERB_DEFAULT_PARSER_OPTIONS;
51
119
 
52
120
  if (!NIL_P(options)) {
53
121
  VALUE track_whitespace = rb_hash_lookup(options, rb_utf8_str_new_cstr("track_whitespace"));
54
122
  if (NIL_P(track_whitespace)) { track_whitespace = rb_hash_lookup(options, ID2SYM(rb_intern("track_whitespace"))); }
123
+ if (!NIL_P(track_whitespace) && RTEST(track_whitespace)) { parser_options.track_whitespace = true; }
124
+
125
+ VALUE analyze = rb_hash_lookup(options, rb_utf8_str_new_cstr("analyze"));
126
+ if (NIL_P(analyze)) { analyze = rb_hash_lookup(options, ID2SYM(rb_intern("analyze"))); }
127
+ if (!NIL_P(analyze) && !RTEST(analyze)) { parser_options.analyze = false; }
55
128
 
56
- if (!NIL_P(track_whitespace) && RTEST(track_whitespace)) {
57
- opts.track_whitespace = true;
58
- parser_options = &opts;
129
+ VALUE strict = rb_hash_lookup(options, rb_utf8_str_new_cstr("strict"));
130
+ if (NIL_P(strict)) { strict = rb_hash_lookup(options, ID2SYM(rb_intern("strict"))); }
131
+ if (!NIL_P(strict)) { parser_options.strict = RTEST(strict); }
132
+
133
+ VALUE action_view_helpers = rb_hash_lookup(options, rb_utf8_str_new_cstr("action_view_helpers"));
134
+ if (NIL_P(action_view_helpers)) {
135
+ action_view_helpers = rb_hash_lookup(options, ID2SYM(rb_intern("action_view_helpers")));
59
136
  }
137
+ if (!NIL_P(action_view_helpers) && RTEST(action_view_helpers)) { parser_options.action_view_helpers = true; }
138
+
139
+ VALUE render_nodes = rb_hash_lookup(options, rb_utf8_str_new_cstr("render_nodes"));
140
+ if (NIL_P(render_nodes)) { render_nodes = rb_hash_lookup(options, ID2SYM(rb_intern("render_nodes"))); }
141
+ if (!NIL_P(render_nodes) && RTEST(render_nodes)) { parser_options.render_nodes = true; }
142
+
143
+ VALUE prism_nodes = rb_hash_lookup(options, rb_utf8_str_new_cstr("prism_nodes"));
144
+ if (NIL_P(prism_nodes)) { prism_nodes = rb_hash_lookup(options, ID2SYM(rb_intern("prism_nodes"))); }
145
+ if (!NIL_P(prism_nodes) && RTEST(prism_nodes)) { parser_options.prism_nodes = true; }
146
+
147
+ VALUE prism_nodes_deep = rb_hash_lookup(options, rb_utf8_str_new_cstr("prism_nodes_deep"));
148
+ if (NIL_P(prism_nodes_deep)) { prism_nodes_deep = rb_hash_lookup(options, ID2SYM(rb_intern("prism_nodes_deep"))); }
149
+ if (!NIL_P(prism_nodes_deep) && RTEST(prism_nodes_deep)) { parser_options.prism_nodes_deep = true; }
150
+
151
+ VALUE prism_program = rb_hash_lookup(options, rb_utf8_str_new_cstr("prism_program"));
152
+ if (NIL_P(prism_program)) { prism_program = rb_hash_lookup(options, ID2SYM(rb_intern("prism_program"))); }
153
+ if (!NIL_P(prism_program) && RTEST(prism_program)) { parser_options.prism_program = true; }
154
+
155
+ VALUE arena_stats = rb_hash_lookup(options, rb_utf8_str_new_cstr("arena_stats"));
156
+ if (NIL_P(arena_stats)) { arena_stats = rb_hash_lookup(options, ID2SYM(rb_intern("arena_stats"))); }
157
+ if (!NIL_P(arena_stats) && RTEST(arena_stats)) { print_arena_stats = true; }
60
158
  }
61
159
 
62
- AST_DOCUMENT_NODE_T* root = herb_parse(string, parser_options);
160
+ parse_args_T args = { 0 };
161
+ args.source = source;
162
+ args.parser_options = &parser_options;
63
163
 
64
- herb_analyze_parse_tree(root, string);
164
+ if (!hb_allocator_init(&args.allocator, HB_ALLOCATOR_ARENA)) { return Qnil; }
65
165
 
66
- VALUE result = create_parse_result(root, source);
166
+ args.root = herb_parse(string, &parser_options, &args.allocator);
67
167
 
68
- ast_node_free((AST_NODE_T*) root);
168
+ if (print_arena_stats) { hb_arena_print_stats((hb_arena_T*) args.allocator.context); }
69
169
 
70
- return result;
170
+ return rb_ensure(parse_convert_body, (VALUE) &args, parse_cleanup, (VALUE) &args);
71
171
  }
72
172
 
73
- static VALUE Herb_parse_file(VALUE self, VALUE path) {
74
- char* file_path = (char*) check_string(path);
173
+ static VALUE Herb_extract_ruby(int argc, VALUE* argv, VALUE self) {
174
+ VALUE source, options;
175
+ rb_scan_args(argc, argv, "1:", &source, &options);
176
+
177
+ char* string = (char*) check_string(source);
75
178
 
76
- VALUE source_value = read_file_to_ruby_string(file_path);
77
- char* string = (char*) check_string(source_value);
179
+ hb_allocator_T allocator;
180
+ if (!hb_allocator_init(&allocator, HB_ALLOCATOR_ARENA)) { return Qnil; }
78
181
 
79
- AST_DOCUMENT_NODE_T* root = herb_parse(string, NULL);
182
+ hb_buffer_T output;
183
+ if (!hb_buffer_init(&output, strlen(string), &allocator)) { return Qnil; }
80
184
 
81
- herb_analyze_parse_tree(root, string);
185
+ herb_extract_ruby_options_T extract_options = HERB_EXTRACT_RUBY_DEFAULT_OPTIONS;
82
186
 
83
- VALUE result = create_parse_result(root, source_value);
187
+ if (!NIL_P(options)) {
188
+ VALUE semicolons_value = rb_hash_lookup(options, rb_utf8_str_new_cstr("semicolons"));
189
+ if (NIL_P(semicolons_value)) { semicolons_value = rb_hash_lookup(options, ID2SYM(rb_intern("semicolons"))); }
190
+ if (!NIL_P(semicolons_value)) { extract_options.semicolons = RTEST(semicolons_value); }
84
191
 
85
- ast_node_free((AST_NODE_T*) root);
192
+ VALUE comments_value = rb_hash_lookup(options, rb_utf8_str_new_cstr("comments"));
193
+ if (NIL_P(comments_value)) { comments_value = rb_hash_lookup(options, ID2SYM(rb_intern("comments"))); }
194
+ if (!NIL_P(comments_value)) { extract_options.comments = RTEST(comments_value); }
86
195
 
87
- return result;
196
+ VALUE preserve_positions_value = rb_hash_lookup(options, rb_utf8_str_new_cstr("preserve_positions"));
197
+ if (NIL_P(preserve_positions_value)) {
198
+ preserve_positions_value = rb_hash_lookup(options, ID2SYM(rb_intern("preserve_positions")));
199
+ }
200
+ if (!NIL_P(preserve_positions_value)) { extract_options.preserve_positions = RTEST(preserve_positions_value); }
201
+ }
202
+
203
+ herb_extract_ruby_to_buffer_with_options(string, &output, &extract_options, &allocator);
204
+
205
+ buffer_args_T args = { .buffer_value = output.value, .allocator = allocator };
206
+
207
+ return rb_ensure(buffer_to_string_body, (VALUE) &args, buffer_cleanup, (VALUE) &args);
88
208
  }
89
209
 
90
- static VALUE Herb_extract_ruby(VALUE self, VALUE source) {
210
+ static VALUE Herb_extract_html(VALUE self, VALUE source) {
91
211
  char* string = (char*) check_string(source);
212
+
213
+ hb_allocator_T allocator;
214
+ if (!hb_allocator_init(&allocator, HB_ALLOCATOR_ARENA)) { return Qnil; }
215
+
92
216
  hb_buffer_T output;
217
+ if (!hb_buffer_init(&output, strlen(string), &allocator)) { return Qnil; }
93
218
 
94
- if (!hb_buffer_init(&output, strlen(string))) { return Qnil; }
219
+ herb_extract_html_to_buffer(string, &output, &allocator);
95
220
 
96
- herb_extract_ruby_to_buffer(string, &output);
221
+ buffer_args_T args = { .buffer_value = output.value, .allocator = allocator };
97
222
 
98
- VALUE result = rb_utf8_str_new_cstr(output.value);
99
- free(output.value);
223
+ return rb_ensure(buffer_to_string_body, (VALUE) &args, buffer_cleanup, (VALUE) &args);
224
+ }
100
225
 
101
- return result;
226
+ static VALUE Herb_arena_stats(int argc, VALUE* argv, VALUE self) {
227
+ VALUE source, options;
228
+ rb_scan_args(argc, argv, "1:", &source, &options);
229
+
230
+ char* string = (char*) check_string(source);
231
+
232
+ parser_options_T parser_options = HERB_DEFAULT_PARSER_OPTIONS;
233
+
234
+ if (!NIL_P(options)) {
235
+ VALUE track_whitespace = rb_hash_lookup(options, rb_utf8_str_new_cstr("track_whitespace"));
236
+ if (NIL_P(track_whitespace)) { track_whitespace = rb_hash_lookup(options, ID2SYM(rb_intern("track_whitespace"))); }
237
+ if (!NIL_P(track_whitespace) && RTEST(track_whitespace)) { parser_options.track_whitespace = true; }
238
+
239
+ VALUE analyze = rb_hash_lookup(options, rb_utf8_str_new_cstr("analyze"));
240
+ if (NIL_P(analyze)) { analyze = rb_hash_lookup(options, ID2SYM(rb_intern("analyze"))); }
241
+ if (!NIL_P(analyze) && !RTEST(analyze)) { parser_options.analyze = false; }
242
+
243
+ VALUE strict = rb_hash_lookup(options, rb_utf8_str_new_cstr("strict"));
244
+ if (NIL_P(strict)) { strict = rb_hash_lookup(options, ID2SYM(rb_intern("strict"))); }
245
+ if (!NIL_P(strict)) { parser_options.strict = RTEST(strict); }
246
+ }
247
+
248
+ hb_allocator_T allocator;
249
+ if (!hb_allocator_init(&allocator, HB_ALLOCATOR_ARENA)) { return Qnil; }
250
+
251
+ AST_DOCUMENT_NODE_T* root = herb_parse(string, &parser_options, &allocator);
252
+
253
+ hb_arena_stats_T stats = hb_arena_get_stats((hb_arena_T*) allocator.context);
254
+
255
+ if (root != NULL) { ast_node_free((AST_NODE_T*) root, &allocator); }
256
+ hb_allocator_destroy(&allocator);
257
+
258
+ VALUE hash = rb_hash_new();
259
+ rb_hash_aset(hash, ID2SYM(rb_intern("pages")), SIZET2NUM(stats.pages));
260
+ rb_hash_aset(hash, ID2SYM(rb_intern("total_capacity")), SIZET2NUM(stats.total_capacity));
261
+ rb_hash_aset(hash, ID2SYM(rb_intern("total_used")), SIZET2NUM(stats.total_used));
262
+ rb_hash_aset(hash, ID2SYM(rb_intern("total_available")), SIZET2NUM(stats.total_available));
263
+ rb_hash_aset(hash, ID2SYM(rb_intern("allocations")), SIZET2NUM(stats.allocations));
264
+ rb_hash_aset(hash, ID2SYM(rb_intern("fragmentation")), SIZET2NUM(stats.fragmentation));
265
+ rb_hash_aset(hash, ID2SYM(rb_intern("default_page_size")), SIZET2NUM(stats.default_page_size));
266
+
267
+ return hash;
102
268
  }
103
269
 
104
- static VALUE Herb_extract_html(VALUE self, VALUE source) {
270
+ static VALUE make_tracking_hash(hb_allocator_tracking_stats_T* stats) {
271
+ VALUE hash = rb_hash_new();
272
+ rb_hash_aset(hash, ID2SYM(rb_intern("allocations")), SIZET2NUM(stats->allocation_count));
273
+ rb_hash_aset(hash, ID2SYM(rb_intern("deallocations")), SIZET2NUM(stats->deallocation_count));
274
+ rb_hash_aset(hash, ID2SYM(rb_intern("bytes_allocated")), SIZET2NUM(stats->bytes_allocated));
275
+ rb_hash_aset(hash, ID2SYM(rb_intern("bytes_deallocated")), SIZET2NUM(stats->bytes_deallocated));
276
+ rb_hash_aset(hash, ID2SYM(rb_intern("untracked_deallocations")), SIZET2NUM(stats->untracked_deallocation_count));
277
+
278
+ VALUE leaks = rb_ary_new();
279
+ for (size_t i = 0; i < stats->buckets_capacity; i++) {
280
+ if (stats->buckets[i].pointer != NULL && stats->buckets[i].pointer != (void*) 1) {
281
+ rb_ary_push(leaks, SIZET2NUM(stats->buckets[i].size));
282
+ }
283
+ }
284
+ rb_hash_aset(hash, ID2SYM(rb_intern("leaks")), leaks);
285
+
286
+ VALUE untracked = rb_ary_new_capa((long) stats->untracked_pointers_size);
287
+ for (size_t i = 0; i < stats->untracked_pointers_size; i++) {
288
+ rb_ary_push(untracked, rb_sprintf("%p", stats->untracked_pointers[i]));
289
+ }
290
+ rb_hash_aset(hash, ID2SYM(rb_intern("untracked_pointers")), untracked);
291
+
292
+ return hash;
293
+ }
294
+
295
+ static VALUE Herb_leak_check(VALUE self, VALUE source) {
105
296
  char* string = (char*) check_string(source);
106
- hb_buffer_T output;
297
+ VALUE result = rb_hash_new();
298
+
299
+ {
300
+ hb_allocator_T allocator;
301
+ if (!hb_allocator_init(&allocator, HB_ALLOCATOR_TRACKING)) { return Qnil; }
302
+
303
+ hb_array_T* tokens = herb_lex(string, &allocator);
304
+ if (tokens != NULL) { herb_free_tokens(&tokens, &allocator); }
305
+
306
+ hb_allocator_tracking_stats_T* stats = hb_allocator_tracking_stats(&allocator);
307
+ rb_hash_aset(result, ID2SYM(rb_intern("lex")), make_tracking_hash(stats));
308
+
309
+ hb_allocator_destroy(&allocator);
310
+ }
311
+
312
+ {
313
+ hb_allocator_T allocator;
314
+ if (!hb_allocator_init(&allocator, HB_ALLOCATOR_TRACKING)) { return Qnil; }
315
+
316
+ parser_options_T parser_options = HERB_DEFAULT_PARSER_OPTIONS;
317
+ AST_DOCUMENT_NODE_T* root = herb_parse(string, &parser_options, &allocator);
318
+ if (root != NULL) { ast_node_free((AST_NODE_T*) root, &allocator); }
319
+
320
+ hb_allocator_tracking_stats_T* stats = hb_allocator_tracking_stats(&allocator);
321
+ rb_hash_aset(result, ID2SYM(rb_intern("parse")), make_tracking_hash(stats));
322
+
323
+ hb_allocator_destroy(&allocator);
324
+ }
107
325
 
108
- if (!hb_buffer_init(&output, strlen(string))) { return Qnil; }
326
+ {
327
+ hb_allocator_T allocator;
328
+ if (!hb_allocator_init(&allocator, HB_ALLOCATOR_TRACKING)) { return Qnil; }
109
329
 
110
- herb_extract_html_to_buffer(string, &output);
330
+ hb_buffer_T output;
331
+ if (!hb_buffer_init(&output, strlen(string), &allocator)) { return Qnil; }
111
332
 
112
- VALUE result = rb_utf8_str_new_cstr(output.value);
113
- free(output.value);
333
+ herb_extract_ruby_options_T extract_options = HERB_EXTRACT_RUBY_DEFAULT_OPTIONS;
334
+ herb_extract_ruby_to_buffer_with_options(string, &output, &extract_options, &allocator);
335
+
336
+ hb_allocator_tracking_stats_T* stats = hb_allocator_tracking_stats(&allocator);
337
+ rb_hash_aset(result, ID2SYM(rb_intern("extract_ruby")), make_tracking_hash(stats));
338
+
339
+ hb_buffer_free(&output);
340
+ hb_allocator_destroy(&allocator);
341
+ }
342
+
343
+ {
344
+ hb_allocator_T allocator;
345
+ if (!hb_allocator_init(&allocator, HB_ALLOCATOR_TRACKING)) { return Qnil; }
346
+
347
+ hb_buffer_T output;
348
+ if (!hb_buffer_init(&output, strlen(string), &allocator)) { return Qnil; }
349
+
350
+ herb_extract_html_to_buffer(string, &output, &allocator);
351
+
352
+ hb_allocator_tracking_stats_T* stats = hb_allocator_tracking_stats(&allocator);
353
+ rb_hash_aset(result, ID2SYM(rb_intern("extract_html")), make_tracking_hash(stats));
354
+
355
+ hb_buffer_free(&output);
356
+ hb_allocator_destroy(&allocator);
357
+ }
114
358
 
115
359
  return result;
116
360
  }
@@ -119,9 +363,31 @@ static VALUE Herb_version(VALUE self) {
119
363
  VALUE gem_version = rb_const_get(self, rb_intern("VERSION"));
120
364
  VALUE libherb_version = rb_utf8_str_new_cstr(herb_version());
121
365
  VALUE libprism_version = rb_utf8_str_new_cstr(herb_prism_version());
122
- VALUE format_string = rb_utf8_str_new_cstr("herb gem v%s, libprism v%s, libherb v%s (Ruby C native extension)");
366
+
367
+ #ifdef HERB_GIT_BUILD
368
+ # ifdef HERB_GIT_SHA
369
+ VALUE format_string = rb_utf8_str_new_cstr(
370
+ "herb gem " HERB_GIT_SHA ", libprism v%s, libherb " HERB_GIT_SHA " (Ruby C native extension, built from source)"
371
+ );
372
+
373
+ return rb_funcall(rb_mKernel, rb_intern("sprintf"), 2, format_string, libprism_version);
374
+ # else
375
+ VALUE format_string =
376
+ rb_utf8_str_new_cstr("herb gem v%s, libprism v%s, libherb v%s (Ruby C native extension, built from source)");
123
377
 
124
378
  return rb_funcall(rb_mKernel, rb_intern("sprintf"), 4, format_string, gem_version, libprism_version, libherb_version);
379
+ # endif
380
+ #else
381
+ return rb_funcall(
382
+ rb_mKernel,
383
+ rb_intern("sprintf"),
384
+ 4,
385
+ rb_utf8_str_new_cstr("herb gem v%s, libprism v%s, libherb v%s (Ruby C native extension)"),
386
+ gem_version,
387
+ libprism_version,
388
+ libherb_version
389
+ );
390
+ #endif
125
391
  }
126
392
 
127
393
  __attribute__((__visibility__("default"))) void Init_herb(void) {
@@ -133,12 +399,16 @@ __attribute__((__visibility__("default"))) void Init_herb(void) {
133
399
  cResult = rb_define_class_under(mHerb, "Result", rb_cObject);
134
400
  cLexResult = rb_define_class_under(mHerb, "LexResult", cResult);
135
401
  cParseResult = rb_define_class_under(mHerb, "ParseResult", cResult);
402
+ cParserOptions = rb_define_class_under(mHerb, "ParserOptions", rb_cObject);
403
+
404
+ rb_init_node_classes();
405
+ rb_init_error_classes();
136
406
 
137
407
  rb_define_singleton_method(mHerb, "parse", Herb_parse, -1);
138
- rb_define_singleton_method(mHerb, "lex", Herb_lex, 1);
139
- rb_define_singleton_method(mHerb, "parse_file", Herb_parse_file, 1);
140
- rb_define_singleton_method(mHerb, "lex_file", Herb_lex_file, 1);
141
- rb_define_singleton_method(mHerb, "extract_ruby", Herb_extract_ruby, 1);
408
+ rb_define_singleton_method(mHerb, "lex", Herb_lex, -1);
409
+ rb_define_singleton_method(mHerb, "extract_ruby", Herb_extract_ruby, -1);
142
410
  rb_define_singleton_method(mHerb, "extract_html", Herb_extract_html, 1);
411
+ rb_define_singleton_method(mHerb, "arena_stats", Herb_arena_stats, -1);
412
+ rb_define_singleton_method(mHerb, "leak_check", Herb_leak_check, 1);
143
413
  rb_define_singleton_method(mHerb, "version", Herb_version, 0);
144
414
  }
data/ext/herb/extension.h CHANGED
@@ -11,5 +11,6 @@ extern VALUE cToken;
11
11
  extern VALUE cResult;
12
12
  extern VALUE cLexResult;
13
13
  extern VALUE cParseResult;
14
+ extern VALUE cParserOptions;
14
15
 
15
16
  #endif
@@ -5,10 +5,11 @@
5
5
  #include "nodes.h"
6
6
 
7
7
  #include "../../src/include/herb.h"
8
- #include "../../src/include/io.h"
9
8
  #include "../../src/include/location.h"
10
9
  #include "../../src/include/position.h"
11
10
  #include "../../src/include/token.h"
11
+ #include "../../src/include/util/hb_allocator.h"
12
+ #include "../../src/include/util/hb_string.h"
12
13
 
13
14
  const char* check_string(VALUE value) {
14
15
  if (NIL_P(value)) { return NULL; }
@@ -44,14 +45,19 @@ VALUE rb_range_from_c_struct(range_T range) {
44
45
  return rb_class_new_instance(2, args, cRange);
45
46
  }
46
47
 
48
+ VALUE rb_string_from_hb_string(hb_string_T string) {
49
+ if (hb_string_is_null(string)) { return Qnil; }
50
+
51
+ return rb_utf8_str_new(string.data, string.length);
52
+ }
53
+
47
54
  VALUE rb_token_from_c_struct(token_T* token) {
48
55
  if (!token) { return Qnil; }
49
56
 
50
- VALUE value = token->value ? rb_utf8_str_new_cstr(token->value) : Qnil;
51
-
57
+ VALUE value = rb_string_from_hb_string(token->value);
52
58
  VALUE range = rb_range_from_c_struct(token->range);
53
59
  VALUE location = rb_location_from_c_struct(token->location);
54
- VALUE type = rb_utf8_str_new_cstr(token_type_to_string(token->type));
60
+ VALUE type = rb_string_from_hb_string(token_type_to_string(token->type));
55
61
 
56
62
  VALUE args[4] = { value, range, location, type };
57
63
 
@@ -73,21 +79,25 @@ VALUE create_lex_result(hb_array_T* tokens, VALUE source) {
73
79
  return rb_class_new_instance(4, args, cLexResult);
74
80
  }
75
81
 
76
- VALUE create_parse_result(AST_DOCUMENT_NODE_T* root, VALUE source) {
82
+ VALUE create_parse_result(AST_DOCUMENT_NODE_T* root, VALUE source, const parser_options_T* options) {
77
83
  VALUE value = rb_node_from_c_struct((AST_NODE_T*) root);
78
84
  VALUE warnings = rb_ary_new();
79
85
  VALUE errors = rb_ary_new();
80
86
 
81
- VALUE args[4] = { value, source, warnings, errors };
82
-
83
- return rb_class_new_instance(4, args, cParseResult);
84
- }
87
+ VALUE kwargs = rb_hash_new();
88
+ rb_hash_aset(kwargs, ID2SYM(rb_intern("strict")), options->strict ? Qtrue : Qfalse);
89
+ rb_hash_aset(kwargs, ID2SYM(rb_intern("track_whitespace")), options->track_whitespace ? Qtrue : Qfalse);
90
+ rb_hash_aset(kwargs, ID2SYM(rb_intern("analyze")), options->analyze ? Qtrue : Qfalse);
91
+ rb_hash_aset(kwargs, ID2SYM(rb_intern("action_view_helpers")), options->action_view_helpers ? Qtrue : Qfalse);
92
+ rb_hash_aset(kwargs, ID2SYM(rb_intern("render_nodes")), options->render_nodes ? Qtrue : Qfalse);
93
+ rb_hash_aset(kwargs, ID2SYM(rb_intern("prism_nodes")), options->prism_nodes ? Qtrue : Qfalse);
94
+ rb_hash_aset(kwargs, ID2SYM(rb_intern("prism_nodes_deep")), options->prism_nodes_deep ? Qtrue : Qfalse);
95
+ rb_hash_aset(kwargs, ID2SYM(rb_intern("prism_program")), options->prism_program ? Qtrue : Qfalse);
85
96
 
86
- VALUE read_file_to_ruby_string(const char* file_path) {
87
- char* source = herb_read_file(file_path);
88
- VALUE source_value = rb_utf8_str_new_cstr(source);
97
+ VALUE parser_options_args[1] = { kwargs };
98
+ VALUE parser_options = rb_class_new_instance_kw(1, parser_options_args, cParserOptions, RB_PASS_KEYWORDS);
89
99
 
90
- free(source);
100
+ VALUE args[5] = { value, source, warnings, errors, parser_options };
91
101
 
92
- return source_value;
102
+ return rb_class_new_instance(5, args, cParseResult);
93
103
  }