flea 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/build.yml +30 -0
  3. data/.gitignore +3 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +30 -0
  6. data/Gemfile +5 -0
  7. data/Gemfile.lock +65 -0
  8. data/MIT-LICENSE +1 -1
  9. data/README.md +345 -0
  10. data/bin/flea +17 -15
  11. data/flea-language-spec/{README.rdoc → README.md} +2 -2
  12. data/flea-language-spec/test-cases/01-display-and-basic-literals/05-display-list-literal-using-quote.scm +1 -1
  13. data/flea-language-spec/test-cases/02-variables/05-define-with-list.scm +1 -1
  14. data/flea-language-spec/test-cases/04-quoting/01-quoting.scm +5 -0
  15. data/flea.gemspec +34 -0
  16. data/lib/flea/environment.rb +20 -18
  17. data/lib/flea/interpreter.rb +44 -44
  18. data/lib/flea/standard_library/display.scm +7 -1
  19. data/lib/flea/standard_library/null.scm +1 -1
  20. data/lib/flea/standard_library/read.scm +1 -1
  21. data/lib/flea/version.rb +5 -0
  22. data/lib/flea.rb +6 -4
  23. metadata +110 -138
  24. data/README.rdoc +0 -274
  25. data/Rakefile +0 -36
  26. data/VERSION +0 -1
  27. data/flea-language-spec/test-cases/04-comma-quoting/01-comma-quoting.scm +0 -5
  28. data/spec/flea/environment_spec.rb +0 -114
  29. data/spec/flea/interpreter_spec.rb +0 -85
  30. data/spec/flea/standard_library/addition_operator_spec.rb +0 -23
  31. data/spec/flea/standard_library/append_spec.rb +0 -17
  32. data/spec/flea/standard_library/begin_spec.rb +0 -20
  33. data/spec/flea/standard_library/car_spec.rb +0 -17
  34. data/spec/flea/standard_library/cdr_spec.rb +0 -17
  35. data/spec/flea/standard_library/cons_spec.rb +0 -25
  36. data/spec/flea/standard_library/display_spec.rb +0 -36
  37. data/spec/flea/standard_library/division_operator_spec.rb +0 -29
  38. data/spec/flea/standard_library/equality_operator_spec.rb +0 -45
  39. data/spec/flea/standard_library/gets_spec.rb +0 -23
  40. data/spec/flea/standard_library/greater_than_spec.rb +0 -29
  41. data/spec/flea/standard_library/if_spec.rb +0 -59
  42. data/spec/flea/standard_library/lambda_spec.rb +0 -70
  43. data/spec/flea/standard_library/less_than_spec.rb +0 -29
  44. data/spec/flea/standard_library/list_predicate_spec.rb +0 -25
  45. data/spec/flea/standard_library/list_spec.rb +0 -24
  46. data/spec/flea/standard_library/list_tail_spec.rb +0 -17
  47. data/spec/flea/standard_library/mutiplication_operator_spec.rb +0 -23
  48. data/spec/flea/standard_library/null_spec.rb +0 -26
  49. data/spec/flea/standard_library/quote_spec.rb +0 -20
  50. data/spec/flea/standard_library/rand_spec.rb +0 -25
  51. data/spec/flea/standard_library/read_spec.rb +0 -23
  52. data/spec/flea/standard_library/set_spec.rb +0 -23
  53. data/spec/flea/standard_library/string_to_num_spec.rb +0 -28
  54. data/spec/flea/standard_library/subtraction_operator_spec.rb +0 -24
  55. data/spec/spec_helper.rb +0 -10
data/README.rdoc DELETED
@@ -1,274 +0,0 @@
1
- = Flea
2
-
3
- Flea is a tiny Lisp interpreter implemented in Ruby. Flea is not designed to be a production language, instead it is designed to be an example of how simple it can be to bootstrap the core of a small flexible language. Flea essentially defines an informal subset of Scheme, just enough to be fun and interesting.
4
-
5
- === A Quick Example
6
-
7
- This is the classic 'guess the number' program implemented in Flea:
8
-
9
- (define number (+ (rand 9) 1))
10
-
11
- (display "\n\nI'm thinking of a number between 1 and 10,\n")
12
- (display "try to guess it!\n\n")
13
-
14
- (define user-guess
15
- (lambda ()
16
- (display "Take a guess - ")
17
- (define guess (string-to-num (gets)))
18
- (if (equal? guess number)
19
- (display "Good guess!\n")
20
- (begin
21
- (if (greater-than? guess number)
22
- (display "Lower!\n")
23
- (display "Higher!\n"))
24
- (user-guess)))))
25
-
26
- (user-guess)
27
-
28
- === Installation
29
-
30
- For ease of use the Flea is packaged as a RubyGem. Providing you already have Ruby and RubyGems installing Flea is as easy as entering the following command in a terminal:
31
-
32
- gem install flea
33
-
34
- Mac OS X and most Unix/Linux distributions come with an installation of Ruby and RubyGems. If you do not have Ruby and RubyGems installed please check the {Ruby website for instructions}[http://www.ruby-lang.org/en/downloads/].
35
-
36
- === Usage
37
-
38
- After Flea is installed you can run a program by typing the following on the command line:
39
-
40
- flea /path/to/program
41
-
42
- You can also launch Flea's interactive shell by simply calling `flea` with no arguments.
43
-
44
- === Author & Credits
45
-
46
- Author:: {Aaron Gough}[mailto:aaron@aarongough.com]
47
-
48
- Copyright (c) 2010 {Aaron Gough}[http://thingsaaronmade.com/] ({thingsaaronmade.com}[http://thingsaaronmade.com/]), released under the MIT license
49
-
50
- ---
51
-
52
- == API Documentation and More Info
53
-
54
- === More Info
55
-
56
- Flea's core is only about 100 lines of code, and provides a simple interface that is used to build the rest of the language. Flea is highly extensible because of its foreign function interface which allows Ruby Proc objects to be defined and then executed within the context of the Flea interpreter. The entire standard library of the language is implemented using this foreign function interface. For example, here is the implementation of the multiplication operator:
57
-
58
- (define *
59
- (native_function "
60
- Proc.new() do |arguments, interpreter|
61
- tmp = arguments.map {|item| interpreter.evaluate(item)}
62
- tmp.inject {|sum, n| sum * n}
63
- end
64
- "))
65
-
66
- === API
67
-
68
- Flea comes with a small but thoughtful standard library that includes functions for:
69
-
70
- ==== Output
71
-
72
- (display) Outputs data to STDOUT:
73
-
74
- (display "test")
75
- # => test
76
-
77
- (display '(1 2 3))
78
- # => (1 2 3)
79
-
80
- ==== Input
81
-
82
- (read) Reads and parses an s-expression from STDIN:
83
-
84
- (read) # user enters '(1 2 3)'
85
- # => (1 2 3)
86
-
87
- (gets) Reads a string from STDIN:
88
-
89
- (gets) # user enters '(1 2 3)'
90
- # => "(1 2 3)"
91
-
92
- ==== Variables
93
-
94
- (define) Sets a variable:
95
-
96
- (define test 1)
97
- (display test)
98
- # => 1
99
-
100
- (set!) Re-defines an existing variable:
101
-
102
- (define test 1)
103
- (set! test 2)
104
- (display test)
105
- # => 2
106
-
107
- ==== Numeric operations
108
-
109
- (+) Add one or more numbers:
110
-
111
- (+ 1 2 3)
112
- # => 6
113
-
114
- (-) Subtract one or more numbers:
115
-
116
- (- 10 2 1)
117
- # => 7
118
-
119
- (*) Multiply one or more numbers:
120
-
121
- (* 10 2 4)
122
- # => 80
123
-
124
- (/) Divide one or more numbers:
125
-
126
- (/ 100 2 2)
127
- # => 25
128
-
129
- (greater-than?) Returns true if it's first argument is greater than all the others, false otherwise:
130
-
131
- (greater-than? 10 2 3 4)
132
- # => #t
133
-
134
- (greater-than? 1 2 3 4)
135
- # => #f
136
-
137
- (less-than?) Returns true if it's first argument is smaller than all the others, false otherwise:
138
-
139
- (less-than? 1 3 4 5)
140
- # => #t
141
-
142
- (less-than? 50 2 45 100)
143
- # => #f
144
-
145
- (equal?) and (=) Return true if all their arguments are the same, false otherwise:
146
-
147
- (equal? 1 1 1)
148
- # => #t
149
-
150
- (= 1 2 3)
151
- # => #f
152
-
153
- ==== List creation and manipulation
154
-
155
- (list) Creates a new list from it's arguments:
156
-
157
- (list (+ 1 2) "test" (rand 10))
158
- # => (3 "test" 5)
159
-
160
- (list?) Returns true if it's first argument is a list, false otherwise:
161
-
162
- (list? '())
163
- # => #t
164
-
165
- (list? 1)
166
- # => #f
167
-
168
- (list-tail) Returns a new list created by removing the first n elements of the provided list:
169
-
170
- (define a '(1 2 3 4 5 6))
171
- (list-tail a 3)
172
- # => (4 5 6)
173
-
174
- (car) Returns the first item of a list:
175
-
176
- (car '(1 2 3))
177
- # => 1
178
-
179
- (cdr) Returns the remainder of a list:
180
-
181
- (cdr '(1 2 3))
182
- # => (2 3)
183
-
184
- (cons) Creates a new list by using it's first argument as the CAR it's second argument as the CDR:
185
-
186
- (cons '(1 2 3) 3)
187
- # => ((1 2 3) 3)
188
-
189
- (cons 1 2)
190
- # => (1 2)
191
-
192
- (cons 1 (2 3 4))
193
- # => (1 2 3 4)
194
-
195
- (append) Creates a new list by concatenating it's arguments, it's first argument must be a list:
196
-
197
- (append '(1 2) '(3 4) 5)
198
- # => (1 2 3 4 5)
199
-
200
- (null?) Returns true if it's first argument is null (the empty list):
201
-
202
- (null '())
203
- # => #t
204
-
205
- (null '(1 2 3))
206
- # => #f
207
-
208
- (null? 1)
209
- # => #f
210
-
211
- ==== Conditionals
212
-
213
- (if) If it's first argument is true then it will execute it's first code block, otherwise it will execute the second code block (if any):
214
-
215
- (if (equal? 1 1)
216
- (display "true")
217
- (display "false"))
218
- # => true
219
-
220
- (if (equal? 2 1)
221
- (display "true")
222
- (display "false"))
223
- # => false
224
-
225
- ==== Function creation
226
-
227
- (lambda) Creates a new function:
228
-
229
- (define adder
230
- (lambda (a)
231
- (+ a 10)))
232
-
233
- (adder 5)
234
- # => 15
235
-
236
- For more info on lambda syntax read the {R5RS Scheme specification}[http://schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-7.html#%_sec_4.1.4]
237
-
238
- ==== Type conversion
239
-
240
- (string-to-num) Converts a string containing a number into a numeric literal:
241
-
242
- (string-to-num "123")
243
- # => 123
244
-
245
- ==== Misc
246
-
247
- (rand) Returns a random number less than or equal to it's first argument:
248
-
249
- (rand 10)
250
- # => 1
251
-
252
- (rand 10)
253
- # => 6
254
-
255
- ==== Foreign function interface
256
-
257
- (native_function) Takes a string representing a Ruby Proc object and returns it in a form that is able to be called like any other Flea function. The proc must take two arguments (arguments and interpreter):
258
-
259
- (define test
260
- (native_function "
261
- Proc.new() do |arguments, interpreter|
262
- puts "Arguments class: " + arguments.class
263
- puts "Arguments: " + arguments.inspect
264
- puts "Interpreter class: " + interpreter.class
265
- puts "foo"
266
- end
267
- ")
268
-
269
- (test 1 2 3 4)
270
- # => Arguments class: Array
271
- Arguments: [1, 2, 3, 4]
272
- Interpreter class: Flea::Interpreter
273
- foo
274
-
data/Rakefile DELETED
@@ -1,36 +0,0 @@
1
- require 'rubygems'
2
- require 'any-spec'
3
- require 'rspec/core/rake_task'
4
-
5
- begin
6
- require 'jeweler'
7
- Jeweler::Tasks.new do |gemspec|
8
- gemspec.name = "flea"
9
- gemspec.summary = "A tiny but flexible Lisp interpreter written in Ruby"
10
- gemspec.description = "Flea is an extremely simple, but extremely extensible Lisp interpreter written in Ruby."
11
- gemspec.email = "aaron@aarongough.com"
12
- gemspec.homepage = "http://github.com/aarongough/flea"
13
- gemspec.authors = ["Aaron Gough"]
14
- gemspec.rdoc_options << '--line-numbers' << '--inline-source'
15
- gemspec.extra_rdoc_files = ['README.rdoc', 'MIT-LICENSE']
16
- gemspec.add_dependency "sexpistol"
17
- gemspec.add_development_dependency "any-spec"
18
- gemspec.executables << 'flea'
19
- end
20
- rescue LoadError
21
- puts "Jeweler not available. Install it with: gem install jeweler"
22
- end
23
-
24
- task :test => [:spec, :anyspec]
25
-
26
- task :anyspec do
27
- test_runner = AnySpec::TestRunner.new("./bin/flea", "./flea-language-spec/flea-language-spec.yaml")
28
- test_runner.run_tests
29
- end
30
-
31
- desc "Run the specs for Flea"
32
- RSpec::Core::RakeTask.new do |t|
33
- t.rspec_opts = "-c"
34
- t.fail_on_error = false
35
- t.verbose = false
36
- end
data/VERSION DELETED
@@ -1 +0,0 @@
1
- 0.1.0
@@ -1,5 +0,0 @@
1
- (display '(1 "Hello!" #t #f))
2
-
3
- ----
4
-
5
- assert_output "(1 Hello! #t #f)"
@@ -1,114 +0,0 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
2
-
3
- describe "Flea" do
4
- describe "::Environment" do
5
-
6
- include Flea
7
-
8
- describe ".new" do
9
- it "should return an environment object" do
10
- Environment.new.should be_an Environment
11
- end
12
-
13
- it "should return an environment object with no parent" do
14
- environment = Environment.new
15
- environment.parent.should be_nil
16
- end
17
-
18
- it "should return an environment object with the specified parent" do
19
- parent_environment = mock("Environment")
20
- environment = Environment.new(parent_environment)
21
- environment.parent.should be parent_environment
22
- end
23
-
24
- it "should add base variables for #t and #f" do
25
- environment = Environment.new
26
- environment.should have_variable :"#t"
27
- environment.should have_variable :"#f"
28
- environment.find(:"#t").should be_true
29
- environment.find(:"#f").should be_false
30
- end
31
- end
32
-
33
- describe "#has_variable?" do
34
- context "without a parent" do
35
- before :each do
36
- @environment = Environment.new
37
- end
38
-
39
- it "should return false if the variable is not set in the current environment" do
40
- @environment.should_not have_variable :test
41
- end
42
-
43
- it "should return true if the variable is set in the current environment" do
44
- @environment.define(:test, 1)
45
- @environment.should have_variable :test
46
- end
47
- end
48
-
49
- context "with a parent" do
50
- before :each do
51
- @parent_environment = Environment.new
52
- @environment = Environment.new(@parent_environment)
53
- end
54
-
55
- it "should return false if the variable is not set in the parent environment" do
56
- @environment.should_not have_variable :test
57
- end
58
-
59
- it "should return true if the variable is set in the parent environment" do
60
- @parent_environment.define(:test, 1)
61
- @environment.should have_variable :test
62
- end
63
- end
64
- end
65
-
66
- describe "#define" do
67
- it "should set a variable to the supplied value" do
68
- env = Environment.new
69
- result = env.define(:test, 1)
70
- env.should have_variable :test
71
- env.find(:test).should == 1
72
- result.should == 1
73
- end
74
- end
75
-
76
- describe "#find" do
77
- context "without a parent" do
78
- before :each do
79
- @environment = Environment.new
80
- @environment.define(:test, 1)
81
- end
82
-
83
- it "should find a variable in the current environment" do
84
- @environment.find(:test).should == 1
85
- end
86
-
87
- it "should return nil when variable is not set" do
88
- @environment.find(:fake).should be_nil
89
- end
90
- end
91
-
92
- context "with a parent" do
93
- before :each do
94
- @parent_environment = Environment.new
95
- @environment = Environment.new(@parent_environment)
96
- @parent_environment.define(:test, 1)
97
- end
98
-
99
- it "should find a variable in the parent environment" do
100
- @environment.find(:test).should == 1
101
- end
102
-
103
- it "should return a variable from the current environment if it is set in both current and parent" do
104
- @environment.define(:test, 5)
105
- @environment.find(:test).should == 5
106
- end
107
-
108
- it "should return nil when variable is not set in the current or parent environment" do
109
- @environment.find(:fake).should be_nil
110
- end
111
- end
112
- end
113
- end
114
- end
@@ -1,85 +0,0 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
2
-
3
- describe "Flea" do
4
- describe "::Interpreter" do
5
-
6
- include Flea
7
-
8
- describe "#new" do
9
- it "should return an Interpreter" do
10
- Interpreter.new.should be_an Interpreter
11
- end
12
-
13
- it "should allow setting the base environment to use" do
14
- environment = mock("Environment")
15
- interpreter = Interpreter.new(
16
- :base_environment => environment,
17
- :load_standard_library => false
18
- )
19
- interpreter.base_environment.should be environment
20
- end
21
- end
22
-
23
- describe ".run" do
24
- it "should run a program and return the last output from the program" do
25
- interpreter = Interpreter.new
26
- result = interpreter.run("(define test 1)")
27
- result.should == 1
28
- interpreter.base_environment.should have_variable :test
29
- end
30
- end
31
-
32
- describe ".parse" do
33
- it "should return an abstract syntax tree representing the supplied program" do
34
- ast = Interpreter.new.parse("(define test 1)")
35
- ast.should == [[:define, :test, 1]]
36
- end
37
- end
38
-
39
- describe ".evaluate" do
40
- before :each do
41
- @environment = mock("Environment")
42
- @interpreter = Interpreter.new(
43
- :base_environment => @environment,
44
- :load_standard_library => false
45
- )
46
- end
47
-
48
- it "should return the value of a variable" do
49
- @environment.should_receive(:find).with(:test).and_return(1)
50
- result = @interpreter.evaluate(:test)
51
- result.should == 1
52
- end
53
-
54
- it "should define a variable in the current environment" do
55
- @environment.should_receive(:define).with(:test, 1).and_return(1)
56
- result = @interpreter.evaluate([:define, :test, 1])
57
- result.should == 1
58
- end
59
-
60
- it "should create a native function" do
61
- result = @interpreter.evaluate([:native_function, "
62
- Proc.new() do |arguments, interpreter|
63
- 1
64
- end
65
- "])
66
- result.should be_a Proc
67
- result.call.should == 1
68
- end
69
-
70
- it "should call a native function" do
71
- @environment.should_receive(:find).with(:foo).and_return(Proc.new {|a,b| "bar"})
72
- result = @interpreter.evaluate([:foo, 1, 2, 3])
73
- result.should == "bar"
74
- end
75
-
76
- [1, 1.0, "string"].each do |literal|
77
- it "should return literal '#{literal}' as is" do
78
- result = @interpreter.evaluate(literal)
79
- result.should be literal
80
- end
81
- end
82
- end
83
-
84
- end
85
- end
@@ -1,23 +0,0 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
2
-
3
- describe "Standard Library" do
4
- describe "addition operator" do
5
- before :each do
6
- @interpreter = Flea::Interpreter.new
7
- end
8
-
9
- it "should add several numbers" do
10
- result = @interpreter.run('(+ 1 2 3)')
11
- result.should == 6
12
- end
13
-
14
- it "should evaluate its arguments before adding them" do
15
- result = @interpreter.run('
16
- (define a 2)
17
- (+ a a a)
18
- ')
19
- result.should == 6
20
- end
21
-
22
- end
23
- end
@@ -1,17 +0,0 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
2
-
3
- describe "Standard Library" do
4
- describe "append" do
5
- before :each do
6
- @interpreter = Flea::Interpreter.new
7
- end
8
-
9
- it "union of two lists" do
10
- result = @interpreter.run('
11
- (append (quote (1 2 3)) (quote (1 2 3)))
12
- ')
13
- result.should == [1, 2, 3, 1, 2, 3]
14
- end
15
-
16
- end
17
- end
@@ -1,20 +0,0 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
2
-
3
- describe "Standard Library" do
4
- describe "begin" do
5
- before :each do
6
- @interpreter = Flea::Interpreter.new
7
- end
8
-
9
- it "should execute each expression after the begin call and return final value" do
10
- result = @interpreter.run('
11
- (begin
12
- (define test 1)
13
- (set! test 2))
14
- ')
15
- result.should == 2
16
- @interpreter.base_environment.find(:test).should == 2
17
- end
18
-
19
- end
20
- end
@@ -1,17 +0,0 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
2
-
3
- describe "Standard Library" do
4
- describe "car" do
5
- before :each do
6
- @interpreter = Flea::Interpreter.new
7
- end
8
-
9
- it "should return first item of list" do
10
- result = @interpreter.run('
11
- (car (quote (10 2 2)))
12
- ')
13
- result.should == 10
14
- end
15
-
16
- end
17
- end
@@ -1,17 +0,0 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
2
-
3
- describe "Standard Library" do
4
- describe "cdr" do
5
- before :each do
6
- @interpreter = Flea::Interpreter.new
7
- end
8
-
9
- it "should return remainder of list" do
10
- result = @interpreter.run('
11
- (cdr (quote (10 2 2)))
12
- ')
13
- result.should == [2, 2]
14
- end
15
-
16
- end
17
- end
@@ -1,25 +0,0 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
2
-
3
- describe "Standard Library" do
4
- describe "cons" do
5
- before :each do
6
- @interpreter = Flea::Interpreter.new
7
- end
8
-
9
- it "should create a list from an atom and an empty list" do
10
- result = @interpreter.run("(cons 'a '())")
11
- result.should == [:a]
12
- end
13
-
14
- it "should create a list from a pair of lists" do
15
- result = @interpreter.run("(cons '(a) '(b c d))")
16
- result.should == [[:a], :b, :c, :d]
17
- end
18
-
19
- it "should push an atom on an existing list" do
20
- result = @interpreter.run('(cons "a" \'(b c ))')
21
- result.should == ["a", :b, :c]
22
- end
23
-
24
- end
25
- end
@@ -1,36 +0,0 @@
1
- require File.dirname(__FILE__) + '/../../spec_helper'
2
-
3
- describe "Standard Library" do
4
- describe "display" do
5
- before :each do
6
- @interpreter = Flea::Interpreter.new
7
- @old_stdout = $stdout
8
- @buffer = StringIO.new
9
- $stdout = @buffer
10
- end
11
-
12
- it "should output simple literal" do
13
- @interpreter.run('(display 1)')
14
- @buffer.string.should == "1"
15
- end
16
-
17
- it "should output a list" do
18
- @interpreter.run('(display (quote (1 2 3)))')
19
- @buffer.string.should == "(1 2 3)"
20
- end
21
-
22
- it "should output true and false using Scheme external representation" do
23
- @interpreter.run('(display #t)(display #f)')
24
- @buffer.string.should == "#t#f"
25
- end
26
-
27
- it "should return the same value that it displayed" do
28
- result = @interpreter.run('(display "abc")')
29
- result.should == "abc"
30
- end
31
-
32
- after :each do
33
- $stdout = @old_stdout
34
- end
35
- end
36
- end