Compiling, Linking, and Running Programs
You compile a RuleWorks program with the command RULEWORKS, which invokes the RuleWorks compiler to process the source code and verify that it contains no syntax errors or violations of the language semantics. You can compile only one source file with each RULEWORK command. If no errors occur, the compiler generates an intermediate C language file.
You can also direct the compiler to produce a listing file or an error file.
This chapter covers the following topics:
Compiling RuleWorks Programs
The source code for a RuleWorks program is contained in one or more ASCII text files. Each file must contain at least one complete block; you can put more than one block into a file, but you must not split a block between files. A RuleWorks program can consist of a single entry block or multiple entry, declaration, and rule blocks (see Dividing a Program into Blocks for details on compiling multiple blocks).
The syntax for the RULEWORKS command is shown below:
File-spec names the source file to be compiled into a C language file. The default file type for source files is RUL if none was supplied, and there is no period (.) in the file spec.
The default file name for most output files is the name of the source file, with an extension (or file type) that depends on the kind of output file being produced. Table 8-1 summarizes RuleWorks's file naming conventions.
Table 8-1. Filename Defaults
You can specify a different name by including a file-spec with the Error or Output qualifiers. For example, the following command generates the intermediate file CONFIG.C and the error file BUGS.ERR:
C:\>rulework config /err=bugs
The following example compiles the source file CONFIG.RUL and generates the intermediate file MY_CONFIG.C:
%rulework -output=my_config config
Qualifier specifies an instruction to the compiler. You can put qualifiers before or after the file specification(s). Table 8-3 shows all the valid qualifiers and values (defaults are in bold print).
Table 8-2 explains how to delimit RULEWORKS command qualifiers:
Table 8-2. How to Delimit RULEWORK Command Qualifiers
You can use either an equal sign (=) or a colon (:) between a qualifer and its value. You can shorten qualifier and value names to the smallest unique leading substring. Table 8-3 shows the full names and the abbreviations.
Note: The RuleWorks command line is common to all platforms, with the exception of the character used to delimit the qualifiers (see above). In other words, the qualifiers themselves are operating-system independent. Thus, the information in this guide is valid for RuleWorks on any platform.
However, some command-line features that you may expect on your operating system may not work with RuleWorks. For example:
Table 8-3. RuleWorks Compiler Qualifiers
The following sections explain how to use each qualifier.
Invoking the RuleWorks Command Interpreter (Debug) Invoking the <delayed>(<pname>) Command Interpreter (Debug)
RuleWorks includes language-specific debugging features that provide information, such as the contents of working memory and the conflict set, that is not accessible to your system debugger. To use the RuleWorks debugging features, compile your entry block(s) with the Debug qualifier set to YES. For example:
$rulework /debug=yes config
The RuleWorks run-time system invokes the command interpreter immediately after the ON-ENTRY actions (if any) of the first entry block that runs, before the first recognize-act cycle executes. At the interpreter prompt, you can give the debugging commands explained in Chapter 9.
If you want to use your system debugger but not the RuleWorks command interpreter, use the Debug qualifier set to MAYBE. This causes the generated C file to include the same information as for YES, but does not invoke the RuleWorks command interpreter automatically. You can invoke it from your system debugger by calling the API routine rul_debug, or by putting the DEBUG action in your source code.
After you've finished debugging your entry blocks, you can increase the speed of compilation and decrease the size of the generated C files by compiling without the Debug qualifier (or with Debug set to NO).
Saving Error Messages (Errors)
By default, messages from the RuleWorks compiler appear on your terminal only. You save them in a file instead by using the Errors qualifier when you compile. For example:
C:\>rulework config /err=bugs
If you specify the Errors qualifier with no value, the default is the source file name with the .ERR file type, in your current directory.
The Errors qualifier affects compile-time messages only. Use the DEFAULT command to redirect run-time messages.
Producing a Listing File (List)
A listing file is useful for debugging because it provides information about errors the compiler detects during compilation together with the source code listing. In interactive mode, the compiler produces a listing file only if you specify the /LIST qualifier. The default file specification for the listing file consists of the name of the RuleWorks source file with a LIS file type. For example, the following command causes the compiler to produce the listing file CONFIG.LIS:
System>rulework config /list
If you want to give the listing file a different name, use the /LIST qualifier with a file specification. For example, to compile the program CONFIG.RUL naming the listing file CONFIG_LIST.LIS, use the following command:
System>rulework config /list=config_list
In batch mode, the compiler produces a listing file by default. To suppress the listing file, use the /NOLIST qualifier.
Controlling the Case of C Function Names (Names)
All RuleWorks block names and external routine names become C function names and are visible to your linker. Because of the case-sensitivity of most C compilers, you may need to specify whether RuleWorks generates C function names in uppercase or lowercase.
The default is uppercase function names. If you need lowercase, use the Names qualifier. For example:
%<lcname> config -n
Optimizing RuleWorks (Optimize)
While a RuleWorks program is running, the match phase of the recognize-act cycle consumes the most CPU time. To reduce time spent matching rules to WMOs, RuleWorks uses extra memory to save partial instantiation and conflict set information between cycles. This allows the run-time system merely to update match information every cycle, rather than recreate it from scratch. (RuleWorks uses a variant of the RETE match algorithm; see Rule-based Programming with OPS5 for more information on writing rules efficiently.)
When an entry block exits, the memory used for its match information is normally freed. This means, however, that if the entry block is called again, then all match information must be recreated. This may have a serious impact on performance if you call the same entry block many times. Use the Optimize qualifier with the REINVOCATION value to keep match information in memory between calls to an entry block.
For example, assuming the entry block VERIFY is called repeatedly, the following command retains some memory but may greatly reduce execution time:
%rulework -opt=r verify
When deciding whether to use REINVOCATION, the determining factor is how much working memory, of classes visible to the called entry block, changes between the time that entry block returns and the time it is called again. If more WMOs stay the same than are created, changed, or deleted, then use REINVOCATION. Conversely, if more WMOs are created, changed, or deleted than are left the same, then you should compile the called entry block with Optimize left at the default, SPACE.
Naming the C File (Output)
To produce a C file with a different name from your source file, use the Ouput qualifier with a new file name. The following command compiles the source file VERIFY.RUL into the generated file CONFIG.C:
C:\>rulework /out=config verify
The default for output files is the same name as the source file, with the .C file type, in the current directory.
Suppressing the Copyright Notice (Quiet)
The RuleWorks compiler usually displays several lines of copyright and version each time it starts. To turn off this display, use the Quiet qualifier. For example:
%rulework config -q
Storing Declarations Separately (Usedirectory)
If you have declaration blocks that are shared by many entry blocks, you may find it convenient to keep their compiled declarations files (.USE, not .C) in a separate directory, perhaps one that is accessible to other people. To do this, first place the .USE files generated by compiling the declaration blocks in one directory. From then on, compile all files that contain either those declaration blocks, or entry blocks and rule blocks that use those declaration blocks, with the Usedirectory qualifier set to the appropriate location. For example:
C:\rules\work>rulework config -usedir=d:\rulework\project\decls
By default, .USE files are created in and read from the current directory (in this example, C:\rules\work). The Usedirectory qualifier resets the directory for .USE files (in this example, to D:\rulework\project\decls).
Compiling Generated C Files
RuleWorks runs on several hardware/operating system pairs, and supports several C compilers. The RuleWorks Release Notes list the supported C compilers and platforms. On Digital UNIX systems, you compile C files generated by RuleWorks just as you would your own C files. (See your C language documentation for details.)
On non-Digital UNIX systems, you must tell your C compiler where the RuleWorks include files are located (this applies both to generated files and to your own C files that call RuleWorks API routines). The syntax for this is shown below:
$ cc file-spec... /include_directory=rul$library:
On OpenVMS systems, you must also compile the generated C files using the default floating-point arithmetic. Table 8-4 shows which C command qualifiers are restricted.
Table 4-4. VAX C and DEC C Compiler Qualifiers
Linking RuleWorks Programs
You link compiled C files generated by RuleWorks the same as your own C files, but with the addition of the RuleWorks run-time library. (Check your C language and/or linker documentation for information on linking with your system debugger.) This section covers the following topics:
Linking with the RuleWorks Run-Time System
When you have successfully compiled all the modules of your program, you need to link them and the RuleWorks run-time system together to produce an executable file. This creates a program that can run on systems that do not have the RuleWorks run-time system installed. You do this with your linker's library option.
For example, the following commands link the modules PHONE.OBJ and PHONBOOK.OBJ with the RuleWorks object library.
Using DEC C on OpenVMS:
Using DEC C on Digital UNIX:
%c89 phone.o phonbook.o -lrulrtl
Using WATCOM on MS-DOS:
C:\>wcl386 phone.obj phonbook.obj \rulework\rul_rtlw.lib
There are three versions of the run-time library for Microsoft Windows: RUL_RTLW.LIB, RUL_RTLM.LIB, and RUL_RTLB.LIB. Use RUL_RTLM.LIB with a Microsoft C compiler, RUL_RTLB with the Borland C compiler, and substitute the appropriate command for WCL386 in the example above.
Linking External Routines
The procedure for compiling an external routine depends, of course, on the programming language in which the routine is written. See the appropriate language user's guide for instructions on how to compile an external routine; see Compiling RuleWorks Programs for instructions on how to compile RuleWorks modules.
Compiling an external routine creates an object file whose name you can include in the command you use to link your RuleWorks modules to produce an executable image. For example, if the compiled RuleWorks modules are STOCKINIT.OBJ and DOSTOCK.OBJ, and the external routine object file is STOCKSUB.OBJ, the link command on OpenVMS is:
This command links the RuleWorks object files with the object file STOCKSUB.OBJ created by another compiler and the RuleWorks run-time system. You can use your system debugger to debug external routines.
Effect of Declaration Blocks on Linking
Every time you compile a declaration block, the generated code (and the .USE compiled declarations file) includes a special generated time/date/name linkage point (that is, a global variable). When other blocks use that declaration block they include a reference to the specific variable named in the compiled declaration file.
These variable names are of the format:
The reason we do this is to guarantee that in a final image all the code that was generated based upon some set of declarations, was also linked based upon the exact same version of those declarations. If your build procedure fails to ensure this, you get unresolved linker references to symbols similar to those shown above. When you get such linker warnings, recompile each module that uses the specified declaration block. (Remember to specify the correct directory - see Storing Declarations Separately (Usedirectory))
If you are using project build utility, you can automate this checking by adding a dependency for each of your RuleWorks-generated .C files. These dependencies should state that the .C file depends not only on the .RUL file, but also on all the shared .USE files. See the section of this chapter, Sample Make File for a sample Wmake file.
Dividing a Program into Blocks
Many programs are too large and complex to be conveniently contained in one source file. In general, when a block is a non-trivial size, Digital recommends that you put each block into a separate file. You can put more than one block into one file, but you cannot split one block into more than one file. If a single block becomes too large to manage reasonably in a single file, you can divide it into multiple blocks of the appropriate types, each in its own file. See Chapter 5 for details on using RuleWorks block constructs.
There are two advantages to using multiple files: first, your development cycle (edit, compile, link, debug) can be faster because you may be able to edit and compile only one small file instead of the entire large program; and second, you can apply compiler qualifiers, especially Debug and Optimize, selectively to specific blocks.
Even if all your RuleWorks code fits into one file, you may want to split it into modular subsystems, each of which performs a specific task, or group of tasks, within the program. You can create subsystems as follows:
By separating class declarations into more than one declaration block, and choosing the arguments to the entry blocks carefully, you can select an appropriate information bandwidth between your subsystems. (See Chapter 5 for more details on private and shared data in RuleWorks.)
Note: If a declaration block is used by entry or rule blocks that are contained in more than one file, Digital recommends the following:
This makes debugging much easier. You may find it easiest simply to put every block in its own file.
Follow these steps when compiling multiple files:
1. RuleWorks compile all files that contain declaration blocks first.
If an entry or rule block uses a declaration block, you must compile the declaration block before you compile the entry or rule block. Inside an entry block or rule block, declarations must come before any executable statements.
Compiling a declaration block creates a file whose name is the first eight characters of the block name and whose type is .USE (for "used" declarations). The compiled file stores the following information:
2. RuleWorks compile files that do not contain any declaration blocks second.
You can compile entry and rule blocks in any order, even if the entry blocks call each other and activate the rule blocks. You must compile all the blocks before you link.
3. C compile all the generated files.
(Check the section titled, Compiling Generated C Files for restrictions.)
4. Link all the object files.
(See the section of this chapter titled Linking RuleWorks Programs for specific instructions.)
5. Run the executable, debug as needed, and repeat the procedure.
If you edit a declaration block, remember to recompile all the files that contain entry and rule blocks that use it, as well as the file that contains the declaration block itself.
If you edit an entry block, you should not need to recompile any file except the one that contains the entry block.
Example 8-1 shows these steps applied to a sample program called PHONES. This program consists of a main routine and two subroutines in C, three RuleWorks entry blocks, and one declaration block. Figure 8-1 illustrates the files used in the command sequence shown in the example, Modular Compilation.
Example 8-1. Modular Compilation
$rulework phonbook.rul (1)
$rulework phon_reg.rul (2)
$cc /incl=rul$library phone.c
$cc /incl=rul$library phonbook.c (3)
$cc /incl=rul$library phon_reg.c
$cc /incl=rul$library phonlook.c
$link phone,phonbook,phon_reg,phonlook,rul$library:rul_rtl/lib (4)
$run phone (5)
changes to entry block LOOKUP_PHONE_NUMBER in file PHONLOOK.RUL
$cc /incl=rul$library phonlook.c
$link phone,phonbook,phon_reg,phonlook,rul$library:rul_rtl/lib (6)
$run phone (7)
Key to the table and figure Modular Compilation:
(1)The PHONBOOK.RUL file contains the only declaration block, so it is RuleWorks compiled first.
(2)The PHON_REG.RUL and PHONLOOK.RUL files contain entry blocks and private declarations, but no declaration blocks. They are RuleWorks compiled after PHONBOOK.RUL.
The file PHONE.C contains all the original C language source code, including the main routine. Note that it is compiled with the RUL$LIBRARY logical (this syntax is for OpenVMS only).
(3)The generated files are all C compiled, again with the RUL$LIBRARY location specified.
(4)All modules are linked with the RuleWorks object library.
(5)The program is run (this syntax is OpenVMS only).
If an entry block or rule block contains errors, you can edit that block and recompile its file separately. However, if you edit a declaration block that is used by an entry block or rule block, you must recompile the file that contains the entry or rule block as well as the file that contains the declaration block.
In this example, after editing the entry block named LOOKUP_PHONE_NUMBER, which is contained in the file PHONLOOK.RUL, the only required recompilation is that of PHONLOOK.RUL
If the declaration block contained in file PHONBOOK.RUL were edited, all the RuleWorks files would have to be recompiled. See the section of this chapter, Effect of Declaration Blocks on Linking for information on linker errors that can result from mismatched .USE files.
Figure 8-1. Modular Compilation
If your program consists of many modules, you probably want to use a project build utility such as Make (or equivalents such as NMake, WMake, or MMS). These tools allow you to automate your system build such that the minimum recompilations are performed. They also ensure that all necessary recompilations are performed. in the example, Make File shows a sample Wmake file for the examples shipped in the RuleWorks kit.
Example 8-2. Make File
# Make file for building RuleWorks example programs using Watcom.
# Build with `wmake -f examples.mak'.
RULDIR = # Assume RuleWorks is in directory
RULECOMP = $(RULDIR) # Command to invoke RuleWorks compiler
RULRTL = $(RULDIR)_rtl.lib # Syntax for including run-time library in link
CC = wcl386 # Command to invoke C compiler
LINK = wcl386 # Command to invoke linker
CFLAGS = -c -I$(RULDIR) # compile only, .h file location
LINKFLAGS = -l=dos4g -k63000 # DOS extender, stack size
.rul.c : # Rule for RuleWorks to C (.rul to .c)
.c.obj : # Rule for compiling C program
$(CC) $(CFLAGS) $<
.obj.exe : # Rule for linking
$(LINK) $(LINKFLAGS) $< $(RULRTL)
all : count.exe advent.exe tourney.exe .symbolic
Running RuleWorks Programs
Running RuleWorks Programs
Once you have compiled and linked your program, run the executable file. This procedure is the same whether your main program is a RuleWorks entry block or a routine written in another language. For example, on OpenVMS systems:
or on UNIX:
or on MS-DOS:
When an entry block receives control, it invokes the RuleWorks run-time system. Depending on how the entry block was compiled, and whether it contains a DEBUG action, the run-time system may invoke the command interpreter:
Table 8-6. Run-time System
At the RuleWorks command interpreter, you run recognize-act cycles by entering the RUN command:
You can control the number of recognize-act cycles the run-time system executes by entering the RUN command with an integer. For example, to execute four recognize-act cycles, specify:
The integer refers to the global (to the program) rule-firing counter, not the local (to this invocation of the entry block) counter.
See Chapter 9 for information on the other commands available at the RuleWorks>prompt.
© RuleWorks.co.uk | | Sitemap