File: ast_transformer.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 (131 lines) | stat: -rw-r--r-- 4,268 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
require_relative '../../../puppet/parser/ast'

# The receiver of `import(file)` calls; once per imported file, or nil if imports are ignored
#
# Transforms a Pops::Model to classic Puppet AST.
# TODO: Documentation is currently skipped completely (it is only used for Rdoc)
#
class Puppet::Pops::Model::AstTransformer
  AST = Puppet::Parser::AST
  Model = Puppet::Pops::Model

  attr_reader :importer
  def initialize(source_file = "unknown-file", importer=nil)
    @@transform_visitor ||= Puppet::Pops::Visitor.new(nil,"transform",0,0)
    @@query_transform_visitor ||= Puppet::Pops::Visitor.new(nil,"query",0,0)
    @@hostname_transform_visitor ||= Puppet::Pops::Visitor.new(nil,"hostname",0,0)
    @importer = importer
    @source_file = source_file
  end

  # Initialize klass from o (location) and hash (options to created instance).
  # The object o is used to compute a source location. It may be nil. Source position is merged into
  # the given options (non surgically). If o is non-nil, the first found source position going up
  # the containment hierarchy is set. I.e. callers should pass nil if a source position is not wanted
  # or known to be unobtainable for the object.
  #
  # @param o [Object, nil] object from which source position / location is obtained, may be nil
  # @param klass [Class<Puppet::Parser::AST>] the ast class to create an instance of
  # @param hash [Hash] hash with options for the class to create
  #
  def ast(o, klass, hash={})
    # create and pass hash with file and line information
    # PUP-3274 - still needed since hostname transformation requires AST::HostName, and AST::Regexp
    klass.new(**merge_location(hash, o))
  end

  # THIS IS AN EXPENSIVE OPERATION
  # The 3x AST requires line, pos etc. to be recorded directly in the AST nodes and this information
  # must be computed.
  # (Newer implementation only computes the information that is actually needed; typically when raising an
  # exception).
  #
  def merge_location(hash, o)
    if o
      pos = {}
      locator = o.locator
      offset = o.is_a?(Model::Program) ? 0 : o.offset
      pos[:line] = locator.line_for_offset(offset)
      pos[:pos]  = locator.pos_on_line(offset)
      pos[:file] = locator.file
      if nil_or_empty?(pos[:file]) && !nil_or_empty?(@source_file)
        pos[:file] = @source_file
      end
      hash = hash.merge(pos)
    end
    hash
  end

  # Transforms pops expressions into AST 3.1 statements/expressions
  def transform(o)
    begin
    @@transform_visitor.visit_this_0(self,o)
    rescue StandardError => e
      loc_data = {}
      merge_location(loc_data, o)
      raise Puppet::ParseError.new(_("Error while transforming to Puppet 3 AST: %{message}") % { message: e.message },
        loc_data[:file], loc_data[:line], loc_data[:pos], e)
    end
  end

  # Transforms pops expressions into AST 3.1 query expressions
  def query(o)
    @@query_transform_visitor.visit_this_0(self, o)
  end

  # Transforms pops expressions into AST 3.1 hostnames
  def hostname(o)
    @@hostname_transform_visitor.visit_this_0(self, o)
  end


  # Ensures transformation fails if a 3.1 non supported object is encountered in a query expression
  #
  def query_Object(o)
    raise _("Not a valid expression in a collection query: %{class_name}") % { class_name: o.class.name }
  end

  # Transforms Array of host matching expressions into a (Ruby) array of AST::HostName
  def hostname_Array(o)
    o.collect {|x| ast x, AST::HostName, :value => hostname(x) }
  end

  def hostname_LiteralValue(o)
    return o.value
  end

  def hostname_QualifiedName(o)
    return o.value
  end

  def hostname_LiteralNumber(o)
    transform(o) # Number to string with correct radix
  end

  def hostname_LiteralDefault(o)
    return 'default'
  end

  def hostname_LiteralRegularExpression(o)
    ast o, AST::Regex, :value => o.value
  end

  def hostname_Object(o)
    raise _("Illegal expression - unacceptable as a node name")
  end

  def transform_Object(o)
    raise _("Unacceptable transform - found an Object without a rule: %{klass}") % { klass: o.class }
  end

  # Nil, nop
  # Bee bopp a luh-lah, a bop bop boom.
  #
  def is_nop?(o)
    o.nil? || o.is_a?(Model::Nop)
  end

  def nil_or_empty?(x)
    x.nil? || x == ''
  end
end