ghazel-parslet 1.4.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.
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)