For a variety of reasons, we might occasionally want to run a script manually without using the run() function. This involves two steps. The first step is to produce bytecode, easily done using the compile() function. Second, the transform() function gives bytecode a perch on a function’s internal code registry. Here is a simple example:
myBytecode := compile("myMessage := \"Hello, world!\"; print(myMessage)")
newFunction :: transform(myBytecode)
newFunction()
Manual compilation and transformation is necessary when user.cicada has not been loaded, for example when running Cicada from the command prompt with a script-file argument.
Another situation where we will want to compile scripts manually is when we want to run a script that uses a different syntax from the default Cicada language. For example, our script might want to process commands typed by the user, that are in a completely different format from the Cicada language. We don’t want to change cicada.c because then none of the other scripts like start.cicada will work. Instead we will need to use the newCompiler() function to process the user’s input differently from our own scripts. Here is an example, relying on language-constant definitions in user.cicada.
newLanguage :: [] compiledCommandType
newLanguage[*] = {
{ cat("add ", type1arg, "to", type1arg), 1, "0", cat(inbytecode, "8 173 10", anonymousmember, "27 a1 a2 0") },
{ cat("negative", type1arg), 2, "1", cat(inbytecode, "29 54 -1 a1") },
{ int_constant, 0, "1", cat(inbytecode, "54 a1") },
{ double_constant, 0, "1", cat(inbytecode, "55 a1") }
}
newLanguageAssociativity :: [] int
newLanguageAssociativity[*] = { l_to_r, r_to_l }
newCompilerID := setCompiler(newLanguage, newLanguageAssociativity)
myBytecode := compile(input())
doMath :: transform(myBytecode)
doMath()
printl("The answer is: ", doMath[1])
Notice how the two array arguments to newCompilerID() are exactly the same as the two arrays that specify the language in cicada.c/cpp. For consistency, all of the constants used in the C file (such as int_constant and inbytecode) are also defined in user.cicada. The main difference is that we have to use the cat() function to concatenate strings. Also, make sure to define both arguments as arrays -- setCompiler() will not understand anything inside of curly braces. As always, any script must have an overall type of 0, which in this case means that any valid script is an ‘add’ command.
Cicada always opens with a single compiler -- compiler ID 1 -- so to switch back we can just type the command newCompiler(1). Calling newCompiler() with no arguments returns the ID of the current compiler, without changing which compiler is active.
Functions produced by different compilers live in different namespaces, because each compiler which keeps its own running tally of all member names it has encountered (and how many anonymous members have been made). But the use of separate compilers does not prevent collisions of member names between these functions: if anything switching compilers makes collisions more likely. (Any new compiler member will assign member IDs starting from 1 and counting upwards, and that ID is the only thing Cicada sees when the function runs.) To avoid problems, use transform()’s ability to customize the search path so that the calling script is hidden from any transformed bytecode that it produces.
Last update: May 11, 2017