exalted_math 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+ module ExaltedMath
3
+ class Node
4
+ def initialize(*)
5
+ end
6
+
7
+ def constant?
8
+ true
9
+ end
10
+
11
+ def value(context={})
12
+ nil
13
+ end
14
+
15
+ def simplify
16
+ self
17
+ end
18
+
19
+ def valid?(*)
20
+ false
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+ require 'exalted_math/node/node'
3
+ module ExaltedMath
4
+ class Node
5
+ class Number < Node
6
+ def initialize(value)
7
+ @value = value
8
+ end
9
+
10
+ def constant?
11
+ true
12
+ end
13
+
14
+ def value(context={})
15
+ @value
16
+ end
17
+
18
+ def ==(o)
19
+ return false unless self.class === o
20
+ @value == o.value
21
+ end
22
+
23
+ def valid?(*)
24
+ true
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+ require 'exalted_math/node/node'
3
+ module ExaltedMath
4
+ class Node
5
+ class Operator < Node
6
+ attr_reader :left, :right
7
+
8
+ def initialize(left, right)
9
+ @left = left
10
+ @right = right
11
+ end
12
+
13
+ def constant?
14
+ left.constant? and right.constant?
15
+ end
16
+
17
+ def value(context={})
18
+ raise NotImplementedError
19
+ end
20
+
21
+ def simplify
22
+ if constant?
23
+ Number.new(value)
24
+ else
25
+ self.class.new(left.simplify, right.simplify)
26
+ end
27
+ end
28
+
29
+ def ==(o)
30
+ return false unless self.class === o
31
+ left == o.left && right == o.right
32
+ end
33
+
34
+ def valid?(stats=[])
35
+ left.valid?(stats) && right.valid?(stats)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ require 'exalted_math/node/node'
3
+ require 'exalted_math/node/operator'
4
+ module ExaltedMath
5
+ class Node
6
+ class Subtract < Operator
7
+ def value(context={})
8
+ left.value(context) - right.value(context)
9
+ end
10
+ end
11
+ end
12
+ end
13
+
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/ruby
2
+ # Jonathan D. Stott <jonathan.stott@gmail.com>
3
+ $LOAD_PATH.unshift 'lib'
4
+ require 'exalted_math'
5
+
6
+ CONTEXT = { 'essence' => 4, 'willpower' => 9, 'compassion' => 4, 'conviction' => 3, 'temperance' => 4, 'valor' => 2 }.freeze
7
+
8
+ string = '(Essence * 4) + Willpower + highest[2](compassion,conviction,temperance,valor) + 11'.gsub(/\s/,'')
9
+ times = []
10
+
11
+ 5.times do
12
+ start = Time.now
13
+ 10_000.times do
14
+ ExaltedMath::MathsParser.new.ast(string)
15
+ end
16
+ end_time = Time.now
17
+ times << end_time - start
18
+ end
19
+
20
+ sum = times.inject(0.0) { |total, time| total += time }
21
+
22
+ puts times
23
+ puts "Average: #{sum/times.size}"
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/ruby
2
+ # Jonathan D. Stott <jonathan.stott@gmail.com>
3
+ require 'spec_helper'
4
+ require 'exalted_math/node'
5
+
6
+ include ExaltedMath
7
+
8
+
9
+ describe "ExaltedMath::Node" do
10
+ before do
11
+ @three = Node::Number.new(3)
12
+ @seven = Node::Number.new(7)
13
+ @foo = Node::Stat.new('foo')
14
+ @bar = Node::Spec.new('bar')
15
+ @invalid = Node::Stat.new('invalid')
16
+ @add = Node::Add.new( @three, @seven)
17
+ @sub = Node::Subtract.new(@three, @seven)
18
+ @mul = Node::Multiply.new(@three, @seven)
19
+ @div = Node::Divide.new(@seven, @three)
20
+ @add_foo = Node::Add.new(@three, @foo)
21
+ @sub_foo = Node::Subtract.new(@three, @foo)
22
+ @mul_foo = Node::Multiply.new(@three, @foo)
23
+ @div_foo = Node::Divide.new(@three, @foo)
24
+ @nested = Node::Multiply.new(@add, @sub_foo)
25
+ @min = Node::Minimum.new([@three, @seven], 1)
26
+ @max = Node::Maximum.new([@three, @seven], 1 )
27
+ @context = { 'foo' => 3, 'bar' => 4 }
28
+ end
29
+ it "a number is constant" do
30
+ @three.should.be.constant
31
+ end
32
+
33
+ it "a stat is not constant" do
34
+ @foo.should.not.be.constant
35
+ end
36
+
37
+ it "an add of two constants is constant" do
38
+ @add.should.be.constant
39
+ end
40
+ it "an sub of two constants is constant" do
41
+ @sub.should.be.constant
42
+ end
43
+ it "an mul of two constants is constant" do
44
+ @mul.should.be.constant
45
+ end
46
+ it "an div of two constants is constant" do
47
+ @div.should.be.constant
48
+ end
49
+ it "a min of constants is constant" do
50
+ @min.should.be.constant
51
+ end
52
+ it "a max of constants is constant" do
53
+ @max.should.be.constant
54
+ end
55
+
56
+ it "an add of constant and not constant is not constant" do
57
+ @add_foo.should.not.be.constant
58
+ end
59
+ it "an sub of constant and not constant is not constant" do
60
+ @sub_foo.should.not.be.constant
61
+ end
62
+ it "an mul of constant and not constant is not constant" do
63
+ @mul_foo.should.not.be.constant
64
+ end
65
+ it "an div of constant and not constant is not constant" do
66
+ @div_foo.should.not.be.constant
67
+ end
68
+
69
+ it "A number is valid" do
70
+ @three.valid?(@context.keys).should.be.true
71
+ end
72
+
73
+ it "A stat is valid if it is in the array of valid keys" do
74
+ @foo.valid?(@context.keys).should.be.true
75
+ end
76
+
77
+ it "A stat is not valid if it is not in the array of valid keys" do
78
+ @invalid.valid?(@context.keys).should.not.be.true
79
+ end
80
+
81
+ it "An add is valid if both its keys are valid" do
82
+ @add.valid?(@context.keys).should.be.true
83
+ end
84
+ it "An sub is valid if both its keys are valid" do
85
+ @sub.valid?(@context.keys).should.be.true
86
+ end
87
+ it "An mul is valid if both its keys are valid" do
88
+ @mul.valid?(@context.keys).should.be.true
89
+ end
90
+ it "An div is valid if both its keys are valid" do
91
+ @div.valid?(@context.keys).should.be.true
92
+ end
93
+ it "An add is invalid if one key is invalid" do
94
+ @add = Node::Add.new(@three, @invalid)
95
+ @add.valid?(@context.keys).should.not.be.true
96
+ end
97
+ it "An sub is invalid if one key is invalid" do
98
+ @sub = Node::Subtract.new(@three, @invalid)
99
+ @sub.valid?(@context.keys).should.not.be.true
100
+ end
101
+ it "An mul is invalid if one key is invalid" do
102
+ @mul = Node::Multiply.new(@three, @invalid)
103
+ @mul.valid?(@context.keys).should.not.be.true
104
+ end
105
+ it "An div is invalid if one key is invalid" do
106
+ @div = Node::Divide.new(@three, @invalid)
107
+ @div.valid?(@context.keys).should.not.be.true
108
+ end
109
+
110
+ it "the value of a number is the number" do
111
+ @three.value(@context).should == 3
112
+ @seven.value(@context).should == 7
113
+ end
114
+
115
+ it "the value of a stat is looked up in the context" do
116
+ @foo.value(@context).should == @context['foo']
117
+ @bar.value(@context).should == @context['bar']
118
+ end
119
+
120
+ it "the value of an add is the sum of the two children" do
121
+ @add.value(@context).should == 10
122
+ end
123
+
124
+ it "the value of an sub is first child minus the second" do
125
+ @sub.value(@context).should == -4
126
+ end
127
+
128
+ it "the value of an mul is the two children multiplied together" do
129
+ @mul.value(@context).should == 21
130
+ end
131
+
132
+ it "the value of a div is the first child divided by the second" do
133
+ @div.value(@context).should == 2
134
+ end
135
+
136
+ it "the value of a min is the mininum value" do
137
+ @min.value(@context).should == 3
138
+ end
139
+ it "the value of a max is the maxinum value" do
140
+ @max.value(@context).should == 7
141
+ end
142
+
143
+ it "it walks the whole tree to compute a value" do
144
+ @nested.value(@context).should == 0
145
+ end
146
+
147
+ it "can simplify a constant add" do
148
+ @add.simplify.should == Node::Number.new(10)
149
+ end
150
+
151
+ it "A number is simple" do
152
+ @three.simplify.should == Node::Number.new(3)
153
+ end
154
+
155
+ it "A stat is simple" do
156
+ @foo.simplify.should == Node::Stat.new('foo')
157
+ end
158
+
159
+ it "A non-constant add isn't simplified" do
160
+ @add_foo.simplify.should == Node::Add.new(@three, @foo)
161
+ end
162
+ it "A constant branches are simplified" do
163
+ @nested.simplify.should == Node::Multiply.new(Node::Number.new(10), @sub_foo)
164
+ end
165
+
166
+
167
+ node_inequality Node::Number.new(1), Node::Number.new(2)
168
+ node_inequality Node::Number.new(1), Node::Spec.new('foo')
169
+ node_inequality Node::Number.new(1), Node::Stat.new('foo')
170
+ node_inequality Node::Stat.new('bar'), Node::Stat.new('foo')
171
+ node_inequality Node::Spec.new('foo'), Node::Stat.new('foo')
172
+ node_inequality Node::Minimum.new([Node::Number.new(1), Node::Stat.new('foo')], 1), Node::Minimum.new([Node::Number.new(1), Node::Stat.new('foo')], 2)
173
+ node_inequality Node::Maximum.new([Node::Number.new(1), Node::Stat.new('foo')], 1), Node::Maximum.new([Node::Number.new(1), Node::Stat.new('foo')], 2)
174
+ node_inequality Node::Minimum.new([Node::Number.new(1), Node::Spec.new('foo')], 1), Node::Minimum.new([Node::Number.new(1), Node::Stat.new('foo')], 1)
175
+ node_inequality Node::Minimum.new([Node::Number.new(1), Node::Spec.new('foo')], 1), Node::Maximum.new([Node::Number.new(1), Node::Spec.new('foo')], 1)
176
+
177
+ node_equality Node::Number.new(1), Node::Number.new(1)
178
+ node_equality Node::Spec.new('foo'), Node::Spec.new('foo')
179
+ node_equality Node::Stat.new('foo'), Node::Stat.new('foo')
180
+ node_equality Node::Minimum.new([Node::Number.new(1), Node::Stat.new('foo')], 1), Node::Minimum.new([Node::Number.new(1), Node::Stat.new('foo')], 1)
181
+ node_equality Node::Maximum.new([Node::Number.new(1), Node::Stat.new('foo')], 1), Node::Maximum.new([Node::Number.new(1), Node::Stat.new('foo')], 1)
182
+ end
183
+
@@ -1,45 +1,35 @@
1
1
  #!/usr/bin/ruby
2
- # Jonathan D. Stott <jonathan.stott@gmail.com>
3
2
  require 'spec_helper'
4
3
  require 'exalted_math'
5
4
 
6
- include Exalted
5
+ include ExaltedMath
7
6
 
8
- class ParserFailure < Bacon::Error
9
- def initialize(failure)
10
- super :failure, failure
11
- end
12
- end
13
7
  describe "Exalted::MathParser" do
14
8
  before do
15
- @parser = Exalted::MathsParser.new
9
+ @parser = ExaltedMath::MathsParser.new
16
10
  end
17
11
 
18
12
  [
19
- [' 4 ', Ast.num(4)],
20
- [' -1 ', Ast.num(-1)],
21
- [' 3 * 4 ', Ast.mul(Ast.num(3), Ast.num(4) )],
22
- [' 3 - 4 ', Ast.sub(Ast.num(3), Ast.num(4) )],
23
- [' 3 + 4 ', Ast.add(Ast.num(3), Ast.num(4) )],
24
- [' 6 / 3 ', Ast.div(Ast.num(6), Ast.num(3) )],
25
- [' spec:"Beating stuff" ', Ast.spec("Beating stuff")],
26
- ['Essence * 4', Ast.mul(Ast.stat('essence'), Ast.num(4) )],
27
- ['(Essence * 4) + Willpower', Ast.add(Ast.mul(Ast.stat('essence'), Ast.num(4) ), Ast.stat('willpower'))],
28
- ['highest[2](compassion,conviction,temperance,valor)', Ast.max(2, [Ast.stat('compassion'),Ast.stat('conviction'),Ast.stat('temperance'),Ast.stat('valor') ])],
29
- ['min(compassion,conviction,temperance,valor)', Ast.min(1, [Ast.stat('compassion'),Ast.stat('conviction'),Ast.stat('temperance'),Ast.stat('valor') ])]
13
+ [' 4 ', Node::Number.new(4)],
14
+ [' -1 ', Node::Number.new(-1)],
15
+ [' 3 * 4 ', Node::Multiply.new(Node::Number.new(3), Node::Number.new(4) )],
16
+ [' 3 - 4 ', Node::Subtract.new(Node::Number.new(3), Node::Number.new(4) )],
17
+ [' 3 + 4 ', Node::Add.new(Node::Number.new(3), Node::Number.new(4) )],
18
+ [' 6 / 3 ', Node::Divide.new(Node::Number.new(6), Node::Number.new(3) )],
19
+ [' spec:"Beating stuff" ', Node::Spec.new("Beating stuff")],
20
+ ['Essence * 4', Node::Multiply.new(Node::Stat.new('essence'), Node::Number.new(4) )],
21
+ ['(Essence * 4) + Willpower', Node::Add.new(Node::Multiply.new(Node::Stat.new('essence'), Node::Number.new(4) ), Node::Stat.new('willpower'))],
22
+ ['highest[2](compassion,conviction,temperance,valor)', Node::Maximum.new([Node::Stat.new('compassion'),Node::Stat.new('conviction'),Node::Stat.new('temperance'),Node::Stat.new('valor') ], 2)],
23
+ ['min(compassion,conviction,temperance,valor)', Node::Minimum.new([Node::Stat.new('compassion'),Node::Stat.new('conviction'),Node::Stat.new('temperance'),Node::Stat.new('valor') ], 1)]
30
24
  ].each do |string, ast|
31
25
  it "parses '#{string}'" do
32
- success, result = @parser.ast(string)
33
- raise ParserFailure, result unless success
34
- success.should.be.true
26
+ result = @parser.ast(string)
35
27
  result.should == ast
36
28
  end
37
29
  end
38
30
 
39
31
  it "Doesn't care about spaces" do
40
- success, result = @parser.ast(' ( Essence * 4 ) + Willpower + highest[2]( compassion , conviction ) ')
41
- raise ParserFailure, result unless success
42
- success.should.be.true
32
+ proc { @parser.ast(' ( Essence * 4 ) + Willpower + highest[2]( compassion , conviction ) ') }.should.not.raise(ParseFailedError)
43
33
  end
44
34
  end
45
35
 
@@ -3,3 +3,16 @@
3
3
  require 'bacon'
4
4
 
5
5
  Bacon.summary_on_exit
6
+
7
+ class Bacon::Context
8
+ def node_inequality(left, right)
9
+ it "`#{left.inspect}' != `#{right.inspect}`" do
10
+ left.should.not == right
11
+ end
12
+ end
13
+ def node_equality(left, right)
14
+ it "`#{left.inspect}' == `#{right.inspect}`" do
15
+ left.should == right
16
+ end
17
+ end
18
+ end
metadata CHANGED
@@ -1,125 +1,120 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: exalted_math
3
- version: !ruby/object:Gem::Version
4
- hash: 29
5
- prerelease: false
6
- segments:
7
- - 0
8
- - 1
9
- - 3
10
- version: 0.1.3
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ prerelease:
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Jonathan Stott
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2010-06-09 00:00:00 +01:00
19
- default_executable:
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
12
+ date: 2012-05-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
22
15
  name: treetop
23
- prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
25
17
  none: false
26
- requirements:
18
+ requirements:
27
19
  - - ~>
28
- - !ruby/object:Gem::Version
29
- hash: 7
30
- segments:
31
- - 1
32
- - 4
33
- version: "1.4"
20
+ - !ruby/object:Gem::Version
21
+ version: '1.4'
34
22
  type: :runtime
35
- version_requirements: *id001
36
- - !ruby/object:Gem::Dependency
37
- name: bacon
38
23
  prerelease: false
39
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.4'
30
+ - !ruby/object:Gem::Dependency
31
+ name: bacon
32
+ requirement: !ruby/object:Gem::Requirement
40
33
  none: false
41
- requirements:
42
- - - ">="
43
- - !ruby/object:Gem::Version
44
- hash: 3
45
- segments:
46
- - 0
47
- version: "0"
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
48
38
  type: :development
49
- version_requirements: *id002
50
- - !ruby/object:Gem::Dependency
51
- name: yard
52
39
  prerelease: false
53
- requirement: &id003 !ruby/object:Gem::Requirement
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: yard
48
+ requirement: !ruby/object:Gem::Requirement
54
49
  none: false
55
- requirements:
56
- - - ">="
57
- - !ruby/object:Gem::Version
58
- hash: 3
59
- segments:
60
- - 0
61
- version: "0"
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
62
54
  type: :development
63
- version_requirements: *id003
64
- description: |-
65
- Parsing and evaluation of simple maths expressions for Exalted
66
-
67
- This intended to aid in evaluating simple calculations which appear on character sheets, especially for Exalted.
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: ! 'Parsing and evaluation of simple maths expressions for Exalted
63
+
64
+
65
+ This intended to aid in evaluating simple calculations which appear on character
66
+ sheets, especially for Exalted.'
68
67
  email: jonathan.stott@gmail.com
69
68
  executables: []
70
-
71
69
  extensions: []
72
-
73
- extra_rdoc_files:
70
+ extra_rdoc_files:
74
71
  - README.rdoc
75
- files:
76
- - .gitignore
72
+ files:
77
73
  - README.rdoc
78
74
  - Rakefile
79
75
  - VERSION
80
76
  - exalted_math.gemspec
81
77
  - lib/exalted_math.rb
82
- - lib/exalted_math/ast.rb
83
78
  - lib/exalted_math/math.rb
84
79
  - lib/exalted_math/math.treetop
85
- - spec/ast_spec.rb
80
+ - lib/exalted_math/node.rb
81
+ - lib/exalted_math/node/add.rb
82
+ - lib/exalted_math/node/divide.rb
83
+ - lib/exalted_math/node/list.rb
84
+ - lib/exalted_math/node/maximum.rb
85
+ - lib/exalted_math/node/minimum.rb
86
+ - lib/exalted_math/node/multiply.rb
87
+ - lib/exalted_math/node/named.rb
88
+ - lib/exalted_math/node/node.rb
89
+ - lib/exalted_math/node/number.rb
90
+ - lib/exalted_math/node/operator.rb
91
+ - lib/exalted_math/node/subtract.rb
92
+ - script/benchmark.rb
93
+ - spec/node_spec.rb
86
94
  - spec/parser_spec.rb
87
95
  - spec/spec_helper.rb
88
- has_rdoc: true
89
96
  homepage: http://github.com/namelessjon/exalted_math
90
97
  licenses: []
91
-
92
98
  post_install_message:
93
- rdoc_options:
94
- - --charset=UTF-8
95
- require_paths:
99
+ rdoc_options: []
100
+ require_paths:
96
101
  - lib
97
- required_ruby_version: !ruby/object:Gem::Requirement
102
+ required_ruby_version: !ruby/object:Gem::Requirement
98
103
  none: false
99
- requirements:
100
- - - ">="
101
- - !ruby/object:Gem::Version
102
- hash: 3
103
- segments:
104
- - 0
105
- version: "0"
106
- required_rubygems_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ! '>='
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
109
  none: false
108
- requirements:
109
- - - ">="
110
- - !ruby/object:Gem::Version
111
- hash: 3
112
- segments:
113
- - 0
114
- version: "0"
110
+ requirements:
111
+ - - ! '>='
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
115
114
  requirements: []
116
-
117
115
  rubyforge_project:
118
- rubygems_version: 1.3.7
116
+ rubygems_version: 1.8.24
119
117
  signing_key:
120
118
  specification_version: 3
121
119
  summary: Parsing and evaluation of simple maths expressions for Exalted
122
- test_files:
123
- - spec/ast_spec.rb
124
- - spec/spec_helper.rb
125
- - spec/parser_spec.rb
120
+ test_files: []