crisp 0.0.5 → 0.0.7

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 (41) hide show
  1. data/CHANGELOG.md +10 -2
  2. data/README.md +2 -1
  3. data/Rakefile +2 -2
  4. data/autotest/discover.rb +1 -0
  5. data/crisp.gemspec +32 -8
  6. data/examples/fibonacci.crisp +6 -0
  7. data/lib/crisp.rb +3 -1
  8. data/lib/crisp/chained_env.rb +13 -0
  9. data/lib/crisp/crisp.treetop +23 -15
  10. data/lib/crisp/env.rb +7 -0
  11. data/lib/crisp/function.rb +6 -42
  12. data/lib/crisp/function_runner.rb +37 -0
  13. data/lib/crisp/functions.rb +5 -3
  14. data/lib/crisp/functions/arithmetic.rb +64 -9
  15. data/lib/crisp/functions/core.rb +80 -18
  16. data/lib/crisp/native_call_invoker.rb +16 -0
  17. data/lib/crisp/nodes.rb +30 -59
  18. data/lib/crisp/nodes/array_literal.rb +21 -0
  19. data/lib/crisp/nodes/base.rb +22 -0
  20. data/lib/crisp/nodes/block.rb +22 -0
  21. data/lib/crisp/nodes/false_literal.rb +11 -0
  22. data/lib/crisp/nodes/float_literal.rb +11 -0
  23. data/lib/crisp/nodes/integer_literal.rb +11 -0
  24. data/lib/crisp/nodes/nil_literal.rb +11 -0
  25. data/lib/crisp/nodes/operation.rb +20 -0
  26. data/lib/crisp/nodes/primitive.rb +16 -0
  27. data/lib/crisp/nodes/string_literal.rb +11 -0
  28. data/lib/crisp/nodes/symbol_literal.rb +15 -0
  29. data/lib/crisp/nodes/true_literal.rb +11 -0
  30. data/lib/crisp/parser.rb +7 -0
  31. data/lib/crisp/runtime.rb +7 -0
  32. data/lib/crisp/shell.rb +4 -0
  33. data/spec/crisp/arithmetics_spec.rb +39 -11
  34. data/spec/crisp/basic_spec.rb +7 -72
  35. data/spec/crisp/core_spec.rb +83 -0
  36. data/spec/crisp/function_spec.rb +49 -0
  37. data/spec/crisp/internal_spec.rb +60 -0
  38. data/spec/crisp/native_call_invoker_spec.rb +23 -0
  39. data/spec/crisp/string_spec.rb +1 -1
  40. data/spec/spec_helper.rb +2 -0
  41. metadata +34 -10
data/lib/crisp/parser.rb CHANGED
@@ -1,11 +1,18 @@
1
1
  module Crisp
2
+ # The Crisp parser
3
+ #
4
+ # The parser uses treetop for defining the syntax (see crisp.treetop)
5
+ # For parsing crisp code (as string) call parse on a instance.
6
+ # The output of the parser can be evaluated by passing it to a Crisp runtime.
2
7
  class Parser
3
8
  Treetop.load(File.expand_path(File.join(File.dirname(__FILE__), 'crisp.treetop')))
4
9
 
10
+ # create a new treetop parser
5
11
  def initialize
6
12
  @parser = CrispParser.new
7
13
  end
8
14
 
15
+ # parse the code and return an abstract syntax tree (ast)
9
16
  def parse(code)
10
17
  ast = @parser.parse(code)
11
18
 
data/lib/crisp/runtime.rb CHANGED
@@ -1,10 +1,17 @@
1
1
  module Crisp
2
+ # The Crisp Runtime
3
+ #
4
+ # Each runtime holds its own environment.
5
+ # All predefined functions are loaded when creating a new runtime.
6
+ # Run code by calling run with the output of the parser as argurment.
2
7
  class Runtime
8
+ # create a new env and load all functions when creating a new runtime
3
9
  def initialize
4
10
  @env = Env.new
5
11
  Functions.load(@env)
6
12
  end
7
13
 
14
+ # run the parsed code (abstract syntax tree)
8
15
  def run(ast)
9
16
  ast.eval(@env)
10
17
  end
data/lib/crisp/shell.rb CHANGED
@@ -1,7 +1,11 @@
1
1
  module Crisp
2
+ # The Crisp interactive shell
3
+ #
4
+ # Create a new instance and call run for having a interactive Crisp shell.
2
5
  class Shell
3
6
  require 'readline'
4
7
 
8
+ # start the shell
5
9
  def run
6
10
  runtime = Runtime.new
7
11
  buffer = ''
@@ -1,67 +1,95 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe "when evaluating arithmetic expressions, the language" do
3
+ describe "arithemtic functions" do
4
4
  include Crisp::SpecHelper
5
5
 
6
- it "should calculate integer addition" do
6
+ it "adds integer values" do
7
7
  evaluate("(+ 1 2)").should == 3
8
8
  evaluate("(+ 12 13 1 5)").should == 31
9
9
  evaluate("(+ 12 13 -1 -5)").should == 19
10
10
  end
11
11
 
12
- it "should calculate integer substraction" do
12
+ it "substracts integer values" do
13
13
  evaluate("(- 3 1)").should == 2
14
14
  evaluate("(- 14 1 4 8)").should == 1
15
15
  evaluate("(- 2 5)").should == -3
16
16
  evaluate("(- 2 -5)").should == 7
17
17
  end
18
18
 
19
- it "should calculate integer multiplications" do
19
+ it "multiplies integer values" do
20
20
  evaluate("(* 2 3)").should == 6
21
21
  evaluate("(* 0 3)").should == 0
22
22
  evaluate("(* 2 -3 1 10)").should == -60
23
23
  end
24
24
 
25
- it "should calculate integer divisions" do
25
+ it "divides integer values" do
26
26
  evaluate("(/ 12 3)").should == 4
27
27
  evaluate("(/ 48 2 12)").should == 2
28
28
  evaluate("(/ 30 -3 2)").should == -5
29
29
  end
30
30
 
31
- it "should calculate nested arithmetic integer expressions" do
31
+ it "calculates different nested operations" do
32
32
  evaluate("(+ 1 2 (+ 2 3) (- 5 1)) ").should == 12
33
33
  evaluate("(+ 1 2 (* 2 3) (/ 10 2)) ").should == 14
34
34
  evaluate("(+ 1 2 (- 10 2 3) (* 1 2 3) (/ 12 4)) ").should == 17
35
35
  evaluate("(/ 20 2 (+ 2 3) (- 5 3)) ").should == 1
36
36
  end
37
37
 
38
- it "should calculate float addition" do
38
+ it "adds float values" do
39
39
  evaluate("(+ 1.0 2.)").should == 3.0
40
40
  evaluate("(+ 12.5 13 1.4 5)").should == 31.9
41
41
  evaluate("(+ 12. 13.5 -1.9 -5.1)").should == 18.5
42
42
  end
43
43
 
44
- it "should calculate float substraction" do
44
+ it "substracts float values" do
45
45
  evaluate("(- 3. 1.4)").should == 1.6
46
46
  evaluate("(- 2 5.5)").should == -3.5
47
47
  evaluate("(- 2.40 -5)").should == 7.4
48
48
  end
49
49
 
50
- it "should calculate float multiplications" do
50
+ it "multiplies float values" do
51
51
  evaluate("(* 2.1 3.4)").should == 7.14
52
52
  evaluate("(* 0 3.5)").should == 0
53
53
  evaluate("(* 2 -3.1 1.9 10.1)").should == -118.978
54
54
  end
55
55
 
56
- it "should calculate float divisions" do
56
+ it "divides float values" do
57
57
  evaluate("(/ 12.5 3.1)").should be_within(0.0000001).of(4.03225806451613)
58
58
  evaluate("(/ 48 2.000 12.5)").should == 1.92
59
59
  evaluate("(/ 30.0 -3 2.5)").should == -4.0
60
60
  end
61
61
 
62
- it "should calculate nested arithmetic float expressions" do
62
+ it "calculates different nested operations" do
63
63
  evaluate("(+ 1 2.3 (* 2 3.5) (/ 10 4)) ").should == 12.3
64
64
  evaluate("(+ 1.5 2 (- 10 2.4 3) (* 1.0 2 3) (/ 12 3.0)) ").should == 18.1
65
65
  evaluate("(/ 20.4 2 (+ 2.5 3) (- 5.5 3)) ").should be_within(0.000000001).of(0.741818181818182)
66
66
  end
67
+
68
+ it "compares values for equality" do
69
+ evaluate("(= 1 1)").should == true
70
+ evaluate("(= 1 2)").should == false
71
+ end
72
+
73
+ it "compares values that are greater than others" do
74
+ evaluate("(> 2 1)").should == true
75
+ evaluate("(> 1 1)").should == false
76
+ evaluate("(> 1 2)").should == false
77
+ end
78
+
79
+ it "compares values that are less than others" do
80
+ evaluate("(< 2 1)").should == false
81
+ evaluate("(< 1 1)").should == false
82
+ evaluate("(< 1 2)").should == true
83
+ end
84
+
85
+ it "should calc fibonacci numbers" do
86
+ evaluate("
87
+ (def fib (
88
+ fn [n]
89
+ (if (< n 2)
90
+ n
91
+ (+ (fib (- n 1)) (fib (- n 2))))))
92
+ (fib 10)
93
+ ").should == 55
94
+ end
67
95
  end
@@ -3,90 +3,25 @@ require 'spec_helper'
3
3
  describe "the language" do
4
4
  include Crisp::SpecHelper
5
5
 
6
- it "should not bother whitespaces, tabs and linebreaks in statements" do
6
+ it "does not bother whitspaces characters in expressions" do
7
7
  evaluate(" \r\t\n (\r+\t 1\n2 \t(\n - 9\r\t \n 2)\r)\r\t ").should == 10
8
8
  end
9
9
 
10
- it "should print results" do
11
- evaluate("(println (+ 1 1))")
12
- end
13
-
14
- it "should bind value to symbol" do
15
- evaluate("(def bla 3)")
16
- end
17
-
18
- it "should not bind value to already binded symbol" do
19
- lambda do
20
- evaluate("(def name 123)(def name 123)")
21
- end.should raise_error(Crisp::EnvironmentError, "name already binded")
22
- end
23
-
24
- it "should raise error if use def with wrong args" do
25
- lambda do
26
- evaluate("(def bla 1 2)")
27
- end.should raise_error(Crisp::ArgumentError, "wrong number of arguments for 'def' (3 for 2)")
28
- end
29
-
30
- it "should evaluate a list of operations" do
31
- lambda do
32
- evaluate("(+ 1 2) (def foo 1) (def foo 2)")
33
- end.should raise_error(Crisp::EnvironmentError, "foo already binded")
34
- end
35
-
36
- it "should produce syntax error on invalid syntax" do
10
+ it "creates syntax error on invalid expressions" do
37
11
  lambda do
38
12
  evaluate("(()")
39
13
  end.should raise_error(Crisp::SyntaxError, "syntax error at : 0")
40
14
  end
41
15
 
42
- it "should use values of binded symbols in later statements" do
43
- evaluate("(def bla 2) (* 4 bla)").should == 8
44
- evaluate("(def bla (* 2 3)) (* 4 bla 2)").should == 48
45
- end
46
-
47
- it "should parse an array" do
16
+ it "bind arrays to symbols" do
48
17
  evaluate("(def bla [1 2 3])").size.should == 3
49
18
  evaluate("(def bla [1 2 3])")[1].should == 2
50
- evaluate("(def bla [1 2 foo])")[2].should == :foo
19
+ evaluate("(def foo 5)(def bla [1 2 foo])")[2].should == 5
51
20
  end
52
21
 
53
- it "should raise error if not providing right amount of parameters for function creation" do
22
+ it "does not evaluate numbers" do
54
23
  lambda do
55
- evaluate("(fn [] (+ 1 2) [])")
56
- end.should raise_error(Crisp::ArgumentError, "wrong number of arguments for 'fn' (3 for 2)")
57
- end
58
-
59
- it "should raise error if not providing proper argument list for function generation" do
60
- lambda do
61
- evaluate("(fn (+ 2 1) (+ 1 2))")
62
- end.should raise_error(Crisp::ArgumentError, "no parameter list defined")
63
- end
64
-
65
- it "should raise error if not providing proper function body" do
66
- lambda do
67
- evaluate("(fn [] [])")
68
- end.should raise_error(Crisp::ArgumentError, "no function body defined")
69
- end
70
-
71
- it "should create functions" do
72
- evaluate("(fn [arg] (+ 2 arg))")
73
- end
74
-
75
- it "should bind functions to symbols" do
76
- evaluate("(def myfn (fn [arg] (+ 1 arg)))").class.name.should == "Crisp::Function"
77
- end
78
-
79
- it "should call functions" do
80
- evaluate("(def myfn (fn [a b] (+ 1 1)))(myfn 1 2)").should == 2
81
- end
82
-
83
- it "should use parameters when calling functions" do
84
- evaluate("(def myfn (fn [a b] (+ a b)))(myfn 5 2)").should == 7
85
- end
86
-
87
- it "should raise error on wrong amount of parameters" do
88
- lambda do
89
- evaluate("(def myfn (fn [a1 a2 a3] (+ 1 1)))(myfn 1)")
90
- end.should raise_error(Crisp::ArgumentError, "wrong number of arguments for 'myfn' (1 for 3)")
24
+ evaluate("(3)")
25
+ end.should raise_error(Crisp::SyntaxError, "syntax error at : 0")
91
26
  end
92
27
  end
@@ -0,0 +1,83 @@
1
+ require 'spec_helper'
2
+
3
+ describe "the core language features" do
4
+ include Crisp::SpecHelper
5
+
6
+ it "prints results" do
7
+ evaluate("(println (+ 1 1))")
8
+ end
9
+
10
+ it "binds values to symbols" do
11
+ evaluate("(def bla 3)")
12
+ end
13
+
14
+ it "does not allow to bind symbols twice" do
15
+ lambda do
16
+ evaluate("(def name 123)(def name 123)")
17
+ end.should raise_error(Crisp::EnvironmentError, "name already binded")
18
+ end
19
+
20
+ it "produces an error when calling bind function with arguments" do
21
+ lambda do
22
+ evaluate("(def bla 1 2)")
23
+ end.should raise_error(Crisp::ArgumentError, "wrong number of arguments for 'def' (3 for 2)")
24
+ end
25
+
26
+ it "uses binded values in later expressions" do
27
+ evaluate("(def bla 2) (* 4 bla)").should == 8
28
+ evaluate("(def bla (* 2 3)) (* 4 bla 2)").should == 48
29
+ end
30
+
31
+ it "can not resolve unbound symbols" do
32
+ lambda do
33
+ evaluate("n")
34
+ end.should raise_error(Crisp::EnvironmentError, "n is unbound")
35
+ end
36
+
37
+ it "can resolve primitve types" do
38
+ evaluate("2").should == 2
39
+ evaluate("2.1").should == 2.1
40
+ evaluate("nil").should == nil
41
+ evaluate("true").should == true
42
+ evaluate("false").should == false
43
+ evaluate('"abc"').should == 'abc'
44
+ evaluate("[1 2 3]").should == [1, 2, 3]
45
+ end
46
+
47
+ it "can resolve bounded symbols" do
48
+ evaluate("(def foo 23) foo").should == 23
49
+ evaluate('(def foo "foo") foo').should == 'foo'
50
+ end
51
+
52
+ it "binds symbols to value of already bound symbol" do
53
+ evaluate("(def a 1)
54
+ (def b a)
55
+ b").should == 1
56
+ end
57
+
58
+ it "has if statements" do
59
+ evaluate("(if true 1)").should == 1
60
+ evaluate("(if false 1)").should == nil
61
+ evaluate("(if nil 1)").should == nil
62
+ end
63
+
64
+ it "has if else statements" do
65
+ evaluate("(if true 1 2)").should == 1
66
+ evaluate("(if false 1 2)").should == 2
67
+ evaluate("(if nil 1 2)").should == 2
68
+ end
69
+
70
+ it "does not run if statements with wrong number of arguments" do
71
+ lambda do
72
+ evaluate("(if true true false 2)")
73
+ end.should raise_error(Crisp::ArgumentError, "wrong number of arguments for 'if' (4 for 2..3)")
74
+
75
+ lambda do
76
+ evaluate("(if true)")
77
+ end.should raise_error(Crisp::ArgumentError, "wrong number of arguments for 'if' (1 for 2..3)")
78
+ end
79
+
80
+ it "resolves symbols in if statements" do
81
+ evaluate("(def foo 2)(if true foo)").should == 2
82
+ end
83
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ describe "the languages functions" do
4
+ include Crisp::SpecHelper
5
+
6
+ it "does not create a function when providing wrong number of arguments" do
7
+ lambda do
8
+ evaluate("(fn [] (+ 1 2) [])")
9
+ end.should raise_error(Crisp::ArgumentError, "wrong number of arguments for 'fn' (3 for 2)")
10
+ end
11
+
12
+ it "does not create a function when not providing a proper argument list" do
13
+ lambda do
14
+ evaluate("(fn (+ 2 1) (+ 1 2))")
15
+ end.should raise_error(Crisp::ArgumentError, "no argument list defined")
16
+ end
17
+
18
+ it "does not create a function when not providing a proper function body" do
19
+ lambda do
20
+ evaluate("(fn [] [])")
21
+ end.should raise_error(Crisp::ArgumentError, "no function body defined")
22
+ end
23
+
24
+ it "creates functions" do
25
+ evaluate("(fn [arg] (+ 2 arg))")
26
+ end
27
+
28
+ it "binds functions to symbols" do
29
+ evaluate("(def myfn (fn [arg] (+ 1 arg)))").class.name.should == "Crisp::Function"
30
+ end
31
+
32
+ it "calls functions" do
33
+ evaluate("(def myfn (fn [a b] (+ 1 1)))(myfn 1 2)").should == 2
34
+ end
35
+
36
+ it "calls functions with arguments" do
37
+ evaluate("(def myfn (fn [a b] (+ a b)))(myfn 5 2)").should == 7
38
+ end
39
+
40
+ it "does not calls functions with wrong number of arguments" do
41
+ lambda do
42
+ evaluate("(def myfn (fn [a1 a2 a3] (+ 1 1)))(myfn 1)")
43
+ end.should raise_error(Crisp::ArgumentError, "wrong number of arguments for 'myfn' (1 for 3)")
44
+ end
45
+
46
+ it "instantly evaluates a function" do
47
+ evaluate("((fn [] (+ 1 2)))").should == 3
48
+ end
49
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe "internals" do
4
+ include Crisp::SpecHelper
5
+
6
+ it "sets and gets values to and from the env" do
7
+ env = Crisp::Env.new
8
+ env['key'] = 'value'
9
+
10
+ env['key'].should == 'value'
11
+ env[:key].should == 'value'
12
+ env.has_key?('key').should == true
13
+ env.has_key?(:key).should == true
14
+ end
15
+
16
+ it "handles mismatched env reads" do
17
+ env = Crisp::Env.new
18
+
19
+ env.has_key?('not_exists').should == false
20
+ env['not_exists'].should == nil
21
+ end
22
+
23
+ it "is not allowed to set env keys twice" do
24
+ env = Crisp::Env.new
25
+ env['key'] = 'value'
26
+
27
+ lambda do
28
+ env['key'] = 'another value'
29
+ end.should raise_error(Crisp::EnvironmentError, "key already binded")
30
+ end
31
+
32
+ it "gets values from a chained env" do
33
+ env1 = Crisp::Env.new
34
+ env1[:key1] = 'val1'
35
+ env1[:key2] = 'val2'
36
+
37
+ env2 = Crisp::Env.new
38
+ env2[:key1] = 'val3'
39
+ env2[:key3] = 'val4'
40
+
41
+ chained = Crisp::ChainedEnv.new(env1, env2)
42
+
43
+ chained[:key1].should == 'val1'
44
+ chained[:key2].should == 'val2'
45
+ chained[:key3].should == 'val4'
46
+ end
47
+
48
+ it "sets values to a chained env" do
49
+ env1 = Crisp::Env.new
50
+ env1[:key] = 'val'
51
+
52
+ env2 = Crisp::Env.new
53
+
54
+ chained = Crisp::ChainedEnv.new(env1, env2)
55
+
56
+ chained[:key] = 'other'
57
+ env2[:key].should == 'other'
58
+ chained[:key].should == 'val'
59
+ end
60
+ end