Reverse Polish Configuration and Control Language

 

What is this? #

RPCL is a minimalistic configuration and control language, initially designed to act as a CLI for daemon-like background processes. RPCL is an acronym for Reverse Polish Configuration (and Control) Language.

It is working stack-oriented in reverse polish notation and therefore borrows some control words from FORTH.

RPCL offers some control structures and has the ability to define compound words - however, the scope and purpose of RPCL remains to configure and control something (and not to program).

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.

This is how it is declared:

typedef uint8_t RPCLWORD[RPCLWORDLEN];

Binary Data with a length #

A second interpretation of a RPCLWORD is binary 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.

Built-in RPCL words converting to another datatype internally 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 skip list, 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.

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 with delete.
  • 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 
platform                         Debian12-amd64
version.rpcl                     1.75
ok

Built-in Words #

The RPCL API #

Background Operation #