Defining Rules
Importable
functions are written in JavaScript and involve a file with one export, that being an object that contains the function along with some other information. The name of the file is how you can import them from within a simkey
program.
Before we get to how to import and use them, we will discuss how to create them in the first place.
First, note that there are two types of Importable
functions. One of them is Boolean
functions, for use within @if
statements. These take only one argument without any formatting behind it, and they then return TRUE
or FALSE
.
The other, more important and versatile one, is regular Importable
functions which can take various types of arguments and either return nothing, or return code to be added directly to the compiled KeyC
. (Check this part of the "What is Simkey?" page for more info on KEYC
)
There's also another similar topic, which is a CALL.BEFORE
function, that cannot be called within the the code of a simkey
program. Instead, it automatically gets called before the parsing stage is done to allow pre-processing for frameworks.
Regular Importable
functions
We will be going step by step, with how to define the parameters for use in simkey programs, what parameters you will actually have in the exported JavaScript functions, and how to take blocks, as well as what you can do with these blocks.
Exported Object
In order to define an Importable
function, you need to export an object which contains some vital information. It is formatted like this:
...
module.exports = {
FUNCTION: ...,
TAKES: {
PARAMS: ...,
BLOCK: ...,
DONT_PARSE_BLOCK: ...
}
}
BLOCK
and DONT_PARSE_BLOCK
are both optional. BLOCK
allows you to take a block (like the { ... }
part of the @if
statements), and is by default false.
DONT_PARSE_BLOCK
tells the simkey compiler whether or not to parse the block the same way everything else is parsed, or simply hand the tokens within the block over to the Importable
function. It is by default false.
Function Parameters
As seen above, there's a PARAMS
key nested within the exported object. It is a string that defines what type of arguments you can receive, and the number of arguments. The types are BOOL
, STR
, LOOSE
, VECTOR
, and NUM
.
LOOSE
just means that everything at a certain point would be interpreted as a STR except without the need to put quotation marks "
around it. This also means it must come last in the PARAMS
if you choose to use it.
The rest are relatively straightforward and can receive literal values or those from variables, except for STR
, because there is no variable that can hold a string value.
You can also have union types and can make some arguments optional. Here's an example putting this all into effect:
module.exports = {
FUNCTION: test,
TAKES: {
PARAMS: "[NUM, VECTOR, BOOL | STR, NUM:OPTIONAL]",
BLOCK: ...,
DONT_PARSE_BLOCK: ...
}
}
There are square brackets on the outside, and each argument is separated by a comma and has a type which it must conform to. Here's some examples of calling this function (called "test"):
# Assuming it is imported (will discuss later) #
<VECTORS>
$my_vector = 0,0,0,0
<MACRO>
# Different valid calls that can be made #
@test [5, $my_vector, TRUE, 5]
@test [5, $my_vector, "this is a STR (string)", 5]
@test [5, $my_vector, FALSE]
Putting :OPTIONAL
right after a type, with no space, makes it so that the argument is not required. Everything that comes after it will also optional as a consequence (even if you don't write :OPTIONAL
for them).
For unions you simply use |
. If one of the types in the union is optional everything other type in the union is automatically (again, the whole argument is optional, not just a specific type).
If your function takes no parameters, both an empty string and "[]" work. You still must have PARAMS
defined. When calling it in a Simkey
program, no brackets for arguments are needed.
Taking a Block
To take a block, you have to modify the exported object's BLOCK
attribute, and maybe the DONT_PARSE_BLOCK
attribute. These are both boolean values, and as mentioned above, DONT_PARSE_BLOCK
tells the compiler whether to parse the tokens in the block before giving it to the function.
It is false by default, and you may typically not need it to be true, so setting BLOCK
to true is the important part:
module.exports = {
FUNCTION: test,
TAKES: {
PARAMS: "[NUM]",
BLOCK: true
}
}
A call to the function would look like this:
# Assuming it is imported (will discuss later) #
<MACRO>
@test [5] {
# This code will be parsed like any code outside #
}
Actual JavaScript Parameters
The actual JavaScript function receives the arguments in this order: INFO (an object with some important information from the compiler), BLOCK (if you choose to have a block for your function), and the arguments defined by PARAMS
in that order.
For function right above with the block, here's how that might work:
function test(INFO, BLOCK, myNum) {
// You can return nothing, or code to be added straight
// to the `KeyC`
}
module.exports = {
FUNCTION: test,
TAKES: {
PARAMS: "[NUM]",
BLOCK: true
}
}
Boolean Importable
functions
The way to convey to the compiler that a particular Importable
function is a boolean one, is by setting the PARAMS
attribute to [CONDITION-EXPRESSION]
. It cannot take a BLOCK
, so that will be the only relevant attribute within the TAKES
object.
Unlike the other Importable
functions, they must return true or false. They can also only be used in a condition (like within an @if
statement). They still receive the INFO
object, as before, but only one more argument, which is the expression.
Here's a straightforward example of defining, as well as using:
function is_even(INFO, number) {
// This number is just a string, as these functions get "LOOSE" arguments
const parsed_num = parseFloat(number)
if (number % 2 == 0) {
return true
}
else {
// Either not even or not a number
return false
}
}
module.exports = {
FUNCTION: is_even,
TAKES: {
PARAMS: "[CONDITION-EXPRESSION]"
}
}
# Assuming it is imported (will discuss later) #
<MACRO>
@if @is_even[6] {
# This code runs #
}
@else {
# This code does not run #
}
CALL.BEFORE functions
These functions get called before the parsing gets done, with the simkey
script as a string, the list of tokens, and the "model" object used for parsing (there are keys for each section, and each has their own rules).
There's no object to export that is formatted the same way as before, you simply export the function and you receive those arguments in that order. You can use it to pre-process for the sake of making things like frameworks.