Skip to content

Neat 0.5.0: Let me try something.

Compare
Choose a tag to compare
@FeepingCreature FeepingCreature released this 12 Nov 21:56
· 143 commits to master since this release

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!