%{
/* MeTA1 configuration parser for Grecs.
   Copyright (C) 2007-2025 Sergey Poznyakoff

   Grecs 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.

   Grecs 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 Grecs. If not, see <http://www.gnu.org/licenses/>. */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <errno.h>
#include <string.h>
#include "grecs.h"

%}

%define api.prefix {grecs_meta1_}
%define api.pure full
%parse-param { struct parser_control *pctl } { void *yyscanner }
%lex-param { yyscan_t yyscanner }
%code requires {
typedef void *yyscan_t;
struct parser_control {
	struct grecs_node *parse_tree;
	struct grecs_locus_point point;
	struct grecs_txtacc *acc;
};
#define GRECS_META1_LTYPE grecs_locus_t
}
%code provides {
int grecs_meta1_lex(GRECS_META1_STYPE *lvalp, GRECS_META1_LTYPE *llocp,
		    yyscan_t yyscanner);

struct grecs_node *grecs_meta1_parser(const char *name, int traceflags);
yyscan_t grecs_meta1_new_scanner(struct parser_control *pctl, int debug);
int grecs_meta1_new_source(yyscan_t scanner, const char *name);
void grecs_meta1_close_scanner(yyscan_t scanner);
void grecs_meta1_error (GRECS_META1_LTYPE const *llocp,
			struct parser_control *pctl, yyscan_t scanner,
			char const *msg);
}
%define parse.error verbose
%locations

%union {
	char *string;
	grecs_value_t svalue, *pvalue;
	struct grecs_list *list;
	struct { struct grecs_node *head, *tail; } node_list;
	struct grecs_node *node;
}

%token <string> META1_STRING META1_IDENT
%type <node> stmt simple block maybe_stmtlist
%type <node_list> stmtlist
%type <pvalue> tag value
%type <string> string slist
%type <list> slist0
%type <list> values list 
%%

input   : maybe_stmtlist
          {
		  pctl->parse_tree = grecs_node_create(grecs_node_root, &@1);
		  pctl->parse_tree->v.texttab = grecs_text_table();
		  grecs_node_bind(pctl->parse_tree, $1, 1);
	  }
        ;

maybe_stmtlist:
          /* empty */
          {
		  $$ = NULL;
	  }
        | stmtlist
	  {
		  $$ = $1.head;
	  }
        ;

stmtlist: stmt
          {
		  $$.head = $$.tail = $1;
	  }
        | stmtlist stmt
          {
		  grecs_node_bind($1.tail, $2, 0);
	  }
        ;

stmt    : simple
        | block
        ;

simple  : META1_IDENT '=' value opt_sc
          {
		  $$ = grecs_node_create_points(grecs_node_stmt,
						@1.beg, @3.end);
		  $$->ident = $1;
		  $$->idloc = @1;
		  $$->v.value = $3;
	  }
        ;

block   : META1_IDENT tag '{' stmtlist '}' opt_sc
          {
		  $$ = grecs_node_create_points(grecs_node_block,
						@1.beg, @5.end);
		  $$->ident = $1;
		  $$->idloc = @1;
		  $$->v.value = $2;
		  grecs_node_bind($$, $4.head, 1);
	  }
        ;

tag     : /* empty */
          {
		  $$ = NULL;
	  }
        | META1_IDENT
	  {
		  $$ = grecs_malloc(sizeof($$[0]));
		  $$->type = GRECS_TYPE_STRING;
		  $$->v.string = $1;
	  }		  
	;

value   : string
          {
		  $$ = grecs_malloc(sizeof($$[0]));
		  $$->type = GRECS_TYPE_STRING;
		  $$->locus = @1;
		  $$->v.string = $1;
	  }
        | list
          {
		  $$ = grecs_malloc(sizeof($$[0]));
		  $$->type = GRECS_TYPE_LIST;
		  $$->locus = @1;
		  $$->v.list = $1;
	  }
        ;

string  : META1_IDENT
        | slist
        ;

slist   : slist0
          {
		  struct grecs_list_entry *ep;
		  
		  grecs_line_begin(&pctl->acc);
		  for (ep = $1->head; ep; ep = ep->next) {
			  grecs_line_add(pctl->acc, ep->data,
					 strlen(ep->data));
			  free(ep->data);
			  ep->data = NULL;
		  }
		  $$ = grecs_line_finish(pctl->acc);
		  grecs_list_free($1);
	  }		  

slist0  : META1_STRING 
          {
		  $$ = grecs_list_create();
		  grecs_list_append($$, $1);
	  }
        | slist0 META1_STRING
          {
		  grecs_list_append($1, $2);
		  $$ = $1;
	  }
        ;

list    : '{' values '}'
          {
		  $$ = $2;
	  }
        | '{' values ',' '}'
          {
		  $$ = $2;
	  }
        ;

values  : value
          {
		  $$ = grecs_value_list_create();
		  grecs_list_append($$, $1);
	  }
        | values ',' value
          {
		  grecs_list_append($1, $3);
		  $$ = $1;
	  }
        ;

opt_sc  : /* empty */
        | ';'
        ;
	  
%%
void
grecs_meta1_error(GRECS_META1_LTYPE const *llocp,
		  struct parser_control *pctl, yyscan_t scanner,
		  char const *msg)
{
	grecs_error(llocp, 0, "%s", msg);
}

struct grecs_node *
grecs_meta1_parser(const char *name, int traceflags)
{
	int rc = 1;
	void *scanner;
	struct parser_control ctl;

	scanner = grecs_meta1_new_scanner(&ctl, traceflags & GRECS_TRACE_LEX);
	if (!scanner)
		return NULL;
	if (grecs_meta1_new_source(scanner, name) == 0) {
		yydebug = traceflags & GRECS_TRACE_GRAM;
		rc = yyparse(&ctl, scanner);
		if (grecs_error_count)
			rc = 1;
	}
	grecs_meta1_close_scanner(scanner);
	if (rc) {
		grecs_tree_free(ctl.parse_tree);
		ctl.parse_tree = NULL;
	}
	return ctl.parse_tree;
}
	
