| |
---|
| | |
---|
| | [FOO[BAR]] = Something Or Other |
---|
| | .fi |
---|
| | |
---|
| | .SS Lexical Namespaces |
---|
| | |
---|
| | The discussion of variable assignment and reference needs to be |
---|
| | expanded now to include the idea of "lexical namespaces". Think of a |
---|
| | namespace as just being the current context for variables - a way to |
---|
| | group variables together. The idea is that a variable name or |
---|
| | reference is always relative to the current namespace. That is, the |
---|
| | "real" name of the variable is the concatenation of the current |
---|
| | namespace with the variable's name. |
---|
| | |
---|
| | For instance, suppose the current namespace is "MYNS" and we see the |
---|
| | following in a configuration file: |
---|
| | |
---|
| | .nf |
---|
| | Foo = Bar |
---|
| | Baz = [Foo] |
---|
| | .fi |
---|
| | |
---|
| | What this |
---|
| | .B really |
---|
| | means to \*(TC is this: |
---|
| | |
---|
| | .nf |
---|
| | MYNS.Foo = MYNS.Bar |
---|
| | MYNS.Baz = [MYNS.Foo] |
---|
| | .fi |
---|
| | .SS Introducing Lexical Namespaces |
---|
| | |
---|
| | So far,the discussion of variables and references has conveniently |
---|
| | ignored the presence of another related \*(TC feature, "lexical |
---|
| | namespaces." Namespaces are a way to automatically group |
---|
| | related variables together. Suppose you wanted to describe |
---|
| | the options on your car in a configuration file. You might do |
---|
| | this: |
---|
| | |
---|
| | .nf |
---|
| | MyCar.Brand = Ferrari |
---|
| | MyCar.Model = 250 GTO |
---|
| | MyCar.Color = Red |
---|
| | |
---|
| | # And so on ... |
---|
| | .fi |
---|
| | |
---|
| | You'll notice that every variable start with the "thing" that |
---|
| | each item has in common - they are features of \'MyCar\'. |
---|
| | We can simplify this considerably by introducing a lexical namespace: |
---|
| | |
---|
| | .nf |
---|
| | [MyCar] |
---|
| | |
---|
| | Brand = Ferrari |
---|
| | Model = 250 GTO |
---|
| | Color = Red |
---|
| | .fi |
---|
| | |
---|
| | The first statement looks like a variable reference, but it is not. |
---|
| | .B A string inside square brackets by itself on a line introduces a namespace. |
---|
| | The first statement in this example sets the namespace to \'MyCar\'. |
---|
| | From that point forward until the namespace is changed again, every |
---|
| | variable assignment |
---|
| | .B and |
---|
| | reference is "relative" to the namespace. What this really means is |
---|
| | that \*(TC sticks the namspace plus a period in front of every |
---|
| | variable assigned or referenced. It does this automatically and |
---|
| | invisibly, so \'Brand\' is turned into \'MyCar.Brand\' and so on. You |
---|
| | can actually check this by loading the example above into a test |
---|
| | configuration file and running the \'test-tc\' program on it. You will |
---|
| | see the "fully qualified" variable names that actually were loaded |
---|
| | into the symbol table, each beginning with \'MyCar.\' and ending with |
---|
| | the variable name you specified. |
---|
| | |
---|
| | Realize that this is entirely a naming "trick". \*(TC has no clue |
---|
| | what the namespace |
---|
| | .B means, |
---|
| | it just combines the current namespace with the variable name to |
---|
| | create the actual variable name that will be returned in the symbol |
---|
| | table. |
---|
| | |
---|
| | You're likely scratching your head wondering why on earth this |
---|
| | feature present in \*(TC. There are, in fact, several reasons for it: |
---|
| | |
---|
| | .IP \(bu 4 |
---|
| | |
---|
| | It reduces typing repetetive information throughout the configuration |
---|
| | file. In turn, this reduces the likelyhood of a typographical or |
---|
| | spelling error. |
---|
| | |
---|
| | .IP \(bu 4 |
---|
| | |
---|
| | It helps visibly organize the configuration file. A namespace makes |
---|
| | it clear which variables are related to each other somehow. This is no |
---|
| | big deal in small configurations, but \*(TC was written with the idea |
---|
| | of supporting configuration files that might contain thousands or |
---|
| | even tens of thousands of entries. |
---|
| | |
---|
| | .IP \(bu 4 |
---|
| | |
---|
| | It simplifies the application programmer's job. Say I want to write a |
---|
| | program that extracts all the information about your car from the |
---|
| | configuration file, but I don't know ahead of time how many things you |
---|
| | will describe. All I really have to know is that you are using |
---|
| | \'MyCar\' as the namespace for this information. My program can then |
---|
| | just scan the symbol table after the configuration file has been parsed, |
---|
| | looking for variables whose name begins with \'MyCar.\'. So if you |
---|
| | want to add other details about your auto like, say, \'Age\', \'Price\', |
---|
| | and so on, you can do so later |
---|
| | .B and the program does not have to be rewritten. |
---|
| | |
---|
| | .IP \(bu 4 |
---|
| | |
---|
| | It helps enforce correct configuration files. By default, you can |
---|
| | introduce new namespaces into the configuration file any time you |
---|
| | like. However, as described in the previous section on the |
---|
| | \*(TC API, the application programmer can limit you to a pre-defined |
---|
| | set of legal namespaces (via the \'AllowNewNamespaces=False\' API |
---|
| | option). By doing this, the programmer is helping you avoid |
---|
| | incorrect configuration file entries by limiting just which |
---|
| | namespaces you can enter to reference or create variables. |
---|
| | |
---|
| | |
---|
| | .SS Lexical Namespace Specifics |
---|
| | |
---|
| | Creating and using lexical namespaces is fairly straightforward, |
---|
| | but there are a few restrictions and rules: |
---|
| | |
---|
| | .IP \(bu 4 |
---|
| | |
---|
| | The default initial namespace is the empty string, "". In this one case, |
---|
| | \*(TC does nothing to variables assigned or referenced. That's |
---|
| | why our early examples of how to assign a value to a variable and |
---|
| | then reference those value in the previous section worked. |
---|
| | When the namespace is "", nothing is done to the variable names. |
---|
| | The namespace, "", is also called the "root namespace." |
---|
| | |
---|
| | Bear in mind that the programmer can change this default namespace to |
---|
| | something other than "" before the configuration file is parsed. If |
---|
| | they do this, they would be well advised to let their users know this |
---|
| | fact. |
---|
| | |
---|
| | .IP \(bu 4 |
---|
| | There two ways to change to a new namespace: |
---|
| | |
---|
| | .nf |
---|
| | [NewNameSpace] # Must appear on a line by itself or with a comment only |
---|
| | NAMESPACE = NewNamespace |
---|
| | .fi |
---|
| | |
---|
| | If, at any point, you want to return to the root namespace, you can use |
---|
| | one of these two methods: |
---|
| | |
---|
| | .nf |
---|
| | [] |
---|
| | NAMESPACE = |
---|
| | .fi |
---|
| | |
---|
| | So, why are there two ways to do the same thing? |
---|
| | |
---|
| | |
---|
| | |
---|
| | |
---|
| | |
---|
| | In other words, \*(TC always prepends the current namespace (plus a |
---|
| | separating period) to any variable assignment or reference it finds in |
---|
| | a configuration. The one exception is when the namespace is "", in |
---|
| |
---|
| | |