diff --git a/tconfpy.3 b/tconfpy.3 index b98b929..7679df9 100644 --- a/tconfpy.3 +++ b/tconfpy.3 @@ -122,7 +122,7 @@ results. -.SS Passing An Initial Symbol Table +.SS Reasons For Passing An Initial Symbol Table The simplest way to parse a configuration file is just to call the parser with the name of that file: @@ -146,7 +146,7 @@ .nf 1) You may wish to write a configuration file which somehow depends - on a pre-defined variable which only the calling program can know: + on a pre-defined variable that only the calling program can know: .if [APPVERSION] == 1.0 # Set configuration for older application releases @@ -167,41 +167,244 @@ but cannot be changed. 3) You may want to place limits on what values can be assigned to - a particular variable. By default, all variables newly defined - within a configuration file are defined to be strings of any - length, with no restrictions on their content. By pre-defining a - variable, you can specify its type (integer, string, boolean, or - complex), a list of possible legal values, a range of legal values, - min/max string length, or a set of regular expressions for string - variables - one of which must match. In other words, you can have + a particular variable. When a variable is newly defined in a + a configuration file, it just defaults to being a string + variable without any limits on its length or content. But + variables that are created by a program have access to the + variable's "descriptor". By setting various attribues + of the variable descriptor you can control variable type, + content, and range of values. In other words, you can have \*(TC "validate" what values the user assigns to particular variables. This substantially simplifies your application because no invalid variable value will ever be returned from the parser. .fi +.SS How To Create An Initial Symbol Table + +A \*(TC "Symbol Table" is really nothing more than a Python +dictionary. The key for each dictionary entry is the variable's name +and the value is a \*(TC-specific object called a "variable +descriptor". Creating new variables in the symbol table involves +nothing more than this: + +.nf + +from tconfpy import * + +# Create an empty symbol table +MySymTable = {} + +# Create descriptor for new variable +MyVarDes = VarDescriptor() + +# Code to fiddle with descriptor contents goes here +MyVarDes.Value = "MyVal" + +# Now load the variable into the symbol table +MySymTable["MyVariableName"] = MyVarDes + +.fi -class VarDescriptor(object): +The heart of this whole business the \'VarDescriptor\' object. It +"describes" the value and properties of a variable. These +descriptor objects have the following attributes and defaults: - # Default variable type is a writeable string with no constraints - def __init__(self): - self.Value = "" - self.Writeable = True - self.Type = TYPE_STRING - self.Default = "" - self.LegalVals = [] - self.Min = None - self.Max = None +.nf + + VarDescriptor.Value = "" + VarDescriptor.Writeable = True + VarDescriptor.Type = TYPE_STRING + VarDescriptor.Default = "" + VarDescriptor.LegalVals = [] + VarDescriptor.Min = None + VarDescriptor.Max = None +.fi -# End of class 'VarDescriptor' +When \*(TC encounters a new variable in a configuration file, it just +instantiates one of these descriptor objects with these defaults for +that variable. That is, variables newly-defined in a configuration +file are entered into the symbol table as string types with no +restriction on content or length. + +But, when you create variables under program control to "prime" an +initial symbol table, you can modify the content of any of these +attributes for each variable. These descriptor attributes are what +\*(TC uses to validate subsequent attempts to change the variable's +value in the configuration file. In other words, modifying a +variable's descriptor tells \*(TC just what you'll accept as "legal" +values for that variable. + +Each attribute has a specific role: + +.TP +.B VarDescriptor.Value (Default: Empty String) + +Hold the current value for the variable. + +.TP +.B VarDescriptor.Writeable (Default: True) + +Sets whether or not the user can change the variable's value. Setting +this attribute to False makes the variable "Read Only". + +.TP +.B VarDescriptor.Type (Default: TYPE_STRING) + +One of TYPE_BOOL, TYPE_COMPLEX, TYPE_FLOAT, TYPE_INT, or TYPE_STRING. +This defines the type of the variable. Each time \*(TC sees +a value being assigned to a variable in the configuration file, it +checks to see if that variable already exists in the symbol table. +If it does, the parser checks the value being assigned and makes sure +it matches the type declared for that variable. For example, +suppose you did this when defining the variable, \'foo\': + +.nf +VarDescriptor.Type = TYPE_INT +.fi + +Now suppose the user puts this in the configuration file: + +.nf +foo = bar +.fi + +This will cause a type mismatch error because \'bar\' cannot be coerced +into an integer type - it is a string. +As a general matter, for existing variables, \*(TC attempts to coerce +the right-hand-side of an assignment to the type declared for that +variable. The least fussy operation here is when the variable is +defined as TYPE_STRING because pretty much everything can be coerced +into a string. For example, here is how \'foo = 3+8j\' is treated: -When you pass an initial symbol table to the parser, \*(TC does -some basic validation that the table contents properly conform -to the \'VarDescriptor\' format and generates error messages if -it finds problems. However, the program does + +If VarDescriptor.Type = TYPE_COMPLEX then Var.Descriptor.Value will +hold the +.B complex number +3+8j. + +If VarDescriptor.Type = TYPE_STRING then Var.Descriptor.Value will +hold the +.B string +"3+8j". + +.TP +.B VarDescriptor.Default (Default: Empty String) + +This is a place to store the default value for a given variable. When +a variable is newly-defined in a configuration file, \*(TC places the +first value assigned to that variable into this attribute. For +variables already in the symbol table, \*TC does nothing to this +attribute. This attribute is not actually used by \*(TC for anything. +It is provided as a convenience so that the calling program can easily +"reset" every variable to its default value if desired. + +.TP +.B VarDescriptor.LegalVals (Default: []) + +Sometimes you want to limit a variable to a specific set of values. +That's what this attribute is for. \'LegalVals\' explictly lists +every legal value for the variable in question. If the list is +empty,then this validation check is skipped. + +.B IMPORTANT: +If you change the content of \'LegalVals\', make sure it is always a +Python list. \*(TC's validation logic presumes this attribute +to be a list and will blow up nicely if it is not. + +The exact semantics of LegalVals varies depending on +the type of the variable. + +.nf + +Variable Type What LegalVals Does +------------- ------------------- +Boolean Nothing - Ignored + +Integer, Float, Complex List of numeric values the + user can assign to this variable + + Examples: [1, 2, 34] + [3.14, 2.73, 6.023e23] + [3.8-4j, 5+8j] + +String List of Python regular expressions. + User must assign a value to this + variable that matches at least + one of these regular expressions. + + Example: [r'a+.*', r'^AnExactString$'] +.fi + +The general semantic here is "If Legal Vals is not an empty list, the +user must assign a value that matches one of the items in LegalVals." + +One special note applies to \'LegalVals\' for string variables. \*(TC +always assumes that this list contains Python regular expressions. +For validation, it grabs each entry in the list, attempts to compile +it as a regex, and checks to see if the value the user wants to set +matches. If you define an illegal regular expression here, \*(TC will +catch it and produce an appropriate error. + + +.TP +.B VarDescriptor.Min and VarDescriptor.Max (Default: None) + +These set the minimum and maxium legal values for the variables, +but the semantics vary by variable type: + +.nf + +Variable Type What Min/Max Do +------------- --------------- + +Boolean, Complex Nothing - Ignored + +Integer, Float Set Minimum/Maxium allowed values. + +String Set Minimum/Maximum string length + +.fi + +In all cases, if you want these tests skipped, set \'Min\' or \'Max\' +to the Python None. + +.P +All these various validations are logically "ANDed" together. +i.e., A new value for a variable must be allowed +AND of the appropriate type AND one of the legal values AND +within the min/max range. + +One last note here concerns Boolean variables. Booleans +are actually stored in the symbol table as True or False. +However, \*(TC accepts user statements in a number of +formats to do this: + +.nf + +Boolean True Boolean False +------------ ------------- + +foo = 1 foo = 0 +foo = True foo = False +foo = Yes foo = No +foo = On foo = Off + +.fi + +This is the one case where \*(TC is insensitive to case - +"tRUE", "TRUE", and "true" are all accepted, for example. + + +.SS How The \*(TC Parser Validates The Initial Symbol Table + +When you pass an initial symbol table to the parser, \*(TC does some +basic validation that the table contents properly conform to the +\'VarDescriptor\' format and generates error messages if it finds +problems. However, the program does .B not check your specifications to see if they make sense. For instance if you define an integer with a minimum value of 100 and a maximum