===== Global and Local Symbol Assignment ===== For the non-programmer, especially, who is nevertheless writing a DCL command procedure (script), the notion of **global** vs. **local** symbols (variables) and symbol tables is at first a bit confusing. This article will hopefully make these concepts more clear. ==== First, the Practical Matter of Notation ==== Any DCL symbol (variable) is //created// (or //defined//) using one of the four following **assignment operators**: ''=='' or '':=='', ''='' or '':=''. Note that the first two assignment operators each use two (double) equal-signs, while the second pair uses only single equal-signs. These four operators, when spoken aloud, are rather clumsily pronounced: "equals-equals" or "colon-equals-equals", "equals" or "colon-equals". Here are the rules for creating DCL symbols: * Global symbols (variables) are always created (defined) with either the //double// ''=='' or '':=='' assignment operators. * Local symbols (variables are always created (defined) with either the //single// ''='' or '':='' assignment operators. ==== Two Forms of Assignment Operators ==== Okay... but why the two forms, like ''=='' and '':==''? Here, DCL's ''HELP'' command sheds some light (don't attempt to use ''$ HELP ='' or ''$ HELP :='', as these won't work because of command parsing limitations): $ HELP HELP The HELP command invokes the HELP Facility to display information about a command or topic. In response to the "Topic?" prompt, you can: o Type the name of the command or topic for which you need help. ... Additional information available: := = @ ABS ACCOUNTING ACL_Editor ... Topic? = = Defines a symbolic name for a character string or integer value. Format symbol-name =[=] expression ... Topic? := := Defines a symbolic name for a character string value. Format symbol-name :=[=] string ... $ Read each of these HELP sections in their entirety. From this HELP text -- rather quaint: "Defines a //symbolic name//...", but we know that we're just creating //variables// here -- we discover that: * Both the ''=='' and ''='' assignment operators create a DCL variable (symbol) which can contain __either__ a //character string value// __or__ a //numeric (integer) value//. * Both the '':=='' and '':='' assignment operators create a DCL variable (symbol) which can contain __only__ a //character string value// -- these assignment operators __cannot__ create symbols having a numeric (integer) value. These operators also have //side effects//: Each will force the string value to all //UPPERCASE// letters, and will squeeze out any repeated //space// or //tab// characters, replacing those sequences with //a single space character// for each such sequence. Because of those //side effects// (also referred to as data "//normalization//"), experienced DCL script programmers usually avoid, or at least limit, their use of the '':=='' and '':='' assignment operators, because the ''=='' and ''='' operators are //more general purpose//, and make //obvious// what the data-type of the value being assigned to a symbol actually is (this is a rule-of-thumb, not a hard-and-fast rule). ==== Next, Some Definitions ==== * Symbols are just Variables -- Remember, as explained [[dcl_symbols|here]], that a DCL //symbol// is nothing more than a DCL //variable//, useful for programming and computing with DCL command procedures (scripts), etc. * Global Symbols -- A global DCL variable is visible (can be used) anywhere in a DCL process, either on the command line, within a batch process, or within a DCL command procedure at any //depth// (see below). * The Global Symbol Table -- A memory-resident data structure within a VMS process which holds the names and values of all //global// variables defined and used by the process. There is only one such global symbol table, and it is automatically created at process creation time. * Local Symbols -- A local DCL variable has limited visibility or scope; when defined at a particular command procedure depth (see below), a local symbol can be used (referenced) only at/within that procedure depth, or by any command procedure depth below (greater depth) its creator depth. * Local Symbol Tables -- One or more memory-resident data structures within a VMS process which holds the names and values of all //local// variables defined and used by the process. A local symbol table is available automatically at command procedure depth 0 (the command line level) if/when any local variables are defined at that level; in addition, a(nother) local symbol table is created on-the-fly for each command procedural depth >0 reached by subsequent command file invocations (see below). * Command Procedure Depth -- When one command file calls or invokes another one, that called command procedure's //depth// or //level// is one greater than the one which calls/invokes it. By definition, the command line's depth is 0 (zero). When you invoke a command file (script) from the command line -- for example: ''$ @RECURSE'' (see [[global_and_local_symbol_assignment#demonstrating_command_procedure_depth|section]] below) -- that command file's level is 1. If an executing command file calls yet another one, that called command file's level is one greater than the one which calls it. Command file depth is limited to 31 or less; if an attempt is made to invoke a script at depth 32 or more, DCL generates the error message ''%DCL-W-STKOVF, command procedures too deeply nested - limit to 32 levels'' (believe me, a depth of 31 is sufficient). ==== Demonstrating Command Procedure Depth ==== To best understand both global and local symbol tables, it helps to get a deeper understanding of how command procedure **depth** works; the following demo script can help you see the behavior of procedure depth and local symbol tables. The following command procedure, RECURSE.COM, demonstrates this notion of depth. When invoked from the command line, this script then calls itself recursively three times; each invocation displays its view of both the process's global symbol table and it's own instance of the local symbol table: $ ! RECURSE.COM -- a recursive-call com-file demo, showing procedure depth ! 1 $ ! $ procdepth = F$ENVIRONMENT( "DEPTH" ) ! capture procedure depth in local variable ! 3 $ ! $ wso = "WRITE sys$output" ! define this local variable (symbol) ! 5 $ wso "" ! 6 $ ! $ wso "*** == Global Symbol Table (part), always at Procedure Depth 0 ***" ! 8 $ SHOW SYMBOL /GLOBAL $S* ! 9 $ wso "### = Local Symbol Table (entire) at Procedure Depth ''procdepth' ###" ! 10 $ SHOW SYMBOL /LOCAL * ! 11 $ ! $ IF ( procdepth .LT. 3 ) ! 13 $ THEN wso "" ! 14 $ callnumber = procdepth + 1 ! 15 $ wso ">>> Recursive call #", callnumber, ": @RECURSE ''callnumber'" ! 16 $ @RECURSE 'callnumber' ! call self recursively, up to 3 times ! 17 $ ENDIF ! 18 $ ! $ wso ">>> exiting from recursion #", procdepth ! 20 $ EXIT 1 ! 21 $ ! Annotations: * Line 3 -- Assign the value of the lexical function ''F$ENVIRONMENT("DEPTH")'' to the local variable (symbol) ''procdepth''. * Lines 5, 6, 8, 10, 14, 16 & 20 -- Create the local command symbol ''wso'', and use it to output messages and blank lines as needed. * Line 9 -- Display a couple of symbols (''$STATUS'' and ''$SEVERITY'') from the global symbol table, each time this command script is recursively called (this is a selective display; dumping the entire global symbol table would be lengthy and would just obscure things here). * Line 11 -- Display the whole __local__ symbol table. Note that, each time RECURSE.COM calls (invokes) itself, a //new local symbols table// (as evidenced from its values) is created and used, one local symbol table for each command procedure __depth__. * Line 13 -- Put a limit (of three) on how many times the script calls (recurses) itself. * Line 15 -- Local symbol ''callnumber'' is the current number of calls, one greater than the current procedure depth. * Line 17 -- The recursive call itself -- RECURSE.COM invokes itself, passing the current ''callnumber'' to the next invocation as command line parameter ''P1'', so you can see & track that value. * Lines 20 & 21 -- Once the recursion call limit (3) is reached, each lower invocation "returns" or exits, with a tracing text message so you can see it. The very first invocation returns to the DCL command line (depth = 0) when it exits. And here's the output/trace from invoking RECURSE.COM, invoked with an illustrative command line parameter ''P1'' -- see the above annotations as you read through this output/trace: $ @RECURSE "from com-line depth=0" *** == Global Symbol Table (part), always at Procedure Depth 0 *** $SEVERITY == "1" $STATUS == "%X00010001" ### = Local Symbol Table (entire) at Procedure Depth 1 ### P1 = "from com-line depth=0" P2 = "" P3 = "" P4 = "" P5 = "" P6 = "" P7 = "" P8 = "" PROCDEPTH = 1 Hex = 00000001 Octal = 00000000001 WSO = "WRITE sys$output" >>> Recursive call #2: @RECURSE 2 *** == Global Symbol Table (part), always at Procedure Depth 0 *** $SEVERITY == "1" $STATUS == "%X00010001" ### = Local Symbol Table (entire) at Procedure Depth 2 ### P1 = "2" P2 = "" P3 = "" P4 = "" P5 = "" P6 = "" P7 = "" P8 = "" PROCDEPTH = 2 Hex = 00000002 Octal = 00000000002 WSO = "WRITE sys$output" >>> Recursive call #3: @RECURSE 3 *** == Global Symbol Table (part), always at Procedure Depth 0 *** $SEVERITY == "1" $STATUS == "%X00010001" ### = Local Symbol Table (entire) at Procedure Depth 3 ### P1 = "3" P2 = "" P3 = "" P4 = "" P5 = "" P6 = "" P7 = "" P8 = "" PROCDEPTH = 3 Hex = 00000003 Octal = 00000000003 WSO = "WRITE sys$output" >>> exiting from recursion #3 >>> exiting from recursion #2 >>> exiting from recursion #1 $