mirror of
https://github.com/johnkerl/miller.git
synced 2026-07-21 18:40:13 +00:00
783 lines
30 KiB
C
783 lines
30 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <ctype.h> // for tolower(), toupper()
|
|
#include "lib/mlr_globals.h"
|
|
#include "lib/mlrutil.h"
|
|
#include "lib/mlrregex.h"
|
|
#include "lib/mtrand.h"
|
|
#include "mapping/mapper.h"
|
|
#include "mapping/rval_evaluators.h"
|
|
|
|
// ================================================================
|
|
// See comments in rval_evaluators.h
|
|
// ================================================================
|
|
|
|
static rval_evaluator_t* rval_evaluator_alloc_from_ast_aux(mlr_dsl_ast_node_t* pnode,
|
|
int type_inferencing, function_lookup_t* fcn_lookup_table);
|
|
|
|
// ================================================================
|
|
rval_evaluator_t* rval_evaluator_alloc_from_ast(mlr_dsl_ast_node_t* pnode, int type_inferencing) {
|
|
return rval_evaluator_alloc_from_ast_aux(pnode, type_inferencing, FUNCTION_LOOKUP_TABLE);
|
|
}
|
|
|
|
static rval_evaluator_t* rval_evaluator_alloc_from_ast_aux(mlr_dsl_ast_node_t* pnode,
|
|
int type_inferencing, function_lookup_t* fcn_lookup_table)
|
|
{
|
|
if (pnode->pchildren == NULL) { // leaf node
|
|
if (pnode->type == MD_AST_NODE_TYPE_FIELD_NAME) {
|
|
return rval_evaluator_alloc_from_field_name(pnode->text, type_inferencing);
|
|
} else if (pnode->type == MD_AST_NODE_TYPE_OOSVAR_NAME) {
|
|
return rval_evaluator_alloc_from_oosvar_name(pnode->text);
|
|
} else if (pnode->type == MD_AST_NODE_TYPE_STRNUM_LITERAL) {
|
|
return rval_evaluator_alloc_from_strnum_literal(pnode->text, type_inferencing);
|
|
} else if (pnode->type == MD_AST_NODE_TYPE_BOOLEAN_LITERAL) {
|
|
return rval_evaluator_alloc_from_boolean_literal(pnode->text);
|
|
} else if (pnode->type == MD_AST_NODE_TYPE_REGEXI) {
|
|
return rval_evaluator_alloc_from_strnum_literal(pnode->text, type_inferencing);
|
|
} else if (pnode->type == MD_AST_NODE_TYPE_CONTEXT_VARIABLE) {
|
|
return rval_evaluator_alloc_from_context_variable(pnode->text);
|
|
} else {
|
|
fprintf(stderr, "%s: internal coding error detected in file %s at line %d.\n",
|
|
MLR_GLOBALS.argv0, __FILE__, __LINE__);
|
|
exit(1);
|
|
}
|
|
|
|
} else if (pnode->type == MD_AST_NODE_TYPE_OOSVAR_LEVEL_KEY) {
|
|
return rval_evaluator_alloc_from_oosvar_level_keys(pnode);
|
|
|
|
} else if (pnode->type == MD_AST_NODE_TYPE_ENV) {
|
|
return rval_evaluator_alloc_from_environment(pnode, type_inferencing);
|
|
|
|
} else { // operator/function
|
|
if ((pnode->type != MD_AST_NODE_TYPE_FUNCTION_NAME)
|
|
&& (pnode->type != MD_AST_NODE_TYPE_OPERATOR)) {
|
|
fprintf(stderr, "%s: internal coding error detected in file %s at line %d (node type %s).\n",
|
|
MLR_GLOBALS.argv0, __FILE__, __LINE__, mlr_dsl_ast_node_describe_type(pnode->type));
|
|
exit(1);
|
|
}
|
|
char* func_name = pnode->text;
|
|
|
|
int user_provided_arity = pnode->pchildren->length;
|
|
|
|
check_arity_with_report(fcn_lookup_table, func_name, user_provided_arity);
|
|
|
|
rval_evaluator_t* pevaluator = NULL;
|
|
if (user_provided_arity == 0) {
|
|
pevaluator = rval_evaluator_alloc_from_zary_func_name(func_name);
|
|
} else if (user_provided_arity == 1) {
|
|
mlr_dsl_ast_node_t* parg1_node = pnode->pchildren->phead->pvvalue;
|
|
rval_evaluator_t* parg1 = rval_evaluator_alloc_from_ast_aux(parg1_node, type_inferencing, fcn_lookup_table);
|
|
pevaluator = rval_evaluator_alloc_from_unary_func_name(func_name, parg1);
|
|
} else if (user_provided_arity == 2) {
|
|
mlr_dsl_ast_node_t* parg1_node = pnode->pchildren->phead->pvvalue;
|
|
mlr_dsl_ast_node_t* parg2_node = pnode->pchildren->phead->pnext->pvvalue;
|
|
int type2 = parg2_node->type;
|
|
|
|
if ((streq(func_name, "=~") || streq(func_name, "!=~")) && type2 == MD_AST_NODE_TYPE_STRNUM_LITERAL) {
|
|
rval_evaluator_t* parg1 = rval_evaluator_alloc_from_ast_aux(parg1_node, type_inferencing,
|
|
fcn_lookup_table);
|
|
pevaluator = rval_evaluator_alloc_from_binary_regex_arg2_func_name(func_name,
|
|
parg1, parg2_node->text, FALSE);
|
|
} else if ((streq(func_name, "=~") || streq(func_name, "!=~")) && type2 == MD_AST_NODE_TYPE_REGEXI) {
|
|
rval_evaluator_t* parg1 = rval_evaluator_alloc_from_ast_aux(parg1_node, type_inferencing,
|
|
fcn_lookup_table);
|
|
pevaluator = rval_evaluator_alloc_from_binary_regex_arg2_func_name(func_name, parg1, parg2_node->text,
|
|
TYPE_INFER_STRING_FLOAT_INT);
|
|
} else {
|
|
// regexes can still be applied here, e.g. if the 2nd argument is a non-terminal AST: however
|
|
// the regexes will be compiled record-by-record rather than once at alloc time, which will
|
|
// be slower.
|
|
rval_evaluator_t* parg1 = rval_evaluator_alloc_from_ast_aux(parg1_node, type_inferencing,
|
|
fcn_lookup_table);
|
|
rval_evaluator_t* parg2 = rval_evaluator_alloc_from_ast_aux(parg2_node, type_inferencing,
|
|
fcn_lookup_table);
|
|
pevaluator = rval_evaluator_alloc_from_binary_func_name(func_name, parg1, parg2);
|
|
}
|
|
|
|
} else if (user_provided_arity == 3) {
|
|
mlr_dsl_ast_node_t* parg1_node = pnode->pchildren->phead->pvvalue;
|
|
mlr_dsl_ast_node_t* parg2_node = pnode->pchildren->phead->pnext->pvvalue;
|
|
mlr_dsl_ast_node_t* parg3_node = pnode->pchildren->phead->pnext->pnext->pvvalue;
|
|
int type2 = parg2_node->type;
|
|
|
|
if ((streq(func_name, "sub") || streq(func_name, "gsub")) && type2 == MD_AST_NODE_TYPE_STRNUM_LITERAL) {
|
|
// sub/gsub-regex special case:
|
|
rval_evaluator_t* parg1 = rval_evaluator_alloc_from_ast_aux(parg1_node, type_inferencing,
|
|
fcn_lookup_table);
|
|
rval_evaluator_t* parg3 = rval_evaluator_alloc_from_ast_aux(parg3_node, type_inferencing,
|
|
fcn_lookup_table);
|
|
pevaluator = rval_evaluator_alloc_from_ternary_regex_arg2_func_name(func_name, parg1, parg2_node->text,
|
|
FALSE, parg3);
|
|
|
|
} else if ((streq(func_name, "sub") || streq(func_name, "gsub")) && type2 == MD_AST_NODE_TYPE_REGEXI) {
|
|
// sub/gsub-regex special case:
|
|
rval_evaluator_t* parg1 = rval_evaluator_alloc_from_ast_aux(parg1_node, type_inferencing,
|
|
fcn_lookup_table);
|
|
rval_evaluator_t* parg3 = rval_evaluator_alloc_from_ast_aux(parg3_node, type_inferencing,
|
|
fcn_lookup_table);
|
|
pevaluator = rval_evaluator_alloc_from_ternary_regex_arg2_func_name(func_name, parg1, parg2_node->text,
|
|
TYPE_INFER_STRING_FLOAT_INT, parg3);
|
|
|
|
} else {
|
|
// regexes can still be applied here, e.g. if the 2nd argument is a non-terminal AST: however
|
|
// the regexes will be compiled record-by-record rather than once at alloc time, which will
|
|
// be slower.
|
|
rval_evaluator_t* parg1 = rval_evaluator_alloc_from_ast_aux(parg1_node, type_inferencing,
|
|
fcn_lookup_table);
|
|
rval_evaluator_t* parg2 = rval_evaluator_alloc_from_ast_aux(parg2_node, type_inferencing,
|
|
fcn_lookup_table);
|
|
rval_evaluator_t* parg3 = rval_evaluator_alloc_from_ast_aux(parg3_node, type_inferencing,
|
|
fcn_lookup_table);
|
|
pevaluator = rval_evaluator_alloc_from_ternary_func_name(func_name, parg1, parg2, parg3);
|
|
}
|
|
} else {
|
|
fprintf(stderr, "Miller: internal coding error: arity for function name \"%s\" misdetected.\n",
|
|
func_name);
|
|
exit(1);
|
|
}
|
|
if (pevaluator == NULL) {
|
|
fprintf(stderr, "Miller: unrecognized function name \"%s\".\n", func_name);
|
|
exit(1);
|
|
}
|
|
return pevaluator;
|
|
}
|
|
}
|
|
|
|
// ================================================================
|
|
typedef struct _rval_evaluator_field_name_state_t {
|
|
char* field_name;
|
|
} rval_evaluator_field_name_state_t;
|
|
|
|
static mv_t rval_evaluator_field_name_func_string_only(lrec_t* prec, lhmsv_t* ptyped_overlay, mlhmmv_t* poosvars,
|
|
string_array_t** ppregex_captures, context_t* pctx, void* pvstate)
|
|
{
|
|
rval_evaluator_field_name_state_t* pstate = pvstate;
|
|
// See comments in rval_evaluator.h and mapper_put.c regarding the typed-overlay map.
|
|
mv_t* poverlay = lhmsv_get(ptyped_overlay, pstate->field_name);
|
|
if (poverlay != NULL) {
|
|
// The lrec-evaluator logic will free its inputs and allocate new outputs, so we must copy
|
|
// a value here to feed into that. Otherwise the typed-overlay map would have its contents
|
|
// freed out from underneath it by the evaluator functions.
|
|
return mv_copy(poverlay);
|
|
} else {
|
|
char* string = lrec_get(prec, pstate->field_name);
|
|
if (string == NULL) {
|
|
return mv_absent();
|
|
} else if (*string == 0) {
|
|
return mv_void();
|
|
} else {
|
|
// string points into lrec memory and is valid as long as the lrec is.
|
|
return mv_from_string_no_free(string);
|
|
}
|
|
}
|
|
}
|
|
|
|
static mv_t rval_evaluator_field_name_func_string_float(lrec_t* prec, lhmsv_t* ptyped_overlay, mlhmmv_t* poosvars,
|
|
string_array_t** ppregex_captures, context_t* pctx, void* pvstate)
|
|
{
|
|
rval_evaluator_field_name_state_t* pstate = pvstate;
|
|
// See comments in rval_evaluator.h and mapper_put.c regarding the typed-overlay map.
|
|
mv_t* poverlay = lhmsv_get(ptyped_overlay, pstate->field_name);
|
|
if (poverlay != NULL) {
|
|
// The lrec-evaluator logic will free its inputs and allocate new outputs, so we must copy
|
|
// a value here to feed into that. Otherwise the typed-overlay map would have its contents
|
|
// freed out from underneath it by the evaluator functions.
|
|
return mv_copy(poverlay);
|
|
} else {
|
|
char* string = lrec_get(prec, pstate->field_name);
|
|
if (string == NULL) {
|
|
return mv_absent();
|
|
} else if (*string == 0) {
|
|
return mv_void();
|
|
} else {
|
|
double fltv;
|
|
if (mlr_try_float_from_string(string, &fltv)) {
|
|
return mv_from_float(fltv);
|
|
} else {
|
|
// string points into lrec memory and is valid as long as the lrec is.
|
|
return mv_from_string_no_free(string);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static mv_t rval_evaluator_field_name_func_string_float_int(lrec_t* prec, lhmsv_t* ptyped_overlay, mlhmmv_t* poosvars,
|
|
string_array_t** ppregex_captures, context_t* pctx, void* pvstate) {
|
|
rval_evaluator_field_name_state_t* pstate = pvstate;
|
|
// See comments in rval_evaluator.h and mapper_put.c regarding the typed-overlay map.
|
|
mv_t* poverlay = lhmsv_get(ptyped_overlay, pstate->field_name);
|
|
if (poverlay != NULL) {
|
|
// The lrec-evaluator logic will free its inputs and allocate new outputs, so we must copy
|
|
// a value here to feed into that. Otherwise the typed-overlay map would have its contents
|
|
// freed out from underneath it by the evaluator functions.
|
|
return mv_copy(poverlay);
|
|
} else {
|
|
char* string = lrec_get(prec, pstate->field_name);
|
|
if (string == NULL) {
|
|
return mv_absent();
|
|
} else if (*string == 0) {
|
|
return mv_void();
|
|
} else {
|
|
long long intv;
|
|
double fltv;
|
|
if (mlr_try_int_from_string(string, &intv)) {
|
|
return mv_from_int(intv);
|
|
} else if (mlr_try_float_from_string(string, &fltv)) {
|
|
return mv_from_float(fltv);
|
|
} else {
|
|
// string points into AST memory and is valid as long as the AST is.
|
|
return mv_from_string_no_free(string);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
static void rval_evaluator_field_name_free(rval_evaluator_t* pevaluator) {
|
|
rval_evaluator_field_name_state_t* pstate = pevaluator->pvstate;
|
|
free(pstate->field_name);
|
|
free(pstate);
|
|
free(pevaluator);
|
|
}
|
|
|
|
rval_evaluator_t* rval_evaluator_alloc_from_field_name(char* field_name, int type_inferencing) {
|
|
rval_evaluator_field_name_state_t* pstate = mlr_malloc_or_die(sizeof(rval_evaluator_field_name_state_t));
|
|
pstate->field_name = mlr_strdup_or_die(field_name);
|
|
|
|
rval_evaluator_t* pevaluator = mlr_malloc_or_die(sizeof(rval_evaluator_t));
|
|
pevaluator->pvstate = pstate;
|
|
pevaluator->pprocess_func = NULL;
|
|
switch (type_inferencing) {
|
|
case TYPE_INFER_STRING_ONLY:
|
|
pevaluator->pprocess_func = rval_evaluator_field_name_func_string_only;
|
|
break;
|
|
case TYPE_INFER_STRING_FLOAT:
|
|
pevaluator->pprocess_func = rval_evaluator_field_name_func_string_float;
|
|
break;
|
|
case TYPE_INFER_STRING_FLOAT_INT:
|
|
pevaluator->pprocess_func = rval_evaluator_field_name_func_string_float_int;
|
|
break;
|
|
default:
|
|
fprintf(stderr, "%s: internal coding error detected in file %s at line %d.\n",
|
|
MLR_GLOBALS.argv0, __FILE__, __LINE__);
|
|
exit(1);
|
|
break;
|
|
}
|
|
pevaluator->pfree_func = rval_evaluator_field_name_free;
|
|
|
|
return pevaluator;
|
|
}
|
|
|
|
// ================================================================
|
|
typedef struct _rval_evaluator_oosvar_name_state_t {
|
|
sllmv_t* pmvkeys;
|
|
} rval_evaluator_oosvar_name_state_t;
|
|
|
|
mv_t rval_evaluator_oosvar_name_func(lrec_t* prec, lhmsv_t* ptyped_overlay, mlhmmv_t* poosvars,
|
|
string_array_t** ppregex_captures, context_t* pctx, void* pvstate)
|
|
{
|
|
rval_evaluator_oosvar_name_state_t* pstate = pvstate;
|
|
int error = 0;
|
|
mv_t* pval = mlhmmv_get_terminal(poosvars, pstate->pmvkeys, &error);
|
|
if (pval != NULL) {
|
|
// The lrec-evaluator logic will free its inputs and allocate new outputs, so we must copy a value here to feed
|
|
// into that. Otherwise the typed-overlay map in mapper_put would have its contents freed out from underneath it
|
|
// by the evaluator functions.
|
|
if (pval->type == MT_STRING && *pval->u.strv == 0)
|
|
return mv_void();
|
|
else
|
|
return mv_copy(pval);
|
|
} else {
|
|
return mv_uninit();
|
|
}
|
|
}
|
|
|
|
static void rval_evaluator_oosvar_name_free(rval_evaluator_t* pevaluator) {
|
|
rval_evaluator_oosvar_name_state_t* pstate = pevaluator->pvstate;
|
|
sllmv_free(pstate->pmvkeys);
|
|
free(pstate);
|
|
free(pevaluator);
|
|
}
|
|
|
|
// This is used for evaluating @-variables that don't have brackets: e.g. @x vs. @x[$1].
|
|
// See comments above rval_evaluator_alloc_from_oosvar_level_keys for more information.
|
|
rval_evaluator_t* rval_evaluator_alloc_from_oosvar_name(char* oosvar_name) {
|
|
rval_evaluator_oosvar_name_state_t* pstate = mlr_malloc_or_die(sizeof(rval_evaluator_oosvar_name_state_t));
|
|
mv_t mv_name = mv_from_string(oosvar_name, NO_FREE);
|
|
pstate->pmvkeys = sllmv_single_no_free(&mv_name);
|
|
mv_free(&mv_name);
|
|
|
|
rval_evaluator_t* pevaluator = mlr_malloc_or_die(sizeof(rval_evaluator_t));
|
|
pevaluator->pvstate = pstate;
|
|
pevaluator->pprocess_func = NULL;
|
|
pevaluator->pprocess_func = rval_evaluator_oosvar_name_func;
|
|
pevaluator->pfree_func = rval_evaluator_oosvar_name_free;
|
|
|
|
return pevaluator;
|
|
}
|
|
|
|
// ================================================================
|
|
typedef struct _rval_evaluator_oosvar_level_keys_state_t {
|
|
sllv_t* poosvar_rhs_keylist_evaluators;
|
|
} rval_evaluator_oosvar_level_keys_state_t;
|
|
|
|
mv_t rval_evaluator_oosvar_level_keys_func(lrec_t* prec, lhmsv_t* ptyped_overlay, mlhmmv_t* poosvars,
|
|
string_array_t** ppregex_captures, context_t* pctx, void* pvstate)
|
|
{
|
|
rval_evaluator_oosvar_level_keys_state_t* pstate = pvstate;
|
|
|
|
int all_non_null_or_error = TRUE;
|
|
sllmv_t* pmvkeys = evaluate_list(pstate->poosvar_rhs_keylist_evaluators,
|
|
prec, ptyped_overlay, poosvars, ppregex_captures, pctx, &all_non_null_or_error);
|
|
|
|
mv_t rv = mv_uninit();
|
|
if (all_non_null_or_error) {
|
|
int error = 0;
|
|
mv_t* pval = mlhmmv_get_terminal(poosvars, pmvkeys, &error);
|
|
if (pval != NULL) {
|
|
if (pval->type == MT_STRING && *pval->u.strv == 0)
|
|
rv = mv_void();
|
|
else
|
|
rv = *pval;
|
|
}
|
|
}
|
|
|
|
sllmv_free(pmvkeys);
|
|
return rv;
|
|
}
|
|
|
|
static void rval_evaluator_oosvar_level_keys_free(rval_evaluator_t* pevaluator) {
|
|
rval_evaluator_oosvar_level_keys_state_t* pstate = pevaluator->pvstate;
|
|
for (sllve_t* pe = pstate->poosvar_rhs_keylist_evaluators->phead; pe != NULL; pe = pe->pnext) {
|
|
rval_evaluator_t* pevaluator = pe->pvvalue;
|
|
pevaluator->pfree_func(pevaluator);
|
|
}
|
|
sllv_free(pstate->poosvar_rhs_keylist_evaluators);
|
|
free(pstate);
|
|
free(pevaluator);
|
|
}
|
|
|
|
// ================================================================
|
|
// Example AST:
|
|
//
|
|
// $ mlr put -v '$y = @x[1]["two"][$3+4][@5]' /dev/null
|
|
// = (srec_assignment):
|
|
// y (field_name).
|
|
// [] (oosvar_level_key):
|
|
// [] (oosvar_level_key):
|
|
// [] (oosvar_level_key):
|
|
// [] (oosvar_level_key):
|
|
// x (oosvar_name).
|
|
// 1 (strnum_literal).
|
|
// two (strnum_literal).
|
|
// + (operator):
|
|
// 3 (field_name).
|
|
// 4 (strnum_literal).
|
|
// 5 (oosvar_name).
|
|
//
|
|
// Here past is the =; pright is the 5; pleft is the string of bracket references
|
|
// ending at the oosvar name.
|
|
//
|
|
// The job of this allocator is to set up a linked list of evaluators, with the first position for the oosvar name,
|
|
// and the rest for each of the bracketed expressions. This is used for when there *are* brackets; see
|
|
// rval_evaluator_alloc_from_oosvar_name for when there are no brackets.
|
|
|
|
rval_evaluator_t* rval_evaluator_alloc_from_oosvar_level_keys(mlr_dsl_ast_node_t* past) {
|
|
rval_evaluator_oosvar_level_keys_state_t* pstate = mlr_malloc_or_die(
|
|
sizeof(rval_evaluator_oosvar_level_keys_state_t));
|
|
|
|
sllv_t* poosvar_rhs_keylist_evaluators = sllv_alloc();
|
|
mlr_dsl_ast_node_t* pnode = past;
|
|
while (TRUE) {
|
|
// Bracket operators come in from the right. So the highest AST node is the rightmost
|
|
// map, and the lowest is the oosvar name. Hence sllv_prepend rather than sllv_append.
|
|
if (pnode->type == MD_AST_NODE_TYPE_OOSVAR_LEVEL_KEY) {
|
|
mlr_dsl_ast_node_t* pkeynode = pnode->pchildren->phead->pnext->pvvalue;
|
|
sllv_prepend(poosvar_rhs_keylist_evaluators,
|
|
rval_evaluator_alloc_from_ast(pkeynode, TYPE_INFER_STRING_FLOAT_INT));
|
|
} else {
|
|
// Oosvar expressions are of the form '@name[$index1][@index2+3][4]["five"]. The first one (name) is
|
|
// special: syntactically, it's outside the brackets, although that issue is for the parser to handle.
|
|
// Here it's special since it's always a string, never an expression that evaluates to string.
|
|
// Yet for the mlhmmv the first key isn't special.
|
|
sllv_prepend(poosvar_rhs_keylist_evaluators,
|
|
rval_evaluator_alloc_from_string(pnode->text));
|
|
}
|
|
if (pnode->pchildren == NULL)
|
|
break;
|
|
pnode = pnode->pchildren->phead->pvvalue;
|
|
}
|
|
pstate->poosvar_rhs_keylist_evaluators = poosvar_rhs_keylist_evaluators;
|
|
|
|
rval_evaluator_t* pevaluator = mlr_malloc_or_die(sizeof(rval_evaluator_t));
|
|
pevaluator->pvstate = pstate;
|
|
pevaluator->pprocess_func = NULL;
|
|
pevaluator->pprocess_func = rval_evaluator_oosvar_level_keys_func;
|
|
pevaluator->pfree_func = rval_evaluator_oosvar_level_keys_free;
|
|
|
|
return pevaluator;
|
|
}
|
|
|
|
// ================================================================
|
|
// This is used for evaluating strings and numbers in literal expressions, e.g. '$x = "abc"'
|
|
// or '$x = "left_\1". The values are subject to replacement with regex captures. See comments
|
|
// in mapper_put for more information.
|
|
//
|
|
// Compare rval_evaluator_alloc_from_string which doesn't do regex replacement: it is intended for
|
|
// oosvar names on expression left-hand sides (outside of this file).
|
|
|
|
typedef struct _rval_evaluator_strnum_literal_state_t {
|
|
mv_t literal;
|
|
} rval_evaluator_strnum_literal_state_t;
|
|
|
|
mv_t rval_evaluator_non_string_literal_func(lrec_t* prec, lhmsv_t* ptyped_overlay, mlhmmv_t* poosvars,
|
|
string_array_t** ppregex_captures, context_t* pctx, void* pvstate)
|
|
{
|
|
rval_evaluator_strnum_literal_state_t* pstate = pvstate;
|
|
return pstate->literal;
|
|
}
|
|
|
|
mv_t rval_evaluator_string_literal_func(lrec_t* prec, lhmsv_t* ptyped_overlay, mlhmmv_t* poosvars,
|
|
string_array_t** ppregex_captures, context_t* pctx, void* pvstate)
|
|
{
|
|
rval_evaluator_strnum_literal_state_t* pstate = pvstate;
|
|
char* input = pstate->literal.u.strv;
|
|
|
|
if (ppregex_captures == NULL || *ppregex_captures == NULL) {
|
|
return mv_from_string_no_free(input);
|
|
} else {
|
|
int was_allocated = FALSE;
|
|
char* output = interpolate_regex_captures(input, *ppregex_captures, &was_allocated);
|
|
if (was_allocated)
|
|
return mv_from_string_with_free(output);
|
|
else
|
|
return mv_from_string_no_free(output);
|
|
}
|
|
}
|
|
static void rval_evaluator_strnum_literal_free(rval_evaluator_t* pevaluator) {
|
|
rval_evaluator_strnum_literal_state_t* pstate = pevaluator->pvstate;
|
|
mv_free(&pstate->literal);
|
|
free(pstate);
|
|
free(pevaluator);
|
|
}
|
|
|
|
rval_evaluator_t* rval_evaluator_alloc_from_strnum_literal(char* string, int type_inferencing) {
|
|
rval_evaluator_strnum_literal_state_t* pstate = mlr_malloc_or_die(sizeof(rval_evaluator_strnum_literal_state_t));
|
|
rval_evaluator_t* pevaluator = mlr_malloc_or_die(sizeof(rval_evaluator_t));
|
|
|
|
if (string == NULL) {
|
|
pstate->literal = mv_absent();
|
|
pevaluator->pprocess_func = rval_evaluator_non_string_literal_func;
|
|
} else {
|
|
long long intv;
|
|
double fltv;
|
|
|
|
pevaluator->pprocess_func = NULL;
|
|
switch (type_inferencing) {
|
|
case TYPE_INFER_STRING_ONLY:
|
|
pstate->literal = mv_from_string_no_free(string);
|
|
pevaluator->pprocess_func = rval_evaluator_string_literal_func;
|
|
break;
|
|
|
|
case TYPE_INFER_STRING_FLOAT:
|
|
if (mlr_try_float_from_string(string, &fltv)) {
|
|
pstate->literal = mv_from_float(fltv);
|
|
pevaluator->pprocess_func = rval_evaluator_non_string_literal_func;
|
|
} else {
|
|
pstate->literal = mv_from_string_no_free(string);
|
|
pevaluator->pprocess_func = rval_evaluator_string_literal_func;
|
|
}
|
|
break;
|
|
|
|
case TYPE_INFER_STRING_FLOAT_INT:
|
|
if (mlr_try_int_from_string(string, &intv)) {
|
|
pstate->literal = mv_from_int(intv);
|
|
pevaluator->pprocess_func = rval_evaluator_non_string_literal_func;
|
|
} else if (mlr_try_float_from_string(string, &fltv)) {
|
|
pstate->literal = mv_from_float(fltv);
|
|
pevaluator->pprocess_func = rval_evaluator_non_string_literal_func;
|
|
} else {
|
|
pstate->literal = mv_from_string_no_free(string);
|
|
pevaluator->pprocess_func = rval_evaluator_string_literal_func;
|
|
}
|
|
break;
|
|
default:
|
|
fprintf(stderr, "%s: internal coding error detected in file %s at line %d.\n",
|
|
MLR_GLOBALS.argv0, __FILE__, __LINE__);
|
|
exit(1);
|
|
break;
|
|
}
|
|
}
|
|
pevaluator->pfree_func = rval_evaluator_strnum_literal_free;
|
|
|
|
pevaluator->pvstate = pstate;
|
|
return pevaluator;
|
|
}
|
|
|
|
// ================================================================
|
|
// This is intended only for oosvar names on expression left-hand sides (outside of this file).
|
|
// Compare rval_evaluator_alloc_from_strnum_literal.
|
|
|
|
typedef struct _rval_evaluator_string_state_t {
|
|
char* string;
|
|
} rval_evaluator_string_state_t;
|
|
|
|
mv_t rval_evaluator_string_func(lrec_t* prec, lhmsv_t* ptyped_overlay, mlhmmv_t* poosvars,
|
|
string_array_t** ppregex_captures, context_t* pctx, void* pvstate)
|
|
{
|
|
rval_evaluator_string_state_t* pstate = pvstate;
|
|
return mv_from_string_no_free(pstate->string);
|
|
}
|
|
static void rval_evaluator_string_free(rval_evaluator_t* pevaluator) {
|
|
rval_evaluator_string_state_t* pstate = pevaluator->pvstate;
|
|
free(pstate->string);
|
|
free(pstate);
|
|
free(pevaluator);
|
|
}
|
|
|
|
rval_evaluator_t* rval_evaluator_alloc_from_string(char* string) {
|
|
rval_evaluator_string_state_t* pstate = mlr_malloc_or_die(sizeof(rval_evaluator_string_state_t));
|
|
rval_evaluator_t* pevaluator = mlr_malloc_or_die(sizeof(rval_evaluator_t));
|
|
|
|
pstate->string = mlr_strdup_or_die(string);
|
|
pevaluator->pprocess_func = rval_evaluator_string_func;
|
|
pevaluator->pfree_func = rval_evaluator_string_free;
|
|
|
|
pevaluator->pvstate = pstate;
|
|
return pevaluator;
|
|
}
|
|
|
|
// ----------------------------------------------------------------
|
|
typedef struct _rval_evaluator_boolean_literal_state_t {
|
|
mv_t literal;
|
|
} rval_evaluator_boolean_literal_state_t;
|
|
|
|
mv_t rval_evaluator_boolean_literal_func(lrec_t* prec, lhmsv_t* ptyped_overlay, mlhmmv_t* poosvars,
|
|
string_array_t** ppregex_captures, context_t* pctx, void* pvstate)
|
|
{
|
|
rval_evaluator_boolean_literal_state_t* pstate = pvstate;
|
|
return pstate->literal;
|
|
}
|
|
|
|
static void rval_evaluator_boolean_literal_free(rval_evaluator_t* pevaluator) {
|
|
rval_evaluator_boolean_literal_state_t* pstate = pevaluator->pvstate;
|
|
mv_free(&pstate->literal);
|
|
free(pstate);
|
|
free(pevaluator);
|
|
}
|
|
|
|
rval_evaluator_t* rval_evaluator_alloc_from_boolean_literal(char* string) {
|
|
rval_evaluator_boolean_literal_state_t* pstate = mlr_malloc_or_die(sizeof(rval_evaluator_boolean_literal_state_t));
|
|
rval_evaluator_t* pevaluator = mlr_malloc_or_die(sizeof(rval_evaluator_t));
|
|
|
|
if (streq(string, "true")) {
|
|
pstate->literal = mv_from_true();
|
|
} else if (streq(string, "false")) {
|
|
pstate->literal = mv_from_false();
|
|
} else {
|
|
fprintf(stderr, "%s: internal coding error detected in file %s at line %d.\n",
|
|
MLR_GLOBALS.argv0, __FILE__, __LINE__);
|
|
exit(1);
|
|
}
|
|
pevaluator->pprocess_func = rval_evaluator_boolean_literal_func;
|
|
pevaluator->pfree_func = rval_evaluator_boolean_literal_free;
|
|
|
|
pevaluator->pvstate = pstate;
|
|
return pevaluator;
|
|
}
|
|
|
|
// ================================================================
|
|
// Example:
|
|
// $ mlr put -v '$y=ENV["X"]' ...
|
|
// AST BEGIN STATEMENTS (0):
|
|
// AST MAIN STATEMENTS (1):
|
|
// = (srec_assignment):
|
|
// y (field_name).
|
|
// env (env):
|
|
// ENV (env).
|
|
// X (strnum_literal).
|
|
// AST END STATEMENTS (0):
|
|
|
|
// ----------------------------------------------------------------
|
|
typedef struct _rval_evaluator_environment_state_t {
|
|
rval_evaluator_t* pname_evaluator;
|
|
} rval_evaluator_environment_state_t;
|
|
|
|
mv_t rval_evaluator_environment_func(lrec_t* prec, lhmsv_t* ptyped_overlay, mlhmmv_t* poosvars,
|
|
string_array_t** ppregex_captures, context_t* pctx, void* pvstate)
|
|
{
|
|
rval_evaluator_environment_state_t* pstate = pvstate;
|
|
|
|
mv_t mvname = pstate->pname_evaluator->pprocess_func(prec, ptyped_overlay,
|
|
poosvars, ppregex_captures, pctx, pstate->pname_evaluator->pvstate);
|
|
if (mv_is_null(&mvname)) {
|
|
return mv_absent();
|
|
}
|
|
char free_flags;
|
|
char* strname = mv_format_val(&mvname, &free_flags);
|
|
char* strvalue = getenv(strname);
|
|
if (strvalue == NULL) {
|
|
mv_free(&mvname);
|
|
if (free_flags & FREE_ENTRY_VALUE)
|
|
free(strname);
|
|
return mv_void();
|
|
}
|
|
mv_t rv = mv_from_string(strvalue, NO_FREE);
|
|
mv_free(&mvname);
|
|
if (free_flags & FREE_ENTRY_VALUE)
|
|
free(strname);
|
|
return rv;
|
|
}
|
|
|
|
static void rval_evaluator_environment_free(rval_evaluator_t* pevaluator) {
|
|
rval_evaluator_environment_state_t* pstate = pevaluator->pvstate;
|
|
pstate->pname_evaluator->pfree_func(pstate->pname_evaluator);
|
|
free(pstate);
|
|
free(pevaluator);
|
|
}
|
|
|
|
rval_evaluator_t* rval_evaluator_alloc_from_environment(mlr_dsl_ast_node_t* pnode, int type_inferencing) {
|
|
rval_evaluator_environment_state_t* pstate = mlr_malloc_or_die(sizeof(rval_evaluator_environment_state_t));
|
|
rval_evaluator_t* pevaluator = mlr_malloc_or_die(sizeof(rval_evaluator_t));
|
|
|
|
mlr_dsl_ast_node_t* pnamenode = pnode->pchildren->phead->pnext->pvvalue;
|
|
|
|
pstate->pname_evaluator = rval_evaluator_alloc_from_ast(pnamenode, type_inferencing);
|
|
pevaluator->pprocess_func = rval_evaluator_environment_func;
|
|
pevaluator->pfree_func = rval_evaluator_environment_free;
|
|
|
|
pevaluator->pvstate = pstate;
|
|
return pevaluator;
|
|
}
|
|
|
|
// ================================================================
|
|
mv_t rval_evaluator_NF_func(lrec_t* prec, lhmsv_t* ptyped_overlay, mlhmmv_t* poosvars,
|
|
string_array_t** ppregex_captures, context_t* pctx, void* pvstate)
|
|
{
|
|
return mv_from_int(prec->field_count);
|
|
}
|
|
static void rval_evaluator_NF_free(rval_evaluator_t* pevaluator) {
|
|
free(pevaluator);
|
|
}
|
|
rval_evaluator_t* rval_evaluator_alloc_from_NF() {
|
|
rval_evaluator_t* pevaluator = mlr_malloc_or_die(sizeof(rval_evaluator_t));
|
|
pevaluator->pvstate = NULL;
|
|
pevaluator->pprocess_func = rval_evaluator_NF_func;
|
|
pevaluator->pfree_func = rval_evaluator_NF_free;
|
|
return pevaluator;
|
|
}
|
|
|
|
// ----------------------------------------------------------------
|
|
mv_t rval_evaluator_NR_func(lrec_t* prec, lhmsv_t* ptyped_overlay, mlhmmv_t* poosvars,
|
|
string_array_t** ppregex_captures, context_t* pctx, void* pvstate)
|
|
{
|
|
return mv_from_int(pctx->nr);
|
|
}
|
|
static void rval_evaluator_NR_free(rval_evaluator_t* pevaluator) {
|
|
free(pevaluator);
|
|
}
|
|
rval_evaluator_t* rval_evaluator_alloc_from_NR() {
|
|
rval_evaluator_t* pevaluator = mlr_malloc_or_die(sizeof(rval_evaluator_t));
|
|
pevaluator->pvstate = NULL;
|
|
pevaluator->pprocess_func = rval_evaluator_NR_func;
|
|
pevaluator->pfree_func = rval_evaluator_NR_free;
|
|
return pevaluator;
|
|
}
|
|
|
|
// ----------------------------------------------------------------
|
|
mv_t rval_evaluator_FNR_func(lrec_t* prec, lhmsv_t* ptyped_overlay, mlhmmv_t* poosvars,
|
|
string_array_t** ppregex_captures, context_t* pctx, void* pvstate)
|
|
{
|
|
return mv_from_int(pctx->fnr);
|
|
}
|
|
static void rval_evaluator_FNR_free(rval_evaluator_t* pevaluator) {
|
|
free(pevaluator);
|
|
}
|
|
rval_evaluator_t* rval_evaluator_alloc_from_FNR() {
|
|
rval_evaluator_t* pevaluator = mlr_malloc_or_die(sizeof(rval_evaluator_t));
|
|
pevaluator->pvstate = NULL;
|
|
pevaluator->pprocess_func = rval_evaluator_FNR_func;
|
|
pevaluator->pfree_func = rval_evaluator_FNR_free;
|
|
return pevaluator;
|
|
}
|
|
|
|
// ----------------------------------------------------------------
|
|
mv_t rval_evaluator_FILENAME_func(lrec_t* prec, lhmsv_t* ptyped_overlay, mlhmmv_t* poosvars,
|
|
string_array_t** ppregex_captures, context_t* pctx, void* pvstate)
|
|
{
|
|
return mv_from_string_no_free(pctx->filename);
|
|
}
|
|
static void rval_evaluator_FILENAME_free(rval_evaluator_t* pevaluator) {
|
|
free(pevaluator);
|
|
}
|
|
|
|
rval_evaluator_t* rval_evaluator_alloc_from_FILENAME() {
|
|
rval_evaluator_t* pevaluator = mlr_malloc_or_die(sizeof(rval_evaluator_t));
|
|
pevaluator->pvstate = NULL;
|
|
pevaluator->pprocess_func = rval_evaluator_FILENAME_func;
|
|
pevaluator->pfree_func = rval_evaluator_FILENAME_free;
|
|
return pevaluator;
|
|
}
|
|
|
|
// ----------------------------------------------------------------
|
|
mv_t rval_evaluator_FILENUM_func(lrec_t* prec, lhmsv_t* ptyped_overlay, mlhmmv_t* poosvars,
|
|
string_array_t** ppregex_captures, context_t* pctx, void* pvstate)
|
|
{
|
|
return mv_from_int(pctx->filenum);
|
|
}
|
|
static void rval_evaluator_FILENUM_free(rval_evaluator_t* pevaluator) {
|
|
free(pevaluator);
|
|
}
|
|
rval_evaluator_t* rval_evaluator_alloc_from_FILENUM() {
|
|
rval_evaluator_t* pevaluator = mlr_malloc_or_die(sizeof(rval_evaluator_t));
|
|
pevaluator->pvstate = NULL;
|
|
pevaluator->pprocess_func = rval_evaluator_FILENUM_func;
|
|
pevaluator->pfree_func = rval_evaluator_FILENUM_free;
|
|
return pevaluator;
|
|
}
|
|
|
|
// ----------------------------------------------------------------
|
|
mv_t rval_evaluator_PI_func(lrec_t* prec, lhmsv_t* ptyped_overlay, mlhmmv_t* poosvars,
|
|
string_array_t** ppregex_captures, context_t* pctx, void* pvstate)
|
|
{
|
|
return mv_from_float(M_PI);
|
|
}
|
|
static void rval_evaluator_PI_free(rval_evaluator_t* pevaluator) {
|
|
free(pevaluator);
|
|
}
|
|
rval_evaluator_t* rval_evaluator_alloc_from_PI() {
|
|
rval_evaluator_t* pevaluator = mlr_malloc_or_die(sizeof(rval_evaluator_t));
|
|
pevaluator->pvstate = NULL;
|
|
pevaluator->pprocess_func = rval_evaluator_PI_func;
|
|
pevaluator->pfree_func = rval_evaluator_PI_free;
|
|
return pevaluator;
|
|
}
|
|
|
|
// ----------------------------------------------------------------
|
|
mv_t rval_evaluator_E_func(lrec_t* prec, lhmsv_t* ptyped_overlay, mlhmmv_t* poosvars,
|
|
string_array_t** ppregex_captures, context_t* pctx, void* pvstate)
|
|
{
|
|
return mv_from_float(M_E);
|
|
}
|
|
static void rval_evaluator_E_free(rval_evaluator_t* pevaluator) {
|
|
free(pevaluator);
|
|
}
|
|
rval_evaluator_t* rval_evaluator_alloc_from_E() {
|
|
rval_evaluator_t* pevaluator = mlr_malloc_or_die(sizeof(rval_evaluator_t));
|
|
pevaluator->pvstate = NULL;
|
|
pevaluator->pprocess_func = rval_evaluator_E_func;
|
|
pevaluator->pfree_func = rval_evaluator_E_free;
|
|
return pevaluator;
|
|
}
|
|
|
|
// ================================================================
|
|
rval_evaluator_t* rval_evaluator_alloc_from_context_variable(char* variable_name) {
|
|
if (streq(variable_name, "NF")) { return rval_evaluator_alloc_from_NF();
|
|
} else if (streq(variable_name, "NR")) { return rval_evaluator_alloc_from_NR();
|
|
} else if (streq(variable_name, "FNR")) { return rval_evaluator_alloc_from_FNR();
|
|
} else if (streq(variable_name, "FILENAME")) { return rval_evaluator_alloc_from_FILENAME();
|
|
} else if (streq(variable_name, "FILENUM")) { return rval_evaluator_alloc_from_FILENUM();
|
|
} else if (streq(variable_name, "PI")) { return rval_evaluator_alloc_from_PI();
|
|
} else if (streq(variable_name, "E")) { return rval_evaluator_alloc_from_E();
|
|
} else { return NULL;
|
|
}
|
|
}
|