From dcb58eb624f87fad1362bfc593b274f84fa44889 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sun, 16 Dec 2018 16:05:38 +0100 Subject: Add WKT1 grammar validation; change prototype of proj_obj_create_from_wkt() --- include/proj/io.hpp | 2 +- scripts/cppcheck.sh | 2 +- src/Makefile.am | 17 +- src/c_api.cpp | 123 ++- src/io.cpp | 35 +- src/lib_proj.cmake | 2 + src/pj_wkt1_generated_parser.c | 1664 ++++++++++++++++++++++++++++++++++++++++ src/pj_wkt1_generated_parser.h | 89 +++ src/pj_wkt1_grammar.y | 301 ++++++++ src/pj_wkt1_parser.cpp | 221 ++++++ src/pj_wkt1_parser.h | 54 ++ src/proj.h | 14 +- src/projinfo.cpp | 34 +- test/cli/testprojinfo | 4 + test/cli/testprojinfo_out.dist | 25 + test/unit/test_c_api.cpp | 201 +++-- test/unit/test_io.cpp | 42 +- test/unit/test_operation.cpp | 28 +- travis/csa/install.sh | 2 +- 19 files changed, 2734 insertions(+), 126 deletions(-) create mode 100644 src/pj_wkt1_generated_parser.c create mode 100644 src/pj_wkt1_generated_parser.h create mode 100644 src/pj_wkt1_grammar.y create mode 100644 src/pj_wkt1_parser.cpp create mode 100644 src/pj_wkt1_parser.h diff --git a/include/proj/io.hpp b/include/proj/io.hpp index c0d4fef5..3d09e7d3 100644 --- a/include/proj/io.hpp +++ b/include/proj/io.hpp @@ -620,7 +620,7 @@ class PROJ_GCC_DLL WKTParser { attachDatabaseContext(const DatabaseContextPtr &dbContext); PROJ_DLL WKTParser &setStrict(bool strict); - PROJ_DLL std::vector warningList() const; + PROJ_DLL std::list warningList() const; PROJ_DLL util::BaseObjectNNPtr createFromWKT(const std::string &wkt); // throw(ParsingException) diff --git a/scripts/cppcheck.sh b/scripts/cppcheck.sh index bd677e3d..76a540aa 100755 --- a/scripts/cppcheck.sh +++ b/scripts/cppcheck.sh @@ -34,7 +34,7 @@ done ret_code=0 -grep -v "unmatchedSuppression" ${LOG_FILE} | grep -v "nn.hpp" > ${LOG_FILE}.tmp +grep -v "unmatchedSuppression" ${LOG_FILE} | grep -v "nn.hpp" | grep -v "pj_wkt1_generated_parser" > ${LOG_FILE}.tmp mv ${LOG_FILE}.tmp ${LOG_FILE} if grep "null pointer" ${LOG_FILE} ; then diff --git a/src/Makefile.am b/src/Makefile.am index 99158f02..9cf1eb88 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -14,8 +14,9 @@ include_HEADERS = proj.h proj_experimental.h proj_constants.h proj_api.h geodesi org_proj4_PJ.h proj_symbol_rename.h EXTRA_DIST = bin_cct.cmake bin_gie.cmake bin_cs2cs.cmake \ - bin_geod.cmake bin_nad2bin.cmake bin_proj.cmake bin_projinfo.cmake \ - lib_proj.cmake CMakeLists.txt bin_geodtest.cmake geodtest.c + bin_geod.cmake bin_nad2bin.cmake bin_proj.cmake bin_projinfo.cmake \ + lib_proj.cmake CMakeLists.txt bin_geodtest.cmake geodtest.c \ + pj_wkt1_grammar.y proj_SOURCES = proj.c gen_cheb.c p_series.c projinfo_SOURCES = projinfo.cpp @@ -94,7 +95,17 @@ libproj_la_SOURCES = \ \ proj_4D_api.c PJ_cart.c PJ_pipeline.c PJ_horner.c PJ_helmert.c \ PJ_vgridshift.c PJ_hgridshift.c PJ_unitconvert.c PJ_molodensky.c \ - PJ_deformation.c pj_internal.c PJ_axisswap.c PJ_affine.c + PJ_deformation.c pj_internal.c PJ_axisswap.c PJ_affine.c \ + pj_wkt1_parser.h pj_wkt1_parser.cpp \ + pj_wkt1_generated_parser.h pj_wkt1_generated_parser.c + + +# The sed hack is to please MSVC +wkt1_parser: + bison --no-lines -d -p pj_wkt1_ -o$(top_srcdir)/src/pj_wkt1_generated_parser.c $(top_srcdir)/src/pj_wkt1_grammar.y + sed "s/\*yyssp = yystate/\*yyssp = (yytype_int16)yystate/" < $(top_srcdir)/src/pj_wkt1_generated_parser.c | sed "s/yyerrorlab:/#if 0\nyyerrorlab:/" | sed "s/yyerrlab1:/#endif\nyyerrlab1:/" | sed "s/for (yylen = 0; yystr\[yylen\]; yylen++)/for (yylen = 0; yystr \&\& yystr\[yylen\]; yylen++)/" > $(top_srcdir)/src/pj_wkt1_generated_parser.c.tmp + mv $(top_srcdir)/src/pj_wkt1_generated_parser.c.tmp $(top_srcdir)/src/pj_wkt1_generated_parser.c + install-exec-local: rm -f $(DESTDIR)$(bindir)/invproj$(EXEEXT) diff --git a/src/c_api.cpp b/src/c_api.cpp index 8140f3b3..d0b5d720 100644 --- a/src/c_api.cpp +++ b/src/c_api.cpp @@ -392,6 +392,28 @@ PJ_OBJ *proj_obj_create_from_user_input(PJ_CONTEXT *ctx, const char *text, // --------------------------------------------------------------------------- +template static PROJ_STRING_LIST to_string_list(T &&set) { + auto ret = new char *[set.size() + 1]; + size_t i = 0; + for (const auto &str : set) { + try { + ret[i] = new char[str.size() + 1]; + } catch (const std::exception &) { + while (--i > 0) { + delete[] ret[i]; + } + delete[] ret; + throw; + } + std::memcpy(ret[i], str.c_str(), str.size() + 1); + i++; + } + ret[i] = nullptr; + return ret; +} + +// --------------------------------------------------------------------------- + /** \brief Instanciate an object from a WKT string. * * This function calls osgeo::proj::io::WKTParser::createFromWKT() @@ -401,28 +423,97 @@ PJ_OBJ *proj_obj_create_from_user_input(PJ_CONTEXT *ctx, const char *text, * * @param ctx PROJ context, or NULL for default context * @param wkt WKT string (must not be NULL) - * @param options should be set to NULL for now + * @param options null-terminated list of options, or NULL. Currently + * supported options are: + *
    + *
  • STRICT=YES/NO. Defaults to NO. When set to YES, strict validation will + * be enabled.
  • + *
+ * @param out_warnings Pointer to a PROJ_STRING_LIST object, or NULL. + * If provided, *out_warnings will contain a list of warnings, typically for + * non recognized projection method or parameters. It must be freed with + * proj_string_list_destroy(). + * @param out_grammar_errors Pointer to a PROJ_STRING_LIST object, or NULL. + * If provided, *out_grammar_errors will contain a list of errors regarding the + * WKT grammaer. It must be freed with proj_string_list_destroy(). * @return Object that must be unreferenced with proj_obj_destroy(), or NULL in * case of error. */ PJ_OBJ *proj_obj_create_from_wkt(PJ_CONTEXT *ctx, const char *wkt, - const char *const *options) { + const char *const *options, + PROJ_STRING_LIST *out_warnings, + PROJ_STRING_LIST *out_grammar_errors) { SANITIZE_CTX(ctx); assert(wkt); - (void)options; + + if (out_warnings) { + *out_warnings = nullptr; + } + if (out_grammar_errors) { + *out_grammar_errors = nullptr; + } + try { WKTParser parser; auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); if (dbContext) { parser.attachDatabaseContext(NN_NO_CHECK(dbContext)); } - auto identifiedObject = nn_dynamic_pointer_cast( + for (auto iter = options; iter && iter[0]; ++iter) { + const char *value; + if ((value = getOptionValue(*iter, "STRICT="))) { + parser.setStrict(ci_equal(value, "YES")); + } else { + std::string msg("Unknown option :"); + msg += *iter; + proj_log_error(ctx, __FUNCTION__, msg.c_str()); + return nullptr; + } + } + auto obj = nn_dynamic_pointer_cast( parser.createFromWKT(wkt)); - if (identifiedObject) { - return PJ_OBJ::create(NN_NO_CHECK(identifiedObject)); + + if (out_grammar_errors) { + auto warnings = parser.warningList(); + if (!warnings.empty()) { + *out_grammar_errors = to_string_list(warnings); + } + } + + if (obj && out_warnings) { + auto derivedCRS = dynamic_cast(obj.get()); + if (derivedCRS) { + auto warnings = + derivedCRS->derivingConversionRef()->validateParameters(); + if (!warnings.empty()) { + *out_warnings = to_string_list(warnings); + } + } else { + auto singleOp = + dynamic_cast(obj.get()); + if (singleOp) { + auto warnings = singleOp->validateParameters(); + if (!warnings.empty()) { + *out_warnings = to_string_list(warnings); + } + } + } + } + + if (obj) { + return PJ_OBJ::create(NN_NO_CHECK(obj)); } } catch (const std::exception &e) { - proj_log_error(ctx, __FUNCTION__, e.what()); + if (out_grammar_errors) { + std::list exc{e.what()}; + try { + *out_grammar_errors = to_string_list(exc); + } catch (const std::exception &) { + proj_log_error(ctx, __FUNCTION__, e.what()); + } + } else { + proj_log_error(ctx, __FUNCTION__, e.what()); + } } return nullptr; } @@ -1832,20 +1923,6 @@ void proj_int_list_destroy(int *list) { delete[] list; } // --------------------------------------------------------------------------- -static PROJ_STRING_LIST set_to_string_list(std::set &&set) { - auto ret = new char *[set.size() + 1]; - size_t i = 0; - for (const auto &str : set) { - ret[i] = new char[str.size() + 1]; - std::memcpy(ret[i], str.c_str(), str.size() + 1); - i++; - } - ret[i] = nullptr; - return ret; -} - -// --------------------------------------------------------------------------- - /** \brief Return the list of authorities used in the database. * * The returned list is NULL terminated and must be freed with @@ -1859,7 +1936,7 @@ static PROJ_STRING_LIST set_to_string_list(std::set &&set) { PROJ_STRING_LIST proj_get_authorities_from_database(PJ_CONTEXT *ctx) { SANITIZE_CTX(ctx); try { - return set_to_string_list(getDBcontext(ctx)->getAuthorities()); + return to_string_list(getDBcontext(ctx)->getAuthorities()); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } @@ -1894,7 +1971,7 @@ PROJ_STRING_LIST proj_get_codes_from_database(PJ_CONTEXT *ctx, if (!valid) { return nullptr; } - return set_to_string_list( + return to_string_list( factory->getAuthorityCodes(typeInternal, allow_deprecated != 0)); } catch (const std::exception &e) { diff --git a/src/io.cpp b/src/io.cpp index 65921019..79f8ae5e 100644 --- a/src/io.cpp +++ b/src/io.cpp @@ -60,6 +60,8 @@ #include "proj_constants.h" +#include "pj_wkt1_parser.h" + // PROJ include order is sensitive // clang-format off #include "proj.h" @@ -1125,7 +1127,7 @@ std::string WKTNode::toString() const { //! @cond Doxygen_Suppress struct WKTParser::Private { bool strict_ = true; - std::vector warningList_{}; + std::list warningList_{}; std::vector toWGS84Parameters_{}; std::string datumPROJ4Grids_{}; bool esriStyle_ = false; @@ -1321,7 +1323,7 @@ WKTParser &WKTParser::setStrict(bool strict) { * * \note The list might be non-empty only is setStrict(false) has been called. */ -std::vector WKTParser::warningList() const { +std::list WKTParser::warningList() const { return d->warningList_; } @@ -4205,8 +4207,10 @@ BaseObjectNNPtr createFromUserInput(const std::string &text, for (const auto &wktConstants : WKTConstants::constants()) { if (ci_starts_with(text, wktConstants)) { - return WKTParser().attachDatabaseContext(dbContext).createFromWKT( - text); + return WKTParser() + .attachDatabaseContext(dbContext) + .setStrict(false) + .createFromWKT(text); } } const char *textWithoutPlusPrefix = text.c_str(); @@ -4339,11 +4343,32 @@ BaseObjectNNPtr createFromUserInput(const std::string &text, // --------------------------------------------------------------------------- /** \brief Instanciate a sub-class of BaseObject from a WKT string. + * + * By default, validation is strict (to the extent of the checks that are + * actually implemented. Currently only WKT1 strict grammar is checked), and + * any issue detected will cause an exception to be thrown, unless + * setStrict(false) is called priorly. + * + * In non-strict mode, non-fatal issues will be recovered and simply listed + * in warningList(). This does not prevent more severe errors to cause an + * exception to be thrown. + * * @throw ParsingException */ BaseObjectNNPtr WKTParser::createFromWKT(const std::string &wkt) { WKTNodeNNPtr root = WKTNode::createFrom(wkt); - return d->build(root); + auto obj = d->build(root); + + const auto dialect = guessDialect(wkt); + if (dialect == WKTGuessedDialect::WKT1_GDAL || + dialect == WKTGuessedDialect::WKT1_ESRI) { + auto errorMsg = pj_wkt1_parse(wkt); + if (!errorMsg.empty()) { + d->emitRecoverableAssertion(errorMsg); + } + } + + return obj; } // --------------------------------------------------------------------------- diff --git a/src/lib_proj.cmake b/src/lib_proj.cmake index 9e6f51fd..9eb75c0b 100644 --- a/src/lib_proj.cmake +++ b/src/lib_proj.cmake @@ -244,6 +244,7 @@ SET(SRC_LIBPROJ_CORE rtodms.c vector1.c pj_strtod.c + pj_wkt1_generated_parser.c ${CMAKE_CURRENT_BINARY_DIR}/proj_config.h ) @@ -260,6 +261,7 @@ set(SRC_LIBPROJ_CPP internal.cpp factory.cpp c_api.cpp + pj_wkt1_parser.cpp ) set(HEADERS_LIBPROJ diff --git a/src/pj_wkt1_generated_parser.c b/src/pj_wkt1_generated_parser.c new file mode 100644 index 00000000..91d5e60d --- /dev/null +++ b/src/pj_wkt1_generated_parser.c @@ -0,0 +1,1664 @@ +/* A Bison parser, made by GNU Bison 3.0.4. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.0.4" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + +/* Substitute the variable and function names. */ +#define yyparse pj_wkt1_parse +#define yylex pj_wkt1_lex +#define yyerror pj_wkt1_error +#define yydebug pj_wkt1_debug +#define yynerrs pj_wkt1_nerrs + + +/* Copy the first part of user declarations. */ + + +/****************************************************************************** + * Project: PROJ + * Purpose: WKT1 parser grammar + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2013-2018 Even Rouault, + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pj_wkt1_parser.h" + + + + +# ifndef YY_NULLPTR +# if defined __cplusplus && 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 1 +#endif + +/* In a future release of Bison, this section will be replaced + by #include "pj_wkt1_generated_parser.h". */ +#ifndef YY_PJ_WKT1_SRC_PJ_WKT1_GENERATED_PARSER_H_INCLUDED +# define YY_PJ_WKT1_SRC_PJ_WKT1_GENERATED_PARSER_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int pj_wkt1_debug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + END = 0, + T_PARAM_MT = 258, + T_CONCAT_MT = 259, + T_INVERSE_MT = 260, + T_PASSTHROUGH_MT = 261, + T_PROJCS = 262, + T_PROJECTION = 263, + T_GEOGCS = 264, + T_DATUM = 265, + T_SPHEROID = 266, + T_PRIMEM = 267, + T_UNIT = 268, + T_GEOCCS = 269, + T_AUTHORITY = 270, + T_VERT_CS = 271, + T_VERT_DATUM = 272, + T_COMPD_CS = 273, + T_AXIS = 274, + T_TOWGS84 = 275, + T_FITTED_CS = 276, + T_LOCAL_CS = 277, + T_LOCAL_DATUM = 278, + T_PARAMETER = 279, + T_EXTENSION = 280, + T_STRING = 281, + T_NUMBER = 282, + T_IDENTIFIER = 283 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef int YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + + +int pj_wkt1_parse (pj_wkt1_parse_context *context); + +#endif /* !YY_PJ_WKT1_SRC_PJ_WKT1_GENERATED_PARSER_H_INCLUDED */ + +/* Copy the second part of user declarations. */ + + + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + +#ifndef YY_ATTRIBUTE +# if (defined __GNUC__ \ + && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \ + || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C +# define YY_ATTRIBUTE(Spec) __attribute__(Spec) +# else +# define YY_ATTRIBUTE(Spec) /* empty */ +# endif +#endif + +#ifndef YY_ATTRIBUTE_PURE +# define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__)) +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__)) +#endif + +#if !defined _Noreturn \ + && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112) +# if defined _MSC_VER && 1200 <= _MSC_VER +# define _Noreturn __declspec (noreturn) +# else +# define _Noreturn YY_ATTRIBUTE ((__noreturn__)) +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(E) ((void) (E)) +#else +# define YYUSE(E) /* empty */ +#endif + +#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 28 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 215 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 34 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 66 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 99 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 257 + +/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned + by yylex, with out-of-bounds checking. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 283 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, without out-of-bounds checking. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 30, 32, 2, 2, 33, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 29, 2, 31, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 76, 76, 88, 88, 91, 94, 94, 97, 97, + 97, 97, 100, 103, 105, 106, 109, 111, 112, 115, + 118, 122, 127, 127, 127, 127, 127, 127, 130, 130, + 134, 138, 139, 142, 143, 145, 146, 147, 148, 150, + 151, 154, 157, 160, 164, 166, 167, 168, 169, 172, + 176, 179, 182, 185, 188, 191, 194, 197, 200, 203, + 204, 205, 206, 209, 212, 215, 217, 218, 219, 222, + 224, 225, 226, 229, 232, 235, 238, 240, 243, 248, + 251, 253, 256, 259, 262, 265, 268, 271, 274, 277, + 280, 283, 286, 289, 292, 294, 296, 297, 298, 301 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || 1 +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of string\"", "error", "$undefined", "\"PARAM_MT\"", + "\"CONCAT_MT\"", "\"INVERSE_MT\"", "\"PASSTHROUGH_MT\"", "\"PROJCS\"", + "\"PROJECTION\"", "\"GEOGCS\"", "\"DATUM\"", "\"SPHEROID\"", + "\"PRIMEM\"", "\"UNIT\"", "\"GEOCCS\"", "\"AUTHORITY\"", "\"VERT_CS\"", + "\"VERT_DATUM\"", "\"COMPD_CS\"", "\"AXIS\"", "\"TOWGS84\"", + "\"FITTED_CS\"", "\"LOCAL_CS\"", "\"LOCAL_DATUM\"", "\"PARAMETER\"", + "\"EXTENSION\"", "\"string\"", "\"number\"", "\"identifier\"", "'['", + "'('", "']'", "')'", "','", "$accept", "input", "begin_node", + "begin_node_name", "end_node", "math_transform", "param_mt", "parameter", + "opt_parameter_list", "concat_mt", "opt_math_transform_list", "inv_mt", + "passthrough_mt", "integer", "coordinate_system", "horz_cs", + "projected_cs", "opt_parameter_list_linear_unit", + "parameter_list_linear_unit", "opt_twin_axis_extension_authority", + "opt_authority", "extension", "projection", "geographic_cs", "datum", + "opt_towgs84_authority_extension", "spheroid", "semi_major_axis", + "inverse_flattening", "prime_meridian", "longitude", "angular_unit", + "linear_unit", "unit", "conversion_factor", "geocentric_cs", + "opt_three_axis_extension_authority", "three_axis", "authority", + "vert_cs", "opt_axis_authority", "vert_datum", "opt_extension_authority", + "datum_type", "compd_cs", "head_cs", "tail_cs", "twin_axis", "axis", + "towgs84", "towgs84_parameters", "three_parameters", "seven_parameters", + "dx", "dy", "dz", "ex", "ey", "ez", "ppm", "fitted_cs", "to_base", + "base_cs", "local_cs", "opt_axis_list_authority", "local_datum", YY_NULLPTR +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 91, + 40, 93, 41, 44 +}; +# endif + +#define YYPACT_NINF -127 + +#define yypact_value_is_default(Yystate) \ + (!!((Yystate) == (-127))) + +#define YYTABLE_NINF -1 + +#define yytable_value_is_error(Yytable_value) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int16 yypact[] = +{ + 136, 2, 2, 2, 2, 2, 2, 2, 26, -127, + -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, + 11, 5, 18, 32, 36, 40, 42, 53, -127, -127, + 35, 66, 66, 61, 136, 85, -127, -127, 59, -127, + 67, 2, 68, 69, 2, 70, -127, 72, 2, 2, + 2, 2, -127, -127, -127, -127, -127, 76, 2, 77, + 84, 79, 87, 87, 82, 105, 136, 88, 85, 85, + 93, 136, 89, 105, 2, 90, 113, 2, 95, 96, + 103, 2, 98, -127, -127, 99, 109, 25, 101, 25, + -127, 107, -127, 25, 103, 111, 114, 1, 2, 115, + 120, 105, 105, -127, 99, 122, 14, 25, 34, 25, + 2, 88, -127, 85, 25, -127, 85, -127, 114, 119, + 141, 25, 126, 127, -127, -127, 128, 15, 25, 135, + 127, -127, 130, 25, 137, 2, 2, -127, 114, -127, + 2, 114, -127, -127, 132, -127, 101, -127, 25, 25, + 133, -127, -127, 1, 33, 25, 140, 2, 114, -127, + 99, -127, -127, 114, 25, 33, 25, -127, -127, 114, + 138, 139, -127, 142, -127, 143, -127, -127, -127, 14, + 25, -127, -127, 114, -127, 99, 144, -127, -127, 145, + 146, -127, -127, 25, -127, 114, 99, -127, 147, -127, + 25, 148, 151, 150, 25, -127, 133, -127, -127, -127, + 119, 154, -127, 25, -127, -127, 149, -127, -127, -127, + 119, -127, 25, 25, 25, -127, -127, -127, -127, 114, + -127, 156, 153, -127, -127, -127, 25, -127, 155, 119, + -127, 157, -127, -127, 158, 160, -127, 159, 162, -127, + 161, 163, -127, 164, 166, -127, -127 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, + 22, 29, 28, 23, 24, 25, 26, 27, 3, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 5, + 0, 0, 0, 0, 0, 0, 6, 7, 0, 95, + 0, 0, 0, 0, 0, 0, 75, 0, 0, 0, + 0, 0, 92, 8, 9, 10, 11, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 66, 55, 76, 70, 0, 0, 17, 0, + 21, 0, 93, 0, 0, 0, 39, 0, 0, 45, + 0, 0, 0, 73, 70, 0, 0, 0, 0, 0, + 0, 14, 12, 0, 0, 19, 0, 91, 39, 0, + 0, 0, 0, 35, 32, 31, 0, 0, 0, 0, + 35, 54, 59, 0, 0, 0, 0, 68, 39, 65, + 0, 39, 72, 74, 0, 15, 17, 16, 0, 0, + 96, 40, 42, 0, 0, 0, 0, 0, 39, 48, + 70, 44, 53, 39, 0, 0, 0, 69, 57, 39, + 0, 0, 67, 0, 71, 0, 18, 20, 99, 0, + 0, 33, 34, 39, 38, 70, 0, 30, 50, 0, + 0, 47, 46, 0, 43, 39, 70, 62, 0, 58, + 0, 0, 0, 0, 0, 97, 96, 94, 37, 36, + 0, 0, 84, 0, 81, 80, 0, 52, 61, 60, + 0, 56, 0, 0, 0, 13, 98, 77, 51, 39, + 79, 0, 0, 64, 78, 41, 0, 85, 0, 0, + 49, 0, 63, 86, 82, 0, 87, 0, 0, 88, + 0, 0, 89, 0, 0, 90, 83 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -127, -127, -46, 6, -87, -50, -127, 83, 57, -127, + 49, -127, -127, -127, -11, -127, -127, -127, 43, 71, + -44, -126, -127, 168, 167, -127, -127, -127, -127, 152, + -127, -127, -81, -56, -127, -127, -127, -127, -84, -127, + -127, -127, -89, 106, -127, -127, -127, -127, -112, -127, + -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, + -127, -127, -127, -127, -4, -127 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + -1, 8, 20, 21, 39, 52, 53, 122, 87, 54, + 114, 55, 56, 91, 9, 10, 11, 123, 124, 155, + 121, 141, 75, 12, 42, 128, 99, 189, 229, 78, + 163, 130, 82, 83, 169, 13, 166, 196, 137, 14, + 107, 45, 109, 104, 15, 47, 85, 185, 138, 160, + 213, 214, 215, 216, 238, 244, 247, 250, 253, 256, + 16, 57, 93, 17, 180, 59 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_uint8 yytable[] = +{ + 112, 158, 115, 68, 69, 70, 117, 150, 22, 23, + 24, 25, 26, 27, 81, 133, 125, 95, 88, 89, + 139, 132, 143, 46, 142, 110, 28, 147, 183, 135, + 135, 18, 19, 136, 152, 157, 151, 29, 30, 195, + 140, 161, 186, 159, 2, 131, 167, 61, 135, 135, + 64, 31, 136, 198, 67, 84, 36, 37, 140, 140, + 92, 177, 178, 146, 72, 32, 148, 206, 187, 33, + 184, 192, 182, 34, 149, 35, 41, 194, 44, 199, + 96, 197, 58, 100, 36, 37, 38, 105, 48, 49, + 50, 51, 74, 207, 172, 205, 209, 174, 227, 77, + 60, 62, 63, 65, 126, 66, 217, 219, 232, 71, + 73, 190, 76, 221, 191, 80, 144, 225, 81, 193, + 90, 86, 94, 97, 98, 200, 230, 242, 101, 102, + 103, 106, 108, 110, 113, 233, 234, 235, 136, 208, + 116, 170, 171, 1, 119, 2, 173, 120, 127, 240, + 3, 218, 4, 129, 5, 134, 135, 6, 7, 153, + 154, 156, 162, 165, 168, 175, 179, 188, 145, 111, + 204, 201, 202, 212, 222, 203, 224, 210, 211, 223, + 220, 228, 231, 237, 243, 236, 239, 246, 241, 249, + 252, 245, 248, 255, 251, 176, 181, 254, 40, 43, + 118, 164, 226, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 79 +}; + +static const yytype_int16 yycheck[] = +{ + 87, 127, 89, 49, 50, 51, 93, 119, 2, 3, + 4, 5, 6, 7, 13, 104, 97, 73, 68, 69, + 107, 102, 109, 34, 108, 24, 0, 114, 154, 15, + 15, 29, 30, 19, 121, 20, 120, 26, 33, 165, + 25, 128, 154, 127, 9, 101, 133, 41, 15, 15, + 44, 33, 19, 165, 48, 66, 31, 32, 25, 25, + 71, 148, 149, 113, 58, 33, 116, 179, 155, 33, + 154, 160, 153, 33, 118, 33, 10, 164, 17, 166, + 74, 165, 23, 77, 31, 32, 33, 81, 3, 4, + 5, 6, 8, 180, 138, 179, 185, 141, 210, 12, + 33, 33, 33, 33, 98, 33, 193, 196, 220, 33, + 33, 157, 33, 200, 158, 33, 110, 204, 13, 163, + 27, 33, 33, 33, 11, 169, 213, 239, 33, 33, + 27, 33, 33, 24, 33, 222, 223, 224, 19, 183, + 33, 135, 136, 7, 33, 9, 140, 33, 33, 236, + 14, 195, 16, 33, 18, 33, 15, 21, 22, 33, + 33, 33, 27, 33, 27, 33, 33, 27, 111, 86, + 27, 33, 33, 27, 26, 33, 26, 33, 33, 28, + 33, 27, 33, 27, 27, 229, 33, 27, 33, 27, + 27, 33, 33, 27, 33, 146, 153, 33, 30, 32, + 94, 130, 206, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 63 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 7, 9, 14, 16, 18, 21, 22, 35, 48, + 49, 50, 57, 69, 73, 78, 94, 97, 29, 30, + 36, 37, 37, 37, 37, 37, 37, 37, 0, 26, + 33, 33, 33, 33, 33, 33, 31, 32, 33, 38, + 57, 10, 58, 58, 17, 75, 48, 79, 3, 4, + 5, 6, 39, 40, 43, 45, 46, 95, 23, 99, + 33, 37, 33, 33, 37, 33, 33, 37, 36, 36, + 36, 33, 37, 33, 8, 56, 33, 12, 63, 63, + 33, 13, 66, 67, 48, 80, 33, 42, 39, 39, + 27, 47, 48, 96, 33, 67, 37, 33, 11, 60, + 37, 33, 33, 27, 77, 37, 33, 74, 33, 76, + 24, 41, 38, 33, 44, 38, 33, 38, 77, 33, + 33, 54, 41, 51, 52, 66, 37, 33, 59, 33, + 65, 67, 66, 76, 33, 15, 19, 72, 82, 38, + 25, 55, 72, 38, 37, 42, 39, 38, 39, 54, + 82, 72, 38, 33, 33, 53, 33, 20, 55, 72, + 83, 38, 27, 64, 53, 33, 70, 38, 27, 68, + 37, 37, 54, 37, 54, 33, 44, 38, 38, 33, + 98, 52, 66, 55, 72, 81, 82, 38, 27, 61, + 36, 54, 76, 54, 38, 55, 71, 72, 82, 38, + 54, 33, 33, 33, 27, 72, 82, 38, 54, 76, + 33, 33, 27, 84, 85, 86, 87, 38, 54, 76, + 33, 38, 26, 28, 26, 38, 98, 82, 27, 62, + 38, 33, 82, 38, 38, 38, 54, 27, 88, 33, + 38, 33, 82, 27, 89, 33, 27, 90, 33, 27, + 91, 33, 27, 92, 33, 27, 93 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 34, 35, 36, 36, 37, 38, 38, 39, 39, + 39, 39, 40, 41, 42, 42, 43, 44, 44, 45, + 46, 47, 48, 48, 48, 48, 48, 48, 49, 49, + 50, 51, 51, 52, 52, 53, 53, 53, 53, 54, + 54, 55, 56, 57, 58, 59, 59, 59, 59, 60, + 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, + 70, 70, 70, 71, 72, 73, 74, 74, 74, 75, + 76, 76, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 84, 85, 86, 87, 88, 89, 90, 91, 92, + 93, 94, 95, 96, 97, 97, 98, 98, 98, 99 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 1, 1, 1, 2, 1, 1, 1, 1, + 1, 1, 4, 5, 0, 3, 5, 0, 3, 4, + 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 10, 1, 1, 3, 3, 0, 3, 3, 2, 0, + 2, 5, 4, 10, 6, 0, 3, 3, 2, 8, + 1, 1, 6, 1, 1, 1, 6, 1, 10, 0, + 3, 3, 2, 5, 5, 8, 0, 3, 2, 6, + 0, 3, 2, 1, 8, 1, 1, 3, 5, 4, + 1, 1, 5, 13, 1, 1, 1, 1, 1, 1, + 1, 7, 1, 1, 10, 3, 0, 2, 3, 6 +}; + + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (context, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (0) + +/* Error token number */ +#define YYTERROR 1 +#define YYERRCODE 256 + + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +/* This macro is provided for backward compatibility. */ +#ifndef YY_LOCATION_PRINT +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +#endif + + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value, context); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*----------------------------------------. +| Print this symbol's value on YYOUTPUT. | +`----------------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, pj_wkt1_parse_context *context) +{ + FILE *yyo = yyoutput; + YYUSE (yyo); + YYUSE (context); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# endif + YYUSE (yytype); +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, pj_wkt1_parse_context *context) +{ + YYFPRINTF (yyoutput, "%s %s (", + yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep, context); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule, pj_wkt1_parse_context *context) +{ + unsigned long int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + yystos[yyssp[yyi + 1 - yynrhs]], + &(yyvsp[(yyi + 1) - (yynrhs)]) + , context); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule, context); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +static YYSIZE_T +yystrlen (const char *yystr) +{ + YYSIZE_T yylen; + for (yylen = 0; yystr && yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return 2 if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, + yytype_int16 *yyssp, int yytoken) +{ + YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); + YYSIZE_T yysize = yysize0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat. */ + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + /* Number of reported tokens (one for the "unexpected", one per + "expected"). */ + int yycount = 0; + + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yytoken != YYEMPTY) + { + int yyn = yypact[*yyssp]; + yyarg[yycount++] = yytname[yytoken]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + break; + } + yyarg[yycount++] = yytname[yyx]; + { + YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); + if (! (yysize <= yysize1 + && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + } + } + } + + switch (yycount) + { +# define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +# undef YYCASE_ + } + + { + YYSIZE_T yysize1 = yysize + yystrlen (yyformat); + if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) + return 2; + yysize = yysize1; + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return 1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyformat += 2; + } + else + { + yyp++; + yyformat++; + } + } + return 0; +} +#endif /* YYERROR_VERBOSE */ + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, pj_wkt1_parse_context *context) +{ + YYUSE (yyvaluep); + YYUSE (context); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yytype); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (pj_wkt1_parse_context *context) +{ +/* The lookahead symbol. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + + /* Number of syntax errors so far. */ + int yynerrs; + + int yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + 'yyss': related to states. + 'yyvs': related to semantic values. + + Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + YYSIZE_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yyssp = yyss = yyssa; + yyvsp = yyvs = yyvsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = (yytype_int16)yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = yylex (&yylval, context); + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token. */ + yychar = YYEMPTY; + + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); + + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (context, YY_("syntax error")); +#else +# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ + yyssp, yytoken) + { + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = YYSYNTAX_ERROR; + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == 1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); + if (!yymsg) + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = 2; + } + else + { + yysyntax_error_status = YYSYNTAX_ERROR; + yymsgp = yymsg; + } + } + yyerror (context, yymsgp); + if (yysyntax_error_status == 2) + goto yyexhaustedlab; + } +# undef YYSYNTAX_ERROR +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, context); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +#if 0 +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +#endif +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp, context); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#if !defined yyoverflow || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (context, YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, context); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp, context); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + return yyresult; +} diff --git a/src/pj_wkt1_generated_parser.h b/src/pj_wkt1_generated_parser.h new file mode 100644 index 00000000..7bdec61f --- /dev/null +++ b/src/pj_wkt1_generated_parser.h @@ -0,0 +1,89 @@ +/* A Bison parser, made by GNU Bison 3.0.4. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +#ifndef YY_PJ_WKT1_SRC_PJ_WKT1_GENERATED_PARSER_H_INCLUDED +# define YY_PJ_WKT1_SRC_PJ_WKT1_GENERATED_PARSER_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int pj_wkt1_debug; +#endif + +/* Token type. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + END = 0, + T_PARAM_MT = 258, + T_CONCAT_MT = 259, + T_INVERSE_MT = 260, + T_PASSTHROUGH_MT = 261, + T_PROJCS = 262, + T_PROJECTION = 263, + T_GEOGCS = 264, + T_DATUM = 265, + T_SPHEROID = 266, + T_PRIMEM = 267, + T_UNIT = 268, + T_GEOCCS = 269, + T_AUTHORITY = 270, + T_VERT_CS = 271, + T_VERT_DATUM = 272, + T_COMPD_CS = 273, + T_AXIS = 274, + T_TOWGS84 = 275, + T_FITTED_CS = 276, + T_LOCAL_CS = 277, + T_LOCAL_DATUM = 278, + T_PARAMETER = 279, + T_EXTENSION = 280, + T_STRING = 281, + T_NUMBER = 282, + T_IDENTIFIER = 283 + }; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef int YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + + +int pj_wkt1_parse (pj_wkt1_parse_context *context); + +#endif /* !YY_PJ_WKT1_SRC_PJ_WKT1_GENERATED_PARSER_H_INCLUDED */ diff --git a/src/pj_wkt1_grammar.y b/src/pj_wkt1_grammar.y new file mode 100644 index 00000000..05ae792a --- /dev/null +++ b/src/pj_wkt1_grammar.y @@ -0,0 +1,301 @@ +%{ +/****************************************************************************** + * Project: PROJ + * Purpose: WKT1 parser grammar + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2013-2018 Even Rouault, + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "pj_wkt1_parser.h" + +%} + +%define api.pure +/* if the next %define is commented out, Bison 2.4 should be sufficient */ +/* but will produce less prettier error messages */ +%define parse.error verbose +%require "3.0" + +%parse-param {pj_wkt1_parse_context *context} +%lex-param {pj_wkt1_parse_context *context} + +%token T_PARAM_MT "PARAM_MT" +%token T_CONCAT_MT "CONCAT_MT" +%token T_INVERSE_MT "INVERSE_MT" +%token T_PASSTHROUGH_MT "PASSTHROUGH_MT" +%token T_PROJCS "PROJCS" +%token T_PROJECTION "PROJECTION" +%token T_GEOGCS "GEOGCS" +%token T_DATUM "DATUM" +%token T_SPHEROID "SPHEROID" +%token T_PRIMEM "PRIMEM" +%token T_UNIT "UNIT" +%token T_GEOCCS "GEOCCS" +%token T_AUTHORITY "AUTHORITY" +%token T_VERT_CS "VERT_CS" +%token T_VERT_DATUM "VERT_DATUM" +%token T_COMPD_CS "COMPD_CS" +%token T_AXIS "AXIS" +%token T_TOWGS84 "TOWGS84" +%token T_FITTED_CS "FITTED_CS" +%token T_LOCAL_CS "LOCAL_CS" +%token T_LOCAL_DATUM "LOCAL_DATUM" +%token T_PARAMETER "PARAMETER" + +%token T_EXTENSION "EXTENSION" + +%token T_STRING "string" +%token T_NUMBER "number" +%token T_IDENTIFIER "identifier" + +%token END 0 "end of string" + +%% + +input: + coordinate_system + +/* Derived from BNF grammar in OGC 01-009 OpenGIS Implementation */ +/* Coordinate Transformation Services Revision 1.00 */ +/* with the following additions : */ +/* - accept an EXTENSION node at the end of GEOGCS, GEOCCS, PROJCS, COMPD_CS, VERT_DATUM */ +/* - accept 3 parameters in TOWGS84 */ +/* - accept LOCAL_CS["foo"] */ + +/* 7.1 Math Transform WKT */ + +begin_node: + '[' | '(' + +begin_node_name: + begin_node T_STRING + +end_node: + ']' | ')' + +math_transform: + param_mt | concat_mt | inv_mt | passthrough_mt + +param_mt: + T_PARAM_MT begin_node_name opt_parameter_list end_node + +parameter: + T_PARAMETER begin_node_name ',' T_NUMBER end_node + +opt_parameter_list: + | ',' parameter opt_parameter_list + +concat_mt: + T_CONCAT_MT begin_node math_transform opt_math_transform_list end_node + +opt_math_transform_list: + | ',' math_transform opt_math_transform_list + +inv_mt: + T_INVERSE_MT begin_node math_transform end_node + +passthrough_mt: + T_PASSTHROUGH_MT begin_node integer ',' math_transform end_node + +/* FIXME */ +integer: + T_NUMBER + +/* 7.2 Coordinate System WKT */ + +coordinate_system: + horz_cs | geocentric_cs | vert_cs | compd_cs | fitted_cs | local_cs + +horz_cs: + geographic_cs | projected_cs + +/* opt_extension is an extension of the CT spec */ +projected_cs: + T_PROJCS begin_node_name ',' geographic_cs ',' projection ',' + opt_parameter_list_linear_unit opt_twin_axis_extension_authority end_node + +opt_parameter_list_linear_unit: + linear_unit + | parameter_list_linear_unit + +parameter_list_linear_unit: + parameter ',' parameter_list_linear_unit + | parameter ',' linear_unit + +opt_twin_axis_extension_authority: + | ',' twin_axis opt_extension_authority + | ',' extension opt_authority + | ',' authority + +opt_authority: + | ',' authority + +extension: + T_EXTENSION begin_node_name ',' T_STRING end_node + +projection: + T_PROJECTION begin_node_name opt_authority end_node + +geographic_cs: + T_GEOGCS begin_node_name',' datum ',' prime_meridian ',' + angular_unit opt_twin_axis_extension_authority end_node + +datum: + T_DATUM begin_node_name ',' spheroid opt_towgs84_authority_extension end_node + +opt_towgs84_authority_extension: + | ',' towgs84 opt_extension_authority + | ',' extension opt_authority + | ',' authority + +spheroid: + T_SPHEROID begin_node_name ',' semi_major_axis ',' + inverse_flattening opt_authority end_node + +semi_major_axis: + T_NUMBER + +inverse_flattening: + T_NUMBER + +prime_meridian: + T_PRIMEM begin_node_name ',' longitude opt_authority end_node + +longitude: + T_NUMBER + +angular_unit: + unit + +linear_unit: + unit + +unit: + T_UNIT begin_node_name ',' conversion_factor opt_authority end_node + +conversion_factor: + T_NUMBER + +geocentric_cs: + T_GEOCCS begin_node_name ',' datum ',' prime_meridian ',' + linear_unit opt_three_axis_extension_authority end_node + +opt_three_axis_extension_authority: + | ',' three_axis opt_extension_authority + | ',' extension opt_authority + | ',' authority + +three_axis: + axis ',' axis ',' axis + +authority: + T_AUTHORITY begin_node_name ',' T_STRING end_node + +vert_cs: + T_VERT_CS begin_node_name ',' vert_datum ',' linear_unit opt_axis_authority end_node + +opt_axis_authority: + | ',' axis opt_authority + | ',' authority + +vert_datum: + T_VERT_DATUM begin_node_name ',' datum_type opt_extension_authority end_node + +opt_extension_authority: + | ',' extension opt_authority + | ',' authority + +datum_type: + T_NUMBER + +compd_cs: + T_COMPD_CS begin_node_name ',' head_cs ',' tail_cs opt_extension_authority end_node + +head_cs: + coordinate_system + +tail_cs: + coordinate_system + +twin_axis: axis ',' axis + +axis: + T_AXIS begin_node_name ',' T_IDENTIFIER end_node +/* Extension of the CT spec */ +/* | T_AXIS '[' T_STRING ',' T_STRING ']'*/ + +towgs84: + T_TOWGS84 begin_node towgs84_parameters end_node + +towgs84_parameters: + seven_parameters +/* Extension of the CT spec */ + | three_parameters + +three_parameters: + dx ',' dy ',' dz + +seven_parameters: + dx ',' dy ',' dz ',' ex ',' ey ',' ez ',' ppm + +dx: + T_NUMBER + +dy: + T_NUMBER + +dz: + T_NUMBER + +ex: + T_NUMBER + +ey: + T_NUMBER + +ez: + T_NUMBER + +ppm: + T_NUMBER + +fitted_cs: + T_FITTED_CS begin_node_name ',' to_base ',' base_cs end_node + +to_base: + math_transform + +base_cs: + coordinate_system + +local_cs: + T_LOCAL_CS begin_node_name ',' local_datum ',' unit ',' axis opt_axis_list_authority end_node +/* Extension of the CT spec: accept only name */ + | T_LOCAL_CS begin_node_name end_node + +opt_axis_list_authority: + | ',' authority + | ',' axis opt_axis_list_authority + +local_datum: + T_LOCAL_DATUM begin_node_name ',' datum_type opt_authority end_node diff --git a/src/pj_wkt1_parser.cpp b/src/pj_wkt1_parser.cpp new file mode 100644 index 00000000..7ddd595a --- /dev/null +++ b/src/pj_wkt1_parser.cpp @@ -0,0 +1,221 @@ +/****************************************************************************** + * Project: PROJ + * Purpose: WKT1 parser grammar + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef FROM_PROJ_CPP +#define FROM_PROJ_CPP +#endif + +#include "proj/internal/internal.hpp" + +#include +#include +#include + +#include "pj_wkt1_parser.h" + +using namespace NS_PROJ::internal; + +//! @cond Doxygen_Suppress + +// --------------------------------------------------------------------------- + +struct pj_wkt1_parse_context { + const char *pszInput = nullptr; + const char *pszLastSuccess = nullptr; + const char *pszNext = nullptr; + std::string errorMsg{}; + + pj_wkt1_parse_context() = default; + pj_wkt1_parse_context(const pj_wkt1_parse_context &) = delete; + pj_wkt1_parse_context &operator=(const pj_wkt1_parse_context &) = delete; +}; + +// --------------------------------------------------------------------------- + +void pj_wkt1_error(pj_wkt1_parse_context *context, const char *msg) { + context->errorMsg = "Parsing error : "; + context->errorMsg += msg; + context->errorMsg += ". Error occurred around:\n"; + + std::string ctxtMsg; + const int n = static_cast(context->pszLastSuccess - context->pszInput); + int start_i = std::max(0, n - 40); + for (int i = start_i; i < n + 40 && context->pszInput[i]; i++) { + if (context->pszInput[i] == '\r' || context->pszInput[i] == '\n') { + if (i > n) { + break; + } else { + ctxtMsg.clear(); + start_i = i + 1; + } + } else { + ctxtMsg += context->pszInput[i]; + } + } + context->errorMsg += ctxtMsg; + context->errorMsg += '\n'; + for (int i = start_i; i < n; i++) + context->errorMsg += ' '; + context->errorMsg += '^'; +} + +// --------------------------------------------------------------------------- + +std::string pj_wkt1_parse(const std::string &wkt) { + pj_wkt1_parse_context context; + context.pszInput = wkt.c_str(); + context.pszLastSuccess = wkt.c_str(); + context.pszNext = wkt.c_str(); + if (pj_wkt1_parse(&context) != 0) { + return context.errorMsg; + } + return std::string(); +} + +// --------------------------------------------------------------------------- + +typedef struct { + const char *pszToken; + int nTokenVal; +} osr_cs_wkt_tokens; + +#define PAIR(X) \ + { #X, T_##X } + +static const osr_cs_wkt_tokens tokens[] = { + PAIR(PARAM_MT), PAIR(PARAMETER), PAIR(CONCAT_MT), PAIR(INVERSE_MT), + PAIR(PASSTHROUGH_MT), + + PAIR(PROJCS), PAIR(PROJECTION), PAIR(GEOGCS), PAIR(DATUM), + PAIR(SPHEROID), PAIR(PRIMEM), PAIR(UNIT), PAIR(GEOCCS), + PAIR(AUTHORITY), PAIR(VERT_CS), PAIR(VERT_DATUM), PAIR(COMPD_CS), + PAIR(AXIS), PAIR(TOWGS84), PAIR(FITTED_CS), PAIR(LOCAL_CS), + PAIR(LOCAL_DATUM), + + PAIR(EXTENSION)}; + +// --------------------------------------------------------------------------- + +int pj_wkt1_lex(YYSTYPE * /*pNode */, pj_wkt1_parse_context *context) { + size_t i; + const char *pszInput = context->pszNext; + + /* -------------------------------------------------------------------- */ + /* Skip white space. */ + /* -------------------------------------------------------------------- */ + while (*pszInput == ' ' || *pszInput == '\t' || *pszInput == 10 || + *pszInput == 13) + pszInput++; + + context->pszLastSuccess = pszInput; + + if (*pszInput == '\0') { + context->pszNext = pszInput; + return EOF; + } + + /* -------------------------------------------------------------------- */ + /* Recognize node names. */ + /* -------------------------------------------------------------------- */ + for (i = 0; i < sizeof(tokens) / sizeof(tokens[0]); i++) { + if (ci_starts_with(pszInput, tokens[i].pszToken)) { + context->pszNext = pszInput + strlen(tokens[i].pszToken); + return tokens[i].nTokenVal; + } + } + + /* -------------------------------------------------------------------- */ + /* Recognize double quoted strings. */ + /* -------------------------------------------------------------------- */ + if (*pszInput == '"') { + pszInput++; + while (*pszInput != '\0' && *pszInput != '"') + pszInput++; + if (*pszInput == '\0') { + context->pszNext = pszInput; + return EOF; + } + context->pszNext = pszInput + 1; + return T_STRING; + } + + /* -------------------------------------------------------------------- */ + /* Recognize numerical values. */ + /* -------------------------------------------------------------------- */ + + if (((*pszInput == '-' || *pszInput == '+') && pszInput[1] >= '0' && + pszInput[1] <= '9') || + (*pszInput >= '0' && *pszInput <= '9')) { + if (*pszInput == '-' || *pszInput == '+') + pszInput++; + + // collect non-decimal part of number + while (*pszInput >= '0' && *pszInput <= '9') + pszInput++; + + // collect decimal places. + if (*pszInput == '.') { + pszInput++; + while (*pszInput >= '0' && *pszInput <= '9') + pszInput++; + } + + // collect exponent + if (*pszInput == 'e' || *pszInput == 'E') { + pszInput++; + if (*pszInput == '-' || *pszInput == '+') + pszInput++; + while (*pszInput >= '0' && *pszInput <= '9') + pszInput++; + } + + context->pszNext = pszInput; + + return T_NUMBER; + } + + /* -------------------------------------------------------------------- */ + /* Recognize identifiers. */ + /* -------------------------------------------------------------------- */ + if ((*pszInput >= 'A' && *pszInput <= 'Z') || + (*pszInput >= 'a' && *pszInput <= 'z')) { + pszInput++; + while ((*pszInput >= 'A' && *pszInput <= 'Z') || + (*pszInput >= 'a' && *pszInput <= 'z')) + pszInput++; + context->pszNext = pszInput; + return T_IDENTIFIER; + } + + /* -------------------------------------------------------------------- */ + /* Handle special tokens. */ + /* -------------------------------------------------------------------- */ + context->pszNext = pszInput + 1; + return *pszInput; +} + +//! @endcond diff --git a/src/pj_wkt1_parser.h b/src/pj_wkt1_parser.h new file mode 100644 index 00000000..f8f7c841 --- /dev/null +++ b/src/pj_wkt1_parser.h @@ -0,0 +1,54 @@ +/****************************************************************************** + * Project: PROJ + * Purpose: WKT1 parser grammar + * Author: Even Rouault, + * + ****************************************************************************** + * Copyright (c) 2013, Even Rouault + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef PJ_WKT1_PARSER_H_INCLUDED +#define PJ_WKT1_PARSER_H_INCLUDED + +#ifndef DOXYGEN_SKIP + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct pj_wkt1_parse_context pj_wkt1_parse_context; + +#include "pj_wkt1_generated_parser.h" + +void pj_wkt1_error( pj_wkt1_parse_context *context, const char *msg ); +int pj_wkt1_lex(YYSTYPE* pNode, pj_wkt1_parse_context *context); +int pj_wkt1_parse(pj_wkt1_parse_context *context); + +#ifdef __cplusplus +} + +std::string pj_wkt1_parse(const std::string& wkt); + +#endif + +#endif /* #ifndef DOXYGEN_SKIP */ + +#endif /* PJ_WKT1_PARSER_H_INCLUDED */ diff --git a/src/proj.h b/src/proj.h index 3ea1ee68..5c0e8fdc 100644 --- a/src/proj.h +++ b/src/proj.h @@ -456,6 +456,11 @@ typedef struct PJ_OBJ PJ_OBJ; typedef struct PJ_OBJ_LIST PJ_OBJ_LIST; /*! @endcond */ +/** \brief Type representing a NULL terminated list of NUL-terminate strings. */ +typedef char **PROJ_STRING_LIST; + +void PROJ_DLL proj_string_list_destroy(PROJ_STRING_LIST list); + int PROJ_DLL proj_context_set_database_path(PJ_CONTEXT *ctx, const char *dbPath, const char *const *auxDbPaths, @@ -494,7 +499,9 @@ PJ_OBJ PROJ_DLL *proj_obj_create_from_user_input(PJ_CONTEXT *ctx, const char* const *options); PJ_OBJ PROJ_DLL *proj_obj_create_from_wkt(PJ_CONTEXT *ctx, const char *wkt, - const char* const *options); + const char* const *options, + PROJ_STRING_LIST *out_warnings, + PROJ_STRING_LIST *out_grammar_errors); PJ_OBJ PROJ_DLL *proj_obj_create_from_proj_string(PJ_CONTEXT *ctx, const char *proj_string, @@ -677,9 +684,6 @@ void PROJ_DLL proj_int_list_destroy(int* list); /* ------------------------------------------------------------------------- */ -/** \brief Type representing a NULL terminated list of NUL-terminate strings. */ -typedef char **PROJ_STRING_LIST; - PROJ_STRING_LIST PROJ_DLL proj_get_authorities_from_database(PJ_CONTEXT *ctx); PROJ_STRING_LIST PROJ_DLL proj_get_codes_from_database(PJ_CONTEXT *ctx, @@ -687,8 +691,6 @@ PROJ_STRING_LIST PROJ_DLL proj_get_codes_from_database(PJ_CONTEXT *ctx, PJ_OBJ_TYPE type, int allow_deprecated); -void PROJ_DLL proj_string_list_destroy(PROJ_STRING_LIST list); - /* ------------------------------------------------------------------------- */ diff --git a/src/projinfo.cpp b/src/projinfo.cpp index d6fa37bc..cf666b4d 100644 --- a/src/projinfo.cpp +++ b/src/projinfo.cpp @@ -136,8 +136,8 @@ static std::string c_ify_string(const std::string &str) { static BaseObjectNNPtr buildObject(DatabaseContextPtr dbContext, const std::string &user_string, bool kindIsCRS, const std::string &context, - bool buildBoundCRSToWGS84, - bool allowPivots) { + bool buildBoundCRSToWGS84, bool allowPivots, + bool quiet) { BaseObjectPtr obj; std::string l_user_string(user_string); @@ -182,7 +182,24 @@ static BaseObjectNNPtr buildObject(DatabaseContextPtr dbContext, l_user_string.find("\\\"") != std::string::npos) { l_user_string = un_c_ify_string(l_user_string); } - obj = createFromUserInput(l_user_string, dbContext).as_nullable(); + WKTParser wktParser; + if (wktParser.guessDialect(l_user_string) != + WKTParser::WKTGuessedDialect::NOT_WKT) { + wktParser.setStrict(false); + wktParser.attachDatabaseContext(dbContext); + obj = wktParser.createFromWKT(l_user_string).as_nullable(); + if (!quiet) { + auto warnings = wktParser.warningList(); + if (!warnings.empty()) { + for (const auto &str : warnings) { + std::cerr << "Warning: " << str << std::endl; + } + } + } + } else { + obj = + createFromUserInput(l_user_string, dbContext).as_nullable(); + } } } catch (const std::exception &e) { std::cerr << context << ": parsing of user string failed: " << e.what() @@ -509,16 +526,16 @@ static void outputOperations( const std::vector> &pivots, const std::string &authority, bool usePROJGridAlternatives, bool showSuperseded, const OutputOptions &outputOpt, bool summary) { - auto sourceObj = - buildObject(dbContext, sourceCRSStr, true, "source CRS", false, false); + auto sourceObj = buildObject(dbContext, sourceCRSStr, true, "source CRS", + false, false, outputOpt.quiet); auto sourceCRS = nn_dynamic_pointer_cast(sourceObj); if (!sourceCRS) { std::cerr << "source CRS string is not a CRS" << std::endl; std::exit(1); } - auto targetObj = - buildObject(dbContext, targetCRSStr, true, "target CRS", false, false); + auto targetObj = buildObject(dbContext, targetCRSStr, true, "target CRS", + false, false, outputOpt.quiet); auto targetCRS = nn_dynamic_pointer_cast(targetObj); if (!targetCRS) { std::cerr << "target CRS string is not a CRS" << std::endl; @@ -910,7 +927,8 @@ int main(int argc, char **argv) { if (!user_string.empty()) { auto obj(buildObject(dbContext, user_string, kindIsCRS, "input string", - buildBoundCRSToWGS84, allowPivots)); + buildBoundCRSToWGS84, allowPivots, + outputOpt.quiet)); if (guessDialect) { auto dialect = WKTParser().guessDialect(user_string); std::cout << "Guessed WKT dialect: "; diff --git a/test/cli/testprojinfo b/test/cli/testprojinfo index ea0dcc2d..bae38482 100755 --- a/test/cli/testprojinfo +++ b/test/cli/testprojinfo @@ -78,6 +78,10 @@ echo "Testing deprecated CRS: projinfo EPSG:26591" >> ${OUT} $EXE EPSG:26591 >>${OUT} 2>&1 echo "" >>${OUT} +echo "Testing non compliant WKT1" >> ${OUT} +$EXE 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563]],UNIT["degree",0.0174532925199433]]' >>${OUT} 2>&1 +echo "" >>${OUT} + # do 'diff' with distribution results echo "diff ${OUT} with testprojinfo_out.dist" diff -u ${OUT} ${TEST_CLI_DIR}/testprojinfo_out.dist diff --git a/test/cli/testprojinfo_out.dist b/test/cli/testprojinfo_out.dist index 40035489..1fd3bbbf 100644 --- a/test/cli/testprojinfo_out.dist +++ b/test/cli/testprojinfo_out.dist @@ -523,3 +523,28 @@ PROJCRS["Monte Mario (Rome) / Italy zone 1", BBOX[36.53,5.94,47.04,12], ID["EPSG",26591]] +Testing non compliant WKT1 +Warning: GEOGCS should have a PRIMEM node +Warning: Parsing error : syntax error, unexpected UNIT, expecting PRIMEM. Error occurred around: +HEROID["WGS 84",6378137,298.257223563]],UNIT["degree",0.0174532925199433]] + ^ +PROJ string: ++proj=pipeline +step +proj=longlat +ellps=WGS84 +step +proj=unitconvert +xy_in=rad +xy_out=deg + +WKT2_2015 string: +GEODCRS["WGS 84", + DATUM["World Geodetic System 1984", + ELLIPSOID["WGS 84",6378137,298.257223563, + LENGTHUNIT["metre",1, + ID["EPSG",9001]]]], + PRIMEM["Greenwich",0, + ANGLEUNIT["degree",0.0174532925199433], + ID["EPSG",8901]], + CS[ellipsoidal,2], + AXIS["longitude",east, + ORDER[1], + ANGLEUNIT["degree",0.0174532925199433]], + AXIS["latitude",north, + ORDER[2], + ANGLEUNIT["degree",0.0174532925199433]]] + diff --git a/test/unit/test_c_api.cpp b/test/unit/test_c_api.cpp index 7df7442a..37ca076c 100644 --- a/test/unit/test_c_api.cpp +++ b/test/unit/test_c_api.cpp @@ -166,14 +166,128 @@ TEST_F(CApi, proj_obj_create_from_user_input) { TEST_F(CApi, proj_obj_create_from_wkt) { proj_obj_destroy(nullptr); - EXPECT_EQ(proj_obj_create_from_wkt(m_ctxt, "invalid", nullptr), nullptr); - auto obj = proj_obj_create_from_wkt( - m_ctxt, - GeographicCRS::EPSG_4326->exportToWKT(WKTFormatter::create().get()) - .c_str(), - nullptr); - ObjectKeeper keeper(obj); - EXPECT_NE(obj, nullptr); + { + EXPECT_EQ(proj_obj_create_from_wkt(m_ctxt, "invalid", nullptr, nullptr, + nullptr), + nullptr); + } + { + PROJ_STRING_LIST warningList = nullptr; + PROJ_STRING_LIST errorList = nullptr; + EXPECT_EQ(proj_obj_create_from_wkt(m_ctxt, "invalid", nullptr, + &warningList, &errorList), + nullptr); + EXPECT_EQ(warningList, nullptr); + proj_string_list_destroy(warningList); + EXPECT_NE(errorList, nullptr); + proj_string_list_destroy(errorList); + } + { + auto obj = proj_obj_create_from_wkt( + m_ctxt, + GeographicCRS::EPSG_4326->exportToWKT(WKTFormatter::create().get()) + .c_str(), + nullptr, nullptr, nullptr); + ObjectKeeper keeper(obj); + EXPECT_NE(obj, nullptr); + } + { + auto obj = proj_obj_create_from_wkt( + m_ctxt, + "GEOGCS[\"WGS 84\",\n" + " DATUM[\"WGS_1984\",\n" + " SPHEROID[\"WGS 84\",6378137,298.257223563,\"unused\"]],\n" + " PRIMEM[\"Greenwich\",0],\n" + " UNIT[\"degree\",0.0174532925199433]]", + nullptr, nullptr, nullptr); + EXPECT_EQ(obj, nullptr); + } + { + PROJ_STRING_LIST warningList = nullptr; + PROJ_STRING_LIST errorList = nullptr; + auto obj = proj_obj_create_from_wkt( + m_ctxt, + "GEOGCS[\"WGS 84\",\n" + " DATUM[\"WGS_1984\",\n" + " SPHEROID[\"WGS 84\",6378137,298.257223563,\"unused\"]],\n" + " PRIMEM[\"Greenwich\",0],\n" + " UNIT[\"degree\",0.0174532925199433]]", + nullptr, &warningList, &errorList); + EXPECT_EQ(obj, nullptr); + EXPECT_EQ(warningList, nullptr); + proj_string_list_destroy(warningList); + EXPECT_NE(errorList, nullptr); + proj_string_list_destroy(errorList); + } + { + PROJ_STRING_LIST warningList = nullptr; + PROJ_STRING_LIST errorList = nullptr; + const char *const options[] = {"STRICT=NO", nullptr}; + auto obj = proj_obj_create_from_wkt( + m_ctxt, + "GEOGCS[\"WGS 84\",\n" + " DATUM[\"WGS_1984\",\n" + " SPHEROID[\"WGS 84\",6378137,298.257223563,\"unused\"]],\n" + " PRIMEM[\"Greenwich\",0],\n" + " UNIT[\"degree\",0.0174532925199433]]", + options, &warningList, &errorList); + ObjectKeeper keeper(obj); + EXPECT_NE(obj, nullptr); + EXPECT_EQ(warningList, nullptr); + proj_string_list_destroy(warningList); + EXPECT_NE(errorList, nullptr); + proj_string_list_destroy(errorList); + } + { + PROJ_STRING_LIST warningList = nullptr; + PROJ_STRING_LIST errorList = nullptr; + auto obj = proj_obj_create_from_wkt( + m_ctxt, + GeographicCRS::EPSG_4326->exportToWKT(WKTFormatter::create().get()) + .c_str(), + nullptr, &warningList, &errorList); + ObjectKeeper keeper(obj); + EXPECT_NE(obj, nullptr); + EXPECT_EQ(warningList, nullptr); + EXPECT_EQ(errorList, nullptr); + } + // Warnings: missing projection parameters + { + PROJ_STRING_LIST warningList = nullptr; + PROJ_STRING_LIST errorList = nullptr; + auto obj = proj_obj_create_from_wkt( + m_ctxt, "PROJCS[\"test\",\n" + " GEOGCS[\"WGS 84\",\n" + " DATUM[\"WGS_1984\",\n" + " SPHEROID[\"WGS 84\",6378137,298.257223563]],\n" + " PRIMEM[\"Greenwich\",0],\n" + " UNIT[\"degree\",0.0174532925199433]],\n" + " PROJECTION[\"Transverse_Mercator\"],\n" + " PARAMETER[\"latitude_of_origin\",31],\n" + " UNIT[\"metre\",1]]", + nullptr, &warningList, &errorList); + ObjectKeeper keeper(obj); + EXPECT_NE(obj, nullptr); + EXPECT_NE(warningList, nullptr); + proj_string_list_destroy(warningList); + EXPECT_EQ(errorList, nullptr); + proj_string_list_destroy(errorList); + } + { + auto obj = proj_obj_create_from_wkt( + m_ctxt, "PROJCS[\"test\",\n" + " GEOGCS[\"WGS 84\",\n" + " DATUM[\"WGS_1984\",\n" + " SPHEROID[\"WGS 84\",6378137,298.257223563]],\n" + " PRIMEM[\"Greenwich\",0],\n" + " UNIT[\"degree\",0.0174532925199433]],\n" + " PROJECTION[\"Transverse_Mercator\"],\n" + " PARAMETER[\"latitude_of_origin\",31],\n" + " UNIT[\"metre\",1]]", + nullptr, nullptr, nullptr); + ObjectKeeper keeper(obj); + EXPECT_NE(obj, nullptr); + } } // --------------------------------------------------------------------------- @@ -195,7 +309,7 @@ TEST_F(CApi, proj_obj_as_wkt) { m_ctxt, GeographicCRS::EPSG_4326->exportToWKT(WKTFormatter::create().get()) .c_str(), - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); @@ -298,7 +412,7 @@ TEST_F(CApi, proj_obj_as_wkt_check_db_use) { m_ctxt, "GEOGCS[\"AGD66\",DATUM[\"Australian_Geodetic_Datum_1966\"," "SPHEROID[\"Australian National Spheroid\",6378160,298.25]]," "PRIMEM[\"Greenwich\",0],UNIT[\"degree\",0.0174532925199433]]", - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); @@ -316,7 +430,7 @@ TEST_F(CApi, proj_obj_as_wkt_incompatible_WKT1) { auto obj = proj_obj_create_from_wkt( m_ctxt, createBoundCRS()->exportToWKT(WKTFormatter::create().get()).c_str(), - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); @@ -331,7 +445,7 @@ TEST_F(CApi, proj_obj_as_proj_string) { m_ctxt, GeographicCRS::EPSG_4326->exportToWKT(WKTFormatter::create().get()) .c_str(), - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); @@ -356,7 +470,7 @@ TEST_F(CApi, proj_obj_as_proj_string_incompatible_WKT1) { auto obj = proj_obj_create_from_wkt( m_ctxt, createBoundCRS()->exportToWKT(WKTFormatter::create().get()).c_str(), - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); @@ -449,7 +563,7 @@ TEST_F(CApi, proj_obj_crs_create_bound_crs_to_WGS84_on_invalid_type) { ->derivingConversion() ->exportToWKT(WKTFormatter::create().get()) .c_str(), - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); @@ -464,7 +578,7 @@ TEST_F(CApi, proj_obj_get_name) { m_ctxt, GeographicCRS::EPSG_4326->exportToWKT(WKTFormatter::create().get()) .c_str(), - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); auto name = proj_obj_get_name(obj); @@ -480,7 +594,7 @@ TEST_F(CApi, proj_obj_get_id_auth_name) { m_ctxt, GeographicCRS::EPSG_4326->exportToWKT(WKTFormatter::create().get()) .c_str(), - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); auto auth = proj_obj_get_id_auth_name(obj, 0); @@ -498,7 +612,7 @@ TEST_F(CApi, proj_obj_get_id_code) { m_ctxt, GeographicCRS::EPSG_4326->exportToWKT(WKTFormatter::create().get()) .c_str(), - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); auto code = proj_obj_get_id_code(obj, 0); @@ -517,7 +631,7 @@ TEST_F(CApi, proj_obj_get_type) { m_ctxt, GeographicCRS::EPSG_4326->exportToWKT(WKTFormatter::create().get()) .c_str(), - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); EXPECT_EQ(proj_obj_get_type(obj), PJ_OBJ_TYPE_GEOGRAPHIC_2D_CRS); @@ -527,7 +641,7 @@ TEST_F(CApi, proj_obj_get_type) { m_ctxt, GeographicCRS::EPSG_4979->exportToWKT(WKTFormatter::create().get()) .c_str(), - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); EXPECT_EQ(proj_obj_get_type(obj), PJ_OBJ_TYPE_GEOGRAPHIC_3D_CRS); @@ -537,7 +651,7 @@ TEST_F(CApi, proj_obj_get_type) { m_ctxt, GeographicCRS::EPSG_4978->exportToWKT(WKTFormatter::create().get()) .c_str(), - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); EXPECT_EQ(proj_obj_get_type(obj), PJ_OBJ_TYPE_GEOCENTRIC_CRS); @@ -547,7 +661,7 @@ TEST_F(CApi, proj_obj_get_type) { m_ctxt, GeographicCRS::EPSG_4326->datum() ->exportToWKT(WKTFormatter::create().get()) .c_str(), - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); EXPECT_EQ(proj_obj_get_type(obj), PJ_OBJ_TYPE_GEODETIC_REFERENCE_FRAME); @@ -557,7 +671,7 @@ TEST_F(CApi, proj_obj_get_type) { m_ctxt, GeographicCRS::EPSG_4326->ellipsoid() ->exportToWKT(WKTFormatter::create().get()) .c_str(), - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); EXPECT_EQ(proj_obj_get_type(obj), PJ_OBJ_TYPE_ELLIPSOID); @@ -567,7 +681,7 @@ TEST_F(CApi, proj_obj_get_type) { m_ctxt, createProjectedCRS() ->exportToWKT(WKTFormatter::create().get()) .c_str(), - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); EXPECT_EQ(proj_obj_get_type(obj), PJ_OBJ_TYPE_PROJECTED_CRS); @@ -577,7 +691,7 @@ TEST_F(CApi, proj_obj_get_type) { m_ctxt, createVerticalCRS() ->exportToWKT(WKTFormatter::create().get()) .c_str(), - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); EXPECT_EQ(proj_obj_get_type(obj), PJ_OBJ_TYPE_VERTICAL_CRS); @@ -588,7 +702,7 @@ TEST_F(CApi, proj_obj_get_type) { ->datum() ->exportToWKT(WKTFormatter::create().get()) .c_str(), - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); EXPECT_EQ(proj_obj_get_type(obj), PJ_OBJ_TYPE_VERTICAL_REFERENCE_FRAME); @@ -599,7 +713,7 @@ TEST_F(CApi, proj_obj_get_type) { ->derivingConversion() ->exportToWKT(WKTFormatter::create().get()) .c_str(), - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); EXPECT_EQ(proj_obj_get_type(obj), PJ_OBJ_TYPE_CONVERSION); @@ -608,7 +722,7 @@ TEST_F(CApi, proj_obj_get_type) { auto obj = proj_obj_create_from_wkt( m_ctxt, createBoundCRS()->exportToWKT(WKTFormatter::create().get()).c_str(), - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); EXPECT_EQ(proj_obj_get_type(obj), PJ_OBJ_TYPE_BOUND_CRS); @@ -619,14 +733,14 @@ TEST_F(CApi, proj_obj_get_type) { ->transformation() ->exportToWKT(WKTFormatter::create().get()) .c_str(), - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); EXPECT_EQ(proj_obj_get_type(obj), PJ_OBJ_TYPE_TRANSFORMATION); } { auto obj = proj_obj_create_from_wkt(m_ctxt, "AUTHORITY[\"EPSG\", 4326]", - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(obj); ASSERT_EQ(obj, nullptr); } @@ -703,7 +817,7 @@ TEST_F(CApi, proj_crs) { ->exportToWKT( WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()) .c_str(), - nullptr); + nullptr, nullptr, nullptr); ASSERT_NE(crs, nullptr); ObjectKeeper keeper(crs); EXPECT_TRUE(proj_obj_is_crs(crs)); @@ -772,7 +886,7 @@ TEST_F(CApi, proj_obj_get_prime_meridian) { ->exportToWKT( WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()) .c_str(), - nullptr); + nullptr, nullptr, nullptr); ASSERT_NE(crs, nullptr); ObjectKeeper keeper(crs); @@ -811,7 +925,7 @@ TEST_F(CApi, proj_crs_compound) { auto crs = proj_obj_create_from_wkt( m_ctxt, createCompoundCRS()->exportToWKT(WKTFormatter::create().get()).c_str(), - nullptr); + nullptr, nullptr, nullptr); ASSERT_NE(crs, nullptr); ObjectKeeper keeper(crs); EXPECT_EQ(proj_obj_get_type(crs), PJ_OBJ_TYPE_COMPOUND_CRS); @@ -837,7 +951,7 @@ TEST_F(CApi, proj_obj_get_source_target_crs_bound_crs) { auto crs = proj_obj_create_from_wkt( m_ctxt, createBoundCRS()->exportToWKT(WKTFormatter::create().get()).c_str(), - nullptr); + nullptr, nullptr, nullptr); ASSERT_NE(crs, nullptr); ObjectKeeper keeper(crs); @@ -860,7 +974,7 @@ TEST_F(CApi, proj_obj_get_source_target_crs_transformation) { ->transformation() ->exportToWKT(WKTFormatter::create().get()) .c_str(), - nullptr); + nullptr, nullptr, nullptr); ASSERT_NE(obj, nullptr); ObjectKeeper keeper(obj); @@ -881,7 +995,7 @@ TEST_F(CApi, proj_obj_get_source_crs_of_projected_crs) { auto crs = proj_obj_create_from_wkt( m_ctxt, createProjectedCRS()->exportToWKT(WKTFormatter::create().get()).c_str(), - nullptr); + nullptr, nullptr, nullptr); ASSERT_NE(crs, nullptr); ObjectKeeper keeper(crs); @@ -911,7 +1025,8 @@ TEST_F(CApi, proj_obj_get_source_target_crs_conversion_without_crs) { TEST_F(CApi, proj_obj_get_source_target_crs_invalid_object) { auto obj = proj_obj_create_from_wkt( - m_ctxt, "ELLIPSOID[\"WGS 84\",6378137,298.257223563]", nullptr); + m_ctxt, "ELLIPSOID[\"WGS 84\",6378137,298.257223563]", nullptr, nullptr, + nullptr); ASSERT_NE(obj, nullptr); ObjectKeeper keeper(obj); @@ -1089,7 +1204,7 @@ TEST_F(CApi, transformation_from_boundCRS) { auto crs = proj_obj_create_from_wkt( m_ctxt, createBoundCRS()->exportToWKT(WKTFormatter::create().get()).c_str(), - nullptr); + nullptr, nullptr, nullptr); ASSERT_NE(crs, nullptr); ObjectKeeper keeper(crs); @@ -1465,7 +1580,7 @@ TEST_F(CApi, proj_obj_identify) { m_ctxt, GeographicCRS::EPSG_4807->exportToWKT(WKTFormatter::create().get()) .c_str(), - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); { @@ -1486,7 +1601,7 @@ TEST_F(CApi, proj_obj_identify) { m_ctxt, Ellipsoid::GRS1980->exportToWKT(WKTFormatter::create().get()) .c_str(), - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeperEllps(objEllps); auto res = proj_obj_identify(m_ctxt, objEllps, nullptr, nullptr, nullptr); @@ -2245,7 +2360,7 @@ TEST_F(CApi, proj_obj_crs_alter_geodetic_crs) { auto projCRS = proj_obj_create_from_wkt( m_ctxt, createProjectedCRS()->exportToWKT(WKTFormatter::create().get()).c_str(), - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(projCRS); ASSERT_NE(projCRS, nullptr); @@ -2306,7 +2421,7 @@ TEST_F(CApi, proj_obj_crs_alter_cs_angular_unit) { m_ctxt, GeographicCRS::EPSG_4326->exportToWKT(WKTFormatter::create().get()) .c_str(), - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(crs); ASSERT_NE(crs, nullptr); @@ -2363,7 +2478,7 @@ TEST_F(CApi, proj_obj_crs_alter_cs_linear_unit) { auto crs = proj_obj_create_from_wkt( m_ctxt, createProjectedCRS()->exportToWKT(WKTFormatter::create().get()).c_str(), - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(crs); ASSERT_NE(crs, nullptr); @@ -2420,7 +2535,7 @@ TEST_F(CApi, proj_obj_crs_alter_parameters_linear_unit) { auto crs = proj_obj_create_from_wkt( m_ctxt, createProjectedCRS()->exportToWKT(WKTFormatter::create().get()).c_str(), - nullptr); + nullptr, nullptr, nullptr); ObjectKeeper keeper(crs); ASSERT_NE(crs, nullptr); diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp index a385fe87..8272cfb4 100644 --- a/test/unit/test_io.cpp +++ b/test/unit/test_io.cpp @@ -446,7 +446,7 @@ TEST(wkt_parse, wkt1_EPSG_4807_grad_mess) { " PRIMEM[\"Paris\",2.33722917,\n" " AUTHORITY[\"EPSG\",\"8903\"]],\n" " UNIT[\"grad\",0.015707963267949,\n" - " AUTHORITY[\"EPSG\",9105]],\n" + " AUTHORITY[\"EPSG\",\"9105\"]],\n" " AXIS[\"latitude\",NORTH],\n" " AXIS[\"longitude\",EAST],\n" " AUTHORITY[\"EPSG\",\"4807\"]]"); @@ -868,7 +868,7 @@ TEST(wkt_parse, wkt1_geocentric) { " PRIMEM[\"Greenwich\",0,\n" " AUTHORITY[\"EPSG\",\"8901\"]],\n" " UNIT[\"metre\",1,\n" - " AUTHORITY[\"EPSG\",9001]],\n" + " AUTHORITY[\"EPSG\",\"9001\"]],\n" " AXIS[\"Geocentric X\",OTHER],\n" " AXIS[\"Geocentric Y\",OTHER],\n" " AXIS[\"Geocentric Z\",NORTH],\n" @@ -890,7 +890,7 @@ TEST(wkt_parse, wkt1_geocentric_with_z_OTHER) { " PRIMEM[\"Greenwich\",0,\n" " AUTHORITY[\"EPSG\",\"8901\"]],\n" " UNIT[\"metre\",1,\n" - " AUTHORITY[\"EPSG\",9001]],\n" + " AUTHORITY[\"EPSG\",\"9001\"]],\n" " AXIS[\"Geocentric X\",OTHER],\n" " AXIS[\"Geocentric Y\",OTHER],\n" " AXIS[\"Geocentric Z\",OTHER],\n" @@ -1004,7 +1004,7 @@ TEST(wkt_parse, wkt1_projected) { " PRIMEM[\"Greenwich\",0,\n" " AUTHORITY[\"EPSG\",\"8901\"]],\n" " UNIT[\"degree\",0.0174532925199433,\n" - " AUTHORITY[\"EPSG\",9122]],\n" + " AUTHORITY[\"EPSG\",\"9122\"]],\n" " AXIS[\"latitude\",NORTH],\n" " AXIS[\"longitude\",EAST],\n" " AUTHORITY[\"EPSG\",\"4326\"]],\n" @@ -1015,7 +1015,7 @@ TEST(wkt_parse, wkt1_projected) { " PARAMETER[\"false_easting\",500000],\n" " PARAMETER[\"false_northing\",0],\n" " UNIT[\"metre\",1,\n" - " AUTHORITY[\"EPSG\",9001]],\n" + " AUTHORITY[\"EPSG\",\"9001\"]],\n" " AXIS[\"(E)\",EAST],\n" " AXIS[\"(N)\",NORTH],\n" " AUTHORITY[\"EPSG\",\"32631\"]]"; @@ -1037,7 +1037,7 @@ TEST(wkt_parse, wkt1_projected_no_axis) { " PRIMEM[\"Greenwich\",0,\n" " AUTHORITY[\"EPSG\",\"8901\"]],\n" " UNIT[\"degree\",0.0174532925199433,\n" - " AUTHORITY[\"EPSG\",9122]],\n" + " AUTHORITY[\"EPSG\",\"9122\"]],\n" " AXIS[\"latitude\",NORTH],\n" " AXIS[\"longitude\",EAST],\n" " AUTHORITY[\"EPSG\",\"4326\"]],\n" @@ -1048,7 +1048,7 @@ TEST(wkt_parse, wkt1_projected_no_axis) { " PARAMETER[\"false_easting\",500000],\n" " PARAMETER[\"false_northing\",0],\n" " UNIT[\"metre\",1,\n" - " AUTHORITY[\"EPSG\",9001]],\n" + " AUTHORITY[\"EPSG\",\"9001\"]],\n" " AUTHORITY[\"EPSG\",\"32631\"]]"; auto obj = WKTParser().createFromWKT(wkt); auto crs = nn_dynamic_pointer_cast(obj); @@ -1753,7 +1753,7 @@ TEST(wkt_parse, vertcrs_WKT1_GDAL) { " VERT_DATUM[\"Ordnance Datum Newlyn\",2005,\n" " AUTHORITY[\"EPSG\",\"5101\"]],\n" " UNIT[\"metre\",1,\n" - " AUTHORITY[\"EPSG\",9001]],\n" + " AUTHORITY[\"EPSG\",\"9001\"]],\n" " AXIS[\"gravity-related height\",UP],\n" " AUTHORITY[\"EPSG\",\"5701\"]]"; @@ -1922,7 +1922,7 @@ TEST(wkt_parse, COMPD_CS) { " PRIMEM[\"Greenwich\",0,\n" " AUTHORITY[\"EPSG\",\"8901\"]],\n" " UNIT[\"degree\",0.0174532925199433,\n" - " AUTHORITY[\"EPSG\",9122]],\n" + " AUTHORITY[\"EPSG\",\"9122\"]],\n" " AXIS[\"Latitude\",NORTH],\n" " AXIS[\"Longitude\",EAST],\n" " AUTHORITY[\"EPSG\",\"4326\"]],\n" @@ -1933,7 +1933,7 @@ TEST(wkt_parse, COMPD_CS) { " PARAMETER[\"false_easting\",500000],\n" " PARAMETER[\"false_northing\",0],\n" " UNIT[\"metre\",1,\n" - " AUTHORITY[\"EPSG\",9001]],\n" + " AUTHORITY[\"EPSG\",\"9001\"]],\n" " AXIS[\"Easting\",EAST],\n" " AXIS[\"Northing\",NORTH],\n" " AUTHORITY[\"EPSG\",\"32631\"]],\n" @@ -1941,7 +1941,7 @@ TEST(wkt_parse, COMPD_CS) { " VERT_DATUM[\"Ordnance Datum Newlyn\",2005,\n" " AUTHORITY[\"EPSG\",\"5101\"]],\n" " UNIT[\"metre\",1,\n" - " AUTHORITY[\"EPSG\",9001]],\n" + " AUTHORITY[\"EPSG\",\"9001\"]],\n" " AXIS[\"Gravity-related height\",UP],\n" " AUTHORITY[\"EPSG\",\"5701\"]],\n" " AUTHORITY[\"codespace\",\"code\"]]"); @@ -2213,7 +2213,7 @@ TEST(wkt_parse, projcs_TOWGS84_7terms) { " PRIMEM[\"Greenwich\",0,\n" " AUTHORITY[\"EPSG\",\"8901\"]],\n" " UNIT[\"degree\",0.0174532925199433,\n" - " AUTHORITY[\"EPSG\",9122]],\n" + " AUTHORITY[\"EPSG\",\"9122\"]],\n" " AXIS[\"Latitude\",NORTH],\n" " AXIS[\"Longitude\",EAST]],\n" " PROJECTION[\"Transverse_Mercator\"],\n" @@ -2223,7 +2223,7 @@ TEST(wkt_parse, projcs_TOWGS84_7terms) { " PARAMETER[\"false_easting\",500000],\n" " PARAMETER[\"false_northing\",0],\n" " UNIT[\"metre\",1,\n" - " AUTHORITY[\"EPSG\",9001]],\n" + " AUTHORITY[\"EPSG\",\"9001\"]],\n" " AXIS[\"Easting\",EAST],\n" " AXIS[\"Northing\",NORTH]]"; @@ -4607,7 +4607,7 @@ TEST(wkt_parse, invalid_PROJCS) { " AUTHORITY[\"EPSG\",\"6326\"]],\n" " PRIMEM[\"x\",0],\n" " UNIT[\"degree\",0.0174532925199433,\n" - " AUTHORITY[\"EPSG\",9122]],\n" + " AUTHORITY[\"EPSG\",\"9122\"]],\n" " AXIS[\"latitude\",NORTH],\n" " AXIS[\"longitude\",EAST],\n" " AUTHORITY[\"EPSG\",\"4326\"]]\n"); @@ -4627,17 +4627,17 @@ TEST(wkt_parse, invalid_PROJCS) { ParsingException); // not enough children in PARAMETER - EXPECT_THROW( - WKTParser().createFromWKT( - startWKT + - ",PROJECTION[\"x\"],PARAMETER[\"z\",\"foo\"],UNIT[\"metre\",1]]"), - ParsingException); + EXPECT_THROW(WKTParser().createFromWKT( + startWKT + + ",PROJECTION[\"x\"],PARAMETER[\"z\"],UNIT[\"metre\",1]]"), + ParsingException); EXPECT_NO_THROW(WKTParser().createFromWKT( - startWKT + ",PROJECTION[\"x\"],UNIT[\"metre\",1]]")); + startWKT + ",PROJECTION[\"x\"],PARAMETER[\"z\",1],UNIT[\"metre\",1]]")); // missing UNIT - EXPECT_THROW(WKTParser().createFromWKT(startWKT + ",PROJECTION[\"x\"]]"), + EXPECT_THROW(WKTParser().createFromWKT( + startWKT + ",PROJECTION[\"x\"],PARAMETER[\"z\",1]]"), ParsingException); } diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp index 905eecdb..636db7a7 100644 --- a/test/unit/test_operation.cpp +++ b/test/unit/test_operation.cpp @@ -3310,7 +3310,7 @@ TEST(operation, webmerc_import_from_broken_esri_WGS_84_Pseudo_Mercator) { "PARAMETER[\"false_northing\",0],UNIT[\"Meter\",1]," "PARAMETER[\"standard_parallel_1\",0.0]]"; - auto obj = WKTParser().createFromWKT(wkt1); + auto obj = WKTParser().setStrict(false).createFromWKT(wkt1); auto crs = nn_dynamic_pointer_cast(obj); ASSERT_TRUE(crs != nullptr); @@ -3620,7 +3620,7 @@ TEST(operation, wkt1_import_polar_stereographic_variantA) { " PARAMETER[\"central_meridian\",2],\n" " PARAMETER[\"scale_factor\",3],\n" " PARAMETER[\"false_easting\",4],\n" - " PARAMETER[\"false_northing\",5]" + " PARAMETER[\"false_northing\",5],\n" " UNIT[\"metre\",1]]"; auto obj = WKTParser().createFromWKT(wkt); auto crs = nn_dynamic_pointer_cast(obj); @@ -3649,7 +3649,7 @@ TEST(operation, wkt1_import_polar_stereographic_variantB) { " PARAMETER[\"central_meridian\",2],\n" " PARAMETER[\"scale_factor\",1],\n" " PARAMETER[\"false_easting\",4],\n" - " PARAMETER[\"false_northing\",5]" + " PARAMETER[\"false_northing\",5],\n" " UNIT[\"metre\",1]]"; auto obj = WKTParser().createFromWKT(wkt); auto crs = nn_dynamic_pointer_cast(obj); @@ -3678,7 +3678,7 @@ TEST(operation, wkt1_import_polar_stereographic_ambiguous) { " PARAMETER[\"central_meridian\",2],\n" " PARAMETER[\"scale_factor\",3],\n" " PARAMETER[\"false_easting\",4],\n" - " PARAMETER[\"false_northing\",5]" + " PARAMETER[\"false_northing\",5],\n" " UNIT[\"metre\",1]]"; auto obj = WKTParser().createFromWKT(wkt); auto crs = nn_dynamic_pointer_cast(obj); @@ -6827,9 +6827,9 @@ TEST(operation, conversion_missing_parameter) { " PARAMETER[\"false_easting\",500000]," " UNIT[\"metre\",1," " AUTHORITY[\"EPSG\",\"9001\"]]," - " AUTHORITY[\"EPSG\",\"2038\"]," " AXIS[\"Easting\",EAST]," - " AXIS[\"Northing\",NORTH]]"; + " AXIS[\"Northing\",NORTH]," + " AUTHORITY[\"EPSG\",\"2038\"]]"; auto obj1 = WKTParser().createFromWKT(wkt1); auto crs1 = nn_dynamic_pointer_cast(obj1); ASSERT_TRUE(crs1 != nullptr); @@ -6854,9 +6854,9 @@ TEST(operation, conversion_missing_parameter) { " PARAMETER[\"false_northing\",0]," " UNIT[\"metre\",1," " AUTHORITY[\"EPSG\",\"9001\"]]," - " AUTHORITY[\"EPSG\",\"2038\"]," " AXIS[\"Easting\",EAST]," - " AXIS[\"Northing\",NORTH]]"; + " AXIS[\"Northing\",NORTH]," + " AUTHORITY[\"EPSG\",\"2038\"]]"; auto obj2 = WKTParser().createFromWKT(wkt2); auto crs2 = nn_dynamic_pointer_cast(obj2); ASSERT_TRUE(crs2 != nullptr); @@ -6881,9 +6881,9 @@ TEST(operation, conversion_missing_parameter) { " PARAMETER[\"false_northing\",0]," " UNIT[\"metre\",1," " AUTHORITY[\"EPSG\",\"9001\"]]," - " AUTHORITY[\"EPSG\",\"2038\"]," " AXIS[\"Easting\",EAST]," - " AXIS[\"Northing\",NORTH]]"; + " AXIS[\"Northing\",NORTH]," + " AUTHORITY[\"EPSG\",\"2038\"]]"; auto obj3 = WKTParser().createFromWKT(wkt3); auto crs3 = nn_dynamic_pointer_cast(obj3); ASSERT_TRUE(crs3 != nullptr); @@ -6909,9 +6909,9 @@ TEST(operation, conversion_missing_parameter) { " PARAMETER[\"UNKNOWN\",13]," " UNIT[\"metre\",1," " AUTHORITY[\"EPSG\",\"9001\"]]," - " AUTHORITY[\"EPSG\",\"2038\"]," " AXIS[\"Easting\",EAST]," - " AXIS[\"Northing\",NORTH]]"; + " AXIS[\"Northing\",NORTH]," + " AUTHORITY[\"EPSG\",\"2038\"]]"; auto obj4 = WKTParser().createFromWKT(wkt4); auto crs4 = nn_dynamic_pointer_cast(obj4); ASSERT_TRUE(crs4 != nullptr); @@ -6936,9 +6936,9 @@ TEST(operation, conversion_missing_parameter) { " PARAMETER[\"false_northing\",-99999]," " UNIT[\"metre\",1," " AUTHORITY[\"EPSG\",\"9001\"]]," - " AUTHORITY[\"EPSG\",\"2038\"]," " AXIS[\"Easting\",EAST]," - " AXIS[\"Northing\",NORTH]]"; + " AXIS[\"Northing\",NORTH]," + " AUTHORITY[\"EPSG\",\"2038\"]]"; auto obj5 = WKTParser().createFromWKT(wkt5); auto crs5 = nn_dynamic_pointer_cast(obj5); ASSERT_TRUE(crs5 != nullptr); diff --git a/travis/csa/install.sh b/travis/csa/install.sh index 4df403e0..301aead9 100755 --- a/travis/csa/install.sh +++ b/travis/csa/install.sh @@ -8,4 +8,4 @@ set -e CXXFLAGS="-std=c++11" ./clang+llvm-6.0.0-x86_64-linux-gnu-ubuntu-14.04/bin/scan-build -o scanbuildoutput -plist -v ./configure ./clang+llvm-6.0.0-x86_64-linux-gnu-ubuntu-14.04/bin/scan-build -o scanbuildoutput -plist -v make -j3 -if grep -r "\.c" scanbuildoutput | grep "" | grep -v ""; then echo "error" && /bin/false; else echo "ok"; fi +if grep -r "\.c" scanbuildoutput | grep "" | grep -v -e "" -e _generated_parser; then echo "error" && /bin/false; else echo "ok"; fi -- cgit v1.2.3