rbl 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.
- 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
|