add object helper functions
This commit is contained in:
parent
06d19b2867
commit
0d5f4545d0
|
@ -12,71 +12,42 @@
|
||||||
;;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
;;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
;;; Singly linked list with tail pointer, message passing style.
|
;;; Singly linked list with tail pointer, message passing style.
|
||||||
|
|
||||||
;;; LINKED-LIST-ELEM:
|
|
||||||
;;;
|
;;;
|
||||||
;;; (NEXT): Goes to the next element, if it exists. Does nothing if the
|
|
||||||
;;; iterator is at the end of the list.
|
|
||||||
;;; (GET): Gets the element at this list, signals error if at the end
|
|
||||||
;;; of the list.
|
|
||||||
;;; (EMPTY?): True if there are no more values to go to.
|
|
||||||
|
|
||||||
(define linked-list-elem:new
|
|
||||||
(lambda (ptr)
|
|
||||||
(lambda args
|
|
||||||
(let ((op (car args)))
|
|
||||||
(cond
|
|
||||||
((eq? op 'next)
|
|
||||||
(if (not (null? ptr))
|
|
||||||
(set! ptr (cdr ptr))
|
|
||||||
#f)
|
|
||||||
(null? ptr))
|
|
||||||
((eq? op 'get) (car ptr))
|
|
||||||
((eq? op 'empty?) (null? ptr)))))))
|
|
||||||
|
|
||||||
;;; LINKED-LIST:
|
;;; LINKED-LIST:
|
||||||
;;;
|
;;;
|
||||||
;;; (PUSH): Pushes a value to the head of the list.
|
;;; (PUSH! VAL): Pushes a value to the head of the list.
|
||||||
;;; (PUSH-TAIL): Pushes a value to the end of the list.
|
;;; (PUSH-TAIL! VAL): Pushes a value to the end of the list.
|
||||||
|
;;; (SET-CDR! VAL): Set the cdr of TAIL to VAL. This will be overrwitten
|
||||||
|
;;; if PUSH-TAIL! is called again.
|
||||||
;;; (TO-LIST): Returns a list structure.
|
;;; (TO-LIST): Returns a list structure.
|
||||||
;;; (TRAVERSE-FROM-HEAD): Returns an instance of LINKED-LIST-ELEM pointing
|
|
||||||
;;; to the head of the list.
|
|
||||||
|
|
||||||
(define linked-list:new
|
(define linked-list:new
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(let ((head '())
|
(letrec
|
||||||
(tail '()))
|
((head '())
|
||||||
(letrec ((push
|
(tail '())
|
||||||
(lambda (val)
|
(this
|
||||||
(set! head (cons val head))
|
(object/procedures
|
||||||
(if (null? tail)
|
'push!
|
||||||
(set! tail head)
|
(lambda (val)
|
||||||
#f)))
|
(set! head (cons val head))
|
||||||
(push-tail
|
(if (null? tail)
|
||||||
(lambda (val)
|
(set! tail head))
|
||||||
(if (null? tail)
|
this)
|
||||||
(push val)
|
'push-tail!
|
||||||
(begin
|
(lambda (val)
|
||||||
(set-cdr! tail (cons val '()))
|
(if (null? tail)
|
||||||
(set! tail (cdr tail))))))
|
(this 'push! val)
|
||||||
(%set-cdr!
|
(begin
|
||||||
(lambda (val)
|
(set-cdr! tail (cons val '()))
|
||||||
(if (null? tail)
|
(set! tail (cdr tail))))
|
||||||
(error "cannot set cdr of empty list")
|
this)
|
||||||
(set-cdr! tail val)))))
|
'set-cdr!
|
||||||
(lambda args
|
(lambda (val)
|
||||||
(let ((op (car args)))
|
(if (null? tail)
|
||||||
(cond
|
(error "cannot set cdr of empty list")
|
||||||
((eq? op 'push) (apply push (cdr args)))
|
(set-cdr! tail val))
|
||||||
((eq? op 'push-tail) (apply push-tail (cdr args)))
|
this)
|
||||||
((eq? op 'set-cdr!) (apply %set-cdr! (cdr args)))
|
'to-list
|
||||||
((eq? op 'to-list) head)
|
(lambda () head))))
|
||||||
((eq? op 'traverse-from-head) (linked-list-elem:new head))
|
this)))
|
||||||
(else (error (cons "invalid operation" args))))))))))
|
|
||||||
|
|
||||||
(define x (linked-list:new))
|
|
||||||
(x 'push-tail 1)
|
|
||||||
(x 'push-tail 2)
|
|
||||||
(x 'push-tail 3)
|
|
||||||
(x 'to-list)
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
;;; Copyright (C) Peter McGoron 2024
|
||||||
|
;;; This program is free software: you can redistribute it and/or modify
|
||||||
|
;;; it under the terms of the GNU General Public License as published by
|
||||||
|
;;; the Free Software Foundation, version 3 of the License.
|
||||||
|
;;;
|
||||||
|
;;; This program is distributed in the hope that it will be useful,
|
||||||
|
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
;;; GNU General Public License for more details.
|
||||||
|
;;;
|
||||||
|
;;; You should have received a copy of the GNU General Public License
|
||||||
|
;;; along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
;;;
|
||||||
|
;;;
|
||||||
|
;;; Handles messages passed to objects. The state of each object is
|
||||||
|
;;; expected to be in the table or in the environment. Each message
|
||||||
|
;;; invokes a procedure whose name is the first argument to the object,
|
||||||
|
;;; and the arguments to that procedure are the rest of the arguments to
|
||||||
|
;;; the object.
|
||||||
|
|
||||||
|
;;; Lookup NAME-AS-SYMBOL in TABLE and returns the handler, or the default
|
||||||
|
;;; handler if not available.
|
||||||
|
(define object:lookup
|
||||||
|
(lambda (table name-as-symbol)
|
||||||
|
(let ((node (smap:search table (symbol->string name-as-symbol))))
|
||||||
|
(if (null? node)
|
||||||
|
(set! node (smap:search table "default")))
|
||||||
|
(if (null? node)
|
||||||
|
(error "object:lookup" "no handler found for" name-as-symbol)
|
||||||
|
(map:val node)))))
|
||||||
|
|
||||||
|
;;; Create an object with TABLE as its procedure table.
|
||||||
|
(define object/table
|
||||||
|
(lambda (table)
|
||||||
|
(lambda (op . args)
|
||||||
|
(apply (object:lookup table op) args))))
|
||||||
|
|
||||||
|
;;; Append procedures to a table.
|
||||||
|
(define object:append-table
|
||||||
|
(lambda (table next-pairs)
|
||||||
|
(if (null? next-pairs)
|
||||||
|
table
|
||||||
|
(let ((key (symbol->string (car next-pairs)))
|
||||||
|
(proc (cadr next-pairs)))
|
||||||
|
(object:append-table
|
||||||
|
(car (smap:insert table key proc))
|
||||||
|
(cddr next-pairs))))))
|
||||||
|
|
||||||
|
;;; Convert a list of 'SYMBOL PROCEDURE ... into a table.
|
||||||
|
(define object:list->table
|
||||||
|
(lambda pairs
|
||||||
|
(object:append-table '() pairs)))
|
||||||
|
|
||||||
|
|
||||||
|
(define object/procedures
|
||||||
|
(lambda procedures
|
||||||
|
(object/table (apply object:list->table procedures))))
|
138
read.scm
138
read.scm
|
@ -39,6 +39,7 @@
|
||||||
(load "chez-compat.scm")
|
(load "chez-compat.scm")
|
||||||
(load "util.scm")
|
(load "util.scm")
|
||||||
(load "set.scm")
|
(load "set.scm")
|
||||||
|
(load "object.scm")
|
||||||
(load "linked-list.scm")
|
(load "linked-list.scm")
|
||||||
|
|
||||||
;;; My text editor cannot parse Scheme's character syntax.
|
;;; My text editor cannot parse Scheme's character syntax.
|
||||||
|
@ -67,72 +68,73 @@
|
||||||
;;; (PEEK): Read character, push it back, and return it.
|
;;; (PEEK): Read character, push it back, and return it.
|
||||||
;;; (FOLD-CASE?): Returns a boolean if case folding is enabled.
|
;;; (FOLD-CASE?): Returns a boolean if case folding is enabled.
|
||||||
;;; (FOLD-CASE! BOOL): Sets the case folding to BOOL.
|
;;; (FOLD-CASE! BOOL): Sets the case folding to BOOL.
|
||||||
|
|
||||||
(define port->read
|
(define port->read
|
||||||
(lambda (read-function filename)
|
(lambda (read-function filename)
|
||||||
(let ((line-number 1)
|
(letrec ((line-number 1)
|
||||||
(offset 0)
|
(offset 0)
|
||||||
(pushback-buffer '())
|
(pushback-buffer '())
|
||||||
(datum-labels '())
|
(datum-labels '())
|
||||||
(fold-case? #f))
|
(fold-case? #f)
|
||||||
(letrec ((update-position!
|
(this
|
||||||
(lambda (ch)
|
(object/procedures
|
||||||
(cond
|
'process
|
||||||
((eqv? ch #\newline)
|
(lambda (ch)
|
||||||
(set! line-number (+ 1 line-number)) (set! offset 0))
|
(this 'update-position! ch)
|
||||||
;; OFFSET is sometimes set to #F to denote an unknown
|
(cond
|
||||||
;; offset.
|
((or (eof-object? ch) (not ch)) ch)
|
||||||
(offset (set! offset (+ 1 offset))))))
|
(fold-case? (char-downcase ch))
|
||||||
(set-datum-label!
|
(else ch)))
|
||||||
(lambda (label value)
|
'update-position!
|
||||||
(set! datum-labels
|
(lambda (ch)
|
||||||
(car (smap:insert datum-labels label value)))))
|
(cond
|
||||||
(get-datum-label
|
((eqv? ch #\newline)
|
||||||
(lambda (label)
|
(set! line-number (+ 1 line-number)) (set! offset 0))
|
||||||
(smap:search datum-labels label)))
|
;; OFFSET is sometimes set to #F to denote an unknown
|
||||||
(dump-mutable
|
;; offset.
|
||||||
(lambda ()
|
(offset (set! offset (+ 1 offset)))))
|
||||||
(list datum-labels fold-case?)))
|
'set-datum-label!
|
||||||
(restore-mutable!
|
(lambda (label value)
|
||||||
(lambda (state)
|
(set! datum-labels
|
||||||
(set! datum-labels (car state))
|
(car (smap:insert datum-labels label value))))
|
||||||
(set! fold-case? (cadr state))))
|
'get-datum-label
|
||||||
(process
|
(lambda (label)
|
||||||
(lambda (ch)
|
(smap:search datum-labels label))
|
||||||
(update-position! ch)
|
'dump-mutable
|
||||||
(cond
|
(lambda ()
|
||||||
((or (eof-object? ch) (not ch)) ch)
|
(list datum-labels fold-case?))
|
||||||
(fold-case? (char-downcase ch))
|
'restore-mutable!
|
||||||
(else ch))))
|
(lambda (state)
|
||||||
(port
|
(set! datum-labels (car state))
|
||||||
(lambda (op . args)
|
(set! fold-case? (cadr state)))
|
||||||
;; TODO: turn into string map?
|
'pos
|
||||||
(cond
|
(lambda () (list filename line-number offset))
|
||||||
((eq? op 'pos) (list filename line-number offset))
|
'read
|
||||||
((eq? op 'read)
|
(lambda ()
|
||||||
(process
|
(this 'process
|
||||||
(if (null? pushback-buffer)
|
(if (null? pushback-buffer)
|
||||||
(read-function)
|
(read-function)
|
||||||
(let ((ch (car pushback-buffer)))
|
(let ((ch (car pushback-buffer)))
|
||||||
(set! pushback-buffer (cdr pushback-buffer))
|
(set! pushback-buffer (cdr pushback-buffer))
|
||||||
ch))))
|
ch))))
|
||||||
((eq? op 'peek)
|
'peek
|
||||||
(let ((ch (port 'read)))
|
(lambda ()
|
||||||
(port 'push ch)
|
(let ((ch (this 'read)))
|
||||||
ch))
|
(this 'push! ch)
|
||||||
((eq? op 'push)
|
ch))
|
||||||
(let ((ch (car args)))
|
'push!
|
||||||
(if (eqv? ch #\newline)
|
(lambda (ch)
|
||||||
(begin
|
(if (eqv? ch #\newline)
|
||||||
(set! line-number (- line-number 1))
|
(begin
|
||||||
(set! offset #f))
|
(set! line-number (- line-number 1))
|
||||||
(set! offset (- offset 1)))
|
(set! offset #f))
|
||||||
(set! pushback-buffer (cons ch pushback-buffer))))
|
(set! offset (- offset 1)))
|
||||||
((eq? op 'fold-case?) fold-case?)
|
(set! pushback-buffer (cons ch pushback-buffer)))
|
||||||
((eq? op 'fold-case!) (set! fold-case? (car args)))
|
'fold-case?
|
||||||
((eq? op 'set-datum-label!) (apply set-datum-label! args))
|
(lambda () fold-case?)
|
||||||
((eq? op 'get-datum-label) (apply get-datum-label args))
|
'fold-case!
|
||||||
(else (error "read->port: invalid" (cons op args)))))))
|
(lambda (val) (set! fold-case? val)))))
|
||||||
port))))
|
this)))
|
||||||
|
|
||||||
;;; ;;;;;;;;;;;;;;
|
;;; ;;;;;;;;;;;;;;
|
||||||
;;; Character maps
|
;;; Character maps
|
||||||
|
@ -286,13 +288,13 @@
|
||||||
;;; Push back CHAR and return ACC.
|
;;; Push back CHAR and return ACC.
|
||||||
(define readtable:return-acc-keep-char
|
(define readtable:return-acc-keep-char
|
||||||
(lambda (table char acc port)
|
(lambda (table char acc port)
|
||||||
(port 'push char)
|
(port 'push! char)
|
||||||
acc))
|
acc))
|
||||||
|
|
||||||
;;; Push CHAR to ACC and continue reading from TABLE.
|
;;; Push CHAR to ACC and continue reading from TABLE.
|
||||||
(define readtable:push-char
|
(define readtable:push-char
|
||||||
(lambda (table char acc port)
|
(lambda (table char acc port)
|
||||||
(acc 'push-tail char)
|
(acc 'push-tail! char)
|
||||||
(readtable:act table (port 'read) acc port)))
|
(readtable:act table (port 'read) acc port)))
|
||||||
|
|
||||||
;;; Define a readtable that constructs an identifier by accepting all
|
;;; Define a readtable that constructs an identifier by accepting all
|
||||||
|
@ -326,7 +328,7 @@
|
||||||
(define readtable:read-ident
|
(define readtable:read-ident
|
||||||
(lambda (table char acc port)
|
(lambda (table char acc port)
|
||||||
(let ((lst (linked-list:new)))
|
(let ((lst (linked-list:new)))
|
||||||
(lst 'push char)
|
(lst 'push! char)
|
||||||
(list->string
|
(list->string
|
||||||
((readtable:act readtable:identifier
|
((readtable:act readtable:identifier
|
||||||
(port 'read) lst port)
|
(port 'read) lst port)
|
||||||
|
@ -385,7 +387,7 @@
|
||||||
port)))
|
port)))
|
||||||
(cond
|
(cond
|
||||||
((eqv? value 'end-of-list) (acc 'to-list))
|
((eqv? value 'end-of-list) (acc 'to-list))
|
||||||
(else (acc 'push-tail value)
|
(else (acc 'push-tail! value)
|
||||||
(loop)))))))
|
(loop)))))))
|
||||||
(loop)))))
|
(loop)))))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue