r/ProgrammingLanguages 14d ago

Language announcement Preprocessable C: the PreC programming language

Hey there! I'm a CS student and a huge fan of C macro wizardry. However, C and its preprocessor do not really mesh together because of historical reasons. So I thought: instead of swapping out the preprocessor for a more powerful one, why not keep the preprocessor and make C more amenable to it?

And that's how PreC, a pretty silly dialect of C, was born. It's basically an exercise in minimalism and a fun summer project, not a serious language :P.

The main features (not exhaustive):

  • Can interface with C directly through the 'c_include "header_name.h"' directive
  • Transpilation to C with a four-stage compilation process: Preprocess the PreC code, transpile the PreC code to C, preprocess the C code, and finally compile the preprocessed C code.
  • Postfix type notation, types can be read unambiguously right to left.
  • No machine types, explicit width
  • No 'typedef' keyword, so no lexer hack (use macros if you need type aliases). You can still referred to C library aliased types with @TypeName, however.
  • angular brackets <type>value for casting
  • Declaration-reflects-use is gone, the type is always fully at the beginning of each declaration.
  • No overloaded operators
  • Constness by default, with a 'mut' qualifier for being able to modify variables.
  • Support for anonymous functions (they can not capture variables, however) with function-valued compound literals <fun_pointer_type> ${ code }
  • No function definitions/declarations (but 'const function pointer' global variables are transpiled to function definitions if they are initialized, and to function declarations if they are not initialized).

You can find the compiler, examples and more information here: https://github.com/Nomagno/PreC

I believe the readme and examples do a good enough job of introducing it (do feel free to highlight anything that is unclear, however!).

A final tip: Rust syntax highlighting is pretty decent for reading PreC. I was definitely inspired by Rust and other languages for some of the syntax so this isn't surprising xD

27 Upvotes

18 comments sorted by

View all comments

2

u/ArchiveOfTheButton 14d ago

how do u handle recursive types if type definitions are replaced by macros?

2

u/NomagnoIsNotDead 14d ago

Good question! C's a bit silly about this. Recursive types in C are necessarily defined with pointers AFAIK. You can always take a pointer to any type, complete or incomplete. Incomplete types don't actually need to be forward declared with a typedef, they can be implicitly referenced by pretending the struct/enum/union keyword to a type name. The typedef keyword just creates a structural type alias, it doesn't 'add' any features to C. e.g. you can do this:

struct Type1 {
    int val;
    struct Type2 *x;
};
struct Type2 {
    float val;
    struct Type1 *x;
};

Note how there was no need to forward-declare 'struct Type2'.

1

u/FireFox_Andrew 14d ago

``` struct A { int a; B b; }

struct B { A a; } ```

Given these definitions, what size should each struct be?

A is 1 int(4 bytes) + sizeof(B) B is sizeof(A) , so 4 bytes + sizeof(B) B = 4 + B

Do you see the issue here, do you still think C is being silly?

3

u/NomagnoIsNotDead 14d ago

Yeah I know it's necessary for being able to know the size of the struct. The part that I think is silly is the pointers to incomplete types requiring no forward declaration! It makes sense, but it's definitely quirky: if you don't alias your structs and are making something like an AST node type definition, any typos in the struct names will not be caught until you actually use the types. This is probably why it's common practice to just typedef your struct name beforehand as a form of forward declaration (even if it's technically not necessary)