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