and
.B SodClass
is its own metaclass.
-It is not possible to define root classes because of circularities:
+It is not possible to define root classes in module files
+because of circularities:
.B SodObject
has
.B SodClass
-as its metaclass, and
+as its metaclass,
+and
.B SodClass
is a subclass of
.BR SodObject .
.IB a ;
.PP
for each of
-.IR C 's superclasses
+.IR C 's
+superclasses
.IR A
in the same chain in some (unimportant) order.
A `pointer to
.B ichain
must have as a prefix the
.B ichain
-for each superclass in the same chain, and
-each slot must be stored in exactly one place.
+for each superclass in the same chain,
+and each slot must be stored in exactly one place.
The layout of vtables doesn't have this second requirement:
it doesn't matter that there are
multiple method entry pointers
for the same effective method
as long as they all work correctly.
+Indeed, it's essential that they do,
+because each chain's method entry function
+will need to apply a different offset to the receiver pointer
+before invoking the effective method.
.PP
A vtable for a class
.I C
.fi
.PP
The outer layer is a
-.IP
.B union
.IB C __vtu_ h
-.PP
containing a member
.IP
.B struct
The metaclass
.I R
of
-.IR O .
+.I O
is then the
.I root metaclass
of
in the same chain as
.IR J .
Then, if there is currently no class pointer of type
-.I Q
+.IR Q ,
then add a member
.RS
.IP
.I A
defines any messages,
and there is currently no member
-.I a
+.IR a ,
then add a member
.RS
.IP
and wishes to read more. If DONEP is true then the condition (<= START
USED END) must hold; the FUNC has consumed the buffer as far as USED
(exclusive) and has completed successfully; the values DONEP and `t' are
- returned as the result of `charbuf-scanner-map'.
+ returned as the result of `charbuf-scanner-map', along with a CONSUMEDP
+ flag.
If end-of-file is encountered before FUNC completes successfully then FAIL
- is called with no arguments, and `charbuf-scanner-map' returns whatever
- FAIL returns.
+ is called with no arguments and expected to return two values, and
+ `charbuf-scanner-map' returns these values, along with a CONSUMEDP flag.
Observe that, if FAIL returns a second value of nil, then
`charbuf-scanner-map' is usable as a parser expression."))
(defmethod charbuf-scanner-map
((scanner charbuf-scanner) func &optional fail)
(with-slots (buf index size) scanner
- (flet ((offer (buf start end)
-
- ;; Pass the buffer to the function, and see what it thought.
- (multiple-value-bind (donep used) (funcall func buf start end)
-
- ;; Update the position as far as the function read.
- (with-slots (line column) scanner
- (let ((l line) (c column) (limit (if donep used end)))
- (do ((i start (1+ i)))
- ((>= i limit))
- (setf (values l c)
- (update-position (char buf i) l c)))
- (setf line l column c)))
-
- ;; If the function is finished then update our state and
- ;; return.
- (when donep
- (setf index used)
- (when (>= index size)
- (charbuf-scanner-fetch scanner))
- (return-from charbuf-scanner-map (values donep t))))))
-
- ;; If there's anything in the current buffer, offer it to the function.
- (when (< index size)
- (offer buf index size))
-
- ;; Repeatedly fetch new buffers and offer them to the function.
- ;; Because the buffers are fresh, we know that we must process them
- ;; from the beginning. Note that `offer' will exit if FUNC has
- ;; finished, so we don't need to worry about that.
- (loop
- (unless (charbuf-scanner-fetch scanner)
- (return (if fail (funcall fail) (values nil nil))))
- (offer buf 0 size)))))
+ (let ((consumedp nil))
+ (flet ((offer (buf start end)
+
+ ;; Pass the buffer to the function, and see what it thought.
+ (multiple-value-bind (donep used) (funcall func buf start end)
+
+ ;; Update the position as far as the function read.
+ (with-slots (line column) scanner
+ (let ((l line) (c column) (limit (if donep used end)))
+ (do ((i start (1+ i)))
+ ((>= i limit))
+ (setf (values l c)
+ (update-position (char buf i) l c)))
+ (setf line l column c)))
+
+ ;; If the function is finished then update our state and
+ ;; return.
+ (when donep
+ (setf index used)
+ (when (>= index size)
+ (charbuf-scanner-fetch scanner))
+ (return-from charbuf-scanner-map
+ (values donep t (or consumedp (> used start)))))
+
+ ;; We've definitely used that buffer.
+ (setf consumedp t))))
+
+ ;; If there's anything in the current buffer, offer it to the
+ ;; function.
+ (when (< index size)
+ (offer buf index size))
+
+ ;; Repeatedly fetch new buffers and offer them to the function.
+ ;; Because the buffers are fresh, we know that we must process them
+ ;; from the beginning. Note that `offer' will exit if FUNC has
+ ;; finished, so we don't need to worry about that.
+ (loop
+ (unless (charbuf-scanner-fetch scanner)
+ (return (if fail
+ (multiple-value-bind (result win) (funcall fail)
+ (values result win consumedp))
+ (values nil nil consumedp))))
+ (offer buf 0 size))))))
;;;--------------------------------------------------------------------------
;;; Initialization.
(unless end (setf end (length seq)))
(let ((i start) (n (- end start)))
(labels ((copy (i buf start end)
- (do ((j i (1+ j))
- (k start (1+ k)))
- ((>= k end))
- (setf (char seq j) (schar buf k))))
+ (replace seq buf :start1 i :start2 start :end2 end))
(snarf (buf start end)
(let ((m (- end start)))
(cond ((< m n)
(flet ((snarf (buf start end)
(let ((pos (position #\newline buf :start start :end end)))
(push (make-charbuf-slice buf start (or pos end)) slices)
- (if pos
- (values (concatenate-charbuf-slices (nreverse slices))
- (1+ pos))
- (values nil 0))))
- (fail ()
- (values (concatenate-charbuf-slices (nreverse slices)) t)))
- (charbuf-scanner-map scanner #'snarf #'fail)))))
+ (values pos (and pos (1+ pos))))))
+ (multiple-value-bind (result eofp consumedp)
+ (charbuf-scanner-map scanner #'snarf)
+ (declare (ignore result consumedp))
+ (values (concatenate-charbuf-slices (nreverse slices))) eofp)))))
;;;----- That's all, folks --------------------------------------------------