%{

/* WKT Parser */
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include "lwin_wkt.h"
#include "lwin_wkt_parse.h"
#include "lwgeom_log.h"


/* Prototypes to quiet the compiler */
int wkt_yyparse(void);
void wkt_yyerror(const char *str);
int wkt_yylex(void);


/* Declare the global parser variable */
LWGEOM_PARSER_RESULT global_parser_result;

/* Turn on/off verbose parsing (turn off for production) */
int wkt_yydebug = 0;

/*
* Error handler called by the bison parser. Mostly we will be
* catching our own errors and filling out the message and errlocation
* from WKT_ERROR in the grammar, but we keep this one
* around just in case.
*/
void wkt_yyerror(__attribute__((__unused__)) const char *str)
{
	/* If we haven't already set a message and location, let's set one now. */
	if ( ! global_parser_result.message )
	{
		global_parser_result.message = parser_error_messages[PARSER_ERROR_OTHER];
		global_parser_result.errcode = PARSER_ERROR_OTHER;
		global_parser_result.errlocation = wkt_yylloc.last_column;
	}
	LWDEBUGF(4,"%s", str);
}

/**
* Parse a WKT geometry string into an LWGEOM structure. Note that this
* process uses globals and is not re-entrant, so don't call it within itself
* (eg, from within other functions in lwin_wkt.c) or from a threaded program.
* Note that parser_result.wkinput picks up a reference to wktstr.
*/
int lwgeom_parse_wkt(LWGEOM_PARSER_RESULT *parser_result, const char *wktstr, int parser_check_flags)
{
	int parse_rv = 0;

	/* Clean up our global parser result. */
	lwgeom_parser_result_init(&global_parser_result);
	/* Work-around possible bug in GNU Bison 3.0.2 resulting in wkt_yylloc
	 * members not being initialized on yyparse() as documented here:
	 * https://www.gnu.org/software/bison/manual/html_node/Location-Type.html
	 * See discussion here:
	 * http://lists.osgeo.org/pipermail/postgis-devel/2014-September/024506.html
	 */
	wkt_yylloc.last_column = wkt_yylloc.last_line = \
		wkt_yylloc.first_column = wkt_yylloc.first_line = 1;

	/* Set the input text string, and parse checks. */
	global_parser_result.wkinput = wktstr;
	global_parser_result.parser_check_flags = parser_check_flags;

	wkt_lexer_init(wktstr); /* Lexer ready */
	parse_rv = wkt_yyparse(); /* Run the parse */
	LWDEBUGF(4,"wkt_yyparse returned %d", parse_rv);
	wkt_lexer_close(); /* Clean up lexer */

	/* A non-zero parser return is an error. */
	if ( parse_rv || global_parser_result.errcode )
	{
		if( ! global_parser_result.errcode )
		{
			global_parser_result.errcode = PARSER_ERROR_OTHER;
			global_parser_result.message = parser_error_messages[PARSER_ERROR_OTHER];
			global_parser_result.errlocation = wkt_yylloc.last_column;
		}
		else if (global_parser_result.geom)
		{
			lwgeom_free(global_parser_result.geom);
			global_parser_result.geom = NULL;
		}

		LWDEBUGF(5, "error returned by wkt_yyparse() @ %d: [%d] '%s'",
		            global_parser_result.errlocation,
		            global_parser_result.errcode,
		            global_parser_result.message);

		/* Copy the global values into the return pointer */
		*parser_result = global_parser_result;
		wkt_yylex_destroy();
		return LW_FAILURE;
	}

	/* Copy the global value into the return pointer */
	*parser_result = global_parser_result;
	wkt_yylex_destroy();
	return LW_SUCCESS;
}

#define SET_PARSER_ERROR(parser_errcode) \
    do { \
        global_parser_result.errcode = (parser_errcode); \
        global_parser_result.message = parser_error_messages[(parser_errcode)]; \
        global_parser_result.errlocation = wkt_yylloc.last_column; \
    } while(0)

#define WKT_ERROR() { if ( global_parser_result.errcode != 0 ) { YYERROR; } }

struct WKT_NURBS_CONTROLPOINTS
{
	POINTARRAY *points;
	POINTARRAY *weights;
};

static void
wkt_parser_nurbs_controlpoints_free(WKT_NURBS_CONTROLPOINTS *controlpoints);

static WKT_NURBS_CONTROLPOINTS *
wkt_parser_nurbs_controlpoints_new(POINT point, double weight, char *dimensionality)
{
	WKT_NURBS_CONTROLPOINTS *controlpoints;
	lwflags_t flags = 0;

	if (!isfinite(weight) || weight <= 0.0)
	{
		SET_PARSER_ERROR(PARSER_ERROR_OTHER);
		return NULL;
	}

	controlpoints = lwalloc(sizeof(WKT_NURBS_CONTROLPOINTS));
	controlpoints->points = NULL;
	controlpoints->weights = NULL;
	controlpoints->points = wkt_parser_ptarray_new(point);
	controlpoints->weights = wkt_parser_ptarray_new(wkt_parser_coord_2(weight, 0));
	if (dimensionality)
	{
		int hasz;
		int hasm;
		int ndims;

		for (size_t i = 0; i < strlen(dimensionality); i++)
		{
			if (dimensionality[i] == 'Z' || dimensionality[i] == 'z')
				FLAGS_SET_Z(flags, LW_TRUE);
			else if (dimensionality[i] == 'M' || dimensionality[i] == 'm')
				FLAGS_SET_M(flags, LW_TRUE);
		}

		hasz = FLAGS_GET_Z(flags);
		hasm = FLAGS_GET_M(flags);
		ndims = 2 + hasz + hasm;
		if (FLAGS_NDIMS(controlpoints->points->flags) != ndims)
		{
			wkt_parser_nurbs_controlpoints_free(controlpoints);
			SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS);
			return NULL;
		}
		FLAGS_SET_Z(controlpoints->points->flags, hasz);
		FLAGS_SET_M(controlpoints->points->flags, hasm);
	}
	return controlpoints;
}

static void
wkt_parser_nurbs_controlpoints_free(WKT_NURBS_CONTROLPOINTS *controlpoints)
{
	if (!controlpoints)
		return;
	if (controlpoints->points)
		ptarray_free(controlpoints->points);
	if (controlpoints->weights)
		ptarray_free(controlpoints->weights);
	lwfree(controlpoints);
}

static WKT_NURBS_CONTROLPOINTS *
wkt_parser_nurbs_controlpoints_add(WKT_NURBS_CONTROLPOINTS *controlpoints, WKT_NURBS_CONTROLPOINTS *next)
{
	POINT4D point;
	POINT4D weight;

	if (!controlpoints || !next)
	{
		wkt_parser_nurbs_controlpoints_free(controlpoints);
		wkt_parser_nurbs_controlpoints_free(next);
		SET_PARSER_ERROR(PARSER_ERROR_OTHER);
		return NULL;
	}

	if (FLAGS_GET_ZM(controlpoints->points->flags) != FLAGS_GET_ZM(next->points->flags))
	{
		wkt_parser_nurbs_controlpoints_free(controlpoints);
		wkt_parser_nurbs_controlpoints_free(next);
		SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS);
		return NULL;
	}

	getPoint4d_p(next->points, 0, &point);
	getPoint4d_p(next->weights, 0, &weight);
	ptarray_append_point(controlpoints->points, &point, LW_TRUE);
	ptarray_append_point(controlpoints->weights, &weight, LW_TRUE);
	wkt_parser_nurbs_controlpoints_free(next);
	return controlpoints;
}

static char *
wkt_parser_nurbscurve_iso_dimensionality(char *outer, char *controlpoints)
{
	if (outer && controlpoints && strcasecmp(outer, controlpoints) != 0)
	{
		SET_PARSER_ERROR(PARSER_ERROR_MIXDIMS);
		return NULL;
	}

	return outer ? outer : controlpoints;
}

static POINTARRAY *
wkt_parser_knot_list_add_repeated(POINTARRAY *knots, double value, double multiplicity)
{
	uint32_t repeat_count;

	if (!isfinite(value) || !isfinite(multiplicity) || multiplicity < 1.0 ||
	    fabs(multiplicity - round(multiplicity)) >= 1e-10)
	{
		if (knots)
			ptarray_free(knots);
		SET_PARSER_ERROR(PARSER_ERROR_OTHER);
		return NULL;
	}

	repeat_count = (uint32_t)round(multiplicity);
	for (uint32_t i = 0; i < repeat_count; i++)
	{
		POINT knot = wkt_parser_coord_2(value, 0);
		knots = knots ? wkt_parser_ptarray_add_coord(knots, knot) : wkt_parser_ptarray_new(knot);
		if (!knots)
			return NULL;
	}
	return knots;
}

%}

%code requires {
typedef struct WKT_NURBS_CONTROLPOINTS WKT_NURBS_CONTROLPOINTS;
}

%locations

%union {
	int integervalue;
	double doublevalue;
	char *stringvalue;
	LWGEOM *geometryvalue;
	POINT coordinatevalue;
	POINTARRAY *ptarrayvalue;
	WKT_NURBS_CONTROLPOINTS *nurbscontrolpointsvalue;
}

%token POINT_TOK LINESTRING_TOK POLYGON_TOK
%token MPOINT_TOK MLINESTRING_TOK MPOLYGON_TOK
%token MSURFACE_TOK MCURVE_TOK CURVEPOLYGON_TOK COMPOUNDCURVE_TOK CIRCULARSTRING_TOK
%token COLLECTION_TOK
%token RBRACKET_TOK LBRACKET_TOK COMMA_TOK EMPTY_TOK
%token SEMICOLON_TOK
%token TRIANGLE_TOK TIN_TOK
%token POLYHEDRALSURFACE_TOK
%token NURBSCURVE_TOK
%token DEGREE_TOK CONTROLPOINTS_TOK KNOTS_TOK NURBSPOINT_TOK
%token WEIGHTEDPOINT_TOK WEIGHT_TOK KNOT_TOK

%token <doublevalue> DOUBLE_TOK
%token <stringvalue> DIMENSIONALITY_TOK
%token <integervalue> SRID_TOK

%type <ptarrayvalue> ring
%type <ptarrayvalue> patchring
%type <ptarrayvalue> ptarray
%type <coordinatevalue> coordinate
%type <geometryvalue> circularstring
%type <geometryvalue> compoundcurve
%type <geometryvalue> compound_list
%type <geometryvalue> curve_list
%type <ptarrayvalue> weight_list
%type <ptarrayvalue> knot_list
%type <geometryvalue> curvepolygon
%type <geometryvalue> curvering
%type <geometryvalue> curvering_list
%type <geometryvalue> geometry
%type <geometryvalue> geometry_no_srid
%type <geometryvalue> geometry_list
%type <geometryvalue> geometrycollection
%type <geometryvalue> linestring
%type <geometryvalue> linestring_list
%type <geometryvalue> linestring_untagged
%type <geometryvalue> multicurve
%type <geometryvalue> multilinestring
%type <geometryvalue> multipoint
%type <geometryvalue> multipolygon
%type <geometryvalue> multisurface
%type <geometryvalue> patch
%type <geometryvalue> patch_list
%type <geometryvalue> patchring_list
%type <geometryvalue> point
%type <geometryvalue> point_list
%type <geometryvalue> point_untagged
%type <geometryvalue> polygon
%type <geometryvalue> polygon_list
%type <geometryvalue> polygon_untagged
%type <geometryvalue> polyhedralsurface
%type <geometryvalue> ring_list
%type <geometryvalue> surface_list
%type <geometryvalue> tin
%type <geometryvalue> triangle
%type <geometryvalue> triangle_list
%type <geometryvalue> triangle_untagged
%type <geometryvalue> nurbscurve
%type <stringvalue> opt_dimensionality
%type <nurbscontrolpointsvalue> iso_controlpoint
%type <nurbscontrolpointsvalue> iso_controlpoint_list
%type <ptarrayvalue> iso_knot_list

/* These clean up memory on errors and parser aborts. */
%destructor { ptarray_free($$); } ptarray
%destructor { ptarray_free($$); } ring
%destructor { ptarray_free($$); } patchring
%destructor { ptarray_free($$); } weight_list
%destructor { ptarray_free($$); } knot_list
%destructor { ptarray_free($$); } iso_knot_list
%destructor { wkt_parser_nurbs_controlpoints_free($$); } iso_controlpoint
%destructor { wkt_parser_nurbs_controlpoints_free($$); } iso_controlpoint_list
%destructor { lwgeom_free($$); } curvering_list
%destructor { lwgeom_free($$); } triangle_list
%destructor { lwgeom_free($$); } surface_list
%destructor { lwgeom_free($$); } polygon_list
%destructor { lwgeom_free($$); } patch_list
%destructor { lwgeom_free($$); } point_list
%destructor { lwgeom_free($$); } linestring_list
%destructor { lwgeom_free($$); } curve_list
%destructor { lwgeom_free($$); } compound_list
%destructor { lwgeom_free($$); } ring_list
%destructor { lwgeom_free($$); } patchring_list
%destructor { lwgeom_free($$); } circularstring
%destructor { lwgeom_free($$); } compoundcurve
%destructor { lwgeom_free($$); } curvepolygon
%destructor { lwgeom_free($$); } curvering
%destructor { lwgeom_free($$); } geometry_no_srid
%destructor { lwgeom_free($$); } geometrycollection
%destructor { lwgeom_free($$); } geometry_list
%destructor { lwgeom_free($$); } linestring
%destructor { lwgeom_free($$); } linestring_untagged
%destructor { lwgeom_free($$); } multicurve
%destructor { lwgeom_free($$); } multilinestring
%destructor { lwgeom_free($$); } multipoint
%destructor { lwgeom_free($$); } multipolygon
%destructor { lwgeom_free($$); } multisurface
%destructor { lwgeom_free($$); } point
%destructor { lwgeom_free($$); } point_untagged
%destructor { lwgeom_free($$); } polygon
%destructor { lwgeom_free($$); } patch
%destructor { lwgeom_free($$); } polygon_untagged
%destructor { lwgeom_free($$); } polyhedralsurface
%destructor { lwgeom_free($$); } nurbscurve
%destructor { lwgeom_free($$); } tin
%destructor { lwgeom_free($$); } triangle
%destructor { lwgeom_free($$); } triangle_untagged

%%

geometry:
	geometry_no_srid
		{ wkt_parser_geometry_new($1, SRID_UNKNOWN); WKT_ERROR(); } |
	SRID_TOK SEMICOLON_TOK geometry_no_srid
		{ wkt_parser_geometry_new($3, $1); WKT_ERROR(); } ;

geometry_no_srid :
	point { $$ = $1; } |
	linestring { $$ = $1; } |
	circularstring { $$ = $1; } |
	compoundcurve { $$ = $1; } |
	polygon { $$ = $1; } |
	curvepolygon { $$ = $1; } |
	multipoint { $$ = $1; } |
	multilinestring { $$ = $1; } |
	multipolygon { $$ = $1; } |
	multisurface { $$ = $1; } |
	multicurve { $$ = $1; } |
	tin { $$ = $1; } |
	polyhedralsurface { $$ = $1; } |
	triangle { $$ = $1; } |
	nurbscurve { $$ = $1; } |
	geometrycollection { $$ = $1; } ;

geometrycollection :
	COLLECTION_TOK LBRACKET_TOK geometry_list RBRACKET_TOK
		{ $$ = wkt_parser_collection_finalize(COLLECTIONTYPE, $3, NULL); WKT_ERROR(); } |
	COLLECTION_TOK DIMENSIONALITY_TOK LBRACKET_TOK geometry_list RBRACKET_TOK
		{ $$ = wkt_parser_collection_finalize(COLLECTIONTYPE, $4, $2); WKT_ERROR(); } |
	COLLECTION_TOK DIMENSIONALITY_TOK EMPTY_TOK
		{ $$ = wkt_parser_collection_finalize(COLLECTIONTYPE, NULL, $2); WKT_ERROR(); } |
	COLLECTION_TOK EMPTY_TOK
		{ $$ = wkt_parser_collection_finalize(COLLECTIONTYPE, NULL, NULL); WKT_ERROR(); } ;

geometry_list :
	geometry_list COMMA_TOK geometry_no_srid
		{ $$ = wkt_parser_collection_add_geom($1,$3); WKT_ERROR(); } |
	geometry_no_srid
		{ $$ = wkt_parser_collection_new($1); WKT_ERROR(); } ;

multisurface :
	MSURFACE_TOK LBRACKET_TOK surface_list RBRACKET_TOK
		{ $$ = wkt_parser_collection_finalize(MULTISURFACETYPE, $3, NULL); WKT_ERROR(); } |
	MSURFACE_TOK DIMENSIONALITY_TOK LBRACKET_TOK surface_list RBRACKET_TOK
		{ $$ = wkt_parser_collection_finalize(MULTISURFACETYPE, $4, $2); WKT_ERROR(); } |
	MSURFACE_TOK DIMENSIONALITY_TOK EMPTY_TOK
		{ $$ = wkt_parser_collection_finalize(MULTISURFACETYPE, NULL, $2); WKT_ERROR(); } |
	MSURFACE_TOK EMPTY_TOK
		{ $$ = wkt_parser_collection_finalize(MULTISURFACETYPE, NULL, NULL); WKT_ERROR(); } ;

surface_list :
	surface_list COMMA_TOK polygon
		{ $$ = wkt_parser_collection_add_geom($1,$3); WKT_ERROR(); } |
	surface_list COMMA_TOK curvepolygon
		{ $$ = wkt_parser_collection_add_geom($1,$3); WKT_ERROR(); } |
	surface_list COMMA_TOK polygon_untagged
		{ $$ = wkt_parser_collection_add_geom($1,$3); WKT_ERROR(); } |
	polygon
		{ $$ = wkt_parser_collection_new($1); WKT_ERROR(); } |
	curvepolygon
		{ $$ = wkt_parser_collection_new($1); WKT_ERROR(); } |
	polygon_untagged
		{ $$ = wkt_parser_collection_new($1); WKT_ERROR(); } ;

tin :
	TIN_TOK LBRACKET_TOK triangle_list RBRACKET_TOK
		{ $$ = wkt_parser_collection_finalize(TINTYPE, $3, NULL); WKT_ERROR(); } |
	TIN_TOK DIMENSIONALITY_TOK LBRACKET_TOK triangle_list RBRACKET_TOK
		{ $$ = wkt_parser_collection_finalize(TINTYPE, $4, $2); WKT_ERROR(); } |
	TIN_TOK DIMENSIONALITY_TOK EMPTY_TOK
		{ $$ = wkt_parser_collection_finalize(TINTYPE, NULL, $2); WKT_ERROR(); } |
	TIN_TOK EMPTY_TOK
		{ $$ = wkt_parser_collection_finalize(TINTYPE, NULL, NULL); WKT_ERROR(); } ;

polyhedralsurface :
	POLYHEDRALSURFACE_TOK LBRACKET_TOK patch_list RBRACKET_TOK
		{ $$ = wkt_parser_collection_finalize(POLYHEDRALSURFACETYPE, $3, NULL); WKT_ERROR(); } |
	POLYHEDRALSURFACE_TOK DIMENSIONALITY_TOK LBRACKET_TOK patch_list RBRACKET_TOK
		{ $$ = wkt_parser_collection_finalize(POLYHEDRALSURFACETYPE, $4, $2); WKT_ERROR(); } |
	POLYHEDRALSURFACE_TOK DIMENSIONALITY_TOK EMPTY_TOK
		{ $$ = wkt_parser_collection_finalize(POLYHEDRALSURFACETYPE, NULL, $2); WKT_ERROR(); } |
	POLYHEDRALSURFACE_TOK EMPTY_TOK
		{ $$ = wkt_parser_collection_finalize(POLYHEDRALSURFACETYPE, NULL, NULL); WKT_ERROR(); } ;

multipolygon :
	MPOLYGON_TOK LBRACKET_TOK polygon_list RBRACKET_TOK
		{ $$ = wkt_parser_collection_finalize(MULTIPOLYGONTYPE, $3, NULL); WKT_ERROR(); } |
	MPOLYGON_TOK DIMENSIONALITY_TOK LBRACKET_TOK polygon_list RBRACKET_TOK
		{ $$ = wkt_parser_collection_finalize(MULTIPOLYGONTYPE, $4, $2); WKT_ERROR(); } |
	MPOLYGON_TOK DIMENSIONALITY_TOK EMPTY_TOK
		{ $$ = wkt_parser_collection_finalize(MULTIPOLYGONTYPE, NULL, $2); WKT_ERROR(); } |
	MPOLYGON_TOK EMPTY_TOK
		{ $$ = wkt_parser_collection_finalize(MULTIPOLYGONTYPE, NULL, NULL); WKT_ERROR(); } ;

polygon_list :
	polygon_list COMMA_TOK polygon_untagged
		{ $$ = wkt_parser_collection_add_geom($1,$3); WKT_ERROR(); } |
	polygon_untagged
		{ $$ = wkt_parser_collection_new($1); WKT_ERROR(); } ;

patch_list :
	patch_list COMMA_TOK patch
		{ $$ = wkt_parser_collection_add_geom($1,$3); WKT_ERROR(); } |
	patch
		{ $$ = wkt_parser_collection_new($1); WKT_ERROR(); } ;

polygon :
	POLYGON_TOK LBRACKET_TOK ring_list RBRACKET_TOK
		{ $$ = wkt_parser_polygon_finalize($3, NULL); WKT_ERROR(); } |
	POLYGON_TOK DIMENSIONALITY_TOK LBRACKET_TOK ring_list RBRACKET_TOK
		{ $$ = wkt_parser_polygon_finalize($4, $2); WKT_ERROR(); } |
	POLYGON_TOK DIMENSIONALITY_TOK EMPTY_TOK
		{ $$ = wkt_parser_polygon_finalize(NULL, $2); WKT_ERROR(); } |
	POLYGON_TOK EMPTY_TOK
		{ $$ = wkt_parser_polygon_finalize(NULL, NULL); WKT_ERROR(); } ;

polygon_untagged :
	LBRACKET_TOK ring_list RBRACKET_TOK
		{ $$ = $2; } |
	EMPTY_TOK
		{ $$ = wkt_parser_polygon_finalize(NULL, NULL); WKT_ERROR(); };

patch :
	LBRACKET_TOK patchring_list RBRACKET_TOK { $$ = $2; } ;

curvepolygon :
	CURVEPOLYGON_TOK LBRACKET_TOK curvering_list RBRACKET_TOK
		{ $$ = wkt_parser_curvepolygon_finalize($3, NULL); WKT_ERROR(); } |
	CURVEPOLYGON_TOK DIMENSIONALITY_TOK LBRACKET_TOK curvering_list RBRACKET_TOK
		{ $$ = wkt_parser_curvepolygon_finalize($4, $2); WKT_ERROR(); } |
	CURVEPOLYGON_TOK DIMENSIONALITY_TOK EMPTY_TOK
		{ $$ = wkt_parser_curvepolygon_finalize(NULL, $2); WKT_ERROR(); } |
	CURVEPOLYGON_TOK EMPTY_TOK
		{ $$ = wkt_parser_curvepolygon_finalize(NULL, NULL); WKT_ERROR(); } ;

curvering_list :
	curvering_list COMMA_TOK curvering
		{ $$ = wkt_parser_curvepolygon_add_ring($1,$3); WKT_ERROR(); } |
	curvering
		{ $$ = wkt_parser_curvepolygon_new($1); WKT_ERROR(); } ;

curvering :
	linestring_untagged { $$ = $1; } |
	linestring { $$ = $1; } |
	compoundcurve { $$ = $1; } |
	nurbscurve { $$ = $1; } |
	circularstring { $$ = $1; } ;

patchring_list :
	patchring_list COMMA_TOK patchring
		{ $$ = wkt_parser_polygon_add_ring($1,$3,'Z'); WKT_ERROR(); } |
	patchring
		{ $$ = wkt_parser_polygon_new($1,'Z'); WKT_ERROR(); } ;

ring_list :
	ring_list COMMA_TOK ring
		{ $$ = wkt_parser_polygon_add_ring($1,$3,'2'); WKT_ERROR(); } |
	ring
		{ $$ = wkt_parser_polygon_new($1,'2'); WKT_ERROR(); } ;

patchring :
	LBRACKET_TOK ptarray RBRACKET_TOK { $$ = $2; } ;

ring :
	LBRACKET_TOK ptarray RBRACKET_TOK { $$ = $2; } ;

compoundcurve :
	COMPOUNDCURVE_TOK LBRACKET_TOK compound_list RBRACKET_TOK
		{ $$ = wkt_parser_compound_finalize($3, NULL); WKT_ERROR(); } |
	COMPOUNDCURVE_TOK DIMENSIONALITY_TOK LBRACKET_TOK compound_list RBRACKET_TOK
		{ $$ = wkt_parser_compound_finalize($4, $2); WKT_ERROR(); } |
	COMPOUNDCURVE_TOK DIMENSIONALITY_TOK EMPTY_TOK
		{ $$ = wkt_parser_compound_finalize(NULL, $2); WKT_ERROR(); } |
	COMPOUNDCURVE_TOK EMPTY_TOK
		{ $$ = wkt_parser_compound_finalize(NULL, NULL); WKT_ERROR(); } ;

compound_list :
	compound_list COMMA_TOK circularstring
		{ $$ = wkt_parser_compound_add_geom($1,$3); WKT_ERROR(); } |
	compound_list COMMA_TOK nurbscurve
		{ $$ = wkt_parser_compound_add_geom($1,$3); WKT_ERROR(); } |
	compound_list COMMA_TOK linestring
		{ $$ = wkt_parser_compound_add_geom($1,$3); WKT_ERROR(); } |
	compound_list COMMA_TOK linestring_untagged
		{ $$ = wkt_parser_compound_add_geom($1,$3); WKT_ERROR(); } |
	circularstring
		{ $$ = wkt_parser_compound_new($1); WKT_ERROR(); } |
	nurbscurve
		{ $$ = wkt_parser_compound_new($1); WKT_ERROR(); } |
	linestring
		{ $$ = wkt_parser_compound_new($1); WKT_ERROR(); } |
	linestring_untagged
		{ $$ = wkt_parser_compound_new($1); WKT_ERROR(); } ;

multicurve :
	MCURVE_TOK LBRACKET_TOK curve_list RBRACKET_TOK
		{ $$ = wkt_parser_collection_finalize(MULTICURVETYPE, $3, NULL); WKT_ERROR(); } |
	MCURVE_TOK DIMENSIONALITY_TOK LBRACKET_TOK curve_list RBRACKET_TOK
		{ $$ = wkt_parser_collection_finalize(MULTICURVETYPE, $4, $2); WKT_ERROR(); } |
	MCURVE_TOK DIMENSIONALITY_TOK EMPTY_TOK
		{ $$ = wkt_parser_collection_finalize(MULTICURVETYPE, NULL, $2); WKT_ERROR(); } |
	MCURVE_TOK EMPTY_TOK
		{ $$ = wkt_parser_collection_finalize(MULTICURVETYPE, NULL, NULL); WKT_ERROR(); } ;

curve_list :
	curve_list COMMA_TOK circularstring
		{ $$ = wkt_parser_collection_add_geom($1,$3); WKT_ERROR(); } |
	curve_list COMMA_TOK compoundcurve
		{ $$ = wkt_parser_collection_add_geom($1,$3); WKT_ERROR(); } |
	curve_list COMMA_TOK nurbscurve
		{ $$ = wkt_parser_collection_add_geom($1,$3); WKT_ERROR(); } |
	curve_list COMMA_TOK linestring
		{ $$ = wkt_parser_collection_add_geom($1,$3); WKT_ERROR(); } |
	curve_list COMMA_TOK linestring_untagged
		{ $$ = wkt_parser_collection_add_geom($1,$3); WKT_ERROR(); } |
	circularstring
		{ $$ = wkt_parser_collection_new($1); WKT_ERROR(); } |
	compoundcurve
		{ $$ = wkt_parser_collection_new($1); WKT_ERROR(); } |
	nurbscurve
		{ $$ = wkt_parser_collection_new($1); WKT_ERROR(); } |
	linestring
		{ $$ = wkt_parser_collection_new($1); WKT_ERROR(); } |
	linestring_untagged
		{ $$ = wkt_parser_collection_new($1); WKT_ERROR(); } ;

multilinestring :
	MLINESTRING_TOK LBRACKET_TOK linestring_list RBRACKET_TOK
		{ $$ = wkt_parser_collection_finalize(MULTILINETYPE, $3, NULL); WKT_ERROR(); } |
	MLINESTRING_TOK DIMENSIONALITY_TOK LBRACKET_TOK linestring_list RBRACKET_TOK
		{ $$ = wkt_parser_collection_finalize(MULTILINETYPE, $4, $2); WKT_ERROR(); } |
	MLINESTRING_TOK DIMENSIONALITY_TOK EMPTY_TOK
		{ $$ = wkt_parser_collection_finalize(MULTILINETYPE, NULL, $2); WKT_ERROR(); } |
	MLINESTRING_TOK EMPTY_TOK
		{ $$ = wkt_parser_collection_finalize(MULTILINETYPE, NULL, NULL); WKT_ERROR(); } ;

linestring_list :
	linestring_list COMMA_TOK linestring_untagged
		{ $$ = wkt_parser_collection_add_geom($1,$3); WKT_ERROR(); } |
	linestring_untagged
		{ $$ = wkt_parser_collection_new($1); WKT_ERROR(); } ;

circularstring :
	CIRCULARSTRING_TOK LBRACKET_TOK ptarray RBRACKET_TOK
		{ $$ = wkt_parser_circularstring_new($3, NULL); WKT_ERROR(); } |
	CIRCULARSTRING_TOK DIMENSIONALITY_TOK LBRACKET_TOK ptarray RBRACKET_TOK
		{ $$ = wkt_parser_circularstring_new($4, $2); WKT_ERROR(); } |
	CIRCULARSTRING_TOK DIMENSIONALITY_TOK EMPTY_TOK
		{ $$ = wkt_parser_circularstring_new(NULL, $2); WKT_ERROR(); } |
	CIRCULARSTRING_TOK EMPTY_TOK
		{ $$ = wkt_parser_circularstring_new(NULL, NULL); WKT_ERROR(); } ;

linestring :
	LINESTRING_TOK LBRACKET_TOK ptarray RBRACKET_TOK
		{ $$ = wkt_parser_linestring_new($3, NULL); WKT_ERROR(); } |
	LINESTRING_TOK DIMENSIONALITY_TOK LBRACKET_TOK ptarray RBRACKET_TOK
		{ $$ = wkt_parser_linestring_new($4, $2); WKT_ERROR(); } |
	LINESTRING_TOK DIMENSIONALITY_TOK EMPTY_TOK
		{ $$ = wkt_parser_linestring_new(NULL, $2); WKT_ERROR(); } |
	LINESTRING_TOK EMPTY_TOK
		{ $$ = wkt_parser_linestring_new(NULL, NULL); WKT_ERROR(); } ;

linestring_untagged :
	LBRACKET_TOK ptarray RBRACKET_TOK
		{ $$ = wkt_parser_linestring_new($2, NULL); WKT_ERROR(); } |
	EMPTY_TOK
		{ $$ = wkt_parser_linestring_new(NULL, NULL); WKT_ERROR(); };

triangle_list :
	triangle_list COMMA_TOK triangle_untagged
		{ $$ = wkt_parser_collection_add_geom($1,$3); WKT_ERROR(); } |
	triangle_untagged
		{ $$ = wkt_parser_collection_new($1); WKT_ERROR(); } ;

triangle :
	TRIANGLE_TOK LBRACKET_TOK LBRACKET_TOK ptarray RBRACKET_TOK RBRACKET_TOK
		{ $$ = wkt_parser_triangle_new($4, NULL); WKT_ERROR(); } |
	TRIANGLE_TOK DIMENSIONALITY_TOK LBRACKET_TOK LBRACKET_TOK ptarray RBRACKET_TOK RBRACKET_TOK
		{ $$ = wkt_parser_triangle_new($5, $2); WKT_ERROR(); } |
	TRIANGLE_TOK DIMENSIONALITY_TOK EMPTY_TOK
		{ $$ = wkt_parser_triangle_new(NULL, $2); WKT_ERROR(); } |
	TRIANGLE_TOK EMPTY_TOK
		{ $$ = wkt_parser_triangle_new(NULL, NULL); WKT_ERROR(); } ;

triangle_untagged :
	LBRACKET_TOK LBRACKET_TOK ptarray RBRACKET_TOK RBRACKET_TOK
		{ $$ = wkt_parser_triangle_new($3, NULL); WKT_ERROR(); } ;

multipoint :
	MPOINT_TOK LBRACKET_TOK point_list RBRACKET_TOK
		{ $$ = wkt_parser_collection_finalize(MULTIPOINTTYPE, $3, NULL); WKT_ERROR(); } |
	MPOINT_TOK DIMENSIONALITY_TOK LBRACKET_TOK point_list RBRACKET_TOK
		{ $$ = wkt_parser_collection_finalize(MULTIPOINTTYPE, $4, $2); WKT_ERROR(); } |
	MPOINT_TOK DIMENSIONALITY_TOK EMPTY_TOK
		{ $$ = wkt_parser_collection_finalize(MULTIPOINTTYPE, NULL, $2); WKT_ERROR(); } |
	MPOINT_TOK EMPTY_TOK
		{ $$ = wkt_parser_collection_finalize(MULTIPOINTTYPE, NULL, NULL); WKT_ERROR(); } ;

point_list :
	point_list COMMA_TOK point_untagged
		{ $$ = wkt_parser_collection_add_geom($1,$3); WKT_ERROR(); } |
	point_untagged
		{ $$ = wkt_parser_collection_new($1); WKT_ERROR(); } ;

point_untagged :
	coordinate
		{ $$ = wkt_parser_point_new(wkt_parser_ptarray_new($1),NULL); WKT_ERROR(); } |
	LBRACKET_TOK coordinate RBRACKET_TOK
		{ $$ = wkt_parser_point_new(wkt_parser_ptarray_new($2),NULL); WKT_ERROR(); } |
	EMPTY_TOK
		{ $$ = wkt_parser_point_new(NULL, NULL); WKT_ERROR(); };

point :
	POINT_TOK LBRACKET_TOK ptarray RBRACKET_TOK
		{ $$ = wkt_parser_point_new($3, NULL); WKT_ERROR(); } |
	POINT_TOK DIMENSIONALITY_TOK LBRACKET_TOK ptarray RBRACKET_TOK
		{ $$ = wkt_parser_point_new($4, $2); WKT_ERROR(); } |
	POINT_TOK DIMENSIONALITY_TOK EMPTY_TOK
		{ $$ = wkt_parser_point_new(NULL, $2); WKT_ERROR(); } |
	POINT_TOK EMPTY_TOK
		{ $$ = wkt_parser_point_new(NULL,NULL); WKT_ERROR(); } ;

ptarray :
	ptarray COMMA_TOK coordinate
		{ $$ = wkt_parser_ptarray_add_coord($1, $3); WKT_ERROR(); } |
	coordinate
		{ $$ = wkt_parser_ptarray_new($1); WKT_ERROR(); } ;

coordinate :
	DOUBLE_TOK DOUBLE_TOK
		{ $$ = wkt_parser_coord_2($1, $2); WKT_ERROR(); } |
	DOUBLE_TOK DOUBLE_TOK DOUBLE_TOK
		{ $$ = wkt_parser_coord_3($1, $2, $3); WKT_ERROR(); } |
	DOUBLE_TOK DOUBLE_TOK DOUBLE_TOK DOUBLE_TOK
		{ $$ = wkt_parser_coord_4($1, $2, $3, $4); WKT_ERROR(); } ;

opt_dimensionality :
	DIMENSIONALITY_TOK
		{ $$ = $1; } |
		{ $$ = NULL; } ;

nurbscurve :
	/* ISO/IEC SQL/MM syntax */
	NURBSCURVE_TOK LBRACKET_TOK DEGREE_TOK DOUBLE_TOK COMMA_TOK CONTROLPOINTS_TOK opt_dimensionality LBRACKET_TOK iso_controlpoint_list RBRACKET_TOK COMMA_TOK KNOTS_TOK LBRACKET_TOK iso_knot_list RBRACKET_TOK RBRACKET_TOK
			{
				$$ = wkt_parser_nurbscurve_new($4, $9->points, $9->weights, $14,
					wkt_parser_nurbscurve_iso_dimensionality(NULL, $7));
				$9->points = NULL;
				$9->weights = NULL;
				wkt_parser_nurbs_controlpoints_free($9);
			WKT_ERROR();
		} |
	NURBSCURVE_TOK DIMENSIONALITY_TOK LBRACKET_TOK DEGREE_TOK DOUBLE_TOK COMMA_TOK CONTROLPOINTS_TOK opt_dimensionality LBRACKET_TOK iso_controlpoint_list RBRACKET_TOK COMMA_TOK KNOTS_TOK LBRACKET_TOK iso_knot_list RBRACKET_TOK RBRACKET_TOK
			{
				$$ = wkt_parser_nurbscurve_new($5, $10->points, $10->weights, $15,
					wkt_parser_nurbscurve_iso_dimensionality($2, $8));
				$10->points = NULL;
				$10->weights = NULL;
				wkt_parser_nurbs_controlpoints_free($10);
			WKT_ERROR();
		} |
	/* Full syntax: NURBSCURVE(degree, (points), (weights), (knots)) */
	NURBSCURVE_TOK LBRACKET_TOK DOUBLE_TOK COMMA_TOK LBRACKET_TOK ptarray RBRACKET_TOK COMMA_TOK LBRACKET_TOK weight_list RBRACKET_TOK COMMA_TOK LBRACKET_TOK knot_list RBRACKET_TOK RBRACKET_TOK
		{ $$ = wkt_parser_nurbscurve_new($3, $6, $10, $14, NULL); WKT_ERROR(); } |
	NURBSCURVE_TOK DIMENSIONALITY_TOK LBRACKET_TOK DOUBLE_TOK COMMA_TOK LBRACKET_TOK ptarray RBRACKET_TOK COMMA_TOK LBRACKET_TOK weight_list RBRACKET_TOK COMMA_TOK LBRACKET_TOK knot_list RBRACKET_TOK RBRACKET_TOK
		{ $$ = wkt_parser_nurbscurve_new($4, $7, $11, $15, $2); WKT_ERROR(); } |
	/* With weights only: NURBSCURVE(degree, (points), (weights)) */
	NURBSCURVE_TOK LBRACKET_TOK DOUBLE_TOK COMMA_TOK LBRACKET_TOK ptarray RBRACKET_TOK COMMA_TOK LBRACKET_TOK weight_list RBRACKET_TOK RBRACKET_TOK
		{ $$ = wkt_parser_nurbscurve_new($3, $6, $10, NULL, NULL); WKT_ERROR(); } |
	NURBSCURVE_TOK DIMENSIONALITY_TOK LBRACKET_TOK DOUBLE_TOK COMMA_TOK LBRACKET_TOK ptarray RBRACKET_TOK COMMA_TOK LBRACKET_TOK weight_list RBRACKET_TOK RBRACKET_TOK
		{ $$ = wkt_parser_nurbscurve_new($4, $7, $11, NULL, $2); WKT_ERROR(); } |
	/* With degree only: NURBSCURVE(degree, (points)) */
	NURBSCURVE_TOK LBRACKET_TOK DOUBLE_TOK COMMA_TOK LBRACKET_TOK ptarray RBRACKET_TOK RBRACKET_TOK
		{ $$ = wkt_parser_nurbscurve_new($3, $6, NULL, NULL, NULL); WKT_ERROR(); } |
	NURBSCURVE_TOK DIMENSIONALITY_TOK LBRACKET_TOK DOUBLE_TOK COMMA_TOK LBRACKET_TOK ptarray RBRACKET_TOK RBRACKET_TOK
		{ $$ = wkt_parser_nurbscurve_new($4, $7, NULL, NULL, $2); WKT_ERROR(); } |


	NURBSCURVE_TOK DIMENSIONALITY_TOK EMPTY_TOK
		{ $$ = wkt_parser_nurbscurve_empty($2); WKT_ERROR(); } |
	NURBSCURVE_TOK EMPTY_TOK
		{ $$ = wkt_parser_nurbscurve_empty(NULL); WKT_ERROR(); }
	;

iso_controlpoint :
	NURBSPOINT_TOK LBRACKET_TOK WEIGHTEDPOINT_TOK opt_dimensionality LBRACKET_TOK coordinate RBRACKET_TOK COMMA_TOK WEIGHT_TOK DOUBLE_TOK RBRACKET_TOK
		{ $$ = wkt_parser_nurbs_controlpoints_new($6, $10, $4); WKT_ERROR(); }
	;

iso_controlpoint_list :
	iso_controlpoint_list COMMA_TOK iso_controlpoint
		{ $$ = wkt_parser_nurbs_controlpoints_add($1, $3); WKT_ERROR(); } |
	iso_controlpoint
		{ $$ = $1; }
	;

iso_knot_list :
	iso_knot_list COMMA_TOK KNOT_TOK LBRACKET_TOK DOUBLE_TOK COMMA_TOK DOUBLE_TOK RBRACKET_TOK
		{ $$ = wkt_parser_knot_list_add_repeated($1, $5, $7); WKT_ERROR(); } |
	KNOT_TOK LBRACKET_TOK DOUBLE_TOK COMMA_TOK DOUBLE_TOK RBRACKET_TOK
		{ $$ = wkt_parser_knot_list_add_repeated(NULL, $3, $5); WKT_ERROR(); }
	;

weight_list :
	weight_list COMMA_TOK DOUBLE_TOK
		{
			$$ = wkt_parser_ptarray_add_coord($1, wkt_parser_coord_2($3, 0));
			WKT_ERROR();
		}
	| DOUBLE_TOK
		{
			$$ = wkt_parser_ptarray_new(wkt_parser_coord_2($1, 0));
			WKT_ERROR();
		}
	;

knot_list :
	knot_list COMMA_TOK DOUBLE_TOK
		{
			$$ = wkt_parser_ptarray_add_coord($1, wkt_parser_coord_2($3, 0));
			WKT_ERROR();
		}
	| DOUBLE_TOK
		{
			$$ = wkt_parser_ptarray_new(wkt_parser_coord_2($1, 0));
			WKT_ERROR();
		}
	;
