653 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			653 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
|  | #!/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); | ||
|  | } |