/* SPDX-License-Identifier: GPL-3.0-only */

/*
 * Provide more verbose and specific error messages instead of just "syntax
 * error".
 */
%define parse.error verbose

/*
 * Verbose error messages may contain incorrect information if LAC (Lookahead
 * Correction) is not enabled.
 */
%define parse.lac full

/* Avoid symbol clashes (lopsub might also expose yy* symbols). */
%define api.prefix {txp_yy}

/*
 * Although locations are automatically enabled as soon as the grammar uses the
 * special @N tokens, specifying %locations explicitly allows for more accurate
 * syntax error messages.
 */
%locations

/*
 * Generate a pure (reentrant) parser. With this option enabled, yylval and
 * yylloc become local variables in yyparse(), and a different calling
 * convention is used for yylex().
 */
%define api.pure full

/* Additional arguments to yylex(), yyparse() and yyerror() */
%param {struct txp_context *ctx}
%param {struct txp_ast_node **ast}
%param {txp_yyscan_t yyscanner} /* reentrant lexers */

%{
#include "tf.h"
#include "txp.bison.h"

int yylex(TXP_YYSTYPE *lvalp, TXP_YYLTYPE *llocp, struct txp_context *ctx,
		struct txp_ast_node **ast, txp_yyscan_t yyscanner);
static void yyerror(YYLTYPE *llocp, struct txp_context *ctx,
		struct txp_ast_node **ast, txp_yyscan_t yyscanner, const char *msg);

%}

%union {
	struct txp_ast_node *node;
}

/* terminals */
%token <node> NUM
%token <node> STRING_LITERAL
%token <node> REGEX_PATTERN

/* keywords with semantic value */
%token <node> LEN
%token <node> FALSE TRUE
%token <node> TEXT

/* keywords without semantic value */
%token TAG

/* operators, ordered by precendence */
%left OR
%left AND
%left EQUAL NOT_EQUAL
%left LESS_THAN LESS_OR_EQUAL GREATER_OR_EQUAL REGEX_MATCH FILENAME_MATCH
%left '-' '+'
%left '*' '/'
%right NOT NEG /* negation (unary minus) */

/* nonterminals */
%type <node> string
%type <node> exp
%type <node> boolexp

%%

program:
        /* empty */ {*ast = NULL; return 0;}
        | string {*ast = $1; return 0;}
        | exp {*ast = $1; return 0;}
	| boolexp {*ast = $1; return 0;}

string: STRING_LITERAL {$$ = $1;}
	| TEXT {$$ = txp_new_ast_leaf_node(TEXT);}
;

exp: NUM {$$ = $1;}
        | exp '+' exp {$$ = ast_node_new_binary('+', $1, $3);}
        | exp '-' exp {$$ = ast_node_new_binary('-', $1, $3);}
        | exp '*' exp {$$ = ast_node_new_binary('*', $1, $3);}
        | exp '/' exp {$$ = ast_node_new_binary('/', $1, $3);}
        | '-' exp %prec NEG {$$ = ast_node_new_unary(NEG, $2);}
        | '(' exp ')' {$$ = $2;}
	| LEN {$$ = txp_new_ast_leaf_node(LEN);}
;

boolexp: TAG '(' STRING_LITERAL ')' {$$ = ast_node_new_unary(TAG, $3);}
	| TRUE {$$ = txp_new_ast_leaf_node(TRUE);}
	| FALSE {$$ = txp_new_ast_leaf_node(FALSE);}
	| '(' boolexp ')' {$$ = $2;}
	| boolexp OR boolexp {$$ = ast_node_new_binary(OR, $1, $3);}
	| boolexp AND boolexp {$$ = ast_node_new_binary(AND, $1, $3);}
	| NOT boolexp {$$ = ast_node_new_unary(NOT, $2);}
	| exp EQUAL exp {$$ = ast_node_new_binary(EQUAL, $1, $3);}
	| exp NOT_EQUAL exp {$$ = ast_node_new_binary(NOT_EQUAL, $1, $3);}
	| exp '<' exp {$$ = ast_node_new_binary('<', $1, $3);}
	| exp '>' exp {$$ = ast_node_new_binary('>', $1, $3);}
	| exp LESS_OR_EQUAL exp {
		$$ = ast_node_new_binary(LESS_OR_EQUAL, $1, $3);
	}
	| exp GREATER_OR_EQUAL exp {
		$$ = ast_node_new_binary(GREATER_OR_EQUAL, $1, $3);
	}
	| string REGEX_MATCH REGEX_PATTERN {
		$$ = ast_node_new_binary(REGEX_MATCH, $1, $3);
	}
	| string EQUAL string {$$ = ast_node_new_binary(EQUAL, $1, $3);}
	| string NOT_EQUAL string {$$ = ast_node_new_binary(NOT_EQUAL, $1, $3);}
;
%%

/* Called by yyparse() on error */
static void yyerror(YYLTYPE *llocp, struct txp_context *ctx,
		__attribute__ ((unused)) struct txp_ast_node **ast,
		__attribute__ ((unused)) txp_yyscan_t yyscanner,
		const char *msg)
{
	txp_parse_error(llocp->first_line, ctx, "%s", msg);
}
