kalc 0.5.8 → 0.5.9

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.
data/README.textile CHANGED
@@ -36,9 +36,9 @@ OR(1 > 2, 3 < 2, 8 == 8) # true
36
36
  h3. Variable assignment
37
37
 
38
38
  <pre>
39
- a = 1
40
- b = 2
41
- d = a + b
39
+ a := 1
40
+ b := 2
41
+ d := a + b
42
42
  </pre>
43
43
 
44
44
  h3. Creating functions
@@ -62,6 +62,40 @@ DEFINE SAMPLE_LOOP(a) {
62
62
  }
63
63
  </pre>
64
64
 
65
+ h3. Weirdness
66
+
67
+ And here is where it gets a bit weird. It has to look a bit like
68
+ Excel, so you can expect things to look odd in places.
69
+
70
+ For example, here is how you compare 2 variables:
71
+
72
+ <pre>
73
+ # Assign '1' to 'a' and '2' to 'b'
74
+
75
+ a := 1
76
+ b := 2
77
+
78
+ # Does 'a' equal 'b'?
79
+
80
+ a = b
81
+
82
+ > false
83
+
84
+ # Also, you can do this:
85
+
86
+ a == b
87
+
88
+ > false
89
+
90
+ (a == a) && (b = b)
91
+
92
+ > true
93
+
94
+ </pre>
95
+
96
+ '=' and '==' are both equality operators. Use
97
+ ':=' for assignment.
98
+
65
99
  h4. More inside
66
100
 
67
101
  Not everything is documented yet. As you can see, it is a mix of
data/bin/kalc CHANGED
@@ -6,14 +6,9 @@ $LOAD_PATH.unshift(kalc_dir) unless $LOAD_PATH.include?(kalc_dir)
6
6
  require "kalc"
7
7
  require "pp"
8
8
 
9
- input = File.read(ARGV.first)
9
+ if ARGV.first
10
+ input = File.read(ARGV.first)
10
11
 
11
- grammar = Kalc::Grammar.new
12
- g = grammar.parse(input)
13
-
14
- transform = Kalc::Transform.new
15
- ast = transform.apply(g)
16
-
17
-
18
- interpreter = Kalc::Interpreter.new
19
- puts interpreter.run(ast)
12
+ r = Kalc::Runner.new
13
+ puts r.run(input)
14
+ end
data/examples/add.kalc ADDED
@@ -0,0 +1 @@
1
+ 1+1
@@ -0,0 +1,22 @@
1
+ DEFINE AVERAGE(x, y) {
2
+ (x + y) / 2
3
+ }
4
+
5
+ DEFINE GOOD_ENOUGH(guess, x) {
6
+ (ABS(SQUARE(guess) - x)) < 0.001
7
+ }
8
+
9
+ DEFINE IMPROVE(guess, x) {
10
+ a := x / guess;
11
+ AVERAGE(guess, a)
12
+ }
13
+
14
+ DEFINE SQRT_ITER(guess, x) {
15
+ IF(GOOD_ENOUGH(guess, x), guess, SQRT_ITER(IMPROVE(guess, x), x))
16
+ }
17
+
18
+ DEFINE NEWTON_SQRT(x) {
19
+ SQRT_ITER(1.0, x)
20
+ }
21
+
22
+ PUTS NEWTON_SQRT(1024)
@@ -0,0 +1 @@
1
+ 100 - 10
data/lib/kalc.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'parslet'
2
+ require 'logger'
2
3
 
3
4
  require 'kalc/version'
4
5
  require 'kalc/grammar'
@@ -9,6 +10,45 @@ require 'kalc/interpreter'
9
10
  require 'kalc/repl'
10
11
 
11
12
  module Kalc
13
+ class Runner
14
+
15
+ attr_accessor :grammar
16
+ attr_accessor :transform
17
+ attr_accessor :interpreter
18
+ attr_accessor :ast
12
19
 
20
+ def initialize(debug = false)
21
+ @debug = debug
22
+ @log = Logger.new(STDOUT)
23
+ load_environment
24
+ end
25
+
26
+ def reload
27
+ load_environment
28
+ end
29
+
30
+ def run(expression)
31
+ if @debug
32
+ @log.debug "Evaluating #{expression}"
33
+ g = @grammar.parse_with_debug(expression)
34
+ else
35
+ g = @grammar.parse(expression)
36
+ end
37
+ @ast = @transform.apply(g)
38
+ @interpreter.run(ast)
39
+ end
40
+
41
+ private
42
+ def load_environment
43
+ @log.debug "Loading grammar" if @debug
44
+ @grammar = Kalc::Grammar.new
45
+ @log.debug "Loading transform" if @debug
46
+ @transform = Kalc::Transform.new
47
+ @log.debug "Loading interpreter" if @debug
48
+ @interpreter = Kalc::Interpreter.new
49
+ @log.debug "Loading stdlib" if @debug
50
+ @interpreter.load_stdlib(@grammar, @transform)
51
+ end
52
+ end
13
53
  end
14
54
 
data/lib/kalc/ast.rb CHANGED
@@ -66,8 +66,6 @@ module Kalc
66
66
  end
67
67
  end
68
68
 
69
-
70
-
71
69
  class BooleanValue
72
70
  attr_reader :value
73
71
 
@@ -159,6 +157,8 @@ module Kalc
159
157
  @left <= @right
160
158
  when '>='
161
159
  @left >= @right
160
+ when '='
161
+ @left == @right
162
162
  when '=='
163
163
  @left == @right
164
164
  when '!='
data/lib/kalc/grammar.rb CHANGED
@@ -74,7 +74,8 @@ class Kalc::Grammar < Parslet::Parser
74
74
  :equal => '==',
75
75
  :not_equal => '!=',
76
76
 
77
- :assign => '=',
77
+ :assign => ':=',
78
+ :excel_equal => '=',
78
79
  :question_mark => '?',
79
80
 
80
81
  :subtract => '-',
@@ -194,10 +195,10 @@ class Kalc::Grammar < Parslet::Parser
194
195
  relational_expression.as(:right)).repeat.as(:ops)
195
196
  }
196
197
 
197
- # 1 == 2
198
+ # 1 = 2
198
199
  rule(:equality_expression) {
199
200
  relational_expression.as(:left) >>
200
- ((equal | not_equal) >>
201
+ ((excel_equal | equal | not_equal) >>
201
202
  equality_expression.as(:right)).repeat.as(:ops)
202
203
  }
203
204
 
@@ -104,7 +104,7 @@ module Kalc
104
104
 
105
105
  end
106
106
 
107
- def load_stdlibs(grammar, transform)
107
+ def load_stdlib(grammar, transform)
108
108
  stdlib = "#{File.dirname(__FILE__)}/stdlib.kalc"
109
109
  input = File.read(stdlib)
110
110
  g = grammar.parse(input)
data/lib/kalc/repl.rb CHANGED
@@ -10,15 +10,8 @@ module Kalc
10
10
 
11
11
  puts heading
12
12
 
13
- puts "= Loading grammar"
14
- @grammar = Kalc::Grammar.new
15
-
16
- puts "= Loading transform"
17
- @transform = Kalc::Transform.new
18
-
19
- puts "= Loading interpreter"
20
- @interpreter = Kalc::Interpreter.new
21
- @interpreter.load_stdlibs(@grammar, @transform)
13
+ # Load Kalc with debug
14
+ @kalc = Kalc::Runner.new(true)
22
15
 
23
16
  puts "You are ready to go. Have fun!"
24
17
  puts ""
@@ -27,7 +20,7 @@ module Kalc
27
20
 
28
21
  function_list = [
29
22
  'quit', 'exit', 'functions', 'variables', 'ast'
30
- ] + @interpreter.env.functions.map { |f| f.first }
23
+ ] + @kalc.interpreter.env.functions.map { |f| f.first }
31
24
 
32
25
  begin
33
26
  comp = proc { |s| function_list.grep( /^#{Regexp.escape(s)}/ ) }
@@ -40,15 +33,13 @@ module Kalc
40
33
  when (input == 'quit' || input == 'exit')
41
34
  break
42
35
  when input == "functions"
43
- puts @interpreter.env.functions.map { |f| f.first }.join(", ")
36
+ puts @kalc.interpreter.env.functions.map { |f| f.first }.join(", ")
44
37
  when input == 'variables'
45
- puts @interpreter.env.variables.map { |v| "#{v[0]} = #{v[1]}" }.join("\n\r")
38
+ puts @kalc.interpreter.env.variables.map { |v| "#{v[0]} = #{v[1]}" }.join("\n\r")
46
39
  when input == 'ast'
47
- pp ast
40
+ pp @kalc.ast
48
41
  when input != ""
49
- g = @grammar.parse_with_debug(input)
50
- ast = @transform.apply(g)
51
- puts @interpreter.run(ast)
42
+ puts @kalc.run(input)
52
43
  end
53
44
  rescue Parslet::ParseFailed => e
54
45
  puts e, g.root.error_tree
data/lib/kalc/stdlib.kalc CHANGED
@@ -1,17 +1,28 @@
1
+ DEFINE PLUS_ONE(x) {
2
+ x + 1
3
+ }
4
+
5
+ DEFINE MINUS_ONE(x) {
6
+ x - 1
7
+ }
8
+
1
9
  DEFINE SQUARE(x) {
2
- x * x * x
10
+ x * x
11
+ }
12
+
13
+ DEFINE CUBE(x) {
14
+ x * SQUARE(x)
3
15
  }
4
16
 
5
17
  DEFINE FIB(x) {
6
- IF(x == 0, 0, IF(x == 1, 1, (FIB(x - 1) + FIB(x - 2))))
18
+ IF(x = 0, 0, IF(x = 1, 1, (FIB(x - 1) + FIB(x - 2))))
7
19
  }
8
20
 
9
21
  DEFINE FACTORIAL(x) {
10
- IF(x == 0, 1, (x * FACTORIAL(x - 1)))
22
+ IF(x = 0, 1, (x * FACTORIAL(x - 1)))
11
23
  }
12
24
 
13
25
  DEFINE TOWERS_OF_HANOI(x) {
14
- IF(x == 1, 1, (2 * TOWERS_OF_HANOI(x - 1) + 1))
26
+ IF(x = 1, 1, (2 * TOWERS_OF_HANOI(x - 1) + 1))
15
27
  }
16
28
 
17
-
data/lib/kalc/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Kalc
2
- VERSION = "0.5.8"
2
+ VERSION = "0.5.9"
3
3
  end
@@ -25,22 +25,54 @@ describe Kalc::Interpreter do
25
25
 
26
26
  it { evaluate("10 >= 10").should == true }
27
27
 
28
+ it { evaluate("ABS(-1 + -2)").should == 3 }
29
+
28
30
  it "should be able to load variables" do
29
- evaluate("a = 1; 1 + a").should == 2
30
- evaluate("a = 1; b = 2; 1 + b").should == 3
31
+ evaluate("a := 1; 1 + a").should == 2
32
+ evaluate("a := 1; b := 2; 1 + b").should == 3
31
33
  end
32
34
 
33
35
  it "should be able to load single quoted variables" do
34
- evaluate("'a' = 1; 1 + 'a'").should == 2
35
- evaluate("'a' = 1; 'b' = 2; 'b' + 'a'").should == 3
36
+ evaluate("'a' := 1; 1 + 'a'").should == 2
37
+ evaluate("'a' := 1; 'b' := 2; 'b' + 'a'").should == 3
36
38
 
37
- evaluate("'a b' = 1; 'a b' + 1").should == 2
39
+ evaluate("'a b' := 1; 'a b' + 1").should == 2
38
40
  end
39
41
 
40
42
  it { evaluate("((75.0)*(25.0))+((125.0)*(25.0))+((150.0)*(25.0))+((250.0)*(25.0))").should == 15000 }
41
43
 
42
44
  it { evaluate("(((40.0)/1000*(4380.0)*(200.0))-((40.0)/1000*(4380.0)*((((75.0)*(25.0))+((125.0)*(25.0))+((150.0)*(25.0))+((250.0)*(25.0)))/(10.0)/(40.0)/(0.8))))*(0.05)").should == 1341.375 }
43
45
 
46
+ context "Negative numbers" do
47
+ it { evaluate("-2").should == -2 }
48
+ it { evaluate("-1000").should == -1000 }
49
+ it { evaluate("-1000.0001").should == -1000.0001 }
50
+ it { evaluate("1 + -1").should == 0 }
51
+ it { evaluate("1 + -10").should == -9 }
52
+ end
53
+
54
+ context "Positive numbers" do
55
+ it { evaluate("1 + +1").should == 2 }
56
+ it { evaluate("1 + +1 - 1").should == 1 }
57
+ it { evaluate("+10000.0001").should == 10000.0001 }
58
+ end
59
+
60
+ context "Boolean value" do
61
+ it { evaluate("TRUE").should == true }
62
+ it { evaluate("FALSE").should == false }
63
+ it { evaluate("FALSE || TRUE").should == true }
64
+ it { evaluate("FALSE && TRUE").should == false }
65
+ end
66
+
67
+ context "Floating point number" do
68
+ it { evaluate("1.01").should == 1.01 }
69
+ it { evaluate("1.01 + 0.02").should == 1.03 }
70
+ it { evaluate("1.01 - 0.01").should == 1 }
71
+ it { evaluate("1.1 + 1.1").should == 2.2 }
72
+ it { evaluate("1.01 = 1.01").should == true }
73
+ it { evaluate("1.01 = 1.02").should == false }
74
+ end
75
+
44
76
  private
45
77
  def evaluate(expression)
46
78
  g = @grammar.parse(expression)
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ class Kalc::Stdlib
4
+
5
+ end
6
+
7
+ describe Kalc::Stdlib do
8
+
9
+ before(:each) do
10
+ @grammar = Kalc::Grammar.new
11
+ @transform = Kalc::Transform.new
12
+ end
13
+
14
+ describe "PLUS_ONE" do
15
+ it { evaluate("PLUS_ONE(1)").should == 2 }
16
+ it { evaluate("PLUS_ONE(101)").should == 102 }
17
+ it { evaluate("PLUS_ONE(0)").should == 1 }
18
+ it { evaluate("PLUS_ONE(-1)").should == 0 }
19
+ end
20
+
21
+ describe "MINUS_ONE" do
22
+ it { evaluate("MINUS_ONE(1)").should == 0 }
23
+ it { evaluate("MINUS_ONE(101)").should == 100 }
24
+ it { evaluate("MINUS_ONE(0)").should == -1 }
25
+ it { evaluate("MINUS_ONE(-1)").should == -2 }
26
+ end
27
+
28
+ describe "SQUARE" do
29
+ it { evaluate("SQUARE(2)").should == 4 }
30
+ it { evaluate("SQUARE(4)").should == 16 }
31
+ end
32
+
33
+ describe "CUBE" do
34
+ it { evaluate("CUBE(2)").should == 8 }
35
+ it { evaluate("CUBE(4)").should == 64 }
36
+ end
37
+
38
+ describe "FIB" do
39
+ it { evaluate("FIB(1)").should == 1 }
40
+ it { evaluate("FIB(10)").should == 55 }
41
+ end
42
+
43
+ describe "FACTORIAL" do
44
+ it { evaluate("FACTORIAL(1)").should == 1 }
45
+ it { evaluate("FACTORIAL(5)").should == 120 }
46
+ end
47
+
48
+ describe "TOWERS_OF_HANOI" do
49
+ it { evaluate("TOWERS_OF_HANOI(4)").should == 15 }
50
+ it { evaluate("TOWERS_OF_HANOI(10)").should == 1023 }
51
+ end
52
+
53
+ private
54
+ def evaluate(expression)
55
+ r = Kalc::Runner.new
56
+ r.run(expression)
57
+ end
58
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kalc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.8
4
+ version: 0.5.9
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-16 00:00:00.000000000 Z
12
+ date: 2012-05-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -73,6 +73,9 @@ files:
73
73
  - README.textile
74
74
  - bin/ikalc
75
75
  - bin/kalc
76
+ - examples/add.kalc
77
+ - examples/newton.kalc
78
+ - examples/subtract.kalc
76
79
  - kalc.gemspec
77
80
  - lib/kalc.rb
78
81
  - lib/kalc/ast.rb
@@ -86,6 +89,7 @@ files:
86
89
  - spec/grammar_spec.rb
87
90
  - spec/interpreter_spec.rb
88
91
  - spec/spec_helper.rb
92
+ - spec/stdlib_spec.rb
89
93
  homepage: https://github.com/mrcsparker/kalc
90
94
  licenses: []
91
95
  post_install_message:
@@ -106,7 +110,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
106
110
  version: '0'
107
111
  requirements: []
108
112
  rubyforge_project: kalc
109
- rubygems_version: 1.8.19
113
+ rubygems_version: 1.8.24
110
114
  signing_key:
111
115
  specification_version: 3
112
116
  summary: Small calculation language.
@@ -114,3 +118,4 @@ test_files:
114
118
  - spec/grammar_spec.rb
115
119
  - spec/interpreter_spec.rb
116
120
  - spec/spec_helper.rb
121
+ - spec/stdlib_spec.rb