methic 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a28f40b0c1a99dc37830175725f8319468a46ab4
4
+ data.tar.gz: 7a96c2312722b4f3498aaaf6fbfe4142b8bb76f9
5
+ SHA512:
6
+ metadata.gz: 6908e75f68ea08980bda60ccf9814093095773c6f8cd20193de3a9c442d494e04117c1d9dcda0ea7725906efb474d06528db8e1f7248eff16da9061265a00372
7
+ data.tar.gz: 2e2b9d8f6550734dc81d2e6372b59275596d1faeafd44515e45c27872f34741f77b38e05f7638ed314f6da7ad05b41fec10cedd82cd68e3cd7c064e6e0362dde
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
@@ -0,0 +1 @@
1
+ language: ruby
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in methic.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Josef Šimánek
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,56 @@
1
+ # Methic [![Build Status](https://travis-ci.org/zizkovrb/methic.svg?branch=master)](https://travis-ci.org/zizkovrb/methic)
2
+
3
+ Simple arithmetic implemented by Treetop.
4
+
5
+ Heavily based on [treetop arithmetic example](https://github.com/cjheath/treetop/blob/master/examples/lambda_calculus/arithmetic.treetop).
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'methic'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install methic
22
+
23
+ ## Usage
24
+
25
+ ```ruby
26
+ parser = MethicParser.new
27
+
28
+ parser.parse('0').eval #=> 0
29
+ parser.parse('1').eval #=> 1
30
+ parser.parse('123').eval #=> 123
31
+
32
+ parser.parse('x').eval('x' => 0) #=> 0
33
+ parser.parse('x').eval('x' => 3) #=> 3
34
+ parser.parse('y').eval('y' => 10) #=> 10
35
+
36
+ parser.parse('x + 5').eval('x' => 5) #=> 10
37
+ parser.parse('x - 5').eval('x' => 5) #=> 0
38
+ parser.parse('x * 2').eval('x' => 3) #=> 6
39
+ parser.parse('x / 2').eval('x' => 6) #=> 3
40
+
41
+ parser.parse('1 + 2 * 3 + 4').eval #=> 11
42
+ parser.parse('5 - 2 - 1').eval #=> 2
43
+
44
+ parser.parse('(5 + x) * (10 - y)').eval('x' => 0, 'y' => 5) #=> 25
45
+
46
+ parser.parse('4 == 4').eval #=> true
47
+ parser.parse('4 == 3').eval #=> false
48
+ ```
49
+
50
+ ## Contributing
51
+
52
+ 1. Fork it ( https://github.com/zizkovrb/methic/fork )
53
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
54
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
55
+ 4. Push to the branch (`git push origin my-new-feature`)
56
+ 5. Create a new Pull Request
@@ -0,0 +1,17 @@
1
+ require "bundler/gem_tasks"
2
+ require 'treetop'
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new(:test) do |t|
6
+ t.libs << 'test'
7
+ t.pattern = 'test/**/*_test.rb'
8
+ end
9
+
10
+ desc 'Compile treetop to ruby'
11
+ task :compile do
12
+ compiler = Treetop::Compiler::GrammarCompiler.new
13
+ treetop_file = File.expand_path('../lib/methic.tt', __FILE__)
14
+ compiler.compile(treetop_file)
15
+ end
16
+
17
+ task :default => [:compile, :test]
@@ -0,0 +1,727 @@
1
+ # Autogenerated from a Treetop grammar. Edits may be lost.
2
+
3
+
4
+ require 'treetop'
5
+ require 'methic_node_classes'
6
+
7
+ module Methic
8
+ include Treetop::Runtime
9
+
10
+ def root
11
+ @root ||= :expression
12
+ end
13
+
14
+ def _nt_expression
15
+ start_index = index
16
+ if node_cache[:expression].has_key?(index)
17
+ cached = node_cache[:expression][index]
18
+ if cached
19
+ node_cache[:expression][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
20
+ @index = cached.interval.end
21
+ end
22
+ return cached
23
+ end
24
+
25
+ i0 = index
26
+ r1 = _nt_comparative
27
+ if r1
28
+ r1 = SyntaxNode.new(input, (index-1)...index) if r1 == true
29
+ r0 = r1
30
+ else
31
+ r2 = _nt_additive
32
+ if r2
33
+ r2 = SyntaxNode.new(input, (index-1)...index) if r2 == true
34
+ r0 = r2
35
+ else
36
+ @index = i0
37
+ r0 = nil
38
+ end
39
+ end
40
+
41
+ node_cache[:expression][start_index] = r0
42
+
43
+ r0
44
+ end
45
+
46
+ module Comparative0
47
+ def space1
48
+ elements[0]
49
+ end
50
+
51
+ def operator
52
+ elements[1]
53
+ end
54
+
55
+ def space2
56
+ elements[2]
57
+ end
58
+
59
+ def operand
60
+ elements[3]
61
+ end
62
+ end
63
+
64
+ module Comparative1
65
+ def head
66
+ elements[0]
67
+ end
68
+
69
+ def tail
70
+ elements[1]
71
+ end
72
+ end
73
+
74
+ def _nt_comparative
75
+ start_index = index
76
+ if node_cache[:comparative].has_key?(index)
77
+ cached = node_cache[:comparative][index]
78
+ if cached
79
+ node_cache[:comparative][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
80
+ @index = cached.interval.end
81
+ end
82
+ return cached
83
+ end
84
+
85
+ i0, s0 = index, []
86
+ r1 = _nt_additive
87
+ s0 << r1
88
+ if r1
89
+ s2, i2 = [], index
90
+ loop do
91
+ i3, s3 = index, []
92
+ r4 = _nt_space
93
+ s3 << r4
94
+ if r4
95
+ r5 = _nt_equality_op
96
+ s3 << r5
97
+ if r5
98
+ r6 = _nt_space
99
+ s3 << r6
100
+ if r6
101
+ r7 = _nt_additive
102
+ s3 << r7
103
+ end
104
+ end
105
+ end
106
+ if s3.last
107
+ r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
108
+ r3.extend(Comparative0)
109
+ else
110
+ @index = i3
111
+ r3 = nil
112
+ end
113
+ if r3
114
+ s2 << r3
115
+ else
116
+ break
117
+ end
118
+ end
119
+ r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
120
+ s0 << r2
121
+ end
122
+ if s0.last
123
+ r0 = instantiate_node(BinaryOperation,input, i0...index, s0)
124
+ r0.extend(Comparative1)
125
+ else
126
+ @index = i0
127
+ r0 = nil
128
+ end
129
+
130
+ node_cache[:comparative][start_index] = r0
131
+
132
+ r0
133
+ end
134
+
135
+ module EqualityOp0
136
+ def apply(a, b)
137
+ a == b
138
+ end
139
+ end
140
+
141
+ def _nt_equality_op
142
+ start_index = index
143
+ if node_cache[:equality_op].has_key?(index)
144
+ cached = node_cache[:equality_op][index]
145
+ if cached
146
+ node_cache[:equality_op][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
147
+ @index = cached.interval.end
148
+ end
149
+ return cached
150
+ end
151
+
152
+ if (match_len = has_terminal?('==', false, index))
153
+ r0 = instantiate_node(SyntaxNode,input, index...(index + match_len))
154
+ r0.extend(EqualityOp0)
155
+ @index += match_len
156
+ else
157
+ terminal_parse_failure('==')
158
+ r0 = nil
159
+ end
160
+
161
+ node_cache[:equality_op][start_index] = r0
162
+
163
+ r0
164
+ end
165
+
166
+ module Additive0
167
+ def space1
168
+ elements[0]
169
+ end
170
+
171
+ def operator
172
+ elements[1]
173
+ end
174
+
175
+ def space2
176
+ elements[2]
177
+ end
178
+
179
+ def operand
180
+ elements[3]
181
+ end
182
+ end
183
+
184
+ module Additive1
185
+ def head
186
+ elements[0]
187
+ end
188
+
189
+ def tail
190
+ elements[1]
191
+ end
192
+ end
193
+
194
+ def _nt_additive
195
+ start_index = index
196
+ if node_cache[:additive].has_key?(index)
197
+ cached = node_cache[:additive][index]
198
+ if cached
199
+ node_cache[:additive][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
200
+ @index = cached.interval.end
201
+ end
202
+ return cached
203
+ end
204
+
205
+ i0, s0 = index, []
206
+ r1 = _nt_multitive
207
+ s0 << r1
208
+ if r1
209
+ s2, i2 = [], index
210
+ loop do
211
+ i3, s3 = index, []
212
+ r4 = _nt_space
213
+ s3 << r4
214
+ if r4
215
+ r5 = _nt_additive_op
216
+ s3 << r5
217
+ if r5
218
+ r6 = _nt_space
219
+ s3 << r6
220
+ if r6
221
+ r7 = _nt_multitive
222
+ s3 << r7
223
+ end
224
+ end
225
+ end
226
+ if s3.last
227
+ r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
228
+ r3.extend(Additive0)
229
+ else
230
+ @index = i3
231
+ r3 = nil
232
+ end
233
+ if r3
234
+ s2 << r3
235
+ else
236
+ break
237
+ end
238
+ end
239
+ r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
240
+ s0 << r2
241
+ end
242
+ if s0.last
243
+ r0 = instantiate_node(BinaryOperation,input, i0...index, s0)
244
+ r0.extend(Additive1)
245
+ else
246
+ @index = i0
247
+ r0 = nil
248
+ end
249
+
250
+ node_cache[:additive][start_index] = r0
251
+
252
+ r0
253
+ end
254
+
255
+ module AdditiveOp0
256
+ def apply(a, b)
257
+ a + b
258
+ end
259
+ end
260
+
261
+ module AdditiveOp1
262
+ def apply(a, b)
263
+ a - b
264
+ end
265
+ end
266
+
267
+ def _nt_additive_op
268
+ start_index = index
269
+ if node_cache[:additive_op].has_key?(index)
270
+ cached = node_cache[:additive_op][index]
271
+ if cached
272
+ node_cache[:additive_op][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
273
+ @index = cached.interval.end
274
+ end
275
+ return cached
276
+ end
277
+
278
+ i0 = index
279
+ if (match_len = has_terminal?('+', false, index))
280
+ r1 = instantiate_node(SyntaxNode,input, index...(index + match_len))
281
+ r1.extend(AdditiveOp0)
282
+ @index += match_len
283
+ else
284
+ terminal_parse_failure('+')
285
+ r1 = nil
286
+ end
287
+ if r1
288
+ r1 = SyntaxNode.new(input, (index-1)...index) if r1 == true
289
+ r0 = r1
290
+ else
291
+ if (match_len = has_terminal?('-', false, index))
292
+ r2 = instantiate_node(SyntaxNode,input, index...(index + match_len))
293
+ r2.extend(AdditiveOp1)
294
+ @index += match_len
295
+ else
296
+ terminal_parse_failure('-')
297
+ r2 = nil
298
+ end
299
+ if r2
300
+ r2 = SyntaxNode.new(input, (index-1)...index) if r2 == true
301
+ r0 = r2
302
+ else
303
+ @index = i0
304
+ r0 = nil
305
+ end
306
+ end
307
+
308
+ node_cache[:additive_op][start_index] = r0
309
+
310
+ r0
311
+ end
312
+
313
+ module Multitive0
314
+ def space1
315
+ elements[0]
316
+ end
317
+
318
+ def operator
319
+ elements[1]
320
+ end
321
+
322
+ def space2
323
+ elements[2]
324
+ end
325
+
326
+ def operand
327
+ elements[3]
328
+ end
329
+ end
330
+
331
+ module Multitive1
332
+ def head
333
+ elements[0]
334
+ end
335
+
336
+ def tail
337
+ elements[1]
338
+ end
339
+ end
340
+
341
+ def _nt_multitive
342
+ start_index = index
343
+ if node_cache[:multitive].has_key?(index)
344
+ cached = node_cache[:multitive][index]
345
+ if cached
346
+ node_cache[:multitive][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
347
+ @index = cached.interval.end
348
+ end
349
+ return cached
350
+ end
351
+
352
+ i0, s0 = index, []
353
+ r1 = _nt_primary
354
+ s0 << r1
355
+ if r1
356
+ s2, i2 = [], index
357
+ loop do
358
+ i3, s3 = index, []
359
+ r4 = _nt_space
360
+ s3 << r4
361
+ if r4
362
+ r5 = _nt_multitive_op
363
+ s3 << r5
364
+ if r5
365
+ r6 = _nt_space
366
+ s3 << r6
367
+ if r6
368
+ r7 = _nt_primary
369
+ s3 << r7
370
+ end
371
+ end
372
+ end
373
+ if s3.last
374
+ r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
375
+ r3.extend(Multitive0)
376
+ else
377
+ @index = i3
378
+ r3 = nil
379
+ end
380
+ if r3
381
+ s2 << r3
382
+ else
383
+ break
384
+ end
385
+ end
386
+ r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
387
+ s0 << r2
388
+ end
389
+ if s0.last
390
+ r0 = instantiate_node(BinaryOperation,input, i0...index, s0)
391
+ r0.extend(Multitive1)
392
+ else
393
+ @index = i0
394
+ r0 = nil
395
+ end
396
+
397
+ node_cache[:multitive][start_index] = r0
398
+
399
+ r0
400
+ end
401
+
402
+ module MultitiveOp0
403
+ def apply(a, b)
404
+ a * b
405
+ end
406
+ end
407
+
408
+ module MultitiveOp1
409
+ def apply(a, b)
410
+ a / b
411
+ end
412
+ end
413
+
414
+ def _nt_multitive_op
415
+ start_index = index
416
+ if node_cache[:multitive_op].has_key?(index)
417
+ cached = node_cache[:multitive_op][index]
418
+ if cached
419
+ node_cache[:multitive_op][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
420
+ @index = cached.interval.end
421
+ end
422
+ return cached
423
+ end
424
+
425
+ i0 = index
426
+ if (match_len = has_terminal?('*', false, index))
427
+ r1 = instantiate_node(SyntaxNode,input, index...(index + match_len))
428
+ r1.extend(MultitiveOp0)
429
+ @index += match_len
430
+ else
431
+ terminal_parse_failure('*')
432
+ r1 = nil
433
+ end
434
+ if r1
435
+ r1 = SyntaxNode.new(input, (index-1)...index) if r1 == true
436
+ r0 = r1
437
+ else
438
+ if (match_len = has_terminal?('/', false, index))
439
+ r2 = instantiate_node(SyntaxNode,input, index...(index + match_len))
440
+ r2.extend(MultitiveOp1)
441
+ @index += match_len
442
+ else
443
+ terminal_parse_failure('/')
444
+ r2 = nil
445
+ end
446
+ if r2
447
+ r2 = SyntaxNode.new(input, (index-1)...index) if r2 == true
448
+ r0 = r2
449
+ else
450
+ @index = i0
451
+ r0 = nil
452
+ end
453
+ end
454
+
455
+ node_cache[:multitive_op][start_index] = r0
456
+
457
+ r0
458
+ end
459
+
460
+ module Primary0
461
+ def space1
462
+ elements[1]
463
+ end
464
+
465
+ def expression
466
+ elements[2]
467
+ end
468
+
469
+ def space2
470
+ elements[3]
471
+ end
472
+
473
+ end
474
+
475
+ module Primary1
476
+ def eval(env={})
477
+ expression.eval(env)
478
+ end
479
+ end
480
+
481
+ def _nt_primary
482
+ start_index = index
483
+ if node_cache[:primary].has_key?(index)
484
+ cached = node_cache[:primary][index]
485
+ if cached
486
+ node_cache[:primary][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
487
+ @index = cached.interval.end
488
+ end
489
+ return cached
490
+ end
491
+
492
+ i0 = index
493
+ r1 = _nt_variable
494
+ if r1
495
+ r1 = SyntaxNode.new(input, (index-1)...index) if r1 == true
496
+ r0 = r1
497
+ else
498
+ r2 = _nt_number
499
+ if r2
500
+ r2 = SyntaxNode.new(input, (index-1)...index) if r2 == true
501
+ r0 = r2
502
+ else
503
+ i3, s3 = index, []
504
+ if (match_len = has_terminal?('(', false, index))
505
+ r4 = true
506
+ @index += match_len
507
+ else
508
+ terminal_parse_failure('(')
509
+ r4 = nil
510
+ end
511
+ s3 << r4
512
+ if r4
513
+ r5 = _nt_space
514
+ s3 << r5
515
+ if r5
516
+ r6 = _nt_expression
517
+ s3 << r6
518
+ if r6
519
+ r7 = _nt_space
520
+ s3 << r7
521
+ if r7
522
+ if (match_len = has_terminal?(')', false, index))
523
+ r8 = true
524
+ @index += match_len
525
+ else
526
+ terminal_parse_failure(')')
527
+ r8 = nil
528
+ end
529
+ s3 << r8
530
+ end
531
+ end
532
+ end
533
+ end
534
+ if s3.last
535
+ r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
536
+ r3.extend(Primary0)
537
+ r3.extend(Primary1)
538
+ else
539
+ @index = i3
540
+ r3 = nil
541
+ end
542
+ if r3
543
+ r3 = SyntaxNode.new(input, (index-1)...index) if r3 == true
544
+ r0 = r3
545
+ else
546
+ @index = i0
547
+ r0 = nil
548
+ end
549
+ end
550
+ end
551
+
552
+ node_cache[:primary][start_index] = r0
553
+
554
+ r0
555
+ end
556
+
557
+ module Variable0
558
+ def eval(env={})
559
+ env[name]
560
+ end
561
+
562
+ def name
563
+ text_value
564
+ end
565
+ end
566
+
567
+ def _nt_variable
568
+ start_index = index
569
+ if node_cache[:variable].has_key?(index)
570
+ cached = node_cache[:variable][index]
571
+ if cached
572
+ node_cache[:variable][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
573
+ @index = cached.interval.end
574
+ end
575
+ return cached
576
+ end
577
+
578
+ s0, i0 = [], index
579
+ loop do
580
+ if has_terminal?(@regexps[gr = '\A[a-z]'] ||= Regexp.new(gr), :regexp, index)
581
+ r1 = true
582
+ @index += 1
583
+ else
584
+ terminal_parse_failure('[a-z]')
585
+ r1 = nil
586
+ end
587
+ if r1
588
+ s0 << r1
589
+ else
590
+ break
591
+ end
592
+ end
593
+ if s0.empty?
594
+ @index = i0
595
+ r0 = nil
596
+ else
597
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
598
+ r0.extend(Variable0)
599
+ end
600
+
601
+ node_cache[:variable][start_index] = r0
602
+
603
+ r0
604
+ end
605
+
606
+ module Number0
607
+ end
608
+
609
+ module Number1
610
+ def eval(env={})
611
+ text_value.to_i
612
+ end
613
+ end
614
+
615
+ def _nt_number
616
+ start_index = index
617
+ if node_cache[:number].has_key?(index)
618
+ cached = node_cache[:number][index]
619
+ if cached
620
+ node_cache[:number][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
621
+ @index = cached.interval.end
622
+ end
623
+ return cached
624
+ end
625
+
626
+ i0 = index
627
+ i1, s1 = index, []
628
+ if has_terminal?(@regexps[gr = '\A[1-9]'] ||= Regexp.new(gr), :regexp, index)
629
+ r2 = true
630
+ @index += 1
631
+ else
632
+ terminal_parse_failure('[1-9]')
633
+ r2 = nil
634
+ end
635
+ s1 << r2
636
+ if r2
637
+ s3, i3 = [], index
638
+ loop do
639
+ if has_terminal?(@regexps[gr = '\A[0-9]'] ||= Regexp.new(gr), :regexp, index)
640
+ r4 = true
641
+ @index += 1
642
+ else
643
+ terminal_parse_failure('[0-9]')
644
+ r4 = nil
645
+ end
646
+ if r4
647
+ s3 << r4
648
+ else
649
+ break
650
+ end
651
+ end
652
+ r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
653
+ s1 << r3
654
+ end
655
+ if s1.last
656
+ r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
657
+ r1.extend(Number0)
658
+ else
659
+ @index = i1
660
+ r1 = nil
661
+ end
662
+ if r1
663
+ r1 = SyntaxNode.new(input, (index-1)...index) if r1 == true
664
+ r0 = r1
665
+ r0.extend(Number1)
666
+ else
667
+ if (match_len = has_terminal?('0', false, index))
668
+ r5 = true
669
+ @index += match_len
670
+ else
671
+ terminal_parse_failure('0')
672
+ r5 = nil
673
+ end
674
+ if r5
675
+ r5 = SyntaxNode.new(input, (index-1)...index) if r5 == true
676
+ r0 = r5
677
+ r0.extend(Number1)
678
+ else
679
+ @index = i0
680
+ r0 = nil
681
+ end
682
+ end
683
+
684
+ node_cache[:number][start_index] = r0
685
+
686
+ r0
687
+ end
688
+
689
+ def _nt_space
690
+ start_index = index
691
+ if node_cache[:space].has_key?(index)
692
+ cached = node_cache[:space][index]
693
+ if cached
694
+ node_cache[:space][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
695
+ @index = cached.interval.end
696
+ end
697
+ return cached
698
+ end
699
+
700
+ s0, i0 = [], index
701
+ loop do
702
+ if (match_len = has_terminal?(' ', false, index))
703
+ r1 = true
704
+ @index += match_len
705
+ else
706
+ terminal_parse_failure(' ')
707
+ r1 = nil
708
+ end
709
+ if r1
710
+ s0 << r1
711
+ else
712
+ break
713
+ end
714
+ end
715
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
716
+
717
+ node_cache[:space][start_index] = r0
718
+
719
+ r0
720
+ end
721
+
722
+ end
723
+
724
+ class MethicParser < Treetop::Runtime::CompiledParser
725
+ include Methic
726
+ end
727
+
@@ -0,0 +1,101 @@
1
+ require 'treetop'
2
+ require 'methic_node_classes'
3
+
4
+ grammar Methic
5
+ rule expression
6
+ comparative / additive
7
+ end
8
+
9
+ rule comparative
10
+ head:additive
11
+ tail:(
12
+ space operator:equality_op
13
+ space operand:additive)* <BinaryOperation>
14
+ end
15
+
16
+ rule equality_op
17
+ '==' {
18
+ def apply(a, b)
19
+ a == b
20
+ end
21
+ }
22
+ end
23
+
24
+ rule additive
25
+ head:multitive
26
+ tail:(
27
+ space operator:additive_op
28
+ space operand:multitive)* <BinaryOperation>
29
+ end
30
+
31
+ rule additive_op
32
+ '+' {
33
+ def apply(a, b)
34
+ a + b
35
+ end
36
+ }
37
+ /
38
+ '-' {
39
+ def apply(a, b)
40
+ a - b
41
+ end
42
+ }
43
+ end
44
+
45
+ rule multitive
46
+ head:primary
47
+ tail:(
48
+ space operator:multitive_op
49
+ space operand:primary)* <BinaryOperation>
50
+ end
51
+
52
+ rule multitive_op
53
+ '*' {
54
+ def apply(a, b)
55
+ a * b
56
+ end
57
+ }
58
+ /
59
+ '/' {
60
+ def apply(a, b)
61
+ a / b
62
+ end
63
+ }
64
+ end
65
+
66
+ rule primary
67
+ variable
68
+ /
69
+ number
70
+ /
71
+ '(' space expression space ')' {
72
+ def eval(env={})
73
+ expression.eval(env)
74
+ end
75
+ }
76
+ end
77
+
78
+ rule variable
79
+ [a-z]+ {
80
+ def eval(env={})
81
+ env[name]
82
+ end
83
+
84
+ def name
85
+ text_value
86
+ end
87
+ }
88
+ end
89
+
90
+ rule number
91
+ ([1-9] [0-9]* / '0') {
92
+ def eval(env={})
93
+ text_value.to_i
94
+ end
95
+ }
96
+ end
97
+
98
+ rule space
99
+ ' '*
100
+ end
101
+ end
@@ -0,0 +1,3 @@
1
+ module Methic
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,9 @@
1
+ module Methic
2
+ class BinaryOperation < Treetop::Runtime::SyntaxNode
3
+ def eval(env={})
4
+ tail.elements.inject(head.eval(env)) do |value, element|
5
+ element.operator.apply(value, element.operand.eval(env))
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'methic/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "methic"
8
+ spec.version = Methic::VERSION
9
+ spec.authors = ["Josef Šimánek"]
10
+ spec.email = ["josef.simanek@gmail.com"]
11
+ spec.summary = %q{Simple arithmetic implemented by Treetop.}
12
+ spec.homepage = "https://github.com/zizkovrb/methic"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_dependency "treetop"
21
+
22
+ spec.add_development_dependency "minitest", "~> 5.0"
23
+ spec.add_development_dependency "rake"
24
+ end
@@ -0,0 +1,54 @@
1
+ require 'test_helper'
2
+
3
+ class MethicParserTest < Minitest::Test
4
+ include ParserTestHelper
5
+
6
+ def setup
7
+ @parser = MethicParser.new
8
+ end
9
+
10
+ def test_number
11
+ assert_equal 0, parse('0').eval
12
+ assert_equal 1, parse('1').eval
13
+ assert_equal 123, parse('123').eval
14
+ end
15
+
16
+ def test_variable
17
+ assert_equal 0, parse('x').eval('x' => 0)
18
+ assert_equal 3, parse('x').eval('x' => 3)
19
+ assert_equal 10, parse('y').eval('y' => 10)
20
+ end
21
+
22
+ def test_addition
23
+ assert_equal 10, parse('x + 5').eval('x' => 5)
24
+ end
25
+
26
+ def test_subtraction
27
+ assert_equal 0, parse('x - 5').eval('x' => 5)
28
+ end
29
+
30
+ def test_multiplication
31
+ assert_equal 6, parse('x * 2').eval('x' => 3)
32
+ end
33
+
34
+ def test_division
35
+ assert_equal 3, parse('x / 2').eval('x' => 6)
36
+ end
37
+
38
+ def test_order_of_operations
39
+ assert_equal 11, parse('1 + 2 * 3 + 4').eval
40
+ end
41
+
42
+ def test_left_to_right
43
+ assert_equal 2, parse('5 - 2 - 1').eval
44
+ end
45
+
46
+ def test_parentheses
47
+ assert_equal 25, parse('(5 + x) * (10 - y)').eval('x' => 0, 'y' => 5)
48
+ end
49
+
50
+ def test_equality
51
+ assert parse('4 == 4').eval
52
+ assert !parse('4 == 3').eval
53
+ end
54
+ end
@@ -0,0 +1,18 @@
1
+ require 'bundler/setup'
2
+ require 'minitest/autorun'
3
+ require 'methic'
4
+
5
+ module ParserTestHelper
6
+ def assert_evals_to_self(input)
7
+ assert_evals_to(input, input)
8
+ end
9
+
10
+ def parse(input)
11
+ result = @parser.parse(input)
12
+ unless result
13
+ puts @parser.terminal_failures.join("\n")
14
+ end
15
+ assert !result.nil?
16
+ result
17
+ end
18
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: methic
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Josef Šimánek
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: treetop
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '5.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '5.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description:
56
+ email:
57
+ - josef.simanek@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - .travis.yml
64
+ - Gemfile
65
+ - LICENSE.txt
66
+ - README.md
67
+ - Rakefile
68
+ - lib/methic.rb
69
+ - lib/methic.tt
70
+ - lib/methic/version.rb
71
+ - lib/methic_node_classes.rb
72
+ - methic.gemspec
73
+ - test/methic_test.rb
74
+ - test/test_helper.rb
75
+ homepage: https://github.com/zizkovrb/methic
76
+ licenses:
77
+ - MIT
78
+ metadata: {}
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubyforge_project:
95
+ rubygems_version: 2.2.1
96
+ signing_key:
97
+ specification_version: 4
98
+ summary: Simple arithmetic implemented by Treetop.
99
+ test_files:
100
+ - test/methic_test.rb
101
+ - test/test_helper.rb
102
+ has_rdoc: