mirror of
https://github.com/janet-lang/janet
synced 2024-12-26 00:10:27 +00:00
Add readline to repl for better experience
This commit is contained in:
parent
fb3abf5a31
commit
5845434529
2
Makefile
2
Makefile
@ -3,7 +3,7 @@
|
|||||||
######################################################
|
######################################################
|
||||||
##### Set global variables for all gst Makefiles #####
|
##### Set global variables for all gst Makefiles #####
|
||||||
######################################################
|
######################################################
|
||||||
CFLAGS=-std=c99 -Wall -Wextra -Wpedantic -I./include -g
|
CFLAGS=-std=c99 -Wall -Wextra -Wpedantic -I./include -g -lreadline
|
||||||
PREFIX=/usr/local
|
PREFIX=/usr/local
|
||||||
GST_TARGET=client/gst
|
GST_TARGET=client/gst
|
||||||
GST_CORELIB=core/libgst.a
|
GST_CORELIB=core/libgst.a
|
||||||
|
@ -28,6 +28,10 @@
|
|||||||
#include <gst/stl.h>
|
#include <gst/stl.h>
|
||||||
#include <gst/disasm.h>
|
#include <gst/disasm.h>
|
||||||
|
|
||||||
|
/* Use readline support for now */
|
||||||
|
#include <readline/readline.h>
|
||||||
|
#include <readline/history.h>
|
||||||
|
|
||||||
/* Compile and run an ast */
|
/* Compile and run an ast */
|
||||||
int debug_compile_and_run(Gst *vm, GstValue ast, GstValue env) {
|
int debug_compile_and_run(Gst *vm, GstValue ast, GstValue env) {
|
||||||
GstCompiler c;
|
GstCompiler c;
|
||||||
@ -43,9 +47,9 @@ int debug_compile_and_run(Gst *vm, GstValue ast, GstValue env) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
/* Print disasm */
|
/* Print disasm */
|
||||||
printf("%c[31m===== Begin Disassembly =====\n", 27);
|
/*printf("%c[31m===== Begin Disassembly =====\n", 27);*/
|
||||||
gst_dasm_function(stdout, func.data.function);
|
/*gst_dasm_function(stdout, func.data.function);*/
|
||||||
printf("===== End Disassembly =====%c[0m\n", 27);
|
/*printf("===== End Disassembly =====%c[0m\n", 27);*/
|
||||||
/* Execute function */
|
/* Execute function */
|
||||||
if (gst_run(vm, func)) {
|
if (gst_run(vm, func)) {
|
||||||
if (vm->crash) {
|
if (vm->crash) {
|
||||||
@ -102,23 +106,20 @@ int debug_run(Gst *vm, FILE *in) {
|
|||||||
|
|
||||||
/* A simple repl */
|
/* A simple repl */
|
||||||
int debug_repl(Gst *vm) {
|
int debug_repl(Gst *vm) {
|
||||||
char buffer[1024] = {0};
|
const char *buffer, *reader;
|
||||||
const char *reader = buffer;
|
|
||||||
GstParser p;
|
GstParser p;
|
||||||
GstValue *st;
|
GstValue *st;
|
||||||
int reset;
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
/* Init parser */
|
/* Init parser */
|
||||||
gst_parser(&p, vm);
|
gst_parser(&p, vm);
|
||||||
reset = 1;
|
buffer = reader = NULL;
|
||||||
while (p.status != GST_PARSER_ERROR && p.status != GST_PARSER_FULL) {
|
while (p.status != GST_PARSER_ERROR && p.status != GST_PARSER_FULL) {
|
||||||
if (*reader == '\0') {
|
gst_parse_cstring(&p, "\n");
|
||||||
if (reset)
|
if (p.status == GST_PARSER_ERROR || p.status == GST_PARSER_FULL)
|
||||||
printf(">> ");
|
break;
|
||||||
reset = 0;
|
if (!reader || *reader == '\0') {
|
||||||
if (!fgets(buffer, sizeof(buffer), stdin)) {
|
buffer = readline(">> ");
|
||||||
break;
|
add_history(buffer);
|
||||||
}
|
|
||||||
reader = buffer;
|
reader = buffer;
|
||||||
}
|
}
|
||||||
reader += gst_parse_cstring(&p, reader);
|
reader += gst_parse_cstring(&p, reader);
|
||||||
|
@ -66,7 +66,8 @@ static uint32_t dasm_varg_op(FILE * out, const uint16_t * current,
|
|||||||
dasm_print_slot(out, current[i + 1]);
|
dasm_print_slot(out, current[i + 1]);
|
||||||
}
|
}
|
||||||
argCount = current[extra + 1];
|
argCount = current[extra + 1];
|
||||||
fprintf(out, ": "); /* Argument separator */
|
if (extra)
|
||||||
|
fprintf(out, ": "); /* Argument separator */
|
||||||
for (i = 0; i < argCount; ++i) {
|
for (i = 0; i < argCount; ++i) {
|
||||||
dasm_print_slot(out, current[i + extra + 2]);
|
dasm_print_slot(out, current[i + extra + 2]);
|
||||||
}
|
}
|
||||||
|
63
core/stl.c
63
core/stl.c
@ -168,6 +168,68 @@ int gst_stl_select(Gst *vm) {
|
|||||||
gst_c_return(vm, gst_arg(vm, n + 1));
|
gst_c_return(vm, gst_arg(vm, n + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get a slice of a sequence */
|
||||||
|
int gst_stl_slice(Gst *vm) {
|
||||||
|
uint32_t count = gst_count_args(vm);
|
||||||
|
int32_t from, to;
|
||||||
|
GstValue x;
|
||||||
|
const GstValue *data;
|
||||||
|
uint32_t length;
|
||||||
|
uint32_t newlength;
|
||||||
|
GstNumber num;
|
||||||
|
|
||||||
|
/* Check args */
|
||||||
|
if (count < 1)
|
||||||
|
gst_c_throwc(vm, "slice takes at least one argument");
|
||||||
|
x = gst_arg(vm, 0);
|
||||||
|
|
||||||
|
/* Get data */
|
||||||
|
if (x.type == GST_TUPLE) {
|
||||||
|
data = x.data.st;
|
||||||
|
length = gst_tuple_length(x.data.st);
|
||||||
|
} else if (x.type == GST_ARRAY) {
|
||||||
|
data = x.data.array->data;
|
||||||
|
length = x.data.array->count;
|
||||||
|
} else {
|
||||||
|
gst_c_throwc(vm, "expected array or tuple");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get from index */
|
||||||
|
if (count < 2) {
|
||||||
|
from = 0;
|
||||||
|
} else {
|
||||||
|
if (!gst_check_number(vm, 1, &num))
|
||||||
|
gst_c_throwc(vm, GST_EXPECTED_NUMBER_OP);
|
||||||
|
from = gst_to_index(num, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get to index */
|
||||||
|
if (count < 3) {
|
||||||
|
to = length;
|
||||||
|
} else {
|
||||||
|
if (!gst_check_number(vm, 2, &num))
|
||||||
|
gst_c_throwc(vm, GST_EXPECTED_NUMBER_OP);
|
||||||
|
to = gst_to_endrange(num, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check from bad bounds */
|
||||||
|
if (from < 0 || to < 0)
|
||||||
|
gst_c_throwc(vm, "index out of bounds");
|
||||||
|
|
||||||
|
/* Build slice */
|
||||||
|
newlength = to - from;
|
||||||
|
if (x.type == GST_TUPLE) {
|
||||||
|
GstValue *tup = gst_tuple_begin(vm, newlength);
|
||||||
|
gst_memcpy(tup, data + from, newlength * sizeof(GstValue));
|
||||||
|
gst_c_return(vm, gst_wrap_tuple(gst_tuple_end(vm, tup)));
|
||||||
|
} else {
|
||||||
|
GstArray *arr = gst_array(vm, newlength);
|
||||||
|
arr->count = newlength;
|
||||||
|
gst_memcpy(arr->data, data + from, newlength * sizeof(GstValue));
|
||||||
|
gst_c_return(vm, gst_wrap_array(arr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Get type of object */
|
/* Get type of object */
|
||||||
int gst_stl_type(Gst *vm) {
|
int gst_stl_type(Gst *vm) {
|
||||||
GstValue x;
|
GstValue x;
|
||||||
@ -446,6 +508,7 @@ static const GstModuleItem const std_module[] = {
|
|||||||
{"length", gst_stl_length},
|
{"length", gst_stl_length},
|
||||||
{"type", gst_stl_type},
|
{"type", gst_stl_type},
|
||||||
{"select", gst_stl_select},
|
{"select", gst_stl_select},
|
||||||
|
{"slice", gst_stl_slice},
|
||||||
{"array", gst_stl_array},
|
{"array", gst_stl_array},
|
||||||
{"tuple", gst_stl_tuple},
|
{"tuple", gst_stl_tuple},
|
||||||
{"object", gst_stl_object},
|
{"object", gst_stl_object},
|
||||||
|
57
core/util.c
57
core/util.c
@ -31,13 +31,25 @@ GstValue gst_wrap_nil() {
|
|||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int gst_check_nil(Gst *vm, uint32_t i) {
|
||||||
|
GstValue a = gst_arg(vm, i);
|
||||||
|
return a.type == GST_NIL;
|
||||||
|
}
|
||||||
|
|
||||||
#define GST_WRAP_DEFINE(NAME, TYPE, GTYPE, UM)\
|
#define GST_WRAP_DEFINE(NAME, TYPE, GTYPE, UM)\
|
||||||
GstValue gst_wrap_##NAME(TYPE x) {\
|
GstValue gst_wrap_##NAME(TYPE x) {\
|
||||||
GstValue y;\
|
GstValue y;\
|
||||||
y.type = GTYPE;\
|
y.type = GTYPE;\
|
||||||
y.data.UM = x;\
|
y.data.UM = x;\
|
||||||
return y;\
|
return y;\
|
||||||
}
|
}\
|
||||||
|
\
|
||||||
|
int gst_check_##NAME(Gst *vm, uint32_t i, TYPE (*out)) {\
|
||||||
|
GstValue a = gst_arg(vm, i);\
|
||||||
|
if (a.type != GTYPE) return 0;\
|
||||||
|
*out = a.data.UM;\
|
||||||
|
return 1;\
|
||||||
|
}\
|
||||||
|
|
||||||
GST_WRAP_DEFINE(number, GstNumber, GST_NUMBER, number)
|
GST_WRAP_DEFINE(number, GstNumber, GST_NUMBER, number)
|
||||||
GST_WRAP_DEFINE(boolean, int, GST_BOOLEAN, boolean)
|
GST_WRAP_DEFINE(boolean, int, GST_BOOLEAN, boolean)
|
||||||
@ -109,3 +121,46 @@ GstValue gst_register_get(Gst *vm, const char *name) {
|
|||||||
return gst_wrap_nil();
|
return gst_wrap_nil();
|
||||||
return gst_object_get(vm->registry, gst_string_cv(vm, name));
|
return gst_object_get(vm->registry, gst_string_cv(vm, name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/****/
|
||||||
|
/* Misc */
|
||||||
|
/****/
|
||||||
|
|
||||||
|
/* Allow negative indexing to get from end of array like structure */
|
||||||
|
/* This probably isn't very fast - look at Lua conversion function.
|
||||||
|
* I would like to keep this standard C for as long as possible, though. */
|
||||||
|
int32_t gst_to_index(GstNumber raw, int64_t len) {
|
||||||
|
int32_t toInt = raw;
|
||||||
|
if ((GstNumber) toInt == raw) {
|
||||||
|
/* We were able to convert */
|
||||||
|
if (toInt < 0 && len > 0) {
|
||||||
|
/* Index from end */
|
||||||
|
if (toInt < -len) return -1;
|
||||||
|
return len + toInt;
|
||||||
|
} else {
|
||||||
|
/* Normal indexing */
|
||||||
|
if (toInt >= len) return -1;
|
||||||
|
return toInt;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t gst_to_endrange(GstNumber raw, int64_t len) {
|
||||||
|
int32_t toInt = raw;
|
||||||
|
if ((GstNumber) toInt == raw) {
|
||||||
|
/* We were able to convert */
|
||||||
|
if (toInt < 0 && len > 0) {
|
||||||
|
/* Index from end */
|
||||||
|
if (toInt < -len - 1) return -1;
|
||||||
|
return len + toInt + 1;
|
||||||
|
} else {
|
||||||
|
/* Normal indexing */
|
||||||
|
if (toInt >= len) return -1;
|
||||||
|
return toInt;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
33
core/value.c
33
core/value.c
@ -236,27 +236,6 @@ int gst_compare(GstValue x, GstValue y) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allow negative indexing to get from end of array like structure */
|
|
||||||
/* This probably isn't very fast - look at Lua conversion function.
|
|
||||||
* I would like to keep this standard C for as long as possible, though. */
|
|
||||||
static int32_t to_index(GstNumber raw, int64_t len) {
|
|
||||||
int32_t toInt = raw;
|
|
||||||
if ((GstNumber) toInt == raw) {
|
|
||||||
/* We were able to convert */
|
|
||||||
if (toInt < 0 && len > 0) {
|
|
||||||
/* Index from end */
|
|
||||||
if (toInt < -len) return -1;
|
|
||||||
return len + toInt;
|
|
||||||
} else {
|
|
||||||
/* Normal indexing */
|
|
||||||
if (toInt >= len) return -1;
|
|
||||||
return toInt;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Convert a number into a byte. */
|
/* Convert a number into a byte. */
|
||||||
static uint8_t to_byte(GstNumber raw) {
|
static uint8_t to_byte(GstNumber raw) {
|
||||||
if (raw > 255) return 255;
|
if (raw > 255) return 255;
|
||||||
@ -273,26 +252,26 @@ const char *gst_get(GstValue ds, GstValue key, GstValue *out) {
|
|||||||
switch (ds.type) {
|
switch (ds.type) {
|
||||||
case GST_ARRAY:
|
case GST_ARRAY:
|
||||||
if (key.type != GST_NUMBER) return "expected numeric key";
|
if (key.type != GST_NUMBER) return "expected numeric key";
|
||||||
index = to_index(key.data.number, ds.data.array->count);
|
index = gst_to_index(key.data.number, ds.data.array->count);
|
||||||
if (index == -1) return "invalid array access";
|
if (index == -1) return "invalid array access";
|
||||||
ret = ds.data.array->data[index];
|
ret = ds.data.array->data[index];
|
||||||
break;
|
break;
|
||||||
case GST_TUPLE:
|
case GST_TUPLE:
|
||||||
if (key.type != GST_NUMBER) return "expected numeric key";
|
if (key.type != GST_NUMBER) return "expected numeric key";
|
||||||
index = to_index(key.data.number, gst_tuple_length(ds.data.tuple));
|
index = gst_to_index(key.data.number, gst_tuple_length(ds.data.tuple));
|
||||||
if (index < 0) return "invalid tuple access";
|
if (index < 0) return "invalid tuple access";
|
||||||
ret = ds.data.tuple[index];
|
ret = ds.data.tuple[index];
|
||||||
break;
|
break;
|
||||||
case GST_BYTEBUFFER:
|
case GST_BYTEBUFFER:
|
||||||
if (key.type != GST_NUMBER) return "expected numeric key";
|
if (key.type != GST_NUMBER) return "expected numeric key";
|
||||||
index = to_index(key.data.number, ds.data.buffer->count);
|
index = gst_to_index(key.data.number, ds.data.buffer->count);
|
||||||
if (index == -1) return "invalid buffer access";
|
if (index == -1) return "invalid buffer access";
|
||||||
ret.type = GST_NUMBER;
|
ret.type = GST_NUMBER;
|
||||||
ret.data.number = ds.data.buffer->data[index];
|
ret.data.number = ds.data.buffer->data[index];
|
||||||
break;
|
break;
|
||||||
case GST_STRING:
|
case GST_STRING:
|
||||||
if (key.type != GST_NUMBER) return "expected numeric key";
|
if (key.type != GST_NUMBER) return "expected numeric key";
|
||||||
index = to_index(key.data.number, gst_string_length(ds.data.string));
|
index = gst_to_index(key.data.number, gst_string_length(ds.data.string));
|
||||||
if (index == -1) return "invalid string access";
|
if (index == -1) return "invalid string access";
|
||||||
ret.type = GST_NUMBER;
|
ret.type = GST_NUMBER;
|
||||||
ret.data.number = ds.data.string[index];
|
ret.data.number = ds.data.string[index];
|
||||||
@ -317,14 +296,14 @@ const char *gst_set(Gst *vm, GstValue ds, GstValue key, GstValue value) {
|
|||||||
switch (ds.type) {
|
switch (ds.type) {
|
||||||
case GST_ARRAY:
|
case GST_ARRAY:
|
||||||
if (key.type != GST_NUMBER) return "expected numeric key";
|
if (key.type != GST_NUMBER) return "expected numeric key";
|
||||||
index = to_index(key.data.number, ds.data.array->count);
|
index = gst_to_index(key.data.number, ds.data.array->count);
|
||||||
if (index == -1) return "invalid array access";
|
if (index == -1) return "invalid array access";
|
||||||
ds.data.array->data[index] = value;
|
ds.data.array->data[index] = value;
|
||||||
break;
|
break;
|
||||||
case GST_BYTEBUFFER:
|
case GST_BYTEBUFFER:
|
||||||
if (key.type != GST_NUMBER) return "expected numeric key";
|
if (key.type != GST_NUMBER) return "expected numeric key";
|
||||||
if (value.type != GST_NUMBER) return "expected numeric value";
|
if (value.type != GST_NUMBER) return "expected numeric value";
|
||||||
index = to_index(key.data.number, ds.data.buffer->count);
|
index = gst_to_index(key.data.number, ds.data.buffer->count);
|
||||||
if (index == -1) return "invalid buffer access";
|
if (index == -1) return "invalid buffer access";
|
||||||
ds.data.buffer->data[index] = to_byte(value.data.number);
|
ds.data.buffer->data[index] = to_byte(value.data.number);
|
||||||
break;
|
break;
|
||||||
|
23
core/vm.c
23
core/vm.c
@ -310,18 +310,6 @@ int gst_continue(Gst *vm) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GST_OP_YLD: /* Yield to new thread */
|
|
||||||
temp = stack[pc[1]];
|
|
||||||
v1 = stack[pc[2]];
|
|
||||||
gst_assert(vm, v1.type == GST_THREAD, "expected thread");
|
|
||||||
gst_assert(vm, v1.data.thread->status != GST_THREAD_DEAD, "cannot rejoin dead thread");
|
|
||||||
gst_frame_pc(stack) = pc + 3;
|
|
||||||
vm->thread = v1.data.thread;
|
|
||||||
vm->thread->status = GST_THREAD_ALIVE;
|
|
||||||
stack = vm->thread->data + vm->thread->count;
|
|
||||||
pc = gst_frame_pc(stack);
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Faster implementations of standard functions
|
/* Faster implementations of standard functions
|
||||||
* These opcodes are nto strictlyre required and can
|
* These opcodes are nto strictlyre required and can
|
||||||
* be reimplemented with stanard library functions */
|
* be reimplemented with stanard library functions */
|
||||||
@ -444,6 +432,17 @@ int gst_continue(Gst *vm) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case GST_OP_YLD: /* Yield to new thread */
|
||||||
|
temp = stack[pc[1]];
|
||||||
|
v1 = stack[pc[2]];
|
||||||
|
gst_assert(vm, v1.type == GST_THREAD, "expected thread");
|
||||||
|
gst_assert(vm, v1.data.thread->status != GST_THREAD_DEAD, "cannot rejoin dead thread");
|
||||||
|
gst_frame_pc(stack) = pc + 3;
|
||||||
|
vm->thread = v1.data.thread;
|
||||||
|
vm->thread->status = GST_THREAD_ALIVE;
|
||||||
|
stack = vm->thread->data + vm->thread->count;
|
||||||
|
pc = gst_frame_pc(stack);
|
||||||
|
continue;
|
||||||
|
|
||||||
/* Handle errors from c functions and vm opcodes */
|
/* Handle errors from c functions and vm opcodes */
|
||||||
vm_error:
|
vm_error:
|
||||||
|
@ -503,9 +503,9 @@ GstValue gst_arg(Gst *vm, uint16_t index);
|
|||||||
void gst_set_arg(Gst *vm, uint16_t index, GstValue x);
|
void gst_set_arg(Gst *vm, uint16_t index, GstValue x);
|
||||||
uint16_t gst_count_args(Gst *vm);
|
uint16_t gst_count_args(Gst *vm);
|
||||||
|
|
||||||
/***/
|
/****/
|
||||||
/* C Api */
|
/* C Api */
|
||||||
/***/
|
/****/
|
||||||
|
|
||||||
GstValue gst_cmodule_object(Gst *vm, const GstModuleItem *mod);
|
GstValue gst_cmodule_object(Gst *vm, const GstModuleItem *mod);
|
||||||
GstValue gst_cmodule_struct(Gst *vm, const GstModuleItem *mod);
|
GstValue gst_cmodule_struct(Gst *vm, const GstModuleItem *mod);
|
||||||
@ -531,4 +531,28 @@ GstValue gst_wrap_userdata(void *x);
|
|||||||
GstValue gst_wrap_funcenv(GstFuncEnv *x);
|
GstValue gst_wrap_funcenv(GstFuncEnv *x);
|
||||||
GstValue gst_wrap_funcdef(GstFuncDef *x);
|
GstValue gst_wrap_funcdef(GstFuncDef *x);
|
||||||
|
|
||||||
|
/* Check data from arguments */
|
||||||
|
int gst_check_nil(Gst *vm, uint32_t i);
|
||||||
|
int gst_check_number(Gst *vm, uint32_t i, GstNumber (*x));
|
||||||
|
int gst_check_boolean(Gst *vm, uint32_t i, int (*x));
|
||||||
|
int gst_check_string(Gst *vm, uint32_t i, const uint8_t *(*x));
|
||||||
|
int gst_check_array(Gst *vm, uint32_t i, GstArray *(*x));
|
||||||
|
int gst_check_tuple(Gst *vm, uint32_t i, const GstValue *(*x));
|
||||||
|
int gst_check_struct(Gst *vm, uint32_t i, const GstValue *(*x));
|
||||||
|
int gst_check_thread(Gst *vm, uint32_t i, GstThread *(*x));
|
||||||
|
int gst_check_buffer(Gst *vm, uint32_t i, GstBuffer *(*x));
|
||||||
|
int gst_check_function(Gst *vm, uint32_t i, GstFunction *(*x));
|
||||||
|
int gst_check_cfunction(Gst *vm, uint32_t i, GstCFunction (*x));
|
||||||
|
int gst_check_object(Gst *vm, uint32_t i, GstObject *(*x));
|
||||||
|
int gst_check_userdata(Gst *vm, uint32_t i, void *(*x));
|
||||||
|
int gst_check_funcenv(Gst *vm, uint32_t i, GstFuncEnv *(*x));
|
||||||
|
int gst_check_funcdef(Gst *vm, uint32_t i, GstFuncDef *(*x));
|
||||||
|
|
||||||
|
/****/
|
||||||
|
/* Misc */
|
||||||
|
/****/
|
||||||
|
|
||||||
|
int32_t gst_to_index(GstNumber raw, int64_t len);
|
||||||
|
int32_t gst_to_endrange(GstNumber raw, int64_t len);
|
||||||
|
|
||||||
#endif // GST_H_defined
|
#endif // GST_H_defined
|
||||||
|
Loading…
Reference in New Issue
Block a user