Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length

 
Advanced search

1398460 Posts in 67602 Topics- by 60944 Members - Latest Member: rashwinbarwa

January 28, 2022, 04:22:05 AM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)Newfus: friendly little type system which compiles to C type definitions + code
Pages: [1]
Print
Author Topic: Newfus: friendly little type system which compiles to C type definitions + code  (Read 132 times)
bayersglassey
Level 0
***



View Profile
« on: November 28, 2021, 08:56:27 PM »

I write in C a lot (e.g. this "Spider Game"), and it's not a very friendly language for writing data structures, to say the least.
You can do it, but there's a ton of boilerplate involved, and you need good discipline in order to keep using the same patterns, or the code becomes a mess.

So, I'm trying to canonize my coding patterns and automate the writing of them!
The result is newfus, a language with a compiler which allows you to define data structures like this:


package: graphs

# A binary tree is either a branch, or a leaf containing arbitrary data.
union bintree:
    branch: struct:
        left: @bintree
        right: @bintree
    leaf: any


...and compile that to the following C code:

Code:
typedef struct graphs_bintree graphs_bintree_t;
typedef struct graphs_bintree_branch graphs_bintree_branch_t;

enum graphs_bintree_tag {
    GRAPHS_BINTREE_TAG_BRANCH,
    GRAPHS_BINTREE_TAG_LEAF,
    GRAPHS_BINTREE_TAGS
};

struct graphs_bintree {
    int tag; /* enum graphs_bintree_tag */
    union {
        graphs_bintree_branch_t* branch;
        any_t leaf;
    } u;
};

struct graphs_bintree_branch {
    graphs_bintree_t* left;
    graphs_bintree_t* right;
};

...along with automatically generated C functions for basic tasks like memory management, serialization, deserialization, etc. (Although currently only the code generation for memory management -- object "cleanup" -- is implemented.)


The major newfus types are:
  • int
  • bool
  • byte
  • string (i.e. const char *)
  • array of T (compiles to a struct with capacity, length, and pointer to array of elements, plus basic array manipulation functions)
  • alias (compiles to typedef)
  • struct
  • union (compiles to an enum of tags, plus a struct with tag and union)
  • function (compiles to function pointer)
  • type (compiles to const type_t *, which contains real-time type info for newfus types, including function pointers to generic methods for memory management, serialization/deserialization, etc)
  • any (compiles to any_t, a struct including a const type_t *type and void *data, so that the correct generic methods for mem mgmt, serialization etc can be automatically used at runtime)
  • extern CTYPE (compiles to a pointer-to-arbitrary-C-type, so you can refer to C types not defined in newfus)


The features are chosen to include everything I would need to move my existing C code over to this system.
For instance, my spider game's graphics engine is centered around a C struct called rendergraph_t, which forms a tree of nodes, each of which may have a cached SDL surface associated with it.
So you end up with something like this in C:

Code:
typedef struct rendergraph_bitmap {
    bool pbox_calculated;
    position_box_t pbox;
    SDL_Surface *surface;
} rendergraph_bitmap_t;

typedef struct rendergraph {
    const char *name;

    /* etc... */

    int n_bitmaps;
    struct rendergraph_bitmap *bitmaps;

    /* etc... */
} rendergraph_t;

...plus a TON of boilerplate functions.
Now I can just write this:


struct rendergraph:
    name: string

    # etc...

    bitmaps: array: struct rendergraph_bitmap:
        !extra_cleanup # Calling SDL_FreeSurface(surface)
        pbox_calculated: bool
        pbox: inplace extern: position_box_t
        surface: extern: SDL_Surface

    # etc...


...and the vast majority of the boilerplate C code is generated for me, except for freeing the SDL_Surface. For that, I've got the "!extra_cleanup" part, which causes the generated code to expect a C macro "RENDERGRAPH_BITMAP_EXTRA_CLEANUP" to be defined, and which I define in a separate .h file as "if (it->surface) SDL_FreeSurface(it->surface);" and tell the newfus compiler to #include in its generated file.


I have no idea if anyone else would be interested in such a thing, but for me it has been fun, instructive (learned a lot about C's type system...), and possibly useful!
I'm looking forward to no longer having to write a billion lines of C code around managing the memory or printing the contents of structs like this:
Code:
typedef struct rendergraph_child {
    trf_t trf;
    int frame_start;
    int frame_len;

    int type; /* enum rendergraph_child_type */
    union {
        struct {
            Uint8 color;

            /* Weakrefs */
            struct prismel *prismel;
        } prismel;
        struct {
            int frame_i;
            bool frame_i_additive;
            bool frame_i_reversed;
            int palmapper_n_applications;


            /* Weakrefs */
            struct rendergraph *rendergraph;
            struct palettemapper *palmapper;
        } rgraph;
        struct {
            const char *name;
        } label;
    } u;
} rendergraph_child_t;


typedef struct rendergraph {
    /* etc... */

    ARRAY_DECL(rendergraph_child_t, children)

    /* etc... */
} rendergraph_t;

...instead just writing a single definition like this:


struct rendergraph:
    # etc...

    children: array: struct rendergraph_child:
        trf: inplace @trf
        frame_start: int
        frame_len: int

        type: union:
            prismel: struct:
                color: extern: Uint8
                prismel: weakref @prismel
            rgraph: struct:
                frame_i: int
                frame_i_additive: bool
                frame_i_reversed: bool
                palmapper_n_applications: int
                rendergraph: weakref @rendergraph
                palmapper: weakref @palettemapper
            label: struct:
                name: string

    # etc...



For comparison, here is rendergraph.h + rendergraph.c, versus geom.fus (of whose 166 lines the entire rendergraph struct takes up about 60).
« Last Edit: November 28, 2021, 10:34:39 PM by bayersglassey » Logged
Pages: [1]
Print
Jump to:  

Theme orange-lt created by panic