flea 0.1.0
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/.gitignore +1 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +274 -0
- data/Rakefile +36 -0
- data/VERSION +1 -0
- data/bin/flea +39 -0
- data/examples/guess-the-number.scm +18 -0
- data/flea-language-spec/README.rdoc +3 -0
- data/flea-language-spec/flea-language-spec.yaml +2 -0
- data/flea-language-spec/test-cases/01-display-and-basic-literals/01-display-string-literal.scm +5 -0
- data/flea-language-spec/test-cases/01-display-and-basic-literals/02-display-integer-literal.scm +5 -0
- data/flea-language-spec/test-cases/01-display-and-basic-literals/03-display-boolean-true-literal.scm +5 -0
- data/flea-language-spec/test-cases/01-display-and-basic-literals/04-display-boolean-false-literal.scm +5 -0
- data/flea-language-spec/test-cases/01-display-and-basic-literals/05-display-list-literal-using-quote.scm +5 -0
- data/flea-language-spec/test-cases/01-display-and-basic-literals/06-display-identifier-quoted.scm +5 -0
- data/flea-language-spec/test-cases/02-variables/01-define-with-string.scm +6 -0
- data/flea-language-spec/test-cases/02-variables/02-define-with-integer.scm +6 -0
- data/flea-language-spec/test-cases/02-variables/03-define-with-boolean-true.scm +6 -0
- data/flea-language-spec/test-cases/02-variables/04-define-with-boolean-false.scm +6 -0
- data/flea-language-spec/test-cases/02-variables/05-define-with-list.scm +6 -0
- data/flea-language-spec/test-cases/03-basic-built-in-procedures/01-addition.scm +5 -0
- data/flea-language-spec/test-cases/03-basic-built-in-procedures/02-subtraction.scm +5 -0
- data/flea-language-spec/test-cases/03-basic-built-in-procedures/03-multiplication.scm +5 -0
- data/flea-language-spec/test-cases/03-basic-built-in-procedures/04-division.scm +5 -0
- data/flea-language-spec/test-cases/03-basic-built-in-procedures/05-equality-true-integer.scm +5 -0
- data/flea-language-spec/test-cases/03-basic-built-in-procedures/06-equality-false-integer.scm +5 -0
- data/flea-language-spec/test-cases/03-basic-built-in-procedures/07-equal?/01-equal?-true-integer.scm +5 -0
- data/flea-language-spec/test-cases/03-basic-built-in-procedures/07-equal?/02-equal?-false-integer.scm +5 -0
- data/flea-language-spec/test-cases/03-basic-built-in-procedures/07-equal?/03-equal?-true-string.scm +5 -0
- data/flea-language-spec/test-cases/03-basic-built-in-procedures/07-equal?/04-equal?-false-string.scm +5 -0
- data/flea-language-spec/test-cases/03-basic-built-in-procedures/07-equal?/05-equal?-true-boolean.scm +5 -0
- data/flea-language-spec/test-cases/03-basic-built-in-procedures/07-equal?/06-equal?-false-boolean.scm +5 -0
- data/flea-language-spec/test-cases/03-basic-built-in-procedures/07-equal?/07-equal?-true-list.scm +7 -0
- data/flea-language-spec/test-cases/03-basic-built-in-procedures/07-equal?/08-equal?-false-list.scm +7 -0
- data/flea-language-spec/test-cases/04-comma-quoting/01-comma-quoting.scm +5 -0
- data/flea-language-spec/test-cases/05-lambda/01-lambda.scm +5 -0
- data/flea-language-spec/test-cases/05-lambda/02-call-in-place-lambda.scm +5 -0
- data/flea-language-spec/test-cases/05-lambda/03-define-with-lambda.scm +6 -0
- data/flea-language-spec/test-cases/05-lambda/04-define-and-call-lambda.scm +8 -0
- data/flea-language-spec/test-cases/05-lambda/05-lambda-repeated-argument.scm +9 -0
- data/flea-language-spec/test-cases/05-lambda/06-lambda-list-argument.scm +8 -0
- data/flea-language-spec/test-cases/05-lambda/07-lambda-n-or-more-arguments.scm +12 -0
- data/flea-language-spec/test-cases/05-lambda/08-lambda-should-return-last-result.scm +16 -0
- data/flea-language-spec/test-cases/06-if/01-if-true-single-arg.scm +6 -0
- data/flea-language-spec/test-cases/06-if/02-if-false-single-arg.scm +6 -0
- data/flea-language-spec/test-cases/06-if/03-if-true-two-args.scm +7 -0
- data/flea-language-spec/test-cases/06-if/04-if-false-two-args.scm +7 -0
- data/flea-language-spec/test-cases/07-set/01-should-not-set-undefined-variable.scm +5 -0
- data/flea-language-spec/test-cases/07-set/02-set-previously-defined-variable.scm +8 -0
- data/flea-language-spec/test-cases/08-derived-expressions/01-begin/01-begin.scm +8 -0
- data/flea-language-spec/test-cases/09-list-manipulation/01-car.scm +7 -0
- data/flea-language-spec/test-cases/09-list-manipulation/02-cdr.scm +7 -0
- data/flea-language-spec/test-cases/09-list-manipulation/03-list-tail.scm +7 -0
- data/flea-language-spec/test-cases/09-list-manipulation/04-append.scm +10 -0
- data/flea-language-spec/test-cases/09-list-manipulation/05-list.scm +5 -0
- data/flea-language-spec/test-cases/10-functional-examples/countdown.scm +15 -0
- data/lib/flea.rb +5 -0
- data/lib/flea/environment.rb +38 -0
- data/lib/flea/interpreter.rb +67 -0
- data/lib/flea/standard_library/addition_operator.scm +7 -0
- data/lib/flea/standard_library/append.scm +7 -0
- data/lib/flea/standard_library/begin.scm +11 -0
- data/lib/flea/standard_library/car.scm +7 -0
- data/lib/flea/standard_library/cdr.scm +7 -0
- data/lib/flea/standard_library/cons.scm +13 -0
- data/lib/flea/standard_library/display.scm +8 -0
- data/lib/flea/standard_library/division_operator.scm +7 -0
- data/lib/flea/standard_library/equality_operator.scm +8 -0
- data/lib/flea/standard_library/gets.scm +6 -0
- data/lib/flea/standard_library/greater_than.scm +8 -0
- data/lib/flea/standard_library/if.scm +10 -0
- data/lib/flea/standard_library/lambda.scm +57 -0
- data/lib/flea/standard_library/less_than.scm +8 -0
- data/lib/flea/standard_library/list.scm +8 -0
- data/lib/flea/standard_library/list_predicate.scm +6 -0
- data/lib/flea/standard_library/list_tail.scm +5 -0
- data/lib/flea/standard_library/multiplication_operator.scm +7 -0
- data/lib/flea/standard_library/null.scm +3 -0
- data/lib/flea/standard_library/quote.scm +6 -0
- data/lib/flea/standard_library/rand.scm +6 -0
- data/lib/flea/standard_library/read.scm +6 -0
- data/lib/flea/standard_library/set.scm +9 -0
- data/lib/flea/standard_library/string_to_num.scm +6 -0
- data/lib/flea/standard_library/subtraction_operator.scm +7 -0
- data/spec/flea/environment_spec.rb +114 -0
- data/spec/flea/interpreter_spec.rb +85 -0
- data/spec/flea/standard_library/addition_operator_spec.rb +23 -0
- data/spec/flea/standard_library/append_spec.rb +17 -0
- data/spec/flea/standard_library/begin_spec.rb +20 -0
- data/spec/flea/standard_library/car_spec.rb +17 -0
- data/spec/flea/standard_library/cdr_spec.rb +17 -0
- data/spec/flea/standard_library/cons_spec.rb +25 -0
- data/spec/flea/standard_library/display_spec.rb +36 -0
- data/spec/flea/standard_library/division_operator_spec.rb +29 -0
- data/spec/flea/standard_library/equality_operator_spec.rb +45 -0
- data/spec/flea/standard_library/gets_spec.rb +23 -0
- data/spec/flea/standard_library/greater_than_spec.rb +29 -0
- data/spec/flea/standard_library/if_spec.rb +59 -0
- data/spec/flea/standard_library/lambda_spec.rb +70 -0
- data/spec/flea/standard_library/less_than_spec.rb +29 -0
- data/spec/flea/standard_library/list_predicate_spec.rb +25 -0
- data/spec/flea/standard_library/list_spec.rb +24 -0
- data/spec/flea/standard_library/list_tail_spec.rb +17 -0
- data/spec/flea/standard_library/mutiplication_operator_spec.rb +23 -0
- data/spec/flea/standard_library/null_spec.rb +26 -0
- data/spec/flea/standard_library/quote_spec.rb +20 -0
- data/spec/flea/standard_library/rand_spec.rb +25 -0
- data/spec/flea/standard_library/read_spec.rb +23 -0
- data/spec/flea/standard_library/set_spec.rb +23 -0
- data/spec/flea/standard_library/string_to_num_spec.rb +28 -0
- data/spec/flea/standard_library/subtraction_operator_spec.rb +24 -0
- data/spec/spec_helper.rb +10 -0
- metadata +231 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
(define cons
|
|
2
|
+
(native_function "
|
|
3
|
+
Proc.new() do |arguments, interpreter|
|
|
4
|
+
arg_1 = interpreter.evaluate(arguments[0])
|
|
5
|
+
arg_2 = interpreter.evaluate(arguments[1]).dup
|
|
6
|
+
|
|
7
|
+
if arg_2.is_a? Array
|
|
8
|
+
arg_2.unshift arg_1
|
|
9
|
+
else
|
|
10
|
+
[arg_1, arg_2]
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
"))
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
(define lambda
|
|
2
|
+
(native_function "
|
|
3
|
+
Proc.new() do |arguments, interpreter|
|
|
4
|
+
formals = arguments[0]
|
|
5
|
+
body = arguments.slice(1, arguments.length)
|
|
6
|
+
|
|
7
|
+
if formals.is_a? Array
|
|
8
|
+
# detect if any formal names have been used more than once
|
|
9
|
+
error_message = 'Formal {FORMAL} declared more than once'
|
|
10
|
+
formals.each_index do |x|
|
|
11
|
+
tmp = formals.dup
|
|
12
|
+
tmp.delete_at(x)
|
|
13
|
+
raise(error_message.gsub('{FORMAL}', formals[x])) if tmp.include? formals[x]
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
sub_env = Flea::Environment.new(interpreter.current_environment)
|
|
18
|
+
|
|
19
|
+
execute_body = Proc.new() do |body, environment, interpreter|
|
|
20
|
+
interpreter.current_environment = environment
|
|
21
|
+
result = nil
|
|
22
|
+
body.each do |expression|
|
|
23
|
+
result = interpreter.evaluate(expression)
|
|
24
|
+
end
|
|
25
|
+
interpreter.current_environment = environment.parent
|
|
26
|
+
result
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
if formals.is_a?(Array) && formals.include?(:'.')
|
|
30
|
+
Proc.new() do |arguments, interpreter|
|
|
31
|
+
args = arguments.dup
|
|
32
|
+
named_formals = formals.slice(0, formals.index(:'.'))
|
|
33
|
+
list_formal = formals[formals.index(:'.') + 1]
|
|
34
|
+
named_formals.each_index do |i|
|
|
35
|
+
sub_env.define(named_formals[i], interpreter.evaluate(args.shift))
|
|
36
|
+
end
|
|
37
|
+
sub_env.define(list_formal, args)
|
|
38
|
+
execute_body.call(body, sub_env, interpreter)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
elsif formals.is_a? Array
|
|
42
|
+
Proc.new() do |arguments, interpreter|
|
|
43
|
+
formals.each_index do |i|
|
|
44
|
+
sub_env.define(formals[i], interpreter.evaluate(arguments[i]))
|
|
45
|
+
end
|
|
46
|
+
execute_body.call(body, sub_env, interpreter)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
elsif formals.is_a? Symbol
|
|
50
|
+
Proc.new() do |arguments, interpreter|
|
|
51
|
+
arguments = arguments.map {|x| interpreter.evaluate(x) }
|
|
52
|
+
sub_env.define(formals, arguments)
|
|
53
|
+
execute_body.call(body, sub_env, interpreter)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
"))
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
(define set!
|
|
2
|
+
(native_function "
|
|
3
|
+
Proc.new() do |arguments, interpreter|
|
|
4
|
+
if( interpreter.current_environment.find(arguments[0]) == nil)
|
|
5
|
+
raise 'Cannot set unbound variable ' + arguments[0]
|
|
6
|
+
end
|
|
7
|
+
interpreter.current_environment.define(arguments[0], arguments[1])
|
|
8
|
+
end
|
|
9
|
+
"))
|
|
@@ -0,0 +1,114 @@
|
|
|
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
|
|
@@ -0,0 +1,85 @@
|
|
|
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
|