flea 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. data/.gitignore +1 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +274 -0
  4. data/Rakefile +36 -0
  5. data/VERSION +1 -0
  6. data/bin/flea +39 -0
  7. data/examples/guess-the-number.scm +18 -0
  8. data/flea-language-spec/README.rdoc +3 -0
  9. data/flea-language-spec/flea-language-spec.yaml +2 -0
  10. data/flea-language-spec/test-cases/01-display-and-basic-literals/01-display-string-literal.scm +5 -0
  11. data/flea-language-spec/test-cases/01-display-and-basic-literals/02-display-integer-literal.scm +5 -0
  12. data/flea-language-spec/test-cases/01-display-and-basic-literals/03-display-boolean-true-literal.scm +5 -0
  13. data/flea-language-spec/test-cases/01-display-and-basic-literals/04-display-boolean-false-literal.scm +5 -0
  14. data/flea-language-spec/test-cases/01-display-and-basic-literals/05-display-list-literal-using-quote.scm +5 -0
  15. data/flea-language-spec/test-cases/01-display-and-basic-literals/06-display-identifier-quoted.scm +5 -0
  16. data/flea-language-spec/test-cases/02-variables/01-define-with-string.scm +6 -0
  17. data/flea-language-spec/test-cases/02-variables/02-define-with-integer.scm +6 -0
  18. data/flea-language-spec/test-cases/02-variables/03-define-with-boolean-true.scm +6 -0
  19. data/flea-language-spec/test-cases/02-variables/04-define-with-boolean-false.scm +6 -0
  20. data/flea-language-spec/test-cases/02-variables/05-define-with-list.scm +6 -0
  21. data/flea-language-spec/test-cases/03-basic-built-in-procedures/01-addition.scm +5 -0
  22. data/flea-language-spec/test-cases/03-basic-built-in-procedures/02-subtraction.scm +5 -0
  23. data/flea-language-spec/test-cases/03-basic-built-in-procedures/03-multiplication.scm +5 -0
  24. data/flea-language-spec/test-cases/03-basic-built-in-procedures/04-division.scm +5 -0
  25. data/flea-language-spec/test-cases/03-basic-built-in-procedures/05-equality-true-integer.scm +5 -0
  26. data/flea-language-spec/test-cases/03-basic-built-in-procedures/06-equality-false-integer.scm +5 -0
  27. data/flea-language-spec/test-cases/03-basic-built-in-procedures/07-equal?/01-equal?-true-integer.scm +5 -0
  28. data/flea-language-spec/test-cases/03-basic-built-in-procedures/07-equal?/02-equal?-false-integer.scm +5 -0
  29. data/flea-language-spec/test-cases/03-basic-built-in-procedures/07-equal?/03-equal?-true-string.scm +5 -0
  30. data/flea-language-spec/test-cases/03-basic-built-in-procedures/07-equal?/04-equal?-false-string.scm +5 -0
  31. data/flea-language-spec/test-cases/03-basic-built-in-procedures/07-equal?/05-equal?-true-boolean.scm +5 -0
  32. data/flea-language-spec/test-cases/03-basic-built-in-procedures/07-equal?/06-equal?-false-boolean.scm +5 -0
  33. data/flea-language-spec/test-cases/03-basic-built-in-procedures/07-equal?/07-equal?-true-list.scm +7 -0
  34. data/flea-language-spec/test-cases/03-basic-built-in-procedures/07-equal?/08-equal?-false-list.scm +7 -0
  35. data/flea-language-spec/test-cases/04-comma-quoting/01-comma-quoting.scm +5 -0
  36. data/flea-language-spec/test-cases/05-lambda/01-lambda.scm +5 -0
  37. data/flea-language-spec/test-cases/05-lambda/02-call-in-place-lambda.scm +5 -0
  38. data/flea-language-spec/test-cases/05-lambda/03-define-with-lambda.scm +6 -0
  39. data/flea-language-spec/test-cases/05-lambda/04-define-and-call-lambda.scm +8 -0
  40. data/flea-language-spec/test-cases/05-lambda/05-lambda-repeated-argument.scm +9 -0
  41. data/flea-language-spec/test-cases/05-lambda/06-lambda-list-argument.scm +8 -0
  42. data/flea-language-spec/test-cases/05-lambda/07-lambda-n-or-more-arguments.scm +12 -0
  43. data/flea-language-spec/test-cases/05-lambda/08-lambda-should-return-last-result.scm +16 -0
  44. data/flea-language-spec/test-cases/06-if/01-if-true-single-arg.scm +6 -0
  45. data/flea-language-spec/test-cases/06-if/02-if-false-single-arg.scm +6 -0
  46. data/flea-language-spec/test-cases/06-if/03-if-true-two-args.scm +7 -0
  47. data/flea-language-spec/test-cases/06-if/04-if-false-two-args.scm +7 -0
  48. data/flea-language-spec/test-cases/07-set/01-should-not-set-undefined-variable.scm +5 -0
  49. data/flea-language-spec/test-cases/07-set/02-set-previously-defined-variable.scm +8 -0
  50. data/flea-language-spec/test-cases/08-derived-expressions/01-begin/01-begin.scm +8 -0
  51. data/flea-language-spec/test-cases/09-list-manipulation/01-car.scm +7 -0
  52. data/flea-language-spec/test-cases/09-list-manipulation/02-cdr.scm +7 -0
  53. data/flea-language-spec/test-cases/09-list-manipulation/03-list-tail.scm +7 -0
  54. data/flea-language-spec/test-cases/09-list-manipulation/04-append.scm +10 -0
  55. data/flea-language-spec/test-cases/09-list-manipulation/05-list.scm +5 -0
  56. data/flea-language-spec/test-cases/10-functional-examples/countdown.scm +15 -0
  57. data/lib/flea.rb +5 -0
  58. data/lib/flea/environment.rb +38 -0
  59. data/lib/flea/interpreter.rb +67 -0
  60. data/lib/flea/standard_library/addition_operator.scm +7 -0
  61. data/lib/flea/standard_library/append.scm +7 -0
  62. data/lib/flea/standard_library/begin.scm +11 -0
  63. data/lib/flea/standard_library/car.scm +7 -0
  64. data/lib/flea/standard_library/cdr.scm +7 -0
  65. data/lib/flea/standard_library/cons.scm +13 -0
  66. data/lib/flea/standard_library/display.scm +8 -0
  67. data/lib/flea/standard_library/division_operator.scm +7 -0
  68. data/lib/flea/standard_library/equality_operator.scm +8 -0
  69. data/lib/flea/standard_library/gets.scm +6 -0
  70. data/lib/flea/standard_library/greater_than.scm +8 -0
  71. data/lib/flea/standard_library/if.scm +10 -0
  72. data/lib/flea/standard_library/lambda.scm +57 -0
  73. data/lib/flea/standard_library/less_than.scm +8 -0
  74. data/lib/flea/standard_library/list.scm +8 -0
  75. data/lib/flea/standard_library/list_predicate.scm +6 -0
  76. data/lib/flea/standard_library/list_tail.scm +5 -0
  77. data/lib/flea/standard_library/multiplication_operator.scm +7 -0
  78. data/lib/flea/standard_library/null.scm +3 -0
  79. data/lib/flea/standard_library/quote.scm +6 -0
  80. data/lib/flea/standard_library/rand.scm +6 -0
  81. data/lib/flea/standard_library/read.scm +6 -0
  82. data/lib/flea/standard_library/set.scm +9 -0
  83. data/lib/flea/standard_library/string_to_num.scm +6 -0
  84. data/lib/flea/standard_library/subtraction_operator.scm +7 -0
  85. data/spec/flea/environment_spec.rb +114 -0
  86. data/spec/flea/interpreter_spec.rb +85 -0
  87. data/spec/flea/standard_library/addition_operator_spec.rb +23 -0
  88. data/spec/flea/standard_library/append_spec.rb +17 -0
  89. data/spec/flea/standard_library/begin_spec.rb +20 -0
  90. data/spec/flea/standard_library/car_spec.rb +17 -0
  91. data/spec/flea/standard_library/cdr_spec.rb +17 -0
  92. data/spec/flea/standard_library/cons_spec.rb +25 -0
  93. data/spec/flea/standard_library/display_spec.rb +36 -0
  94. data/spec/flea/standard_library/division_operator_spec.rb +29 -0
  95. data/spec/flea/standard_library/equality_operator_spec.rb +45 -0
  96. data/spec/flea/standard_library/gets_spec.rb +23 -0
  97. data/spec/flea/standard_library/greater_than_spec.rb +29 -0
  98. data/spec/flea/standard_library/if_spec.rb +59 -0
  99. data/spec/flea/standard_library/lambda_spec.rb +70 -0
  100. data/spec/flea/standard_library/less_than_spec.rb +29 -0
  101. data/spec/flea/standard_library/list_predicate_spec.rb +25 -0
  102. data/spec/flea/standard_library/list_spec.rb +24 -0
  103. data/spec/flea/standard_library/list_tail_spec.rb +17 -0
  104. data/spec/flea/standard_library/mutiplication_operator_spec.rb +23 -0
  105. data/spec/flea/standard_library/null_spec.rb +26 -0
  106. data/spec/flea/standard_library/quote_spec.rb +20 -0
  107. data/spec/flea/standard_library/rand_spec.rb +25 -0
  108. data/spec/flea/standard_library/read_spec.rb +23 -0
  109. data/spec/flea/standard_library/set_spec.rb +23 -0
  110. data/spec/flea/standard_library/string_to_num_spec.rb +28 -0
  111. data/spec/flea/standard_library/subtraction_operator_spec.rb +24 -0
  112. data/spec/spec_helper.rb +10 -0
  113. metadata +231 -0
@@ -0,0 +1,7 @@
1
+ (define +
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ tmp = arguments.map {|item| interpreter.evaluate(item)}
5
+ tmp.inject {|sum, n| sum + n}
6
+ end
7
+ "))
@@ -0,0 +1,7 @@
1
+ (define append
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+
5
+ arguments.map {|x| interpreter.evaluate(x) }.flatten
6
+ end
7
+ "))
@@ -0,0 +1,11 @@
1
+ (define begin
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ val = nil
5
+ arguments.each do |i|
6
+ val = interpreter.evaluate(i)
7
+ end
8
+
9
+ val
10
+ end
11
+ "))
@@ -0,0 +1,7 @@
1
+ (define car
2
+ (native_function "
3
+ Proc.new() do |argument, interpreter|
4
+ list = interpreter.evaluate(argument[0])
5
+ list[0]
6
+ end
7
+ "))
@@ -0,0 +1,7 @@
1
+ (define cdr
2
+ (native_function "
3
+ Proc.new() do |argument, interpreter|
4
+ list = interpreter.evaluate(argument[0])
5
+ list.slice(1, list.length)
6
+ end
7
+ "))
@@ -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,8 @@
1
+ (define display
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ output = interpreter.evaluate(arguments[0])
5
+ print interpreter.parser.to_sexp(output)
6
+ output
7
+ end
8
+ "))
@@ -0,0 +1,7 @@
1
+ (define /
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ tmp = arguments.map {|item| interpreter.evaluate(item)}
5
+ tmp.inject {|sum, n| sum / n}
6
+ end
7
+ "))
@@ -0,0 +1,8 @@
1
+ (define =
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ !(arguments.map{|x| interpreter.evaluate(x) == interpreter.evaluate(arguments[0])}).include?( false )
5
+ end
6
+ "))
7
+
8
+ (define equal? =)
@@ -0,0 +1,6 @@
1
+ (define gets
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ $stdin.gets
5
+ end
6
+ "))
@@ -0,0 +1,8 @@
1
+ (define greater-than?
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ arguments.slice(1, arguments.length).all? do |x|
5
+ interpreter.evaluate(arguments[0]) > interpreter.evaluate(x)
6
+ end
7
+ end
8
+ "))
@@ -0,0 +1,10 @@
1
+ (define if
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ if(interpreter.evaluate(arguments[0]))
5
+ interpreter.evaluate(arguments[1])
6
+ else
7
+ interpreter.evaluate(arguments[2]) unless(arguments[2].nil?)
8
+ end
9
+ end
10
+ "))
@@ -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,8 @@
1
+ (define less-than?
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ arguments.slice(1, arguments.length).all? do |x|
5
+ interpreter.evaluate(arguments[0]) < interpreter.evaluate(x)
6
+ end
7
+ end
8
+ "))
@@ -0,0 +1,8 @@
1
+ (define list
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ arguments.map {|x|
5
+ interpreter.evaluate(x)
6
+ }
7
+ end
8
+ "))
@@ -0,0 +1,6 @@
1
+ (define list?
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ interpreter.evaluate(arguments[0]).is_a? Array
5
+ end
6
+ "))
@@ -0,0 +1,5 @@
1
+ (define list-tail
2
+ (lambda (x k)
3
+ (if (equal? k 0)
4
+ x
5
+ (list-tail (cdr x) (- k 1)))))
@@ -0,0 +1,7 @@
1
+ (define *
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ tmp = arguments.map {|item| interpreter.evaluate(item)}
5
+ tmp.inject {|sum, n| sum * n}
6
+ end
7
+ "))
@@ -0,0 +1,3 @@
1
+ (define null?
2
+ (lambda (arg)
3
+ (equal? arg '())))
@@ -0,0 +1,6 @@
1
+ (define quote
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ arguments[0]
5
+ end
6
+ "))
@@ -0,0 +1,6 @@
1
+ (define rand
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ rand(interpreter.evaluate(arguments[0]))
5
+ end
6
+ "))
@@ -0,0 +1,6 @@
1
+ (define read
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ interpreter.parser.parse_string($stdin.gets)
5
+ end
6
+ "))
@@ -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,6 @@
1
+ (define string-to-num
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ interpreter.evaluate(arguments[0]).to_i
5
+ end
6
+ "))
@@ -0,0 +1,7 @@
1
+ (define -
2
+ (native_function "
3
+ Proc.new() do |arguments, interpreter|
4
+ tmp = arguments.map {|item| interpreter.evaluate(item) }
5
+ tmp.inject {|sum, n| sum - n }
6
+ end
7
+ "))
@@ -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