rmc

Racket-made C


Keywords
c, dsl, ffi, systems
License
Other

Documentation

rmc - Racket-Made C

This is an experimental library for generating C from Racket.

      h/ - Header files for existing C libraries
      t/ - Test suites
private/ - Utlities for implementation
   h.rkt - Header definition helpers
   t.rkt - Testing helpers
 lib.rkt - Using rmc functions from Racket

At a first glance, all it is is parenthetical C with a pretty-printer
written in Racket.

However, it does a few things that may not be entirely intuitive...

- It allows the C code to be written in any order (i.e. prototypes and
  structure forward declarations are automatically generated.)

- It defaults to (what I think is) sane C by, for instance, declaring
  everything static that is not explicitly exported, initializing all
  structures, only exposing fixed-width numeric types, etc.

- It takes care of compilation and discovers which headers and
  libraries are needed by virtue of their types and functions being
  referenced (transitively) from exported symbols.

- It makes distinct operators with different behavior (e.g., unary -
  from binary -.)

- It ensures that function results are never thrown away (i.e. at
  least saved in a local variable), although they may be ignored.

From this perspective, it just-so-happens to compile to C, but could
be thought of as an alternative portable assembler.

The most interesting things, however, are the type system and the
power of Racket:

- A type system equivalent to C is enforced by contracts during the
  construction of the C AST. The goal is that no errors will ever be
  generated by the C compiler.

- You can transparently define functions in C and use them in
  Racket. This could be a lot more fleshed out, but it is pretty neat
  already. See t/lib.rkt

- The type system is extended in the following ways: 

  (1) an Any type is introduced. This is primarily for use with
  existing C libraries, you can just type them as Any (and make a
  convenient macro to input a literal C symbol Any type) and not have
  to write an FFI-like binding.

  (2) Existential types (with seals and unseals) are included whereby
  ASTs are tagged at compile-type and then enforced to match and be
  unseal before being exposed to C. This can be used to make
  static-enforcement of abstract data types.

- Any Racket function can produce an AST, so Racket functions
  essentially act as C macros. However, they are more convenient than
  Racket macros in some ways because they do not need to use things
  like `local-expand` and worry about phase separation in the same
  way.

  I've implemented examples like a for loop that takes an abstract
  iterator and resolves it into a while loop at compile time. It would
  be sensible to implement things like exhaustive matching, generic
  containers, and so on.

  Less obviously, because the library implements C's type system, a
  Racket function can always extract the type of an AST so you can
  implement type-directed macros. An example would be a generic
  printer that inspected the type and generate the appropriate printf
  format string.

  Similarly, exciting DSLs are quite possible that inspect and
  re-arrange the ASTs given to them (or they could use their own
  DSL-specific AST and translate it into this C AST.)

=== TODO

* Look at David Fisher's Ziggurat dissertation again