hammock-ruby 0.0.1.alpha

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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +132 -0
  3. data/bin/hammock +55 -0
  4. data/lib/clojure/core.clj +6872 -0
  5. data/lib/hammock.rb +0 -0
  6. data/lib/hammock/aref.rb +57 -0
  7. data/lib/hammock/array_chunk.rb +49 -0
  8. data/lib/hammock/atom.rb +50 -0
  9. data/lib/hammock/block.rb +8 -0
  10. data/lib/hammock/chunk_buffer.rb +25 -0
  11. data/lib/hammock/chunked_cons.rb +108 -0
  12. data/lib/hammock/chunked_seq.rb +97 -0
  13. data/lib/hammock/compiler.rb +197 -0
  14. data/lib/hammock/environment.rb +40 -0
  15. data/lib/hammock/errors.rb +14 -0
  16. data/lib/hammock/function.rb +187 -0
  17. data/lib/hammock/ichunked_seq.rb +23 -0
  18. data/lib/hammock/ideref.rb +5 -0
  19. data/lib/hammock/ifn.rb +10 -0
  20. data/lib/hammock/ilookup.rb +6 -0
  21. data/lib/hammock/interfaces.rb +80 -0
  22. data/lib/hammock/ipersistent_collection.rb +15 -0
  23. data/lib/hammock/ireference.rb +15 -0
  24. data/lib/hammock/iseq.rb +12 -0
  25. data/lib/hammock/lazy_sequence.rb +82 -0
  26. data/lib/hammock/lazy_transformer.rb +169 -0
  27. data/lib/hammock/list.rb +232 -0
  28. data/lib/hammock/loop_locals.rb +34 -0
  29. data/lib/hammock/map.rb +205 -0
  30. data/lib/hammock/meta.rb +12 -0
  31. data/lib/hammock/multi_method.rb +52 -0
  32. data/lib/hammock/namespace.rb +185 -0
  33. data/lib/hammock/reader.rb +570 -0
  34. data/lib/hammock/recur_locals.rb +16 -0
  35. data/lib/hammock/reduced.rb +15 -0
  36. data/lib/hammock/repl.rb +29 -0
  37. data/lib/hammock/rt.rb +580 -0
  38. data/lib/hammock/sequence.rb +59 -0
  39. data/lib/hammock/set.rb +144 -0
  40. data/lib/hammock/stream.rb +40 -0
  41. data/lib/hammock/symbol.rb +65 -0
  42. data/lib/hammock/var.rb +186 -0
  43. data/lib/hammock/vector.rb +309 -0
  44. data/lib/hammock/version.rb +3 -0
  45. data/lib/hammock/volatile.rb +19 -0
  46. data/spec/examples/data.hmk +4 -0
  47. data/spec/hammock/reader_spec.rb +242 -0
  48. data/spec/hammock/rt_spec.rb +10 -0
  49. data/spec/hammock/sequence_spec.rb +24 -0
  50. metadata +139 -0
@@ -0,0 +1,12 @@
1
+ module Hammock
2
+ # meta should always be a persistent map
3
+ module Meta
4
+ def meta
5
+ @meta
6
+ end
7
+
8
+ def with_meta(meta)
9
+ self.class.alloc_from(self, meta)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,52 @@
1
+ require 'hammock/map'
2
+ require 'hammock/ifn'
3
+ require 'hammock/errors'
4
+
5
+ module Hammock
6
+ class MultiMethod
7
+ include IFn
8
+ attr_reader :method_table
9
+
10
+ def initialize(name, dispatch_fn, default_dispatch)
11
+ @name = name.to_s
12
+ @dispatch_fn = dispatch_fn
13
+ @default_dispatch = default_dispatch
14
+ @method_table = Map.new
15
+ @lock = Mutex.new
16
+ end
17
+
18
+ def reset
19
+ @lock.synchronize do
20
+ @method_table = Map.new
21
+ end
22
+ self
23
+ end
24
+
25
+ def add_method(dispatch_value, fn)
26
+ @lock.synchronize do
27
+ @method_table = method_table.assoc(dispatch_value, fn)
28
+ end
29
+ self
30
+ end
31
+
32
+ def remove_method(dispatch_value)
33
+ @lock.synchronize do
34
+ @method_table = method_table.without(dispatch_value)
35
+ end
36
+ self
37
+ end
38
+
39
+ def get_method(dispatch_val)
40
+ @method_table.get(dispatch_val) || @method_table.get(@default_dispatch)
41
+ end
42
+
43
+ def call(*args)
44
+ dval = @dispatch_fn.call(*args)
45
+ if fn = get_method(dval)
46
+ fn.call(*args)
47
+ else
48
+ raise Error, "No method in multimethod #@name for dispatch value: #{dval}"
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,185 @@
1
+ require 'atomic'
2
+ require 'hammock/errors'
3
+ require 'hammock/map'
4
+
5
+ module Hammock
6
+ NAMESPACES = {}
7
+
8
+ class Namespace
9
+ attr_reader :name
10
+
11
+ def self.all
12
+ Sequence.from_array NAMESPACES.values
13
+ end
14
+
15
+ def self.find(name)
16
+ NAMESPACES[name_only(name)]
17
+ end
18
+
19
+ def self.remove(name)
20
+ NAMESPACES.delete(name_only(name))
21
+ end
22
+
23
+ def self.name_only(name)
24
+ case name
25
+ when Hammock::Symbol, Namespace
26
+ name.name
27
+ else
28
+ name
29
+ end
30
+ end
31
+
32
+ def self.find_or_create(name)
33
+ name = name_only(name)
34
+ existing = find(name)
35
+ existing and return existing
36
+
37
+ newns = new(name)
38
+ NAMESPACES[name] = newns
39
+ end
40
+
41
+ def self.find_item(ns, symbol)
42
+ if symbol.ns == ns.name
43
+ ns.find_var(symbol.name)
44
+ elsif symbol.ns
45
+ mod = find(symbol.ns)
46
+ mod.find_var(symbol.name)
47
+ else
48
+ ns.find_var(symbol.name)
49
+ end
50
+ end
51
+
52
+ def initialize(name)
53
+ @name = name
54
+ @mappings = Atomic.new(Map.new)
55
+ @aliases = Atomic.new(Map.new)
56
+ end
57
+
58
+ def ==(other)
59
+ self.object_id == other.object_id
60
+ end
61
+
62
+ def aliases
63
+ @aliases.value
64
+ end
65
+
66
+ def mappings
67
+ @mappings.value
68
+ end
69
+
70
+ def publics
71
+ ret = []
72
+ mappings.each do |k,v|
73
+ if Var === v && v.namespace == self && v.public?
74
+ ret << [k,v]
75
+ end
76
+ end
77
+ Map.from_pairs(ret)
78
+ end
79
+
80
+ def interns
81
+ ret = []
82
+ mappings.each do |k,v|
83
+ if Var === v && v.namespace == self
84
+ ret << [k,v]
85
+ end
86
+ end
87
+ Map.from_pairs(ret)
88
+ end
89
+
90
+ def refers
91
+ ret = []
92
+ mappings.each do |k,v|
93
+ if Var === v && v.namespace != self
94
+ ret << [k,v]
95
+ end
96
+ end
97
+ Map.from_pairs(ret)
98
+ end
99
+
100
+ def add_alias(sym, ns)
101
+ sym = self.class.name_only(sym)
102
+ @aliases.update do |map|
103
+ map.assoc(sym, ns)
104
+ end
105
+ end
106
+
107
+ def remove_alias(sym)
108
+ sym = self.class.name_only(sym)
109
+ @aliases.update do |map|
110
+ map.dissoc(sym)
111
+ end
112
+ end
113
+
114
+ def lookup_alias(name)
115
+ name = self.class.name_only(name)
116
+ aliases[name]
117
+ end
118
+
119
+ def find_var(name)
120
+ name = self.class.name_only(name)
121
+ mappings[name]
122
+ end
123
+
124
+ def unmap(symbol)
125
+ sym = Symbol.intern(symbol)
126
+ if sym.ns
127
+ raise Error, "Can't unmap a ns-qualified symbol"
128
+ end
129
+ @mappings.update do |map|
130
+ map.dissoc(sym.name)
131
+ end
132
+ nil
133
+ end
134
+
135
+ def has_var?(name)
136
+ name = self.class.name_only(name)
137
+ mappings.key?(name)
138
+ end
139
+
140
+ def find_var!(name)
141
+ find_var(name) or raise "Unable to find #{name} within #@name"
142
+ end
143
+
144
+ def intern(symbol)
145
+ sym = Symbol.intern(symbol)
146
+ if sym.ns
147
+ raise ArgumentError, "Can't intern a ns-qualified symbol"
148
+ end
149
+ var = Var.new(self, symbol)
150
+ @mappings.update do |mappings|
151
+ mappings.assoc(sym.name, var)
152
+ end
153
+ var
154
+ end
155
+
156
+ def refer(symbol, var)
157
+ sym = Symbol.intern(symbol)
158
+ if sym.ns
159
+ raise ArgumentError, "Can't intern a ns-qualified symbol"
160
+ end
161
+
162
+ if obj = find_var(symbol)
163
+ if obj == var
164
+ return var
165
+ else
166
+ # warn on redefine
167
+ end
168
+ end
169
+
170
+ @mappings.update do |mappings|
171
+ mappings.assoc(sym.name, var)
172
+ end
173
+
174
+ var
175
+ end
176
+
177
+ def inspect
178
+ "#<Namespace: #@name>"
179
+ end
180
+
181
+ def to_s
182
+ @name
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,570 @@
1
+ require 'delegate'
2
+ require 'hammock/meta'
3
+ require 'hammock/map'
4
+ require 'hammock/rt'
5
+ require 'hammock/set'
6
+ require 'hammock/sequence'
7
+ require 'hammock/symbol'
8
+ require 'hammock/vector'
9
+
10
+ module Hammock
11
+ class Reader
12
+ class LineNumberingIO < SimpleDelegator
13
+ attr_reader :line_number, :column_number, :filename
14
+
15
+ NEWLINE = $/ # line sep
16
+
17
+ def initialize(io)
18
+ @column_number = 0
19
+ @line_number = 1
20
+ @filename = if File === io
21
+ io.path
22
+ else
23
+ "(input)"
24
+ end
25
+ super
26
+ end
27
+
28
+ def getc
29
+ @column_number += 1
30
+ char = __getobj__.getc
31
+ if char == NEWLINE
32
+ @line_number += 1
33
+ @last_line_length = @column_number
34
+ @column_number = 0
35
+ end
36
+ char
37
+ end
38
+
39
+ def ungetc(char)
40
+ __getobj__.ungetc(char)
41
+ if char == NEWLINE
42
+ @line_number -= 1
43
+ @column_number = @last_line_length
44
+ end
45
+ end
46
+ end
47
+
48
+ TOKENS = {
49
+ "true" => true,
50
+ "false" => false,
51
+ "nil" => nil
52
+ }
53
+
54
+ # SYMBOL_PATTERN = Regexp.new("^:?([^/0-9].*/)?(/|[^/0-9][^/]*)$")
55
+ UNQUOTE = Symbol.intern("clojure.core", "unquote")
56
+ UNQUOTE_SPLICING = Symbol.intern("clojure.core", "unquote-splicing")
57
+ APPLY = Symbol.intern("clojure.core", "apply")
58
+ DEREF = Symbol.intern("clojure.core", "deref")
59
+ SEQ = Symbol.intern("clojure.core", "seq")
60
+ CONCAT = Symbol.intern("clojure.core", "concat")
61
+ LIST = Symbol.intern("list")
62
+ QUOTE = Symbol.intern("quote")
63
+ AMP = Symbol.intern("&")
64
+ VECTOR = Symbol.intern("clojure.core", "vector")
65
+ HASHMAP = Symbol.intern("clojure.core", "hash-map")
66
+ HASHSET = Symbol.intern("clojure.core", "hash-set")
67
+ FN = Symbol.intern("clojure.core", "fn")
68
+
69
+ MACROS = {
70
+ ?( => :read_list,
71
+ ?) => :read_rparen,
72
+ ?[ => :read_vector,
73
+ ?] => :read_rbracket,
74
+ ?{ => :read_map,
75
+ ?} => :read_rbrace,
76
+ ?: => :read_keyword,
77
+ ?" => :read_string,
78
+ ?; => :read_comment,
79
+ ?# => :read_dispatched,
80
+ ?% => :read_arg,
81
+ ?' => :read_quoted,
82
+ ?@ => :read_deref,
83
+ ?` => :read_syntax_quoted,
84
+ ?^ => :read_meta,
85
+ ?~ => :read_unquote,
86
+ "\\" => :read_char
87
+ }
88
+
89
+ DISPATCH_MACROS = {
90
+ ?{ => :read_set,
91
+ ?" => :read_regex,
92
+ ?^ => :read_meta,
93
+ ?( => :read_function,
94
+ ?' => :read_var
95
+ }
96
+
97
+ HEXCHARS = "0123456789ABCDEF".split("")
98
+ THE_VAR = Symbol.intern("var")
99
+
100
+ def whitespace?(char)
101
+ char == " " || char == "\n" || char == "\t" || char == ","
102
+ end
103
+
104
+ def macro?(char)
105
+ MACROS.has_key? char
106
+ end
107
+
108
+ def terminating_macro?(char)
109
+ macro?(char) && char != ?# && char != ?' && char != ?%
110
+ end
111
+
112
+ def ensure_line_numbering(io)
113
+ if LineNumberingIO === io
114
+ io
115
+ else
116
+ LineNumberingIO.new io
117
+ end
118
+ end
119
+
120
+ def read_all(io)
121
+ io = ensure_line_numbering(io)
122
+ yield read(io) until io.eof?
123
+ end
124
+
125
+ def read(io)
126
+ io = ensure_line_numbering(io)
127
+ until io.eof?
128
+ char = io.getc
129
+ while whitespace?(char)
130
+ char = io.getc
131
+ end
132
+
133
+ break if io.eof?
134
+
135
+ if char.match(/\d/)
136
+ break read_number(io, char)
137
+ end
138
+
139
+ if m = MACROS[char]
140
+ ret = send(m, io, char)
141
+ next if ret == io
142
+ break ret
143
+ end
144
+
145
+ if char == "+" || char == "-"
146
+ nextc = io.getc
147
+ io.ungetc(nextc)
148
+ if nextc.match(/\d/)
149
+ break read_number(io, char)
150
+ end
151
+ end
152
+
153
+ token = read_token(io, char)
154
+ break interpret_token(io, token)
155
+ end
156
+ end
157
+
158
+ def interpret_token(io, token)
159
+ TOKENS.fetch(token) do
160
+ meta = Map.from_hash(line: io.line_number, column: io.column_number, file: io.filename)
161
+ Symbol.intern(token).with_meta(meta)
162
+ end
163
+ end
164
+
165
+ def read_list(io, char)
166
+ meta = Map.from_hash(line: io.line_number, column: io.column_number, file: io.filename)
167
+ list = read_delimited_list(")", io)
168
+ Sequence.from_array(list, meta)
169
+ end
170
+
171
+ def read_vector(io, char)
172
+ vec = read_delimited_list("]", io)
173
+ Vector.from_array vec
174
+ end
175
+
176
+ def read_map(io, char)
177
+ map = read_delimited_list("}", io)
178
+ Map.from_array map
179
+ end
180
+
181
+ def read_set(io, char)
182
+ set = read_delimited_list("}", io)
183
+ Set.from_array set
184
+ end
185
+
186
+ def read_delimited_list(delimiter, io)
187
+ a = []
188
+
189
+ loop do
190
+ char = io.getc
191
+ while whitespace?(char)
192
+ char = io.getc
193
+ end
194
+
195
+ raise "Unexpected EOF at line #{io.line_number}" unless char
196
+
197
+ break if char == delimiter
198
+
199
+ if m = MACROS[char]
200
+ ret = send(m, io, char)
201
+ if ret && ret != io
202
+ a << ret
203
+ end
204
+ else
205
+ io.ungetc(char)
206
+ a << read(io)
207
+ end
208
+ end
209
+
210
+ a
211
+ end
212
+
213
+ def read_rparen(io, char)
214
+ raise "Unexpected ) at line #{io.line_number}"
215
+ end
216
+
217
+ def read_keyword(io, colon)
218
+ keyword = ""
219
+ char = io.getc
220
+ if char == ":"
221
+ prefix = RT::CURRENT_NS.deref.name
222
+ keyword << prefix << "/"
223
+ else
224
+ io.ungetc(char)
225
+ end
226
+
227
+ loop do
228
+ char = io.getc
229
+ if whitespace?(char) || terminating_macro?(char) || !char
230
+ io.ungetc(char)
231
+ break
232
+ end
233
+ keyword << char
234
+ end
235
+ keyword.to_sym
236
+ end
237
+
238
+ def read_stringish(io, open_quote)
239
+ str = ""
240
+ loop do
241
+ char = io.getc
242
+ if !char
243
+ io.ungetc(char)
244
+ break
245
+ end
246
+
247
+ break if char == '"'
248
+
249
+ if char == "\\"
250
+ char = io.getc
251
+ case char
252
+ when ?t
253
+ char = "\t"
254
+ when ?r
255
+ char = "\r"
256
+ when ?n
257
+ char = "\n"
258
+ when ?b
259
+ char = "\b"
260
+ when ?f
261
+ char = "\f"
262
+ when ?u
263
+ char = io.getc
264
+ unless HEXCHARS.include?(char)
265
+ raise "Expected only hex characters in unicode escape sequence: line #{io.line_number}"
266
+ end
267
+ char = read_unicode_char(io, char)
268
+ end
269
+ end
270
+
271
+ str << char
272
+ end
273
+ str
274
+ end
275
+
276
+ def read_unicode_char(io, char)
277
+ digits = char
278
+ loop do
279
+ char = io.getc
280
+
281
+ if !char
282
+ io.ungetc(char)
283
+ break
284
+ end
285
+
286
+ break unless HEXCHARS.include?(char)
287
+ digits << char
288
+ end
289
+
290
+ interpret_unicode_char(digits)
291
+ end
292
+
293
+ def interpret_unicode_char(digits)
294
+ [digits.hex].pack "U"
295
+ end
296
+
297
+ def read_string(io, open_quote)
298
+ str = read_stringish(io, open_quote)
299
+ str
300
+ end
301
+
302
+ def read_regex(io, open_quote)
303
+ str = read_stringish(io, open_quote)
304
+ Regexp.new(str)
305
+ end
306
+
307
+ def read_char(io, escape)
308
+ char = io.getc
309
+ c = read_token(io, char)
310
+ char = case c
311
+ when "newline" then "\n"
312
+ when "space" then " "
313
+ when "tab" then "\t"
314
+ when "backspace" then "\b"
315
+ when "formfeed" then "\f"
316
+ when "return" then "\r"
317
+ when /^u/
318
+ interpret_unicode_char(c[1..-1])
319
+ else
320
+ c
321
+ end
322
+
323
+ char
324
+ end
325
+
326
+ def read_comment(io, char)
327
+ while char && char != "\n" do
328
+ char = io.getc
329
+ end
330
+ end
331
+
332
+ def read_dispatched(io, _)
333
+ char = io.getc
334
+ unless char
335
+ raise "Unexpected EOF at line #{io.line_number}" unless char
336
+ end
337
+
338
+ method = DISPATCH_MACROS.fetch(char)
339
+ send(method, io, char)
340
+ end
341
+
342
+ def read_number(io, char)
343
+ digits = read_token(io, char)
344
+ match_number(digits) or raise "Unable to parse number: #{digits} at line #{io.line_number}"
345
+ end
346
+
347
+ def match_number(digits)
348
+ case digits
349
+ when /^[-+]?[\d]+$/
350
+ digits.to_i
351
+ when /^[-+]?\d+\.\d+$/
352
+ digits.to_f
353
+ when /^[-+]?\d+\/\d+$/
354
+ digits.to_r
355
+ end
356
+ end
357
+
358
+ def read_quoted(io, quote_mark)
359
+ read_wrapped(io, QUOTE)
360
+ end
361
+
362
+ def read_deref(io, at)
363
+ read_wrapped(io, DEREF)
364
+ end
365
+
366
+ def read_wrapped(io, sym)
367
+ RT.list(sym, read(io))
368
+ end
369
+
370
+ def read_unquote(io, quote_mark)
371
+ char = io.getc
372
+ if char == "@"
373
+ ret = read(io)
374
+ RT.list(UNQUOTE_SPLICING, ret)
375
+ else
376
+ io.ungetc(char)
377
+ ret = read(io)
378
+ RT.list(UNQUOTE, ret)
379
+ end
380
+ end
381
+
382
+ def read_syntax_quoted(io, quote_mark)
383
+ form = read(io)
384
+ Thread.current[:gensym_env] = Map.new
385
+ syntax_quote(form)
386
+ ensure
387
+ Thread.current[:gensym_env] = nil
388
+ end
389
+
390
+ def syntax_quote(form)
391
+ ret = nil
392
+ return unless form
393
+ if RT.special(form)
394
+ ret = RT.list(QUOTE, form)
395
+ elsif Hammock::Symbol === form
396
+ sym = form
397
+ if !sym.ns && sym.name.end_with?("#")
398
+ unless map = Thread.current[:gensym_env]
399
+ raise "Gensym literal not in syntax-quote"
400
+ end
401
+ unless gs = map[sym.name]
402
+ gs = Symbol.intern(nil, sym.name[0..-2] + "__#{RT.next_id}__auto__")
403
+ Thread.current[:gensym_env] = map.assoc(sym.name, gs)
404
+ end
405
+ sym = gs
406
+ else
407
+ ns = RT::CURRENT_NS.deref
408
+ lookup = ns.find_var(sym.name)
409
+ if lookup
410
+ sym = Hammock::Symbol.intern(ns.name, sym.name)
411
+ else
412
+ sym
413
+ end
414
+ end
415
+ ret = RT.list(QUOTE, sym)
416
+ elsif form.respond_to?(:first)
417
+ if form.first == UNQUOTE
418
+ ret = form.tail.first
419
+ elsif form.first == UNQUOTE_SPLICING
420
+ raise "This is a problem"
421
+ elsif Map === form
422
+ ret = RT.list(APPLY, HASHMAP, RT.list(SEQ, RT.cons(CONCAT, syntax_quote_expand_list(form))))
423
+ elsif Hammock::Set === form
424
+ ret = RT.list(APPLY, HASHSET, RT.list(SEQ, RT.cons(CONCAT, syntax_quote_expand_list(form))))
425
+ elsif Vector === form
426
+ ret = RT.list(APPLY, VECTOR, RT.list(SEQ, RT.cons(CONCAT, syntax_quote_expand_list(form))))
427
+ elsif Hammock::List === form
428
+ seq = RT.seq(form)
429
+ if seq
430
+ ret = RT.list(SEQ, RT.cons(CONCAT, syntax_quote_expand_list(seq)))
431
+ else
432
+ ret = RT.cons(Hammock::Symbol.intern("list"), nil)
433
+ end
434
+ end
435
+ elsif String === form || Numeric === form || ::Symbol === form
436
+ ret = form
437
+ else
438
+ ret = RT.list(QUOTE, form)
439
+ end
440
+ ret
441
+ end
442
+
443
+
444
+ def unquote?(form)
445
+ Hammock::List === form && (UNQUOTE == RT.first(form))
446
+ end
447
+
448
+ def unquote_splicing?(form)
449
+ Hammock::List === form && (UNQUOTE_SPLICING == RT.first(form))
450
+ end
451
+
452
+ def syntax_quote_expand_list(seq)
453
+ ret = Vector.new
454
+ seq = RT.seq(seq)
455
+ while seq && !seq.empty?
456
+ item = seq.first
457
+ if unquote?(item)
458
+ ret = ret.cons(RT.list(LIST, RT.second(item)))
459
+ elsif unquote_splicing?(item)
460
+ ret = ret.cons(RT.second(item))
461
+ else
462
+ ret = ret.cons(RT.list(LIST, syntax_quote(item)))
463
+ end
464
+ seq = seq.tail
465
+ end
466
+ RT.seq(ret)
467
+ end
468
+
469
+ def read_var(io, quote_mark)
470
+ Hammock::Sequence.from_array [THE_VAR, read(io)]
471
+ end
472
+
473
+ def read_function(io, paren)
474
+ Thread.current[:arg_env] = Map.new
475
+ io.ungetc(paren)
476
+ form = read(io)
477
+ args = Vector.new
478
+ argsyms = Thread.current[:arg_env]
479
+ keys = argsyms.keys.to_a
480
+ unless argsyms.empty?
481
+ higharg = keys.max
482
+ if higharg > 0
483
+ (1..higharg).each do |i|
484
+ sym = argsyms[i] || garg(i)
485
+ args = args.cons(sym)
486
+ end
487
+ end
488
+
489
+ if restsym = argsyms[-1]
490
+ args = args.cons(AMP)
491
+ args = args.cons(restsym)
492
+ end
493
+ end
494
+ RT.list(FN, args, form)
495
+ ensure
496
+ Thread.current[:arg_env] = nil
497
+ end
498
+
499
+ def read_meta(io, hat)
500
+ meta = read(io)
501
+ if ::Symbol === meta
502
+ meta = Map.from_array [meta, true]
503
+ elsif Hammock::Symbol === meta
504
+ return
505
+ end
506
+ following = read(io)
507
+
508
+ unless Meta === following
509
+ raise "#{following.inspect} does not implement metadata"
510
+ end
511
+
512
+ if Map === following.meta
513
+ meta = following.meta.merge(meta)
514
+ end
515
+
516
+ following.with_meta(meta)
517
+ end
518
+
519
+ def read_token(io, initch)
520
+ chars = initch.dup
521
+ loop do
522
+ char = io.getc
523
+ if !char || whitespace?(char) || terminating_macro?(char)
524
+ io.ungetc(char)
525
+ break
526
+ end
527
+ chars << char
528
+ end
529
+ chars
530
+ end
531
+
532
+ def garg(n)
533
+ Symbol.intern (n == -1 ? "rest" : "p#{n}") + "__#{RT.next_id}#"
534
+ end
535
+
536
+ def register_arg(n)
537
+ argsyms = Thread.current[:arg_env]
538
+ if(!argsyms)
539
+ raise ArgumentError, "arg literal not in #()"
540
+ end
541
+ unless ret = argsyms[n]
542
+ ret = garg(n)
543
+ Thread.current[:arg_env] = argsyms.assoc(n, ret)
544
+ end
545
+ ret
546
+ end
547
+
548
+ def read_arg(io, pct)
549
+ unless Thread.current[:arg_env]
550
+ return interpret_token(io, read_token(io, '%'))
551
+ end
552
+ char = io.getc
553
+ io.ungetc(char)
554
+
555
+ # % alone is first arg
556
+ if whitespace?(char) || terminating_macro?(char)
557
+ return register_arg(1)
558
+ end
559
+ n = read(io)
560
+ if n == AMP
561
+ return register_arg(-1)
562
+ elsif Integer === n
563
+ register_arg(n)
564
+ else
565
+ raise ArgumentError, "arg literal must be %, %& or %integer"
566
+ end
567
+ end
568
+
569
+ end
570
+ end