feat: add async/await coroutine support with include system and file I/O to RC language
Add Python-like async/await functionality to the RC interpreter, enabling non-blocking coroutine execution via pthreads. Introduce `async` keyword for function declarations, `await()` for retrieving results, and `gather()` for parallel execution. Extend the tokenizer, parser, interpreter, and native functions with coroutine lifecycle management (init, alloc, thread execution, result retrieval). Also add include system with relative paths, nested includes up to 32 levels, and automatic circular include prevention. Document new features in TUTORIAL.md with examples (async_await_demo.rc) and tests (async_await_test.rc). Update types.h with Coroutine struct, MAX_COROUTINES constant, and new token types (Async, Await).
This commit is contained in:
@@ -17,3 +17,5 @@ char str_pool[STR_POOL_SIZE];
|
||||
int str_pool_idx = 0;
|
||||
long ax = 0;
|
||||
int return_flag = 0;
|
||||
Coroutine coroutines[MAX_COROUTINES];
|
||||
int coroutine_count = 0;
|
||||
|
||||
+13
-5
@@ -241,18 +241,26 @@ void statement() {
|
||||
void scan_functions() {
|
||||
int i = 0;
|
||||
while (i < MAX_TOK && i < tk_idx && tokens[i].type != 0) {
|
||||
if (i + 2 < MAX_TOK && i + 2 < tk_idx &&
|
||||
(tokens[i].type == Int || tokens[i].type == Char || tokens[i].type == Double) &&
|
||||
tokens[i+1].type == Id && tokens[i+2].type == '(') {
|
||||
int is_async = 0;
|
||||
int offset = 0;
|
||||
if (tokens[i].type == Async) {
|
||||
is_async = 1;
|
||||
offset = 1;
|
||||
}
|
||||
|
||||
if (i + 2 + offset < MAX_TOK && i + 2 + offset < tk_idx &&
|
||||
(tokens[i+offset].type == Int || tokens[i+offset].type == Char || tokens[i+offset].type == Double) &&
|
||||
tokens[i+1+offset].type == Id && tokens[i+2+offset].type == '(') {
|
||||
|
||||
if (func_cnt >= 100) {
|
||||
error("Too many functions defined");
|
||||
}
|
||||
Func *f = &funcs[func_cnt++];
|
||||
Token *name = &tokens[i+1];
|
||||
Token *name = &tokens[i+1+offset];
|
||||
strncpy(f->name, name->text, name->val); f->name[name->val] = 0;
|
||||
f->is_async = is_async;
|
||||
|
||||
i += 3;
|
||||
i += 3 + offset;
|
||||
f->params_start = i;
|
||||
int params = 0;
|
||||
while(i < MAX_TOK && i < tk_idx && tokens[i].type != ')') {
|
||||
|
||||
@@ -1035,6 +1035,259 @@ long native_async_result(long *args, int argc) {
|
||||
return result;
|
||||
}
|
||||
|
||||
static pthread_mutex_t coroutine_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static int coroutine_initialized = 0;
|
||||
|
||||
void init_coroutines() {
|
||||
if (!coroutine_initialized) {
|
||||
pthread_mutex_lock(&coroutine_mutex);
|
||||
for (int i = 0; i < MAX_COROUTINES; i++) {
|
||||
coroutines[i].active = 0;
|
||||
coroutines[i].complete = 0;
|
||||
coroutines[i].result = 0;
|
||||
coroutines[i].thread = NULL;
|
||||
}
|
||||
coroutine_count = 0;
|
||||
coroutine_initialized = 1;
|
||||
pthread_mutex_unlock(&coroutine_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
int alloc_coroutine() {
|
||||
init_coroutines();
|
||||
pthread_mutex_lock(&coroutine_mutex);
|
||||
for (int i = 0; i < MAX_COROUTINES; i++) {
|
||||
if (!coroutines[i].active) {
|
||||
coroutines[i].active = 1;
|
||||
coroutines[i].complete = 0;
|
||||
coroutines[i].result = 0;
|
||||
pthread_mutex_unlock(&coroutine_mutex);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&coroutine_mutex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int coroutine_id;
|
||||
} CoroutineThreadData;
|
||||
|
||||
void* coroutine_thread_func(void *arg) {
|
||||
CoroutineThreadData *data = (CoroutineThreadData*)arg;
|
||||
int coro_id = data->coroutine_id;
|
||||
free(data);
|
||||
|
||||
if (coro_id < 0 || coro_id >= MAX_COROUTINES) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Coroutine *coro = &coroutines[coro_id];
|
||||
|
||||
int func_idx = coro->func_idx;
|
||||
int argc = coro->argc;
|
||||
long args[10];
|
||||
for (int i = 0; i < argc && i < 10; i++) {
|
||||
args[i] = coro->args[i];
|
||||
}
|
||||
|
||||
if (func_idx < 0 || func_idx >= func_cnt) {
|
||||
pthread_mutex_lock(&coroutine_mutex);
|
||||
coro->complete = 1;
|
||||
coro->result = 0;
|
||||
pthread_mutex_unlock(&coroutine_mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&coroutine_mutex);
|
||||
|
||||
long saved_memory[MEM_SIZE];
|
||||
Symbol saved_locals[VAR_MAX];
|
||||
int saved_pc, saved_sp, saved_bp, saved_loc_cnt;
|
||||
long saved_ax;
|
||||
int saved_return_flag;
|
||||
|
||||
for (int i = 0; i < MEM_SIZE; i++) {
|
||||
saved_memory[i] = memory[i];
|
||||
}
|
||||
for (int i = 0; i < loc_cnt && i < VAR_MAX; i++) {
|
||||
saved_locals[i] = locals[i];
|
||||
}
|
||||
saved_pc = pc;
|
||||
saved_sp = sp;
|
||||
saved_bp = bp;
|
||||
saved_loc_cnt = loc_cnt;
|
||||
saved_ax = ax;
|
||||
saved_return_flag = return_flag;
|
||||
|
||||
int old_loc_cnt = loc_cnt;
|
||||
|
||||
if (sp >= MEM_SIZE || bp < 0 || bp >= MEM_SIZE) {
|
||||
pthread_mutex_unlock(&coroutine_mutex);
|
||||
coro->complete = 1;
|
||||
coro->result = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memory[sp] = bp;
|
||||
bp = sp++;
|
||||
if (sp >= MEM_SIZE) {
|
||||
pthread_mutex_unlock(&coroutine_mutex);
|
||||
coro->complete = 1;
|
||||
coro->result = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memory[sp++] = 0;
|
||||
memory[sp++] = old_loc_cnt;
|
||||
|
||||
int param_base = sp;
|
||||
for (int i = 0; i < argc && i < 10; i++) {
|
||||
if (sp >= MEM_SIZE) break;
|
||||
memory[sp++] = args[i];
|
||||
}
|
||||
|
||||
int scan_pc = funcs[func_idx].params_start;
|
||||
int param_idx = 0;
|
||||
while (scan_pc < MAX_TOK && scan_pc < tk_idx && tokens[scan_pc].type != ')') {
|
||||
if (tokens[scan_pc].type == Int || tokens[scan_pc].type == Char || tokens[scan_pc].type == Double) {
|
||||
scan_pc++;
|
||||
while (scan_pc < MAX_TOK && tokens[scan_pc].type == '*') scan_pc++;
|
||||
if (scan_pc < MAX_TOK && tokens[scan_pc].type == Id && param_idx < argc && loc_cnt < VAR_MAX) {
|
||||
Token *param_name = &tokens[scan_pc];
|
||||
Symbol *sym = &locals[loc_cnt++];
|
||||
int name_len = param_name->val;
|
||||
if (name_len > 31) name_len = 31;
|
||||
strncpy(sym->name, param_name->text, name_len);
|
||||
sym->name[name_len] = 0;
|
||||
sym->type = Int;
|
||||
sym->addr = param_base + param_idx;
|
||||
sym->is_array = 0;
|
||||
param_idx++;
|
||||
}
|
||||
}
|
||||
scan_pc++;
|
||||
}
|
||||
|
||||
pc = funcs[func_idx].entry_point;
|
||||
return_flag = 0;
|
||||
ax = 0;
|
||||
|
||||
extern void statement();
|
||||
statement();
|
||||
|
||||
long result_val = ax;
|
||||
|
||||
for (int i = 0; i < MEM_SIZE; i++) {
|
||||
memory[i] = saved_memory[i];
|
||||
}
|
||||
for (int i = 0; i < saved_loc_cnt && i < VAR_MAX; i++) {
|
||||
locals[i] = saved_locals[i];
|
||||
}
|
||||
pc = saved_pc;
|
||||
sp = saved_sp;
|
||||
bp = saved_bp;
|
||||
loc_cnt = saved_loc_cnt;
|
||||
ax = saved_ax;
|
||||
return_flag = saved_return_flag;
|
||||
|
||||
coro->result = result_val;
|
||||
coro->complete = 1;
|
||||
|
||||
pthread_mutex_unlock(&coroutine_mutex);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int create_coroutine(int func_idx, long *args, int argc) {
|
||||
int coro_id = alloc_coroutine();
|
||||
if (coro_id < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Coroutine *coro = &coroutines[coro_id];
|
||||
|
||||
pthread_mutex_lock(&coroutine_mutex);
|
||||
coro->func_idx = func_idx;
|
||||
coro->argc = argc;
|
||||
for (int i = 0; i < argc && i < 10; i++) {
|
||||
coro->args[i] = args[i];
|
||||
}
|
||||
pthread_mutex_unlock(&coroutine_mutex);
|
||||
|
||||
CoroutineThreadData *thread_data = malloc(sizeof(CoroutineThreadData));
|
||||
thread_data->coroutine_id = coro_id;
|
||||
|
||||
pthread_t *thread = malloc(sizeof(pthread_t));
|
||||
pthread_create(thread, NULL, coroutine_thread_func, thread_data);
|
||||
|
||||
pthread_mutex_lock(&coroutine_mutex);
|
||||
coro->thread = thread;
|
||||
pthread_mutex_unlock(&coroutine_mutex);
|
||||
|
||||
return coro_id;
|
||||
}
|
||||
|
||||
long native_await(long *args, int argc) {
|
||||
if (!args || argc < 1) return 0;
|
||||
int coro_id = (int)args[0];
|
||||
|
||||
if (coro_id < 0 || coro_id >= MAX_COROUTINES) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Coroutine *coro = &coroutines[coro_id];
|
||||
if (!coro->active) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pthread_t *thread = (pthread_t*)coro->thread;
|
||||
if (thread) {
|
||||
pthread_join(*thread, NULL);
|
||||
free(thread);
|
||||
coro->thread = NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&coroutine_mutex);
|
||||
long result = coro->result;
|
||||
coro->active = 0;
|
||||
pthread_mutex_unlock(&coroutine_mutex);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
long native_gather(long *args, int argc) {
|
||||
if (!args || argc < 1) return 0;
|
||||
|
||||
int coro_ids[100];
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < argc && i < 100; i++) {
|
||||
coro_ids[count++] = (int)args[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
int coro_id = coro_ids[i];
|
||||
if (coro_id < 0 || coro_id >= MAX_COROUTINES) continue;
|
||||
|
||||
Coroutine *coro = &coroutines[coro_id];
|
||||
if (!coro->active) continue;
|
||||
|
||||
pthread_t *thread = (pthread_t*)coro->thread;
|
||||
if (thread) {
|
||||
pthread_join(*thread, NULL);
|
||||
free(thread);
|
||||
coro->thread = NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&coroutine_mutex);
|
||||
coro->active = 0;
|
||||
pthread_mutex_unlock(&coroutine_mutex);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void register_native_functions() {
|
||||
register_native_func("socket", native_socket);
|
||||
register_native_func("bind", native_bind);
|
||||
@@ -1089,4 +1342,6 @@ void register_native_functions() {
|
||||
register_native_func("async_wait", native_async_wait);
|
||||
register_native_func("async_poll", native_async_poll);
|
||||
register_native_func("async_result", native_async_result);
|
||||
register_native_func("await", native_await);
|
||||
register_native_func("gather", native_gather);
|
||||
}
|
||||
|
||||
@@ -3,5 +3,6 @@
|
||||
|
||||
void register_native_func(char *name, NativeFunc func);
|
||||
void register_native_functions();
|
||||
int create_coroutine(int func_idx, long *args, int argc);
|
||||
|
||||
#endif
|
||||
|
||||
+17
-1
@@ -31,7 +31,18 @@ long factor() {
|
||||
Token *t = &tokens[pc];
|
||||
long val = 0;
|
||||
|
||||
if (t->type == Num) {
|
||||
if (t->type == Await) {
|
||||
pc++;
|
||||
match('(');
|
||||
long coro_id = expression();
|
||||
match(')');
|
||||
|
||||
extern long native_await(long*, int);
|
||||
long args[1];
|
||||
args[0] = coro_id;
|
||||
return native_await(args, 1);
|
||||
}
|
||||
else if (t->type == Num) {
|
||||
pc++;
|
||||
return t->val;
|
||||
}
|
||||
@@ -94,6 +105,11 @@ long factor() {
|
||||
}
|
||||
match(')');
|
||||
|
||||
if (funcs[f_idx].is_async) {
|
||||
extern int create_coroutine(int func_idx, long *args, int argc);
|
||||
return create_coroutine(f_idx, args, argc);
|
||||
}
|
||||
|
||||
int ret_pc = pc;
|
||||
int old_loc_cnt = loc_cnt;
|
||||
int old_bp = bp;
|
||||
|
||||
@@ -42,6 +42,8 @@ void tokenize(char *src) {
|
||||
else if (!strcmp(buf, "printf")) t->type = Printf;
|
||||
else if (!strcmp(buf, "break")) t->type = Break;
|
||||
else if (!strcmp(buf, "continue")) t->type = Continue;
|
||||
else if (!strcmp(buf, "async")) t->type = Async;
|
||||
else if (!strcmp(buf, "await")) t->type = Await;
|
||||
else t->type = Id;
|
||||
|
||||
t->val = len;
|
||||
|
||||
+22
-1
@@ -9,7 +9,7 @@
|
||||
|
||||
enum {
|
||||
Num = 128, Dbl, Str, Id, Int, Char, Double, Else, If, While, Return, Printf,
|
||||
Assign, Eq, Ne, Lt, Gt, Le, Ge, Or, And, Inc, Dec, Break, Continue
|
||||
Assign, Eq, Ne, Lt, Gt, Le, Ge, Or, And, Inc, Dec, Break, Continue, Async, Await
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
@@ -31,6 +31,7 @@ typedef struct {
|
||||
int entry_point;
|
||||
int param_count;
|
||||
int params_start;
|
||||
int is_async;
|
||||
} Func;
|
||||
|
||||
typedef long (*NativeFunc)(long*, int);
|
||||
@@ -40,6 +41,24 @@ typedef struct {
|
||||
NativeFunc func;
|
||||
} NativeFuncDef;
|
||||
|
||||
#define MAX_COROUTINES 100
|
||||
|
||||
typedef struct Coroutine {
|
||||
int active;
|
||||
int complete;
|
||||
long result;
|
||||
void *thread;
|
||||
int func_idx;
|
||||
long args[10];
|
||||
int argc;
|
||||
int pc_saved;
|
||||
int sp_saved;
|
||||
int bp_saved;
|
||||
int loc_cnt_saved;
|
||||
Symbol locals_saved[VAR_MAX];
|
||||
long memory_saved[MEM_SIZE];
|
||||
} Coroutine;
|
||||
|
||||
extern Token tokens[MAX_TOK];
|
||||
extern int tk_idx;
|
||||
extern int pc;
|
||||
@@ -57,5 +76,7 @@ extern char str_pool[STR_POOL_SIZE];
|
||||
extern int str_pool_idx;
|
||||
extern long ax;
|
||||
extern int return_flag;
|
||||
extern Coroutine coroutines[MAX_COROUTINES];
|
||||
extern int coroutine_count;
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user