rbl 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +103 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/rbl +27 -0
- data/bin/setup +8 -0
- data/lib/rubylisp/environment.rb +75 -0
- data/lib/rubylisp/evaluator.rb +227 -0
- data/lib/rubylisp/parser.rb +28 -0
- data/lib/rubylisp/printer.rb +33 -0
- data/lib/rubylisp/reader.rb +209 -0
- data/lib/rubylisp/repl.rb +26 -0
- data/lib/rubylisp/types.rb +169 -0
- data/lib/rubylisp/version.rb +3 -0
- data/lib/rubylisp.rb +1 -0
- data/rubylisp/core.rbl +245 -0
- data/rubylisp/repl.rbl +2 -0
- data/rubylisp.gemspec +34 -0
- metadata +109 -0
@@ -0,0 +1,209 @@
|
|
1
|
+
require 'rubylisp/types'
|
2
|
+
|
3
|
+
module RubyLisp
|
4
|
+
class Reader
|
5
|
+
# from kanaka/mal
|
6
|
+
TOKEN_REGEX = %r{
|
7
|
+
# ignore whitespace and commas
|
8
|
+
[\s,]*
|
9
|
+
|
10
|
+
# match any of...
|
11
|
+
(
|
12
|
+
# the splice-unquote reader macro
|
13
|
+
~@|
|
14
|
+
|
15
|
+
# special characters
|
16
|
+
[\[\]{}()'`~^@]|
|
17
|
+
|
18
|
+
# strings
|
19
|
+
"(?:\\.|[^\\"])*"|
|
20
|
+
|
21
|
+
# comments
|
22
|
+
;.*|
|
23
|
+
|
24
|
+
# any sequence of non-special characters
|
25
|
+
# e.g. symbols, numbers, keywords, booleans, etc.
|
26
|
+
[^\s\[\]{}('"`,;)]*
|
27
|
+
)
|
28
|
+
}x
|
29
|
+
|
30
|
+
attr_accessor :tokens, :position
|
31
|
+
|
32
|
+
def peek
|
33
|
+
@tokens[@position]
|
34
|
+
end
|
35
|
+
|
36
|
+
def next_token
|
37
|
+
token = peek
|
38
|
+
@position += 1
|
39
|
+
token
|
40
|
+
end
|
41
|
+
|
42
|
+
def read_seq(type, end_token, seq=[])
|
43
|
+
case peek
|
44
|
+
when nil
|
45
|
+
raise RubyLisp::ParseError, "Unexpected EOF while parsing #{type}."
|
46
|
+
when end_token
|
47
|
+
next_token
|
48
|
+
type.new(seq)
|
49
|
+
else
|
50
|
+
seq << read_form
|
51
|
+
read_seq(type, end_token, seq)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def read_list
|
56
|
+
read_seq RubyLisp::List, ')'
|
57
|
+
end
|
58
|
+
|
59
|
+
def read_vector
|
60
|
+
read_seq RubyLisp::Vector, ']'
|
61
|
+
end
|
62
|
+
|
63
|
+
def read_hashmap
|
64
|
+
read_seq RubyLisp::HashMap, '}'
|
65
|
+
end
|
66
|
+
|
67
|
+
def read_atom
|
68
|
+
token = next_token
|
69
|
+
case token
|
70
|
+
when nil
|
71
|
+
nil
|
72
|
+
when /^\-?\d+$/
|
73
|
+
RubyLisp::Int.new(token.to_i)
|
74
|
+
when /^\-?\d+\.\d+$/
|
75
|
+
RubyLisp::Float.new(token.to_f)
|
76
|
+
when /^".*"$/
|
77
|
+
# it's safe to use eval here because the tokenizer ensures that
|
78
|
+
# the token is an escaped string representation
|
79
|
+
RubyLisp::String.new(eval(token))
|
80
|
+
# it's a little weird that an unfinished string (e.g. "abc) gets
|
81
|
+
# tokenized as "", but at least the behavior is consistent ¯\_(ツ)_/¯
|
82
|
+
when ""
|
83
|
+
raise RubyLisp::ParseError,
|
84
|
+
"Unexpected EOF while parsing RubyLisp::String."
|
85
|
+
when /^:/
|
86
|
+
RubyLisp::Keyword.new(token[1..-1].to_sym)
|
87
|
+
when 'nil'
|
88
|
+
RubyLisp::Nil.new
|
89
|
+
when 'true'
|
90
|
+
RubyLisp::Boolean.new(true)
|
91
|
+
when 'false'
|
92
|
+
RubyLisp::Boolean.new(false)
|
93
|
+
else
|
94
|
+
RubyLisp::Symbol.new(token)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def read_special_form(special)
|
99
|
+
form = read_form
|
100
|
+
unless form
|
101
|
+
raise RubyLisp::ParseError,
|
102
|
+
"Unexpected EOF while parsing #{special} form."
|
103
|
+
end
|
104
|
+
RubyLisp::List.new([RubyLisp::Symbol.new(special), form])
|
105
|
+
end
|
106
|
+
|
107
|
+
def read_quoted_form
|
108
|
+
read_special_form 'quote'
|
109
|
+
end
|
110
|
+
|
111
|
+
def read_quasiquoted_form
|
112
|
+
read_special_form 'quasiquote'
|
113
|
+
end
|
114
|
+
|
115
|
+
def read_unquoted_form
|
116
|
+
read_special_form 'unquote'
|
117
|
+
end
|
118
|
+
|
119
|
+
def read_splice_unquoted_form
|
120
|
+
read_special_form 'splice-unquote'
|
121
|
+
end
|
122
|
+
|
123
|
+
def read_deref_form
|
124
|
+
read_special_form 'deref'
|
125
|
+
end
|
126
|
+
|
127
|
+
def read_form_with_metadata
|
128
|
+
token = peek
|
129
|
+
case token
|
130
|
+
when nil
|
131
|
+
raise RubyLisp::ParseError, "Unexpected EOF while parsing metadata."
|
132
|
+
when '{'
|
133
|
+
next_token
|
134
|
+
metadata = read_hashmap
|
135
|
+
when /^:/
|
136
|
+
kw = read_form
|
137
|
+
metadata = RubyLisp::HashMap.new([kw, RubyLisp::Boolean.new(true)])
|
138
|
+
else
|
139
|
+
raise RubyLisp::ParseError, "Invalid metadata: '#{token}'"
|
140
|
+
end
|
141
|
+
|
142
|
+
form = read_form
|
143
|
+
unless form
|
144
|
+
raise RubyLisp::ParseError, "Unexpected EOF after metadata."
|
145
|
+
end
|
146
|
+
|
147
|
+
RubyLisp::List.new([RubyLisp::Symbol.new("with-meta"), form, metadata])
|
148
|
+
end
|
149
|
+
|
150
|
+
def read_form
|
151
|
+
case peek
|
152
|
+
when /^;/
|
153
|
+
# ignore comments
|
154
|
+
next_token
|
155
|
+
read_form
|
156
|
+
when '('
|
157
|
+
next_token
|
158
|
+
read_list
|
159
|
+
when '['
|
160
|
+
next_token
|
161
|
+
read_vector
|
162
|
+
when '{'
|
163
|
+
next_token
|
164
|
+
read_hashmap
|
165
|
+
when ')'
|
166
|
+
raise RubyLisp::ParseError, "Unexpected ')'."
|
167
|
+
when ']'
|
168
|
+
raise RubyLisp::ParseError, "Unexpected ']'."
|
169
|
+
when '}'
|
170
|
+
raise RubyLisp::ParseError, "Unexpected '}'."
|
171
|
+
when "'"
|
172
|
+
next_token
|
173
|
+
read_quoted_form
|
174
|
+
when '`'
|
175
|
+
next_token
|
176
|
+
read_quasiquoted_form
|
177
|
+
when '~'
|
178
|
+
next_token
|
179
|
+
read_unquoted_form
|
180
|
+
when '~@'
|
181
|
+
next_token
|
182
|
+
read_splice_unquoted_form
|
183
|
+
when '@'
|
184
|
+
next_token
|
185
|
+
read_deref_form
|
186
|
+
when '^'
|
187
|
+
next_token
|
188
|
+
read_form_with_metadata
|
189
|
+
else
|
190
|
+
read_atom
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def tokenize str
|
195
|
+
@tokens = str.strip.scan(TOKEN_REGEX).flatten[0...-1]
|
196
|
+
@position = 0
|
197
|
+
end
|
198
|
+
|
199
|
+
def Reader.read_str str
|
200
|
+
reader = Reader.new
|
201
|
+
reader.tokenize(str)
|
202
|
+
forms = []
|
203
|
+
while reader.position < reader.tokens.count
|
204
|
+
forms << reader.read_form
|
205
|
+
end
|
206
|
+
forms
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'readline'
|
2
|
+
require 'rubylisp/environment'
|
3
|
+
require 'rubylisp/parser'
|
4
|
+
|
5
|
+
module RubyLisp
|
6
|
+
module REPL
|
7
|
+
module_function
|
8
|
+
|
9
|
+
def start
|
10
|
+
env = RubyLisp::Environment.new(namespace: 'user').stdlib.repl
|
11
|
+
|
12
|
+
while buf = Readline.readline("#{env.namespace}> ", true)
|
13
|
+
begin
|
14
|
+
input = buf.nil? ? '' : buf.strip
|
15
|
+
puts input.empty? ? '' : RubyLisp::Parser.parse(input, env)
|
16
|
+
rescue => e
|
17
|
+
# If an error happens, print it like Ruby would and continue accepting
|
18
|
+
# REPL input.
|
19
|
+
puts e.backtrace
|
20
|
+
.join("\n\t")
|
21
|
+
.sub("\n\t", ": #{e}#{e.class ? " (#{e.class})" : ''}\n\t")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require 'hamster/core_ext'
|
2
|
+
require 'hamster/hash'
|
3
|
+
require 'hamster/list'
|
4
|
+
require 'hamster/vector'
|
5
|
+
|
6
|
+
# Monkey-patch Ruby symbols to act more like Clojure keywords
|
7
|
+
class Symbol
|
8
|
+
def to_s
|
9
|
+
inspect
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(target)
|
13
|
+
if [Hash, Hamster::Hash].member? target.class
|
14
|
+
target[self]
|
15
|
+
else
|
16
|
+
target.instance_variable_get "@#{self.to_s}".to_sym
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Monkey-patch Hamster types to have nicer (and more Clojure-like) string
|
22
|
+
# representations
|
23
|
+
module Hamster
|
24
|
+
class Cons
|
25
|
+
def to_s
|
26
|
+
"(#{join " "})"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module EmptyList
|
31
|
+
def EmptyList.to_s
|
32
|
+
"()"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Vector
|
37
|
+
def to_s
|
38
|
+
"[#{join " "}]"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
module RubyLisp
|
44
|
+
class Value
|
45
|
+
attr_accessor :value, :quoted
|
46
|
+
|
47
|
+
def initialize(value)
|
48
|
+
@value = value
|
49
|
+
end
|
50
|
+
|
51
|
+
def quote
|
52
|
+
@quoted = true
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
def unquote
|
57
|
+
@quoted = false
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_s
|
62
|
+
@value.to_s
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class Boolean < Value; end
|
67
|
+
class Float < Value; end
|
68
|
+
class Int < Value; end
|
69
|
+
class Keyword < Value; end
|
70
|
+
class String < Value; end
|
71
|
+
|
72
|
+
class ParseError < StandardError; end
|
73
|
+
class RuntimeError < StandardError; end
|
74
|
+
|
75
|
+
class HashMap < Value
|
76
|
+
def initialize(seq)
|
77
|
+
if seq.size.odd?
|
78
|
+
raise RubyLisp::ParseError,
|
79
|
+
"A RubyLisp::HashMap must contain an even number of forms."
|
80
|
+
else
|
81
|
+
@value = Hamster::Hash[seq.each_slice(2).to_a]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class List < Value
|
87
|
+
def initialize(values)
|
88
|
+
@value = values.to_list
|
89
|
+
end
|
90
|
+
|
91
|
+
def quote
|
92
|
+
@value.each(&:quote)
|
93
|
+
@quoted = true
|
94
|
+
self
|
95
|
+
end
|
96
|
+
|
97
|
+
def unquote
|
98
|
+
@value.each(&:unquote)
|
99
|
+
@quoted = false
|
100
|
+
self
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class Nil < Value
|
105
|
+
def initialize
|
106
|
+
@value = nil
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class Symbol < Value
|
111
|
+
def to_s
|
112
|
+
@value
|
113
|
+
end
|
114
|
+
|
115
|
+
def resolve(env)
|
116
|
+
# rbl: (.+ 1 2)
|
117
|
+
# ruby: 1.+(2)
|
118
|
+
instance_method = /^\.(.*)/.match(@value).to_a[1]
|
119
|
+
if instance_method
|
120
|
+
return lambda {|obj, *args| obj.send instance_method, *args}
|
121
|
+
end
|
122
|
+
|
123
|
+
# rbl: (File::open "/tmp/foo")
|
124
|
+
# ruby: File::open("/tmp/foo") OR File.open("/tmp/foo")
|
125
|
+
#
|
126
|
+
# rbl: Foo::Bar::BAZ
|
127
|
+
# ruby: Foo::Bar::BAZ
|
128
|
+
if /^\w+(::\w+)+$/ =~ @value
|
129
|
+
first_segment, *segments = @value.split('::')
|
130
|
+
first_segment = Object.const_get first_segment
|
131
|
+
return segments.reduce(first_segment) do |result, segment|
|
132
|
+
if result.class == Proc
|
133
|
+
# a method can only be in the final position
|
134
|
+
raise RubyLisp::RuntimeError, "Invalid value: #{@value}"
|
135
|
+
elsif /^[A-Z]/ =~ segment
|
136
|
+
# get module/class constant
|
137
|
+
result.const_get segment
|
138
|
+
else
|
139
|
+
# if module/class method doesn't exist, trigger a NoMethodError
|
140
|
+
result.send segment unless result.respond_to? segment
|
141
|
+
# call module/class method
|
142
|
+
lambda {|*args| result.send segment, *args }
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
env.get @value
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
class Vector < Value
|
152
|
+
def initialize(values)
|
153
|
+
@value = Hamster::Vector.new(values)
|
154
|
+
end
|
155
|
+
|
156
|
+
def quote
|
157
|
+
@value.each(&:quote)
|
158
|
+
@quoted = true
|
159
|
+
self
|
160
|
+
end
|
161
|
+
|
162
|
+
def unquote
|
163
|
+
@value.each(&:unquote)
|
164
|
+
@quoted = false
|
165
|
+
self
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
data/lib/rubylisp.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# ʕ •ᴥ• ʔ nothing to see here
|
data/rubylisp/core.rbl
ADDED
@@ -0,0 +1,245 @@
|
|
1
|
+
; TODO:
|
2
|
+
; and, or
|
3
|
+
; implement macros
|
4
|
+
; defn
|
5
|
+
; str
|
6
|
+
; comment
|
7
|
+
; ->, ->>
|
8
|
+
; private vars/fns
|
9
|
+
; if-not, when, when-not, if-let, when-let
|
10
|
+
; cond
|
11
|
+
; case
|
12
|
+
|
13
|
+
(ns rubylisp.core)
|
14
|
+
|
15
|
+
;; MATH & LOGIC
|
16
|
+
|
17
|
+
(def +
|
18
|
+
(fn + [x y & more]
|
19
|
+
(let [xy (.+ x y)]
|
20
|
+
(if more
|
21
|
+
(apply + xy more)
|
22
|
+
xy))))
|
23
|
+
|
24
|
+
(def -
|
25
|
+
(fn - [x y & more]
|
26
|
+
(let [xy (.- x y)]
|
27
|
+
(if more
|
28
|
+
(apply - xy more)
|
29
|
+
xy))))
|
30
|
+
|
31
|
+
(def inc
|
32
|
+
(fn inc [x]
|
33
|
+
(+ x 1)))
|
34
|
+
|
35
|
+
(def dec
|
36
|
+
(fn dec [x]
|
37
|
+
(- x 1)))
|
38
|
+
|
39
|
+
(def *
|
40
|
+
(fn * [x y & more]
|
41
|
+
(let [xy (.* x y)]
|
42
|
+
(if more
|
43
|
+
(apply * xy more)
|
44
|
+
xy))))
|
45
|
+
|
46
|
+
(def /
|
47
|
+
(fn / [x y & more]
|
48
|
+
(let [xy (./ x y)]
|
49
|
+
(if more
|
50
|
+
(apply / xy more)
|
51
|
+
xy))))
|
52
|
+
|
53
|
+
(def <
|
54
|
+
(fn < [x y & more]
|
55
|
+
(let [xy (.< x y)]
|
56
|
+
(if more
|
57
|
+
(apply < xy more)
|
58
|
+
xy))))
|
59
|
+
|
60
|
+
(def <=
|
61
|
+
(fn <= [x y & more]
|
62
|
+
(let [xy (.<= x y)]
|
63
|
+
(if more
|
64
|
+
(apply <= xy more)
|
65
|
+
xy))))
|
66
|
+
|
67
|
+
(def =
|
68
|
+
(fn = [x y & more]
|
69
|
+
(let [xy (.== x y)]
|
70
|
+
(if more
|
71
|
+
(apply = xy more)
|
72
|
+
xy))))
|
73
|
+
|
74
|
+
(def not=
|
75
|
+
(fn not= [x y & more]
|
76
|
+
(let [xy (.!= x y)]
|
77
|
+
(if more
|
78
|
+
(apply not= xy more)
|
79
|
+
xy))))
|
80
|
+
|
81
|
+
(def boolean
|
82
|
+
(fn boolean [x]
|
83
|
+
(if x true false)))
|
84
|
+
|
85
|
+
(def not
|
86
|
+
(fn not [x]
|
87
|
+
(if x false true)))
|
88
|
+
|
89
|
+
(def >
|
90
|
+
(fn > [x y & more]
|
91
|
+
(let [xy (.> x y)]
|
92
|
+
(if more
|
93
|
+
(apply > xy more)
|
94
|
+
xy))))
|
95
|
+
|
96
|
+
(def >=
|
97
|
+
(fn >= [x y & more]
|
98
|
+
(let [xy (.>= x y)]
|
99
|
+
(if more
|
100
|
+
(apply >= xy more)
|
101
|
+
xy))))
|
102
|
+
|
103
|
+
(def pos?
|
104
|
+
(fn pos? [x]
|
105
|
+
(> (count x) 0)))
|
106
|
+
|
107
|
+
(def neg?
|
108
|
+
(fn neg? [x]
|
109
|
+
(< (count x) 0)))
|
110
|
+
|
111
|
+
(def zero?
|
112
|
+
(fn zero? [x]
|
113
|
+
(= 0 x)))
|
114
|
+
|
115
|
+
;; LISTS & COLLECTIONS
|
116
|
+
|
117
|
+
(def nil?
|
118
|
+
(fn nil? [x]
|
119
|
+
(= nil x)))
|
120
|
+
|
121
|
+
(def count
|
122
|
+
(fn count [x]
|
123
|
+
(if (nil? x)
|
124
|
+
0
|
125
|
+
(.count x))))
|
126
|
+
|
127
|
+
(def empty?
|
128
|
+
(fn empty? [coll]
|
129
|
+
(if (nil? coll)
|
130
|
+
true
|
131
|
+
(.empty? coll))))
|
132
|
+
|
133
|
+
(def list
|
134
|
+
(fn list [& args]
|
135
|
+
(.to_list (if (nil? args)
|
136
|
+
[]
|
137
|
+
args))))
|
138
|
+
|
139
|
+
(def list?
|
140
|
+
(fn list? [x]
|
141
|
+
(.is_a? x Hamster::List)))
|
142
|
+
|
143
|
+
(def string?
|
144
|
+
(fn string? [x]
|
145
|
+
(.is_a? x Kernel::String)))
|
146
|
+
|
147
|
+
(def seq
|
148
|
+
(fn seq [x]
|
149
|
+
(if (not (empty? x))
|
150
|
+
(if (string? x)
|
151
|
+
(.to_list (.chars x))
|
152
|
+
(.to_list x)))))
|
153
|
+
|
154
|
+
(def cons
|
155
|
+
(fn cons [x y]
|
156
|
+
(let [lst (if (nil? y) (Hamster::List::empty) (.to_list y))]
|
157
|
+
(.cons lst x))))
|
158
|
+
|
159
|
+
(def first
|
160
|
+
(fn first [coll]
|
161
|
+
(.first coll)))
|
162
|
+
|
163
|
+
(def last
|
164
|
+
(fn last [coll]
|
165
|
+
(.last coll)))
|
166
|
+
|
167
|
+
(def rest
|
168
|
+
(fn rest [coll]
|
169
|
+
(let [lst (if (nil? coll) (Hamster::List::empty) (.to_list coll))]
|
170
|
+
(.tail lst))))
|
171
|
+
|
172
|
+
(def take
|
173
|
+
(fn take [n coll]
|
174
|
+
(.to_list (.take coll n))))
|
175
|
+
|
176
|
+
;; TODO: support mapping over multiple collections
|
177
|
+
(def map
|
178
|
+
(fn map [f coll]
|
179
|
+
(if (empty? coll)
|
180
|
+
()
|
181
|
+
(cons (f (first coll)) (map f (rest coll))))))
|
182
|
+
|
183
|
+
(def reverse
|
184
|
+
(fn reverse [coll]
|
185
|
+
(if (empty? coll)
|
186
|
+
coll
|
187
|
+
(+ [(last coll)]
|
188
|
+
(reverse (.slice coll 0 (dec (count coll))))))))
|
189
|
+
|
190
|
+
;; TODO: implement as multiple arity fn
|
191
|
+
;; TODO: zero-arity that produces a lazy list, i.e. Hamster::iterate(0,
|
192
|
+
;; &:next).take(5)
|
193
|
+
;; (requires some kind of block syntax)
|
194
|
+
(def range
|
195
|
+
(fn range [& args]
|
196
|
+
(if (= 1 (count args))
|
197
|
+
(apply range 0 args)
|
198
|
+
(apply Hamster::interval args))))
|
199
|
+
|
200
|
+
;; TODO: implement as multiple arity fn
|
201
|
+
(def repeat
|
202
|
+
(fn repeat [& args]
|
203
|
+
(if (= 1 (count args))
|
204
|
+
(apply Hamster::repeat args)
|
205
|
+
(apply Hamster::replicate args))))
|
206
|
+
|
207
|
+
;; STRINGS
|
208
|
+
|
209
|
+
(def str
|
210
|
+
(fn str [& args]
|
211
|
+
(.join (map .to_s args))))
|
212
|
+
|
213
|
+
;; I/O
|
214
|
+
|
215
|
+
(def pr-str
|
216
|
+
(fn pr-str [& args]
|
217
|
+
(if (zero? (count args))
|
218
|
+
""
|
219
|
+
(apply RubyLisp::Printer::pr_str args))))
|
220
|
+
|
221
|
+
(def prn
|
222
|
+
(fn prn [& args]
|
223
|
+
(Kernel::puts (apply pr-str args))
|
224
|
+
nil))
|
225
|
+
|
226
|
+
(def print
|
227
|
+
(fn print [& args]
|
228
|
+
(Kernel::print (.join (map str args) " "))
|
229
|
+
nil))
|
230
|
+
|
231
|
+
(def println
|
232
|
+
(fn print [& args]
|
233
|
+
(Kernel::puts (.join (map str args) " "))
|
234
|
+
nil))
|
235
|
+
|
236
|
+
;; RUBY INTEROP
|
237
|
+
|
238
|
+
(def =@
|
239
|
+
(fn =@ [obj kw value]
|
240
|
+
(.instance_variable_set obj (.to_sym (+ "@" (.to_s kw))) value)))
|
241
|
+
|
242
|
+
(def class
|
243
|
+
(fn class [x]
|
244
|
+
(.class x)))
|
245
|
+
|
data/rubylisp/repl.rbl
ADDED
data/rubylisp.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rubylisp/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "rbl"
|
8
|
+
spec.version = RubyLisp::VERSION
|
9
|
+
spec.authors = ["Dave Yarwood"]
|
10
|
+
spec.email = ["dave.yarwood@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = "A Lisp dialect of Ruby"
|
13
|
+
spec.description = "A Lisp dialect of Ruby"
|
14
|
+
spec.homepage = "https://github.com/daveyarwood/rubylisp"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
if spec.respond_to?(:metadata)
|
18
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
19
|
+
else
|
20
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
21
|
+
"public gem pushes."
|
22
|
+
end
|
23
|
+
|
24
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
25
|
+
f.match(%r{^(test|spec|features)/})
|
26
|
+
end
|
27
|
+
spec.bindir = "exe"
|
28
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
29
|
+
spec.require_paths = ["lib"]
|
30
|
+
|
31
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
32
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
33
|
+
spec.add_runtime_dependency 'hamster', '3.0.0'
|
34
|
+
end
|