;;; May 20, 1984 --- Jim des Rivieres
;;; Temporary patchs to 3-Lisp 1.0 LETREC and LOAD.
;;; This revised definition to LETREC fixes two bugs
;;; in the release version that caused the uncompiled
;;; version of LETREC to be unuseable. As it turned out,
;;; LOAD was written in such a way as to cause the
;;; processor to run one level higher than necessary.
;;; Hence the revised version of LOAD. Both of these
;;; changes have been propagated to the appropriate
;;; {phylum}<3-lisp>new> sources.
(rebind-in-system-environment 'letrec
(kernel-macro-ccons
↑(lambda [call]
(let [[args (pargs call)]]
(if (and (rail args)
(>= (length args) 1)
(rail (first args))
(every (lambda [x]
(and (rail x)
(= (length x) 2)
(atom (first x))))
(first args)))
(let [[body (rest args)]
[vars (map first (first args))]]
(pcons
(pcons ↑lambda
(append
(rcons vars)
(map (lambda [x] (pcons ↑set x)) (first args))
body))
(map (lambda [x] '"unbound") (first args))))
(pcons ↑trap (rcons '"malformed letrec" call)))))
'letrec-macro))
; (LOAD file-name)
(letrec
[[load-helper
(lambda [stream file-name]
(let [[structure (forgiving-read-structure stream)]]
(if (string structure)
(begin
(close-all-streams) ; Work around.
(if (= structure "end of stream")
'ok
(error (string-append structure
" while LOADing the file " file-name)
'ok)))
(normalize structure global
(lambda [a b c d e f g]
(close-all-streams) ; Work around.
(standard-escape a b c d e f g))
(lambda [result-nf]
(print-structure primary-stream result-nf)
(print primary-stream cr)
(load-helper stream file-name))))))]
[forgiving-read-structure
(lambda [stream]
(cps-error-protect (read-structure stream) id id))]]
(system-define load
(lambda [file-name]
(let [[stream (open-stream file-name)]]
(load-helper stream file-name)
(close-stream stream)
file-name))))
;;; May 14, 1984 --- Jim des Rivieres
;;; Patch to 3-lisp 1.0 to remedy problem with
;;; (internalize "foo") causing an error.
;;; Problem is with end of stream being encountered.
;;; Workaround: always append a space to the string
;;; before internalizing it.
(let [[old-internalize
\(primitive-ccons global '[string]
'(internalize string)
"internalize"
'prim-internalize)]]
(system-define internalize
(lambda [string]
(old-internalize
(string-append string " ")))))
((rlambda [a e s c]
(load "{phylum}<3-lisp>course>problem-sets>parsing-utilities.3-lisp")
(c ''ok)))
;;; Code stored as: {turing}<3-lisp.problem-sets>parsing-utilities.3-lisp
;;; {phylum}<3-lisp>course>problem-sets>parsing-utilities.3-lisp
;;; =================================================================
;;; SEQUENCE UTILITIES
;;; =================================================================
;;; (FILTER predicate sequence) Designates the sequence consisting
;;; =========================== of each element of sequence for which
;;; (predicate element) is true.
(define FILTER
(lambda [predicate sequence]
(collect (lambda [element]
(if (predicate element)
[element]
[]))
sequence)))
;;; (COLLECT fun args1 ... argsk) Appends together the results of
;;; ============================= mapping fun (of arity k) to the
;;; sequences of args args1 ... argsk.
;;; Useful, in combination with SINGLETON and NOTHING, for assembling
;;; some results from a sequence of args.
(define COLLECT
(lambda args
(append . (map . args))))
;;; (SINGLETON exp) To be used by COLLECT and F-COLLECT
;;; (NOTHING)
;;; ===============
(define SINGLETON (lambda [element] [element]))
(define NOTHING (lambda [] []))
;;; (FILTERED-MAP fun predicate sequence) Maps <fun> down a version of <sequence>
;;; ===================================== that has been filtered by <predicate>.
;;;
(define FILTERED-MAP
(lambda [fun predicate sequence]
(map fun (filter predicate sequence))))
;;; =================================================================
;;; General purposes COMPOSE:
;;;
;;; ((compose f1 f2 ... fk) a1 a2 ... an) ; 1 =< k
;;;
;;; => (f1 (f2 ... (fk a1 a2 ... an)...)) ; 0 =< n
;;; =================================================================
(define COMPOSE
(lambda funs
(cond [(null funs) (lambda args args)]
[(null (rest funs)) (first funs)]
[$true
(lambda args
((first funs)
((compose . (rest funs)) . args)))])))
;;; CONSTANT
;;; ========
(define CONSTANT
(lambda [constant]
(lambda args constant)))
;;; ===============================================================
;;; STRANGE CONTROL STRUCTURES
;;; ===============================================================
;;; DYNAMIC-CATCH
;;; DYNAMIC-THROW
;;; =============
(define DYNAMIC-CATCH
(rlambda [call env esc cont]
(cont (normalize (arg 1 call) env esc id))))
(define DYNAMIC-THROW
(rlambda [call env esc cont]
(normalize (arg 1 call) env esc id)))
;;; =================================================================
;;; OBJECTS:
;;; =================================================================
;;;
;;; General form:
;;;
;;; (object [init-var-1 ... init-var-k]
;;; [[var-1 init-1] ... [var-n init-n]]
;;; [<mess1> <fun1>]
;;; [<mess2> <fun2>]
;;; ...
;;; [<messk> <funk>])
;;; =================================================================
(define OBJECT
(let [[define-message
(lambda [name]
'(define ,name
(lambda args
(((first args) ,↑name) . (rest args)))))]]
(mlambda [call]
(letseq [[state-vars (arg 1 call)]
[inited-vars (arg 2 call)]
[pairs (tail 2 (pargs call))]
[fun-names (map (lambda [pair] (acons)) pairs)]]
'(begin
,(map (lambda [pair]
(define-message (first pair)))
pairs)
(lambda ,state-vars
(let ,inited-vars
(letrec ,(map (lambda [pair fun-name]
'[,fun-name ,(second pair)])
pairs
fun-names)
(lambda [message]
(cond . ,(map (lambda [pair fun-name]
'[(= message ,↑(first pair))
,fun-name])
pairs
fun-names)))))))))))
;;; Code stored as: {turing}<3-lisp.problem-sets>lexical-analyser.3-lisp
;;; {phylum}<3-lisp>course>problem-sets>lexical-analyser.3-lisp
;;; ========================
;;; 3-LISP Lexical Analyser:
;;; ========================
;;; ===============================================================
;;; PUBLIC ROUTINES
;;; ===============================================================
;;; (INIT-LEXAN) Initializes all tables.
;;; ============
;;;
;;; (LEXAN-OBJECT string) A type of object that embodies the tokenization of a
;;; ===================== given string. Supports the following messages:
;;;
;;; (CURRENT-TOKEN lexan-object) Returns the token currently being scanned (i.e.,.
;;; ============================ the one made current by the last use of GET-TOKEN)
;;;
;;; (GET-TOKEN lexan-object) Also returns the current token being scanned but
;;; ======================== moves past the token in the input. This is the
;;; normal method to be used in accessing tokens.
;;;
;;; (NEXT-TOKEN lexan-object) Returns the 'look-ahead' token, the token just
;;; ========================= about to be scanned. Useful for dispatching without
;;; actually reading the token.
;;;
;;; (LEXICAL-ANALYSIS string) Designates the sequence of tokens into which
;;; ========================= string is lexically analyzed. For example
;;; (lexical-analysis "a (b)") => ["a" "(" "b" ")"].
;;;
;;; (IDENTIFIER-EXPRESSION token) True just in case token is a sequence of alphanumeric
;;; ============================= characters, but is not a numeral-expression.
;;;
;;; (NUMERAL-EXPRESSION token) True just in case token is a sequence of digits,
;;; ========================== possibly including a leading "+" or "-".
(define INIT-LEXAN
(lambda []
(set *special-chars* [ #( #) #[ #] #$ #' #, #. #↑ #\ #{ #} ])
(set *white-space-chars* [ # cr ])
(set *delimiters* (append *white-space-chars* *special-chars*))
(set *digits* [ #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 ])
(set *comment-begin* #;)
(set *comment-end* cr)
"ok"))
(define LEXAN-OBJECT
(let [[create-lexan-object
(object [string]
[[current-tok ""]
[next-tok ""]]
[GET-TOKEN
(lambda []
(let [[token-info (global-get-token string)]]
(set string (second token-info))
(set current-tok next-tok)
(set next-tok (first token-info))
current-tok))]
[CURRENT-TOKEN (lambda [] current-tok)]
[NEXT-TOKEN (lambda [] next-tok)])]]
(lambda [string]
(let [[lex-obj (create-lexan-object string)]]
(get-token lex-obj) ; initialize
lex-obj))))
;;; Not dealt with by this code:
;;;
;;; -- strings
;;; -- charats
;;; -- character quotation (%)
;;; LEXICAL-ANALYSIS:
;;; =========
(define LEXICAL-ANALYSIS
(lambda [string]
(let [[a (lexan-object string)]]
(letrec [[helper (lambda []
(let [[tok (get-token a)]]
(if (= tok 'eot)
[]
(cons tok (helper)))))]]
(helper)))))
;;; ===============================================================
;;; INTERNAL ROUTINES
;;; ===============================================================
;;; GLOBAL-GET-TOKEN takes a STRING and returns a rail of:
;;; ================ a) the prefix of the string corresponding to the
;;; first token in the string
;;; b) the rest of the string after the prefix
;;; In case there are no tokens left in the string, returns the
;;; handle 'eot (end of tokens).
;;;
;;; Used by GET-TOKEN (within INIT-LEXAN)
(define GLOBAL-GET-TOKEN
(lambda [string]
(add-to-token "" (strip-leading-whitespace string))))
;;; ADD-TO-TOKEN auxiliary function for GLOBAL-GET-TOKEN which
;;; ============ adds characters to a token found so far (SO-FAR)
;;; taking characters from a string STRING.
;;;
;;; Uses information from global-variables: *special-chars*
;;; defined by INIT-LEXAN: *delimiters*
(define ADD-TO-TOKEN
(lambda [so-far string]
(cond [(string-null string)
[(if (string-null so-far) 'eot so-far) string]]
[(and (string-null so-far)
(member (string-first string) *special-chars*))
[(substring 1 1 string) (string-rest string)]]
[(member (string-first string) *delimiters*)
[so-far string]]
[$true
(add-to-token (string-append so-far
(substring 1 1 string))
(string-rest string))])))
;;; STRIP-LEADING-WHITESPACE removes all leading white-space and comments
;;; ======================== from a string. White-space is defined as
;;; any character in the global variable
;;; *white-space-chars* (defined by INIT-LEXAN).
;;; Comments are any string of characters starting with the character
;;; bound to *comment-begin*, up through the character *comment-end*
(define STRIP-LEADING-WHITESPACE
(lambda [string]
(cond [(string-null string) string]
[(member (string-first string) *white-space-chars*)
(strip-leading-whitespace (string-rest string))]
[(= (string-first string) *comment-begin*)
(strip-leading-whitespace (strip-comment string))]
[$true
string])))
;;; STRIP-COMMENT Removes one full comment prefix from a string. A comment
;;; ============= is defined as any string of characters starting with the
;;; globally bound *comment-begin* character, up through and
;;; including *comment-end* (both defined by INIT-LEXAN).
(define STRIP-COMMENT
(lambda [string]
(cond [(string-null string) string]
[(= (string-first string) *comment-end*)
(string-rest string)]
[$true
(strip-comment (string-rest string))])))
;;; ===============================================================
;;; SPECIAL TOKEN IDENTIFICATION PREDICATES
;;; ===============================================================
(define IDENTIFIER-EXPRESSION
(lambda [token]
(and (every-character
(lambda [ch] (not (member ch *special-chars*)))
token)
(not (every-character
(lambda [ch] (member ch *digits*))
token)))))
(define NUMERAL-EXPRESSION
(lambda [token]
(every-character (lambda [ch] (member ch *digits*))
(if (member (nth-char 1 token) [#+ #-])
(string-rest token)
token))))
;;; ===============================================================
;;; STRING MANIPULATION ROUTINES
;;; ===============================================================
(define STRING-NULL
(lambda [string] (= string "")))
(define STRING-FIRST
(lambda [string] (nth-char 1 string)))
(define STRING-REST
(lambda [string] (substring 2 (string-length string) string)))
;;; (EVERY-CHARACTER predicate string) True just in case predicate
;;; ================================== is true of each character in
;;; string.
(define EVERY-CHARACTER
(lambda [predicate string]
(cond [(string-null string) $true]
[(predicate (string-first string))
(every-character predicate (string-rest string))]
[$true $false])))
(init-lexan)