Hello, my name is Vlad Faust.

I'm a passionate developer currently working on my own programming language.

You can follow me on Twitter and GitHub and also send nudes to mail at vladfaust.com

Exporting from Onyx


In Onyx, you may export a C entity from within an Onyx source file. Later in the Onyx context, the entity is treated as if it was imported directly from a C header.

Unlike in raw C headers, an export statement is written in some mix of C with Onyx, allowing Onyx annotations, Onyx macros and having function bodies written in Onyx.

Normally, an export statement uses the ISO C syntax, and C vendor extensions support is undefined. It is recommended to make use of Onyx annotations instead, which may be converted in accordance with a target vendor’s semantics. To achieve that, an Onyx implementation should maintain some sort of mapping between vendor C extensions and according Onyx semantics.

For example, FNX supports GCC extensions. Upon generating a header file from Onyx source, the C vendor option (-c[-w]) is respected, and Onyx annotations are converted to the vendor’s.

main.nx
# A struct comment.
@[Pack]
export struct struct_t {
  // A variable comment.
  @[Align<8>]
  int foo;

  double bar;
};

After running fnx doc -fh -c[-wgnu] main.nx:

main.h
// A struct comment.
struct __attribute__ ((__packed__)) struct_t {
  // My comment.
  int foo __attribute__ ((aligned (8)));

  double bar;
}

A C macro is treated as a freestanding entity, hence requiring a separate export entity per macro, for example:

main.nx
export #ifdef __GNUC__
@[AlwaysInline]
export int foo();
export #elif _MSC_VER
@[AlwaysInline]
export double foo();
export #endif

After running fnx doc -fh -c[-wgnu] main.nx:

main.h
#ifdef __GNUC__
__attribute__((always_inline))
int foo();
#elif _MSC_VER
__attribute__((always_inline))
double foo();
#endif

Note that both annotations have expanded to GCC attributes regardless of the _MSC_VER macro, because the behaviour is controlled by the -c[-w] option. To solve that, you either want to use explicit C annotations:

main.nx
export #ifdef __GNUC__
export __attribute__((always_inline)) int foo();
export #elif _MSC_VER
export __forceinline double foo();
export #endif

Or expand them in macros (which is equivalent to writing them explicitly):

main.nx
macro attr(vendor)
  {{ nx.id["AlwaysInline"]:c(vendor) }}
end

export #ifdef __GNUC__
export @attr("gnu") int foo();
export #elif _MSC_VER
export @attr("msvc") double foo();
export #endif

Sometimes it may become cumbersome to have a separate export statement per entity, like in the examples above. To deal with that, Onyx allows to export entire blocks in the same C-NX syntax. The same rules apply.

main.nx
export {
#ifdef __GNUC__
  @[AlwaysInline]
  int foo();
#elif _MSC_VER
  @[AlwaysInline]
  double foo();
#endif

  // A C comment
  @[NoInline]
  void main() {
    # Some Onyx code
  }
}

After running fnx doc -fh -c[-wgnu] main.nx:

main.h
#ifdef __GNUC__
__attribute__((always_inline))
int foo();
#elif _MSC_VER
__attribute__((always_inline))
double foo();
#endif

// A C comment
void main() __attribute__((noinline));

It is common to have entire files written in this manner when tight interoperability with C is implied, usually with an .cnx extension.