flea 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|