?? doc
字號:
libparam came from the the disksim parameter file parser.This document is divided into two sections, the first for users ofapplications that use libparam and the second for developers usinglibparam in new applications.Still to document:the concrete syntaxdefinition vs instantiation"inheritance"say a little about:parse tree structs and functionsFor the user:In a libparam parameter file, you provide a number of blocks ofconfiguration information for the application. You first _define_blocks, you can _specialize_ blocks you've already defined and thenyou _instantiate_ blocks to hand them off to the application.Blocks# comments start with a '#' and continue to the end of the lineblockname { param name = value, another name = another value, . . . the last param = the last value # note no trailing ',' here}Parameter names may contain spaces but not ',' or '='. Multiplespaces will be reduced to a single space and leading and trailingspaces will be eaten. Parameter names are case-sensitive.e.g. a parameter named " foo bar " will be reduced to "foo bar"Values may be:1. decimal or '0x'-prefixed hexadecimal integers.2. strtod() format floats3. strings. In this version, strings may not contain any whitespace,semicolons, curly or square braces, colons, '?', '='. Quoted/escapedstrings may be supported in a future version.4. Lists. Lists are of the form [ l1, l2, <...> , ln ]The following notation is supported for automatically expanding stringvalues in list elements: if you want to create a list of stringvalues element1 through element100, you can use:[ element1 .. element100 ] which will be expanded to[ element1, element2, element3, <...>, element99, element100 ]You can mix '..' expansions with other elements so [ element14, element23, element29 .. element32, element213 ] is ok.5. Other blocks.Block definitions. Blocks are defined byblockname typename { <...> }Your application will provide a number of basic groups of configurableparameters called _modules_. You fill these in by defining a blockfor that module name. The application might export a module 'HEAD' head1 HEAD { material = stone}# typename can also be the name of a block you've already defined, e.g.head2 head1 { expression = frown}head2 is now reallyhead2 HEAD { material = stone, expression = frown}You can also override parameters previously set:head3 head2 { expression = smile}So now head3 ishead3 HEAD { material = stone, expression = smile}The application doesn't see anything we've done up to this point.To "pass off" a block to the application, we must _instantiate_ theblock. This is done by instantiate <list> as <blockname> where <list> is a list of one or more names to instantiate the definedblock <blockname> with.e.g.instantiate [ Head1, Head2 ] as head1creates two head1s called Head1 and Head2. '..'-expansion is allowed here soinstantiate [ Head1 .. Head100 ] as head1 creates 100 head1s as you might expect.Each module defines the set of parameters which are configurable,whether or not they are required and what types of values they maytake. Consult your application's documentation for a list of modulesand their parameters. Providing blocks that do not meet the module'srequirements will typically result in a run-time error for theapplication; it should provide an informative error message as to whythe given block is not ok.Some applications (e.g. DiskSim) may also automatically load modulesdefined with a particular name. See the application's documentationfor a list of such modules.For the programmer:What libparam provides:1. A concrete syntax for describing nested structures, lists, etc.2. A lex/yacc parser for the syntax.3. A parse tree format which the parser outputs.4. A number of utility functions for manipulating parse trees.5. Pretty-printing unparse functions for parse trees.6. Perl scripts to produce stub code from a user-provided moduledescription.Central to libparam is the notion of a block; a block is just acollection of name-value assignments. In the concrete syntax, a blockis delimited by curly braces and preceeded by a module name. As auser of libparam, you must provide a description of all of yourmodules that you want to be able to input with libparam. These moduledescriptions list all of the parameters that each module may take,what their types are and some code to do something with the value onceits been input.What a modspec (module specification) file looks like.The modspec file begins with two linesMODULE <module name>HEADER <code headers>RESTYPE <type of result>PROTO <module loader function prototype>If this module is used at top level -- i.e. not as a sub-blockof another module -- whenever a block for this module is instantiated,the function described by PROTO will be called-back with theparse-tree of the module. You must provide this function but libparamgenerates stub code to do most of the work; typically, you must onlyallocate your structure and then do any extra sanity-checkingor cleanup after the stub code finishes filling in your structure fromthe parse tree.HEADER lines are output verbatim to the beginning of package_module_param.cRESTYPE is the type of the object that is passed to the lp_loadparamsto be initialized.For each parameter, PARAM and INIT lines are mandatory. TEST andDEPEND lines are optional. Any text that isn't a comment or directiveup to the next PARAM line will be regarded as inline documentation andbe output into a .tex file with some fancy formatting that explainsthe module, name and type of the parameter and whether or not it ismandatory.# '#' comments here, too, # can be escaped with \# PARAM line fields are TAB delimited since <name> might contain# spacesPARAM <name> <type> <mandatory?> TEST <C code to determine if the value is valid>The stub code generated defines the following variables which you may use in your code:d for a doublei for an ints for a stringl for a listblk for a blockThe object being initialized is called 'result' in the stub code.The test code must be a C expression that can be a predicate for 'if'i.e. TEST i == 3will yield stub code:if(i == 3) { // do INIT step}else { // print an error message} lp_loadparams() takes the target object, the block of parameters andthe module descriptor struct and fills in the object with the contentsof the block of parameters according to the loader code referenced bythe module descriptor.You will typically call lp_loadparams() from your own initializationfunction which will allocate the structures and do anypost-processing. You should get warnings if the stub code accidentlyshadows something.INIT <C code to do something with the value> DEPEND <name of another parameter>Don't create cyclic dependencies! The generated stub code uses astack to handle the dependencies so deps can have sub-deps and so onup to a static stack size limit.<optional doc data>e.g.MODULE headPARAPM zardoz_head *zardoz_head_loadparams(struct lp_block *b)PARAM Material S 1TEST !strcmp(s, "granite") || !strcmp(s, "marble")INIT if(!strcmp(s, "granite")) { result->material = GRANITE; }INIT else if(!strcmp(s, "marble)) { result->material = MARBLE; }INIT else { assert(0); /* the TEST should have caught this! */ }In Zardoz, heads may be constructed from a number of materials. Legalvalues are "granite" or "marble."What mod.pl does:For your package p, for each module m described by m.modspec, mod.ploutputs p_m_param.h, p_m_param.c and p_m_param.texp_m_param.h contains:- an enum to define numeric codes for the parameters "p_m_param_t"These symbols are obtained by converting all leters to upper case,mashing multiple spaces into single spaces and then replacing spaceswith underscores and eliminating other characters that are not allowedin C symbol names. e.g. "A Silly param." appearing in module "vortex" in package "zardoz"would become ZARDOZ_VORTEX_A_SILLY_PARAMYou may have noticed that if you have two parameter names that differonly in capitialization, the resulting symbols after the abovetransformation will be the same. For this reason, you *should not*define multiple parameters that differ only in capitalization!- an array of parameter structs "p_m_params", indexed by p_m_param_t- a symbol "P_M_MAX" which is the number of elements in p_m_params- a module struct "p_m_mod" which contains: the module's name, a pointer to the parameter array the number of parameters a function pointer to your loader function an pointer to the loader function table in p_m_param.c an pointer to the dependency tabe in p_m_param.cp_m_param.c contains:#include "p_m_param.h"the contents of any other HEADER lines in the module specificationFor each parameter P: a function void P_loader(restype result, argtyp argname) where argtype is one of int, double, char *, struct dm_list * or struct dm_block * and is named i, d, s, l or blk respectively a function int P_depend(char *bv) which takes a bitvector of the parameters already successfully loaded and returns -1 if this parameter has no unmet dependencies or the numeric code (p_m_param_t) of an unmet dependency an array P_M_loaders of all of the loader functions in p_m_param_t order an array P_M_deps of all of the dependency functions What make_modules_h.pl does:Generates modules.h from all of p_m_params.hmodules.h contains:- an array p_mods containing pointers to each of the module structs inthe package- an enum "p_mod_t" assigning numeric codes for the modules- a symbol P_MAX_MODULE which is the highest such codeit also #includes all of the other p_m_param.h headers for each modulem in package p.Basic usage:1. Create a subdirectory. Not strictly necessary but you willprobably be happier later if you do. For this example, we'll call it"modules".2. Decide on a package name for your piece of software. "Zardoz"3. Generate one or more modspec files in the directory.head.modspecvortex.modspecgun.modspec4. Set up a Makefile in the modules directory:PACKAGE=zardozLIBPARAM=/path/to/libparam/scriptsPARAM_PROTO = head.modspec vortex.modspec gun.modspecPARAM_CODE = $(PARAM_PROTO:%.modspec=$(PACKAGE)_%_param.c)PARAM_HEADERS = $(PARAM_PROTO:%.modspec=$(PACKAGE)_%_param.h)$(PARAM_CODE): $(PACKAGE)_%_param.c: %.modspec $(LIBPARAM)/mod.pl $(PACKAGE) $< indent $@ || true# The indent step isn't necessary but makes the files look nice.# GNU indent sometimes complains about "unterminated string constant";# this is not an error as far as we knowmodules.h: $(PARAM_HEADERS) $(LIBPARAM)/make_modules_h.pl $(PACKAGE) *.modspec > modules.h5. Run make in the modules directory. 6. Now attach the stub code to your codee.g. in head.modspec, we have the head loader function#include "modules/modules.h"zardoz_head *zardoz_head_loadparams(struct lp_block *b) { zardoz_head *result = malloc(sizeof(zardoz_head)); lp_loadparams(result, b, &zardoz_head_mod); /* maybe do some extra checks here */ return result;} 7. In zardoz's initialization code, you will need to register all ofzardoz's libparam modules with libparam. This can be accomplished by:#include "modules/modules.h"for(c = 0; c <= ZARDOZ_MAX_MODULE; c++) { lp_register_module(zardoz_mods[c]);}If zardoz also uses the library "crystal" which also uses libparam,register crystal's modules here as well:#include <crystal/modules/modules.h>for(c = 0; c <= CRYSTAL_MAX_MODULE; c++) { lp_register_module(crystal_mods[c]);}Its ok that the enum module codes overlap in the different modules.hfiles; internal codes get dynamically assigned when youlp_register_module().now do lp_init_typetbl();to do some final initialization steps once you've registered all ofyour modules.Now you can load in an input file with lp_loadfile(). At a minimum:lp_loadfile(filehandle, 0, 0, filename, 0, 0);where filehandle is a stdio.h FILE and filename is a char * with thefile name. We just use the file name for some internal book-keepingso you can pass whatever you want there."instantiate" lines in your input will be processed as they areencountered. Put another way: your loader function is only calledwhen a module is instantiated, not when its only defined.What this means:foo FOO { # stuff}bar BAR { # more stuff}instantiate [ foo1 ] as foonone of the BAR loader code will be invoked until you instantiate abar. Consequently, if foo's loader code depends on something on bar,you'll need to instantiate a dummy bar before you instantiate foo.Advanced topics:Parameter overrides. This is an artifact from DiskSim but potentiallyuseful elsewhere. If you want to override parameter assignments fromthe file, give a char ** as the 5th arg to lp_instantiate and thelength of that array as the 6th arg. The array is a list of overrideswhich are interpreted as (name,param,value) triples. name is theinstantiate-time name of the block, param is a parameter in the blockand value is a new value. Recreating the top-level input from the parse tree:pass the address of an int and a struct lp_tlt ** to lp_loadfile:FILE *infile, *outfile;char *filename = "foo";int tlts_len;struct lp_tlt **tlts;/* tlt stands for "Top Level Thing" */lp_loadfile(infile, &tlts, &tlts_len, filename);now unparse it withlp_unparse_tlts(tlts, tlts_len, outfile, filename);The point of the filename parameter is so we can tag things internallyand avoid unparsing past "source <file>" boundaries; this will exactlyrecreate the top-level inputfile rather than the result of inliningall of the sourced files into it.
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -