module Puppet::Pops::Parser::InterpolationSupport

This module is an integral part of the Lexer. It defines interpolation support PERFORMANCE NOTE: There are 4 very similar methods in this module that are designed to be as performant as possible. While it is possible to parameterize them into one common method, the overhead of passing parameters and evaluating conditional logic has a negative impact on performance.

Constants

PATTERN_VARIABLE

Public Instance Methods

enqueue_until(brace_count) click to toggle source

Enqueues lexed tokens until either end of input, or the given brace_count is reached

    # File lib/puppet/pops/parser/interpolation_support.rb
189 def enqueue_until brace_count
190   scn = @scanner
191   ctx = @lexing_context
192   queue = @token_queue
193   queue_base = @token_queue[0]
194 
195   scn.skip(self.class::PATTERN_WS)
196   queue_size = queue.size
197   until scn.eos? do
198     token = lex_token
199     if token
200       if token.equal?(queue_base)
201         # A nested #interpolate_dq call shifted the queue_base token from the @token_queue. It must
202         # be put back since it is intended for the top level #interpolate_dq call only.
203         queue.insert(0, token)
204         next # all relevant tokens are already on the queue
205       end
206       token_name = token[0]
207       ctx[:after] = token_name
208       if token_name == :RBRACE && ctx[:brace_count] == brace_count
209         qlength = queue.size - queue_size
210         if qlength == 1
211           # Single token is subject to replacement
212           queue[-1] = transform_to_variable(queue[-1])
213         elsif qlength > 1 && [:DOT, :LBRACK].include?(queue[queue_size + 1][0])
214           # A first word, number of name token followed by '[' or '.' is subject to replacement
215           # But not for other operators such as ?, +, - etc. where user must use a $ before the name
216           # to get a variable
217           queue[queue_size] = transform_to_variable(queue[queue_size])
218         end
219         return
220       end
221       queue << token
222     else
223       scn.skip(self.class::PATTERN_WS)
224     end
225   end
226 end
interpolate_dq() click to toggle source

This is the starting point for a double quoted string with possible interpolation The structure mimics that of the grammar. The logic is explicit (where the former implementation used parameters/structures) given to a generic handler. (This is both easier to understand and faster).

   # File lib/puppet/pops/parser/interpolation_support.rb
18 def interpolate_dq
19   scn = @scanner
20   ctx = @lexing_context
21   before = scn.pos
22   # skip the leading " by doing a scan since the slurp_dqstring uses last matched when there is an error
23   scn.scan(/"/)
24   value,terminator = slurp_dqstring()
25   text = value
26   after = scn.pos
27   loop do
28     case terminator
29     when '"'
30       # simple case, there was no interpolation, return directly
31       return emit_completed([:STRING, text, scn.pos-before], before)
32     when '${'
33       count = ctx[:brace_count]
34       ctx[:brace_count] += 1
35       # The ${ terminator is counted towards the string part
36       enqueue_completed([:DQPRE, text, scn.pos-before], before)
37       # Lex expression tokens until a closing (balanced) brace count is reached
38       enqueue_until count
39       break
40     when '$'
41       varname = scn.scan(PATTERN_VARIABLE)
42       if varname
43         # The $ is counted towards the variable
44         enqueue_completed([:DQPRE, text, after-before-1], before)
45         enqueue_completed([:VARIABLE, varname, scn.pos - after + 1], after - 1)
46         break
47       else
48         # false $ variable start
49         text += terminator
50         value,terminator = slurp_dqstring()
51         text += value
52         after = scn.pos
53       end
54     end
55   end
56   interpolate_tail_dq
57   # return the first enqueued token and shift the queue
58   @token_queue.shift
59 end
interpolate_tail_dq() click to toggle source
   # File lib/puppet/pops/parser/interpolation_support.rb
61 def interpolate_tail_dq
62   scn = @scanner
63   ctx = @lexing_context
64   before = scn.pos
65   value,terminator = slurp_dqstring
66   text = value
67   after = scn.pos
68   loop do
69     case terminator
70     when '"'
71       # simple case, there was no further interpolation, return directly
72       enqueue_completed([:DQPOST, text, scn.pos-before], before)
73       return
74     when '${'
75       count = ctx[:brace_count]
76       ctx[:brace_count] += 1
77       # The ${ terminator is counted towards the string part
78       enqueue_completed([:DQMID, text, scn.pos-before], before)
79       # Lex expression tokens until a closing (balanced) brace count is reached
80       enqueue_until count
81       break
82     when '$'
83       varname = scn.scan(PATTERN_VARIABLE)
84       if varname
85         # The $ is counted towards the variable
86         enqueue_completed([:DQMID, text, after-before-1], before)
87         enqueue_completed([:VARIABLE, varname, scn.pos - after + 1], after - 1)
88         break
89       else
90         # false $ variable start
91         text += terminator
92         value,terminator = slurp_dqstring
93         text += value
94         after = scn.pos
95       end
96     end
97   end
98   interpolate_tail_dq
99 end
interpolate_tail_uq() click to toggle source
    # File lib/puppet/pops/parser/interpolation_support.rb
147 def interpolate_tail_uq
148   scn = @scanner
149   ctx = @lexing_context
150   before = scn.pos
151   value,terminator = slurp_uqstring
152   text = value
153   after = scn.pos
154   loop do
155     case terminator
156     when ''
157       # simple case, there was no further interpolation, return directly
158       enqueue_completed([:DQPOST, text, scn.pos-before], before)
159       return
160     when '${'
161       count = ctx[:brace_count]
162       ctx[:brace_count] += 1
163       # The ${ terminator is counted towards the string part
164       enqueue_completed([:DQMID, text, scn.pos-before], before)
165       # Lex expression tokens until a closing (balanced) brace count is reached
166       enqueue_until count
167       break
168     when '$'
169       varname = scn.scan(PATTERN_VARIABLE)
170       if varname
171         # The $ is counted towards the variable
172         enqueue_completed([:DQMID, text, after-before-1], before)
173         enqueue_completed([:VARIABLE, varname, scn.pos - after + 1], after - 1)
174         break
175       else
176         # false $ variable start
177         text += terminator
178         value,terminator = slurp_uqstring
179         text += value
180         after = scn.pos
181       end
182     end
183   end
184   interpolate_tail_uq
185 end
interpolate_uq() click to toggle source

This is the starting point for a un-quoted string with possible interpolation The logic is explicit (where the former implementation used parameters/strucures) given to a generic handler. (This is both easier to understand and faster).

    # File lib/puppet/pops/parser/interpolation_support.rb
106 def interpolate_uq
107   scn = @scanner
108   ctx = @lexing_context
109   before = scn.pos
110   value,terminator = slurp_uqstring()
111   text = value
112   after = scn.pos
113   loop do
114     case terminator
115     when ''
116       # simple case, there was no interpolation, return directly
117       enqueue_completed([:STRING, text, scn.pos-before], before)
118       return
119     when '${'
120       count = ctx[:brace_count]
121       ctx[:brace_count] += 1
122       # The ${ terminator is counted towards the string part
123       enqueue_completed([:DQPRE, text, scn.pos-before], before)
124       # Lex expression tokens until a closing (balanced) brace count is reached
125       enqueue_until count
126       break
127     when '$'
128       varname = scn.scan(PATTERN_VARIABLE)
129       if varname
130         # The $ is counted towards the variable
131         enqueue_completed([:DQPRE, text, after-before-1], before)
132         enqueue_completed([:VARIABLE, varname, scn.pos - after + 1], after - 1)
133         break
134       else
135         # false $ variable start
136         text += terminator
137         value,terminator = slurp_uqstring()
138         text += value
139         after = scn.pos
140       end
141     end
142   end
143   interpolate_tail_uq
144   nil
145 end
interpolate_uq_to(lexer) click to toggle source

Interpolates unquoted string and transfers the result to the given lexer (This is used when a second lexer instance is used to lex a substring)

    # File lib/puppet/pops/parser/interpolation_support.rb
242 def interpolate_uq_to(lexer)
243   interpolate_uq
244   queue = @token_queue
245   until queue.empty? do
246     lexer.enqueue(queue.shift)
247   end
248 end
transform_to_variable(token) click to toggle source
    # File lib/puppet/pops/parser/interpolation_support.rb
228 def transform_to_variable(token)
229   token_name = token[0]
230   if [:NUMBER, :NAME, :WORD].include?(token_name) || self.class::KEYWORD_NAMES[token_name] || @taskm_keywords[token_name]
231     t = token[1]
232     ta = t.token_array
233     [:VARIABLE, self.class::TokenValue.new([:VARIABLE, ta[1], ta[2]], t.offset, t.locator)]
234   else
235     token
236   end
237 end