defstruct.red 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. %
  2. % DEFSTRUCT.RED - Interim structure definition facility.
  3. %
  4. % Author: Russ Fish
  5. % Computer Science Dept.
  6. % University of Utah
  7. % Date: 18 December 1981
  8. % Copyright (c) 1981 University of Utah
  9. %
  10. % See files Defstruct.{Hlp,Doc} for description of usage.
  11. %%%% To compile this code, it must first be loaded interpretively. %%%%
  12. %%%% Bootstrap is necessary because defstructs are used internally %%%%
  13. %%%% to record the descriptions of structures, including the %%%%
  14. %%%% descriptions of the defstruct descriptors themselves. %%%%
  15. % First, an aside to the compiler.
  16. CompileTime % Compiler needs to know about LHS forms which will be used.
  17. put( 'SlotDescInitForm, 'Assign!-Op, 'PUTSlotDescInitForm );
  18. BothTimes % Declare lists of fluids used for binding options.
  19. <<
  20. fluid '( DefstructOptions SlotOptions );
  21. fluid (
  22. DefstructOptions :=
  23. '( !:Constructor !:Alterant !:Predicate !:Creator
  24. !:Prefix !:Include !:IncludeInit ) );
  25. fluid (
  26. SlotOptions := '( !:Type !:UserGet !:UserPut ) );
  27. flag('(defstruct), 'Eval);
  28. >>;
  29. % ////////////// Externally known fns //////////////////////////
  30. % Struct type predicate.
  31. lisp procedure DefstructP( Name );
  32. get( Name, 'Defstruct );
  33. % Access to "struct type name" field of structure.
  34. lisp procedure DefstructType( Struct );
  35. if VectorP Struct then % Minimal checking.
  36. getv( Struct, 0 )
  37. else
  38. NIL;
  39. % Type inclusion predicate.
  40. lisp procedure SubTypeP( I1, I2 ); % T if I1 is a subtype of I2.
  41. begin scalar Incl;
  42. return
  43. I1 eq I2 % Type is subtype of itself. (LEQ.)
  44. or
  45. (Incl := DsDescInclude GetDefstruct I2) % Done if no subtype.
  46. and
  47. ( I1 eq Incl % Proper subtype.
  48. or SubTypeP( I1, Incl ) ) % Or a subsubtype, or...
  49. end;
  50. % ////////////// Defstruct /////////////////////////////////////
  51. fexpr procedure Defstruct( Spec );
  52. begin scalar StructName, Init, NameValue, Desc, DsSize, SlotSpec, SlotAlist;
  53. if atom Spec then % Spec must be a list.
  54. TypeError( Spec, 'Defstruct, "a spec list" );
  55. StructName := if atom first Spec then
  56. first Spec % Grab the struct id.
  57. else
  58. first first Spec;
  59. if not idp StructName then % Struct id better be one.
  60. UsageTypeError( StructName, 'Defstruct, "an id", "a StructName" );
  61. % Defaults for options.
  62. !:Constructor := !:Alterant := !:Predicate := T;
  63. !:Creator := !:Include := !:IncludeInit := NIL;
  64. !:Prefix := "";
  65. % Process option list if present.
  66. if pairp first Spec then
  67. ProcessOptions( rest first Spec, DefstructOptions );
  68. if !:Prefix = T then % Default prefix is StructName.
  69. !:Prefix := id2string StructName;
  70. if idp !:Prefix then % Convert id to printname string.
  71. !:Prefix := id2string !:Prefix
  72. else
  73. if not stringp !:Prefix then % Error if not id or string.
  74. UsageTypeError( !:Prefix, 'Defstruct,
  75. "an id or a string", "a SlotName prefix" );
  76. % Construct macro names in default pattern if necessary.
  77. if !:Constructor eq T then !:Constructor := IdConcat( 'MAKE, StructName );
  78. if !:Alterant eq T then !:Alterant := IdConcat( 'ALTER, StructName );
  79. if !:Predicate eq T then !:Predicate := IdConcat( StructName, 'P );
  80. if !:Creator eq T then !:Creator := IdConcat( 'CREATE, StructName );
  81. % Define the constructor, alterant, predicate, and creator, if desired.
  82. MkStructMac( !:Constructor, 'Make, StructName );
  83. MkStructMac( !:Alterant, 'Alter, StructName );
  84. MkStructPred( !:Predicate, StructName );
  85. MkStructMac( !:Creator, 'Create, StructName );
  86. DsSize := 0; % Accumulate size, starting with the DefstructType.
  87. SlotAlist := NIL;
  88. if !:Include then % If including another struct, start after it.
  89. if Desc := GetDefstruct( !:Include ) then
  90. <<
  91. DsSize := DsDescDsSize( Desc );
  92. % Get slots of included type, modified by !:IncludeInit.
  93. SlotAlist := for each Init in DsDescSlotAlist( Desc ) collect
  94. <<
  95. if !:IncludeInit and
  96. (NameValue := atsoc( car Init, !:IncludeInit )) then
  97. <<
  98. Init := TotalCopy Init;
  99. SlotDescInitForm cdr Init := second NameValue
  100. >>;
  101. Init
  102. >>
  103. >>
  104. else
  105. TypeError( !:Include, "Defstruct !:Include", "a type id" );
  106. % Define the Selector macros, and build the alist of slot ids.
  107. SlotAlist := append( SlotAlist,
  108. for each SlotSpec in rest Spec collect
  109. ProcessSlot( SlotSpec, !:Prefix, DsSize := DsSize+1 ) );
  110. if Defstructp Structname then
  111. ErrorPrintF("*** Defstruct %r has been redefined", StructName);
  112. Put( StructName, 'Defstruct, % Stash the Structure Descriptor.
  113. CreateDefstructDescriptor(
  114. DsSize, !:Prefix, SlotAlist, !:Constructor, !:Alterant,
  115. !:Predicate, !:Creator, !:Include, !:IncludeInit )
  116. );
  117. return StructName
  118. end;
  119. % Turn slot secifications into (SlotName . SlotDescriptor) pairs.
  120. lisp procedure ProcessSlot( SlotSpec, Prefix, SlotNum );
  121. begin scalar SlotName, SlotFn, It, OptList, InitForm;
  122. % Got a few possibilities to unravel.
  123. InitForm := OptList := NIL; % Only slot-name required.
  124. if atom SlotSpec then
  125. SlotName := SlotSpec % Bare slot-name, no default-init or options.
  126. else
  127. <<
  128. SlotName := first SlotSpec;
  129. if It := rest SlotSpec then % Default-init and/or options provided.
  130. <<
  131. % See if option immediately after name.
  132. while pairp It do It := first It; % Down to first atom.
  133. if idp It and memq( It, SlotOptions ) then % Option keyword?
  134. OptList := rest SlotSpec % Yes, no init-form.
  135. else
  136. <<
  137. InitForm := second SlotSpec; % Init-form after keyword.
  138. OptList := rest rest SlotSpec % Options or NIL.
  139. >>
  140. >>
  141. >>;
  142. if not idp SlotName then % Slot id better be one.
  143. UsageTypeError( SlotName, 'Defstruct, "an id", "a SlotName" );
  144. SlotFn := if Prefix eq "" then % Slot fns may have a prefix.
  145. SlotName
  146. else
  147. IdConcat( Prefix, Slotname );
  148. % Defaults for options.
  149. !:Type := !:UserGet := !:UserPut := NIL;
  150. if OptList then % Process option list
  151. ProcessOptions( OptList, SlotOptions );
  152. % Make Selector and Depositor unless overridden.
  153. if not !:UserGet then MkSelector( SlotFn, SlotNum );
  154. if not !:UserPut then MkDepositor( SlotFn, SlotNum );
  155. % Return the ( SlotName . SlotDescriptor ) pair.
  156. return SlotName .
  157. CreateSlotDescriptor(
  158. SlotNum, InitForm, SlotFn, !:Type, !:UserGet, !:UserPut )
  159. end;
  160. % ////////////// Internal fns //////////////////////////////////
  161. % Process defstruct and slot options, binding values of valid options.
  162. lisp procedure ProcessOptions( OptList, OptVarList );
  163. begin scalar OptSpec, Option, OptArg;
  164. for each OptSpec in OptList do
  165. <<
  166. if atom OptSpec then % Bare option id.
  167. <<
  168. Option := OptSpec;
  169. OptArg := T
  170. >>
  171. else
  172. <<
  173. Option := first OptSpec;
  174. OptArg := rest OptSpec; % List of args to option.
  175. if not rest OptArg then % Single arg, unlist it.
  176. OptArg := first OptArg
  177. >>;
  178. if memq( Option, OptVarList ) then
  179. set( Option, OptArg )
  180. else
  181. UsageTypeError( Option, 'ProcessOptions,
  182. ("one of" . OptVarList . "is needed"), "an option id" )
  183. >>
  184. end;
  185. lisp procedure GetDefstruct( StructId ); % Yank struct defn from id.
  186. begin scalar Desc;
  187. if Desc := get( StructId, 'Defstruct )
  188. then return Desc % Return Struct defn.
  189. else
  190. TypeError( StructId, 'GetDefstruct, "a defstruct id" )
  191. end;
  192. lisp procedure IdConcat( I1, I2 ); % Make two-part names.
  193. <<
  194. if idp I1 then I1 := id2String I1;
  195. if idp I2 then I2 := id2String I2;
  196. intern concat( I1, I2 )
  197. >>;
  198. % ////////////// Fn building fns ///////////////////////////////
  199. % Fn to build specific Structure Fns as macros which use generic macros.
  200. % The generic macro is called with the StructName and the original
  201. % list of arguments.
  202. % MacName( arg1, arg2, ... )
  203. % => GenericMac( StructName, arg1, arg2, ... )
  204. lisp procedure MkStructMac( MacName, GenericMac, StructName );
  205. if MacName then % No macro if NIL name.
  206. putd( MacName, 'macro,
  207. list( 'lambda,
  208. '(MacroArgs),
  209. list( 'append,
  210. list( 'quote,
  211. list( GenericMac, StructName )
  212. ),
  213. '(rest MacroArgs)
  214. )
  215. )
  216. );
  217. % Fn to build specific Structure Predicates.
  218. lisp procedure MkStructPred( FnName, StructName );
  219. putd( FnName, 'expr,
  220. list( 'lambda, '(PredArg),
  221. list( 'and,
  222. '(vectorp PredArg),
  223. list( 'eq,
  224. list('quote,StructName),
  225. '(DefstructType PredArg) )
  226. )
  227. )
  228. );
  229. % RHS selector (get fn) constructor.
  230. lisp procedure MkSelector( Name, Slotnum );
  231. putd( Name, 'expr,
  232. list( 'lambda, '(Struct), List( 'getV, 'Struct, SlotNum ) ) );
  233. % LHS depositor (put fn) constructor.
  234. lisp procedure MkDepositor( Name, Slotnum );
  235. begin scalar PutName;
  236. PutName := intern concat( "PUT", id2string Name );
  237. putd( PutName, 'expr,
  238. list( 'lambda, '(Struct Val),
  239. List( 'putV, 'Struct, SlotNum, 'Val ) ) );
  240. put( Name, 'Assign!-Op, PutName );
  241. return PutName
  242. end;
  243. % ////////////// Fns used by macros. ///////////////////////////
  244. % Generic macro for constructors, called with structure name and list
  245. % of slot-name:value-form pairs to merge with default-inits.
  246. % Returns vector constructor.
  247. macro procedure Make( ArgList );
  248. begin scalar StructName, OverrideAlist, Slot, NameValue;
  249. StructName := second ArgList;
  250. OverrideAlist := rest rest ArgList;
  251. return append( % Return vector constructor.
  252. list( 'vector,
  253. list('quote,StructName) ), % Mark struct type as first element.
  254. % Build list of init forms for vector constructor.
  255. for each Slot in DsDescSlotAlist GetDefstruct StructName collect
  256. if NameValue := atsoc( car Slot, OverrideAlist ) then
  257. second NameValue
  258. else
  259. SlotDescInitForm cdr Slot
  260. )
  261. end;
  262. % Generic Alterant macro, called with structure name, struct instance and
  263. % slot name:value alist. A list of depositor calls is returned, with a
  264. % PROGN wrapped around it and the struct instance at the end for a return
  265. % value.
  266. macro procedure Alter( ArgList );
  267. begin scalar StructName, StructInstance, SlotValueDlist, SlotAlist,
  268. NameValue, Slot;
  269. StructName := second ArgList;
  270. StructInstance := third ArgList;
  271. SlotValueDlist := rest rest rest ArgList;
  272. SlotAlist := DsDescSlotAList GetDefstruct StructName;
  273. return append( append(
  274. '(PROGN), % wraparound PROGN.
  275. % List of depositor calls.
  276. for each NameValue in SlotValueDlist collect
  277. if Slot := atsoc( first NameValue, SlotAlist) then
  278. list(
  279. % Use depositors, which may be user fns, rather than PutV.
  280. IdConCat( 'PUT, SlotDescSlotFn cdr Slot ),
  281. StructInstance,
  282. second NameValue )
  283. else
  284. TypeError( car NameValue, 'Alter,
  285. concat( "a slot of ", id2string StructName ) )
  286. ), list( StructInstance ) ) % Value of PROGN is altered instance.
  287. end;
  288. % Generic Create macro, called with struct name and list of positional args
  289. % which are slot value forms. Returns struct vector constructor.
  290. macro procedure Create( ArgList );
  291. begin scalar StructName, SlotValues, DsSize;
  292. StructName := second ArgList;
  293. SlotValues := rest rest ArgList;
  294. DsSize := DsDescDsSize GetDefstruct StructName;
  295. if DsSize = Length SlotValues then
  296. return append(
  297. list( 'VECTOR,
  298. list( 'quote, StructName ) ), % Mark with struct id.
  299. SlotValues )
  300. else
  301. UsageTypeError( SlotValues, 'Create,
  302. BldMsg( "a list of length %p", DsSize ),
  303. concat( "an initializer for ", id2string StructName) )
  304. end;
  305. % ////////////// Boot Defstruct structs. ///////////////////////
  306. % Chicken-and-egg problem, need some knowledge of Defstruct descriptor
  307. % structures before they are defined, in order to define them.
  308. CompileTime <<
  309. MkSelector( 'DsDescDsSize, 1 );
  310. MkStructMac( 'CreateDefstructDescriptor, 'Create, 'DefstructDescriptor );
  311. MkStructMac( 'CreateSlotDescriptor, 'Create, 'SlotDescriptor );
  312. put( 'DefstructDescriptor, 'Defstruct, % Abbreviated struct defns for boot.
  313. '[ DefstructDescriptor 9 ] ); % Just DsSize, for Create Fns.
  314. put( 'SlotDescriptor, 'Defstruct,
  315. '[ SlotDescriptor 6 ] );
  316. >>;
  317. % Now really declare the Defstruct Descriptor structs.
  318. Defstruct(
  319. DefstructDescriptor( !:Prefix(DsDesc), !:Creator ),
  320. DsSize( !:Type int ), % (Upper Bound of vector.)
  321. Prefix( !:Type string ),
  322. SlotAlist( !:Type alist ), % (Cdrs are SlotDescriptors.)
  323. ConsName( !:Type fnId ),
  324. AltrName( !:Type fnId ),
  325. PredName( !:Type fnId ),
  326. CreateName( !:Type fnId ),
  327. Include( !:Type typeid ),
  328. InclInit( !:Type alist )
  329. );
  330. Defstruct(
  331. SlotDescriptor( !:Prefix(SlotDesc), !:Creator ),
  332. SlotNum( !:Type int ),
  333. InitForm( !:Type form ),
  334. SlotFn( !:Type fnId ), % Selector/Depositor id.
  335. SlotType( !:Type type ), % Hm...
  336. UserGet( !:Type boolean ),
  337. UserPut( !:Type boolean )
  338. );
  339. END;