kalc 0.5.8 → 0.5.9

Sign up to get free protection for your applications and to get access to all the features.
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