nendo 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/nendo +83 -14
- data/emacs/nendo-mode.el +7 -0
- data/example/cgi/dekamoji.cgi +74 -55
- data/example/fact.nnd +2 -5
- data/example/fizzbuzz1.nnd +5 -3
- data/example/nqueen.nnd +71 -0
- data/example/scratch.nnd +1 -2
- data/lib/debug/syslog.nnd +1 -1
- data/lib/debug/syslog.nndc +9 -0
- data/lib/init.nnd +248 -16
- data/lib/init.nndc +7194 -4456
- data/lib/nendo.rb +220 -50
- data/lib/text/html-lite.nndc +74 -57
- data/lib/text/tree.nndc +9 -0
- metadata +6 -12
data/lib/nendo.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
#
|
4
4
|
# nendo.rb - "language core of nendo"
|
5
5
|
#
|
6
|
-
# Copyright (c)
|
6
|
+
# Copyright (c) 2009-2010 Kiyoka Nishiyama <kiyoka@sumibi.org>
|
7
7
|
#
|
8
8
|
# Redistribution and use in source and binary forms, with or without
|
9
9
|
# modification, are permitted provided that the following conditions
|
@@ -35,6 +35,7 @@
|
|
35
35
|
# $Id:
|
36
36
|
#
|
37
37
|
require 'stringio'
|
38
|
+
require 'digest/sha1'
|
38
39
|
#require 'profile'
|
39
40
|
|
40
41
|
class Nil
|
@@ -57,7 +58,7 @@ end
|
|
57
58
|
class LispString < String
|
58
59
|
def LispString.escape( str )
|
59
60
|
if str.is_a? String
|
60
|
-
str.gsub( /["]/, "\\\"" )
|
61
|
+
str.gsub( /\\/, "\\\\\\\\" ).gsub( /["]/, "\\\"" ).gsub( /[\r]/, "\\r" ).gsub( /[\t]/, "\\t" )
|
61
62
|
else
|
62
63
|
raise TypeError
|
63
64
|
end
|
@@ -262,6 +263,7 @@ class Reader
|
|
262
263
|
T_EOF = :t_eof
|
263
264
|
T_LPAREN = :t_lparen
|
264
265
|
T_RPAREN = :t_rparen
|
266
|
+
T_LVECTOR = :t_lvector
|
265
267
|
T_SYMBOL = :t_symbol
|
266
268
|
T_KEYWORD = :t_keyword
|
267
269
|
T_NUM = :t_num
|
@@ -304,7 +306,7 @@ class Reader
|
|
304
306
|
@chReader.ungetc( ch ) if nil != ch
|
305
307
|
end
|
306
308
|
|
307
|
-
def readwhile( exp )
|
309
|
+
def readwhile( exp, oneshot = false )
|
308
310
|
ret = ""
|
309
311
|
while true
|
310
312
|
ch = @chReader.getc
|
@@ -318,10 +320,25 @@ class Reader
|
|
318
320
|
@chReader.ungetc( ch )
|
319
321
|
break
|
320
322
|
end
|
323
|
+
if oneshot then break end
|
321
324
|
end
|
322
325
|
ret
|
323
326
|
end
|
324
327
|
|
328
|
+
def peekchar( exp )
|
329
|
+
ch = @chReader.getc
|
330
|
+
#printf( " peekchar: [%02x]\n", ch ) if @debug
|
331
|
+
if !ch # eof?
|
332
|
+
return nil
|
333
|
+
end
|
334
|
+
if ch.chr.match( exp )
|
335
|
+
ch.chr
|
336
|
+
else
|
337
|
+
@chReader.ungetc( ch )
|
338
|
+
nil
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
325
342
|
def readstring()
|
326
343
|
ret = ""
|
327
344
|
while true
|
@@ -330,15 +347,23 @@ class Reader
|
|
330
347
|
if !ch # eof?
|
331
348
|
break
|
332
349
|
end
|
333
|
-
if ch.chr
|
350
|
+
if ch.chr == "\\"
|
334
351
|
ch2 = @chReader.getc
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
352
|
+
ret += case ch2.chr
|
353
|
+
when '"' # \" reduce to "
|
354
|
+
'"'
|
355
|
+
when '\\' # \\ reduce to \
|
356
|
+
"\\"
|
357
|
+
when 'n'
|
358
|
+
"\n"
|
359
|
+
when 'r'
|
360
|
+
"\r"
|
361
|
+
when 't'
|
362
|
+
"\t"
|
363
|
+
else
|
364
|
+
""
|
365
|
+
end
|
366
|
+
elsif ch.chr != '"'
|
342
367
|
ret += ch.chr
|
343
368
|
else
|
344
369
|
@chReader.ungetc( ch )
|
@@ -362,7 +387,7 @@ class Reader
|
|
362
387
|
when /[\`]/
|
363
388
|
T_QUASIQUOTE
|
364
389
|
when /[,]/
|
365
|
-
str += readwhile( /[@]
|
390
|
+
str += readwhile( /[@]/, true )
|
366
391
|
if 1 == str.length
|
367
392
|
T_UNQUOTE
|
368
393
|
else
|
@@ -386,28 +411,62 @@ class Reader
|
|
386
411
|
str = ""
|
387
412
|
T_COMMENT
|
388
413
|
when /[#]/
|
389
|
-
|
390
|
-
case
|
391
|
-
when
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
readwhile( /[^\r\n]/ )
|
396
|
-
str = ""
|
397
|
-
T_COMMENT
|
398
|
-
else
|
399
|
-
keyword = readwhile( /[a-z]/ )
|
400
|
-
case keyword
|
401
|
-
when "t"
|
402
|
-
str = "true"
|
403
|
-
T_SYMBOL
|
404
|
-
when "f"
|
405
|
-
str = "false"
|
406
|
-
T_SYMBOL
|
414
|
+
nextch = peekchar( /[?!tfbodx(]/ )
|
415
|
+
case nextch
|
416
|
+
when "?"
|
417
|
+
if peekchar( /[=]/ )
|
418
|
+
str = ""
|
419
|
+
T_DEBUG_PRINT
|
407
420
|
else
|
408
421
|
str += readwhile( /[^ \t\r\n]/ )
|
409
422
|
raise NameError, sprintf( "Error: unknown #xxxx syntax for Nendo %s", str )
|
423
|
+
end
|
424
|
+
when "!"
|
425
|
+
readwhile( /[^\r\n]/ )
|
426
|
+
str = ""
|
427
|
+
T_COMMENT
|
428
|
+
when "("
|
429
|
+
str = ""
|
430
|
+
T_LVECTOR
|
431
|
+
when "t"
|
432
|
+
str = "true"
|
433
|
+
T_SYMBOL
|
434
|
+
when "f"
|
435
|
+
str = "false"
|
436
|
+
T_SYMBOL
|
437
|
+
when "b","o","d","x"
|
438
|
+
str = readwhile( /[0-9a-zA-Z]/ )
|
439
|
+
case nextch
|
440
|
+
when "b"
|
441
|
+
if str.match( /^[0-1]+$/ )
|
442
|
+
str = "0b" + str
|
443
|
+
else
|
444
|
+
raise RuntimeError, sprintf( "Error: illegal #b number for Nendo #b%s", str )
|
445
|
+
end
|
446
|
+
when "o"
|
447
|
+
if str.match( /^[0-7]+$/ )
|
448
|
+
str = "0o" + str
|
449
|
+
else
|
450
|
+
raise RuntimeError, sprintf( "Error: illegal #o number for Nendo #o%s", str )
|
451
|
+
end
|
452
|
+
when "d"
|
453
|
+
if str.match( /^[0-9]+$/ )
|
454
|
+
str = "0d" + str
|
455
|
+
else
|
456
|
+
raise RuntimeError, sprintf( "Error: illegal #d number for Nendo #d%s", str )
|
457
|
+
end
|
458
|
+
when "x"
|
459
|
+
if str.match( /^[0-9a-fA-F]+$/ )
|
460
|
+
str = "0x" + str
|
461
|
+
else
|
462
|
+
raise RuntimeError, sprintf( "Error: illegal #x number for Nendo #x%s", str )
|
463
|
+
end
|
410
464
|
end
|
465
|
+
str = Integer( str ).to_s
|
466
|
+
T_NUM
|
467
|
+
else
|
468
|
+
str += readwhile( /[^ \t\r\n]/ )
|
469
|
+
raise NameError, sprintf( "Error: unknown #xxxx syntax for Nendo %s", str )
|
411
470
|
end
|
412
471
|
when /[_a-zA-Z!$%&*+\/:<=>?@^~-]/ # symbol
|
413
472
|
str += readwhile( /[0-9._a-zA-Z!$%&*+\/:<=>?@^~-]/ )
|
@@ -421,7 +480,7 @@ class Reader
|
|
421
480
|
else
|
422
481
|
T_SYMBOL
|
423
482
|
end
|
424
|
-
when /[0-9]/ #
|
483
|
+
when /[0-9]/ # Numeric
|
425
484
|
str += readwhile( /[0-9.]/ )
|
426
485
|
T_NUM
|
427
486
|
when /["]/ # String
|
@@ -498,6 +557,32 @@ class Reader
|
|
498
557
|
end
|
499
558
|
end
|
500
559
|
|
560
|
+
# vector := sexp
|
561
|
+
# | atom ... atom
|
562
|
+
def vector
|
563
|
+
printf( " NonT: [%s]\n", "vector" ) if @debug
|
564
|
+
arr = []
|
565
|
+
while true
|
566
|
+
case curtoken.kind
|
567
|
+
when T_LINEFEED
|
568
|
+
token # skipEnter
|
569
|
+
when T_EOF
|
570
|
+
raise RuntimeError, "Error: unbalanced vector's paren(4)"
|
571
|
+
when T_LPAREN, T_LVECTOR
|
572
|
+
arr << sexp()
|
573
|
+
when T_RPAREN
|
574
|
+
break
|
575
|
+
when T_QUOTE , T_QUASIQUOTE , T_UNQUOTE , T_UNQUOTE_SPLICING, T_DEBUG_PRINT
|
576
|
+
arr << sexp()
|
577
|
+
when T_DOT
|
578
|
+
raise RuntimeError, "Error: illegal list."
|
579
|
+
else
|
580
|
+
arr << atom()
|
581
|
+
end
|
582
|
+
end
|
583
|
+
arr
|
584
|
+
end
|
585
|
+
|
501
586
|
# list := sexp
|
502
587
|
# | atom ... atom
|
503
588
|
# | atom ... . atom
|
@@ -511,8 +596,8 @@ class Reader
|
|
511
596
|
when T_LINEFEED
|
512
597
|
token # skipEnter
|
513
598
|
when T_EOF
|
514
|
-
raise
|
515
|
-
when T_LPAREN
|
599
|
+
raise RuntimeError, "Error: unbalanced paren(1)"
|
600
|
+
when T_LPAREN, T_LVECTOR
|
516
601
|
cells << Cell.new( sexp() )
|
517
602
|
when T_RPAREN
|
518
603
|
break
|
@@ -565,7 +650,7 @@ class Reader
|
|
565
650
|
end
|
566
651
|
end
|
567
652
|
|
568
|
-
# sexp := ( list ) | 'sexp | `sexp | atom
|
653
|
+
# sexp := ( list ) | | #( vector ) | 'sexp | `sexp | atom
|
569
654
|
def sexp
|
570
655
|
printf( " NonT: [%s]\n", "sexp" ) if @debug
|
571
656
|
case curtoken.kind
|
@@ -573,7 +658,7 @@ class Reader
|
|
573
658
|
token
|
574
659
|
sexp()
|
575
660
|
when T_EOF
|
576
|
-
raise
|
661
|
+
raise RuntimeError, "Error: unbalanced paren(2)"
|
577
662
|
when T_LPAREN
|
578
663
|
skipEnter
|
579
664
|
token # consume '('
|
@@ -582,7 +667,14 @@ class Reader
|
|
582
667
|
token # consume ')'
|
583
668
|
ret
|
584
669
|
when T_RPAREN
|
585
|
-
raise
|
670
|
+
raise RuntimeError, "Error: unbalanced paren(3)"
|
671
|
+
when T_LVECTOR
|
672
|
+
skipEnter
|
673
|
+
token # consume '#('
|
674
|
+
ret = vector()
|
675
|
+
skipEnter
|
676
|
+
token # consume ')'
|
677
|
+
ret
|
586
678
|
when T_QUOTE , T_QUASIQUOTE , T_UNQUOTE , T_UNQUOTE_SPLICING
|
587
679
|
_atom = atom() ## "quote" symbol
|
588
680
|
Cell.new( _atom, Cell.new( sexp() ))
|
@@ -825,7 +917,7 @@ module BuiltinFunctions
|
|
825
917
|
(Cell == arg.class)
|
826
918
|
end
|
827
919
|
end
|
828
|
-
def _number_QUMARK( arg )
|
920
|
+
def _number_QUMARK( arg ) arg.is_a? Numeric end
|
829
921
|
def _string_QUMARK( arg ) String == arg.class end
|
830
922
|
def _macroexpand_MIMARK1( arg )
|
831
923
|
if _pair_QUMARK( arg )
|
@@ -850,11 +942,31 @@ module BuiltinFunctions
|
|
850
942
|
raise TypeError
|
851
943
|
end
|
852
944
|
end
|
945
|
+
def _to_arr( arg ) _to_MIMARKarr( arg ) end
|
946
|
+
def _to_MIMARKarr( arg )
|
947
|
+
case arg
|
948
|
+
when Cell
|
949
|
+
arg.to_arr
|
950
|
+
when Array
|
951
|
+
arg
|
952
|
+
else
|
953
|
+
raise TypeError
|
954
|
+
end
|
955
|
+
end
|
853
956
|
def _intern( arg ) arg.intern end
|
854
957
|
def _string_MIMARK_GTMARKsymbol( arg ) arg.intern end
|
855
958
|
def _symbol_MIMARK_GTMARKstring( arg ) arg.to_s end
|
856
|
-
def _string_MIMARKjoin( lst,
|
857
|
-
|
959
|
+
def _string_MIMARKjoin( lst, *args )
|
960
|
+
arr = args[0].to_arr
|
961
|
+
if 0 < arr.length
|
962
|
+
if not arr[0].is_a? String
|
963
|
+
raise TypeError, "Error string-join's expects delimitter as String."
|
964
|
+
else
|
965
|
+
lst.to_a.map{ |x| x.car }.join( arr[0] )
|
966
|
+
end
|
967
|
+
else
|
968
|
+
lst.to_a.map{ |x| x.car }.join
|
969
|
+
end
|
858
970
|
end
|
859
971
|
def _require( arg )
|
860
972
|
Kernel::require( arg )
|
@@ -967,8 +1079,17 @@ module BuiltinFunctions
|
|
967
1079
|
def __ASMARKFILE_ASMARK()
|
968
1080
|
@lastSourcefile
|
969
1081
|
end
|
970
|
-
end
|
971
1082
|
|
1083
|
+
def _vector_MIMARKset_EXMARK( v, index, value )
|
1084
|
+
if !(v.is_a? Array)
|
1085
|
+
raise TypeError, "Error: vector-set! requires Array as argument v(Lisp's vector).\n"
|
1086
|
+
end
|
1087
|
+
if (index < 0) or (v.size <= index)
|
1088
|
+
raise ArgumentError, "Error: vector-set! requires index between 0 and (size-1) number.\n"
|
1089
|
+
end
|
1090
|
+
v[index] = value
|
1091
|
+
end
|
1092
|
+
end
|
972
1093
|
|
973
1094
|
|
974
1095
|
# Translate S expression to Ruby expression and Evaluation
|
@@ -1006,9 +1127,7 @@ class Evaluator
|
|
1006
1127
|
|
1007
1128
|
# built-in functions
|
1008
1129
|
self.methods.grep( /^_/ ) { |rubySymbol|
|
1009
|
-
|
1010
|
-
eval( sprintf( "@%s = @___tmp;", rubySymbol ), @binding )
|
1011
|
-
eval( sprintf( "@global_lisp_binding['%s'] = true;", rubySymbol ), @binding )
|
1130
|
+
global_lisp_define( rubySymbol, self.method( rubySymbol ))
|
1012
1131
|
}
|
1013
1132
|
|
1014
1133
|
# initialize buildin functions as Proc objects
|
@@ -1028,9 +1147,24 @@ class Evaluator
|
|
1028
1147
|
@compiled_code = Hash.new
|
1029
1148
|
end
|
1030
1149
|
|
1150
|
+
def global_lisp_define( rubySymbol, val )
|
1151
|
+
@___tmp = val
|
1152
|
+
eval( sprintf( "@%s = @___tmp;", rubySymbol ), @binding )
|
1153
|
+
eval( sprintf( "@global_lisp_binding['%s'] = true;", rubySymbol ), @binding )
|
1154
|
+
end
|
1155
|
+
|
1156
|
+
def setArgv( argv )
|
1157
|
+
self.global_lisp_define( toRubySymbol( "*argv*"), argv.to_list )
|
1158
|
+
end
|
1159
|
+
|
1031
1160
|
def _gensym( )
|
1032
1161
|
@gensym_counter += 1
|
1033
|
-
|
1162
|
+
filename = if @lastSourcefile.is_a? String
|
1163
|
+
Digest::SHA1.hexdigest( @lastSourcefile )
|
1164
|
+
else
|
1165
|
+
""
|
1166
|
+
end
|
1167
|
+
sprintf( "__gensym__%s_%d", filename, @gensym_counter ).intern
|
1034
1168
|
end
|
1035
1169
|
|
1036
1170
|
def forward_gensym_counter( )
|
@@ -1342,9 +1476,12 @@ class Evaluator
|
|
1342
1476
|
str += "," + genQuote( lastAtom ) if lastAtom
|
1343
1477
|
str += arr.map{ |e| ")" }.join
|
1344
1478
|
end
|
1479
|
+
when Array
|
1480
|
+
arr = sexp.map { |x| genQuote( x ) }
|
1481
|
+
str += "[" + arr.join(",") + "]"
|
1345
1482
|
when Symbol
|
1346
1483
|
str += sprintf( ":\"%s\"", sexp.to_s )
|
1347
|
-
when String
|
1484
|
+
when String, LispString
|
1348
1485
|
str += sprintf( "\"%s\"", LispString.escape( sexp ))
|
1349
1486
|
when LispKeyword
|
1350
1487
|
str += sprintf( "LispKeyword.new( \"%s\" )", sexp.key.to_s )
|
@@ -1419,6 +1556,8 @@ class Evaluator
|
|
1419
1556
|
else
|
1420
1557
|
self.apply( sexp.car, sexp.cdr, sexp.car.sourcefile, sexp.car.lineno, locals )
|
1421
1558
|
end
|
1559
|
+
when Array
|
1560
|
+
raise RuntimeError, "Error: can't eval unquoted vector."
|
1422
1561
|
else
|
1423
1562
|
case sexp
|
1424
1563
|
when Symbol
|
@@ -1427,7 +1566,7 @@ class Evaluator
|
|
1427
1566
|
lispSymbolReference( sym, locals, nil, sexp.sourcefile, sexp.lineno )
|
1428
1567
|
when Fixnum
|
1429
1568
|
sexp.to_s
|
1430
|
-
when String
|
1569
|
+
when String, LispString
|
1431
1570
|
sprintf( "\"%s\"", LispString.escape( sexp ))
|
1432
1571
|
when LispKeyword
|
1433
1572
|
sprintf( "LispKeyword.new( \"%s\" )", sexp.key )
|
@@ -1488,7 +1627,7 @@ class Evaluator
|
|
1488
1627
|
sexp.cdr = Cell.new( letArgumentList( sexp.cdr.car ),
|
1489
1628
|
quoting( sexp.cdr.cdr ))
|
1490
1629
|
when Symbol # named letrec is illegal
|
1491
|
-
raise
|
1630
|
+
raise RuntimeError, "Error: named letrec is not a illegal form"
|
1492
1631
|
end
|
1493
1632
|
sexp
|
1494
1633
|
else
|
@@ -1642,14 +1781,22 @@ class Evaluator
|
|
1642
1781
|
forward_gensym_counter()
|
1643
1782
|
end
|
1644
1783
|
|
1784
|
+
def _load_MIMARKcompiled_MIMARKcode_MIMARKfrom_MIMARKstring( rubyExp )
|
1785
|
+
eval( rubyExp, @binding )
|
1786
|
+
forward_gensym_counter()
|
1787
|
+
end
|
1788
|
+
|
1645
1789
|
def _load_MIMARKcompiled_MIMARKcode( filename )
|
1646
1790
|
open( filename, "r:utf-8" ) { |f|
|
1647
|
-
|
1648
|
-
eval( rubyExp, @binding )
|
1791
|
+
eval( f.read, @binding )
|
1649
1792
|
}
|
1650
1793
|
forward_gensym_counter()
|
1651
1794
|
end
|
1652
1795
|
|
1796
|
+
def _clean_MIMARKcompiled_MIMARKcode
|
1797
|
+
@compiled_code = Hash.new
|
1798
|
+
end
|
1799
|
+
|
1653
1800
|
def _get_MIMARKcompiled_MIMARKcode
|
1654
1801
|
@compiled_code
|
1655
1802
|
ret = Hash.new
|
@@ -1705,6 +1852,13 @@ class Printer
|
|
1705
1852
|
else
|
1706
1853
|
"(" + arr.join( " " ) + (lastAtom ? " . " + lastAtom : "") + ")"
|
1707
1854
|
end
|
1855
|
+
when Array # is a vector in the Nendo world.
|
1856
|
+
arr = sexp.map { |x| __write( x, readable ) }
|
1857
|
+
"#(" + arr.join( " " ) + ")"
|
1858
|
+
when true
|
1859
|
+
"#t"
|
1860
|
+
when false
|
1861
|
+
"#f"
|
1708
1862
|
when Symbol
|
1709
1863
|
keyword = getQuoteKeyword.call( sexp )
|
1710
1864
|
if keyword
|
@@ -1712,7 +1866,7 @@ class Printer
|
|
1712
1866
|
else
|
1713
1867
|
sprintf( "%s", sexp.to_s )
|
1714
1868
|
end
|
1715
|
-
when String
|
1869
|
+
when String, LispString
|
1716
1870
|
if readable
|
1717
1871
|
sprintf( "\"%s\"", LispString.escape( sexp.to_s ))
|
1718
1872
|
else
|
@@ -1763,6 +1917,22 @@ class Nendo
|
|
1763
1917
|
@evaluator._load( path )
|
1764
1918
|
end
|
1765
1919
|
|
1920
|
+
def load_compiled_code( path )
|
1921
|
+
@evaluator._load_MIMARKcompiled_MIMARKcode( path )
|
1922
|
+
end
|
1923
|
+
|
1924
|
+
def load_compiled_code_from_string( rubyExp )
|
1925
|
+
@evaluator._load_MIMARKcompiled_MIMARKcode_MIMARKfrom_MIMARKstring( rubyExp )
|
1926
|
+
end
|
1927
|
+
|
1928
|
+
def setArgv( argv )
|
1929
|
+
@evaluator.setArgv( argv )
|
1930
|
+
end
|
1931
|
+
|
1932
|
+
def clean_compiled_code
|
1933
|
+
@evaluator._clean_MIMARKcompiled_MIMARKcode()
|
1934
|
+
end
|
1935
|
+
|
1766
1936
|
def repl
|
1767
1937
|
printer = Printer.new( @debug_printer )
|
1768
1938
|
reader = nil
|