The ReportScript language

1. General

The ReportScript language is used to write report scripts. Scripts are interpreted at run time by TScriptReport VCL component and used to preview and print reports. Although the language is developed to write database reports it can be used unrelated to databases. This paper defines semantics and syntax of the ReportScript language.

Report script consists of lines of plain text and ReportScript language operators. Operators are preceded by '\' sign. If a token is preceded by '\' it is considered as operator otherwise it is considered as text. Backslash '\' escapes '\'. Hash '#' sign is used to comment the text until end of line. The text formatted by operators is then splitted into lines, all spaces (spaces, tabs, newness) being considered to be a single space although some operators such as \EOL, \SPACE and others can override this.

The operator has the following syntax:
\ <name> [ <parameters> ] [ { <body> } ]

The body if any is enclosed in curly brackets. Example of operator FONT: \FONT NAME='Arial Cyr' SIZE=10 { This font is Arial Cyr, size 10 }. Operator and parameter names are not case-sensitive. Blanks are ignored.

There are 3 types of operators: text formatting, definition/evaluation and flow control. They are described below.

Script report can contain variables. Besides of using variable substitutions in the text, variables are used in arithmetic operations and as \FOR parameters. There are three types of variables: QUERY-variables, local variables and extern variables, each type having its own namespace. QUERY-variables specified in INTO clause get values by executing select SQL queries and are defined within \FOR corresponding to particular \QUERY only. They are undefined out of appropriate \FOR operator scope and can not be used as left part of \SET operator. Local variables are those defined by \SET operators. Once defined by \SET local variable can be used anywhere below this point in the report script. Extern variables are interfaces to your application. Any time TScriptReport encounteres extern variable it calls the function specified by ExtFunction event. Extern variables can be used anywhere in report script except in a left part of \SET operator.

To simplify all types and procedure calls are given in Pascal notation.
For detailed examples and sample database refer to EXAMPLES directory.

2. Operators summary

# | REM { block }
\A_SINGLE_CHAR
\SPACE
\SKIP N
\HSKIP N
\VSKIP N
\EOL | \ /
\NEWPAGE
\FONT [FACE [=]] 'Face' [SIZE [=]] N
\ITALIC | \IT
\BOLD | \BD
\NORMAL | \RM
\FONTSIZE [=] N | \SZ [=] N
\CENTER { block } | \! { block }
\RIGHT { block } | \> { block }
\LEFT { block } | \< { block }
\BOX [WIDTH[=]] W [BORDER[[=]N]] { block }
\TABLE [COLS[=]] W1[[,] W2 …] [BORDER[[=]N]] { block }
\TH { block }
\TR { block }
\TD { block }
\PAGE { block }
\PSIZE W H
\ORIENTATION PORTRAIT|LANDSCAPE
\TMARGIN [=]N
\BMARGIN [=]N
\LMARGIN [=]N
\RMARGIN [=]N
\PAGETOP { block }
\PAGEBOT { block }
\NOBREAK { block } | \BLANK { block }
\QUERY Name { Select-Sql-Query INTO [:]V1[[,] [:]V2 ... ] }
\SET LocalVarName = Expr
\% QUERY_VarName
\$ LocalVarName
\EXT VarName[, Expr]
\EVAL { EnhExpr }
\PAGENUM
\DATE
\FOR QueryName [PARAMS = Expr1 [[,] Expr2 … ]] { block }
\IF (BoolExpr) { block1 } [ELSE { block2 }]

3. Operators description

3.1. Text formatting

Operator: Comment until EOL or comment block
Syntax: #
Scope: Anywhere
Alternative: \REM { block }

All text from '#' (including it) until the end of line is ignored, like in UNIX shell scripts. All text within curly brackets of \REM is ignored.

Operator: Escape single character
Syntax: \A_SINGLE_CHAR
Parameters: A_SINGLE_CHAR - single character to escape
Scope: Anywhere

Places any single character into report. The character is placed as it is. This is the only way to place '\' and '#' into report (otherwise '\' is considered to be operator sign and '#' is considered to be comment sign).

Operator: Single space
Syntax: \SPACE
Scope: Anywhere

Places a single space into report (using current font) which is not ignored by ReportWriter. Use of \SKIP is recommended instead of \SPACE.

Operator: Relative horizontal skip
Syntax: \SKIP N
Parameters: N - relative offset in mm
Scope: Anywhere

Skips N mm from current position to the right.

Operator: Absolute horizontal skip
Syntax: \HSKIP N
Parameters: N - absolute offset in mm
Scope: Anywhere

Skips N mm from the edge of page to the right.

Operator: Relative vertical skip
Syntax: \VSKIP N
Parameters: N – relative offset in mm
Scope: Anywhere

Skips N mm down from the current position.

Operator: Line break
Syntax: \EOL
Scope: Anywhere
Alternative: \ /

Breaks current line. The text after \EOL would be placed at the beginning of next line.

Operator: Page break
Syntax: \NEWPAGE
Scope: Anywhere

Breaks current page. The text after \NEWPAGE would be placed at the beginning of next page.

Operator: Font definition
Syntax: \FONT [FACE [=]] 'Face' [SIZE [=]] N
Parameters: Face - face of the font, i.e. 'Arial', 'Times', etc; N - font size
Scope: Anywhere

Sets default font to the font specified. Example: \FONT FACE = 'Arial Cyr' SIZE=10.

Operator: Italic font
Syntax: \ITALIC
Scope: Anywhere
Alternative: \IT

Makes current font italic.

Operator: Bold font
Syntax:
Scope: Anywhere
Alternative: \BD

Makes current font bold.

Operator: Normal font
Syntax:
Scope: Anywhere
Alternative: \RM

Clears all styles (i.e. italic and bold) from current font.

Operator: Font size definition
Syntax: \FONTSIZE [=] N
Scope: Anywhere
Alternative: \SZ

Sets the size of current font to N (pixels).

Operator: Center alignment
Syntax: \CENTER { block }
Scope: Anywhere
Alternative: \!

Centers the block within current scope.

Operator: Right alignment
Syntax: \RIGHT { block }
Scope: Anywhere
Alternative: \>

Right-justifies the block within current scope.

Operator: Left alignment
Syntax: \LEFT { block }
Scope: Anywhere
Alternative: \<

Left-justifies the block within current scope.

Operator: Text box
Syntax: \BOX [WIDTH[=]] W [BORDER[[=]N]] { block }
Parameters: W = box width in mm; N = border width in pixels
Scope: Anywhere

Places the block within a box. If BORDER is not specified the box is not bordered. Otherwise if border width is not specified the default one of 1 pixel is used. Continuos boxes are drawn one after another, internal borders being drawn once.

Operator: Table
Syntax: \TABLE [COLS[=]] W1[[,] W2 ...] [BORDER[[=]N]] { block }
Parameters: W1, W2 - column widths in mm; N - border width in pixels
Scope: Anywhere

Places the block within a table. If BORDER is not specified cells within table are not bordered. Otherwise if border width is not specified the default one of 1 pixel is used. The block can contain as \TR as other operators. For instance sometimes it is useful to place \FOR operators and do calculations before \TR.
See also: \TH, \TR, \TD.

Operator: Table header
Syntax: \TH { block }
Scope: Allowed within \TABLE block only

Defines table header. The header is placed before the table. If the table does not fit in current page the header is placed on the beginning of every page the table fits in. The header and the table are separated by empty line.
See also: \TABLE, \TR, \TD.

Operator: Table row
Syntax: \TR { block }
Scope: Allowed within \TABLE block only

Defines table record (table row). The table is a set of records (rows) each consisting of a set of cells (columns). At present only tables MxN are supported. Incorrect number of columns specified by \TD are ignored when possible. The block can contain as \TD as other operators.
See also: \TABLE, \TH, \TD.

Operator: Table cell
Syntax: \TD { block }
Scope: Allowed within \TR block only

Defines table data (table cell).
See also: \TABLE, \TR

Operator: Page definition
Syntax: \PAGE { block }
Scope: Global

Defines and starts a new page.

Operator: Papre size definition
Syntax: \PSIZE W H
Parameters: W - width in mm; H - height in mm
Scope: Allowed within \PAGE block only

Defines page paper size. If \PSIZE is not specified the default size of 210x297 (A4) is used.

Operator: Page orientation definition
Syntax: \ORIENTATION PORTRAIT|LANDSCAPE
Parameters: self-explanatory
Scope: Allowed within \PAGE block only

Defines page paper orientation. Default is PORTRAIT. Sets Printer page orientation. Does not affect anything else. Should be preceded by PSIZE. Note that if \PSIZE is not specified the page is built as if it is A4 which is much like PORTRAIT ;) rather than LANDSCAPE.

Operator: Top margin
Syntax: \TMARGIN [=]N
Parameters: N - top margin in mm
Scope: Allowed within \PAGE block only

Defines page top margin. The default is 20mm.

Operator: Bottom margin
Syntax: \BMARGIN [=]N
Parameters: N - bottom margin in mm
Scope: Allowed within \PAGE block only

Defines page bottom margin. The default is 20mm.

Operator: Left margin
Syntax: \LMARGIN [=]N
Parameters: N - left margin in mm
Scope: Allowed within \PAGE block only

Defines page left margin. The default is 20mm.

Operator: Right margin
Syntax: \RMARGIN [=]N
Parameters: N - right margin in mm
Scope: Allowed within \PAGE block only

Defines page right margin. The default is 20mm.

Operator: Page header definition
Syntax: \PAGETOP { block }
Scope: Allowed within \PAGE block only

Defines page header.
See also: \PAGENUM, \DATE.

Operator: Page footer definition
Syntax: \PAGEBOT { block }
Scope: Allowed within \PAGE block only

Defines page footer.
See also: \PAGENUM, \DATE.

Operator: No-breaking block
Syntax: \NOBREAK { block }
Scope: Anywhere
Alternatives: \BLANK

Places all the text produced by the block on a single page if possible. If the block does not fit in the current page it is placed from the beginning of the next page.

3.2. Definition and evaluation operators

3.2.1. Expressions

Expressions referred to in Section 3.2 are defined by the following grammar:

Term = Number | QueryVar | LocalVar | Ext | Eval | "'" Text "'"
QueryVar = \% QUERY_VarName
LocalVar = \$ LocalVarName
Ext = \EXT ident
Eval = \EVAL { EnhExpr }

Expr = Term | ArithmExpr | Eval
ArithmExpr = Factor MulOp Factor | "(" ArithmExpr ")"
Factor = Term AddOp Term
MulOp = "*" | "/"
AddOp = "+" | "-"
BoolExpr = Expr Relation Expr
Relation = "<" | ">" | "<=" | ">=" | "<>"

Text case is not supported by \EVAL and \SET operators.

3.2.2. Operators

Operator: Query definition
Syntax: \QUERY Name { Select-Sql-Query INTO [:]V1[[,] [:]V2 ... ] }
Parameters: Name - report script internal name of the QUERY; V1, V2, ...- QUERY variables
Scope: Anywhere

Defines a query to use with \FOR operators. The body is a select-sql query followed by INTO clause. The number of variables should be the same as number of columns to select in sql query. At present variables are position-based, each of them corresponding to sql query column having the same position within sql query. If sql query has parameters they must be specified in \FOR. They are not checked during parsing \QUERY.
See also: \FOR, Section 1.
Example:
\QUERY QEmployees { SELECT FirstName, SecondName FROM Depts WHERE RowID = :Param0 INTO :FirstName, LastName }

Operator: Local variable definition/assignment
Syntax: \SET LocalVarName = Expr
Parameters: LocalVarName - name of local variable; Expr - expression defined in Section 3.2.1
Scope: Anywhere

Sets local variable LocalVarName value to Expr. If it is the first \SET for variable LocalVarName in the report script it is considered as variable declaration as well.
See also: \EVAL, \%, \$, Section 1.
Example: \SET x=1

Operator: QUERY variable substitution
Syntax: \% QUERY_VarName
Parameters: QUERY_VarName - name of QUERY variable
Scope: Within \FOR corresponding to \QUERY the variable belongs to

Substitutes the value for QUERY variable.
See also: \QUERY, \FOR, \EVAL, \$, Section 1.

Operator: Local variable substitution
Syntax: \$ LocalVarName
Parameters: LocalVarName - name of local variable
Scope: Anywhere (the variable must be defined)

Substitutes the value for local variable.
See also: \SET, \EVAL, \%, Section 1.

Operator: Extern variable substitution
Syntax: \EXT VarName[, Term]
Parameters: VarName - name of extern variable (to check in application C/C++/Pascal function); Term - data passed to application
Scope: Anywhere

Substitutes the value for extern variable.. This is the way to exchange data with application C/C++/Pascal functions. If ExtFunction event (design time published property) of TScriptReport instance is assigned, each time ReportWriter encounters \EXT it calls the function specified by ExtFunction property. The ExtFunction is defined by:

TExtfunction = function(ExtName: string; var Data: string; var MoreValues: boolean): boolean of object;
ExtFunction: TExtfunction;

ExtFunction is called with the following parameters: ExtName is filled by VarName, Data is filled by \EVAL { Expr }. Having executed ExtFunction \EXT returns one's Data to the report. MoreValues specifies if extern variable considered has more values. This case is considered in Section 3.2.3 in description of \FOR. At present ExtFunction return value (boolean) is ignored.

Example:
Consider TForm1.ScpiptReport1.ExtFunction = TForm1.ExtFunc and TForm1.ExtFunc is:

function TForm1.ExtFunc(ExtName: string; var Data: string; var MoreValues: boolean): boolean;
begin
   if LowerCase(ExtName) = 'hello' then begin { we were asked extern variable hello }
     Data := 'Hello, ' + Data + '!' { a new value of variable hello is defined }
     MoreValues := false;
   end;
end; { TForm1.ExtFunc }

Then consider we place the following in the report script:
\EXT hello, 'World' # so the report script would contain 'Hello, World!'.

See also: \SET, \EVAL, \%, Section 1, \FOR.

Operator: Expression evaluation
Syntax: \EVAL { Expr }
Parameters: Expr - expression to evaluate
Scope: Anywhere

Evaluates expression Expr and return data evaluated to the report.
See also: \SET, \EVAL, \%, \EXT, Section 1, Section 3.2.1.

Operator: Page number substitution
Syntax: \PAGENUM
Scope: Anywhere

\PAGENUM is substituted by current page number. Can be used in arithmetic expressions.

Operator: Current date substitution
Syntax: \DATE
Scope: Anywhere

\DATE is substituted by current date according to ShortDateFormat global variable. Cannot be used in arithmetic expressions.

3.3. Flow control operators

Operator: For-cycle
Syntax: \FOR QueryName [PARAMS = Term [[,] Term ... ]] { block }
Parameters: QueryName - QUERY name to open; Term(s) is(are) to fill sql query parameters
Scope: Anywhere

Fills QUERY parameters in positional order, opens the QUERY and executes the block for each resulting record. If there is no resulting record the block is not executed. Term1, Term2 ... are evaluated, resulting values being used as sql query parameters.

When using \EXT as Expr, each parameter in PARAMS clause is considered to be a multi-value extern variable. In this case MoreValues parameter of ExtFunction is used. \FOR opens the sql query and execute the block for each value of multi-value extern variable by calling ExtFunction appropriate number of times to obtain all values of variable, until MoreValues is set to false. If you use several multi-value extern variables \FOR will execute the block for each combination of their values, i.e. if Expr1 has N values and Expr2 has M values \FOR will execute the block N*M times.

Example:
It is a very common case when user is to choose a series of objects to build a report for these objects only. Using \FOR with multi-value extern variables is a good possibility to do that. Consider user chooses several employees into ListBox, database application filling ListBox by their RowIDs as Items.Objects[i] and names as Items.Strings[i]. To build a database report on chosen employees write TForm1.ExtFunction as following:

function TForm1.ExtFunc(ExtName: string; var Data: string; var MoreValues: boolean): boolean;
   const i: integer = 0;
begin
   MoreValues := false;
   if LowerCase() = 'emplist' then begin { we were asked extern variable EmpList }
     if (i < 0) or (i >= ListBox1.Count) then Data := '0'
     else with ListBox1 do begin
       Data := IntToStr(integer(Items.Objects[i]));
       inc(i);
       MoreValues := i < Count; { i = 0 .. Count - 1 }
     end;
   end else if LowerCase(ExtName) = 'seti' then begin { we were asked extern variable SetI }
     i := StrToInt(Data);
     Data := ''; { not to place anything into report }
   end;
end; { TForm1.ExtFunc }

Then place into report script the following:

\QUERY QEmployees { SELECT FirstName, LastName FROM Employees WHERE RowID = :v0 INTO FirstName, LastName }
\EXT SetI, 0
\FOR QEmployees PARAMS=\EXT EmpList {\%FirstName, \%LastName\EOL} # would place into report employees names chosen in TForm1.ListBox1.

See also: \QUERY, \%, \$, \EXT, \SET, Section 1, Section 3.2.1.

Operator: if-then-else
Syntax: \IF (BoolExpr) { block1 } [ELSE { block2 }]
Scope: Anywhere

Executes block1 if BoolExpr is true, executes block2 otherwise.
Example: \IF (\%XCount > 1) {\EVAL{\%XETC / \%XCount} / \%XETC} ELSE {\%XETC}
If XCount = 2 and XETC = 10, it would place into report the following string:
5/2
See also: Section 3.2.1.