Scramble is a C preprocessor which allows to write C code which looks a bit like Python code with a full Python3 environment available for meta programming. That is, no semantics at all are changed - when using Scramble you still write C code. But it has a syntax more similar to Python.
Similar (and better) projects:
Scramble is used like this:
scramble.py -i input.py -c output.c -h output.h -n name
For example, if you have a file main.py, then you could run:
scramble.py -i src/main.py -c build/c/main.c -h build/h/main.h -n main
And if your src/main.py would look like to the left, the resulting files would look like to the right:
src/main.py import stdio, string, math def main(int argc, char **argv) -> int: if argc == 2: printf("%f\n", sin(strtod(argv[1])) return 0 else: fprintf(stderr, "Need exactly one argument!\n") return 1 |
build/c/main.c #include "main.h" int main(int argc, char **argv) { if (argc == 2) { printf("%f\n", sin(strtod(argv[1])); return 0; } else { fprintf(stderr, "Need exactly one argument!\n"); return 1; } } build/h/main.h #ifndef _MAIN_ #include "stdio.h" #include "string.h" #include "math.h" extern int main(int argc, char **argv); #endif |
All C, C++ and Python keywords basically are also Scramble keywords. The following control flow constructs are used by scramble: while, switch...case, do...while, for X while Y with Z [for (X; Y; Z) in C], for...in, if...elif...else, label [: in C], goto.
And these declarations: class [struct in C], def, enum, global, import, macro [#define in C], static, struct, typedef, union.
These Python operators are used instead of the C++ ones: and [&& in C], max, min, not [! in C], or [|| in C].
And there's a few new constants: True, False, None.
In general, here is what scramble will do:
-
No more ; required.
-
: and indentation instead of { and }.
-
No ( and ) for builtin C keywords like if. For example:
if x == 2: x = 3 y = 3
translates to if (x == 2) { x = 3; y = 2; }
-
Use elif instead of else if, like in python.
-
Use and, or and not inside conditionals, like in python.
-
Functions are declared with def, and parameter types can be grouped. For example:
def f1(): pass def f2(int x, y, z): pass def f3 -> int: pass
translates to void f1(void) { } void f2(int x, int y, int z) { } int f3(void) { }
-
Headers are generated automatically. This works by outputting a declaration for each function or global variable which is not declared static. Similarly, struct/union/enum declarations are written into the header unless declared static, and a type is automatically defined for them. For example:
test.py
class A: int x class B: A *a static class C: B *b static def a_new() -> A*: A *self = calloc(1, sizeof *self) return self def b_new() -> B*: B *self = calloc(1, sizeof *self) self->a = a_new() return self
translates to test.c
#include "test.h" typedef struct C C; struct C { B *b; }; static A *a_new(void) { A *self = calloc(1, sizeof *self); return self; } B *b_new(void) { B *self = calloc(1, sizeof *self); self->a = a_new(); return self; }
and
test.h
#ifndef _TEST_ typedef struct A A; typedef struct B B; struct A { int x; }; struct B { A *a; }; B *a_new(void); #endif
-
Comments are started with #.
-
Include files are included with import. For example:
import test, global stdio
translates to #include "test.h" #include <stdio.h>
-
Meta programming using full Python. This is possibly the single most useful feature - at compile time you have the full Python interpreter at your disposal to create any code you want.
***scramble for x in ["A", "B", "C"]: parse("char def function" + x + "(): return '" + x + "'") ***
translates to char functionA(void) { return 'A'; } char functionB(void) { return 'B'; } char functionC(void) { return 'C'; }
-
Triple quoted strings, useful for multi-line string constants.
-
Support for auto declarations (type is inferred)
-
In many cases you can use . instead of -> (if the type inference sees a pointer left of the . a -> is output instead)
-
Extended for-loop syntax, for example:
MyArray *arr for MyElem *x in arr: handle(x)
translates to MyArrayIterator __iter__ = MyArrayIterator_first(arr); for (MyElem *x = MyArrayIterator_item(arr, &__iter__); MyArrayIterator_next(arr, &__iter__); x = MyArrayIterator_item(arr, &__iter__)) { handle(x); }
-
Docstrings can be used to automatically generate documentation. Strings at the beginning of a function are ignored, but can optionally be output to a separate file, associated with the function they are defined in. This can then be used to translate into different documentation formats.
- None, min, max, True, False keywords. For now min and max are macros for the ternary operator - with the usual problem of evaluating the argument twice.
- As the hash sign starts a comment, *** can be used to insert C preprocessor commands.
- Instead of #define, macro and static macro can be used to place a #define either into the .h or .c file.
- Since the : is used up by Python syntax, labels are marked with the label keyword instead of :. Bitfields use the keyword with. The colon in the C tertiary operator works for now until I implemnt the if-else construct.