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.
Files changed (5) hide show
  1. data/README +10 -0
  2. data/bin/nendo +17 -0
  3. data/lib/init.nnd +486 -0
  4. data/lib/nendo.rb +1270 -0
  5. 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