RDNH SV4 Language Features

Return to Index

This page summarizes any new language features in SV4.
Anything self-explanatory that was carried over from the original script version (SV3) might not be explained here.

Values and Types

RDNH SV4 is a dynamically typed language and variables are allowed to change types at any time. This is different from DNH ph3, which did not allow types to change after the first assignment.

SV4's types are: nil, bool, real, char, pointer, function, array, string, and table
function may be shortened to func, this will behave the same in all contexts. Note that pointer is currently unused, but reserved.

New Types

Function Type

To obtain a function value, write $ (dollar symbol) before the name of a function:

function TestFunc() { WriteLog("meow"); }
let myFunc = $TestFunc;
WriteLog(myFunc); // ""
myFunc(); // "meow"

Functions stored in variables behave much like normal functions, but have a small runtime cost to check their validity.

Important: function values cannot be shared across script boundaries. This means it's generally unsafe to store them in common data or pass them as event arguments.

Table Type

Tables are like custom objects and behave similarly to objects in Javascript.

let myTable = {
  x = 123,
  y = 456,
  name = "mana"
};
let fieldY = "y";

// access 'x' field of myTable
WriteLog(myTable.x);

// alternate form of accessing 'y' field, allows for field access
// using variables
WriteLog(myTable[fieldY]);

WriteLog(myTable.name); // mana

Literals

WriteLog(nil);              // nil literal (absence of value)
WriteLog(true, false);      // boolean literals
WriteLog(69, 420.0);        // real literal (base 10)
WriteLog(0xCA7F00D);        // real literal (hexadecimal)
WriteLog('a');              // char literal
WriteLog("cat");            // string literal
WriteLog( [0, 1, 2] );      // array literal
WriteLog( {x = 1, y = 2} ); // table literal (like objects in JS)

Variable Declaration

Variables can be declared using let, var, or const. When declared with let or var the variable will be reassignable, while with const the variable will not be reassignable. When a variable is marked as const, the compiler will also attempt to perform a very basic form of constant propagation, where the constant's value will be compiled directly into the bytecode instead of loading/storing the variable. Currently, this optimization only works when a literal value is assigned.

Note: Variables that have not yet been assigned to are implicitly initialized with nil.

Example:

let x = 123;
WriteLog(x); // 123.0
x = "hello";
WriteLog(x); // hello

const y = 456;
WriteLog(y); // 456.0
y = "world"; // error: can't assign to constant variable

let z;
WriteLog(z); // nil
WriteLog(x + z); // error: can't add 'real' and 'nil'

Type Annotations

Optionally, variables and function parameters may be annotated with 'hints' to suggest what type is stored in them to the scripter. These are not enforced by the compiler and are only meant to serve as a way to document your code without excessive comments.

Example:

let cat: string = "mana";
cat = 69; // It's not a string anymore!!

// this is a function named CountSheep that takes an
// array of strings named 'sheep' and returns a 'real' value
function CountSheep(sheep: array[string]): real {
    return length(sheep);
}

Blocks

Async Block

A new type of block called async has been ported from ph3sx.
These are simply nameless tasks that execute immediately.

Example:

async {
    WriteLog("hello");
    wait(60);
    WriteLog("world");
}

The above would be equivalent to the following in execution:

task MyTask() {
    WriteLog("hello");
    wait(60);
    WriteLog("world");
}

MyTask();

Scoping Behaviour

Curly braces can be used to define blocks. A block is a series of statements with a scope.

Note: The keyword local is a deprecated way to create a new block and should be omitted in new code.

if (mana == "cat") { // new block starts after {
    let sound = "meow";
    WriteLog(sound); // statement
} // block ends here, local variable 'sound' is discarded as it is unreachable

The following is an example of how variables are scoped in blocks:

{
    let a = 1;
    {
        let b = 2;
        WriteLog(a, b); // 1 2
        {
            let c = 3;        
            WriteLog(a, b, c); // 1 2 3
        } // c is no longer accessible after this brace
    } // b is no longer accessible after this brace
    WriteLog(a); // 1
}

Variables may be shadowed within a lower scope and even initialized with a variable of the same name from an upper scope.

{
    let a = 1;
    {
        // performance is improved in some cases by making a local 
        // reference to a variable from an upper scope
        let a = a;
        a += 68;
        WriteLog(a); // 69
    }
    WriteLog(a); // 1
}

Operators

Loops

Normal Loop

The usage of the loop keyword is unchanged in SV4.

loop (3)
    WriteLog("meow");

Result: The string "meow" is printed 3 times.

Ascent and Descent

Basic Form

As in SV3, the standard Danmakufu ascent and descent loops are available:

ascent (i in 0..3)
    WriteLog(i);

Result: The values 0.0, 1.0, then 2.0 are printed, while using descent would print the same values in reverse.

'Each' Form

SV4 adds in a new 'each' version of these loops which iterates over collection types like arrays, strings, and tables:

ascent (c in "meow")
    WriteLog(c);

Result: The character values 'm', 'e', 'o', 'w' are printed. If descent was used, then it would print 'w', 'o', 'e', 'm'. This allows for easy reversal of arrays and strings.