Wren

Wren is a small, fast, class-based concurrent scripting language

History

Wren was crated by Bob Nystrom as a follow up for magpie and finch. The idea is to embed this in games etc.

FAQ

What is it?

Embeddable scripting language.

How cool is it?

Very.

Competitors?

lua, cscript

Where is it?

https://wren.io

When to use it?

When you want to have a fast, small, clean, not-reantrant scripting language in your application.

Is it dead?

Nope, There is some activity every few month and a nice mailing list.

How to use it

Init

file: download.sh

#!/bin/bash

mkdir tmp
cd tmp
git clone https://github.com/munificent/wren.git
cd wren
make static

I am taking a different approach then in the examples. Things are further abstracted as a preparation for a manageble big application.

file: main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <wren.h>

typedef enum { PR_FAILURE = -1, PR_SUCCESS = 0 } PRStatus;


#define WRENCODE \
    "var fred = Person.new(\"Fred\")" "\n" \
    "fred.hai()"                      "\n" \
    "\n"
/*
 * load functions for wren
 */

#define RM_END { NULL, NULL }
typedef struct _RegisterMethod_t {
    const char *                name;
    WrenForeignMethodFn         method_cb;
} RegisterMethod_t;


#define RC_END { NULL, NULL, NULL, NULL, NULL, NULL }
typedef struct _registerClass_t {
    const char *                name;
    const WrenForeignMethodFn   allocate_cb;
    const WrenFinalizerFn       finalize_cb;
    const RegisterMethod_t *    staticMethods;
    const RegisterMethod_t *    dynamicMethods;
    const char *                signature_code;
} registerclass_t;

/*
 * 'low' level C object
 */

typedef struct _Person_t {
    char * name;
} Person_t;

PRStatus Person_Set_Name(Person_t * instance, const char *  name);
Person_t * Person_New(const char * name);
PRStatus Person_Init(Person_t * person, const char * name);
const char * Person_Get_Name(const Person_t * instance);
PRStatus Person_Set_Name(Person_t * instance, const char *  name);
void Person_Clear(Person_t * person);
void Person_Delete(Person_t * person);

Person_t * Person_New(const char * name) {
    Person_t * struc ;
    if ((struc = malloc(sizeof(*struc))) != NULL) {
        struc->name = NULL;
        if (Person_Init(struc, name) == PR_SUCCESS) {
            //ok
        } else {
            free(struc); struc = NULL;
        }
    }
    return struc;
}
/*
 * OO style method for the 'low' level C object
 */

PRStatus Person_Init(Person_t * person, const char * name) {
    return Person_Set_Name(person, name);
}

PRStatus Person_Set_Name(Person_t * instance, const char *  name) { \
    PRStatus status;
    if (instance->name != NULL) {
        free(instance->name); instance->name = NULL;
    }
    if (name) {
        status = ((instance->name = strdup(name)) != NULL) ? PR_SUCCESS: PR_FAILURE;
    } else {
        status = PR_SUCCESS;
    }
    return status;
}

const char * Person_Get_Name(const Person_t * instance) {
    return instance->name;
}

void Person_Clear(Person_t * person) {
    if (person->name != NULL) {
        free(person->name); person->name = NULL;
    }
}

void Person_Delete(Person_t * person) {
    Person_Clear(person);
    free(person); person = NULL;
}

/*
 * Interface with wren to match the OO methods and properties
 */

void PersonInstance_New(WrenVM* vm);
void PersonInstance_GetName(WrenVM* vm);
void PersonInstance_SetName(WrenVM* vm);
void PersonInstance_Delete(void* data);

void PersonInstance_New(WrenVM* vm) {
    Person_t * person;
    const char * name;
    //int argc;

    person = wrenSetSlotNewForeign(vm, 0, 0, sizeof(* person));
    //argc = wrenGetSlotCount(vm);
    name = wrenGetSlotString(vm, 1);
    Person_Init(person, name);
}

void PersonInstance_SetName(WrenVM* vm) {
    Person_t * person;
    const char * name;
    bool success;

    person = (Person_t *) wrenGetSlotForeign(vm, 0);
    name = wrenGetSlotString(vm, 1);
    success = (PR_SUCCESS == Person_Set_Name(person, name));

    wrenSetSlotBool(vm, 0, success);
}

void PersonInstance_GetName(WrenVM* vm) {
    Person_t * person;
    const char * name;

    person = (Person_t *) wrenGetSlotForeign(vm, 0);
    name = Person_Get_Name(person);

    wrenSetSlotString(vm, 0, name);
}

void PersonInstance_Delete(void* data) {
    Person_t * person;

    person = (Person_t *) data;
    Person_Clear(person);
}

/*
 * Regstration of the wren interface
 */
static RegisterMethod_t Person_static_methods[] = {
    RM_END
};

static RegisterMethod_t Person_dynamic_methods[] = {
    {"name()", PersonInstance_GetName },
    {"name(_)", PersonInstance_SetName },
    RM_END
};

#define PERSON_SIGNATURE_CODE  \
"foreign class Person {"                                                  "\n" \
"    construct new (name) {}"                                             "\n" \
"    foreign name()"                                                      "\n" \
"    foreign name(name)"                                                  "\n" \
"    say () {"                                                           "\n" \
"        System.print(\"hello i am \" + name())"                               "\n" \
"    }"                                                                   "\n" \
"}"                                                                       "\n"

static registerclass_t registered[] = {
    {"Person",
        PersonInstance_New,
        PersonInstance_Delete,
        Person_static_methods,
        Person_dynamic_methods,
        PERSON_SIGNATURE_CODE },
    /*
     * Register other objects
     */
    RC_END
};

/*
 * Boilerplate
 */

void * reallocate_cb (void* memory, size_t newSize);
WrenForeignMethodFn find_foreign_methods_cb(WrenVM* vm, const char* module, const char* className, bool isStatic, const char* signature);
WrenForeignClassMethods find_foreign_class_cb(WrenVM* vm, const char* module, const char* className);
void error_cb(WrenVM* vm, WrenErrorType type, const char* module, int line, const char* message);

void * reallocate_cb (void* memory, size_t newSize) {
    // this is just for demonstating,
    // the default values do 'the same'
    if (memory == NULL && newSize > 0) {
        memory = calloc(newSize, 1);
    } else if (memory != NULL && newSize > 0) {
        memory = realloc(memory, newSize);
    } else if (memory != NULL && newSize == 0) {
        free(memory); memory = NULL;
    }

    return memory;
}

static void write_cb(WrenVM* vm, const char* text) {
    fprintf(stdout, "%s\n", text);
}

WrenForeignMethodFn find_foreign_methods_cb(WrenVM* vm, const char* module, const char* className, bool isStatic, const char* signature) {
    registerclass_t * reg;
    WrenForeignMethodFn method;
    const RegisterMethod_t * list, * l;

    //printf("Method: mod %s, class %s, static: %s, sign: %s\n", module, className, (isStatic) ? "true": "false", signature);
    method = NULL;
    reg = &registered[0];
    for(;;) {
        if (reg == NULL || reg->name == NULL) {
            break; //end of the list
        } else if (strcmp(className, reg->name) == 0) { //ah, the right classname
            list = (isStatic) ? reg->staticMethods : reg->dynamicMethods;
            l = &list[0];
            for (;;) {
                if (l == NULL || l->name == NULL) { //  end of the list
                    break;
                } else if (strcmp(signature, l->name) == 0) { //ah, the right signature
                    method = l->method_cb;
                    break;
                } else { // try the next one
                    l++;
                }
            }
            break;
        } else {
            reg++;
        }
    }
    return method;
}

WrenForeignClassMethods find_foreign_class_cb(WrenVM* vm, const char* module, const char* className) {
    WrenForeignClassMethods structor;
    registerclass_t * reg;

    structor.allocate = NULL;
    structor.finalize = NULL;
    //printf("Class : mod %s, class %s\n", module, className);
    reg = registered;
    for(;;) {
        if (reg == NULL || reg->name == NULL) { // end of the list
            break;
        } else if (strcmp(className, reg->name) == 0) { //ah, the right classname
            structor.allocate = reg->allocate_cb;
            structor.finalize = reg->finalize_cb;
            break;
        } else { //try the next one
            reg++;
        }
    }

    return structor;
}

void error_cb(WrenVM* vm, WrenErrorType type, const char* module, int line, const char* message) {
    const char * ts;

    switch (type) {
    //default:
    case WREN_ERROR_RUNTIME:
            ts = "Runtime error";
            break;
      case WREN_ERROR_COMPILE:
            ts = "Compile error";
            break;
      case WREN_ERROR_STACK_TRACE:
            ts = "Stack error";
            break;
    }
    fprintf(stderr, "%s:%05d [%s]\t %s\n", module, line, ts, message);
}

/*
 * Main
 */
int main(const int argc, const char **argv) {
    WrenConfiguration config;
    WrenInterpretResult result;
    WrenVM* vm;

    wrenInitConfiguration(&config);
    config.writeFn = write_cb;
    config.errorFn = error_cb;
    config.bindForeignMethodFn = find_foreign_methods_cb;
    config.bindForeignClassFn = find_foreign_class_cb;
    config.reallocateFn = reallocate_cb;
    //config.loadModuleFn = load_module_cb;

    vm = wrenNewVM(&config);
    if ((result = wrenInterpret(vm, WRENCODE)) != WREN_RESULT_SUCCESS) {
        fprintf(stderr, "Something went wrong\n");
    }

    wrenFreeVM(vm);

    return 0;
}

file: compile_and_run.sh

#!/bin/bash

clang \
    -Wall -Wextra -Weverything \
    -Wno-padded \
    -Wno-unused-parameter \
    -o ./tmp/main main.c \
    -I./tmp/wren/src/include/ \
    -L./tmp/wren/lib -lwren \
    -lm
./tmp/main

main:00001 [Compile error]	 Error at 'Person': Variable is used but not defined.
Something went wrong`

Further reading:

We only tipped the top of the iceberg here. There is much more:

The documentation on the website and in the source is good.

Have a look at src/include/wren.h, /doc, /example, /test/api.