Scheme in Perl? (sp?): The Code. Part 2 of 2.
Felix Lee
flee at guardian.cs.psu.edu
Mon Nov 19 18:55:59 AEST 1990
Just what you've all been waiting for, a Scheme interpreter written in
Perl. See the Blurb, in a separate article (in comp.lang.perl).
After unpacking parts 1 and 2, you should
cat sp.pl.part1 sp.pl.part2 > sp.pl
--
Felix Lee flee at cs.psu.edu
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of shell archive."
# Contents: sp.pl.part2
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'sp.pl.part2' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'sp.pl.part2'\"
else
echo shar: Extracting \"'sp.pl.part2'\" \(20760 characters\)
sed "s/^X//" >'sp.pl.part2' <<'END_OF_FILE'
X#------
X#-- Strings.
X#------
X
X&SUBR1('string?');
Xsub stringP {
X &TYPE(@_[0]) == $T_STRING;
X}
X
X&SUBR('make-string', 1, 2, $T_NUMBER, $T_CHAR);
Xsub make_string {
X local(@sip) = @_;
X local($c) = @sip > 1 ? &Cval(@sip[1]) : '.';
X &S($c x &Nval(@sip[0]));
X}
X
X&SUBR1('string-length', $T_STRING);
Xsub string_length {
X &N(length(&Sval(@_[0])));
X}
X
X&SUBR2('string-ref', $T_STRING, $T_NUMBER);
Xsub string_ref {
X &C(substr(&Sval(@_[0]), &Nval(@_[1]), 1));
X}
X
X&SUBR3('string-set!', $T_STRING, $T_NUMBER, $T_CHAR);
Xsub string_setI {
X &Sset(@_[0], &Nval(@_[1]), 1, &Cval(@_[2])); # XXX domain error.
X $TRUE;
X}
X
X&CMP_SUBR('string=?', 'string-eq?', $T_STRING, '&Sval', 'eq');
X&CMP_SUBR('string<?', 'string-lt?', $T_STRING, '&Sval', 'lt');
X&CMP_SUBR('string>?', 'string-gt?', $T_STRING, '&Sval', 'gt');
X&CMP_SUBR('string<=?', 'string-le?', $T_STRING, '&Sval', 'le');
X&CMP_SUBR('string>=?', 'string-ge?', $T_STRING, '&Sval', 'ge');
X
Xsub ciSval {
X local($_) = &Sval(@_[0]);
X tr/A-Z/a-z/;
X $_;
X}
X&CMP_SUBR('string-ci=?', 'string-ci-eq?', $T_STRING, '&ciSval', 'eq');
X&CMP_SUBR('string-ci<?', 'string-ci-lt?', $T_STRING, '&ciSval', 'lt');
X&CMP_SUBR('string-ci>?', 'string-ci-gt?', $T_STRING, '&ciSval', 'gt');
X&CMP_SUBR('string-ci<=?', 'string-ci-le?', $T_STRING, '&ciSval', 'le');
X&CMP_SUBR('string-ci>=?', 'string-ci-ge?', $T_STRING, '&ciSval', 'ge');
X
X&SUBR3('substring', $T_STRING, $T_NUMBER, $T_NUMBER);
Xsub substring {
X local(@sip) = @_;
X local($p) = &Nval(@sip[1]);
X &S(substr(&Sval(@sip[0]), $p, &Nval(@sip[2]) - $p));
X}
X
X&SUBRN('string-append', $T_STRING);
Xsub string_append {
X local(@sip) = @_;
X local($s) = '';
X $s .= &Sval(shift @sip) while @sip;
X &S($s);
X}
X
X&SUBR1('string->list', $T_STRING);
Xsub string_2list {
X local(@sip) = @_;
X local($p) = $NIL;
X for $c (reverse split(//, &Sval(@sip[0]))) {
X $p = &P(&C($c), $p);
X }
X $p;
X}
X
X&SUBR1('list->string', $T_LIST);
Xsub list_2string {
X local($p) = @_;
X local($s) = '';
X local($a);
X while ($p ne $NIL) { # XXX improper lists.
X ($a, $p) = &Pval($p);
X &CHKtype($a, $T_CHAR, 'list->string');
X $s = $s . &Cval($a);
X }
X &S($s);
X}
X
X&SUBR1('string-copy', $T_STRING);
Xsub string_copy {
X &S(&Sval(@_[0]));
X}
X
X&SUBR2('string-fill!', $T_STRING, $T_CHAR);
Xsub string_fillI {
X local(@sip) = @_;
X local($s, $c) = @sip;
X local($len) = length(&Sval($s));
X &Sset($s, 0, $len, &Cval($c) x $len);
X $TRUE;
X}
X
X#------
X#-- Vectors.
X#------
X
X&SUBR1('vector?');
Xsub vectorP {
X &TYPE(@_[0]) == $T_VECTOR;
X}
X
X&SUBR('make-vector', 1, 2, $T_NUMBER);
Xsub make_vector {
X local(@sip) = @_;
X local($n) = &Nval(@sip[0]);
X local($x) = @sip > 1 ? @sip[1] : $FALSE;
X local(@v);
X $#v = $n - 1;
X for $k (@v) { $k = $x; }
X &V(@v);
X}
X
X&DEF('vector', &SUBRN('V'));
X
X&SUBR1('vector-length', $T_VECTOR);
Xsub vector_length {
X &N(&Vval(@_[0]) + 0);
X}
X
X&SUBR2('vector-ref', $T_VECTOR, $T_NUMBER);
Xsub vector_ref {
X (&Vval(@_[0]))[&Nval(@_[1])];
X}
X
X&SUBR3('vector-set!', $T_VECTOR, $T_NUMBER, $T_ANY);
Xsub vector_setI {
X &Vset(@_[0], &Nval(@_[1]), 1, @_[2]);
X}
X
X&SUBR1('vector-copy', $T_VECTOR);
Xsub vector_copy {
X &V(&Vval(@_[0]));
X}
X
X&SUBR1('vector->list', $T_VECTOR);
Xsub vector_2list {
X &L(&Vval(@_[0]));
X}
X
X&SUBR1('list->vector', $T_LIST);
Xsub list_2vector {
X &V(&Lval(@_[0])); # XXX improper lists.
X}
X
X#------
X#-- Tables. (extension)
X#------
X
X&SUBR1('table?');
Xsub tableP {
X &TYPE(@_[0]) == $T_TABLE;
X}
X
X&DEF('make-table', &SUBR0('T'));
X
X&SUBR3('table-set!', $T_TABLE, $T_SYMBOL);
Xsub table_setI {
X &Tset(@_[0], @_[1], @_[2]);
X $TRUE;
X}
X
X&SUBR2('table-ref', $T_TABLE, $T_SYMBOL);
Xsub table_ref {
X &Tval(@_[0], @_[1]);
X}
X
X&SUBR1('table-keys', $T_TABLE);
Xsub table_keys {
X local(@v) = &Tkeys(@_[0]);
X for $k (@v) {
X $k = &Y($k);
X }
X &V(@v);
X}
X
X#------
X#-- Syntactic keywords, special forms.
X#------
X
X$ARROW = &Y('=>');
X$ELSE = &Y('else');
X$QUOTE = &Y('quote');
X$QUASIQUOTE = &Y('quasiquote');
X$UNQUOTE = &Y('unquote');
X$UNQUOTE_SPLICING = &Y('unquote-splicing');
X
X&FORM('quote');
Xsub quote {
X @_[0];
X}
X
X# XXX wrote quasiquote in a delirium. it may not work correctly.
X&FORM('quasiquote');
Xsub quasiquote {
X &QQ(@_[0], 0);
X}
X
Xsub QQ {
X local(@sip) = @_;
X local($it, $n) = @sip;
X local($t) = &TYPE($it);
X if ($t == $T_VECTOR) {
X return &QQvector($it, $n);
X } elsif ($t == $T_PAIR) {
X return &QQlist($it, $n);
X } else {
X return $it;
X }
X}
X
Xsub QQvector {
X local(@sip) = @_;
X local($it, $n) = @sip;
X return &list_2vector(&QQlist(&vector_2list($it), $n));
X}
X
Xsub QQlist {
X local(@sip) = @_;
X local($it, $n) = @sip;
X local($a, $d) = &Pval($it);
X if ($a eq $QUASIQUOTE) {
X return &L($QUASIQUOTE, &QQ(&car($d), $n + 1));
X } elsif ($a eq $UNQUOTE) {
X return $n == 0
X ? &eval(&car($d))
X : &L($UNQUOTE, &QQ(&car($d), $n - 1));
X }
X
X if (&pairP($a) && &car($a) eq $UNQUOTE_SPLICING) {
X $a = ($n == 0)
X ? &eval(&cadr($a))
X : &L($UNQUOTE_SPLICING, &QQ(&cadr($a), $n - 1));
X } else {
X $a = &L(&QQ($a, $n));
X }
X if ($d ne $NIL) {
X return &append($a, &QQ($d, $n));
X } else {
X return $a;
X }
X}
X
X&FORM('delay');
Xsub delay {
X &V($PROMISE, $NIL, $NIL, &ENVcurrent(), @_);
X}
X
X&FORM('lambda');
Xsub lambda {
X local(@code) = @_;
X local($args) = shift @code;
X local($a, @syms);
X while (&pairP($args)) {
X ($a, $args) = &Pval($args);
X &CHKtype($a, $T_SYMBOL, 'lambda');
X push(@syms, $a);
X }
X &CHKtype($args, $T_SYMBOL, 'lambda') if $args ne $NIL;
X &V($CLOSURE, &ENVcurrent(), $args, &N(@syms + 0), @syms, @code);
X}
X
X# XXX named let form
X&FORM('let');
Xsub let {
X local(@code) = @_;
X local(@bindings) = &Lval(shift @code);
X local(@syms, @vals);
X for $x (@bindings) {
X push(@syms, &car($x));
X push(@vals, &eval(&cadr($x)));
X }
X &ENVpush_frame();
X &ENVbind(@syms, @vals);
X local($x) = &begin(@code);
X &ENVpop_frame();
X $x;
X}
X
X&FORM('let*');
Xsub letX {
X local(@code) = @_;
X local(@bindings) = &Lval(shift @code);
X local($x);
X &ENVpush(&ENVcurrent());
X for $b (@bindings) {
X $x = &eval(&cadr($b));
X &ENVpush_frame();
X &ENVbind(&car($b), $x);
X }
X $x = &begin(@code);
X &ENVpop();
X $x;
X}
X
X&FORM('letrec');
Xsub letrec {
X local(@code) = @_;
X local(@bindings) = &Lval(shift @code);
X local($x, @syms, @vals);
X for $x (@bindings) {
X push(@syms, &car($x));
X }
X &ENVpush_frame();
X &ENVbind(@syms, @syms);
X for $x (@bindings) {
X push(@vals, &eval(&cadr($x)));
X }
X &ENVbind(@syms, @vals);
X local($x) = &begin(@code);
X &ENVpop_frame();
X $x;
X}
X
X&FORM('do');
Xsub do {
X local(@code) = @_;
X local($bindings) = shift @code;
X local($y, $v, $n, @syms, @vals, @nexts);
X for $x (&Lval($bindings)) {
X ($y, $v, $n) = &Lval($x);
X if (defined $n) {
X unshift(@syms, $y);
X unshift(@vals, &eval($v));
X unshift(@nexts, $n);
X } else {
X push(@syms, $y);
X push(@vals, &eval($v));
X }
X }
X &ENVpush_frame();
X &ENVbind(@syms, @vals);
X
X $#syms = $#nexts;
X
X local($test, @exit) = &Lval(shift @code);
X
X while (!&eval($test)) {
X &begin(@code);
X } continue {
X @vals = ();
X for $x (@nexts) {
X push(@vals, &eval($x));
X }
X &ENVbind(@syms, @vals);
X }
X local($x) = &begin(@exit);
X &ENVpop_frame();
X $x;
X}
X
X&FORM('set!');
Xsub setI {
X &CHKtype(@_[0], $T_SYMBOL, 'set!');
X # XXX argcount, syntax error.
X # XXX error if unbound?
X &ENVset(@_[0], &eval(@_[1]));
X $TRUE;
X}
X
X&FORM('define');
Xsub define {
X local(@sip) = @_;
X local($sym) = shift @sip;
X local($t) = &TYPE($sym);
X if ($t == $T_SYMBOL) {
X &ENVbind($sym, &eval(@sip[0]));
X } elsif ($t == $T_PAIR) {
X local($args);
X ($sym, $args) = &Pval($sym);
X &CHKtype($sym, $T_SYMBOL, 'define');
X &ENVbind($sym, &lambda($args, @sip));
X } else {
X &ERRtype($sym, 'a symbol or a pair', 'define');
X }
X $TRUE;
X}
X
X&FORM('begin');
Xsub begin {
X local(@sip) = @_;
X local($x) = $NIL;
X $x = &eval(shift @sip) while @sip;
X $x;
X}
X
X&FORM('and');
Xsub and {
X local(@sip) = @_;
X local($x) = $TRUE;
X $x = &eval(shift @sip) while $x && @sip;
X $x;
X}
X
X&FORM('or');
Xsub or {
X local(@sip) = @_;
X local($x) = $FALSE;
X $x = &eval(shift @sip) while !$x && @sip;
X $x;
X}
X
X&FORM('if');
Xsub if {
X # XXX argcount, syntax error.
X if (&eval(@_[0])) {
X &eval(@_[1]);
X } elsif (@_[2] ne '') {
X &eval(@_[2]);
X } else {
X $NIL;
X }
X}
X
X&FORM('cond');
Xsub cond {
X local(@sip) = @_;
X local($a, $d, $x);
X for $it (@sip) {
X &CHKtype($it, $T_PAIR, 'cond');
X ($a, $d) = &Pval($it);
X if ($a eq $ELSE || ($x = &eval($a))) {
X &CHKtype($it, $T_PAIR, 'cond');
X local(@v) = &Lval($d);
X if (@v[0] eq $ARROW) {
X # XXX syntax error, @v > 2;
X return &applyN(&eval(@v[1]), $x);
X } else {
X return &begin(@v);
X }
X }
X }
X return $NIL;
X}
X
X&FORM('case');
Xsub case {
X local(@sip) = @_;
X local($x) = &eval(shift @sip);
X local($a, $d);
X for $it (@sip) {
X &CHKtype($it, $T_PAIR, 'case');
X ($a, $d) = &Pval($it);
X if ($a eq $ELSE || &memv($x, $a)) { # XXX pair? $a
X &CHKtype($d, $T_PAIR, 'case');
X return &begin(&Lval($d));
X }
X }
X return $NIL;
X}
X
X&FORM('*time-execution');
Xsub Xtime_execution {
X local(@code) = @_;
X local($x);
X local($u0, $s0, $cu0, $cs0, $t0);
X local($u1, $s1, $cu1, $cs1, $t1);
X $t0 = time;
X ($u0, $s0, $cu0, $cs0) = times;
X $x = &begin(@code);
X ($u1, $s1, $cu1, $cs1) = times;
X $t1 = time;
X printf $stderr "\ntimes: %.3f user, %.3f system, %d:%02d real.\n",
X $u1 - $u0 + $cu1 - $cu1,
X $s1 - $s0 + $cs1 - $cu1,
X ($t1 - $t0) / 60, ($t1 - $t0) % 60;
X}
X
X#------
X#-- Input and output ports.
X#------
X
X at IPstack = ();
X at OPstack = ();
X
X$IPcurrent = $stdin;
X$OPcurrent = $stdout;
X
X# Restore I/O to a sane state.
Xsub IOreset {
X @IPstack = ();
X @OPstack = ();
X $IPcurrent = $stdin;
X $OPcurrent = $stdout;
X select(&OPval($stdout));
X $| = 1;
X}
X
X&SUBR1('input-port?');
Xsub input_portP {
X &TYPE(@_[0]) == $T_INPUT;
X}
X
X&SUBR1('output-port?');
Xsub output_portP {
X &TYPE(@_[0]) == $T_OUTPUT;
X}
X
X&SUBR0('current-input-port');
Xsub current_input_port {
X $IPcurrent;
X}
X
X&SUBR0('current-output-port');
Xsub current_output_port {
X $OPcurrent;
X}
X
X&SUBR2('with-input-from-file', $T_STRING, $T_PROCEDURE);
Xsub with_input_from_file {
X local(@sip) = @_;
X local($f) = &IP(&Sval(@sip[0]));
X return $NIL if !$f; # XXX open error
X
X push(@IPstack, $IPcurrent);
X $IPcurrent = $f;
X local($x) = &applyN(@sip[1]);
X $IPcurrent = pop @IPstack;
X close(&IPval($f));
X $x;
X}
X
X&SUBR2('with-output-to-file', $T_STRING, $T_PROCEDURE);
Xsub with_output_to_file {
X local(@sip) = @_;
X local($f) = &OP(&Sval(@sip[0]));
X return $NIL if !$f; # XXX open error.
X
X push(@OPstack, $OPcurrent);
X $OPcurrent = $f;
X local($x) = &applyN(@sip[1]);
X $OPcurrent = pop @OPstack;
X close(&OPval($f));
X $x;
X}
X
X&SUBR1('open-input-file', $T_STRING);
Xsub open_input_file {
X &IP(&Sval(@_[0])); # XXX open error.
X}
X
X&SUBR1('open-output-file', $T_STRING);
Xsub open_output_file {
X &OP(&Sval(@_[0])); # XXX open error.
X}
X
X&SUBR1('close-input-port', $T_INPUT);
Xsub close_input_port {
X close(&IPval(@_[0])); # XXX should destroy port.
X &IPget(@_[0]); # flush the input buffer.
X $TRUE;
X}
X
X&SUBR1('close-output-port', $T_OUTPUT);
Xsub close_output_port {
X close(&OPval(@_[0])); # XXX should destroy port.
X $TRUE;
X}
X
X#------
X#-- Input.
X#------
X
X$EOF = &Y('#EOF'); # eof object.
X
X&SUBR1('eof-object?');
Xsub eof_objectP {
X @_[0] eq $EOF;
X}
X
X&SUBR('read-char', 0, 1, $T_INPUT);
Xsub read_char {
X local($ip) = @_ ? @_ : $IPcurrent;
X local($_) = &IPget($ip);
X return $EOF if $_ eq '';
X local($c) = substr($_, 0, 1);
X &IPput($ip, substr($_, 1, length - 1));
X &C($c);
X}
X
X&SUBR('char-ready?', 0, 1, $T_INPUT);
Xsub char_readyP {
X local($ip) = @_ ? @_ : $IPcurrent;
X $IPbuffer{$ip} ne ''; # XXX shouldn't refer to IPbuffer directly.
X}
X
X&SUBR('read-line', 0, 1, $T_INPUT); # (extension)
Xsub read_line {
X local($ip) = @_ ? @_ : $IPcurrent;
X local($_) = &IPget($ip);
X $_ eq '' ? $EOF : &S($_);
X}
X
X&SUBR('read', 0, 1, $T_INPUT);
Xsub read {
X local($ip) = @_ ? @_ : $IPcurrent;
X local($_) = &IPgetns($ip);
X
X if ($_ eq '') {
X $EOF;
X } elsif (/^\(/) {
X &IPput($ip, $');
X &L(&RDvec($ip));
X } elsif (/^'/) {
X &IPput($ip, $');
X &P($QUOTE, &P(&read($ip), $NIL));
X } elsif (/^`/) {
X &IPput($ip, $');
X &P($QUASIQUOTE, &P(&read($ip), $NIL));
X } elsif (/^,@/) {
X &IPput($ip, $');
X &P($UNQUOTE_SPLICING, &P(&read($ip), $NIL));
X } elsif (/^,/) {
X &IPput($ip, $');
X &P($UNQUOTE, &P(&read($ip), $NIL));
X } elsif (/^"/) {
X &IPput($ip, $');
X &S(&RDstring($ip));
X } elsif (/^#\(/) {
X &IPput($ip, $');
X &V(&RDvec($ip));
X } elsif (/^(#\\\w\w+)\s*/) {
X local($x) = $1;
X &IPput($ip, $');
X &RDtoken($x);
X } elsif (/^#\\([\0-\377])\s*/) {
X local($c) = $1;
X &IPput($ip, $');
X &C($c);
X } elsif (/^([^()"',\s]+)\s*/) {
X local($x) = $1;
X &IPput($ip, $');
X &RDtoken($x);
X } else {
X &ERR("failure in READ, can't understand $_");
X }
X}
X
Xsub RDtoken {
X local($_) = @_;
X $_ =~ tr/A-Z/a-z/;
X
X if (/^\.$/) { '.'; } # read hack.
X elsif (/^#t$/) { $TRUE; }
X elsif (/^#f$/) { $FALSE; }
X elsif (/^#\\space$/) { &C(' '); }
X elsif (/^#\\newline$/) { &C("\n"); }
X elsif (/^#\\tab$/) { &C("\t"); }
X
X elsif (/^#/) {
X &ERR("read, bad token $_");
X } elsif (/^[-+]?(\d+\.?\d*|\d*\.\d+)(e[-+]?\d+)?$/) {
X &N($_ + 0);
X } elsif (/^[-+]?(\d+)\/(\d+)$/) {
X &N($1 / $2);
X } else {
X &Y($_);
X }
X}
X
Xsub RDvec {
X local($ip) = @_;
X local($_, @v);
X while (($_ = &IPgetns($ip)) ne '') {
X &IPput($ip, $'), last if /^\)\s*/;
X &IPput($ip, $_);
X push(@v, &read($ip));
X }
X if ($_ eq '') {
X &ERR("EOF while reading list or vector.");
X }
X return @v;
X}
X
Xsub RDstring {
X local($ip) = @_;
X local($s) = "";
X $_ = &IPget($ip);
X while ($_ ne '') {
X &IPput($ip, $'), last if /^"\s*/;
X if (/^\\([\0-\377])/) {
X $s .= $1; $_ = $';
X } elsif (/^[^"\\]+/) {
X $s .= $&; $_ = $';
X } else {
X $s .= $_; $_ = '';
X }
X $_ = &IPget($ip) if $_ eq '';
X }
X return $s;
X}
X
X#------
X#-- Output.
X#------
X
X&SUBR('newline', 0, 1, $T_OUTPUT);
Xsub newline {
X &OPput(@_ ? @_[0] : $OPcurrent, "\n");
X}
X
X&SUBR('write-char', 1, 2, $T_CHAR, $T_OUTPUT);
Xsub write_char {
X &OPput(@_ > 1 ? @_[1] : $OPcurrent, &Cval(@_[0]));
X}
X
X$WRquoted = 0;
X%WRmark = ();
X
X&SUBR('write', 1, 2, $T_ANY, $T_OUTPUT);
Xsub write {
X $WRquoted = 1;
X &WR(@_);
X}
X
X&SUBR('display', 1, 2, $T_ANY, $T_OUTPUT);
Xsub display {
X $WRquoted = 0;
X &WR(@_);
X}
Xsub WR {
X local(@sip) = @_;
X local($fh) = &OPval(@_ > 1 ? @_[1] : $OPcurrent);
X local($oldfh) = select($fh);
X %WRmark = ();
X &WR1(@_[0]);
X select($oldfh);
X $TRUE;
X}
X
Xsub WR1 {
X local($it) = @_;
X local($t) = &TYPE($it);
X if ($t == $T_NIL) { print '()'; }
X elsif ($t == $T_BOOLEAN){ print $it ? '#t' : '#f'; }
X elsif ($t == $T_NUMBER) { print &Nval($it); }
X elsif ($t == $T_CHAR) { &WRchar($it); }
X elsif ($t == $T_SYMBOL) { print &Yname($it); }
X elsif ($t == $T_STRING) { &WRstring($it); }
X elsif ($t == $T_VECTOR) { &WRvector($it); }
X elsif ($t == $T_TABLE) { &WRtable($it); }
X elsif ($t == $T_PAIR) { &WRlist($it); }
X
X elsif ($t == $T_INPUT) {
X print '#<input port ', &IPval($it), '>';
X } elsif ($t == $T_OUTPUT) {
X print '#<output port ', &OPval($it), '>';
X } elsif ($t == $T_SUBR) {
X print '#<built-in ', (&SUBRval($it))[0], '>';
X } elsif ($t == $T_FORM) {
X print '#<keyword ', (&FORMval($it))[0], '>';
X } else {
X print "#<strange object: $it>";
X }
X}
X
Xsub WRstring {
X local($s) = &Sval(@_[0]);
X if (!$WRquoted) {
X print $s;
X } else {
X $s =~ s/\\/\\\\/g;
X $s =~ s/"/\\"/g;
X print '"', $s, '"';
X }
X}
X
Xsub WRchar {
X local($c) = &Cval(@_[0]);
X if (!$WRquoted) { print $c; }
X elsif ($c eq ' ') { print '#\space'; }
X elsif ($c eq "\n") { print '#\newline'; }
X elsif ($c eq "\t") { print '#\tab'; }
X else { print "#\\$c"; }
X}
X
X# XXX Can't read a written table.
Xsub WRtable {
X local($it) = @_;
X return print '{...}' if $WRmark{$it};
X $WRmark{$it} += 3; # strong bias against printing tables again.
X
X print '{';
X local(@keys) = &Tkeys($it);
X if (@keys) {
X local($k) = pop @keys;
X print $k, ' => ';
X &WR1(&Tval($it, &Y($k)));
X }
X for $k (@keys) {
X print ', ', $k, ' => ';
X &WR1(&Tval($it, &Y($k)));
X }
X print '}';
X
X $WRmark{$it} -= 3;
X}
X
Xsub WRvector {
X local($it) = @_;
X return print '#(...)' if $WRmark{$it};
X ++$WRmark{$it};
X
X local(@v) = &Vval($it);
X print '#(';
X &WR1(shift @v) if @v;
X while (@v) {
X print ' ';
X &WR1(shift @v);
X }
X print ')';
X
X --$WRmark{$it};
X}
X
Xsub WRlist {
X local($it) = @_;
X return print '(...)' if $WRmark{$it};
X local(%save) = %WRmark;
X ++$WRmark{$it};
X
X local($a, $d) = &Pval($it);
X print "(";
X &WR1($a);
X while ($d ne $NIL) {
X if ($WRmark{$d}) {
X print ' ...';
X last;
X } elsif (&TYPE($d) != $T_PAIR) {
X print ' . ';
X &WR1($d);
X last;
X } else {
X ++$WRmark{$d};
X ($a, $d) = &Pval($d);
X print ' ';
X &WR1($a);
X }
X }
X print ')';
X
X %WRmark = %save;
X}
X
X#------
X#-- Control features.
X#------
X
X# XXX SUBR call-with-current-continuation
X
X&SUBR1('procedure?');
Xsub procedureP {
X local($it) = @_;
X local($t) = &TYPE($it);
X $t == $T_SUBR ||
X ($t == $T_VECTOR && (&Vval($it))[0] eq $CLOSURE);
X}
X
X&SUBR1('force');
Xsub force {
X &ERRtype(@_[0], 'a promise', 'force') if &TYPE(@_[0]) ne $T_VECTOR;
X local($thunk) = @_;
X local($k, $forced, $val, $env, @code) = &Vval($thunk);
X &ERRtype($thunk, 'a promise', 'force') if $k ne $PROMISE;
X if (!$forced) {
X &ENVpush($env);
X $val = &begin(@code);
X &ENVpop();
X &Vset($thunk, 1, 2, $TRUE, $val);
X }
X $val;
X}
X
X&SUBRN('apply');
Xsub apply {
X local(@sip) = @_;
X local($f, @args) = @_;
X &CHKtype(@args[$#args], $T_LIST, 'apply');
X push(@args, &Lval(pop @args));
X &applyN($f, @args);
X}
X
Xsub applyN {
X local(@args) = @_;
X local($f) = shift @args;
X local($t) = &TYPE($f);
X
X if ($t == $T_SUBR) {
X local($f, $min, $max, @t) = &SUBRval($f);
X if (@args < $min) {
X &ERR("Error, $f needs at least $min arguments.");
X } elsif ($max >= 0 && @args > $max) {
X &ERR("Error, $f wants at most $max arguments.");
X }
X if ($max < 0 && @t[0]) {
X for $x (@args) {
X &CHKtype($x, @t[0], $f);
X }
X } elsif (@t) {
X local($k) = $#t < $#args ? $#t : $#args;
X for (; $k >= 0; --$k) {
X &CHKtype(@args[$k], @t[$k], $f);
X }
X }
X return do $f (@args);
X
X } elsif ($t == $T_VECTOR) {
X local($k, $env, $nsym, $n, @code) = &Vval($f);
X &ERRtype($f, $T_PROCEDURE, 'applyN') if $k ne $CLOSURE;
X $n = &Nval($n);
X if (@args < $n) {
X &ERR('not enough args to procedure.');
X } elsif (@args > $n && $nsym eq $NIL) {
X &ERR('too many args to procedure.');
X }
X &ENVpush($env);
X &ENVpush_frame();
X if ($n > 0) {
X &ENVbind(splice(@code, 0, $n), splice(@args, 0, $n));
X }
X if ($nsym ne $NIL) {
X &ENVbind($nsym, &L(@args));
X }
X local($x) = &begin(@code);
X &ENVpop();
X return $x;
X
X } else {
X &ERRtype($f, $T_PROCEDURE, 'applyN');
X }
X}
X
X&SUBRN('map');
Xsub map {
X local(@lists) = @_;
X local($f) = &eval(shift @lists);
X local(@result, @args, $a);
X &CHKtype($f, $T_PROCEDURE, 'map');
X # XXX CHKtype lists. and all lists must be same length.
X while (@lists[0] ne $NIL) {
X @args = ();
X for $x (@lists) {
X ($a, $x) = &Pval($x);
X push(@args, $a);
X }
X push(@result, &applyN($f, @args));
X }
X &L(@result);
X}
X
X&SUBRN('for-each');
Xsub for_each {
X local(@lists) = @_;
X local($f) = &eval(shift @lists);
X local(@args, $a);
X &CHKtype($f, $T_PROCEDURE, 'for-each');
X # XXX CHKtype lists. and all lists must be same length.
X while (@lists[0] ne $NIL) {
X @args = ();
X for $x (@lists) {
X ($a, $x) = &Pval($x);
X push(@args, $a);
X }
X &applyN($f, @args);
X }
X $TRUE;
X}
X
X
Xsub eval {
X local($it) = @_;
X local($t) = &TYPE($it);
X
X if ($t == $T_SYMBOL) {
X return &ENVval($it);
X } elsif ($t != $T_PAIR) {
X return $it;
X }
X
X local($f, $args) = &Pval($it);
X
X $t = &TYPE($f);
X if ($t == $T_SYMBOL) {
X $f = &ENVval($f);
X $t = &TYPE($f);
X } elsif ($t == $T_PAIR) {
X $f = &eval($f);
X $t = &TYPE($f);
X }
X
X if ($t == $T_FORM) {
X $f = &FORMval($f);
X return do $f (&Lval($args));
X }
X
X if ($t != $T_SUBR && $t != $T_VECTOR) {
X &ERRtype(&car(@_[0]), $T_PROCEDURE, 'eval');
X }
X
X local(@args) = &Lval($args);
X for $a (@args) { $a = &eval($a); }
X &applyN($f, @args);
X}
X
X#------
X#-- User interface.
X#------
X
X&SUBR1('load', $T_STRING);
Xsub load {
X local($f) = &Sval(@_[0]);
X local($ip) = &IP($f . '.sp') || &IP($f) ||
X &ERR("load, neither $f nor $f.sp found.");
X
X print $stderr "Loading $f...\n";
X
X local($x, $y);
X while (($x = &read($ip)) ne $EOF) {
X $y = &eval($x);
X }
X close(&IPval($ip));
X
X $y;
X}
X
X# XXX SUBR transcript-on, transcript-off
X
X&SUBR('exit', 0, 1, $T_NUMBER);
Xsub exit {
X local($x) = @_ ? &Nval(@_[0]) : 0;
X &DB'prof_dump if defined &DB'prof_dump;
X exit $x;
X}
X
X&SUBR0('sp-version');
Xsub sp_version {
X &N($version);
X}
X
Xsub repl {
X local($x);
X while {
X print "> ";
X $x = &read();
X $x ne $EOF;
X } {
X $x = &eval($x);
X print "\n";
X &write($x);
X print "\n";
X }
X}
X
X#------
X#-- Main program.
X#------
X
Xsub catch_interrupt {
X print $stderr "Interrupt\n";
X goto TOP; # Not quite a safe thing to do.
X}
X
X$# = '%.15g'; # the default, %.20g, is a little too many digits.
X
XINIT:;
X
X&IOinit();
X
X$TOPjmp = 0;
X
XTOP:;
X
X&IOreset();
X&ENVreset();
X
Xif ($TOPjmp) {
X print $stderr "\nContinuing from top...\n";
X} else {
X $TOPjmp = 1;
X print $stderr "Scheme in Perl? (sp?)\n";
X print $stderr " version $version\n";
X}
X
Xif (! @ARGV) {
X $SIG{'INT'} = 'catch_interrupt';
X &repl();
X} else {
X $dodump = (@ARGV[0] eq '-D') && shift @ARGV;
X for $x (@ARGV) {
X &load(&S($x));
X }
X if ($dodump) {
X &IOshutdown();
X dump INIT;
X }
X}
X
X&exit();
END_OF_FILE
if test 20760 -ne `wc -c <'sp.pl.part2'`; then
echo shar: \"'sp.pl.part2'\" unpacked with wrong size!
fi
# end of 'sp.pl.part2'
fi
echo shar: End of shell archive.
exit 0
More information about the Alt.sources
mailing list