crisp 0.0.7 → 0.0.8
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/CHANGELOG.md +10 -1
- data/Rakefile +1 -1
- data/bin/crisp +1 -1
- data/crisp.gemspec +7 -5
- data/examples/factorial.crisp +6 -0
- data/examples/fibonacci.crisp +0 -1
- data/examples/run.crisp +5 -0
- data/lib/crisp.rb +1 -1
- data/lib/crisp/chained_env.rb +10 -0
- data/lib/crisp/crisp.treetop +1 -1
- data/lib/crisp/env.rb +2 -0
- data/lib/crisp/errors.rb +3 -0
- data/lib/crisp/functions/core.rb +153 -6
- data/lib/crisp/nodes/block.rb +3 -7
- data/lib/crisp/nodes/operation.rb +8 -2
- data/lib/crisp/runtime.rb +3 -3
- data/spec/crisp/arithmetics_spec.rb +1 -1
- data/spec/crisp/basic_spec.rb +3 -3
- data/spec/crisp/core_spec.rb +136 -0
- data/spec/crisp/function_spec.rb +5 -4
- data/spec/crisp/native_call_invoker_spec.rb +10 -4
- data/spec/crisp/string_spec.rb +1 -1
- metadata +9 -7
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,16 @@
|
|
1
|
-
## 0.0.
|
1
|
+
## 0.0.9 (2011-xx-xx)
|
2
2
|
|
3
3
|
* ...
|
4
4
|
|
5
|
+
## 0.0.8 (2011-01-06)
|
6
|
+
|
7
|
+
* local bindings with let
|
8
|
+
* cond provides a kind of switch/case statemnts, including default path
|
9
|
+
* load lets you load crisp source files at runtime
|
10
|
+
* loops can be performed with loop/recur
|
11
|
+
* internal fixes and refactorings
|
12
|
+
* more examples
|
13
|
+
|
5
14
|
## 0.0.6/0.0.7 (2010-12-17)
|
6
15
|
|
7
16
|
* if else statements
|
data/Rakefile
CHANGED
@@ -15,7 +15,7 @@ begin
|
|
15
15
|
gem.homepage = "http://github.com/mgsnova/crisp"
|
16
16
|
gem.authors = ['Markus Gerdes']
|
17
17
|
gem.add_dependency 'treetop', '~> 1.4.9'
|
18
|
-
gem.add_development_dependency 'rspec', '~> 2.
|
18
|
+
gem.add_development_dependency 'rspec', '~> 2.4.0'
|
19
19
|
end
|
20
20
|
|
21
21
|
Jeweler::GemcutterTasks.new
|
data/bin/crisp
CHANGED
data/crisp.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{crisp}
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.8"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Markus Gerdes"]
|
12
|
-
s.date = %q{
|
12
|
+
s.date = %q{2011-01-06}
|
13
13
|
s.default_executable = %q{crisp}
|
14
14
|
s.email = %q{github@mgsnova.de}
|
15
15
|
s.executables = ["crisp"]
|
@@ -25,7 +25,9 @@ Gem::Specification.new do |s|
|
|
25
25
|
"autotest/discover.rb",
|
26
26
|
"bin/crisp",
|
27
27
|
"crisp.gemspec",
|
28
|
+
"examples/factorial.crisp",
|
28
29
|
"examples/fibonacci.crisp",
|
30
|
+
"examples/run.crisp",
|
29
31
|
"lib/crisp.rb",
|
30
32
|
"lib/crisp/chained_env.rb",
|
31
33
|
"lib/crisp/crisp.treetop",
|
@@ -83,14 +85,14 @@ Gem::Specification.new do |s|
|
|
83
85
|
|
84
86
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
85
87
|
s.add_runtime_dependency(%q<treetop>, ["~> 1.4.9"])
|
86
|
-
s.add_development_dependency(%q<rspec>, ["~> 2.
|
88
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.4.0"])
|
87
89
|
else
|
88
90
|
s.add_dependency(%q<treetop>, ["~> 1.4.9"])
|
89
|
-
s.add_dependency(%q<rspec>, ["~> 2.
|
91
|
+
s.add_dependency(%q<rspec>, ["~> 2.4.0"])
|
90
92
|
end
|
91
93
|
else
|
92
94
|
s.add_dependency(%q<treetop>, ["~> 1.4.9"])
|
93
|
-
s.add_dependency(%q<rspec>, ["~> 2.
|
95
|
+
s.add_dependency(%q<rspec>, ["~> 2.4.0"])
|
94
96
|
end
|
95
97
|
end
|
96
98
|
|
data/examples/fibonacci.crisp
CHANGED
data/examples/run.crisp
ADDED
data/lib/crisp.rb
CHANGED
data/lib/crisp/chained_env.rb
CHANGED
@@ -29,5 +29,15 @@ module Crisp
|
|
29
29
|
def []=(key, val)
|
30
30
|
@second[key] = val
|
31
31
|
end
|
32
|
+
|
33
|
+
# returns global loop data of global/second env
|
34
|
+
def global_loop_data
|
35
|
+
@second.global_loop_data
|
36
|
+
end
|
37
|
+
|
38
|
+
# set global loop data to global/second env
|
39
|
+
def global_loop_data=(data)
|
40
|
+
@second.global_loop_data = data
|
41
|
+
end
|
32
42
|
end
|
33
43
|
end
|
data/lib/crisp/crisp.treetop
CHANGED
data/lib/crisp/env.rb
CHANGED
data/lib/crisp/errors.rb
CHANGED
data/lib/crisp/functions/core.rb
CHANGED
@@ -54,10 +54,6 @@ module Crisp
|
|
54
54
|
raise ArgumentError, "no argument list defined"
|
55
55
|
end
|
56
56
|
|
57
|
-
if args[1].class.name != "Crisp::Nodes::Operation"
|
58
|
-
raise ArgumentError, "no function body defined"
|
59
|
-
end
|
60
|
-
|
61
57
|
fn_arg_list = args[0].raw_elements
|
62
58
|
fn_operation = args[1]
|
63
59
|
|
@@ -72,7 +68,7 @@ module Crisp
|
|
72
68
|
|
73
69
|
chained_env = ChainedEnv.new(local_env, env)
|
74
70
|
|
75
|
-
fn_operation.
|
71
|
+
fn_operation.resolve_and_eval(chained_env)
|
76
72
|
end
|
77
73
|
end.bind('fn', current_env)
|
78
74
|
|
@@ -96,6 +92,127 @@ module Crisp
|
|
96
92
|
res ? res.resolve_and_eval(env) : res
|
97
93
|
end.bind('if', current_env)
|
98
94
|
|
95
|
+
# cond
|
96
|
+
# cond works like a switch/case statement, evaluating the first expression where the condition matches
|
97
|
+
#
|
98
|
+
# (cond
|
99
|
+
# false (println "should not be printed")
|
100
|
+
# true (println "should be printed"))
|
101
|
+
Function.new do
|
102
|
+
if args.size.odd?
|
103
|
+
raise ArgumentError, "argument list has to contain even list of arguments"
|
104
|
+
end
|
105
|
+
|
106
|
+
result = nil
|
107
|
+
|
108
|
+
args.each_with_index do |arg, idx|
|
109
|
+
next if idx.odd?
|
110
|
+
if (arg.class.name == 'Crisp::Nodes::SymbolLiteral' and arg.text_value == 'else') or
|
111
|
+
(![nil, false].include?(arg.resolve_and_eval(env)))
|
112
|
+
result = args[idx + 1].resolve_and_eval(env)
|
113
|
+
break
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
result
|
118
|
+
end.bind("cond", current_env)
|
119
|
+
|
120
|
+
# let
|
121
|
+
# create local binding only valid within let and evaluate expressions
|
122
|
+
#
|
123
|
+
# (let [x 1 y 2] (+ x y))
|
124
|
+
# => 3
|
125
|
+
Function.new do
|
126
|
+
if args[0].class.name != "Crisp::Nodes::ArrayLiteral"
|
127
|
+
raise ArgumentError, "no argument list defined"
|
128
|
+
end
|
129
|
+
|
130
|
+
if args[0].raw_elements.size.odd?
|
131
|
+
raise ArgumentError, "argument list has to contain even list of arguments"
|
132
|
+
end
|
133
|
+
|
134
|
+
local_env = Env.new
|
135
|
+
chained_env = ChainedEnv.new(local_env, env)
|
136
|
+
binding_array = args[0].raw_elements
|
137
|
+
|
138
|
+
binding_array.each_with_index do |key, idx|
|
139
|
+
next if idx.odd?
|
140
|
+
local_env[key.text_value] = binding_array[idx+1].resolve_and_eval(chained_env)
|
141
|
+
end
|
142
|
+
|
143
|
+
args[1..-1].map do |op|
|
144
|
+
op.resolve_and_eval(chained_env)
|
145
|
+
end.last
|
146
|
+
end.bind('let', current_env)
|
147
|
+
|
148
|
+
# loop
|
149
|
+
# create loop with a local binding, perform a loopjump using recur
|
150
|
+
#
|
151
|
+
# (loop [cnt 5 acc 1]
|
152
|
+
# (if (= 0 cnt)
|
153
|
+
# acc
|
154
|
+
# (recur (- cnt 1) (* acc cnt))))
|
155
|
+
# => 120
|
156
|
+
Function.new do
|
157
|
+
if env.global_loop_data
|
158
|
+
raise LoopError, "nested loops are not allowed"
|
159
|
+
end
|
160
|
+
|
161
|
+
if args[0].class.name != "Crisp::Nodes::ArrayLiteral"
|
162
|
+
raise ArgumentError, "no argument list defined"
|
163
|
+
end
|
164
|
+
|
165
|
+
if args[0].raw_elements.size.odd?
|
166
|
+
raise ArgumentError, "argument list has to contain even list of arguments"
|
167
|
+
end
|
168
|
+
|
169
|
+
local_env = Env.new
|
170
|
+
chained_env = ChainedEnv.new(local_env, env)
|
171
|
+
binding_array = args[0].raw_elements
|
172
|
+
argument_name_array = []
|
173
|
+
|
174
|
+
binding_array.each_with_index do |key, idx|
|
175
|
+
next if idx.odd?
|
176
|
+
local_env[key.text_value] = binding_array[idx+1].resolve_and_eval(chained_env)
|
177
|
+
argument_name_array << key.text_value
|
178
|
+
end
|
179
|
+
|
180
|
+
env.global_loop_data = {
|
181
|
+
:operations => args[1..-1],
|
182
|
+
:argument_names => argument_name_array
|
183
|
+
}
|
184
|
+
|
185
|
+
result = args[1..-1].map do |op|
|
186
|
+
op.resolve_and_eval(chained_env)
|
187
|
+
end.last
|
188
|
+
|
189
|
+
env.global_loop_data = nil
|
190
|
+
|
191
|
+
result
|
192
|
+
end.bind('loop', current_env)
|
193
|
+
|
194
|
+
# recur
|
195
|
+
# only used inside a loop. recur will trigger a new run of the loop
|
196
|
+
# see 'loop' for an example
|
197
|
+
Function.new do
|
198
|
+
if !env.global_loop_data
|
199
|
+
raise LoopError, "recur called outside loop"
|
200
|
+
end
|
201
|
+
|
202
|
+
validate_args_count(env.global_loop_data[:argument_names].size, args.size)
|
203
|
+
|
204
|
+
local_env = Env.new
|
205
|
+
chained_env = ChainedEnv.new(local_env, env)
|
206
|
+
|
207
|
+
env.global_loop_data[:argument_names].each_with_index do |key, idx|
|
208
|
+
local_env[key] = args[idx].resolve_and_eval(env)
|
209
|
+
end
|
210
|
+
|
211
|
+
env.global_loop_data[:operations].map do |op|
|
212
|
+
op.resolve_and_eval(chained_env)
|
213
|
+
end.last
|
214
|
+
end.bind('recur', current_env)
|
215
|
+
|
99
216
|
# .
|
100
217
|
# perform a native call on an object with optional arguments
|
101
218
|
#
|
@@ -103,12 +220,42 @@ module Crisp
|
|
103
220
|
# => "ABC"
|
104
221
|
Function.new do
|
105
222
|
meth = args[0].text_value.to_sym
|
106
|
-
target = args[1].
|
223
|
+
target = if args[1].class.name == "Crisp::Nodes::SymbolLiteral" and !env.has_key?(args[1].text_value)
|
224
|
+
Object.const_get(args[1].text_value)
|
225
|
+
else
|
226
|
+
args[1].resolve_and_eval(env)
|
227
|
+
end
|
107
228
|
values = args[2..-1].map { |arg| arg.resolve_and_eval(env) }
|
108
229
|
|
109
230
|
NativeCallInvoker.new(target, meth, values).invoke!
|
110
231
|
end.bind('.', current_env)
|
111
232
|
|
233
|
+
# load
|
234
|
+
# include content of another crisp source file
|
235
|
+
#
|
236
|
+
# (load "business_logic")
|
237
|
+
# => true
|
238
|
+
Function.new do
|
239
|
+
args_evaled.collect(&:to_s).each do |filename|
|
240
|
+
pwd = `pwd`.strip
|
241
|
+
file = if filename[0..1] == '/'
|
242
|
+
File.join(pwd, filename)
|
243
|
+
else
|
244
|
+
filename
|
245
|
+
end.sub(".crisp$", '') + '.crisp'
|
246
|
+
|
247
|
+
if !File.exists?(file)
|
248
|
+
raise Crisp::ArgumentError, "file #{file} not found"
|
249
|
+
end
|
250
|
+
|
251
|
+
filecontent = File.read(file)
|
252
|
+
ast = Crisp::Parser.new.parse(filecontent)
|
253
|
+
Crisp::Runtime.new(env).run(ast)
|
254
|
+
end
|
255
|
+
|
256
|
+
true
|
257
|
+
end.bind('load', current_env)
|
258
|
+
|
112
259
|
end
|
113
260
|
end
|
114
261
|
end
|
data/lib/crisp/nodes/block.rb
CHANGED
@@ -4,13 +4,9 @@ module Crisp
|
|
4
4
|
class Block < Base
|
5
5
|
# eval each element of the block and return the last result
|
6
6
|
def eval(env)
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
last_result = op.resolve_and_eval(env)
|
11
|
-
end
|
12
|
-
|
13
|
-
last_result
|
7
|
+
elements.map do |op|
|
8
|
+
op.resolve_and_eval(env)
|
9
|
+
end.last
|
14
10
|
end
|
15
11
|
|
16
12
|
# a block resolves to itself
|
@@ -5,9 +5,9 @@ module Crisp
|
|
5
5
|
# get the function binded to the given function name and eval it (including arguments)
|
6
6
|
def eval(env)
|
7
7
|
if self.func_identifier.class.name == "Crisp::Nodes::Operation"
|
8
|
-
self.func_identifier.eval(env).eval(env,
|
8
|
+
self.func_identifier.eval(env).eval(env, raw_element_list)
|
9
9
|
else
|
10
|
-
env[self.func_identifier.text_value].eval(env,
|
10
|
+
env[self.func_identifier.text_value].eval(env, raw_element_list)
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
@@ -15,6 +15,12 @@ module Crisp
|
|
15
15
|
def resolve(env)
|
16
16
|
self
|
17
17
|
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def raw_element_list
|
22
|
+
self.element_list.elements.collect(&:element)
|
23
|
+
end
|
18
24
|
end
|
19
25
|
end
|
20
26
|
end
|
data/lib/crisp/runtime.rb
CHANGED
@@ -6,9 +6,9 @@ module Crisp
|
|
6
6
|
# Run code by calling run with the output of the parser as argurment.
|
7
7
|
class Runtime
|
8
8
|
# create a new env and load all functions when creating a new runtime
|
9
|
-
def initialize
|
10
|
-
@env = Env.new
|
11
|
-
Functions.load(@env)
|
9
|
+
def initialize(env = nil)
|
10
|
+
@env = env || Env.new
|
11
|
+
Functions.load(@env) if !env
|
12
12
|
end
|
13
13
|
|
14
14
|
# run the parsed code (abstract syntax tree)
|
data/spec/crisp/basic_spec.rb
CHANGED
@@ -3,17 +3,17 @@ require 'spec_helper'
|
|
3
3
|
describe "the language" do
|
4
4
|
include Crisp::SpecHelper
|
5
5
|
|
6
|
-
it "does not bother
|
6
|
+
it "does not bother whitspace 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 "
|
10
|
+
it "raises a syntax error on invalid expressions" do
|
11
11
|
lambda do
|
12
12
|
evaluate("(()")
|
13
13
|
end.should raise_error(Crisp::SyntaxError, "syntax error at : 0")
|
14
14
|
end
|
15
15
|
|
16
|
-
it "
|
16
|
+
it "binds arrays to symbols" do
|
17
17
|
evaluate("(def bla [1 2 3])").size.should == 3
|
18
18
|
evaluate("(def bla [1 2 3])")[1].should == 2
|
19
19
|
evaluate("(def foo 5)(def bla [1 2 foo])")[2].should == 5
|
data/spec/crisp/core_spec.rb
CHANGED
@@ -80,4 +80,140 @@ describe "the core language features" do
|
|
80
80
|
it "resolves symbols in if statements" do
|
81
81
|
evaluate("(def foo 2)(if true foo)").should == 2
|
82
82
|
end
|
83
|
+
|
84
|
+
it "uses local binding with let" do
|
85
|
+
evaluate("(let [x 1] x)").should == 1
|
86
|
+
end
|
87
|
+
|
88
|
+
it "uses more complex local bindings with let" do
|
89
|
+
evaluate("(let [x 2 y x] (* x y))").should == 4
|
90
|
+
end
|
91
|
+
|
92
|
+
it "evaluates several expressions within local binding" do
|
93
|
+
evaluate("(let [x 2 y 3] (* x y) (+ x y))").should == 5
|
94
|
+
end
|
95
|
+
|
96
|
+
it "binds symbols to global binding within local binding" do
|
97
|
+
evaluate("(let [x 1 y 2] (def foo (+ x y))) foo").should == 3
|
98
|
+
end
|
99
|
+
|
100
|
+
it "overrides global binding within local binding" do
|
101
|
+
evaluate("(def x 1)(let [x 2] x)").should == 2
|
102
|
+
end
|
103
|
+
|
104
|
+
it "ensures that local binding is only valid within let" do
|
105
|
+
evaluate("(def x 1)(let [x 2] x) x").should == 1
|
106
|
+
end
|
107
|
+
|
108
|
+
it "can handle emtpy local binding" do
|
109
|
+
evaluate("(let [] 2)").should == 2
|
110
|
+
end
|
111
|
+
|
112
|
+
it "raises an error when calling let without correct argument" do
|
113
|
+
lambda do
|
114
|
+
evaluate("(let 2 2)")
|
115
|
+
end.should raise_error(Crisp::ArgumentError, "no argument list defined")
|
116
|
+
end
|
117
|
+
|
118
|
+
it "raises an error when calling let with odd binding list" do
|
119
|
+
lambda do
|
120
|
+
evaluate("(let [x 1 y] 2)")
|
121
|
+
end.should raise_error(Crisp::ArgumentError, "argument list has to contain even list of arguments")
|
122
|
+
end
|
123
|
+
|
124
|
+
it "raises an error if file to be load not there" do
|
125
|
+
lambda do
|
126
|
+
evaluate('(load "not_there")')
|
127
|
+
end.should raise_error(Crisp::ArgumentError, /file (.*) not found/)
|
128
|
+
|
129
|
+
lambda do
|
130
|
+
evaluate('(load "/not_there")')
|
131
|
+
end.should raise_error(Crisp::ArgumentError, "file /not_there.crisp not found")
|
132
|
+
end
|
133
|
+
|
134
|
+
it "loads other crisp files" do
|
135
|
+
File.open("/tmp/crisp_test_file.crisp", 'w') do |f|
|
136
|
+
f << "(def foo 123)"
|
137
|
+
end
|
138
|
+
|
139
|
+
evaluate('(load "/tmp/crisp_test_file")(+ 1 foo)').should == 124
|
140
|
+
end
|
141
|
+
|
142
|
+
it "uses the current environment when loading other crisp source files" do
|
143
|
+
File.open("/tmp/crisp_test_file.crisp", 'w') do |f|
|
144
|
+
f << "(def bla 123)"
|
145
|
+
end
|
146
|
+
|
147
|
+
lambda do
|
148
|
+
evaluate('(def bla 321)(load "/tmp/crisp_test_file")')
|
149
|
+
end.should raise_error(Crisp::EnvironmentError, "bla already binded")
|
150
|
+
end
|
151
|
+
|
152
|
+
it "raises an error if calling cond with wrong number of arguments" do
|
153
|
+
lambda do
|
154
|
+
evaluate("(cond false 1 true)")
|
155
|
+
end.should raise_error(Crisp::ArgumentError, "argument list has to contain even list of arguments")
|
156
|
+
end
|
157
|
+
|
158
|
+
it "has cond statement" do
|
159
|
+
evaluate("(cond)").should == nil
|
160
|
+
evaluate("(cond false 1 false 2)").should == nil
|
161
|
+
evaluate("(cond true 3)").should == 3
|
162
|
+
evaluate("(cond true 3 false 2)").should == 3
|
163
|
+
evaluate("(cond false 3 true 2)").should == 2
|
164
|
+
evaluate("(cond (= 1 1) (+ 1 1))").should == 2
|
165
|
+
evaluate("(cond true 3 true 2 true 1)").should == 3
|
166
|
+
end
|
167
|
+
|
168
|
+
it "does not eval expressions for unmatched condition" do
|
169
|
+
evaluate("(cond false (def foo 1) true 2)(def foo 2) foo").should == 2
|
170
|
+
end
|
171
|
+
|
172
|
+
it "has a default condition in cond" do
|
173
|
+
evaluate("(cond false 1 true 2 else 3)").should == 2
|
174
|
+
evaluate("(cond true 1 true 2 else 3)").should == 1
|
175
|
+
evaluate("(cond false 1 else 2 true 3)").should == 2
|
176
|
+
end
|
177
|
+
|
178
|
+
it "raises an error when calling loop without correct argument" do
|
179
|
+
lambda do
|
180
|
+
evaluate("(loop 2 2)")
|
181
|
+
end.should raise_error(Crisp::ArgumentError, "no argument list defined")
|
182
|
+
end
|
183
|
+
|
184
|
+
it "raises an error when calling loop with odd binding list" do
|
185
|
+
lambda do
|
186
|
+
evaluate("(loop [x 1 y] 2)")
|
187
|
+
end.should raise_error(Crisp::ArgumentError, "argument list has to contain even list of arguments")
|
188
|
+
end
|
189
|
+
|
190
|
+
it "raises an error when calling recur outside a loop" do
|
191
|
+
lambda do
|
192
|
+
evaluate("(recur 1)")
|
193
|
+
end.should raise_error(Crisp::LoopError, "recur called outside loop")
|
194
|
+
end
|
195
|
+
|
196
|
+
it "raises an error when calling recur with wrong number of arguments" do
|
197
|
+
lambda do
|
198
|
+
evaluate("(loop [x 1] (recur 1 2))")
|
199
|
+
end.should raise_error(Crisp::ArgumentError, "wrong number of arguments for 'recur' (2 for 1)")
|
200
|
+
end
|
201
|
+
|
202
|
+
it "calculates factorials using loop recur" do
|
203
|
+
evaluate("
|
204
|
+
(def factorial
|
205
|
+
(fn [n]
|
206
|
+
(loop [cnt n acc 1]
|
207
|
+
(if (= 0 cnt)
|
208
|
+
acc
|
209
|
+
(recur (- cnt 1) (* acc cnt))))))
|
210
|
+
(factorial 5)
|
211
|
+
").should == 120
|
212
|
+
end
|
213
|
+
|
214
|
+
it "raises an error when nesting loops" do
|
215
|
+
lambda do
|
216
|
+
evaluate("(loop [x 1] (loop [a 1 b 2] (+ a b)))")
|
217
|
+
end.should raise_error(Crisp::LoopError, "nested loops are not allowed")
|
218
|
+
end
|
83
219
|
end
|
data/spec/crisp/function_spec.rb
CHANGED
@@ -15,10 +15,11 @@ describe "the languages functions" do
|
|
15
15
|
end.should raise_error(Crisp::ArgumentError, "no argument list defined")
|
16
16
|
end
|
17
17
|
|
18
|
-
it "does
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
it "does return primitive values as result" do
|
19
|
+
evaluate("((fn [] 1))").should == 1
|
20
|
+
evaluate('((fn [] "abc"))').should == 'abc'
|
21
|
+
evaluate('((fn [] [1 2 3]))').should == [1, 2, 3]
|
22
|
+
evaluate("((fn [x] x) 2)").should == 2
|
22
23
|
end
|
23
24
|
|
24
25
|
it "creates functions" do
|
@@ -3,21 +3,27 @@ require 'spec_helper'
|
|
3
3
|
describe "NativeCallInvoker functionality" do
|
4
4
|
include Crisp::SpecHelper
|
5
5
|
|
6
|
-
it "
|
6
|
+
it "executes ruby native String#reverse" do
|
7
7
|
evaluate('(. reverse "foobar")').should == 'raboof'
|
8
8
|
end
|
9
9
|
|
10
|
-
it "
|
10
|
+
it "executes ruby native Array#first" do
|
11
11
|
evaluate('(. first [1 2 3])').should == 1
|
12
12
|
end
|
13
13
|
|
14
|
-
it "
|
14
|
+
it "executes ruby native Array#first(n)" do
|
15
15
|
evaluate('(. first [1 2 3] 2)').should == [1,2]
|
16
16
|
end
|
17
17
|
|
18
|
-
it "
|
18
|
+
it "raises a named exception on invalid method" do
|
19
19
|
lambda do
|
20
20
|
evaluate('(. foo "bar")')
|
21
21
|
end.should raise_error(NoMethodError, %q{undefined method `foo' for "bar":String})
|
22
22
|
end
|
23
|
+
|
24
|
+
it "executes calls on Ruby classes" do
|
25
|
+
evaluate("(. new String)").should == ''
|
26
|
+
evaluate('(. new String "123")').should == '123'
|
27
|
+
evaluate("(. new Array 5 2)").should == [2, 2, 2, 2, 2]
|
28
|
+
end
|
23
29
|
end
|
data/spec/crisp/string_spec.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: crisp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 15
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 8
|
10
|
+
version: 0.0.8
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Markus Gerdes
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2011-01-06 00:00:00 +01:00
|
19
19
|
default_executable: crisp
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -42,12 +42,12 @@ dependencies:
|
|
42
42
|
requirements:
|
43
43
|
- - ~>
|
44
44
|
- !ruby/object:Gem::Version
|
45
|
-
hash:
|
45
|
+
hash: 31
|
46
46
|
segments:
|
47
47
|
- 2
|
48
|
-
-
|
48
|
+
- 4
|
49
49
|
- 0
|
50
|
-
version: 2.
|
50
|
+
version: 2.4.0
|
51
51
|
type: :development
|
52
52
|
version_requirements: *id002
|
53
53
|
description:
|
@@ -67,7 +67,9 @@ files:
|
|
67
67
|
- autotest/discover.rb
|
68
68
|
- bin/crisp
|
69
69
|
- crisp.gemspec
|
70
|
+
- examples/factorial.crisp
|
70
71
|
- examples/fibonacci.crisp
|
72
|
+
- examples/run.crisp
|
71
73
|
- lib/crisp.rb
|
72
74
|
- lib/crisp/chained_env.rb
|
73
75
|
- lib/crisp/crisp.treetop
|