Running_cRexx - adesutherland/CREXX GitHub Wiki

Running cRexx entails compiling

All Rexx scripts you run with cRexx are compiled by rxc into Rexx assembler code (.rxas) and then assembled into an .rxbin file, which contains the Rexx bytecode for execution by the Rexx Virtual Machine rxvm. This rxvm executable takes care of linking separately compiled modules together and executing them, so one function can find another.

A first program - Hello World

Let's say you have a Rexx exec you would like to run. To not have any surprises, it is of the hello world kind. We have a file called hello.rexx, containing:

/* rexx */
options levelb
say 'hello cRexx world!'

When cRexx level 'C' (for 'Classic') is available, the 'options levelb' (on line 2) statement can be left out; for the moment, level B is all we have, and the compiler will refuse to compile without it.

With all our cRexx executables on the PATH, we only need to do:

rxc hello
rxas hello
rxvm hello

to see 'hello cRexx world!' on the console.

It might be a good idea to make a shell script to execute these three programs in succession, and perhaps call it 'crexx'. But take into account that this really is a very simple case, in which no built-in functions are called. You can look into the generated rexx assembler (hello.rxas) file:

/*
 * cREXX COMPILER VERSION : cREXX F0043
 * SOURCE                 : hello
 * BUILT                  : 2022-12-03 22:27:52
 */

.srcfile="hello"
.globals=0

main() .locals=1 .expose=hello.main
   .meta "hello.main"="b" ".int" main() "" ""
   .src 3:1="say 'hello cRexx world!'"
   say "hello cRexx world!"
   .src 4:1="return 0"
   ret 0

and you can see here that the compiler actually has generated a 'say' assembler instruction for the Rexx 'say' instruction. (Assembler became a whole lot easier with Rexx assembler.) But we did not yet call any function.

Using built-in functions

Most Rexx programs use the extremely well designed built-in functions. Now with these functions written in Rexx, and not hidden in the compiler somewhere, we must tell it to import those from the library where we put them earlier during the build process. Let's say we want to add a display of the current weekday to our hello program. This will now be:

/* rexx */
options levelb
import rxfnsb
say 'hello cRexx world!'
say 'today is' date('w')
return 0

Never mind the import statement, which you will not need when cRexx 'Classic' level C is available. But in level B, we need this, because we need the flexibility it affords our plans for the future.

We must tell the compiler where to find the signature of the date() function, so it can check if we call it in the correct way, with the right parameters. This is done with the -i switch, which points to the directory containing the library - which is called 'library', by the way.

rxc -i ~/crexx-build/lib/rxfns hello

The assembler runs unchanged, because it trusts the compiler to have checked if the called function really sits in that library, and has the right parameters - the right code to call it has been generated.

rxas hello

To run it, we can employ the rxvme executable - this one is extended with linked-in versions of all the functions in the library:

rxvme hello

which yields:

hello cRexx world!
today is Saturday

Building a standalone executable

To end this short tour of running cRexx, we are taking that last example and will build a standalone executable out of it. If your neighbour, family member or sports club runs the same OS as you do, they can now run your compiled Rexx program from a USB stick, without ever installing anything.

For this, we need the next set of commands, expressed as a Rexx exec:

/* rexx compile a rexx exec to a native executable */
/* Classic Rexx and NetRexx compatible             */
crexx_home='~/crexx-build'
if arg='' then do
  say 'exec name expected.'
  exit 99
end
parse arg execName'.'extension
if extension<>'' then say 'filename extension ignored.'
'rxc  -i' crexx_home'/lib/rxfns' execName
'rxas' execName
'rxcpack' execName crexx_home'/lib/rxfns/library'
'gcc -o' execName,
'-lrxvml -lmachine -lavl_tree -lplatform -lm -L',
crexx_home'/interpreter -L'crexx_home'/machine -L',
crexx_home'/avl_tree -L'crexx_home'/platform'  execName'.c'

This exec is delivered in the source tree, bin directory. At the moment it can be run by classic rexx interpreters and NetRexx, it will soon be runnable by cRexx itself. The exec works by compiling the Rexx program specified (again without the .rexx file extension) to an .rxbin Rexx bytecode file, which is then serialized to a C source file, containing the cRexx Virtual Machine and the library rxbin files, by the rxcpack command. It is a rather peculiar looking C source, but nevertheless it will compile to a working executable, which is done by the last step in the exec, here using the gcc compiler. And you will be able to run it without the overhead of checking, compiling, tokenizing to bytecode and linking, so it will be quite fast:

hello cRexx world!
today is Saturday
./hello  0.00s user 0.00s system 61% cpu 0.009 total
⚠️ **GitHub.com Fallback** ⚠️