223 lines
5.5 KiB
Ruby
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
|