Module: Ibex::Codegen::RubyAST
- Included in:
- ActionSource, Ruby, RubyActions
- Defined in:
- lib/ibex/codegen/ruby_ast.rb,
sig/ibex/codegen/ruby_ast.rbs
Overview
Generates typed AST data classes and traversal bases from @node metadata.
Instance Method Summary collapse
- #append_ast(lines) ⇒ void
- #append_listener(lines, definitions) ⇒ void
- #append_listener_dispatch(lines, definitions, kind) ⇒ void
- #append_visit_children(lines) ⇒ void
- #append_visitor(lines, definitions) ⇒ void
- #ast_method_name(name) ⇒ String
- #ast_node_action_source(production) ⇒ String
- #ast_node_definitions ⇒ Hash[String, IR::node_annotation]
Instance Method Details
#append_ast(lines) ⇒ void
This method returns an undefined value.
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# File 'lib/ibex/codegen/ruby_ast.rb', line 11 def append_ast(lines) definitions = ast_node_definitions return if definitions.empty? lines << " module AST" definitions.each_value do |node| members = node.fetch(:fields).map { |field| ":#{field}" }.join(", ") lines << " #{node.fetch(:name)} = Ibex::Runtime::ASTData.define(#{members})" end lines << "" append_visitor(lines, definitions) lines << "" append_listener(lines, definitions) lines.push(" end", "") end |
#append_listener(lines, definitions) ⇒ void
This method returns an undefined value.
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/ibex/codegen/ruby_ast.rb', line 65 def append_listener(lines, definitions) lines.push(" class Listener", " def walk(node)", " enter(node)", " listener_children(node).each { |child| walk(child) }", " exit(node)", " node", " end", "") append_listener_dispatch(lines, definitions, :enter) lines << "" append_listener_dispatch(lines, definitions, :exit) lines << "" definitions.each_value do |node| method = ast_method_name(node.fetch(:name)) lines.push(" def enter_#{method}(_node); end", " def exit_#{method}(_node); end") end lines << "" lines.push( " def listener_children(node)", " return node if node.is_a?(Array)", " return node.deconstruct if node.respond_to?(:deconstruct)", "", " []", " end", " end" ) end |
#append_listener_dispatch(lines, definitions, kind) ⇒ void
This method returns an undefined value.
90 91 92 93 94 95 96 97 |
# File 'lib/ibex/codegen/ruby_ast.rb', line 90 def append_listener_dispatch(lines, definitions, kind) lines.push(" def #{kind}(node)", " case node") definitions.each_value do |node| method = ast_method_name(node.fetch(:name)) lines << " when #{node.fetch(:name)} then #{kind}_#{method}(node)" end lines.push(" end", " end") end |
#append_visit_children(lines) ⇒ void
This method returns an undefined value.
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/ibex/codegen/ruby_ast.rb', line 48 def append_visit_children(lines) lines.push( " def visit_children(node)", " children = if node.is_a?(Array)", " node", " elsif node.respond_to?(:deconstruct)", " node.deconstruct", " else", " []", " end", " children.each { |child| visit(child) }", " node", " end" ) end |
#append_visitor(lines, definitions) ⇒ void
This method returns an undefined value.
33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/ibex/codegen/ruby_ast.rb', line 33 def append_visitor(lines, definitions) lines.push(" class Visitor", " def visit(node)", " case node") definitions.each_value do |node| lines << " when #{node.fetch(:name)} then visit_#{ast_method_name(node.fetch(:name))}(node)" end lines.push(" else visit_children(node)", " end", " end", "") definitions.each_value do |node| method = ast_method_name(node.fetch(:name)) lines.push(" def visit_#{method}(node)", " visit_children(node)", " end", "") end append_visit_children(lines) lines << " end" end |
#ast_method_name(name) ⇒ String
114 115 116 117 118 |
# File 'lib/ibex/codegen/ruby_ast.rb', line 114 def ast_method_name(name) name.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') .gsub(/([a-z\d])([A-Z])/, '\1_\2') .downcase end |
#ast_node_action_source(production) ⇒ String
100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/ibex/codegen/ruby_ast.rb', line 100 def ast_node_action_source(production) node = production.node || raise(Ibex::Error, "missing AST node metadata") parameters = node.fetch(:fields).each_index.map { |index| "v#{index}" } arguments = node.fetch(:fields).each_index.map do |index| "#{node.fetch(:fields).fetch(index)}: v#{index}" end [ "private def _ibex_action_#{production.id}(#{parameters.join(', ')})", " AST::#{node.fetch(:name)}.new(#{arguments.join(', ')})", "end" ].join("\n") end |
#ast_node_definitions ⇒ Hash[String, IR::node_annotation]
28 29 30 |
# File 'lib/ibex/codegen/ruby_ast.rb', line 28 def ast_node_definitions @grammar.productions.filter_map(&:node).to_h { |node| [node.fetch(:name), node] } end |