123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- module mstruct; % A tiny structure package for Standard Lisp.
- % Author: Bruce A. Florman.
- % Copyright (c) 1989, The RAND Corporation. All rights reserved.
- comment
- DESCRIPTION
- (defstruct <structspec> [ <slotspec>... ] )
- The <structspec> may be either the name of the structure, or a list
- containing the name followed by zero or more options. Each <slotspec>
- may be either a list containing the slot name and its default value,
- or simply the slot name, in which case the default value is NIL.
- Each option in the <structspec> may be either an option name, or a
- list containing the option name and a specified value. If only the
- option name is given, then the default value for the given option is
- used. If NIL is the specified value in an option, then the option is
- not used at all (in general a NIL value is the same as not having that
- option in the list at all). If the same option appears more than once
- with different values, the last one in the <structspec> takes
- precedence.
- These are the valid options:
- PREDICATE
- Makes the zeroth element of the structure contain the structure name
- and creates a predicate macro to test if a given item is an instance
- of this structure. The specified value is the name of the predicate
- macro. The default value is the structure name followed by a `P'.
- CONSTRUCTOR
- By default the name of the constructor macro is `MAKE-' followed by
- the structure name. You may provide a different constructor name
- with this option. If there is no constructor option in the
- <structspec> the default constructor will still be generated. The
- only way to completely suppress the generation of a constructor
- macro is to have a (CONSTRUCTOR NIL) option.
- The flag !*FASTSTRUCTS controls how the accessor macros expand.
- If it is NIL, they expand as GETVs, otherwise they expand as
- IGETVs.
- NOTE: see records.tst for a level 0 test file.
- REVISION HISTORY
- 07/19/85 BAF -- File created.
- 01/26/89 BAF -- Added predicate and constructor macros so that
- this code can replace the RLISP record code.
- Changed GetR to StructFetch, and !*FAST-RECORDS
- to !*FASTSTRUCTS. Added code to check the
- validity of the options. Also added this file
- header.
- 01/30/89 BAF -- Added CONC-NAME as a synonym for SLOT-PREFIX and
- the ExplodeId function for compatability with
- existing programs (eg. ernie).
- Wed Apr 21 14:22:18 1993 - JBM Convert to RLISP '88, remove prefix
- stuff.
- Tue May 11 09:03:20 1993 - JBM Remove tconc and fix evaluator bug.
- Mon May 17 15:36:54 1993 - JBM Add RSETF function.
- Tue May 18 11:09:07 1993 - JBM add qputv for CSL to RSETF;
- flag('(defstruct), 'eval);
- fluid '(!*faststructs);
- switch FASTSTRUCTS;
- macro procedure defstruct u;
- begin integer indx;
- scalar options,slot_forms,name,predicate,constructor,functions;
- options := get_defstruct_options cadr u;
- if cdr u
- then slot_forms := for each slot in cddr u
- collect if idp slot then {slot,nil} else slot;
- name := car options;
- predicate := atsoc('predicate,cdr options);
- if predicate then predicate := cdr predicate;
- constructor := atsoc('constructor,cdr options);
- if constructor then constructor := cdr constructor;
- functions := NIL;
- if constructor then
- functions := build_defstruct_constructor_macro(name,
- constructor,
- slot_forms,
- predicate)
- . functions;
- if predicate then
- functions :=
- build_defstruct_predicate_function(name, predicate) . functions;
- indx := if predicate then 1 else 0;
- for each slot in slot_forms do
- <<functions :=
- build_defstruct_accessor_macro(car slot, indx) . functions;
- indx := indx + 1>>;
- functions := mkquote name . functions;
- return 'progn . reverse functions
- end;
- expr procedure get_defstruct_options u;
- begin scalar name, options, predicate, constructor;
- if pairp u then << name := car u; options := cdr u >>
- else << name := u; options := nil >>;
- if not idp name then error(0, {"bad defstruct name:", name});
- for each entry in options
- do if entry eq 'predicate then
- predicate := intern compress append(explode name, '(p))
- else if eqcar(entry, 'predicate) then predicate := cadr entry
- else if entry eq 'constructor then
- constructor := intern compress append('(m a k e !! !-),
- explode name)
- else if eqcar(entry,'constructor)
- then constructor := cadr entry
- else error(0, {"bad defstruct option:", entry});
- if null constructor then
- constructor := intern compress append('(m a k e !! !-),
- explode name);
- return {name, 'predicate . predicate, 'constructor . constructor}
- end;
- expr procedure explodeid x;
- % EXPLODEID(X) - Explode whatever x is and make sure the result can
- % be compressed back into an id no matter what it is.
- if idp x then explode x
- else for each elt in explode2 x join {'!!, elt};
- expr procedure build_defstruct_constructor_macro
- (name,macro_name,slot_forms,has_predicate);
- begin scalar dflts;
- dflts := for each x in slot_forms collect
- {'cons, mkquote car x, cadr x};
- % I deal with the name field by inserting it as an extra slot, with
- % slot-name made by a gensym so that the user will not get to
- % override the default value ever. As coded here if the default
- % value of a slot depends on a variable called !$!$!$ then scope
- % issues will lead to silly results being generated.
- if has_predicate
- then dflts := {'cons, '(gensym), mkquote name} . dflts;
- return {'putd,
- mkquote macro_name,
- ''macro,
- mkquote {'lambda, '(!$!$!$),
- {'list, ''defstructvector,
- {'mklist, {'defstruct_constructor,
- '(cdr !$!$!$),
- 'list . dflts}}}}}
- end;
- symbolic procedure mklist x; 'list . x;
- expr procedure defstruct_constructor(u, dflts);
- for each d in dflts collect find_struct_key(car d, u, cdr d);
- expr procedure find_struct_key(key, u, dflt);
- if null u then mkquote dflt
- else if car u eq key then
- if null cdr u then nil else cadr u
- else find_struct_key(key, cddr u, dflt);
- expr procedure defstructvector l;
- % DEFSTRUCTVECTOR(L) - Create a vector and store the list L into it.
- % This is a portable substitute for PSL's list2vector.
- begin integer i; scalar v;
- v := mkvect sub1 length l;
- i := 0;
- for each vl in l do <<putv(v,i,vl); i := i+1>>;
- return v
- end;
- expr procedure build_defstruct_predicate_function(name, fnname);
- % BUILD_DEFSTRUCT_PREDICATE_FUNCTION(NAME, FNNAME) - Builds a defstruct
- % predicate to return as a function.
- {'de, fnname, '(x),
- {'and, '(vectorp x), {'eq, mkquote name, '(igetv x 0)}}};
- expr procedure build_defstruct_accessor_macro(slot_name,indx);
- {'dm, slot_name, '(u), {'list, '(quote structfetch), '(cadr u), indx}};
- macro procedure structfetch u;
- if !*faststructs then 'igetv . cdr u else 'getv . cdr u;
- %-----------------------------------------------------------------------
- % SETF for RLISP88
- %-----------------------------------------------------------------------
- macro procedure rsetf u; expandrsetf(cadr u, caddr u);
- expr procedure expandrsetf(lhs, rhs);
- if atom lhs then {'setq, lhs, rhs}
- else if eqcar(lhs, '!&VARIABLE_FETCH) then
- '!&VARIABLE_STORE . append(cdr lhs, {rhs})
- else if get(car lhs, 'ASSIGN_OP) then
- get(car lhs, 'ASSIGN_OP) . append(cdr lhs, {rhs})
- else if getd car lhs and eqcar(getd car lhs, 'macro) then
- expandrsetf(apply(cdr getd car lhs, {lhs}), rhs)
- else error(0, {lhs, "bad RSETF form"});
- deflist('((getv putv) (igetv putv) (car rplaca) (cdr rplacd)),
- 'ASSIGN_OP);
- % This is CSL specific but shouldn't hurt anybody.
- put('qgetv, 'ASSIGN_OP, 'qputv);
- endmodule;
- end;
|