Coverage Report

Created: 2026-08-14 06:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PROJ/src/wkt2_parser.cpp
Line
Count
Source
1
/******************************************************************************
2
 * Project:  PROJ
3
 * Purpose:  WKT2 parser grammar
4
 * Author:   Even Rouault, <even.rouault at spatialys.com>
5
 *
6
 ******************************************************************************
7
 * Copyright (c) 2018 Even Rouault, <even.rouault at spatialys.com>
8
 *
9
 * Permission is hereby granted, free of charge, to any person obtaining a
10
 * copy of this software and associated documentation files (the "Software"),
11
 * to deal in the Software without restriction, including without limitation
12
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13
 * and/or sell copies of the Software, and to permit persons to whom the
14
 * Software is furnished to do so, subject to the following conditions:
15
 *
16
 * The above copyright notice and this permission notice shall be included
17
 * in all copies or substantial portions of the Software.
18
 *
19
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25
 * DEALINGS IN THE SOFTWARE.
26
 ****************************************************************************/
27
28
#ifndef FROM_PROJ_CPP
29
#define FROM_PROJ_CPP
30
#endif
31
32
#include "proj/internal/internal.hpp"
33
34
#include <algorithm>
35
#include <cctype>
36
#include <cstring>
37
#include <string>
38
39
#include "proj_constants.h"
40
#include "wkt2_parser.h"
41
#include "wkt_parser.hpp"
42
43
using namespace NS_PROJ::internal;
44
45
//! @cond Doxygen_Suppress
46
47
// ---------------------------------------------------------------------------
48
49
struct pj_wkt2_parse_context : public pj_wkt_parse_context {};
50
51
// ---------------------------------------------------------------------------
52
53
505
void pj_wkt2_error(pj_wkt2_parse_context *context, const char *msg) {
54
505
    pj_wkt_error(context, msg);
55
505
}
56
57
// ---------------------------------------------------------------------------
58
59
569
std::string pj_wkt2_parse(const std::string &wkt) {
60
569
    pj_wkt2_parse_context context;
61
569
    context.pszInput = wkt.c_str();
62
569
    context.pszLastSuccess = wkt.c_str();
63
569
    context.pszNext = wkt.c_str();
64
569
    if (pj_wkt2_parse(&context) != 0) {
65
505
        return context.errorMsg;
66
505
    }
67
64
    return std::string();
68
569
}
69
70
// ---------------------------------------------------------------------------
71
72
typedef struct {
73
    const char *pszToken;
74
    int nTokenVal;
75
} wkt2_tokens;
76
77
#define PAIR(X)                                                                \
78
    {                                                                          \
79
#X, T_##X                                                              \
80
    }
81
82
static const wkt2_tokens tokens[] = {
83
    PAIR(PARAMETER),
84
    PAIR(PROJECTION),
85
    PAIR(DATUM),
86
    PAIR(SPHEROID),
87
    PAIR(PRIMEM),
88
    PAIR(UNIT),
89
    PAIR(AXIS),
90
91
    PAIR(GEODCRS),
92
    PAIR(LENGTHUNIT),
93
    PAIR(ANGLEUNIT),
94
    PAIR(SCALEUNIT),
95
    PAIR(TIMEUNIT),
96
    PAIR(ELLIPSOID),
97
    PAIR(CS),
98
    PAIR(ID),
99
    PAIR(PROJCRS),
100
    PAIR(BASEGEODCRS),
101
    PAIR(MERIDIAN),
102
    PAIR(BEARING),
103
    PAIR(ORDER),
104
    PAIR(ANCHOR),
105
    PAIR(ANCHOREPOCH),
106
    PAIR(CONVERSION),
107
    PAIR(METHOD),
108
    PAIR(REMARK),
109
    PAIR(GEOGCRS),
110
    PAIR(BASEGEOGCRS),
111
    PAIR(SCOPE),
112
    PAIR(AREA),
113
    PAIR(BBOX),
114
    PAIR(CITATION),
115
    PAIR(URI),
116
    PAIR(VERTCRS),
117
    PAIR(VDATUM),
118
    PAIR(GEOIDMODEL),
119
    PAIR(COMPOUNDCRS),
120
    PAIR(PARAMETERFILE),
121
    PAIR(COORDINATEOPERATION),
122
    PAIR(SOURCECRS),
123
    PAIR(TARGETCRS),
124
    PAIR(INTERPOLATIONCRS),
125
    PAIR(OPERATIONACCURACY),
126
    PAIR(CONCATENATEDOPERATION),
127
    PAIR(STEP),
128
    PAIR(BOUNDCRS),
129
    PAIR(ABRIDGEDTRANSFORMATION),
130
    PAIR(DERIVINGCONVERSION),
131
    PAIR(TDATUM),
132
    PAIR(CALENDAR),
133
    PAIR(TIMEORIGIN),
134
    PAIR(TIMECRS),
135
    PAIR(VERTICALEXTENT),
136
    PAIR(TIMEEXTENT),
137
    PAIR(USAGE),
138
    PAIR(DYNAMIC),
139
    PAIR(FRAMEEPOCH),
140
    PAIR(MODEL),
141
    PAIR(VELOCITYGRID),
142
    PAIR(ENSEMBLE),
143
    PAIR(MEMBER),
144
    PAIR(ENSEMBLEACCURACY),
145
    PAIR(DERIVEDPROJCRS),
146
    PAIR(BASEPROJCRS),
147
    PAIR(EDATUM),
148
    PAIR(ENGCRS),
149
    PAIR(PDATUM),
150
    PAIR(PARAMETRICCRS),
151
    PAIR(PARAMETRICUNIT),
152
    PAIR(BASEVERTCRS),
153
    PAIR(BASEENGCRS),
154
    PAIR(BASEPARAMCRS),
155
    PAIR(BASETIMECRS),
156
    PAIR(GEODETICCRS),
157
    PAIR(GEODETICDATUM),
158
    PAIR(PROJECTEDCRS),
159
    PAIR(PRIMEMERIDIAN),
160
    PAIR(GEOGRAPHICCRS),
161
    PAIR(TRF),
162
    PAIR(VERTICALCRS),
163
    PAIR(VERTICALDATUM),
164
    PAIR(VRF),
165
    PAIR(TIMEDATUM),
166
    PAIR(TEMPORALQUANTITY),
167
    PAIR(ENGINEERINGDATUM),
168
    PAIR(ENGINEERINGCRS),
169
    PAIR(PARAMETRICDATUM),
170
    PAIR(EPOCH),
171
    PAIR(COORDEPOCH),
172
    PAIR(COORDINATEMETADATA),
173
    PAIR(POINTMOTIONOPERATION),
174
    PAIR(VERSION),
175
    PAIR(AXISMINVALUE),
176
    PAIR(AXISMAXVALUE),
177
    PAIR(RANGEMEANING),
178
    PAIR(exact),
179
    PAIR(wraparound),
180
    PAIR(DEFININGTRANSFORMATION),
181
182
    // CS types
183
    PAIR(AFFINE),
184
    PAIR(CARTESIAN),
185
    PAIR(CYLINDRICAL),
186
    PAIR(ELLIPSOIDAL),
187
    PAIR(LINEAR),
188
    PAIR(PARAMETRIC),
189
    PAIR(POLAR),
190
    PAIR(SPHERICAL),
191
    PAIR(VERTICAL),
192
    PAIR(TEMPORAL),
193
    PAIR(TEMPORALCOUNT),
194
    PAIR(TEMPORALMEASURE),
195
    PAIR(ORDINAL),
196
    PAIR(TEMPORALDATETIME),
197
198
    // Axis directions
199
    PAIR(NORTH),
200
    PAIR(NORTHNORTHEAST),
201
    PAIR(NORTHEAST),
202
    PAIR(EASTNORTHEAST),
203
    PAIR(EAST),
204
    PAIR(EASTSOUTHEAST),
205
    PAIR(SOUTHEAST),
206
    PAIR(SOUTHSOUTHEAST),
207
    PAIR(SOUTH),
208
    PAIR(SOUTHSOUTHWEST),
209
    PAIR(SOUTHWEST),
210
    PAIR(WESTSOUTHWEST),
211
    PAIR(WEST),
212
    PAIR(WESTNORTHWEST),
213
    PAIR(NORTHWEST),
214
    PAIR(NORTHNORTHWEST),
215
    PAIR(UP),
216
    PAIR(DOWN),
217
    PAIR(GEOCENTRICX),
218
    PAIR(GEOCENTRICY),
219
    PAIR(GEOCENTRICZ),
220
    PAIR(COLUMNPOSITIVE),
221
    PAIR(COLUMNNEGATIVE),
222
    PAIR(ROWPOSITIVE),
223
    PAIR(ROWNEGATIVE),
224
    PAIR(DISPLAYRIGHT),
225
    PAIR(DISPLAYLEFT),
226
    PAIR(DISPLAYUP),
227
    PAIR(DISPLAYDOWN),
228
    PAIR(FORWARD),
229
    PAIR(AFT),
230
    PAIR(PORT),
231
    PAIR(STARBOARD),
232
    PAIR(CLOCKWISE),
233
    PAIR(COUNTERCLOCKWISE),
234
    PAIR(TOWARDS),
235
    PAIR(AWAYFROM),
236
    PAIR(FUTURE),
237
    PAIR(PAST),
238
    PAIR(UNSPECIFIED),
239
};
240
241
// ---------------------------------------------------------------------------
242
243
5.36k
int pj_wkt2_lex(YYSTYPE * /*pNode */, pj_wkt2_parse_context *context) {
244
5.36k
    size_t i;
245
5.36k
    const char *pszInput = context->pszNext;
246
247
    /* -------------------------------------------------------------------- */
248
    /*      Skip white space.                                               */
249
    /* -------------------------------------------------------------------- */
250
5.74k
    while (*pszInput == ' ' || *pszInput == '\t' || *pszInput == 10 ||
251
5.57k
           *pszInput == 13)
252
372
        pszInput++;
253
254
5.36k
    context->pszLastSuccess = pszInput;
255
256
5.36k
    if (*pszInput == '\0') {
257
61
        context->pszNext = pszInput;
258
61
        return EOF;
259
61
    }
260
261
    /* -------------------------------------------------------------------- */
262
    /*      Recognize node names.                                           */
263
    /* -------------------------------------------------------------------- */
264
5.30k
    if (isalpha(*pszInput)) {
265
96.9k
        for (i = 0; i < sizeof(tokens) / sizeof(tokens[0]); i++) {
266
96.4k
            if (ci_starts_with(pszInput, tokens[i].pszToken) &&
267
1.15k
                !isalpha(pszInput[strlen(tokens[i].pszToken)])) {
268
1.14k
                context->pszNext = pszInput + strlen(tokens[i].pszToken);
269
1.14k
                return tokens[i].nTokenVal;
270
1.14k
            }
271
96.4k
        }
272
1.59k
    }
273
274
    /* -------------------------------------------------------------------- */
275
    /*      Recognize unsigned integer                                      */
276
    /* -------------------------------------------------------------------- */
277
278
4.16k
    if (*pszInput >= '0' && *pszInput <= '9') {
279
280
        // Special case for 1, 2, 3
281
767
        if ((*pszInput == '1' || *pszInput == '2' || *pszInput == '3') &&
282
519
            !(pszInput[1] >= '0' && pszInput[1] <= '9')) {
283
405
            context->pszNext = pszInput + 1;
284
405
            return *pszInput;
285
405
        }
286
287
362
        pszInput++;
288
1.99k
        while (*pszInput >= '0' && *pszInput <= '9')
289
1.63k
            pszInput++;
290
291
362
        context->pszNext = pszInput;
292
293
362
        return T_UNSIGNED_INTEGER_DIFFERENT_ONE_TWO_THREE;
294
767
    }
295
296
    /* -------------------------------------------------------------------- */
297
    /*      Recognize double quoted strings.                                */
298
    /* -------------------------------------------------------------------- */
299
3.40k
    if (*pszInput == '"') {
300
542
        pszInput++;
301
9.81k
        while (*pszInput != '\0') {
302
9.81k
            if (*pszInput == '"') {
303
665
                if (pszInput[1] == '"')
304
126
                    pszInput++;
305
539
                else
306
539
                    break;
307
665
            }
308
9.27k
            pszInput++;
309
9.27k
        }
310
542
        if (*pszInput == '\0') {
311
3
            context->pszNext = pszInput;
312
3
            return EOF;
313
3
        }
314
539
        context->pszNext = pszInput + 1;
315
539
        return T_STRING;
316
542
    }
317
318
    // As used in examples of OGC 12-063r5
319
2.85k
    const char *startPrintedQuote = "\xE2\x80\x9C";
320
2.85k
    const char *endPrintedQuote = "\xE2\x80\x9D";
321
2.85k
    if (strncmp(pszInput, startPrintedQuote, 3) == 0) {
322
5
        context->pszNext = strstr(pszInput, endPrintedQuote);
323
5
        if (context->pszNext == nullptr) {
324
0
            context->pszNext = pszInput + strlen(pszInput);
325
0
            return EOF;
326
0
        }
327
5
        context->pszNext += 3;
328
5
        return T_STRING;
329
5
    }
330
331
    /* -------------------------------------------------------------------- */
332
    /*      Handle special tokens.                                          */
333
    /* -------------------------------------------------------------------- */
334
2.85k
    context->pszNext = pszInput + 1;
335
2.85k
    return *pszInput;
336
2.85k
}
337
338
//! @endcond