// Text of project 
C:\dev\Newton\MyProjects\LittleLisp\version 1.01\LittleLisp.ntk written on:
01/01/02 23:04:19
// Beginning of file LittleLispOutput.lyt
outputView :=
    {viewBounds: {left: -1, top: 68, bottom: 236, right: 213},
     SetText:
       func(string txt)
       begin
       	// Set the floating window's static text value and update the view.
       	SetValue(theText, 'text, txt);
       end,
     GetText:
       func()
       begin
       	// Return the value of the floating window's text field.
       	GetVariable(theText, 'text);
       end,
     _proto: @180
    };

theText := /* child of outputView */
    {viewBounds: {left: -1, top: 5, bottom: 151, right: 215},
     text: "",
     viewFont: editFont10,
     viewFlags: 2561,
     _proto: @185
    };
// View theText is declared to outputView




constant |layout_LittleLispOutput.lyt| :=  outputView;
// End of file LittleLispOutput.lyt
// Beginning of file GfxOutput.lyt
gfxWdw :=
    {viewBounds: {left: -1, top: 61, bottom: 277, right: 217}, _proto: @180};

canvas := /* child of gfxWdw */
    {viewBounds: {left: -1, top: 7, bottom: 207, right: 219},
     viewFlags: 67108897,
     viewFormat: 0,
     ViewQuitScript:
       // must return the value of inherited:?ViewQuitScript()
       func()
       begin
       	local lispVars := GetGlobals().littleLisp;
       
       	// Set turtle to the home position.
       	SetVariable(lispVars, 'turtle, {x:110, y:100, angle:270});
       	
       	// Empty the graphics view content array.
       	SetVariable(lispVars, 'gfxViewContents, []);
       	
       	inherited:?ViewQuitScript();
       end,
     ViewDrawScript:
       func()
       begin
       	// Redraw the contents of the graphics view.
       	local frame lispVars := GetGlobals().littleLisp;
       
       	for i:= 0 to Length(lispVars.gfxViewContents)-1 do
       	begin
       		local frame gfxObj := lispVars.gfxViewContents[i];
       		:DoDrawing('DrawShape, [gfxObj.shape, gfxObj.style]);
       	end;
       end,
     viewClass: 74
    };
// View canvas is declared to gfxWdw




constant |layout_GfxOutput.lyt| :=  gfxWdw;
// End of file GfxOutput.lyt
// Beginning of file InputDialog.lyt
inputDialogView :=
    {viewBounds: {left: -3, top: 90, bottom: 214, right: 186},
     viewFlags: 64,
     _proto: @180
    };

inputDialogTitle := /* child of inputDialogView */
    {title: "LittleLisp Input",
     viewBounds: {left: 2, top: 7, right: 177, bottom: 27},
     _proto: @229
    };



inputText := /* child of inputDialogView */
    {viewBounds: {left: 2, top: 42, right: 187, bottom: 97},
     text: "",
     viewFlags: 14849,
     ViewChangedScript:
       func(slot, view)
       begin
       	// Set the value of the modal dialog's text for reading elsewhere.
       	SetVariable(GetGlobals().littleLisp, 'inputText, Clone(text));
       end,
     _proto: @185
    };




constant |layout_InputDialog.lyt| :=  inputDialogView;
// End of file InputDialog.lyt
// Beginning of file LittleLisp.lyt
LittleLispMainView :=
    {title: "LittleLisp",
     viewBounds: {left: 0, top: 2, bottom: 318, right: 234},
     viewFormat: 83951953,
     writer:
       {	fromSexpr:	func(array Sexpr)
       				// Helper function for outputSexpr.
       				// Collects array of tokens.
       				// Returns a string to be printed.
       				begin
       					local tok;
       					local array toks := [];
       					local string output := "";
       					
       					// Convert internal S-expression to array of 
       					// printable tokens.												
       					:outputSexpr(DeepClone(Sexpr), toks);
       							
       					// Convert array into string.
       					foreach tok in toks do
       						output := output && tok;
       														
       					// Return string form of S-expression.	
       					output;
       				end;,		
       						
       	outputSexpr:func(array Sexpr, array toks)
       				// Given one or more S-expressions, generate 
       				// an array of tokens. Note that this function will be
       				// highly recursive for some S-expressions.
       				begin
       					local expr;
       																				
       					foreach expr in Sexpr do
       					begin
       						// Is this element a list?
       						if IsArray(expr) then
       						begin
       							// Left parenthesis. 
       							AddArraySlot(toks, "(");	
       							
       							// Add list to the token array.
       							:outputSexpr(expr, toks);
       																
       							// Right parenthesis.
       							AddArraySlot(toks, ")");
       						end;
       						else
       						if IsFrame(expr) then
       						begin
       							if HasSlot(expr, 'type) and expr.type = 'primitive then
       								// Built-in function.
       								AddArraySlot(toks, "<PRIMITIVE:" &&
       											 SPrintObject(expr.name) & ">");
       							else
       							if HasSlot(expr, 'type) and expr.type = 'lambda then
       							begin
       								// Lambda expression.
       								if expr.env <> nil then
       									AddArraySlot(toks, "<CLOSURE; arity" &&
       												 Length(expr.formals) & ">");
       								else
       									AddArraySlot(toks, "<LAMBDA; arity" &&
       												 Length(expr.formals) & ">");									
       							end
       							else
       								// Generic frame.
       								AddArraySlot(toks, "<FRAME>");
       						end
       						else
       						begin
       							// True?
       							if expr = True then AddArraySlot(toks, "T");
       							else
       							// nil?
       							if expr = nil then AddArraySlot(toks, "NIL"); 
       							else
       							// String? (exclude error strings and linefeeds (result separators)).
       							if IsString(expr) and not BeginsWith(expr, "Error:") and not StrEqual(expr, "\n") then
       								AddArraySlot(toks, $" & expr & $");
       							else 
       								// Must be some other atom, a linefeed (result separator)
       								// or an error message string.
       								AddArraySlot(toks, SprintObject(expr));
       						end;
       					end;
       				end;	},
     lexer:
       {	tokenize:	func(string s)
       				// Return an array of LittleLisp tokens given a string.
       				begin
       				  	local i := 0;
       				  	local max := strlen(s)-1;
       				  	local toks := [];
       				  	local tok;
       														
       				  	// Tokenise.
       					while i <= max do
       					begin
       						// Left or right parenthesis?
       						if s[i] = $( then begin AddArraySlot(toks, "("); i:=i+1; end;
       						else
       						if s[i] = $) then begin AddArraySlot(toks, ")"); i:=i+1; end;
       						else
       						// Quote?
       						if s[i] = $' then begin AddArraySlot(toks, "'"); i:=i+1; end;
       						else
       						// String?
       						if s[i] = $" then
       						begin
       							i:=i+1;
       							local string str := "\""; 
       							while i <= max and s[i] <> $" do
       							begin
       								str := str & s[i];
       								i:=i+1;
       							end;
       							str := str & "\"";
       							AddArraySlot(toks, str);
       							i:=i+1; // point to character after closing quote
       						end
       						else
       						begin
       							// Skip whitespace.
       							while i <= max and IsWhiteSpace(s[i]) do i:=i+1;
       
       							// Comment? (;...)
       							if i <= max and s[i] = $; then 
       							begin
       								i:=i+1;
       								while i <= max and s[i] <> $\n do i:=i+1;
       								if i <= max then i:=i+1; // point to next character
       							end;
       									
       							// Collect next token.
       							tok := "";
       							while i <= max and 
       								  not IsWhiteSpace(s[i]) and
       								  s[i] <> $( and s[i] <> $) and s[i] <> $' and
       								  s[i] <> $; and s[i] <> $" do 
       							begin
       								tok := tok & s[i];
       								i:=i+1;
       							end;
       							
       							// Make sure we have a token to add.
       							if strlen(tok) > 0 then AddArraySlot(toks, DownCase(tok));
       						end;
       					end;
       				  	
       				  	// Return array of tokens (strings).
       				  	toks;		
       				end;	},
     reader:
       {	parensBalanced:	
       						func(array toks)
       						// Same number of left as right parentheses?
       						// This check could either be here or in lexer
       						// but we're starting to assign some meaning here
       						// so reader is probably the most suitable object.
       						begin
       							local tok;
       							local int lparens := 0;
       							local int rparens := 0;
       							
       							foreach tok in toks do
       								if StrEqual(tok,"(") then lparens := lparens + 1;
       								else
       								if StrEqual(tok,")") then rparens := rparens + 1;
       							
       							lparens = rparens;
       						end;,
       
       			toSexpr:	func(array toks)
       						// Read each token from an array and convert to
       						// internal form, ie. an S-expression. The latter
       						// is defined here to be a number, symbol or list. 
       						// 
       						// There are no pairs in the sense that Scheme uses 
       						// them, ie. the cdr of a list is always a list.
       						//
       						// This function is only a helper for mkSexpr.
       						// Since the array of tokens is going to be mutated, 
       						// clone it in case we want to use the original for 
       						// some other purpose.
       						begin
       							local SexprArray;
       							
       							// Collect S-expressions.
       							SexprArray := :mkSexpr(DeepClone(toks), []);
       														
       							// Convert all occurrences of 'arg to ['quote, arg].
       							SexprArray := :convertQuotes(SexprArray);
       								
       							// Return internal form of S-expression(s).
       							SexprArray;
       						end;,		
       
       			convertQuotes:
       						func(array SexprArray)
       						// Handle the special case of converting each ' to 
       						// a "quote" function call.
       						//
       						begin
       							local i, max;
       							local array newSexprArray := [];
       							
       							i:=0;
       							max:=Length(SexprArray)-1;
       
       							while i<=max do
       							begin
       								if SexprArray[i] <> '|'| then
       								begin
       									// List or atom? Convert quotes in a list.
       									//
       									if IsArray(SexprArray[i]) then
       										AddArraySlot(newSexprArray, 
       													 :convertQuotes(SexprArray[i]));
       									else
       										AddArraySlot(newSexprArray, SexprArray[i]);										
       								end;
       								else
       								begin
       									// Quoted S-expression.
       									
       									// If there's a single quote at the end of the array
       									// let it fail in evaluate:quote().
       									//
       									if i = max then AddArraySlot(newSexprArray, ['quote]);
       									else
       									begin
       								      // Point to the object to quoted.
       								      //
       									  i:=i+1;
       									  
       									  // List or atom? Convert quotes in a list.
       									  //
       									  if IsArray(SexprArray[i]) then
       										AddArraySlot(newSexprArray, 
       												 	 ['quote, :convertQuotes(SexprArray[i])]);
       									  else
       										AddArraySlot(newSexprArray, 
       											 	 	 ['quote, SexprArray[i]]);	
       									end;
       								end;
       								
       								i:=i+1;
       							end;
       							
       							return newSexprArray;
       						end;,
       																		
       			mkSexpr:	func(array toks, array Sexpr)
       						// Return an S-expression given an array
       						// of tokens. Note that this function will be
       						// highly recursive for some S-expressions.
       						begin
       							while Length(toks) > 0 do 
       							begin		
       								// Are we at the start of a new list?
       								if StrEqual(toks[0],"(") then 
       								begin
       									// Remove the zeroth token.
       									ArrayRemoveCount(toks, 0, 1);
       									
       									// Recursively add a list.
       									AddArraySlot(Sexpr, :mkSexpr(toks, []));
       								end;
       								else
       								// Right parenthesis? Just remove it and return.
       								if StrEqual(toks[0],")") then
       								begin
       									// Remove the zeroth token.
       									ArrayRemoveCount(toks, 0, 1);
       										
       									// We're finished with this list, so exit loop
       									// and return the function's value.
       									break;
       								end;
       								else
       								begin
       									// Is it a number?
       									local n := StringToNumber(toks[0]);
       									if IsNumber(n) then AddArraySlot(Sexpr, n);
       									else
       									// True?
       									if StrEqual(toks[0], "T") then AddArraySlot(Sexpr, True);
       									else
       									// nil?
       									if StrEqual(toks[0], "NIL") then AddArraySlot(Sexpr, nil);
       									else
       									// String?
       									if IsString(toks[0]) and
       												BeginsWith(toks[0], "\"") and 
       												EndsWith(toks[0], "\"") then AddArraySlot(Sexpr, SubStr(toks[0], 1, StrLen(toks[0])-2));
       									else
       										// It's a symbol.
       										AddArraySlot(Sexpr, Intern(toks[0]));
       										
       									// Remove the zeroth token.
       									ArrayRemoveCount(toks, 0, 1);
       								end;	
       							end;		
       							
       							// Return internal form of S-expression.
       							Sexpr;								
       						end;	},
     evaluate:
       {	eval:	func(array SexprArray, boolean breakEnabled)
       			// Helper function for doEval.
       			// Evaluates all S-expressions in input buffer.
       			//
       			begin
       				local expr;
       				local result;
       				local evaluatedSexprArray := [];
       				local lispVars := GetGlobals().littleLisp;
       
       				// Collect results.
       				foreach expr in SexprArray do 
       				begin
       					try
       						local result;
       						if breakEnabled then
       						begin
       							// If in break mode, wrap the evaluation up in a progress slip.
       							local evalProgress := func(progressView)
       							begin
       								SetVariable(lispVars, 'evalProgressView, progressView);
       								// Take a snapshot of the result in case its mutated later.
       								result := DeepClone(:doEval(expr));
       							end;
       							result := nil;
       							DoProgress('vBarber, {closebox: true, barber: true,
       												  statusText: kAppName, 
       												  titleText: "Evaluating..."}, evalProgress);
       						end
       						else
       						begin
       							// Not in break mode, so don't invoke a progress slip.
       							SetVariable(lispVars, 'evalProgressView, nil);
       							// Take a snapshot of the result in case its mutated later.
       							result := DeepClone(:doEval(expr));	
       						end;
       
       				  	onexception |evt.ex.msg;littlelisperr| do
       						result := CurrentException().message;
       
       					AddArraySlot(evaluatedSexprArray, result);
       					AddArraySlot(evaluatedSexprArray, "\n"); // separate each result											
       				end;
       					
       				// Return final value(s). This may include error messages.
       				return evaluatedSexprArray;
       			end;,
       			
       	doEval:	func (Sexpr)
       			// Evaluate an S-expression.
       			begin
       				local fun;
       				local value;
       				
       				// Update the progress bar every 2 seconds if we're in break mode.
       				local evalProgressView := GetGlobals().littleLisp.evalProgressView;
       				if evalProgressView then
       				begin
       					if TimeInSeconds() mod 2 = 0 then 
       						evalProgressView:SetStatus('vBarber, {barber: true});
       				end;
       						
       				// Is this S-expression a list?
       				if IsArray(Sexpr) then
       				begin	
       					// Empty list?
       					if Length(Sexpr) = 0 then
       						return nil;
       					else
       					// Non-empty list; assume a function application or special form.
       					begin
       						// If the head of this list another list (e.g. a lambda
       						// expression, a cond), this must first be evaluated.
       						local head := Sexpr[0];
       						if IsArray(head) then
       							fun := :doEval(head); // could we always do this?
       						else
       							fun := head;
       
       						// Remove function/form name, leaving us with the argument list.
       						ArrayRemoveCount(Sexpr,0,1);
       						
       						// Special form?
       						//
       						if fun = '|and| then return :andOp(Sexpr);  
       						else
       						if fun = '|begin| then return :beginForm(Sexpr);  
       						else
       						if fun = 'cond then return :cond(Sexpr);
       						else			
       						if fun = '|if| then return :ifStatement(Sexpr);
       						else			
       						if fun = 'defun then return :defun(Sexpr);
       						else						
       						if fun = 'lambda then return :lambda(Sexpr);
       						else						
       						if fun = '|let*| then return :letStar(Sexpr);
       						else						
       						if fun = '|or| then return :orOp(Sexpr);  
       						else
       						if fun = 'quote then return :quote(Sexpr);
       						else							
       						if fun = '|repeat| then return :repeatExpr(Sexpr); 
       						else
       						if fun = 'setq then return :setq(Sexpr); 
       						else 						
       						if fun = '|while| then return :whileLoop(Sexpr);  						
       						else
       						if fun = 'error then return :error(Sexpr);  
       						else
       						if fun = '|frame| then return :mkFrame(Sexpr);
       						else
       						begin						
       							// Apply a built-in (primitive) or user-defined function.
       							// Convert empty list to nil.
       							value := :Apply(fun, Sexpr, true);
       							if IsArray(value) and Length(value) = 0 then value := nil;
       							return value;
       						end;
       					end;	
       				end;
       				else
       				begin					
       					// True, nil, numbers, and strings stand for themselves.
       					if Sexpr = True or Sexpr = nil or IsNumber(Sexpr) or IsString(Sexpr) then 
       						return Sexpr;
       					else
       					begin
       						// Lookup symbol and return value, or error.
       						// Convert empty list to nil.
       						// The symbol could be a list or function.
       						value := :GetLispVar(Sexpr);
       						if IsArray(value) and Length(value) = 0 then value := nil;
       						return value;
       					end;
       				end;
       			end;,	
       			
       	Apply:	func (fun, array SexprArray, boolean eval)
       			begin
       				local array args;
       
       				// Evaluate all arguments except if instructed not to
       				// (e.g. MAP has already evaluated its args).
       				if eval then
       					args := :evalArgList(SexprArray);
       				else
       					args := SexprArray;
       
       				// *** Apply the user-defined or primitive function to the arguments. ***
       				// *** User-defined functions may shadow primitive functions. ***
       															
       				// Built-in (primitive) function?
       				if :IsPrimitiveFunc(fun) then
       					// Function already evaluated.
       					return :PrimitiveFunCall(fun, args);
       				else
       				begin
       					// An atom which is bound to a built-in function?
       					// It is only necessary to look in the global environment for these!
       					local frame lispVars := GetGlobals().littleLisp;					
       					local funvalue := nil;
       					if IsSymbol(fun) and lispVars.globalEnv.(fun) exists then
       						funvalue := GetVariable(lispVars.globalEnv, fun);
       				 	if funvalue <> nil and :IsPrimitiveFunc(funvalue) then 
        						return :PrimitiveFunCall(funvalue, args);
       				 	else
       					// It's now either a function or an error.
       					if IsSymbol(fun) and (SPrintObject(fun))[0] = $# then
       						// Assume a NewtonScript function.
       						return :nsApply(fun, args);
       					else
       						// User-defined function (named or lambda expression).
       						return :FunCall(fun, args);
       				end;
       			end;,
       						
       	evalArgList:
       			func (array SexprArray)
       			// Return a list containing the evaluated 
       			// contents of the argument list. Stop if
       			// an error has occurred.
       			//
       			begin
       				local array argList := [];
       				local arg;
       				
       				if Length(SexprArray) > 0 then
       					// Evaluate each input list member,
       					// building a new list.
       					//
       					foreach arg in SexprArray do
       					begin
       						AddArraySlot(argList, :doEval(arg));
       					end;
       					
       				// Return new list (which may be empty).
       				return argList;
       			end;,
       
       	IsPrimitiveFunc:	
       				func(funvalue)
       				begin
       					// Is the supplied argument a built-in function?
       					return IsFrame(funvalue) and funvalue.type = 'primitive;
       				end;,
       
       	PrimitiveFunCall:
       				func(frame funvalue, array args)
       				// Apply a built-in function to an argument list.
       				begin
       					local array argList;
       
       					if funvalue.fun exists then
       						argList := [funvalue, args]; // eg. see math functions
       					else
       						argList := [args];
       						
       			 		return Perform(self, funvalue.funName, argList);  
       				end;,
       				
       	IsUserFunc:	func(funvalue)
       				begin
       					// Is the supplied argument a user-defined function?
       					return IsFrame(funvalue) and funvalue.type = 'lambda;
       				end;,
       				
       	FunCall:	func(fun, array argList)
       				// Apply a user-defined function to an argument list.
       				begin
       					local funvalue;
       					local string argCount;
       					local string msg;
       					local integer i;
       					local lispVars := GetGlobals().littleLisp;
       					local args := {};
       					local integer localFrameCount := 1; // actual params frame
       					local Sexpr;
       					local result := nil;
       
       					// Get the function's value.
       					//
       					if :IsUserFunc(fun) then
       						funvalue := fun; // lambda expression
       					else
       					begin
       						// Hopefully a bound lambda expression (from defun or setq).
       						if not IsSymbol(fun) then
       							:SetLispError("expected a function, but got " & 
       										  lispVars.mainView.writer:fromSexpr([fun]));						
       				 		funvalue := :GetLispVar(fun);
       					 	if not :IsUserFunc(funvalue) then 
       							:SetLispError("not a function - " & SPrintObject(fun));
       				 	end;
       
       				 	// Check number of actual parameters against formals.
       				 	//
       				 	if Length(funvalue.formals) <> Length(argList) then
       				 	begin				 		
       				 		local string funcName := SPrintObject(fun);
       				 		UpCase(funcName);
       				 		msg := funcName && "requires exactly" &&
       				 			   Length(funvalue.formals) && "argument";
       				 		if Length(funvalue.formals) <> 1 then msg := msg & "s";
       				 		msg := msg & ".";
       				 		:SetLispError(msg);
       				 	end;
       
       				 	// Extend the local environment by adding the lambda expression's
       				 	// definition-time environment, if any, and binding formals to actuals.
       				 	// A DEFUN isn't a closure so it won't have a local environment apart
       				 	// from the actual parameters.
       				 	//
       					if funvalue.env <> nil then
       					begin
       						// Lambda's environment. Can be mutated, e.g. by SETQ.
       				 		AddArraySlot(lispVars.localEnv, funvalue.env);
       				 		localFrameCount := localFrameCount+1;
       				 	end;
       
       				 	i:=0;
       				 	while i < Length(funvalue.formals) do
       				 	begin
       				 		local arg := argList[i];
       				 		// Actual parameters are passed by *value*.
       				 		if not :IsAtom(arg) then arg := DeepClone(arg);
       				 		SetVariable(args, funvalue.formals[i], arg);
       				 		i:=i+1;
       				 	end;
       
       			 		AddArraySlot(lispVars.localEnv, args); // actual parameters
       						
       					// Evaluate the function body in the current environment.
       					// Work with a copy of the function body since :doEval()
       					// will remove the head of any list-based S-expressions.
       					//
       					foreach Sexpr in DeepClone(funvalue.body) do
       					begin
       						result := :doEval(Sexpr);
       					end;
       					
       					// Remove the frames that were added to the environment.
       					ArrayRemoveCount(lispVars.localEnv,
       									 Length(lispVars.localEnv)-localFrameCount,
       									 localFrameCount);
       
       					// Return the result of the last expression.
       					return result;
       				end;,
       				
       	SetLispVar:
       			func(name, value)
       			begin
       				local lispVars, i;
       	
       				// Bind a symbol to a value in the local or global environment
       				// and return this value.
       				//				
       				lispVars := GetGlobals().littleLisp;
       				
       				// Is the symbol in the local environment?
       				// Search backwards from most recently placed on the stack
       				// (:FunCall() adds to a stack of frames). 
       				//
       				if Length(lispVars.localEnv) > 0 then
       				begin
       					i := Length(lispVars.localEnv)-1;  
       					while i >= 0 do
       					begin
       						if lispVars.localEnv[i].(name) exists then
       						begin
       							SetVariable(lispVars.localEnv[i], name, value);
       							return value;
       						end;
       					
       						i := i - 1;
       					end;
       				end;
       				
       				// No, so create this name-value pair in the global environment.
       				//					
       				SetVariable(lispVars.globalEnv, name, value);
       				
       				// Add this name to the bound symbols popup menu list
       				// unless it's already present.
       				local String theName := SPrintObject(name);
       				
       				local found := nil;
       				
       				for i:=0 to Length(lispVars.symbolBindings)-1 do
       					if StrExactCompare(lispVars.symbolBindings[i].item, theName) = 0 then
       				begin
       					found := true; 			
       					break;
       				end;
       					
       				if not found then 
       				begin
       					AddArraySlot(lispVars.symbolBindings, {item: theName});			
       					Sort(lispVars.symbolBindings, '|str<|, 'item);
       				end;
       				
       				// Return the value of the symbol.
       				return value;
       			end;,
       			
       	GetLispVar:
       			func(name)
       			begin
       				local lispVars, i, value;
       				
       				// Return the value to which a symbol in the local or global
       				// environment is bound.
       				//
       				lispVars := GetGlobals().littleLisp;
       				
       				// Is symbol in the local environment?
       				// Search backwards from most recently placed on the stack
       				// (apply() creates a stack of frames). 
       				//
       				if Length(lispVars.localEnv) > 0 then
       				begin
       					i := Length(lispVars.localEnv)-1;  
       					while i >= 0 do
       					begin
       						if lispVars.localEnv[i].(name) exists then
       							return GetVariable(lispVars.localEnv[i], name);
       					
       						i := i - 1;
       					end;
       				end;
       
       				// No, so it's either in the global environment or unbound.
       				//				
       				if lispVars.globalEnv.(name) exists then
       					return GetVariable(lispVars.globalEnv, name);
       				else
       				begin
       					:SetLispError("unbound variable -" && SprintObject(name));
       				end;
       			end;,
       
       	/***********************************/
       	/* Built-in functions (primitives) */
       	/***********************************/			
       	
       	car:	func(array argList)
       			// Return the car of a non-null list, otherwise error.
       			//
       			// syntax: (car list)
       			//
       			begin
       				local arg;
       
       				if Length(argList) <> 1 then
       				begin
       					:SetLispError("CAR requires 1 argument.");
       				end;
       				else
       				begin				
       					// Get the argument.
       					arg := argList[0];
       						
       					// Argument must be a non-empty list.
       					if arg = nil then
       					begin
       						:SetLispError("CAR is undefined for an empty list.");
       					end;
       					else
       					if not IsArray(arg) then
       					begin
       						:SetLispError("CAR requires a list argument.");
       					end;
       					else
       						// Return head of list.
       						return arg[0];	
       				end;
       			end;,
       
       	cdr:	func(array argList)
       			// Return the cdr of a list, otherwise error.
       			//
       			// syntax: (cdr list)
       			//
       			begin
       				local arg, argCopy;
       
       				if Length(argList) <> 1 then
       				begin
       					:SetLispError("CDR takes 1 argument.");
       				end;
       				else
       				begin				
       					// Get the argument.
       					arg := argList[0];
       						
       					// Argument must be a list or nil (empty list).
       					if arg = nil then
       						// Empty list.
       						return nil;
       					else
       					if not IsArray(arg) then
       					begin
       						:SetLispError("CDR requires a list argument.");
       					end;
       					else
       					begin
       						// Return tail of list.
       						argCopy := DeepClone(arg);
       						ArrayRemoveCount(argCopy,0,1);
       						if Length(argCopy) = 0 then
       							// Empty list.
       							return nil;
       						else
       							return argCopy;	
       					end;
       				end;
       			end;,
       	
       	cons:	func(array argList)
       			// Return the cons of two S-expressions, the second
       			// of which must be a list, otherwise return error.
       			//
       			// syntax: (cons S-expression list)
       			//
       			begin
       				local arg1, arg2;
       
       				if Length(argList) <> 2 then
       				begin
       					:SetLispError("CONS requires 2 arguments.");
       				end;
       				else
       				begin				
       					// Get the arguments.
       					arg1 := argList[0];
       					arg2 := argList[1];
       							
       					// Second argument must be a list or nil (empty list).
       					if arg2 = nil then
       						// cons first argument onto empty list.
       						return [arg1];
       					else
       					if not IsArray(arg2) then
       					begin
       						:SetLispError("the second argument to CONS must be a list.");
       					end;
       					else
       					begin
       						if IsArray(arg1) then arg1 := DeepClone(arg1); // avoid side effects
       						// cons first argument onto start of second argument.
       						return ArrayInsert(DeepClone(arg2),arg1,0);
       					end;
       				end;
       			end;,
       	
       	append:	func(array argList)
       			// Syntax: (APPEND [list1 ... listN])
       			// Returns the contents of each list appended together as a new list.
       			begin
       				local array newList := [];
       				local array expr;
       
       				foreach expr in argList do
       				begin
       					if expr <> nil then
       					begin
       						if not IsArray(expr) then
       							:SetLispError("non-list argument to APPEND.");
       						else
       						begin
       							local item;
       							foreach item in expr do
       							begin
       								// Avoid side effects by cloning each list item.
       								if IsArray(item) then item := DeepClone(item);
       								AddArraySlot(newList, item);
       							end;
       						end;
       					end;
       				end;
       				
       				return newList;
       			end;,
       				
       	eq:		func(array argList)
       			// Performs a simple non-structural test for the identity of 2 expressions.
       			// This is essentially a test for pointer or atomic identity.
       			//
       			// syntax: (eq? S-expression S-expression)
       			//
       			begin
       				if Length(argList) <> 2 then
       					:SetLispError("EQ? requires 2 arguments.");
       				else
       					// Symbol, Number, T, NIL, identical references.
       					return argList[0] = argList[1];
       			end;,
       	
       			// Is the argument an empty list or NIL?
       	IsNull:	func(arg) return (IsArray(arg) and Length(arg) = 0) or arg = nil;,
       	
       			// Return true or false indicating whether the argument is an atom.
       	IsAtom: func(arg) return IsSymbol(arg) or IsNumber(arg) or arg = True or arg = nil;,
       					
       	equal:	func(array argList)
       			// Return True or nil indicating whether two S-expressions
       			// are structurally identical.
       			//
       			// syntax: (equal? s-expression s-expression)
       			//
       			begin
       				local array argCopy;
       				
       				if Length(argList) <> 2 then
       				begin
       					:SetLispError("EQUAL? requires 2 arguments.");
       				end;
       				else
       				begin	
       					// Invoke the function that does the real work
       					// but first make a copy of the arguments since
       					// "equalq" may remove list elements.
       					argCopy := DeepClone(argList);
       					return :equalq(argCopy[0], argCopy[1]);					
       				end;				
       			end;,
       
       
       	equalq:	func(arg1, arg2)
       			// Tests for structural equivalence of two S-expressions.
       			// See "equal" function above.
       			// If both arguments are the empty list, they're equivalent.
       			// If both arguments are atoms, test for their equivalence.
       			// If both arguments are strings, test for their equivalence.
       			// If both arguments are lists, ask whether the head and tail 
       			// of each list is equivalent. Otherwise, there can be no 
       			// equivalence.
       			begin
       				local heads;
       				if :IsNull(arg1) and :IsNull(arg2) then return true;
       				else
       				if :IsAtom(arg1) and :IsAtom(arg2) then return arg1 = arg2;
       				else
       				if IsString(arg1) and IsString(arg2) then return StrEqual(arg1,arg2);
       				else
       				if IsArray(arg1) and IsArray(arg2) then
       				begin
       					heads := :equalq(arg1[0], arg2[0]);
       					ArrayRemoveCount(arg1,0,1);
       					ArrayRemoveCount(arg2,0,1);
       					return heads and :equalq(arg1, arg2);
       				end
       				else
       					return nil;
       			end;,
       			
       	member:	func(array argList)
       			// Return the first sublist of list whose car is obj. If obj is 
       			// not found, nil is returned. The test for equality is equal?, ie.
       			// structural equivalence.
       			//
       			// syntax: (member obj list)
       			//
       			begin
       				local arg1;
       				local array arg2;
       				
       				if Length(argList) <> 2 then
       				begin
       					:SetLispError("MEMBER requires 2 arguments.");
       				end
       				else
       				if argList[1] = nil then
       					return nil;
       				else
       				if not IsArray(argList[1]) then
       				begin
       					:SetLispError("MEMBER - second argument must be a list.");
       				end
       				else
       				begin	
       					// Invoke the function that does the real work
       					// but first make a copy of the second argument 
       					// since the member* functions may remove their elements.
       					arg1 := argList[0];
       					arg2 := DeepClone(argList[1]);
       					if :IsAtom(arg1) then
       						return :memberAtom(arg1, arg2);					
       					else
       					if IsString(arg1) then
       						return :memberString(arg1, arg2);					
       					else
       						return :memberList(arg1, arg2);										
       				end;				
       			end;,
       
       	memberAtom:
       			func(x, array L)
       			// Return the first sublist of L whose car is the atom x. If x is 
       			// not found, nil is returned.
       			begin
       				while (Length(L) > 0) do
       				begin
       					if :IsAtom(L[0]) and x = L[0] then return L;
       					ArrayRemoveCount(L,0,1);					
       				end;
       				
       				return nil;
       			end;,
       			
       	memberString:
       			func(string x, array L)
       			// Return the first sublist of L whose car is the string x. If x is 
       			// not found, nil is returned.
       			begin
       				while (Length(L) > 0) do
       				begin
       					if IsString(L[0]) and StrEqual(x, L[0]) then return L;
       					ArrayRemoveCount(L,0,1);					
       				end;
       				
       				return nil;
       			end;,
       
       	memberList:
       			func(array x, array L)
       			// Return the first sublist of L whose car is the list x. If x is 
       			// not found, nil is returned.
       			begin
       				while (Length(L) > 0) do
       				begin
       					if IsArray(L[0]) and :equalq(DeepClone(x), DeepClone(L[0])) then return L;
       					ArrayRemoveCount(L,0,1);					
       				end;
       				
       				return nil;
       			end;,
       
       	atom:	func(array argList)
       			// Return True or nil indicating whether the argument
       			// is an atom. Note that nil is both an atom and a list.
       			//
       			// syntax: (atom? S-expression)
       			//			
       			begin
       				local arg;
       				
       				if Length(argList) <> 1 then
       				begin
       					:SetLispError("ATOM? requires 1 argument.");
       				end;
       				else
       				begin	
       					// Get the argument.
       					arg := argList[0];
       					
       					// Is the argument an atom?
       					return IsSymbol(arg) or IsNumber(arg) or arg = True or arg = nil;
       				end;				
       			end;,
       			
       	listp:	func(array argList)
       			// Return True or nil indicating whether the argument
       			// is a list. Note that nil is both an atom and a list.
       			//
       			// syntax: (list? S-expression)
       			//			
       			begin
       				local arg;
       				
       				if Length(argList) <> 1 then
       					:SetLispError("LIST? requires 1 argument.");
       				else
       				begin	
       					// Get the argument.
       					arg := argList[0];
       					
       					// Is the argument a list?
       					return IsArray(arg) or :IsNull(arg);
       				end;				
       			end;,
       
       	null:	func(array argList)			
       			// Return True or nil indicating whether the argument
       			// is an empty list, or NIL.
       			//
       			// syntax: (null? S-expression)
       			//			
       			begin
       				local arg;
       				
       				if Length(argList) <> 1 then
       				begin
       					:SetLispError("NULL? requires 1 argument.");
       				end;
       				else
       				begin	
       					// Get the argument.
       					arg := argList[0];
       					
       					// Is the argument an empty list or NIL?
       					return (IsArray(arg) and Length(arg) = 0) or arg = nil;
       				end;				
       			end;,
       
       	plus:	func(array argList)
       			// Sum a list of numbers. If the list length 
       			// is 1, this amounts to the number itself.
       			// Some Lisp systems return 0 when no arguments
       			// are supplied.
       			//
       			begin
       				local arg;
       				local real sum := 0;
       				
       					if Length(argList) = 0 then
       					begin
       						:SetLispError("'+' requires at least 1 argument.");
       					end;
       					else
       					begin	
       						foreach arg in argList do
       						if IsNumber(arg) then 
       							sum:=sum+arg;				
       						else
       						begin
       							:SetLispError("not a number -" && SprintObject(arg));
       						end;
       						
       						return sum;
       					end;
       			end;,
       				
       	minus:	func(array argList)
       			// Subtract a list of numbers. If the list length 
       			// is 1, subtract the number from zero.
       			//
       			begin
       				local i, len, arg;
       				local real result;
       				
       					if Length(argList) = 0 then
       					begin
       						:SetLispError("'-' requires at least 1 argument.");
       					end;
       					else
       					begin	
       						len := Length(argList);
       			
       						// Unary negation or subtraction of values?			
       						if len = 1 then 
       							return 0-argList[0]; 
       						else 
       						begin
       							result:=argList[0];
       						
       							for i:=1 to len-1 do
       							begin
       								arg := argList[i];
       								
       								if IsNumber(arg) then 
       									result:=result-arg;				
       								else
       								begin
       									:SetLispError("not a number -" && SprintObject(arg));
       								end;
       							end;
       						end;
       						
       						return result;
       					end;
       			end;,
       			
       	multiply:
       			func(array argList)
       			// Multiply a list of numbers. If the list length 
       			// is 1, multiply the number by 1.
       			//
       			begin
       				local arg;
       				local real product := 1;
       				
       					if Length(argList) = 0 then
       					begin
       						:SetLispError("'*' requires at least 1 argument.");
       					end;
       					else
       					begin	
       						foreach arg in argList do
       						if IsNumber(arg) then 
       							product:=product*arg;				
       						else
       						begin
       							:SetLispError("not a number -" && SprintObject(arg));
       						end;
       							
       						return product;
       					end;
       			end;,
       
       	divide:	func(array argList)
       			// Divide a list of numbers. If the list length 
       			// is 1, take the reciprocal of the number. Note 
       			// that some Lisp systems seem to give rational
       			// numbers as results (eg. (/ 1 12) => 1/12). 
       			// Here, results are given as floating point.
       			//
       			begin
       				local integer i;
       				local integer len;
       				local arg;
       				local real quotient;
       				
       					if Length(argList) = 0 then
       					begin
       						:SetLispError("'/' requires at least 1 argument.");
       					end;
       					else
       					begin	
       						len := Length(argList);
       						
       						// Reciprocal or division of list of numbers?
       						if len = 1 then
       						begin
       							// Reciprocal.
       							arg := argList[0];
       							if IsNumber(arg) then 
       							begin
       								if arg = 0 then 
       								begin
       									:SetLispError("zero divisor");
       								end;
       								else
       									return 1/arg;
       							end
       							else
       							begin
       								:SetLispError("not a number -" && SprintObject(arg));
       							end;
       						end
       						else
       						begin
       							// Division of a list of numbers.
       							quotient:=argList[0];
       							
       							for i:=1 to len-1 do
       							begin
       								arg := argList[i];
       								
       								if IsNumber(arg) then 
       								begin
       									if arg = 0 then 
       									begin
       										:SetLispError("zero divisor");
       									end;
       									else
       										quotient:=quotient/arg;				
       								end;
       								else
       								begin
       									:SetLispError("not a number -" && SprintObject(arg));
       								end;
       							end;
       						end;
       						
       						return quotient;
       					end;
       			end;,
       
       	equals:	func(array argList)
       			// Return True or nil indicating whether all numeric 
       			// values are identical in magnitude. 
       			//
       			// syntax: (= x1 x2 .. xN)
       			//
       			begin
       				local arg1, arg2, i;
       				
       				if Length(argList) < 1 then
       				begin
       					:SetLispError("'=' requires at least one argument.");
       				end;
       				else
       				begin	
       					arg1 := argList[0];
       					if not IsNumber(arg1) then 
       					begin
       						:SetLispError("not a number -" && SprintObject(arg1));
       					end;
       					
       					i := 1;
       					while i < Length(argList) do
       					begin
       						arg2 := argList[i];
       						if not IsNumber(arg2) then 
       						begin
       							:SetLispError("not a number -" && SprintObject(arg2));
       						end;
       						
       						if arg1 <> arg2 then return nil;
       						arg1 := arg2;
       						i := i + 1;
       					end;
       					
       					return True;						
       				end;				
       			end;,
       
       	gt:	func(array argList)
       			// Return True or nil indicating whether each numeric 
       			// value in a sequence is greater in magnitude than the
       			// one before it. 
       			//
       			// syntax: (> x1 x2 .. xN)
       			//
       			begin
       				local arg1, arg2, i;
       				
       				if Length(argList) < 1 then
       				begin
       					:SetLispError("'>' requires at least one argument.");
       				end;
       				else
       				begin	
       					arg1 := argList[0];
       					if not IsNumber(arg1) then return 
       					begin
       						:SetLispError("not a number -" && SprintObject(arg1));
       					end;
       					
       					i := 1;
       					while i < Length(argList) do
       					begin
       						arg2 := argList[i];
       						if not IsNumber(arg2) then 
       						begin
       							:SetLispError("not a number -" && SprintObject(arg2));
       						end;
       						
       						if arg1 <= arg2 then return nil;
       						arg1 := arg2;
       						i := i + 1;
       					end;
       					
       					return True;						
       				end;				
       			end;,
       
       	lt:	func(array argList)
       			// Return True or nil indicating whether each numeric 
       			// value in a sequence is lesser in magnitude than the
       			// one before it. 
       			//
       			// syntax: (< x1 x2 .. xN)
       			//
       			begin
       				local arg1, arg2, i;
       				
       				if Length(argList) < 1 then
       				begin
       					:SetLispError("'<' requires at least one argument.");
       				end;
       				else
       				begin	
       					arg1 := argList[0];
       					if not IsNumber(arg1) then
       					begin
       						:SetLispError("not a number -" && SprintObject(arg1));
       					end;
       					
       					i := 1;
       					while i < Length(argList) do
       					begin
       						arg2 := argList[i];
       						if not IsNumber(arg2) then 
       						begin
       							:SetLispError("not a number -" && SprintObject(arg2));
       						end;
       						
       						if arg1 >= arg2 then return nil;
       						arg1 := arg2;
       						i := i + 1;
       					end;
       					
       					return True;						
       				end;				
       			end;,
       
       	notOp:	func(array argList)
       			// Logical NOT.  
       			//
       			// Unlike AND and OR, this operation takes just one argument.
       			// If the argument evaluates to nil, True is returned, otherwise
       			// nil is returned.
       			//
       			// syntax: (not x)
       			//
       			begin
       				local arg;
       				
       				if Length(argList) <> 1 then
       				begin
       					:SetLispError("NOT requires exactly 1 argument.");
       				end;
       				else
       				// Get the argument.
       				if argList[0] = nil then
       					return True;
       				else
       					return nil;	
       			end;,
       			
       	evalOp:	func(array argList)
       			// EVAL function. Simply calls :doEval().
       			// 
       			// Syntax: (EVAL S-expression)
       			//
       			// This permits code to be constructed and evaluated at run-time.
       			// The argument is copied since :doEval() will remove the head
       			// of any list-based S-expression, and if the argument is bound
       			// to a variable, this will be mutated (as opposed to a literal
       			// list).
       			//
       			// LittleLisp sides with XLisp's EVAL implementation. MIT Scheme
       			// requires a second argument which is an environment. It seems
       			// to me that there is no reason why EVAL shouldn't be able to
       			// accept multiple arguments. This is logically equivalent to
       			// multiple EVAL calls though.
       			//
       			begin
       				if Length(argList) <> 1 then
       				begin
       					:SetLispError("EVAL requires exactly 1 argument.");
       				end;
       				else				
       					return :doEval(DeepClone(argList[0]));
       			end;,
       
       	nsApply:func(symbol fun, array argList)
       			// Apply a NewtonScript function to a list of arguments.
       			// Note that this will be subject to the normal NewtonScript
       			// error processing facilities. Should add a try block here.
       			// See also :applyOp().
       			//
       			begin
       				// Isolate name of function.
       				local string theFunc := SPrintObject(fun);
       				theFunc := SubStr(theFunc, 1, StrLen(theFunc)-1);	
       				
       				// Hopefully, 52 arguments will be enough! The reason for
       				// this value is that formal argument names are A..Z and a..z.
       				if Length(argList) > 52 then
       				begin
       					:SetLispError("APPLY - a maximum of 52 arguments is permitted");
       				end;
       				
       				// Construct formal argument list (with names A..Z, a..z).
       				// Hopefully, 52 arguments will be enough! Do I really
       				// need to check for this?
       				local i;
       				local string formalArgs;
       				for i:=0 to Length(argList)-1 do
       				begin
       					formalArgs := formalArgs & SPrintObject(Chr(Ord($A)+i)); 
       					if i < Length(argList)-1 then formalArgs := formalArgs & ",";
       				end;
       				
       				// Construct function expression.
       				local string funcExpr := "func(" & formalArgs & ") ";
       				funcExpr := funcExpr & theFunc & "(" & formalArgs & ")";
       				
       				// Compile the function to yield a zero argument function 
       				// which can construct the target function. Create a slot 
       				// in a temporary frame for the function.
       				local env := { f:Compile(funcExpr) };
       									
       				// Apply the function to the actual arguments and return the result.
       				// Note that this uses NewtonScript's Apply() function, not evaluate:Apply()!
       				return Apply(env:f(), argList);
       			end;,
       			
       	applyOp:func(array Sexpr)
       			// APPLY function.
       			//
       			// Applies a function or primitive to an argument list.
       			// Function symbols may optionally be quoted, e.g. 
       			//
       			//	(apply + '(1 2 3))
       			//	(apply sqr '(7))
       			//
       			// but this is not mandatory since all functions 
       			// (including built-ins) are first class.
       			//
       			// A typical use for APPLY is to pass an arbitrary 
       			// function to another function in order to apply it to
       			// one or more arguments, e.g. MAP.
       			//
       			// If the function is preceded by "#", it is assumed to
       			// be a NewtonScript function.
       			//
       			// syntax: (apply [#]f arglist)
       			//
       			begin
       				local fun, argList;
       				
       				// Apply a function to an argument list.
       				if Length(Sexpr) <> 2 then
       				begin
       					:SetLispError("APPLY requires exactly 2 arguments.");
       				end;
       				else
       				begin
       					// Get the function symbol, lambda expression, built-in function value. 
       					// The function symbol could be the result of some other computation,
       					// which is what makes APPLY useful and interesting.
       					fun := Sexpr[0];
       					if not IsSymbol(fun) and 
       					   not :IsUserFunc(fun) and 
       					   not :IsPrimitiveFunc(fun) then
       					begin
       						:SetLispError("not a function -" && Intern(fun));
       					end;
       
       					// Get the argument list, which might be empty.
       					argList := Sexpr[1];
       					if argList = nil then argList := [];
       					if not IsArray(argList) then
       					begin
       						:SetLispError("second argument to APPLY not a list");
       					end;
       
       					// If the function name starts with "#", assume it to be a 
       					// NewtonScript function.
       					if IsSymbol(fun) and (SPrintObject(fun))[0] = $# then 
       						return :nsApply(fun, argList);
       					else
       						// Apply the function to the argument list and return the result.
       						return :Apply(fun, argList, nil);
       				end;
       			end;,
       	
       	map:	func(array argList)
       			// Syntax: (MAP f arg-list1 arg-list2 ...)
       			// The function f is applied to as many arguments as there are
       			// argument lists and a list of the results is returned. Arguments
       			// for each invocation of f are taken from the same position in each
       			// list. All argument lists must have the same length in this implementation.
       			// All this should be compared against MIT Scheme and Common Lisp.
       			begin
       				if Length(argList) < 2 then
       				begin
       					:SetLispError("MAP requires at least two arguments.");
       				end
       				else
       				begin
       					local integer i;
       					local integer arity;
       
       					if IsArray(argList[1]) then arity := Length(argList[1]);
       
       					for i:=1 to Length(argList)-1 do
       						if not IsArray(argList[i]) or Length(argList[i]) <> arity then
       							:SetLispError("one or more non-empty equal length argument arrays expected.");
       
       					local f := argList[0]; // function to be mapped
       					local array args;
       					local array resultList := [];
       
       					// ith position in each argument list.
       					for i:=0 to Length(argList[1])-1 do
       					begin
       						args := [];
       						local integer j;
       						// jth argument list.
       						for j:=1 to Length(argList)-1 do
       							AddArraySlot(args, argList[j][i]);
       						AddArraySlot(resultList, :Apply(f, args, nil));
       					end;
       					return resultList;
       				end;
       			end;,
       			
       	list:	func(array argList)
       			// LIST function. Creates a list consisting of all its arguments.
       			// 
       			// Syntax: (LIST S-expression ...)
       			//
       			begin
       				// The arguments are already in a list, so just copy this.
       				// Cloning this may be unnecessary, but it will prevent any
       				// possible side effects on the original list.
       				return DeepClone(argList);
       			end;,
       
       	length:	func(array argList)
       			// LENGTH function. Returns the length of a list.
       			// 
       			// Syntax: (LENGTH list)
       			//
       			begin
       				if Length(argList) <> 1 then
       				begin
       					:SetLispError("LENGTH requires exactly one argument");
       				end
       				else
       				if argList[0] = nil then return 0;
       				else
       				if not IsArray(argList[0]) then
       				begin
       					:SetLispError("argument to LENGTH not a list");
       				end
       				else
       					return Length(argList[0]);
       			end;,
       
       	execMathFunc:	
       			func(frame mathFunc, array argList)
       			// Execute a math function.
       			// 
       			// Syntax: (math-func arg1 [arg2 ...])
       			//		
       			begin
       				local symbol fun := mathFunc.name;				
       				local integer arity := mathFunc.arity;
       				
       				if Length(argList) <> arity then
       				begin
       					local string errMsg := SPrintObject(fun) && 
       										   "requires exactly" && arity && "argument";
       					if arity <> 1 then errMsg := errMsg & "s";
       					:SetLispError(errMsg);
       				end
       				else  
       				begin
       					if arity > 0 then 
       					begin
       						local arg;
       						foreach arg in argList do
       						begin
       							if not IsNumber(arg) then
       							begin
       								:SetLispError(SPrintObject(fun) && "expects a numeric argument");
       							end;
       						end;
       						return Apply(mathFunc.fun, argList);
       					end
       					else
       						return mathFunc:fun();
       				end;
       			end;,
       			
       	seq:	func(array argList)
       			// Syntax: (SEQ n)
       			// Returns a list of the integers 0..n-1.
       			begin
       				if Length(argList) <> 1 then
       				begin
       					:SetLispError("SEQ requires exactly 1 argument.");
       				end
       				else
       				begin
       					local array intSeq := [];
       					local integer i;
       					for i := 0 to Floor(Round(argList[0]))-1 do
       						AddArraySlot(intSeq, i);
       					return intSeq;	
       				end;			
       			end;,
       			
       	read_string:
       			func(array argList)
       			// Syntax: (READ-STRING)
       			//
       			// Read a user input string, currently from a modal dialog.
       			// Could also extend this to read from IR, serial port, and Notes application
       			// e.g. (read-string 'IR), (read-string 'SERIAL '(8 N 1 9600)),
       			//      (read-string 'note-foo).
       			begin			
       				if Length(argList) <> 0 then
       				begin
       					:SetLispError("READ-STRING takes no arguments");
       				end
       				else
       				begin
       					local lispVars := GetGlobals().littleLisp;
       					SetVariable(lispVars, 'inputText, nil);
       					lispVars.mainView.inputDialogView:ModalDialog();
       					return lispVars.inputText;
       				end;
       			end;,
       			
       	display: 
       			func(array argList)
       			// Syntax: (DISPLAY object)
       			// Invokes a dialog with human readable output of any LittleLisp object.
       			begin
       				if Length(argList) <> 1 then
       				begin
       					:SetLispError("DISPLAY takes exactly one argument");
       				end
       				else
       				begin
       					local string output;
       					if IsString(argList[0]) then output := argList[0];
       					else output := GetGlobals().littleLisp.mainView.writer:fromSexpr([argList[0]]);
       					ModalConfirm(output, ["OK"]);			
       				end;
       				return true;
       			end;,	
       			
       	ask:	func(array argList)
       			// Syntax: (ASK <message-string> <button-string1> ... <button-stringN>)
       			// Displays a message and the specified buttons, returning the number
       			// (from 1 to N) of the clicked button.
       			begin
       				if Length(argList) < 2 then
       				begin
       					:SetLispError("ASK requires at least 2 arguments");
       				end
       				else
       				begin
       					local string message := argList[0];
       					if not IsString(message) then 
       					begin
       						:SetLispError("ASK - all arguments must be strings.");
       					end
       					else
       					begin
       						local array buttons := [];
       						local integer i:= Length(argList)-1;
       						while i >= 1 do
       						begin
       							if not IsString(argList[i]) then 
       							begin
       								:SetLispError("ASK - all arguments must be strings.");
       							end
       							else
       							begin
       								AddArraySlot(buttons, argList[i]);
       								i:=i-1;
       							end;
       						end;
       	 					return (Length(argList)-1) - ModalConfirm(message, buttons);
       	 				end;
        				end;
         			end;,		
       
       	nth:	func(array argList)
       			// Return the Nth element of the list. It is the equivalent of:
       			//
       			// (defun nth (n L)
       	      	//		(cond ((null? L) '()) 
       		    //			  ((= n 1) (car L))
       			//			  (t (nth (- n 1) (cdr L))))))
       			//
       			// Nth has been taken from XLisp.
       			//
       			// Syntax: (nth <n> <list>)
       			begin
       				if Length(argList) <> 2 then
       					:SetLispError("NTH takes exactly 2 arguments (NTH <index> <list>).");
       				else
       				if not IsNumber(argList[0]) then
       					:SetLispError("NTH - expected index followed by list.");
       				else
       				if argList[1] = nil then
       					return nil;
       				else 
       				if not IsArray(argList[1]) then
       					:SetLispError("NTH - expected index followed by list.");
       				else
       				begin
       					local integer n := Floor(Round(argList[0]));
       					local array theList := argList[1];
       					if n >= 1 and n <= Length(theList) then
       						return theList[n-1];
       					else
       						:SetLispError("index out of bounds - must be 1..N.");
       				end;
       			end;,
       			
       	set_nth:
       			func(array argList)
       			// Set the Nth element of the list to the specified value.
       			//
       			// Syntax: (set-nth! <n> <list> <expr>)
       			begin
       				if Length(argList) <> 3 then
       					:SetLispError("SET-NTH! takes exactly 3 arguments (SET-NTH! <index> <list> <expr>).");
       				else
       				if not IsNumber(argList[0]) or not IsArray(argList[1]) then
       					:SetLispError("SET-NTH! - expected index followed by non-empty list and new value.");
       				else
       				begin
       					local integer n := Floor(Round(argList[0]));
       					local array theList := argList[1];
       					if n >= 1 and n <= Length(theList) then
       					begin
       						theList[n-1] := argList[2];
       						theList; // return mutated list
       					end
       					else
       						:SetLispError("index out of bounds - must be 1..N.");
       				end;
       			end;,
       			
       	/************/
       	/* Graphics */
       	/************/
       	
       	drawLine:	
       			func (integer x1, integer y1, integer x2, integer y2)
       			// Draw a line from x1,y1 to x2,y2.
       			begin
       				local frame lispVars := GetGlobals().littleLisp;
       				local frame gfxView := lispVars.mainView.gfxView;
       				
       				// Open the graphics view if necessary.
       				if gfxView.viewCObject = nil then gfxView:Open();
       				
       				// Draw the line.
       				local theLine := MakeLine(x1,y1,x2,y2);
       				local frame canvas := gfxView.canvas;
       				canvas:LockScreen(true);
       				canvas:DoDrawing('DrawShape, [theLine, nil]);
       				canvas:LockScreen(nil);
       				
       				// Store line in case graphics view needs to be refreshed later.
       				AddArraySlot(lispVars.gfxViewContents, {shape: theLine, style: nil}); 
       			end;,
       			
       	moveTo:	func (array argList)
       			// SYNTAX: (MOVE-TO x y)
       			// Moves the turtle's pen to (x,y).
       			// Note: Should check that each argument is a number, but haven't
       			// in an effort to improve efficiency. The same is true for other
       			// turtle graphic functions. 
       			begin
       				if Length(argList) <> 2 then
       				begin
       					:SetLispError("MOVE-TO requires exactly 2 arguments.");
       				end
       				else
       				begin
       					local integer x,y;
       					x := Floor(Round(argList[0])); 
       					y := Floor(Round(argList[1])); 
       					local turtle := GetGlobals().littleLisp.turtle;
       					turtle.x := x;
       					turtle.y := y;
       					return true;
       				end; 	
       			end;,
       			
       	lineTo:	func (array argList)
       			// SYNTAX: (LINE-TO x y)
       			// Moves the turtle's pen to (x,y), drawing from the
       			// current position to there.
       			begin
       				if Length(argList) <> 2 then
       				begin
       					:SetLispError("LINE-TO requires exactly 2 arguments.");
       				end
       				else
       				begin
       					local integer x,y;
       					x := Floor(Round(argList[0]));
       					y := Floor(Round(argList[1])); 
       					local turtle := GetGlobals().littleLisp.turtle;
       					:drawLine(turtle.x, turtle.y, x, y);
       					turtle.x := x;
       					turtle.y := y;
       					return true;
       				end; 	
       			end;,
       			
       	plot:	func (array argList)
       			// SYNTAX: (PLOT x y)
       			// Plots a pixel at (x,y) but does NOT affect the turtle's position.
       			// This is really just a general graphics command and not TG at all.
       			// Q: Is there a more efficient way of setting a pixel?
       			begin
       				if Length(argList) <> 2 then
       				begin
       					:SetLispError("PLOT requires exactly 2 arguments.");
       				end
       				else
       				begin
       					local integer x,y;
       					x := Floor(Round(argList[0])); 
       					y := Floor(Round(argList[1]));
       					:drawLine(x, y, x, y);
       					return true;
       				end; 	
       			end;,
       
       	turn:	func (array argList)
       			// SYNTAX: (TURN degrees)
       			// Rotates the turtle left (minus) or right by the specified amount.
       			begin
       				if Length(argList) <> 1 then
       				begin
       					:SetLispError("TURN requires exactly 1 argument.");
       				end
       				else
       				begin
       					local turtle := GetGlobals().littleLisp.turtle;
       					turtle.angle := turtle.angle + argList[0];
       					return true;
       				end; 	
       			end;,
       						
       	walk:	func (array argList)
       			// SYNTAX: (WALK steps)
       			// Moves the turtle by the specified amount in the current direction.
       			begin
       				if Length(argList) <> 1 then
       				begin
       					:SetLispError("WALK requires exactly 1 argument.");
       				end
       				else
       				begin
       					local turtle := GetGlobals().littleLisp.turtle;
       					local real theta := turtle.angle / 57.295779;
       					local real steps := argList[0];
           				local integer newX := Floor(Round(turtle.x + steps*Cos(theta)));
           				local integer newY := Floor(Round(turtle.y + steps*Sin(theta)));
          					:drawLine(turtle.x, turtle.y, newX, newY);
       					turtle.x := newX;
       					turtle.y := newY;
       					return true;
       				end; 	
       			end;,
       			
       	home:	func(array argList)
       			// SYNTAX: (HOME)
       			// Resets the turtle to its home position and direction.
       			begin
       				if Length(argList) <> 0 then
       				begin
       					:SetLispError("HOME takes no arguments.");
       				end
       				else
       				begin
       					SetVariable(GetGlobals().littleLisp, 'turtle, {x:110, y:100, angle:270});
       					return true;
       				end; 	
       			end;,	
       			
       	draw_shape:	
       			func (array argList)
       			// SYNTAX: (DRAW-SHAPE shape-object [style-frame])
       			// Draw a shape on LittleLisp's graphics view where shape is
       			// the return value from a NewtonScript shape-creation
       			// function, eg. (draw-shape (#MakeOval 10 10 50 50)). The drawing
       			// is clipped to the graphics view and added to a refresh 
       			// list. This function does NOT affect the turtle's position.
       			// Note: if you use this function, it is assumed that you know
       			// enough NewtonScript to understand what a shape object and style
       			// frame are, that you have access to Newton programming documentation,
       			// and that you pass appropriate values.
       			begin
       				if Length(argList) > 2 or Length(argList) < 1 then
       					:SetLispError("DRAW-SHAPE requires at least 1 shape argument" &&
       								  "and may take an optional style frame.");
       				else
       				begin
       					local frame lispVars := GetGlobals().littleLisp;
       					local frame gfxView := lispVars.mainView.gfxView;
       				
       					// Get the shape and style arguments to DrawShape.
       					local frame theStyle := nil;
       					if Length(argList) = 2 then 
       					begin
       						if IsFrame(argList[1]) then
       							theStyle := argList[1];
       						else
       							:SetLispError("The second argument to DRAW-SHAPE must be a frame."); 		
       					end;
       					local theShape := argList[0];
       
       					// Open the graphics view if necessary.
       					if gfxView.viewCObject = nil then gfxView:Open();
       
       					// Draw the shape.									
       					local frame canvas := gfxView.canvas;
       					canvas:LockScreen(true);
       					canvas:DoDrawing('DrawShape, [theShape, theStyle]);
       					canvas:LockScreen(nil);
       				
       					// Store shape in case graphics view needs to be refreshed later.
       					AddArraySlot(lispVars.gfxViewContents, {shape: theShape, style: theStyle}); 				
       
       					return true;
       				end;
       			end;,
       
       	/********************/
       	/* String functions */	
       	/********************/
       	
       	stringp:
       			func(array argList)
       			// Syntax: (STRING? <object>)
       			// Returns T or NIL indicating whether <object> is a string.
       			begin
       				if Length(argList) <> 1 then
       				begin
       					:SetLispError("STRING? takes exactly 1 argument.");
       				end
       				else
       					return IsString(argList[0]);
       			end;,					
       	
       	string:
       			func(array argList)
       			// Syntax: (STRING <expr1> <expr2> ... <exprN>)
       			// Constructs a string from all its arguments and returns this.
       			begin
       				if Length(argList) < 1 then
       				begin
       					:SetLispError("STRING requires at least 1 argument.");
       				end
       				else
       				begin
       					local writer := GetGlobals().littleLisp.mainView.writer;
       					local string str := "";
       					local expr;
       					foreach expr in argList do
       					begin
       						if IsString(expr) then str := str & expr;
       						else
       							// Make expression human-readable and
       							// Remove leading space before appending.
       							str := str & TrimString(writer:fromSexpr([expr]));
       					end;
       					return str;
       				end;
       			end;,
       	
       	string_copy:
       			func(array argList)
       			// Syntax: (STRING-COPY <string>)
       			// Returns a copy of the supplied string.
       			begin
       				if Length(argList) <> 1 or not IsString(argList[0]) then
       				begin
       					:SetLispError("STRING-COPY takes exactly 1 string argument.");
       				end
       				else
       					return Clone(argList[0]);
       			end;,
       		
       	string_length:
       			func(array argList)
       			// Syntax: (STRING-LENGTH <string>)
       			// Returns the length of the specified string.
       			begin
       				if Length(argList) <> 1 or not IsString(argList[0]) then
       				begin
       					:SetLispError("STRING-LENGTH takes exactly 1 string argument.");
       				end
       				else
       					return StrLen(argList[0]);
       			end;,
       
       	substring:
       			func(array argList)
       			// Syntax: (SUBSTRING <string> <start> <end>)
       			// Returns a string corresponding to the specified portion of the string.
       			// <start> and <end> must be exact integers satisfying
       			// 0 <= start <= end <= (string-length string). 
       			begin
       				if Length(argList) <> 3 or not IsString(argList[0]) then
       				begin
       					:SetLispError("SUBSTRING takes exactly 3 string arguments.");
       				end
       				else
       				begin
       					local string str := argList[0];
       					local integer start := Floor(Round(argList[1]));
       					local integer _end := Floor(Round(argList[2]));
       					if start < 0 or start > _end or _end > StrLen(str) then
       					begin
       						:SetLispError("SUBSTRING - index out of bounds.");
       					end
       					else
       						return SubStr(str, start, (_end-start)+1);
       				end;
       			end;,	
       				
       	string2number:
       			func(array argList)
       			// Syntax: (STRING->NUMBER <string>)
       			// Returns a number given a string, or nil if the string does not
       			// represent a valid number or the count of digits on either side
       			// of the decimal point exceeds 63. Examples of valid numbers are: 
       			// 1, 1.2, -12,345, (12,345.78)
       			begin
       				if Length(argList) <> 1 or not IsString(argList[0]) then
       				begin
       					:SetLispError("STRING->NUMBER takes exactly 1 string argument.");
       				end
       				else
       					return StringToNumber(argList[0]);
       			end;,
       
       	write_string:
       			func(array argList)
       			// Syntax: (write-string string notepad-filename)
       			// 
       			// Write a string to the specified Notepad file. Both arguments
       			// are strings. The file must already exist. 
       			//
       			begin
       				GetGlobals().littleLisp.mainView.util:write_string(argList);
       				return nil;
       			end;,
       			
       	/*****************/
       	/* Special Forms */
       	/*****************/
       	
       	andOp:	func(array Sexpr)
       			// AND special form.
       			//
       			// Return nil upon first encountering a nil expression, otherwise
       			// return the final value. If no arguments are provided, the
       			// default value is T. MIT Scheme, XLisp and Allegro CL all default 
       			// to this.
       			//
       			// syntax: (and x1 x2 .. xN)
       			//
       			begin
       				local val := True;	 // in case we don't enter the loop.
       				local arg;
       				
       				foreach arg in Sexpr do
       				begin
       					val := :doEval(arg);
       					if val = nil then return nil;
       				end;
       				
       				return val;  // True or the last S-expression.
       			end;,
       
       	orOp:	func(array Sexpr)
       			// OR special form.  
       			//
       			// Return the first non-nil expression, otherwise nil.
       			// If no arguments are provided, the default value is nil.
       			// MIT Scheme, XLisp and Allegro CL all default to this.
       			//
       			// syntax: (or x1 x2 .. xN)
       			//
       			begin
       				local val; 
       				local arg;
       				
       				foreach arg in Sexpr do
       				begin
       					val := :doEval(arg);
       					if val <> nil then return val;
       				end;
       				
       				return nil;				
       			end;,
       
       	quote:	func(array Sexpr)
       			// Return an argument unevaluated.
       			//
       			// syntax: (quote S-expression)
       			//		or 'S-expression
       			//
       			begin								
       				if Length(Sexpr) <> 1 then
       				begin
       					:SetLispError("QUOTE requires exactly 1 argument.");
       				end;
       				else
       				if IsArray(Sexpr[0]) and Length(Sexpr[0]) = 0 then
       					// Empty list.
       					return nil;
       				else 
       					return Sexpr[0];
       			end;,
       								
       	setq:	func(array Sexpr)
       			// Bind a value to a symbol.
       			// The value may be a quoted atom or list,
       			// or the result of a function application or
       			// special form. The newly bound value is returned 
       			// by this special form.
       			//
       			begin
       				local name, value;
       				
       				if Length(Sexpr) <> 2 then
       				begin
       					:SetLispError("SETQ requires exactly 2 arguments.");
       				end;
       				else
       				begin
       					name := Sexpr[0];
       					if not IsSymbol(name) then
       					begin
       						:SetLispError("not a symbol -" && SprintObject(name));
       					end;
       					else
       						return :SetLispVar(name, :doEval(Sexpr[1]));
       				end;
       			end;,
       			
       	cond:	func(array Sexpr)
       			// COND special form.
       			//
       			// One or more lists each containing one or two expressions are expected. 
       			// The first expression in each list is evaluated and if non-nil,
       			// the second expression (or the first if only one is present) is 
       			// evaluated and returned.
       			// 
       			// Nil is returned if no lists are supplied or if no first expression 
       			// evaluates to non-nil.
       			//
       			// If there are either zero or more than two expressions in
       			// a cond clause, an error is generated. This is what MIT Scheme
       			// does, while XLisp evaluates a zero-length cond clause as '().
       			//
       			begin
       				local condList, len, firstExpr;
       				local mainView;
       				
       				if Length(Sexpr) = 0 then
       					return nil;
       				else
       				foreach condList in Sexpr do
       				begin
       					len := Length(condList);
       					
       					if len = 1 or len = 2 then
       					begin
       						firstExpr := :doEval(condList[0]);
       					
       						if firstExpr <> nil then
       							if len = 2 then
       								return :doEval(condList[1]);  // second expression
       							else
       								return firstExpr;  // first expression
       					end;
       					else
       					begin
       						mainView := GetGlobals().littleLisp.mainView;
       						:SetLispError("bad COND clause -" &&
       									  mainView.writer:fromSexpr([condList]));
       					end;
       				end;
       								
       				return nil;  // default value
       			end;,
       		
       	ifStatement:
       			func(array Sexpr)
       			// Return expr1 if condition is true (non-nil) otherwise nil or expr2 if
       			// present. If condition is false and expr2 is absent, nil is returned.
       			// Only one of expr1 or expr2 is evaluated. The only difference to this
       			// in Scheme is that #f represents false, rather than nil, and in the
       			// case where condition is false and expr2 is absent, the return value
       			// is undefined.
       			//
       			// Syntax: (IF condition expr1 expr2)
       			//
       			begin
       				local argLength := Length(Sexpr);
       				if argLength <> 2 and argLength <> 3 then
       					:SetLispError("IF expects a condition and one or two expressions.");
       				else
       				begin
       					if :doEval(Sexpr[0]) <> nil then
       						return :doEval(Sexpr[1]); // expr1
       					else
       					if argLength = 3 then
       						return :doEval(Sexpr[2]); // expr2
       					else
       						return nil;
       						
       					
       				end;
       			end;,
       				
       	defun:	func(array Sexpr)
       			// Define a function. Note that this is syntactic
       			// sugar for (SETQ name (lambda ([formal-arglist]) expr1 [, expr2 ...])).
       			//
       			// Syntax: (DEFUN name ([formal-arglist]) expression1 [, expression2 ...])
       			//
       			begin
       				local name, i;
       				local fn := {type:'lambda, body:[], env:nil};
       				local lispVars := GetGlobals().littleLisp;
       				local string theName;
       				local found;
       				
       				// There must be at least 3 arguments to DEFUN. The body may 
       				// consist of multiple expressions so there could be > 3 
       				// arguments in total.
       				//				
       				if Length(Sexpr) < 3 then 
       				begin
       					:SetLispError("syntax - (DEFUN name ([formal-args]) body)");
       				end;
       				
       				// Store function name.
       				if IsSymbol(Sexpr[0]) then
       					name := Sexpr[0];
       				else
       				begin
       					:SetLispError("function name expected as first argument to DEFUN.");
       				end;					
       				
       				// Store the formal argument list. Could be empty.
       				if :IsFormalsList(Sexpr[1]) then 
       					fn.formals := Sexpr[1];
       				else
       				begin
       					:SetLispError("formal argument list expected as second argument to DEFUN.");
       				end;					
       
       				// Store the body of the function. Could be any valid S-expression.
       				// This will be applied to an actual argument list when the function
       				// is invoked.
       				//
       				i:=2;
       				while i<Length(Sexpr) do
       				begin
       					AddArraySlot(fn.body,Sexpr[i]);
       					i:=i+1;
       				end;
       
       				// Make this a top level symbol.
       				SetVariable(lispVars.globalEnv, name, fn);
       
       				// Add this name to the bound symbols popup menu list
       				// unless it's already present.
       				theName := SPrintObject(name);
       				
       				found := nil;
       				
       				for i:=0 to Length(lispVars.symbolBindings)-1 do
       					if StrExactCompare(lispVars.symbolBindings[i].item, 
       									   theName/*&& Chr(402)*/) = 0 then
       				begin
       					found := true; 			
       					break;
       				end;
       					
       				if not found then
       				begin
       					AddArraySlot(lispVars.symbolBindings, {item: theName/*&& Chr(402)*/});
       					Sort(lispVars.symbolBindings, '|str<|, 'item);
       				end;
       				
       				return name;
       			end;,
       			
       	lambda:	func(array Sexpr)
       			// Return an unnamed function.
       			//
       			// Syntax: (LAMBDA ([formal-arglist]) expression1 [, expression2 ...])
       			//
       			begin
       				local integer i;
       				local fn := {type:'lambda, body:[], env:nil};
       				local lispVars := GetGlobals().littleLisp;
       				
       				// Capture the current local environment. Do so by starting with
       				// the oldest frame and working towards the youngest, collecting
       				// all slot-value pairs into one frame such that newer bindings
       				// take precedence over older. Each value is cloned since the
       				// lambda expression must be an independent closure.
       				if Length(lispVars.localEnv) > 0 then
       				begin
       					fn.env := {};
       					local integer i;
       					for i:=0 to Length(lispVars.localEnv)-1 do
       						foreach slot,value in lispVars.localEnv[i] do
       		 					SetVariable(fn.env, slot, DeepClone(value));
       		 		end;
       				 		
       				// There must be at least 2 arguments to LAMBDA. The body may 
       				// consist of multiple expressions so there could be > 2 
       				// arguments in total.
       				//
       				if Length(Sexpr) < 2 then 
       				begin
       					:SetLispError("syntax - (LAMBDA ([formal-args]) body)");
       				end;
       				
       				// Store the formal argument list. Could be empty.
       				if :IsFormalsList(Sexpr[0]) then 
       					fn.formals := Sexpr[0];
       				else
       				begin
       					:SetLispError("formal argument list expected as first argument to LAMBDA.");
       				end;					
       
       				// Store the body of the function. Could be any valid S-expression.
       				// This will be applied to an actual argument list when the function
       				// is invoked.
       				//
       				i:=1;
       				while i<Length(Sexpr) do
       				begin
       					AddArraySlot(fn.body,Sexpr[i]);
       					i:=i+1;
       				end;
       				
       				// Return the lambda expression. Could be applied immediately
       				// to arguments, or bound to a variable. 				
       				return fn;
       			end;,			
       			
       	IsFormalsList:
       			func(x)
       			// Does the parameter correspond to a Lisp formal parameter list?
       			begin
       				local item;
       				
       				if not IsArray(x) then
       					return nil;
       				else
       				foreach item in x do
       					if not IsSymbol(item) then return nil;
       
       				return true;
       			end;,
       						
       	beginForm: 	func(array Sexpr)
       				// Syntax: (BEGIN expr1 expr2 ... exprN)
       				// Each expression is evaluated from left to right and the
       				// value of the last is returned.
       				begin
       					local expr;
       					local result;
       					
       					if Length(Sexpr) = 0 then
       					begin
       						:SetLispError("expressions expected.");
       					end
       					else
       					begin
       						// Work with a copy of the S-expressions since :doEval()
       						// will remove the head of any list-based S-expressions.
       						foreach expr in DeepClone(Sexpr) do
       						begin
       							result := :doEval(expr);
       						end;
       						return result;
       					end;
       				end;,
       	
       	repeatExpr:	
       		func(array Sexpr)
       		// Syntax: (REPEAT N expr)
       		// Evaluate expr N times and return the result as an N-element list. 
       		begin
       			if Length(Sexpr) <> 2 then
       			begin
       				:SetLispError("repetition count and expression expected.");
       			end
       			else
       			begin
       				local expr;
       				local array repetitions := [];
       				local integer i;
       				for i := 1 to Floor(Round(:doEval(Sexpr[0]))) do
       				begin
       					expr := DeepClone(Sexpr[1]);
       					AddArraySlot(repetitions, :doEval(expr));
       				end;
       				return repetitions;	
       			end;			
       		end;,
       		
       	whileLoop:
       		func(Sexpr)
       		// Syntax: (WHILE <cond-expr> <expr1> <expr2> ... <exprN>)
       		// This special form executes the N expressions so long as <cond-expr> is non-nil.
       		begin
       			if Length(Sexpr) < 2 then
       			begin
       				:SetLispError("conditional expression and 1 or more body expressions expected.");
       			end
       			else
       			begin
       				local integer i;
       				local expr := nil;
       				while :doEval(DeepClone(Sexpr[0])) do
       					for i:= 1 to Length(Sexpr)-1 do
       					begin
       						 expr := :doEval(DeepClone(Sexpr[i]));
       					end;
       			end;
       			return expr; // last expression evaluated
       		end;,
       				
       	mkFrame:
       		func(array Sexpr)
       		// Syntax: (FRAME ((symbol1 expr1) (symbol2 expr2) ...))
       		// Constructs a NewtonScript frame object from a list of bindings.
       		// This is essentially LET without the subsequent expressions. The
       		// intention here is that this frame can be used in conjunction
       		// with NewtonScript functions. Note that frames are NOT currently
       		// first class values in LittleLisp and printing them yields nothing.
       		// Note that it may be necessary to quote a symbol more than once when
       		// calling NewtonScript functions, since (quote x) evaluates to x, when
       		// some functions require symbols quoted symbols,
       		// e.g. (setq f (frame ((x 2)))) (#HasSlot 'f (quote (quote x)))
       		// This feature has had minimal testing and should be considered
       		// experimental.
       		begin
       			if Length(Sexpr) <> 1 or not IsArray(Sexpr[0]) then
       			begin
       				:SetLispError("a list of binding pairs was expected.");
       			end
       			else
       			begin
       				local frame theFrame := {};
       				local expr;
       				foreach expr in Sexpr[0] do
       				begin
       					if not IsArray(expr) or Length(expr) <> 2 or not IsSymbol(expr[0]) then
       					begin
       						:SetLispError("a binding pair was expected.");
       					end
       					else
       						SetVariable(theFrame, expr[0], :doEval(expr[1]));						
       				end;
       				return theFrame;
       			end;
       		end;,
       		
       	letStar:
       		func(array Sexpr)
       		// Syntax: (LET* ((symbol1 expr1) (symbol2 expr2) ...) expr1 expr2 ...)
       		// This is the same as Scheme's lexical binding LET* special form.
       		// Symbols are bound from left to right and the scope of a binding
       		// is the portion of the LET* expression to the right of that binding. 
       		begin
       			if Length(Sexpr) < 2 or not IsArray(Sexpr[0]) then
       			begin
       				:SetLispError("a list of binding pairs followed by one or more expressions was expected.");
       			end
       			else
       			begin
       				// Bind variables and add them to the local environment.
       				local frame theEnv := {};
       			 	local lispVars := GetGlobals().littleLisp;
       			 	AddArraySlot(lispVars.localEnv, theEnv);
       				local expr;
       				foreach expr in Sexpr[0] do
       				begin
       					if not IsArray(expr) or Length(expr) <> 2 or not IsSymbol(expr[0]) then
       					begin
       						:SetLispError("a binding pair was expected.");
       					end
       					else
       					begin
       						// Use pass by value, as with function parameters, i.e.
       						// evaluate and bind a *copy* of the expression.
       						SetVariable(theEnv, expr[0], :doEval(DeepClone(expr[1])));
       					end;						
       				end;
       				
       				// Evaluate the expressions in the extended environment.
       				// Whatever variables in the extended environment are used,
       				// it should be safe to do anything to them since they are
       				// copies. Of course, if a function mutates the copies, e.g.
       				// NewtonScript's Sort function, that should still be
       				// permitted. Perhaps a LittleLisp clone function should be
       				// supplied for such occasions. (clone x) would of course be
       				// syntactic sugar for (lambda (x) x).
       				local integer i := 1;
       				local result := nil;
       				while i < Length(Sexpr) do
       				begin
       					result := :doEval(Sexpr[i]);
       					i := i+1;
       				end;
       				
       				// Remove the frame that was added to the environment.
       				ArrayRemoveCount(lispVars.localEnv, Length(lispVars.localEnv)-1, 1);
       
       				// Return the result of the last expression evaluated.
       				return result;
       			end;
       		end;,
       
       	error: 
       			func(array Sexpr)
       			// Syntax: (ERROR object)
       			// Aborts the current read-eval-print loop with an error message.
       			// Results prior to this are also output. I have categorised this
       			// as a special form since it changes the flow of control.
       			begin
       				if Length(Sexpr) <> 1 then
       				begin
       					:SetLispError("ERROR takes exactly one argument.");
       				end
       				else
       				begin
       					local expr := :doEval(Sexpr[0]); 
       					local string output;
       					if IsString(expr) then output := expr;
       					else output := GetGlobals().littleLisp.mainView.writer:fromSexpr([expr]);
       					Throw('|evt.ex.msg;littlelisperr|, output);			
       				end;
       			end;,	
       
       /*******************/
       /* Error functions */
       /*******************/
       	
       SetLispError: 	
       			func(string msg)
       			// Set the local environment to empty and throw an exception
       			// with the specified error message.
       			begin
       				GetGlobals().littleLisp.localEnv := [];		
       				Throw('|evt.ex.msg;littlelisperr|, "Error: " && msg);							
       			end;	},
     ViewQuitScript:
       func()
       begin
       	// Delete the global LittleLisp slot.
       	//
       	RemoveSlot(GetGlobals(), 'littleLisp);
       end,
     ViewSetupFormScript:
       // be sure to call inherited:?ViewSetupFormScript()
       func()
       begin
       	inherited:?ViewSetupFormScript();
       	
       	// Set view bounds.
       	local a := GetAppParams();
          	self.viewBounds := RelBounds(a.appAreaLeft, a.appAreaTop, a.appAreaWidth, a.appAreaHeight);
          	
       	// Create global and local environments (symbol tables) if
       	// they don't exist. Also store the main view for LittleLisp
       	// so we can get to slots such as reader and writer later.
       	//
       	local globalVars := GetGlobals();
       				
       	if not (globalVars.littleLisp exists) then
       	begin
       		// Establish LittleLisp global variables.
       		SetVariable(globalVars, 'littleLisp, {});
       		SetVariable(globalVars.littleLisp, 'globalEnv, {});					
       		SetVariable(globalVars.littleLisp, 'localEnv, []);
       		SetVariable(globalVars.littleLisp, 'evalResult, {outputText: "", startPos: 0});		
       		SetVariable(globalVars.littleLisp, 'gfxViewContents, []);		
       		SetVariable(globalVars.littleLisp, 'symbolBindings, []);
       		SetVariable(globalVars.littleLisp, 'notepadFiles, []);
       		SetVariable(globalVars.littleLisp, 'mainView, self);
       		SetVariable(globalVars.littleLisp, 'turtle, {x:110, y:100, angle:270}); // x & y = half canvas width and height
       		SetVariable(globalVars.littleLisp, 'notePad, GetRoot().paperroll);
       		self.lispVars := globalVars.littleLisp; // use instead of GetGlobals().littleLisp?
       		
       		// Add intrinsic (ie. primitive or built-in) functions to global environment.
       		// One NewtonScript function per primitive function.
       		SetVariable(globalVars.littleLisp.globalEnv, 'car, {type: 'primitive, funName: 'car, name: 'car});
       		SetVariable(globalVars.littleLisp.globalEnv, 'cdr, {type: 'primitive, funName: 'cdr, name: 'cdr});
       		SetVariable(globalVars.littleLisp.globalEnv, 'cons, {type: 'primitive, funName: 'cons, name: 'cons});
       		SetVariable(globalVars.littleLisp.globalEnv, 'append, {type: 'primitive, funName: 'append, name: 'append});
       		SetVariable(globalVars.littleLisp.globalEnv, '|eq?|, {type: 'primitive, funName: 'eq, name: '|eq?|});
       		SetVariable(globalVars.littleLisp.globalEnv, '|equal?|, {type: 'primitive, funName: 'equal, name: '|equal?|});
       		SetVariable(globalVars.littleLisp.globalEnv, '|member|, {type: 'primitive, funName: 'member, name: 'member});
       		SetVariable(globalVars.littleLisp.globalEnv, '|eval|, {type: 'primitive, funName: 'evalOp, name: 'eval});
       		SetVariable(globalVars.littleLisp.globalEnv, '|atom?|, {type: 'primitive, funName: 'atom, name: '|atom?|});
       		SetVariable(globalVars.littleLisp.globalEnv, '|list?|, {type: 'primitive, funName: 'listp, name: '|list?|});
       		SetVariable(globalVars.littleLisp.globalEnv, '|null?|, {type: 'primitive, funName: 'null, name: '|null?|});
       		SetVariable(globalVars.littleLisp.globalEnv, 'apply, {type: 'primitive, funName: 'applyOp, name: 'apply});
       		SetVariable(globalVars.littleLisp.globalEnv, 'map, {type: 'primitive, funName: 'map, name: 'map});
       		SetVariable(globalVars.littleLisp.globalEnv, 'list, {type: 'primitive, funName: 'list, name: 'list});
       		SetVariable(globalVars.littleLisp.globalEnv, 'length, {type: 'primitive, funName: 'length, name: 'length});
       		SetVariable(globalVars.littleLisp.globalEnv, '|+|, {type: 'primitive, funName: 'plus, name: '|+|});
       		SetVariable(globalVars.littleLisp.globalEnv, '|-|, {type: 'primitive, funName: 'minus, name: '|-|});
       		SetVariable(globalVars.littleLisp.globalEnv, '|*|, {type: 'primitive, funName: 'multiply, name: '|*|});
       		SetVariable(globalVars.littleLisp.globalEnv, '|/|, {type: 'primitive, funName: 'divide, name: '|/|});
       		SetVariable(globalVars.littleLisp.globalEnv, '|=|, {type: 'primitive, funName: 'equals, name: '|=|});
       		SetVariable(globalVars.littleLisp.globalEnv, '|>|, {type: 'primitive, funName: 'gt, name: '|>|});
       		SetVariable(globalVars.littleLisp.globalEnv, '|<|, {type: 'primitive, funName: 'lt, name: '|<|});
       		SetVariable(globalVars.littleLisp.globalEnv, '|not|, {type: 'primitive, funName: 'notOp, name: '|not|});
       		SetVariable(globalVars.littleLisp.globalEnv, '|move-to|, {type: 'primitive, funName: 'moveTo, name: '|move-to|});
       		SetVariable(globalVars.littleLisp.globalEnv, '|line-to|, {type: 'primitive, funName: 'lineTo, name: '|line-to|});
       		SetVariable(globalVars.littleLisp.globalEnv, 'plot, {type: 'primitive, funName: 'plot, name: 'plot});
       		SetVariable(globalVars.littleLisp.globalEnv, 'turn, {type: 'primitive, funName: 'turn, name: 'turn});
       		SetVariable(globalVars.littleLisp.globalEnv, 'walk, {type: 'primitive, funName: 'walk, name: 'walk});
       		SetVariable(globalVars.littleLisp.globalEnv, 'home, {type: 'primitive, funName: 'home, name: 'home});
       		SetVariable(globalVars.littleLisp.globalEnv, '|draw-shape|, {type: 'primitive, funName: 'draw_shape, name: '|draw-shape|});
       		SetVariable(globalVars.littleLisp.globalEnv, 'seq, {type: 'primitive, funName: 'seq, name: 'seq});
       		SetVariable(globalVars.littleLisp.globalEnv, 'display, {type: 'primitive, funName: 'display, name: 'display});
       		SetVariable(globalVars.littleLisp.globalEnv, 'ask, {type: 'primitive, funName: 'ask, name: 'ask});
       		SetVariable(globalVars.littleLisp.globalEnv, 'string, {type: 'primitive, funName: 'string, name: 'string});
       		SetVariable(globalVars.littleLisp.globalEnv, '|string?|, {type: 'primitive, funName: 'stringp, name: '|string?|});
       		SetVariable(globalVars.littleLisp.globalEnv, '|string-copy|, {type: 'primitive, funName: 'string_copy, name: '|string-copy|});
       		SetVariable(globalVars.littleLisp.globalEnv, '|string-length|, {type: 'primitive, funName: 'string_length, name: '|string-length|});
       		SetVariable(globalVars.littleLisp.globalEnv, '|string->number|, {type: 'primitive, funName: 'string2number, name: '|string->number|});
       		SetVariable(globalVars.littleLisp.globalEnv, 'substring, {type: 'primitive, funName: 'substring, name: 'substring});
       		SetVariable(globalVars.littleLisp.globalEnv, '|read-string|, {type: 'primitive, funName: 'read_string, name: '|read-string|});
       		SetVariable(globalVars.littleLisp.globalEnv, '|write-string|, {type: 'primitive, funName: 'write_string, name: '|write-string|});				
       		SetVariable(globalVars.littleLisp.globalEnv, 'nth, {type: 'primitive, funName: 'nth, name: 'nth});
       		SetVariable(globalVars.littleLisp.globalEnv, '|set-nth!|, {type: 'primitive, funName: 'set_nth, name: '|set-nth!|});
       		SetVariable(globalVars.littleLisp.globalEnv, '|sin|, {type: 'primitive, funName: 'execMathFunc, arity: 1, name: 'sin, fun: func(x) Sin(x)});
       		SetVariable(globalVars.littleLisp.globalEnv, '|cos|, {type: 'primitive, funName: 'execMathFunc, arity: 1, name: 'cos, fun: func(x) Cos(x)});
       		SetVariable(globalVars.littleLisp.globalEnv, '|tan|, {type: 'primitive, funName: 'execMathFunc, arity: 1, name: 'tan, fun: func(x) Tan(x)});
       		SetVariable(globalVars.littleLisp.globalEnv, '|log|, {type: 'primitive, funName: 'execMathFunc, arity: 1, name: 'log, fun: func(x) Log(x)});
       		SetVariable(globalVars.littleLisp.globalEnv, '|log10|, {type: 'primitive, funName: 'execMathFunc, arity: 1, name: 'log10, fun: func(x) Log10(x)});
       		SetVariable(globalVars.littleLisp.globalEnv, '|sqrt|, {type: 'primitive, funName: 'execMathFunc, arity: 1, name: 'sqrt, fun: func(x) Sqrt(x)});
       		SetVariable(globalVars.littleLisp.globalEnv, '|pow|, {type: 'primitive, funName: 'execMathFunc, arity: 2, name: 'pow, fun: func(x,y) Pow(x,y)});
       		SetVariable(globalVars.littleLisp.globalEnv, '|random|, {type: 'primitive, funName: 'execMathFunc, arity: 1, name: 'random, fun: func(x) Random(0,Floor(x-1))});
       	end;
       end,
     ViewScrollDownScript:
       func()
       // Scroll the text in this view down a line.
       begin
       	if outputView.ViewCObject <> nil then
       	begin
       		local evalResult := GetGlobals().littleLisp.evalResult;
       		local int i, len;
       	
       		// Search for the next linefeed.
       		i := evalResult.startPos;
       		len := StrLen(evalResult.outputText); 
       		while i < len do
       		begin
       			if evalResult.outputText[i] = $\n then 
       			begin
       				// Start text display one character after linefeed.
       				evalResult.startPos := i+1;
       				break;
       			end
       			else
       				i := i+1;
       		end;
       	
       		outputView:SetText(Substr(evalResult.outputText, evalResult.startPos, 
       						   		  len-evalResult.startPos/*+1*/));
       	end;
       end,
     viewFlags: 5,
     ViewScrollUpScript:
       func()
       // Scroll the text in this view up a line.
       begin
       	if outputView.ViewCObject <> nil then
       	begin
       		local evalResult := GetGlobals().littleLisp.evalResult;
       		local int i, len;
       	
       		// Search for the previous linefeed.
       		// Start one character before last linefeed (or possibly before start of text).
       		i := evalResult.startPos-2;
       		while i > 0 do
       		begin
       			if evalResult.outputText[i] = $\n then 
       			begin
       				evalResult.startPos := i+1;
       				break;
       			end
       			else
       				i := i-1;
       		end;
       	
       		if i <= 0 then evalResult.startPos := 0; // before start of text
       		outputView:SetText(Substr(evalResult.outputText, evalResult.startPos,
       						   		  StrLen(evalResult.outputText)-evalResult.startPos/*+1*/));
       	end;
       end,
     util:
       { write_string:
       			func(array argList)
       			// Syntax: (write-string string notepad-filename)
       			// 
       			// Write a string to the specified Notepad file. Both arguments
       			// are strings. The file must already exist. 
       			//
       			begin
       				if Length(argList) <> 2 or
       				   not IsString(argList[0]) or
       				   not IsString(argList[1]) then
       				begin
       					GetGlobals().littleLisp.mainView.evaluate:SetLispError("WRITE-STRING takes exactly 2 string arguments.");
       				end
       				else
       				begin
       					local notesApp := GetGlobals().littleLisp.notePad;
       					if notesApp then
       					begin
       						local string fileName := argList[1];
       						local notepadFile := Intern(fileName);
       						local theNote := notesApp:MakeTextNote(argList[0], nil);
       						theNote.labels := notepadFile; // set the Notes file
       						notesApp:NewNote(theNote, nil, nil);
       					end;
       				end;
       			end;
       },
     _proto: @157
    };

inputArea := /* child of LittleLispMainView */
    {viewBounds: {left: 5, top: 13, right: 233, bottom: 180},
     ViewChangedScript:
       func(slot, view)
       begin
       	// Pretty print?
       	
       	/*// Test of getting cursor position.
       	local ARRAY offset := GetHiliteOffsets()[0];
       	local view := offset[0];
       	local start := offset[1];
       	local stop := offset[2];
       	if view then SubStr(view.text, start, stop-start);
       	Print ("start:" && start);
       	Print ("stop:" && stop);
       	for i:=0 to Length(offset)-1 do
       		print (i & ":" && offset[i]);
       	*/
       end,
     text: ,
     viewFont: editFont10,
     _proto: @185
    };
// View inputArea is declared to LittleLispMainView



evalButton := /* child of LittleLispMainView */
    {
     buttonClickScript:
       func()
       begin
       	local inToks, inSexpr, outSexpr, output;
       		
       	// Tokenize.
       	inToks := lexer:tokenize(Clone(inputArea.text));
       	if Length(inToks) = 0 then return;
       	
       	// Are the parentheses balanced?
       	if not reader:parensBalanced(inToks) then
       	begin
       		if outputView exists then
       		begin
       			outputView:Open();
       			outputView:SetText("Error: unbalanced parentheses.");
       		end;
       	end;
       	else
       	begin
       		// Convert to internal form.
       		inSexpr := reader:toSexpr(inToks);
       		
       		// Eval.
       		outSexpr := evaluate:eval(inSexpr, breakToggle.viewValue);
       		
       		// Convert evaluated internal form to printable tokens.
       		// This may include one or more error messages.
       		output := writer:fromSexpr(outSexpr);
       					
       		// Output result to floater window.
       		if outputView exists then
       		begin
       			local expr;
       			local boolean wasError := nil;
       			foreach expr in outSexpr do
       			if IsString(expr) and BeginsWith(expr, "Error:") then
       			begin
       				wasError := true;
       				break;
       			end;
       			
       			if printToggle.viewValue = true or wasError then
       			begin
       				// Is the view already open?
       				if outputView.ViewCObject = nil then
       				begin
       					// No, so open it and set the eval result output text.
       					outputView:Open();
       					lispVars.evalResult.outputText := output;
       					lispVars.evalResult.startPos := 0;
       				end
       				else
       					// View is already open, so append to the eval result output text.
       					lispVars.evalResult.outputText := lispVars.evalResult.outputText & output;
       					
       				// Update the view.
       				outputView:SetText(lispVars.evalResult.outputText);
       			end;
       		end;
       	end;
       end,
     text: "Eval",
     viewBounds: {left: 201, top: 185, right: 232, bottom: 201},
     _proto: @226
    };



clearButton := /* child of LittleLispMainView */
    {
     buttonClickScript:
       func()
       begin
       	// Clear the input area.
       	SetValue(inputArea, 'text, "");
       end,
     text: "Clear",
     viewBounds: {left: 163, top: 217, right: 194, bottom: 233},
     _proto: @226
    };



leftParenButton := /* child of LittleLispMainView */
    {
     buttonClickScript:
       func()
       begin
       	// Send "keystroke" ("(") to input view.
       	//
       	PostKeyString(inputArea, text);	
       end,
     text: "(",
     viewBounds: {left: 154, top: 239, right: 164, bottom: 255},
     _proto: @226
    };



rightParenButton := /* child of LittleLispMainView */
    {
     buttonClickScript:
       func()
       begin
       	// Send "keystroke" (")") to input view.
       	//
       	PostKeyString(inputArea, text);
       end,
     text: ")",
     viewBounds: {left: 169, top: 239, right: 179, bottom: 255},
     _proto: @226
    };


outputView := /* child of LittleLispMainView */ LinkedSubview(outputView,
{viewBounds: {left: 13, top: 124, right: 103, bottom: 144}})// View outputView
is declared to LittleLispMainView

specialForms := /* child of LittleLispMainView */
    {
     labelCommands:
       ["and ", "begin ", "cond (", "defun ", "error ", "frame ((", "if ", "lambda (", 
        "let* ((", "or ", "quote ", "repeat ", "setq ", "while "],
     text: "Special Forms",
     viewBounds: {left: 3, top: 287, bottom: 300, right: 143},
     labelActionScript:
       func(cmd)
       begin
       	// Send "keystrokes" to input view.
       	//
       	PostKeyString(inputArea, labelCommands[cmd]);
       end,
     _proto: @190
    };



keyboardButton := /* child of LittleLispMainView */
    {viewBounds: {left: 201, top: 217, right: 232, bottom: 233},
     viewFlags: 515,
     _proto: @434
    };



loadButton := /* child of LittleLispMainView */
    {
     buttonClickScript:
       func()
       // Create a list of Notepad file names for user to choose from.
       // The loading of this file occurs in PickActionScript.
       begin
       	local soup, curs, entry;
       	local globalVars := GetGlobals();
       	local Integer i;
       	local Boolean found;
       	local String theName;
       		
       	// Build a list and render a popup menu of Notepad file names.
       	if GetRoot().paperroll then 
       	begin
       		soup := GetUnionSoup("Notes");
       		curs := soup:Query(nil);
       		entry := curs:Entry(); 
       		while entry do 
       		begin 
       			if HasSlot(entry, 'labels) then
       			begin
       				theName := SPrintObject(entry.labels);
       				
       				// Don't add this name to the list if it's already present.
       				found := nil;
       				
       				for i:=0 to Length(globalVars.littleLisp.notepadFiles)-1 do
       					if StrExactCompare(globalVars.littleLisp.notepadFiles[i].item, 
       							  	   	   theName) = 0 then
       				begin
       					found := true; 			
       					break;
       				end;
       					
       				if not found then AddArraySlot(globalVars.littleLisp.notepadFiles, 
       							 				   { item: theName });
       			end;
       			entry := curs:Next(); 
       		end;
       		 		
       		if Length(globalVars.littleLisp.notepadFiles) > 0 then
       		begin
       			Sort(globalVars.littleLisp.notepadFiles, '|str<|, 'item);
       			self:PopupMenu(globalVars.littleLisp.notepadFiles, nil);
       		end;
       	end;
       end,
     text: "Load",
     viewBounds: {left: 201, top: 239, right: 232, bottom: 255},
     PickActionScript:
       func(itemSelected)
       // Read the text of the selected notepad file and evaluate it.
       begin	
       	local soup, curs, entry, i, inToks, inSexpr, outSexpr, output;
       	local String theText := "";
       	local Boolean allWhiteSpace := true;
       	local string selection := GetGlobals().littleLisp.notepadFiles[itemSelected].item;
       	local notepadFile := Intern(selection);
       	
       	local progress := func(progressView)
       	begin
       		// Get any Notepad entries (from the Notes application) which
       		// correspond to the specified file.
       		// See McKeehan and Rhodes, p 266.
       		if GetRoot().paperroll then 
       		begin
       			soup := GetUnionSoup("Notes");
       			curs := soup:Query(nil);
       			entry := curs:Entry(); 
       			while entry do 
       			begin 
       				if HasSlot(entry, 'labels) and entry.labels = notepadFile then
       				for i:=0 to Length(entry.data)-1 do
       				begin
       					progressView:SetStatus('vBarber, {barber: true,
       										titleText: "Loading from \"" & selection & "\" note file..."});
       					// Concatenate all strings in a note adding space
       					// between each.
       					theText := theText && entry.data[i].text;
       				end;
       				entry := curs:Next();
       			end;
       		
       			// Don't proceed if there's nothing but whitespace.
       			for i:=0 to StrLen(theText)-1 do
       				if not IsWhiteSpace(theText[i]) then 
       				begin
       					allWhiteSpace := nil;
       					break;
       				end;
       						
       			if allWhiteSpace then
       			begin
       				:Notify(kNotifyAlert, "Load Error", 
       						"No text was found in the '" & SPrintObject(notepadFile) & 
       						"' File in the Notes application.");
       				return;
       			end;	
       		end;
       		else
       		begin
       			:Notify(kNotifyAlert, "Load Error", "The Notes application was not found.");
       			return;
       		end;
       
       		progressView:SetStatus('vBarber, {barber: true, titleText: "Processing..."});
       	
       		// ** The code is the same as for evalButton.buttonClickScript() from here on
       		// except for the parameter passed to lexer:tokenizer(). Should factor this code
       		// out.
       	
       		// Tokenize.
       		inToks := lexer:tokenize(theText);
       	
       		// Are the parentheses parenthesized?
       		if not reader:parensBalanced(inToks) then
       		begin
       			if outputView exists then
       			begin
       				outputView:Open();
       				outputView:SetText("Error: unbalanced parentheses.");
       			end;
       		end;
       		else
       		begin
       			// Convert to internal form.
       			progressView:SetStatus('vBarber, {barber: true});
       			inSexpr := reader:toSexpr(inToks);
       		
       			// Eval.
       			progressView:SetStatus('vBarber, {barber: true});
       			outSexpr := evaluate:eval(inSexpr, nil);
       
       			// Convert evaluated internal form to printable tokens.
       			// This may include one or more error messages.
       			progressView:SetStatus('vBarber, {barber: true});
       			output := writer:fromSexpr(outSexpr);
       					
       			// Output result to floater window.
       			if outputView exists  and printToggle.viewValue = true then
       			begin
       				// Is the view already open?
       				if outputView.ViewCObject = nil then
       				begin
       					// No, so open it and set the eval result output text.
       					outputView:Open();
       					lispVars.evalResult.outputText := output;
       					lispVars.evalResult.startPos := 0;
       				end
       				else
       					// View is already open, so append to the eval result output text.
       					lispVars.evalResult.outputText := lispVars.evalResult.outputText & output;
       					
       				// Update the view.
       				outputView:SetText(lispVars.evalResult.outputText);
       			end;
       		end;
       	end;
       
       	// Show a progress indicator.	
       	DoProgress('vBarber, {closebox: true, barber: true, statusText: kAppName}, progress);
       end,
     _proto: @226
    };



corePrimitives := /* child of LittleLispMainView */
    {
     labelCommands:
       ["append ",
        "apply ", "ask ", "atom? ", "car ", "cdr ", "cons ", "display ",
        "eq? ", "equal? ", "eval ", "length ", 
        "list ", "list? ", "map ", "member ", "null? ", "not ", "nth ", "seq ", "set-nth! "]
     ,
     text: "General",
     viewBounds: {left: 3, top: 270, bottom: 284, right: 143}
     ,
     labelActionScript:
       func(cmd)
       begin
       	// Send "keystrokes" to input view.
       	//
       	PostKeyString(inputArea, labelCommands[cmd]);			 
       end,
     _proto: @190
    };


inputDialogView := /* child of LittleLispMainView */ LinkedSubview(
inputDialogView,{viewBounds: {left: 13, top: 103, right: 103, bottom: 123}})
// View inputDialogView is declared to LittleLispMainView

symbolBindings := /* child of LittleLispMainView */
    {labelCommands: [],
     text: "Symbol Bindings",
     viewBounds: {left: 3, top: 182, right: 96, bottom: 196},
     PickActionScript:
       func(itemSelected)
       begin
       	local String theItem := GetGlobals().littleLisp.symbolBindings[itemSelected].item;
       	/*if EndsWith(theItem, " " & Chr(402)) then theItem := Substr(theItem, 0, 
       																StrLen(theItem)-2) & " ";*/
       	 
        	// Send "keystrokes" to input view based upon the bound symbol selected.
       	PostKeyString(inputArea, theItem & " ");		
       end,
     ViewClickScript:
       func(unit)
       begin
       	// Call inherited method to get click sound.
       	inherited:?viewClickScript(unit);		// this method is defined internally
       
       	// Render a list of user defined functions as a popup menu.
       	if GetGlobals().littleLisp exists then
       	begin
       		local funcs := GetGlobals().littleLisp.symbolBindings; 
       		if Length(funcs) > 0 then self:PopupMenu(funcs, nil);
       	end;
       
       	return true; // click handled completely
       end,
     _proto: @190
    };



quoteButton := /* child of LittleLispMainView */
    {
     buttonClickScript:
       func()
       begin
       	// Send "keystroke" ("'") to input view.
       	//
       	PostKeyString(inputArea, text);	
       end,
     text: " '",
     viewBounds: {left: 154, top: 261, bottom: 277, right: 164},
     _proto: @226
    };



endParensButton := /* child of LittleLispMainView */
    {
     buttonClickScript:
       func()
       // Balance right parentheses against left and notify if there 
       // are already too many right parentheses. Note that if 
       // parentheses are in the wrong place within an expression,
       // this function will NOT correct that.
       begin
       	local theText := inputArea.text;
       	local lparens := 0;
       	local rparens := 0;
       	local i;
       	
       	for i:=0 to StrLen(theText)-1 do
       		if theText[i] = $( then lparens := lparens + 1;
       		else
       		if theText[i] = $) then rparens := rparens + 1;
       		
       	if rparens > lparens then
       		:Notify(kNotifyAlert, "Error", "There are too many right parentheses.");		
       	else
       	begin
       		count := lparens - rparens;
       		if count > 0 then
       		begin
       			for i:=1 to count do
       				theText := theText & ")"; 
       				
       			SetValue(inputArea, 'text, theText);	
       		end;
       	end;
       end,
     text: "]",
     viewBounds: {left: 184, top: 239, right: 194, bottom: 255},
     _proto: @226
    };



newlineButton := /* child of LittleLispMainView */
    {
     buttonClickScript:
       func()
       // Add a newline to the text. Currently assumes insertion
       // point is at end of text.
       begin
       	local string theText := inputArea.text;
       
       	// Count open parens.
       	local int i;
       	local int openParens := 0;
       	for i:=0 to StrLen(theText)-1 do
       		if theText[i] = $( then openParens := openParens + 1;
       		else
       		if theText[i] = $) then openParens := openParens - 1;
       		
       	// Indent next line.
       	theText := theText & "\n";
       	for i:=1 to openParens do
       		theText := theText & "   ";
       
       	// Output text, linefeed and indent spaces.
       	SetValue(inputArea, 'text, theText);
       end,
     text: ""&Chr(182),
     viewBounds: {left: 184, top: 261, bottom: 277, right: 194},
     _proto: @226
    };



numericPrimitives := /* child of LittleLispMainView */
    {
     labelCommands:
       ["=", ">", "<", "+", "-", "*", "/", "cos", "log", "log10", "pow", "random", "sin", "sqrt", "tan"]
     ,
     text: "Numeric",
     viewBounds: {left: 3, top: 235, right: 143, bottom: 249}
     ,
     labelActionScript:
       func(cmd)
       begin
       	// Send "keystrokes" to input view.
       	//
       	PostKeyString(inputArea, labelCommands[cmd] & " ");			 
       end,
     _proto: @190
    };



gfxPrimitives := /* child of LittleLispMainView */
    {
     labelCommands:
       ["draw-shape", "home", "line-to ", "move-to ", "plot ", "turn ", "walk "]
     ,
     text: "Graphics",
     viewBounds: {left: 3, top: 217, right: 143, bottom: 232},
     labelActionScript:
       func(cmd)
       begin
       	// Send "keystrokes" to input view.
       	//
       	PostKeyString(inputArea, labelCommands[cmd]);			 
       end,
     _proto: @190
    };

_view000 := /* child of gfxPrimitives */
    {viewFlags: 1,
     viewFormat: 337,
     viewBounds: {left: 103, top: 7, right: 102, bottom: 6},
     viewClass: 77
    };





usefulConstants := /* child of LittleLispMainView */
    {labelCommands: ["e", "nil", "\u03c0", "t"],
     text: "Useful Constants",
     viewBounds: {left: 3, top: 199, right: 143, bottom: 214},
     labelActionScript:
       func(cmd)
       begin
       	// Send constants as "keystrokes" to input view.
       	//
       	local string theCmd := labelCommands[cmd];
       	if StrExactCompare(theCmd, "e") = 0 then theCmd := "2.718281828";
       	else	
       	if StrExactCompare(theCmd, "\u03c0") = 0 then theCmd := "3.141592654";
       	PostKeyString(inputArea, theCmd & " ");			 
       end,
     _proto: @190
    };



doubleQuoteButton := /* child of LittleLispMainView */
    {
     buttonClickScript:
       func()
       begin
       	// Send "keystroke" (double quote) to input view.
       	//
       	PostKeyString(inputArea, text);	
       end,
     text: "\"",
     viewBounds: {left: 169, top: 261, bottom: 277, right: 179},
     _proto: @226
    };



hashButton := /* child of LittleLispMainView */
    {
     buttonClickScript:
       func()
       begin
       	// Send "keystroke" ("#") to input view.
       	//
       	PostKeyString(inputArea, text);	
       end,
     text: "#",
     viewBounds: {left: 184, top: 283, bottom: 299, right: 194},
     _proto: @226
    };


gfxView := /* child of LittleLispMainView */ LinkedSubview(gfxWdw,
{viewBounds: {left: 13, top: 145, right: 103, bottom: 165}})// View gfxView
is declared to LittleLispMainView

printToggle := /* child of LittleLispMainView */
    {text: "Print?",
     viewBounds: {left: 148, top: 172, right: 196, bottom: 192},
     viewValue: true
     ,
     _proto: @164
    };
// View printToggle is declared to LittleLispMainView



storeButton := /* child of LittleLispMainView */
    {
     buttonClickScript:
       func()
       // Create a list of Notepad file names for user to choose from.
       // The storing into this file occurs in PickActionScript.
       begin
       	local soup, curs, entry;
       	local globalVars := GetGlobals();
       	local i, found;
       	local String theName;
       		
       	// Build a list and render a popup menu of Notepad file names.
       	if GetRoot().paperroll then 
       	begin
       		soup := GetUnionSoup("Notes");
       		curs := soup:Query(nil);
       		entry := curs:Entry(); 
       		while entry do 
       		begin 
       			if HasSlot(entry, 'labels) then
       			begin
       				theName := SPrintObject(entry.labels);
       				
       				// Don't add this name to the list if it's already present.
       				found := nil;
       				
       				for i:=0 to Length(globalVars.littleLisp.notepadFiles)-1 do
       					if StrExactCompare(globalVars.littleLisp.notepadFiles[i].item, 
       							  	   	   theName) = 0 then
       				begin
       					found := true; 			
       					break;
       				end;
       					
       				if not found then AddArraySlot(globalVars.littleLisp.notepadFiles, 
       							 				   { item: theName });
       			end;
       			entry := curs:Next(); 
       		end;
       		 
       		if Length(globalVars.littleLisp.notepadFiles) > 0 then
       		begin
       			Sort(globalVars.littleLisp.notepadFiles, '|str<|, 'item);
       			self:PopupMenu(globalVars.littleLisp.notepadFiles, nil);
       		end;
       	end;
       end,
     text: "Store",
     viewBounds: {left: 201, top: 261, bottom: 277, right: 232},
     PickActionScript:
       func(itemSelected)
       // Add a note to the the specified file in the Notes application.
       begin
       	local notesApp := GetRoot().paperroll;
       	if notesApp then
       	begin
       		local string fileName := GetGlobals().littleLisp.notepadFiles[itemSelected].item;
       		local notepadFile := Intern(fileName);
       		local theNote := notesApp:MakeTextNote(GetGlobals().littleLisp.mainView.inputArea.text, nil);
       		theNote.labels := notepadFile; // set the Notes file
       		notesApp:NewNote(theNote, nil, nil);
       		:Notify(kNotifyQAlert, "Store", 
       							   "The input area has been stored as a \"" & fileName & "\" note.");
       	end;
       end,
     _proto: @226
    };



notesButton := /* child of LittleLispMainView */
    {
     buttonClickScript:
       func()
       begin
       	// Open the Notes application if it exists.
       	local notesApp := GetRoot().paperroll;
       	if notesApp then notesApp:Open();
       end,
     text: "Notes",
     viewBounds: {left: 201, top: 283, bottom: 299, right: 232},
     _proto: @226
    };



commentButton := /* child of LittleLispMainView */
    {
     buttonClickScript:
       func()
       begin
       	// Send "keystroke" (;;) to input view.
       	//
       	PostKeyString(inputArea, text);	
       end,
     text: ";",
     viewBounds: {left: 169, top: 283, bottom: 299, right: 179},
     _proto: @226
    };



stringPrimitives := /* child of LittleLispMainView */
    {
     labelCommands:
       ["read-string", "string ", "string? ", "string-copy ", "string-length ", "string->number ", "substring ", "write-string "]
     ,
     text: "String",
     viewBounds: {left: 3, top: 252, bottom: 267, right: 143},
     labelActionScript:
       func(cmd)
       begin
       	// Send "keystrokes" to input view.
       	//
       	PostKeyString(inputArea, labelCommands[cmd]);			 
       end,
     _proto: @190
    };



infoButton := /* child of LittleLispMainView */
    {viewFlags: 515,
     viewBounds: {left: -114, top: 52, bottom: 63, right: -101},
     DoInfoAbout:
       func()
       begin
       	AsyncConfirm("LittleLisp 1.01, Copyright  David Benn, 1998-2000",
       				["OK"], func(button) begin end);				
       end,
     DoInfoHelp:
       func()
       begin
       	// Open Newton Book if present.
       	// See 5-4, Newton Book Maker docs.
       	if getRoot().copperfield:WhereIsBook("129121291916") then 
       		getRoot().copperfield:OpenBook("129121291916");
       	else
       		:Notify(kNotifyAlert, "Help Book Error", 
       				"LittleLisp help book not present.");
       end,
     _proto: @478
    };



breakToggle := /* child of LittleLispMainView */
    {text: "Break?",
     viewBounds: {left: 148, top: 193, right: 196, bottom: 213},
     viewValue: nil,
     _proto: @164
    };
// View breakToggle is declared to LittleLispMainView




constant |layout_LittleLisp.lyt| :=  LittleLispMainView;
// End of file LittleLisp.lyt



