herb 0.8.10-arm-linux-gnu → 0.9.0-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 (209) 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 +317 -34
  6. data/ext/herb/error_helpers.c +367 -140
  7. data/ext/herb/error_helpers.h +1 -0
  8. data/ext/herb/extconf.rb +67 -28
  9. data/ext/herb/extension.c +317 -51
  10. data/ext/herb/extension.h +1 -0
  11. data/ext/herb/extension_helpers.c +23 -14
  12. data/ext/herb/extension_helpers.h +2 -2
  13. data/ext/herb/nodes.c +537 -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 +1132 -157
  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 +82 -35
  36. data/lib/herb/errors.rb +563 -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 +57 -0
  41. data/lib/herb/position.rb +1 -0
  42. data/lib/herb/prism_inspect.rb +116 -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 +37 -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 +641 -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 +17 -3
  59. data/sig/herb/errors.rbs +258 -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 +42 -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 +25 -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/serialized_ast_errors.rbs +54 -6
  72. data/sig/serialized_ast_nodes.rbs +60 -6
  73. data/src/analyze/action_view/attribute_extraction_helpers.c +290 -0
  74. data/src/analyze/action_view/content_tag.c +70 -0
  75. data/src/analyze/action_view/link_to.c +143 -0
  76. data/src/analyze/action_view/registry.c +60 -0
  77. data/src/analyze/action_view/tag.c +64 -0
  78. data/src/analyze/action_view/tag_helper_node_builders.c +305 -0
  79. data/src/analyze/action_view/tag_helpers.c +748 -0
  80. data/src/analyze/action_view/turbo_frame_tag.c +88 -0
  81. data/src/analyze/analyze.c +882 -0
  82. data/src/{analyzed_ruby.c → analyze/analyzed_ruby.c} +13 -11
  83. data/src/analyze/builders.c +343 -0
  84. data/src/analyze/conditional_elements.c +594 -0
  85. data/src/analyze/conditional_open_tags.c +640 -0
  86. data/src/analyze/control_type.c +250 -0
  87. data/src/{analyze_helpers.c → analyze/helpers.c} +48 -23
  88. data/src/analyze/invalid_structures.c +193 -0
  89. data/src/{analyze_missing_end.c → analyze/missing_end.c} +33 -22
  90. data/src/analyze/parse_errors.c +84 -0
  91. data/src/analyze/prism_annotate.c +397 -0
  92. data/src/{analyze_transform.c → analyze/transform.c} +17 -3
  93. data/src/ast_node.c +17 -7
  94. data/src/ast_nodes.c +662 -387
  95. data/src/ast_pretty_print.c +190 -6
  96. data/src/errors.c +1076 -520
  97. data/src/extract.c +145 -49
  98. data/src/herb.c +52 -34
  99. data/src/html_util.c +241 -12
  100. data/src/include/analyze/action_view/attribute_extraction_helpers.h +36 -0
  101. data/src/include/analyze/action_view/tag_helper_handler.h +41 -0
  102. data/src/include/analyze/action_view/tag_helper_node_builders.h +70 -0
  103. data/src/include/analyze/action_view/tag_helpers.h +38 -0
  104. data/src/include/{analyze.h → analyze/analyze.h} +14 -4
  105. data/src/include/{analyzed_ruby.h → analyze/analyzed_ruby.h} +3 -3
  106. data/src/include/analyze/builders.h +27 -0
  107. data/src/include/analyze/conditional_elements.h +9 -0
  108. data/src/include/analyze/conditional_open_tags.h +9 -0
  109. data/src/include/analyze/control_type.h +14 -0
  110. data/src/include/{analyze_helpers.h → analyze/helpers.h} +4 -2
  111. data/src/include/analyze/invalid_structures.h +11 -0
  112. data/src/include/analyze/prism_annotate.h +16 -0
  113. data/src/include/ast_node.h +11 -5
  114. data/src/include/ast_nodes.h +117 -38
  115. data/src/include/ast_pretty_print.h +5 -0
  116. data/src/include/element_source.h +3 -8
  117. data/src/include/errors.h +148 -55
  118. data/src/include/extract.h +21 -5
  119. data/src/include/herb.h +18 -6
  120. data/src/include/herb_prism_node.h +13 -0
  121. data/src/include/html_util.h +7 -2
  122. data/src/include/io.h +3 -1
  123. data/src/include/lex_helpers.h +29 -0
  124. data/src/include/lexer.h +1 -1
  125. data/src/include/lexer_peek_helpers.h +87 -13
  126. data/src/include/lexer_struct.h +2 -0
  127. data/src/include/location.h +2 -1
  128. data/src/include/parser.h +27 -2
  129. data/src/include/parser_helpers.h +19 -3
  130. data/src/include/pretty_print.h +10 -5
  131. data/src/include/prism_context.h +45 -0
  132. data/src/include/prism_helpers.h +10 -7
  133. data/src/include/prism_serialized.h +12 -0
  134. data/src/include/token.h +16 -4
  135. data/src/include/token_struct.h +10 -3
  136. data/src/include/utf8.h +2 -1
  137. data/src/include/util/hb_allocator.h +78 -0
  138. data/src/include/util/hb_arena.h +6 -1
  139. data/src/include/util/hb_arena_debug.h +12 -1
  140. data/src/include/util/hb_array.h +7 -3
  141. data/src/include/util/hb_buffer.h +6 -4
  142. data/src/include/util/hb_foreach.h +79 -0
  143. data/src/include/util/hb_narray.h +8 -4
  144. data/src/include/util/hb_string.h +56 -9
  145. data/src/include/util.h +6 -3
  146. data/src/include/version.h +1 -1
  147. data/src/io.c +3 -2
  148. data/src/lexer.c +42 -30
  149. data/src/lexer_peek_helpers.c +12 -74
  150. data/src/location.c +2 -2
  151. data/src/main.c +53 -28
  152. data/src/parser.c +783 -247
  153. data/src/parser_helpers.c +110 -23
  154. data/src/parser_match_tags.c +109 -48
  155. data/src/pretty_print.c +29 -24
  156. data/src/prism_helpers.c +30 -27
  157. data/src/ruby_parser.c +2 -0
  158. data/src/token.c +151 -66
  159. data/src/token_matchers.c +0 -1
  160. data/src/utf8.c +7 -6
  161. data/src/util/hb_allocator.c +341 -0
  162. data/src/util/hb_arena.c +81 -56
  163. data/src/util/hb_arena_debug.c +32 -17
  164. data/src/util/hb_array.c +30 -15
  165. data/src/util/hb_buffer.c +17 -21
  166. data/src/util/hb_narray.c +22 -7
  167. data/src/util/hb_string.c +49 -35
  168. data/src/util.c +21 -11
  169. data/src/visitor.c +47 -0
  170. data/templates/ext/herb/error_helpers.c.erb +24 -11
  171. data/templates/ext/herb/error_helpers.h.erb +1 -0
  172. data/templates/ext/herb/nodes.c.erb +50 -16
  173. data/templates/ext/herb/nodes.h.erb +1 -0
  174. data/templates/java/error_helpers.c.erb +1 -1
  175. data/templates/java/nodes.c.erb +30 -8
  176. data/templates/java/org/herb/ast/Errors.java.erb +24 -1
  177. data/templates/java/org/herb/ast/Nodes.java.erb +80 -21
  178. data/templates/javascript/packages/core/src/errors.ts.erb +16 -3
  179. data/templates/javascript/packages/core/src/node-type-guards.ts.erb +3 -1
  180. data/templates/javascript/packages/core/src/nodes.ts.erb +109 -32
  181. data/templates/javascript/packages/node/extension/error_helpers.cpp.erb +13 -4
  182. data/templates/javascript/packages/node/extension/nodes.cpp.erb +43 -4
  183. data/templates/lib/herb/ast/nodes.rb.erb +88 -31
  184. data/templates/lib/herb/errors.rb.erb +15 -3
  185. data/templates/lib/herb/visitor.rb.erb +2 -2
  186. data/templates/rust/src/ast/nodes.rs.erb +97 -44
  187. data/templates/rust/src/errors.rs.erb +2 -1
  188. data/templates/rust/src/nodes.rs.erb +167 -15
  189. data/templates/rust/src/union_types.rs.erb +60 -0
  190. data/templates/rust/src/visitor.rs.erb +81 -0
  191. data/templates/src/{analyze_missing_end.c.erb → analyze/missing_end.c.erb} +9 -6
  192. data/templates/src/{analyze_transform.c.erb → analyze/transform.c.erb} +2 -2
  193. data/templates/src/ast_nodes.c.erb +34 -26
  194. data/templates/src/ast_pretty_print.c.erb +24 -5
  195. data/templates/src/errors.c.erb +60 -54
  196. data/templates/src/include/ast_nodes.h.erb +6 -2
  197. data/templates/src/include/ast_pretty_print.h.erb +5 -0
  198. data/templates/src/include/errors.h.erb +15 -11
  199. data/templates/src/include/util/hb_foreach.h.erb +20 -0
  200. data/templates/src/parser_match_tags.c.erb +10 -4
  201. data/templates/src/visitor.c.erb +2 -2
  202. data/templates/template.rb +204 -29
  203. data/templates/wasm/error_helpers.cpp.erb +9 -5
  204. data/templates/wasm/nodes.cpp.erb +41 -4
  205. metadata +57 -16
  206. data/src/analyze.c +0 -1608
  207. data/src/element_source.c +0 -12
  208. data/src/include/util/hb_system.h +0 -9
  209. 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,244 @@ 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 prism_nodes = rb_hash_lookup(options, rb_utf8_str_new_cstr("prism_nodes"));
140
+ if (NIL_P(prism_nodes)) { prism_nodes = rb_hash_lookup(options, ID2SYM(rb_intern("prism_nodes"))); }
141
+ if (!NIL_P(prism_nodes) && RTEST(prism_nodes)) { parser_options.prism_nodes = true; }
142
+
143
+ VALUE prism_nodes_deep = rb_hash_lookup(options, rb_utf8_str_new_cstr("prism_nodes_deep"));
144
+ if (NIL_P(prism_nodes_deep)) { prism_nodes_deep = rb_hash_lookup(options, ID2SYM(rb_intern("prism_nodes_deep"))); }
145
+ if (!NIL_P(prism_nodes_deep) && RTEST(prism_nodes_deep)) { parser_options.prism_nodes_deep = true; }
146
+
147
+ VALUE prism_program = rb_hash_lookup(options, rb_utf8_str_new_cstr("prism_program"));
148
+ if (NIL_P(prism_program)) { prism_program = rb_hash_lookup(options, ID2SYM(rb_intern("prism_program"))); }
149
+ if (!NIL_P(prism_program) && RTEST(prism_program)) { parser_options.prism_program = true; }
150
+
151
+ VALUE arena_stats = rb_hash_lookup(options, rb_utf8_str_new_cstr("arena_stats"));
152
+ if (NIL_P(arena_stats)) { arena_stats = rb_hash_lookup(options, ID2SYM(rb_intern("arena_stats"))); }
153
+ if (!NIL_P(arena_stats) && RTEST(arena_stats)) { print_arena_stats = true; }
60
154
  }
61
155
 
62
- AST_DOCUMENT_NODE_T* root = herb_parse(string, parser_options);
156
+ parse_args_T args = { 0 };
157
+ args.source = source;
158
+ args.parser_options = &parser_options;
63
159
 
64
- herb_analyze_parse_tree(root, string);
160
+ if (!hb_allocator_init(&args.allocator, HB_ALLOCATOR_ARENA)) { return Qnil; }
65
161
 
66
- VALUE result = create_parse_result(root, source);
162
+ args.root = herb_parse(string, &parser_options, &args.allocator);
67
163
 
68
- ast_node_free((AST_NODE_T*) root);
164
+ if (print_arena_stats) { hb_arena_print_stats((hb_arena_T*) args.allocator.context); }
69
165
 
70
- return result;
166
+ return rb_ensure(parse_convert_body, (VALUE) &args, parse_cleanup, (VALUE) &args);
71
167
  }
72
168
 
73
- static VALUE Herb_parse_file(VALUE self, VALUE path) {
74
- char* file_path = (char*) check_string(path);
169
+ static VALUE Herb_extract_ruby(int argc, VALUE* argv, VALUE self) {
170
+ VALUE source, options;
171
+ rb_scan_args(argc, argv, "1:", &source, &options);
75
172
 
76
- VALUE source_value = read_file_to_ruby_string(file_path);
77
- char* string = (char*) check_string(source_value);
173
+ char* string = (char*) check_string(source);
78
174
 
79
- AST_DOCUMENT_NODE_T* root = herb_parse(string, NULL);
175
+ hb_allocator_T allocator;
176
+ if (!hb_allocator_init(&allocator, HB_ALLOCATOR_ARENA)) { return Qnil; }
80
177
 
81
- herb_analyze_parse_tree(root, string);
178
+ hb_buffer_T output;
179
+ if (!hb_buffer_init(&output, strlen(string), &allocator)) { return Qnil; }
82
180
 
83
- VALUE result = create_parse_result(root, source_value);
181
+ herb_extract_ruby_options_T extract_options = HERB_EXTRACT_RUBY_DEFAULT_OPTIONS;
84
182
 
85
- ast_node_free((AST_NODE_T*) root);
183
+ if (!NIL_P(options)) {
184
+ VALUE semicolons_value = rb_hash_lookup(options, rb_utf8_str_new_cstr("semicolons"));
185
+ if (NIL_P(semicolons_value)) { semicolons_value = rb_hash_lookup(options, ID2SYM(rb_intern("semicolons"))); }
186
+ if (!NIL_P(semicolons_value)) { extract_options.semicolons = RTEST(semicolons_value); }
86
187
 
87
- return result;
188
+ VALUE comments_value = rb_hash_lookup(options, rb_utf8_str_new_cstr("comments"));
189
+ if (NIL_P(comments_value)) { comments_value = rb_hash_lookup(options, ID2SYM(rb_intern("comments"))); }
190
+ if (!NIL_P(comments_value)) { extract_options.comments = RTEST(comments_value); }
191
+
192
+ VALUE preserve_positions_value = rb_hash_lookup(options, rb_utf8_str_new_cstr("preserve_positions"));
193
+ if (NIL_P(preserve_positions_value)) {
194
+ preserve_positions_value = rb_hash_lookup(options, ID2SYM(rb_intern("preserve_positions")));
195
+ }
196
+ if (!NIL_P(preserve_positions_value)) { extract_options.preserve_positions = RTEST(preserve_positions_value); }
197
+ }
198
+
199
+ herb_extract_ruby_to_buffer_with_options(string, &output, &extract_options, &allocator);
200
+
201
+ buffer_args_T args = { .buffer_value = output.value, .allocator = allocator };
202
+
203
+ return rb_ensure(buffer_to_string_body, (VALUE) &args, buffer_cleanup, (VALUE) &args);
88
204
  }
89
205
 
90
- static VALUE Herb_extract_ruby(VALUE self, VALUE source) {
206
+ static VALUE Herb_extract_html(VALUE self, VALUE source) {
91
207
  char* string = (char*) check_string(source);
208
+
209
+ hb_allocator_T allocator;
210
+ if (!hb_allocator_init(&allocator, HB_ALLOCATOR_ARENA)) { return Qnil; }
211
+
92
212
  hb_buffer_T output;
213
+ if (!hb_buffer_init(&output, strlen(string), &allocator)) { return Qnil; }
93
214
 
94
- if (!hb_buffer_init(&output, strlen(string))) { return Qnil; }
215
+ herb_extract_html_to_buffer(string, &output, &allocator);
95
216
 
96
- herb_extract_ruby_to_buffer(string, &output);
217
+ buffer_args_T args = { .buffer_value = output.value, .allocator = allocator };
97
218
 
98
- VALUE result = rb_utf8_str_new_cstr(output.value);
99
- free(output.value);
219
+ return rb_ensure(buffer_to_string_body, (VALUE) &args, buffer_cleanup, (VALUE) &args);
220
+ }
100
221
 
101
- return result;
222
+ static VALUE Herb_arena_stats(int argc, VALUE* argv, VALUE self) {
223
+ VALUE source, options;
224
+ rb_scan_args(argc, argv, "1:", &source, &options);
225
+
226
+ char* string = (char*) check_string(source);
227
+
228
+ parser_options_T parser_options = HERB_DEFAULT_PARSER_OPTIONS;
229
+
230
+ if (!NIL_P(options)) {
231
+ VALUE track_whitespace = rb_hash_lookup(options, rb_utf8_str_new_cstr("track_whitespace"));
232
+ if (NIL_P(track_whitespace)) { track_whitespace = rb_hash_lookup(options, ID2SYM(rb_intern("track_whitespace"))); }
233
+ if (!NIL_P(track_whitespace) && RTEST(track_whitespace)) { parser_options.track_whitespace = true; }
234
+
235
+ VALUE analyze = rb_hash_lookup(options, rb_utf8_str_new_cstr("analyze"));
236
+ if (NIL_P(analyze)) { analyze = rb_hash_lookup(options, ID2SYM(rb_intern("analyze"))); }
237
+ if (!NIL_P(analyze) && !RTEST(analyze)) { parser_options.analyze = false; }
238
+
239
+ VALUE strict = rb_hash_lookup(options, rb_utf8_str_new_cstr("strict"));
240
+ if (NIL_P(strict)) { strict = rb_hash_lookup(options, ID2SYM(rb_intern("strict"))); }
241
+ if (!NIL_P(strict)) { parser_options.strict = RTEST(strict); }
242
+ }
243
+
244
+ hb_allocator_T allocator;
245
+ if (!hb_allocator_init(&allocator, HB_ALLOCATOR_ARENA)) { return Qnil; }
246
+
247
+ AST_DOCUMENT_NODE_T* root = herb_parse(string, &parser_options, &allocator);
248
+
249
+ hb_arena_stats_T stats = hb_arena_get_stats((hb_arena_T*) allocator.context);
250
+
251
+ if (root != NULL) { ast_node_free((AST_NODE_T*) root, &allocator); }
252
+ hb_allocator_destroy(&allocator);
253
+
254
+ VALUE hash = rb_hash_new();
255
+ rb_hash_aset(hash, ID2SYM(rb_intern("pages")), SIZET2NUM(stats.pages));
256
+ rb_hash_aset(hash, ID2SYM(rb_intern("total_capacity")), SIZET2NUM(stats.total_capacity));
257
+ rb_hash_aset(hash, ID2SYM(rb_intern("total_used")), SIZET2NUM(stats.total_used));
258
+ rb_hash_aset(hash, ID2SYM(rb_intern("total_available")), SIZET2NUM(stats.total_available));
259
+ rb_hash_aset(hash, ID2SYM(rb_intern("allocations")), SIZET2NUM(stats.allocations));
260
+ rb_hash_aset(hash, ID2SYM(rb_intern("fragmentation")), SIZET2NUM(stats.fragmentation));
261
+ rb_hash_aset(hash, ID2SYM(rb_intern("default_page_size")), SIZET2NUM(stats.default_page_size));
262
+
263
+ return hash;
102
264
  }
103
265
 
104
- static VALUE Herb_extract_html(VALUE self, VALUE source) {
266
+ static VALUE make_tracking_hash(hb_allocator_tracking_stats_T* stats) {
267
+ VALUE hash = rb_hash_new();
268
+ rb_hash_aset(hash, ID2SYM(rb_intern("allocations")), SIZET2NUM(stats->allocation_count));
269
+ rb_hash_aset(hash, ID2SYM(rb_intern("deallocations")), SIZET2NUM(stats->deallocation_count));
270
+ rb_hash_aset(hash, ID2SYM(rb_intern("bytes_allocated")), SIZET2NUM(stats->bytes_allocated));
271
+ rb_hash_aset(hash, ID2SYM(rb_intern("bytes_deallocated")), SIZET2NUM(stats->bytes_deallocated));
272
+ rb_hash_aset(hash, ID2SYM(rb_intern("untracked_deallocations")), SIZET2NUM(stats->untracked_deallocation_count));
273
+
274
+ VALUE leaks = rb_ary_new();
275
+ for (size_t i = 0; i < stats->buckets_capacity; i++) {
276
+ if (stats->buckets[i].pointer != NULL && stats->buckets[i].pointer != (void*) 1) {
277
+ rb_ary_push(leaks, SIZET2NUM(stats->buckets[i].size));
278
+ }
279
+ }
280
+ rb_hash_aset(hash, ID2SYM(rb_intern("leaks")), leaks);
281
+
282
+ VALUE untracked = rb_ary_new_capa((long) stats->untracked_pointers_size);
283
+ for (size_t i = 0; i < stats->untracked_pointers_size; i++) {
284
+ rb_ary_push(untracked, rb_sprintf("%p", stats->untracked_pointers[i]));
285
+ }
286
+ rb_hash_aset(hash, ID2SYM(rb_intern("untracked_pointers")), untracked);
287
+
288
+ return hash;
289
+ }
290
+
291
+ static VALUE Herb_leak_check(VALUE self, VALUE source) {
105
292
  char* string = (char*) check_string(source);
106
- hb_buffer_T output;
293
+ VALUE result = rb_hash_new();
294
+
295
+ {
296
+ hb_allocator_T allocator;
297
+ if (!hb_allocator_init(&allocator, HB_ALLOCATOR_TRACKING)) { return Qnil; }
298
+
299
+ hb_array_T* tokens = herb_lex(string, &allocator);
300
+ if (tokens != NULL) { herb_free_tokens(&tokens, &allocator); }
301
+
302
+ hb_allocator_tracking_stats_T* stats = hb_allocator_tracking_stats(&allocator);
303
+ rb_hash_aset(result, ID2SYM(rb_intern("lex")), make_tracking_hash(stats));
304
+
305
+ hb_allocator_destroy(&allocator);
306
+ }
307
+
308
+ {
309
+ hb_allocator_T allocator;
310
+ if (!hb_allocator_init(&allocator, HB_ALLOCATOR_TRACKING)) { return Qnil; }
311
+
312
+ parser_options_T parser_options = HERB_DEFAULT_PARSER_OPTIONS;
313
+ AST_DOCUMENT_NODE_T* root = herb_parse(string, &parser_options, &allocator);
314
+ if (root != NULL) { ast_node_free((AST_NODE_T*) root, &allocator); }
315
+
316
+ hb_allocator_tracking_stats_T* stats = hb_allocator_tracking_stats(&allocator);
317
+ rb_hash_aset(result, ID2SYM(rb_intern("parse")), make_tracking_hash(stats));
318
+
319
+ hb_allocator_destroy(&allocator);
320
+ }
107
321
 
108
- if (!hb_buffer_init(&output, strlen(string))) { return Qnil; }
322
+ {
323
+ hb_allocator_T allocator;
324
+ if (!hb_allocator_init(&allocator, HB_ALLOCATOR_TRACKING)) { return Qnil; }
109
325
 
110
- herb_extract_html_to_buffer(string, &output);
326
+ hb_buffer_T output;
327
+ if (!hb_buffer_init(&output, strlen(string), &allocator)) { return Qnil; }
111
328
 
112
- VALUE result = rb_utf8_str_new_cstr(output.value);
113
- free(output.value);
329
+ herb_extract_ruby_options_T extract_options = HERB_EXTRACT_RUBY_DEFAULT_OPTIONS;
330
+ herb_extract_ruby_to_buffer_with_options(string, &output, &extract_options, &allocator);
331
+
332
+ hb_allocator_tracking_stats_T* stats = hb_allocator_tracking_stats(&allocator);
333
+ rb_hash_aset(result, ID2SYM(rb_intern("extract_ruby")), make_tracking_hash(stats));
334
+
335
+ hb_buffer_free(&output);
336
+ hb_allocator_destroy(&allocator);
337
+ }
338
+
339
+ {
340
+ hb_allocator_T allocator;
341
+ if (!hb_allocator_init(&allocator, HB_ALLOCATOR_TRACKING)) { return Qnil; }
342
+
343
+ hb_buffer_T output;
344
+ if (!hb_buffer_init(&output, strlen(string), &allocator)) { return Qnil; }
345
+
346
+ herb_extract_html_to_buffer(string, &output, &allocator);
347
+
348
+ hb_allocator_tracking_stats_T* stats = hb_allocator_tracking_stats(&allocator);
349
+ rb_hash_aset(result, ID2SYM(rb_intern("extract_html")), make_tracking_hash(stats));
350
+
351
+ hb_buffer_free(&output);
352
+ hb_allocator_destroy(&allocator);
353
+ }
114
354
 
115
355
  return result;
116
356
  }
@@ -119,9 +359,31 @@ static VALUE Herb_version(VALUE self) {
119
359
  VALUE gem_version = rb_const_get(self, rb_intern("VERSION"));
120
360
  VALUE libherb_version = rb_utf8_str_new_cstr(herb_version());
121
361
  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)");
362
+
363
+ #ifdef HERB_GIT_BUILD
364
+ # ifdef HERB_GIT_SHA
365
+ VALUE format_string = rb_utf8_str_new_cstr(
366
+ "herb gem " HERB_GIT_SHA ", libprism v%s, libherb " HERB_GIT_SHA " (Ruby C native extension, built from source)"
367
+ );
368
+
369
+ return rb_funcall(rb_mKernel, rb_intern("sprintf"), 2, format_string, libprism_version);
370
+ # else
371
+ VALUE format_string =
372
+ rb_utf8_str_new_cstr("herb gem v%s, libprism v%s, libherb v%s (Ruby C native extension, built from source)");
123
373
 
124
374
  return rb_funcall(rb_mKernel, rb_intern("sprintf"), 4, format_string, gem_version, libprism_version, libherb_version);
375
+ # endif
376
+ #else
377
+ return rb_funcall(
378
+ rb_mKernel,
379
+ rb_intern("sprintf"),
380
+ 4,
381
+ rb_utf8_str_new_cstr("herb gem v%s, libprism v%s, libherb v%s (Ruby C native extension)"),
382
+ gem_version,
383
+ libprism_version,
384
+ libherb_version
385
+ );
386
+ #endif
125
387
  }
126
388
 
127
389
  __attribute__((__visibility__("default"))) void Init_herb(void) {
@@ -133,12 +395,16 @@ __attribute__((__visibility__("default"))) void Init_herb(void) {
133
395
  cResult = rb_define_class_under(mHerb, "Result", rb_cObject);
134
396
  cLexResult = rb_define_class_under(mHerb, "LexResult", cResult);
135
397
  cParseResult = rb_define_class_under(mHerb, "ParseResult", cResult);
398
+ cParserOptions = rb_define_class_under(mHerb, "ParserOptions", rb_cObject);
399
+
400
+ rb_init_node_classes();
401
+ rb_init_error_classes();
136
402
 
137
403
  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);
404
+ rb_define_singleton_method(mHerb, "lex", Herb_lex, -1);
405
+ rb_define_singleton_method(mHerb, "extract_ruby", Herb_extract_ruby, -1);
142
406
  rb_define_singleton_method(mHerb, "extract_html", Herb_extract_html, 1);
407
+ rb_define_singleton_method(mHerb, "arena_stats", Herb_arena_stats, -1);
408
+ rb_define_singleton_method(mHerb, "leak_check", Herb_leak_check, 1);
143
409
  rb_define_singleton_method(mHerb, "version", Herb_version, 0);
144
410
  }
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,24 @@ 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("prism_nodes")), options->prism_nodes ? Qtrue : Qfalse);
93
+ rb_hash_aset(kwargs, ID2SYM(rb_intern("prism_nodes_deep")), options->prism_nodes_deep ? Qtrue : Qfalse);
94
+ rb_hash_aset(kwargs, ID2SYM(rb_intern("prism_program")), options->prism_program ? Qtrue : Qfalse);
85
95
 
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);
96
+ VALUE parser_options_args[1] = { kwargs };
97
+ VALUE parser_options = rb_class_new_instance_kw(1, parser_options_args, cParserOptions, RB_PASS_KEYWORDS);
89
98
 
90
- free(source);
99
+ VALUE args[5] = { value, source, warnings, errors, parser_options };
91
100
 
92
- return source_value;
101
+ return rb_class_new_instance(5, args, cParseResult);
93
102
  }