dslisprb 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
File without changes
data/README.rdoc ADDED
@@ -0,0 +1,63 @@
1
+ dslisprb (dseminara 'lisp)
2
+ http://github.com/tario/dslisprb
3
+ Dario Seminara (http://tario-project.blogspot.com.ar/)
4
+
5
+ == DESCRIPTION:
6
+
7
+ dseminara 'lisp is my own implementation of lisp on ruby, created as an excercise in preparation for finals
8
+
9
+ dslisprb try to be minimalist (only 200 lines of ruby code including parser, default
10
+ functions and macros and about 30 lines for the repl) by using ruby code generation where possible
11
+
12
+ For example, lisp lambdas are represented as ruby lambdas defined with the code translated from lisp to ruby
13
+ and lisp variables (including those where default functions/lambda are stored) are translated as ruby variables
14
+ allowing reuse of the ruby stack instead of reimplement it
15
+
16
+ == FEATURES:
17
+
18
+ * dslisprb executable with readline
19
+ * Basic common lisp functions
20
+
21
+ == TODO:
22
+
23
+ * MAPCAR
24
+ * Defined functions and variable persistent between multiple evaluate calls
25
+ * caaaar, cdadar, cddra, etc...
26
+
27
+ == SYNOPSIS:
28
+
29
+ See 'dslisprb --help' for usage information.
30
+
31
+ == REQUIREMENTS:
32
+
33
+ * Ruby
34
+
35
+ == INSTALL:
36
+
37
+ * gem install dslisprb
38
+
39
+ == LICENSE:
40
+
41
+ (The MIT License)
42
+
43
+ Copyright (c) 2012 Dario Seminara
44
+
45
+ Permission is hereby granted, free of charge, to any person obtaining
46
+ a copy of this software and associated documentation files (the
47
+ 'Software'), to deal in the Software without restriction, including
48
+ without limitation the rights to use, copy, modify, merge, publish,
49
+ distribute, sublicense, and/or sell copies of the Software, and to
50
+ permit persons to whom the Software is furnished to do so, subject to
51
+ the following conditions:
52
+
53
+ The above copyright notice and this permission notice shall be
54
+ included in all copies or substantial portions of the Software.
55
+
56
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
57
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
58
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
59
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
60
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
61
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
62
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
63
+
data/Rakefile ADDED
@@ -0,0 +1,44 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+ require 'rdoc/task'
5
+ require 'rake/gempackagetask'
6
+ require "rspec/core/rake_task"
7
+
8
+ spec = Gem::Specification.new do |s|
9
+ s.name = 'dslisprb'
10
+ s.version = '0.0.1'
11
+ s.author = 'Dario Seminara'
12
+ s.email = 'robertodarioseminara@gmail.com'
13
+ s.platform = Gem::Platform::RUBY
14
+ s.summary = 'Lisp interpreter written in ruby, using code generation'
15
+ s.homepage = "http://github.com/tario/dslisprb"
16
+ s.has_rdoc = true
17
+ s.executables = ["dslisprb"]
18
+ s.files = Dir.glob("{lib,spec}/**/*") + [ 'README.rdoc', 'Rakefile', 'TODO', 'CHANGELOG' ]
19
+ end
20
+
21
+ desc 'Run tests'
22
+
23
+ RSpec::Core::RakeTask.new("test:units") do |t|
24
+ t.pattern= 'spec/**/*.rb'
25
+ end
26
+
27
+ desc 'Generate RDoc'
28
+ Rake::RDocTask.new :rdoc do |rd|
29
+ rd.rdoc_dir = 'doc'
30
+ rd.rdoc_files.add 'lib', 'README.rdoc'
31
+ rd.main = 'README.rdoc'
32
+ end
33
+
34
+ desc 'Build Gem'
35
+ Rake::GemPackageTask.new spec do |pkg|
36
+ pkg.need_tar = true
37
+ end
38
+
39
+ desc 'Clean up'
40
+ task :clean => [ :clobber_rdoc, :clobber_package ]
41
+
42
+ desc 'Clean up'
43
+ task :clobber => [ :clean ]
44
+
data/TODO ADDED
@@ -0,0 +1,3 @@
1
+ * MAPCAR
2
+ * Defined functions and variable persistent between multiple evaluate calls
3
+ * caaaar, cdadar, cddra, etc...
data/bin/dslisprb ADDED
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env ruby
2
+ require "dslisprb"
3
+
4
+ dslisp = DsLisp.new
5
+ if ARGV[0]
6
+ if ARGV[0] == '--help'
7
+ print "
8
+ dslisprb --help
9
+ show this message
10
+ dslisprb <lisp expression>
11
+ evaluate lisp expression, shows the result on standard output and exit
12
+
13
+ Example:
14
+ dslisprb \"(car '(1 2 3))\"
15
+ (2 3)
16
+
17
+ dslisprb without arguments starts the repl
18
+ "
19
+
20
+ else
21
+ # execute and exit
22
+ print dslisp.evaluate(ARGV[0]).lisp_inspect,"\n"
23
+ end
24
+ else
25
+ # repl
26
+ require 'readline'
27
+ print "enter lisp commands or quit to exit\n"
28
+
29
+ stty_save = `stty -g`.chomp
30
+ trap('INT') { system('stty', stty_save); exit }
31
+
32
+ while cmd = Readline.readline('> ', true)
33
+ if cmd == "quit"
34
+ exit
35
+ end
36
+
37
+ begin
38
+ print dslisp.evaluate(cmd).lisp_inspect,"\n"
39
+ rescue Exception => e
40
+ p e
41
+ print e.backtrace.join("\n"),"\n"
42
+ end
43
+ end
44
+ end
data/lib/dslisprb.rb ADDED
@@ -0,0 +1,201 @@
1
+ class DsLisp
2
+ class ToRuby
3
+ class << self
4
+ def name_convert(name)
5
+ {:< => "lt", :> => "ht", :+ => "plus", :* => "mult" , :/ => "divide", :- => "minus" }[name] || "_" + name.to_s
6
+ end
7
+
8
+ def to_ruby(code)
9
+ if Array === code
10
+ # lisp call
11
+
12
+ function_name = code.first
13
+
14
+ if (CommonLispOperators.methods - Class.methods).include?(function_name)
15
+ CommonLispOperators.send(function_name, code)
16
+ else
17
+ strargs = code[1..-1].map{|x|
18
+ if Symbol === x
19
+ "(local_variables.include?(:#{x}) ? #{x} : (#{to_ruby(x)}))"
20
+ else
21
+ "(#{to_ruby(x)})"
22
+ end
23
+ }.join(",")
24
+
25
+ if Symbol === function_name
26
+ "#{name_convert(function_name)}.call(#{strargs})"
27
+ else
28
+ "#{to_ruby(code.first)}.call(#{strargs})"
29
+ end
30
+ end
31
+ else
32
+ code.inspect
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ module CommonLispOperators
39
+ class << self
40
+ def quote(code)
41
+ code[1].inspect
42
+ end
43
+
44
+ def lambda(code)
45
+ arguments = code[1].map(&:to_s).join(",")
46
+ "lambda{|#{arguments}| #{ToRuby.to_ruby(code[2])}}.lisp_inner_code(#{code.lisp_inspect.inspect})"
47
+ end
48
+
49
+ def block(code)
50
+ code[1..-1].map(&ToRuby.method(:to_ruby)).join(";")
51
+ end
52
+
53
+ def set(code)
54
+ "#{ToRuby.name_convert(code[1])} = #{ToRuby.to_ruby(code[2])}"
55
+ end
56
+
57
+ def defun(code)
58
+ newcode = [:set, code[1], [:lambda, code[2], code[3]]]
59
+ ToRuby.to_ruby(newcode)
60
+ end
61
+
62
+ def defmacro(code)
63
+ name = code[1]
64
+ arguments = code[2]
65
+ impl = code[3][1]
66
+
67
+ subsl = Kernel.lambda{|_code, rplmap|
68
+ _code.map{|subcode|
69
+ if Array === subcode
70
+ subsl.call(subcode, rplmap)
71
+ else
72
+ rplmap[subcode] || subcode
73
+ end
74
+ }
75
+ }
76
+
77
+ CommonLispOperators.define_singleton_method(name) do |_code|
78
+ rplmap = {}
79
+ (0..arguments.size-1).each do |i|
80
+ rplmap[arguments[i]] = _code[i+1]
81
+ end
82
+
83
+ ToRuby.to_ruby subsl.call(impl, rplmap)
84
+ end
85
+
86
+ ""
87
+ end
88
+ end
89
+ end
90
+
91
+ def parse(str)
92
+ if str.count("(") != str.count(")")
93
+ raise "Parentheshis count does not match, try adding parenthesis at the end of string :P"
94
+ end
95
+
96
+ if matchdata = /^\s*\'(.*)\s*$/.match(str)
97
+ [:quote, parse(matchdata[1].sub("'",""))]
98
+ elsif matchdata = /^\s*\((.*)\)\s*$/.match(str)
99
+ inside_code = matchdata[1]
100
+ if inside_code =~ /^\s*$/
101
+ []
102
+ else
103
+ # split tokens preserving parenthesis count
104
+ tokens = []
105
+ newtoken = ""
106
+ inside_code.split.each do |str|
107
+ newtoken << " "
108
+ newtoken << str
109
+ if newtoken.count("(") == newtoken.count(")")
110
+ tokens << newtoken
111
+ newtoken = ""
112
+ end
113
+ end
114
+
115
+ tokens.map(&method(:parse))
116
+ end
117
+ else
118
+ if str =~ /^\s*\d+\s*$/
119
+ str.to_i
120
+ else
121
+ str.strip.to_sym
122
+ end
123
+ end
124
+ end
125
+
126
+ def evaluate(code)
127
+ if String === code
128
+ return evaluate(parse code)
129
+ end
130
+ # arithmethic
131
+ plus = lambda{|x,y| x+y}
132
+ mult = lambda{|x,y| x*y}
133
+ divide = lambda{|x,y| x/y}
134
+ minus = lambda{|x,y| x-y}
135
+
136
+ lt = lambda{|x,y| x<y || nil}
137
+ ht = lambda{|x,y| x>y || nil}
138
+ _eq = lambda{|x,y| x==y || nil}
139
+
140
+
141
+ # list selectors
142
+ _car = lambda{|x| x.first}
143
+ _cdr = lambda{|x| x[1..-1]}
144
+ _nth = lambda{|index,list| list[index-1]}
145
+
146
+ # list constructors
147
+ _cons = lambda{|element, list| [element]+list}
148
+ _append = plus
149
+ _list = lambda{|*args| args}
150
+
151
+ # recognizers
152
+ _null = lambda{|element| (element == nil or element == []) || nil}
153
+ _atom = lambda{|element| (not Array === element) || nil}
154
+ _numberp = lambda{|element| Numeric === element || nil}
155
+ _symbolp = lambda{|element| Symbol === element || nil}
156
+ _listp = lambda{|element| Array === element || nil}
157
+ _length = lambda{|list| list.size}
158
+
159
+ # nil
160
+ _nil = lambda{nil}
161
+
162
+ # boolean
163
+ _not = lambda{|a| (not a) || nil}
164
+ _and = lambda{|a,b| (a and b) || nil}
165
+ _or = lambda{|a,b| (a or b) || nil}
166
+
167
+ # generate ruby code for lisp ast
168
+ ruby_code = ToRuby.to_ruby(code)
169
+ eval(ruby_code)
170
+ end
171
+ end
172
+
173
+ class Object
174
+ def lisp_inspect
175
+ inspect
176
+ end
177
+ end
178
+
179
+ class Symbol
180
+ def lisp_inspect
181
+ to_s
182
+ end
183
+ end
184
+
185
+ class Array
186
+ def lisp_inspect
187
+ "(" + map(&:lisp_inspect).join(" ") + ")"
188
+ end
189
+ end
190
+
191
+ class Proc
192
+ def lisp_inspect
193
+ @inner_code
194
+ end
195
+
196
+ def lisp_inner_code(code)
197
+ @inner_code = code
198
+ self
199
+ end
200
+ end
201
+
data/lib/dslisprb.rb~ ADDED
@@ -0,0 +1,201 @@
1
+ class DsLisp
2
+ class ToRuby
3
+ class << self
4
+ def name_convert(name)
5
+ {:< => "lt", :> => "ht", :+ => "plus", :* => "mult" , :/ => "divide", :- => "minus" }[name] || "_" + name.to_s
6
+ end
7
+
8
+ def to_ruby(code)
9
+ if Array === code
10
+ # lisp call
11
+
12
+ function_name = code.first
13
+
14
+ if (CommonLispOperators.methods - Class.methods).include?(function_name)
15
+ CommonLispOperators.send(function_name, code)
16
+ else
17
+ strargs = code[1..-1].map{|x|
18
+ if Symbol === x
19
+ "(local_variables.include?(:#{x}) ? #{x} : (#{to_ruby(x)}))"
20
+ else
21
+ "(#{to_ruby(x)})"
22
+ end
23
+ }.join(",")
24
+
25
+ if Symbol === function_name
26
+ "#{name_convert(function_name)}.call(#{strargs})"
27
+ else
28
+ "#{to_ruby(code.first)}.call(#{strargs})"
29
+ end
30
+ end
31
+ else
32
+ code.inspect
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ module CommonLispOperators
39
+ class << self
40
+ def quote(code)
41
+ code[1].inspect
42
+ end
43
+
44
+ def lambda(code)
45
+ arguments = code[1].map(&:to_s).join(",")
46
+ "lambda{|#{arguments}| #{ToRuby.to_ruby(code[2])}}.lisp_inner_code(#{code.lisp_inspect.inspect})"
47
+ end
48
+
49
+ def block(code)
50
+ code[1..-1].map(&ToRuby.method(:to_ruby)).join(";")
51
+ end
52
+
53
+ def set(code)
54
+ "#{ToRuby.name_convert(code[1])} = #{ToRuby.to_ruby(code[2])}"
55
+ end
56
+
57
+ def defun(code)
58
+ newcode = [:set, code[1], [:lambda, code[2], code[3]]]
59
+ ToRuby.to_ruby(newcode)
60
+ end
61
+
62
+ def defmacro(code)
63
+ name = code[1]
64
+ arguments = code[2]
65
+ impl = code[3][1]
66
+
67
+ subsl = Kernel.lambda{|_code, rplmap|
68
+ _code.map{|subcode|
69
+ if Array === subcode
70
+ subsl.call(subcode, rplmap)
71
+ else
72
+ rplmap[subcode] || subcode
73
+ end
74
+ }
75
+ }
76
+
77
+ CommonLispOperators.define_singleton_method(name) do |_code|
78
+ rplmap = {}
79
+ (0..arguments.size-1).each do |i|
80
+ rplmap[arguments[i]] = _code[i+1]
81
+ end
82
+
83
+ ToRuby.to_ruby subsl.call(impl, rplmap)
84
+ end
85
+
86
+ ""
87
+ end
88
+ end
89
+ end
90
+
91
+ def parse(str)
92
+ if str.count("(") != str.count(")")
93
+ raise "Parentheshis count does not match, try adding parenthesis to the end :P"
94
+ end
95
+
96
+ if matchdata = /^\s*\'(.*)\s*$/.match(str)
97
+ [:quote, parse(matchdata[1].sub("'",""))]
98
+ elsif matchdata = /^\s*\((.*)\)\s*$/.match(str)
99
+ inside_code = matchdata[1]
100
+ if inside_code =~ /^\s*$/
101
+ []
102
+ else
103
+ # split tokens preserving parenthesis count
104
+ tokens = []
105
+ newtoken = ""
106
+ inside_code.split.each do |str|
107
+ newtoken << " "
108
+ newtoken << str
109
+ if newtoken.count("(") == newtoken.count(")")
110
+ tokens << newtoken
111
+ newtoken = ""
112
+ end
113
+ end
114
+
115
+ tokens.map(&method(:parse))
116
+ end
117
+ else
118
+ if str =~ /^\s*\d+\s*$/
119
+ str.to_i
120
+ else
121
+ str.strip.to_sym
122
+ end
123
+ end
124
+ end
125
+
126
+ def evaluate(code)
127
+ if String === code
128
+ return evaluate(parse code)
129
+ end
130
+ # arithmethic
131
+ plus = lambda{|x,y| x+y}
132
+ mult = lambda{|x,y| x*y}
133
+ divide = lambda{|x,y| x/y}
134
+ minus = lambda{|x,y| x-y}
135
+
136
+ lt = lambda{|x,y| x<y || nil}
137
+ ht = lambda{|x,y| x>y || nil}
138
+ _eq = lambda{|x,y| x==y || nil}
139
+
140
+
141
+ # list selectors
142
+ _car = lambda{|x| x.first}
143
+ _cdr = lambda{|x| x[1..-1]}
144
+ _nth = lambda{|index,list| list[index-1]}
145
+
146
+ # list constructors
147
+ _cons = lambda{|element, list| [element]+list}
148
+ _append = plus
149
+ _list = lambda{|*args| args}
150
+
151
+ # recognizers
152
+ _null = lambda{|element| (element == nil or element == []) || nil}
153
+ _atom = lambda{|element| (not Array === element) || nil}
154
+ _numberp = lambda{|element| Numeric === element || nil}
155
+ _symbolp = lambda{|element| Symbol === element || nil}
156
+ _listp = lambda{|element| Array === element || nil}
157
+ _length = lambda{|list| list.size}
158
+
159
+ # nil
160
+ _nil = lambda{nil}
161
+
162
+ # boolean
163
+ _not = lambda{|a| (not a) || nil}
164
+ _and = lambda{|a,b| (a and b) || nil}
165
+ _or = lambda{|a,b| (a or b) || nil}
166
+
167
+ # generate ruby code for lisp ast
168
+ ruby_code = ToRuby.to_ruby(code)
169
+ eval(ruby_code)
170
+ end
171
+ end
172
+
173
+ class Object
174
+ def lisp_inspect
175
+ inspect
176
+ end
177
+ end
178
+
179
+ class Symbol
180
+ def lisp_inspect
181
+ to_s
182
+ end
183
+ end
184
+
185
+ class Array
186
+ def lisp_inspect
187
+ "(" + map(&:lisp_inspect).join(" ") + ")"
188
+ end
189
+ end
190
+
191
+ class Proc
192
+ def lisp_inspect
193
+ @inner_code
194
+ end
195
+
196
+ def lisp_inner_code(code)
197
+ @inner_code = code
198
+ self
199
+ end
200
+ end
201
+
data/spec/ast.rb ADDED
@@ -0,0 +1,55 @@
1
+ require "dslisprb"
2
+
3
+ describe DsLisp, "ds lisp" do
4
+
5
+ (1..10).each do |i|
6
+ it "should parse simple numeric atom #{i}" do
7
+ DsLisp.new.parse(i.to_s).should be == i
8
+ end
9
+ end
10
+
11
+ (1..10).each do |i|
12
+ it "should parse simple numeric atom #{i} with spaces around" do
13
+ DsLisp.new.parse(" " + i.to_s + " ").should be == i
14
+ end
15
+ end
16
+
17
+ [:x,:y,:z].each do |symbol|
18
+ it "should parse simple symbolic atom #{symbol}" do
19
+ DsLisp.new.parse(symbol.to_s).should be == symbol
20
+ end
21
+ end
22
+
23
+ it "should parse empty list" do
24
+ DsLisp.new.parse('()').should be == []
25
+ end
26
+
27
+ it "should parse list with atom inside" do
28
+ DsLisp.new.parse('(1)').should be == [1]
29
+ end
30
+
31
+ it "should parse list with two atoms inside" do
32
+ DsLisp.new.parse('(1 2)').should be == [1, 2]
33
+ end
34
+
35
+ it "should parse list with a list with two atoms inside" do
36
+ DsLisp.new.parse('((1 2))').should be == [[1, 2]]
37
+ end
38
+
39
+ it "should parse list with two list with two atoms inside" do
40
+ DsLisp.new.parse('((1 2) (3 4))').should be == [[1, 2],[3, 4]]
41
+ end
42
+
43
+ it "should parse list with two list with two symbolic atoms inside" do
44
+ DsLisp.new.parse('((a b) (c d))').should be == [[:a, :b],[:c, :d]]
45
+ end
46
+
47
+ it "should parse quote with atoms" do
48
+ DsLisp.new.parse("'car").should be == [:quote, :car]
49
+ end
50
+
51
+ it "should parse quote with lists" do
52
+ DsLisp.new.parse("'(1 2 3)").should be == [:quote, [1,2,3]]
53
+ end
54
+ end
55
+
data/spec/ast.rb~ ADDED
@@ -0,0 +1,55 @@
1
+ require "dslisprb"
2
+
3
+ describe DsLisp, "ds lisp" do
4
+
5
+ (1..10).each do |i|
6
+ it "should parse simple numeric atom #{i}" do
7
+ DsLisp.new.parse(i.to_s).should be == i
8
+ end
9
+ end
10
+
11
+ (1..10).each do |i|
12
+ it "should parse simple numeric atom #{i} with spaces around" do
13
+ DsLisp.new.parse(" " + i.to_s + " ").should be == i
14
+ end
15
+ end
16
+
17
+ [:x,:y,:z].each do |symbol|
18
+ it "should parse simple symbolic atom #{symbol}" do
19
+ DsLisp.new.parse(symbol.to_s).should be == symbol
20
+ end
21
+ end
22
+
23
+ it "should parse empty list" do
24
+ DsLisp.new.parse('()').should be == []
25
+ end
26
+
27
+ it "should parse list with atom inside" do
28
+ DsLisp.new.parse('(1)').should be == [1]
29
+ end
30
+
31
+ it "should parse list with two atoms inside" do
32
+ DsLisp.new.parse('(1 2)').should be == [1, 2]
33
+ end
34
+
35
+ it "should parse list with a list with two atoms inside" do
36
+ DsLisp.new.parse('((1 2))').should be == [[1, 2]]
37
+ end
38
+
39
+ it "should parse list with two list with two atoms inside" do
40
+ DsLisp.new.parse('((1 2) (3 4))').should be == [[1, 2],[3, 4]]
41
+ end
42
+
43
+ it "should parse list with two list with two symbolic atoms inside" do
44
+ DsLisp.new.parse('((a b) (c d))').should be == [[:a, :b],[:c, :d]]
45
+ end
46
+
47
+ it "should parse quote with atoms" do
48
+ DsLisp.new.parse("'car").should be == [:quote, :car]
49
+ end
50
+
51
+ it "should parse quote with lists" do
52
+ DsLisp.new.parse("'(1,2,3)").should be == [:quote, [1,2,3]]
53
+ end
54
+ end
55
+
data/spec/defun.rb ADDED
@@ -0,0 +1,27 @@
1
+ require "dslisprb"
2
+
3
+ describe DsLisp, "ds lisp defun" do
4
+ it "should allow lambda" do
5
+ DsLisp.new.evaluate([:lambda, [:x], [:+, :x, 1]]).call(1).should be == 2
6
+ end
7
+
8
+ it "should allow lambda assign and call" do
9
+ DsLisp.new.evaluate([:block,
10
+ [:set, :lambda_test, [:lambda, [:x], [:+, :x, 1]]],
11
+ [:lambda_test, 1]
12
+ ]).should be == 2
13
+ end
14
+
15
+ it "should allow lambda returning 3" do
16
+ DsLisp.new.evaluate([:lambda, [:x], [:+, :x, 2]]).call(1).should be == 3
17
+ end
18
+
19
+ it "should allow defun as brief for lambda assign" do
20
+ DsLisp.new.evaluate([:block,
21
+ [:defun, :lambda_test, [:x], [:+, :x, 1]],
22
+ [:lambda_test, 1]
23
+ ]).should be == 2
24
+ end
25
+ end
26
+
27
+
data/spec/defun.rb~ ADDED
@@ -0,0 +1,27 @@
1
+ require "dslisprb"
2
+
3
+ describe DsLisp, "ds lisp defun" do
4
+ it "should allow lambda" do
5
+ DsLisp.new.evaluate([:lambda, [:x], [:+, :x, 1]]).call(1).should be == 2
6
+ end
7
+
8
+ it "should allow lambda assign and call" do
9
+ DsLisp.new.evaluate([:block,
10
+ [:set, :lambda_test, [:lambda, [:x], [:+, :x, 1]]],
11
+ [:lambda_test, 1]
12
+ ]).should be == 2
13
+ end
14
+
15
+ it "should allow lambda returning 3" do
16
+ DsLisp.new.evaluate([:lambda, [:x], [:+, :x, 2]]).call(1).should be == 3
17
+ end
18
+
19
+ it "should allow defun as brief for lambda assign" do
20
+ DsLisp.new.evaluate([:block,
21
+ [:defun, :lambda_test, [:x], [:+, :x, 1]],
22
+ [:lambda_test, 1]
23
+ ]).should be == 2
24
+ end
25
+ end
26
+
27
+
data/spec/execution.rb ADDED
@@ -0,0 +1,25 @@
1
+ require "dslisprb"
2
+
3
+ describe DsLisp, "ds lisp execution" do
4
+
5
+ (1..10).each do |i|
6
+ it "should evaluate simple numeric atom #{i}" do
7
+ DsLisp.new.evaluate(i).should be == i
8
+ end
9
+ end
10
+
11
+ [:x,:y,:z].each do |symbol|
12
+ it "should parse simple symbolic atom #{symbol}" do
13
+ DsLisp.new.evaluate(symbol).should be == symbol
14
+ end
15
+ end
16
+
17
+ it "should evaluate call to arithmetic method +" do
18
+ DsLisp.new.evaluate([:+, 1, 2]).should be == 3
19
+ end
20
+
21
+ it "should evaluate quote call to define literals" do
22
+ DsLisp.new.evaluate([:quote, [1, 2, 3]]).should be == [1, 2, 3]
23
+ end
24
+ end
25
+
@@ -0,0 +1,25 @@
1
+ require "dslisprb"
2
+
3
+ describe DsLisp, "ds lisp execution" do
4
+
5
+ (1..10).each do |i|
6
+ it "should evaluate simple numeric atom #{i}" do
7
+ DsLisp.new.evaluate(i).should be == i
8
+ end
9
+ end
10
+
11
+ [:x,:y,:z].each do |symbol|
12
+ it "should parse simple symbolic atom #{symbol}" do
13
+ DsLisp.new.evaluate(symbol).should be == symbol
14
+ end
15
+ end
16
+
17
+ it "should evaluate call to arithmetic method +" do
18
+ DsLisp.new.evaluate([:+, 1, 2]).should be == 3
19
+ end
20
+
21
+ it "should evaluate quote call to define literals" do
22
+ DsLisp.new.evaluate([:quote, [1, 2, 3]).should be == [1, 2, 3]
23
+ end
24
+ end
25
+
data/spec/functions.rb ADDED
@@ -0,0 +1,132 @@
1
+ require "dslisprb"
2
+
3
+ describe DsLisp, "ds lisp functions" do
4
+
5
+ # selectors
6
+ it "should return 1 for (car (quote (1 2)))" do
7
+ DsLisp.new.evaluate([:car, [:quote, [1, 2]]]).should be == 1
8
+ end
9
+
10
+ it "should return (2 3) for (cdr (quote (1 2 3)))" do
11
+ DsLisp.new.evaluate([:cdr, [:quote, [1, 2, 3]]]).should be == [2, 3]
12
+ end
13
+
14
+ it "should return 5 for (nth 2 (quote (4 5 6)))" do
15
+ DsLisp.new.evaluate([:nth, 2, [:quote, [4, 5, 6]]]).should be == 5
16
+ end
17
+
18
+ # constructors
19
+ it "should return (1 2 3 4) for (cons 1 (quote (2 3 4)))" do
20
+ DsLisp.new.evaluate([:cons, 1, [:quote, [2, 3, 4]]]).should be == [1,2,3,4]
21
+ end
22
+
23
+ it "should return (1 2 3 4 5) for (append (quote (1 2)) (quote (3 4 5)))" do
24
+ DsLisp.new.evaluate([:append, [:quote, [1,2]], [:quote, [3, 4, 5]]]).should be == [1,2,3,4,5]
25
+ end
26
+
27
+ it "should return (1 2) for (list 1 2)" do
28
+ DsLisp.new.evaluate([:list, 1, 2]).should be == [1,2]
29
+ end
30
+
31
+ it "should return (1 2 3) for (list 1 2 3)" do
32
+ DsLisp.new.evaluate([:list, 1, 2, 3]).should be == [1,2,3]
33
+ end
34
+
35
+ # type recognizers
36
+ [:a, 1, [1,2,3,4], [], nil].each do |obj|
37
+ result = (obj == nil or obj == []) ? true : nil
38
+ it "should return #{result} for (null #{obj})" do
39
+ DsLisp.new.evaluate([:null, [:quote, obj]]).should be == result
40
+ end
41
+ end
42
+
43
+ [:a, 1, [1,2,3,4]].each do |obj|
44
+ result = (not Array === obj) ? true : nil
45
+ it "should return #{result} for (atom #{obj})" do
46
+ DsLisp.new.evaluate([:atom, [:quote, obj]]).should be == result
47
+ end
48
+
49
+ numberp_result = (Numeric === obj) ? true : nil
50
+ it "should return #{result} for (numberp #{obj})" do
51
+ DsLisp.new.evaluate([:numberp, [:quote, obj]]).should be == numberp_result
52
+ end
53
+
54
+ symbolp_result = (Symbol === obj) ? true : nil
55
+ it "should return #{result} for (symbolp #{obj})" do
56
+ DsLisp.new.evaluate([:symbolp, [:quote, obj]]).should be == symbolp_result
57
+ end
58
+
59
+ listp_result = (Array === obj) ? true : nil
60
+ it "should return #{result} for (listp #{obj})" do
61
+ DsLisp.new.evaluate([:listp, [:quote, obj]]).should be == listp_result
62
+ end
63
+ end
64
+
65
+ [[1], [1,2], [1,2,3], [1,2,3,4]].each do |obj|
66
+ result = obj.size
67
+ it "should return #{result} for (length #{obj})" do
68
+ DsLisp.new.evaluate([:length, [:quote, obj]]).should be == result
69
+ end
70
+ end
71
+
72
+ it "should return nil on nil function" do
73
+ DsLisp.new.evaluate([:nil]).should be == nil
74
+ end
75
+
76
+ # arithmetic
77
+ it "should call +" do
78
+ DsLisp.new.evaluate([:+,1,2]).should be == 3
79
+ end
80
+
81
+ it "should call -" do
82
+ DsLisp.new.evaluate([:-,7,4]).should be == 3
83
+ end
84
+
85
+ it "should call *" do
86
+ DsLisp.new.evaluate([:*,5,6]).should be == 30
87
+ end
88
+
89
+ it "should call /" do
90
+ DsLisp.new.evaluate([:/,10,2]).should be == 5
91
+ end
92
+
93
+ # relational
94
+ [[3,2],[3,3],[3,4]].each do |pair|
95
+ result = pair.first < pair.last || nil
96
+ it "should call <" do
97
+ DsLisp.new.evaluate([:<,pair.first,pair.last]).should be == result
98
+ end
99
+
100
+ resultht = pair.first > pair.last || nil
101
+ it "should call >" do
102
+ DsLisp.new.evaluate([:>,pair.first,pair.last]).should be == resultht
103
+ end
104
+
105
+ resulteq = pair.first == pair.last || nil
106
+ it "should call eq" do
107
+ DsLisp.new.evaluate([:eq,pair.first,pair.last]).should be == resulteq
108
+ end
109
+ end
110
+
111
+ # boolean
112
+ [[true,true],[true,nil],[nil,true],[nil,nil]].each do |pair|
113
+ resultor = (pair.first or pair.last) || nil
114
+ resultand = (pair.first and pair.last) || nil
115
+ resultnot = (not pair.first) || nil
116
+
117
+ it "should call or with #{pair} and return #{resultor}" do
118
+ DsLisp.new.evaluate([:or,pair.first,pair.last]).should be == resultor
119
+ end
120
+
121
+ it "should call and with #{pair} and return #{resultand}" do
122
+ DsLisp.new.evaluate([:and,pair.first,pair.last]).should be == resultand
123
+ end
124
+
125
+ it "should call not" do
126
+ DsLisp.new.evaluate([:not,pair.first]).should be == resultnot
127
+ end
128
+ end
129
+
130
+ end
131
+
132
+
@@ -0,0 +1,132 @@
1
+ require "dslisprb"
2
+
3
+ describe DsLisp::CommonLispFunctions, "ds lisp functions" do
4
+
5
+ # selectors
6
+ it "should return 1 for (car (quote (1 2)))" do
7
+ DsLisp.new.evaluate([:car, [:quote, [1, 2]]]).should be == 1
8
+ end
9
+
10
+ it "should return (2 3) for (cdr (quote (1 2 3)))" do
11
+ DsLisp.new.evaluate([:cdr, [:quote, [1, 2, 3]]]).should be == [2, 3]
12
+ end
13
+
14
+ it "should return 5 for (nth 2 (quote (4 5 6)))" do
15
+ DsLisp.new.evaluate([:nth, 2, [:quote, [4, 5, 6]]]).should be == 5
16
+ end
17
+
18
+ # constructors
19
+ it "should return (1 2 3 4) for (cons 1 (quote (2 3 4)))" do
20
+ DsLisp.new.evaluate([:cons, 1, [:quote, [2, 3, 4]]]).should be == [1,2,3,4]
21
+ end
22
+
23
+ it "should return (1 2 3 4 5) for (append (quote (1 2)) (quote (3 4 5)))" do
24
+ DsLisp.new.evaluate([:append, [:quote, [1,2]], [:quote, [3, 4, 5]]]).should be == [1,2,3,4,5]
25
+ end
26
+
27
+ it "should return (1 2) for (list 1 2)" do
28
+ DsLisp.new.evaluate([:list, 1, 2]).should be == [1,2]
29
+ end
30
+
31
+ it "should return (1 2 3) for (list 1 2 3)" do
32
+ DsLisp.new.evaluate([:list, 1, 2, 3]).should be == [1,2,3]
33
+ end
34
+
35
+ # type recognizers
36
+ [:a, 1, [1,2,3,4], [], nil].each do |obj|
37
+ result = (obj == nil or obj == []) ? true : nil
38
+ it "should return #{result} for (null #{obj})" do
39
+ DsLisp.new.evaluate([:null, [:quote, obj]]).should be == result
40
+ end
41
+ end
42
+
43
+ [:a, 1, [1,2,3,4]].each do |obj|
44
+ result = (not Array === obj) ? true : nil
45
+ it "should return #{result} for (atom #{obj})" do
46
+ DsLisp.new.evaluate([:atom, [:quote, obj]]).should be == result
47
+ end
48
+
49
+ numberp_result = (Numeric === obj) ? true : nil
50
+ it "should return #{result} for (numberp #{obj})" do
51
+ DsLisp.new.evaluate([:numberp, [:quote, obj]]).should be == numberp_result
52
+ end
53
+
54
+ symbolp_result = (Symbol === obj) ? true : nil
55
+ it "should return #{result} for (symbolp #{obj})" do
56
+ DsLisp.new.evaluate([:symbolp, [:quote, obj]]).should be == symbolp_result
57
+ end
58
+
59
+ listp_result = (Array === obj) ? true : nil
60
+ it "should return #{result} for (listp #{obj})" do
61
+ DsLisp.new.evaluate([:listp, [:quote, obj]]).should be == listp_result
62
+ end
63
+ end
64
+
65
+ [[1], [1,2], [1,2,3], [1,2,3,4]].each do |obj|
66
+ result = obj.size
67
+ it "should return #{result} for (length #{obj})" do
68
+ DsLisp.new.evaluate([:length, [:quote, obj]]).should be == result
69
+ end
70
+ end
71
+
72
+ it "should return nil on nil function" do
73
+ DsLisp.new.evaluate([:nil]).should be == nil
74
+ end
75
+
76
+ # arithmetic
77
+ it "should call +" do
78
+ DsLisp.new.evaluate([:+,1,2]).should be == 3
79
+ end
80
+
81
+ it "should call -" do
82
+ DsLisp.new.evaluate([:-,7,4]).should be == 3
83
+ end
84
+
85
+ it "should call *" do
86
+ DsLisp.new.evaluate([:*,5,6]).should be == 30
87
+ end
88
+
89
+ it "should call /" do
90
+ DsLisp.new.evaluate([:/,10,2]).should be == 5
91
+ end
92
+
93
+ # relational
94
+ [[3,2],[3,3],[3,4]].each do |pair|
95
+ result = pair.first < pair.last || nil
96
+ it "should call <" do
97
+ DsLisp.new.evaluate([:<,pair.first,pair.last]).should be == result
98
+ end
99
+
100
+ resultht = pair.first > pair.last || nil
101
+ it "should call >" do
102
+ DsLisp.new.evaluate([:>,pair.first,pair.last]).should be == resultht
103
+ end
104
+
105
+ resulteq = pair.first == pair.last || nil
106
+ it "should call eq" do
107
+ DsLisp.new.evaluate([:eq,pair.first,pair.last]).should be == resulteq
108
+ end
109
+ end
110
+
111
+ # boolean
112
+ [[true,true],[true,nil],[nil,true],[nil,nil]].each do |pair|
113
+ resultor = (pair.first or pair.last) || nil
114
+ resultand = (pair.first and pair.last) || nil
115
+ resultnot = (not pair.first) || nil
116
+
117
+ it "should call or with #{pair} and return #{resultor}" do
118
+ DsLisp.new.evaluate([:or,pair.first,pair.last]).should be == resultor
119
+ end
120
+
121
+ it "should call and with #{pair} and return #{resultand}" do
122
+ DsLisp.new.evaluate([:and,pair.first,pair.last]).should be == resultand
123
+ end
124
+
125
+ it "should call not" do
126
+ DsLisp.new.evaluate([:not,pair.first]).should be == resultnot
127
+ end
128
+ end
129
+
130
+ end
131
+
132
+
data/spec/macro.rb ADDED
@@ -0,0 +1,11 @@
1
+ require "dslisprb"
2
+
3
+ describe DsLisp, "ds lisp macro" do
4
+ it "should allow defmacro and use on different call" do
5
+ dslisp = DsLisp.new
6
+ dslisp.evaluate([:defmacro, :square, [:x], [:quote, [:*, :x, :x]]])
7
+ dslisp.evaluate([:square, 5]).should be == 25
8
+ end
9
+ end
10
+
11
+
data/spec/macro.rb~ ADDED
@@ -0,0 +1,11 @@
1
+ require "dslisprb"
2
+
3
+ describe DsLisp, "ds lisp macro" do
4
+ it "should allow defmacro and use on different call" do
5
+ dslisp = DsLisp.new
6
+ dslisp.evaluate([:defmacro, :square, [:z], [:quote, [:*, :x, :x]]])
7
+ dslisp.evaluate([:square, 5]).should be == 25
8
+ end
9
+ end
10
+
11
+
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dslisprb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Dario Seminara
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-25 00:00:00.000000000Z
13
+ dependencies: []
14
+ description:
15
+ email: robertodarioseminara@gmail.com
16
+ executables:
17
+ - dslisprb
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - lib/dslisprb.rb
22
+ - lib/dslisprb.rb~
23
+ - spec/macro.rb
24
+ - spec/ast.rb
25
+ - spec/defun.rb~
26
+ - spec/execution.rb~
27
+ - spec/execution.rb
28
+ - spec/defun.rb
29
+ - spec/functions.rb~
30
+ - spec/macro.rb~
31
+ - spec/functions.rb
32
+ - spec/ast.rb~
33
+ - README.rdoc
34
+ - Rakefile
35
+ - TODO
36
+ - CHANGELOG
37
+ - bin/dslisprb
38
+ homepage: http://github.com/tario/dslisprb
39
+ licenses: []
40
+ post_install_message:
41
+ rdoc_options: []
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ! '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ requirements: []
57
+ rubyforge_project:
58
+ rubygems_version: 1.8.10
59
+ signing_key:
60
+ specification_version: 3
61
+ summary: Lisp interpreter written in ruby, using code generation
62
+ test_files: []