Racket-made C
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