herb 0.11.0-arm-linux-gnu → 0.11.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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/config.yml +3 -0
  3. data/ext/herb/nodes.c +34 -17
  4. data/lib/herb/3.2/herb.so +0 -0
  5. data/lib/herb/3.3/herb.so +0 -0
  6. data/lib/herb/3.4/herb.so +0 -0
  7. data/lib/herb/4.0/herb.so +0 -0
  8. data/lib/herb/ast/nodes.rb +32 -6
  9. data/lib/herb/bootstrap.rb +6 -2
  10. data/lib/herb/dev/runner.rb +6 -1
  11. data/lib/herb/engine/compiler.rb +47 -11
  12. data/lib/herb/engine/slots/dynamics_compiler.rb +10 -0
  13. data/lib/herb/engine/slots/markers.rb +24 -0
  14. data/lib/herb/engine/slots/state_compiler.rb +1 -2
  15. data/lib/herb/engine/slots/visitor.rb +39 -28
  16. data/lib/herb/engine.rb +0 -2
  17. data/lib/herb/version.rb +1 -1
  18. data/sig/herb/ast/nodes.rbs +10 -4
  19. data/sig/herb/bootstrap.rbs +2 -0
  20. data/sig/herb/dev/runner.rbs +3 -0
  21. data/sig/herb/engine/compiler.rbs +14 -3
  22. data/sig/herb/engine/slots/dynamics_compiler.rbs +3 -0
  23. data/sig/herb/engine/slots/markers.rbs +14 -0
  24. data/sig/herb/engine/slots/visitor.rbs +17 -13
  25. data/sig/serialized_ast_nodes.rbs +1 -0
  26. data/src/analyze/action_view/tag_helper_node_builders.c +6 -3
  27. data/src/analyze/action_view/tag_helpers.c +76 -20
  28. data/src/analyze/analyze.c +5 -25
  29. data/src/analyze/analyze_helpers.c +94 -232
  30. data/src/analyze/analyzed_ruby.c +2 -0
  31. data/src/analyze/builders.c +1 -0
  32. data/src/analyze/control_type.c +17 -7
  33. data/src/analyze/prism_annotate.c +32 -9
  34. data/src/ast/ast_nodes.c +3 -1
  35. data/src/ast/ast_pretty_print.c +5 -1
  36. data/src/extract.c +204 -143
  37. data/src/include/analyze/analyze.h +0 -22
  38. data/src/include/analyze/analyzed_ruby.h +25 -0
  39. data/src/include/analyze/helpers.h +2 -22
  40. data/src/include/ast/ast_nodes.h +2 -1
  41. data/src/include/lexer/lexer.h +1 -0
  42. data/src/include/prism/herb_prism_node.h +6 -1
  43. data/src/include/version.h +1 -1
  44. data/src/lexer.c +32 -0
  45. data/src/prism/prism_helpers.c +91 -0
  46. data/templates/ext/herb/nodes.c.erb +1 -1
  47. data/templates/java/nodes.c.erb +1 -1
  48. data/templates/javascript/packages/core/src/nodes.ts.erb +21 -1
  49. data/templates/javascript/packages/node/extension/nodes.cpp.erb +1 -1
  50. data/templates/rust/src/ast/nodes.rs.erb +1 -4
  51. data/templates/wasm/nodes.cpp.erb +5 -15
  52. metadata +1 -1
@@ -226,23 +226,6 @@ bool has_error_message(analyzed_ruby_T* analyzed, const char* message) {
226
226
  return false;
227
227
  }
228
228
 
229
- bool search_if_nodes(const pm_node_t* node, void* data) {
230
- analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
231
-
232
- if (node->type == PM_IF_NODE) {
233
- const pm_if_node_t* if_node = (const pm_if_node_t*) node;
234
-
235
- bool has_if_keyword = if_node->if_keyword_loc.start != NULL && if_node->if_keyword_loc.end != NULL;
236
- bool has_end_keyword = if_node->end_keyword_loc.start != NULL && if_node->end_keyword_loc.end != NULL;
237
-
238
- if (has_if_keyword && has_end_keyword) { analyzed->if_node_count++; }
239
- }
240
-
241
- pm_visit_child_nodes(node, search_if_nodes, analyzed);
242
-
243
- return false;
244
- }
245
-
246
229
  bool is_do_block(pm_location_t opening_location) {
247
230
  size_t length = opening_location.end - opening_location.start;
248
231
 
@@ -285,257 +268,136 @@ bool has_valid_block_closing(pm_location_t opening_loc, pm_location_t closing_lo
285
268
  return false;
286
269
  }
287
270
 
288
- bool search_block_nodes(const pm_node_t* node, void* data) {
289
- analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
290
-
291
- if (node->type == PM_BLOCK_NODE) {
292
- pm_block_node_t* block_node = (pm_block_node_t*) node;
293
-
294
- bool has_opening = is_do_block(block_node->opening_loc) || is_brace_block(block_node->opening_loc);
295
- bool is_unclosed = !has_valid_block_closing(block_node->opening_loc, block_node->closing_loc);
296
-
297
- if (has_opening && is_unclosed) { analyzed->block_node_count++; }
298
- }
299
-
300
- if (node->type == PM_LAMBDA_NODE) {
301
- pm_lambda_node_t* lambda_node = (pm_lambda_node_t*) node;
302
-
303
- bool has_opening = is_do_block(lambda_node->opening_loc) || is_brace_block(lambda_node->opening_loc);
304
- bool is_unclosed = !has_valid_block_closing(lambda_node->opening_loc, lambda_node->closing_loc);
305
-
306
- if (has_opening && is_unclosed) { analyzed->block_node_count++; }
307
- }
308
-
309
- pm_visit_child_nodes(node, search_block_nodes, analyzed);
310
-
311
- return false;
312
- }
313
-
314
- bool search_case_nodes(const pm_node_t* node, void* data) {
315
- analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
316
-
317
- if (node->type == PM_CASE_NODE) { analyzed->case_node_count++; }
318
-
319
- pm_visit_child_nodes(node, search_case_nodes, analyzed);
320
-
321
- return false;
322
- }
323
-
324
- bool search_case_match_nodes(const pm_node_t* node, void* data) {
325
- analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
326
-
327
- if (node->type == PM_CASE_MATCH_NODE) { analyzed->case_match_node_count++; }
328
-
329
- pm_visit_child_nodes(node, search_case_match_nodes, analyzed);
330
-
331
- return false;
271
+ static bool has_keyword_location(pm_location_t location) {
272
+ return location.start != NULL && location.end != NULL;
332
273
  }
333
274
 
334
- bool search_while_nodes(const pm_node_t* node, void* data) {
335
- analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
336
-
337
- if (node->type == PM_WHILE_NODE) { analyzed->while_node_count++; }
338
-
339
- pm_visit_child_nodes(node, search_while_nodes, analyzed);
275
+ static bool is_unclosed_block(pm_location_t opening_loc, pm_location_t closing_loc) {
276
+ bool has_opening = is_do_block(opening_loc) || is_brace_block(opening_loc);
340
277
 
341
- return false;
278
+ return has_opening && !has_valid_block_closing(opening_loc, closing_loc);
342
279
  }
343
280
 
344
- bool search_for_nodes(const pm_node_t* node, void* data) {
281
+ bool search_control_flow_nodes(const pm_node_t* node, void* data) {
345
282
  analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
346
283
 
347
- if (node->type == PM_FOR_NODE) { analyzed->for_node_count++; }
348
-
349
- pm_visit_child_nodes(node, search_for_nodes, analyzed);
350
-
351
- return false;
352
- }
353
-
354
- bool search_until_nodes(const pm_node_t* node, void* data) {
355
- analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
356
-
357
- if (node->type == PM_UNTIL_NODE) { analyzed->until_node_count++; }
358
-
359
- pm_visit_child_nodes(node, search_until_nodes, analyzed);
360
-
361
- return false;
362
- }
363
-
364
- bool search_begin_nodes(const pm_node_t* node, void* data) {
365
- analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
366
-
367
- if (node->type == PM_BEGIN_NODE) { analyzed->begin_node_count++; }
368
-
369
- pm_visit_child_nodes(node, search_begin_nodes, analyzed);
370
-
371
- return false;
372
- }
373
-
374
- bool search_unless_nodes(const pm_node_t* node, void* data) {
375
- analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
376
-
377
- if (node->type == PM_UNLESS_NODE) {
378
- const pm_unless_node_t* unless_node = (const pm_unless_node_t*) node;
379
-
380
- bool has_if_keyword = unless_node->keyword_loc.start != NULL && unless_node->keyword_loc.end != NULL;
381
- bool has_end_keyword = unless_node->end_keyword_loc.start != NULL && unless_node->end_keyword_loc.end != NULL;
382
-
383
- if (has_if_keyword && has_end_keyword) { analyzed->unless_node_count++; }
384
- }
385
-
386
- pm_visit_child_nodes(node, search_unless_nodes, analyzed);
387
-
388
- return false;
389
- }
390
-
391
- bool search_when_nodes(const pm_node_t* node, void* data) {
392
- analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
393
-
394
- if (node->type == PM_WHEN_NODE) { analyzed->when_node_count++; }
395
-
396
- pm_visit_child_nodes(node, search_when_nodes, analyzed);
397
-
398
- return false;
399
- }
400
-
401
- bool search_in_nodes(const pm_node_t* node, void* data) {
402
- analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
284
+ switch (node->type) {
285
+ case PM_IF_NODE: {
286
+ const pm_if_node_t* if_node = (const pm_if_node_t*) node;
403
287
 
404
- if (node->type == PM_IN_NODE) { analyzed->in_node_count++; }
405
- if (node->type == PM_CASE_MATCH_NODE) {
406
- const pm_case_match_node_t* case_match_node = (const pm_case_match_node_t*) node;
288
+ if (has_keyword_location(if_node->if_keyword_loc) && has_keyword_location(if_node->end_keyword_loc)) {
289
+ analyzed->if_node_count++;
290
+ }
407
291
 
408
- if (case_match_node->predicate != NULL && case_match_node->predicate->type == PM_MATCH_PREDICATE_NODE) {
409
- analyzed->in_node_count++;
292
+ if (has_keyword_location(if_node->then_keyword_loc)) { analyzed->then_keyword_count++; }
293
+ break;
410
294
  }
411
- }
412
-
413
- pm_visit_child_nodes(node, search_in_nodes, analyzed);
414
295
 
415
- return false;
416
- }
296
+ case PM_UNLESS_NODE: {
297
+ const pm_unless_node_t* unless_node = (const pm_unless_node_t*) node;
417
298
 
418
- bool search_unexpected_elsif_nodes(analyzed_ruby_T* analyzed) {
419
- if (has_error_message(analyzed, "unexpected 'elsif', ignoring it")) {
420
- analyzed->elsif_node_count++;
421
- return true;
422
- }
299
+ if (has_keyword_location(unless_node->keyword_loc) && has_keyword_location(unless_node->end_keyword_loc)) {
300
+ analyzed->unless_node_count++;
301
+ }
423
302
 
424
- return false;
425
- }
303
+ if (has_keyword_location(unless_node->then_keyword_loc)) { analyzed->then_keyword_count++; }
304
+ break;
305
+ }
426
306
 
427
- bool search_unexpected_else_nodes(analyzed_ruby_T* analyzed) {
428
- if (has_error_message(analyzed, "unexpected 'else', ignoring it")) {
429
- analyzed->else_node_count++;
430
- return true;
431
- }
307
+ case PM_WHEN_NODE: {
308
+ const pm_when_node_t* when_node = (const pm_when_node_t*) node;
432
309
 
433
- return false;
434
- }
310
+ analyzed->when_node_count++;
435
311
 
436
- bool search_unexpected_end_nodes(analyzed_ruby_T* analyzed) {
437
- if (has_error_message(analyzed, "unexpected 'end', ignoring it")) {
438
- if (has_error_message(analyzed, "unexpected '=', ignoring it")) {
439
- // `=end`
440
- return false;
312
+ if (has_keyword_location(when_node->then_keyword_loc)) { analyzed->then_keyword_count++; }
313
+ break;
441
314
  }
442
315
 
443
- analyzed->end_count++;
444
- return true;
445
- }
446
-
447
- return false;
448
- }
449
-
450
- bool search_unexpected_block_closing_nodes(analyzed_ruby_T* analyzed) {
451
- if (has_error_message(analyzed, "unexpected '}', ignoring it")) {
452
- analyzed->block_closing_count++;
453
- return true;
454
- }
316
+ case PM_BLOCK_NODE: {
317
+ const pm_block_node_t* block_node = (const pm_block_node_t*) node;
455
318
 
456
- return false;
457
- }
319
+ if (is_unclosed_block(block_node->opening_loc, block_node->closing_loc)) { analyzed->block_node_count++; }
320
+ break;
321
+ }
458
322
 
459
- bool search_unexpected_when_nodes(analyzed_ruby_T* analyzed) {
460
- if (has_error_message(analyzed, "unexpected 'when', ignoring it")) {
461
- analyzed->when_node_count++;
462
- return true;
463
- }
323
+ case PM_LAMBDA_NODE: {
324
+ const pm_lambda_node_t* lambda_node = (const pm_lambda_node_t*) node;
464
325
 
465
- return false;
466
- }
326
+ if (is_unclosed_block(lambda_node->opening_loc, lambda_node->closing_loc)) { analyzed->block_node_count++; }
327
+ break;
328
+ }
467
329
 
468
- bool search_unexpected_in_nodes(analyzed_ruby_T* analyzed) {
469
- if (has_error_message(analyzed, "unexpected 'in', ignoring it")) {
470
- analyzed->in_node_count++;
471
- return true;
472
- }
330
+ case PM_CASE_MATCH_NODE: {
331
+ const pm_case_match_node_t* case_match_node = (const pm_case_match_node_t*) node;
473
332
 
474
- return false;
475
- }
333
+ analyzed->case_match_node_count++;
476
334
 
477
- bool search_unexpected_rescue_nodes(analyzed_ruby_T* analyzed) {
478
- if (has_error_message(analyzed, "unexpected 'rescue', ignoring it")) {
479
- analyzed->rescue_node_count++;
480
- return true;
481
- }
335
+ if (case_match_node->predicate != NULL && case_match_node->predicate->type == PM_MATCH_PREDICATE_NODE) {
336
+ analyzed->in_node_count++;
337
+ }
338
+ break;
339
+ }
482
340
 
483
- return false;
484
- }
341
+ case PM_CASE_NODE: analyzed->case_node_count++; break;
342
+ case PM_IN_NODE: analyzed->in_node_count++; break;
343
+ case PM_WHILE_NODE: analyzed->while_node_count++; break;
344
+ case PM_FOR_NODE: analyzed->for_node_count++; break;
345
+ case PM_UNTIL_NODE: analyzed->until_node_count++; break;
346
+ case PM_BEGIN_NODE: analyzed->begin_node_count++; break;
347
+ case PM_YIELD_NODE: analyzed->yield_node_count++; break;
485
348
 
486
- bool search_unexpected_ensure_nodes(analyzed_ruby_T* analyzed) {
487
- if (has_error_message(analyzed, "unexpected 'ensure', ignoring it")) {
488
- analyzed->ensure_node_count++;
489
- return true;
349
+ default: break;
490
350
  }
491
351
 
492
- return false;
493
- }
494
-
495
- bool search_yield_nodes(const pm_node_t* node, void* data) {
496
- analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
497
-
498
- if (node->type == PM_YIELD_NODE) { analyzed->yield_node_count++; }
499
-
500
- pm_visit_child_nodes(node, search_yield_nodes, analyzed);
352
+ pm_visit_child_nodes(node, search_control_flow_nodes, analyzed);
501
353
 
502
354
  return false;
503
355
  }
504
356
 
505
- bool search_then_keywords(const pm_node_t* node, void* data) {
506
- analyzed_ruby_T* analyzed = (analyzed_ruby_T*) data;
507
-
508
- switch (node->type) {
509
- case PM_IF_NODE: {
510
- const pm_if_node_t* if_node = (const pm_if_node_t*) node;
511
- if (if_node->then_keyword_loc.start != NULL && if_node->then_keyword_loc.end != NULL) {
512
- analyzed->then_keyword_count++;
513
- }
514
- break;
515
- }
516
-
517
- case PM_UNLESS_NODE: {
518
- const pm_unless_node_t* unless_node = (const pm_unless_node_t*) node;
519
- if (unless_node->then_keyword_loc.start != NULL && unless_node->then_keyword_loc.end != NULL) {
520
- analyzed->then_keyword_count++;
521
- }
522
- break;
523
- }
357
+ void search_unexpected_keyword_errors(analyzed_ruby_T* analyzed) {
358
+ bool unexpected_elsif = false;
359
+ bool unexpected_else = false;
360
+ bool unexpected_end = false;
361
+ bool unexpected_equals = false;
362
+ bool unexpected_closing_brace = false;
363
+ bool unexpected_when = false;
364
+ bool unexpected_in = false;
365
+ bool unexpected_rescue = false;
366
+ bool unexpected_ensure = false;
524
367
 
525
- case PM_WHEN_NODE: {
526
- const pm_when_node_t* when_node = (const pm_when_node_t*) node;
527
- if (when_node->then_keyword_loc.start != NULL && when_node->then_keyword_loc.end != NULL) {
528
- analyzed->then_keyword_count++;
529
- }
530
- break;
368
+ for (const pm_diagnostic_t* error = (const pm_diagnostic_t*) analyzed->parser.error_list.head; error != NULL;
369
+ error = (const pm_diagnostic_t*) error->node.next) {
370
+ const char* message = error->message;
371
+
372
+ if (string_equals(message, "unexpected 'elsif', ignoring it")) {
373
+ unexpected_elsif = true;
374
+ } else if (string_equals(message, "unexpected 'else', ignoring it")) {
375
+ unexpected_else = true;
376
+ } else if (string_equals(message, "unexpected 'end', ignoring it")) {
377
+ unexpected_end = true;
378
+ } else if (string_equals(message, "unexpected '=', ignoring it")) {
379
+ unexpected_equals = true;
380
+ } else if (string_equals(message, "unexpected '}', ignoring it")) {
381
+ unexpected_closing_brace = true;
382
+ } else if (string_equals(message, "unexpected 'when', ignoring it")) {
383
+ unexpected_when = true;
384
+ } else if (string_equals(message, "unexpected 'in', ignoring it")) {
385
+ unexpected_in = true;
386
+ } else if (string_equals(message, "unexpected 'rescue', ignoring it")) {
387
+ unexpected_rescue = true;
388
+ } else if (string_equals(message, "unexpected 'ensure', ignoring it")) {
389
+ unexpected_ensure = true;
531
390
  }
532
-
533
- default: break;
534
391
  }
535
392
 
536
- pm_visit_child_nodes(node, search_then_keywords, analyzed);
537
-
538
- return false;
393
+ if (unexpected_elsif) { analyzed->elsif_node_count++; }
394
+ if (unexpected_else) { analyzed->else_node_count++; }
395
+ if (unexpected_end && !unexpected_equals) { analyzed->end_count++; }
396
+ if (unexpected_when) { analyzed->when_node_count++; }
397
+ if (unexpected_in) { analyzed->in_node_count++; }
398
+ if (unexpected_rescue) { analyzed->rescue_node_count++; }
399
+ if (unexpected_ensure) { analyzed->ensure_node_count++; }
400
+ if (unexpected_closing_brace) { analyzed->block_closing_count++; }
539
401
  }
540
402
 
541
403
  static bool is_postfix_conditional(const pm_statements_node_t* statements, pm_location_t keyword_location) {
@@ -34,6 +34,8 @@ analyzed_ruby_T* init_analyzed_ruby(hb_string_T source) {
34
34
  analyzed->yield_node_count = 0;
35
35
  analyzed->then_keyword_count = 0;
36
36
  analyzed->unclosed_control_flow_count = 0;
37
+ analyzed->control_type = CONTROL_TYPE_UNKNOWN;
38
+ analyzed->control_type_detected = false;
37
39
 
38
40
  return analyzed;
39
41
  }
@@ -367,6 +367,7 @@ static AST_NODE_T* build_yield_node(control_builder_context_T* context) {
367
367
  token_copy(context->tag_opening, context->allocator),
368
368
  token_copy(context->content, context->allocator),
369
369
  token_copy(context->tag_closing, context->allocator),
370
+ HERB_PRISM_NODE_EMPTY,
370
371
  context->start_position,
371
372
  context->end_position,
372
373
  context->errors,
@@ -194,13 +194,7 @@ static bool erb_content_is_unterminated(const AST_ERB_CONTENT_NODE_T* erb_node)
194
194
  return false;
195
195
  }
196
196
 
197
- control_type_t detect_control_type(AST_ERB_CONTENT_NODE_T* erb_node) {
198
- if (!erb_node || erb_node->base.type != AST_ERB_CONTENT_NODE) { return CONTROL_TYPE_UNKNOWN; }
199
- if (erb_content_is_unterminated(erb_node)) { return CONTROL_TYPE_UNKNOWN; }
200
-
201
- analyzed_ruby_T* ruby = erb_node->analyzed_ruby;
202
-
203
- if (!ruby) { return CONTROL_TYPE_UNKNOWN; }
197
+ static control_type_t detect_analyzed_control_type(analyzed_ruby_T* ruby) {
204
198
  if (ruby->valid) { return CONTROL_TYPE_UNKNOWN; }
205
199
 
206
200
  pm_node_t* root = ruby->root;
@@ -219,6 +213,22 @@ control_type_t detect_control_type(AST_ERB_CONTENT_NODE_T* erb_node) {
219
213
  return find_earliest_control_keyword(root, ruby->parser.start);
220
214
  }
221
215
 
216
+ control_type_t detect_control_type(AST_ERB_CONTENT_NODE_T* erb_node) {
217
+ if (!erb_node || erb_node->base.type != AST_ERB_CONTENT_NODE) { return CONTROL_TYPE_UNKNOWN; }
218
+ if (erb_content_is_unterminated(erb_node)) { return CONTROL_TYPE_UNKNOWN; }
219
+
220
+ analyzed_ruby_T* ruby = erb_node->analyzed_ruby;
221
+
222
+ if (!ruby) { return CONTROL_TYPE_UNKNOWN; }
223
+
224
+ if (!ruby->control_type_detected) {
225
+ ruby->control_type = detect_analyzed_control_type(ruby);
226
+ ruby->control_type_detected = true;
227
+ }
228
+
229
+ return ruby->control_type;
230
+ }
231
+
222
232
  bool is_subsequent_type(control_type_t parent_type, control_type_t child_type) {
223
233
  switch (parent_type) {
224
234
  case CONTROL_TYPE_IF:
@@ -170,6 +170,7 @@ static token_T* get_content_token(const AST_NODE_T* node) {
170
170
  switch (node->type) {
171
171
  case AST_ERB_CONTENT_NODE: return ((AST_ERB_CONTENT_NODE_T*) node)->content;
172
172
  case AST_ERB_RENDER_NODE: return ((AST_ERB_RENDER_NODE_T*) node)->content;
173
+ case AST_ERB_YIELD_NODE: return ((AST_ERB_YIELD_NODE_T*) node)->content;
173
174
  case AST_ERB_IF_NODE: return ((AST_ERB_IF_NODE_T*) node)->content;
174
175
  case AST_ERB_BLOCK_NODE: return ((AST_ERB_BLOCK_NODE_T*) node)->content;
175
176
  case AST_ERB_ITERATION_BLOCK_NODE: return ((AST_ERB_ITERATION_BLOCK_NODE_T*) node)->content;
@@ -188,6 +189,7 @@ static void set_prism_node(AST_NODE_T* node, herb_prism_node_T prism_ref) {
188
189
  switch (node->type) {
189
190
  case AST_ERB_CONTENT_NODE: ((AST_ERB_CONTENT_NODE_T*) node)->prism_node = prism_ref; break;
190
191
  case AST_ERB_RENDER_NODE: ((AST_ERB_RENDER_NODE_T*) node)->prism_node = prism_ref; break;
192
+ case AST_ERB_YIELD_NODE: ((AST_ERB_YIELD_NODE_T*) node)->prism_node = prism_ref; break;
191
193
  case AST_ERB_IF_NODE: ((AST_ERB_IF_NODE_T*) node)->prism_node = prism_ref; break;
192
194
  case AST_ERB_BLOCK_NODE: ((AST_ERB_BLOCK_NODE_T*) node)->prism_node = prism_ref; break;
193
195
  case AST_ERB_ITERATION_BLOCK_NODE: ((AST_ERB_ITERATION_BLOCK_NODE_T*) node)->prism_node = prism_ref; break;
@@ -202,6 +204,12 @@ static void set_prism_node(AST_NODE_T* node, herb_prism_node_T prism_ref) {
202
204
  }
203
205
  }
204
206
 
207
+ static size_t prism_node_start(pm_parser_t* parser, hb_narray_T* node_list, size_t index) {
208
+ pm_node_t* prism_node = *(pm_node_t**) hb_narray_get(node_list, index);
209
+
210
+ return (size_t) (prism_node->location.start - parser->start);
211
+ }
212
+
205
213
  static herb_prism_node_T find_prism_node_for_herb_node(
206
214
  pm_parser_t* parser,
207
215
  hb_narray_T* node_list,
@@ -215,17 +223,29 @@ static herb_prism_node_T find_prism_node_for_herb_node(
215
223
  uint32_t content_from = content->range.from;
216
224
  uint32_t content_to = content->range.to;
217
225
 
218
- for (size_t i = 0; i < hb_narray_size(node_list); i++) {
219
- pm_node_t* prism_node = *(pm_node_t**) hb_narray_get(node_list, i);
220
- size_t prism_start = (size_t) (prism_node->location.start - parser->start);
226
+ size_t low = 0;
227
+ size_t high = hb_narray_size(node_list);
221
228
 
222
- if (prism_start >= content_from && prism_start < content_to) {
223
- result.node = prism_node;
224
- result.parser = parser;
225
- break;
229
+ while (low < high) {
230
+ size_t middle = low + (high - low) / 2;
231
+
232
+ if (prism_node_start(parser, node_list, middle) < content_from) {
233
+ low = middle + 1;
234
+ } else {
235
+ high = middle;
226
236
  }
227
237
  }
228
238
 
239
+ if (low >= hb_narray_size(node_list) || prism_node_start(parser, node_list, low) >= content_to) { return result; }
240
+
241
+ pm_node_t* prism_node = *(pm_node_t**) hb_narray_get(node_list, low);
242
+ size_t prism_end = (size_t) (prism_node->location.end - parser->start);
243
+
244
+ result.node = prism_node;
245
+ result.parser = parser;
246
+ result.source_start = content_from;
247
+ result.source_end = prism_end > content_to ? (uint32_t) prism_end : content_to;
248
+
229
249
  return result;
230
250
  }
231
251
 
@@ -237,7 +257,8 @@ static bool annotate_visitor(const AST_NODE_T* node, void* data) {
237
257
  pm_parser_t* parser;
238
258
  hb_narray_T* node_list;
239
259
 
240
- if (node->type == AST_ERB_CONTENT_NODE || node->type == AST_ERB_RENDER_NODE || context->prism_nodes_deep) {
260
+ if (node->type == AST_ERB_CONTENT_NODE || node->type == AST_ERB_RENDER_NODE || node->type == AST_ERB_YIELD_NODE
261
+ || context->prism_nodes_deep) {
241
262
  parser = context->parser;
242
263
  node_list = context->node_list;
243
264
  } else {
@@ -253,7 +274,9 @@ static bool annotate_visitor(const AST_NODE_T* node, void* data) {
253
274
  }
254
275
 
255
276
  static bool collect_content_ranges_visitor(const AST_NODE_T* node, void* data) {
256
- if (node->type != AST_ERB_CONTENT_NODE && node->type != AST_ERB_RENDER_NODE) { return true; }
277
+ if (node->type != AST_ERB_CONTENT_NODE && node->type != AST_ERB_RENDER_NODE && node->type != AST_ERB_YIELD_NODE) {
278
+ return true;
279
+ }
257
280
 
258
281
  hb_narray_T* ranges = (hb_narray_T*) data;
259
282
  token_T* content = get_content_token(node);
data/src/ast/ast_nodes.c CHANGED
@@ -712,7 +712,7 @@ AST_ERB_STRICT_LOCALS_NODE_T* ast_erb_strict_locals_node_init(token_T* tag_openi
712
712
  return erb_strict_locals_node;
713
713
  }
714
714
 
715
- AST_ERB_YIELD_NODE_T* ast_erb_yield_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, position_T start_position, position_T end_position, hb_array_T* errors, hb_allocator_T* allocator) {
715
+ AST_ERB_YIELD_NODE_T* ast_erb_yield_node_init(token_T* tag_opening, token_T* content, token_T* tag_closing, herb_prism_node_T prism_node, position_T start_position, position_T end_position, hb_array_T* errors, hb_allocator_T* allocator) {
716
716
  AST_ERB_YIELD_NODE_T* erb_yield_node = hb_allocator_alloc(allocator, sizeof(AST_ERB_YIELD_NODE_T));
717
717
 
718
718
  if (!erb_yield_node) { return NULL; }
@@ -722,6 +722,7 @@ AST_ERB_YIELD_NODE_T* ast_erb_yield_node_init(token_T* tag_opening, token_T* con
722
722
  erb_yield_node->tag_opening = tag_opening;
723
723
  erb_yield_node->content = content;
724
724
  erb_yield_node->tag_closing = tag_closing;
725
+ erb_yield_node->prism_node = prism_node;
725
726
 
726
727
  return erb_yield_node;
727
728
  }
@@ -1628,6 +1629,7 @@ static void ast_free_erb_yield_node(AST_ERB_YIELD_NODE_T* erb_yield_node, hb_all
1628
1629
  if (erb_yield_node->tag_opening != NULL) { token_free(erb_yield_node->tag_opening, allocator); }
1629
1630
  if (erb_yield_node->content != NULL) { token_free(erb_yield_node->content, allocator); }
1630
1631
  if (erb_yield_node->tag_closing != NULL) { token_free(erb_yield_node->tag_closing, allocator); }
1632
+ /* prism_node is a borrowed reference into the prism context, not freed here */
1631
1633
 
1632
1634
  ast_free_base_node(&erb_yield_node->base, allocator);
1633
1635
  }
@@ -1228,7 +1228,11 @@ void ast_pretty_print_node(AST_NODE_T* node, const size_t indent, const size_t r
1228
1228
  pretty_print_errors(node, indent, relative_indent, false, buffer);
1229
1229
  pretty_print_token_property(erb_yield_node->tag_opening, hb_string("tag_opening"), indent, relative_indent, false, buffer);
1230
1230
  pretty_print_token_property(erb_yield_node->content, hb_string("content"), indent, relative_indent, false, buffer);
1231
- pretty_print_token_property(erb_yield_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, true, buffer);
1231
+ pretty_print_token_property(erb_yield_node->tag_closing, hb_string("tag_closing"), indent, relative_indent, false, buffer);
1232
+ if (erb_yield_node->prism_node.node != NULL) {
1233
+ pretty_print_label(hb_string("prism_node"), indent, relative_indent, true, buffer);
1234
+ hb_buffer_append(buffer, " (pm_node_t*)\n");
1235
+ }
1232
1236
  } break;
1233
1237
 
1234
1238
  case AST_ERB_IN_NODE: {