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
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
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
# 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
# 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
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
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
# 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