summaryrefslogtreecommitdiff
path: root/src/wren_inter/wren_inter.c
blob: 868b2fe1a47d823deb8b7d27c572eb344102a09f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#include "wren_inter.h"
#include "../utils/stringBuilder.h"
#include <stddef.h>
#include <string.h> 
#include <raylib.h>
#include <stdio.h>
#include <wren.h>

void writeFn(WrenVM* vm, const char* text) {
    StringBuilder* sb = (StringBuilder*)wrenGetUserData(vm);
    AppendStringBuilder(sb, text);
}
 
void errorFn(WrenVM* vm, WrenErrorType errorType,
        const char* module, const int line,
        const char* msg)
{
    StringBuilder* sb = (StringBuilder*)wrenGetUserData(vm);
    switch (errorType)
    {
        case WREN_ERROR_COMPILE:
            AppendStringBuilder(sb, "Comptime error: ");
            AppendStringBuilder(sb, msg);
            break;
        case WREN_ERROR_STACK_TRACE:
            AppendStringBuilder(sb, "Stack trace: ");
            AppendStringBuilder(sb, msg);
            break;
        case WREN_ERROR_RUNTIME:
            AppendStringBuilder(sb, "Runtime error: ");
            AppendStringBuilder(sb, msg);
            break;
    }
}

// Obsolete
char *EvaluateScript(char *script) {
    TraceLog(LOG_INFO, "Evaluating %s...", script); 
    WrenVM *vm = InterpreterPrepareMachine();

    if (vm == NULL) {
        TraceLog(LOG_ERROR, "Failed to initialize a wren vm");
        return NULL;
    }

    WrenInterpretResult result = wrenInterpret(vm, "main", script);
    switch (result) {
        case WREN_RESULT_SUCCESS: break;
        case WREN_RESULT_COMPILE_ERROR: break;
        case WREN_RESULT_RUNTIME_ERROR: break;
    }

    char *outp = GetMachineOutput(vm);
    InterpreterFreeMachine(vm);

    return outp;
}

WrenVM *InterpreterPrepareMachine() {
    WrenConfiguration config;
    wrenInitConfiguration(&config);
    config.writeFn = writeFn;
    config.errorFn = errorFn;

    StringBuilder *stdout_sb = calloc(1, sizeof(StringBuilder));
    config.userData = stdout_sb;
    return wrenNewVM(&config);
}

char *GetMachineOutput(WrenVM *vm) {
    StringBuilder* sb = (StringBuilder*)wrenGetUserData(vm);
    if (sb == NULL || sb->val == NULL) return NULL;
    return strdup(sb->val);
}

void InterpreterFreeMachine(WrenVM *vm) {
    StringBuilder* sb = (StringBuilder*)wrenGetUserData(vm);
    if (sb != NULL && sb->val != NULL) free(sb);
    wrenFreeVM(vm);
}

// ========== Async ==========

typedef struct {
    // The data that you want to access in the worker
    void *data;
    // Toggle this to true when the task is finished
    bool is_done;
} Task;

bool RunWithCancellation(Task *task, void *(*runner) (void *), int timeout);

#if defined(unix) || defined(__unix__) || defined(__unix)

#include <threads.h>
#include <pthread.h>
#include <unistd.h>

// Returns false if the process was aborted
bool RunWithCancellation(Task *task, void *(*runner) (void *), int timeout) {
    pthread_t thread_id;
    if (pthread_create(&thread_id, NULL, runner, &task) != 0) {
        TraceLog(LOG_ERROR, "Failed to create a pthread");
        return RESULT_INIT_ERROR;
    }

    int elapsed = 0;
    while (!task->is_done && elapsed < timeout) {
        sleep(1);
        elapsed += 1;
    }

    if (!task->is_done) {
        pthread_cancel(thread_id); 
        pthread_join(thread_id, NULL);
        return false;
    }

    return true;
}

#endif

// The task that the interpreter would need to run. Made for async reasons
typedef struct {
    WrenVM *vm;
    const char *module;
    const char *script;
    WrenInterpretResult result;
    bool is_done;
} wrenTask;

// Runs the wrenTask asynchronously
// FIX: Inject the cancellation parameters inside the worker thread definition
void *wren_worker(void *arg) {
    int old_type;
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old_type); //

    Task *task_raw = (Task*)arg;
    wrenTask *task = (wrenTask*)task_raw->data;
    task->result = wrenInterpret(task->vm, task->module, task->script);
    task_raw->is_done = true;
    return NULL;
}


enum PUZZLE_TEST_RESULT wrenInterpretLimited(WrenVM* vm, const char* module,
                                  const char* script, int sMax) {
    
    wrenTask *data = malloc(sizeof(wrenTask));
    data->vm = vm;
    data->module = module;
    data->script = script;
    data->result = 0;

    Task *task = malloc(sizeof(Task));
    task->is_done = false;
    task->data = data;

    if (!RunWithCancellation(task, wren_worker, sMax)) {
        return RESULT_TIMEOUT;
    }

    WrenInterpretResult interpret_res = data->result;

    free(data);
    free(task);

    switch (interpret_res) {
        case WREN_RESULT_COMPILE_ERROR: 
            return RESULT_COMPTIME_ERR;
        case WREN_RESULT_RUNTIME_ERROR:
            return RESULT_RUNTIME_ERR;
        default: break;
    }
    return RESULT_OK;
}