Page Numbers: Yes X: 310 Y: 10.42" First Page: 1
Margins: Top: 1.0" Bottom: 1.5"
Heading:
3-LISP WORKING GUIDE#9: Characters
———————————————————————————————————————————
Issue #9:Characters
Description:How are characters going to be incorporated into the language? Sub-issues: character designators; primitive operations exclusively for characters; treatment by TYPE, =, REPLACE, etc.; notation; collating sequence; character sequences = strings
Status:Partially resolved. A new abstract category called characters will be designated by a new canonical normal-form structural caregory called charats. Like numerals, charats are not constructed — they just exist. Charats are notated ‘#x’ where x is any single character that you can type there (forgive the circularity in this definition, but I fear that it’s unavoidable). More details below...
Last Edited:September 20, 1982 (Jim des Rivières)
———————————————————————————————————————————
The following examples should give some ideas of how they can be worked into the fabric of 3-LISP.
1> (TYPE #A); TYPE knows about them (of course).
=> ’CHARACTER
1> #A
; They’re self-normalizing.
=> #A
1> (= #A #A)
; They’re canonical.
=> $T
1> (= #A #B)
=> $F
1> (TYPE ↑#A)
=> ’CHARAT
1> ↑#A
=> ’#A
1> (REPLACE ’#A ’#B)
<Error: Charats cannot be replaced>
Strings can simply be treated as character sequences. Syntactic sugar can be provided to make this convenient.
1> "A string"
=> "A string"
1> (TYPE "A string")
=> ’SEQUENCE
1> (FIRST "Hi")
=> #H
1> (REST "Hello")
=> "ello"
1> (LENGTH "To")
=> 2
1> (LENGTH """")
; As usual, inner quotation marks are
=> 1
; double.
1> (FIRST """")
=> #"
1> ""
; The empty character string is also
=> []
; the empty sequence!
1> (APPEND "Lamb" "da")
=> "Lambda"
1> (PREP 1 "Hello")
; Fine...but it no longer prints
=> [1 #H #e #l #l #o]
; nicely.
The issue of number–character inter-conversion is exactly the same as the question of collating sequence. Here we can adopt (modern) APL’s approach by providing a global variable called CHARACTER-SET that contains all of the characters known to the implementation (I assume there are only a finite number) in the standard collating sequence.
1> CHARACTER-SET; Just a sample --- not for real.
=> "ABCDEFGHIHJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%~&*()-=+|\{[}]↑←:;’""<,>.?/"
1> (DEFINE CHR; Integer to character conversion.
(LAMBDA SIMPLE [X]
(NTH X CHARACTER-SET)))
1> (DEFINE ORD; Character to integer conversion.
(LAMBDA SIMPLE [X]
(IF (CHARACTER X)
(LET [[POSITION (INDEX X CHARACTER-SET)]]
(IF (= POSITION 0)
(ERROR "Where did you get that crazy character?")
POSITION))
(ERROR "First argument must designate a character."))))
1> ; (INDEX X S) designates the smallest number N such that (= (NTH N S) X).
1> ; If no such N exists, the expression designates 0.
1> (DEFINE INDEX

(LAMBDA SIMPLE [OBJECT SEQUENCE]
(LABEL [[SEARCH (LAMBDA SIMPLE [S POSITION]
(COND [(EMPTY S) 0]
[(= OBJECT (FIRST S)) POSITION]
[$T (SEARCH (REST S) (+ POSITION 1))]))]]
(SEARCH X SEQUENCE 1))))
1> (ORD #Z); For any character C found in
=> 26
; CHARACTER-SET,
1> (CHR 26)
; (CHR (ORD C)) = C
=> #Z
1> (= (CHR (ORD #5)) #5)
=> $T
New primitives for reading and writing characters can be added to provide the basis for READ and PRINT (ignore, for the moment, the problem of input streams).
1> (READCH) A
=> #A
1> (WRITECH #A) A
1> (MAP WRITECH "Hello") Hello
1> (define WRITE-STRING
(lambda simple [x]
(map writech x)))
1> (define EXTERNALIZE
(lambda simple [x]
(cond [(boolean x) (if (= x ’$T) "$T" "$F")]
[(charat x) (prep ## \x)]
[(numeral x) (externalize-numeral \x)]
[(pair x) (externalize-pair x)]
[(closure x) "<Closure>"]
; Cheap!
[(handle x) (prep #’ (externalize \x))]
[(atom x) (externalize-atom x)]
[(rail x) (append* "[" (externalize-rail-open x) "]")])))
1> (define EXTERNALIZE-PAIR
(lambda simple [p]
(let [[car-part (car p)] [cdr-part (cdr p)]]
(if (rail cdr-part)
(cond [(and (= car-part ↑up) (= (length cdr-part) 1))
(prep #↑ (externalize-rail-open cdr-part))]
[(and (=car-part ↑down (= (length cdr-part) 1))
(prep #\ (externalize-rail-open cdr-part))]
[(= (length cdr-part) 0)
(append* "(" (externalize car-part) ")")]
[$T (append* "("
(externalize car-part)
" "
(externalize-rail-open cdr-part)
")")])
(append* "("
(externalize car-part)
" . "
(externalize cdr-part)
")")))))
1> (define EXTERNALIZE-RAIL-OPEN; without the brackets.
(lambda simple [r]
(cond [(empty r) ""]
[(unit r) (externalize (first r))]
[$T (append* (externalize (first r))
" "
(externalize-rail-open (rest r)))])))
1> (define EXTERNALIZE-ATOM
(lambda simple [a]
(let [[[found value] (lookup2 a global-theta-env)]]
(if found value "<Atom>"))))
1> (define LOOKUP2; Search an env. using second item a
(lambda simple [x env]
; as the key. Returns a rail; first
(cond [(empty env) [$F 0]]; designates success/failure; second
[(= x (second (first env))) [$T (first (first env))]] ; designates
[$T (lookup2 x (rest env))]))) ; associated beastie.
1> (define EXTERNALIZE-NUMERAL
(lambda simple [n]
(if (negative n)
(prep #- (externalize-numeral (- 0 n)))
(labels [[digit-loop
(lambda simple [remainder]
(if (= remainder 0)
""
(prep (nth (mod remainder 10) "0123456789")
(digit-loop (/ remainder 10)))))
(reverse (digit-loop n))))))