puppet-lint 2.3.6 → 2.4.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.
@@ -63,82 +63,1010 @@ 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 'an interpolated variable with an unnecessary $' do
257
+ let(:segments) do
258
+ [
259
+ [:STRING, ''],
260
+ [:INTERP, '$bar'],
261
+ [:STRING, ''],
262
+ ]
263
+ end
264
+
265
+ it 'creates a tokenised string' do
266
+ expect(tokens).to have(3).tokens
267
+
268
+ expect(tokens[0]).to have_attributes(
269
+ :type => :DQPRE,
270
+ :value => '',
271
+ :line => 1,
272
+ :column => 1
273
+ )
274
+ expect(tokens[1]).to have_attributes(
275
+ :type => :VARIABLE,
276
+ :value => 'bar',
277
+ :line => 1,
278
+ :column => 4
279
+ )
280
+ expect(tokens[2]).to have_attributes(
281
+ :type => :DQPOST,
282
+ :value => '',
283
+ :line => 1,
284
+ :column => 8
285
+ )
286
+ end
287
+
288
+ it 'includes the extra $ in the rendered manifest' do
289
+ expect(manifest).to eq('"${$bar}"')
290
+ end
291
+ end
292
+
293
+ context 'an interpolated variable with an array reference' do
294
+ let(:segments) do
295
+ [
296
+ [:STRING, ''],
297
+ [:INTERP, 'foo[bar][baz]'],
298
+ [:STRING, ''],
299
+ ]
300
+ end
301
+
302
+ it 'creates a tokenised string' do
303
+ expect(tokens).to have(9).tokens
304
+
305
+ expect(tokens[0]).to have_attributes(
306
+ :type => :DQPRE,
307
+ :value => '',
308
+ :line => 1,
309
+ :column => 1
310
+ )
311
+ expect(tokens[1]).to have_attributes(
312
+ :type => :VARIABLE,
313
+ :value => 'foo',
314
+ :line => 1,
315
+ :column => 4
316
+ )
317
+ expect(tokens[2]).to have_attributes(
318
+ :type => :LBRACK,
319
+ :value => '[',
320
+ :line => 1,
321
+ :column => 7
322
+ )
323
+ expect(tokens[3]).to have_attributes(
324
+ :type => :NAME,
325
+ :value => 'bar',
326
+ :line => 1,
327
+ :column => 8
328
+ )
329
+ expect(tokens[4]).to have_attributes(
330
+ :type => :RBRACK,
331
+ :value => ']',
332
+ :line => 1,
333
+ :column => 11
334
+ )
335
+ expect(tokens[5]).to have_attributes(
336
+ :type => :LBRACK,
337
+ :value => '[',
338
+ :line => 1,
339
+ :column => 12
340
+ )
341
+ expect(tokens[6]).to have_attributes(
342
+ :type => :NAME,
343
+ :value => 'baz',
344
+ :line => 1,
345
+ :column => 13
346
+ )
347
+ expect(tokens[7]).to have_attributes(
348
+ :type => :RBRACK,
349
+ :value => ']',
350
+ :line => 1,
351
+ :column => 16
352
+ )
353
+ expect(tokens[8]).to have_attributes(
354
+ :type => :DQPOST,
355
+ :value => '',
356
+ :line => 1,
357
+ :column => 17
358
+ )
359
+ end
360
+
361
+ it 'can render the result back into a manifest' do
362
+ expect(manifest).to eq('"${foo[bar][baz]}"')
363
+ end
364
+ end
365
+
366
+ context 'multiple interpreted variables' do
367
+ let(:segments) do
368
+ [
369
+ [:STRING, ''],
370
+ [:INTERP, 'foo'],
371
+ [:STRING, ''],
372
+ [:INTERP, 'bar'],
373
+ [:STRING, ''],
374
+ ]
375
+ end
376
+
377
+ it 'creates a tokenised string' do
378
+ expect(tokens).to have(5).tokens
379
+
380
+ expect(tokens[0]).to have_attributes(
381
+ :type => :DQPRE,
382
+ :value => '',
383
+ :line => 1,
384
+ :column => 1
385
+ )
386
+ expect(tokens[1]).to have_attributes(
387
+ :type => :VARIABLE,
388
+ :value => 'foo',
389
+ :line => 1,
390
+ :column => 4
391
+ )
392
+ expect(tokens[2]).to have_attributes(
393
+ :type => :DQMID,
394
+ :value => '',
395
+ :line => 1,
396
+ :column => 7
397
+ )
398
+ expect(tokens[3]).to have_attributes(
399
+ :type => :VARIABLE,
400
+ :value => 'bar',
401
+ :line => 1,
402
+ :column => 10
403
+ )
404
+ expect(tokens[4]).to have_attributes(
405
+ :type => :DQPOST,
406
+ :value => '',
407
+ :line => 1,
408
+ :column => 13
409
+ )
410
+ end
411
+
412
+ it 'can render the result back into a manifest' do
413
+ expect(manifest).to eq('"${foo}${bar}"')
414
+ end
415
+ end
416
+
417
+ context 'an unenclosed variable' do
418
+ let(:segments) do
419
+ [
420
+ [:STRING, ''],
421
+ [:UNENC_VAR, '$foo'],
422
+ [:STRING, ''],
423
+ ]
424
+ end
425
+
426
+ it 'creates a tokenised string' do
427
+ expect(tokens).to have(3).tokens
428
+
429
+ expect(tokens[0]).to have_attributes(
430
+ :type => :DQPRE,
431
+ :value => '',
432
+ :line => 1,
433
+ :column => 1
434
+ )
435
+ expect(tokens[1]).to have_attributes(
436
+ :type => :UNENC_VARIABLE,
437
+ :value => 'foo',
438
+ :line => 1,
439
+ :column => 2
440
+ )
441
+ expect(tokens[2]).to have_attributes(
442
+ :type => :DQPOST,
443
+ :value => '',
444
+ :line => 1,
445
+ :column => 6
446
+ )
447
+ end
448
+
449
+ it 'can render the result back into a manifest' do
450
+ expect(manifest).to eq('"$foo"')
451
+ end
452
+ end
453
+
454
+ context 'an interpolation with a nested single quote string' do
455
+ let(:segments) do
456
+ [
457
+ [:STRING, 'string with '],
458
+ [:INTERP, "'a nested single quoted string'"],
459
+ [:STRING, ' inside it'],
460
+ ]
461
+ end
462
+
463
+ it 'creates a tokenised string' do
464
+ expect(tokens).to have(3).tokens
465
+
466
+ expect(tokens[0]).to have_attributes(
467
+ :type => :DQPRE,
468
+ :value => 'string with ',
469
+ :line => 1,
470
+ :column => 1
471
+ )
472
+ expect(tokens[1]).to have_attributes(
473
+ :type => :SSTRING,
474
+ :value => 'a nested single quoted string',
475
+ :line => 1,
476
+ :column => 16
477
+ )
478
+ expect(tokens[2]).to have_attributes(
479
+ :type => :DQPOST,
480
+ :value => ' inside it',
481
+ :line => 1,
482
+ :column => 47
483
+ )
484
+ end
485
+
486
+ it 'can render the result back into a manifest' do
487
+ expect(manifest).to eq(%("string with ${'a nested single quoted string'} inside it"))
488
+ end
489
+ end
490
+
491
+ context 'an interpolation with a nested math expression' do
492
+ let(:segments) do
493
+ [
494
+ [:STRING, 'string with '],
495
+ [:INTERP, '(3+5)/4'],
496
+ [:STRING, ' nested math'],
497
+ ]
498
+ end
499
+
500
+ it 'creates a tokenised string' do
501
+ expect(tokens).to have(9).tokens
502
+
503
+ expect(tokens[0]).to have_attributes(
504
+ :type => :DQPRE,
505
+ :value => 'string with ',
506
+ :line => 1,
507
+ :column => 1
508
+ )
509
+ expect(tokens[1]).to have_attributes(
510
+ :type => :LPAREN,
511
+ :value => '(',
512
+ :line => 1,
513
+ :column => 16
514
+ )
515
+ expect(tokens[2]).to have_attributes(
516
+ :type => :NUMBER,
517
+ :value => '3',
518
+ :line => 1,
519
+ :column => 17
520
+ )
521
+ expect(tokens[3]).to have_attributes(
522
+ :type => :PLUS,
523
+ :value => '+',
524
+ :line => 1,
525
+ :column => 18
526
+ )
527
+ expect(tokens[4]).to have_attributes(
528
+ :type => :NUMBER,
529
+ :value => '5',
530
+ :line => 1,
531
+ :column => 19
532
+ )
533
+ expect(tokens[5]).to have_attributes(
534
+ :type => :RPAREN,
535
+ :value => ')',
536
+ :line => 1,
537
+ :column => 20
538
+ )
539
+ expect(tokens[6]).to have_attributes(
540
+ :type => :DIV,
541
+ :value => '/',
542
+ :line => 1,
543
+ :column => 21
544
+ )
545
+ expect(tokens[7]).to have_attributes(
546
+ :type => :NUMBER,
547
+ :value => '4',
548
+ :line => 1,
549
+ :column => 22
550
+ )
551
+ expect(tokens[8]).to have_attributes(
552
+ :type => :DQPOST,
553
+ :value => ' nested math',
554
+ :line => 1,
555
+ :column => 23
556
+ )
557
+ end
558
+
559
+ it 'can render the result back into a manifest' do
560
+ expect(manifest).to eq('"string with ${(3+5)/4} nested math"')
561
+ end
562
+ end
563
+
564
+ context 'an interpolation with a nested array' do
565
+ let(:segments) do
566
+ [
567
+ [:STRING, 'string with '],
568
+ [:INTERP, "['an array ', $v2]"],
569
+ [:STRING, ' in it'],
570
+ ]
571
+ end
572
+
573
+ it 'creates a tokenised string' do
574
+ expect(tokens).to have(8).tokens
575
+
576
+ expect(tokens[0]).to have_attributes(
577
+ :type => :DQPRE,
578
+ :value => 'string with ',
579
+ :line => 1,
580
+ :column => 1
581
+ )
582
+ expect(tokens[1]).to have_attributes(
583
+ :type => :LBRACK,
584
+ :value => '[',
585
+ :line => 1,
586
+ :column => 16
587
+ )
588
+ expect(tokens[2]).to have_attributes(
589
+ :type => :SSTRING,
590
+ :value => 'an array ',
591
+ :line => 1,
592
+ :column => 17
593
+ )
594
+ expect(tokens[3]).to have_attributes(
595
+ :type => :COMMA,
596
+ :value => ',',
597
+ :line => 1,
598
+ :column => 28
599
+ )
600
+ expect(tokens[4]).to have_attributes(
601
+ :type => :WHITESPACE,
602
+ :value => ' ',
603
+ :line => 1,
604
+ :column => 29
605
+ )
606
+ expect(tokens[5]).to have_attributes(
607
+ :type => :VARIABLE,
608
+ :value => 'v2',
609
+ :line => 1,
610
+ :column => 30
611
+ )
612
+ expect(tokens[6]).to have_attributes(
613
+ :type => :RBRACK,
614
+ :value => ']',
615
+ :line => 1,
616
+ :column => 33
617
+ )
618
+ expect(tokens[7]).to have_attributes(
619
+ :type => :DQPOST,
620
+ :value => ' in it',
621
+ :line => 1,
622
+ :column => 34
623
+ )
624
+ end
625
+
626
+ it 'can render the result back into a manifest' do
627
+ expect(manifest).to eq(%("string with ${['an array ', $v2]} in it"))
628
+ end
629
+ end
630
+
631
+ context 'multiple unenclosed variables' do
632
+ let(:segments) do
633
+ [
634
+ [:STRING, ''],
635
+ [:UNENC_VAR, '$foo'],
636
+ [:STRING, ''],
637
+ [:UNENC_VAR, '$bar'],
638
+ [:STRING, ''],
639
+ ]
640
+ end
641
+
642
+ it 'creates a tokenised string' do
643
+ expect(tokens).to have(5).tokens
644
+
645
+ expect(tokens[0]).to have_attributes(
646
+ :type => :DQPRE,
647
+ :value => '',
648
+ :line => 1,
649
+ :column => 1
650
+ )
651
+ expect(tokens[1]).to have_attributes(
652
+ :type => :UNENC_VARIABLE,
653
+ :value => 'foo',
654
+ :line => 1,
655
+ :column => 2
656
+ )
657
+ expect(tokens[2]).to have_attributes(
658
+ :type => :DQMID,
659
+ :value => '',
660
+ :line => 1,
661
+ :column => 6
662
+ )
663
+ expect(tokens[3]).to have_attributes(
664
+ :type => :UNENC_VARIABLE,
665
+ :value => 'bar',
666
+ :line => 1,
667
+ :column => 6
668
+ )
669
+ expect(tokens[4]).to have_attributes(
670
+ :type => :DQPOST,
671
+ :value => '',
672
+ :line => 1,
673
+ :column => 10
674
+ )
675
+ end
676
+
677
+ it 'can render the result back into a manifest' do
678
+ expect(manifest).to eq('"$foo$bar"')
679
+ end
680
+ end
681
+
682
+ context 'an unenclosed variable with a trailing $' do
683
+ let(:segments) do
684
+ [
685
+ [:STRING, 'foo'],
686
+ [:UNENC_VAR, '$bar'],
687
+ [:STRING, '$'],
688
+ ]
689
+ end
690
+
691
+ it 'creates a tokenised string' do
692
+ expect(tokens).to have(3).tokens
693
+
694
+ expect(tokens[0]).to have_attributes(
695
+ :type => :DQPRE,
696
+ :value => 'foo',
697
+ :line => 1,
698
+ :column => 1
699
+ )
700
+ expect(tokens[1]).to have_attributes(
701
+ :type => :UNENC_VARIABLE,
702
+ :value => 'bar',
703
+ :line => 1,
704
+ :column => 5
705
+ )
706
+ expect(tokens[2]).to have_attributes(
707
+ :type => :DQPOST,
708
+ :value => '$',
709
+ :line => 1,
710
+ :column => 9
711
+ )
712
+ end
713
+
714
+ it 'can render the result back into a manifest' do
715
+ expect(manifest).to eq('"foo$bar$"')
716
+ end
717
+ end
718
+
719
+ context 'an interpolation with a complex function chain' do
720
+ let(:segments) do
721
+ [
722
+ [:STRING, ''],
723
+ [:INTERP, 'key'],
724
+ [:STRING, ' '],
725
+ [:INTERP, 'flatten([$value]).join("\nkey ")'],
726
+ [:STRING, ''],
727
+ ]
728
+ end
729
+
730
+ it 'creates a tokenised string' do
731
+ expect(tokens).to have(15).tokens
732
+
733
+ expect(tokens[0]).to have_attributes(
734
+ :type => :DQPRE,
735
+ :value => '',
736
+ :line => 1,
737
+ :column => 1
738
+ )
739
+ expect(tokens[1]).to have_attributes(
740
+ :type => :VARIABLE,
741
+ :value => 'key',
742
+ :line => 1,
743
+ :column => 4
744
+ )
745
+ expect(tokens[2]).to have_attributes(
746
+ :type => :DQMID,
747
+ :value => ' ',
748
+ :line => 1,
749
+ :column => 7
750
+ )
751
+ expect(tokens[3]).to have_attributes(
752
+ :type => :FUNCTION_NAME,
753
+ :value => 'flatten',
754
+ :line => 1,
755
+ :column => 11
756
+ )
757
+ expect(tokens[4]).to have_attributes(
758
+ :type => :LPAREN,
759
+ :value => '(',
760
+ :line => 1,
761
+ :column => 18
762
+ )
763
+ expect(tokens[5]).to have_attributes(
764
+ :type => :LBRACK,
765
+ :value => '[',
766
+ :line => 1,
767
+ :column => 19
768
+ )
769
+ expect(tokens[6]).to have_attributes(
770
+ :type => :VARIABLE,
771
+ :value => 'value',
772
+ :line => 1,
773
+ :column => 20
774
+ )
775
+ expect(tokens[7]).to have_attributes(
776
+ :type => :RBRACK,
777
+ :value => ']',
778
+ :line => 1,
779
+ :column => 26
780
+ )
781
+ expect(tokens[8]).to have_attributes(
782
+ :type => :RPAREN,
783
+ :value => ')',
784
+ :line => 1,
785
+ :column => 27
786
+ )
787
+ expect(tokens[9]).to have_attributes(
788
+ :type => :DOT,
789
+ :value => '.',
790
+ :line => 1,
791
+ :column => 28
792
+ )
793
+ expect(tokens[10]).to have_attributes(
794
+ :type => :FUNCTION_NAME,
795
+ :value => 'join',
796
+ :line => 1,
797
+ :column => 29
798
+ )
799
+ expect(tokens[11]).to have_attributes(
800
+ :type => :LPAREN,
801
+ :value => '(',
802
+ :line => 1,
803
+ :column => 33
804
+ )
805
+ expect(tokens[12]).to have_attributes(
806
+ :type => :STRING,
807
+ :value => '\nkey ',
808
+ :line => 1,
809
+ :column => 34
810
+ )
811
+ expect(tokens[13]).to have_attributes(
812
+ :type => :RPAREN,
813
+ :value => ')',
814
+ :line => 1,
815
+ :column => 42
816
+ )
817
+ expect(tokens[14]).to have_attributes(
818
+ :type => :DQPOST,
819
+ :value => '',
820
+ :line => 1,
821
+ :column => 43
822
+ )
823
+ end
824
+
825
+ it 'can render the result back into a manifest' do
826
+ expect(manifest).to eq('"${key} ${flatten([$value]).join("\nkey ")}"')
827
+ end
828
+ end
829
+
830
+ context 'nested interpolations' do
831
+ let(:segments) do
832
+ [
833
+ [:STRING, ''],
834
+ [:INTERP, 'facts["network_${iface}"]'],
835
+ [:STRING, '/'],
836
+ [:INTERP, 'facts["netmask_${iface}"]'],
837
+ [:STRING, ''],
838
+ ]
839
+ end
840
+
841
+ it 'creates a tokenised string' do
842
+ expect(tokens).to have(15).tokens
843
+
844
+ expect(tokens[0]).to have_attributes(
845
+ :type => :DQPRE,
846
+ :value => '',
847
+ :line => 1,
848
+ :column => 1
849
+ )
850
+ expect(tokens[1]).to have_attributes(
851
+ :type => :VARIABLE,
852
+ :value => 'facts',
853
+ :line => 1,
854
+ :column => 4
855
+ )
856
+ expect(tokens[2]).to have_attributes(
857
+ :type => :LBRACK,
858
+ :value => '[',
859
+ :line => 1,
860
+ :column => 9
861
+ )
862
+ expect(tokens[3]).to have_attributes(
863
+ :type => :DQPRE,
864
+ :value => 'network_',
865
+ :line => 1,
866
+ :column => 10
867
+ )
868
+ expect(tokens[4]).to have_attributes(
869
+ :type => :VARIABLE,
870
+ :value => 'iface',
871
+ :line => 1,
872
+ :column => 21
873
+ )
874
+ expect(tokens[5]).to have_attributes(
875
+ :type => :DQPOST,
876
+ :value => '',
877
+ :line => 1,
878
+ :column => 26
879
+ )
880
+ expect(tokens[6]).to have_attributes(
881
+ :type => :RBRACK,
882
+ :value => ']',
883
+ :line => 1,
884
+ :column => 28
885
+ )
886
+ expect(tokens[7]).to have_attributes(
887
+ :type => :DQMID,
888
+ :value => '/',
889
+ :line => 1,
890
+ :column => 29
891
+ )
892
+ expect(tokens[8]).to have_attributes(
893
+ :type => :VARIABLE,
894
+ :value => 'facts',
895
+ :line => 1,
896
+ :column => 33
897
+ )
898
+ expect(tokens[9]).to have_attributes(
899
+ :type => :LBRACK,
900
+ :value => '[',
901
+ :line => 1,
902
+ :column => 38
903
+ )
904
+ expect(tokens[10]).to have_attributes(
905
+ :type => :DQPRE,
906
+ :value => 'netmask_',
907
+ :line => 1,
908
+ :column => 39
909
+ )
910
+ expect(tokens[11]).to have_attributes(
911
+ :type => :VARIABLE,
912
+ :value => 'iface',
913
+ :line => 1,
914
+ :column => 50
915
+ )
916
+ expect(tokens[12]).to have_attributes(
917
+ :type => :DQPOST,
918
+ :value => '',
919
+ :line => 1,
920
+ :column => 55
921
+ )
922
+ expect(tokens[13]).to have_attributes(
923
+ :type => :RBRACK,
924
+ :value => ']',
925
+ :line => 1,
926
+ :column => 57
927
+ )
928
+ expect(tokens[14]).to have_attributes(
929
+ :type => :DQPOST,
930
+ :value => '',
931
+ :line => 1,
932
+ :column => 58
933
+ )
934
+ end
935
+
936
+ it 'can render the result back into a manifest' do
937
+ expect(manifest).to eq('"${facts["network_${iface}"]}/${facts["netmask_${iface}"]}"')
938
+ end
939
+ end
940
+
941
+ context 'interpolation with nested braces' do
942
+ let(:segments) do
943
+ [
944
+ [:STRING, ''],
945
+ [:INTERP, '$foo.map |$bar| { something($bar) }'],
946
+ [:STRING, ''],
947
+ ]
948
+ end
949
+
950
+ it 'creates a tokenised string' do
951
+ expect(tokens).to have(18).tokens
952
+
953
+ expect(tokens[0]).to have_attributes(
954
+ :type => :DQPRE,
955
+ :value => '',
956
+ :line => 1,
957
+ :column => 1
958
+ )
959
+ expect(tokens[1]).to have_attributes(
960
+ :type => :VARIABLE,
961
+ :value => 'foo',
962
+ :line => 1,
963
+ :column => 4
964
+ )
965
+ expect(tokens[2]).to have_attributes(
966
+ :type => :DOT,
967
+ :value => '.',
968
+ :line => 1,
969
+ :column => 8
970
+ )
971
+ expect(tokens[3]).to have_attributes(
972
+ :type => :NAME,
973
+ :value => 'map',
974
+ :line => 1,
975
+ :column => 9
976
+ )
977
+ expect(tokens[4]).to have_attributes(
978
+ :type => :WHITESPACE,
979
+ :value => ' ',
980
+ :line => 1,
981
+ :column => 12
982
+ )
983
+ expect(tokens[5]).to have_attributes(
984
+ :type => :PIPE,
985
+ :value => '|',
986
+ :line => 1,
987
+ :column => 13
988
+ )
989
+ expect(tokens[6]).to have_attributes(
990
+ :type => :VARIABLE,
991
+ :value => 'bar',
992
+ :line => 1,
993
+ :column => 14
994
+ )
995
+ expect(tokens[7]).to have_attributes(
996
+ :type => :PIPE,
997
+ :value => '|',
998
+ :line => 1,
999
+ :column => 18
1000
+ )
1001
+ expect(tokens[8]).to have_attributes(
1002
+ :type => :WHITESPACE,
1003
+ :value => ' ',
1004
+ :line => 1,
1005
+ :column => 19
1006
+ )
1007
+ expect(tokens[9]).to have_attributes(
1008
+ :type => :LBRACE,
1009
+ :value => '{',
1010
+ :line => 1,
1011
+ :column => 20
1012
+ )
1013
+ expect(tokens[10]).to have_attributes(
1014
+ :type => :WHITESPACE,
1015
+ :value => ' ',
1016
+ :line => 1,
1017
+ :column => 21
1018
+ )
1019
+ expect(tokens[11]).to have_attributes(
1020
+ :type => :FUNCTION_NAME,
1021
+ :value => 'something',
1022
+ :line => 1,
1023
+ :column => 22
1024
+ )
1025
+ expect(tokens[12]).to have_attributes(
1026
+ :type => :LPAREN,
1027
+ :value => '(',
1028
+ :line => 1,
1029
+ :column => 31
1030
+ )
1031
+ expect(tokens[13]).to have_attributes(
1032
+ :type => :VARIABLE,
1033
+ :value => 'bar',
1034
+ :line => 1,
1035
+ :column => 32
1036
+ )
1037
+ expect(tokens[14]).to have_attributes(
1038
+ :type => :RPAREN,
1039
+ :value => ')',
1040
+ :line => 1,
1041
+ :column => 36
1042
+ )
1043
+ expect(tokens[15]).to have_attributes(
1044
+ :type => :WHITESPACE,
1045
+ :value => ' ',
1046
+ :line => 1,
1047
+ :column => 37
1048
+ )
1049
+ expect(tokens[16]).to have_attributes(
1050
+ :type => :RBRACE,
1051
+ :value => '}',
1052
+ :line => 1,
1053
+ :column => 38
1054
+ )
1055
+ expect(tokens[17]).to have_attributes(
1056
+ :type => :DQPOST,
1057
+ :value => '',
1058
+ :line => 1,
1059
+ :column => 39
1060
+ )
1061
+ end
1062
+
1063
+ it 'can render the result back into a manifest' do
1064
+ expect(manifest).to eq('"${$foo.map |$bar| { something($bar) }}"')
1065
+ end
71
1066
  end
72
1067
  end
73
1068
 
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
-
1069
+ context ':STRING / :DQ' do
142
1070
  it 'should handle a string with newline characters' do
143
1071
  # rubocop:disable Layout/TrailingWhitespace
144
1072
  manifest = <<END
@@ -147,14 +1075,14 @@ describe PuppetLint::Lexer do # rubocop:disable Metrics/BlockLength
147
1075
  command => "echo > /home/bar/.token;
148
1076
  kdestroy;
149
1077
  kinit ${pseudouser}@EXAMPLE.COM -kt ${keytab_path};
150
- test $(klist | egrep '^Default principal:' | sed 's/Default principal:\s//') = '${pseudouser}'@EXAMPLE.COM",
1078
+ test $(klist | egrep '^Default principal:' | sed 's/Default principal:\\s//') = '${pseudouser}'@EXAMPLE.COM",
151
1079
  refreshonly => true;
152
1080
  }
153
1081
  END
154
1082
  # rubocop:enable Layout/TrailingWhitespace
155
1083
  tokens = @lexer.tokenise(manifest)
156
1084
 
157
- expect(tokens.length).to eq(36)
1085
+ expect(tokens.length).to eq(34)
158
1086
 
159
1087
  expect(tokens[0].type).to eq(:WHITESPACE)
160
1088
  expect(tokens[0].value).to eq(' ')
@@ -219,530 +1147,79 @@ END
219
1147
  expect(tokens[15].type).to eq(:VARIABLE)
220
1148
  expect(tokens[15].value).to eq('pseudouser')
221
1149
  expect(tokens[15].line).to eq(5)
222
- expect(tokens[15].column).to eq(131)
1150
+ expect(tokens[15].column).to eq(41)
223
1151
  expect(tokens[16].type).to eq(:DQMID)
224
1152
  expect(tokens[16].value).to eq('@EXAMPLE.COM -kt ')
225
1153
  expect(tokens[16].line).to eq(5)
226
- expect(tokens[16].column).to eq(143)
1154
+ expect(tokens[16].column).to eq(51)
227
1155
  expect(tokens[17].type).to eq(:VARIABLE)
228
1156
  expect(tokens[17].value).to eq('keytab_path')
229
1157
  expect(tokens[17].line).to eq(5)
230
- expect(tokens[17].column).to eq(161)
1158
+ expect(tokens[17].column).to eq(71)
231
1159
  expect(tokens[18].type).to eq(:DQMID)
232
- expect(tokens[18].value).to eq("; \n test ")
1160
+ expect(tokens[18].value).to eq("; \n test $(klist | egrep '^Default principal:' | sed 's/Default principal:\\s//') = '")
233
1161
  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('$')
1162
+ expect(tokens[18].column).to eq(82)
1163
+ expect(tokens[19].type).to eq(:VARIABLE)
1164
+ expect(tokens[19].value).to eq('pseudouser')
237
1165
  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: //') = '")
1166
+ expect(tokens[19].column).to eq(115)
1167
+ expect(tokens[20].type).to eq(:DQPOST)
1168
+ expect(tokens[20].value).to eq("'@EXAMPLE.COM")
241
1169
  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')
1170
+ expect(tokens[20].column).to eq(125)
1171
+ expect(tokens[21].type).to eq(:COMMA)
1172
+ expect(tokens[21].value).to eq(',')
245
1173
  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")
1174
+ expect(tokens[21].column).to eq(140)
1175
+ expect(tokens[22].type).to eq(:NEWLINE)
1176
+ expect(tokens[22].value).to eq("\n")
249
1177
  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(' ')
1178
+ expect(tokens[22].column).to eq(141)
1179
+ expect(tokens[23].type).to eq(:INDENT)
1180
+ expect(tokens[23].value).to eq(' ')
1181
+ expect(tokens[23].line).to eq(7)
1182
+ expect(tokens[23].column).to eq(1)
1183
+ expect(tokens[24].type).to eq(:NAME)
1184
+ expect(tokens[24].value).to eq('refreshonly')
1185
+ expect(tokens[24].line).to eq(7)
1186
+ expect(tokens[24].column).to eq(7)
1187
+ expect(tokens[25].type).to eq(:WHITESPACE)
1188
+ expect(tokens[25].value).to eq(' ')
261
1189
  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')
1190
+ expect(tokens[25].column).to eq(18)
1191
+ expect(tokens[26].type).to eq(:FARROW)
1192
+ expect(tokens[26].value).to eq('=>')
265
1193
  expect(tokens[26].line).to eq(7)
266
- expect(tokens[26].column).to eq(7)
1194
+ expect(tokens[26].column).to eq(19)
267
1195
  expect(tokens[27].type).to eq(:WHITESPACE)
268
1196
  expect(tokens[27].value).to eq(' ')
269
1197
  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('=>')
1198
+ expect(tokens[27].column).to eq(21)
1199
+ expect(tokens[28].type).to eq(:TRUE)
1200
+ expect(tokens[28].value).to eq('true')
273
1201
  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(' ')
1202
+ expect(tokens[28].column).to eq(22)
1203
+ expect(tokens[29].type).to eq(:SEMIC)
1204
+ expect(tokens[29].value).to eq(';')
277
1205
  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')
1206
+ expect(tokens[29].column).to eq(26)
1207
+ expect(tokens[30].type).to eq(:NEWLINE)
1208
+ expect(tokens[30].value).to eq("\n")
281
1209
  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(' ')
1210
+ expect(tokens[30].column).to eq(27)
1211
+ expect(tokens[31].type).to eq(:INDENT)
1212
+ expect(tokens[31].value).to eq(' ')
1213
+ expect(tokens[31].line).to eq(8)
1214
+ expect(tokens[31].column).to eq(1)
1215
+ expect(tokens[32].type).to eq(:RBRACE)
1216
+ expect(tokens[32].value).to eq('}')
1217
+ expect(tokens[32].line).to eq(8)
1218
+ expect(tokens[32].column).to eq(3)
1219
+ expect(tokens[33].type).to eq(:NEWLINE)
1220
+ expect(tokens[33].value).to eq("\n")
293
1221
  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)
1222
+ expect(tokens[33].column).to eq(4)
746
1223
  end
747
1224
 
748
1225
  it 'should calculate the column number correctly after an enclosed variable' do
@@ -875,10 +1352,17 @@ END
875
1352
  expect(token.value).to eq('Collection')
876
1353
  end
877
1354
 
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')
1355
+ describe 'Platform Types' do
1356
+ it 'should match Callable' do
1357
+ token = @lexer.tokenise('Callable').first
1358
+ expect(token.type).to eq(:TYPE)
1359
+ expect(token.value).to eq('Callable')
1360
+ end
1361
+ it 'should match Sensitive' do
1362
+ token = @lexer.tokenise('Sensitive').first
1363
+ expect(token.type).to eq(:TYPE)
1364
+ expect(token.value).to eq('Sensitive')
1365
+ end
882
1366
  end
883
1367
  end
884
1368
 
@@ -1012,7 +1496,7 @@ END
1012
1496
  expect(tokens[6].type).to eq(:DQMID)
1013
1497
  expect(tokens[6].value).to eq(' ')
1014
1498
  expect(tokens[6].line).to eq(1)
1015
- expect(tokens[6].column).to eq(19)
1499
+ expect(tokens[6].column).to eq(18)
1016
1500
  expect(tokens[7].type).to eq(:HEREDOC_OPEN)
1017
1501
  expect(tokens[7].value).to eq('end2')
1018
1502
  expect(tokens[7].line).to eq(1)
@@ -1020,7 +1504,7 @@ END
1020
1504
  expect(tokens[8].type).to eq(:DQPOST)
1021
1505
  expect(tokens[8].value).to eq('')
1022
1506
  expect(tokens[8].line).to eq(1)
1023
- expect(tokens[8].column).to eq(30)
1507
+ expect(tokens[8].column).to eq(29)
1024
1508
  expect(tokens[9].type).to eq(:NEWLINE)
1025
1509
  expect(tokens[9].value).to eq("\n")
1026
1510
  expect(tokens[9].line).to eq(1)
@@ -1091,6 +1575,21 @@ END
1091
1575
  expect(tokens[7].line).to eq(5)
1092
1576
  expect(tokens[7].column).to eq(8)
1093
1577
  end
1578
+
1579
+ it 'should handle a heredoc with spaces in the tag' do
1580
+ manifest = <<-END.gsub(%r{^ {6}}, '')
1581
+ $str = @("myheredoc" /)
1582
+ foo
1583
+ |-myheredoc
1584
+ END
1585
+ tokens = @lexer.tokenise(manifest)
1586
+ expect(tokens.length).to eq(8)
1587
+
1588
+ expect(tokens[4].type).to eq(:HEREDOC_OPEN)
1589
+ expect(tokens[4].value).to eq('"myheredoc" /')
1590
+ expect(tokens[6].type).to eq(:HEREDOC)
1591
+ expect(tokens[6].value).to eq(" foo\n ")
1592
+ end
1094
1593
  end
1095
1594
 
1096
1595
  context ':HEREDOC with interpolation' do
@@ -1143,7 +1642,7 @@ END
1143
1642
  manifest = <<-END.gsub(%r{^ {6}}, '')
1144
1643
  $str = @("myheredoc"/)
1145
1644
  SOMETHING
1146
- ${else}
1645
+ ${here}
1147
1646
  AND :
1148
1647
  $another
1149
1648
  THING
@@ -1182,10 +1681,10 @@ END
1182
1681
  expect(tokens[6].line).to eq(2)
1183
1682
  expect(tokens[6].column).to eq(1)
1184
1683
  expect(tokens[7].type).to eq(:VARIABLE)
1185
- expect(tokens[7].value).to eq('else')
1684
+ expect(tokens[7].value).to eq('here')
1186
1685
  expect(tokens[7].line).to eq(3)
1187
1686
  expect(tokens[7].column).to eq(5)
1188
- expect(tokens[7].to_manifest).to eq('else')
1687
+ expect(tokens[7].to_manifest).to eq('here')
1189
1688
  expect(tokens[8].type).to eq(:HEREDOC_MID)
1190
1689
  expect(tokens[8].value).to eq("\n AND :\n ")
1191
1690
  expect(tokens[8].line).to eq(3)
@@ -1453,9 +1952,10 @@ END
1453
1952
  expect(token.value).to eq('this is a regex')
1454
1953
  end
1455
1954
 
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)
1955
+ it 'should match even if there is \n in the regex' do
1956
+ token = @lexer.tokenise("/this is a regex,\ntoo/").first
1957
+ expect(token.type).to eq(:REGEX)
1958
+ expect(token.value).to eq("this is a regex,\ntoo")
1459
1959
  end
1460
1960
 
1461
1961
  it 'should not consider \/ to be the end of the regex' do
@@ -1497,6 +1997,13 @@ END
1497
1997
  expect(tokens[8].type).to eq(:REGEX)
1498
1998
  expect(tokens[8].value).to eq('([\w\.]+(:\d+)?(\/\w+)?)(:(\w+))?')
1499
1999
  end
2000
+
2001
+ it 'should discriminate between division and regexes' do
2002
+ tokens = @lexer.tokenise('if $a/10==0 or $b=~/{}/')
2003
+ expect(tokens[3].type).to eq(:DIV)
2004
+ expect(tokens[12].type).to eq(:REGEX)
2005
+ expect(tokens[12].value).to eq('{}')
2006
+ end
1500
2007
  end
1501
2008
 
1502
2009
  context ':STRING' do