1 #include <stdlib.h>
2 #include "lib/mlr_globals.h"
3 #include "lib/mlrutil.h"
4 #include "keylist_evaluators.h"
5 #include "mlr_dsl_cst.h"
6 #include "context_flags.h"
7
8 // ----------------------------------------------------------------
file_output_mode_from_ast_node_type(mlr_dsl_ast_node_type_t mlr_dsl_ast_node_type)9 static file_output_mode_t file_output_mode_from_ast_node_type(mlr_dsl_ast_node_type_t mlr_dsl_ast_node_type) {
10 switch(mlr_dsl_ast_node_type) {
11 case MD_AST_NODE_TYPE_FILE_APPEND:
12 return MODE_APPEND;
13 case MD_AST_NODE_TYPE_PIPE:
14 return MODE_PIPE;
15 case MD_AST_NODE_TYPE_FILE_WRITE:
16 return MODE_WRITE;
17 default:
18 MLR_INTERNAL_CODING_ERROR();
19 return MODE_WRITE; // not reached
20 }
21 }
22
23 // ================================================================
24 typedef struct _print_state_t {
25 rxval_evaluator_t* prhs_xevaluator;
26 FILE* stdfp;
27 file_output_mode_t file_output_mode;
28 rval_evaluator_t* poutput_filename_evaluator;
29 int flush_every_record;
30 multi_out_t* pmulti_out;
31 char* print_terminator;
32 } print_state_t;
33
34 static mlr_dsl_cst_statement_handler_t handle_print;
35 static mlr_dsl_cst_statement_freer_t free_print;
36
37 // ----------------------------------------------------------------
alloc_print(mlr_dsl_cst_t * pcst,mlr_dsl_ast_node_t * pnode,int type_inferencing,int context_flags,char * print_terminator)38 mlr_dsl_cst_statement_t* alloc_print(
39 mlr_dsl_cst_t* pcst,
40 mlr_dsl_ast_node_t* pnode,
41 int type_inferencing,
42 int context_flags,
43 char* print_terminator)
44 {
45 print_state_t* pstate = mlr_malloc_or_die(sizeof(print_state_t));
46
47 pstate->prhs_xevaluator = NULL;
48 pstate->stdfp = NULL;
49 pstate->poutput_filename_evaluator = NULL;
50 pstate->pmulti_out = NULL;
51
52 MLR_INTERNAL_CODING_ERROR_IF((pnode->pchildren == NULL) || (pnode->pchildren->length != 2));
53 mlr_dsl_ast_node_t* pvalue_node = pnode->pchildren->phead->pvvalue;
54 pstate->prhs_xevaluator = rxval_evaluator_alloc_from_ast(pvalue_node, pcst->pfmgr,
55 type_inferencing, context_flags);
56 pstate->print_terminator = print_terminator;
57
58 mlr_dsl_ast_node_t* poutput_node = pnode->pchildren->phead->pnext->pvvalue;
59 mlr_dsl_ast_node_t* pfilename_node = poutput_node->pchildren->phead->pvvalue;
60 if (pfilename_node->type == MD_AST_NODE_TYPE_STDOUT) {
61 pstate->stdfp = stdout;
62 } else if (pfilename_node->type == MD_AST_NODE_TYPE_STDERR) {
63 pstate->stdfp = stderr;
64 } else {
65 pstate->poutput_filename_evaluator = rval_evaluator_alloc_from_ast(pfilename_node, pcst->pfmgr,
66 type_inferencing, context_flags);
67 pstate->file_output_mode = file_output_mode_from_ast_node_type(poutput_node->type);
68 pstate->pmulti_out = multi_out_alloc();
69 }
70 pstate->flush_every_record = pcst->flush_every_record;
71
72 return mlr_dsl_cst_statement_valloc(
73 pnode,
74 handle_print,
75 free_print,
76 pstate);
77 }
78
79 // ----------------------------------------------------------------
free_print(mlr_dsl_cst_statement_t * pstatement,context_t * _)80 static void free_print(mlr_dsl_cst_statement_t* pstatement, context_t* _) {
81 print_state_t* pstate = pstatement->pvstate;
82
83 if (pstate->prhs_xevaluator != NULL) {
84 pstate->prhs_xevaluator->pfree_func(pstate->prhs_xevaluator);
85 }
86
87 if (pstate->poutput_filename_evaluator != NULL) {
88 pstate->poutput_filename_evaluator->pfree_func(pstate->poutput_filename_evaluator);
89 }
90
91 if (pstate->pmulti_out != NULL) {
92 multi_out_close(pstate->pmulti_out);
93 multi_out_free(pstate->pmulti_out);
94 }
95
96 free(pstate);
97 }
98
99 // ----------------------------------------------------------------
handle_print(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)100 static void handle_print(
101 mlr_dsl_cst_statement_t* pstatement,
102 variables_t* pvars,
103 cst_outputs_t* pcst_outputs)
104 {
105 print_state_t* pstate = pstatement->pvstate;
106
107 rxval_evaluator_t* prhs_xevaluator = pstate->prhs_xevaluator;
108 boxed_xval_t bxval = prhs_xevaluator->pprocess_func(prhs_xevaluator->pvstate, pvars);
109
110 char sfree_flags = NO_FREE;
111 char* sval = "{is-a-map}";
112
113 if (bxval.xval.is_terminal) {
114 sval = mv_alloc_format_val(&bxval.xval.terminal_mlrval);
115 sfree_flags = FREE_ENTRY_VALUE;
116 }
117
118 rval_evaluator_t* poutput_filename_evaluator = pstate->poutput_filename_evaluator;
119 if (poutput_filename_evaluator == NULL) {
120 fprintf(pstate->stdfp, "%s%s", sval, pstate->print_terminator);
121 if (pstate->flush_every_record)
122 fflush(pstate->stdfp);
123 } else {
124 mv_t filename_mv = poutput_filename_evaluator->pprocess_func(poutput_filename_evaluator->pvstate, pvars);
125
126 char fn_free_flags;
127 char* filename = mv_format_val(&filename_mv, &fn_free_flags);
128
129 FILE* outfp = multi_out_get(pstate->pmulti_out, filename, pstate->file_output_mode);
130 fprintf(outfp, "%s%s", sval, pstate->print_terminator);
131 if (pstate->flush_every_record)
132 fflush(outfp);
133
134 if (fn_free_flags)
135 free(filename);
136 mv_free(&filename_mv);
137 }
138
139 if (sfree_flags) {
140 free(sval);
141 }
142 if (bxval.is_ephemeral) {
143 mlhmmv_xvalue_free(&bxval.xval);
144 }
145 }
146
147 // ================================================================
148 typedef struct _tee_state_t {
149 FILE* stdfp;
150 file_output_mode_t file_output_mode;
151 rval_evaluator_t* poutput_filename_evaluator;
152 int flush_every_record;
153 lrec_writer_t* psingle_lrec_writer;
154 multi_lrec_writer_t* pmulti_lrec_writer;
155 } tee_state_t;
156
157 static mlr_dsl_cst_statement_handler_t handle_tee_to_stdfp;
158 static mlr_dsl_cst_statement_handler_t handle_tee_to_file;
159 static mlr_dsl_cst_statement_freer_t free_tee;
160
161 static lrec_t* handle_tee_common(
162 tee_state_t* pstate,
163 variables_t* pvars,
164 cst_outputs_t* pcst_outputs);
165
166 // ----------------------------------------------------------------
alloc_tee(mlr_dsl_cst_t * pcst,mlr_dsl_ast_node_t * pnode,int type_inferencing,int context_flags)167 mlr_dsl_cst_statement_t* alloc_tee(mlr_dsl_cst_t* pcst, mlr_dsl_ast_node_t* pnode,
168 int type_inferencing, int context_flags)
169 {
170 tee_state_t* pstate = mlr_malloc_or_die(sizeof(tee_state_t));
171
172 pstate->stdfp = NULL;
173 pstate->poutput_filename_evaluator = NULL;
174 pstate->psingle_lrec_writer = NULL;
175 pstate->pmulti_lrec_writer = NULL;
176
177 mlr_dsl_ast_node_t* poutput_node = pnode->pchildren->phead->pvvalue;
178 mlr_dsl_ast_node_t* pfilename_node = poutput_node->pchildren->phead->pvvalue;
179
180 pstate->flush_every_record = pcst->flush_every_record;
181 if (pfilename_node->type == MD_AST_NODE_TYPE_STDOUT || pfilename_node->type == MD_AST_NODE_TYPE_STDERR) {
182 pstate->stdfp = (pfilename_node->type == MD_AST_NODE_TYPE_STDOUT) ? stdout : stderr;
183
184 return mlr_dsl_cst_statement_valloc(
185 pnode,
186 handle_tee_to_stdfp,
187 free_tee,
188 pstate);
189
190 } else {
191 pstate->poutput_filename_evaluator = rval_evaluator_alloc_from_ast(pfilename_node, pcst->pfmgr,
192 type_inferencing, context_flags);
193 pstate->file_output_mode = file_output_mode_from_ast_node_type(poutput_node->type);
194
195 return mlr_dsl_cst_statement_valloc(
196 pnode,
197 handle_tee_to_file,
198 free_tee,
199 pstate);
200 }
201 }
202
203 // ----------------------------------------------------------------
free_tee(mlr_dsl_cst_statement_t * pstatement,context_t * pctx)204 static void free_tee(mlr_dsl_cst_statement_t* pstatement, context_t* pctx) {
205 tee_state_t* pstate = pstatement->pvstate;
206
207 if (pstate->poutput_filename_evaluator != NULL) {
208 pstate->poutput_filename_evaluator->pfree_func(pstate->poutput_filename_evaluator);
209 }
210
211 if (pstate->psingle_lrec_writer != NULL) {
212 pstate->psingle_lrec_writer->pfree_func(pstate->psingle_lrec_writer, pctx);
213 }
214
215 if (pstate->pmulti_lrec_writer != NULL) {
216 multi_lrec_writer_drain(pstate->pmulti_lrec_writer, pctx);
217 multi_lrec_writer_free(pstate->pmulti_lrec_writer, pctx);
218 }
219
220 free(pstate);
221 }
222
223 // ----------------------------------------------------------------
handle_tee_to_stdfp(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)224 static void handle_tee_to_stdfp(
225 mlr_dsl_cst_statement_t* pstatement,
226 variables_t* pvars,
227 cst_outputs_t* pcst_outputs)
228 {
229 tee_state_t* pstate = pstatement->pvstate;
230
231 // The opts aren't complete at alloc time so we need to handle them on first use.
232 if (pstate->psingle_lrec_writer == NULL)
233 pstate->psingle_lrec_writer = lrec_writer_alloc_or_die(pcst_outputs->pwriter_opts);
234
235 lrec_t* pcopy = handle_tee_common(pstate, pvars, pcst_outputs);
236
237 // The writer frees the lrec
238 pstate->psingle_lrec_writer->pprocess_func(pstate->psingle_lrec_writer->pvstate,
239 pstate->stdfp, pcopy, pvars->pctx);
240 if (pstate->flush_every_record)
241 fflush(pstate->stdfp);
242 }
243
244 // ----------------------------------------------------------------
handle_tee_to_file(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)245 static void handle_tee_to_file(
246 mlr_dsl_cst_statement_t* pstatement,
247 variables_t* pvars,
248 cst_outputs_t* pcst_outputs)
249 {
250 tee_state_t* pstate = pstatement->pvstate;
251
252 // The opts aren't complete at alloc time so we need to handle them on first use.
253 if (pstate->pmulti_lrec_writer == NULL)
254 pstate->pmulti_lrec_writer = multi_lrec_writer_alloc(pcst_outputs->pwriter_opts);
255
256 rval_evaluator_t* poutput_filename_evaluator = pstate->poutput_filename_evaluator;
257 mv_t filename_mv = poutput_filename_evaluator->pprocess_func(poutput_filename_evaluator->pvstate, pvars);
258
259 lrec_t* pcopy = handle_tee_common(pstate, pvars, pcst_outputs);
260
261 char fn_free_flags = 0;
262 char* filename = mv_format_val(&filename_mv, &fn_free_flags);
263 // The writer frees the lrec
264 multi_lrec_writer_output_srec(pstate->pmulti_lrec_writer, pcopy, filename,
265 pstate->file_output_mode, pstate->flush_every_record, pvars->pctx);
266
267 if (fn_free_flags)
268 free(filename);
269 mv_free(&filename_mv);
270 }
271
272 // ----------------------------------------------------------------
handle_tee_common(tee_state_t * pstate,variables_t * pvars,cst_outputs_t * pcst_outputs)273 static lrec_t* handle_tee_common(
274 tee_state_t* pstate,
275 variables_t* pvars,
276 cst_outputs_t* pcst_outputs)
277 {
278 lrec_t* pcopy = lrec_copy(pvars->pinrec);
279
280 // Write the output fields from the typed overlay back to the lrec.
281 for (lhmsmve_t* pe = pvars->ptyped_overlay->phead; pe != NULL; pe = pe->pnext) {
282 char* output_field_name = pe->key;
283 mv_t* pval = &pe->value;
284
285 // Ownership transfer from mv_t to lrec.
286 if (pval->type == MT_STRING || pval->type == MT_EMPTY) {
287 lrec_put(pcopy, output_field_name, mlr_strdup_or_die(pval->u.strv), FREE_ENTRY_VALUE);
288 } else {
289 char free_flags = NO_FREE;
290 char* string = mv_format_val(pval, &free_flags);
291 lrec_put(pcopy, output_field_name, string, free_flags);
292 }
293 }
294 return pcopy;
295 }
296
297 // ================================================================
298 // Most statements have one item, except emit and emitf.
299 struct _emitf_item_t;
300 typedef void emitf_item_handler_t(
301 struct _emitf_item_t* pemitf_item,
302 variables_t* pvars,
303 cst_outputs_t* pcst_outputs);
304
305 typedef struct _emitf_item_t {
306 char* srec_field_name;
307 rval_evaluator_t* parg_evaluator;
308 } emitf_item_t;
309
310 static emitf_item_t* alloc_emitf_item(char* srec_field_name, rval_evaluator_t* parg_evaluator);
311 static void free_emitf_item(emitf_item_t* pemitf_item);
312
313 // ----------------------------------------------------------------
314 typedef struct _emitf_state_t {
315 FILE* stdfp;
316 file_output_mode_t file_output_mode;
317 rval_evaluator_t* poutput_filename_evaluator;
318 int flush_every_record;
319 lrec_writer_t* psingle_lrec_writer;
320 multi_lrec_writer_t* pmulti_lrec_writer;
321 sllv_t* pemitf_items;
322 } emitf_state_t;
323
324 static mlr_dsl_cst_statement_handler_t handle_emitf;
325 static mlr_dsl_cst_statement_freer_t free_emitf;
326
327 static void handle_emitf(
328 mlr_dsl_cst_statement_t* pstatement,
329 variables_t* pvars,
330 cst_outputs_t* pcst_outputs);
331
332 static void handle_emitf_to_stdfp(
333 mlr_dsl_cst_statement_t* pstatement,
334 variables_t* pvars,
335 cst_outputs_t* pcst_outputs);
336
337 static void handle_emitf_to_file(
338 mlr_dsl_cst_statement_t* pstatement,
339 variables_t* pvars,
340 cst_outputs_t* pcst_outputs);
341
342 static void handle_emitf_common(
343 emitf_state_t* pstate,
344 variables_t* pvars,
345 sllv_t* poutrecs);
346
347 // ----------------------------------------------------------------
alloc_emitf(mlr_dsl_cst_t * pcst,mlr_dsl_ast_node_t * pnode,int type_inferencing,int context_flags)348 mlr_dsl_cst_statement_t* alloc_emitf(mlr_dsl_cst_t* pcst, mlr_dsl_ast_node_t* pnode,
349 int type_inferencing, int context_flags)
350 {
351 emitf_state_t* pstate = mlr_malloc_or_die(sizeof(emitf_state_t));
352
353 pstate->stdfp = NULL;
354 pstate->poutput_filename_evaluator = NULL;
355 pstate->psingle_lrec_writer = NULL;
356 pstate->pmulti_lrec_writer = NULL;
357 pstate->pemitf_items = NULL;
358
359 mlr_dsl_ast_node_t* pnamesnode = pnode->pchildren->phead->pvvalue;
360
361 // Loop over oosvar names to emit in e.g. 'emitf @a, @b, @c'.
362 pstate->pemitf_items = sllv_alloc();
363 for (sllve_t* pe = pnamesnode->pchildren->phead; pe != NULL; pe = pe->pnext) {
364 mlr_dsl_ast_node_t* pwalker = pe->pvvalue;
365
366 char* name = NULL;
367 switch(pwalker->type) {
368 case MD_AST_NODE_TYPE_OOSVAR_KEYLIST:
369 name = ((mlr_dsl_ast_node_t*)(pwalker->pchildren->phead->pvvalue))->text;
370 break;
371 case MD_AST_NODE_TYPE_NONINDEXED_LOCAL_VARIABLE:
372 name = pwalker->text;
373 break;
374 case MD_AST_NODE_TYPE_INDEXED_LOCAL_VARIABLE:
375 name = pwalker->text;
376 break;
377 default:
378 MLR_INTERNAL_CODING_ERROR();
379 break;
380 }
381 sllv_append(pstate->pemitf_items,
382 alloc_emitf_item(
383 name,
384 rval_evaluator_alloc_from_ast(pwalker, pcst->pfmgr, type_inferencing, context_flags)));
385 }
386
387 mlr_dsl_ast_node_t* poutput_node = pnode->pchildren->phead->pnext->pvvalue;
388 mlr_dsl_ast_node_t* pfilename_node = poutput_node->pchildren == NULL
389 ? NULL
390 : poutput_node->pchildren->phead == NULL
391 ? NULL
392 : poutput_node->pchildren->phead->pvvalue;
393 mlr_dsl_cst_statement_handler_t* phandler = NULL;
394 if (poutput_node->type == MD_AST_NODE_TYPE_STREAM) {
395 phandler = handle_emitf;
396 } else if (pfilename_node->type == MD_AST_NODE_TYPE_STDOUT || pfilename_node->type == MD_AST_NODE_TYPE_STDERR) {
397 pstate->stdfp = (pfilename_node->type == MD_AST_NODE_TYPE_STDOUT) ? stdout : stderr;
398 phandler = handle_emitf_to_stdfp;
399 } else {
400 pstate->poutput_filename_evaluator = rval_evaluator_alloc_from_ast(pfilename_node, pcst->pfmgr,
401 type_inferencing, context_flags);
402 pstate->file_output_mode = file_output_mode_from_ast_node_type(poutput_node->type);
403 phandler = handle_emitf_to_file;
404 }
405 pstate->flush_every_record = pcst->flush_every_record;
406
407 return mlr_dsl_cst_statement_valloc(
408 pnode,
409 phandler,
410 free_emitf,
411 pstate);
412 }
413
alloc_emitf_item(char * srec_field_name,rval_evaluator_t * parg_evaluator)414 static emitf_item_t* alloc_emitf_item(char* srec_field_name, rval_evaluator_t* parg_evaluator) {
415 emitf_item_t* pemitf_item = mlr_malloc_or_die(sizeof(emitf_item_t));
416 pemitf_item->srec_field_name = srec_field_name;
417 pemitf_item->parg_evaluator = parg_evaluator;
418 return pemitf_item;
419 }
420
free_emitf_item(emitf_item_t * pemitf_item)421 static void free_emitf_item(emitf_item_t* pemitf_item) {
422 pemitf_item->parg_evaluator->pfree_func(pemitf_item->parg_evaluator);
423 free(pemitf_item);
424 }
425
free_emitf(mlr_dsl_cst_statement_t * pstatement,context_t * pctx)426 static void free_emitf(mlr_dsl_cst_statement_t* pstatement, context_t* pctx) {
427 emitf_state_t* pstate = pstatement->pvstate;
428
429 if (pstate->poutput_filename_evaluator != NULL) {
430 pstate->poutput_filename_evaluator->pfree_func(pstate->poutput_filename_evaluator);
431 }
432
433 if (pstate->psingle_lrec_writer != NULL) {
434 pstate->psingle_lrec_writer->pfree_func(pstate->psingle_lrec_writer, pctx);
435 }
436
437 if (pstate->pmulti_lrec_writer != NULL) {
438 multi_lrec_writer_drain(pstate->pmulti_lrec_writer, pctx);
439 multi_lrec_writer_free(pstate->pmulti_lrec_writer, pctx);
440 }
441
442 if (pstate->pemitf_items != NULL) {
443 for (sllve_t* pe = pstate->pemitf_items->phead; pe != NULL; pe = pe->pnext)
444 free_emitf_item(pe->pvvalue);
445 sllv_free(pstate->pemitf_items);
446 }
447
448 free(pstate);
449 }
450
451 // ----------------------------------------------------------------
handle_emitf(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)452 static void handle_emitf(
453 mlr_dsl_cst_statement_t* pstatement,
454 variables_t* pvars,
455 cst_outputs_t* pcst_outputs)
456 {
457 emitf_state_t* pstate = pstatement->pvstate;
458 handle_emitf_common(pstate, pvars, pcst_outputs->poutrecs);
459 }
460
handle_emitf_to_stdfp(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)461 static void handle_emitf_to_stdfp(
462 mlr_dsl_cst_statement_t* pstatement,
463 variables_t* pvars,
464 cst_outputs_t* pcst_outputs)
465 {
466 emitf_state_t* pstate = pstatement->pvstate;
467
468 // The opts aren't complete at alloc time so we need to handle them on first use.
469 if (pstate->psingle_lrec_writer == NULL)
470 pstate->psingle_lrec_writer = lrec_writer_alloc_or_die(pcst_outputs->pwriter_opts);
471
472 sllv_t* poutrecs = sllv_alloc();
473
474 handle_emitf_common(pstate, pvars, poutrecs);
475
476 lrec_writer_print_all(pstate->psingle_lrec_writer, pstate->stdfp, poutrecs, pvars->pctx);
477 if (pstate->flush_every_record)
478 fflush(pstate->stdfp);
479 sllv_free(poutrecs);
480 }
481
handle_emitf_to_file(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)482 static void handle_emitf_to_file(
483 mlr_dsl_cst_statement_t* pstatement,
484 variables_t* pvars,
485 cst_outputs_t* pcst_outputs)
486 {
487 emitf_state_t* pstate = pstatement->pvstate;
488
489 // The opts aren't complete at alloc time so we need to handle them on first use.
490 if (pstate->pmulti_lrec_writer == NULL)
491 pstate->pmulti_lrec_writer = multi_lrec_writer_alloc(pcst_outputs->pwriter_opts);
492
493 rval_evaluator_t* poutput_filename_evaluator = pstate->poutput_filename_evaluator;
494 mv_t filename_mv = poutput_filename_evaluator->pprocess_func(poutput_filename_evaluator->pvstate, pvars);
495
496 sllv_t* poutrecs = sllv_alloc();
497
498 handle_emitf_common(pstate, pvars, poutrecs);
499
500 char fn_free_flags = 0;
501 char* filename = mv_format_val(&filename_mv, &fn_free_flags);
502 multi_lrec_writer_output_list(pstate->pmulti_lrec_writer, poutrecs, filename,
503 pstate->file_output_mode, pstate->flush_every_record, pvars->pctx);
504
505 sllv_free(poutrecs);
506 if (fn_free_flags)
507 free(filename);
508 mv_free(&filename_mv);
509 }
510
handle_emitf_common(emitf_state_t * pstate,variables_t * pvars,sllv_t * poutrecs)511 static void handle_emitf_common(
512 emitf_state_t* pstate,
513 variables_t* pvars,
514 sllv_t* poutrecs)
515 {
516 lrec_t* prec_to_emit = lrec_unbacked_alloc();
517 for (sllve_t* pf = pstate->pemitf_items->phead; pf != NULL; pf = pf->pnext) {
518 emitf_item_t* pemitf_item = pf->pvvalue;
519 char* srec_field_name = pemitf_item->srec_field_name;
520 rval_evaluator_t* parg_evaluator = pemitf_item->parg_evaluator;
521
522 // This is overkill ... the grammar allows only for oosvar names as args to emit. So we could bypass
523 // that and just hashmap-get keyed by srec_field_name here.
524 mv_t val = parg_evaluator->pprocess_func(parg_evaluator->pvstate, pvars);
525
526 if (val.type == MT_STRING) {
527 // Ownership transfer from (newly created) mlrval to (newly created) lrec.
528 lrec_put(prec_to_emit, srec_field_name, val.u.strv, val.free_flags);
529 } else {
530 char free_flags = NO_FREE;
531 char* string = mv_format_val(&val, &free_flags);
532 lrec_put(prec_to_emit, srec_field_name, string, free_flags);
533 }
534
535 }
536 sllv_append(poutrecs, prec_to_emit);
537 }
538
539 // ================================================================
540 struct _emit_state_t; // Forward reference
541 typedef void record_emitter_t(
542 struct _emit_state_t* pstate,
543 variables_t* pvars,
544 sllv_t* poutrecs,
545 char* oosvar_flatten_separator);
546
547 typedef struct _emit_state_t {
548 rval_evaluator_t* poutput_filename_evaluator;
549 FILE* stdfp;
550 file_output_mode_t file_output_mode;
551 sllv_t* pemit_namelist_evaluators;
552 int do_full_prefixing;
553
554 record_emitter_t* precord_emitter;
555
556 // For map literals
557 rxval_evaluator_t* prhs_xevaluator;
558
559 // For local variables
560 char* localvar_name;
561 int localvar_frame_relative_index;
562
563 // For oosvars and localvars: indices ["a", 1, $2] in 'for (k,v in @a[1][$2]) {...}'.
564 sllv_t* pemit_keylist_evaluators;
565
566 lrec_writer_t* psingle_lrec_writer; // emit/tee to stdout/stderr
567 multi_lrec_writer_t* pmulti_lrec_writer; // emit-to-file
568
569 int flush_every_record;
570 } emit_state_t;
571
572 static mlr_dsl_cst_statement_handler_t handle_emit;
573 static mlr_dsl_cst_statement_handler_t handle_emit_to_stdfp;
574 static mlr_dsl_cst_statement_handler_t handle_emit_to_file;
575
576 static void record_emitter_from_oosvar(
577 emit_state_t* pstate,
578 variables_t* pvars,
579 sllv_t* poutrecs,
580 char* oosvar_flatten_separator);
581
582 static void record_emitter_from_local_variable(
583 emit_state_t* pstate,
584 variables_t* pvars,
585 sllv_t* poutrecs,
586 char* oosvar_flatten_separator);
587
588 static void record_emitter_from_full_srec(
589 emit_state_t* pstate,
590 variables_t* pvars,
591 sllv_t* poutrecs,
592 char* oosvar_flatten_separator);
593
594 static void record_emitter_from_ephemeral_map(
595 emit_state_t* pstate,
596 variables_t* pvars,
597 sllv_t* poutrecs,
598 char* oosvar_flatten_separator);
599
600 static mlr_dsl_cst_statement_handler_t handle_emit_all;
601 static mlr_dsl_cst_statement_handler_t handle_emit_all_to_stdfp;
602 static mlr_dsl_cst_statement_handler_t handle_emit_all_to_file;
603
604 static mlr_dsl_cst_statement_freer_t free_emit;
605
606 // ----------------------------------------------------------------
607 // $ mlr -n put -v 'emit @a[2][3], "x", "y", "z"'
608 // AST ROOT:
609 // text="list", type=statement_list:
610 // text="emit", type=emit:
611 // text="emit", type=emit:
612 // text="oosvar_keylist", type=oosvar_keylist:
613 // text="a", type=string_literal.
614 // text="2", type=numeric_literal.
615 // text="3", type=numeric_literal.
616 // text="emit_namelist", type=emit:
617 // text="x", type=numeric_literal.
618 // text="y", type=numeric_literal.
619 // text="z", type=numeric_literal.
620 // text="stream", type=stream:
621 //
622 // $ mlr -n put -v 'emit all, "x", "y", "z"'
623 // AST ROOT:
624 // text="list", type=statement_list:
625 // text="emit", type=emit:
626 // text="emit", type=emit:
627 // text="all", type=all.
628 // text="emit_namelist", type=emit:
629 // text="x", type=numeric_literal.
630 // text="y", type=numeric_literal.
631 // text="z", type=numeric_literal.
632 // text="stream", type=stream:
633
alloc_emit(mlr_dsl_cst_t * pcst,mlr_dsl_ast_node_t * pnode,int type_inferencing,int context_flags,int do_full_prefixing)634 mlr_dsl_cst_statement_t* alloc_emit(
635 mlr_dsl_cst_t* pcst,
636 mlr_dsl_ast_node_t* pnode,
637 int type_inferencing,
638 int context_flags,
639 int do_full_prefixing)
640 {
641 emit_state_t* pstate = mlr_malloc_or_die(sizeof(emit_state_t));
642
643 pstate->poutput_filename_evaluator = NULL;
644 pstate->stdfp = NULL;
645 pstate->precord_emitter = NULL;
646 pstate->prhs_xevaluator = NULL;
647 pstate->localvar_name = NULL;
648 pstate->localvar_frame_relative_index = MD_UNUSED_INDEX;
649 pstate->pemit_namelist_evaluators = NULL;
650 pstate->pemit_keylist_evaluators = NULL;
651 pstate->psingle_lrec_writer = NULL;
652 pstate->pmulti_lrec_writer = NULL;
653
654 mlr_dsl_ast_node_t* pemit_node = pnode->pchildren->phead->pvvalue;
655 mlr_dsl_ast_node_t* poutput_node = pnode->pchildren->phead->pnext->pvvalue;
656 mlr_dsl_ast_node_t* pkeylist_node = pemit_node->pchildren->phead->pvvalue;
657
658 // Name note: difference between keylist and namelist: in emit @a[$b]["c"], "d", @e,
659 // the keylist is ["a", $b, "c"] and the namelist is ["d", @e].
660
661 // xxx why not rxval_evaluator here??
662
663 int output_all = FALSE;
664 // The grammar allows only 'emit all', not 'emit @x, all, $y'.
665 // So if 'all' appears at all, it's the only name.
666 if (pkeylist_node->type == MD_AST_NODE_TYPE_ALL || pkeylist_node->type == MD_AST_NODE_TYPE_FULL_OOSVAR) {
667 output_all = TRUE;
668
669 pstate->precord_emitter = record_emitter_from_oosvar;
670
671 } else if (pkeylist_node->type == MD_AST_NODE_TYPE_OOSVAR_KEYLIST) {
672
673 pstate->pemit_keylist_evaluators = allocate_keylist_evaluators_from_ast_node(
674 pkeylist_node, pcst->pfmgr, type_inferencing, context_flags);
675
676 pstate->precord_emitter = record_emitter_from_oosvar;
677
678 } else if (pkeylist_node->type == MD_AST_NODE_TYPE_NONINDEXED_LOCAL_VARIABLE) {
679 pstate->precord_emitter = record_emitter_from_local_variable;
680
681 MLR_INTERNAL_CODING_ERROR_IF(pkeylist_node->vardef_frame_relative_index == MD_UNUSED_INDEX);
682 pstate->localvar_name = pkeylist_node->text;
683 pstate->localvar_frame_relative_index = pkeylist_node->vardef_frame_relative_index;
684 pstate->pemit_keylist_evaluators = sllv_alloc();
685
686 } else if (pkeylist_node->type == MD_AST_NODE_TYPE_INDEXED_LOCAL_VARIABLE) {
687 pstate->precord_emitter = record_emitter_from_local_variable;
688
689 MLR_INTERNAL_CODING_ERROR_IF(pkeylist_node->vardef_frame_relative_index == MD_UNUSED_INDEX);
690 pstate->localvar_name = pkeylist_node->text;
691 pstate->localvar_frame_relative_index = pkeylist_node->vardef_frame_relative_index;
692 pstate->pemit_keylist_evaluators = allocate_keylist_evaluators_from_ast_node(
693 pkeylist_node, pcst->pfmgr, type_inferencing, context_flags);
694
695 } else if (pkeylist_node->type == MD_AST_NODE_TYPE_FULL_SREC) {
696 pstate->precord_emitter = record_emitter_from_full_srec;
697
698 } else if (pkeylist_node->type == MD_AST_NODE_TYPE_FUNCTION_CALLSITE) {
699 pstate->precord_emitter = record_emitter_from_ephemeral_map;
700 pstate->prhs_xevaluator = rxval_evaluator_alloc_from_ast(
701 pkeylist_node, pcst->pfmgr, type_inferencing, context_flags);
702
703 // xxx indexed function callsite -- ?
704
705 } else if (pkeylist_node->type == MD_AST_NODE_TYPE_MAP_LITERAL) {
706 pstate->precord_emitter = record_emitter_from_ephemeral_map;
707 pstate->prhs_xevaluator = rxval_evaluator_alloc_from_ast(
708 pkeylist_node, pcst->pfmgr, type_inferencing, context_flags);
709
710 } else {
711 MLR_INTERNAL_CODING_ERROR();
712 }
713
714 pstate->pemit_namelist_evaluators = sllv_alloc();
715 if (pemit_node->pchildren->length == 2) {
716 mlr_dsl_ast_node_t* pnamelist_node = pemit_node->pchildren->phead->pnext->pvvalue;
717 for (sllve_t* pe = pnamelist_node->pchildren->phead; pe != NULL; pe = pe->pnext) {
718 mlr_dsl_ast_node_t* pkeynode = pe->pvvalue;
719 sllv_append(pstate->pemit_namelist_evaluators,
720 rval_evaluator_alloc_from_ast(pkeynode, pcst->pfmgr, type_inferencing, context_flags));
721 }
722 }
723
724 mlr_dsl_cst_statement_handler_t* phandler = NULL;
725
726 pstate->do_full_prefixing = do_full_prefixing;
727 mlr_dsl_ast_node_t* pfilename_node = poutput_node->pchildren == NULL
728 ? NULL
729 : poutput_node->pchildren->phead == NULL
730 ? NULL
731 : poutput_node->pchildren->phead->pvvalue;
732 if (poutput_node->type == MD_AST_NODE_TYPE_STREAM) {
733 phandler = output_all ? handle_emit_all : handle_emit;
734 } else if (pfilename_node->type == MD_AST_NODE_TYPE_STDOUT || pfilename_node->type == MD_AST_NODE_TYPE_STDERR) {
735 phandler = output_all ? handle_emit_all_to_stdfp : handle_emit_to_stdfp;
736 pstate->stdfp = (pfilename_node->type == MD_AST_NODE_TYPE_STDOUT) ? stdout : stderr;
737 } else {
738 pstate->poutput_filename_evaluator = rval_evaluator_alloc_from_ast(pfilename_node, pcst->pfmgr,
739 type_inferencing, context_flags);
740 pstate->file_output_mode = file_output_mode_from_ast_node_type(poutput_node->type);
741 phandler = output_all ? handle_emit_all_to_file : handle_emit_to_file;
742 }
743 pstate->flush_every_record = pcst->flush_every_record;
744
745 return mlr_dsl_cst_statement_valloc(
746 pnode,
747 phandler,
748 free_emit,
749 pstate);
750 }
751
752 // ----------------------------------------------------------------
free_emit(mlr_dsl_cst_statement_t * pstatement,context_t * pctx)753 static void free_emit(mlr_dsl_cst_statement_t* pstatement, context_t* pctx) {
754 emit_state_t* pstate = pstatement->pvstate;
755
756 if (pstate->poutput_filename_evaluator != NULL) {
757 pstate->poutput_filename_evaluator->pfree_func(pstate->poutput_filename_evaluator);
758 }
759
760 if (pstate->prhs_xevaluator != NULL) {
761 pstate->prhs_xevaluator->pfree_func(pstate->prhs_xevaluator);
762 }
763
764 if (pstate->pemit_namelist_evaluators != NULL) {
765 for (sllve_t* pe = pstate->pemit_namelist_evaluators->phead; pe != NULL; pe = pe->pnext) {
766 rval_evaluator_t* phandler = pe->pvvalue;
767 phandler->pfree_func(phandler);
768 }
769 sllv_free(pstate->pemit_namelist_evaluators);
770 }
771
772 if (pstate->pemit_keylist_evaluators != NULL) {
773 for (sllve_t* pe = pstate->pemit_keylist_evaluators->phead; pe != NULL; pe = pe->pnext) {
774 rval_evaluator_t* phandler = pe->pvvalue;
775 phandler->pfree_func(phandler);
776 }
777 sllv_free(pstate->pemit_keylist_evaluators);
778 }
779
780 if (pstate->psingle_lrec_writer != NULL) {
781 pstate->psingle_lrec_writer->pfree_func(pstate->psingle_lrec_writer, pctx);
782 }
783
784 if (pstate->pmulti_lrec_writer != NULL) {
785 multi_lrec_writer_drain(pstate->pmulti_lrec_writer, pctx);
786 multi_lrec_writer_free(pstate->pmulti_lrec_writer, pctx);
787 }
788
789 free(pstate);
790 }
791
792 // ----------------------------------------------------------------
handle_emit(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)793 static void handle_emit(
794 mlr_dsl_cst_statement_t* pstatement,
795 variables_t* pvars,
796 cst_outputs_t* pcst_outputs)
797 {
798 emit_state_t* pstate = pstatement->pvstate;
799 pstate->precord_emitter(pstate, pvars, pcst_outputs->poutrecs, pcst_outputs->oosvar_flatten_separator);
800 }
801
802 // ----------------------------------------------------------------
handle_emit_to_stdfp(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)803 static void handle_emit_to_stdfp(
804 mlr_dsl_cst_statement_t* pstatement,
805 variables_t* pvars,
806 cst_outputs_t* pcst_outputs)
807 {
808 emit_state_t* pstate = pstatement->pvstate;
809 sllv_t* poutrecs = sllv_alloc();
810
811 pstate->precord_emitter(pstate, pvars, poutrecs, pcst_outputs->oosvar_flatten_separator);
812
813 // The opts aren't complete at alloc time so we need to handle them on first use.
814 if (pstate->psingle_lrec_writer == NULL)
815 pstate->psingle_lrec_writer = lrec_writer_alloc_or_die(pcst_outputs->pwriter_opts);
816
817 lrec_writer_print_all(pstate->psingle_lrec_writer, pstate->stdfp, poutrecs, pvars->pctx);
818 if (pstate->flush_every_record)
819 fflush(pstate->stdfp);
820
821 sllv_free(poutrecs);
822 }
823
824 // ----------------------------------------------------------------
handle_emit_to_file(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)825 static void handle_emit_to_file(
826 mlr_dsl_cst_statement_t* pstatement,
827 variables_t* pvars,
828 cst_outputs_t* pcst_outputs)
829 {
830 emit_state_t* pstate = pstatement->pvstate;
831
832 // The opts aren't complete at alloc time so we need to handle them on first use.
833 if (pstate->pmulti_lrec_writer == NULL)
834 pstate->pmulti_lrec_writer = multi_lrec_writer_alloc(pcst_outputs->pwriter_opts);
835
836 sllv_t* poutrecs = sllv_alloc();
837
838 pstate->precord_emitter(pstate, pvars, poutrecs, pcst_outputs->oosvar_flatten_separator);
839
840 rval_evaluator_t* poutput_filename_evaluator = pstate->poutput_filename_evaluator;
841 mv_t filename_mv = poutput_filename_evaluator->pprocess_func(poutput_filename_evaluator->pvstate, pvars);
842 char fn_free_flags = 0;
843 char* filename = mv_format_val(&filename_mv, &fn_free_flags);
844
845 multi_lrec_writer_output_list(pstate->pmulti_lrec_writer, poutrecs, filename,
846 pstate->file_output_mode, pstate->flush_every_record, pvars->pctx);
847 sllv_free(poutrecs);
848
849 if (fn_free_flags)
850 free(filename);
851 mv_free(&filename_mv);
852 }
853
854 // ----------------------------------------------------------------
record_emitter_from_oosvar(emit_state_t * pstate,variables_t * pvars,sllv_t * poutrecs,char * oosvar_flatten_separator)855 static void record_emitter_from_oosvar(
856 emit_state_t* pstate,
857 variables_t* pvars,
858 sllv_t* poutrecs,
859 char* oosvar_flatten_separator)
860 {
861 int keys_all_non_null_or_error = TRUE;
862 sllmv_t* pmvkeys = evaluate_list(pstate->pemit_keylist_evaluators, pvars, &keys_all_non_null_or_error);
863 if (keys_all_non_null_or_error) {
864 int names_all_non_null_or_error = TRUE;
865 sllmv_t* pmvnames = evaluate_list(pstate->pemit_namelist_evaluators, pvars,
866 &names_all_non_null_or_error);
867 if (names_all_non_null_or_error) {
868 mlhmmv_root_partial_to_lrecs(pvars->poosvars, pmvkeys, pmvnames, poutrecs,
869 pstate->do_full_prefixing, oosvar_flatten_separator);
870 }
871 sllmv_free(pmvnames);
872 }
873 sllmv_free(pmvkeys);
874 }
875
record_emitter_from_local_variable(emit_state_t * pstate,variables_t * pvars,sllv_t * poutrecs,char * oosvar_flatten_separator)876 static void record_emitter_from_local_variable(
877 emit_state_t* pstate,
878 variables_t* pvars,
879 sllv_t* poutrecs,
880 char* oosvar_flatten_separator)
881 {
882 int keys_all_non_null_or_error = TRUE;
883 sllmv_t* pmvkeys = evaluate_list(pstate->pemit_keylist_evaluators, pvars, &keys_all_non_null_or_error);
884
885 mv_t name = mv_from_string(pstate->localvar_name, NO_FREE);
886 sllmv_prepend_no_free(pmvkeys, &name);
887
888 if (keys_all_non_null_or_error) {
889 int names_all_non_null_or_error = TRUE;
890 sllmv_t* pmvnames = evaluate_list(pstate->pemit_namelist_evaluators, pvars,
891 &names_all_non_null_or_error);
892 if (names_all_non_null_or_error) {
893
894 local_stack_frame_t* pframe = local_stack_get_top_frame(pvars->plocal_stack);
895 mlhmmv_xvalue_t* pmval = local_stack_frame_ref_extended_from_indexed(pframe,
896 pstate->localvar_frame_relative_index, NULL);
897 if (pmval != NULL) {
898 // Temporarily wrap the localvar in a parent map whose single key is the variable name.
899 mlhmmv_root_t* pmap = mlhmmv_wrap_name_and_xvalue(&name, pmval);
900
901 mlhmmv_root_partial_to_lrecs(pmap, pmvkeys, pmvnames, poutrecs,
902 pstate->do_full_prefixing, oosvar_flatten_separator);
903
904 mlhmmv_unwrap_name_and_xvalue(pmap);
905 }
906 }
907 sllmv_free(pmvnames);
908 }
909 sllmv_free(pmvkeys);
910 }
911
record_emitter_from_full_srec(emit_state_t * pstate,variables_t * pvars,sllv_t * poutrecs,char * oosvar_flatten_separator)912 static void record_emitter_from_full_srec(
913 emit_state_t* pstate,
914 variables_t* pvars,
915 sllv_t* poutrecs,
916 char* oosvar_flatten_separator)
917 {
918 sllv_append(poutrecs, lrec_copy(pvars->pinrec));
919 }
920
record_emitter_from_ephemeral_map(emit_state_t * pstate,variables_t * pvars,sllv_t * poutrecs,char * oosvar_flatten_separator)921 static void record_emitter_from_ephemeral_map(
922 emit_state_t* pstate,
923 variables_t* pvars,
924 sllv_t* poutrecs,
925 char* oosvar_flatten_separator)
926 {
927 rxval_evaluator_t* prhs_xevaluator = pstate->prhs_xevaluator;
928 boxed_xval_t boxed_xval = prhs_xevaluator->pprocess_func(prhs_xevaluator->pvstate, pvars);
929 sllmv_t* pmvkeys = sllmv_alloc();
930
931 if (!boxed_xval.xval.is_terminal) {
932 int names_all_non_null_or_error = TRUE;
933 sllmv_t* pmvnames = evaluate_list(pstate->pemit_namelist_evaluators, pvars,
934 &names_all_non_null_or_error);
935 if (names_all_non_null_or_error) {
936 mv_t name = mv_from_string("_", NO_FREE);
937 sllmv_prepend_no_free(pmvkeys, &name);
938
939 // Temporarily wrap the localvar in a parent map whose single key is the variable name.
940 mlhmmv_root_t* pmap = mlhmmv_wrap_name_and_xvalue(&name, &boxed_xval.xval);
941
942 mlhmmv_root_partial_to_lrecs(pmap, pmvkeys, pmvnames, poutrecs,
943 pstate->do_full_prefixing, oosvar_flatten_separator);
944
945 mlhmmv_unwrap_name_and_xvalue(pmap);
946 }
947 sllmv_free(pmvnames);
948 }
949
950 if (boxed_xval.is_ephemeral) {
951 mlhmmv_xvalue_free(&boxed_xval.xval);
952 }
953
954 sllmv_free(pmvkeys);
955 }
956
957 // ----------------------------------------------------------------
handle_emit_all(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)958 static void handle_emit_all(
959 mlr_dsl_cst_statement_t* pstatement,
960 variables_t* pvars,
961 cst_outputs_t* pcst_outputs)
962 {
963 emit_state_t* pstate = pstatement->pvstate;
964 int all_non_null_or_error = TRUE;
965 sllmv_t* pmvnames = evaluate_list(pstate->pemit_namelist_evaluators, pvars, &all_non_null_or_error);
966 if (all_non_null_or_error) {
967 mlhmmv_root_all_to_lrecs(pvars->poosvars, pmvnames, pcst_outputs->poutrecs,
968 pstate->do_full_prefixing, pcst_outputs->oosvar_flatten_separator);
969 }
970 sllmv_free(pmvnames);
971 }
972
973 // ----------------------------------------------------------------
handle_emit_all_to_stdfp(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)974 static void handle_emit_all_to_stdfp(
975 mlr_dsl_cst_statement_t* pstatement,
976 variables_t* pvars,
977 cst_outputs_t* pcst_outputs)
978 {
979 emit_state_t* pstate = pstatement->pvstate;
980
981 // The opts aren't complete at alloc time so we need to handle them on first use.
982 if (pstate->psingle_lrec_writer == NULL)
983 pstate->psingle_lrec_writer = lrec_writer_alloc_or_die(pcst_outputs->pwriter_opts);
984
985 sllv_t* poutrecs = sllv_alloc();
986 int all_non_null_or_error = TRUE;
987 sllmv_t* pmvnames = evaluate_list(pstate->pemit_namelist_evaluators, pvars, &all_non_null_or_error);
988 if (all_non_null_or_error) {
989 mlhmmv_root_all_to_lrecs(pvars->poosvars, pmvnames, poutrecs,
990 pstate->do_full_prefixing, pcst_outputs->oosvar_flatten_separator);
991 }
992 sllmv_free(pmvnames);
993
994 lrec_writer_print_all(pstate->psingle_lrec_writer, pstate->stdfp, poutrecs, pvars->pctx);
995 if (pstate->flush_every_record)
996 fflush(pstate->stdfp);
997 sllv_free(poutrecs);
998 }
999
1000 // ----------------------------------------------------------------
handle_emit_all_to_file(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)1001 static void handle_emit_all_to_file(
1002 mlr_dsl_cst_statement_t* pstatement,
1003 variables_t* pvars,
1004 cst_outputs_t* pcst_outputs)
1005 {
1006 emit_state_t* pstate = pstatement->pvstate;
1007
1008 // The opts aren't complete at alloc time so we need to handle them on first use.
1009 if (pstate->pmulti_lrec_writer == NULL)
1010 pstate->pmulti_lrec_writer = multi_lrec_writer_alloc(pcst_outputs->pwriter_opts);
1011
1012 sllv_t* poutrecs = sllv_alloc();
1013 rval_evaluator_t* poutput_filename_evaluator = pstate->poutput_filename_evaluator;
1014 mv_t filename_mv = poutput_filename_evaluator->pprocess_func(poutput_filename_evaluator->pvstate, pvars);
1015 int all_non_null_or_error = TRUE;
1016 sllmv_t* pmvnames = evaluate_list(pstate->pemit_namelist_evaluators, pvars, &all_non_null_or_error);
1017 if (all_non_null_or_error) {
1018 mlhmmv_root_all_to_lrecs(pvars->poosvars, pmvnames, poutrecs,
1019 pstate->do_full_prefixing, pcst_outputs->oosvar_flatten_separator);
1020 }
1021
1022 char fn_free_flags = 0;
1023 char* filename = mv_format_val(&filename_mv, &fn_free_flags);
1024 multi_lrec_writer_output_list(pstate->pmulti_lrec_writer, poutrecs, filename,
1025 pstate->file_output_mode, pstate->flush_every_record, pvars->pctx);
1026 sllv_free(poutrecs);
1027
1028 if (fn_free_flags)
1029 free(filename);
1030 mv_free(&filename_mv);
1031 sllmv_free(pmvnames);
1032 }
1033
1034 // ================================================================
1035 struct _emit_lashed_item_t; // Forward reference
1036
1037 typedef struct _emit_lashed_item_t {
1038 rval_evaluator_t* pbasename_evaluator;
1039 rxval_evaluator_t* pitem_xevaluator;
1040 } emit_lashed_item_t;
1041
emit_lashed_item_alloc(rval_evaluator_t * pbasename_evaluator,rxval_evaluator_t * pitem_xevaluator)1042 static emit_lashed_item_t* emit_lashed_item_alloc(
1043 rval_evaluator_t* pbasename_evaluator,
1044 rxval_evaluator_t* pitem_xevaluator)
1045 {
1046 emit_lashed_item_t* pitem = mlr_malloc_or_die(sizeof(emit_lashed_item_t));
1047 pitem->pbasename_evaluator = pbasename_evaluator;
1048 pitem->pitem_xevaluator = pitem_xevaluator;
1049 return pitem;
1050 }
1051
emit_lashed_item_free(emit_lashed_item_t * pitem)1052 static void emit_lashed_item_free(emit_lashed_item_t* pitem) {
1053 pitem->pbasename_evaluator->pfree_func(pitem->pbasename_evaluator);
1054 pitem->pitem_xevaluator->pfree_func(pitem->pitem_xevaluator);
1055 free(pitem);
1056 }
1057
1058 // ----------------------------------------------------------------
1059 typedef struct _emit_lashed_state_t {
1060 int num_emit_lashed_items;
1061 emit_lashed_item_t** ppitems;
1062 sllv_t* pemit_namelist_evaluators;
1063
1064 // Used per-call but allocated once in the constructor:
1065 mv_t* pbasenames;
1066 boxed_xval_t* pboxed_xvals;
1067 mlhmmv_xvalue_t** ptop_values;
1068
1069 rval_evaluator_t* poutput_filename_evaluator;
1070 FILE* stdfp;
1071 file_output_mode_t file_output_mode;
1072 lrec_writer_t* psingle_lrec_writer; // emit/tee to stdout/stderr
1073 multi_lrec_writer_t* pmulti_lrec_writer; // emit-to-file
1074
1075 int do_full_prefixing;
1076 int flush_every_record;
1077 } emit_lashed_state_t;
1078
1079 static mlr_dsl_cst_statement_handler_t handle_emit_lashed;
1080 static mlr_dsl_cst_statement_handler_t handle_emit_lashed_to_stdfp;
1081 static mlr_dsl_cst_statement_handler_t handle_emit_lashed_to_file;
1082 static void handle_emit_lashed_common(
1083 emit_lashed_state_t* pstate,
1084 variables_t* pvars,
1085 sllv_t* poutrecs,
1086 char* oosvar_flatten_separator);
1087 static void free_emit_lashed(mlr_dsl_cst_statement_t* pstatement, context_t* pctx);
1088
1089 // ----------------------------------------------------------------
alloc_emit_lashed(mlr_dsl_cst_t * pcst,mlr_dsl_ast_node_t * pnode,int type_inferencing,int context_flags,int do_full_prefixing)1090 mlr_dsl_cst_statement_t* alloc_emit_lashed(mlr_dsl_cst_t* pcst, mlr_dsl_ast_node_t* pnode,
1091 int type_inferencing, int context_flags, int do_full_prefixing)
1092 {
1093 emit_lashed_state_t* pstate = mlr_malloc_or_die(sizeof(emit_lashed_state_t));
1094
1095 pstate->num_emit_lashed_items = 0;
1096 pstate->ppitems = NULL;
1097 pstate->pemit_namelist_evaluators = NULL;
1098
1099 pstate->poutput_filename_evaluator = NULL;
1100 pstate->stdfp = NULL;
1101 pstate->psingle_lrec_writer = NULL;
1102 pstate->pmulti_lrec_writer = NULL;
1103
1104 mlr_dsl_ast_node_t* pemit_node = pnode->pchildren->phead->pvvalue;
1105 mlr_dsl_ast_node_t* poutput_node = pnode->pchildren->phead->pnext->pvvalue;
1106 mlr_dsl_ast_node_t* pkeylists_node = pemit_node->pchildren->phead->pvvalue;
1107
1108 pstate->num_emit_lashed_items = pkeylists_node->pchildren->length;
1109 pstate->ppitems = mlr_malloc_or_die(pstate->num_emit_lashed_items * sizeof(emit_lashed_item_t*));
1110 int i = 0;
1111 for (sllve_t* pe = pkeylists_node->pchildren->phead; pe != NULL; pe = pe->pnext, i++) {
1112 mlr_dsl_ast_node_t* pkeylist_node = pe->pvvalue;
1113
1114 rval_evaluator_t* pbasename_evaluator = NULL;
1115 switch (pkeylist_node->type) {
1116 case MD_AST_NODE_TYPE_NONINDEXED_LOCAL_VARIABLE:
1117 case MD_AST_NODE_TYPE_INDEXED_LOCAL_VARIABLE:
1118 pbasename_evaluator = rval_evaluator_alloc_from_string(pkeylist_node->text);
1119 break;
1120 case MD_AST_NODE_TYPE_OOSVAR_KEYLIST:
1121 pbasename_evaluator = rval_evaluator_alloc_from_string(
1122 ((mlr_dsl_ast_node_t*)pkeylist_node->pchildren->phead->pvvalue)->text);
1123 break;
1124 default:
1125 pbasename_evaluator = rval_evaluator_alloc_from_string("_");
1126 break;
1127 }
1128
1129 rxval_evaluator_t* pitem_xevaluator = rxval_evaluator_alloc_from_ast(
1130 pkeylist_node, pcst->pfmgr, type_inferencing, context_flags);
1131
1132 pstate->ppitems[i] = emit_lashed_item_alloc(pbasename_evaluator, pitem_xevaluator);
1133 }
1134
1135 sllv_t* pemit_namelist_evaluators = sllv_alloc();
1136 if (pemit_node->pchildren->length == 2) {
1137 mlr_dsl_ast_node_t* pnamelist_node = pemit_node->pchildren->phead->pnext->pvvalue;
1138 for (sllve_t* pe = pnamelist_node->pchildren->phead; pe != NULL; pe = pe->pnext) {
1139 mlr_dsl_ast_node_t* pkeynode = pe->pvvalue;
1140 sllv_append(pemit_namelist_evaluators,
1141 rval_evaluator_alloc_from_ast(pkeynode, pcst->pfmgr, type_inferencing, context_flags));
1142 }
1143 }
1144 pstate->pemit_namelist_evaluators = pemit_namelist_evaluators;
1145
1146 pstate->pbasenames = mlr_malloc_or_die(pstate->num_emit_lashed_items * sizeof(mv_t));
1147 pstate->pboxed_xvals = mlr_malloc_or_die(pstate->num_emit_lashed_items * sizeof(boxed_xval_t));
1148 pstate->ptop_values = mlr_malloc_or_die(pstate->num_emit_lashed_items * sizeof(mlhmmv_level_entry_t*));
1149
1150 mlr_dsl_cst_statement_handler_t* phandler = NULL;
1151 pstate->do_full_prefixing = do_full_prefixing;
1152 mlr_dsl_ast_node_t* pfilename_node = poutput_node->pchildren == NULL
1153 ? NULL
1154 : poutput_node->pchildren->phead == NULL
1155 ? NULL
1156 : poutput_node->pchildren->phead->pvvalue;
1157 if (poutput_node->type == MD_AST_NODE_TYPE_STREAM) {
1158 phandler = handle_emit_lashed;
1159 } else if (pfilename_node->type == MD_AST_NODE_TYPE_STDOUT || pfilename_node->type == MD_AST_NODE_TYPE_STDERR) {
1160 phandler = handle_emit_lashed_to_stdfp;
1161 pstate->stdfp = (pfilename_node->type == MD_AST_NODE_TYPE_STDOUT) ? stdout : stderr;
1162 } else {
1163 pstate->poutput_filename_evaluator = NULL;
1164 pstate->poutput_filename_evaluator = rval_evaluator_alloc_from_ast(pfilename_node, pcst->pfmgr,
1165 type_inferencing, context_flags);
1166 pstate->file_output_mode = file_output_mode_from_ast_node_type(poutput_node->type);
1167 phandler = handle_emit_lashed_to_file;
1168 }
1169 pstate->flush_every_record = pcst->flush_every_record;
1170
1171 return mlr_dsl_cst_statement_valloc(
1172 pnode,
1173 phandler,
1174 free_emit_lashed,
1175 pstate);
1176 }
1177
1178 // ----------------------------------------------------------------
free_emit_lashed(mlr_dsl_cst_statement_t * pstatement,context_t * pctx)1179 static void free_emit_lashed(mlr_dsl_cst_statement_t* pstatement, context_t* pctx) {
1180 emit_lashed_state_t* pstate = pstatement->pvstate;
1181
1182 if (pstate->ppitems != NULL) {
1183 for (int i = 0; i < pstate->num_emit_lashed_items; i++) {
1184 emit_lashed_item_free(pstate->ppitems[i]);
1185 }
1186 free(pstate->ppitems);
1187 }
1188
1189 if (pstate->poutput_filename_evaluator != NULL) {
1190 pstate->poutput_filename_evaluator->pfree_func(pstate->poutput_filename_evaluator);
1191 }
1192
1193 if (pstate->pemit_namelist_evaluators != NULL) {
1194 for (sllve_t* pe = pstate->pemit_namelist_evaluators->phead; pe != NULL; pe = pe->pnext) {
1195 rval_evaluator_t* phandler = pe->pvvalue;
1196 phandler->pfree_func(phandler);
1197 }
1198 sllv_free(pstate->pemit_namelist_evaluators);
1199 }
1200
1201 free(pstate->pbasenames);
1202 free(pstate->pboxed_xvals);
1203 free(pstate->ptop_values);
1204
1205 if (pstate->psingle_lrec_writer != NULL) {
1206 pstate->psingle_lrec_writer->pfree_func(pstate->psingle_lrec_writer, pctx);
1207 }
1208
1209 if (pstate->pmulti_lrec_writer != NULL) {
1210 multi_lrec_writer_drain(pstate->pmulti_lrec_writer, pctx);
1211 multi_lrec_writer_free(pstate->pmulti_lrec_writer, pctx);
1212 }
1213
1214 free(pstate);
1215 }
1216
1217 // ----------------------------------------------------------------
handle_emit_lashed(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)1218 static void handle_emit_lashed(
1219 mlr_dsl_cst_statement_t* pstatement,
1220 variables_t* pvars,
1221 cst_outputs_t* pcst_outputs)
1222 {
1223 emit_lashed_state_t* pstate = pstatement->pvstate;
1224 handle_emit_lashed_common(pstate, pvars, pcst_outputs->poutrecs, pcst_outputs->oosvar_flatten_separator);
1225 }
1226
handle_emit_lashed_to_stdfp(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)1227 static void handle_emit_lashed_to_stdfp(
1228 mlr_dsl_cst_statement_t* pstatement,
1229 variables_t* pvars,
1230 cst_outputs_t* pcst_outputs)
1231 {
1232 emit_lashed_state_t* pstate = pstatement->pvstate;
1233
1234 // The opts aren't complete at alloc time so we need to handle them on first use.
1235 if (pstate->psingle_lrec_writer == NULL)
1236 pstate->psingle_lrec_writer = lrec_writer_alloc_or_die(pcst_outputs->pwriter_opts);
1237
1238 sllv_t* poutrecs = sllv_alloc();
1239
1240 handle_emit_lashed_common(pstate, pvars, poutrecs, pcst_outputs->oosvar_flatten_separator);
1241
1242 lrec_writer_print_all(pstate->psingle_lrec_writer, pstate->stdfp, poutrecs, pvars->pctx);
1243 if (pstate->flush_every_record)
1244 fflush(pstate->stdfp);
1245
1246 sllv_free(poutrecs);
1247 }
1248
1249 // ----------------------------------------------------------------
handle_emit_lashed_to_file(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)1250 static void handle_emit_lashed_to_file(
1251 mlr_dsl_cst_statement_t* pstatement,
1252 variables_t* pvars,
1253 cst_outputs_t* pcst_outputs)
1254 {
1255 emit_lashed_state_t* pstate = pstatement->pvstate;
1256
1257 // The opts aren't complete at alloc time so we need to handle them on first use.
1258 if (pstate->pmulti_lrec_writer == NULL)
1259 pstate->pmulti_lrec_writer = multi_lrec_writer_alloc(pcst_outputs->pwriter_opts);
1260
1261 sllv_t* poutrecs = sllv_alloc();
1262
1263 handle_emit_lashed_common(pstate, pvars, poutrecs, pcst_outputs->oosvar_flatten_separator);
1264
1265 rval_evaluator_t* poutput_filename_evaluator = pstate->poutput_filename_evaluator;
1266 mv_t filename_mv = poutput_filename_evaluator->pprocess_func(poutput_filename_evaluator->pvstate, pvars);
1267 char fn_free_flags = 0;
1268 char* filename = mv_format_val(&filename_mv, &fn_free_flags);
1269
1270 multi_lrec_writer_output_list(pstate->pmulti_lrec_writer, poutrecs, filename,
1271 pstate->file_output_mode, pstate->flush_every_record, pvars->pctx);
1272
1273 sllv_free(poutrecs);
1274
1275 if (fn_free_flags)
1276 free(filename);
1277 mv_free(&filename_mv);
1278 }
1279
1280 // ----------------------------------------------------------------
handle_emit_lashed_common(emit_lashed_state_t * pstate,variables_t * pvars,sllv_t * poutrecs,char * oosvar_flatten_separator)1281 static void handle_emit_lashed_common(
1282 emit_lashed_state_t* pstate,
1283 variables_t* pvars,
1284 sllv_t* poutrecs,
1285 char* oosvar_flatten_separator)
1286 {
1287 for (int i = 0; i < pstate->num_emit_lashed_items; i++) {
1288 rval_evaluator_t* pev = pstate->ppitems[i]->pbasename_evaluator;
1289 pstate->pbasenames[i] = pev->pprocess_func(pev->pvstate, pvars);
1290 }
1291
1292 int names_all_non_null_or_error = TRUE;
1293 sllmv_t* pmvnames = evaluate_list(pstate->pemit_namelist_evaluators, pvars,
1294 &names_all_non_null_or_error);
1295 if (names_all_non_null_or_error) {
1296
1297 for (int i = 0; i < pstate->num_emit_lashed_items; i++) {
1298 emit_lashed_item_t* pitem = pstate->ppitems[i];
1299 pstate->pboxed_xvals[i] = pitem->pitem_xevaluator->pprocess_func(
1300 pitem->pitem_xevaluator->pvstate, pvars);
1301 pstate->ptop_values[i] = &pstate->pboxed_xvals[i].xval;
1302 }
1303
1304 mlhmmv_xvalues_to_lrecs_lashed(pstate->ptop_values, pstate->num_emit_lashed_items,
1305 pstate->pbasenames, pmvnames, poutrecs, pstate->do_full_prefixing, oosvar_flatten_separator);
1306
1307 for (int i = 0; i < pstate->num_emit_lashed_items; i++) {
1308 if (pstate->pboxed_xvals[i].is_ephemeral) {
1309 mlhmmv_xvalue_free(&pstate->pboxed_xvals[i].xval);
1310 }
1311 }
1312 }
1313 sllmv_free(pmvnames);
1314
1315 for (int i = 0; i < pstate->num_emit_lashed_items; i++) {
1316 mv_free(&pstate->pbasenames[i]);
1317 }
1318 }
1319
1320 // ================================================================
1321 struct _dump_state_t; // forward reference
1322
1323 typedef struct _dump_state_t {
1324 rxval_evaluator_t* ptarget_xevaluator;
1325
1326 FILE* stdfp;
1327 file_output_mode_t file_output_mode;
1328 rval_evaluator_t* poutput_filename_evaluator;
1329 int flush_every_record;
1330 multi_out_t* pmulti_out;
1331 } dump_state_t;
1332
1333 static mlr_dsl_cst_statement_handler_t handle_dump;
1334 static mlr_dsl_cst_statement_handler_t handle_dump_to_file;
1335 static mlr_dsl_cst_statement_freer_t free_dump;
1336
1337 // ----------------------------------------------------------------
alloc_dump(mlr_dsl_cst_t * pcst,mlr_dsl_ast_node_t * pnode,int type_inferencing,int context_flags)1338 mlr_dsl_cst_statement_t* alloc_dump(mlr_dsl_cst_t* pcst, mlr_dsl_ast_node_t* pnode,
1339 int type_inferencing, int context_flags)
1340 {
1341 dump_state_t* pstate = mlr_malloc_or_die(sizeof(dump_state_t));
1342
1343 pstate->ptarget_xevaluator = NULL;
1344 pstate->stdfp = NULL;
1345 pstate->poutput_filename_evaluator = NULL;
1346 pstate->pmulti_out = NULL;
1347 pstate->flush_every_record = pcst->flush_every_record;
1348
1349 mlr_dsl_ast_node_t* poutput_node = pnode->pchildren->phead->pvvalue;
1350 mlr_dsl_ast_node_t* pfilename_node = poutput_node->pchildren->phead->pvvalue;
1351 mlr_dsl_cst_statement_handler_t* phandler = NULL;
1352
1353 MLR_INTERNAL_CODING_ERROR_UNLESS(pnode->pchildren->length == 2);
1354
1355 mlr_dsl_ast_node_t* ptarget_node = pnode->pchildren->phead->pnext->pvvalue;
1356
1357 pstate->ptarget_xevaluator = rxval_evaluator_alloc_from_ast(
1358 ptarget_node, pcst->pfmgr, type_inferencing, context_flags);
1359
1360 if (pfilename_node->type == MD_AST_NODE_TYPE_STDOUT) {
1361 phandler = handle_dump;
1362 pstate->stdfp = stdout;
1363 } else if (pfilename_node->type == MD_AST_NODE_TYPE_STDERR) {
1364 phandler = handle_dump;
1365 pstate->stdfp = stderr;
1366 } else {
1367 pstate->poutput_filename_evaluator = rval_evaluator_alloc_from_ast(pfilename_node, pcst->pfmgr,
1368 type_inferencing, context_flags);
1369 pstate->file_output_mode = file_output_mode_from_ast_node_type(poutput_node->type);
1370 pstate->pmulti_out = multi_out_alloc();
1371 phandler = handle_dump_to_file;
1372 }
1373
1374 return mlr_dsl_cst_statement_valloc(
1375 pnode,
1376 phandler,
1377 free_dump,
1378 pstate);
1379 }
1380
1381 // ----------------------------------------------------------------
free_dump(mlr_dsl_cst_statement_t * pstatement,context_t * _)1382 static void free_dump(mlr_dsl_cst_statement_t* pstatement, context_t* _) {
1383 dump_state_t* pstate = pstatement->pvstate;
1384
1385 if (pstate->ptarget_xevaluator != NULL) {
1386 pstate->ptarget_xevaluator->pfree_func(pstate->ptarget_xevaluator);
1387 }
1388 if (pstate->poutput_filename_evaluator != NULL) {
1389 pstate->poutput_filename_evaluator->pfree_func(pstate->poutput_filename_evaluator);
1390 }
1391 if (pstate->pmulti_out != NULL) {
1392 multi_out_close(pstate->pmulti_out);
1393 multi_out_free(pstate->pmulti_out);
1394 }
1395
1396 free(pstate);
1397 }
1398
1399 // ----------------------------------------------------------------
handle_dump(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)1400 static void handle_dump(
1401 mlr_dsl_cst_statement_t* pstatement,
1402 variables_t* pvars,
1403 cst_outputs_t* pcst_outputs)
1404 {
1405 dump_state_t* pstate = pstatement->pvstate;
1406
1407 rxval_evaluator_t* ptarget_xevaluator = pstate->ptarget_xevaluator;
1408 boxed_xval_t boxed_xval = ptarget_xevaluator->pprocess_func(ptarget_xevaluator->pvstate, pvars);
1409
1410 if (boxed_xval.xval.is_terminal) {
1411 mlhmmv_print_terminal(&boxed_xval.xval.terminal_mlrval,
1412 pvars->json_quote_int_keys, pvars->json_quote_non_string_values,
1413 pstate->stdfp);
1414 fprintf(pstate->stdfp, "\n");
1415 } else {
1416 mlhmmv_level_print_stacked(boxed_xval.xval.pnext_level, 0, FALSE,
1417 pvars->json_quote_int_keys, pvars->json_quote_non_string_values,
1418 "", pvars->pctx->auto_line_term,
1419 pstate->stdfp);
1420 }
1421
1422 if (boxed_xval.is_ephemeral) {
1423 mlhmmv_xvalue_free(&boxed_xval.xval);
1424 }
1425 }
1426
1427 // ----------------------------------------------------------------
handle_dump_to_file(mlr_dsl_cst_statement_t * pstatement,variables_t * pvars,cst_outputs_t * pcst_outputs)1428 static void handle_dump_to_file(
1429 mlr_dsl_cst_statement_t* pstatement,
1430 variables_t* pvars,
1431 cst_outputs_t* pcst_outputs)
1432 {
1433 dump_state_t* pstate = pstatement->pvstate;
1434 rval_evaluator_t* poutput_filename_evaluator = pstate->poutput_filename_evaluator;
1435 mv_t filename_mv = poutput_filename_evaluator->pprocess_func(poutput_filename_evaluator->pvstate, pvars);
1436 char fn_free_flags;
1437 char* filename = mv_format_val(&filename_mv, &fn_free_flags);
1438
1439 FILE* outfp = multi_out_get(pstate->pmulti_out, filename, pstate->file_output_mode);
1440
1441 rxval_evaluator_t* ptarget_xevaluator = pstate->ptarget_xevaluator;
1442 boxed_xval_t boxed_xval = ptarget_xevaluator->pprocess_func(ptarget_xevaluator->pvstate, pvars);
1443
1444 if (boxed_xval.xval.is_terminal) {
1445 mlhmmv_print_terminal(&boxed_xval.xval.terminal_mlrval,
1446 pvars->json_quote_int_keys, pvars->json_quote_non_string_values,
1447 outfp);
1448 fprintf(outfp, "\n");
1449 } else if (boxed_xval.xval.pnext_level != NULL) {
1450 mlhmmv_level_print_stacked(boxed_xval.xval.pnext_level, 0, FALSE,
1451 pvars->json_quote_int_keys, pvars->json_quote_non_string_values,
1452 "", pvars->pctx->auto_line_term,
1453 outfp);
1454 }
1455
1456 if (pstate->flush_every_record)
1457 fflush(outfp);
1458
1459 if (fn_free_flags)
1460 free(filename);
1461 mv_free(&filename_mv);
1462
1463 if (boxed_xval.is_ephemeral) {
1464 mlhmmv_xvalue_free(&boxed_xval.xval);
1465 }
1466 }
1467