objects.sl 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922
  1. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  2. %
  3. % Objects.SL - A simple facility for object-oriented programming.
  4. %
  5. % Author: Alan Snyder
  6. % Hewlett-Packard/CRC
  7. % Date: 22 July 1982
  8. % Revised: 16 February 1983
  9. %
  10. % 16-Feb-83 Alan Snyder
  11. % Add ev-send function. Rename declare and undeclare to declare-flavor
  12. % and undeclare-flavor, to avoid conflict with common lisp declare.
  13. % 30-Dec-82 Alan Snyder
  14. % General clean-up; rename internal functions and variables; document
  15. % method lookup functions; add method lookup trace facility.
  16. % 1-Nov-82 Alan Snyder
  17. % Added Object-Type function.
  18. % 27-Sept-82 Alan Snyder
  19. % Removed Variable-Table (which was available only at compile-time); made
  20. % Variable-Names available at both compile-time and load-time; now use
  21. % Variable-Names to "compile" method bodies. Result: now can compile new
  22. % method bodies after loading a "compiled" flavor definition.
  23. % 27-Sept-82 Alan Snyder
  24. % Evaluating (or loading) a DEFFLAVOR no longer clears the method table, if it
  25. % had been defined previously.
  26. %
  27. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  28. (Bothtimes (imports '(common fast-vector)))
  29. (imports '(association strings))
  30. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  31. % NOTE: THIS FILE DEFINES MACROS. IT MUST BE LOADED BEFORE ANY OF THESE
  32. % FUNCTIONS ARE USED. The recommended way to do this is to put the statement
  33. % (BothTimes (load objects)) at the beginning of your source file.
  34. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  35. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  36. %
  37. % Summary of Public Functions:
  38. %
  39. % (defflavor flavor-name (var1 var2 ...) (flav1 flav2 ...) option1 option2 ...)
  40. % (defmethod (flavor-name message-name) (arg1 arg2 ...) form1 form2 ...)
  41. %
  42. % (make-instance 'flavor-name 'var1 value1 ...)
  43. %
  44. % (=> foo message-name arg1 arg2 ...)
  45. %
  46. % (send foo 'message-name arg1 arg2 ...)
  47. % (lexpr-send foo 'message-name arg1 arg2 ... rest-arg-list)
  48. % (lexpr-send-1 foo 'message-name arg-list)
  49. % (ev-send foo 'message-name arg-list) {EXPR form}
  50. %
  51. % (send-if-handles foo 'message-name arg1 arg2 ...)
  52. % (lexpr-send-if-handles foo 'message-name arg1 arg2 ... rest-arg-list)
  53. % (lexpr-send-1-if-handles foo 'message-name arg-list)
  54. %
  55. % (instantiate-flavor 'flavor-name init-list)
  56. %
  57. % (object-type x) --- returns the type of an object, or NIL if not an object
  58. %
  59. % (object-get-handler x message-name) -- lookup method function (see below)
  60. % (object-get-handler-quietly x message-name)
  61. %
  62. % (trace-method-lookups) - start recording stats about method lookup
  63. % (untrace-method-lookups) - stop recording stats about method lookup
  64. % (print-method-lookup-info) - untrace and print accumulated stats
  65. %
  66. % (declare-flavor flavor var1 var2 ...) NOTE: see warnings below!
  67. % (undeclare-flavor var1 var2 ...)
  68. %
  69. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  70. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  71. % Private Constants, Fluids, and Macros (mere mortals should ignore these)
  72. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  73. (fluid '($defflavor-expansion-context
  74. $object-number-of-reserved-slots
  75. $object-flavor-slot
  76. $object-debug-slot
  77. $defflavor-option-table
  78. $method-lookup-stats
  79. ))
  80. (setf $defflavor-expansion-context NIL)
  81. (BothTimes (progn
  82. (setf $object-number-of-reserved-slots 2)
  83. (setf $object-flavor-slot 0)
  84. (setf $object-debug-slot 1)
  85. ))
  86. (setf $defflavor-option-table
  87. (list
  88. (cons 'gettable-instance-variables '$defflavor-do-gettable-option)
  89. (cons 'settable-instance-variables '$defflavor-do-settable-option)
  90. (cons 'initable-instance-variables '$defflavor-do-initable-option)
  91. ))
  92. % Note the free variable FLAVOR-NAME in this macro:
  93. (defmacro $defflavor-error (format . arguments)
  94. `(ContinuableError 1000 (BldMsg ,(string-concat "DEFFLAVOR %w: " format)
  95. flavor-name . ,arguments) NIL))
  96. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  97. % Public Functions
  98. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  99. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  100. %
  101. % DEFFLAVOR - Define a new flavor of Object
  102. %
  103. % Examples:
  104. %
  105. % (defflavor complex-number (real-part imaginary-part) ())
  106. %
  107. % (defflavor complex-number (real-part imaginary-part) ()
  108. % gettable-instance-variables
  109. % initable-instance-variables
  110. % )
  111. %
  112. % (defflavor complex-number ((real-part 0.0)
  113. % (imaginary-part 0.0)
  114. % )
  115. % ()
  116. % gettable-instance-variables
  117. % (settable-instance-variables real-part)
  118. % )
  119. %
  120. % An object is represented by a vector; instance variables are allocated
  121. % specific slots in the vector. Do not use names like "IF" or "WHILE" for
  122. % instance varibles: they are translated freely within method bodies (see
  123. % DEFMETHOD). Initial values for instance variables may be specified as
  124. % arguments to MAKE-INSTANCE, or as initializing expressions in the variable
  125. % list, or may be supplied by an INIT method (see MAKE-INSTANCE).
  126. % Uninitializied instance variables are bound to *UNBOUND*.
  127. %
  128. % The component flavor list currently must be null. Recognized options are:
  129. %
  130. % (GETTABLE-INSTANCE-VARIABLES var1 var2 ...)
  131. % (SETTABLE-INSTANCE-VARIABLES var1 var2 ...)
  132. % (INITABLE-INSTANCE-VARIABLES var1 var2 ...)
  133. % GETTABLE-INSTANCE-VARIABLES [make all instance variables GETTABLE]
  134. % SETTABLE-INSTANCE-VARIABLES [make all instance variables SETTABLE]
  135. % INITABLE-INSTANCE-VARIABLES [make all instance variables INITABLE]
  136. %
  137. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  138. (defmacro defflavor (flavor-name variable-list flavor-list . options-list)
  139. (prog (var-names % List of valid instance variable names
  140. init-code % body of DEFAULT-INIT method
  141. describe-code % body of DESCRIBE method
  142. defmethod-list % list of created DEFMETHODs
  143. var-options % AList mapping var names to option list
  144. initable-vars % list of INITABLE instance variables
  145. )
  146. (desetq (var-names init-code)
  147. ($defflavor-process-varlist flavor-name variable-list)
  148. )
  149. (setf describe-code ($defflavor-build-describe flavor-name var-names))
  150. (setf var-options
  151. ($defflavor-process-options-list flavor-name var-names options-list)
  152. )
  153. (setf defmethod-list ($defflavor-create-methods flavor-name var-options))
  154. (setf initable-vars ($defflavor-initable-vars flavor-name var-options))
  155. (put flavor-name 'variable-names var-names)
  156. (setf defmethod-list
  157. (cons `(defmethod (,flavor-name default-init) () . ,init-code)
  158. defmethod-list))
  159. (setf defmethod-list
  160. (cons `(defmethod (,flavor-name describe) () . ,describe-code)
  161. defmethod-list))
  162. (if flavor-list
  163. ($defflavor-error "Component Flavors not implemented")
  164. )
  165. % The previous actions happen at compile or dskin time.
  166. % The following actions happen at dskin or load time.
  167. (return `(progn
  168. (if (not (get ',flavor-name 'method-table))
  169. (put ',flavor-name 'method-table (association-create)))
  170. (put ',flavor-name 'instance-vector-size
  171. ,(+ #.$object-number-of-reserved-slots (length var-names)))
  172. (put ',flavor-name 'variable-names ',var-names)
  173. (put ',flavor-name 'initable-variables ',initable-vars)
  174. ,@defmethod-list
  175. '(flavor ,flavor-name) % for documentation only
  176. ))
  177. ))
  178. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  179. %
  180. % DEFMETHOD - Define a method on an existing flavor.
  181. %
  182. % Examples:
  183. %
  184. % (defmethod (complex-number real-part) ()
  185. % real-part)
  186. %
  187. % (defmethod (complex-number set-real-part) (new-real-part)
  188. % (setf real-part new-real-part))
  189. %
  190. % The body of a method can freely refer to the instance variables of the flavor
  191. % and can set them using SETF. Each method defines a function FLAVOR$METHOD
  192. % whose first argument is SELF, the object that is performing the method. All
  193. % references to instance variables (except within vectors or quoted lists) are
  194. % translated to an invocation of the form (IGETV SELF n).
  195. %
  196. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  197. (defmacro defmethod ((flavor-name method-name) argument-list . body)
  198. (setf argument-list (cons 'self argument-list))
  199. (let ((function-name ($defflavor-function-name flavor-name method-name)))
  200. (put function-name 'source-code `(lambda ,argument-list . ,body))
  201. (let ((new-code ($create-method-source-code function-name flavor-name)))
  202. % The previous actions happen at compile or dskin time.
  203. % The following actions happen at dskin or load time.
  204. `(progn
  205. ($flavor-define-method ',flavor-name ',method-name ',function-name)
  206. (putd ',function-name 'expr ',new-code)
  207. '(method ,flavor-name ,method-name) % for documentation only
  208. ))))
  209. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  210. %
  211. % => - Convenient form for sending a message
  212. %
  213. % Examples:
  214. %
  215. % (=> r real-part)
  216. %
  217. % (=> r set-real-part 1.0)
  218. %
  219. % The message name is not quoted. Arguments to the method are supplied as
  220. % arguments to =>.
  221. %
  222. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  223. (defmacro => (object message-name . arguments)
  224. `(send ,object ',message-name . ,arguments))
  225. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  226. %
  227. % SEND - Send a Message (Evaluated Message Name)
  228. %
  229. % Examples:
  230. %
  231. % (send r 'real-part)
  232. %
  233. % (send r 'set-real-part 1.0)
  234. %
  235. % Note that the message name is quoted.
  236. %
  237. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  238. (defmacro send (target-form method-form . argument-forms)
  239. % If the method name is known at compile time (i.e., the method-form is of
  240. % the form (QUOTE <id>)) and the target is either SELF (within the body of a
  241. % DEFMETHOD) or a variable which has been declared (using DECLARE-FLAVOR),
  242. % then optimize the form to a direct invocation of the method function.
  243. (if (and (PairP method-form)
  244. (eq (car method-form) 'quote)
  245. (not (null (cdr method-form)))
  246. (IdP (cadr method-form))
  247. )
  248. (let ((method-name (cadr method-form)))
  249. (cond ((and (eq target-form 'self) $defflavor-expansion-context)
  250. ($self-send-expansion method-name argument-forms))
  251. ((and (IdP target-form) (get target-form 'declared-type))
  252. ($direct-send-expansion target-form method-name argument-forms))
  253. (t ($normal-send-expansion target-form method-form argument-forms))
  254. ))
  255. ($normal-send-expansion target-form method-form argument-forms)
  256. ))
  257. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  258. %
  259. % SEND-IF-HANDLES - Conditionally Send a Message (Evaluated Message Name)
  260. %
  261. % Examples:
  262. %
  263. % (send-if-handles r 'real-part)
  264. %
  265. % (send-if-handles r 'set-real-part 1.0)
  266. %
  267. % SEND-IF-HANDLES is like SEND, except that if the object defines no method
  268. % to handle the message, no error is reported and NIL is returned.
  269. %
  270. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  271. (defmacro send-if-handles (object message-name . arguments)
  272. `(let* ((***SELF*** ,object)
  273. (***HANDLER*** (object-get-handler-quietly ***SELF*** ,message-name))
  274. )
  275. (and ***HANDLER*** (apply ***HANDLER*** (list ***SELF*** ,@arguments)))))
  276. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  277. %
  278. % LEXPR-SEND - Send a Message (Explicit "Rest" Argument List)
  279. %
  280. % Examples:
  281. %
  282. % (lexpr-send foo 'bar a b c list)
  283. %
  284. % The last argument to LEXPR-SEND is a list of the remaining arguments.
  285. %
  286. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  287. (defmacro lexpr-send (object message-name . arguments)
  288. (if arguments
  289. (let ((explicit-args (reverse (cdr (reverse arguments))))
  290. (last-arg (LastCar arguments))
  291. )
  292. (if explicit-args
  293. `(lexpr-send-1 ,object ,message-name
  294. (append (list ,@explicit-args) ,last-arg))
  295. `(lexpr-send-1 ,object ,message-name ,last-arg)
  296. )
  297. )
  298. `(let ((***SELF*** ,object))
  299. (apply (object-get-handler ***SELF*** ,message-name)
  300. (list ***SELF***)))
  301. ))
  302. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  303. %
  304. % LEXPR-SEND-IF-HANDLES
  305. %
  306. % This is the same as LEXPR-SEND, except that no error is reported
  307. % if the object fails to handle the message.
  308. %
  309. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  310. (defmacro lexpr-send-if-handles (object message-name . arguments)
  311. (if arguments
  312. (let ((explicit-args (reverse (cdr (reverse arguments))))
  313. (last-arg (LastCar arguments))
  314. )
  315. (if explicit-args
  316. `(lexpr-send-1-if-handles ,object ,message-name
  317. (append (list ,@explicit-args) ,last-arg))
  318. `(lexpr-send-1-if-handles ,object ,message-name ,last-arg)
  319. )
  320. )
  321. `(let* ((***SELF*** ,object)
  322. (***HANDLER***
  323. (object-get-handler-quietly ***SELF*** ,message-name))
  324. )
  325. (and ***HANDLER*** (apply ***HANDLER*** (list ***SELF***))))
  326. ))
  327. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  328. %
  329. % LEXPR-SEND-1 - Send a Message (Explicit Argument List)
  330. %
  331. % Examples:
  332. %
  333. % (lexpr-send-1 r 'real-part nil)
  334. %
  335. % (lexpr-send-1 r 'set-real-part (list 1.0))
  336. %
  337. % Note that the message name is quoted and that the argument list is passed as a
  338. % single argument to LEXPR-SEND-1.
  339. %
  340. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  341. (defmacro lexpr-send-1 (object message-name argument-list)
  342. `(let ((***SELF*** ,object))
  343. (apply (object-get-handler ***SELF*** ,message-name)
  344. (cons ***SELF*** ,argument-list))))
  345. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  346. %
  347. % EV-SEND - EXPR form of LEXPR-SEND-1
  348. %
  349. % EV-SEND is just like LEXPR-SEND-1, except that it is an EXPR instead of
  350. % a MACRO. Its sole purpose is to be used as a run-time function object,
  351. % for example, as a function argument to a function.
  352. %
  353. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  354. (de ev-send (obj msg arg-list)
  355. (lexpr-send-1 obj msg arg-list)
  356. )
  357. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  358. %
  359. % LEXPR-SEND-1-IF-HANDLES
  360. %
  361. % This is the same as LEXPR-SEND-1, except that no error is reported if the
  362. % object fails to handle the message.
  363. %
  364. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  365. (defmacro lexpr-send-1-if-handles (object message-name argument-list)
  366. `(let* ((***SELF*** ,object)
  367. (***HANDLER*** (object-get-handler-quietly ***SELF*** ,message-name))
  368. )
  369. (and ***HANDLER*** (apply ***HANDLER*** (cons ***SELF*** ,argument-list)))
  370. ))
  371. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  372. %
  373. % MAKE-INSTANCE - Create a new instance of a flavor.
  374. %
  375. % Examples:
  376. %
  377. % (make-instance 'complex-number)
  378. % (make-instance 'complex-number 'real-part 0.0 'imaginary-part 1.0)
  379. %
  380. % MAKE-INSTANCE accepts an optional initialization list, consisting of
  381. % alternating pairs of instance variable names and corresponding initial values.
  382. %
  383. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  384. (defmacro make-instance (flavor-name . init-plist)
  385. `(instantiate-flavor ,flavor-name
  386. (list . ,init-plist)
  387. ))
  388. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  389. %
  390. % INSTANTIATE-FLAVOR
  391. %
  392. % This is the same as MAKE-INSTANCE, except that the initialization list is
  393. % provided as a single (required) argument.
  394. %
  395. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  396. (defun instantiate-flavor (flavor-name init-plist)
  397. (let* ((vector-size (get flavor-name 'instance-vector-size)))
  398. (if vector-size
  399. (let* ((object (MkVect (- vector-size 1)))
  400. )
  401. (setf (igetv object #.$object-flavor-slot) flavor-name)
  402. (setf (igetv object #.$object-debug-slot) NIL)
  403. (for (from i #.$object-number-of-reserved-slots (- vector-size 1) 1)
  404. (do (iputv object i '*UNBOUND*))
  405. )
  406. ($object-perform-initialization object init-plist)
  407. (send-if-handles object 'default-init)
  408. (send-if-handles object 'init init-plist)
  409. object
  410. )
  411. (ContError 0 "Attempt to instantiate undefined flavor: %w"
  412. flavor-name (Instantiate-Flavor flavor-name init-plist))
  413. )))
  414. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  415. %
  416. % Object-Type
  417. %
  418. % The OBJECT-TYPE function returns the type (an ID) of the specified object, or
  419. % NIL, if the argument is not an object.
  420. %
  421. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  422. (defun object-type (object)
  423. (if (and (VectorP object) (> (UpbV object) 1))
  424. (let ((flavor-name (igetv object #.$object-flavor-slot)))
  425. (if (IdP flavor-name) flavor-name)
  426. )))
  427. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  428. %
  429. % Method Lookup
  430. %
  431. % The following functions return method functions given an object and a message
  432. % name. The returned function can be invoked, passing the object as the first
  433. % argument and the message arguments as the remaining arguments. For example,
  434. % the expression (=> foo gorp a b c) is equivalent to:
  435. %
  436. % (apply (object-get-handler foo 'gorp) (list foo a b c))
  437. %
  438. % It can be useful for efficiency reasons to lookup a method function once and
  439. % then apply it many times to the same object.
  440. %
  441. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  442. (defun object-get-handler (object message-name)
  443. % Returns the method function that implements the specified message when sent
  444. % to the specified object. If no such method exists, generate a continuable
  445. % error.
  446. (let ((flavor-name (object-type object)))
  447. (cond
  448. (flavor-name
  449. (let ((function-name ($flavor-fetch-method flavor-name message-name)))
  450. (or function-name
  451. (ContError 1000
  452. "Flavor %w has no method %w."
  453. flavor-name
  454. message-name
  455. (object-get-handler object message-name)
  456. ))))
  457. (t (ContError 1000
  458. "Object %w cannot receive messages."
  459. object
  460. (object-get-handler object message-name)
  461. )))))
  462. (defun object-get-handler-quietly (object message-name)
  463. % Returns the method function that implements the specified message when sent
  464. % to the specified object, if it exists, otherwise returns NIL.
  465. (let ((flavor-name (object-type object)))
  466. (if flavor-name
  467. ($flavor-fetch-method flavor-name message-name))))
  468. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  469. %
  470. % Method Lookup Tracing
  471. %
  472. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  473. (de trace-method-lookups ()
  474. % Begin accumulating information about method lookups (invocations of
  475. % object-get-handler). The statistics are reset.
  476. (setf $method-lookup-stats (association-create))
  477. (copyd 'object-get-handler '$traced-object-get-handler)
  478. )
  479. (de untrace-method-lookups ()
  480. % Stop accumulating information about method lookups.
  481. (copyd 'object-get-handler '$untraced-object-get-handler)
  482. )
  483. (de print-method-lookup-info ()
  484. % Stop accumulating information about method lookups and print a summary of
  485. % the accumulated information about method lookups. This summary shows which
  486. % methods were looked up and how many times each method was looked up.
  487. (untrace-method-lookups)
  488. (load gsort stringx)
  489. (setf $method-lookup-stats (gsort $method-lookup-stats '$method-info-sortfn))
  490. (for (in pair $method-lookup-stats)
  491. (do (printf "%w %w%n"
  492. (string-pad-left (bldmsg "%w" (cdr pair)) 6)
  493. (car pair))))
  494. )
  495. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  496. %
  497. % DECLARE-FLAVOR
  498. %
  499. % *** Read these warnings carefully! ***
  500. %
  501. % The DECLARE-FLAVOR macro allows you to declare that a specific symbol is
  502. % bound to an object of a specific flavor. This allows the flavors
  503. % implementation to eliminate the run-time method lookup normally associated
  504. % with sending a message to that variable, which can result in an appreciable
  505. % improvement in execution speed. This feature is motivated solely by
  506. % efficiency considerations and should be used ONLY where the performance
  507. % improvement is critical.
  508. %
  509. % Details: if you declare the variable X to be bound to an object of flavor
  510. % FOO, then WITHIN THE CONTEXT OF THE DECLARATION (see below), expressions of
  511. % the form (=> X GORP ...) or (SEND X 'GORP ...) will be replaced by function
  512. % invocations of the form (FOO$GORP X ...). Note that there is no check made
  513. % that the flavor FOO actually contains a method GORP. If it does not, then a
  514. % run-time error "Invocation of undefined function FOO$GORP" will be reported.
  515. %
  516. % WARNING: The DECLARE-FLAVOR feature is not presently well integrated with
  517. % the compiler. Currently, the DECLARE-FLAVOR macro may be used only as a
  518. % top-level form, like the PSL FLUID declaration. It takes effect for all
  519. % code evaluated or compiled henceforth. Thus, if you should later compile a
  520. % different file in the same compiler, the declaration will still be in
  521. % effect! THIS IS A DANGEROUS CROCK, SO BE CAREFUL! To avoid problems, I
  522. % recommend that DECLARE-FLAVOR be used only for uniquely-named variables.
  523. % The effect of a DECLARE-FLAVOR can be undone by an UNDECLARE-FLAVOR, which
  524. % also may be used only as a top-level form. Therefore, it is good practice
  525. % to bracket your code in the source file with a DECLARE-FLAVOR and a
  526. % corresponding UNDECLARE-FLAVOR.
  527. %
  528. % Here are the syntactic details:
  529. %
  530. % (DECLARE-FLAVOR FLAVOR-NAME VAR1 VAR2 ...)
  531. % (UNDECLARE-FLAVOR VAR1 VAR2 ...)
  532. %
  533. % *** Did you read the above warnings??? ***
  534. %
  535. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  536. (defmacro declare-flavor (flavor-name . variable-names)
  537. (prog () % This macro returns NIL!
  538. (if (not (IdP flavor-name))
  539. (StdError
  540. (BldMsg "Flavor name in DECLARE-FLAVOR is not an ID: %p" flavor-name))
  541. % else
  542. (for (in var-name variable-names)
  543. (do (if (not (IdP var-name))
  544. (StdError (BldMsg
  545. "Variable name in DECLARE-FLAVOR is not an ID: %p"
  546. var-name))
  547. % else
  548. (put var-name 'declared-type flavor-name)
  549. )))
  550. )))
  551. (dm undeclare-flavor (form)
  552. (prog () % This macro returns NIL!
  553. (for (in var-name (cdr form))
  554. (do (if (not (IdP var-name))
  555. (StdError (BldMsg
  556. "Variable name in UNDECLARE-FLAVOR is not an ID: %p"
  557. var-name))
  558. % else
  559. (remprop var-name 'declared-type)
  560. )))
  561. ))
  562. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  563. %
  564. % Representation Information:
  565. %
  566. % (You don't need to know any of this to use this stuff.)
  567. %
  568. % A flavor-name is an ID. It has the following properties:
  569. %
  570. % VARIABLE-NAMES A list of the instance variables of the flavor, in
  571. % order of their location in the instance vector. This
  572. % property exists at compile time, dskin time, and load
  573. % time.
  574. %
  575. % INITABLE-VARIABLES A list of the instance variables that have been declared
  576. % to be INITABLE. This property exists at dskin time and
  577. % at load time.
  578. %
  579. % METHOD-TABLE An association list mapping each method name (ID)
  580. % defined for the flavor to the corresponding function
  581. % name (ID) that implements the method. This property
  582. % exists at dskin time and at load time.
  583. %
  584. % INSTANCE-VECTOR-SIZE An integer that specifies the number of elements in the
  585. % vector that represents an instance of this flavor. This
  586. % property exists at dskin time and at load time. It is
  587. % used by MAKE-INSTANCE.
  588. %
  589. % The function that implements a method has a name of the form FLAVOR$METHOD.
  590. % Each such function ID has the following properties:
  591. %
  592. % SOURCE-CODE A list of the form (LAMBDA (SELF ...) ...) which is the
  593. % untransformed source code for the method. This property
  594. % exists at compile time and dskin time.
  595. %
  596. % Implementation Note:
  597. %
  598. % A tricky aspect of this code is making sure that the right things happen at
  599. % the right time. When a source file is read and evaluated (using DSKIN), then
  600. % everything must happen at once. However, when a source file is compiled to
  601. % produce a FASL file, then some actions must be performed at compile-time,
  602. % whereas other actions are supposed to occur when the FASL file is loaded.
  603. % Actions to occur at compile time are performed by macros; actions to occur at
  604. % load time are performed by the forms returned by macros.
  605. %
  606. % Another goal of the implementation is to avoid consing whenever possible
  607. % during method invocation. The current scheme prefers to compile into (APPLY
  608. % HANDLER (LIST args...)), for which the PSL compiler will produce code that
  609. % performs no consing.
  610. %
  611. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  612. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  613. % Internal Functions
  614. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  615. (defun $object-perform-initialization (object init-plist)
  616. % Perform the initialization of instance variables in OBJECT as specified by
  617. % the INIT-PLIST, which contains alternating instance variable names and
  618. % initializing values.
  619. (let* ((flavor-name (igetv object #.$object-flavor-slot))
  620. (initable-vars (get flavor-name 'initable-variables))
  621. (variable-names (get flavor-name 'variable-names))
  622. name value
  623. )
  624. (while init-plist
  625. (setf name (car init-plist))
  626. (setf init-plist (cdr init-plist))
  627. (if init-plist
  628. (progn (setf value (car init-plist))
  629. (setf init-plist (cdr init-plist)))
  630. (setf value nil)
  631. )
  632. (if (memq name initable-vars)
  633. (iputv object
  634. ($object-lookup-variable-in-list variable-names name)
  635. value)
  636. (ContinuableError 1000
  637. (BldMsg "%p not an initable instance variable of flavor %w"
  638. name
  639. flavor-name)
  640. NIL)
  641. ))))
  642. (defun $object-lookup-variable-in-list (variable-names name)
  643. (for (in v-name variable-names)
  644. (for i #.$object-number-of-reserved-slots (+ i 1))
  645. (do (if (eq v-name name) (exit i)))
  646. (returns nil)
  647. ))
  648. (defun $substitute-for-symbols (U var-names)
  649. % Substitute in U for all unquoted instances of the symbols defined in
  650. % Var-Names. Also, change SETQ to SETF in forms, since only SETF can handle
  651. % the substituted forms.
  652. (cond
  653. ((IdP U)
  654. (let ((address ($object-lookup-variable-in-list var-names U)))
  655. (if address (list 'igetv 'self address) U)
  656. ))
  657. ((PairP U)
  658. (cond
  659. ((eq (car U) 'quote) U)
  660. ((eq (car U) 'setq)
  661. (cons 'setf ($substitute-for-symbols (cdr U) var-names)))
  662. (t (cons ($substitute-for-symbols (car U) var-names)
  663. ($substitute-for-symbols (cdr U) var-names)))
  664. )
  665. )
  666. (t U)
  667. ))
  668. (defun $flavor-define-method (flavor-name method-name function-name)
  669. (let ((method-table (get flavor-name 'method-table)))
  670. (association-bind method-table method-name function-name)))
  671. (copyd 'flavor-define-method '$flavor-define-method) % for compatibility!
  672. (defun $flavor-fetch-method (flavor-name method-name)
  673. % Returns NIL if the method is undefined.
  674. (let* ((method-table (get flavor-name 'method-table))
  675. (assoc-pair (atsoc method-name method-table))
  676. )
  677. (if assoc-pair (cdr assoc-pair) nil)))
  678. (defun $create-method-source-code (function-name flavor-name)
  679. (let ((var-names (get flavor-name 'variable-names))
  680. (source-code (get function-name 'source-code))
  681. ($defflavor-expansion-context flavor-name) % FLUID variable!
  682. )
  683. ($substitute-for-symbols (MacroExpand source-code) var-names)
  684. ))
  685. (defun $defflavor-process-varlist (flavor-name variable-list)
  686. % Process the instance variable list of a DEFFLAVOR. Create a list of valid
  687. % instance variable names and a list of forms to perform default
  688. % initialization of instance variables.
  689. (prog (var-names default-init-code init-form v)
  690. (for (in v-entry variable-list) (do
  691. (cond ((and (PairP v-entry) (IdP (car v-entry)))
  692. (setf v (car v-entry))
  693. (setf init-form (cdr v-entry))
  694. (if init-form (setf init-form (car init-form)))
  695. (setf init-form `(if (eq ,v '*UNBOUND*) (setf ,v ,init-form)))
  696. (setf default-init-code (aconc default-init-code init-form))
  697. )
  698. ((IdP v-entry) (setf v v-entry))
  699. (t ($defflavor-error "Bad item in variable list: %p" v-entry)
  700. (setf v NIL)
  701. )
  702. )
  703. (if v (setf var-names (aconc var-names v)))
  704. ))
  705. (return (list var-names default-init-code))))
  706. (defun $defflavor-build-describe (flavor-name var-names)
  707. % Return a list of forms that print a description of an instance.
  708. (let ((describe-code
  709. `((printf ,(string-concat "An object of flavor "
  710. (id2string flavor-name)
  711. ", has instance variable values:%n")))))
  712. (for (in v var-names)
  713. (do
  714. (setf describe-code
  715. (aconc describe-code `(printf " %w: %p%n" ',v ,v)))
  716. ))
  717. (aconc describe-code NIL)
  718. ))
  719. (defun $defflavor-process-options-list (flavor-name var-names options-list)
  720. % Return an AList mapping var-names to a list of options
  721. (let ((var-options (association-create)))
  722. (for (in option options-list)
  723. (do ($defflavor-process-option flavor-name var-names
  724. var-options option)
  725. ))
  726. var-options
  727. ))
  728. (defun $defflavor-process-option (flavor-name var-names var-options option)
  729. % Process the option by modifying the AList VAR-OPTIONS.
  730. (let (option-keyword option-arguments)
  731. (cond ((PairP option)
  732. (setf option-keyword (car option))
  733. (setf option-arguments (cdr option))
  734. )
  735. ((IdP option)
  736. (setf option-keyword option)
  737. )
  738. (t ($defflavor-error "Bad item in options list: %p" option)
  739. (setf option-keyword '*NONE*)
  740. )
  741. )
  742. (when (neq option-keyword '*NONE*)
  743. (let ((pair (atsoc option-keyword $defflavor-option-table)))
  744. (if (null pair)
  745. ($defflavor-error "Bad option in options list: %w" option)
  746. (apply (cdr pair)
  747. (list flavor-name var-names var-options option-arguments))
  748. )))))
  749. (defun $defflavor-do-gettable-option (flavor-name var-names var-options args)
  750. ($defflavor-insert-keyword flavor-name var-names var-options args 'GETTABLE)
  751. )
  752. (defun $defflavor-do-settable-option (flavor-name var-names var-options args)
  753. ($defflavor-insert-keyword flavor-name var-names var-options args 'SETTABLE)
  754. )
  755. (defun $defflavor-do-initable-option (flavor-name var-names var-options args)
  756. ($defflavor-insert-keyword flavor-name var-names var-options args 'INITABLE)
  757. )
  758. (defun $defflavor-insert-keyword (flavor-name var-names var-options args key)
  759. (if (null args) (setf args var-names)) % default: applies to all variables
  760. (for (in var args) % for each specified instance variable
  761. (do
  762. (if (not (memq var var-names))
  763. ($defflavor-error "%p (in keyword option) not a variable." var)
  764. % else
  765. (let ((pair (atsoc var var-options)))
  766. (when (null pair)
  767. (setf pair (cons var nil))
  768. (aconc var-options pair)
  769. )
  770. (setf (cdr pair) (adjoinq key (cdr pair)))
  771. )))))
  772. (defun $defflavor-define-access-function (flavor-name var-name)
  773. `(defmethod (,flavor-name ,var-name) () ,var-name))
  774. (defun $defflavor-define-update-function (flavor-name var-name)
  775. (let ((method-name (intern (string-concat "SET-" (id2string var-name)))))
  776. `(defmethod (,flavor-name ,method-name) (new-value)
  777. (setf ,var-name new-value))))
  778. (defun $defflavor-create-methods (flavor-name var-options)
  779. % Return a list of DEFMETHODs for GETTABLE and SETTABLE instance variables.
  780. (let ((defmethod-list))
  781. (for (in pair var-options)
  782. (do
  783. (let ((var-name (car pair))
  784. (keywords (cdr pair))
  785. )
  786. (if (or (memq 'GETTABLE keywords) (memq 'SETTABLE keywords))
  787. (setf defmethod-list
  788. (cons ($defflavor-define-access-function flavor-name var-name)
  789. defmethod-list
  790. )))
  791. (if (memq 'SETTABLE keywords)
  792. (setf defmethod-list
  793. (cons ($defflavor-define-update-function flavor-name var-name)
  794. defmethod-list
  795. )))
  796. )))
  797. defmethod-list
  798. ))
  799. (defun $defflavor-initable-vars (flavor-name var-options)
  800. % Return a list containing the names of instance variables that have been
  801. % declared to be INITable.
  802. (for (in pair var-options)
  803. (when (and (PairP pair)
  804. (or (memq 'INITABLE (cdr pair))
  805. (memq 'SETTABLE (cdr pair))
  806. )))
  807. (collect (car pair))
  808. )
  809. )
  810. (de $defflavor-function-name (flavor-name method-name)
  811. (intern (string-concat (id2string flavor-name) "$" (id2string method-name))))
  812. (de $normal-send-expansion (target-form method-form argument-forms)
  813. `(let ((***SELF*** ,target-form))
  814. (apply (object-get-handler ***SELF*** ,method-form)
  815. (list ***SELF*** ,@argument-forms))))
  816. (de $self-send-expansion (method-name argument-forms)
  817. (cons ($defflavor-function-name $defflavor-expansion-context method-name)
  818. (cons 'self argument-forms)))
  819. (de $direct-send-expansion (target-id method-name argument-forms)
  820. (let ((target-type (get target-id 'declared-type)))
  821. (cons ($defflavor-function-name target-type method-name)
  822. (cons target-id argument-forms))))
  823. (copyd '$untraced-object-get-handler 'object-get-handler)
  824. (de $traced-object-get-handler (obj method-name)
  825. (let* ((result ($untraced-object-get-handler obj method-name))
  826. (count (association-lookup $method-lookup-stats result))
  827. )
  828. (association-bind $method-lookup-stats result (if count (+ count 1) 1))
  829. result
  830. ))
  831. (de $method-info-sortfn (m1 m2)
  832. (numbersortfn (cdr m2) (cdr m1))
  833. )