Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion subprojects/vinumc/eval.c
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,38 @@ DO_CALLS_FUNC_SIGNATURE(do_calls_call) {
find_symbol_on_scopes(&ctx->scopes, curr_scope, symbol_node->text);

// expose context to external function
struct _call_ctx cctx = { .text = tmp_out.base };
struct _call_ctx cctx = { .text = tmp_out.base, .status = false };

struct return_value call_return = symbol_info->as.func(&cctx);
if (call_return.status == false) {
size_t node;
const struct ast_node *args_node = &VUT_VEC_AT(&ast->nodes, args_id);
size_t len = cctx.args_req.len;
for (size_t i = 0; i < len; i++) {
node = VUT_VEC_AT(&args_node->childs, VUT_VEC_POP(&cctx.args_req));
const struct ast_node *arg_node = &VUT_VEC_AT(&ast->nodes, node);
if (arg_node->type == ASSIGNMENT) {
node = VUT_VEC_AT(&arg_node->childs, 1);
}
struct vut_str arg_out = {};
VUT_VEC_PUT(&arg_out, '\0');
do_calls(ctx, ast, &arg_out, node, flags);
VUT_VEC_PUT(&cctx.args, arg_out.base);
}
len = cctx.names_req.len;
for (size_t i = 0; i < len; i++) {
struct namespace_entry symbol = *find_symbol_on_scopes(
&ctx->scopes, curr_scope, VUT_VEC_POP(&cctx.names_req));
node = symbol.as.ast_node_id;
struct vut_str name_out = {};
VUT_VEC_PUT(&name_out, '\0');
do_calls(ctx, ast, &name_out, node, flags);
VUT_VEC_PUT(&cctx.names, name_out.base);
}

cctx.status = true;
call_return = symbol_info->as.func(&cctx);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't call the the function twice on a single vinum call.

What if the external function is not idempotent? For example, a function that adds a single entry to a database: If we call it on vinum code, two entries could be added instead of just a single one.

Is it possible to pre-compute the args before calling the external function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to pre-compute the args before calling the external function?

This is the biggest change, so I'll focus on it first. Precomputing the arguments is possible, which is how I did it before, but for example, to search for a name in the namespace, we would have to provide the scope and the function to search for symbols.
The current method allows for flexibility, without having to provide the internal functions to the external library. To ensure it's idempotent, I thought about requiring the external library to check if ctx.status is true before performing the operations.
I would like to discuss this thoroughly before making any changes, since it alters the entire implementation.
In any case, suggestions are welcome.

}

// put the extern function call return on the out str

Expand Down
2 changes: 2 additions & 0 deletions subprojects/vinumc/include/vinumc/extern_library.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,7 @@ struct loaded_lib {
};

struct return_value ctx_get_text(call_ctx ctx);
struct return_value ctx_call(call_ctx ctx);
struct return_value ctx_get_index(call_ctx ctx, int index);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No way of knowing the total amount of arguments.
No way of accessing individual parameters by name.

int ctx_get_arg_count(call_ctx ctx);
call_ctx_or_null get_arg_by_name(call_ctx ctx);


#endif //__EXTERN_LIBRARY_H__
7 changes: 5 additions & 2 deletions subprojects/vinumc/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,15 @@ install_subdir(
install_dir: 'include',
)

vutils_dep = dependency('vutils', fallback : ['vutils', 'vutils_dep'])

extern_library = library(
'extern_library',
'v_lib.c',
include_directories: local_inc,
dependencies: [
vutils_dep,
],
install: true
)

Expand All @@ -63,8 +68,6 @@ extern_library_dep = declare_dependency(
include_directories: external_inc,
)

vutils_dep = dependency('vutils', fallback : ['vutils', 'vutils_dep'])

vinumc = executable(
'vinumc',
vinumc_srcs,
Expand Down
29 changes: 26 additions & 3 deletions subprojects/vinumc/tests/library_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,38 @@
void test_call(struct vunit_test_ctx *ctx) {
char *out = NULL;

vunit_run_vinumc_ok(ctx, "[return_arg test]\n", &out, "--with",
vunit_run_vinumc_ok(ctx, "[return_text test]\n", &out, "--with",
"subprojects/vinumc/tests/libtestlib.so", NULL);

VUNIT_ASSERT_STREQ(ctx, out, "test");
}

void test_arg_call(struct vunit_test_ctx *ctx) {
char *out = NULL;

vunit_run_vinumc_ok(ctx,
"[hello: world!]\n"
"[return_text hello]\n"
"[return_call hello]\n",
&out, "--with", "subprojects/vinumc/tests/libtestlib.so", NULL);

VUNIT_ASSERT_STREQ(ctx, out, "helloworld!");
}

void test_arg_by_index(struct vunit_test_ctx *ctx) {
char *out = NULL;

vunit_run_vinumc_ok(ctx, "[invert [a:2][b:1]]\n", &out, "--with",
"subprojects/vinumc/tests/libtestlib.so", NULL);

VUNIT_ASSERT_STREQ(ctx, out, "1 2");
}

void test_nested_call(struct vunit_test_ctx *ctx) {
char *out = NULL;

vunit_run_vinumc_ok(ctx,
"[a: This is a [return_arg Test!]!]\n"
"[a: This is a [return_text Test!]!]\n"
"[a]\n",
&out, "--with", "subprojects/vinumc/tests/libtestlib.so", NULL);

Expand All @@ -23,7 +44,7 @@ void test_nested_call(struct vunit_test_ctx *ctx) {
void test_empty_nested_call(struct vunit_test_ctx *ctx) {
char *out = NULL;

vunit_run_vinumc_ok(ctx, "[return_arg [return_arg [return_arg]]]", &out, "--with",
vunit_run_vinumc_ok(ctx, "[return_text [return_text [return_text]]]", &out, "--with",
"subprojects/vinumc/tests/libtestlib.so", NULL);

VUNIT_ASSERT_STREQ(ctx, out, "");
Expand All @@ -40,6 +61,8 @@ void test_call_no_args(struct vunit_test_ctx *ctx) {

struct vunit_test tests[] = {
{ .name = "Test extern library call", .test_func = test_call },
{ .name = "Test extern library get by index", .test_func = test_arg_by_index },
{ .name = "Test extern library call get text and get arg", .test_func = test_arg_call },
{ .name = "Test extern library call inside call", .test_func = test_nested_call },
{ .name = "Test nested empty library calls", .test_func = test_empty_nested_call },
{
Expand Down
23 changes: 21 additions & 2 deletions subprojects/vinumc/tests/testlib.c
Original file line number Diff line number Diff line change
@@ -1,12 +1,29 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <vinumc/extern_library.h>

struct return_value return_arg(call_ctx ctx) {
struct return_value return_text(call_ctx ctx) {
return ctx_get_text(ctx);
}

struct return_value return_call(call_ctx ctx) {
return ctx_call(ctx);
}

struct return_value invert(call_ctx ctx) {
struct return_value ret = {};
ret = ctx_get_index(ctx, 0);
char *a = ret.ptr;
ret = ctx_get_index(ctx, 1);
char *b = ret.ptr;
if (ret.status) {
sprintf(ret.ptr, "%s %s", b, a);
}
return ret;
}

struct return_value parenthesize(call_ctx ctx) {
struct return_value ret = ctx_get_text(ctx);
if (!ret.status) {
Expand All @@ -26,8 +43,10 @@ struct return_value parenthesize(call_ctx ctx) {
}

struct extern_function *expose_library() {
static struct extern_function lib[] = { { "return_arg", return_arg },
static struct extern_function lib[] = { { "return_text", return_text },
{ "return_call", return_call },
{ "parenthesize", parenthesize },
{ "invert", invert },
{} };
return lib;
}
18 changes: 18 additions & 0 deletions subprojects/vinumc/v_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,21 @@
struct return_value ctx_get_text(call_ctx ctx) {
return (struct return_value){ .ptr = ctx->text, .status = true };
}

struct return_value ctx_call(call_ctx ctx) {
if (ctx->status == true) {
return (struct return_value){ .ptr = VUT_VEC_POP(&ctx->names), .status = true };
}
Comment on lines +10 to +12
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to check if ctx->names is not empty.

struct return_value ret = { .status = false };
VUT_VEC_PUT(&ctx->names_req, ctx->text);
return ret;
}

struct return_value ctx_get_index(call_ctx ctx, int index) {
if (ctx->status == true) {
return (struct return_value){ .ptr = VUT_VEC_POP(&ctx->args), .status = true };
}
Comment on lines +19 to +21
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No proper range checking.

struct return_value ret = { .status = false };
VUT_VEC_PUT(&ctx->args_req, index);
return ret;
}
12 changes: 12 additions & 0 deletions subprojects/vinumc/v_lib.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
#ifndef __V_LIB_H__
#define __V_LIB_H__

#include <stdbool.h>
#include <vutils/vec.h>

struct v_str_vec VUT_VEC_DEF(char *);
struct arg_vec VUT_VEC_DEF(int);
struct name_vec VUT_VEC_DEF(char *);
Comment on lines 5 to 11
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like using vutils in the header. This adds an obligatory dependency to the developer of the external lib.

To help things, you could create a vec macro that gives you the base pointer and length to put it in the ctx.

Something like that:

int* ptr;
size_t len;
VUT_VEC_GET_BASE_ARRAY(&vec, ptr, len)


struct _call_ctx {
char *text;
struct v_str_vec args;
struct v_str_vec names;
struct arg_vec args_req;
struct name_vec names_req;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing doc comments for added fields

bool status;
};

#endif //__V_LIB_H__
9 changes: 2 additions & 7 deletions subprojects/vutils/include/vutils/vec.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,8 @@
(vec)->capacity = 0; \
} while (0)

/// Decrements the length of the vector by one.
#define VUT_VEC_POP(arr) \
do { \
if ((arr)->len > 0) { \
(arr)->len--; \
} \
} while (0)
/// Returns the last element and decrements the length of the vector by one.
#define VUT_VEC_POP(arr) ((arr)->len > 0 ? (arr)->base[--(arr)->len] : (arr)->base[0])
Comment on lines +70 to +71
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returns (possibly) garbage values when the vector is empty, may be better to read the last element before popping. You could create a VUT_VEC_LAST macro.


/// Will append `from_size` elements from `from` to `to`.
/// It will reserve enough space for `from_size + 1` elements in `to`.
Expand Down
8 changes: 7 additions & 1 deletion subprojects/vutils_tests/vec_macros.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,16 @@ void test_vec_pop(struct vunit_test_ctx *ctx) {
VUNIT_ASSERT_EQ(ctx, vec.len, 3);
VUNIT_ASSERT_EQ(ctx, VUT_VEC_AT(&vec, 2), 3);

VUT_VEC_POP(&vec);
int popped = VUT_VEC_POP(&vec);
VUNIT_ASSERT_EQ(ctx, popped, 3);
VUNIT_ASSERT_EQ(ctx, vec.len, 2);
VUNIT_ASSERT_EQ(ctx, VUT_VEC_AT(&vec, 1), 2);

popped = VUT_VEC_POP(&vec);
VUNIT_ASSERT_EQ(ctx, popped, 2);
popped = VUT_VEC_POP(&vec);
VUNIT_ASSERT_EQ(ctx, popped, 1);

VUT_VEC_FREE(&vec);
}

Expand Down