Implement stdlib as actual Lustre file
Instead of hard-coding Lustre's standard library functions (e.g. the named operators: Lustre::add
, Lustre::mul
...), it may be reasonable to define them in an actual Lustre file that gets shipped inside the compiler. This would make the compiler much simpler to maintain.
Some functions may benefit from having specialized diagnostics. To deal with this, it could be possible to follow Rust's path where the standard library code has access to various compiler-private attributes to alter error emitting.
Some functions, such as Lustre's "iterators", can literally not be expressed in Lustre syntax (both the body and the signature, as they're variadic). Again, this can probably be patched using custom stdlib-reserved attributes. It won't be super clean, but always better than special-casing in 1) the name resolver 2) the function type-checker 3) the potential "lustredoc" 4) the various function-level LSP refactorings & probably more.
Examples of standard function implementations
Operator static arguments
There's a weird case in Lustre where node
-kind static parameters may be instanciated using an iterator literal, as in res = fold_left<<+>>(my_list)
. This example would be equivalent to res = fold_left<<Lustre::add>>(my_list)
. In the standard library file, we could have:
function add {% lustre_internal_operator_impl:_ %} <<type N>> (a, b: N) returns (r : N);
let
r = a + b;
tel;
The pragma would be interpreted as a very special case, and would require the function body to have a very precise strucure, token-wise, so it can extract the +
inside the body and correctly create a mapping to the enclosing function. Of course, this should also work with unary operators. I don't know how it should work for minus (unary/binary depending on context) though.
This is clearly "weird" and kinda feels wrong to handle such specific special cases, but it looks to me like the cleanest way to not hardcode all ot this.
Iterators
Iterators are exceptionally weird due to them looking like functions but having variadic args. It makes complete sense to treat them as functions though. As soon as their static arguments are known, their number of arguments is known, and they can be aliased in function alias declarations or passed as node parameters, so it wouldn't make sense to specialize them that much.
- A possible way to implement them is to declare a stub with zero arguments and a special pragma, and override its signature resolution on a case-by-case basis (much better already than treating the entire iterator as a special ghost function)
- We can also think about implementing special syntax that would only be allowed inside the standard library (like Rust's
box
operator). This would be a bit weird though since our parser isn't tied to the Rustre core. - We can also try to see if one or more of the iterators can be expressed as an alias of one of the other one, to simplify even more