Skip to content

drpriver/DrC

Repository files navigation

Table of Contents

DrC

DrC is a C Preprocessor/Parser/Interpreter targeting C2y with a boatload of extensions. It can interpret real C projects (including itself) and has seamless interfacing with native dynamic libraries. #include a header and #pragma lib it and you can call native C functions from your interpreted C code.

A REPL mode is also offered.

After building, try it out with Bin/drc Samples/hello.c or peruse the other Samples.

Major Features Include:

Building

Nobuild

This project uses a "nobuild" build system. Bootstrap the build system by doing something like:

$ drc build.c -o build && ./build -b Bin && ./build tests

From then on, you can just build using the build program. If you edit it or any of its deps, it will rebuild itself.

The build system remembers the previous flags, like -O. If you change those or switch them off, it knows to rebuild any programs built with those flags.

See build.c for more info.

Makefile

If you like using make, the makefile will bootstrap the build system for you.

build.bat

You can bootstrap the build system on windows using build.bat. This is purely convenience.

Warning: windows is untested and probably doesn't compile.

Dependencies

Depends on libffi. Assumes you can just do -lffi and that just works. If you are on windows, that sucks. It will work there but getting it so it "just werks" on windows is not worth the hassle.

C Interpreter

C scripts, top level statements, no separate compile and run steps, no intermediate files.

REPL mode

Run Bin/drc with --repl for a REPL experience, with interactive function calls etc.

$ Bin/drc --repl
cc> #include <stdio.h>
... int x = 3;
... for(int i = 0; i < 4; i++) printf("x = %d\n", x);
...
x = 3
x = 3
x = 3
x = 3
cc> ^D

Bindings-Free FFI

#include real C headers, #pragma lib to load them. Also supports #pragma pkg_config to populate include paths, lib paths etc.

#pragma pkg_config "sdl2" // could ifdef for non-pkg-config systems
#pragma lib "SDL2" // load the library
#include <SDL2/SDL.h> <SDL.h> // extension, tries each in order
// top level statements
SDL_Init(SDL_INIT_VIDEO);
// Call SDL funcs from your scripts now!

Native Threads

Related to above, you can call actual system APIs to create threads with interpreted code.

#include <stdio.h>
#include <pthread.h>
pthread_t t;
pthread_create(&t, NULL, void*(void* arg){
  printf("Hello from a thread! %p\n", (void*)pthread_self());
  return NULL;
}, NULL);
pthread_join(t, NULL);

This example also shows off our captureless-lambda extension.

First Class Types

_Type T = int; printf("%s\n", T.name), etc. Pass them as values, reflect over them, see the json demo

Static If

Include or exclude statements/decls based on compile time expressions.

static if(__shell("date")[0] == 'W'){
  enum {IT_IS_WEDNESDAY=1};
}
printf("IT_IS_WEDNESDAY: %d\n", IT_IS_WEDNESDAY); // only compiles on wednesday

More seriously:

#define S_(x) #x
#define S(x) S_(x)
// __ident produces a single identifier token
// from catted strings, even if it has non-identifier characters.
#define Optional(T) __ident("Optional." S(T))
#defblock OPTIONAL_DEF(T)
typedef struct Optional(T) Optional(T);
static if(T.is_pointer){
    // use NULL as sentinel
    struct Optional(T){
        T value;
        _Bool has_value(Optional(T)* self){ return self.value; }
    };
}
else {
    struct Optional(T){
        T value;
        _Bool _has_value;
        _Bool has_value(Optional(T)* self){ return self._has_value; }
    };
}
#endblock
OPTIONAL_DEF(const char*);
OPTIONAL_DEF(int);
_Static_assert(sizeof(Optional(const char*)) == sizeof(const char*));
_Static_assert(sizeof(Optional(int)) > sizeof(int));

Unlike #if, static if has access to C-level information without the boilerplate of a procmacro.

Procmacros

Register a C function as a macro, call it as a macro, outputs get turned into pp-tokens, you can mix them into your source file for code generation and other things.

Also, power up preprocessor conditionals:

_Bool IS_POINTER(_Type T){ return T.is_pointer;}
#pragma procmacro IS_POINTER

#define T void*
#if IS_POINTER(T)
#pragma message T "is a pointer"
#else
#pragma message T "is not a pointer"
#endif

#undef T
#define T int
#if IS_POINTER(T)
#pragma message T "is a pointer"
#else
#pragma message T "is not a pointer"
#endif

Methods and FUCS

Define methods in struct body, auto deref, . and -> merged, FUCS: Function Uniform Call Syntax to have .func notation with free functions.

Named Arguments

Same syntax as designated initializers.

int func_with_lotta_bools(_Bool jump, _Bool skip, _Bool hop);
func_with_lotta_bools(.hop = 1, .jump = 0, .skip = 1); // out of order

Numbered args are also supported, but honestly aren't that useful, but whatever.

Crazy Preprocessor

There's a bunch of cool preprocessor macros like __mixin, __map, #defblock. There's also an API for registering your own builtin macros if you are embedding this interpreter.

Embeddable

This will support being embedded. WIP. No global state.

Self Hosting

The C interpreter is able to run itself.

$ Bin/drc cc.c Samples/hello.c
Hello world
$ Bin/drc cc.c cc.c Samples/hello.c
Hello world

About

C2y Compiler/Interpreter/REPL with lotta extensions

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages