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
@@ -1,51 +1,113 @@
1
1
  module Crisp
2
2
  module Functions
3
+ # Defining core crisp functions
3
4
  class Core
4
- def self.load(env)
5
+ # load the functions and bind them into the given environment
6
+ def self.load(current_env)
5
7
 
8
+ # reserve nil/true/false in environment
9
+ current_env['nil'] = nil
10
+ current_env['true'] = true
11
+ current_env['false'] = false
12
+
13
+ # println
14
+ # print arguments (seperated by whitspace) including a newline to the standard output
15
+ #
16
+ # (println 123)
17
+ # 123
6
18
  Function.new do
7
- print params_evaled.collect(&:to_s).join(' ') + "\n"
8
- end.bind('println', env)
19
+ print args_evaled.collect(&:to_s).join(' ') + "\n"
20
+ end.bind('println', current_env)
9
21
 
22
+ # def
23
+ # bind the second argument to the symbol name of the first argument
24
+ # actually a key/value pair will be stored in the environment
25
+ #
26
+ # (def foo 1)
27
+ # => foo
28
+ # (println foo)
29
+ # 1
10
30
  Function.new do
11
- validate_params_count(2, params.size)
31
+ validate_args_count(2, args.size)
12
32
 
13
- value = params_evaled[1]
33
+ key = args[0].text_value
34
+ value = args[1].resolve_and_eval(env)
14
35
 
15
36
  if value.class.name == "Crisp::Function"
16
- value.bind(params_values[0], env)
37
+ value.bind(key, env)
17
38
  else
18
- env[params_values[0]] = value
39
+ env[key] = value
19
40
  end
20
- end.bind('def', env)
41
+ end.bind('def', current_env)
21
42
 
43
+ # fn
44
+ # creates a function
45
+ # the first argument has to be an array containing the argumentlist
46
+ # the second argument is the function body
47
+ #
48
+ # (fn [a b] (+ a b)
49
+ # => ...)
22
50
  Function.new do
23
- validate_params_count(2, params.size)
51
+ validate_args_count(2, args.size)
24
52
 
25
- if params[0].class.name != "Crisp::ArrayLiteral"
26
- raise ArgumentError, "no parameter list defined"
53
+ if args[0].class.name != "Crisp::Nodes::ArrayLiteral"
54
+ raise ArgumentError, "no argument list defined"
27
55
  end
28
56
 
29
- if params[1].class.name != "Crisp::Operation"
57
+ if args[1].class.name != "Crisp::Nodes::Operation"
30
58
  raise ArgumentError, "no function body defined"
31
59
  end
32
60
 
33
- fn_param_list = params[0].raw_elements
34
- fn_operation = params[1]
61
+ fn_arg_list = args[0].raw_elements
62
+ fn_operation = args[1]
35
63
 
64
+ # create and return the new function
36
65
  Function.new do
37
- validate_params_count(fn_param_list.size, params.size)
66
+ validate_args_count(fn_arg_list.size, args.size)
38
67
 
39
68
  local_env = Env.new
40
- fn_param_list.each_with_index do |key, idx|
41
- local_env[key.eval(env)] = params[idx]
69
+ fn_arg_list.each_with_index do |key, idx|
70
+ local_env[key.text_value] = args[idx].resolve_and_eval(env)
42
71
  end
43
72
 
44
73
  chained_env = ChainedEnv.new(local_env, env)
45
74
 
46
75
  fn_operation.eval(chained_env)
47
76
  end
48
- end.bind('fn', env)
77
+ end.bind('fn', current_env)
78
+
79
+ # if
80
+ # the if function evaluates the second argument if the condition (first argument) returns not nil or false.
81
+ # Otherwise the third argument will be evaluated (optional)
82
+ #
83
+ # (if (= 1 2) 1 2)
84
+ # => 2
85
+ Function.new do
86
+ validate_args_count((2..3), args.size)
87
+
88
+ result = args[0].resolve_and_eval(env)
89
+
90
+ res = if ![nil, false].include?(result)
91
+ args[1]
92
+ elsif args[2]
93
+ args[2]
94
+ end
95
+
96
+ res ? res.resolve_and_eval(env) : res
97
+ end.bind('if', current_env)
98
+
99
+ # .
100
+ # perform a native call on an object with optional arguments
101
+ #
102
+ # (. upcase "abc")
103
+ # => "ABC"
104
+ Function.new do
105
+ meth = args[0].text_value.to_sym
106
+ target = args[1].resolve_and_eval(env)
107
+ values = args[2..-1].map { |arg| arg.resolve_and_eval(env) }
108
+
109
+ NativeCallInvoker.new(target, meth, values).invoke!
110
+ end.bind('.', current_env)
49
111
 
50
112
  end
51
113
  end
@@ -0,0 +1,16 @@
1
+ module Crisp
2
+ # class for handling native ruby method invokations
3
+ class NativeCallInvoker
4
+ # create instance with target object, method to be called and arguments
5
+ def initialize(target, method, *args)
6
+ @target = target
7
+ @method = method
8
+ @args = args.flatten
9
+ end
10
+
11
+ # invoke the native ruby call
12
+ def invoke!
13
+ @target.send(@method, *@args)
14
+ end
15
+ end
16
+ end
data/lib/crisp/nodes.rb CHANGED
@@ -1,62 +1,33 @@
1
1
  module Crisp
2
- class Base < Treetop::Runtime::SyntaxNode
3
- def eval(env)
4
- nil
5
- end
6
- end
7
-
8
- class Operation < Base
9
- def eval(env)
10
- env[self.func_identifier.text_value].eval(env, self.element_list.elements.collect(&:element))
11
- end
12
- end
13
-
14
- class Block < Base
15
- def eval(env)
16
- last_result = nil
17
-
18
- elements.each do |op|
19
- last_result = op.eval(env)
20
- end
21
-
22
- last_result
23
- end
24
- end
25
-
26
- class ArrayLiteral < Base
27
- def eval(env)
28
- raw_elements.map { |e| e.eval(env) }
29
- end
30
-
31
- def raw_elements
32
- self.element_list.elements.collect(&:element)
33
- end
34
- end
35
-
36
- class Primitive < Base
37
- end
38
-
39
- class Number < Primitive
40
- def eval(env)
41
- text_value.to_i
42
- end
43
- end
44
-
45
- class Float < Primitive
46
- def eval(env)
47
- text_value.to_f
48
- end
49
- end
50
-
51
- class StringLiteral < Primitive
52
- def eval(env)
53
- text_value[1..-2]
54
- end
55
- end
56
-
57
- class Symbol < Primitive
58
- def eval(env)
59
- text_value.to_sym
60
- end
2
+ module Nodes
3
+ # require all used node types
4
+ #
5
+ # hierarchy:
6
+ #
7
+ # Base
8
+ # + Operation
9
+ # + Block
10
+ # + Array
11
+ # + Primitive
12
+ # + Integer
13
+ # + Float
14
+ # + String
15
+ # + Symbol
16
+ # + True
17
+ # + False
18
+ # + Nil
19
+
20
+ require 'crisp/nodes/base'
21
+ require 'crisp/nodes/operation'
22
+ require 'crisp/nodes/block'
23
+ require 'crisp/nodes/array_literal'
24
+ require 'crisp/nodes/primitive'
25
+ require 'crisp/nodes/integer_literal'
26
+ require 'crisp/nodes/float_literal'
27
+ require 'crisp/nodes/string_literal'
28
+ require 'crisp/nodes/symbol_literal'
29
+ require 'crisp/nodes/true_literal'
30
+ require 'crisp/nodes/false_literal'
31
+ require 'crisp/nodes/nil_literal'
61
32
  end
62
33
  end
@@ -0,0 +1,21 @@
1
+ module Crisp
2
+ module Nodes
3
+ # The array node
4
+ class ArrayLiteral < Base
5
+ # eval each array element and return it as array
6
+ def eval(env)
7
+ raw_elements.map { |e| e.eval(env) }
8
+ end
9
+
10
+ # an array resolves to its raw elements
11
+ def resolve(env)
12
+ raw_elements.map { |e| e.resolve(env) }
13
+ end
14
+
15
+ # return an array with the raw parsed array elements
16
+ def raw_elements
17
+ self.element_list.elements.collect(&:element)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ module Crisp
2
+ module Nodes
3
+ # The base node
4
+ class Base < Treetop::Runtime::SyntaxNode
5
+ # this is only an abstract method
6
+ def eval(env)
7
+ raise "abstract method!"
8
+ end
9
+
10
+ # this is only an abstract method
11
+ def resolve(env)
12
+ raise "abstract method!"
13
+ end
14
+
15
+ # resolves the node and evals it it is a crisp operation node
16
+ def resolve_and_eval(env)
17
+ res = self.resolve(env)
18
+ res.class.name == "Crisp::Nodes::Operation" ? res.eval(env) : res
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ module Crisp
2
+ module Nodes
3
+ # The block node
4
+ class Block < Base
5
+ # eval each element of the block and return the last result
6
+ def eval(env)
7
+ last_result = nil
8
+
9
+ elements.each do |op|
10
+ last_result = op.resolve_and_eval(env)
11
+ end
12
+
13
+ last_result
14
+ end
15
+
16
+ # a block resolves to itself
17
+ def resolve(env)
18
+ self
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ module Crisp
2
+ module Nodes
3
+ # The false node
4
+ class FalseLiteral < Primitive
5
+ # yes it resolves to false
6
+ def resolve(env)
7
+ false
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Crisp
2
+ module Nodes
3
+ # The float node
4
+ class FloatLiteral < Primitive
5
+ # returns float value
6
+ def resolve(env)
7
+ text_value.to_f
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Crisp
2
+ module Nodes
3
+ # The integer node
4
+ class IntegerLiteral < Primitive
5
+ # returns integer value
6
+ def resolve(env)
7
+ text_value.to_i
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Crisp
2
+ module Nodes
3
+ # The nil node
4
+ class NilLiteral < Primitive
5
+ # yes it resolves to nil
6
+ def resolve(env)
7
+ nil
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ module Crisp
2
+ module Nodes
3
+ # The operation node
4
+ class Operation < Base
5
+ # get the function binded to the given function name and eval it (including arguments)
6
+ def eval(env)
7
+ if self.func_identifier.class.name == "Crisp::Nodes::Operation"
8
+ self.func_identifier.eval(env).eval(env, self.element_list.elements.collect(&:element))
9
+ else
10
+ env[self.func_identifier.text_value].eval(env, self.element_list.elements.collect(&:element))
11
+ end
12
+ end
13
+
14
+ # an operation resolves to itself
15
+ def resolve(env)
16
+ self
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ module Crisp
2
+ module Nodes
3
+ # The primitive node
4
+ class Primitive < Base
5
+ # raise error if trying to eval a primitve
6
+ def eval(env)
7
+ raise "cannot eval primitive"
8
+ end
9
+
10
+ # abstract method
11
+ def resolve(env)
12
+ raise "abstract method!"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ module Crisp
2
+ module Nodes
3
+ # The string node
4
+ class StringLiteral < Primitive
5
+ # return a string
6
+ def resolve(env)
7
+ text_value[1..-2]
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ module Crisp
2
+ module Nodes
3
+ # The symbol node
4
+ class SymbolLiteral < Primitive
5
+ # return the value for the key in the env the symbol stays for
6
+ def resolve(env)
7
+ if !env.has_key?(text_value)
8
+ raise Crisp::EnvironmentError, "#{text_value} is unbound"
9
+ end
10
+
11
+ env[text_value]
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ module Crisp
2
+ module Nodes
3
+ # The true node
4
+ class TrueLiteral < Primitive
5
+ # yes it resolves to true
6
+ def resolve(env)
7
+ true
8
+ end
9
+ end
10
+ end
11
+ end