~lattis/muon

ref: 49a50d56c57dfdf470b4bcbffc16bc5ec92dcb4e muon/src/functions/environment.c -rw-r--r-- 3.0 KiB
49a50d56Stone Tickle support comma seperated option array 8 months ago
                                                                                
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
#include "posix.h"

#include "functions/common.h"
#include "functions/environment.h"
#include "lang/interpreter.h"
#include "log.h"

static enum iteration_result
typecheck_environment_dict_iter(struct workspace *wk, void *_ctx, obj key, obj val)
{
	uint32_t *err_node = _ctx;

	enum obj_type t = get_obj(wk, val)->type;
	if (t != obj_string) {
		interp_error(wk, *err_node, "all values in environment dict must be strings, got: %s", obj_type_to_s(t));
		return ir_err;
	}

	return ir_cont;
}

bool
typecheck_environment_dict(struct workspace *wk, uint32_t err_node, obj dict)
{
	return obj_dict_foreach(wk, dict, &err_node, typecheck_environment_dict_iter);
}

enum environment_set_mode {
	environment_set_mode_set,
	environment_set_mode_append,
	environment_set_mode_prepend,
};

static bool
environment_set_common(struct workspace *wk, obj rcvr, uint32_t args_node, enum environment_set_mode mode)
{
	struct args_norm an[] = { { obj_string }, { ARG_TYPE_GLOB }, ARG_TYPE_NULL };
	enum kwargs {
		kw_separator,
	};
	struct args_kw akw[] = {
		[kw_separator] = { "separator", obj_string },
		0
	};
	if (!interp_args(wk, args_node, an, NULL, akw)) {
		return false;
	}

	obj dict = get_obj(wk, rcvr)->dat.environment.env,
	    key = an[0].val,
	    value = an[1].val,
	    sep;

	if (akw[kw_separator].set) {
		sep = akw[kw_separator].val;
	} else {
		sep = make_str(wk, ":");
	}

	assert(get_obj(wk, value)->type == obj_array);
	if (!get_obj(wk, value)->dat.arr.len) {
		interp_error(wk, an[0].node, "you must pass at least one value");
		return false;
	}

	obj joined;
	if (!obj_array_join(wk, value, sep, &joined)) {
		return false;
	}

	obj orig;
	if (!obj_dict_index(wk, dict, key, &orig)) {
		obj_dict_set(wk, dict, key, joined);
		return true;
	}

	obj head, tail;

	switch (mode) {
	case environment_set_mode_set:
		obj_dict_set(wk, dict, key, joined);
		return true;
	case environment_set_mode_append:
		head = orig;
		tail = joined;
		break;
	case environment_set_mode_prepend:
		head = joined;
		tail = orig;
		break;
	default:
		head = 0; tail = 0;
		assert(false && "unreachable");
		return false;
	}

	obj new_val;
	make_obj(wk, &new_val, obj_string)->dat.str =
		wk_str_pushf(wk, "%s%s%s", get_cstr(wk, head), get_cstr(wk, sep), get_cstr(wk, tail));

	obj_dict_set(wk, dict, key, new_val);
	return true;
}

static bool
func_environment_set(struct workspace *wk, obj rcvr, uint32_t args_node, obj *res)
{
	return environment_set_common(wk, rcvr, args_node, environment_set_mode_set);
}

static bool
func_environment_append(struct workspace *wk, obj rcvr, uint32_t args_node, obj *res)
{
	return environment_set_common(wk, rcvr, args_node, environment_set_mode_append);
}

static bool
func_environment_prepend(struct workspace *wk, obj rcvr, uint32_t args_node, obj *res)
{
	return environment_set_common(wk, rcvr, args_node, environment_set_mode_prepend);
}

const struct func_impl_name impl_tbl_environment[] = {
	{ "set", func_environment_set },
	{ "append", func_environment_append },
	{ "prepend", func_environment_prepend },
	{ NULL, NULL },
};