New features in the 4th beta preview
Posted on August 5th, 2007 by SealedSun
Hey, it's been a while but I have added a number of neat features to Prexonite and especially to Prexonite Script.Conditional expressions
function max(a,b) =
if(a> b)
a
else
b;
if(a> b)
a
else
b;
You can also use the 'traditional' {cond} ? {expr} : {expr} syntax but it won't be as easy to read.Loop expressions
function main()
{
var xs = for(i = 0; i <100; i++)
yield i;
;
var ys = foreach(var x in xs)
if(x mod 2 == 0)
yield 2*x;
;
}
{
var xs = for(i = 0; i <100; i++)
yield i;
;
var ys = foreach(var x in xs)
if(x mod 2 == 0)
yield 2*x;
;
}
Loops can be used as expressions. Their 'value' is a list of all values 'returned' via the yield keyword. Although it may look like a coroutine, in reality a Prexonite list is returned and not a coroutine reference.Coalescence operator
function chapter(text ,title)
{
title ??= "Chapter " + (counter++);
println(title);
addTOCentry(title);
println(text ?? "Lorem Ipsum [...] ");
}
{
title ??= "Chapter " + (counter++);
println(title);
addTOCentry(title);
println(text ?? "Lorem Ipsum [...] ");
}
?? behaves just like you are used to it: it returns the expression on it's left hand side unless that is null, in which case it returns it's right hand side.The assignment operator 'x ??= y' is syntactic sugar for 'x = x ?? y', which in turn is translated into 'x = x is Null ? y : x'.Compiler hooksCompiler hooks are functions that are called whenever the AST (abstract sytax tree) for a function has been created. They can be used to transform the users code however they want. In the following sample, calls to the non-existant function 'debug' in if-blocks are replaced by the value of the meta key "debugging". This way, the code generator can figure out, that the code inside the if-block can either always (if 'debugging enabled' is set) or never be reached and therefor remove the check or the whole block respecitively.
build does require = @"ast.pxs";
build does hook (t =>
{
var debugging = func.ParentApplication.Meta["debugging"]~Bool;
function replace_debug(block)
{
foreach(var stmt in block)
{
//look for conditions
if( stmt is ::AstCondition && isFunctionCall("debug", stmt.Condition))
stmt.Condition = ast("Constant", debugging);
//Recursively replace 'debug' in nested blocks.
if(stmt.\implements("Prexonite.Compiler.Ast.IAstHasBlocks"))
foreach(var subBlock in stmt.Blocks)
replace_debug(subBlock);
}
}
replace_debug(t.Ast);
});
declare function debug;
debugging enabled;
function main()
{
if(debug)
println("Entering main function");
execute_program();
if(debug)
println("Leaving main function");
}
build does hook (t =>
{
var debugging = func.ParentApplication.Meta["debugging"]~Bool;
function replace_debug(block)
{
foreach(var stmt in block)
{
//look for conditions
if( stmt is ::AstCondition && isFunctionCall("debug", stmt.Condition))
stmt.Condition = ast("Constant", debugging);
//Recursively replace 'debug' in nested blocks.
if(stmt.\implements("Prexonite.Compiler.Ast.IAstHasBlocks"))
foreach(var subBlock in stmt.Blocks)
replace_debug(subBlock);
}
}
replace_debug(t.Ast);
});
declare function debug;
debugging enabled;
function main()
{
if(debug)
println("Entering main function");
execute_program();
if(debug)
println("Leaving main function");
}
In this case, the call to 'debug' would be removed and replaced by 'true'. Since that expression is constant, the contents of the if-block are directly inserted into the function.While this is a really powerful tool, it requires thorough knowledge of the AST API, which is not documented at all. Unlike the 'runtime' part of the Prexonite library, the lower level of the public compiler interface assumes it's user knows what he or she does. You can easily cripple the whole loader, if you are not careful!Tab completionThe next release of the interactive interpreter features tab completion for globally declared symbols (functions, commands and global variables) as well as user defined strings (such as "ToString").
[...] New features in the 4th beta preview [...]