libdap  Updated for version 3.20.6
libdap4 is an implementation of OPeNDAP's DAP protocol.
D4FunctionEvaluator.cc
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 <cstdlib>
26 #include <cerrno>
27 
28 #include <string>
29 #include <sstream>
30 #include <iterator>
31 #include <list>
32 #include <algorithm>
33 
34 //#define DODS_DEBUG
35 
36 #include "D4FunctionScanner.h"
37 #include "D4FunctionEvaluator.h"
38 #include "d4_function_parser.tab.hh"
39 
40 #include "DMR.h"
41 #include "D4Group.h"
42 #include "D4RValue.h"
43 
44 #include "BaseType.h"
45 #include "Array.h"
46 #include "D4Enum.h"
47 
48 #include "escaping.h"
49 #include "util.h"
50 #include "debug.h"
51 
52 namespace libdap {
53 
66 bool D4FunctionEvaluator::parse(const std::string &expr)
67 {
68  d_expr = expr; // set for error messages. See the %initial-action section of .yy
69 
70  std::istringstream iss(expr);
71  D4FunctionScanner scanner(iss);
72  D4FunctionParser parser(scanner, *this /* driver */);
73 
74  if (trace_parsing()) {
75  parser.set_debug_level(1);
76  parser.set_debug_stream(std::cerr);
77  }
78 
79  return parser.parse() == 0;
80 }
81 
109 void D4FunctionEvaluator::eval(DMR *function_result)
110 {
111 #if 0
112  ServerFunctionsList *sf_list = ServerFunctionsList::TheList();
113  ServerFunction *scale = new D4TestFunction;
114  sf_list->add_function(scale);
115 
116  D4FunctionEvaluator parser(dataset, sf_list);
117  if (ce_parser_debug) parser.set_trace_parsing(true);
118  bool parse_ok = parser.parse(function);
119  if (!parse_ok)
120  Error(malformed_expr, "Function Expression failed to parse.");
121  else {
122  if (ce_parser_debug) cerr << "Function Parse OK" << endl;
123  D4RValueList *result = parser.result();
124 
125  function_result = new DMR(&d4_factory, "function_results");
126 #endif
127 
128  if (!d_result) throw InternalErr(__FILE__, __LINE__, "Must parse() the function expression before calling eval()");
129 
130  D4Group *root = function_result->root(); // Load everything in the root group
131 
132  for (D4RValueList::iter i = d_result->begin(), e = d_result->end(); i != e; ++i) {
133  // Copy the BaseTypes; this means all of the function results can
134  // be deleted, which addresses the memory leak issue with function
135  // results. This should also copy the D4Dimensions. jhrg 3/17/14
136  root->add_var((*i)->value(*d_dmr));
137  }
138 
139  delete d_result; // The parser/function allocates the BaseType*s that hold the results.
140  d_result = 0;
141 
142  // Variables can use Dimensions and Enumerations, so those need to be copied
143  // from the source dataset to the result. NB: The variables that refer to these
144  // use weak pointers.
145 
146  // Make a set of D4Dimensions. For each variable in 'function_result', look
147  // for its dimensions in 'dataset' (by name) and add a pointer to those to the
148  // set. Then copy all the stuff in the set into the root group of 'function_
149  // result.'
150  list<D4Dimension*> dim_set;
151 
152  for (Constructor::Vars_iter i = root->var_begin(), ie = root->var_end(); i != ie; ++i) {
153  if ((*i)->is_vector_type()) {
154  Array *a = static_cast<Array*>(*i);
155  for (Array::Dim_iter d = a->dim_begin(), de = a->dim_end(); d != de; ++d) {
156  // Only add Dimensions that are not already present; share dims are not repeated. jhrg 2/7/18
157  D4Dimension *d4_dim = a->dimension_D4dim(d);
158  if (d4_dim) {
159  bool found = (std::find(dim_set.begin(), dim_set.end(), d4_dim) != dim_set.end());
160  if (!found)
161  dim_set.push_back(a->dimension_D4dim(d));
162  }
163  }
164  }
165  }
166 
167  // Copy the D4Dimensions and EnumDefs because this all goes in a new DMR - we don't
168  // want to share those across DMRs because the DMRs delete those (so sharing htem
169  // across DMRs would lead to dangling pointers.
170  for (list<D4Dimension*>::iterator i = dim_set.begin(), e = dim_set.end(); i != e; ++i) {
171  root->dims()->add_dim(*i);
172  }
173 
174  // Now lets do the enumerations....
175  list<D4EnumDef*> enum_def_set;
176  for (Constructor::Vars_iter i = root->var_begin(), ie = root->var_end(); i != ie; ++i) {
177  if ((*i)->type() == dods_enum_c) {
178  enum_def_set.push_back(static_cast<D4Enum*>(*i)->enumeration());
179  }
180  }
181 
182  for (list<D4EnumDef*>::iterator i = enum_def_set.begin(), e = enum_def_set.end(); i != e; ++i) {
183  root->enum_defs()->add_enum(*i);
184  }
185 }
186 
187 // libdap contains functions (in parser-util.cc) that test if a string
188 // can be converted to an int32, e.g., but I used a more streamlined
189 // approach here. 3/13/14 jhrg
202 D4RValue *
203 D4FunctionEvaluator::build_rvalue(const std::string &id)
204 {
205  BaseType *btp = 0;
206 
207  // Look for the id in the dataset first
208  if (top_basetype()) {
209  btp = top_basetype()->var(id);
210  }
211  else {
212  btp = dmr()->root()->find_var(id);
213  }
214 
215  if (btp) return new D4RValue(btp);
216 
217  // If the id is not a variable, try to turn it into a constant,
218  // otherwise, its an error.
219  char *end_ptr = 0;
220 
221  errno = 0;
222  long long ll_val = strtoll(id.c_str(), &end_ptr, 0);
223  if (*end_ptr == '\0' && errno == 0) return new D4RValue(ll_val);
224 
225  // Test for unsigned after signed since strtoull() accepts a minus sign
226  // (and will return a huge number if that's the case). jhrg 3/13/14
227  errno = 0;
228  unsigned long long ull_val = strtoull(id.c_str(), &end_ptr, 0);
229  if (*end_ptr == '\0' && errno == 0) return new D4RValue(ull_val);
230 
231  errno = 0;
232  double d_val = strtod(id.c_str(), &end_ptr);
233  if (*end_ptr == '\0' && errno == 0) return new D4RValue(d_val);
234 
235  // To be a valid string, the id must be quoted (using double quotes)
236  if (is_quoted(id)) return new D4RValue(www2id(id));
237 
238  // if it's none of these, return null
239  return 0;
240 }
241 
242 template<typename T>
243 std::vector<T> *
244 D4FunctionEvaluator::init_arg_list(T val)
245 {
246  std::vector<T> *arg_list = new std::vector<T>();
247  if (get_arg_length_hint() > 0) arg_list->reserve(get_arg_length_hint());
248 
249  arg_list->push_back(val);
250 
251  return arg_list;
252 }
253 
254 // Force an instantiation so this can be called from within the d4_function.yy
255 // parser.
256 template std::vector<dods_byte> *D4FunctionEvaluator::init_arg_list(dods_byte val);
257 template std::vector<dods_int8> *D4FunctionEvaluator::init_arg_list(dods_int8 val);
258 template std::vector<dods_uint16> *D4FunctionEvaluator::init_arg_list(dods_uint16 val);
259 template std::vector<dods_int16> *D4FunctionEvaluator::init_arg_list(dods_int16 val);
260 template std::vector<dods_uint32> *D4FunctionEvaluator::init_arg_list(dods_uint32 val);
261 template std::vector<dods_int32> *D4FunctionEvaluator::init_arg_list(dods_int32 val);
262 template std::vector<dods_uint64> *D4FunctionEvaluator::init_arg_list(dods_uint64 val);
263 template std::vector<dods_int64> *D4FunctionEvaluator::init_arg_list(dods_int64 val);
264 template std::vector<dods_float32> *D4FunctionEvaluator::init_arg_list(dods_float32 val);
265 template std::vector<dods_float64> *D4FunctionEvaluator::init_arg_list(dods_float64 val);
266 
267 // This method is called from the parser (see d4_function_parser.yy, down in the code
268 // section). This will be called during the call to D4FunctionParser::parse(), that
269 // is inside D4FunctionEvaluator::parse(...)
270 void D4FunctionEvaluator::error(const libdap::location &l, const std::string &m)
271 {
272  ostringstream oss;
273  oss << l << ": " << m << ends;
274  throw Error(malformed_expr, oss.str());
275 }
276 
277 } /* namespace libdap */
D4Group * root()
Definition: DMR.cc:407
virtual void add_function(ServerFunction *func)
Adds the passed ServerFunction pointer to the list of ServerFunctions.
virtual void add_var(BaseType *bt, Part part=nil)
Definition: Constructor.cc:407
top level DAP object to house generic methods
Definition: AISConnect.cc:30
A class for software fault reporting.
Definition: InternalErr.h:64
Dim_iter dim_end()
Definition: Array.cc:696
virtual BaseType * var(const string &name="", bool exact_match=true, btp_stack *s=0)
Returns a pointer to a member of a constructor class.
Definition: BaseType.cc:758
std::vector< dimension >::iterator Dim_iter
Definition: Array.h:206
void add_enum(D4EnumDef *enum_def)
Definition: D4EnumDefs.h:155
bool is_quoted(const string &s)
Definition: util.cc:574
D4RValueList * result() const
string www2id(const string &in, const string &escape, const string &except)
Definition: escaping.cc:220
bool parse(const std::string &expr)
The basic data type for the DODS DAP types.
Definition: BaseType.h:117
Dim_iter dim_begin()
Definition: Array.cc:690
Vars_iter var_begin()
Definition: Constructor.cc:356
Vars_iter var_end()
Definition: Constructor.cc:364
A class for error processing.
Definition: Error.h:92
D4EnumDefs * enum_defs()
Get the enumerations defined for this Group.
Definition: D4Group.h:97
A multidimensional array of identical data types.
Definition: Array.h:112
void add_dim(D4Dimension *dim)
Definition: D4Dimensions.h:155
BaseType * find_var(const string &name)
Definition: D4Group.cc:376
D4Dimensions * dims()
Get the dimensions defined for this Group.
Definition: D4Group.h:82