methic 0.0.1

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.
@@ -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: