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