hammock-ruby 0.0.1.alpha

Sign up to get free protection for your applications and to get access to all the features.
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