herb 0.11.0 → 0.11.1
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 +4 -4
- data/config.yml +3 -0
- data/ext/herb/nodes.c +34 -17
- data/lib/herb/ast/nodes.rb +32 -6
- data/lib/herb/bootstrap.rb +6 -2
- data/lib/herb/dev/runner.rb +6 -1
- data/lib/herb/engine/compiler.rb +47 -11
- data/lib/herb/engine/slots/dynamics_compiler.rb +10 -0
- data/lib/herb/engine/slots/markers.rb +24 -0
- data/lib/herb/engine/slots/state_compiler.rb +1 -2
- data/lib/herb/engine/slots/visitor.rb +39 -28
- data/lib/herb/engine.rb +0 -2
- data/lib/herb/version.rb +1 -1
- data/sig/herb/ast/nodes.rbs +10 -4
- data/sig/herb/bootstrap.rbs +2 -0
- data/sig/herb/dev/runner.rbs +3 -0
- data/sig/herb/engine/compiler.rbs +14 -3
- data/sig/herb/engine/slots/dynamics_compiler.rbs +3 -0
- data/sig/herb/engine/slots/markers.rbs +14 -0
- data/sig/herb/engine/slots/visitor.rbs +17 -13
- data/sig/serialized_ast_nodes.rbs +1 -0
- data/src/analyze/action_view/tag_helper_node_builders.c +6 -3
- data/src/analyze/action_view/tag_helpers.c +76 -20
- data/src/analyze/analyze.c +5 -25
- data/src/analyze/analyze_helpers.c +94 -232
- data/src/analyze/analyzed_ruby.c +2 -0
- data/src/analyze/builders.c +1 -0
- data/src/analyze/control_type.c +17 -7
- data/src/analyze/prism_annotate.c +32 -9
- data/src/ast/ast_nodes.c +3 -1
- data/src/ast/ast_pretty_print.c +5 -1
- data/src/extract.c +204 -143
- data/src/include/analyze/analyze.h +0 -22
- data/src/include/analyze/analyzed_ruby.h +25 -0
- data/src/include/analyze/helpers.h +2 -22
- data/src/include/ast/ast_nodes.h +2 -1
- data/src/include/lexer/lexer.h +1 -0
- data/src/include/prism/herb_prism_node.h +6 -1
- data/src/include/version.h +1 -1
- data/src/lexer.c +32 -0
- data/src/prism/prism_helpers.c +91 -0
- data/templates/ext/herb/nodes.c.erb +1 -1
- data/templates/java/nodes.c.erb +1 -1
- data/templates/javascript/packages/core/src/nodes.ts.erb +21 -1
- data/templates/javascript/packages/node/extension/nodes.cpp.erb +1 -1
- data/templates/rust/src/ast/nodes.rs.erb +1 -4
- data/templates/wasm/nodes.cpp.erb +5 -15
- 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
|
|
289
|
-
|
|
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
|
|
335
|
-
|
|
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
|
|
278
|
+
return has_opening && !has_valid_block_closing(opening_loc, closing_loc);
|
|
342
279
|
}
|
|
343
280
|
|
|
344
|
-
bool
|
|
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
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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
|
-
|
|
405
|
-
|
|
406
|
-
|
|
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
|
-
|
|
409
|
-
|
|
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
|
-
|
|
416
|
-
|
|
296
|
+
case PM_UNLESS_NODE: {
|
|
297
|
+
const pm_unless_node_t* unless_node = (const pm_unless_node_t*) node;
|
|
417
298
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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
|
-
|
|
425
|
-
|
|
303
|
+
if (has_keyword_location(unless_node->then_keyword_loc)) { analyzed->then_keyword_count++; }
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
426
306
|
|
|
427
|
-
|
|
428
|
-
|
|
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
|
-
|
|
434
|
-
}
|
|
310
|
+
analyzed->when_node_count++;
|
|
435
311
|
|
|
436
|
-
|
|
437
|
-
|
|
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
|
-
|
|
444
|
-
|
|
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
|
-
|
|
457
|
-
|
|
319
|
+
if (is_unclosed_block(block_node->opening_loc, block_node->closing_loc)) { analyzed->block_node_count++; }
|
|
320
|
+
break;
|
|
321
|
+
}
|
|
458
322
|
|
|
459
|
-
|
|
460
|
-
|
|
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
|
-
|
|
466
|
-
|
|
326
|
+
if (is_unclosed_block(lambda_node->opening_loc, lambda_node->closing_loc)) { analyzed->block_node_count++; }
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
467
329
|
|
|
468
|
-
|
|
469
|
-
|
|
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
|
-
|
|
475
|
-
}
|
|
333
|
+
analyzed->case_match_node_count++;
|
|
476
334
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
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
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
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
|
-
|
|
537
|
-
|
|
538
|
-
|
|
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) {
|
data/src/analyze/analyzed_ruby.c
CHANGED
|
@@ -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
|
}
|
data/src/analyze/builders.c
CHANGED
|
@@ -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,
|
data/src/analyze/control_type.c
CHANGED
|
@@ -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
|
|
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
|
-
|
|
219
|
-
|
|
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
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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 ||
|
|
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
|
|
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
|
}
|
data/src/ast/ast_pretty_print.c
CHANGED
|
@@ -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,
|
|
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: {
|