jsonnet 0.1.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,206 @@
1
+ #include <libjsonnet.h>
2
+ #include <ruby/ruby.h>
3
+ #include <ruby/intern.h>
4
+
5
+ #include "ruby_jsonnet.h"
6
+
7
+ static struct JsonnetJsonValue *protect_obj_to_json(struct JsonnetVm *vm, VALUE obj,
8
+ struct JsonnetJsonValue *parent);
9
+
10
+ /**
11
+ * Converts a Jsonnet JSON value into a Ruby object.
12
+ *
13
+ * Arrays and objects in JSON are not supported due to the limitation of
14
+ * libjsonnet API.
15
+ */
16
+ VALUE
17
+ rubyjsonnet_json_to_obj(struct JsonnetVm *vm, const struct JsonnetJsonValue *value)
18
+ {
19
+ union {
20
+ const char *str;
21
+ double num;
22
+ } typed_value;
23
+
24
+ if ((typed_value.str = jsonnet_json_extract_string(vm, value))) {
25
+ return rb_enc_str_new_cstr(typed_value.str, rb_utf8_encoding());
26
+ }
27
+ if (jsonnet_json_extract_number(vm, value, &typed_value.num)) {
28
+ return DBL2NUM(typed_value.num);
29
+ }
30
+
31
+ switch (jsonnet_json_extract_bool(vm, value)) {
32
+ case 0:
33
+ return Qfalse;
34
+ case 1:
35
+ return Qtrue;
36
+ case 2:
37
+ break;
38
+ default:
39
+ /* never happens */
40
+ rb_raise(rb_eRuntimeError, "unrecognized json bool value");
41
+ }
42
+ if (jsonnet_json_extract_null(vm, value)) {
43
+ return Qnil;
44
+ }
45
+
46
+ /* TODO: support arrays and objects when they get accessible */
47
+ rb_raise(rb_eArgError, "unsupported type of JSON value");
48
+ }
49
+
50
+ static struct JsonnetJsonValue *
51
+ string_to_json(struct JsonnetVm *vm, VALUE str)
52
+ {
53
+ rubyjsonnet_assert_asciicompat(str);
54
+ return jsonnet_json_make_string(vm, RSTRING_PTR(str));
55
+ }
56
+
57
+ static struct JsonnetJsonValue *
58
+ num_to_json(struct JsonnetVm *vm, VALUE n)
59
+ {
60
+ return jsonnet_json_make_number(vm, NUM2DBL(n));
61
+ }
62
+
63
+ static struct JsonnetJsonValue *
64
+ ary_to_json(struct JsonnetVm *vm, VALUE ary)
65
+ {
66
+ struct JsonnetJsonValue *const json_array = jsonnet_json_make_array(vm);
67
+
68
+ int i;
69
+ for (i = 0; i < RARRAY_LEN(ary); ++i) {
70
+ struct JsonnetJsonValue *const v = protect_obj_to_json(vm, RARRAY_AREF(ary, i), json_array);
71
+ jsonnet_json_array_append(vm, json_array, v);
72
+ }
73
+ return json_array;
74
+ }
75
+
76
+ struct hash_to_json_params {
77
+ struct JsonnetVm *vm;
78
+ struct JsonnetJsonValue *obj;
79
+ };
80
+
81
+ static int
82
+ hash_item_to_json(VALUE key, VALUE value, VALUE paramsval)
83
+ {
84
+ const struct hash_to_json_params *const params = (const struct hash_to_json_params *)paramsval;
85
+
86
+ StringValue(key);
87
+ rubyjsonnet_assert_asciicompat(key);
88
+
89
+ jsonnet_json_object_append(params->vm, params->obj, StringValueCStr(key),
90
+ protect_obj_to_json(params->vm, value, params->obj));
91
+
92
+ return ST_CONTINUE;
93
+ }
94
+
95
+ static struct JsonnetJsonValue *
96
+ hash_to_json(struct JsonnetVm *vm, VALUE hash)
97
+ {
98
+ struct JsonnetJsonValue *const json_obj = jsonnet_json_make_object(vm);
99
+ const struct hash_to_json_params args = {vm, json_obj};
100
+ rb_hash_foreach(hash, hash_item_to_json, (VALUE)&args);
101
+ return json_obj;
102
+ }
103
+
104
+ /**
105
+ * Converts a Ruby object into a Jsonnet JSON value
106
+ *
107
+ * TODO(yugui): Safely destorys an intermediate object on exception.
108
+ */
109
+ static struct JsonnetJsonValue *
110
+ obj_to_json(struct JsonnetVm *vm, VALUE obj)
111
+ {
112
+ VALUE converted;
113
+
114
+ switch (obj) {
115
+ case Qnil:
116
+ return jsonnet_json_make_null(vm);
117
+ case Qtrue:
118
+ return jsonnet_json_make_bool(vm, 1);
119
+ case Qfalse:
120
+ return jsonnet_json_make_bool(vm, 0);
121
+ }
122
+
123
+ converted = rb_check_string_type(obj);
124
+ if (converted != Qnil) {
125
+ return string_to_json(vm, converted);
126
+ }
127
+
128
+ converted = rb_check_to_float(obj);
129
+ if (converted != Qnil) {
130
+ return num_to_json(vm, converted);
131
+ }
132
+
133
+ converted = rb_check_array_type(obj);
134
+ if (converted != Qnil) {
135
+ return ary_to_json(vm, converted);
136
+ }
137
+
138
+ converted = rb_check_hash_type(obj);
139
+ if (converted != Qnil) {
140
+ return hash_to_json(vm, converted);
141
+ }
142
+
143
+ converted = rb_any_to_s(obj);
144
+ return string_to_json(vm, converted);
145
+ }
146
+
147
+ struct protect_args {
148
+ struct JsonnetVm *vm;
149
+ VALUE obj;
150
+ };
151
+
152
+ static VALUE
153
+ protect_obj_to_json_block(VALUE paramsval)
154
+ {
155
+ const struct protect_args *const params = (const struct protect_args *)paramsval;
156
+ return (VALUE)obj_to_json(params->vm, params->obj);
157
+ }
158
+
159
+ /**
160
+ * Safely converts a Ruby object into a JSON value.
161
+ *
162
+ * It automatically destroys \a parent on exception.
163
+ * @param[in] vm a Jsonnet VM
164
+ * @param[in] obj a Ruby object to be converted
165
+ * @param[in] parent destroys this value on failure
166
+ * @throws can throw \c TypeError or other exceptions
167
+ */
168
+ static struct JsonnetJsonValue *
169
+ protect_obj_to_json(struct JsonnetVm *vm, VALUE obj, struct JsonnetJsonValue *parent)
170
+ {
171
+ const struct protect_args args = {vm, obj};
172
+ int state = 0;
173
+
174
+ VALUE result = rb_protect(protect_obj_to_json_block, (VALUE)&args, &state);
175
+ if (!state) {
176
+ return (struct JsonnetJsonValue *)result;
177
+ }
178
+
179
+ jsonnet_json_destroy(vm, parent);
180
+ rb_jump_tag(state);
181
+ }
182
+
183
+ /**
184
+ * Converts a Ruby object into a JSON value.
185
+ * Returns an error message on failure.
186
+ *
187
+ * @param[in] vm a Jsonnet VM
188
+ * @param[in] obj a Ruby object to be converted
189
+ * @param[out] success set to 1 on success, set to 0 on failure.
190
+ * @returns the converted value on success, an error message on failure.
191
+ */
192
+ struct JsonnetJsonValue *
193
+ rubyjsonnet_obj_to_json(struct JsonnetVm *vm, VALUE obj, int *success)
194
+ {
195
+ int state = 0;
196
+ const struct protect_args args = {vm, obj};
197
+ VALUE result = rb_protect(protect_obj_to_json_block, (VALUE)&args, &state);
198
+ if (state) {
199
+ const VALUE msg = rubyjsonnet_format_exception(rb_errinfo());
200
+ rb_set_errinfo(Qnil);
201
+ *success = 0;
202
+ return string_to_json(vm, msg);
203
+ }
204
+ *success = 1;
205
+ return (struct JsonnetJsonValue *)result;
206
+ }
@@ -0,0 +1,39 @@
1
+ #ifndef RUBY_JSONNET_RUBY_JSONNET_H_
2
+ #define RUBY_JSONNET_RUBY_JSONNET_H_
3
+
4
+ #include <ruby/ruby.h>
5
+ #include <ruby/encoding.h>
6
+
7
+ extern const rb_data_type_t jsonnet_vm_type;
8
+
9
+ struct native_callback_ctx {
10
+ VALUE callback;
11
+ long arity;
12
+ VALUE vm;
13
+ };
14
+
15
+ struct jsonnet_vm_wrap {
16
+ struct JsonnetVm *vm;
17
+
18
+ VALUE import_callback;
19
+ struct {
20
+ long len;
21
+ struct native_callback_ctx **contexts;
22
+ } native_callbacks;
23
+ };
24
+
25
+ void rubyjsonnet_init_vm(VALUE mod);
26
+ void rubyjsonnet_init_callbacks(VALUE cVM);
27
+ void rubyjsonnet_init_helpers(VALUE mod);
28
+
29
+ struct jsonnet_vm_wrap *rubyjsonnet_obj_to_vm(VALUE vm);
30
+
31
+ VALUE rubyjsonnet_json_to_obj(struct JsonnetVm *vm, const struct JsonnetJsonValue *value);
32
+ struct JsonnetJsonValue *rubyjsonnet_obj_to_json(struct JsonnetVm *vm, VALUE obj, int *success);
33
+
34
+ rb_encoding *rubyjsonnet_assert_asciicompat(VALUE str);
35
+ char *rubyjsonnet_str_to_cstr(struct JsonnetVm *vm, VALUE str);
36
+ VALUE rubyjsonnet_format_exception(VALUE exc);
37
+ int rubyjsonnet_jump_tag(const char *exc_mesg);
38
+
39
+ #endif /* RUBY_JSONNET_RUBY_JSONNET_H_ */
@@ -0,0 +1,508 @@
1
+ #include <libjsonnet.h>
2
+ #ifdef HAVE_LIBJSONNET_FMT_H
3
+ # include <libjsonnet_fmt.h>
4
+ #endif
5
+ #include <ruby/ruby.h>
6
+ #include <ruby/intern.h>
7
+
8
+ #include "ruby_jsonnet.h"
9
+
10
+ /*
11
+ * defines the core part of Jsonnet::VM
12
+ */
13
+
14
+ /*
15
+ * Jsonnet evaluator
16
+ *
17
+ * call-seq:
18
+ * Jsonnet::VM
19
+ */
20
+ static VALUE cVM;
21
+
22
+ /*
23
+ * Raised on evaluation errors in a Jsonnet VM.
24
+ */
25
+ static VALUE eEvaluationError;
26
+ static VALUE eFormatError;
27
+
28
+ static void raise_eval_error(struct JsonnetVm *vm, char *msg, rb_encoding *enc);
29
+ static void raise_format_error(struct JsonnetVm *vm, char *msg, rb_encoding *enc);
30
+ static VALUE str_new_json(struct JsonnetVm *vm, char *json, rb_encoding *enc);
31
+ static VALUE fileset_new(struct JsonnetVm *vm, char *buf, rb_encoding *enc);
32
+
33
+ static void vm_free(void *ptr);
34
+ static void vm_mark(void *ptr);
35
+
36
+ const rb_data_type_t jsonnet_vm_type = {
37
+ "JsonnetVm",
38
+ {
39
+ /* dmark = */ vm_mark,
40
+ /* dfree = */ vm_free,
41
+ /* dsize = */ 0,
42
+ },
43
+ /* parent = */ 0,
44
+ /* data = */ 0,
45
+ /* flags = */ RUBY_TYPED_FREE_IMMEDIATELY,
46
+ };
47
+
48
+ struct jsonnet_vm_wrap *
49
+ rubyjsonnet_obj_to_vm(VALUE wrap)
50
+ {
51
+ struct jsonnet_vm_wrap *vm;
52
+ TypedData_Get_Struct(wrap, struct jsonnet_vm_wrap, &jsonnet_vm_type, vm);
53
+
54
+ return vm;
55
+ }
56
+
57
+ static VALUE
58
+ vm_s_new(int argc, const VALUE *argv, VALUE klass)
59
+ {
60
+ struct jsonnet_vm_wrap *vm;
61
+ VALUE self = TypedData_Make_Struct(cVM, struct jsonnet_vm_wrap, &jsonnet_vm_type, vm);
62
+ vm->vm = jsonnet_make();
63
+ vm->import_callback = Qnil;
64
+ vm->native_callbacks.len = 0;
65
+ vm->native_callbacks.contexts = NULL;
66
+
67
+ rb_obj_call_init(self, argc, argv);
68
+ return self;
69
+ }
70
+
71
+ static void
72
+ vm_free(void *ptr)
73
+ {
74
+ int i;
75
+ struct jsonnet_vm_wrap *vm = (struct jsonnet_vm_wrap *)ptr;
76
+ jsonnet_destroy(vm->vm);
77
+
78
+ for (i = 0; i < vm->native_callbacks.len; ++i) {
79
+ struct native_callback_ctx *ctx = vm->native_callbacks.contexts[i];
80
+ RB_REALLOC_N(ctx, struct native_callback_ctx, 0);
81
+ }
82
+ RB_REALLOC_N(vm->native_callbacks.contexts, struct native_callback_ctx *, 0);
83
+
84
+ RB_REALLOC_N(vm, struct jsonnet_vm_wrap, 0);
85
+ }
86
+
87
+ static void
88
+ vm_mark(void *ptr)
89
+ {
90
+ int i;
91
+ struct jsonnet_vm_wrap *vm = (struct jsonnet_vm_wrap *)ptr;
92
+
93
+ rb_gc_mark(vm->import_callback);
94
+ for (i = 0; i < vm->native_callbacks.len; ++i) {
95
+ rb_gc_mark(vm->native_callbacks.contexts[i]->callback);
96
+ }
97
+ }
98
+
99
+ static VALUE
100
+ vm_evaluate_file(VALUE self, VALUE fname, VALUE encoding, VALUE multi_p)
101
+ {
102
+ int error;
103
+ char *result;
104
+ rb_encoding *const enc = rb_to_encoding(encoding);
105
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
106
+
107
+ FilePathValue(fname);
108
+ if (RTEST(multi_p)) {
109
+ result = jsonnet_evaluate_file_multi(vm->vm, StringValueCStr(fname), &error);
110
+ } else {
111
+ result = jsonnet_evaluate_file(vm->vm, StringValueCStr(fname), &error);
112
+ }
113
+
114
+ if (error) {
115
+ raise_eval_error(vm->vm, result, rb_enc_get(fname));
116
+ }
117
+ return RTEST(multi_p) ? fileset_new(vm->vm, result, enc) : str_new_json(vm->vm, result, enc);
118
+ }
119
+
120
+ static VALUE
121
+ vm_evaluate(VALUE self, VALUE snippet, VALUE fname, VALUE multi_p)
122
+ {
123
+ int error;
124
+ char *result;
125
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
126
+
127
+ rb_encoding *enc = rubyjsonnet_assert_asciicompat(StringValue(snippet));
128
+ FilePathValue(fname);
129
+ if (RTEST(multi_p)) {
130
+ result = jsonnet_evaluate_snippet_multi(vm->vm, StringValueCStr(fname),
131
+ StringValueCStr(snippet), &error);
132
+ } else {
133
+ result = jsonnet_evaluate_snippet(vm->vm, StringValueCStr(fname), StringValueCStr(snippet),
134
+ &error);
135
+ }
136
+
137
+ if (error) {
138
+ raise_eval_error(vm->vm, result, rb_enc_get(fname));
139
+ }
140
+ return RTEST(multi_p) ? fileset_new(vm->vm, result, enc) : str_new_json(vm->vm, result, enc);
141
+ }
142
+
143
+ #define vm_bind_variable(type, self, key, val) \
144
+ do { \
145
+ struct jsonnet_vm_wrap *vm; \
146
+ \
147
+ rubyjsonnet_assert_asciicompat(StringValue(key)); \
148
+ rubyjsonnet_assert_asciicompat(StringValue(val)); \
149
+ TypedData_Get_Struct(self, struct jsonnet_vm_wrap, &jsonnet_vm_type, vm); \
150
+ jsonnet_##type(vm->vm, StringValueCStr(key), StringValueCStr(val)); \
151
+ } while (0)
152
+
153
+ /*
154
+ * Binds an external variable to a value.
155
+ * @param [String] key name of the variable
156
+ * @param [String] val the value
157
+ */
158
+ static VALUE
159
+ vm_ext_var(VALUE self, VALUE key, VALUE val)
160
+ {
161
+ vm_bind_variable(ext_var, self, key, val);
162
+ return Qnil;
163
+ }
164
+
165
+ /*
166
+ * Binds an external variable to a code fragment.
167
+ * @param [String] key name of the variable
168
+ * @param [String] code Jsonnet expression
169
+ */
170
+ static VALUE
171
+ vm_ext_code(VALUE self, VALUE key, VALUE code)
172
+ {
173
+ vm_bind_variable(ext_code, self, key, code);
174
+ return Qnil;
175
+ }
176
+
177
+ /*
178
+ * Binds a top-level argument to a value.
179
+ * @param [String] key name of the variable
180
+ * @param [String] val the value
181
+ */
182
+ static VALUE
183
+ vm_tla_var(VALUE self, VALUE key, VALUE val)
184
+ {
185
+ vm_bind_variable(tla_var, self, key, val);
186
+ return Qnil;
187
+ }
188
+
189
+ /*
190
+ * Binds a top-level argument to a code fragment.
191
+ * @param [String] key name of the variable
192
+ * @param [String] code Jsonnet expression
193
+ */
194
+ static VALUE
195
+ vm_tla_code(VALUE self, VALUE key, VALUE code)
196
+ {
197
+ vm_bind_variable(tla_code, self, key, code);
198
+ return Qnil;
199
+ }
200
+
201
+ /*
202
+ * Adds library search paths
203
+ */
204
+ static VALUE
205
+ vm_jpath_add_m(int argc, const VALUE *argv, VALUE self)
206
+ {
207
+ int i;
208
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
209
+
210
+ for (i = 0; i < argc; ++i) {
211
+ VALUE jpath = argv[i];
212
+ FilePathValue(jpath);
213
+ jsonnet_jpath_add(vm->vm, StringValueCStr(jpath));
214
+ }
215
+ return Qnil;
216
+ }
217
+
218
+ static VALUE
219
+ vm_set_max_stack(VALUE self, VALUE val)
220
+ {
221
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
222
+ jsonnet_max_stack(vm->vm, NUM2UINT(val));
223
+ return Qnil;
224
+ }
225
+
226
+ static VALUE
227
+ vm_set_gc_min_objects(VALUE self, VALUE val)
228
+ {
229
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
230
+ jsonnet_gc_min_objects(vm->vm, NUM2UINT(val));
231
+ return Qnil;
232
+ }
233
+
234
+ static VALUE
235
+ vm_set_gc_growth_trigger(VALUE self, VALUE val)
236
+ {
237
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
238
+ jsonnet_gc_growth_trigger(vm->vm, NUM2DBL(val));
239
+ return Qnil;
240
+ }
241
+
242
+ /*
243
+ * Let #evaluate and #evaluate_file return a raw String instead of JSON-encoded string if val is
244
+ * true
245
+ * @param [Boolean] val
246
+ */
247
+ static VALUE
248
+ vm_set_string_output(VALUE self, VALUE val)
249
+ {
250
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
251
+ jsonnet_string_output(vm->vm, RTEST(val));
252
+ return Qnil;
253
+ }
254
+
255
+ static VALUE
256
+ vm_set_max_trace(VALUE self, VALUE val)
257
+ {
258
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
259
+ jsonnet_max_trace(vm->vm, NUM2UINT(val));
260
+ return Qnil;
261
+ }
262
+
263
+ static VALUE
264
+ vm_set_fmt_indent(VALUE self, VALUE val)
265
+ {
266
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
267
+ jsonnet_fmt_indent(vm->vm, NUM2INT(val));
268
+ return val;
269
+ }
270
+
271
+ static VALUE
272
+ vm_set_fmt_max_blank_lines(VALUE self, VALUE val)
273
+ {
274
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
275
+ jsonnet_fmt_max_blank_lines(vm->vm, NUM2INT(val));
276
+ return val;
277
+ }
278
+
279
+ static VALUE
280
+ vm_set_fmt_string(VALUE self, VALUE str)
281
+ {
282
+ const char *ptr;
283
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
284
+ StringValue(str);
285
+ if (RSTRING_LEN(str) != 1) {
286
+ rb_raise(rb_eArgError, "fmt_string must have a length of 1");
287
+ }
288
+ ptr = RSTRING_PTR(str);
289
+ switch (*ptr) {
290
+ case 'd':
291
+ case 's':
292
+ case 'l':
293
+ jsonnet_fmt_string(vm->vm, *ptr);
294
+ return str;
295
+ default:
296
+ rb_raise(rb_eArgError, "fmt_string only accepts 'd', 's', or 'l'");
297
+ }
298
+ }
299
+
300
+ static VALUE
301
+ vm_set_fmt_comment(VALUE self, VALUE str)
302
+ {
303
+ const char *ptr;
304
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
305
+ StringValue(str);
306
+ if (RSTRING_LEN(str) != 1) {
307
+ rb_raise(rb_eArgError, "fmt_comment must have a length of 1");
308
+ }
309
+ ptr = RSTRING_PTR(str);
310
+ switch (*ptr) {
311
+ case 'h':
312
+ case 's':
313
+ case 'l':
314
+ jsonnet_fmt_comment(vm->vm, *ptr);
315
+ return str;
316
+ default:
317
+ rb_raise(rb_eArgError, "fmt_comment only accepts 'h', 's', or 'l'");
318
+ }
319
+ }
320
+
321
+ static VALUE
322
+ vm_set_fmt_pad_arrays(VALUE self, VALUE val)
323
+ {
324
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
325
+ jsonnet_fmt_pad_objects(vm->vm, RTEST(val) ? 1 : 0);
326
+ return val;
327
+ }
328
+
329
+ static VALUE
330
+ vm_set_fmt_pad_objects(VALUE self, VALUE val)
331
+ {
332
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
333
+ jsonnet_fmt_pad_objects(vm->vm, RTEST(val) ? 1 : 0);
334
+ return val;
335
+ }
336
+
337
+ static VALUE
338
+ vm_set_fmt_pretty_field_names(VALUE self, VALUE val)
339
+ {
340
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
341
+ jsonnet_fmt_pretty_field_names(vm->vm, RTEST(val) ? 1 : 0);
342
+ return val;
343
+ }
344
+
345
+ static VALUE
346
+ vm_set_fmt_sort_imports(VALUE self, VALUE val)
347
+ {
348
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
349
+ jsonnet_fmt_sort_imports(vm->vm, RTEST(val) ? 1 : 0);
350
+ return val;
351
+ }
352
+
353
+ static VALUE
354
+ vm_fmt_file(VALUE self, VALUE fname, VALUE encoding)
355
+ {
356
+ int error;
357
+ char *result;
358
+ rb_encoding *const enc = rb_to_encoding(encoding);
359
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
360
+
361
+ FilePathValue(fname);
362
+ result = jsonnet_fmt_file(vm->vm, StringValueCStr(fname), &error);
363
+ if (error) {
364
+ raise_format_error(vm->vm, result, rb_enc_get(fname));
365
+ }
366
+ return str_new_json(vm->vm, result, enc);
367
+ }
368
+
369
+ static VALUE
370
+ vm_fmt_snippet(VALUE self, VALUE snippet, VALUE fname)
371
+ {
372
+ int error;
373
+ char *result;
374
+ struct jsonnet_vm_wrap *vm = rubyjsonnet_obj_to_vm(self);
375
+
376
+ rb_encoding *enc = rubyjsonnet_assert_asciicompat(StringValue(snippet));
377
+ FilePathValue(fname);
378
+ result = jsonnet_fmt_snippet(vm->vm, StringValueCStr(fname), StringValueCStr(snippet), &error);
379
+ if (error) {
380
+ raise_format_error(vm->vm, result, rb_enc_get(fname));
381
+ }
382
+ return str_new_json(vm->vm, result, enc);
383
+ }
384
+
385
+ void
386
+ rubyjsonnet_init_vm(VALUE mJsonnet)
387
+ {
388
+ cVM = rb_define_class_under(mJsonnet, "VM", rb_cData);
389
+ rb_define_singleton_method(cVM, "new", vm_s_new, -1);
390
+ rb_define_private_method(cVM, "eval_file", vm_evaluate_file, 3);
391
+ rb_define_private_method(cVM, "eval_snippet", vm_evaluate, 3);
392
+ rb_define_private_method(cVM, "fmt_file", vm_fmt_file, 2);
393
+ rb_define_private_method(cVM, "fmt_snippet", vm_fmt_snippet, 2);
394
+ rb_define_method(cVM, "ext_var", vm_ext_var, 2);
395
+ rb_define_method(cVM, "ext_code", vm_ext_code, 2);
396
+ rb_define_method(cVM, "tla_var", vm_tla_var, 2);
397
+ rb_define_method(cVM, "tla_code", vm_tla_code, 2);
398
+ rb_define_method(cVM, "jpath_add", vm_jpath_add_m, -1);
399
+ rb_define_method(cVM, "max_stack=", vm_set_max_stack, 1);
400
+ rb_define_method(cVM, "gc_min_objects=", vm_set_gc_min_objects, 1);
401
+ rb_define_method(cVM, "gc_growth_trigger=", vm_set_gc_growth_trigger, 1);
402
+ rb_define_method(cVM, "string_output=", vm_set_string_output, 1);
403
+ rb_define_method(cVM, "max_trace=", vm_set_max_trace, 1);
404
+ rb_define_method(cVM, "fmt_indent=", vm_set_fmt_indent, 1);
405
+ rb_define_method(cVM, "fmt_max_blank_lines=", vm_set_fmt_max_blank_lines, 1);
406
+ rb_define_method(cVM, "fmt_string=", vm_set_fmt_string, 1);
407
+ rb_define_method(cVM, "fmt_comment=", vm_set_fmt_comment, 1);
408
+ rb_define_method(cVM, "fmt_pad_arrays=", vm_set_fmt_pad_arrays, 1);
409
+ rb_define_method(cVM, "fmt_pad_objects=", vm_set_fmt_pad_objects, 1);
410
+ rb_define_method(cVM, "fmt_pretty_field_names=", vm_set_fmt_pretty_field_names, 1);
411
+ rb_define_method(cVM, "fmt_sort_imports=", vm_set_fmt_sort_imports, 1);
412
+
413
+ rb_define_const(mJsonnet, "STRING_STYLE_DOUBLE", rb_str_new_cstr("d"));
414
+ rb_define_const(mJsonnet, "STRING_STYLE_SINGLE", rb_str_new_cstr("s"));
415
+ rb_define_const(mJsonnet, "STRING_STYLE_LEAVE", rb_str_new_cstr("l"));
416
+ rb_define_const(mJsonnet, "COMMENT_STYLE_HASH", rb_str_new_cstr("h"));
417
+ rb_define_const(mJsonnet, "COMMENT_STYLE_SLASH", rb_str_new_cstr("s"));
418
+ rb_define_const(mJsonnet, "COMMENT_STYLE_LEAVE", rb_str_new_cstr("l"));
419
+
420
+ rubyjsonnet_init_callbacks(cVM);
421
+
422
+ eEvaluationError = rb_define_class_under(mJsonnet, "EvaluationError", rb_eRuntimeError);
423
+ eFormatError = rb_define_class_under(mJsonnet, "FormatError", rb_eRuntimeError);
424
+ }
425
+
426
+ static void
427
+ raise_error(VALUE exception_class, struct JsonnetVm *vm, char *msg, rb_encoding *enc)
428
+ {
429
+ VALUE ex;
430
+ const int state = rubyjsonnet_jump_tag(msg);
431
+ if (state) {
432
+ /*
433
+ * This is not actually an exception but another type of long jump
434
+ * with the state, temporarily caught by rescue_callback().
435
+ */
436
+ jsonnet_realloc(vm, msg, 0);
437
+ rb_jump_tag(state);
438
+ }
439
+
440
+ ex = rb_exc_new3(exception_class, rb_enc_str_new_cstr(msg, enc));
441
+ jsonnet_realloc(vm, msg, 0);
442
+ rb_exc_raise(ex);
443
+ }
444
+
445
+ /**
446
+ * raises an EvaluationError whose message is \c msg.
447
+ * @param[in] vm a JsonnetVM
448
+ * @param[in] msg must be a NUL-terminated string returned by \c vm.
449
+ * @return never returns
450
+ * @throw EvaluationError
451
+ * @sa rescue_callback
452
+ */
453
+ static void
454
+ raise_eval_error(struct JsonnetVm *vm, char *msg, rb_encoding *enc)
455
+ {
456
+ raise_error(eEvaluationError, vm, msg, enc);
457
+ }
458
+
459
+ static void
460
+ raise_format_error(struct JsonnetVm *vm, char *msg, rb_encoding *enc)
461
+ {
462
+ raise_error(eFormatError, vm, msg, enc);
463
+ }
464
+
465
+ /**
466
+ * Returns a String whose contents is equal to \c json.
467
+ * It automatically frees \c json just after constructing the return value.
468
+ *
469
+ * @param[in] vm a JsonnetVM
470
+ * @param[in] json must be a NUL-terminated string returned by \c vm.
471
+ * @return Ruby string equal to \c json.
472
+ */
473
+ static VALUE
474
+ str_new_json(struct JsonnetVm *vm, char *json, rb_encoding *enc)
475
+ {
476
+ VALUE str = rb_enc_str_new_cstr(json, enc);
477
+ jsonnet_realloc(vm, json, 0);
478
+ return str;
479
+ }
480
+
481
+ /**
482
+ * Returns a Hash, whose keys are file names in the multi-mode of Jsonnet,
483
+ * and whose values are corresponding JSON values.
484
+ * It automatically frees \c json just after constructing the return value.
485
+ *
486
+ * @param[in] vm a JsonnetVM
487
+ * @param[in] buf NUL-separated and double-NUL-terminated sequence of strings returned by \c vm.
488
+ * @return Hash
489
+ */
490
+ static VALUE
491
+ fileset_new(struct JsonnetVm *vm, char *buf, rb_encoding *enc)
492
+ {
493
+ VALUE fileset = rb_hash_new();
494
+ char *ptr, *json;
495
+ for (ptr = buf; *ptr; ptr = json + strlen(json) + 1) {
496
+ json = ptr + strlen(ptr) + 1;
497
+ if (!*json) {
498
+ VALUE ex = rb_exc_new3(eEvaluationError,
499
+ rb_enc_sprintf(enc, "output file %s without body", ptr));
500
+ jsonnet_realloc(vm, buf, 0);
501
+ rb_exc_raise(ex);
502
+ }
503
+
504
+ rb_hash_aset(fileset, rb_enc_str_new_cstr(ptr, enc), rb_enc_str_new_cstr(json, enc));
505
+ }
506
+ jsonnet_realloc(vm, buf, 0);
507
+ return fileset;
508
+ }