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