mailcatcher/vendor/gems/mustermann/lib/mustermann/ast/node.rb

223 lines
5.5 KiB
Ruby

module Mustermann
# @see Mustermann::AST::Pattern
module AST
# @!visibility private
class Node
# @!visibility private
attr_accessor :payload, :start, :stop
# @!visibility private
# @param [Symbol] name of the node
# @return [Class] factory for the node
def self.[](name)
@names ||= {}
@names[name] ||= begin
const_name = constant_name(name)
Object.const_get(const_name) if Object.const_defined?(const_name)
end
end
# Turns a class name into a node identifier.
# @!visibility private
def self.type
name[/[^:]+$/].split(/(?<=.)(?=[A-Z])/).map(&:downcase).join(?_).to_sym
end
# @!visibility private
# @param [Symbol] name of the node
# @return [String] qualified name of factory for the node
def self.constant_name(name)
return self.name if name.to_sym == :node
name = name.to_s.split(?_).map(&:capitalize).join
"#{self.name}::#{name}"
end
# Helper for creating a new instance and calling #parse on it.
# @return [Mustermann::AST::Node]
# @!visibility private
def self.parse(*args, &block)
new(*args).tap { |n| n.parse(&block) }
end
# @!visibility private
def initialize(payload = nil, **options)
options.each { |key, value| public_send("#{key}=", value) }
self.payload = payload
end
# @!visibility private
def is_a?(type)
type = Node[type] if type.is_a? Symbol
super(type)
end
# Double dispatch helper for reading from the buffer into the payload.
# @!visibility private
def parse
self.payload ||= []
while element = yield
payload << element
end
end
# Loop through all nodes that don't have child nodes.
# @!visibility private
def each_leaf(&block)
return enum_for(__method__) unless block_given?
called = false
Array(payload).each do |entry|
next unless entry.respond_to? :each_leaf
entry.each_leaf(&block)
called = true
end
yield(self) unless called
end
# @return [Integer] length of the substring
# @!visibility private
def length
stop - start if start and stop
end
# @return [Integer] minimum size for a node
# @!visibility private
def min_size
0
end
# Turns a class name into a node identifier.
# @!visibility private
def type
self.class.type
end
# @!visibility private
class Capture < Node
# @see Mustermann::AST::Compiler::Capture#default
# @!visibility private
attr_accessor :constraint
# @see Mustermann::AST::Compiler::Capture#qualified
# @!visibility private
attr_accessor :qualifier
# @see Mustermann::AST::Pattern#map_param
# @!visibility private
attr_accessor :convert
# @see Mustermann::AST::Node#parse
# @!visibility private
def parse
self.payload ||= ""
super
end
# @!visibility private
alias_method :name, :payload
end
# @!visibility private
class Char < Node
# @return [Integer] minimum size for a node
# @!visibility private
def min_size
1
end
end
# AST node for template expressions.
# @!visibility private
class Expression < Node
# @!visibility private
attr_accessor :operator
end
# @!visibility private
class Composition < Node
# @!visibility private
def initialize(payload = nil, **options)
super(Array(payload), **options)
end
end
# @!visibility private
class Group < Composition
end
# @!visibility private
class Union < Composition
end
# @!visibility private
class Optional < Node
end
# @!visibility private
class Or < Node
end
# @!visibility private
class Root < Node
# @!visibility private
attr_accessor :pattern
# Will trigger transform.
#
# @see Mustermann::AST::Node.parse
# @!visibility private
def self.parse(string, &block)
root = new
root.pattern = string
root.parse(&block)
root
end
end
# @!visibility private
class Separator < Node
# @return [Integer] minimum size for a node
# @!visibility private
def min_size
1
end
end
# @!visibility private
class Splat < Capture
# @see Mustermann::AST::Node::Capture#name
# @!visibility private
def name
"splat"
end
end
# @!visibility private
class NamedSplat < Splat
# @see Mustermann::AST::Node::Capture#name
# @!visibility private
alias_method :name, :payload
end
# AST node for template variables.
# @!visibility private
class Variable < Capture
# @!visibility private
attr_accessor :prefix, :explode
end
# @!visibility private
class WithLookAhead < Node
# @!visibility private
attr_accessor :head, :at_end
# @!visibility private
def initialize(payload, at_end, **options)
super(**options)
self.head, *self.payload = Array(payload)
self.at_end = at_end
end
end
end
end
end