diff --git a/tconfpy.py b/tconfpy.py index 55cb225..8286970 100755 --- a/tconfpy.py +++ b/tconfpy.py @@ -6,7 +6,7 @@ # Program Information PROGNAME = "tconfpy" -RCSID = "$Id: tconfpy.py,v 1.186 2005/01/13 10:36:24 tundra Exp $" +RCSID = "$Id: tconfpy.py,v 1.187 2005/01/13 20:45:50 tundra Exp $" VERSION = RCSID.split()[2] # Copyright Information @@ -158,52 +158,35 @@ #----------------------------------------------------------# -ALLOWNEWVAR = True # Allow new variable creation in cfg file -TEMPLATES = {} # Place to hold variable templates -TEMPONLY = False # Allow only template variable creation -LITERALVARS = False -DEBUG = False # Control Debug output -INLITERAL = False # Indicates we are currently in a literal block - -DebugMsg = [] # Place to store and return debug info -ErrMsgs = [] # Place to store and return errors -WarnMsgs = [] # Place to store and return warnings -LiteralLines = [] # Place to store and return unprocessed lines - - -CondStack = [["", True,]] # Conditional stack -TotalLines = 0 # Total number of lines parsed - - -##### -# Dummy Object Used To Return Parsing Results -##### - -# This is done so the caller only needs to know the name of -# each result set, not its position in the list of returned items - - -class RetObj(object): - - def __init__(self): - self.SymTable = {} - self.Errors = [] - self.Warnings = [] - self.Debug = [] - self.Literals = [] - -# End of 'RetObj' - - ########## -# Symbol Table +# Symbol Table Related Classes ########## -# Symbol Table is a dictionary in the form: +# Symbol Table is an object containing a dictionary in the form: # # {varname : descriptor} # -# where the descriptor is an object with the following attributes + +class SymbolTable(object): + + Symbols = {} + DebugMsgs = [] + ErrMsgs = [] + WarnMsgs = [] + LiteralLines = [] + ALLOWNEWVAR = True + TEMPLATES = {} + TEMPONLY = False + LITERALVARS = False + INLITERAL = False + DEBUG = False + CondStack = [["", True],] # Always has one entry as a sentinel + TotalLines = 0 + +# End of class 'SymbolTable' + + +# The descriptor is an object with the following attributes # # Value, Writeable, Type, Default, LegalVals = [list of legal vals], Min, Max] @@ -234,16 +217,16 @@ # End of class 'VarDescriptor' -# Initialize the table using the predefined symbols -SymTable = {} + +SymTable = SymbolTable() for var in Predefined.keys(): d = VarDescriptor() d.Value = Predefined[var] d.Writeable = False - SymTable[var] = d + SymTable.Symbols[var] = d ########## @@ -368,9 +351,9 @@ def DebugMsg(dmsg, args): - global DebugMsgs + global SymTable - DebugMsgs.append(mkmsg(Messages[dmsg] % args, dmsg)) + SymTable.DebugMsgs.append(mkmsg(Messages[dmsg] % args, dmsg)) # End of 'DebugMsg()' @@ -381,9 +364,9 @@ def ErrorMsg(error, args): - global ErrMsgs + global SymTable - ErrMsgs.append(mkmsg(Messages[error] % args + "!", error)) + SymTable.ErrMsgs.append(mkmsg(Messages[error] % args + "!", error)) # End of 'ErrorMsg()' @@ -394,9 +377,9 @@ def WarningMsg(warning, args): - global WarnMsgs + global SymTable - WarnMsgs.append(mkmsg(Messages[warning] % args + "!", warning)) + SymTable.WarnMsgs.append(mkmsg(Messages[warning] % args + "!", warning)) # End of 'WarningMsg()' @@ -421,7 +404,7 @@ def ParseConfig(cfgfile, CallingProgram=PROGINFO, - InitialSymTable={}, + InitialSymTable=SymbolTable(), AllowNewVars=True, Templates={}, TemplatesOnly=False, @@ -429,43 +412,32 @@ ReturnPredefs=True, Debug=False): - global DebugMsgs, ErrMsgs, WarnMsgs, LiteralLines - global ALLOWNEWVAR, TEMPLATES, TEMPONLY, LITERALVARS, INLITERAL, DEBUG - global CondStack, SymTable, TotalLines + global SymTable - # Initialize the globals - - DebugMsgs = [] - ErrMsgs = [] - WarnMsgs = [] - LiteralLines = [] - - ALLOWNEWVAR = AllowNewVars - TEMPLATES = Templates - TEMPONLY = TemplatesOnly - LITERALVARS = LiteralVars - INLITERAL = False - DEBUG = Debug - - CondStack = [["", True],] # Always has one entry as a sentinel - TotalLines = 0 - # Set the name of the calling program for output messages mkmsg.proginfo = CallingProgram - - # Setup container to return parsing results + # Create a new symbol table - retobj = RetObj() + SymTable = SymbolTable() + + # Initialize the globals + + SymTable.ALLOWNEWVAR = AllowNewVars + SymTable.TEMPLATES = Templates + SymTable.TEMPONLY = TemplatesOnly + SymTable.LITERALVARS = LiteralVars + SymTable.INLITERAL = False + SymTable.DEBUG = Debug # Add any passed symbols to the SymbolTable deserror = False - for sym in InitialSymTable: + for sym in InitialSymTable.Symbols: - des = InitialSymTable[sym] + des = InitialSymTable.Symbols[sym] # Make sure a valid descriptor was passed for each variable @@ -521,41 +493,36 @@ # Only load the symbol table with valid entries if desok: - SymTable[sym] = des + SymTable.Symbols[sym] = des # Indicate that a problem was encountered else: deserror = True # If any of the passed symbols had bogus descriptor contents, we're done - if deserror: - retobj.SymTable = SymTable - retobj.Errors = ErrMsgs - retobj.Warnings = WarnMsgs - retobj.Debug = DebugMsgs - retobj.Literals = LiteralLines - return retobj + if deserror: + return SymTable # Symbol Table passed to API was OK, so keep going # Make sure the symbol table has a valid namespace - if NAMESPACE not in SymTable: - SymTable[NAMESPACE] = VarDescriptor() - SymTable[NAMESPACE].Value = "" + if NAMESPACE not in SymTable.Symbols: + SymTable.Symbols[NAMESPACE] = VarDescriptor() + SymTable.Symbols[NAMESPACE].Value = "" # Otherwise, ensure that the initial namespace is properly formed. # If not, revert to root namespace. - elif not ValidateSymbolName(SymTable[NAMESPACE].Value, STARTUP, STARTUP, AllowEmpty=True): - SymTable[NAMESPACE].Value = "" + elif not ValidateSymbolName(SymTable.Symbols[NAMESPACE].Value, STARTUP, STARTUP, AllowEmpty=True): + SymTable.Symbols[NAMESPACE].Value = "" # Report namespace to debug output - if DEBUG: - DebugMsg("dNAMESPACE", (STARTUP, STARTUP, SymTable[NAMESPACE].Value)) + if SymTable.DEBUG: + DebugMsg("dNAMESPACE", (STARTUP, STARTUP, SymTable.Symbols[NAMESPACE].Value)) # Parse the file @@ -564,35 +531,29 @@ # Make sure we had all condition blocks balanced with matching '.endif' - finalcond = len(CondStack) + finalcond = len(SymTable.CondStack) if finalcond != 1: ErrorMsg("eENDIFMISS", (cfgfile, ATEOF, finalcond-1)) # Make sure we ended any literal processing properly - if INLITERAL: + if SymTable.INLITERAL: WarningMsg("wENDLITMISS", (cfgfile, ATEOF)) # Return the parsing results - if DEBUG: - DebugMsg("dNUMLINES", (cfgfile, TotalLines)) + if SymTable.DEBUG: + DebugMsg("dNUMLINES", (cfgfile, SymTable.TotalLines)) # Strip out Prefefined Variables if user does not want them if not ReturnPredefs: - del SymTable[NAMESPACE] + del SymTable.Symbols[NAMESPACE] for sym in Predefined: - del SymTable[sym] + del SymTable.Symbols[sym] - retobj.SymTable = SymTable - retobj.Errors = ErrMsgs - retobj.Warnings = WarnMsgs - retobj.Debug = DebugMsgs - retobj.Literals = LiteralLines - - return retobj + return SymTable # End of 'ParseConfig()' @@ -655,23 +616,23 @@ # Prepend the current namespace unless we are in the top-level # namespace. - elif SymTable[NAMESPACE].Value: - sym = "%s%s%s" % (SymTable[NAMESPACE].Value, NSSEP, sym) + elif SymTable.Symbols[NAMESPACE].Value: + sym = "%s%s%s" % (SymTable.Symbols[NAMESPACE].Value, NSSEP, sym) # Handle environment variables if sym and sym[0] == ENVIRO and sym[1:] in os.environ: envvar = os.getenv(sym[1:]) line = line.replace(var, envvar) - if DEBUG: + if SymTable.DEBUG: DebugMsg("dVARREF", (cfgfile, linenum, var, envvar)) # Handle variables in symbol table - elif sym in SymTable: - symval = str(SymTable[sym].Value) + elif sym in SymTable.Symbols: + symval = str(SymTable.Symbols[sym].Value) line = line.replace(var, symval) - if DEBUG: + if SymTable.DEBUG: DebugMsg("dVARREF", (cfgfile, linenum, var, symval)) @@ -701,7 +662,7 @@ def ParseFile(cfgfile, current_cfg, current_linenum): - global IgnoreCase, MsgList, SymTable, TotalLines + global MsgList, SymTable linenum=0 @@ -712,7 +673,7 @@ # Process and massage the configuration file for line in cf.read().splitlines(): linenum += 1 - TotalLines += 1 + SymTable.TotalLines += 1 # Parse this line ParseLine(line, cfgfile, linenum) @@ -733,7 +694,7 @@ def ParseLine(line, cfgfile, linenum): - global CondStack, MsgList, SymTable, INLITERAL + global MsgList, SymTable orig = line # May need copy of original for debug output @@ -754,20 +715,20 @@ if line.strip() in (LITERAL, ENDLITERAL): if line.strip() == LITERAL: - if INLITERAL: + if SymTable.INLITERAL: WarningMsg("wLITEXTRA", (cfgfile, linenum)) else: - INLITERAL = True + SymTable.INLITERAL = True # Process ENDLITERAL statements else: - if not INLITERAL: + if not SymTable.INLITERAL: WarningMsg("wENDLITEXTRA", (cfgfile, linenum)) else: - INLITERAL = False + SymTable.INLITERAL = False - if DEBUG: + if SymTable.DEBUG: DebugMsg("dPARSEDLINE", (cfgfile, linenum, orig, line)) return @@ -781,16 +742,16 @@ # block. ##### - if INLITERAL: + if SymTable.INLITERAL: - if CondStack[-1][1]: + if SymTable.CondStack[-1][1]: - if LITERALVARS: + if SymTable.LITERALVARS: line, ref_ok = DerefVar(line, cfgfile, linenum) - LiteralLines.append(line) + SymTable.LiteralLines.append(line) - if DEBUG: + if SymTable.DEBUG: DebugMsg("dPARSEDLINE", (cfgfile, linenum, orig, line)) @@ -798,7 +759,7 @@ # noting in debug output if requested. else: - if DEBUG: + if SymTable.DEBUG: DebugMsg("dLINEIGNORE", (cfgfile, linenum, orig, dNOTINCLUDE)) return @@ -818,7 +779,7 @@ if not line: # Note blank lines for debug purposes - if DEBUG: + if SymTable.DEBUG: DebugMsg("dLINEIGNORE", (cfgfile, linenum, orig, dBLANKLINE)) return @@ -836,14 +797,14 @@ # Get the enclosing block type and state - btyp, bst = CondStack.pop() + btyp, bst = SymTable.CondStack.pop() # ELSE is only permitted after an immediately preceding IF form if btyp != IF: ErrorMsg("eELSENOIF", (cfgfile, linenum)) - CondStack.append(["", False]) # Error makes all that follows False + SymTable.CondStack.append(["", False]) # Error makes all that follows False # We *are* in an IF block an ELSE is appropriate. # To determine whether the ELSE should be taken or not we have @@ -859,15 +820,15 @@ # If the containing block is True, the contained IF state is legitimate. # The ELSE inverts the IF state in that case. - if CondStack[-1][1]: - CondStack.append([ELSE, not bst]) + if SymTable.CondStack[-1][1]: + SymTable.CondStack.append([ELSE, not bst]) # The containing block is false, so everything within it is also false else: - CondStack.append([ELSE, False]) + SymTable.CondStack.append([ELSE, False]) - if DEBUG: + if SymTable.DEBUG: DebugMsg("dPARSEDLINE", (cfgfile, linenum, orig, line)) return @@ -880,14 +841,14 @@ if line == ENDIF: # Remove one level of conditional nesting - CondStack.pop() + SymTable.CondStack.pop() # Error, if there are more .endifs than conditionals - if not CondStack: + if not SymTable.CondStack: ErrorMsg("eENDIFEXTRA", (cfgfile, linenum)) - CondStack.append(["", False]) # Restore sentinel & inhibit further parsing + SymTable.CondStack.append(["", False]) # Restore sentinel & inhibit further parsing - if DEBUG: + if SymTable.DEBUG: DebugMsg("dPARSEDLINE", (cfgfile, linenum, orig, line)) return @@ -915,8 +876,8 @@ if FIRSTTOK == INCLUDE: - if not CondStack[-1][1]: - if DEBUG: + if not SymTable.CondStack[-1][1]: + if SymTable.DEBUG: DebugMsg("dLINEIGNORE", (cfgfile, linenum, orig, dNOTINCLUDE)) return @@ -926,7 +887,7 @@ if ref_ok: ParseFile(line, cfgfile, linenum) - if DEBUG: + if SymTable.DEBUG: DebugMsg("dPARSEDLINE", (cfgfile, linenum, orig, line)) return @@ -991,7 +952,7 @@ # 4) We're dealing with the NAMESPACE variable # 5) The variable name is not escaped - ns = SymTable[NAMESPACE].Value + ns = SymTable.Symbols[NAMESPACE].Value if ns and \ v[0] != ENVIRO and \ @@ -1007,7 +968,7 @@ v = "%s%s%s" % (ns, NSSEP, v) # Produce debug ouput of actual variable name being checked - if DEBUG: + if SymTable.DEBUG: DebugMsg("dVAREXISTS", (cfgfile, linenum, v)) # Handle environment variables @@ -1016,7 +977,7 @@ numexist += 1 # Handle local local variables - elif v in SymTable: + elif v in SymTable.Symbols: numexist += 1 # And set the conditional state accordingly @@ -1093,15 +1054,15 @@ # Set parser state based on a successful conditional test # But it has to be ANDed with the state of the enclosing block - enclosing = CondStack[-1][1] - CondStack.append([IF, condstate and enclosing]) + enclosing = SymTable.CondStack[-1][1] + SymTable.CondStack.append([IF, condstate and enclosing]) # Now reflect this in the parsed line line = sTRUE if not condstate: line = sFALSE - if DEBUG: + if SymTable.DEBUG: DebugMsg("dPARSEDLINE", (cfgfile, linenum, orig, line)) return @@ -1117,8 +1078,8 @@ elif EQUAL in line: - if not CondStack[-1][1]: - if DEBUG: + if not SymTable.CondStack[-1][1]: + if SymTable.DEBUG: DebugMsg("dLINEIGNORE", (cfgfile, linenum, orig, dNOTINCLUDE)) return @@ -1127,7 +1088,7 @@ if line.count(DELIML + DELIMR): ErrorMsg("eSYMNONAME", (cfgfile, linenum)) - if DEBUG: + if SymTable.DEBUG: DebugMsg("dPARSEDLINE", (cfgfile, linenum, orig, line)) return @@ -1163,7 +1124,7 @@ # move this test down into the body of the variable name # processing where namespace fiddling takes place. - elif l in SymTable and not SymTable[l].Writeable: + elif l in SymTable.Symbols and not SymTable.Symbols[l].Writeable: ErrorMsg("eVARREADONLY", (cfgfile, linenum, l)) # Load variable into the symbol table @@ -1188,7 +1149,7 @@ # In all other cases prepend current namespace else: varname = l - ns = SymTable[NAMESPACE].Value + ns = SymTable.Symbols[NAMESPACE].Value # Top level namespace variables don't need # separator. @@ -1200,10 +1161,10 @@ # default to be this first value assigned and # create the new entry - if l not in SymTable: + if l not in SymTable.Symbols: # Only do this if new variable creation allowed - if ALLOWNEWVAR: + if SymTable.ALLOWNEWVAR: # Rules for new variable creation: # @@ -1213,16 +1174,16 @@ # Rule 1 - if varname in TEMPLATES: + if varname in SymTable.TEMPLATES: # Create the new variable - SymTable[l] = TEMPLATES[varname] + SymTable.Symbols[l] = SymTable.TEMPLATES[varname] # Load the proposed value only if valid if ValidateValue(l, r, cfgfile, linenum): - SymTable[l].Value = r + SymTable.Symbols[l].Value = r # If value is invalid, remove this # variable. We cannot create a new @@ -1231,11 +1192,11 @@ # function. else: - del(SymTable[l]) + del(SymTable.Symbols[l]) # Rule 2 - elif TEMPONLY: + elif SymTable.TEMPONLY: ErrorMsg("eTEMPONLY", (cfgfile, linenum, varname)) # Rule 3 @@ -1247,7 +1208,7 @@ d = VarDescriptor() d.Default = r d.Value = r - SymTable[l] = d + SymTable.Symbols[l] = d # New vars not allowed else: @@ -1272,16 +1233,16 @@ # equivalent logical value for boolean # variables first - if SymTable[l].Type == TYPE_BOOL: + if SymTable.Symbols[l].Type == TYPE_BOOL: r = Booleans[r.capitalize()] - SymTable[l].Value = r + SymTable.Symbols[l].Value = r # Produce debug output when we change namespaces - if DEBUG and l == NAMESPACE: + if SymTable.DEBUG and l == NAMESPACE: DebugMsg("dNAMESPACE", (cfgfile, linenum, r)) - if DEBUG: + if SymTable.DEBUG: DebugMsg("dPARSEDLINE", (cfgfile, linenum, orig, line)) return @@ -1369,7 +1330,7 @@ update = False # Validation function issues appropriate errors - des = SymTable[l] + des = SymTable.Symbols[l] typ = des.Type lv = des.LegalVals low = des.Min @@ -1434,7 +1395,7 @@ # This can be kind of complex, so produce # debug output here to help the poor user - if DEBUG: + if SymTable.DEBUG: DebugMsg("dREGEXMATCH", (cfgfile, linenum, r, rex, l)) except: @@ -1486,7 +1447,7 @@ __all__ = ["ParseConfig", - "RetObj", + "SymbolTable", "TYPE_BOOL", "TYPE_COMPLEX", "TYPE_FLOAT",