Class: MPS::Engines::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/mps/engines/mps.rb

Defined Under Namespace

Classes: Unknown

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config) ⇒ Parser

Returns a new instance of Parser.



15
16
17
18
19
20
21
22
23
24
# File 'lib/mps/engines/mps.rb', line 15

def initialize(config)
  @config = config
  @element_classes = ::MPS::Elements.constants
    .map    { |k| ::MPS::Elements.const_get(k) }
    .select { |x| x.class == Class }
  @interpolator_classes = ::MPS::Interpolators.constants
    .map    { |k| ::MPS::Interpolators.const_get(k) }
    .select { |x| x.class == Class }
  @logger = @config.logger
end

Instance Attribute Details

#element_classesObject (readonly)

Returns the value of attribute element_classes.



12
13
14
# File 'lib/mps/engines/mps.rb', line 12

def element_classes
  @element_classes
end

#interpolator_classesObject (readonly)

Returns the value of attribute interpolator_classes.



13
14
15
# File 'lib/mps/engines/mps.rb', line 13

def interpolator_classes
  @interpolator_classes
end

#loggerObject (readonly)

Returns the value of attribute logger.



11
12
13
# File 'lib/mps/engines/mps.rb', line 11

def logger
  @logger
end

Class Method Details

._apply_interpolations(elements, interpolator_classes) ⇒ Object

Apply registered interpolators to the body_str of each element.



95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/mps/engines/mps.rb', line 95

def self._apply_interpolations(elements, interpolator_classes)
  return if interpolator_classes.empty?
  elements.each_value do |el|
    next unless el.respond_to?(:body_str)
    body = el.instance_variable_get(:@body_str)
    next unless body
    interpolator_classes.each do |ic|
      obj = ic.new
      new_body = body.gsub(ic::SIGNATURE_REGEX) { obj.get_str }
      el.instance_variable_set(:@body_str, new_body) if new_body != body
      body = new_body
    end
  end
end

.matched_element_class(str, element_classes) ⇒ Object

Returns the element class whose SIGNATURE_REGEX matches str, or nil.



27
28
29
# File 'lib/mps/engines/mps.rb', line 27

def self.matched_element_class(str, element_classes)
  element_classes.find { |ec| str =~ ec::SIGNATURE_REGEX }
end

.parse_mps_file_to_elements_hash(mps_file_path, element_classes, interpolator_classes: []) ⇒ Object Also known as: parse_mps_file_to_elments_hash

Parses mps_file_path into a flat hash of ref-path => element instances. Applies interpolators to body strings if interpolator_classes are provided.



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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/mps/engines/mps.rb', line 33

def self.parse_mps_file_to_elements_hash(mps_file_path, element_classes,
                                         interpolator_classes: [])
  content  = File.read(mps_file_path)
  wrapped  = "@#{::MPS::Elements::MPS::SIGNATURE_STAMP}[]{\n#{content}\n}"
  base_ref = ::MPS::Constants::MPS_FILE_NAME_CLIPPER
               .call(File.basename(mps_file_path)).to_i

  open_re  = ::MPS::Constants::AT_REGEXP
  close_re = ::MPS::Constants::END_CURLY_REGEXP

  elements = {}
  stack    = []
  pos      = 0

  while pos < wrapped.size
    open_m  = open_re.match(wrapped, pos)
    close_m = close_re.match(wrapped, pos)

    break if open_m.nil? && close_m.nil?

    use_open = open_m && (close_m.nil? || open_m.begin(0) < close_m.begin(0))

    if use_open
      ref_path = if stack.empty?
        [base_ref]
      else
        parent = stack.last
        parent[:child_counter] += 1
        parent[:ref_path] + [parent[:child_counter]]
      end

      stack.push(
        sign:          open_m[:element_sign],
        args:          open_m[:args],
        body_start:    open_m.end(0),
        child_counter: 0,
        ref_path:      ref_path
      )
      pos = open_m.end(0)
    else
      break if stack.empty?

      frame    = stack.pop
      body_str = wrapped[frame[:body_start]...close_m.begin(0)]
      ref_key  = frame[:ref_path].join(".")
      ec       = matched_element_class(frame[:sign], element_classes)

      elements[ref_key] = if ec
        ec.new(args: frame[:args], refs: frame[:ref_path], body_str: body_str)
      else
        Unknown.new(frame[:sign], frame[:args], frame[:ref_path], body_str)
      end

      pos = close_m.end(0)
    end
  end

  _apply_interpolations(elements, interpolator_classes) unless interpolator_classes.empty?
  elements
end