puppet-lint 2.3.6 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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