bt_parse.y revision 1.33
1/*	$OpenBSD: bt_parse.y,v 1.33 2021/04/22 11:36:11 mpi Exp $	*/
2
3/*
4 * Copyright (c) 2019-2021 Martin Pieuchot <mpi@openbsd.org>
5 * Copyright (c) 2019 Tobias Heider <tobhe@openbsd.org>
6 * Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
7 *
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 */
20
21/*
22 * B tracing language parser.
23 *
24 * The dialect of the language understood by this parser aims to be
25 * compatible with the one understood by bpftrace(8), see:
26 *
27 * https://github.com/iovisor/bpftrace/blob/master/docs/reference_guide.md
28 *
29 */
30
31%{
32#include <sys/queue.h>
33
34#include <assert.h>
35#include <ctype.h>
36#include <err.h>
37#include <limits.h>
38#include <stdarg.h>
39#include <stdint.h>
40#include <stdio.h>
41
42#include "bt_parser.h"
43
44/* Name for the default map @[], hopefully nobody will use this one ;) */
45#define UNNAMED_MAP	"___unnamed_map_doesnt_have_any_name"
46
47/* Number of rules to evaluate. */
48struct bt_ruleq		g_rules = TAILQ_HEAD_INITIALIZER(g_rules);
49
50/* Number of probes except BEGIN/END. */
51int		 	g_nprobes;
52
53/* List of global variables, including maps. */
54SLIST_HEAD(, bt_var)	 g_variables;
55
56/* List of local variables, cleaned for each new rule. */
57SLIST_HEAD(, bt_var)	l_variables;
58
59struct bt_rule	*br_new(struct bt_probe *, struct bt_filter *, struct bt_stmt *,
60		     enum bt_rtype);
61struct bt_probe	*bp_new(const char *, const char *, const char *, int32_t);
62struct bt_arg	*ba_append(struct bt_arg *, struct bt_arg *);
63struct bt_arg	*ba_op(enum bt_argtype, struct bt_arg *, struct bt_arg *);
64struct bt_stmt	*bs_new(enum bt_action, struct bt_arg *, struct bt_var *);
65struct bt_stmt	*bs_append(struct bt_stmt *, struct bt_stmt *);
66
67struct bt_var	*bg_lookup(const char *);
68struct bt_stmt	*bg_store(const char *, struct bt_arg *);
69struct bt_arg	*bg_find(const char *);
70struct bt_var	*bg_get(const char *);
71
72struct bt_var	*bl_lookup(const char *);
73struct bt_stmt	*bl_store(const char *, struct bt_arg *);
74struct bt_arg	*bl_find(const char *);
75
76struct bt_arg	*bm_find(const char *, struct bt_arg *);
77struct bt_stmt	*bm_insert(const char *, struct bt_arg *, struct bt_arg *);
78struct bt_stmt	*bm_op(enum bt_action, struct bt_arg *, struct bt_arg *);
79
80struct bt_stmt	*bh_inc(const char *, struct bt_arg *, struct bt_arg *);
81
82/*
83 * Lexer
84 */
85const char	*pbuf;
86size_t		 plen;
87size_t		 pindex;
88int		 perrors = 0;
89
90typedef struct {
91	union {
92		long			 number;
93		int			 i;
94		const char		*string;
95		struct bt_probe		*probe;
96		struct bt_filter	*filter;
97		struct bt_stmt		*stmt;
98		struct bt_arg		*arg;
99	} v;
100	const char			*filename;
101	int				 lineno;
102	int				 colno;
103} yystype;
104#define YYSTYPE yystype
105
106static void	 yyerror(const char *, ...);
107static int	 yylex(void);
108
109static int pflag;
110%}
111
112%token	<v.i>		ERROR ENDPRED OP_EQ OP_NE OP_LE OP_GE OP_LAND OP_LOR
113/* Builtins */
114%token	<v.i>		BUILTIN BEGIN END HZ
115/* Functions and Map operators */
116%token  <v.i>		F_DELETE F_PRINT FUNC0 FUNC1 FUNCN OP1 OP4 MOP0 MOP1
117%token	<v.string>	STRING CSTRING
118%token	<v.number>	NUMBER
119
120%type	<v.string>	gvar lvar
121%type	<v.number>	staticval
122%type	<v.i>		beginend
123%type	<v.probe>	probe probename
124%type	<v.filter>	predicate
125%type	<v.stmt>	action stmt stmtlist
126%type	<v.arg>		expr vargs mentry mexpr printargs term globalvar variable
127
128%right	'='
129%nonassoc OP_EQ OP_NE OP_LE OP_GE OP_LAND OP_LOR
130%left	'&' '|'
131%left	'+' '-'
132%left	'/' '*'
133%%
134
135grammar		: /* empty */
136		| grammar '\n'
137		| grammar rule
138		| grammar error
139		;
140
141rule		: beginend action	 { br_new(NULL, NULL, $2, $1); }
142		| probe predicate action { br_new($1, $2, $3, B_RT_PROBE); }
143		;
144
145beginend	: BEGIN	| END ;
146
147probe		: { pflag = 1; } probename	{ $$ = $2; pflag = 0; }
148
149probename	: STRING ':' STRING ':' STRING	{ $$ = bp_new($1, $3, $5, 0); }
150		| STRING ':' HZ ':' NUMBER	{ $$ = bp_new($1, "hz", NULL, $5); }
151		;
152
153staticval	: NUMBER
154		| '$' NUMBER			{ $$ = get_varg($2); }
155		;
156
157gvar		: '@' STRING			{ $$ = $2; }
158		| '@'				{ $$ = UNNAMED_MAP; }
159		;
160
161lvar		: '$' STRING			{ $$ = $2; }
162		;
163
164mentry		: gvar '[' vargs ']'		{ $$ = bm_find($1, $3); }
165		;
166
167globalvar	: gvar				{ $$ = bg_find($1); }
168		| mentry
169		;
170
171variable	: globalvar
172		| lvar				{ $$ = bl_find($1); }
173		;
174
175mexpr		: MOP0 '(' ')'			{ $$ = ba_new(NULL, $1); }
176		| MOP1 '(' expr ')'		{ $$ = ba_new($3, $1); }
177		| expr
178		;
179
180expr		: CSTRING			{ $$ = ba_new($1, B_AT_STR); }
181		| term
182		;
183
184predicate	: /* empty */		{ $$ = NULL; }
185		| '/' term ENDPRED	{ $$ = bc_new(NULL, B_AT_OP_NE, $2); }
186		;
187
188term		: '(' term ')'		{ $$ = $2; }
189		| term OP_EQ term	{ $$ = ba_op(B_AT_OP_EQ, $1, $3); }
190		| term OP_NE term	{ $$ = ba_op(B_AT_OP_NE, $1, $3); }
191		| term OP_LE term	{ $$ = ba_op(B_AT_OP_LE, $1, $3); }
192		| term OP_GE term	{ $$ = ba_op(B_AT_OP_GE, $1, $3); }
193		| term OP_LAND term	{ $$ = ba_op(B_AT_OP_LAND, $1, $3); }
194		| term OP_LOR term	{ $$ = ba_op(B_AT_OP_LOR, $1, $3); }
195		| term '+' term		{ $$ = ba_op(B_AT_OP_PLUS, $1, $3); }
196		| term '-' term		{ $$ = ba_op(B_AT_OP_MINUS, $1, $3); }
197		| term '*' term		{ $$ = ba_op(B_AT_OP_MULT, $1, $3); }
198		| term '/' term		{ $$ = ba_op(B_AT_OP_DIVIDE, $1, $3); }
199		| term '&' term		{ $$ = ba_op(B_AT_OP_BAND, $1, $3); }
200		| term '|' term		{ $$ = ba_op(B_AT_OP_BOR, $1, $3); }
201		| staticval		{ $$ = ba_new($1, B_AT_LONG); }
202		| BUILTIN		{ $$ = ba_new(NULL, $1); }
203		| variable
204		;
205
206
207vargs		: expr
208		| vargs ',' expr		{ $$ = ba_append($1, $3); }
209		;
210
211printargs	: term
212		| gvar ',' expr			{ $$ = ba_append(bg_find($1), $3); }
213		;
214
215NL		: /* empty */ | '\n'
216		;
217
218stmt		: ';' NL			{ $$ = NULL; }
219		| gvar '=' expr			{ $$ = bg_store($1, $3); }
220		| lvar '=' expr			{ $$ = bl_store($1, $3); }
221		| gvar '[' vargs ']' '=' mexpr	{ $$ = bm_insert($1, $3, $6); }
222		| FUNCN '(' vargs ')'		{ $$ = bs_new($1, $3, NULL); }
223		| FUNC1 '(' expr ')'		{ $$ = bs_new($1, $3, NULL); }
224		| FUNC0 '(' ')'			{ $$ = bs_new($1, NULL, NULL); }
225		| F_DELETE '(' mentry ')'	{ $$ = bm_op($1, $3, NULL); }
226		| F_PRINT '(' printargs ')'	{ $$ = bs_new($1, $3, NULL); }
227		| gvar '=' OP1 '(' expr ')'	{ $$ = bh_inc($1, $5, NULL); }
228		| gvar '=' OP4 '(' expr ',' vargs ')' {$$ = bh_inc($1, $5, $7);}
229		;
230
231stmtlist	: stmt
232		| stmtlist stmt			{ $$ = bs_append($1, $2); }
233		;
234
235action		: '{' stmtlist '}'		{ $$ = $2; }
236		;
237
238%%
239
240int
241get_varg(int index)
242{
243	extern int vargs[];
244
245	assert(index == 1);
246
247	return vargs[index - 1];
248}
249
250/* Create a new rule, representing  "probe / filter / { action }" */
251struct bt_rule *
252br_new(struct bt_probe *probe, struct bt_filter *filter, struct bt_stmt *head,
253    enum bt_rtype rtype)
254{
255	struct bt_rule *br;
256
257	br = calloc(1, sizeof(*br));
258	if (br == NULL)
259		err(1, "bt_rule: calloc");
260	br->br_probe = probe;
261	br->br_filter = filter;
262	/* SLIST_INSERT_HEAD() nullify the next pointer. */
263	SLIST_FIRST(&br->br_action) = head;
264	br->br_type = rtype;
265
266	SLIST_FIRST(&br->br_variables) = SLIST_FIRST(&l_variables);
267	SLIST_INIT(&l_variables);
268
269	if (rtype == B_RT_PROBE) {
270		g_nprobes++;
271		TAILQ_INSERT_TAIL(&g_rules, br, br_next);
272	} else {
273		TAILQ_INSERT_HEAD(&g_rules, br, br_next);
274	}
275
276	return br;
277}
278
279/* Create a new condition */
280struct bt_filter *
281bc_new(struct bt_arg *term, enum bt_argtype op, struct bt_arg *ba)
282{
283	struct bt_filter *bf;
284
285	bf = calloc(1, sizeof(*bf));
286	if (bf == NULL)
287		err(1, "bt_filter: calloc");
288
289	bf->bf_condition = bs_new(B_AC_TEST, ba_op(op, term, ba), NULL);
290
291	return bf;
292}
293
294/* Create a new probe */
295struct bt_probe *
296bp_new(const char *prov, const char *func, const char *name, int32_t rate)
297{
298	struct bt_probe *bp;
299
300	if (rate < 0 || rate > INT32_MAX)
301		errx(1, "only positive values permitted");
302
303	bp = calloc(1, sizeof(*bp));
304	if (bp == NULL)
305		err(1, "bt_probe: calloc");
306	bp->bp_prov = prov;
307	bp->bp_func = func;
308	bp->bp_name = name;
309	bp->bp_rate = rate;
310
311	return bp;
312}
313
314/* Create a new argument */
315struct bt_arg *
316ba_new0(void *val, enum bt_argtype type)
317{
318	struct bt_arg *ba;
319
320	ba = calloc(1, sizeof(*ba));
321	if (ba == NULL)
322		err(1, "bt_arg: calloc");
323	ba->ba_value = val;
324	ba->ba_type = type;
325
326	return ba;
327}
328
329/*
330 * Link two arguments together, to build an argument list used in
331 * function calls.
332 */
333struct bt_arg *
334ba_append(struct bt_arg *da0, struct bt_arg *da1)
335{
336	struct bt_arg *ba = da0;
337
338	assert(da1 != NULL);
339
340	if (da0 == NULL)
341		return da1;
342
343	while (SLIST_NEXT(ba, ba_next) != NULL)
344		ba = SLIST_NEXT(ba, ba_next);
345
346	SLIST_INSERT_AFTER(ba, da1, ba_next);
347
348	return da0;
349}
350
351/* Create an operator argument */
352struct bt_arg *
353ba_op(enum bt_argtype op, struct bt_arg *da0, struct bt_arg *da1)
354{
355	return ba_new(ba_append(da0, da1), op);
356}
357
358/* Create a new statement: function call or assignment. */
359struct bt_stmt *
360bs_new(enum bt_action act, struct bt_arg *head, struct bt_var *var)
361{
362	struct bt_stmt *bs;
363
364	bs = calloc(1, sizeof(*bs));
365	if (bs == NULL)
366		err(1, "bt_stmt: calloc");
367	bs->bs_act = act;
368	bs->bs_var = var;
369	/* SLIST_INSERT_HEAD() nullify the next pointer. */
370	SLIST_FIRST(&bs->bs_args) = head;
371
372	return bs;
373}
374
375/* Link two statements together, to build an 'action'. */
376struct bt_stmt *
377bs_append(struct bt_stmt *ds0, struct bt_stmt *ds1)
378{
379	struct bt_stmt *bs = ds0;
380
381	if (ds0 == NULL)
382		return ds1;
383
384	if (ds1 == NULL)
385		return ds0;
386
387	while (SLIST_NEXT(bs, bs_next) != NULL)
388		bs = SLIST_NEXT(bs, bs_next);
389
390	SLIST_INSERT_AFTER(bs, ds1, bs_next);
391
392	return ds0;
393}
394
395const char *
396bv_name(struct bt_var *bv)
397{
398	if (strncmp(bv->bv_name, UNNAMED_MAP, strlen(UNNAMED_MAP)) == 0)
399		return "";
400	return bv->bv_name;
401}
402
403/* Allocate a variable. */
404struct bt_var *
405bv_new(const char *vname)
406{
407	struct bt_var *bv;
408
409	bv = calloc(1, sizeof(*bv));
410	if (bv == NULL)
411		err(1, "bt_var: calloc");
412	bv->bv_name = vname;
413
414	return bv;
415}
416
417/* Return the global variable corresponding to `vname'. */
418struct bt_var *
419bg_lookup(const char *vname)
420{
421	struct bt_var *bv;
422
423	SLIST_FOREACH(bv, &g_variables, bv_next) {
424		if (strcmp(vname, bv->bv_name) == 0)
425			break;
426	}
427
428	return bv;
429}
430
431/* Find or allocate a global variable corresponding to `vname' */
432struct bt_var *
433bg_get(const char *vname)
434{
435	struct bt_var *bv;
436
437	bv = bg_lookup(vname);
438	if (bv == NULL) {
439		bv = bv_new(vname);
440		SLIST_INSERT_HEAD(&g_variables, bv, bv_next);
441	}
442
443	return bv;
444}
445
446/* Create an "argument" that points to an existing untyped variable. */
447struct bt_arg *
448bg_find(const char *vname)
449{
450	struct bt_var *bv;
451
452	bv = bg_lookup(vname);
453	if (bv == NULL)
454		yyerror("variable '%s' accessed before being set", vname);
455
456	return ba_new(bv, B_AT_VAR);
457}
458
459/* Create a 'store' statement to assign a value to a global variable. */
460struct bt_stmt *
461bg_store(const char *vname, struct bt_arg *vval)
462{
463	return bs_new(B_AC_STORE, vval, bg_get(vname));
464}
465
466/* Return the local variable corresponding to `vname'. */
467struct bt_var *
468bl_lookup(const char *vname)
469{
470	struct bt_var *bv;
471
472	SLIST_FOREACH(bv, &l_variables, bv_next) {
473		if (strcmp(vname, bv->bv_name) == 0)
474			break;
475	}
476
477	return bv;
478}
479
480/* Find or create a local variable corresponding to `vname' */
481struct bt_arg *
482bl_find(const char *vname)
483{
484	struct bt_var *bv;
485
486	bv = bl_lookup(vname);
487	if (bv == NULL) {
488		bv = bv_new(vname);
489		SLIST_INSERT_HEAD(&l_variables, bv, bv_next);
490	}
491
492	return ba_new(bv, B_AT_VAR);
493}
494
495/* Create a 'store' statement to assign a value to a local variable. */
496struct bt_stmt *
497bl_store(const char *vname, struct bt_arg *vval)
498{
499	struct bt_var *bv;
500
501	bv = bl_lookup(vname);
502	if (bv == NULL) {
503		bv = bv_new(vname);
504		SLIST_INSERT_HEAD(&l_variables, bv, bv_next);
505	}
506
507	return bs_new(B_AC_STORE, vval, bv);
508}
509
510struct bt_stmt *
511bm_op(enum bt_action mact, struct bt_arg *ba, struct bt_arg *mval)
512{
513	return bs_new(mact, ba, (struct bt_var *)mval);
514}
515
516/* Create a 'map store' statement to assign a value to a map entry. */
517struct bt_stmt *
518bm_insert(const char *mname, struct bt_arg *mkey, struct bt_arg *mval)
519{
520	struct bt_arg *ba;
521
522	ba = ba_new(bg_get(mname), B_AT_MAP);
523	ba->ba_key = mkey;
524
525	return bs_new(B_AC_INSERT, ba, (struct bt_var *)mval);
526}
527
528/* Create an argument that points to a variable and attach a key to it. */
529struct bt_arg *
530bm_find(const char *vname, struct bt_arg *mkey)
531{
532	struct bt_var *bv;
533	struct bt_arg *ba;
534
535	bv = bg_lookup(vname);
536	if (bv == NULL)
537		yyerror("variable '%s' accessed before being set", vname);
538
539	ba = ba_new(bv, B_AT_MAP);
540	ba->ba_key = mkey;
541	return ba;
542}
543
544/*
545 * Histograms implemented using associative arrays (maps).  In the case
546 * of linear histograms `ba_key' points to a list of (min, max, step)
547 * necessary to "bucketize" any value.
548 */
549struct bt_stmt *
550bh_inc(const char *hname, struct bt_arg *hval, struct bt_arg *hrange)
551{
552	struct bt_arg *ba;
553
554	if (hrange == NULL) {
555		/* Power-of-2 histogram */
556	} else {
557		long min = 0, max;
558		int count = 0;
559
560		/* Linear histogram */
561		for (ba = hrange; ba != NULL; ba = SLIST_NEXT(ba, ba_next)) {
562			if (++count > 3)
563				yyerror("too many arguments");
564			if (ba->ba_type != B_AT_LONG)
565				yyerror("type invalid");
566
567			switch (count) {
568			case 1:
569				min = (long)ba->ba_value;
570				if (min >= 0)
571					break;
572				yyerror("negative minium");
573			case 2:
574				max = (long)ba->ba_value;
575				if (max > min)
576					break;
577				yyerror("maximum smaller than minium (%d < %d)",
578				    max,  min);
579			case 3:
580				break;
581			default:
582				assert(0);
583			}
584		}
585		if (count < 3)
586			yyerror("%d missing arguments", 3 - count);
587	}
588
589	ba = ba_new(bg_get(hname), B_AT_HIST);
590	ba->ba_key = hrange;
591	return bs_new(B_AC_BUCKETIZE, ba, (struct bt_var *)hval);
592}
593
594struct keyword {
595	const char	*word;
596	int		 token;
597	int		 type;
598};
599
600int
601kw_cmp(const void *str, const void *xkw)
602{
603	return (strcmp(str, ((const struct keyword *)xkw)->word));
604}
605
606struct keyword *
607lookup(char *s)
608{
609	static const struct keyword kws[] = {
610		{ "BEGIN",	BEGIN,		B_RT_BEGIN },
611		{ "END",	END,		B_RT_END },
612		{ "arg0",	BUILTIN,	B_AT_BI_ARG0 },
613		{ "arg1",	BUILTIN,	B_AT_BI_ARG1 },
614		{ "arg2",	BUILTIN,	B_AT_BI_ARG2 },
615		{ "arg3",	BUILTIN,	B_AT_BI_ARG3 },
616		{ "arg4",	BUILTIN,	B_AT_BI_ARG4 },
617		{ "arg5",	BUILTIN,	B_AT_BI_ARG5 },
618		{ "arg6",	BUILTIN,	B_AT_BI_ARG6 },
619		{ "arg7",	BUILTIN,	B_AT_BI_ARG7 },
620		{ "arg8",	BUILTIN,	B_AT_BI_ARG8 },
621		{ "arg9",	BUILTIN,	B_AT_BI_ARG9 },
622		{ "clear",	FUNC1,		B_AC_CLEAR },
623		{ "comm",	BUILTIN,	B_AT_BI_COMM },
624		{ "count",	MOP0, 		B_AT_MF_COUNT },
625		{ "cpu",	BUILTIN,	B_AT_BI_CPU },
626		{ "delete",	F_DELETE,	B_AC_DELETE },
627		{ "exit",	FUNC0,		B_AC_EXIT },
628		{ "hist",	OP1,		0 },
629		{ "hz",		HZ,		0 },
630		{ "kstack",	BUILTIN,	B_AT_BI_KSTACK },
631		{ "lhist",	OP4,		0 },
632		{ "max",	MOP1,		B_AT_MF_MAX },
633		{ "min",	MOP1,		B_AT_MF_MIN },
634		{ "nsecs",	BUILTIN,	B_AT_BI_NSECS },
635		{ "pid",	BUILTIN,	B_AT_BI_PID },
636		{ "print",	F_PRINT,	B_AC_PRINT },
637		{ "printf",	FUNCN,		B_AC_PRINTF },
638		{ "retval",	BUILTIN,	B_AT_BI_RETVAL },
639		{ "sum",	MOP1,		B_AT_MF_SUM },
640		{ "tid",	BUILTIN,	B_AT_BI_TID },
641		{ "time",	FUNC1,		B_AC_TIME },
642		{ "ustack",	BUILTIN,	B_AT_BI_USTACK },
643		{ "zero",	FUNC1,		B_AC_ZERO },
644	};
645
646	return bsearch(s, kws, nitems(kws), sizeof(kws[0]), kw_cmp);
647}
648
649int
650peek(void)
651{
652	if (pbuf != NULL) {
653		if (pindex < plen)
654			return pbuf[pindex];
655	}
656	return EOF;
657}
658
659int
660lgetc(void)
661{
662	if (pbuf != NULL) {
663		if (pindex < plen) {
664			yylval.colno++;
665			return pbuf[pindex++];
666		}
667	}
668	return EOF;
669}
670
671void
672lungetc(void)
673{
674	if (pbuf != NULL && pindex > 0) {
675		yylval.colno--;
676		pindex--;
677	}
678}
679
680int
681yylex(void)
682{
683	unsigned char	 buf[1024];
684	unsigned char	*ebuf, *p, *str;
685	int		 c;
686
687	ebuf = buf + sizeof(buf);
688	p = buf;
689
690again:
691	/* skip whitespaces */
692	for (c = lgetc(); isspace(c); c = lgetc()) {
693		if (c == '\n') {
694			yylval.lineno++;
695			yylval.colno = 0;
696		}
697	}
698
699	/* skip single line comments and shell magic */
700	if ((c == '/' && peek() == '/') ||
701	    (yylval.lineno == 1 && yylval.colno == 1 && c == '#' &&
702	     peek() == '!')) {
703		for (c = lgetc(); c != EOF; c = lgetc()) {
704			if (c == '\n') {
705				yylval.lineno++;
706				yylval.colno = 0;
707				goto again;
708			}
709		}
710	}
711
712	/* skip multi line comments */
713	if (c == '/' && peek() == '*') {
714		int pc;
715
716		for (pc = 0, c = lgetc(); c != EOF; c = lgetc()) {
717			if (pc == '*' && c == '/')
718				goto again;
719			else if (c == '\n')
720				yylval.lineno++;
721			pc = c;
722		}
723	}
724
725	switch (c) {
726	case '!':
727	case '=':
728		if (peek() == '=') {
729			lgetc();
730			return (c == '=') ? OP_EQ : OP_NE;
731		}
732	case '&':
733		if (peek() == '&') {
734			lgetc();
735			return OP_LAND;
736		}
737	case '|':
738		if (peek() == '|') {
739			lgetc();
740			return OP_LOR;
741		}
742	case '/':
743		if (peek() == '{' || peek() == '/' || peek() == '\n') {
744			return ENDPRED;
745		}
746		/* FALLTHROUGH */
747	case ',':
748	case '(':
749	case ')':
750	case '{':
751	case '}':
752	case ':':
753	case ';':
754		return c;
755	case EOF:
756		return 0;
757	case '"':
758		/* parse C-like string */
759		while ((c = lgetc()) != EOF && c != '"') {
760			if (c == '\\') {
761				c = lgetc();
762				switch (c) {
763				case '\\':	c = '\\';	break;
764				case '\'':	c = '\'';	break;
765				case '"':	c = '"';	break;
766				case 'a':	c = '\a';	break;
767				case 'b':	c = '\b';	break;
768				case 'e':	c = 033;	break;
769				case 'f':	c = '\f';	break;
770				case 'n':	c = '\n';	break;
771				case 'r':	c = '\r';	break;
772				case 't':	c = '\t';	break;
773				case 'v':	c = '\v';	break;
774				default:
775					yyerror("'%c' unsuported escape", c);
776					return ERROR;
777				}
778			}
779			*p++ = c;
780			if (p == ebuf) {
781				yyerror("too long line");
782				return ERROR;
783			}
784		}
785		if (c == EOF) {
786			yyerror("\"%s\" invalid EOF", buf);
787			return ERROR;
788		}
789		*p++ = '\0';
790		if ((str = strdup(buf)) == NULL)
791			err(1, "%s", __func__);
792		yylval.v.string = str;
793		return CSTRING;
794	default:
795		break;
796	}
797
798#define allowed_to_end_number(x) \
799    (isspace(x) || x == ')' || x == '/' || x == '{' || x == ';' || x == ']' || x == ',')
800
801	/* parsing number */
802	if (isdigit(c)) {
803		do {
804			*p++ = c;
805			if (p == ebuf) {
806				yyerror("too long line");
807				return ERROR;
808			}
809		} while ((c = lgetc()) != EOF && isdigit(c));
810		lungetc();
811		if (c == EOF || allowed_to_end_number(c)) {
812			const char *errstr = NULL;
813
814			*p = '\0';
815			yylval.v.number = strtonum(buf, LONG_MIN, LONG_MAX,
816			    &errstr);
817			if (errstr) {
818				yyerror("invalid number '%s' (%s)", buf,
819				    errstr);
820				return ERROR;
821			}
822			return NUMBER;
823		} else {
824			while (p > buf + 1) {
825				--p;
826				lungetc();
827			}
828			c = *--p;
829		}
830	}
831
832#define allowed_in_string(x) (isalnum(c) || c == '_')
833
834	/* parsing next word */
835	if (allowed_in_string(c)) {
836		struct keyword *kwp;
837		do {
838			*p++ = c;
839			if (p == ebuf) {
840				yyerror("too long line");
841				return ERROR;
842			}
843		} while ((c = lgetc()) != EOF && (allowed_in_string(c)));
844		lungetc();
845		*p = '\0';
846		kwp = lookup(buf);
847		if (kwp == NULL) {
848			if ((yylval.v.string = strdup(buf)) == NULL)
849				err(1, "%s", __func__);
850			return STRING;
851		}
852		if (pflag) {
853			/*
854			 * Probe lexer backdoor, interpret the token as a string
855			 * rather than a keyword. Otherwise, reserved keywords
856			 * would conflict with syscall names. The exception to
857			 * this is 'hz', which hopefully will never be a
858			 * syscall.
859			 */
860			if (kwp->token != HZ) {
861				yylval.v.string = kwp->word;
862				return STRING;
863			}
864		}
865		yylval.v.i = kwp->type;
866		return kwp->token;
867	}
868
869	if (c == '\n') {
870		yylval.lineno++;
871		yylval.colno = 0;
872	}
873	if (c == EOF)
874		return 0;
875	return c;
876}
877
878void
879pprint_syntax_error(void)
880{
881	char line[BUFSIZ];
882	int c, indent = yylval.colno;
883	size_t i;
884
885	strlcpy(line, &pbuf[pindex - yylval.colno], sizeof(line));
886
887	for (i = 0; line[i] != '\0' && (c = line[i]) != '\n'; i++) {
888		if (c == '\t')
889			indent += (8 - 1);
890		fputc(c, stderr);
891	}
892
893	fprintf(stderr, "\n%*c\n", indent, '^');
894}
895
896void
897yyerror(const char *fmt, ...)
898{
899	const char *prefix;
900	va_list	va;
901
902	prefix = (yylval.filename != NULL) ? yylval.filename : getprogname();
903
904	fprintf(stderr, "%s:%d:%d: ", prefix, yylval.lineno, yylval.colno);
905	va_start(va, fmt);
906	vfprintf(stderr, fmt, va);
907	va_end(va);
908	fprintf(stderr, ":\n");
909
910	pprint_syntax_error();
911
912	perrors++;
913}
914
915int
916btparse(const char *str, size_t len, const char *filename, int debug)
917{
918	if (debug > 0)
919		yydebug = 1;
920	pbuf = str;
921	plen = len;
922	pindex = 0;
923	yylval.filename = filename;
924	yylval.lineno = 1;
925
926	yyparse();
927
928	assert(SLIST_EMPTY(&l_variables));
929
930	return perrors;
931}
932