Main Content

Read Record Files with TLC

Tutorial Overview

Objective: Understand the structure of record files and learn how to parse them with TLC directives.

Open the Example:

openExample('simulinkcoder/AdviceAboutTLCTutorialsExample')
cd('tlctutorial/guide')

In this tutorial you interpret a simple file of structured records with a series of TLC scripts. You will learn how records are structured, and how TLC %assign and %<> token expansion directives are used to process them. In addition, the tutorial illustrates loops using %foreach, and scoping using %with.

The tutorial includes these steps, which you should follow sequentially:

  1. Structure of Record Files — Some background and a simple example

  2. Interpret Records — Presenting contents of the record file

  3. Anatomy of a TLC Script — Deconstructing the presentation

  4. Modify read-guide.tlc — Experiment with TLC

  5. Pass and Use a Parameter— Pass parameters from the command line to TLC files

  6. Review

Structure of Record Files

The code generator compiles models into a structured form called a record file, referred to as model.rtw. Such compiled model files are similar in syntax and organization to source model files, in that they contain a series of hierarchically nested records of the form

recordName {itemName itemValue}

Item names are alphabetic. Item values can be strings or numbers. Numeric values can be scalars, vectors, or matrices. Curly braces set off the contents of each record, which may contain one or more items, delimited by space, tab, or return characters.

In a model.rtw file, the top-level (first) record’s name is CompiledModel. Each block is represented by a subrecord within it, identified by the block’s name. TLC can parse well-formed record files, as this exercise illustrates.

The following listing is a valid record file that TLC can parse, although not one for which it can generate code. Comments are indicated by a pound sign (#):

# 
# File: guide.rtw	Illustrative record file, which can't be used by Simulink
#                                     Note: string values MUST be in quotes
Top {                               # Outermost Record, called Top
  Date         "21-Aug-2008"        # Name/Value pair named Top.Date
  Employee {                        # Nested record within the Top record
    FirstName  "Arthur"             #   Alpha field Top.Employee.FirstName
    LastName   "Dent"               #   Alpha field Top.Employee.LastName
    Overhead    1.78                #   Numeric field Top.Employee.Overhead
    PayRate     11.50               #   Numeric field Top.Employee.PayRate
    GrossRate   0.0                 #   Numeric Field Top.Employee.GrossRate
  }                                 # End of Employee record
  NumProject   3                    # Indicates length of following list
  Project {                         # First list item, called Top.Project[0]
    Name       "Tea"                #   Alpha field Name, Top.Project[0].Name
    Difficulty 3                    #   Numeric field Top.Project[0].Difficulty
  }                                 # End of first list item
  Project {                         # Second list item, called Top.Project[1]
    Name       "Gillian"            #   Alpha field Name, Top.Project[1].Name
    Difficulty 8                    #   Numeric field Top.Project[1].Difficulty
  }                                 # End of second list item
  Project {                         # Third list item, called Top.Project[2]
    Name       "Zaphod"             #   Alpha field Name, Top.Project[2].Name
    Difficulty 10                   #   Numeric field Top.Project[2].Difficulty
  }                                 # End of third list item
}                                   # End of Top record and of file

As long as programmers know the names of records and fields, and their expected contents, they can compose TLC statements to read, parse, and manipulate record file data.

Interpret Records

Here is the output from a TLC program script that reads guide.rtw, interprets its records, manipulates field data, and formats descriptions, which are directed to the MATLAB® Command Window:

Using TLC you can:
* Directly access a field's value, e.g.
  %<Top.Date> -- evaluates to:
  "21-Aug-2008"
  
* Assign contents of a field to a variable, e.g.
  "%assign worker = Top.Employee.FirstName"
  worker expands to Top.Employee.FirstName = "Arthur"

* Concatenate string values, e.g.
  "%assign worker = worker + " " + Top.Employee.LastName"
  worker expands to worker + " " + Top.Employee.LastName = "Arthur Dent"
  
* Perform arithmetic operations, e.g.
  "%assign wageCost = Top.Employee.PayRate * Top.Employee.Overhead"
  wageCost expands to Top.Employee.PayRate * Top.Employee.Overhead <- 11.5 * 1.78 = 20.47
  
* Put variables into a field, e.g.
  Top.Employee.GrossRate starts at 0.0
  "%assign Top.Employee.GrossRate = wageCost"
  Top.Employee.GrossRate expands to wageCost = 20.47

* Index lists of values, e.g.
  "%assign projects = Top.Project[0].Name + ", " + Top.Project[1].Name..."
  "+ ", " + Top.Project[2].Name"
  projects expands to Top.Project[0].Name + ", " + Top.Project[1].Name
  + ", " + Top.Project[2].Name = Tea, Gillian, Zaphod

* Traverse and manipulate list data via loops, e.g.
  - At top of Loop, Project = Tea; Difficulty = 3
  - Bottom of Loop, i = 0; diffSum = 3.0
  - At top of Loop, Project = Gillian; Difficulty = 8
  - Bottom of Loop, i = 1; diffSum = 11.0
  - At top of Loop, Project = Zaphod; Difficulty = 10
  - Bottom of Loop, i = 2; diffSum = 21.0
  Average Project Difficulty expands to diffSum / Top.NumProject = 21.0 / 3 = 7.0

This output from guide.rtw was produced by invoking TLC from the MATLAB Command Window, executing a script called read-guide.tlc. Do this yourself now, by following these steps:

  1. In MATLAB, change folder (cd) to your copy of tlctutorial/guide within your working folder.

  2. To produce the output just listed, process guide.rtw with the TLC script read-guide.tlc by typing the following command:

    tlc -v -r guide.rtw read-guide.tlc

Note command usage:

  • The -r switch (for read) identifies the input data file, in this case guide.rtw.

  • The TLC script handling the data file is specified by the last token typed.

  • The -v switch (for verbose) directs output to the command window, unless the TLC file handles this itself.

Anatomy of a TLC Script

You now dissect the script you just ran. Each “paragraph” of output from guide.tlc is discussed in sequence in the following brief sections:

Coding Conventions

These are some basic TLC syntax and coding conventions:

%% Comment TLC comment, which is not output
/* comment */ Comment, to be output
%keyword TLC directive (keyword), start with “%
%<expr>TLC token operator
. (period) Scoping operator, for example, Top.Lev2.Lev3
... (at end-of-line) Statement continuation (line break is not output)
\ (at end-of-line)Statement continuation (line break is output)
localvarIdentifier Local variables start in lowercase
GlobalvarIdentifier Global variables start in uppercase
RecordIdentifier Record identifiers start in uppercase
EXISTS() TLC built-in functions are named in uppercase
Note: TLC identifiers are case-sensitive.

For further information, see TLC Coding Conventions.

File Header

The file read-guide.tlc begins with:

%% File: read-guide.tlc   (This line is a TLC Comment, and will not print)
%% 
%% To execute this file, type: tlc -v -r guide.rtw read-guide.tlc
%% Set format for displaying real values (default is "EXPONENTIAL")
%realformat "CONCISE"
  • Lines 1 through 4 — Text on a line following the characters %% is treated as a comment (ignored, not interpreted or output).

  • Line 5 — As explained in the text of the fourth line, is the TLC directive (keyword) %realformat, which controls how subsequent floating-point numbers are formatted when displayed in output. Here we want to minimize the digits displayed.

Token Expansion

The first section of output is produced by the script lines:

Using TLC you can:
* Directly access a field's value, e.g.
%assign td = "%" + "<Top.Date>"
  %<td> -- evaluates to:
  "%<Top.Date>"
  • Lines 1 and 2 — (and lines that contain no TLC directives or tokens) are simply echoed to the output stream, including leading and trailing spaces.

  • Line 3 — Creates a variable named td and assigns the string value %<Top.Date> to it. The %assign directive creates new and modifies existing variables. Its general syntax is:

    %assign ::variable = expression
    The optional double colon prefix specifies that the variable being assigned to is a global variable. In its absence, TLC creates or modifies a local variable in the current scope.

  • Line 4 — Displays

    %<Top.Date> -- evaluates to:
    The preceding line enables TLC to print %<Top.Date> without expanding it. It constructs the string by pasting together two literals.
    %assign td = "%" + "<Top.Date>"
    As discussed in String Processing Plus, the plus operator concatenates strings as and adds numbers, vectors, matrices, and records.

  • Line 5 — Evaluates (expands) the record Top.Date. More precisely, it evaluates the field Date which exists in scope Top. The syntax %<expr> causes expression expr (which can be a record, a variable, or a function) to be evaluated. This operation is sometimes referred to as an eval.

Note

You cannot nest the %<expr> operator (that is, %<foo%<bar>> is not allowed).

Note

When you use the %<expr> operator within quotation marks, for example, "%<Top.Date>", TLC expands the expression and then encloses the result in quotation marks. However, placing %assign within quotation marks, for example, "%assign foo = 3", simply echoes the statement enclosed in quotation marks to the output stream. No assignment results (the value of foo remains unchanged or undefined).

General Assignment

The second section of output is produced by the script lines:

* Assign contents of a field to a variable, e.g.
%assign worker = Top.Employee.FirstName
  "%assign worker = Top.Employee.FirstName"
  worker expands to Top.Employee.FirstName = %<worker>
  • Line 1 — Echoed to output.

  • Line 2 — An assignment of field FirstName in the Top.Employee record scope to a new local variable called worker.

  • Line 3 — Repeats the previous statement, producing output by enclosing it in quotation marks.

  • Line 4 — Explains the following assignment and illustrates the token expansion. The token %<worker> expands to Arthur.

String Processing Plus

The next section of the script illustrates string concatenation, one of the uses of the “+” operator:

* Concatenate string values, e.g.
%assign worker = worker + " " + Top.Employee.LastName
  "%assign worker = worker + " " + Top.Employee.LastName"
  worker expands to worker + " " + Top.Employee.LastName = "%<worker>"
  • Line 1 — Echoed to output.

  • Line 2 — Performs the concatenation.

  • Line 3 — Echoes line 2 to the output.

  • Line 4 — Describes the operation, in which a variable is concatenated to a field separated by a space character. An alternative way to do this, without using the + operator, is

    %assign worker = "%<Top.Employee.FirstName> %<Top.Employee.LastName>"
    The alternative method uses evals of fields and is equally efficient.

The + operator, which is associative, also works for numeric types, vectors, matrices, and records:

  • Numeric Types — Add two expressions together; both operands must be numeric. For example:

    * Numeric Type example, e.g.
      Top.Employee.PayRate = %<Top.Employee.PayRate>
      Top.Employee.Overhead = %<Top.Employee.Overhead>
    %assign td = Top.Employee.PayRate + Top.Employee.GrossRate
      td = Top.Employee.PayRate + Top.Employee.Overhead
      td evaluates to %<td>
    Output:
    * Numeric Type example, e.g.
      Top.Employee.PayRate = 11.5
      Top.Employee.Overhead = 1.78
      td = Top.Employee.PayRate + Top.Employee.Overhead
      td evaluates to 13.28

  • Vectors — If the first argument is a vector and the second is a scalar value, TLC appends the scalar value to the vector. For example:

    * Vector example, e.g.
    %assign v1 = [0, 1, 2, 3]
      v1 is %<v1>
    %assign tp1d = Top.Project[1].Difficulty
      Top.Project[1].Difficulty is %<tp1d>
    %assign v2 = v1 + tp1d
      v2 = v1 + Top.Project[1].Difficulty
      v2 evaluates to: %<v2>
    Output:
    * Vector example, e.g.
      v1 is [0, 1, 2, 3]
      Top.Project[1].Difficulty is 8
      v2 = v1 + Top.Project[1].Difficulty
      v2 evaluates to: [0, 1, 2, 3, 8]

  • Matrices — If the first argument is a matrix and the second is a vector of the same column-width as the matrix, TLC appends the vector as another row to the matrix. For example:

    * Matrices example, e.g.
    %assign mx1 = [[4, 5, 6, 7]; [8, 9, 10, 11]]
      mx1 is %<mx1>
      v1 is %<v1>
    %assign mx = mx1 + v1
      mx = mx1 + v1
      mx evaluates to %<mx>
    Output:
    * Matrices example, e.g.
      mx1 is [ [4, 5, 6, 7]; [8, 9, 10, 11] ]
      v1 is [0, 1, 2, 3]
      mx = mx1 + v1
      mx evaluates to [ [4, 5, 6, 7]; [8, 9, 10, 11]; [0, 1, 2, 3] ]

  • Records — If the first argument is a record, TLC adds the second argument as a parameter identifier (with its current value). For example:

    * Record example, e.g.
    %assign StartDate = "August 28, 2008"
      StartDate is %<StartDate>
    %assign tsd = Top + StartDate
      Top + StartDate
      Top.StartDate evaluates to %<Top.StartDate>
    Output:
    * Record example, e.g.
      StartDate is August 28, 2008 
      Top + StartDate
      Top.StartDate evaluates to August 28, 2008

Arithmetic Operations

TLC provides a full complement of arithmetic operators for numeric data. In the next portion of our TLC script, two numeric fields are multiplied:

* Perform arithmetic operations, e.g.
%assign wageCost = Top.Employee.PayRate * Top.Employee.Overhead
  "%assign wageCost = Top.Employee.PayRate * Top.Employee.Overhead"
  wageCost expands to Top.Employee.PayRate * Top.Employee.Overhead ...
 <- %<Top.Employee.PayRate> * %<Top.Employee.Overhead> = %<wageCost>
  • Line 1 — Echoed to output.

  • Line 2 — %assign statement that computes the value, which TLC stores in local variable wageCost.

  • Line 3 — Echoes the operation in line 2.

  • Lines 4 and 5 — Compose a single statement. The ellipsis (typed as three consecutive periods, for example, ... ) signals that a statement is continued on the following line, but if the statement has output, TLC does not insert a line break. To continue a statement and insert a line break, replace the ellipsis with a backslash (\).

Modify Records

Once read into memory, you can modify and manipulate records just like variables you create by assignment. The next segment of read-guide.tlc replaces the value of record field Top.Employee.GrossRate:

* Put variables into a field, e.g.
%assign Top.Employee.GrossRate = wageCost
  "%assign Top.Employee.GrossRate = wageCost"
  Top.Employee.GrossRate expands to wageCost = %<Top.Employee.GrossRate>

Such changes to records are nonpersistent (because record files are inputs to TLC; other file types, such as C source code, are outputs), but can be useful.

You can use several TLC directives besides %assign to modify records:

%createrecordCreates new top-level records, and might also specify subrecords within them, including name/value pairs.
%addtorecordAdds fields to an existing record. The new fields can be name/value pairs or aliases to existing records.
%mergerecordCombines one or more records. The first record contains itself plus copies of the other records' contents specified by the command, in sequence.
%copyrecordCreates a new record as %createrecord does, except the components of the record come from the existing record you specify.
%undef varRemoves (deletes) var (a variable or a record) from scope. If var is a field in a record, TLC removes the field from the record. If var is a record array (list), TLC removes the first element of the array; the remaining elements remain accessible. You can remove only records you create with %createrecord or %copyrecord.

See Target Language Compiler Directives for details on these directives.

Index Lists

Record files can contain lists, or sequences of records having the same identifier. Our example contains a list of three records identified as Project within the Top scope. List references are indexed, numbered from 0, in the order in which they appear in the record file. Here is TLC code that compiles data from the Name field of the Project list:

* Index lists of values, e.g.
%assign projects = Top.Project[0].Name + ", " + Top.Project[1].Name...
+ ", " + Top.Project[2].Name
  "%assign projects = Top.Project[0].Name + ", " + Top.Project[1].Name..."
  "+ ", " + Top.Project[2].Name"
  projects expands to Top.Project[0].Name + ", " + Top.Project[1].Name
  + ", " + Top.Project[2].Name = %<projects>

The Scope.Record[n].Field syntax is similar to that used in C to reference elements in an array of structures.

While explicit indexing, such as the above, is perfectly acceptable, it is often preferable to use a loop construct when traversing entire lists, as shown in Loop Over Lists.

Loop Over Lists

By convention, the section of a record file that a list occupies is preceded by a record that indicates how many list elements are present. In model.rtw files, such parameters are declared as NumIdent, where Ident is the identifier used for records in the list that follows. In guide.rtw, the Project list looks like this:

  NumProject    3                   # Indicates length of following list
  Project {                         # First list item, called Top.Project[0]
    Name       "Tea"                #   Alpha field Name, Top.Project[0].Name
    Difficulty  3                   #   Numeric field Top.Project[0].Difficulty
  }                                 # End of first list item
  Project {                         # Second list item, called Top.Project[1]
    Name       "Gillian"            #   Alpha field Name, Top.Project[1].Name
    Difficulty  8                   #   Numeric field Top.Project[1].Difficulty
  }                                 # End of second list item
  Project {                         # Third list item, called Top.Project[2]
    Name       "Zaphod"             #   Alpha field Name, Top.Project[2].Name
    Difficulty  10                  #   Numeric field Top.Project[2].Difficulty
  }                                 # End of third list item

Thus, the value of NumProject describes how many Project records occur.

Note

model.rtw files might also contain records that start with Num but are not list-size parameters. TLC does not require that list size parameters start with Num. Therefore you need to be cautious when interpreting NumIdent record identifiers. The built-in TLC function SIZE() can determine the number of records in a specified scope, hence the length of a list.

The last segment of read-guide.tlc uses a %foreach loop, controlled by the NumProject parameter, to iterate the Project list and manipulate its values.

* Traverse and manipulate list data via loops, e.g.
%assign diffSum = 0.0
%foreach i = Top.NumProject
  - At top of Loop, Project = %<Top.Project[i].Name>; Difficulty =...
 %<Top.Project[i].Difficulty>
  %assign diffSum = diffSum + Top.Project[i].Difficulty
  - Bottom of Loop, i = %<i>; diffSum = %<diffSum>
%endforeach
%assign avgDiff = diffSum / Top.NumProject
  Average Project Difficulty expands to diffSum / Top.NumProject = %<diffSum> ...
 / %<Top.NumProject> = %<avgDiff>

As you may recall, the TLC output looks like this:

* Traverse and manipulate list data via loops, e.g.
- At top of Loop, Project = Tea; Difficulty = 3
  - Bottom of Loop, i = 0; diffSum = 3.0
  - At top of Loop, Project = Gillian; Difficulty = 8
  - Bottom of Loop, i = 1; diffSum = 11.0
  - At top of Loop, Project = Zaphod; Difficulty = 10
  - Bottom of Loop, i = 2; diffSum = 21.0
Average Project Difficulty expands to diffSum / Top.NumProjects = 21.0 / 3 = 7.0
After initializing the summation variable diffSum, a %foreach loop is entered, with variable i declared as the loop counter, iterating up to NumProject. The scope of the loop is all statements encountered until the corresponding %endforeach is reached (%foreach loops may be nested).

Note

Loop iterations implicitly start at zero and range to one less than the index that specifies the upper bound. The loop index is local to the loop body.

Modify read-guide.tlc

Now that you have studied read-guide.tlc, it is time to modify it. This exercise introduces two important TLC facilities, file control and scoping control. You implement both within the read-guide.tlc script.

File Control Basics

TLC scripts almost invariably produce output in the form of streams of characters. Output is normally directed to one or more buffers and files, collectively called streams. So far, you have directed output from read-guide.tlc to the MATLAB Command Window because you included the -v switch on the command line. Prove this by omitting -v when you run read-guide.tlc. Type

tlc -r guide.rtw read-guide.tlc

Nothing appears to happen. In fact, the script was executed, but output was directed to a null device (sometimes called the “bit bucket”).

There is one active output file, even if it is null. To specify, open, and close files, use the following TLC directives:

%openfile streamid ="filename" , "mode"
%closefile streamid
%selectfile streamid

If you do not give a file name, subsequent output flows to the memory buffer named by streamid. If you do not specify a mode, TLC opens the file for writing and deletes any existing content (subject to system-level file protection mechanisms). Valid mode identifiers are a (append) and w (write, the default). Enclose these characters in quotes.

The %openfile directive creates a file/buffer (in w mode), or opens an existing one (in a mode). Note the required equals sign for file specification. Multiple streams can be open for writing, but only one can be active at one time. To switch output streams, use the %selectfile directive. You do not need to close files until you are done with them.

The default output stream, which you can respecify with the stream ID NULL_FILE, is null. Another built-in stream is STDOUT. When activated using %selectfile, STDOUT directs output to the MATLAB Command Window.

Note

The streams NULL_FILE and STDOUT are always open. Specifying them with %openfile generates errors. Use %selectfile to activate them.

The directive %closefile closes the current output file or buffer. Until an %openfile or a %selectfile directive is encountered, output goes to the previously opened stream (or, if none exists, to null). Use %selectfile to designate an open stream for reading or writing. In practice, many TLC scripts write pieces of output data to separate buffers, which are then selected in a sequence and their contents spooled to one or more files.

Implement Output File Control

In your tlctutorial/guide folder, find the file read-guide-file-src.tlc. The supplied version of this file contains comments and three lines of text added. Edit this file to implement output file control, as follows:

  1. Open read-guide-file-src.tlc in your text editor.

  2. Save the file as read-guide-file.tlc.

  3. Note five comment lines that begin with %% ->.

    Under each of these comments, insert a TLC directive as indicated.

  4. Save the edited file as read-guide-file.tlc.

  5. Execute read-guide-file.tlc with the following command:

    tlc -r guide.rtw read-guide-file.tlc

    If you succeeded, TLC creates the file guidetext.txt which contains the expected output, and the MATLAB Command Window displays

    *** Output being directed to file: guidetext.txt
    *** We're almost done . . .
    *** Processing completed.

If you did not see these messages, or if a text file was not produced, review the material and try again. If problems persist, inspect read-guide-file.tlc in the guide/solutions subfolder to see how you should specify file control.

Scope Basics

Structure of Record Files explains the hierarchical organization of records. Each record exists within a scope defined by the records in which it is nested. The example file, guide.rtw, contains the following scopes:

Top
Top.Employee
Top.Project[0]
Top.Project[1]
Top.Project[2]

To refer to a field or a record, specify its scoping, even if no other context that contains the identifier exists. For example, in guide.rtw, the field FirstName exists only in the scope Top.Employee. You must refer to it as Top.Employee.FirstName whenever accessing it.

When models present scopes that are deeply nested, this can lead to extremely long identifiers that are tedious and error prone to type. For example:

CompiledModel.BlockOutputs.BlockOutput.ReusedBlockOutput

This identifier has a scope that is long and has similar item names that you could easily enter incorrectly.

The %with/%endwith directive eases the burden of coding TLC scripts and clarifies their flow of control. The syntax is

%with RecordName
  [TLC statements]
%endwith

Every %with is eventually followed by an %endwith, and these pairs might be nested (but not overlapping). If RecordName is below the top level, you need not include the top-level scope in its description. For example, to make the current scope of guide.rtw Top.Employee, you can specify

%with Employee
  [TLC statements]
%endwith

Naturally, %with Top.Employee is also valid syntax. Once bracketed by %with/%endwith, record identifiers in TLC statements do not require you to specify their outer scope. However, note the following conditions :

  • You can access records outside of the current %with scope, but you must qualify them fully (for example, using record name and fields).

  • Whenever you make assignments to records inside a %with directive, you must qualify them fully.

Change Scope Using %with

In the last segment of this exercise, you modify the TLC script by adding a %with/%endwith directive. You also need to edit record identifier names (but not those of local variables) to account for the changes of scope resulting from the %with directives.

  1. Open the TLC script read-guide-scope-src.tlc in the text editor.

  2. Save the file as read-guide-scope.tlc.

  3. Note comment lines that commence with %% ->.

    Under each of these comments, insert a TLC directive or modify statements already present, as indicated.

  4. Save the edited file as read-guide-scope.tlc.

  5. Execute read-guide-scope.tlc with the following command:

    tlc -v -r guide.rtw read-guide-scope.tlc

The output should be exactly the same as from read-guide.tlc, except possibly for white space that you might have introduced by indenting sections of code inside %with/%endwith or by eliminating blank lines.

Fully specifying a scope inside a %with context is not an error, it is simply unnecessary. However, failing to fully specify its scope when assigning it to a record (for example, %assign GrossRate = wageCost) is invalid.

If errors result from running the script, review the discussion of scoping above and edit read-guide-scope.tlc to eliminate them. As a last resort, inspect read-guide-scope.tlc in the /solutions subfolder to see how you should have handled scoping in this exercise.

For additional information, see Scopes in the model.rtw File and Variable Scoping.

Pass and Use a Parameter

You can use the TLC commands and built-in functions to pass parameters from the command line to the TLC file being executed. The most general command switch is -a, which assigns arbitrary variables. For example:

tlc -r input.rtw -avar=1 -afoo="abc" vars.tlc

The result of passing this pair of strings via -a is the same as declaring and initializing local variables in the file being executed (here, vars.tlc). For example:

%assign var = 1
%assign foo = "abc"

You do not need to declare such variables in the TLC file, and they are available for use when set with -a. However, errors result if the code assigns undeclared variables that you do not specify with the -a switch when invoking the file. Also note that (in contrast to the -r switch) a space should not separate -a from the parameter you are declaring.

In the final section of this tutorial, you use the built-in function GET_COMMAND_SWITCH() to print the name of the record file being used in the TLC script, and provide a parameter to control whether or not the code is suppressed. By default the code is executed, but is suppressed if the command line contains -alist=0:

  1. Open the TLC script read-guide-param-src.tlc in your text editor.

  2. Save the file as read-guide-param.tlc.

  3. To enable your program to access the input filename from the command line, do the following:

    1. Below the line %selectfile STDOUT, add the line:

      %assign inputfile = GET_COMMAND_SWITCH ("r")

      The %assign directive declares and sets variables. In this instance, it holds a string filename identifier. GET_COMMAND_SWITCH() returns whatever string argument follows a specified TLC command switch. You must use UPPERCASE for built-in function names.

    2. Change the line “*** WORKING WITH RECORDFILE” to read as follows:

      *** WORKING WITH RECORDFILE %<inputfile>
  4. To control whether or not a section of TLC code is executed, do the following:

    1. Below the line “%assign inputfile = GET_COMMAND_SWITCH ("r")”, add:

      %if (!EXISTS(list))
      	%assign list = 1
      %endif
      The program checks whether a list parameter has been declared, via the intrinsic (built-in) function EXISTS(). If no list variable exists, the program assigns one. This defines list and by default its value is TRUE.

    2. Enclose lines of code within an %if block.

      %if (list)
          * Assign contents of a field to a variable, e.g.
            %assign worker = FirstName
            "%assign worker = FirstName"
            worker expands to FirstName = %<worker>
      %endif

      Now the code to assign worker is sent to the output only when list is TRUE.

    3. Save read-guide-param.tlc.

  5. Execute read-guide-param.tlc and examine the output, using the command

    tlc -r guide.rtw read-guide-param.tlc

    This yields

    *** WORKING WITH RECORDFILE [guide.rtw]
    * Assign contents of a field to a variable, e.g.
          "%assign worker = FirstName"
          worker expands to FirstName = Arthur
    ***END
  6. Execute read-guide-param.tlc with the command:

    tlc -r guide.rtw -alist=0 read-guide-param.tlc

    With the -alist=0 switch, the output displays only the information outside of the if statement.

    *** WORKING WITH RECORDFILE [guide.rtw]
    ***END

Review

The preceding exercises examined the structure of record files, and expanded on how to use TLC directives. The following TLC directives are commonly used in TLC scripts (see Target Language Compiler Directives for detailed descriptions):

%addincludepathEnable TLC to find included files.
%addtorecordAdd fields to existing record. New fields can be name/value pairs or aliases to existing records.
%assignCreate or modify variables.
%copyrecordCreate new record and, if applicable, specify subrecords within them, including name/value pairs. The components of the record come from the existing record specified.
%createrecordCreate new top-level records and, if applicable, specify subrecords within them, including name/value pairs.
%foreach/%endforeachIterate loop variable from 0 to upper limit.
%if/%endifControl whether code is executed, as in C.
%includeInsert one file into another, as in C.
%mergerecordCombine one or more records. The first record contains itself plus copies of the other records contents specified by the command, in sequence.
%selectfileDirect outputs to a stream or file.
%undef varRemove (delete) var (a variable or a record) from scope. If var is a field in a record, TLC removes the field from the record. If var is a record array (list), TLC removes the first element of the array; the remaining elements remain accessible. Only records created via %createrecord or %copyrecord can be removed.
%with/%endwithAdd scope to simplify referencing blocks.

Related Topics