dslisprb 0.0.1

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/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: []