Re: a "dispatch"-ing object system in CL
- From: Madhu <enometh@xxxxxxxx>
- Date: Sat, 06 Oct 2007 02:31:00 +0530
SCNR. (Will the modeling approach I outline be sufficient ?)
* (Rob Warnock) <OeKdnVTeOPByPZjanZ2dnUVZ_oCvnZ2d@xxxxxxxxxxxxx> :
| Eli Bendersky <eliben@xxxxxxxxx> wrote:
| +---------------
| | OK, heeding the advice I received in this thread, I implemented "wire"
| | as a CLOS class and defined the appropriate generics and methods for
| | it. I chose a class eventually (and not a struct with closures) to
| | allow extension, because it appears to me that such a "wire" class
| | might be extended with special wire types. ...
| +---------------
|
| Great! Here's a hopefully-simple exercise for you: Redfine your WIRE
| type so that the SIGNAL-VALUE is constrained to exactly four values:
| 0, 1, Z, & X (or in CL, maybe 0, 1, nil, & T would be better choices,
| you decide), where the SIGNAL-VALUE of a WIRE is:
;; Use a GATE class that wraps up the the bare thunk abstraction
;; presented in SICP: a GATE has upto two input wires going in an and an
;; output wire coming out. The values are driven by a generic function
;; DRIVER.
(deftype signal-value () '(member 1 0 Z X))
(defclass wire ()
((signal-value :initform 0 :type signal-value :reader get-signal
:writer set-signal)
(gates :initform nil :accessor gates)))
(defclass gate ()
((output :type wire :initarg :output :reader output)))
(defclass gate1 (gate)
((input1 :type wire :initarg :input1 :reader input1)))
(defclass gate2 (gate1)
((input2 :type wire :initarg :input2 :reader input2)))
(defgeneric inputs (gate)
(:method ((gate gate1)) (list (input1 gate)))
(:method ((gate gate2)) (list (input1 gate) (input2 gate))))
(defgeneric driver (gate))
(defmethod set-signal :around (new-value (wire wire))
(let ((old-value (get-signal wire)))
(prog1 (call-next-method)
(unless (eql old-value new-value)
(map nil #'driver (gates wire))))))
(defun %findgate (gate-type gates output &rest inputs)
(find-if (lambda (gate)
(and (typep gate gate-type)
(eql output (output gate))
(endp (set-difference (inputs gate) inputs))))
gates))
;;;
;;;
(defclass and-gate (gate2) ())
(defvar *and-gate-delay* 3)
(defmethod driver ((and-gate and-gate))
(with-slots (input1 input2 output) and-gate
(let ((new-value (logical-and (get-signal input1) (get-signal input2)))
(output output))
(after-delay *and-gate-delay*
(lambda () (set-signal new-value output))))))
(defun and-gate (a1 a2 output)
(or (%findgate 'and-gate (gates a1) output a1 a2)
(let ((gate (make-instance 'and-gate :input1 a1 :input2 a2
:output output)))
(push gate (gates a1))
(push gate (gates a2))
(driver gate)
gate)))
;;;
;;;
(defclass inverter (gate1) ())
(defvar *inverter-delay* 2)
(defmethod driver ((not-gate inverter))
(let ((new-value (logical-not (get-signal (input1 not-gate))))
(output (output not-gate)))
(after-delay *inverter-delay* (lambda () (set-signal new-value
output)))))
(defun inverter (input output)
(or (%findgate 'inverter (gates input) output input)
(let ((gate (make-instance 'inverter :input1 input :output output)))
(push gate (gates input))
(driver gate)
gate)))
;;;
;;;
(defclass or-gate (gate2) ())
(defvar *or-gate-delay* 5)
(defmethod driver ((or-gate or-gate))
(let ((new-value (logical-or (get-signal (input1 or-gate))
(get-signal (input2 or-gate))))
(output (output or-gate)))
(after-delay *or-gate-delay* (lambda () (set-signal new-value
output)))))
(defun or-gate (a1 a2 output)
(or (%findgate 'or-gate (gates a1) output a1 a2)
(let ((gate (make-instance 'or-gate :input1 a1 :input2 a2
:output output)))
(push gate (gates a1))
(push gate (gates a2))
(driver gate)
gate)))
;;; Now Model the situation where ``output of several-gates are
;;; connected to the same wire'' using a CONNECTOR object. This has One
;;; output wire and several iinput wires. (defclass connector (gate)
;;; ((inputs :initform nil :initarg :inputs)))
(defmethod inputs ((connector connector))
(with-slots (inputs) connector
inputs))
(defun connect-outputs-of-gates-with-wire (gates wire)
(make-instance 'connector :inputs (mapcar #'output gates) :output wire))
#||
| 0 if the wire is connected to one or more drivers which are
| actively driving a "0", and is connected to no drivers which
| are actively driving a "1". Any number of connected drivers
| may also be driving with "Z"; they have no effect as long as
| at least one driver is driving a "0".
|
| 1 if the wire is connected to one or more drivers which are
| actively driving a "1", and is connected to no drivers which
| are actively driving a "0". Any number of connected drivers
| may also be driving with "Z"; they have no effect as long as
| at least one driver is driving a "1".
|
| Z if all of the drivers connected to the wire are driving with "Z".
|
| X in any other case. That is, if the wire is being driven to
| "1" by some driver(s) and "0" by other(s), or if *any* driver
| is driving an "X". [I.e., a "bus collision" or otherwise
| indeterminate state.]
||#
;; These rules would go into a driver method:
(defmethod driver ((gate connector))
(let* ((inputs (inputs gate))
(output (output gate))
(new-value
(cond ((and (some (lambda (s) (eql s 0)) inputs)
(notany (lambda (s) (eql s 1)) inputs)
(notany (lambda (s) (eql s 'X)) inputs)) 0)
((and (some (lambda (s) (eql s 1)) inputs)
(notany (lambda (s) (eql s 0)) inputs)
(notany (lambda (s) (eql s 'X)) inputs)) 1)
((every (lambda (s) (eql s 'Z)) inputs) 'Z)
(t 'X))))
(after-delay *and-gate-delay* (lambda () (set-signal new-value output)))))
#||
| Now also redefine all of your gates to implement their logic in
| a similar fashion, e.g., the tables for NOT, TRI, OR, & AND become
| as follows [note that any "Z" as an input must be treated as an
| "X" value, since it's level is arbitrary & unknown]:
|
| NOT (inverter) TRI (non-inverting driver with output enable)
| Input | Output Input | Enable | Output
| ------+------- ------+--------+-------
| 0 | 1 0 | 1 | 0
| 1 | 0 1 | 1 | 1
| Z | X Z | 1 | X
| X | X X | 1 | X
| (any)| 0 | Z
|
| OR Input1 AND Input1
| \ 0 1 Z X \ 0 1 Z X
| I +----------- I +-----------
| n 0 | 0 1 X X n 0 | 0 0 X X
| p 1 | 1 1 X X p 1 | 0 1 X X
| u Z | X X X X u Z | X X X X
| t X | X X X X t X | X X X X
| 2 2
| When the outputs of several gates are connected to the same wire,
| the result is adjusted according to the SIGNAL-VALUE definitions
| given above.
||#
;; XXX How would TRI be used in this framework? For the other functions
;; we used above:
(defun logical-not (a)
(ecase a (0 1) (1 0) (Z 'X) (X 'Z)))
(defun logical-or (a b)
(ecase a
(0 (ecase b (0 0) (1 1) (Z 'X) (X 'X)))
(1 (ecase b (0 1) (1 1) (Z 'X) (Z 'X)))
(Z (ecase b (0 'X) (1 'X) (Z 'X) (X 'X)))
(X (ecase b (0 'X) (1 'X) (Z 'X) (X 'X)))))
(defun logical-and (a b)
(ecase a
(0 (ecase b (0 0) (1 0) (Z 'X) (X 'X)))
(1 (ecase b (0 0) (1 1) (Z 'X) (Z 'X)))
(Z (ecase b (0 'X) (1 'X) (Z 'X) (X 'X)))
(X (ecase b (0 'X) (1 'X) (Z 'X) (X 'X)))))
;; IRL we would use a priority queue but for testing this we can add
;; simple framework bits:
(defstruct queue front tail-cons)
(defun dequeue (queue)
"Remove and return the first item of QUEUE's list"
(let ((tail-cons (queue-tail-cons queue))
(old-queue (queue-front queue)))
(cond ((endp old-queue) (cerror "Return NIL" "Queue Empty"))
(T (prog1 (car old-queue)
(setf (queue-front queue) (cdr old-queue))
(when (eq old-queue tail-cons)
(setf (queue-tail-cons queue) nil)))))))
(defstruct event (timeout 0) thunk)
(defvar *event-queue* (make-queue))
(defvar *global-clock* 0)
(defun after-delay (delay thunk &key (event-queue *event-queue*)
(global-clock *global-clock*))
(let* ((event-timeout (+ global-clock delay))
(item (make-event :timeout event-timeout :thunk thunk))
(tail-cons (queue-tail-cons event-queue))
(list (queue-front event-queue)))
(if (or (endp list) (<= event-timeout (event-timeout (car list))))
(let ((new-cons (cons item list))) ; add at front of event queue
(setf (queue-front event-queue) new-cons)
(unless tail-cons (setf (queue-tail-cons event-queue) new-cons)))
(loop for prev-cons = list then head ; insert sorted
for head on (cdr list)
if (<= event-timeout (event-timeout (car head)))
do (loop-finish) finally
(let ((new-cons (cons item head)))
(setf (cdr prev-cons) new-cons)
(unless head (setf (queue-tail-cons event-queue) new-cons)))))))
(defun propagate (&key (event-queue *event-queue*))
(loop for x = (queue-front event-queue) while x do
(let* ((event (dequeue event-queue))
(thunk (event-thunk event)))
(assert (>= (event-timeout event) *global-clock*))
(setq *global-clock* (event-timeout event))
(when thunk (funcall thunk))))
*global-clock*)
;; and test the half adder like this:
(defun half-adder (a b s c)
(let ((d (make-instance 'wire :name 'd))
(e (make-instance 'wire :name 'e)))
(or-gate a b d)
(and-gate a b c)
(inverter c e)
(and-gate d e s)
(values d e)))
#||
(setq *global-clock* 0 *event-queue* (make-queue))
(defvar $input-1 (make-instance 'wire))
(defvar $input-2 (make-instance 'wire))
(defvar $sum (make-instance 'wire))
(defvar $carry (make-instance 'wire))
(half-adder $input-1 $input-2 $sum $carry)
(set-signal 1 $input-2)
(propagate)
(get-signal $sum)
||#
#:END
| Advanced exercise: Now that you've got all *that* working, add
| a subclass of WIRE called, say, WIRE/LIGHT-PULLUP, which never
| resolves to the value of "Z". Instead, if the resulting SIGNAL-VALUE
| would be "Z", let it be "1" instead. Verify that this doesn't
| break anything else.
I'm not sure what this means. Do you think the stuff above models your
requirements correctly so far?
I havent risked hurting my head reading below this :)
<snip>
--
Madhu
.
- References:
- a "dispatch"-ing object system in CL
- From: Eli Bendersky
- Re: a "dispatch"-ing object system in CL
- From: Eli Bendersky
- Re: a "dispatch"-ing object system in CL
- From: Rainer Joswig
- Re: a "dispatch"-ing object system in CL
- From: Eli Bendersky
- Re: a "dispatch"-ing object system in CL
- From: Rob Warnock
- a "dispatch"-ing object system in CL
- Prev by Date: Re: defvar affecting captured closure variables ?
- Next by Date: Re: a "dispatch"-ing object system in CL
- Previous by thread: Re: a "dispatch"-ing object system in CL
- Next by thread: Re: a "dispatch"-ing object system in CL
- Index(es):
Relevant Pages
|