⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 doc

📁 目前最精确的磁盘模拟器的第3版
💻
字号:
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.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.  This moduledescription lists all of the parameters that the module may take, whattheir types are and some code to do something with the value once itsbeen loaded in.What a modspec (module specification) file looks like.The modspec file begins with two linesMODULE <module name>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.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# 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 stringblk for a blockThe 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} You will typically #include the stub code into your own loaderfunction which will allocate the structures and do anypost-processing.  We recommend for sanity's sake uniformly calling thereturn value of the loader function "result" though the code doesn'tdepend on this.  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! */ }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 names the module and contains apointer to the parameter array, the number of parameters, a functionpointer to your loader function.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/zardoz_head_param.h"zardoz_head *zardoz_head_loadparams(struct lp_block *b) {	zardoz_head *result = malloc(sizeof(zardoz_head));	#include "modules/zardoz_head_param.c"	/* 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 + -