home *** CD-ROM | disk | FTP | other *** search
Text File | 1990-12-28 | 23.3 KB | 1,216 lines |
- #!/usr/bin/perl
- # Scheme in Perl? (sp?)
- # Public domain. No strings attached.
-
- ($version) = '$Revision: 2.6 $' =~ /: (\d+\.\d+)/;
-
- #------
- #-- Basic data types.
- #------
-
- # There are three places that know about data type representation:
- # 1. The &TYPE function.
- # 2. The basic functions for that type in this section.
- # 3. The equivalence routines (eq?, eqv?, and equal?).
- # Any change in representation needs to look at all these.
-
- %TYPEname = ();
-
- sub TYPES {
- local($k);
- for ($k = 0; $k < @_; $k += 2) {
- @_[$k] = $k;
- $TYPEname{@_[$k]} = @_[$k + 1];
- }
- }
- &TYPES( $T_NONE, 'nothing',
- $T_NIL, 'a null list',
- $T_BOOLEAN, 'a boolean',
- $T_NUMBER, 'a number',
- $T_CHAR, 'a character',
- $T_STRING, 'a string',
- $T_PAIR, 'a pair',
- $T_VECTOR, 'a vector',
- $T_TABLE, 'a table',
- $T_SYMBOL, 'a symbol',
- $T_INPUT, 'an input port',
- $T_OUTPUT, 'an output port',
- $T_FORM, 'a special form',
- $T_SUBR, 'a built-in procedure',
- # Some derived types. See &CHKtype.
- $T_LIST, 'a list',
- $T_PROCEDURE, 'a procedure',
- $T_ANY, 'anything');
-
- # Scheme object -> type.
- sub TYPE {
- local($_) = @_;
- if (/^$/) { $T_NIL; }
- elsif (/^[01]/) { $T_BOOLEAN; }
- elsif (/^N/) { $T_NUMBER; }
- elsif (/^C/) { $T_CHAR; }
- elsif (/^Z'S/) { $T_STRING; }
- elsif (/^Z'P/) { $T_PAIR; }
- elsif (/^Z'V/) { $T_VECTOR; }
- elsif (/^Z'T/) { $T_TABLE; }
- elsif (/^Y/) { $T_SYMBOL; }
- elsif (/^FORM/) { $T_FORM; }
- elsif (/^SUBR/) { $T_SUBR; }
- elsif (/^Z'IP/) { $T_INPUT; }
- elsif (/^Z'OP/) { $T_OUTPUT; }
- else { $T_NONE; }
- }
-
- #-- More derived types.
-
- # A closure is a vector that looks like
- # #(CLOSURE env listarg nargs arg... code...)
- # See &lambda and &applyN.
- $CLOSURE = &Y('CLOSURE');
-
- # A promise is a vector that looks like
- # #(PROMISE env forced? value code...)
- # See &delay and &force.
- $PROMISE = &Y('PROMISE');
-
- #-- Booleans.
-
- # Scheme booleans and Perl booleans are designed to be equivalent.
-
- $NIL = '';
- $TRUE = 1;
- $FALSE = 0;
-
- #-- Numbers.
-
- # Perl number -> Scheme number.
- sub N {
- 'N' . @_[0];
- }
-
- # Scheme number -> Perl number.
- sub Nval {
- &ERRbad_type(@_[0], $T_NUMBER) if @_[0] !~ /^N/;
- $';
- }
-
- #-- Characters.
-
- # Perl character -> Scheme character.
- sub C {
- 'C' . @_[0];
- }
-
- # Scheme character -> Perl character.
- sub Cval {
- &ERRbad_type(@_[0], $T_CHAR) if @_[0] !~ /^C/;
- $';
- }
-
- #-- Strings.
- # Strings are encapsulated so that eqv? works properly.
-
- # Perl string -> Scheme string.
- sub S {
- local($sip) = @_;
- local(*s) = local($z) = "Z'S" . ++$Z'S;
- $s = $sip;
- $z;
- }
-
- # Scheme string -> Perl string.
- sub Sval {
- &ERRbad_type(@_[0], $T_STRING) if @_[0] !~ /^Z'S/;
- local(*s) = @_;
- $s;
- }
-
- # Scheme string <= start, length, new Perl string.
- sub Sset {
- &ERRbad_type(@_[0], $T_STRING) if @_[0] !~ /^Z'S/;
- local(@sip) = @_;
- local(*s, $p, $l, $n) = @sip;
- substr($s, $p, $l) = $n;
- }
-
- #-- Pairs and lists.
-
- # Perl vector (A, D) -> Scheme pair (A . D).
- sub P {
- local(@sip) = @_;
- local(*p) = local($z) = "Z'P" . ++$Z'P;
- @p = @sip;
- $z;
- }
-
- # Scheme pair (A . D) -> Perl list (A, D).
- sub Pval {
- &ERRbad_type(@_[0], $T_PAIR) if @_[0] !~ /^Z'P/;
- local(*p) = @_;
- @p;
- }
-
- # Scheme pair (sexp0 . sexp1) <= index, new Scheme value.
- sub Pset {
- &ERRbad_type(@_[0], $T_PAIR) if @_[0] !~ /^Z'P/;
- local(@sip) = @_;
- local(*p, $k, $n) = @sip;
- @p[$k] = $n;
- }
-
- # Perl vector -> Scheme list.
- sub L {
- local(@v) = @_;
- local($list) = $NIL;
- $list = pop @v, pop @v if @v > 2 && @v[$#v - 1] eq '.';
- $list = &P(pop @v, $list) while @v;
- $list;
- }
-
- # Scheme list -> Perl vector. XXX Doesn't do improper or recursive lists.
- sub Lval {
- local($list) = @_;
- local($x, @v);
- while ($list ne $NIL) {
- ($x, $list) = &Pval($list);
- push(@v, $x);
- }
- @v;
- }
-
- #-- Vectors.
-
- # Perl vector -> Scheme vector.
- sub V {
- local(@sip) = @_;
- local(*v) = local($z) = "Z'V" . ++$Z'V;
- @v = @sip;
- $z;
- }
-
- # Scheme vector -> Perl vector.
- sub Vval {
- &ERRbad_type(@_[0], $T_VECTOR) if @_[0] !~ /^Z'V/;
- local(*v) = @_;
- @v;
- }
-
- # Scheme vector <= start, length, new Perl vector.
- sub Vset {
- &ERRbad_type(@_[0], $T_VECTOR) if @_[0] !~ /^Z'V/;
- local(@sip) = @_;
- local(*v, $s, $l, @n) = @sip;
- splice(@v, $s, $l, @n);
- }
-
- #-- Tables.
-
- # XXX Tables could use a "default value".
-
- # -> Scheme table.
- sub T {
- "Z'T" . ++$Z'T;
- }
-
- # Scheme table, Scheme symbol -> Scheme value.
- sub Tval {
- &ERRbad_type(@_[0], $T_TABLE) if @_[0] !~ /^Z'T/;
- &ERRbad_type(@_[1], $T_SYMBOL) if @_[1] !~ /^Y/;
- local(*t) = @_;
- $t{$'};
- }
-
- # Scheme table <= Perl string, new Scheme value.
- sub Tset {
- &ERRbad_type(@_[0], $T_TABLE) if @_[0] !~ /^Z'T/;
- &ERRbad_type(@_[1], $T_SYMBOL) if @_[1] !~ /^Y/;
- local(@sip) = @_;
- local(*t) = @sip;
- $t{$'} = @sip[2];
- }
-
- # Scheme table -> Perl vector of keys.
- sub Tkeys {
- &ERRbad_type(@_[0], $T_TABLE) if @_[0] !~ /^Z'T/;
- local(*t) = @_;
- keys %t;
- }
-
- #-- Symbols.
-
- %OBLIST = ();
- $OBLIST = &REF("Z'Toblist", 'OBLIST');
-
- # Perl string -> Scheme symbol.
- sub Y {
- 'Y' . @_[0];
- }
-
- # Scheme symbol -> Perl string.
- sub Yname {
- &ERRbad_type(@_[0], $T_SYMBOL) if @_[0] !~ /^Y/;
- $';
- }
-
- # Scheme symbol -> global Scheme value.
- sub Yval {
- &ERRbad_type(@_[0], $T_SYMBOL) if @_[0] !~ /^Y/;
- $OBLIST{$'};
- }
-
- # Scheme symbol <= new global Scheme value.
- sub Yset {
- &ERRbad_type(@_[0], $T_SYMBOL) if @_[0] !~ /^Y/;
- $OBLIST{$'} = @_[1];
- }
-
- # Perl string symbol name <= new global Scheme value.
- sub DEF {
- $OBLIST{@_[0]} = @_[1];
- }
-
- # Create an aliased object.
- sub REF {
- local(@sip) = @_;
- local($a, $b) = @sip;
- eval "*$a = *$b" || die "ALIAS: $@.\n";
- $a;
- }
-
- &SUBR0('global-environment');
- sub global_environment {
- $OBLIST;
- }
-
- #-- Input and output ports.
-
- %IPbuffer = ();
-
- # Perl string filename -> Scheme input port.
- sub IP {
- local($f) = @_;
- local($z) = "Z'IP" . ++$Z'IP;
- open($z, "< $f\0") || return $NIL;
- $IPbuffer{$z} = '';
- $z;
- }
-
- # Scheme input port -> Perl filehandle.
- sub IPval {
- &ERRbad_type(@_[0], $T_INPUT) if @_[0] !~ /^Z'IP/;
- @_[0];
- }
-
- # Scheme input port => Perl string.
- sub IPget {
- &ERRbad_type(@_[0], $T_INPUT) if @_[0] !~ /^Z'IP/;
- local($ip) = @_;
- local($_) = $IPbuffer{$ip};
- $_ ne '' ? ($IPbuffer{$ip} = '') : ($_ = <$ip>);
- $_;
- }
-
- # Like &IPget, but skip leading whitespace and comments.
- sub IPgetns {
- &ERRbad_type(@_[0], $T_INPUT) if @_[0] !~ /^Z'IP/;
- local($ip) = @_;
- local($_) = $IPbuffer{$ip};
- $_ ne '' ? ($IPbuffer{$ip} = '') : ($_ = <$ip>);
- $_ = <$ip> while $_ ne '' && /^\s*;|^\s*$/;
- s/^\s+//;
- $_;
- }
-
- # Scheme input port <= Perl string.
- sub IPput {
- &ERRbad_type(@_[0], $T_INPUT) if @_[0] !~ /^Z'IP/;
- $IPbuffer{@_[0]} .= @_[1];
- }
-
- # Perl string filename -> Scheme output port.
- sub OP {
- local($f) = @_;
- local($z) = "Z'OP" . ++$Z'OP;
- open($z, "> $f\0") || return $NIL;
- $z;
- }
-
- # Scheme output port -> Perl filehandle.
- sub OPval {
- &ERRbad_type(@_[0], $T_OUTPUT) if @_[0] !~ /^Z'OP/;
- @_[0];
- }
-
- # Scheme output port <= Perl string.
- sub OPput {
- &ERRbad_type(@_[0], $T_OUTPUT) if @_[0] !~ /^Z'OP/;
- local(@sip) = @_;
- local($fh) = shift @sip;
- print $fh @sip;
- }
-
- sub IOinit {
- open($stdin = "Z'IPstdin", "<& STDIN");
- open($stdout = "Z'OPstdout", ">& STDOUT");
- open($stderr = "Z'OPstderr", ">& STDERR");
- select($stderr); $| = 1;
- $ttyin = &IP('/dev/tty');
- $ttyout = &OP('/dev/tty');
- }
-
- sub IOshutdown {
- close($stdin);
- close($stdout);
- close($stderr);
- close($ttyin);
- close($ttyout);
- }
-
- &SUBR0('standard-input'); sub standard_input { $stdin; }
- &SUBR0('standard-output'); sub standard_output { $stdout; }
- &SUBR0('standard-error'); sub standard_error { $stderr; }
- &SUBR0('terminal-input'); sub terminal_input { $ttyin; }
- &SUBR0('terminal-output'); sub terminal_output { $ttyout; }
-
- #-- Special forms.
-
- # Define Scheme special form <= name.
- sub FORM {
- local($sub) = local($name) = @_[0];
- $sub =~ tr/->?!*/_2PIX/;
- &DEF($name, 'FORM' . $sub);
- }
-
- # Scheme special form -> Perl subroutine name.
- sub FORMval {
- &ERRbad_type(@_[0], $T_FORM) if @_[0] !~ /^FORM/;
- $';
- }
-
- #-- Builtin functions (subrs).
-
- %SUBRmin = ();
- %SUBRmax = ();
- %SUBRtypes = ();
-
- # Define Scheme builtin <= name, minargs, maxargs, type list.
- sub SUBR {
- local(@sip) = @_;
- local($name, $min, $max, @types) = @sip;
- local($sub) = $name;
- $sub =~ tr/->?!*/_2PIX/;
- $SUBRmin{$sub} = $min;
- $SUBRmax{$sub} = $max;
- $SUBRtypes{$sub} = pack('L*', @types);
- &DEF($name, 'SUBR' . $sub);
- }
-
- # Scheme builtin function -> Perl sub name, minargs, maxargs, type list.
- sub SUBRval {
- &ERRbad_type(@_[0], $T_SUBR) if @_[0] !~ /^SUBR/;
- ($', $SUBRmin{$'}, $SUBRmax{$'}, unpack('L*', $SUBRtypes{$'}));
- }
-
- # Some convenient aliases...
- sub SUBR0 { &SUBR(shift, 0, 0); }
- sub SUBR1 { &SUBR(shift, 1, 1, @_); }
- sub SUBR2 { &SUBR(shift, 2, 2, @_); }
- sub SUBR3 { &SUBR(shift, 3, 3, @_); }
- sub SUBRN { &SUBR(shift, 0, -1, @_); }
-
- # A convenient macro...
- sub CMP_SUBR {
- local(@sip) = @_;
- local($name, $longname, $type, $acc, $cmp) = @sip;
- local($s) = &SUBR($longname, 0, -1, $type);
- &DEF($name, $s);
- eval 'sub ' . (&SUBRval($s))[0] . ' {
- local(@sip) = @_;
- local($r) = 1;
- for (; $r && @sip > 1; shift @sip) {
- $r = '.$acc.'(@sip[0]) '.$cmp.' '.$acc.'(@sip[1]);
- }
- $r;
- }';
- }
-
- #-- Miscellany.
-
- &SUBR0('*show-memory-use');
- sub Xshow_memory_use {
- print $stderr 'memory use: s', $Z'S+0, ' p', $Z'P+0, ' v', $Z'V+0;
- print $stderr ' t', $Z'T+0, ' ip', $Z'IP+0, ' op', $Z'OP+0;
- print $stderr "\n";
- }
-
- #------
- #-- Environments and frames.
- #------
-
- # @ENVcurrent is a Perl vector that gets modified in place, for efficiency.
- # $ENVcache is a Scheme vector that's a copy of the current environment.
-
- @ENVcurrent = ();
- $ENVcache = $FALSE;
- @ENVstack = ();
-
- # Returns the current environment.
- sub ENVcurrent {
- $ENVcache = &V(@ENVcurrent) if ! $ENVcache;
- $ENVcache;
- }
-
- # Push to a new environment.
- sub ENVpush {
- local($new) = @_;
- push(@ENVstack, $ENVcache || &V(@ENVcurrent));
- @ENVcurrent = &Vval($new);
- $ENVcache = $new;
- }
-
- # Pop to the old environment.
- sub ENVpop {
- $ENVcache = pop @ENVstack;
- @ENVcurrent = &Vval($ENVcache);
- }
-
- # Pop to the global environment.
- sub ENVreset {
- @ENVstack = ();
- $ENVcache = $FALSE;
- @ENVcurrent = ();
- }
-
- # Get a value from the current environment.
- sub ENVval {
- local($sym) = @_;
- local($x);
- for $f (@ENVcurrent) {
- return $x if defined($x = &Tval($f, $sym));
- }
- defined($x = &Yval($sym)) || &ERRunbound($sym);
- $x;
- }
-
- # Set a value in the current environment.
- sub ENVset {
- local(@sip) = @_;
- local($sym, $val) = @sip;
- local($x);
- for $f (@ENVcurrent) {
- return &Tset($f, $sym, $val) if defined($x = &Tval($f, $sym));
- }
- return &Yset($sym, $val);
- }
-
- # Push a new frame onto the current environment.
- sub ENVpush_frame {
- $ENVcache = $FALSE;
- unshift(@ENVcurrent, &T());
- }
-
- # Remove the top frame from the current environment.
- sub ENVpop_frame {
- $ENVcache = $FALSE;
- shift @ENVcurrent;
- }
-
- # Bind new values in the top frame of the current environment.
- sub ENVbind {
- local(@syms) = @_;
- local(@vals) = splice(@syms, @syms / 2, @syms / 2);
- if (@ENVcurrent == 0) {
- &Yset(shift @syms, shift @vals) while @syms;
- } else {
- local($t) = @ENVcurrent[0];
- &Tset($t, shift @syms, shift @vals) while @syms;
- }
- }
-
- &DEF('current-environment', &SUBR0('ENVcurrent'));
-
- #------
- #-- Error handling.
- #------
-
- sub ERR {
- print $stderr '** ', @_, "\n";
- goto TOP;
- }
-
- sub ERRbad_type {
- local(@sip) = @_;
- local($it, $what) = @sip;
- $what = $TYPEname{$what} || "type $what";
- print $stderr "** Internal type error, $it is not $what.\n";
- goto TOP;
- }
-
- sub ERRtype {
- local(@sip) = @_;
- local($it, $what, $where) = @_;
- $what = $TYPEname{$what} || "type $what";
- print $stderr "** Type error, ";
- print $stderr "in $where, " if $where ne '';
- &write($it);
- print " is not $what.\n";
- goto TOP;
- }
-
- sub CHKtype {
- local(@sip) = @_;
- local($t0) = &TYPE(@sip[0]);
- local($t1) = @sip[1];
- &ERRtype(@_) unless
- $t1 == $T_ANY ||
- $t0 == $t1 ||
- ($t1 == $T_LIST &&
- ($t0 == $T_PAIR || $t0 == $T_NIL)) ||
- ($t1 == $T_PROCEDURE &&
- ($t0 == $T_SUBR || $t0 == $T_VECTOR))
- ;
- }
-
- sub ERRdomain {
- local(@sip) = @_;
- local($where) = shift @sip;
- print $stderr "** Domain error, ";
- print $stderr "in $where, " if $where ne '';
- print $stderr @sip, "\n";
- goto TOP;
- }
-
- sub ERRunbound {
- local($sym) = @_;
- print $stderr '** Symbol ', &Yname($sym), " is unbound.\n";
- goto TOP;
- }
-
- #------
- #-- Booleans.
- #------
-
- &DEF('t', $TRUE);
- &DEF('nil', $FALSE);
-
- &SUBR1('boolean?');
- sub booleanP {
- @_[0] eq $TRUE || @_[0] eq $FALSE;
- }
-
- &SUBR1('not');
- sub not {
- @_[0] ? $FALSE : $TRUE;
- }
-
- #------
- #-- Equivalence.
- #------
-
- # Perl ($x eq $y) means the same thing as Scheme (eq? x y).
-
- &SUBR2('eq?');
- sub eqP {
- @_[0] eq @_[1];
- }
-
- &SUBR2('eqv?');
- sub eqvP {
- return $TRUE if @_[0] eq @_[1];
- local(@sip) = @_;
- local($t) = &TYPE(@sip[0]);
- if ($t != &TYPE(@sip[1])) {
- $FALSE;
- } elsif ($t == $T_NUMBER) {
- &Nval(@sip[0]) == &Nval(@sip[1]);
- } elsif ($t == $T_STRING) {
- &Sval(@sip[0]) eq '' && &Sval(@sip[1]) eq '';
- } elsif ($t == $T_VECTOR) {
- &Vval(@sip[0]) == 0 && &Vval(@sip[1]) == 0;
- } else {
- $FALSE;
- }
- }
-
- # XXX Fails to terminate for recursive types.
- &SUBR2('equal?');
- sub equalP {
- return $TRUE if @_[0] eq @_[1];
- local(@sip) = @_;
- local($t) = &TYPE(@sip[0]);
- if ($t != &TYPE(@sip[1])) {
- $FALSE;
- } elsif ($t == $T_STRING) {
- &Sval(@sip[0]) eq &Sval(@sip[1]);
- } elsif ($t == $T_PAIR) {
- local($a0, $d0) = &Pval(@sip[0]);
- local($a1, $d1) = &Pval(@sip[1]);
- &equalP($a0, $a1) && &equalP($d0, $d1);
- } elsif ($t == $T_VECTOR) {
- local(@v) = &Vval(@sip[0]);
- local(@u) = &Vval(@sip[1]);
- return $FALSE if @v != @u;
- while (@v) {
- return $FALSE if ! &equalP(shift @v, shift @u);
- }
- $TRUE;
- } else {
- &eqvP(@sip[0], @sip[1]);
- }
- }
-
- #------
- #-- Pairs and lists.
- #------
-
- &SUBR1('pair?');
- sub pairP {
- &TYPE(@_[0]) == $T_PAIR;
- }
-
- &DEF('cons', &SUBR2('P'));
-
- &SUBR1('car');
- sub car {
- # XXX Patchlevel 41 broke something; &car(&car($x)) doesn't work if this
- # XXX line is uncommented.
- # &CHKtype(@_[0], $T_PAIR, 'car');
- (&Pval(@_[0]))[0];
- }
-
- &SUBR1('cdr', $T_PAIR);
- sub cdr {
- # XXX See comment for car.
- # &CHKtype(@_[0], $T_PAIR, 'cdr');
- (&Pval(@_[0]))[1];
- }
-
- &SUBR2('set-car!', $T_PAIR);
- sub set_carI {
- &Pset(@_[0], 0, @_[1]);
- }
-
- &SUBR2('set-cdr!', $T_PAIR);
- sub set_cdrI {
- &Pset(@_[0], 1, @_[1]);
- }
-
- &SUBR1('caar'); sub caar { &car(&car(@_[0])); }
- &SUBR1('cadr'); sub cadr { &car(&cdr(@_[0])); }
- &SUBR1('cdar'); sub cdar { &cdr(&car(@_[0])); }
- &SUBR1('cddr'); sub cddr { &cdr(&cdr(@_[0])); }
-
- # XXX caaar and friends.
-
- &SUBR1('null?');
- sub nullP {
- @_[0] eq $NIL;
- }
-
- &DEF('list', &SUBRN('L'));
-
- &SUBR1('length', $T_LIST);
- sub length {
- local($p) = @_;
- local($n) = 0;
- $n += 1, $p = &cdr($p) while $p ne $NIL;
- &N($n);
- }
-
- &SUBRN('append');
- sub append {
- local(@v) = @_;
- local($p) = pop @v;
- for $a (reverse @v) {
- &CHKtype($a, $T_LIST, 'append');
- for $b (reverse &Lval($a)) {
- $p = &P($b, $p);
- }
- }
- $p;
- }
-
- &SUBR1('reverse', $T_LIST);
- sub reverse {
- &L(reverse(&Lval(@_[0])));
- }
-
- &SUBR2('list-tail', $T_LIST, $T_NUMBER);
- sub list_tail {
- local(@sip) = @_;
- local($p) = @sip[0];
- local($k) = &Nval(@sip[1]);
- $p = &cdr($p) while $k--;
- $p;
- }
-
- &SUBR2('list-ref', $T_LIST, $T_NUMBER);
- sub list_ref {
- local(@sip) = @_;
- local(@v) = &Lval(@sip[0]);
- local($n) = &Nval(@sip[1]);
- 0 < $n && $n < @v ? @v[$n] : $NIL; # XXX error?
- }
-
- &SUBR1('last-pair', $T_LIST);
- sub last_pair {
- local($p) = @_;
- local($d);
- $p = $d while &TYPE($d = &cdr($p)) == $T_PAIR;
- $p;
- }
-
- &SUBR2('memq', $T_ANY, $T_LIST);
- sub memq {
- local(@sip) = @_;
- local($x, $p) = @sip;
- local($a, $d);
- for (; $p ne $NIL; $p = $d) { # XXX improper lists
- ($a, $d) = &Pval($p);
- return $p if $x eq $a;
- }
- return $FALSE;
- }
-
- &SUBR2('memv', $T_ANY, $T_LIST);
- sub memv {
- local(@sip) = @_;
- local($x, $p) = @sip;
- local($a, $d);
- for (; $p ne $NIL; $p = $d) { # XXX improper lists
- ($a, $d) = &Pval($p);
- return $p if &eqvP($x, $a);
- }
- return $FALSE;
- }
-
- &SUBR2('member', $T_ANY, $T_LIST);
- sub member {
- local(@sip) = @_;
- local($x, $p) = @sip;
- local($a, $d);
- for (; $p ne $NIL; $p = $d) { # XXX improper lists
- ($a, $d) = &Pval($p);
- return $p if &equalP($x, $a);
- }
- return $FALSE;
- }
-
- &SUBR2('assq', $T_ANY, $T_LIST);
- sub assq {
- local(@sip) = @_;
- local($x, $p) = @_;
- local($a);
- while ($p ne $NIL) { # XXX improper lists
- ($a, $p) = &Pval($p);
- return $a if $x eq &car($a);
- }
- return $FALSE;
- }
-
- &SUBR2('assv', $T_ANY, $T_LIST);
- sub assv {
- local(@sip) = @_;
- local($x, $p) = @_;
- local($a);
- while ($p ne $NIL) { # XXX improper lists
- ($a, $p) = &Pval($p);
- return $a if &eqvP($x, &car($a));
- }
- return $FALSE;
- }
-
- &SUBR2('assoc', $T_ANY, $T_LIST);
- sub assoc {
- local(@sip) = @_;
- local($x, $p) = @_;
- local($a);
- while ($p ne $NIL) { # XXX improper lists
- ($a, $p) = &Pval($p);
- return $a if &equalP($x, &car($a));
- }
- return $FALSE;
- }
-
- #------
- #-- Symbols.
- #------
-
- &SUBR1('symbol?');
- sub symbolP {
- &TYPE(@_[0]) == $T_SYMBOL;
- }
-
- &SUBR1('symbol->string', $T_SYMBOL);
- sub symbol_2string {
- &S(&Yname(@_[0]));
- }
-
- &SUBR1('string->symbol', $T_STRING);
- sub string_2symbol {
- &Y(&Sval(@_[0]));
- }
-
- #------
- #-- Numbers.
- #------
-
- &SUBR1('number?');
- sub numberP {
- &TYPE(@_[0]) == $T_NUMBER;
- }
-
- &SUBR1('complex?');
- sub complexP {
- &TYPE(@_[0]) == $T_NUMBER;
- }
-
- &SUBR1('real?');
- sub realP {
- &TYPE(@_[0]) == $T_NUMBER;
- }
-
- &SUBR1('rational?');
- sub rationalP {
- &integerP(@_[0]);
- }
-
- &SUBR1('integer?');
- sub integerP {
- return $FALSE if &TYPE(@_[0]) != $T_NUMBER;
- local($n) = &Nval(@_[0]);
- $n == int($n);
- }
-
- &SUBR1('zero?', $T_NUMBER);
- sub zeroP {
- &Nval(@_[0]) == 0;
- }
-
- &SUBR1('positive?', $T_NUMBER);
- sub positiveP {
- &Nval(@_[0]) > 0;
- }
-
- &SUBR1('negative?', $T_NUMBER);
- sub negativeP {
- &Nval(@_[0]) < 0;
- }
-
- &SUBR1('odd?', $T_NUMBER);
- sub oddP {
- &integerP(@_[0]) && (&Nval(@_[0]) & 1) == 1;
- }
-
- &SUBR1('even?', $T_NUMBER);
- sub evenP {
- &integerP(@_[0]) && (&Nval(@_[0]) & 1) == 0;
- }
-
- &CMP_SUBR('=', 'number-eq?', $T_NUMBER, '&Nval', '==');
- &CMP_SUBR('<', 'number-lt?', $T_NUMBER, '&Nval', '<');
- &CMP_SUBR('>', 'number-gt?', $T_NUMBER, '&Nval', '>');
- &CMP_SUBR('<=', 'number-le?', $T_NUMBER, '&Nval', '<=');
- &CMP_SUBR('>=', 'number-ge?', $T_NUMBER, '&Nval', '>=');
-
- &SUBR('max', 1, -1, $T_NUMBER);
- sub max {
- local(@sip) = @_;
- local($x) = &Nval(shift @sip);
- for (; @sip; shift @sip) {
- $x = &Nval(@sip[0]) if &Nval(@sip[0]) > $x;
- }
- &N($x);
- }
-
- &SUBR('min', 1, -1, $T_NUMBER);
- sub min {
- local(@sip) = @_;
- local($x) = &Nval(shift @sip);
- for (; @sip; shift @sip) {
- $x = &Nval(@sip[0]) if &Nval(@sip[0]) < $x;
- }
- &N($x);
- }
-
- &DEF('+', &SUBRN('add', $T_NUMBER));
- sub add {
- local(@sip) = @_;
- local($x) = 0;
- $x += &Nval(shift @sip) while @sip;
- &N($x);
- }
-
- &DEF('-', &SUBR('subtract', 1, -1, $T_NUMBER));
- sub subtract {
- local(@sip) = @_;
- local($x) = &Nval(shift @sip);
- $x = -$x if !@sip;
- $x -= &Nval(shift @sip) while @sip;
- &N($x);
- }
-
- &DEF('*', &SUBRN('multiply', $T_NUMBER));
- sub multiply {
- local(@sip) = @_;
- local($x) = 1;
- $x *= &Nval(shift @sip) while @sip;
- &N($x);
- }
-
- &DEF('/', &SUBR('divide', 1, -1, $T_NUMBER));
- sub divide {
- local(@sip) = @_;
- local($x) = &Nval(shift @sip);
- if (@sip == 0) {
- &ERRdomain('/', 'division by zero.') if $x == 0;
- $x = 1 / $x;
- } else {
- local($y);
- while (@sip) {
- $y = &Nval(shift @sip);
- &ERRdomain('/', 'division by zero.') if $y == 0;
- $x /= $y;
- }
- }
- &N($x);
- }
-
- &DEF('1+', &SUBR1('inc', $T_NUMBER));
- sub inc {
- &N(&Nval(@_[0]) + 1);
- }
-
- &DEF('-1+', &SUBR1('dec', $T_NUMBER));
- sub dec {
- &N(&Nval(@_[0]) - 1);
- }
-
- &SUBR1('abs', $T_NUMBER);
- sub abs {
- local($x) = &Nval(@_[0]);
- &N($x > 0 ? $x : -$x);
- }
-
- &SUBR2('quotient', $T_NUMBER, $T_NUMBER);
- sub quotient {
- local(@sip) = @_;
- local($y) = &Nval(@sip[1]);
- &ERRdomain('quotient', 'division by zero.') if $y == 0;
- &N(int(&Nval(@sip[0]) / $y));
- }
-
- &SUBR2('remainder', $T_NUMBER, $T_NUMBER);
- sub remainder {
- local(@sip) = @_;
- local($x) = &Nval(@sip[0]);
- local($y) = &Nval(@sip[1]);
- &ERRdomain('remainder', 'division by zero.') if $y == 0;
- &N($x - $y * int($x / $y));
- }
-
- &SUBR2('modulo', $T_NUMBER, $T_NUMBER);
- sub modulo {
- local(@sip) = @_;
- local($x) = &Nval(@sip[0]);
- local($y) = &Nval(@sip[1]);
- &ERRdomain('modulo', 'division by zero.') if $y == 0;
- local($r) = $x - $y * int($x / $y);
- $r += $y if ($y < 0 && $r > 0) || ($y > 0 && $r < 0);
- &N($r);
- }
-
- # XXX SUBR numerator, denominator (rationals)
-
- # XXX SUBR gcd, lcm
-
- &SUBR1('floor', $T_NUMBER);
- sub floor {
- local($n) = &Nval(@_[0]);
- if ($n == int($n)) {
- &N($n);
- } else {
- $n < 0 ? &N($n - 1) : &N($n);
- }
- }
-
- &SUBR1('ceiling', $T_NUMBER);
- sub ceiling {
- local($n) = &Nval(@_[0]);
- if ($n == int($n)) {
- &N($n);
- } else {
- $n < 0 ? &N($n) : &N($n + 1);
- }
- }
-
- &SUBR1('truncate', $T_NUMBER);
- sub truncate {
- &N(int(&Nval(@_[0])));
- }
-
- &SUBR1('round', $T_NUMBER);
- sub round {
- local($n) = &Nval(@_[0]);
- if ($n + .5 == int($n + .5)) {
- if ($n < 0) {
- 1 & (-$n - .5) ? &N($n - .5) : &N($n + .5);
- } else {
- 1 & ($n + .5) ? &N($n - .5) : &N($n + .5);
- }
- } else {
- $n < 0 ? &N(int($n - .5)) : &N(int($n + .5));
- }
- }
-
- # XXX SUBR rationalize
-
- &SUBR1('exp', $T_NUMBER);
- sub exp {
- &N(exp(&Nval(@_[0])));
- }
-
- &SUBR1('log', $T_NUMBER);
- sub log {
- local($x) = &Nval(@_[0]);
- &ERRdomain('log', 'singularity at zero.') if $x == 0;
- &N(log($x));
- }
-
- &SUBR1('sin', $T_NUMBER);
- sub sin {
- &N(sin(&Nval(@_[0])));
- }
-
- &SUBR1('cos', $T_NUMBER);
- sub cos {
- &N(cos(&Nval(@_[0])));
- }
-
- &SUBR1('tan', $T_NUMBER);
- sub tan {
- local($x) = &Nval(@_[0]);
- &N(sin($x)/cos($x)); # XXX domain error
- }
-
- &SUBR1('asin', $T_NUMBER);
- sub asin {
- local($x) = &Nval(@_[0]);
- &ERRdomain('asin', $x, ' is not in [-1, 1].') if $x < -1 || $x > 1;
- &N(atan2($x, sqrt(1 - $x * $x)));
- }
-
- &SUBR1('acos', $T_NUMBER);
- sub acos {
- local($x) = &Nval(@_[0]);
- &ERRdomain('acos', $x, ' is not in [-1, 1].') if $x < -1 || $x > 1;
- &N(atan2(sqrt(1 - $x * $x), $x));
- }
-
- &SUBR('atan', 1, 2, $T_NUMBER, $T_NUMBER);
- sub atan {
- local(@sip) = @_;
- local($x) = &Nval(@_[0]);
- local($y) = @_ > 1 ? &Nval(@_[1]) : 1;
- &N(atan2($x, $y)); # XXX domain error
- }
-
- &SUBR1('sqrt', $T_NUMBER);
- sub sqrt {
- &N(sqrt(&Nval(@_[0]))); # XXX domain error
- }
-
- &SUBR2('expt', $T_NUMBER, $T_NUMBER);
- sub expt {
- local(@sip) = @_;
- local($x) = &Nval(@_[0]);
- local($y) = &Nval(@_[1]);
- if ($x == 0 && $y == 0) {
- &N(1); # required in R3RS.
- } else {
- &N($x ** $y); # XXX domain error.
- }
- }
-
- # XXX SUBR make-rectangular, make-polar, real-part, imag-part,
- # XXX SUBR magnitude, angle
- # XXX SUBR exact->inexact, inexact->exact
-
- # XXX SUBR number->string, string->number
-
- #------
- #-- Characters.
- #------
-
- &SUBR1('char?');
- sub charP {
- &TYPE(@_[0]) == $T_CHAR;
- }
-
- &CMP_SUBR('char=?', 'char-eq?', $T_CHAR, '&Cval', 'eq');
- &CMP_SUBR('char<?', 'char-lt?', $T_CHAR, '&Cval', 'lt');
- &CMP_SUBR('char>?', 'char-gt?', $T_CHAR, '&Cval', 'gt');
- &CMP_SUBR('char<=?', 'char-le?', $T_CHAR, '&Cval', 'le');
- &CMP_SUBR('char>=?', 'char-ge?', $T_CHAR, '&Cval', 'ge');
-
- sub ciCval {
- local($_) = &Cval(@_[0]);
- tr/A-Z/a-z/;
- $_;
- }
- &CMP_SUBR('char-ci=?', 'char-ci-eq?', $T_CHAR, '&ciCval', 'eq');
- &CMP_SUBR('char-ci<?', 'char-ci-lt?', $T_CHAR, '&ciCval', 'lt');
- &CMP_SUBR('char-ci>?', 'char-ci-gt?', $T_CHAR, '&ciCval', 'gt');
- &CMP_SUBR('char-ci<=?', 'char-ci-le?', $T_CHAR, '&ciCval', 'le');
- &CMP_SUBR('char-ci>=?', 'char-ci-ge?', $T_CHAR, '&ciCval', 'ge');
-
- &SUBR1('char-alphabetic?', $T_CHAR);
- sub char_alphabeticP {
- &Cval(@_[0]) =~ /[a-zA-Z]/ ? $TRUE : $FALSE;
- }
-
- &SUBR1('char-numeric?', $T_CHAR);
- sub char_numericP {
- &Cval(@_[0]) =~ /[0-9]/ ? $TRUE : $FALSE;
- }
-
- &SUBR1('char-whitespace?', $T_CHAR);
- sub char_whitespaceP {
- &Cval(@_[0]) =~ /\s/ ? $TRUE : $FALSE;
- }
-
- &SUBR1('char-upper-case?', $T_CHAR);
- sub char_upper_caseP {
- &Cval(@_[0]) =~ /[A-Z]/ ? $TRUE : $FALSE;
- }
-
- &SUBR1('char-lower-case?', $T_CHAR);
- sub char_lower_caseP {
- &Cval(@_[0]) =~ /[a-z]/ ? $TRUE : $FALSE;
- }
-
- &SUBR1('char->integer', $T_CHAR);
- sub char_2integer {
- &N(ord(&Cval(@_[0])));
- }
-
- &SUBR1('integer->char', $T_NUMBER);
- sub integer_2char {
- &C(sprintf("%c", &Nval(@_[0])));
- }
-
- &SUBR1('char-upcase', $T_CHAR);
- sub char_upcase {
- local($c) = &Cval(@_[0]);
- $c =~ tr/a-z/A-Z/;
- &C($c);
- }
-
- &SUBR1('char-downcase', $T_CHAR);
- sub char_downcase {
- local($c) = &Cval(@_[0]);
- $c =~ tr/A-Z/a-z/;
- &C($c);
- }
-
-