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 +37 -3
- data/bin/kalc +5 -10
- data/examples/add.kalc +1 -0
- data/examples/newton.kalc +22 -0
- data/examples/subtract.kalc +1 -0
- data/lib/kalc.rb +40 -0
- data/lib/kalc/ast.rb +2 -2
- data/lib/kalc/grammar.rb +4 -3
- data/lib/kalc/interpreter.rb +1 -1
- data/lib/kalc/repl.rb +7 -16
- data/lib/kalc/stdlib.kalc +16 -5
- data/lib/kalc/version.rb +1 -1
- data/spec/interpreter_spec.rb +37 -5
- data/spec/stdlib_spec.rb +58 -0
- metadata +8 -3
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
|
40
|
-
b
|
41
|
-
d
|
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
|
-
|
9
|
+
if ARGV.first
|
10
|
+
input = File.read(ARGV.first)
|
10
11
|
|
11
|
-
|
12
|
-
|
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
|
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
|
|
data/lib/kalc/interpreter.rb
CHANGED
data/lib/kalc/repl.rb
CHANGED
@@ -10,15 +10,8 @@ module Kalc
|
|
10
10
|
|
11
11
|
puts heading
|
12
12
|
|
13
|
-
|
14
|
-
@
|
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
|
-
|
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
|
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
|
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
|
22
|
+
IF(x = 0, 1, (x * FACTORIAL(x - 1)))
|
11
23
|
}
|
12
24
|
|
13
25
|
DEFINE TOWERS_OF_HANOI(x) {
|
14
|
-
IF(x
|
26
|
+
IF(x = 1, 1, (2 * TOWERS_OF_HANOI(x - 1) + 1))
|
15
27
|
}
|
16
28
|
|
17
|
-
|
data/lib/kalc/version.rb
CHANGED
data/spec/interpreter_spec.rb
CHANGED
@@ -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
|
30
|
-
evaluate("a
|
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'
|
35
|
-
evaluate("'a'
|
36
|
+
evaluate("'a' := 1; 1 + 'a'").should == 2
|
37
|
+
evaluate("'a' := 1; 'b' := 2; 'b' + 'a'").should == 3
|
36
38
|
|
37
|
-
evaluate("'a b'
|
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)
|
data/spec/stdlib_spec.rb
ADDED
@@ -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.
|
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-
|
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.
|
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
|