libdap Updated for version 3.21.1
libdap4 is an implementation of OPeNDAP's DAP protocol.
D4FunctionEvaluator.cc
Go to the documentation of this file.
1// -*- mode: c++; c-basic-offset:4 -*-
2
3// This file is part of libdap, A C++ implementation of the OPeNDAP Data
4// Access Protocol.
5
6// Copyright (c) 2014 OPeNDAP, Inc.
7// Author: James Gallagher <jgallagher@opendap.org>
8//
9// This library is free software; you can redistribute it and/or
10// modify it under the terms of the GNU Lesser General Public
11// License as published by the Free Software Foundation; either
12// version 2.1 of the License, or (at your option) any later version.
13//
14// This library is distributed in the hope that it will be useful,
15// but WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17// Lesser General Public License for more details.
18//
19// You should have received a copy of the GNU Lesser General Public
20// License along with this library; if not, write to the Free Software
21// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22//
23// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
24
25#include <cerrno>
26#include <cstdlib>
27
28#include <algorithm>
29#include <iterator>
30#include <list>
31#include <memory>
32#include <sstream>
33#include <string>
34
35#include "D4FunctionEvaluator.h"
36#include "D4FunctionScanner.h"
37#include "d4_function_parser.tab.hh"
38
39#include "Array.h"
40#include "D4Enum.h"
41#include "D4Group.h"
42#include "DMR.h"
43
44#include "debug.h"
45#include "escaping.h"
46#include "util.h"
47
48namespace libdap {
49
62bool D4FunctionEvaluator::parse(const std::string &expr) {
63 d_expr = expr; // set for error messages. See the %initial-action section of .yy
64
65 std::istringstream iss(expr);
66 D4FunctionScanner scanner(iss);
67 D4FunctionParser parser(scanner, *this /* driver */);
68
69 if (trace_parsing()) {
70 parser.set_debug_level(1);
71 parser.set_debug_stream(std::cerr);
72 }
73
74 return parser.parse() == 0;
75}
76
104void D4FunctionEvaluator::eval(DMR *function_result) {
105 if (!d_result)
106 throw InternalErr(__FILE__, __LINE__, "Must parse() the function expression before calling eval()");
107
108 D4Group *root = function_result->root(); // Load everything in the root group
109 for (auto result : *d_result) {
110 // Copy the BaseTypes; this means all the function results can
111 // be deleted, which addresses the memory leak issue with function
112 // results. This should also copy the D4Dimensions. jhrg 3/17/14
113 root->add_var(result->value(*d_dmr));
114 }
115
116 delete d_result; // The parser/function allocates the BaseType*s that hold the results.
117 d_result = nullptr;
118
119 // Variables can use Dimensions and Enumerations, so those need to be copied
120 // from the source dataset to the result. NB: The variables that refer to these
121 // use weak pointers.
122
123 // Make a set of D4Dimensions. For each variable in 'function_result', look
124 // for its dimensions in 'dataset' (by name) and add a pointer to those to the
125 // set. Then copy all the stuff in the set into the root group of 'function_
126 // result.'
127 list<D4Dimension *> dim_set;
128
129 for (auto i = root->var_begin(), ie = root->var_end(); i != ie; ++i) {
130 if ((*i)->is_vector_type()) {
131 auto a = static_cast<Array *>(*i);
132 for (auto d = a->dim_begin(), de = a->dim_end(); d != de; ++d) {
133 // Only add Dimensions that are not already present; share dims are not repeated. jhrg 2/7/18
134 D4Dimension *d4_dim = a->dimension_D4dim(d);
135 if (d4_dim) {
136 bool found = (std::find(dim_set.begin(), dim_set.end(), d4_dim) != dim_set.end());
137 if (!found)
138 dim_set.push_back(a->dimension_D4dim(d));
139 }
140 }
141 }
142 }
143
144 // Copy the D4Dimensions and EnumDefs because this all goes in a new DMR - we don't
145 // want to share those across DMRs because the DMRs delete those (so sharing htem
146 // across DMRs would lead to dangling pointers.
147 for (auto dim : dim_set) {
148 root->dims()->add_dim(dim);
149 }
150
151 // Now let's do the enumerations....
152 list<D4EnumDef *> enum_def_set;
153 for (auto i = root->var_begin(), ie = root->var_end(); i != ie; ++i) {
154 if ((*i)->type() == dods_enum_c) {
155 enum_def_set.push_back(static_cast<D4Enum *>(*i)->enumeration());
156 }
157 }
158
159 for (auto enum_def : enum_def_set) {
160 root->enum_defs()->add_enum(enum_def);
161 }
162}
163
164// libdap contains functions (in parser-util.cc) that test if a string
165// can be converted to an int32, e.g., but I used a more streamlined
166// approach here. 3/13/14 jhrg
182D4RValue *D4FunctionEvaluator::build_rvalue(const std::string &id) {
183 BaseType *btp = nullptr;
184
185 // Look for the id in the dataset first
186 if (top_basetype()) {
187 btp = top_basetype()->var(id);
188 } else {
189 btp = dmr()->root()->find_var(id);
190 }
191
192 if (btp)
193 return new D4RValue(btp);
194
195 // If the id is not a variable, try to turn it into a constant,
196 // otherwise, it's an error.
197 char *end_ptr = nullptr;
198
199 errno = 0;
200 long long ll_val = strtoll(id.c_str(), &end_ptr, 0);
201 if (*end_ptr == '\0' && errno == 0)
202 return new D4RValue(ll_val);
203
204 // Test for unsigned after signed since strtoull() accepts a minus sign
205 // (and will return a huge number if that's the case). jhrg 3/13/14
206 errno = 0;
207 unsigned long long ull_val = strtoull(id.c_str(), &end_ptr, 0);
208 if (*end_ptr == '\0' && errno == 0)
209 return new D4RValue(ull_val);
210
211 errno = 0;
212 double d_val = strtod(id.c_str(), &end_ptr);
213 if (*end_ptr == '\0' && errno == 0)
214 return new D4RValue(d_val);
215
216 // To be a valid string, the id must be quoted (using double quotes)
217 if (is_quoted(id))
218 return new D4RValue(www2id(id));
219
220 // if it's none of these, return null
221 return nullptr;
222}
223
224// TODO Make the arg_list a unique_ptr through out. jhrg 2/22/24
225template <typename T> vector<T> *D4FunctionEvaluator::init_arg_list(T val) {
226 auto arg_list = make_unique<vector<T>>();
227 if (get_arg_length_hint() > 0)
228 arg_list->reserve(get_arg_length_hint());
229
230 arg_list->push_back(val);
231
232 return arg_list.release();
233}
234
235// Force an instantiation so this can be called from within the d4_function.yy
236// parser.
237template std::vector<dods_byte> *D4FunctionEvaluator::init_arg_list(dods_byte val);
238template std::vector<dods_int8> *D4FunctionEvaluator::init_arg_list(dods_int8 val);
239template std::vector<dods_uint16> *D4FunctionEvaluator::init_arg_list(dods_uint16 val);
240template std::vector<dods_int16> *D4FunctionEvaluator::init_arg_list(dods_int16 val);
241template std::vector<dods_uint32> *D4FunctionEvaluator::init_arg_list(dods_uint32 val);
242template std::vector<dods_int32> *D4FunctionEvaluator::init_arg_list(dods_int32 val);
243template std::vector<dods_uint64> *D4FunctionEvaluator::init_arg_list(dods_uint64 val);
244template std::vector<dods_int64> *D4FunctionEvaluator::init_arg_list(dods_int64 val);
245template std::vector<dods_float32> *D4FunctionEvaluator::init_arg_list(dods_float32 val);
246template std::vector<dods_float64> *D4FunctionEvaluator::init_arg_list(dods_float64 val);
247
248// This method is called from the parser (see d4_function_parser.yy, down in the code
249// section). This will be called during the call to D4FunctionParser::parse(), that
250// is inside D4FunctionEvaluator::parse(...)
251[[noreturn]] void D4FunctionEvaluator::error(const libdap::location &l, const std::string &m) {
252 ostringstream oss;
253 oss << l << ": " << m << ends;
254 throw Error(malformed_expr, oss.str());
255}
256
257} /* namespace libdap */
#define malformed_expr
(400)
Definition Error.h:66
A multidimensional array of identical data types.
Definition Array.h:121
The basic data type for the DODS DAP types.
Definition BaseType.h:118
virtual BaseType * var(const string &name="", bool exact_match=true, btp_stack *s=nullptr)
Returns a pointer to a member of a constructor class.
Definition BaseType.cc:646
void add_var(BaseType *bt, Part part=nil) override
Vars_iter var_begin()
void add_dim(D4Dimension *dim)
void add_enum(D4EnumDef *enum_def)
Definition D4EnumDefs.h:150
Holds a DAP4 enumeration.
Definition D4Enum.h:53
virtual D4EnumDef * enumeration() const
Definition D4Enum.h:112
std::vector< t > * init_arg_list(t val)
static void error(const libdap::location &l, const std::string &m)
unsigned long long get_arg_length_hint() const
bool parse(const std::string &expr)
D4RValueList * result() const
BaseType * find_var(const string &name)
Definition D4Group.cc:354
D4Dimensions * dims()
Get the dimensions defined for this Group.
Definition D4Group.h:84
D4EnumDefs * enum_defs()
Get the enumerations defined for this Group.
Definition D4Group.h:100
D4Group * root()
Definition DMR.cc:228
A class for error processing.
Definition Error.h:92
A class for software fault reporting.
Definition InternalErr.h:61
top level DAP object to house generic methods
Definition AISConnect.cc:30
@ dods_enum_c
Definition Type.h:120
string www2id(const string &in, const string &escape, const string &except)
Definition escaping.cc:202
uint64_t dods_uint64
uint32_t dods_uint32
uint16_t dods_uint16
bool is_quoted(const string &s)
Definition util.cc:554