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