blob: 20847e3ef4692b135a3592c376d5bffbd6a52982 [file] [log] [blame]
/*
* Copyright (c) 2021-2025 Symas Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of the Symas Corporation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// This file is included only by parse.y
#include <map>
/*
* Intrinsics
* types are:
* A Alphabetic
* D DBCS
* I Integer
* K Keyword
* N Numeric
* O Other
* U National
* 8 UTF-8
* X Alphanumeric
* n variadic
* We use just A, I, N, or X, choosing the most general for each parameter.
*/
static const function_descr_t function_descrs[] = {
{ ABS, "ABS",
"__gg__abs", "N", {}, FldNumericBin5 },
{ ACOS, "ACOS",
"__gg__acos", "N", {}, FldNumericBin5 },
{ ANNUITY, "ANNUITY",
"__gg__annuity", "NI", {}, FldNumericBin5 },
{ ASIN, "ASIN",
"__gg__asin", "N", {}, FldNumericBin5 },
{ ATAN, "ATAN",
"__gg__atan", "N", {}, FldNumericBin5 },
{ BASECONVERT, "BASECONVERT",
"__gg__baseconvert", "XII", {}, FldNumericBin5 },
{ BIT_OF, "BIT-OF",
"__gg__bit_of", "X", {}, FldAlphanumeric },
{ BIT_TO_CHAR, "BIT-TO-CHAR",
"__gg__bit_to_char", "X", {}, FldAlphanumeric },
// BOOLEAN-OF-INTEGER requires FldBoolean
{ BOOLEAN_OF_INTEGER, "BOOLEAN-OF-INTEGER",
"__gg__boolean_of_integer", "II", {}, FldNumericBin5 },
{ BYTE_LENGTH, "BYTE-LENGTH",
"__gg__byte_length", "X", {}, FldNumericBin5 },
{ CHAR, "CHAR",
"__gg__char", "I", {}, FldAlphanumeric },
{ CHAR_NATIONAL, "CHAR-NATIONAL",
"__gg__char_national", "I", {}, FldAlphanumeric },
{ COMBINED_DATETIME, "COMBINED-DATETIME",
"__gg__combined_datetime", "IN", {}, FldNumericBin5 },
{ CONCAT, "CONCAT",
"__gg__concat", "n", {}, FldAlphanumeric },
{ CONVERT, "CONVERT",
"__gg__convert", "XII", {}, FldAlphanumeric },
{ COS, "COS",
"__gg__cos", "N", {}, FldNumericBin5 },
{ CURRENT_DATE, "CURRENT-DATE",
"__gg__current_date", "", {}, FldAlphanumeric },
{ DATE_OF_INTEGER, "DATE-OF-INTEGER",
"__gg__date_of_integer", "I", {}, FldNumericBin5 },
{ DATE_TO_YYYYMMDD, "DATE-TO-YYYYMMDD",
"__gg__date_to_yyyymmdd", "III", {}, FldNumericBin5 },
{ DAY_OF_INTEGER, "DAY-OF-INTEGER",
"__gg__day_of_integer", "I", {}, FldNumericBin5 },
{ DAY_TO_YYYYDDD, "DAY-TO-YYYYDDD",
"__gg__day_to_yyyyddd", "III", {}, FldNumericBin5 },
{ DISPLAY_OF, "DISPLAY-OF",
"__gg__display_of", "UUI", {}, FldAlphanumeric },
{ E, "E",
"__gg_e", "", {}, FldNumericBin5 },
{ EXCEPTION_FILE, "EXCEPTION-FILE",
"__gg__func_exception_file", "", {}, FldAlphanumeric },
{ EXCEPTION_FILE_N, "EXCEPTION-FILE-N",
"__gg__func_exception_file_n", "", {}, FldAlphanumeric },
{ EXCEPTION_LOCATION, "EXCEPTION-LOCATION",
"__gg__func_exception_location", "", {}, FldAlphanumeric },
{ EXCEPTION_LOCATION_N, "EXCEPTION-LOCATION-N",
"__gg__func_exception_location_n", "", {}, FldAlphanumeric },
{ EXCEPTION_STATEMENT, "EXCEPTION-STATEMENT",
"__gg__func_exception_statement", "", {}, FldAlphanumeric },
{ EXCEPTION_STATUS, "EXCEPTION-STATUS",
"__gg__func_exception_status", "", {}, FldAlphanumeric },
{ EXP, "EXP",
"__gg__exp", "N", {}, FldNumericBin5 },
{ EXP10, "EXP10",
"__gg__exp10", "N", {}, FldNumericBin5 },
{ FACTORIAL, "FACTORIAL",
"__gg__factorial", "I", {}, FldNumericBin5 },
{ FIND_STRING, "FIND-STRING",
"__gg__find_string", "AXI", {}, FldNumericBin5 },
{ FORMATTED_CURRENT_DATE, "FORMATTED-CURRENT-DATE",
"__gg__formatted_current_date", "X", {}, FldAlphanumeric },
{ FORMATTED_DATE, "FORMATTED-DATE",
"__gg__formatted_date", "XX", {}, FldAlphanumeric },
{ FORMATTED_DATETIME, "FORMATTED-DATETIME",
"__gg__formatted_datetime", "XINI", {}, FldAlphanumeric },
{ FORMATTED_TIME, "FORMATTED-TIME",
"__gg__formatted_time", "INI", {}, FldNumericBin5 },
{ FRACTION_PART, "FRACTION-PART",
"__gg__fraction_part", "N", {}, FldNumericBin5 },
{ HEX_OF, "HEX-OF",
"__gg__hex_of", "X", {}, FldAlphanumeric },
{ HEX_TO_CHAR, "HEX-TO-CHAR",
"__gg__hex_to_char", "X", {}, FldAlphanumeric },
{ HIGHEST_ALGEBRAIC, "HIGHEST-ALGEBRAIC",
"__gg__highest_algebraic", "N", {}, FldNumericBin5 },
{ INTEGER, "INTEGER",
"__gg__integer", "N", {}, FldNumericBin5 },
// requires FldBoolean
{ INTEGER_OF_BOOLEAN, "INTEGER-OF-BOOLEAN",
"__gg__integer_of_boolean", "B", {}, FldNumericBin5 },
{ INTEGER_OF_DATE, "INTEGER-OF-DATE",
"__gg__integer_of_date", "I", {}, FldNumericBin5 },
{ INTEGER_OF_DAY, "INTEGER-OF-DAY",
"__gg__integer_of_day", "I", {}, FldNumericBin5 },
{ INTEGER_OF_FORMATTED_DATE, "INTEGER-OF-FORMATTED-DATE",
"__gg__integer_of_formatted_date", "XX", {}, FldAlphanumeric },
{ INTEGER_PART, "INTEGER-PART",
"__gg__integer_part", "N", {}, FldNumericBin5 },
{ LENGTH, "LENGTH",
"__gg__length", "X", {}, FldNumericBin5 },
{ LOCALE_COMPARE, "LOCALE-COMPARE",
"__gg__locale_compare", "XXX", {}, FldNumericBin5 },
{ LOCALE_DATE, "LOCALE-DATE",
"__gg__locale_date", "XX", {}, FldNumericBin5 },
{ LOCALE_TIME, "LOCALE-TIME",
"__gg__locale_time", "XX", {}, FldNumericBin5 },
{ LOCALE_TIME_FROM_SECONDS, "LOCALE-TIME-FROM-SECONDS",
"__gg__locale_time_from_seconds", "NX", {}, FldNumericBin5 },
{ LOG, "LOG",
"__gg__log", "N", {}, FldNumericBin5 },
{ LOG10, "LOG10",
"__gg__log10", "N", {}, FldNumericBin5 },
{ LOWER_CASE, "LOWER-CASE",
"__gg__lower_case", "X", {}, FldAlphanumeric },
{ LOWEST_ALGEBRAIC, "LOWEST-ALGEBRAIC",
"__gg__lowest_algebraic", "N", {}, FldNumericBin5 },
{ MAXX, "MAX",
"__gg__max", "n", {}, FldAlphanumeric },
{ MEAN, "MEAN",
"__gg__mean", "n", {}, FldNumericBin5 },
{ MEDIAN, "MEDIAN",
"__gg__median", "n", {}, FldNumericBin5 },
{ MIDRANGE, "MIDRANGE",
"__gg__midrange", "n", {}, FldNumericBin5 },
{ MINN, "MIN",
"__gg__min", "n", {}, FldAlphanumeric },
{ MOD, "MOD",
"__gg__mod", "IN", {}, FldNumericBin5 },
{ MODULE_NAME, "MODULE-NAME",
"__gg__module_name", "I", {}, FldAlphanumeric },
{ NATIONAL_OF, "NATIONAL-OF",
"__gg__national_of", "XX", {}, FldAlphanumeric },
{ NUMVAL, "NUMVAL",
"__gg__numval", "X", {}, FldNumericBin5 },
{ NUMVAL_C, "NUMVAL-C",
"__gg__numval_c", "XXU", {}, FldNumericBin5 },
{ NUMVAL_F, "NUMVAL-F",
"__gg__numval_f", "X", {}, FldNumericBin5 },
{ ORD, "ORD",
"__gg__ord", "X", {}, FldNumericBin5 },
{ ORD_MAX, "ORD-MAX",
"__gg__ord_max", "n", {}, FldNumericBin5 },
{ ORD_MIN, "ORD-MIN",
"__gg__ord_min", "n", {}, FldNumericBin5 },
{ PI, "PI",
"__gg__pi", "", {}, FldNumericBin5 },
{ PRESENT_VALUE, "PRESENT-VALUE",
"__gg__present_value", "n", {}, FldNumericBin5 },
{ RANDOM, "RANDOM",
"__gg__random", "I", {}, FldNumericBin5 },
{ RANGE, "RANGE",
"__gg__range", "n", {}, FldNumericBin5 },
{ REM, "REM",
"__gg__rem", "NN", {}, FldNumericBin5 },
{ REVERSE, "REVERSE",
"__gg__reverse", "X", {}, FldAlphanumeric },
{ SECONDS_FROM_FORMATTED_TIME, "SECONDS-FROM-FORMATTED-TIME",
"__gg__seconds_from_formatted_time", "XX", {}, FldAlphanumeric },
{ SECONDS_PAST_MIDNIGHT, "SECONDS_PAST_MIDNIGHT",
"__gg__seconds_past_midnight", "", {}, FldAlphanumeric },
{ SIGN, "SIGN",
"__gg__sign", "N", {}, FldNumericBin5 },
{ SIN, "SIN",
"__gg__sin", "N", {}, FldNumericBin5 },
{ SMALLEST_ALGEBRAIC, "SMALLEST-ALGEBRAIC",
"__gg__smallest_algebraic", "N", {}, FldNumericBin5 },
{ SQRT, "SQRT",
"__gg__sqrt", "N", {}, FldNumericBin5 },
{ STANDARD_COMPARE, "STANDARD-COMPARE",
"__gg__standard_compare", "XXXI", {}, FldAlphanumeric },
{ STANDARD_DEVIATION, "STANDARD-DEVIATION",
"__gg__standard_deviation", "n", {}, FldNumericBin5 },
{ SUBSTITUTE, "SUBSTITUTE",
"__gg__substitute", "XXX", {}, FldAlphanumeric },
{ SUM, "SUM",
"__gg__sum", "n", {}, FldNumericBin5 },
{ TAN, "TAN",
"__gg__tan", "N", {}, FldNumericBin5 },
{ TEST_DATE_YYYYMMDD, "TEST-DATE-YYYYMMDD",
"__gg__test_date_yyyymmdd", "I", {}, FldNumericBin5 },
{ TEST_DAY_YYYYDDD, "TEST-DAY-YYYYDDD",
"__gg__test_day_yyyyddd", "I", {}, FldNumericBin5 },
{ TEST_FORMATTED_DATETIME, "TEST-FORMATTED-DATETIME",
"__gg__test_formatted_datetime", "XX", {}, FldNumericBin5 },
{ TEST_NUMVAL, "TEST-NUMVAL",
"__gg__test_numval", "X", {}, FldNumericBin5 },
{ TEST_NUMVAL_C, "TEST-NUMVAL-C",
"__gg__test_numval_c", "XXU", {}, FldNumericBin5 },
{ TEST_NUMVAL_F, "TEST-NUMVAL-F",
"__gg__test_numval_f", "X", {}, FldNumericBin5 },
{ TRIM, "TRIM",
"__gg__trim", "XI", {}, FldNumericBin5 },
{ ULENGTH, "ULENGTH",
"__gg__ulength", "X", {}, FldAlphanumeric },
{ UPOS, "UPOS",
"__gg__upos", "XI", {}, FldAlphanumeric },
{ UPPER_CASE, "UPPER-CASE",
"__gg__upper_case", "X", {}, FldAlphanumeric },
{ USUBSTR, "USUBSTR",
"__gg__usubstr", "XII", {}, FldAlphanumeric },
{ USUPPLEMENTARY, "USUPPLEMENTARY",
"__gg__usupplementary", "X", {}, FldAlphanumeric },
{ UUID4, "UUID4",
"__gg_uuid4", "", {}, FldAlphanumeric },
{ UVALID, "UVALID",
"__gg__uvalid", "X", {}, FldAlphanumeric },
{ UWIDTH, "UWIDTH",
"__gg__uwidth", "XI", {}, FldAlphanumeric },
{ VARIANCE, "VARIANCE",
"__gg__variance", "n", {}, FldNumericBin5 },
{ WHEN_COMPILED, "WHEN-COMPILED",
"__gg__when_compiled", "", {}, FldAlphanumeric },
{ YEAR_TO_YYYY, "YEAR-TO-YYYY",
"__gg__year_to_yyyy", "III", {}, FldNumericBin5 },
};
static const
function_descr_t *function_descrs_end = function_descrs + COUNT_OF(function_descrs);
class cname_cmp {
const char *cname;
public:
explicit cname_cmp( const char *cname ) : cname(cname) {}
bool operator()( const function_descr_t& descr ) {
return strlen(cname) == strlen(descr.cname) &&
0 == strcmp(cname, descr.cname);
}
bool operator()( const char that[] ) {
return strlen(cname) == strlen(that) &&
0 == strcmp(cname, that);
}
};
static int
intrinsic_token_of( const char name[] ) {
auto pdescr = std::find_if( function_descrs, function_descrs_end,
[name]( const function_descr_t& descr ) {
return 0 == strcmp(name, descr.name);
} );
return pdescr == function_descrs_end? 0 : pdescr->token;
}
/*
* For variadic intrinsic functions, ensure all parameters are commensurate.
* Return pointer in 1st inconsistent parameter type.
* Return NULL to indicate success.
*/
static cbl_refer_t *
intrinsic_inconsistent_parameter( size_t n, cbl_refer_t *args ) {
class commensurate_type {
cbl_refer_t first;
public:
explicit commensurate_type( const cbl_refer_t& first ) : first(first) {}
bool operator()( const cbl_refer_t& arg ) const {
return is_numeric(first.field) == is_numeric(arg.field);
}
};
auto p = std::find_if_not(args, args + n, commensurate_type(args[0]));
return p == args + n? NULL : p;
}
static cbl_field_type_t
intrinsic_return_type( int token ) {
auto p = std::find_if( function_descrs,
function_descrs_end,
[token]( const auto& descr ) {
return token == descr.token;
} );
return p == function_descrs_end? FldAlphanumeric : p->ret_type;
}
static const char *
intrinsic_cname( int token ) {
auto p = std::find_if( function_descrs,
function_descrs_end,
[token]( const auto& descr ) {
return token == descr.token;
} );
return p == function_descrs_end? NULL : p->cname;
}
const char *
intrinsic_function_name( int token ) {
auto p = std::find_if( function_descrs,
function_descrs_end,
[token]( const auto& descr ) {
return token == descr.token;
} );
return p == function_descrs_end? NULL : p->name;
}
/*
* Provide supplied function parameters.
* Return index to 1st invalid parameter type.
* Return N to indicate success.
*/
static size_t
intrinsic_invalid_parameter( int token,
const std::vector<cbl_refer_t>& args )
{
auto p = std::find_if( function_descrs,
function_descrs_end,
[token]( const auto& descr ) {
return token == descr.token;
} );
if( p == function_descrs_end ) {
cbl_internal_error( "%s: intrinsic function %qs not found",
__func__, keyword_str(token) );
}
gcc_assert(!args.empty());
gcc_assert(p < function_descrs_end);
const function_descr_t& descr = *p;
size_t i = 0;
for( auto arg : args ) {
if( arg.field == NULL ) {
i++;
continue;
}
assert(i < strlen(descr.types));
switch(descr.types[i]) {
case 'A' : //Alphabetic
case 'I' : //Integer
case 'N' : //Numeric
case 'X' : //Alphanumeric
break;
case 'n' : //variadic
return args.size();
break;
case 'D' : //DBCS
case 'K' : //Keyword
case 'O' : //Other
case 'U' : //National
case '8' : //UTF-8
default:
cbl_internal_error( "%s: invalid function descr type '%c'",
__func__, descr.types[i]);
}
static std::map<char, const char*> typenames
{
{ 'A', "Alphabetic" },
{ 'I', "Integer" },
{ 'N', "Numeric" },
{ 'X', "Alphanumeric" },
};
switch( arg.field->type ) {
case FldInvalid:
case FldClass:
case FldConditional:
case FldForward:
case FldIndex:
yyerror("%s: field '%s' (%s) invalid for %s parameter",
descr.name,
arg.field->name, cbl_field_type_str(arg.field->type),
typenames[descr.types[i]]);
return i;
break;
case FldGroup:
default:
break;
}
if( is_numeric(arg.field) || is_integer_literal(arg.field)) {
if( strchr("A", descr.types[i]) != NULL ) {
yyerror("%s: numeric field '%s' (%s) invalid for %s parameter",
descr.name,
arg.field->name, cbl_field_type_str(arg.field->type),
typenames[descr.types[i]]);
return i;
}
} else { // string field
if( strchr("IN", descr.types[i]) != NULL ) {
if( data_category_of(arg.field) == data_alphabetic_e ) {
yyerror("%s: non-numeric field '%s' (%s) invalid for %s parameter",
descr.name,
arg.field->name, cbl_field_type_str(arg.field->type),
typenames[descr.types[i]]);
return i;
}
}
}
i++;
} // end loop
return args.size();
}
/*
* Functions used by code gen
*/
size_t
intrinsic_parameter_count( const char cname[] ) {
const function_descr_t *descr = std::find_if(function_descrs,
function_descrs_end, cname_cmp(cname));
return descr == function_descrs_end || descr->types[0] == 'n'?
size_t(-1) : strlen(descr->types);
}
#if 0
static int
yyreport_syntax_error (const yypcontext_t *ctx)
{
int res = 0;
YYLOCATION_PRINT (stderr, yypcontext_location (ctx));
fprintf (stderr, ": syntax error");
// Report the tokens expected at this point.
{
enum { TOKENMAX = 5 };
yysymbol_kind_t expected[TOKENMAX];
int n = yypcontext_expected_tokens (ctx, expected, TOKENMAX);
if (n < 0)
// Forward errors to yyparse.
res = n;
else
for (int i = 0; i < n; ++i)
fprintf (stderr, "%s %s",
i == 0 ? ": expected" : " or", yysymbol_name (expected[i]));
}
// Report the unexpected token.
{
yysymbol_kind_t lookahead = yypcontext_token (ctx);
if (lookahead != YYSYMBOL_YYEMPTY)
fprintf (stderr, " before %s", yysymbol_name (lookahead));
}
fprintf (stderr, "\n");
return res;
}
#endif