Appendix

Stanza has a number of convenience constructs that make your life easier, but they are not necessary for day to day programming. You may skim through this appendix and learn about these constructs as their need arises.

Stanza Compiler Options

.stanza Configuration File

Stanza's platform and compiler settings are stored in the .stanza file that was created when you installed Stanza with stanza install. When you run Stanza it will first look for an appropriate .stanza file. Here are the places that Stanza searches in, in order, for the .stanza file.

  1. Stanza first looks in the current working directory.
  2. If the STANZA_CONFIG environment variable is set, then Stanza looks in that directory.
  3. If the HOME environment variable is set, then Stanza looks in that directory.

Basic Compilation

To compile myfile.stanza and generate the binary myprogram use the following command.

stanza myfile.stanza -o myprogram

Optimization

To compile with optimizations, use the -optimize flag.

stanza myfile.stanza -o myprogram -optimize

Be warned that Stanza's optimizer is only designed to handle correct programs. A correct program is defined to be a program that successfully runs to completion without ever failing with a call to fatal. If an unoptimized program runs to completion and generates a result, then the optimized program is guaranteed to run to completion and generate the same result. However, if the unoptimized program fails, then the behaviour of the optimized program is undefined.

Generating Assembly Files

By default, Stanza generates a temporary .asm file containing the generated assembly instructions and then links it with GCC. To use a specific name for the .asm file use the -s flag.

stanza myfile.stanza -s myprogram.s -o myprogram

The above command will generate the assembly file myprogram.s and link it to produce the binary file myprogram.

For expert users that only want the assembly file, the -o flag may be omitted. The following command only generates the assembly file myprograms.s.

stanza myfile.stanza -s myprogram.s

Pkg Files

Stanza's separate compilation system allows for packages to be compiled into .pkg files. The following command compiles each package in myfile.stanza to a separate .pkg file.

stanza myfile.stanza -pkg

By default, the resultant .pkg files are generated in the current working directory. To specify the folder into which they should be generated, provide the path after the -pkg flag. The following command puts the resultant .pkg files in the mypkgs folder.

stanza myfile.stanza -pkg mypkgs

Note that the current compiler requires for source files containing mutually dependent packages to be compiled together. For example, if myfile1.stanza contains

defpackage mypackage1 :
   import mypackage2
...   

and myfile2.stanza contains

defpackage mypackage2 :
   import mypackage1
...

then myfile1.stanza and myfile2.stanza must be compiled together with the following command.

stanza myfile1.stanza myfile2.stanza -pkg

Automatic Pkg Loading

When you compile a program, Stanza automatically looks for the .pkg files containing the definitions of the packages that you import. Here is the order in which Stanza looks for appropriate .pkg files.

  1. If you've provided a path using the -pkg-path flag, then Stanza will first look there for .pkg files. For example, the following command compiles myfile.stanza using the .pkg files in the mypkgs folder.
    stanza myfile.stanza -pkg-path mypkgs
  2. If the -pkg-path flag is not provided, then Stanza will first look in the current working directory for .pkg files.
  3. If the -optimize flag is provided, then Stanza will look in the directories specified by the fast-pkg-dirs option in your .stanza configuration file. To add additional directories to the pkg path, add the following to your .stanza file.
    fast-pkg-dirs = ("/path/to/myfastpkgs1" "/path/to/myfastpkgs2")
  4. If the -optimize flag is provided, then Stanza will look in the fast-pkgs folder in your Stanza installation directory.
  5. Stanza will then look in the directories specified by the pkg-dirs option in your .stanza configuration file. To add additional directories to the pkg path, add the following to your .stanza file.
    pkg-dirs = ("/path/to/mypkgs1" "/path/to/mypkgs2")
  6. Stanza will then look in the pkgs folder in your Stanza installation directory.

C Compiler Options

Stanza provides the -ccfiles flag to include additional files to the call to the C compiler. The following command compiles the myfile.stanza program and links it against the functions contained in supportfunctions.c to produce the myprogram executable.

stanza myfile.stanza -ccfiles supportfunctions.c -o myprogram

You may also use the -ccflags flag to include additional flags to the C compiler. The following command compiles the myfile.stanza  program and calls the C compiler with the additional -lmylib flag to produce the myprogram executable.

stanza myfile.stanza -ccflags -lmylib -o myprogram

Note that to provide multiple flags to the C compiler, the flags must be quoted.

stanza myfile.stanza -ccflags -lmylib1 -lmylib2 -o myprogram

Target Platform Settings

By default, Stanza generates code appropriate for the platform that you specified in the call to stanza install. If you wish to generate code appropriate for a different platform, then you can override the platform using the -platform flag.

The following generates the assembly file myprogram.s appropriate for the Windows platform.

stanza myfile.stanza -s myprogram.s -platform windows

The When Expression

The when expression provides a convenient syntax for very short if expressions. The following

val name =
   if meerkat? : "Timon"
   else : "Pumbaa"

assigns the string "Timon" to name if meerkat? is true, otherwise it assigns "Pumbaa". It can be equivalently written as

val name = "Timon" when meerkat? else "Pumbaa"

In general, the form

a when c else b

is equivalent to the if expression

if c : a
else : b

Optional Else Branch

You may also leave off the else branch, in which case

a when c

is equivalent to the if expression

if c : a

This form is often convenient if you want to call a function only when some condition is true.

press(button) when action == "press"

The when expression is another example of a convenience construct implemented as a macro.

The Where Expression

The where expression provides a convenient syntax for pulling out short definitions from complicated expressions. The following code

println("They call me Mr. %_" % [name]) where :
   val name = "Pig!" when angry? else "Pumbaa."

first defines name, and then prints the message. It is equivalent to

let :
   val name = "Pig!" when angry? else "Pumbaa."
   println("They call me Mr. %_" % [name])

The where expression is also implemented as a macro. As you can see, Stanza's core library makes heavy use of macros.

The Switch Expression

The switch expression provides a convenient syntax for choosing amongst a number of nested if branches. Here is an example of evaluating the first branch for which empty? evaluates to true.

switch empty? :
   a : println("List a is empty.")
   b : println("List b is empty.")
   head(c) : println("The head of list c is empty.")
   else : println("Nothing is empty.")

The above is equivalent to these nested if expressions.

if empty?(a) :
   println("List a is empty.")
else if empty?(b) :
   println("List b is empty.")
else if empty?(head(c)) :
   println("The head of list c is empty.")
else :
   println("Nothing is empty.")

If the else branch is omitted then a default else branch is provided that prints an error and causes the program to fail.

The switch construct is commonly used with an anonymous function as its predicate. Here is an example of using switch to evaluate different branches depending on the value of x.

switch {x == _} :
   0 : println("Sunday")
   1 : println("Monday")
   2 : println("Tuesday")
   3 : println("Wednesday")
   4 : println("Thursday")
   5 : println("Friday")
   6 : println("Saturday")
   else : println("Elseday")

More on Visibility

Package Qualified Identifiers

Suppose our main program makes use of the following definitions from an animals package.

public defstruct Dog
public defstruct Cat
public name (x:Dog|Cat) -> String
public sound (x:Dog|Cat) -> String

Package-qualified identifiers allow us to reference those definitions without having to import the animals package. Here is a main function written using package-qualified identifiers and without importing animals.

defpackage animal-main :
   import core

defn main () :
   val d = animals/Dog("Shadow")
   val c = animals/Cat("Sassy")
   println("My dog %_ goes %_!" % [animals/name(d), animals/sound(d)])
   println("My cat %_ goes %_!" % [animals/name(c), animals/sound(c)])

In general, a package qualified identifier is any identifier that contains the '/' character. The characters after the last occurrence of the '/' form the name of the definition being referenced. The characters before the last occurrence form the name of the package containing the definition being referenced. For example, the following identifier

stanza/compiler/type/FunctionType

refers to the FunctionType definition in the stanza/compiler/type package.

Package-qualified identifiers are mostly used by macro writers. Macros should expand into references to package-qualified identifiers to prevent users from having to explicitly import the runtime libraries that the macros depend upon.

Top Level Identifiers

Identifiers whose only occurrence of the '/' character is at the beginning of the identifier are called top-level identifiers. For example, /sound and /name are top-level identifiers.

Top level identifiers are used to refer to a definition that is visible from the top most scope in the current package. It is used to refer to a top-level definition when its actual name has been shadowed by a local definition.

For example, the following

defn main () :
   val s = "Hello"
   val length = 42
   println(length(s))

fails to compile with the error

Value length of type Int cannot be called as a function.

This is because length refers to the value 42, not the function that returns the length of a string. We can get around this either by renaming the length value to something else, or by using a top-level identifier to refer to the length function.

defn main () :
   val s = "Hello"
   val length = 42
   println(/length(s))

Protected Visibility

In addition to public and private visibilities, Stanza supports one last visibility setting: the protected visibility. A definition with protected visibility can be referred to from other packages, but they can only be referred to using package-qualified identifiers.

Suppose we have an animals package containing the following definitions.

public defstruct Dog
public defstruct Cat
public name (x:Dog|Cat) -> String
protected sound (x:Dog|Cat) -> String

And we will import the animals package into our animals-main package.

defpackage animals-main :
   import animals

defn main () :
   val d = Dog("Shadow")
   val c = Cat("Sassy")
   name(d)
   animals/sound(c)

All of the public definitions in animals can be directly referred to in animals-main after they have been imported, but the protected function sound must be package-qualified.

Protected definitions are most often used by macro writers. Often, a macro simply expands into a decorated call to a helper function. We want to encourage users to use the macro form, and not call the helper function directly. By annotating the macro with the protected visibility we make it unlikely for users to accidentally call the helper function.