Neat 0.5.0: Let me try something.
Again with the new syntax! When will it end?
Two big new things.
If let(var)
This month's first change is complementary with 0.4.0's breakelse
feature.
You can now write if
statements like so:
if let(int var = expr?) {}
If you use the let
form, the truth value of the variable expression will not be considered.
Only the breakelse
branches from within the expression can prevent the if
branch from being taken.
In other words, it acts equivalent to if (true) { int var = expr?; ... }
.
(Why not just use that then? Some day, I'll probably disable breakelse
outside the if
expression.
It's just too confusing. But then, this idiom will still work.)
The goal is to enable usecases where, for instance, you want to conditionally access an integer
from a data struct, but you don't actually care about the value of the integer, only that it is present.
C interprets 0
as false
because it has an impoverished typesystem that heavily overloads integers,
ie. see error codes and -1
. I was (am) considering removing if
's ability to parse integers as booleans,
but then people coming from C would be very confused, especially if there is another reason (breakelse
)
why the branch might not be taken. Better to throw in a new syntax, at least that way
they know that they have to go look at the manual.
std.macro.easymacro
Previously, the only way to write macros was the cumbersome "load a function, the function instantiates a
macro class, which then hooks the compiler and adds a new syntax rule, which then..." workflow. This is
clearly not competetive with even C++, let alone D's CTFE. So I am proud to introduce std.macro.easymacro
:
void test() {
macro {
print("Hello World from compile-time code!");
code {
print("Hello World from runtime code!");
}
}
}
The code
statement can quasiquote-inject any variables from the macro
block with the standard $var
syntax. code
and macro
blocks can be mixed and recursed arbitrarily.
Nested macro
blocks can access variables from surrounding macro
blocks directly.
If a code
statement is evaluated multiple times, it is inserted into the surrounding function multiple
times.
The macro
statement contents are loaded into the compiler and run at compiletime, just like regular macros.
A good example for how this all works is the new JSON stream parser at std/json/stream.nt.
JSON stream parser
Oh yeah, we have a JSON stream parser now. Basic types, arrays and structs can be automatically encoded
and decoded. See the unittests in std/json/stream.nt for examples.
fail
annotations are out (ish), Error
is in
There were always problems with the fail
attribute. Why does it only work inside tuples? What happens if
you remove all non-fail
types from a tuple with one fail
type? As a result of these issues, the
functionality has been "soft-deprecated": it still works fine, but you are recommended to use
subclasses of std.error.Error
instead. Error
classes are also automatically returned by ?
, but also if
the only return type is Error
, ?
correctly results in a bottom expression.
__CALLER__
__RANGE__
is a built-in expression that evaluates to the range expression of its syntax node.
Complementing this, we now have __CALLER__
. If __CALLER__
is set as the default value of a LocRange
parameter, it will be set to the range expression of the call to that function.
Note that it is an error to use this expression outside a parameter's default value.
That's it! Enjoy!