diff --git a/docs/medley-irm/001-TITLEPAGE.TEDIT b/docs/medley-irm/001-TITLEPAGE.TEDIT index 598f5fb0..2948d62b 100644 Binary files a/docs/medley-irm/001-TITLEPAGE.TEDIT and b/docs/medley-irm/001-TITLEPAGE.TEDIT differ diff --git a/docs/medley-irm/003-TOC.TEDIT b/docs/medley-irm/003-TOC.TEDIT index 407e8f83..258204e8 100644 --- a/docs/medley-irm/003-TOC.TEDIT +++ b/docs/medley-irm/003-TOC.TEDIT @@ -1,6 +1,486 @@ -1 MEDLEY REFERENCE 1 MEDLEY REFERENCE TABLE OF CONTENTS 1 TABLE OF CONTENTS 1 TABLE of CONTENTS 6 Volume 1 - Lanuage Reference 1. Introduction 1 2. Litatoms (Symbols) 2-1 Using Symbols as Variables 2-1 Function Definition Cells 2-3 Property Lists 2-4 Print Names 2-5 Characters and Character Codes 2-9 3. Lists 3-1 Creating Lists 3-3 Building Lists from Left to Right 3-4 Copying Lists 3-6 Extracting Tails of Lists 3-6 Counting List Cells 3-8 Logical Operations 3-9 Searching Lists 3-10 Substitution Functions 3-10 Association Lists and Property Lists 3-11 Sorting Lists 3-13 Other List Functions 3-15 4. Strings 4-1 5. Arrays 5-1 6. Hash Arrays 6-1 Hash Overflow 6-3 User-Specified Hashing Functions 6-3 7. Numbers and Arithmetic Functions 7-1 Generic Arithmetic 7-2 Integer Arithmetic 7-3 Logical Arithmetic Functions 7-6 Floating-Point Arithmetic 7-8 Other Arithmetic Functions 7-10 8. Record Package 8-1 FETCH and REPLACE 8-1 CREATE 8-2 TYPE? 8-3 WITH 8-4 Record Declarations 8-4 Record Types 8-5 Optional Record Specifications 8-10 Defining New Record Types 8-12 Record Manipulation Functions 8-12 Changetran 8-13 Built-in and User Data Types 8-15 9. Conditionals and Iterative Statements 9-1 Data Type Predicates 9-1 Equality Predicates 9-2 Logical Predicates 9-3 COND Conditional Function 9-3 The IF Statement 9-4 Selection Functions 9-5 PROG and Associated Control Functions 9-6 The Iterative Statement 9-7 I.s. Types 9-8 Iterative Variable I.s.oprs 9-9 Condition I.s.oprs 9-12 Other I.s.oprs 9-13 Miscellaneous Hints on I.s.oprs 9-13 Errors in Iterative Statements 9-15 Defining New Iterative Statement Operators 9-15 10. Function Definition, Manipulation, and Evaluation 10-1 Function Types 10-2 Lambda-Spread Functions 10-2 Nlambda-Spread Functions 10-3 Lambda-Nospread Functions 10-4 Nlambda-Nospread Functions 10-4 Compiled Functions 10-5 Function Type Functions 10-5 Defining Functions 10-7 Function Evaluation 10-1 Iterating and Mapping Functions 10-1 Function Arguments 10-1 Macros 10-1 DEFMACRO 10-15 Interpreting Macros 10-15 11. Variable Binds and the Interlisp Stack 11-1 Spaghetti Stack 11-2 Stack Functions 11-3 Searching the Stack 11-4 Variable Binds in Stack Frames 11-5 Evaluating Expressions in Stack Frames 11-6 Altering Flow of Control 11-6 Releasing and Reusing Stack Pointers 11-7 Backtrace Functions 11-8 Other Stack Functions 11-10 The Stack and the Interpreter 11-10 Generators 11-12 Coroutines 11-14 Possibilities Lists 11-15 12. Miscellaneous 12-1 Greeting and Initialization Files 12-1 Idle Mode 12-3 Saving Virtual Memory State 12-5 System Version Information 12-9 Date and Time Functions 12-11 Timers and Duration Functions 12-13 Resources 12-15 A Simple Example 12-16 Trade-offs in More Complicated Cases 12-18 Macros for Accessing Resources 12-18 Saving Resources in a File 12-19 Pattern Matching 12-19 Pattern Elements 12-20 Element Patterns 12-20 Segment Patterns 12-21 Assignments 12-23 Place-Markers 12-23 Replacements 12-24 Reconstruction 12-24 Examples 12-25 Volume 2 - Environment Reference 13. Interlisp Executive 13-1 Input Formats 13-3 Programmer's Assistant Commands 13-4 Event Specification 13-4 Commands 13-6 P.A. Commands Applied to P.A. Commands 13-15 Changing the Programmer's Assistant 13-16 Undoing 13-19 Undoing Out of Order 13-20 SAVESET 13-21 UNDONLSETQ and RESETUNDO 13-22 Format and Use of the History List 13-23 Programmer's Assistant Functions 13-26 The Editor and the Programmer's Assistant 13-32 14. Errors and Breaks 14-1 Breaks 14-1 Break Windows 14-2 Break Commands 14-3 Controlling When to Break 14-10 Break Window Variables 14-11 Creating Breaks with BREAK1 14-12 Signalling Errors 14-14 Catching Errors 14-16 Changing and Restoring System State 14-18 Error List 14-20 15. Breaking, Tracing, and Advising 15-1 Breaking Functions and Debugging 15-1 Advising 15-7 Implementation of Advising 15-7 Advise Functions 15-8 16. List Structure Editor 16-1 SEdit 16-1 Local Attention-Changing Commands 16-10 Commands That Search 16-14 Search Algorithm 16-15 Search Commands 16-16 Location Specification 16-18 Commands That Save and Restore the Edit Chain 16-21 Commands That Modify Structure 16-22 Implementation 16-23 The A, B, and : Commands 16-24 Form Oriented Editing and the Role of UP 16-26 Extract and Embed 16-26 The MOVE Command 16-28 Commands That Move Parentheses 16-30 TO and THRU 16-31 The R Command 16-34 Commands That Print 16-35 Commands for Leaving the Editor 16-37 Nested Calls to Editor 16-39 Manipulating the Characters of an Atom or String 16-39 Manipulating Predicates and Conditional Expressions 16-40 History Commands in the Editor 16-41 Miscellaneous Commands 16-41 Commands That Evaluate 16-43 Commands That Test 16-45 Edit Macros 16-46 Undo 16-48 EDITDEFAULT 16-50 Editor Functions 16-51 Time Stamps 16-57 17. File Package 17-1 Loading Files 17-3 Storing Files 17-8 Remaking a Symbolic File 17-12 Loading Files in a Distributed Environment 17-13 Marking Changes 17-13 Noticing Files 17-15 Distributing Change Information 17-16 File Package Types 17-16 Functions for Manipulating Typed Definitions 17-19 Defining New File Package Types 17-23 File Package Commands 17-25 Functions and Macros 17-26 Variables 17-27 Litatom Properties 17-29 Miscellaneous File Package Commands 17-30 DECLARE: 17-31 Exporting Definitions 17-33 FileVars 17-34 Defining New File Package Commands 17-35 Functions for Manipulating File Command Lists 17-37 Symbolic File Format 17-38 Copyright Notices 17-40 Functions Used Within Source Files 17-42 File Maps 17-42 18. Compiler 18-1 Compiler Printout 18-2 Global Variables 18-3 Local Variables and Special Variables 18-4 Constants 18-5 Compiling Function Calls 18-6 FUNCTION and Functional Arguments 18-7 Open Functions 18-8 COMPILETYPELST 18-8 Compiling CLISP 18-9 Compiler Functions 18-9 Block Compiling 18-12 Block Declarations 18-13 Block Compiling Functions 18-15 Compiler Error Messages 18-16 19. DWIM 20-1 Spelling Correction Protocol 20-3 Parentheses Errors Protocol 20-4 Undefined Function T Errors 20-4 DWIM Operation 20-5 DWIM Correction: Unbound Atoms 20-6 Undefined CAR of Form 20-7 Undefined Function in APPLY 20-8 DWIMUSERFORMS 20-8 DWIM Functions and Variables 20-10 Spelling Correction 20-11 Synonyms 20-12 Spelling Lists 20-12 Generators for Spelling Correction 20-14 Spelling Corrector Algorithm 20-14 Spelling Corrector Functions and Variables 20-15 20. CLISP 21-1 CLISP Interaction with User 21-4 CLISP Character Operators 21-5 Declarations 21-9 CLISP Operation 21-10 CLISP Translations 21-12 DWIMIFY 21-13 CLISPIFY 21-16 Miscellaneous Functions and Variables 21-18 CLISP Internal Conventions 21-20 21. Performance Issues 22-1 Storage Allocation and Garbage Collection 22-1 Variable Bindings 22-4 Performance Measuring 22-5 BREAKDOWN 22-7 GAINSPACE 22-9 Using Data Types Instead of Records 22-9 Using Incomplete File Names 22-10 Using "Fast" and "Destructive" Functions 22-10 22. Processes 23-1 Creating and Destroying Processes 23-1 Process Control Constructs 23-4 Events 23-5 Monitors 23-7 Global Resources 23-8 Typein and the TTY Process 23-9 Switing the TTY Process 23-9 Handling of Interrupts 23-11 Keeping the Mouse Alive 23-12 Process Status Window 23-12 Non-Process Compatibility 23-14 Volume 3 - I/O Reference 23. Streams and Files 24-1 Opening and Closing File Streams 24-1 File Names 24-4 Incomplete File Names 24-7 Version Recognition 24-9 Using File Names Instead of Streams 24-10 File Name Efficiency Considerations 24-11 Obsolete File Opening Functions 24-11 Converting Old Programs 24-11 Using Files with Processes 24-12 File Attributes 24-12 Closing and Reopening Files 24-15 Local Hard Disk Device 24-16 Floppy Disk Device 24-18 I/O Operations To and From Strings 24-22 Temporary Files and the CORE Device 24-23 NULL Device 24-24 Deleting, Copying, and Renaming Files 24-24 Searching File Directories 24-24 Listing File Directories 24-25 File Servers 24-28 PUP File Server Protocols 24-28 Xerox NS File Server Protocols 24-28 Operating System Designations 24-29 Logging In 24-30 Abnormal Conditions 24-31 24. Input/Output Functions 25-1 Specifying Streams for Input/Output Functions 25-1 Input Functions 25-2 Output Functions 25-6 PRINTLEVEL 25-8 Printing Numbers 25-10 User Defined Printing 25-12 Printing Unusual Data Structures 25-13 Random Access File Operations 25-14 Input/Output Operations with Characters and Bytes 25-17 PRINTOUT 25-17 Horizontal Spacing Commands 25-19 Vertical Spacing Commands 25-20 Special Formatting Controls 25-20 Printing Specifications 25-20 Paragraph Format 25-21 Right-Flushing 25-21 Centering 25-22 Numbering 25-22 Escaping to Lisp 25-23 User-Defined Commands 25-23 Special Printing Functions 25-24 READFILE and WRITEFILE 25-25 Read Tables 25-25 Read Table Functions 25-26 Syntax Classes 25-26 Read Macros 25-29 25. User Input/Output Packages 26-1 Inspector 26-1 Calling the Inspector 26-1 Multiple Ways of Inspecting 26-2 Inspect Windows 26-3 Inspect Window Commands 26-3 Interaction with Break Windows 26-4 Controlling the Amount Displayed During Inspection 26-4 Inspect Macros 26-4 INSPECTWs 26-5 PROMPTFORWORD 26-7 ASKUSER 26-9 Format of KEYLST 26-10 Options 26-12 Operation 26-13 Completing a Key 26-14 Special Keys 26-15 Startup Protocol and Typeahead 26-16 TTYIN Typein Editor 26-17 Entering Input with TTYIN 26-17 Mouse Commands (Interlisp-D Only) 26-19 Display Editing Commands 26-19 Using TTYIN for Lisp Input 26-22 Useful Macros 26-23 Programming with TTYIN 26-23 Using TTYIN as a General Editor 26-25 ?= Handler 26-26 Read Macros 26-27 Assorted Flags 26-28 Special Responses 26-29 Display Types 26-30 Prettyprint 26-31 Comment Feature 26-33 Comment Pointers 26-34 Converting Comments to Lowercase 26-35 Special Prettyprint Controls 26-36 26. Graphics Output Operations 27-1 Primitive Graphics Concepts 27-1 Positions 27-1 Regions 27-1 Bitmaps 27-2 Textures 27-5 Opening Image Streams 27-6 Accessing Image Stream Fields 27-8 Current Position of an Image Stream 27-10 Moving Bits Between Bitmaps with BITBLT 27-11 Drawing Lines 27-13 Drawing Curves 27-14 Miscellaneous Drawing and Printing Operations 27-15 Drawing and Shading Grids 27-17 Display Streams 27-18 Fonts 27-19 Font Files and Font Directories 27-24 Font Profiles 27-24 Image Objects 27-27 IMAGEFNS Methods 27-28 Registering Image Objects 27-30 Reading and Writing Image Objects on Files 27-31 Copying Image Objects Between Windows 27-31 Implementation of Image Streams 27-32 27. Windows and Menus 28-1 Using the Window System 28-1 Changing the Window System 28-6 Interactive Display Functions 28-7 Windows 28-9 Window Properties 28-10 Creating Windows 28-10 Opening and Closing Windows 28-11 Redisplaying Windows 28-12 Reshaping Windows 28-13 Moving Windows 28-14 Exposing and Burying Windows 28-16 Shrinking Windows into Icons 28-16 Coordinate Systems, Extents, and Scrolling 28-18 Mouse Activity in Windows 28-21 Terminal I/O and Page Holding 28-22 TTY Process and the Caret 28-23 Miscellaneous Window Functions 28-24 Miscellaneous Window Properties 28-25 Example: A Scrollable Window 28-26 Menus 28-28 Menu Fields 28-29 Miscellaneous Menu Functions 28-32 Examples of Menu Use 28-32 Attached Windows 28-34 Attaching Menus to Windows 28-37 Attached Prompt Windows 28-38 Window Operations and Attached Windows 28-39 Window Properties of Attached Windows 28-41 28. Hardcopy Facilities 29-1 Hardcopy Functions 29-1 Low-Level Hardcopy Variables 29-4 29. Terminal Input/Output 30-1 Interrupt Characters 30-1 Terminal Tables 30-4 Terminal Syntax Classes 30-4 Terminal Control Functions 30-5 Line-Buffering 30-7 Dribble Files 30-10 Cursor and Mouse 30-10 Changing the Cursor Image 30-11 Flashing Bars on the Cursor 30-13 Cursor Position 30-13 Mouse Button Testing 30-14 Low-Level Mouse Functions 30-15 Keyboard Interpretation 30-15 Display Screen 30-18 Miscellaneous Terminal I/O 30-19 30. Ethernet 31-1 Ethernet Protocols 31-1 Protocol Layering 31-1 Level Zero Protocols 31-2 Level One Protocols 31-2 Higher Level Protocols 31-3 Connecting Networks: Routers and Gateways 31-3 Addressing Conflicts with Level Zero Mediums 31-3 References 31-4 Higher-Level PUP Protocol Functions 31-4 Higher-Level NS Protocol Functions 31-5 Name and Address Conventions 31-5 Clearinghouse Functions 31-7 NS Printing 31-9 SPP Stream Interface 31-9 Courier Remote Procedure Call Protocol 31-11 Defining Courier Programs 31-11 Courier Type Definitions 31-12 Pre-defined Types 31-13 Constructed Types 31-13 User Extensions to the Type Language 31-15 Performing Courier Transactions 31-16 Expedited Procedure Call 31-17 Expanding Ring Broadcast 31-18 Using Bulk Data Transfer 31-18 Courier Subfunctions for Data Transfer 31-19 Level One Ether Packet Format 31-20 PUP Level One Functions 31-21 Creating and Managing Pups 31-21 Sockets 31-22 Sending and Receiving Pups 31-23 Pup Routing Information 31-23 Miscellaneous PUP Utilities 31-24 PUP Debugging Aids 31-24 NS Level One Functions 31-28 Creating and Managing XIPs 31-28 NS Sockets 31-28 Sending and Receiving XIPs 31-29 NS Debugging Aids 31-29 Support for Other Level One Protocols 31-29 The SYSQUEUE Mechanism 31-31 Glossary GLOSSARY-1 Index INDEX-1 [This page intentionally left blank](LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (192 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (192 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (408 612 204 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))))),,"-T3 T25TTf2 5F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGR5F PAGEHEADING VERSOHEADF PAGEHEADING RECTOHEADE PAGEHEADINGFOOTINGVE PAGEHEADINGFOOTINGRPALATINO  HELVETICA - HELVETICAPALATINO MODERN -MODERN -CLASSIC  HRULE.GETFNPALATINO   HRULE.GETFNPALATINO  - HRULE.GETFNPALATINO   HRULE.GETFNPALATINO   HRULE.GETFNCLASSIC#&*%*!   - %#"/*!&%1= !% 2%-+$'! $,&"!%.* )'0  "*+&!! (4% 0&&7:% 1&4'+*4*+'!"!!&"#*$2!,!/)"/'    &*+'!")*,!!&%"3($8#!#"&"%9 &!) "'($&##*.4  &!2-& # #$$2!%!&'$ $".-"!!!#!!03)(#. +&-$""#"",$4}z \ No newline at end of file +1 + +MEDLEY REFERENCE +1 + +MEDLEY REFERENCE +TABLE OF CONTENTS +1 + +TABLE OF CONTENTS +1 + + +TABLE of CONTENTS +6 + +Volume 1 - Lanuage Reference +1. Introduction 1 +2. Litatoms (Symbols) 2-1 +Using Symbols as Variables 2-1 +Function Definition Cells 2-3 +Property Lists 2-4 +Print Names 2-5 +Characters and Character Codes 2-9 +3. Lists 3-1 +Creating Lists 3-3 +Building Lists from Left to Right 3-4 +Copying Lists 3-6 +Extracting Tails of Lists 3-6 +Counting List Cells 3-8 +Logical Operations 3-9 +Searching Lists 3-10 +Substitution Functions 3-10 +Association Lists and Property Lists 3-11 +Sorting Lists 3-13 +Other List Functions 3-15 +4. Strings 4-1 +5. Arrays 5-1 +6. Hash Arrays 6-1 +Hash Overflow 6-3 +User-Specified Hashing Functions 6-3 +7. Numbers and Arithmetic Functions 7-1 +Generic Arithmetic 7-2 +Integer Arithmetic 7-3 +Logical Arithmetic Functions 7-6 +Floating-Point Arithmetic 7-8 +Other Arithmetic Functions 7-10 +8. Record Package 8-1 +FETCH and REPLACE 8-1 +CREATE 8-2 +TYPE? 8-3 +WITH 8-4 +Record Declarations 8-4 + Record Types 8-5 + Optional Record Specifications 8-10 +Defining New Record Types 8-12 +Record Manipulation Functions 8-12 +Changetran 8-13 +Built-in and User Data Types 8-15 +9. Conditionals and Iterative Statements 9-1 +Data Type Predicates 9-1 +Equality Predicates 9-2 +Logical Predicates 9-3 +COND Conditional Function 9-3 +The IF Statement 9-4 +Selection Functions 9-5 +PROG and Associated Control Functions 9-6 +The Iterative Statement 9-7 + I.s. Types 9-8 + Iterative Variable I.s.oprs 9-9 + Condition I.s.oprs 9-12 + Other I.s.oprs 9-13 + Miscellaneous Hints on I.s.oprs 9-13 + Errors in Iterative Statements 9-15 + Defining New Iterative Statement Operators 9-15 +10. Function Definition, Manipulation, and Evaluation 10-1 +Function Types 10-2 + Lambda-Spread Functions 10-2 + Nlambda-Spread Functions 10-3 + Lambda-Nospread Functions 10-4 + Nlambda-Nospread Functions 10-4 + Compiled Functions 10-5 + Function Type Functions 10-5 +Defining Functions 10-7 +Function Evaluation 10-1 +Iterating and Mapping Functions 10-1 +Function Arguments 10-1 +Macros 10-1 + DEFMACRO 10-15 + Interpreting Macros 10-15 +11. Variable Binds and the Interlisp Stack 11-1 +Spaghetti Stack 11-2 +Stack Functions 11-3 + Searching the Stack 11-4 + Variable Binds in Stack Frames 11-5 + Evaluating Expressions in Stack Frames 11-6 + Altering Flow of Control 11-6 + Releasing and Reusing Stack Pointers 11-7 + Backtrace Functions 11-8 + Other Stack Functions 11-10 +The Stack and the Interpreter 11-10 +Generators 11-12 +Coroutines 11-14 +Possibilities Lists 11-15 +12. Miscellaneous 12-1 +Greeting and Initialization Files 12-1 +Idle Mode 12-3 +Saving Virtual Memory State 12-5 +System Version Information 12-9 +Date and Time Functions 12-11 +Timers and Duration Functions 12-13 +Resources 12-15 + A Simple Example 12-16 + Trade-offs in More Complicated Cases 12-18 + Macros for Accessing Resources 12-18 + Saving Resources in a File 12-19 +Pattern Matching 12-19 + Pattern Elements 12-20 + Element Patterns 12-20 + Segment Patterns 12-21 + Assignments 12-23 + Place-Markers 12-23 + Replacements 12-24 + Reconstruction 12-24 + Examples 12-25 +Volume 2 - Environment Reference +13. Interlisp Executive 13-1 +Input Formats 13-3 +Programmer's Assistant Commands 13-4 + Event Specification 13-4 + Commands 13-6 + P.A. Commands Applied to P.A. Commands 13-15 +Changing the Programmer's Assistant 13-16 +Undoing 13-19 + Undoing Out of Order 13-20 + SAVESET 13-21 + UNDONLSETQ and RESETUNDO 13-22 +Format and Use of the History List 13-23 +Programmer's Assistant Functions 13-26 +The Editor and the Programmer's Assistant 13-32 +14. Errors and Breaks 14-1 +Breaks 14-1 +Break Windows 14-2 +Break Commands 14-3 +Controlling When to Break 14-10 +Break Window Variables 14-11 +Creating Breaks with BREAK1 14-12 +Signalling Errors 14-14 +Catching Errors 14-16 +Changing and Restoring System State 14-18 +Error List 14-20 +15. Breaking, Tracing, and Advising 15-1 +Breaking Functions and Debugging 15-1 +Advising 15-7 + Implementation of Advising 15-7 + Advise Functions 15-8 +16. List Structure Editor 16-1 +SEdit 16-1 +Local Attention-Changing Commands 16-10 +Commands That Search 16-14 + Search Algorithm 16-15 + Search Commands 16-16 + Location Specification 16-18 +Commands That Save and Restore the Edit Chain 16-21 +Commands That Modify Structure 16-22 + Implementation 16-23 + The A, B, and : Commands 16-24 + Form Oriented Editing and the Role of UP 16-26 + Extract and Embed 16-26 + The MOVE Command 16-28 + Commands That Move Parentheses 16-30 + TO and THRU 16-31 + The R Command 16-34 +Commands That Print 16-35 +Commands for Leaving the Editor 16-37 +Nested Calls to Editor 16-39 +Manipulating the Characters of an Atom or String 16-39 +Manipulating Predicates and Conditional Expressions 16-40 +History Commands in the Editor 16-41 +Miscellaneous Commands 16-41 +Commands That Evaluate 16-43 +Commands That Test 16-45 +Edit Macros 16-46 +Undo 16-48 +EDITDEFAULT 16-50 +Editor Functions 16-51 +Time Stamps 16-57 +17. File Package 17-1 +Loading Files 17-3 +Storing Files 17-8 +Remaking a Symbolic File 17-12 +Loading Files in a Distributed Environment 17-13 +Marking Changes 17-13 +Noticing Files 17-15 +Distributing Change Information 17-16 +File Package Types 17-16 + Functions for Manipulating Typed Definitions 17-19 + Defining New File Package Types 17-23 +File Package Commands 17-25 + Functions and Macros 17-26 + Variables 17-27 + Litatom Properties 17-29 + Miscellaneous File Package Commands 17-30 + DECLARE: 17-31 + Exporting Definitions 17-33 + FileVars 17-34 + Defining New File Package Commands 17-35 +Functions for Manipulating File Command Lists 17-37 +Symbolic File Format 17-38 + Copyright Notices 17-40 + Functions Used Within Source Files 17-42 + File Maps 17-42 +18. Compiler 18-1 +Compiler Printout 18-2 +Global Variables 18-3 +Local Variables and Special Variables 18-4 +Constants 18-5 +Compiling Function Calls 18-6 +FUNCTION and Functional Arguments 18-7 +Open Functions 18-8 +COMPILETYPELST 18-8 +Compiling CLISP 18-9 +Compiler Functions 18-9 +Block Compiling 18-12 + Block Declarations 18-13 + Block Compiling Functions 18-15 +Compiler Error Messages 18-16 +19. DWIM 20-1 +Spelling Correction Protocol 20-3 +Parentheses Errors Protocol 20-4 +Undefined Function T Errors 20-4 +DWIM Operation 20-5 + DWIM Correction: Unbound Atoms 20-6 + Undefined CAR of Form 20-7 + Undefined Function in APPLY 20-8 +DWIMUSERFORMS 20-8 +DWIM Functions and Variables 20-10 +Spelling Correction 20-11 + Synonyms 20-12 + Spelling Lists 20-12 + Generators for Spelling Correction 20-14 + Spelling Corrector Algorithm 20-14 + Spelling Corrector Functions and Variables 20-15 +20. CLISP 21-1 +CLISP Interaction with User 21-4 +CLISP Character Operators 21-5 +Declarations 21-9 +CLISP Operation 21-10 +CLISP Translations 21-12 +DWIMIFY 21-13 +CLISPIFY 21-16 +Miscellaneous Functions and Variables 21-18 +CLISP Internal Conventions 21-20 +21. Performance Issues 22-1 +Storage Allocation and Garbage Collection 22-1 +Variable Bindings 22-4 +Performance Measuring 22-5 +BREAKDOWN 22-7 +GAINSPACE 22-9 +Using Data Types Instead of Records 22-9 +Using Incomplete File Names 22-10 +Using "Fast" and "Destructive" Functions 22-10 +22. Processes 23-1 +Creating and Destroying Processes 23-1 +Process Control Constructs 23-4 +Events 23-5 +Monitors 23-7 +Global Resources 23-8 +Typein and the TTY Process 23-9 + Switing the TTY Process 23-9 + Handling of Interrupts 23-11 +Keeping the Mouse Alive 23-12 +Process Status Window 23-12 +Non-Process Compatibility 23-14 +Volume 3 - I/O Reference +23. Streams and Files 24-1 +Opening and Closing File Streams 24-1 +File Names 24-4 +Incomplete File Names 24-7 +Version Recognition 24-9 +Using File Names Instead of Streams 24-10 + File Name Efficiency Considerations 24-11 + Obsolete File Opening Functions 24-11 + Converting Old Programs 24-11 +Using Files with Processes 24-12 +File Attributes 24-12 +Closing and Reopening Files 24-15 +Local Hard Disk Device 24-16 +Floppy Disk Device 24-18 +I/O Operations To and From Strings 24-22 +Temporary Files and the CORE Device 24-23 +NULL Device 24-24 +Deleting, Copying, and Renaming Files 24-24 +Searching File Directories 24-24 +Listing File Directories 24-25 +File Servers 24-28 + PUP File Server Protocols 24-28 + Xerox NS File Server Protocols 24-28 + Operating System Designations 24-29 + Logging In 24-30 + Abnormal Conditions 24-31 +24. Input/Output Functions 25-1 +Specifying Streams for Input/Output Functions 25-1 +Input Functions 25-2 +Output Functions 25-6 + PRINTLEVEL 25-8 + Printing Numbers 25-10 + User Defined Printing 25-12 + Printing Unusual Data Structures 25-13 +Random Access File Operations 25-14 +Input/Output Operations with Characters and Bytes 25-17 +PRINTOUT 25-17 + Horizontal Spacing Commands 25-19 + Vertical Spacing Commands 25-20 + Special Formatting Controls 25-20 + Printing Specifications 25-20 + Paragraph Format 25-21 + Right-Flushing 25-21 + Centering 25-22 + Numbering 25-22 + Escaping to Lisp 25-23 + User-Defined Commands 25-23 + Special Printing Functions 25-24 +READFILE and WRITEFILE 25-25 +Read Tables 25-25 + Read Table Functions 25-26 + Syntax Classes 25-26 + Read Macros 25-29 +25. User Input/Output Packages 26-1 +Inspector 26-1 + Calling the Inspector 26-1 + Multiple Ways of Inspecting 26-2 + Inspect Windows 26-3 + Inspect Window Commands 26-3 + Interaction with Break Windows 26-4 + Controlling the Amount Displayed During Inspection 26-4 + Inspect Macros 26-4 + INSPECTWs 26-5 +PROMPTFORWORD 26-7 +ASKUSER 26-9 + Format of KEYLST 26-10 + Options 26-12 + Operation 26-13 + Completing a Key 26-14 + Special Keys 26-15 + Startup Protocol and Typeahead 26-16 +TTYIN Typein Editor 26-17 + Entering Input with TTYIN 26-17 + Mouse Commands (Interlisp-D Only) 26-19 + Display Editing Commands 26-19 + Using TTYIN for Lisp Input 26-22 + Useful Macros 26-23 + Programming with TTYIN 26-23 + Using TTYIN as a General Editor 26-25 + ?= Handler 26-26 + Read Macros 26-27 + Assorted Flags 26-28 + Special Responses 26-29 + Display Types 26-30 +Prettyprint 26-31 + Comment Feature 26-33 + Comment Pointers 26-34 + Converting Comments to Lowercase 26-35 + Special Prettyprint Controls 26-36 +26. Graphics Output Operations 27-1 +Primitive Graphics Concepts 27-1 + Positions 27-1 + Regions 27-1 + Bitmaps 27-2 + Textures 27-5 +Opening Image Streams 27-6 +Accessing Image Stream Fields 27-8 +Current Position of an Image Stream 27-10 +Moving Bits Between Bitmaps with BITBLT 27-11 +Drawing Lines 27-13 +Drawing Curves 27-14 +Miscellaneous Drawing and Printing Operations 27-15 +Drawing and Shading Grids 27-17 +Display Streams 27-18 +Fonts 27-19 +Font Files and Font Directories 27-24 +Font Profiles 27-24 +Image Objects 27-27 + IMAGEFNS Methods 27-28 + Registering Image Objects 27-30 + Reading and Writing Image Objects on Files 27-31 + Copying Image Objects Between Windows 27-31 +Implementation of Image Streams 27-32 +27. Windows and Menus 28-1 +Using the Window System 28-1 +Changing the Window System 28-6 +Interactive Display Functions 28-7 +Windows 28-9 + Window Properties 28-10 + Creating Windows 28-10 + Opening and Closing Windows 28-11 + Redisplaying Windows 28-12 + Reshaping Windows 28-13 + Moving Windows 28-14 + Exposing and Burying Windows 28-16 + Shrinking Windows into Icons 28-16 + Coordinate Systems, Extents, and Scrolling 28-18 + Mouse Activity in Windows 28-21 + Terminal I/O and Page Holding 28-22 + TTY Process and the Caret 28-23 + Miscellaneous Window Functions 28-24 + Miscellaneous Window Properties 28-25 + Example: A Scrollable Window 28-26 +Menus 28-28 + Menu Fields 28-29 + Miscellaneous Menu Functions 28-32 + Examples of Menu Use 28-32 +Attached Windows 28-34 + Attaching Menus to Windows 28-37 + Attached Prompt Windows 28-38 + Window Operations and Attached Windows 28-39 + Window Properties of Attached Windows 28-41 +28. Hardcopy Facilities 29-1 +Hardcopy Functions 29-1 +Low-Level Hardcopy Variables 29-4 +29. Terminal Input/Output 30-1 +Interrupt Characters 30-1 +Terminal Tables 30-4 + Terminal Syntax Classes 30-4 + Terminal Control Functions 30-5 + Line-Buffering 30-7 +Dribble Files 30-10 +Cursor and Mouse 30-10 + Changing the Cursor Image 30-11 + Flashing Bars on the Cursor 30-13 + Cursor Position 30-13 + Mouse Button Testing 30-14 + Low-Level Mouse Functions 30-15 +Keyboard Interpretation 30-15 +Display Screen 30-18 +Miscellaneous Terminal I/O 30-19 +30. Ethernet 31-1 +Ethernet Protocols 31-1 + Protocol Layering 31-1 + Level Zero Protocols 31-2 + Level One Protocols 31-2 + Higher Level Protocols 31-3 + Connecting Networks: Routers and Gateways 31-3 + Addressing Conflicts with Level Zero Mediums 31-3 + References 31-4 +Higher-Level PUP Protocol Functions 31-4 +Higher-Level NS Protocol Functions 31-5 + Name and Address Conventions 31-5 + Clearinghouse Functions 31-7 + NS Printing 31-9 + SPP Stream Interface 31-9 + Courier Remote Procedure Call Protocol 31-11 +Defining Courier Programs 31-11 +Courier Type Definitions 31-12 +Pre-defined Types 31-13 +Constructed Types 31-13 +User Extensions to the Type Language 31-15 +Performing Courier Transactions 31-16 +Expedited Procedure Call 31-17 +Expanding Ring Broadcast 31-18 +Using Bulk Data Transfer 31-18 +Courier Subfunctions for Data Transfer 31-19 +Level One Ether Packet Format 31-20 +PUP Level One Functions 31-21 + Creating and Managing Pups 31-21 + Sockets 31-22 + Sending and Receiving Pups 31-23 + Pup Routing Information 31-23 + Miscellaneous PUP Utilities 31-24 + PUP Debugging Aids 31-24 +NS Level One Functions 31-28 + Creating and Managing XIPs 31-28 + NS Sockets 31-28 + Sending and Receiving XIPs 31-29 + NS Debugging Aids 31-29 +Support for Other Level One Protocols 31-29 +The SYSQUEUE Mechanism 31-31 +Glossary GLOSSARY-1 +Index INDEX-1 +[This page intentionally left blank](SEQUENCE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (192 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (ALTERNATE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "" "")) (192 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (408 612 204 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))))1$2$T4 $7TT$f4$1$1"$5 $T7$7$H$ PAGEHEADING VERSOHEADH$ PAGEHEADING VERSOHEADH$ PAGEHEADING RECTOHEADH$ PAGEHEADING RECTOHEADG$ PAGEHEADINGFOOTINGVG$ PAGEHEADINGFOOTINGVG$ PAGEHEADINGFOOTINGRG$ PAGEHEADINGFOOTINGR1PALATINO (CHARPROPS (COLOR . BLACK)).GACHA +(CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK))2 HELVETICA +(CHARPROPS (COLOR . BLACK))2 HELVETICA(CHARPROPS (COLOR . BLACK))0CLASSIC(CHARPROPS (COLOR . BLACK))/MODERN +(CHARPROPS (COLOR . BLACK))/MODERN +(CHARPROPS (COLOR . BLACK)) HRULE.GETFN HRULE.GETFN  HRULE.GETFN   HRULE.GETFN +  HRULE.GETFN#&*%*!   + %#"/*!&%1= !% 2%-+$'! $,&"!%.* )'0  "*+&!! (4% 0&&7:% 1&4'+*4*+'!"!!&"#*$2!,!/)"/'    &*+'!")*,!!&%"3($8#!#"&"%9 &!) "'($&##*.4  &!2-& # #$$2!%!&'$ $".-"!!!#!!03)(#. +&-$""#"",$(((CHARENCODING . MCCS)))PROPS:#DATE:j4}z \ No newline at end of file diff --git a/docs/medley-irm/01-INTRO.TEDIT b/docs/medley-irm/01-INTRO.TEDIT index 1f57223a..451fadc5 100644 Binary files a/docs/medley-irm/01-INTRO.TEDIT and b/docs/medley-irm/01-INTRO.TEDIT differ diff --git a/docs/medley-irm/02-LITATOM.TEDIT b/docs/medley-irm/02-LITATOM.TEDIT index 7749ab2f..21f408be 100644 Binary files a/docs/medley-irm/02-LITATOM.TEDIT and b/docs/medley-irm/02-LITATOM.TEDIT differ diff --git a/docs/medley-irm/04-STRINGS.TEDIT b/docs/medley-irm/04-STRINGS.TEDIT index a17c1e68..4f577bc8 100644 --- a/docs/medley-irm/04-STRINGS.TEDIT +++ b/docs/medley-irm/04-STRINGS.TEDIT @@ -56,24 +56,24 @@ If X is not a string, it is converted to one. For example, SUBSTRING does not actually copy any characters, but simply creates a new string pointer to the characters in X. If OLDPTR is a string pointer, it is modified and returned. (GNC(GNC (Function) NIL NIL ("4") 3) X) [Function] Get Next Character. Returns the next character of the string X (as a symbol); also removes the character from the string, by changing the string pointer. Returns NIL if X is the null string. If X isn't a string, a string is made. Used for sequential access to characters of a string. Example: -(SETQ FOO "ABCDEFG") +_(SETQ FOO "ABCDEFG") "ABCDEFG" -(GNC FOO) +_(GNC FOO) A -(GNC FOO) +_(GNC FOO) B -FOO +_FOO "CDEFG" Note that if A is a substring of B, (GNC A) does not remove the character from B. (GLC(GLC (Function) NIL NIL ("4") 3) X) [Function] Get Last Character. Returns the last character of the string X (as a symbol); also removes the character from the string. Similar to GNC. Example: -(SETQ FOO "ABCDEFG") +_(SETQ FOO "ABCDEFG") "ABCDEFG" -(GLC FOO) +_(GLC FOO) G -(GLC FOO) +_(GLC FOO) F -FOO +_FOO "ABCDE" (CONCAT(CONCAT (Function) NIL NIL ("4") 4) X1 X2 ... XN) [NoSpread Function] Returns a new string which is the concatenation of (copies of) its arguments. Any arguments which are not strings are transformed to strings. Examples: @@ -125,25 +125,25 @@ Note: If NEG = T, STRPOSL must call MAKEBITTABLE whether A is a list or a bit t [This page intentionally left blank] (SEQUENCE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "4-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "4-" "")) (222 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (ALTERNATE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "4-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "4-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "4-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "4-" "")) (222 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (48 612 564 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))))1$6$EVENT2$TE$ PAGEHEADING RIGHTPAGE1HH$506 -$T1l~$1ll$1~~$1l~$1$1$1$7$D$ PAGEHEADINGLEFTBACKMODERN -GACHA -PALATINO TITAN TITAN PALATINO PALATINO TITAN -CLASSIC -TITAN TITAN CLASSIC -MODERN - +$T1l~$1ll$1~~$1l~$1$1$1$7$D$ PAGEHEADINGLEFTBACK/MODERN +(CHARPROPS (COLOR . BLACK)).GACHA +(CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK)).TITAN +(CHARPROPS (COLOR . BLACK))0CLASSIC +(CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK))0CLASSIC +(CHARPROPS (COLOR . BLACK))3 TIMESROMAN -MODERN   IM.CHAP.GETFN)IM.INDEX.GETFN HRULE.GETFN  %D, 8 +T%" +(CHARPROPS (COLOR . BLACK))/MODERN +(CHARPROPS (COLOR . BLACK))/MODERN(CHARPROPS (COLOR . BLACK)) IM.CHAP.GETFN)IM.INDEX.GETFN HRULE.GETFN   %D, 8 +T%"        N  - p"! - H !  #  + p"! + H !  #    S'K   H #.  1" -   6M +   6M &IM.INDEX.GETFN  c  .(,%#)&##8$ e3 IM.INDEX.GETFN@ec   @@ -169,4 +169,4 @@ TIMESROMAN $IM.INDEX.GETFN :.  F # )h+;  ;>: $ -)IM.INDEX.GETFN (- F    3 <%DATE:i.,z \ No newline at end of file +)IM.INDEX.GETFN (- F    3 <%(((CHARENCODING . MCCS)))PROPS:#DATE:j.,z \ No newline at end of file diff --git a/docs/medley-irm/05-ARRAY.TEDIT b/docs/medley-irm/05-ARRAY.TEDIT index 14afeefa..23b013c4 100644 Binary files a/docs/medley-irm/05-ARRAY.TEDIT and b/docs/medley-irm/05-ARRAY.TEDIT differ diff --git a/docs/medley-irm/06-HASHARRAYS.TEDIT b/docs/medley-irm/06-HASHARRAYS.TEDIT index 8529b8e8..bd7b294a 100644 --- a/docs/medley-irm/06-HASHARRAYS.TEDIT +++ b/docs/medley-irm/06-HASHARRAYS.TEDIT @@ -1,79 +1,104 @@ -INTERLISP-D REFERENCE MANUAL HASHARRAYS 6. HASHARRAYS 3 Hash arrays(HASH% ARRAYS NIL Hash% arrays NIL (NIL) 1) let you associate arbitrary Lisp objects (hash keys) with other objects (hash values), so you can get from key to value quickly. There are functions for creating hash arrays, putting a hash key/value pair in a hash array, and quickly retrieving the hash value associated with a given hash key. By default, the hash array functions use EQ for comparing hash keys. This means that if non-symbols are used as hash keys, the exact same object (not a copy) must be used to retrieve the hash value. However, you can specify the function used to compare hash keys and to hash a hash key to a number. You can, for example, create hash arrays where EQUAL but non-EQ strings will hash to the same value. Specifying alternative hashing algorithms is described below. In the description of the functions below, the argument HARRAY should be a hasharray created by HASHARRAY. For convenience in interactive program development, it may also be NIL, in which case a hash array (SYSHASHARRAY) provided by the system is used; you must watch out for confusions if this form is used to associate more than one kind of value with the same key. Note: For backwards compatibility, the hash array functions will accept a list whose CAR is a hash array, and whose CDR is the overflow method for the hash array (see below). However, hash array functions are guaranteed to perform with maximum efficiency only if a direct value of HASHARRAY is given. Note: Interlisp hash arrays and Common Lisp hash tables are the same data type, so functions from both may be intermixed. The only difference between the functions may be argument order, as in MAPHASH and CL:MAPHASH (see below). (HASHARRAY(HASHARRAY (Function) NIL NIL (NIL) 1) MINKEYS OVERFLOW HASHBITSFN EQUIVFN RECLAIMABLE REHASH-THRESHOLD) [Function] Creates a hash array with space for at least MINKEYS hash keys, with overflow method OVERFLOW. See discussion of overflow behavior below. If HASHBITSFN and EQUIVFN are non-NIL, they specify the hashing function and comparison function used to interpret hash keys. This is described in the section on user-specified hashing functions below. If HASHBITSFN and EQUIVFN are NIL, the default is to hash EQ hash keys to the same value. If RECLAIMABLE is T the entries in the hash table will be removed if the key has a reference count of one and the table is about to be rehashed. This allows the system, in some cases, to reuse keys instead of expanding the table. Note: CL:MAKE-HASH-TABLE does not allow you to specify your own hashing functions but does provide three built-in types specified by Common Lisp, the Language. (HARRAY(HARRAY (Function) NIL NIL (NIL) 1) MINKEYS) [Function] Provided for backward compatibility, this is equivalent to (HASHARRAY MINKEYS 'ERROR), i.e. if the resulting hasarray gets full, an error occurs. (HARRAYP(HARRAYP (Function) NIL NIL (NIL) 2) X) [Function] Returns X if it is a hash array; otherwise NIL. HARRAYP returns NIL if X is a list whose CAR is an HARRAYP, even though this is accepted by the hash array functions (see below). (PUTHASH(PUTHASH (Function) NIL NIL (NIL) 2) KEY VAL HARRAY) [Function] Associates the hash value VAL with the hash key KEY in HARRAY. Replaces the previous hash value, if any. If VAL is NIL, any old association is removed (hence a hash value of NIL is not allowed). If HARRAY is full when PUTHASH is called with a key not already in the hash array, the function HASHOVERFLOW is called, and the PUTHASH is applied to the value returned (see below). Returns VAL. ((GETHASH (Function) NIL NIL (NIL) 2)GETHASH KEY HARRAY) [Function] Returns the hash value associated with the hash key KEY in HARRAY. Returns NIL, if KEY is not found. (CLRHASH(CLRHASH (Function) NIL NIL (NIL) 2) HARRAY) [Function] Clears all hash keys/values from HARRAY. Returns HARRAY. (HARRAYPROP(HARRAYPROP (Function) NIL NIL (NIL) 2) HARRAY PROP NEWVALUE) [NoSpread Function] Returns the property PROP of HARRAY; PROP can have the system-defined values SIZE (the maximum occupancy of HARRAY), NUMKEYS (number of occupied slots), OVERFLOW (overflow method), HASHBITSFN (hashing function) and EQUIVFN (comparison function). Except for SIZE and NUMKEYS, a new value may be specified as NEWVALUE. By using other values for PROP, the user may also set and get arbitrary property values, to associate additional information with a hash array. The HASHBITSFN or EQUIVFN properties can only be changed if the hash array is empty. (HARRAYSIZE(HARRAYSIZE (Function) NIL NIL (NIL) 2) HARRAY) [Function] Returns the number of slots in HARRAY. It's equivalent to (HARRAYPROP HARRAY 'SIZE). (REHASH(REHASH (Function) NIL NIL (NIL) 2) OLDHARRAY NEWHARRAY) [Function] Hashes all hash keys and values in OLDHARRAY into NEWHARRAY. The two hash arrays do not have to be (and usually aren't) the same size. Returns NEWHARRAY. (MAPHASH(MAPHASH (Function) NIL NIL (NIL) 2) HARRAY MAPHFN) [Function] MAPHFN is a function of two arguments. For each hash key in HARRAY, MAPHFN will be applied to the hash value, and the hash key. For example: [MAPHASH A (FUNCTION (LAMBDA (VAL KEY) (if (LISTP KEY) then (PRINT VAL)] will print the hash value for all hash keys that are lists. MAPHASH returns HARRAY. Note: the argument order for CL:MAPHASH is MAPHFN HARRAY. (DMPHASH(DMPHASH (Function) NIL NIL (NIL) 3) HARRAY1 HARRAY2 ... HARRAYN) [NLambda NoSpread Function] Prints on the primary output file LOADable forms which will restore the hash-arrays contained as the values of the atoms HARRAY1, HARRAY2, ... HARRAYN. Example: (DMPHASH SYSHASHARRAY) will dump the system hash-array. All EQ identities except symbols and small integers are lost by dumping and loading because READ will create new structure for each item. Thus if two lists contain an EQ substructure, when they are dumped and loaded back in, the corresponding substructures while EQUAL are no longer EQ. The HORRIBLEVARS file package command (Chapter 17) provides a way of dumping hash tables such that these identities are preserved. Hash Overflow 1 When a hash array(HASH% ARRAY NIL Hash% array NIL (NIL) 3 SUBNAME OVERFLOW SUBTEXT overflow) becomes full, trying to add another hash key will cause the function HASHOVERFLOW to be called. This either enlarges the hash array, or causes the error Hash table full. How hash overflow is handled is determined by the value of the OVERFLOW property of the hash array (which can be accessed by HARRAYPROP). The possibilities for the overflow method are: the symbol ERROR The error Hash array full is generated when the hash array overflows. This is the default overflow behavior for hash arrays returned by HARRAY. NIL The array is automatically enlarged by at least a factor 1.5 every time it overflows. This is the default overflow behavior for hash arrays returned by HASHARRAY. a positive integer N The array is enlarged to include at least N more slots than it currently has. a floating point number F The array is changed to include F times the number of current slots. a function or lambda expression FN Upon hash overflow, FN is called with the hash array as its argument. If FN returns a number, that will become the size of the array. Otherwise, the new size defaults to 1.5 times its previous size. FN could be used to print a message, or perform some monitor function. Note: For backwards compatibility, the hash array functions accept a list whose CAR is the hash array, and whose CDR is the overflow method. In this case, the overflow method specified in the list overrides the overflow method set in the hash array. Hash array functions perform with maximum efficiency only if a direct value of HASHARRAY is given. Specifying Your Own Hashing Functions 1 In general terms, when a key is looked up in a hash array(HASH% ARRAY NIL Hash% array NIL (NIL) 3 SUBNAME USER-SPECIFIED% FUNCTIONS SUBTEXT user-specified% functions), it is converted to an integer, which is used to index into a linear array. If the key is not the same as the one found at that index, other indices are tried until it the desired key is found. The value stored with that key is then returned (from GETHASH) or replaced (from PUTHASH). To customize hash arrays, you'll need to supply the hashing function used to convert a key to an integer and the comparison function used to compare the key found in the array with the key being looked up. For hash arrays to work correctly, any two objects which are equal according to the comparison function must hash to equal integers. By default, Medley uses a hashing function that computes an integer from the internal address of a key, and use EQ for comparing keys. This means that if non-atoms are used as hash keys, the exact same object (not a copy) must be used to retrieve the hash value. There are some applications for which the EQ constraint is too restrictive. For example, it may be useful to use strings as hash keys, without the restriction that EQUAL but not EQ strings are considered to be different hash keys. The user can override this default behavior for any hash array by specifying the functions used to compare keys and to hash a key to a number. This can be done by giving the HASHBITSFN and EQUIVFN arguments to HASHARRAY (see above). The EQUIVFN argument is a function of two arguments that returns non-NIL when its arguments are considered equal. The HASHBITSFN argument is a function of one argument that produces a positive small integer (in the range [0..216 - 1]) with the property that objects that are considered equal by the EQUIVFN produce the same hash bits. For an existing hash array, the function HARRAYPROP (see above) can be used to examine the hashing and equivalence functions as the HASHBITSFN and EQUIVFN hash array properties. These properties are read-only for non-empty hash arrays, as it makes no sense to change the equivalence relationship once some keys have been hashed. The following function is useful for creating hash arrays that take strings as hash keys: (STRINGHASHBITS(STRINGHASHBITS (Function) NIL NIL (NIL) 4) STRING) [Function] Hashes the string STRING into an integer that can be used as a HASHBITSFN for a hash array. Strings which are STREQUAL hash to the same integer. Example: (HASHARRAY MINKEYS OVERFLOW 'STRINGHASHBITS 'STREQUAL) creates a hash array where you can use strings as hash keys. [This page intentionally left blank] (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "6-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "6-" "")) (192 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "6-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "6-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (102 612 510 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "6-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "6-" "")) (222 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (48 612 564 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))5,,ZZ1EVENT-T@ PAGEHEADING RIGHTPAGE,xx,xx0 -T30<T,A PAGEHEADING RIGHTPAGET,HH,@ PAGEHEADINGLEFTBACKT +INTERLISP-D REFERENCE MANUAL +HASHARRAYS +6. HASHARRAYS +3 + +Hash arrays(HASH% ARRAYS NIL Hash% arrays NIL (NIL) 1) let you associate arbitrary Lisp objects (hash keys) with other objects (hash values), so you can get from key to value quickly. There are functions for creating hash arrays, putting a hash key/value pair in a hash array, and quickly retrieving the hash value associated with a given hash key. +By default, the hash array functions use EQ for comparing hash keys. This means that if non-symbols are used as hash keys, the exact same object (not a copy) must be used to retrieve the hash value. However, you can specify the function used to compare hash keys and to hash a hash key to a number. You can, for example, create hash arrays where EQUAL but non-EQ strings will hash to the same value. Specifying alternative hashing algorithms is described below. +In the description of the functions below, the argument HARRAY should be a hasharray created by HASHARRAY. For convenience in interactive program development, it may also be NIL, in which case a hash array (SYSHASHARRAY) provided by the system is used; you must watch out for confusions if this form is used to associate more than one kind of value with the same key. +Note: For backwards compatibility, the hash array functions will accept a list whose CAR is a hash array, and whose CDR is the overflow method for the hash array (see below). However, hash array functions are guaranteed to perform with maximum efficiency only if a direct value of HASHARRAY is given. +Note: Interlisp hash arrays and Common Lisp hash tables are the same data type, so functions from both may be intermixed. The only difference between the functions may be argument order, as in MAPHASH and CL:MAPHASH (see below). +(HASHARRAY(HASHARRAY (Function) NIL NIL (NIL) 1) MINKEYS OVERFLOW HASHBITSFN EQUIVFN RECLAIMABLE REHASH-THRESHOLD) [Function] +Creates a hash array with space for at least MINKEYS hash keys, with overflow method OVERFLOW. See discussion of overflow behavior below. +If HASHBITSFN and EQUIVFN are non-NIL, they specify the hashing function and comparison function used to interpret hash keys. This is described in the section on user-specified hashing functions below. If HASHBITSFN and EQUIVFN are NIL, the default is to hash EQ hash keys to the same value. +If RECLAIMABLE is T the entries in the hash table will be removed if the key has a reference count of one and the table is about to be rehashed. This allows the system, in some cases, to reuse keys instead of expanding the table. +Note: CL:MAKE-HASH-TABLE does not allow you to specify your own hashing functions but does provide three built-in types specified by Common Lisp, the Language. +(HARRAY(HARRAY (Function) NIL NIL (NIL) 1) MINKEYS) [Function] +Provided for backward compatibility, this is equivalent to (HASHARRAY MINKEYS 'ERROR), i.e. if the resulting hasarray gets full, an error occurs. +(HARRAYP(HARRAYP (Function) NIL NIL (NIL) 2) X) [Function] +Returns X if it is a hash array; otherwise NIL. +HARRAYP returns NIL if X is a list whose CAR is an HARRAYP, even though this is accepted by the hash array functions (see below). +(PUTHASH(PUTHASH (Function) NIL NIL (NIL) 2) KEY VAL HARRAY) [Function] +Associates the hash value VAL with the hash key KEY in HARRAY. Replaces the previous hash value, if any. If VAL is NIL, any old association is removed (hence a hash value of NIL is not allowed). If HARRAY is full when PUTHASH is called with a key not already in the hash array, the function HASHOVERFLOW is called, and the PUTHASH is applied to the value returned (see below). Returns VAL. +((GETHASH (Function) NIL NIL (NIL) 2)GETHASH KEY HARRAY) [Function] +Returns the hash value associated with the hash key KEY in HARRAY. Returns NIL, if KEY is not found. +(CLRHASH(CLRHASH (Function) NIL NIL (NIL) 2) HARRAY) [Function] +Clears all hash keys/values from HARRAY. Returns HARRAY. +(HARRAYPROP(HARRAYPROP (Function) NIL NIL (NIL) 2) HARRAY PROP NEWVALUE) [NoSpread Function] +Returns the property PROP of HARRAY; PROP can have the system-defined values SIZE (the maximum occupancy of HARRAY), NUMKEYS (number of occupied slots), OVERFLOW (overflow method), HASHBITSFN (hashing function) and EQUIVFN (comparison function). Except for SIZE and NUMKEYS, a new value may be specified as NEWVALUE. +By using other values for PROP, the user may also set and get arbitrary property values, to associate additional information with a hash array. +The HASHBITSFN or EQUIVFN properties can only be changed if the hash array is empty. +(HARRAYSIZE(HARRAYSIZE (Function) NIL NIL (NIL) 2) HARRAY) [Function] +Returns the number of slots in HARRAY. It's equivalent to (HARRAYPROP HARRAY 'SIZE). +(REHASH(REHASH (Function) NIL NIL (NIL) 2) OLDHARRAY NEWHARRAY) [Function] +Hashes all hash keys and values in OLDHARRAY into NEWHARRAY. The two hash arrays do not have to be (and usually aren't) the same size. Returns NEWHARRAY. +(MAPHASH(MAPHASH (Function) NIL NIL (NIL) 2) HARRAY MAPHFN) [Function] +MAPHFN is a function of two arguments. For each hash key in HARRAY, MAPHFN will be applied to the hash value, and the hash key. For example: +[MAPHASH A + (FUNCTION (LAMBDA (VAL KEY) + (if (LISTP KEY) then (PRINT VAL)] +will print the hash value for all hash keys that are lists. MAPHASH returns HARRAY. +Note: the argument order for CL:MAPHASH is MAPHFN HARRAY. +(DMPHASH(DMPHASH (Function) NIL NIL (NIL) 3) HARRAY1 HARRAY2 ... HARRAYN) [NLambda NoSpread Function] +Prints on the primary output file LOADable forms which will restore the hash-arrays contained as the values of the atoms HARRAY1, HARRAY2, ... HARRAYN. Example: (DMPHASH SYSHASHARRAY) will dump the system hash-array. +All EQ identities except symbols and small integers are lost by dumping and loading because READ will create new structure for each item. Thus if two lists contain an EQ substructure, when they are dumped and loaded back in, the corresponding substructures while EQUAL are no longer EQ. The HORRIBLEVARS file package command (Chapter 17) provides a way of dumping hash tables such that these identities are preserved. +Hash Overflow +1 + +When a hash array(HASH% ARRAY NIL Hash% array NIL (NIL) 3 SUBNAME OVERFLOW SUBTEXT overflow) becomes full, trying to add another hash key will cause the function HASHOVERFLOW to be called. This either enlarges the hash array, or causes the error Hash table full. How hash overflow is handled is determined by the value of the OVERFLOW property of the hash array (which can be accessed by HARRAYPROP). The possibilities for the overflow method are: + the symbol ERROR The error Hash array full is generated when the hash array overflows. This is the default overflow behavior for hash arrays returned by HARRAY. + NIL The array is automatically enlarged by at least a factor 1.5 every time it overflows. This is the default overflow behavior for hash arrays returned by HASHARRAY. + a positive integer N The array is enlarged to include at least N more slots than it currently has. + a floating point number F The array is changed to include F times the number of current slots. + a function or lambda expression FN Upon hash overflow, FN is called with the hash array as its argument. If FN returns a number, that will become the size of the array. Otherwise, the new size defaults to 1.5 times its previous size. FN could be used to print a message, or perform some monitor function. +Note: For backwards compatibility, the hash array functions accept a list whose CAR is the hash array, and whose CDR is the overflow method. In this case, the overflow method specified in the list overrides the overflow method set in the hash array. Hash array functions perform with maximum efficiency only if a direct value of HASHARRAY is given. +Specifying Your Own Hashing Functions +1 + +In general terms, when a key is looked up in a hash array(HASH% ARRAY NIL Hash% array NIL (NIL) 3 SUBNAME USER-SPECIFIED% FUNCTIONS SUBTEXT user-specified% functions), it is converted to an integer, which is used to index into a linear array. If the key is not the same as the one found at that index, other indices are tried until it the desired key is found. The value stored with that key is then returned (from GETHASH) or replaced (from PUTHASH). +To customize hash arrays, you'll need to supply the hashing function used to convert a key to an integer and the comparison function used to compare the key found in the array with the key being looked up. For hash arrays to work correctly, any two objects which are equal according to the comparison function must hash to equal integers. +By default, Medley uses a hashing function that computes an integer from the internal address of a key, and use EQ for comparing keys. This means that if non-atoms are used as hash keys, the exact same object (not a copy) must be used to retrieve the hash value. +There are some applications for which the EQ constraint is too restrictive. For example, it may be useful to use strings as hash keys, without the restriction that EQUAL but not EQ strings are considered to be different hash keys. +The user can override this default behavior for any hash array by specifying the functions used to compare keys and to hash a key to a number. This can be done by giving the HASHBITSFN and EQUIVFN arguments to HASHARRAY (see above). +The EQUIVFN argument is a function of two arguments that returns non-NIL when its arguments are considered equal. The HASHBITSFN argument is a function of one argument that produces a positive small integer (in the range [0..216 - 1]) with the property that objects that are considered equal by the EQUIVFN produce the same hash bits. +For an existing hash array, the function HARRAYPROP (see above) can be used to examine the hashing and equivalence functions as the HASHBITSFN and EQUIVFN hash array properties. These properties are read-only for non-empty hash arrays, as it makes no sense to change the equivalence relationship once some keys have been hashed. +The following function is useful for creating hash arrays that take strings as hash keys: +(STRINGHASHBITS(STRINGHASHBITS (Function) NIL NIL (NIL) 4) STRING) [Function] +Hashes the string STRING into an integer that can be used as a HASHBITSFN for a hash array. Strings which are STREQUAL hash to the same integer. +Example: +(HASHARRAY MINKEYS OVERFLOW 'STRINGHASHBITS 'STREQUAL) +creates a hash array where you can use strings as hash keys. + + +[This page intentionally left blank] +(SEQUENCE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "6-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "6-" "")) (192 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (ALTERNATE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "6-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "6-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (102 612 510 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "6-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "6-" "")) (222 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (48 612 564 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))))1$6$EVENT2$TE$ PAGEHEADING RIGHTPAGE1HH$1ZZ$50<$T1$1$2 +T71xx$1xx$1$F$ PAGEHEADING RIGHTPAGETE$ PAGEHEADINGLEFTBACKT/MODERN +(CHARPROPS (COLOR . BLACK)).GACHA +(CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK)).TITAN +(CHARPROPS (COLOR . BLACK))/MODERN +(CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK))1PALATINO(CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK))0CLASSIC +(CHARPROPS (COLOR . BLACK))0CLASSIC +(CHARPROPS (COLOR . BLACK))3 TIMESROMAN -PALATINO PALATINO PALATINOPALATINO TITAN TITAN TITAN TITAN TITAN -CLASSIC -CLASSIC -MODERN -MODERNMODERN - -   HRULE.GETFNMODERN +IM.INDEX.GETFN, ) 4  e 8" F   R      -  - - &IM.INDEX.GETFNMODERN -   - $ -  -!-  -   -      m - -#IM.INDEX.GETFNMODERN -  - ; ;  - -$IM.INDEX.GETFNMODERN -  - "      I - -$IM.INDEX.GETFNTITAN - -   - 1 8  B  8 - -$IM.INDEX.GETFN -  - 4   - -$IM.INDEX.GETFNTITAN - - - !  - - -'IM.INDEX.GETFNMODERN -    - $    -  $  " r  -  < - - -'IM.INDEX.GETFNTITAN - - -   - -#IM.INDEX.GETFNTITAN - -   - #  V  - -$IM.INDEX.GETFNTITAN - -  - 7D  )  =   -   - -$IM.INDEX.GETFNTITAN -   - - " S   "  V H ^   s  HRULE.GETFN KIM.INDEX.GETFNF I B 6 - 3   p   +#!$!4~E M    %  HRULE.GETFN 9mIM.INDEX.GETFN   Yp J7 * y  3  -  : / -bG ) - Q -   Z - -+IM.INDEX.GETFNMODERN -  - ' - &      =%)/ z \ No newline at end of file +(CHARPROPS (COLOR . BLACK))/MODERN(CHARPROPS (COLOR . BLACK)) + HRULE.GETFN +IM.INDEX.GETFN,)4 e8" F R    + &IM.INDEX.GETFN  + $ -!- +  +  m +#IM.INDEX.GETFN;;$IM.INDEX.GETFN" I$IM.INDEX.GETFN  18B 8$IM.INDEX.GETFN 4 $IM.INDEX.GETFN!  +'IM.INDEX.GETFN  $ +$"r +< +'IM.INDEX.GETFN#IM.INDEX.GETFN  #  V $IM.INDEX.GETFN 7D   )=   + $IM.INDEX.GETFN    + "S   "VH^ s +  HRULE.GETFNKIM.INDEX.GETFNF IB6 +3  p   +# !$ !4~E M   +% HRULE.GETFN9mIM.INDEX.GETFNY pJ +7*y 3 + :/ +b G) +Q +Z+IM.INDEX.GETFN' +&  =%(((CHARENCODING . MCCS)))PROPS:#DATE:jt)/ z \ No newline at end of file diff --git a/docs/medley-irm/07-NUMBERS.TEDIT b/docs/medley-irm/07-NUMBERS.TEDIT index 5ebcc055..2c4836e3 100644 --- a/docs/medley-irm/07-NUMBERS.TEDIT +++ b/docs/medley-irm/07-NUMBERS.TEDIT @@ -1,569 +1,321 @@ -INTERLISP-D REFERENCE MANUAL NUMBERS AND ARITHMETIC FUNCTIONS "7"7. NUMBERS AND ARITHMETIC FUNCTIONS 3 There are four different types of numbers(NUMBERS NIL Numbers NIL ("7") 1) in Interlisp: small integers(NUMBERS NIL Numbers NIL ("7") 1 SUBNAME SMALL% INTEGERS SUBTEXT small% integers), large integers(NUMBERS NIL Numbers NIL ("7") 1 SUBNAME LARGE% INTEGERS SUBTEXT large% integers), bignums (arbitrary-size integers)(NUMBERS NIL Numbers NIL ("7") 1 SUBNAME BIGNUMS SUBTEXT bignums), and floating-point numbers(NUMBERS NIL Numbers NIL ("7") 1 SUBNAME FLOATING-POINT SUBTEXT floating-point). Small integers are in the range -65536 to 65535. Large integers and floating-point numbers are 32-bit quantities that are stored by boxing the number (see below). Bignums are boxed as a series of words. Large integers and floating-point numbers can be any full word quantity. To distinguish among the various kinds of numbers, and other Interlisp pointers, these numbers are boxed When a large integer or floating-point number is created (by an arithmetic operation or by READ), Interlisp gets a new word from number storage and puts the number into that word. Interlisp then passes around the pointer to that word, i.e., the boxed number(BOXED% NUMBERS NIL Boxed% numbers NIL ("7") 1), rather than the actual quantity itself. When a numeric function needs the actual numeric quantity, it performs the extra level of addressing to obtain the value of the number. This latter process is called unboxing. Unboxing does not use any storage, but each boxing operation uses one new word of number storage. If a computation creates many large integers or floating-point numbers, i.e., does lots of boxes, it may cause a garbage collection of large integer space, or of floating-point number space. The following functions can be used to distinguish the different types of numbers: (SMALLP(SMALLP (Function) NIL NIL ("7") 1) X) [Function] Returns X, if X is a small integer; NIL otherwise. Does not generate an error if X is not a number. (FIXP(FIXP (Function) NIL NIL ("7") 1) X) [Function] Returns X, if X is an integer; NIL otherwise. Note that FIXP is true for small integers, large integers, and bignums. Does not generate an error if X is not a number. (FLOATP(FLOATP (Function) NIL NIL ("7") 1) X) [Function] Returns X if X is a floating-point number; NIL otherwise. Does not give an error if X is not a number. (NUMBERP(NUMBERP (Function) NIL NIL ("7") 1) X) [Function] Returns X, if X is a number of any type; NIL otherwise. Does not generate an error if X is not a number. Note: In previous releases, NUMBERP was true only if (FLOATP X) or (FIXP X) were true. With the additon of Common Lisp ratios and complex numbers, NUMBERP now returns T for all number types . Code relying on the "old" behavior should be modified. Each small integer has a unique representation, so EQ may be used to check equality. EQ should not be used for large integers, bignums, or floating-point numbers, EQP, IEQP, or EQUAL must be used instead. (EQP(EQP (Function) NIL NIL ("7") 1) X Y) [Function] Returns T, if X and Y are equal numbers; NIL otherwise. EQ may be used if X and Y are known to be small integers. EQP does not convert X and Y to integers, e.g., (EQP 2000 2000.3) => NIL, but it can be used to compare an integer and a floating-point number, e.g., (EQP 2000 2000.0) => T. EQP does not generate an error if X or Y are not numbers. EQP can also be used to compare stack pointers (see Chapter 11) and compiled code objects (see Chapter 10). The action taken on division by zero and floating-point overflow is determined with the following function: (OVERFLOW(OVERFLOW (Function) NIL NIL ("7") 2) FLG) [Function] Sets a flag that determines the system response to arithmetic overflow (for floating-point arithmetic) and division by zero; returns the previous setting. For integer arithmetic: If FLG = T, an error occurs on division by zero. If FLG = NIL or 0, integer division by zero returns zero. Integer overflow cannot occur, because small integers are converted to bignums (see the beginning of this chapter). For floating-point arithmetic: If FLG = T, an error occurs on floating overflow or floating division by zero. If FLG = NIL or 0, the largest (or smallest) floating-point number is returned as the result of the overflowed computation or floating division by zero. The default value for OVERFLOW is T, meaning an error is generated on division by zero or floating overflow. Generic Arithmetic(GENERIC% ARITHMETIC NIL Generic% arithmetic NIL ("7") 2)(ARITHMETIC NIL Arithmetic NIL ("7") 2 SUBNAME GENERIC SUBTEXT generic) 1 The functions in this section are generic arithmetic functions. If any of the arguments are floating-point numbers (see the Floating-Point Arithmetic section below), they act exactly like floating-point functions, floating all arguments and returning a floating-point number as their value. Otherwise, they act like the integer functions (see the Integer Arithmetic section below). If given a non-numeric argument, they generate an error, Non-numeric arg. The results of division by zero and floating-point overflow is determined by the function OVERFLOW (see the section above). (PLUS(PLUS (Function) NIL NIL ("7") 2) X1 X2 ... XN) [NoSpread Function] X1 + X2 + ... + XN. (MINUS(MINUS (Function) NIL NIL ("7") 2) X) [Function] - X (DIFFERENCE(DIFFERENCE (Function) NIL NIL ("7") 2) X Y) [Function] X - Y ((TIMES (Function) NIL NIL ("7") 2)TIMES X1 X2 ... XN) [NoSpread Function] X1 * X2 * ... * XN (QUOTIENT(QUOTIENT (Function) NIL NIL ("7") 2) X Y) [Function] If X and Y are both integers, returns the integer division of X and Y. Otherwise, converts both X and Y to floating-point numbers, and does a floating-point division. (REMAINDER(REMAINDER (Function) NIL NIL ("7") 3) X Y) [Function] If X and Y are both integers, returns (IREMAINDER X Y), otherwise (FREMAINDER X Y). (GREATERP (GREATERP% (Function) NIL NIL ("7") 3)X Y) [Function] T, if X > Y, NIL otherwise. (LESSP(LESSP (Function) NIL NIL ("7") 3) X Y) [Function] T if X < Y, NIL otherwise. (GEQ(GEQ (Function) NIL NIL ("7") 3) X Y) [Function] T, if X >= Y, NIL otherwise. (LEQ(LEQ (Function) NIL NIL ("7") 3) X Y) [Function] T, if X <= Y, NIL otherwise. (ZEROP(ZEROP (Function) NIL NIL ("7") 3) X) [Function] The same as (EQP X 0). (MINUSP(MINUSP (Function) NIL NIL ("7") 3) X) [Function] T, if X is negative; NIL otherwise. Works for both integers and floating-point numbers. (MIN(MIN (Function) NIL NIL ("7") 3) X1 X2 ... XN) [NoSpread Function] Returns the minimum of X1, X2, ..., XN. (MIN) returns the value of MAX.INTEGER (see the Integer Arithmetic section below). (MAX(MAX (Function) NIL NIL ("7") 3) X1 X2 ... XN) [NoSpread Function] Returns the maximum of X1, X2, ..., XN. (MAX) returns the value of MIN.INTEGER (see the Integer Arithmetic section below). (ABS(ABS (Function) NIL NIL ("7") 3) X) [Function] X if X > 0, otherwise -X. ABS uses GREATERP and MINUS (not IGREATERP and IMINUS). Integer Arithmetic(ARITHMETIC NIL Arithmetic NIL ("7") 3 SUBNAME INTEGER SUBTEXT integer)(INTEGER% ARITHMETIC NIL Integer% arithmetic NIL ("7") 3) 1 The input syntax for an integer is an optional sign (+ or -) followed by a sequence of decimal digits, and terminated by a delimiting character. Integers entered with this syntax are interpreted as decimal integers. Integers in other radices can be entered as follows: 123Q #o123 If an integer is followed by the letter Q, or preceeded by a pound sign and the letter o, the digits are interpreted as an octal (base 8) integer. #b10101 If an integer is preceeded by a pound sign and the letter b, the digits are interpreted as a binary (base 2) integer. #x1A90 If an integer is preceeded by a pound sign and the letter x, the digits are interpreted as a hexadecimal (base 16) integer. #5r1243 If an integer is preceeded by a pound sign, a positive decimal integer BASE, and the letter r, the digits are interpreted as an integer in the base BASE. For example, #8r123 = 123Q, and #16r12A3 = #x12A3. When typing a number in a radix above ten, the uppercase letters A through Z can be used as the digits after 9 (but there is no digit above Z, so it is not possible to type all base-99 digits). Medley keeps no record of how you typed a number, so 77Q and 63 both correspond to the same integer, and are indistinguishable internally. The function RADIX (see Chapter 25), sets the radix used to print integers. PACK and MKATOM create numbers when given a sequence of characters observing the above syntax, e.g. (PACK '(1 2 Q)) => 10. Integers are also created as a result of arithmetic operations. The range of integers of various types is implementation-dependent. This information is accessible to you through the following variables: MIN.SMALLP(MIN.SMALLP (Variable) NIL NIL ("7") 4) [Variable] MAX.SMALLP(MAX.SMALLP (Variable) NIL NIL ("7") 4) [Variable] The smallest/largest possible small integer. MIN.FIXP(MIN.FIXP (Variable) NIL NIL ("7") 4) [Variable] MAX.FIXP(MAX.FIXP (Variable) NIL NIL ("7") 4) [Variable] The smallest/largest possible large integer. MIN.INTEGER(MIN.INTEGER (Variable) NIL NIL ("7") 4) [Variable] MAX.INTEGER(MAX.INTEGER (Variable) NIL NIL ("7") 4) [Variable] The value of MAX.INTEGER and MIN.INTEGER are two special system datatypes. For some algorithms, it is useful to have an integer that is larger than any other integer. Therefore, the values of MAX.INTEGER and MIN.INTEGER are two special data types; the value of MAX.INTEGER is GREATERP than any other integer, and the value of MIN.INTEGER is LESSP than any other integer. Trying to do arithmetic using these special bignums, other than comparison, will cause an error. All of the functions described below work on integers. Unless specified otherwise, if given a floating-point number, they first convert the number to an integer by truncating the fractional bits, e.g., (IPLUS 2.3 3.8) = 5; if given a non-numeric argument, they generate an error, Non-numeric arg. (IPLUS X1 X2 ... XN) [NoSpread Function] Returns the sum X1 + X2 + ... + XN. (IPLUS) = 0. (IMINUS X) [Function] -X (IDIFFERENCE X Y) [Function] X - Y (ADD1(ADD1 (Function) NIL NIL ("7") 5) X) [Function] X + 1 (SUB1(SUB1 (Function) NIL NIL ("7") 5) X) [Function] X - 1 (ITIMES(ITIMES (Function) NIL NIL ("7") 5) X1 X2 ... XN) [NoSpread Function] Returns the product X1 * X2 * ... * XN. (ITIMES) = 1. (IQUOTIENT(IQUOTIENT (Function) NIL NIL ("7") 5) X Y) [Function] X / Y truncated. Examples: (IQUOTIENT 3 2) => 1 (IQUOTIENT -3 2) => -1 If Y is zero, the result is determined by the function OVERFLOW . (IREMAINDER(IREMAINDER (Function) NIL NIL ("7") 5) X Y) [Function] Returns the remainder when X is divided by Y. Example: (IREMAINDER 5 2) => 1 (IMOD(IMOD (Function) NIL NIL ("7") 5) X N) [Function] Computes the integer modulus of X mod N; this differs from IREMAINDER in that the result is always a non-negative integer in the range [0,N). (IGREATERP(IGREATERP (Function) NIL NIL ("7") 5) X Y) [Function] T, if X > Y; NIL otherwise. (ILESSP(ILESSP (Function) NIL NIL ("7") 5) X Y) [Function] T, if X < Y; NIL otherwise. (IGEQ(IGEQ (Function) NIL NIL ("7") 5) X Y) [Function] T, if X >= Y; NIL otherwise. (ILEQ(ILEQ (Function) NIL NIL ("7") 5) X Y) [Function] T, if X <= Y; NIL otherwise. (IMIN(IMIN (Function) NIL NIL ("7") 5) X1 X2 ... XN) [NoSpread Function] Returns the minimum of X1, X2, ..., XN. (IMIN) returns the largest possible large integer, the value of MAX.INTEGER. (IMAX(IMAX (Function) NIL NIL ("7") 5) X1 X2 ... XN) [NoSpread Function] Returns the maximum of X1, X2, ..., XN. (IMAX) returns the smallest possible large integer, the value of MIN.INTEGER. (IEQP(EQP (Function) NIL NIL ("7") 6) X Y) [Function] Returns T if X and Y are equal integers; NIL otherwise. Note that EQ may be used if X and Y are known to be small integers. IEQP converts X and Y to integers, e.g., (IEQP 2000 2000.3) => T. (FIX(FIX (Function) NIL NIL ("7") 6) N) [Function] If N is an integer, returns N. Otherwise, converts N to an integer by truncating fractional bits For example, (FIX 2.3) => 2, (FIX -1.7) => -1. Since FIX is also a programmer's assistant command (see Chapter 13), typing FIX directly to a Medley executive will not cause the function FIX to be called. (FIXR(FIXR (Function) NIL NIL ("7") 6) N) [Function] If N is an integer, returns N. Otherwise, converts N to an integer by rounding. FIXR will round towards the even number if N is exactly half way between two integers. For example, (FIXR 2.3) => 2, (FIXR -1.7) => -2, (FIXR 3.5) => 4). (GCD(GCD (Function) NIL NIL ("7") 6) N1 N2) [Function] Returns the greatest common divisor of N1 and N2, (GCD 72 64)=8. Logical Arithmetic(ARITHMETIC NIL Arithmetic NIL ("7") 6 SUBNAME LOGICAL% FUNCTIONS SUBTEXT logical% functions) Functions(LOGICAL% ARITHMETIC% FUNCTIONS NIL Logical% arithmetic% functions NIL ("7") 6) 1 (LOGAND(LOGAND (Function) NIL NIL ("7") 6) X1 X2 ... XN) [NoSpread Function] Returns the logical AND of all its arguments, as an integer. Example: (LOGAND 7 5 6) => 4 (LOGOR(LOGOR (Function) NIL NIL ("7") 6) X1 X2 ... XN) [NoSpread Function] Returns the logical OR of all its arguments, as an integer. Example: (LOGOR 1 3 9) => 11 (LOGXOR(LOGXOR (Function) NIL NIL ("7") 6) X1 X2 ... XN) [NoSpread Function] Returns the logical exclusive OR of its arguments, as an integer. Example: (LOGXOR 11 5) => 14 (LOGXOR 11 5 9) = (LOGXOR 14 9) => 7 (LSH(LSH (Function) NIL NIL ("7") 6) X N) [Function] (Arithmetic) Left Shift. Returns X shifted left N places, with the sign bit unaffected. X can be positive or negative. If N is negative, X is shifted right -N places. (RSH(RSH (Function) NIL NIL ("7") 6) X N) [Function] (Arithmetic) Right Shift. Returns X shifted right N places, with the sign bit unaffected, and copies of the sign bit shifted into the leftmost bit. X can be positive or negative. If N is negative, X is shifted left -N places. Warning: Be careful if using RSH to simulate division; RSHing a negative number isn't the same as dividing by a power of two. (LLSH(LLSH (Function) NIL NIL ("7") 7) X N) [Function] (LRSH(LLSH (Function) NIL NIL ("7") 7) X N) [Function] Logical Left Shift and Logical Right Shift. The difference between a logical and arithmetic right shift lies in the treatment of the sign bit. Logical shifting treats it just like any other bit; arithmetic shifting will not change it, and will propagate rightward when actually shifting rightwards. Note that shifting (arithmetic) a negative number all the way to the right yields -1, not 0. Note: LLSH and LRSH always operate mod-232 arithmetic. Passing a bignum to either of these will cause an error. LRSH of negative numbers will shift 0s into the high bits. (INTEGERLENGTH(LLSH (Function) NIL NIL ("7") 7) X) [Function] Returns the number of bits needed to represent X. This is equivalent to: 1+floor[log2[abs[X]]]. (INTEGERLENGTH 0) = 0. (POWEROFTWOP(POWEROFTWOP (Function) NIL NIL ("7") 7) X) [Function] Returns non-NIL if X (coerced to an integer) is a power of two. (EVENP(EVENP (Function) NIL NIL ("7") 7) X Y) [NoSpread Function] If Y is not given, equivalent to (ZEROP (IMOD X 2)); otherwise equivalent to (ZEROP (IMOD X Y)). (ODDP(ODDP (Function) NIL NIL ("7") 7) N MODULUS) [NoSpread Function] Equivalent to (NOT (EVENP N MODULUS)). MODULUS defaults to 2. (LOGNOT (LOGNOT% (Macro) NIL NIL ("7") 7)N) [Macro] Logical negation of the bits in N. Equivalent to (LOGXOR N -1). (BITTEST(BITTEST (Macro) NIL NIL ("7") 7) N MASK) [Macro] Returns T if any of the bits in MASK are on in the number N. Equivalent to (NOT (ZEROP (LOGAND N MASK))). (BITCLEAR(BITCLEAR (Macro) NIL NIL ("7") 7) N MASK) [Macro] Turns off bits from MASK in N. Equivalent to (LOGAND N (LOGNOT MASK)). (BITSET(BITSET (Macro) NIL NIL ("7") 7) N MASK) [Macro] Turns on the bits from MASK in N. Equivalent to (LOGOR N MASK). (MASK.1'S(MASK.1'S (Macro) NIL NIL ("7") 7) POSITION SIZE) [Macro] Returns a bit-mask with SIZE one-bits starting with the bit at POSITION. Equivalent to (LLSH (SUB1 (EXPT 2 SIZE)) POSITION). (MASK.0'S(MASK.0'S (Macro) NIL NIL ("7") 8) POSITION SIZE) [Macro] Returns a bit-mask with all one bits, except for SIZE bits starting at POSITION. Equivalent to (LOGNOT (MASK.1'S POSITION SIZE)). (LOADBYTE(LOADBYTE (Function) NIL NIL ("7") 8) N POS SIZE) [Function] Extracts SIZE bits from N, starting at position POS. Equivalent to (LOGAND (RSH N POS) (MASK.1'S 0 SIZE)). (DEPOSITBYTE(DEPOSITBYTE (Function) NIL NIL ("7") 8) N POS SIZE VAL) [Function] Insert SIZE bits of VAL at position POS into N, returning the result. Equivalent to (LOGOR (BITCLEAR N (MASK.1'S POS SIZE)) (LSH (LOGAND VAL (MASK.1'S 0 SIZE)) POS)) (ROT(ROT (Function) NIL NIL ("7") 8) X N FIELDSIZE) [Function] Rotate bits in field. It performs a bitwise left-rotation of the integer X, by N places, within a field of FIELDSIZE bits wide. Bits being shifted out of the position selected by (EXPT 2 (SUB1 FIELDSIZE)) will flow into the units position. The notions of position and size can be combined to make up a byte specifier, which is constructed by the macro BYTE [note reversal of arguments as compared with the above functions]: (BYTE(BYTE (Macro) NIL NIL ("7") 8) SIZE POSITION) [Macro] Constructs and returns a byte specifier containing SIZE and POSITION. (BYTESIZE(BYTESIZE (Macro) NIL NIL ("7") 8) BYTESPEC) [Macro] Returns the SIZE componant of the byte specifier BYTESPEC. (BYTEPOSITION(BYTEPOSITION (Macro) NIL NIL ("7") 8) BYTESPEC) [Macro] Returns the POSITION componant of the byte specifier BYTESPEC. (LDB(LDB (Macro) NIL NIL ("7") 8) BYTESPEC VAL) [Macro] Equivalent to (LOADBYTE VAL (BYTEPOSITION BYTESPEC)(BYTESIZE BYTESPEC)) (DPB(DPB (Macro) NIL NIL ("7") 8) N BYTESPEC VAL) [Macro] Equivalent to (DEPOSITBYTE VAL (BYTEPOSITION BYTESPEC)(BYTESIZE BYTESPEC) N) Floating-Point Arithmetic(ARITHMETIC NIL Arithmetic NIL ("7") 8 SUBNAME FLOATING% POINT SUBTEXT floating% point)(FLOATING% POINT% ARITHMETIC NIL Floating% point% arithmetic NIL ("7") 8) 1 A floating-point number is input as a signed integer, followed by a decimal point, and another sequence of digits called the fraction, followed by an exponent (represented by E followed by a signed integer) and terminated by a delimiter. Both signs are optional, and either the fraction following the decimal point, or the integer preceding the decimal point may be omitted. One or the other of the decimal point or exponent may also be omitted, but at least one of them must be present to distinguish a floating-point number from an integer. For example, the following will be recognized as floating-point numbers: 5. 5.00 5.01 .3 5E2 5.1E2 5E-3 -5.2E+6 Floating-point numbers are printed using the format control specified by the function FLTFMT (see Chapter 25). FLTFMT is initialized to T, or free format. For example, the above floating-point numbers would be printed free format as: 5.0 5.0 5.01 .3 500.0 510.0 .005 -5.2E6 Floating-point numbers are created by the reader when a . or an E appears in a number, e.g., 1000 is an integer, 1000. a floating-point number, as are 1E3 and 1.E3. Note that 1000D, 1000F, and 1E3D are perfectly legal literal atoms. Floating-point numbers are also created by PACK and MKATOM, and as a result of arithmetic operations. PRINTNUM (see Chapter 25) permits greater control over the printed appearance of floating-point numbers, allowing such things as left-justification, suppression of trailing decimals, etc. The floating-point number range is stored in the following variables: MIN.FLOAT(MIN.FLOAT (Variable) NIL NIL ("7") 9) [Variable] The smallest possible floating-point number. MAX.FLOAT(MAX.FLOAT (Variable) NIL NIL ("7") 9) [Variable] The largest possible floating-point number. All of the functions described below work on floating-point numbers. Unless specified otherwise, if given an integer, they first convert the number to a floating-point number, e.g., (FPLUS 1 2.3) <=> (FPLUS 1.0 2.3) => 3.3; if given a non-numeric argument, they generate an error, Non-numeric arg. (FPLUS(FPLUS (Function) NIL NIL ("7") 9) X1 X2 ... XN) [NoSpread Function] X1 + X2 + ... + XN (FMINUS(FMINUS (Function) NIL NIL ("7") 9) X) [Function] - X (FDIFFERENCE(FDIFFERENCE (Function) NIL NIL ("7") 9) X Y) [Function] X - Y (FTIMES(FTIMES (Function) NIL NIL ("7") 9) X1 X2 ... XN) [NoSpread Function] X1 * X2 * ... * XN (FQUOTIENT(FQUOTIENT (Function) NIL NIL ("7") 10) X Y) [Function] X / Y. The results of division by zero and floating-point overflow is determined by the function OVERFLOW. (FREMAINDER(FREMAINDER (Function) NIL NIL ("7") 10) X Y) [Function] Returns the remainder when X is divided by Y. Equivalent to: (FDIFFERENCE X (FTIMES Y (FIX (FQUOTIENT X Y)))) Example: (FREMAINDER 7.5 2.3) => 0.6 (FGREATERP(FGREATERP (Function) NIL NIL ("7") 10) X Y) [Function] T, if X > Y, NIL otherwise. (FLESSP(FLESSP (Function) NIL NIL ("7") 10) X Y) [Function] T, if X < Y, NIL otherwise. (FEQP(FEQP (Function) NIL NIL ("7") 10) X Y) [Function] Returns T if X and Y are equal floating-point numbers; NIL otherwise. FEQP converts X and Y to floating-point numbers. (FMIN(FMIN (Function) NIL NIL ("7") 10) X1 X2 ... XN) [NoSpread Function] Returns the minimum of X1, X2, ..., XN. (FMIN) returns the largest possible floating-point number, the value of MAX.FLOAT. (FMAX(FMAX (Function) NIL NIL ("7") 10) X1 X2 ... XN) [NoSpread Function] Returns the maximum of X1, X2, ..., XN. (FMAX) returns the smallest possible floating-point number, the value of MIN.FLOAT. (FLOAT(FLOAT (Function) NIL NIL ("7") 10) X) [Function] Converts X to a floating-point number. Example: (FLOAT 0) => 0.0 Transcendental Arithmetic Functions 1 (EXPT(EXPT (Function) NIL NIL ("7") 10) A N) [Function] Returns AN. If A is an integer and N is a positive integer, returns an integer, e.g, (EXPT 3 4) => 81, otherwise returns a floating-point number. If A is negative and N fractional, generates the error, Illegal exponentiation. If N is floating and either too large or too small, generates the error, Value out of range expt. (SQRT(SQRT (Function) NIL NIL ("7") 11) N) [Function] Returns the square root of N as a floating-point number. N may be fixed or floating-point. Generates an error if N is negative. (LOG(LOG (Function) NIL NIL ("7") 11) X) [Function] Returns the natural logarithm of X as a floating-point number. X can be integer or floating-point. (ANTILOG(ANTILOG (Function) NIL NIL ("7") 11) X) [Function] Returns the floating-point number whose logarithm is X. X can be integer or floating-point. Example: (ANTILOG 1) = e => 2.71828... (SIN(SIN (Function) NIL NIL ("7") 11) X RADIANSFLG) [Function] Returns the sine of X as a floating-point number. X is in degrees unless RADIANSFLG = T. (COS(COS (Function) NIL NIL ("7") 11) X RADIANSFLG) [Function] Similar to SIN. ((TAN (Function) NIL NIL ("7") 11)TAN X RADIANSFLG) [Function] Similar to SIN. (ARCSIN (ARCSIN% (Function) NIL NIL ("7") 11)X RADIANSFLG) [Function] The value of ARCSIN is a floating-point number, and is in degrees unless RADIANSFLG = T. In other words, if (ARCSIN X RADIANSFLG) = Z then (SIN Z RADIANSFLG) = X. The range of the value of ARCSIN is -90 to +90 for degrees, -&s/2 to &s/2 for radians. X must be a number between -1 and 1. (ARCCOS(ARCCOS (Function) NIL NIL ("7") 11) X RADIANSFLG) [Function] Similar to ARCSIN. Range is 0 to 180, 0 to &s. (ARCTAN(ARCTAN (Function) NIL NIL ("7") 11) X RADIANSFLG) [Function] Similar to ARCSIN. Range is 0 to 180, 0 to &s. (ARCTAN2(ARCTAN2 (Function) NIL NIL ("7") 11) Y X RADIANSFLG) [Function] Computes (ARCTAN (FQUOTIENT Y X) RADIANSFLG), and returns a corresponding value in the range -180 to 180 (or -&s to &s), i.e. the result is in the proper quadrant as determined by the signs of X and Y. Generating Random Numbers 1 (RAND(RAND (Function) NIL NIL ("7") 11) LOWER UPPER) [Function] Returns a pseudo-random number between LOWER and UPPER inclusive, i.e., RAND can be used to generate a sequence of random numbers. If both limits are integers, the value of RAND is an integer, otherwise it is a floating-point number. The algorithm is completely deterministic, i.e., given the same initial state, RAND produces the same sequence of values. The internal state of RAND is initialized using the function RANDSET. (RANDSET(RANDSET (Function) NIL NIL ("7") 12) X) [Function] Returns the internal state of RAND. If X = NIL, just returns the current state. If X = T, RAND is initialized using the clocks, and RANDSET returns the new state. Otherwise, X is interpreted as a previous internal state, i.e., a value of RANDSET, and is used to reset RAND. For example, (SETQ OLDSTATE (RANDSET)) ... (for X from 1 to 10 do (PRIN1 (RAND 1 10))) 2847592748NIL (RANDSET OLDSTATE) ... (for X from 1 to 10 do (PRIN1 (RAND 1 10))) 2847592748NIL [This page intentionally left blank] (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "7-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "7-" "")) (222 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "7-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "7-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "7-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "7-" "")) (222 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (48 612 564 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))))),l~,rr30`T30`T,HH1EVENT-T@ PAGEHEADING RIGHTPAGE0 -T,l~,ll,l~60``T5HBH5HBH5HBH,/A PAGEHEADING RIGHTPAGET,@ PAGEHEADINGLEFTBACKT,HHTITANTITAN +INTERLISP-D REFERENCE MANUAL +NUMBERS AND ARITHMETIC FUNCTIONS +"7"7. NUMBERS AND ARITHMETIC FUNCTIONS +3 + + +There are four different types of numbers(NUMBERS NIL Numbers NIL ("7") 1) in Interlisp: small integers(NUMBERS NIL Numbers NIL ("7") 1 SUBNAME SMALL% INTEGERS SUBTEXT small% integers), large integers(NUMBERS NIL Numbers NIL ("7") 1 SUBNAME LARGE% INTEGERS SUBTEXT large% integers), bignums (arbitrary-size integers)(NUMBERS NIL Numbers NIL ("7") 1 SUBNAME BIGNUMS SUBTEXT bignums), and floating-point numbers(NUMBERS NIL Numbers NIL ("7") 1 SUBNAME FLOATING-POINT SUBTEXT floating-point). Small integers are in the range -65536 to 65535. Large integers and floating-point numbers are 32-bit quantities that are stored by boxing the number (see below). Bignums are boxed as a series of words. +Large integers and floating-point numbers can be any full word quantity. To distinguish among the various kinds of numbers, and other Interlisp pointers, these numbers are boxed When a large integer or floating-point number is created (by an arithmetic operation or by READ), Interlisp gets a new word from number storage and puts the number into that word. Interlisp then passes around the pointer to that word, i.e., the boxed number(BOXED% NUMBERS NIL Boxed% numbers NIL ("7") 1), rather than the actual quantity itself. When a numeric function needs the actual numeric quantity, it performs the extra level of addressing to obtain the value of the number. This latter process is called unboxing. Unboxing does not use any storage, but each boxing operation uses one new word of number storage. If a computation creates many large integers or floating-point numbers, i.e., does lots of boxes, it may cause a garbage collection of large integer space, or of floating-point number space. +The following functions can be used to distinguish the different types of numbers: +(SMALLP(SMALLP (Function) NIL NIL ("7") 1) X) [Function] +Returns X, if X is a small integer; NIL otherwise. Does not generate an error if X is not a number. +(FIXP(FIXP (Function) NIL NIL ("7") 1) X) [Function] +Returns X, if X is an integer; NIL otherwise. Note that FIXP is true for small integers, large integers, and bignums. Does not generate an error if X is not a number. +(FLOATP(FLOATP (Function) NIL NIL ("7") 1) X) [Function] +Returns X if X is a floating-point number; NIL otherwise. Does not give an error if X is not a number. +(NUMBERP(NUMBERP (Function) NIL NIL ("7") 1) X) [Function] +Returns X, if X is a number of any type; NIL otherwise. Does not generate an error if X is not a number. +Note: In previous releases, NUMBERP was true only if (FLOATP X) or (FIXP X) were true. With the additon of Common Lisp ratios and complex numbers, NUMBERP now returns T for all number types . Code relying on the "old" behavior should be modified. +Each small integer has a unique representation, so EQ may be used to check equality. EQ should not be used for large integers, bignums, or floating-point numbers, EQP, IEQP, or EQUAL must be used instead. +(EQP(EQP (Function) NIL NIL ("7") 1) X Y) [Function] +Returns T, if X and Y are equal numbers; NIL otherwise. EQ may be used if X and Y are known to be small integers. EQP does not convert X and Y to integers, e.g., (EQP 2000 2000.3) => NIL, but it can be used to compare an integer and a floating-point number, e.g., (EQP 2000 2000.0) => T. EQP does not generate an error if X or Y are not numbers. +EQP can also be used to compare stack pointers (see Chapter 11) and compiled code objects (see Chapter 10). +The action taken on division by zero and floating-point overflow is determined with the following function: +(OVERFLOW(OVERFLOW (Function) NIL NIL ("7") 2) FLG) [Function] +Sets a flag that determines the system response to arithmetic overflow (for floating-point arithmetic) and division by zero; returns the previous setting. +For integer arithmetic: If FLG = T, an error occurs on division by zero. If FLG = NIL or 0, integer division by zero returns zero. Integer overflow cannot occur, because small integers are converted to bignums (see the beginning of this chapter). +For floating-point arithmetic: If FLG = T, an error occurs on floating overflow or floating division by zero. If FLG = NIL or 0, the largest (or smallest) floating-point number is returned as the result of the overflowed computation or floating division by zero. +The default value for OVERFLOW is T, meaning an error is generated on division by zero or floating overflow. +Generic Arithmetic(GENERIC% ARITHMETIC NIL Generic% arithmetic NIL ("7") 2)(ARITHMETIC NIL Arithmetic NIL ("7") 2 SUBNAME GENERIC SUBTEXT generic) +1 + +The functions in this section are generic arithmetic functions. If any of the arguments are floating-point numbers (see the Floating-Point Arithmetic section below), they act exactly like floating-point functions, floating all arguments and returning a floating-point number as their value. Otherwise, they act like the integer functions (see the Integer Arithmetic section below). If given a non-numeric argument, they generate an error, Non-numeric arg. The results of division by zero and floating-point overflow is determined by the function OVERFLOW (see the section above). +(PLUS(PLUS (Function) NIL NIL ("7") 2) X1 X2 ... XN) [NoSpread Function] +X1 + X2 + ... + XN. +(MINUS(MINUS (Function) NIL NIL ("7") 2) X) [Function] +- X +(DIFFERENCE(DIFFERENCE (Function) NIL NIL ("7") 2) X Y) [Function] +X - Y +((TIMES (Function) NIL NIL ("7") 2)TIMES X1 X2 ... XN) [NoSpread Function] +X1 * X2 * ... * XN +(QUOTIENT(QUOTIENT (Function) NIL NIL ("7") 2) X Y) [Function] +If X and Y are both integers, returns the integer division of X and Y. Otherwise, converts both X and Y to floating-point numbers, and does a floating-point division. +(REMAINDER(REMAINDER (Function) NIL NIL ("7") 3) X Y) [Function] +If X and Y are both integers, returns (IREMAINDER X Y), otherwise (FREMAINDER X Y). +(GREATERP (GREATERP% (Function) NIL NIL ("7") 3)X Y) [Function] +T, if X > Y, NIL otherwise. +(LESSP(LESSP (Function) NIL NIL ("7") 3) X Y) [Function] +T if X < Y, NIL otherwise. +(GEQ(GEQ (Function) NIL NIL ("7") 3) X Y) [Function] +T, if X >= Y, NIL otherwise. +(LEQ(LEQ (Function) NIL NIL ("7") 3) X Y) [Function] +T, if X <= Y, NIL otherwise. +(ZEROP(ZEROP (Function) NIL NIL ("7") 3) X) [Function] +The same as (EQP X 0). +(MINUSP(MINUSP (Function) NIL NIL ("7") 3) X) [Function] +T, if X is negative; NIL otherwise. Works for both integers and floating-point numbers. +(MIN(MIN (Function) NIL NIL ("7") 3) X1 X2 ... XN) [NoSpread Function] +Returns the minimum of X1, X2, ..., XN. (MIN) returns the value of MAX.INTEGER (see the Integer Arithmetic section below). +(MAX(MAX (Function) NIL NIL ("7") 3) X1 X2 ... XN) [NoSpread Function] +Returns the maximum of X1, X2, ..., XN. (MAX) returns the value of MIN.INTEGER (see the Integer Arithmetic section below). +(ABS(ABS (Function) NIL NIL ("7") 3) X) [Function] +X if X > 0, otherwise -X. ABS uses GREATERP and MINUS (not IGREATERP and IMINUS). +Integer Arithmetic(ARITHMETIC NIL Arithmetic NIL ("7") 3 SUBNAME INTEGER SUBTEXT integer)(INTEGER% ARITHMETIC NIL Integer% arithmetic NIL ("7") 3) +1 + +The input syntax for an integer is an optional sign (+ or -) followed by a sequence of decimal digits, and terminated by a delimiting character. Integers entered with this syntax are interpreted as decimal integers. Integers in other radices can be entered as follows: + 123Q + #o123 If an integer is followed by the letter Q, or preceeded by a pound sign and the letter o, the digits are interpreted as an octal (base 8) integer. + #b10101 If an integer is preceeded by a pound sign and the letter b, the digits are interpreted as a binary (base 2) integer. + #x1A90 If an integer is preceeded by a pound sign and the letter x, the digits are interpreted as a hexadecimal (base 16) integer. + #5r1243 If an integer is preceeded by a pound sign, a positive decimal integer BASE, and the letter r, the digits are interpreted as an integer in the base BASE. For example, #8r123 = 123Q, and #16r12A3 = #x12A3. When typing a number in a radix above ten, the uppercase letters A through Z can be used as the digits after 9 (but there is no digit above Z, so it is not possible to type all base-99 digits). +Medley keeps no record of how you typed a number, so 77Q and 63 both correspond to the same integer, and are indistinguishable internally. The function RADIX (see Chapter 25), sets the radix used to print integers. +PACK and MKATOM create numbers when given a sequence of characters observing the above syntax, e.g. (PACK '(1 2 Q)) => 10. Integers are also created as a result of arithmetic operations. +The range of integers of various types is implementation-dependent. This information is accessible to you through the following variables: +MIN.SMALLP(MIN.SMALLP (Variable) NIL NIL ("7") 4) [Variable] +MAX.SMALLP(MAX.SMALLP (Variable) NIL NIL ("7") 4) [Variable] +The smallest/largest possible small integer. +MIN.FIXP(MIN.FIXP (Variable) NIL NIL ("7") 4) [Variable] +MAX.FIXP(MAX.FIXP (Variable) NIL NIL ("7") 4) [Variable] +The smallest/largest possible large integer. +MIN.INTEGER(MIN.INTEGER (Variable) NIL NIL ("7") 4) [Variable] +MAX.INTEGER(MAX.INTEGER (Variable) NIL NIL ("7") 4) [Variable] +The value of MAX.INTEGER and MIN.INTEGER are two special system datatypes. For some algorithms, it is useful to have an integer that is larger than any other integer. Therefore, the values of MAX.INTEGER and MIN.INTEGER are two special data types; the value of MAX.INTEGER is GREATERP than any other integer, and the value of MIN.INTEGER is LESSP than any other integer. Trying to do arithmetic using these special bignums, other than comparison, will cause an error. +All of the functions described below work on integers. Unless specified otherwise, if given a floating-point number, they first convert the number to an integer by truncating the fractional bits, e.g., (IPLUS 2.3 3.8) = 5; if given a non-numeric argument, they generate an error, Non-numeric arg. +(IPLUS X1 X2 ... XN) [NoSpread Function] +Returns the sum X1 + X2 + ... + XN. (IPLUS) = 0. +(IMINUS X) [Function] +-X +(IDIFFERENCE X Y) [Function] +X - Y +(ADD1(ADD1 (Function) NIL NIL ("7") 5) X) [Function] +X + 1 +(SUB1(SUB1 (Function) NIL NIL ("7") 5) X) [Function] +X - 1 +(ITIMES(ITIMES (Function) NIL NIL ("7") 5) X1 X2 ... XN) [NoSpread Function] +Returns the product X1 * X2 * ... * XN. (ITIMES) = 1. +(IQUOTIENT(IQUOTIENT (Function) NIL NIL ("7") 5) X Y) [Function] +X / Y truncated. Examples: +(IQUOTIENT 3 2) => 1 +(IQUOTIENT -3 2) => -1 +If Y is zero, the result is determined by the function OVERFLOW . +(IREMAINDER(IREMAINDER (Function) NIL NIL ("7") 5) X Y) [Function] +Returns the remainder when X is divided by Y. Example: +(IREMAINDER 5 2) => 1 +(IMOD(IMOD (Function) NIL NIL ("7") 5) X N) [Function] +Computes the integer modulus of X mod N; this differs from IREMAINDER in that the result is always a non-negative integer in the range [0,N). +(IGREATERP(IGREATERP (Function) NIL NIL ("7") 5) X Y) [Function] +T, if X > Y; NIL otherwise. +(ILESSP(ILESSP (Function) NIL NIL ("7") 5) X Y) [Function] +T, if X < Y; NIL otherwise. +(IGEQ(IGEQ (Function) NIL NIL ("7") 5) X Y) [Function] +T, if X >= Y; NIL otherwise. +(ILEQ(ILEQ (Function) NIL NIL ("7") 5) X Y) [Function] +T, if X <= Y; NIL otherwise. +(IMIN(IMIN (Function) NIL NIL ("7") 5) X1 X2 ... XN) [NoSpread Function] +Returns the minimum of X1, X2, ..., XN. (IMIN) returns the largest possible large integer, the value of MAX.INTEGER. +(IMAX(IMAX (Function) NIL NIL ("7") 5) X1 X2 ... XN) [NoSpread Function] +Returns the maximum of X1, X2, ..., XN. (IMAX) returns the smallest possible large integer, the value of MIN.INTEGER. +(IEQP(EQP (Function) NIL NIL ("7") 6) X Y) [Function] +Returns T if X and Y are equal integers; NIL otherwise. Note that EQ may be used if X and Y are known to be small integers. IEQP converts X and Y to integers, e.g., (IEQP 2000 2000.3) => T. +(FIX(FIX (Function) NIL NIL ("7") 6) N) [Function] +If N is an integer, returns N. Otherwise, converts N to an integer by truncating fractional bits For example, (FIX 2.3) => 2, (FIX -1.7) => -1. +Since FIX is also a programmer's assistant command (see Chapter 13), typing FIX directly to a Medley executive will not cause the function FIX to be called. +(FIXR(FIXR (Function) NIL NIL ("7") 6) N) [Function] +If N is an integer, returns N. Otherwise, converts N to an integer by rounding. FIXR will round towards the even number if N is exactly half way between two integers. For example, (FIXR 2.3) => 2, (FIXR -1.7) => -2, (FIXR 3.5) => 4). +(GCD(GCD (Function) NIL NIL ("7") 6) N1 N2) [Function] +Returns the greatest common divisor of N1 and N2, (GCD 72 64)=8. +Logical Arithmetic(ARITHMETIC NIL Arithmetic NIL ("7") 6 SUBNAME LOGICAL% FUNCTIONS SUBTEXT logical% functions) Functions(LOGICAL% ARITHMETIC% FUNCTIONS NIL Logical% arithmetic% functions NIL ("7") 6) +1 + +(LOGAND(LOGAND (Function) NIL NIL ("7") 6) X1 X2 ... XN) [NoSpread Function] +Returns the logical AND of all its arguments, as an integer. Example: +(LOGAND 7 5 6) => 4 +(LOGOR(LOGOR (Function) NIL NIL ("7") 6) X1 X2 ... XN) [NoSpread Function] +Returns the logical OR of all its arguments, as an integer. Example: +(LOGOR 1 3 9) => 11 +(LOGXOR(LOGXOR (Function) NIL NIL ("7") 6) X1 X2 ... XN) [NoSpread Function] +Returns the logical exclusive OR of its arguments, as an integer. Example: +(LOGXOR 11 5) => 14 +(LOGXOR 11 5 9) = (LOGXOR 14 9) => 7 +(LSH(LSH (Function) NIL NIL ("7") 6) X N) [Function] +(Arithmetic) Left Shift. Returns X shifted left N places, with the sign bit unaffected. X can be positive or negative. If N is negative, X is shifted right -N places. +(RSH(RSH (Function) NIL NIL ("7") 6) X N) [Function] +(Arithmetic) Right Shift. Returns X shifted right N places, with the sign bit unaffected, and copies of the sign bit shifted into the leftmost bit. X can be positive or negative. If N is negative, X is shifted left -N places. +Warning: Be careful if using RSH to simulate division; RSHing a negative number isn't the same as dividing by a power of two. +(LLSH(LLSH (Function) NIL NIL ("7") 7) X N) [Function] +(LRSH(LLSH (Function) NIL NIL ("7") 7) X N) [Function] +Logical Left Shift and Logical Right Shift. The difference between a logical and arithmetic right shift lies in the treatment of the sign bit. Logical shifting treats it just like any other bit; arithmetic shifting will not change it, and will propagate rightward when actually shifting rightwards. Note that shifting (arithmetic) a negative number all the way to the right yields -1, not 0. +Note: LLSH and LRSH always operate mod-232 arithmetic. Passing a bignum to either of these will cause an error. LRSH of negative numbers will shift 0s into the high bits. +(INTEGERLENGTH(LLSH (Function) NIL NIL ("7") 7) X) [Function] +Returns the number of bits needed to represent X. This is equivalent to: 1+floor[log2[abs[X]]]. (INTEGERLENGTH 0) = 0. +(POWEROFTWOP(POWEROFTWOP (Function) NIL NIL ("7") 7) X) [Function] +Returns non-NIL if X (coerced to an integer) is a power of two. +(EVENP(EVENP (Function) NIL NIL ("7") 7) X Y) [NoSpread Function] +If Y is not given, equivalent to (ZEROP (IMOD X 2)); otherwise equivalent to (ZEROP (IMOD X Y)). +(ODDP(ODDP (Function) NIL NIL ("7") 7) N MODULUS) [NoSpread Function] +Equivalent to (NOT (EVENP N MODULUS)). MODULUS defaults to 2. +(LOGNOT (LOGNOT% (Macro) NIL NIL ("7") 7)N) [Macro] +Logical negation of the bits in N. Equivalent to (LOGXOR N -1). +(BITTEST(BITTEST (Macro) NIL NIL ("7") 7) N MASK) [Macro] +Returns T if any of the bits in MASK are on in the number N. Equivalent to (NOT (ZEROP (LOGAND N MASK))). +(BITCLEAR(BITCLEAR (Macro) NIL NIL ("7") 7) N MASK) [Macro] +Turns off bits from MASK in N. Equivalent to (LOGAND N (LOGNOT MASK)). +(BITSET(BITSET (Macro) NIL NIL ("7") 7) N MASK) [Macro] +Turns on the bits from MASK in N. Equivalent to (LOGOR N MASK). +(MASK.1'S(MASK.1'S (Macro) NIL NIL ("7") 7) POSITION SIZE) [Macro] +Returns a bit-mask with SIZE one-bits starting with the bit at POSITION. Equivalent to (LLSH (SUB1 (EXPT 2 SIZE)) POSITION). +(MASK.0'S(MASK.0'S (Macro) NIL NIL ("7") 8) POSITION SIZE) [Macro] +Returns a bit-mask with all one bits, except for SIZE bits starting at POSITION. Equivalent to (LOGNOT (MASK.1'S POSITION SIZE)). +(LOADBYTE(LOADBYTE (Function) NIL NIL ("7") 8) N POS SIZE) [Function] +Extracts SIZE bits from N, starting at position POS. Equivalent to (LOGAND (RSH N POS) (MASK.1'S 0 SIZE)). +(DEPOSITBYTE(DEPOSITBYTE (Function) NIL NIL ("7") 8) N POS SIZE VAL) [Function] +Insert SIZE bits of VAL at position POS into N, returning the result. Equivalent to +(LOGOR (BITCLEAR N (MASK.1'S POS SIZE)) + (LSH (LOGAND VAL (MASK.1'S 0 SIZE)) + POS)) +(ROT(ROT (Function) NIL NIL ("7") 8) X N FIELDSIZE) [Function] +Rotate bits in field. It performs a bitwise left-rotation of the integer X, by N places, within a field of FIELDSIZE bits wide. Bits being shifted out of the position selected by (EXPT 2 (SUB1 FIELDSIZE)) will flow into the units position. +The notions of position and size can be combined to make up a byte specifier, which is constructed by the macro BYTE [note reversal of arguments as compared with the above functions]: +(BYTE(BYTE (Macro) NIL NIL ("7") 8) SIZE POSITION) [Macro] +Constructs and returns a byte specifier containing SIZE and POSITION. +(BYTESIZE(BYTESIZE (Macro) NIL NIL ("7") 8) BYTESPEC) [Macro] +Returns the SIZE componant of the byte specifier BYTESPEC. +(BYTEPOSITION(BYTEPOSITION (Macro) NIL NIL ("7") 8) BYTESPEC) [Macro] +Returns the POSITION componant of the byte specifier BYTESPEC. +(LDB(LDB (Macro) NIL NIL ("7") 8) BYTESPEC VAL) [Macro] +Equivalent to +(LOADBYTE VAL (BYTEPOSITION BYTESPEC)(BYTESIZE BYTESPEC)) +(DPB(DPB (Macro) NIL NIL ("7") 8) N BYTESPEC VAL) [Macro] +Equivalent to +(DEPOSITBYTE VAL (BYTEPOSITION BYTESPEC)(BYTESIZE BYTESPEC) N) +Floating-Point Arithmetic(ARITHMETIC NIL Arithmetic NIL ("7") 8 SUBNAME FLOATING% POINT SUBTEXT floating% point)(FLOATING% POINT% ARITHMETIC NIL Floating% point% arithmetic NIL ("7") 8) +1 + +A floating-point number is input as a signed integer, followed by a decimal point, and another sequence of digits called the fraction, followed by an exponent (represented by E followed by a signed integer) and terminated by a delimiter. +Both signs are optional, and either the fraction following the decimal point, or the integer preceding the decimal point may be omitted. One or the other of the decimal point or exponent may also be omitted, but at least one of them must be present to distinguish a floating-point number from an integer. For example, the following will be recognized as floating-point numbers: +5. 5.00 5.01 .3 +5E2 5.1E2 5E-3 -5.2E+6 +Floating-point numbers are printed using the format control specified by the function FLTFMT (see Chapter 25). FLTFMT is initialized to T, or free format. For example, the above floating-point numbers would be printed free format as: +5.0 5.0 5.01 .3 +500.0 510.0 .005 -5.2E6 +Floating-point numbers are created by the reader when a . or an E appears in a number, e.g., 1000 is an integer, 1000. a floating-point number, as are 1E3 and 1.E3. Note that 1000D, 1000F, and 1E3D are perfectly legal literal atoms. Floating-point numbers are also created by PACK and MKATOM, and as a result of arithmetic operations. +PRINTNUM (see Chapter 25) permits greater control over the printed appearance of floating-point numbers, allowing such things as left-justification, suppression of trailing decimals, etc. +The floating-point number range is stored in the following variables: +MIN.FLOAT(MIN.FLOAT (Variable) NIL NIL ("7") 9) [Variable] +The smallest possible floating-point number. +MAX.FLOAT(MAX.FLOAT (Variable) NIL NIL ("7") 9) [Variable] +The largest possible floating-point number. +All of the functions described below work on floating-point numbers. Unless specified otherwise, if given an integer, they first convert the number to a floating-point number, e.g., (FPLUS 1 2.3) <=> (FPLUS 1.0 2.3) => 3.3; if given a non-numeric argument, they generate an error, Non-numeric arg. +(FPLUS(FPLUS (Function) NIL NIL ("7") 9) X1 X2 ... XN) [NoSpread Function] +X1 + X2 + ... + XN +(FMINUS(FMINUS (Function) NIL NIL ("7") 9) X) [Function] +- X +(FDIFFERENCE(FDIFFERENCE (Function) NIL NIL ("7") 9) X Y) [Function] +X - Y +(FTIMES(FTIMES (Function) NIL NIL ("7") 9) X1 X2 ... XN) [NoSpread Function] +X1 * X2 * ... * XN +(FQUOTIENT(FQUOTIENT (Function) NIL NIL ("7") 10) X Y) [Function] +X / Y. +The results of division by zero and floating-point overflow is determined by the function OVERFLOW. +(FREMAINDER(FREMAINDER (Function) NIL NIL ("7") 10) X Y) [Function] +Returns the remainder when X is divided by Y. Equivalent to: +(FDIFFERENCE X (FTIMES Y (FIX (FQUOTIENT X Y)))) +Example: +(FREMAINDER 7.5 2.3) => 0.6 +(FGREATERP(FGREATERP (Function) NIL NIL ("7") 10) X Y) [Function] +T, if X > Y, NIL otherwise. +(FLESSP(FLESSP (Function) NIL NIL ("7") 10) X Y) [Function] +T, if X < Y, NIL otherwise. +(FEQP(FEQP (Function) NIL NIL ("7") 10) X Y) [Function] +Returns T if X and Y are equal floating-point numbers; NIL otherwise. FEQP converts X and Y to floating-point numbers. +(FMIN(FMIN (Function) NIL NIL ("7") 10) X1 X2 ... XN) [NoSpread Function] +Returns the minimum of X1, X2, ..., XN. (FMIN) returns the largest possible floating-point number, the value of MAX.FLOAT. +(FMAX(FMAX (Function) NIL NIL ("7") 10) X1 X2 ... XN) [NoSpread Function] +Returns the maximum of X1, X2, ..., XN. (FMAX) returns the smallest possible floating-point number, the value of MIN.FLOAT. +(FLOAT(FLOAT (Function) NIL NIL ("7") 10) X) [Function] +Converts X to a floating-point number. Example: +(FLOAT 0) => 0.0 +Transcendental Arithmetic Functions +1 + +(EXPT(EXPT (Function) NIL NIL ("7") 10) A N) [Function] +Returns AN. If A is an integer and N is a positive integer, returns an integer, e.g, (EXPT 3 4) => 81, otherwise returns a floating-point number. If A is negative and N fractional, generates the error, Illegal exponentiation. If N is floating and either too large or too small, generates the error, Value out of range expt. +(SQRT(SQRT (Function) NIL NIL ("7") 11) N) [Function] +Returns the square root of N as a floating-point number. N may be fixed or floating-point. Generates an error if N is negative. +(LOG(LOG (Function) NIL NIL ("7") 11) X) [Function] +Returns the natural logarithm of X as a floating-point number. X can be integer or floating-point. +(ANTILOG(ANTILOG (Function) NIL NIL ("7") 11) X) [Function] +Returns the floating-point number whose logarithm is X. X can be integer or floating-point. Example: +(ANTILOG 1) = e => 2.71828... +(SIN(SIN (Function) NIL NIL ("7") 11) X RADIANSFLG) [Function] +Returns the sine of X as a floating-point number. X is in degrees unless RADIANSFLG = T. +(COS(COS (Function) NIL NIL ("7") 11) X RADIANSFLG) [Function] +Similar to SIN. +((TAN (Function) NIL NIL ("7") 11)TAN X RADIANSFLG) [Function] +Similar to SIN. +(ARCSIN (ARCSIN% (Function) NIL NIL ("7") 11)X RADIANSFLG) [Function] +The value of ARCSIN is a floating-point number, and is in degrees unless RADIANSFLG = T. In other words, if (ARCSIN X RADIANSFLG) = Z then (SIN Z RADIANSFLG) = X. The range of the value of ARCSIN is -90 to +90 for degrees, -&s/2 to &s/2 for radians. X must be a number between -1 and 1. +(ARCCOS(ARCCOS (Function) NIL NIL ("7") 11) X RADIANSFLG) [Function] +Similar to ARCSIN. Range is 0 to 180, 0 to &s. +(ARCTAN(ARCTAN (Function) NIL NIL ("7") 11) X RADIANSFLG) [Function] +Similar to ARCSIN. Range is 0 to 180, 0 to &s. +(ARCTAN2(ARCTAN2 (Function) NIL NIL ("7") 11) Y X RADIANSFLG) [Function] +Computes (ARCTAN (FQUOTIENT Y X) RADIANSFLG), and returns a corresponding value in the range -180 to 180 (or -&s to &s), i.e. the result is in the proper quadrant as determined by the signs of X and Y. +Generating Random Numbers +1 + +(RAND(RAND (Function) NIL NIL ("7") 11) LOWER UPPER) [Function] +Returns a pseudo-random number between LOWER and UPPER inclusive, i.e., RAND can be used to generate a sequence of random numbers. If both limits are integers, the value of RAND is an integer, otherwise it is a floating-point number. The algorithm is completely deterministic, i.e., given the same initial state, RAND produces the same sequence of values. The internal state of RAND is initialized using the function RANDSET. +(RANDSET(RANDSET (Function) NIL NIL ("7") 12) X) [Function] +Returns the internal state of RAND. If X = NIL, just returns the current state. If X = T, RAND is initialized using the clocks, and RANDSET returns the new state. Otherwise, X is interpreted as a previous internal state, i.e., a value of RANDSET, and is used to reset RAND. For example, +_(SETQ OLDSTATE (RANDSET)) +... +_(for X from 1 to 10 do (PRIN1 (RAND 1 10))) +2847592748NIL +_(RANDSET OLDSTATE) +... +_(for X from 1 to 10 do (PRIN1 (RAND 1 10))) +2847592748NIL + + +[This page intentionally left blank] +(SEQUENCE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "7-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "7-" "")) (222 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (ALTERNATE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "7-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "7-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "7-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "7-" "")) (222 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (48 612 564 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))))1$6$EVENT2$TE$ PAGEHEADING RIGHTPAGE1rr$1l~$1l~$1HH$50`$T2 +T1$1ll$1HH$1l~$50`$T80`$`T7HBH7HBH7HBH1$1$F$ PAGEHEADING RIGHTPAGETE$ PAGEHEADINGLEFTBACKT/MODERN +(CHARPROPS (COLOR . BLACK)).GACHA +(CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK))0CLASSIC +(CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK))0CLASSIC +(CHARPROPS (COLOR . BLACK))/MODERN +(CHARPROPS (COLOR . BLACK))2 HELVETICA (CHARPROPS (COLOR . BLACK)).TITAN(CHARPROPS (COLOR . BLACK)).TITAN(CHARPROPS (COLOR . BLACK))0CLASSIC +(CHARPROPS (COLOR . BLACK))1PALATINO(CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK))3 TIMESROMAN -PALATINO PALATINO PALATINOPALATINO  HELVETICA TITAN TITAN TITAN CLASSIC -CLASSIC -CLASSIC -MODERN -MODERNMODERN -! IM.CHAP.GETFNMODERN -% HRULE.GETFNMODERN)!IM.INDEX.GETFNQIM.INDEX.GETFNQIM.INDEX.GETFN#AIM.INDEX.GETFNOIM.INDEX.GETFN -/IM.INDEX.GETFNS - #IM.INDEX.GETFNTITAN   -   -+  - !IM.INDEX.GETFNTITAN   -    - -Y  - #IM.INDEX.GETFNTITAN   -   -'  - $IM.INDEX.GETFNTITAN   -   -+  - -  - J -  -I3 -! -L - - - - IM.INDEX.GETFNTITAN     - -   -  -  " -   -N - -   -il - %IM.INDEX.GETFNTITAN   -  -+  - -#  -I  - - - -J 9IM.INDEX.GETFNGIM.INDEX.GETFN HRULE.GETFN -] - - !IM.INDEX.GETFNTITAN  -    -  -  -  - "IM.INDEX.GETFNTITAN  -  - -  - - 'IM.INDEX.GETFNTITAN  -  -   -"IM.INDEX.GETFN  -    -  -  -  -  - %IM.INDEX.GETFNTITAN  -  -  4    @ - &IM.INDEX.GETFNTITAN     -   -  -  -  -     - - 'IM.INDEX.GETFN    - -  -  -  - "IM.INDEX.GETFNMODERN -    - -  -  -  - IM.INDEX.GETFNMODERN -    - -  -  -  - IM.INDEX.GETFNCLASSIC -    - -  -  -  - "IM.INDEX.GETFNMODERN -  -  -    - - #IM.INDEX.GETFNMODERN -  - -  -? - IM.INDEX.GETFNMODERN -     -   -   -  - -- - IM.INDEX.GETFNMODERN -     -   -    - -- - IM.INDEX.GETFNCLASSIC -  -    -  -  - - - - - GIM.INDEX.GETFN9IM.INDEX.GETFN HRULE.GETFN - -) -/ -< -< -< -< -B -H - -8 - - -5 - -Z -: - -U -C - 'IM.INDEX.GETFNTITAN  - 'IM.INDEX.GETFNTITAN - %IM.INDEX.GETFNTITAN  %IM.INDEX.GETFNTITAN - (IM.INDEX.GETFNTITAN  (IM.INDEX.GETFNTITAN   - - - -* - -* - -z -; - -      -   -  -  -  - -    - -  -      -  -  - !IM.INDEX.GETFNTITAN    -   - - !IM.INDEX.GETFNTITAN    -   - - #IM.INDEX.GETFNTITAN       -   -  -  -  - - &IM.INDEX.GETFNTITAN     -  -   - - - 3 - - - 'IM.INDEX.GETFNTITAN     -    - - !IM.INDEX.GETFNTITAN     -    - -E  - &IM.INDEX.GETFNTITAN     - -  -  -  - #IM.INDEX.GETFNTITAN     - -  -  -  - !IM.INDEX.GETFNCLASSIC -    - -  -  -  - !IM.INDEX.GETFNTITAN     - -  -  -  - !IM.INDEX.GETFNTITAN      -   -   -  -: - - !IM.INDEX.GETFNTITAN      -   -   -  -; - - IM.INDEX.GETFNTITAN     - -   - -  " - -   - - - IM.INDEX.GETFNTITAN   -    ; - - -C -< - - !IM.INDEX.GETFNTITAN   -     -' 9 - - - - IM.INDEX.GETFNTITAN     -'   - ]IM.INDEX.GETFN -OIM.INDEX.GETFN HRULE.GETFN - #IM.INDEX.GETFNTITAN      -   - -0  - - "IM.INDEX.GETFNTITAN      -   - -0  - - #IM.INDEX.GETFNTITAN      -   - -,  - -' - - IM.INDEX.GETFNTITAN     -$  ( "   -  - IM.INDEX.GETFNTITAN     -%  b "    - -D - !IM.INDEX.GETFNTITAN     - - !IM.INDEX.GETFNTITAN     - - - - -G -7 - !IM.INDEX.GETFNTITAN   -/  -  - - - - (IM.INDEX.GETFNTITAN   -  - , - "IM.INDEX.GETFNTITAN     -  -  - - - !IM.INDEX.GETFNTITAN     - -  - "IM.INDEX.GETFN  -    -  - - !IM.INDEX.GETFNTITAN     -  -   -   - - "IM.INDEX.GETFNTITAN     -    -  -  - - IM.INDEX.GETFNTITAN     -    -  -  - - "IM.INDEX.GETFNTITAN     -  #  -  -  - - "IM.INDEX.GETFNTITAN     - 1   -   - - %IM.INDEX.GETFNTITAN       -     -    -   - - (IM.INDEX.GETFNTITAN         -    '  - -  -  - - - -  - -  - - IM.INDEX.GETFNTITAN      -K   @ -  -&r -D - IM.INDEX.GETFNTITAN     - 5   - "IM.INDEX.GETFNTITAN   -   -#  - &IM.INDEX.GETFNTITAN   -   -#  - IM.INDEX.GETFNTITAN     -  -  - - -  - - IM.INDEX.GETFNTITAN       -  -  - - -  -  - WIM.INDEX.GETFNIIM.INDEX.GETFN HRULE.GETFN ->|  - -V - - -b  -  -8 - - - -! - -  - - -P - -, -F &IM.INDEX.GETFNTITAN - &IM.INDEX.GETFNTITAN ,) -; - - "IM.INDEX.GETFNTITAN      -   -  -  -  - #IM.INDEX.GETFNTITAN   -  -  - (IM.INDEX.GETFNTITAN     -  -  - #IM.INDEX.GETFNTITAN      -   -  -  -  - 'IM.INDEX.GETFNTITAN     -  - Z - - - (IM.INDEX.GETFNTITAN     -   - -  -  -  -   - - 'IM.INDEX.GETFNTITAN     - -  -  -  - $IM.INDEX.GETFNTITAN     - -  -  -  - "IM.INDEX.GETFNTITAN     - -  # -  - -   - "IM.INDEX.GETFNTITAN      -   -   -  -B - - "IM.INDEX.GETFNTITAN      -   -   -  -C - - #IM.INDEX.GETFNTITAN   -  '  - # HRULE.GETFN - "IM.INDEX.GETFNTITAN     -   1 -1  " - E - - "IM.INDEX.GETFNTITAN   -   8  - !IM.INDEX.GETFNTITAN   - !  # - %IM.INDEX.GETFNTITAN   -5  -  - - !IM.INDEX.GETFNMODERN -  -  -   -  - - !IM.INDEX.GETFNMODERN -  -  -  - -!IM.INDEX.GETFN   -  -  - - &IM.INDEX.GETFN  -  -  -6 -  - -  - - -  -  - - - - & - $IM.INDEX.GETFNTITAN   -  -  -  - - - - $IM.INDEX.GETFNTITAN   -  -  -  - - - - %IM.INDEX.GETFNTITAN     -  -  -   - -  -1 - - - - - -M    HRULE.GETFN - "IM.INDEX.GETFNTITAN      -'   -b - -> -# - - %IM.INDEX.GETFNTITAN   - -  -&  - -& -$ ? - -  - -- - - - -- - -%b cz \ No newline at end of file +(CHARPROPS (COLOR . BLACK))/MODERN(CHARPROPS (COLOR . BLACK))! IM.CHAP.GETFN%  HRULE.GETFN)!IM.INDEX.GETFNQIM.INDEX.GETFNQIM.INDEX.GETFN#AIM.INDEX.GETFNOIM.INDEX.GETFN /IM.INDEX.GETFN S #IM.INDEX.GETFN+ !IM.INDEX.GETFN Y #IM.INDEX.GETFN' $IM.INDEX.GETFN+J I 3!L  IM.INDEX.GETFN "Ni l %IM.INDEX.GETFN+#IJ + 9IM.INDEX.GETFNGIM.INDEX.GETFN HRULE.GETFN ] !IM.INDEX.GETFN       "IM.INDEX.GETFN  +'IM.INDEX.GETFN "IM.INDEX.GETFN       %IM.INDEX.GETFN4@  &IM.INDEX.GETFN       'IM.INDEX.GETFN  "IM.INDEX.GETFN   IM.INDEX.GETFN    IM.INDEX.GETFN  "IM.INDEX.GETFN    #IM.INDEX.GETFN?  IM.INDEX.GETFN       -  IM.INDEX.GETFN       -  IM.INDEX.GETFN    + GIM.INDEX.GETFN9IM.INDEX.GETFN HRULE.GETFN )/<<<<BH8  5Z: UC  +'IM.INDEX.GETFN +'IM.INDEX.GETFN- %IM.INDEX.GETFN %IM.INDEX.GETFN - (IM.INDEX.GETFN (IM.INDEX.GETFN     * * z ;            !IM.INDEX.GETFN  !IM.INDEX.GETFN  #IM.INDEX.GETFN         &IM.INDEX.GETFN3  +'IM.INDEX.GETFN  !IM.INDEX.GETFN  +E  &IM.INDEX.GETFN  #IM.INDEX.GETFN  !IM.INDEX.GETFN  !IM.INDEX.GETFN  !IM.INDEX.GETFN      :  !IM.INDEX.GETFN      ;   IM.INDEX.GETFN" +  IM.INDEX.GETFN ;C< !IM.INDEX.GETFN '9  IM.INDEX.GETFN  '    + ]IM.INDEX.GETFN + OIM.INDEX.GETFN HRULE.GETFN #IM.INDEX.GETFN   0 "IM.INDEX.GETFN   0 #IM.INDEX.GETFN   ,'  IM.INDEX.GETFN$("   IM.INDEX.GETFN%b" D !IM.INDEX.GETFN!IM.INDEX.GETFNG7  !IM.INDEX.GETFN/  (IM.INDEX.GETFN , "IM.INDEX.GETFN  !IM.INDEX.GETFN "IM.INDEX.GETFN   !IM.INDEX.GETFN  "IM.INDEX.GETFN   IM.INDEX.GETFN  "IM.INDEX.GETFN # "IM.INDEX.GETFN 1 %IM.INDEX.GETFN         (IM.INDEX.GETFN  '    IM.INDEX.GETFN K @ & rD IM.INDEX.GETFN 5 "IM.INDEX.GETFN  #  &IM.INDEX.GETFN  # IM.INDEX.GETFN   + IM.INDEX.GETFN   + + WIM.INDEX.GETFNIIM.INDEX.GETFN HRULE.GETFN > |   Vb   8! P,  F &IM.INDEX.GETFN- &IM.INDEX.GETFN, ); "IM.INDEX.GETFN       #IM.INDEX.GETFN   (IM.INDEX.GETFN #IM.INDEX.GETFN        'IM.INDEX.GETFNZ  +(IM.INDEX.GETFN     'IM.INDEX.GETFN  $IM.INDEX.GETFN  "IM.INDEX.GETFN#  + "IM.INDEX.GETFN      B  "IM.INDEX.GETFN      C  #IM.INDEX.GETFN ' +#  HRULE.GETFN "IM.INDEX.GETFN 11"E "IM.INDEX.GETFN 8 !IM.INDEX.GETFN !# %IM.INDEX.GETFN5- !IM.INDEX.GETFN + + !IM.INDEX.GETFN +  !IM.INDEX.GETFN +  &IM.INDEX.GETFN + 6 +  + +& $IM.INDEX.GETFN +   $IM.INDEX.GETFN +   %IM.INDEX.GETFN  +  +1M +  HRULE.GETFN  "IM.INDEX.GETFN'b># %IM.INDEX.GETFN&&$?--%(((CHARENCODING . MCCS)))PROPS:#DATE:jb az \ No newline at end of file diff --git a/docs/medley-irm/08-RECORDPACKAGE.TEDIT b/docs/medley-irm/08-RECORDPACKAGE.TEDIT index 0b1b2c32..f62ceb98 100644 Binary files a/docs/medley-irm/08-RECORDPACKAGE.TEDIT and b/docs/medley-irm/08-RECORDPACKAGE.TEDIT differ diff --git a/docs/medley-irm/09-conditionals.TEDIT b/docs/medley-irm/09-conditionals.TEDIT index c998b695..e99c948f 100644 --- a/docs/medley-irm/09-conditionals.TEDIT +++ b/docs/medley-irm/09-conditionals.TEDIT @@ -1,105 +1,527 @@ - INTERLISP-D REFERENCE MANUAL CONDITIONALS AND ITERATIVE STATEMENTS "9"9. LISTS AND ITERATIVE STATEMENTS 3 Medley gives you a large number of predicates, conditional functions, and control functions. Also, there is a complex iterative statement facility which allows you to easily create complex loops and iterative constructs. Data Type Predicates 1 Medley provides separate functions for testing whether objects are of certain commonly-used types: (LITATOM(LITATOM (Function) NIL NIL ("9") 1) X) [Function] Returns T if X is a symbol; NIL otherwise. Note that a number is not a symbol. (SMALLP(SMALLP (Function) NIL NIL ("9") 1) X) [Function] Returns X if X is a small integer; NIL otherwise. (The range of small integers is -65536 to +65535. (FIXP(FIXP (Function) NIL NIL ("9") 1) X) [Function] Returns X if X is a small or large integer; NIL otherwise. (FLOATP(FLOATP (Function) NIL NIL ("9") 1) X) [Function] Returns X if X is a floating point number; NIL otherwise. (NUMBERP(NUMBERP (Function) NIL NIL ("9") 1) X) [Function] Returns X if X is a number of any type, NIL otherwise. (ATOM(ATOM (Function) NIL NIL ("9") 1) X) [Function] Returns T if X is an atom (i.e. a symbol or a number); NIL otherwise. (ATOM X) is NIL if X is an array, string, etc. In Common Lisp, CL:ATOM is defined equivalent to the Interlisp function NLISTP. (LISTP(LISTP (Function) NIL NIL ("9") 1) X) [Function] Returns X if X is a list cell (something created by CONS); NIL otherwise. (NLISTP(NLISTP (Function) NIL NIL ("9") 1) X) [Function] (NOT (LISTP X)). Returns T if X is not a list cell, NIL otherwise. (STRINGP(STRINGP (Function) NIL NIL ("9") 1) X) [Function] Returns X if X is a string, NIL otherwise. (ARRAYP X) [Function] Returns X if X is an array, NIL otherwise. (HARRAYP X) [Function] Returns X if it is a hash array object; otherwise NIL. HARRAYP(HARRAYP (Function) NIL NIL ("9") 2) returns NIL if X is a list whose CAR is an HARRAYP, even though this is accepted by the hash array functions. Note: The empty list, () or NIL, is considered to be a symbol, rather than a list. Therefore, (LITATOM NIL) = (ATOM NIL) = T and (LISTP NIL) = NIL. Take care when using these functions if the object may be the empty list NIL. Equality Predicates 1 Sometimes, there is more than one type of equality. For instance, given two lists, you can ask whether they are exactly the same object, or whether they are two distinct lists that contain the same elements. Confusion between these two types of equality is often the source of program errors. (EQ(EQ (Function) NIL NIL ("9") 2) X Y) [Function] Returns T if X and Y are identical pointers; NIL otherwise. EQ should not be used to compare two numbers, unless they are small integers; use EQP instead. (NEQ(NEQ (Function) NIL NIL ("9") 2) X Y) [Function] The same as (NOT (EQ X Y)) (NULL(NULL (Function) NIL NIL ("9") 2) X) [Function] (NOT(NOT (Function) NIL NIL ("9") 2) X) [Function] The same as (EQ X NIL) (EQP(EQP (Function) NIL NIL ("9") 2) X Y) [Function] Returns T if X and Y are EQ, or if X and Y are numbers and are equal in value; NIL otherwise. For more discussion of EQP and other number functions, see Chapter 7. EQP also can be used to compare stack pointers (Section 11) and compiled code (Chapter 10). (EQUAL(EQUAL (Function) NIL NIL ("9") 2) X Y) [Function] EQUAL returns T if X and Y are one of the following: 1. EQ 2. EQP, i.e., numbers with equal value 3. STREQUAL, i.e., strings containing the same sequence of characters 4. Lists and CAR of X is EQUAL to CAR of Y, and CDR of X is EQUAL to CDR of Y EQUAL returns NIL otherwise. Note that EQUAL can be significantly slower than EQ. A loose description of EQUAL might be to say that X and Y are EQUAL if they print out the same way. (EQUALALL(EQUALALL (Function) NIL NIL ("9") 2) X Y) [Function] Like EQUAL, except it descends into the contents of arrays, hash arrays, user data types, etc. Two non-EQ arrays may be EQUALALL if their respective componants are EQUALALL. Note: In general, EQUALALL descends all the way into all datatypes, both those you've defined and those built into the system. If you have a data structure with fonts and pointers to windows, EQUALALL will descend those also. If the data structures are circular, as windows are, EQUALALL can cause stack overflow. Logical Predicates 1 (AND X1 X2 ... XN)(AND% X1% X2% ...% XN%) (Function) %(AND% X1% X2% ...% XN%) NIL ("9") 3) [NLambda NoSpread Function] Takes an indefinite number of arguments (including zero), that are evaluated in order. If any argument evaluates to NIL, AND immediately returns NIL, without evaluating the remaining arguments. If all of the arguments evaluate to non-NIL, the value of the last argument is returned. (AND) => T. (OR X1 X2 ... XN)(OR% X1% X2% ...% XN%) (Function) %(OR% X1% X2% ...% XN%) NIL ("9") 3) [NLambda NoSpread Function] Takes an indefinite number of arguments (including zero), that are evaluated in order. If any argument is non-NIL, the value of that argument is returned by OR (without evaluating the remaining arguments). If all of the arguments evaluate to NIL, NIL is returned. (OR) => NIL. AND and OR can be used as simple logical connectives, but note that they may not evaluate all of their arguments. This makes a difference if some of the arguments cause side-effects. This also means you can use AND and OR as simple conditional statements. For example: (AND (LISTP X) (CDR X)) returns the value of (CDR X) if X is a list cell; otherwise it returns NIL without evaluating (CDR X). In general, you should avoid this use of AND and OR in favor of more explicit conditional statements in order to make programs more readable. COND Conditional Function 1 (COND CLAUSE1 CLAUSE2 ... CLAUSEK)(COND% CLAUSE1% CLAUSE2% ...% CLAUSEK%) (Function) %(COND% CLAUSE1% CLAUSE2% ...% CLAUSEK%) NIL ("9") 3) [NLambda NoSpread Function] COND takes an indefinite number of arguments, called clauses. Each CLAUSEi is a list of the form (Pi Ci1 ... CiN), where Pi is the predicate, and Ci1 ... CiN are the consequents. The operation of COND can be paraphrased as: IF P1 THEN C11 ... C1N ELSEIF P2 THEN C21 ... C2N ELSEIF P3 ... The clauses are considered in sequence as follows: The predicate P1 of the clause CLAUSEi is evaluated. If the value of P1 is true (non-NIL), the consequents Ci1 ... CiN are evaluated in order, and the value of the COND is the value of the last expression in the clause. If P1 is false (EQ to NIL), then the remainder of CLAUSEi is ignored, and the next clause, CLAUSEi+1, is considered. If no Pi is true for any clause, the value of the COND is NIL. If a clause has no consequents, and has the form (Pi), then if Pi evaluates to non-NIL, it is returned as the value of the COND. It is only evaluated once. Example: (DEFINEQ (DOUBLE (X) (COND ((NUMBERP X) (PLUS X X)) ((STRINGP X) (CONCAT X X)) ((ATOM X) (PACK* X X)) (T (PRINT "unknown") X) ((HORRIBLE-ERROR))] (DOUBLE) (DOUBLE 5) 10 (DOUBLE "FOO") "FOOFOO" (DOUBLE 'BAR) BARBAR (DOUBLE '(A B C)) "unknown" (A B C) A few points about this example: Notice that 5 is both a number and an atom, but it is caught by the NUMBERP clause before the ATOM clause. Also notice the predicate T, which is always true. This is the normal way to indicate a COND clause which will always be executed (if none of the preceeding clauses are true). (HORRIBLE-ERROR) will never be executed. The IF Statement 1 The IF statement(IF% STATEMENT NIL IF% statement NIL ("9") 4) lets you write conditional expressions that are easier to read than using COND directly. CLISP translates expressions using IF, THEN, ELSEIF, or ELSE (or their lowercase versions) into equivalent CONDs. In general, statements of the form: (if AAA then BBB elseif CCC then DDD else EEE) are translated to: (COND (AAA BBB) (CCC DDD) (T EEE)) The segment between IF or ELSEIF and the next THEN corresponds to the predicate of a COND clause, and the segment between THEN and the next ELSE or ELSEIF as the consequent(s). ELSE is the same as ELSEIF T THEN. These words are spelling corrected using the spelling list CLISPIFWORDSPLST. You may also use lower-case versions (if, then, elseif, else). If there is nothing following a THEN, or THEN is omitted entirely, the resulting COND clause has a predicate but no consequent. For example, (if X then elseif ...) and (if X elseif ...) both translate to (COND (X) ...)%if X is not NIL, it is returned as the value of the COND. Each predicate must be a single expression, but multiple expressions are allowed as the consequents after THEN or ELSE. Multiple consequent expressions are implicitely wrapped in a PROGN, and the value of the last one is returned as the value of the consequent. For example: (if X then (PRINT "FOO") (PRINT "BAR") elseif Y then (PRINT "BAZ")) Selection Functions 1 (SELECTQ X CLAUSE1 CLAUSE2 ... CLAUSEK DEFAULT) [NLambda NoSpread Function] Selects a form or sequence of forms based on the value of X. Each clause CLAUSEi is a list of the form (Si Ci1 ... CiN) where Si is the selection key. Think of SELECTQ as: IF X = S1 THEN C11 ... C1N ELSEIF X = S2 THEN ... ELSE DEFAULT If Si is a symbol, the value of X is tested to see if it is EQ to Si (which is not evaluated). If so, the expressions Ci1 ... CiN are evaluated in sequence, and the value of the SELECTQ is the value of the last expression. If Si is a list, the value of X is compared with each element (not evaluated) of Si, and if X is EQ to any one of them, then Ci1 ... CiN are evaluated as above. If CLAUSEi is not selected in one of the two ways described, CLAUSEi+1 is tested, etc., until all the clauses have been tested. If none is selected, DEFAULT is evaluated, and its value is returned as the value of the SELECTQ. DEFAULT must be present. An example of the form of a SELECTQ is: [SELECTQ MONTH (FEBRUARY (if (LEAPYEARP) then 29 else 28)) ((SEPTEMBER APRIL JUNE NOVEMBER) 30) 31] If the value of MONTH is the symbol FEBRUARY, the SELECTQ returns 28 or 29 (depending on (LEAPYEARP)); otherwise if MONTH is APRIL, JUNE, SEPTEMBER, or NOVEMBER, the SELECTQ returns 30; otherwise it returns 31. SELECTQ compiles open, and is therefore very fast; however, it will not work if the value of X is a list, a large integer, or floating point number, since SELECTQ uses EQ for all comparisons. SELCHARQ (Chapter 2) is a version of SELECTQ that recognizes CHARCODE symbols. (SELECTC X CLAUSE1 CLAUSE2 ... CLAUSEK DEFAULT) [NLambda NoSpread Function] SELECTQ-on-Constant. Like SELECTQ, but the selection keys are evaluated, and the result used as a SELECTQ-style selection key. SELECTC is compiled as a SELECTQ, with the selection keys evaluated at compile-time. Therefore, the selection keys act like compile-time constants (see Chapter 18). For example: [SELECTC NUM ((for X from 1 to 9 collect (TIMES X X)) "SQUARE") "HIP"] compiles as: (SELECTQ NUM ((1 4 9 16 25 36 49 64 81) "SQUARE") "HIP") PROG and Associated Control Functions 1 (PROG1 X1 X2 ... XN) [NLambda NoSpread Function] Evaluates its arguments in order, and returns the value of its first argument X1. For example, (PROG1 X (SETQ X Y)) sets X to Y, and returns X's original value. (PROG2 X1 X2 ... XN) [NoSpread Function] Like PROG1. Evaluates its arguments in order, and returns the value of its second argument X2. (PROGN X1 X2 ... XN) [NLambda NoSpread Function] PROGN evaluates each of its arguments in order, and returns the value of its last argument. PROGN is used to specify more than one computation where the syntax allows only one, e.g., (SELECTQ ... (PROGN ...)) allows evaluation of several expressions as the default condition for a SELECTQ. (PROG VARLST E1 E2 ... EN) [NLambda NoSpread Function] Lets you bind some variables while you execute a series of expressions. VARLST is a list of local variables (must be NIL if no variables are used). Each symbol in VARLST is treated as the name of a local variable and bound to NIL. VARLST can also contain lists of the form (NAME FORM). In this case, NAME is the name of the variable and is bound to the value of FORM. The evaluation takes place before any of the bindings are performed, e.g., (PROG ((X Y) (Y X)) ...) will bind local variable X to the value of Y (evaluated outside the PROG) and local variable Y to the value of X (outside the PROG). An attempt to use anything other than a symbol as a PROG variable will cause an error, Arg not symbol. An attempt to use NIL or T as a PROG variable will cause an error, Attempt to bind NIL or T. The rest of the PROG is a sequence of forms and symbols (labels). The forms are evaluated sequentially; the labels serve only as markers. The two special functions, GO and RETURN, alter this flow of control as described below. The value of the PROG is usually specified by the function RETURN. If no RETURN is executed before the PROG falls off the end, the value of the PROG is NIL. (GO L) [NLambda NoSpread Function] GO is used to cause a transfer in a PROG. (GO L) will cause the PROG to evaluate forms starting at the label L (GO does not evaluate its argument). A GO can be used at any level in a PROG. If the label is not found, GO will search higher progs within the same function, e.g., (PROG ... A ... (PROG ... (GO A))). If the label is not found in the function in which the PROG appears, an error is generated, Undefined or illegal GO. (RETURN X) [Function] A RETURN is the normal exit for a PROG. Its argument is evaluated and is immediately returned the value of the PROG in which it appears. Note: If a GO or RETURN is executed in an interpreted function which is not a PROG, the GO or RETURN will be executed in the last interpreted PROG entered if any, otherwise cause an error. GO or RETURN inside of a compiled function that is not a PROG is not allowed, and will cause an error at compile time. As a corollary, GO or RETURN in a functional argument, e.g., to SORT, will not work compiled. Also, since NLSETQ's and ERSETQ's compile as separate functions, a GO or RETURN cannot be used inside of a compiled NLSETQ or ERSETQ if the corresponding PROG is outside, i.e., above, the NLSETQ or ERSETQ. (LET VARLST E1 E2 ... EN) [Macro] LET is essentially a PROG that can't contain GO's or RETURN's, and whose last form is the returned value. (LET* VARLST E1 E2 ... EN) [Macro] (PROG* VARLST E1 E2 ... EN) [Macro] LET* and PROG* differ from LET and PROG only in that the binding of the bound variables is done sequentially. Thus (LET* ((A (LIST 5)) (B (LIST A A))) (EQ A (CADR B))) would evaluate to T; whereas the same form with LET might find A an unbound variable when evaluating (LIST A A). The Iterative Statement 1 The various forms of the iterative statement(ITERATIVE% STATEMENT NIL Iterative% statement NIL ("9") 7) (i.s.) let you write complex loops easily. Rather than writing PROG, MAPC, MAPCAR, etc., let Medley do it for you. An iterative statement is a form consisting of a number of special words (known as i.s. operators or i.s.oprs), followed by operands. Many i.s.oprs (FOR, DO, WHILE, etc.) act like loops in other programming languages; others (COLLECT, JOIN, IN, etc.) do things useful in Lisp. You can also use lower-case versions of i.s.oprs (do, collect, etc.). (for X from 1 to 5 do (PRINT 'FOO)) FOO FOO FOO FOO FOO NIL (for X from 2 to 10 by 2 collect (TIMES X X)) (4 16 36 64 100) (for X in '(A B 1 C 6.5 NIL (45)) count (NUMBERP X)) 2 Iterative statements are implemented using CLISP, which translates them into the appropriate PROGs, MAPCARs, etc. They're are translated using all CLISP declarations in effect (standard/fast/undoable/ etc.); see Chapter 21. Misspelled i.s.oprs are recognized and corrected using the spelling list CLISPFORWORDSPLST. Operators can appear in any order; CLISP scans the entire statement before it begins to translate. If you define a function with the same name as an i.s.opr (WHILE, TO, etc.), that i.s.opr will no longer cause looping when it appears as CAR of a form, although it will continue to be treated as an i.s.opr if it appears in the interior of an iterative statement. To alert you, a warning message is printed, e.g., (While defined, therefore disabled in CLISP). I.S. Types(I.S.% TYPES NIL I.S.% Types NIL ("9") 8) Every iterative statement must have exactly one of the following operators in it (its is.stype), to specify what happens on each iteration. Its operand is called the body of the iterative statement. DO FORMS(DO% FORM (I.S. Operator) NIL NIL ("9") 8) [I.S. Operator] Evaluate FORMS at each iteration. DO with no other operator specifies an infinite loop. If some explicit or implicit terminating condition is specified, the value of the loop is NIL. Translates to MAPC or MAP whenever possible. COLLECT FORM(COLLECT% FORM (I.S. Operator) NIL NIL ("9") 8) [I.S. Operator] The value of FORM at each iteration is collected in a list, which is returned as the value of the loop when it terminates. Translates to MAPCAR, MAPLIST or SUBSET whenever possible. When COLLECT translates to a PROG (if UNTIL, WHILE, etc. appear in the loop), the translation employs an open TCONC using two pointers similar to that used by the compiler for compiling MAPCAR. To disable this translation, perform (CLDISABLE 'FCOLLECT). JOIN FORM (JOIN% FORM% (I.S. Operator) NIL NIL ("9") 8) [I.S. Operator] FORM returns a list; the lists from each iteration are concatenated using NCONC, forming one long list. Translates to MAPCONC or MAPCON whenever possible. /NCONC, /MAPCONC, and /MAPCON are used when the CLISP declaration UNDOABLE is in effect. SUM FORM(SUM% FORM (I.S. Operator) NIL NIL ("9") 8) [I.S. Operator] The values of FORM from each iteration are added together and returned as the value of the loop, e.g., (for I from 1 to 5 sum (TIMES I I)) returns 1+4+9+16+25 = 55. IPLUS, FPLUS, or PLUS will be used in the translation depending on the CLISP declarations in effect. COUNT FORM(COUNT% FORM (I.S. Operator) NIL NIL ("9") 8) [I.S. Operator] Counts the number of times that FORM is true, and returns that count as the loop's value. ALWAYS FORM(ALWAYS% FORM (I.S. Operator) NIL NIL ("9") 9) [I.S. Operator] Returns T if the value of FORM is non-NIL for all iterations. Note: Returns NIL as soon as the value of FORM is NIL). NEVER FORM(NEVER% FORM (I.S. Operator) NIL NIL ("9") 9) [I.S. Operator] Like ALWAYS, but returns T if the value of FORM is never true. Note: Returns NIL as soon as the value of FORM is non-NIL. Often, you'll want to set a variable each time through the loop; that's called the iteration variable, or i.v. for short. The following i.s.types explicitly refer to the i.v. This is explained below under FOR. THEREIS FORM(THEREIS% FORM (I.S. Operator) NIL NIL ("9") 9) [I.S. Operator] Returns the first value of the i.v. for which FORM is non-NIL, e.g., (for X in Y thereis (NUMBERP X)) returns the first number in Y. Note: Returns the value of the i.v. as soon as the value of FORM is non-NIL. LARGEST FORM(LARGEST% FORM (I.S. Operator) NIL NIL ("9") 9) [I.S. Operator] SMALLEST FORM(SMALLEST% FORM (I.S. Operator) NIL NIL ("9") 9) [I.S. Operator] Returns the value of the i.v. that provides the largest/smallest value of FORM. $$EXTREME is always bound to the current greatest/smallest value, $$VAL to the value of the i.v. from which it came. Iteration Variable I.s.oprs You'll want to bind variables to use during the loop. Rather than putting the loop inside a PROG or LET, you can specify bindings like so: BIND VAR(BIND% VAR (I.S. Operator) NIL NIL ("9") 9) [I.S. Operator] BIND VARS(BIND% VARS (I.S. Operator) NIL NIL ("9") 9) [I.S. Operator] Used to specify dummy variables, which are bound locally within the i.s. Note: You can initialize a variable VAR by saying VARFORM: (bind HEIGHT 0 WEIGHT 0 for SOLDIER in ...) To specify iteration variables, use these operators: FOR VAR(FOR% VAR (I.S. Operator) NIL NIL ("9") 9) [I.S. Operator] Specifies the iteration variable (i.v.) that is used in conjunction with IN, ON, FROM, TO, and BY. The variable is rebound within the loop, so the value of the variable outside the loop is not affected. Example: (SETQ X 55) 55 (for X from 1 to 5 collect (TIMES X X)) (1 4 9 16 25) X 55 FOR OLD VAR(FOR% OLD% VAR (I.S. Operator) NIL NIL ("9") 10) [I.S. Operator] Like FOR, but VAR is not rebound, so its value outside the loop is changed. Example: (SETQ X 55) 55 (for old X from 1 to 5 collect (TIMES X X)) (1 4 9 16 25) X 6 FOR VARS(FOR% VARS (I.S. Operator) NIL NIL ("9") 10) [I.S. Operator] VARS a list of variables, e.g., (for (X Y Z) in ...). The first variable is the i.v., the rest are dummy variables. See BIND above. IN FORM(IN% FORM (I.S. Operator) NIL NIL ("9") 10) [I.S. Operator] FORM must evaluate to a list. The i.v. is set to successive elements of the list, one per iteration. For example, (for X in Y do ...) corresponds to (MAPC Y (FUNCTION (LAMBDA (X) ...))). If no i.v. has been specified, a dummy is supplied, e.g., (in Y collect CADR) is equivalent to (MAPCAR Y (FUNCTION CADR)). ON FORM(ON% FORM (I.S. Operator) NIL NIL ("9") 10) [I.S. Operator] Same as IN, but the i.v. is reset to the corresponding tail at each iteration. Thus IN corresponds to MAPC, MAPCAR, and MAPCONC, while ON corresponds to MAP, MAPLIST, and MAPCON. (for X on '(A B C) do (PRINT X)) (A B C) (B C) (C) NIL Note: For both IN and ON, FORM is evaluated before the main part of the i.s. is entered, i.e. outside of the scope of any of the bound variables of the i.s. For example, (for X bind (Y'(1 2 3)) in Y ...) will map down the list which is the value of Y evaluated outside of the i.s., not (1 2 3). IN OLD VAR(IN% OLD% VAR (I.S. Operator) NIL NIL ("9") 10) [I.S. Operator] Specifies that the i.s. is to iterate down VAR, with VAR itself being reset to the corresponding tail at each iteration, e.g., after (for X in old L do ... until ...) finishes, L will be some tail of its original value. IN OLD (VARFORM)(IN% OLD% %(VAR_FORM%) (I.S. Operator) NIL NIL ("9") 10) [I.S. Operator] Same as IN OLD VAR, except VAR is first set to value of FORM. ON OLD VAR(ON% OLD% VAR (I.S. Operator) NIL NIL ("9") 10) [I.S. Operator] Same as IN OLD VAR except the i.v. is reset to the current value of VAR at each iteration, instead of to (CAR VAR). ON OLD (VARFORM)(ON% OLD% %(VAR_FORM%) (I.S. Operator) NIL NIL ("9") 11) [I.S. Operator] Same as ON OLD VAR, except VAR is first set to value of FORM. INSIDE FORM(INSIDE% FORM (I.S. Operator) NIL NIL ("9") 11) [I.S. Operator] Like IN, but treats first non-list, non-NIL tail as the last element of the iteration, e.g., INSIDE '(A B C D . E) iterates five times with the i.v. set to E on the last iteration. INSIDE 'A is equivalent to INSIDE '(A), which will iterate once. FROM FORM(FROM% FORM (I.S. Operator) NIL NIL ("9") 11) [I.S. Operator] Specifies the initial value for a numerical i.v. The i.v. is automatically incremented by 1 after each iteration (unless BY is specified). If no i.v. has been specified, a dummy i.v. is supplied and initialized, e.g., (from 2 to 5 collect SQRT) returns (1.414 1.732 2.0 2.236). TO FORM(TO% FORM (I.S. Operator) NIL NIL ("9") 11) [I.S. Operator] Specifies the final value for a numerical i.v. If FROM is not specified, the i.v. is initialized to 1. If no i.v. has been specified, a dummy i.v. is supplied and initialized. If BY is not specified, the i.v. is automatically incremented by 1 after each iteration. When the i.v. is definitely being incremented, i.e., either BY is not specified, or its operand is a positive number, the i.s. terminates when the i.v. exceeds the value of FORM. Similarly, when the i.v. is definitely being decremented the i.s. terminates when the i.v. becomes less than the value of FORM (see description of BY). FORM is evaluated only once, when the i.s. is first entered, and its value bound to a temporary variable against which the i.v. is checked each interation. If the user wishes to specify an i.s. in which the value of the boundary condition is recomputed each iteration, he should use WHILE or UNTIL instead of TO. When both the operands to TO and FROM are numbers, and TO's operand is less than FROM's operand, the i.v. is decremented by 1 after each iteration. In this case, the i.s. terminates when the i.v. becomes less than the value of FORM. For example, (from 10 to 1 do PRINT) prints the numbers from 10 down to 1. BY FORM (without IN or ON) (BY% FORM% %(WITHOUT% IN/ON%)% (I.S. Operator) BY% FORM% %(without% IN/ON%)% NIL ("9") 11) [I.S. Operator] If you aren't using IN or ON, BY specifies how the i.v. itself is reset at each iteration. If you're using FROM or TO, the i.v. is known to be numerical, so the new i.v. is computed by adding the value of FORM (which is reevaluated each iteration) to the current value of the i.v., e.g., (for N from 1 to 10 by 2 collect N) makes a list of the first five odd numbers. If FORM is a positive number (FORM itself, not its value, which in general CLISP would have no way of knowing in advance), the loop stops when the value of the i.v. exceeds the value of TO's operand. If FORM is a negative number, the loop stops when the value of the i.v. becomes less than TO's operand, e.g., (for I from N to M by -2 until (LESSP I M) ...). Otherwise, the terminating condition for each iteration depends on the value of FORM for that iteration: if FORM<0, the test is whether the i.v. is less than TO's operand, if FORM>0 the test is whether the i.v. exceeds TO's operand; if FORM = 0, the loop terminates unconditionally. If you didn't use FROM or TO and FORM is not a number, the i.v. is simply reset to the value of FORM after each iteration, e.g., (for I from N by (FOO) ...) sets I to the value of (FOO) on each loop after the first. BY FORM (with IN or ON) (BY% FORM% %(WITH% IN/ON%)% (I.S. Operator) BY% FORM% %(with% IN/ON%)% NIL ("9") 12) [I.S. Operator] If you did use IN or ON, FORM's value determines the tail for the next iteration, which in turn determines the value for the i.v. as described earlier, i.e., the new i.v. is CAR of the tail for IN, the tail itself for ON. In conjunction with IN, you can refer to the current tail within FORM by using the i.v. or the operand for IN/ON, e.g., (for Z in L by (CDDR Z) ...) or (for Z in L by (CDDR L) ...). At translation time, the name of the internal variable which holds the value of the current tail is substituted for the i.v. throughout FORM. For example, (for X in Y by (CDR (MEMB 'FOO (CDR X))) collect X) specifies that after each iteration, CDR of the current tail is to be searched for the atom FOO, and (CDR of) this latter tail to be used for the next iteration. AS VAR(AS% VAR (I.S. Operator) NIL NIL ("9") 12) [I.S. Operator] Lets you have more than one i.v. for a single loop, e.g., (for X in Y as U in V do ...) moves through the lisps Y and V in parallel (see MAP2C). The loop ends when any of the terminating conditions is met, e.g., (for X in Y as I from 1 to 10 collect X) makes a list of the first ten elements of Y, or however many elements there are on Y if less than 10. The operand to AS, VAR, specifies the new i.v. For the remainder of the i.s., or until another AS is encountered, all operators refer to the new i.v. For example, (for I from 1 to N1 as J from 1 to N2 by 2 as K from N3 to 1 by -1 ...) terminates when I exceeds N1, or J exceeds N2, or K becomes less than 1. After each iteration, I is incremented by 1, J by 2, and K by -1. OUTOF FORM(OUTOF% FORM (I.S. Operator) NIL NIL ("9") 12) [I.S. Operator] For use with generators. On each iteration, the i.v. is set to successive values returned by the generator. The loop ends when the generator runs out. Condition I.S. Oprs What if you want to do things only on certain times through the loop? You could make the loop body a big COND, but it's much more readable to use one of these: WHEN FORM(WHEN% FORM (I.S. Operator) NIL NIL ("9") 12) [I.S. Operator] Only run the loop body when FORM's value is non-NIL. For example, (for X in Y collect X when (NUMBERP X)) collects only the elements of Y that are numbers. UNLESS FORM(UNLESS% FORM (I.S. Operator) NIL NIL ("9") 12) [I.S. Operator] Opposite of WHEN: WHEN Z is the same as UNLESS (NOT Z). WHILE FORM(WHILE% FORM (I.S. Operator) NIL NIL ("9") 13) [I.S. Operator] WHILE FORM evaluates FORM before each iteration, and if the value is NIL, exits. UNTIL FORM(UNTIL% FORM (I.S. Operator) NIL NIL ("9") 13) [I.S. Operator] Opposite of WHILE: Evaluates FORM before each iteration, and if the value is not NIL, exits. REPEATWHILE FORM(REPEATWHILE% FORM (I.S. Operator) NIL NIL ("9") 13) [I.S. Operator] Same as WHILE except the test is performed after the loop body, but before the i.v. is reset for the next iteration. REPEATUNTIL FORM(REPEATUNTIL% FORM (I.S. Operator) NIL NIL ("9") 13) [I.S. Operator] Same as UNTIL, except the test is performed after the loop body. Other I.S. Operators FIRST FORM(FIRST% FORM (I.S. Operator) NIL NIL ("9") 13) [I.S. Operator] FORM is evaluated once before the first iteration, e.g., (for X Y Z in L first (FOO Y Z) ...), and FOO could be used to initialize Y and Z. FINALLY FORM(FINALLY% FORM (I.S. Operator) NIL NIL ("9") 13) [I.S. Operator] FORM is evaluated after the loop terminates. For example, (for X in L bind Y_0 do (if (ATOM X) then (SETQ Y (PLUS Y 1))) finally (RETURN Y)) will return the number of atoms in L. EACHTIME FORM(EACHTIME% FORM (I.S. Operator) NIL NIL ("9") 13) [I.S. Operator] FORM is evaluated at the beginning of each iteration before, and regardless of, any testing. For example, consider, (for I from 1 to N do (... (FOO I) ...) unless (... (FOO I) ...) until (... (FOO I) ...)) You might want to set a temporary variable to the value of (FOO I) in order to avoid computing it three times each iteration. However, without knowing the translation, you can't know whether to put the assignment in the operand to DO, UNLESS, or UNTIL. You can avoid this problem by simply writing EACHTIME (SETQ J (FOO I)). DECLARE: DECL(DECLARE:% DECL (I.S. Operator) NIL NIL ("9") 13) [I.S. Operator] Inserts the form (DECLARE DECL) immediately following the PROG variable list in the translation, or, in the case that the translation is a mapping function rather than a PROG, immediately following the argument list of the lambda expression in the translation. This can be used to declare variables bound in the iterative statement to be compiled as local or special variables. For example (for X in Y declare: (LOCALVARS X) ...). Several DECLARE:s can apppear in the same i.s.; the declarations are inserted in the order they appear. DECLARE DECL(DECLARE% DECL (I.S. Operator) NIL NIL ("9") 14) [I.S. Operator] Same as DECLARE:. Since DECLARE is also the name of a function, DECLARE cannot be used as an i.s. operator when it appears as CAR of a form, i.e. as the first i.s. operator in an iterative statement. However, declare (lowercase version) can be the first i.s. operator. ORIGINAL I.S.OPR OPERAND(ORIGINAL% I.S.OPR% OPERAND (I.S. Operator) NIL NIL ("9") 14) [I.S. Operator] I.S.OPR will be translated using its original, built-in interpretation, independent of any user defined i.s. operators. There are also a number of i.s.oprs that make it easier to create iterative statements that use the clock, looping for a given period of time. See timers, Chapter 12. Miscellaneous Hints For Using I.S.Oprs Lowercase versions of all i.s. operators are equivalent to the uppercase, e.g., (for X in Y ...) is equivalent to (FOR X IN Y ...). Each i.s. operator is of lower precedence than all Interlisp forms, so parentheses around the operands can be omitted, and will be supplied where necessary, e.g., BIND (X Y Z) can be written BIND X Y Z, OLD (X_FORM) as OLD X_FORM, etc. RETURN or GO may be used in any operand. (In this case, the translation of the iterative statement will always be in the form of a PROG, never a mapping function.) RETURN means return from the loop (with the indicated value), not from the function in which the loop appears. GO refers to a label elsewhere in the function in which the loop. appears, except for the labels $$LP, $$ITERATE, and $$OUT which are reserved, as described below. In the case of FIRST, FINALLY, EACHTIME, DECLARE: or one of the i.s.types, e.g., DO, COLLECT, SUM, etc., the operand can consist of more than one form, e.g., COLLECT (PRINT (CAR X)) (CDR X), in which case a PROGN is supplied. Each operand can be the name of a function, in which case it is applied to the (last) i.v., e.g., (for X in Y do PRINT when NUMBERP) is the same as (for X in Y do (PRINT X) when (NUMBERP X)). Note that the i.v. need not be explicitly specified, e.g., (in Y do PRINT when NUMBERP) will work. For i.s.types, e.g., DO, COLLECT, JOIN, the function is always applied to the first i.v. in the i.s., whether explicity named or not. For example, (in Y as I from 1 to 10 do PRINT) prints elements on Y, not integers between 1 and 10. Note that this feature does not make much sense for FOR, OLD, BIND, IN, or ON, since they operate before the loop starts, when the i.v. may not even be bound. In the case of BY in conjunction with IN, the function is applied to the current tail e.g., (for X in Y by CDDR ...) is the same as (for X in Y by (CDDR X) ...). While the exact translation of a loop depends on which operators are present, a PROG will always be used whenever the loop specifies dummy variables%if BIND appears, or there is more than one variable specified by a FOR, or a GO, RETURN, or a reference to the variable $$VAL appears in any of the operands. When PROG is used, the form of the translation is: (PROG VARIABLES {initialize} $$LP {eachtime} {test} {body} $$ITERATE {aftertest} {update} (GO $$LP) $$OUT {finalize} (RETURN $$VAL)) where {test} corresponds to that part of the loop that tests for termination and also for those iterations for which {body} is not going to be executed, (as indicated by a WHEN or UNLESS); {body} corresponds to the operand of the i.s.type, e.g., DO, COLLECT, etc.; {aftertest} corresponds to those tests for termination specified by REPEATWHILE or REPEATUNTIL; and {update} corresponds to that part that resets the tail, increments the counter, etc. in preparation for the next iteration. {initialize}, {finalize}, and {eachtime} correspond to the operands of FIRST, FINALLY, and EACHTIME, if any. Since {body} always appears at the top level of the PROG, you can insert labels in {body}, and GO to them from within {body} or from other i.s. operands, e.g., (for X in Y first (GO A) do (FOO) A (FIE)). However, since {body} is dwimified as a list of forms, the label(s) should be added to the dummy variables for the iterative statement in order to prevent their being dwimified and possibly corrected, e.g., (for X in Y bind A first (GO A) do (FOO) A (FIE)). You can also GO to $$LP, $$ITERATE, or $$OUT, or explicitly set $$VAL. Errors in Iterative Statements An error will be generated and an appropriate diagnostic printed if any of the following conditions hold: 1. Operator with null operand, i.e., two adjacent operators, as in (for X in Y until do ...) 2. Operand consisting of more than one form (except as operand to FIRST, FINALLY, or one of the i.s.types), e.g., (for X in Y (PRINT X) collect ...). 3. IN, ON, FROM, TO, or BY appear twice in same i.s. 4. Both IN and ON used on same i.v. 5. FROM or TO used with IN or ON on same i.v. 6. More than one i.s.type, e.g., a DO and a SUM. In 3, 4, or 5, an error is not generated if an intervening AS occurs. If an error occurs, the i.s. is left unchanged. If no DO, COLLECT, JOIN or any of the other i.s.types are specified, CLISP will first attempt to find an operand consisting of more than one form, e.g., (for X in Y (PRINT X) when ATOM X ...), and in this case will insert a DO after the first form. (In this case, condition 2 is not considered to be met, and an error is not generated.) If CLISP cannot find such an operand, and no WHILE or UNTIL appears in the i.s., a warning message is printed: NO DO, COLLECT, OR JOIN: followed by the i.s. Similarly, if no terminating condition is detected, i.e., no IN, ON, WHILE, UNTIL, TO, or a RETURN or GO, a warning message is printed: Possible non-terminating iterative statement: followed by the iterative statement. However, since the user may be planning to terminate the i.s. via an error, Control-E, or a RETFROM from a lower function, the i.s. is still translated. Note: The error message is not printed if the value of CLISPI.S.GAG is T (initially NIL). Defining New Iterative Statement Operators The following function is available for defining new or redefining existing iterative statement operators: (I.S.OPR NAME FORM OTHERS EVALFLG)(I.S.OPR% NAME% FORM% OTHERS% EVALFLG%) (Function) %(I.S.OPR% NAME% FORM% OTHERS% EVALFLG%) NIL ("9") 16) [Function] NAME is the name of the new i.s.opr. If FORM is a list, NAME will be a new i.s.type, and FORM its body. OTHERS is an (optional) list of additional i.s. operators and operands which will be added to the i.s. at the place where NAME appears. If FORM is NIL, NAME is a new i.s.opr defined entirely by OTHERS. In both FORM and OTHERS, the atom $$VAL can be used to reference the value to be returned by the i.s., I.V. to reference the current i.v., and BODY to reference NAME's operand. In other words, the current i.v. will be substituted for all instances of I.V. and NAME's operand will be substituted for all instances of BODY throughout FORM and OTHERS. If EVALFLG is T, FORM and OTHERS are evaluated at translation time, and their values used as described above. A dummy variable for use in translation that does not clash with a dummy variable already used by some other i.s. operators can be obtained by calling (GETDUMMYVAR). (GETDUMMYVAR T) will return a dummy variable and also insure that it is bound as a PROG variable in the translation. If NAME was previously an i.s.opr and is being redefined, the message (NAME REDEFINED) will be printed (unless DFNFLG=T), and all expressions using the i.s.opr NAME that have been translated will have their translations discarded. The following are some examples of how I.S.OPR could be called to define some existing i.s.oprs, and create some new ones: COLLECT (I.S.OPR 'COLLECT '(SETQ $$VAL (NCONC1 $$VAL BODY))) SUM (I.S.OPR 'SUM '(SETQ $$VAL_(PLUS $$VAL BODY) '(FIRST (SETQ $$VAL0)) NEVER (I.S.OPR 'NEVER '(if BODY then (SETQ $$VAL NIL) (GO $$OUT)) Note: (if BODY then (RETURN NIL)) would exit from the i.s. immediately and therefore not execute the operations specified via a FINALLY (if any). THEREIS (I.S.OPR 'THEREIS '(if BODY then (SETQ $$VAL I.V.) (GO $$OUT))) RCOLLECT To define RCOLLECT, a version of COLLECT which uses CONS instead of NCONC1 and then reverses the list of values: (I.S.OPR 'RCOLLECT '(FINALLY (RETURN (DREVERSE $$VAL)))] TCOLLECT To define TCOLLECT, a version of COLLECT which uses TCONC: (I.S.OPR 'TCOLLECT '(TCONC $$VAL BODY) '(FIRST (SETQ $$VAL (CONS)) FINALLY (RETURN (CAR $$VAL)))] PRODUCT (I.S.OPR 'PRODUCT '(SETQ $$VAL $$VAL*BODY) '(FIRST ($$VAL 1))] UPTO To define UPTO, a version of TO whose operand is evaluated only once: (I.S.OPR 'UPTO NIL '(BIND $$FOOBODY TO $$FOO)] TO To redefine TO so that instead of recomputing FORM each iteration, a variable is bound to the value of FORM, and then that variable is used: (I.S.OPR 'TO NIL '(BIND $$END FIRST (SETQ $$END BODY) ORIGINALTO $$END)] Note the use of ORIGINAL to redefine TO in terms of its original definition. ORIGINAL is intended for use in redefining built-in operators, since their definitions are not accessible, and hence not directly modifiable. Thus if the operator had been defined by the user via I.S.OPR, ORIGINAL would not obtain its original definition. In this case, one presumably would simply modify the i.s.opr definition. I.S.OPR can also be used to define synonyms for already defined i.s. operators by calling I.S.OPR with FORM an atom, e.g., (I.S.OPR 'WHERE 'WHEN) makes WHERE be the same as WHEN. Similarly, following (I.S.OPR 'ISTHERE 'THEREIS), one can write (ISTHERE ATOM IN Y), and following (I.S.OPR 'FIND 'FOR) and (I.S.OPR 'SUCHTHAT 'THEREIS), one can write (find X in Y suchthat X member Z) . In the current system, WHERE is synonymous with WHEN, SUCHTHAT and ISTHERE with THEREIS, FIND with FOR, and THRU with TO. If FORM is the atom MODIFIER, then NAME is defined as an i.s.opr which can immediately follow another i.s. operator (i.e., an error will not be generated, as described previously). NAME will not terminate the scope of the previous operator, and will be stripped off when DWIMIFY is called on its operand. OLD is an example of a MODIFIER type of operator. The MODIFIER feature allows the user to define i.s. operators similar to OLD, for use in conjunction with some other user defined i.s.opr which will produce the appropriate translation. The file package command I.S.OPRS (Chapter 17) will dump the definition of i.s.oprs. (I.S.OPRS PRODUCT UPTO) as a file package command will print suitable expressions so that these iterative statement operators will be (re)defined when the file is loaded. [This page intentionally left blank] (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "9-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "9-" "")) (222 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "9-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "9-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "9-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "9-" "")) (222 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (48 612 564 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))),ll2$$,ll,<<,r~2HTT2HTT,555,Zl,<<,~~,l~,l~,HH,HH,~~,ZZ,l~306T,l~,~~,l~1EVENT-T@ PAGEHEADING RIGHTPAGE//HH0 -T,3 -(T/F PAGEHEADING RIGHTPAGE/HH,HH306T,,@ PAGEHEADINGLEFTBACKTTITANTITAN + INTERLISP-D REFERENCE MANUAL +CONDITIONALS AND ITERATIVE STATEMENTS +"9"9. LISTS AND ITERATIVE STATEMENTS +3 + + +Medley gives you a large number of predicates, conditional functions, and control functions. Also, there is a complex iterative statement facility which allows you to easily create complex loops and iterative constructs. +Data Type Predicates +1 + +Medley provides separate functions for testing whether objects are of certain commonly-used types: +(LITATOM(LITATOM (Function) NIL NIL ("9") 1) X) [Function] +Returns T if X is a symbol; NIL otherwise. Note that a number is not a symbol. +(SMALLP(SMALLP (Function) NIL NIL ("9") 1) X) [Function] +Returns X if X is a small integer; NIL otherwise. (The range of small integers is -65536 to +65535. +(FIXP(FIXP (Function) NIL NIL ("9") 1) X) [Function] +Returns X if X is a small or large integer; NIL otherwise. +(FLOATP(FLOATP (Function) NIL NIL ("9") 1) X) [Function] +Returns X if X is a floating point number; NIL otherwise. +(NUMBERP(NUMBERP (Function) NIL NIL ("9") 1) X) [Function] +Returns X if X is a number of any type, NIL otherwise. +(ATOM(ATOM (Function) NIL NIL ("9") 1) X) [Function] +Returns T if X is an atom (i.e. a symbol or a number); NIL otherwise. +(ATOM X) is NIL if X is an array, string, etc. In Common Lisp, CL:ATOM is defined equivalent to the Interlisp function NLISTP. +(LISTP(LISTP (Function) NIL NIL ("9") 1) X) [Function] +Returns X if X is a list cell (something created by CONS); NIL otherwise. +(NLISTP(NLISTP (Function) NIL NIL ("9") 1) X) [Function] +(NOT (LISTP X)). Returns T if X is not a list cell, NIL otherwise. +(STRINGP(STRINGP (Function) NIL NIL ("9") 1) X) [Function] +Returns X if X is a string, NIL otherwise. +(ARRAYP X) [Function] +Returns X if X is an array, NIL otherwise. +(HARRAYP X) [Function] +Returns X if it is a hash array object; otherwise NIL. +HARRAYP(HARRAYP (Function) NIL NIL ("9") 2) returns NIL if X is a list whose CAR is an HARRAYP, even though this is accepted by the hash array functions. +Note: The empty list, () or NIL, is considered to be a symbol, rather than a list. Therefore, (LITATOM NIL) = (ATOM NIL) = T and (LISTP NIL) = NIL. Take care when using these functions if the object may be the empty list NIL. +Equality Predicates +1 + +Sometimes, there is more than one type of equality. For instance, given two lists, you can ask whether they are exactly the same object, or whether they are two distinct lists that contain the same elements. Confusion between these two types of equality is often the source of program errors. +(EQ(EQ (Function) NIL NIL ("9") 2) X Y) [Function] +Returns T if X and Y are identical pointers; NIL otherwise. EQ should not be used to compare two numbers, unless they are small integers; use EQP instead. +(NEQ(NEQ (Function) NIL NIL ("9") 2) X Y) [Function] +The same as (NOT (EQ X Y)) +(NULL(NULL (Function) NIL NIL ("9") 2) X) [Function] +(NOT(NOT (Function) NIL NIL ("9") 2) X) [Function] +The same as (EQ X NIL) +(EQP(EQP (Function) NIL NIL ("9") 2) X Y) [Function] +Returns T if X and Y are EQ, or if X and Y are numbers and are equal in value; NIL otherwise. For more discussion of EQP and other number functions, see Chapter 7. +EQP also can be used to compare stack pointers (Section 11) and compiled code (Chapter 10). +(EQUAL(EQUAL (Function) NIL NIL ("9") 2) X Y) [Function] +EQUAL returns T if X and Y are one of the following: +1. EQ +2. EQP, i.e., numbers with equal value +3. STREQUAL, i.e., strings containing the same sequence of characters +4. Lists and CAR of X is EQUAL to CAR of Y, and CDR of X is EQUAL to CDR of Y +EQUAL returns NIL otherwise. Note that EQUAL can be significantly slower than EQ. +A loose description of EQUAL might be to say that X and Y are EQUAL if they print out the same way. +(EQUALALL(EQUALALL (Function) NIL NIL ("9") 2) X Y) [Function] +Like EQUAL, except it descends into the contents of arrays, hash arrays, user data types, etc. Two non-EQ arrays may be EQUALALL if their respective componants are EQUALALL. +Note: In general, EQUALALL descends all the way into all datatypes, both those you've defined and those built into the system. If you have a data structure with fonts and pointers to windows, EQUALALL will descend those also. If the data structures are circular, as windows are, EQUALALL can cause stack overflow. +Logical Predicates +1 + +(AND X1 X2 ... XN)(AND% X1% X2% ...% XN%) (Function) %(AND% X1% X2% ...% XN%) NIL ("9") 3) [NLambda NoSpread Function] +Takes an indefinite number of arguments (including zero), that are evaluated in order. If any argument evaluates to NIL, AND immediately returns NIL, without evaluating the remaining arguments. If all of the arguments evaluate to non-NIL, the value of the last argument is returned. (AND) => T. +(OR X1 X2 ... XN)(OR% X1% X2% ...% XN%) (Function) %(OR% X1% X2% ...% XN%) NIL ("9") 3) [NLambda NoSpread Function] +Takes an indefinite number of arguments (including zero), that are evaluated in order. If any argument is non-NIL, the value of that argument is returned by OR (without evaluating the remaining arguments). If all of the arguments evaluate to NIL, NIL is returned. (OR) => NIL. +AND and OR can be used as simple logical connectives, but note that they may not evaluate all of their arguments. This makes a difference if some of the arguments cause side-effects. This also means you can use AND and OR as simple conditional statements. For example: (AND (LISTP X) (CDR X)) returns the value of (CDR X) if X is a list cell; otherwise it returns NIL without evaluating (CDR X). In general, you should avoid this use of AND and OR in favor of more explicit conditional statements in order to make programs more readable. +COND Conditional Function +1 + +(COND CLAUSE1 CLAUSE2 ... CLAUSEK)(COND% CLAUSE1% CLAUSE2% ...% CLAUSEK%) (Function) %(COND% CLAUSE1% CLAUSE2% ...% CLAUSEK%) NIL ("9") 3) [NLambda NoSpread Function] +COND takes an indefinite number of arguments, called clauses. Each CLAUSEi is a list of the form (Pi Ci1 ... CiN), where Pi is the predicate, and Ci1 ... CiN are the consequents. The operation of COND can be paraphrased as: +IF P1 THEN C11 ... C1N ELSEIF P2 THEN C21 ... C2N ELSEIF P3 ... +The clauses are considered in sequence as follows: The predicate P1 of the clause CLAUSEi is evaluated. If the value of P1 is true (non-NIL), the consequents Ci1 ... CiN are evaluated in order, and the value of the COND is the value of the last expression in the clause. If P1 is false (EQ to NIL), then the remainder of CLAUSEi is ignored, and the next clause, CLAUSEi+1, is considered. If no Pi is true for any clause, the value of the COND is NIL. +If a clause has no consequents, and has the form (Pi), then if Pi evaluates to non-NIL, it is returned as the value of the COND. It is only evaluated once. +Example: +_(DEFINEQ (DOUBLE (X) +(COND ((NUMBERP X) (PLUS X X)) +((STRINGP X) (CONCAT X X)) +((ATOM X) (PACK* X X)) +(T (PRINT "unknown") X) +((HORRIBLE-ERROR))] +(DOUBLE) +_(DOUBLE 5) +10 +_(DOUBLE "FOO") +"FOOFOO" +_(DOUBLE 'BAR) +BARBAR +_(DOUBLE '(A B C)) +"unknown" +(A B C) +A few points about this example: Notice that 5 is both a number and an atom, but it is caught by the NUMBERP clause before the ATOM clause. Also notice the predicate T, which is always true. This is the normal way to indicate a COND clause which will always be executed (if none of the preceeding clauses are true). (HORRIBLE-ERROR) will never be executed. +The IF Statement +1 + +The IF statement(IF% STATEMENT NIL IF% statement NIL ("9") 4) lets you write conditional expressions that are easier to read than using COND directly. CLISP translates expressions using IF, THEN, ELSEIF, or ELSE (or their lowercase versions) into equivalent CONDs. In general, statements of the form: +(if AAA then BBB elseif CCC then DDD else EEE) +are translated to: +(COND (AAA BBB) + (CCC DDD) + (T EEE)) +The segment between IF or ELSEIF and the next THEN corresponds to the predicate of a COND clause, and the segment between THEN and the next ELSE or ELSEIF as the consequent(s). ELSE is the same as ELSEIF T THEN. These words are spelling corrected using the spelling list CLISPIFWORDSPLST. You may also use lower-case versions (if, then, elseif, else). +If there is nothing following a THEN, or THEN is omitted entirely, the resulting COND clause has a predicate but no consequent. For example, (if X then elseif ...) and (if X elseif ...) both translate to (COND (X) ...)%if X is not NIL, it is returned as the value of the COND. +Each predicate must be a single expression, but multiple expressions are allowed as the consequents after THEN or ELSE. Multiple consequent expressions are implicitely wrapped in a PROGN, and the value of the last one is returned as the value of the consequent. For example: +(if X then (PRINT "FOO") (PRINT "BAR") elseif Y then (PRINT "BAZ")) +Selection Functions +1 + +(SELECTQ X CLAUSE1 CLAUSE2 ... CLAUSEK +DEFAULT) [NLambda NoSpread Function] +Selects a form or sequence of forms based on the value of X. Each clause CLAUSEi is a list of the form (Si Ci1 ... CiN) where Si is the selection key. Think of SELECTQ as: +IF X = S1 THEN C11 ... C1N ELSEIF X = S2 + THEN ... ELSE DEFAULT +If Si is a symbol, the value of X is tested to see if it is EQ to Si (which is not evaluated). If so, the expressions Ci1 ... CiN are evaluated in sequence, and the value of the SELECTQ is the value of the last expression. +If Si is a list, the value of X is compared with each element (not evaluated) of Si, and if X is EQ to any one of them, then Ci1 ... CiN are evaluated as above. +If CLAUSEi is not selected in one of the two ways described, CLAUSEi+1 is tested, etc., until all the clauses have been tested. If none is selected, DEFAULT is evaluated, and its value is returned as the value of the SELECTQ. DEFAULT must be present. +An example of the form of a SELECTQ is: +[SELECTQ MONTH +(FEBRUARY (if (LEAPYEARP) then 29 else 28)) + ((SEPTEMBER APRIL JUNE NOVEMBER) 30) 31] +If the value of MONTH is the symbol FEBRUARY, the SELECTQ returns 28 or 29 (depending on (LEAPYEARP)); otherwise if MONTH is APRIL, JUNE, SEPTEMBER, or NOVEMBER, the SELECTQ returns 30; otherwise it returns 31. +SELECTQ compiles open, and is therefore very fast; however, it will not work if the value of X is a list, a large integer, or floating point number, since SELECTQ uses EQ for all comparisons. +SELCHARQ (Chapter 2) is a version of SELECTQ that recognizes CHARCODE symbols. +(SELECTC X CLAUSE1 CLAUSE2 ... CLAUSEK +DEFAULT) [NLambda NoSpread Function] +SELECTQ-on-Constant. Like SELECTQ, but the selection keys are evaluated, and the result used as a SELECTQ-style selection key. +SELECTC is compiled as a SELECTQ, with the selection keys evaluated at compile-time. Therefore, the selection keys act like compile-time constants (see Chapter 18). +For example: +[SELECTC NUM + ((for X from 1 to 9 collect (TIMES X X)) "SQUARE") "HIP"] +compiles as: +(SELECTQ NUM + ((1 4 9 16 25 36 49 64 81) "SQUARE") "HIP") +PROG and Associated Control Functions +1 + +(PROG1 X1 X2 ... XN) [NLambda NoSpread Function] +Evaluates its arguments in order, and returns the value of its first argument X1. For example, (PROG1 X (SETQ X Y)) sets X to Y, and returns X's original value. +(PROG2 X1 X2 ... XN) [NoSpread Function] +Like PROG1. Evaluates its arguments in order, and returns the value of its second argument X2. +(PROGN X1 X2 ... XN) [NLambda NoSpread Function] +PROGN evaluates each of its arguments in order, and returns the value of its last argument. PROGN is used to specify more than one computation where the syntax allows only one, e.g., (SELECTQ ... (PROGN ...)) allows evaluation of several expressions as the default condition for a SELECTQ. +(PROG VARLST E1 E2 ... EN) [NLambda NoSpread Function] +Lets you bind some variables while you execute a series of expressions. VARLST is a list of local variables (must be NIL if no variables are used). Each symbol in VARLST is treated as the name of a local variable and bound to NIL. VARLST can also contain lists of the form (NAME FORM). In this case, NAME is the name of the variable and is bound to the value of FORM. The evaluation takes place before any of the bindings are performed, e.g., (PROG ((X Y) (Y X)) ...) will bind local variable X to the value of Y (evaluated outside the PROG) and local variable Y to the value of X (outside the PROG). An attempt to use anything other than a symbol as a PROG variable will cause an error, Arg not symbol. An attempt to use NIL or T as a PROG variable will cause an error, Attempt to bind NIL or T. +The rest of the PROG is a sequence of forms and symbols (labels). The forms are evaluated sequentially; the labels serve only as markers. The two special functions, GO and RETURN, alter this flow of control as described below. The value of the PROG is usually specified by the function RETURN. If no RETURN is executed before the PROG falls off the end, the value of the PROG is NIL. +(GO L) [NLambda NoSpread Function] +GO is used to cause a transfer in a PROG. (GO L) will cause the PROG to evaluate forms starting at the label L (GO does not evaluate its argument). A GO can be used at any level in a PROG. If the label is not found, GO will search higher progs within the same function, e.g., (PROG ... A ... (PROG ... (GO A))). If the label is not found in the function in which the PROG appears, an error is generated, Undefined or illegal GO. +(RETURN X) [Function] +A RETURN is the normal exit for a PROG. Its argument is evaluated and is immediately returned the value of the PROG in which it appears. +Note: If a GO or RETURN is executed in an interpreted function which is not a PROG, the GO or RETURN will be executed in the last interpreted PROG entered if any, otherwise cause an error. +GO or RETURN inside of a compiled function that is not a PROG is not allowed, and will cause an error at compile time. +As a corollary, GO or RETURN in a functional argument, e.g., to SORT, will not work compiled. Also, since NLSETQ's and ERSETQ's compile as separate functions, a GO or RETURN cannot be used inside of a compiled NLSETQ or ERSETQ if the corresponding PROG is outside, i.e., above, the NLSETQ or ERSETQ. +(LET VARLST E1 E2 ... EN) [Macro] +LET is essentially a PROG that can't contain GO's or RETURN's, and whose last form is the returned value. +(LET* VARLST E1 E2 ... EN) [Macro] +(PROG* VARLST E1 E2 ... EN) [Macro] +LET* and PROG* differ from LET and PROG only in that the binding of the bound variables is done sequentially. Thus +(LET* ((A (LIST 5)) + (B (LIST A A))) + (EQ A (CADR B))) +would evaluate to T; whereas the same form with LET might find A an unbound variable when evaluating (LIST A A). +The Iterative Statement +1 + +The various forms of the iterative statement(ITERATIVE% STATEMENT NIL Iterative% statement NIL ("9") 7) (i.s.) let you write complex loops easily. Rather than writing PROG, MAPC, MAPCAR, etc., let Medley do it for you. +An iterative statement is a form consisting of a number of special words (known as i.s. operators or i.s.oprs), followed by operands. Many i.s.oprs (FOR, DO, WHILE, etc.) act like loops in other programming languages; others (COLLECT, JOIN, IN, etc.) do things useful in Lisp. You can also use lower-case versions of i.s.oprs (do, collect, etc.). +_ (for X from 1 to 5 do (PRINT 'FOO)) +FOO +FOO +FOO +FOO +FOO +NIL +_(for X from 2 to 10 by 2 collect (TIMES X X)) +(4 16 36 64 100) +_(for X in '(A B 1 C 6.5 NIL (45)) count (NUMBERP X)) +2 +Iterative statements are implemented using CLISP, which translates them into the appropriate PROGs, MAPCARs, etc. They're are translated using all CLISP declarations in effect (standard/fast/undoable/ etc.); see Chapter 21. Misspelled i.s.oprs are recognized and corrected using the spelling list CLISPFORWORDSPLST. Operators can appear in any order; CLISP scans the entire statement before it begins to translate. +If you define a function with the same name as an i.s.opr (WHILE, TO, etc.), that i.s.opr will no longer cause looping when it appears as CAR of a form, although it will continue to be treated as an i.s.opr if it appears in the interior of an iterative statement. To alert you, a warning message is printed, e.g., (While defined, therefore disabled in CLISP). +I.S. Types(I.S.% TYPES NIL I.S.% Types NIL ("9") 8) +Every iterative statement must have exactly one of the following operators in it (its is.stype), to specify what happens on each iteration. Its operand is called the body of the iterative statement. +DO FORMS(DO% FORM (I.S. Operator) NIL NIL ("9") 8) [I.S. Operator] +Evaluate FORMS at each iteration. DO with no other operator specifies an infinite loop. If some explicit or implicit terminating condition is specified, the value of the loop is NIL. Translates to MAPC or MAP whenever possible. +COLLECT FORM(COLLECT% FORM (I.S. Operator) NIL NIL ("9") 8) [I.S. Operator] +The value of FORM at each iteration is collected in a list, which is returned as the value of the loop when it terminates. Translates to MAPCAR, MAPLIST or SUBSET whenever possible. +When COLLECT translates to a PROG (if UNTIL, WHILE, etc. appear in the loop), the translation employs an open TCONC using two pointers similar to that used by the compiler for compiling MAPCAR. To disable this translation, perform (CLDISABLE 'FCOLLECT). +JOIN FORM (JOIN% FORM% (I.S. Operator) NIL NIL ("9") 8) [I.S. Operator] +FORM returns a list; the lists from each iteration are concatenated using NCONC, forming one long list. Translates to MAPCONC or MAPCON whenever possible. /NCONC, /MAPCONC, and /MAPCON are used when the CLISP declaration UNDOABLE is in effect. +SUM FORM(SUM% FORM (I.S. Operator) NIL NIL ("9") 8) [I.S. Operator] +The values of FORM from each iteration are added together and returned as the value of the loop, e.g., (for I from 1 to 5 sum (TIMES I I)) returns 1+4+9+16+25 = 55. IPLUS, FPLUS, or PLUS will be used in the translation depending on the CLISP declarations in effect. +COUNT FORM(COUNT% FORM (I.S. Operator) NIL NIL ("9") 8) [I.S. Operator] +Counts the number of times that FORM is true, and returns that count as the loop's value. +ALWAYS FORM(ALWAYS% FORM (I.S. Operator) NIL NIL ("9") 9) [I.S. Operator] +Returns T if the value of FORM is non-NIL for all iterations. Note: Returns NIL as soon as the value of FORM is NIL). +NEVER FORM(NEVER% FORM (I.S. Operator) NIL NIL ("9") 9) [I.S. Operator] +Like ALWAYS, but returns T if the value of FORM is never true. Note: Returns NIL as soon as the value of FORM is non-NIL. +Often, you'll want to set a variable each time through the loop; that's called the iteration variable, or i.v. for short. The following i.s.types explicitly refer to the i.v. This is explained below under FOR. +THEREIS FORM(THEREIS% FORM (I.S. Operator) NIL NIL ("9") 9) [I.S. Operator] +Returns the first value of the i.v. for which FORM is non-NIL, e.g., (for X in Y thereis (NUMBERP X)) returns the first number in Y. +Note: Returns the value of the i.v. as soon as the value of FORM is non-NIL. +LARGEST FORM(LARGEST% FORM (I.S. Operator) NIL NIL ("9") 9) [I.S. Operator] +SMALLEST FORM(SMALLEST% FORM (I.S. Operator) NIL NIL ("9") 9) [I.S. Operator] +Returns the value of the i.v. that provides the largest/smallest value of FORM. $$EXTREME is always bound to the current greatest/smallest value, $$VAL to the value of the i.v. from which it came. +Iteration Variable I.s.oprs +You'll want to bind variables to use during the loop. Rather than putting the loop inside a PROG or LET, you can specify bindings like so: +BIND VAR(BIND% VAR (I.S. Operator) NIL NIL ("9") 9) [I.S. Operator] +BIND VARS(BIND% VARS (I.S. Operator) NIL NIL ("9") 9) [I.S. Operator] +Used to specify dummy variables, which are bound locally within the i.s. +Note: You can initialize a variable VAR by saying VAR_FORM: +(bind HEIGHT _ 0 WEIGHT _ 0 for SOLDIER in ...) +To specify iteration variables, use these operators: +FOR VAR(FOR% VAR (I.S. Operator) NIL NIL ("9") 9) [I.S. Operator] +Specifies the iteration variable (i.v.) that is used in conjunction with IN, ON, FROM, TO, and BY. The variable is rebound within the loop, so the value of the variable outside the loop is not affected. Example: +_(SETQ X 55) +55 +_(for X from 1 to 5 collect (TIMES X X)) +(1 4 9 16 25) +_X +55 +FOR OLD VAR(FOR% OLD% VAR (I.S. Operator) NIL NIL ("9") 10) [I.S. Operator] +Like FOR, but VAR is not rebound, so its value outside the loop is changed. Example: +_(SETQ X 55) +55 +_(for old X from 1 to 5 collect (TIMES X X)) +(1 4 9 16 25) +_X +6 +FOR VARS(FOR% VARS (I.S. Operator) NIL NIL ("9") 10) [I.S. Operator] +VARS a list of variables, e.g., (for (X Y Z) in ...). The first variable is the i.v., the rest are dummy variables. See BIND above. +IN FORM(IN% FORM (I.S. Operator) NIL NIL ("9") 10) [I.S. Operator] +FORM must evaluate to a list. The i.v. is set to successive elements of the list, one per iteration. For example, (for X in Y do ...) corresponds to (MAPC Y (FUNCTION (LAMBDA (X) ...))). If no i.v. has been specified, a dummy is supplied, e.g., (in Y collect CADR) is equivalent to (MAPCAR Y (FUNCTION CADR)). +ON FORM(ON% FORM (I.S. Operator) NIL NIL ("9") 10) [I.S. Operator] +Same as IN, but the i.v. is reset to the corresponding tail at each iteration. Thus IN corresponds to MAPC, MAPCAR, and MAPCONC, while ON corresponds to MAP, MAPLIST, and MAPCON. +_(for X on '(A B C) do (PRINT X)) +(A B C) +(B C) +(C) +NIL +Note: For both IN and ON, FORM is evaluated before the main part of the i.s. is entered, i.e. outside of the scope of any of the bound variables of the i.s. For example, (for X bind (Y_'(1 2 3)) in Y ...) will map down the list which is the value of Y evaluated outside of the i.s., not (1 2 3). +IN OLD VAR(IN% OLD% VAR (I.S. Operator) NIL NIL ("9") 10) [I.S. Operator] +Specifies that the i.s. is to iterate down VAR, with VAR itself being reset to the corresponding tail at each iteration, e.g., after (for X in old L do ... until ...) finishes, L will be some tail of its original value. +IN OLD (VAR_FORM)(IN% OLD% %(VAR_FORM%) (I.S. Operator) NIL NIL ("9") 10) [I.S. Operator] +Same as IN OLD VAR, except VAR is first set to value of FORM. +ON OLD VAR(ON% OLD% VAR (I.S. Operator) NIL NIL ("9") 10) [I.S. Operator] +Same as IN OLD VAR except the i.v. is reset to the current value of VAR at each iteration, instead of to (CAR VAR). +ON OLD (VAR_FORM)(ON% OLD% %(VAR_FORM%) (I.S. Operator) NIL NIL ("9") 11) [I.S. Operator] +Same as ON OLD VAR, except VAR is first set to value of FORM. +INSIDE FORM(INSIDE% FORM (I.S. Operator) NIL NIL ("9") 11) [I.S. Operator] +Like IN, but treats first non-list, non-NIL tail as the last element of the iteration, e.g., INSIDE '(A B C D . E) iterates five times with the i.v. set to E on the last iteration. INSIDE 'A is equivalent to INSIDE '(A), which will iterate once. +FROM FORM(FROM% FORM (I.S. Operator) NIL NIL ("9") 11) [I.S. Operator] +Specifies the initial value for a numerical i.v. The i.v. is automatically incremented by 1 after each iteration (unless BY is specified). If no i.v. has been specified, a dummy i.v. is supplied and initialized, e.g., (from 2 to 5 collect SQRT) returns (1.414 1.732 2.0 2.236). +TO FORM(TO% FORM (I.S. Operator) NIL NIL ("9") 11) [I.S. Operator] +Specifies the final value for a numerical i.v. If FROM is not specified, the i.v. is initialized to 1. If no i.v. has been specified, a dummy i.v. is supplied and initialized. If BY is not specified, the i.v. is automatically incremented by 1 after each iteration. When the i.v. is definitely being incremented, i.e., either BY is not specified, or its operand is a positive number, the i.s. terminates when the i.v. exceeds the value of FORM. Similarly, when the i.v. is definitely being decremented the i.s. terminates when the i.v. becomes less than the value of FORM (see description of BY). +FORM is evaluated only once, when the i.s. is first entered, and its value bound to a temporary variable against which the i.v. is checked each interation. If the user wishes to specify an i.s. in which the value of the boundary condition is recomputed each iteration, he should use WHILE or UNTIL instead of TO. +When both the operands to TO and FROM are numbers, and TO's operand is less than FROM's operand, the i.v. is decremented by 1 after each iteration. In this case, the i.s. terminates when the i.v. becomes less than the value of FORM. For example, (from 10 to 1 do PRINT) prints the numbers from 10 down to 1. +BY FORM (without IN or ON) (BY% FORM% %(WITHOUT% IN/ON%)% (I.S. Operator) BY% FORM% %(without% IN/ON%)% NIL ("9") 11) [I.S. Operator] +If you aren't using IN or ON, BY specifies how the i.v. itself is reset at each iteration. If you're using FROM or TO, the i.v. is known to be numerical, so the new i.v. is computed by adding the value of FORM (which is reevaluated each iteration) to the current value of the i.v., e.g., (for N from 1 to 10 by 2 collect N) makes a list of the first five odd numbers. +If FORM is a positive number (FORM itself, not its value, which in general CLISP would have no way of knowing in advance), the loop stops when the value of the i.v. exceeds the value of TO's operand. If FORM is a negative number, the loop stops when the value of the i.v. becomes less than TO's operand, e.g., (for I from N to M by -2 until (LESSP I M) ...). Otherwise, the terminating condition for each iteration depends on the value of FORM for that iteration: if FORM<0, the test is whether the i.v. is less than TO's operand, if FORM>0 the test is whether the i.v. exceeds TO's operand; if FORM = 0, the loop terminates unconditionally. +If you didn't use FROM or TO and FORM is not a number, the i.v. is simply reset to the value of FORM after each iteration, e.g., (for I from N by (FOO) ...) sets I to the value of (FOO) on each loop after the first. +BY FORM (with IN or ON) (BY% FORM% %(WITH% IN/ON%)% (I.S. Operator) BY% FORM% %(with% IN/ON%)% NIL ("9") 12) [I.S. Operator] +If you did use IN or ON, FORM's value determines the tail for the next iteration, which in turn determines the value for the i.v. as described earlier, i.e., the new i.v. is CAR of the tail for IN, the tail itself for ON. In conjunction with IN, you can refer to the current tail within FORM by using the i.v. or the operand for IN/ON, e.g., (for Z in L by (CDDR Z) ...) or (for Z in L by (CDDR L) ...). At translation time, the name of the internal variable which holds the value of the current tail is substituted for the i.v. throughout FORM. For example, (for X in Y by (CDR (MEMB 'FOO (CDR X))) collect X) specifies that after each iteration, CDR of the current tail is to be searched for the atom FOO, and (CDR of) this latter tail to be used for the next iteration. +AS VAR(AS% VAR (I.S. Operator) NIL NIL ("9") 12) [I.S. Operator] +Lets you have more than one i.v. for a single loop, e.g., (for X in Y as U in V do ...) moves through the lisps Y and V in parallel (see MAP2C). The loop ends when any of the terminating conditions is met, e.g., (for X in Y as I from 1 to 10 collect X) makes a list of the first ten elements of Y, or however many elements there are on Y if less than 10. +The operand to AS, VAR, specifies the new i.v. For the remainder of the i.s., or until another AS is encountered, all operators refer to the new i.v. For example, (for I from 1 to N1 as J from 1 to N2 by 2 as K from N3 to 1 by -1 ...) terminates when I exceeds N1, or J exceeds N2, or K becomes less than 1. After each iteration, I is incremented by 1, J by 2, and K by -1. +OUTOF FORM(OUTOF% FORM (I.S. Operator) NIL NIL ("9") 12) [I.S. Operator] +For use with generators. On each iteration, the i.v. is set to successive values returned by the generator. The loop ends when the generator runs out. +Condition I.S. Oprs +What if you want to do things only on certain times through the loop? You could make the loop body a big COND, but it's much more readable to use one of these: +WHEN FORM(WHEN% FORM (I.S. Operator) NIL NIL ("9") 12) [I.S. Operator] +Only run the loop body when FORM's value is non-NIL. For example, (for X in Y collect X when (NUMBERP X)) collects only the elements of Y that are numbers. +UNLESS FORM(UNLESS% FORM (I.S. Operator) NIL NIL ("9") 12) [I.S. Operator] +Opposite of WHEN: WHEN Z is the same as UNLESS (NOT Z). +WHILE FORM(WHILE% FORM (I.S. Operator) NIL NIL ("9") 13) [I.S. Operator] +WHILE FORM evaluates FORM before each iteration, and if the value is NIL, exits. +UNTIL FORM(UNTIL% FORM (I.S. Operator) NIL NIL ("9") 13) [I.S. Operator] +Opposite of WHILE: Evaluates FORM before each iteration, and if the value is not NIL, exits. +REPEATWHILE FORM(REPEATWHILE% FORM (I.S. Operator) NIL NIL ("9") 13) [I.S. Operator] +Same as WHILE except the test is performed after the loop body, but before the i.v. is reset for the next iteration. +REPEATUNTIL FORM(REPEATUNTIL% FORM (I.S. Operator) NIL NIL ("9") 13) [I.S. Operator] +Same as UNTIL, except the test is performed after the loop body. +Other I.S. Operators +FIRST FORM(FIRST% FORM (I.S. Operator) NIL NIL ("9") 13) [I.S. Operator] +FORM is evaluated once before the first iteration, e.g., (for X Y Z in L first (FOO Y Z) ...), and FOO could be used to initialize Y and Z. +FINALLY FORM(FINALLY% FORM (I.S. Operator) NIL NIL ("9") 13) [I.S. Operator] +FORM is evaluated after the loop terminates. For example, (for X in L bind Y0 do (if (ATOM X) then (SETQ Y (PLUS Y 1))) finally (RETURN Y)) will return the number of atoms in L. +EACHTIME FORM(EACHTIME% FORM (I.S. Operator) NIL NIL ("9") 13) [I.S. Operator] +FORM is evaluated at the beginning of each iteration before, and regardless of, any testing. For example, consider, +(for I from 1 to N +do (... (FOO I) ...) +unless (... (FOO I) ...) +until (... (FOO I) ...)) +You might want to set a temporary variable to the value of (FOO I) in order to avoid computing it three times each iteration. However, without knowing the translation, you can't know whether to put the assignment in the operand to DO, UNLESS, or UNTIL. You can avoid this problem by simply writing EACHTIME (SETQ J (FOO I)). +DECLARE: DECL(DECLARE:% DECL (I.S. Operator) NIL NIL ("9") 13) [I.S. Operator] +Inserts the form (DECLARE DECL) immediately following the PROG variable list in the translation, or, in the case that the translation is a mapping function rather than a PROG, immediately following the argument list of the lambda expression in the translation. This can be used to declare variables bound in the iterative statement to be compiled as local or special variables. For example (for X in Y declare: (LOCALVARS X) ...). Several DECLARE:s can apppear in the same i.s.; the declarations are inserted in the order they appear. +DECLARE DECL(DECLARE% DECL (I.S. Operator) NIL NIL ("9") 14) [I.S. Operator] +Same as DECLARE:. +Since DECLARE is also the name of a function, DECLARE cannot be used as an i.s. operator when it appears as CAR of a form, i.e. as the first i.s. operator in an iterative statement. However, declare (lowercase version) can be the first i.s. operator. +ORIGINAL I.S.OPR OPERAND(ORIGINAL% I.S.OPR% OPERAND (I.S. Operator) NIL NIL ("9") 14) [I.S. Operator] +I.S.OPR will be translated using its original, built-in interpretation, independent of any user defined i.s. operators. +There are also a number of i.s.oprs that make it easier to create iterative statements that use the clock, looping for a given period of time. See timers, Chapter 12. +Miscellaneous Hints For Using I.S.Oprs +Lowercase versions of all i.s. operators are equivalent to the uppercase, e.g., (for X in Y ...) is equivalent to (FOR X IN Y ...). +Each i.s. operator is of lower precedence than all Interlisp forms, so parentheses around the operands can be omitted, and will be supplied where necessary, e.g., BIND (X Y Z) can be written BIND X Y Z, OLD (XFORM) as OLD XFORM, etc. +RETURN or GO may be used in any operand. (In this case, the translation of the iterative statement will always be in the form of a PROG, never a mapping function.) RETURN means return from the loop (with the indicated value), not from the function in which the loop appears. GO refers to a label elsewhere in the function in which the loop. appears, except for the labels $$LP, $$ITERATE, and $$OUT which are reserved, as described below. +In the case of FIRST, FINALLY, EACHTIME, DECLARE: or one of the i.s.types, e.g., DO, COLLECT, SUM, etc., the operand can consist of more than one form, e.g., COLLECT (PRINT (CAR X)) (CDR X), in which case a PROGN is supplied. +Each operand can be the name of a function, in which case it is applied to the (last) i.v., e.g., (for X in Y do PRINT when NUMBERP) is the same as (for X in Y do (PRINT X) when (NUMBERP X)). Note that the i.v. need not be explicitly specified, e.g., (in Y do PRINT when NUMBERP) will work. +For i.s.types, e.g., DO, COLLECT, JOIN, the function is always applied to the first i.v. in the i.s., whether explicity named or not. For example, (in Y as I from 1 to 10 do PRINT) prints elements on Y, not integers between 1 and 10. +Note that this feature does not make much sense for FOR, OLD, BIND, IN, or ON, since they operate before the loop starts, when the i.v. may not even be bound. +In the case of BY in conjunction with IN, the function is applied to the current tail e.g., (for X in Y by CDDR ...) is the same as (for X in Y by (CDDR X) ...). +While the exact translation of a loop depends on which operators are present, a PROG will always be used whenever the loop specifies dummy variables%if BIND appears, or there is more than one variable specified by a FOR, or a GO, RETURN, or a reference to the variable $$VAL appears in any of the operands. When PROG is used, the form of the translation is: +(PROG VARIABLES +{initialize} +$$LP {eachtime} +{test} +{body} +$$ITERATE +{aftertest} +{update} +(GO $$LP) +$$OUT {finalize} +(RETURN $$VAL)) +where {test} corresponds to that part of the loop that tests for termination and also for those iterations for which {body} is not going to be executed, (as indicated by a WHEN or UNLESS); {body} corresponds to the operand of the i.s.type, e.g., DO, COLLECT, etc.; {aftertest} corresponds to those tests for termination specified by REPEATWHILE or REPEATUNTIL; and {update} corresponds to that part that resets the tail, increments the counter, etc. in preparation for the next iteration. {initialize}, {finalize}, and {eachtime} correspond to the operands of FIRST, FINALLY, and EACHTIME, if any. +Since {body} always appears at the top level of the PROG, you can insert labels in {body}, and GO to them from within {body} or from other i.s. operands, e.g., (for X in Y first (GO A) do (FOO) A (FIE)). However, since {body} is dwimified as a list of forms, the label(s) should be added to the dummy variables for the iterative statement in order to prevent their being dwimified and possibly corrected, e.g., (for X in Y bind A first (GO A) do (FOO) A (FIE)). You can also GO to $$LP, $$ITERATE, or $$OUT, or explicitly set $$VAL. +Errors in Iterative Statements +An error will be generated and an appropriate diagnostic printed if any of the following conditions hold: +1. Operator with null operand, i.e., two adjacent operators, as in (for X in Y until do ...) +2. Operand consisting of more than one form (except as operand to FIRST, FINALLY, or one of the i.s.types), e.g., (for X in Y (PRINT X) collect ...). +3. IN, ON, FROM, TO, or BY appear twice in same i.s. +4. Both IN and ON used on same i.v. +5. FROM or TO used with IN or ON on same i.v. +6. More than one i.s.type, e.g., a DO and a SUM. +In 3, 4, or 5, an error is not generated if an intervening AS occurs. +If an error occurs, the i.s. is left unchanged. +If no DO, COLLECT, JOIN or any of the other i.s.types are specified, CLISP will first attempt to find an operand consisting of more than one form, e.g., (for X in Y (PRINT X) when ATOM X ...), and in this case will insert a DO after the first form. (In this case, condition 2 is not considered to be met, and an error is not generated.) If CLISP cannot find such an operand, and no WHILE or UNTIL appears in the i.s., a warning message is printed: NO DO, COLLECT, OR JOIN: followed by the i.s. +Similarly, if no terminating condition is detected, i.e., no IN, ON, WHILE, UNTIL, TO, or a RETURN or GO, a warning message is printed: Possible non-terminating iterative statement: followed by the iterative statement. However, since the user may be planning to terminate the i.s. via an error, Control-E, or a RETFROM from a lower function, the i.s. is still translated. +Note: The error message is not printed if the value of CLISPI.S.GAG is T (initially NIL). +Defining New Iterative Statement Operators +The following function is available for defining new or redefining existing iterative statement operators: +(I.S.OPR NAME FORM OTHERS EVALFLG)(I.S.OPR% NAME% FORM% OTHERS% EVALFLG%) (Function) %(I.S.OPR% NAME% FORM% OTHERS% EVALFLG%) NIL ("9") 16) [Function] +NAME is the name of the new i.s.opr. If FORM is a list, NAME will be a new i.s.type, and FORM its body. +OTHERS is an (optional) list of additional i.s. operators and operands which will be added to the i.s. at the place where NAME appears. If FORM is NIL, NAME is a new i.s.opr defined entirely by OTHERS. +In both FORM and OTHERS, the atom $$VAL can be used to reference the value to be returned by the i.s., I.V. to reference the current i.v., and BODY to reference NAME's operand. In other words, the current i.v. will be substituted for all instances of I.V. and NAME's operand will be substituted for all instances of BODY throughout FORM and OTHERS. +If EVALFLG is T, FORM and OTHERS are evaluated at translation time, and their values used as described above. A dummy variable for use in translation that does not clash with a dummy variable already used by some other i.s. operators can be obtained by calling (GETDUMMYVAR). (GETDUMMYVAR T) will return a dummy variable and also insure that it is bound as a PROG variable in the translation. +If NAME was previously an i.s.opr and is being redefined, the message (NAME REDEFINED) will be printed (unless DFNFLG=T), and all expressions using the i.s.opr NAME that have been translated will have their translations discarded. +The following are some examples of how I.S.OPR could be called to define some existing i.s.oprs, and create some new ones: + COLLECT (I.S.OPR 'COLLECT + '(SETQ $$VAL (NCONC1 $$VAL BODY))) + SUM (I.S.OPR 'SUM + '(SETQ $$VAL(PLUS $$VAL BODY) + '(FIRST (SETQ $$VAL0)) + NEVER (I.S.OPR 'NEVER + '(if BODY then + (SETQ $$VAL NIL) (GO $$OUT)) + Note: (if BODY then (RETURN NIL)) would exit from the i.s. immediately and therefore not execute the operations specified via a FINALLY (if any). + THEREIS (I.S.OPR 'THEREIS + '(if BODY then + (SETQ $$VAL I.V.) (GO $$OUT))) + RCOLLECT To define RCOLLECT, a version of COLLECT which uses CONS instead of NCONC1 and then reverses the list of values: + (I.S.OPR 'RCOLLECT + '(FINALLY (RETURN + (DREVERSE $$VAL)))] + TCOLLECT To define TCOLLECT, a version of COLLECT which uses TCONC: + (I.S.OPR 'TCOLLECT + '(TCONC $$VAL BODY) + '(FIRST (SETQ $$VAL (CONS)) + FINALLY (RETURN + (CAR $$VAL)))] + PRODUCT (I.S.OPR 'PRODUCT + '(SETQ $$VAL $$VAL*BODY) + '(FIRST ($$VAL 1))] + UPTO To define UPTO, a version of TO whose operand is evaluated only once: + (I.S.OPR 'UPTO + NIL + '(BIND $$FOO_BODY TO $$FOO)] + TO To redefine TO so that instead of recomputing FORM each iteration, a variable is bound to the value of FORM, and then that variable is used: + (I.S.OPR 'TO + NIL + '(BIND $$END FIRST + (SETQ $$END BODY) + ORIGINALTO $$END)] + Note the use of ORIGINAL to redefine TO in terms of its original definition. ORIGINAL is intended for use in redefining built-in operators, since their definitions are not accessible, and hence not directly modifiable. Thus if the operator had been defined by the user via I.S.OPR, ORIGINAL would not obtain its original definition. In this case, one presumably would simply modify the i.s.opr definition. +I.S.OPR can also be used to define synonyms for already defined i.s. operators by calling I.S.OPR with FORM an atom, e.g., (I.S.OPR 'WHERE 'WHEN) makes WHERE be the same as WHEN. Similarly, following (I.S.OPR 'ISTHERE 'THEREIS), one can write (ISTHERE ATOM IN Y), and following (I.S.OPR 'FIND 'FOR) and (I.S.OPR 'SUCHTHAT 'THEREIS), one can write (find X in Y suchthat X member Z) . In the current system, WHERE is synonymous with WHEN, SUCHTHAT and ISTHERE with THEREIS, FIND with FOR, and THRU with TO. +If FORM is the atom MODIFIER, then NAME is defined as an i.s.opr which can immediately follow another i.s. operator (i.e., an error will not be generated, as described previously). NAME will not terminate the scope of the previous operator, and will be stripped off when DWIMIFY is called on its operand. OLD is an example of a MODIFIER type of operator. The MODIFIER feature allows the user to define i.s. operators similar to OLD, for use in conjunction with some other user defined i.s.opr which will produce the appropriate translation. +The file package command I.S.OPRS (Chapter 17) will dump the definition of i.s.oprs. (I.S.OPRS PRODUCT UPTO) as a file package command will print suitable expressions so that these iterative statement operators will be (re)defined when the file is loaded. + + +[This page intentionally left blank] +(SEQUENCE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "9-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "9-" "")) (222 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (ALTERNATE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "9-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "9-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "9-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "9-" "")) (222 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (48 612 564 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))))))))1$6$EVENT2$TE$ PAGEHEADING RIGHTPAGE17771HH506$T2 +T4$$1~~$1l~$1$1HH$1~~$1l~$1ll$1ll$1l~$1Zl$506$T1<<$1<<$5 +(T1~~$1l~$1HH$1HH$1l~$1$1r~$1ZZ$4HT$T4HT$T1$1$1$H$ PAGEHEADING RIGHTPAGEE$ PAGEHEADINGLEFTBACKT/MODERN +(CHARPROPS (COLOR . BLACK)).GACHA +(CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK))0CLASSIC +(CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK))0CLASSIC +(CHARPROPS (COLOR . BLACK))/MODERN +(CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK)).TITAN(CHARPROPS (COLOR . BLACK))/MODERN +(CHARPROPS (COLOR . BLACK)).TITAN(CHARPROPS (COLOR . BLACK))3 TIMESROMAN -PALATINO PALATINO PALATINO TITAN TITAN TITAN CLASSIC - HELVETICACLASSIC -MODERN -MODERN -MODERNMODERN -) #%" IM.CHAP.GETFNMODERN -# HRULE.GETFNMODERN"'!( HRULE.GETFNMODERN - c$IM.INDEX.GETFNTITAN  % 2#IM.INDEX.GETFNTITAN  %  ?!IM.INDEX.GETFNTITAN  %   #IM.INDEX.GETFNTITAN  %   $IM.INDEX.GETFNTITAN  %   !IM.INDEX.GETFNTITAN  % ) %  ,1"IM.INDEX.GETFNTITAN  %  & #IM.INDEX.GETFNTITAN  %   $IM.INDEX.GETFNTITAN  %    %    % )%$IM.INDEX.GETFNPALATINO  <%@L! HRULE.GETFN 'IM.INDEX.GETFNTITAN   - %   P - IM.INDEX.GETFNTITAN   - %   !IM.INDEX.GETFNTITAN  & IM.INDEX.GETFNTITAN  %   IM.INDEX.GETFNTITAN   - %    %$,%Y"IM.INDEX.GETFNTITAN   - %   ";     % "%  !%IM.INDEX.GETFNTITAN   - %^$%O! HRULE.GETFN   HIM.INDEX.GETFN%uW.    FIM.INDEX.GETFN%o,T 1     & ,[! HRULE.GETFN  -  -  - hIM.INDEX.GETFN%@       (       %B      .8   "   %2   %%     -%.9$>U! HRULE.GETFN  --IM.INDEX.GETFNK//(             #!>) $9% j@ZD! HRULE.GETFN  -  -  -  -  - %:      !        %    %  1&%  2     % 3 P = %,*%    %V =% -  -  -  -  -  - %A%%  <%  .!% HRULE.GETFN  -  -  - %N   -  -  - %R    -  -  - %XVI  -  -  -  - %I ', 9 %   : N 8%C& ' %" ) %"9! %I%7*+%-:%$'         %/         &         % O% % -! HRULE.GETFN ,;IM.INDEX.GETFNA" @U  & / 6 ]f ;A* -)IM.INDEX.GETFN   *IM.INDEX.GETFNCLASSIC -%    /IM.INDEX.GETFNCLASSIC -%  y%<G(   .IM.INDEX.GETFN% F(%  +IM.INDEX.GETFNCLASSIC -% U# P  -IM.INDEX.GETFNCLASSIC -%  6 - .IM.INDEX.GETFNCLASSIC -%    - -IM.INDEX.GETFNCLASSIC -%     - /IM.INDEX.GETFNCLASSIC -%.  %9   /IM.INDEX.GETFNCLASSIC -&  0IM.INDEX.GETFNCLASSIC -%J  9. ]$ +IM.INDEX.GETFNCLASSIC -& ,IM.INDEX.GETFNCLASSIC -%I%!    0 5  *IM.INDEX.GETFNCLASSIC -%Iu )  0IM.INDEX.GETFNCLASSIC -% ( -  ,IM.INDEX.GETFNCLASSIC -% F +IM.INDEX.GETFNMODERN -% o$=  +IM.INDEX.GETFNCLASSIC -%-"  @F".  /IM.INDEX.GETFNCLASSIC -%+  M! *  8IM.INDEX.GETFN%    /IM.INDEX.GETFNCLASSIC -% 2 "    8IM.INDEX.GETFN%    /IM.INDEX.GETFNCLASSIC -%!2*   -IM.INDEX.GETFNCLASSIC -%z`  +IM.INDEX.GETFNCLASSIC -%3w o f %  %x    \IM.INDEX.GETFN%LX O$,%   I(S  , & '% ;  VIM.INDEX.GETFN% u+ &   3&49 *IM.INDEX.GETFNCLASSIC -%:F(+(% IB   .IM.INDEX.GETFNCLASSIC -%  j3 -IM.INDEX.GETFNCLASSIC -%' /IM.INDEX.GETFNCLASSIC -%  .IM.INDEX.GETFNCLASSIC -%  %  .IM.INDEX.GETFNCLASSIC -%   %   4IM.INDEX.GETFNCLASSIC -%E  4IM.INDEX.GETFNCLASSIC -% .IM.INDEX.GETFNCLASSIC -% 5$ 0IM.INDEX.GETFNCLASSIC -% 6R$ 1IM.INDEX.GETFNCLASSIC -% q%<0 1IM.INDEX.GETFNCLASSIC -% l' X 0IM.INDEX.GETFNCLASSIC -%%!7Q  =IM.INDEX.GETFNCLASSIC -% r &P  -  x8/_ ) =b"*> n!!4T)P@<!&*   -   -i03 9  u  - - -($*1 jCB"" #; 0&!4= ,64  +k  -  -  - iIM.INDEX.GETFN$ %   $ t   & $  @% W 4   $    D$ ?  -) C$'M - & - "  -  ! _  -  #    ' -      -     -    ' -      5 "       'uS !   V=n5%" z \ No newline at end of file +(CHARPROPS (COLOR . BLACK))/MODERN(CHARPROPS (COLOR . BLACK))2 HELVETICA(CHARPROPS (COLOR . BLACK)))(%' IM.CHAP.GETFN# + HRULE.GETFN' +& +% HRULE.GETFNc +$IM.INDEX.GETFN2 +#IM.INDEX.GETFN? +!IM.INDEX.GETFN  +#IM.INDEX.GETFN  +$IM.INDEX.GETFN  +!IM.INDEX.GETFN) ,1 +"IM.INDEX.GETFN&  +#IM.INDEX.GETFN   +$IM.INDEX.GETFN  +  +)$IM.INDEX.GETFN < @L + HRULE.GETFN' +IM.INDEX.GETFN  P + + IM.INDEX.GETFN    +!IM.INDEX.GETFN IM.INDEX.GETFN  + IM.INDEX.GETFN %$,Y +"IM.INDEX.GETFN  $#"#;#  "! +%IM.INDEX.GETFN ^$ O + HRULE.GETFN +HIM.INDEX.GETFNuW.  +FIM.INDEX.GETFNo,T1 &,[ + HRULE.GETFN +   hIM.INDEX.GETFN@("B .8 " 2 % !    +.9$>U + HRULE.GETFN +-IM.INDEX.GETFNK//( #!>) $9%j@Z D + HRULE.GETFN +     :! %1&2 3P=,*    V= + +     A  <  .% + HRULE.GETFN +   N +   R +   XVI +    I',9%:N 8C& ' + +")%"9! + +I 7*+-:$' + / +   O % + + HRULE.GETFN,;IM.INDEX.GETFNA"@U &/6]f;A* + +)IM.INDEX.GETFN + +*IM.INDEX.GETFN  +/IM.INDEX.GETFN y<G( +.IM.INDEX.GETFNF(% ++IM.INDEX.GETFNU# P +-IM.INDEX.GETFN 6 + .IM.INDEX.GETFN  + -IM.INDEX.GETFN  + /IM.INDEX.GETFN.  9 +/IM.INDEX.GETFN0IM.INDEX.GETFNJ 9.  +]$ + ++IM.INDEX.GETFN +,IM.INDEX.GETFNI ! 05 +*IM.INDEX.GETFNIu ) +0IM.INDEX.GETFN( - +,IM.INDEX.GETFNF + ++IM.INDEX.GETFNo$= + ++IM.INDEX.GETFN-" @F".  + +/IM.INDEX.GETFN+M! * +8IM.INDEX.GETFN   + +/IM.INDEX.GETFN 2" +8IM.INDEX.GETFN   + +/IM.INDEX.GETFN!2*   + +-IM.INDEX.GETFNz`  + ++IM.INDEX.GETFN3w of x  + + \IM.INDEX.GETFNLXO$,I(S,&'; + +VIM.INDEX.GETFNu+& 3&49 + +*IM.INDEX.GETFN:F (+(IB         + +.IM.INDEX.GETFN  +j3 + +-IM.INDEX.GETFN' + +/IM.INDEX.GETFN  + +.IM.INDEX.GETFN  %  + +.IM.INDEX.GETFN  %  +  +4IM.INDEX.GETFNE +  +4IM.INDEX.GETFN  + + +.IM.INDEX.GETFN5$ + +0IM.INDEX.GETFN6 R$ + +1IM.INDEX.GETFNq<0 + +1IM.INDEX.GETFNl' X + +0IM.INDEX.GETFN!7Q + =IM.INDEX.GETFNr & +P  +x8/_ ) = b"*> n!!4T)P@<!&*     +   + i0 3 9  u  + + +($*1   +j C B""     #; 0&!4  = ,6 4   + +k +   iIM.INDEX.GETFN %   t&  @%W4   D ? +)C 'M&"! _ #    '    '   5" 'uS! V=n5%(((CHARENCODING . MCCS)))PROPS:#DATE:jd" z \ No newline at end of file diff --git a/docs/medley-irm/10-FUNC-DEF.TEDIT b/docs/medley-irm/10-FUNC-DEF.TEDIT index cc1d1a55..cd39b9a2 100644 --- a/docs/medley-irm/10-FUNC-DEF.TEDIT +++ b/docs/medley-irm/10-FUNC-DEF.TEDIT @@ -9,15 +9,15 @@ A function's definition specifies if the function has a fixed or variable number (LAMBDA (X Y) (PRINT X) (PRINT Y)) This function has two evaluated arguments, X and Y, and it will execute (PRINT X) and (PRINT Y) when evaluated. Other types of function definitions are described below. A function is defined by putting an expr definition in the function definition cell of a symbol. There are a number of functions for accessing and setting function definition cells, but one usually defines a function with DEFINEQ (see the Defining Functions section below). For example: - (DEFINEQ (FOO (LAMBDA (X Y) (PRINT X) (PRINT Y))))(FOO) +_ (DEFINEQ (FOO (LAMBDA (X Y) (PRINT X) (PRINT Y))))(FOO) The expression above will define the function FOO to have the expr definition (LAMBDA (X Y) (PRINT X) (PRINT Y)). After being defined, this function may be evaluated just like any system function: - (FOO 3 (IPLUS 3 4)) +_ (FOO 3 (IPLUS 3 4)) 3 7 7 Not all function definition cells contain expr definitions. The compiler (see the first page of Chapter 18) translates expr definitions into compiled code objects, which execute much faster. Interlisp provides a number of function type functions which determine how a given function is defined, the number and names of function arguments, etc. See the Function Type Functions section below. Usually, functions are evaluated automatically when they appear within another function or when typed into Interlisp. However, sometimes it is useful to envoke the Interlisp interpreter explicitly to apply a given functional argument to some data. There are a number of functions which will apply a given function repeatedly. For example, MAPCAR will apply a function (or an expr definition) to all of the elements of a list, and return the values returned by the function: - (MAPCAR '(1 2 3 4 5) '(LAMBDA (X) (ITIMES X X)) +_ (MAPCAR '(1 2 3 4 5) '(LAMBDA (X) (ITIMES X X)) (1 4 9 16 25) When using functional arguments, there are a number of problems which can arise, related to accessing free variables from within a function argument. Many times these problems can be solved using the function FUNCTION to create a FUNARG object. The macro facility provides another way of specifying the behavior of a function (see the Macros section below). Macros are very useful when developing code which should run very quickly, which should be compiled differently than when it is interpreted, or which should run differently in different implementations of Interlisp. @@ -34,9 +34,9 @@ Lambda-Spread Functions(LAMBDA-SPREAD% FUNCTIONS NIL Lambda-Spread% Functions NI Lambda-spread functions take a fixed number of evaluated arguments. This is the most common function type. A lambda-spread expr definition has the form: (LAMBDA (ARG1 ... ARGM) FORM1 ... FORMN) The argument list (ARG1 ... ARGM) is a list of symbols that gives the number and names of the formal arguments to the function. If the argument list is ( ) or NIL, this indicates that the function takes no arguments. When a lambda-spread function is applied to some arguments, the arguments are evaluated, and bound to the local variables ARG1 ... ARGM. Then, FORM1 ... FORMN are evaluated in order, and the value of the function is the value of FORMN. - (DEFINEQ (FOO (LAMBDA (X Y) (LIST X Y)))) +_ (DEFINEQ (FOO (LAMBDA (X Y) (LIST X Y)))) (FOO) - (FOO 99 (PLUS 3 4)) +_ (FOO 99 (PLUS 3 4)) (99 7) In the above example, the function FOO defined by (LAMBDA (X Y) (LIST X Y)) is applied to the arguments 99 and (PLUS 3 4). These arguments are evaluated (giving 99 and 7), the local variable X is bound to 99 and Y to 7, (LIST X Y) is evaluated, returning (99 7), and this is returned as the value of the function. A standard feature of the Interlisp system is that no error occurs if a spread function is called with too many or too few arguments. If a function is called with too many argumnents, the extra arguments are evaluated but ignored. If a function is called with too few arguments, the unsupplied ones will be delivered as NIL. In fact, a spread function cannot distinguish between being given NIL as an argument, and not being given that argument, e.g., (FOO) and (FOO NIL) are exactly the same for spread functions. If it is necessary to distinguish between these two cases, use an nlambda function and explicitly evaluate the arguments with the EVAL function. @@ -44,9 +44,9 @@ Nlambda-Spread Functions(NLAMBDA-SPREAD% FUNCTIONS NIL Nlambda-Spread% Functions Nlambda-spread functions take a fixed number of unevaluated arguments. An nlambda-spread expr definition has the form: (NLAMBDA (ARG1 ... ARGM) FORM1 ... FORMN) Nlambda-spread functions are evaluated similarly to lanbda-spread functions, except that the arguments are not evaluated before being bound to the variables ARG1 ... ARGM. - (DEFINEQ (FOO (NLAMBDA (X Y) (LIST X Y)))) +_ (DEFINEQ (FOO (NLAMBDA (X Y) (LIST X Y)))) (FOO) - (FOO 99 (PLUS 3 4)) +_ (FOO 99 (PLUS 3 4)) (99 (PLUS 3 4)) In the above example, the function FOO defined by (NLAMBDA (X Y) (LIST X Y)) is applied to the arguments 99 and (PLUS 3 4). These arguments are unevaluated to X and Y. (LIST X Y) is evaluated, returning (99 (PLUS 3 4)), and this is returned as the value of the function. Functions can be defined so that all of their arguments are evaluated (lambda functions) or none are evaluated (nlambda functions). If it is desirable to write a function which only evaluates some of its arguments (e.g., SETQ), the functions should be defined as an nlambda, with some arguments explicitly evaluated using the function EVAL. If this is done, the user should put the symbol EVAL on the property list of the function under the property INFO. This informs various system packages, such as DWIM, CLISP, and Masterscope, that this function in fact does evaluate its arguments, even though it is an nlambda. @@ -61,23 +61,23 @@ Returns the Mth argument for the lambda-nospread function whose argument list is (SETARG(SETARG (Function) NIL NIL ("10") 4) VAR M X) [NLambda Function] Sets the Mth argument for the lambda-nospread function whose argument list is VAR to X. VAR is not evaluated; M and X are evaluated. M should be between 1 and the value of VAR. In the example below, the function FOO is defined to collect and return a list of all of the evaluated arguments it is given (the value of the for statement). - (DEFINEQ (FOO +_ (DEFINEQ (FOO (LAMBDA X (for ARGNUM from 1 to X collect (ARG X ARGNUM)] (FOO) - (FOO 99 (PLUS 3 4)) +_ (FOO 99 (PLUS 3 4)) (99 7) - (FOO 99 (PLUS 3 4)(TIMES 3 4))) +_ (FOO 99 (PLUS 3 4)(TIMES 3 4))) (99 7 12) NLambda-Nospread Functions(NLAMBDA-NOSPREAD% FUNCTIONS NIL NLambda-Nospread% Functions NIL ("10") 4) Nlambda-nospread functions take a variable number of unevaluated arguments. An nlambda-nospread expr definition has the form: (NLAMBDA VAR FORM1 ... FORMN) VAR may be any symbol, except NIL and T. Though similar in form to lambda-nospread expr definitions, an nlambda-nospread is evaluated quite differently. When an nlambda-nospread function is applied to some arguments, VAR is simply bound to a list of the unevaluated arguments. The user may pick apart this list, and evaluate different arguments. In the example below, FOO is defined to return the reverse of the list of arguments it is given (unevaluated): - (DEFINEQ (FOO (NLAMBDA X (REVERSE X)))) +_ (DEFINEQ (FOO (NLAMBDA X (REVERSE X)))) (FOO) - (FOO 99 (PLUS 3 4)) +_ (FOO 99 (PLUS 3 4)) ((PLUS 3 4) 99) - (FOO 99 (PLUS 3 4)(TIMES 3 4)) +_ (FOO 99 (PLUS 3 4)(TIMES 3 4)) (TIMES 3 4)(PLUS 3 4) 99) The warning about evaluating arguments to nlambda functions also applies to nlambda-nospread function. Compiled Functions(COMPILED% FUNCTIONS NIL Compiled% Functions NIL ("10") 5) @@ -158,23 +158,23 @@ Usually, function application is done automatically by the Interlisp interpreter There are some situations where it is necessary to explicitly call the evaluator, and Interlisp supplies a number of functions that will do this. These functions take functional arguments, which may either be symbols with function definitions, or expr definition forms such as (LAMBDA (X)...), or FUNARG expressions. (APPLY(APPLY (Function) NIL NIL ("10") 9) FN ARGLIST %) [Function] Applies the function FN to the arguments in the list ARGLIST, and returns its value. APPLY is a lambda function, so its arguments are evaluated, but the individual elements of ARGLIST are not evaluated. Therefore, lambda and nlambda functions are treated the same by APPLY%lambda functions take their arguments from ARGLIST without evaluating them. For example: - (APPLY 'APPEND '((PLUS 1 2 3)(4 5 6))) +_ (APPLY 'APPEND '((PLUS 1 2 3)(4 5 6))) (PLUS 1 2 3 4 5 6) Note that FN may explicitly evaluate one or more of its arguments itself. For example, the system function SETQ is an nlambda function that explicitly evaluates its second argument. Therefore, (APPLY 'SETQ '(FOO (ADD1 3)))will set FOO to 4, instead of setting it to the expression (ADD1 3). APPLY can be used for manipulating expr definitions. For example: - (APPLY '(LAMBDA (X Y)(ITIMES X Y)) '(3 4))) +_ (APPLY '(LAMBDA (X Y)(ITIMES X Y)) '(3 4))) 12 (APPLY*(APPLY* (Function) NIL NIL ("10") 9) FN ARG1 ARG2 ... ARGN ) [NoSpread Function] Nospread version of APPLY. Applies the function FN to the arguments ARG1 ARG2 ... ARGN. For example: - (APPLY 'APPEND '(PLUS 1 2 3)(4 5 6)) +_ (APPLY 'APPEND '(PLUS 1 2 3)(4 5 6)) (PLUS 1 2 3 4 5 6) (EVAL(EVAL (Function) NIL NIL ("10") 10) X%) [Function] EVAL evaluates the expression X and returns this value, i.e., EVAL provides a way of calling the Interlisp interpreter. Note that EVAL is itself a lambda function, so its argument is first evaluated, e.g.: - (SETQ FOO 'ADD1 3))) +_ (SETQ FOO 'ADD1 3))) (ADD1 3) -(EVAL FOO) +_(EVAL FOO) 4 -(EVAL 'FOO) +_(EVAL 'FOO) (ADD1 3) (QUOTE(QUOTE (Function) NIL NIL ("10") 10) X) [Nlambda NoSpread Function] QUOTE prevents its arguments from being evaluated. Its value is X itself, e.g., (QUOTE FOO) is FOO. @@ -198,12 +198,12 @@ COMPILETYPELST (see Chapter 18) permits the user to specify how a datum of a par (EVALHOOK(EVALHOOK (Function) NIL NIL ("10") 11) FORM EVALHOOKFN) [Function] EVALHOOK evaluates the expression FORM, and returns its value. While evaluating FORM, the function EVAL behaves in a special way. Whenever a list other than FORM itself is to be evaluated, whether implicitly or via an explicit call to EVAL, EVALHOOKFN is invoked (it should be a function), with the form to be evaluated as its argument. EVALHOOKFN is then responsible for evaluating the form. Whatever is returned is assume to be the result of evaluating the form. During the execution of EVALHOOKFN, this special evaluation is turned off. (Note that EVALHOOK does not affect the evaluations of variables, only of lists). Here is an example of a simple tracing routine that uses the EVALHOOK feature: -(DEFINEQ (PRINTHOOK (FORM) +_(DEFINEQ (PRINTHOOK (FORM) (printout T "eval: "FORM T) (EVALHOOK FORM (FUNCTION PRINTHOOK (PRINTHOOK) Using PRINTHOOK, one might see the following interaction: -(EVALHOOK '(LIST (CONS 1 2)(CONS 3 4)) 'PRINTHOOK) +_(EVALHOOK '(LIST (CONS 1 2)(CONS 3 4)) 'PRINTHOOK) eval: (CONS 1 2) eval: (CONS 3 4) ((1.2)(3.4)) @@ -381,21 +381,21 @@ Note: Because of the way that the evaluator processes macros, if you have a mac (SEQUENCE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "10-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "10-" "")) (222 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (ALTERNATE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "10-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "10-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (102 612 510 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "10-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "10-" "")) (222 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (48 612 564 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))))))))1$16$EVENT2$TE$ PAGEHEADING RIGHTPAGE1$2 T7777771HH1l~$506$T2 -T1HH$1HH1$1$1xx$2l~$T1Zl$1l~$1~~$7~71HH1<N2<NT1<<$1<<1NN2<<T1NN$1<<$1$2$TF$ PAGEHEADING RIGHTPAGETE$ PAGEHEADINGLEFTBACKTPALATINO GACHA -PALATINO TITANMODERN -MODERN -CLASSIC -TITAN TITAN PALATINO CLASSIC -CLASSIC -CLASSIC -TITANPALATINO TITANPALATINO CLASSIC - +T1HH$1HH1$1$1xx$2l~$T1Zl$1l~$1~~$7~71HH1<N2<NT1<<$1<<1NN2<<T1NN$1<<$1$2$TF$ PAGEHEADING RIGHTPAGETE$ PAGEHEADINGLEFTBACKT/MODERN +(CHARPROPS (COLOR . BLACK)).GACHA +(CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK)).TITAN(CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK))/MODERN +(CHARPROPS (COLOR . BLACK))0CLASSIC +(CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK))0CLASSIC +(CHARPROPS (COLOR . BLACK))0CLASSIC +(CHARPROPS (COLOR . BLACK))0CLASSIC +(CHARPROPS (COLOR . BLACK)).TITAN(CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK)).TITAN(CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK))0CLASSIC +(CHARPROPS (COLOR . BLACK))3 TIMESROMAN -MODERN HELVETICA)(1' IM.CHAP.GETFN5 HRULE.GETFN'&%#+  +(CHARPROPS (COLOR . BLACK))2 HELVETICA(CHARPROPS (COLOR . BLACK)))(1' IM.CHAP.GETFN5 HRULE.GETFN'&%#+  J;%:."V%$$$X%2$  H HRULE.GETFN|  M 0 _ 8 P a?: (YW DIM.INDEX.GETFN! w  F#,"#"#  )  5BE:  FIM.INDEX.GETFNx! !-!#  -& 5n39V_HIM.INDEX.GETFNz  V  [ !IM.INDEX.GETFN  D_H6 $IM.INDEX.GETFN  D&#y <" +& 5n39V_HIM.INDEX.GETFNz  V  [ !IM.INDEX.GETFN  D_H6 $IM.INDEX.GETFN  D&#y <" JIM.INDEX.GETFN  V+!i:IM.INDEX.GETFN #IM.INDEX.GETFN  MU"$$ $ $AQ8* #IM.INDEX.GETFN   < $IM.INDEX.GETFN    %IM.INDEX.GETFN  / 5  #IM.INDEX.GETFN  #& %IM.INDEX.GETFN   L N^" = 4( *IM.INDEX.GETFN  5 c      @@ -406,9 +406,9 @@ TIMESROMAN  HRULE.GETFN@ %IM.INDEX.GETFN   3,.#6  &IM.INDEX.GETFN  %<: 3 2IM.INDEX.GETFN 2 7 $IM.INDEX.GETFN 4  -  #3( GF   +  #3( GF   vT -  "IM.INDEX.GETFN  # ( . $IM.INDEX.GETFN  ' - $IM.INDEX.GETFN  3(  +  "IM.INDEX.GETFN  # ( . $IM.INDEX.GETFN  ' - $IM.INDEX.GETFN  3(   %IM.INDEX.GETFN & HRULE.GETFN{A #IM.INDEX.GETFN VU[P, `S *>/ $IM.INDEX.GETFN      @@ -439,7 +439,7 @@ TIMESROMAN {k +.   1"  *) -) +) 3 9  @@ -447,7 +447,7 @@ TIMESROMAN V P n, -8 .0 > .?  '  ,IM.INDEX.GETFN  k/ &R! 'IM.INDEX.GETFN 3< :MB d,()\ S ~ +8 .0 > .?  '  ,IM.INDEX.GETFN  k/ &R! 'IM.INDEX.GETFN 3< :MB d,()\ S ~    > m @@ -455,7 +455,7 @@ TIMESROMAN $ <# |   7#~^M        6  -;      ! J +;      ! J M<  T @@ -463,7 +463,7 @@ TIMESROMAN ) Jc6 %   [ ( ( - -0 %   +0 %   +   -K 'M  - R   %DATE:i5p۷ z \ No newline at end of file +K 'M  + R   %(((CHARENCODING . MCCS)))PROPS:#DATE:j۷ z \ No newline at end of file diff --git a/docs/medley-irm/11-VAR-BINDINGS.TEDIT b/docs/medley-irm/11-VAR-BINDINGS.TEDIT index f3d913eb..feaecf67 100644 --- a/docs/medley-irm/11-VAR-BINDINGS.TEDIT +++ b/docs/medley-irm/11-VAR-BINDINGS.TEDIT @@ -1,142 +1,406 @@ -INTERLISP-D REFERENCE MANUAL VARIABLE BINDINGS AND THE STACK "11"11. VARIABLE BINDINGS AND THE STACK 3 Medley uses deep binding. Every time a function is entered, a basic frame containing the new variables is put on top of the stack. Therefore, any variable reference requires searching the stack for the first instance of that variable, which makes free variable use somewhat more expensive than in a shallow binding scheme. On the other hand, spaghetti stack operations are considerably faster. Some other tricks involving copying freely-referenced variables to higher frames on the stack are also used to speed up the search. The basic frames are allocated on a stack; for most user purposes, these frames should be thought of as containing the variable names associated with the function call, and the current values for that frame. The descriptions of the stack functions in below are presented from this viewpoint. Both interpreted and compiled functions store both the names and values of variables so that interpreted and compiled functions are compatible and can be freely intermixed, i.e., free variables can be used with no SPECVAR declarations necessary. However, it is possible to suppress storing of names in compiled functions, either for efficiency or to avoid a clash, via a LOCALVAR declaration (see the Local Variables and Special Variables section of Chapter 18). The names are also very useful in debugging, for they make possible a complete symbolic backtrace in case of error. In addition to the binding information, additional information is associated with each function call: access information indicating the path to search the basic frames for variable bindings, control information, and temporary results are also stored on the stack in a block called the frame extension. The interpreter also stores information about partially evaluated expressions as described in the Stack and Interpreter section of Chapter 11. Spaghetti Stack 1 The Bobrow/Wegbreit paper, A Model and Stack Implementation for Multiple Environments (Communications of the ACM, Vol. 16, 10, October 1973.), describes an access and control mechanism more general than a simple linear stack. The access and control mechanism used by Interlisp is a slightly modified version of the one proposed by Bobrow and Wegbreit. This mechanism is called the spaghetti stack. The spaghetti system presents the access and control stack as a data structure composed of frames. The functions described below operate on this structure. These primitives allow user functions to manipulate the stack in a machine independent way. Backtracking, coroutines, and more sophisticated control schemes can be easily implemented with these primitives. The evaluation of a function requires the allocation of storage to hold the values of its local variables during the computation. In addition to variable bindings, an activation of a function requires a return link (indicating where control is to go after the completion of the computation) and room for temporaries needed during the computation. In the spaghetti system, one stack is used for storing all this information, but it is best to view this stack as a tree of linked objects called frame extensions (or simply frames). A frame extension is a variable sized block of storage containing a frame name, a pointer to some variable bindings (the BLINK), and two pointers to other frame extensions (the ALINK and CLINK). In addition to these components, a frame extension contains other information (such as temporaries and reference counts) that does not interest us here. The block of storage holding the variable bindings is called a basic frame. A basic frame is essentially an array of pairs, each of which contains a variable name and its value. The reason frame extensions point to basic frames (rather than just having them built in) is so that two frame extensions can share a common basic frame. This allows two processes to communicate via shared variable bindings. The chain of frame extensions which can be reached via the successive ALINKs from a given frame is called the access chain of the frame. The first frame in the access chain is the starting frame. The chain through successive CLINKs is called the control chain. A frame extension completely specifies the variable bindings and control information necessary for the evaluation of a function. Whenever a function (or in fact, any form which generally binds local variables) is evaluated, it is associated with some frame extension. In the beginning there is precisely one frame extension in existence. This is the frame in which the top-level call to the interpreter is being run. This frame is called the top-level frame. Since precisely one function is being executed at any instant, exactly one frame is distinguished as having the control bubble in it. This frame is called the active frame. Initially, the top-level frame is the active frame. If the computation in the active frame invokes another function, a new basic frame and frame extension are built. The frame name of this basic frame will be the name of the function being called. The ALINK, BLINK, and CLINK of the new frame all depend on precisely how the function is invoked. The new function is then run in this new frame by passing control to that frame, i.e., it is made the active frame. Once the active computation has been completed, control normally returns to the frame pointed to by the CLINK of the active frame. That is, the frame in the CLINK becomes the active frame. In most cases, the storage associated with the basic frame and frame extension just abandoned can be reclaimed. However, it is possible to obtain a pointer to a frame extension and to hold on to this frame even after it has been exited. This pointer can be used later to run another computation in that environment, or even continue the exited computation. A separate data type, called a stack pointer, is used for this purpose. A stack pointer is just a cell that literally points to a frame extension. Stack pointers print as #ADR/FRAMENAME, e.g., #1,13636/COND. Stack pointers are returned by many of the stack manipulating functions described below. Except for certain abbreviations (such as the frame with such-and-such a name), stack pointers are the only way you can reference a frame extension. As long as you have a stack pointer which references a frame extension, that frame extension (and all those that can be reached from it) will not be garbage collected. Two stack pointers referencing the same frame extension are not necessarily EQ, i.e., (EQ (STKPOS 'FOO) (STKPOS 'FOO)) = NIL. However, EQP can be used to test if two different stack pointers reference the same frame extension (see the Equality Predicates section of Chapter 9). It is possible to evaluate a form with respect to an access chain other than the current one by using a stack pointer to refer to the head of the access chain desired. Note, however, that this can be very expensive when using a shallow binding scheme such as that in Interlisp-10. When evaluating the form, since all references to variables under the shallow binding scheme go through the variable's value cell, the values in the value cells must be adjusted to reflect the values appropriate to the desired access chain. This is done by changing all the bindings on the current access chain (all the name-value pairs) so that they contain the value current at the time of the call. Then along the new access path, all bindings are made to contain the previous value of the variable, and the current value is placed in the value cell. For that part of the access path which is shared by the old and new chain, no work has to be done. The context switching time, i.e. the overhead in switching from the current, active, access chain to another one, is directly proportional to the size of the two branches that are not shared between the access contexts. This cost should be remembered in using generators and coroutines (see the Generators section below). Stack Functions 1 In the descriptions of the stack functions below, when we refer to an argument as a stack descriptor, we mean that it is one of the following: A stack pointer An object that points to a frame on the stack. Stack pointers are returned by many of the stack manipulating functions described below. NIL Specifies the active frame; that is, the frame of the stack function itself. T Specifies the top-level frame. A symbol Specifies the first frame (along the control chain from the active frame) that has the frame name LITATOM. Equivalent to (STKPOS LITATOM -1). A list of symbols Specifies the first frame (along the control chain from the active frame) whose frame name is included in the list. A number N Specifies the Nth frame back from the active frame. If N is negative, the control chain is followed, otherwise the access chain is followed. Equivalent to (STKNTH N). In the stack functions described below, the following errors can occur: The error Illegal stack arg occurs when a stack descriptor is expected and the supplied argument is either not a legal stack descriptor (i.e., not a stack pointer, symbol, or number), or is a symbol or number for which there is no corresponding stack frame, e.g., (STKNTH -1 'FOO) where there is no frame named FOO in the active control chain or (STKNTH -10 'EVALQT). The error Stack pointer has been released occurs whenever a released stack pointer is supplied as a stack descriptor argument for any purpose other than as a stack pointer to re-use. Note: The creation of a single stack pointer can result in the retention of a large amount of stack space. Therefore, one should try to release stack pointers when they are no longer needed (see the Releasing and Reusing Stack Pointers section below). In Lisp there is a fixed ammount of space allocated for the stack. When most of this space is exhausted, the STACK OVERFLOW error occurs and the debugger will be invoked. You will still have a little room on the stack to use inside the debugger. If you use up this last little bit of stack you will encounter a hard stack overflow. A hard stack overflow will put you into URaid (see the documentation on URaid). Searching the Stack 1 (STKPOS(STKPOS (Function) NIL NIL ("11") 4) FRAMENAME N POS OLDPOS) [Function] Returns a stack pointer to the Nth frame with frame name FRAMENAME. The search begins with (and includes) the frame specified by the stack descriptor POS. The search proceeds along the control chain from POS if N is negative, or along the access chain if N is positive. If N is NIL, -1 is used. Returns a stack pointer to the frame if such a frame exists, otherwise returns NIL. If OLDPOS is supplied and is a stack pointer, it is reused. If OLDPOS is supplied and is a stack pointer and STKPOS returns NIL, OLDPOS is released. If OLDPOS is not a stack pointer it is ignored. (STKNTH(STKNTH (Function) NIL NIL ("11") 4) N POS OLDPOS) [Function] Returns a stack pointer to the Nth frame back from the frame specified by the stack descriptor POS. If N is negative, the control chain from POS is followed. If N is positive the access chain is followed. If N equals 0, STKNTH returns a stack pointer to POS (this provides a way to copy a stack pointer). Returns NIL if there are fewer than N frames in the appropriate chain. If OLDPOS is supplied and is a stack pointer, it is reused. If OLDPOS is not a stack pointer it is ignored. Note: (STKNTH 0) causes an error, Illegal stack arg; it is not possible to create a stack pointer to the active frame. (STKNAME(STKNAME (Function) NIL NIL ("11") 4) POS) [Function] Returns the frame name of the frame specified by the stack descriptor POS. (SETSTKNAME(SETSTKNAME (Function) NIL NIL ("11") 4) POS NAME) [Function] Changes the frame name of the frame specified by POS to be NAME. Returns NAME. (STKNTHNAME(STKNTHNAME (Function) NIL NIL ("11") 4) N POS) [Function] Returns the frame name of the Nth frame back from POS. Equivalent to (STKNAME (STKNTH N POS)) but avoids creation of a stack pointer. In summary, STKPOS converts function names to stack pointers, STKNTH converts numbers to stack pointers, STKNAME converts stack pointers to function names, and STKNTHNAME converts numbers to function names. Variable Bindings in Stack Frames 1 The following functions are used for accessing and changing bindings. Some of functions take an argument, N, which specifies a particular binding in the basic frame. If N is a literal atom, it is assumed to be the name of a variable bound in the basic frame. If N is a number, it is assumed to reference the Nth binding in the basic frame. The first binding is 1. If the basic frame contains no binding with the given name or if the number is too large or too small, the error Illegal arg occurs. (STKSCAN(STKSCAN (Function) NIL NIL ("11") 5) VAR IPOS OPOS) [Function] Searches beginning at IPOS for a frame in which a variable named VAR is bound. The search follows the access chain. Returns a stack pointer to the frame if found, otherwise returns NIL. If OPOS is a stack pointer it is reused, otherwise it is ignored. (FRAMESCAN(FRAMESCAN (Function) NIL NIL ("11") 5) ATOM POS) [Function] Returns the relative position of the binding of ATOM in the basic frame of POS. Returns NIL if ATOM is not found. (STKARG(STKARG (Function) NIL NIL ("11") 5) N POS %) [Function] Returns the value of the binding specified by N in the basic frame of the frame specified by the stack descriptor POS. N can be a literal atom or number. (STKARGNAME(STKARGNAME (Function) NIL NIL ("11") 5) N POS) [Function] Returns the name of the binding specified by N, in the basic frame of the frame specified by the stack descriptor POS. N can be a literal atom or number. (SETSTKARG(SETSTKARG (Function) NIL NIL ("11") 5) N POS VAL) [Function] Sets the value of the binding specified by N in the basic frame of the frame specified by the stack descriptor POS. N can be a literal atom or a number. Returns VAL. (SETSTKARGNAME(SETSTKARGNAME (Function) NIL NIL ("11") 5) N POS NAME) [Function] Sets the variable name to NAME of the binding specified by N in the basic frame of the frame specified by the stack descriptor POS. N can be a literal atom or a number. Returns NAME. This function does not work for interpreted frames. (STKNARGS(STKNARGS (Function) NIL NIL ("11") 5) POS %) [Function] Returns the number of arguments bound in the basic frame of the frame specified by the stack descriptor POS. (VARIABLES(VARIABLES (Function) NIL NIL ("11") 5) POS) [Function] Returns a list of the variables bound at POS. (STKARGS(STKARGS (Function) NIL NIL ("11") 5) POS %) [Function] Returns a list of the values of the variables bound at POS. Evaluating Expressions in Stack Frames 1 The following functions are used to evaluate an expression in a different environment: (ENVEVAL(ENVEVAL (Function) NIL NIL ("11") 5) FORM APOS CPOS AFLG CFLG) [Function] Evaluates FORM in the environment specified by APOS and CPOS. That is, a new active frame is created with the frame specified by the stack descriptor APOS as its ALINK, and the frame specified by the stack descriptor CPOS as its CLINK. Then FORM is evaluated. If AFLG is not NIL, and APOS is a stack pointer, then APOS will be released. Similarly, if CFLG is not NIL, and CPOS is a stack pointer, then CPOS will be released. (ENVAPPLY(ENVAPPLY (Function) NIL NIL ("11") 6) FN ARGS APOS CPOS AFLG CFLG) [Function] APPLYs FN to ARGS in the environment specified by APOS and CPOS. AFLG and CFLG have the same interpretation as with ENVEVAL. (EVALV(EVALV (Function) NIL NIL ("11") 6) VAR POS RELFLG) [Function] Evaluates VAR, where VAR is assumed to be a symbol, in the access environment specifed by the stack descriptor POS. If VAR is unbound, EVALV returns NOBIND and does not generate an error. If RELFLG is non-NIL and POS is a stack pointer, it will be released after the variable is looked up. While EVALV could be defined as (ENVEVAL VAR POS NIL RELFLG) it is in fact somewhat faster. (STKEVAL(STKEVAL (Function) NIL NIL ("11") 6) POS FORM FLG %) [Function] Evaluates FORM in the access environment of the frame specified by the stack descriptor POS. If FLG is not NIL and POS is a stack pointer, releases POS. The definition of STKEVAL is (ENVEVAL FORM POS NIL FLG). (STKAPPLY(STKAPPLY (Function) NIL NIL ("11") 6) POS FN ARGS FLG) [Function] Like STKEVAL but applies FN to ARGS. Altering Flow of Control 1 The following functions are used to alter the normal flow of control, possibly jumping to a different frame on the stack. RETEVAL and RETAPPLY allow evaluating an expression in the specified environment first. (RETFROM(RETFROM (Function) NIL NIL ("11") 6) POS VAL FLG) [Function] Return from the frame specified by the stack descriptor POS, with the value VAL. If FLG is not NIL, and POS is a stack pointer, then POS is released. An attempt to RETFROM the top level (e.g., (RETFROM T)) causes an error, Illegal stack arg. RETFROM can be written in terms of ENVEVAL as follows: (RETFROM (LAMBDA (POS VAL FLG) (ENVEVAL (LIST 'QUOTE VAL) NIL (if (STKNTH -1 POS (if FLG then POS)) else (ERRORX (LIST 19 POS))) NIL T))) (RETTO(RETTO (Function) NIL NIL ("11") 6) POS VAL FLG) [Function] Like RETFROM, but returns to the frame specified by POS. (RETEVAL(RETEVAL (Function) NIL NIL ("11") 7) POS FORM FLG %) [Function] Evaluates FORM in the access environment of the frame specified by the stack descriptor POS, and then returns from POS with that value. If FLG is not NIL and POS is a stack pointer, then POS is released. The definition of RETEVAL is equivalent to (ENVEVAL FORM POS (STKNTH -1 POS) FLG T), but RETEVAL does not create a stack pointer. (RETAPPLY(RETAPPLY (Function) NIL NIL ("11") 7) POS FN ARGS FLG) [Function] Like RETEVAL but applies FN to ARGS. Releasing and Reusing Stack Pointers 1 The following functions and variables are used for manipulating stack pointers: (STACKP(STACKP (Function) NIL NIL ("11") 7) X) [Function] Returns X if X is a stack pointer, otherwise returns NIL. (RELSTK(RELSTK (Function) NIL NIL ("11") 7) POS) [Function] Release the stack pointer POS (see below). If POS is not a stack pointer, does nothing. Returns POS. (RELSTKP(RELSTKP (Function) NIL NIL ("11") 7) X) [Function] Returns T is X is a released stack pointer, NIL otherwise. (CLEARSTK(CLEARSTK (Function) NIL NIL ("11") 7) FLG) [Function] If FLG is T, returns a list of all the active (unreleased) stack pointers. If FLG is NIL, this call is a no-op. The abillity to clear all stack pointers is inconsistent with the modularity implicit in a multi processing environment. CLEARSTKLST(CLEARSTKLST (Variable) NIL NIL ("11") 7) [Variable] A variable used by the top-level executive. Every time the top-level executive is re-entered (e.g., following errors, or Control-D), CLEARSTKLST is checked. If its value is T, all active stack pointers are released using CLEARSTK. If its value is a list, then all stack pointers on that list are released. If its value is NIL, nothing is released. CLEARSTKLST is initially T. NOCLEARSTKLST(NOCLEARSTKLST (Variable) NIL NIL ("11") 7) [Variable] A variable used by the top-level executive. If CLEARSTKLST is T (see above) all active stack pointers except those on NOCLEARSTKLST are released. NOCLEARSTKLST is initially NIL. Creating a single stack pointer can cause the retention of a large amount of stack space. Furthermore, this space will not be freed until the next garbage collection, even if the stack pointer is no longer being used, unless the stack pointer is explicitly released or reused. If there is sufficient amount of stack space tied up in this fashion, a STACK OVERFLOW condition can occur, even in the simplest of computations. For this reason, you should consider releasing a stack pointer when the environment referenced by the stack pointer is no longer needed. The effects of releasing a stack pointer are: 1. The link between the stack pointer and the stack is broken by setting the contents of the stack pointer to the released mark. A released stack pointer prints as #ADR/#0. 2. If this stack pointer was the last remaining reference to a frame extension; that is, if no other stack pointer references the frame extension and the extension is not contained in the active control or access chain, then the extension may be reclaimed, and is reclaimed immediately. The process repeats for the access and control chains of the reclaimed extension so that all stack space that was reachable only from the released stack pointer is reclaimed. A stack pointer may be released using the function RELSTK, but there are some cases for which RELSTK is not sufficient. For example, if a function contains a call to RETFROM in which a stack pointer was used to specify where to return to, it would not be possible to simultaneously release the stack pointer. (A RELSTK appearing in the function following the call to RETFROM would not be executed!) To permit release of a stack pointer in this situation, the stack functions that relinquish control have optional flag arguments to denote whether or not a stack pointer is to be released (AFLG and CFLG). Note that in this case releasing the stack pointer will not cause the stack space to be reclaimed immediately because the frame referenced by the stack pointer will have become part of the active environment. Another way to avoid creating new stack pointers is to reuse stack pointers that are no longer needed. The stack functions that create stack pointers (STKPOS, STKNTH, and STKSCAN) have an optional argument that is a stack pointer to reuse. When a stack pointer is reused, two things happen. First the stack pointer is released (see above). Then the pointer to the new frame extension is deposited in the stack pointer. The old stack pointer (with its new contents) is returned as the value of the function. Note that the reused stack pointer will be released even if the function does not find the specified frame. Even if stack pointers are explicitly being released, creating many stack pointers can cause a garbage collection of stack pointer space. Thus, if your application requires creating many stack pointers, you definitely should take advantage of reusing stack pointers. Backtrace Functions 1 The following functions perform a backtrace, printing information about every frame on the stack. Arguments allow only backtracing a selected range of the stack, skipping selected frames, and printing different amounts of information about each frame. (BACKTRACE(BACKTRACE (Function) NIL NIL ("11") 8) IPOS EPOS FLAGS FILE PRINTFN) [Function] Performs a backtrace beginning at the frame specified by the stack descriptor IPOS, and ending with the frame specified by the stack descriptor EPOS. FLAGS is a number in which the options of the BACKTRACE are encoded. If a bit is set, the corresponding information is included in the backtrace. 1Q - print arguments of non-SUBRs 2Q - print temporaries of the interpreter 4Q - print SUBR arguments and local variables 10Q - omit printing of UNTRACE: and function names 20Q - follow access chain instead of control chain 40Q - print temporaries, i.e. the blips (see the stack and interpreter section below) For example: If FLAGS = 47Q, everything is printed. If FLAGS = 21Q, follows the access chain, prints arguments. FILE is the file that the backtrace is printed to. FILE must be open. PRINTFN is used when printing the values of variables, temporaries, blips, etc. PRINTFN = NIL defaults to PRINT. (BAKTRACE(BAKTRACE (Function) NIL NIL ("11") 9) IPOS EPOS SKIPFNS FLAGS FILE) [Function] Prints a backtrace from IPOS to EPOS onto FILE. FLAGS specifies the options of the backtrace, e.g., do/don't print arguments, do/don't print temporaries of the interpreter, etc., and is the same as for BACKTRACE. SKIPFNS is a list of functions. As BAKTRACE scans down the stack, the stack name of each frame is passed to each function in SKIPFNS, and if any of them returnS non-NIL, POS is skipped (including all variables). BAKTRACE collapses the sequence of several function calls corresponding to a call to a system package into a single function using BAKTRACELST as described below. For example, any call to the editor is printed as **EDITOR**, a break is printed as **BREAK**, etc. BAKTRACE is used by the BT, BTV, BTV+, BTV*, and BTV! break commands, with FLAGS = 0, 1, 5, 7, and 47Q respectively. If SYSPRETTYFLG = T, the values arguments and local variables will be prettyprinted. BAKTRACELST(BAKTRACELST (Variable) NIL NIL ("11") 9) [Variable] Used to tell BAKTRACE (therefore, the BT, BTV, etc. commands) to abbreviate various sequences of function calls on the stack by a single key, e.g. **BREAK**, **EDITOR**, etc. Each entry on BAKTRACELST is a list of the form (FRAMENAME KEY . PATTERN) or (FRAMENAME (KEY1 . PATTERN1) ... (KEYN . PATTERNN)), where a pattern is a list of elements that are either atoms, which match a single frame, or lists, which are interpreted as a list of alternative patterns, e.g. (PROGN **BREAK** EVAL ((ERRORSET BREAK1A BREAK1) (BREAK1))) BAKTRACE operates by scanning up the stack and, at each point, comparing the current frame name, with the frame names on BAKTRACELST, i.e. it does an ASSOC. If the frame name does appear, BAKTRACE attempts to match the stack as of that point with (one of) the patterns. If the match is successful, BAKTRACE prints the corresponding key, and continues with where the match left off. If the frame name does not appear, or the match fails, BAKTRACE simply prints the frame name and continues with the next higher frame (unless the SKIPFNS applied to the frame name are non-NIL as described above). Matching is performed by comparing symbols in the pattern with the current frame name, and matching lists as patterns, i.e. sequences of function calls, always working up the stack. For example, either of the sequence of function calls ... BREAK1 BREAK1A ERRORSET EVAL PROGN ... or ... BREAK1 EVAL PROGN ... would match with the sample entry given above, causing **BREAK** to be printed. Special features: f The symbol & can be used to match any frame. f The pattern - can be used to match nothing. - is useful for specifying an optional match, e.g. the example above could also have been written as (PROGN **BREAK** EVAL ((ERRORSET BREAK1A) -) BREAK1). f It is not necessary to provide in the pattern for matching dummy frames, i.e. frames for which DUMMYFRAMEP (see below) is true. When working on a match, the matcher automatically skips over these frames when they do not match. f If a match succeeds and the KEY is NIL, nothing is printed. For example, (*PROG*LAM NIL EVALA *ENV). This sequence will occur following an error which then causes a break if some of the function's arguments are LOCALVARS. Other Stack Functions (DUMMYFRAMEP(DUMMYFRAMEP (Function) NIL NIL ("11") 10) POS) [Function] Returns T if you never wrote a call to the function at POS, e.g. in Interlisp-10, DUMMYFRAMEP is T for *PROG*LAM, *ENV*, and FOOBLOCK frames (see the Block Compiling section of Chapter 18). REALFRAMEP and REALSTKNTH can be used to write functions which manipulate the stack and work on either interpreted or compiled code: (REALFRAMEP(REALFRAMEP (Function) NIL NIL ("11") 10) POS INTERPFLG) [Function] Returns POS if POS is a real frame, i.e. if POS is not a dummy frame and POS is a frame that does not disappear when compiled (such as COND); otherwise NIL. If INTERPFLG = T, returns T if POS is not a dummy frame. For example, if (STKNAME POS) = COND, (REALFRAMEP POS) is NIL, but (REALFRAMEP POS T) is T. (REALSTKNTH(REALFRAMEP (Function) NIL NIL ("11") 10) N POS INTERPFLG OLDPOS) [Function] Returns a stack pointer to the Nth (or -Nth) frames for which (REALFRAMEP POS INTERPFLG) is POS. (MAPDL(REALFRAMEP (Function) NIL NIL ("11") 10) MAPDLFN MAPDLPOS) [Function] Starts at MAPDLPOS and applies the function MAPDLFN to two arguments (the frame name and a stack pointer to the frame), for each frame until the top of the stack is reached. Returns NIL. For example, [MAPDL (FUNCTION (LAMBDA (X POS) (if (IGREATERP (STKNARGS POS) 2) then (PRINT X)] will print all functions of more than two arguments. (SEARCHPDL(SEARCHPDL (Function) NIL NIL ("11") 11) SRCHFN SRCHPOS) [Function] Like MAPDL, but searches the stack starting at position SRCHPOS until it finds a frame for which SRCHFN, a function of two arguments applied to the name of the frame and the frame itself, is not NIL. Returns (NAME . FRAME) if such a frame is found, otherwise NIL. The Stack and the Interpreter 1 In addition to the names and values of arguments for functions, information regarding partially-evaluated expressions is kept on the push-down list. For example, consider the following definition of the function FACT (intentionally faulty): (FACT [LAMBDA (N) (COND ((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N]) In evaluating the form (FACT 1), as soon as FACT is entered, the interpreter begins evaluating the implicit PROGN following the LAMBDA. The first function entered in this process is COND. COND begins to process its list of clauses. After calling ZEROP and getting a NIL value, COND proceeds to the next clause and evaluates T. Since T is true, the evaluation of the implicit PROGN that is the consequent of the T clause is begun. This requires calling the function ITIMES. However before ITIMES can be called, its arguments must be evaluated. The first argument is evaluated by retrieving the current binding of N from its value cell; the second involves a recursive call to FACT, and another implicit PROGN, etc. At each stage of this process, some portion of an expression has been evaluated, and another is awaiting evaluation. The output below (from Interlisp-10) illustrates this by showing the state of the push-down list at the point in the computation of (FACT 1) when the unbound atom L is reached. FACT(1) u.b.a. L {in FACT} in ((ZEROP NO L) (L broken) :BTV! *TAIL* (L) *ARG1 (((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N))))) COND *FORM* (COND ((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N))))) *TAIL* ((COND ((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N)))))) N 0 FACT *FORM* (FACT (SUB1 N)) *FN* ITIMES *TAIL* ((FACT (SUB1 N))) *ARGVAL* 1 *FORM* (ITIMES N (FACT (SUB1 N))) *TAIL* ((ITIMES N (FACT (SUB1 N)))) *ARG1 (((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N))))) COND *FORM* (COND ((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N))))) *TAIL* ((COND ((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N)))))) N 1 FACT **TOP** Internal calls to EVAL, e.g., from COND and the interpreter, are marked on the push-down list by a special mark or blip which the backtrace prints as *FORM*. The genealogy of *FORM*'s is thus a history of the computation. Other temporary information stored on the stack by the interpreter includes the tail of a partially evaluated implicit PROGN (e.g., a cond clause or lambda expression) and the tail of a partially evaluated form (i.e., those arguments not yet evaluated), both indicated on the backtrace by *TAIL*, the values of arguments that have already been evaluated, indicated by *ARGVAL*, and the names of functions waiting to be called, indicated by *FN*. *ARG1, ..., *ARGn are used by the backtrace to indicate the (unnamed) arguments to SUBRs. Note that a function is not actually entered and does not appear on the stack, until its arguments have been evaluated (except for nlambda functions, of course). Also note that the *ARG1, *FORM*, *TAIL*, etc. bindings comprise the actual working storage. In other words, in the above example, if a (lower) function changed the value of the *ARG1 binding, the COND would continue interpreting the new binding as a list of COND clauses. Similarly, if the *ARGVAL* binding were changed, the new value would be given to ITIMES as its first argument after its second argument had been evaluated, and ITIMES was actually called. *FORM*, *TAIL*, *ARGVAL*, etc., do not actually appear as variables on the stack, i.e., evaluating *FORM* or calling STKSCAN to search for it will not work. However, the functions BLIPVAL, SETBLIPVAL, and BLIPSCAN described below are available for accessing these internal blips. These functions currently know about four different types of blips: *FN* The name of a function about to be called *ARGVAL* An argument for a function about to be called *FORM* A form in the process of evaluation *TAIL* The tail of a COND clause, implicit PROGN, PROG, etc. (BLIPVAL(BLIPVAL (Function) NIL NIL ("11") 12) BLIPTYP IPOS FLG) [Function] Returns the value of the specified blip of type BLIPTYP. If FLG is a number N, finds the Nth blip of the desired type, searching the control chain beginning at the frame specified by the stack descriptor IPOS. If FLG is NIL, 1 is used. If FLG is T, returns the number of blips of the specified type at IPOS. (SETBLIPVAL(SETBLIPVAL (Function) NIL NIL ("11") 12) BLIPTYP IPOS N VAL) [Function] Sets the value of the specified blip of type BLIPTYP. Searches for the Nth blip of the desired type, beginning with the frame specified by the stack descriptor IPOS, and following the control chain. (BLIPSCAN(BLIPSCAN (Function) NIL NIL ("11") 13) BLIPTYP IPOS) [Function] Returns a stack pointer to the frame in which a blip of type BLIPTYP is located. Search begins at the frame specified by the stack descriptor IPOS and follows the control chain. Generators 1 A generator is like a subroutine except that it retains information about previous times it has been called. Some of this state may be data (for example, the seed in a random number generator), and some may be in program state (as in a recursive generator which finds all the atoms in a list structure). For example, if LISTGEN is defined by: (DEFINEQ (LISTGEN (L) (if L then (PRODUCE (CAR L)) (LISTGEN (CDR L)))) we can use the function GENERATOR (described below) to create a generator that uses LISTGEN to produce the elements of a list one at a time, e.g., (SETQ GR (GENERATOR (LISTGEN '(A B C)))) creates a generator, which can be called by (GENERATE GR) to produce as values on successive calls, A, B, C. When GENERATE (not GENERATOR) is called the first time, it simply starts evaluating (LISTGEN '(A B C)). PRODUCE gets called from LISTGEN, and pops back up to GENERATE with the indicated value after saving the state. When GENERATE gets called again, it continues from where the last PRODUCE left off. This process continues until finally LISTGEN completes and returns a value (it doesn't matter what it is). GENERATE then returns GR itself as its value, so that the program that called GENERATE can tell that it is finished, i.e., there are no more values to be generated. (GENERATOR(GENERATOR (Function) NIL NIL ("11") 13) FORM COMVAR) [NLambda Function] An nlambda function that creates a generator which uses FORM to compute values. GENERATOR returns a generator handle which is represented by a dotted pair of stack pointers. COMVAR is optional. If its value (EVAL of) is a generator handle, the list structure and stack pointers will be reused. Otherwise, a new generator handle will be constructed. GENERATOR compiles open. (PRODUCE(PRODUCE (Function) NIL NIL ("11") 13) VAL) [Function] Used from within a generator to return VAL as the value of the corresponding call to GENERATE. (GENERATE(GENERATE (Function) NIL NIL ("11") 13) HANDLE VAL) [Function] Restarts the generator represented by HANDLE. VAL is returned as the value of the PRODUCE which last suspended the operation of the generator. When the generator runs out of values, GENERATE returns HANDLE itself. Examples: The following function will go down recursively through a list structure and produce the atoms in the list structure one at a time. (DEFINEQ (LEAVESG (L) (if (ATOM L) then (PRODUCE L) else (LEAVESG (CAR L)) (if (CDR L) then (LEAVESG (CDR L)] The following function prints each of these atoms as it appears. It illustrates how a loop can be set up to use a generator. (DEFINEQ (PLEAVESG1 (L) (PROG (X LHANDLE) (SETQ LHANDLE (GENERATOR (LEAVESG L))) LP (SETQ X (GENERATE LHANDLE)) (if (EQ X LHANDLE) then (RETURN NIL)) (PRINT X) (GO LP))] The loop terminates when the value of the generator is EQ to the dotted pair which is the value produced by the call to GENERATOR. A CLISP iterative operator, OUTOF, is provided which makes it much easier to write the loop in PLEAVESG1. OUTOF (or outof) can precede a form which is to be used as a generator. On each iteration, the iteration variable will be set to successive values returned by the generator; the loop will be terminated automatically when the generator runs out. Therefore, the following is equivalent to the above program PLEAVESG1: (DEFINEQ (PLEAVESG2 (L) (for X outof (LEAVESG L) do (PRINT X))] Here is another example; the following form will print the first N atoms. (for X outof (MAPATOMS (FUNCTION PRODUCE)) as I from 1 to N do (PRINT X)) Coroutines 1 This package provides facilities for the creation and use of fully general coroutine structures. It uses a stack pointer to preserve the state of a coroutine, and allows arbitrary switching between N different coroutines, rather than just a call to a generator and return. This package is slightly more efficient than the generator package described above, and allows more flexibility on specification of what to do when a coroutine terminates. (COROUTINE(COROUTINE (Function) NIL NIL ("11") 14) CALLPTR COROUTPTR COROUTFORM ENDFORM) [NLambda Function] This nlambda function is used to create a coroutine and initialize the linkage. CALLPTR and COROUTPTR are the names of two variables, which will be set to appropriate stack pointers. If the values of CALLPTR or COROUTPTR are already stack pointers, the stack pointers will be reused. COROUTFORM is the form which is evaluated to start the coroutine; ENDFORM is a form to be evaluated if COROUTFORM actually returns when it runs out of values. COROUTINE compiles open. (RESUME(RESUME (Function) NIL NIL ("11") 15) FROMPTR TOPTR VAL) [Function] Used to transfer control from one coroutine to another. FROMPTR should be the stack pointer for the current coroutine, which will be smashed to preserve the current state. TOPTR should be the stack pointer which has preserved the state of the coroutine to be transferred to, and VAL is the value that is to be returned to the latter coroutine as the value of the RESUME which suspended the operation of that coroutine. For example, the following is the way one might write the LEAVES program using the coroutine package: (DEFINEQ (LEAVESC (L COROUTPTR CALLPTR) (if (ATOM L) then (RESUME COROUTPTR CALLPTR L) else (LEAVESC (CAR L) COROUTPTR CALLPTR) (if (CDR L) then (LEAVESC (CDR L) COROUTPTR CALLPTR))))] A function PLEAVESC which uses LEAVESC can be defined as follows: (DEFINEQ (PLEAVESC (L) (bind PLHANDLE LHANDLE first (COROUTINE PLHANDLE LHANDLE (LEAVESC L LHANDLE PLHANDLE) (RETFROM 'PLEAVESC)) do (PRINT (RESUME PLHANDLE LHANDLE))))] By RESUMEing LEAVESC repeatedly, this function will print all the leaves of list L and then return out of PLEAVESC via the RETFROM. The RETFROM is necessary to break out of the non-terminating do-loop. This was done to illustrate the additional flexibility allowed through the use of ENDFORM. We use two coroutines working on two trees in the example EQLEAVES, defined below. EQLEAVES tests to see whether two trees have the same leaf set in the same order, e.g., (EQLEAVES '(A B C) '(A B (C))) is true. (DEFINEQ (EQLEAVES (L1 L2) (bind LHANDLE1 LHANDLE2 PE EL1 EL2 first (COROUTINE PE LHANDLE1 (LEAVESC L1 LHANDLE1 PE) 'NO-MORE) (COROUTINE PE LHANDLE2 (LEAVESC L2 LHANDLE2 PE) 'NO-MORE) do (SETQ EL1 (RESUME PE LHANDLE1)) (SETQ EL2 (RESUME PE LHANDLE2)) (if (NEQ EL1 EL2) then (RETURN NIL)) repeatuntil (EQ EL1 'NO-MORE) finally (RETURN T)))] Possibilities Lists 1 A possibilities list is the interface between a generator and a consumer. The possibilities list is initialized by a call to POSSIBILITIES, and elements are obtained from it by using TRYNEXT. By using the spaghetti stack to maintain separate environments, this package allows a regime in which a generator can put a few items in a possibilities list, suspend itself until they have been consumed, and be subsequently aroused and generate some more. (POSSIBILITIES FORM) [NLambda Function] This nlambda function is used for the initial creation of a possibilities list. FORM will be evaluated to create the list. It should use the functions NOTE and AU-REVOIR described below to generate possibilities. Normally, one would set some variable to the possibilities list which is returned, so it can be used later, e.g.: (SETQ PLIST (POSSIBILITIES (GENERFN V1 V2))). POSSIBILITIES compiles open. (NOTE(NOTE (Function) NIL NIL ("11") 16) VAL LSTFLG) [Function] Used within a generator to put items on the possibilities list being generated. If LSTFLG is equal to NIL, VAL is treated as a single item. If LSTFLG is non-NIL, then the list VAL is NCONCed on the end of the possibilities list. Note that it is perfectly reasonable to create a possibilities list using a second generator, and NOTE that list as possibilities for the current generator with LSTFLG equal to T. The lower generator will be resumed at the appropriate point. (AU-REVOIR(AU-REVOIR (Function) NIL NIL ("11") 16) VAL) [NoSpread Function] Puts VAL on the possibilities list if it is given, and then suspends the generator and returns to the consumer in such a fashion that control will return to the generator at the AU-REVOIR if the consumer exhausts the possibilities list. NIL is not put on the possibilities list unless it is explicitly given as an argument to AU-REVOIR, i.e., (AU-REVOIR) and (AU-REVOIR NIL) are not the same. AU-REVOIR and ADIEU are lambda nospreads to enable them to distinguish these two cases. (ADIEU(ADIEU (Function) NIL NIL ("11") 16) VAL) [NoSpread Function] Like AU-REVOIR but releases the generator instead of suspending it. (TRYNEXT(TRYNEXT (Function) NIL NIL ("11") 16) PLST ENDFORM VAL) [NLambda Function] This nlambda function allows a consumer to use a possibilities list. It removes the first item from the possibilities list named by PLST (i.e. PLST must be an atom whose value is a possiblities list), and returns that item, provided it is not a generator handle. If a generator handle is encountered, the generator is reawakened. When it returns a possibilities list, this list is added to the front of the current list. When a call to TRYNEXT causes a generator to be awakened, VAL is returned as the value of the AU-REVOIR which put that generator to sleep. If PLST is empty, it evaluates ENDFORM in the caller's environment. TRYNEXT compiles open. (CLEANPOSLST(CLEANPOSLST (Function) NIL NIL ("11") 16) PLST) [Function] This function is provided to release any stack pointers which may be left in the PLST which was not used to exhaustion. For example, FIB is a generator for fibonnaci numbers. It starts out by NOTEing its two arguments, then suspends itself. Thereafter, on being re-awakened, it will NOTE two more terms in the series and suspends again. PRINTFIB uses FIB to print the first N fibonacci numbers. (DEFINEQ (FIB (F1 F2) (do (NOTE F1) (NOTE F2) (SETQ F1 (IPLUS F1 F2)) (SETQ F2 (IPLUS F1 F2)) (AU-REVOIR)] Note that this AU-REVOIR just suspends the generator and adds nothing to the possibilities list except the generator. (DEFINEQ (PRINTFIB (N) (PROG ((FL (POSSIBILITIES (FIB 0 1)))) (RPTQ N (PRINT (TRYNEXT FL))) (CLEANPOSLST FL)] Note that FIB itself will never terminate. [This page intentionally left blank] (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "11-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "11-" "")) (222 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "11-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "11-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (102 612 510 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "11-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "11-" "")) (222 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (48 612 564 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))))),**,0052$$5~x~,<<,6,,002$$2HfH,~~,l~30BT0 -T0 -T1EVENT-T@ PAGEHEADING RIGHTPAGE/,/A PAGEHEADING RIGHTPAGET,ZZ/HH,<<,HH,@ PAGEHEADINGLEFTBACKT/TITAN +INTERLISP-D REFERENCE MANUAL +VARIABLE BINDINGS AND THE STACK +"11"11. VARIABLE BINDINGS AND THE STACK +3 + + +Medley uses deep binding. Every time a function is entered, a basic frame containing the new variables is put on top of the stack. Therefore, any variable reference requires searching the stack for the first instance of that variable, which makes free variable use somewhat more expensive than in a shallow binding scheme. On the other hand, spaghetti stack operations are considerably faster. Some other tricks involving copying freely-referenced variables to higher frames on the stack are also used to speed up the search. +The basic frames are allocated on a stack; for most user purposes, these frames should be thought of as containing the variable names associated with the function call, and the current values for that frame. The descriptions of the stack functions in below are presented from this viewpoint. Both interpreted and compiled functions store both the names and values of variables so that interpreted and compiled functions are compatible and can be freely intermixed, i.e., free variables can be used with no SPECVAR declarations necessary. However, it is possible to suppress storing of names in compiled functions, either for efficiency or to avoid a clash, via a LOCALVAR declaration (see the Local Variables and Special Variables section of Chapter 18). The names are also very useful in debugging, for they make possible a complete symbolic backtrace in case of error. +In addition to the binding information, additional information is associated with each function call: access information indicating the path to search the basic frames for variable bindings, control information, and temporary results are also stored on the stack in a block called the frame extension. The interpreter also stores information about partially evaluated expressions as described in the Stack and Interpreter section of Chapter 11. +Spaghetti Stack +1 + +The Bobrow/Wegbreit paper, A Model and Stack Implementation for Multiple Environments (Communications of the ACM, Vol. 16, 10, October 1973.), describes an access and control mechanism more general than a simple linear stack. The access and control mechanism used by Interlisp is a slightly modified version of the one proposed by Bobrow and Wegbreit. This mechanism is called the spaghetti stack. +The spaghetti system presents the access and control stack as a data structure composed of frames. The functions described below operate on this structure. These primitives allow user functions to manipulate the stack in a machine independent way. Backtracking, coroutines, and more sophisticated control schemes can be easily implemented with these primitives. +The evaluation of a function requires the allocation of storage to hold the values of its local variables during the computation. In addition to variable bindings, an activation of a function requires a return link (indicating where control is to go after the completion of the computation) and room for temporaries needed during the computation. In the spaghetti system, one stack is used for storing all this information, but it is best to view this stack as a tree of linked objects called frame extensions (or simply frames). +A frame extension is a variable sized block of storage containing a frame name, a pointer to some variable bindings (the BLINK), and two pointers to other frame extensions (the ALINK and CLINK). In addition to these components, a frame extension contains other information (such as temporaries and reference counts) that does not interest us here. +The block of storage holding the variable bindings is called a basic frame. A basic frame is essentially an array of pairs, each of which contains a variable name and its value. The reason frame extensions point to basic frames (rather than just having them built in) is so that two frame extensions can share a common basic frame. This allows two processes to communicate via shared variable bindings. +The chain of frame extensions which can be reached via the successive ALINKs from a given frame is called the access chain of the frame. The first frame in the access chain is the starting frame. The chain through successive CLINKs is called the control chain. +A frame extension completely specifies the variable bindings and control information necessary for the evaluation of a function. Whenever a function (or in fact, any form which generally binds local variables) is evaluated, it is associated with some frame extension. +In the beginning there is precisely one frame extension in existence. This is the frame in which the top-level call to the interpreter is being run. This frame is called the top-level frame. +Since precisely one function is being executed at any instant, exactly one frame is distinguished as having the control bubble in it. This frame is called the active frame. Initially, the top-level frame is the active frame. If the computation in the active frame invokes another function, a new basic frame and frame extension are built. The frame name of this basic frame will be the name of the function being called. The ALINK, BLINK, and CLINK of the new frame all depend on precisely how the function is invoked. The new function is then run in this new frame by passing control to that frame, i.e., it is made the active frame. +Once the active computation has been completed, control normally returns to the frame pointed to by the CLINK of the active frame. That is, the frame in the CLINK becomes the active frame. +In most cases, the storage associated with the basic frame and frame extension just abandoned can be reclaimed. However, it is possible to obtain a pointer to a frame extension and to hold on to this frame even after it has been exited. This pointer can be used later to run another computation in that environment, or even continue the exited computation. +A separate data type, called a stack pointer, is used for this purpose. A stack pointer is just a cell that literally points to a frame extension. Stack pointers print as #ADR/FRAMENAME, e.g., #1,13636/COND. Stack pointers are returned by many of the stack manipulating functions described below. Except for certain abbreviations (such as the frame with such-and-such a name), stack pointers are the only way you can reference a frame extension. As long as you have a stack pointer which references a frame extension, that frame extension (and all those that can be reached from it) will not be garbage collected. +Two stack pointers referencing the same frame extension are not necessarily EQ, i.e., (EQ (STKPOS 'FOO) (STKPOS 'FOO)) = NIL. However, EQP can be used to test if two different stack pointers reference the same frame extension (see the Equality Predicates section of Chapter 9). +It is possible to evaluate a form with respect to an access chain other than the current one by using a stack pointer to refer to the head of the access chain desired. Note, however, that this can be very expensive when using a shallow binding scheme such as that in Interlisp-10. When evaluating the form, since all references to variables under the shallow binding scheme go through the variable's value cell, the values in the value cells must be adjusted to reflect the values appropriate to the desired access chain. This is done by changing all the bindings on the current access chain (all the name-value pairs) so that they contain the value current at the time of the call. Then along the new access path, all bindings are made to contain the previous value of the variable, and the current value is placed in the value cell. For that part of the access path which is shared by the old and new chain, no work has to be done. The context switching time, i.e. the overhead in switching from the current, active, access chain to another one, is directly proportional to the size of the two branches that are not shared between the access contexts. This cost should be remembered in using generators and coroutines (see the Generators section below). +Stack Functions +1 + +In the descriptions of the stack functions below, when we refer to an argument as a stack descriptor, we mean that it is one of the following: + A stack pointer An object that points to a frame on the stack. Stack pointers are returned by many of the stack manipulating functions described below. + NIL Specifies the active frame; that is, the frame of the stack function itself. + T Specifies the top-level frame. + A symbol Specifies the first frame (along the control chain from the active frame) that has the frame name LITATOM. Equivalent to (STKPOS LITATOM -1). + A list of symbols Specifies the first frame (along the control chain from the active frame) whose frame name is included in the list. + A number N Specifies the Nth frame back from the active frame. If N is negative, the control chain is followed, otherwise the access chain is followed. Equivalent to (STKNTH N). +In the stack functions described below, the following errors can occur: The error Illegal stack arg occurs when a stack descriptor is expected and the supplied argument is either not a legal stack descriptor (i.e., not a stack pointer, symbol, or number), or is a symbol or number for which there is no corresponding stack frame, e.g., (STKNTH -1 'FOO) where there is no frame named FOO in the active control chain or (STKNTH -10 'EVALQT). The error Stack pointer has been released occurs whenever a released stack pointer is supplied as a stack descriptor argument for any purpose other than as a stack pointer to re-use. +Note: The creation of a single stack pointer can result in the retention of a large amount of stack space. Therefore, one should try to release stack pointers when they are no longer needed (see the Releasing and Reusing Stack Pointers section below). +In Lisp there is a fixed ammount of space allocated for the stack. When most of this space is exhausted, the STACK OVERFLOW error occurs and the debugger will be invoked. You will still have a little room on the stack to use inside the debugger. If you use up this last little bit of stack you will encounter a hard stack overflow. A hard stack overflow will put you into URaid (see the documentation on URaid). +Searching the Stack +1 + +(STKPOS(STKPOS (Function) NIL NIL ("11") 4) FRAMENAME N POS OLDPOS) [Function] +Returns a stack pointer to the Nth frame with frame name FRAMENAME. The search begins with (and includes) the frame specified by the stack descriptor POS. The search proceeds along the control chain from POS if N is negative, or along the access chain if N is positive. If N is NIL, -1 is used. Returns a stack pointer to the frame if such a frame exists, otherwise returns NIL. If OLDPOS is supplied and is a stack pointer, it is reused. If OLDPOS is supplied and is a stack pointer and STKPOS returns NIL, OLDPOS is released. If OLDPOS is not a stack pointer it is ignored. +(STKNTH(STKNTH (Function) NIL NIL ("11") 4) N POS OLDPOS) [Function] +Returns a stack pointer to the Nth frame back from the frame specified by the stack descriptor POS. If N is negative, the control chain from POS is followed. If N is positive the access chain is followed. If N equals 0, STKNTH returns a stack pointer to POS (this provides a way to copy a stack pointer). Returns NIL if there are fewer than N frames in the appropriate chain. If OLDPOS is supplied and is a stack pointer, it is reused. If OLDPOS is not a stack pointer it is ignored. +Note: (STKNTH 0) causes an error, Illegal stack arg; it is not possible to create a stack pointer to the active frame. +(STKNAME(STKNAME (Function) NIL NIL ("11") 4) POS) [Function] +Returns the frame name of the frame specified by the stack descriptor POS. +(SETSTKNAME(SETSTKNAME (Function) NIL NIL ("11") 4) POS NAME) [Function] +Changes the frame name of the frame specified by POS to be NAME. Returns NAME. +(STKNTHNAME(STKNTHNAME (Function) NIL NIL ("11") 4) N POS) [Function] +Returns the frame name of the Nth frame back from POS. Equivalent to (STKNAME (STKNTH N POS)) but avoids creation of a stack pointer. +In summary, STKPOS converts function names to stack pointers, STKNTH converts numbers to stack pointers, STKNAME converts stack pointers to function names, and STKNTHNAME converts numbers to function names. +Variable Bindings in Stack Frames +1 + +The following functions are used for accessing and changing bindings. Some of functions take an argument, N, which specifies a particular binding in the basic frame. If N is a literal atom, it is assumed to be the name of a variable bound in the basic frame. If N is a number, it is assumed to reference the Nth binding in the basic frame. The first binding is 1. If the basic frame contains no binding with the given name or if the number is too large or too small, the error Illegal arg occurs. +(STKSCAN(STKSCAN (Function) NIL NIL ("11") 5) VAR IPOS OPOS) [Function] +Searches beginning at IPOS for a frame in which a variable named VAR is bound. The search follows the access chain. Returns a stack pointer to the frame if found, otherwise returns NIL. If OPOS is a stack pointer it is reused, otherwise it is ignored. +(FRAMESCAN(FRAMESCAN (Function) NIL NIL ("11") 5) ATOM POS) [Function] +Returns the relative position of the binding of ATOM in the basic frame of POS. Returns NIL if ATOM is not found. +(STKARG(STKARG (Function) NIL NIL ("11") 5) N POS %) [Function] +Returns the value of the binding specified by N in the basic frame of the frame specified by the stack descriptor POS. N can be a literal atom or number. +(STKARGNAME(STKARGNAME (Function) NIL NIL ("11") 5) N POS) [Function] +Returns the name of the binding specified by N, in the basic frame of the frame specified by the stack descriptor POS. N can be a literal atom or number. +(SETSTKARG(SETSTKARG (Function) NIL NIL ("11") 5) N POS VAL) [Function] +Sets the value of the binding specified by N in the basic frame of the frame specified by the stack descriptor POS. N can be a literal atom or a number. Returns VAL. +(SETSTKARGNAME(SETSTKARGNAME (Function) NIL NIL ("11") 5) N POS NAME) [Function] +Sets the variable name to NAME of the binding specified by N in the basic frame of the frame specified by the stack descriptor POS. N can be a literal atom or a number. Returns NAME. This function does not work for interpreted frames. +(STKNARGS(STKNARGS (Function) NIL NIL ("11") 5) POS %) [Function] +Returns the number of arguments bound in the basic frame of the frame specified by the stack descriptor POS. +(VARIABLES(VARIABLES (Function) NIL NIL ("11") 5) POS) [Function] +Returns a list of the variables bound at POS. +(STKARGS(STKARGS (Function) NIL NIL ("11") 5) POS %) [Function] +Returns a list of the values of the variables bound at POS. +Evaluating Expressions in Stack Frames +1 + +The following functions are used to evaluate an expression in a different environment: +(ENVEVAL(ENVEVAL (Function) NIL NIL ("11") 5) FORM APOS CPOS AFLG CFLG) [Function] +Evaluates FORM in the environment specified by APOS and CPOS. That is, a new active frame is created with the frame specified by the stack descriptor APOS as its ALINK, and the frame specified by the stack descriptor CPOS as its CLINK. Then FORM is evaluated. If AFLG is not NIL, and APOS is a stack pointer, then APOS will be released. Similarly, if CFLG is not NIL, and CPOS is a stack pointer, then CPOS will be released. +(ENVAPPLY(ENVAPPLY (Function) NIL NIL ("11") 6) FN ARGS APOS CPOS AFLG CFLG) [Function] +APPLYs FN to ARGS in the environment specified by APOS and CPOS. AFLG and CFLG have the same interpretation as with ENVEVAL. +(EVALV(EVALV (Function) NIL NIL ("11") 6) VAR POS RELFLG) [Function] +Evaluates VAR, where VAR is assumed to be a symbol, in the access environment specifed by the stack descriptor POS. If VAR is unbound, EVALV returns NOBIND and does not generate an error. If RELFLG is non-NIL and POS is a stack pointer, it will be released after the variable is looked up. While EVALV could be defined as (ENVEVAL VAR POS NIL RELFLG) it is in fact somewhat faster. +(STKEVAL(STKEVAL (Function) NIL NIL ("11") 6) POS FORM FLG %) [Function] +Evaluates FORM in the access environment of the frame specified by the stack descriptor POS. If FLG is not NIL and POS is a stack pointer, releases POS. The definition of STKEVAL is (ENVEVAL FORM POS NIL FLG). +(STKAPPLY(STKAPPLY (Function) NIL NIL ("11") 6) POS FN ARGS FLG) [Function] +Like STKEVAL but applies FN to ARGS. +Altering Flow of Control +1 + +The following functions are used to alter the normal flow of control, possibly jumping to a different frame on the stack. RETEVAL and RETAPPLY allow evaluating an expression in the specified environment first. +(RETFROM(RETFROM (Function) NIL NIL ("11") 6) POS VAL FLG) [Function] +Return from the frame specified by the stack descriptor POS, with the value VAL. If FLG is not NIL, and POS is a stack pointer, then POS is released. An attempt to RETFROM the top level (e.g., (RETFROM T)) causes an error, Illegal stack arg. RETFROM can be written in terms of ENVEVAL as follows: +(RETFROM + (LAMBDA (POS VAL FLG) + (ENVEVAL (LIST 'QUOTE VAL) + NIL + (if (STKNTH -1 POS + (if FLG then POS)) + else (ERRORX (LIST 19 POS))) + NIL + T))) +(RETTO(RETTO (Function) NIL NIL ("11") 6) POS VAL FLG) [Function] +Like RETFROM, but returns to the frame specified by POS. +(RETEVAL(RETEVAL (Function) NIL NIL ("11") 7) POS FORM FLG %) [Function] +Evaluates FORM in the access environment of the frame specified by the stack descriptor POS, and then returns from POS with that value. If FLG is not NIL and POS is a stack pointer, then POS is released. The definition of RETEVAL is equivalent to (ENVEVAL FORM POS (STKNTH -1 POS) FLG T), but RETEVAL does not create a stack pointer. +(RETAPPLY(RETAPPLY (Function) NIL NIL ("11") 7) POS FN ARGS FLG) [Function] +Like RETEVAL but applies FN to ARGS. +Releasing and Reusing Stack Pointers +1 + +The following functions and variables are used for manipulating stack pointers: +(STACKP(STACKP (Function) NIL NIL ("11") 7) X) [Function] +Returns X if X is a stack pointer, otherwise returns NIL. +(RELSTK(RELSTK (Function) NIL NIL ("11") 7) POS) [Function] +Release the stack pointer POS (see below). If POS is not a stack pointer, does nothing. Returns POS. +(RELSTKP(RELSTKP (Function) NIL NIL ("11") 7) X) [Function] +Returns T is X is a released stack pointer, NIL otherwise. +(CLEARSTK(CLEARSTK (Function) NIL NIL ("11") 7) FLG) [Function] +If FLG is T, returns a list of all the active (unreleased) stack pointers. If FLG is NIL, this call is a no-op. The abillity to clear all stack pointers is inconsistent with the modularity implicit in a multi processing environment. +CLEARSTKLST(CLEARSTKLST (Variable) NIL NIL ("11") 7) [Variable] +A variable used by the top-level executive. Every time the top-level executive is re-entered (e.g., following errors, or Control-D), CLEARSTKLST is checked. If its value is T, all active stack pointers are released using CLEARSTK. If its value is a list, then all stack pointers on that list are released. If its value is NIL, nothing is released. CLEARSTKLST is initially T. +NOCLEARSTKLST(NOCLEARSTKLST (Variable) NIL NIL ("11") 7) [Variable] +A variable used by the top-level executive. If CLEARSTKLST is T (see above) all active stack pointers except those on NOCLEARSTKLST are released. NOCLEARSTKLST is initially NIL. +Creating a single stack pointer can cause the retention of a large amount of stack space. Furthermore, this space will not be freed until the next garbage collection, even if the stack pointer is no longer being used, unless the stack pointer is explicitly released or reused. If there is sufficient amount of stack space tied up in this fashion, a STACK OVERFLOW condition can occur, even in the simplest of computations. For this reason, you should consider releasing a stack pointer when the environment referenced by the stack pointer is no longer needed. +The effects of releasing a stack pointer are: +1. The link between the stack pointer and the stack is broken by setting the contents of the stack pointer to the released mark. A released stack pointer prints as #ADR/#0. +2. If this stack pointer was the last remaining reference to a frame extension; that is, if no other stack pointer references the frame extension and the extension is not contained in the active control or access chain, then the extension may be reclaimed, and is reclaimed immediately. The process repeats for the access and control chains of the reclaimed extension so that all stack space that was reachable only from the released stack pointer is reclaimed. +A stack pointer may be released using the function RELSTK, but there are some cases for which RELSTK is not sufficient. For example, if a function contains a call to RETFROM in which a stack pointer was used to specify where to return to, it would not be possible to simultaneously release the stack pointer. (A RELSTK appearing in the function following the call to RETFROM would not be executed!) To permit release of a stack pointer in this situation, the stack functions that relinquish control have optional flag arguments to denote whether or not a stack pointer is to be released (AFLG and CFLG). Note that in this case releasing the stack pointer will not cause the stack space to be reclaimed immediately because the frame referenced by the stack pointer will have become part of the active environment. +Another way to avoid creating new stack pointers is to reuse stack pointers that are no longer needed. The stack functions that create stack pointers (STKPOS, STKNTH, and STKSCAN) have an optional argument that is a stack pointer to reuse. When a stack pointer is reused, two things happen. First the stack pointer is released (see above). Then the pointer to the new frame extension is deposited in the stack pointer. The old stack pointer (with its new contents) is returned as the value of the function. Note that the reused stack pointer will be released even if the function does not find the specified frame. +Even if stack pointers are explicitly being released, creating many stack pointers can cause a garbage collection of stack pointer space. Thus, if your application requires creating many stack pointers, you definitely should take advantage of reusing stack pointers. +Backtrace Functions +1 + +The following functions perform a backtrace, printing information about every frame on the stack. Arguments allow only backtracing a selected range of the stack, skipping selected frames, and printing different amounts of information about each frame. +(BACKTRACE(BACKTRACE (Function) NIL NIL ("11") 8) IPOS EPOS FLAGS FILE PRINTFN) [Function] +Performs a backtrace beginning at the frame specified by the stack descriptor IPOS, and ending with the frame specified by the stack descriptor EPOS. FLAGS is a number in which the options of the BACKTRACE are encoded. If a bit is set, the corresponding information is included in the backtrace. +1Q - print arguments of non-SUBRs +2Q - print temporaries of the interpreter +4Q - print SUBR arguments and local variables +10Q - omit printing of UNTRACE: and function names +20Q - follow access chain instead of control chain +40Q - print temporaries, i.e. the blips (see the stack and interpreter section below) +For example: If FLAGS = 47Q, everything is printed. If FLAGS = 21Q, follows the access chain, prints arguments. +FILE is the file that the backtrace is printed to. FILE must be open. PRINTFN is used when printing the values of variables, temporaries, blips, etc. PRINTFN = NIL defaults to PRINT. +(BAKTRACE(BAKTRACE (Function) NIL NIL ("11") 9) IPOS EPOS SKIPFNS FLAGS FILE) [Function] +Prints a backtrace from IPOS to EPOS onto FILE. FLAGS specifies the options of the backtrace, e.g., do/don't print arguments, do/don't print temporaries of the interpreter, etc., and is the same as for BACKTRACE. +SKIPFNS is a list of functions. As BAKTRACE scans down the stack, the stack name of each frame is passed to each function in SKIPFNS, and if any of them returnS non-NIL, POS is skipped (including all variables). +BAKTRACE collapses the sequence of several function calls corresponding to a call to a system package into a single function using BAKTRACELST as described below. For example, any call to the editor is printed as **EDITOR**, a break is printed as **BREAK**, etc. +BAKTRACE is used by the BT, BTV, BTV+, BTV*, and BTV! break commands, with FLAGS = 0, 1, 5, 7, and 47Q respectively. +If SYSPRETTYFLG = T, the values arguments and local variables will be prettyprinted. +BAKTRACELST(BAKTRACELST (Variable) NIL NIL ("11") 9) [Variable] +Used to tell BAKTRACE (therefore, the BT, BTV, etc. commands) to abbreviate various sequences of function calls on the stack by a single key, e.g. **BREAK**, **EDITOR**, etc. +Each entry on BAKTRACELST is a list of the form (FRAMENAME KEY . PATTERN) or (FRAMENAME (KEY1 . PATTERN1) ... (KEYN . PATTERNN)), where a pattern is a list of elements that are either atoms, which match a single frame, or lists, which are interpreted as a list of alternative patterns, e.g. (PROGN **BREAK** EVAL ((ERRORSET BREAK1A BREAK1) (BREAK1))) +BAKTRACE operates by scanning up the stack and, at each point, comparing the current frame name, with the frame names on BAKTRACELST, i.e. it does an ASSOC. If the frame name does appear, BAKTRACE attempts to match the stack as of that point with (one of) the patterns. If the match is successful, BAKTRACE prints the corresponding key, and continues with where the match left off. If the frame name does not appear, or the match fails, BAKTRACE simply prints the frame name and continues with the next higher frame (unless the SKIPFNS applied to the frame name are non-NIL as described above). +Matching is performed by comparing symbols in the pattern with the current frame name, and matching lists as patterns, i.e. sequences of function calls, always working up the stack. For example, either of the sequence of function calls ... BREAK1 BREAK1A ERRORSET EVAL PROGN ... or ... BREAK1 EVAL PROGN ... would match with the sample entry given above, causing **BREAK** to be printed. +Special features: +f The symbol & can be used to match any frame. +f The pattern - can be used to match nothing. - is useful for specifying an optional match, e.g. the example above could also have been written as (PROGN **BREAK** EVAL ((ERRORSET BREAK1A) -) BREAK1). +f It is not necessary to provide in the pattern for matching dummy frames, i.e. frames for which DUMMYFRAMEP (see below) is true. When working on a match, the matcher automatically skips over these frames when they do not match. +f If a match succeeds and the KEY is NIL, nothing is printed. For example, (*PROG*LAM NIL EVALA *ENV). This sequence will occur following an error which then causes a break if some of the function's arguments are LOCALVARS. +Other Stack Functions +(DUMMYFRAMEP(DUMMYFRAMEP (Function) NIL NIL ("11") 10) POS) [Function] +Returns T if you never wrote a call to the function at POS, e.g. in Interlisp-10, DUMMYFRAMEP is T for *PROG*LAM, *ENV*, and FOOBLOCK frames (see the Block Compiling section of Chapter 18). +REALFRAMEP and REALSTKNTH can be used to write functions which manipulate the stack and work on either interpreted or compiled code: +(REALFRAMEP(REALFRAMEP (Function) NIL NIL ("11") 10) POS INTERPFLG) [Function] +Returns POS if POS is a real frame, i.e. if POS is not a dummy frame and POS is a frame that does not disappear when compiled (such as COND); otherwise NIL. If INTERPFLG = T, returns T if POS is not a dummy frame. For example, if (STKNAME POS) = COND, (REALFRAMEP POS) is NIL, but (REALFRAMEP POS T) is T. +(REALSTKNTH(REALFRAMEP (Function) NIL NIL ("11") 10) N POS INTERPFLG OLDPOS) [Function] +Returns a stack pointer to the Nth (or -Nth) frames for which (REALFRAMEP POS INTERPFLG) is POS. +(MAPDL(REALFRAMEP (Function) NIL NIL ("11") 10) MAPDLFN MAPDLPOS) [Function] +Starts at MAPDLPOS and applies the function MAPDLFN to two arguments (the frame name and a stack pointer to the frame), for each frame until the top of the stack is reached. Returns NIL. For example, +[MAPDL (FUNCTION (LAMBDA (X POS) +(if (IGREATERP (STKNARGS POS) 2) then (PRINT X)] +will print all functions of more than two arguments. +(SEARCHPDL(SEARCHPDL (Function) NIL NIL ("11") 11) SRCHFN SRCHPOS) [Function] +Like MAPDL, but searches the stack starting at position SRCHPOS until it finds a frame for which SRCHFN, a function of two arguments applied to the name of the frame and the frame itself, is not NIL. Returns (NAME . FRAME) if such a frame is found, otherwise NIL. +The Stack and the Interpreter +1 + +In addition to the names and values of arguments for functions, information regarding partially-evaluated expressions is kept on the push-down list. For example, consider the following definition of the function FACT (intentionally faulty): +(FACT +[LAMBDA (N) + (COND + ((ZEROP N) + L) + (T (ITIMES N (FACT (SUB1 N]) +In evaluating the form (FACT 1), as soon as FACT is entered, the interpreter begins evaluating the implicit PROGN following the LAMBDA. The first function entered in this process is COND. COND begins to process its list of clauses. After calling ZEROP and getting a NIL value, COND proceeds to the next clause and evaluates T. Since T is true, the evaluation of the implicit PROGN that is the consequent of the T clause is begun. This requires calling the function ITIMES. However before ITIMES can be called, its arguments must be evaluated. The first argument is evaluated by retrieving the current binding of N from its value cell; the second involves a recursive call to FACT, and another implicit PROGN, etc. +At each stage of this process, some portion of an expression has been evaluated, and another is awaiting evaluation. The output below (from Interlisp-10) illustrates this by showing the state of the push-down list at the point in the computation of (FACT 1) when the unbound atom L is reached. +_ FACT(1) +u.b.a. L {in FACT} in ((ZEROP NO L) +(L broken) +:BTV! + *TAIL* (L) + *ARG1 (((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N))))) +COND + *FORM* (COND ((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N))))) + *TAIL* ((COND ((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N)))))) + N 0 +FACT + *FORM* (FACT (SUB1 N)) + *FN* ITIMES + *TAIL* ((FACT (SUB1 N))) + *ARGVAL* 1 + *FORM* (ITIMES N (FACT (SUB1 N))) + *TAIL* ((ITIMES N (FACT (SUB1 N)))) + *ARG1 (((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N))))) +COND + *FORM* (COND ((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N))))) + *TAIL* ((COND ((ZEROP N) L) (T (ITIMES N (FACT (SUB1 N)))))) + N 1 +FACT +**TOP** +Internal calls to EVAL, e.g., from COND and the interpreter, are marked on the push-down list by a special mark or blip which the backtrace prints as *FORM*. The genealogy of *FORM*'s is thus a history of the computation. Other temporary information stored on the stack by the interpreter includes the tail of a partially evaluated implicit PROGN (e.g., a cond clause or lambda expression) and the tail of a partially evaluated form (i.e., those arguments not yet evaluated), both indicated on the backtrace by *TAIL*, the values of arguments that have already been evaluated, indicated by *ARGVAL*, and the names of functions waiting to be called, indicated by *FN*. *ARG1, ..., *ARGn are used by the backtrace to indicate the (unnamed) arguments to SUBRs. +Note that a function is not actually entered and does not appear on the stack, until its arguments have been evaluated (except for nlambda functions, of course). Also note that the *ARG1, *FORM*, *TAIL*, etc. bindings comprise the actual working storage. In other words, in the above example, if a (lower) function changed the value of the *ARG1 binding, the COND would continue interpreting the new binding as a list of COND clauses. Similarly, if the *ARGVAL* binding were changed, the new value would be given to ITIMES as its first argument after its second argument had been evaluated, and ITIMES was actually called. +*FORM*, *TAIL*, *ARGVAL*, etc., do not actually appear as variables on the stack, i.e., evaluating *FORM* or calling STKSCAN to search for it will not work. However, the functions BLIPVAL, SETBLIPVAL, and BLIPSCAN described below are available for accessing these internal blips. These functions currently know about four different types of blips: + *FN* The name of a function about to be called + *ARGVAL* An argument for a function about to be called + *FORM* A form in the process of evaluation + *TAIL* The tail of a COND clause, implicit PROGN, PROG, etc. +(BLIPVAL(BLIPVAL (Function) NIL NIL ("11") 12) BLIPTYP IPOS FLG) [Function] +Returns the value of the specified blip of type BLIPTYP. If FLG is a number N, finds the Nth blip of the desired type, searching the control chain beginning at the frame specified by the stack descriptor IPOS. If FLG is NIL, 1 is used. If FLG is T, returns the number of blips of the specified type at IPOS. +(SETBLIPVAL(SETBLIPVAL (Function) NIL NIL ("11") 12) BLIPTYP IPOS N VAL) [Function] +Sets the value of the specified blip of type BLIPTYP. Searches for the Nth blip of the desired type, beginning with the frame specified by the stack descriptor IPOS, and following the control chain. +(BLIPSCAN(BLIPSCAN (Function) NIL NIL ("11") 13) BLIPTYP IPOS) [Function] +Returns a stack pointer to the frame in which a blip of type BLIPTYP is located. Search begins at the frame specified by the stack descriptor IPOS and follows the control chain. +Generators +1 + +A generator is like a subroutine except that it retains information about previous times it has been called. Some of this state may be data (for example, the seed in a random number generator), and some may be in program state (as in a recursive generator which finds all the atoms in a list structure). For example, if LISTGEN is defined by: +(DEFINEQ (LISTGEN (L) + (if L then (PRODUCE (CAR L)) + (LISTGEN (CDR L)))) +we can use the function GENERATOR (described below) to create a generator that uses LISTGEN to produce the elements of a list one at a time, e.g., +(SETQ GR (GENERATOR (LISTGEN '(A B C)))) +creates a generator, which can be called by +(GENERATE GR) +to produce as values on successive calls, A, B, C. When GENERATE (not GENERATOR) is called the first time, it simply starts evaluating (LISTGEN '(A B C)). PRODUCE gets called from LISTGEN, and pops back up to GENERATE with the indicated value after saving the state. When GENERATE gets called again, it continues from where the last PRODUCE left off. This process continues until finally LISTGEN completes and returns a value (it doesn't matter what it is). GENERATE then returns GR itself as its value, so that the program that called GENERATE can tell that it is finished, i.e., there are no more values to be generated. +(GENERATOR(GENERATOR (Function) NIL NIL ("11") 13) FORM COMVAR) [NLambda Function] +An nlambda function that creates a generator which uses FORM to compute values. GENERATOR returns a generator handle which is represented by a dotted pair of stack pointers. +COMVAR is optional. If its value (EVAL of) is a generator handle, the list structure and stack pointers will be reused. Otherwise, a new generator handle will be constructed. +GENERATOR compiles open. +(PRODUCE(PRODUCE (Function) NIL NIL ("11") 13) VAL) [Function] +Used from within a generator to return VAL as the value of the corresponding call to GENERATE. +(GENERATE(GENERATE (Function) NIL NIL ("11") 13) HANDLE VAL) [Function] +Restarts the generator represented by HANDLE. VAL is returned as the value of the PRODUCE which last suspended the operation of the generator. When the generator runs out of values, GENERATE returns HANDLE itself. +Examples: +The following function will go down recursively through a list structure and produce the atoms in the list structure one at a time. +(DEFINEQ (LEAVESG (L) +(if (ATOM L) + then (PRODUCE L) + else (LEAVESG (CAR L)) + (if (CDR L) + then (LEAVESG (CDR L)] +The following function prints each of these atoms as it appears. It illustrates how a loop can be set up to use a generator. +(DEFINEQ (PLEAVESG1 (L) +(PROG (X LHANDLE) + (SETQ LHANDLE (GENERATOR (LEAVESG L))) + LP (SETQ X (GENERATE LHANDLE)) + (if (EQ X LHANDLE) + then (RETURN NIL)) + (PRINT X) + (GO LP))] +The loop terminates when the value of the generator is EQ to the dotted pair which is the value produced by the call to GENERATOR. A CLISP iterative operator, OUTOF, is provided which makes it much easier to write the loop in PLEAVESG1. OUTOF (or outof) can precede a form which is to be used as a generator. On each iteration, the iteration variable will be set to successive values returned by the generator; the loop will be terminated automatically when the generator runs out. Therefore, the following is equivalent to the above program PLEAVESG1: +(DEFINEQ (PLEAVESG2 (L) (for X outof (LEAVESG L) do (PRINT X))] +Here is another example; the following form will print the first N atoms. +(for X outof (MAPATOMS (FUNCTION PRODUCE)) as I from 1 to N do (PRINT X)) +Coroutines +1 + +This package provides facilities for the creation and use of fully general coroutine structures. It uses a stack pointer to preserve the state of a coroutine, and allows arbitrary switching between N different coroutines, rather than just a call to a generator and return. This package is slightly more efficient than the generator package described above, and allows more flexibility on specification of what to do when a coroutine terminates. +(COROUTINE(COROUTINE (Function) NIL NIL ("11") 14) CALLPTR COROUTPTR COROUTFORM ENDFORM) [NLambda Function] +This nlambda function is used to create a coroutine and initialize the linkage. CALLPTR and COROUTPTR are the names of two variables, which will be set to appropriate stack pointers. If the values of CALLPTR or COROUTPTR are already stack pointers, the stack pointers will be reused. COROUTFORM is the form which is evaluated to start the coroutine; ENDFORM is a form to be evaluated if COROUTFORM actually returns when it runs out of values. +COROUTINE compiles open. +(RESUME(RESUME (Function) NIL NIL ("11") 15) FROMPTR TOPTR VAL) [Function] +Used to transfer control from one coroutine to another. FROMPTR should be the stack pointer for the current coroutine, which will be smashed to preserve the current state. TOPTR should be the stack pointer which has preserved the state of the coroutine to be transferred to, and VAL is the value that is to be returned to the latter coroutine as the value of the RESUME which suspended the operation of that coroutine. +For example, the following is the way one might write the LEAVES program using the coroutine package: +(DEFINEQ (LEAVESC (L COROUTPTR CALLPTR) + (if (ATOM L) + then (RESUME COROUTPTR CALLPTR L) + else (LEAVESC (CAR L) COROUTPTR CALLPTR) + (if (CDR L) then (LEAVESC (CDR L) COROUTPTR CALLPTR))))] +A function PLEAVESC which uses LEAVESC can be defined as follows: +(DEFINEQ (PLEAVESC (L) + (bind PLHANDLE LHANDLE + first (COROUTINE PLHANDLE LHANDLE + (LEAVESC L LHANDLE PLHANDLE) + (RETFROM 'PLEAVESC)) + do (PRINT (RESUME PLHANDLE LHANDLE))))] +By RESUMEing LEAVESC repeatedly, this function will print all the leaves of list L and then return out of PLEAVESC via the RETFROM. The RETFROM is necessary to break out of the non-terminating do-loop. This was done to illustrate the additional flexibility allowed through the use of ENDFORM. +We use two coroutines working on two trees in the example EQLEAVES, defined below. EQLEAVES tests to see whether two trees have the same leaf set in the same order, e.g., (EQLEAVES '(A B C) '(A B (C))) is true. +(DEFINEQ (EQLEAVES (L1 L2) + (bind LHANDLE1 LHANDLE2 PE EL1 EL2 + first (COROUTINE PE LHANDLE1 (LEAVESC L1 LHANDLE1 PE) 'NO-MORE) + (COROUTINE PE LHANDLE2 (LEAVESC L2 LHANDLE2 PE) 'NO-MORE) + do (SETQ EL1 (RESUME PE LHANDLE1)) + (SETQ EL2 (RESUME PE LHANDLE2)) + (if (NEQ EL1 EL2) + then (RETURN NIL)) + repeatuntil (EQ EL1 'NO-MORE) + finally (RETURN T)))] +Possibilities Lists +1 + +A possibilities list is the interface between a generator and a consumer. The possibilities list is initialized by a call to POSSIBILITIES, and elements are obtained from it by using TRYNEXT. By using the spaghetti stack to maintain separate environments, this package allows a regime in which a generator can put a few items in a possibilities list, suspend itself until they have been consumed, and be subsequently aroused and generate some more. +(POSSIBILITIES FORM) [NLambda Function] +This nlambda function is used for the initial creation of a possibilities list. FORM will be evaluated to create the list. It should use the functions NOTE and AU-REVOIR described below to generate possibilities. Normally, one would set some variable to the possibilities list which is returned, so it can be used later, e.g.: +(SETQ PLIST (POSSIBILITIES (GENERFN V1 V2))). +POSSIBILITIES compiles open. +(NOTE(NOTE (Function) NIL NIL ("11") 16) VAL LSTFLG) [Function] +Used within a generator to put items on the possibilities list being generated. If LSTFLG is equal to NIL, VAL is treated as a single item. If LSTFLG is non-NIL, then the list VAL is NCONCed on the end of the possibilities list. Note that it is perfectly reasonable to create a possibilities list using a second generator, and NOTE that list as possibilities for the current generator with LSTFLG equal to T. The lower generator will be resumed at the appropriate point. +(AU-REVOIR(AU-REVOIR (Function) NIL NIL ("11") 16) VAL) [NoSpread Function] +Puts VAL on the possibilities list if it is given, and then suspends the generator and returns to the consumer in such a fashion that control will return to the generator at the AU-REVOIR if the consumer exhausts the possibilities list. +NIL is not put on the possibilities list unless it is explicitly given as an argument to AU-REVOIR, i.e., (AU-REVOIR) and (AU-REVOIR NIL) are not the same. AU-REVOIR and ADIEU are lambda nospreads to enable them to distinguish these two cases. +(ADIEU(ADIEU (Function) NIL NIL ("11") 16) VAL) [NoSpread Function] +Like AU-REVOIR but releases the generator instead of suspending it. +(TRYNEXT(TRYNEXT (Function) NIL NIL ("11") 16) PLST ENDFORM VAL) [NLambda Function] +This nlambda function allows a consumer to use a possibilities list. It removes the first item from the possibilities list named by PLST (i.e. PLST must be an atom whose value is a possiblities list), and returns that item, provided it is not a generator handle. If a generator handle is encountered, the generator is reawakened. When it returns a possibilities list, this list is added to the front of the current list. When a call to TRYNEXT causes a generator to be awakened, VAL is returned as the value of the AU-REVOIR which put that generator to sleep. If PLST is empty, it evaluates ENDFORM in the caller's environment. +TRYNEXT compiles open. +(CLEANPOSLST(CLEANPOSLST (Function) NIL NIL ("11") 16) PLST) [Function] +This function is provided to release any stack pointers which may be left in the PLST which was not used to exhaustion. +For example, FIB is a generator for fibonnaci numbers. It starts out by NOTEing its two arguments, then suspends itself. Thereafter, on being re-awakened, it will NOTE two more terms in the series and suspends again. PRINTFIB uses FIB to print the first N fibonacci numbers. +(DEFINEQ (FIB (F1 F2) + (do (NOTE F1) + (NOTE F2) + (SETQ F1 (IPLUS F1 F2)) + (SETQ F2 (IPLUS F1 F2)) + (AU-REVOIR)] +Note that this AU-REVOIR just suspends the generator and adds nothing to the possibilities list except the generator. + (DEFINEQ (PRINTFIB (N) + (PROG ((FL (POSSIBILITIES (FIB 0 1)))) + (RPTQ N (PRINT (TRYNEXT FL))) + (CLEANPOSLST FL)] +Note that FIB itself will never terminate. + + +[This page intentionally left blank] +(SEQUENCE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "11-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "11-" "")) (222 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (ALTERNATE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "11-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "11-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (102 612 510 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "11-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "11-" "")) (222 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (48 612 564 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))))1$6$EVENT2$TE$ PAGEHEADING RIGHTPAGE1$1<<$1<<$1HH$50B$T1l~$2 +T16$1$1ZZ$100$7100$1**$1~~$2 +T4$$4Hf$H4$$$7~x~1$1$1$F$ PAGEHEADING RIGHTPAGETE$ PAGEHEADINGLEFTBACKT/MODERN +(CHARPROPS (COLOR . BLACK)).GACHA +(CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK))0CLASSIC +(CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK))0CLASSIC +(CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK))/MODERN +(CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK)).TITAN(CHARPROPS (COLOR . BLACK))0CLASSIC +(CHARPROPS (COLOR . BLACK))3 TIMESROMAN -PALATINO PALATINO PALATINO TITAN TITAN TITAN MODERN -CLASSIC -CLASSIC - HELVETICACLASSIC -MODERNMODERN -  IM.CHAP.GETFNMODERN -%  HRULE.GETFNMODERND5Z  HRULE.GETFNY!oy3F! h1k < &   HRULE.GETFNN l -)d -S  m%  HRULE.GETFN $IM.INDEX.GETFNCLASSIC - - U4+^7( '$IM.INDEX.GETFNCLASSIC - -  ?%/ 9&7' -E%IM.INDEX.GETFNCLASSIC - -F -(IM.INDEX.GETFNCLASSIC - - 1  -(IM.INDEX.GETFNCLASSIC - - ) ,%0 -%"  HRULE.GETFN k?]-  %IM.INDEX.GETFNCLASSIC - -  's; 'IM.INDEX.GETFNCLASSIC - - 0  $IM.INDEX.GETFNCLASSIC - -  .C" -(IM.INDEX.GETFNCLASSIC - - -D!  'IM.INDEX.GETFNCLASSIC - -  +C- +IM.INDEX.GETFNCLASSIC - -  C-6&IM.INDEX.GETFNCLASSIC - - h 'IM.INDEX.GETFNCLASSIC - -)%IM.INDEX.GETFNCLASSIC - - 7'  HRULE.GETFN W%IM.INDEX.GETFNCLASSIC - -     -![2"&IM.INDEX.GETFNCLASSIC - -     !&#IM.INDEX.GETFNCLASSIC - -   -W  %Q  %IM.INDEX.GETFNCLASSIC - -    -J &IM.INDEX.GETFNCLASSIC - -      HRULE.GETFN {D%IM.INDEX.GETFNCLASSIC - -  8         *  #IM.INDEX.GETFNCLASSIC - -  %IM.INDEX.GETFNCLASSIC - -    -J!  "&IM.INDEX.GETFNCLASSIC - -    %  HRULE.GETFN P$IM.INDEX.GETFNCLASSIC - -'$IM.INDEX.GETFNCLASSIC - -0%IM.INDEX.GETFNCLASSIC - - &IM.INDEX.GETFNCLASSIC - -D )IM.INDEX.GETFNCLASSIC - /_  +IM.INDEX.GETFNTITAN 0 ' -  1.3%C1<7\6  HRULE.GETFN  'IM.INDEX.GETFNCLASSIC - -    N>) \  *   3 V.0J &IM.INDEX.GETFNCLASSIC - -     R!'} H - B )IM.INDEX.GETFNCLASSIC - f  -   ;q "gS#J9  -! -!d4 -b z -$q   *IM.INDEX.GETFNCLASSIC - -. 9 - -l -)IM.INDEX.GETFNCLASSIC - - ;   -(    -)IM.INDEX.GETFNCLASSIC - -     )IM.INDEX.GETFNCLASSIC - -  - ! 15 (IM.INDEX.GETFNCLASSIC - - ."-+ %  HRULE.GETFN       <17+ )6w>  -$ 8>@%'8>@ oI@B:7IK 9 -+/%&IM.INDEX.GETFNCLASSIC - -  0  r7 -)IM.INDEX.GETFNCLASSIC - -   -X#'IM.INDEX.GETFNPALATINO =K  -  HRULE.GETFN 7 #% 38 ), * 8851@FO (IM.INDEX.GETFNCLASSIC - - 8  : &IM.INDEX.GETFNCLASSIC - -'+'IM.INDEX.GETFNCLASSIC - - &!^   -  ~ )"7? > $ @AJ -  HRULE.GETFN (IM.INDEX.GETFNCLASSIC - -  - Q d A -8 -. %IM.INDEX.GETFNCLASSIC - -  :nfQ2:&(&-B  %1)+V :P -'EE((#  HRULE.GETFN~ -  -QD  . #IM.INDEX.GETFNCLASSIC - - T "; -A (IM.INDEX.GETFNCLASSIC - - 2V    E$IM.INDEX.GETFNCLASSIC - - 6&IM.INDEX.GETFNCLASSIC - -  $$! ( *IM.INDEX.GETFNCLASSIC - -Q# 9X3 ^,* -%z \ No newline at end of file +(CHARPROPS (COLOR . BLACK))/MODERN(CHARPROPS (COLOR . BLACK))2 HELVETICA(CHARPROPS (COLOR . BLACK)) IM.CHAP.GETFN% + HRULE.GETFN D5 Z  + HRULE.GETFNY !oy3F! h1k < &   + HRULE.GETFNN l +)d +S   m%  + HRULE.GETFN + $IM.INDEX.GETFN U4+^7( ' $IM.INDEX.GETFN?%/ 9&7'  +E %IM.INDEX.GETFNF  +(IM.INDEX.GETFN1   +(IM.INDEX.GETFN) ,%0 +% " + HRULE.GETFN +k?]-   %IM.INDEX.GETFN's;  'IM.INDEX.GETFN0   &IM.INDEX.GETFN.C"  +(IM.INDEX.GETFN-D!   'IM.INDEX.GETFN+C-  +IM.INDEX.GETFNC-6 (IM.INDEX.GETFNh  'IM.INDEX.GETFN) 'IM.INDEX.GETFN7 ' + HRULE.GETFN +W %IM.INDEX.GETFN +![2" &IM.INDEX.GETFN!& #IM.INDEX.GETFN +W  %Q   %IM.INDEX.GETFN +J  &IM.INDEX.GETFN   + HRULE.GETFN +{D %IM.INDEX.GETFN8   +  * #IM.INDEX.GETFN  'IM.INDEX.GETFN +J!  " &IM.INDEX.GETFN  % + HRULE.GETFN +P $IM.INDEX.GETFN' $IM.INDEX.GETFN0 %IM.INDEX.GETFN  &IM.INDEX.GETFND )IM.INDEX.GETFN /_  +IM.INDEX.GETFN0 ' +  1 .3%C1< 7 \6   + HRULE.GETFN +  'IM.INDEX.GETFNN>) \* 3V.0J  &IM.INDEX.GETFN R!'} H + B )IM.INDEX.GETFN f  +       ;q "gS#J9 !!d4b z$q  +  *IM.INDEX.GETFN. 9 + +l  +)IM.INDEX.GETFN ;   +(     +)IM.INDEX.GETFN    )IM.INDEX.GETFN + +!15  (IM.INDEX.GETFN."- + %  + HRULE.GETFN +   <17+ )6w>  +$ 8>@%'8>@ oI@B:7IK 9 ++/% &IM.INDEX.GETFN0  r7  +)IM.INDEX.GETFN-X# 'IM.INDEX.GETFN  =K  + + HRULE.GETFN 7#% 38),* 8851@FO  (IM.INDEX.GETFN8   :  &IM.INDEX.GETFN'+ 'IM.INDEX.GETFN&!^   + ~)"7? > $  @A J + + HRULE.GETFN  (IM.INDEX.GETFN  +Q d A +8 +.  %IM.INDEX.GETFN:nfQ2:&(&-B  %1)+V :P +'EE((#  + HRULE.GETFN~ -  QD  +.  #IM.INDEX.GETFNT "; +A  (IM.INDEX.GETFN 2V    E $IM.INDEX.GETFN 6 &IM.INDEX.GETFN$$! (  *IM.INDEX.GETFNQ# 9X3 ^,* +%(((CHARENCODING . MCCS)))PROPS:#DATE:j~z \ No newline at end of file diff --git a/docs/medley-irm/12-MISC.TEDIT b/docs/medley-irm/12-MISC.TEDIT index f632b3b2..d068231c 100644 Binary files a/docs/medley-irm/12-MISC.TEDIT and b/docs/medley-irm/12-MISC.TEDIT differ diff --git a/docs/medley-irm/13-EXECUTIVE.TEDIT b/docs/medley-irm/13-EXECUTIVE.TEDIT index e7bc6f9f..d2767f51 100644 Binary files a/docs/medley-irm/13-EXECUTIVE.TEDIT and b/docs/medley-irm/13-EXECUTIVE.TEDIT differ diff --git a/docs/medley-irm/14-ERRORS.TEDIT b/docs/medley-irm/14-ERRORS.TEDIT index 75bcdca5..a50aff9d 100644 Binary files a/docs/medley-irm/14-ERRORS.TEDIT and b/docs/medley-irm/14-ERRORS.TEDIT differ diff --git a/docs/medley-irm/15-BREAKING.TEDIT b/docs/medley-irm/15-BREAKING.TEDIT index 69d22950..b2609d0f 100644 Binary files a/docs/medley-irm/15-BREAKING.TEDIT and b/docs/medley-irm/15-BREAKING.TEDIT differ diff --git a/docs/medley-irm/16-SEDIT.TEDIT b/docs/medley-irm/16-SEDIT.TEDIT index ac9813fd..72e7c42d 100644 Binary files a/docs/medley-irm/16-SEDIT.TEDIT and b/docs/medley-irm/16-SEDIT.TEDIT differ diff --git a/docs/medley-irm/17-FILEPACKAGE.TEDIT b/docs/medley-irm/17-FILEPACKAGE.TEDIT index 29196c90..ecb6d413 100644 Binary files a/docs/medley-irm/17-FILEPACKAGE.TEDIT and b/docs/medley-irm/17-FILEPACKAGE.TEDIT differ diff --git a/docs/medley-irm/18-COMPILER.TEDIT b/docs/medley-irm/18-COMPILER.TEDIT index 5acaf3bb..2dd0ef9a 100644 Binary files a/docs/medley-irm/18-COMPILER.TEDIT and b/docs/medley-irm/18-COMPILER.TEDIT differ diff --git a/docs/medley-irm/19-DWIM.TEDIT b/docs/medley-irm/19-DWIM.TEDIT index cf78df7a..20e81e42 100644 Binary files a/docs/medley-irm/19-DWIM.TEDIT and b/docs/medley-irm/19-DWIM.TEDIT differ diff --git a/docs/medley-irm/20-CLISP.TEDIT b/docs/medley-irm/20-CLISP.TEDIT index b16edf46..041bb398 100644 --- a/docs/medley-irm/20-CLISP.TEDIT +++ b/docs/medley-irm/20-CLISP.TEDIT @@ -1,107 +1,400 @@ -INTERLISP-D REFERENCE MANUAL CLISP "20"20. CLISP 2 The syntax of Lisp is very simple. It can be described concisely, but it makes Lisp difficult to read and write without tools. Unlike many languages, there are no reserved words in Lisp such as IF, THEN, FOR, DO, etc., nor reserved characters like +, -, =, , etc. The only components of the language are atoms and delimiters. This eliminates the need for parsers and precedence rules, and makes Lisp programs easy to mainpuilate. For example, a Lisp interpreter can be written in one or two pages of Lisp code. This makes Lisp the most suitable programming language for writing programs that deal with other programs as data. Human language is based on more complicated structures and relies more on special words to carry the meaning. The definiton of the factorial function looks like this in Lisp: (COND ((ZEROP N) 1) (T (TIMES N (FACTORIAL ((SUB1 N)))))) This definition is easy to read for a machine but difficult to read for a human. CLISP(CLISP NIL NIL NIL ("21") 1) is designed to make Interlisp programs easier to read and write. CLISP does this by translating various operators, conditionals, and iterative statements to Interlisp. For example, factorial can be written in CLISP: (IF N = 0 THEN 1 ELSE N*(FACTORIAL N-1)) CLISP will translate this expression to the form in the example above. The translation will take place when the form is read so there are no performance penalties. You should view CLISP as a shothand for produceing Lisp programs. CLISP makes a program easy to read and sometimes more compact. CLISP is implemented via the error correction machinery in Interlisp (see Chapter 20). Any expression that Interlisp thinks is well-formed will never be seen by CLISP This means that interpreted programs that do not use CLISP constructs do not pay for its availability by slower execution time. In fact, the Interlisp interpreter does not know about CLISP at all. When the interpreter finds an error it calls an error routine which in turn invokes the Do-What-I-Mean (DWIM) analyzer. The DWIM analyzer knows how to deal with CLISP expressions. If the expression in question turns out to be a CLISP construct, the translated form is returned to the interpreter. In addition, the original CLISP expression is modified so that it becomes the correctly translated Interlisp form. In this way, the analysis and translation are done only once. Integrating CLISP into Medley makes possible Do-What-I-Mean features for CLISP constructs as well as for pure Lisp expressions. For example, if you have defined a function named GET-PARENT, CLISP would know not to attempt to interpret the form (GET-PARENT) as an arithmetic infix operation. (Actually, CLISP would never get to see this form, since it does not contain any errors.) If you mistakenly write (GET-PRAENT), CLISP would know you meant (GET-PARENT), and not (DIFFERENCE GET PRAENT), by using the information that PARENT is not the name of a variable, and that GET-PARENT is the name of a user function whose spelling is "very close" to that of GET-PRAENT. Similarly, by using information about the program's environment not readily available to a preprocessor, CLISP can successfully resolve the following sorts of ambiguities: 1. (LIST X*FACT N), where FACT is the name of a variable, means (LIST (X*FACT) N). 2. (LIST X*FACT N), where FACT is not the name of a variable but instead is the name of a function, means (LIST X*(FACT N)), i.e., N is FACT's argument. 3. (LIST X*FACT(N)), FACT the name of a function (and not the name of a variable), means (LIST X*(FACT N)). 4. Cases 1, 2 and 3 with FACT misspelled! The first expression is correct both from the standpoint of CLISP syntax and semantics so the change would be made notification. In the other cases, you would be informed or consulted about what was taking place. For example, suppose you write the expression (LIST X*FCCT N). Assume also that there was both a function named FACT and a variable named FCT. 1. You will first be asked if FCCT is a misspelling of FCT. If you say YES, the expression will be interpreted as (LIST (X*FCT) N). If you say NO, you will be asked if FCCT was a misspelling of FACT, i.e., if you intended X*FCCT N to mean X*(FACT N). 2. If you say YES to this question, the indicated transformation will be performed. If you say NO, the system will ask if X*FCCT should be treated as CLISP, since FCCT is not the name of a (bound) variable. 3. If you say YES, the expression will be transformed, if NO, it will be left alone, i.e., as (LIST X*FCCT N). Note that we have not even considered the case where X*FCCT is itself a misspelling of a variable name, e.g., a variable named XFCT (as with GET-PRAENT). This sort of transformation will be considered after you said NO to X*FCCT N -> X*(FACT N). The question of whether X*FCCT should be treated as CLISP is important because Interlisp users may have programs that employ identifiers containing CLISP operators. Thus, if CLISP encounters the expression A/B in a context where either A or B are not the names of variables, it will ask you if A/B is intended to be CLISP, in case you really do have a free variable named A/B. Note: Through the discussion above, we speak of CLISP or DWIM asking you. Actually, if you typed in the expression in question for immediate execution, you are simply informed of the transformation, on the grounds that you would prefer an occasional misinterpretation rather than being continuously bothered, especially since you can always retype what you intended if a mistake occurs, and ask the programmer's assistant to UNDO the effects of the mistaken operations if necessary. For transformations on expressions in your programs, you can tell CLISP whether you wish to operate in CAUTIOUS or TRUSTING mode. In the former case (most typical) you will be asked to approve transformations, in the latter, CLISP will operate as it does on type-in, i.e., perform the transformation after informing you. CLISP can also handle parentheses errors caused by typing 8 or 9 for ( or ). (On most terminals, 8 and 9 are the lowercase characters for ( and ), i.e., ( and 8 appear on the same key, as do ) and 9.) For example, if you write N*8FACTORIAL N-1, the parentheses error can be detected and fixed before the infix operator * is converted to the Interlisp function TIMES. CLISP is able to distinguish this situation from cases like N*8*X meaning (TIMES N 8 X), or N*8X, where 8X is the name of a variable, again by using information about the programming environment. In fact, by integrating CLISP with DWIM, CLISP has been made sufficiently tolerant of errors that almost everything can be misspelled! For example, CLISP can successfully translate the definition of FACTORIAL: (IFF N = 0 THENN1 ESLE N*8FACTTORIALNN-1) to the corresponding COND, while making five spelling corrections and fixing the parenthesis error. CLISP also contains a facility for converting from Interlisp back to CLISP, so that after running the above incorrect definition of FACTORIAL, you could "clispify" the now correct version to obtain (IF N = 0 THEN 1 ELSE N*(FACTORIAL N-1)). This sort of robustness prevails throughout CLISP. For example, the iterative statement permits you to say things like: (FOR OLD X FROM M TO N DO (PRINT X) WHILE (PRIMEP X)) However, you can also write OLD (XM), (OLD XM), (OLD (XM)), permute the order of the operators, e.g., (DO PRINT X TO N FOR OLD XM WHILE PRIMEP X), omit either or both sets of parentheses, misspell any or all of the operators FOR, OLD, FROM, TO, DO, or WHILE, or leave out the word DO entirely! And, of course, you can also misspell PRINT, PRIMEP, M or N! In this example, the only thing you could not misspell is the first X, since it specifies the name of the variable of iteration. The other two instances of X could be misspelled. CLISP is well integrated into Medley. For example, the above iterative statement translates into an equivalent Interlisp form using PROG, COND, GO, etc. When the interpreter subsequently encounters this CLISP expression, it automatically obtains and evaluates the translation. Similarly, the compiler "knows" to compile the translated form. However, if you PRETTYPRINT your program, PRETTYPRINT "knows" to print the original CLISP at the corresponding point in your function. Similarly, when you edit your program, the editor keeps the translation invisible to you. If you modify the CLISP, the translation is automatically discarded and recomputed the next time the expression is evaluated. In short, CLISP is not a language at all, but rather a system. It plays a role analagous to that of the programmer's assistant (Chapter 13). Whereas the programmer's assistant is an invisible intermediary agent between your console requests and the Interlisp executive, CLISP sits between your programs and the Interlisp interpreter. Only a small effort has been devoted to defining the core syntax of CLISP. Instead, most of the effort has been concentrated on providing a facility which "makes sense" out of the input expressions using context information as well as built-in and acquired information about user and system programs. It has been said that communication is based on the intention of the speaker to produce an effect in the recipient. CLISP operates under the assumption that what you say is intended to represent a meaningful operation, and therefore tries very hard to make sense out of it. The motivation behind CLISP is not to provide you with many different ways of saying the same thing, but to enable you to worry less about the syntactic aspects of your communication with the system. In other words, it gives you a new degree of freedom by permitting you to concentrate more on the problem at hand, rather than on translation into a formal and unambiguous language. DWIM and CLISP are invoked on iterative statements because CAR of the iterative statement is not the name of a function, and hence generates an error. If you define a function by the same name as an i.s. operator, e.g., WHILE, TO, etc., the operator will no longer have the CLISP interpretation when it appears as CAR of a form, although it will continue to be treated as an i.s. operator if it appears in the interior of an i.s. To alert you, a warning message is printed, e.g., (WHILE DEFINED, THEREFORE DISABLED IN CLISP). CLISP(CLISP NIL NIL NIL ("21") 4 SUBNAME INTERACTION% WITH% USER SUBTEXT interaction% with% user) Interaction with User 1 Syntactically and semantically well formed CLISP transformations are always performed without informing you. Other CLISP transformations described in the previous section, e.g., misspellings of operands, infix operators, parentheses errors, unary minus - binary minus errors, all follow the same protocol as other DWIM transformations (Chapter 19). That is, if DWIM has been enabled in TRUSTING mode, or the transformation is in an expression you typed in for immediate execution, your approval is not requested, but you are informed. However, if the transformation involves a user program, and DWIM was enabled in CAUTIOUS mode, you will be asked to approve. If you say NO, the transformation is not performed. Thus, in the previous section, phrases such as "one of these (transformations) succeeds" and "the transformation LAST-ELL -> LAST-EL would be found" etc., all mean if you are in CAUTIOUS mode and the error is in a program, the corresponding transformation will be performed only if you approve (or defaults by not responding). If you say NO, the procedure followed is the same as though the transformation had not been found. For example, if A*B appears in the function FOO, and B is not bound (and no other transformations are found) you would be asked A*B [IN FOO] TREAT AS CLISP ? (The waiting time on such interactions is three times as long as for simple corrections, i.e., 3*DWIMWAIT). In certain situations, DWIM asks for approval even if DWIM is enabled in TRUSTING mode. For example, you are always asked to approve a spelling correction that might also be interpreted as a CLISP transformation, as in LAST-ELL -> LAST-EL. If you approved, A*B would be transformed to (ITIMES A B), which would then cause a U.B.A.B. error in the event that the program was being run (remember the entire discussion also applies to DWIMifying). If you said NO, A*B would be left alone. If the value of CLISPHELPFLG = NIL (initally T), you will not be asked to approve any CLISP transformation. Instead, in those situations where approval would be required, the effect is the same as though you had been asked and said NO. CLISP(CLISP NIL NIL NIL ("21") 4 SUBNAME CHARACTER% OPERATORS SUBTEXT character% operators) Character Operators 1 CLISP recognizes a number of special characters operators, both prefix and infix, which are translated into common expressions. For example, the character + is recognized to represent addition, so CLISP translates the symbol A+B to the form (IPLUS A B). Note that CLISP is invoked, and this translation is made, only if an error occurs, such as an unbound atom error or an undefined function error for the perfectly legitamate symbol A+B. Therefore you may choose not to use these facilities with no penalty, similar to other CLISP facilities. You have a lot of flexability in using CLISP character operators. A list can always be substituted for a symbol, and vice versa, without changing the interpretation of a phrase. For example, if the value of (FOO X) is A, and the value of (FIE Y) is B, then (LIST (FOO X)+(FIE Y)) has the same value as (LIST A+B). Note that the first expression is a list of four elements: the atom "LIST", the list "(FOO X)", the atom "+", and the list "(FIE X)", whereas the second expression, (LIST A+B), is a list of only two elements: the symbol "LIST" and the symbol "A+B". Since (LIST (FOO X)+(FIE Y)) is indistinguishable from (LIST (FOO X) + (FIE Y)) because spaces before or after parentheses have no effect on the Interlisp READ program, to be consistent, extra spaces have no effect on atomic operands either. In other words, CLISP will treat (LIST A+ B), (LIST A +B), and (LIST A + B) the same as (LIST A+B). Note: CLISP does not use its own special READ program because this would require you to explicitly identify CLISP expressions, instead of being able to intermix Interlisp and CLISP. +(+ (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] -(- (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] *(* (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] /(/ (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] ( (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] CLISP recognizes +, -, *, /, and as the normal arithmetic infix operators. The - is also recognized as the prefix operator, unary minus. These are converted to PLUS, DIFFERENCE (or in the case of unary minus, MINUS), TIMES, QUOTIENT, and EXPT. Normally, CLISP uses the "generic" arithmetic functions PLUS, TIMES, etc. CLISP contains a facility for declaring which type of arithmetic is to be used, either by making a global declaration, or by separate declarations about individual functions or variables. The usual precedence rules apply (although you can easily change them), i.e., * has higher precedence than + so that A+B*C is the same as A+(B*C), and both * and / are lower than so that 2*X2 is the same as 2*(X2). Operators of the same precedence group from left to right, e.g., A/B/C is equivalent to (A/B)/C. Minus is binary whenever possible, i.e., except when it is the first operator in a list, as in (-A) or (-A), or when it immediately follows another operator, as in A*-B. Note that grouping with parentheses can always be used to override the normal precedence grouping, or when you are not sure how a particular expression will parse. The complete order of precedence for CLISP operators is given below. Note that + in front of a number will disappear when the number is read, e.g., (FOO X +2) is indistinguishable from (FOO X 2). This means that (FOO X +2) will not be interpreted as CLISP, or be converted to (FOO (IPLUS X 2)). Similarly, (FOO X -2) will not be interpreted the same as (FOO X-2). To circumvent this, always type a space between the + or - and a number if an infix operator is intended, e.g., write (FOO X + 2). =(= (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] GT(GT (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] LT(LT (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] GE(GE (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] LE(LE (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] These are infix operators for "Equal", "Greater Than", "Less Than", "Greater Than or Equal", and "Less Than or Equal". GT, LT, GE, and LE are all affected by the same declarations as + and *, with the initial default to use GREATERP and LESSP. Note that only single character operators, e.g., +, , =, etc., can appear in the interior of an atom. All other operators must be set off from identifiers with spaces. For example, XLTY will not be recognized as CLISP. In some cases, DWIM will be able to diagnose this situation as a run-on spelling error, in which case after the atom is split apart, CLISP will be able to perform the indicated transformation. A number of Lisp functions, such as EQUAL, MEMBER, AND, OR, etc., can also be treated as CLISP infix operators. New infix operators can be easily added (see the CLISP Internal Convetions section below). Spelling correction on misspelled infix operators is peformed using CLISPINFIXSPLST as a spelling list. AND is higher than OR, and both AND and OR are lower than the other infix operators, so (X OR Y AND Z) is the same as (X OR (Y AND Z)), and (X AND Y EQUAL Z) is the same as (X AND (Y EQUAL Z)). All of the infix predicates have lower precedence than Interlisp forms, since it is far more common to apply a predicate to two forms, than to use a Boolean as an argument to a function. Therefore, (FOO X GT FIE Y) is translated as ((FOO X) GT (FIE Y)), rather than as (FOO (X GT (FIE Y))). However, you can easily change this. :(: (CLISP Operator) NIL NIL ("21") 6) [CLISP Operator] X:N extracts the Nth element of the list X. FOO:3 specifies the third element of FOO, or (CADDR FOO). If N is less than zero, this indicates elements counting from the end of the list; i.e. FOO:-1 is the last element of FOO. : operators can be nested, so FOO:1:2 means the second element of the first element of FOO, or (CADAR FOO). The : operator can also be used for extracting substructures of records (see Chapter 8). Record operations are implemented by replacing expressions of the form X:FOO by (fetch FOO of X). Both lower- and uppercase are acceptable. : is also used to indicate operations in the pattern match facility (see Chapter 12). X:(& 'A -- 'B) translates to (match X with (& 'A -- 'B)) .(%. (CLISP Operator) NIL NIL ("21") 6) [CLISP Operator] In combination with :, a period can be used to specify the "data path" for record operations. For example, if FOO is a field of the BAR record, X:BAR.FOO is translated into (fetch (BAR FOO) of X). Subrecord fields can be specified with multiple periods: X:BAR.FOO.BAZ translates into (fetch (BAR FOO BAZ) of X). Note: If a record contains fields with periods in them, CLISPIFY will not translate a record operation into a form using periods to specify the data path. For example, CLISPIFY will NOT translate (fetch A.B of X) into X:A.B. ::(:: (CLISP Operator) NIL NIL ("21") 6) [CLISP Operator] X:N, returns the Nth tail of the list X. For example, FOO::3 is (CDDDR FOO), and FOO::-1 is (LAST FOO). [CLISP Operator] is used to indicate assignment. For example, XY translates to (SETQ X Y). If X does not have a value, and is not the name of one of the bound variables of the function in which it appears, spelling correction is attempted. However, since this may simply be a case of assigning an initial value to a new free variable, DWIM will always ask for approval before making the correction. In conjunction with : and ::, can also be used to perform a more general type of assignment, involving structure modification. For example, X:2Y means "make the second element of X be Y", in Interlisp terms (RPLACA (CDR X) Y). Note that the value of this operation is the value of RPLACA, which is (CDR X), rather than Y. Negative numbers can also be used, e.g., X:-2_Y, which translates to (RPLACA (NLEFT X 2) Y). You can indicate you want /RPLACA and /RPLACD used (undoable version of RPLACA and RPLACD, see Chapter 13), or FRPLACA and FRPLACD (fast versions of RPLACA and RPLACD, see Chapter 3), by means of CLISP declarations. The initial default is to use RPLACA and RPLACD. is also used to indicate assignment in record operations (X:FOOY translates to (replace FOO of X with Y).), and pattern match operations (Chapter 12). has different precedence on the left from on the right. On the left, is a "tight" operator, i.e., high precedence, so that A+BC is the same as A+(BC). On the right, has broader scope so that AB+C is the same as A(B+C). On type-in, $FORM (where $ is the escape key) is equivalent to set the "last thing mentioned", i.e., is equivalent to (SET LASTWORD FORM) (see Chapter 20). For example, immediately after examining the value of LONGVARIABLENAME, you could set it by typing $ followed by a form. Note that an atom of the form XY, appearing at the top level of a PROG, will not be recognized as an assignment statement because it will be interpreted as a PROG label by the Interlisp interpreter, and therefore will not cause an error, so DWIM and CLISP will never get to see it. Instead, one must write (XY). < [CLISP Operator] > [CLISP Operator] Angle brackets are used in CLISP to indicate list construction. The appearance of a "<" corresponds to a "(" and indicates that a list is to be constructed containing all the elements up to the corresponding ">". For example, > translates to (LIST A B (LIST C)). ! can be used to indicate that the next expression is to be inserted in the list as a segment, e.g., translates to (CONS A (CONS B C)) and to (APPEND A B (LIST C)). !! is used to indicate that the next expression is to be inserted as a segment, and furthermore, all list structure to its right in the angle brackets is to be physically attached to it, e.g., translates to (NCONC1 A B), and to (NCONC A (APPEND B C)). Not (NCONC (APPEND A B) C), which would have the same value, but would attach C to B, and not attach either to A. Note that <, !, !!, and > need not be separate atoms, for example, may be written equally well as < A B !C >. Also, arbitrary Interlisp or CLISP forms may be used within angle brackets. For example, one can write which translates to (CONS (SETQ FOO (FIE X)) Y). CLISPIFY converts expressions in CONS, LIST, APPEND, NCONC, NCONC1, /NCONC, and /NCONC1 into equivalent CLISP expressions using <, >, !, and !!. Note: brackets differ from other CLISP operators. For example, translates to (LIST A B (QUOTE C)) even though following ', all operators are ignored for the rest of the identifier. (This is true only if a previous unmatched < has been seen, e.g., (PRINT 'A>B) will print the atom A>B.) Note however that D> is equivalent to (LIST A B (QUOTE C>) D). ' [CLISP Operator] CLISP recognizes ' as a prefix operator. ' means QUOTE when it is the first character in an identifier, and is ignored when it is used in the interior of an identifier. Thus, X = 'Y means (EQ X (QUOTE Y)), but X = CAN'T means (EQ X CAN'T), not (EQ X CAN) followed by (QUOTE T). This enables users to have variable and function names with ' in them (so long as the ' is not the first character). Following ', all operators are ignored for the rest of the identifier, e.g., '*A means (QUOTE *A), and 'X=Y means (QUOTE X=Y), not (EQ (QUOTE X) Y). To write (EQ (QUOTE X) Y), one writes Y='X, or 'X =Y. This is one place where an extra space does make a difference. On type-in, '$ (escape) is equivalent to (QUOTE VALUE-OF-LASTWORD) (see Chapter 19). For example, after calling PRETTYPRINT on LONGFUNCTION, you could move its definition to FOO by typing (MOVD '$ 'FOO). Note that this is not (MOVD $ 'FOO), which would be equivalent to (MOVD LONGFUNCTION 'FOO), and would (probably) cause a U.B.A. LONGFUNCTION error, nor MOVD($ FOO), which would actually move the definition of $ to FOO, since DWIM and the spelling corrector would never be invoked. ~ [CLISP Operator] CLISP recognizes ~ as a prefix operator meaning NOT. ~ can negate a form, as in ~(ASSOC X Y), or ~X, or negate an infix operator, e.g., (A ~GT B) is the same as (A LEQ B). Note that ~A = B means (EQ (NOT A) B). When ~ negates an operator, e.g., ~=, ~LT, the two operators are treated as a single operator whose precedence is that of the second operator. When ~ negates a function, e.g., (~FOO X Y), it negates the whole form, i.e., (~(FOO X Y)). Order of Precedence of CLISP Operators: ' : (left precedence) - (unary), ~ *, / +, - (binary) (right precedence) = Interlisp forms LT, GT, EQUAL, MEMBER, etc. AND OR IF, THEN, ELSEIF, ELSE iterative statement operators Declarations 1 CLISP declarations are used to affect the choice of Interlisp function used as the translation of a particular operator. For example, A+B can be translated as either (PLUS A B), (FPLUS A B), or (IPLUS A B), depending on the declaration in effect. Similarly X:1Y can mean (RPLACA X Y), (FRPLACA X Y), or (/RPLACA X Y), and either (NCONC1 A B) or (/NCONC1 A B). Note that the choice of function on all CLISP transformations are affected by the CLISP declaration in effect, i.e., iterative statements, pattern matches, record operations, as well as infix and prefix operators. (CLISPDEC(CLISPDEC (Function) NIL NIL ("21") 8) DECLST) [Function] Puts into effect the declarations in DECLST. CLISPDEC performs spelling corrections on words not recognized as declarations. CLISPDEC is undoable. You can makes (changes) a global declaration by calling CLISPDEC with DECLST a list of declarations, e.g., (CLISPDEC '(FLOATING UNDOABLE)). Changing a global declaration does not affect the speed of subsequent CLISP transformations, since all CLISP transformation are table driven (i.e., property list), and global declarations are accomplished by making the appropriate internal changes to CLISP at the time of the declaration. If a function employs local declarations (described below), there will be a slight loss in efficiency owing to the fact that for each CLISP transformation, the declaration list must be searched for possibly relevant declarations. Declarations are implemented in the order that they are given, so that later declarations override earlier ones. For example, the declaration FAST specifies that FRPLACA, FRPLACD, FMEMB, and FLAST be used in place of RPLACA, RPLACD, MEMB, and LAST; the declaration RPLACA specifies that RPLACA be used. Therefore, the declarations (FAST RPLACA RPLACD) will cause FMEMB, FLAST, RPLACA, and RPLACD to be used. The initial global declaration is MIXED and STANDARD. The table below gives the declarations available in CLISP, and the Interlisp functions they indicate: Declaration: Interlisp Functions to be used: MIXED PLUS, MINUS, DIFFERENCE, TIMES, QUOTIENT, LESSP, GREATERP INTEGER or FIXED IPLUS, IMINUS, IDIFFERENCE, ITIMES, IQUOTIENT, ILESSP, IGREATERP FLOATING FPLUS, FMINUS, FDIFFERENCE, FTIMES, FQUOTIENT, LESSP, FGREATERP FAST FRPLACA, FRPLACD, FMEMB, FLAST, FASSOC UNDOABLE /RPLACA, /RPLACD, /NCONC, /NCONC1, /MAPCONC, /MAPCON STANDARD RPLACA, RPLACD, MEMB, LAST, ASSOC, NCONC, NCONC1, MAPCONC, MAPCON RPLACA, RPLACD, /RPLACA, etc. corresponding function You can also make local declarations affecting a selected function or functions by inserting an expression of the form (CLISP: . DECLARATIONS) immediately following the argument list, i.e., as CADDR of the definition. Such local declarations take precedence over global declarations. Declarations affecting selected variables can be indicated by lists, where the first element is the name of a variable, and the rest of the list the declarations for that variable. For example, (CLISP: FLOATING (X INTEGER)) specifies that in this function integer arithmetic be used for computations involving X, and floating arithmetic for all other computations, where "involving" means where the variable itself is an operand. For example, with the declaration (FLOATING (X INTEGER)) in effect, (FOO X)+(FIE X) would translate to FPLUS, i.e., use floating arithmetic, even though X appears somewhere inside of the operands, whereas X+(FIE X) would translate to IPLUS. If there are declarations involving both operands, e.g., X+Y, with (X FLOATING) (Y INTEGER), whichever appears first in the declaration list will be used. You can also make local record declarations by inserting a record declaration, e.g., (RECORD --), (ARRAYRECORD --), etc., in the local declaration list. In addition, a local declaration of the form (RECORDS A B C) is equivalent to having copies of the global declarations A, B, and C in the local declaration. Local record declarations override global record declarations for the function in which they appear. Local declarations can also be used to override the global setting of certain DWIM/CLISP parameters effective only for transformations within that function, by including in the local declaration an expression of the form (VARIABLE = VALUE), e.g., (PATVARDEFAULT = QUOTE). The CLISP: expression is converted to a comment of a special form recognized by CLISP. Whenever a CLISP transformation that is affected by declarations is about to be performed in a function, this comment will be searched for a relevant declaration, and if one is found, the corresponding function will be used. Otherwise, if none are found, the global declaration(s) currently in effect will be used. Local declarations are effective in the order that they are given, so that later declarations can be used to override earlier ones, e.g., (CLISP: FAST RPLACA RPLACD) specifies that FMEMB, FLAST, RPLACA, and RPLACD be used. An exception to this is that declarations for specific variables take precedence of general, function-wide declarations, regardless of the order of appearance, as in (CLISP: (X INTEGER) FLOATING). CLISPIFY also checks the declarations in effect before selecting an infix operator to ensure that the corresponding CLISP construct would in fact translate back to this form. For example, if a FLOATING declaration is in effect, CLISPIFY will convert (FPLUS X Y) to X+Y, but leave (IPLUS X Y) as is. If (FPLUS X Y) is CLISPIFYed while a FLOATING declaration is under effect, and then the declaration is changed to INTEGER, when X+Y is translated back to Interlisp, it will become (IPLUS X Y). CLISP Operation 1 CLISP is a part of the basic Medley system. Without any special preparations, you can include CLISP constructs in programs, or type them in directly for evaluation (in EVAL or APPLY format), then, when the "error" occurrs, and DWIM is called, it will destructively transform the CLISP to the equivalent Interlisp expression and evaluate the Interlisp expression. CLISP transformations, like all DWIM corrections, are undoable. User approval is not requested, and no message is printed. This entire discussion also applies to CLISP transformation initiated by calls to DWIM from DWIMIFY. However, if a CLISP construct contains an error, an appropriate diagnostic is generated, and the form is left unchanged. For example, if you write (LIST X+Y*), the error diagnostic MISSING OPERAND AT X+Y* IN (LIST X+Y*) would be generated. Similarly, if you write (LAST+EL X), CLISP knows that ((IPLUS LAST EL) X) is not a valid Interlisp expression, so the error diagnostic MISSING OPERATOR IN (LAST+EL X) is generated. (For example, you might have meant to say (LAST+EL*X).) If LAST+EL were the name of a defined function, CLISP would never see this form. Since the bad CLISP transformation might not be CLISP at all, for example, it might be a misspelling of a user function or variable, DWIM holds all CLISP error messages until after trying other corrections. If one of these succeeds, the CLISP message is discarded. Otherwise, if all fail, the message is printed (but no change is made). For example, suppose you type (R/PLACA X Y). CLISP generates a diagnostic, since ((IQUOTIENT R PLACA) X Y) is obviously not right. However, since R/PLACA spelling corrects to /RPLACA, this diagnostic is never printed. Note: CLISP error messages are not printed on type-in. For example, typing X+*Y will just produce a U.B.A. X+*Y message. If a CLISP infix construct is well formed from a syntactic standpoint, but one or both of its operands are atomic and not bound, it is possible that either the operand is misspelled, e.g., you wrote X+YY for X+Y, or that a CLISP transformation operation was not intended at all, but that the entire expression is a misspelling. For the purpose of DWIMIFYing, "not bound" means no top level value, not on list of bound variables built up by DWIMIFY during its analysis of the expression, and not on NOFIXVARSLST, i.e., not previously seen. For example, if you have a variable named LAST-EL, and write (LIST LAST-ELL). Therefore, CLISP computes, but does not actually perform, the indicated infix transformation. DWIM then continues, and if it is able to make another correction, does so, and ignores the CLISP interpretation. For example, with LAST-ELL, the transformation LAST-ELL -> LAST-EL would be found. If no other transformation is found, and DWIM is about to interpret a construct as CLISP for which one of the operands is not bound, DWIM will ask you whether CLISP was intended, in this case by printing LAST-ELL TREAT AS CLISP ?. Note: If more than one infix operator was involved in the CLISP construct, e.g., X+Y+Z, or the operation was an assignment to a variable already noticed, or TREATASCLISPFLG is T (initially NIL), you will simply be informed of the correction, e.g., X+Y+Z TREATED AS CLISP. Otherwise, even if DWIM was enabled in TRUSTING mode, you will be asked to approve the correction. The same sort of procedure is followed with 8 and 9 errors. For example, suppose you write FOO8*X where FOO8 is not bound. The CLISP transformation is noted, and DWIM proceeds. It next asks you to approve FOO8*X -> FOO ( *X. For example, this would make sense if you have (or plan to define) a function named *X. If you refuses, you are asked whether FOO8*X is to be treated as CLISP. Similarly, if FOO8 were the name of a variable, and you write FOOO8*X, you will first be asked to approve FOOO8*X -> FOOO ( XX, and if you refuse, then be offered the FOOO8 -> FOO8 correction. The 8-9 transformation is tried before spelling correction since it is empirically more likely that an unbound atom or undefined function containing an 8 or a 9 is a parenthesis error, rather than a spelling error. CLISP also contains provision for correcting misspellings of infix operators (other than single characters), IF words, and i.s. operators. This is implemented in such a way that the user who does not misspell them is not penalized. For example, if you write IF N = 0 THEN 1 ELSSE N*(FACT N-1) CLISP does not operate by checking each word to see if it is a misspelling of IF, THEN, ELSE, or ELSEIF, since this would seriously degrade CLISP's performance on all IF statements. Instead, CLISP assumes that all of the IF words are spelled correctly, and transforms the expression to (COND ((ZEROP N) 1 ELSSE N*(FACT N-1))). Later, after DWIM cannot find any other interpretation for ELSSE, and using the fact that this atom originally appeared in an IF statement, DWIM attempts spelling correction, using (IF THEN ELSE ELSEIF) for a spelling list. When this is successful, DWIM "fails" all the way back to the original IF statement, changes ELSSE to ELSE, and starts over. Misspellings of AND, OR, LT, GT, etc. are handled similarly. CLISP also contains many Do-What-I-Mean features besides spelling corrections. For example, the form (LIST +X Y) would generate a MISSING OPERATOR error. However, (LIST -X Y) makes sense, if the minus is unary, so DWIM offers this interpretation to you. Another common error, especially for new users, is to write (LIST X*FOO(Y)) or (LIST X*FOO Y), where FOO is the name of a function, instead of (LIST X*(FOO Y)). Therefore, whenever an operand that is not bound is also the name of a function (or corrects to one), the above interpretations are offered. CLISP Translations 1 The translation of CLISP character operators and the CLISP word IF are handled by replacing the CLISP expression with the corresponding Interlisp expression, and discarding the original CLISP. This is done because (1) the CLISP expression is easily recomputable (by CLISPIFY) and (2) the Interlisp expressions are simple and straightforward. Another reason for discarding the original CLISP is that it may contain errors that were corrected in the course of translation (e.g., FOOFOOO:1, N*8FOO X), etc.). If the original CLISP were retained, either you would have to go back and fix these errors by hand, thereby negating the advantage of having DWIM perform these corrections, or else DWIM would have to keep correcting these errors over and over. Note that CLISPIFY is sufficiently fast that it is practical for you to configure your Interlisp system so that all expressions are automatically CLISPIFYed immediately before they are presented to you. For example, you can define an edit macro to use in place of P which calls CLISPIFY on the current expression before printing it. Similarly, you can inform PRETTYPRINT to call CLISPIFY on each expression before printing it, etc. Where (1) or (2) are not the case, e.g., with iterative statements, pattern matches, record expressions, etc. the original CLISP is retained (or a slightly modified version thereof), and the translation is stored elsewhere (by the function CLISPTRAN, in the Miscellaneous Functions and Variables), usually in the hash array CLISPARRAY. The interpreter automatically checks this array when given a form CAR of which is not a function. Similarly, the compiler performs a GETHASH when given a form it does not recognize to see if it has a translation, which is then compiled instead of the form. Whenever you change a CLISP expresson by editing it, the editor automatically deletes its translation (if one exists), so that the next time it is evaluated or DWIMIFIed, the expression will be retranslated (if the value of CLISPRETRANFLG is T, DWIMIFY will also (re)translate any expressions which have translations stored remotely, see the CLISPIFY section). The function PPT and the edit commands PPT and CLISP: are available for examining translations (see the Miscellaneous Functions and Variables section). You can also indicate that you want the original CLISP retained by embedding it in an expression of the form (CLISP . CLISP-EXPRESSION), e.g., (CLISP X:5:3) or (CLISP ). In such cases, the translation will be stored remotely as described above. Furthermore, such expressions will be treated as CLISP even if infix and prefix transformations have been disabled by setting CLISPFLG to NIL (see the Miscellaneous Functions and Variables section). In other words, you can instruct the system to interpret as CLISP infix or prefix constructs only those expressions that are specifically flagged as such. You can also include CLISP declarations by writing (CLISP DECLARATIONS . FORM), e.g., (CLISP (CLISP: FLOATING) ...). These declarations will be used in place of any CLISP declarations in the function definition. This feature provides a way of including CLISP declarations in macro definitions. Note: CLISP translations can also be used to supply an interpretation for function objects, as well as forms, either for function objects that are used openly, i.e., appearing as CAR of form, function objects that are explicitly APPLYed, as with arguments to mapping functions, or function objects contained in function definition cells. In all cases, if CAR of the object is not LAMBDA or NLAMBDA, the interpreter and compiler will check CLISPARRAY. DWIMIFY 1 DWIMIFY is effectively a preprocessor for CLISP. DWIMIFY operates by scanning an expression as though it were being interpreted, and for each form that would generate an error, calling DWIM to "fix" it. DWIMIFY performs all DWIM transformations, not just CLISP transformations, so it does spelling correction, fixes 8-9 errors, handles F/L, etc. Thus you will see the same messages, and be asked for approval in the same situations, as you would if the expression were actually run. If DWIM is unable to make a correction, no message is printed, the form is left as it was, and the analysis proceeds. DWIMIFY knows exactly how the interpreter works. It knows the syntax of PROGs, SELECTQs, LAMBDA expressions, SETQs, et al. It knows how variables are bound, and that the argument of NLAMBDAs are not evaluated (you can inform DWIMIFY of a function or macro's nonstandard binding or evaluation by giving it a suitable INFO property, see below). In the course of its analysis of a particular expression, DWIMIFY builds a list of the bound variables from the LAMBDA expressions and PROGs that it encounters. It uses this list for spelling corrections. DWIMIFY also knows not to try to "correct" variables that are on this list since they would be bound if the expression were actually being run. However, note that DWIMIFY cannot, a priori, know about variables that are used freely but would be bound in a higher function if the expression were evaluated in its normal context. Therefore, DWIMIFY will try to "correct" these variables. Similarly, DWIMIFY will attempt to correct forms for which CAR is undefined, even when the form is not in error from your standpoint, but the corresponding function has simply not yet been defined. Note: DWIMIFY rebinds FIXSPELLDEFAULT to N, so that if you are not at the terminal when DWIMIFYing (or compiling), spelling corrections will not be performed. DWIMIFY will also inform you when it encounters an expression with too many arguments (unless DWIMCHECK#ARGSFLG = NIL), because such an occurrence, although does not cause an error in the Interlisp interpreter, nevertheless is frequently symptomatic of a parenthesis error. For example, if you wrote (CONS (QUOTE FOO X)) instead of (CONS (QUOTE FOO) X), DWIMIFY will print: POSSIBLE PARENTHESIS ERROR IN (QUOTE FOO X) TOO MANY ARGUMENTS (MORE THAN 1) DWIMIFY will also check to see if a PROG label contains a clisp character (unless DWIMCHECKPROGLABELSFLG = NIL, or the label is a member of NOFIXVARSLST), and if so, will alert you by printing the message SUSPICIOUS PROG LABEL, followed by the label. The PROG label will not be treated as CLISP. Note that in most cases, an attempt to transform a form that is already as you intended will have no effect (because there will be nothing to which that form could reasonably be transformed). However, in order to avoid needless calls to DWIM or to avoid possible confusion, you can inform DWIMIFY not to attempt corrections or transformations on certain functions or variables by adding them to the list NOFIXFNSLST or NOFIXVARSLST respectively. Note that you could achieve the same effect by simply setting the corresponding variables, and giving the functions dummy definitions. DWIMIFY will never attempt corrections on global variables, i.e., variables that are a member of the list GLOBALVARS, or have the property GLOBALVAR with value T, on their property list. Similarly, DWIMIFY will not attempt to correct variables declared to be SPECVARS in block declarations or via DECLARE expressions in the function body. You can also declare variables that are simply used freely in a function by using the USEDFREE declaration. DWIMIFY and DWIMIFYFNS (used to DWIMIFY several functions) maintain two internal lists of those functions and variables for which corrections were unsuccessfully attempted. These lists are initialized to the values of NOFIXFNSLST and NOFIXVARSLST. Once an attempt is made to fix a particular function or variable, and the attempt fails, the function or variable is added to the corresponding list, so that on subsequent occurrences (within this call to DWIMIFY or DWIMIFYFNS), no attempt at correction is made. For example, if FOO calls FIE several times, and FIE is undefined at the time FOO is DWIMIFYed, DWIMIFY will not bother with FIE after the first occurrence. In other words, once DWIMIFY "notices" a function or variable, it no longer attempts to correct it. DWIMIFY and DWIMIFYFNS also "notice" free variables that are set in the expression being processed. Moreover, once DWIMIFY "notices" such functions or variables, it subsequently treats them the same as though they were actually defined or set. Note that these internal lists are local to each call to DWIMIFY and DWIMIFYFNS, so that if a function containing FOOO, a misspelled call to FOO, is DWIMIFYed before FOO is defined or mentioned, if the function is DWIMIFYed again after FOO has been defined, the correction will be made. You can undo selected transformations performed by DWIMIFY, as described in Chapter 13. (DWIMIFY(DWIMIFY (Function) NIL NIL ("21") 13) X QUIETFLG L) [Function] Performs all DWIM and CLISP corrections and transformations on X that would be performed if X were run, and prints the result unless QUIETFLG = T. If X is an atom and L is NIL, X is treated as the name of a function, and its entire definition is DWIMIFYed. If X is a list or L is not NIL, X is the expression to be DWIMIFYed. If L is not NIL, it is the edit push-down list leading to X, and is used for determining context, i.e., what bound variables would be in effect when X was evaluated, whether X is a form or sequence of forms, e.g., a COND clause, etc. If X is an iterative statement and L is NIL, DWIMIFY will also print the translation, i.e., what is stored in the hash array. (DWIMIFYFNS(DWIMIFYFNS (Function) NIL NIL ("21") 13) FN1 ... FNN) [NLambda NoSpread Function] DWIMIFYs each of the functions given. If only one argument is given, it is evalued. If its value is a list, the functions on this list are DWIMIFYed. If only one argument is given, it is atomic, its value is not a list, and it is the name of a known file, DWIMIFYFNS will operate on (FILEFNSLST FN1), e.g. (DWIMIFYFNS FOO.LSP) will DWIMIFY every function in the file FOO.LSP. Every 30 seconds, DWIMIFYFNS prints the name of the function it is processing, a la PRETTYPRINT. Value is a list of the functions DWIMIFYed. DWIMINMACROSFLG(DWIMINMACROSFLG (Variable) NIL NIL ("21") 14) [Variable] Controls how DWIMIFY treats the arguments in a "call" to a macro, i.e., where the CAR of the form is undefined, but has a macro definition. If DWIMINMACROSFLG is T, then macros are treated as LAMBDA functions, i.e., the arguments are assumed to be evaluated, which means that DWIMIFY will descend into the argument list. If DWIMINMACROSFLG is NIL, macros are treated as NLAMBDA functions. DWIMINMACROSFLG is initially T. INFO(INFO (Property) NIL NIL ("21") 14) [Property Name] Used to inform DWIMIFY of nonstandard behavior of particular forms with respect to evaluation, binding of arguments, etc. The INFO property of a symbol is a single atom or list of atoms chosen from among the following: EVAL Informs DWIMIFY (and CLISP and Masterscope) that an nlambda function does evaluate its arguments. Can also be placed on a macro name to override the behavior of DWIMINMACROSFLG = NIL. NOEVAL Informs DWIMIFY that a macro does not evaluate all of its arguments, even when DWIMINMACROSFLG = T. BINDS Placed on the INFO property of a function or the CAR of a special form to inform DWIMIFY that the function or form binds variables. In this case, DWIMIFY assumes that CADR of the form is the variable list, i.e., a list of symbols, or lists of the form (VAL VALUE). LAMBDA, NLAMBDA, PROG, and RESETVARS are handled in this fashion. LABELS Informs CLISPIFY that the form interprets top-level symbols as labels, so that CLISPIFY will never introduce an atom (by packing) at the top level of the expression. PROG is handled in this fashion. NOFIXFNSLST (NOFIXFNSLST% (Variable) NIL NIL ("21") 14) [Variable] List of functions that DWIMIFY will not try to correct. NOFIXVARSLST(NOFIXVARSLST (Variable) NIL NIL ("21") 14) [Variable] List of variables that DWIMIFY will not try to correct. NOSPELLFLG(NOSPELLFLG (Variable) NIL NIL ("21") 14) [Variable] If T, DWIMIFY will not perform any spelling corrections. Initially NIL. NOSPELLFLG is reset to T when compiling functions whose definitions are obtained from a file, as opposed to being in core. CLISPHELPFLG (CLISPHELPFLG% (Variable) NIL NIL ("21") 14) [Variable] If NIL, DWIMIFY will not ask you for approval of any CLISP transformations. Instead, in those situations where approval would be required, the effect is the same as though you had been asked and said NO. Initially T. DWIMIFYCOMPFLG (DWIMIFYCOMPFLG% (Variable) NIL NIL ("21") 14) [Variable] If T, DWIMIFY is called before compiling an expression. Initially NIL. DWIMCHECK#ARGSFLG(DWIMCHECK#ARGSFLG (Variable) NIL NIL ("21") 14) [Variable] If T, causes DWIMIFY to check for too many arguments in a form. Initially T. DWIMCHECKPROGLABELSFLG(DWIMCHECKPROGLABELSFLG (Variable) NIL NIL ("21") 14) [Variable] If T, causes DWIMIFY to check whether a PROG label contains a CLISP character. Initially T. DWIMESSGAG(DWIMESSGAG (Variable) NIL NIL ("21") 15) [Variable] If T, suppresses all DWIMIFY error messages. Initially NIL. CLISPRETRANFLG(CLISPRETRANFLG (Variable) NIL NIL ("21") 15) [Variable] If T, informs DWIMIFY to (re)translate all expressions which have remote translations in the CLISP hash array. Initially NIL. CLISPIFY 1 CLISPIFY converts Interlisp expressions to CLISP. Note that the expression given to CLISPIFY need not have originally been input as CLISP, i.e., CLISPIFY can be used on functions that were written before CLISP was even implemented. CLISPIFY is cognizant of declaration rules as well as all of the precedence rules. For example, CLISPIFY will convert (IPLUS A (ITIMES B C)) into A+B*C, but (ITIMES A (IPLUS B C)) into A*(B+C). CLISPIFY handles such cases by first DWIMIFYing the expression. CLISPIFY also knows how to handle expressions consisting of a mixture of Interlisp and CLISP, e.g., (IPLUS A B*C) is converted to A+B*C, but (ITIMES A B+C) to (A*(B+C)). CLISPIFY converts calls to the six basic mapping functions, MAP, MAPC, MAPCAR, MAPLIST, MAPCONC, and MAPCON, into equivalent iterative statements. It also converts certain easily recognizable internal PROG loops to the corresponding iterative statements. CLISPIFY can convert all iterative statements input in CLISP back to CLISP, regardless of how complicated the translation was, because the original CLISP is saved. CLISPIFY is not destructive to the original Interlisp expression, i.e., CLISPIFY produces a new expression without changing the original. The new expression may however contain some "pieces" of the original, since CLISPIFY attempts to minimize the number of CONSes by not copying structure whenever possible. CLISPIFY will not convert expressions appearing as arguments to NLAMBDA functions, except for those functions whose INFO property is or contains the atom EVAL. CLISPIFY also contains built in information enabling it to process special forms such as PROG, SELECTQ, etc. If the INFO property is or contains the atom LABELS, CLISPIFY will never create an atom (by packing) at the top level of the expression. PROG is handled in this fashion. Note: Disabling a CLISP operator with CLDISABLE (see the Miscellaneous Functions and Variables section) will also disable the corresponding CLISPIFY transformation. Thus, if is "turned off", AB will not transform to (SETQ A B), nor vice versa. (CLISPIFY X EDITCHAIN) [Function] Clispifies X. If X is an atom and EDITCHAIN is NIL, X is treated as the name of a function, and its definition (or EXPR property) is clispified. After CLISPIFY has finished, X is redefined (using /PUTD) with its new CLISP definition. The value of CLISPIFY is X. If X is atomic and not the name of a function, spelling correction is attempted. If this fails, an error is generated. If X is a list, or EDITCHAIN is not NIL, X itself is the expression to be clispified. If EDITCHAIN is not NIL, it is the edit push-down list leading to X and is used to determine context as with DWIMIFY, as well as to obtain the local declarations, if any. The value of CLISPIFY is the clispified version of X. (CLISPIFYFNS FN1 ... FNN) [NLambda NoSpread Function] Like DWIMIFYFNS except calls CLISPIFY instead of DWIMIFY. CL:FLG(CL:FLG (Variable) NIL NIL ("21") 16) [Variable] Affects CLISPIFY's handling of forms beginning with CAR, CDR, ... CDDDDR, as well as pattern match and record expressions. If CL:FLG is NIL, these are not transformed into the equivalent : expressions. This will prevent CLISPIFY from constructing any expression employing a : infix operator, e.g., (CADR X) will not be transformed to X:2. If CL:FLG is T, CLISPIFY will convert to : notation only when the argument is atomic or a simple list (a function name and one atomic argument). If CL:FLG is ALL, CLISPIFY will convert to : expressions whenever possible. CL:FLG is initially T. CLREMPARSFLG (CLREMPARSFLG% (Variable) NIL NIL ("21") 16) [Variable] If T, CLISPIFY will remove parentheses in certain cases from simple forms, where "simple" means a function name and one or two atomic arguments. For example, (COND ((ATOM X) --)) will CLISPIFY to (IF ATOM X THEN --). However, if CLREMPARSFLG is set to NIL, CLISPIFY will produce (IF (ATOM X) THEN --). Regardless of the flag setting, the expression can be input in either form. CLREMPARSFLG is initially NIL. CLISPIFYPACKFLG (CLISPIFYPACKFLG% (Variable) NIL NIL ("21") 16) [Variable] CLISPIFYPACKFLG affects the treatment of infix operators with atomic operands. If CLISPIFYPACKFLG is T, CLISPIFY will pack these into single atoms, e.g., (IPLUS A (ITIMES B C)) becomes A+B*C. If CLISPIFYPACKFLG is NIL, no packing is done, e.g., the above becomes A + B * C. CLISPIFYPACKFLG is initially T. CLISPIFYUSERFN(CLISPIFYUSERFN (Variable) NIL NIL ("21") 16) [Variable] If T, causes the function CLISPIFYUSERFN, which should be a function of one argument, to be called on each form (list) not otherwise recognized by CLISPIFY. If a non-NIL value is returned, it is treated as the clispified form. Initially NIL Note that CLISPIFYUSERFN must be both set and defined to use this feature. FUNNYATOMLST(FUNNYATOMLST (Variable) NIL NIL ("21") 16) [Variable] Suppose you have variables named A, B, and A*B. If CLISPIFY were to convert (ITIMES A B) to A*B, A*B would not translate back correctly to (ITIMES A B), since it would be the name of a variable, and therefore would not cause an error. You can prevent this from happening by adding A*B to the list FUNNYATOMLST. Then, (ITIMES A B) would CLISPIFY to A * B. Note that A*B's appearance on FUNNYATOMLST would not enable DWIM and CLISP to decode A*B+C as (IPLUS A*B C); FUNNYATOMLST is used only by CLISPIFY. Thus, if an identifier contains a CLISP character, it should always be separated (with spaces) from other operators. For example, if X* is a variable, you should write (SETQ X* FORM) in CLISP as X* FORM, not X*FORM. In general, it is best to avoid use of identifiers containing CLISP character operators as much as possible. Miscellaneous Functions and Variables 1 CLISPFLG(CLISPFLG (Variable) NIL NIL ("21") 16) [Variable] If CLISPFLG = NIL, disables all CLISP infix or prefix transformations (but does not affect IF/THEN/ELSE statements, or iterative statements). If CLISPFLG = TYPE-IN, CLISP transformations are performed only on expressions that are typed in for evaluation, i.e., not on user programs. If CLISPFLG = T, CLISP transformations are performed on all expressions. The initial value for CLISPFLG is T. CLISPIFYing anything will cause CLISPFLG to be set to T. CLISPCHARS (CLISPCHARS% (Variable) NIL NIL ("21") 17) [Variable] A list of the operators that can appear in the interior of an atom. Currently (+ - * / ~ ' = : < > +- ~= @ !). CLISPCHARRAY(CLISPCHARRAY (Variable) NIL NIL ("21") 17) [Variable] A bit table of the characters on CLISPCHARS used for calls to STRPOSL (Chapter 4). CLISPCHARRAY is initialized by performing (SETQ CLISPCHARRAY (MAKEBITTABLE CLISPCHARS)). (CLISPINFIXSPLST% (Variable) NIL NIL ("21") 17)CLISPINFIXSPLST [Variable] A list of infix operators used for spelling correction. CLISPARRAY (CLISPARRAY (Variable) NIL NIL ("21") 17) [Variable] Hash array used for storing CLISP translations. CLISPARRAY is checked by FAULTEVAL and FAULTAPPLY on erroneous forms before calling DWIM, and by the compiler. (CLEARCLISPARRAY(CLEARCLISPARRAY (Function) NIL NIL (21) 17) NAME --) [Function] Macro and CLISP expansions are cached in CLISPARRAY, the systems CLISP hash array. When anything changes that would invalidate an expansion, it needs to be removed from the cache. CLEARCLISPARRAY does this for you. The system does this automatically whenever you define redefine a CLISP or macro form. If you have changed something that a CLISP word or a macro depends on the system will not be able to detect this, so you will have to invalidate the cahce by calling CLEARCLISPARRAY. You can clear the whole cache by calling (CLRHASH CLISPARRAY). (CLISPTRAN(CLISPTRAN (Function) NIL NIL ("21") 17) X TRAN) [Function] Gives X the translation TRAN by storing (key X, value TRAN) in the hash array CLISPARRAY. CLISPTRAN is called for all CLISP translations, via a non-linked, external function call, so it can be advised. (CLISPDEC(CLISPDEC (Function) NIL NIL ("21") 17) DECLST) [Function] Puts into effect the declarations in DECLST. CLISPDEC performs spelling corrections on words not recognized as declarations. CLISPDEC is undoable. (CLDISABLE(CLDISABLE (Function) NIL NIL ("21") 17) OP) [Function] Disables the CLISP operator OP. For example, (CLDISABLE '-) makes - be just another character. CLDISABLE can be used on all CLISP operators, e.g., infix operators, prefix operators, iterative statement operators, etc. CLDISABLE is undoable. Note: Simply removing a character operator from CLISPCHARS will prevent it from being treated as a CLISP operator when it appears as part of an atom, but it will continue to be an operator when it appears as a separate atom, e.g. (FOO + X) vs FOO+X. CLISPIFTRANFLG(CLISPIFTRANFLG (Variable) NIL NIL ("21") 17) [Variable] Affects handling of translations of IF-THEN-ELSE statements (see Chapter 9). If T, the translations are stored elsewhere, and the (modified) CLISP retained. If NIL, the corresponding COND expression replaces the CLISP. Initially T. CLISPIFYPRETTYFLG(CLISPIFYPRETTYFLG (Variable) NIL NIL ("21") 17) [Variable] If non-NIL, causes PRETTYPRINT (and therefore PP and MAKEFILE) to CLISPIFY selected function definitions before printing them according to the following interpretations of CLISPIFYPRETTYFLG: ALL Clispify all functions. T or EXPRS Clispify all functions currently defined as EXPRs. CHANGES Clispify all functions marked as having been changed. a list Clispify all functions in that list. CLISPIFYPRETTYFLG is (temporarily) reset to T when MAKEFILE is called with the option CLISPIFY, and reset to CHANGES when the file being dumped has the property FILETYPE value CLISP. CLISPIFYPRETTYFLG is initially NIL. Note: If CLISPIFYPRETTYFLG is non-NIL, and the only transformation performed by DWIM are well formed CLISP transformations, i.e., no spelling corrections, the function will not be marked as changed, since it would only have to be re-clispified and re-prettyprinted when the file was written out. (PPT X) [NLambda NoSpread Function] Both a function and an edit macro for prettyprinting translations. It performs a PP after first resetting PRETTYTRANFLG to T, thereby causing any translations to be printed instead of the corresponding CLISP. CLISP:(CL (Editor Command) NIL NIL ("21") 18) [Editor Command] Edit macro that obtains the translation of the correct expression, if any, from CLISPARRAY, and calls EDITE on it. CL(CL (Editor Command) NIL NIL ("21") 18) [Editor Command] Edit macro. Replaces current expression with CLISPIFYed current expression. Current expression can be an element or tail. DW(DW (Editor Command) NIL NIL ("21") 18) [Editor Command] Edit macro. DWIMIFYs current expression, which can be an element (atom or list) or tail. Both CL and DW can be called when the current expression is either an element or a tail and will work properly. Both consult the declarations in the function being edited, if any, and both are undoable. (LOWERCASE(LOWERCASE (Function) NIL NIL ("21") 18) FLG) [Function] If FLG = T, LOWERCASE makes the necessary internal modifications so that CLISPIFY will use lower case versions of AND, OR, IF, THEN, ELSE, ELSEIF, and all i.s. operators. This produces more readable output. Note that you can always type in either upper or lower case (or a combination), regardless of the action of LOWERCASE. If FLG = NIL, CLISPIFY will use uppercase versions of AND, OR, et al. The value of LOWERCASE is its previous "setting". LOWERCASE is undoable. The initial setting for LOWERCASE is T. CLISP Internal Conventions 1 CLISP is almost entirely table driven by the property lists of the corresponding infix or prefix operators. For example, much of the information used for translating the + infix operator is stored on the property list of the symbol "+". Thus it is relatively easy to add new infix or prefix operators or change old ones, simply by adding or changing selected property values. (There is some built in information for handling minus, :, ', and ~, i.e., you could not yourself add such "special" operators, although you can disable or redefine them.) Global declarations operate by changing the LISPFN and CLISPINFIX properties of the appropriate operators. CLISPTYPE(CLISPTYPE (Property) NIL NIL ("21") 18) [Property Name] The property value of the property CLISPTYPE is the precedence number of the operator: higher values have higher precedence, i.e., are tighter. Note that the actual value is unimportant, only the value relative to other operators. For example, CLISPTYPE for :, , and * are 14, 6, and 4 respectively. Operators with the same precedence group left to right, e.g., / also has precedence 4, so A/B*C is (A/B)*C. An operator can have a different left and right precedence by making the value of CLISPTYPE be a dotted pair of two numbers, e.g., CLISPTYPE of is (8 . -12). In this case, CAR is the left precedence, and CDR the right, i.e., CAR is used when comparing with operators on the left, and CDR with operators on the right. For example, A*BC+D is parsed as A*(B(C+D)) because the left precedence of is 8, which is higher than that of *, which is 4. The right precedence of is -12, which is lower than that of +, which is 2. If the CLISPTYPE property for any operator is removed, the corresponding CLISP transformation is disabled, as well as the inverse CLISPIFY transformation. UNARYOP(UNARYOP (Property) NIL NIL ("21") 19) [Property Name] The value of property UNARYOP must be T for unary operators or brackets. The operand is always on the right, i.e., unary operators or brackets are always prefix operators. BROADSCOPE(BROADSCOPE (Property) NIL NIL ("21") 19) [Property Name] The value of property BROADSCOPE is T if the operator has lower precedence than Interlisp forms, e.g., LT, EQUAL, AND, etc. For example, (FOO X AND Y) parses as ((FOO X) AND Y). If the BROADSCOPE property were removed from the property list of AND, (FOO X AND Y) would parse as (FOO (X AND Y)). LISPFN(LISPFN (Property) NIL NIL ("21") 19) [Property Name] The value of the property LISPFN is the name of the function to which the infix operator translates. For example, the value of LISPFN for is EXPT, for ' QUOTE, etc. If the value of the property LISPFN is NIL, the infix operator itself is also the function, e.g., AND, OR, EQUAL. SETFN(SETFN (Property) NIL NIL ("21") 19) [Property Name] If FOO has a SETFN property FIE, then (FOO --)X translates to (FIE -- X). For example, if you make ELT be an infix operator, e.g. #, by putting appropriate CLISPTYPE and LISPFN properties on the property list of # then you can also make # followed by translate to SETA, e.g., X#NY to (SETA X N Y), by putting SETA on the property list of ELT under the property SETFN. Putting the list (ELT) on the property list of SETA under property SETFN will enable SETA forms to CLISPIFY back to ELT's. CLISPINFIX(CLISPINFIX (Property) NIL NIL ("21") 19) [Property Name] The value of this property is the CLISP infix to be used in CLISPIFYing. This property is stored on the property list of the corresponding Interlisp function, e.g., the value of property CLISPINFIX for EXPT is , for QUOTE is ' etc. CLISPWORD(CLISPWORD (Property) NIL NIL ("21") 19) [Property Name] Appears on the property list of clisp operators which can appear as CAR of a form, such as FETCH, REPLACE, IF, iterative statement operators, etc. Value of property is of the form (KEYWORD . NAME), where NAME is the lowercase version of the operator, and KEYWORD is its type, e.g. FORWORD, IFWORD, RECORDWORD, etc. KEYWORD can also be the name of a function. When the atom appears as CAR of a form, the function is applied to the form and the result taken as the correct form. In this case, the function should either physically change the form, or call CLISPTRAN to store the translation. As an example, to make & be an infix character operator meaning OR, you could do the following: (PUTPROP '& 'CLISPTYPE (GETPROP 'OR 'CLISPTYPE)) (PUTPROP '& 'LISPFN 'OR) (PUTPROP '& 'BROADSCOPE T) (PUTPROP 'OR 'CLISPINFIX '&) (SETQ CLISPCHARS (CONS '& CLISPCHARS)) (SETQ CLISPCHARRAY (MAKEBITTABLE CLISPCHARS)) (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "20-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "20-" "")) (192 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "20-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "20-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "20-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "20-" "")) (192 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (408 612 204 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))F PAGEHEADING RIGHTPAGE55552`~~306 -T,HH,HH206206 -,HH56TT26TT5<0<@ PAGEHEADINGLEFTBACKT-T3(T,,ll,,3(T,3(T//,HH,,, PALATINO PALATINO PALATINO TITAN TITAN TITAN TITAN MODERNMODERN -MODERNMODERN -   - IM.CHAP.GETFNMODERN -  HRULE.GETFNMODERN %u:WIM.INDEX.GETFN!i -8    - ) -J -!E @ 2(  O"))"8D - A4K :"L(>  !  ! 9 y 5 ,,P1G;  ,P ;U  \IM.INDEX.GETFN  HRULE.GETFNMODERN - 1 .gJa -I }   VIM.INDEX.GETFN  HRULE.GETFNMODERN - E  l -/    ! - Lu     - ) %IM.INDEX.GETFNTITAN   - %IM.INDEX.GETFN  -%IM.INDEX.GETFNTITAN    -%IM.INDEX.GETFNTITAN   -%IM.INDEX.GETFNPALATINO   8Q -! 8 N  Db9 -D -  -6 -% 7<   %IM.INDEX.GETFNTITAN    -&IM.INDEX.GETFNTITAN   -&IM.INDEX.GETFNTITAN   -&IM.INDEX.GETFNTITAN   -&IM.INDEX.GETFNTITAN   w ." 1^$ .( %IM.INDEX.GETFNTITAN     T2   . V  &IM.INDEX.GETFNTITAN   o  >  :i  &IM.INDEX.GETFN     -   . -1 o## + Q ;/ E7 \ J  "X N =    -  U  e  -  U     4 *   -k )A Y      z  -  ? -B -   B /  #      .@    %    k -# (                HRULE.GETFNMODERN -  -  5 -      &IM.INDEX.GETFNMODERN - -  %H 8: '   " f.       -  -  w  3W -3 '@ U U;_         E1   HRULE.GETFNMODERN -  &. >: Gt ')$L  -V3 * RG 8*4\cW)+,%) m @<5?'>>5]$f  '  HRULE.GETFNMODERN - @  -o J  -m K -EA9{cm    - /z) -  HRULE.GETFNMODERN - + -qBF$TR/D4) .@@   !* 5 "h  c -  &6z - -   -63I -^z9 -# --03&IM.INDEX.GETFNMODERN - -  ?( D+Z) I  -)IM.INDEX.GETFNPALATINO  o -   -8  !.IM.INDEX.GETFNPALATINO  >;N* #IM.INDEX.GETFNPALATINO  iY 6Y *;Q  ?P ,IM.INDEX.GETFN   +IM.INDEX.GETFNPALATINO   -)IM.INDEX.GETFNPALATINO  7 - c -IM.INDEX.GETFN   /IM.INDEX.GETFN  60IM.INDEX.GETFNPALATINO   75IM.INDEX.GETFNPALATINO   . -)IM.INDEX.GETFNPALATINO  -IM.INDEX.GETFNPALATINO   -e  HRULE.GETFNMODERN - M,PY\  4_3@$/8-"Q"M' ]. -   >!/t  0 +*E    - %IM.INDEX.GETFNTITAN   $70!.k    -IM.INDEX.GETFN    O  0IM.INDEX.GETFN  D* .  -IM.INDEX.GETFNTITAN   k E -3 +IM.INDEX.GETFNTITAN   ! '      - !    o &  HRULE.GETFNMODERN - 'IM.INDEX.GETFNTITAN   J ' x :   - +IM.INDEX.GETFN  O#  +IM.INDEX.GETFNMODERN -  ! - - 0IM.INDEX.GETFNTITAN   7  - )IM.INDEX.GETFNTITAN  1 -  -= ,IM.INDEX.GETFNTITAN   ) -* (IM.INDEX.GETFNTITAN    - f 'IM.INDEX.GETFNTITAN   -  %I   (IM.INDEX.GETFNTITAN     s 0 - -IM.INDEX.GETFNTITAN   $ !P+ 0IM.INDEX.GETFNMODERN -    b-7- , +Cw    R T 'IM.INDEX.GETFNMODERN -  P -  'IM.INDEX.GETFNMODERN -  .E 'IM.INDEX.GETFNMODERN -   F  (IM.INDEX.GETFNTITAN      4!aE    '    HRULE.GETFNMODERN - >/i, -* (IM.INDEX.GETFNTITAN    #   _ R (  - .  $'%  r&IM.INDEX.GETFNTITAN      -)IM.INDEX.GETFNTITAN     -B   +INTERLISP-D REFERENCE MANUAL +CLISP + +"20"20. CLISP +2 + +The syntax of Lisp is very simple. It can be described concisely, but it makes Lisp difficult to read and write without tools. Unlike many languages, there are no reserved words in Lisp such as IF, THEN, FOR, DO, etc., nor reserved characters like +, -, =, _, etc. The only components of the language are atoms and delimiters. This eliminates the need for parsers and precedence rules, and makes Lisp programs easy to mainpuilate. For example, a Lisp interpreter can be written in one or two pages of Lisp code. This makes Lisp the most suitable programming language for writing programs that deal with other programs as data. +Human language is based on more complicated structures and relies more on special words to carry the meaning. The definiton of the factorial function looks like this in Lisp: +(COND ((ZEROP N) 1) (T (TIMES N (FACTORIAL ((SUB1 N)))))) +This definition is easy to read for a machine but difficult to read for a human. CLISP(CLISP NIL NIL NIL ("21") 1) is designed to make Interlisp programs easier to read and write. CLISP does this by translating various operators, conditionals, and iterative statements to Interlisp. For example, factorial can be written in CLISP: +(IF N = 0 THEN 1 ELSE N*(FACTORIAL N-1)) +CLISP will translate this expression to the form in the example above. The translation will take place when the form is read so there are no performance penalties. +You should view CLISP as a shothand for produceing Lisp programs. CLISP makes a program easy to read and sometimes more compact. +CLISP is implemented via the error correction machinery in Interlisp (see Chapter 20). Any expression that Interlisp thinks is well-formed will never be seen by CLISP This means that interpreted programs that do not use CLISP constructs do not pay for its availability by slower execution time. In fact, the Interlisp interpreter does not know about CLISP at all. When the interpreter finds an error it calls an error routine which in turn invokes the Do-What-I-Mean (DWIM) analyzer. The DWIM analyzer knows how to deal with CLISP expressions. If the expression in question turns out to be a CLISP construct, the translated form is returned to the interpreter. In addition, the original CLISP expression is modified so that it becomes the correctly translated Interlisp form. In this way, the analysis and translation are done only once. +Integrating CLISP into Medley makes possible Do-What-I-Mean features for CLISP constructs as well as for pure Lisp expressions. For example, if you have defined a function named GET-PARENT, CLISP would know not to attempt to interpret the form (GET-PARENT) as an arithmetic infix operation. (Actually, CLISP would never get to see this form, since it does not contain any errors.) If you mistakenly write (GET-PRAENT), CLISP would know you meant (GET-PARENT), and not (DIFFERENCE GET PRAENT), by using the information that PARENT is not the name of a variable, and that GET-PARENT is the name of a user function whose spelling is "very close" to that of GET-PRAENT. Similarly, by using information about the program's environment not readily available to a preprocessor, CLISP can successfully resolve the following sorts of ambiguities: + 1. (LIST X*FACT N), where FACT is the name of a variable, means (LIST (X*FACT) N). + 2. (LIST X*FACT N), where FACT is not the name of a variable but instead is the name of a function, means (LIST X*(FACT N)), i.e., N is FACT's argument. + 3. (LIST X*FACT(N)), FACT the name of a function (and not the name of a variable), means (LIST X*(FACT N)). + 4. Cases 1, 2 and 3 with FACT misspelled! +The first expression is correct both from the standpoint of CLISP syntax and semantics so the change would be made notification. In the other cases, you would be informed or consulted about what was taking place. For example, suppose you write the expression (LIST X*FCCT N). Assume also that there was both a function named FACT and a variable named FCT. + 1. You will first be asked if FCCT is a misspelling of FCT. If you say YES, the expression will be interpreted as (LIST (X*FCT) N). If you say NO, you will be asked if FCCT was a misspelling of FACT, i.e., if you intended X*FCCT N to mean X*(FACT N). + 2. If you say YES to this question, the indicated transformation will be performed. If you say NO, the system will ask if X*FCCT should be treated as CLISP, since FCCT is not the name of a (bound) variable. + 3. If you say YES, the expression will be transformed, if NO, it will be left alone, i.e., as (LIST X*FCCT N). Note that we have not even considered the case where X*FCCT is itself a misspelling of a variable name, e.g., a variable named XFCT (as with GET-PRAENT). This sort of transformation will be considered after you said NO to X*FCCT N -> X*(FACT N). +The question of whether X*FCCT should be treated as CLISP is important because Interlisp users may have programs that employ identifiers containing CLISP operators. Thus, if CLISP encounters the expression A/B in a context where either A or B are not the names of variables, it will ask you if A/B is intended to be CLISP, in case you really do have a free variable named A/B. +Note: Through the discussion above, we speak of CLISP or DWIM asking you. Actually, if you typed in the expression in question for immediate execution, you are simply informed of the transformation, on the grounds that you would prefer an occasional misinterpretation rather than being continuously bothered, especially since you can always retype what you intended if a mistake occurs, and ask the programmer's assistant to UNDO the effects of the mistaken operations if necessary. For transformations on expressions in your programs, you can tell CLISP whether you wish to operate in CAUTIOUS or TRUSTING mode. In the former case (most typical) you will be asked to approve transformations, in the latter, CLISP will operate as it does on type-in, i.e., perform the transformation after informing you. +CLISP can also handle parentheses errors caused by typing 8 or 9 for ( or ). (On most terminals, 8 and 9 are the lowercase characters for ( and ), i.e., ( and 8 appear on the same key, as do ) and 9.) For example, if you write N*8FACTORIAL N-1, the parentheses error can be detected and fixed before the infix operator * is converted to the Interlisp function TIMES. CLISP is able to distinguish this situation from cases like N*8*X meaning (TIMES N 8 X), or N*8X, where 8X is the name of a variable, again by using information about the programming environment. In fact, by integrating CLISP with DWIM, CLISP has been made sufficiently tolerant of errors that almost everything can be misspelled! For example, CLISP can successfully translate the definition of FACTORIAL: +(IFF N = 0 THENN1 ESLE N*8FACTTORIALNN-1) +to the corresponding COND, while making five spelling corrections and fixing the parenthesis error. CLISP also contains a facility for converting from Interlisp back to CLISP, so that after running the above incorrect definition of FACTORIAL, you could "clispify" the now correct version to obtain (IF N = 0 THEN 1 ELSE N*(FACTORIAL N-1)). +This sort of robustness prevails throughout CLISP. For example, the iterative statement permits you to say things like: +(FOR OLD X FROM M TO N DO (PRINT X) WHILE (PRIMEP X)) +However, you can also write OLD (X_M), (OLD X_M), (OLD (X_M)), permute the order of the operators, e.g., (DO PRINT X TO N FOR OLD X_M WHILE PRIMEP X), omit either or both sets of parentheses, misspell any or all of the operators FOR, OLD, FROM, TO, DO, or WHILE, or leave out the word DO entirely! And, of course, you can also misspell PRINT, PRIMEP, M or N! In this example, the only thing you could not misspell is the first X, since it specifies the name of the variable of iteration. The other two instances of X could be misspelled. +CLISP is well integrated into Medley. For example, the above iterative statement translates into an equivalent Interlisp form using PROG, COND, GO, etc. When the interpreter subsequently encounters this CLISP expression, it automatically obtains and evaluates the translation. Similarly, the compiler "knows" to compile the translated form. However, if you PRETTYPRINT your program, PRETTYPRINT "knows" to print the original CLISP at the corresponding point in your function. Similarly, when you edit your program, the editor keeps the translation invisible to you. If you modify the CLISP, the translation is automatically discarded and recomputed the next time the expression is evaluated. +In short, CLISP is not a language at all, but rather a system. It plays a role analagous to that of the programmer's assistant (Chapter 13). Whereas the programmer's assistant is an invisible intermediary agent between your console requests and the Interlisp executive, CLISP sits between your programs and the Interlisp interpreter. +Only a small effort has been devoted to defining the core syntax of CLISP. Instead, most of the effort has been concentrated on providing a facility which "makes sense" out of the input expressions using context information as well as built-in and acquired information about user and system programs. It has been said that communication is based on the intention of the speaker to produce an effect in the recipient. CLISP operates under the assumption that what you say is intended to represent a meaningful operation, and therefore tries very hard to make sense out of it. The motivation behind CLISP is not to provide you with many different ways of saying the same thing, but to enable you to worry less about the syntactic aspects of your communication with the system. In other words, it gives you a new degree of freedom by permitting you to concentrate more on the problem at hand, rather than on translation into a formal and unambiguous language. +DWIM and CLISP are invoked on iterative statements because CAR of the iterative statement is not the name of a function, and hence generates an error. If you define a function by the same name as an i.s. operator, e.g., WHILE, TO, etc., the operator will no longer have the CLISP interpretation when it appears as CAR of a form, although it will continue to be treated as an i.s. operator if it appears in the interior of an i.s. To alert you, a warning message is printed, e.g., (WHILE DEFINED, THEREFORE DISABLED IN CLISP). +CLISP(CLISP NIL NIL NIL ("21") 4 SUBNAME INTERACTION% WITH% USER SUBTEXT interaction% with% user) Interaction with User +1 + +Syntactically and semantically well formed CLISP transformations are always performed without informing you. Other CLISP transformations described in the previous section, e.g., misspellings of operands, infix operators, parentheses errors, unary minus - binary minus errors, all follow the same protocol as other DWIM transformations (Chapter 19). That is, if DWIM has been enabled in TRUSTING mode, or the transformation is in an expression you typed in for immediate execution, your approval is not requested, but you are informed. However, if the transformation involves a user program, and DWIM was enabled in CAUTIOUS mode, you will be asked to approve. If you say NO, the transformation is not performed. Thus, in the previous section, phrases such as "one of these (transformations) succeeds" and "the transformation LAST-ELL -> LAST-EL would be found" etc., all mean if you are in CAUTIOUS mode and the error is in a program, the corresponding transformation will be performed only if you approve (or defaults by not responding). If you say NO, the procedure followed is the same as though the transformation had not been found. For example, if A*B appears in the function FOO, and B is not bound (and no other transformations are found) you would be asked A*B [IN FOO] TREAT AS CLISP ? (The waiting time on such interactions is three times as long as for simple corrections, i.e., 3*DWIMWAIT). +In certain situations, DWIM asks for approval even if DWIM is enabled in TRUSTING mode. For example, you are always asked to approve a spelling correction that might also be interpreted as a CLISP transformation, as in LAST-ELL -> LAST-EL. +If you approved, A*B would be transformed to (ITIMES A B), which would then cause a U.B.A.B. error in the event that the program was being run (remember the entire discussion also applies to DWIMifying). If you said NO, A*B would be left alone. +If the value of CLISPHELPFLG = NIL (initally T), you will not be asked to approve any CLISP transformation. Instead, in those situations where approval would be required, the effect is the same as though you had been asked and said NO. +CLISP(CLISP NIL NIL NIL ("21") 4 SUBNAME CHARACTER% OPERATORS SUBTEXT character% operators) Character Operators +1 + +CLISP recognizes a number of special characters operators, both prefix and infix, which are translated into common expressions. For example, the character + is recognized to represent addition, so CLISP translates the symbol A+B to the form (IPLUS A B). Note that CLISP is invoked, and this translation is made, only if an error occurs, such as an unbound atom error or an undefined function error for the perfectly legitamate symbol A+B. Therefore you may choose not to use these facilities with no penalty, similar to other CLISP facilities. +You have a lot of flexability in using CLISP character operators. A list can always be substituted for a symbol, and vice versa, without changing the interpretation of a phrase. For example, if the value of (FOO X) is A, and the value of (FIE Y) is B, then (LIST (FOO X)+(FIE Y)) has the same value as (LIST A+B). Note that the first expression is a list of four elements: the atom "LIST", the list "(FOO X)", the atom "+", and the list "(FIE X)", whereas the second expression, (LIST A+B), is a list of only two elements: the symbol "LIST" and the symbol "A+B". Since (LIST (FOO X)+(FIE Y)) is indistinguishable from (LIST (FOO X) + (FIE Y)) because spaces before or after parentheses have no effect on the Interlisp READ program, to be consistent, extra spaces have no effect on atomic operands either. In other words, CLISP will treat (LIST A+ B), (LIST A +B), and (LIST A + B) the same as (LIST A+B). +Note: CLISP does not use its own special READ program because this would require you to explicitly identify CLISP expressions, instead of being able to intermix Interlisp and CLISP. ++(+ (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] +-(- (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] +*(* (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] +/(/ (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] ++( (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] +CLISP recognizes +, -, *, /, and + as the normal arithmetic infix operators. The - is also recognized as the prefix operator, unary minus. These are converted to PLUS, DIFFERENCE (or in the case of unary minus, MINUS), TIMES, QUOTIENT, and EXPT. +Normally, CLISP uses the "generic" arithmetic functions PLUS, TIMES, etc. CLISP contains a facility for declaring which type of arithmetic is to be used, either by making a global declaration, or by separate declarations about individual functions or variables. +The usual precedence rules apply (although you can easily change them), i.e., * has higher precedence than + so that A+B*C is the same as A+(B*C), and both * and / are lower than + so that 2*X+2 is the same as 2*(X+2). Operators of the same precedence group from left to right, e.g., A/B/C is equivalent to (A/B)/C. Minus is binary whenever possible, i.e., except when it is the first operator in a list, as in (-A) or (-A), or when it immediately follows another operator, as in A*-B. Note that grouping with parentheses can always be used to override the normal precedence grouping, or when you are not sure how a particular expression will parse. The complete order of precedence for CLISP operators is given below. +Note that + in front of a number will disappear when the number is read, e.g., (FOO X +2) is indistinguishable from (FOO X 2). This means that (FOO X +2) will not be interpreted as CLISP, or be converted to (FOO (IPLUS X 2)). Similarly, (FOO X -2) will not be interpreted the same as (FOO X-2). To circumvent this, always type a space between the + or - and a number if an infix operator is intended, e.g., write (FOO X + 2). +=(= (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] +GT(GT (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] +LT(LT (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] +GE(GE (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] +LE(LE (CLISP Operator) NIL NIL ("21") 5) [CLISP Operator] +These are infix operators for "Equal", "Greater Than", "Less Than", "Greater Than or Equal", and "Less Than or Equal". +GT, LT, GE, and LE are all affected by the same declarations as + and *, with the initial default to use GREATERP and LESSP. +Note that only single character operators, e.g., +, *, =, etc., can appear in the interior of an atom. All other operators must be set off from identifiers with spaces. For example, XLTY will not be recognized as CLISP. In some cases, DWIM will be able to diagnose this situation as a run-on spelling error, in which case after the atom is split apart, CLISP will be able to perform the indicated transformation. +A number of Lisp functions, such as EQUAL, MEMBER, AND, OR, etc., can also be treated as CLISP infix operators. New infix operators can be easily added (see the CLISP Internal Convetions section below). Spelling correction on misspelled infix operators is peformed using CLISPINFIXSPLST as a spelling list. +AND is higher than OR, and both AND and OR are lower than the other infix operators, so (X OR Y AND Z) is the same as (X OR (Y AND Z)), and (X AND Y EQUAL Z) is the same as (X AND (Y EQUAL Z)). All of the infix predicates have lower precedence than Interlisp forms, since it is far more common to apply a predicate to two forms, than to use a Boolean as an argument to a function. Therefore, (FOO X GT FIE Y) is translated as ((FOO X) GT (FIE Y)), rather than as (FOO (X GT (FIE Y))). However, you can easily change this. +:(: (CLISP Operator) NIL NIL ("21") 6) [CLISP Operator] +X:N extracts the Nth element of the list X. FOO:3 specifies the third element of FOO, or (CADDR FOO). If N is less than zero, this indicates elements counting from the end of the list; i.e. FOO:-1 is the last element of FOO. : operators can be nested, so FOO:1:2 means the second element of the first element of FOO, or (CADAR FOO). +The : operator can also be used for extracting substructures of records (see Chapter 8). Record operations are implemented by replacing expressions of the form X:FOO by (fetch FOO of X). Both lower- and uppercase are acceptable. +: is also used to indicate operations in the pattern match facility (see Chapter 12). X:(& 'A -- 'B) translates to (match X with (& 'A -- 'B)) +.(%. (CLISP Operator) NIL NIL ("21") 6) [CLISP Operator] +In combination with :, a period can be used to specify the "data path" for record operations. For example, if FOO is a field of the BAR record, X:BAR.FOO is translated into (fetch (BAR FOO) of X). Subrecord fields can be specified with multiple periods: X:BAR.FOO.BAZ translates into (fetch (BAR FOO BAZ) of X). +Note: If a record contains fields with periods in them, CLISPIFY will not translate a record operation into a form using periods to specify the data path. For example, CLISPIFY will NOT translate (fetch A.B of X) into X:A.B. +::(:: (CLISP Operator) NIL NIL ("21") 6) [CLISP Operator] +X:N, returns the Nth tail of the list X. For example, FOO::3 is (CDDDR FOO), and FOO::-1 is (LAST FOO). +_ [CLISP Operator] +_ is used to indicate assignment. For example, X_Y translates to (SETQ X Y). If X does not have a value, and is not the name of one of the bound variables of the function in which it appears, spelling correction is attempted. However, since this may simply be a case of assigning an initial value to a new free variable, DWIM will always ask for approval before making the correction. +In conjunction with : and ::, _ can also be used to perform a more general type of assignment, involving structure modification. For example, X:2_Y means "make the second element of X be Y", in Interlisp terms (RPLACA (CDR X) Y). Note that the value of this operation is the value of RPLACA, which is (CDR X), rather than Y. Negative numbers can also be used, e.g., X:-2Y, which translates to (RPLACA (NLEFT X 2) Y). +You can indicate you want /RPLACA and /RPLACD used (undoable version of RPLACA and RPLACD, see Chapter 13), or FRPLACA and FRPLACD (fast versions of RPLACA and RPLACD, see Chapter 3), by means of CLISP declarations. The initial default is to use RPLACA and RPLACD. +_ is also used to indicate assignment in record operations (X:FOO_Y translates to (replace FOO of X with Y).), and pattern match operations (Chapter 12). +_ has different precedence on the left from on the right. On the left,_ is a "tight" operator, i.e., high precedence, so that A+B_C is the same as A+(B_C). On the right, _ has broader scope so that A_B+C is the same as A_(B+C). +On type-in, $_FORM (where $ is the escape key) is equivalent to set the "last thing mentioned", i.e., is equivalent to (SET LASTWORD FORM) (see Chapter 20). For example, immediately after examining the value of LONGVARIABLENAME, you could set it by typing $_ followed by a form. +Note that an atom of the form X_Y, appearing at the top level of a PROG, will not be recognized as an assignment statement because it will be interpreted as a PROG label by the Interlisp interpreter, and therefore will not cause an error, so DWIM and CLISP will never get to see it. Instead, one must write (X_Y). +< [CLISP Operator] +> [CLISP Operator] +Angle brackets are used in CLISP to indicate list construction. The appearance of a "<" corresponds to a "(" and indicates that a list is to be constructed containing all the elements up to the corresponding ">". For example, > translates to (LIST A B (LIST C)). ! can be used to indicate that the next expression is to be inserted in the list as a segment, e.g., translates to (CONS A (CONS B C)) and to (APPEND A B (LIST C)). !! is used to indicate that the next expression is to be inserted as a segment, and furthermore, all list structure to its right in the angle brackets is to be physically attached to it, e.g., translates to (NCONC1 A B), and to (NCONC A (APPEND B C)). Not (NCONC (APPEND A B) C), which would have the same value, but would attach C to B, and not attach either to A. Note that <, !, !!, and > need not be separate atoms, for example, may be written equally well as < A B !C >. Also, arbitrary Interlisp or CLISP forms may be used within angle brackets. For example, one can write which translates to (CONS (SETQ FOO (FIE X)) Y). CLISPIFY converts expressions in CONS, LIST, APPEND, NCONC, NCONC1, /NCONC, and /NCONC1 into equivalent CLISP expressions using <, >, !, and !!. +Note: brackets differ from other CLISP operators. For example, translates to (LIST A B (QUOTE C)) even though following ', all operators are ignored for the rest of the identifier. (This is true only if a previous unmatched < has been seen, e.g., (PRINT 'A>B) will print the atom A>B.) Note however that D> is equivalent to (LIST A B (QUOTE C>) D). +' [CLISP Operator] +CLISP recognizes ' as a prefix operator. ' means QUOTE when it is the first character in an identifier, and is ignored when it is used in the interior of an identifier. Thus, X = 'Y means (EQ X (QUOTE Y)), but X = CAN'T means (EQ X CAN'T), not (EQ X CAN) followed by (QUOTE T). This enables users to have variable and function names with ' in them (so long as the ' is not the first character). +Following ', all operators are ignored for the rest of the identifier, e.g., '*A means (QUOTE *A), and 'X=Y means (QUOTE X=Y), not (EQ (QUOTE X) Y). To write (EQ (QUOTE X) Y), one writes Y='X, or 'X =Y. This is one place where an extra space does make a difference. +On type-in, '$ (escape) is equivalent to (QUOTE VALUE-OF-LASTWORD) (see Chapter 19). For example, after calling PRETTYPRINT on LONGFUNCTION, you could move its definition to FOO by typing (MOVD '$ 'FOO). +Note that this is not (MOVD $ 'FOO), which would be equivalent to (MOVD LONGFUNCTION 'FOO), and would (probably) cause a U.B.A. LONGFUNCTION error, nor MOVD($ FOO), which would actually move the definition of $ to FOO, since DWIM and the spelling corrector would never be invoked. +~ [CLISP Operator] +CLISP recognizes ~ as a prefix operator meaning NOT. ~ can negate a form, as in ~(ASSOC X Y), or ~X, or negate an infix operator, e.g., (A ~GT B) is the same as (A LEQ B). Note that ~A = B means (EQ (NOT A) B). +When ~ negates an operator, e.g., ~=, ~LT, the two operators are treated as a single operator whose precedence is that of the second operator. When ~ negates a function, e.g., (~FOO X Y), it negates the whole form, i.e., (~(FOO X Y)). +Order of Precedence of CLISP Operators: +' +: +_ (left precedence) +- (unary), ~ +^ +*, / ++, - (binary) +_ (right precedence) += +Interlisp forms +LT, GT, EQUAL, MEMBER, etc. +AND +OR +IF, THEN, ELSEIF, ELSE +iterative statement operators + +Declarations +1 + +CLISP declarations are used to affect the choice of Interlisp function used as the translation of a particular operator. For example, A+B can be translated as either (PLUS A B), (FPLUS A B), or (IPLUS A B), depending on the declaration in effect. Similarly X:1_Y can mean (RPLACA X Y), (FRPLACA X Y), or (/RPLACA X Y), and either (NCONC1 A B) or (/NCONC1 A B). Note that the choice of function on all CLISP transformations are affected by the CLISP declaration in effect, i.e., iterative statements, pattern matches, record operations, as well as infix and prefix operators. +(CLISPDEC(CLISPDEC (Function) NIL NIL ("21") 8) DECLST) [Function] +Puts into effect the declarations in DECLST. CLISPDEC performs spelling corrections on words not recognized as declarations. CLISPDEC is undoable. +You can makes (changes) a global declaration by calling CLISPDEC with DECLST a list of declarations, e.g., (CLISPDEC '(FLOATING UNDOABLE)). Changing a global declaration does not affect the speed of subsequent CLISP transformations, since all CLISP transformation are table driven (i.e., property list), and global declarations are accomplished by making the appropriate internal changes to CLISP at the time of the declaration. If a function employs local declarations (described below), there will be a slight loss in efficiency owing to the fact that for each CLISP transformation, the declaration list must be searched for possibly relevant declarations. +Declarations are implemented in the order that they are given, so that later declarations override earlier ones. For example, the declaration FAST specifies that FRPLACA, FRPLACD, FMEMB, and FLAST be used in place of RPLACA, RPLACD, MEMB, and LAST; the declaration RPLACA specifies that RPLACA be used. Therefore, the declarations (FAST RPLACA RPLACD) will cause FMEMB, FLAST, RPLACA, and RPLACD to be used. +The initial global declaration is MIXED and STANDARD. +The table below gives the declarations available in CLISP, and the Interlisp functions they indicate: + Declaration: Interlisp Functions to be used: + MIXED PLUS, MINUS, DIFFERENCE, TIMES, QUOTIENT, LESSP, GREATERP + INTEGER or FIXED IPLUS, IMINUS, IDIFFERENCE, ITIMES, IQUOTIENT, ILESSP, IGREATERP + FLOATING FPLUS, FMINUS, FDIFFERENCE, FTIMES, FQUOTIENT, LESSP, FGREATERP + FAST FRPLACA, FRPLACD, FMEMB, FLAST, FASSOC + UNDOABLE /RPLACA, /RPLACD, /NCONC, /NCONC1, /MAPCONC, /MAPCON + STANDARD RPLACA, RPLACD, MEMB, LAST, ASSOC, NCONC, NCONC1, MAPCONC, MAPCON + RPLACA, RPLACD, + /RPLACA, etc. corresponding function +You can also make local declarations affecting a selected function or functions by inserting an expression of the form (CLISP: . DECLARATIONS) immediately following the argument list, i.e., as CADDR of the definition. Such local declarations take precedence over global declarations. Declarations affecting selected variables can be indicated by lists, where the first element is the name of a variable, and the rest of the list the declarations for that variable. For example, (CLISP: FLOATING (X INTEGER)) specifies that in this function integer arithmetic be used for computations involving X, and floating arithmetic for all other computations, where "involving" means where the variable itself is an operand. For example, with the declaration (FLOATING (X INTEGER)) in effect, (FOO X)+(FIE X) would translate to FPLUS, i.e., use floating arithmetic, even though X appears somewhere inside of the operands, whereas X+(FIE X) would translate to IPLUS. If there are declarations involving both operands, e.g., X+Y, with (X FLOATING) (Y INTEGER), whichever appears first in the declaration list will be used. +You can also make local record declarations by inserting a record declaration, e.g., (RECORD --), (ARRAYRECORD --), etc., in the local declaration list. In addition, a local declaration of the form (RECORDS A B C) is equivalent to having copies of the global declarations A, B, and C in the local declaration. Local record declarations override global record declarations for the function in which they appear. Local declarations can also be used to override the global setting of certain DWIM/CLISP parameters effective only for transformations within that function, by including in the local declaration an expression of the form (VARIABLE = VALUE), e.g., (PATVARDEFAULT = QUOTE). +The CLISP: expression is converted to a comment of a special form recognized by CLISP. Whenever a CLISP transformation that is affected by declarations is about to be performed in a function, this comment will be searched for a relevant declaration, and if one is found, the corresponding function will be used. Otherwise, if none are found, the global declaration(s) currently in effect will be used. +Local declarations are effective in the order that they are given, so that later declarations can be used to override earlier ones, e.g., (CLISP: FAST RPLACA RPLACD) specifies that FMEMB, FLAST, RPLACA, and RPLACD be used. An exception to this is that declarations for specific variables take precedence of general, function-wide declarations, regardless of the order of appearance, as in (CLISP: (X INTEGER) FLOATING). +CLISPIFY also checks the declarations in effect before selecting an infix operator to ensure that the corresponding CLISP construct would in fact translate back to this form. For example, if a FLOATING declaration is in effect, CLISPIFY will convert (FPLUS X Y) to X+Y, but leave (IPLUS X Y) as is. If (FPLUS X Y) is CLISPIFYed while a FLOATING declaration is under effect, and then the declaration is changed to INTEGER, when X+Y is translated back to Interlisp, it will become (IPLUS X Y). +CLISP Operation +1 + +CLISP is a part of the basic Medley system. Without any special preparations, you can include CLISP constructs in programs, or type them in directly for evaluation (in EVAL or APPLY format), then, when the "error" occurrs, and DWIM is called, it will destructively transform the CLISP to the equivalent Interlisp expression and evaluate the Interlisp expression. CLISP transformations, like all DWIM corrections, are undoable. User approval is not requested, and no message is printed. This entire discussion also applies to CLISP transformation initiated by calls to DWIM from DWIMIFY. +However, if a CLISP construct contains an error, an appropriate diagnostic is generated, and the form is left unchanged. For example, if you write (LIST X+Y*), the error diagnostic MISSING OPERAND AT X+Y* IN (LIST X+Y*) would be generated. Similarly, if you write (LAST+EL X), CLISP knows that ((IPLUS LAST EL) X) is not a valid Interlisp expression, so the error diagnostic MISSING OPERATOR IN (LAST+EL X) is generated. (For example, you might have meant to say (LAST+EL*X).) If LAST+EL were the name of a defined function, CLISP would never see this form. +Since the bad CLISP transformation might not be CLISP at all, for example, it might be a misspelling of a user function or variable, DWIM holds all CLISP error messages until after trying other corrections. If one of these succeeds, the CLISP message is discarded. Otherwise, if all fail, the message is printed (but no change is made). For example, suppose you type (R/PLACA X Y). CLISP generates a diagnostic, since ((IQUOTIENT R PLACA) X Y) is obviously not right. However, since R/PLACA spelling corrects to /RPLACA, this diagnostic is never printed. +Note: CLISP error messages are not printed on type-in. For example, typing X+*Y will just produce a U.B.A. X+*Y message. +If a CLISP infix construct is well formed from a syntactic standpoint, but one or both of its operands are atomic and not bound, it is possible that either the operand is misspelled, e.g., you wrote X+YY for X+Y, or that a CLISP transformation operation was not intended at all, but that the entire expression is a misspelling. For the purpose of DWIMIFYing, "not bound" means no top level value, not on list of bound variables built up by DWIMIFY during its analysis of the expression, and not on NOFIXVARSLST, i.e., not previously seen. +For example, if you have a variable named LAST-EL, and write (LIST LAST-ELL). Therefore, CLISP computes, but does not actually perform, the indicated infix transformation. DWIM then continues, and if it is able to make another correction, does so, and ignores the CLISP interpretation. For example, with LAST-ELL, the transformation LAST-ELL -> LAST-EL would be found. +If no other transformation is found, and DWIM is about to interpret a construct as CLISP for which one of the operands is not bound, DWIM will ask you whether CLISP was intended, in this case by printing LAST-ELL TREAT AS CLISP ?. +Note: If more than one infix operator was involved in the CLISP construct, e.g., X+Y+Z, or the operation was an assignment to a variable already noticed, or TREATASCLISPFLG is T (initially NIL), you will simply be informed of the correction, e.g., X+Y+Z TREATED AS CLISP. Otherwise, even if DWIM was enabled in TRUSTING mode, you will be asked to approve the correction. +The same sort of procedure is followed with 8 and 9 errors. For example, suppose you write FOO8*X where FOO8 is not bound. The CLISP transformation is noted, and DWIM proceeds. It next asks you to approve FOO8*X -> FOO ( *X. For example, this would make sense if you have (or plan to define) a function named *X. If you refuses, you are asked whether FOO8*X is to be treated as CLISP. Similarly, if FOO8 were the name of a variable, and you write FOOO8*X, you will first be asked to approve FOOO8*X -> FOOO ( XX, and if you refuse, then be offered the FOOO8 -> FOO8 correction. The 8-9 transformation is tried before spelling correction since it is empirically more likely that an unbound atom or undefined function containing an 8 or a 9 is a parenthesis error, rather than a spelling error. +CLISP also contains provision for correcting misspellings of infix operators (other than single characters), IF words, and i.s. operators. This is implemented in such a way that the user who does not misspell them is not penalized. For example, if you write IF N = 0 THEN 1 ELSSE N*(FACT N-1) CLISP does not operate by checking each word to see if it is a misspelling of IF, THEN, ELSE, or ELSEIF, since this would seriously degrade CLISP's performance on all IF statements. Instead, CLISP assumes that all of the IF words are spelled correctly, and transforms the expression to (COND ((ZEROP N) 1 ELSSE N*(FACT N-1))). Later, after DWIM cannot find any other interpretation for ELSSE, and using the fact that this atom originally appeared in an IF statement, DWIM attempts spelling correction, using (IF THEN ELSE ELSEIF) for a spelling list. When this is successful, DWIM "fails" all the way back to the original IF statement, changes ELSSE to ELSE, and starts over. Misspellings of AND, OR, LT, GT, etc. are handled similarly. +CLISP also contains many Do-What-I-Mean features besides spelling corrections. For example, the form (LIST +X Y) would generate a MISSING OPERATOR error. However, (LIST -X Y) makes sense, if the minus is unary, so DWIM offers this interpretation to you. Another common error, especially for new users, is to write (LIST X*FOO(Y)) or (LIST X*FOO Y), where FOO is the name of a function, instead of (LIST X*(FOO Y)). Therefore, whenever an operand that is not bound is also the name of a function (or corrects to one), the above interpretations are offered. +CLISP Translations +1 + +The translation of CLISP character operators and the CLISP word IF are handled by replacing the CLISP expression with the corresponding Interlisp expression, and discarding the original CLISP. This is done because (1) the CLISP expression is easily recomputable (by CLISPIFY) and (2) the Interlisp expressions are simple and straightforward. Another reason for discarding the original CLISP is that it may contain errors that were corrected in the course of translation (e.g., FOO_FOOO:1, N*8FOO X), etc.). If the original CLISP were retained, either you would have to go back and fix these errors by hand, thereby negating the advantage of having DWIM perform these corrections, or else DWIM would have to keep correcting these errors over and over. +Note that CLISPIFY is sufficiently fast that it is practical for you to configure your Interlisp system so that all expressions are automatically CLISPIFYed immediately before they are presented to you. For example, you can define an edit macro to use in place of P which calls CLISPIFY on the current expression before printing it. Similarly, you can inform PRETTYPRINT to call CLISPIFY on each expression before printing it, etc. +Where (1) or (2) are not the case, e.g., with iterative statements, pattern matches, record expressions, etc. the original CLISP is retained (or a slightly modified version thereof), and the translation is stored elsewhere (by the function CLISPTRAN, in the Miscellaneous Functions and Variables), usually in the hash array CLISPARRAY. The interpreter automatically checks this array when given a form CAR of which is not a function. Similarly, the compiler performs a GETHASH when given a form it does not recognize to see if it has a translation, which is then compiled instead of the form. Whenever you change a CLISP expresson by editing it, the editor automatically deletes its translation (if one exists), so that the next time it is evaluated or DWIMIFIed, the expression will be retranslated (if the value of CLISPRETRANFLG is T, DWIMIFY will also (re)translate any expressions which have translations stored remotely, see the CLISPIFY section). The function PPT and the edit commands PPT and CLISP: are available for examining translations (see the Miscellaneous Functions and Variables section). +You can also indicate that you want the original CLISP retained by embedding it in an expression of the form (CLISP . CLISP-EXPRESSION), e.g., (CLISP X:5:3) or (CLISP ). In such cases, the translation will be stored remotely as described above. Furthermore, such expressions will be treated as CLISP even if infix and prefix transformations have been disabled by setting CLISPFLG to NIL (see the Miscellaneous Functions and Variables section). In other words, you can instruct the system to interpret as CLISP infix or prefix constructs only those expressions that are specifically flagged as such. You can also include CLISP declarations by writing (CLISP DECLARATIONS . FORM), e.g., (CLISP (CLISP: FLOATING) ...). These declarations will be used in place of any CLISP declarations in the function definition. This feature provides a way of including CLISP declarations in macro definitions. +Note: CLISP translations can also be used to supply an interpretation for function objects, as well as forms, either for function objects that are used openly, i.e., appearing as CAR of form, function objects that are explicitly APPLYed, as with arguments to mapping functions, or function objects contained in function definition cells. In all cases, if CAR of the object is not LAMBDA or NLAMBDA, the interpreter and compiler will check CLISPARRAY. +DWIMIFY +1 + +DWIMIFY is effectively a preprocessor for CLISP. DWIMIFY operates by scanning an expression as though it were being interpreted, and for each form that would generate an error, calling DWIM to "fix" it. DWIMIFY performs all DWIM transformations, not just CLISP transformations, so it does spelling correction, fixes 8-9 errors, handles F/L, etc. Thus you will see the same messages, and be asked for approval in the same situations, as you would if the expression were actually run. If DWIM is unable to make a correction, no message is printed, the form is left as it was, and the analysis proceeds. +DWIMIFY knows exactly how the interpreter works. It knows the syntax of PROGs, SELECTQs, LAMBDA expressions, SETQs, et al. It knows how variables are bound, and that the argument of NLAMBDAs are not evaluated (you can inform DWIMIFY of a function or macro's nonstandard binding or evaluation by giving it a suitable INFO property, see below). In the course of its analysis of a particular expression, DWIMIFY builds a list of the bound variables from the LAMBDA expressions and PROGs that it encounters. It uses this list for spelling corrections. DWIMIFY also knows not to try to "correct" variables that are on this list since they would be bound if the expression were actually being run. However, note that DWIMIFY cannot, a priori, know about variables that are used freely but would be bound in a higher function if the expression were evaluated in its normal context. Therefore, DWIMIFY will try to "correct" these variables. Similarly, DWIMIFY will attempt to correct forms for which CAR is undefined, even when the form is not in error from your standpoint, but the corresponding function has simply not yet been defined. +Note: DWIMIFY rebinds FIXSPELLDEFAULT to N, so that if you are not at the terminal when DWIMIFYing (or compiling), spelling corrections will not be performed. +DWIMIFY will also inform you when it encounters an expression with too many arguments (unless DWIMCHECK#ARGSFLG = NIL), because such an occurrence, although does not cause an error in the Interlisp interpreter, nevertheless is frequently symptomatic of a parenthesis error. For example, if you wrote (CONS (QUOTE FOO X)) instead of (CONS (QUOTE FOO) X), DWIMIFY will print: +POSSIBLE PARENTHESIS ERROR IN +(QUOTE FOO X) +TOO MANY ARGUMENTS (MORE THAN 1) +DWIMIFY will also check to see if a PROG label contains a clisp character (unless DWIMCHECKPROGLABELSFLG = NIL, or the label is a member of NOFIXVARSLST), and if so, will alert you by printing the message SUSPICIOUS PROG LABEL, followed by the label. The PROG label will not be treated as CLISP. +Note that in most cases, an attempt to transform a form that is already as you intended will have no effect (because there will be nothing to which that form could reasonably be transformed). However, in order to avoid needless calls to DWIM or to avoid possible confusion, you can inform DWIMIFY not to attempt corrections or transformations on certain functions or variables by adding them to the list NOFIXFNSLST or NOFIXVARSLST respectively. Note that you could achieve the same effect by simply setting the corresponding variables, and giving the functions dummy definitions. +DWIMIFY will never attempt corrections on global variables, i.e., variables that are a member of the list GLOBALVARS, or have the property GLOBALVAR with value T, on their property list. Similarly, DWIMIFY will not attempt to correct variables declared to be SPECVARS in block declarations or via DECLARE expressions in the function body. You can also declare variables that are simply used freely in a function by using the USEDFREE declaration. +DWIMIFY and DWIMIFYFNS (used to DWIMIFY several functions) maintain two internal lists of those functions and variables for which corrections were unsuccessfully attempted. These lists are initialized to the values of NOFIXFNSLST and NOFIXVARSLST. Once an attempt is made to fix a particular function or variable, and the attempt fails, the function or variable is added to the corresponding list, so that on subsequent occurrences (within this call to DWIMIFY or DWIMIFYFNS), no attempt at correction is made. For example, if FOO calls FIE several times, and FIE is undefined at the time FOO is DWIMIFYed, DWIMIFY will not bother with FIE after the first occurrence. In other words, once DWIMIFY "notices" a function or variable, it no longer attempts to correct it. DWIMIFY and DWIMIFYFNS also "notice" free variables that are set in the expression being processed. Moreover, once DWIMIFY "notices" such functions or variables, it subsequently treats them the same as though they were actually defined or set. +Note that these internal lists are local to each call to DWIMIFY and DWIMIFYFNS, so that if a function containing FOOO, a misspelled call to FOO, is DWIMIFYed before FOO is defined or mentioned, if the function is DWIMIFYed again after FOO has been defined, the correction will be made. +You can undo selected transformations performed by DWIMIFY, as described in Chapter 13. +(DWIMIFY(DWIMIFY (Function) NIL NIL ("21") 13) X QUIETFLG L) [Function] +Performs all DWIM and CLISP corrections and transformations on X that would be performed if X were run, and prints the result unless QUIETFLG = T. +If X is an atom and L is NIL, X is treated as the name of a function, and its entire definition is DWIMIFYed. If X is a list or L is not NIL, X is the expression to be DWIMIFYed. If L is not NIL, it is the edit push-down list leading to X, and is used for determining context, i.e., what bound variables would be in effect when X was evaluated, whether X is a form or sequence of forms, e.g., a COND clause, etc. +If X is an iterative statement and L is NIL, DWIMIFY will also print the translation, i.e., what is stored in the hash array. +(DWIMIFYFNS(DWIMIFYFNS (Function) NIL NIL ("21") 13) FN1 ... FNN) [NLambda NoSpread Function] +DWIMIFYs each of the functions given. If only one argument is given, it is evalued. If its value is a list, the functions on this list are DWIMIFYed. If only one argument is given, it is atomic, its value is not a list, and it is the name of a known file, DWIMIFYFNS will operate on (FILEFNSLST FN1), e.g. (DWIMIFYFNS FOO.LSP) will DWIMIFY every function in the file FOO.LSP. +Every 30 seconds, DWIMIFYFNS prints the name of the function it is processing, a la PRETTYPRINT. +Value is a list of the functions DWIMIFYed. +DWIMINMACROSFLG(DWIMINMACROSFLG (Variable) NIL NIL ("21") 14) [Variable] +Controls how DWIMIFY treats the arguments in a "call" to a macro, i.e., where the CAR of the form is undefined, but has a macro definition. If DWIMINMACROSFLG is T, then macros are treated as LAMBDA functions, i.e., the arguments are assumed to be evaluated, which means that DWIMIFY will descend into the argument list. If DWIMINMACROSFLG is NIL, macros are treated as NLAMBDA functions. DWIMINMACROSFLG is initially T. +INFO(INFO (Property) NIL NIL ("21") 14) [Property Name] +Used to inform DWIMIFY of nonstandard behavior of particular forms with respect to evaluation, binding of arguments, etc. The INFO property of a symbol is a single atom or list of atoms chosen from among the following: + EVAL Informs DWIMIFY (and CLISP and Masterscope) that an nlambda function does evaluate its arguments. Can also be placed on a macro name to override the behavior of DWIMINMACROSFLG = NIL. + NOEVAL Informs DWIMIFY that a macro does not evaluate all of its arguments, even when DWIMINMACROSFLG = T. + BINDS Placed on the INFO property of a function or the CAR of a special form to inform DWIMIFY that the function or form binds variables. In this case, DWIMIFY assumes that CADR of the form is the variable list, i.e., a list of symbols, or lists of the form (VAL VALUE). LAMBDA, NLAMBDA, PROG, and RESETVARS are handled in this fashion. + LABELS Informs CLISPIFY that the form interprets top-level symbols as labels, so that CLISPIFY will never introduce an atom (by packing) at the top level of the expression. PROG is handled in this fashion. +NOFIXFNSLST (NOFIXFNSLST% (Variable) NIL NIL ("21") 14) [Variable] +List of functions that DWIMIFY will not try to correct. +NOFIXVARSLST(NOFIXVARSLST (Variable) NIL NIL ("21") 14) [Variable] +List of variables that DWIMIFY will not try to correct. +NOSPELLFLG(NOSPELLFLG (Variable) NIL NIL ("21") 14) [Variable] +If T, DWIMIFY will not perform any spelling corrections. Initially NIL. NOSPELLFLG is reset to T when compiling functions whose definitions are obtained from a file, as opposed to being in core. +CLISPHELPFLG (CLISPHELPFLG% (Variable) NIL NIL ("21") 14) [Variable] +If NIL, DWIMIFY will not ask you for approval of any CLISP transformations. Instead, in those situations where approval would be required, the effect is the same as though you had been asked and said NO. Initially T. +DWIMIFYCOMPFLG (DWIMIFYCOMPFLG% (Variable) NIL NIL ("21") 14) [Variable] +If T, DWIMIFY is called before compiling an expression. Initially NIL. +DWIMCHECK#ARGSFLG(DWIMCHECK#ARGSFLG (Variable) NIL NIL ("21") 14) [Variable] +If T, causes DWIMIFY to check for too many arguments in a form. Initially T. +DWIMCHECKPROGLABELSFLG(DWIMCHECKPROGLABELSFLG (Variable) NIL NIL ("21") 14) [Variable] +If T, causes DWIMIFY to check whether a PROG label contains a CLISP character. Initially T. +DWIMESSGAG(DWIMESSGAG (Variable) NIL NIL ("21") 15) [Variable] +If T, suppresses all DWIMIFY error messages. Initially NIL. +CLISPRETRANFLG(CLISPRETRANFLG (Variable) NIL NIL ("21") 15) [Variable] +If T, informs DWIMIFY to (re)translate all expressions which have remote translations in the CLISP hash array. Initially NIL. +CLISPIFY +1 + +CLISPIFY converts Interlisp expressions to CLISP. Note that the expression given to CLISPIFY need not have originally been input as CLISP, i.e., CLISPIFY can be used on functions that were written before CLISP was even implemented. CLISPIFY is cognizant of declaration rules as well as all of the precedence rules. For example, CLISPIFY will convert (IPLUS A (ITIMES B C)) into A+B*C, but (ITIMES A (IPLUS B C)) into A*(B+C). CLISPIFY handles such cases by first DWIMIFYing the expression. CLISPIFY also knows how to handle expressions consisting of a mixture of Interlisp and CLISP, e.g., (IPLUS A B*C) is converted to A+B*C, but (ITIMES A B+C) to (A*(B+C)). CLISPIFY converts calls to the six basic mapping functions, MAP, MAPC, MAPCAR, MAPLIST, MAPCONC, and MAPCON, into equivalent iterative statements. It also converts certain easily recognizable internal PROG loops to the corresponding iterative statements. CLISPIFY can convert all iterative statements input in CLISP back to CLISP, regardless of how complicated the translation was, because the original CLISP is saved. +CLISPIFY is not destructive to the original Interlisp expression, i.e., CLISPIFY produces a new expression without changing the original. The new expression may however contain some "pieces" of the original, since CLISPIFY attempts to minimize the number of CONSes by not copying structure whenever possible. +CLISPIFY will not convert expressions appearing as arguments to NLAMBDA functions, except for those functions whose INFO property is or contains the atom EVAL. CLISPIFY also contains built in information enabling it to process special forms such as PROG, SELECTQ, etc. If the INFO property is or contains the atom LABELS, CLISPIFY will never create an atom (by packing) at the top level of the expression. PROG is handled in this fashion. +Note: Disabling a CLISP operator with CLDISABLE (see the Miscellaneous Functions and Variables section) will also disable the corresponding CLISPIFY transformation. Thus, if * is "turned off", A*B will not transform to (SETQ A B), nor vice versa. +(CLISPIFY X EDITCHAIN) [Function] +Clispifies X. If X is an atom and EDITCHAIN is NIL, X is treated as the name of a function, and its definition (or EXPR property) is clispified. After CLISPIFY has finished, X is redefined (using /PUTD) with its new CLISP definition. The value of CLISPIFY is X. If X is atomic and not the name of a function, spelling correction is attempted. If this fails, an error is generated. +If X is a list, or EDITCHAIN is not NIL, X itself is the expression to be clispified. If EDITCHAIN is not NIL, it is the edit push-down list leading to X and is used to determine context as with DWIMIFY, as well as to obtain the local declarations, if any. The value of CLISPIFY is the clispified version of X. +(CLISPIFYFNS FN1 ... FNN) [NLambda NoSpread Function] +Like DWIMIFYFNS except calls CLISPIFY instead of DWIMIFY. +CL:FLG(CL:FLG (Variable) NIL NIL ("21") 16) [Variable] +Affects CLISPIFY's handling of forms beginning with CAR, CDR, ... CDDDDR, as well as pattern match and record expressions. If CL:FLG is NIL, these are not transformed into the equivalent : expressions. This will prevent CLISPIFY from constructing any expression employing a : infix operator, e.g., (CADR X) will not be transformed to X:2. If CL:FLG is T, CLISPIFY will convert to : notation only when the argument is atomic or a simple list (a function name and one atomic argument). If CL:FLG is ALL, CLISPIFY will convert to : expressions whenever possible. +CL:FLG is initially T. +CLREMPARSFLG (CLREMPARSFLG% (Variable) NIL NIL ("21") 16) [Variable] +If T, CLISPIFY will remove parentheses in certain cases from simple forms, where "simple" means a function name and one or two atomic arguments. For example, (COND ((ATOM X) --)) will CLISPIFY to (IF ATOM X THEN --). However, if CLREMPARSFLG is set to NIL, CLISPIFY will produce (IF (ATOM X) THEN --). Regardless of the flag setting, the expression can be input in either form. +CLREMPARSFLG is initially NIL. +CLISPIFYPACKFLG (CLISPIFYPACKFLG% (Variable) NIL NIL ("21") 16) [Variable] +CLISPIFYPACKFLG affects the treatment of infix operators with atomic operands. If CLISPIFYPACKFLG is T, CLISPIFY will pack these into single atoms, e.g., (IPLUS A (ITIMES B C)) becomes A+B*C. If CLISPIFYPACKFLG is NIL, no packing is done, e.g., the above becomes A + B * C. +CLISPIFYPACKFLG is initially T. +CLISPIFYUSERFN(CLISPIFYUSERFN (Variable) NIL NIL ("21") 16) [Variable] +If T, causes the function CLISPIFYUSERFN, which should be a function of one argument, to be called on each form (list) not otherwise recognized by CLISPIFY. If a non-NIL value is returned, it is treated as the clispified form. Initially NIL +Note that CLISPIFYUSERFN must be both set and defined to use this feature. +FUNNYATOMLST(FUNNYATOMLST (Variable) NIL NIL ("21") 16) [Variable] +Suppose you have variables named A, B, and A*B. If CLISPIFY were to convert (ITIMES A B) to A*B, A*B would not translate back correctly to (ITIMES A B), since it would be the name of a variable, and therefore would not cause an error. You can prevent this from happening by adding A*B to the list FUNNYATOMLST. Then, (ITIMES A B) would CLISPIFY to A * B. +Note that A*B's appearance on FUNNYATOMLST would not enable DWIM and CLISP to decode A*B+C as (IPLUS A*B C); FUNNYATOMLST is used only by CLISPIFY. Thus, if an identifier contains a CLISP character, it should always be separated (with spaces) from other operators. For example, if X* is a variable, you should write (SETQ X* FORM) in CLISP as X* _FORM, not X*_FORM. In general, it is best to avoid use of identifiers containing CLISP character operators as much as possible. +Miscellaneous Functions and Variables +1 + +CLISPFLG(CLISPFLG (Variable) NIL NIL ("21") 16) [Variable] +If CLISPFLG = NIL, disables all CLISP infix or prefix transformations (but does not affect IF/THEN/ELSE statements, or iterative statements). +If CLISPFLG = TYPE-IN, CLISP transformations are performed only on expressions that are typed in for evaluation, i.e., not on user programs. +If CLISPFLG = T, CLISP transformations are performed on all expressions. +The initial value for CLISPFLG is T. CLISPIFYing anything will cause CLISPFLG to be set to T. +CLISPCHARS (CLISPCHARS% (Variable) NIL NIL ("21") 17) [Variable] +A list of the operators that can appear in the interior of an atom. Currently (+ - * / ^ ~ ' = _ : < > +- ~= @ !). +CLISPCHARRAY(CLISPCHARRAY (Variable) NIL NIL ("21") 17) [Variable] +A bit table of the characters on CLISPCHARS used for calls to STRPOSL (Chapter 4). CLISPCHARRAY is initialized by performing (SETQ CLISPCHARRAY (MAKEBITTABLE CLISPCHARS)). +(CLISPINFIXSPLST% (Variable) NIL NIL ("21") 17)CLISPINFIXSPLST [Variable] +A list of infix operators used for spelling correction. +CLISPARRAY (CLISPARRAY (Variable) NIL NIL ("21") 17) [Variable] +Hash array used for storing CLISP translations. CLISPARRAY is checked by FAULTEVAL and FAULTAPPLY on erroneous forms before calling DWIM, and by the compiler. +(CLEARCLISPARRAY(CLEARCLISPARRAY (Function) NIL NIL (21) 17) NAME --) [Function] +Macro and CLISP expansions are cached in CLISPARRAY, the systems CLISP hash array. When anything changes that would invalidate an expansion, it needs to be removed from the cache. CLEARCLISPARRAY does this for you. The system does this automatically whenever you define redefine a CLISP or macro form. If you have changed something that a CLISP word or a macro depends on the system will not be able to detect this, so you will have to invalidate the cahce by calling CLEARCLISPARRAY. You can clear the whole cache by calling (CLRHASH CLISPARRAY). +(CLISPTRAN(CLISPTRAN (Function) NIL NIL ("21") 17) X TRAN) [Function] +Gives X the translation TRAN by storing (key X, value TRAN) in the hash array CLISPARRAY. CLISPTRAN is called for all CLISP translations, via a non-linked, external function call, so it can be advised. +(CLISPDEC(CLISPDEC (Function) NIL NIL ("21") 17) DECLST) [Function] +Puts into effect the declarations in DECLST. CLISPDEC performs spelling corrections on words not recognized as declarations. CLISPDEC is undoable. +(CLDISABLE(CLDISABLE (Function) NIL NIL ("21") 17) OP) [Function] +Disables the CLISP operator OP. For example, (CLDISABLE '-) makes - be just another character. CLDISABLE can be used on all CLISP operators, e.g., infix operators, prefix operators, iterative statement operators, etc. CLDISABLE is undoable. +Note: Simply removing a character operator from CLISPCHARS will prevent it from being treated as a CLISP operator when it appears as part of an atom, but it will continue to be an operator when it appears as a separate atom, e.g. (FOO + X) vs FOO+X. +CLISPIFTRANFLG(CLISPIFTRANFLG (Variable) NIL NIL ("21") 17) [Variable] +Affects handling of translations of IF-THEN-ELSE statements (see Chapter 9). If T, the translations are stored elsewhere, and the (modified) CLISP retained. If NIL, the corresponding COND expression replaces the CLISP. Initially T. +CLISPIFYPRETTYFLG(CLISPIFYPRETTYFLG (Variable) NIL NIL ("21") 17) [Variable] +If non-NIL, causes PRETTYPRINT (and therefore PP and MAKEFILE) to CLISPIFY selected function definitions before printing them according to the following interpretations of CLISPIFYPRETTYFLG: + ALL Clispify all functions. + T or EXPRS Clispify all functions currently defined as EXPRs. + CHANGES Clispify all functions marked as having been changed. + a list Clispify all functions in that list. +CLISPIFYPRETTYFLG is (temporarily) reset to T when MAKEFILE is called with the option CLISPIFY, and reset to CHANGES when the file being dumped has the property FILETYPE value CLISP. CLISPIFYPRETTYFLG is initially NIL. +Note: If CLISPIFYPRETTYFLG is non-NIL, and the only transformation performed by DWIM are well formed CLISP transformations, i.e., no spelling corrections, the function will not be marked as changed, since it would only have to be re-clispified and re-prettyprinted when the file was written out. +(PPT X) [NLambda NoSpread Function] +Both a function and an edit macro for prettyprinting translations. It performs a PP after first resetting PRETTYTRANFLG to T, thereby causing any translations to be printed instead of the corresponding CLISP. +CLISP:(CL (Editor Command) NIL NIL ("21") 18) [Editor Command] +Edit macro that obtains the translation of the correct expression, if any, from CLISPARRAY, and calls EDITE on it. +CL(CL (Editor Command) NIL NIL ("21") 18) [Editor Command] +Edit macro. Replaces current expression with CLISPIFYed current expression. Current expression can be an element or tail. +DW(DW (Editor Command) NIL NIL ("21") 18) [Editor Command] +Edit macro. DWIMIFYs current expression, which can be an element (atom or list) or tail. +Both CL and DW can be called when the current expression is either an element or a tail and will work properly. Both consult the declarations in the function being edited, if any, and both are undoable. +(LOWERCASE(LOWERCASE (Function) NIL NIL ("21") 18) FLG) [Function] +If FLG = T, LOWERCASE makes the necessary internal modifications so that CLISPIFY will use lower case versions of AND, OR, IF, THEN, ELSE, ELSEIF, and all i.s. operators. This produces more readable output. Note that you can always type in either upper or lower case (or a combination), regardless of the action of LOWERCASE. If FLG = NIL, CLISPIFY will use uppercase versions of AND, OR, et al. The value of LOWERCASE is its previous "setting". LOWERCASE is undoable. The initial setting for LOWERCASE is T. +CLISP Internal Conventions +1 + +CLISP is almost entirely table driven by the property lists of the corresponding infix or prefix operators. For example, much of the information used for translating the + infix operator is stored on the property list of the symbol "+". Thus it is relatively easy to add new infix or prefix operators or change old ones, simply by adding or changing selected property values. (There is some built in information for handling minus, :, ', and ~, i.e., you could not yourself add such "special" operators, although you can disable or redefine them.) +Global declarations operate by changing the LISPFN and CLISPINFIX properties of the appropriate operators. +CLISPTYPE(CLISPTYPE (Property) NIL NIL ("21") 18) [Property Name] +The property value of the property CLISPTYPE is the precedence number of the operator: higher values have higher precedence, i.e., are tighter. Note that the actual value is unimportant, only the value relative to other operators. For example, CLISPTYPE for :, +, and * are 14, 6, and 4 respectively. Operators with the same precedence group left to right, e.g., / also has precedence 4, so A/B*C is (A/B)*C. +An operator can have a different left and right precedence by making the value of CLISPTYPE be a dotted pair of two numbers, e.g., CLISPTYPE of * is (8 . -12). In this case, CAR is the left precedence, and CDR the right, i.e., CAR is used when comparing with operators on the left, and CDR with operators on the right. For example, A*B_C+D is parsed as A*(B_(C+D)) because the left precedence of _ is 8, which is higher than that of *, which is 4. The right precedence of _ is -12, which is lower than that of +, which is 2. +If the CLISPTYPE property for any operator is removed, the corresponding CLISP transformation is disabled, as well as the inverse CLISPIFY transformation. +UNARYOP(UNARYOP (Property) NIL NIL ("21") 19) [Property Name] +The value of property UNARYOP must be T for unary operators or brackets. The operand is always on the right, i.e., unary operators or brackets are always prefix operators. +BROADSCOPE(BROADSCOPE (Property) NIL NIL ("21") 19) [Property Name] +The value of property BROADSCOPE is T if the operator has lower precedence than Interlisp forms, e.g., LT, EQUAL, AND, etc. For example, (FOO X AND Y) parses as ((FOO X) AND Y). If the BROADSCOPE property were removed from the property list of AND, (FOO X AND Y) would parse as (FOO (X AND Y)). +LISPFN(LISPFN (Property) NIL NIL ("21") 19) [Property Name] +The value of the property LISPFN is the name of the function to which the infix operator translates. For example, the value of LISPFN for ^ is EXPT, for ' QUOTE, etc. If the value of the property LISPFN is NIL, the infix operator itself is also the function, e.g., AND, OR, EQUAL. +SETFN(SETFN (Property) NIL NIL ("21") 19) [Property Name] +If FOO has a SETFN property FIE, then (FOO --)_X translates to (FIE -- X). For example, if you make ELT be an infix operator, e.g. #, by putting appropriate CLISPTYPE and LISPFN properties on the property list of # then you can also make # followed by _ translate to SETA, e.g., X#N_Y to (SETA X N Y), by putting SETA on the property list of ELT under the property SETFN. Putting the list (ELT) on the property list of SETA under property SETFN will enable SETA forms to CLISPIFY back to ELT's. +CLISPINFIX(CLISPINFIX (Property) NIL NIL ("21") 19) [Property Name] +The value of this property is the CLISP infix to be used in CLISPIFYing. This property is stored on the property list of the corresponding Interlisp function, e.g., the value of property CLISPINFIX for EXPT is ^, for QUOTE is ' etc. +CLISPWORD(CLISPWORD (Property) NIL NIL ("21") 19) [Property Name] +Appears on the property list of clisp operators which can appear as CAR of a form, such as FETCH, REPLACE, IF, iterative statement operators, etc. Value of property is of the form (KEYWORD . NAME), where NAME is the lowercase version of the operator, and KEYWORD is its type, e.g. FORWORD, IFWORD, RECORDWORD, etc. +KEYWORD can also be the name of a function. When the atom appears as CAR of a form, the function is applied to the form and the result taken as the correct form. In this case, the function should either physically change the form, or call CLISPTRAN to store the translation. +As an example, to make & be an infix character operator meaning OR, you could do the following: +_(PUTPROP '& 'CLISPTYPE (GETPROP 'OR 'CLISPTYPE)) +_(PUTPROP '& 'LISPFN 'OR) +_(PUTPROP '& 'BROADSCOPE T) +_(PUTPROP 'OR 'CLISPINFIX '&) +_(SETQ CLISPCHARS (CONS '& CLISPCHARS)) +_(SETQ CLISPCHARRAY (MAKEBITTABLE CLISPCHARS)) +(SEQUENCE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "20-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "20-" "")) (192 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (ALTERNATE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "20-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "20-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "20-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "20-" "")) (192 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (408 612 204 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))))1$1$1$1HH$506 +$T1$2$T5(T4`~$~746T$T71HH$1HH$77406 +$1$1ll$406$76T$T5(T7<0<11HH$1$1$H$ PAGEHEADING RIGHTPAGEE$ PAGEHEADINGLEFTBACKT /MODERN +(CHARPROPS (COLOR . BLACK)).GACHA +(CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK))/MODERN +(CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK))/MODERN(CHARPROPS (COLOR . BLACK))/MODERN(CHARPROPS (COLOR . BLACK))  IM.CHAP.GETFN   HRULE.GETFN%u:WIM.INDEX.GETFN!i +8    + ) +J +!E @ 2(  O"))"8D + A4K:"L(>  ! ! 9 y5 ,,P1G;  ,P ;U  \IM.INDEX.GETFN  HRULE.GETFN1 .gJa +I }   VIM.INDEX.GETFN  HRULE.GETFNE  l +/    ! + Lu     +)%IM.INDEX.GETFN %IM.INDEX.GETFN%IM.INDEX.GETFN%IM.INDEX.GETFN'IM.INDEX.GETFNsQ +!8N  7Db9 +D +  +6 +% 7< %IM.INDEX.GETFN&IM.INDEX.GETFN&IM.INDEX.GETFN&IM.INDEX.GETFN&IM.INDEX.GETFNw."1 ^$ .(%IM.INDEX.GETFN  T2  .V &IM.INDEX.GETFNo  >   :i &IM.INDEX.GETFN   +. +1o## +Q;/E7 \ J"XN=Ue  +U     4 *   +k) A Y  z  +  ? +B +   B /  #     .@ %   k +# (               HRULE.GETFN +  5 +     &IM.INDEX.GETFN +%H8:'  "f.       +  + w  3W -3 '@U U;_      E1   HRULE.GETFN &. >: Gt ')$ L  +V3 *  RG 8*4\cW)+,%) m @<5?'>>5]$f  '  HRULE.GETFN@  +o J  -m K +EA9{cm    +  /z) +  HRULE.GETFN+ +qBF$TR/D4)  .@@    !* 5 "h  c +  &6z  -1  %IM.INDEX.GETFNTITAN   `%8 $IM.INDEX.GETFNTITAN    - - - $     -  -)IM.INDEX.GETFNTITAN   <x -  (IM.INDEX.GETFNTITAN    DH/ - ?  (2(/ z \ No newline at end of file +   +63I +^z9 +# +-03&IM.INDEX.GETFN +?(D+Z)I +)IM.INDEX.GETFN + +o +  + +8 !.IM.INDEX.GETFN >;N* #IM.INDEX.GETFNiY  6Y  * ;Q   ?P ,IM.INDEX.GETFN  +IM.INDEX.GETFN +)IM.INDEX.GETFN7 + c -IM.INDEX.GETFN  /IM.INDEX.GETFN 60IM.INDEX.GETFN 75IM.INDEX.GETFN . +)IM.INDEX.GETFN-IM.INDEX.GETFN +e  HRULE.GETFNM,PY\  4_3@$/8-"Q"M ' ]_ +   >!/t 0 +*E  + + + %IM.INDEX.GETFN $70!.k  -IM.INDEX.GETFN   O 0IM.INDEX.GETFN D* . -IM.INDEX.GETFN k E +3 +IM.INDEX.GETFN ! '      + !    o&  HRULE.GETFN'IM.INDEX.GETFN J 'x: ++IM.INDEX.GETFN O# +IM.INDEX.GETFN ! + -0IM.INDEX.GETFN 7 +)IM.INDEX.GETFN 1 +  +=,IM.INDEX.GETFN ) +* (IM.INDEX.GETFN  + f'IM.INDEX.GETFN  +%I  (IM.INDEX.GETFN  s  0 +-IM.INDEX.GETFN $ !P+0IM.INDEX.GETFN   b + +- +7 +-, +Cw R T'IM.INDEX.GETFNP + 'IM.INDEX.GETFN.E'IM.INDEX.GETFN F (IM.INDEX.GETFN   4!aE    '   HRULE.GETFN>/i, +* (IM.INDEX.GETFN#  _R (  .  $'% r&IM.INDEX.GETFN  +)IM.INDEX.GETFN +B   + +1 %IM.INDEX.GETFN`%8$IM.INDEX.GETFN + + + $     +  +)IM.INDEX.GETFN<x + (IM.INDEX.GETFNDH/ +? (2(/(((CHARENCODING . MCCS)))PROPS:#DATE:j z \ No newline at end of file diff --git a/docs/medley-irm/21-PERFORMANCE.TEDIT b/docs/medley-irm/21-PERFORMANCE.TEDIT index 910113d2..6af7a175 100644 --- a/docs/medley-irm/21-PERFORMANCE.TEDIT +++ b/docs/medley-irm/21-PERFORMANCE.TEDIT @@ -1,57 +1,233 @@ -INTERLISP-D REFERENCE MANUAL PERFORMANCE ISSUES "22"21. PERFORMANCE ISSUES 2 This chapter describes a number of areas that often contribute to performance problems in Medley programs. Many performance problems can be improved by optimizing the use of storage, since allocating and reclaiming large amounts of storage is expensive. Another tactic that can sometimes yield performance improvements is to change the use of variable bindings on the stack to reduce variable lookup time. There are a number of tools that can be used to determine which parts of a computation cause performance bottlenecks. Storage Allocation and Garbage Collection 1 As an Medley application program runs, it creates data structures (allocated out of free storage space), manipulates them, and then discards them. If there were no way to reclaim this space, over time the Medley memory would fill up, and the computation would come to a halt. Actually, long before this could happen the system would probably become intolerably slow, due to data fragmentation, which occurs when the data currently in use are spread over many virtual memory pages, so that most of the computer time must be spent swapping disk pages into physical memory. The problem of fragmentation will occur in any situation where the virtual memory is significantly larger than the real physical memory. To reduce swapping, you want to keep the "working set" (the set of pages containing actively referenced data) as small as possible. You can write programs that don't generate much garbage data, or which recycle data, but such programs tend to be complex and hard to debug. Spending effort writing such programs defeats the whole point of using a system with automatic storage allocation. An important part of any Lisp implementation is the garbage collector that finds discarded data and reclaims its space. There are several well-known approaches to garbage collection. One method is the traditional mark-and-sweep, which identifies garbage data by marking all accessible data structures, and then sweeping through the data spaces to find all unmarked objects (i.e., not referenced by any other object). This method is guaranteed to reclaim all garbage, but it takes time proportional to the number of allocated objects, which may be very large. Also, the time that a mark-and-sweep garbage collection takes is independent of the amount of garbage collected; it is possible to sweep through the whole virtual memory, and only recover a small amount of garbage. For interactive applications, it is not acceptable to have long interruptions in a computation for to garbage collect. Medley solves this problem by using a reference-counting garbage collector. With this scheme, there is a table containing counts of how many times each object is referenced. This table is updated as pointers are created and discarded, incurring a small overhead distributed over the computation as a whole. (Note: References from the stack are not counted, but are handled separately at "sweep" time; thus the vast majority of data manipulations do not cause updates to this table.) At opportune moments, the garbage collector scans this table, and reclaims all objects that are no longer accessible (have a reference count of zero). The pause while objects are reclaimed is only the time for scanning the reference count tables (small) plus time proportional to the amount of garbage that has to be collected (typically less than a second). Opportune times occur when a certain number of cells have been allocated or when the system has been waiting for you to type something for long enough. The frequency of garbage collection is controlled by the functions and variables described below. For the best system performance, it is desirable to adjust these parameters for frequent, short garbage collections, which will not interrupt interactive applications for very long, and which will have the added benefit of reducing data fragmentation, keeping the working set small. One problem with the Medley garbage collector is that not all garbage is guaranteed to be collected. Circular data structures, which point to themselves directly or indirectly, are never reclaimed, since their reference counts are always at least one. With time, this unreclaimable garbage may increase the working set to unacceptable levels. Some users have worked with the same Medley virtual memory for a very long time, but it is a good idea to occasionally save all of your functions in files, reinitialize Medley, and rebuild your system. Many users end their working day by issuing a command to rebuild their system and then leaving the machine to perform this task in their absence. If the system seems to be spending too much time swapping (an indication of fragmented working set), this procedure is definitely recommended. Another limitation of the reference-counting garbage collector is that the table in which reference counts are maintained is of fixed size. For typical Lisp objects that are pointed to from exactly one place (e.g., the individual conses in a list), no burden is placed on this table, since objects whose reference count is 1 are not explicitly represented in the table. However, large, "rich" data structures, with many interconnections, backward links, cross references, etc, can contribute many entries to the reference count table. For example, if you created a data structure that functioned as a doubly-linked list, such a structure would contribute an entry (reference count 2) for each element. When the reference count table fills up, the garbage collector can no longer maintain consistent reference counts, so it stops doing so altogether. At this point, a window appears on the screen with the following message, and the debugger is entered: Internal garbage collector(GARBAGE% COLLECTOR NIL garbage% collector NIL (4) NIL) tables have overflowed, due to too many pointers with reference count greater than 1. *** The garbage collector is now disabled. *** Save your work and reload as soon as possible. [This message is slightly misleading, in that it should say "count not equal to 1". In the current implementation, the garbage collection of a large pointer array whose elements are not otherwise pointed to can place a special burden on the table, as each element's reference count simultaneously drops to zero and is thus added to the reference count table for the short period before the element is itself reclaimed.] If you exit the debugger window (e.g., with the RETURN command), your computation can proceed; however, the garbage collector is no longer operating. Thus, your virtual memory will become cluttered with objects no longer accessible, and if you continue for long enough in the same virtual memory image you will eventually fill up the virtual memory backing store and grind to a halt. Garbage collection in Medley is controlled by the following functions and variables: (RECLAIM(RECLAIM (Function) NIL NIL ("22") 2)) [Function] Initiates a garbage collection. Returns 0. (RECLAIMMIN(RECLAIMMIN (Function) NIL NIL ("22") 2) N) [Function] Sets the frequency of garbage collection. Interlisp keeps track of the number of cells of any type that have been allocated; when it reaches a given number, a garbage collection occurs. If N is non-NIL, this number is set to N. Returns the current setting of the number. RECLAIMWAIT(RECLAIMWAIT (Variable) NIL NIL ("22") 2) [Variable] Medley will invoke a RECLAIM if the system is idle and waiting for your input for RECLAIMWAIT seconds (currently set for 4 seconds). (GCGAG(GCGAG (Function) NIL NIL ("22") 2) MESSAGE) [Function] Sets the behavior that occurs while a garbage collection is taking place. If MESSAGE is non-NIL, the cursor is complemented during a RECLAIM; if MESSAGE = NIL, nothing happens. The value of GCGAG is its previous setting. (GCTRP(GCGAG (Function) NIL NIL ("22") 2)) [Function] Returns the number of cells until the next garbage collection, according to the RECLAIMMIN number. The amount of storage allocated to different data types, how much of that storage is in use, and the amount of data fragmentation can be determined using the following function: (STORAGE(STORAGE (Function) NIL NIL ("22") 2) TYPES PAGETHRESHOLD) [Function] STORAGE prints out a summary, for each data type, of the amount of space allocated to the data type, and how much of that space is currently in use. If TYPES is non-NIL, STORAGE only lists statistics for the specified types. TYPES can be a symbol or a list of types. If PAGETHRESHOLD is non-NIL, then STORAGE only lists statistics for types that have at least PAGETHRESHOLD pages allocated to them. STORAGE prints out a table with the column headings Type, Assigned, Free Items, In use, and Total alloc. Type is the name of the data type. Assigned is how much of your virtual memory is set aside for items of this type. Currently, memory is allocated in quanta of two pages (1024 bytes). The numbers under Assigned show the number of pages and the total number of items that fit on those pages. Free Items shows how many items are available to be allocated (using the create construct, Chapter 8); these constitute the "free list" for that data type. In use shows how many items of this type are currently in use, i.e., have pointers to them and hence have not been garbage collected. If this number is higher than your program seems to warrant, you may want to look for storage leaks. The sum of Free Items and In use is always the same as the total Assigned items. Total alloc is the total number of items of this type that have ever been allocated (see BOXCOUNT, in the Performance Measuring section below). Note: The information about the number of items of type LISTP is only approximate, because list cells are allocated in a special way that precludes easy computation of the number of items per page. Note: When a data type is redeclared, the data type name is reassigned. Pages which were assigned to instances of the old data type are labeled **DEALLOC**. At the end of the table printout, STORAGE prints a "Data Spaces Summary" listing the number of pages allocated to the major data areas in the virtual address space: the space for fixed-length items (including datatypes), the space for variable-length items, and the space for symbols. Variable-length data types such as arrays have fixed-length "headers," which is why they also appear in the printout of fixed-length data types. Thus, the line printed for the BITMAP data type says how many bitmaps have been allocated, but the "assigned pages" column counts only the headers, not the space used by the variable-length part of the bitmap. This summary also lists "Remaining Pages" in relation to the largest possible virtual memory, not the size of the virtual memory backing file in use. This file may fill up, causing a STORAGE FULL error, long before the "Remaining Pages" numbers reach zero. STORAGE also prints out information about the sizes of the entries on the variable-length data free list. The block sizes are broken down by the value of the variable STORAGE.ARRAYSIZES, initially (4 16 64 256 1024 4096 16384 NIL), which yields a printout of the form: variable-datum free list: le 4 26 items; 104 cells. le 16 72 items; 783 cells. le 64 36 items; 964 cells. le 256 28 items; 3155 cells. le 1024 3 items; 1175 cells. le 4096 5 items; 8303 cells. le 16384 3 items; 17067 cells. others 1 items; 17559 cells. This information can be useful in determining if the variable-length data space is fragmented. If most of the free space is composed of small items, then the allocator may not be able to find room for large items, and will extend the variable datum space. If this is extended too much, this could cause an ARRAYS FULL error, even if there is a lot of space left in little chunks. (STORAGE.LEFT(STORAGE.LEFT (Function) NIL NIL ("22") 3)) [Function] Provides a programmatic way of determining how much storage is left in the major data areas in the virtual address space. Returns a list of the form (MDSFREE MDSFRAC 8MBFRAC ATOMFREE ATOMFRAC), where the elements are interpreted as follows: MDSFREE The number of free pages left in the main data space (which includes both fixed-length and variable-length data types). MDSFRAC The fraction of the total possible main data space that is free. 8MBFRAC The fraction of the total main data space that is free, relative to eight megabytes. This number is useful when using Medley on some early computers where the hardware limits the address space to eight megabytes. The function 32MBADDRESSABLE returns non-NIL if the currently running Medley system can use the full 32 megabyte address space. ATOMFREE The number of free pages left in the symbol space. ATOMFRAC The fraction of the total symbol space that is free. Note: Another important space resource is the amount of the virtual memory backing file in use (see VMEMSIZE, Chapter 12). The system will crash if the virtual memory file is full, even if the address space is not exhausted. Variable Bindings 1 Different implementations of Lisp use different methods of accessing free variables. The binding of variables occurs when a function or a PROG is entered. For example, if the function FOO has the definition (LAMBDA (A B) BODY), the variables A and B are bound so that any reference to A or B from BODY or any function called from BODY will refer to the arguments to the function FOO and not to the value of A or B from a higher level function. All variable names (symbols) have a top level value cell which is used if the variable has not been bound in any function. In discussions of variable access, it is useful to distinguish between three types of variable access: local, special and global. Local variable access is the use of a variable that is bound within the function from which it is used. Special variable access is the use of a variable that is bound by another function. Global variable access is the use of a variable that has not been bound in any function. We will often refer to a variable all of whose accesses are local as a "local variable." Similarly, a variable all of whose accesses are global we call a "global variable." In a deep bound system, a variable is bound by saving on the stack the variable's name together with a value cell which contains that variable's new value. When a variable is accessed, its value is found by searching the stack for the most recent binding (occurrence) and retrieving the value stored there. If the variable is not found on the stack, the variable's top level value cell is used. In a shallow bound system, a variable is bound by saving on the stack the variable name and the variable's old value and putting the new value in the variable's top level value cell. When a variable is accessed, its value is always found in its top level value cell. The deep binding scheme has one disadvantage: the amount of cpu time required to fetch the value of a variable depends on the stack distance between its use and its binding. The compiler can determine local variable accesses and compiles them as fetches directly from the stack. Thus this computation cost only arises in the use of variable not bound in the local frame ("free" variables). The process of finding the value of a free variable is called free variable lookup. In a shallow bound system, the amount of cpu time required to fetch the value of a variable is constant regardless of whether the variable is local, special or global. The disadvantages of this scheme are that the actual binding of a variable takes longer (thus slowing down function call), the cells that contain the current in use values are spread throughout the space of all symbol value cells (thus increasing the working set size of functions) and context switching between processes requires unwinding and rewinding the stack (thus effectively prohibiting the use of context switching for many applications). Medley uses deep binding, because of the working set considerations and the speed of context switching. The free variable lookup routine is microcoded, thus greatly reducing the search time. In benchmarks, the largest percentage of free variable lookup time was 20 percent of the total ellapsed time; the normal time was between 5 and 10 percent. Because of the deep binding, you can sometimes significantly improve performance by declaring global variables. If a variable is declared global, the compiler will compile an access to that variable as a retrieval of its top level value, completely bypassing a stack search. This should be done only for variables that are never bound in functions, such as global databases and flags. Global variable declarations should be done using the GLOBALVARS file manager command (Chapter 17). Its form is (GLOBALVARS VAR1 ... VARN). Another way of improving performance is to declare variables as local within a function. Normally, all variables bound within a function have their names put on the stack, and these names are scanned during free variable lookup. If a variable is declared to be local within a function, its name is not put on the stack, so it is not scanned during free variable lookup, which may increase the speed of lookups. The compiler can also make some other optimizations if a variable is known to be local to a function. A variable may be declared as local within a function by including the form (DECLARE (LOCALVARS VAR1 ... VARN)) following the argument list in the definition of the function. Local variable declarations only effect the compilation of a function. Interpreted functions put all of their variable names on the stack, regardless of any declarations. Performance Measuring 1 This section describes functions that gather and display statistics about a computation, such as as the elapsed time, and the number of data objects of different types allocated. TIMEALL and TIME gather statistics on the evaluation of a specified form. BREAKDOWN gathers statistics on individual functions called during a computation. These functions can be used to determine which parts of a computation are consuming the most resources (time, storage, etc.), and could most profitably be improved. (TIMEALL(TIMEALL (Function) NIL NIL ("22") 6) TIMEFORM NUMBEROFTIMES TIMEWHAT INTERPFLG) [NLambda Function] Evaluates the form TIMEFORM and prints statistics on time spent in various categories (elapsed, keyboard wait, swapping time, gc) and data type allocation. For more accurate measurement on small computations, NUMBEROFTIMES may be specified (its default is 1) to cause TIMEFORM to be executed NUMBEROFTIMES times. To improve the accuracy of timing open-coded operations in this case, TIMEALL compiles a form to execute TIMEFORM NUMBEROFTIMES times (unless INTERPFLG is non-NIL), and then times the execution of the compiled form. Note: If TIMEALL is called with NUMBEROFTIMES > 1, the dummy form is compiled with compiler optimizations on. This means that it is not meaningful to use TIMEALL with very simple forms that are optimized out by the compiler. For example, (TIMEALL '(IPLUS 2 3) 1000) will time a compiled function which simply returns the number 5, since (IPLUS 2 3) is optimized to the integer 5. TIMEWHAT restricts the statistics to specific categories. It can be an atom or list of datatypes to monitor, and/or the atom TIME to monitor time spent. Note that ordinarily, TIMEALL monitors all time and datatype usage, so this argument is rarely needed. TIMEALL returns the value of the last evaluation of TIMEFORM. (TIME(TIME (Function) NIL NIL ("22") 6) TIMEX TIMEN TIMETYP) [NLambda Function] TIME evaluates the form TIMEX, and prints out the number of CONS cells allocated and computation time. Garbage collection time is subtracted out. This function has been largely replaced by TIMEALL. If TIMEN is greater than 1, TIMEX is executed TIMEN times, and TIME prints out (number of conses)/TIMEN, and (computation time)/TIMEN. If TIMEN = NIL, it defaults to 1. This is useful for more accurate measurement on small computations. If TIMETYP is 0, TIME measures and prints total real time as well as computation time. If TIMETYP = 3, TIME measures and prints garbage collection time as well as computation time. If TIMETYP = T, TIME measures and prints the number of pagefaults. TIME returns the value of the last evaluation of TIMEX. (BOXCOUNT(BOXCOUNT (Function) NIL NIL ("22") 6) TYPE N) [Function] Returns the number of data objects of type TYPE allocated since this Interlisp system was created. TYPE can be any data type name (see TYPENAME, Chapter 8). If TYPE is NIL, it defaults to FIXP. If N is non-NIL, the corresponding counter is reset to N. (CONSCOUNT(CONSCOUNT (Function) NIL NIL ("22") 6) N) [Function] Returns the number of CONS cells allocated since this Interlisp system was created. If N is non-NIL, resets the counter to N. Equivalent to (BOXCOUNT 'LISTP N). (PAGEFAULTS(PAGEFAULTS (Function) NIL NIL ("22") 6)) [Function] Returns the number of page faults since this Interlisp system was created. BREAKDOWN 1 TIMEALL collects statistics for whole computations. BREAKDOWN is available to analyze the breakdown of computation time (or any other measureable quantity) function by function. (BREAKDOWN(BREAKDOWN (Function) NIL NIL ("22") 7) FN1 ... FNN) [NLambda NoSpread Function] You call BREAKDOWN giving it a list of function names (unevaluated). These functions are modified so that they keep track of various statistics. To remove functions from those being monitored, simply UNBREAK (Chapter 15) the functions, thereby restoring them to their original state. To add functions, call BREAKDOWN on the new functions. This will not reset the counters for any functions not on the new list. However (BREAKDOWN) will zero the counters of all functions being monitored. The procedure used for measuring is such that if one function calls other and both are "broken down", then the time (or whatever quantity is being measured) spent in the inner function is not charged to the outer function as well. BREAKDOWN will not give accurate results if a function being measured is not returned from normally, e.g., a lower RETFROM (or ERROR) bypasses it. In this case, all of the time (or whatever quantity is being measured) between the time that function is entered and the time the next function being measured is entered will be charged to the first function. (BRKDWNRESULTS(BRKDWNRESULTS (Function) NIL NIL ("22") 7) RETURNVALUESFLG) [Function] BRKDWNRESULTS prints the analysis of the statistics requested as well as the number of calls to each function. If RETURNVALUESFLG is non-NIL, BRKDWNRESULTS will not to print the results, but instead return them in the form of a list of elements of the form (FNNAME #CALLS VALUE). Example: (BREAKDOWN SUPERPRINT SUBPRINT COMMENT1) (SUPERPRINT SUBPRINT COMMENT1) (PRETTYDEF '(SUPERPRINT) 'FOO) FOO.;3 (BRKDWNRESULTS) FUNCTIONS TIME #CALLS PER CALL % SUPERPRINT 8.261 365 0.023 20 SUBPRINT 31.910 141 0.226 76 COMMENT1 1.612 8 0.201 4 TOTAL 41.783 514 0.081 NIL (BRKDWNRESULTS T) ((SUPERPRINT 365 8261) (SUBPRINT 141 31910) (COMMENT1 8 1612)) BREAKDOWN can be used to measure other statistics, by setting the following variables: BRKDWNTYPE(BRKDWNTYPE (Variable) NIL NIL ("22") 7) [Variable] To use BREAKDOWN to measure other statistics, before calling BREAKDOWN, set the variable BRKDWNTYPE to the quantity of interest, e.g., TIME, CONSES, etc, or a list of such quantities. Whenever BREAKDOWN is called with BRKDWNTYPE not NIL, BREAKDOWN performs the necessary changes to its internal state to conform to the new analysis. In particular, if this is the first time an analysis is being run with a particular statistic, a measuring function will be defined, and the compiler will be called to compile it. The functions being broken down will be redefined to call this measuring function. When BREAKDOWN is through initializing, it sets BRKDWNTYPE back to NIL. Subsequent calls to BREAKDOWN will measure the new statistic until BRKDWNTYPE is again set and a new BREAKDOWN performed. BRKDWNTYPES(BRKDWNTYPES (Variable) NIL NIL ("22") 8) [Variable] The list BRKDWNTYPES contains the information used to analyze new statistics. Each entry on BRKDWNTYPES should be of the form (TYPE FORM FUNCTION), where TYPE is a statistic name (as would appear in BRKDWNTYPE), FORM computes the statistic, and FUNCTION (optional) converts the value of form to some more interesting quantity. For example, (TIME (CLOCK 2) (LAMBDA (X) (FQUOTIENT X 1000))) measures computation time and reports the result in seconds instead of milliseconds. BRKDWNTYPES currently contains entries for TIME, CONSES, PAGEFAULTS, BOXES, and FBOXES. Example: (SETQ BRKDWNTYPE '(TIME CONSES)) (TIME CONSES) (BREAKDOWN MATCH CONSTRUCT) (MATCH CONSTRUCT) (FLIP '(A B C D E F G H C Z) '(.. $1 .. #2 ..) '(.. #3 ..)) (A B D E F G H Z) (BRKDWNRESULTS) FUNCTIONS TIME #CALLS PER CALL % MATCH 0.036 1 0.036 54 CONSTRUCT 0.031 1 0.031 46 TOTAL 0.067 2 0.033 FUNCTIONS CONSES #CALLS PER CALL % MATCH 32 1 32.000 40 CONSTRUCT 49 1 49.000 60 TOTAL 81 2 40.500 NIL Occasionally, a function being analyzed is sufficiently fast that the overhead involved in measuring it obscures the actual time spent in the function. If you were using TIME, you would specify a value for TIMEN greater than 1 to give greater accuracy. A similar option is available for BREAKDOWN. You can specify that a function(s) be executed a multiple number of times for each measurement, and the average value reported, by including a number in the list of functions given to BREAKDOWN. For example, BREAKDOWN(EDITCOM EDIT4F 10 EDIT4E EQP) means normal breakdown for EDITCOM and EDIT4F but executes (the body of) EDIT4E and EQP 10 times each time they are called. Of course, the functions so measured must not cause any harmful side effects, since they are executed more than once for each call. The printout from BRKDWNRESULTS will look the same as though each function were run only once, except that the measurement will be more accurate. Another way of obtaining more accurate measurement is to expand the call to the measuring function in-line. If the value of BRKDWNCOMPFLG is non-NIL (initially NIL), then whenever a function is broken-down, it will be redefined to call the measuring function, and then recompiled. The measuring function is expanded in-line via an appropriate macro. In addition, whenever BRKDWNTYPE is reset, the compiler is called for all functions for which BRKDWNCOMPFLG was set at the time they were originally broken-down, i.e. the setting of the flag at the time a function is broken-down determines whether the call to the measuring code is compiled in-line. GAINSPACE 1 If you have large programs and databases, you may sometimes find yourself in a situation where you need to obtain more space, and are willing to pay the price of eliminating some or all of the context information that the various user-assistance facilities such as the programmer's assistant, file package, CLISP, etc., have accumulated during the course of his session. The function GAINSPACE provides an easy way to selectively throw away accumulated data: (GAINSPACE(GAINSPACE (Function) NIL NIL ("22") 9)) [Function] Prints a list of deletable objects, allowing you to specify at each point what should be discarded and what should be retained. For example: (GAINSPACE) purge history lists ? Yes purge everything, or just the properties, e.g., SIDE, LISPXPRINT, etc. ? just the properties discard definitions on property lists ? Yes discard old values of variables ? Yes erase properties ? No erase CLISP translations? Yes GAINSPACE is driven by the list GAINSPACEFORMS. Each element on GAINSPACEFORMS is of the form (PRECHECK MESSAGE FORM KEYLST). If PRECHECK, when evaluated, returns NIL, GAINSPACE skips to the next entry. For example, you will not be asked whether or not to purge the history list if it is not enabled. Otherwise, ASKUSER (Chapter 26) is called with the indicated MESSAGE and the (optional) KEYLST. If you respond No, i.e., ASKUSER returns N, GAINSPACE skips to the next entry. Otherwise, FORM is evaluated with the variable RESPONSE bound to the value of ASKUSER. In the above example, the FORM for the "purge history lists" question calls ASKUSER to ask "purge everything, ..." only if you had responded Yes. If you had responded with Everything, the second question would not have been asked. The "erase properties" question is driven by a list SMASHPROPSMENU. Each element on this list is of the form (MESSAGE . PROPS). You are prompted with MESSAGE (by ASKUSER), and if your response is Yes, PROPS is added to the list SMASHPROPS. The "discard definitions on property lists" and "discard old values of variables" questions also add to SMASHPROPS. You will not be prompted for any entry on SMASHPROPSMENU for which all of the corresponding properties are already on SMASHPROPS. SMASHPROPS is initially set to the value of SMASHPROPSLST. This permits you to specify in advance those properties which you always want discarded, and not be asked about them subsequently. After finishing all the entries on GAINSPACEFORMS, GAINSPACE checks to see if the value of SMASHPROPS is non-NIL, and if so, does a MAPATOMS, i.e., looks at every atom in the system, and erases the indicated properties. You can change or add new entries to GAINSPACEFORMS or SMASHPROPSMENU, so that GAINSPACE can also be used to purge structures that your programs have accumulated. Using Data Types Instead of Records 1 If a program uses large numbers of large data structures, there are several advantages to representing them as user data types rather than as list structures. The primary advantage is increased speed: accessing and setting the fields of a data type can be significantly faster than walking through a list with repeated CARs and CDRs. Also, Compiled code for referencing data types is usually smaller. Finally, by reducing the number of objects created (one object against many list cells), this can reduce the expense of garbage collection. User data types are declared by using the DATATYPE record type (Chapter 8). If a list structure has been defined using the RECORD record type (Chapter 8), and all accessing operations are written using the record package's fetch, replace, and create operations, changing from RECORDs to DATATYPEs only requires editing the record declaration (using EDITREC, Chapter 8) to replace declaration type RECORD by DATATYPE, and recompiling. Note: There are some minor disadvantages: First, there is an upper limit on the number of data types that can exist. Also, space for data types is allocated two pages at a time. Each data type which has any instances allocated has at least two pages assigned to it, which may be wasteful of space if there are only a few examples of a given data type. These problems should not effect most applications programs. Using Fast and Destructive Functions 1 Among the functions used for manipulating objects of various data types, there are a number of functions which have "fast" and "destructive" versions. You should be aware of what these functions do, and when they should be used. Fast functions: By convention, a function named by prefixing an existing function name with F indicates that the new function is a "fast" version of the old. These usually have the same definitions as the slower versions, but they compile open and run without any "safety" error checks. For example, FNTH runs faster than NTH, however, it does not make as many checks (for lists ending with anything but NIL, etc). If these functions are given arguments that are not in the form that they expect, their behavior is unpredictable; they may run forever, or cause a system error. In general, you should only use "fast" functions in code that has already been completely debugged, to speed it up. Destructive functions: By convention, a function named by prefixing an existing function with D indicates the new function is a "destructive" version of the old one, which does not make any new structure but cannibalizes its argument(s). For example, REMOVE returns a copy of a list with a particular element removed, but DREMOVE actually changes the list structure of the list. (Unfortunately, not all destructive functions follow this naming convention: the destructive version of APPEND is NCONC.) You should be careful when using destructive functions that they do not inadvertantly change data structures. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "21-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "21-" "")) (192 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "21-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "21-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "21-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "21-" "")) (192 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (408 612 204 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))A PAGEHEADING RIGHTPAGET,~~,~~26TT52`~~,HH@ PAGEHEADINGLEFTBACKT-T,206 -306 -T,3(T,,,3(T/ PALATINO PALATINO PALATINO TITAN TITAN TITAN TITAN TITAN VH(DEFAULTFONT 1 (GACHA 10) (GACHA 8) (TERMINAL 8) (POSTSCRIPT (GACHA 8))) -MODERN -MODERN -    IM.CHAP.GETFNMODERN - - - HRULE.GETFNMODERN - * -  HRULE.GETFNMODERN - M~G7IM.INDEX.GETFNU %IM.INDEX.GETFNTITAN  +   -(IM.INDEX.GETFNMODERN - -  )IM.INDEX.GETFNMODERN - 6 '  #IM.INDEX.GETFNTITAN  N&!  #IM.INDEX.GETFNTITAN  P -   %IM.INDEX.GETFNTITAN   1) 4 -# R -?N -!  N/9 "f > !'########4 >   *IM.INDEX.GETFNMODERN - 1yBV T46dv -  HRULE.GETFNMODERN - -+ $- i]6 -1 L - -  HRULE.GETFNMODERN - F -v %IM.INDEX.GETFNTITAN  &5 . O  6  nNH  v/L-  "IM.INDEX.GETFNTITAN  -   Y'N/-  &IM.INDEX.GETFNMODERN - +5 (   'IM.INDEX.GETFNTITAN  >   -(IM.INDEX.GETFNTITAN  J  - -  HRULE.GETFNMODERN - . u  'IM.INDEX.GETFNTITAN    7e i :* a   +IM.INDEX.GETFNTITAN   f f + ))))"?  M -(IM.INDEX.GETFNTITAN   -  -$/  - e " -  & -    )IM.INDEX.GETFNMODERN -   I ) -X0W   - "=(((#(((# M  ' s}   -&  - -  HRULE.GETFNMODERN -  B  'IM.INDEX.GETFNTITAN   I,&  +  &  -  -1, -% --> - -"   -P% - K$ -  HRULE.GETFNMODERN - @ *J^6) ) -  HRULE.GETFNMODERN - _O! aAr z \ No newline at end of file +INTERLISP-D REFERENCE MANUAL +PERFORMANCE ISSUES + +"22"21. PERFORMANCE ISSUES +2 + +This chapter describes a number of areas that often contribute to performance problems in Medley programs. Many performance problems can be improved by optimizing the use of storage, since allocating and reclaiming large amounts of storage is expensive. Another tactic that can sometimes yield performance improvements is to change the use of variable bindings on the stack to reduce variable lookup time. There are a number of tools that can be used to determine which parts of a computation cause performance bottlenecks. +Storage Allocation and Garbage Collection +1 + +As an Medley application program runs, it creates data structures (allocated out of free storage space), manipulates them, and then discards them. If there were no way to reclaim this space, over time the Medley memory would fill up, and the computation would come to a halt. Actually, long before this could happen the system would probably become intolerably slow, due to data fragmentation, which occurs when the data currently in use are spread over many virtual memory pages, so that most of the computer time must be spent swapping disk pages into physical memory. The problem of fragmentation will occur in any situation where the virtual memory is significantly larger than the real physical memory. To reduce swapping, you want to keep the "working set" (the set of pages containing actively referenced data) as small as possible. +You can write programs that don't generate much garbage data, or which recycle data, but such programs tend to be complex and hard to debug. Spending effort writing such programs defeats the whole point of using a system with automatic storage allocation. An important part of any Lisp implementation is the garbage collector that finds discarded data and reclaims its space. +There are several well-known approaches to garbage collection. One method is the traditional mark-and-sweep, which identifies garbage data by marking all accessible data structures, and then sweeping through the data spaces to find all unmarked objects (i.e., not referenced by any other object). This method is guaranteed to reclaim all garbage, but it takes time proportional to the number of allocated objects, which may be very large. Also, the time that a mark-and-sweep garbage collection takes is independent of the amount of garbage collected; it is possible to sweep through the whole virtual memory, and only recover a small amount of garbage. +For interactive applications, it is not acceptable to have long interruptions in a computation for to garbage collect. Medley solves this problem by using a reference-counting garbage collector. With this scheme, there is a table containing counts of how many times each object is referenced. This table is updated as pointers are created and discarded, incurring a small overhead distributed over the computation as a whole. (Note: References from the stack are not counted, but are handled separately at "sweep" time; thus the vast majority of data manipulations do not cause updates to this table.) At opportune moments, the garbage collector scans this table, and reclaims all objects that are no longer accessible (have a reference count of zero). The pause while objects are reclaimed is only the time for scanning the reference count tables (small) plus time proportional to the amount of garbage that has to be collected (typically less than a second). Opportune times occur when a certain number of cells have been allocated or when the system has been waiting for you to type something for long enough. The frequency of garbage collection is controlled by the functions and variables described below. For the best system performance, it is desirable to adjust these parameters for frequent, short garbage collections, which will not interrupt interactive applications for very long, and which will have the added benefit of reducing data fragmentation, keeping the working set small. +One problem with the Medley garbage collector is that not all garbage is guaranteed to be collected. Circular data structures, which point to themselves directly or indirectly, are never reclaimed, since their reference counts are always at least one. With time, this unreclaimable garbage may increase the working set to unacceptable levels. Some users have worked with the same Medley virtual memory for a very long time, but it is a good idea to occasionally save all of your functions in files, reinitialize Medley, and rebuild your system. Many users end their working day by issuing a command to rebuild their system and then leaving the machine to perform this task in their absence. If the system seems to be spending too much time swapping (an indication of fragmented working set), this procedure is definitely recommended. +Another limitation of the reference-counting garbage collector is that the table in which reference counts are maintained is of fixed size. For typical Lisp objects that are pointed to from exactly one place (e.g., the individual conses in a list), no burden is placed on this table, since objects whose reference count is 1 are not explicitly represented in the table. However, large, "rich" data structures, with many interconnections, backward links, cross references, etc, can contribute many entries to the reference count table. For example, if you created a data structure that functioned as a doubly-linked list, such a structure would contribute an entry (reference count 2) for each element. +When the reference count table fills up, the garbage collector can no longer maintain consistent reference counts, so it stops doing so altogether. At this point, a window appears on the screen with the following message, and the debugger is entered: +Internal garbage collector(GARBAGE% COLLECTOR NIL garbage% collector NIL (4) NIL) tables have overflowed, due +to too many pointers with reference count greater than 1. +*** The garbage collector is now disabled. *** +Save your work and reload as soon as possible. +[This message is slightly misleading, in that it should say "count not equal to 1". In the current implementation, the garbage collection of a large pointer array whose elements are not otherwise pointed to can place a special burden on the table, as each element's reference count simultaneously drops to zero and is thus added to the reference count table for the short period before the element is itself reclaimed.] +If you exit the debugger window (e.g., with the RETURN command), your computation can proceed; however, the garbage collector is no longer operating. Thus, your virtual memory will become cluttered with objects no longer accessible, and if you continue for long enough in the same virtual memory image you will eventually fill up the virtual memory backing store and grind to a halt. + +Garbage collection in Medley is controlled by the following functions and variables: +(RECLAIM(RECLAIM (Function) NIL NIL ("22") 2)) [Function] +Initiates a garbage collection. Returns 0. +(RECLAIMMIN(RECLAIMMIN (Function) NIL NIL ("22") 2) N) [Function] +Sets the frequency of garbage collection. Interlisp keeps track of the number of cells of any type that have been allocated; when it reaches a given number, a garbage collection occurs. If N is non-NIL, this number is set to N. Returns the current setting of the number. +RECLAIMWAIT(RECLAIMWAIT (Variable) NIL NIL ("22") 2) [Variable] +Medley will invoke a RECLAIM if the system is idle and waiting for your input for RECLAIMWAIT seconds (currently set for 4 seconds). +(GCGAG(GCGAG (Function) NIL NIL ("22") 2) MESSAGE) [Function] +Sets the behavior that occurs while a garbage collection is taking place. If MESSAGE is non-NIL, the cursor is complemented during a RECLAIM; if MESSAGE = NIL, nothing happens. The value of GCGAG is its previous setting. +(GCTRP(GCGAG (Function) NIL NIL ("22") 2)) [Function] +Returns the number of cells until the next garbage collection, according to the RECLAIMMIN number. +The amount of storage allocated to different data types, how much of that storage is in use, and the amount of data fragmentation can be determined using the following function: +(STORAGE(STORAGE (Function) NIL NIL ("22") 2) TYPES PAGETHRESHOLD) [Function] +STORAGE prints out a summary, for each data type, of the amount of space allocated to the data type, and how much of that space is currently in use. If TYPES is non-NIL, STORAGE only lists statistics for the specified types. TYPES can be a symbol or a list of types. If PAGETHRESHOLD is non-NIL, then STORAGE only lists statistics for types that have at least PAGETHRESHOLD pages allocated to them. +STORAGE prints out a table with the column headings Type, Assigned, Free Items, In use, and Total alloc. Type is the name of the data type. Assigned is how much of your virtual memory is set aside for items of this type. Currently, memory is allocated in quanta of two pages (1024 bytes). The numbers under Assigned show the number of pages and the total number of items that fit on those pages. Free Items shows how many items are available to be allocated (using the create construct, Chapter 8); these constitute the "free list" for that data type. In use shows how many items of this type are currently in use, i.e., have pointers to them and hence have not been garbage collected. If this number is higher than your program seems to warrant, you may want to look for storage leaks. The sum of Free Items and In use is always the same as the total Assigned items. Total alloc is the total number of items of this type that have ever been allocated (see BOXCOUNT, in the Performance Measuring section below). +Note: The information about the number of items of type LISTP is only approximate, because list cells are allocated in a special way that precludes easy computation of the number of items per page. +Note: When a data type is redeclared, the data type name is reassigned. Pages which were assigned to instances of the old data type are labeled **DEALLOC**. +At the end of the table printout, STORAGE prints a "Data Spaces Summary" listing the number of pages allocated to the major data areas in the virtual address space: the space for fixed-length items (including datatypes), the space for variable-length items, and the space for symbols. Variable-length data types such as arrays have fixed-length "headers," which is why they also appear in the printout of fixed-length data types. Thus, the line printed for the BITMAP data type says how many bitmaps have been allocated, but the "assigned pages" column counts only the headers, not the space used by the variable-length part of the bitmap. This summary also lists "Remaining Pages" in relation to the largest possible virtual memory, not the size of the virtual memory backing file in use. This file may fill up, causing a STORAGE FULL error, long before the "Remaining Pages" numbers reach zero. +STORAGE also prints out information about the sizes of the entries on the variable-length data free list. The block sizes are broken down by the value of the variable STORAGE.ARRAYSIZES, initially (4 16 64 256 1024 4096 16384 NIL), which yields a printout of the form: +variable-datum free list: +le 4 26 items; 104 cells. +le 16 72 items; 783 cells. +le 64 36 items; 964 cells. +le 256 28 items; 3155 cells. +le 1024 3 items; 1175 cells. +le 4096 5 items; 8303 cells. +le 16384 3 items; 17067 cells. +others 1 items; 17559 cells. +This information can be useful in determining if the variable-length data space is fragmented. If most of the free space is composed of small items, then the allocator may not be able to find room for large items, and will extend the variable datum space. If this is extended too much, this could cause an ARRAYS FULL error, even if there is a lot of space left in little chunks. +(STORAGE.LEFT(STORAGE.LEFT (Function) NIL NIL ("22") 3)) [Function] +Provides a programmatic way of determining how much storage is left in the major data areas in the virtual address space. Returns a list of the form (MDSFREE MDSFRAC 8MBFRAC ATOMFREE ATOMFRAC), where the elements are interpreted as follows: + MDSFREE The number of free pages left in the main data space (which includes both fixed-length and variable-length data types). + MDSFRAC The fraction of the total possible main data space that is free. + 8MBFRAC The fraction of the total main data space that is free, relative to eight megabytes. + This number is useful when using Medley on some early computers where the hardware limits the address space to eight megabytes. The function 32MBADDRESSABLE returns non-NIL if the currently running Medley system can use the full 32 megabyte address space. + ATOMFREE The number of free pages left in the symbol space. + ATOMFRAC The fraction of the total symbol space that is free. +Note: Another important space resource is the amount of the virtual memory backing file in use (see VMEMSIZE, Chapter 12). The system will crash if the virtual memory file is full, even if the address space is not exhausted. +Variable Bindings +1 + +Different implementations of Lisp use different methods of accessing free variables. The binding of variables occurs when a function or a PROG is entered. For example, if the function FOO has the definition (LAMBDA (A B) BODY), the variables A and B are bound so that any reference to A or B from BODY or any function called from BODY will refer to the arguments to the function FOO and not to the value of A or B from a higher level function. All variable names (symbols) have a top level value cell which is used if the variable has not been bound in any function. In discussions of variable access, it is useful to distinguish between three types of variable access: local, special and global. Local variable access is the use of a variable that is bound within the function from which it is used. Special variable access is the use of a variable that is bound by another function. Global variable access is the use of a variable that has not been bound in any function. We will often refer to a variable all of whose accesses are local as a "local variable." Similarly, a variable all of whose accesses are global we call a "global variable." +In a deep bound system, a variable is bound by saving on the stack the variable's name together with a value cell which contains that variable's new value. When a variable is accessed, its value is found by searching the stack for the most recent binding (occurrence) and retrieving the value stored there. If the variable is not found on the stack, the variable's top level value cell is used. +In a shallow bound system, a variable is bound by saving on the stack the variable name and the variable's old value and putting the new value in the variable's top level value cell. When a variable is accessed, its value is always found in its top level value cell. +The deep binding scheme has one disadvantage: the amount of cpu time required to fetch the value of a variable depends on the stack distance between its use and its binding. The compiler can determine local variable accesses and compiles them as fetches directly from the stack. Thus this computation cost only arises in the use of variable not bound in the local frame ("free" variables). The process of finding the value of a free variable is called free variable lookup. +In a shallow bound system, the amount of cpu time required to fetch the value of a variable is constant regardless of whether the variable is local, special or global. The disadvantages of this scheme are that the actual binding of a variable takes longer (thus slowing down function call), the cells that contain the current in use values are spread throughout the space of all symbol value cells (thus increasing the working set size of functions) and context switching between processes requires unwinding and rewinding the stack (thus effectively prohibiting the use of context switching for many applications). +Medley uses deep binding, because of the working set considerations and the speed of context switching. The free variable lookup routine is microcoded, thus greatly reducing the search time. In benchmarks, the largest percentage of free variable lookup time was 20 percent of the total ellapsed time; the normal time was between 5 and 10 percent. +Because of the deep binding, you can sometimes significantly improve performance by declaring global variables. If a variable is declared global, the compiler will compile an access to that variable as a retrieval of its top level value, completely bypassing a stack search. This should be done only for variables that are never bound in functions, such as global databases and flags. +Global variable declarations should be done using the GLOBALVARS file manager command (Chapter 17). Its form is (GLOBALVARS VAR1 ... VARN). +Another way of improving performance is to declare variables as local within a function. Normally, all variables bound within a function have their names put on the stack, and these names are scanned during free variable lookup. If a variable is declared to be local within a function, its name is not put on the stack, so it is not scanned during free variable lookup, which may increase the speed of lookups. The compiler can also make some other optimizations if a variable is known to be local to a function. +A variable may be declared as local within a function by including the form (DECLARE (LOCALVARS VAR1 ... VARN)) following the argument list in the definition of the function. Local variable declarations only effect the compilation of a function. Interpreted functions put all of their variable names on the stack, regardless of any declarations. +Performance Measuring +1 + +This section describes functions that gather and display statistics about a computation, such as as the elapsed time, and the number of data objects of different types allocated. TIMEALL and TIME gather statistics on the evaluation of a specified form. BREAKDOWN gathers statistics on individual functions called during a computation. These functions can be used to determine which parts of a computation are consuming the most resources (time, storage, etc.), and could most profitably be improved. +(TIMEALL(TIMEALL (Function) NIL NIL ("22") 6) TIMEFORM NUMBEROFTIMES TIMEWHAT INTERPFLG) [NLambda Function] +Evaluates the form TIMEFORM and prints statistics on time spent in various categories (elapsed, keyboard wait, swapping time, gc) and data type allocation. +For more accurate measurement on small computations, NUMBEROFTIMES may be specified (its default is 1) to cause TIMEFORM to be executed NUMBEROFTIMES times. To improve the accuracy of timing open-coded operations in this case, TIMEALL compiles a form to execute TIMEFORM NUMBEROFTIMES times (unless INTERPFLG is non-NIL), and then times the execution of the compiled form. +Note: If TIMEALL is called with NUMBEROFTIMES > 1, the dummy form is compiled with compiler optimizations on. This means that it is not meaningful to use TIMEALL with very simple forms that are optimized out by the compiler. For example, (TIMEALL '(IPLUS 2 3) 1000) will time a compiled function which simply returns the number 5, since (IPLUS 2 3) is optimized to the integer 5. +TIMEWHAT restricts the statistics to specific categories. It can be an atom or list of datatypes to monitor, and/or the atom TIME to monitor time spent. Note that ordinarily, TIMEALL monitors all time and datatype usage, so this argument is rarely needed. +TIMEALL returns the value of the last evaluation of TIMEFORM. +(TIME(TIME (Function) NIL NIL ("22") 6) TIMEX TIMEN TIMETYP) [NLambda Function] +TIME evaluates the form TIMEX, and prints out the number of CONS cells allocated and computation time. Garbage collection time is subtracted out. This function has been largely replaced by TIMEALL. +If TIMEN is greater than 1, TIMEX is executed TIMEN times, and TIME prints out (number of conses)/TIMEN, and (computation time)/TIMEN. If TIMEN = NIL, it defaults to 1. This is useful for more accurate measurement on small computations. +If TIMETYP is 0, TIME measures and prints total real time as well as computation time. If TIMETYP = 3, TIME measures and prints garbage collection time as well as computation time. If TIMETYP = T, TIME measures and prints the number of pagefaults. +TIME returns the value of the last evaluation of TIMEX. +(BOXCOUNT(BOXCOUNT (Function) NIL NIL ("22") 6) TYPE N) [Function] +Returns the number of data objects of type TYPE allocated since this Interlisp system was created. TYPE can be any data type name (see TYPENAME, Chapter 8). If TYPE is NIL, it defaults to FIXP. If N is non-NIL, the corresponding counter is reset to N. +(CONSCOUNT(CONSCOUNT (Function) NIL NIL ("22") 6) N) [Function] +Returns the number of CONS cells allocated since this Interlisp system was created. If N is non-NIL, resets the counter to N. Equivalent to (BOXCOUNT 'LISTP N). +(PAGEFAULTS(PAGEFAULTS (Function) NIL NIL ("22") 6)) [Function] +Returns the number of page faults since this Interlisp system was created. +BREAKDOWN +1 + +TIMEALL collects statistics for whole computations. BREAKDOWN is available to analyze the breakdown of computation time (or any other measureable quantity) function by function. +(BREAKDOWN(BREAKDOWN (Function) NIL NIL ("22") 7) FN1 ... FNN) [NLambda NoSpread Function] +You call BREAKDOWN giving it a list of function names (unevaluated). These functions are modified so that they keep track of various statistics. +To remove functions from those being monitored, simply UNBREAK (Chapter 15) the functions, thereby restoring them to their original state. To add functions, call BREAKDOWN on the new functions. This will not reset the counters for any functions not on the new list. However (BREAKDOWN) will zero the counters of all functions being monitored. +The procedure used for measuring is such that if one function calls other and both are "broken down", then the time (or whatever quantity is being measured) spent in the inner function is not charged to the outer function as well. +BREAKDOWN will not give accurate results if a function being measured is not returned from normally, e.g., a lower RETFROM (or ERROR) bypasses it. In this case, all of the time (or whatever quantity is being measured) between the time that function is entered and the time the next function being measured is entered will be charged to the first function. +(BRKDWNRESULTS(BRKDWNRESULTS (Function) NIL NIL ("22") 7) RETURNVALUESFLG) [Function] +BRKDWNRESULTS prints the analysis of the statistics requested as well as the number of calls to each function. If RETURNVALUESFLG is non-NIL, BRKDWNRESULTS will not to print the results, but instead return them in the form of a list of elements of the form (FNNAME #CALLS VALUE). +Example: +_ (BREAKDOWN SUPERPRINT SUBPRINT COMMENT1) +(SUPERPRINT SUBPRINT COMMENT1) +_(PRETTYDEF '(SUPERPRINT) 'FOO) +FOO.;3 +_(BRKDWNRESULTS) +FUNCTIONS TIME #CALLS PER CALL % +SUPERPRINT 8.261 365 0.023 20 +SUBPRINT 31.910 141 0.226 76 +COMMENT1 1.612 8 0.201 4 +TOTAL 41.783 514 0.081 +NIL +_(BRKDWNRESULTS T) +((SUPERPRINT 365 8261) (SUBPRINT 141 31910) (COMMENT1 8 1612)) +BREAKDOWN can be used to measure other statistics, by setting the following variables: +BRKDWNTYPE(BRKDWNTYPE (Variable) NIL NIL ("22") 7) [Variable] +To use BREAKDOWN to measure other statistics, before calling BREAKDOWN, set the variable BRKDWNTYPE to the quantity of interest, e.g., TIME, CONSES, etc, or a list of such quantities. Whenever BREAKDOWN is called with BRKDWNTYPE not NIL, BREAKDOWN performs the necessary changes to its internal state to conform to the new analysis. In particular, if this is the first time an analysis is being run with a particular statistic, a measuring function will be defined, and the compiler will be called to compile it. The functions being broken down will be redefined to call this measuring function. When BREAKDOWN is through initializing, it sets BRKDWNTYPE back to NIL. Subsequent calls to BREAKDOWN will measure the new statistic until BRKDWNTYPE is again set and a new BREAKDOWN performed. +BRKDWNTYPES(BRKDWNTYPES (Variable) NIL NIL ("22") 8) [Variable] +The list BRKDWNTYPES contains the information used to analyze new statistics. Each entry on BRKDWNTYPES should be of the form (TYPE FORM FUNCTION), where TYPE is a statistic name (as would appear in BRKDWNTYPE), FORM computes the statistic, and FUNCTION (optional) converts the value of form to some more interesting quantity. For example, (TIME (CLOCK 2) (LAMBDA (X) (FQUOTIENT X 1000))) measures computation time and reports the result in seconds instead of milliseconds. BRKDWNTYPES currently contains entries for TIME, CONSES, PAGEFAULTS, BOXES, and FBOXES. +Example: +_(SETQ BRKDWNTYPE '(TIME CONSES)) +(TIME CONSES) +_(BREAKDOWN MATCH CONSTRUCT) +(MATCH CONSTRUCT) +_(FLIP '(A B C D E F G H C Z) '(.. $1 .. #2 ..) '(.. #3 ..)) +(A B D E F G H Z) +_(BRKDWNRESULTS) +FUNCTIONS TIME #CALLS PER CALL % +MATCH 0.036 1 0.036 54 +CONSTRUCT 0.031 1 0.031 46 +TOTAL 0.067 2 0.033 +FUNCTIONS CONSES #CALLS PER CALL % +MATCH 32 1 32.000 40 +CONSTRUCT 49 1 49.000 60 +TOTAL 81 2 40.500 +NIL +Occasionally, a function being analyzed is sufficiently fast that the overhead involved in measuring it obscures the actual time spent in the function. If you were using TIME, you would specify a value for TIMEN greater than 1 to give greater accuracy. A similar option is available for BREAKDOWN. You can specify that a function(s) be executed a multiple number of times for each measurement, and the average value reported, by including a number in the list of functions given to BREAKDOWN. For example, BREAKDOWN(EDITCOM EDIT4F 10 EDIT4E EQP) means normal breakdown for EDITCOM and EDIT4F but executes (the body of) EDIT4E and EQP 10 times each time they are called. Of course, the functions so measured must not cause any harmful side effects, since they are executed more than once for each call. The printout from BRKDWNRESULTS will look the same as though each function were run only once, except that the measurement will be more accurate. +Another way of obtaining more accurate measurement is to expand the call to the measuring function in-line. If the value of BRKDWNCOMPFLG is non-NIL (initially NIL), then whenever a function is broken-down, it will be redefined to call the measuring function, and then recompiled. The measuring function is expanded in-line via an appropriate macro. In addition, whenever BRKDWNTYPE is reset, the compiler is called for all functions for which BRKDWNCOMPFLG was set at the time they were originally broken-down, i.e. the setting of the flag at the time a function is broken-down determines whether the call to the measuring code is compiled in-line. +GAINSPACE +1 + +If you have large programs and databases, you may sometimes find yourself in a situation where you need to obtain more space, and are willing to pay the price of eliminating some or all of the context information that the various user-assistance facilities such as the programmer's assistant, file package, CLISP, etc., have accumulated during the course of his session. The function GAINSPACE provides an easy way to selectively throw away accumulated data: +(GAINSPACE(GAINSPACE (Function) NIL NIL ("22") 9)) [Function] +Prints a list of deletable objects, allowing you to specify at each point what should be discarded and what should be retained. For example: +_(GAINSPACE) +purge history lists ? Yes +purge everything, or just the properties, e.g., SIDE, LISPXPRINT, etc. ? +just the properties +discard definitions on property lists ? Yes +discard old values of variables ? Yes +erase properties ? No +erase CLISP translations? Yes +GAINSPACE is driven by the list GAINSPACEFORMS. Each element on GAINSPACEFORMS is of the form (PRECHECK MESSAGE FORM KEYLST). If PRECHECK, when evaluated, returns NIL, GAINSPACE skips to the next entry. For example, you will not be asked whether or not to purge the history list if it is not enabled. Otherwise, ASKUSER (Chapter 26) is called with the indicated MESSAGE and the (optional) KEYLST. If you respond No, i.e., ASKUSER returns N, GAINSPACE skips to the next entry. Otherwise, FORM is evaluated with the variable RESPONSE bound to the value of ASKUSER. In the above example, the FORM for the "purge history lists" question calls ASKUSER to ask "purge everything, ..." only if you had responded Yes. If you had responded with Everything, the second question would not have been asked. +The "erase properties" question is driven by a list SMASHPROPSMENU. Each element on this list is of the form (MESSAGE . PROPS). You are prompted with MESSAGE (by ASKUSER), and if your response is Yes, PROPS is added to the list SMASHPROPS. The "discard definitions on property lists" and "discard old values of variables" questions also add to SMASHPROPS. You will not be prompted for any entry on SMASHPROPSMENU for which all of the corresponding properties are already on SMASHPROPS. SMASHPROPS is initially set to the value of SMASHPROPSLST. This permits you to specify in advance those properties which you always want discarded, and not be asked about them subsequently. After finishing all the entries on GAINSPACEFORMS, GAINSPACE checks to see if the value of SMASHPROPS is non-NIL, and if so, does a MAPATOMS, i.e., looks at every atom in the system, and erases the indicated properties. +You can change or add new entries to GAINSPACEFORMS or SMASHPROPSMENU, so that GAINSPACE can also be used to purge structures that your programs have accumulated. +Using Data Types Instead of Records +1 + +If a program uses large numbers of large data structures, there are several advantages to representing them as user data types rather than as list structures. The primary advantage is increased speed: accessing and setting the fields of a data type can be significantly faster than walking through a list with repeated CARs and CDRs. Also, +Compiled code for referencing data types is usually smaller. Finally, by reducing the number of objects created (one object against many list cells), this can reduce the expense of garbage collection. +User data types are declared by using the DATATYPE record type (Chapter 8). If a list structure has been defined using the RECORD record type (Chapter 8), and all accessing operations are written using the record package's fetch, replace, and create operations, changing from RECORDs to DATATYPEs only requires editing the record declaration (using EDITREC, Chapter 8) to replace declaration type RECORD by DATATYPE, and recompiling. +Note: There are some minor disadvantages: First, there is an upper limit on the number of data types that can exist. Also, space for data types is allocated two pages at a time. Each data type which has any instances allocated has at least two pages assigned to it, which may be wasteful of space if there are only a few examples of a given data type. These problems should not effect most applications programs. +Using Fast and Destructive Functions +1 + +Among the functions used for manipulating objects of various data types, there are a number of functions which have "fast" and "destructive" versions. You should be aware of what these functions do, and when they should be used. +Fast functions: By convention, a function named by prefixing an existing function name with F indicates that the new function is a "fast" version of the old. These usually have the same definitions as the slower versions, but they compile open and run without any "safety" error checks. For example, FNTH runs faster than NTH, however, it does not make as many checks (for lists ending with anything but NIL, etc). If these functions are given arguments that are not in the form that they expect, their behavior is unpredictable; they may run forever, or cause a system error. In general, you should only use "fast" functions in code that has already been completely debugged, to speed it up. +Destructive functions: By convention, a function named by prefixing an existing function with D indicates the new function is a "destructive" version of the old one, which does not make any new structure but cannibalizes its argument(s). For example, REMOVE returns a copy of a list with a particular element removed, but DREMOVE actually changes the list structure of the list. (Unfortunately, not all destructive functions follow this naming convention: the destructive version of APPEND is NCONC.) You should be careful when using destructive functions that they do not inadvertantly change data structures. +(SEQUENCE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "21-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "21-" "")) (192 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (ALTERNATE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "21-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "21-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "21-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "21-" "")) (192 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (408 612 204 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))))1$1$1$2$T5(T46T$T1~~$1~~$1HH$506 +$T4`~$~7406 +$1$1$F$ PAGEHEADING RIGHTPAGETE$ PAGEHEADINGLEFTBACKT /MODERN +(CHARPROPS (COLOR . BLACK)).GACHA +(CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK))/MODERN +(CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK))f=(TEDIT-FONTCLASS 0 (GACHA 10 MRR 0) NIL NIL (POSTSCRIPT NIL)) +(CHARPROPS (COLOR . BLACK))  IM.CHAP.GETFN HRULE.GETFN* HRULE.GETFNM~G 7IM.INDEX.GETFNU +%IM.INDEX.GETFN  + + +(IM.INDEX.GETFN  - + )IM.INDEX.GETFN  6 ' +#IM.INDEX.GETFN  N&! +#IM.INDEX.GETFN  P +  +%IM.INDEX.GETFN   1) 4  -# R +?N +!  N/ 9   "f >  !'######## 4 >  *IM.INDEX.GETFN  1  y  B  V  T  4  6dv HRULE.GETFN+  +$-i]6 +1  +  + + L + + +  + +  HRULE.GETFNF +v +'IM.INDEX.GETFN&  5 . O  6  nNH   v/L - +"IM.INDEX.GETFN    Y 'N/ - +&IM.INDEX.GETFN  +5 ( + 'IM.INDEX.GETFN  > + + +(IM.INDEX.GETFN  J + HRULE.GETFN. u + 'IM.INDEX.GETFN     7e i : * a + +IM.INDEX.GETFN  f f + ))))"? M + +(IM.INDEX.GETFN   -  +$/  + e " +  & +   + )IM.INDEX.GETFN   I ) +X0W   + "=(((#(((#  M  ' s }   +&  + HRULE.GETFN B + 'IM.INDEX.GETFN   I,&  +  &  +  +1, +% +-> + +"   +P% + K$ HRULE.GETFN@ *J^6) ) HRULE.GETFN_O!aAr(((CHARENCODING . MCCS)))PROPS:#DATE:jz \ No newline at end of file diff --git a/docs/medley-irm/22-PROCESSES.TEDIT b/docs/medley-irm/22-PROCESSES.TEDIT index 3b350df8..7863b6a9 100644 Binary files a/docs/medley-irm/22-PROCESSES.TEDIT and b/docs/medley-irm/22-PROCESSES.TEDIT differ diff --git a/docs/medley-irm/23-STREAMS.TEDIT b/docs/medley-irm/23-STREAMS.TEDIT index 67fedb22..fd41f8b3 100644 Binary files a/docs/medley-irm/23-STREAMS.TEDIT and b/docs/medley-irm/23-STREAMS.TEDIT differ diff --git a/docs/medley-irm/24-IO.TEDIT b/docs/medley-irm/24-IO.TEDIT index 519b9b5c..54942dde 100644 --- a/docs/medley-irm/24-IO.TEDIT +++ b/docs/medley-irm/24-IO.TEDIT @@ -1,141 +1,692 @@ - INTERLISP-D REFERENCE MANUAL I/O FUNCTIONS "25"24. INPUT/OUTPUT FUNCTIONS 2 This chapter describes the standard I/O functions used for reading and printing characters and Interlisp expressions on files and other streams. First, the primitive input functions are presented, then the output functions, then functions for random-access operations (such as searching a file for a given stream, or changing the "next-character" pointer to a position in a file). Next, the PRINTOUT statement is documented (see below), which provides an easy way to write complex output operations. Finally, read tables, used to parse characters as Interlisp expressions, are documented. Specifying Streams for Input/Output Functions(INPUT/OUTPUT% FUNCTIONS NIL Input/Output% Functions NIL ("25") 1 SUBNAME SPECIFYING% STREAMS% FOR SUBTEXT specifying% streams% for) 1 Most of the input/output functions in Interlisp-D have an argument named STREAM or FILE, specifying on which open stream the function's action should occur (the name FILE is used in older functions that predate the concept of stream; the two should, however, be treated synonymously). The value of this argument should be one of the following: a stream An object of type STREAM, as returned by OPENSTREAM (Chapter 23) or other stream-producing functions, is always the most precise and efficient way to designate a stream argument. T The litatom T designates the terminal input or output stream of the currently running process, controlling input from the keyboard and output to the display screen. For functions where the direction (input or output) is ambiguous, T is taken to designate the terminal output stream. The T streams are always open; they cannot be closed. The terminal output stream can be set to a given window or display stream by using TTYDISPLAYSTREAM (Chapter 28). The terminal input stream cannot be changed. For more information on terminal I/O, see Chapter 30. NIL The litatom NIL designates the "primary" input or output stream. These streams are initially the same as the terminal input/output streams, but they can be changed by using the functions INPUT and OUTPUT. For functions where the direction (input or output) is ambiguous, e.g., GETFILEPTR, the argument NIL is taken to mean the primary input stream, if that stream is not identical to the terminal input stream, else the primary output stream. a window Uses the display stream of the window . Valid for output only. a file name As of this writing, the name of an open file (as a litatom) can be used as a stream argument. However, there are inefficiencies and possible future incompatibilities associated with doing so. See Chapter 24 for details. (GETSTREAM(GETSTREAM (Function) NIL NIL ("25") 2) FILE ACCESS) [Function] Coerces the argument FILE to a stream by the above rules. If ACCESS is INPUT, OUTPUT, or BOTH, produces the stream designated by FILE that is open for ACCESS. If ACCESS=NIL, returns a stream for FILE open for any kind of input/output (see the list above for the ambiguous cases). If FILE does not designate a stream open in the specified mode, causes an error, FILE NOT OPEN. (STREAMP(STREAMP (Function) NIL NIL ("25") 2) X) [Function] Returns X if X is a STREAM, otherwise NIL. Input Functions 1 While the functions described below can take input from any stream, some special actions occur when the input is from the terminal (the T input stream, see above). When reading from the terminal, the input is buffered a line at a time, unless buffering has been inhibited by CONTROL (Chapter 30) or the input is being read by READC or PEEKC. Using specified editing characters, you can erase a character at a time, a word at a time, or the whole line. The keys that perform these editing functions are assignable via SETSYNTAX, with the initial settings chosen to be those most natural for the given operating system. In Interlisp-D, the initial settings are as follows: characters are deleted one at a time by Backspace; words are erased by control-W; the whole line is erased by Control-Q. On the Interlisp-D display, deleting a character or a line causes the characters to be physically erased from the screen. In Interlisp-10, the deleting action can be modified for various types of display terminals by using DELETECONTROL (Chapter 30). Unless otherwise indicated, when the end of file is encountered while reading from a file, all input functions generate an error, END OF FILE. Note that this does not close the input file. The ENDOFSTREAMOP stream attribute (Chapter 24) is useful for changing the behavior at end of file. Most input functions have a RDTBL argument, which specifies the read table to be used for input. Unless otherwise specified, if RDTBL is NIL, the primary read table is used. If the FILE or STREAM argument to an input function is NIL, the primary input stream is used. (INPUT(INPUT (Function) NIL NIL ("25") 2) FILE) [Function] Sets FILE as the primary input stream; returns the old primary input stream. FILE must be open for input. (INPUT) returns the current primary input stream, which is not changed. Note: If the primary input stream is set to a file, the file's full name, rather than the stream itself, is returned. See discussion in Chapter 24. (READ(READ (Function) NIL NIL ("25") 3) FILE RDTBL FLG) [Function] Reads one expression from FILE. Atoms are delimited by the break and separator characters as defined in RDTBL. To include a break or separator character in an atom, the character must be preceded by the character %, e.g., AB%(C is the atom AB(C, %% is the atom %, %control-K is the atom Control-K. For input from the terminal, an atom containing an interrupt character can be input by typing instead the corresponding alphabetic character preceded by Control-V, e.g., ^VD for Control-D. Strings are delimited by double quotes. To input a string containing a double quote or a %, precede it by %, e.g., "AB%"C" is the string AB"C. Note that % can always be typed even if next character is not "special", e.g., %A%B%C is read as ABC. If an atom is interpretable as a number, READ creates a number, e.g., 1E3 reads as a floating point number, 1D3 as a literal atom, 1.0 as a number, 1,0 as a literal atom, etc. An integer can be input in a non-decimal radix by using syntax such as 123Q, |b10101, |5r1234 (see Chapter 7). The function RADIX, sets the radix used to print integers. When reading from the terminal, all input is line-buffered to enable the action of the backspacing control characters, unless inhibited by CONTROL (Chapter 30). Thus no characters are actually seen by the program until a carriage-return (actually the character with terminal syntax class EOL, see Chapter 30), is typed. However, for reading by READ, when a matching right parenthesis is encountered, the effect is the same as though a carriage-return were typed, i.e., the characters are transmitted. To indicate this, Interlisp also prints a carriage-return line-feed on the terminal. The line buffer is also transmitted to READ whenever an IMMEDIATE read macro character is typed (see below). FLG=T suppresses the carriage-return normally typed by READ following a matching right parenthesis. (However, the characters are still given to READ; i.e., you do not have to type the carriage-return.) (RATOM FILE RDTBL) [Function] Reads in one atom from FILE. Separation of atoms is defined by RDTBL. % is also defined for RATOM, and the remarks concerning line-buffering and editing control characters also apply. If the characters comprising the atom would normally be interpreted as a number by READ, that number is returned by RATOM. Note however that RATOM takes no special action for " whether or not it is a break character, i.e., RATOM never makes a string. (RSTRING(RSTRING (Function) NIL NIL ("25") 3) FILE RDTBL) [Function] Reads characters from FILE up to, but not including, the next break or separator character, and returns them as a string. Backspace, Control-W, Control-Q, Control-V, and % have the same effect as with READ. Note that the break or separator character that terminates a call to RATOM or RSTRING is not read by that call, but remains in the buffer to become the first character seen by the next reading function that is called. If that function is RSTRING, it will return the null string. This is a common source of program bugs. (RATOMS(RATOMS (Function) NIL NIL ("25") 4) A FILE RDTBL) [Function] Calls RATOM repeatedly until the atom A is read. Returns a list of the atoms read, not including A. (RATEST(RATEST (Function) NIL NIL ("25") 4) FLG) [Function] If FLG = T, RATEST returns T if a separator was encountered immediately prior to the atom returned by the last RATOM or READ, NIL otherwise. If FLG = NIL, RATEST returns T if last atom read by RATOM or READ was a break character, NIL otherwise. If FLG = 1, RATEST returns T if last atom read (by READ or RATOM) contained a % used to quote the next character (as in %[ or %A%B%C), NIL otherwise. (READC (READC% (Function) NIL NIL ("25") 4)FILE RDTBL) [Function] Reads and returns the next character, including %, ", etc, i.e., is not affected by break or separator characters. The action of READC is subject to line-buffering, i.e., READC does not return a value until the line has been terminated even if a character has been typed. Thus, the editing control characters have their usual effect. RDTBL does not directly affect the value returned, but is used as usual in line-buffering, e.g., determining when input has been terminated. If (CONTROL T) has been executed (Chapter 30), defeating line-buffering, the RDTBL argument is irrelevant, and READC returns a value as soon as a character is typed (even if the character typed is one of the editing characters, which ordinarily would never be seen in the input buffer). (PEEKC (PEEKC% (Function) NIL NIL ("25") 4)FILE) [Function] Returns the next character, but does not actually read it and remove it from the buffer. If reading from the terminal, the character is echoed as soon as PEEKC reads it, even though it is then "put back" into the system buffer, where Backspace, Control-W, etc. could change it. Thus it is possible for the value returned by PEEKC to "disagree" in the first character with a subsequent READ. (LASTC(LASTC (Function) NIL NIL ("25") 4) FILE) [Function] Returns the last character read from FILE. LASTC can return an incorrect result when called immediatley following a PEEKC on a file that contains run-coded NS characters. (READCCODE(READCCODE (Function) NIL NIL ("25") 4) FILE RDTBL) [Function] Returns the next character code from STREAM; thus, this operation is equivalent to, but more efficient than, (CHCON1 (READC FILE RDTBL)). (PEEKCCODE(PEEKCCODE (Function) NIL NIL ("25") 5) FILE) [Function] Returns, without consuming, the next character code from STREAM; thus, this operation is equivalent to, but more efficient than, (CHCON1 (PEEKC FILE)). (BIN(BIN (Function) NIL NIL ("25") 5) STREAM) [Function] Returns the next byte from STREAM. This operation is useful for reading streams of binary, rather than character, data. Note: BIN is similar to READCCODE, except that BIN always reads a single byte, whereas READCCODE reads a "character" that can consist of more than one byte, depending on the character and its encoding. READ, RATOM, RATOMS, PEEKC, READC all wait for input if there is none. The only way to test whether or not there is input is to use READP: (READP(READP (Function) NIL NIL ("25") 5) FILE FLG) [Function] Returns T if there is anything in the input buffer of FILE, NIL otherwise. This operation is only interesting for streams whose source of data is dynamic, e.g., the terminal or a byte stream over a network; for other streams, such as to files, (READP FILE) is equivalent to (NOT (EOFP FILE)). Note that because of line-buffering, READP may return T, indicating there is input in the buffer, but READ may still have to wait. Frequently, the terminal's input buffer contains a single EOL character left over from a previous input. For most applications, this situation wants to be treated as though the buffer were empty, and so READP returns NIL in this case. However, if FLG=T, READP returns T if there is any character in the input buffer, including a single EOL. FLG is ignored for streams other than the terminal. (EOFP(EOFP (Function) NIL NIL ("25") 5) FILE) [Function] Returns true if FILE is at "end of file", i.e., the next call to an input function would cause an END OF FILE error; NIL otherwise. For randomly accessible files, this can also be thought of as the file pointer pointing beyond the last byte of the file. FILE must be open for (at least) input, or an error is generated, FILE NOT OPEN. Note that EOFP can return NIL and yet the next call to READ might still cause an END OF FILE error, because the only characters remaining in the input were separators or otherwise constituted an incomplete expression. The function SKIPSEPRS is sometimes more useful as a way of detecting end of file when it is known that all the expressions in the file are well formed. (WAITFORINPUT (WAITFORINPUT% (Function) NIL NIL ("25") 5)FILE) [Function] Waits until input is available from FILE or from the terminal, i.e. from T. WAITFORINPUT is functionally equivalent to (until (OR (READP T) (READP FILE)) do NIL), except that it does not use up machine cycles while waiting. Returns the device for which input is now available, i.e. FILE or T. FILE can also be an integer, in which case WAITFORINPUT waits until there is input available from the terminal, or until FILE milliseconds have elapsed. Value is T if input is now available, NIL in the case that WAITFORINPUT timed out. (SKREAD(SKREAD (Function) NIL NIL ("25") 6) FILE REREADSTRING RDTBL) [Function] "Skip Read". SKREAD consumes characters from FILE as if one call to READ had been performed, without paying the storage and compute cost to really read in the structure. REREADSTRING is for the case where the caller has already performed some READC's and RATOM's before deciding to skip this expression. In this case, REREADSTRING should be the material already read (as a string), and SKREAD operates as though it had seen that material first, thus setting up its parenthesis count, double-quote count, etc. The read table RDTBL is used for reading from FILE. If RDTBL is NIL, it defaults to the value of FILERDTBL. SKREAD may have difficulties if unusual read macros are defined in RDTBL. SKREAD does not recognize read macro characters in REREADSTRING, nor SPLICE or INFIX read macros. This is only a problem if the read macros are defined to parse subsequent input in the stream that does not follow the normal parenthesis and string-quote conventions. SKREAD returns %) if the read terminated on an unbalanced closing parenthesis; %] if the read terminated on an unbalanced %], i.e., one which also would have closed any extant open left parentheses; otherwise NIL. (SKIPSEPRS(SKIPSEPRS (Function) NIL NIL ("25") 6) FILE RDTBL) [Function] Consumes characters from FILE until it encounters a non-separator character (as defined by RDTBL). SKIPSEPRS returns, but does not consume, the terminating character, so that the next call to READC would return the same character. If no non-separator character is found before the end of file is reached, SKIPSEPRS returns NIL and leaves the stream at end of file. This function is useful for skipping over "white space" when scanning a stream character by character, or for detecting end of file when reading expressions from a stream with no pre-arranged terminating expression. Output Functions(OUTPUT% FUNCTIONS NIL Output% Functions NIL ("25") 6) 1 Unless otherwise specified by DEFPRINT, pointers other than lists, strings, atoms, or numbers, are printed in the form {DATATYPE} followed by the octal representation of the address of the pointer (regardless of radix). For example, an array pointer might print as {ARRAYP}#43,2760. This printed representation is for compactness of display on your terminal, and will not read back in correctly; if the form above is read, it will produce the litatom {ARRAYP}#43,2760. Note: The term "end-of-line" appearing in the description of an output function means the character or characters used to terminate a line in the file system being used by the given implementation of Interlisp. For example, in Interlisp-D end-of-line is indicated by the character carriage-return. Some of the functions described below have a RDTBL argument, which specifies the read table to be used for output. If RDTBL is NIL, the primary read table is used. Most of the functions described below have an argument FILE, which specifies the stream on which the operation is to take place. If FILE is NIL, the primary output stream is used . (OUTPUT(OUTPUT (Function) NIL NIL ("25") 7) FILE) [Function] Sets FILE as the primary output stream; returns the old primary output stream. FILE must be open for output. (OUTPUT) returns the current primary output stream, which is not changed. Note: If the primary output stream is set to a file, the file's full name, rather than the stream itself, is returned. See the discussion in Chapter 24. (PRIN1(PRIN1 (Function) NIL NIL ("25") 7) X FILE) [Function] Prints X on FILE. (PRIN2(PRIN2 (Function) NIL NIL ("25") 7) X FILE RDTBL) [Function] Prints X on FILE with %'s and "'s inserted where required for it to read back in properly by READ, using RDTBL. Both PRIN1 and PRIN2 print any kind of Lisp expression, including lists, atoms, numbers, and strings. PRIN1 is generally used for printing expressions where human readability, rather than machine readability, is important, e.g., when printing text rather than program fragments. PRIN1 does not print double quotes around strings, or % in front of special characters. PRIN2 is used for printing Interlisp expressions which can then be read back into Interlisp with READ; i.e., break and separator characters in atoms will be preceded by %'s. For example, the atom "()" is printed as %(%) by PRIN2. If the integer output radix (as set by RADIX) is not 10, PRIN2 prints the integer using the input syntax for non-decimal integers (see Chapter 7) but PRIN1 does not (but both print the integer in the output radix). (PRIN3(PRIN3 (Function) NIL NIL ("25") 7) X FILE) [Function] (PRIN4(PRIN4 (Function) NIL NIL ("25") 7) X FILE RDTBL) [Function] PRIN3 and PRIN4 are the same as PRIN1 and PRIN2 respectively, except that they do not increment the horizontal position counter nor perform any linelength checks. They are useful primarily for printing control characters. (PRINT(PRINT (Function) NIL NIL ("25") 7) X FILE RDTBL) [Function] Prints the expression X using PRIN2 followed by an end-of-line. Returns X. (PRINTCCODE(PRINT (Function) NIL NIL ("25") 8) CHARCODE FILE) [Function] Outputs a single character whose code is CHARCODE to FILE. This is similar to (PRIN1 (CHARACTER CHARCODE)), except that numeric characters are guaranteed to print "correctly"; e.g., (PRINTCCODE (CHARCODE 9)) always prints "9", independent of the setting of RADIX. PRINTCCODE may actually print more than one byte on FILE, due to character encoding and end of line conventions; thus, no assumptions should be made about the relative motion of the file pointer (see GETFILEPTR) during this operation. (BOUT(BOUT (Function) NIL NIL ("25") 8) STREAM BYTE) [Function] Outputs a single 8-bit byte to STREAM. This is similar to PRINTCCODE, but for binary streams the character position in STREAM is not updated (as with PRIN3), and end of line conventions are ignored. Note: BOUT is similar to PRINTCCODE, except that BOUT always writes a single byte, whereas PRINTCCODE writes a "character" that can consist of more than one byte, depending on the character and its encoding. (SPACES(SPACES (Function) NIL NIL ("25") 8) N FILE) [Function] Prints N spaces. Returns NIL. (TERPRI (TERPRI% (Function) NIL NIL ("25") 8)FILE) [Function] Prints an end-of-line character. Returns NIL. (FRESHLINE(FRESHLINE (Function) NIL NIL ("25") 8) STREAM) [Function] Equivalent to TERPRI, except it does nothing if it is already at the beginning of the line. Returns T if it prints an end-of-line, NIL otherwise. (TAB(TAB (Function) NIL NIL ("25") 8) POS MINSPACES FILE) [Function] Prints the appropriate number of spaces to move to position POS. MINSPACES indicates how many spaces must be printed (if NIL, 1 is used). If the current position plus MINSPACES is greater than POS, TAB does a TERPRI and then (SPACES POS). If MINSPACES is T, and the current position is greater than POS, then TAB does nothing. Note: A sequence of PRINT, PRIN2, SPACES, and TERPRI expressions can often be more conveniently coded with a single PRINTOUT statement. (SHOWPRIN2(SHOWPRIN2 (Function) NIL NIL ("25") 8) X FILE RDTBL) [Function] Like PRIN2 except if SYSPRETTYFLG=T, prettyprints X instead. Returns X. (SHOWPRINT(SHOWPRINT (Function) NIL NIL ("25") 9) X FILE RDTBL) [Function] Like PRINT except if SYSPRETTYFLG=T, prettyprints X instead, followed by an end-of-line. Returns X. SHOWPRINT and SHOWPRIN2 are used by the programmer's assistant (Chapter 13) for printing the values of expressions and for printing the history list, by various commands of the break package (Chapter 14), e.g. ?= and BT commands, and various other system packages. The idea is that by simply settting or binding SYSPRETTYFLG to T (initially NIL), you instruct the system when interacting with you to PRETTYPRINT expressions (Chapter 26) instead of printing them. (PRINTBELLS(PRINTBELLS (Function) NIL NIL ("25") 9) ) [Function] Used by DWIM (Chapter 19) to print a sequence of bells to alert you to stop typing. Can be advised or redefined for special applications, e.g., to flash the screen on a display terminal. (FORCEOUTPUT(FORCEOUTPUT (Function) NIL NIL ("25") 9) STREAM WAITFORFINISH) [Function] Forces any buffered output data in STREAM to be transmitted. If WAITFORFINISH is non-NIL, this doesn't return until the data has been forced out. (POSITION(POSITION (Function) NIL NIL ("25") 9) FILE N) [Function] Returns the column number at which the next character will be read or printed. After a end of line, the column number is 0. If N is non-NIL, resets the column number to be N. Note that resetting POSITION only changes Lisp's belief about the current column number; it does not cause any horizontal motion. Also note that (POSITION FILE) is not the same as (GETFILEPTR FILE) which gives the position in the file, not on the line. (LINELENGTH(LINELENGTH (Function) NIL NIL ("25") 9) N FILE) [Function] Sets the length of the print line for the output file FILE to N; returns the former setting of the line length. FILE defaults to the primary output stream. (LINELENGTH NIL FILE) returns the current setting for FILE. When a file is first opened, its line length is set to the value of the variable FILELINELENGTH. Whenever printing an atom or string would increase a file's position beyond the line length of the file, an end of line is automatically inserted first. This action can be defeated by using PRIN3 and PRIN4. (SETLINELENGTH(SETLINELENGTH (Function) NIL NIL ("25") 9) N) [Function] Sets the line length for the terminal by doing (LINELENGTH N T). If N is NIL, it determines N by consulting the operating system's belief about the terminal's characteristics. In Interlisp-D, this is a no-op. PRINTLEVEL When using Interlisp one often has to handle large, complicated lists, which are difficult to understand when printed out. PRINTLEVEL allows you to specify in how much detail lists should be printed. The print functions PRINT, PRIN1, and PRIN2 are all affected by level parameters set by: (PRINTLEVEL(PRINTLEVEL (Function) NIL NIL ("25") 10) CARVAL CDRVAL) [Function] Sets the CAR print level to CARVAL, and the CDR print level to CDRVAL. Returns a list cell whose CAR and CDR are the old settings. PRINTLEVEL is initialized with the value (1000 . -1). In order that PRINTLEVEL can be used with RESETFORM or RESETSAVE, if CARVAL is a list cell it is equivalent to (PRINTLEVEL (CAR CARVAL) (CDR CARVAL)). (PRINTLEVEL N NIL) changes the CAR printlevel without affecting the CDR printlevel. (PRINTLEVEL NIL N) changes the CDR printlevel with affecting the CAR printlevel. (PRINTLEVEL) gives the current setting without changing either. Note: Control-P (Chapter 30) can be used to change the PRINTLEVEL setting dynamically, even while Interlisp is printing. The CAR printlevel specifies how "deep" to print a list. Specifically, it is the number of unpaired left parentheses which will be printed. Below that level, all lists will be printed as &. If the CAR printlevel is negative, the action is similar except that an end-of-line is inserted after each right parentheses that would be immediately followed by a left parenthesis. The CDR printlevel specifies how "long" to print a list. It is the number of top level list elements that will be printed before the printing is terminated with --. For example, if CDRVAL=2, (A B C D E) will print as (A B --). For sublists, the number of list elements printed is also affected by the depth of printing in the CAR direction: Whenever the sum of the depth of the sublist (i.e. the number of unmatched left parentheses) and the number of elements is greater than the CDR printlevel, -- is printed. This gives a "triangular" effect in that less is printed the farther one goes in either CAR or CDR direction. If the CDR printlevel is negative, then it is the same as if the CDR printlevel were infinite. Examples: After: (A (B C (D (E F) G) H) K L) prints as: (PRINTLEVEL 3 -1) (A (B C (D & G) H) K L) (PRINTLEVEL 2 -1) (A (B C & H) K L) (PRINTLEVEL 1 -1) (A & K L) (PRINTLEVEL 0 -1) & (PRINTLEVEL 1000 2) (A (B --) --) (PRINTLEVEL 1000 3) (A (B C --) K --) (PRINTLEVEL 1 3) (A & K --) PLVLFILEFLG (PLVLFILEFLG% (Variable) NIL NIL ("25") 11) [Variable] Normally, PRINTLEVEL only affects terminal output. Output to all other files acts as though the print level is infinite. However, if PLVLFILEFLG is T (initially NIL), then PRINTLEVEL affects output to files as well. The following three functions are useful for printing isolated expressions at a specified print level without going to the overhead of resetting the global print level. (LVLPRINT (LVLPRINT% (Function) NIL NIL ("25") 11)X FILE CARLVL CDRLVL TAIL) [Function] Performs PRINT of X to FILE, using as CAR and CDR print levels the values CARLVL and CDRLVL, respectively. Uses the T read table. If TAIL is specified, and X is a tail of it, then begins its printing with "...", rather than on open parenthesis. (LVLPRIN2(LVLPRIN2 (Function) NIL NIL ("25") 11) X FILE CARLVL CDRLVL TAIL) [Function] Similar to LVLPRIN2, but performs a PRIN2. (LVLPRIN1(LVLPRIN1 (Function) NIL NIL ("25") 11) X FILE CARLVL CDRLVL TAIL) [Function] Similar to LVLPRIN1, but performs a PRIN1. Printing Numbers(PRINTING% NUMBERS NIL Printing% Numbers NIL ("25") 11) How the ordinary printing functions (PRIN1, PRIN2, etc.) print numbers can be affected in several ways. RADIX influences the printing of integers, and FLTFMT influences the printing of floating point numbers. The setting of the variable PRXFLG determines how the symbol-manipulation functions handle numbers. The PRINTNUM package permits greater controls on the printed appearance of numbers, allowing such things as left-justification, suppression of trailing decimals, etc. (RADIX(RADIX (Function) NIL NIL ("25") 11) N) [Function] Resets the output radix for integers to the absolute value of N. The value of RADIX is its previous setting. (RADIX) gives the current setting without changing it. The initial setting is 10. Note that RADIX affects output only. There is no input radix; on input, numbers are interpreted as decimal unless they are entered in a non-decimal radix with syntax such as 123Q, |b10101, |5r1234 (see Chapter 7). RADIX does not affect the behavior of UNPACK, etc., unless the value of PRXFLG (below) is T. For example, if PRXFLG is NIL and the radix is set to 8 with (RADIX 8), the value of (UNPACK 9) is (9), not (1 1). Using PRINTNUM (below) or the PRINTOUT command .I (below) is often a more convenient and appropriate way to print a single number in a specified radix than to globally change RADIX. (FLTFMT(FLTFMT (Function) NIL NIL ("25") 12) FORMAT) [Function] Resets the output format for floating point numbers to the FLOAT format FORMAT (see PRINTNUM below for a description of FLOAT formats). FORMAT=T specifies the default "free" formatting: some number of significant digits (a function of the implementation) are printed, with trailing zeros suppressed; numbers with sufficiently large or small exponents are instead printed in exponent notation. FLTFMT returns its current setting. (FLTFMT) returns the current setting without changing it. The initial setting is T. Note: In Interlisp-D, FLTFMT ignores the WIDTH and PAD fields of the format (they are implemented only by PRINTNUM). Whether print name manipulation functions (UNPACK, NCHARS, etc.) use the values of RADIX and FLTFMT is determined by the variable PRXFLG: PRXFLG(PRXFLG (Variable) NIL NIL ("25") 12) [Variable] If PRXFLG=NIL (the initial setting), then the "PRIN1" name used by PACK, UNPACK, MKSTRING, etc., is computed using base 10 for integers and the system default floating format for floating point numbers, independent of the current setting of RADIX or FLTFMT. If PRXFLG=T, then RADIX and FLTFMT do dictate the "PRIN1" name of numbers. Note that in this case, PACK and UNPACK are not inverses. Examples with (RADIX 8), (FLTFMT '(FLOAT 4 2)): With PRXFLG=NIL, (UNPACK 13) => (1 3) (PACK '(A 9)) => A9 (UNPACK 1.2345) => (1 %. 2 3 4 5) With PRXFLG=T, (UNPACK 13) => (1 5) (PACK '(A 9)) => A11 (UNPACK 1.2345) => (1 %. 2 3) Note that PRXFLG does not effect the radix of "PRIN2" names, so with (RADIX 8), (NCHARS 9 T), which uses PRIN2 names, would return 3, (since 9 would print as 11Q) for either setting of PRXFLG. Warning: Some system functions will not work correctly if PRXFLG is not NIL. Therefore, resetting the global value of PRXFLG is not recommended. It is much better to rebind PRXFLG as a SPECVAR for that part of a program where it needs to be non-NIL. The basic function for printing numbers under format control is PRINTNUM. Its utility is considerably enhanced when used in conjunction with the PRINTOUT package, which implements a compact language for specifying complicated sequences of elementary printing operations, and makes fancy output formats easy to design and simple to program. (PRINTNUM FORMAT NUMBER FILE) [Function] Prints NUMBER on FILE according to the format FORMAT. FORMAT is a list structure with one of the forms described below. If FORMAT is a list of the form (FIX WIDTH RADIX PAD0 LEFTFLUSH), this specifies a FIX format. NUMBER is rounded to the nearest integer, and then printed in a field WIDTH characters long with radix set to RADIX (or 10 if RADIX=NIL; note that the setting from the function RADIX is not used as the default). If PAD0 and LEFTFLUSH are both NIL, the number is right-justified in the field, and the padding characters to the left of the leading digit are spaces. If PAD0 is T, the character "0" is used for padding. If LEFTFLUSH is T, then the number is left-justified in the field, with trailing spaces to fill out WIDTH characters. The following examples illustrate the effects of the FIX format options on the number 9 (the vertical bars indicate the field width): FORMAT: (PRINTNUM FORMAT 9) prints: (FIX 2) | 9| (FIX 2 NIL T) |09| (FIX 12 8 T) |000000000011| (FIX 5 NIL NIL T) |9 | If FORMAT is a list of the form (FLOAT WIDTH DECPART EXPPART PAD0 ROUND), this specifies a FLOAT format. NUMBER is printed as a decimal number in a field WIDTH characters wide, with DECPART digits to the right of the decimal point. If EXPPART is not 0 (or NIL), the number is printed in exponent notation, with the exponent occupying EXPPART characters in the field. EXPPART should allow for the character E and an optional sign to be printed before the exponent digits. As with FIX format, padding on the left is with spaces, unless PAD0 is T. If ROUND is given, it indicates the digit position at which rounding is to take place, counting from the leading digit of the number. Interlisp-D interprets WIDTH=NIL to mean no padding, i.e., to use however much space the number needs, and interprets DECPART=NIL to mean as many decimal places as needed. The following examples illustrate the effects of the FLOAT format options on the number 27.689 (the vertical bars indicate the field width): FORMAT: (PRINTNUM FORMAT 27.689) prints: (FLOAT 7 2) | 27.69| (FLOAT 7 2 NIL 0) |0027.69| (FLOAT 7 2 2) | 2.77E1| (FLOAT 11 2 4) | 2.77E+01| (FLOAT 7 2 NIL NIL 1) | 30.00| (FLOAT 7 2 NIL NIL 2) | 28.00| NILNUMPRINTFLG(NILNUMPRINTFLG (Variable) NIL NIL ("25") 14) [Variable] If PRINTNUM's NUMBER argument is not a number and not NIL, a NON-NUMERIC ARG error is generated. If NUMBER is NIL, the effect depends on the setting of the variable NILNUMPRINTFLG. If NILNUMPRINTFLG is NIL, then the error occurs as usual. If it is non-NIL, then no error occurs, and the value of NILNUMPRINTFLG is printed right-justified in the field described by FORMAT. This option facilitates the printing of numbers in aggregates with missing values coded as NIL. User Defined Printing Initially, Interlisp only knows how to print in an interesting way objects of type litatom, number, string, list and stackp. All other types of objects are printed in the form {datatype} followed by the octal representation of the address of the pointer, a format that cannot be read back in to produce an equivalent object. When defining user data types (using the DATATYPE record type, Chapter 8), it is often desirable to specify as well how objects of that type should be printed, so as to make their contents readable, or at least more informative to the viewer. The function DEFPRINT is used to specify the printing format of a data type. (DEFPRINT(DEFPRINT (Function) NIL NIL ("25") 14) TYPE FN) [Function] TYPE is a type name. Whenever a printing function (PRINT, PRIN1, PRIN2, etc.) or a function requiring a print name (CHCON, NCHARS, etc.) encounters an object of the indicated type, FN is called with two arguments: the item to be printed and the name of the stream, if any, to which the object is to be printed. The second argument is NIL on calls that request the print name of an object without actually printing it. If FN returns a list of the form (ITEM1 . ITEM2), ITEM1 is printed using PRIN1 (unless it is NIL), and then ITEM2 is printed using PRIN2 (unless it is NIL). No spaces are printed between the two items. Typically, ITEM1 is a read macro character. If FN returns NIL, the datum is printed in the system default manner. If FN returns T, nothing further is printed; FN is assumed to have printed the object to the stream itself. Note that this case if permitted only when the second argument passed to FN is non-NIL; otherwise, there is no destination for FN to do its printing, so it must return as in one of the other two cases. Printing Unusual Data Structures HPRINT (for "Horrible Print") and HREAD provide a mechanism for printing and reading back in general data structures that cannot normally be dumped and loaded easily, such as (possibly re-entrant or circular) structures containing user datatypes, arrays, hash tables, as well as list structures. HPRINT will correctly print and read back in any structure containing any or all of the above, chasing all pointers down to the level of literal atoms, numbers or strings. HPRINT currently cannot handle compiled code arrays, stack positions, or arbitrary unboxed numbers. HPRINT operates by simulating the Interlisp PRINT routine for normal list structures. When it encounters a user datatype (see Chapter 8), or an array or hash array, it prints the data contained therein, surrounded by special characters defined as read macro characters. While chasing the pointers of a structure, it also keeps a hash table of those items it encounters, and if any item is encountered a second time, another read macro character is inserted before the first occurrence (by resetting the file pointer with SETFILEPTR) and all subsequent occurrences are printed as a back reference using an appropriate macro character. Thus the inverse function, HREAD merely calls the Interlisp READ routine with the appropriate read table. (HPRINT(HPRINT (Function) NIL NIL ("25") 15) EXPR FILE UNCIRCULAR DATATYPESEEN) [Function] Prints EXPR on FILE. If UNCIRCULAR is non-NIL, HPRINT does no checking for any circularities in EXPR (but is still useful for dumping arbitrary structures of arrays, hash arrays, lists, user data types, etc., that do not contain circularities). Specifying UNCIRCULAR as non-NIL results in a large speed and internal-storage advantage. Normally, when HPRINT encounters a user data type for the first time, it outputs a summary of the data type's declaration. When this is read in, the data type is redeclared. If DATATYPESEEN is non-NIL, HPRINT assumes that the same data type declarations will be in force at read time as were at HPRINT time, and not output declarations. HPRINT is intended primarily for output to random access files, since the algorithm depends on being able to reset the file pointer. If FILE is not a random access file (and UNCIRCULAR = NIL), a temporary file, HPRINT.SCRATCH, is opened, EXPR is HPRINTed on it, and then that file is copied to the final output file and the temporary file is deleted. You can not use HPRINT to save things that contains pointers to raw storage. Fontdescriptors contain pointers to raw storage and windows contain pointers to fontdescriptors. Netiher can therefor be saved with HPRINT. (HREAD(HREAD (Function) NIL NIL ("25") 15) FILE) [Function] Reads and returns an HPRINT-ed expression from FILE. (HCOPYALL(HCOPYALL (Function) NIL NIL ("25") 15) X) [Function] Copies data structure X. X may contain circular pointers as well as arbitrary structures. Note: HORRIBLEVARS and UGLYVARS (Chapter 17) are two file package commands for dumping and reloading circular and re-entrant data structures. They provide a convenient interface to HPRINT and HREAD. When HPRINT is dumping a data structure that contains an instance of an Interlisp datatype, the datatype declaration is also printed onto the file. Reading such a data structure with HREAD can cause problems if it redefines a system datatype. Redefining a system datatype will almost definitely cause serious errors. The Interlisp system datatypes do not change very often, but there is always a possibility when loading in old files created under an old Interlisp release. To prevent accidental system crashes, HREAD will not redefine datatypes. Instead, it will cause an error "attempt to read DATATYPE with different field specification than currently defined". Continuing from this error will redefine the datatype. Random Access File Operations 1 For most applications, files are read starting at their beginning and proceeding sequentially, i.e., the next character read is the one immediately following the last character read. Similarly, files are written sequentially. However, for files on some devices, it is also possible to read/write characters at arbitrary positions in a file, essentially treating the file as a large block of auxiliary storage. For example, one application might involve writing an expression at the beginning of the file, and then reading an expression from a specified point in its middle. This particular example requires the file be open for both input and output. However, random file input or output can also be performed on files that have been opened for only input or only output. Associated with each file is a "file pointer" that points to the location where the next character is to be read from or written to. The file position of a byte is the number of bytes that precede it in the file, i.e., 0 is the position of the beginning of the file. The file pointer to a file is automatically advanced after each input or output operation. This section describes functions which can be used to reposition the file pointer on those files that can be randomly accessed. A file used in this fashion is much like an array in that it has a certain number of addressable locations that characters can be put into or taken from. However, unlike arrays, files can be enlarged. For example, if the file pointer is positioned at the end of a file and anything is written, the file "grows." It is also possible to position the file pointer beyond the end of file and then to write. (If the program attempts to read beyond the end of file, an END OF FILE error occurs.) In this case, the file is enlarged, and a "hole" is created, which can later be written into. Note that this enlargement only takes place at the end of a file; it is not possible to make more room in the middle of a file. In other words, if expression A begins at position 1000, and expression B at 1100, and the program attempts to overwrite A with expression C, whose printed representation is 200 bytes long, part of B will be altered. Warning: File positions are always in terms of bytes, not characters. You should thus be very careful about computing the space needed for an expression. In particular, NS characters may take multiple bytes (see below). Also, the end-of-line character (see Chapter 24) may be represented by a different number of characters in different implementations. Output functions may also introduce end-of-line's as a result of LINELENGTH considerations. Therefore NCHARS (see Chapter 2) does not specify how many bytes an expression takes to print, even ignoring line length considerations. (GETFILEPTR(GETFILEPTR (Function) NIL NIL ("25") 16) FILE) [Function] Returns the current position of the file pointer for FILE, i.e., the byte address at which the next input/output operation will commence. (SETFILEPTR(SETFILEPTR (Function) NIL NIL ("25") 16) FILE ADR) [Function] Sets the file pointer for FILE to the position ADR; returns ADR. The special value ADR=-1 is interpreted to mean the address of the end of file. Note: If a file is opened for output only, the end of file is initially zero, even if an old file by the same name had existed (see OPENSTREAM, Chapter 24). If a file is opened for both input and output, the initial file pointer is the beginning of the file, but (SETFILEPTR FILE -1) sets it to the end of the file. If the file had been opened in append mode by (OPENSTREAM FILE 'APPEND), the file pointer right after opening would be set to the end of the existing file, in which case a SETFILEPTR to position the file at the end would be unnecessary. (GETEOFPTR(GETEOFPTR (Function) NIL NIL ("25") 17) FILE) [Function] Returns the byte address of the end of file, i.e., the number of bytes in the file. Equivalent to performing (SETFILEPTR FILE -1) and returning (GETFILEPTR FILE) except that it does not change the current file pointer. (RANDACCESSP(RANDACCESSP (Function) NIL NIL ("25") 17) FILE) [Function] Returns FILE if FILE is randomly accessible, NIL otherwise. The file T is not randomly accessible, nor are certain network file connections in Interlisp-D. FILE must be open or an error is generated, FILE NOT OPEN. (COPYBYTES(COPYBYTES (Function) NIL NIL ("25") 17) SRCFIL DSTFIL START END) [Function] Copies bytes from SRCFIL to DSTFIL, starting from position START and up to but not including position END. Both SRCFIL and DSTFIL must be open. Returns T. If END=NIL, START is interpreted as the number of bytes to copy (starting at the current position). If START is also NIL, bytes are copied until the end of the file is reached. Warning: COPYBYTES does not take any account of multi-byte NS characters (see Chapter 2). COPYCHARS (below) should be used whenever copying information that might include NS characters. (COPYCHARS(COPYCHARS (Function) NIL NIL ("25") 17) SRCFIL DSTFIL START END) [Function] Like COPYBYTES except that it copies NS characters (see Chapter 2), and performs the proper conversion if the end-of-line conventions of SRCFIL and DSTFIL are not the same (see Chapter 24). START and END are interpreted the same as with COPYBYTES, i.e., as byte (not character) specifications in SRCFIL. The number of bytes actually output to DSTFIL might be more or less than the number of bytes specified by START and END, depending on what the end-of-line conventions are. In the case where the end-of-line conventions happen to be the same, COPYCHARS simply calls COPYBYTES. (FILEPOS(FILEPOS (Function) NIL NIL ("25") 17) STR FILE START END SKIP TAIL CASEARRAY) [Function] Analogous to STRPOS (see Chapter 4), but searches a file rather than a string. FILEPOS searches FILE for the string STR. Search begins at START (or the current position of the file pointer, if START=NIL), and goes to END (or the end of FILE, if END=NIL). Returns the address of the start of the match, or NIL if not found. SKIP can be used to specify a character which matches any character in the file. If TAIL is T, and the search is successful, the value is the address of the first character after the sequence of characters corresponding to STR, instead of the starting address of the sequence. In either case, the file is left so that the next i/o operation begins at the address returned as the value of FILEPOS. CASEARRAY should be a "case array" that specifies that certain characters should be transformed to other characters before matching. Case arrays are returned by CASEARRAY or SEPRCASE below. CASEARRAY=NIL means no transformation will be performed. A case array is an implementation-dependent object that is logically an array of character codes with one entry for each possible character. FILEPOS maps each character in the file "through" CASEARRAY in the sense that each character code is transformed into the corresponding character code from CASEARRAY before matching. Thus if two characters map into the same value, they are treated as equivalent by FILEPOS. CASEARRAY and SETCASEARRAY provide an implementation-independent interface to case arrays. For example, to search without regard to upper and lower case differences, CASEARRAY would be a case array where all characters map to themselves, except for lower case characters, whose corresponding elements would be the upper case characters. To search for a delimited atom, one could use " ATOM " as the pattern, and specify a case array in which all of the break and separator characters mapped into the same code as space. For applications calling for extensive file searches, the function FFILEPOS is often faster than FILEPOS. (FFILEPOS(FFILEPOS (Function) NIL NIL ("25") 18) PATTERN FILE START END SKIP TAIL CASEARRAY) [Function] Like FILEPOS, except much faster in most applications. FFILEPOS is an implementation of the Boyer-Moore fast string searching algorithm. This algorithm preprocesses the string being searched for and then scans through the file in steps usually equal to the length of the string. Thus, FFILEPOS speeds up roughly in proportion to the length of the string, e.g., a string of length 10 will be found twice as fast as a string of length 5 in the same position. Because of certain fixed overheads, it is generally better to use FILEPOS for short searches or short strings. (CASEARRAY(CASEARRAY (Function) NIL NIL ("25") 18) OLDARRAY) [Function] Creates and returns a new case array, with all elements set to themselves, to indicate the identity mapping. If OLDARRAY is given, it is reused. (SETCASEARRAY(SETCASEARRAY (Function) NIL NIL ("25") 18) CASEARRAY FROMCODE TOCODE) [Function] Modifies the case array CASEARRAY so that character code FROMCODE is mapped to character code TOCODE. (GETCASEARRAY(GETCASEARRAY (Function) NIL NIL ("25") 19) CASEARRAY FROMCODE) [Function] Returns the character code that FROMCODE is mapped to in CASEARRAY. (SEPRCASE(SEPRCASE (Function) NIL NIL ("25") 19) CLFLG) [Function] Returns a new case array suitable for use by FILEPOS or FFILEPOS in which all of the break/separators of FILERDTBL are mapped into character code zero. If CLFLG is non-NIL, then all CLISP characters are mapped into this character as well. This is useful for finding a delimited atom in a file. For example, if PATTERN is " FOO ", and (SEPRCASE T) is used for CASEARRAY, then FILEPOS will find "(FOO_". UPPERCASEARRAY(UPPERCASEARRAY (Variable) NIL NIL ("25") 19) [Variable] Value is a case array in which every lowercase character is mapped into the corresponding uppercase character. Useful for searching text files. Input/Output Operations with Characters and Bytes 1 Interlisp-D supports the 16-bit NS character set (see Chapter 2). All of the standard string and print name functions accept litatoms and strings containing NS characters. In almost all cases, a program does not have to distinguish between NS characters or 8-bit characters. The exception to this rule is the handling of input/output operations. Interlisp-D uses two ways of writing 16-bit NS characters on files. One way is to write the full 16-bits (two bytes) every time a character is output. The other way is to use "run-encoding." Each 16 NS character can be decoded into a character set (an integer from 0 to 254 inclusive) and a character number (also an integer from 0 to 254 inclusive). In run-encoding, the byte 255 (illegal as either a character set number or a character number) is used to signal a change to a given character set, and the following bytes are all assumed to come from the same character set (until the next change-character set sequence). Run-encoding can reduce the number of bytes required to encode a string of NS characters, as long as there are long sequences of characters from the same character set (usually the case). Note that characters are not the same as bytes. A single character can take anywhere from one to four bytes bytes, depending on whether it is in the same character set as the preceeding character, and whether run-encoding is enabled. Programs which assume that characters are equal to bytes must be changed to work with NS characters. The functions BIN and BOUT (see above) should only be used to read and write single eight-bit bytes. The functions READCCODE and PRINTCCODE (see above) should be used to read and write single character codes, interpreting run-encoded NS characters. COPYBYTES should only be used to copy blocks of 8-bit data; COPYCHARS should be used to copy characters. Most I/O functions (READC, PRIN1, etc.) read or write 16-bit NS characters. The use of NS characters has serious consequences for any program that uses file pointers to access a file in a random access manner. At any point when a file is being read or written, it has a "current character set." If the file pointer is changed with SETFILEPTR to a part of the file with a different character set, any characters read or written may have the wrong character set. The current character set can be accessed with the following function: (CHARSET(CHARSET (Function) NIL NIL ("25") 20) STREAM CHARACTERSET) [Function] Returns the current character set of the stream STREAM. If CHARACTERSET is non-NIL, the current character set for STREAM is set. Note that for output streams this may cause bytes to be written to the stream. If CHARACTERSET is T, run encoding for STREAM is disabled: both the character set and the character number (two bytes total) will be written to the stream for each character printed. PRINTOUT 1 Interlisp provides many facilities for controlling the format of printed output. By executing various sequences of PRIN1, PRIN2, TAB, TERPRI, SPACES, PRINTNUM, and PRINTDEF, almost any effect can be achieved. PRINTOUT implements a compact language for specifying complicated sequences of these elementary printing functions. It makes fancy output formats easy to design and simple to program. PRINTOUT is a CLISP word (like FOR and IF) for interpreting a special printing language in which you can describe the kinds of printing desired. The description is translated by DWIMIFY to the appropriate sequence of PRIN1, TAB, etc., before it is evaluated or compiled. PRINTOUT printing descriptions have the following general form: (PRINTOUT STREAM PRINTCOM1 ... PRINTCOMN) STREAM is evaluated to obtain the stream to which the output from this specification is directed. The PRINTOUT commands are strung together, one after the other without punctuation, after STREAM. Some commands occupy a single position in this list, but many commands expect to find arguments following the command name in the list. The commands fall into several logical groups: one set deals with horizontal and vertical spacing, another group provides controls for certain formatting capabilities (font changes and subscripting), while a third set is concerned with various ways of actually printing items. Finally, there is a command that permits escaping to a simple Lisp evaluation in the middle of a PRINTOUT form. The various commands are described below. The following examples give a general flavor of how PRINTOUT is used: Example 1: Suppose you want to print out on the terminal the values of three variables, X, Y, and Z, separated by spaces and followed by a carriage return. This could be done by: (PRIN1 X T) (SPACES 1 T) (PRIN1 Y T) (SPACES 1 T) (PRIN1 Z T) (TERPRI T) or by the more concise PRINTOUT form: (PRINTOUT T X , Y , Z T) Here the first T specifies output to the terminal, the commas cause single spaces to be printed, and the final T specifies a TERPRI. The variable names are not recognized as special PRINTOUT commands, so they are printed using PRIN1 by default. Example 2: Suppose the values of X and Y are to be pretty-printed lined up at position 10, preceded by identifying strings. If the output is to go to the primary output stream, you could write either: (PRIN1 "X =") (PRINTDEF X 10 T) (TERPRI ) (PRIN1 "Y =") (PRINTDEF Y 10 T) (TERPRI) or the equivalent: (PRINTOUT NIL "X =" 10 .PPV X T "Y =" 10 .PPV Y T) Since strings are not recognized as special commands, "X =" is also printed with PRIN1 by default. The positive integer means TAB to position 10, where the .PPV command causes the value of X to be prettyprinted as a variable. By convention, special atoms used as PRINTOUT commands are prefixed with a period. The T causes a carriage return, so the Y information is printed on the next line. Example 3. As a final example, suppose that the value of X is an integer and the value of Y is a floating-point number. X is to be printed right-flushed in a field of width 5 beginning at position 15, and Y is to be printed in a field of width 10 also starting at position 15 with 2 places to the right of the decimal point. Furthermore, suppose that the variable names are to appear in the font class named BOLDFONT and the values in font class SMALLFONT. The program in ordinary Interlisp that would accomplish these effects is too complicated to include here. With PRINTOUT, one could write: (PRINTOUT NIL .FONT BOLDFONT "X =" 15 .FONT SMALLFONT .I5 X T .FONT BOLDFONT "Y =" 15 .FONT SMALLFONT .F10.2 Y T .FONT BOLDFONT) The .FONT commands do whatever is necessary to change the font on a multi-font output device. The .I5 command sets up a FIX format for a call to the function PRINTNUM (see above) to print X in the desired format. The .F10.2 specifies a FLOAT format for PRINTNUM. Horizontal Spacing Commands The horizontal spacing commands provide convenient ways of calling TAB and SPACES. In the following descriptions, N stands for a literal positive integer (not for a variable or expression whose value is an integer). N (N a number) [PRINTOUT Command] Used for absolute spacing. It results in a TAB to position N (literally, a (TAB N)). If the line is currently at position N or beyond, the file will be positioned at position N on the next line. .TAB(TAB (Command) .TAB NIL ("25") 22)(.TAB (Command) NIL NIL ("25") 22) POS [PRINTOUT Command] Specifies TAB to position (the value of) POS. This is one of several commands whose effect could be achieved by simply escaping to Lisp, and executing the corresponding form. It is provided as a separate command so that the PRINTOUT form is more concise and is prettyprinted more compactly. Note that .TAB N and N, where N is an integer, are equivalent. .TAB0(TAB0 (Command) .TAB0 NIL ("25") 22)(.TAB0 (Command) NIL NIL ("25") 22) POS [PRINTOUT Command] Like .TAB except that it can result in zero spaces (i.e. the call to TAB specifies MINSPACES=0). -N (N a number) [PRINTOUT Command] Negative integers indicate relative (as opposed to absolute) spacing. Translates as (SPACES |N|). ,(, (Command) NIL NIL ("25") 22) [PRINTOUT Command] ,,(,, (Command) NIL NIL ("25") 22) [PRINTOUT Command] ,,,(,,, (Command) NIL NIL ("25") 22) [PRINTOUT Command] (1, 2 or 3 commas) Provides a short-hand way of specifying 1, 2 or 3 spaces, i.e., these commands are equivalent to -1, -2, and -3, respectively. .SP(SP (Command) .SP NIL ("25") 22)(.SP (Command) NIL NIL ("25") 22) DISTANCE [PRINTOUT Command] Translates as (SPACES DISTANCE). Note that .SP N and -N, where N is an integer, are equivalent. Vertical Spacing Commands Vertical spacing is obtained by calling TERPRI or printing form-feeds. The relevant commands are: T(T (Command) NIL NIL ("25") 23) [PRINTOUT Command] Translates as (TERPRI), i.e., move to position 0 (the first column) of the next line. To print the letter T, use the string "T". .SKIP(SKIP (Command) .SKIP NIL ("25") 23)(.SKIP (Command) NIL NIL ("25") 23) LINES [PRINTOUT Command] Equivalent to a sequence of LINES (TERPRI)'s. The .SKIP command allows for skipping large constant distances and for computing the distance to be skipped. .PAGE(PAGE (Command) .PAGE NIL ("25") 23)(.PAGE (Command) NIL NIL ("25") 23) [PRINTOUT Command] Puts a form-feed (Control-L) out on the file. Care is taken to make sure that Interlisp's view of the current line position is correctly updated. Special Formatting Controls There are a small number of commands for invoking some of the formatting capabilities of multi-font output devices. The available commands are: .FONT(FONT (Command) .FONT NIL ("25") 23)(.FONT (Command) NIL NIL ("25") 23) FONTSPEC [PRINTOUT Command] Changes printing to the font FONTSPEC, which can be a font descriptor, a "font list" such as '(MODERN 10), an image stream (coerced to its current font), or a windows (coerced to the current font of its display stream). The DSPFONT is changed permanently. See fonts (Chapter 27) for more information. FONTSPEC may also be a positive integer N, which is taken as an abbreviated reference to the font class named FONTN (e.g. 1 => FONT1). .SUP(SUP (Command) .SUP NIL ("25") 23)(.SUP (Command) NIL NIL ("25") 23) [PRINTOUT Command] Specifies superscripting. All subsequent characters are printed above the base of the current line. Note that this is absolute, not relative: a .SUP following a .SUP is a no-op. .SUB(SUB (Command) .SUB NIL ("25") 23)(.SUB (Command) NIL NIL ("25") 23) [PRINTOUT Command] Specifies subscripting. Subsequent printing is below the base of the current line. As with superscripting, the effect is absolute. .BASE(BASE (Command) .BASE NIL ("25") 23)(.BASE (Command) NIL NIL ("25") 23) [PRINTOUT Command] Moves printing back to the base of the current line. Un-does a previous .SUP or .SUB; a no-op, if printing is currently at the base. Printing Specifications The value of any expression in a PRINTOUT form that is not recognized as a command itself or as a command argument is printed using PRIN1 by default. For example, title strings can be printed by simply including the string as a separate PRINTOUT command, and the values of variables and forms can be printed in much the same way. Note that a literal integer, say 51, cannot be printed by including it as a command, since it would be interpreted as a TAB; the desired effect can be obtained by using instead the string specification "51", or the form (QUOTE 51). For those instances when PRIN1 is not appropriate, e.g., PRIN2 is required, or a list structures must be prettyprinted, the following commands are available: .P2(P2 (Command) .P2 NIL ("25") 24)(.P2 (Command) NIL NIL ("25") 24) THING [PRINTOUT Command] Causes THING to be printed using PRIN2; translates as (PRIN2 THING). .PPF(PPF (Command) .PPF NIL ("25") 24)(.PPF (Command) NIL NIL ("25") 24) THING [PRINTOUT Command] Causes THING to be prettyprinted at the current line position via PRINTDEF (see Chapter 26). The call to PRINTDEF specifies that THING is to be printed as if it were part of a function definition. That is, SELECTQ, PROG, etc., receive special treatment. .PPV(PPV (Command) .PPV NIL ("25") 24)(.PPV (Command) NIL NIL ("25") 24) THING [PRINTOUT Command] Prettyprints THING as a variable; no special interpretation is given to SELECTQ, PROG, etc. .PPFTL(PPFTL (Command) .PPFTL NIL ("25") 24)(.PPFTL (Command) NIL NIL ("25") 24) THING [PRINTOUT Command] Like .PPF, but prettyprints THING as a tail, that is, without the initial and final parentheses if it is a list. Useful for prettyprinting sub-lists of a list whose other elements are formatted with other commands. .PPVTL(PPVTL (Command) .PPVTL NIL ("25") 24)(.PPVTL (Command) NIL NIL ("25") 24) THING [PRINTOUT Command] Like .PPV, but prettyprints THING as a tail. Paragraph Format Interlisp's prettyprint routines are designed to display the structure of expressions, but they are not really suitable for formatting unstructured text. If a list is to be printed as a textual paragraph, its internal structure is less important than controlling its left and right margins, and the indentation of its first line. The .PARA and .PARA2 commands allow these parameters to be conveniently specified. .PARA(PARA (Command) .PARA NIL ("25") 24)(.PARA (Command) NIL NIL ("25") 24) LMARG RMARG LIST [PRINTOUT Command] Prints LIST in paragraph format, using PRIN1. Translates as (PRINTPARA LMARG RMARG LIST) (see below). Example: (PRINTOUT T 10 .PARA 5 -5 LST) will print the elements of LST as a paragraph with left margin at 5, right margin at (LINELENGTH)-5, and the first line indented to 10. .PARA2(PARA2 (Command) .PARA2 NIL ("25") 25)(.PARA2 (Command) NIL NIL ("25") 25) LMARG RMARG LIST [PRINTOUT Command] Print as paragraph using PRIN2 instead of PRIN1. Translates as (PRINTPARA LMARG RMARG LIST T). Right-Flushing Two commands are provided for printing simple expressions flushed-right against a specified line position, using the function FLUSHRIGHT (see below). They take into account the current position, the number of characters in the print-name of the expression, and the position the expression is to be flush against, and then print the appropriate number of spaces to achieve the desired effect. Note that this might entail going to a new line before printing. Note also that right-flushing of expressions longer than a line (e.g. a large list) makes little sense, and the appearance of the output is not guaranteed. .FR(FR (Command) .FR NIL ("25") 25)(.FR (Command) NIL NIL ("25") 25) POS EXPR [PRINTOUT Command] Flush-right using PRIN1. The value of POS determines the position that the right end of EXPR will line up at. As with the horizontal spacing commands, a negative position number means |POS| columns from the current position, a positive number specifies the position absolutely. POS=0 specifies the right-margin, i.e. is interpreted as (LINELENGTH). .FR2(FR2 (Command) .FR2 NIL ("25") 25)(.FR2 (Command) NIL NIL ("25") 25) POS EXPR [PRINTOUT Command] Flush-right using PRIN2 instead of PRIN1. Centering Commands for centering simple expressions between the current line position and another specified position are also available. As with right flushing, centering of large expressions is not guaranteed. .CENTER(CENTER (Command) .CENTER NIL ("25") 25)(.CENTER (Command) NIL NIL ("25") 25) POS EXPR [PRINTOUT Command] Centers EXPR between the current line position and the position specified by the value of POS. A positive POS is an absolute position number, a negative POS specifies a position relative to the current position, and 0 indicates the right-margin. Uses PRIN1 for printing. .CENTER2(CENTER2 (Command) .CENTER2 NIL ("25") 25)(.CENTER2 (Command) NIL NIL ("25") 25) POS EXPR [PRINTOUT Command] Centers using PRIN2 instead of PRIN1. Numbering The following commands provide FORTRAN-like formatting capabilities for integer and floating-point numbers. Each command specifies a printing format and a number to be printed. The format specification translates into a format-list for the function PRINTNUM. .I(I (Command) .I NIL ("25") 26)(.I (Command) NIL NIL ("25") 26)FORMAT NUMBER [PRINTOUT Command] Specifies integer printing. Translates as a call to the function PRINTNUM with a FIX format-list constructed from FORMAT. The atomic format is broken apart at internal periods to form the format-list. For example, .I5.8.T yields the format-list (FIX 5 8 T), and the command sequence (PRINTOUT T .I5.8.T FOO) translates as (PRINTNUM '(FIX 5 8 T) FOO). This expression causes the value of FOO to be printed in radix 8 right-flushed in a field of width 5, with 0's used for padding on the left. Internal NIL's in the format specification may be omitted, e.g., the commands .I5..T and .I5.NIL.T are equivalent. The format specification .I1 is often useful for forcing a number to be printed in radix 10 (but not otherwise specially formatted), independent of the current setting of RADIX. .F(F (Command) .F NIL ("25") 26)(.F (Command) NIL NIL ("25") 26) FORMAT NUMBER [PRINTOUT Command] Specifies floating-number printing. Like the .I format command, except translates with a FLOAT format-list. .N(N (Command) .N NIL ("25") 26)(.N (Command) NIL NIL ("25") 26) FORMAT NUMBER [PRINTOUT Command] The .I and .F commands specify calls to PRINTNUM with quoted format specifications. The .N command translates as (PRINTNUM FORMAT NUMBER), i.e., it permits the format to be the value of some expression. Note that, unlike the .I and .F commands, FORMAT is a separate element in the command list, not part of an atom beginning with .N. Escaping to Lisp There are many reasons for taking control away from PRINTOUT in the middle of a long printing expression. Common situations involve temporary changes to system printing parameters (e.g. LINELENGTH), conditional printing (e.g. print FOO only if FIE is T), or lower-level iterative printing within a higher-level print specification. #(# (Command) NIL NIL ("25") 26) FORM [PRINTOUT Command] The escape command. FORM is an arbitrary Lisp expression that is evaluated within the context established by the PRINTOUT form, i.e., FORM can assume that the primary output stream has been set to be the FILE argument to PRINTOUT. Note that nothing is done with the value of FORM; any printing desired is accomplished by FORM itself, and the value is discarded. Note: Although PRINTOUT logically encloses its translation in a RESETFORM (Chapter 14) to change the primary output file to the FILE argument (if non-NIL), in most cases it can actually pass FILE (or a locally bound variable if FILE is a non-trivial expression) to each printing function. Thus, the RESETFORM is only generated when the # command is used, or user-defined commands (below) are used. If many such occur in repeated PRINTOUT forms, it may be more efficient to embed them all in a single RESETFORM which changes the primary output file, and then specify FILE=NIL in the PRINTOUT expressions themselves. User-Defined Commands The collection of commands and options outlined above is aimed at fulfilling all common printing needs. However, certain applications might have other, more specialized printing idioms, so a facility is provided whereby you can define new commands. This is done by adding entries to the global list PRINTOUTMACROS to define how the new commands are to be translated. PRINTOUTMACROS(PRINTOUTMACROS (Variable) NIL NIL ("25") 27) [Variable] PRINTOUTMACROS is an association-list whose elements are of the form (COMM FN). Whenever COMM appears in command position in the sequence of PRINTOUT commands (as opposed to an argument position of another command), FN is applied to the tail of the command-list (including the command). After inspecting as much of the tail as necessary, the function must return a list whose CAR is the translation of the user-defined command and its arguments, and whose CDR is the list of commands still remaining to be translated in the normal way. For example, suppose you want to define a command "?", which will cause its single argument to be printed with PRIN1 only if it is not NIL. This can be done by entering (? ?TRAN) on PRINTOUTMACROS, and defining the function ?TRAN as follows: (DEFINEQ (?TRAN (COMS) (CONS (SUBST (CADR COMS) 'ARG '(PROG ((TEMP ARG)) (COND (TEMP (PRIN1 TEMP))))) (CDDR COMS))] Note that ?TRAN does not do any printing itself; it returns a form which, when evaluated in the proper context, will perform the desired action. This form should direct all printing to the primary output file. Special Printing Functions The paragraph printing commands are translated into calls on the function PRINTPARA, which may also be called directly: (PRINTPARA(PRINTPARA (Function) NIL NIL ("25") 27) LMARG RMARG LIST P2FLAG PARENFLAG FILE) [Function] Prints LIST on FILE in line-filled paragraph format with its first element beginning at the current line position and ending at or before RMARG, and with subsequent lines appearing between LMARG and RMARG. If P2FLAG is non-NIL, prints elements using PRIN2, otherwise PRIN1. If PARENFLAG is non-NIL, then parentheses will be printed around the elements of LIST. If LMARG is zero or positive, it is interpreted as an absolute column position. If it is negative, then the left margin will be at |LMARG|+(POSITION). If LMARG=NIL, the left margin will be at (POSITION), and the paragraph will appear in block format. If RMARG is positive, it also is an absolute column position (which may be greater than the current (LINELENGTH)). Otherwise, it is interpreted as relative to (LINELENGTH), i.e., the right margin will be at (LINELENGTH)+|RMARG|. Example: (TAB 10) (PRINTPARA 5 -5 LST T) will PRIN2 the elements of LST in a paragraph with the first line beginning at column 10, subsequent lines beginning at column 5, and all lines ending at or before (LINELENGTH)-5. The current (LINELENGTH) is unaffected by PRINTPARA, and upon completion, FILE will be positioned immediately after the last character of the last item of LIST. PRINTPARA is a no-op if LIST is not a list. The right-flushing and centering commands translate as calls to the function FLUSHRIGHT: (FLUSHRIGHT(FLUSHRIGHT (Function) NIL NIL ("25") 28) POS X MIN P2FLAG CENTERFLAG FILE) [Function] If CENTERFLAG=NIL, prints X right-flushed against position POS on FILE; otherwise, centers X between the current line position and POS. Makes sure that it spaces over at least MIN spaces before printing by doing a TERPRI if necessary; MIN=NIL is equivalent to MIN=1. A positive POS indicates an absolute position, while a negative POS signifies the position which is |POS| to the right of the current line position. POS=0 is interpreted as (LINELENGTH), the right margin. READFILE and WRITEFILE 1 For those applications where you simply want to simply read all of the expressions on a file, and not evaluate them, the function READFILE is available: (READFILE(READFILE (Function) NIL NIL ("25") 28) FILE RDTBL ENDTOKEN) [NoSpread Function] Reads successive expressions from file using READ (with read table RDTBL) until the single litatom ENDTOKEN is read, or an end of file encountered. Returns a list of these expressions. If RDTBL is not specified, it defaults to FILERDTBL. If ENDTOKEN is not specified, it defaults to the litatom STOP. (WRITEFILE(WRITEFILE (Function) NIL NIL ("25") 28) X FILE) [Function] Writes a date expression onto FILE, followed by successive expressions from X, using FILERDTBL as a read table. If X is atomic, its value is used. If FILE is not open, it is opened. If FILE is a list, (CAR FILE) is used and the file is left opened. Otherwise, when X is finished, the litatom STOP is printed on FILE and it is closed. Returns FILE. (ENDFILE(ENDFILE (Function) NIL NIL ("25") 29) FILE) [Function] Prints STOP on FILE and closes it. Read Tables 1 Many Interlisp input functions treat certain characters in special ways. For example, READ recognizes that the right and left parenthesis characters are used to specify list structures, and that the quote character is used to delimit text strings. The Interlisp input and (to a certain extent) output routines are table driven by read tables. Read tables are objects that specify the syntactic properties of characters for input routines. Since the input routines parse character sequences into objects, the read table in use determines which sequences are recognized as literal atoms, strings, list structures, etc. Most Interlisp input functions take an optional read table argument, which specifies the read table to use when reading an expression. If NIL is given as the read table, the "primary read table" is used. If T is specified, the system terminal read table is used. Some functions will also accept the atom ORIG (not the value of ORIG) as indicating the "original" system read table. Some output functions also take a read table argument. For example, PRIN2 prints an expression so that it would be read in correctly using a given read table. The Interlisp-D system uses the following read tables: T for input/output from terminals, the value of FILERDTBL for input/output from files, the value of EDITRDTBL for input from terminals while in the tty-based editor, the value of DEDITRDTBL for input from terminals while in the display-based editor, and the value of CODERDTBL for input/output from compiled files. These five read tables are initially copies of the ORIG read table, with changes made to some of them to provide read macros that are specific to terminal input or file input. Using the functions described below, you may further change, reset, or copy these tables. However, in the case of FILERDTBL and CODERDTBL, you are cautioned that changing these tables may prevent the system from being able to read files made with the original tables, or prevent users possessing only the standard tables from reading files made using the modified tables. You can also create new read tables, and either explicitly pass them to input/output functions as arguments, or install them as the primary read table, via SETREADTABLE, and then not specify a RDTBL argument, i.e., use NIL. Read Table Functions (READTABLEP(READTABLEP (Function) NIL NIL ("25") 29) RDTBL) [Function] Returns RDTBL if RDTBL is a real read table (not T or ORIG), otherwise NIL. (GETREADTABLE(GETREADTABLE (Function) NIL NIL ("25") 30) RDTBL) [Function] If RDTBL=NIL, returns the primary read table. If RDTBL=T, returns the system terminal read table. If RDTBL is a real read table, returns RDTBL. Otherwise, generates an ILLEGAL READTABLE error. (SETREADTABLE(SETREADTABLE (Function) NIL NIL ("25") 30) RDTBL FLG) [Function] Sets the primary read table to RDTBL. If FLG=T, SETREADTABLE sets the system terminal read table, T. Note that you can reset the other system read tables with SETQ, e.g., (SETQ FILERDTBL (GETREADTABLE)). Generates an ILLEGAL READTABLE error if RDTBL is not NIL, T, or a real read table. Returns the previous setting of the primary read table, so SETREADTABLE is suitable for use with RESETFORM (Chapter 14). (COPYREADTABLE(COPYREADTABLE (Function) NIL NIL ("25") 30) RDTBL) [Function] Returns a copy of RDTBL. RDTBL can be a real read table, NIL, T, or ORIG (in which case COPYREADTABLE returns a copy of the original system read table), otherwise COPYREADTABLE generates an ILLEGAL READTABLE error. Note that COPYREADTABLE is the only function that creates a read table. (RESETREADTABLE(RESETREADTABLE (Function) NIL NIL ("25") 30) RDTBL FROM) [Function] Copies (smashes) FROM into RDTBL. FROM and RDTBL can be NIL, T, or a real read table. In addition, FROM can be ORIG, meaning use the system's original read table. Syntax Classes A read table is an object that contains information about the "syntax class" of each character. There are nine basic syntax classes: LEFTPAREN, RIGHTPAREN, LEFTBRACKET, RIGHTBRACKET, STRINGDELIM, ESCAPE, BREAKCHAR, SEPRCHAR, and OTHER, each associated with a primitive syntactic property. In addition, there is an unlimited assortment of user-defined syntax classes, known as "read macros". The basic syntax classes are interpreted as follows: LEFTPAREN (normally left parenthesis) Begins list structure. RIGHTPAREN (normally right parenthesis) Ends list structure. LEFTBRACKET (normally left bracket) Begins list structure. Also matches RIGHTBRACKET characters. RIGHTBRACKET (normally left bracket) Ends list structure. Can close an arbitrary numbers of LEFTPAREN lists, back to the last LEFTBRACKET. STRINGDELIM (normally double quote) Begins and ends text strings. Within the string, all characters except for the one(s) with class ESCAPE are treated as ordinary, i.e., interpreted as if they were of syntax class OTHER. To include the string delimiter inside a string, prefix it with the ESCAPE character. ESCAPE (normally percent sign) Inhibits any special interpretation of the next character, i.e., the next character is interpreted to be of class OTHER, independent of its normal syntax class. BREAKCHAR (None initially) Is a break character, i.e., delimits atoms, but is otherwise an ordinary character. SEPRCHAR (space, carriage return, etc.) Delimits atoms, and is otherwise ignored. OTHER Characters that are not otherwise special belong to the class OTHER. Characters of syntax class LEFTPAREN, RIGHTPAREN, LEFTBRACKET, RIGHTBRACKET, and STRINGDELIM are all break characters. That is, in addition to their interpretation as delimiting list or string structures, they also terminate the reading of an atom. Characters of class BREAKCHAR serve only to terminate atoms, with no other special meaning. In addition, if a break character is the first non-separator encountered by RATOM, it is read as a one-character atom. In order for a break character to be included in an atom, it must be preceded by the ESCAPE character. Characters of class SEPRCHAR also terminate atoms, but are otherwise completely ignored; they can be thought of as logically spaces. As with break characters, they must be preceded by the ESCAPE character in order to appear in an atom. For example, if $ were a break character and * a separator character, the input stream ABC**DEF$GH*$$ would be read by six calls to RATOM returning respectively ABC, DEF, $, GH, $, $. Although normally there is only one character in a read table having each of the list- and string-delimiting syntax classes (such as LEFTPAREN), it is perfectly acceptable for any character to have any syntax class, and for more than one to have the same class. Note that a "syntax class" is an abstraction: there is no object referencing a collection of characters called a syntax class. Instead, a read table provides the association between a character and its syntax class, and the input/output routines enforce the abstraction by using read tables to drive the parsing. The functions below are used to obtain and set the syntax class of a character in a read table. CH can either be a character code (a integer), or a character (a single-character atom). Single-digit integers are interpreted as character codes, rather than as characters. For example, 1 indicates Control-A, and 49 indicates the character 1. Note that CH can be a full sixteen-bit NS character (see Chapter 2). Note: Terminal tables, described in Chapter 30, also associate characters with syntax classes, and they can also be manipulated with the functions below. The set of read table and terminal table syntax classes are disjoint, so there is never any ambiguity about which type of table is being referred to. (GETSYNTAX CH TABLE) [Function] Returns the syntax class of CH, a character or a character code, with respect to TABLE. TABLE can be NIL, T, ORIG, or a real read table or terminal table. CH can also be a syntax class, in which case GETSYNTAX returns a list of the character codes in TABLE that have that syntax class. (SETSYNTAX CHAR CLASS TABLE) [Function] Sets the syntax class of CHAR, a character or character code, in TABLE. TABLE can be either NIL, T, or a real read table or terminal table. SETSYNTAX returns the previous syntax class of CHAR. CLASS can be any one of the following: f The name of one of the basic syntax classes. f A list, which is interpreted as a read macro (see below). f NIL, T, ORIG, or a real read table or terminal table, which means to give CHAR the syntax class it has in the table indicated by CLASS. For example, (SETSYNTAX '%( 'ORIG TABLE) gives the left parenthesis character in TABLE the same syntax class that it has in the original system read table. f A character code or character, which means to give CHAR the same syntax class as the character CHAR in TABLE. For example, (SETSYNTAX '{ '%[ TABLE) gives the left brace character the same syntax class as the left bracket. (SYNTAXP(SYNTAXP (Function) NIL NIL ("25") 32) CODE CLASS TABLE) [Function] CODE is a character code; TABLE is NIL, T, or a real read table or terminal table. Returns T if CODE has the syntax class CLASS in TABLE; NIL otherwise. CLASS can also be a read macro type (MACRO, SPLICE, INFIX), or a read macro option (FIRST, IMMEDIATE, etc.), in which case SYNTAXP returns T if the syntax class is a read macro with the specified property. SYNTAXP will not accept a character as an argument, only a character code. For convenience in use with SYNTAXP, the atom BREAK may be used to refer to all break characters, i.e., it is the union of LEFTPAREN, RIGHTPAREN, LEFTBRACKET, RIGHTBRACKET, STRINGDELIM, and BREAKCHAR. For purely symmetrical reasons, the atom SEPR corresponds to all separator characters. However, since the only separator characters are those that also appear in SEPRCHAR, SEPR and SEPRCHAR are equivalent. Note that GETSYNTAX never returns BREAK or SEPR as a value although SETSYNTAX and SYNTAXP accept them as arguments. Instead, GETSYNTAX returns one of the disjoint basic syntax classes that comprise BREAK. BREAK as an argument to SETSYNTAX is interpreted to mean BREAKCHAR if the character is not already of one of the BREAK classes. Thus, if %( is of class LEFTPAREN, then (SETSYNTAX '%( 'BREAK) doesn't do anything, since %( is already a break character, but (SETSYNTAX '%( 'BREAKCHAR) means make %( be just a break character, and therefore disables the LEFTPAREN function of %(. Similarly, if one of the format characters is disabled completely, e.g., by (SETSYNTAX '%( 'OTHER), then (SETSYNTAX '%( 'BREAK) would make %( be only a break character; it would not restore %( as LEFTPAREN. The following functions provide a way of collectively accessing and setting the separator and break characters in a read table: (GETSEPR(GETSEPR (Function) NIL NIL ("25") 33) RDTBL) [Function] Returns a list of separator character codes in RDTBL. Equivalent to (GETSYNTAX 'SEPR RDTBL). (GETBRK(GETBRK (Function) NIL NIL ("25") 33) RDTBL) [Function] Returns a list of break character codes in RDTBL. Equivalent to (GETSYNTAX 'BREAK RDTBL). (SETSEPR(SETSEPR (Function) NIL NIL ("25") 33) LST FLG RDTBL) [Function] Sets or removes the separator characters for RDTBL. LST is a list of charactors or character codes. FLG determines the action of SETSEPR as follows: If FLG=NIL, makes RDTBL have exactly the elements of LST as separators, discarding from RDTBL any old separator characters not in LST. If FLG=0, removes from RDTBL as separator characters all elements of LST. This provides an "UNSETSEPR". If FLG=1, makes each of the characters in LST be a separator in RDTBL. If LST=T, the separator characters are reset to be those in the system's read table for terminals, regardless of the value of FLG, i.e., (SETSEPR T) is equivalent to (SETSEPR (GETSEPR T)). If RDTBL is T, then the characters are reset to those in the original system table. Returns NIL. (SETBRK(SETBRK (Function) NIL NIL ("25") 33) LST FLG RDTBL) [Function] Sets the break characters for RDTBL. Similar to SETSEPR. As with SETSYNTAX to the BREAK class, if any of the list- or string-delimiting break characters are disabled by an appropriate SETBRK (or by making it be a separator character), its special action for READ will not be restored by simply making it be a break character again with SETBRK. However, making these characters be break characters when they already are will have no effect. The action of the ESCAPE character (normally %) is not affected by SETSEPR or SETBRK. It can be disabled by setting its syntax to the class OTHER, and other characters can be used for escape on input by assigning them the class ESCAPE. As of this writing, however, there is no way to change the output escape character; it is "hardwired" as %. That is, on output, characters of special syntax that need to be preceded by the ESCAPE character will always be preceded by %, independent of the syntax of % or which, if any characters, have syntax ESCAPE. The following function can be used for defeating the action of the ESCAPE character or characters: (ESCAPE(ESCAPE (Function) NIL NIL ("25") 34) FLG RDTBL) [Function] If FLG=NIL, makes characters of class ESCAPE behave like characters of class OTHER on input. Normal setting is (ESCAPE T). ESCAPE returns the previous setting. Read Macros This is a description of the OLD-INTERLISP-T read macros. Read macros are user-defined syntax classes that can cause complex operations when certain characters are read. Read macro characters are defined by specifying as a syntax class an expression of the form: (TYPE OPTION1 ... OPTIONN FN) where TYPE is one of MACRO, SPLICE, or INFIX, and FN is the name of a function or a lambda expression. Whenever READ encounters a read macro character, it calls the associated function, giving it as arguments the input stream and read table being used for that call to READ. The interpretation of the value returned depends on the type of read macro: MACRO This is the simplest type of read macro. The result returned from the macro is treated as the expression to be read, instead of the read macro character. Often the macro reads more input itself. For example, in order to cause ~EXPR to be read as (NOT EXPR), one could define ~ as the read macro: [MACRO (LAMBDA (FL RDTBL) (LIST 'NOT (READ FL RDTBL] SPLICE The result (which should be a list or NIL) is spliced into the input using NCONC. For example, if $ is defined by the read macro: (SPLICE (LAMBDA NIL (APPEND FOO))) and the value of FOO is (A B C), then when you input (X $ Y), the result will be (X A B C Y). INFIX The associated function is called with a third argument, which is a list, in TCONC format (Chapter 3), of what has been read at the current level of list nesting. The function's value is taken as a new TCONC list which replaces the old one. For example, the infix operator + could be defined by the read macro: (INFIX (LAMBDA (FL RDTBL Z) (RPLACA (CDR Z) (LIST (QUOTE IPLUS) (CADR Z) (READ FL RDTBL))) Z)) If an INFIX read macro character is encountered not in a list, the third argument to its associated function is NIL. If the function returns NIL, the read macro character is essentially ignored and reading continues. Otherwise, if the function returns a TCONC list of one element, that element is the value of the READ. If it returns a TCONC list of more than one element, the list is the value of the READ. The specification for a read macro character can be augmented to specify various options OPTION1 ... OPTIONN, e.g., (MACRO FIRST IMMEDIATE FN). The following three disjoint options specify when the read macro character is to be effective: ALWAYS The default. The read macro character is always effective (except when preceded by the % character), and is a break character, i.e., a member of (GETSYNTAX 'BREAK RDTBL). FIRST The character is interpreted as a read macro character only when it is the first character seen after a break or separator character; in all other situations, the character is treated as having class OTHER. The read macro character is not a break character. For example, the quote character is a FIRST read macro character, so that DON'T is read as the single atom DON'T, rather than as DON followed by (QUOTE T). ALONE The read macro character is not a break character, and is interpreted as a read macro character only when the character would have been read as a separate atom if it were not a read macro character, i.e., when its immediate neighbors are both break or separator characters. Making a FIRST or ALONE read macro character be a break character (with SETBRK) disables the read macro interpretation, i.e., converts it to syntax class BREAKCHAR. Making an ALWAYS read macro character be a break character is a no-op. The following two disjoint options control whether the read macro character is to be protected by the ESCAPE character on output when a litatom containing the character is printed: ESCQUOTE or ESC The default. When printed with PRIN2, the read macro character will be preceded by the output escape character (%) as needed to permit the atom containing it to be read correctly. Note that for FIRST macros, this means that the character need be quoted only when it is the first character of the atom. NOESCQUOTE or NOESC The read macro character will always be printed without an escape. For example, the ? read macro in the T read table is a NOESCQUOTE character. Unless you are very careful what you are doing, read macro characters in FILERDTBL should never be NOESCQUOTE, since symbols that happen to contain the read macro character will not read back in correctly. The following two disjoint options control when the macro's function is actually executed: IMMEDIATE or IMMED The read macro character is immediately activated, i.e., the current line is terminated, as if an EOL had been typed, a carriage-return line-feed is printed, and the entire line (including the macro character) is passed to the input function. IMMEDIATE read macro characters enable you to specify a character that will take effect immediately, as soon as it is encountered in the input, rather than waiting for the line to be terminated. Note that this is not necessarily as soon as the character is typed. Characters that cause action as soon as they are typed are interrupt characters (see Chapter 30). Note that since an IMMEDIATE macro causes any input before it to be sent to the reader, characters typed before an IMMEDIATE read macro character cannot be erased by Control-A or Control-Q once the IMMEDIATE character has been typed, since they have already passed through the line buffer. However, an INFIX read macro can still alter some of what has been typed earlier, via its third argument. NONIMMEDIATE or NONIMMED The default. The read macro character is a normal character with respect to the line buffering, and so will not be activated until a carriage-return or matching right parenthesis or bracket is seen. Making a read macro character be both ALONE and IMMEDIATE is a contradiction, since ALONE requires that the next character be input in order to see if it is a break or separator character. Thus, ALONE read macros are always NONIMMEDIATE, regardless of whether or not IMMEDIATE is specified. Read macro characters can be "nested". For example, if = is defined by (MACRO (LAMBDA (FL RDTBL) (EVAL (READ FL RDTBL)))) and ! is defined by (SPLICE (LAMBDA (FL RDTBL) (READ FL RDTBL))) then if the value of FOO is (A B C), and (X =FOO Y) is input, (X (A B C) Y) will be returned. If (X !=FOO Y) is input, (X A B C Y) will be returned. Note: If a read macro's function calls READ, and the READ returns NIL, the function cannot distinguish the case where a RIGHTPAREN or RIGHTBRACKET followed the read macro character, (e.g. "(A B ')"), from the case where the atom NIL (or "()") actually appeared. In Interlisp-D, a READ inside of a read macro when the next input character is a RIGHTPAREN or RIGHTBRACKET reads the character and returns NIL, just as if the READ had not occurred inside a read macro. If a call to READ from within a read macro encounters an unmatched RIGHTBRACKET within a list, the bracket is simply put back into the buffer to be read (again) at the higher level. Thus, inputting an expression such as (A B '(C D] works correctly. (INREADMACROP(INREADMACROP (Function) NIL NIL ("25") 37)) [Function] Returns NIL if currently not under a read macro function, otherwise the number of unmatched left parentheses or brackets. (READMACROS(READMACROS (Function) NIL NIL ("25") 37) FLG RDTBL) [Function] If FLG=NIL, turns off action of read macros in read table RDTBL. If FLG=T, turns them on. Returns previous setting. The following read macros are standardly defined in Interlisp in the T and EDITRDTBL read tables: ' (single-quote) Returns the next expression, wrapped in a call to QUOTE; e.g., 'FOO reads as (QUOTE FOO). The macro is defined as a FIRST read macro, so that the quote character has no effect in the middle of a symbol. The macro is also ignored if the quote character is immediately followed by a separator character. Control-Y Defined in T and EDITRDTBL. Returns the result of evaluating the next expression. For example, if the value of FOO is (A B), then (LIST 1 control-YFOO 2) is read as (LIST 1 (A B) 2). Note that no structure is copied; the third element of that input expression is still EQ to the value of FOO. Control-Y can thus be used to read structures that ordinarily have no read syntax. For example, the value returned from reading (KEY1 Control-Y(ARRAY 10)) has an array as its second element. Control-Y can be thought of as an "un-quote" character. The choice of character to perform this function is changeable with SETTERMCHARS (see Chapter 16). ` (backquote) Backquote makes it easier to write programs to construct complex data structures. Backquote is like quote, except that within the backquoted expression, forms can be evaluated. The general idea is that the backquoted expression is a "template" containing some constant parts (as with a quoted form) and some parts to be filled in by evaluating something. Unlike with control-Y, however, the evaluation occurs not at the time the form is read, but at the time the backquoted expression is evaluated. That is, the backquote macro returns an expression which, when evaluated, produces the desired structure. Within the backquoted expression, the character "," (comma) introduces a form to be evaluated. The value of a form preceded by ",@" is to be spliced in, using APPEND. If it is permissible to destroy the list being spliced in (i.e., NCONC may be used in the translation), then ",." can be used instead of ",@". For example, if the value of FOO is (1 2 3 4), then the form `(A ,(CAR FOO) ,@(CDDR FOO) D E) evaluates to (A 1 3 4 D E); it is logically equivalent to writing (CONS 'A (CONS (CAR FOO) (APPEND (CDDR FOO) '(D E)))) . Backquote is particularly useful for writing macros. For example, the body of a macro that refers to X as the macro's argument list might be `(COND ((FIXP ,(CAR X)) ,(CADR X)) (T .,(CDDR X))) which is equivalent to writing (LIST 'COND (LIST (LIST 'FIXP (CAR X)) (CADR X)) (CONS 'T (CDDR X))) Note that comma does not have any special meaning outside of a backquote context. For users without a backquote character on their keyboards, backquote can also be written as |' (vertical-bar, quote). ? Implements the ?= command for on-line help regarding the function currently being "called" in the typein (see Chapter 26). | (vertical bar) When followed by an end of line, tab or space, | is ignored, i.e., treated as a separator character, enabling the editor's CHANGECHAR feature (see Chapter 26). Otherwise it is a "dispatching" read macro whose meaning depends on the character(s) following it. The following are currently defined: ' (quote) -- A synonym for backquote. . (period) -- Returns the evaluation of the next expression, i.e., this is a synonym for Control-Y. , (comma) -- Returns the evaluation of the next expression at load time, i.e., the following expression is quoted in such a manner that the compiler treats it as a literal whose value is not determined until the compiled expression is loaded. O or o (the letter O) -- Treats the next number as octal, i.e., reads it in radix 8. For example, |o12 = 10 (decimal). B or b -- Treats the next number as binary, i.e., reads it in radix 2. For example, |b101 = 5 (decimal). X or x -- Treats the next number as hexadecimal, i.e., reads it in radix 16. The uppercase letters A though F are used as the digits after 9. For example, |x1A = 26 (decimal). R or r -- Reads the next number in the radix specified by the (decimal) number that appears between the | and the R. When inputting a number in a radix above ten, the upper-case letters A through Z can be used as the digits after 9 (but there is no digit above Z, so it is not possible to type all base-99 digits). For example, |3r120 reads 120 in radix 3, returning 15. (, {, ^ -- Used internally by HPRINT and HREAD (see above) to print and read unusual expressions. The dispatching characters that are letters can appear in either upper- or lowercase. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "24-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "24-" "")) (192 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "24-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "24-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "24-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "24-" "")) (192 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (408 612 204 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))25xlx2lxx2Hll5xlx3(T/HHF PAGEHEADING RIGHTPAGE@ PAGEHEADINGLEFTBACKT-T,3T206 -306T306 -T306 -T2/52HHl2l/2(,2l,,HH/5,2lll2HHl2l22l2l5,,52l -2Hll3(T2Hl -l,HH,,3(T,,, TITAN PALATINO PALATINO PALATINO TITAN TITAN TITAN MODERN -MODERN -   IM.CHAP.GETFN - HRULE.GETFNMODERN - 0*-IM.INDEX.GETFN  HRULE.GETFNMODERN - -IO - 81Ut J -J 'IM.INDEX.GETFNTITAN  ,%$UJ %IM.INDEX.GETFNTITAN  , /  HRULE.GETFNMODERN - .,  - - 6 S-a"-"$#IM.INDEX.GETFNTITAN  ,E,A,"IM.INDEX.GETFNTITAN  ,Ki   ,tR ,)#a ),6  ,,2V6 ,%W,S/%IM.INDEX.GETFNTITAN  ,.EL$IM.INDEX.GETFNTITAN ,;$IM.INDEX.GETFNTITAN  , S ,  , ) %IM.INDEX.GETFN ,0N% ? -%IM.INDEX.GETFNTITAN  ,8#IM.INDEX.GETFNTITAN  ,%E2 'IM.INDEX.GETFNTITAN  ,B'IM.INDEX.GETFNTITAN  ,/B!IM.INDEX.GETFNMODERN - ,X+ % j.c#IM.INDEX.GETFNTITAN  ,- -,% /,:   31"IM.INDEX.GETFNTITAN  ,N > , -    ,IM.INDEX.GETFNMODERN - ,$! *z,' B%  $IM.INDEX.GETFNMODERN -  ,c =; 8u, =- , >)U 'IM.INDEX.GETFNMODERN - ,> Tm  *6IM.INDEX.GETFN  HRULE.GETFNMODERN - .RXP),--E"-7J&$IM.INDEX.GETFNTITAN  ,G,B"#IM.INDEX.GETFNTITAN  ,#IM.INDEX.GETFNTITAN  ,>.S1"\D* X<#IM.INDEX.GETFNTITAN  -  #IM.INDEX.GETFNTITAN  ,#IM.INDEX.GETFNTITAN  ,& -#IM.INDEX.GETFNTITAN  ,)L!, -* -"IM.INDEX.GETFNTITAN  , -3.( -& -j $IM.INDEX.GETFNTITAN  ,&IM.INDEX.GETFN ,* 'IM.INDEX.GETFNTITAN  ,Q !IM.INDEX.GETFNTITAN   ,< /,  - ++@  'IM.INDEX.GETFNTITAN  ,  'IM.INDEX.GETFNTITAN  , /. - I  -Y  ;(IM.INDEX.GETFNMODERN - , )IM.INDEX.GETFNMODERN -  ,#, :&IM.INDEX.GETFNMODERN - ,,v   !  -(IM.INDEX.GETFNPALATINO  ,62)!T,Et +IM.INDEX.GETFNPALATINO  ,/ u .| -X. -)IM.INDEX.GETFNMODERN - ,  - - , -  $  "  4(8 -7 , -, f| f7, -'  '+'%'''#'''  ,IM.INDEX.GETFN , - -s   -".)IM.INDEX.GETFN ,  0" 'IM.INDEX.GETFNMODERN - , 'IM.INDEX.GETFNMODERN - ,  7IM.INDEX.GETFN .%8*QG$IM.INDEX.GETFNMODERN - ,>L, -!    -, ~ %IM.INDEX.GETFNTITAN  ,; ,J, 4.+%IM.INDEX.GETFNMODERN -  , -",  !& %$& -% , -   K,;,25.@J ,; ,  -@# *  -z S ,5N$    $ $$ $, -+/K K4~,V+,5S$   $ $$$ $$-IM.INDEX.GETFNMODERN - ,"40)6^  . -8'IM.INDEX.GETFNMODERN - ,0.4Q, =, 5, )H   .^-& -*%IM.INDEX.GETFNMODERN - -  , -+ -:, W$," - b , $IM.INDEX.GETFNMODERN - , 'IM.INDEX.GETFNMODERN - ,? , , ,&7R;/  HRULE.GETFNMODERN - - K9- -A i)0:- -` -)IM.INDEX.GETFNTITAN  ,5Q -)IM.INDEX.GETFNTITAN  , -8 -z P e -7 (IM.INDEX.GETFNTITAN  ,n  : *IM.INDEX.GETFNPALATINO ,W(  (IM.INDEX.GETFNMODERN - ,&,W 9, - I W (IM.INDEX.GETFNMODERN - , {%" 2*={  &IM.INDEX.GETFNMODERN -  , = -26,QP-,    ,,+ a e  C,K  !C'IM.INDEX.GETFNMODERN -  ,,,B& (IM.INDEX.GETFNMODERN - ,q +IM.INDEX.GETFNMODERN -  ,  +IM.INDEX.GETFNMODERN -  ,  'IM.INDEX.GETFNMODERN - ,-) *     -IM.INDEX.GETFNTITAN  ,/2  HRULE.GETFNMODERN - .]-0-/"-Z  -o 3 9,- -&IM.INDEX.GETFNMODERN -  ,0  Y,  *   HRULE.GETFNMODERN - .t&- -8 --aNg -- PQ      --_ 4% -  - - -6)J+"*- 1 T s-Z#   .C"(: - ,, )4"IM.INDEX.GETFNMODERN -"IM.INDEX.GETFN , -F $IM.INDEX.GETFNMODERN -#IM.INDEX.GETFN ,<    ,UIM.INDEX.GETFN   IM.INDEX.GETFN   !IM.INDEX.GETFN ,t IM.INDEX.GETFNTITAN !IM.INDEX.GETFNPALATINO ,  .(5IM.INDEX.GETFNPALATINO ,U$IM.INDEX.GETFNTITAN #IM.INDEX.GETFNPALATINO , d$IM.INDEX.GETFNTITAN #IM.INDEX.GETFNPALATINO ,.$IM.INDEX.GETFNTITAN #IM.INDEX.GETFN,8 xF, E "IM.INDEX.GETFNTITAN "IM.INDEX.GETFNPALATINO ,  "IM.INDEX.GETFNTITAN "IM.INDEX.GETFNPALATINO ,$IM.INDEX.GETFNPALATINO #IM.INDEX.GETFN,I1 .![ea --a IM.INDEX.GETFNTITAN !IM.INDEX.GETFNPALATINO ,"IM.INDEX.GETFNTITAN "IM.INDEX.GETFNPALATINO ,6 I#"IM.INDEX.GETFNTITAN "IM.INDEX.GETFNPALATINO , 6&IM.INDEX.GETFNTITAN $IM.INDEX.GETFNPALATINO ,&IM.INDEX.GETFNPALATINO $IM.INDEX.GETFN, .PA$IM.INDEX.GETFNTITAN #IM.INDEX.GETFNPALATINO , -, -7 (&IM.INDEX.GETFNTITAN $IM.INDEX.GETFNPALATINO ,  -.~ - IM.INDEX.GETFNTITAN !IM.INDEX.GETFNPALATINO ,/^[6 "IM.INDEX.GETFNPALATINO "IM.INDEX.GETFN,  -.(IM.INDEX.GETFNTITAN %IM.INDEX.GETFNPALATINO ,N,`*IM.INDEX.GETFNPALATINO &IM.INDEX.GETFN,   .IM.INDEX.GETFNTITAN IM.INDEX.GETFN,B` 'pB ,IM.INDEX.GETFNTITAN IM.INDEX.GETFN,.* IM.INDEX.GETFNTITAN IM.INDEX.GETFN ,) Y O .4 -$ PIM.INDEX.GETFNMODERN - ,Y B &*%+) 7&!D ]? 9.-8-IM.INDEX.GETFNTITAN ,6 0CE,YMM,3;    +, -.J % (IM.INDEX.GETFNTITAN  ,w.  :,} - -1,\ 0 % ,   M .M - -)IM.INDEX.GETFNTITAN  -, -  '+#2".    HRULE.GETFNMODERN - .'IM.INDEX.GETFNMODERN - ,-O," . (IM.INDEX.GETFNMODERN - ,* #  7&IM.INDEX.GETFNMODERN - ,*   HRULE.GETFNMODERN - .W-CaxV-8/ + F -N [  -   -)IM.INDEX.GETFNTITAN   +IM.INDEX.GETFNTITAN &. +IM.INDEX.GETFNTITAN  &=  -T   ,IM.INDEX.GETFNTITAN    - -IM.INDEX.GETFNTITAN &/  .  -     5 -4 ?   R   |LG * gK?  -     | *) zr & b 92 ,3*,+ * $+ &"0=>3)F7(K&IM.INDEX.GETFNPALATINO 3    B5 ,  -    ,v -  % @  /  # /  O   &IM.INDEX.GETFNPALATINO /%IM.INDEX.GETFNPALATINO + -&IM.INDEX.GETFNPALATINO -. %) #v G%IM.INDEX.GETFNPALATINO  aDAJ9SlT&*C%IM.INDEX.GETFNPALATINO ! -    # =O21$'"% NyC%211 11$1 %=o7=YbY98;   1L  7hI!LRg -V -V  -a] c e W J `Y ( k  8      ( - 3 - +!); - !' 2    +IM.INDEX.GETFNTITAN  ]   -)IM.INDEX.GETFNMODERN -  0,E A   W  Y   n3OD( # ( %h'211122 1 11:`  j -%c: ^ P ^ lH !D%5Xz \ No newline at end of file + +INTERLISP-D REFERENCE MANUAL +I/O FUNCTIONS + + "25"24. INPUT/OUTPUT FUNCTIONS +2 + +This chapter describes the standard I/O functions used for reading and printing characters and Interlisp expressions on files and other streams. First, the primitive input functions are presented, then the output functions, then functions for random-access operations (such as searching a file for a given stream, or changing the "next-character" pointer to a position in a file). Next, the PRINTOUT statement is documented (see below), which provides an easy way to write complex output operations. Finally, read tables, used to parse characters as Interlisp expressions, are documented. +Specifying Streams for Input/Output Functions(INPUT/OUTPUT% FUNCTIONS NIL Input/Output% Functions NIL ("25") 1 SUBNAME SPECIFYING% STREAMS% FOR SUBTEXT specifying% streams% for) +1 + +Most of the input/output functions in Interlisp-D have an argument named STREAM or FILE, specifying on which open stream the function's action should occur (the name FILE is used in older functions that predate the concept of stream; the two should, however, be treated synonymously). The value of this argument should be one of the following: + a stream An object of type STREAM, as returned by OPENSTREAM (Chapter 23) or other stream-producing functions, is always the most precise and efficient way to designate a stream argument. + T The litatom T designates the terminal input or output stream of the currently running process, controlling input from the keyboard and output to the display screen. For functions where the direction (input or output) is ambiguous, T is taken to designate the terminal output stream. The T streams are always open; they cannot be closed. + The terminal output stream can be set to a given window or display stream by using TTYDISPLAYSTREAM (Chapter 28). The terminal input stream cannot be changed. For more information on terminal I/O, see Chapter 30. + NIL The litatom NIL designates the "primary" input or output stream. These streams are initially the same as the terminal input/output streams, but they can be changed by using the functions INPUT and OUTPUT. + For functions where the direction (input or output) is ambiguous, e.g., GETFILEPTR, the argument NIL is taken to mean the primary input stream, if that stream is not identical to the terminal input stream, else the primary output stream. + a window Uses the display stream of the window . Valid for output only. + a file name As of this writing, the name of an open file (as a litatom) can be used as a stream argument. However, there are inefficiencies and possible future incompatibilities associated with doing so. See Chapter 24 for details. +(GETSTREAM(GETSTREAM (Function) NIL NIL ("25") 2) FILE ACCESS) [Function] +Coerces the argument FILE to a stream by the above rules. If ACCESS is INPUT, OUTPUT, or BOTH, produces the stream designated by FILE that is open for ACCESS. If ACCESS=NIL, returns a stream for FILE open for any kind of input/output (see the list above for the ambiguous cases). If FILE does not designate a stream open in the specified mode, causes an error, FILE NOT OPEN. +(STREAMP(STREAMP (Function) NIL NIL ("25") 2) X) [Function] +Returns X if X is a STREAM, otherwise NIL. +Input Functions +1 + +While the functions described below can take input from any stream, some special actions occur when the input is from the terminal (the T input stream, see above). When reading from the terminal, the input is buffered a line at a time, unless buffering has been inhibited by CONTROL (Chapter 30) or the input is being read by READC or PEEKC. Using specified editing characters, you can erase a character at a time, a word at a time, or the whole line. The keys that perform these editing functions are assignable via SETSYNTAX, with the initial settings chosen to be those most natural for the given operating system. In Interlisp-D, the initial settings are as follows: characters are deleted one at a time by Backspace; words are erased by control-W; the whole line is erased by Control-Q. +On the Interlisp-D display, deleting a character or a line causes the characters to be physically erased from the screen. In Interlisp-10, the deleting action can be modified for various types of display terminals by using DELETECONTROL (Chapter 30). +Unless otherwise indicated, when the end of file is encountered while reading from a file, all input functions generate an error, END OF FILE. Note that this does not close the input file. The ENDOFSTREAMOP stream attribute (Chapter 24) is useful for changing the behavior at end of file. +Most input functions have a RDTBL argument, which specifies the read table to be used for input. Unless otherwise specified, if RDTBL is NIL, the primary read table is used. +If the FILE or STREAM argument to an input function is NIL, the primary input stream is used. +(INPUT(INPUT (Function) NIL NIL ("25") 2) FILE) [Function] +Sets FILE as the primary input stream; returns the old primary input stream. FILE must be open for input. +(INPUT) returns the current primary input stream, which is not changed. +Note: If the primary input stream is set to a file, the file's full name, rather than the stream itself, is returned. See discussion in Chapter 24. +(READ(READ (Function) NIL NIL ("25") 3) FILE RDTBL FLG) [Function] +Reads one expression from FILE. Atoms are delimited by the break and separator characters as defined in RDTBL. To include a break or separator character in an atom, the character must be preceded by the character %, e.g., AB%(C is the atom AB(C, %% is the atom %, %control-K is the atom Control-K. For input from the terminal, an atom containing an interrupt character can be input by typing instead the corresponding alphabetic character preceded by Control-V, e.g., VD for Control-D. +Strings are delimited by double quotes. To input a string containing a double quote or a %, precede it by %, e.g., "AB%"C" is the string AB"C. Note that % can always be typed even if next character is not "special", e.g., %A%B%C is read as ABC. +If an atom is interpretable as a number, READ creates a number, e.g., 1E3 reads as a floating point number, 1D3 as a literal atom, 1.0 as a number, 1,0 as a literal atom, etc. An integer can be input in a non-decimal radix by using syntax such as 123Q, |b10101, |5r1234 (see Chapter 7). The function RADIX, sets the radix used to print integers. +When reading from the terminal, all input is line-buffered to enable the action of the backspacing control characters, unless inhibited by CONTROL (Chapter 30). Thus no characters are actually seen by the program until a carriage-return (actually the character with terminal syntax class EOL, see Chapter 30), is typed. However, for reading by READ, when a matching right parenthesis is encountered, the effect is the same as though a carriage-return were typed, i.e., the characters are transmitted. To indicate this, Interlisp also prints a carriage-return line-feed on the terminal. The line buffer is also transmitted to READ whenever an IMMEDIATE read macro character is typed (see below). +FLG=T suppresses the carriage-return normally typed by READ following a matching right parenthesis. (However, the characters are still given to READ; i.e., you do not have to type the carriage-return.) +(RATOM FILE RDTBL) [Function] +Reads in one atom from FILE. Separation of atoms is defined by RDTBL. % is also defined for RATOM, and the remarks concerning line-buffering and editing control characters also apply. +If the characters comprising the atom would normally be interpreted as a number by READ, that number is returned by RATOM. Note however that RATOM takes no special action for " whether or not it is a break character, i.e., RATOM never makes a string. +(RSTRING(RSTRING (Function) NIL NIL ("25") 3) FILE RDTBL) [Function] +Reads characters from FILE up to, but not including, the next break or separator character, and returns them as a string. Backspace, Control-W, Control-Q, Control-V, and % have the same effect as with READ. +Note that the break or separator character that terminates a call to RATOM or RSTRING is not read by that call, but remains in the buffer to become the first character seen by the next reading function that is called. If that function is RSTRING, it will return the null string. This is a common source of program bugs. +(RATOMS(RATOMS (Function) NIL NIL ("25") 4) A FILE RDTBL) [Function] +Calls RATOM repeatedly until the atom A is read. Returns a list of the atoms read, not including A. +(RATEST(RATEST (Function) NIL NIL ("25") 4) FLG) [Function] +If FLG = T, RATEST returns T if a separator was encountered immediately prior to the atom returned by the last RATOM or READ, NIL otherwise. +If FLG = NIL, RATEST returns T if last atom read by RATOM or READ was a break character, NIL otherwise. +If FLG = 1, RATEST returns T if last atom read (by READ or RATOM) contained a % used to quote the next character (as in %[ or %A%B%C), NIL otherwise. +(READC (READC% (Function) NIL NIL ("25") 4)FILE RDTBL) [Function] +Reads and returns the next character, including %, ", etc, i.e., is not affected by break or separator characters. The action of READC is subject to line-buffering, i.e., READC does not return a value until the line has been terminated even if a character has been typed. Thus, the editing control characters have their usual effect. RDTBL does not directly affect the value returned, but is used as usual in line-buffering, e.g., determining when input has been terminated. If (CONTROL T) has been executed (Chapter 30), defeating line-buffering, the RDTBL argument is irrelevant, and READC returns a value as soon as a character is typed (even if the character typed is one of the editing characters, which ordinarily would never be seen in the input buffer). +(PEEKC (PEEKC% (Function) NIL NIL ("25") 4)FILE) [Function] +Returns the next character, but does not actually read it and remove it from the buffer. If reading from the terminal, the character is echoed as soon as PEEKC reads it, even though it is then "put back" into the system buffer, where Backspace, Control-W, etc. could change it. Thus it is possible for the value returned by PEEKC to "disagree" in the first character with a subsequent READ. +(LASTC(LASTC (Function) NIL NIL ("25") 4) FILE) [Function] +Returns the last character read from FILE. LASTC can return an incorrect result when called immediatley following a PEEKC on a file that contains run-coded NS characters. +(READCCODE(READCCODE (Function) NIL NIL ("25") 4) FILE RDTBL) [Function] +Returns the next character code from STREAM; thus, this operation is equivalent to, but more efficient than, (CHCON1 (READC FILE RDTBL)). +(PEEKCCODE(PEEKCCODE (Function) NIL NIL ("25") 5) FILE) [Function] +Returns, without consuming, the next character code from STREAM; thus, this operation is equivalent to, but more efficient than, (CHCON1 (PEEKC FILE)). +(BIN(BIN (Function) NIL NIL ("25") 5) STREAM) [Function] +Returns the next byte from STREAM. This operation is useful for reading streams of binary, rather than character, data. +Note: BIN is similar to READCCODE, except that BIN always reads a single byte, whereas READCCODE reads a "character" that can consist of more than one byte, depending on the character and its encoding. +READ, RATOM, RATOMS, PEEKC, READC all wait for input if there is none. The only way to test whether or not there is input is to use READP: +(READP(READP (Function) NIL NIL ("25") 5) FILE FLG) [Function] +Returns T if there is anything in the input buffer of FILE, NIL otherwise. This operation is only interesting for streams whose source of data is dynamic, e.g., the terminal or a byte stream over a network; for other streams, such as to files, (READP FILE) is equivalent to (NOT (EOFP FILE)). +Note that because of line-buffering, READP may return T, indicating there is input in the buffer, but READ may still have to wait. +Frequently, the terminal's input buffer contains a single EOL character left over from a previous input. For most applications, this situation wants to be treated as though the buffer were empty, and so READP returns NIL in this case. However, if FLG=T, READP returns T if there is any character in the input buffer, including a single EOL. FLG is ignored for streams other than the terminal. +(EOFP(EOFP (Function) NIL NIL ("25") 5) FILE) [Function] +Returns true if FILE is at "end of file", i.e., the next call to an input function would cause an END OF FILE error; NIL otherwise. For randomly accessible files, this can also be thought of as the file pointer pointing beyond the last byte of the file. FILE must be open for (at least) input, or an error is generated, FILE NOT OPEN. +Note that EOFP can return NIL and yet the next call to READ might still cause an END OF FILE error, because the only characters remaining in the input were separators or otherwise constituted an incomplete expression. The function SKIPSEPRS is sometimes more useful as a way of detecting end of file when it is known that all the expressions in the file are well formed. +(WAITFORINPUT (WAITFORINPUT% (Function) NIL NIL ("25") 5)FILE) [Function] +Waits until input is available from FILE or from the terminal, i.e. from T. WAITFORINPUT is functionally equivalent to (until (OR (READP T) (READP FILE)) do NIL), except that it does not use up machine cycles while waiting. Returns the device for which input is now available, i.e. FILE or T. +FILE can also be an integer, in which case WAITFORINPUT waits until there is input available from the terminal, or until FILE milliseconds have elapsed. Value is T if input is now available, NIL in the case that WAITFORINPUT timed out. +(SKREAD(SKREAD (Function) NIL NIL ("25") 6) FILE REREADSTRING RDTBL) [Function] +"Skip Read". SKREAD consumes characters from FILE as if one call to READ had been performed, without paying the storage and compute cost to really read in the structure. REREADSTRING is for the case where the caller has already performed some READC's and RATOM's before deciding to skip this expression. In this case, REREADSTRING should be the material already read (as a string), and SKREAD operates as though it had seen that material first, thus setting up its parenthesis count, double-quote count, etc. +The read table RDTBL is used for reading from FILE. If RDTBL is NIL, it defaults to the value of FILERDTBL. SKREAD may have difficulties if unusual read macros are defined in RDTBL. SKREAD does not recognize read macro characters in REREADSTRING, nor SPLICE or INFIX read macros. This is only a problem if the read macros are defined to parse subsequent input in the stream that does not follow the normal parenthesis and string-quote conventions. +SKREAD returns %) if the read terminated on an unbalanced closing parenthesis; %] if the read terminated on an unbalanced %], i.e., one which also would have closed any extant open left parentheses; otherwise NIL. +(SKIPSEPRS(SKIPSEPRS (Function) NIL NIL ("25") 6) FILE RDTBL) [Function] +Consumes characters from FILE until it encounters a non-separator character (as defined by RDTBL). SKIPSEPRS returns, but does not consume, the terminating character, so that the next call to READC would return the same character. If no non-separator character is found before the end of file is reached, SKIPSEPRS returns NIL and leaves the stream at end of file. This function is useful for skipping over "white space" when scanning a stream character by character, or for detecting end of file when reading expressions from a stream with no pre-arranged terminating expression. +Output Functions(OUTPUT% FUNCTIONS NIL Output% Functions NIL ("25") 6) +1 + +Unless otherwise specified by DEFPRINT, pointers other than lists, strings, atoms, or numbers, are printed in the form {DATATYPE} followed by the octal representation of the address of the pointer (regardless of radix). For example, an array pointer might print as {ARRAYP}#43,2760. This printed representation is for compactness of display on your terminal, and will not read back in correctly; if the form above is read, it will produce the litatom {ARRAYP}#43,2760. +Note: The term "end-of-line" appearing in the description of an output function means the character or characters used to terminate a line in the file system being used by the given implementation of Interlisp. For example, in Interlisp-D end-of-line is indicated by the character carriage-return. +Some of the functions described below have a RDTBL argument, which specifies the read table to be used for output. If RDTBL is NIL, the primary read table is used. +Most of the functions described below have an argument FILE, which specifies the stream on which the operation is to take place. If FILE is NIL, the primary output stream is used . +(OUTPUT(OUTPUT (Function) NIL NIL ("25") 7) FILE) [Function] +Sets FILE as the primary output stream; returns the old primary output stream. FILE must be open for output. +(OUTPUT) returns the current primary output stream, which is not changed. +Note: If the primary output stream is set to a file, the file's full name, rather than the stream itself, is returned. See the discussion in Chapter 24. +(PRIN1(PRIN1 (Function) NIL NIL ("25") 7) X FILE) [Function] +Prints X on FILE. +(PRIN2(PRIN2 (Function) NIL NIL ("25") 7) X FILE RDTBL) [Function] +Prints X on FILE with %'s and "'s inserted where required for it to read back in properly by READ, using RDTBL. +Both PRIN1 and PRIN2 print any kind of Lisp expression, including lists, atoms, numbers, and strings. PRIN1 is generally used for printing expressions where human readability, rather than machine readability, is important, e.g., when printing text rather than program fragments. PRIN1 does not print double quotes around strings, or % in front of special characters. PRIN2 is used for printing Interlisp expressions which can then be read back into Interlisp with READ; i.e., break and separator characters in atoms will be preceded by %'s. For example, the atom "()" is printed as %(%) by PRIN2. If the integer output radix (as set by RADIX) is not 10, PRIN2 prints the integer using the input syntax for non-decimal integers (see Chapter 7) but PRIN1 does not (but both print the integer in the output radix). +(PRIN3(PRIN3 (Function) NIL NIL ("25") 7) X FILE) [Function] +(PRIN4(PRIN4 (Function) NIL NIL ("25") 7) X FILE RDTBL) [Function] +PRIN3 and PRIN4 are the same as PRIN1 and PRIN2 respectively, except that they do not increment the horizontal position counter nor perform any linelength checks. They are useful primarily for printing control characters. +(PRINT(PRINT (Function) NIL NIL ("25") 7) X FILE RDTBL) [Function] +Prints the expression X using PRIN2 followed by an end-of-line. Returns X. +(PRINTCCODE(PRINT (Function) NIL NIL ("25") 8) CHARCODE FILE) [Function] +Outputs a single character whose code is CHARCODE to FILE. This is similar to (PRIN1 (CHARACTER CHARCODE)), except that numeric characters are guaranteed to print "correctly"; e.g., (PRINTCCODE (CHARCODE 9)) always prints "9", independent of the setting of RADIX. +PRINTCCODE may actually print more than one byte on FILE, due to character encoding and end of line conventions; thus, no assumptions should be made about the relative motion of the file pointer (see GETFILEPTR) during this operation. +(BOUT(BOUT (Function) NIL NIL ("25") 8) STREAM BYTE) [Function] +Outputs a single 8-bit byte to STREAM. This is similar to PRINTCCODE, but for binary streams the character position in STREAM is not updated (as with PRIN3), and end of line conventions are ignored. +Note: BOUT is similar to PRINTCCODE, except that BOUT always writes a single byte, whereas PRINTCCODE writes a "character" that can consist of more than one byte, depending on the character and its encoding. +(SPACES(SPACES (Function) NIL NIL ("25") 8) N FILE) [Function] +Prints N spaces. Returns NIL. +(TERPRI (TERPRI% (Function) NIL NIL ("25") 8)FILE) [Function] +Prints an end-of-line character. Returns NIL. +(FRESHLINE(FRESHLINE (Function) NIL NIL ("25") 8) STREAM) [Function] +Equivalent to TERPRI, except it does nothing if it is already at the beginning of the line. Returns T if it prints an end-of-line, NIL otherwise. +(TAB(TAB (Function) NIL NIL ("25") 8) POS MINSPACES FILE) [Function] +Prints the appropriate number of spaces to move to position POS. MINSPACES indicates how many spaces must be printed (if NIL, 1 is used). If the current position plus MINSPACES is greater than POS, TAB does a TERPRI and then (SPACES POS). If MINSPACES is T, and the current position is greater than POS, then TAB does nothing. +Note: A sequence of PRINT, PRIN2, SPACES, and TERPRI expressions can often be more conveniently coded with a single PRINTOUT statement. +(SHOWPRIN2(SHOWPRIN2 (Function) NIL NIL ("25") 8) X FILE RDTBL) [Function] +Like PRIN2 except if SYSPRETTYFLG=T, prettyprints X instead. Returns X. +(SHOWPRINT(SHOWPRINT (Function) NIL NIL ("25") 9) X FILE RDTBL) [Function] +Like PRINT except if SYSPRETTYFLG=T, prettyprints X instead, followed by an end-of-line. Returns X. +SHOWPRINT and SHOWPRIN2 are used by the programmer's assistant (Chapter 13) for printing the values of expressions and for printing the history list, by various commands of the break package (Chapter 14), e.g. ?= and BT commands, and various other system packages. The idea is that by simply settting or binding SYSPRETTYFLG to T (initially NIL), you instruct the system when interacting with you to PRETTYPRINT expressions (Chapter 26) instead of printing them. +(PRINTBELLS(PRINTBELLS (Function) NIL NIL ("25") 9) ) [Function] +Used by DWIM (Chapter 19) to print a sequence of bells to alert you to stop typing. Can be advised or redefined for special applications, e.g., to flash the screen on a display terminal. +(FORCEOUTPUT(FORCEOUTPUT (Function) NIL NIL ("25") 9) STREAM WAITFORFINISH) [Function] +Forces any buffered output data in STREAM to be transmitted. +If WAITFORFINISH is non-NIL, this doesn't return until the data has been forced out. +(POSITION(POSITION (Function) NIL NIL ("25") 9) FILE N) [Function] +Returns the column number at which the next character will be read or printed. After a end of line, the column number is 0. If N is non-NIL, resets the column number to be N. +Note that resetting POSITION only changes Lisp's belief about the current column number; it does not cause any horizontal motion. Also note that (POSITION FILE) is not the same as (GETFILEPTR FILE) which gives the position in the file, not on the line. +(LINELENGTH(LINELENGTH (Function) NIL NIL ("25") 9) N FILE) [Function] +Sets the length of the print line for the output file FILE to N; returns the former setting of the line length. FILE defaults to the primary output stream. (LINELENGTH NIL FILE) returns the current setting for FILE. When a file is first opened, its line length is set to the value of the variable FILELINELENGTH. +Whenever printing an atom or string would increase a file's position beyond the line length of the file, an end of line is automatically inserted first. This action can be defeated by using PRIN3 and PRIN4. +(SETLINELENGTH(SETLINELENGTH (Function) NIL NIL ("25") 9) N) [Function] +Sets the line length for the terminal by doing (LINELENGTH N T). If N is NIL, it determines N by consulting the operating system's belief about the terminal's characteristics. In Interlisp-D, this is a no-op. +PRINTLEVEL +When using Interlisp one often has to handle large, complicated lists, which are difficult to understand when printed out. PRINTLEVEL allows you to specify in how much detail lists should be printed. The print functions PRINT, PRIN1, and PRIN2 are all affected by level parameters set by: +(PRINTLEVEL(PRINTLEVEL (Function) NIL NIL ("25") 10) CARVAL CDRVAL) [Function] +Sets the CAR print level to CARVAL, and the CDR print level to CDRVAL. Returns a list cell whose CAR and CDR are the old settings. PRINTLEVEL is initialized with the value (1000 . -1). +In order that PRINTLEVEL can be used with RESETFORM or RESETSAVE, if CARVAL is a list cell it is equivalent to (PRINTLEVEL (CAR CARVAL) (CDR CARVAL)). +(PRINTLEVEL N NIL) changes the CAR printlevel without affecting the CDR printlevel. (PRINTLEVEL NIL N) changes the CDR printlevel with affecting the CAR printlevel. (PRINTLEVEL) gives the current setting without changing either. +Note: Control-P (Chapter 30) can be used to change the PRINTLEVEL setting dynamically, even while Interlisp is printing. +The CAR printlevel specifies how "deep" to print a list. Specifically, it is the number of unpaired left parentheses which will be printed. Below that level, all lists will be printed as &. If the CAR printlevel is negative, the action is similar except that an end-of-line is inserted after each right parentheses that would be immediately followed by a left parenthesis. +The CDR printlevel specifies how "long" to print a list. It is the number of top level list elements that will be printed before the printing is terminated with --. For example, if CDRVAL=2, (A B C D E) will print as (A B --). For sublists, the number of list elements printed is also affected by the depth of printing in the CAR direction: Whenever the sum of the depth of the sublist (i.e. the number of unmatched left parentheses) and the number of elements is greater than the CDR printlevel, -- is printed. This gives a "triangular" effect in that less is printed the farther one goes in either CAR or CDR direction. If the CDR printlevel is negative, then it is the same as if the CDR printlevel were infinite. +Examples: + After: (A (B C (D (E F) G) H) K L) prints as: + (PRINTLEVEL 3 -1) (A (B C (D & G) H) K L) + (PRINTLEVEL 2 -1) (A (B C & H) K L) + (PRINTLEVEL 1 -1) (A & K L) + (PRINTLEVEL 0 -1) & + (PRINTLEVEL 1000 2) (A (B --) --) + (PRINTLEVEL 1000 3) (A (B C --) K --) + (PRINTLEVEL 1 3) (A & K --) +PLVLFILEFLG (PLVLFILEFLG% (Variable) NIL NIL ("25") 11) [Variable] +Normally, PRINTLEVEL only affects terminal output. Output to all other files acts as though the print level is infinite. However, if PLVLFILEFLG is T (initially NIL), then PRINTLEVEL affects output to files as well. +The following three functions are useful for printing isolated expressions at a specified print level without going to the overhead of resetting the global print level. +(LVLPRINT (LVLPRINT% (Function) NIL NIL ("25") 11)X FILE CARLVL CDRLVL TAIL) [Function] +Performs PRINT of X to FILE, using as CAR and CDR print levels the values CARLVL and CDRLVL, respectively. Uses the T read table. If TAIL is specified, and X is a tail of it, then begins its printing with "...", rather than on open parenthesis. +(LVLPRIN2(LVLPRIN2 (Function) NIL NIL ("25") 11) X FILE CARLVL CDRLVL TAIL) [Function] +Similar to LVLPRIN2, but performs a PRIN2. +(LVLPRIN1(LVLPRIN1 (Function) NIL NIL ("25") 11) X FILE CARLVL CDRLVL TAIL) [Function] +Similar to LVLPRIN1, but performs a PRIN1. +Printing Numbers(PRINTING% NUMBERS NIL Printing% Numbers NIL ("25") 11) +How the ordinary printing functions (PRIN1, PRIN2, etc.) print numbers can be affected in several ways. RADIX influences the printing of integers, and FLTFMT influences the printing of floating point numbers. The setting of the variable PRXFLG determines how the symbol-manipulation functions handle numbers. The PRINTNUM package permits greater controls on the printed appearance of numbers, allowing such things as left-justification, suppression of trailing decimals, etc. +(RADIX(RADIX (Function) NIL NIL ("25") 11) N) [Function] +Resets the output radix for integers to the absolute value of N. The value of RADIX is its previous setting. (RADIX) gives the current setting without changing it. The initial setting is 10. +Note that RADIX affects output only. There is no input radix; on input, numbers are interpreted as decimal unless they are entered in a non-decimal radix with syntax such as 123Q, |b10101, |5r1234 (see Chapter 7). RADIX does not affect the behavior of UNPACK, etc., unless the value of PRXFLG (below) is T. For example, if PRXFLG is NIL and the radix is set to 8 with (RADIX 8), the value of (UNPACK 9) is (9), not (1 1). +Using PRINTNUM (below) or the PRINTOUT command .I (below) is often a more convenient and appropriate way to print a single number in a specified radix than to globally change RADIX. +(FLTFMT(FLTFMT (Function) NIL NIL ("25") 12) FORMAT) [Function] +Resets the output format for floating point numbers to the FLOAT format FORMAT (see PRINTNUM below for a description of FLOAT formats). FORMAT=T specifies the default "free" formatting: some number of significant digits (a function of the implementation) are printed, with trailing zeros suppressed; numbers with sufficiently large or small exponents are instead printed in exponent notation. +FLTFMT returns its current setting. (FLTFMT) returns the current setting without changing it. The initial setting is T. +Note: In Interlisp-D, FLTFMT ignores the WIDTH and PAD fields of the format (they are implemented only by PRINTNUM). +Whether print name manipulation functions (UNPACK, NCHARS, etc.) use the values of RADIX and FLTFMT is determined by the variable PRXFLG: +PRXFLG(PRXFLG (Variable) NIL NIL ("25") 12) [Variable] +If PRXFLG=NIL (the initial setting), then the "PRIN1" name used by PACK, UNPACK, MKSTRING, etc., is computed using base 10 for integers and the system default floating format for floating point numbers, independent of the current setting of RADIX or FLTFMT. If PRXFLG=T, then RADIX and FLTFMT do dictate the "PRIN1" name of numbers. Note that in this case, PACK and UNPACK are not inverses. +Examples with (RADIX 8), (FLTFMT '(FLOAT 4 2)): +With PRXFLG=NIL, +(UNPACK 13) => (1 3) +(PACK '(A 9)) => A9 +(UNPACK 1.2345) => (1 %. 2 3 4 5) +With PRXFLG=T, +(UNPACK 13) => (1 5) +(PACK '(A 9)) => A11 +(UNPACK 1.2345) => (1 %. 2 3) +Note that PRXFLG does not effect the radix of "PRIN2" names, so with (RADIX 8), (NCHARS 9 T), which uses PRIN2 names, would return 3, (since 9 would print as 11Q) for either setting of PRXFLG. +Warning: Some system functions will not work correctly if PRXFLG is not NIL. Therefore, resetting the global value of PRXFLG is not recommended. It is much better to rebind PRXFLG as a SPECVAR for that part of a program where it needs to be non-NIL. +The basic function for printing numbers under format control is PRINTNUM. Its utility is considerably enhanced when used in conjunction with the PRINTOUT package, which implements a compact language for specifying complicated sequences of elementary printing operations, and makes fancy output formats easy to design and simple to program. +(PRINTNUM FORMAT NUMBER FILE) [Function] +Prints NUMBER on FILE according to the format FORMAT. FORMAT is a list structure with one of the forms described below. +If FORMAT is a list of the form (FIX WIDTH RADIX PAD0 LEFTFLUSH), this specifies a FIX format. NUMBER is rounded to the nearest integer, and then printed in a field WIDTH characters long with radix set to RADIX (or 10 if RADIX=NIL; note that the setting from the function RADIX is not used as the default). If PAD0 and LEFTFLUSH are both NIL, the number is right-justified in the field, and the padding characters to the left of the leading digit are spaces. If PAD0 is T, the character "0" is used for padding. If LEFTFLUSH is T, then the number is left-justified in the field, with trailing spaces to fill out WIDTH characters. +The following examples illustrate the effects of the FIX format options on the number 9 (the vertical bars indicate the field width): + FORMAT: (PRINTNUM FORMAT 9) prints: + (FIX 2) | 9| + (FIX 2 NIL T) |09| + (FIX 12 8 T) |000000000011| + (FIX 5 NIL NIL T) |9 | +If FORMAT is a list of the form (FLOAT WIDTH DECPART EXPPART PAD0 ROUND), this specifies a FLOAT format. NUMBER is printed as a decimal number in a field WIDTH characters wide, with DECPART digits to the right of the decimal point. If EXPPART is not 0 (or NIL), the number is printed in exponent notation, with the exponent occupying EXPPART characters in the field. EXPPART should allow for the character E and an optional sign to be printed before the exponent digits. As with FIX format, padding on the left is with spaces, unless PAD0 is T. If ROUND is given, it indicates the digit position at which rounding is to take place, counting from the leading digit of the number. +Interlisp-D interprets WIDTH=NIL to mean no padding, i.e., to use however much space the number needs, and interprets DECPART=NIL to mean as many decimal places as needed. +The following examples illustrate the effects of the FLOAT format options on the number 27.689 (the vertical bars indicate the field width): + FORMAT: (PRINTNUM FORMAT 27.689) prints: + (FLOAT 7 2) | 27.69| + (FLOAT 7 2 NIL 0) |0027.69| + (FLOAT 7 2 2) | 2.77E1| + (FLOAT 11 2 4) | 2.77E+01| + (FLOAT 7 2 NIL NIL 1) | 30.00| + (FLOAT 7 2 NIL NIL 2) | 28.00| +NILNUMPRINTFLG(NILNUMPRINTFLG (Variable) NIL NIL ("25") 14) [Variable] +If PRINTNUM's NUMBER argument is not a number and not NIL, a NON-NUMERIC ARG error is generated. If NUMBER is NIL, the effect depends on the setting of the variable NILNUMPRINTFLG. If NILNUMPRINTFLG is NIL, then the error occurs as usual. If it is non-NIL, then no error occurs, and the value of NILNUMPRINTFLG is printed right-justified in the field described by FORMAT. This option facilitates the printing of numbers in aggregates with missing values coded as NIL. +User Defined Printing +Initially, Interlisp only knows how to print in an interesting way objects of type litatom, number, string, list and stackp. All other types of objects are printed in the form {datatype} followed by the octal representation of the address of the pointer, a format that cannot be read back in to produce an equivalent object. When defining user data types (using the DATATYPE record type, Chapter 8), it is often desirable to specify as well how objects of that type should be printed, so as to make their contents readable, or at least more informative to the viewer. The function DEFPRINT is used to specify the printing format of a data type. +(DEFPRINT(DEFPRINT (Function) NIL NIL ("25") 14) TYPE FN) [Function] +TYPE is a type name. Whenever a printing function (PRINT, PRIN1, PRIN2, etc.) or a function requiring a print name (CHCON, NCHARS, etc.) encounters an object of the indicated type, FN is called with two arguments: the item to be printed and the name of the stream, if any, to which the object is to be printed. The second argument is NIL on calls that request the print name of an object without actually printing it. +If FN returns a list of the form (ITEM1 . ITEM2), ITEM1 is printed using PRIN1 (unless it is NIL), and then ITEM2 is printed using PRIN2 (unless it is NIL). No spaces are printed between the two items. Typically, ITEM1 is a read macro character. +If FN returns NIL, the datum is printed in the system default manner. +If FN returns T, nothing further is printed; FN is assumed to have printed the object to the stream itself. Note that this case if permitted only when the second argument passed to FN is non-NIL; otherwise, there is no destination for FN to do its printing, so it must return as in one of the other two cases. +Printing Unusual Data Structures +HPRINT (for "Horrible Print") and HREAD provide a mechanism for printing and reading back in general data structures that cannot normally be dumped and loaded easily, such as (possibly re-entrant or circular) structures containing user datatypes, arrays, hash tables, as well as list structures. HPRINT will correctly print and read back in any structure containing any or all of the above, chasing all pointers down to the level of literal atoms, numbers or strings. HPRINT currently cannot handle compiled code arrays, stack positions, or arbitrary unboxed numbers. +HPRINT operates by simulating the Interlisp PRINT routine for normal list structures. When it encounters a user datatype (see Chapter 8), or an array or hash array, it prints the data contained therein, surrounded by special characters defined as read macro characters. While chasing the pointers of a structure, it also keeps a hash table of those items it encounters, and if any item is encountered a second time, another read macro character is inserted before the first occurrence (by resetting the file pointer with SETFILEPTR) and all subsequent occurrences are printed as a back reference using an appropriate macro character. Thus the inverse function, HREAD merely calls the Interlisp READ routine with the appropriate read table. +(HPRINT(HPRINT (Function) NIL NIL ("25") 15) EXPR FILE UNCIRCULAR DATATYPESEEN) [Function] +Prints EXPR on FILE. If UNCIRCULAR is non-NIL, HPRINT does no checking for any circularities in EXPR (but is still useful for dumping arbitrary structures of arrays, hash arrays, lists, user data types, etc., that do not contain circularities). Specifying UNCIRCULAR as non-NIL results in a large speed and internal-storage advantage. +Normally, when HPRINT encounters a user data type for the first time, it outputs a summary of the data type's declaration. When this is read in, the data type is redeclared. If DATATYPESEEN is non-NIL, HPRINT assumes that the same data type declarations will be in force at read time as were at HPRINT time, and not output declarations. +HPRINT is intended primarily for output to random access files, since the algorithm depends on being able to reset the file pointer. If FILE is not a random access file (and UNCIRCULAR = NIL), a temporary file, HPRINT.SCRATCH, is opened, EXPR is HPRINTed on it, and then that file is copied to the final output file and the temporary file is deleted. +You can not use HPRINT to save things that contains pointers to raw storage. Fontdescriptors contain pointers to raw storage and windows contain pointers to fontdescriptors. Netiher can therefor be saved with HPRINT. +(HREAD(HREAD (Function) NIL NIL ("25") 15) FILE) [Function] +Reads and returns an HPRINT-ed expression from FILE. +(HCOPYALL(HCOPYALL (Function) NIL NIL ("25") 15) X) [Function] +Copies data structure X. X may contain circular pointers as well as arbitrary structures. +Note: HORRIBLEVARS and UGLYVARS (Chapter 17) are two file package commands for dumping and reloading circular and re-entrant data structures. They provide a convenient interface to HPRINT and HREAD. +When HPRINT is dumping a data structure that contains an instance of an Interlisp datatype, the datatype declaration is also printed onto the file. Reading such a data structure with HREAD can cause problems if it redefines a system datatype. Redefining a system datatype will almost definitely cause serious errors. The Interlisp system datatypes do not change very often, but there is always a possibility when loading in old files created under an old Interlisp release. +To prevent accidental system crashes, HREAD will not redefine datatypes. Instead, it will cause an error "attempt to read DATATYPE with different field specification than currently defined". Continuing from this error will redefine the datatype. +Random Access File Operations +1 + +For most applications, files are read starting at their beginning and proceeding sequentially, i.e., the next character read is the one immediately following the last character read. Similarly, files are written sequentially. However, for files on some devices, it is also possible to read/write characters at arbitrary positions in a file, essentially treating the file as a large block of auxiliary storage. For example, one application might involve writing an expression at the beginning of the file, and then reading an expression from a specified point in its middle. This particular example requires the file be open for both input and output. However, random file input or output can also be performed on files that have been opened for only input or only output. +Associated with each file is a "file pointer" that points to the location where the next character is to be read from or written to. The file position of a byte is the number of bytes that precede it in the file, i.e., 0 is the position of the beginning of the file. The file pointer to a file is automatically advanced after each input or output operation. This section describes functions which can be used to reposition the file pointer on those files that can be randomly accessed. A file used in this fashion is much like an array in that it has a certain number of addressable locations that characters can be put into or taken from. However, unlike arrays, files can be enlarged. For example, if the file pointer is positioned at the end of a file and anything is written, the file "grows." It is also possible to position the file pointer beyond the end of file and then to write. (If the program attempts to read beyond the end of file, an END OF FILE error occurs.) In this case, the file is enlarged, and a "hole" is created, which can later be written into. Note that this enlargement only takes place at the end of a file; it is not possible to make more room in the middle of a file. In other words, if expression A begins at position 1000, and expression B at 1100, and the program attempts to overwrite A with expression C, whose printed representation is 200 bytes long, part of B will be altered. +Warning: File positions are always in terms of bytes, not characters. You should thus be very careful about computing the space needed for an expression. In particular, NS characters may take multiple bytes (see below). Also, the end-of-line character (see Chapter 24) may be represented by a different number of characters in different implementations. Output functions may also introduce end-of-line's as a result of LINELENGTH considerations. Therefore NCHARS (see Chapter 2) does not specify how many bytes an expression takes to print, even ignoring line length considerations. +(GETFILEPTR(GETFILEPTR (Function) NIL NIL ("25") 16) FILE) [Function] +Returns the current position of the file pointer for FILE, i.e., the byte address at which the next input/output operation will commence. +(SETFILEPTR(SETFILEPTR (Function) NIL NIL ("25") 16) FILE ADR) [Function] +Sets the file pointer for FILE to the position ADR; returns ADR. The special value ADR=-1 is interpreted to mean the address of the end of file. +Note: If a file is opened for output only, the end of file is initially zero, even if an old file by the same name had existed (see OPENSTREAM, Chapter 24). If a file is opened for both input and output, the initial file pointer is the beginning of the file, but (SETFILEPTR FILE -1) sets it to the end of the file. If the file had been opened in append mode by (OPENSTREAM FILE 'APPEND), the file pointer right after opening would be set to the end of the existing file, in which case a SETFILEPTR to position the file at the end would be unnecessary. +(GETEOFPTR(GETEOFPTR (Function) NIL NIL ("25") 17) FILE) [Function] +Returns the byte address of the end of file, i.e., the number of bytes in the file. Equivalent to performing (SETFILEPTR FILE -1) and returning (GETFILEPTR FILE) except that it does not change the current file pointer. +(RANDACCESSP(RANDACCESSP (Function) NIL NIL ("25") 17) FILE) [Function] +Returns FILE if FILE is randomly accessible, NIL otherwise. The file T is not randomly accessible, nor are certain network file connections in Interlisp-D. FILE must be open or an error is generated, FILE NOT OPEN. +(COPYBYTES(COPYBYTES (Function) NIL NIL ("25") 17) SRCFIL DSTFIL START END) [Function] +Copies bytes from SRCFIL to DSTFIL, starting from position START and up to but not including position END. Both SRCFIL and DSTFIL must be open. Returns T. +If END=NIL, START is interpreted as the number of bytes to copy (starting at the current position). If START is also NIL, bytes are copied until the end of the file is reached. +Warning: COPYBYTES does not take any account of multi-byte NS characters (see Chapter 2). COPYCHARS (below) should be used whenever copying information that might include NS characters. +(COPYCHARS(COPYCHARS (Function) NIL NIL ("25") 17) SRCFIL DSTFIL START END) [Function] +Like COPYBYTES except that it copies NS characters (see Chapter 2), and performs the proper conversion if the end-of-line conventions of SRCFIL and DSTFIL are not the same (see Chapter 24). START and END are interpreted the same as with COPYBYTES, i.e., as byte (not character) specifications in SRCFIL. The number of bytes actually output to DSTFIL might be more or less than the number of bytes specified by START and END, depending on what the end-of-line conventions are. In the case where the end-of-line conventions happen to be the same, COPYCHARS simply calls COPYBYTES. +(FILEPOS(FILEPOS (Function) NIL NIL ("25") 17) STR FILE START END SKIP TAIL CASEARRAY) [Function] +Analogous to STRPOS (see Chapter 4), but searches a file rather than a string. FILEPOS searches FILE for the string STR. Search begins at START (or the current position of the file pointer, if START=NIL), and goes to END (or the end of FILE, if END=NIL). Returns the address of the start of the match, or NIL if not found. +SKIP can be used to specify a character which matches any character in the file. If TAIL is T, and the search is successful, the value is the address of the first character after the sequence of characters corresponding to STR, instead of the starting address of the sequence. In either case, the file is left so that the next i/o operation begins at the address returned as the value of FILEPOS. +CASEARRAY should be a "case array" that specifies that certain characters should be transformed to other characters before matching. Case arrays are returned by CASEARRAY or SEPRCASE below. CASEARRAY=NIL means no transformation will be performed. +A case array is an implementation-dependent object that is logically an array of character codes with one entry for each possible character. FILEPOS maps each character in the file "through" CASEARRAY in the sense that each character code is transformed into the corresponding character code from CASEARRAY before matching. Thus if two characters map into the same value, they are treated as equivalent by FILEPOS. CASEARRAY and SETCASEARRAY provide an implementation-independent interface to case arrays. +For example, to search without regard to upper and lower case differences, CASEARRAY would be a case array where all characters map to themselves, except for lower case characters, whose corresponding elements would be the upper case characters. To search for a delimited atom, one could use " ATOM " as the pattern, and specify a case array in which all of the break and separator characters mapped into the same code as space. +For applications calling for extensive file searches, the function FFILEPOS is often faster than FILEPOS. +(FFILEPOS(FFILEPOS (Function) NIL NIL ("25") 18) PATTERN FILE START END SKIP TAIL CASEARRAY) [Function] +Like FILEPOS, except much faster in most applications. FFILEPOS is an implementation of the Boyer-Moore fast string searching algorithm. This algorithm preprocesses the string being searched for and then scans through the file in steps usually equal to the length of the string. Thus, FFILEPOS speeds up roughly in proportion to the length of the string, e.g., a string of length 10 will be found twice as fast as a string of length 5 in the same position. +Because of certain fixed overheads, it is generally better to use FILEPOS for short searches or short strings. +(CASEARRAY(CASEARRAY (Function) NIL NIL ("25") 18) OLDARRAY) [Function] +Creates and returns a new case array, with all elements set to themselves, to indicate the identity mapping. If OLDARRAY is given, it is reused. +(SETCASEARRAY(SETCASEARRAY (Function) NIL NIL ("25") 18) CASEARRAY FROMCODE TOCODE) [Function] +Modifies the case array CASEARRAY so that character code FROMCODE is mapped to character code TOCODE. +(GETCASEARRAY(GETCASEARRAY (Function) NIL NIL ("25") 19) CASEARRAY FROMCODE) [Function] +Returns the character code that FROMCODE is mapped to in CASEARRAY. +(SEPRCASE(SEPRCASE (Function) NIL NIL ("25") 19) CLFLG) [Function] +Returns a new case array suitable for use by FILEPOS or FFILEPOS in which all of the break/separators of FILERDTBL are mapped into character code zero. If CLFLG is non-NIL, then all CLISP characters are mapped into this character as well. This is useful for finding a delimited atom in a file. For example, if PATTERN is " FOO ", and (SEPRCASE T) is used for CASEARRAY, then FILEPOS will find "(FOO". +UPPERCASEARRAY(UPPERCASEARRAY (Variable) NIL NIL ("25") 19) [Variable] +Value is a case array in which every lowercase character is mapped into the corresponding uppercase character. Useful for searching text files. +Input/Output Operations with Characters and Bytes +1 + +Interlisp-D supports the 16-bit NS character set (see Chapter 2). All of the standard string and print name functions accept litatoms and strings containing NS characters. In almost all cases, a program does not have to distinguish between NS characters or 8-bit characters. The exception to this rule is the handling of input/output operations. +Interlisp-D uses two ways of writing 16-bit NS characters on files. One way is to write the full 16-bits (two bytes) every time a character is output. The other way is to use "run-encoding." Each 16 NS character can be decoded into a character set (an integer from 0 to 254 inclusive) and a character number (also an integer from 0 to 254 inclusive). In run-encoding, the byte 255 (illegal as either a character set number or a character number) is used to signal a change to a given character set, and the following bytes are all assumed to come from the same character set (until the next change-character set sequence). Run-encoding can reduce the number of bytes required to encode a string of NS characters, as long as there are long sequences of characters from the same character set (usually the case). +Note that characters are not the same as bytes. A single character can take anywhere from one to four bytes bytes, depending on whether it is in the same character set as the preceeding character, and whether run-encoding is enabled. Programs which assume that characters are equal to bytes must be changed to work with NS characters. +The functions BIN and BOUT (see above) should only be used to read and write single eight-bit bytes. The functions READCCODE and PRINTCCODE (see above) should be used to read and write single character codes, interpreting run-encoded NS characters. COPYBYTES should only be used to copy blocks of 8-bit data; COPYCHARS should be used to copy characters. Most I/O functions (READC, PRIN1, etc.) read or write 16-bit NS characters. +The use of NS characters has serious consequences for any program that uses file pointers to access a file in a random access manner. At any point when a file is being read or written, it has a "current character set." If the file pointer is changed with SETFILEPTR to a part of the file with a different character set, any characters read or written may have the wrong character set. The current character set can be accessed with the following function: +(CHARSET(CHARSET (Function) NIL NIL ("25") 20) STREAM CHARACTERSET) [Function] +Returns the current character set of the stream STREAM. If CHARACTERSET is non-NIL, the current character set for STREAM is set. Note that for output streams this may cause bytes to be written to the stream. +If CHARACTERSET is T, run encoding for STREAM is disabled: both the character set and the character number (two bytes total) will be written to the stream for each character printed. +PRINTOUT +1 + +Interlisp provides many facilities for controlling the format of printed output. By executing various sequences of PRIN1, PRIN2, TAB, TERPRI, SPACES, PRINTNUM, and PRINTDEF, almost any effect can be achieved. PRINTOUT implements a compact language for specifying complicated sequences of these elementary printing functions. It makes fancy output formats easy to design and simple to program. +PRINTOUT is a CLISP word (like FOR and IF) for interpreting a special printing language in which you can describe the kinds of printing desired. The description is translated by DWIMIFY to the appropriate sequence of PRIN1, TAB, etc., before it is evaluated or compiled. PRINTOUT printing descriptions have the following general form: + (PRINTOUT STREAM PRINTCOM1 ... PRINTCOMN) +STREAM is evaluated to obtain the stream to which the output from this specification is directed. The PRINTOUT commands are strung together, one after the other without punctuation, after STREAM. Some commands occupy a single position in this list, but many commands expect to find arguments following the command name in the list. The commands fall into several logical groups: one set deals with horizontal and vertical spacing, another group provides controls for certain formatting capabilities (font changes and subscripting), while a third set is concerned with various ways of actually printing items. Finally, there is a command that permits escaping to a simple Lisp evaluation in the middle of a PRINTOUT form. The various commands are described below. The following examples give a general flavor of how PRINTOUT is used: +Example 1: Suppose you want to print out on the terminal the values of three variables, X, Y, and Z, separated by spaces and followed by a carriage return. This could be done by: +(PRIN1 X T) +(SPACES 1 T) +(PRIN1 Y T) +(SPACES 1 T) +(PRIN1 Z T) +(TERPRI T) +or by the more concise PRINTOUT form: +(PRINTOUT T X , Y , Z T) +Here the first T specifies output to the terminal, the commas cause single spaces to be printed, and the final T specifies a TERPRI. The variable names are not recognized as special PRINTOUT commands, so they are printed using PRIN1 by default. +Example 2: Suppose the values of X and Y are to be pretty-printed lined up at position 10, preceded by identifying strings. If the output is to go to the primary output stream, you could write either: +(PRIN1 "X =") +(PRINTDEF X 10 T) +(TERPRI ) +(PRIN1 "Y =") +(PRINTDEF Y 10 T) +(TERPRI) +or the equivalent: +(PRINTOUT NIL "X =" 10 .PPV X T + "Y =" 10 .PPV Y T) +Since strings are not recognized as special commands, "X =" is also printed with PRIN1 by default. The positive integer means TAB to position 10, where the .PPV command causes the value of X to be prettyprinted as a variable. By convention, special atoms used as PRINTOUT commands are prefixed with a period. The T causes a carriage return, so the Y information is printed on the next line. +Example 3. As a final example, suppose that the value of X is an integer and the value of Y is a floating-point number. X is to be printed right-flushed in a field of width 5 beginning at position 15, and Y is to be printed in a field of width 10 also starting at position 15 with 2 places to the right of the decimal point. Furthermore, suppose that the variable names are to appear in the font class named BOLDFONT and the values in font class SMALLFONT. The program in ordinary Interlisp that would accomplish these effects is too complicated to include here. With PRINTOUT, one could write: +(PRINTOUT NIL + .FONT BOLDFONT "X =" 15 + .FONT SMALLFONT .I5 X T + .FONT BOLDFONT "Y =" 15 + .FONT SMALLFONT .F10.2 Y T + .FONT BOLDFONT) +The .FONT commands do whatever is necessary to change the font on a multi-font output device. The .I5 command sets up a FIX format for a call to the function PRINTNUM (see above) to print X in the desired format. The .F10.2 specifies a FLOAT format for PRINTNUM. +Horizontal Spacing Commands +The horizontal spacing commands provide convenient ways of calling TAB and SPACES. In the following descriptions, N stands for a literal positive integer (not for a variable or expression whose value is an integer). +N (N a number) [PRINTOUT Command] +Used for absolute spacing. It results in a TAB to position N (literally, a (TAB N)). If the line is currently at position N or beyond, the file will be positioned at position N on the next line. +.TAB(TAB (Command) .TAB NIL ("25") 22)(.TAB (Command) NIL NIL ("25") 22) POS [PRINTOUT Command] +Specifies TAB to position (the value of) POS. This is one of several commands whose effect could be achieved by simply escaping to Lisp, and executing the corresponding form. It is provided as a separate command so that the PRINTOUT form is more concise and is prettyprinted more compactly. Note that .TAB N and N, where N is an integer, are equivalent. +.TAB0(TAB0 (Command) .TAB0 NIL ("25") 22)(.TAB0 (Command) NIL NIL ("25") 22) POS [PRINTOUT Command] +Like .TAB except that it can result in zero spaces (i.e. the call to TAB specifies MINSPACES=0). +-N (N a number) [PRINTOUT Command] +Negative integers indicate relative (as opposed to absolute) spacing. Translates as (SPACES |N|). +,(, (Command) NIL NIL ("25") 22) [PRINTOUT Command] +,,(,, (Command) NIL NIL ("25") 22) [PRINTOUT Command] +,,,(,,, (Command) NIL NIL ("25") 22) [PRINTOUT Command] +(1, 2 or 3 commas) Provides a short-hand way of specifying 1, 2 or 3 spaces, i.e., these commands are equivalent to -1, -2, and -3, respectively. +.SP(SP (Command) .SP NIL ("25") 22)(.SP (Command) NIL NIL ("25") 22) DISTANCE [PRINTOUT Command] +Translates as (SPACES DISTANCE). Note that .SP N and -N, where N is an integer, are equivalent. +Vertical Spacing Commands +Vertical spacing is obtained by calling TERPRI or printing form-feeds. The relevant commands are: +T(T (Command) NIL NIL ("25") 23) [PRINTOUT Command] +Translates as (TERPRI), i.e., move to position 0 (the first column) of the next line. To print the letter T, use the string "T". +.SKIP(SKIP (Command) .SKIP NIL ("25") 23)(.SKIP (Command) NIL NIL ("25") 23) LINES [PRINTOUT Command] +Equivalent to a sequence of LINES (TERPRI)'s. The .SKIP command allows for skipping large constant distances and for computing the distance to be skipped. +.PAGE(PAGE (Command) .PAGE NIL ("25") 23)(.PAGE (Command) NIL NIL ("25") 23) [PRINTOUT Command] +Puts a form-feed (Control-L) out on the file. Care is taken to make sure that Interlisp's view of the current line position is correctly updated. +Special Formatting Controls +There are a small number of commands for invoking some of the formatting capabilities of multi-font output devices. The available commands are: +.FONT(FONT (Command) .FONT NIL ("25") 23)(.FONT (Command) NIL NIL ("25") 23) FONTSPEC [PRINTOUT Command] +Changes printing to the font FONTSPEC, which can be a font descriptor, a "font list" such as '(MODERN 10), an image stream (coerced to its current font), or a windows (coerced to the current font of its display stream). The DSPFONT is changed permanently. See fonts (Chapter 27) for more information. +FONTSPEC may also be a positive integer N, which is taken as an abbreviated reference to the font class named FONTN (e.g. 1 => FONT1). +.SUP(SUP (Command) .SUP NIL ("25") 23)(.SUP (Command) NIL NIL ("25") 23) [PRINTOUT Command] +Specifies superscripting. All subsequent characters are printed above the base of the current line. Note that this is absolute, not relative: a .SUP following a .SUP is a no-op. +.SUB(SUB (Command) .SUB NIL ("25") 23)(.SUB (Command) NIL NIL ("25") 23) [PRINTOUT Command] +Specifies subscripting. Subsequent printing is below the base of the current line. As with superscripting, the effect is absolute. +.BASE(BASE (Command) .BASE NIL ("25") 23)(.BASE (Command) NIL NIL ("25") 23) [PRINTOUT Command] +Moves printing back to the base of the current line. Un-does a previous .SUP or .SUB; a no-op, if printing is currently at the base. +Printing Specifications +The value of any expression in a PRINTOUT form that is not recognized as a command itself or as a command argument is printed using PRIN1 by default. For example, title strings can be printed by simply including the string as a separate PRINTOUT command, and the values of variables and forms can be printed in much the same way. Note that a literal integer, say 51, cannot be printed by including it as a command, since it would be interpreted as a TAB; the desired effect can be obtained by using instead the string specification "51", or the form (QUOTE 51). +For those instances when PRIN1 is not appropriate, e.g., PRIN2 is required, or a list structures must be prettyprinted, the following commands are available: +.P2(P2 (Command) .P2 NIL ("25") 24)(.P2 (Command) NIL NIL ("25") 24) THING [PRINTOUT Command] +Causes THING to be printed using PRIN2; translates as (PRIN2 THING). +.PPF(PPF (Command) .PPF NIL ("25") 24)(.PPF (Command) NIL NIL ("25") 24) THING [PRINTOUT Command] +Causes THING to be prettyprinted at the current line position via PRINTDEF (see Chapter 26). The call to PRINTDEF specifies that THING is to be printed as if it were part of a function definition. That is, SELECTQ, PROG, etc., receive special treatment. +.PPV(PPV (Command) .PPV NIL ("25") 24)(.PPV (Command) NIL NIL ("25") 24) THING [PRINTOUT Command] +Prettyprints THING as a variable; no special interpretation is given to SELECTQ, PROG, etc. +.PPFTL(PPFTL (Command) .PPFTL NIL ("25") 24)(.PPFTL (Command) NIL NIL ("25") 24) THING [PRINTOUT Command] +Like .PPF, but prettyprints THING as a tail, that is, without the initial and final parentheses if it is a list. Useful for prettyprinting sub-lists of a list whose other elements are formatted with other commands. +.PPVTL(PPVTL (Command) .PPVTL NIL ("25") 24)(.PPVTL (Command) NIL NIL ("25") 24) THING [PRINTOUT Command] +Like .PPV, but prettyprints THING as a tail. +Paragraph Format +Interlisp's prettyprint routines are designed to display the structure of expressions, but they are not really suitable for formatting unstructured text. If a list is to be printed as a textual paragraph, its internal structure is less important than controlling its left and right margins, and the indentation of its first line. The .PARA and .PARA2 commands allow these parameters to be conveniently specified. +.PARA(PARA (Command) .PARA NIL ("25") 24)(.PARA (Command) NIL NIL ("25") 24) LMARG RMARG LIST [PRINTOUT Command] +Prints LIST in paragraph format, using PRIN1. Translates as (PRINTPARA LMARG RMARG LIST) (see below). +Example: (PRINTOUT T 10 .PARA 5 -5 LST) will print the elements of LST as a paragraph with left margin at 5, right margin at (LINELENGTH)-5, and the first line indented to 10. +.PARA2(PARA2 (Command) .PARA2 NIL ("25") 25)(.PARA2 (Command) NIL NIL ("25") 25) LMARG RMARG LIST [PRINTOUT Command] +Print as paragraph using PRIN2 instead of PRIN1. Translates as (PRINTPARA LMARG RMARG LIST T). +Right-Flushing +Two commands are provided for printing simple expressions flushed-right against a specified line position, using the function FLUSHRIGHT (see below). They take into account the current position, the number of characters in the print-name of the expression, and the position the expression is to be flush against, and then print the appropriate number of spaces to achieve the desired effect. Note that this might entail going to a new line before printing. Note also that right-flushing of expressions longer than a line (e.g. a large list) makes little sense, and the appearance of the output is not guaranteed. +.FR(FR (Command) .FR NIL ("25") 25)(.FR (Command) NIL NIL ("25") 25) POS EXPR [PRINTOUT Command] +Flush-right using PRIN1. The value of POS determines the position that the right end of EXPR will line up at. As with the horizontal spacing commands, a negative position number means |POS| columns from the current position, a positive number specifies the position absolutely. POS=0 specifies the right-margin, i.e. is interpreted as (LINELENGTH). +.FR2(FR2 (Command) .FR2 NIL ("25") 25)(.FR2 (Command) NIL NIL ("25") 25) POS EXPR [PRINTOUT Command] +Flush-right using PRIN2 instead of PRIN1. +Centering +Commands for centering simple expressions between the current line position and another specified position are also available. As with right flushing, centering of large expressions is not guaranteed. +.CENTER(CENTER (Command) .CENTER NIL ("25") 25)(.CENTER (Command) NIL NIL ("25") 25) POS EXPR [PRINTOUT Command] +Centers EXPR between the current line position and the position specified by the value of POS. A positive POS is an absolute position number, a negative POS specifies a position relative to the current position, and 0 indicates the right-margin. Uses PRIN1 for printing. +.CENTER2(CENTER2 (Command) .CENTER2 NIL ("25") 25)(.CENTER2 (Command) NIL NIL ("25") 25) POS EXPR [PRINTOUT Command] +Centers using PRIN2 instead of PRIN1. +Numbering +The following commands provide FORTRAN-like formatting capabilities for integer and floating-point numbers. Each command specifies a printing format and a number to be printed. The format specification translates into a format-list for the function PRINTNUM. +.I(I (Command) .I NIL ("25") 26)(.I (Command) NIL NIL ("25") 26)FORMAT NUMBER [PRINTOUT Command] +Specifies integer printing. Translates as a call to the function PRINTNUM with a FIX format-list constructed from FORMAT. The atomic format is broken apart at internal periods to form the format-list. For example, .I5.8.T yields the format-list (FIX 5 8 T), and the command sequence (PRINTOUT T .I5.8.T FOO) translates as (PRINTNUM '(FIX 5 8 T) FOO). This expression causes the value of FOO to be printed in radix 8 right-flushed in a field of width 5, with 0's used for padding on the left. Internal NIL's in the format specification may be omitted, e.g., the commands .I5..T and .I5.NIL.T are equivalent. +The format specification .I1 is often useful for forcing a number to be printed in radix 10 (but not otherwise specially formatted), independent of the current setting of RADIX. +.F(F (Command) .F NIL ("25") 26)(.F (Command) NIL NIL ("25") 26) FORMAT NUMBER [PRINTOUT Command] +Specifies floating-number printing. Like the .I format command, except translates with a FLOAT format-list. +.N(N (Command) .N NIL ("25") 26)(.N (Command) NIL NIL ("25") 26) FORMAT NUMBER [PRINTOUT Command] +The .I and .F commands specify calls to PRINTNUM with quoted format specifications. The .N command translates as (PRINTNUM FORMAT NUMBER), i.e., it permits the format to be the value of some expression. Note that, unlike the .I and .F commands, FORMAT is a separate element in the command list, not part of an atom beginning with .N. +Escaping to Lisp +There are many reasons for taking control away from PRINTOUT in the middle of a long printing expression. Common situations involve temporary changes to system printing parameters (e.g. LINELENGTH), conditional printing (e.g. print FOO only if FIE is T), or lower-level iterative printing within a higher-level print specification. +#(# (Command) NIL NIL ("25") 26) FORM [PRINTOUT Command] +The escape command. FORM is an arbitrary Lisp expression that is evaluated within the context established by the PRINTOUT form, i.e., FORM can assume that the primary output stream has been set to be the FILE argument to PRINTOUT. Note that nothing is done with the value of FORM; any printing desired is accomplished by FORM itself, and the value is discarded. +Note: Although PRINTOUT logically encloses its translation in a RESETFORM (Chapter 14) to change the primary output file to the FILE argument (if non-NIL), in most cases it can actually pass FILE (or a locally bound variable if FILE is a non-trivial expression) to each printing function. Thus, the RESETFORM is only generated when the # command is used, or user-defined commands (below) are used. If many such occur in repeated PRINTOUT forms, it may be more efficient to embed them all in a single RESETFORM which changes the primary output file, and then specify FILE=NIL in the PRINTOUT expressions themselves. +User-Defined Commands +The collection of commands and options outlined above is aimed at fulfilling all common printing needs. However, certain applications might have other, more specialized printing idioms, so a facility is provided whereby you can define new commands. This is done by adding entries to the global list PRINTOUTMACROS to define how the new commands are to be translated. +PRINTOUTMACROS(PRINTOUTMACROS (Variable) NIL NIL ("25") 27) [Variable] +PRINTOUTMACROS is an association-list whose elements are of the form (COMM FN). Whenever COMM appears in command position in the sequence of PRINTOUT commands (as opposed to an argument position of another command), FN is applied to the tail of the command-list (including the command). +After inspecting as much of the tail as necessary, the function must return a list whose CAR is the translation of the user-defined command and its arguments, and whose CDR is the list of commands still remaining to be translated in the normal way. +For example, suppose you want to define a command "?", which will cause its single argument to be printed with PRIN1 only if it is not NIL. This can be done by entering (? ?TRAN) on PRINTOUTMACROS, and defining the function ?TRAN as follows: +(DEFINEQ (?TRAN (COMS) + (CONS + (SUBST (CADR COMS) 'ARG + '(PROG ((TEMP ARG)) + (COND (TEMP (PRIN1 TEMP))))) + (CDDR COMS))] +Note that ?TRAN does not do any printing itself; it returns a form which, when evaluated in the proper context, will perform the desired action. This form should direct all printing to the primary output file. +Special Printing Functions +The paragraph printing commands are translated into calls on the function PRINTPARA, which may also be called directly: +(PRINTPARA(PRINTPARA (Function) NIL NIL ("25") 27) LMARG RMARG LIST P2FLAG PARENFLAG FILE) [Function] +Prints LIST on FILE in line-filled paragraph format with its first element beginning at the current line position and ending at or before RMARG, and with subsequent lines appearing between LMARG and RMARG. If P2FLAG is non-NIL, prints elements using PRIN2, otherwise PRIN1. If PARENFLAG is non-NIL, then parentheses will be printed around the elements of LIST. +If LMARG is zero or positive, it is interpreted as an absolute column position. If it is negative, then the left margin will be at |LMARG|+(POSITION). If LMARG=NIL, the left margin will be at (POSITION), and the paragraph will appear in block format. +If RMARG is positive, it also is an absolute column position (which may be greater than the current (LINELENGTH)). Otherwise, it is interpreted as relative to (LINELENGTH), i.e., the right margin will be at (LINELENGTH)+|RMARG|. Example: (TAB 10) (PRINTPARA 5 -5 LST T) will PRIN2 the elements of LST in a paragraph with the first line beginning at column 10, subsequent lines beginning at column 5, and all lines ending at or before (LINELENGTH)-5. +The current (LINELENGTH) is unaffected by PRINTPARA, and upon completion, FILE will be positioned immediately after the last character of the last item of LIST. PRINTPARA is a no-op if LIST is not a list. +The right-flushing and centering commands translate as calls to the function FLUSHRIGHT: +(FLUSHRIGHT(FLUSHRIGHT (Function) NIL NIL ("25") 28) POS X MIN P2FLAG CENTERFLAG FILE) [Function] +If CENTERFLAG=NIL, prints X right-flushed against position POS on FILE; otherwise, centers X between the current line position and POS. Makes sure that it spaces over at least MIN spaces before printing by doing a TERPRI if necessary; MIN=NIL is equivalent to MIN=1. A positive POS indicates an absolute position, while a negative POS signifies the position which is |POS| to the right of the current line position. POS=0 is interpreted as (LINELENGTH), the right margin. +READFILE and WRITEFILE +1 + +For those applications where you simply want to simply read all of the expressions on a file, and not evaluate them, the function READFILE is available: +(READFILE(READFILE (Function) NIL NIL ("25") 28) FILE RDTBL ENDTOKEN) [NoSpread Function] +Reads successive expressions from file using READ (with read table RDTBL) until the single litatom ENDTOKEN is read, or an end of file encountered. Returns a list of these expressions. +If RDTBL is not specified, it defaults to FILERDTBL. If ENDTOKEN is not specified, it defaults to the litatom STOP. +(WRITEFILE(WRITEFILE (Function) NIL NIL ("25") 28) X FILE) [Function] +Writes a date expression onto FILE, followed by successive expressions from X, using FILERDTBL as a read table. If X is atomic, its value is used. If FILE is not open, it is opened. If FILE is a list, (CAR FILE) is used and the file is left opened. Otherwise, when X is finished, the litatom STOP is printed on FILE and it is closed. Returns FILE. +(ENDFILE(ENDFILE (Function) NIL NIL ("25") 29) FILE) [Function] +Prints STOP on FILE and closes it. +Read Tables +1 + +Many Interlisp input functions treat certain characters in special ways. For example, READ recognizes that the right and left parenthesis characters are used to specify list structures, and that the quote character is used to delimit text strings. The Interlisp input and (to a certain extent) output routines are table driven by read tables. Read tables are objects that specify the syntactic properties of characters for input routines. Since the input routines parse character sequences into objects, the read table in use determines which sequences are recognized as literal atoms, strings, list structures, etc. +Most Interlisp input functions take an optional read table argument, which specifies the read table to use when reading an expression. If NIL is given as the read table, the "primary read table" is used. If T is specified, the system terminal read table is used. Some functions will also accept the atom ORIG (not the value of ORIG) as indicating the "original" system read table. Some output functions also take a read table argument. For example, PRIN2 prints an expression so that it would be read in correctly using a given read table. +The Interlisp-D system uses the following read tables: T for input/output from terminals, the value of FILERDTBL for input/output from files, the value of EDITRDTBL for input from terminals while in the tty-based editor, the value of DEDITRDTBL for input from terminals while in the display-based editor, and the value of CODERDTBL for input/output from compiled files. These five read tables are initially copies of the ORIG read table, with changes made to some of them to provide read macros that are specific to terminal input or file input. Using the functions described below, you may further change, reset, or copy these tables. However, in the case of FILERDTBL and CODERDTBL, you are cautioned that changing these tables may prevent the system from being able to read files made with the original tables, or prevent users possessing only the standard tables from reading files made using the modified tables. +You can also create new read tables, and either explicitly pass them to input/output functions as arguments, or install them as the primary read table, via SETREADTABLE, and then not specify a RDTBL argument, i.e., use NIL. +Read Table Functions +(READTABLEP(READTABLEP (Function) NIL NIL ("25") 29) RDTBL) [Function] +Returns RDTBL if RDTBL is a real read table (not T or ORIG), otherwise NIL. +(GETREADTABLE(GETREADTABLE (Function) NIL NIL ("25") 30) RDTBL) [Function] +If RDTBL=NIL, returns the primary read table. If RDTBL=T, returns the system terminal read table. If RDTBL is a real read table, returns RDTBL. Otherwise, generates an ILLEGAL READTABLE error. +(SETREADTABLE(SETREADTABLE (Function) NIL NIL ("25") 30) RDTBL FLG) [Function] +Sets the primary read table to RDTBL. If FLG=T, SETREADTABLE sets the system terminal read table, T. Note that you can reset the other system read tables with SETQ, e.g., (SETQ FILERDTBL (GETREADTABLE)). +Generates an ILLEGAL READTABLE error if RDTBL is not NIL, T, or a real read table. Returns the previous setting of the primary read table, so SETREADTABLE is suitable for use with RESETFORM (Chapter 14). +(COPYREADTABLE(COPYREADTABLE (Function) NIL NIL ("25") 30) RDTBL) [Function] +Returns a copy of RDTBL. RDTBL can be a real read table, NIL, T, or ORIG (in which case COPYREADTABLE returns a copy of the original system read table), otherwise COPYREADTABLE generates an ILLEGAL READTABLE error. +Note that COPYREADTABLE is the only function that creates a read table. +(RESETREADTABLE(RESETREADTABLE (Function) NIL NIL ("25") 30) RDTBL FROM) [Function] +Copies (smashes) FROM into RDTBL. FROM and RDTBL can be NIL, T, or a real read table. In addition, FROM can be ORIG, meaning use the system's original read table. +Syntax Classes +A read table is an object that contains information about the "syntax class" of each character. There are nine basic syntax classes: LEFTPAREN, RIGHTPAREN, LEFTBRACKET, RIGHTBRACKET, STRINGDELIM, ESCAPE, BREAKCHAR, SEPRCHAR, and OTHER, each associated with a primitive syntactic property. In addition, there is an unlimited assortment of user-defined syntax classes, known as "read macros". The basic syntax classes are interpreted as follows: + LEFTPAREN (normally left parenthesis) Begins list structure. + RIGHTPAREN (normally right parenthesis) Ends list structure. + LEFTBRACKET (normally left bracket) Begins list structure. Also matches RIGHTBRACKET characters. + RIGHTBRACKET (normally left bracket) Ends list structure. Can close an arbitrary numbers of LEFTPAREN lists, back to the last LEFTBRACKET. + STRINGDELIM (normally double quote) Begins and ends text strings. Within the string, all characters except for the one(s) with class ESCAPE are treated as ordinary, i.e., interpreted as if they were of syntax class OTHER. To include the string delimiter inside a string, prefix it with the ESCAPE character. + ESCAPE (normally percent sign) Inhibits any special interpretation of the next character, i.e., the next character is interpreted to be of class OTHER, independent of its normal syntax class. + BREAKCHAR (None initially) Is a break character, i.e., delimits atoms, but is otherwise an ordinary character. + SEPRCHAR (space, carriage return, etc.) Delimits atoms, and is otherwise ignored. + OTHER Characters that are not otherwise special belong to the class OTHER. +Characters of syntax class LEFTPAREN, RIGHTPAREN, LEFTBRACKET, RIGHTBRACKET, and STRINGDELIM are all break characters. That is, in addition to their interpretation as delimiting list or string structures, they also terminate the reading of an atom. Characters of class BREAKCHAR serve only to terminate atoms, with no other special meaning. In addition, if a break character is the first non-separator encountered by RATOM, it is read as a one-character atom. In order for a break character to be included in an atom, it must be preceded by the ESCAPE character. +Characters of class SEPRCHAR also terminate atoms, but are otherwise completely ignored; they can be thought of as logically spaces. As with break characters, they must be preceded by the ESCAPE character in order to appear in an atom. +For example, if $ were a break character and * a separator character, the input stream ABC**DEF$GH*$$ would be read by six calls to RATOM returning respectively ABC, DEF, $, GH, $, $. +Although normally there is only one character in a read table having each of the list- and string-delimiting syntax classes (such as LEFTPAREN), it is perfectly acceptable for any character to have any syntax class, and for more than one to have the same class. +Note that a "syntax class" is an abstraction: there is no object referencing a collection of characters called a syntax class. Instead, a read table provides the association between a character and its syntax class, and the input/output routines enforce the abstraction by using read tables to drive the parsing. +The functions below are used to obtain and set the syntax class of a character in a read table. CH can either be a character code (a integer), or a character (a single-character atom). Single-digit integers are interpreted as character codes, rather than as characters. For example, 1 indicates Control-A, and 49 indicates the character 1. Note that CH can be a full sixteen-bit NS character (see Chapter 2). +Note: Terminal tables, described in Chapter 30, also associate characters with syntax classes, and they can also be manipulated with the functions below. The set of read table and terminal table syntax classes are disjoint, so there is never any ambiguity about which type of table is being referred to. +(GETSYNTAX CH TABLE) [Function] +Returns the syntax class of CH, a character or a character code, with respect to TABLE. TABLE can be NIL, T, ORIG, or a real read table or terminal table. +CH can also be a syntax class, in which case GETSYNTAX returns a list of the character codes in TABLE that have that syntax class. +(SETSYNTAX CHAR CLASS TABLE) [Function] +Sets the syntax class of CHAR, a character or character code, in TABLE. TABLE can be either NIL, T, or a real read table or terminal table. SETSYNTAX returns the previous syntax class of CHAR. CLASS can be any one of the following: +f The name of one of the basic syntax classes. +f A list, which is interpreted as a read macro (see below). +f NIL, T, ORIG, or a real read table or terminal table, which means to give CHAR the syntax class it has in the table indicated by CLASS. For example, (SETSYNTAX '%( 'ORIG TABLE) gives the left parenthesis character in TABLE the same syntax class that it has in the original system read table. +f A character code or character, which means to give CHAR the same syntax class as the character CHAR in TABLE. For example, (SETSYNTAX '{ '%[ TABLE) gives the left brace character the same syntax class as the left bracket. +(SYNTAXP(SYNTAXP (Function) NIL NIL ("25") 32) CODE CLASS TABLE) [Function] +CODE is a character code; TABLE is NIL, T, or a real read table or terminal table. Returns T if CODE has the syntax class CLASS in TABLE; NIL otherwise. +CLASS can also be a read macro type (MACRO, SPLICE, INFIX), or a read macro option (FIRST, IMMEDIATE, etc.), in which case SYNTAXP returns T if the syntax class is a read macro with the specified property. +SYNTAXP will not accept a character as an argument, only a character code. +For convenience in use with SYNTAXP, the atom BREAK may be used to refer to all break characters, i.e., it is the union of LEFTPAREN, RIGHTPAREN, LEFTBRACKET, RIGHTBRACKET, STRINGDELIM, and BREAKCHAR. For purely symmetrical reasons, the atom SEPR corresponds to all separator characters. However, since the only separator characters are those that also appear in SEPRCHAR, SEPR and SEPRCHAR are equivalent. +Note that GETSYNTAX never returns BREAK or SEPR as a value although SETSYNTAX and SYNTAXP accept them as arguments. Instead, GETSYNTAX returns one of the disjoint basic syntax classes that comprise BREAK. BREAK as an argument to SETSYNTAX is interpreted to mean BREAKCHAR if the character is not already of one of the BREAK classes. Thus, if %( is of class LEFTPAREN, then (SETSYNTAX '%( 'BREAK) doesn't do anything, since %( is already a break character, but (SETSYNTAX '%( 'BREAKCHAR) means make %( be just a break character, and therefore disables the LEFTPAREN function of %(. Similarly, if one of the format characters is disabled completely, e.g., by (SETSYNTAX '%( 'OTHER), then (SETSYNTAX '%( 'BREAK) would make %( be only a break character; it would not restore %( as LEFTPAREN. +The following functions provide a way of collectively accessing and setting the separator and break characters in a read table: +(GETSEPR(GETSEPR (Function) NIL NIL ("25") 33) RDTBL) [Function] +Returns a list of separator character codes in RDTBL. Equivalent to (GETSYNTAX 'SEPR RDTBL). +(GETBRK(GETBRK (Function) NIL NIL ("25") 33) RDTBL) [Function] +Returns a list of break character codes in RDTBL. Equivalent to (GETSYNTAX 'BREAK RDTBL). +(SETSEPR(SETSEPR (Function) NIL NIL ("25") 33) LST FLG RDTBL) [Function] +Sets or removes the separator characters for RDTBL. LST is a list of charactors or character codes. FLG determines the action of SETSEPR as follows: If FLG=NIL, makes RDTBL have exactly the elements of LST as separators, discarding from RDTBL any old separator characters not in LST. If FLG=0, removes from RDTBL as separator characters all elements of LST. This provides an "UNSETSEPR". If FLG=1, makes each of the characters in LST be a separator in RDTBL. +If LST=T, the separator characters are reset to be those in the system's read table for terminals, regardless of the value of FLG, i.e., (SETSEPR T) is equivalent to (SETSEPR (GETSEPR T)). If RDTBL is T, then the characters are reset to those in the original system table. +Returns NIL. +(SETBRK(SETBRK (Function) NIL NIL ("25") 33) LST FLG RDTBL) [Function] +Sets the break characters for RDTBL. Similar to SETSEPR. +As with SETSYNTAX to the BREAK class, if any of the list- or string-delimiting break characters are disabled by an appropriate SETBRK (or by making it be a separator character), its special action for READ will not be restored by simply making it be a break character again with SETBRK. However, making these characters be break characters when they already are will have no effect. +The action of the ESCAPE character (normally %) is not affected by SETSEPR or SETBRK. It can be disabled by setting its syntax to the class OTHER, and other characters can be used for escape on input by assigning them the class ESCAPE. As of this writing, however, there is no way to change the output escape character; it is "hardwired" as %. That is, on output, characters of special syntax that need to be preceded by the ESCAPE character will always be preceded by %, independent of the syntax of % or which, if any characters, have syntax ESCAPE. +The following function can be used for defeating the action of the ESCAPE character or characters: +(ESCAPE(ESCAPE (Function) NIL NIL ("25") 34) FLG RDTBL) [Function] +If FLG=NIL, makes characters of class ESCAPE behave like characters of class OTHER on input. Normal setting is (ESCAPE T). ESCAPE returns the previous setting. +Read Macros +This is a description of the OLD-INTERLISP-T read macros. Read macros are user-defined syntax classes that can cause complex operations when certain characters are read. Read macro characters are defined by specifying as a syntax class an expression of the form: + (TYPE OPTION1 ... OPTIONN FN) +where TYPE is one of MACRO, SPLICE, or INFIX, and FN is the name of a function or a lambda expression. Whenever READ encounters a read macro character, it calls the associated function, giving it as arguments the input stream and read table being used for that call to READ. The interpretation of the value returned depends on the type of read macro: + MACRO This is the simplest type of read macro. The result returned from the macro is treated as the expression to be read, instead of the read macro character. Often the macro reads more input itself. For example, in order to cause ~EXPR to be read as (NOT EXPR), one could define ~ as the read macro: +[MACRO (LAMBDA (FL RDTBL) + (LIST 'NOT (READ FL RDTBL] + SPLICE The result (which should be a list or NIL) is spliced into the input using NCONC. For example, if $ is defined by the read macro: + (SPLICE (LAMBDA NIL (APPEND FOO))) + and the value of FOO is (A B C), then when you input (X $ Y), the result will be (X A B C Y). + INFIX The associated function is called with a third argument, which is a list, in TCONC format (Chapter 3), of what has been read at the current level of list nesting. The function's value is taken as a new TCONC list which replaces the old one. For example, the infix operator + could be defined by the read macro: +(INFIX (LAMBDA (FL RDTBL Z) + (RPLACA (CDR Z) + (LIST (QUOTE IPLUS) + (CADR Z) + (READ FL RDTBL))) + Z)) + If an INFIX read macro character is encountered not in a list, the third argument to its associated function is NIL. If the function returns NIL, the read macro character is essentially ignored and reading continues. Otherwise, if the function returns a TCONC list of one element, that element is the value of the READ. If it returns a TCONC list of more than one element, the list is the value of the READ. +The specification for a read macro character can be augmented to specify various options OPTION1 ... OPTIONN, e.g., (MACRO FIRST IMMEDIATE FN). The following three disjoint options specify when the read macro character is to be effective: + ALWAYS The default. The read macro character is always effective (except when preceded by the % character), and is a break character, i.e., a member of (GETSYNTAX 'BREAK RDTBL). + FIRST The character is interpreted as a read macro character only when it is the first character seen after a break or separator character; in all other situations, the character is treated as having class OTHER. The read macro character is not a break character. For example, the quote character is a FIRST read macro character, so that DON'T is read as the single atom DON'T, rather than as DON followed by (QUOTE T). + ALONE The read macro character is not a break character, and is interpreted as a read macro character only when the character would have been read as a separate atom if it were not a read macro character, i.e., when its immediate neighbors are both break or separator characters. + Making a FIRST or ALONE read macro character be a break character (with SETBRK) disables the read macro interpretation, i.e., converts it to syntax class BREAKCHAR. Making an ALWAYS read macro character be a break character is a no-op. + The following two disjoint options control whether the read macro character is to be protected by the ESCAPE character on output when a litatom containing the character is printed: + ESCQUOTE or ESC The default. When printed with PRIN2, the read macro character will be preceded by the output escape character (%) as needed to permit the atom containing it to be read correctly. Note that for FIRST macros, this means that the character need be quoted only when it is the first character of the atom. + NOESCQUOTE or NOESC The read macro character will always be printed without an escape. For example, the ? read macro in the T read table is a NOESCQUOTE character. Unless you are very careful what you are doing, read macro characters in FILERDTBL should never be NOESCQUOTE, since symbols that happen to contain the read macro character will not read back in correctly. + The following two disjoint options control when the macro's function is actually executed: + IMMEDIATE or IMMED The read macro character is immediately activated, i.e., the current line is terminated, as if an EOL had been typed, a carriage-return line-feed is printed, and the entire line (including the macro character) is passed to the input function. + IMMEDIATE read macro characters enable you to specify a character that will take effect immediately, as soon as it is encountered in the input, rather than waiting for the line to be terminated. Note that this is not necessarily as soon as the character is typed. Characters that cause action as soon as they are typed are interrupt characters (see Chapter 30). + Note that since an IMMEDIATE macro causes any input before it to be sent to the reader, characters typed before an IMMEDIATE read macro character cannot be erased by Control-A or Control-Q once the IMMEDIATE character has been typed, since they have already passed through the line buffer. However, an INFIX read macro can still alter some of what has been typed earlier, via its third argument. + NONIMMEDIATE or NONIMMED The default. The read macro character is a normal character with respect to the line buffering, and so will not be activated until a carriage-return or matching right parenthesis or bracket is seen. + Making a read macro character be both ALONE and IMMEDIATE is a contradiction, since ALONE requires that the next character be input in order to see if it is a break or separator character. Thus, ALONE read macros are always NONIMMEDIATE, regardless of whether or not IMMEDIATE is specified. +Read macro characters can be "nested". For example, if = is defined by + (MACRO (LAMBDA (FL RDTBL) + (EVAL (READ FL RDTBL)))) +and ! is defined by + (SPLICE (LAMBDA (FL RDTBL) + (READ FL RDTBL))) +then if the value of FOO is (A B C), and (X =FOO Y) is input, (X (A B C) Y) will be returned. If (X !=FOO Y) is input, (X A B C Y) will be returned. +Note: If a read macro's function calls READ, and the READ returns NIL, the function cannot distinguish the case where a RIGHTPAREN or RIGHTBRACKET followed the read macro character, (e.g. "(A B ')"), from the case where the atom NIL (or "()") actually appeared. In Interlisp-D, a READ inside of a read macro when the next input character is a RIGHTPAREN or RIGHTBRACKET reads the character and returns NIL, just as if the READ had not occurred inside a read macro. +If a call to READ from within a read macro encounters an unmatched RIGHTBRACKET within a list, the bracket is simply put back into the buffer to be read (again) at the higher level. Thus, inputting an expression such as (A B '(C D] works correctly. +(INREADMACROP(INREADMACROP (Function) NIL NIL ("25") 37)) [Function] +Returns NIL if currently not under a read macro function, otherwise the number of unmatched left parentheses or brackets. +(READMACROS(READMACROS (Function) NIL NIL ("25") 37) FLG RDTBL) [Function] +If FLG=NIL, turns off action of read macros in read table RDTBL. If FLG=T, turns them on. Returns previous setting. +The following read macros are standardly defined in Interlisp in the T and EDITRDTBL read tables: + ' (single-quote) Returns the next expression, wrapped in a call to QUOTE; e.g., 'FOO reads as (QUOTE FOO). The macro is defined as a FIRST read macro, so that the quote character has no effect in the middle of a symbol. The macro is also ignored if the quote character is immediately followed by a separator character. + Control-Y Defined in T and EDITRDTBL. Returns the result of evaluating the next expression. For example, if the value of FOO is (A B), then (LIST 1 control-YFOO 2) is read as (LIST 1 (A B) 2). Note that no structure is copied; the third element of that input expression is still EQ to the value of FOO. Control-Y can thus be used to read structures that ordinarily have no read syntax. For example, the value returned from reading (KEY1 Control-Y(ARRAY 10)) has an array as its second element. Control-Y can be thought of as an "un-quote" character. The choice of character to perform this function is changeable with SETTERMCHARS (see Chapter 16). + ` (backquote) Backquote makes it easier to write programs to construct complex data structures. Backquote is like quote, except that within the backquoted expression, forms can be evaluated. The general idea is that the backquoted expression is a "template" containing some constant parts (as with a quoted form) and some parts to be filled in by evaluating something. Unlike with control-Y, however, the evaluation occurs not at the time the form is read, but at the time the backquoted expression is evaluated. That is, the backquote macro returns an expression which, when evaluated, produces the desired structure. + Within the backquoted expression, the character "," (comma) introduces a form to be evaluated. The value of a form preceded by ",@" is to be spliced in, using APPEND. If it is permissible to destroy the list being spliced in (i.e., NCONC may be used in the translation), then ",." can be used instead of ",@". + For example, if the value of FOO is (1 2 3 4), then the form + `(A ,(CAR FOO) ,@(CDDR FOO) D E) + evaluates to (A 1 3 4 D E); it is logically equivalent to writing + (CONS 'A + (CONS (CAR FOO) + (APPEND (CDDR FOO) '(D E)))) + . + Backquote is particularly useful for writing macros. For example, the body of a macro that refers to X as the macro's argument list might be +`(COND + ((FIXP ,(CAR X)) + ,(CADR X)) + (T .,(CDDR X))) +which is equivalent to writing +(LIST 'COND + (LIST (LIST 'FIXP (CAR X)) + (CADR X)) + (CONS 'T (CDDR X))) + Note that comma does not have any special meaning outside of a backquote context. + For users without a backquote character on their keyboards, backquote can also be written as |' (vertical-bar, quote). + ? Implements the ?= command for on-line help regarding the function currently being "called" in the typein (see Chapter 26). + | (vertical bar) When followed by an end of line, tab or space, | is ignored, i.e., treated as a separator character, enabling the editor's CHANGECHAR feature (see Chapter 26). Otherwise it is a "dispatching" read macro whose meaning depends on the character(s) following it. The following are currently defined: + ' (quote) -- A synonym for backquote. + . (period) -- Returns the evaluation of the next expression, i.e., this is a synonym for Control-Y. + , (comma) -- Returns the evaluation of the next expression at load time, i.e., the following expression is quoted in such a manner that the compiler treats it as a literal whose value is not determined until the compiled expression is loaded. + O or o (the letter O) -- Treats the next number as octal, i.e., reads it in radix 8. For example, |o12 = 10 (decimal). + B or b -- Treats the next number as binary, i.e., reads it in radix 2. For example, |b101 = 5 (decimal). + X or x -- Treats the next number as hexadecimal, i.e., reads it in radix 16. The uppercase letters A though F are used as the digits after 9. For example, |x1A = 26 (decimal). + R or r -- Reads the next number in the radix specified by the (decimal) number that appears between the | and the R. When inputting a number in a radix above ten, the upper-case letters A through Z can be used as the digits after 9 (but there is no digit above Z, so it is not possible to type all base-99 digits). For example, |3r120 reads 120 in radix 3, returning 15. + (, {, -- Used internally by HPRINT and HREAD (see above) to print and read unusual expressions. + The dispatching characters that are letters can appear in either upper- or lowercase. +(SEQUENCE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "24-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "24-" "")) (192 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (ALTERNATE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "24-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "24-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "24-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "24-" "")) (192 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (408 612 204 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))))+1$7xlx1$1$771HH$5$T1$4HH$l4$l4$l5(T506 +$T4lx$x506 +$T4Hll12$T5(T4(1$1$4Hl +$l406 +$506$T4ll$l4HH$l4$4l$71$1$1$1HH$74l +$4Hl$l7xlx1$4$H$ PAGEHEADING RIGHTPAGEE$ PAGEHEADINGLEFTBACKT +/MODERN +(CHARPROPS (COLOR . BLACK)).GACHA +(CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK))/MODERN +(CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK)))+* ) IM.CHAP.GETFN( HRULE.GETFN -IM.INDEX.GETFN HRULE.GETFN IO' +' 81'Ut' 'J +'J' 'IM.INDEX.GETFN %$UJ  %IM.INDEX.GETFN   HRULE.GETFN ,      6 S a" "$ #IM.INDEX.GETFN EA "IM.INDEX.GETFN Ki   tR )#a )6  ,2V6  %WS/ %IM.INDEX.GETFN  EL $IM.INDEX.GETFN; $IM.INDEX.GETFN  S    )  %IM.INDEX.GETFN 0N% ? + 'IM.INDEX.GETFN 8 #IM.INDEX.GETFN %E2 'IM.INDEX.GETFN B )IM.INDEX.GETFN/B !IM.INDEX.GETFN X % j c #IM.INDEX.GETFN - +% /:   31 "IM.INDEX.GETFN N >  +    ,IM.INDEX.GETFN $! *z' B%   $IM.INDEX.GETFN  c =; 8u =-  >)U 'IM.INDEX.GETFN > Tm  6IM.INDEX.GETFN HRULE.GETFN RXP&, -E" 7J& $IM.INDEX.GETFN GB #IM.INDEX.GETFN  #IM.INDEX.GETFN > S1"\D* X< #IM.INDEX.GETFN + #IM.INDEX.GETFN  #IM.INDEX.GETFN & + #IM.INDEX.GETFN )L! +* + "IM.INDEX.GETFN  +3.% +& +j $IM.INDEX.GETFN  &IM.INDEX.GETFN * 'IM.INDEX.GETFN Q  !IM.INDEX.GETFN  < /,  + +@  'IM.INDEX.GETFN   'IM.INDEX.GETFN  / + I  +Y  ; *IM.INDEX.GETFN )IM.INDEX.GETFN  # : &IM.INDEX.GETFN v   !  + (IM.INDEX.GETFN 62)!TEt +IM.INDEX.GETFN / u  | +X. + )IM.INDEX.GETFN   + +  +  $#  "  4%8 +7 + f| f7 +$ $+$%$$$#$'$ ,IM.INDEX.GETFN  + +s   +"  )IM.INDEX.GETFN   0" 'IM.INDEX.GETFN   'IM.INDEX.GETFN   7IM.INDEX.GETFN %8*QG $IM.INDEX.GETFN >L +!    + ~ %IM.INDEX.GETFN ; J 4 + %IM.INDEX.GETFN  +", # !" !  $" +!   +   K;,25 @J  ;  +@# *  +z S 5N   ++/K K4~V+5S    -IM.INDEX.GETFN "40)6^   +8 'IM.INDEX.GETFN 0.4Q = 5 )H  ^ & +* %IM.INDEX.GETFN +   ++ +: W$" + b $IM.INDEX.GETFN  'IM.INDEX.GETFN ?  &7R; HRULE.GETFN  K9  +A i)0:  +` + )IM.INDEX.GETFN 5Q + )IM.INDEX.GETFN  +8 +z P e +7 (IM.INDEX.GETFN n  : *IM.INDEX.GETFNW(  (IM.INDEX.GETFN &W 9 + I W (IM.INDEX.GETFN  {%" 2*={   &IM.INDEX.GETFN   = +26QP-    ,+ a e  CK C 'IM.INDEX.GETFN  ,B& (IM.INDEX.GETFN q +IM.INDEX.GETFN    +IM.INDEX.GETFN     'IM.INDEX.GETFN -) *     -IM.INDEX.GETFN 2 HRULE.GETFN ] 0 /" Z  +o 3 9,  + &IM.INDEX.GETFN  0  Y   HRULE.GETFN t&  -8 + + + aNg + PQ        _ 4%   +    6)J+"* 1 T s Z#    C"(:  +, )4 "IM.INDEX.GETFN"IM.INDEX.GETFN +F  $IM.INDEX.GETFN#IM.INDEX.GETFN<    UIM.INDEX.GETFN IM.INDEX.GETFN!IM.INDEX.GETFNt IM.INDEX.GETFN!IM.INDEX.GETFN    (5 IM.INDEX.GETFNU $IM.INDEX.GETFN#IM.INDEX.GETFN d $IM.INDEX.GETFN#IM.INDEX.GETFN   $IM.INDEX.GETFN#IM.INDEX.GETFN8 xF E  "IM.INDEX.GETFN"IM.INDEX.GETFN   "IM.INDEX.GETFN"IM.INDEX.GETFN $IM.INDEX.GETFN#IM.INDEX.GETFNI1  ![ea + a IM.INDEX.GETFN!IM.INDEX.GETFN "IM.INDEX.GETFN"IM.INDEX.GETFN6 I# "IM.INDEX.GETFN"IM.INDEX.GETFN 6 &IM.INDEX.GETFN$IM.INDEX.GETFN &IM.INDEX.GETFN$IM.INDEX.GETFN   PA $IM.INDEX.GETFN#IM.INDEX.GETFN + +7 ( &IM.INDEX.GETFN$IM.INDEX.GETFN  +  ~ + IM.INDEX.GETFN!IM.INDEX.GETFN/^[6  "IM.INDEX.GETFN"IM.INDEX.GETFN  +  (IM.INDEX.GETFN%IM.INDEX.GETFNN,` *IM.INDEX.GETFN&IM.INDEX.GETFN    IM.INDEX.GETFN IM.INDEX.GETFNB` 'pB  IM.INDEX.GETFN IM.INDEX.GETFN.* IM.INDEX.GETFN IM.INDEX.GETFN) Y O  4 +$ P IM.INDEX.GETFNY B &*%) 7&!D ]? 9  -8 -IM.INDEX.GETFN6 0CEYMM3;    + +  J % (IM.INDEX.GETFN w.  :} + +1\ 0 %    M  M + + )IM.INDEX.GETFN + +  '+#2".  HRULE.GETFN  'IM.INDEX.GETFN-O" . (IM.INDEX.GETFN * #  7 &IM.INDEX.GETFN   HRULE.GETFN W CaxV 8/ + F +N [      + )IM.INDEX.GETFN  +IM.INDEX.GETFN&. +IM.INDEX.GETFN &=  +T   ,IM.INDEX.GETFN   +  -IM.INDEX.GETFN&/    +     5 +4 ?   R   |LG * gK?  +     | *) zr & b 92 3*+ * $+ &"0=>3)F7(K &IM.INDEX.GETFN3    B5 ,  +    ,v +  % @  /  # /  O    &IM.INDEX.GETFN/ %IM.INDEX.GETFN+ + &IM.INDEX.GETFN-. %) #v G %IM.INDEX.GETFN aDAJ9SlT&*C %IM.INDEX.GETFN! +     + +  =O$'"% NyC% $ %=o7= Y + +bY98;   1L  7 hI!LRg +V +V  +a ] c e W J `Y ( k   8  +   +        ( + 3 + +!); + !' 2   +IM.INDEX.GETFN ] + )IM.INDEX.GETFN 0,E A   W  Y   n3OD( # ( %h'  :`j +%c: ^ P ^ lH !D%5X(((CHARENCODING . MCCS)))PROPS:#DATE:jz \ No newline at end of file diff --git a/docs/medley-irm/25-USERIO-PACKAGES.TEDIT b/docs/medley-irm/25-USERIO-PACKAGES.TEDIT index 23cd7dcd..9b48ec00 100644 Binary files a/docs/medley-irm/25-USERIO-PACKAGES.TEDIT and b/docs/medley-irm/25-USERIO-PACKAGES.TEDIT differ diff --git a/docs/medley-irm/26-GRAPHICS.TEDIT b/docs/medley-irm/26-GRAPHICS.TEDIT index 9ac57b7b..d7d40796 100644 --- a/docs/medley-irm/26-GRAPHICS.TEDIT +++ b/docs/medley-irm/26-GRAPHICS.TEDIT @@ -1,4 +1,280 @@ -INTERLISP-D REFERENCE MANUAL GRAPHICS OUTPUT OPERATIONS "27"26. GRAPHICS OUTPUT OPERATIONS 2 Streams are used as the basis for all I/O operations. Files are implemented as streams that can support character printing and reading operations, and file pointer manipulation. An image stream is a type of stream that also provides an interface for graphical operations. All of the operations that can applied to streams can be applied to image streams. For example, an image stream can be passed as the argument to PRINT, to print something on an image stream. In addition, special functions are provided to draw lines and curves and perform other graphical operations. Calling these functions on a stream that is not an image stream will generate an error. Primitive Graphics Concepts 1 The Interlisp-D graphics system is based on manipulating bitmaps (rectangular arrays of pixels), positions, regions, and textures. These objects are used by all of the graphics functions. Positions A position denotes a point in an X,Y coordinate system. A POSITION is an instance of a record with fields XCOORD and YCOORD and is manipulated with the standard record package facilities. For example, (create POSITION XCOORD 10 YCOORD 20) creates a position representing the point (10,20). (POSITIONP X) [Function] Returns X if X is a position; NIL otherwise. Regions A Region denotes a rectangular area in a coordinate system. Regions are characterized by the coordinates of their bottom left corner and their width and height. A REGION is a record with fields LEFT, BOTTOM, WIDTH, and HEIGHT. It can be manipulated with the standard record package facilities. There are access functions for the REGION record that return the TOP and RIGHT of the region. The following functions are provided for manipulating regions: (CREATEREGION LEFT BOTTOM WIDTH HEIGHT) [Function] Returns an instance of the REGION record which has LEFT, BOTTOM, WIDTH and HEIGHT as respectively its LEFT, BOTTOM, WIDTH, and HEIGHT fields. Example: (CREATEREGION 10 -20 100 200) will create a region that denotes a rectangle whose width is 100, whose height is 200, and whose lower left corner is at the position (10,-20). (REGIONP X) [Function] Returns X if X is a region, NIL otherwise. (INTERSECTREGIONS(INTERSECTREGIONS (Function) NIL NIL ("27") 2) REGION1 REGION2 ... REGIONn) [NoSpread Function] Returns a region which is the intersection of a number of regions. Returns NIL if the intersection is empty. (UNIONREGIONS(UNIONREGIONS (Function) NIL NIL ("27") 2) REGION1 REGION2 ... REGIONn) [NoSpread Function] Returns a region which is the union of a number of regions, i.e. the smallest region that contains all of them. Returns NIL if there are no regions given. (REGIONSINTERSECTP(REGIONSINTERSECTP (Function) NIL NIL ("27") 2) REGION1 REGION2) [Function] Returns T if REGION1 intersects REGION2. Returns NIL if they do not intersect. (SUBREGIONP(SUBREGIONP (Function) NIL NIL ("27") 2) LARGEREGION SMALLREGION) [Function] Returns T if SMALLREGION is a subregion (is equal to or entirely contained in) LARGEREGION; otherwise returns NIL. (EXTENDREGION(EXTENDREGION (Function) NIL NIL ("27") 2) REGION INCLUDEREGION) [Function] Changes (destructively modifies) the region REGION so that it includes the region INCLUDEREGION. It returns REGION. (MAKEWITHINREGION(MAKEWITHINREGION (Function) NIL NIL ("27") 2) REGION LIMITREGION) [Function] Changes (destructively modifies) the left and bottom of the region REGION so that it is within the region LIMITREGION, if possible. If the dimension of REGION are larger than LIMITREGION, REGION is moved to the lower left of LIMITREGION. If LIMITREGION is NIL, the value of the variable WHOLEDISPLAY (the screen region) is used. MAKEWITHINREGION returns the modified REGION. (INSIDEP(INSIDEP (Function) NIL NIL ("27") 2) REGION POSORX Y) [Function] If POSORX and Y are numbers, it returns T if the point (POSORX,Y) is inside of REGION. If POSORX is a POSITION, it returns T if POSORX is inside of REGION. If REGION is a WINDOW, the window's interior region in window coordinates is used. Otherwise, it returns NIL. Bitmaps The display primitives manipulate graphical images in the form of bitmaps. A bitmap is a rectangular array of "pixels," each of which is an integer representing the color of one point in the bitmap image. A bitmap is created with a specific number of bits allocated for each pixel. Most bitmaps used for the display screen use one bit per pixel, so that at most two colors can be represented. If a pixel is 0, the corresponding location on the image is white. If a pixel is 1, its location is black. This interpretation can be changed for the display screen with the function VIDEOCOLOR. Bitmaps with more than one bit per pixel are used to represent color or grey scale images. Bitmaps use a positive integer coordinate system with the lower left corner pixel at coordinate (0,0). Bitmaps are represented as instances of the datatype BITMAP. Bitmaps can be saved on files with the VARS file package command. (BITMAPCREATE WIDTH HEIGHT BITSPERPIXEL) [Function] Creates and returns a new bitmap which is WIDTH pixels wide by HEIGHT pixels high, with BITSPERPIXEL bits per pixel. If BITSPERPIXEL is NIL, it defaults to 1. (BITMAPP(BITMAPP (Function) NIL NIL ("27") 3) X) [Function] Returns X if X is a bitmap, NIL otherwise. (BITMAPWIDTH(BITMAPWIDTH (Function) NIL NIL ("27") 3) BITMAP) [Function] Returns the width of BITMAP in pixels. (BITMAPHEIGHT(BITMAPHEIGHT (Function) NIL NIL ("27") 3) BITMAP) [Function] Returns the height of BITMAP in pixels. (BITSPERPIXEL BITMAP) [Function] Returns the number of bits per pixel of BITMAP. (BITMAPBIT(BITMAPBIT (Function) NIL NIL ("27") 3) BITMAP X Y NEWVALUE) [Function] If NEWVALUE is between 0 and the maximum value for a pixel in BITMAP, the pixel (X,Y) is changed to NEWVALUE and the old value is returned. If NEWVALUE is NIL, BITMAP is not changed but the value of the pixel is returned. If NEWVALUE is anything else, an error is generated. If (X,Y) is outside the limits of BITMAP, 0 is returned and no pixels are changed. BITMAP can also be a window or display stream. Note: non-window image streams are "write-only"; the NEWVALUE argument must be non-NIL. (BITMAPCOPY(BITMAPCOPY (Function) NIL NIL ("27") 3) BITMAP) [Function] Returns a new bitmap which is a copy of BITMAP (same dimensions, bits per pixel, and contents). (EXPANDBITMAP(EXPANDBITMAP (Function) NIL NIL ("27") 3) BITMAP WIDTHFACTOR HEIGHTFACTOR) [Function] Returns a new bitmap that is WIDTHFACTOR times as wide as BITMAP a nd HEIGHTFACTOR times as high. Each pixel of BITMAP is copied into a WIDTHFACTOR times HEIGHTFACTOR block of pixels. If NIL, WIDTHFACTOR defaults to 4, HEIGHTFACTOR to 1. (ROTATEBITMAP(EXPANDBITMAP (Function) NIL NIL ("27") 4) BITMAP) [Function] Given an m-high by n-wide bitmap, this function returns an n-high by m-wide bitmap. The returned bitmap is the image of the original bitmap, rotated 90 degrees clockwise. (SHRINKBITMAP(SHRINKBITMAP (Function) NIL NIL ("27") 4) BITMAP WIDTHFACTOR HEIGHTFACTOR DESTINATIONBITMAP) [Function] Returns a copy of BITMAP that has been shrunken by WIDTHFACTOR and HEIGHTFACTOR in the width and height, respectively. If NIL, WIDTHFACTOR defaults to 4, HEIGHTFACTOR to 1. If DESTINATIONBITMAP is not provided, a bitmap that is 1/WIDTHFACTOR by 1/HEIGHTFACTOR the size of BITMAP is created and returned. WIDTHFACTOR and HEIGHTFACTOR must be positive integers. (PRINTBITMAP(PRINTBITMAP (Function) NIL NIL ("27") 4) BITMAP FILE) [Function] Prints the bitmap BITMAP on the file FILE in a format that can be read back in by READBITMAP. (READBITMAP(READBITMAP (Function) NIL NIL ("27") 4) FILE) [Function] Creates a bitmap by reading an expression (written by PRINTBITMAP) from the file FILE. (EDITBM(EDITBM (Function) NIL NIL ("27") 4) BMSPEC) [Function] EDITBM provides an easy-to-use interactive editing facility for various types of bitmaps. If BMSPEC is a bitmap, it is edited. If BMSPEC is an atom whose value is a bitmap, its value is edited. If BMSPEC is NIL, EDITBM asks for dimensions and creates a bitmap. If BMSPEC is a region, that portion of the screen bitmap is used. If BMSPEC is a window, it is brought to the top and its contents edited. EDITBM sets up the bitmap being edited in an editing window. The editing window has two major areas: a gridded edit area in the lower part of the window and a display area in the upper left part. In the edit area, the left button will add points, the middle button will erase points. The right button provides access to the normal window commands to reposition and reshape the window. The actual size bitmap is shown in the display area. For example, the following is a picture of the bitmap editing window editing a eight-high by eighteen-wide bitmap: `*~RsFuvmmqmmmv[bfwUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTxDUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTDxUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTHDUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTDUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPƪUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUUUUUPUPUPUPUPªUUUUUPUPUPUPUPªUUUUUPUPUPUPUPªUUUUUPUPUPUPUPªUUUUUPUPUPUPUPªUUUUUPUPUPUPUPªUUUUUPUPUPUPUPªUUUUUPUPUPUPUPƪ If the bitmap is too large to fit in the edit area, only a portion will be editable. This portion can be changed by scrolling both up and down in the left margin and left and right in the bottom margin. Pressing the middle button while in the display area will bring up a menu that allows global placement of the portion of the bitmap being edited. To allow more of the bitmap to be editing at once, the window can be reshaped to make it larger or the GridSize command described below can be used to reduce the size of a bit in the edit area. The bitmap editing window can be reshaped to provide more or less room for editing. When this happens, the space allocated to the editing area will be changed to fit in the new region. Whenever the left or middle button is down and the cursor is not in the edit area, the section of the display of the bitmap that is currently in the edit area is complemented. Pressing the left button while not in the edit region will put the lower left 16 x 16 section of the bitmap into the cursor for as long as the left button is held down. Pressing the middle button while not in either the edit area or the display area (i.e., while in the grey area in the upper right or in the title) will bring up a command menu. `Nn|BB8<BD|@d@D@:  $ DPp!#$$DP!$DD$"p!'Ā$$"!$$$!!$D#p!#!@ _R0@ H@ H@ H20! @@ @"!GC@H!I p$$p ! @#@$H@'Ð@$ !$H#P g  ~  xBDHPhDBxA  PxPĄĄx There are commands to stop editing, to restore the bitmap to its initial state and to clear the bitmap. Holding the middle button down over a command will result in an explanatory message being printed in the prompt window. The commands are described below: Paint Puts the current bitmap into a window and call the window PAINT command on it. The PAINT command implements drawing with various brush sizes and shapes but only on an actual sized bitmap. The PAINT mode is left by pressing the RIGHT button and selecting the QUIT command from the menu. At this point, you will be given a choice of whether or not the changes you made while in PAINT mode should be made to the current bitmap. ShowAsTile Tesselates the current bitmap in the upper part of the window. This is useful for determining how a bitmap will look if it were made the display background (using the function CHANGEBACKGROUND). Note: The tiled display will not automatically change as the bitmap changes; to update it, use the ShowAsTile command again. Grid,On/Off Turns the editing grid display on or off. GridSize Allows specification of the size of the editing grid. Another menu will appear giving a choice of several sizes. If one is selected, the editing portion of the bitmap editor will be redrawn using the selected grid size, allowing more or less of the bitmap to be edited without scrolling. The original size is chosen hueristically and is typically about 8. It is particularly useful when editing large bitmaps to set the edit grid size smaller than the original. Reset Sets all or part of the bitmap to the contents it had when EDITBM was called. Another menu will appear giving a choice between resetting the entire bitmap or just the portion that is in the edit area. The second menu also acts as a confirmation, since not selecting one of the choices on this menu results in no action being taken. Clear Sets all or part of the bitmap to 0. As with the Reset command, another menu gives a choice between clearing the entire bitmap or just the portion that is in the edit area. Cursor Sets the cursor to the lower left part of the bitmap. This prompts the user to specify the cursor "hot spot" by clicking in the lower left corner of the grid. OK Copies the changed image into the original bitmap, stops the bitmap editor and closes the edit windows. The changes the bitmap editor makes during the interaction occur on a copy of the original bitmap. Unless the bitmap editor is exited via OK, no changes are made in the original. Stop Stops the bitmap editor without making any changes to the original bitmap. Textures A Texture denotes a pattern of gray which can be used to (conceptually) tessellate the plane to form an infinite sheet of gray. It is currently either a 4 by 4 pattern or a 16 by N (N <= 16) pattern. Textures are created from bitmaps using the following function: (CREATETEXTUREFROMBITMAP(CREATETEXTUREFROMBITMAP (Function) NIL NIL ("27") 6) BITMAP) [Function] Returns a texture object that will produce the texture of BITMAP. If BITMAP is too large, its lower left portion is used. If BITMAP is too small, it is repeated to fill out the texture. (TEXTUREP(TEXTUREP (Function) NIL NIL ("27") 7) OBJECT) [Function] Returns OBJECT if it is a texture; NIL otherwise. The functions which accept textures (TEXTUREP, BITBLT, DSPTEXTURE, etc.) also accept bitmaps up to 16 bits wide by 16 bits high as textures. When a region is being filled with a bitmap texture, the texture is treated as if it were 16 bits wide (if less, the rest is filled with white space). The common textures white and black are available as system constants WHITESHADE and BLACKSHADE. The global variable GRAYSHADE is used by many system facilities as a background gray shade and can be set by the user. (EDITSHADE(EDITSHADE (Function) NIL NIL ("27") 7) SHADE) [Function] Opens a window that allows the user to edit textures. Textures can be either small (4 by 4) patterns or large (16 by 16). In the edit area, the left button adds bits to the shade and the middle button erases bits from the shade. The top part of the window is painted with the current texture whenever all mouse keys are released. Thus it is possible to directly compare two textures that differ by more than one pixel by holding a mouse key down until all changes are made. When the "quit" button is selected, the texture being edited is returned. If SHADE is a texture object, EDITSHADE starts with it. If SHADE is T, it starts with a large (16 by 16) white texture. Otherwise, it starts with WHITESHADE. The following is a picture of the texture editor, editing a large (16 by 16) pattern: @,,000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000``0``0``0`>`0`H`0`H`0`H`0`H`0`H`0`H`0``0``0``0``0``0``000000 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00UUUuU]UWUUUuU]UW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW0ꪺꪺ0 TUU@UTUU@W00 TUU@UTUU@W00 TUU@UTUU@W00 TUU@UTUU@W00UUU`WUUU`W00 TUUUUPUTUUUUPW00 TUUUUPUTUUUUPW00 TUUUUPUTUUUUPW00 TUUUUPUTUUUUPW0ꪪꪪ0 TUPUTUPW0**0 TUPUTUPW0**0 TUPUTUPW0**0 TUPUTUPW0**0U@5UXWU@5UXW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW0ꪪꪪ0 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00UUUuUXWUUUuUXW0 0 0 0 0 0 0 0 0@ @ 0 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00UUUuUXWUUUuUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW0ꪪꪪ0 TUPUTUPW0**0 TUPUTUPW0**0 TUPUTUPW0**0 TUPUTUPW0**0U@5UPWU@5UXW00 TUUUUPUTUUUUPW00 TUUUUPUTUUUUPW00 TUUUUPUTUUUUPW00 TUUUUPUTUUUUPW0ꪪꪪ0 TUU@UTUU@W00 TUU@UTUU@W00 TUU@UTUU@W00 TUU@UTUU@W00TUU`WTUU`W00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW0ꪺꪺ0 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00UUUuU]UWTUUuU]UW0 0 0 0 0 0 0 0 0@ @ 000000 Opening Image Streams 1 An image stream is an output stream which "knows" how to process graphic commands to a graphics output device. Besides accepting the normal character-output functions (PRINT, etc.), an image stream can also be passed as an argument to functions to draw curves, to print characters in multiple fonts, and other graphics operations. Each image stream has an "image stream type," a litatom that specifies the type of graphic output device that the image stream is processing graphics commands for. Currently, the built-in image stream types are DISPLAY (for the display screen), INTERPRESS (for Interpress format printers), and PRESS (for Press format printers). There are also library packages available that define image stream types for the IRIS display, 4045 printer, FX-80 printer, C150 printer, etc. Image streams to the display (display streams) interpret graphics commands by immediately executing the appropriate operations to cause the desired image to appear on the display screen. Image streams for hardcopy devices such as Interpress printers interpret the graphic commands by saving information in a file, which can later be sent to the printer. Note: Not all graphics operations can be properly executed for all image stream types. For example, BITBLT may not be supported to all printers. This functionality is still being developed, but even in the long run some operations may be beyond the physical or logical capabilities of some devices or image file formats. In these cases, the stream will approximate the specified image as best it can. (OPENIMAGESTREAM(OPENIMAGESTREAM (Function) NIL NIL ("27") 8) FILE IMAGETYPE OPTIONS) [Function] Opens and returns an image stream of type IMAGETYPE on a destination specified by FILE. If FILE is a file name on a normal file storage device, the image stream will store graphics commands on the specified file, which can be transmitted to a printer by explicit calls to LISTFILES and SEND.FILE.TO.PRINTER. If IMAGETYPE is DISPLAY, then the user is prompted for a window to open. FILE in this case will be used as the title of the window. If FILE is a file name on the LPT device, this indicates that the graphics commands should be stored in a temporary file, and automatically sent to the printer when the image stream is closed by CLOSEF. FILE = NIL is equivalent to FILE = {LPT}. File names on the LPT device are of the form {LPT}PRINTERNAME.TYPE, where PRINTERNAME, TYPE, or both may be omitted. PRINTERNAME is the name of the particular printer to which the file will be transmitted on closing; it defaults to the first printer on DEFAULTPRINTINGHOST that can print IMAGETYPE files. The TYPE extension supplies the value of IMAGETYPE when it is defaulted (see below). OPENIMAGESTREAM will generate an error if the specified printer does not accept the kind of file specified by IMAGETYPE. If IMAGETYPE is NIL, the image type is inferred from the extension field of FILE and the EXTENSIONS properties in the list PRINTFILETYPES. Thus, the extensions IP, IPR, and INTERPRESS indicate Interpress format, and the extension PRESS indicates Press format. If FILE is a printer file with no extension (of the form {LPT}PRINTERNAME), then IMAGETYPE will be the type that the indicated printer can print. If FILE has no extension but is not on the printer device {LPT}, then IMAGETYPE will default to the type accepted by the first printer on DEFAULTPRINTINGHOST. OPTIONS is a list in property list format, (PROP1 VAL1 PROP2 VAL2 %), used to specify certain attributes of the image stream; not all attributes are meaningful or interpreted by all types of image streams. Acceptable properties are: REGION Value is the region on the page (in stream scale units, 0,0 being the lower-left corner of the page) that text will fill up. It establishes the initial values for DSPLEFTMARGIN, DSPRIGHTMARGIN, DSPBOTTOMMARGIN (the point at which carriage returns cause page advancement) and DSPTOPMARGIN (where the stream is positioned at the beginning of a new page). If this property is not given, the value of the variable DEFAULTPAGEREGION, is used. FONTS Value is a list of fonts that are expected to be used in the image stream. Some image streams (e.g. Interpress) are more efficient if the expected fonts are specified in advance, but this is not necessary. The first font in this list will be the initial font of the stream, otherwise the default font for that image stream type will be used. HEADING Value is the heading to be placed automatically on each page. NIL means no heading. Examples: Suppose that Tremor: is an Interpress printer, Quake is a Press printer, and DEFAULTPRINTINGHOST is (Tremor: Quake): (OPENIMAGESTREAM) returns an Interpress image stream on printer Tremor:. (OPENIMAGESTREAM NIL 'PRESS) returns a Press stream on Quake. (OPENIMAGESTREAM '{LPT}.INTERPRESS) returns an Interpress stream on Tremor:. (OPENIMAGESTREAM '{CORE}FOO.PRESS) returns a Press stream on the file {CORE}FOO.PRESS. (IMAGESTREAMP(IMAGESTREAMP (Function) NIL NIL ("27") 9) X IMAGETYPE) [NoSpread Function] Returns X (possibly coerced to a stream) if it is an output image stream of type IMAGETYPE (or of any type if IMAGETYPE = NIL), otherwise NIL. (IMAGESTREAMTYPE(IMAGESTREAMTYPE (Function) NIL NIL ("27") 9) STREAM) [Function] Returns the image stream type of STREAM. (IMAGESTREAMTYPEP(IMAGESTREAMTYPEP (Function) NIL NIL ("27") 9) STREAM TYPE) [Function] Returns T if STREAM is an image stream of type TYPE. Accessing Image Stream Fields 1 The following functions manipulate the fields of an image stream. These functions return the old value (the one being replaced). A value of NIL for the new value will return the current setting without changing it. These functions do not change any of the bits drawn on the image stream; they just affect future operations done on the image stream. (DSPCLIPPINGREGION(DSPCLIPPINGREGION (Function) NIL NIL ("27") 10) REGION STREAM) [Function] The clipping region is a region that limits the extent of characters printed and lines drawn (in the image stream's coordinate system). Initially set so that no clipping occurs. Warning: For display streams, the window system maintains the clipping region during window operations. Users should be very careful about changing this field. (DSPFONT(DSPFONT (Function) NIL NIL ("27") 10) FONT STREAM) [Function] The font field specifies the font used when printing characters to the image stream. Note: DSPFONT determines its new font descriptor from FONT by the same coercion rules that FONTPROP and FONTCREATE use , with one additional possibility: If FONT is a list of the form (PROP1 VAL1 PROP2 VAL2 ...) where PROP1 is acceptable as a font-property to FONTCOPY, then the new font is obtained by (FONTCOPY (DSPFONT NIL STREAM) PROP1 VAL1 PROP2 VAL2 ...). For example, (DSPFONT '(SIZE 12) STREAM) would change the font to the 12 point version of the current font, leaving all other font properties the same. (DSPTOPMARGIN(DSPTOPMARGIN (Function) NIL NIL ("27") 10) YPOSITION STREAM) [Function] The top margin is an integer that is the Y position after a new page (in the image stream's coordinate system). This function has no effect on windows. (DSPBOTTOMMARGIN(DSPBOTTOMMARGIN (Function) NIL NIL ("27") 10) YPOSITION STREAM) [Function] The bottom margin is an integer that is the minimum Y position that characters will be printed by PRIN1 (in the image stream's coordinate system). This function has no effect on windows. (DSPLEFTMARGIN(DSPLEFTMARGIN (Function) NIL NIL ("27") 10) XPOSITION STREAM) [Function] The left margin is an integer that is the X position after an end-of-line (in the image stream's coordinate system). Initially the left edge of the clipping region. (DSPRIGHTMARGIN(DSPRIGHTMARGIN (Function) NIL NIL ("27") 10) XPOSITION STREAM) [Function] The right margin is an integer that is the maximum X position that characters will be printed by PRIN1 (in the image stream's coordinate system). This is initially the position of the right edge of the window or page. The line length of a window or image stream (as returned by LINELENGTH) is computed by dividing the distance between the left and right margins by the width of an uppercase "A" in the current font. The line length is changed whenever the font, left margin, or right margin are changed or whenever the window is reshaped. (DSPOPERATION(DSPOPERATION (Function) NIL NIL ("27") 11) OPERATION STREAM) [Function] The operation is the default BITBLT operation used when printing or drawing on the image stream. One of REPLACE, PAINT, INVERT, or ERASE. Initially REPLACE. This is a meaningless operation for most printers which support the model that once dots are deposited on a page they cannot be removed. (DSPLINEFEED(DSPLINEFEED (Function) NIL NIL ("27") 11) DELTAY STREAM) [Function] The linefeed is an integer that specifies the Y increment for each linefeed, normally negative. Initially minus the height of the initial font. (DSPCLEOL(DSPLINEFEED (Function) NIL NIL ("27") 11) DSPSTREAM XPOS YPOS HEIGHT) [Function] "Clear to end of line". Clears a region from (XPOS,YPOS) to the right margin of the display, with a height of HEIGHT. If XPOS and YPOS are NIL, clears the remainder of the current display line, using the height of the current font. (DSPRUBOUTCHAR(DSPRUBOUTCHAR (Function) NIL NIL ("27") 11) DSPSTREAM CHAR X Y TTBL) [Function] Backs up over character code CHAR in the DSPSTREAM, erasing it. If X, Y are supplied, the rubbing out starts from the position specified. DSPRUBOUTCHAR assumes CHAR was printed with the terminal table TTBL, so it knows to handle control characters, etc. TTBL defaults to the primary terminal table. (DSPSCALE(DSPSCALE (Function) NIL NIL ("27") 11) SCALE STREAM) [Function] Returns the scale of the image stream STREAM, a number indicating how many units in the streams coordinate system correspond to one printer's point (1/72 of an inch). For example, DSPSCALE returns 1 for display streams, and 35.27778 for Interpress and Press streams (the number of micas per printer's point). In order to be device-independent, user graphics programs must either not specify position values absolutely, or must multiply absolute point quantities by the DSPSCALE of the destination stream. For example, to set the left margin of the Interpress stream XX to one inch, do (DSPLEFTMARGIN (TIMES 72 (DSPSCALE NIL XX)) XX) The SCALE argument to DSPSCALE is currently ignored. In a future release it will enable the scale of the stream to be changed under user control, so that the necessary multiplication will be done internal to the image stream interface. In this case, it would be possible to set the left margin of the Interpress stream XX to one inch by doing (DSPSCALE 1 XX) (DSPLEFTMARGIN 72 XX) (DSPSPACEFACTOR FACTOR STREAM) [Function] The space factor is the amount by which to multiply the natural width of all following space characters on STREAM; this can be used for the justification of text. The default value is 1. For example, if the natural width of a space in STREAM's current font is 12 units, and the space factor is set to two, spaces appear 24 units wide. The values returned by STRINGWIDTH and CHARWIDTH are also affected. The following two functions only have meaning for image streams that can display color: (DSPCOLOR COLOR STREAM) [Function] Sets the default foreground color of STREAM. Returns the previous foreground color. If COLOR is NIL, it returns the current foreground color without changing anything. The default color is white (DSPBACKCOLOR COLOR STREAM) [Function] Sets the background color of STREAM. Returns the previous background color. If COLOR is NIL, it returns the current background color without changing anything. The default background color is black. Current Position of an Image Stream 1 Each image stream has a "current position," which is a position (in the image stream's coordinate system) where the next printing operation will start from. The functions which print characters or draw on an image stream update these values appropriately. The following functions are used to explicitly access the current position of an image stream: (DSPXPOSITION XPOSITION STREAM) [Function] Returns the X coordinate of the current position of STREAM. If XPOSITION is non-NIL, the X coordinate is set to it (without changing the Y coordinate). (DSPYPOSITION YPOSITION STREAM) [Function] Returns the Y coordinate of the current position of STREAM. If YPOSITION is non-NIL, the Y coordinate is set to it (without changing the X coordinate). (MOVETO X Y STREAM) [Function] Changes the current position of STREAM to the point (X,Y). (RELMOVETO DX DY STREAM) [Function] Changes the current position to the point (DX,DY) coordinates away from current position of STREAM. (MOVETOUPPERLEFT STREAM REGION) [Function] Moves the current position to the beginning position of the top line of text. If REGION is non-NIL, it must be a REGION and the X position is changed to the left edge of REGION and the Y position changed to the top of REGION less the font ascent of STREAM. If REGION is NIL, the X coordinate is changed to the left margin of STREAM and the Y coordinate is changed to the top of the clipping region of STREAM less the font ascent of STREAM. Moving Bits Between Bitmaps With BITBLT 1 BITBLT is the primitive function for moving bits from one bitmap to another, or from a bitmap to an image stream. (BITBLT SOURCE SOURCELEFT SOURCEBOTTOM DESTINATION DESTINATIONLEFT DESTINATIONBOTTOM WIDTH HEIGHT SOURCETYPE OPERATION TEXTURE CLIPPINGREGION) [Function] Transfers a rectangular array of bits from SOURCE to DESTINATION. SOURCE can be a bitmap, or a display stream or window, in which case its associated bitmap is used. DESTINATION can be a bitmap or an arbitrary image stream. WIDTH and HEIGHT define a pair of rectangles, one in each of the SOURCE and DESTINATION whose left, bottom corners are at, respectively, (SOURCELEFT, SOURCEBOTTOM) and (DESTINATIONLEFT, DESTINATIONBOTTOM). If these rectangles overlap the boundaries of either source or destination they are both reduced in size (without translation) so that they fit within their respective boundaries. If CLIPPINGREGION is non-NIL it should be a REGION and is interpreted as a clipping region within DESTINATION; clipping to this region may further reduce the defining rectangles. These (possibly reduced) rectangles define the source and destination rectangles for BITBLT. The mode of transferring bits is defined by SOURCETYPE and OPERATION. SOURCETYPE and OPERATION specify whether the source bits should come from SOURCE or TEXTURE, and how these bits are combined with those of DESTINATION. SOURCETYPE and OPERATION are described further below. TEXTURE is a texture. BITBLT aligns the texture so that the upper-left pixel of the texture coincides with the upper-left pixel of the destination bitmap. SOURCELEFT, SOURCEBOTTOM, DESTINATIONLEFT, and DESTINATIONBOTTOM default to 0. WIDTH and HEIGHT default to the width and height of the SOURCE. TEXTURE defaults to white. SOURCETYPE defaults to INPUT. OPERATION defaults to REPLACE. If CLIPPINGREGION is not provided, no additional clipping is done. BITBLT returns T if any bits were moved; NIL otherwise. Note: If SOURCE or DESTINATION is a window or image stream, the remaining arguments are interpreted as values in the coordinate system of the window or image stream and the operation of BITBLT is translated and clipped accordingly. Also, if a window or image stream is used as the destination to BITBLT, its clipping region further limits the region involved. SOURCETYPE specifies whether the source bits should come from the bitmap SOURCE, or from the texture TEXTURE. SOURCETYPE is interpreted as follows: INPUT The source bits come from SOURCE. TEXTURE is ignored. INVERT The source bits are the inverse of the bits from SOURCE. TEXTURE is ignored. TEXTURE The source bits come from TEXTURE. SOURCE, SOURCELEFT, and SOURCEBOTTOM are ignored. OPERATION specifies how the source bits (as specified by SOURCETYPE) are combined with the bits in DESTINATION and stored back into DESTINATION. DESTINATION is one of the following: REPLACE All source bits (on or off) replace destination bits. PAINT Any source bits that are on replace the corresponding destination bits. Source bits that are off have no effect. Does a logical OR between the source bits and the destination bits. INVERT Any source bits that are on invert the corresponding destination bits. Does a logical XOR between the source bits and the destination bits. ERASE Any source bits that are on erase the corresponding destination bits. Does a logical AND operation between the inverse of the source bits and the destination bits. Different combinations of SOURCETYPE and OPERATION can be specified to achieve many different effects. Given the following bitmaps as the values of SOURCE, TEXTURE, and DESTINATION: @<F??~~??<Ǿal0ql09>l0 l0' @;FDDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" 3 f3f3f3f3 f3< @PZxbgl6gllfÃ7glnf7mlnf6lmlc6lmx6xg BITBLT would produce the results given below for the difference combinations of SOURCETYPE and OPERATION (assuming CLIPPINGREGION, SOURCELEFT, etc. are set correctly, of course): @Pf???~~~~~~?~~?~~?~~??????????????????????????????7`6ٌ8ٌflٌg|wc`v3<6 y mq9mqmyهmmmm9e @Pf~~~?~?~?~??????????????????????????????7`6ٌ8ٌflٌg|wc`v3<6 xCflf09lv0mmv0myn0mcn09cf0 @Pf~~~?~?~?~???????????????????????????????7`6ٌ8ٌflٌg|wc`v3<6 ll9n0mn>mm0mm09l> @Pf?7`6ٌ8ٌflٌg|wc`v3<6 }a09a0m}>maðma9}> @Pf??mm0mm09l> @Pf???~~~~~~maðma9}> @PfDDDDDDDffffffd"""""" DDDDDDDffffffd"""""" DDDDDDDffffffd"""""" DDDDDDDffffffd"""""" DDDDDDDffffffd"""""" DDDDDDDffffffd"""""" DDDDDDDffffffd"""""" DDDDDDDffffffd"""""""#DDDDDDG&ffffffg"""""""#DDDDDDG&ffffffg"""""""#DDDDDDG&ffffffg"""""""#DDDDDDG&ffffffg"""""""#DDDDDDG&ffffffg"""""""#DDDDDDG&ffffffg"""""""#DDDDDDG&ffffffg"""""""#}|`a`8``fl|`|g|``c`a`3<}Ǚ|y mq9mqmyهmmmm9e @PfDDDDfffd""" DDDDfffd""" DDDDfffd""" DDDDfffd""" DDDDfffd""" DDDDfffd""" DDDDfffd""" DDDDfffd""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""}|`a`8``fl|`|g|``c`a`3<}Ǚ|xCflf09lv0mmv0myn0mcn09cf0 @PfDDDDٙfffd""" DDDDٙfffd""" DDDDٙfffd""" DDDDٙfffd""" DDDDٙfffd""" DDDDٙfffd""" DDDDٙfffd""" DDDDٙfffd""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""}|`a`8``fl|`|g|``c`a`3<}Ǚ|ll9n0mn>mm0mm09l> @Pfٙٙٙٙٙٙٙٙ}|`a`8``fl|`|g|``c`a`3<}Ǚ|}a09a0m}>maðma9}> (BLTSHADE TEXTURE DESTINATION DESTINATIONLEFT DESTINATIONBOTTOM WIDTH HEIGHT OPERATION CLIPPINGREGION) [Function] BLTSHADE is the SOURCETYPE = TEXTURE case of BITBLT. It fills the specified region of the destination bitmap DESTINATION with the texture TEXTURE. DESTINATION can be a bitmap or image stream. (BITMAPIMAGESIZE BITMAP DIMENSION STREAM) [Function] Returns the size that BITMAP will be when BITBLTed to STREAM, in STREAM's units. DIMENSION can be one of WIDTH, HEIGHT, or NIL, in which case the dotted pair (WIDTH . HEIGHT) will be returned. Drawing Lines 1 Interlisp-D provides several functions for drawing lines and curves on image streams. The line drawing functions are intended for interactive applications where efficiency is important. They do not allow the use of "brush" patterns, like the curve drawing functions, but (for display streams) they support drawing a line in INVERT mode, so redrawing the line will erase it. DRAWCURVE can be used to draw lines using a brush. (DRAWLINE X1 Y1 X2 Y2 WIDTH OPERATION STREAM COLOR DASHING) [Function] Draws a straight line from the point (X1,Y1) to the point (X2,Y2) on the image stream STREAM. The position of STREAM is set to (X2,Y2). If X1 equals X2 and Y1 equals Y2, a point is drawn at (X1,Y1). WIDTH is the width of the line, in the units of the device. If WIDTH is NIL, the default is 1. OPERATION is the BITBLT operation used to draw the line. If OPERATION is NIL, the value of DSPOPERATION for the image stream is used. COLOR is a color specification that determines the color used to draw the line for image streams that support color. If COLOR is NIL, the DSPCOLOR of STREAM is used. DASHING is a list of positive integers that determines the dashing characteristics of the line. The line is drawn for the number of points indicated by the first element of the dashing list, is not drawn for the number of points indicated by the second element. The third element indicates how long it will be on again, and so forth. The dashing sequence is repeated from the beginning when the list is exhausted. A brush LINEWITHBRUSH-by-LINEWITHBRUSH is used. If DASHING is NIL, the line is not dashed. (DRAWBETWEEN POSITION1 POSITION2 WIDTH OPERATION STREAM COLOR DASHING) [Function] Draws a line from the point POSITION1 to the point POSITION2 onto the destination bitmap of STREAM. The position of STREAM is set to POSITION2. In the Medley release, when using the color argument, Interpress (DRAWLINE (Function) NIL NIL ("27") 16)DRAWLINE treats 16x16 bitmaps or negative numbers as shades/textures. Positive numbers continue to refer to color maps, and so cannot be used as textures. To convert an integer shade into a negative number use NEGSHADE (e.g. (NEGSHADE 42495) is -23041). (DRAWTO X Y WIDTH OPERATION STREAM COLOR DASHING) [Function] Draws a line from the current position to the point (X,Y) onto the destination bitmap of STREAM. The position of STREAM is set to (X,Y). (RELDRAWTO DX DY WIDTH OPERATION STREAM COLOR DASHING) [Function] Draws a line from the current position to the point (DX,DY) coordinates away onto the destination bitmap of STREAM. The position of STREAM is set to the end of the line. If DX and DY are both 0, nothing is drawn. Drawing Curves 1 A curve is drawn by placing a brush pattern centered at each point along the curve's trajectory. A brush pattern is defined by its shape, size, and color. The predefined brush shapes are ROUND, SQUARE, HORIZONTAL, VERTICAL, and DIAGONAL; new brush shapes can be created using the INSTALLBRUSH function, described below. A brush size is an integer specifying the width of the brush in the units of the device. The color is a color specification, which is only used if the curve is drawn to an image stream that supports colors. A brush is specified to the various drawing functions as a list of the form (SHAPE WIDTH COLOR), for example (SQUARE 2) or (VERTICAL 4 RED). A brush can also be specified as a positive integer, which is interpreted as a ROUND brush of that width. If a brush is a litatom, it is assumed to be a function which is called at each point of the curve's trajectory (with three arguments: the X-coordinate of the point, the Y-coordinate, and the image stream), and should do whatever image stream operations are necessary to draw each point. Finally, if a brush is specified as NIL, a (ROUND 1) brush is used as default. The appearance of a curve is also determined by its dashing characteristics. Dashing is specified by a list of positive integers. If a curve is dashed, the brush is placed along the trajectory for the number of units indicated by the first element of the dashing list. The brush is off, not placed in the bitmap, for a number of units indicated by the second element. The third element indicates how long it will be on again, and so forth. The dashing sequence is repeated from the beginning when the list is exhausted. The units used to measure dashing are the units of the brush. For example, specifying the dashing as (1 1) with a brush of (ROUND 16) would put the brush on the trajectory, skip 16 points, and put down another brush. A curve is not dashed if the dashing argument to the drawing function is NIL. The curve functions use the image stream's clipping region and operation. Most types of image streams only support the PAINT operation when drawing curves. When drawing to a display stream, the curve-drawing functions accept the operation INVERT if the brush argument is 1. For brushes larger than 1, these functions will use the ERASE operation instead of INVERT. For display streams, the curve-drawing functions treat the REPLACE operation the same as PAINT. (DRAWCURVE KNOTS CLOSED BRUSH DASHING STREAM) [Function] Draws a "parametric cubic spline curve" on the image stream STREAM. KNOTS is a list of positions to which the curve will be fitted. If CLOSED is non-NIL, the curve will be closed; otherwise it ends at the first and last positions in KNOTS. BRUSH and DASHING are interpreted as described above. For example, (DRAWCURVE '((10 . 10)(50 . 50)(100 . 10)(150 . 50)) NIL '(ROUND 5) '(1 1 1 2) XX) would draw a curve like the following on the display stream XX: `;  > > >                 p p p p       > > > q  p   (DRAWCIRCLE CENTERX CENTERY RADIUS BRUSH DASHING STREAM) [Function] Draws a circle of radius RADIUS about the point (CENTERX,CENTERY) onto the image stream STREAM. STREAM's position is left at (CENTERX,CENTERY). The other arguments are interpreted as described above. (DRAWARC CENTERX CENTERY RADIUS STARTANGLE NDEGREES BRUSH DASHINGSTREAM) [Function] Draws an arc of the circle whose center point is (CENTERX CENTERY) and whose radius is RADIUS from the position at STARTANGLE degrees for NDEGREES number of degrees. If STARTANGLE is 0, the starting point will be (CENTERX (CENTERY + RADIUS)). If NDEGREES is positive, the arc will be counterclockwise. If NDEGREES is negative, the arc will be clockwise. The other arguments are interpreted as described in DRAWCIRCLE. (DRAWELLIPSE CENTERX CENTERY SEMIMINORRADIUS SEMIMAJORRADIUS ORIENTATION BRUSH DASHING STREAM) [Function] Draws an ellipse with a minor radius of SEMIMINORRADIUS and a major radius of SEMIMAJORRADIUS about the point (CENTERX,CENTERY) onto the image stream STREAM. ORIENTATION is the angle of the major axis in degrees, positive in the counterclockwise direction. STREAM's position is left at (CENTERX,CENTERY). The other arguments are interpreted as described above. New brush shapes can be defined using the following function: (INSTALLBRUSH BRUSHNAME BRUSHFN BRUSHARRAY) [Function] Installs a new brush called BRUSHNAME with creation-function BRUSHFN and optional array BRUSHARRAY. BRUSHFN should be a function of one argument (a width), which returns a bitmap of the brush for that width. BRUSHFN will be called to create new instances of BRUSHNAME-type brushes; the sixteen smallest instances will be pre-computed and cached. "Hand-crafted" brushes can be supplied as the BRUSHARRAY argument. Changing an existing brush can be done by calling INSTALLBRUSH with new BRUSHFN and/or BRUSHARRAY. (DRAWPOINT X Y BRUSH STREAM OPERATION) [Function] Draws BRUSH centered around point (X, Y) on STREAM, using the operation OPERATION. BRUSH may be a bitmap or a brush. Miscellaneous Drawing and Printing Operations 1 (DSPFILL REGION TEXTURE OPERATION STREAM) [Function] Fills REGION of the image stream STREAM (within the clipping region) with the texture TEXTURE. If REGION is NIL, the whole clipping region of STREAM is used. If TEXTURE or OPERATION is NIL, the values for STREAM are used. (DRAWPOLYGON(DRAWPOLYGON (Function) NIL NIL ("27") 18) POINTS CLOSED BRUSH DASHING STREAM) [Function] Draws a polygon on the image stream STREAM. POINTS is a list of positions to which the figure will be fitted (the vertices of the polygon). If CLOSED is non-NIL, then the starting position is specified only once in POINTS. If CLOSED is NIL, then the starting vertex must be specified twice in POINTS. BRUSH and DASHING are interpreted as described in Chapter 27 of the Interlisp-D Reference Manual. For example, (DRAWPOLYGON '((100 . 100) (50 . 125) (150 . 175) (200 . 100) (150 . 50)) T '(ROUND 3) '(4 2) XX) will draw a polygon like the following on the display stream XX. `p88>>8>@~p~8~@pπp8~~>q19qpA8 p8pp@8 p8pp@8 p@ (FILLPOLYGON POINTS TEXTURE OPERATION WINDNUMBER STREAM) [Function] OPERATION is the BITBLT operation (see page 27.15 in the Interlisp-D Reference Manual) used to fill the polygon. If the OPERATION is NIL, the OPERATION defaults to the STREAM default OPERATION. WINDNUMBER is the number for the winding rule convention . This number is either 0 or 1; 0 indicates the "zero" winding rule, 1 indicates the "odd" winding rule. When filling a polygon, there is more than one way of dealing with the situation where two polygon sides intersect, or one polygon is fully inside the other. Currently, FILLPOLYGON to a display stream uses the "odd" winding rule, which means that intersecting polygon sides define areas that are filled or not filled somewhat like a checkerboard. For example, (FILLPOLYGON '( ((110 . 110)(150 . 200)(190 . 110)) ((135 . 125)(160 . 125)(160 . 150)(135 . 150)) ) GRAYSHADE WINDOW) will produce a display something like this: `/E@@@ +INTERLISP-D REFERENCE MANUAL + GRAPHICS OUTPUT OPERATIONS + +"27"26. GRAPHICS OUTPUT OPERATIONS +2 + +Streams are used as the basis for all I/O operations. Files are implemented as streams that can support character printing and reading operations, and file pointer manipulation. An image stream is a type of stream that also provides an interface for graphical operations. All of the operations that can applied to streams can be applied to image streams. For example, an image stream can be passed as the argument to PRINT, to print something on an image stream. In addition, special functions are provided to draw lines and curves and perform other graphical operations. Calling these functions on a stream that is not an image stream will generate an error. +Primitive Graphics Concepts +1 + +The Interlisp-D graphics system is based on manipulating bitmaps (rectangular arrays of pixels), positions, regions, and textures. These objects are used by all of the graphics functions. +Positions +A position denotes a point in an X,Y coordinate system. A POSITION is an instance of a record with fields XCOORD and YCOORD and is manipulated with the standard record package facilities. For example, (create POSITION XCOORD _ 10 YCOORD _ 20) creates a position representing the point (10,20). +(POSITIONP X) [Function] +Returns X if X is a position; NIL otherwise. +Regions +A Region denotes a rectangular area in a coordinate system. Regions are characterized by the coordinates of their bottom left corner and their width and height. A REGION is a record with fields LEFT, BOTTOM, WIDTH, and HEIGHT. It can be manipulated with the standard record package facilities. There are access functions for the REGION record that return the TOP and RIGHT of the region. +The following functions are provided for manipulating regions: +(CREATEREGION LEFT BOTTOM WIDTH HEIGHT) [Function] +Returns an instance of the REGION record which has LEFT, BOTTOM, WIDTH and HEIGHT as respectively its LEFT, BOTTOM, WIDTH, and HEIGHT fields. +Example: (CREATEREGION 10 -20 100 200) will create a region that denotes a rectangle whose width is 100, whose height is 200, and whose lower left corner is at the position (10,-20). +(REGIONP X) [Function] +Returns X if X is a region, NIL otherwise. +(INTERSECTREGIONS(INTERSECTREGIONS (Function) NIL NIL ("27") 2) REGION1 REGION2 ... REGIONn) [NoSpread Function] +Returns a region which is the intersection of a number of regions. Returns NIL if the intersection is empty. +(UNIONREGIONS(UNIONREGIONS (Function) NIL NIL ("27") 2) REGION1 REGION2 ... REGIONn) [NoSpread Function] +Returns a region which is the union of a number of regions, i.e. the smallest region that contains all of them. Returns NIL if there are no regions given. +(REGIONSINTERSECTP(REGIONSINTERSECTP (Function) NIL NIL ("27") 2) REGION1 REGION2) [Function] +Returns T if REGION1 intersects REGION2. Returns NIL if they do not intersect. +(SUBREGIONP(SUBREGIONP (Function) NIL NIL ("27") 2) LARGEREGION SMALLREGION) [Function] +Returns T if SMALLREGION is a subregion (is equal to or entirely contained in) LARGEREGION; otherwise returns NIL. +(EXTENDREGION(EXTENDREGION (Function) NIL NIL ("27") 2) REGION INCLUDEREGION) [Function] +Changes (destructively modifies) the region REGION so that it includes the region INCLUDEREGION. It returns REGION. +(MAKEWITHINREGION(MAKEWITHINREGION (Function) NIL NIL ("27") 2) REGION LIMITREGION) [Function] +Changes (destructively modifies) the left and bottom of the region REGION so that it is within the region LIMITREGION, if possible. If the dimension of REGION are larger than LIMITREGION, REGION is moved to the lower left of LIMITREGION. If LIMITREGION is NIL, the value of the variable WHOLEDISPLAY (the screen region) is used. MAKEWITHINREGION returns the modified REGION. +(INSIDEP(INSIDEP (Function) NIL NIL ("27") 2) REGION POSORX Y) [Function] +If POSORX and Y are numbers, it returns T if the point (POSORX,Y) is inside of REGION. If POSORX is a POSITION, it returns T if POSORX is inside of REGION. If REGION is a WINDOW, the window's interior region in window coordinates is used. Otherwise, it returns NIL. +Bitmaps +The display primitives manipulate graphical images in the form of bitmaps. A bitmap is a rectangular array of "pixels," each of which is an integer representing the color of one point in the bitmap image. A bitmap is created with a specific number of bits allocated for each pixel. Most bitmaps used for the display screen use one bit per pixel, so that at most two colors can be represented. If a pixel is 0, the corresponding location on the image is white. If a pixel is 1, its location is black. This interpretation can be changed for the display screen with the function VIDEOCOLOR. Bitmaps with more than one bit per pixel are used to represent color or grey scale images. Bitmaps use a positive integer coordinate system with the lower left corner pixel at coordinate (0,0). Bitmaps are represented as instances of the datatype BITMAP. Bitmaps can be saved on files with the VARS file package command. +(BITMAPCREATE WIDTH HEIGHT BITSPERPIXEL) [Function] +Creates and returns a new bitmap which is WIDTH pixels wide by HEIGHT pixels high, with BITSPERPIXEL bits per pixel. If BITSPERPIXEL is NIL, it defaults to 1. +(BITMAPP(BITMAPP (Function) NIL NIL ("27") 3) X) [Function] +Returns X if X is a bitmap, NIL otherwise. +(BITMAPWIDTH(BITMAPWIDTH (Function) NIL NIL ("27") 3) BITMAP) [Function] +Returns the width of BITMAP in pixels. +(BITMAPHEIGHT(BITMAPHEIGHT (Function) NIL NIL ("27") 3) BITMAP) [Function] +Returns the height of BITMAP in pixels. +(BITSPERPIXEL BITMAP) [Function] +Returns the number of bits per pixel of BITMAP. +(BITMAPBIT(BITMAPBIT (Function) NIL NIL ("27") 3) BITMAP X Y NEWVALUE) [Function] +If NEWVALUE is between 0 and the maximum value for a pixel in BITMAP, the pixel (X,Y) is changed to NEWVALUE and the old value is returned. If NEWVALUE is NIL, BITMAP is not changed but the value of the pixel is returned. If NEWVALUE is anything else, an error is generated. If (X,Y) is outside the limits of BITMAP, 0 is returned and no pixels are changed. BITMAP can also be a window or display stream. Note: non-window image streams are "write-only"; the NEWVALUE argument must be non-NIL. +(BITMAPCOPY(BITMAPCOPY (Function) NIL NIL ("27") 3) BITMAP) [Function] +Returns a new bitmap which is a copy of BITMAP (same dimensions, bits per pixel, and contents). +(EXPANDBITMAP(EXPANDBITMAP (Function) NIL NIL ("27") 3) BITMAP WIDTHFACTOR HEIGHTFACTOR) [Function] +Returns a new bitmap that is WIDTHFACTOR times as wide as BITMAP a +nd HEIGHTFACTOR times as high. Each pixel of BITMAP is copied into a WIDTHFACTOR times HEIGHTFACTOR block of pixels. If NIL, WIDTHFACTOR defaults to 4, HEIGHTFACTOR to 1. +(ROTATEBITMAP(EXPANDBITMAP (Function) NIL NIL ("27") 4) BITMAP) [Function] +Given an m-high by n-wide bitmap, this function returns an n-high by m-wide bitmap. The returned bitmap is the image of the original bitmap, rotated 90 degrees clockwise. +(SHRINKBITMAP(SHRINKBITMAP (Function) NIL NIL ("27") 4) BITMAP WIDTHFACTOR HEIGHTFACTOR DESTINATIONBITMAP) [Function] +Returns a copy of BITMAP that has been shrunken by WIDTHFACTOR and HEIGHTFACTOR in the width and height, respectively. If NIL, WIDTHFACTOR defaults to 4, HEIGHTFACTOR to 1. If DESTINATIONBITMAP is not provided, a bitmap that is 1/WIDTHFACTOR by 1/HEIGHTFACTOR the size of BITMAP is created and returned. WIDTHFACTOR and HEIGHTFACTOR must be positive integers. +(PRINTBITMAP(PRINTBITMAP (Function) NIL NIL ("27") 4) BITMAP FILE) [Function] +Prints the bitmap BITMAP on the file FILE in a format that can be read back in by READBITMAP. +(READBITMAP(READBITMAP (Function) NIL NIL ("27") 4) FILE) [Function] +Creates a bitmap by reading an expression (written by PRINTBITMAP) from the file FILE. +(EDITBM(EDITBM (Function) NIL NIL ("27") 4) BMSPEC) [Function] +EDITBM provides an easy-to-use interactive editing facility for various types of bitmaps. If BMSPEC is a bitmap, it is edited. If BMSPEC is an atom whose value is a bitmap, its value is edited. If BMSPEC is NIL, EDITBM asks for dimensions and creates a bitmap. If BMSPEC is a region, that portion of the screen bitmap is used. If BMSPEC is a window, it is brought to the top and its contents edited. +EDITBM sets up the bitmap being edited in an editing window. The editing window has two major areas: a gridded edit area in the lower part of the window and a display area in the upper left part. In the edit area, the left button will add points, the middle button will erase points. The right button provides access to the normal window commands to reposition and reshape the window. The actual size bitmap is shown in the display area. For example, the following is a picture of the bitmap editing window editing a eight-high by eighteen-wide bitmap: +*~RsFuvmmqmmmv[bfwUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTxDUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTDxUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTHDUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUTDUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPªUUUUUPUUUUUUUPUPUPƪUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUUUPUUUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUUUUUUUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUPUUUUUPUPUPUPUPªUUUUUPUPUPUPUPªUUUUUPUPUPUPUPªUUUUUPUPUPUPUPªUUUUUPUPUPUPUPªUUUUUPUPUPUPUPªUUUUUPUPUPUPUPªUUUUUPUPUPUPUPƪ0.75 0 0 +If the bitmap is too large to fit in the edit area, only a portion will be editable. This portion can be changed by scrolling both up and down in the left margin and left and right in the bottom margin. Pressing the middle button while in the display area will bring up a menu that allows global placement of the portion of the bitmap being edited. To allow more of the bitmap to be editing at once, the window can be reshaped to make it larger or the GridSize_ command described below can be used to reduce the size of a bit in the edit area. +The bitmap editing window can be reshaped to provide more or less room for editing. When this happens, the space allocated to the editing area will be changed to fit in the new region. +Whenever the left or middle button is down and the cursor is not in the edit area, the section of the display of the bitmap that is currently in the edit area is complemented. Pressing the left button while not in the edit region will put the lower left 16 x 16 section of the bitmap into the cursor for as long as the left button is held down. +Pressing the middle button while not in either the edit area or the display area (i.e., while in the grey area in the upper right or in the title) will bring up a command menu. +Nn|BB8<BD|@d@D@:  $ DPp!#$$DP!$DD$"p!'Ā$$"!$$$!!$D#p!#!@ _R0@ H@ H@ H20! @@ @"!GC@H!I p$$p ! @#@$H@'Ð@$ !$H#P g  ~  xBDHPhDBxA  PxPĄĄx0.75 0 0 +There are commands to stop editing, to restore the bitmap to its initial state and to clear the bitmap. Holding the middle button down over a command will result in an explanatory message being printed in the prompt window. The commands are described below: + Paint Puts the current bitmap into a window and call the window PAINT command on it. The PAINT command implements drawing with various brush sizes and shapes but only on an actual sized bitmap. The PAINT mode is left by pressing the RIGHT button and selecting the QUIT command from the menu. At this point, you will be given a choice of whether or not the changes you made while in PAINT mode should be made to the current bitmap. + ShowAsTile Tesselates the current bitmap in the upper part of the window. This is useful for determining how a bitmap will look if it were made the display background (using the function CHANGEBACKGROUND). Note: The tiled display will not automatically change as the bitmap changes; to update it, use the ShowAsTile command again. + Grid,On/Off Turns the editing grid display on or off. + GridSize_ Allows specification of the size of the editing grid. Another menu will appear giving a choice of several sizes. If one is selected, the editing portion of the bitmap editor will be redrawn using the selected grid size, allowing more or less of the bitmap to be edited without scrolling. The original size is chosen hueristically and is typically about 8. It is particularly useful when editing large bitmaps to set the edit grid size smaller than the original. + Reset Sets all or part of the bitmap to the contents it had when EDITBM was called. Another menu will appear giving a choice between resetting the entire bitmap or just the portion that is in the edit area. The second menu also acts as a confirmation, since not selecting one of the choices on this menu results in no action being taken. + Clear Sets all or part of the bitmap to 0. As with the Reset command, another menu gives a choice between clearing the entire bitmap or just the portion that is in the edit area. + Cursor_ Sets the cursor to the lower left part of the bitmap. This prompts the user to specify the cursor "hot spot" by clicking in the lower left corner of the grid. + OK Copies the changed image into the original bitmap, stops the bitmap editor and closes the edit windows. The changes the bitmap editor makes during the interaction occur on a copy of the original bitmap. Unless the bitmap editor is exited via OK, no changes are made in the original. + Stop Stops the bitmap editor without making any changes to the original bitmap. +Textures +A Texture denotes a pattern of gray which can be used to (conceptually) tessellate the plane to form an infinite sheet of gray. It is currently either a 4 by 4 pattern or a 16 by N (N <= 16) pattern. Textures are created from bitmaps using the following function: +(CREATETEXTUREFROMBITMAP(CREATETEXTUREFROMBITMAP (Function) NIL NIL ("27") 6) BITMAP) [Function] +Returns a texture object that will produce the texture of BITMAP. If BITMAP is too large, its lower left portion is used. If BITMAP is too small, it is repeated to fill out the texture. +(TEXTUREP(TEXTUREP (Function) NIL NIL ("27") 7) OBJECT) [Function] +Returns OBJECT if it is a texture; NIL otherwise. +The functions which accept textures (TEXTUREP, BITBLT, DSPTEXTURE, etc.) also accept bitmaps up to 16 bits wide by 16 bits high as textures. When a region is being filled with a bitmap texture, the texture is treated as if it were 16 bits wide (if less, the rest is filled with white space). +The common textures white and black are available as system constants WHITESHADE and BLACKSHADE. The global variable GRAYSHADE is used by many system facilities as a background gray shade and can be set by the user. +(EDITSHADE(EDITSHADE (Function) NIL NIL ("27") 7) SHADE) [Function] +Opens a window that allows the user to edit textures. Textures can be either small (4 by 4) patterns or large (16 by 16). In the edit area, the left button adds bits to the shade and the middle button erases bits from the shade. The top part of the window is painted with the current texture whenever all mouse keys are released. Thus it is possible to directly compare two textures that differ by more than one pixel by holding a mouse key down until all changes are made. When the "quit" button is selected, the texture being edited is returned. +If SHADE is a texture object, EDITSHADE starts with it. If SHADE is T, it starts with a large (16 by 16) white texture. Otherwise, it starts with WHITESHADE. +The following is a picture of the texture editor, editing a large (16 by 16) pattern: +,,000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000``0``0``0`>`0`H`0`H`0`H`0`H`0`H`0`H`0``0``0``0``0``0``000000 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00UUUuU]UWUUUuU]UW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW0ꪺꪺ0 TUU@UTUU@W00 TUU@UTUU@W00 TUU@UTUU@W00 TUU@UTUU@W00UUU`WUUU`W00 TUUUUPUTUUUUPW00 TUUUUPUTUUUUPW00 TUUUUPUTUUUUPW00 TUUUUPUTUUUUPW0ꪪꪪ0 TUPUTUPW0**0 TUPUTUPW0**0 TUPUTUPW0**0 TUPUTUPW0**0U@5UXWU@5UXW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW0ꪪꪪ0 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00UUUuUXWUUUuUXW0 0 0 0 0 0 0 0 0@ @ 0 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00UUUuUXWUUUuUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW00 UUUUUUPUUUUUUUPW0ꪪꪪ0 TUPUTUPW0**0 TUPUTUPW0**0 TUPUTUPW0**0 TUPUTUPW0**0U@5UPWU@5UXW00 TUUUUPUTUUUUPW00 TUUUUPUTUUUUPW00 TUUUUPUTUUUUPW00 TUUUUPUTUUUUPW0ꪪꪪ0 TUU@UTUU@W00 TUU@UTUU@W00 TUU@UTUU@W00 TUU@UTUU@W00TUU`WTUU`W00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW0ꪺꪺ0 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00 TUUUUUUUTUUUUUUW00UUUuU]UWTUUuU]UW0 0 0 0 0 0 0 0 0@ @ 0000000.5 0 0 +Opening Image Streams +1 + +An image stream is an output stream which "knows" how to process graphic commands to a graphics output device. Besides accepting the normal character-output functions (PRINT, etc.), an image stream can also be passed as an argument to functions to draw curves, to print characters in multiple fonts, and other graphics operations. +Each image stream has an "image stream type," a litatom that specifies the type of graphic output device that the image stream is processing graphics commands for. Currently, the built-in image stream types are DISPLAY (for the display screen), INTERPRESS (for Interpress format printers), and PRESS (for Press format printers). There are also library packages available that define image stream types for the IRIS display, 4045 printer, FX-80 printer, C150 printer, etc. +Image streams to the display (display streams) interpret graphics commands by immediately executing the appropriate operations to cause the desired image to appear on the display screen. Image streams for hardcopy devices such as Interpress printers interpret the graphic commands by saving information in a file, which can later be sent to the printer. +Note: Not all graphics operations can be properly executed for all image stream types. For example, BITBLT may not be supported to all printers. This functionality is still being developed, but even in the long run some operations may be beyond the physical or logical capabilities of some devices or image file formats. In these cases, the stream will approximate the specified image as best it can. +(OPENIMAGESTREAM(OPENIMAGESTREAM (Function) NIL NIL ("27") 8) FILE IMAGETYPE OPTIONS) [Function] +Opens and returns an image stream of type IMAGETYPE on a destination specified by FILE. If FILE is a file name on a normal file storage device, the image stream will store graphics commands on the specified file, which can be transmitted to a printer by explicit calls to LISTFILES and SEND.FILE.TO.PRINTER. If IMAGETYPE is DISPLAY, then the user is prompted for a window to open. FILE in this case will be used as the title of the window. +If FILE is a file name on the LPT device, this indicates that the graphics commands should be stored in a temporary file, and automatically sent to the printer when the image stream is closed by CLOSEF. FILE = NIL is equivalent to FILE = {LPT}. File names on the LPT device are of the form {LPT}PRINTERNAME.TYPE, where PRINTERNAME, TYPE, or both may be omitted. PRINTERNAME is the name of the particular printer to which the file will be transmitted on closing; it defaults to the first printer on DEFAULTPRINTINGHOST that can print IMAGETYPE files. The TYPE extension supplies the value of IMAGETYPE when it is defaulted (see below). OPENIMAGESTREAM will generate an error if the specified printer does not accept the kind of file specified by IMAGETYPE. +If IMAGETYPE is NIL, the image type is inferred from the extension field of FILE and the EXTENSIONS properties in the list PRINTFILETYPES. Thus, the extensions IP, IPR, and INTERPRESS indicate Interpress format, and the extension PRESS indicates Press format. If FILE is a printer file with no extension (of the form {LPT}PRINTERNAME), then IMAGETYPE will be the type that the indicated printer can print. If FILE has no extension but is not on the printer device {LPT}, then IMAGETYPE will default to the type accepted by the first printer on DEFAULTPRINTINGHOST. +OPTIONS is a list in property list format, (PROP1 VAL1 PROP2 VAL2 %), used to specify certain attributes of the image stream; not all attributes are meaningful or interpreted by all types of image streams. Acceptable properties are: + REGION Value is the region on the page (in stream scale units, 0,0 being the lower-left corner of the page) that text will fill up. It establishes the initial values for DSPLEFTMARGIN, DSPRIGHTMARGIN, DSPBOTTOMMARGIN (the point at which carriage returns cause page advancement) and DSPTOPMARGIN (where the stream is positioned at the beginning of a new page). + If this property is not given, the value of the variable DEFAULTPAGEREGION, is used. + FONTS Value is a list of fonts that are expected to be used in the image stream. Some image streams (e.g. Interpress) are more efficient if the expected fonts are specified in advance, but this is not necessary. The first font in this list will be the initial font of the stream, otherwise the default font for that image stream type will be used. + HEADING Value is the heading to be placed automatically on each page. NIL means no heading. + Examples: Suppose that Tremor: is an Interpress printer, Quake is a Press printer, and DEFAULTPRINTINGHOST is (Tremor: Quake): + (OPENIMAGESTREAM) returns an Interpress image stream on printer Tremor:. + (OPENIMAGESTREAM NIL 'PRESS) returns a Press stream on Quake. + (OPENIMAGESTREAM '{LPT}.INTERPRESS) returns an Interpress stream on Tremor:. + (OPENIMAGESTREAM '{CORE}FOO.PRESS) returns a Press stream on the file {CORE}FOO.PRESS. +(IMAGESTREAMP(IMAGESTREAMP (Function) NIL NIL ("27") 9) X IMAGETYPE) [NoSpread Function] +Returns X (possibly coerced to a stream) if it is an output image stream of type IMAGETYPE (or of any type if IMAGETYPE = NIL), otherwise NIL. +(IMAGESTREAMTYPE(IMAGESTREAMTYPE (Function) NIL NIL ("27") 9) STREAM) [Function] +Returns the image stream type of STREAM. +(IMAGESTREAMTYPEP(IMAGESTREAMTYPEP (Function) NIL NIL ("27") 9) STREAM TYPE) [Function] +Returns T if STREAM is an image stream of type TYPE. +Accessing Image Stream Fields +1 + +The following functions manipulate the fields of an image stream. These functions return the old value (the one being replaced). A value of NIL for the new value will return the current setting without changing it. These functions do not change any of the bits drawn on the image stream; they just affect future operations done on the image stream. +(DSPCLIPPINGREGION(DSPCLIPPINGREGION (Function) NIL NIL ("27") 10) REGION STREAM) [Function] +The clipping region is a region that limits the extent of characters printed and lines drawn (in the image stream's coordinate system). Initially set so that no clipping occurs. +Warning: For display streams, the window system maintains the clipping region during window operations. Users should be very careful about changing this field. +(DSPFONT(DSPFONT (Function) NIL NIL ("27") 10) FONT STREAM) [Function] +The font field specifies the font used when printing characters to the image stream. +Note: DSPFONT determines its new font descriptor from FONT by the same coercion rules that FONTPROP and FONTCREATE use , with one additional possibility: If FONT is a list of the form (PROP1 VAL1 PROP2 VAL2 ...) where PROP1 is acceptable as a font-property to FONTCOPY, then the new font is obtained by (FONTCOPY (DSPFONT NIL STREAM) PROP1 VAL1 PROP2 VAL2 ...). For example, (DSPFONT '(SIZE 12) STREAM) would change the font to the 12 point version of the current font, leaving all other font properties the same. +(DSPTOPMARGIN(DSPTOPMARGIN (Function) NIL NIL ("27") 10) YPOSITION STREAM) [Function] +The top margin is an integer that is the Y position after a new page (in the image stream's coordinate system). This function has no effect on windows. +(DSPBOTTOMMARGIN(DSPBOTTOMMARGIN (Function) NIL NIL ("27") 10) YPOSITION STREAM) [Function] +The bottom margin is an integer that is the minimum Y position that characters will be printed by PRIN1 (in the image stream's coordinate system). This function has no effect on windows. +(DSPLEFTMARGIN(DSPLEFTMARGIN (Function) NIL NIL ("27") 10) XPOSITION STREAM) [Function] +The left margin is an integer that is the X position after an end-of-line (in the image stream's coordinate system). Initially the left edge of the clipping region. +(DSPRIGHTMARGIN(DSPRIGHTMARGIN (Function) NIL NIL ("27") 10) XPOSITION STREAM) [Function] +The right margin is an integer that is the maximum X position that characters will be printed by PRIN1 (in the image stream's coordinate system). This is initially the position of the right edge of the window or page. +The line length of a window or image stream (as returned by LINELENGTH) is computed by dividing the distance between the left and right margins by the width of an uppercase "A" in the current font. The line length is changed whenever the font, left margin, or right margin are changed or whenever the window is reshaped. +(DSPOPERATION(DSPOPERATION (Function) NIL NIL ("27") 11) OPERATION STREAM) [Function] +The operation is the default BITBLT operation used when printing or drawing on the image stream. One of REPLACE, PAINT, INVERT, or ERASE. Initially REPLACE. This is a meaningless operation for most printers which support the model that once dots are deposited on a page they cannot be removed. +(DSPLINEFEED(DSPLINEFEED (Function) NIL NIL ("27") 11) DELTAY STREAM) [Function] +The linefeed is an integer that specifies the Y increment for each linefeed, normally negative. Initially minus the height of the initial font. +(DSPCLEOL(DSPLINEFEED (Function) NIL NIL ("27") 11) DSPSTREAM XPOS YPOS HEIGHT) [Function] +"Clear to end of line". Clears a region from (XPOS,YPOS) to the right margin of the display, with a height of HEIGHT. If XPOS and YPOS are NIL, clears the remainder of the current display line, using the height of the current font. +(DSPRUBOUTCHAR(DSPRUBOUTCHAR (Function) NIL NIL ("27") 11) DSPSTREAM CHAR X Y TTBL) [Function] +Backs up over character code CHAR in the DSPSTREAM, erasing it. If X, Y are supplied, the rubbing out starts from the position specified. DSPRUBOUTCHAR assumes CHAR was printed with the terminal table TTBL, so it knows to handle control characters, etc. TTBL defaults to the primary terminal table. + +(DSPSCALE(DSPSCALE (Function) NIL NIL ("27") 11) SCALE STREAM) [Function] +Returns the scale of the image stream STREAM, a number indicating how many units in the streams coordinate system correspond to one printer's point (1/72 of an inch). For example, DSPSCALE returns 1 for display streams, and 35.27778 for Interpress and Press streams (the number of micas per printer's point). In order to be device-independent, user graphics programs must either not specify position values absolutely, or must multiply absolute point quantities by the DSPSCALE of the destination stream. For example, to set the left margin of the Interpress stream XX to one inch, do +(DSPLEFTMARGIN (TIMES 72 (DSPSCALE NIL XX)) XX) +The SCALE argument to DSPSCALE is currently ignored. In a future release it will enable the scale of the stream to be changed under user control, so that the necessary multiplication will be done internal to the image stream interface. In this case, it would be possible to set the left margin of the Interpress stream XX to one inch by doing +(DSPSCALE 1 XX) +(DSPLEFTMARGIN 72 XX) +(DSPSPACEFACTOR FACTOR STREAM) [Function] +The space factor is the amount by which to multiply the natural width of all following space characters on STREAM; this can be used for the justification of text. The default value is 1. For example, if the natural width of a space in STREAM's current font is 12 units, and the space factor is set to two, spaces appear 24 units wide. The values returned by STRINGWIDTH and CHARWIDTH are also affected. +The following two functions only have meaning for image streams that can display color: +(DSPCOLOR COLOR STREAM) [Function] +Sets the default foreground color of STREAM. Returns the previous foreground color. If COLOR is NIL, it returns the current foreground color without changing anything. The default color is white +(DSPBACKCOLOR COLOR STREAM) [Function] +Sets the background color of STREAM. Returns the previous background color. If COLOR is NIL, it returns the current background color without changing anything. The default background color is black. +Current Position of an Image Stream +1 + +Each image stream has a "current position," which is a position (in the image stream's coordinate system) where the next printing operation will start from. The functions which print characters or draw on an image stream update these values appropriately. The following functions are used to explicitly access the current position of an image stream: +(DSPXPOSITION XPOSITION STREAM) [Function] +Returns the X coordinate of the current position of STREAM. If XPOSITION is non-NIL, the X coordinate is set to it (without changing the Y coordinate). +(DSPYPOSITION YPOSITION STREAM) [Function] +Returns the Y coordinate of the current position of STREAM. If YPOSITION is non-NIL, the Y coordinate is set to it (without changing the X coordinate). +(MOVETO X Y STREAM) [Function] +Changes the current position of STREAM to the point (X,Y). +(RELMOVETO DX DY STREAM) [Function] +Changes the current position to the point (DX,DY) coordinates away from current position of STREAM. +(MOVETOUPPERLEFT STREAM REGION) [Function] +Moves the current position to the beginning position of the top line of text. If REGION is non-NIL, it must be a REGION and the X position is changed to the left edge of REGION and the Y position changed to the top of REGION less the font ascent of STREAM. If REGION is NIL, the X coordinate is changed to the left margin of STREAM and the Y coordinate is changed to the top of the clipping region of STREAM less the font ascent of STREAM. +Moving Bits Between Bitmaps With BITBLT +1 + +BITBLT is the primitive function for moving bits from one bitmap to another, or from a bitmap to an image stream. +(BITBLT SOURCE SOURCELEFT SOURCEBOTTOM DESTINATION DESTINATIONLEFT DESTINATIONBOTTOM WIDTH HEIGHT SOURCETYPE OPERATION TEXTURE CLIPPINGREGION) [Function] +Transfers a rectangular array of bits from SOURCE to DESTINATION. SOURCE can be a bitmap, or a display stream or window, in which case its associated bitmap is used. DESTINATION can be a bitmap or an arbitrary image stream. +WIDTH and HEIGHT define a pair of rectangles, one in each of the SOURCE and DESTINATION whose left, bottom corners are at, respectively, (SOURCELEFT, SOURCEBOTTOM) and (DESTINATIONLEFT, DESTINATIONBOTTOM). If these rectangles overlap the boundaries of either source or destination they are both reduced in size (without translation) so that they fit within their respective boundaries. If CLIPPINGREGION is non-NIL it should be a REGION and is interpreted as a clipping region within DESTINATION; clipping to this region may further reduce the defining rectangles. These (possibly reduced) rectangles define the source and destination rectangles for BITBLT. +The mode of transferring bits is defined by SOURCETYPE and OPERATION. SOURCETYPE and OPERATION specify whether the source bits should come from SOURCE or TEXTURE, and how these bits are combined with those of DESTINATION. SOURCETYPE and OPERATION are described further below. +TEXTURE is a texture. BITBLT aligns the texture so that the upper-left pixel of the texture coincides with the upper-left pixel of the destination bitmap. +SOURCELEFT, SOURCEBOTTOM, DESTINATIONLEFT, and DESTINATIONBOTTOM default to 0. WIDTH and HEIGHT default to the width and height of the SOURCE. TEXTURE defaults to white. SOURCETYPE defaults to INPUT. OPERATION defaults to REPLACE. If CLIPPINGREGION is not provided, no additional clipping is done. BITBLT returns T if any bits were moved; NIL otherwise. +Note: If SOURCE or DESTINATION is a window or image stream, the remaining arguments are interpreted as values in the coordinate system of the window or image stream and the operation of BITBLT is translated and clipped accordingly. Also, if a window or image stream is used as the destination to BITBLT, its clipping region further limits the region involved. +SOURCETYPE specifies whether the source bits should come from the bitmap SOURCE, or from the texture TEXTURE. SOURCETYPE is interpreted as follows: + INPUT The source bits come from SOURCE. TEXTURE is ignored. + INVERT The source bits are the inverse of the bits from SOURCE. TEXTURE is ignored. + TEXTURE The source bits come from TEXTURE. SOURCE, SOURCELEFT, and SOURCEBOTTOM are ignored. + OPERATION specifies how the source bits (as specified by SOURCETYPE) are combined with the bits in DESTINATION and stored back into DESTINATION. DESTINATION is one of the following: + REPLACE All source bits (on or off) replace destination bits. + PAINT Any source bits that are on replace the corresponding destination bits. Source bits that are off have no effect. Does a logical OR between the source bits and the destination bits. + INVERT Any source bits that are on invert the corresponding destination bits. Does a logical XOR between the source bits and the destination bits. + ERASE Any source bits that are on erase the corresponding destination bits. Does a logical AND operation between the inverse of the source bits and the destination bits. + Different combinations of SOURCETYPE and OPERATION can be specified to achieve many different effects. Given the following bitmaps as the values of SOURCE, TEXTURE, and DESTINATION: + <F??~~??<Ǿal0ql09>l0 l0'0.5 0 0 ;FDDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" DDDDDDD@fffffff`""""""" 3 f3f3f3f3 f3<0.5 0 0 PZxbgl6gllfÃ7glnf7mlnf6lmlc6lmx6xg0.5 0 0 + BITBLT would produce the results given below for the difference combinations of SOURCETYPE and OPERATION (assuming CLIPPINGREGION, SOURCELEFT, etc. are set correctly, of course): +Pf???~~~~~~?~~?~~?~~??????????????????????????????7`6ٌ8ٌflٌg|wc`v3<6 y mq9mqmyهmmmm9e0.5 0 0 Pf~~~?~?~?~??????????????????????????????7`6ٌ8ٌflٌg|wc`v3<6 xCflf09lv0mmv0myn0mcn09cf00.5 0 0 Pf~~~?~?~?~???????????????????????????????7`6ٌ8ٌflٌg|wc`v3<6 ll9n0mn>mm0mm09l>0.5 0 0 Pf?7`6ٌ8ٌflٌg|wc`v3<6 }a09a0m}>maðma9}>0.5 0 0 +Pf??mm0mm09l>0.5 0 0 Pf???~~~~~~maðma9}>0.5 0 0 +PfDDDDDDDffffffd"""""" DDDDDDDffffffd"""""" DDDDDDDffffffd"""""" DDDDDDDffffffd"""""" DDDDDDDffffffd"""""" DDDDDDDffffffd"""""" DDDDDDDffffffd"""""" DDDDDDDffffffd"""""""#DDDDDDG&ffffffg"""""""#DDDDDDG&ffffffg"""""""#DDDDDDG&ffffffg"""""""#DDDDDDG&ffffffg"""""""#DDDDDDG&ffffffg"""""""#DDDDDDG&ffffffg"""""""#DDDDDDG&ffffffg"""""""#}|`a`8``fl|`|g|``c`a`3<}Ǚ|y mq9mqmyهmmmm9e0.5 0 0 PfDDDDfffd""" DDDDfffd""" DDDDfffd""" DDDDfffd""" DDDDfffd""" DDDDfffd""" DDDDfffd""" DDDDfffd""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""}|`a`8``fl|`|g|``c`a`3<}Ǚ|xCflf09lv0mmv0myn0mcn09cf00.5 0 0 PfDDDDٙfffd""" DDDDٙfffd""" DDDDٙfffd""" DDDDٙfffd""" DDDDٙfffd""" DDDDٙfffd""" DDDDٙfffd""" DDDDٙfffd""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""DDD&fff""""}|`a`8``fl|`|g|``c`a`3<}Ǚ|ll9n0mn>mm0mm09l>0.5 0 0 Pfٙٙٙٙٙٙٙٙ}|`a`8``fl|`|g|``c`a`3<}Ǚ|}a09a0m}>maðma9}>0.5 0 0 +(BLTSHADE TEXTURE DESTINATION DESTINATIONLEFT DESTINATIONBOTTOM WIDTH HEIGHT OPERATION CLIPPINGREGION) [Function] +BLTSHADE is the SOURCETYPE = TEXTURE case of BITBLT. It fills the specified region of the destination bitmap DESTINATION with the texture TEXTURE. DESTINATION can be a bitmap or image stream. +(BITMAPIMAGESIZE BITMAP DIMENSION STREAM) [Function] +Returns the size that BITMAP will be when BITBLTed to STREAM, in STREAM's units. DIMENSION can be one of WIDTH, HEIGHT, or NIL, in which case the dotted pair (WIDTH . HEIGHT) will be returned. +Drawing Lines +1 + +Interlisp-D provides several functions for drawing lines and curves on image streams. The line drawing functions are intended for interactive applications where efficiency is important. They do not allow the use of "brush" patterns, like the curve drawing functions, but (for display streams) they support drawing a line in INVERT mode, so redrawing the line will erase it. DRAWCURVE can be used to draw lines using a brush. +(DRAWLINE X1 Y1 X2 Y2 WIDTH OPERATION STREAM COLOR DASHING) [Function] +Draws a straight line from the point (X1,Y1) to the point (X2,Y2) on the image stream STREAM. The position of STREAM is set to (X2,Y2). If X1 equals X2 and Y1 equals Y2, a point is drawn at (X1,Y1). +WIDTH is the width of the line, in the units of the device. If WIDTH is NIL, the default is 1. +OPERATION is the BITBLT operation used to draw the line. If OPERATION is NIL, the value of DSPOPERATION for the image stream is used. +COLOR is a color specification that determines the color used to draw the line for image streams that support color. If COLOR is NIL, the DSPCOLOR of STREAM is used. +DASHING is a list of positive integers that determines the dashing characteristics of the line. The line is drawn for the number of points indicated by the first element of the dashing list, is not drawn for the number of points indicated by the second element. The third element indicates how long it will be on again, and so forth. The dashing sequence is repeated from the beginning when the list is exhausted. A brush LINEWITHBRUSH-by-LINEWITHBRUSH is used. + If DASHING is NIL, the line is not dashed. +(DRAWBETWEEN POSITION1 POSITION2 WIDTH OPERATION STREAM COLOR DASHING) [Function] +Draws a line from the point POSITION1 to the point POSITION2 onto the destination bitmap of STREAM. The position of STREAM is set to POSITION2. +In the Medley release, when using the color argument, Interpress (DRAWLINE (Function) NIL NIL ("27") 16)DRAWLINE treats 16x16 bitmaps or negative numbers as shades/textures. Positive numbers continue to refer to color maps, and so cannot be used as textures. To convert an integer shade into a negative number use NEGSHADE (e.g. (NEGSHADE 42495) is -23041). + +(DRAWTO X Y WIDTH OPERATION STREAM COLOR DASHING) [Function] +Draws a line from the current position to the point (X,Y) onto the destination bitmap of STREAM. The position of STREAM is set to (X,Y). +(RELDRAWTO DX DY WIDTH OPERATION STREAM COLOR DASHING) [Function] +Draws a line from the current position to the point (DX,DY) coordinates away onto the destination bitmap of STREAM. The position of STREAM is set to the end of the line. If DX and DY are both 0, nothing is drawn. +Drawing Curves +1 + +A curve is drawn by placing a brush pattern centered at each point along the curve's trajectory. A brush pattern is defined by its shape, size, and color. The predefined brush shapes are ROUND, SQUARE, HORIZONTAL, VERTICAL, and DIAGONAL; new brush shapes can be created using the INSTALLBRUSH function, described below. A brush size is an integer specifying the width of the brush in the units of the device. The color is a color specification, which is only used if the curve is drawn to an image stream that supports colors. +A brush is specified to the various drawing functions as a list of the form (SHAPE WIDTH COLOR), for example (SQUARE 2) or (VERTICAL 4 RED). A brush can also be specified as a positive integer, which is interpreted as a ROUND brush of that width. If a brush is a litatom, it is assumed to be a function which is called at each point of the curve's trajectory (with three arguments: the X-coordinate of the point, the Y-coordinate, and the image stream), and should do whatever image stream operations are necessary to draw each point. Finally, if a brush is specified as NIL, a (ROUND 1) brush is used as default. +The appearance of a curve is also determined by its dashing characteristics. Dashing is specified by a list of positive integers. If a curve is dashed, the brush is placed along the trajectory for the number of units indicated by the first element of the dashing list. The brush is off, not placed in the bitmap, for a number of units indicated by the second element. The third element indicates how long it will be on again, and so forth. The dashing sequence is repeated from the beginning when the list is exhausted. The units used to measure dashing are the units of the brush. For example, specifying the dashing as (1 1) with a brush of (ROUND 16) would put the brush on the trajectory, skip 16 points, and put down another brush. A curve is not dashed if the dashing argument to the drawing function is NIL. +The curve functions use the image stream's clipping region and operation. Most types of image streams only support the PAINT operation when drawing curves. When drawing to a display stream, the curve-drawing functions accept the operation INVERT if the brush argument is 1. For brushes larger than 1, these functions will use the ERASE operation instead of INVERT. For display streams, the curve-drawing functions treat the REPLACE operation the same as PAINT. +(DRAWCURVE KNOTS CLOSED BRUSH DASHING STREAM) [Function] +Draws a "parametric cubic spline curve" on the image stream STREAM. KNOTS is a list of positions to which the curve will be fitted. If CLOSED is non-NIL, the curve will be closed; otherwise it ends at the first and last positions in KNOTS. BRUSH and DASHING are interpreted as described above. +For example, +(DRAWCURVE '((10 . 10)(50 . 50)(100 . 10)(150 . 50)) + NIL '(ROUND 5) '(1 1 1 2) XX) +would draw a curve like the following on the display stream XX: +;  > > >                 p p p p       > > > q  p   0.75 0 0 +(DRAWCIRCLE CENTERX CENTERY RADIUS BRUSH DASHING + STREAM) [Function] +Draws a circle of radius RADIUS about the point (CENTERX,CENTERY) onto the image stream STREAM. STREAM's position is left at (CENTERX,CENTERY). The other arguments are interpreted as described above. +(DRAWARC CENTERX CENTERY RADIUS STARTANGLE NDEGREES BRUSH DASHINGSTREAM) [Function] +Draws an arc of the circle whose center point is (CENTERX CENTERY) and whose radius is RADIUS from the position at STARTANGLE degrees for NDEGREES number of degrees. If STARTANGLE is 0, the starting point will be (CENTERX (CENTERY + RADIUS)). If NDEGREES is positive, the arc will be counterclockwise. If NDEGREES is negative, the arc will be clockwise. The other arguments are interpreted as described in DRAWCIRCLE. + +(DRAWELLIPSE CENTERX CENTERY SEMIMINORRADIUS SEMIMAJORRADIUS ORIENTATION BRUSH DASHING STREAM) [Function] +Draws an ellipse with a minor radius of SEMIMINORRADIUS and a major radius of SEMIMAJORRADIUS about the point (CENTERX,CENTERY) onto the image stream STREAM. ORIENTATION is the angle of the major axis in degrees, positive in the counterclockwise direction. STREAM's position is left at (CENTERX,CENTERY). The other arguments are interpreted as described above. +New brush shapes can be defined using the following function: +(INSTALLBRUSH BRUSHNAME BRUSHFN BRUSHARRAY) [Function] +Installs a new brush called BRUSHNAME with creation-function BRUSHFN and optional array BRUSHARRAY. BRUSHFN should be a function of one argument (a width), which returns a bitmap of the brush for that width. BRUSHFN will be called to create new instances of BRUSHNAME-type brushes; the sixteen smallest instances will be pre-computed and cached. "Hand-crafted" brushes can be supplied as the BRUSHARRAY argument. Changing an existing brush can be done by calling INSTALLBRUSH with new BRUSHFN and/or BRUSHARRAY. +(DRAWPOINT X Y BRUSH STREAM OPERATION) [Function] +Draws BRUSH centered around point (X, Y) on STREAM, using the operation OPERATION. BRUSH may be a bitmap or a brush. +Miscellaneous Drawing and Printing Operations +1 + +(DSPFILL REGION TEXTURE OPERATION STREAM) [Function] +Fills REGION of the image stream STREAM (within the clipping region) with the texture TEXTURE. If REGION is NIL, the whole clipping region of STREAM is used. If TEXTURE or OPERATION is NIL, the values for STREAM are used. +(DRAWPOLYGON(DRAWPOLYGON (Function) NIL NIL ("27") 18) POINTS CLOSED BRUSH DASHING STREAM) [Function] +Draws a polygon on the image stream STREAM. POINTS is a list of positions to which the figure will be fitted (the vertices of the polygon). If CLOSED is non-NIL, then the starting position is specified only once in POINTS. If CLOSED is NIL, then the starting vertex must be specified twice in POINTS. BRUSH and DASHING are interpreted as described in Chapter 27 of the Interlisp-D Reference Manual. +For example, +(DRAWPOLYGON '((100 . 100) (50 . 125) + (150 . 175) (200 . 100) (150 . 50)) + T '(ROUND 3) '(4 2) XX) +will draw a polygon like the following on the display stream XX. +p88>>8>@~p~8~@pπp8~~>q19qpA8 p8pp@8 p8pp@8 p@0.75 0 0 +(FILLPOLYGON POINTS TEXTURE OPERATION WINDNUMBER STREAM) [Function] +OPERATION is the BITBLT operation (see page 27.15 in the Interlisp-D Reference Manual) used to fill the polygon. If the OPERATION is NIL, the OPERATION defaults to the STREAM default OPERATION. +WINDNUMBER is the number for the winding rule convention . This number is either 0 or 1; 0 indicates the "zero" winding rule, 1 indicates the "odd" winding rule. +When filling a polygon, there is more than one way of dealing with the situation where two polygon sides intersect, or one polygon is fully inside the other. Currently, FILLPOLYGON to a display stream uses the "odd" winding rule, which means that intersecting polygon sides define areas that are filled or not filled somewhat like a checkerboard. For example, +(FILLPOLYGON + '( ((110 . 110)(150 . 200)(190 . 110)) + ((135 . 125)(160 . 125)(160 . 150)(135 . 150)) ) + GRAYSHADE WINDOW) +will produce a display something like this: +/E@@@ PP**PUT**UTUT*UTUTTUP * @@ -11,291 +287,606 @@ INTERLISP-D REFERENCE MANUAL GRAPHICS OUTPUT OPERATIONS "27"26. GRAPHICS OUT -@PP**PP  @ This fill convention also takes into account all polygons in POINTS, if it specifies multiple polygons. (FILLCIRCLE CENTERX CENTERY RADIUS TEXTURE STREAM) [Function] Fills in a circular area of radius RADIUS about the point (CENTERX,CENTERY) in STREAM with TEXTURE. STREAM's position is left at (CENTERX,CENTERY). (DSPRESET STREAM) [Function] Sets the X coordinate of STREAM to its left margin, sets its Y coordinate to the top of the clipping region minus the font ascent. For a display stream, this also fills its destination bitmap with its background texture. (DSPNEWPAGE STREAM) [Function] Starts a new page. The X coordinate is set to the left margin, and the Y coordinate is set to the top margin plus the linefeed. (CENTERPRINTINREGION EXP REGION STREAM) [Function] Prints EXP so that is it centered within REGION of the STREAM. If REGION is NIL, EXP will be centered in the clipping region of STREAM. Drawing and Shading Grids 1 A grid is a partitioning of an arbitrary coordinate system (hereafter referred to as the "source system") into rectangles. This section describes functions that operate on grids. It includes functions to draw the outline of a grid, to translate between positions in a source system and grid coordinates (the coordinates of the rectangle which contains a given position), and to shade grid rectangles. A grid is defined by its "unit grid," a region (called a grid specification) which is the origin rectangle of the grid in terms of the source system. Its LEFT field is interpreted as the X-coordinate of the left edge of the origin rectangle, its BOTTOM field is the Y-coordinate of the bottom edge of the origin rectangle, its WIDTH is the width of the grid rectangles, and its HEIGHT is the height of the grid rectangles. (GRID GRIDSPEC WIDTH HEIGHT BORDER STREAM GRIDSHADE) [Function] Outlines the grid defined by GRIDSPEC which is WIDTH rectangles wide and HEIGHT rectangles high on STREAM. Each box in the grid has a border within it that is BORDER points on each side; so the resulting lines in the grid are 2*BORDER thick. If BORDER is the atom POINT, instead of a border the lower left point of each grid rectangle will be turned on. If GRIDSHADE is non-NIL, it should be a texture and the border lines will be drawn using that texture. (SHADEGRIDBOX X Y SHADE OPERATION GRIDSPEC GRIDBORDER STREAM) [Function] Shades the grid rectangle (X,Y) of GRIDSPEC with texture SHADE using OPERATION on STREAM. GRIDBORDER is interpreted the same as for GRID. The following two functions map from the X,Y coordinates of the source system into the grid X,Y coordinates: (GRIDXCOORD XCOORD GRIDSPEC) [Function] Returns the grid X-coordinate (in the grid specified by GRIDSPEC) that contains the source system X-coordinate XCOORD. (GRIDYCOORD YCOORD GRIDSPEC) [Function] Returns the grid Y-coordinate (in the grid specified by GRIDSPEC) that contains the source system Y-coordinate YCOORD. The following two functions map from the grid X,Y coordinates into the X,Y coordinates of the source system: (LEFTOFGRIDCOORD GRIDX GRIDSPEC) [Function] Returns the source system X-coordinate of the left edge of a grid rectangle at grid X-coordinate GRIDX (in the grid specified by GRIDSPEC). (BOTTOMOFGRIDCOORD GRIDY GRIDSPEC) [Function] Returns the source system Y-coordinate of the bottom edge of a grid rectangle at grid Y-coordinate GRIDY (in the grid specified by GRIDSPEC). Display Streams 1 Display streams (image streams of type DISPLAY) are used to control graphic output operations to a bitmap, known as the "destination" bitmap of the display stream. For each window on the screen, there is an associated display stream which controls graphics operations to a specific part of the screen bitmap. Any of the functions that take a display stream will also take a window, and use the associated display stream. Display streams can also have a destination bitmap that is not connected to any window or display device. (DSPCREATE DESTINATION) [Function] Creates and returns a display stream. If DESTINATION is specified, it is used as the destination bitmap, otherwise the screen bitmap is used. (DSPDESTINATION DESTINATION DISPLAYSTREAM) [Function] Returns the current destination bitmap for DISPLAYSTREAM, setting it to DESTINATION if non-NIL. DESTINATION can be either the screen bitmap, or an auxilliary bitmap in order to construct figures, possibly save them, and then display them in a single operation. Warning: The window system maintains the destination of a window's display stream. Users should be very careful about changing this field. (DSPXOFFSET XOFFSET DISPLAYSTREAM) [Function] (DSPYOFFSET YOFFSET DISPLAYSTREAM) [Function] Each display stream has its own coordinate system, separate from the coordinate system of its destination bitmap. Having the coordinate system local to the display stream allows objects to be displayed at different places by translating the display stream's coordinate system relative to its destination bitmap. This local coordinate system is defined by the X offset and Y offset. DSPXOFFSET returns the current X offset for DISPLAYSTREAM, the X origin of the display stream's coordinate system in the destination bitmap's coordinate system. It is set to XOFFSET if non-NIL. DSPYOFFSET returns the current Y offset for DISPLAYSTREAM, the Y origin of the display stream's coordinate system in the destination bitmap's coordinate system. It is set to YOFFSET if non-NIL. The X offset and Y offset for a display stream are both initially 0 (no X or Y-coordinate translation). Warning: The window system maintains the X and Y offset of a window's display stream. Users should be very careful about changing these fields. (DSPTEXTURE TEXTURE DISPLAYSTREAM) [Function] Returns the current texture used as the background pattern for DISPLAYSTREAM. It is set to TEXTURE if non-NIL. Initially the value of WHITESHADE. (DSPSOURCETYPE SOURCETYPE DISPLAYSTREAM) [Function] Returns the current BITBLT sourcetype used when printing characters to the display stream. It is set to SOURCETYPE, if non-NIL. Must be either INPUT or INVERT. Initially INPUT. (DSPSCROLL SWITCHSETTING DISPLAYSTREAM) [Function] Returns the current value of the "scroll flag," a flag that determines the scrolling behavior of the display stream; either ON or OFF. If ON, the bits in the display streams's destination bitmap are moved after any linefeed that moves the current position out of the destination bitmap. Any bits moved out of the current clipping region are lost. Does not adjust the X offset, Y offset, or clipping region of the display stream. Initially OFF. Sets the scroll flag to SWITCHSETTING, if non-NIL. Note: The word "scrolling" also describes the use of "scroll bars" on the left and bottom of a window to move an object displayed in a window. Each window has an associated display stream. To get the window of a particular display stream, use WFROMDS: (WFROMDS DISPLAYSTREAM DONTCREATE) [Function] Returns the window associated with DISPLAYSTREAM, creating a window if one does not exist (and DONTCREATE is NIL). Returns NIL if the destination of DISPLAYSTREAM is not a screen bitmap that supports a window system. If DONTCREATE is non-NIL, WFROMDS will never create a window, and returns NIL if DISPLAYSTREAM does not have an associated window. TTYDISPLAYSTREAM calls WFROMDS with DONTCREATE = T, so it will not create a window unnecessarily. Also, if WFROMDS does create a window, it calls CREATEW with NOOPENFLG = T. (DSPBACKUP WIDTH DISPLAYSTREAM) [Function] Backs up DISPLAYSTREAM over a character which is WIDTH screen points wide. DSPBACKUP fills the backed over area with the display stream's background texture and decreases the X position by WIDTH. If this would put the X position less than DISPLAYSTREAM's left margin, its operation is stopped at the left margin. It returns T if any bits were written, NIL otherwise. Fonts 1 A font is the collection of images that are printed or displayed when characters are output to a graphic output device. Some simple displays and printers can only print characters using one font. Bitmap displays and graphic printers can print characters using a large number of fonts. Fonts are identified by a distinctive style or family (such as Modern or Classic), a size (such as 10 points), and a face (such as bold or italic). Fonts also have a rotation that indicates the orientation of characters on the screen or page. A normal horizontal font (also called a portrait font) has a rotation of 0; the rotation of a vertical (landscape) font is 90 degrees. While any combination can be specified, in practice the user will find that only certain combinations of families, sizes, faces, and rotations are available for any graphic output device. To specify a font to the functions described below, a FAMILY is represented by a literal atom, a SIZE by a positive integer, and a FACE by a three-element list of the form (WEIGHT SLOPE EXPANSION). WEIGHT, which indicates the thickness of the characters, can be BOLD, MEDIUM, or LIGHT; SLOPE can be ITALIC or REGULAR; and EXPANSION can be REGULAR, COMPRESSED, or EXPANDED, indicating how spread out the characters are. For convenience, faces may also be specified by three-character atoms, where each character is the first letter of the corresponding field. Thus, MRR is a synonym for (MEDIUM REGULAR REGULAR). In addition, certain common face combinations may be indicated by special literal atoms: STANDARD = (MEDIUM REGULAR REGULAR) = MRR ITALIC = (MEDIUM ITALIC REGULAR) = MIR BOLD = (BOLD REGULAR REGULAR) = BRR BOLDITALIC = (BOLD ITALIC REGULAR) = BIR Interlisp represents all the information related to a font in an object called a font descriptor. Font descriptors contain the family, size, etc. properties used to represent the font. In addition, for each character in the font, the font descriptor contains width information for the character and (for display fonts) a bitmap containing the picture of the character. The font functions can take fonts specified in a variety of different ways. DSPFONT, FONTCREATE, FONTCOPY, etc. can be applied to font descriptors, "font lists" such as '(MODERN 10), image streams (coerced to its current font), or windows (coerced to the current font of its display stream). The printout command ".FONT" will also accept fonts specified in any of these forms. In general font files use the following format: The family name (e.g., Modern); a two digit size (e.g., 08); a three letter Face (e.g., BIR, for Bold Italic Regular); the letter C followed by the font's character set in base 8 (e.g., C41); and finally an extension (e.g., Displayfont). ((SKETCH a% figure% from% a% document VERSION 3 PRIRANGE (19 . 0) SKETCHCONTEXT ((ROUND 1 BLACK) (GACHA 10 (MEDIUM REGULAR REGULAR)) (CENTER BASELINE) (CURVE 18.0 8) NIL NIL (CENTER CENTER) (NIL NIL NIL) T NIL NIL 1 NIL)) ((0.05 13.0 (PRI 3)) (TEXT (88.0 . 120.0) ("Family") 1 (CENTER BASELINE) (MODERN 10 (MEDIUM REGULAR REGULAR)) ((72.5 116.5 31 13)) BLACK)) ((0.05 13.0 (PRI 4)) (TEXT (112.0 . 176.0) ("Size") 1 (CENTER BASELINE) (MODERN 10 (MEDIUM REGULAR REGULAR)) ((102.5 172.5 19 13)) BLACK)) ((0.05 13.0 (PRI 5)) (TEXT (132.0 . 120.0) ("Face") 1 (CENTER BASELINE) (MODERN 10 (MEDIUM REGULAR REGULAR)) ((121.5 116.5 21 13)) BLACK)) ((0.05 13.0 (PRI 7)) (TEXT (152.0 . 144.0) ("Modern08-BIR-C41.Displayfont") 1 (CENTER BASELINE) (TERMINAL 10 (MEDIUM REGULAR REGULAR)) ((68.0 140.5 168 13)) BLACK)) ((0.05 13.0 (PRI 8)) (TEXT (192.0 . 168.0) ("CharacterSet (base 8)") 1 (CENTER BASELINE) (MODERN 10 (MEDIUM REGULAR REGULAR)) ((142.5 164.5 99 13)) BLACK)) ((0.05 13.0 (PRI 9)) (TEXT (208.0 . 120.0) ("Extension") 1 (CENTER BASELINE) (MODERN 10 (MEDIUM REGULAR REGULAR)) ((185.0 116.5 46 13)) BLACK)) ((0.0 6.0 (PRI 11)) (WIRE ((88 . 128) (88 . 140)) (ROUND 1 BLACK) (NIL (SOLID 18.0 8)) NIL 1 NIL (NIL ((88 . 140) (90.47214 . 132.3916) (85.52786 . 132.3916))))) ((0.0 6.0 (PRI 13)) (WIRE ((208 . 128) (208 . 140)) (ROUND 1 BLACK) (NIL (SOLID 18.0 8)) NIL 1 NIL (NIL ((208 . 140) (210.4721 . 132.3916) (205.5279 . 132.3916))))) ((0.0 6.0 (PRI 14)) (WIRE ((156 . 164) (156 . 152)) (ROUND 1 BLACK) (NIL (SOLID 18.0 8)) NIL 1 NIL (NIL ((156 . 152) (153.5279 . 159.6085) (158.4721 . 159.6085))))) ((0.0 6.0 (PRI 15)) (WIRE ((112 . 164) (112 . 152)) (ROUND 1 BLACK) (NIL (SOLID 18.0 8)) NIL 1 NIL (NIL ((112 . 152) (109.5279 . 159.6085) (114.4721 . 159.6085))))) ((0.05 11.0 (PRI 16)) (TEXT (112.0 . 168.0) ("(two digits)") 1 (CENTER BASELINE) (MODERN 8 (MEDIUM REGULAR REGULAR)) ((90.5 165.5 43 11)) BLACK)) ((0.0 10.0 (PRI 18)) (WIRE ((120 . 144) (120 . 136) (140 . 136) (140 . 144)) (ROUND 1 BLACK) NIL NIL 1 NIL NIL)) ((0.0 6.0 (PRI 19)) (WIRE ((132 . 124) (132 . 136)) (ROUND 1 BLACK) NIL NIL 1 NIL NIL))) (68.0 116.0 174.0 69.0) 1.0 4 (FONTCREATE FAMILY SIZE FACE ROTATION DEVICE NOERRORFLG CHARSET) [Function] Returns a font descriptor for the specified font. FAMILY is a litatom specifying the font family. SIZE is an integer indicating the size of the font in points. FACE specifies the face characteristics in one of the formats listed above; if FACE is NIL, STANDARD is used. ROTATION, which specifies the orientation of the font, is 0 (or NIL) for a portrait font and 90 for a landscape font. DEVICE indicates the output device for the font, and can be any image stream type , such as DISPLAY, INTERPRESS, etc. DEVICE may also be an image stream, in which case the type of the stream determines the font device. DEVICE defaults to DISPLAY. The FAMILY argument to FONTCREATE may also be a list, in which case it is interpreted as a font-specification quintuple, a list of the form (FAMILY SIZE FACE ROTATION DEVICE). Thus, (FONTCREATE '(GACHA 10 BOLD)) is equivalent to (FONTCREATE 'GACHA 10 'BOLD). FAMILY may also be a font descriptor, in which case that descriptor is simply returned. If a font descriptor has already been created for the specified font, FONTCREATE simply returns it. If it has not been created, FONTCREATE has to read the font information from a font file that contains the information for that font. The name of an appropriate font file, and the algorithm for searching depends on the device that the font is for, and is described in more detail below. If an appropriate font file is found, it is read into a font descriptor. If no file is found, for DISPLAY fonts FONTCREATE looks for fonts with less face information and fakes the remaining faces (such as by doubling the bit pattern of each character or slanting it). For hardcopy printer fonts, there is no acceptable faking algorithm. If no acceptable font is found, the action of FONTCREATE is determined by NOERRORFLG. If NOERRORFLG is NIL, it generates a FONT NOT FOUND error with the offending font specification; otherwise, FONTCREATE returns NIL. CHARSET is the character set which will be read to create the font. Defaults to 0. For more information on character sets, see NS Characters. (FONTP X) [Function] Returns X if X is a font descriptor; NIL otherwise. (FONTPROP FONT PROP) [Function] Returns the value of the PROP property of font FONT. The following font properties are recognized: FAMILY The style of the font, represented as a literal atom, such as CLASSIC or MODERN. SIZE A positive integer giving the size of the font, in printer's points (1/72 of an inch). WEIGHT The thickness of the characters; one of BOLD, MEDIUM, or LIGHT. SLOPE The "slope" of the characters in the font; one of ITALIC or REGULAR. EXPANSION The extent to which the characters in the font are spread out; one of REGULAR, COMPRESSED, or EXPANDED. Most available fonts have EXPANSION = REGULAR. FACE A three-element list of the form (WEIGHT SLOPE EXPANSION), giving all of the typeface parameters. ROTATION An integer that gives the orientation of the font characters on the screen or page, in degrees. A normal horizontal font (also called a portrait font) has a rotation of 0; the rotation of a vertical (landscape) font is 90. DEVICE The device that the font can be printed on; one of DISPLAY, INTERPRESS, etc. ASCENT An integer giving the maximum height of any character in the font from its base line (the printing position). The top line will be at BASELINE+ASCENT-1. DESCENT An integer giving the maximum extent of any character below the base line, such as the lower part of a "p". The bottom line of a character will be at BASELINE-DESCENT. HEIGHT Equal to ASCENT + DESCENT. SPEC The (FAMILY SIZE FACE ROTATION DEVICE) quintuple by which the font is known to Lisp. DEVICESPEC The (FAMILY SIZE FACE ROTATION DEVICE) quintuple that identifies what will be used to represent the font on the display or printer. It will differ from the SPEC property only if an implicit coercion is done to approximate the specified font with one that actually exists on the device. SCALE The units per printer's point (1/72 of an inch) in which the font is measured. For example, this is 35.27778 (the number of micas per printer's point) for Interpress fonts, which are measured in terms of micas. (FONTCOPY OLDFONT PROP1 VAL1 PROP2 VAL2 ...) [NoSpread Function] Returns a font descriptor that is a copy of the font OLDFONT, but which differs from OLDFONT in that OLDFONT's properties are replaced by the specified properties and values. Thus, (FONTCOPY FONT 'WEIGHT 'BOLD 'DEVICE 'INTERPRESS) will return a bold Interpress font with all other properties the same as those of FONT. FONTCOPY accepts the properties FAMILY, SIZE, WEIGHT, SLOPE, EXPANSION, FACE, ROTATION, and DEVICE. If the first property is a list, it is taken to be the PROP1 VAL1 PROP2 VAL2 ... sequence. Thus, (FONTCOPY FONT '(WEIGHT BOLD DEVICE INTERPRESS)) is equivalent to the example above. If the property NOERROR is specified with value non-NIL, FONTCOPY will return NIL rather than causing an error if the specified font cannot be created. (FONTSAVAILABLE FAMILY SIZE FACE ROTATION DEVICE CHECKFILESTOO?) [Function] Returns a list of available fonts that match the given specification. FAMILY, SIZE, FACE, ROTATION, and DEVICE are the same as for FONTCREATE. Additionally, any of them can be the atom *, in which case all values of that field are matched. If CHECKFILESTOO? is NIL, only fonts already loaded into virtual memory will be considered. If CHECKFILESTOO? is non-NIL, the font directories for the specified device will be searched. When checking font files, the ROTATION is ignored. Note: The search is conditional on the status of the server which holds the font. Thus a file server crash may prevent FONTCREATE from finding a file that an earlier FONTSAVAILABLE returned. Each element of the list returned will be of the form (FAMILY SIZE FACE ROTATION DEVICE). Examples: (FONTSAVAILABLE 'MODERN 10 'MRR 0 'DISPLAY) will return ((MODERN 10 (MEDIUM REGULAR REGULAR) 0 DISPLAY)) if the regular Modern 10 font for the display is in virtual memory; NIL otherwise. (FONTSAVAILABLE '* 14 '* '* 'INTERPRESS T) will return a list of all the size 14 Interpress fonts, whether they are in virtual memory or in font files. (SETFONTDESCRIPTOR FAMILY SIZE FACE ROTATION DEVICE FONT) [Function] Indicates to the system that FONT is the font that should be associated with the FAMILY SIZE FACE ROTATION DEVICE characteristics. If FONT is NIL, the font associated with these characteristics is cleared and will be recreated the next time it is needed. As with FONTPROP and FONTCOPY, FONT is coerced to a font descriptor if it is not one already. This functions is useful when it is desirable to simulate an unavailable font or to use a font with characteristics different from the interpretations provided by the system. (DEFAULTFONT DEVICE FONT %) [Function] Returns the font that would be used as the default (if NIL were specified as a font argument) for image stream type DEVICE. If FONT is a font descriptor, it is set to be the default font for DEVICE. (CHARWIDTH CHARCODE FONT) [Function] CHARCODE is an integer that represents a valid character (as returned by CHCON1). Returns the amount by which an image stream's X-position will be incremented when the character is printed. (CHARWIDTHY CHARCODE FONT) [Function] Like CHARWIDTH, but returns the Y component of the character's width, the amount by which an image stream's Y-position will be incremented when the character is printed. This will be zero for most characters in normal portrait fonts, but may be non-zero for landscape fonts or for vector-drawing fonts. (STRINGWIDTH STR FONT FLG RDTBL) [Function] Returns the amount by which a stream's X-position will be incremented if the printname for the Interlisp-D object STR is printed in font FONT. If FONT is NIL, DEFAULTFONT is used as FONT. If FONT is an image stream, its font is used. If FLG is non-NIL, the PRIN2-pname of STR with respect to the readtable RDTBL is used. (STRINGREGION STR STREAM PRIN2FLG RDTBL) [Function] Returns the region occupied by STR if it were printed at the current location in the image stream STREAM. This is useful, for example, for determining where text is in a window to allow the user to select it. The arguments PRIN2FLG and RDTBL are passed to STRINGWIDTH. Note: STRINGREGION does not take into account any carriage returns in the string, or carriage returns that may be automatically printed if STR is printed to STREAM. Therefore, the value returned is meaningless for multi-line strings. The following functions allow the user to access and change the bitmaps for individual characters in a display font. Note: Character code 256 can be used to access the "dummy" character, used for characters in the font with no bitmap defined. (GETCHARBITMAP CHARCODE FONT) [Function] Returns a bitmap containing a copy of the image of the character CHARCODE in the font FONT. (PUTCHARBITMAP CHARCODE FONT NEWCHARBITMAP NEWCHARDESCENT) [Function] Changes the bitmap image of the character CHARCODE in the font FONT to the bitmap NEWCHARBITMAP. If NEWCHARDESCENT is non-NIL, the descent of the character is changed to the value of NEWCHARDESCENT. (EDITCHAR CHARCODE FONT) [Function] Calls the bitmap editor (EDITBM) on the bitmap image of the character CHARCODE in the font FONT. CHARCODE can be a character code (as returned by CHCON1) or an atom or string, in which case the first character of CHARCODE is used. (WRITESTRIKEFONTFILE(WRITESTRIKEFONTFILE (Function) NIL NIL (4) NIL) FONT CHARSET FILENAME) [Function] Takes a display font font descriptor(FONT% DESCRIPTOR NIL font% descriptor NIL (4) NIL) and a character set number, and writes that character set into a file suitable for reading in again. Note that the font descriptor's current state is used (which was perhaps modified by INSPECTing the datum), so this provides a mechanism for creating/modifying new fonts. For example: (WRITESTRIKEFONTFILE (FONTCREATE 'GACHA 10) 0 '{DSK}Magic10-MRR-C0.DISPLAYFONT) If your DISPLAYFONTDIRECTORIES(DISPLAYFONTDIRECTORIES (Variable) NIL NIL (4) NIL) includes {DSK}, then a subsequent (FONTCREATE 'MAGIC 10) will create a new font descriptor whose appearance is the same as the old Gacha font descriptor. However, the new font is identical to the old one in appearance only. The individual datatype fields and bitmap may not be the same as those in the old font descriptor, due to peculiarities of different font file formats. Font Files and Font Directories 1 If FONTCREATE is called to create a font that has not been loaded into Interlisp, FONTCREATE has to read the font information from a font file that contains the information for that font. For printer devices, the font files have to contain width information for each character in the font. For display fonts, the font files have to contain, in addition, bitmap images for each character in the fonts. The font file names, formats, and searching algorithms are different for each device. There are a set of variables for each device, that determine the directories that are searched for font files. All of these variables must be set before Interlisp can auto-load font files. These variables should be initialized in the site-specific INIT file. DISPLAYFONTDIRECTORIES [Variable] Value is a list of directories searched to find font bitmap files for display fonts. DISPLAYFONTEXTENSIONS [Variable] Value is a list of file extensions used when searching DISPLAYFONTDIRECTORIES for display fonts. Initially set to (DISPLAYFONT), but when using older font files it may be necessary to add STRIKE and AC to this list. INTERPRESSFONTDIRECTORIES [Variable] Value is a list of directories searched to find font widths files for Interpress fonts. PRESSFONTWIDTHSFILES [Variable] Value is a list of files (not directories) searched to find font widths files for Press fonts. Press font widths are packed into large files (usually named FONTS.WIDTHS). Font Profiles 1 PRETTYPRINT contains a facility for printing different elements (user functions, system functions, clisp words, comments, etc.) in different fonts to emphasize (or deemphasize) their importance, and in general to provide for a more pleasing appearance. Of course, in order to be useful, this facility requires that the user is printing on a device (such as a bitmapped display or a laser printer) which supports multiple fonts. PRETTYPRINT signals font changes by inserting into the file a user-defined escape sequence (the value of the variable FONTESCAPECHAR) followed by the character code which specifies, by number, which font to use, i.e. A for font number 1, etc. Thus, if FONTESCAPECHAR were the character F, FC would be output to change to font 3, FA to change to font 1, etc. If FONTESCAPECHAR consists of characters which are separator charactors in FILERDTBL, then a file with font changes in it can also be loaded back in. Currently, PRETTYPRINT uses the following font classes. The user can specify separate fonts for each of these classes, or use the same font for several different classes. LAMBDAFONT The font for printing the name of the function being prettyprinted, before the actual definition (usually a large font). CLISPFONT If CLISPFLG is on, the font for printing any clisp words, i.e. atoms with property CLISPWORD. COMMENTFONT The font used for comments. USERFONT The font for the name of any function in the file, or any member of the list FONTFNS. SYSTEMFONT The font for any other (defined) function. CHANGEFONT The font for an expression marked by the editor as having been changed. PRETTYCOMFONT The font for the operand of a file package command. DEFAULTFONT The font for everything else. Note that not all combinations of fonts will be aesthetically pleasing (or even readable!) and the user may have to experiment to find a compatible set. Although in some implementations LAMBDAFONT et al. may be defined as variables, one should not set them directly, but should indicate what font is to be used for each class by calling the function FONTPROFILE: (FONTPROFILE PROFILE) [Function] Sets up the font classes as determined by PROFILE, a list of elements which defines the correspondence between font classes and specific fonts. Each element of PROFILE is a list of the form: (FONTCLASS FONT# DISPLAYFONT PRESSFONT INTERPRESSFONT) FONTCLASS is the font class name and FONT# is the font number for that class. For each font class name, the escape sequence will consist of FONTESCAPECHAR followed by the character code for the font number, e.g. A for font number 1, etc. If FONT# is NIL for any font class, the font class named DEFAULTFONT (which must always be specified) is used. Alternatively, if FONT# is the name of a previously defined font class, this font class will be equivalenced to the previously defined one. DISPLAYFONT, PRESSFONT, and INTERPRESSFONT are font specifications (of the form accepted by FONTCREATE) for the fonts to use when printing to the display and to Press and Interpress printers respectively. FONTPROFILE [Variable] This is the variable used to store the current font profile, in the form accepted by the function FONTPROFILE. Note that simply editing this value will not change the fonts used for the various font classes; it is necessary to execute (FONTPROFILE FONTPROFILE) to install the value of this variable. The process of printing with multiple fonts is affected by a large number of variables: FONTPROFILE, FILELINELENGTH, PRETTYLCOM, etc. To facilitate switching back and forth between various sets of values for the font variables, Interlisp supports the idea of named "font configurations" encapsulating the values of all relevant variables. To create a new font configuration, set all "relevant" variables to the values you want, and then call FONTNAME to save them (on the variable FONTDEFS) under a given name. To install a particular font configuration, call FONTSET giving it your name. To change the values in a saved font configuration, edit the value of the variable FONTDEFS. Note: The list of variables saved by FONTNAME is stored in the variable FONTDEFSVARS. This can be changed by the user. (FONTSET NAME) [Function] Installs font configuration for NAME. Also evaluates (FONTPROFILE FONTPROFILE) to install the font classes as specified in the new value of the variable FONTPROFILE. Generates an error if NAME not previously defined. FONTDEFSVARS [Variable] The list of variables to be packaged by a FONTNAME. Initially FONTCHANGEFLG, FILELINELENGTH, COMMENTLINELENGTH, FIRSTCOL, PRETTYLCOM, LISTFILESTR, and FONTPROFILE. FONTDEFS [Variable] An association list of font configurations. FONTDEFS is a list of elements of form (NAME . PARAMETER-PAIRS). To save a configuration on a file after performing a FONTNAME to define it, the user could either save the entire value of FONTDEFS, or use the ALISTS file package command to dump out just the one configuration. FONTESCAPECHAR [Variable] The character or string used to signal the start of a font escape sequence. FONTCHANGEFLG [Variable] If T, enables fonts when prettyprinting. If NIL, disables fonts. ALL indicates that all calls to CHANGEFONT are executed. LISTFILESTR [Variable] In Interlisp-10, passed to the operating system by LISTFILES. Can be used to specify subcommands to the LIST command, e.g. to establish correspondance between font number and font name. COMMENTLINELENGTH [Variable] Since comments are usually printed in a smaller font, COMMENTLINELENGTH is provided to offset the fact that Interlisp does not know about font widths. When FONTCHANGEFLG = T, CAR of COMMENTLINELENGTH is the linelength used to print short comments, i.e. those printed in the right margin, and CDR is the linelength used when printing full width comments. (CHANGEFONT FONT STREAM) [Function] Executes the operations on STREAM to change to the font FONT. For use in PRETTYPRINTMACROS. Image Objects 1 An Image Object is an object that includes information about an image, such as how to display it, how to print it, and how to manipulate it when it is included in a collection of images (such as a document). More generally, it enables you to include one kind of image, with its own semantics, layout rules, and editing paradigms, inside another kind of image. Image Objects provide a general-purpose interface between image users who want to manipulate arbitrary images, and image producers, who create images for use, say, in documents. Images are encapsulated inside a uniform barrier%the IMAGEOBJ data type. From the outside, you communicate to the image by calling a standard set of functions. For example, calling one function tells you how big the image is; calling another causes the image object to be displayed where you tell it, and so on. Anyone who wants to create images for general use can implement his own brand of IMAGEOBJ. IMAGEOBJs have been implemented (in library packages) for bitmaps, menus, annotations, graphs, and sketches. Image Objects were originally implemented to support inserting images into TEdit text files, but the facility is available for use by any tools that manipulate images. The Image Object interface allows objects to exist in TEdit documents and be edited with their own editor. It also provides a facility in which objects can be shift-selected (or "copy-selected") between TEdit and non-TEdit windows. For example, the Image Objects interface allows you to copy-select graphs from a Grapher window into a TEdit window. The source window (where the object comes from) does not have to know what sort of window the destination window (where the object is inserted) is, and the destination does not have to know where the insertion comes from. A new data type, IMAGEOBJ, contains the data and the procedures necessary to manipulate an object that is to be manipulated in this way. IMAGEOBJs are created with the function IMAGEOBJCREATE (below). Another new data type, IMAGEFNS, is a vector of the procedures necessary to define the behavior of a type of IMAGEOBJ. Grouping the operations in a separate data type allows multiple instances of the same type of image object to share procedure vectors. The data and procedure fields of an IMAGEOBJ have a uniform interface through the function IMAGEOBJPROP. IMAGEFNS are created with the function IMAGEFNSCREATE: (IMAGEFNSCREATE(IMAGEFNSCREATE (Function) NIL NIL ("27") 27) DISPLAYFN IMAGEBOXFN PUTFN GETFN COPYFN BUTTONEVENTINFN COPYBUTTONEVENTINFN WHENMOVEDFN WHENINSERTEDFN WHENDELETEDFN WHENCOPIEDFN WHENOPERATEDONFN PREPRINTFN %) [Function] Returns an IMAGEFNS object that contains the functions necessary to define the behavior of an IMAGEOBJ. The arguments DISPLAYFN through PREPRINTFN should all be function names to be stored as the "methods" of the IMAGEFNS. The purpose of each IMAGEFNS method is described below. Note: Image objects must be "registered" before they can be read by TEdit or HREAD. IMAGEFNSCREATE implicitly registers its GETFN argument. (IMAGEOBJCREATE(IMAGEOBJCREATE (Function) NIL NIL ("27") 27) OBJECTDATUM IMAGEFNS) [Function] Returns an IMAGEOBJ that contains the object datum OBJECTDATUM and the operations vector IMAGEFNS. OBJECTDATUM can be arbitrary data. (IMAGEOBJPROP(IMAGEOBJPROP (Function) NIL NIL ("27") 28) IMAGEOBJECT PROPERTY NEWVALUE) [NoSpread Function] Accesses and sets the properties of an IMAGEOBJ. Returns the current value of the PROPERTY property of the image object IMAGEOBJECT. If NEWVALUE is given, the property is set to it. IMAGEOBJPROP can be used on the system properties OBJECTDATUM, DISPLAYFN, IMAGEBOXFN, PUTFN, GETFN, COPYFN, BUTTONEVENTINFN, COPYBUTTONEVENTINFN, WHENOPERATEDONFN, and PREPRINTFN. Additionally, it can be used to save arbitrary properties on an IMAGEOBJ. (IMAGEFNSP(IMAGEFNSP (Function) NIL NIL ("27") 28) X) [Function] Returns X if X is an IMAGEFNS object, NIL otherwise. (IMAGEOBJP(IMAGEOBJP (Function) NIL NIL ("27") 28) X) [Function] Returns X if X is an IMAGEOBJ object, NIL otherwise. IMAGEFNS Methods Note: Many of the IMAGEFNS methods below are passed "host stream" arguments. The TEdit text editor passes the "text stream" (an object contain all of the information in the document being edited) as the "host stream" argument. Other editing programs that want to use image objects may want to pass the data structure being edited to the IMAGEFNS methods as the "host stream" argument. (DISPLAYFN IMAGEOBJ IMAGESTREAM IMAGESTREAMTYPE HOSTSTREAM) [IMAGEFNS Method] The DISPLAYFN method is called to display the object IMAGEOBJ at the current position on IMAGESTREAM. The type of IMAGESTREAM indicates whether the device is the display or some other image stream. Note: When the DISPLAYFN method is called, the offset and clipping regions for the stream are set so the object's image is at (0,0), and only that image area can be modified. (IMAGEBOXFN IMAGEOBJ IMAGESTREAM CURRENTX RIGHTMARGIN) [IMAGEFNS Method] The IMAGEBOXFN method should return the size of the object as an IMAGEBOX, which is a data structure that describes the image laid down when an IMAGEOBJ is displayed in terms of width, height, and descender height. An IMAGEBOX has four fields: XSIZE, YSIZE, YDESC, and XKERN. XSIZE and YSIZE are the width and height of the object image. YDESC and XKERN give the position of the baseline and the left edge of the image relative to where you want to position it. For characters, the YDESC is the descent (height of the descender) and the XKERN is the amount of left kerning (note: TEdit doesn't support left kerning). The IMAGEBOXFN looks at the type of the stream to determine the output device if the object's size changes from device to device. (For example, a bit-map object may specify a scale factor that is ignored when the bit map is displayed on the screen.) CURRENTX and RIGHTMARGIN allow an object to take account of its environment when deciding how big it is. If these fields are not available, they are NIL. Note: TEdit calls the IMAGEBOXFN only during line formatting, then caches the IMAGEBOX as the BOUNDBOX property of the IMAGEOBJ. This avoids the need to call the IMAGEBOXFN when incomplete position and margin information is available. (PUTFN IMAGEOBJ FILESTREAM) [IMAGEFNS Method] The PUTFN method is called to save the object on a file. It prints a description on FILESTREAM that, when read by the corresponding GETFN method (see below), regenerates the image object. (TEdit and HPRINT take care of writing out the name of the GETFN.) (GETFN FILESTREAM) [IMAGEFNS Method] The GETFN method is called when the object is encountered on the file during input. It reads the description that was written by the PUTFN method and returns an IMAGEOBJ. (COPYFN IMAGEOBJ SOURCEHOSTSTREAM TARGETHOSTSTREAM) [IMAGEFNS Method] The COPYFN method is called during a copy-select operation. It should return a copy of IMAGEOBJ. If it returns the litatom DON'T, copying is suppressed. (BUTTONEVENTINFN IMAGEOBJ WINDOWSTREAM SELECTION RELX RELY WINDOW HOSTSTREAM BUTTON) [IMAGEFNS Method] The BUTTONEVENTINFN method is called when you press a mouse button inside the object. The BUTTONEVENTINFN decides whether or not to handle the button, to track the cursor in parallel with mouse movement, and to invoke selections or edits supported by the object (but see the COPYBUTTONEVENTINFN method below). If the BUTTONEVENTINFN returns NIL, TEdit treats the button press as a selection at its level. Note that when this function is first called, a button is down. The BUTTONEVENTINFN should also support the button-down protocol to descend inside of any composite objects with in it. In most cases, the BUTTONEVENTINFN relinquishes control (i.e., returns) when the cursor leaves its object's region. When the BUTTONEVENTINFN is called, the window's clipping region and offsets have been changed so that the lower-left corner of the object's image is at (0,0), and only the object's image can be changed. The selection is available for changing to fit your needs; the mouse button went down at (RELX,RELY) within the object's image. You can affect how TEdit treats the selection by returning one of several values. If you return NIL, TEdit forgets that you selected an object; if you return the atom DON'T, TEdit doesn't permit the selection; if you return the atom CHANGED, TEdit updates the screen. Use CHANGED to signal TEdit that the object has changed size or will have side effects on other parts of the screen image. (COPYBUTTONEVENTINFN IMAGEOBJ WINDOWSTREAM) [IMAGEFNS Method] The COPYBUTTONEVENTINFN method is called when you button inside an object while holding down a copy key. Many of the comments about BUTTONEVENTINFN apply here too. Also, see the discussion below about copying image objects between windows. (WHENMOVEDFN IMAGEOBJ TARGETWINDOWSTREAM SOURCEHOSTSTREAM TARGETHOSTSTREAM) [IMAGEFNS Method] The WHENMOVEDFN method provides hooks by which the object is notified when TEdit performs an operation (MOVEing) on the whole object. It allows objects to have side effects. (WHENINSERTEDFN IMAGEOBJ TARGETWINDOWSTREAM SOURCEHOSTSTREAM TARGETHOSTSTREAM) [IMAGEFNS Method] The WHENINSERTEDFN method provides hooks by which the object is notified when TEdit performs an operation (INSERTing) on the whole object. It allows objects to have side effects. (WHENDELETEDFN IMAGEOBJ TARGETWINDOWSTREAM) [IMAGEFNS Method] The WHENDELETEDFN method provides hooks by which the object is notified when TEdit performs an operation (DELETEing) on the whole object. It allows objects to have side effects. (WHENCOPIEDFN IMAGEOBJ TARGETWINDOWSTREAM SOURCEHOSTSTREAM TARGETHOSTSTREAM) [IMAGEFNS Method] The WHENCOPIEDFN method provides hooks by which the object is notified when TEdit performs an operation (COPYing) on the whole object. The WHENCOPIEDFN method is called in addition to (and after) the COPYFN method above. It allows objects to have side effects. (WHENOPERATEDONFN IMAGEOBJ WINDOWSTREAM HOWOPERATEDON SELECTION HOSTSTREAM) [IMAGEFNS Method] The WHENOPERATEDONFN method provides a hook for edit operations. HOWOPERATEDON should be one of SELECTED, DESELECTED, HIGHLIGHTED, and UNHILIGHTED. The WHENOPERATEDONFN differs from the BUTTONEVENTINFN because it is called when you extend a selection through the object. That is, the object is treated in toto as a TEdit character. HIGHLIGHTED refers to the selection being highlighted on the screen, and UNHIGHLIGHTED means that the highlighting is being turned off. (PREPRINTFN IMAGEOBJ) [IMAGEFNS Method] The PREPRINTFN method is called to convert the object into something that can be printed for inclusion in documents. It returns an object that the receiving window can print (using either PRIN1 or PRIN2,its choice) to obtain a character representation of the object. If the PREPRINTFN method is NIL, the OBJECTDATUM field of IMAGEOBJ itself is used. TEdit uses this function when you indicate that you want to print the characters from an object rather than the object itself (presumably using PRIN1 case). Registering Image Objects Each legitimate GETFN needs to be known to the system, to prevent various Trojan-horse problems and to allow the automatic loading of the supporting code for infrequently used IMAGEOBJs. To this end, there is a global list, IMAGEOBJGETFNS, that contains an entry for each GETFN. The existence of the entry marks the GETFN as legitimate; the entry itself is a property list, which can hold information about the GETFN. No action needs to be taken for GETFNs that are currently in use: the function IMAGEFNSCREATE automatically adds its GETFN argument to the list. However, packages that support obsolete versions of objects may need to explicitly add the obsolete GETFNs. For example, TEdit supports bit-map IMAGEOBJs. Recently, a change was made in the format in which objects are stored; to retain compatibility with the old object format, there are now two GETFNs. The current GETFN is automatically on the list, courtesy of IMAGEFNSCREATE. However, the code file that supports the old bit-map objects contains the clause: (ADDVARS (IMAGEOBJGETFNS (OLDGETFNNAME))), which adds the old GETFN to IMAGEOBJGETFNS. For a given GETFN, the entry on IMAGEOBJGETFNS may be a property list of information. Currently the only recognized property is FILE. FILE is the name of the file that can be loaded if the GETFN isn't defined. This file should define the GETFN, along with all the other functions needed to support that kind of IMAGEOBJ. For example, the bit-map IMAGEOBJ implemented by TEdit use the GETFN BMOBJ.GETFN2. Its entry on IMAGEOBJGETFNS is (BMOBJ.GETFN2 FILE IMAGEOBJ), indicating that the support code for bit-map image objects resides on the file IMAGEOBJ, and that the GETFN for them is BMOBJ.GETFN2. This makes it possible to have entries for GETFNs whose supporting code isn't loaded%you might, for instance, have your init file add entries to IMAGEOBJGETFNS for the kinds of image objects you commonly use. The system's default reading method will automatically load the code when necessary. Reading and Writing Image Objects on Files Image Objects can be written out to files using HPRINT and read back using HREAD. The following functions can also be used: (WRITEIMAGEOBJ(WRITEIMAGEOBJ (Function) NIL NIL ("27") 31) IMAGEOBJ STREAM) [Function] Prints (using PRIN2) a call to READIMAGEOBJ, then calls the PUTFN for IMAGEOBJ to write it onto STREAM. During input, then, the call to READIMAGEOBJ is read and evaluated; it in turn reads back the object's description, using the appropriate GETFN. (READIMAGEOBJ(READIMAGEOBJ (Function) NIL NIL ("27") 31) STREAM GETFN NOERROR) [Function] Reads an IMAGEOBJ from STREAM, starting at the current file position. Uses the function GETFN after validating it (and loading support code, if necessary). If the GETFN can't be validated or isn't defined, READIMAGEOBJ returns an "encapsulated image object", an IMAGEOBJ that safely encapsulates all of the information in the image object. An encapsulated image object displays as a rectangle that says, "Unknown IMAGEOBJ Type" and lists the GETFN's name. Selecting an encapsulated image object with the mouse causes another attempt to read the object from the file; this is so you can load any necessary support code and then get to the object. Warning: You cannot save an encapsulated image object on a file because there isn't enough information to allow copying the description to the new file from the old one. If NOERROR is non-NIL, READIMAGEOBJ returns NIL if it can't successfully read the object. Copying Image Objects Between Windows Copying between windows is implemented as follows: If a button event occurs in a window when a copy key is down, the window's COPYBUTTONEVENTFN window property is called. If this window supports copy-selection, it should track the mouse, indicating the item to be copied. When the button is released, the COPYBUTTONEVENTFN should create an image object out of the selected information, and call COPYINSERT to insert it in the current TTY window. COPYINSERT calls the COPYINSERTFN window property of the TTY window to insert this image object. Therefore, both the source and destination windows can determine how they handle copying image objects. If the COPYBUTTONEVENTFN of a window is NIL, the BUTTONEVENTFN is called instead when a button event occurs in the window when a copy key is down, and copying from that window is not supported. If the COPYINSERTFN of the TTY window is NIL, COPYINSERT will turn the image object into a string (by calling the PREPRINTFN method of the image object) and insert it by calling BKSYSBUF. COPYBUTTONEVENTFN [Window Property] The COPYBUTTONEVENTFN of a window is called (if it exists) when a button event occurs in the window and a copy key is down. If no COPYBUTTONEVENTFN exists, the BUTTONEVENTFN is called. COPYINSERTFN [Window Property] The COPYINSERTFN of the "destination" window is called by COPYINSERT to insert something into the destination window. It is called with two arguments: the object to be inserted and the destination window. The object to be inserted can be a character string, an IMAGEOBJ, or a list of IMAGEOBJs and character strings. As a convention, the COPYINSERTFN should call BKSYSBUF if the object to be inserted insert is a character string. (COPYINSERT IMAGEOBJ) [Function] COPYINSERT inserts IMAGEOBJ into the window that currently has the TTY. If the current TTY window has a COPYINSERTFN, it is called, passing it IMAGEOBJ and the window as arguments. If no COPYINSERTFN exists and if IMAGEOBJ is an image object, BKSYSBUF is called on the result of calling its PREPRINTFN on it. If IMAGEOBJ is not an image object, it is simply passed to BKSYSBUF . In this case, BKSYSBUF will call PRIN2 with a read table taken from the process associated with the TTY window. A window that wishes to use PRIN1 or a different read table must provide its own COPYINSERTFN to do this. Implementation of Image Streams 1 Interlisp does all image creation through a set of functions and data structures for device-independent graphics, known popularly as DIG. DIG is implemented through the use of a special type of stream, known as an image stream. An image stream, by convention, is any stream that has its IMAGEOPS field (described in detail below) set to a vector of meaningful graphical operations. Using image streams, you can write programs that draw and print on an output stream without regard to the underlying device, be it a window, a disk, or a printer. To define a new image stream type, it is necessary to put information on the variable IMAGESTREAMTYPES: IMAGESTREAMTYPES [Variable] This variable describes how to create a stream for a given image stream type. The value of IMAGESTREAMTYPES is an association list, indexed by the image stream type (e.g., DISPLAY, INTERPRESS, etc.). The format of a single association list item is: (IMAGETYPE (OPENSTREAM OPENSTREAMFN) (FONTCREATE FONTCREATEFN) (FONTSAVAILABLE FONTSAVAILABLEFN)) OPENSTREAMFN, FONTCREATEFN, and FONTSAVAILABLEFN are "image stream methods," device-dependent functions used to implement generic image stream operations. For Interpress image streams, the association list entry is: (INTERPRESS (OPENSTREAM OPENIPSTREAM) (FONTCREATE \CREATEINTERPRESSFONT) (FONTSAVAILABLE \SEARCHINTERPRESSFONTS)) (OPENSTREAMFN FILE OPTIONS) [Image Stream Method] FILE is the file name as it was passed to OPENIMAGESTREAM, and OPTIONS is the OPTIONS property list passed to OPENIMAGESTREAM. The result must be a stream of the appropriate image type. (FONTCREATEFN FAMILY SIZE FACE ROTATION DEVICE) [Image Stream Method] FAMILY is the family name for the font, e.g., MODERN. SIZE is the body size of the font, in printer's points. FACE is a three-element list describing the weight, slope, and expansion of the face desired, e.g., (MEDIUM ITALIC EXPANDED). ROTATION is how much the font is to be rotated from the normal orientation, in minutes of arc. For example, to print a landscape page, fonts have the rotation 5400 (90 degrees). The function's result must be a FONTDESCRIPTOR with the fields filled in appropriately. (FONTSAVAILABLEFN FAMILY SIZE FACE ROTATION DEVICE) [Image Stream Method] This function returns a list of all fonts agreeing with the FAMILY, SIZE, FACE, and ROTATION arguments; any of them may be wild-carded (i.e., equal to *, which means any value is acceptable). Each element of the list should be a quintuple of the form (FAMILY SIZE FACE ROTATION DEVICE). Where the function looks is an implementation decision: the FONTSAVAILABLEFN for the display device looks at DISPLAYFONTDIRECTORIES, the Interpress code looks on INTERPRESSFONTDIRECTORIES, and implementors of new devices should feel free to introduce new search path variables. As indicated above, image streams use a field that no other stream uses: IMAGEOPS. IMAGEOPS is an instance of the IMAGEOPS data type and contains a vector of the stream's graphical methods. The methods contained in the IMAGEOPS object can make arbitrary use of the stream's IMAGEDATA field, which is provided for their use, and may contain any data needed. IMAGETYPE [IMAGEOPS Field] Value is the name of an image type. Monochrome display streams have an IMAGETYPE of DISPLAY; color display streams are identified as (COLOR DISPLAY). The IMAGETYPE field is informational and can be set to anything you choose. IMFONTCREATE [IMAGEOPS Field] Value is the device name to pass to FONTCREATE when fonts are created for the stream. The remaining fields are all image stream methods, whose value should be a device-dependent function that implements the generic operation. Most methods are called by a similarly-named function, e.g. the function DRAWLINE calls the IMDRAWLINE method. All coordinates that refer to points in a display device's space are measured in the device's units. (The IMSCALE method provides access to a device's scale.) For arguments that have defaults (such as the BRUSH argument of DRAWCURVE), the default is substituted for the NIL argument before it is passed to the image stream method. Therefore, image stream methods do not have to handle defaults. (IMCLOSEFN STREAM) [Image Stream Method] Called before a stream is closed with CLOSEF. This method should flush buffers, write header or trailer information, etc. (IMDRAWLINE STREAM X1 Y1 X2 Y2 WIDTH OPERATION COLOR DASHING) [Image Stream Method] Draws a line of width WIDTH from (X1, Y1) to (X2, Y2). See DRAWLINE. (IMDRAWCURVE STREAM KNOTS CLOSED BRUSH DASHING) [Image Stream Method] Draws a curve through KNOTS. See DRAWCURVE. (IMDRAWCIRCLE STREAM CENTERX CENTERY RADIUS BRUSH DASHING) [Image Stream Method] Draws a circle of radius RADIUS around (CENTERX, CENTERY). See DRAWCIRCLE. (IMDRAWELLIPSE STREAM CENTERX CENTERY SEMIMINORRADIUS SEMIMAJORRADIUS ORIENTATION BRUSH DASHING) [Image Stream Method] Draws an ellipse around (CENTERX, CENTERY). See DRAWELLIPSE. (IMFILLPOLYGON STREAM POINTS TEXTURE) [Image Stream Method] Fills in the polygon outlined by POINTS on the image stream STREAM, using the texture TEXTURE. See FILLPOLYGON. (IMFILLCIRCLE STREAM CENTERX CENTERY RADIUS TEXTURE) [Image Stream Method] Draws a circle filled with texture TEXTURE around (CENTERX, CENTERY). See FILLCIRCLE. (IMBLTSHADE TEXTURE STREAM DESTINATIONLEFT DESTINATIONBOTTOM WIDTH HEIGHT OPERATION CLIPPINGREGION) [Image Stream Method] The texture-source case of BITBLT. DESTINATIONLEFT, DESTINATIONBOTTOM, WIDTH, HEIGHT, and CLIPPINGREGION are measured in STREAM's units. This method is invoked by the functions BITBLT and BLTSHADE. (IMBITBLT SOURCEBITMAP SOURCELEFT SOURCEBOTTOM STREAM DESTINATIONLEFT DESTINATIONBOTTOM WIDTH HEIGHT SOURCETYPE OPERATION TEXTURE CLIPPINGREGION CLIPPEDSOURCELEFT CLIPPEDSOURCEBOTTOM SCALE) [Image Stream Method] Contains the bit-map-source cases of BITBLT. SOURCELEFT, SOURCEBOTTOM, CLIPPEDSOURCELEFT, CLIPPEDSOURCEBOTTOM, WIDTH, and HEIGHT are measured in pixels; DESTINATIONLEFT, DESTINATIONBOTTOM, and CLIPPINGREGION are in the units of the destination stream. (IMSCALEDBITBLT SOURCEBITMAP SOURCELEFT SOURCEBOTTOM STREAM DESTINATIONLEFT DESTINATIONBOTTOM WIDTH HEIGHT SOURCETYPE OPERATION TEXTURE CLIPPINGREGION CLIPPEDSOURCELEFT CLIPPEDSOURCEBOTTOM SCALE) [Image Stream Method] A scaled version of IMBITBLT. Each pixel in SOURCEBITMAP is replicated SCALE times in the X and Y directions; currently, SCALE must be an integer. (IMMOVETO STREAM X Y) [Image Stream Method] Moves to (X,Y). This method is invoked by the function MOVETO. If IMMOVETO is not supplied, a default method composed of calls to the IMXPOSITION and IMYPOSITION methods is used. (IMSTRINGWIDTH STREAM STR RDTBL) [Image Stream Method] Returns the width of string STR in STREAM's units, using STREAM's current font. This is envoked when STRINGWIDTH is passed a stream as its FONT argument. If IMSTRINGWIDTH is not supplied, it defaults to calling STRINGWIDTH on the default font of STREAM. (IMCHARWIDTH STREAM CHARCODE) [Image Stream Method] Returns the width of character CHARCODE in STREAM's units, using STREAM's current font. This is invoked when CHARWIDTH is passed a stream as its FONT argument. If IMCHARWIDTH is not supplied, it defaults to calling CHARWIDTH on the default font of STREAM. (IMCHARWIDTHY STREAM CHARCODE) [Image Stream Method] Returns the Y componant of the width of character CHARCODE in STREAM's units, using STREAM's current font. This is envoked when CHARWIDTHY is passed a stream as its FONT argument. If IMCHARWIDTHY is not supplied, it defaults to calling CHARWIDTHY on the default font of STREAM. (IMBITMAPSIZE STREAM BITMAP DIMENSION) [Image Stream Method] Returns the size that BITMAP will be when BITBLTed to STREAM, in STREAM's units. DIMENSION can be one of WIDTH, HEIGHT, or NIL, in which case the dotted pair (WIDTH . HEIGHT) will be returned. This is envoked by BITMAPIMAGESIZE. If IMBITMAPSIZE is not supplied, it defaults to a method that multiplies the bitmap height and width by the scale of STREAM. (IMNEWPAGE STREAM) [Image Stream Method] Causes a new page to be started. The X position is set to the left margin, and the Y position is set to the top margin plus the linefeed. If not supplied, defaults to (\OUTCHAR STREAM (CHARCODE ^L)). Envoked by DSPNEWPAGE. (IMTERPRI STREAM) [Image Stream Method] Causes a new line to be started. The X position is set to the left margin, and the Y position is set to the current Y position plus the linefeed. If not supplied, defaults to (\OUTCHAR STREAM (CHARCODE EOL)). Envoked by TERPRI. (IMRESET(IMRESET (Image Stream Method) NIL NIL ("27") 35) STREAM) [Image Stream Method] Resets the X and Y position of STREAM. The X coordinate is set to its left margin; the Y coordinate is set to the top of the clipping region minus the font ascent. Envoked by DSPRESET. The following methods all have corresponding DSPxx functions (e.g., IMYPOSITION corresponds to DSPYPOSITION) that invoke them. They also have the property of returning their previous value; when called with NIL they return the old value without changing it. (IMCLIPPINGREGION(IMCLIPPINGREGION (Image Stream Method) NIL NIL ("27") 35) STREAM REGION) [Image Stream Method] Sets a new clipping region on STREAM. (IMXPOSITION(IMXPOSITION (Image Stream Method) NIL NIL ("27") 36) STREAM XPOSITION) [Image Stream Method] Sets the X-position on STREAM. (IMYPOSITION(IMYPOSITION (Image Stream Method) NIL NIL ("27") 36) STREAM YPOSITION) [Image Stream Method] Sets a new Y-position on STREAM. (IMFONT(IMFONT (Image Stream Method) NIL NIL ("27") 36) STREAM FONT) [Image Stream Method] Sets STREAM's font to be FONT. (IMLEFTMARGIN(IMLEFTMARGIN (Image Stream Method) NIL NIL ("27") 36) STREAM LEFTMARGIN) [Image Stream Method] Sets STREAM's left margin to be LEFTMARGIN. The left margin is defined as the X-position set after the new line. (IMRIGHTMARGIN(IMRIGHTMARGIN (Image Stream Method) NIL NIL ("27") 36) STREAM RIGHTMARGIN) [Image Stream Method] Sets STREAM's right margin to be RIGHTMARGIN. The right margin is defined as the maximum X-position at which characters are printed; printing beyond it causes a new line. (IMTOPMARGIN(IMTOPMARGIN (Image Stream Method) NIL NIL ("27") 36) STREAM YPOSITION) [Image Stream Method] Sets STREAM's top margin (the Y-position of the tops of characters that is set after a new page) to be YPOSITION. (IMBOTTOMMARGIN(IMBOTTOMMARGIN (Image Stream Method) NIL NIL ("27") 36) STREAM YPOSITION) [Image Stream Method] Sets STREAM's bottom margin (the Y-position beyond which any printing causes a new page) to be YPOSITION. (IMLINEFEED(IMLINEFEED (Image Stream Method) NIL NIL ("27") 36) STREAM DELTA) [Image Stream Method] Sets STREAM's line feed distance (distance to move vertically after a new line) to be DELTA. (IMSCALE(IMSCALE (Image Stream Method) NIL NIL ("27") 36) STREAM SCALE) [Image Stream Method] Returns the number of device points per screen point (a screen point being ~1/72 inch). SCALE is ignored. (IMSPACEFACTOR(IMSPACEFACTOR (Image Stream Method) NIL NIL ("27") 36) STREAM FACTOR) [Image Stream Method] Sets the amount by which to multiply the natural width of all following space characters on STREAM; this can be used for the justification of text. The default value is 1. For example, if the natural width of a space in STREAM's current font is 12 units, and the space factor is set to two, spaces appear 24 units wide. The values returned by STRINGWIDTH and CHARWIDTH are also affected. (IMOPERATION(IMOPERATION (Image Stream Method) NIL NIL ("27") 36) STREAM OPERATION) [Image Stream Method] Sets the default BITBLT OPERATION argument. (IMBACKCOLOR(IMBACKCOLOR (Image Stream Method) NIL NIL ("27") 36) STREAM COLOR) [Image Stream Method] Sets the background color of STREAM. (IMCOLOR (IMCOLOR% (Image Stream Method) NIL NIL ("27") 36)STREAM COLOR) [Image Stream Method] Sets the default color of STREAM. In addition to the IMAGEOPS methods described above, there are two other important methods, which are contained in the stream itself. These fields can be installed using a form like (replace (STREAM OUTCHARFN) of STREAM with (FUNCTION MYOUTCHARFN)). Note: You need to have loaded the Interlisp-D system declarations to manipulate the fields of STREAMs. The declarations can be loaded by loading the Lisp Library package SYSEDIT. (STRMBOUTFN(STRMBOUTFN (Stream Method) NIL NIL ("27") 37) STREAM CHARCODE) [Stream Method] The function called by BOUT. (OUTCHARFN(OUTCHARFN (Stream Method) NIL NIL ("27") 37) STREAM CHARCODE) [Stream Method] The function that is called to output a single byte. This is like STRMBOUTFN, except for being one level higher: it is intended for text output. Hence, this function should convert (CHARCODE EOL) into the stream's actual end-of-line sequence and should adjust the stream's CHARPOSITION appropriately before invoking the stream's STRMBOUTFN (by calling BOUT) to actually put the character. Defaults to \FILEOUTCHARFN, which is probably incorrect for an image stream. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "26-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "26-" "")) (192 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "26-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "26-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "26-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "26-" "")) (192 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (408 612 204 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))&F PAGEHEADING RIGHTPAGE,//HH,xx,2`~~,0T,,HH,0T,002HH5xlx-T,HH -,HH306 -T-T0T-T3(T,,25l5l5l/xx/xx3HHT306 -T,ll,ll,ll@ PAGEHEADINGLEFTBACKTTERMINAL -PALATINO TITAN -TITAN TITAN TITAN TITAN TITAN -CLASSIC -CLASSIC - HELVETICA -CLASSIC -MODERN -MODERNMODERN -&  IM.CHAP.GETFNMODERN -!  HRULE.GETFNMODERN -  HRULE.GETFNMODERN -  !(O)4"  -!  -  -    j?"  -  -"  ".IM.INDEX.GETFNTITAN -  - - -L" *IM.INDEX.GETFNTITAN -  - - -y "/IM.INDEX.GETFNTITAN -   " -(IM.INDEX.GETFNTITAN -  7 " *IM.INDEX.GETFNTITAN - ,  ".IM.INDEX.GETFNTITAN - C! $    "%IM.INDEX.GETFNTITAN -    U F -*"  *  "%IM.INDEX.GETFNTITAN -  " )IM.INDEX.GETFNTITAN -    " *IM.INDEX.GETFNTITAN -  "  (" 'IM.INDEX.GETFNMODERN -   3 $</,_" -(IM.INDEX.GETFNTITAN - (2" *IM.INDEX.GETFNMODERN - -   -    " *IM.INDEX.GETFNMODERN - -" *IM.INDEX.GETFNTITAN - 1  ,   %     " )IM.INDEX.GETFNTITAN -    ) -" -(IM.INDEX.GETFNTITAN - 6 "$IM.INDEX.GETFNMODERN - X >/=@( BMOBJ.GETFN3MODERN - SZX BMOBJ.GETFN3PALATINO ;is, -g - + < 3w'L   -"5IM.INDEX.GETFNCLASSIC - :37"&IM.INDEX.GETFNCLASSIC -    %F - - Z" 'IM.INDEX.GETFNTITAN - ) S -V, BMOBJ.GETFN3MODERN -  HRULE.GETFNMODERN - -'?o cf)"-IM.INDEX.GETFNCLASSIC - *   37  }  ! $_  9  - -/ -2 <+ -v&J.N> B B; W ?W6 &(%#" *IM.INDEX.GETFNCLASSIC - H  - "-IM.INDEX.GETFNCLASSIC -   !".IM.INDEX.GETFNCLASSIC -   HRULE.GETFNMODERN -"0IM.INDEX.GETFNTITAN - "&IM.INDEX.GETFNTITAN - U)! -+%#$p" +IM.INDEX.GETFNTITAN - )o".IM.INDEX.GETFNTITAN -   4-U" ,IM.INDEX.GETFNTITAN -   *{"-IM.INDEX.GETFNCLASSIC - 3-u< -" +IM.INDEX.GETFNCLASSIC - F " *IM.INDEX.GETFNTITAN - . -b"*IM.INDEX.GETFNTITAN - / 7Z" ,IM.INDEX.GETFNMODERN -  D  %2*"'IM.INDEX.GETFNTITAN - &Z 0 # " k|v  X"   %.a"  .m$  HRULE.GETFNMODERN -a"   ' /"   ' /" - "  +," R )  - <(  HRULE.GETFNMODERN -l" + ^ /1 3"0    -  - 20  - )( -   3   - i: -? - 2   0 -    73X3WL - c  < BMOBJ.GETFN3 < BMOBJ.GETFN3  BMOBJ.GETFN3 J -   -& BMOBJ.GETFN3MODERN -  BMOBJ.GETFN3  BMOBJ.GETFN3  BMOBJ.GETFN3  BMOBJ.GETFN3MODERN -  BMOBJ.GETFN3  BMOBJ.GETFN3  BMOBJ.GETFN3  BMOBJ.GETFN3MODERN -  BMOBJ.GETFN3  BMOBJ.GETFN3  BMOBJ.GETFN3 " [ ;  ""   !  HRULE.GETFNMODERN -F- +"  - - - -$& - -  - -; &  t - -"   - -$  A'IM.INDEX.GETFN" (5! "  *52$   HRULE.GETFNMODERN - -, MSxtV>"  !<?Q%# #5$!@% BMOBJ.GETFN3MODERN -" - , <"@ 2 -  -#4^ -"  P( Y<>"    -f+ ~ ->  - -"  .  HRULE.GETFNMODERN -" /  " *IM.INDEX.GETFNMODERN -" $^76Q  ~=  BMOBJ.GETFN3TERMINAL -"  * b      -   -) -6 -,  BMOBJ.GETFN3PALATINO >%" - %#"  " - /8"   ,  HRULE.GETFNMODERN -/:<.'" - -7?  Y P"  /  - )0" -   &" " - &" .#" 9 " ;   HRULE.GETFNMODERN -'"  * Z" +   " - " - i   -  o -  o 7*a" - ?  -"  O -  "  | >   e"  # / -  7 -) %:  "     [  I   HRULE.GETFNMODERN -96%&:  -]#*$'$$$)sM -B ;1  SKIO.GETFN.2PALATINO " - 33+;K 84V -`   -l  -RF -1 -^ -. - - -9 - z "  " 1?X)3 G -#*4 - - 0 - x~"  - - - -5 K/T 9.& G" 0G --6H` x -% 7  -, .F +n" %0w;" 7:<"  A2 =" -    K "   ' J  + - -"   @y  yH"  A "  * - *  :" ' )= -"0IM.INDEX.GETFNMODERN - $3IM.INDEX.GETFNO P3IM.INDEX.GETFNPALATINO -d  HRULE.GETFNMODERN - -E -"U"7' >"X"  HRULE.GETFNMODERN -  kye: B   -z H  N -, -I 5 ! - "  *p7 cU* >u  2 -g" b   *X  -gHj& $"  L " *   -  "- 9> >"L" ) -" 3 -N"6V];" -  HRULE.GETFNMODERN -j_q  N/ "-IM.INDEX.GETFNTITAN - ? K   -CN "-IM.INDEX.GETFNCLASSIC -      " +IM.INDEX.GETFNCLASSIC - '$ & &   - -C" (IM.INDEX.GETFNTITAN -   " (IM.INDEX.GETFNCLASSIC -    9("  /  (  I " - )  -3GC02K - ~ -.$ -?"  L -&?*" - }" * N" B H yR  D=!p"  n`"  =  YE" = YE"     YE"  =  Y 18" 8 .  -   > 4" -   -I -   - )"(Z *|(+W) T3-DM  -V -T  0-" ,IM.INDEX.GETFNCLASSIC -   # ^" +IM.INDEX.GETFNCLASSIC -  <?& ,  +& I - - -      -: -6"n   "  * -/  <" -  - (  ( - 0 >&0    HRULE.GETFNMODERN -W;V "\A -;  - - -'    - -& -,"  &>"  (5a*" <;e <![Ib/ J" H +  ?" $ -(  -u]  &{"  &O" -  - - - - - - -"  ! "  +  -"  P "  ! "  %#  -" - V 3" %G-  " - -+<  "   -'  ) "  '  ) "   %' - ) -"     ! f"  &-T -" &- ; "1IM.INDEX.GETFNMODERN -   +X-  e0":IM.INDEX.GETFNMODERN - " 5IM.INDEX.GETFNMODERN -   " 5IM.INDEX.GETFNMODERN -   "0IM.INDEX.GETFNMODERN -  -" 6IM.INDEX.GETFNMODERN -  -%"" 7IM.INDEX.GETFNMODERN -  .Q" 5IM.INDEX.GETFNMODERN - H "8IM.INDEX.GETFNMODERN - -= " -4IM.INDEX.GETFNMODERN - - K"1IM.INDEX.GETFNMODERN - Y " 7IM.INDEX.GETFNMODERN - -  \|v  " 5IM.INDEX.GETFNMODERN - -  " 5IM.INDEX.GETFNMODERN - - " 3IM.INDEX.GETFN  dG" -.IM.INDEX.GETFNMODERN - " -IM.INDEX.GETFNMODERN - C -kO , - .3/s z \ No newline at end of file +@PP**PP  @0.75 0 0 +This fill convention also takes into account all polygons in POINTS, if it specifies multiple polygons. +(FILLCIRCLE CENTERX CENTERY RADIUS TEXTURE STREAM) [Function] +Fills in a circular area of radius RADIUS about the point (CENTERX,CENTERY) in STREAM with TEXTURE. STREAM's position is left at (CENTERX,CENTERY). +(DSPRESET STREAM) [Function] +Sets the X coordinate of STREAM to its left margin, sets its Y coordinate to the top of the clipping region minus the font ascent. For a display stream, this also fills its destination bitmap with its background texture. +(DSPNEWPAGE STREAM) [Function] +Starts a new page. The X coordinate is set to the left margin, and the Y coordinate is set to the top margin plus the linefeed. +(CENTERPRINTINREGION EXP REGION STREAM) [Function] +Prints EXP so that is it centered within REGION of the STREAM. If REGION is NIL, EXP will be centered in the clipping region of STREAM. +Drawing and Shading Grids +1 + +A grid is a partitioning of an arbitrary coordinate system (hereafter referred to as the "source system") into rectangles. This section describes functions that operate on grids. It includes functions to draw the outline of a grid, to translate between positions in a source system and grid coordinates (the coordinates of the rectangle which contains a given position), and to shade grid rectangles. A grid is defined by its "unit grid," a region (called a grid specification) which is the origin rectangle of the grid in terms of the source system. Its LEFT field is interpreted as the X-coordinate of the left edge of the origin rectangle, its BOTTOM field is the Y-coordinate of the bottom edge of the origin rectangle, its WIDTH is the width of the grid rectangles, and its HEIGHT is the height of the grid rectangles. +(GRID GRIDSPEC WIDTH HEIGHT BORDER STREAM GRIDSHADE) [Function] +Outlines the grid defined by GRIDSPEC which is WIDTH rectangles wide and HEIGHT rectangles high on STREAM. Each box in the grid has a border within it that is BORDER points on each side; so the resulting lines in the grid are 2*BORDER thick. If BORDER is the atom POINT, instead of a border the lower left point of each grid rectangle will be turned on. If GRIDSHADE is non-NIL, it should be a texture and the border lines will be drawn using that texture. +(SHADEGRIDBOX X Y SHADE OPERATION GRIDSPEC GRIDBORDER +STREAM) [Function] +Shades the grid rectangle (X,Y) of GRIDSPEC with texture SHADE using OPERATION on STREAM. GRIDBORDER is interpreted the same as for GRID. +The following two functions map from the X,Y coordinates of the source system into the grid X,Y coordinates: +(GRIDXCOORD XCOORD GRIDSPEC) [Function] +Returns the grid X-coordinate (in the grid specified by GRIDSPEC) that contains the source system X-coordinate XCOORD. +(GRIDYCOORD YCOORD GRIDSPEC) [Function] +Returns the grid Y-coordinate (in the grid specified by GRIDSPEC) that contains the source system Y-coordinate YCOORD. +The following two functions map from the grid X,Y coordinates into the X,Y coordinates of the source system: +(LEFTOFGRIDCOORD GRIDX GRIDSPEC) [Function] +Returns the source system X-coordinate of the left edge of a grid rectangle at grid X-coordinate GRIDX (in the grid specified by GRIDSPEC). +(BOTTOMOFGRIDCOORD GRIDY GRIDSPEC) [Function] +Returns the source system Y-coordinate of the bottom edge of a grid rectangle at grid Y-coordinate GRIDY (in the grid specified by GRIDSPEC). +Display Streams +1 + +Display streams (image streams of type DISPLAY) are used to control graphic output operations to a bitmap, known as the "destination" bitmap of the display stream. For each window on the screen, there is an associated display stream which controls graphics operations to a specific part of the screen bitmap. Any of the functions that take a display stream will also take a window, and use the associated display stream. Display streams can also have a destination bitmap that is not connected to any window or display device. +(DSPCREATE DESTINATION) [Function] +Creates and returns a display stream. If DESTINATION is specified, it is used as the destination bitmap, otherwise the screen bitmap is used. +(DSPDESTINATION DESTINATION DISPLAYSTREAM) [Function] +Returns the current destination bitmap for DISPLAYSTREAM, setting it to DESTINATION if non-NIL. DESTINATION can be either the screen bitmap, or an auxilliary bitmap in order to construct figures, possibly save them, and then display them in a single operation. +Warning: The window system maintains the destination of a window's display stream. Users should be very careful about changing this field. +(DSPXOFFSET XOFFSET DISPLAYSTREAM) [Function] +(DSPYOFFSET YOFFSET DISPLAYSTREAM) [Function] +Each display stream has its own coordinate system, separate from the coordinate system of its destination bitmap. Having the coordinate system local to the display stream allows objects to be displayed at different places by translating the display stream's coordinate system relative to its destination bitmap. This local coordinate system is defined by the X offset and Y offset. +DSPXOFFSET returns the current X offset for DISPLAYSTREAM, the X origin of the display stream's coordinate system in the destination bitmap's coordinate system. It is set to XOFFSET if non-NIL. +DSPYOFFSET returns the current Y offset for DISPLAYSTREAM, the Y origin of the display stream's coordinate system in the destination bitmap's coordinate system. It is set to YOFFSET if non-NIL. +The X offset and Y offset for a display stream are both initially 0 (no X or Y-coordinate translation). +Warning: The window system maintains the X and Y offset of a window's display stream. Users should be very careful about changing these fields. +(DSPTEXTURE TEXTURE DISPLAYSTREAM) [Function] +Returns the current texture used as the background pattern for DISPLAYSTREAM. It is set to TEXTURE if non-NIL. Initially the value of WHITESHADE. +(DSPSOURCETYPE SOURCETYPE DISPLAYSTREAM) [Function] +Returns the current BITBLT sourcetype used when printing characters to the display stream. It is set to SOURCETYPE, if non-NIL. Must be either INPUT or INVERT. Initially INPUT. +(DSPSCROLL SWITCHSETTING DISPLAYSTREAM) [Function] +Returns the current value of the "scroll flag," a flag that determines the scrolling behavior of the display stream; either ON or OFF. If ON, the bits in the display streams's destination bitmap are moved after any linefeed that moves the current position out of the destination bitmap. Any bits moved out of the current clipping region are lost. Does not adjust the X offset, Y offset, or clipping region of the display stream. Initially OFF. +Sets the scroll flag to SWITCHSETTING, if non-NIL. +Note: The word "scrolling" also describes the use of "scroll bars" on the left and bottom of a window to move an object displayed in a window. +Each window has an associated display stream. To get the window of a particular display stream, use WFROMDS: +(WFROMDS DISPLAYSTREAM DONTCREATE) [Function] +Returns the window associated with DISPLAYSTREAM, creating a window if one does not exist (and DONTCREATE is NIL). Returns NIL if the destination of DISPLAYSTREAM is not a screen bitmap that supports a window system. +If DONTCREATE is non-NIL, WFROMDS will never create a window, and returns NIL if DISPLAYSTREAM does not have an associated window. +TTYDISPLAYSTREAM calls WFROMDS with DONTCREATE = T, so it will not create a window unnecessarily. Also, if WFROMDS does create a window, it calls CREATEW with NOOPENFLG = T. +(DSPBACKUP WIDTH DISPLAYSTREAM) [Function] +Backs up DISPLAYSTREAM over a character which is WIDTH screen points wide. DSPBACKUP fills the backed over area with the display stream's background texture and decreases the X position by WIDTH. If this would put the X position less than DISPLAYSTREAM's left margin, its operation is stopped at the left margin. It returns T if any bits were written, NIL otherwise. +Fonts +1 + +A font is the collection of images that are printed or displayed when characters are output to a graphic output device. Some simple displays and printers can only print characters using one font. Bitmap displays and graphic printers can print characters using a large number of fonts. +Fonts are identified by a distinctive style or family (such as Modern or Classic), a size (such as 10 points), and a face (such as bold or italic). Fonts also have a rotation that indicates the orientation of characters on the screen or page. A normal horizontal font (also called a portrait font) has a rotation of 0; the rotation of a vertical (landscape) font is 90 degrees. While any combination can be specified, in practice the user will find that only certain combinations of families, sizes, faces, and rotations are available for any graphic output device. +To specify a font to the functions described below, a FAMILY is represented by a literal atom, a SIZE by a positive integer, and a FACE by a three-element list of the form (WEIGHT SLOPE EXPANSION). WEIGHT, which indicates the thickness of the characters, can be BOLD, MEDIUM, or LIGHT; SLOPE can be ITALIC or REGULAR; and EXPANSION can be REGULAR, COMPRESSED, or EXPANDED, indicating how spread out the characters are. For convenience, faces may also be specified by three-character atoms, where each character is the first letter of the corresponding field. Thus, MRR is a synonym for (MEDIUM REGULAR REGULAR). In addition, certain common face combinations may be indicated by special literal atoms: +STANDARD = (MEDIUM REGULAR REGULAR) = MRR +ITALIC = (MEDIUM ITALIC REGULAR) = MIR +BOLD = (BOLD REGULAR REGULAR) = BRR +BOLDITALIC = (BOLD ITALIC REGULAR) = BIR +Interlisp represents all the information related to a font in an object called a font descriptor. Font descriptors contain the family, size, etc. properties used to represent the font. In addition, for each character in the font, the font descriptor contains width information for the character and (for display fonts) a bitmap containing the picture of the character. +The font functions can take fonts specified in a variety of different ways. DSPFONT, FONTCREATE, FONTCOPY, etc. can be applied to font descriptors, "font lists" such as '(MODERN 10), image streams (coerced to its current font), or windows (coerced to the current font of its display stream). The printout command ".FONT" will also accept fonts specified in any of these forms. +In general font files use the following format: +The family name (e.g., Modern); a two digit size (e.g., 08); a three letter Face (e.g., BIR, for Bold Italic Regular); the letter C followed by the font's character set in base 8 (e.g., C41); and finally an extension (e.g., Displayfont). +((SKETCH a% figure% from% a% document VERSION 3 PRIRANGE (19 . 0) SKETCHCONTEXT ((ROUND 1 BLACK) (GACHA 10 (MEDIUM REGULAR REGULAR)) (CENTER BASELINE) (CURVE 18.0 8) NIL NIL (CENTER CENTER) (NIL NIL NIL) T NIL NIL 1 NIL)) ((0.05 13.0 (PRI 3)) (TEXT (88.0 . 120.0) ("Family") 1 (CENTER BASELINE) (MODERN 10 (MEDIUM REGULAR REGULAR)) ((72.5 116.5 31 13)) BLACK)) ((0.05 13.0 (PRI 4)) (TEXT (112.0 . 176.0) ("Size") 1 (CENTER BASELINE) (MODERN 10 (MEDIUM REGULAR REGULAR)) ((102.5 172.5 19 13)) BLACK)) ((0.05 13.0 (PRI 5)) (TEXT (132.0 . 120.0) ("Face") 1 (CENTER BASELINE) (MODERN 10 (MEDIUM REGULAR REGULAR)) ((121.5 116.5 21 13)) BLACK)) ((0.05 13.0 (PRI 7)) (TEXT (152.0 . 144.0) ("Modern08-BIR-C41.Displayfont") 1 (CENTER BASELINE) (TERMINAL 10 (MEDIUM REGULAR REGULAR)) ((68.0 140.5 168 13)) BLACK)) ((0.05 13.0 (PRI 8)) (TEXT (192.0 . 168.0) ("CharacterSet (base 8)") 1 (CENTER BASELINE) (MODERN 10 (MEDIUM REGULAR REGULAR)) ((142.5 164.5 99 13)) BLACK)) ((0.05 13.0 (PRI 9)) (TEXT (208.0 . 120.0) ("Extension") 1 (CENTER BASELINE) (MODERN 10 (MEDIUM REGULAR REGULAR)) ((185.0 116.5 46 13)) BLACK)) ((0.0 6.0 (PRI 11)) (WIRE ((88 . 128) (88 . 140)) (ROUND 1 BLACK) (NIL (SOLID 18.0 8)) NIL 1 NIL (NIL ((88 . 140) (90.47214 . 132.3916) (85.52786 . 132.3916))))) ((0.0 6.0 (PRI 13)) (WIRE ((208 . 128) (208 . 140)) (ROUND 1 BLACK) (NIL (SOLID 18.0 8)) NIL 1 NIL (NIL ((208 . 140) (210.4721 . 132.3916) (205.5279 . 132.3916))))) ((0.0 6.0 (PRI 14)) (WIRE ((156 . 164) (156 . 152)) (ROUND 1 BLACK) (NIL (SOLID 18.0 8)) NIL 1 NIL (NIL ((156 . 152) (153.5279 . 159.6085) (158.4721 . 159.6085))))) ((0.0 6.0 (PRI 15)) (WIRE ((112 . 164) (112 . 152)) (ROUND 1 BLACK) (NIL (SOLID 18.0 8)) NIL 1 NIL (NIL ((112 . 152) (109.5279 . 159.6085) (114.4721 . 159.6085))))) ((0.05 11.0 (PRI 16)) (TEXT (112.0 . 168.0) ("(two digits)") 1 (CENTER BASELINE) (MODERN 8 (MEDIUM REGULAR REGULAR)) ((90.5 165.5 43 11)) BLACK)) ((0.0 10.0 (PRI 18)) (WIRE ((120 . 144) (120 . 136) (140 . 136) (140 . 144)) (ROUND 1 BLACK) NIL NIL 1 NIL NIL)) ((0.0 6.0 (PRI 19)) (WIRE ((132 . 124) (132 . 136)) (ROUND 1 BLACK) NIL NIL 1 NIL NIL))) +(68.0 116.0 174.0 69.0) +1.0 +4 + + + +(FONTCREATE FAMILY SIZE FACE ROTATION DEVICE NOERRORFLG CHARSET) [Function] +Returns a font descriptor for the specified font. FAMILY is a litatom specifying the font family. SIZE is an integer indicating the size of the font in points. FACE specifies the face characteristics in one of the formats listed above; if FACE is NIL, STANDARD is used. ROTATION, which specifies the orientation of the font, is 0 (or NIL) for a portrait font and 90 for a landscape font. DEVICE indicates the output device for the font, and can be any image stream type , such as DISPLAY, INTERPRESS, etc. DEVICE may also be an image stream, in which case the type of the stream determines the font device. DEVICE defaults to DISPLAY. +The FAMILY argument to FONTCREATE may also be a list, in which case it is interpreted as a font-specification quintuple, a list of the form (FAMILY SIZE FACE ROTATION DEVICE). Thus, (FONTCREATE '(GACHA 10 BOLD)) is equivalent to (FONTCREATE 'GACHA 10 'BOLD). FAMILY may also be a font descriptor, in which case that descriptor is simply returned. +If a font descriptor has already been created for the specified font, FONTCREATE simply returns it. If it has not been created, FONTCREATE has to read the font information from a font file that contains the information for that font. The name of an appropriate font file, and the algorithm for searching depends on the device that the font is for, and is described in more detail below. If an appropriate font file is found, it is read into a font descriptor. If no file is found, for DISPLAY fonts FONTCREATE looks for fonts with less face information and fakes the remaining faces (such as by doubling the bit pattern of each character or slanting it). For hardcopy printer fonts, there is no acceptable faking algorithm. +If no acceptable font is found, the action of FONTCREATE is determined by NOERRORFLG. If NOERRORFLG is NIL, it generates a FONT NOT FOUND error with the offending font specification; otherwise, FONTCREATE returns NIL. +CHARSET is the character set which will be read to create the font. Defaults to 0. For more information on character sets, see NS Characters. +(FONTP X) [Function] +Returns X if X is a font descriptor; NIL otherwise. +(FONTPROP FONT PROP) [Function] +Returns the value of the PROP property of font FONT. The following font properties are recognized: + FAMILY The style of the font, represented as a literal atom, such as CLASSIC or MODERN. + SIZE A positive integer giving the size of the font, in printer's points (1/72 of an inch). + WEIGHT The thickness of the characters; one of BOLD, MEDIUM, or LIGHT. + SLOPE The "slope" of the characters in the font; one of ITALIC or REGULAR. + EXPANSION The extent to which the characters in the font are spread out; one of REGULAR, COMPRESSED, or EXPANDED. Most available fonts have EXPANSION = REGULAR. + FACE A three-element list of the form (WEIGHT SLOPE EXPANSION), giving all of the typeface parameters. + ROTATION An integer that gives the orientation of the font characters on the screen or page, in degrees. A normal horizontal font (also called a portrait font) has a rotation of 0; the rotation of a vertical (landscape) font is 90. + DEVICE The device that the font can be printed on; one of DISPLAY, INTERPRESS, etc. + ASCENT An integer giving the maximum height of any character in the font from its base line (the printing position). The top line will be at BASELINE+ASCENT-1. + DESCENT An integer giving the maximum extent of any character below the base line, such as the lower part of a "p". The bottom line of a character will be at BASELINE-DESCENT. + HEIGHT Equal to ASCENT + DESCENT. + SPEC The (FAMILY SIZE FACE ROTATION DEVICE) quintuple by which the font is known to Lisp. + DEVICESPEC The (FAMILY SIZE FACE ROTATION DEVICE) quintuple that identifies what will be used to represent the font on the display or printer. It will differ from the SPEC property only if an implicit coercion is done to approximate the specified font with one that actually exists on the device. + SCALE The units per printer's point (1/72 of an inch) in which the font is measured. For example, this is 35.27778 (the number of micas per printer's point) for Interpress fonts, which are measured in terms of micas. +(FONTCOPY OLDFONT PROP1 VAL1 PROP2 VAL2 ...) [NoSpread Function] +Returns a font descriptor that is a copy of the font OLDFONT, but which differs from OLDFONT in that OLDFONT's properties are replaced by the specified properties and values. Thus, (FONTCOPY FONT 'WEIGHT 'BOLD 'DEVICE 'INTERPRESS) will return a bold Interpress font with all other properties the same as those of FONT. FONTCOPY accepts the properties FAMILY, SIZE, WEIGHT, SLOPE, EXPANSION, FACE, ROTATION, and DEVICE. If the first property is a list, it is taken to be the PROP1 VAL1 PROP2 VAL2 ... sequence. Thus, (FONTCOPY FONT '(WEIGHT BOLD DEVICE INTERPRESS)) is equivalent to the example above. +If the property NOERROR is specified with value non-NIL, FONTCOPY will return NIL rather than causing an error if the specified font cannot be created. +(FONTSAVAILABLE FAMILY SIZE FACE ROTATION DEVICE +CHECKFILESTOO?) [Function] +Returns a list of available fonts that match the given specification. FAMILY, SIZE, FACE, ROTATION, and DEVICE are the same as for FONTCREATE. Additionally, any of them can be the atom *, in which case all values of that field are matched. +If CHECKFILESTOO? is NIL, only fonts already loaded into virtual memory will be considered. If CHECKFILESTOO? is non-NIL, the font directories for the specified device will be searched. When checking font files, the ROTATION is ignored. +Note: The search is conditional on the status of the server which holds the font. Thus a file server crash may prevent FONTCREATE from finding a file that an earlier FONTSAVAILABLE returned. +Each element of the list returned will be of the form (FAMILY SIZE FACE ROTATION DEVICE). +Examples: +(FONTSAVAILABLE 'MODERN 10 'MRR 0 'DISPLAY) +will return ((MODERN 10 (MEDIUM REGULAR REGULAR) 0 DISPLAY)) if the regular Modern 10 font for the display is in virtual memory; NIL otherwise. +(FONTSAVAILABLE '* 14 '* '* 'INTERPRESS T) +will return a list of all the size 14 Interpress fonts, whether they are in virtual memory or in font files. +(SETFONTDESCRIPTOR FAMILY SIZE FACE ROTATION DEVICE FONT) [Function] +Indicates to the system that FONT is the font that should be associated with the FAMILY SIZE FACE ROTATION DEVICE characteristics. If FONT is NIL, the font associated with these characteristics is cleared and will be recreated the next time it is needed. As with FONTPROP and FONTCOPY, FONT is coerced to a font descriptor if it is not one already. +This functions is useful when it is desirable to simulate an unavailable font or to use a font with characteristics different from the interpretations provided by the system. +(DEFAULTFONT DEVICE FONT %) [Function] +Returns the font that would be used as the default (if NIL were specified as a font argument) for image stream type DEVICE. If FONT is a font descriptor, it is set to be the default font for DEVICE. +(CHARWIDTH CHARCODE FONT) [Function] +CHARCODE is an integer that represents a valid character (as returned by CHCON1). Returns the amount by which an image stream's X-position will be incremented when the character is printed. +(CHARWIDTHY CHARCODE FONT) [Function] +Like CHARWIDTH, but returns the Y component of the character's width, the amount by which an image stream's Y-position will be incremented when the character is printed. This will be zero for most characters in normal portrait fonts, but may be non-zero for landscape fonts or for vector-drawing fonts. +(STRINGWIDTH STR FONT FLG RDTBL) [Function] +Returns the amount by which a stream's X-position will be incremented if the printname for the Interlisp-D object STR is printed in font FONT. If FONT is NIL, DEFAULTFONT is used as FONT. If FONT is an image stream, its font is used. If FLG is non-NIL, the PRIN2-pname of STR with respect to the readtable RDTBL is used. +(STRINGREGION STR STREAM PRIN2FLG RDTBL) [Function] +Returns the region occupied by STR if it were printed at the current location in the image stream STREAM. This is useful, for example, for determining where text is in a window to allow the user to select it. The arguments PRIN2FLG and RDTBL are passed to STRINGWIDTH. +Note: STRINGREGION does not take into account any carriage returns in the string, or carriage returns that may be automatically printed if STR is printed to STREAM. Therefore, the value returned is meaningless for multi-line strings. +The following functions allow the user to access and change the bitmaps for individual characters in a display font. Note: Character code 256 can be used to access the "dummy" character, used for characters in the font with no bitmap defined. +(GETCHARBITMAP CHARCODE FONT) [Function] +Returns a bitmap containing a copy of the image of the character CHARCODE in the font FONT. +(PUTCHARBITMAP CHARCODE FONT NEWCHARBITMAP NEWCHARDESCENT) [Function] +Changes the bitmap image of the character CHARCODE in the font FONT to the bitmap NEWCHARBITMAP. If NEWCHARDESCENT is non-NIL, the descent of the character is changed to the value of NEWCHARDESCENT. +(EDITCHAR CHARCODE FONT) [Function] +Calls the bitmap editor (EDITBM) on the bitmap image of the character CHARCODE in the font FONT. CHARCODE can be a character code (as returned by CHCON1) or an atom or string, in which case the first character of CHARCODE is used. +(WRITESTRIKEFONTFILE(WRITESTRIKEFONTFILE (Function) NIL NIL (4) NIL) FONT CHARSET FILENAME) [Function] +Takes a display font font descriptor(FONT% DESCRIPTOR NIL font% descriptor NIL (4) NIL) and a character set number, and writes that character set into a file suitable for reading in again. Note that the font descriptor's current state is used (which was perhaps modified by INSPECTing the datum), so this provides a mechanism for creating/modifying new fonts. +For example: +(WRITESTRIKEFONTFILE (FONTCREATE 'GACHA 10) 0 '{DSK}Magic10-MRR-C0.DISPLAYFONT) +If your DISPLAYFONTDIRECTORIES(DISPLAYFONTDIRECTORIES (Variable) NIL NIL (4) NIL) includes {DSK}, then a subsequent (FONTCREATE 'MAGIC 10) will create a new font descriptor whose appearance is the same as the old Gacha font descriptor. +However, the new font is identical to the old one in appearance only. The individual datatype fields and bitmap may not be the same as those in the old font descriptor, due to peculiarities of different font file formats. + +Font Files and Font Directories +1 + +If FONTCREATE is called to create a font that has not been loaded into Interlisp, FONTCREATE has to read the font information from a font file that contains the information for that font. For printer devices, the font files have to contain width information for each character in the font. For display fonts, the font files have to contain, in addition, bitmap images for each character in the fonts. The font file names, formats, and searching algorithms are different for each device. There are a set of variables for each device, that determine the directories that are searched for font files. All of these variables must be set before Interlisp can auto-load font files. These variables should be initialized in the site-specific INIT file. +DISPLAYFONTDIRECTORIES [Variable] +Value is a list of directories searched to find font bitmap files for display fonts. +DISPLAYFONTEXTENSIONS [Variable] +Value is a list of file extensions used when searching DISPLAYFONTDIRECTORIES for display fonts. Initially set to (DISPLAYFONT), but when using older font files it may be necessary to add STRIKE and AC to this list. +INTERPRESSFONTDIRECTORIES [Variable] +Value is a list of directories searched to find font widths files for Interpress fonts. +PRESSFONTWIDTHSFILES [Variable] +Value is a list of files (not directories) searched to find font widths files for Press fonts. Press font widths are packed into large files (usually named FONTS.WIDTHS). +Font Profiles +1 + +PRETTYPRINT contains a facility for printing different elements (user functions, system functions, clisp words, comments, etc.) in different fonts to emphasize (or deemphasize) their importance, and in general to provide for a more pleasing appearance. Of course, in order to be useful, this facility requires that the user is printing on a device (such as a bitmapped display or a laser printer) which supports multiple fonts. +PRETTYPRINT signals font changes by inserting into the file a user-defined escape sequence (the value of the variable FONTESCAPECHAR) followed by the character code which specifies, by number, which font to use, i.e. +A for font number 1, etc. Thus, if FONTESCAPECHAR were the character +F, +F+C would be output to change to font 3, +F+A to change to font 1, etc. If FONTESCAPECHAR consists of characters which are separator charactors in FILERDTBL, then a file with font changes in it can also be loaded back in. +Currently, PRETTYPRINT uses the following font classes. The user can specify separate fonts for each of these classes, or use the same font for several different classes. + LAMBDAFONT The font for printing the name of the function being prettyprinted, before the actual definition (usually a large font). + CLISPFONT If CLISPFLG is on, the font for printing any clisp words, i.e. atoms with property CLISPWORD. + COMMENTFONT The font used for comments. + USERFONT The font for the name of any function in the file, or any member of the list FONTFNS. + SYSTEMFONT The font for any other (defined) function. + CHANGEFONT The font for an expression marked by the editor as having been changed. + PRETTYCOMFONT The font for the operand of a file package command. + DEFAULTFONT The font for everything else. +Note that not all combinations of fonts will be aesthetically pleasing (or even readable!) and the user may have to experiment to find a compatible set. +Although in some implementations LAMBDAFONT et al. may be defined as variables, one should not set them directly, but should indicate what font is to be used for each class by calling the function FONTPROFILE: +(FONTPROFILE PROFILE) [Function] +Sets up the font classes as determined by PROFILE, a list of elements which defines the correspondence between font classes and specific fonts. Each element of PROFILE is a list of the form: +(FONTCLASS FONT# DISPLAYFONT PRESSFONT INTERPRESSFONT) +FONTCLASS is the font class name and FONT# is the font number for that class. For each font class name, the escape sequence will consist of FONTESCAPECHAR followed by the character code for the font number, e.g. +A for font number 1, etc. +If FONT# is NIL for any font class, the font class named DEFAULTFONT (which must always be specified) is used. Alternatively, if FONT# is the name of a previously defined font class, this font class will be equivalenced to the previously defined one. +DISPLAYFONT, PRESSFONT, and INTERPRESSFONT are font specifications (of the form accepted by FONTCREATE) for the fonts to use when printing to the display and to Press and Interpress printers respectively. +FONTPROFILE [Variable] +This is the variable used to store the current font profile, in the form accepted by the function FONTPROFILE. Note that simply editing this value will not change the fonts used for the various font classes; it is necessary to execute (FONTPROFILE FONTPROFILE) to install the value of this variable. +The process of printing with multiple fonts is affected by a large number of variables: FONTPROFILE, FILELINELENGTH, PRETTYLCOM, etc. To facilitate switching back and forth between various sets of values for the font variables, Interlisp supports the idea of named "font configurations" encapsulating the values of all relevant variables. +To create a new font configuration, set all "relevant" variables to the values you want, and then call FONTNAME to save them (on the variable FONTDEFS) under a given name. To install a particular font configuration, call FONTSET giving it your name. To change the values in a saved font configuration, edit the value of the variable FONTDEFS. +Note: The list of variables saved by FONTNAME is stored in the variable FONTDEFSVARS. This can be changed by the user. +(FONTSET NAME) [Function] +Installs font configuration for NAME. Also evaluates (FONTPROFILE FONTPROFILE) to install the font classes as specified in the new value of the variable FONTPROFILE. Generates an error if NAME not previously defined. +FONTDEFSVARS [Variable] +The list of variables to be packaged by a FONTNAME. Initially FONTCHANGEFLG, FILELINELENGTH, COMMENTLINELENGTH, FIRSTCOL, PRETTYLCOM, LISTFILESTR, and FONTPROFILE. +FONTDEFS [Variable] +An association list of font configurations. FONTDEFS is a list of elements of form (NAME . PARAMETER-PAIRS). To save a configuration on a file after performing a FONTNAME to define it, the user could either save the entire value of FONTDEFS, or use the ALISTS file package command to dump out just the one configuration. +FONTESCAPECHAR [Variable] +The character or string used to signal the start of a font escape sequence. +FONTCHANGEFLG [Variable] +If T, enables fonts when prettyprinting. If NIL, disables fonts. ALL indicates that all calls to CHANGEFONT are executed. +LISTFILESTR [Variable] +In Interlisp-10, passed to the operating system by LISTFILES. Can be used to specify subcommands to the LIST command, e.g. to establish correspondance between font number and font name. +COMMENTLINELENGTH [Variable] +Since comments are usually printed in a smaller font, COMMENTLINELENGTH is provided to offset the fact that Interlisp does not know about font widths. When FONTCHANGEFLG = T, CAR of COMMENTLINELENGTH is the linelength used to print short comments, i.e. those printed in the right margin, and CDR is the linelength used when printing full width comments. +(CHANGEFONT FONT STREAM) [Function] +Executes the operations on STREAM to change to the font FONT. For use in PRETTYPRINTMACROS. +Image Objects +1 + +An Image Object is an object that includes information about an image, such as how to display it, how to print it, and how to manipulate it when it is included in a collection of images (such as a document). More generally, it enables you to include one kind of image, with its own semantics, layout rules, and editing paradigms, inside another kind of image. Image Objects provide a general-purpose interface between image users who want to manipulate arbitrary images, and image producers, who create images for use, say, in documents. +Images are encapsulated inside a uniform barrier%the IMAGEOBJ data type. From the outside, you communicate to the image by calling a standard set of functions. For example, calling one function tells you how big the image is; calling another causes the image object to be displayed where you tell it, and so on. Anyone who wants to create images for general use can implement his own brand of IMAGEOBJ. IMAGEOBJs have been implemented (in library packages) for bitmaps, menus, annotations, graphs, and sketches. +Image Objects were originally implemented to support inserting images into TEdit text files, but the facility is available for use by any tools that manipulate images. The Image Object interface allows objects to exist in TEdit documents and be edited with their own editor. It also provides a facility in which objects can be shift-selected (or "copy-selected") between TEdit and non-TEdit windows. For example, the Image Objects interface allows you to copy-select graphs from a Grapher window into a TEdit window. The source window (where the object comes from) does not have to know what sort of window the destination window (where the object is inserted) is, and the destination does not have to know where the insertion comes from. +A new data type, IMAGEOBJ, contains the data and the procedures necessary to manipulate an object that is to be manipulated in this way. IMAGEOBJs are created with the function IMAGEOBJCREATE (below). +Another new data type, IMAGEFNS, is a vector of the procedures necessary to define the behavior of a type of IMAGEOBJ. Grouping the operations in a separate data type allows multiple instances of the same type of image object to share procedure vectors. The data and procedure fields of an IMAGEOBJ have a uniform interface through the function IMAGEOBJPROP. IMAGEFNS are created with the function IMAGEFNSCREATE: +(IMAGEFNSCREATE(IMAGEFNSCREATE (Function) NIL NIL ("27") 27) DISPLAYFN IMAGEBOXFN PUTFN GETFN COPYFN BUTTONEVENTINFN COPYBUTTONEVENTINFN WHENMOVEDFN WHENINSERTEDFN WHENDELETEDFN WHENCOPIEDFN WHENOPERATEDONFN PREPRINTFN %) [Function] +Returns an IMAGEFNS object that contains the functions necessary to define the behavior of an IMAGEOBJ. +The arguments DISPLAYFN through PREPRINTFN should all be function names to be stored as the "methods" of the IMAGEFNS. The purpose of each IMAGEFNS method is described below. +Note: Image objects must be "registered" before they can be read by TEdit or HREAD. IMAGEFNSCREATE implicitly registers its GETFN argument. +(IMAGEOBJCREATE(IMAGEOBJCREATE (Function) NIL NIL ("27") 27) OBJECTDATUM IMAGEFNS) [Function] +Returns an IMAGEOBJ that contains the object datum OBJECTDATUM and the operations vector IMAGEFNS. OBJECTDATUM can be arbitrary data. +(IMAGEOBJPROP(IMAGEOBJPROP (Function) NIL NIL ("27") 28) IMAGEOBJECT PROPERTY NEWVALUE) [NoSpread Function] +Accesses and sets the properties of an IMAGEOBJ. Returns the current value of the PROPERTY property of the image object IMAGEOBJECT. If NEWVALUE is given, the property is set to it. +IMAGEOBJPROP can be used on the system properties OBJECTDATUM, DISPLAYFN, IMAGEBOXFN, PUTFN, GETFN, COPYFN, BUTTONEVENTINFN, COPYBUTTONEVENTINFN, WHENOPERATEDONFN, and PREPRINTFN. Additionally, it can be used to save arbitrary properties on an IMAGEOBJ. +(IMAGEFNSP(IMAGEFNSP (Function) NIL NIL ("27") 28) X) [Function] +Returns X if X is an IMAGEFNS object, NIL otherwise. +(IMAGEOBJP(IMAGEOBJP (Function) NIL NIL ("27") 28) X) [Function] +Returns X if X is an IMAGEOBJ object, NIL otherwise. +IMAGEFNS Methods +Note: Many of the IMAGEFNS methods below are passed "host stream" arguments. The TEdit text editor passes the "text stream" (an object contain all of the information in the document being edited) as the "host stream" argument. Other editing programs that want to use image objects may want to pass the data structure being edited to the IMAGEFNS methods as the "host stream" argument. +(DISPLAYFN IMAGEOBJ IMAGESTREAM IMAGESTREAMTYPE HOSTSTREAM) [IMAGEFNS Method] +The DISPLAYFN method is called to display the object IMAGEOBJ at the current position on IMAGESTREAM. The type of IMAGESTREAM indicates whether the device is the display or some other image stream. +Note: When the DISPLAYFN method is called, the offset and clipping regions for the stream are set so the object's image is at (0,0), and only that image area can be modified. +(IMAGEBOXFN IMAGEOBJ IMAGESTREAM CURRENTX RIGHTMARGIN) [IMAGEFNS Method] +The IMAGEBOXFN method should return the size of the object as an IMAGEBOX, which is a data structure that describes the image laid down when an IMAGEOBJ is displayed in terms of width, height, and descender height. An IMAGEBOX has four fields: XSIZE, YSIZE, YDESC, and XKERN. XSIZE and YSIZE are the width and height of the object image. YDESC and XKERN give the position of the baseline and the left edge of the image relative to where you want to position it. For characters, the YDESC is the descent (height of the descender) and the XKERN is the amount of left kerning (note: TEdit doesn't support left kerning). +The IMAGEBOXFN looks at the type of the stream to determine the output device if the object's size changes from device to device. (For example, a bit-map object may specify a scale factor that is ignored when the bit map is displayed on the screen.) CURRENTX and RIGHTMARGIN allow an object to take account of its environment when deciding how big it is. If these fields are not available, they are NIL. +Note: TEdit calls the IMAGEBOXFN only during line formatting, then caches the IMAGEBOX as the BOUNDBOX property of the IMAGEOBJ. This avoids the need to call the IMAGEBOXFN when incomplete position and margin information is available. +(PUTFN IMAGEOBJ FILESTREAM) [IMAGEFNS Method] +The PUTFN method is called to save the object on a file. It prints a description on FILESTREAM that, when read by the corresponding GETFN method (see below), regenerates the image object. (TEdit and HPRINT take care of writing out the name of the GETFN.) +(GETFN FILESTREAM) [IMAGEFNS Method] +The GETFN method is called when the object is encountered on the file during input. It reads the description that was written by the PUTFN method and returns an IMAGEOBJ. +(COPYFN IMAGEOBJ SOURCEHOSTSTREAM TARGETHOSTSTREAM) [IMAGEFNS Method] +The COPYFN method is called during a copy-select operation. It should return a copy of IMAGEOBJ. If it returns the litatom DON'T, copying is suppressed. +(BUTTONEVENTINFN IMAGEOBJ WINDOWSTREAM SELECTION RELX RELY WINDOW HOSTSTREAM BUTTON) [IMAGEFNS Method] +The BUTTONEVENTINFN method is called when you press a mouse button inside the object. The BUTTONEVENTINFN decides whether or not to handle the button, to track the cursor in parallel with mouse movement, and to invoke selections or edits supported by the object (but see the COPYBUTTONEVENTINFN method below). If the BUTTONEVENTINFN returns NIL, TEdit treats the button press as a selection at its level. Note that when this function is first called, a button is down. The BUTTONEVENTINFN should also support the button-down protocol to descend inside of any composite objects with in it. In most cases, the BUTTONEVENTINFN relinquishes control (i.e., returns) when the cursor leaves its object's region. +When the BUTTONEVENTINFN is called, the window's clipping region and offsets have been changed so that the lower-left corner of the object's image is at (0,0), and only the object's image can be changed. The selection is available for changing to fit your needs; the mouse button went down at (RELX,RELY) within the object's image. You can affect how TEdit treats the selection by returning one of several values. If you return NIL, TEdit forgets that you selected an object; if you return the atom DON'T, TEdit doesn't permit the selection; if you return the atom CHANGED, TEdit updates the screen. Use CHANGED to signal TEdit that the object has changed size or will have side effects on other parts of the screen image. +(COPYBUTTONEVENTINFN IMAGEOBJ WINDOWSTREAM) [IMAGEFNS Method] +The COPYBUTTONEVENTINFN method is called when you button inside an object while holding down a copy key. Many of the comments about BUTTONEVENTINFN apply here too. Also, see the discussion below about copying image objects between windows. +(WHENMOVEDFN IMAGEOBJ TARGETWINDOWSTREAM SOURCEHOSTSTREAM TARGETHOSTSTREAM) [IMAGEFNS Method] +The WHENMOVEDFN method provides hooks by which the object is notified when TEdit performs an operation (MOVEing) on the whole object. It allows objects to have side effects. +(WHENINSERTEDFN IMAGEOBJ TARGETWINDOWSTREAM SOURCEHOSTSTREAM TARGETHOSTSTREAM) [IMAGEFNS Method] +The WHENINSERTEDFN method provides hooks by which the object is notified when TEdit performs an operation (INSERTing) on the whole object. It allows objects to have side effects. +(WHENDELETEDFN IMAGEOBJ TARGETWINDOWSTREAM) [IMAGEFNS Method] +The WHENDELETEDFN method provides hooks by which the object is notified when TEdit performs an operation (DELETEing) on the whole object. It allows objects to have side effects. +(WHENCOPIEDFN IMAGEOBJ TARGETWINDOWSTREAM SOURCEHOSTSTREAM TARGETHOSTSTREAM) [IMAGEFNS Method] +The WHENCOPIEDFN method provides hooks by which the object is notified when TEdit performs an operation (COPYing) on the whole object. The WHENCOPIEDFN method is called in addition to (and after) the COPYFN method above. It allows objects to have side effects. +(WHENOPERATEDONFN IMAGEOBJ WINDOWSTREAM HOWOPERATEDON SELECTION HOSTSTREAM) [IMAGEFNS Method] +The WHENOPERATEDONFN method provides a hook for edit operations. HOWOPERATEDON should be one of SELECTED, DESELECTED, HIGHLIGHTED, and UNHILIGHTED. The WHENOPERATEDONFN differs from the BUTTONEVENTINFN because it is called when you extend a selection through the object. That is, the object is treated in toto as a TEdit character. HIGHLIGHTED refers to the selection being highlighted on the screen, and UNHIGHLIGHTED means that the highlighting is being turned off. +(PREPRINTFN IMAGEOBJ) [IMAGEFNS Method] +The PREPRINTFN method is called to convert the object into something that can be printed for inclusion in documents. It returns an object that the receiving window can print (using either PRIN1 or PRIN2,its choice) to obtain a character representation of the object. If the PREPRINTFN method is NIL, the OBJECTDATUM field of IMAGEOBJ itself is used. TEdit uses this function when you indicate that you want to print the characters from an object rather than the object itself (presumably using PRIN1 case). +Registering Image Objects +Each legitimate GETFN needs to be known to the system, to prevent various Trojan-horse problems and to allow the automatic loading of the supporting code for infrequently used IMAGEOBJs. To this end, there is a global list, IMAGEOBJGETFNS, that contains an entry for each GETFN. The existence of the entry marks the GETFN as legitimate; the entry itself is a property list, which can hold information about the GETFN. +No action needs to be taken for GETFNs that are currently in use: the function IMAGEFNSCREATE automatically adds its GETFN argument to the list. However, packages that support obsolete versions of objects may need to explicitly add the obsolete GETFNs. For example, TEdit supports bit-map IMAGEOBJs. Recently, a change was made in the format in which objects are stored; to retain compatibility with the old object format, there are now two GETFNs. The current GETFN is automatically on the list, courtesy of IMAGEFNSCREATE. However, the code file that supports the old bit-map objects contains the clause: (ADDVARS (IMAGEOBJGETFNS (OLDGETFNNAME))), which adds the old GETFN to IMAGEOBJGETFNS. +For a given GETFN, the entry on IMAGEOBJGETFNS may be a property list of information. Currently the only recognized property is FILE. +FILE is the name of the file that can be loaded if the GETFN isn't defined. This file should define the GETFN, along with all the other functions needed to support that kind of IMAGEOBJ. +For example, the bit-map IMAGEOBJ implemented by TEdit use the GETFN BMOBJ.GETFN2. Its entry on IMAGEOBJGETFNS is (BMOBJ.GETFN2 FILE IMAGEOBJ), indicating that the support code for bit-map image objects resides on the file IMAGEOBJ, and that the GETFN for them is BMOBJ.GETFN2. +This makes it possible to have entries for GETFNs whose supporting code isn't loaded%you might, for instance, have your init file add entries to IMAGEOBJGETFNS for the kinds of image objects you commonly use. The system's default reading method will automatically load the code when necessary. +Reading and Writing Image Objects on Files +Image Objects can be written out to files using HPRINT and read back using HREAD. The following functions can also be used: +(WRITEIMAGEOBJ(WRITEIMAGEOBJ (Function) NIL NIL ("27") 31) IMAGEOBJ STREAM) [Function] +Prints (using PRIN2) a call to READIMAGEOBJ, then calls the PUTFN for IMAGEOBJ to write it onto STREAM. During input, then, the call to READIMAGEOBJ is read and evaluated; it in turn reads back the object's description, using the appropriate GETFN. +(READIMAGEOBJ(READIMAGEOBJ (Function) NIL NIL ("27") 31) STREAM GETFN NOERROR) [Function] +Reads an IMAGEOBJ from STREAM, starting at the current file position. Uses the function GETFN after validating it (and loading support code, if necessary). +If the GETFN can't be validated or isn't defined, READIMAGEOBJ returns an "encapsulated image object", an IMAGEOBJ that safely encapsulates all of the information in the image object. An encapsulated image object displays as a rectangle that says, "Unknown IMAGEOBJ Type" and lists the GETFN's name. Selecting an encapsulated image object with the mouse causes another attempt to read the object from the file; this is so you can load any necessary support code and then get to the object. +Warning: You cannot save an encapsulated image object on a file because there isn't enough information to allow copying the description to the new file from the old one. +If NOERROR is non-NIL, READIMAGEOBJ returns NIL if it can't successfully read the object. +Copying Image Objects Between Windows +Copying between windows is implemented as follows: If a button event occurs in a window when a copy key is down, the window's COPYBUTTONEVENTFN window property is called. If this window supports copy-selection, it should track the mouse, indicating the item to be copied. When the button is released, the COPYBUTTONEVENTFN should create an image object out of the selected information, and call COPYINSERT to insert it in the current TTY window. COPYINSERT calls the COPYINSERTFN window property of the TTY window to insert this image object. Therefore, both the source and destination windows can determine how they handle copying image objects. +If the COPYBUTTONEVENTFN of a window is NIL, the BUTTONEVENTFN is called instead when a button event occurs in the window when a copy key is down, and copying from that window is not supported. If the COPYINSERTFN of the TTY window is NIL, COPYINSERT will turn the image object into a string (by calling the PREPRINTFN method of the image object) and insert it by calling BKSYSBUF. +COPYBUTTONEVENTFN [Window Property] +The COPYBUTTONEVENTFN of a window is called (if it exists) when a button event occurs in the window and a copy key is down. If no COPYBUTTONEVENTFN exists, the BUTTONEVENTFN is called. +COPYINSERTFN [Window Property] +The COPYINSERTFN of the "destination" window is called by COPYINSERT to insert something into the destination window. It is called with two arguments: the object to be inserted and the destination window. The object to be inserted can be a character string, an IMAGEOBJ, or a list of IMAGEOBJs and character strings. As a convention, the COPYINSERTFN should call BKSYSBUF if the object to be inserted insert is a character string. +(COPYINSERT IMAGEOBJ) [Function] +COPYINSERT inserts IMAGEOBJ into the window that currently has the TTY. If the current TTY window has a COPYINSERTFN, it is called, passing it IMAGEOBJ and the window as arguments. +If no COPYINSERTFN exists and if IMAGEOBJ is an image object, BKSYSBUF is called on the result of calling its PREPRINTFN on it. If IMAGEOBJ is not an image object, it is simply passed to BKSYSBUF . In this case, BKSYSBUF will call PRIN2 with a read table taken from the process associated with the TTY window. A window that wishes to use PRIN1 or a different read table must provide its own COPYINSERTFN to do this. +Implementation of Image Streams +1 + +Interlisp does all image creation through a set of functions and data structures for device-independent graphics, known popularly as DIG. DIG is implemented through the use of a special type of stream, known as an image stream. +An image stream, by convention, is any stream that has its IMAGEOPS field (described in detail below) set to a vector of meaningful graphical operations. Using image streams, you can write programs that draw and print on an output stream without regard to the underlying device, be it a window, a disk, or a printer. +To define a new image stream type, it is necessary to put information on the variable IMAGESTREAMTYPES: +IMAGESTREAMTYPES [Variable] +This variable describes how to create a stream for a given image stream type. The value of IMAGESTREAMTYPES is an association list, indexed by the image stream type (e.g., DISPLAY, INTERPRESS, etc.). The format of a single association list item is: +(IMAGETYPE + (OPENSTREAM OPENSTREAMFN) + (FONTCREATE FONTCREATEFN) + (FONTSAVAILABLE FONTSAVAILABLEFN)) +OPENSTREAMFN, FONTCREATEFN, and FONTSAVAILABLEFN are "image stream methods," device-dependent functions used to implement generic image stream operations. For Interpress image streams, the association list entry is: +(INTERPRESS + (OPENSTREAM OPENIPSTREAM) + (FONTCREATE \CREATEINTERPRESSFONT) + (FONTSAVAILABLE \SEARCHINTERPRESSFONTS)) +(OPENSTREAMFN FILE OPTIONS) [Image Stream Method] +FILE is the file name as it was passed to OPENIMAGESTREAM, and OPTIONS is the OPTIONS property list passed to OPENIMAGESTREAM. The result must be a stream of the appropriate image type. +(FONTCREATEFN FAMILY SIZE FACE ROTATION DEVICE) [Image Stream Method] +FAMILY is the family name for the font, e.g., MODERN. SIZE is the body size of the font, in printer's points. FACE is a three-element list describing the weight, slope, and expansion of the face desired, e.g., (MEDIUM ITALIC EXPANDED). ROTATION is how much the font is to be rotated from the normal orientation, in minutes of arc. For example, to print a landscape page, fonts have the rotation 5400 (90 degrees). The function's result must be a FONTDESCRIPTOR with the fields filled in appropriately. +(FONTSAVAILABLEFN FAMILY SIZE FACE ROTATION DEVICE) [Image Stream Method] +This function returns a list of all fonts agreeing with the FAMILY, SIZE, FACE, and ROTATION arguments; any of them may be wild-carded (i.e., equal to *, which means any value is acceptable). Each element of the list should be a quintuple of the form (FAMILY SIZE FACE ROTATION DEVICE). +Where the function looks is an implementation decision: the FONTSAVAILABLEFN for the display device looks at DISPLAYFONTDIRECTORIES, the Interpress code looks on INTERPRESSFONTDIRECTORIES, and implementors of new devices should feel free to introduce new search path variables. +As indicated above, image streams use a field that no other stream uses: IMAGEOPS. IMAGEOPS is an instance of the IMAGEOPS data type and contains a vector of the stream's graphical methods. The methods contained in the IMAGEOPS object can make arbitrary use of the stream's IMAGEDATA field, which is provided for their use, and may contain any data needed. +IMAGETYPE [IMAGEOPS Field] +Value is the name of an image type. Monochrome display streams have an IMAGETYPE of DISPLAY; color display streams are identified as (COLOR DISPLAY). The IMAGETYPE field is informational and can be set to anything you choose. +IMFONTCREATE [IMAGEOPS Field] +Value is the device name to pass to FONTCREATE when fonts are created for the stream. +The remaining fields are all image stream methods, whose value should be a device-dependent function that implements the generic operation. Most methods are called by a similarly-named function, e.g. the function DRAWLINE calls the IMDRAWLINE method. All coordinates that refer to points in a display device's space are measured in the device's units. (The IMSCALE method provides access to a device's scale.) For arguments that have defaults (such as the BRUSH argument of DRAWCURVE), the default is substituted for the NIL argument before it is passed to the image stream method. Therefore, image stream methods do not have to handle defaults. +(IMCLOSEFN STREAM) [Image Stream Method] +Called before a stream is closed with CLOSEF. This method should flush buffers, write header or trailer information, etc. +(IMDRAWLINE STREAM X1 Y1 X2 Y2 WIDTH OPERATION COLOR DASHING) [Image Stream Method] +Draws a line of width WIDTH from (X1, Y1) to (X2, Y2). See DRAWLINE. +(IMDRAWCURVE STREAM KNOTS CLOSED BRUSH DASHING) [Image Stream Method] +Draws a curve through KNOTS. See DRAWCURVE. +(IMDRAWCIRCLE STREAM CENTERX CENTERY RADIUS BRUSH DASHING) [Image Stream Method] +Draws a circle of radius RADIUS around (CENTERX, CENTERY). See DRAWCIRCLE. +(IMDRAWELLIPSE STREAM CENTERX CENTERY SEMIMINORRADIUS SEMIMAJORRADIUS ORIENTATION BRUSH DASHING) [Image Stream Method] +Draws an ellipse around (CENTERX, CENTERY). See DRAWELLIPSE. +(IMFILLPOLYGON STREAM POINTS TEXTURE) [Image Stream Method] +Fills in the polygon outlined by POINTS on the image stream STREAM, using the texture TEXTURE. See FILLPOLYGON. +(IMFILLCIRCLE STREAM CENTERX CENTERY RADIUS TEXTURE) [Image Stream Method] +Draws a circle filled with texture TEXTURE around (CENTERX, CENTERY). See FILLCIRCLE. +(IMBLTSHADE TEXTURE STREAM DESTINATIONLEFT DESTINATIONBOTTOM WIDTH HEIGHT OPERATION CLIPPINGREGION) [Image Stream Method] +The texture-source case of BITBLT. DESTINATIONLEFT, DESTINATIONBOTTOM, WIDTH, HEIGHT, and CLIPPINGREGION are measured in STREAM's units. This method is invoked by the functions BITBLT and BLTSHADE. +(IMBITBLT SOURCEBITMAP SOURCELEFT SOURCEBOTTOM STREAM DESTINATIONLEFT DESTINATIONBOTTOM WIDTH HEIGHT SOURCETYPE OPERATION TEXTURE CLIPPINGREGION CLIPPEDSOURCELEFT CLIPPEDSOURCEBOTTOM SCALE) [Image Stream Method] +Contains the bit-map-source cases of BITBLT. SOURCELEFT, SOURCEBOTTOM, CLIPPEDSOURCELEFT, CLIPPEDSOURCEBOTTOM, WIDTH, and HEIGHT are measured in pixels; DESTINATIONLEFT, DESTINATIONBOTTOM, and CLIPPINGREGION are in the units of the destination stream. +(IMSCALEDBITBLT SOURCEBITMAP SOURCELEFT SOURCEBOTTOM STREAM DESTINATIONLEFT DESTINATIONBOTTOM WIDTH HEIGHT SOURCETYPE OPERATION TEXTURE CLIPPINGREGION CLIPPEDSOURCELEFT CLIPPEDSOURCEBOTTOM SCALE) [Image Stream Method] +A scaled version of IMBITBLT. Each pixel in SOURCEBITMAP is replicated SCALE times in the X and Y directions; currently, SCALE must be an integer. +(IMMOVETO STREAM X Y) [Image Stream Method] +Moves to (X,Y). This method is invoked by the function MOVETO. If IMMOVETO is not supplied, a default method composed of calls to the IMXPOSITION and IMYPOSITION methods is used. +(IMSTRINGWIDTH STREAM STR RDTBL) [Image Stream Method] +Returns the width of string STR in STREAM's units, using STREAM's current font. This is envoked when STRINGWIDTH is passed a stream as its FONT argument. If IMSTRINGWIDTH is not supplied, it defaults to calling STRINGWIDTH on the default font of STREAM. +(IMCHARWIDTH STREAM CHARCODE) [Image Stream Method] +Returns the width of character CHARCODE in STREAM's units, using STREAM's current font. This is invoked when CHARWIDTH is passed a stream as its FONT argument. If IMCHARWIDTH is not supplied, it defaults to calling CHARWIDTH on the default font of STREAM. +(IMCHARWIDTHY STREAM CHARCODE) [Image Stream Method] +Returns the Y componant of the width of character CHARCODE in STREAM's units, using STREAM's current font. This is envoked when CHARWIDTHY is passed a stream as its FONT argument. If IMCHARWIDTHY is not supplied, it defaults to calling CHARWIDTHY on the default font of STREAM. +(IMBITMAPSIZE STREAM BITMAP DIMENSION) [Image Stream Method] +Returns the size that BITMAP will be when BITBLTed to STREAM, in STREAM's units. DIMENSION can be one of WIDTH, HEIGHT, or NIL, in which case the dotted pair (WIDTH . HEIGHT) will be returned. +This is envoked by BITMAPIMAGESIZE. If IMBITMAPSIZE is not supplied, it defaults to a method that multiplies the bitmap height and width by the scale of STREAM. +(IMNEWPAGE STREAM) [Image Stream Method] +Causes a new page to be started. The X position is set to the left margin, and the Y position is set to the top margin plus the linefeed. If not supplied, defaults to (\OUTCHAR STREAM (CHARCODE L)). Envoked by DSPNEWPAGE. +(IMTERPRI STREAM) [Image Stream Method] +Causes a new line to be started. The X position is set to the left margin, and the Y position is set to the current Y position plus the linefeed. If not supplied, defaults to (\OUTCHAR STREAM (CHARCODE EOL)). Envoked by TERPRI. +(IMRESET(IMRESET (Image Stream Method) NIL NIL ("27") 35) STREAM) [Image Stream Method] +Resets the X and Y position of STREAM. The X coordinate is set to its left margin; the Y coordinate is set to the top of the clipping region minus the font ascent. Envoked by DSPRESET. +The following methods all have corresponding DSPxx functions (e.g., IMYPOSITION corresponds to DSPYPOSITION) that invoke them. They also have the property of returning their previous value; when called with NIL they return the old value without changing it. +(IMCLIPPINGREGION(IMCLIPPINGREGION (Image Stream Method) NIL NIL ("27") 35) STREAM REGION) [Image Stream Method] +Sets a new clipping region on STREAM. +(IMXPOSITION(IMXPOSITION (Image Stream Method) NIL NIL ("27") 36) STREAM XPOSITION) [Image Stream Method] +Sets the X-position on STREAM. +(IMYPOSITION(IMYPOSITION (Image Stream Method) NIL NIL ("27") 36) STREAM YPOSITION) [Image Stream Method] +Sets a new Y-position on STREAM. +(IMFONT(IMFONT (Image Stream Method) NIL NIL ("27") 36) STREAM FONT) [Image Stream Method] +Sets STREAM's font to be FONT. +(IMLEFTMARGIN(IMLEFTMARGIN (Image Stream Method) NIL NIL ("27") 36) STREAM LEFTMARGIN) [Image Stream Method] +Sets STREAM's left margin to be LEFTMARGIN. The left margin is defined as the X-position set after the new line. +(IMRIGHTMARGIN(IMRIGHTMARGIN (Image Stream Method) NIL NIL ("27") 36) STREAM RIGHTMARGIN) [Image Stream Method] +Sets STREAM's right margin to be RIGHTMARGIN. The right margin is defined as the maximum X-position at which characters are printed; printing beyond it causes a new line. +(IMTOPMARGIN(IMTOPMARGIN (Image Stream Method) NIL NIL ("27") 36) STREAM YPOSITION) [Image Stream Method] +Sets STREAM's top margin (the Y-position of the tops of characters that is set after a new page) to be YPOSITION. +(IMBOTTOMMARGIN(IMBOTTOMMARGIN (Image Stream Method) NIL NIL ("27") 36) STREAM YPOSITION) [Image Stream Method] +Sets STREAM's bottom margin (the Y-position beyond which any printing causes a new page) to be YPOSITION. +(IMLINEFEED(IMLINEFEED (Image Stream Method) NIL NIL ("27") 36) STREAM DELTA) [Image Stream Method] +Sets STREAM's line feed distance (distance to move vertically after a new line) to be DELTA. +(IMSCALE(IMSCALE (Image Stream Method) NIL NIL ("27") 36) STREAM SCALE) [Image Stream Method] +Returns the number of device points per screen point (a screen point being ~1/72 inch). SCALE is ignored. +(IMSPACEFACTOR(IMSPACEFACTOR (Image Stream Method) NIL NIL ("27") 36) STREAM FACTOR) [Image Stream Method] +Sets the amount by which to multiply the natural width of all following space characters on STREAM; this can be used for the justification of text. The default value is 1. For example, if the natural width of a space in STREAM's current font is 12 units, and the space factor is set to two, spaces appear 24 units wide. The values returned by STRINGWIDTH and CHARWIDTH are also affected. +(IMOPERATION(IMOPERATION (Image Stream Method) NIL NIL ("27") 36) STREAM OPERATION) [Image Stream Method] +Sets the default BITBLT OPERATION argument. +(IMBACKCOLOR(IMBACKCOLOR (Image Stream Method) NIL NIL ("27") 36) STREAM COLOR) [Image Stream Method] +Sets the background color of STREAM. +(IMCOLOR (IMCOLOR% (Image Stream Method) NIL NIL ("27") 36)STREAM COLOR) [Image Stream Method] +Sets the default color of STREAM. +In addition to the IMAGEOPS methods described above, there are two other important methods, which are contained in the stream itself. These fields can be installed using a form like (replace (STREAM OUTCHARFN) of STREAM with (FUNCTION MYOUTCHARFN)). Note: You need to have loaded the Interlisp-D system declarations to manipulate the fields of STREAMs. The declarations can be loaded by loading the Lisp Library package SYSEDIT. +(STRMBOUTFN(STRMBOUTFN (Stream Method) NIL NIL ("27") 37) STREAM CHARCODE) [Stream Method] +The function called by BOUT. +(OUTCHARFN(OUTCHARFN (Stream Method) NIL NIL ("27") 37) STREAM CHARCODE) [Stream Method] +The function that is called to output a single byte. This is like STRMBOUTFN, except for being one level higher: it is intended for text output. Hence, this function should convert (CHARCODE EOL) into the stream's actual end-of-line sequence and should adjust the stream's CHARPOSITION appropriately before invoking the stream's STRMBOUTFN (by calling BOUT) to actually put the character. Defaults to \FILEOUTCHARFN, which is probably incorrect for an image stream. +(SEQUENCE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "26-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "26-" "")) (192 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (ALTERNATE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "26-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "26-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "26-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "26-" "")) (192 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (408 612 204 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))))#1$1HH$506 +$T2$T1$506 +$T1$1$2$T5(T2T7l$1xx$7xlx1$4`~$~1$1ll$1ll$1HH$1$1ll$2T100$1xx1xx4HH$7l$1HH +$2$T5HH$T1$4$H$ PAGEHEADING RIGHTPAGEE$ PAGEHEADINGLEFTBACKT/MODERN +(CHARPROPS (COLOR . BLACK)).GACHA +(CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK)).TITAN +(CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK))0CLASSIC +(CHARPROPS (COLOR . BLACK))/MODERN +(CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK))0CLASSIC +(CHARPROPS (COLOR . BLACK))0CLASSIC +(CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK))1TERMINAL +(CHARPROPS (COLOR . BLACK)).TITAN +(CHARPROPS (COLOR . BLACK))2 HELVETICA +(CHARPROPS (COLOR . BLACK))/MODERN(CHARPROPS (COLOR . BLACK))#" ! IM.CHAP.GETFN!  HRULE.GETFN  +  HRULE.GETFN ! (  O) 4   +   +  +        j   ?  +  +  .IM.INDEX.GETFN   +  + + L *IM.INDEX.GETFN   +  + + y /IM.INDEX.GETFN      +(IM.INDEX.GETFN  7  *IM.INDEX.GETFN ,  .IM.INDEX.GETFN C! $    %IM.INDEX.GETFN    U F +* *  %IM.INDEX.GETFN  )IM.INDEX.GETFN    *IM.INDEX.GETFN  ( 'IM.INDEX.GETFN   3 $</,_ +(IM.INDEX.GETFN (2 *IM.INDEX.GETFN +   +     *IM.INDEX.GETFN + *IM.INDEX.GETFN 1  ,   %      )IM.INDEX.GETFN    ) + +(IM.INDEX.GETFN 6 $IM.INDEX.GETFN X >/=@( BMOBJ.GETFN5 SZ[ BMOBJ.GETFN5;is, +g + + < 3w'L  +5IM.INDEX.GETFN:37&IM.INDEX.GETFN   %F + + Z 'IM.INDEX.GETFN) S +V, BMOBJ.GETFN5 +  HRULE.GETFN +'?o cf)-IM.INDEX.GETFN*   37  }  ! $_  9  + +/ +2 <+ +v&J.N> B B; W ?W6 &(%# *IM.INDEX.GETFN H  + -IM.INDEX.GETFN  !.IM.INDEX.GETFN  +  HRULE.GETFN0IM.INDEX.GETFN &IM.INDEX.GETFN U)! ++%#$    p +IM.INDEX.GETFN)o.IM.INDEX.GETFN  4-U ,IM.INDEX.GETFN  *{-IM.INDEX.GETFN3-u< + +IM.INDEX.GETFNF  *IM.INDEX.GETFN . +b*IM.INDEX.GETFN/ 7Z ,IM.INDEX.GETFN  D  %2*'IM.INDEX.GETFN &Z0 # k|v  X   %.a  .m +$  HRULE.GETFNa  ' /  ' / +   +, R )  - < +(  HRULE.GETFNl+ ^ /1 3"0    +  + 20  + )( +   3   + i: +? + 2   0 +    73X3WL + c > BMOBJ.GETFN5> BMOBJ.GETFN5 BMOBJ.GETFN5J +   +& + BMOBJ.GETFN5 + BMOBJ.GETFN5 + BMOBJ.GETFN5 + BMOBJ.GETFN5 + BMOBJ.GETFN5 + BMOBJ.GETFN5 + BMOBJ.GETFN5 + BMOBJ.GETFN5 + BMOBJ.GETFN5 + BMOBJ.GETFN5 + BMOBJ.GETFN5 + BMOBJ.GETFN5[ ;  "  ! +  HRULE.GETFNF- +  +  +  +  +$&  +   +    +       + ; &  t + +   +  +$    A'IM.INDEX.GETFN(5!  *52$  HRULE.GETFN +, MSxtV> !<?Q% 5!@ BMOBJ.GETFN5 +, <@ 2 +  +#4^ + P( Y<>   +f+ ~ +>  + +   +.  HRULE.GETFN/   *IM.INDEX.GETFN" $^76Q ~=  BMOBJ.GETFN5  * b      +  )6, BMOBJ.GETFN5>% +%#   +/ 8  , +  HRULE.GETFN/:<.'- +7?  Y P /  + )  0   +  &"  +&" .#9 ;  +  HRULE.GETFN'   * Z+     + +i   +  o +  o 7*a +?  + O +   | >  e# / +  7 +) %:      [  I  +  HRULE.GETFN96%&:  +]*'$)sM +B ;1  SKIO.GETFN.2 +33+;K 84V +`   +l  +RF +1 +^ +. + + +9 + z   1?X)3 G +#*4 + + 0 + x~   +  +  +  +5 K/T 9    .& G0G +-6H`  x +% 7  +, .F +n%0w;7:<  A2= +  K 'J  + + + @y  yH  A  * + *  : ' )= +0IM.INDEX.GETFN $3IM.INDEX.GETFNO P3IM.INDEX.GETFN +d +   HRULE.GETFN +E +U7' >X +  HRULE.GETFN  k: B   +z H  N +, +I 5 ! +  *p 7 c* >u  2 +g b   *X  +gHj& $ L  *   +  - 9> >L ) + 3 -N6V]; + +  HRULE.GETFNj_q  N/ -IM.INDEX.GETFN? K   +CN -IM.INDEX.GETFN     +IM.INDEX.GETFN'$ & &   + +C (IM.INDEX.GETFN   (IM.INDEX.GETFN   9( /  (  I  +)  +3GC02K + ~  +.$ +? L +&?* + }* NB H yR  D=!p n` =  YE= YE   YE =  Y 188 .  +   > 4 +  +I +   + )"(Z *|(+W) T3-DM  +V + T0- ,IM.INDEX.GETFN  # ^ +IM.INDEX.GETFN <?& ,  + &I + + +      +: +6n     * +/  < + + (  ( + 0 >&0   +   HRULE.GETFNW;V \A +; '   &,  &>  (5a* <;e <![Ib/ J H +  ? $ +(  +u]  &{ &O + +  +  +  +  +  +   +  !  +  + P  !  %#  + +V 3 %G-  + ++<     +'  )   '  )    %' + ) +     ! f  &-T +&- ; 1IM.INDEX.GETFN  +X-  e0:IM.INDEX.GETFN  5IM.INDEX.GETFN   5IM.INDEX.GETFN  0IM.INDEX.GETFN + 6IM.INDEX.GETFN +%" 7IM.INDEX.GETFN .Q 5IM.INDEX.GETFNH 8IM.INDEX.GETFN +=  +4IM.INDEX.GETFN + K1IM.INDEX.GETFN Y  7IM.INDEX.GETFN +  \|v   5IM.INDEX.GETFN +   5IM.INDEX.GETFN + 3IM.INDEX.GETFN dG +.IM.INDEX.GETFN  -IM.INDEX.GETFNC +kO , + .3(((CHARENCODING . MCCS)))PROPS:#DATE:j0 z \ No newline at end of file diff --git a/docs/medley-irm/27-WINDOWS.TEDIT b/docs/medley-irm/27-WINDOWS.TEDIT index 3e9589d4..47e284c9 100644 Binary files a/docs/medley-irm/27-WINDOWS.TEDIT and b/docs/medley-irm/27-WINDOWS.TEDIT differ diff --git a/docs/medley-irm/28-HARDCOPY.TEDIT b/docs/medley-irm/28-HARDCOPY.TEDIT index 06d7b7ca..c1a85ceb 100644 --- a/docs/medley-irm/28-HARDCOPY.TEDIT +++ b/docs/medley-irm/28-HARDCOPY.TEDIT @@ -1,27 +1,103 @@ -INTERLISP-D REFERENCE MANUAL HARDCOPY FACILITIES "29"28. HARDCOPY FACILITIES 2 Interlisp-D includes facilities for generating hardcopy in "Interpress" format and "Press" format. Interpress is a file format used for communicating documents to Xerox Network System printers such as the Xerox 8044 and Xerox 5700. Press is a file format used for communicating documents to Xerox laser Xerographic printers known by the names "Dover", "Spruce", "Penguin", and "Raven". There are also library packages available for supporting other types of printer formats (4045, FX-80, C150, etc.). The hardcopy facilities are designed to allow the user to support new types of printers with minimal changes to the user interface. Files can be in a number of formats, including Interpress files, plain text files, and formatted Tedit files. In order to print a file on a given printer, it is necessary to identify the format of the file, convert the file to a format that the printer can accept, and transmit it. Rather than require that the user explicitly determine file types and do the conversion, the Interlisp-D hardcopy functions generate Interpress or other format output depending on the appropriate choice for the designated printer. The hardcopy functions use the variables PRINTERTYPES and PRINTFILETYPES (described below) to determine the type of a file, how to convert it for a given printer, and how to send it. By changing these variables, the user can define other kinds of printers and print to them using the normal hardcopy functions. (SEND.FILE.TO.PRINTER(SEND.FILE.TO.PRINTER (Function) NIL NIL ("29") 1) FILE HOST PRINTOPTIONS) [Function] The function SEND.FILE.TO.PRINTER causes the file FILE to be sent to the printer HOST. If HOST is NIL, the first host in the list DEFAULTPRINTINGHOST which can print FILE is used. PRINTOPTIONS is a property list of the form (PROP1 VALUE1 PROP2 VALUE2 ...). The properties accepted depends on the type of printer. For Interpress printers, the following properties are accepted: DOCUMENT.NAME The document name to appear on the header page (a string). Default is the full name of the file. DOCUMENT.CREATION.DATE The creation date to appear on the header page (a Lisp integer date, such as returned by IDATE). The default value is the creation date of the file. SENDER.NAME The name of the sender to appear on the header page (a string). The default value is the name of the user. RECIPIENT.NAME The name of the recipient to appear on the header page (a string). The default is none. MESSAGE An additional message to appear on the header page (a string). The default is none. #COPIES The number of copies to be printed. The default value is 1. PAGES.TO.PRINT The pages of the document that should be printed, represented as a list (FIRSTPAGE# LASTPAGE#). For example, if this option is (3 5), this specifies that pages 3 through 5, inclusive, should be printed. Note that the page numbering used for this purpose has no connection to any page numbers that may be printed on the document. The default is to print all of the pages in the document. MEDIUM The medium on which the master is to be printed. If omitted, this defaults to the value of NSPRINT.DEFAULT.MEDIUM, as follows: NIL means to use the printer's default; T means to use the first medium reported available by the printer; any other value must be a Courier value of type MEDIUM. The format of this type is a list (PAPER (KNOWN.SIZE TYPE)) or (PAPER (OTHER.SIZE (WIDTH LENGTH))). The paper TYPE is one of US.LETTER, US.LEGAL, A0 through A10, ISO.B0 through ISO.B10, and JIS.B0 through JIS.B10. For users who use A4 paper exclusively, it should be sufficient to set NSPRINT.DEFAULT.MEDIUM to (PAPER (KNOWN.SIZE "A4")). When using different paper sizes, it may be necessary to reset the variable DEFAULTPAGEREGION, the region on the page used for printing (measured in micas from the lower-left corner). STAPLE? True if the document should be stapled. #SIDES 1 or 2 to indicate that the document should be printed on one or two sides, respectively. The default is the value of EMPRESS#SIDES. PRIORITY The priority of this print request, one of LOW, NORMAL, or HIGH. The default is the printer's default. Note: Press printers only recognize the options #COPIES, #SIDES, DOCUMENT.CREATION.DATE, and DOCUMENT.NAME. For example, (SEND.FILE.TO.PRINTER 'FOO NIL '(#COPIES 3 #SIDES 2 DOCUMENT.NAME "For John")) SEND.FILE.TO.PRINTER calls PRINTERTYPE and PRINTFILETYPE to determine the printer type of HOST and the file format of FILE. If FILE is a formatted file already in a form that the printer can print, it is transmitted directly. Otherwise, CONVERT.FILE.TO.TYPE.FOR.PRINTER is called to do the conversion. [Note: If the file is converted, PRINTOPTIONS is passed to the formatting function, so it can include properties such as HEADING, REGION, and FONTS.] All of these functions use the lists PRINTERTYPES and PRINTFILETYPES to actually determine how to do the conversion. LISTFILES (Chapter 17) calls the function LISTFILES1 to send a single file to a hardcopy printing device. Interlisp-D is initialized with LISTFILES1 defined to call SEND.FILE.TO.PRINTER. (HARDCOPYW(HARDCOPYW (Function) NIL NIL ("29") 2) WINDOW/BITMAP/REGION FILE HOST SCALEFACTOR ROTATION PRINTERTYPE HARDCOPYTITLE) [Function] Creates a hardcopy file from a bitmap and optionally sends it to a printer. Note that some printers may have limitations concerning how big or how "complicated" the bitmap may be printed. WINDOW/BITMAP/REGION can either be a WINDOW (open or closed), a BITMAP, or a REGION (interpreted as a region of the screen). If WINDOW/BITMAP/REGION is NIL, the user is prompted for a screen region using GETREGION. If FILE is non-NIL, it is used as the name of the file for output. If HOST = NIL, this file is not printed. If FILE is NIL, a temporary file is created, and sent to HOST. To save an image on a file without printing it, perform (HARDCOPYW IMAGE FILE). To print an image to the printer PRINTER without saving the file, perform (HARDCOPYW IMAGE NIL PRINTER). If both FILE and HOST are NIL, the default action is to print the image, without saving the file. The printer used is determined by the argument PRINTERTYPE and the value of the variable DEFAULTPRINTINGHOST. If PRINTERTYPE is non-NIL, the first host on DEFAULTPRINTINGHOST of the type PRINTERTYPE is used. If PRINTERTYPE is NIL, the first printer on DEFAULTPRINTINGHOST that implements the BITMAPSCALE (as determined by PRINTERTYPES) operation is used, if any. Otherwise, the first printer on DEFAULTPRINTINGHOST is used. The type of hardcopy file produced is determined by HOST if non-NIL, else by PRINTERTYPE if non-NIL, else by the value of DEFAULTPRINTINGHOST, as described above. SCALEFACTOR is a reduction factor. If not given, it is computed automatically based on the size of the bitmap and the capabilities of the printer type. This may not be supported for some printers. ROTATION specifies how the bitmap image should be rotated on the printed page. Most printers (including Interpress printers) only support a ROTATION of multiples of 90. PRINTERTYPE specifies what type of printer to use when HOST is NIL. HARDCOPYW uses this information to select which printer to use or what print file format to convert the output into, as described above. The background menu contains a "Hardcopy" command (Chapter 28) that prompts the user for a region on the screen, and sends the image to the default printer. Hardcopy output may also be obtained by writing a file on the printer device LPT, e.g. (COPYFILE 'FOO '{LPT}). When a file on this device is closed, it is converted to Interpress or some other format (if necessary) and sent to the default printer (the first host on DEFAULTPRINTINGHOST). One can include the printer name directly in the file name, e.g. (COPYFILE 'FOO {LPT}TREMOR:) will send the file to the printer TREMOR:. HARDCOPYTITLE is a string specifying a title to print on the page containing the screen image. If NIL, the string "Window Image" is used. To omit a title, specify the null string. (PRINTERSTATUS(PRINTERSTATUS (Function) NIL NIL ("29") 3) PRINTER) [Function] Returns a list describing the current status of the printer named PRINTER. The exact form of the value returned depends on the type of printer. For InterPress printers, the status describes whether the printer is available or busy or needs attention, and what type of paper is loaded in the printer. Returns NIL if the printer does not respond in a reasonable time, which can occur if the printer is very busy, or does not implement the printer status service. DEFAULTPRINTINGHOST (DEFAULTPRINTINGHOST% (Variable) NIL NIL ("29") 3) [Variable] The variable DEFAULTPRINTINGHOST is used to designate the default printer to be used as the output of printing operations. It should be a list of the known printer host names, for example, (QUAKE LISPPRINT:). If an element of DEFAULTPRINTINGHOST is a list, is interpreted as (PRINTERTYPE HOST), specifying both the host type and the host name. The type of the printer, which determines the protocol used to send to it and the file format it requires, is determined by the function PRINTERTYPE. If DEFAULTPRINTINGHOST is a single printer name, it is treated as if it were a list of one element. (PRINTFILETYPE(PRINTFILETYPE (Function) NIL NIL ("29") 4) FILE %) [Function] Returns the format of the file FILE. Possible values include INTERPRESS, TEDIT, etc. If it cannot determine the file type, it returns NIL. Uses the global variable PRINTFILETYPES. (PRINTERTYPE(PRINTERTYPE (Function) NIL NIL ("29") 4) HOST) [Function] Returns the type of the printer HOST. Currently uses the following heuristic: 1. If HOST is a list, the CAR is assumed to be the printer type and CADR the name of the printer 2. If HOST is a litatom with a non-NIL PRINTERTYPE property, the property value is returned as the printer type 3. If HOST contains a colon (e.g., PRINTER:PARC:XEROX) it is assumed to be an INTERPRESS printer 4. If HOST is the CADR of a list on DEFAULTPRINTINGHOST, the CAR is returned as the printer type 5. Otherwise, the value of DEFAULTPRINTERTYPE is returned as the printer type. Low-level Hardcopy Variables 1 The following variables are used to define how Interlisp should generate hardcopy of different types. The user should only need to change these variables when it is necessary to access a new type of printer, or define a new hardcopy document type (not often). PRINTERTYPES(PRINTERTYPES (Variable) NIL NIL ("29") 4) [Variable] The characteristics of a given printer are determined by the value of the list PRINTERTYPES. Each element is a list of the form (TYPES (PROPERTY1 VALUE1) (PROPERTY2 VALUE2) ...) TYPES is a list of the printer types that this entry addresses. The (PROPERTYn VALUEn) pairs define properties associated with each printer type. The printer properties include the following: CANPRINT Value is a list of the file types that the printer can print directly. STATUS Value is a function that knows how to find out the status of the printer, used by PRINTERSTATUS. PROPERTIES Value is a function which returns a list of known printer properties. SEND Value is a function which invokes the appropriate protocol to send a file to the printer. BITMAPSCALE Value is a function of arguments WIDTH and HEIGHT in bits which returns a scale factor for scaling a bitmap. BITMAPFILE Value is a form which, when evaluated, converts a bitmap to a file format that the printer will accept. Note: The name 8044 is defined on PRINTERTYPES as a synonym for the INTERPRESS printer type. The names SPRUCE, PENGUIN, and DOVER are defined on PRINTERTYPES as synonyms for the PRESS printer type. The printer types FULLPRESS and RAVEN are also defined the same as PRESS, except that these printer types indicate that the printer is a "Full Press" printer that is able to scale bitmap images, in addition to the normal Press printer facilities. PRINTFILETYPES(PRINTFILETYPES (Variable) NIL NIL ("29") 5) [Variable] The variable PRINTFILETYPES contains information about various file formats, such as Tedit files and Interpress files. The format is similar to PRINTERTYPES. The properties that can be specified include: TEST Value is a function which tests a file if it is of the given type. Note that this function is passed an open stream. CONVERSION Value is a property list of other file types and funcitons that convert from the specified type to the file format. EXTENSION Value is a list of possible file extensions for files of this type. (LIST ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "28-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "28-" "")) (192 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "28-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "28-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "28-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "28-" "")) (192 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (408 612 204 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))F PAGEHEADING RIGHTPAGE;xllxx,xx2l~~2`~~,HH/HH206 -,@ PAGEHEADINGLEFTBACKT-T3(T,2 PALATINO TITAN TITAN TITAN TITAN -CLASSIC -MODERN -MODERNMODERN - -   IM.CHAP.GETFNMODERN -  HRULE.GETFNMODERN -  } - 2IM.INDEX.GETFNMODERN -  -  | b Y8  l  X  S  =  H(  \%r%@      JN[  ( x   +)0  4  "k D L) 1 ! -W - 'IM.INDEX.GETFNMODERN -? .1 5 +8Nu       > -@ -   , ME# UO +IM.INDEX.GETFNMODERN -B 3IM.INDEX.GETFN   M+IM.INDEX.GETFNMODERN -  -X )IM.INDEX.GETFNTITAN - +' > - !"   HRULE.GETFNMODERN -   *IM.INDEX.GETFNMODERN -O & @ <.FR  -FZ !< -h#  - " ,IM.INDEX.GETFNMODERN - v 1 v t D1=z \ No newline at end of file +INTERLISP-D REFERENCE MANUAL + HARDCOPY FACILITIES + +"29"28. HARDCOPY FACILITIES +2 + +Interlisp-D includes facilities for generating hardcopy in "Interpress" format and "Press" format. Interpress is a file format used for communicating documents to Xerox Network System printers such as the Xerox 8044 and Xerox 5700. Press is a file format used for communicating documents to Xerox laser Xerographic printers known by the names "Dover", "Spruce", "Penguin", and "Raven". There are also library packages available for supporting other types of printer formats (4045, FX-80, C150, etc.). The hardcopy facilities are designed to allow the user to support new types of printers with minimal changes to the user interface. +Files can be in a number of formats, including Interpress files, plain text files, and formatted Tedit files. In order to print a file on a given printer, it is necessary to identify the format of the file, convert the file to a format that the printer can accept, and transmit it. Rather than require that the user explicitly determine file types and do the conversion, the Interlisp-D hardcopy functions generate Interpress or other format output depending on the appropriate choice for the designated printer. The hardcopy functions use the variables PRINTERTYPES and PRINTFILETYPES (described below) to determine the type of a file, how to convert it for a given printer, and how to send it. By changing these variables, the user can define other kinds of printers and print to them using the normal hardcopy functions. +(SEND.FILE.TO.PRINTER(SEND.FILE.TO.PRINTER (Function) NIL NIL ("29") 1) FILE HOST PRINTOPTIONS) [Function] +The function SEND.FILE.TO.PRINTER causes the file FILE to be sent to the printer HOST. If HOST is NIL, the first host in the list DEFAULTPRINTINGHOST which can print FILE is used. +PRINTOPTIONS is a property list of the form (PROP1 VALUE1 PROP2 VALUE2 ...). The properties accepted depends on the type of printer. For Interpress printers, the following properties are accepted: + DOCUMENT.NAME The document name to appear on the header page (a string). Default is the full name of the file. + DOCUMENT.CREATION.DATE The creation date to appear on the header page (a Lisp integer date, such as returned by IDATE). The default value is the creation date of the file. + SENDER.NAME The name of the sender to appear on the header page (a string). The default value is the name of the user. + RECIPIENT.NAME The name of the recipient to appear on the header page (a string). The default is none. + MESSAGE An additional message to appear on the header page (a string). The default is none. + #COPIES The number of copies to be printed. The default value is 1. + PAGES.TO.PRINT The pages of the document that should be printed, represented as a list (FIRSTPAGE# LASTPAGE#). For example, if this option is (3 5), this specifies that pages 3 through 5, inclusive, should be printed. Note that the page numbering used for this purpose has no connection to any page numbers that may be printed on the document. The default is to print all of the pages in the document. + MEDIUM The medium on which the master is to be printed. If omitted, this defaults to the value of NSPRINT.DEFAULT.MEDIUM, as follows: NIL means to use the printer's default; T means to use the first medium reported available by the printer; any other value must be a Courier value of type MEDIUM. The format of this type is a list (PAPER (KNOWN.SIZE TYPE)) or (PAPER (OTHER.SIZE (WIDTH LENGTH))). The paper TYPE is one of US.LETTER, US.LEGAL, A0 through A10, ISO.B0 through ISO.B10, and JIS.B0 through JIS.B10. For users who use A4 paper exclusively, it should be sufficient to set NSPRINT.DEFAULT.MEDIUM to (PAPER (KNOWN.SIZE "A4")). + When using different paper sizes, it may be necessary to reset the variable DEFAULTPAGEREGION, the region on the page used for printing (measured in micas from the lower-left corner). + STAPLE? True if the document should be stapled. + #SIDES 1 or 2 to indicate that the document should be printed on one or two sides, respectively. The default is the value of EMPRESS#SIDES. + PRIORITY The priority of this print request, one of LOW, NORMAL, or HIGH. The default is the printer's default. +Note: Press printers only recognize the options #COPIES, #SIDES, DOCUMENT.CREATION.DATE, and DOCUMENT.NAME. + For example, + (SEND.FILE.TO.PRINTER 'FOO NIL + '(#COPIES 3 #SIDES 2 DOCUMENT.NAME "For John")) +SEND.FILE.TO.PRINTER calls PRINTERTYPE and PRINTFILETYPE to determine the printer type of HOST and the file format of FILE. If FILE is a formatted file already in a form that the printer can print, it is transmitted directly. Otherwise, CONVERT.FILE.TO.TYPE.FOR.PRINTER is called to do the conversion. [Note: If the file is converted, PRINTOPTIONS is passed to the formatting function, so it can include properties such as HEADING, REGION, and FONTS.] All of these functions use the lists PRINTERTYPES and PRINTFILETYPES to actually determine how to do the conversion. +LISTFILES (Chapter 17) calls the function LISTFILES1 to send a single file to a hardcopy printing device. Interlisp-D is initialized with LISTFILES1 defined to call SEND.FILE.TO.PRINTER. +(HARDCOPYW(HARDCOPYW (Function) NIL NIL ("29") 2) WINDOW/BITMAP/REGION FILE HOST SCALEFACTOR ROTATION PRINTERTYPE HARDCOPYTITLE) [Function] +Creates a hardcopy file from a bitmap and optionally sends it to a printer. Note that some printers may have limitations concerning how big or how "complicated" the bitmap may be printed. +WINDOW/BITMAP/REGION can either be a WINDOW (open or closed), a BITMAP, or a REGION (interpreted as a region of the screen). If WINDOW/BITMAP/REGION is NIL, the user is prompted for a screen region using GETREGION. +If FILE is non-NIL, it is used as the name of the file for output. If HOST = NIL, this file is not printed. If FILE is NIL, a temporary file is created, and sent to HOST. +To save an image on a file without printing it, perform (HARDCOPYW IMAGE FILE). To print an image to the printer PRINTER without saving the file, perform (HARDCOPYW IMAGE NIL PRINTER). +If both FILE and HOST are NIL, the default action is to print the image, without saving the file. The printer used is determined by the argument PRINTERTYPE and the value of the variable DEFAULTPRINTINGHOST. If PRINTERTYPE is non-NIL, the first host on DEFAULTPRINTINGHOST of the type PRINTERTYPE is used. If PRINTERTYPE is NIL, the first printer on DEFAULTPRINTINGHOST that implements the BITMAPSCALE (as determined by PRINTERTYPES) operation is used, if any. Otherwise, the first printer on DEFAULTPRINTINGHOST is used. +The type of hardcopy file produced is determined by HOST if non-NIL, else by PRINTERTYPE if non-NIL, else by the value of DEFAULTPRINTINGHOST, as described above. +SCALEFACTOR is a reduction factor. If not given, it is computed automatically based on the size of the bitmap and the capabilities of the printer type. This may not be supported for some printers. +ROTATION specifies how the bitmap image should be rotated on the printed page. Most printers (including Interpress printers) only support a ROTATION of multiples of 90. +PRINTERTYPE specifies what type of printer to use when HOST is NIL. HARDCOPYW uses this information to select which printer to use or what print file format to convert the output into, as described above. +The background menu contains a "Hardcopy" command (Chapter 28) that prompts the user for a region on the screen, and sends the image to the default printer. +Hardcopy output may also be obtained by writing a file on the printer device LPT, e.g. (COPYFILE 'FOO '{LPT}). When a file on this device is closed, it is converted to Interpress or some other format (if necessary) and sent to the default printer (the first host on DEFAULTPRINTINGHOST). One can include the printer name directly in the file name, e.g. (COPYFILE 'FOO {LPT}TREMOR:) will send the file to the printer TREMOR:. +HARDCOPYTITLE is a string specifying a title to print on the page containing the screen image. If NIL, the string "Window Image" is used. To omit a title, specify the null string. + +(PRINTERSTATUS(PRINTERSTATUS (Function) NIL NIL ("29") 3) PRINTER) [Function] +Returns a list describing the current status of the printer named PRINTER. The exact form of the value returned depends on the type of printer. For InterPress printers, the status describes whether the printer is available or busy or needs attention, and what type of paper is loaded in the printer. +Returns NIL if the printer does not respond in a reasonable time, which can occur if the printer is very busy, or does not implement the printer status service. +DEFAULTPRINTINGHOST (DEFAULTPRINTINGHOST% (Variable) NIL NIL ("29") 3) [Variable] +The variable DEFAULTPRINTINGHOST is used to designate the default printer to be used as the output of printing operations. It should be a list of the known printer host names, for example, (QUAKE LISPPRINT:). If an element of DEFAULTPRINTINGHOST is a list, is interpreted as (PRINTERTYPE HOST), specifying both the host type and the host name. The type of the printer, which determines the protocol used to send to it and the file format it requires, is determined by the function PRINTERTYPE. +If DEFAULTPRINTINGHOST is a single printer name, it is treated as if it were a list of one element. +(PRINTFILETYPE(PRINTFILETYPE (Function) NIL NIL ("29") 4) FILE %) [Function] +Returns the format of the file FILE. Possible values include INTERPRESS, TEDIT, etc. If it cannot determine the file type, it returns NIL. Uses the global variable PRINTFILETYPES. +(PRINTERTYPE(PRINTERTYPE (Function) NIL NIL ("29") 4) HOST) [Function] +Returns the type of the printer HOST. Currently uses the following heuristic: +1. If HOST is a list, the CAR is assumed to be the printer type and CADR the name of the printer +2. If HOST is a litatom with a non-NIL PRINTERTYPE property, the property value is returned as the printer type +3. If HOST contains a colon (e.g., PRINTER:PARC:XEROX) it is assumed to be an INTERPRESS printer +4. If HOST is the CADR of a list on DEFAULTPRINTINGHOST, the CAR is returned as the printer type +5. Otherwise, the value of DEFAULTPRINTERTYPE is returned as the printer type. +Low-level Hardcopy Variables +1 + +The following variables are used to define how Interlisp should generate hardcopy of different types. The user should only need to change these variables when it is necessary to access a new type of printer, or define a new hardcopy document type (not often). +PRINTERTYPES(PRINTERTYPES (Variable) NIL NIL ("29") 4) [Variable] +The characteristics of a given printer are determined by the value of the list PRINTERTYPES. Each element is a list of the form +(TYPES (PROPERTY1 VALUE1) (PROPERTY2 VALUE2) ...) +TYPES is a list of the printer types that this entry addresses. The (PROPERTYn VALUEn) pairs define properties associated with each printer type. +The printer properties include the following: + CANPRINT Value is a list of the file types that the printer can print directly. + STATUS Value is a function that knows how to find out the status of the printer, used by PRINTERSTATUS. + PROPERTIES Value is a function which returns a list of known printer properties. + SEND Value is a function which invokes the appropriate protocol to send a file to the printer. + BITMAPSCALE Value is a function of arguments WIDTH and HEIGHT in bits which returns a scale factor for scaling a bitmap. + BITMAPFILE Value is a form which, when evaluated, converts a bitmap to a file format that the printer will accept. +Note: The name 8044 is defined on PRINTERTYPES as a synonym for the INTERPRESS printer type. The names SPRUCE, PENGUIN, and DOVER are defined on PRINTERTYPES as synonyms for the PRESS printer type. The printer types FULLPRESS and RAVEN are also defined the same as PRESS, except that these printer types indicate that the printer is a "Full Press" printer that is able to scale bitmap images, in addition to the normal Press printer facilities. +PRINTFILETYPES(PRINTFILETYPES (Variable) NIL NIL ("29") 5) [Variable] +The variable PRINTFILETYPES contains information about various file formats, such as Tedit files and Interpress files. The format is similar to PRINTERTYPES. The properties that can be specified include: + TEST Value is a function which tests a file if it is of the given type. Note that this function is passed an open stream. + CONVERSION Value is a property list of other file types and funcitons that convert from the specified type to the file format. + EXTENSION Value is a list of possible file extensions for files of this type. +(SEQUENCE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "28-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "28-" "")) (192 24 288 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL))) (ALTERNATE NIL NIL (0 0 0 0) ((PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "28-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD LEFT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "28-" "")) (102 24 288 36) NIL) (HEADING NIL (HEADINGTYPE LEFTBACK) (96 612 516 36) NIL) (TEXT NIL NIL (102 36 456 540) NIL))) (PAGE NIL (PAPERSIZE Letter FOLIOINFO (ARABIC "28-" "")) (0 0 612 792) ((FOLIO NIL (PARALOOKS (QUAD RIGHT) CHARLOOKS (SUPERSCRIPT 0 INVISIBLE OFF SELECTPOINT OFF PROTECTED OFF SIZE 9 FAMILY TIMESROMAN OVERLINE OFF STRIKEOUT OFF UNDERLINE OFF EXPANSION REGULAR SLOPE REGULAR WEIGHT MEDIUM INVERTED OFF USERINFO NIL STYLE NIL) FORMATINFO (ARABIC "28-" "")) (192 24 288 36) NIL) (HEADING NIL (HEADINGTYPE RIGHTPAGE) (408 612 204 36) NIL) (TEXT NIL NIL (54 36 408 540) NIL)))))))1$=xllxx1HH$406 +$4`~$~1xx$1$2$T5(T4l~$~1$4$H$ PAGEHEADING RIGHTPAGEE$ PAGEHEADINGLEFTBACKT +/MODERN +(CHARPROPS (COLOR . BLACK)).GACHA +(CHARPROPS (COLOR . BLACK))1PALATINO (CHARPROPS (COLOR . BLACK))0CLASSIC +(CHARPROPS (COLOR . BLACK)).TITAN +(CHARPROPS (COLOR . BLACK))/MODERN +(CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK)).TITAN (CHARPROPS (COLOR . BLACK))/MODERN(CHARPROPS (COLOR . BLACK))  +  IM.CHAP.GETFN  HRULE.GETFN}- 2IM.INDEX.GETFN      + | bY8 lXS=H(\%r%@      JN[(x +)0  4  "   k D L) 1 ! +W + 'IM.INDEX.GETFN?      . 1  5   + 8N  u       > +@ +    , ME# UO +IM.INDEX.GETFN B 3IM.INDEX.GETFN   M-IM.INDEX.GETFN    +X )IM.INDEX.GETFN   + + ' +  > +  +  + ! +"  HRULE.GETFN *IM.INDEX.GETFNO & @ <.FR  +FZ !< +h#  + " ,IM.INDEX.GETFN v 1v t D(((CHARENCODING . MCCS)))PROPS:#DATE:jJ1;z \ No newline at end of file diff --git a/docs/medley-irm/29-TERMINAL.TEDIT b/docs/medley-irm/29-TERMINAL.TEDIT index d8bd06bc..17e761d5 100644 Binary files a/docs/medley-irm/29-TERMINAL.TEDIT and b/docs/medley-irm/29-TERMINAL.TEDIT differ