Skip to content

Foreign Function Interface

Michael Wang edited this page Aug 6, 2022 · 8 revisions

cish's foreign function interface is more akin to a strange concoction of high-level opcode assembly. Generally speaking, there will be wrapper functions implemented via cish's standard library. This documentation only applies to those who are developing foreign functions, those looking to modify cish, and most likely not you, a high-level developer.

The Syntax

SuperForth Foreign functions are composed of the foreign keyword, followed by a value representing the foreign funcion's opcode, followed by a operand value. No typechecking occurs duirng this phase, except for the foreign function opcode expression. Beware of too many automatic definitions to the degree in which type inference becomes impossible. when they are used with foreign. For example:

int output_var = foreign[123]("michael")

or conversley with a variable foreign id

int lib_offset = foreign[17]("./lib.so");
foriegn[lib_offset + 9](100); $invokes behavior from lib_offset

would find function number 123, which may have a different functionality depending on how you modify cish, send in the operand value "michael", and write the output into output_var. Errors may occur because, as mentioned before foreign functions don't perform any meaningful type checking. The function will carry out its purpose regardless of whether the input values are correct.

However, if an foreign call has no input, it must have no output. Foreign call may always have inputs regardless of whether it has an output. Foreigns undergo the least amount of enforcement, so be sure to read/use foreigns correctly as described by their authors documentation.

Standard Library Foreign OpCode Table

This table details the opcodes on a standard, unmodified cish implementation. If you're using a modified version of cish, consult it's developers before directly accessing foreign functions. Otherwise, it's best to use the wrapper functions they provide.

On Every Cish Platform

OpCode Expected Operand Value Expected Output Value Functionality
0 int float Converts a int to float
1 float int Gets the floor of a float.
2 float int Gets the ciel of a float.
3 float int Rounds a float.
4 float array<char> Converts a float to a string.
5 array<char> float Converts a string to a float.
6 int array<char> Converts an int to a string.
7 array<char> int Converts a string to an int.
8 char None Prints a character to stdout.
9 None char Reads a character, returns '\0' if waiting for more input.
10 None int/float Generates a random int, or float.
11 float float Returns the sin of a radian angle.
12 float float Returns the cos of a radian angle.
13 float float Returns the tan of a radian angle.
14 int char Converts an int to it's equivalent ASCII char.
15 char int Converts a ASCII char to its int representation.
16 None int Returns the UNIX time.
17 int None Sleeps for a given amount of milliseconds.
18 int array<any> Reallocs an array - output register is also used as input.

On the Cish Interpreter...

OpCode Expected Operand Value Expected Output Value Functionality
19 array<char> int Loads a library, imports it's foreigns, and returns it's library offset
20 int array<int> Initializes an array full of zero-values. Works for char, int, and bool.

On the Cish Compiler (with -robomode or -vex)...

OpCode Expected Operand Value Expected Output Value Functionality
19 None int Returns the competition mode
20 array<char> None Logs a message to the robot and stdout.
21 int None Selects a Vex Port.
22 int None Gets a motor encoding at a port.
23 int* None Configures a motors gearset.
24 int* None Configures motor encoding units for motor.
25 bool* None Configures motor in reverse mode or not.
26 int None Gets the RPM of a motor.
27 int None Sets a motors encoding position to zero.

The asterisk means a motor port must be selected prior to calling the said foreign function, via foreign function 21, in addition to the requested operand.

The Philosophy

The cish Foreign Function interface designed to interop with native-C code, especially for things that cannot be done by cish: for example interoperating with c to provide low-level functionality like matrix multiplication at higher speeds, or interacting with system components. Often times, the FFI serves as a bridge between an existing c-library and cish

As such, because the FFI is one of multiple layers (the other being the cish compatible DLL, or some builtin standard functionality) in-between the metaphorical bridge, the average high-level user shouldn't be exposed to such lower-level, implementation details on a frequent basis.

Because of the low level of exposure between the average user and the FFI directly, the FFI's syntax is designed to be as simple to implement, understand, and maintain as possible. Only allowing for one or zero inputs, and a potential output drastically reduces cish's implementation complexity.

cish doesn't perform any type-checking on foreign functions chiefly because it is expected that the developer of any said library would provide a cish wrapper function around any ffi-functionality. This shifts the development overhead towards the library creator, but is consistent with the cish's principle of keeping it simple for high-level users. These wrappers would provide much more flexibility, potentially much more than any type-checked foreign function possibly could.

The odd variable-numerical way of addressing foreign functions at an index stems from the desire to keep cish's implementation relatively simple and easy to maintain. It also provides a fairly large degree of dynamism and flexibility for importing multiple libraries and interacting with native plugins.