-
Notifications
You must be signed in to change notification settings - Fork 0
syntax
Prismscript provides pretty full-featured syntax for expressing logical directives, structured to be similar to that of PHP.
As in PHP and Python, assignments do not require explicit forward declaration of a variable.
x = 5;
x = 'hello!';
Identifier names are, as with most languages, case-sensitive: foo
is not the same as Foo
. Standard character rules apply, too: [_a-zA-Z][_a-zA-Z0-9]*
.
Prismscript allows for scope disambiguation, for global and local variables:
global x = 5; //x will be bound to 5 in the global scope.
local x = 5; //'local' is optional, since this is the default behaviour anyway.
Complex assignments, based on Python, are also possible:
[x, local y, None, global z] = [1, 2, 3, 4]; #Any sequence or Python generator is acceptable as a right-hand argument.
Coupled with return
, this is a very powerful idiom, since it allows for multi-value return statements:
node{
[a, b, c] = function();
}
function(){
return [1, 2, 3];
}
By default, Prismscript will resolve the value of a variable based on locals, falling back to globals if it's not found. (If it's not found there, an error occurs)
This process can be circumvented, in the event that a value needs to explicitly exist in one of the two scopes:
return local x;
return global x;
Standard mathematical operators are provided.
y = 5 + x;
y = x - 3;
y = 4 * 2;
y = x / 5.6;
y = x \ 2; #Integer division
y = x % 2;
y = x ^ 3; #Exponent notation
y = x * (5 * 2); #BEDMAS applies, so brackets are evaluated first.
When the left-hand or right-hand side is a string, the resulting value is a string.
When both the left-hand and right-hand sides are Sequences, a single unified Sequence is produced.
Prismscript provides all expected boolean evaluation functions.
x == y;
x != y;
x > y;
x >= y;
x < y;
x <= y;
!True;
!x;
In general, an exception will be raised if comparing incompatible types, though the rules applied are up to Python. Additionally, negation works using Python's not operator, with scope bound to the element occurring most immediately to its right, so !5
yields False
, for example.
Lazy-evaluating boolean operators are provided, too.
y = False && function_that_destroys_everything();
y = True || function_that_destroys_everything();
Note that, like Python's or
and and
, one of the expressions is returned, rather than a boolean value. This is generally a non-issue, since implicit boolean evaluation is performed in conditional checks anyway, but if you explicitly need a boolean value, either use a test-expression or lang.convert.bool()
.
The advantage to using Python's technique is that None || 5
will yield 5
, allowing writers to avoid introducing an explicit conditional and allowing for more seamless inlining of substitutions.
Existing variables can be modified using shorthand syntax.
x += 5;
x -= 1;
x ^= 2; #squares x
x *= 4.3;
x /= 2.4;
x \= 5.1;
x %= 6;
Type-transformation rules are identical to those for the base operators.
Comments work as they do in most post-C languages, and two notations are provided.
#I am a comment.
//So am I.
x = 5; //Line-comment
#x = 5; //Commented-out statement.
x = [
#4 //Commented-out list-member.
];
Prismscript's top level is made up of "nodes" and "functions" that can be declared in any order. The only syntactic difference between them is that functions are declared with a parameter-list.
Nodes and functions exist in different namespaces, so it's perfectly legal to have one node that shares the same name as a function. However, if you give the same identifier to two entities of the same type, the previous one will be overwritten.
Note that the word 'identifier' was used instead of 'name' in the second sentence above: functions can be overloaded, as in most C-like languages. The name of each parameter to a function, and the number of parameters, treated as an unordered set, make up the function's signature, together with the function's name: x(y, z)
== x(z, y)
!= x(y)
.
sample{
exit "Hello!";
}
Prismscript is whitespace-insensitive, so the following works, too:
sample{exit 'Hello!';}
sample(x, y){
return x + y;
}
While functions can accept parameters that are treated as variables in their local scope, nodes initially only have access to the global variable scope.
Functions also, as in most other languages, allow values to be returned (with None being the default if they reach their end). Nodes do not.
Additionally, and most significantly, when any node reaches completion, execution is assumed to be entirely finished. Functions, on the other hand, return control to their caller.
Either a node or a function may be used as an entry-point by an interpreter instance, though nodes are guaranteed to have an exit-value while functions could end with either a returned value or an exit-value, depending on whether an exit
directive was encountered during execution.
-
return x
- returnsx
to the controlling context. Ifx
is omitted, None is returned. -
exit x
- exits with ax
, which can be of any type. Ifx
is omitted, None is used. -
goto x
- begins execution of the nodex
. Control is not returned.
Conditionals are blocks of statements that have their execution hinged upon the outcome of a test. Prismscript supports if
-elif
-else
, while
, and for
(each) conditional statements.
Conditionals may be nested. Conditionals also share scope with their parents, unlike Java and C#: if 'x' is defined in an if-block that gets executed, 'x' remains defined after that if-block has passed.
Python's bool-coercion is applied to all conditional tests, so if you want to trigger on x = any non-zero number an expression of 'x' alone is sufficient.
As in PHP, any expression that produces a value can be used as the condition for an if
or elif
statement, and else
, if present, triggers if no prior block was executed.
if(x == 1){
//...
}elif(y){
//...
}elif(z == 2){
//...
}else{
//...
}
Note: unlike most languages with C-like syntax, there is no shorthand for if
-blocks: braces are always needed, regardless of the number of enclosed statements.
Not much to be said about these.
while(False || True){
//...
}
A for
(each) loop sequentially consumes items from a sequence (or Python generator) and assigns them to a named (optionally global) variable. It stops when the source has been exhausted.
for(i in [1, 2, 3, 4, 5]){
//...
}
for
(each) loops also support complex assignments, just like the example above:
for([i, j, k] in [[1, 2, 3], [4, 5, 6]]){
//...
}
Within a loop, the break
and continue
statements may be used as in other languages, exiting the current loop or proceeding to the next iteration immediately.
Unlike most other languages, Prismscript requires that every parameter being passed to a function be explicitly named. This was done for both forwards- and backwards-compatibility reasons and because it enforces at least a small level of automatic documentation in code (who wants to figure out what 'x' is in a big sea of like-named variables?). As side-benefits, it also allows parameters to be specified in any order and simplifies the process of making arguments optional.
Functions are available in two flavours: the first are functions declared within Prismscript itself; the second are external functions marshalled in by the application's developer (these will require supplementary documentation, naturally). The method of invoking either type of function will appear identical:
x = myfunction(
v=y,
k=z
); // Where 'v' and 'k' are the names of the parameters.
x = lang.math.pow(
x=5,
exponent=2
);
When resolving the function, a similar strategy to the local/global scope lookup is used: locally defined functions are searched for first, and marshalled functions are resolved afterwards, to ensure that, if a namespace expansion collides with a pre-existing script, the script continues to function as it always did before.