rip-parser 0.1.0

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