kiyoka-nendo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/README +10 -0
  2. data/bin/nendo +26 -0
  3. data/lib/init.nnd +486 -0
  4. data/lib/nendo.rb +1270 -0
  5. metadata +57 -0
@@ -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