ssc/language.md

8.0 KiB

Language

SunScript is the name of the language parsed natively by ssc. It closely resembles both JavaScript and the native byte-code used by Super Mario Sunshine's SPC interpreter. A SunScript file uses the extension ".sun" for convenience. Below is a brief summary of the language's syntax.

Comments

Both single-line comments and multi-line comments are supported. A single line comment begins with // and ends at the next new-line character. A multi-line comment begins with /* and ends with */.

Variables

A variable is created by use of the var keyword and may initially be either declared or defined. A declaration leaves the variable with an undefined initial value, while a definition assigns a value to the variable explicitly:

var a;
var b = 33;

There are several primitive types in SunScript, as shown in the following table. All variables are dynamically typed and can change at any time by simply assigning it an expression of another type.

Type Example
int 132, -0xff
float 15.64, 9.0
string "foo\nbar"
address $803200A0

Note: SunScript also has the two boolean keywords true and false (currently defined as the integers one and zero, respectively).

Casting

To explicitly cast to the integer and floating-point types, use the int and float casting statements:

var const PI = 3.14;
var piIsExactlyThree = int(PI); // *gasp*

To get the type of any variable or expression, use the typeof statement. Constants for the possible return values are defined in the standard include library.

var a = 1.1 * 8;
var b = typeof(a);
var c = typeof(1.1 * 8); // you may also put expressions as the argument

Constants

Read-only variables (constants) may be created via the const modifier. They represent a special literal value which does not change.

Note: A constant cannot be declared without a value.

Constants are not stored in the symbol table, nor are their definitions compiled the text section. Instead, they are simply compiled each time they are actually used (similar to macros in the C preprocessor):

var const PI = 3.14;
var const TWOPI = 2 * PI;
var const R = 300.0;

var circ = R * TWOPI; // this actually compiles to 'var circ = 300.0 * (2 * (3.14));'

Note: The expression assigned to a constant must be evaluated to be constant. A constant expression is one which contains only literals and constant symbols.

Callables

There are two types of callables in SunScript: builtins and functions. A builtin may only be declared (must not have a body), while a function may only be defined (must have a body). Callables may have any number of parameters.

To define a function, use the function keyword. For builtins, use the builtin keyword:

builtin getSystemFlag(flag);
builtin setSystemFlag(flag, value);

function setOnSystemFlag(flag) { setSystemFlag(flag, true); }
function setOffSystemFlag(flag) { setSystemFlag(flag, false); }

Each of the callable's parameters is dynamically typed. A builtin may have a variadic signature by specifying the ellipsis identifier ... as the final parameter:

builtin print(...); // variadic builtin

print("Hello, ", "world!"); // this is legal
print("I have ", 3, " arguments."); // so is this

Note: Variadic functions are not supported.

A callable's return value is also dynamic. Use a return statement to set the return value for a given code path. Not all code paths are required to have a return value.


Functions and builtins may be called either as standalone statements or in expressions. To call a function or builtin, simply pass its name, followed by any arguments in parentheses:

appearReadyGo(); // calls the function 'appearReadyGo' with no arguments
insertTimer(1, 0); // calls the function 'insertTimer' with two arguments, '1' and '0'

Note: You cannot call a builtin or function in code preceding its declaration or definition.

Modifiers

A declared symbol may be assigned compile-time modifiers. The modifiers follow their respective keywords in their declaration/definition:

var foo; // global script symbol (can be resolved from other scripts)
var local bar; // local script symbol (can be resolved only from the current script)

function local const getBaz() {
    return 132;
}

var local const baz = getBaz(); // getBaz is marked as constant

SunScript supports the following modifiers:

Modifier Description
local Resolves only within the current script file. Applies to only script-scope symbols.
const Marks the symbol as constant. Allows for the symbol's use in constant expressions.

The following matrix details which symbol types support which modifiers:

type local const
variable
function
builtin

Operators

Operators and their precedence in SunScript are largely borrowed from C++. The following table describes the operators supported by SunScript, as well as their precedence and associativity:

Precedence Symbol Name Associativity
-1 =
+= -= *= /= %= &= `
=` assignment
compound assignment
0 ` `
1 && logical-AND left
2 ` ` bitwise-OR
3 & bitwise-AND left
4 == != equality and inequality left
5 < > <= >= comparison left
6 << >> bitwise-shift left
7 + - addition and subtraction left
8 * / % multiplication, division, and modulo left
9 !
-
logical-NOT
negation
right
10 [?:] ternary conditional right

Flow Control

Simple boolean flow control can be created using the if and else statements:

var coinNum = getSystemFlag(SYSF_GOLDCOINNUM);
if (coinNum >= needCoinNum) {
  setSystemFlag(SYSF_GOLDCOINNUM, coinNum - needCoinNum);
  startEventSE(1);
  talkAndClose(0xD0019);
  yield;
  setNextStage(20, 0);
}
else {
  talkAndClose(0xD001A);
}

SunScript also supports the standard loop blocks while, do, and for:

var maniShineNum, var i;
for (i = 70; i < 86; ++i) {
  if (isGetShine(i)) {
    ++maniShineNum;
  }
}

To stop execution of the script for the rest of the frame, use the yield statement. Control will return on the next frame exactly where the script left off.

while(!isTalkModeNow()) {
  yield;
}

To end execution of the script completely, use the exit statement:

if (getSystemFlag(SYSF_MARIOLIVES) > 5) {
  exit;
}
startMiss();

Named Loops

Loops may be named in order to reference them from within nested loops. To name a loop, simply prefix the loop with a label. break and continue statements may be passed the name of the loop which they affect:

outer_loop:
for (var a = 0; a < 4; ++a) {
    for (var b = 0; b < 4; ++b) {
        if (b == 2) break outer_loop;
    }
}

Importing

You may split a script amongst several files. Doing this requires the use of the import statement. Simply pass the name of the SunScript file to import:

import "ssc/common.sun";
import "constants.sun";
import "C:/math.sun";

Note: It is recommended to avoid using backslashes, as they may be interpreted as an escape.

Importing files is managed by the sunImportResolver instance passed to the compiler. The resolver can be the default (returned by the sunImportResolver.Default property) or a completely custom one. If an import resolver fails to find a file, a compiler error will occur.


The default import resolver imports scripts by searching and loading files from disk. Where the resolver looks for the script depends on whether the given name is absolute or relative:

  • If the name is relative, then the resolver will look relative to the directory of the current file, followed by the compiler's executable directory.
  • If the name is absolute, then the resolver will simply look for the script only in the path specified.

It also keeps track of all files that have been imported and will skip over files whose contents have already been compiled. This helps prevent infinite recursion.