-
Notifications
You must be signed in to change notification settings - Fork 1
Description
this is a complex feature that requires joint support from both the CLEO runtime & compilers.
See also Functions RFC
Idea
Be able to export SCM functions from CLEO scripts and import them in other scripts.
Goals
- Have modular code, library code can be updated independently from the main code
- third-party utils can be written in edit modes different from the main script or even in different languages
- DRY (Don't repeat yourself)
- Improve development velocity
- help with migrating from legacy modes to the new ones (custom->SBL/SRC)
Design
Export
CLEO scripts could export pure SCM functions. In the sense of this document a pure function is the one that only depends on its inputs. Functions are exported using the export keyword:
:fun1
...
:fun2
...
:fun3
...
export @fun1
export @fun2
export @fun3
Export line can be anywhere in the script. The label must mark an SCM function start. Duplicate entries are allowed:
export @fun1
export @fun1
By default exported functions get named after the label name (e.g. fun1, fun2). This name is important as another script uses it for the import. To give the export a custom name, as keyword can be used:
export @fun1 as matrix_mult
It should not be possible to name different functions with the same export name:
export @fun1 as matrix_mult
export @fun2 as matrix_mult
If @fun1 and @fun2 mark different locations, it is a hard compilation error. Otherwise import of 'matrix_mult' would be ambiguous.
Import
A script can import functions using the import keyword.
import fun1 from "scripts.s"
cleo_call fun1 3 1 2 3
This is a syntactic sugar for:
cleo_call "fun1@scripts.s" 3 1 2 3
@ separates the function name that is invoked and the file name.
A label used in the import statement can not be used in a script as a regular label and vice versa. Otherwise the call destination is ambiguous.
Note that this is not relevant if the function syntax is implemented (see below)
import fun1 from "scripts.s"
cleo_call fun1 3 1 2 3
:fun1 // error because :fun1 is an external label
:fun1
...
import fun1 from "scripts.s" // error because :fun1 is a local label
cleo_call fun1 3 1 2 3
Import can also use as keyword to avoid name collisions:
import @fun1 as matrix_mult from "scripts.s"
Duplicate imports are allowed:
import fun1 from "scripts.s"
import fun1 from "scripts.s"
Duplicate aliases are not allowed:
import fun1 as fun1 from "scripts.s"
import fun2 as fun1 from "scripts.s" // error
Import names are case-insensitive. import FUN1 and import fun1 are equivalent.
Import statement can be used anywhere before cleo_call, preferably at the top of the script.
Path separator can be either \\ or /. The runtime normalizes them.
import fun1 from "folder\\scripts.s"
import fun1 from "folder/scripts.s"
Because a single \ serves as an escape character, double \\ is required.
CLEO Library support
Changes have to be made to cleo_call and cleo_return commands.
If the first argument to cleo_call is a string, CLEO treats as an import path (NEW).
If a string is given, CLEO resolves the path similarly to 0A92, 0A94. See the post down below for the path resolution algorithm.
Once the path is resolved and the file is found, it gets loaded into the game process and the pointer is obtained (P). If the file is already loaded, the P is returned immediately.
Then cleo_call saves all current lvars, gosub stack and base IP (NEW).
Then the base IP of the current script is set to P (NEW).
Then as with any SCM function local variables are reset and input arguments are passed.
Then CLEO finds the export table. The runtime uses jump offsets to find the section with id 01 (see Future extensions) and scans the memory after it. Each "row" is a pair of a null-terminated string and a 32-bit offset. When a match between the requested function name and an exported name is found, current ip of the script is set to the found offset. Search is case-insensitive.
Execution continues inside the loaded file, all offsets work relative to P.
When the script encounters a cleo_return, results are stored in shared ScriptParams.
Then base ip is restored alongside other things (NEW).
Then stored values get copied into the host script variables.
Compiler support
Exported function should be discoverable by the runtime. When fun@scripts.s gets loaded, there must be a way to find the offset for 'fun' inside the scripts.s.
To achieve this, the compiler constructs an export table. It is trivially located at the start of the script and routed away from the normal execution with a jump instruction. This technique is similar to what a main.scm header uses.
source code for
:fun
...
:fun2
...
export @fun
export @fun2
transformed into this by the compiler:
0002: @after_table
hex
"fun" 00 @fun 00 00
"fun2" 00 @fun2 00 00
end
:after_table
:fun
...
:fun2
See "Compatibility with Function syntax" down below for the structure of the export table. Last two 00 are reserved for input and output.
As with main.scm header, it gets constructed after the full pass on the code, so the compiler knows all functions that need to be exported.
Disassembler support
- No extra work is planned. Export table will be present as a regular jump and hex..end block (Extra info is needed). A stretch goal would be to reconstruct the export table to the initial form.
Possible limitations
- length of the import argument is limited to 255. Very long file names or function names will result in a compilation error.
- there is no limit on a number of function to export from a single file
Compatibility with future extensions
Header extension in CLEO scripts could be used to store more than just an export table. Need to provide a clear distinction between different sections. It could be a service byte after the jump instruction:
02 00 01 xx xx xx xx 01
where the last 01 is the marker for an export table.
The runtime logic should then jump +8, not +7.
Custom Headers proposal
Stretch goal: https://gist.github.com/x87/5d0bd6bdd0062380628eb35103894e1b
IDE Support (stretch goal)
IDE should scan the export table and offer a list of available functions for autocomplete, and also display function signature if function syntax is implemented