rip-parser 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +15 -0
  3. data/.gitignore +9 -0
  4. data/.rspec +1 -0
  5. data/.ruby-version +1 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE.md +9 -0
  8. data/README.md +13 -0
  9. data/Rakefile +1 -0
  10. data/bin/console +8 -0
  11. data/bin/setup +8 -0
  12. data/legacy/normalizer.rb +279 -0
  13. data/legacy/parser_spec.rb +999 -0
  14. data/legacy/rules.rb +250 -0
  15. data/legacy/rules_spec.rb +1700 -0
  16. data/rip-parser.gemspec +20 -0
  17. data/source/rip/parser/about.rb +9 -0
  18. data/source/rip/parser/error.rb +36 -0
  19. data/source/rip/parser/grammar.rb +23 -0
  20. data/source/rip/parser/keywords.rb +84 -0
  21. data/source/rip/parser/location.rb +47 -0
  22. data/source/rip/parser/node.rb +115 -0
  23. data/source/rip/parser/rules/assignment.rb +23 -0
  24. data/source/rip/parser/rules/binary_condition.rb +24 -0
  25. data/source/rip/parser/rules/character.rb +40 -0
  26. data/source/rip/parser/rules/class.rb +60 -0
  27. data/source/rip/parser/rules/common.rb +47 -0
  28. data/source/rip/parser/rules/date_time.rb +31 -0
  29. data/source/rip/parser/rules/expression.rb +122 -0
  30. data/source/rip/parser/rules/import.rb +23 -0
  31. data/source/rip/parser/rules/invocation.rb +15 -0
  32. data/source/rip/parser/rules/invocation_index.rb +15 -0
  33. data/source/rip/parser/rules/keyword.rb +12 -0
  34. data/source/rip/parser/rules/lambda.rb +45 -0
  35. data/source/rip/parser/rules/list.rb +18 -0
  36. data/source/rip/parser/rules/map.rb +15 -0
  37. data/source/rip/parser/rules/module.rb +29 -0
  38. data/source/rip/parser/rules/number.rb +21 -0
  39. data/source/rip/parser/rules/pair.rb +13 -0
  40. data/source/rip/parser/rules/property.rb +33 -0
  41. data/source/rip/parser/rules/range.rb +15 -0
  42. data/source/rip/parser/rules/reference.rb +16 -0
  43. data/source/rip/parser/rules/string.rb +41 -0
  44. data/source/rip/parser/rules/unit.rb +17 -0
  45. data/source/rip/parser/rules.rb +7 -0
  46. data/source/rip/parser/utilities/normalizer.rb +638 -0
  47. data/source/rip/parser.rb +24 -0
  48. data/source/rip-parser.rb +1 -0
  49. data/spec/fixtures/syntax_sample.rip +96 -0
  50. data/spec/spec_helper.rb +20 -0
  51. data/spec/support/helpers.rb +57 -0
  52. data/spec/support/parslet.rb +1 -0
  53. data/spec/support/shared_examples.rb +9 -0
  54. data/spec/unit/rip/parser/grammar_spec.rb +8 -0
  55. data/spec/unit/rip/parser/location_spec.rb +89 -0
  56. data/spec/unit/rip/parser/node_spec.rb +157 -0
  57. data/spec/unit/rip/parser/rules/assignment_spec.rb +59 -0
  58. data/spec/unit/rip/parser/rules/binary_condition_spec.rb +41 -0
  59. data/spec/unit/rip/parser/rules/character_spec.rb +29 -0
  60. data/spec/unit/rip/parser/rules/class_spec.rb +181 -0
  61. data/spec/unit/rip/parser/rules/common_spec.rb +88 -0
  62. data/spec/unit/rip/parser/rules/date_time_spec.rb +61 -0
  63. data/spec/unit/rip/parser/rules/expression_spec.rb +95 -0
  64. data/spec/unit/rip/parser/rules/import_spec.rb +64 -0
  65. data/spec/unit/rip/parser/rules/invocation_index_spec.rb +46 -0
  66. data/spec/unit/rip/parser/rules/invocation_spec.rb +46 -0
  67. data/spec/unit/rip/parser/rules/keyword_spec.rb +17 -0
  68. data/spec/unit/rip/parser/rules/lambda_spec.rb +174 -0
  69. data/spec/unit/rip/parser/rules/list_spec.rb +45 -0
  70. data/spec/unit/rip/parser/rules/map_spec.rb +36 -0
  71. data/spec/unit/rip/parser/rules/module_spec.rb +63 -0
  72. data/spec/unit/rip/parser/rules/number_spec.rb +40 -0
  73. data/spec/unit/rip/parser/rules/pair_spec.rb +25 -0
  74. data/spec/unit/rip/parser/rules/property_spec.rb +27 -0
  75. data/spec/unit/rip/parser/rules/range_spec.rb +37 -0
  76. data/spec/unit/rip/parser/rules/reference_spec.rb +43 -0
  77. data/spec/unit/rip/parser/rules/string_spec.rb +166 -0
  78. data/spec/unit/rip/parser/rules/unit_spec.rb +17 -0
  79. data/spec/unit/rip/parser_spec.rb +106 -0
  80. metadata +192 -0
@@ -0,0 +1,999 @@
1
+ require 'hashie'
2
+ require 'spec_helper'
3
+
4
+ describe Rip::Parser do
5
+ describe '.root' do
6
+ specify { expect(Rip::Parser.root).to eq(Pathname.new(__dir__).parent.parent.parent.expand_path) }
7
+ end
8
+
9
+ recognizes_as_expected 'several statements together' do
10
+ let(:rip) do
11
+ strip_heredoc(<<-RIP)
12
+ if (true) {
13
+ lambda = -> {
14
+ # comment
15
+ 42
16
+ }
17
+ lambda()
18
+ } else {
19
+ 1 + 2
20
+ }
21
+ RIP
22
+ end
23
+ end
24
+
25
+ describe '#reference' do
26
+ it 'recognizes valid references, including predefined references' do
27
+ [
28
+ 'name',
29
+ 'Person',
30
+ '==',
31
+ 'save!',
32
+ 'valid?',
33
+ 'long_ref-name',
34
+ '*-+&$~%',
35
+ 'one_9',
36
+ 'ɹÇʇɹoÔ€uÉl∀™',
37
+ 'nilly',
38
+ 'nil',
39
+ 'true',
40
+ 'false',
41
+ 'Kernel',
42
+ 'returner'
43
+ ].each do |reference|
44
+ expect(reference).to parse_as(Hashie::Mash.new(:module => [ { :reference => reference } ]))
45
+ end
46
+ end
47
+
48
+ it 'skips invalid references' do
49
+ [
50
+ 'one.two',
51
+ '999',
52
+ '6teen',
53
+ 'rip rocks',
54
+ 'key:value'
55
+ ].each do |non_reference|
56
+ expect(non_reference).to_not parse_as({ :reference => non_reference })
57
+ end
58
+ end
59
+ end
60
+
61
+ describe '#property_name' do
62
+ it 'recognizes special-case property names' do
63
+ [
64
+ '/',
65
+ '<=>',
66
+ '<',
67
+ '<<',
68
+ '<=',
69
+ '>',
70
+ '>>',
71
+ '>=',
72
+ '[]'
73
+ ].each do |property_name|
74
+ rip = "@.#{property_name}"
75
+ expected = {
76
+ :module => [
77
+ {
78
+ :object => { :reference => '@' },
79
+ :location => '.',
80
+ :property_name => property_name
81
+ }
82
+ ]
83
+ }
84
+ expect(rip).to parse_as(Hashie::Mash.new(expected))
85
+ end
86
+ end
87
+ end
88
+
89
+ describe '#expression' do
90
+ context 'block' do
91
+ recognizes_as_expected 'empty block' do
92
+ let(:rip) { 'try {}' }
93
+ end
94
+
95
+ recognizes_as_expected 'block with argument' do
96
+ let(:rip) { 'if (:name) {} else {}' }
97
+ end
98
+
99
+ recognizes_as_expected 'block with multiple arguments' do
100
+ let(:rip) { 'type (one, two) {}' }
101
+ end
102
+
103
+ recognizes_as_expected 'type with no super_types' do
104
+ let(:rip) do
105
+ <<-RIP
106
+ type {
107
+ # comment
108
+ }
109
+ RIP
110
+ end
111
+ let(:expected) do
112
+ {
113
+ :module => [
114
+ {
115
+ :type => 'type',
116
+ :arguments => [],
117
+ :location_body => '{',
118
+ :body => []
119
+ }
120
+ ]
121
+ }
122
+ end
123
+ end
124
+
125
+ recognizes_as_expected 'overload with no parameters' do
126
+ let(:rip) { '-> {}' }
127
+ let(:expected) do
128
+ {
129
+ :module => [
130
+ {
131
+ :dash_rocket => '->',
132
+ :parameters => [],
133
+ :location_body => '{',
134
+ :body => []
135
+ }
136
+ ]
137
+ }
138
+ end
139
+ end
140
+
141
+ recognizes_as_expected 'lambda with no parameters' do
142
+ let(:rip) { '=> { -> {} }' }
143
+ let(:expected) do
144
+ {
145
+ :module => [
146
+ {
147
+ :fat_rocket => '=>',
148
+ :location_body => '{',
149
+ :overload_blocks => [
150
+ {
151
+ :dash_rocket => '->',
152
+ :parameters => [],
153
+ :location_body => '{',
154
+ :body => []
155
+ }
156
+ ]
157
+ }
158
+ ]
159
+ }
160
+ end
161
+ end
162
+
163
+ recognizes_as_expected 'overload with multiple required parameters' do
164
+ let(:rip) { '-> (one, two) {}' }
165
+ end
166
+
167
+ recognizes_as_expected 'overload with multiple required parameters with type restrictions' do
168
+ let(:rip) { '-> (one, two<CustomType>) {}' }
169
+ end
170
+
171
+ recognizes_as_expected 'overload with multiple optional parameters' do
172
+ let(:rip) { '-> (one = 1, two = 2) {}' }
173
+ end
174
+
175
+ recognizes_as_expected 'overload with multiple optional parameters with type restrictions' do
176
+ let(:rip) { '-> (one<System.Integer> = 1, two = 2) {}' }
177
+ let(:expected) do
178
+ {
179
+ :module => [
180
+ {
181
+ :dash_rocket => '->',
182
+ :parameters => [
183
+ {
184
+ :parameter => 'one',
185
+ :type_argument => {
186
+ :object => { :reference => 'System' },
187
+ :location => '.',
188
+ :property_name => 'Integer'
189
+ },
190
+ :default_expression => { :integer => '1', :sign => '+' }
191
+ },
192
+ {
193
+ :parameter => 'two',
194
+ :default_expression => { :integer => '2', :sign => '+' }
195
+ }
196
+ ],
197
+ :location_body => '{',
198
+ :body => []
199
+ }
200
+ ]
201
+ }
202
+ end
203
+ end
204
+
205
+ recognizes_as_expected 'overload with required parameter and optional parameter' do
206
+ let(:rip) { '-> (platform, name = :rip) {}' }
207
+ end
208
+
209
+ recognizes_as_expected 'overload with multiple required parameter and multiple optional parameter' do
210
+ let(:rip) { '-> (abc, xyz, one = 1, two = 2) {}' }
211
+ end
212
+
213
+ recognizes_as_expected 'blocks with block arguments' do
214
+ let(:rip) { 'type (type () {}) {}' }
215
+ end
216
+
217
+ recognizes_as_expected 'switch' do
218
+ let(:rip) { 'switch (foo) { case (true) { 42 } else { 0 } }' }
219
+ let(:expected) do
220
+ {
221
+ :module => [
222
+ {
223
+ :switch => 'switch',
224
+ :argument => { :reference => 'foo' },
225
+ :case_blocks => [
226
+ {
227
+ :case => 'case',
228
+ :arguments => [
229
+ { :reference => 'true' }
230
+ ],
231
+ :location_body => '{',
232
+ :body => [
233
+ { :sign => '+', :integer => '42' }
234
+ ]
235
+ }
236
+ ],
237
+ :else_block => {
238
+ :else => 'else',
239
+ :location_body => '{',
240
+ :body => [
241
+ { :sign => '+', :integer => '0' }
242
+ ]
243
+ }
244
+ }
245
+ ]
246
+ }
247
+ end
248
+ end
249
+
250
+ recognizes_as_expected 'switch without argument' do
251
+ let(:rip) { 'switch { case (true) { 42 } else { 0 } }' }
252
+ let(:expected) do
253
+ {
254
+ :module => [
255
+ {
256
+ :switch => 'switch',
257
+ :case_blocks => [
258
+ {
259
+ :case => 'case',
260
+ :arguments => [
261
+ { :reference => 'true' }
262
+ ],
263
+ :location_body => '{',
264
+ :body => [
265
+ { :sign => '+', :integer => '42' }
266
+ ]
267
+ }
268
+ ],
269
+ :else_block => {
270
+ :else => 'else',
271
+ :location_body => '{',
272
+ :body => [
273
+ { :sign => '+', :integer => '0' }
274
+ ]
275
+ }
276
+ }
277
+ ]
278
+ }
279
+ end
280
+ end
281
+
282
+ recognizes_as_expected 'try-catch' do
283
+ let(:rip) { 'try {} catch (Exception: e) {}' }
284
+ let(:expected) do
285
+ {
286
+ :module => [
287
+ {
288
+ :try_block => {
289
+ :try => 'try',
290
+ :location_body => '{',
291
+ :body => []
292
+ },
293
+ :catch_blocks => [
294
+ {
295
+ :catch => 'catch',
296
+ :argument => {
297
+ :key => { :reference => 'Exception' },
298
+ :location => ':',
299
+ :value => { :reference => 'e' }
300
+ },
301
+ :location_body => '{',
302
+ :body => []
303
+ }
304
+ ]
305
+ }
306
+ ]
307
+ }
308
+ end
309
+ end
310
+
311
+ recognizes_as_expected 'try-finally' do
312
+ let(:rip) { 'try {} finally {}' }
313
+ let(:expected) do
314
+ {
315
+ :module => [
316
+ {
317
+ :try_block => {
318
+ :try => 'try',
319
+ :location_body => '{',
320
+ :body => []
321
+ },
322
+ :catch_blocks => [],
323
+ :finally_block => {
324
+ :finally => 'finally',
325
+ :location_body => '{',
326
+ :body => []
327
+ }
328
+ }
329
+ ]
330
+ }
331
+ end
332
+ end
333
+
334
+ recognizes_as_expected 'try-catch-finally' do
335
+ let(:rip) { 'try {} catch (Exception: e) {} finally {}' }
336
+ let(:expected) do
337
+ {
338
+ :module => [
339
+ {
340
+ :try_block => {
341
+ :try => 'try',
342
+ :location_body => '{',
343
+ :body => []
344
+ },
345
+ :catch_blocks => [
346
+ {
347
+ :catch => 'catch',
348
+ :argument => {
349
+ :key => { :reference => 'Exception' },
350
+ :location => ':',
351
+ :value => { :reference => 'e' }
352
+ },
353
+ :location_body => '{',
354
+ :body => []
355
+ }
356
+ ],
357
+ :finally_block => {
358
+ :finally => 'finally',
359
+ :location_body => '{',
360
+ :body => []
361
+ }
362
+ }
363
+ ]
364
+ }
365
+ end
366
+ end
367
+ end
368
+
369
+ context 'block body' do
370
+ recognizes_as_expected 'comments inside block body' do
371
+ let(:rip) do
372
+ <<-RIP
373
+ -> (x) {
374
+ # comment
375
+ }
376
+ RIP
377
+ end
378
+ end
379
+
380
+ recognizes_as_expected 'references inside block body' do
381
+ let(:rip) { '-> (x) { name }' }
382
+ end
383
+
384
+ recognizes_as_expected 'assignments inside block body' do
385
+ let(:rip) { '-> (foo) { x = :y }' }
386
+ end
387
+
388
+ recognizes_as_expected 'invocations inside block body' do
389
+ let(:rip) { '-> (run!) { run!() }' }
390
+ end
391
+
392
+ recognizes_as_expected 'operator invocations inside block body' do
393
+ let(:rip) { '-> (steam) { steam will :rise }' }
394
+ end
395
+
396
+ recognizes_as_expected 'literals inside block body' do
397
+ let(:rip) { '-> (n) { `3 }' }
398
+ end
399
+
400
+ recognizes_as_expected 'blocks inside block body' do
401
+ let(:rip) { '-> (foo) { if (false) { 42 } else { -42 } }' }
402
+ end
403
+ end
404
+
405
+ recognizes_as_expected 'keyword' do
406
+ let(:rip) { 'return;' }
407
+ end
408
+
409
+ recognizes_as_expected 'keyword followed by phrase' do
410
+ let(:rip) { 'exit 0' }
411
+ end
412
+
413
+ recognizes_as_expected 'keyword followed by parenthesis around phrase' do
414
+ let(:rip) { 'throw (e)' }
415
+ end
416
+
417
+ context 'multiple expressions' do
418
+ recognizes_as_expected 'terminates expressions properly' do
419
+ let(:rip) do
420
+ <<-RIP
421
+ one
422
+ two
423
+ three
424
+ RIP
425
+ end
426
+ let(:expected) do
427
+ {
428
+ :module => [
429
+ { :reference => 'one' },
430
+ { :reference => 'two' },
431
+ { :reference => 'three' }
432
+ ]
433
+ }
434
+ end
435
+ end
436
+
437
+ recognizes_as_expected 'allows expressions to take more than one line' do
438
+ let(:rip) do
439
+ <<-RIP
440
+ 1 +
441
+ 2 -
442
+ 3
443
+ RIP
444
+ end
445
+ let(:expected) do
446
+ {
447
+ :module => [
448
+ {
449
+ :callable => {
450
+ :object => {
451
+ :callable => {
452
+ :object => { :sign => '+', :integer => '1' },
453
+ :location => '+',
454
+ :property_name => '+'
455
+ },
456
+ :location => '+',
457
+ :arguments => [
458
+ { :sign => '+', :integer => '2' }
459
+ ]
460
+ },
461
+ :location => '-',
462
+ :property_name => '-'
463
+ },
464
+ :location => '-',
465
+ :arguments => [
466
+ { :sign => '+', :integer => '3' }
467
+ ]
468
+ }
469
+ ]
470
+ }
471
+ end
472
+ end
473
+ end
474
+
475
+ context 'invoking lambdas' do
476
+ recognizes_as_expected 'overload literal invocation' do
477
+ let(:rip) { '-> () {}()' }
478
+ end
479
+
480
+ recognizes_as_expected 'lambda reference invocation' do
481
+ let(:rip) { 'full_name()' }
482
+ end
483
+
484
+ recognizes_as_expected 'lambda reference invocation arguments' do
485
+ let(:rip) { 'full_name(:Thomas, :Ingram)' }
486
+ end
487
+
488
+ recognizes_as_expected 'index invocation' do
489
+ let(:rip) { 'list[0]' }
490
+ end
491
+
492
+ recognizes_as_expected 'operator invocation' do
493
+ let(:rip) { '2 + 2' }
494
+ end
495
+
496
+ recognizes_as_expected 'reference assignment' do
497
+ let(:rip) { 'favorite_language = :rip' }
498
+ let(:expected) do
499
+ {
500
+ :module => [
501
+ {
502
+ :lhs => { :reference => 'favorite_language' },
503
+ :location => '=',
504
+ :rhs => { :location => ':', :string => rip_string('rip') }
505
+ }
506
+ ]
507
+ }
508
+ end
509
+ end
510
+
511
+ recognizes_as_expected 'property assignment' do
512
+ let(:rip) { 'favorite.language = :rip.lang' }
513
+ let(:expected) do
514
+ {
515
+ :module => [
516
+ {
517
+ :lhs => {
518
+ :object => { :reference => 'favorite' },
519
+ :location => '.',
520
+ :property_name => 'language'
521
+ },
522
+ :location => '=',
523
+ :rhs => {
524
+ :object => { :location => ':', :string => rip_string('rip') },
525
+ :location => '.',
526
+ :property_name => 'lang'
527
+ }
528
+ }
529
+ ]
530
+ }
531
+ end
532
+ end
533
+ end
534
+
535
+ context 'nested parenthesis' do
536
+ recognizes_as_expected 'anything surrounded by parenthesis' do
537
+ let(:rip) { '(0)' }
538
+ end
539
+
540
+ recognizes_as_expected 'anything surrounded by parenthesis with crazy nesting' do
541
+ let(:rip) { '((((((l((1 + (((2 - 3)))))))))))' }
542
+ end
543
+ end
544
+
545
+ context 'property chaining' do
546
+ recognizes_as_expected 'chaining with properies and invocations' do
547
+ let(:rip) { '0.one().two.three()' }
548
+ end
549
+
550
+ recognizes_as_expected 'chaining off opererators' do
551
+ let(:rip) { '(1 - 2).zero?()' }
552
+ end
553
+
554
+ recognizes_as_expected 'chaining several opererators' do
555
+ let(:rip) { '1 + 2 + 3 + 4' }
556
+ end
557
+ end
558
+
559
+ context 'atomic literals' do
560
+ describe 'numbers' do
561
+ recognizes_as_expected 'integer' do
562
+ let(:rip) { '42' }
563
+ let(:expected) do
564
+ {
565
+ :module => [
566
+ { :sign => '+', :integer => '42' }
567
+ ]
568
+ }
569
+ end
570
+ end
571
+
572
+ recognizes_as_expected 'decimal' do
573
+ let(:rip) { '4.2' }
574
+ let(:expected) do
575
+ {
576
+ :module => [
577
+ { :sign => '+', :decimal => '4.2' }
578
+ ]
579
+ }
580
+ end
581
+ end
582
+
583
+ recognizes_as_expected 'negative number' do
584
+ let(:rip) { '-3' }
585
+ let(:expected) do
586
+ {
587
+ :module => [
588
+ { :sign => '-', :integer => '3' }
589
+ ]
590
+ }
591
+ end
592
+ end
593
+
594
+ recognizes_as_expected 'large number' do
595
+ let(:rip) { '123_456_789' }
596
+ let(:expected) do
597
+ {
598
+ :module => [
599
+ { :sign => '+', :integer => '123_456_789' }
600
+ ]
601
+ }
602
+ end
603
+ end
604
+ end
605
+
606
+ recognizes_as_expected 'regular character' do
607
+ let(:rip) { '`9' }
608
+ let(:expected) do
609
+ {
610
+ :module => [
611
+ {
612
+ :location => '`',
613
+ :character => '9'
614
+ }
615
+ ]
616
+ }
617
+ end
618
+ end
619
+
620
+ recognizes_as_expected 'escaped character' do
621
+ let(:rip) { '`\n' }
622
+ let(:expected) do
623
+ {
624
+ :module => [
625
+ {
626
+ :location => '`',
627
+ :character => "\n"
628
+ }
629
+ ]
630
+ }
631
+ end
632
+ end
633
+
634
+ recognizes_as_expected 'symbol string' do
635
+ let(:rip) { ':0' }
636
+ end
637
+
638
+ recognizes_as_expected 'symbol string with escape' do
639
+ let(:rip) { ':on\e' }
640
+ let(:expected) do
641
+ {
642
+ :module => [
643
+ {
644
+ :location => ':',
645
+ :string => [
646
+ { :location => 'o', :character => 'o' },
647
+ { :location => 'n', :character => 'n' },
648
+ { :location => '\\', :character => '\\' },
649
+ { :location => 'e', :character => 'e' }
650
+ ]
651
+ }
652
+ ]
653
+ }
654
+ end
655
+ end
656
+
657
+ recognizes_as_expected 'single-quoted string (empty)' do
658
+ let(:rip) { "''" }
659
+ let(:expected) do
660
+ {
661
+ :module => [
662
+ {
663
+ :location => '\'',
664
+ :string => []
665
+ }
666
+ ]
667
+ }
668
+ end
669
+ end
670
+
671
+ recognizes_as_expected 'single-quoted string' do
672
+ let(:rip) { '\'two\'' }
673
+ end
674
+
675
+ recognizes_as_expected 'double-quoted string (empty)' do
676
+ let(:rip) { '""' }
677
+ let(:expected) do
678
+ {
679
+ :module => [
680
+ {
681
+ :location => '"',
682
+ :string => []
683
+ }
684
+ ]
685
+ }
686
+ end
687
+ end
688
+
689
+ recognizes_as_expected 'double-quoted string' do
690
+ let(:rip) { '"a\nb"' }
691
+ let(:expected) do
692
+ {
693
+ :module => [
694
+ {
695
+ :location => '"',
696
+ :string => [
697
+ { :location => 'a', :character => 'a' },
698
+ { :location => "\n", :character => "\n" },
699
+ { :location => 'b', :character => 'b' }
700
+ ]
701
+ }
702
+ ]
703
+ }
704
+ end
705
+ end
706
+
707
+ recognizes_as_expected 'double-quoted string with interpolation' do
708
+ let(:rip) { '"ab#{cd}ef"' }
709
+ let(:expected) do
710
+ {
711
+ :module => [
712
+ {
713
+ :callable => {
714
+ :object => {
715
+ :callable => {
716
+ :object => {
717
+ :location => '"',
718
+ :string => rip_string('ab')
719
+ },
720
+ :location => '+',
721
+ :property_name => '+'
722
+ },
723
+ :location => '+',
724
+ :arguments => [
725
+ {
726
+ :start => '#{',
727
+ :interpolation => [
728
+ { :reference => 'cd' }
729
+ ],
730
+ :end => '}'
731
+ }
732
+ ]
733
+ },
734
+ :location => '+',
735
+ :property_name => '+'
736
+ },
737
+ :location => '+',
738
+ :arguments => [
739
+ {
740
+ :location => '"',
741
+ :string => rip_string('ef')
742
+ }
743
+ ]
744
+ }
745
+ ]
746
+ }
747
+ end
748
+ end
749
+
750
+ recognizes_as_expected 'empty heredoc' do
751
+ let(:rip) { "<<HERE_DOC\nHERE_DOC" }
752
+ let(:expected) do
753
+ {
754
+ :module => [
755
+ {
756
+ :location => '<<',
757
+ :string => rip_string('')
758
+ }
759
+ ]
760
+ }
761
+ end
762
+ end
763
+
764
+ recognizes_as_expected 'heredoc with just blank lines' do
765
+ let(:rip) { "<<HERE_DOC\r\n\r\n\r\nHERE_DOC\r\n" }
766
+ let(:expected) do
767
+ {
768
+ :module => [
769
+ {
770
+ :location => '<<',
771
+ :string => rip_string("\r\n\r\n")
772
+ }
773
+ ]
774
+ }
775
+ end
776
+ end
777
+
778
+ recognizes_as_expected 'heredoc with just indented lines' do
779
+ let(:rip) { "\t<<HERE_DOC\n\t\n\t\n\tHERE_DOC\n" }
780
+ let(:expected) do
781
+ {
782
+ :module => [
783
+ {
784
+ :location => '<<',
785
+ :string => rip_string("\t\n\t\n")
786
+ }
787
+ ]
788
+ }
789
+ end
790
+ end
791
+
792
+ recognizes_as_expected 'heredoc containing label' do
793
+ let(:rip) do
794
+ strip_heredoc(<<-RIP)
795
+ <<HERE_DOC
796
+ i'm a HERE_DOC
797
+ HERE_DOC are multi-line strings
798
+ HERE_DOC
799
+ RIP
800
+ end
801
+ let(:expected) do
802
+ {
803
+ :module => [
804
+ {
805
+ :location => '<<',
806
+ :string => rip_string("i'm a HERE_DOC\nHERE_DOC are multi-line strings\n")
807
+ }
808
+ ]
809
+ }
810
+ end
811
+ end
812
+
813
+ recognizes_as_expected 'heredoc with interpolation' do
814
+ let(:rip) do
815
+ strip_heredoc(<<-RIP)
816
+ <<HERE_DOC
817
+ here docs are good for
818
+ strings that \#{need} multiple lines
819
+ advantageous, eh?
820
+ HERE_DOC
821
+ RIP
822
+ end
823
+ end
824
+ end
825
+
826
+ context 'date and time literals' do
827
+ recognizes_as_expected 'date' do
828
+ let(:rip) { '2012-02-12' }
829
+ let(:expected) do
830
+ {
831
+ :module => [
832
+ {
833
+ :year => '2012',
834
+ :month => '02',
835
+ :day => '12'
836
+ }
837
+ ]
838
+ }
839
+ end
840
+ end
841
+
842
+ recognizes_as_expected 'time' do
843
+ let(:rip) { '05:24:00' }
844
+ let(:expected) do
845
+ {
846
+ :module => [
847
+ {
848
+ :hour => '05',
849
+ :minute => '24',
850
+ :second => '00',
851
+ :sub_second => '0',
852
+ :offset => {
853
+ :sign => '+',
854
+ :hour => '00',
855
+ :minute => '00'
856
+ }
857
+ }
858
+ ]
859
+ }
860
+ end
861
+ end
862
+
863
+ recognizes_as_expected 'time with optional fractional second' do
864
+ let(:rip) { '05:24:00.14159' }
865
+ let(:expected) do
866
+ {
867
+ :module => [
868
+ {
869
+ :hour => '05',
870
+ :minute => '24',
871
+ :second => '00',
872
+ :sub_second => '14159',
873
+ :offset => {
874
+ :sign => '+',
875
+ :hour => '00',
876
+ :minute => '00'
877
+ }
878
+ }
879
+ ]
880
+ }
881
+ end
882
+ end
883
+
884
+ recognizes_as_expected 'time with optional offset' do
885
+ let(:rip) { '00:24:00-0500' }
886
+ let(:expected) do
887
+ {
888
+ :module => [
889
+ {
890
+ :hour => '00',
891
+ :minute => '24',
892
+ :second => '00',
893
+ :sub_second => '0',
894
+ :offset => {
895
+ :sign => '-',
896
+ :hour => '05',
897
+ :minute => '00'
898
+ }
899
+ }
900
+ ]
901
+ }
902
+ end
903
+ end
904
+
905
+ recognizes_as_expected 'time with optional fractional second and optional offset' do
906
+ let(:rip) { '00:24:00.14159-0500' }
907
+ let(:expected) do
908
+ {
909
+ :module => [
910
+ {
911
+ :hour => '00',
912
+ :minute => '24',
913
+ :second => '00',
914
+ :sub_second => '14159',
915
+ :offset => {
916
+ :sign => '-',
917
+ :hour => '05',
918
+ :minute => '00'
919
+ }
920
+ }
921
+ ]
922
+ }
923
+ end
924
+ end
925
+
926
+ recognizes_as_expected 'datetime' do
927
+ let(:rip) { '2012-02-12T05:24:00' }
928
+ let(:expected) do
929
+ {
930
+ :module => [
931
+ {
932
+ :date => {
933
+ :year => '2012',
934
+ :month => '02',
935
+ :day => '12'
936
+ },
937
+ :time => {
938
+ :hour => '05',
939
+ :minute => '24',
940
+ :second => '00',
941
+ :sub_second => '0',
942
+ :offset => {
943
+ :sign => '+',
944
+ :hour => '00',
945
+ :minute => '00'
946
+ }
947
+ }
948
+ }
949
+ ]
950
+ }
951
+ end
952
+ end
953
+ end
954
+
955
+ context 'molecular literals' do
956
+ recognizes_as_expected 'key-value pairs' do
957
+ let(:rip) { '5: \'five\'' }
958
+ end
959
+
960
+ recognizes_as_expected 'ranges' do
961
+ let(:rip) { '1..3' }
962
+ end
963
+
964
+ recognizes_as_expected 'exclusive ranges' do
965
+ let(:rip) { '1...age' }
966
+ end
967
+
968
+ recognizes_as_expected 'empty map' do
969
+ let(:rip) { '{}' }
970
+ end
971
+
972
+ recognizes_as_expected 'map with content' do
973
+ let(:rip) do
974
+ <<-RIP
975
+ {
976
+ :age: 31,
977
+ :name: :Thomas
978
+ }
979
+ RIP
980
+ end
981
+ end
982
+
983
+ recognizes_as_expected 'empty list' do
984
+ let(:rip) { '[]' }
985
+ end
986
+
987
+ recognizes_as_expected 'list with content' do
988
+ let(:rip) do
989
+ <<-RIP
990
+ [
991
+ 31,
992
+ :Thomas
993
+ ]
994
+ RIP
995
+ end
996
+ end
997
+ end
998
+ end
999
+ end