mstruct.red 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. module mstruct; % A tiny structure package for Standard Lisp.
  2. % Author: Bruce A. Florman.
  3. % Copyright (c) 1989, The RAND Corporation. All rights reserved.
  4. comment
  5. DESCRIPTION
  6. (defstruct <structspec> [ <slotspec>... ] )
  7. The <structspec> may be either the name of the structure, or a list
  8. containing the name followed by zero or more options. Each <slotspec>
  9. may be either a list containing the slot name and its default value,
  10. or simply the slot name, in which case the default value is NIL.
  11. Each option in the <structspec> may be either an option name, or a
  12. list containing the option name and a specified value. If only the
  13. option name is given, then the default value for the given option is
  14. used. If NIL is the specified value in an option, then the option is
  15. not used at all (in general a NIL value is the same as not having that
  16. option in the list at all). If the same option appears more than once
  17. with different values, the last one in the <structspec> takes
  18. precedence.
  19. These are the valid options:
  20. PREDICATE
  21. Makes the zeroth element of the structure contain the structure name
  22. and creates a predicate macro to test if a given item is an instance
  23. of this structure. The specified value is the name of the predicate
  24. macro. The default value is the structure name followed by a `P'.
  25. CONSTRUCTOR
  26. By default the name of the constructor macro is `MAKE-' followed by
  27. the structure name. You may provide a different constructor name
  28. with this option. If there is no constructor option in the
  29. <structspec> the default constructor will still be generated. The
  30. only way to completely suppress the generation of a constructor
  31. macro is to have a (CONSTRUCTOR NIL) option.
  32. The flag !*FASTSTRUCTS controls how the accessor macros expand.
  33. If it is NIL, they expand as GETVs, otherwise they expand as
  34. IGETVs.
  35. NOTE: see records.tst for a level 0 test file.
  36. REVISION HISTORY
  37. 07/19/85 BAF -- File created.
  38. 01/26/89 BAF -- Added predicate and constructor macros so that
  39. this code can replace the RLISP record code.
  40. Changed GetR to StructFetch, and !*FAST-RECORDS
  41. to !*FASTSTRUCTS. Added code to check the
  42. validity of the options. Also added this file
  43. header.
  44. 01/30/89 BAF -- Added CONC-NAME as a synonym for SLOT-PREFIX and
  45. the ExplodeId function for compatability with
  46. existing programs (eg. ernie).
  47. Wed Apr 21 14:22:18 1993 - JBM Convert to RLISP '88, remove prefix
  48. stuff.
  49. Tue May 11 09:03:20 1993 - JBM Remove tconc and fix evaluator bug.
  50. Mon May 17 15:36:54 1993 - JBM Add RSETF function.
  51. Tue May 18 11:09:07 1993 - JBM add qputv for CSL to RSETF;
  52. flag('(defstruct), 'eval);
  53. fluid '(!*faststructs);
  54. switch FASTSTRUCTS;
  55. macro procedure defstruct u;
  56. begin integer indx;
  57. scalar options,slot_forms,name,predicate,constructor,functions;
  58. options := get_defstruct_options cadr u;
  59. if cdr u
  60. then slot_forms := for each slot in cddr u
  61. collect if idp slot then {slot,nil} else slot;
  62. name := car options;
  63. predicate := atsoc('predicate,cdr options);
  64. if predicate then predicate := cdr predicate;
  65. constructor := atsoc('constructor,cdr options);
  66. if constructor then constructor := cdr constructor;
  67. functions := NIL;
  68. if constructor then
  69. functions := build_defstruct_constructor_macro(name,
  70. constructor,
  71. slot_forms,
  72. predicate)
  73. . functions;
  74. if predicate then
  75. functions :=
  76. build_defstruct_predicate_function(name, predicate) . functions;
  77. indx := if predicate then 1 else 0;
  78. for each slot in slot_forms do
  79. <<functions :=
  80. build_defstruct_accessor_macro(car slot, indx) . functions;
  81. indx := indx + 1>>;
  82. functions := mkquote name . functions;
  83. return 'progn . reverse functions
  84. end;
  85. expr procedure get_defstruct_options u;
  86. begin scalar name, options, predicate, constructor;
  87. if pairp u then << name := car u; options := cdr u >>
  88. else << name := u; options := nil >>;
  89. if not idp name then error(0, {"bad defstruct name:", name});
  90. for each entry in options
  91. do if entry eq 'predicate then
  92. predicate := intern compress append(explode name, '(p))
  93. else if eqcar(entry, 'predicate) then predicate := cadr entry
  94. else if entry eq 'constructor then
  95. constructor := intern compress append('(m a k e !! !-),
  96. explode name)
  97. else if eqcar(entry,'constructor)
  98. then constructor := cadr entry
  99. else error(0, {"bad defstruct option:", entry});
  100. if null constructor then
  101. constructor := intern compress append('(m a k e !! !-),
  102. explode name);
  103. return {name, 'predicate . predicate, 'constructor . constructor}
  104. end;
  105. expr procedure explodeid x;
  106. % EXPLODEID(X) - Explode whatever x is and make sure the result can
  107. % be compressed back into an id no matter what it is.
  108. if idp x then explode x
  109. else for each elt in explode2 x join {'!!, elt};
  110. expr procedure build_defstruct_constructor_macro
  111. (name,macro_name,slot_forms,has_predicate);
  112. begin scalar dflts;
  113. dflts := for each x in slot_forms collect
  114. {'cons, mkquote car x, cadr x};
  115. % I deal with the name field by inserting it as an extra slot, with
  116. % slot-name made by a gensym so that the user will not get to
  117. % override the default value ever. As coded here if the default
  118. % value of a slot depends on a variable called !$!$!$ then scope
  119. % issues will lead to silly results being generated.
  120. if has_predicate
  121. then dflts := {'cons, '(gensym), mkquote name} . dflts;
  122. return {'putd,
  123. mkquote macro_name,
  124. ''macro,
  125. mkquote {'lambda, '(!$!$!$),
  126. {'list, ''defstructvector,
  127. {'mklist, {'defstruct_constructor,
  128. '(cdr !$!$!$),
  129. 'list . dflts}}}}}
  130. end;
  131. symbolic procedure mklist x; 'list . x;
  132. expr procedure defstruct_constructor(u, dflts);
  133. for each d in dflts collect find_struct_key(car d, u, cdr d);
  134. expr procedure find_struct_key(key, u, dflt);
  135. if null u then mkquote dflt
  136. else if car u eq key then
  137. if null cdr u then nil else cadr u
  138. else find_struct_key(key, cddr u, dflt);
  139. expr procedure defstructvector l;
  140. % DEFSTRUCTVECTOR(L) - Create a vector and store the list L into it.
  141. % This is a portable substitute for PSL's list2vector.
  142. begin integer i; scalar v;
  143. v := mkvect sub1 length l;
  144. i := 0;
  145. for each vl in l do <<putv(v,i,vl); i := i+1>>;
  146. return v
  147. end;
  148. expr procedure build_defstruct_predicate_function(name, fnname);
  149. % BUILD_DEFSTRUCT_PREDICATE_FUNCTION(NAME, FNNAME) - Builds a defstruct
  150. % predicate to return as a function.
  151. {'de, fnname, '(x),
  152. {'and, '(vectorp x), {'eq, mkquote name, '(igetv x 0)}}};
  153. expr procedure build_defstruct_accessor_macro(slot_name,indx);
  154. {'dm, slot_name, '(u), {'list, '(quote structfetch), '(cadr u), indx}};
  155. macro procedure structfetch u;
  156. if !*faststructs then 'igetv . cdr u else 'getv . cdr u;
  157. %-----------------------------------------------------------------------
  158. % SETF for RLISP88
  159. %-----------------------------------------------------------------------
  160. macro procedure rsetf u; expandrsetf(cadr u, caddr u);
  161. expr procedure expandrsetf(lhs, rhs);
  162. if atom lhs then {'setq, lhs, rhs}
  163. else if eqcar(lhs, '!&VARIABLE_FETCH) then
  164. '!&VARIABLE_STORE . append(cdr lhs, {rhs})
  165. else if get(car lhs, 'ASSIGN_OP) then
  166. get(car lhs, 'ASSIGN_OP) . append(cdr lhs, {rhs})
  167. else if getd car lhs and eqcar(getd car lhs, 'macro) then
  168. expandrsetf(apply(cdr getd car lhs, {lhs}), rhs)
  169. else error(0, {lhs, "bad RSETF form"});
  170. deflist('((getv putv) (igetv putv) (car rplaca) (cdr rplacd)),
  171. 'ASSIGN_OP);
  172. % This is CSL specific but shouldn't hurt anybody.
  173. put('qgetv, 'ASSIGN_OP, 'qputv);
  174. endmodule;
  175. end;