Cicada ---> Online Help Docs ---> Customizing the Cicada language

Custom-compiling within a script

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()
   

Another compile()-transform() situation arises when we 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. In that case we won’t want to change cclang.c since doing so would break Cicada’s built-in scripts. Instead we must use the newCompiler() function to process the user’s input using a different syntax from that of our own scripts. Here is an example, relying on language-constant definitions in defs.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 := newCompiler(newLanguage, newLanguageAssociativity)
   
    myBytecode := compile(input("Give me math: "); compilerID = newCompilerID)
    doMath :: transform(myBytecode)
   
    doMath()
    printl("The answer is: ", doMath[1])
   

Running this script:


    Give me math: add negative 1 to 3.14
    The answer is: 2.14
   

Notice how the two array arguments to newCompiler() are almost exactly the same as the two arrays that specify the language in cclang.c, the main difference just being the use of cat() to concatenate strings. For consistency, all of the constants used in the C file (such as int_constant and inbytecode) are also defined in the scripting environment. Also, make sure to define both arguments as arrays -- newCompiler() will not understand anything inside of curly braces. As always, any script must have an overall type of 0. In this primitive example the only possible valid script is an add command.

Functions produced by different compilers live in different namespaces, because each compiler keeps its own running tally of all member names and anonymous members it has encountered. 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, manually isolate the new bytecode’s search path from the workspace using transform().


Prev: Adapters    Next: Reference


Last update: November 12, 2025