# frozen_string_literal: true
module Mutant
class AST
# AST Structure metadata
# rubocop:disable Metrics/ModuleLength
module Structure
class Node
include Adamantium, Anima.new(:type, :fixed, :variable)
class Fixed
include Adamantium, Anima.new(:index, :name)
class Descendant < self
end
class Attribute < self
end
def attribute?
instance_of?(Attribute)
end
def descendant?
instance_of?(Descendant)
end
def value(node)
node.children.at(index)
end
end
class Variable
include Adamantium, Anima.new(:name, :range)
class Descendants < self
def nodes(node)
node.children[range]
end
end
class Attributes < self
def nodes(_node)
EMPTY_ARRAY
end
end
end
def each_descendant_deep(node, &block)
each_descendant(node) do |descendant|
block.call(descendant)
Structure.for(descendant.type).each_descendant_deep(descendant, &block)
end
end
def each_node(node, &block)
block.call(node)
each_descendant_deep(node, &block)
end
def each_descendant(node, &block)
descendants.each_value do |descendant|
value = descendant.value(node)
block.call(value) if value
end
variable_descendants(node).each do |value|
block.call(value) if value
end
self
end
def descendants
fixed.select(&:descendant?).to_h { |child| [child.name, child] }
end
def attributes
fixed.select(&:attribute?).to_h { |child| [child.name, child] }
end
def descendant(name)
maybe_descendant(name) or fail "Node #{type} does not have fixed descendant #{name}"
end
def attribute(name)
maybe_attribute(name) or fail "Node #{type} does not have fixed attribute #{name}"
end
def maybe_attribute(name)
attributes[name]
end
def maybe_descendant(name)
descendants[name]
end
def self.fixed(values)
values.each_with_index.map do |(klass, name), index|
klass.new(index:, name:)
end
end
private
def variable_descendants(node)
return EMPTY_ARRAY unless variable
variable.nodes(node)
end
end
ALL = [
Node.new(
type: :__ENCODING__,
fixed: EMPTY_ARRAY,
variable: nil
),
Node.new(
type: :__FILE__,
fixed: EMPTY_ARRAY,
variable: nil
),
Node.new(
type: :__LINE__,
fixed: EMPTY_ARRAY,
variable: nil
),
Node.new(
type: :alias,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :source],
[Node::Fixed::Descendant, :target]
]
),
variable: nil
),
Node.new(
type: :and,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :left],
[Node::Fixed::Descendant, :right]
]
),
variable: nil
),
Node.new(
type: :and_asgn,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :target],
[Node::Fixed::Descendant, :value]
]
),
variable: nil
),
Node.new(
type: :arg,
fixed: Node.fixed([[Node::Fixed::Attribute, :name]]),
variable: nil
),
Node.new(
type: :args,
fixed: EMPTY_ARRAY,
variable: Node::Variable::Descendants.new(name: :arguments, range: 0..)
),
Node.new(
type: :array,
fixed: EMPTY_ARRAY,
variable: Node::Variable::Descendants.new(name: :members, range: 0..)
),
Node.new(
type: :array_pattern,
fixed: EMPTY_ARRAY,
variable: Node::Variable::Descendants.new(name: :members, range: 0..)
),
Node.new(
type: :back_ref,
fixed: Node.fixed([[Node::Fixed::Attribute, :name]]),
variable: nil
),
Node.new(
type: :begin,
fixed: EMPTY_ARRAY,
variable: Node::Variable::Descendants.new(name: :members, range: 0..)
),
Node.new(
type: :block,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :receiver],
[Node::Fixed::Descendant, :arguments],
[Node::Fixed::Descendant, :body]
]
),
variable: nil
),
Node.new(
type: :blockarg,
fixed: Node.fixed([[Node::Fixed::Attribute, :name]]),
variable: nil
),
Node.new(
type: :block_pass,
fixed: Node.fixed([[Node::Fixed::Descendant, :value]]),
variable: nil
),
Node.new(
type: :break,
fixed: EMPTY_ARRAY,
variable: Node::Variable::Descendants.new(name: :values, range: 0..)
),
Node.new(
type: :case,
fixed: Node.fixed([[Node::Fixed::Descendant, :value]]),
variable: Node::Variable::Descendants.new(name: :members, range: 1..)
),
Node.new(
type: :case_match,
fixed: [
Node::Fixed::Descendant.new(index: 0, name: :target),
Node::Fixed::Descendant.new(index: -1, name: :else_branch)
],
variable: Node::Variable::Descendants.new(name: :patterns, range: 1..-2)
),
Node.new(
type: :casgn,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :base],
[Node::Fixed::Attribute, :name],
[Node::Fixed::Descendant, :value]
]
),
variable: nil
),
Node.new(
type: :cbase,
fixed: EMPTY_ARRAY,
variable: nil
),
Node.new(
type: :class,
fixed: Node.fixed(
[
[Node::Fixed::Attribute, :name],
[Node::Fixed::Descendant, :superclass],
[Node::Fixed::Descendant, :body]
]
),
variable: nil
),
Node.new(
type: :complex,
fixed: Node.fixed([[Node::Fixed::Attribute, :name]]),
variable: nil
),
Node.new(
type: :const,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :base],
[Node::Fixed::Attribute, :name]
]
),
variable: nil
),
Node.new(
type: :const_pattern,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :target],
[Node::Fixed::Descendant, :pattern]
]
),
variable: nil
),
Node.new(
type: :csend,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :receiver],
[Node::Fixed::Attribute, :selector]
]
),
variable: Node::Variable::Descendants.new(name: :arguments, range: 2..)
),
Node.new(
type: :cvar,
fixed: Node.fixed([[Node::Fixed::Attribute, :name]]),
variable: nil
),
Node.new(
type: :cvasgn,
fixed: Node.fixed(
[
[Node::Fixed::Attribute, :name],
[Node::Fixed::Descendant, :value]
]
),
variable: nil
),
Node.new(
type: :defined?,
fixed: Node.fixed([[Node::Fixed::Descendant, :value]]),
variable: nil
),
Node.new(
type: :dstr,
fixed: EMPTY_ARRAY,
variable: Node::Variable::Descendants.new(name: :members, range: 0..)
),
Node.new(
type: :dsym,
fixed: EMPTY_ARRAY,
variable: Node::Variable::Descendants.new(name: :members, range: 0..)
),
Node.new(
type: :def,
fixed: Node.fixed(
[
[Node::Fixed::Attribute, :name],
[Node::Fixed::Descendant, :arguments],
[Node::Fixed::Descendant, :body]
]
),
variable: nil
),
Node.new(
type: :defs,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :singleton],
[Node::Fixed::Attribute, :name],
[Node::Fixed::Descendant, :arguments],
[Node::Fixed::Descendant, :body]
]
),
variable: nil
),
Node.new(
type: :empty_else,
fixed: EMPTY_ARRAY,
variable: nil
),
Node.new(
type: :ensure,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :target],
[Node::Fixed::Descendant, :ensure_body]
]
),
variable: nil
),
Node.new(
type: :eflipflop,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :start],
[Node::Fixed::Descendant, :end]
]
),
variable: nil
),
Node.new(
type: :erange,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :begin],
[Node::Fixed::Descendant, :end]
]
),
variable: nil
),
Node.new(
type: :false,
fixed: EMPTY_ARRAY,
variable: nil
),
Node.new(
type: :float,
fixed: Node.fixed([[Node::Fixed::Attribute, :value]]),
variable: nil
),
Node.new(
type: :forwarded_args,
fixed: EMPTY_ARRAY,
variable: nil
),
Node.new(
type: :forwarded_kwrestarg,
fixed: EMPTY_ARRAY,
variable: nil
),
Node.new(
type: :forwarded_restarg,
fixed: EMPTY_ARRAY,
variable: nil
),
Node.new(
type: :for,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :target],
[Node::Fixed::Descendant, :source],
[Node::Fixed::Descendant, :body]
]
),
variable: nil
),
Node.new(
type: :gvar,
fixed: Node.fixed([[Node::Fixed::Attribute, :name]]),
variable: nil
),
Node.new(
type: :gvasgn,
fixed: Node.fixed(
[
[Node::Fixed::Attribute, :name],
[Node::Fixed::Descendant, :value]
]
),
variable: nil
),
Node.new(
type: :hash,
fixed: EMPTY_ARRAY,
variable: Node::Variable::Descendants.new(name: :members, range: 0..)
),
Node.new(
type: :index,
fixed: Node.fixed([[Node::Fixed::Descendant, :receiver]]),
variable: Node::Variable::Descendants.new(name: :members, range: 1..)
),
Node.new(
type: :indexasgn,
fixed: Node.fixed([[Node::Fixed::Descendant, :receiver]]),
variable: Node::Variable::Descendants.new(name: :members, range: 1..)
),
Node.new(
type: :if,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :condition],
[Node::Fixed::Descendant, :true_branch],
[Node::Fixed::Descendant, :false_branch]
]
),
variable: nil
),
Node.new(
type: :iflipflop,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :start],
[Node::Fixed::Descendant, :end]
]
),
variable: nil
),
Node.new(
type: :in_pattern,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :target],
[Node::Fixed::Descendant, :unless_guard],
[Node::Fixed::Descendant, :branch],
[Node::Fixed::Descendant, :else_branch]
]
),
variable: nil
),
Node.new(
type: :int,
fixed: Node.fixed([[Node::Fixed::Attribute, :value]]),
variable: nil
),
Node.new(
type: :irange,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :begin],
[Node::Fixed::Descendant, :end]
]
),
variable: nil
),
Node.new(
type: :ivar,
fixed: Node.fixed([[Node::Fixed::Attribute, :name]]),
variable: nil
),
Node.new(
type: :ivasgn,
fixed: Node.fixed(
[
[Node::Fixed::Attribute, :name],
[Node::Fixed::Descendant, :value]
]
),
variable: nil
),
Node.new(
type: :kwarg,
fixed: Node.fixed([[Node::Fixed::Attribute, :name]]),
variable: nil
),
Node.new(
type: :kwargs,
fixed: EMPTY_ARRAY,
variable: Node::Variable::Descendants.new(name: :members, range: 0..)
),
Node.new(
type: :kwbegin,
fixed: EMPTY_ARRAY,
variable: Node::Variable::Descendants.new(name: :members, range: 0..)
),
Node.new(
type: :kwoptarg,
fixed: Node.fixed(
[
[Node::Fixed::Attribute, :name],
[Node::Fixed::Descendant, :value]
]
),
variable: nil
),
Node.new(
type: :kwrestarg,
fixed: Node.fixed([[Node::Fixed::Attribute, :name]]),
variable: nil
),
Node.new(
type: :kwsplat,
fixed: Node.fixed([[Node::Fixed::Descendant, :value]]),
variable: nil
),
Node.new(
type: :lambda,
fixed: EMPTY_ARRAY,
variable: nil
),
Node.new(
type: :lvar,
fixed: Node.fixed([[Node::Fixed::Attribute, :value]]),
variable: nil
),
Node.new(
type: :lvasgn,
fixed: Node.fixed(
[
[Node::Fixed::Attribute, :name],
[Node::Fixed::Descendant, :value]
]
),
variable: nil
),
Node.new(
type: :match_current_line,
fixed: Node.fixed([[Node::Fixed::Descendant, :pattern]]),
variable: nil
),
Node.new(
type: :match_pattern,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :target],
[Node::Fixed::Descendant, :pattern]
]
),
variable: nil
),
Node.new(
type: :match_rest,
fixed: Node.fixed([[Node::Fixed::Descendant, :value]]),
variable: nil
),
Node.new(
type: :match_var,
fixed: Node.fixed([[Node::Fixed::Attribute, :name]]),
variable: nil
),
Node.new(
type: :match_with_lvasgn,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :pattern],
[Node::Fixed::Descendant, :target]
]
),
variable: nil
),
Node.new(
type: :masgn,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :targets],
[Node::Fixed::Descendant, :values]
]
),
variable: nil
),
Node.new(
type: :mlhs,
fixed: EMPTY_ARRAY,
variable: Node::Variable::Descendants.new(name: :members, range: 0..)
),
Node.new(
type: :module,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :name],
[Node::Fixed::Descendant, :body]
]
),
variable: Node::Variable::Descendants.new(name: :members, range: 2..)
),
Node.new(
type: :next,
fixed: EMPTY_ARRAY,
variable: Node::Variable::Descendants.new(name: :values, range: 0..)
),
Node.new(
type: :nth_ref,
fixed: Node.fixed([[Node::Fixed::Attribute, :value]]),
variable: nil
),
Node.new(
type: :numblock,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :receiver],
[Node::Fixed::Attribute, :parameters],
[Node::Fixed::Descendant, :body]
]
),
variable: nil
),
Node.new(
type: :op_asgn,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :target],
[Node::Fixed::Attribute, :operator],
[Node::Fixed::Descendant, :value]
]
),
variable: nil
),
Node.new(
type: :optarg,
fixed: Node.fixed(
[
[Node::Fixed::Attribute, :name],
[Node::Fixed::Descendant, :value]
]
),
variable: nil
),
Node.new(
type: :or,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :left],
[Node::Fixed::Descendant, :right]
]
),
variable: nil
),
Node.new(
type: :or_asgn,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :target],
[Node::Fixed::Descendant, :value]
]
),
variable: nil
),
Node.new(
type: :rational,
fixed: Node.fixed([[Node::Fixed::Attribute, :name]]),
variable: nil
),
Node.new(
type: :redo,
fixed: EMPTY_ARRAY,
variable: nil
),
Node.new(
type: :regexp,
fixed: [
Node::Fixed::Descendant.new(index: -1, name: :options)
],
variable: Node::Variable::Descendants.new(name: :members, range: 0..-2)
),
Node.new(
type: :regopt,
fixed: EMPTY_ARRAY,
variable: Node::Variable::Attributes.new(name: :options, range: 0..)
),
Node.new(
type: :rescue,
fixed: [
Node::Fixed::Descendant.new(index: 0, name: :body),
Node::Fixed::Descendant.new(index: 1, name: :resbody),
Node::Fixed::Descendant.new(index: -1, name: :else_body)
],
variable: Node::Variable::Descendants.new(name: :resbodies, range: 2..-2)
),
Node.new(
type: :resbody,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :captures],
[Node::Fixed::Descendant, :assignment],
[Node::Fixed::Descendant, :body]
]
),
variable: nil
),
Node.new(
type: :restarg,
fixed: Node.fixed([[Node::Fixed::Attribute, :name]]),
variable: nil
),
Node.new(
type: :retry,
fixed: EMPTY_ARRAY,
variable: nil
),
Node.new(
type: :return,
fixed: EMPTY_ARRAY,
variable: Node::Variable::Descendants.new(name: :values, range: 0..)
),
Node.new(
type: :self,
fixed: EMPTY_ARRAY,
variable: nil
),
Node.new(
type: :send,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :receiver],
[Node::Fixed::Attribute, :selector]
]
),
variable: Node::Variable::Descendants.new(name: :arguments, range: 2..)
),
Node.new(
type: :shadowarg,
fixed: Node.fixed([[Node::Fixed::Attribute, :name]]),
variable: nil
),
Node.new(
type: :nil,
fixed: EMPTY_ARRAY,
variable: nil
),
Node.new(
type: :preexe,
fixed: Node.fixed([[Node::Fixed::Descendant, :body]]),
variable: nil
),
Node.new(
type: :pair,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :name],
[Node::Fixed::Descendant, :value]
]
),
variable: nil
),
Node.new(
type: :postexe,
fixed: Node.fixed([[Node::Fixed::Descendant, :body]]),
variable: nil
),
Node.new(
type: :procarg0,
fixed: EMPTY_ARRAY,
variable: Node::Variable::Descendants.new(name: :arguments, range: 0..)
),
Node.new(
type: :sclass,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :singleton],
[Node::Fixed::Descendant, :body]
]
),
variable: nil
),
Node.new(
type: :splat,
fixed: Node.fixed([[Node::Fixed::Descendant, :value]]),
variable: nil
),
Node.new(
type: :str,
fixed: Node.fixed([[Node::Fixed::Attribute, :value]]),
variable: nil
),
Node.new(
type: :super,
fixed: EMPTY_ARRAY,
variable: Node::Variable::Descendants.new(name: :arguments, range: 0..)
),
Node.new(
type: :sym,
fixed: Node.fixed([[Node::Fixed::Attribute, :value]]),
variable: nil
),
Node.new(
type: :true,
fixed: EMPTY_ARRAY,
variable: nil
),
Node.new(
type: :undef,
fixed: EMPTY_ARRAY,
variable: Node::Variable::Descendants.new(name: :members, range: 0..)
),
Node.new(
type: :until,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :condition],
[Node::Fixed::Descendant, :body]
]
),
variable: nil
),
Node.new(
type: :until_post,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :condition],
[Node::Fixed::Descendant, :body]
]
),
variable: nil
),
Node.new(
type: :when,
fixed: [Node::Fixed::Descendant.new(index: -1, name: :body)],
variable: Node::Variable::Descendants.new(name: :conditions, range: 0..-2)
),
Node.new(
type: :while_post,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :condition],
[Node::Fixed::Descendant, :body]
]
),
variable: nil
),
Node.new(
type: :while,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :condition],
[Node::Fixed::Descendant, :body]
]
),
variable: nil
),
Node.new(
type: :xstr,
fixed: EMPTY_ARRAY,
variable: Node::Variable::Descendants.new(name: :members, range: 0..)
),
Node.new(
type: :yield,
fixed: EMPTY_ARRAY,
variable: Node::Variable::Descendants.new(name: :values, range: 0..)
),
Node.new(
type: :zsuper,
fixed: EMPTY_ARRAY,
variable: nil
)
].to_h { |node| [node.type, node] }.freeze
# Lookup AST structure for a specific type
#
# @param [Symbol] type
#
# @return [Structure]
def self.for(type)
ALL.fetch(type)
end
end # Structure
# rubocop:enable Metrics/ModuleLength
end # AST
end # Mutant