File: evaluating_parser.rb

package info (click to toggle)
puppet-agent 7.23.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 19,092 kB
  • sloc: ruby: 245,074; sh: 456; makefile: 38; xml: 33
file content (162 lines) | stat: -rw-r--r-- 4,646 bytes parent folder | download
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
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
require_relative '../../../puppet/concurrent/thread_local_singleton'

module Puppet::Pops
module Parser

# Does not support "import" and parsing ruby files
#
class EvaluatingParser
  extend Puppet::Concurrent::ThreadLocalSingleton

  attr_reader :parser

  def initialize()
    @parser = Parser.new()
  end

  def parse_string(s, file_source = nil)
    clear()
    # Handling of syntax error can be much improved (in general), now it bails out of the parser
    # and does not have as rich information (when parsing a string), need to update it with the file source
    # (ideally, a syntax error should be entered as an issue, and not just thrown - but that is a general problem
    # and an improvement that can be made in the eparser (rather than here).
    # Also a possible improvement (if the YAML parser returns positions) is to provide correct output of position.
    #
    begin
      assert_and_report(parser.parse_string(s, file_source), file_source).model
    rescue Puppet::ParseErrorWithIssue => e
      raise e
    rescue Puppet::ParseError => e
      # TODO: This is not quite right, why does not the exception have the correct file?
      e.file = file_source unless e.file.is_a?(String) && !e.file.empty?
      raise e
    end
  end

  def parse_file(file)
    clear()
    assert_and_report(parser.parse_file(file), file).model
  end

  def evaluate_string(scope, s, file_source = nil)
    evaluate(scope, parse_string(s, file_source))
  end

  def evaluate_file(scope, file)
    evaluate(scope, parse_file(file))
  end

  def clear()
    @acceptor = nil
  end

  # Create a closure that can be called in the given scope
  def closure(model, scope)
    Evaluator::Closure::Dynamic.new(evaluator, model, scope)
  end

  def evaluate(scope, model)
    return nil unless model
    evaluator.evaluate(model, scope)
  end

  # Evaluates the given expression in a local scope with the given variable bindings
  # set in this local scope, returns what the expression returns.
  #
  def evaluate_expression_with_bindings(scope, variable_bindings, expression)
    evaluator.evaluate_block_with_bindings(scope, variable_bindings, expression)
  end

  def evaluator
    # Do not use the cached evaluator if this is a migration run
    if (Puppet.lookup(:migration_checker) { nil })
      return Evaluator::EvaluatorImpl.new()
    end
    @@evaluator ||= Evaluator::EvaluatorImpl.new()
    @@evaluator
  end

  def convert_to_3x(object, scope)
    evaluator.convert(object, scope, nil)
  end

  def validate(parse_result)
    resulting_acceptor = acceptor()
    validator(resulting_acceptor).validate(parse_result)
    resulting_acceptor
  end

  def acceptor()
    Validation::Acceptor.new
  end

  def validator(acceptor)
    Validation::ValidatorFactory_4_0.new().validator(acceptor)
  end

  def assert_and_report(parse_result, file_source)
    return nil unless parse_result
    if parse_result['source_ref'].nil? || parse_result['source_ref'] == ''
      parse_result['source_ref'] = file_source
    end
    validation_result = validate(parse_result.model)

    IssueReporter.assert_and_report(validation_result,
                                          :emit_warnings => true)
    parse_result
  end

  def quote(x)
    self.class.quote(x)
  end

  # Translates an already parsed string that contains control characters, quotes
  # and backslashes into a quoted string where all such constructs have been escaped.
  # Parsing the return value of this method using the puppet parser should yield
  # exactly the same string as the argument passed to this method
  #
  # The method makes an exception for the two character sequences \$ and \s. They
  # will not be escaped since they have a special meaning in puppet syntax.
  #
  # TODO: Handle \uXXXX characters ??
  #
  # @param x [String] The string to quote and "unparse"
  # @return [String] The quoted string
  #
  def self.quote(x)
    escaped = '"'
    p = nil
    x.each_char do |c|
      case p
      when nil
        # do nothing
      when "\t"
        escaped << '\\t'
      when "\n"
        escaped << '\\n'
      when "\f"
        escaped << '\\f'
      # TODO: \cx is a range of characters - skip for now
      #      when "\c"
      #        escaped << '\\c'
      when '"'
        escaped << '\\"'
      when '\\'
        escaped << if c == '$' || c == 's'; p; else '\\\\'; end # don't escape \ when followed by s or $
      else
        escaped << p
      end
      p = c
    end
    escaped << p unless p.nil?
    escaped << '"'
  end

  class EvaluatingEppParser < EvaluatingParser
    def initialize()
      @parser = EppParser.new()
    end
  end
end
end
end