RPCL Overview #
The Datatypes #
C-compatible String #
RPCL is built around a basic datatype, a classical char
array with a maximum length
of RPCLWORDLEN
. Pushes and pops and all other operations are always ‘by value’. A RPCLWORD
is always 0-terminated internally, which directly allows typical C referencing as a string.
This is how it is declared:
typedef uint8_t RPCLWORD[RPCLWORDLEN];
Data with a length #
A second interpretation of a RPCLWORD
is data with a length. When interpreted as
a C-compatible string by a function that does not expect it, it just appears as a zero-length
string ""
. The available “payload” maximum buffer length is RCPLWORDLEN-4
to have 4 bytes
space to distinguish it internally.
With this data type RPCL is able to handle and compose raw networking packets
and all other raw binary data as long as the data fits into an area of RPCLWORDLEN-4
.
Data Subtypes #
Data may be generic or may carry a specific subtype, all corresponding C types are implicitly in host byte order as stored on the host system. This extension has been added to interface C functions properly handling endianess.
In the stack notation of an RPCL word the data subtype appears as in the second
column without the leading D
.
When printing a data subtype with .
, the conversion if known is indicated by underscoring
the output. If it’s not known or generic_t
, then a “hexdump” type of output is
displayed (as with .x
).
// bytes
#define Dgeneric_t 0 // * unspecified data
#define Dint8_t 1 // 1 int8_t
#define Duint8_t 2 // 1 uint8_t
#define Dint16_t 3 // 2 int16_t
#define Duint16_t 4 // 2 uint16_t
#define Dint32_t 5 // 4 int32_t
#define Duint32_t 6 // 4 uint32_t
#define Dint64_t 7 // 8 int64_t
#define Duint64_t 8 // 8 uint64_t
#define Dip_t 9 // 16 IP address
#define Dip4_t 10 // 4 IPv4 address
#define Diprange_t 11 // 32 IP address "from"-"to"
#define Dmac_t 12 // 6 MAC address
#define Dmacrange_t 13 // 12 MAC address "from"-"to"
Built-in RPCL words converting from a string to data with a subtype need to verify that an RPCLWORD converts successfully and that the converted result is within its expected range. On failure, the built-in word is required to raise an error (return RPCL_FAILURE).
The Dictionary #
The RPCL dictionary is implemented as a skiplist, where each entry holds a key, a value and an optional function pointer. Key and value are of size RPCLWORDLEN and always guaranteed to be 0 terminated, the value can also hold arbitrary data with a length being a 0-terminated string of strlen 0 at the same time.
UTF-8 / Unicode within the value field is supported, variable names are restricted to ASCII printable only, without double quotes, backslash and any whitespace. The variable name is resticted to a maximum length of 32 characters. The variable names are case sensitive.
An key-value entry in an RPCL dictionary belongs to one of these 3 classes:
- ordinary variables: A normal key-value pair held in the RPCL dictionary, accessible with
!
(store),@
fetch and removable withdelete
. - built-in words: A built-in word is additionally associated with a function pointer, the
value contains a brief description as specified with
RPCLRegister()
. A built-in word is immutable and therefore cannot be redefined or deleted from the RPCL command line. - compound words: A compound word holds the RPCL line as its value, wich is executed when the compound word is called. Compound words can be redefined and deleted.
The Stacks #
The Data Stack #
struct rpcl_stack {
char stack[RPCLSTACKDEPTH][RPCLWORDLEN];
int sp;
};
The Execution Stack #
The execution stack is represented by the underlying CPU stack, the interpreter keeps track of nested calls and signals an error when a threshold overflows.
The Stack Frame Delimiter #
A special object, the stack frame delimiter (SFD) allows to implement
- built-in functions which accept an arbitrary number of arguments
- and built-in functions, which return multiple results.
Comments #
Comments within a RPCL line are started with a #
.
Example:
$ rpclsh
ok 1 2 + . # just adding 1 and 2
3
ok "# This is not a comment" string ! # but this is
ok .d
string # This is not a comment
ok ▂
Double Quotes #
RPCL accepts strings enclosed in ASCII double quotes (0x22), nested double quotes can be specified with a preceeding backslash. Note that the Unicode-UTF-8 double quotes (0xe2 0x80 0x9d) are not recognized and do not work as it might be expected.
Example:
$ rpclsh
ok "This is a string containing also \\ and \"" .
This is a string containing also \ and "
ok "#" "#" strcat .
##
ok ▂
Forcing built-in/compound availability #
With an preceeding &
the remaining part of a token is forced to be available
as a built-in or compound word. This might be helpful to deal with misspellings,
which would be put on the stack as a value otherwise.
Another use case is to ensure the availability of a specific extension functionality registered by another component.
Example:
$ rpclsh
ok "3 +" addthree !!
ok 3 addthree .
6
ok 3 addthreee .
addthreee
ok 3 &addthreee .
unknown word "addthreee"
ERROR detected
ok ▂
True and False #
False is defined to be the empty string ""
, everything else is treated as logical
true.
Error Handling #
In case of an error (or when an error is raised) the execution stack is unwound
and the error message shown. When this happens during load
or load-if-present
,
the affected line number is additionally shown.
Loop Detection #
Endless loops are detected by an internal evaluation (or instruction) counter,
an “Evaluation threshold exceeded
” error
is signalled when a quite resonable threshold is exceeded.
Predefined Variables #
With RPCL itself, there are a few variables predefined. Other layers which embed RPCL are able to add their own set of predefined variables and compound words as desired.
Example:
$ rpclsh
ok .d
rpcl.platform Debian12-amd64
rpcl.version 1.137
ok ▂
Changing the Prompt #
rpcl.prompt
#
The variable rpcl.prompt
allows to change the prompt from ok
to something else.
This may be useful to be included in a machine specific configuration file of a RPCL controlled application, for example.
Example:
$ rpclsh
ok application@testmachine rpcl.prompt !
application@testmachine .d
rpcl.platform Debian12-amd64
rpcl.prompt application@testmachine
rpcl.version 1.139
application@testmachine ▂
rpcl.prompt.color
#
The variable rpcl.prompt.color
allows to set the color of the prompt. Possible
values are:
red
green
blue
yellow
- anything else: the terminal default color
If rpcl.prompt.color
is not set (undefined), the prompt color defaults to green
.
Example:
$ rpclsh
ok bngx@test rpcl.prompt !
bngx@test red rpcl.prompt.color !
bngx@test .d
rpcl.platform Debian12-amd64
rpcl.prompt bng@test
rpcl.prompt.color red
rpcl.version 1.139
bngx@test default rpcl.prompt.color !
bngx@test rpcl.prompt.color delete
bngx@test .d
rpcl.platform Debian12-amd64
rpcl.prompt bng@test
rpcl.version 1.139
bngx@test rpcl.prompt delete
ok ▂