herb 0.10.1-x86-linux-musl → 0.10.2-x86-linux-musl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd9ab6fe09717a673a5aa7f3ce0cb4d2264461839be66b4066d4f738e50d220b
4
- data.tar.gz: 1b23695b1a69420fd113fc4f2ec3738f569cb5aa14c41bd8eabf4356adebbfde
3
+ metadata.gz: d8580eb8bced26efa1b7d2e9d706be035b0c19d3f47cb2c8a97faede0c6fbbed
4
+ data.tar.gz: 2cbf6c0923d2a3a0ef088b8fc978e6f2895718571afd045a18b7eb10dd4ab397
5
5
  SHA512:
6
- metadata.gz: ba61010831b454c3e85df2146c5220387f3ce73c0354b0e250a836a25e73000dc0208eb30df6a03790e0e01428117e4a937d982a7409e1c309505b8c932c91c4
7
- data.tar.gz: 3889f34d9762abab0796c98df40c2234dd4a251e75732f676c5bfa75fe2a73b8100606cbb8e5727b422b1e9677a851dba878f301121d5f791aefae1b3fb9c649
6
+ metadata.gz: d3208283da316cc34d9e1c93ebbf685cea626b0e7b63138dd808b6fefc50495d8e50c0f256ce2fffdf6daf1d26ecdb937d1e48ffbac4d59acdb2451c6afb5d45
7
+ data.tar.gz: 59158a54fa34f44ee7599381b05d00ea3a38af915a7e53d82e60b67fe7572a9d8641e2bdba18a2e68295eb99507a380b26e871b925b635ced059c2202a9b7bc6
data/Makefile CHANGED
@@ -53,7 +53,7 @@ production_flags = $(warning_flags) -O3 -march=native -flto
53
53
  shared_library_flags = -fPIC
54
54
 
55
55
  # Default build mode (change this as needed)
56
- flags = $(warning_flags) $(debug_flags) $(prism_flags) -std=c99
56
+ flags = $(warning_flags) $(debug_flags) $(prism_flags) -std=gnu99
57
57
 
58
58
  # Separate test compilation flags
59
59
  test_flags = $(debug_flags) $(prism_flags) -std=gnu99
data/config.yml CHANGED
@@ -486,6 +486,16 @@ errors:
486
486
  - name: segment
487
487
  type: token
488
488
 
489
+ - name: TimeoutError
490
+ message:
491
+ template: "Parsing timed out after %zu ms."
492
+ arguments:
493
+ - timeout_ms
494
+
495
+ fields:
496
+ - name: timeout_ms
497
+ type: size_t
498
+
489
499
  warnings:
490
500
  fields: []
491
501
  types: []
@@ -53,6 +53,7 @@ static VALUE cStrictLocalsMissingParenthesisError;
53
53
  static VALUE cStrictLocalsDuplicateDeclarationError;
54
54
  static VALUE cVoidElementContentError;
55
55
  static VALUE cDotNotationCasingError;
56
+ static VALUE cTimeoutError;
56
57
 
57
58
  void rb_init_error_classes(void) {
58
59
  mErrors = rb_define_module_under(mHerb, "Errors");
@@ -94,6 +95,7 @@ void rb_init_error_classes(void) {
94
95
  cStrictLocalsDuplicateDeclarationError = rb_define_class_under(mErrors, "StrictLocalsDuplicateDeclarationError", cError);
95
96
  cVoidElementContentError = rb_define_class_under(mErrors, "VoidElementContentError", cError);
96
97
  cDotNotationCasingError = rb_define_class_under(mErrors, "DotNotationCasingError", cError);
98
+ cTimeoutError = rb_define_class_under(mErrors, "TimeoutError", cError);
97
99
  }
98
100
 
99
101
  static VALUE rb_unexpected_error_from_c_struct(UNEXPECTED_ERROR_T* unexpected_error) {
@@ -906,6 +908,27 @@ static VALUE rb_dot_notation_casing_error_from_c_struct(DOT_NOTATION_CASING_ERRO
906
908
  return rb_class_new_instance(4, args, cDotNotationCasingError);
907
909
  };
908
910
 
911
+ static VALUE rb_timeout_error_from_c_struct(TIMEOUT_ERROR_T* timeout_error) {
912
+ if (timeout_error == NULL) { return Qnil; }
913
+
914
+ ERROR_T* error = &timeout_error->base;
915
+
916
+ VALUE type = rb_string_from_hb_string(error_type_to_string(error));
917
+ VALUE location = rb_location_from_c_struct(error->location);
918
+ VALUE message = rb_string_from_hb_string(error->message);
919
+
920
+ VALUE timeout_error_timeout_ms = ULONG2NUM(timeout_error->timeout_ms);
921
+
922
+ VALUE args[4] = {
923
+ type,
924
+ location,
925
+ message,
926
+ timeout_error_timeout_ms
927
+ };
928
+
929
+ return rb_class_new_instance(4, args, cTimeoutError);
930
+ };
931
+
909
932
 
910
933
  VALUE rb_error_from_c_struct(ERROR_T* error) {
911
934
  if (!error) { return Qnil; }
@@ -948,6 +971,7 @@ VALUE rb_error_from_c_struct(ERROR_T* error) {
948
971
  case STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR: return rb_strict_locals_duplicate_declaration_error_from_c_struct((STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR_T*) error); break;
949
972
  case VOID_ELEMENT_CONTENT_ERROR: return rb_void_element_content_error_from_c_struct((VOID_ELEMENT_CONTENT_ERROR_T*) error); break;
950
973
  case DOT_NOTATION_CASING_ERROR: return rb_dot_notation_casing_error_from_c_struct((DOT_NOTATION_CASING_ERROR_T*) error); break;
974
+ case TIMEOUT_ERROR: return rb_timeout_error_from_c_struct((TIMEOUT_ERROR_T*) error); break;
951
975
  }
952
976
 
953
977
  return Qnil;
data/ext/herb/extension.c CHANGED
@@ -174,6 +174,21 @@ static VALUE Herb_parse(int argc, VALUE* argv, VALUE self) {
174
174
  if (NIL_P(html)) { html = rb_hash_lookup(options, ID2SYM(rb_intern("html"))); }
175
175
  if (!NIL_P(html) && !RTEST(html)) { parser_options.html = false; }
176
176
 
177
+ VALUE timeout = rb_hash_lookup(options, rb_utf8_str_new_cstr("timeout"));
178
+ if (NIL_P(timeout)) { timeout = rb_hash_lookup(options, ID2SYM(rb_intern("timeout"))); }
179
+ if (!NIL_P(timeout)) { parser_options.timeout_ms = (uint32_t) (NUM2DBL(timeout) * 1000); }
180
+
181
+ VALUE max_errors_sentinel = ID2SYM(rb_intern("__not_set__"));
182
+ VALUE max_errors = rb_hash_lookup2(options, rb_utf8_str_new_cstr("max_errors"), max_errors_sentinel);
183
+
184
+ if (max_errors == max_errors_sentinel) {
185
+ max_errors = rb_hash_lookup2(options, ID2SYM(rb_intern("max_errors")), max_errors_sentinel);
186
+ }
187
+
188
+ if (max_errors != max_errors_sentinel) {
189
+ parser_options.max_errors = NIL_P(max_errors) ? 0 : (uint32_t) NUM2UINT(max_errors);
190
+ }
191
+
177
192
  VALUE arena_stats = rb_hash_lookup(options, rb_utf8_str_new_cstr("arena_stats"));
178
193
  if (NIL_P(arena_stats)) { arena_stats = rb_hash_lookup(options, ID2SYM(rb_intern("arena_stats"))); }
179
194
  if (!NIL_P(arena_stats) && RTEST(arena_stats)) { print_arena_stats = true; }
@@ -95,6 +95,13 @@ VALUE create_parse_result(AST_DOCUMENT_NODE_T* root, VALUE source, const parser_
95
95
  rb_hash_aset(kwargs, ID2SYM(rb_intern("prism_nodes")), options->prism_nodes ? Qtrue : Qfalse);
96
96
  rb_hash_aset(kwargs, ID2SYM(rb_intern("prism_nodes_deep")), options->prism_nodes_deep ? Qtrue : Qfalse);
97
97
  rb_hash_aset(kwargs, ID2SYM(rb_intern("prism_program")), options->prism_program ? Qtrue : Qfalse);
98
+ rb_hash_aset(kwargs, ID2SYM(rb_intern("timeout")), DBL2NUM((double) options->timeout_ms / 1000.0));
99
+
100
+ rb_hash_aset(
101
+ kwargs,
102
+ ID2SYM(rb_intern("max_errors")),
103
+ options->max_errors == 0 ? Qnil : UINT2NUM(options->max_errors)
104
+ );
98
105
 
99
106
  VALUE parser_options_args[1] = { kwargs };
100
107
  VALUE parser_options = rb_class_new_instance_kw(1, parser_options_args, cParserOptions, RB_PASS_KEYWORDS);
data/herb.gemspec CHANGED
@@ -31,6 +31,7 @@ Gem::Specification.new do |spec|
31
31
  "lib/**/*.rb",
32
32
  "lib/**/*.yml",
33
33
  "sig/**/*.rbs",
34
+ "sig/manifest.yaml",
34
35
  "src/**/*.{c,h}",
35
36
  "ext/**/*.{c,h}",
36
37
  "templates/**/*.{rb,erb}",
data/lib/herb/3.2/herb.so CHANGED
Binary file
data/lib/herb/3.3/herb.so CHANGED
Binary file
data/lib/herb/3.4/herb.so CHANGED
Binary file
data/lib/herb/4.0/herb.so CHANGED
Binary file
@@ -784,7 +784,7 @@ module Herb
784
784
  def ensure_parallel!
785
785
  return if defined?(Parallel)
786
786
 
787
- Herb.ensure_installed { gem "parallel" } # steep:ignore
787
+ Herb.ensure_installed { gem "parallel" }
788
788
  end
789
789
 
790
790
  def collect_ruby_render_references
data/lib/herb/errors.rb CHANGED
@@ -1526,5 +1526,44 @@ module Herb
1526
1526
  end
1527
1527
  end
1528
1528
 
1529
+ class TimeoutError < Error
1530
+ include Colors
1531
+
1532
+ #| timeout_ms: Integer?,
1533
+ #| }
1534
+
1535
+ attr_reader :timeout_ms #: Integer?
1536
+
1537
+ #: (String, Location?, String, Integer) -> void
1538
+ def initialize(type, location, message, timeout_ms)
1539
+ super(type, location, message)
1540
+ @timeout_ms = timeout_ms
1541
+ end
1542
+
1543
+ #: () -> String
1544
+ def inspect
1545
+ tree_inspect.rstrip.gsub(/\s+$/, "")
1546
+ end
1547
+
1548
+ #: () -> serialized_timeout_error
1549
+ def to_hash
1550
+ super.merge(
1551
+ timeout_ms: timeout_ms
1552
+ ) #: Herb::serialized_timeout_error
1553
+ end
1554
+
1555
+ #: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
1556
+ def tree_inspect(indent: 0, depth: 0, depth_limit: 25)
1557
+ output = +""
1558
+
1559
+ output += white("@ #{bold(red(error_name))} #{dimmed("(location: #{location&.tree_inspect})\n")}")
1560
+ output += white("├── message: #{green(message.inspect)}\n")
1561
+ output += white("└── timeout_ms: #{green(timeout_ms.inspect)}\n")
1562
+ output += %(\n)
1563
+
1564
+ output.gsub(/^/, " " * indent)
1565
+ end
1566
+ end
1567
+
1529
1568
  end
1530
1569
  end
@@ -12,6 +12,8 @@ module Herb
12
12
  attr_reader :prism_program #: bool
13
13
  attr_reader :prism_nodes #: bool
14
14
  attr_reader :prism_nodes_deep #: bool
15
+ attr_reader :timeout #: Numeric
16
+ attr_reader :max_errors #: Integer?
15
17
 
16
18
  DEFAULT_STRICT = true #: bool
17
19
  DEFAULT_TRACK_WHITESPACE = false #: bool
@@ -23,9 +25,11 @@ module Herb
23
25
  DEFAULT_PRISM_PROGRAM = false #: bool
24
26
  DEFAULT_PRISM_NODES = false #: bool
25
27
  DEFAULT_PRISM_NODES_DEEP = false #: bool
28
+ DEFAULT_TIMEOUT = 1 #: Numeric
29
+ DEFAULT_MAX_ERRORS = 25 #: Integer
26
30
 
27
- #: (?strict: bool, ?track_whitespace: bool, ?analyze: bool, ?action_view_helpers: bool, ?transform_conditionals: bool, ?render_nodes: bool, ?strict_locals: bool, ?prism_nodes: bool, ?prism_nodes_deep: bool, ?prism_program: bool) -> void
28
- def initialize(strict: DEFAULT_STRICT, track_whitespace: DEFAULT_TRACK_WHITESPACE, analyze: DEFAULT_ANALYZE, action_view_helpers: DEFAULT_ACTION_VIEW_HELPERS, transform_conditionals: DEFAULT_TRANSFORM_CONDITIONALS, render_nodes: DEFAULT_RENDER_NODES, strict_locals: DEFAULT_STRICT_LOCALS, prism_nodes: DEFAULT_PRISM_NODES, prism_nodes_deep: DEFAULT_PRISM_NODES_DEEP, prism_program: DEFAULT_PRISM_PROGRAM)
31
+ #: (?strict: bool, ?track_whitespace: bool, ?analyze: bool, ?action_view_helpers: bool, ?transform_conditionals: bool, ?render_nodes: bool, ?strict_locals: bool, ?prism_nodes: bool, ?prism_nodes_deep: bool, ?prism_program: bool, ?timeout: Numeric) -> void
32
+ def initialize(strict: DEFAULT_STRICT, track_whitespace: DEFAULT_TRACK_WHITESPACE, analyze: DEFAULT_ANALYZE, action_view_helpers: DEFAULT_ACTION_VIEW_HELPERS, transform_conditionals: DEFAULT_TRANSFORM_CONDITIONALS, render_nodes: DEFAULT_RENDER_NODES, strict_locals: DEFAULT_STRICT_LOCALS, prism_nodes: DEFAULT_PRISM_NODES, prism_nodes_deep: DEFAULT_PRISM_NODES_DEEP, prism_program: DEFAULT_PRISM_PROGRAM, timeout: DEFAULT_TIMEOUT, max_errors: DEFAULT_MAX_ERRORS)
29
33
  @strict = strict
30
34
  @track_whitespace = track_whitespace
31
35
  @analyze = analyze
@@ -36,9 +40,11 @@ module Herb
36
40
  @prism_nodes = prism_nodes
37
41
  @prism_nodes_deep = prism_nodes_deep
38
42
  @prism_program = prism_program
43
+ @timeout = timeout
44
+ @max_errors = max_errors
39
45
  end
40
46
 
41
- #: () -> Hash[Symbol, bool]
47
+ #: () -> Hash[Symbol, (bool | Numeric | nil)]
42
48
  def to_h
43
49
  {
44
50
  strict: @strict,
@@ -51,6 +57,8 @@ module Herb
51
57
  prism_nodes: @prism_nodes,
52
58
  prism_nodes_deep: @prism_nodes_deep,
53
59
  prism_program: @prism_program,
60
+ timeout: @timeout,
61
+ max_errors: @max_errors,
54
62
  }
55
63
  end
56
64
 
@@ -66,7 +74,9 @@ module Herb
66
74
  "strict_locals=#{@strict_locals}\n " \
67
75
  "prism_nodes=#{@prism_nodes}\n " \
68
76
  "prism_nodes_deep=#{@prism_nodes_deep}\n " \
69
- "prism_program=#{@prism_program}>"
77
+ "prism_program=#{@prism_program}\n " \
78
+ "timeout=#{@timeout}\n " \
79
+ "max_errors=#{@max_errors}>"
70
80
  end
71
81
  end
72
82
  end
data/lib/herb/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # typed: true
3
3
 
4
4
  module Herb
5
- VERSION = "0.10.1"
5
+ VERSION = "0.10.2"
6
6
  end
data/sig/herb/errors.rbs CHANGED
@@ -694,5 +694,23 @@ module Herb
694
694
  # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
695
695
  def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
696
696
  end
697
+
698
+ class TimeoutError < Error
699
+ include Colors
700
+
701
+ attr_reader timeout_ms: Integer?
702
+
703
+ # : (String, Location?, String, Integer) -> void
704
+ def initialize: (String, Location?, String, Integer) -> void
705
+
706
+ # : () -> String
707
+ def inspect: () -> String
708
+
709
+ # : () -> serialized_timeout_error
710
+ def to_hash: () -> serialized_timeout_error
711
+
712
+ # : (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
713
+ def tree_inspect: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
714
+ end
697
715
  end
698
716
  end
@@ -22,6 +22,10 @@ module Herb
22
22
 
23
23
  attr_reader prism_nodes_deep: bool
24
24
 
25
+ attr_reader timeout: Numeric
26
+
27
+ attr_reader max_errors: Integer?
28
+
25
29
  DEFAULT_STRICT: bool
26
30
 
27
31
  DEFAULT_TRACK_WHITESPACE: bool
@@ -42,11 +46,15 @@ module Herb
42
46
 
43
47
  DEFAULT_PRISM_NODES_DEEP: bool
44
48
 
45
- # : (?strict: bool, ?track_whitespace: bool, ?analyze: bool, ?action_view_helpers: bool, ?transform_conditionals: bool, ?render_nodes: bool, ?strict_locals: bool, ?prism_nodes: bool, ?prism_nodes_deep: bool, ?prism_program: bool) -> void
46
- def initialize: (?strict: bool, ?track_whitespace: bool, ?analyze: bool, ?action_view_helpers: bool, ?transform_conditionals: bool, ?render_nodes: bool, ?strict_locals: bool, ?prism_nodes: bool, ?prism_nodes_deep: bool, ?prism_program: bool) -> void
49
+ DEFAULT_TIMEOUT: Numeric
50
+
51
+ DEFAULT_MAX_ERRORS: Integer
52
+
53
+ # : (?strict: bool, ?track_whitespace: bool, ?analyze: bool, ?action_view_helpers: bool, ?transform_conditionals: bool, ?render_nodes: bool, ?strict_locals: bool, ?prism_nodes: bool, ?prism_nodes_deep: bool, ?prism_program: bool, ?timeout: Numeric) -> void
54
+ def initialize: (?strict: bool, ?track_whitespace: bool, ?analyze: bool, ?action_view_helpers: bool, ?transform_conditionals: bool, ?render_nodes: bool, ?strict_locals: bool, ?prism_nodes: bool, ?prism_nodes_deep: bool, ?prism_program: bool, ?timeout: Numeric) -> void
47
55
 
48
- # : () -> Hash[Symbol, bool]
49
- def to_h: () -> Hash[Symbol, bool]
56
+ # : () -> Hash[Symbol, (bool | Numeric | nil)]
57
+ def to_h: () -> Hash[Symbol, bool | Numeric | nil]
50
58
 
51
59
  # : () -> String
52
60
  def inspect: () -> String
data/sig/manifest.yaml ADDED
@@ -0,0 +1,13 @@
1
+ dependencies:
2
+ - name: delegate
3
+ - name: did_you_mean
4
+ - name: fileutils
5
+ - name: json
6
+ - name: optparse
7
+ - name: pathname
8
+ - name: socket
9
+ - name: stringio
10
+ - name: tempfile
11
+ - name: time
12
+ - name: timeout
13
+ - name: yaml
@@ -169,4 +169,8 @@ module Herb
169
169
  segment: Herb::Token,
170
170
  }
171
171
 
172
+ type serialized_timeout_error = serialized_error & {
173
+ timeout_ms: Integer,
174
+ }
175
+
172
176
  end
@@ -277,7 +277,13 @@ bool search_in_nodes(const pm_node_t* node, void* data) {
277
277
  analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
278
278
 
279
279
  if (node->type == PM_IN_NODE) { analyzed->in_node_count++; }
280
- if (node->type == PM_MATCH_PREDICATE_NODE) { analyzed->in_node_count++; }
280
+ if (node->type == PM_CASE_MATCH_NODE) {
281
+ const pm_case_match_node_t* case_match_node = (const pm_case_match_node_t*) node;
282
+
283
+ if (case_match_node->predicate != NULL && case_match_node->predicate->type == PM_MATCH_PREDICATE_NODE) {
284
+ analyzed->in_node_count++;
285
+ }
286
+ }
281
287
 
282
288
  pm_visit_child_nodes(node, search_in_nodes, analyzed);
283
289
 
data/src/errors.c CHANGED
@@ -1248,6 +1248,33 @@ void append_dot_notation_casing_error(token_T* segment, position_T start, positi
1248
1248
  hb_array_append(errors, dot_notation_casing_error_init(segment, start, end, allocator));
1249
1249
  }
1250
1250
 
1251
+ TIMEOUT_ERROR_T* timeout_error_init(size_t timeout_ms, position_T start, position_T end, hb_allocator_T* allocator) {
1252
+ TIMEOUT_ERROR_T* timeout_error = hb_allocator_alloc(allocator, sizeof(TIMEOUT_ERROR_T));
1253
+
1254
+ if (!timeout_error) { return NULL; }
1255
+
1256
+ error_init(&timeout_error->base, TIMEOUT_ERROR, start, end);
1257
+
1258
+ const char* message_template = "Parsing timed out after %zu ms.";
1259
+
1260
+ char message[52];
1261
+ snprintf(
1262
+ message,
1263
+ sizeof(message),
1264
+ message_template,
1265
+ timeout_ms
1266
+ );
1267
+
1268
+ timeout_error->base.message = hb_string_copy(hb_string(message), allocator);
1269
+
1270
+ timeout_error->timeout_ms = timeout_ms;
1271
+ return timeout_error;
1272
+ }
1273
+
1274
+ void append_timeout_error(size_t timeout_ms, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors) {
1275
+ hb_array_append(errors, timeout_error_init(timeout_ms, start, end, allocator));
1276
+ }
1277
+
1251
1278
  hb_string_T error_type_to_string(ERROR_T* error) {
1252
1279
  switch (error->type) {
1253
1280
  case UNEXPECTED_ERROR: return hb_string("UNEXPECTED_ERROR");
@@ -1287,6 +1314,7 @@ hb_string_T error_type_to_string(ERROR_T* error) {
1287
1314
  case STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR: return hb_string("STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR");
1288
1315
  case VOID_ELEMENT_CONTENT_ERROR: return hb_string("VOID_ELEMENT_CONTENT_ERROR");
1289
1316
  case DOT_NOTATION_CASING_ERROR: return hb_string("DOT_NOTATION_CASING_ERROR");
1317
+ case TIMEOUT_ERROR: return hb_string("TIMEOUT_ERROR");
1290
1318
  }
1291
1319
 
1292
1320
  return hb_string("Unknown error_type_T");
@@ -1331,6 +1359,7 @@ hb_string_T error_human_type(ERROR_T* error) {
1331
1359
  case STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR: return hb_string("StrictLocalsDuplicateDeclarationError");
1332
1360
  case VOID_ELEMENT_CONTENT_ERROR: return hb_string("VoidElementContentError");
1333
1361
  case DOT_NOTATION_CASING_ERROR: return hb_string("DotNotationCasingError");
1362
+ case TIMEOUT_ERROR: return hb_string("TimeoutError");
1334
1363
  }
1335
1364
 
1336
1365
  return hb_string("Unknown error_type_T");
@@ -1588,6 +1617,12 @@ static void error_free_dot_notation_casing_error(DOT_NOTATION_CASING_ERROR_T* do
1588
1617
  error_free_base_error(&dot_notation_casing_error->base, allocator);
1589
1618
  }
1590
1619
 
1620
+ static void error_free_timeout_error(TIMEOUT_ERROR_T* timeout_error, hb_allocator_T* allocator) {
1621
+ // size_t is part of struct
1622
+
1623
+ error_free_base_error(&timeout_error->base, allocator);
1624
+ }
1625
+
1591
1626
  void error_free(ERROR_T* error, hb_allocator_T* allocator) {
1592
1627
  if (!error) { return; }
1593
1628
 
@@ -1629,6 +1664,7 @@ void error_free(ERROR_T* error, hb_allocator_T* allocator) {
1629
1664
  case STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR: error_free_strict_locals_duplicate_declaration_error((STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR_T*) error, allocator); break;
1630
1665
  case VOID_ELEMENT_CONTENT_ERROR: error_free_void_element_content_error((VOID_ELEMENT_CONTENT_ERROR_T*) error, allocator); break;
1631
1666
  case DOT_NOTATION_CASING_ERROR: error_free_dot_notation_casing_error((DOT_NOTATION_CASING_ERROR_T*) error, allocator); break;
1667
+ case TIMEOUT_ERROR: error_free_timeout_error((TIMEOUT_ERROR_T*) error, allocator); break;
1632
1668
  }
1633
1669
  }
1634
1670
 
@@ -2212,6 +2248,20 @@ static void error_pretty_print_dot_notation_casing_error(DOT_NOTATION_CASING_ERR
2212
2248
  pretty_print_token_property(error->segment, hb_string("segment"), indent, relative_indent, true, buffer);
2213
2249
  }
2214
2250
 
2251
+ static void error_pretty_print_timeout_error(TIMEOUT_ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
2252
+ if (!error) { return; }
2253
+
2254
+ hb_buffer_append(buffer, "@ ");
2255
+ hb_buffer_append_string(buffer, error_human_type((ERROR_T*) error));
2256
+ hb_buffer_append(buffer, " ");
2257
+
2258
+ pretty_print_location(error->base.location, buffer);
2259
+ hb_buffer_append(buffer, "\n");
2260
+
2261
+ pretty_print_quoted_property(hb_string("message"), error->base.message, indent, relative_indent, false, buffer);
2262
+ pretty_print_size_t_property(error->timeout_ms, hb_string("timeout_ms"), indent, relative_indent, true, buffer);
2263
+ }
2264
+
2215
2265
  void error_pretty_print(ERROR_T* error, const size_t indent, const size_t relative_indent, hb_buffer_T* buffer) {
2216
2266
  if (!error) { return; }
2217
2267
 
@@ -2253,6 +2303,7 @@ void error_pretty_print(ERROR_T* error, const size_t indent, const size_t relati
2253
2303
  case STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR: error_pretty_print_strict_locals_duplicate_declaration_error((STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR_T*) error, indent, relative_indent, buffer); break;
2254
2304
  case VOID_ELEMENT_CONTENT_ERROR: error_pretty_print_void_element_content_error((VOID_ELEMENT_CONTENT_ERROR_T*) error, indent, relative_indent, buffer); break;
2255
2305
  case DOT_NOTATION_CASING_ERROR: error_pretty_print_dot_notation_casing_error((DOT_NOTATION_CASING_ERROR_T*) error, indent, relative_indent, buffer); break;
2306
+ case TIMEOUT_ERROR: error_pretty_print_timeout_error((TIMEOUT_ERROR_T*) error, indent, relative_indent, buffer); break;
2256
2307
  }
2257
2308
  }
2258
2309
 
data/src/herb.c CHANGED
@@ -1,6 +1,7 @@
1
1
  #include "include/herb.h"
2
2
  #include "include/analyze/analyze.h"
3
3
  #include "include/analyze/prism_annotate.h"
4
+ #include "include/errors.h"
4
5
  #include "include/lexer/lexer.h"
5
6
  #include "include/lexer/token.h"
6
7
  #include "include/lib/hb_allocator.h"
@@ -44,6 +45,11 @@ HERB_EXPORTED_FUNCTION AST_DOCUMENT_NODE_T* herb_parse(
44
45
  parser_options_T parser_options = HERB_DEFAULT_PARSER_OPTIONS;
45
46
  if (options != NULL) { parser_options = *options; }
46
47
 
48
+ uint32_t error_count = 0;
49
+ parser_options.error_count = &error_count;
50
+
51
+ parser_options_set_deadline(&parser_options);
52
+
47
53
  if (parser_options.start_line > 0) {
48
54
  lexer.current_line = parser_options.start_line;
49
55
  lexer.previous_line = parser_options.start_line;
@@ -73,6 +79,16 @@ HERB_EXPORTED_FUNCTION AST_DOCUMENT_NODE_T* herb_parse(
73
79
  );
74
80
  }
75
81
 
82
+ if (parser_options_past_deadline(&parser_options)) {
83
+ append_timeout_error(
84
+ parser_options.timeout_ms,
85
+ document->base.location.start,
86
+ document->base.location.end,
87
+ allocator,
88
+ document->base.errors
89
+ );
90
+ }
91
+
76
92
  return document;
77
93
  }
78
94
 
data/src/include/errors.h CHANGED
@@ -51,6 +51,7 @@ typedef enum {
51
51
  STRICT_LOCALS_DUPLICATE_DECLARATION_ERROR,
52
52
  VOID_ELEMENT_CONTENT_ERROR,
53
53
  DOT_NOTATION_CASING_ERROR,
54
+ TIMEOUT_ERROR,
54
55
  } error_type_T;
55
56
 
56
57
  typedef struct ERROR_STRUCT {
@@ -267,6 +268,11 @@ typedef struct {
267
268
  token_T* segment;
268
269
  } DOT_NOTATION_CASING_ERROR_T;
269
270
 
271
+ typedef struct {
272
+ ERROR_T base;
273
+ size_t timeout_ms;
274
+ } TIMEOUT_ERROR_T;
275
+
270
276
  UNEXPECTED_ERROR_T* unexpected_error_init(hb_string_T description, hb_string_T expected, hb_string_T found, position_T start, position_T end, hb_allocator_T* allocator);
271
277
  void append_unexpected_error(hb_string_T description, hb_string_T expected, hb_string_T found, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors);
272
278
  UNEXPECTED_TOKEN_ERROR_T* unexpected_token_error_init(token_type_T expected_type, token_T* found, position_T start, position_T end, hb_allocator_T* allocator);
@@ -341,6 +347,8 @@ VOID_ELEMENT_CONTENT_ERROR_T* void_element_content_error_init(token_T* tag_name,
341
347
  void append_void_element_content_error(token_T* tag_name, hb_string_T helper_name, hb_string_T content_type, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors);
342
348
  DOT_NOTATION_CASING_ERROR_T* dot_notation_casing_error_init(token_T* segment, position_T start, position_T end, hb_allocator_T* allocator);
343
349
  void append_dot_notation_casing_error(token_T* segment, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors);
350
+ TIMEOUT_ERROR_T* timeout_error_init(size_t timeout_ms, position_T start, position_T end, hb_allocator_T* allocator);
351
+ void append_timeout_error(size_t timeout_ms, position_T start, position_T end, hb_allocator_T* allocator, hb_array_T* errors);
344
352
 
345
353
  void error_init(ERROR_T* error, error_type_T type, position_T start, position_T end);
346
354
 
@@ -0,0 +1,15 @@
1
+ #ifndef HERB_CLOCK_H
2
+ #define HERB_CLOCK_H
3
+
4
+ #include <stdint.h>
5
+ #include <time.h>
6
+
7
+ static inline uint64_t hb_monotonic_ms(void) {
8
+ struct timespec now;
9
+
10
+ clock_gettime(CLOCK_MONOTONIC, &now);
11
+
12
+ return (uint64_t) now.tv_sec * 1000 + (uint64_t) now.tv_nsec / 1000000;
13
+ }
14
+
15
+ #endif
@@ -5,6 +5,7 @@
5
5
  #include "../lexer/lexer.h"
6
6
  #include "../lib/hb_allocator.h"
7
7
  #include "../lib/hb_array.h"
8
+ #include "../lib/hb_clock.h"
8
9
 
9
10
  #include <stdint.h>
10
11
 
@@ -33,6 +34,10 @@ typedef struct PARSER_OPTIONS_STRUCT {
33
34
  bool html;
34
35
  uint32_t start_line;
35
36
  uint32_t start_column;
37
+ uint32_t timeout_ms;
38
+ uint32_t max_errors;
39
+ uint32_t* error_count;
40
+ uint64_t deadline_ms;
36
41
  } parser_options_T;
37
42
 
38
43
  typedef struct MATCH_TAGS_CONTEXT_STRUCT {
@@ -43,6 +48,29 @@ typedef struct MATCH_TAGS_CONTEXT_STRUCT {
43
48
 
44
49
  extern const parser_options_T HERB_DEFAULT_PARSER_OPTIONS;
45
50
 
51
+ static inline bool parser_options_past_deadline(const parser_options_T* options) {
52
+ if (options == NULL || options->timeout_ms == 0) { return false; }
53
+
54
+ return hb_monotonic_ms() >= options->deadline_ms;
55
+ }
56
+
57
+ static inline bool parser_options_errors_exceeded(const parser_options_T* options) {
58
+ if (options == NULL || options->error_count == NULL) { return false; }
59
+ if (options->max_errors == 0) { return false; }
60
+
61
+ return *options->error_count >= options->max_errors;
62
+ }
63
+
64
+ static inline void parser_options_increment_error_count(const parser_options_T* options) {
65
+ if (options != NULL && options->error_count != NULL) { (*options->error_count)++; }
66
+ }
67
+
68
+ static inline void parser_options_set_deadline(parser_options_T* options) {
69
+ if (options->timeout_ms == 0) { return; }
70
+
71
+ options->deadline_ms = hb_monotonic_ms() + options->timeout_ms;
72
+ }
73
+
46
74
  typedef struct PARSER_STRUCT {
47
75
  hb_allocator_T* allocator;
48
76
  lexer_T* lexer;
@@ -1,6 +1,6 @@
1
1
  #ifndef HERB_VERSION_H
2
2
  #define HERB_VERSION_H
3
3
 
4
- #define HERB_VERSION "0.10.1"
4
+ #define HERB_VERSION "0.10.2"
5
5
 
6
6
  #endif
data/src/parser.c CHANGED
@@ -23,7 +23,6 @@
23
23
  #include <strings.h>
24
24
 
25
25
  #define MAX_CONSECUTIVE_ERRORS 10
26
-
27
26
  static void parser_parse_in_data_state(parser_T* parser, hb_array_T* children, hb_array_T* errors);
28
27
  static void parser_parse_foreign_content(parser_T* parser, hb_array_T* children, hb_array_T* errors);
29
28
  static AST_ERB_CONTENT_NODE_T* parser_parse_erb_tag(parser_T* parser);
@@ -48,7 +47,10 @@ const parser_options_T HERB_DEFAULT_PARSER_OPTIONS = { .track_whitespace = false
48
47
  .dot_notation_tags = false,
49
48
  .html = true,
50
49
  .start_line = 0,
51
- .start_column = 0 };
50
+ .start_column = 0,
51
+ .timeout_ms = 1000,
52
+ .max_errors = 25,
53
+ .deadline_ms = 0 };
52
54
 
53
55
  size_t parser_sizeof(void) {
54
56
  return sizeof(struct PARSER_STRUCT);
@@ -1498,6 +1500,7 @@ static void parser_parse_in_data_state(parser_T* parser, hb_array_T* children, h
1498
1500
  }
1499
1501
 
1500
1502
  while (token_is_not(parser, TOKEN_EOF)) {
1503
+ if (parser_options_past_deadline(&parser->options)) { break; }
1501
1504
 
1502
1505
  if (token_is(parser, TOKEN_ERB_START)) {
1503
1506
  hb_array_append(children, parser_parse_erb_tag(parser));
@@ -1601,11 +1604,18 @@ static void parser_parse_in_data_state(parser_T* parser, hb_array_T* children, h
1601
1604
  }
1602
1605
  }
1603
1606
 
1604
- static size_t find_matching_close_tag(hb_array_T* nodes, size_t start_idx, hb_string_T tag_name) {
1607
+ static size_t find_matching_close_tag(
1608
+ hb_array_T* nodes,
1609
+ size_t start_index,
1610
+ hb_string_T tag_name,
1611
+ const parser_options_T* options
1612
+ ) {
1605
1613
  int depth = 0;
1606
1614
 
1607
- for (size_t i = start_idx + 1; i < hb_array_size(nodes); i++) {
1608
- AST_NODE_T* node = (AST_NODE_T*) hb_array_get(nodes, i);
1615
+ for (size_t index = start_index + 1; index < hb_array_size(nodes); index++) {
1616
+ if (parser_options_past_deadline(options)) { return (size_t) -1; }
1617
+
1618
+ AST_NODE_T* node = (AST_NODE_T*) hb_array_get(nodes, index);
1609
1619
  if (node == NULL) { continue; }
1610
1620
 
1611
1621
  if (node->type == AST_HTML_OPEN_TAG_NODE) {
@@ -1616,7 +1626,7 @@ static size_t find_matching_close_tag(hb_array_T* nodes, size_t start_idx, hb_st
1616
1626
  AST_HTML_CLOSE_TAG_NODE_T* close = (AST_HTML_CLOSE_TAG_NODE_T*) node;
1617
1627
 
1618
1628
  if (hb_string_equals_case_insensitive(close->tag_name->value, tag_name)) {
1619
- if (depth == 0) { return i; }
1629
+ if (depth == 0) { return index; }
1620
1630
  depth--;
1621
1631
  }
1622
1632
  }
@@ -1625,23 +1635,23 @@ static size_t find_matching_close_tag(hb_array_T* nodes, size_t start_idx, hb_st
1625
1635
  return (size_t) -1;
1626
1636
  }
1627
1637
 
1628
- static size_t find_implicit_close_index(hb_array_T* nodes, size_t start_idx, hb_string_T tag_name) {
1638
+ static size_t find_implicit_close_index(hb_array_T* nodes, size_t start_index, hb_string_T tag_name) {
1629
1639
  if (!has_optional_end_tag(tag_name)) { return (size_t) -1; }
1630
1640
 
1631
- for (size_t i = start_idx + 1; i < hb_array_size(nodes); i++) {
1632
- AST_NODE_T* node = (AST_NODE_T*) hb_array_get(nodes, i);
1641
+ for (size_t index = start_index + 1; index < hb_array_size(nodes); index++) {
1642
+ AST_NODE_T* node = (AST_NODE_T*) hb_array_get(nodes, index);
1633
1643
  if (node == NULL) { continue; }
1634
1644
 
1635
1645
  if (node->type == AST_HTML_OPEN_TAG_NODE) {
1636
1646
  AST_HTML_OPEN_TAG_NODE_T* open = (AST_HTML_OPEN_TAG_NODE_T*) node;
1637
1647
  hb_string_T next_tag_name = open->tag_name->value;
1638
1648
 
1639
- if (should_implicitly_close(tag_name, next_tag_name)) { return i; }
1649
+ if (should_implicitly_close(tag_name, next_tag_name)) { return index; }
1640
1650
  } else if (node->type == AST_HTML_CLOSE_TAG_NODE) {
1641
1651
  AST_HTML_CLOSE_TAG_NODE_T* close = (AST_HTML_CLOSE_TAG_NODE_T*) node;
1642
1652
  hb_string_T close_tag_name = close->tag_name->value;
1643
1653
 
1644
- if (parent_closes_element(tag_name, close_tag_name)) { return i; }
1654
+ if (parent_closes_element(tag_name, close_tag_name)) { return index; }
1645
1655
  }
1646
1656
  }
1647
1657
 
@@ -1655,6 +1665,35 @@ static hb_array_T* parser_build_elements_from_tags(
1655
1665
  hb_allocator_T* allocator
1656
1666
  );
1657
1667
 
1668
+ static bool has_close_tag_for_name(hb_array_T* close_tag_names, hb_string_T tag_name) {
1669
+ for (size_t i = 0; i < hb_array_size(close_tag_names); i++) {
1670
+ hb_string_T* name = (hb_string_T*) hb_array_get(close_tag_names, i);
1671
+ if (name != NULL && hb_string_equals_case_insensitive(*name, tag_name)) { return true; }
1672
+ }
1673
+
1674
+ return false;
1675
+ }
1676
+
1677
+ static hb_array_T* collect_close_tag_names(hb_array_T* nodes, hb_allocator_T* allocator) {
1678
+ hb_array_T* close_tag_names = hb_array_init(16, allocator);
1679
+
1680
+ for (size_t i = 0; i < hb_array_size(nodes); i++) {
1681
+ AST_NODE_T* node = (AST_NODE_T*) hb_array_get(nodes, i);
1682
+ if (node == NULL || node->type != AST_HTML_CLOSE_TAG_NODE) { continue; }
1683
+
1684
+ AST_HTML_CLOSE_TAG_NODE_T* close_tag = (AST_HTML_CLOSE_TAG_NODE_T*) node;
1685
+ hb_string_T name = close_tag->tag_name->value;
1686
+
1687
+ if (!has_close_tag_for_name(close_tag_names, name)) {
1688
+ hb_string_T* stored = hb_allocator_alloc(allocator, sizeof(hb_string_T));
1689
+ *stored = name;
1690
+ hb_array_append(close_tag_names, stored);
1691
+ }
1692
+ }
1693
+
1694
+ return close_tag_names;
1695
+ }
1696
+
1658
1697
  static hb_array_T* parser_build_elements_from_tags(
1659
1698
  hb_array_T* nodes,
1660
1699
  hb_array_T* errors,
@@ -1663,8 +1702,11 @@ static hb_array_T* parser_build_elements_from_tags(
1663
1702
  ) {
1664
1703
  bool strict = options ? options->strict : false;
1665
1704
  hb_array_T* result = hb_array_init(hb_array_size(nodes), allocator);
1705
+ hb_array_T* close_tag_names = collect_close_tag_names(nodes, allocator);
1666
1706
 
1667
1707
  for (size_t index = 0; index < hb_array_size(nodes); index++) {
1708
+ if (parser_options_past_deadline(options)) { break; }
1709
+
1668
1710
  AST_NODE_T* node = (AST_NODE_T*) hb_array_get(nodes, index);
1669
1711
  if (node == NULL) { continue; }
1670
1712
 
@@ -1672,7 +1714,11 @@ static hb_array_T* parser_build_elements_from_tags(
1672
1714
  AST_HTML_OPEN_TAG_NODE_T* open_tag = (AST_HTML_OPEN_TAG_NODE_T*) node;
1673
1715
  hb_string_T tag_name = open_tag->tag_name->value;
1674
1716
 
1675
- size_t close_index = find_matching_close_tag(nodes, index, tag_name);
1717
+ size_t close_index = (size_t) -1;
1718
+
1719
+ if (has_close_tag_for_name(close_tag_names, tag_name)) {
1720
+ close_index = find_matching_close_tag(nodes, index, tag_name, options);
1721
+ }
1676
1722
 
1677
1723
  if (close_index == (size_t) -1) {
1678
1724
  size_t implicit_close_index = find_implicit_close_index(nodes, index, tag_name);
@@ -1732,7 +1778,7 @@ static hb_array_T* parser_build_elements_from_tags(
1732
1778
 
1733
1779
  index = implicit_close_index - 1;
1734
1780
  } else {
1735
- if (hb_array_size(open_tag->base.errors) == 0) {
1781
+ if (hb_array_size(open_tag->base.errors) == 0 && !parser_options_errors_exceeded(options)) {
1736
1782
  append_missing_closing_tag_error(
1737
1783
  open_tag->tag_name,
1738
1784
  open_tag->base.location.start,
@@ -1740,6 +1786,8 @@ static hb_array_T* parser_build_elements_from_tags(
1740
1786
  allocator,
1741
1787
  open_tag->base.errors
1742
1788
  );
1789
+
1790
+ parser_options_increment_error_count(options);
1743
1791
  }
1744
1792
 
1745
1793
  hb_array_append(result, node);
@@ -1779,7 +1827,7 @@ static hb_array_T* parser_build_elements_from_tags(
1779
1827
  AST_HTML_CLOSE_TAG_NODE_T* close_tag = (AST_HTML_CLOSE_TAG_NODE_T*) node;
1780
1828
 
1781
1829
  if (!is_void_element(close_tag->tag_name->value)) {
1782
- if (hb_array_size(close_tag->base.errors) == 0) {
1830
+ if (hb_array_size(close_tag->base.errors) == 0 && !parser_options_errors_exceeded(options)) {
1783
1831
  append_missing_opening_tag_error(
1784
1832
  close_tag->tag_name,
1785
1833
  close_tag->base.location.start,
@@ -1787,6 +1835,8 @@ static hb_array_T* parser_build_elements_from_tags(
1787
1835
  allocator,
1788
1836
  close_tag->base.errors
1789
1837
  );
1838
+
1839
+ parser_options_increment_error_count(options);
1790
1840
  }
1791
1841
  }
1792
1842
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: herb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.1
4
+ version: 0.10.2
5
5
  platform: x86-linux-musl
6
6
  authors:
7
7
  - Marco Roth
@@ -397,12 +397,11 @@ files:
397
397
  - sig/herb/visitor.rbs
398
398
  - sig/herb/warnings.rbs
399
399
  - sig/herb_c_extension.rbs
400
+ - sig/manifest.yaml
400
401
  - sig/rubyvm.rbs
401
402
  - sig/serialized.rbs
402
403
  - sig/serialized_ast_errors.rbs
403
404
  - sig/serialized_ast_nodes.rbs
404
- - sig/vendor/did_you_mean.rbs
405
- - sig/vendor/parallel.rbs
406
405
  - src/analyze/action_view/attribute_extraction_helpers.c
407
406
  - src/analyze/action_view/generated_handlers.c
408
407
  - src/analyze/action_view/generated_handlers.h
@@ -488,6 +487,7 @@ files:
488
487
  - src/include/lib/hb_arena_debug.h
489
488
  - src/include/lib/hb_array.h
490
489
  - src/include/lib/hb_buffer.h
490
+ - src/include/lib/hb_clock.h
491
491
  - src/include/lib/hb_foreach.h
492
492
  - src/include/lib/hb_narray.h
493
493
  - src/include/lib/hb_string.h
@@ -705,7 +705,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
705
705
  - !ruby/object:Gem::Version
706
706
  version: 3.3.22
707
707
  requirements: []
708
- rubygems_version: 4.0.3
708
+ rubygems_version: 4.0.6
709
709
  specification_version: 4
710
710
  summary: The modern HTML+ERB Toolchain
711
711
  test_files: []
@@ -1,6 +0,0 @@
1
- module DidYouMean
2
- class SpellChecker
3
- def initialize: (dictionary: Array[String]) -> void
4
- def correct: (String input) -> Array[String]
5
- end
6
- end
@@ -1,4 +0,0 @@
1
- module Parallel
2
- def self.map: [T, U] (Array[T], ?in_processes: Integer) { (T) -> U } -> Array[U]
3
- def self.processor_count: () -> Integer
4
- end