aboutsummaryrefslogtreecommitdiffstats
path: root/tool/lrama/lib/lrama/grammar/code/rule_action.rb
blob: 72afb6230353397dca9f1e4888f7e2b14c074b12 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
module Lrama
  class Grammar
    class Code
      class RuleAction < Code
        def initialize(type: nil, token_code: nil, rule: nil)
          super(type: type, token_code: token_code)
          @rule = rule
        end

        private

        # * ($$) yyval
        # * (@$) yyloc
        # * ($1) yyvsp[i]
        # * (@1) yylsp[i]
        #
        # "Rule"                class: keyword_class { $1 } tSTRING { $2 + $3 } keyword_end { $class = $1 + $keyword_end }
        # "Position in grammar"                   $1    $2      $3          $4          $5                             $6
        # "Index for yyvsp"                       -4    -3      -2          -1           0
        def reference_to_c(ref)
          case
          when ref.type == :dollar && ref.name == "$" # $$
            tag = ref.ex_tag || lhs.tag
            raise_tag_not_found_error(ref) unless tag
            "(yyval.#{tag.member})"
          when ref.type == :at && ref.name == "$" # @$
            "(yyloc)"
          when ref.type == :dollar # $n
            i = -position_in_rhs + ref.index
            tag = ref.ex_tag || rhs[ref.index - 1].tag
            raise_tag_not_found_error(ref) unless tag
            "(yyvsp[#{i}].#{tag.member})"
          when ref.type == :at # @n
            i = -position_in_rhs + ref.index
            "(yylsp[#{i}])"
          else
            raise "Unexpected. #{self}, #{ref}"
          end
        end

        def position_in_rhs
          # If rule is not derived rule, User Code is only action at
          # the end of rule RHS. In such case, the action is located on
          # `@rule.rhs.count`.
          @rule.position_in_original_rule_rhs || @rule.rhs.count
        end

        def rhs
          (@rule.original_rule || @rule).rhs
        end

        def lhs
          (@rule.original_rule || @rule).lhs
        end

        def raise_tag_not_found_error(ref)
          raise "Tag is not specified for '$#{ref.value}' in '#{@rule.to_s}'"
        end
      end
    end
  end
end