puppet-lint 2.3.6 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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