ghazel-parslet 1.4.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. data/HISTORY.txt +195 -0
  2. data/LICENSE +23 -0
  3. data/README +70 -0
  4. data/Rakefile +49 -0
  5. data/example/boolean_algebra.rb +70 -0
  6. data/example/calc.rb +153 -0
  7. data/example/comments.rb +35 -0
  8. data/example/deepest_errors.rb +131 -0
  9. data/example/documentation.rb +18 -0
  10. data/example/email_parser.rb +52 -0
  11. data/example/empty.rb +13 -0
  12. data/example/erb.rb +47 -0
  13. data/example/ignore.rb +33 -0
  14. data/example/ip_address.rb +125 -0
  15. data/example/json.rb +128 -0
  16. data/example/local.rb +34 -0
  17. data/example/mathn.rb +44 -0
  18. data/example/minilisp.rb +94 -0
  19. data/example/modularity.rb +47 -0
  20. data/example/nested_errors.rb +132 -0
  21. data/example/output/boolean_algebra.out +4 -0
  22. data/example/output/calc.out +1 -0
  23. data/example/output/comments.out +8 -0
  24. data/example/output/deepest_errors.out +54 -0
  25. data/example/output/documentation.err +4 -0
  26. data/example/output/documentation.out +1 -0
  27. data/example/output/email_parser.out +2 -0
  28. data/example/output/empty.err +1 -0
  29. data/example/output/erb.out +7 -0
  30. data/example/output/ignore.out +1 -0
  31. data/example/output/ignore_whitespace.out +1 -0
  32. data/example/output/ip_address.out +9 -0
  33. data/example/output/json.out +5 -0
  34. data/example/output/local.out +3 -0
  35. data/example/output/mathn.out +4 -0
  36. data/example/output/minilisp.out +5 -0
  37. data/example/output/modularity.out +0 -0
  38. data/example/output/nested_errors.out +54 -0
  39. data/example/output/parens.out +8 -0
  40. data/example/output/readme.out +1 -0
  41. data/example/output/seasons.out +28 -0
  42. data/example/output/sentence.out +1 -0
  43. data/example/output/simple_xml.out +2 -0
  44. data/example/output/string_parser.out +3 -0
  45. data/example/parens.rb +42 -0
  46. data/example/readme.rb +30 -0
  47. data/example/seasons.rb +46 -0
  48. data/example/sentence.rb +36 -0
  49. data/example/simple.lit +3 -0
  50. data/example/simple_xml.rb +54 -0
  51. data/example/string_parser.rb +77 -0
  52. data/example/test.lit +4 -0
  53. data/lib/parslet.rb +254 -0
  54. data/lib/parslet/atoms.rb +32 -0
  55. data/lib/parslet/atoms/alternative.rb +50 -0
  56. data/lib/parslet/atoms/base.rb +124 -0
  57. data/lib/parslet/atoms/can_flatten.rb +137 -0
  58. data/lib/parslet/atoms/context.rb +94 -0
  59. data/lib/parslet/atoms/dsl.rb +98 -0
  60. data/lib/parslet/atoms/entity.rb +41 -0
  61. data/lib/parslet/atoms/lookahead.rb +49 -0
  62. data/lib/parslet/atoms/named.rb +32 -0
  63. data/lib/parslet/atoms/re.rb +38 -0
  64. data/lib/parslet/atoms/repetition.rb +63 -0
  65. data/lib/parslet/atoms/rule.rb +12 -0
  66. data/lib/parslet/atoms/rule/position.rb +143 -0
  67. data/lib/parslet/atoms/sequence.rb +38 -0
  68. data/lib/parslet/atoms/str.rb +37 -0
  69. data/lib/parslet/atoms/visitor.rb +89 -0
  70. data/lib/parslet/cause.rb +94 -0
  71. data/lib/parslet/convenience.rb +35 -0
  72. data/lib/parslet/error_reporter.rb +7 -0
  73. data/lib/parslet/error_reporter/deepest.rb +95 -0
  74. data/lib/parslet/error_reporter/tree.rb +57 -0
  75. data/lib/parslet/export.rb +162 -0
  76. data/lib/parslet/expression.rb +51 -0
  77. data/lib/parslet/expression/treetop.rb +92 -0
  78. data/lib/parslet/parser.rb +67 -0
  79. data/lib/parslet/pattern.rb +114 -0
  80. data/lib/parslet/pattern/binding.rb +49 -0
  81. data/lib/parslet/rig/rspec.rb +51 -0
  82. data/lib/parslet/slice.rb +101 -0
  83. data/lib/parslet/source.rb +62 -0
  84. data/lib/parslet/source/line_cache.rb +95 -0
  85. data/lib/parslet/transform.rb +236 -0
  86. data/lib/parslet/transform/context.rb +32 -0
  87. metadata +264 -0
@@ -0,0 +1,195 @@
1
+ = 2.0 / ?? (future release changes, like a reminder to self)
2
+
3
+ - prsnt? and absnt? are now finally banned into oblivion. Wasting vocals for
4
+ the win.
5
+
6
+ = 1.4.0 / 25May2012
7
+
8
+ + Revised documentation. A few new API features have finally made it into
9
+ the documentation. Examples in the documentation are now curated and
10
+ run against the current code so that they really really work.
11
+ Also, the website generation tools have been replaced with 2012-style
12
+ tools. Much less pain to update now.
13
+
14
+ + Parslet::Source now doesn't hold a StringIO, it directly holds the
15
+ buffer to be parsed. The api of Source has changed a tiny bit. This change
16
+ has been made for speed optimisation reasons.
17
+
18
+ + :reporter argument to parse, allowing to customize error reporting within
19
+ wide boundaries. See issue #64 for a discussion.
20
+ Included are two error reporters, one (default) with the existing error
21
+ tree functionality, one reporting deepest errors as defined by the above
22
+ ticket.
23
+
24
+ + Optimistic parse: Parsing is two phase, with the first phase assuming
25
+ there will be no errors. This yields ~ 20% speed improvement in the
26
+ case where the parse succeeds.
27
+ Also, internal error handling is now using tuples. This and other
28
+ optimizations have yielded ~ 30% overall improvement.
29
+
30
+ ! #error_tree and #cause removed from all of parslet. The
31
+ Parslet::ParseFailed exception now contains a #cause field that can
32
+ be asked for an #ascii_tree as before.
33
+ Cleaner internal error handling, not stateful in atoms anymore. Some
34
+ parsers will see correct error reporting for the first time. (issue #65)
35
+
36
+ + Made it possible to pass a custom Parslet::Source implementor to #parse.
37
+ (see #63)
38
+
39
+ + #parse has now a second argument that is an options hash. See
40
+ Parslet::Atoms::Base#parse for documentation.
41
+
42
+ - VM engine on the way out. No benefit except for the intellectual
43
+ challenge.
44
+
45
+ = 1.3.0 / 5Mar2012
46
+
47
+ ! Parslet::Transform::Context is now much more well-behaved. It has
48
+ #respond_to? and #method_missing; it now looks like a plain old Ruby
49
+ object with instance variables and attribute readers.
50
+
51
+ - Grammar transforms turned out to be a dead end and have been removed.
52
+
53
+ ! A few problems in error message generation have been fixed. This will
54
+ improve diagnostics further.
55
+
56
+ + A VM driven parser engine: Removes the limitation that parsing needs a
57
+ lot of stack space, something dearly missing from Ruby 1.9.3 fibers.
58
+ This engine is experimental and might be removed in the future.
59
+
60
+ ! Interaction with mathn fixed - Line number generation will terminate.
61
+
62
+ . Internal reorganisation, removing cruft and bit rot.
63
+
64
+ = 1.2.3 / 22Sep2011
65
+
66
+ + Transform#apply can now be called with a hash as second argument. This
67
+ provides bindings and a way to inject context.
68
+
69
+ ! Fixes a bug thar modified parslet atoms in place, defeating oop chaining.
70
+ (#50)
71
+
72
+ = 1.2.1 / 6Jun2011
73
+
74
+ ! FIX: Input at the end of a parse raises Parslet::UnconsumedInput. (see
75
+ issue 18)
76
+
77
+ ! FIX: Unicode parsing should now work as expected. (see issue 38)
78
+
79
+ ! FIX: Slice#slice returned wrong bits at times (see issue 36).
80
+
81
+ = 1.2.0 / 4Feb2011
82
+
83
+ + Parslet::Parser is now also a grammar atom, it can be composed freely with
84
+ other atoms. (str('f') >> MiniLispParser.new >> str('b'))
85
+
86
+ + No strings, only slices are returned as part of the parser result.
87
+ Parslet::Slice is almost a string class, but one that remembers the
88
+ source offset. This has also bought us a slight speedup.
89
+
90
+ + require 'parslet/convenience' now brings #parse_with_debug to all parslets.
91
+ This is a consequence of the above change.
92
+
93
+ + Deprecates prsnt? and absnt? in favor of the more readable absent? and
94
+ prsnt?. Uses 3 bytes more RAM. The old variants will exist until we release
95
+ 2.0.
96
+
97
+ INTERNALLY
98
+
99
+ + Visitors now should have methods that all begin with 'visit_*'. #str
100
+ becomes #visit_str.
101
+
102
+ + Parslet::Atoms::Entity now takes only a block argument instead of context
103
+ and block.
104
+
105
+ = 1.1.1 / 4Feb2011
106
+
107
+ ! FIX: Line counting was broken by performance optimisations.
108
+
109
+ + Squeezed out another few drops of performance.
110
+
111
+ = 1.1.0 / 2Feb2011
112
+
113
+ + Uses return (fail/success), cached line counts, memoizing of parse results
114
+ and other tricks internally for at least an order of magnitude increase
115
+ in execution speed.
116
+
117
+ + str('foo').maybe will now return an empty string again. Use .as(...) to
118
+ name things and get back [] from #repeat and nil from #maybe.
119
+
120
+ + If you require 'parslet/atoms/visitor', you'll get an accept method on
121
+ all known Parslet::Atoms.
122
+
123
+ + If you require 'parslet/export', you can call #to_citrus and #to_treetop
124
+ to produce string versions of your grammar in those dialects.
125
+
126
+ + Requiring 'parslet/convenience' will given you a parse_with_debug on
127
+ your Parslet::Parser class. This prints some diagnostics on parse failure.
128
+ (Thanks to Florian Hanke)
129
+
130
+ = 1.0.1 / 17Jan2011
131
+
132
+ A happy new year!
133
+
134
+ ! FIX: Parslet::Transform was wrongly fixed earlier - it now wont mangle
135
+ hashes anymore. (Blake Sweeney)
136
+
137
+ + parslet/rig/rspec.rb contains useful rspec matchers. (R. Konstantin Haase)
138
+
139
+ = 1.0.0 / 29Dez2010
140
+
141
+ - #each_match was removed. There was some duplication of code that even
142
+ confused me - and we should not have 2 methods of achieving the same
143
+ goal.
144
+
145
+ + Full documentation. Fixed sdoc.
146
+
147
+ = 0.11.0 / 25Nov2010
148
+
149
+ ! Bugfixes to tree handling. Let's hope that was the last such significant
150
+ change to the core.
151
+
152
+ = 0.10.1 / 22Nov2010
153
+
154
+ + Allow match['a-z'], shortcut for match('[a-z]')
155
+
156
+ ! Fixed output inconsistencies (behaviour in connection to 'maybe')
157
+
158
+ = 0.10.0 / 22Nov2010
159
+
160
+ + Parslet::Transform now takes a block on initialisation, wherein you can
161
+ define all the rules directly.
162
+
163
+ + Parslet::Transform now only passes a hash to the block during transform
164
+ when its arity is 1. Otherwise all hash contents as bound as local
165
+ variables.
166
+
167
+ + Both inline and other documentation have been improved.
168
+
169
+ + You can now use 'subtree(:x)' to bind any subtree to x during tree pattern
170
+ matching.
171
+
172
+ + Transform classes can now include rules into class definition. This makes
173
+ Parser and Transformer behave the same.
174
+
175
+ = 0.9.0 / 28Oct2010
176
+ * More of everything: Examples, documentation, etc...
177
+
178
+ * Breaking change: Ruby's binary or ('|') is now used for alternatives,
179
+ instead of the division sign ('/') - this reduces the amount of
180
+ parenthesis needed for a grammar overall.
181
+
182
+ * parslet.maybe now yields the result or nil in case of parse failure. This
183
+ is probably better than the array it did before; the jury is still out on
184
+ that.
185
+
186
+ * parslet.repeat(min, max) is now valid syntax
187
+
188
+ = 0.1.0 / not released.
189
+
190
+ * Initial version. Classes for parsing, matching in the resulting trees
191
+ and transforming the trees into something more useful.
192
+
193
+ * Parses and outputs intermediary trees
194
+
195
+ * Matching of single elements and sequences
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+
2
+ Copyright (c) 2010 Kaspar Schiess
3
+
4
+ Permission is hereby granted, free of charge, to any person
5
+ obtaining a copy of this software and associated documentation
6
+ files (the "Software"), to deal in the Software without
7
+ restriction, including without limitation the rights to use,
8
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the
10
+ Software is furnished to do so, subject to the following
11
+ 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
18
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23
+ OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,70 @@
1
+ INTRODUCTION
2
+
3
+ Parslet makes developing complex parsers easy. It does so by
4
+
5
+ * providing the best error reporting possible
6
+ * not generating reams of code for you to debug
7
+
8
+ Parslet takes the long way around to make your job easier. It allows for
9
+ incremental language construction. Often, you start out small, implementing
10
+ the atoms of your language first; _parslet_ takes pride in making this
11
+ possible.
12
+
13
+ Eager to try this out? Please see the associated web site:
14
+ http://kschiess.github.com/parslet
15
+
16
+ SYNOPSIS
17
+
18
+ require 'parslet'
19
+ include Parslet
20
+
21
+ # parslet parses strings
22
+ str('foo').
23
+ parse('foo') # => "foo"@0
24
+
25
+ # it matches character sets
26
+ match['abc'].parse('a') # => "a"@0
27
+ match['abc'].parse('b') # => "b"@0
28
+ match['abc'].parse('c') # => "c"@0
29
+
30
+ # and it annotates its output
31
+ str('foo').as(:important_bit).
32
+ parse('foo') # => {:important_bit=>"foo"@0}
33
+
34
+ # you can construct parsers with just a few lines
35
+ quote = str('"')
36
+ simple_string = quote >> (quote.absent? >> any).repeat >> quote
37
+
38
+ simple_string.
39
+ parse('"Simple Simple Simple"') # => "\"Simple Simple Simple\""@0
40
+
41
+ # or by making a fuss about it
42
+ class Smalltalk < Parslet::Parser
43
+ root :smalltalk
44
+
45
+ rule(:smalltalk) { statements }
46
+ rule(:statements) {
47
+ # insert smalltalk parser here (outside of the scope of this readme)
48
+ }
49
+ end
50
+
51
+ # and then
52
+ Smalltalk.new.parse('smalltalk')
53
+
54
+ COMPATIBILITY
55
+
56
+ This library should work with most rubies. I've tested it with MRI 1.8
57
+ (except 1.8.6), 1.9, rbx-head, jruby. Please report as a bug if you encounter
58
+ issues.
59
+
60
+ Note that due to Ruby 1.8 internals, Unicode parsing is not supported on that
61
+ version.
62
+
63
+ On Mac OS X Lion, ruby-1.8.7-p352 has been known to segfault. Use
64
+ ruby-1.8.7-p334 for better results.
65
+
66
+ STATUS
67
+
68
+ At version 1.4.0 - See HISTORY.txt for changes.
69
+
70
+ (c) 2010, 2011, 2012 Kaspar Schiess
@@ -0,0 +1,49 @@
1
+ require 'rdoc/task'
2
+ require 'sdoc'
3
+
4
+ require 'rspec/core/rake_task'
5
+ require "rubygems/package_task"
6
+
7
+ desc "Run all tests: Exhaustive."
8
+ RSpec::Core::RakeTask.new
9
+
10
+ namespace :spec do
11
+ desc "Only run unit tests: Fast. "
12
+ RSpec::Core::RakeTask.new(:unit) do |task|
13
+ task.pattern = "spec/parslet/**/*_spec.rb"
14
+ end
15
+ end
16
+
17
+ task :default => :spec
18
+
19
+ # Generate documentation
20
+ RDoc::Task.new do |rdoc|
21
+ rdoc.title = "parslet - construction of parsers made easy"
22
+ rdoc.options << '--line-numbers'
23
+ rdoc.options << '--fmt' << 'shtml' # explictly set shtml generator
24
+ rdoc.template = 'direct' # lighter template used on railsapi.com
25
+ rdoc.main = "README"
26
+ rdoc.rdoc_files.include("README", "lib/**/*.rb")
27
+ rdoc.rdoc_dir = "rdoc"
28
+ end
29
+
30
+ desc 'Clear out RDoc'
31
+ task :clean => [:clobber_rdoc, :clobber_package]
32
+
33
+ # This task actually builds the gem.
34
+ task :gem => :spec
35
+ spec = eval(File.read('parslet.gemspec'))
36
+
37
+ desc "Generate the gem package."
38
+ Gem::PackageTask.new(spec) do |pkg|
39
+ pkg.gem_spec = spec
40
+ end
41
+
42
+ desc "Prints LOC stats"
43
+ task :stat do
44
+ %w(lib spec example).each do |dir|
45
+ loc = %x(find #{dir} -name "*.rb" | xargs wc -l | grep 'total').split.first.to_i
46
+ printf("%20s %d\n", dir, loc)
47
+ end
48
+ end
49
+
@@ -0,0 +1,70 @@
1
+ $:.unshift File.dirname(__FILE__) + "/../lib"
2
+
3
+ require "parslet"
4
+ require "pp"
5
+
6
+ # Parses strings like "var1 and (var2 or var3)" respecting operator precedence
7
+ # and parentheses. After that transforms the parse tree into an array of
8
+ # arrays like this:
9
+ #
10
+ # [["1", "2"], ["1", "3"]]
11
+ #
12
+ # The array represents a DNF (disjunctive normal form). Elements of outer
13
+ # array are connected with "or" operator, while elements of inner arrays are
14
+ # joined with "and".
15
+ #
16
+ class Parser < Parslet::Parser
17
+ rule(:space) { match[" "].repeat(1) }
18
+ rule(:space?) { space.maybe }
19
+
20
+ rule(:lparen) { str("(") >> space? }
21
+ rule(:rparen) { str(")") >> space? }
22
+
23
+ rule(:and_operator) { str("and") >> space? }
24
+ rule(:or_operator) { str("or") >> space? }
25
+
26
+ rule(:var) { str("var") >> match["0-9"].repeat(1).as(:var) >> space? }
27
+
28
+ # The primary rule deals with parentheses.
29
+ rule(:primary) { lparen >> or_operation >> rparen | var }
30
+
31
+ # Note that following rules are both right-recursive.
32
+ rule(:and_operation) {
33
+ (primary.as(:left) >> and_operator >>
34
+ and_operation.as(:right)).as(:and) |
35
+ primary }
36
+
37
+ rule(:or_operation) {
38
+ (and_operation.as(:left) >> or_operator >>
39
+ or_operation.as(:right)).as(:or) |
40
+ and_operation }
41
+
42
+ # We start at the lowest precedence rule.
43
+ root(:or_operation)
44
+ end
45
+
46
+ class Transformer < Parslet::Transform
47
+ rule(:var => simple(:var)) { [[String(var)]] }
48
+
49
+ rule(:or => { :left => subtree(:left), :right => subtree(:right) }) do
50
+ (left + right)
51
+ end
52
+
53
+ rule(:and => { :left => subtree(:left), :right => subtree(:right) }) do
54
+ res = []
55
+ left.each do |l|
56
+ right.each do |r|
57
+ res << (l + r)
58
+ end
59
+ end
60
+ res
61
+ end
62
+ end
63
+
64
+ pp tree = Parser.new.parse("var1 and (var2 or var3)")
65
+ # {:and=>
66
+ # {:left=>{:var=>"1"@3},
67
+ # :right=>{:or=>{:left=>{:var=>"2"@13}, :right=>{:var=>"3"@21}}}}}
68
+ pp Transformer.new.apply(tree)
69
+ # [["1", "2"], ["1", "3"]]
70
+
@@ -0,0 +1,153 @@
1
+ # A simple integer calculator to answer the question about how to do
2
+ # left and right associativity in parslet (PEG) once and for all.
3
+
4
+ $:.unshift File.dirname(__FILE__) + "/../lib"
5
+
6
+ require 'rspec'
7
+ require 'parslet'
8
+ require 'parslet/rig/rspec'
9
+
10
+ # This is the parsing stage. It expresses left associativity by compiling
11
+ # list of things that have the same associativity.
12
+ class CalcParser < Parslet::Parser
13
+ root :addition
14
+
15
+ rule(:addition) {
16
+ multiplication.as(:l) >> (add_op >> multiplication.as(:r)).repeat(1) |
17
+ multiplication
18
+ }
19
+
20
+ rule(:multiplication) {
21
+ integer.as(:l) >> (mult_op >> integer.as(:r)).repeat(1) |
22
+ integer }
23
+
24
+ rule(:integer) { digit.repeat(1).as(:i) >> space? }
25
+
26
+ rule(:mult_op) { match['*/'].as(:o) >> space? }
27
+ rule(:add_op) { match['+-'].as(:o) >> space? }
28
+
29
+ rule(:digit) { match['0-9'] }
30
+ rule(:space?) { match['\s'].repeat }
31
+ end
32
+
33
+ # Classes for the abstract syntax tree.
34
+ Int = Struct.new(:int) {
35
+ def eval; self end
36
+ def op(operation, other)
37
+ left = int
38
+ right = other.int
39
+
40
+ Int.new(
41
+ case operation
42
+ when '+'
43
+ left + right
44
+ when '-'
45
+ left - right
46
+ when '*'
47
+ left * right
48
+ when '/'
49
+ left / right
50
+ end)
51
+ end
52
+ def to_i
53
+ int
54
+ end
55
+ }
56
+ Seq = Struct.new(:sequence) {
57
+ def eval
58
+ sequence.reduce { |accum, operation|
59
+ operation.call(accum) }
60
+ end
61
+ }
62
+ LeftOp = Struct.new(:operation, :right) {
63
+ def call(left)
64
+ left = left.eval
65
+ right = self.right.eval
66
+
67
+ left.op(operation, right)
68
+ end
69
+ }
70
+
71
+ # Transforming intermediary syntax tree into a real AST.
72
+ class CalcTransform < Parslet::Transform
73
+ rule(i: simple(:i)) { Int.new(Integer(i)) }
74
+ rule(o: simple(:o), r: simple(:i)) { LeftOp.new(o, i) }
75
+ rule(l: simple(:i)) { i }
76
+ rule(sequence(:seq)) { Seq.new(seq) }
77
+ end
78
+
79
+ # And this calls everything in the right order.
80
+ def calculate(str)
81
+ intermediary_tree = CalcParser.new.parse(str)
82
+ abstract_tree = CalcTransform.new.apply(intermediary_tree)
83
+ result = abstract_tree.eval
84
+
85
+ result.to_i
86
+ end
87
+
88
+ # A test suite for the above parser
89
+ describe CalcParser do
90
+ let(:p) { described_class.new }
91
+ describe '#integer' do
92
+ let(:i) { p.integer }
93
+ it "parses integers" do
94
+ i.should parse('1')
95
+ i.should parse('123')
96
+ end
97
+ it "consumes trailing white space" do
98
+ i.should parse('123 ')
99
+ end
100
+ it "doesn't parse floats" do
101
+ i.should_not parse('1.3')
102
+ end
103
+ end
104
+ describe '#multiplication' do
105
+ let(:m) { p.multiplication }
106
+ it "parses simple multiplication" do
107
+ m.should parse('1*2')
108
+ end
109
+ it "parses division" do
110
+ m.should parse('1/2')
111
+ end
112
+ end
113
+ describe '#addition' do
114
+ let(:a) { p.addition }
115
+
116
+ it "parses simple addition" do
117
+ a.should parse('1+2')
118
+ a.should parse('1+2+3-4')
119
+ end
120
+ end
121
+ end
122
+ describe CalcTransform do
123
+ def t(obj)
124
+ described_class.new.apply(obj)
125
+ end
126
+
127
+ it "transforms integers" do
128
+ t(i: '1').should == Int.new(1)
129
+ end
130
+ it "unwraps left operand" do
131
+ t(l: :obj).should == :obj
132
+ end
133
+ end
134
+ describe 'whole computation specs' do
135
+ def self.result_of(str, int)
136
+ it(str) { calculate(str).should == int }
137
+ end
138
+
139
+ result_of '1+1', 2
140
+ result_of '1-1-1', -1
141
+ result_of '1+1+3*5/2', 9
142
+ result_of '123*2', 246
143
+ end
144
+
145
+
146
+ # Enable these if you want to change the code.
147
+ # RSpec::Core::Runner.run([], $stderr, $stdout)
148
+
149
+ str = ARGV.join
150
+ str = '123*2' if str.match(/^\s*$/)
151
+
152
+ print "#{str} (command line): -> "
153
+ puts calculate(str)