123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414 |
- %
- % DEFSTRUCT.RED - Interim structure definition facility.
- %
- % Author: Russ Fish
- % Computer Science Dept.
- % University of Utah
- % Date: 18 December 1981
- % Copyright (c) 1981 University of Utah
- %
- % See files Defstruct.{Hlp,Doc} for description of usage.
- %%%% To compile this code, it must first be loaded interpretively. %%%%
- %%%% Bootstrap is necessary because defstructs are used internally %%%%
- %%%% to record the descriptions of structures, including the %%%%
- %%%% descriptions of the defstruct descriptors themselves. %%%%
- % First, an aside to the compiler.
- CompileTime % Compiler needs to know about LHS forms which will be used.
- put( 'SlotDescInitForm, 'Assign!-Op, 'PUTSlotDescInitForm );
- BothTimes % Declare lists of fluids used for binding options.
- <<
- fluid '( DefstructOptions SlotOptions );
- fluid (
- DefstructOptions :=
- '( !:Constructor !:Alterant !:Predicate !:Creator
- !:Prefix !:Include !:IncludeInit ) );
- fluid (
- SlotOptions := '( !:Type !:UserGet !:UserPut ) );
- flag('(defstruct), 'Eval);
- >>;
- % ////////////// Externally known fns //////////////////////////
- % Struct type predicate.
- lisp procedure DefstructP( Name );
- get( Name, 'Defstruct );
- % Access to "struct type name" field of structure.
- lisp procedure DefstructType( Struct );
- if VectorP Struct then % Minimal checking.
- getv( Struct, 0 )
- else
- NIL;
- % Type inclusion predicate.
- lisp procedure SubTypeP( I1, I2 ); % T if I1 is a subtype of I2.
- begin scalar Incl;
- return
- I1 eq I2 % Type is subtype of itself. (LEQ.)
- or
- (Incl := DsDescInclude GetDefstruct I2) % Done if no subtype.
- and
- ( I1 eq Incl % Proper subtype.
- or SubTypeP( I1, Incl ) ) % Or a subsubtype, or...
- end;
- % ////////////// Defstruct /////////////////////////////////////
- fexpr procedure Defstruct( Spec );
- begin scalar StructName, Init, NameValue, Desc, DsSize, SlotSpec, SlotAlist;
- if atom Spec then % Spec must be a list.
- TypeError( Spec, 'Defstruct, "a spec list" );
- StructName := if atom first Spec then
- first Spec % Grab the struct id.
- else
- first first Spec;
- if not idp StructName then % Struct id better be one.
- UsageTypeError( StructName, 'Defstruct, "an id", "a StructName" );
- % Defaults for options.
- !:Constructor := !:Alterant := !:Predicate := T;
- !:Creator := !:Include := !:IncludeInit := NIL;
- !:Prefix := "";
- % Process option list if present.
- if pairp first Spec then
- ProcessOptions( rest first Spec, DefstructOptions );
- if !:Prefix = T then % Default prefix is StructName.
- !:Prefix := id2string StructName;
- if idp !:Prefix then % Convert id to printname string.
- !:Prefix := id2string !:Prefix
- else
- if not stringp !:Prefix then % Error if not id or string.
- UsageTypeError( !:Prefix, 'Defstruct,
- "an id or a string", "a SlotName prefix" );
- % Construct macro names in default pattern if necessary.
- if !:Constructor eq T then !:Constructor := IdConcat( 'MAKE, StructName );
- if !:Alterant eq T then !:Alterant := IdConcat( 'ALTER, StructName );
- if !:Predicate eq T then !:Predicate := IdConcat( StructName, 'P );
- if !:Creator eq T then !:Creator := IdConcat( 'CREATE, StructName );
- % Define the constructor, alterant, predicate, and creator, if desired.
- MkStructMac( !:Constructor, 'Make, StructName );
- MkStructMac( !:Alterant, 'Alter, StructName );
- MkStructPred( !:Predicate, StructName );
- MkStructMac( !:Creator, 'Create, StructName );
- DsSize := 0; % Accumulate size, starting with the DefstructType.
- SlotAlist := NIL;
- if !:Include then % If including another struct, start after it.
- if Desc := GetDefstruct( !:Include ) then
- <<
- DsSize := DsDescDsSize( Desc );
- % Get slots of included type, modified by !:IncludeInit.
- SlotAlist := for each Init in DsDescSlotAlist( Desc ) collect
- <<
- if !:IncludeInit and
- (NameValue := atsoc( car Init, !:IncludeInit )) then
- <<
- Init := TotalCopy Init;
- SlotDescInitForm cdr Init := second NameValue
- >>;
- Init
- >>
- >>
- else
- TypeError( !:Include, "Defstruct !:Include", "a type id" );
- % Define the Selector macros, and build the alist of slot ids.
- SlotAlist := append( SlotAlist,
- for each SlotSpec in rest Spec collect
- ProcessSlot( SlotSpec, !:Prefix, DsSize := DsSize+1 ) );
- if Defstructp Structname then
- ErrorPrintF("*** Defstruct %r has been redefined", StructName);
- Put( StructName, 'Defstruct, % Stash the Structure Descriptor.
- CreateDefstructDescriptor(
- DsSize, !:Prefix, SlotAlist, !:Constructor, !:Alterant,
- !:Predicate, !:Creator, !:Include, !:IncludeInit )
- );
- return StructName
- end;
- % Turn slot secifications into (SlotName . SlotDescriptor) pairs.
- lisp procedure ProcessSlot( SlotSpec, Prefix, SlotNum );
- begin scalar SlotName, SlotFn, It, OptList, InitForm;
- % Got a few possibilities to unravel.
- InitForm := OptList := NIL; % Only slot-name required.
- if atom SlotSpec then
- SlotName := SlotSpec % Bare slot-name, no default-init or options.
- else
- <<
- SlotName := first SlotSpec;
- if It := rest SlotSpec then % Default-init and/or options provided.
- <<
- % See if option immediately after name.
- while pairp It do It := first It; % Down to first atom.
- if idp It and memq( It, SlotOptions ) then % Option keyword?
- OptList := rest SlotSpec % Yes, no init-form.
- else
- <<
- InitForm := second SlotSpec; % Init-form after keyword.
- OptList := rest rest SlotSpec % Options or NIL.
- >>
- >>
- >>;
- if not idp SlotName then % Slot id better be one.
- UsageTypeError( SlotName, 'Defstruct, "an id", "a SlotName" );
- SlotFn := if Prefix eq "" then % Slot fns may have a prefix.
- SlotName
- else
- IdConcat( Prefix, Slotname );
- % Defaults for options.
- !:Type := !:UserGet := !:UserPut := NIL;
-
- if OptList then % Process option list
- ProcessOptions( OptList, SlotOptions );
- % Make Selector and Depositor unless overridden.
- if not !:UserGet then MkSelector( SlotFn, SlotNum );
- if not !:UserPut then MkDepositor( SlotFn, SlotNum );
- % Return the ( SlotName . SlotDescriptor ) pair.
- return SlotName .
- CreateSlotDescriptor(
- SlotNum, InitForm, SlotFn, !:Type, !:UserGet, !:UserPut )
- end;
- % ////////////// Internal fns //////////////////////////////////
- % Process defstruct and slot options, binding values of valid options.
- lisp procedure ProcessOptions( OptList, OptVarList );
- begin scalar OptSpec, Option, OptArg;
- for each OptSpec in OptList do
- <<
- if atom OptSpec then % Bare option id.
- <<
- Option := OptSpec;
- OptArg := T
- >>
- else
- <<
- Option := first OptSpec;
- OptArg := rest OptSpec; % List of args to option.
- if not rest OptArg then % Single arg, unlist it.
- OptArg := first OptArg
- >>;
- if memq( Option, OptVarList ) then
- set( Option, OptArg )
- else
- UsageTypeError( Option, 'ProcessOptions,
- ("one of" . OptVarList . "is needed"), "an option id" )
- >>
- end;
- lisp procedure GetDefstruct( StructId ); % Yank struct defn from id.
- begin scalar Desc;
- if Desc := get( StructId, 'Defstruct )
- then return Desc % Return Struct defn.
- else
- TypeError( StructId, 'GetDefstruct, "a defstruct id" )
- end;
- lisp procedure IdConcat( I1, I2 ); % Make two-part names.
- <<
- if idp I1 then I1 := id2String I1;
- if idp I2 then I2 := id2String I2;
- intern concat( I1, I2 )
- >>;
- % ////////////// Fn building fns ///////////////////////////////
- % Fn to build specific Structure Fns as macros which use generic macros.
- % The generic macro is called with the StructName and the original
- % list of arguments.
- % MacName( arg1, arg2, ... )
- % => GenericMac( StructName, arg1, arg2, ... )
- lisp procedure MkStructMac( MacName, GenericMac, StructName );
- if MacName then % No macro if NIL name.
- putd( MacName, 'macro,
- list( 'lambda,
- '(MacroArgs),
- list( 'append,
- list( 'quote,
- list( GenericMac, StructName )
- ),
- '(rest MacroArgs)
- )
- )
- );
- % Fn to build specific Structure Predicates.
- lisp procedure MkStructPred( FnName, StructName );
- putd( FnName, 'expr,
- list( 'lambda, '(PredArg),
- list( 'and,
- '(vectorp PredArg),
- list( 'eq,
- list('quote,StructName),
- '(DefstructType PredArg) )
- )
- )
- );
- % RHS selector (get fn) constructor.
- lisp procedure MkSelector( Name, Slotnum );
- putd( Name, 'expr,
- list( 'lambda, '(Struct), List( 'getV, 'Struct, SlotNum ) ) );
- % LHS depositor (put fn) constructor.
- lisp procedure MkDepositor( Name, Slotnum );
- begin scalar PutName;
- PutName := intern concat( "PUT", id2string Name );
- putd( PutName, 'expr,
- list( 'lambda, '(Struct Val),
- List( 'putV, 'Struct, SlotNum, 'Val ) ) );
- put( Name, 'Assign!-Op, PutName );
- return PutName
- end;
- % ////////////// Fns used by macros. ///////////////////////////
- % Generic macro for constructors, called with structure name and list
- % of slot-name:value-form pairs to merge with default-inits.
- % Returns vector constructor.
- macro procedure Make( ArgList );
- begin scalar StructName, OverrideAlist, Slot, NameValue;
- StructName := second ArgList;
- OverrideAlist := rest rest ArgList;
- return append( % Return vector constructor.
- list( 'vector,
- list('quote,StructName) ), % Mark struct type as first element.
- % Build list of init forms for vector constructor.
- for each Slot in DsDescSlotAlist GetDefstruct StructName collect
- if NameValue := atsoc( car Slot, OverrideAlist ) then
- second NameValue
- else
- SlotDescInitForm cdr Slot
- )
- end;
- % Generic Alterant macro, called with structure name, struct instance and
- % slot name:value alist. A list of depositor calls is returned, with a
- % PROGN wrapped around it and the struct instance at the end for a return
- % value.
- macro procedure Alter( ArgList );
- begin scalar StructName, StructInstance, SlotValueDlist, SlotAlist,
- NameValue, Slot;
- StructName := second ArgList;
- StructInstance := third ArgList;
- SlotValueDlist := rest rest rest ArgList;
- SlotAlist := DsDescSlotAList GetDefstruct StructName;
- return append( append(
- '(PROGN), % wraparound PROGN.
- % List of depositor calls.
- for each NameValue in SlotValueDlist collect
- if Slot := atsoc( first NameValue, SlotAlist) then
- list(
- % Use depositors, which may be user fns, rather than PutV.
- IdConCat( 'PUT, SlotDescSlotFn cdr Slot ),
- StructInstance,
- second NameValue )
- else
- TypeError( car NameValue, 'Alter,
- concat( "a slot of ", id2string StructName ) )
- ), list( StructInstance ) ) % Value of PROGN is altered instance.
- end;
- % Generic Create macro, called with struct name and list of positional args
- % which are slot value forms. Returns struct vector constructor.
- macro procedure Create( ArgList );
- begin scalar StructName, SlotValues, DsSize;
- StructName := second ArgList;
- SlotValues := rest rest ArgList;
- DsSize := DsDescDsSize GetDefstruct StructName;
- if DsSize = Length SlotValues then
- return append(
- list( 'VECTOR,
- list( 'quote, StructName ) ), % Mark with struct id.
- SlotValues )
- else
- UsageTypeError( SlotValues, 'Create,
- BldMsg( "a list of length %p", DsSize ),
- concat( "an initializer for ", id2string StructName) )
- end;
- % ////////////// Boot Defstruct structs. ///////////////////////
- % Chicken-and-egg problem, need some knowledge of Defstruct descriptor
- % structures before they are defined, in order to define them.
- CompileTime <<
- MkSelector( 'DsDescDsSize, 1 );
- MkStructMac( 'CreateDefstructDescriptor, 'Create, 'DefstructDescriptor );
- MkStructMac( 'CreateSlotDescriptor, 'Create, 'SlotDescriptor );
- put( 'DefstructDescriptor, 'Defstruct, % Abbreviated struct defns for boot.
- '[ DefstructDescriptor 9 ] ); % Just DsSize, for Create Fns.
- put( 'SlotDescriptor, 'Defstruct,
- '[ SlotDescriptor 6 ] );
- >>;
- % Now really declare the Defstruct Descriptor structs.
- Defstruct(
- DefstructDescriptor( !:Prefix(DsDesc), !:Creator ),
- DsSize( !:Type int ), % (Upper Bound of vector.)
- Prefix( !:Type string ),
- SlotAlist( !:Type alist ), % (Cdrs are SlotDescriptors.)
- ConsName( !:Type fnId ),
- AltrName( !:Type fnId ),
- PredName( !:Type fnId ),
- CreateName( !:Type fnId ),
- Include( !:Type typeid ),
- InclInit( !:Type alist )
- );
- Defstruct(
- SlotDescriptor( !:Prefix(SlotDesc), !:Creator ),
- SlotNum( !:Type int ),
- InitForm( !:Type form ),
- SlotFn( !:Type fnId ), % Selector/Depositor id.
- SlotType( !:Type type ), % Hm...
- UserGet( !:Type boolean ),
- UserPut( !:Type boolean )
- );
- END;
|