libdap  Updated for version 3.17.0
D4FunctionEvaluator.cc
00001 // -*- mode: c++; c-basic-offset:4 -*-
00002 
00003 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
00004 // Access Protocol.
00005 
00006 // Copyright (c) 2014 OPeNDAP, Inc.
00007 // Author: James Gallagher <jgallagher@opendap.org>
00008 //
00009 // This library is free software; you can redistribute it and/or
00010 // modify it under the terms of the GNU Lesser General Public
00011 // License as published by the Free Software Foundation; either
00012 // version 2.1 of the License, or (at your option) any later version.
00013 //
00014 // This library is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017 // Lesser General Public License for more details.
00018 //
00019 // You should have received a copy of the GNU Lesser General Public
00020 // License along with this library; if not, write to the Free Software
00021 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00022 //
00023 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
00024 
00025 #include <cstdlib>
00026 #include <cerrno>
00027 
00028 #include <string>
00029 #include <sstream>
00030 #include <iterator>
00031 
00032 //#define DODS_DEBUG
00033 
00034 #include "D4FunctionScanner.h"
00035 #include "D4FunctionEvaluator.h"
00036 #include "d4_function_parser.tab.hh"
00037 
00038 #include "DMR.h"
00039 #include "D4Group.h"
00040 #include "D4RValue.h"
00041 
00042 #include "BaseType.h"
00043 #include "Array.h"
00044 #include "D4Enum.h"
00045 
00046 #include "escaping.h"
00047 #include "util.h"
00048 #include "debug.h"
00049 
00050 namespace libdap {
00051 
00064 bool D4FunctionEvaluator::parse(const std::string &expr)
00065 {
00066     d_expr = expr;      // set for error messages. See the %initial-action section of .yy
00067 
00068     std::istringstream iss(expr);
00069     D4FunctionScanner scanner(iss);
00070     D4FunctionParser parser(scanner, *this /* driver */);
00071 
00072     if (trace_parsing()) {
00073         parser.set_debug_level(1);
00074         parser.set_debug_stream(std::cerr);
00075     }
00076 
00077     return parser.parse() == 0;
00078 }
00079 
00107 void D4FunctionEvaluator::eval(DMR *function_result)
00108 {
00109 #if 0
00110     ServerFunctionsList *sf_list = ServerFunctionsList::TheList();
00111     ServerFunction *scale = new D4TestFunction;
00112     sf_list->add_function(scale);
00113 
00114     D4FunctionEvaluator parser(dataset, sf_list);
00115     if (ce_parser_debug) parser.set_trace_parsing(true);
00116     bool parse_ok = parser.parse(function);
00117     if (!parse_ok)
00118     Error(malformed_expr, "Function Expression failed to parse.");
00119     else {
00120         if (ce_parser_debug) cerr << "Function Parse OK" << endl;
00121         D4RValueList *result = parser.result();
00122 
00123         function_result = new DMR(&d4_factory, "function_results");
00124 #endif
00125 
00126     if (!d_result) throw InternalErr(__FILE__, __LINE__, "Must parse() the function expression before calling eval()");
00127 
00128     D4Group *root = function_result->root();    // Load everything in the root group
00129 
00130     for (D4RValueList::iter i = d_result->begin(), e = d_result->end(); i != e; ++i) {
00131         // Copy the BaseTypes; this means all of the function results can
00132         // be deleted, which addresses the memory leak issue with function
00133         // results. This should also copy the D4Dimensions. jhrg 3/17/14
00134         root->add_var((*i)->value(*d_dmr));
00135     }
00136 
00137     delete d_result;    // The parser/function allocates the BaseType*s that hold the results.
00138     d_result = 0;
00139 
00140     // Variables can use Dimensions and Enumerations, so those need to be copied
00141     // from the source dataset to the result. NB: The variables that refer to these
00142     // use weak pointers.
00143 
00144     // Make a set of D4Dimensions. For each variable in 'function_result', look
00145     // for its dimensions in 'dataset' (by name) and add a pointer to those to the
00146     // set. Then copy all the stuff in the set into the root group of 'function_
00147     // result.'
00148     set<D4Dimension*> dim_set;
00149 
00150     for (Constructor::Vars_iter i = root->var_begin(), ie = root->var_end(); i != ie; ++i) {
00151         if ((*i)->is_vector_type()) {
00152             Array *a = static_cast<Array*>(*i);
00153             for (Array::Dim_iter d = a->dim_begin(), de = a->dim_end(); d != de; ++d) {
00154                 if (a->dimension_D4dim(d)) {
00155                     dim_set.insert(a->dimension_D4dim(d));
00156                 }
00157             }
00158         }
00159     }
00160 
00161     // Copy the D4Dimensions and EnumDefs because this all goes in a new DMR - we don't
00162     // want to share those across DMRs because the DMRs delete those (so sharing htem
00163     // across DMRs would lead to dangling pointers.
00164     for (set<D4Dimension*>::iterator i = dim_set.begin(), e = dim_set.end(); i != e; ++i) {
00165         root->dims()->add_dim(*i);
00166     }
00167 
00168     // Now lets do the enumerations....
00169     set<D4EnumDef*> enum_def_set;
00170     for (Constructor::Vars_iter i = root->var_begin(), ie = root->var_end(); i != ie; ++i) {
00171         if ((*i)->type() == dods_enum_c) {
00172             enum_def_set.insert(static_cast<D4Enum*>(*i)->enumeration());
00173         }
00174     }
00175 
00176     for (set<D4EnumDef*>::iterator i = enum_def_set.begin(), e = enum_def_set.end(); i != e; ++i) {
00177         root->enum_defs()->add_enum(*i);
00178     }
00179 }
00180 
00181 // libdap contains functions (in parser-util.cc) that test if a string
00182 // can be converted to an int32, e.g., but I used a more streamlined
00183 // approach here. 3/13/14 jhrg
00196 D4RValue *
00197 D4FunctionEvaluator::build_rvalue(const std::string &id)
00198 {
00199     BaseType *btp = 0;
00200 
00201     // Look for the id in the dataset first
00202     if (top_basetype()) {
00203         btp = top_basetype()->var(id);
00204     }
00205     else {
00206         btp = dmr()->root()->find_var(id);
00207     }
00208 
00209     if (btp) return new D4RValue(btp);
00210 
00211     // If the id is not a variable, try to turn it into a constant,
00212     // otherwise, its an error.
00213     char *end_ptr = 0;
00214 
00215     errno = 0;
00216     long long ll_val = strtoll(id.c_str(), &end_ptr, 0);
00217     if (*end_ptr == '\0' && errno == 0) return new D4RValue(ll_val);
00218 
00219     // Test for unsigned after signed since strtoull() accepts a minus sign
00220     // (and will return a huge number if that's the case). jhrg 3/13/14
00221     errno = 0;
00222     unsigned long long ull_val = strtoull(id.c_str(), &end_ptr, 0);
00223     if (*end_ptr == '\0' && errno == 0) return new D4RValue(ull_val);
00224 
00225     errno = 0;
00226     double d_val = strtod(id.c_str(), &end_ptr);
00227     if (*end_ptr == '\0' && errno == 0) return new D4RValue(d_val);
00228 
00229     // To be a valid string, the id must be quoted (using double quotes)
00230     if (is_quoted(id)) return new D4RValue(www2id(id));
00231 
00232     // if it's none of these, return null
00233     return 0;
00234 }
00235 
00236 template<typename T>
00237 std::vector<T> *
00238 D4FunctionEvaluator::init_arg_list(T val)
00239 {
00240     std::vector<T> *arg_list = new std::vector<T>();
00241     if (get_arg_length_hint() > 0) arg_list->reserve(get_arg_length_hint());
00242 
00243     arg_list->push_back(val);
00244 
00245     return arg_list;
00246 }
00247 
00248 // Force an instantiation so this can be called from within the d4_function.yy
00249 // parser.
00250 template std::vector<dods_byte> *D4FunctionEvaluator::init_arg_list(dods_byte val);
00251 template std::vector<dods_int8> *D4FunctionEvaluator::init_arg_list(dods_int8 val);
00252 template std::vector<dods_uint16> *D4FunctionEvaluator::init_arg_list(dods_uint16 val);
00253 template std::vector<dods_int16> *D4FunctionEvaluator::init_arg_list(dods_int16 val);
00254 template std::vector<dods_uint32> *D4FunctionEvaluator::init_arg_list(dods_uint32 val);
00255 template std::vector<dods_int32> *D4FunctionEvaluator::init_arg_list(dods_int32 val);
00256 template std::vector<dods_uint64> *D4FunctionEvaluator::init_arg_list(dods_uint64 val);
00257 template std::vector<dods_int64> *D4FunctionEvaluator::init_arg_list(dods_int64 val);
00258 template std::vector<dods_float32> *D4FunctionEvaluator::init_arg_list(dods_float32 val);
00259 template std::vector<dods_float64> *D4FunctionEvaluator::init_arg_list(dods_float64 val);
00260 
00261 // This method is called from the parser (see d4_function_parser.yy, down in the code
00262 // section). This will be called during the call to D4FunctionParser::parse(), that
00263 // is inside D4FunctionEvaluator::parse(...)
00264 void D4FunctionEvaluator::error(const libdap::location &l, const std::string &m)
00265 {
00266     ostringstream oss;
00267     oss << l << ": " << m << ends;
00268     throw Error(malformed_expr, oss.str());
00269 }
00270 
00271 } /* namespace libdap */