|
Packit |
a4aae4 |
// -*- mode: c++; c-basic-offset:4 -*-
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// This file is part of libdap, A C++ implementation of the OPeNDAP Data
|
|
Packit |
a4aae4 |
// Access Protocol.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Copyright (c) 2014 OPeNDAP, Inc.
|
|
Packit |
a4aae4 |
// Author: James Gallagher <jgallagher@opendap.org>
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// This library is free software; you can redistribute it and/or
|
|
Packit |
a4aae4 |
// modify it under the terms of the GNU Lesser General Public
|
|
Packit |
a4aae4 |
// License as published by the Free Software Foundation; either
|
|
Packit |
a4aae4 |
// version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// This library is distributed in the hope that it will be useful,
|
|
Packit |
a4aae4 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
a4aae4 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
a4aae4 |
// Lesser General Public License for more details.
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// You should have received a copy of the GNU Lesser General Public
|
|
Packit |
a4aae4 |
// License along with this library; if not, write to the Free Software
|
|
Packit |
a4aae4 |
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include <cstdlib>
|
|
Packit |
a4aae4 |
#include <cerrno>
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include <string>
|
|
Packit |
a4aae4 |
#include <sstream>
|
|
Packit |
a4aae4 |
#include <iterator>
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
//#define DODS_DEBUG
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include "D4FunctionScanner.h"
|
|
Packit |
a4aae4 |
#include "D4FunctionEvaluator.h"
|
|
Packit |
a4aae4 |
#include "d4_function_parser.tab.hh"
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include "DMR.h"
|
|
Packit |
a4aae4 |
#include "D4Group.h"
|
|
Packit |
a4aae4 |
#include "D4RValue.h"
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include "BaseType.h"
|
|
Packit |
a4aae4 |
#include "Array.h"
|
|
Packit |
a4aae4 |
#include "D4Enum.h"
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include "escaping.h"
|
|
Packit |
a4aae4 |
#include "util.h"
|
|
Packit |
a4aae4 |
#include "debug.h"
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
namespace libdap {
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/**
|
|
Packit |
a4aae4 |
* Parse the DAP4 function expression.
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* Calling this method with a DAP4 function expression builds a
|
|
Packit |
a4aae4 |
* D4RvalueList that can then be evaluated. The list of rvalues
|
|
Packit |
a4aae4 |
* can be accessed or evaluated (using the result() or eval()
|
|
Packit |
a4aae4 |
* methods). Note that the result is a list of rvalues because the
|
|
Packit |
a4aae4 |
* input can be zero or more function expressions.
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* @param expr The function expression.
|
|
Packit |
a4aae4 |
* @return True if the parse succeeded, false otherwise.
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
bool D4FunctionEvaluator::parse(const std::string &expr)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
d_expr = expr; // set for error messages. See the %initial-action section of .yy
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
std::istringstream iss(expr);
|
|
Packit |
a4aae4 |
D4FunctionScanner scanner(iss);
|
|
Packit |
a4aae4 |
D4FunctionParser parser(scanner, *this /* driver */);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (trace_parsing()) {
|
|
Packit |
a4aae4 |
parser.set_debug_level(1);
|
|
Packit |
a4aae4 |
parser.set_debug_stream(std::cerr);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
return parser.parse() == 0;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/**
|
|
Packit |
a4aae4 |
* Evaluate the recently parsed function expression and put the resulting
|
|
Packit |
a4aae4 |
* rvalues (which return values packaged in libdap BaseType objects) into
|
|
Packit |
a4aae4 |
* the top-level Group of the DMR passed as a param here.
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* @note The DMR passed to this method can (and usually will) be different
|
|
Packit |
a4aae4 |
* from the DMR passed to the D4FunctionEvaluator constructor. That DMR is
|
|
Packit |
a4aae4 |
* the 'source dataset' for values of variables used by the functions. It
|
|
Packit |
a4aae4 |
* is not generally where the results of evaluating the functions wind up.
|
|
Packit |
a4aae4 |
* Usually, you'll want those results in their own DMR; the functions
|
|
Packit |
a4aae4 |
* effectively make a new dataset. However, you _can_ pass the source dataset
|
|
Packit |
a4aae4 |
* DMR into this method and in that case the new variables will be appended
|
|
Packit |
a4aae4 |
* to its root Group.
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* @note This code is really just a place to package results. The actual
|
|
Packit |
a4aae4 |
* evaluation happens in the D4RValue object when values are accessed.
|
|
Packit |
a4aae4 |
* The parse() method here builds the list of D4RValue objects and this
|
|
Packit |
a4aae4 |
* code accesses the values which triggers the function evaluation. Thus,
|
|
Packit |
a4aae4 |
* if you'd like to build a system that performs lazy evaluation, you can
|
|
Packit |
a4aae4 |
* work with the result() method from this class instead of this one.
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* @note Calling this method will delete the D4RValueList object built
|
|
Packit |
a4aae4 |
* by the parse() method.
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* @param dmr Store the results here
|
|
Packit |
a4aae4 |
* @exception Throws Error if the evaluation fails.
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
void D4FunctionEvaluator::eval(DMR *function_result)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
#if 0
|
|
Packit |
a4aae4 |
ServerFunctionsList *sf_list = ServerFunctionsList::TheList();
|
|
Packit |
a4aae4 |
ServerFunction *scale = new D4TestFunction;
|
|
Packit |
a4aae4 |
sf_list->add_function(scale);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
D4FunctionEvaluator parser(dataset, sf_list);
|
|
Packit |
a4aae4 |
if (ce_parser_debug) parser.set_trace_parsing(true);
|
|
Packit |
a4aae4 |
bool parse_ok = parser.parse(function);
|
|
Packit |
a4aae4 |
if (!parse_ok)
|
|
Packit |
a4aae4 |
Error(malformed_expr, "Function Expression failed to parse.");
|
|
Packit |
a4aae4 |
else {
|
|
Packit |
a4aae4 |
if (ce_parser_debug) cerr << "Function Parse OK" << endl;
|
|
Packit |
a4aae4 |
D4RValueList *result = parser.result();
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
function_result = new DMR(&d4_factory, "function_results");
|
|
Packit |
a4aae4 |
#endif
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (!d_result) throw InternalErr(__FILE__, __LINE__, "Must parse() the function expression before calling eval()");
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
D4Group *root = function_result->root(); // Load everything in the root group
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
for (D4RValueList::iter i = d_result->begin(), e = d_result->end(); i != e; ++i) {
|
|
Packit |
a4aae4 |
// Copy the BaseTypes; this means all of the function results can
|
|
Packit |
a4aae4 |
// be deleted, which addresses the memory leak issue with function
|
|
Packit |
a4aae4 |
// results. This should also copy the D4Dimensions. jhrg 3/17/14
|
|
Packit |
a4aae4 |
root->add_var((*i)->value(*d_dmr));
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
delete d_result; // The parser/function allocates the BaseType*s that hold the results.
|
|
Packit |
a4aae4 |
d_result = 0;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Variables can use Dimensions and Enumerations, so those need to be copied
|
|
Packit |
a4aae4 |
// from the source dataset to the result. NB: The variables that refer to these
|
|
Packit |
a4aae4 |
// use weak pointers.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Make a set of D4Dimensions. For each variable in 'function_result', look
|
|
Packit |
a4aae4 |
// for its dimensions in 'dataset' (by name) and add a pointer to those to the
|
|
Packit |
a4aae4 |
// set. Then copy all the stuff in the set into the root group of 'function_
|
|
Packit |
a4aae4 |
// result.'
|
|
Packit |
a4aae4 |
set<D4Dimension*> dim_set;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
for (Constructor::Vars_iter i = root->var_begin(), ie = root->var_end(); i != ie; ++i) {
|
|
Packit |
a4aae4 |
if ((*i)->is_vector_type()) {
|
|
Packit |
a4aae4 |
Array *a = static_cast<Array*>(*i);
|
|
Packit |
a4aae4 |
for (Array::Dim_iter d = a->dim_begin(), de = a->dim_end(); d != de; ++d) {
|
|
Packit |
a4aae4 |
if (a->dimension_D4dim(d)) {
|
|
Packit |
a4aae4 |
dim_set.insert(a->dimension_D4dim(d));
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Copy the D4Dimensions and EnumDefs because this all goes in a new DMR - we don't
|
|
Packit |
a4aae4 |
// want to share those across DMRs because the DMRs delete those (so sharing htem
|
|
Packit |
a4aae4 |
// across DMRs would lead to dangling pointers.
|
|
Packit |
a4aae4 |
for (set<D4Dimension*>::iterator i = dim_set.begin(), e = dim_set.end(); i != e; ++i) {
|
|
Packit |
a4aae4 |
root->dims()->add_dim(*i);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Now lets do the enumerations....
|
|
Packit |
a4aae4 |
set<D4EnumDef*> enum_def_set;
|
|
Packit |
a4aae4 |
for (Constructor::Vars_iter i = root->var_begin(), ie = root->var_end(); i != ie; ++i) {
|
|
Packit |
a4aae4 |
if ((*i)->type() == dods_enum_c) {
|
|
Packit |
a4aae4 |
enum_def_set.insert(static_cast<D4Enum*>(*i)->enumeration());
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
for (set<D4EnumDef*>::iterator i = enum_def_set.begin(), e = enum_def_set.end(); i != e; ++i) {
|
|
Packit |
a4aae4 |
root->enum_defs()->add_enum(*i);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// libdap contains functions (in parser-util.cc) that test if a string
|
|
Packit |
a4aae4 |
// can be converted to an int32, e.g., but I used a more streamlined
|
|
Packit |
a4aae4 |
// approach here. 3/13/14 jhrg
|
|
Packit |
a4aae4 |
/**
|
|
Packit |
a4aae4 |
* Build and return a new RValue. Allocates the new D4RValue object.
|
|
Packit |
a4aae4 |
* The code tries first to find the id in the DMR - that is, it checks
|
|
Packit |
a4aae4 |
* first to see if it is a variable in the current dataset. If that
|
|
Packit |
a4aae4 |
* fails it will try to build an unsigned long long, a long long or
|
|
Packit |
a4aae4 |
* a double from the string (in that order). If that fails the code
|
|
Packit |
a4aae4 |
* converts the id into a string.
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* @param id An identifier (really a string) parsed from the function
|
|
Packit |
a4aae4 |
* expression. May contain quotes.
|
|
Packit |
a4aae4 |
* @return Return a pointer to the new allocated D4RValue object.
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
D4RValue *
|
|
Packit |
a4aae4 |
D4FunctionEvaluator::build_rvalue(const std::string &id)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
BaseType *btp = 0;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Look for the id in the dataset first
|
|
Packit |
a4aae4 |
if (top_basetype()) {
|
|
Packit |
a4aae4 |
btp = top_basetype()->var(id);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
else {
|
|
Packit |
a4aae4 |
btp = dmr()->root()->find_var(id);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (btp) return new D4RValue(btp);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// If the id is not a variable, try to turn it into a constant,
|
|
Packit |
a4aae4 |
// otherwise, its an error.
|
|
Packit |
a4aae4 |
char *end_ptr = 0;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
errno = 0;
|
|
Packit |
a4aae4 |
long long ll_val = strtoll(id.c_str(), &end_ptr, 0);
|
|
Packit |
a4aae4 |
if (*end_ptr == '\0' && errno == 0) return new D4RValue(ll_val);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Test for unsigned after signed since strtoull() accepts a minus sign
|
|
Packit |
a4aae4 |
// (and will return a huge number if that's the case). jhrg 3/13/14
|
|
Packit |
a4aae4 |
errno = 0;
|
|
Packit |
a4aae4 |
unsigned long long ull_val = strtoull(id.c_str(), &end_ptr, 0);
|
|
Packit |
a4aae4 |
if (*end_ptr == '\0' && errno == 0) return new D4RValue(ull_val);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
errno = 0;
|
|
Packit |
a4aae4 |
double d_val = strtod(id.c_str(), &end_ptr);
|
|
Packit |
a4aae4 |
if (*end_ptr == '\0' && errno == 0) return new D4RValue(d_val);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// To be a valid string, the id must be quoted (using double quotes)
|
|
Packit |
a4aae4 |
if (is_quoted(id)) return new D4RValue(www2id(id));
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// if it's none of these, return null
|
|
Packit |
a4aae4 |
return 0;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
template<typename T>
|
|
Packit |
a4aae4 |
std::vector<T> *
|
|
Packit |
a4aae4 |
D4FunctionEvaluator::init_arg_list(T val)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
std::vector<T> *arg_list = new std::vector<T>();
|
|
Packit |
a4aae4 |
if (get_arg_length_hint() > 0) arg_list->reserve(get_arg_length_hint());
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
arg_list->push_back(val);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
return arg_list;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Force an instantiation so this can be called from within the d4_function.yy
|
|
Packit |
a4aae4 |
// parser.
|
|
Packit |
a4aae4 |
template std::vector<dods_byte> *D4FunctionEvaluator::init_arg_list(dods_byte val);
|
|
Packit |
a4aae4 |
template std::vector<dods_int8> *D4FunctionEvaluator::init_arg_list(dods_int8 val);
|
|
Packit |
a4aae4 |
template std::vector<dods_uint16> *D4FunctionEvaluator::init_arg_list(dods_uint16 val);
|
|
Packit |
a4aae4 |
template std::vector<dods_int16> *D4FunctionEvaluator::init_arg_list(dods_int16 val);
|
|
Packit |
a4aae4 |
template std::vector<dods_uint32> *D4FunctionEvaluator::init_arg_list(dods_uint32 val);
|
|
Packit |
a4aae4 |
template std::vector<dods_int32> *D4FunctionEvaluator::init_arg_list(dods_int32 val);
|
|
Packit |
a4aae4 |
template std::vector<dods_uint64> *D4FunctionEvaluator::init_arg_list(dods_uint64 val);
|
|
Packit |
a4aae4 |
template std::vector<dods_int64> *D4FunctionEvaluator::init_arg_list(dods_int64 val);
|
|
Packit |
a4aae4 |
template std::vector<dods_float32> *D4FunctionEvaluator::init_arg_list(dods_float32 val);
|
|
Packit |
a4aae4 |
template std::vector<dods_float64> *D4FunctionEvaluator::init_arg_list(dods_float64 val);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// This method is called from the parser (see d4_function_parser.yy, down in the code
|
|
Packit |
a4aae4 |
// section). This will be called during the call to D4FunctionParser::parse(), that
|
|
Packit |
a4aae4 |
// is inside D4FunctionEvaluator::parse(...)
|
|
Packit |
a4aae4 |
void D4FunctionEvaluator::error(const libdap::location &l, const std::string &m)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
ostringstream oss;
|
|
Packit |
a4aae4 |
oss << l << ": " << m << ends;
|
|
Packit |
a4aae4 |
throw Error(malformed_expr, oss.str());
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
} /* namespace libdap */
|