require 'strscan' class Usher module Util class Parser def self.for_delimiters(router, valid_regex) ParserInstance.new( router, Regexp.new('((:|\*)?' + valid_regex + '|' + router.delimiters_regex + '|\(|\)|\||\{)') ) end class ParserInstance def initialize(router, split_regex) @router = router @split_regex = split_regex end def generate_route(unprocessed_path, conditions, requirements, default_values, generate_with) match_partially = if unprocessed_path.is_a?(String) unprocessed_path = parse(unprocessed_path, requirements, default_values) if unprocessed_path[-1] == ?* unprocessed_path.slice!(-1) true else false end else false end unless unprocessed_path.first.is_a?(Route::Util::Group) group = Usher::Route::Util::Group.new(:all, nil) unprocessed_path.each{|p| group << p} unprocessed_path = group end paths = Route::Util.expand_path(unprocessed_path) paths.each do |path| path.each_with_index do |part, index| part.default_value = default_values[part.name] if part.is_a?(Usher::Route::Variable) && default_values && default_values[part.name] case part when Usher::Route::Variable::Glob if part.look_ahead && !part.look_ahead_priority part.look_ahead = nil part.look_ahead_priority = true else part.look_ahead = path[index + 1, path.size].find{|p| !p.is_a?(Usher::Route::Variable) && !router.delimiter_chars.include?(p[0])} || nil end when Usher::Route::Variable if part.look_ahead && !part.look_ahead_priority part.look_ahead = nil part.look_ahead_priority = true else part.look_ahead = path[index + 1, path.size].find{|p| router.delimiter_chars.include?(p[0])} || router.delimiters.first end end end end Route.new( paths, router, conditions, requirements, default_values, generate_with, match_partially ) end def parse_and_expand(path, requirements = nil, default_values = nil) Usher::Route::Util.expand_path(parse(path, requirements, default_values)) end def parse(path, requirements = nil, default_values = nil) parts = Usher::Route::Util::Group.new(:all, nil) ss = StringScanner.new(path) current_group = parts while !ss.eos? part = ss.scan(@split_regex) case part[0] when ?* var_name = part[1, part.size - 1].to_sym current_group << Usher::Route::Variable::Glob.new(part[1, part.size - 1], nil, requirements && requirements[var_name]) when ?: var_name = part[1, part.size - 1].to_sym current_group << Usher::Route::Variable::Single.new(part[1, part.size - 1], nil, requirements && requirements[var_name]) when ?{ pattern = '' count = 1 variable = ss.scan(/[!:\*]([^,]+),/) until count.zero? regex_part = ss.scan(/\{|\}|[^\{\}]+/) case regex_part[0] when ?{ count += 1 when ?} count -= 1 end pattern << regex_part end pattern.slice!(pattern.length - 1) regex = Regexp.new(pattern) if variable variable_type = variable.slice!(0).chr.to_sym variable_class = case variable_type when :'!' then Usher::Route::Variable::Greedy when :* then Usher::Route::Variable::Glob when :':' then Usher::Route::Variable::Single end variable_name = variable[0, variable.size - 1].to_sym current_group << variable_class.new(variable_name, regex, requirements && requirements[variable_name]) else current_group << regex end when ?( new_group = Usher::Route::Util::Group.new(:any, current_group) current_group << new_group current_group = new_group when ?) current_group = current_group.parent.group_type == :one ? current_group.parent.parent : current_group.parent when ?| unless current_group.parent.group_type == :one detached_group = current_group.parent.pop new_group = Usher::Route::Util::Group.new(:one, detached_group.parent) detached_group.parent = new_group detached_group.group_type = :all new_group << detached_group new_group.parent << new_group end current_group.parent << Usher::Route::Util::Group.new(:all, current_group.parent) current_group = current_group.parent.last else current_group << part end end unless !path || path.empty? parts end private attr_reader :router end end end end