nendo 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/README +10 -0
- data/bin/nendo +17 -0
- data/lib/init.nnd +486 -0
- data/lib/nendo.rb +1270 -0
- metadata +58 -0
data/lib/nendo.rb
ADDED
|
@@ -0,0 +1,1270 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# -*- encoding: utf-8 -*-
|
|
3
|
+
#
|
|
4
|
+
# Nendo: "Principle of Least Surprise (for Rubyist)"
|
|
5
|
+
#
|
|
6
|
+
#
|
|
7
|
+
#
|
|
8
|
+
require 'stringio'
|
|
9
|
+
require 'pp'
|
|
10
|
+
|
|
11
|
+
class Nil
|
|
12
|
+
include Enumerable
|
|
13
|
+
def each() end
|
|
14
|
+
def to_arr() [] end
|
|
15
|
+
def length() 0 end
|
|
16
|
+
def isNull() true end
|
|
17
|
+
def isDotted() false end
|
|
18
|
+
def lastAtom() nil end
|
|
19
|
+
def to_s() "" end
|
|
20
|
+
def car()
|
|
21
|
+
raise "Error: pair required, but got ()"
|
|
22
|
+
end
|
|
23
|
+
def cdr()
|
|
24
|
+
raise "Error: pair required, but got ()"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class LispString < String
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class LispMacro < Proc
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class Symbol
|
|
35
|
+
def setLispToken( token )
|
|
36
|
+
@token = token
|
|
37
|
+
end
|
|
38
|
+
def sourcefile
|
|
39
|
+
@token ? @token.sourcefile : ""
|
|
40
|
+
end
|
|
41
|
+
def lineno
|
|
42
|
+
@token ? @token.lineno : 1
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class Cell
|
|
47
|
+
include Enumerable
|
|
48
|
+
|
|
49
|
+
def initialize( car = Nil.new, cdr = Nil.new )
|
|
50
|
+
@car = car
|
|
51
|
+
@cdr = cdr
|
|
52
|
+
end
|
|
53
|
+
attr_accessor :car, :cdr
|
|
54
|
+
|
|
55
|
+
def each # Supporting iterator
|
|
56
|
+
if not isNull
|
|
57
|
+
it = self
|
|
58
|
+
while Nil != it.class
|
|
59
|
+
yield it
|
|
60
|
+
if it.cdr.is_a? Cell
|
|
61
|
+
it = it.cdr
|
|
62
|
+
else
|
|
63
|
+
it = Nil.new
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def length() self.to_arr.length end
|
|
70
|
+
def size() self.length end # alias of length
|
|
71
|
+
|
|
72
|
+
def isDotted
|
|
73
|
+
((Cell != @cdr.class) and (Nil != @cdr.class))
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def isNull
|
|
77
|
+
((Nil == @car.class) and (Nil == @cdr.class))
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def lastCell
|
|
81
|
+
lastOne = self
|
|
82
|
+
self.each { |x| lastOne = x }
|
|
83
|
+
lastOne
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def lastAtom
|
|
87
|
+
lastOne = self.lastCell
|
|
88
|
+
if lastOne.isDotted
|
|
89
|
+
lastOne.cdr
|
|
90
|
+
else
|
|
91
|
+
nil
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def to_arr
|
|
96
|
+
if isNull
|
|
97
|
+
[]
|
|
98
|
+
else
|
|
99
|
+
self.map {|x| x.car}
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
class Array
|
|
105
|
+
def to_list( lastAtom = nil )
|
|
106
|
+
if 0 == self.length
|
|
107
|
+
Nil.new
|
|
108
|
+
else
|
|
109
|
+
cells = self.map { |x|
|
|
110
|
+
Cell.new( x )
|
|
111
|
+
}
|
|
112
|
+
ptr = cells.pop
|
|
113
|
+
ptr.cdr = lastAtom if lastAtom
|
|
114
|
+
cells.reverse.each { |x|
|
|
115
|
+
x.cdr = ptr
|
|
116
|
+
ptr = x
|
|
117
|
+
}
|
|
118
|
+
return ptr
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
class Token
|
|
124
|
+
def initialize( kind, str, sourcefile, lineno = nil, column = nil )
|
|
125
|
+
@kind = kind
|
|
126
|
+
@str = str
|
|
127
|
+
@sourcefile = sourcefile
|
|
128
|
+
@lineno = lineno
|
|
129
|
+
@column = column
|
|
130
|
+
end
|
|
131
|
+
attr_accessor :kind, :str, :sourcefile, :lineno, :column
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class CharReader
|
|
136
|
+
def initialize( inport, sourcefile )
|
|
137
|
+
@inport = inport
|
|
138
|
+
@sourcefile = sourcefile
|
|
139
|
+
self.reset
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def reset
|
|
143
|
+
@lineno = 1
|
|
144
|
+
@column = 1
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def getc
|
|
148
|
+
@undo_lineno = @lineno
|
|
149
|
+
@undo_column = @column
|
|
150
|
+
ch = @inport.getc
|
|
151
|
+
if nil != ch
|
|
152
|
+
if ch.chr.match( /[\r\n]/ )
|
|
153
|
+
@lineno += 1
|
|
154
|
+
end
|
|
155
|
+
@column += 1
|
|
156
|
+
end
|
|
157
|
+
ch
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def ungetc( ch )
|
|
161
|
+
@lineno = @undo_lineno
|
|
162
|
+
@column = @undo_column
|
|
163
|
+
@inport.ungetc( ch )
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def sourcefile
|
|
167
|
+
@sourcefile
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def lineno
|
|
171
|
+
@lineno
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def column
|
|
175
|
+
@column
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
class Reader
|
|
180
|
+
## tokens
|
|
181
|
+
T_EOF = :t_eof
|
|
182
|
+
T_LPAREN = :t_lparen
|
|
183
|
+
T_RPAREN = :t_rparen
|
|
184
|
+
T_SYMBOL = :t_symbol
|
|
185
|
+
T_NUM = :t_num
|
|
186
|
+
T_STRING = :t_string
|
|
187
|
+
T_QUOTE = :t_quote
|
|
188
|
+
T_QUASIQUOTE = :t_quasiquote
|
|
189
|
+
T_UNQUOTE = :t_unquote
|
|
190
|
+
T_UNQUOTE_SPLICING = :t_unquote_splicing
|
|
191
|
+
T_FEEDTO = :t_feedto
|
|
192
|
+
T_DOT = :t_dot
|
|
193
|
+
T_LINEFEED = :t_linefeed
|
|
194
|
+
T_COMMENT = :t_comment
|
|
195
|
+
|
|
196
|
+
# inport is IO class
|
|
197
|
+
def initialize( inport, sourcefile, debug = false )
|
|
198
|
+
@chReader = CharReader.new( inport, sourcefile )
|
|
199
|
+
@curtoken = nil
|
|
200
|
+
@debug = debug
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def reset
|
|
204
|
+
@chReader.reset
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def sourcefile
|
|
208
|
+
@chReader.sourcefile
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def lineno
|
|
212
|
+
@chReader.lineno
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def skipspace
|
|
216
|
+
begin
|
|
217
|
+
ch = @chReader.getc
|
|
218
|
+
break if nil == ch # non eof?
|
|
219
|
+
#printf( " skipspace: [%02x]\n", ch ) if @debug
|
|
220
|
+
end while ch.chr.match( /[ \t]/ )
|
|
221
|
+
@chReader.ungetc( ch ) if nil != ch
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def readwhile( exp )
|
|
225
|
+
ret = ""
|
|
226
|
+
while true
|
|
227
|
+
ch = @chReader.getc
|
|
228
|
+
#printf( " readwhile: [%02x]\n", ch ) if @debug
|
|
229
|
+
if !ch # eof?
|
|
230
|
+
break
|
|
231
|
+
end
|
|
232
|
+
if ch.chr.match( exp )
|
|
233
|
+
ret += ch.chr
|
|
234
|
+
else
|
|
235
|
+
@chReader.ungetc( ch )
|
|
236
|
+
break
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
ret
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def tokenWithComment
|
|
243
|
+
skipspace
|
|
244
|
+
ch = @chReader.getc
|
|
245
|
+
if nil == ch # eof?
|
|
246
|
+
@curtoken = Token.new( T_EOF, "", @chReader.sourcefile, @chReader.lineno, @chReader.column )
|
|
247
|
+
else
|
|
248
|
+
str = ch.chr
|
|
249
|
+
kind =
|
|
250
|
+
case str
|
|
251
|
+
when /[\']/
|
|
252
|
+
T_QUOTE
|
|
253
|
+
when /[\`]/
|
|
254
|
+
T_QUASIQUOTE
|
|
255
|
+
when /[,]/
|
|
256
|
+
str += readwhile( /[@]/ )
|
|
257
|
+
if 1 == str.length
|
|
258
|
+
T_UNQUOTE
|
|
259
|
+
else
|
|
260
|
+
T_UNQUOTE_SPLICING
|
|
261
|
+
end
|
|
262
|
+
when '('
|
|
263
|
+
T_LPAREN
|
|
264
|
+
when ')'
|
|
265
|
+
T_RPAREN
|
|
266
|
+
when '.'
|
|
267
|
+
str += readwhile( /[_a-zA-Z0-9!?.-]/ ).gsub( /[-]/, '_' )
|
|
268
|
+
if 1 == str.length
|
|
269
|
+
T_DOT
|
|
270
|
+
else
|
|
271
|
+
T_SYMBOL
|
|
272
|
+
end
|
|
273
|
+
when /[\r\n]/
|
|
274
|
+
T_LINEFEED
|
|
275
|
+
when /;/
|
|
276
|
+
readwhile( /[^\r\n]/ )
|
|
277
|
+
str = ""
|
|
278
|
+
T_COMMENT
|
|
279
|
+
when /[#!]/
|
|
280
|
+
readwhile( /[^\r\n]/ )
|
|
281
|
+
str = ""
|
|
282
|
+
T_COMMENT
|
|
283
|
+
when /[_a-zA-Z]/ # symbol
|
|
284
|
+
str += readwhile( /[_a-zA-Z0-9!?*.-]/ ).gsub( /[-]/, '_' )
|
|
285
|
+
T_SYMBOL
|
|
286
|
+
when /[*\/=!<>&|%]/ # symbol
|
|
287
|
+
str += readwhile( /[+*\/=!<>&|?%-]/ )
|
|
288
|
+
if str.match( /^[=][>]$/ )
|
|
289
|
+
T_FEEDTO
|
|
290
|
+
else
|
|
291
|
+
T_SYMBOL
|
|
292
|
+
end
|
|
293
|
+
when /[+-]/ # number
|
|
294
|
+
str += readwhile( /[0-9.]/ )
|
|
295
|
+
if 1 < str.length
|
|
296
|
+
T_NUM
|
|
297
|
+
else
|
|
298
|
+
T_SYMBOL
|
|
299
|
+
end
|
|
300
|
+
when /[0-9]/ # number
|
|
301
|
+
str += readwhile( /[0-9.]/ )
|
|
302
|
+
T_NUM
|
|
303
|
+
when /["]/ # String
|
|
304
|
+
str = LispString.new( readwhile( /[^"]/ )) ; readwhile( /["]/ )
|
|
305
|
+
T_STRING
|
|
306
|
+
else
|
|
307
|
+
str += readwhile( /[^ \t\r\n]/ )
|
|
308
|
+
raise NameError, sprintf( "unknown token for Nendo [%s]", str )
|
|
309
|
+
end
|
|
310
|
+
printf( " token: [%s] : %s (%s:L%d:C%d)\n", str, kind.to_s, @chReader.sourcefile, @chReader.lineno, @chReader.column ) if @debug
|
|
311
|
+
@curtoken = Token.new( kind, str, @chReader.sourcefile, @chReader.lineno, @chReader.column )
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def token
|
|
316
|
+
begin
|
|
317
|
+
tokenWithComment
|
|
318
|
+
end while T_COMMENT == curtoken.kind
|
|
319
|
+
curtoken
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def curtoken
|
|
323
|
+
if !@curtoken
|
|
324
|
+
self.token
|
|
325
|
+
end
|
|
326
|
+
@curtoken
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def atom
|
|
330
|
+
cur = curtoken
|
|
331
|
+
printf( " NonT: [%s] : [%s]\n", "atom", cur.str ) if @debug
|
|
332
|
+
token
|
|
333
|
+
case cur.kind
|
|
334
|
+
when T_SYMBOL
|
|
335
|
+
sym = cur.str.intern
|
|
336
|
+
sym.setLispToken( cur )
|
|
337
|
+
case sym
|
|
338
|
+
when :true
|
|
339
|
+
true
|
|
340
|
+
when :false
|
|
341
|
+
false
|
|
342
|
+
when :nil
|
|
343
|
+
nil
|
|
344
|
+
else
|
|
345
|
+
sym
|
|
346
|
+
end
|
|
347
|
+
when T_NUM
|
|
348
|
+
if cur.str.match( /[.]/ ) # floating point
|
|
349
|
+
cur.str.to_f
|
|
350
|
+
else
|
|
351
|
+
cur.str.to_i
|
|
352
|
+
end
|
|
353
|
+
when T_STRING
|
|
354
|
+
cur.str
|
|
355
|
+
when T_QUOTE
|
|
356
|
+
:quote
|
|
357
|
+
when T_QUASIQUOTE
|
|
358
|
+
:quasiquote
|
|
359
|
+
when T_UNQUOTE
|
|
360
|
+
:unquote
|
|
361
|
+
when T_UNQUOTE_SPLICING
|
|
362
|
+
:unquote_splicing
|
|
363
|
+
when T_DOT
|
|
364
|
+
:dot_operator
|
|
365
|
+
when T_FEEDTO
|
|
366
|
+
:feedto
|
|
367
|
+
else
|
|
368
|
+
raise "Error: Unknown token in atom()"
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
# list := sexp
|
|
373
|
+
# | atom ... atom
|
|
374
|
+
# | atom ... . atom
|
|
375
|
+
def list
|
|
376
|
+
printf( " NonT: [%s]\n", "list" ) if @debug
|
|
377
|
+
dotted = false
|
|
378
|
+
cells = []
|
|
379
|
+
lastAtom = nil
|
|
380
|
+
while true
|
|
381
|
+
case curtoken.kind
|
|
382
|
+
when T_LINEFEED
|
|
383
|
+
token # skipEnter
|
|
384
|
+
when T_EOF
|
|
385
|
+
raise SyntaxError, "Error: unbalanced paren(1)"
|
|
386
|
+
when T_LPAREN
|
|
387
|
+
cells << Cell.new( sexp() )
|
|
388
|
+
when T_RPAREN
|
|
389
|
+
break
|
|
390
|
+
when T_DOT
|
|
391
|
+
if 0 == cells.length
|
|
392
|
+
# (. symbol1 symbol2 ... ) form
|
|
393
|
+
cells << Cell.new( atom() )
|
|
394
|
+
else
|
|
395
|
+
# ( symbol1 . symbol2 ... ) form
|
|
396
|
+
token
|
|
397
|
+
lastAtom = sexp()
|
|
398
|
+
end
|
|
399
|
+
when T_QUOTE , T_QUASIQUOTE , T_UNQUOTE , T_UNQUOTE_SPLICING
|
|
400
|
+
cells << Cell.new( sexp() )
|
|
401
|
+
else
|
|
402
|
+
if lastAtom
|
|
403
|
+
raise "Error : illegal dotted pair syntax."
|
|
404
|
+
else
|
|
405
|
+
cells << Cell.new( atom() )
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
## setup list
|
|
410
|
+
if 0 == cells.size
|
|
411
|
+
Cell.new() # null list
|
|
412
|
+
elsif 1 == cells.size
|
|
413
|
+
if lastAtom
|
|
414
|
+
cells.first.cdr = lastAtom
|
|
415
|
+
end
|
|
416
|
+
cells.first
|
|
417
|
+
elsif 1 < cells.size
|
|
418
|
+
ptr = cells.pop
|
|
419
|
+
if lastAtom
|
|
420
|
+
ptr.cdr = lastAtom
|
|
421
|
+
end
|
|
422
|
+
cells.reverse.each { |x|
|
|
423
|
+
x.cdr = ptr
|
|
424
|
+
ptr = x
|
|
425
|
+
}
|
|
426
|
+
cells.first
|
|
427
|
+
end
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
def skipEnter
|
|
431
|
+
while T_LINEFEED == curtoken.kind
|
|
432
|
+
token
|
|
433
|
+
end
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
# sexp := ( list ) | 'sexp | `sexp | atom
|
|
437
|
+
def sexp
|
|
438
|
+
printf( " NonT: [%s]\n", "sexp" ) if @debug
|
|
439
|
+
case curtoken.kind
|
|
440
|
+
when T_LINEFEED
|
|
441
|
+
token
|
|
442
|
+
sexp()
|
|
443
|
+
when T_EOF
|
|
444
|
+
raise SyntaxError, "Error: unbalanced paren(2)"
|
|
445
|
+
when T_LPAREN
|
|
446
|
+
skipEnter
|
|
447
|
+
token # consume '('
|
|
448
|
+
ret = list()
|
|
449
|
+
skipEnter
|
|
450
|
+
token # consume ')'
|
|
451
|
+
ret
|
|
452
|
+
when T_RPAREN
|
|
453
|
+
raise SyntaxError, "Error: unbalanced paren(3)"
|
|
454
|
+
when T_QUOTE , T_QUASIQUOTE , T_UNQUOTE , T_UNQUOTE_SPLICING
|
|
455
|
+
_atom = atom() ## "quote" symbol
|
|
456
|
+
Cell.new( _atom, Cell.new( sexp() ))
|
|
457
|
+
else
|
|
458
|
+
atom()
|
|
459
|
+
end
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
# return value is [ S-expression-tree, eof-flag, valid-sexp-flag ]
|
|
463
|
+
def _read
|
|
464
|
+
case curtoken.kind
|
|
465
|
+
when T_EOF
|
|
466
|
+
[ Nil.new, true, false ]
|
|
467
|
+
when T_LINEFEED
|
|
468
|
+
token
|
|
469
|
+
[ Nil.new, false, false ]
|
|
470
|
+
else
|
|
471
|
+
[ sexp(), false, true ]
|
|
472
|
+
end
|
|
473
|
+
end
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
# built-in functions
|
|
478
|
+
module BuiltinFunctions
|
|
479
|
+
def __assertFlat( *args )
|
|
480
|
+
if 0 == args.length
|
|
481
|
+
raise ArgumentError, "Error: + - * / % operator got illegal argument. "
|
|
482
|
+
else
|
|
483
|
+
args.each { |x|
|
|
484
|
+
if Cell == x.class or Nil == x.class
|
|
485
|
+
raise ArgumentError, "Error: + - * / % operator got illegal argument. "
|
|
486
|
+
end
|
|
487
|
+
}
|
|
488
|
+
end
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
def __assertList( funcname, arg )
|
|
492
|
+
if Cell != arg.class
|
|
493
|
+
raise ArgumentError, "Error: %s expects a list argument.\n"
|
|
494
|
+
end
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
def _equal_QMARK( a, b )
|
|
498
|
+
if a.class != b.class
|
|
499
|
+
false
|
|
500
|
+
elsif a.class == Cell
|
|
501
|
+
if a.isNull and b.isNull
|
|
502
|
+
true
|
|
503
|
+
else
|
|
504
|
+
_equal_QMARK( a.car, b.car ) and _equal_QMARK( a.cdr, b.cdr )
|
|
505
|
+
end
|
|
506
|
+
elsif a.class == Nil and b.class == Nil
|
|
507
|
+
true
|
|
508
|
+
else
|
|
509
|
+
(a === b)
|
|
510
|
+
end
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
def _plus( first, *rest )
|
|
514
|
+
raise TypeError if not (_number_QMARK(first) or _string_QMARK(first))
|
|
515
|
+
rest = rest[0].to_arr
|
|
516
|
+
__assertFlat( rest )
|
|
517
|
+
if 0 == rest.length
|
|
518
|
+
first
|
|
519
|
+
else
|
|
520
|
+
rest.inject(first){|x,y| x+y}
|
|
521
|
+
end
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
def _minus( first, *rest )
|
|
525
|
+
raise TypeError if not (_number_QMARK(first) or _string_QMARK(first))
|
|
526
|
+
rest = rest[0].to_arr
|
|
527
|
+
__assertFlat( rest )
|
|
528
|
+
if 0 == rest.length
|
|
529
|
+
- first
|
|
530
|
+
else
|
|
531
|
+
rest.inject(first){|x,y| x-y}
|
|
532
|
+
end
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
def _multi( first, *rest )
|
|
536
|
+
raise TypeError if not _number_QMARK(first)
|
|
537
|
+
rest = rest[0].to_arr
|
|
538
|
+
__assertFlat( rest )
|
|
539
|
+
if 0 == rest.length
|
|
540
|
+
first
|
|
541
|
+
else
|
|
542
|
+
rest.inject(first){|x,y| x*y}
|
|
543
|
+
end
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
def _div( first, *rest )
|
|
547
|
+
raise TypeError if not _number_QMARK(first)
|
|
548
|
+
rest = rest[0].to_arr
|
|
549
|
+
__assertFlat( rest )
|
|
550
|
+
if 0 == rest.length
|
|
551
|
+
1 / first
|
|
552
|
+
else
|
|
553
|
+
rest.inject(first){|x,y| x/y}
|
|
554
|
+
end
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
def _mod( first, *rest )
|
|
558
|
+
raise TypeError if not _number_QMARK(first)
|
|
559
|
+
rest = rest[0].to_arr
|
|
560
|
+
__assertFlat( rest )
|
|
561
|
+
if 0 == rest.length
|
|
562
|
+
1 % first
|
|
563
|
+
else
|
|
564
|
+
rest.inject(first){|x,y| x%y}
|
|
565
|
+
end
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
def _not( arg )
|
|
569
|
+
arg = false if Nil == arg.class
|
|
570
|
+
not arg
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
def _cons( first, second )
|
|
574
|
+
Cell.new( first, second )
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
def _exit( *args )
|
|
578
|
+
if 0 == args[0].length
|
|
579
|
+
Kernel::exit(0)
|
|
580
|
+
else
|
|
581
|
+
arr = args[0].to_arr
|
|
582
|
+
Kernel::exit(arr[0])
|
|
583
|
+
end
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
def _print( format, *rest )
|
|
587
|
+
print( format, *(rest[0].to_arr) )
|
|
588
|
+
end
|
|
589
|
+
|
|
590
|
+
def _printf( format, *rest )
|
|
591
|
+
Kernel::printf( format, *(rest[0].to_arr) )
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
def _sprintf( format, *rest )
|
|
595
|
+
Kernel::sprintf( format, *(rest[0].to_arr) )
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
def _null_QMARK( arg )
|
|
599
|
+
if Nil == arg.class
|
|
600
|
+
true
|
|
601
|
+
elsif Cell == arg.class
|
|
602
|
+
arg.isNull
|
|
603
|
+
else
|
|
604
|
+
false
|
|
605
|
+
end
|
|
606
|
+
end
|
|
607
|
+
def _length( arg )
|
|
608
|
+
if _null_QMARK( arg )
|
|
609
|
+
0
|
|
610
|
+
elsif arg.is_a? Cell
|
|
611
|
+
arg.length
|
|
612
|
+
else
|
|
613
|
+
raise TypeError
|
|
614
|
+
end
|
|
615
|
+
end
|
|
616
|
+
def _list( *args) args[0] end
|
|
617
|
+
def _sort( arg ) arg.to_arr.sort.to_list end
|
|
618
|
+
def _reverse( arg ) arg.to_arr.reverse.to_list end
|
|
619
|
+
def _uniq( arg ) arg.to_arr.uniq.to_list end
|
|
620
|
+
def _range( num ) (0..num-1).to_a.to_list end
|
|
621
|
+
def _eq_QMARK( a,b ) a == b end
|
|
622
|
+
def _gt_QMARK( a,b ) a > b end
|
|
623
|
+
def _ge_QMARK( a,b ) a >= b end
|
|
624
|
+
def _lt_QMARK( a,b ) a < b end
|
|
625
|
+
def _le_QMARK( a,b ) a <= b end
|
|
626
|
+
def _eqv_QMARK( a,b ) a === b end
|
|
627
|
+
def _car( cell ) cell.car end
|
|
628
|
+
def _cdr( cell ) cell.cdr end
|
|
629
|
+
def _write( arg ) printer = Printer.new ; print printer._write( arg ) ; arg end
|
|
630
|
+
def _write_to_string( arg ) printer = Printer.new ; printer._write( arg ) end
|
|
631
|
+
def _display( arg ) printer = Printer.new ; print printer._print( arg ) ; arg end
|
|
632
|
+
def _print( arg ) self._display( arg ) ; self._newline() ; arg end
|
|
633
|
+
def _newline( ) print "\n" end
|
|
634
|
+
def _procedure_QMARK( arg ) ((Proc == arg.class) or (Method == arg.class)) end
|
|
635
|
+
def _macro_QMARK( arg ) (LispMacro == arg.class) end
|
|
636
|
+
def _symbol_QMARK( arg ) (Symbol == arg.class) end
|
|
637
|
+
def _pair_QMARK( arg )
|
|
638
|
+
if _null_QMARK( arg )
|
|
639
|
+
false
|
|
640
|
+
else
|
|
641
|
+
(Cell == arg.class)
|
|
642
|
+
end
|
|
643
|
+
end
|
|
644
|
+
def _number_QMARK( arg ) ((Fixnum == arg.class) or (Float == arg.class)) end
|
|
645
|
+
def _string_QMARK( arg ) String == arg.class end
|
|
646
|
+
def _macroexpand_1( arg )
|
|
647
|
+
if _pair_QMARK( arg )
|
|
648
|
+
macroexpand_1( arg )
|
|
649
|
+
else
|
|
650
|
+
arg
|
|
651
|
+
end
|
|
652
|
+
end
|
|
653
|
+
def _to_s( arg ) arg.to_s end
|
|
654
|
+
def _to_list( arg )
|
|
655
|
+
case arg
|
|
656
|
+
when Array
|
|
657
|
+
arg.to_list
|
|
658
|
+
when Cell
|
|
659
|
+
arg
|
|
660
|
+
else
|
|
661
|
+
raise TypeError
|
|
662
|
+
end
|
|
663
|
+
end
|
|
664
|
+
def _intern( arg ) arg.intern end
|
|
665
|
+
def _string_join( lst, delim )
|
|
666
|
+
lst.to_a.map{ |x| x.car }.join( delim )
|
|
667
|
+
end
|
|
668
|
+
def _require( arg ) Kernel::require( arg ) end
|
|
669
|
+
def _read( *args )
|
|
670
|
+
lst = args[0].to_arr
|
|
671
|
+
io = if 0 == lst.length
|
|
672
|
+
STDIN
|
|
673
|
+
else
|
|
674
|
+
lst[0]
|
|
675
|
+
end
|
|
676
|
+
reader = Reader.new( io, "STDIN", false )
|
|
677
|
+
ret = nil
|
|
678
|
+
begin
|
|
679
|
+
s = reader._read
|
|
680
|
+
ret = s[0]
|
|
681
|
+
if s[1] # EOF?
|
|
682
|
+
ret = Cell.new
|
|
683
|
+
break
|
|
684
|
+
end
|
|
685
|
+
end until s[2]
|
|
686
|
+
ret
|
|
687
|
+
end
|
|
688
|
+
end
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
# Translate S expression to Ruby expression and Evaluation
|
|
692
|
+
class Evaluator
|
|
693
|
+
include BuiltinFunctions
|
|
694
|
+
def initialize( debug = false )
|
|
695
|
+
@binding = binding
|
|
696
|
+
@debug = debug
|
|
697
|
+
@alias = {'+' => 'plus',
|
|
698
|
+
'-' => 'minus',
|
|
699
|
+
'*' => 'multi',
|
|
700
|
+
'/' => 'div',
|
|
701
|
+
'%' => 'mod',
|
|
702
|
+
'=' => 'eq?',
|
|
703
|
+
'==' => 'eq?',
|
|
704
|
+
'>' => 'gt?',
|
|
705
|
+
'>=' => 'ge?',
|
|
706
|
+
'<' => 'lt?',
|
|
707
|
+
'<=' => 'le?',
|
|
708
|
+
'===' => 'eqv?',
|
|
709
|
+
}
|
|
710
|
+
# built-in functions
|
|
711
|
+
@sym = Hash.new
|
|
712
|
+
self.methods.grep( /^_/ ) { |rubySymbol|
|
|
713
|
+
@sym[ rubySymbol ] = self.method( rubySymbol )
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
# initialize global symbols
|
|
717
|
+
rubyExp = @sym.keys.map { |name|
|
|
718
|
+
sprintf( "%s = @sym[ '%s' ] ", name, name )
|
|
719
|
+
}.join( " ; " )
|
|
720
|
+
eval( rubyExp, @binding )
|
|
721
|
+
|
|
722
|
+
# initialize buildin functions as Proc objects
|
|
723
|
+
rubyExp = self.methods.select { |x|
|
|
724
|
+
x.to_s.match( /^_/ )
|
|
725
|
+
}.map { |name|
|
|
726
|
+
sprintf( "%s = self.method( :%s ).to_proc", name, name )
|
|
727
|
+
}.join( " ; " )
|
|
728
|
+
eval( rubyExp, @binding )
|
|
729
|
+
|
|
730
|
+
# reset gensym counter
|
|
731
|
+
@gensym_counter = 0
|
|
732
|
+
end
|
|
733
|
+
|
|
734
|
+
def _gensym( )
|
|
735
|
+
@gensym_counter += 1
|
|
736
|
+
sprintf( "__gensym__%d", @gensym_counter ).intern
|
|
737
|
+
end
|
|
738
|
+
|
|
739
|
+
def toRubyValue( val )
|
|
740
|
+
if NilClass == val.class
|
|
741
|
+
"nil"
|
|
742
|
+
elsif TrueClass == val.class
|
|
743
|
+
val.to_s
|
|
744
|
+
elsif FalseClass == val.class
|
|
745
|
+
val.to_s
|
|
746
|
+
else
|
|
747
|
+
val.to_s
|
|
748
|
+
end
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
def toRubySymbol( name )
|
|
752
|
+
name = name.to_s if Symbol == name.class
|
|
753
|
+
if 0 == name.length
|
|
754
|
+
""
|
|
755
|
+
else
|
|
756
|
+
arr = name.split( /[.]/ )
|
|
757
|
+
arr[0] = arr[0].gsub( /[*]/, '_AMARK' ).gsub( /[?]/, '_QMARK' ).gsub( /[!]/, '_EMARK' ).gsub( /[-]/, '_' ).gsub( /["]/, '' )
|
|
758
|
+
if arr[0].match( /^[A-Z]/ )
|
|
759
|
+
# nothing to do
|
|
760
|
+
elsif arr[0] == ""
|
|
761
|
+
arr[0] = 'Kernel'
|
|
762
|
+
else
|
|
763
|
+
arr[0] = '_' + arr[0]
|
|
764
|
+
end
|
|
765
|
+
arr.join( "." )
|
|
766
|
+
end
|
|
767
|
+
end
|
|
768
|
+
|
|
769
|
+
def isRubyInterface( name )
|
|
770
|
+
name.to_s.match( /[.]/ )
|
|
771
|
+
end
|
|
772
|
+
|
|
773
|
+
def toLispSymbol( name )
|
|
774
|
+
name = name.to_s if Symbol == name.class
|
|
775
|
+
raise ArgumentError if not ('_' == name[0])
|
|
776
|
+
name = name[1..-1]
|
|
777
|
+
name.gsub( /_AMARK/, '*' ).gsub( /_QMARK/, '?' ).gsub( /_EMARK/, '!' )
|
|
778
|
+
end
|
|
779
|
+
|
|
780
|
+
def toRubyArgument( origname, pred, args )
|
|
781
|
+
num = pred.arity
|
|
782
|
+
if 0 == num
|
|
783
|
+
raise ArgumentError, sprintf( "at function `%s'", origname ) if 0 != args.length
|
|
784
|
+
[]
|
|
785
|
+
elsif 0 < num
|
|
786
|
+
if args.isNull
|
|
787
|
+
[ Nil.new ]
|
|
788
|
+
else
|
|
789
|
+
raise ArgumentError, sprintf( "at function `%s'", origname ) if num != args.length
|
|
790
|
+
args.map { |x| x.car }
|
|
791
|
+
end
|
|
792
|
+
else
|
|
793
|
+
num = num.abs( )-1
|
|
794
|
+
raise ArgumentError, sprintf( "at function `%s'", origname ) if num > args.length
|
|
795
|
+
params = []
|
|
796
|
+
rest = []
|
|
797
|
+
args.each_with_index { |x,i|
|
|
798
|
+
if i < num
|
|
799
|
+
params << x.car
|
|
800
|
+
else
|
|
801
|
+
rest << x.car
|
|
802
|
+
end
|
|
803
|
+
}
|
|
804
|
+
result = []
|
|
805
|
+
if 0 < params.length
|
|
806
|
+
result = params
|
|
807
|
+
end
|
|
808
|
+
if 0 == rest.length
|
|
809
|
+
result << Cell.new
|
|
810
|
+
else
|
|
811
|
+
result << rest.to_list
|
|
812
|
+
end
|
|
813
|
+
result
|
|
814
|
+
end
|
|
815
|
+
end
|
|
816
|
+
|
|
817
|
+
def callProcedure( origname, pred, args )
|
|
818
|
+
rubyArgument = toRubyArgument( origname, pred, args )
|
|
819
|
+
pred.call( *rubyArgument )
|
|
820
|
+
end
|
|
821
|
+
|
|
822
|
+
def execFunc( funcname, args, sourcefile, lineno, lambda_flag )
|
|
823
|
+
case funcname
|
|
824
|
+
when :set! # `set!' special form
|
|
825
|
+
sprintf( "%s = %s", toRubySymbol( args.car.to_s.sub( /^:/, "" )), toRubyValue( args.cdr.car ))
|
|
826
|
+
when :error
|
|
827
|
+
sprintf( 'begin raise RuntimeError, %s ; rescue => __e ; __e.set_backtrace( ["%s:%d"] + __e.backtrace ) ; raise __e ; end ', toRubyValue( args.car ), sourcefile, lineno )
|
|
828
|
+
else
|
|
829
|
+
if (not lambda_flag) and isRubyInterface( funcname )
|
|
830
|
+
# Ruby method
|
|
831
|
+
# 1) convert arguments
|
|
832
|
+
argStr = args.map { |x| toRubyValue( x.car ) }.join( "," )
|
|
833
|
+
# 2) generate caller code part
|
|
834
|
+
sprintf( "%s( %s )", toRubySymbol( funcname ), argStr )
|
|
835
|
+
else
|
|
836
|
+
# Nendo function
|
|
837
|
+
argStr = args.map { |x| toRubyValue( x.car ) }.join( " ,Cell.new(" )
|
|
838
|
+
argStr += args.map { |x| "" }.join( ")" )
|
|
839
|
+
if lambda_flag
|
|
840
|
+
"anonymouse"
|
|
841
|
+
sprintf( "callProcedure( 'anonymouse', %s, Cell.new( %s ))", funcname, argStr )
|
|
842
|
+
else
|
|
843
|
+
origname = funcname.to_s
|
|
844
|
+
funcname = funcname.to_s
|
|
845
|
+
funcname = @alias[ funcname ] if @alias[ funcname ]
|
|
846
|
+
sprintf( "callProcedure( '%s', %s, Cell.new( %s ))", origname, toRubySymbol( funcname ), argStr )
|
|
847
|
+
end
|
|
848
|
+
end
|
|
849
|
+
end
|
|
850
|
+
end
|
|
851
|
+
|
|
852
|
+
def makeBegin( args )
|
|
853
|
+
ar = args.map { |e|
|
|
854
|
+
translate( e.car )
|
|
855
|
+
}
|
|
856
|
+
"begin " + ar.join( ";" ) + " end"
|
|
857
|
+
end
|
|
858
|
+
|
|
859
|
+
def toRubyParameter( argform )
|
|
860
|
+
argsyms = []
|
|
861
|
+
rest = nil
|
|
862
|
+
if Symbol == argform.class
|
|
863
|
+
rest = argform
|
|
864
|
+
else
|
|
865
|
+
argsyms = argform.map { |x| toRubySymbol( x.car ) }
|
|
866
|
+
rest = argform.lastAtom
|
|
867
|
+
end
|
|
868
|
+
if rest
|
|
869
|
+
rest = toRubySymbol( rest )
|
|
870
|
+
argsyms << "*__rest__"
|
|
871
|
+
sprintf( "|%s| %s = __rest__[0] ; ", argsyms.join( "," ), rest )
|
|
872
|
+
else
|
|
873
|
+
sprintf( "|%s|", argsyms.join( "," ))
|
|
874
|
+
end
|
|
875
|
+
end
|
|
876
|
+
|
|
877
|
+
def makeClosure( sym, args )
|
|
878
|
+
first = args.car
|
|
879
|
+
if args.car.car == :quote
|
|
880
|
+
first = args.car.cdr.car
|
|
881
|
+
end
|
|
882
|
+
rest = args.cdr
|
|
883
|
+
argStr = toRubyParameter( first )
|
|
884
|
+
str = case sym
|
|
885
|
+
when :macro
|
|
886
|
+
sprintf( "LispMacro.new { %s ", argStr )
|
|
887
|
+
when :lambda
|
|
888
|
+
sprintf( " Proc.new { %s ", argStr )
|
|
889
|
+
else
|
|
890
|
+
raise "Error: makeClosure: unknown symbol type " + sym
|
|
891
|
+
end
|
|
892
|
+
ar = rest.map { |e|
|
|
893
|
+
translate( e.car )
|
|
894
|
+
}
|
|
895
|
+
str += ar.join( ";" ) + "}"
|
|
896
|
+
end
|
|
897
|
+
|
|
898
|
+
def makeIf( args )
|
|
899
|
+
_condition = translate( args.car )
|
|
900
|
+
_then = translate( args.cdr.car )
|
|
901
|
+
_else = nil
|
|
902
|
+
if 2 < args.length
|
|
903
|
+
_else = translate( args.cdr.cdr.car )
|
|
904
|
+
end
|
|
905
|
+
if _else
|
|
906
|
+
str = sprintf( "if ( %s ) then %s else %s end ", _condition, _then, _else )
|
|
907
|
+
else
|
|
908
|
+
str = sprintf( "if ( %s ) then %s end ", _condition, _then )
|
|
909
|
+
end
|
|
910
|
+
end
|
|
911
|
+
|
|
912
|
+
def makeLet( args )
|
|
913
|
+
_name = "___lambda"
|
|
914
|
+
str = ""
|
|
915
|
+
argvals = []
|
|
916
|
+
if args.car.is_a? Nil
|
|
917
|
+
# nothing to do
|
|
918
|
+
str = sprintf( "begin %s = lambda { || ", _name )
|
|
919
|
+
rest = args.cdr
|
|
920
|
+
else
|
|
921
|
+
if :quote == args.car.car
|
|
922
|
+
_name = args.car.cdr.car.to_s
|
|
923
|
+
args = args.cdr
|
|
924
|
+
end
|
|
925
|
+
rest = args.cdr
|
|
926
|
+
argsyms = args.car.map { |x|
|
|
927
|
+
toRubySymbol( x.car.car.cdr.car.to_s )
|
|
928
|
+
}
|
|
929
|
+
argvals = args.car.map { |x|
|
|
930
|
+
translate( x.car.cdr.car )
|
|
931
|
+
}
|
|
932
|
+
str = sprintf( "begin %s = lambda { |%s| ", _name, argsyms.join( "," ))
|
|
933
|
+
end
|
|
934
|
+
ar = rest.map { |e| translate( e.car ) }
|
|
935
|
+
str += ar.join( ";" ) + "} ; "
|
|
936
|
+
str += sprintf( "%s.call( %s ) end ", _name, argvals.join( "," ))
|
|
937
|
+
end
|
|
938
|
+
|
|
939
|
+
def apply( car, cdr, sourcefile, lineno, lambda_flag = false )
|
|
940
|
+
cdr.each { |x|
|
|
941
|
+
if Cell == x.class
|
|
942
|
+
x.car = translate( x.car )
|
|
943
|
+
end
|
|
944
|
+
}
|
|
945
|
+
execFunc( car, cdr, sourcefile, lineno, lambda_flag )
|
|
946
|
+
end
|
|
947
|
+
|
|
948
|
+
def genQuote( sexp, str = "" )
|
|
949
|
+
origStr = str
|
|
950
|
+
case sexp
|
|
951
|
+
when Cell
|
|
952
|
+
if sexp.isNull
|
|
953
|
+
str += "Nil.new"
|
|
954
|
+
else
|
|
955
|
+
arr = sexp.map { |x| genQuote( x.car ) }
|
|
956
|
+
str += "Cell.new("
|
|
957
|
+
str += arr.join( " ,Cell.new(" )
|
|
958
|
+
lastAtom = sexp.lastAtom
|
|
959
|
+
str += "," + genQuote( lastAtom ) if lastAtom
|
|
960
|
+
str += arr.map{ |e| ")" }.join
|
|
961
|
+
end
|
|
962
|
+
when Symbol
|
|
963
|
+
str += sprintf( ":\"%s\"", sexp.to_s )
|
|
964
|
+
when String
|
|
965
|
+
str += sprintf( "\"%s\"", sexp )
|
|
966
|
+
when TrueClass, FalseClass, NilClass # reserved symbols
|
|
967
|
+
str += toRubyValue( sexp )
|
|
968
|
+
else
|
|
969
|
+
str += sprintf( "%s", sexp )
|
|
970
|
+
end
|
|
971
|
+
str
|
|
972
|
+
end
|
|
973
|
+
|
|
974
|
+
def translate( sexp )
|
|
975
|
+
str = ""
|
|
976
|
+
case sexp
|
|
977
|
+
when Cell
|
|
978
|
+
if :quote == sexp.car
|
|
979
|
+
genQuote( sexp.cdr.car )
|
|
980
|
+
elsif sexp.isDotted
|
|
981
|
+
print "Error: can't eval dotted pair"
|
|
982
|
+
raise NameError
|
|
983
|
+
elsif sexp.isNull
|
|
984
|
+
str += "Cell.new()"
|
|
985
|
+
elsif Cell == sexp.car.class
|
|
986
|
+
if :lambda == sexp.car.car
|
|
987
|
+
str += self.apply( translate( sexp.car ), sexp.cdr, sexp.car.car.sourcefile, sexp.car.car.lineno, true )
|
|
988
|
+
else
|
|
989
|
+
str += translate( sexp.car )
|
|
990
|
+
end
|
|
991
|
+
elsif :begin == sexp.car
|
|
992
|
+
str += self.makeBegin( sexp.cdr )
|
|
993
|
+
elsif :lambda == sexp.car
|
|
994
|
+
str += self.makeClosure( :lambda, sexp.cdr )
|
|
995
|
+
elsif :macro == sexp.car
|
|
996
|
+
str += self.makeClosure( :macro, sexp.cdr )
|
|
997
|
+
elsif :if == sexp.car
|
|
998
|
+
str += self.makeIf( sexp.cdr )
|
|
999
|
+
elsif :let == sexp.car
|
|
1000
|
+
str += self.makeLet( sexp.cdr )
|
|
1001
|
+
else
|
|
1002
|
+
str += self.apply( sexp.car, sexp.cdr, sexp.car.sourcefile, sexp.car.lineno )
|
|
1003
|
+
end
|
|
1004
|
+
else
|
|
1005
|
+
case sexp
|
|
1006
|
+
when Symbol
|
|
1007
|
+
sym = sexp.to_s
|
|
1008
|
+
sym = @alias[ sym ] if @alias[ sym ]
|
|
1009
|
+
sym = toRubySymbol( sym )
|
|
1010
|
+
str += sprintf( 'begin %s ; rescue NameError => __e ; __e.set_backtrace( ["%s:%d"] + __e.backtrace ) ; raise __e ; end', sym, sexp.sourcefile, sexp.lineno )
|
|
1011
|
+
when Fixnum
|
|
1012
|
+
str += sexp.to_s
|
|
1013
|
+
when LispString
|
|
1014
|
+
str += sprintf( "\"%s\"", sexp )
|
|
1015
|
+
when Nil
|
|
1016
|
+
str += "Nil.new"
|
|
1017
|
+
when TrueClass, FalseClass, NilClass # reserved symbols
|
|
1018
|
+
str += toRubyValue( sexp )
|
|
1019
|
+
else
|
|
1020
|
+
str += sexp.to_s
|
|
1021
|
+
end
|
|
1022
|
+
end
|
|
1023
|
+
end
|
|
1024
|
+
|
|
1025
|
+
# insert quote in let argument list
|
|
1026
|
+
# ((sym1 list1)
|
|
1027
|
+
# (sym2 list2)
|
|
1028
|
+
# (sym3 list3))
|
|
1029
|
+
# will be transformed
|
|
1030
|
+
# (((quote sym1) list1)
|
|
1031
|
+
# ((quote sym2) list2)
|
|
1032
|
+
# ((quote sym3) list3))
|
|
1033
|
+
def letArgumentList( sexp )
|
|
1034
|
+
sexp.each { |arg|
|
|
1035
|
+
arg.car.car = Cell.new( :quote, Cell.new( arg.car.car ))
|
|
1036
|
+
arg.car.cdr = quoting( arg.car.cdr )
|
|
1037
|
+
}
|
|
1038
|
+
sexp
|
|
1039
|
+
end
|
|
1040
|
+
|
|
1041
|
+
def quoting( sexp )
|
|
1042
|
+
case sexp
|
|
1043
|
+
when Cell
|
|
1044
|
+
if :quote == sexp.car or :quasiquote == sexp.car
|
|
1045
|
+
sexp
|
|
1046
|
+
elsif :set! == sexp.car or :lambda == sexp.car or :macro == sexp.car
|
|
1047
|
+
sexp.cdr.car = Cell.new( :quote, Cell.new( sexp.cdr.car ))
|
|
1048
|
+
sexp.cdr.cdr = quoting( sexp.cdr.cdr )
|
|
1049
|
+
sexp
|
|
1050
|
+
elsif :let == sexp.car
|
|
1051
|
+
case sexp.cdr.car
|
|
1052
|
+
when Cell # let
|
|
1053
|
+
sexp.cdr = Cell.new( letArgumentList( sexp.cdr.car ),
|
|
1054
|
+
quoting( sexp.cdr.cdr ))
|
|
1055
|
+
when Symbol # named let
|
|
1056
|
+
sexp.cdr.car = Cell.new( :quote, Cell.new( sexp.cdr.car ))
|
|
1057
|
+
sexp.cdr.cdr = Cell.new( letArgumentList( sexp.cdr.cdr.car ),
|
|
1058
|
+
quoting( sexp.cdr.cdr.cdr ))
|
|
1059
|
+
end
|
|
1060
|
+
sexp
|
|
1061
|
+
else
|
|
1062
|
+
Cell.new( quoting( sexp.car ), quoting( sexp.cdr ))
|
|
1063
|
+
end
|
|
1064
|
+
else
|
|
1065
|
+
sexp
|
|
1066
|
+
end
|
|
1067
|
+
end
|
|
1068
|
+
|
|
1069
|
+
def macroexpand_1( sexp )
|
|
1070
|
+
case sexp
|
|
1071
|
+
when Cell
|
|
1072
|
+
if :quote == sexp.car
|
|
1073
|
+
sexp
|
|
1074
|
+
else
|
|
1075
|
+
sym = sexp.car.to_s
|
|
1076
|
+
sym = @alias[ sym ] if @alias[ sym ]
|
|
1077
|
+
sym = toRubySymbol( sym )
|
|
1078
|
+
if isRubyInterface( sym )
|
|
1079
|
+
arr = sexp.map { |x| macroexpand_1( x.car ) }
|
|
1080
|
+
arr.to_list( sexp.lastAtom )
|
|
1081
|
+
elsif sexp.car.class == Symbol and eval( sprintf( "(defined? %s and LispMacro == %s.class)", sym,sym ), @binding )
|
|
1082
|
+
eval( sprintf( "@_macro = %s", sym ), @binding )
|
|
1083
|
+
callProcedure( sym, @_macro, sexp.cdr )
|
|
1084
|
+
else
|
|
1085
|
+
arr = sexp.map { |x| macroexpand_1( x.car ) }
|
|
1086
|
+
arr.to_list( sexp.lastAtom )
|
|
1087
|
+
end
|
|
1088
|
+
end
|
|
1089
|
+
else
|
|
1090
|
+
sexp
|
|
1091
|
+
end
|
|
1092
|
+
end
|
|
1093
|
+
|
|
1094
|
+
def lispCompile( sexp )
|
|
1095
|
+
converge = true
|
|
1096
|
+
begin
|
|
1097
|
+
newSexp = macroexpand_1( sexp )
|
|
1098
|
+
converge = _equal_QMARK( newSexp, sexp )
|
|
1099
|
+
sexp = newSexp
|
|
1100
|
+
end until converge
|
|
1101
|
+
sexp
|
|
1102
|
+
end
|
|
1103
|
+
|
|
1104
|
+
def lispEval( sexp, sourcefile, lineno )
|
|
1105
|
+
sexp = lispCompile( sexp )
|
|
1106
|
+
sexp = quoting( sexp );
|
|
1107
|
+
if @debug
|
|
1108
|
+
printf( "\n quoting=<<< %s >>>\n", (Printer.new())._print(sexp))
|
|
1109
|
+
end
|
|
1110
|
+
rubyExp = translate( sexp );
|
|
1111
|
+
printf( " rubyExp=<<< %s >>>\n", rubyExp ) if @debug
|
|
1112
|
+
eval( rubyExp, @binding, sourcefile, lineno );
|
|
1113
|
+
end
|
|
1114
|
+
|
|
1115
|
+
def _load( filename )
|
|
1116
|
+
printer = Printer.new( @debug )
|
|
1117
|
+
open( filename ) {|f|
|
|
1118
|
+
reader = Reader.new( f, filename, false )
|
|
1119
|
+
while true
|
|
1120
|
+
lineno = reader.lineno
|
|
1121
|
+
s = reader._read
|
|
1122
|
+
if s[1] # EOF?
|
|
1123
|
+
break
|
|
1124
|
+
elsif Nil != s[0].class
|
|
1125
|
+
printf( "\n readExp=<<< %s >>>\n", printer._print(s[0]) ) if @debug
|
|
1126
|
+
self.lispEval( s[0], reader.sourcefile, lineno )
|
|
1127
|
+
end
|
|
1128
|
+
end
|
|
1129
|
+
}
|
|
1130
|
+
end
|
|
1131
|
+
|
|
1132
|
+
def _eval( sexp )
|
|
1133
|
+
self.lispEval( sexp, "dynamic S-expression ( no source )", 1 )
|
|
1134
|
+
end
|
|
1135
|
+
|
|
1136
|
+
def _enable_debug()
|
|
1137
|
+
@debug = true
|
|
1138
|
+
end
|
|
1139
|
+
end
|
|
1140
|
+
|
|
1141
|
+
class Printer
|
|
1142
|
+
def initialize( debug = false )
|
|
1143
|
+
@debug = debug
|
|
1144
|
+
end
|
|
1145
|
+
|
|
1146
|
+
def __write( sexp, readable )
|
|
1147
|
+
getQuoteKeyword = lambda { |x|
|
|
1148
|
+
case x
|
|
1149
|
+
when :quote
|
|
1150
|
+
"'"
|
|
1151
|
+
when :quasiquote
|
|
1152
|
+
"`"
|
|
1153
|
+
when :unquote
|
|
1154
|
+
","
|
|
1155
|
+
when :unquote_splicing
|
|
1156
|
+
",@"
|
|
1157
|
+
when :dot_operator
|
|
1158
|
+
"."
|
|
1159
|
+
else
|
|
1160
|
+
false
|
|
1161
|
+
end
|
|
1162
|
+
}
|
|
1163
|
+
case sexp
|
|
1164
|
+
when Cell
|
|
1165
|
+
arr = sexp.map { |x| __write( x.car, readable ) }
|
|
1166
|
+
lastAtom = sexp.lastAtom
|
|
1167
|
+
lastAtom = __write( lastAtom, readable ) if lastAtom
|
|
1168
|
+
keyword = getQuoteKeyword.call( sexp.car )
|
|
1169
|
+
if keyword
|
|
1170
|
+
keyword + arr[1..-1].join( " " ) + (lastAtom ? " . " + lastAtom : "")
|
|
1171
|
+
else
|
|
1172
|
+
"(" + arr.join( " " ) + (lastAtom ? " . " + lastAtom : "") + ")"
|
|
1173
|
+
end
|
|
1174
|
+
when Symbol
|
|
1175
|
+
keyword = getQuoteKeyword.call( sexp )
|
|
1176
|
+
if keyword
|
|
1177
|
+
keyword
|
|
1178
|
+
else
|
|
1179
|
+
sprintf( "%s", sexp.to_s )
|
|
1180
|
+
end
|
|
1181
|
+
when String
|
|
1182
|
+
if readable
|
|
1183
|
+
sprintf( "\"%s\"", sexp.to_s )
|
|
1184
|
+
else
|
|
1185
|
+
sexp.to_s
|
|
1186
|
+
end
|
|
1187
|
+
when Nil
|
|
1188
|
+
"()"
|
|
1189
|
+
when nil
|
|
1190
|
+
"nil"
|
|
1191
|
+
else
|
|
1192
|
+
sprintf( "%s", sexp )
|
|
1193
|
+
end
|
|
1194
|
+
end
|
|
1195
|
+
|
|
1196
|
+
def _print( sexp )
|
|
1197
|
+
self.__write( sexp, false )
|
|
1198
|
+
end
|
|
1199
|
+
def _write( sexp )
|
|
1200
|
+
self.__write( sexp, true )
|
|
1201
|
+
end
|
|
1202
|
+
end
|
|
1203
|
+
|
|
1204
|
+
|
|
1205
|
+
class Nendo
|
|
1206
|
+
def initialize( debug_evaluator = false, debug_printer = false )
|
|
1207
|
+
@debug_evaluator = debug_evaluator
|
|
1208
|
+
@evaluator = Evaluator.new( debug_evaluator )
|
|
1209
|
+
@debug_printer = debug_printer
|
|
1210
|
+
end
|
|
1211
|
+
|
|
1212
|
+
def loadInitFile
|
|
1213
|
+
@evaluator._load( File.dirname(__FILE__) + "/init.nnd" )
|
|
1214
|
+
end
|
|
1215
|
+
|
|
1216
|
+
def load( path )
|
|
1217
|
+
@evaluator._load( path )
|
|
1218
|
+
end
|
|
1219
|
+
|
|
1220
|
+
def repl
|
|
1221
|
+
printer = Printer.new( @debug_printer )
|
|
1222
|
+
reader = nil
|
|
1223
|
+
print "nendo> "
|
|
1224
|
+
begin
|
|
1225
|
+
begin
|
|
1226
|
+
reader = Reader.new( STDIN, "(stdin)", false )
|
|
1227
|
+
rescue => e
|
|
1228
|
+
print e.message + "\n"
|
|
1229
|
+
e.backtrace.each { |x| printf( "\tfrom %s\n", x ) }
|
|
1230
|
+
reader = nil
|
|
1231
|
+
print "\n" + "nendo> "
|
|
1232
|
+
end
|
|
1233
|
+
end until reader
|
|
1234
|
+
while true
|
|
1235
|
+
lineno = reader.lineno
|
|
1236
|
+
begin
|
|
1237
|
+
s = reader._read
|
|
1238
|
+
if s[1] # EOF?
|
|
1239
|
+
break
|
|
1240
|
+
elsif Nil != s[0].class
|
|
1241
|
+
printf( "\n readExp=<<< %s >>>\n", printer._print(s[0]) ) if @debug_evaluator
|
|
1242
|
+
print printer._print( @evaluator.lispEval( s[0], reader.sourcefile, lineno ))
|
|
1243
|
+
print "\n" + "nendo> "
|
|
1244
|
+
end
|
|
1245
|
+
rescue => e
|
|
1246
|
+
print e.message + "\n"
|
|
1247
|
+
e.backtrace.each { |x| printf( "\tfrom %s\n", x ) }
|
|
1248
|
+
print "\n" + "nendo> "
|
|
1249
|
+
end
|
|
1250
|
+
end
|
|
1251
|
+
end
|
|
1252
|
+
|
|
1253
|
+
def replStr( str )
|
|
1254
|
+
printer = Printer.new( @debug_printer )
|
|
1255
|
+
sio = StringIO.open( str )
|
|
1256
|
+
reader = Reader.new( sio, "(string)", false )
|
|
1257
|
+
result = nil
|
|
1258
|
+
while true
|
|
1259
|
+
lineno = reader.lineno
|
|
1260
|
+
s = reader._read
|
|
1261
|
+
if s[1] # EOF?
|
|
1262
|
+
break
|
|
1263
|
+
elsif Nil != s[0].class
|
|
1264
|
+
printf( "\n readExp=<<< %s >>>\n", printer._print(s[0]) ) if @debug_evaluator
|
|
1265
|
+
result = printer._print( @evaluator.lispEval( s[0], reader.sourcefile, lineno ))
|
|
1266
|
+
end
|
|
1267
|
+
end
|
|
1268
|
+
result
|
|
1269
|
+
end
|
|
1270
|
+
end
|