~tim/scheme-vm

scheme-vm/compiler/pattern.rb -rw-r--r-- 1.6 KiB
00cdee12Tim Morgan Remove old code 3 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
class Compiler
  class Pattern
    def initialize(pattern, literals: [])
      @pattern = pattern
      @literals = literals.map(&:to_s)
    end

    def match(expr)
      return if @pattern.first != expr.first
      match_sub(expr.dup[1..-1], @pattern[1..-1])
    end

    private

    def match_sub(expr, pattern)
      hash = {}
      return hash if expr.flatten.empty? && pattern.flatten.empty?
      previous = nil
      while (identifier = pattern.shift)
        if @literals.include?(identifier)
          keyword = expr.shift
          return if identifier != keyword
        elsif identifier == '...'
          match_dotted_sub(identifier, expr, previous, hash)
        elsif identifier.is_a?(Array)
          return unless match_sub_array(identifier, expr, hash)
        else
          value = expr.shift
          return if value.nil? && pattern.first != '...'
          hash[identifier] = value
        end
        previous = identifier
      end
      return if expr.any?
      hash
    end

    def match_sub_array(identifier, expr, hash)
      sub_expr = expr.shift
      if sub_expr.nil?
        match = Hash[identifier.zip(Array.new(identifier.size))]
      elsif sub_expr.is_a?(Array)
        match = match_sub(sub_expr.dup, identifier.dup)
      end
      match && hash.merge!(match)
    end

    def match_dotted_sub(identifier, expr, previous, hash)
      if previous.is_a?(Array)
        previous.each_with_index.each do |pr, index|
          hash[pr + identifier] = expr.map { |e| e[index] }
        end
      else
        hash[previous + identifier] = expr.dup
      end
      expr.clear
    end
  end
end