xHarbour Reference Documentation > Preprocessor Reference |
User defined command or translation rule for the preprocessor.
#command <searchPattern> => <resultPattern> #translate <searchPattern> => <resultPattern>
The directives #command and #translate specify translation rules for the preprocessor. Source code is modified in a compilation cycle according to these rules by the preprocessor. The modified source code is then processed by the compiler. To obtain the result of the preprocessor, the compiler switch /p or /pt must be used. This causes the translation result of the preprocessor be output to a file with the PPO extension (PPO stands for PreProcessed Output).
Both directives provide a powerful automated transformation tool, that allow for extending the xHarbour language with user-defined commands, pseudo functions, and for source code patterns to be simplified by the programmer and later expanded to verbose expressions with the aid of the preprocessor.
While #command is used to define a complete statement, #translate instructs the preprocessor to find even portions of statements. Both directives identify keywords only up to the first four characters and are not case-sensitive. To match keywords to their entire length, the directives #xcommand or #xtranslate must be used (note the 'x' prefix).
Directives are usually programmed in #include files having the CH extension. The rules they define for translating source code are applied according to their preference and sequence in which they appear in an include file. The directive with highest preference is #define, followed by #translate/#xtranslate and #command/#xcommand. That means, #define directives are processed first until no more #define matches are found. In a next step, #translate/#xtranslate rules are applied and finally #command/#xcommand is processed. This process is recurring, so that each transformation forces a re-scan of the whole new line, starting again with #define directives.
The sequence for processing directives is determined by their order of definition in the program code. The most recently specified directive overrides/hides any prior directive that would have otherwise matched the same input.
Because directives defined at the end of a file are processed prior to those at the beginning of a file, when a set of rules depend one on the output of the other, the first rule to expand, should be listed last.
This is important to note when having to use multiple rules in order to implement user-defined commands that have various syntactical forms or use mutual exclusive options, thus requiring multiple rules. The most general form of a user-defined command must appear towards the top of an #include file, while specialized forms must be placed below (towards the end of a file). This makes sure that spezialized forms of a user-defined command are given the opportunity to match first, before the more general rules are evaluated.
When a directive results in a pattern of program code for which another directive is defined, the translation procedure is repeated until all translation directives are resolved by the preprocessor. This allows for defining complex translation rules where a particualer sequence of program code is translated in multiple intermediate steps using different directives.
Directives may be defined directly within a given source files, or within any of the files included into the compiled source file, by means of the #include directive.
The xHarbour compiler includes a set of pre-loaded standard rules, compatible with the CA-Clipper 'std.ch' file. You may choose to exclude these default rules, by using the compiler -u command line switch. You may also "inject" a set of rules from any command header file, into the compliation of any source, by means of the -u+<filename> command line switch. This will load such command header file as if it was directly included in the compiled source, by means of the #include directive.
When the compilation of one PRG file is complete, all translation rules valid for this file are discarded and the preprocessor operates with a new set of translation rules for the next PRG file.
Translation rules
The <searchPattern> and <resultPattern> portions of a translation rule are syntactically programmed using literal tokens, words and markers. A directive must begin with a word or a literal token, it cannot begin with a marker.
Markers are a place holders in a translation rule for variable portions of patterns in the program code to translate. They can be compared to memory variables that hold character strings during the translation process. Markers have symbolic names and are always enclosed in angled brackets.
+------ marker --------+--------+ | | | #command SET FILTER TO <xpr> => DbSetFilter( <{xpr}>, <"xpr"> ) | | | | | | +----+----+---- words -----+ +- literal token --+
This translation rule instructs the preprocessor to search for the words SET FILTER TO and match the following expression with the marker <xpr>. This marker has the symbolic name xpr and is included in the resulting code in two syntactically different ways. The words SET FILTER TO are completely removed and replaced with DbSetFilter().
The symbolic name for markers must follow the same rules as for memory variables, i.e. it must begin with an alphabetic character or an underscore, followed by alphabetic characters, digits or underscores. Unlike symbolic variable names, symbolic names for markers are case sensitive.
Markers on the left side of the => characters are referred to as Match markers, while those on the right side are called Result markers. There is a set of Match markers to distinguish syntactically different patterns of program code to search for, and a set of Result markers that define syntactically different forms of how the matched input string should appear in the output.
The large number of possible combinations of different Match and Result markers makes the preprocesser a very flexible tool for translating almost any kind of PRG source code.
Match marker
Match markers are used in the <searchPattern> part of a directive and instruct the preprocessor about the pattern to match in the PRG code. The following table lists the available Match markers:
Match Markers
Syntax | Name |
---|---|
<marker> | Regular match marker |
<marker,...> | List match marker |
<marker:word list,...> | Restricted match marker |
<*marker*> | Wild match marker |
<(marker)> | Extended Expression match marker |
<!marker!> | Single token match marker |
Regular match marker: This marker is the most commonly used one. It matches any valid expression in the program code.
List match marker: Expressions in program code that are comma separated lists are matched with the List match marker. It is used when a command has a variable number of arguments separated with commas.
Restricted match marker: This marker limits the number of words or literals valid for a command option. One or more words valid for an option are included following a colon after the symbolic name of the marker. Multiple words must be separated with commas. The words are matched case-insensitive.
Wild match marker: The Wild match marker matches everything from the current position to the end of a statement. This includes input text that may not be a valid expression.
Extended expression match marker: This marker is used for command arguments that may or may not be enclosed in parentheses.
Single token match marker: This marker identifies single tokens appearing adjacent in an expression. It is required to match words and literal tokens when they are not separated with blank spaces.
Optional clauses: It is possible to define optional clauses for <searchPattern> to match program code with optional arguments or keywords that may be absent. Optional clauses for <searchPattern> must be enclosed in square brackets [] and can be nested. They are matched in program code irrespective of the order they appear in a search rule and a user defined command. That means, if the search rule defines optional clauses in the order A,B,C and they are used in PRG code in C,A,B order, there will still be a match for all three optional clauses.
Adjacent optional clauses in a search rule must consist either of a keyword or a keyword/literal token plus Match marker. They cannot contain only Match markers without keyword or literal token.
Escape character: When the characters [ and < are part of the PRG code to match, they must be escaped in the search rule by prepending a backslash \.
Line continuation: When the definition of a search pattern spans across multiple lines, each line must end with a semicolon. The ; serves as line continuation character.
Result marker
A result marker receives the input text matched by the Match marker having the same symbolic name. The input text is written to the output text at the place where the Result marker appears in the <resultPattern> portion of a translation directive. Different Result markers allow for modifying the syntactical notation written to the output. The following table lists the available Result markers:
Result Markers
Syntax | Name |
---|---|
<marker> | Regular result marker |
#<marker> | Dumb stringify result marker |
<"marker"> | Normal stringify result marker |
<(marker)> | Smart stringify result marker |
<{marker}> | Blockify result marker |
<.marker.> | Logify result marker |
Regular result marker: This marker is the most general one. It receives matched input and writes it unmodified to the output. If there is no match in the search, nothing is written to the output.
Dumb stringify result marker: This marker encloses matched input in quotes and writes a literal character string to the output. If there is no match in the search, a null string ("") is written to the output. If the input is matched with the List match marker, the entire list is enclosed in quotes, not individual list elements.
Normal stringify result marker: This marker encloses matched input in quotes and writes a literal character string to the output. If there is no match in the search, nothing is written to the output. If the input is matched with the List match marker, each individual list element is written as a quoted character string to the output.
Smart stringify result marker: This marker encloses matched input only in quotes when it is not enclosd in parentheses. If the input is enclosed in parentheses, it is written unmodified to the output. When the matched input is a macro expression, it is optimized by the Smart stringify result marker.
Blockify result marker: This marker embeds matched input in code block literals {|| }. The resulting code block has no parameters but contains only the matched expression. If the input is matched with the List match marker, each element of the list is output as an individual code block.
Logify result marker: The marker writes literal logical values to the output. If there is a match, the output is .T. (true), otherwise .F. (false).
Optional clauses: Optional clauses for <resultPattern> must be enclosed in square brackets []. Unlike optional clauses for <searchPattern> the ones for <resultPattern> cannot be nested. If an optional clause is matched multiple times in <searchPattern> it is output as many times in <resultPattern>.
Escape character: When the characters [ and < are part of the PRG code to output, they must be escaped in the result rule by prepending a backslash \.
Line continuation: When the definition of a result pattern spans across multiple lines, each line must end with a semicolon. The ; serves as line continuation character. If the output should be created as multiple statements, use two ;; semicolons to separate each statement.
Examples for preprocessor directives
The examples below demonstrate various combinations of Match and Result markers as they are used in the definition of preprocessor directives. Note that each example has three sections: the directive (CH file), the match pattern (PRG file) and the resulting output (PPO file).
See also: | #define, #include, #uncommand | #untranslate, #xcommand | #xtranslate |
Category: | Preprocessor directives |
Regular match marker => Regular result marker
// CH file #translate IsNegative(<num>) => (<num> \< 0) // PRG file IF IsNegative( nValue ) <statements> ENDIF // PPO file IF (nValue < 0) <statements> ENDIF
List match marker => Regular result marker
// CH file #command ? [<list,...>] => QOut( <list> ) // PRG file ? "Today is", Date(), "!" // PPO file QOut("Today is",Date(),"!" )
Restricted match marker => Smart stringify result marker
// CH file #command SET DELETED <x:ON,OFF,&> => Set( _SET_DELETED, <(x)> ) // PRG file cVar := "ON" SET DELETED OFF SET DELETED &cVar // PPO file cVar := "ON" Set(11,"OFF" ) Set(11,cVar )
Restricted match marker => Logify result marker
// CH file #command SET ALTERNATE TO <(file)> [<add: ADDITIVE>] => ; Set( _SET_ALTFILE, <(file)>, <.add.> ) // PRG file SET ALTERNATE TO test.log SET ALTERNATE TO test.log ADDITIVE // PPO file Set(19,"test.log",.F. ) Set(19,"test.log",.T. )
List match marker => Smart stringify result marker
// CH file #command COPY STRUCTURE [TO <(file)>] [FIELDS <fields,...>] => ; __dbCopyStruct( <(file)>, { <(fields)> } ) // PRG file COPY STRUCTURE TO Temp.dbf FIELDS Lastname, Firstname cDbFile := "Customer.dbf" cFName1 := "Firstname" cFName2 := "Lastname" COPY STRUCTURE TO (cDbFile) FIELDS (cFname1), (cFname2) // PPO file __dbCopyStruct("Temp.dbf",{ "Lastname","Firstname" } ) cDbFile := "Customer.dbf" cFName1 := "Firstname" cFName2 := "Lastname" __dbCopyStruct((cDbFile),{ (cFname1),(cFname2) } )
Regular match marker => Blockify result marker
// CH file #command SET FILTER TO <xpr> => DbSetFilter( <{xpr}>, <"xpr"> ) // PRG file SET FILTER TO FIELD->City = "New York" // PPO file DbSetFilter({||FIELD->City = "New York"},'FIELD->City = "New York"' )
Wild match marker => Smart stringify result marker
// CH file #command SET PATH TO <*path*> => Set( _SET_PATH, <(path)> ) // PRG file cTemp := "C:\temp" SET PATH TO c:\xhb\source\samples SET PATH TO (cTemp) // PPO file cTemp := "C:\temp" Set(6,"c:\xhb\source\samples" ) Set(6,(cTemp) )
Single token match marker => Regular result marker
// CH file #translate := <!const!> <op:?> <a>,<b> => ; := IIF( .NOT. Empty(<const>), <a>, <b>) // PRG file lLogic := (Val(Time()) < 12) ? c := lLogic?"am", "pm" // PPO file lLogic := (Val(Time()) < 12) QOut(c := IIF(.NOT. Empty(lLogic),"am","pm") )
Optional match clauses => Multiple statements
// CH file #command REPLACE <fld1> WITH <val1> ; [, <fldN> WITH <valN> ] => ; <fld1> := <val1> ; [; <fldN> := <valN>] // PRG file REPLACE FIELD->LastName WITH "Miller" REPLACE FIELD->LastName WITH "Miller", ; FIELD->FirstName WITH "John" // PPO file FIELD->LastName := "Miller" FIELD->LastName := "Miller" ; FIELD->FirstName := "John"
http://www.xHarbour.com