This system is based around the mini-funge fingerprint systems used by Paul Mahon's PJSF interpreter at http://dufflebank.iwarp.com/JSFunge/, Mike Riley's RC/Funge-98, and Zinx Verituse's zfunge. With a few alterations, fingerprints written for their systems should be useable with the one described here. Note that the PJSF specification for pick (K) and roll (O) are the wrong way round; pick should duplicate the item, while roll should move it.
Dynamic fingerprint files are essentially the same as normal funge code files. However only Befunge (i.e. 2 dimensions) are allowed. When the fingerprint is first used by a program, the entire file will be loaded into a fungespace seperate from the program which is using it. This copy of the file will persist until execution of the program is complete, allowing variables to be stored in fingerprint fungespace. Apart from trefunge and concurrency instructions, every Funge-98 instruction supported by your interpreter should be supported by its dynamic fingerprint system, and those instructions should affect the fingerprint fungespace as opposed to the callee's fungespace. Note that the dynamic fingerprint may inherit some of the constraints of the callee's environment, such as limited fungespace/stack size or lack of file access commands. The y instruction should be used to query these if needed. Dynamic fingerprint files can be given two names: Either the ASCII version of the fingerprint they represent (e.g. NULL) or the hex version (e.g. 0x4e554c4c). Your interpreter should provide a central place to store these files so they can be searched for and loaded as necessary. File type extensions can be used if your OS supports them (e.g. .df), as well as removing the 0x prefix if only 8-character file names are available. Compatibility with minifunge funge-lib files could also be provided, allowing the two formats to be used side-by-side. Information about this should be provided in your interpreter's documentation.
In a dynamic fingerprint file, a line of the form =X marks the start of a semantic definition. The fingerprint IP will be started on the following line, heading east. E.g.:
would define S to output a string. When the program executes a fingerprint instruction, a new IP is created inside the corresponding fingerprint space, at the corresponding location. The IP will have a storage offset of (0,0). The callee's stack-stack will be transplanted to act as the fingerprint IP's stack-stack; when the fingerprint IP dies, the stack-stack will be transferred back to the callee (known as the "haunted" IP), and so interaction with the haunted IP's stack is possible. The fingerprint IP will have a special invisible fingerprint loaded, which is impossible to remove (but can be overloaded as normal). This fingerprint should have no name, and so is impossible to load or remove any other way apart from creating a fingerprint IP. This invisible fingerprint defines the following semantics:=S >:#,_$@
Note that D, G, L, P and S will all pop vectors with a size corresponding to the callee's fungespace, not the fingerprint's fungespace.
B Haunted Back Move haunted IP back one step D Haunted Delta Pull a vector off the stack and assign the haunted IP's delta to it E Haunted Stacksize Push the size of the TOSS F Haunted Forward Move haunted IP forwards one step G Haunted Get Works the same as g, but it reads from the callee's fungespace (using their storage offset) instead of the fingerprint fungespace. K Stack Pick Pop a number n, and copy the nth element of the stack to the top. Offsets are relative to the top of stack after n has been popped. If n points outside the stack, a zero will be pushed. Reflects if n<0 L Haunted Location Pop a vector v and set the haunted IP's location to it, such that when the fingerprint exits the IP will execute the instruction at v. This means that the location will actually be set to v − delta. M Haunted IP modes Pushes a scalar onto the stack, containing details of the processor modes the haunted IP is in:
Note that when a fingerprint IP is created, it does not inherit any of the parent IP's modes.
0x01 Stringmode 0x02 Hovermode (Parts of the
0x04 Invertmode 0x08 Queuemode 0x10 Switchmode O Stack Roll Pop a number n, and move the nth element of the stack to the top. If n points outside the stack, a zero will be pushed and no shuffling will occur. Offsets are relative to the top of stack after n has been popped. Reflects if n<0.
If your interpreter supports the 0x4d4f4445 MODE fingerprint, the operation should be as follows:
I.e. n54321Q1K,,,,, should output 53214
- Temporarily set the invertmode flag to the same value as the queuemode flag
- Pick the value out of the stack and shuffle the displaced items down
- Restore the invertmode flag
- Push the rolled item onto the stack
P Haunted Put Works the same as p, but it writes to the callee's fungespace (using their offset) instead of the fingerprint fungespace. R Haunted Reflect Reflects the delta of the haunted IP S Haunted Storage Offset Pop a vector off the stack and assign the haunted IP's storage offset to it Y Haunted Y Acts the same as y, but information is returned about the callee's fungespace (and the currently haunted IP). Consequently, the stack-stack will be temporarily transferred back for this. The data will be placed on the stack according to the fingerprint IP's invertmode and queuemode status.
The only two normal instructions with modified meanings are:
@ Exit fingerprint Causes the stack-stack to be transferred back to the haunted IP, and the fingerprint IP to be killed. Execution of the haunted IP will then resume as normal. y Kill haunted IP Causes the haunted IP (and its ghost) to be killed.
As well as the A-Z semantic instructions, you can also define code for the following:
Note that in some implementations =@ may not be called if q is executed. Using fingerprint-q will cause =@ to be executed.
=t IP split notification Called whenever an IP successfully executes a t. The haunted IP will be the new IP (i.e. the reversed one), and the ID of the source IP will have been pushed on the stack. This allows you to duplicate any IP-specific data the fingerprint might hold. The IP ID must be removed from the stack on exit, otherwise it will be left in place and confuse the callee. =@ IP death notification The haunted IP will be an IP which is about to die (e.g. by executing @). This allows you to delete any IP-specific data the fingerprint might hold.
Depending on the implementation, dynamic fingerprints may or may not execute in one tick of the callee's time. This may also affect executing a dynamic fingerprint by k.
Concurrency is disabled in dynamic fingerprints because determining which IP should get haunting rights requires several expensive lawsuits.
Dynamic fingerprints which add instructions to themselves or modify their entry points are possible, but not officially supported due to the stress they place on the fingerprint system (e.g. loading a dynamic fingerprint, triggering it to add a new instruction, then unloading it may result in removal of an instruction which didn't exist when the fingerprint was loaded. Add to this concurrency and an IP may end up destroying instructions which it didn't mean to). Because of this, avoid writing any =s to the 1st column of fungespace.
Nesting of dynamic fingerprints should be possible. Each sub-fingerprint will have its fungespace reloaded from disc, in order to keep it from seeing the data stored in the parent version of itself.