Class: Ibex::Codegen::ActionSource

Inherits:
Object
  • Object
show all
Includes:
RubyAST
Defined in:
lib/ibex/codegen/action_source.rb,
sig/ibex/codegen/action_source.rbs

Overview

Generates static-check-only Ruby containing semantic action methods.

Instance Method Summary collapse

Methods included from RubyAST

#append_ast, #append_listener, #append_listener_dispatch, #append_visit_children, #append_visitor, #ast_method_name, #ast_node_action_source, #ast_node_definitions

Constructor Details

#initialize(automaton, omit_action_call: nil) ⇒ ActionSource

Returns a new instance of ActionSource.

RBS:

  • (IR::Automaton automaton, ?omit_action_call: bool?) -> void

Parameters:

  • automaton (IR::Automaton)
  • omit_action_call: (Boolean, nil) (defaults to: nil)


19
20
21
22
23
24
# File 'lib/ibex/codegen/action_source.rb', line 19

def initialize(automaton, omit_action_call: nil)
  @automaton = automaton
  @grammar = automaton.grammar
  @omit_action_call = omit_action_call.nil? ? @grammar.options[:omit_action_call] : omit_action_call
  @method_source = ActionMethodSource.new(@grammar)
end

Instance Method Details

#append_action(lines, production) ⇒ void

This method returns an undefined value.

RBS:

  • (Array[String] lines, IR::Production production) -> void

Parameters:



85
86
87
88
# File 'lib/ibex/codegen/action_source.rb', line 85

def append_action(lines, production)
  location = production.action&.location || production.origin.fetch(:loc)
  append_method(lines, location, @method_source.compiled_action_method_source(production))
end

#append_actions(lines) ⇒ void

This method returns an undefined value.

RBS:

  • (Array[String] lines) -> void

Parameters:

  • lines (Array[String])


57
58
59
60
61
62
63
64
65
66
67
# File 'lib/ibex/codegen/action_source.rb', line 57

def append_actions(lines)
  @grammar.productions.each do |production|
    if production.node
      append_method(lines, production.node.fetch(:loc), ast_node_action_source(production))
    elsif composed_action?(production)
      append_composed_actions(lines, production)
    elsif production.action || !@omit_action_call
      append_action(lines, production)
    end
  end
end

#append_composed_actions(lines, production) ⇒ void

This method returns an undefined value.

RBS:

  • (Array[String] lines, IR::Production production) -> void

Parameters:



70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/ibex/codegen/action_source.rb', line 70

def append_composed_actions(lines, production)
  plan = production.action&.composition&.dig(:plan)
  raise Ibex::Error, "missing action composition plan" unless plan

  plan.fetch(:steps).each_with_index do |step, index|
    next unless step[:code]

    append_method(
      lines, step.fetch(:loc),
      @method_source.composed_fragment_method_source(production, step, index)
    )
  end
end

#append_method(lines, location, source) ⇒ void

This method returns an undefined value.

RBS:

  • (Array[String] lines, IR::location location, String source) -> void

Parameters:

  • lines (Array[String])
  • location (IR::location)
  • source (String)


91
92
93
94
95
96
97
98
99
# File 'lib/ibex/codegen/action_source.rb', line 91

def append_method(lines, location, source)
  lines << ""
  lines << "  # Grammar action: #{location.fetch(:file).inspect}:" \
           "#{location.fetch(:line)}:#{location.fetch(:column)}"
  # Keep action bytes at column zero. Ruby lexical nesting does not require
  # indentation, while changing it can invalidate <<HEREDOC terminators or
  # alter <<~HEREDOC dedentation.
  lines << source
end

#append_value_printers(lines) ⇒ void

This method returns an undefined value.

RBS:

  • (Array[String] lines) -> void

Parameters:

  • lines (Array[String])


49
50
51
52
53
54
# File 'lib/ibex/codegen/action_source.rb', line 49

def append_value_printers(lines)
  @grammar.value_printers.each do |printer|
    symbol = @grammar.symbol(printer[:symbol]) || raise(Ibex::Error, "missing printer symbol #{printer[:symbol]}")
    append_method(lines, printer[:loc], @method_source.value_printer_method_source(symbol.id, printer))
  end
end

#class_parts[ Array[String], String ]

RBS:

  • () -> [Array[String], String]

Returns:

  • ([ Array[String], String ])


107
108
109
110
# File 'lib/ibex/codegen/action_source.rb', line 107

def class_parts
  parts = @grammar.class_name.split("::")
  [parts.take(parts.length - 1), parts.fetch(-1)]
end

#composed_action?(production) ⇒ Boolean

RBS:

  • (IR::Production production) -> bool

Parameters:

Returns:

  • (Boolean)


102
103
104
# File 'lib/ibex/codegen/action_source.rb', line 102

def composed_action?(production)
  production.action&.composition&.dig(:plan, :version) == 1
end

#generateString

RBS:

  • () -> String

Returns:

  • (String)


27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/ibex/codegen/action_source.rb', line 27

def generate
  lines = [
    "# frozen_string_literal: true",
    "# Generated by Ibex #{grammar_digest_comment}.",
    "# Static-check-only semantic action shadow source.",
    "# DO NOT LOAD OR EXECUTE: runtime wiring and user code are intentionally absent.",
    ""
  ]
  modules, class_name = class_parts
  modules.each { |name| lines << "module #{name}" }
  lines << "class #{class_name}"
  append_ast(lines)
  append_value_printers(lines)
  append_actions(lines)
  lines << "end"
  modules.reverse_each { lines << "end" }
  "#{lines.join("\n")}\n"
end

#grammar_digest_commentString

RBS:

  • () -> String

Returns:

  • (String)


113
114
115
# File 'lib/ibex/codegen/action_source.rb', line 113

def grammar_digest_comment
  @automaton.grammar_digest.delete_prefix("sha256:").chars.first(12).join
end