home *** CD-ROM | disk | FTP | other *** search
Text File | 1990-12-28 | 20.3 KB | 1,107 lines |
- #------
- #-- Strings.
- #------
-
- &SUBR1('string?');
- sub stringP {
- &TYPE(@_[0]) == $T_STRING;
- }
-
- &SUBR('make-string', 1, 2, $T_NUMBER, $T_CHAR);
- sub make_string {
- local(@sip) = @_;
- local($c) = @sip > 1 ? &Cval(@sip[1]) : '.';
- &S($c x &Nval(@sip[0]));
- }
-
- &SUBR1('string-length', $T_STRING);
- sub string_length {
- &N(length(&Sval(@_[0])));
- }
-
- &SUBR2('string-ref', $T_STRING, $T_NUMBER);
- sub string_ref {
- &C(substr(&Sval(@_[0]), &Nval(@_[1]), 1));
- }
-
- &SUBR3('string-set!', $T_STRING, $T_NUMBER, $T_CHAR);
- sub string_setI {
- &Sset(@_[0], &Nval(@_[1]), 1, &Cval(@_[2])); # XXX domain error.
- $TRUE;
- }
-
- &CMP_SUBR('string=?', 'string-eq?', $T_STRING, '&Sval', 'eq');
- &CMP_SUBR('string<?', 'string-lt?', $T_STRING, '&Sval', 'lt');
- &CMP_SUBR('string>?', 'string-gt?', $T_STRING, '&Sval', 'gt');
- &CMP_SUBR('string<=?', 'string-le?', $T_STRING, '&Sval', 'le');
- &CMP_SUBR('string>=?', 'string-ge?', $T_STRING, '&Sval', 'ge');
-
- sub ciSval {
- local($_) = &Sval(@_[0]);
- tr/A-Z/a-z/;
- $_;
- }
- &CMP_SUBR('string-ci=?', 'string-ci-eq?', $T_STRING, '&ciSval', 'eq');
- &CMP_SUBR('string-ci<?', 'string-ci-lt?', $T_STRING, '&ciSval', 'lt');
- &CMP_SUBR('string-ci>?', 'string-ci-gt?', $T_STRING, '&ciSval', 'gt');
- &CMP_SUBR('string-ci<=?', 'string-ci-le?', $T_STRING, '&ciSval', 'le');
- &CMP_SUBR('string-ci>=?', 'string-ci-ge?', $T_STRING, '&ciSval', 'ge');
-
- &SUBR3('substring', $T_STRING, $T_NUMBER, $T_NUMBER);
- sub substring {
- local(@sip) = @_;
- local($p) = &Nval(@sip[1]);
- &S(substr(&Sval(@sip[0]), $p, &Nval(@sip[2]) - $p));
- }
-
- &SUBRN('string-append', $T_STRING);
- sub string_append {
- local(@sip) = @_;
- local($s) = '';
- $s .= &Sval(shift @sip) while @sip;
- &S($s);
- }
-
- &SUBR1('string->list', $T_STRING);
- sub string_2list {
- local(@sip) = @_;
- local($p) = $NIL;
- for $c (reverse split(//, &Sval(@sip[0]))) {
- $p = &P(&C($c), $p);
- }
- $p;
- }
-
- &SUBR1('list->string', $T_LIST);
- sub list_2string {
- local($p) = @_;
- local($s) = '';
- local($a);
- while ($p ne $NIL) { # XXX improper lists.
- ($a, $p) = &Pval($p);
- &CHKtype($a, $T_CHAR, 'list->string');
- $s = $s . &Cval($a);
- }
- &S($s);
- }
-
- &SUBR1('string-copy', $T_STRING);
- sub string_copy {
- &S(&Sval(@_[0]));
- }
-
- &SUBR2('string-fill!', $T_STRING, $T_CHAR);
- sub string_fillI {
- local(@sip) = @_;
- local($s, $c) = @sip;
- local($len) = length(&Sval($s));
- &Sset($s, 0, $len, &Cval($c) x $len);
- $TRUE;
- }
-
- #------
- #-- Vectors.
- #------
-
- &SUBR1('vector?');
- sub vectorP {
- &TYPE(@_[0]) == $T_VECTOR;
- }
-
- &SUBR('make-vector', 1, 2, $T_NUMBER);
- sub make_vector {
- local(@sip) = @_;
- local($n) = &Nval(@sip[0]);
- local($x) = @sip > 1 ? @sip[1] : $FALSE;
- local(@v);
- $#v = $n - 1;
- for $k (@v) { $k = $x; }
- &V(@v);
- }
-
- &DEF('vector', &SUBRN('V'));
-
- &SUBR1('vector-length', $T_VECTOR);
- sub vector_length {
- &N(&Vval(@_[0]) + 0);
- }
-
- &SUBR2('vector-ref', $T_VECTOR, $T_NUMBER);
- sub vector_ref {
- (&Vval(@_[0]))[&Nval(@_[1])];
- }
-
- &SUBR3('vector-set!', $T_VECTOR, $T_NUMBER, $T_ANY);
- sub vector_setI {
- &Vset(@_[0], &Nval(@_[1]), 1, @_[2]);
- }
-
- &SUBR1('vector-copy', $T_VECTOR);
- sub vector_copy {
- &V(&Vval(@_[0]));
- }
-
- &SUBR1('vector->list', $T_VECTOR);
- sub vector_2list {
- &L(&Vval(@_[0]));
- }
-
- &SUBR1('list->vector', $T_LIST);
- sub list_2vector {
- &V(&Lval(@_[0])); # XXX improper lists.
- }
-
- #------
- #-- Tables. (extension)
- #------
-
- &SUBR1('table?');
- sub tableP {
- &TYPE(@_[0]) == $T_TABLE;
- }
-
- &DEF('make-table', &SUBR0('T'));
-
- &SUBR3('table-set!', $T_TABLE, $T_SYMBOL);
- sub table_setI {
- &Tset(@_[0], @_[1], @_[2]);
- $TRUE;
- }
-
- &SUBR2('table-ref', $T_TABLE, $T_SYMBOL);
- sub table_ref {
- &Tval(@_[0], @_[1]);
- }
-
- &SUBR1('table-keys', $T_TABLE);
- sub table_keys {
- local(@v) = &Tkeys(@_[0]);
- for $k (@v) {
- $k = &Y($k);
- }
- &V(@v);
- }
-
- #------
- #-- Syntactic keywords, special forms.
- #------
-
- $ARROW = &Y('=>');
- $ELSE = &Y('else');
- $QUOTE = &Y('quote');
- $QUASIQUOTE = &Y('quasiquote');
- $UNQUOTE = &Y('unquote');
- $UNQUOTE_SPLICING = &Y('unquote-splicing');
-
- &FORM('quote');
- sub quote {
- @_[0];
- }
-
- # XXX wrote quasiquote in a delirium. it may not work correctly.
- &FORM('quasiquote');
- sub quasiquote {
- &QQ(@_[0], 0);
- }
-
- sub QQ {
- local(@sip) = @_;
- local($it, $n) = @sip;
- local($t) = &TYPE($it);
- if ($t == $T_VECTOR) {
- return &QQvector($it, $n);
- } elsif ($t == $T_PAIR) {
- return &QQlist($it, $n);
- } else {
- return $it;
- }
- }
-
- sub QQvector {
- local(@sip) = @_;
- local($it, $n) = @sip;
- return &list_2vector(&QQlist(&vector_2list($it), $n));
- }
-
- sub QQlist {
- local(@sip) = @_;
- local($it, $n) = @sip;
- local($a, $d) = &Pval($it);
- if ($a eq $QUASIQUOTE) {
- return &L($QUASIQUOTE, &QQ(&car($d), $n + 1));
- } elsif ($a eq $UNQUOTE) {
- return $n == 0
- ? &eval(&car($d))
- : &L($UNQUOTE, &QQ(&car($d), $n - 1));
- }
-
- if (&pairP($a) && &car($a) eq $UNQUOTE_SPLICING) {
- $a = ($n == 0)
- ? &eval(&cadr($a))
- : &L($UNQUOTE_SPLICING, &QQ(&cadr($a), $n - 1));
- } else {
- $a = &L(&QQ($a, $n));
- }
- if ($d ne $NIL) {
- return &append($a, &QQ($d, $n));
- } else {
- return $a;
- }
- }
-
- &FORM('delay');
- sub delay {
- &V($PROMISE, $NIL, $NIL, &ENVcurrent(), @_);
- }
-
- &FORM('lambda');
- sub lambda {
- local(@code) = @_;
- local($args) = shift @code;
- local($a, @syms);
- while (&pairP($args)) {
- ($a, $args) = &Pval($args);
- &CHKtype($a, $T_SYMBOL, 'lambda');
- push(@syms, $a);
- }
- &CHKtype($args, $T_SYMBOL, 'lambda') if $args ne $NIL;
- &V($CLOSURE, &ENVcurrent(), $args, &N(@syms + 0), @syms, @code);
- }
-
- # XXX named let form
- &FORM('let');
- sub let {
- local(@code) = @_;
- local(@bindings) = &Lval(shift @code);
- local(@syms, @vals);
- for $x (@bindings) {
- push(@syms, &car($x));
- push(@vals, &eval(&cadr($x)));
- }
- &ENVpush_frame();
- &ENVbind(@syms, @vals);
- local($x) = &begin(@code);
- &ENVpop_frame();
- $x;
- }
-
- &FORM('let*');
- sub letX {
- local(@code) = @_;
- local(@bindings) = &Lval(shift @code);
- local($x);
- &ENVpush(&ENVcurrent());
- for $b (@bindings) {
- $x = &eval(&cadr($b));
- &ENVpush_frame();
- &ENVbind(&car($b), $x);
- }
- $x = &begin(@code);
- &ENVpop();
- $x;
- }
-
- &FORM('letrec');
- sub letrec {
- local(@code) = @_;
- local(@bindings) = &Lval(shift @code);
- local($x, @syms, @vals);
- for $x (@bindings) {
- push(@syms, &car($x));
- }
- &ENVpush_frame();
- &ENVbind(@syms, @syms);
- for $x (@bindings) {
- push(@vals, &eval(&cadr($x)));
- }
- &ENVbind(@syms, @vals);
- local($x) = &begin(@code);
- &ENVpop_frame();
- $x;
- }
-
- &FORM('do');
- sub do {
- local(@code) = @_;
- local($bindings) = shift @code;
- local($y, $v, $n, @syms, @vals, @nexts);
- for $x (&Lval($bindings)) {
- ($y, $v, $n) = &Lval($x);
- if (defined $n) {
- unshift(@syms, $y);
- unshift(@vals, &eval($v));
- unshift(@nexts, $n);
- } else {
- push(@syms, $y);
- push(@vals, &eval($v));
- }
- }
- &ENVpush_frame();
- &ENVbind(@syms, @vals);
-
- $#syms = $#nexts;
-
- local($test, @exit) = &Lval(shift @code);
-
- while (!&eval($test)) {
- &begin(@code);
- } continue {
- @vals = ();
- for $x (@nexts) {
- push(@vals, &eval($x));
- }
- &ENVbind(@syms, @vals);
- }
- local($x) = &begin(@exit);
- &ENVpop_frame();
- $x;
- }
-
- &FORM('set!');
- sub setI {
- &CHKtype(@_[0], $T_SYMBOL, 'set!');
- # XXX argcount, syntax error.
- # XXX error if unbound?
- &ENVset(@_[0], &eval(@_[1]));
- $TRUE;
- }
-
- &FORM('define');
- sub define {
- local(@sip) = @_;
- local($sym) = shift @sip;
- local($t) = &TYPE($sym);
- if ($t == $T_SYMBOL) {
- &ENVbind($sym, &eval(@sip[0]));
- } elsif ($t == $T_PAIR) {
- local($args);
- ($sym, $args) = &Pval($sym);
- &CHKtype($sym, $T_SYMBOL, 'define');
- &ENVbind($sym, &lambda($args, @sip));
- } else {
- &ERRtype($sym, 'a symbol or a pair', 'define');
- }
- $TRUE;
- }
-
- &FORM('begin');
- sub begin {
- local(@sip) = @_;
- local($x) = $NIL;
- $x = &eval(shift @sip) while @sip;
- $x;
- }
-
- &FORM('and');
- sub and {
- local(@sip) = @_;
- local($x) = $TRUE;
- $x = &eval(shift @sip) while $x && @sip;
- $x;
- }
-
- &FORM('or');
- sub or {
- local(@sip) = @_;
- local($x) = $FALSE;
- $x = &eval(shift @sip) while !$x && @sip;
- $x;
- }
-
- &FORM('if');
- sub if {
- # XXX argcount, syntax error.
- if (&eval(@_[0])) {
- &eval(@_[1]);
- } elsif (@_[2] ne '') {
- &eval(@_[2]);
- } else {
- $NIL;
- }
- }
-
- &FORM('cond');
- sub cond {
- local(@sip) = @_;
- local($a, $d, $x);
- for $it (@sip) {
- &CHKtype($it, $T_PAIR, 'cond');
- ($a, $d) = &Pval($it);
- if ($a eq $ELSE || ($x = &eval($a))) {
- &CHKtype($it, $T_PAIR, 'cond');
- local(@v) = &Lval($d);
- if (@v[0] eq $ARROW) {
- # XXX syntax error, @v > 2;
- return &applyN(&eval(@v[1]), $x);
- } else {
- return &begin(@v);
- }
- }
- }
- return $NIL;
- }
-
- &FORM('case');
- sub case {
- local(@sip) = @_;
- local($x) = &eval(shift @sip);
- local($a, $d);
- for $it (@sip) {
- &CHKtype($it, $T_PAIR, 'case');
- ($a, $d) = &Pval($it);
- if ($a eq $ELSE || &memv($x, $a)) { # XXX pair? $a
- &CHKtype($d, $T_PAIR, 'case');
- return &begin(&Lval($d));
- }
- }
- return $NIL;
- }
-
- &FORM('*time-execution');
- sub Xtime_execution {
- local(@code) = @_;
- local($x);
- local($u0, $s0, $cu0, $cs0, $t0);
- local($u1, $s1, $cu1, $cs1, $t1);
- $t0 = time;
- ($u0, $s0, $cu0, $cs0) = times;
- $x = &begin(@code);
- ($u1, $s1, $cu1, $cs1) = times;
- $t1 = time;
- printf $stderr "\ntimes: %.3f user, %.3f system, %d:%02d real.\n",
- $u1 - $u0 + $cu1 - $cu1,
- $s1 - $s0 + $cs1 - $cu1,
- ($t1 - $t0) / 60, ($t1 - $t0) % 60;
- }
-
- #------
- #-- Input and output ports.
- #------
-
- @IPstack = ();
- @OPstack = ();
-
- $IPcurrent = $stdin;
- $OPcurrent = $stdout;
-
- # Restore I/O to a sane state.
- sub IOreset {
- @IPstack = ();
- @OPstack = ();
- $IPcurrent = $stdin;
- $OPcurrent = $stdout;
- select(&OPval($stdout));
- $| = 1;
- }
-
- &SUBR1('input-port?');
- sub input_portP {
- &TYPE(@_[0]) == $T_INPUT;
- }
-
- &SUBR1('output-port?');
- sub output_portP {
- &TYPE(@_[0]) == $T_OUTPUT;
- }
-
- &SUBR0('current-input-port');
- sub current_input_port {
- $IPcurrent;
- }
-
- &SUBR0('current-output-port');
- sub current_output_port {
- $OPcurrent;
- }
-
- &SUBR2('with-input-from-file', $T_STRING, $T_PROCEDURE);
- sub with_input_from_file {
- local(@sip) = @_;
- local($f) = &IP(&Sval(@sip[0]));
- return $NIL if !$f; # XXX open error
-
- push(@IPstack, $IPcurrent);
- $IPcurrent = $f;
- local($x) = &applyN(@sip[1]);
- $IPcurrent = pop @IPstack;
- close(&IPval($f));
- $x;
- }
-
- &SUBR2('with-output-to-file', $T_STRING, $T_PROCEDURE);
- sub with_output_to_file {
- local(@sip) = @_;
- local($f) = &OP(&Sval(@sip[0]));
- return $NIL if !$f; # XXX open error.
-
- push(@OPstack, $OPcurrent);
- $OPcurrent = $f;
- local($x) = &applyN(@sip[1]);
- $OPcurrent = pop @OPstack;
- close(&OPval($f));
- $x;
- }
-
- &SUBR1('open-input-file', $T_STRING);
- sub open_input_file {
- &IP(&Sval(@_[0])); # XXX open error.
- }
-
- &SUBR1('open-output-file', $T_STRING);
- sub open_output_file {
- &OP(&Sval(@_[0])); # XXX open error.
- }
-
- &SUBR1('close-input-port', $T_INPUT);
- sub close_input_port {
- close(&IPval(@_[0])); # XXX should destroy port.
- &IPget(@_[0]); # flush the input buffer.
- $TRUE;
- }
-
- &SUBR1('close-output-port', $T_OUTPUT);
- sub close_output_port {
- close(&OPval(@_[0])); # XXX should destroy port.
- $TRUE;
- }
-
- #------
- #-- Input.
- #------
-
- $EOF = &Y('#EOF'); # eof object.
-
- &SUBR1('eof-object?');
- sub eof_objectP {
- @_[0] eq $EOF;
- }
-
- &SUBR('read-char', 0, 1, $T_INPUT);
- sub read_char {
- local($ip) = @_ ? @_ : $IPcurrent;
- local($_) = &IPget($ip);
- return $EOF if $_ eq '';
- local($c) = substr($_, 0, 1);
- &IPput($ip, substr($_, 1, length - 1));
- &C($c);
- }
-
- &SUBR('char-ready?', 0, 1, $T_INPUT);
- sub char_readyP {
- local($ip) = @_ ? @_ : $IPcurrent;
- $IPbuffer{$ip} ne ''; # XXX shouldn't refer to IPbuffer directly.
- }
-
- &SUBR('read-line', 0, 1, $T_INPUT); # (extension)
- sub read_line {
- local($ip) = @_ ? @_ : $IPcurrent;
- local($_) = &IPget($ip);
- $_ eq '' ? $EOF : &S($_);
- }
-
- &SUBR('read', 0, 1, $T_INPUT);
- sub read {
- local($ip) = @_ ? @_ : $IPcurrent;
- local($_) = &IPgetns($ip);
-
- if ($_ eq '') {
- $EOF;
- } elsif (/^\(/) {
- &IPput($ip, $');
- &L(&RDvec($ip));
- } elsif (/^'/) {
- &IPput($ip, $');
- &P($QUOTE, &P(&read($ip), $NIL));
- } elsif (/^`/) {
- &IPput($ip, $');
- &P($QUASIQUOTE, &P(&read($ip), $NIL));
- } elsif (/^,@/) {
- &IPput($ip, $');
- &P($UNQUOTE_SPLICING, &P(&read($ip), $NIL));
- } elsif (/^,/) {
- &IPput($ip, $');
- &P($UNQUOTE, &P(&read($ip), $NIL));
- } elsif (/^"/) {
- &IPput($ip, $');
- &S(&RDstring($ip));
- } elsif (/^#\(/) {
- &IPput($ip, $');
- &V(&RDvec($ip));
- } elsif (/^(#\\\w\w+)\s*/) {
- local($x) = $1;
- &IPput($ip, $');
- &RDtoken($x);
- } elsif (/^#\\([\0-\377])\s*/) {
- local($c) = $1;
- &IPput($ip, $');
- &C($c);
- } elsif (/^([^()"',\s]+)\s*/) {
- local($x) = $1;
- &IPput($ip, $');
- &RDtoken($x);
- } else {
- &ERR("failure in READ, can't understand $_");
- }
- }
-
- sub RDtoken {
- local($_) = @_;
- $_ =~ tr/A-Z/a-z/;
-
- if (/^\.$/) { '.'; } # read hack.
- elsif (/^#t$/) { $TRUE; }
- elsif (/^#f$/) { $FALSE; }
- elsif (/^#\\space$/) { &C(' '); }
- elsif (/^#\\newline$/) { &C("\n"); }
- elsif (/^#\\tab$/) { &C("\t"); }
-
- elsif (/^#/) {
- &ERR("read, bad token $_");
- } elsif (/^[-+]?(\d+\.?\d*|\d*\.\d+)(e[-+]?\d+)?$/) {
- &N($_ + 0);
- } elsif (/^[-+]?(\d+)\/(\d+)$/) {
- &N($1 / $2);
- } else {
- &Y($_);
- }
- }
-
- sub RDvec {
- local($ip) = @_;
- local($_, @v);
- while (($_ = &IPgetns($ip)) ne '') {
- &IPput($ip, $'), last if /^\)\s*/;
- &IPput($ip, $_);
- push(@v, &read($ip));
- }
- if ($_ eq '') {
- &ERR("EOF while reading list or vector.");
- }
- return @v;
- }
-
- sub RDstring {
- local($ip) = @_;
- local($s) = "";
- $_ = &IPget($ip);
- while ($_ ne '') {
- &IPput($ip, $'), last if /^"\s*/;
- if (/^\\([\0-\377])/) {
- $s .= $1; $_ = $';
- } elsif (/^[^"\\]+/) {
- $s .= $&; $_ = $';
- } else {
- $s .= $_; $_ = '';
- }
- $_ = &IPget($ip) if $_ eq '';
- }
- return $s;
- }
-
- #------
- #-- Output.
- #------
-
- &SUBR('newline', 0, 1, $T_OUTPUT);
- sub newline {
- &OPput(@_ ? @_[0] : $OPcurrent, "\n");
- }
-
- &SUBR('write-char', 1, 2, $T_CHAR, $T_OUTPUT);
- sub write_char {
- &OPput(@_ > 1 ? @_[1] : $OPcurrent, &Cval(@_[0]));
- }
-
- $WRquoted = 0;
- %WRmark = ();
-
- &SUBR('write', 1, 2, $T_ANY, $T_OUTPUT);
- sub write {
- $WRquoted = 1;
- &WR(@_);
- }
-
- &SUBR('display', 1, 2, $T_ANY, $T_OUTPUT);
- sub display {
- $WRquoted = 0;
- &WR(@_);
- }
- sub WR {
- local(@sip) = @_;
- local($fh) = &OPval(@_ > 1 ? @_[1] : $OPcurrent);
- local($oldfh) = select($fh);
- %WRmark = ();
- &WR1(@_[0]);
- select($oldfh);
- $TRUE;
- }
-
- sub WR1 {
- local($it) = @_;
- local($t) = &TYPE($it);
- if ($t == $T_NIL) { print '()'; }
- elsif ($t == $T_BOOLEAN){ print $it ? '#t' : '#f'; }
- elsif ($t == $T_NUMBER) { print &Nval($it); }
- elsif ($t == $T_CHAR) { &WRchar($it); }
- elsif ($t == $T_SYMBOL) { print &Yname($it); }
- elsif ($t == $T_STRING) { &WRstring($it); }
- elsif ($t == $T_VECTOR) { &WRvector($it); }
- elsif ($t == $T_TABLE) { &WRtable($it); }
- elsif ($t == $T_PAIR) { &WRlist($it); }
-
- elsif ($t == $T_INPUT) {
- print '#<input port ', &IPval($it), '>';
- } elsif ($t == $T_OUTPUT) {
- print '#<output port ', &OPval($it), '>';
- } elsif ($t == $T_SUBR) {
- print '#<built-in ', (&SUBRval($it))[0], '>';
- } elsif ($t == $T_FORM) {
- print '#<keyword ', (&FORMval($it))[0], '>';
- } else {
- print "#<strange object: $it>";
- }
- }
-
- sub WRstring {
- local($s) = &Sval(@_[0]);
- if (!$WRquoted) {
- print $s;
- } else {
- $s =~ s/\\/\\\\/g;
- $s =~ s/"/\\"/g;
- print '"', $s, '"';
- }
- }
-
- sub WRchar {
- local($c) = &Cval(@_[0]);
- if (!$WRquoted) { print $c; }
- elsif ($c eq ' ') { print '#\space'; }
- elsif ($c eq "\n") { print '#\newline'; }
- elsif ($c eq "\t") { print '#\tab'; }
- else { print "#\\$c"; }
- }
-
- # XXX Can't read a written table.
- sub WRtable {
- local($it) = @_;
- return print '{...}' if $WRmark{$it};
- $WRmark{$it} += 3; # strong bias against printing tables again.
-
- print '{';
- local(@keys) = &Tkeys($it);
- if (@keys) {
- local($k) = pop @keys;
- print $k, ' => ';
- &WR1(&Tval($it, &Y($k)));
- }
- for $k (@keys) {
- print ', ', $k, ' => ';
- &WR1(&Tval($it, &Y($k)));
- }
- print '}';
-
- $WRmark{$it} -= 3;
- }
-
- sub WRvector {
- local($it) = @_;
- return print '#(...)' if $WRmark{$it};
- ++$WRmark{$it};
-
- local(@v) = &Vval($it);
- print '#(';
- &WR1(shift @v) if @v;
- while (@v) {
- print ' ';
- &WR1(shift @v);
- }
- print ')';
-
- --$WRmark{$it};
- }
-
- sub WRlist {
- local($it) = @_;
- return print '(...)' if $WRmark{$it};
- local(%save) = %WRmark;
- ++$WRmark{$it};
-
- local($a, $d) = &Pval($it);
- print "(";
- &WR1($a);
- while ($d ne $NIL) {
- if ($WRmark{$d}) {
- print ' ...';
- last;
- } elsif (&TYPE($d) != $T_PAIR) {
- print ' . ';
- &WR1($d);
- last;
- } else {
- ++$WRmark{$d};
- ($a, $d) = &Pval($d);
- print ' ';
- &WR1($a);
- }
- }
- print ')';
-
- %WRmark = %save;
- }
-
- #------
- #-- Control features.
- #------
-
- # XXX SUBR call-with-current-continuation
-
- &SUBR1('procedure?');
- sub procedureP {
- local($it) = @_;
- local($t) = &TYPE($it);
- $t == $T_SUBR ||
- ($t == $T_VECTOR && (&Vval($it))[0] eq $CLOSURE);
- }
-
- &SUBR1('force');
- sub force {
- &ERRtype(@_[0], 'a promise', 'force') if &TYPE(@_[0]) ne $T_VECTOR;
- local($thunk) = @_;
- local($k, $forced, $val, $env, @code) = &Vval($thunk);
- &ERRtype($thunk, 'a promise', 'force') if $k ne $PROMISE;
- if (!$forced) {
- &ENVpush($env);
- $val = &begin(@code);
- &ENVpop();
- &Vset($thunk, 1, 2, $TRUE, $val);
- }
- $val;
- }
-
- &SUBRN('apply');
- sub apply {
- local(@sip) = @_;
- local($f, @args) = @_;
- &CHKtype(@args[$#args], $T_LIST, 'apply');
- push(@args, &Lval(pop @args));
- &applyN($f, @args);
- }
-
- sub applyN {
- local(@args) = @_;
- local($f) = shift @args;
- local($t) = &TYPE($f);
-
- if ($t == $T_SUBR) {
- local($f, $min, $max, @t) = &SUBRval($f);
- if (@args < $min) {
- &ERR("Error, $f needs at least $min arguments.");
- } elsif ($max >= 0 && @args > $max) {
- &ERR("Error, $f wants at most $max arguments.");
- }
- if ($max < 0 && @t[0]) {
- for $x (@args) {
- &CHKtype($x, @t[0], $f);
- }
- } elsif (@t) {
- local($k) = $#t < $#args ? $#t : $#args;
- for (; $k >= 0; --$k) {
- &CHKtype(@args[$k], @t[$k], $f);
- }
- }
- return do $f (@args);
-
- } elsif ($t == $T_VECTOR) {
- local($k, $env, $nsym, $n, @code) = &Vval($f);
- &ERRtype($f, $T_PROCEDURE, 'applyN') if $k ne $CLOSURE;
- $n = &Nval($n);
- if (@args < $n) {
- &ERR('not enough args to procedure.');
- } elsif (@args > $n && $nsym eq $NIL) {
- &ERR('too many args to procedure.');
- }
- &ENVpush($env);
- &ENVpush_frame();
- if ($n > 0) {
- &ENVbind(splice(@code, 0, $n), splice(@args, 0, $n));
- }
- if ($nsym ne $NIL) {
- &ENVbind($nsym, &L(@args));
- }
- local($x) = &begin(@code);
- &ENVpop();
- return $x;
-
- } else {
- &ERRtype($f, $T_PROCEDURE, 'applyN');
- }
- }
-
- &SUBRN('map');
- sub map {
- local(@lists) = @_;
- local($f) = &eval(shift @lists);
- local(@result, @args, $a);
- &CHKtype($f, $T_PROCEDURE, 'map');
- # XXX CHKtype lists. and all lists must be same length.
- while (@lists[0] ne $NIL) {
- @args = ();
- for $x (@lists) {
- ($a, $x) = &Pval($x);
- push(@args, $a);
- }
- push(@result, &applyN($f, @args));
- }
- &L(@result);
- }
-
- &SUBRN('for-each');
- sub for_each {
- local(@lists) = @_;
- local($f) = &eval(shift @lists);
- local(@args, $a);
- &CHKtype($f, $T_PROCEDURE, 'for-each');
- # XXX CHKtype lists. and all lists must be same length.
- while (@lists[0] ne $NIL) {
- @args = ();
- for $x (@lists) {
- ($a, $x) = &Pval($x);
- push(@args, $a);
- }
- &applyN($f, @args);
- }
- $TRUE;
- }
-
-
- sub eval {
- local($it) = @_;
- local($t) = &TYPE($it);
-
- if ($t == $T_SYMBOL) {
- return &ENVval($it);
- } elsif ($t != $T_PAIR) {
- return $it;
- }
-
- local($f, $args) = &Pval($it);
-
- $t = &TYPE($f);
- if ($t == $T_SYMBOL) {
- $f = &ENVval($f);
- $t = &TYPE($f);
- } elsif ($t == $T_PAIR) {
- $f = &eval($f);
- $t = &TYPE($f);
- }
-
- if ($t == $T_FORM) {
- $f = &FORMval($f);
- return do $f (&Lval($args));
- }
-
- if ($t != $T_SUBR && $t != $T_VECTOR) {
- &ERRtype(&car(@_[0]), $T_PROCEDURE, 'eval');
- }
-
- local(@args) = &Lval($args);
- for $a (@args) { $a = &eval($a); }
- &applyN($f, @args);
- }
-
- #------
- #-- User interface.
- #------
-
- &SUBR1('load', $T_STRING);
- sub load {
- local($f) = &Sval(@_[0]);
- local($ip) = &IP($f . '.sp') || &IP($f) ||
- &ERR("load, neither $f nor $f.sp found.");
-
- print $stderr "Loading $f...\n";
-
- local($x, $y);
- while (($x = &read($ip)) ne $EOF) {
- $y = &eval($x);
- }
- close(&IPval($ip));
-
- $y;
- }
-
- # XXX SUBR transcript-on, transcript-off
-
- &SUBR('exit', 0, 1, $T_NUMBER);
- sub exit {
- local($x) = @_ ? &Nval(@_[0]) : 0;
- &DB'prof_dump if defined &DB'prof_dump;
- exit $x;
- }
-
- &SUBR0('sp-version');
- sub sp_version {
- &N($version);
- }
-
- sub repl {
- local($x);
- while {
- print "> ";
- $x = &read();
- $x ne $EOF;
- } {
- $x = &eval($x);
- print "\n";
- &write($x);
- print "\n";
- }
- }
-
- #------
- #-- Main program.
- #------
-
- sub catch_interrupt {
- print $stderr "Interrupt\n";
- goto TOP; # Not quite a safe thing to do.
- }
-
- $# = '%.15g'; # the default, %.20g, is a little too many digits.
-
- INIT:;
-
- &IOinit();
-
- $TOPjmp = 0;
-
- TOP:;
-
- &IOreset();
- &ENVreset();
-
- if ($TOPjmp) {
- print $stderr "\nContinuing from top...\n";
- } else {
- $TOPjmp = 1;
- print $stderr "Scheme in Perl? (sp?)\n";
- print $stderr " version $version\n";
- }
-
- if (! @ARGV) {
- $SIG{'INT'} = 'catch_interrupt';
- &repl();
- } else {
- $dodump = (@ARGV[0] eq '-D') && shift @ARGV;
- for $x (@ARGV) {
- &load(&S($x));
- }
- if ($dodump) {
- &IOshutdown();
- dump INIT;
- }
- }
-
- &exit();
-