herb 0.9.0-aarch64-linux-gnu → 0.9.1-aarch64-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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/config.yml +156 -0
  3. data/ext/herb/error_helpers.c +168 -0
  4. data/ext/herb/extension.c +4 -0
  5. data/ext/herb/extension_helpers.c +1 -0
  6. data/ext/herb/nodes.c +110 -0
  7. data/lib/herb/3.0/herb.so +0 -0
  8. data/lib/herb/3.1/herb.so +0 -0
  9. data/lib/herb/3.2/herb.so +0 -0
  10. data/lib/herb/3.3/herb.so +0 -0
  11. data/lib/herb/3.4/herb.so +0 -0
  12. data/lib/herb/4.0/herb.so +0 -0
  13. data/lib/herb/ast/nodes.rb +393 -17
  14. data/lib/herb/engine.rb +55 -26
  15. data/lib/herb/errors.rb +245 -0
  16. data/lib/herb/parser_options.rb +6 -1
  17. data/lib/herb/prism_inspect.rb +5 -1
  18. data/lib/herb/version.rb +1 -1
  19. data/lib/herb/visitor.rb +10 -0
  20. data/sig/herb/ast/nodes.rbs +132 -0
  21. data/sig/herb/engine.rbs +4 -0
  22. data/sig/herb/errors.rbs +114 -0
  23. data/sig/herb/parser_options.rbs +4 -0
  24. data/sig/herb/visitor.rbs +6 -0
  25. data/sig/rubyvm.rbs +5 -0
  26. data/sig/serialized_ast_errors.rbs +28 -0
  27. data/sig/serialized_ast_nodes.rbs +31 -0
  28. data/src/analyze/action_view/attribute_extraction_helpers.c +14 -1
  29. data/src/analyze/action_view/content_tag.c +19 -11
  30. data/src/analyze/action_view/link_to.c +25 -1
  31. data/src/analyze/action_view/registry.c +23 -0
  32. data/src/analyze/action_view/tag.c +14 -8
  33. data/src/analyze/action_view/tag_helpers.c +78 -11
  34. data/src/analyze/analyze.c +3 -0
  35. data/src/analyze/prism_annotate.c +4 -2
  36. data/src/analyze/render_nodes.c +761 -0
  37. data/src/analyze/transform.c +7 -0
  38. data/src/ast_nodes.c +97 -0
  39. data/src/ast_pretty_print.c +74 -0
  40. data/src/errors.c +379 -0
  41. data/src/include/analyze/action_view/tag_helper_handler.h +2 -0
  42. data/src/include/analyze/render_nodes.h +11 -0
  43. data/src/include/ast_nodes.h +37 -0
  44. data/src/include/errors.h +58 -0
  45. data/src/include/parser.h +1 -0
  46. data/src/include/version.h +1 -1
  47. data/src/parser.c +1 -0
  48. data/src/parser_match_tags.c +20 -0
  49. data/src/visitor.c +20 -0
  50. data/templates/lib/herb/ast/nodes.rb.erb +8 -2
  51. data/templates/rust/src/ast/nodes.rs.erb +1 -1
  52. data/templates/rust/src/nodes.rs.erb +1 -1
  53. metadata +4 -1
@@ -34,7 +34,13 @@ module Herb
34
34
  return nil unless prism_node
35
35
  return nil unless source
36
36
 
37
- require "prism"
37
+ begin
38
+ require "prism"
39
+ rescue LoadError
40
+ warn "The 'prism' gem is required to deserialize Prism nodes. Add it to your Gemfile or install it with: gem install prism"
41
+ return nil
42
+ end
43
+
38
44
  Prism.load(source, prism_node).value
39
45
  end
40
46
 
@@ -108,7 +114,7 @@ module Herb
108
114
  #: (String, Location, Array[Herb::Errors::Error], String) -> void
109
115
  def initialize(type, location, errors, content)
110
116
  super(type, location, errors)
111
- @content = content.force_encoding("utf-8")
117
+ @content = content&.force_encoding("utf-8")
112
118
  end
113
119
 
114
120
  #: () -> serialized_literal_node
@@ -683,7 +689,7 @@ module Herb
683
689
  #: (String, Location, Array[Herb::Errors::Error], String, (Herb::AST::ERBIfNode | Herb::AST::ERBUnlessNode), Herb::AST::HTMLOpenTagNode, Array[Herb::AST::Node], (Herb::AST::HTMLCloseTagNode | Herb::AST::HTMLOmittedCloseTagNode), (Herb::AST::ERBIfNode | Herb::AST::ERBUnlessNode), Herb::Token, String) -> void
684
690
  def initialize(type, location, errors, condition, open_conditional, open_tag, body, close_tag, close_conditional, tag_name, element_source)
685
691
  super(type, location, errors)
686
- @condition = condition.force_encoding("utf-8")
692
+ @condition = condition&.force_encoding("utf-8")
687
693
  @open_conditional = open_conditional
688
694
  @open_tag = open_tag
689
695
  @body = body
@@ -1036,7 +1042,7 @@ module Herb
1036
1042
  #: (String, Location, Array[Herb::Errors::Error], String) -> void
1037
1043
  def initialize(type, location, errors, content)
1038
1044
  super(type, location, errors)
1039
- @content = content.force_encoding("utf-8")
1045
+ @content = content&.force_encoding("utf-8")
1040
1046
  end
1041
1047
 
1042
1048
  #: () -> serialized_ruby_literal_node
@@ -1101,8 +1107,8 @@ module Herb
1101
1107
  #: (String, Location, Array[Herb::Errors::Error], String, String) -> void
1102
1108
  def initialize(type, location, errors, content, prefix)
1103
1109
  super(type, location, errors)
1104
- @content = content.force_encoding("utf-8")
1105
- @prefix = prefix.force_encoding("utf-8")
1110
+ @content = content&.force_encoding("utf-8")
1111
+ @prefix = prefix&.force_encoding("utf-8")
1106
1112
  end
1107
1113
 
1108
1114
  #: () -> serialized_ruby_html_attributes_splat_node
@@ -1259,7 +1265,7 @@ module Herb
1259
1265
  #: (String, Location, Array[Herb::Errors::Error], String) -> void
1260
1266
  def initialize(type, location, errors, content)
1261
1267
  super(type, location, errors)
1262
- @content = content.force_encoding("utf-8")
1268
+ @content = content&.force_encoding("utf-8")
1263
1269
  end
1264
1270
 
1265
1271
  #: () -> serialized_html_text_node
@@ -1726,7 +1732,13 @@ module Herb
1726
1732
  return nil unless prism_node
1727
1733
  return nil unless source
1728
1734
 
1729
- require "prism"
1735
+ begin
1736
+ require "prism"
1737
+ rescue LoadError
1738
+ warn "The 'prism' gem is required to deserialize Prism nodes. Add it to your Gemfile or install it with: gem install prism"
1739
+ return nil
1740
+ end
1741
+
1730
1742
  Prism.load(source, prism_node).value
1731
1743
  end
1732
1744
 
@@ -2010,7 +2022,13 @@ module Herb
2010
2022
  return nil unless prism_node
2011
2023
  return nil unless source
2012
2024
 
2013
- require "prism"
2025
+ begin
2026
+ require "prism"
2027
+ rescue LoadError
2028
+ warn "The 'prism' gem is required to deserialize Prism nodes. Add it to your Gemfile or install it with: gem install prism"
2029
+ return nil
2030
+ end
2031
+
2014
2032
  Prism.load(source, prism_node).value
2015
2033
  end
2016
2034
 
@@ -2139,7 +2157,13 @@ module Herb
2139
2157
  return nil unless prism_node
2140
2158
  return nil unless source
2141
2159
 
2142
- require "prism"
2160
+ begin
2161
+ require "prism"
2162
+ rescue LoadError
2163
+ warn "The 'prism' gem is required to deserialize Prism nodes. Add it to your Gemfile or install it with: gem install prism"
2164
+ return nil
2165
+ end
2166
+
2143
2167
  Prism.load(source, prism_node).value
2144
2168
  end
2145
2169
 
@@ -2353,7 +2377,13 @@ module Herb
2353
2377
  return nil unless prism_node
2354
2378
  return nil unless source
2355
2379
 
2356
- require "prism"
2380
+ begin
2381
+ require "prism"
2382
+ rescue LoadError
2383
+ warn "The 'prism' gem is required to deserialize Prism nodes. Add it to your Gemfile or install it with: gem install prism"
2384
+ return nil
2385
+ end
2386
+
2357
2387
  Prism.load(source, prism_node).value
2358
2388
  end
2359
2389
 
@@ -2487,7 +2517,13 @@ module Herb
2487
2517
  return nil unless prism_node
2488
2518
  return nil unless source
2489
2519
 
2490
- require "prism"
2520
+ begin
2521
+ require "prism"
2522
+ rescue LoadError
2523
+ warn "The 'prism' gem is required to deserialize Prism nodes. Add it to your Gemfile or install it with: gem install prism"
2524
+ return nil
2525
+ end
2526
+
2491
2527
  Prism.load(source, prism_node).value
2492
2528
  end
2493
2529
 
@@ -2615,7 +2651,13 @@ module Herb
2615
2651
  return nil unless prism_node
2616
2652
  return nil unless source
2617
2653
 
2618
- require "prism"
2654
+ begin
2655
+ require "prism"
2656
+ rescue LoadError
2657
+ warn "The 'prism' gem is required to deserialize Prism nodes. Add it to your Gemfile or install it with: gem install prism"
2658
+ return nil
2659
+ end
2660
+
2619
2661
  Prism.load(source, prism_node).value
2620
2662
  end
2621
2663
 
@@ -2731,7 +2773,13 @@ module Herb
2731
2773
  return nil unless prism_node
2732
2774
  return nil unless source
2733
2775
 
2734
- require "prism"
2776
+ begin
2777
+ require "prism"
2778
+ rescue LoadError
2779
+ warn "The 'prism' gem is required to deserialize Prism nodes. Add it to your Gemfile or install it with: gem install prism"
2780
+ return nil
2781
+ end
2782
+
2735
2783
  Prism.load(source, prism_node).value
2736
2784
  end
2737
2785
 
@@ -2847,7 +2895,13 @@ module Herb
2847
2895
  return nil unless prism_node
2848
2896
  return nil unless source
2849
2897
 
2850
- require "prism"
2898
+ begin
2899
+ require "prism"
2900
+ rescue LoadError
2901
+ warn "The 'prism' gem is required to deserialize Prism nodes. Add it to your Gemfile or install it with: gem install prism"
2902
+ return nil
2903
+ end
2904
+
2851
2905
  Prism.load(source, prism_node).value
2852
2906
  end
2853
2907
 
@@ -3154,7 +3208,13 @@ module Herb
3154
3208
  return nil unless prism_node
3155
3209
  return nil unless source
3156
3210
 
3157
- require "prism"
3211
+ begin
3212
+ require "prism"
3213
+ rescue LoadError
3214
+ warn "The 'prism' gem is required to deserialize Prism nodes. Add it to your Gemfile or install it with: gem install prism"
3215
+ return nil
3216
+ end
3217
+
3158
3218
  Prism.load(source, prism_node).value
3159
3219
  end
3160
3220
 
@@ -3303,7 +3363,13 @@ module Herb
3303
3363
  return nil unless prism_node
3304
3364
  return nil unless source
3305
3365
 
3306
- require "prism"
3366
+ begin
3367
+ require "prism"
3368
+ rescue LoadError
3369
+ warn "The 'prism' gem is required to deserialize Prism nodes. Add it to your Gemfile or install it with: gem install prism"
3370
+ return nil
3371
+ end
3372
+
3307
3373
  Prism.load(source, prism_node).value
3308
3374
  end
3309
3375
 
@@ -3397,6 +3463,316 @@ module Herb
3397
3463
  end
3398
3464
  end
3399
3465
 
3466
+ #: type serialized_ruby_render_local_node = {
3467
+ #| name: Herb::Token?,
3468
+ #| value: Herb::AST::RubyLiteralNode?,
3469
+ #| }
3470
+ class RubyRenderLocalNode < Node
3471
+ include Colors
3472
+
3473
+ attr_reader :name #: Herb::Token?
3474
+ attr_reader :value #: Herb::AST::RubyLiteralNode?
3475
+
3476
+ #: (String, Location, Array[Herb::Errors::Error], Herb::Token, Herb::AST::RubyLiteralNode) -> void
3477
+ def initialize(type, location, errors, name, value)
3478
+ super(type, location, errors)
3479
+ @name = name
3480
+ @value = value
3481
+ end
3482
+
3483
+ #: () -> serialized_ruby_render_local_node
3484
+ def to_hash
3485
+ super.merge({
3486
+ name: name,
3487
+ value: value,
3488
+ }) #: Herb::serialized_ruby_render_local_node
3489
+ end
3490
+
3491
+ #: (Visitor) -> void
3492
+ def accept(visitor)
3493
+ visitor.visit_ruby_render_local_node(self)
3494
+ end
3495
+
3496
+ #: () -> Array[Herb::AST::Node?]
3497
+ def child_nodes
3498
+ [value]
3499
+ end
3500
+
3501
+ #: () -> Array[Herb::AST::Node]
3502
+ def compact_child_nodes
3503
+ child_nodes.compact
3504
+ end
3505
+
3506
+ #: () -> String
3507
+ def inspect
3508
+ tree_inspect.rstrip.gsub(/\s+$/, "")
3509
+ end
3510
+
3511
+ #: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
3512
+ def tree_inspect(indent: 0, depth: 0, depth_limit: 10)
3513
+ output = +""
3514
+
3515
+ output += white("@ #{bold(yellow(node_name.to_s))} #{dimmed("(location: #{location.tree_inspect})")}")
3516
+ output += "\n"
3517
+
3518
+ if depth >= depth_limit
3519
+ output += dimmed("└── [depth limit reached ...]\n\n")
3520
+
3521
+ return output.gsub(/^/, " " * indent)
3522
+ end
3523
+
3524
+ output += inspect_errors(prefix: "│ ")
3525
+
3526
+ output += white("├── name: ")
3527
+ output += name ? name.tree_inspect : magenta("∅")
3528
+ output += "\n"
3529
+ output += white("└── value: ")
3530
+ if value
3531
+ output += "\n"
3532
+ output += " └── "
3533
+ output += value.tree_inspect(indent: indent, depth: depth + 1, depth_limit: depth_limit).gsub(/^/, " " * (indent + 1)).lstrip.gsub(/^/, " ").delete_prefix(" ")
3534
+ else
3535
+ output += magenta("∅\n")
3536
+ end
3537
+ output += "\n"
3538
+
3539
+ output.gsub(/^/, " " * indent)
3540
+ end
3541
+ end
3542
+
3543
+ #: type serialized_erb_render_node = {
3544
+ #| tag_opening: Herb::Token?,
3545
+ #| content: Herb::Token?,
3546
+ #| tag_closing: Herb::Token?,
3547
+ #| analyzed_ruby: nil,
3548
+ #| prism_node: String?,
3549
+ #| partial: Herb::Token?,
3550
+ #| template_path: Herb::Token?,
3551
+ #| layout: Herb::Token?,
3552
+ #| file: Herb::Token?,
3553
+ #| inline_template: Herb::Token?,
3554
+ #| body: Herb::Token?,
3555
+ #| plain: Herb::Token?,
3556
+ #| html: Herb::Token?,
3557
+ #| renderable: Herb::Token?,
3558
+ #| collection: Herb::Token?,
3559
+ #| object: Herb::Token?,
3560
+ #| as_name: Herb::Token?,
3561
+ #| spacer_template: Herb::Token?,
3562
+ #| formats: Herb::Token?,
3563
+ #| variants: Herb::Token?,
3564
+ #| handlers: Herb::Token?,
3565
+ #| content_type: Herb::Token?,
3566
+ #| locals: Array[Herb::AST::RubyRenderLocalNode]?,
3567
+ #| }
3568
+ class ERBRenderNode < Node
3569
+ include Colors
3570
+
3571
+ attr_reader :tag_opening #: Herb::Token?
3572
+ attr_reader :content #: Herb::Token?
3573
+ attr_reader :tag_closing #: Herb::Token?
3574
+ attr_reader :analyzed_ruby #: nil
3575
+ attr_reader :prism_node #: String?
3576
+ attr_reader :partial #: Herb::Token?
3577
+ attr_reader :template_path #: Herb::Token?
3578
+ attr_reader :layout #: Herb::Token?
3579
+ attr_reader :file #: Herb::Token?
3580
+ attr_reader :inline_template #: Herb::Token?
3581
+ attr_reader :body #: Herb::Token?
3582
+ attr_reader :plain #: Herb::Token?
3583
+ attr_reader :html #: Herb::Token?
3584
+ attr_reader :renderable #: Herb::Token?
3585
+ attr_reader :collection #: Herb::Token?
3586
+ attr_reader :object #: Herb::Token?
3587
+ attr_reader :as_name #: Herb::Token?
3588
+ attr_reader :spacer_template #: Herb::Token?
3589
+ attr_reader :formats #: Herb::Token?
3590
+ attr_reader :variants #: Herb::Token?
3591
+ attr_reader :handlers #: Herb::Token?
3592
+ attr_reader :content_type #: Herb::Token?
3593
+ attr_reader :locals #: Array[Herb::AST::RubyRenderLocalNode]?
3594
+
3595
+ #: (String, Location, Array[Herb::Errors::Error], Herb::Token, Herb::Token, Herb::Token, nil, String, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Herb::Token, Array[Herb::AST::RubyRenderLocalNode]) -> void
3596
+ def initialize(type, location, errors, tag_opening, content, tag_closing, analyzed_ruby, prism_node, partial, template_path, layout, file, inline_template, body, plain, html, renderable, collection, object, as_name, spacer_template, formats, variants, handlers, content_type, locals)
3597
+ super(type, location, errors)
3598
+ @tag_opening = tag_opening
3599
+ @content = content
3600
+ @tag_closing = tag_closing
3601
+ @analyzed_ruby = analyzed_ruby
3602
+ @prism_node = prism_node
3603
+ @partial = partial
3604
+ @template_path = template_path
3605
+ @layout = layout
3606
+ @file = file
3607
+ @inline_template = inline_template
3608
+ @body = body
3609
+ @plain = plain
3610
+ @html = html
3611
+ @renderable = renderable
3612
+ @collection = collection
3613
+ @object = object
3614
+ @as_name = as_name
3615
+ @spacer_template = spacer_template
3616
+ @formats = formats
3617
+ @variants = variants
3618
+ @handlers = handlers
3619
+ @content_type = content_type
3620
+ @locals = locals
3621
+ end
3622
+
3623
+ #: () -> Prism::node?
3624
+ def deserialized_prism_node
3625
+ prism_node = @prism_node
3626
+ return nil unless prism_node
3627
+ return nil unless source
3628
+
3629
+ begin
3630
+ require "prism"
3631
+ rescue LoadError
3632
+ warn "The 'prism' gem is required to deserialize Prism nodes. Add it to your Gemfile or install it with: gem install prism"
3633
+ return nil
3634
+ end
3635
+
3636
+ Prism.load(source, prism_node).value
3637
+ end
3638
+
3639
+ #: () -> serialized_erb_render_node
3640
+ def to_hash
3641
+ super.merge({
3642
+ tag_opening: tag_opening,
3643
+ content: content,
3644
+ tag_closing: tag_closing,
3645
+ analyzed_ruby: analyzed_ruby,
3646
+ prism_node: prism_node,
3647
+ partial: partial,
3648
+ template_path: template_path,
3649
+ layout: layout,
3650
+ file: file,
3651
+ inline_template: inline_template,
3652
+ body: body,
3653
+ plain: plain,
3654
+ html: html,
3655
+ renderable: renderable,
3656
+ collection: collection,
3657
+ object: object,
3658
+ as_name: as_name,
3659
+ spacer_template: spacer_template,
3660
+ formats: formats,
3661
+ variants: variants,
3662
+ handlers: handlers,
3663
+ content_type: content_type,
3664
+ locals: locals,
3665
+ }) #: Herb::serialized_erb_render_node
3666
+ end
3667
+
3668
+ #: (Visitor) -> void
3669
+ def accept(visitor)
3670
+ visitor.visit_erb_render_node(self)
3671
+ end
3672
+
3673
+ #: () -> Array[Herb::AST::Node?]
3674
+ def child_nodes
3675
+ [*(locals || [])]
3676
+ end
3677
+
3678
+ #: () -> Array[Herb::AST::Node]
3679
+ def compact_child_nodes
3680
+ child_nodes.compact
3681
+ end
3682
+
3683
+ #: () -> String
3684
+ def inspect
3685
+ tree_inspect.rstrip.gsub(/\s+$/, "")
3686
+ end
3687
+
3688
+ #: (?indent: Integer, ?depth: Integer, ?depth_limit: Integer) -> String
3689
+ def tree_inspect(indent: 0, depth: 0, depth_limit: 10)
3690
+ output = +""
3691
+
3692
+ output += white("@ #{bold(yellow(node_name.to_s))} #{dimmed("(location: #{location.tree_inspect})")}")
3693
+ output += "\n"
3694
+
3695
+ if depth >= depth_limit
3696
+ output += dimmed("└── [depth limit reached ...]\n\n")
3697
+
3698
+ return output.gsub(/^/, " " * indent)
3699
+ end
3700
+
3701
+ output += inspect_errors(prefix: "│ ")
3702
+
3703
+ output += white("├── tag_opening: ")
3704
+ output += tag_opening ? tag_opening.tree_inspect : magenta("∅")
3705
+ output += "\n"
3706
+ output += white("├── content: ")
3707
+ output += content ? content.tree_inspect : magenta("∅")
3708
+ output += "\n"
3709
+ output += white("├── tag_closing: ")
3710
+ output += tag_closing ? tag_closing.tree_inspect : magenta("∅")
3711
+ output += "\n"
3712
+ if prism_node && source
3713
+ output += white("├── prism_node: ")
3714
+ output += Herb::PrismInspect.inspect_prism_serialized(prism_node, source, "│ ")
3715
+ output += "\n"
3716
+ end
3717
+ output += white("├── partial: ")
3718
+ output += partial ? partial.tree_inspect : magenta("∅")
3719
+ output += "\n"
3720
+ output += white("├── template_path: ")
3721
+ output += template_path ? template_path.tree_inspect : magenta("∅")
3722
+ output += "\n"
3723
+ output += white("├── layout: ")
3724
+ output += layout ? layout.tree_inspect : magenta("∅")
3725
+ output += "\n"
3726
+ output += white("├── file: ")
3727
+ output += file ? file.tree_inspect : magenta("∅")
3728
+ output += "\n"
3729
+ output += white("├── inline_template: ")
3730
+ output += inline_template ? inline_template.tree_inspect : magenta("∅")
3731
+ output += "\n"
3732
+ output += white("├── body: ")
3733
+ output += body ? body.tree_inspect : magenta("∅")
3734
+ output += "\n"
3735
+ output += white("├── plain: ")
3736
+ output += plain ? plain.tree_inspect : magenta("∅")
3737
+ output += "\n"
3738
+ output += white("├── html: ")
3739
+ output += html ? html.tree_inspect : magenta("∅")
3740
+ output += "\n"
3741
+ output += white("├── renderable: ")
3742
+ output += renderable ? renderable.tree_inspect : magenta("∅")
3743
+ output += "\n"
3744
+ output += white("├── collection: ")
3745
+ output += collection ? collection.tree_inspect : magenta("∅")
3746
+ output += "\n"
3747
+ output += white("├── object: ")
3748
+ output += object ? object.tree_inspect : magenta("∅")
3749
+ output += "\n"
3750
+ output += white("├── as_name: ")
3751
+ output += as_name ? as_name.tree_inspect : magenta("∅")
3752
+ output += "\n"
3753
+ output += white("├── spacer_template: ")
3754
+ output += spacer_template ? spacer_template.tree_inspect : magenta("∅")
3755
+ output += "\n"
3756
+ output += white("├── formats: ")
3757
+ output += formats ? formats.tree_inspect : magenta("∅")
3758
+ output += "\n"
3759
+ output += white("├── variants: ")
3760
+ output += variants ? variants.tree_inspect : magenta("∅")
3761
+ output += "\n"
3762
+ output += white("├── handlers: ")
3763
+ output += handlers ? handlers.tree_inspect : magenta("∅")
3764
+ output += "\n"
3765
+ output += white("├── content_type: ")
3766
+ output += content_type ? content_type.tree_inspect : magenta("∅")
3767
+ output += "\n"
3768
+ output += white("└── locals: ")
3769
+ output += inspect_array(locals, prefix: " ", indent: indent, depth: depth + 1, depth_limit: depth_limit)
3770
+ output += "\n"
3771
+
3772
+ output.gsub(/^/, " " * indent)
3773
+ end
3774
+ end
3775
+
3400
3776
  #: type serialized_erb_yield_node = {
3401
3777
  #| tag_opening: Herb::Token?,
3402
3778
  #| content: Herb::Token?,
data/lib/herb/engine.rb CHANGED
@@ -148,15 +148,7 @@ module Herb
148
148
  @src << "; ensure\n #{@bufvar} = __original_outvar\nend\n" if properties[:ensure]
149
149
 
150
150
  if properties.fetch(:validate_ruby, false)
151
- require "prism"
152
-
153
- prism_result = Prism.parse(@src)
154
- syntax_errors = prism_result.errors.reject { |e| e.type == :invalid_yield }
155
-
156
- if syntax_errors.any?
157
- details = syntax_errors.map { |e| " - #{e.message} (line #{e.location.start_line})" }.join("\n")
158
- raise InvalidRubyError.new("Compiled template produced invalid Ruby:\n#{details}", compiled_source: @src)
159
- end
151
+ ensure_valid_ruby!(@src)
160
152
  end
161
153
 
162
154
  @src.freeze
@@ -238,11 +230,17 @@ module Herb
238
230
  @buffer_on_stack = false
239
231
  end
240
232
 
233
+ def expression_block?
234
+ @_in_expression_block || false
235
+ end
236
+
241
237
  def add_expression(indicator, code)
242
- if (indicator == "=") ^ @escape
243
- add_expression_result(code)
238
+ unescaped = (indicator == "=") ^ @escape
239
+
240
+ if expression_block?
241
+ unescaped ? add_expression_block_result(code) : add_expression_block_result_escaped(code)
244
242
  else
245
- add_expression_result_escaped(code)
243
+ unescaped ? add_expression_result(code) : add_expression_result_escaped(code)
246
244
  end
247
245
  end
248
246
 
@@ -259,39 +257,48 @@ module Herb
259
257
  end
260
258
 
261
259
  def add_expression_block(indicator, code)
262
- if (indicator == "=") ^ @escape
263
- add_expression_block_result(code)
264
- else
265
- add_expression_block_result_escaped(code)
266
- end
260
+ @_in_expression_block = true
261
+ @_expression_block_open_paren = false
262
+
263
+ add_expression(indicator, code)
264
+ ensure
265
+ @_in_expression_block = false
267
266
  end
268
267
 
269
268
  def add_expression_block_result(code)
269
+ @_expression_block_open_paren = true
270
+
270
271
  with_buffer {
271
272
  @src << " << (" << code << trailing_newline(code)
272
273
  }
273
274
  end
274
275
 
275
276
  def add_expression_block_result_escaped(code)
277
+ @_expression_block_open_paren = true
278
+
276
279
  with_buffer {
277
280
  @src << " << " << @escapefunc << "((" << code << trailing_newline(code)
278
281
  }
279
282
  end
280
283
 
281
284
  def add_expression_block_end(code, escaped: false)
282
- terminate_expression
285
+ if @_expression_block_open_paren
286
+ terminate_expression
283
287
 
284
- trailing_newline = code.end_with?("\n")
285
- code_stripped = code.chomp
288
+ trailing_newline = code.end_with?("\n")
289
+ code_stripped = code.chomp
286
290
 
287
- @src.chomp! if @src.end_with?("\n") && code_stripped.start_with?(" ")
291
+ @src.chomp! if @src.end_with?("\n") && code_stripped.start_with?(" ")
288
292
 
289
- @src << " " << code_stripped
290
- @src << "\n" if self.class.comment?(code_stripped)
291
- @src << (escaped ? "))" : ")")
292
- @src << (trailing_newline ? "\n" : ";")
293
+ @src << " " << code_stripped
294
+ @src << "\n" if self.class.comment?(code_stripped)
295
+ @src << (escaped ? "))" : ")")
296
+ @src << (trailing_newline ? "\n" : ";")
293
297
 
294
- @buffer_on_stack = false
298
+ @buffer_on_stack = false
299
+ else
300
+ add_code(code)
301
+ end
295
302
  end
296
303
 
297
304
  def trailing_newline(code)
@@ -440,5 +447,27 @@ module Herb
440
447
  def default_visitors
441
448
  []
442
449
  end
450
+
451
+ def ensure_valid_ruby!(source)
452
+ RubyVM::InstructionSequence.compile(source)
453
+ rescue SyntaxError => e
454
+ return if e.message.include?("Invalid yield")
455
+
456
+ begin
457
+ require "prism"
458
+ rescue LoadError
459
+ # Prism not available, fall through
460
+ end
461
+
462
+ raise InvalidRubyError.new("Compiled template produced invalid Ruby:\n - #{e.message}", compiled_source: @src) unless defined?(Prism)
463
+
464
+ prism_result = Prism.parse(@src)
465
+ syntax_errors = prism_result.errors.reject { |error| error.type == :invalid_yield }
466
+
467
+ if syntax_errors.any?
468
+ details = syntax_errors.map { |err| " - #{err.message} (line #{err.location.start_line})" }.join("\n")
469
+ raise InvalidRubyError.new("Compiled template produced invalid Ruby:\n#{details}", compiled_source: @src)
470
+ end
471
+ end
443
472
  end
444
473
  end