puppet-lint 2.3.6 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +238 -87
  3. data/README.md +18 -0
  4. data/lib/puppet-lint.rb +1 -1
  5. data/lib/puppet-lint/data.rb +26 -11
  6. data/lib/puppet-lint/lexer.rb +97 -200
  7. data/lib/puppet-lint/lexer/string_slurper.rb +173 -0
  8. data/lib/puppet-lint/lexer/token.rb +8 -0
  9. data/lib/puppet-lint/optparser.rb +4 -5
  10. data/lib/puppet-lint/plugins/check_classes/parameter_order.rb +12 -1
  11. data/lib/puppet-lint/plugins/check_conditionals/case_without_default.rb +15 -1
  12. data/lib/puppet-lint/plugins/check_documentation/documentation.rb +4 -0
  13. data/lib/puppet-lint/plugins/check_resources/ensure_first_param.rb +5 -2
  14. data/lib/puppet-lint/plugins/check_strings/quoted_booleans.rb +1 -0
  15. data/lib/puppet-lint/plugins/check_strings/variables_not_enclosed.rb +71 -0
  16. data/lib/puppet-lint/plugins/check_whitespace/arrow_alignment.rb +1 -1
  17. data/lib/puppet-lint/tasks/puppet-lint.rb +14 -0
  18. data/lib/puppet-lint/tasks/release_test.rb +3 -1
  19. data/lib/puppet-lint/version.rb +1 -1
  20. data/spec/fixtures/test/manifests/two_warnings.pp +5 -0
  21. data/spec/puppet-lint/bin_spec.rb +47 -6
  22. data/spec/puppet-lint/data_spec.rb +12 -0
  23. data/spec/puppet-lint/lexer/string_slurper_spec.rb +473 -0
  24. data/spec/puppet-lint/lexer_spec.rb +1153 -590
  25. data/spec/puppet-lint/plugins/check_classes/parameter_order_spec.rb +18 -0
  26. data/spec/puppet-lint/plugins/check_classes/variable_scope_spec.rb +15 -1
  27. data/spec/puppet-lint/plugins/check_conditionals/case_without_default_spec.rb +39 -0
  28. data/spec/puppet-lint/plugins/check_documentation/documentation_spec.rb +18 -0
  29. data/spec/puppet-lint/plugins/check_resources/ensure_first_param_spec.rb +16 -0
  30. data/spec/puppet-lint/plugins/check_strings/double_quoted_strings_spec.rb +5 -5
  31. data/spec/puppet-lint/plugins/check_strings/only_variable_string_spec.rb +6 -6
  32. data/spec/puppet-lint/plugins/check_strings/variables_not_enclosed_spec.rb +32 -0
  33. data/spec/puppet-lint/plugins/check_variables/variable_is_lowercase_spec.rb +28 -0
  34. data/spec/spec_helper.rb +7 -5
  35. metadata +14 -17
  36. data/.gitignore +0 -12
  37. data/.rspec +0 -2
  38. data/.rubocop.yml +0 -74
  39. data/.rubocop_todo.yml +0 -89
  40. data/.travis.yml +0 -24
  41. data/Gemfile +0 -40
  42. data/Rakefile +0 -42
  43. data/appveyor.yml +0 -33
  44. data/puppet-lint.gemspec +0 -19
@@ -63,82 +63,1051 @@ describe PuppetLint::Lexer do # rubocop:disable Metrics/BlockLength
63
63
  end
64
64
  end
65
65
 
66
- describe '#slurp_string' do
67
- it 'raises a LexerError if the string is not terminated' do
68
- expect {
69
- @lexer.slurp_string('unterminated string')
70
- }.to raise_error(PuppetLint::LexerError)
66
+ context '#process_string_segments' do
67
+ subject(:tokens) { @lexer.tokens }
68
+ subject(:manifest) { @lexer.tokens.map(&:to_manifest).join }
69
+
70
+ before(:each) do
71
+ @lexer.process_string_segments(segments)
72
+ end
73
+
74
+ context 'an empty string segment' do
75
+ let(:segments) do
76
+ [
77
+ [:STRING, ''],
78
+ ]
79
+ end
80
+
81
+ it 'creates a :STRING token' do
82
+ expect(tokens).to have(1).token
83
+ expect(tokens[0]).to have_attributes(
84
+ :type => :STRING,
85
+ :value => '',
86
+ :line => 1,
87
+ :column => 1
88
+ )
89
+ end
90
+
91
+ it 'can render the result back into a manifest' do
92
+ expect(manifest).to eq('""')
93
+ end
94
+ end
95
+
96
+ context 'an interpolated variable with a suffix' do
97
+ let(:segments) do
98
+ [
99
+ [:STRING, ''],
100
+ [:INTERP, 'foo'],
101
+ [:STRING, 'bar'],
102
+ ]
103
+ end
104
+
105
+ it 'creates a tokenised string with an interpolated variable' do
106
+ expect(tokens).to have(3).tokens
107
+ expect(tokens[0]).to have_attributes(
108
+ :type => :DQPRE,
109
+ :value => '',
110
+ :line => 1,
111
+ :column => 1
112
+ )
113
+ expect(tokens[1]).to have_attributes(
114
+ :type => :VARIABLE,
115
+ :value => 'foo',
116
+ :line => 1,
117
+ :column => 4
118
+ )
119
+ expect(tokens[2]).to have_attributes(
120
+ :type => :DQPOST,
121
+ :value => 'bar',
122
+ :line => 1,
123
+ :column => 7
124
+ )
125
+ end
126
+
127
+ it 'can render the result back into a manifest' do
128
+ expect(manifest).to eq('"${foo}bar"')
129
+ end
130
+ end
131
+
132
+ context 'an interpolated variable surrounded by string segments' do
133
+ let(:segments) do
134
+ [
135
+ [:STRING, 'foo'],
136
+ [:INTERP, 'bar'],
137
+ [:STRING, 'baz'],
138
+ ]
139
+ end
140
+
141
+ it 'creates a tokenised string with an interpolated variable' do
142
+ expect(tokens).to have(3).tokens
143
+ expect(tokens[0]).to have_attributes(
144
+ :type => :DQPRE,
145
+ :value => 'foo',
146
+ :line => 1,
147
+ :column => 1
148
+ )
149
+ expect(tokens[1]).to have_attributes(
150
+ :type => :VARIABLE,
151
+ :value => 'bar',
152
+ :line => 1,
153
+ :column => 7
154
+ )
155
+ expect(tokens[2]).to have_attributes(
156
+ :type => :DQPOST,
157
+ :value => 'baz',
158
+ :line => 1,
159
+ :column => 10
160
+ )
161
+ end
162
+
163
+ it 'can render the result back into a manifest' do
164
+ expect(manifest).to eq('"foo${bar}baz"')
165
+ end
166
+ end
167
+
168
+ context 'multiple interpolated variables with surrounding text' do
169
+ let(:segments) do
170
+ [
171
+ [:STRING, 'foo'],
172
+ [:INTERP, 'bar'],
173
+ [:STRING, 'baz'],
174
+ [:INTERP, 'gronk'],
175
+ [:STRING, 'meh'],
176
+ ]
177
+ end
178
+
179
+ it 'creates a tokenised string with the interpolated variables' do
180
+ expect(tokens).to have(5).tokens
181
+
182
+ expect(tokens[0]).to have_attributes(
183
+ :type => :DQPRE,
184
+ :value => 'foo',
185
+ :line => 1,
186
+ :column => 1
187
+ )
188
+ expect(tokens[1]).to have_attributes(
189
+ :type => :VARIABLE,
190
+ :value => 'bar',
191
+ :line => 1,
192
+ :column => 7
193
+ )
194
+ expect(tokens[2]).to have_attributes(
195
+ :type => :DQMID,
196
+ :value => 'baz',
197
+ :line => 1,
198
+ :column => 10
199
+ )
200
+ expect(tokens[3]).to have_attributes(
201
+ :type => :VARIABLE,
202
+ :value => 'gronk',
203
+ :line => 1,
204
+ :column => 16
205
+ )
206
+ expect(tokens[4]).to have_attributes(
207
+ :type => :DQPOST,
208
+ :value => 'meh',
209
+ :line => 1,
210
+ :column => 21
211
+ )
212
+ end
213
+
214
+ it 'can render the result back into a manifest' do
215
+ expect(manifest).to eq('"foo${bar}baz${gronk}meh"')
216
+ end
217
+ end
218
+
219
+ context 'only a single interpolated variable' do
220
+ let(:segments) do
221
+ [
222
+ [:STRING, ''],
223
+ [:INTERP, 'foo'],
224
+ [:STRING, ''],
225
+ ]
226
+ end
227
+
228
+ it 'creates a tokenised string' do
229
+ expect(tokens).to have(3).tokens
230
+
231
+ expect(tokens[0]).to have_attributes(
232
+ :type => :DQPRE,
233
+ :value => '',
234
+ :line => 1,
235
+ :column => 1
236
+ )
237
+ expect(tokens[1]).to have_attributes(
238
+ :type => :VARIABLE,
239
+ :value => 'foo',
240
+ :line => 1,
241
+ :column => 4
242
+ )
243
+ expect(tokens[2]).to have_attributes(
244
+ :type => :DQPOST,
245
+ :value => '',
246
+ :line => 1,
247
+ :column => 7
248
+ )
249
+ end
250
+
251
+ it 'can render the result back into a manifest' do
252
+ expect(manifest).to eq('"${foo}"')
253
+ end
254
+ end
255
+
256
+ context 'treats a variable named the same as the keyword as a variable' do
257
+ PuppetLint::Lexer::KEYWORDS.keys.each do |keyword|
258
+ context "for '#{keyword}'" do
259
+ let(:segments) do
260
+ [
261
+ [:STRING, ''],
262
+ [:INTERP, keyword],
263
+ [:STRING, ''],
264
+ ]
265
+ end
266
+
267
+ it 'creates a tokenised string' do
268
+ expect(tokens).to have(3).tokens
269
+
270
+ expect(tokens[0]).to have_attributes(
271
+ :type => :DQPRE,
272
+ :value => '',
273
+ :line => 1,
274
+ :column => 1
275
+ )
276
+ expect(tokens[1]).to have_attributes(
277
+ :type => :VARIABLE,
278
+ :value => keyword,
279
+ :line => 1,
280
+ :column => 4
281
+ )
282
+ expect(tokens[2]).to have_attributes(
283
+ :type => :DQPOST,
284
+ :value => '',
285
+ :line => 1,
286
+ :column => keyword.size + 4
287
+ )
288
+ end
289
+
290
+ it 'can render the result back into a manifest' do
291
+ expect(manifest).to eq("\"${#{keyword}}\"")
292
+ end
293
+ end
294
+ end
295
+ end
296
+
297
+ context 'an interpolated variable with an unnecessary $' do
298
+ let(:segments) do
299
+ [
300
+ [:STRING, ''],
301
+ [:INTERP, '$bar'],
302
+ [:STRING, ''],
303
+ ]
304
+ end
305
+
306
+ it 'creates a tokenised string' do
307
+ expect(tokens).to have(3).tokens
308
+
309
+ expect(tokens[0]).to have_attributes(
310
+ :type => :DQPRE,
311
+ :value => '',
312
+ :line => 1,
313
+ :column => 1
314
+ )
315
+ expect(tokens[1]).to have_attributes(
316
+ :type => :VARIABLE,
317
+ :value => 'bar',
318
+ :line => 1,
319
+ :column => 4
320
+ )
321
+ expect(tokens[2]).to have_attributes(
322
+ :type => :DQPOST,
323
+ :value => '',
324
+ :line => 1,
325
+ :column => 8
326
+ )
327
+ end
328
+
329
+ it 'includes the extra $ in the rendered manifest' do
330
+ expect(manifest).to eq('"${$bar}"')
331
+ end
332
+ end
333
+
334
+ context 'an interpolated variable with an array reference' do
335
+ let(:segments) do
336
+ [
337
+ [:STRING, ''],
338
+ [:INTERP, 'foo[bar][baz]'],
339
+ [:STRING, ''],
340
+ ]
341
+ end
342
+
343
+ it 'creates a tokenised string' do
344
+ expect(tokens).to have(9).tokens
345
+
346
+ expect(tokens[0]).to have_attributes(
347
+ :type => :DQPRE,
348
+ :value => '',
349
+ :line => 1,
350
+ :column => 1
351
+ )
352
+ expect(tokens[1]).to have_attributes(
353
+ :type => :VARIABLE,
354
+ :value => 'foo',
355
+ :line => 1,
356
+ :column => 4
357
+ )
358
+ expect(tokens[2]).to have_attributes(
359
+ :type => :LBRACK,
360
+ :value => '[',
361
+ :line => 1,
362
+ :column => 7
363
+ )
364
+ expect(tokens[3]).to have_attributes(
365
+ :type => :NAME,
366
+ :value => 'bar',
367
+ :line => 1,
368
+ :column => 8
369
+ )
370
+ expect(tokens[4]).to have_attributes(
371
+ :type => :RBRACK,
372
+ :value => ']',
373
+ :line => 1,
374
+ :column => 11
375
+ )
376
+ expect(tokens[5]).to have_attributes(
377
+ :type => :LBRACK,
378
+ :value => '[',
379
+ :line => 1,
380
+ :column => 12
381
+ )
382
+ expect(tokens[6]).to have_attributes(
383
+ :type => :NAME,
384
+ :value => 'baz',
385
+ :line => 1,
386
+ :column => 13
387
+ )
388
+ expect(tokens[7]).to have_attributes(
389
+ :type => :RBRACK,
390
+ :value => ']',
391
+ :line => 1,
392
+ :column => 16
393
+ )
394
+ expect(tokens[8]).to have_attributes(
395
+ :type => :DQPOST,
396
+ :value => '',
397
+ :line => 1,
398
+ :column => 17
399
+ )
400
+ end
401
+
402
+ it 'can render the result back into a manifest' do
403
+ expect(manifest).to eq('"${foo[bar][baz]}"')
404
+ end
405
+ end
406
+
407
+ context 'multiple interpreted variables' do
408
+ let(:segments) do
409
+ [
410
+ [:STRING, ''],
411
+ [:INTERP, 'foo'],
412
+ [:STRING, ''],
413
+ [:INTERP, 'bar'],
414
+ [:STRING, ''],
415
+ ]
416
+ end
417
+
418
+ it 'creates a tokenised string' do
419
+ expect(tokens).to have(5).tokens
420
+
421
+ expect(tokens[0]).to have_attributes(
422
+ :type => :DQPRE,
423
+ :value => '',
424
+ :line => 1,
425
+ :column => 1
426
+ )
427
+ expect(tokens[1]).to have_attributes(
428
+ :type => :VARIABLE,
429
+ :value => 'foo',
430
+ :line => 1,
431
+ :column => 4
432
+ )
433
+ expect(tokens[2]).to have_attributes(
434
+ :type => :DQMID,
435
+ :value => '',
436
+ :line => 1,
437
+ :column => 7
438
+ )
439
+ expect(tokens[3]).to have_attributes(
440
+ :type => :VARIABLE,
441
+ :value => 'bar',
442
+ :line => 1,
443
+ :column => 10
444
+ )
445
+ expect(tokens[4]).to have_attributes(
446
+ :type => :DQPOST,
447
+ :value => '',
448
+ :line => 1,
449
+ :column => 13
450
+ )
451
+ end
452
+
453
+ it 'can render the result back into a manifest' do
454
+ expect(manifest).to eq('"${foo}${bar}"')
455
+ end
456
+ end
457
+
458
+ context 'an unenclosed variable' do
459
+ let(:segments) do
460
+ [
461
+ [:STRING, ''],
462
+ [:UNENC_VAR, '$foo'],
463
+ [:STRING, ''],
464
+ ]
465
+ end
466
+
467
+ it 'creates a tokenised string' do
468
+ expect(tokens).to have(3).tokens
469
+
470
+ expect(tokens[0]).to have_attributes(
471
+ :type => :DQPRE,
472
+ :value => '',
473
+ :line => 1,
474
+ :column => 1
475
+ )
476
+ expect(tokens[1]).to have_attributes(
477
+ :type => :UNENC_VARIABLE,
478
+ :value => 'foo',
479
+ :line => 1,
480
+ :column => 2
481
+ )
482
+ expect(tokens[2]).to have_attributes(
483
+ :type => :DQPOST,
484
+ :value => '',
485
+ :line => 1,
486
+ :column => 6
487
+ )
488
+ end
489
+
490
+ it 'can render the result back into a manifest' do
491
+ expect(manifest).to eq('"$foo"')
492
+ end
493
+ end
494
+
495
+ context 'an interpolation with a nested single quote string' do
496
+ let(:segments) do
497
+ [
498
+ [:STRING, 'string with '],
499
+ [:INTERP, "'a nested single quoted string'"],
500
+ [:STRING, ' inside it'],
501
+ ]
502
+ end
503
+
504
+ it 'creates a tokenised string' do
505
+ expect(tokens).to have(3).tokens
506
+
507
+ expect(tokens[0]).to have_attributes(
508
+ :type => :DQPRE,
509
+ :value => 'string with ',
510
+ :line => 1,
511
+ :column => 1
512
+ )
513
+ expect(tokens[1]).to have_attributes(
514
+ :type => :SSTRING,
515
+ :value => 'a nested single quoted string',
516
+ :line => 1,
517
+ :column => 16
518
+ )
519
+ expect(tokens[2]).to have_attributes(
520
+ :type => :DQPOST,
521
+ :value => ' inside it',
522
+ :line => 1,
523
+ :column => 47
524
+ )
525
+ end
526
+
527
+ it 'can render the result back into a manifest' do
528
+ expect(manifest).to eq(%("string with ${'a nested single quoted string'} inside it"))
529
+ end
530
+ end
531
+
532
+ context 'an interpolation with a nested math expression' do
533
+ let(:segments) do
534
+ [
535
+ [:STRING, 'string with '],
536
+ [:INTERP, '(3+5)/4'],
537
+ [:STRING, ' nested math'],
538
+ ]
539
+ end
540
+
541
+ it 'creates a tokenised string' do
542
+ expect(tokens).to have(9).tokens
543
+
544
+ expect(tokens[0]).to have_attributes(
545
+ :type => :DQPRE,
546
+ :value => 'string with ',
547
+ :line => 1,
548
+ :column => 1
549
+ )
550
+ expect(tokens[1]).to have_attributes(
551
+ :type => :LPAREN,
552
+ :value => '(',
553
+ :line => 1,
554
+ :column => 16
555
+ )
556
+ expect(tokens[2]).to have_attributes(
557
+ :type => :NUMBER,
558
+ :value => '3',
559
+ :line => 1,
560
+ :column => 17
561
+ )
562
+ expect(tokens[3]).to have_attributes(
563
+ :type => :PLUS,
564
+ :value => '+',
565
+ :line => 1,
566
+ :column => 18
567
+ )
568
+ expect(tokens[4]).to have_attributes(
569
+ :type => :NUMBER,
570
+ :value => '5',
571
+ :line => 1,
572
+ :column => 19
573
+ )
574
+ expect(tokens[5]).to have_attributes(
575
+ :type => :RPAREN,
576
+ :value => ')',
577
+ :line => 1,
578
+ :column => 20
579
+ )
580
+ expect(tokens[6]).to have_attributes(
581
+ :type => :DIV,
582
+ :value => '/',
583
+ :line => 1,
584
+ :column => 21
585
+ )
586
+ expect(tokens[7]).to have_attributes(
587
+ :type => :NUMBER,
588
+ :value => '4',
589
+ :line => 1,
590
+ :column => 22
591
+ )
592
+ expect(tokens[8]).to have_attributes(
593
+ :type => :DQPOST,
594
+ :value => ' nested math',
595
+ :line => 1,
596
+ :column => 23
597
+ )
598
+ end
599
+
600
+ it 'can render the result back into a manifest' do
601
+ expect(manifest).to eq('"string with ${(3+5)/4} nested math"')
602
+ end
603
+ end
604
+
605
+ context 'an interpolation with a nested array' do
606
+ let(:segments) do
607
+ [
608
+ [:STRING, 'string with '],
609
+ [:INTERP, "['an array ', $v2]"],
610
+ [:STRING, ' in it'],
611
+ ]
612
+ end
613
+
614
+ it 'creates a tokenised string' do
615
+ expect(tokens).to have(8).tokens
616
+
617
+ expect(tokens[0]).to have_attributes(
618
+ :type => :DQPRE,
619
+ :value => 'string with ',
620
+ :line => 1,
621
+ :column => 1
622
+ )
623
+ expect(tokens[1]).to have_attributes(
624
+ :type => :LBRACK,
625
+ :value => '[',
626
+ :line => 1,
627
+ :column => 16
628
+ )
629
+ expect(tokens[2]).to have_attributes(
630
+ :type => :SSTRING,
631
+ :value => 'an array ',
632
+ :line => 1,
633
+ :column => 17
634
+ )
635
+ expect(tokens[3]).to have_attributes(
636
+ :type => :COMMA,
637
+ :value => ',',
638
+ :line => 1,
639
+ :column => 28
640
+ )
641
+ expect(tokens[4]).to have_attributes(
642
+ :type => :WHITESPACE,
643
+ :value => ' ',
644
+ :line => 1,
645
+ :column => 29
646
+ )
647
+ expect(tokens[5]).to have_attributes(
648
+ :type => :VARIABLE,
649
+ :value => 'v2',
650
+ :line => 1,
651
+ :column => 30
652
+ )
653
+ expect(tokens[6]).to have_attributes(
654
+ :type => :RBRACK,
655
+ :value => ']',
656
+ :line => 1,
657
+ :column => 33
658
+ )
659
+ expect(tokens[7]).to have_attributes(
660
+ :type => :DQPOST,
661
+ :value => ' in it',
662
+ :line => 1,
663
+ :column => 34
664
+ )
665
+ end
666
+
667
+ it 'can render the result back into a manifest' do
668
+ expect(manifest).to eq(%("string with ${['an array ', $v2]} in it"))
669
+ end
670
+ end
671
+
672
+ context 'multiple unenclosed variables' do
673
+ let(:segments) do
674
+ [
675
+ [:STRING, ''],
676
+ [:UNENC_VAR, '$foo'],
677
+ [:STRING, ''],
678
+ [:UNENC_VAR, '$bar'],
679
+ [:STRING, ''],
680
+ ]
681
+ end
682
+
683
+ it 'creates a tokenised string' do
684
+ expect(tokens).to have(5).tokens
685
+
686
+ expect(tokens[0]).to have_attributes(
687
+ :type => :DQPRE,
688
+ :value => '',
689
+ :line => 1,
690
+ :column => 1
691
+ )
692
+ expect(tokens[1]).to have_attributes(
693
+ :type => :UNENC_VARIABLE,
694
+ :value => 'foo',
695
+ :line => 1,
696
+ :column => 2
697
+ )
698
+ expect(tokens[2]).to have_attributes(
699
+ :type => :DQMID,
700
+ :value => '',
701
+ :line => 1,
702
+ :column => 6
703
+ )
704
+ expect(tokens[3]).to have_attributes(
705
+ :type => :UNENC_VARIABLE,
706
+ :value => 'bar',
707
+ :line => 1,
708
+ :column => 6
709
+ )
710
+ expect(tokens[4]).to have_attributes(
711
+ :type => :DQPOST,
712
+ :value => '',
713
+ :line => 1,
714
+ :column => 10
715
+ )
716
+ end
717
+
718
+ it 'can render the result back into a manifest' do
719
+ expect(manifest).to eq('"$foo$bar"')
720
+ end
721
+ end
722
+
723
+ context 'an unenclosed variable with a trailing $' do
724
+ let(:segments) do
725
+ [
726
+ [:STRING, 'foo'],
727
+ [:UNENC_VAR, '$bar'],
728
+ [:STRING, '$'],
729
+ ]
730
+ end
731
+
732
+ it 'creates a tokenised string' do
733
+ expect(tokens).to have(3).tokens
734
+
735
+ expect(tokens[0]).to have_attributes(
736
+ :type => :DQPRE,
737
+ :value => 'foo',
738
+ :line => 1,
739
+ :column => 1
740
+ )
741
+ expect(tokens[1]).to have_attributes(
742
+ :type => :UNENC_VARIABLE,
743
+ :value => 'bar',
744
+ :line => 1,
745
+ :column => 5
746
+ )
747
+ expect(tokens[2]).to have_attributes(
748
+ :type => :DQPOST,
749
+ :value => '$',
750
+ :line => 1,
751
+ :column => 9
752
+ )
753
+ end
754
+
755
+ it 'can render the result back into a manifest' do
756
+ expect(manifest).to eq('"foo$bar$"')
757
+ end
758
+ end
759
+
760
+ context 'an interpolation with a complex function chain' do
761
+ let(:segments) do
762
+ [
763
+ [:STRING, ''],
764
+ [:INTERP, 'key'],
765
+ [:STRING, ' '],
766
+ [:INTERP, 'flatten([$value]).join("\nkey ")'],
767
+ [:STRING, ''],
768
+ ]
769
+ end
770
+
771
+ it 'creates a tokenised string' do
772
+ expect(tokens).to have(15).tokens
773
+
774
+ expect(tokens[0]).to have_attributes(
775
+ :type => :DQPRE,
776
+ :value => '',
777
+ :line => 1,
778
+ :column => 1
779
+ )
780
+ expect(tokens[1]).to have_attributes(
781
+ :type => :VARIABLE,
782
+ :value => 'key',
783
+ :line => 1,
784
+ :column => 4
785
+ )
786
+ expect(tokens[2]).to have_attributes(
787
+ :type => :DQMID,
788
+ :value => ' ',
789
+ :line => 1,
790
+ :column => 7
791
+ )
792
+ expect(tokens[3]).to have_attributes(
793
+ :type => :FUNCTION_NAME,
794
+ :value => 'flatten',
795
+ :line => 1,
796
+ :column => 11
797
+ )
798
+ expect(tokens[4]).to have_attributes(
799
+ :type => :LPAREN,
800
+ :value => '(',
801
+ :line => 1,
802
+ :column => 18
803
+ )
804
+ expect(tokens[5]).to have_attributes(
805
+ :type => :LBRACK,
806
+ :value => '[',
807
+ :line => 1,
808
+ :column => 19
809
+ )
810
+ expect(tokens[6]).to have_attributes(
811
+ :type => :VARIABLE,
812
+ :value => 'value',
813
+ :line => 1,
814
+ :column => 20
815
+ )
816
+ expect(tokens[7]).to have_attributes(
817
+ :type => :RBRACK,
818
+ :value => ']',
819
+ :line => 1,
820
+ :column => 26
821
+ )
822
+ expect(tokens[8]).to have_attributes(
823
+ :type => :RPAREN,
824
+ :value => ')',
825
+ :line => 1,
826
+ :column => 27
827
+ )
828
+ expect(tokens[9]).to have_attributes(
829
+ :type => :DOT,
830
+ :value => '.',
831
+ :line => 1,
832
+ :column => 28
833
+ )
834
+ expect(tokens[10]).to have_attributes(
835
+ :type => :FUNCTION_NAME,
836
+ :value => 'join',
837
+ :line => 1,
838
+ :column => 29
839
+ )
840
+ expect(tokens[11]).to have_attributes(
841
+ :type => :LPAREN,
842
+ :value => '(',
843
+ :line => 1,
844
+ :column => 33
845
+ )
846
+ expect(tokens[12]).to have_attributes(
847
+ :type => :STRING,
848
+ :value => '\nkey ',
849
+ :line => 1,
850
+ :column => 34
851
+ )
852
+ expect(tokens[13]).to have_attributes(
853
+ :type => :RPAREN,
854
+ :value => ')',
855
+ :line => 1,
856
+ :column => 42
857
+ )
858
+ expect(tokens[14]).to have_attributes(
859
+ :type => :DQPOST,
860
+ :value => '',
861
+ :line => 1,
862
+ :column => 43
863
+ )
864
+ end
865
+
866
+ it 'can render the result back into a manifest' do
867
+ expect(manifest).to eq('"${key} ${flatten([$value]).join("\nkey ")}"')
868
+ end
869
+ end
870
+
871
+ context 'nested interpolations' do
872
+ let(:segments) do
873
+ [
874
+ [:STRING, ''],
875
+ [:INTERP, 'facts["network_${iface}"]'],
876
+ [:STRING, '/'],
877
+ [:INTERP, 'facts["netmask_${iface}"]'],
878
+ [:STRING, ''],
879
+ ]
880
+ end
881
+
882
+ it 'creates a tokenised string' do
883
+ expect(tokens).to have(15).tokens
884
+
885
+ expect(tokens[0]).to have_attributes(
886
+ :type => :DQPRE,
887
+ :value => '',
888
+ :line => 1,
889
+ :column => 1
890
+ )
891
+ expect(tokens[1]).to have_attributes(
892
+ :type => :VARIABLE,
893
+ :value => 'facts',
894
+ :line => 1,
895
+ :column => 4
896
+ )
897
+ expect(tokens[2]).to have_attributes(
898
+ :type => :LBRACK,
899
+ :value => '[',
900
+ :line => 1,
901
+ :column => 9
902
+ )
903
+ expect(tokens[3]).to have_attributes(
904
+ :type => :DQPRE,
905
+ :value => 'network_',
906
+ :line => 1,
907
+ :column => 10
908
+ )
909
+ expect(tokens[4]).to have_attributes(
910
+ :type => :VARIABLE,
911
+ :value => 'iface',
912
+ :line => 1,
913
+ :column => 21
914
+ )
915
+ expect(tokens[5]).to have_attributes(
916
+ :type => :DQPOST,
917
+ :value => '',
918
+ :line => 1,
919
+ :column => 26
920
+ )
921
+ expect(tokens[6]).to have_attributes(
922
+ :type => :RBRACK,
923
+ :value => ']',
924
+ :line => 1,
925
+ :column => 28
926
+ )
927
+ expect(tokens[7]).to have_attributes(
928
+ :type => :DQMID,
929
+ :value => '/',
930
+ :line => 1,
931
+ :column => 29
932
+ )
933
+ expect(tokens[8]).to have_attributes(
934
+ :type => :VARIABLE,
935
+ :value => 'facts',
936
+ :line => 1,
937
+ :column => 33
938
+ )
939
+ expect(tokens[9]).to have_attributes(
940
+ :type => :LBRACK,
941
+ :value => '[',
942
+ :line => 1,
943
+ :column => 38
944
+ )
945
+ expect(tokens[10]).to have_attributes(
946
+ :type => :DQPRE,
947
+ :value => 'netmask_',
948
+ :line => 1,
949
+ :column => 39
950
+ )
951
+ expect(tokens[11]).to have_attributes(
952
+ :type => :VARIABLE,
953
+ :value => 'iface',
954
+ :line => 1,
955
+ :column => 50
956
+ )
957
+ expect(tokens[12]).to have_attributes(
958
+ :type => :DQPOST,
959
+ :value => '',
960
+ :line => 1,
961
+ :column => 55
962
+ )
963
+ expect(tokens[13]).to have_attributes(
964
+ :type => :RBRACK,
965
+ :value => ']',
966
+ :line => 1,
967
+ :column => 57
968
+ )
969
+ expect(tokens[14]).to have_attributes(
970
+ :type => :DQPOST,
971
+ :value => '',
972
+ :line => 1,
973
+ :column => 58
974
+ )
975
+ end
976
+
977
+ it 'can render the result back into a manifest' do
978
+ expect(manifest).to eq('"${facts["network_${iface}"]}/${facts["netmask_${iface}"]}"')
979
+ end
980
+ end
981
+
982
+ context 'interpolation with nested braces' do
983
+ let(:segments) do
984
+ [
985
+ [:STRING, ''],
986
+ [:INTERP, '$foo.map |$bar| { something($bar) }'],
987
+ [:STRING, ''],
988
+ ]
989
+ end
990
+
991
+ it 'creates a tokenised string' do
992
+ expect(tokens).to have(18).tokens
993
+
994
+ expect(tokens[0]).to have_attributes(
995
+ :type => :DQPRE,
996
+ :value => '',
997
+ :line => 1,
998
+ :column => 1
999
+ )
1000
+ expect(tokens[1]).to have_attributes(
1001
+ :type => :VARIABLE,
1002
+ :value => 'foo',
1003
+ :line => 1,
1004
+ :column => 4
1005
+ )
1006
+ expect(tokens[2]).to have_attributes(
1007
+ :type => :DOT,
1008
+ :value => '.',
1009
+ :line => 1,
1010
+ :column => 8
1011
+ )
1012
+ expect(tokens[3]).to have_attributes(
1013
+ :type => :NAME,
1014
+ :value => 'map',
1015
+ :line => 1,
1016
+ :column => 9
1017
+ )
1018
+ expect(tokens[4]).to have_attributes(
1019
+ :type => :WHITESPACE,
1020
+ :value => ' ',
1021
+ :line => 1,
1022
+ :column => 12
1023
+ )
1024
+ expect(tokens[5]).to have_attributes(
1025
+ :type => :PIPE,
1026
+ :value => '|',
1027
+ :line => 1,
1028
+ :column => 13
1029
+ )
1030
+ expect(tokens[6]).to have_attributes(
1031
+ :type => :VARIABLE,
1032
+ :value => 'bar',
1033
+ :line => 1,
1034
+ :column => 14
1035
+ )
1036
+ expect(tokens[7]).to have_attributes(
1037
+ :type => :PIPE,
1038
+ :value => '|',
1039
+ :line => 1,
1040
+ :column => 18
1041
+ )
1042
+ expect(tokens[8]).to have_attributes(
1043
+ :type => :WHITESPACE,
1044
+ :value => ' ',
1045
+ :line => 1,
1046
+ :column => 19
1047
+ )
1048
+ expect(tokens[9]).to have_attributes(
1049
+ :type => :LBRACE,
1050
+ :value => '{',
1051
+ :line => 1,
1052
+ :column => 20
1053
+ )
1054
+ expect(tokens[10]).to have_attributes(
1055
+ :type => :WHITESPACE,
1056
+ :value => ' ',
1057
+ :line => 1,
1058
+ :column => 21
1059
+ )
1060
+ expect(tokens[11]).to have_attributes(
1061
+ :type => :FUNCTION_NAME,
1062
+ :value => 'something',
1063
+ :line => 1,
1064
+ :column => 22
1065
+ )
1066
+ expect(tokens[12]).to have_attributes(
1067
+ :type => :LPAREN,
1068
+ :value => '(',
1069
+ :line => 1,
1070
+ :column => 31
1071
+ )
1072
+ expect(tokens[13]).to have_attributes(
1073
+ :type => :VARIABLE,
1074
+ :value => 'bar',
1075
+ :line => 1,
1076
+ :column => 32
1077
+ )
1078
+ expect(tokens[14]).to have_attributes(
1079
+ :type => :RPAREN,
1080
+ :value => ')',
1081
+ :line => 1,
1082
+ :column => 36
1083
+ )
1084
+ expect(tokens[15]).to have_attributes(
1085
+ :type => :WHITESPACE,
1086
+ :value => ' ',
1087
+ :line => 1,
1088
+ :column => 37
1089
+ )
1090
+ expect(tokens[16]).to have_attributes(
1091
+ :type => :RBRACE,
1092
+ :value => '}',
1093
+ :line => 1,
1094
+ :column => 38
1095
+ )
1096
+ expect(tokens[17]).to have_attributes(
1097
+ :type => :DQPOST,
1098
+ :value => '',
1099
+ :line => 1,
1100
+ :column => 39
1101
+ )
1102
+ end
1103
+
1104
+ it 'can render the result back into a manifest' do
1105
+ expect(manifest).to eq('"${$foo.map |$bar| { something($bar) }}"')
1106
+ end
71
1107
  end
72
1108
  end
73
1109
 
74
- context '#get_string_segment' do
75
- it 'should get a segment with a single terminator' do
76
- data = StringScanner.new('foo"bar')
77
- value, terminator = @lexer.get_string_segment(data, '"')
78
- expect(value).to eq('foo')
79
- expect(terminator).to eq('"')
80
- end
81
-
82
- it 'should get a segment with multiple terminators' do
83
- data = StringScanner.new('foo"bar$baz')
84
- value, terminator = @lexer.get_string_segment(data, "'$")
85
- expect(value).to eq('foo"bar')
86
- expect(terminator).to eq('$')
87
- end
88
-
89
- it 'should not get a segment with an escaped terminator' do
90
- data = StringScanner.new('foo"bar')
91
- value, terminator = @lexer.get_string_segment(data, '$')
92
- expect(value).to be_nil
93
- expect(terminator).to be_nil
94
- end
95
- end
96
-
97
- context '#interpolate_string' do
98
- it 'should handle a string with no variables' do
99
- @lexer.interpolate_string('foo bar baz"', 1, 1)
100
- token = @lexer.tokens.first
101
-
102
- expect(@lexer.tokens.length).to eq(1)
103
- expect(token.type).to eq(:STRING)
104
- expect(token.value).to eq('foo bar baz')
105
- expect(token.line).to eq(1)
106
- expect(token.column).to eq(1)
107
- end
108
-
109
- it 'should handle a string with a newline' do
110
- @lexer.interpolate_string(%(foo\nbar"), 1, 1)
111
- token = @lexer.tokens.first
112
-
113
- expect(@lexer.tokens.length).to eq(1)
114
- expect(token.type).to eq(:STRING)
115
- expect(token.value).to eq("foo\nbar")
116
- expect(token.line).to eq(1)
117
- expect(token.column).to eq(1)
118
- end
119
-
120
- it 'should handle a string with a single variable and suffix' do
121
- @lexer.interpolate_string('${foo}bar"', 1, 1)
122
- tokens = @lexer.tokens
123
-
124
- expect(tokens.length).to eq(3)
125
-
126
- expect(tokens[0].type).to eq(:DQPRE)
127
- expect(tokens[0].value).to eq('')
128
- expect(tokens[0].line).to eq(1)
129
- expect(tokens[0].column).to eq(1)
130
-
131
- expect(tokens[1].type).to eq(:VARIABLE)
132
- expect(tokens[1].value).to eq('foo')
133
- expect(tokens[1].line).to eq(1)
134
- expect(tokens[1].column).to eq(3)
135
-
136
- expect(tokens[2].type).to eq(:DQPOST)
137
- expect(tokens[2].value).to eq('bar')
138
- expect(tokens[2].line).to eq(1)
139
- expect(tokens[2].column).to eq(8)
140
- end
141
-
1110
+ context ':STRING / :DQ' do
142
1111
  it 'should handle a string with newline characters' do
143
1112
  # rubocop:disable Layout/TrailingWhitespace
144
1113
  manifest = <<END
@@ -147,14 +1116,14 @@ describe PuppetLint::Lexer do # rubocop:disable Metrics/BlockLength
147
1116
  command => "echo > /home/bar/.token;
148
1117
  kdestroy;
149
1118
  kinit ${pseudouser}@EXAMPLE.COM -kt ${keytab_path};
150
- test $(klist | egrep '^Default principal:' | sed 's/Default principal:\s//') = '${pseudouser}'@EXAMPLE.COM",
1119
+ test $(klist | egrep '^Default principal:' | sed 's/Default principal:\\s//') = '${pseudouser}'@EXAMPLE.COM",
151
1120
  refreshonly => true;
152
1121
  }
153
1122
  END
154
1123
  # rubocop:enable Layout/TrailingWhitespace
155
1124
  tokens = @lexer.tokenise(manifest)
156
1125
 
157
- expect(tokens.length).to eq(36)
1126
+ expect(tokens.length).to eq(34)
158
1127
 
159
1128
  expect(tokens[0].type).to eq(:WHITESPACE)
160
1129
  expect(tokens[0].value).to eq(' ')
@@ -219,530 +1188,79 @@ END
219
1188
  expect(tokens[15].type).to eq(:VARIABLE)
220
1189
  expect(tokens[15].value).to eq('pseudouser')
221
1190
  expect(tokens[15].line).to eq(5)
222
- expect(tokens[15].column).to eq(131)
1191
+ expect(tokens[15].column).to eq(41)
223
1192
  expect(tokens[16].type).to eq(:DQMID)
224
1193
  expect(tokens[16].value).to eq('@EXAMPLE.COM -kt ')
225
1194
  expect(tokens[16].line).to eq(5)
226
- expect(tokens[16].column).to eq(143)
1195
+ expect(tokens[16].column).to eq(51)
227
1196
  expect(tokens[17].type).to eq(:VARIABLE)
228
1197
  expect(tokens[17].value).to eq('keytab_path')
229
1198
  expect(tokens[17].line).to eq(5)
230
- expect(tokens[17].column).to eq(161)
1199
+ expect(tokens[17].column).to eq(71)
231
1200
  expect(tokens[18].type).to eq(:DQMID)
232
- expect(tokens[18].value).to eq("; \n test ")
1201
+ expect(tokens[18].value).to eq("; \n test $(klist | egrep '^Default principal:' | sed 's/Default principal:\\s//') = '")
233
1202
  expect(tokens[18].line).to eq(5)
234
- expect(tokens[18].column).to eq(174)
235
- expect(tokens[19].type).to eq(:DQMID)
236
- expect(tokens[19].value).to eq('$')
1203
+ expect(tokens[18].column).to eq(82)
1204
+ expect(tokens[19].type).to eq(:VARIABLE)
1205
+ expect(tokens[19].value).to eq('pseudouser')
237
1206
  expect(tokens[19].line).to eq(6)
238
- expect(tokens[19].column).to eq(213)
239
- expect(tokens[20].type).to eq(:DQMID)
240
- expect(tokens[20].value).to eq("(klist | egrep '^Default principal:' | sed 's/Default principal: //') = '")
1207
+ expect(tokens[19].column).to eq(115)
1208
+ expect(tokens[20].type).to eq(:DQPOST)
1209
+ expect(tokens[20].value).to eq("'@EXAMPLE.COM")
241
1210
  expect(tokens[20].line).to eq(6)
242
- expect(tokens[20].column).to eq(215)
243
- expect(tokens[21].type).to eq(:VARIABLE)
244
- expect(tokens[21].value).to eq('pseudouser')
1211
+ expect(tokens[20].column).to eq(125)
1212
+ expect(tokens[21].type).to eq(:COMMA)
1213
+ expect(tokens[21].value).to eq(',')
245
1214
  expect(tokens[21].line).to eq(6)
246
- expect(tokens[21].column).to eq(289)
247
- expect(tokens[22].type).to eq(:DQPOST)
248
- expect(tokens[22].value).to eq("'@EXAMPLE.COM")
1215
+ expect(tokens[21].column).to eq(140)
1216
+ expect(tokens[22].type).to eq(:NEWLINE)
1217
+ expect(tokens[22].value).to eq("\n")
249
1218
  expect(tokens[22].line).to eq(6)
250
- expect(tokens[22].column).to eq(301)
251
- expect(tokens[23].type).to eq(:COMMA)
252
- expect(tokens[23].value).to eq(',')
253
- expect(tokens[23].line).to eq(6)
254
- expect(tokens[23].column).to eq(315)
255
- expect(tokens[24].type).to eq(:NEWLINE)
256
- expect(tokens[24].value).to eq("\n")
257
- expect(tokens[24].line).to eq(6)
258
- expect(tokens[24].column).to eq(316)
259
- expect(tokens[25].type).to eq(:INDENT)
260
- expect(tokens[25].value).to eq(' ')
1219
+ expect(tokens[22].column).to eq(141)
1220
+ expect(tokens[23].type).to eq(:INDENT)
1221
+ expect(tokens[23].value).to eq(' ')
1222
+ expect(tokens[23].line).to eq(7)
1223
+ expect(tokens[23].column).to eq(1)
1224
+ expect(tokens[24].type).to eq(:NAME)
1225
+ expect(tokens[24].value).to eq('refreshonly')
1226
+ expect(tokens[24].line).to eq(7)
1227
+ expect(tokens[24].column).to eq(7)
1228
+ expect(tokens[25].type).to eq(:WHITESPACE)
1229
+ expect(tokens[25].value).to eq(' ')
261
1230
  expect(tokens[25].line).to eq(7)
262
- expect(tokens[25].column).to eq(1)
263
- expect(tokens[26].type).to eq(:NAME)
264
- expect(tokens[26].value).to eq('refreshonly')
1231
+ expect(tokens[25].column).to eq(18)
1232
+ expect(tokens[26].type).to eq(:FARROW)
1233
+ expect(tokens[26].value).to eq('=>')
265
1234
  expect(tokens[26].line).to eq(7)
266
- expect(tokens[26].column).to eq(7)
1235
+ expect(tokens[26].column).to eq(19)
267
1236
  expect(tokens[27].type).to eq(:WHITESPACE)
268
1237
  expect(tokens[27].value).to eq(' ')
269
1238
  expect(tokens[27].line).to eq(7)
270
- expect(tokens[27].column).to eq(18)
271
- expect(tokens[28].type).to eq(:FARROW)
272
- expect(tokens[28].value).to eq('=>')
1239
+ expect(tokens[27].column).to eq(21)
1240
+ expect(tokens[28].type).to eq(:TRUE)
1241
+ expect(tokens[28].value).to eq('true')
273
1242
  expect(tokens[28].line).to eq(7)
274
- expect(tokens[28].column).to eq(19)
275
- expect(tokens[29].type).to eq(:WHITESPACE)
276
- expect(tokens[29].value).to eq(' ')
1243
+ expect(tokens[28].column).to eq(22)
1244
+ expect(tokens[29].type).to eq(:SEMIC)
1245
+ expect(tokens[29].value).to eq(';')
277
1246
  expect(tokens[29].line).to eq(7)
278
- expect(tokens[29].column).to eq(21)
279
- expect(tokens[30].type).to eq(:TRUE)
280
- expect(tokens[30].value).to eq('true')
1247
+ expect(tokens[29].column).to eq(26)
1248
+ expect(tokens[30].type).to eq(:NEWLINE)
1249
+ expect(tokens[30].value).to eq("\n")
281
1250
  expect(tokens[30].line).to eq(7)
282
- expect(tokens[30].column).to eq(22)
283
- expect(tokens[31].type).to eq(:SEMIC)
284
- expect(tokens[31].value).to eq(';')
285
- expect(tokens[31].line).to eq(7)
286
- expect(tokens[31].column).to eq(26)
287
- expect(tokens[32].type).to eq(:NEWLINE)
288
- expect(tokens[32].value).to eq("\n")
289
- expect(tokens[32].line).to eq(7)
290
- expect(tokens[32].column).to eq(27)
291
- expect(tokens[33].type).to eq(:INDENT)
292
- expect(tokens[33].value).to eq(' ')
1251
+ expect(tokens[30].column).to eq(27)
1252
+ expect(tokens[31].type).to eq(:INDENT)
1253
+ expect(tokens[31].value).to eq(' ')
1254
+ expect(tokens[31].line).to eq(8)
1255
+ expect(tokens[31].column).to eq(1)
1256
+ expect(tokens[32].type).to eq(:RBRACE)
1257
+ expect(tokens[32].value).to eq('}')
1258
+ expect(tokens[32].line).to eq(8)
1259
+ expect(tokens[32].column).to eq(3)
1260
+ expect(tokens[33].type).to eq(:NEWLINE)
1261
+ expect(tokens[33].value).to eq("\n")
293
1262
  expect(tokens[33].line).to eq(8)
294
- expect(tokens[33].column).to eq(1)
295
- expect(tokens[34].type).to eq(:RBRACE)
296
- expect(tokens[34].value).to eq('}')
297
- expect(tokens[34].line).to eq(8)
298
- expect(tokens[34].column).to eq(3)
299
- expect(tokens[35].type).to eq(:NEWLINE)
300
- expect(tokens[35].value).to eq("\n")
301
- expect(tokens[35].line).to eq(8)
302
- expect(tokens[35].column).to eq(4)
303
- end
304
-
305
- it 'should handle a string with a single variable and newline characters' do
306
- manifest = <<-END
307
- foo
308
- /bin/${foo} >>
309
- /bar/baz"
310
- END
311
- @lexer.interpolate_string(manifest, 1, 1)
312
- tokens = @lexer.tokens
313
-
314
- expect(tokens.length).to eq(3)
315
-
316
- expect(tokens[0].type).to eq(:DQPRE)
317
- expect(tokens[0].value).to eq(" foo\n /bin/")
318
- expect(tokens[0].line).to eq(1)
319
- expect(tokens[0].column).to eq(1)
320
-
321
- expect(tokens[1].type).to eq(:VARIABLE)
322
- expect(tokens[1].value).to eq('foo')
323
- expect(tokens[1].line).to eq(2)
324
- expect(tokens[1].column).to eq(20)
325
-
326
- expect(tokens[2].type).to eq(:DQPOST)
327
- expect(tokens[2].value).to eq(" >>\n /bar/baz")
328
- expect(tokens[2].line).to eq(2)
329
- expect(tokens[2].column).to eq(25)
330
- end
331
-
332
- it 'should handle a string with a single variable and surrounding text' do
333
- @lexer.interpolate_string('foo${bar}baz"', 1, 1)
334
- tokens = @lexer.tokens
335
-
336
- expect(tokens.length).to eq(3)
337
-
338
- expect(tokens[0].type).to eq(:DQPRE)
339
- expect(tokens[0].value).to eq('foo')
340
- expect(tokens[0].line).to eq(1)
341
- expect(tokens[0].column).to eq(1)
342
-
343
- expect(tokens[1].type).to eq(:VARIABLE)
344
- expect(tokens[1].value).to eq('bar')
345
- expect(tokens[1].line).to eq(1)
346
- expect(tokens[1].column).to eq(6)
347
-
348
- expect(tokens[2].type).to eq(:DQPOST)
349
- expect(tokens[2].value).to eq('baz')
350
- expect(tokens[2].line).to eq(1)
351
- expect(tokens[2].column).to eq(11)
352
- end
353
-
354
- it 'should handle a string with multiple variables and surrounding text' do
355
- @lexer.interpolate_string('foo${bar}baz${gronk}meh"', 1, 1)
356
- tokens = @lexer.tokens
357
-
358
- expect(tokens.length).to eq(5)
359
-
360
- expect(tokens[0].type).to eq(:DQPRE)
361
- expect(tokens[0].value).to eq('foo')
362
- expect(tokens[0].line).to eq(1)
363
- expect(tokens[0].column).to eq(1)
364
-
365
- expect(tokens[1].type).to eq(:VARIABLE)
366
- expect(tokens[1].value).to eq('bar')
367
- expect(tokens[1].line).to eq(1)
368
- expect(tokens[1].column).to eq(6)
369
-
370
- expect(tokens[2].type).to eq(:DQMID)
371
- expect(tokens[2].value).to eq('baz')
372
- expect(tokens[2].line).to eq(1)
373
- expect(tokens[2].column).to eq(11)
374
-
375
- expect(tokens[3].type).to eq(:VARIABLE)
376
- expect(tokens[3].value).to eq('gronk')
377
- expect(tokens[3].line).to eq(1)
378
- expect(tokens[3].column).to eq(15)
379
-
380
- expect(tokens[4].type).to eq(:DQPOST)
381
- expect(tokens[4].value).to eq('meh')
382
- expect(tokens[4].line).to eq(1)
383
- expect(tokens[4].column).to eq(22)
384
- end
385
-
386
- it 'should handle a string with only a single variable' do
387
- @lexer.interpolate_string('${bar}"', 1, 1)
388
- tokens = @lexer.tokens
389
-
390
- expect(tokens.length).to eq(3)
391
-
392
- expect(tokens[0].type).to eq(:DQPRE)
393
- expect(tokens[0].value).to eq('')
394
- expect(tokens[0].line).to eq(1)
395
- expect(tokens[0].column).to eq(1)
396
-
397
- expect(tokens[1].type).to eq(:VARIABLE)
398
- expect(tokens[1].value).to eq('bar')
399
- expect(tokens[1].line).to eq(1)
400
- expect(tokens[1].column).to eq(3)
401
- expect(tokens[1].to_manifest).to eq('bar')
402
-
403
- expect(tokens[2].type).to eq(:DQPOST)
404
- expect(tokens[2].value).to eq('')
405
- expect(tokens[2].line).to eq(1)
406
- expect(tokens[2].column).to eq(8)
407
-
408
- expect(tokens.map(&:to_manifest).join('')).to eq('"${bar}"')
409
- end
410
-
411
- it 'should not remove the unnecessary $ from enclosed variables' do
412
- tokens = @lexer.tokenise('"${$bar}"')
413
-
414
- expect(tokens.length).to eq(3)
415
-
416
- expect(tokens[0].type).to eq(:DQPRE)
417
- expect(tokens[0].value).to eq('')
418
- expect(tokens[0].line).to eq(1)
419
- expect(tokens[0].column).to eq(1)
420
-
421
- expect(tokens[1].type).to eq(:VARIABLE)
422
- expect(tokens[1].value).to eq('bar')
423
- expect(tokens[1].raw).to eq('$bar')
424
- expect(tokens[1].line).to eq(1)
425
- expect(tokens[1].column).to eq(4)
426
- expect(tokens[1].to_manifest).to eq('$bar')
427
-
428
- expect(tokens[2].type).to eq(:DQPOST)
429
- expect(tokens[2].value).to eq('')
430
- expect(tokens[2].line).to eq(1)
431
- expect(tokens[2].column).to eq(9)
432
-
433
- expect(tokens.map(&:to_manifest).join('')).to eq('"${$bar}"')
434
- end
435
-
436
- it 'should handle a variable with an array reference' do
437
- @lexer.interpolate_string('${foo[bar][baz]}"', 1, 1)
438
- tokens = @lexer.tokens
439
-
440
- expect(tokens.length).to eq(3)
441
-
442
- expect(tokens[0].type).to eq(:DQPRE)
443
- expect(tokens[0].value).to eq('')
444
- expect(tokens[0].line).to eq(1)
445
- expect(tokens[0].column).to eq(1)
446
-
447
- expect(tokens[1].type).to eq(:VARIABLE)
448
- expect(tokens[1].value).to eq('foo[bar][baz]')
449
- expect(tokens[1].line).to eq(1)
450
- expect(tokens[1].column).to eq(3)
451
-
452
- expect(tokens[2].type).to eq(:DQPOST)
453
- expect(tokens[2].value).to eq('')
454
- expect(tokens[2].line).to eq(1)
455
- expect(tokens[2].column).to eq(18)
456
- end
457
-
458
- it 'should handle a string with only many variables' do
459
- @lexer.interpolate_string('${bar}${gronk}"', 1, 1)
460
- tokens = @lexer.tokens
461
-
462
- expect(tokens.length).to eq(5)
463
-
464
- expect(tokens[0].type).to eq(:DQPRE)
465
- expect(tokens[0].value).to eq('')
466
- expect(tokens[0].line).to eq(1)
467
- expect(tokens[0].column).to eq(1)
468
-
469
- expect(tokens[1].type).to eq(:VARIABLE)
470
- expect(tokens[1].value).to eq('bar')
471
- expect(tokens[1].line).to eq(1)
472
- expect(tokens[1].column).to eq(3)
473
-
474
- expect(tokens[2].type).to eq(:DQMID)
475
- expect(tokens[2].value).to eq('')
476
- expect(tokens[2].line).to eq(1)
477
- expect(tokens[2].column).to eq(8)
478
-
479
- expect(tokens[3].type).to eq(:VARIABLE)
480
- expect(tokens[3].value).to eq('gronk')
481
- expect(tokens[3].line).to eq(1)
482
- expect(tokens[3].column).to eq(9)
483
-
484
- expect(tokens[4].type).to eq(:DQPOST)
485
- expect(tokens[4].value).to eq('')
486
- expect(tokens[4].line).to eq(1)
487
- expect(tokens[4].column).to eq(16)
488
- end
489
-
490
- it 'should handle a string with only an unenclosed variable' do
491
- @lexer.interpolate_string('$foo"', 1, 1)
492
- tokens = @lexer.tokens
493
-
494
- expect(tokens.length).to eq(3)
495
-
496
- expect(tokens[0].type).to eq(:DQPRE)
497
- expect(tokens[0].value).to eq('')
498
- expect(tokens[0].line).to eq(1)
499
- expect(tokens[0].column).to eq(1)
500
-
501
- expect(tokens[1].type).to eq(:UNENC_VARIABLE)
502
- expect(tokens[1].value).to eq('foo')
503
- expect(tokens[1].line).to eq(1)
504
- expect(tokens[1].column).to eq(2)
505
-
506
- expect(tokens[2].type).to eq(:DQPOST)
507
- expect(tokens[2].value).to eq('')
508
- expect(tokens[2].line).to eq(1)
509
- expect(tokens[2].column).to eq(6)
510
- end
511
-
512
- it 'should handle a string with a nested string inside it' do
513
- @lexer.interpolate_string(%q(string with ${'a nested single quoted string'} inside it"), 1, 1)
514
- tokens = @lexer.tokens
515
-
516
- expect(tokens.length).to eq(3)
517
-
518
- expect(tokens[0].type).to eq(:DQPRE)
519
- expect(tokens[0].value).to eq('string with ')
520
- expect(tokens[0].line).to eq(1)
521
- expect(tokens[0].column).to eq(1)
522
-
523
- expect(tokens[1].type).to eq(:SSTRING)
524
- expect(tokens[1].value).to eq('a nested single quoted string')
525
- expect(tokens[1].line).to eq(1)
526
- expect(tokens[1].column).to eq(16)
527
-
528
- expect(tokens[2].type).to eq(:DQPOST)
529
- expect(tokens[2].value).to eq(' inside it')
530
- expect(tokens[2].line).to eq(1)
531
- expect(tokens[2].column).to eq(48)
532
- end
533
-
534
- it 'should handle a string with nested math' do
535
- @lexer.interpolate_string('string with ${(3+5)/4} nested math"', 1, 1)
536
- tokens = @lexer.tokens
537
-
538
- expect(tokens.length).to eq(9)
539
-
540
- expect(tokens[0].type).to eq(:DQPRE)
541
- expect(tokens[0].value).to eq('string with ')
542
- expect(tokens[0].line).to eq(1)
543
- expect(tokens[0].column).to eq(1)
544
-
545
- expect(tokens[1].type).to eq(:LPAREN)
546
- expect(tokens[1].line).to eq(1)
547
- expect(tokens[1].column).to eq(16)
548
-
549
- expect(tokens[2].type).to eq(:NUMBER)
550
- expect(tokens[2].value).to eq('3')
551
- expect(tokens[2].line).to eq(1)
552
- expect(tokens[2].column).to eq(17)
553
-
554
- expect(tokens[3].type).to eq(:PLUS)
555
- expect(tokens[3].line).to eq(1)
556
- expect(tokens[3].column).to eq(18)
557
-
558
- expect(tokens[4].type).to eq(:NUMBER)
559
- expect(tokens[4].value).to eq('5')
560
- expect(tokens[4].line).to eq(1)
561
- expect(tokens[4].column).to eq(19)
562
-
563
- expect(tokens[5].type).to eq(:RPAREN)
564
- expect(tokens[5].line).to eq(1)
565
- expect(tokens[5].column).to eq(20)
566
-
567
- expect(tokens[6].type).to eq(:DIV)
568
- expect(tokens[6].line).to eq(1)
569
- expect(tokens[6].column).to eq(21)
570
-
571
- expect(tokens[7].type).to eq(:NUMBER)
572
- expect(tokens[7].value).to eq('4')
573
- expect(tokens[7].line).to eq(1)
574
- expect(tokens[7].column).to eq(22)
575
-
576
- expect(tokens[8].type).to eq(:DQPOST)
577
- expect(tokens[8].value).to eq(' nested math')
578
- expect(tokens[8].line).to eq(1)
579
- expect(tokens[8].column).to eq(24)
580
- end
581
-
582
- it 'should handle a string with a nested array' do
583
- @lexer.interpolate_string(%q(string with ${['an array ', $v2]} in it"), 1, 1)
584
- tokens = @lexer.tokens
585
-
586
- expect(tokens.length).to eq(8)
587
-
588
- expect(tokens[0].type).to eq(:DQPRE)
589
- expect(tokens[0].value).to eq('string with ')
590
- expect(tokens[0].line).to eq(1)
591
- expect(tokens[0].column).to eq(1)
592
-
593
- expect(tokens[1].type).to eq(:LBRACK)
594
- expect(tokens[1].line).to eq(1)
595
- expect(tokens[1].column).to eq(16)
596
-
597
- expect(tokens[2].type).to eq(:SSTRING)
598
- expect(tokens[2].value).to eq('an array ')
599
- expect(tokens[2].line).to eq(1)
600
- expect(tokens[2].column).to eq(17)
601
-
602
- expect(tokens[3].type).to eq(:COMMA)
603
- expect(tokens[3].line).to eq(1)
604
- expect(tokens[3].column).to eq(28)
605
-
606
- expect(tokens[4].type).to eq(:WHITESPACE)
607
- expect(tokens[4].value).to eq(' ')
608
- expect(tokens[4].line).to eq(1)
609
- expect(tokens[4].column).to eq(29)
610
-
611
- expect(tokens[5].type).to eq(:VARIABLE)
612
- expect(tokens[5].value).to eq('v2')
613
- expect(tokens[5].line).to eq(1)
614
- expect(tokens[5].column).to eq(30)
615
-
616
- expect(tokens[6].type).to eq(:RBRACK)
617
- expect(tokens[6].line).to eq(1)
618
- expect(tokens[6].column).to eq(33)
619
-
620
- expect(tokens[7].type).to eq(:DQPOST)
621
- expect(tokens[7].value).to eq(' in it')
622
- expect(tokens[7].line).to eq(1)
623
- expect(tokens[7].column).to eq(35)
624
- end
625
-
626
- it 'should handle a string of $s' do
627
- @lexer.interpolate_string('$$$$"', 1, 1)
628
- tokens = @lexer.tokens
629
-
630
- expect(tokens.length).to eq(1)
631
-
632
- expect(tokens[0].type).to eq(:STRING)
633
- expect(tokens[0].value).to eq('$$$$')
634
- expect(tokens[0].line).to eq(1)
635
- expect(tokens[0].column).to eq(1)
636
- end
637
-
638
- it 'should handle "$foo$bar"' do
639
- @lexer.interpolate_string('$foo$bar"', 1, 1)
640
- tokens = @lexer.tokens
641
-
642
- expect(tokens.length).to eq(5)
643
-
644
- expect(tokens[0].type).to eq(:DQPRE)
645
- expect(tokens[0].value).to eq('')
646
- expect(tokens[0].line).to eq(1)
647
- expect(tokens[0].column).to eq(1)
648
-
649
- expect(tokens[1].type).to eq(:UNENC_VARIABLE)
650
- expect(tokens[1].value).to eq('foo')
651
- expect(tokens[1].line).to eq(1)
652
- expect(tokens[1].column).to eq(2)
653
-
654
- expect(tokens[2].type).to eq(:DQMID)
655
- expect(tokens[2].value).to eq('')
656
- expect(tokens[2].line).to eq(1)
657
- expect(tokens[2].column).to eq(6)
658
-
659
- expect(tokens[3].type).to eq(:UNENC_VARIABLE)
660
- expect(tokens[3].value).to eq('bar')
661
- expect(tokens[3].line).to eq(1)
662
- expect(tokens[3].column).to eq(6)
663
-
664
- expect(tokens[4].type).to eq(:DQPOST)
665
- expect(tokens[4].value).to eq('')
666
- expect(tokens[4].line).to eq(1)
667
- expect(tokens[4].column).to eq(10)
668
- end
669
-
670
- it 'should handle "foo$bar$"' do
671
- @lexer.interpolate_string('foo$bar$"', 1, 1)
672
- tokens = @lexer.tokens
673
-
674
- expect(tokens.length).to eq(3)
675
-
676
- expect(tokens[0].type).to eq(:DQPRE)
677
- expect(tokens[0].value).to eq('foo')
678
- expect(tokens[0].line).to eq(1)
679
- expect(tokens[0].column).to eq(1)
680
-
681
- expect(tokens[1].type).to eq(:UNENC_VARIABLE)
682
- expect(tokens[1].value).to eq('bar')
683
- expect(tokens[1].line).to eq(1)
684
- expect(tokens[1].column).to eq(5)
685
-
686
- expect(tokens[2].type).to eq(:DQPOST)
687
- expect(tokens[2].value).to eq('$')
688
- expect(tokens[2].line).to eq(1)
689
- expect(tokens[2].column).to eq(9)
690
- end
691
-
692
- it 'should handle "foo$$bar"' do
693
- @lexer.interpolate_string('foo$$bar"', 1, 1)
694
- tokens = @lexer.tokens
695
-
696
- expect(tokens.length).to eq(3)
697
-
698
- expect(tokens[0].type).to eq(:DQPRE)
699
- expect(tokens[0].value).to eq('foo$')
700
- expect(tokens[0].line).to eq(1)
701
- expect(tokens[0].column).to eq(1)
702
-
703
- expect(tokens[1].type).to eq(:UNENC_VARIABLE)
704
- expect(tokens[1].value).to eq('bar')
705
- expect(tokens[1].line).to eq(1)
706
- expect(tokens[1].column).to eq(6)
707
-
708
- expect(tokens[2].type).to eq(:DQPOST)
709
- expect(tokens[2].value).to eq('')
710
- expect(tokens[2].line).to eq(1)
711
- expect(tokens[2].column).to eq(10)
712
- end
713
-
714
- it 'should handle an empty string' do
715
- @lexer.interpolate_string('"', 1, 1)
716
- tokens = @lexer.tokens
717
-
718
- expect(tokens.length).to eq(1)
719
-
720
- expect(tokens[0].type).to eq(:STRING)
721
- expect(tokens[0].value).to eq('')
722
- expect(tokens[0].line).to eq(1)
723
- expect(tokens[0].column).to eq(1)
724
- end
725
-
726
- it 'should handle "$foo::::bar"' do
727
- @lexer.interpolate_string('$foo::::bar"', 1, 1)
728
- tokens = @lexer.tokens
729
-
730
- expect(tokens.length).to eq(3)
731
-
732
- expect(tokens[0].type).to eq(:DQPRE)
733
- expect(tokens[0].value).to eq('')
734
- expect(tokens[0].line).to eq(1)
735
- expect(tokens[0].column).to eq(1)
736
-
737
- expect(tokens[1].type).to eq(:UNENC_VARIABLE)
738
- expect(tokens[1].value).to eq('foo')
739
- expect(tokens[1].line).to eq(1)
740
- expect(tokens[1].column).to eq(2)
741
-
742
- expect(tokens[2].type).to eq(:DQPOST)
743
- expect(tokens[2].value).to eq('::::bar')
744
- expect(tokens[2].line).to eq(1)
745
- expect(tokens[2].column).to eq(6)
1263
+ expect(tokens[33].column).to eq(4)
746
1264
  end
747
1265
 
748
1266
  it 'should calculate the column number correctly after an enclosed variable' do
@@ -875,10 +1393,17 @@ END
875
1393
  expect(token.value).to eq('Collection')
876
1394
  end
877
1395
 
878
- it 'should match Platform Types' do
879
- token = @lexer.tokenise('Callable').first
880
- expect(token.type).to eq(:TYPE)
881
- expect(token.value).to eq('Callable')
1396
+ describe 'Platform Types' do
1397
+ it 'should match Callable' do
1398
+ token = @lexer.tokenise('Callable').first
1399
+ expect(token.type).to eq(:TYPE)
1400
+ expect(token.value).to eq('Callable')
1401
+ end
1402
+ it 'should match Sensitive' do
1403
+ token = @lexer.tokenise('Sensitive').first
1404
+ expect(token.type).to eq(:TYPE)
1405
+ expect(token.value).to eq('Sensitive')
1406
+ end
882
1407
  end
883
1408
  end
884
1409
 
@@ -1012,7 +1537,7 @@ END
1012
1537
  expect(tokens[6].type).to eq(:DQMID)
1013
1538
  expect(tokens[6].value).to eq(' ')
1014
1539
  expect(tokens[6].line).to eq(1)
1015
- expect(tokens[6].column).to eq(19)
1540
+ expect(tokens[6].column).to eq(18)
1016
1541
  expect(tokens[7].type).to eq(:HEREDOC_OPEN)
1017
1542
  expect(tokens[7].value).to eq('end2')
1018
1543
  expect(tokens[7].line).to eq(1)
@@ -1020,7 +1545,7 @@ END
1020
1545
  expect(tokens[8].type).to eq(:DQPOST)
1021
1546
  expect(tokens[8].value).to eq('')
1022
1547
  expect(tokens[8].line).to eq(1)
1023
- expect(tokens[8].column).to eq(30)
1548
+ expect(tokens[8].column).to eq(29)
1024
1549
  expect(tokens[9].type).to eq(:NEWLINE)
1025
1550
  expect(tokens[9].value).to eq("\n")
1026
1551
  expect(tokens[9].line).to eq(1)
@@ -1091,6 +1616,36 @@ END
1091
1616
  expect(tokens[7].line).to eq(5)
1092
1617
  expect(tokens[7].column).to eq(8)
1093
1618
  end
1619
+
1620
+ it 'should handle a heredoc with spaces in the tag' do
1621
+ manifest = <<-END.gsub(%r{^ {6}}, '')
1622
+ $str = @("myheredoc" /)
1623
+ foo
1624
+ |-myheredoc
1625
+ END
1626
+ tokens = @lexer.tokenise(manifest)
1627
+ expect(tokens.length).to eq(8)
1628
+
1629
+ expect(tokens[4].type).to eq(:HEREDOC_OPEN)
1630
+ expect(tokens[4].value).to eq('"myheredoc" /')
1631
+ expect(tokens[6].type).to eq(:HEREDOC)
1632
+ expect(tokens[6].value).to eq(" foo\n ")
1633
+ end
1634
+
1635
+ it 'should handle a heredoc with no indentation' do
1636
+ manifest = <<-END.gsub(%r{^ {6}}, '')
1637
+ $str = @(EOT)
1638
+ something
1639
+ EOT
1640
+ END
1641
+ tokens = @lexer.tokenise(manifest)
1642
+
1643
+ expect(tokens.length).to eq(8)
1644
+ expect(tokens[4].type).to eq(:HEREDOC_OPEN)
1645
+ expect(tokens[4].value).to eq('EOT')
1646
+ expect(tokens[6].type).to eq(:HEREDOC)
1647
+ expect(tokens[6].value).to eq('something')
1648
+ end
1094
1649
  end
1095
1650
 
1096
1651
  context ':HEREDOC with interpolation' do
@@ -1143,7 +1698,7 @@ END
1143
1698
  manifest = <<-END.gsub(%r{^ {6}}, '')
1144
1699
  $str = @("myheredoc"/)
1145
1700
  SOMETHING
1146
- ${else}
1701
+ ${here}
1147
1702
  AND :
1148
1703
  $another
1149
1704
  THING
@@ -1182,10 +1737,10 @@ END
1182
1737
  expect(tokens[6].line).to eq(2)
1183
1738
  expect(tokens[6].column).to eq(1)
1184
1739
  expect(tokens[7].type).to eq(:VARIABLE)
1185
- expect(tokens[7].value).to eq('else')
1740
+ expect(tokens[7].value).to eq('here')
1186
1741
  expect(tokens[7].line).to eq(3)
1187
1742
  expect(tokens[7].column).to eq(5)
1188
- expect(tokens[7].to_manifest).to eq('else')
1743
+ expect(tokens[7].to_manifest).to eq('here')
1189
1744
  expect(tokens[8].type).to eq(:HEREDOC_MID)
1190
1745
  expect(tokens[8].value).to eq("\n AND :\n ")
1191
1746
  expect(tokens[8].line).to eq(3)
@@ -1453,9 +2008,10 @@ END
1453
2008
  expect(token.value).to eq('this is a regex')
1454
2009
  end
1455
2010
 
1456
- it 'should not match if there is \n in the regex' do
1457
- token = @lexer.tokenise("/this is \n a regex/").first
1458
- expect(token.type).to_not eq(:REGEX)
2011
+ it 'should match even if there is \n in the regex' do
2012
+ token = @lexer.tokenise("/this is a regex,\ntoo/").first
2013
+ expect(token.type).to eq(:REGEX)
2014
+ expect(token.value).to eq("this is a regex,\ntoo")
1459
2015
  end
1460
2016
 
1461
2017
  it 'should not consider \/ to be the end of the regex' do
@@ -1497,6 +2053,13 @@ END
1497
2053
  expect(tokens[8].type).to eq(:REGEX)
1498
2054
  expect(tokens[8].value).to eq('([\w\.]+(:\d+)?(\/\w+)?)(:(\w+))?')
1499
2055
  end
2056
+
2057
+ it 'should discriminate between division and regexes' do
2058
+ tokens = @lexer.tokenise('if $a/10==0 or $b=~/{}/')
2059
+ expect(tokens[3].type).to eq(:DIV)
2060
+ expect(tokens[12].type).to eq(:REGEX)
2061
+ expect(tokens[12].value).to eq('{}')
2062
+ end
1500
2063
  end
1501
2064
 
1502
2065
  context ':STRING' do