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.
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.
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.
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
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)
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'
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); }
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();
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 }
The usage of the loop keyword is unchanged in SV4.
loop (3) WriteLog("meow");
Result: The string "meow" is printed 3 times.
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.
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.