puppet-lint-halyard 1.1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.travis.yml +10 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE +20 -0
  6. data/README.md +210 -0
  7. data/Rakefile +14 -0
  8. data/bin/puppet-lint +7 -0
  9. data/lib/puppet-lint.rb +214 -0
  10. data/lib/puppet-lint/bin.rb +79 -0
  11. data/lib/puppet-lint/checkplugin.rb +176 -0
  12. data/lib/puppet-lint/checks.rb +91 -0
  13. data/lib/puppet-lint/configuration.rb +153 -0
  14. data/lib/puppet-lint/data.rb +521 -0
  15. data/lib/puppet-lint/lexer.rb +373 -0
  16. data/lib/puppet-lint/lexer/token.rb +101 -0
  17. data/lib/puppet-lint/monkeypatches.rb +2 -0
  18. data/lib/puppet-lint/monkeypatches/string_percent.rb +52 -0
  19. data/lib/puppet-lint/monkeypatches/string_prepend.rb +13 -0
  20. data/lib/puppet-lint/optparser.rb +118 -0
  21. data/lib/puppet-lint/plugins.rb +74 -0
  22. data/lib/puppet-lint/plugins/check_classes.rb +285 -0
  23. data/lib/puppet-lint/plugins/check_comments.rb +55 -0
  24. data/lib/puppet-lint/plugins/check_conditionals.rb +65 -0
  25. data/lib/puppet-lint/plugins/check_documentation.rb +31 -0
  26. data/lib/puppet-lint/plugins/check_nodes.rb +29 -0
  27. data/lib/puppet-lint/plugins/check_resources.rb +194 -0
  28. data/lib/puppet-lint/plugins/check_strings.rb +174 -0
  29. data/lib/puppet-lint/plugins/check_variables.rb +19 -0
  30. data/lib/puppet-lint/plugins/check_whitespace.rb +170 -0
  31. data/lib/puppet-lint/tasks/puppet-lint.rb +91 -0
  32. data/lib/puppet-lint/version.rb +3 -0
  33. data/puppet-lint.gemspec +24 -0
  34. data/spec/fixtures/test/manifests/fail.pp +2 -0
  35. data/spec/fixtures/test/manifests/ignore.pp +1 -0
  36. data/spec/fixtures/test/manifests/ignore_multiple_block.pp +6 -0
  37. data/spec/fixtures/test/manifests/ignore_multiple_line.pp +2 -0
  38. data/spec/fixtures/test/manifests/ignore_reason.pp +1 -0
  39. data/spec/fixtures/test/manifests/init.pp +3 -0
  40. data/spec/fixtures/test/manifests/malformed.pp +1 -0
  41. data/spec/fixtures/test/manifests/url_interpolation.pp +12 -0
  42. data/spec/fixtures/test/manifests/warning.pp +2 -0
  43. data/spec/puppet-lint/bin_spec.rb +326 -0
  44. data/spec/puppet-lint/configuration_spec.rb +56 -0
  45. data/spec/puppet-lint/ignore_overrides_spec.rb +109 -0
  46. data/spec/puppet-lint/lexer/token_spec.rb +18 -0
  47. data/spec/puppet-lint/lexer_spec.rb +783 -0
  48. data/spec/puppet-lint/plugins/check_classes/autoloader_layout_spec.rb +105 -0
  49. data/spec/puppet-lint/plugins/check_classes/class_inherits_from_params_class_spec.rb +35 -0
  50. data/spec/puppet-lint/plugins/check_classes/inherits_across_namespaces_spec.rb +33 -0
  51. data/spec/puppet-lint/plugins/check_classes/names_containing_dash_spec.rb +45 -0
  52. data/spec/puppet-lint/plugins/check_classes/nested_classes_or_defines_spec.rb +76 -0
  53. data/spec/puppet-lint/plugins/check_classes/parameter_order_spec.rb +73 -0
  54. data/spec/puppet-lint/plugins/check_classes/right_to_left_relationship_spec.rb +25 -0
  55. data/spec/puppet-lint/plugins/check_classes/variable_scope_spec.rb +196 -0
  56. data/spec/puppet-lint/plugins/check_comments/slash_comments_spec.rb +45 -0
  57. data/spec/puppet-lint/plugins/check_comments/star_comments_spec.rb +84 -0
  58. data/spec/puppet-lint/plugins/check_conditionals/case_without_default_spec.rb +98 -0
  59. data/spec/puppet-lint/plugins/check_conditionals/selector_inside_resource_spec.rb +36 -0
  60. data/spec/puppet-lint/plugins/check_documentation/documentation_spec.rb +52 -0
  61. data/spec/puppet-lint/plugins/check_nodes/unquoted_node_name_spec.rb +146 -0
  62. data/spec/puppet-lint/plugins/check_resources/duplicate_params_spec.rb +100 -0
  63. data/spec/puppet-lint/plugins/check_resources/ensure_first_param_spec.rb +55 -0
  64. data/spec/puppet-lint/plugins/check_resources/ensure_not_symlink_target_spec.rb +89 -0
  65. data/spec/puppet-lint/plugins/check_resources/file_mode_spec.rb +113 -0
  66. data/spec/puppet-lint/plugins/check_resources/unquoted_file_mode_spec.rb +45 -0
  67. data/spec/puppet-lint/plugins/check_resources/unquoted_resource_title_spec.rb +216 -0
  68. data/spec/puppet-lint/plugins/check_strings/double_quoted_strings_spec.rb +199 -0
  69. data/spec/puppet-lint/plugins/check_strings/only_variable_string_spec.rb +114 -0
  70. data/spec/puppet-lint/plugins/check_strings/puppet_url_without_modules_spec.rb +62 -0
  71. data/spec/puppet-lint/plugins/check_strings/quoted_booleans_spec.rb +129 -0
  72. data/spec/puppet-lint/plugins/check_strings/single_quote_string_with_variables_spec.rb +17 -0
  73. data/spec/puppet-lint/plugins/check_strings/variables_not_enclosed_spec.rb +73 -0
  74. data/spec/puppet-lint/plugins/check_variables/variable_contains_dash_spec.rb +37 -0
  75. data/spec/puppet-lint/plugins/check_whitespace/2sp_soft_tabs_spec.rb +21 -0
  76. data/spec/puppet-lint/plugins/check_whitespace/80chars_spec.rb +54 -0
  77. data/spec/puppet-lint/plugins/check_whitespace/arrow_alignment_spec.rb +524 -0
  78. data/spec/puppet-lint/plugins/check_whitespace/hard_tabs_spec.rb +45 -0
  79. data/spec/puppet-lint/plugins/check_whitespace/trailing_whitespace_spec.rb +101 -0
  80. data/spec/puppet-lint_spec.rb +20 -0
  81. data/spec/spec_helper.rb +129 -0
  82. metadata +229 -0
@@ -0,0 +1,373 @@
1
+ require 'pp'
2
+ require 'strscan'
3
+ require 'puppet-lint/lexer/token'
4
+ require 'set'
5
+
6
+ class PuppetLint
7
+ # Internal: A generic error thrown by the lexer when it encounters something
8
+ # it can't handle.
9
+ class LexerError < StandardError
10
+ # Internal: Get the Integer line number of the location of the error.
11
+ attr_reader :line_no
12
+
13
+ # Internal: Get the Integer column number of the location of the error.
14
+ attr_reader :column
15
+
16
+ # Internal: Initialise a new PuppetLint::LexerError object.
17
+ #
18
+ # line_no - The Integer line number of the location of the error.
19
+ # column - The Integer column number of the location of the error.
20
+ def initialize(line_no, column)
21
+ @line_no = line_no
22
+ @column = column
23
+ end
24
+ end
25
+
26
+ # Internal: The puppet-lint lexer. Converts your manifest into its tokenised
27
+ # form.
28
+ class Lexer
29
+ def initialize
30
+ @line_no = 1
31
+ @column = 1
32
+ end
33
+
34
+ # Internal: A Hash whose keys are Strings representing reserved keywords in
35
+ # the Puppet DSL.
36
+ KEYWORDS = {
37
+ 'class' => true,
38
+ 'case' => true,
39
+ 'default' => true,
40
+ 'define' => true,
41
+ 'import' => true,
42
+ 'if' => true,
43
+ 'else' => true,
44
+ 'elsif' => true,
45
+ 'inherits' => true,
46
+ 'node' => true,
47
+ 'and' => true,
48
+ 'or' => true,
49
+ 'undef' => true,
50
+ 'true' => true,
51
+ 'false' => true,
52
+ 'in' => true,
53
+ 'unless' => true,
54
+ }
55
+
56
+ # Internal: A Hash whose keys are Symbols representing token types which
57
+ # a regular expression can follow.
58
+ REGEX_PREV_TOKENS = {
59
+ :NODE => true,
60
+ :LBRACE => true,
61
+ :RBRACE => true,
62
+ :MATCH => true,
63
+ :NOMATCH => true,
64
+ :COMMA => true,
65
+ }
66
+
67
+ # Internal: An Array of Arrays containing tokens that can be described by
68
+ # a single regular expression. Each sub-Array contains 2 elements, the
69
+ # name of the token as a Symbol and a regular expression describing the
70
+ # value of the token.
71
+ KNOWN_TOKENS = [
72
+ [:CLASSREF, /\A(((::){0,1}[A-Z][-\w]*)+)/],
73
+ [:NUMBER, /\A\b((?:0[xX][0-9A-Fa-f]+|0?\d+(?:\.\d+)?(?:[eE]-?\d+)?))\b/],
74
+ [:NAME, /\A(((::)?[a-z0-9][-\w]*)(::[a-z0-9][-\w]*)*)/],
75
+ [:LBRACK, /\A(\[)/],
76
+ [:RBRACK, /\A(\])/],
77
+ [:LBRACE, /\A(\{)/],
78
+ [:RBRACE, /\A(\})/],
79
+ [:LPAREN, /\A(\()/],
80
+ [:RPAREN, /\A(\))/],
81
+ [:ISEQUAL, /\A(==)/],
82
+ [:MATCH, /\A(=~)/],
83
+ [:FARROW, /\A(=>)/],
84
+ [:EQUALS, /\A(=)/],
85
+ [:APPENDS, /\A(\+=)/],
86
+ [:PARROW, /\A(\+>)/],
87
+ [:PLUS, /\A(\+)/],
88
+ [:GREATEREQUAL, /\A(>=)/],
89
+ [:RSHIFT, /\A(>>)/],
90
+ [:GREATERTHAN, /\A(>)/],
91
+ [:LESSEQUAL, /\A(<=)/],
92
+ [:LLCOLLECT, /\A(<<\|)/],
93
+ [:OUT_EDGE, /\A(<-)/],
94
+ [:OUT_EDGE_SUB, /\A(<~)/],
95
+ [:LCOLLECT, /\A(<\|)/],
96
+ [:LSHIFT, /\A(<<)/],
97
+ [:LESSTHAN, /\A(<)/],
98
+ [:NOMATCH, /\A(!~)/],
99
+ [:NOTEQUAL, /\A(!=)/],
100
+ [:NOT, /\A(!)/],
101
+ [:RRCOLLECT, /\A(\|>>)/],
102
+ [:RCOLLECT, /\A(\|>)/],
103
+ [:IN_EDGE, /\A(->)/],
104
+ [:IN_EDGE_SUB, /\A(~>)/],
105
+ [:MINUS, /\A(-)/],
106
+ [:COMMA, /\A(,)/],
107
+ [:DOT, /\A(\.)/],
108
+ [:COLON, /\A(:)/],
109
+ [:AT, /\A(@)/],
110
+ [:SEMIC, /\A(;)/],
111
+ [:QMARK, /\A(\?)/],
112
+ [:BACKSLASH, /\A(\\)/],
113
+ [:TIMES, /\A(\*)/],
114
+ [:MODULO, /\A(%)/],
115
+ [:PIPE, /\A(\|)/],
116
+ ]
117
+
118
+ # Internal: A Hash whose keys are Symbols representing token types which
119
+ # are considered to be formatting tokens (i.e. tokens that don't contain
120
+ # code).
121
+ FORMATTING_TOKENS = {
122
+ :WHITESPACE => true,
123
+ :NEWLINE => true,
124
+ :COMMENT => true,
125
+ :MLCOMMENT => true,
126
+ :SLASH_COMMENT => true,
127
+ :INDENT => true,
128
+ }
129
+
130
+ # Internal: Access the internal token storage.
131
+ #
132
+ # Returns an Array of PuppetLint::Lexer::Toxen objects.
133
+ def tokens
134
+ @tokens ||= []
135
+ end
136
+
137
+ # Internal: Convert a Puppet manifest into tokens.
138
+ #
139
+ # code - The Puppet manifest to be tokenised as a String.
140
+ #
141
+ # Returns an Array of PuppetLint::Lexer::Token objects.
142
+ # Raises PuppetLint::LexerError if it encounters unexpected characters
143
+ # (usually the result of syntax errors).
144
+ def tokenise(code)
145
+ i = 0
146
+
147
+ while i < code.size
148
+ chunk = code[i..-1]
149
+
150
+ found = false
151
+
152
+ KNOWN_TOKENS.each do |type, regex|
153
+ if value = chunk[regex, 1]
154
+ length = value.size
155
+ if type == :NAME
156
+ if KEYWORDS.include? value
157
+ tokens << new_token(value.upcase.to_sym, value, length)
158
+ else
159
+ tokens << new_token(type, value, length)
160
+ end
161
+ else
162
+ tokens << new_token(type, value, length)
163
+ end
164
+ i += length
165
+ found = true
166
+ break
167
+ end
168
+ end
169
+
170
+ unless found
171
+ if var_name = chunk[/\A\$((::)?([\w-]+::)*[\w-]+(\[.+?\])*)/, 1]
172
+ length = var_name.size + 1
173
+ tokens << new_token(:VARIABLE, var_name, length)
174
+
175
+ elsif chunk.match(/\A'(.*?)'/m)
176
+ str_content = StringScanner.new(code[i+1..-1]).scan_until(/(\A|[^\\])(\\\\)*'/m)
177
+ length = str_content.size + 1
178
+ tokens << new_token(:SSTRING, str_content[0..-2], length)
179
+
180
+ elsif chunk.match(/\A"/)
181
+ str_contents = StringScanner.new(code[i+1..-1]).scan_until(/(\A|[^\\])(\\\\)*"/m)
182
+ _ = code[0..i].split("\n")
183
+ interpolate_string(str_contents, _.count, _.last.length)
184
+ length = str_contents.size + 1
185
+
186
+ elsif comment = chunk[/\A(#.*)/, 1]
187
+ length = comment.size
188
+ comment.sub!(/#/, '')
189
+ tokens << new_token(:COMMENT, comment, length)
190
+
191
+ elsif slash_comment = chunk[/\A(\/\/.*)/, 1]
192
+ length = slash_comment.size
193
+ slash_comment.sub!(/\/\//, '')
194
+ tokens << new_token(:SLASH_COMMENT, slash_comment, length)
195
+
196
+ elsif mlcomment = chunk[/\A(\/\*.*?\*\/)/m, 1]
197
+ length = mlcomment.size
198
+ mlcomment_raw = mlcomment.dup
199
+ mlcomment.sub!(/\A\/\* ?/, '')
200
+ mlcomment.sub!(/ ?\*\/\Z/, '')
201
+ mlcomment.gsub!(/^ *\*/, '')
202
+ tokens << new_token(:MLCOMMENT, mlcomment, length)
203
+ tokens.last.raw = mlcomment_raw
204
+
205
+ elsif chunk.match(/\A\/.*?\//) && possible_regex?
206
+ str_content = StringScanner.new(code[i+1..-1]).scan_until(/(\A|[^\\])(\\\\)*\//m)
207
+ length = str_content.size + 1
208
+ tokens << new_token(:REGEX, str_content[0..-2], length)
209
+
210
+ elsif eolindent = chunk[/\A((\r\n|\r|\n)[ \t]+)/m, 1]
211
+ eol = eolindent[/\A([\r\n]+)/m, 1]
212
+ indent = eolindent[/\A[\r\n]+([ \t]+)/m, 1]
213
+ tokens << new_token(:NEWLINE, eol, eol.size)
214
+ tokens << new_token(:INDENT, indent, indent.size)
215
+ length = indent.size + eol.size
216
+
217
+ elsif whitespace = chunk[/\A([ \t]+)/, 1]
218
+ length = whitespace.size
219
+ tokens << new_token(:WHITESPACE, whitespace, length)
220
+
221
+ elsif eol = chunk[/\A(\r\n|\r|\n)/, 1]
222
+ length = eol.size
223
+ tokens << new_token(:NEWLINE, eol, length)
224
+
225
+ elsif chunk.match(/\A\//)
226
+ length = 1
227
+ tokens << new_token(:DIV, '/', length)
228
+
229
+ else
230
+ raise PuppetLint::LexerError.new(@line_no, @column)
231
+ end
232
+
233
+ i += length
234
+ end
235
+ end
236
+
237
+ tokens
238
+ end
239
+
240
+ # Internal: Given the tokens already processed, determine if the next token
241
+ # could be a regular expression.
242
+ #
243
+ # Returns true if the next token could be a regex, otherwise return false.
244
+ def possible_regex?
245
+ prev_token = tokens.reject { |r|
246
+ FORMATTING_TOKENS.include? r.type
247
+ }.last
248
+
249
+ return true if prev_token.nil?
250
+
251
+ if REGEX_PREV_TOKENS.include? prev_token.type
252
+ true
253
+ else
254
+ false
255
+ end
256
+ end
257
+
258
+ # Internal: Create a new PuppetLint::Lexer::Token object, calculate its
259
+ # line number and column and then add it to the Linked List of tokens.
260
+ #
261
+ # type - The Symbol token type.
262
+ # value - The token value.
263
+ # length - The Integer length of the token's value.
264
+ # opts - A Hash of additional values required to determine line number and
265
+ # column:
266
+ # :line - The Integer line number if calculated externally.
267
+ # :column - The Integer column number if calculated externally.
268
+ #
269
+ # Returns the instantiated PuppetLint::Lexer::Token object.
270
+ def new_token(type, value, length, opts = {})
271
+ column = opts[:column] || @column
272
+ line_no = opts[:line] || @line_no
273
+
274
+ token = Token.new(type, value, line_no, column)
275
+ unless tokens.last.nil?
276
+ token.prev_token = tokens.last
277
+ tokens.last.next_token = token
278
+
279
+ unless FORMATTING_TOKENS.include?(token.type)
280
+ prev_nf_idx = tokens.rindex { |r| ! FORMATTING_TOKENS.include? r.type }
281
+ unless prev_nf_idx.nil?
282
+ prev_nf_token = tokens[prev_nf_idx]
283
+ prev_nf_token.next_code_token = token
284
+ token.prev_code_token = prev_nf_token
285
+ end
286
+ end
287
+ end
288
+
289
+ @column += length
290
+ if type == :NEWLINE
291
+ @line_no += 1
292
+ @column = 1
293
+ end
294
+
295
+ token
296
+ end
297
+
298
+ # Internal: Split a string on multiple terminators, excluding escaped
299
+ # terminators.
300
+ #
301
+ # string - The String to be split.
302
+ # terminators - The String of terminators that the String should be split
303
+ # on.
304
+ #
305
+ # Returns an Array consisting of two Strings, the String up to the first
306
+ # terminator and the terminator that was found.
307
+ def get_string_segment(string, terminators)
308
+ str = string.scan_until(/([^\\]|^|[^\\])([\\]{2})*[#{terminators}]+/)
309
+ begin
310
+ [str[0..-2], str[-1,1]]
311
+ rescue
312
+ [nil, nil]
313
+ end
314
+ end
315
+
316
+ # Internal: Tokenise the contents of a double quoted string.
317
+ #
318
+ # string - The String to be tokenised.
319
+ # line - The Integer line number of the start of the passed string.
320
+ # column - The Integer column number of the start of the passed string.
321
+ #
322
+ # Returns nothing.
323
+ def interpolate_string(string, line, column)
324
+ ss = StringScanner.new(string)
325
+ first = true
326
+ value, terminator = get_string_segment(ss, '"$')
327
+ until value.nil?
328
+ if terminator == "\""
329
+ if first
330
+ tokens << new_token(:STRING, value, value.size + 2, :line => line, :column => column)
331
+ first = false
332
+ else
333
+ line += value.scan(/(\r\n|\r|\n)/).size
334
+ token_column = column + (ss.pos - value.size)
335
+ tokens << new_token(:DQPOST, value, value.size + 1, :line => line, :column => token_column)
336
+ end
337
+ else
338
+ if first
339
+ tokens << new_token(:DQPRE, value, value.size + 1, :line => line, :column => column)
340
+ first = false
341
+ else
342
+ line += value.scan(/(\r\n|\r|\n)/).size
343
+ token_column = column + (ss.pos - value.size)
344
+ tokens << new_token(:DQMID, value, value.size, :line => line, :column => token_column)
345
+ end
346
+ if ss.scan(/\{/).nil?
347
+ var_name = ss.scan(/(::)?([\w-]+::)*[\w-]+/)
348
+ if var_name.nil?
349
+ token_column = column + ss.pos - 1
350
+ tokens << new_token(:DQMID, "$", 1, :line => line, :column => token_column)
351
+ else
352
+ token_column = column + (ss.pos - var_name.size)
353
+ tokens << new_token(:UNENC_VARIABLE, var_name, var_name.size, :line => line, :column => token_column)
354
+ end
355
+ else
356
+ contents = ss.scan_until(/\}/)[0..-2]
357
+ if contents.match(/\A(::)?([\w-]+::)*[\w-]+(\[.+?\])*/)
358
+ contents = "$#{contents}"
359
+ end
360
+ lexer = PuppetLint::Lexer.new
361
+ lexer.tokenise(contents)
362
+ lexer.tokens.each do |token|
363
+ tok_col = column + token.column + (ss.pos - contents.size - 1)
364
+ tok_line = token.line + line - 1
365
+ tokens << new_token(token.type, token.value, token.value.size, :line => tok_line, :column => tok_col)
366
+ end
367
+ end
368
+ end
369
+ value, terminator = get_string_segment(ss, '"$')
370
+ end
371
+ end
372
+ end
373
+ end
@@ -0,0 +1,101 @@
1
+ class PuppetLint
2
+ class Lexer
3
+ # Public: Stores a fragment of the manifest and the information about its
4
+ # location in the manifest.
5
+ class Token
6
+ # Public: Returns the Symbol type of the Token.
7
+ attr_accessor :type
8
+
9
+ # Public: Returns the String value of the Token.
10
+ attr_accessor :value
11
+
12
+ # Public: Returns the raw value of the Token.
13
+ attr_accessor :raw
14
+
15
+ # Public: Returns the Integer line number of the manifest text where
16
+ # the Token can be found.
17
+ attr_reader :line
18
+
19
+ # Public: Returns the Integer column number of the line of the manifest
20
+ # text where the Token can be found.
21
+ attr_reader :column
22
+
23
+ # Public: Gets/sets the next token in the manifest.
24
+ attr_accessor :next_token
25
+
26
+ # Public: Gets/sets the previous token in the manifest.
27
+ attr_accessor :prev_token
28
+
29
+ # Public: Gets/sets the next code token (skips whitespace, comments,
30
+ # etc) in the manifest.
31
+ attr_accessor :next_code_token
32
+
33
+ # Public: Gets/sets the previous code tokne (skips whitespace,
34
+ # comments, etc) in the manifest.
35
+ attr_accessor :prev_code_token
36
+
37
+ # Public: Initialise a new Token object.
38
+ #
39
+ # type - An upper case Symbol describing the type of Token.
40
+ # value - The String value of the Token.
41
+ # line - The Integer line number where the Token can be found in the
42
+ # manifest.
43
+ # column - The Integer number of characters from the start of the line to
44
+ # the start of the Token.
45
+ #
46
+ # Returns the instantiated Token.
47
+ def initialize(type, value, line, column)
48
+ @value = value
49
+ @type = type
50
+ @line = line
51
+ @column = column
52
+ @next_token = nil
53
+ @prev_token = nil
54
+ @next_code_token = nil
55
+ @prev_code_token = nil
56
+ end
57
+
58
+ # Public: Produce a human friendly description of the Token when
59
+ # inspected.
60
+ #
61
+ # Returns a String describing the Token.
62
+ def inspect
63
+ "<Token #{@type.inspect} (#{@value}) @#{@line}:#{@column}>"
64
+ end
65
+
66
+ # Public: Produce a Puppet DSL representation of a Token.
67
+ #
68
+ # Returns a Puppet DSL String.
69
+ def to_manifest
70
+ case @type
71
+ when :STRING
72
+ "\"#{@value}\""
73
+ when :SSTRING
74
+ "'#{@value}'"
75
+ when :DQPRE
76
+ "\"#{@value}"
77
+ when :DQPOST
78
+ "#{@value}\""
79
+ when :VARIABLE
80
+ if !@prev_code_token.nil? && [:DQPRE, :DQMID].include?(@prev_code_token.type)
81
+ "${#{@value}}"
82
+ else
83
+ "$#{@value}"
84
+ end
85
+ when :UNENC_VARIABLE
86
+ "$#{@value}"
87
+ when :NEWLINE
88
+ "\n"
89
+ when :COMMENT
90
+ "##{@value}"
91
+ when :REGEX
92
+ "/#{@value}/"
93
+ when :MLCOMMENT
94
+ @raw
95
+ else
96
+ @value
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end