// -*- mode: c++; c-basic-offset:4 -*- // This file is part of libdap, A C++ implementation of the OPeNDAP Data // Access Protocol. // Copyright (c) 2014 OPeNDAP, Inc. // Author: James Gallagher // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. %skeleton "lalr1.cc" /* -*- C++ -*- */ %require "2.5" %defines // The d4_function_parser.tab.cc and .hh files define and declare this class %define parser_class_name {D4FunctionParser} // D4FunctionParser is in this namespace %define api.namespace {libdap} %define parse.trace %define parse.error verbose %define parse.assert // Could not get this to work with a C++ scanner built by flex. 8/10/13 jhrg // %define api.token.constructor %define api.value.type variant // Because the code uses the C++ mode of flex, we don't use this. 8/8/13 jhrg // %define api.prefix { d4_function_ } %code requires { #include "D4FunctionEvaluator.h" #include "D4RValue.h" #include "dods-datatypes.h" namespace libdap { class D4FunctionScanner; } } // Pass both the scanner and parser objects to both the automatically generated // parser and scanner. %lex-param { D4FunctionScanner &scanner } %parse-param { D4FunctionScanner &scanner } %lex-param { D4FunctionEvaluator &evaluator } %parse-param { D4FunctionEvaluator &evaluator } %locations %initial-action { // Initialize the initial location. This is printed when the parser builds // its own error messages - when the parse fails as opposed to when the // function(s) name(s) a missing variable, ... @$.initialize (evaluator.expression()); }; %code { #include "BaseType.h" #include "DMR.h" #include "D4RValue.h" #include "ServerFunctionsList.h" #include "parser-util.h" /* include for all driver functions */ #include "D4FunctionEvaluator.h" using namespace libdap ; /* this is silly, but I can't figure out a way around it */ static int yylex(libdap::D4FunctionParser::semantic_type *yylval, libdap::location *loc, libdap::D4FunctionScanner &scanner, libdap::D4FunctionEvaluator &evaluator); } %type functions "functions" %type args "arguments" %type arg "argument" %type function "function" %type fname "function name" %type variable_or_constant "variable or constant" %type array_constant "array constant" %type *> fast_byte_arg_list "fast byte arg list" %type *> fast_int8_arg_list "fast int8 arg list" %type *> fast_uint16_arg_list "fast uint16 arg list" %type *> fast_int16_arg_list "fast int16 arg list" %type *> fast_uint32_arg_list "fast uint32 arg list" %type *> fast_int32_arg_list "fast int32 arg list" %type *> fast_uint64_arg_list "fast uint64 arg list" %type *> fast_int64_arg_list "fast int64 arg list" %type *> fast_float32_arg_list "fast float32 arg list" %type *> fast_float64_arg_list "fast float64 arg list" %type id path group name // The strings used in the token definitions are used for error messages %token WORD "word" %token STRING "string" %token END 0 "end of file" SEMICOLON ";" COLON ":" LPAREN "(" RPAREN ")" COMMA "," GROUP_SEP "/" PATH_SEP "." DOLLAR_BYTE "$Byte" DOLLAR_UINT8 "$UInt8" DOLLAR_INT8 "$Int8" DOLLAR_UINT16 "$UInt16" DOLLAR_INT16 "$Int16" DOLLAR_UINT32 "$UInt32" DOLLAR_INT32 "$Int32" DOLLAR_UINT64 "$UInt64" DOLLAR_INT64 "$Int64" DOLLAR_FLOAT32 "$Float32" DOLLAR_FLOAT64 "$Float64" ; %% %start program; program : functions { evaluator.set_result($1); } ; functions : function { $$ = new D4RValueList($1); } | functions ";" function { $1->add_rvalue($3); $$ = $1; } ; function : fname "(" args ")" { $$ = new D4RValue($1, $3); // Build a D4RValue from a D4Function pointer and a D4RValueList } ; fname: WORD { D4Function f; if (!evaluator.sf_list()->find_function($1, &f)) { // ...cloud use @1.{first,last}_column in these error messages. throw Error(malformed_expr, "'" + $1 + "' is not a registered DAP4 server function."); } $$ = f; } ; args: arg { $$ = new D4RValueList($1); // build a D4RValueList from the D4RValue } | args "," arg { $1->add_rvalue($3); $$ = $1; // Append the D4RValue ($3) to the D4RValueList ($1), then return } ; arg: function { $$ = $1; } | variable_or_constant { $$ = $1; } | array_constant { $$ = $1; } ; variable_or_constant : id { D4RValue *rvalue = evaluator.build_rvalue($1); if (!rvalue) { throw Error(malformed_expr, "'" + $1 + "' is not a variable, number or string."); } $$ = rvalue; } ; array_constant : DOLLAR_BYTE "(" arg_length_hint ":" fast_byte_arg_list ")" { $$ = new D4RValue(*($5)); delete $5; } | DOLLAR_UINT8 "(" arg_length_hint ":" fast_byte_arg_list ")" { $$ = new D4RValue(*($5)); delete $5; } | DOLLAR_INT8 "(" arg_length_hint ":" fast_int8_arg_list ")" { $$ = new D4RValue(*($5)); delete $5; } | DOLLAR_UINT16 "(" arg_length_hint ":" fast_uint16_arg_list ")" { $$ = new D4RValue(*($5)); delete $5; } | DOLLAR_INT16 "(" arg_length_hint ":" fast_int16_arg_list ")" { $$ = new D4RValue(*($5)); delete $5; } | DOLLAR_UINT32 "(" arg_length_hint ":" fast_uint32_arg_list ")" { $$ = new D4RValue(*($5)); delete $5; } | DOLLAR_INT32 "(" arg_length_hint ":" fast_int32_arg_list ")" { $$ = new D4RValue(*($5)); delete $5; } | DOLLAR_UINT64 "(" arg_length_hint ":" fast_uint64_arg_list ")" { $$ = new D4RValue(*($5)); delete $5; } | DOLLAR_INT64 "(" arg_length_hint ":" fast_int64_arg_list ")" { $$ = new D4RValue(*($5)); delete $5; } | DOLLAR_FLOAT32 "(" arg_length_hint ":" fast_float32_arg_list ")" { $$ = new D4RValue(*($5)); delete $5; } | DOLLAR_FLOAT64 "(" arg_length_hint ":" fast_float64_arg_list ")" { $$ = new D4RValue(*($5)); delete $5; } ; /* Here the arg length hint is stored in the eval class so it can be used by the method that allocates the vector. The value is passed to vector::reserve(). This rule is run for it's side-effect only. This is also used to track the current arg number so that the hint can be used. */ arg_length_hint : WORD { evaluator.set_arg_length_hint(get_uint64($1.c_str())); } ; fast_byte_arg_list: WORD { $$ = evaluator.init_arg_list(dods_byte(strtol($1.c_str(), 0, 0))); } | fast_byte_arg_list "," WORD { $1->push_back(strtol($3.c_str(), 0, 0)); $$ = $1; } ; fast_int8_arg_list: WORD { $$ = evaluator.init_arg_list(dods_int8(strtol($1.c_str(), 0, 0))); } | fast_int8_arg_list "," WORD { $1->push_back(strtol($3.c_str(), 0, 0)); $$ = $1; } ; fast_uint16_arg_list: WORD { $$ = evaluator.init_arg_list(dods_uint16(strtol($1.c_str(), 0, 0))); } | fast_uint16_arg_list "," WORD { $1->push_back(strtol($3.c_str(), 0, 0)); $$ = $1; } ; fast_int16_arg_list: WORD { $$ = evaluator.init_arg_list(dods_int16(strtol($1.c_str(), 0, 0))); } | fast_int16_arg_list "," WORD { $1->push_back(strtol($3.c_str(), 0, 0)); $$ = $1; } ; fast_uint32_arg_list: WORD { $$ = evaluator.init_arg_list(dods_uint32(strtoul($1.c_str(), 0, 0))); } | fast_uint32_arg_list "," WORD { $1->push_back(strtoul($3.c_str(), 0, 0)); $$ = $1; } ; fast_int32_arg_list: WORD { $$ = evaluator.init_arg_list(dods_int32(strtol($1.c_str(), 0, 0))); } | fast_int32_arg_list "," WORD { $1->push_back(strtol($3.c_str(), 0, 0)); $$ = $1; } ; fast_uint64_arg_list: WORD { $$ = evaluator.init_arg_list(dods_uint64(strtoull($1.c_str(), 0, 0))); } | fast_uint64_arg_list "," WORD { $1->push_back(strtoull($3.c_str(), 0, 0)); $$ = $1; } ; fast_int64_arg_list: WORD { $$ = evaluator.init_arg_list(dods_int64(strtoll($1.c_str(), 0, 0))); } | fast_int64_arg_list "," WORD { $1->push_back(strtoll($3.c_str(), 0, 0)); $$ = $1; } ; // I'm using path for the $Float constants so that tokens with dots will // parse. I could add a FLOAT token, and I might have to do that later on // when filters are added to the CE, but this will work for now. fast_float32_arg_list: path { $$ = evaluator.init_arg_list(dods_float32(strtof($1.c_str(), 0))); } | fast_float32_arg_list "," path { $1->push_back(strtof($3.c_str(), 0)); $$ = $1; } ; fast_float64_arg_list: path { $$ = evaluator.init_arg_list(dods_float64(strtod($1.c_str(), 0))); } | fast_float64_arg_list "," path { $1->push_back(strtod($3.c_str(), 0)); $$ = $1; } ; id : path { $$ = $1; } | "/" path { $$.append("/"); $$.append($2); } | group "/" path { $1.append("/"); $1.append($3); $$ = $1; } ; group : "/" name { $$.append("/"); $$.append($2); } | group "/" name { $1.append("."); $1.append($3); $$ = $1; } ; path : name { $$ = $1; } | path "." name { $1.append("."); $1.append($3); $$ = $1; } ; // Because some formats/datasets allow 'any' name for a variable, it's possible // that a variable name will be a number, etc. The grammar also allows STRING // to support "name"."name with spaces and dots (.)".x name : WORD { $$=$1; } | STRING { $$=$1; } ; %% // Forward the error to the driver for handling. The location parameter // provides the line number and character position of the error. void libdap::D4FunctionParser::error(const location_type &l, const std::string &m) { evaluator.error(l, m); } /* include for access to scanner.yylex */ #include "D4FunctionScanner.h" static int yylex(libdap::D4FunctionParser::semantic_type *yylval, libdap::location *loc, libdap::D4FunctionScanner &scanner, libdap::D4FunctionEvaluator &evaluator) { if (evaluator.trace_scanning()) scanner.set_debug(true); return( scanner.yylex(yylval, loc) ); }