nendo 0.3.1 → 0.3.2
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/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
|