653 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			653 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
| #!/usr/bin/perl
 | |
| 
 | |
| use FileHandle;
 | |
| 
 | |
| # --- Representation of Lisp data
 | |
| 
 | |
|     $a_number = 0;
 | |
|     $pair_ptr = $a_pair = 1;
 | |
|     $symbol_ptr = $a_symbol = 2;
 | |
|     
 | |
|     $type_name{$a_number} = "number";
 | |
|     $type_name{$a_pair} = "pair";
 | |
|     $type_name{$a_symbol} = "symbol";
 | |
| 
 | |
| sub is {
 | |
|     local($type, $expr)=@_;
 | |
|     if ($expr % 4 != $type) {
 | |
|         error(__LINE__,"Expected a ".$type_name{$type}.", not a "
 | |
| 		. $type_name{$expr % 4}) ;
 | |
|     }
 | |
|     $expr;
 | |
| }
 | |
| 
 | |
| sub is_number     { local($expr)=@_; $expr % 4 == 0; }
 | |
| sub is_pair       { local($expr)=@_; $expr % 4 == 1; }
 | |
| sub is_symbol     { local($expr)=@_; $expr % 4 == 2; }
 | |
| sub is_atom       { local($expr)=@_; $expr % 4 != 1; }
 | |
| 
 | |
| sub make_number { local($n)=@_;	 $n * 4 ;}
 | |
| 
 | |
| sub numeric_value { 
 | |
|     local($expr)=@_;
 | |
|     if ($expr % 4 != 0) { error(__LINE__,"Not a number"); }
 | |
|     $expr / 4;
 | |
| }
 | |
| 
 | |
| # Make a new pair.
 | |
| sub cons {
 | |
|     local($the_car, $the_cdr)= @_;
 | |
|     if ($free_list == $NIL) { gc($the_car, $the_cdr); }
 | |
|     $result = $free_list;
 | |
|     $free_list = $cdr{$free_list};
 | |
|     $car{$result} = $the_car;
 | |
|     $cdr{$result} = $the_cdr;
 | |
|     $result;
 | |
| }
 | |
| 
 | |
| # Return the symbol :string names.
 | |
| sub string_to_symbol{
 | |
|     local($string)=@_;
 | |
|     if (defined ($intern{$string}) ) { return $intern{$string}; }
 | |
|     $symbol_ptr += 4;
 | |
|     $intern{$string} = $symbol_ptr;
 | |
|     $printname{$symbol_ptr} = $string;
 | |
|     $symbol_ptr;
 | |
| }
 | |
| 
 | |
| # Define a primitive procedure, with :nparams parameters,
 | |
| # bound to the symbol named :name.
 | |
| sub def_prim {
 | |
|     local($name, $nparams)=@_;
 | |
|     local($sym);
 | |
|     $sym = string_to_symbol($name);
 | |
|     $value{$sym} = string_to_symbol(sprintf("#<Primitive %s>", $name));
 | |
|     if ($nparams ne "") {
 | |
|         $num_params{$value{$sym}} = $nparams; }
 | |
|     $value{$sym};
 | |
| }
 | |
| 
 | |
| # --- Garbage collection
 | |
| 
 | |
| sub expand_heap {
 | |
|     local($limit);
 | |
|     if ($loud_gc) {
 | |
|         printf STDERR "Expanding heap..." ; }
 | |
|     $limit = $pair_ptr + 4 * ($heap_increment ? $heap_increment : 1500);
 | |
|     for (; $pair_ptr < $limit; $pair_ptr += 4) {
 | |
|         $cdr{$pair_ptr} = $free_list;
 | |
|         $free_list = $pair_ptr;
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub protect { local($object)=@_; $protected{++$protected_ptr} = $object; }
 | |
| sub unprotect {	 --$protected_ptr; }
 | |
| 
 | |
| sub mark {
 | |
|     local($object)=@_;
 | |
|     while (is_pair($object) && !(defined $marks{$object})) {	#** speed
 | |
|         $marks{$object} = 1;
 | |
|         mark($car{$object});
 | |
|         $object = $cdr{$object};
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub sweep {
 | |
|     $free_list = $NIL;
 | |
|     for ($p = $a_pair; $p < $pair_ptr; $p += 4) { # for defined($car{$p}) might be faster
 | |
|         if (!(defined $marks{$p})) {
 | |
|             $cdr{$p} = $free_list;
 | |
|             $free_list = $p;
 | |
|         } else {
 | |
|             delete $marks{$p};
 | |
| 	}
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub gc {
 | |
|     local ($the_car, $the_cdr) = @_;
 | |
| 	 local($p, $i);
 | |
|     if ($loud_gc) {
 | |
|         printf STDERR "\nGC..."  }
 | |
|     mark($the_car); mark($the_cdr);
 | |
|     for (defined $protected{$p}) { mark($protected{$p}); }
 | |
|     for (defined $stack{$p})     { mark($stack{$p}); }
 | |
|     for (defined $value{$p})     { mark($value{$p}); }
 | |
|     for (defined $property{$p}) {
 | |
|         $i = index($;, $p);  # SUBSEP
 | |
|         mark(substr($p, 1, $i-1));
 | |
|         mark(substr($p, $i+1));
 | |
|         mark(property{$p});
 | |
|     }
 | |
|     sweep();
 | |
|     if ($free_list == $NIL) {  expand_heap(); }
 | |
| }
 | |
| 
 | |
| # --- Set up
 | |
| 
 | |
| # BEGIN {	
 | |
|     $trace = 0;
 | |
|     $lineno = 0;
 | |
|     $filehandle = \*STDIN;
 | |
|     $filename = '<STDIN>';
 | |
|     $interactive = 1;
 | |
|     @filestack = ();
 | |
|     $pline = "**prev**";
 | |
|     $sline = "**BOF**";
 | |
|     $gensym_counter=0;
 | |
|     srand();
 | |
|     $token = "";
 | |
|     
 | |
|     $frame_ptr = $stack_ptr = 0;
 | |
|     
 | |
|     $NIL 	= string_to_symbol("nil");
 | |
|     $T 		= string_to_symbol("t");
 | |
|     ;
 | |
|     $value{$NIL} = $NIL;
 | |
|     $value{$T} = $T;
 | |
|     ;
 | |
|     $car{$NIL} = $cdr{$NIL} = $NIL; # this is convenient in a couple places...;
 | |
| 
 | |
|     $free_list = $NIL; expand_heap();
 | |
|     $THE_EOF_OBJECT = string_to_symbol("#eof");
 | |
|     $value{string_to_symbol("the-eof-object")} = $THE_EOF_OBJECT;
 | |
|     ;
 | |
|     $eof = "(eof)";
 | |
| 
 | |
|     $QUOTE 	= string_to_symbol("quote");	$is_special{$QUOTE} = 1;
 | |
|     $LAMBDA 	= string_to_symbol("lambda");	$is_special{$LAMBDA} = 1;
 | |
|     $IF 	= string_to_symbol("if");	$is_special{$IF} = 1;
 | |
|     $SETQ 	= string_to_symbol("set!");	$is_special{$SETQ} = 1;
 | |
|     $DEFINE 	= string_to_symbol("define");	$is_special{$DEFINE} = 1;
 | |
|     $PROGN 	= string_to_symbol("begin");	$is_special{$PROGN} = 1;
 | |
|     $WHILE 	= string_to_symbol("while");	$is_special{$WHILE} = 1;
 | |
| 
 | |
|     $QUIT 	= string_to_symbol("quit");	$is_special{$QUIT} = 1;
 | |
| 
 | |
|     $EQ		= def_prim("eq?", 2);
 | |
|     $NULL 	= def_prim("null?", 1);
 | |
|     $CAR 	= def_prim("car", 1);
 | |
|     $CDR 	= def_prim("cdr", 1);
 | |
|     $CADR 	= def_prim("cadr", 1);
 | |
|     $CDDR 	= def_prim("cddr", 1);
 | |
|     $CONS 	= def_prim("cons", 2);
 | |
|     $LIST 	= def_prim("list");
 | |
|     $EVAL 	= def_prim("eval", 1);
 | |
|     $APPLY 	= def_prim("apply", 2);
 | |
|     $LREAD 	= def_prim("read", 0);
 | |
|     $LWRITE 	= def_prim("write", 1);
 | |
| 
 | |
|     $LOAD	= def_prim('load', 1);
 | |
|     $TRACE	= def_prim('trace', 0);
 | |
|     $LINENO	= def_prim('f_lineno', 0);
 | |
|     $INTER	= def_prim('f_interactive', 0);
 | |
|     $FILE	= def_prim('f_file', 0);
 | |
| 
 | |
|     $NEWLINE 	= def_prim("newline", 0);
 | |
|     $ADD	= def_prim("+", 2);
 | |
|     $SUB 	= def_prim("-", 2);
 | |
|     $MUL 	= def_prim("*", 2);
 | |
|     $DIV 	= def_prim("quotient", 2);
 | |
|     $MOD 	= def_prim("remainder", 2);
 | |
|     $LT 	= def_prim("<", 2);
 | |
|     $GET 	= def_prim("get", 2);
 | |
|     $PUT 	= def_prim("put", 3);
 | |
|     $ATOMP 	= def_prim("atom?", 1);
 | |
|     $PAIRP 	= def_prim("pair?", 1);
 | |
|     $SYMBOLP 	= def_prim("symbol?", 1);
 | |
|     $NUMBERP 	= def_prim("number?", 1);
 | |
|     $SETCAR 	= def_prim("set-car!", 2);
 | |
|     $SETCDR 	= def_prim("set-cdr!", 2);
 | |
|     $NREV 	= def_prim("reverse!", 1);
 | |
|     $GENSYM 	= def_prim("gensym", 0);
 | |
|     $RANDOM	= def_prim("random", 1);
 | |
|     $ERROR	= def_prim("error");
 | |
| 
 | |
|     $EXPLODE 	= def_prim("explode");
 | |
|     $IMPLODE 	= def_prim("implode");
 | |
|     $OPEN 	= def_prim("open");
 | |
|     $CLOSE 	= def_prim("close");
 | |
|     $READCHAR 	= def_prim("readchar");
 | |
|     $WRITEFI	= def_prim("writefi");
 | |
|     $PRINTF	= def_prim("printf");
 | |
| 
 | |
|     $HASH	= def_prim("hash");
 | |
| 
 | |
|     $DRIVER 	= string_to_symbol("top-level-driver");
 | |
| # }
 | |
| 
 | |
| # --- The interpreter
 | |
| 
 | |
| # BEGIN {	
 | |
|    $ts = "testme";
 | |
|    if ( -f $ts ) { load($ts); }
 | |
|    $rc = "$ENV{'HOME'}/.perlisprc";
 | |
|    if ( -f $rc ) { load($rc); }
 | |
| 
 | |
|    READLOOP: for (;;) {
 | |
|    eval {
 | |
| 	for (;;) {
 | |
|         if (defined($value{$DRIVER}) && $value{$DRIVER} != $NIL) {
 | |
|             apply($value{$DRIVER});
 | |
|         } else {
 | |
|             $expr = lread();
 | |
| 	    if ($trace) { 
 | |
| 		    print_expr($expr);
 | |
| 		    print "%$expr%\n"; }
 | |
|             if ($expr == $THE_EOF_OBJECT) { last READLOOP; }
 | |
|             protect($expr);
 | |
| 	    $result = l_eval($expr);
 | |
|             if ($interactive) { print "Interactive p e\n "; print_expr($result); }
 | |
|             unprotect();
 | |
|         }
 | |
|       }
 | |
|     };
 | |
|     print "\n\nRestarting\n\n";
 | |
|     $frame_ptr = $stack_ptr = 0;
 | |
|   }
 | |
|     
 | |
|     if ($profiling) {
 | |
|         while (defined $call_count{$proc}) {
 | |
|             printf "%5d ", $call_count{$proc} ;
 | |
|             print_expr($proc);
 | |
|         }
 | |
|     }
 | |
| # }
 | |
| 
 | |
| # All the interpretation routines have the precondition that their
 | |
| # arguments are protected from garbage collection.
 | |
| 
 | |
| sub l_eval {
 | |
|      local($expr)=@_;
 | |
| 	local($old_frame_ptr);
 | |
|     if (is_atom($expr))	{		#** speed
 | |
|         if (is_symbol($expr)) {
 | |
|             if (!(defined $value{$expr} )) {
 | |
| 		error(__LINE__,"Unbound variable: ".$printname{$expr}); }
 | |
|             return $value{$expr};
 | |
|         } else {
 | |
|             return $expr;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     $op = $car{$expr};		# op is global to save awk stack space
 | |
| 
 | |
|      if ($trace) { print "eval called ".$printname{$op}.
 | |
| 	" with ". ($stack_ptr - $frame_ptr) ." args"  . "\n"; }
 | |
|     if (!(defined $is_special{$op})) {
 | |
|         $old_frame_ptr = $frame_ptr;
 | |
|         $frame_ptr = $stack_ptr;
 | |
| 
 | |
|         l_eval_rands($cdr{$expr});
 | |
|         protect($tmp_proc = l_eval($car{$expr}));
 | |
|         $result = apply($tmp_proc);
 | |
|         unprotect();
 | |
| 
 | |
|         $stack_ptr = $frame_ptr;
 | |
|         $frame_ptr = $old_frame_ptr;
 | |
|         return $result;
 | |
|     }
 | |
| 
 | |
|     if ($op == $QUOTE)	{ return $car{$cdr{$expr}}; }
 | |
|     if ($op == $LAMBDA)	{ return $expr; }
 | |
|     if ($op == $IF)	{ return ( l_eval($car{$cdr{$expr}}) != $NIL )
 | |
|                                 ? l_eval($car{$cdr{$cdr{$expr}}})
 | |
|                                 : l_eval($car{$cdr{$cdr{$cdr{$expr}}}}); };
 | |
|     if ($op == $PROGN)	{ return progn($cdr{$expr}); };
 | |
|     if ($op == $SETQ)	{
 | |
|         if (! defined( $value{$car{$cdr{$expr}}} )) {
 | |
|             error(__LINE__,"Unbound variable: ".$printname{$car{$cdr{$expr}}});
 | |
| 	}
 | |
|         return $value{$car{$cdr{$expr}}} = l_eval($car{$cdr{$cdr{$expr}}});
 | |
|     };
 | |
|     if ($op == $WHILE) {
 | |
|         while (l_eval($car{$cdr{$expr}}) != $NIL) {
 | |
|             progn($cdr{$cdr{$expr}}); 
 | |
| 	}
 | |
|         return $NIL
 | |
|     }
 | |
|     if ($op == $DEFINE) {
 | |
|         $value{$car{$cdr{$expr}}} = l_eval($car{$cdr{$cdr{$expr}}});
 | |
|         return $car{$cdr{$expr}};
 | |
|     }
 | |
|     if ($op == $QUIT)   { exit(); }
 | |
|     
 | |
|     error(__LINE__,"BUG: Unknown special form");
 | |
| }
 | |
| 
 | |
| sub progn { 
 | |
|      local ($exprs) = @_;
 | |
|      for (; $cdr{$exprs} != $NIL; $exprs = $cdr{$exprs}) {
 | |
|          l_eval($car{$exprs});
 | |
|      }
 | |
|      return l_eval($car{$exprs});
 | |
| }
 | |
|  
 | |
| sub l_eval_rands { 
 | |
|     local ($rands) = @_;
 | |
|      for (; $rands != $NIL; $rands = $cdr{$rands}) {
 | |
|          $stack[$stack_ptr++] = l_eval($car{$rands});
 | |
|      }
 | |
| }
 | |
| 
 | |
| sub apply {
 | |
|      local ($proc) = @_;
 | |
|      if ($profiling) {
 | |
|          ++$call_count[$proc];
 | |
|      }
 | |
|      if (is_pair($proc) && $car{$proc} == $LAMBDA) {
 | |
|          extend_env($car{$cdr{$proc}});
 | |
|          $result = progn($cdr{$cdr{$proc}});# result is global to save stack space
 | |
|          unwind_env($car{$cdr{$proc}});
 | |
|          return $result;
 | |
|      }
 | |
|      if (defined($num_params{$proc}) &&
 | |
| 	$num_params{$proc} != $stack_ptr - $frame_ptr) {
 | |
|          error(__LINE__,'Wrong number of arguments to '.$printname{$cdr{$proc}}); }
 | |
|      if ($trace) { print "Proc called ".$printname{$cdr{$proc}}.
 | |
| 		" with ". ($stack_ptr - $frame_ptr) ." args"  . "\n"; }
 | |
|      if ($proc == $CAR)	 { return $car{ is($a_pair, $stack[$frame_ptr])}; }
 | |
|      if ($proc == $CDR)	 { return $cdr{ is($a_pair, $stack[$frame_ptr])}; }
 | |
|      if ($proc == $CONS) { return cons( $stack[$frame_ptr], $stack[$frame_ptr+1]); }
 | |
|      if ($proc == $NULL) { return $stack[$frame_ptr] == $NIL ? $T : $NIL;}
 | |
|      if ($proc == $EQ)	 { return $stack[$frame_ptr] == $stack[$frame_ptr+1] ? $T : $NIL}
 | |
|      if ($proc == $ATOMP){ return is_atom($stack[$frame_ptr]) ? $T : $NIL}
 | |
|      if ($proc == $ADD)	 { return is($a_number, $stack[$frame_ptr]) + is($a_number, $stack[$frame_ptr+1]); }
 | |
|     if ($proc == $SUB)	 { return is($a_number, $stack[$frame_ptr]) - is($a_number, $stack[$frame_ptr+1])}
 | |
|      if ($proc == $MUL)	 { return make_number(numeric_value($stack[$frame_ptr]) * numeric_value($stack[$frame_ptr+1])); }
 | |
|      if ($proc == $DIV)	 { return make_number(int(numeric_value($stack[$frame_ptr]) / numeric_value($stack[$frame_ptr+1]))); }
 | |
|      if ($proc == $MOD)	 { return make_number(numeric_value($stack[$frame_ptr]) % numeric_value($stack[$frame_ptr+1]))}
 | |
|      if ($proc == $LT)	 { return ($stack[$frame_ptr] + 0 < $stack[$frame_ptr+1] + 0) ? $T : $NIL}
 | |
| # what does this mean? a list of args to property.
 | |
|      if ($proc == $GET)	 { return
 | |
| 	 defined( $property{$stack[$frame_ptr], $stack[$frame_ptr+1]} ) ?
 | |
| 	 $property{ $stack[$frame_ptr], $stack[$frame_ptr+1] } : $NIL; }
 | |
|      if ($proc == $PUT) 	 { return
 | |
| 	( $property{$stack[$frame_ptr], $stack[$frame_ptr+1] } =
 | |
| 		$stack[$frame_ptr+2]); }
 | |
|      if ($proc == $CADR)	 { return $car{is($a_pair, $cdr{is($a_pair,
 | |
| 	$stack[$frame_ptr])})} ; }
 | |
|      if ($proc == $CDDR)	 { return $cdr{is($a_pair, $cdr{is($a_pair, $stack[$frame_ptr])})}; }
 | |
|      if ($proc == $LIST)	 { return listify_args()}
 | |
|      if ($proc == $SYMBOLP)  { return is_symbol($stack[$frame_ptr]) ? $T : $NIL}
 | |
|      if ($proc == $PAIRP)	 { return is_pair($stack[$frame_ptr]) ? $T : $NIL}
 | |
|      if ($proc == $NUMBERP)  { return is_number($stack[$frame_ptr]) ? $T : $NIL;}
 | |
|      if ($proc == $SETCAR)	 { return $car{is($a_pair, $stack[$frame_ptr] = $stack[$frame_ptr + 1])}; }
 | |
|      if ($proc == $SETCDR)	 { return $cdr{is($a_pair, $stack[$frame_ptr] = $stack[$frame_ptr+1])}; }
 | |
|      if ($proc == $APPLY)	 { return do_apply() }
 | |
|      if ($proc == $EVAL)	 { return l_eval($stack[$frame_ptr]) }
 | |
|      if ($proc == $NREV)	 { return nreverse($stack[$frame_ptr], $NIL)}
 | |
|      if ($proc == $LWRITE)  { lwrite_expr($stack[$frame_ptr]);
 | |
| 				printf(" ");  { return $NIL }}
 | |
|      if ($proc == $NEWLINE) { printf("\n");   return $NIL ; }
 | |
|      if ($proc == $LREAD)   { return lread(); }
 | |
| 
 | |
| # source file handling functions
 | |
|      if ($proc == $LOAD)    { $xyz = load($printname{$stack[$frame_ptr]});
 | |
| 			       return make_number($xyz); }
 | |
|      if ($proc == $INTER)   { return($interactive ? $T : $NIL); }
 | |
|      if ($proc == $FILE)    { return string_to_symbol($filename); }
 | |
|      if ($proc == $LINENO)  { return make_number(($lineno)+1); }
 | |
|      if ($proc == $TRACE)   { if ($trace) { $trace = 0;} 
 | |
| 				else { $trace = 1; } return $NIL;  }
 | |
| 
 | |
|      if ($proc == $RANDOM)  {
 | |
| 	return make_number(int(rand() * numeric_value($stack[$frame_ptr])))}
 | |
|      if ($proc == $GENSYM)  { return string_to_symbol("#G". ++$gensym_counter)}
 | |
|      if ($proc == $ERROR)   {
 | |
| 	printf("Error!\n"); print_expr(listify_args()); exit(1);
 | |
|      }
 | |
|      error(__LINE__,'Unknown procedure type');
 | |
|  }
 | |
| 
 | |
| sub load {
 | |
| 	local($file) = @_;
 | |
| 	local($err);
 | |
| 	$hand = new FileHandle;
 | |
| 	$err=open($hand,"<$file");
 | |
| 	if ($err <= 0 ) { return $err; }
 | |
| 	push @filestack, $filename;
 | |
| 	push @filestack, $filehandle;
 | |
| 	push @filestack, $lineno;
 | |
| 	push @filestack, $sline;
 | |
| 	push @filestack, $pline;
 | |
| 	push @filestack, $interactive;
 | |
| 	$lineno = 0;
 | |
| 	$filehandle = $hand;
 | |
| 	$filename = $file;
 | |
| 	$interactive = 0;
 | |
| 	$pline = "";
 | |
| }
 | |
| 
 | |
| sub endfile {
 | |
| 	$interactive = pop @filestack;
 | |
| 	$pline = pop @filestack;
 | |
| 	$sline = pop @filestack;
 | |
| 	$lineno = pop @filestack;
 | |
| 	$filehandle = pop @filestack;
 | |
| 	$filename = pop @filestack;
 | |
| }
 | |
| 
 | |
|  sub do_apply {
 | |
|      local($old_frame_ptr, $proc, $args) = @_;
 | |
|      $proc = $stack[$frame_ptr];
 | |
|      $args = $stack[$frame_ptr+1];
 | |
| 
 | |
|      $old_frame_ptr = $frame_ptr;
 | |
|      $frame_ptr = $stack_ptr;
 | |
| 
 | |
|      for (; is_pair($args); $args = $cdr{$args}) {
 | |
|          $stack[$stack_ptr++] = $car{$args};
 | |
|     }
 | |
|      if ($args != $NIL) {
 | |
|          error(__LINE__,"Bad argument to APPLY: not a proper list"); }
 | |
| 
 | |
|      $result = apply($proc);
 | |
|  
 | |
|      $stack_ptr = $frame_ptr;
 | |
|      $frame_ptr = $old_frame_ptr;
 | |
|      return $result;
 | |
| }
 | |
| 
 | |
| sub listify_args {
 | |
| 	local($p, $result)= @_;
 | |
| 
 | |
|      $result = $NIL;
 | |
|      for ($p = $stack_ptr - 1; $frame_ptr <= $p; --$p) {
 | |
|          $result = cons($stack[$p], $result);
 | |
|      }
 | |
|      return $result;
 | |
| }
 | |
| 
 | |
| # --- The environment
 | |
| 
 | |
| # Clobbers the stack frame.
 | |
|  sub extend_env {
 | |
| 	local( $vars ) = @_; local($p, $temp);
 | |
|      for ($p = $frame_ptr; $vars != $NIL; $vars = $cdr{$vars}) {
 | |
|          if ($p == $stack_ptr)  {
 | |
|  	    error(__LINE__,"Too many arguments to procedure");
 | |
| 	 }
 | |
|          $temp = $value{$car{$vars}};
 | |
|          $value{$car{$vars}} = $stack[$p];
 | |
|          $stack[$p] = $temp;
 | |
|          ++$p;
 | |
|      }
 | |
|      if ($p != $stack_ptr) {
 | |
|  	error(__LINE__,"Not enough arguments to procedure");
 | |
|      }
 | |
| }
 | |
| 
 | |
|  sub unwind_env {
 | |
| 	local ($vars, $p) = @_;
 | |
|      for ($p = $frame_ptr; $vars != $NIL; $vars = $cdr{$vars}) {
 | |
|          if (!defined ($stack[$p])) {
 | |
|  	    delete $value{$car{$vars}};
 | |
| 	} else {
 | |
|  	    $value{$car{$vars}} = $stack[$p];
 | |
| 	}
 | |
|  	++$p;
 | |
|      }
 | |
| }
 | |
| 
 | |
| # --- Output
 | |
| 
 | |
| sub print_expr {
 | |
|       local($expr)=@_;
 | |
|       lwrite_expr($expr);
 | |
|       print "\n";
 | |
|  }
 | |
| 
 | |
| sub lwrite_expr {
 | |
|      local($expr) = @_;
 | |
| 
 | |
|      if (is_atom($expr)) {
 | |
|          if (!is_symbol($expr)) {
 | |
|              printf("%d", numeric_value($expr));
 | |
|          } else {
 | |
|              if (!(defined($printname{$expr}))) {
 | |
|                  error(__LINE__,"BUG: ". $expr." has no printname");
 | |
| 	     }
 | |
|              printf("%s", $printname{$expr});
 | |
|          }
 | |
|      } else {
 | |
|          printf("(");
 | |
|          lwrite_expr($car{$expr});
 | |
|          for ($expr = $cdr{$expr}; is_pair($expr); $expr = $cdr{$expr}) {
 | |
|              printf(" ");
 | |
|              lwrite_expr($car{$expr});
 | |
|          }
 | |
|          if ($expr != $NIL) {
 | |
|              printf(" . ");
 | |
|              lwrite_expr($expr);
 | |
|          }
 | |
|          printf(")");
 | |
|      }
 | |
|  }
 | |
|  
 | |
| # --- Input
 | |
| 
 | |
| sub lread {
 | |
| # 	 my (@a);
 | |
| #	 foreach $i (@-) {
 | |
| #	    print '@'; lwrite_expr($i); print '@';
 | |
| #	 }
 | |
| #	 @a=do_lread(@_);
 | |
| #	 foreach $i (@a) {
 | |
| #	    print '#'; lwrite_expr($i); print '#';
 | |
| #	 }
 | |
| #	 return (@a);
 | |
| # }
 | |
| #  sub do_lread {
 | |
|      local($committed, $result) = @_;
 | |
|      skip_blanks();
 | |
|      if ($token eq $eof) {
 | |
|          if ($committed) {
 | |
|              error(__LINE__,"Unexpected EOF");
 | |
| 	} else {
 | |
|              return $THE_EOF_OBJECT;
 | |
| 	}
 | |
|      }
 | |
|      if ($token eq "(") {			# lread a list
 | |
|          advance();
 | |
|          $result = $NIL;
 | |
|          for (;;) {
 | |
|              skip_blanks();
 | |
|              if ($token eq ".") {
 | |
|                  advance();
 | |
|                  $after_dot = lread(1);
 | |
|                  skip_blanks();
 | |
|                  if ($token ne ")") {
 | |
|                      error(__LINE__,"')' expected");
 | |
| 		 }
 | |
|                  advance();
 | |
|                  return nreverse($result, $after_dot);
 | |
|              } elsif ($token eq ")" ) {
 | |
|                  advance();
 | |
|                  return nreverse($result, $NIL);
 | |
|              } else {
 | |
|                  protect($result);
 | |
|                  $result = cons(lread(1), $result);
 | |
|                  unprotect()
 | |
|              }
 | |
|          }
 | |
|      } elsif ($token eq "'") {		# a quoted expression
 | |
|          advance();
 | |
|          return cons($QUOTE, cons(lread(1), $NIL));
 | |
|      } elsif ($token =~  /^-?[0-9]+$/) {	# a number
 | |
|          $result = make_number($token);
 | |
|          advance();
 | |
|          return $result;
 | |
|      } else {				# a symbol
 | |
| 	if ($trace) { print "A symbol $token\n"; }
 | |
|          $result = string_to_symbol($token);
 | |
|          advance();
 | |
|          return $result;
 | |
|      }
 | |
|  }
 | |
|  
 | |
|  sub skip_blanks {
 | |
|      while ($token =~ /^[ \t]*$/) {
 | |
|          advance();
 | |
|      }
 | |
| }
 | |
| #
 | |
|  sub advance {
 | |
|      if ($token eq $eof) { return $eof; }
 | |
|      if ($token eq "") {
 | |
| 	RLOOP: for (;;) {
 | |
| 		$lineno++;
 | |
| 		if ( $interactive && ( ! defined($value{$DRIVER}) ) ) {
 | |
| 		  print "$lineno> "; }
 | |
| 		$pline = $sline;
 | |
| 		if ( ($line = <$filehandle> )  ne "" ) { last RLOOP; }
 | |
| 		if (@filestack > 0) { endfile(); }
 | |
|                  else {
 | |
| 		     $token = $eof;
 | |
| 		     return;
 | |
| 	         }
 | |
| 	}
 | |
| 	chop $line;
 | |
| 	$sline = $line;
 | |
|      }
 | |
|      if ( ($line =~ /^[(\)'.]/ ) ||
 | |
|          ($line =~/^[_A-Za-z0-9\.=!\@\$%&\*<>\?\+\\\-\/\:]+/) ||
 | |
|          ($line =~ /^[ \t]+/)) {
 | |
|          $token = substr($line, length($`), length($&));
 | |
|          $line = substr($line, length($&));
 | |
|      } elsif ($line eq "" || substr($line, 0, 1) eq ";") {
 | |
|          $token = "";	# this kludge permits interactive use
 | |
|      } else {
 | |
|          error(__LINE__,"Lexical error starting at ".$line);
 | |
|      }
 | |
|      if ($trace) { print ":$token:"; flush STDOUT; }
 | |
|  }
 | |
| 
 | |
| # --- Miscellany
 | |
| 
 | |
| # Destructively reverse :list and append :reversed_head.
 | |
| sub nreverse {
 | |
| 	local($list, $reversed_head, $tail) = @_;
 | |
| 
 | |
|     while (is_pair($list)) {		#** speed?
 | |
|         $tail = $cdr{$list};
 | |
|         $cdr{$list} = $reversed_head;
 | |
|         $reversed_head = $list;
 | |
|         $list = $tail;
 | |
|     }
 | |
|     if ($list != $NIL) {
 | |
|  	error(__LINE__,"Not a proper list - reverse!");
 | |
|     }
 | |
|     return $reversed_head;
 | |
| }
 | |
| 
 | |
| 
 | |
| sub error {
 | |
|     local($line,$reason)=@_;
 | |
|     print STDERR "Error $line \t: file-$filename line-$lineno $reason:\n";
 | |
|     print STDERR "     \t: $pline\n";
 | |
|     print STDERR " near $token\t: $sline\n";
 | |
|     $token="";
 | |
|     die($reason);
 | |
| }
 |