summaryrefslogtreecommitdiff
path: root/txp.y
blob: ee9080db91bcc780c7c44c5ebf6086873976f90c (plain)
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/* 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);
}