bt_parse.y revision 1.11
1/* $OpenBSD: bt_parse.y,v 1.11 2020/04/23 14:54:12 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_fn(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 ARG0 ARG1 ARG2 ARG3 ARG4 ARG5 ARG6 ARG7 ARG8 ARG9 104%token COMM HZ KSTACK USTACK NSECS PID RETVAL TID 105/* Functions */ 106%token F_CLEAR F_DELETE F_EXIT F_PRINT F_PRINTF F_TIME F_ZERO 107/* Map functions */ 108%token M_COUNT M_MAX M_MIN M_SUM 109%token <v.string> STRING CSTRING 110%token <v.number> NUMBER 111%type <v.string> gvar 112%type <v.i> filterval oper builtin fn0 fn1 fnN mfn0 mfn1 mfnN 113%type <v.probe> probe 114%type <v.filter> predicate 115%type <v.stmt> action stmt stmtlist 116%type <v.arg> arg arglist map marg term 117%type <v.rtype> beginend 118 119%left '+' '-' 120%left '/' '*' 121%% 122 123grammar : /* empty */ 124 | grammar '\n' 125 | grammar rule 126 | grammar error 127 ; 128 129rule : beginend action { br_new(NULL, NULL, $2, $1); } 130 | probe predicate action { br_new($1, $2, $3, B_RT_PROBE); } 131 ; 132 133beginend : BEGIN { $$ = B_RT_BEGIN; } 134 | END { $$ = B_RT_END; } 135 ; 136 137probe : STRING ':' STRING ':' STRING { $$ = bp_new($1, $3, $5, 0); } 138 | STRING ':' HZ ':' NUMBER { $$ = bp_new($1, "hz", NULL, $5); } 139 ; 140 141 142filterval : PID { $$ = B_FV_PID; } 143 | TID { $$ = B_FV_TID; } 144 ; 145 146oper : OP_EQ { $$ = B_OP_EQ; } 147 | OP_NEQ { $$ = B_OP_NE; } 148 ; 149 150predicate : /* empty */ { $$ = NULL; } 151 | '/' filterval oper NUMBER '/' { $$ = bf_new($3, $2, $4); } 152 | '/' NUMBER oper filterval '/' { $$ = bf_new($3, $4, $2); } 153 ; 154 155builtin : PID { $$ = B_AT_BI_PID; } 156 | TID { $$ = B_AT_BI_TID; } 157 | COMM { $$ = B_AT_BI_COMM; } 158 | NSECS { $$ = B_AT_BI_NSECS; } 159 | KSTACK { $$ = B_AT_BI_KSTACK; } 160 | USTACK { $$ = B_AT_BI_USTACK; } 161 | ARG0 { $$ = B_AT_BI_ARG0; } 162 | ARG1 { $$ = B_AT_BI_ARG1; } 163 | ARG2 { $$ = B_AT_BI_ARG2; } 164 | ARG3 { $$ = B_AT_BI_ARG3; } 165 | ARG4 { $$ = B_AT_BI_ARG4; } 166 | ARG5 { $$ = B_AT_BI_ARG5; } 167 | ARG6 { $$ = B_AT_BI_ARG6; } 168 | ARG7 { $$ = B_AT_BI_ARG7; } 169 | ARG8 { $$ = B_AT_BI_ARG8; } 170 | ARG9 { $$ = B_AT_BI_ARG9; } 171 | RETVAL { $$ = B_AT_BI_RETVAL; } 172 ; 173 174fn0 : F_EXIT { $$ = B_AC_EXIT; } 175 ; 176 177fn1 : F_CLEAR { $$ = B_AC_CLEAR; } 178 | F_TIME { $$ = B_AC_TIME; } 179 | F_ZERO { $$ = B_AC_ZERO; } 180 ; 181 182fnN : F_PRINTF { $$ = B_AC_PRINTF; } 183 | F_PRINT { $$ = B_AC_PRINT; } 184 ; 185 186mfn0 : M_COUNT { $$ = B_AT_MF_COUNT; } 187 ; 188 189mfn1 : F_DELETE { $$ = B_AC_DELETE; } 190 ; 191 192mfnN : M_MAX { $$ = B_AT_MF_MAX; } 193 | M_MIN { $$ = B_AT_MF_MIN; } 194 | M_SUM { $$ = B_AT_MF_SUM; } 195 ; 196 197term : '(' term ')' { $$ = $2; } 198 | term '+' term { $$ = ba_op('+', $1, $3); } 199 | term '-' term { $$ = ba_op('-', $1, $3); } 200 | term '/' term { $$ = ba_op('/', $1, $3); } 201 | term '*' term { $$ = ba_op('*', $1, $3); } 202 | NUMBER { $$ = ba_new($1, B_AT_LONG); } 203 | builtin { $$ = ba_new(NULL, $1); } 204 | gvar { $$ = bv_get($1); } 205 | map { $$ = $1; } 206 ; 207 208gvar : '@' STRING { $$ = $2; } 209 | '@' { $$ = UNNAMED_MAP; } 210 211map : gvar '[' arglist ']' { $$ = bm_get($1, $3); } 212 ; 213 214marg : arg { $$ = $1; } 215 | mfn0 '(' ')' { $$ = ba_new(NULL, $1); } 216 | mfnN '(' arg ')' { $$ = ba_new($3, $1); } 217 ; 218 219arg : CSTRING { $$ = ba_new($1, B_AT_STR); } 220 | term 221 ; 222 223arglist : arg 224 | arglist ',' arg { $$ = ba_append($1, $3); } 225 ; 226 227NL : /* empty */ | '\n' 228 ; 229 230stmt : ';' NL { $$ = NULL; } 231 | gvar '=' arg { $$ = bv_set($1, $3); } 232 | gvar '[' arglist ']' '=' marg { $$ = bm_set($1, $3, $6); } 233 | fnN '(' arglist ')' { $$ = bs_new($1, $3, NULL); } 234 | fn1 '(' arg ')' { $$ = bs_new($1, $3, NULL); } 235 | fn0 '(' ')' { $$ = bs_new($1, NULL, NULL); } 236 | mfn1 '(' map ')' { $$ = bm_fn($1, $3, NULL); } 237 ; 238 239stmtlist : stmt 240 | stmtlist stmt { $$ = bs_append($1, $2); } 241 ; 242 243action : '{' stmtlist '}' { $$ = $2; } 244 ; 245 246%% 247 248/* Create a new rule, representing "probe / filter / { action }" */ 249struct bt_rule * 250br_new(struct bt_probe *probe, struct bt_filter *filter, struct bt_stmt *head, 251 enum bt_rtype rtype) 252{ 253 struct bt_rule *br; 254 255 br = calloc(1, sizeof(struct bt_rule)); 256 if (br == NULL) 257 err(1, "bt_rule: calloc"); 258 br->br_probe = probe; 259 br->br_filter = filter; 260 /* SLIST_INSERT_HEAD() nullify the next pointer. */ 261 SLIST_FIRST(&br->br_action) = head; 262 br->br_type = rtype; 263 264 if (rtype == B_RT_PROBE) { 265 g_nprobes++; 266 TAILQ_INSERT_TAIL(&g_rules, br, br_next); 267 } else { 268 TAILQ_INSERT_HEAD(&g_rules, br, br_next); 269 } 270 271 return br; 272} 273 274/* Create a new filter */ 275struct bt_filter * 276bf_new(enum bt_operand op, enum bt_filtervar var, int val) 277{ 278 struct bt_filter *df; 279 280 if (val < 0 || val > INT_MAX) 281 errx(1, "invalid pid '%d'", val); 282 283 df = calloc(1, sizeof(struct bt_filter)); 284 if (df == NULL) 285 err(1, "bt_filter: calloc"); 286 df->bf_op = op; 287 df->bf_var = var; 288 df->bf_val = val; 289 290 return df; 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(struct bt_probe)); 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(struct bt_arg)); 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 default: 370 assert(0); 371 } 372 373 return ba_new(ba_append(da0, da1), type); 374} 375 376/* Create a new statement: function call or assignment. */ 377struct bt_stmt * 378bs_new(enum bt_action act, struct bt_arg *head, struct bt_var *var) 379{ 380 struct bt_stmt *bs; 381 382 bs = calloc(1, sizeof(struct bt_stmt)); 383 if (bs == NULL) 384 err(1, "bt_stmt: calloc"); 385 bs->bs_act = act; 386 bs->bs_var = var; 387 /* SLIST_INSERT_HEAD() nullify the next pointer. */ 388 SLIST_FIRST(&bs->bs_args) = head; 389 390 return bs; 391} 392 393/* Link two statements together, to build an 'action'. */ 394struct bt_stmt * 395bs_append(struct bt_stmt *ds0, struct bt_stmt *ds1) 396{ 397 struct bt_stmt *bs = ds0; 398 399 if (ds0 == NULL) 400 return ds1; 401 402 if (ds1 == NULL) 403 return ds0; 404 405 while (SLIST_NEXT(bs, bs_next) != NULL) 406 bs = SLIST_NEXT(bs, bs_next); 407 408 SLIST_INSERT_AFTER(bs, ds1, bs_next); 409 410 return ds0; 411} 412 413const char * 414bv_name(struct bt_var *bv) 415{ 416 if (strncmp(bv->bv_name, UNNAMED_MAP, strlen(UNNAMED_MAP)) == 0) 417 return ""; 418 return bv->bv_name; 419} 420 421/* Return the global variable corresponding to `vname'. */ 422struct bt_var * 423bv_find(const char *vname) 424{ 425 struct bt_var *bv; 426 427 SLIST_FOREACH(bv, &g_variables, bv_next) { 428 if (strcmp(vname, bv->bv_name) == 0) 429 break; 430 } 431 432 return bv; 433} 434 435/* Find or allocate a global variable. */ 436struct bt_var * 437bv_new(const char *vname) 438{ 439 struct bt_var *bv; 440 441 bv = calloc(1, sizeof(struct bt_var)); 442 if (bv == NULL) 443 err(1, "bt_var: calloc"); 444 bv->bv_name = vname; 445 SLIST_INSERT_HEAD(&g_variables, bv, bv_next); 446 447 return bv; 448} 449 450/* Create a 'variable store' statement to assign a value to a variable. */ 451struct bt_stmt * 452bv_set(const char *vname, struct bt_arg *vval) 453{ 454 struct bt_var *bv; 455 456 bv = bv_find(vname); 457 if (bv == NULL) 458 bv = bv_new(vname); 459 return bs_new(B_AC_STORE, vval, bv); 460} 461 462/* Create an argument that points to a variable. */ 463struct bt_arg * 464bv_get(const char *vname) 465{ 466 struct bt_var *bv; 467 468 bv = bv_find(vname); 469 if (bv == NULL) 470 yyerror("variable '%s' accessed before being set", vname); 471 472 return ba_new(bv, B_AT_VAR); 473} 474 475struct bt_stmt * 476bm_fn(enum bt_action mact, struct bt_arg *ba, struct bt_arg *mval) 477{ 478 return bs_new(mact, ba, (struct bt_var *)mval); 479} 480 481/* Create a 'map store' statement to assign a value to a map entry. */ 482struct bt_stmt * 483bm_set(const char *mname, struct bt_arg *mkey, struct bt_arg *mval) 484{ 485 struct bt_arg *ba; 486 struct bt_var *bv; 487 488 bv = bv_find(mname); 489 if (bv == NULL) 490 bv = bv_new(mname); 491 ba = ba_new(bv, B_AT_MAP); 492 ba->ba_key = mkey; 493 return bs_new(B_AC_INSERT, ba, (struct bt_var *)mval); 494} 495 496/* Create an argument that points to a variable and attach a key to it. */ 497struct bt_arg * 498bm_get(const char *mname, struct bt_arg *mkey) 499{ 500 struct bt_arg *ba; 501 502 ba = bv_get(mname); 503 ba->ba_type = B_AT_MAP; 504 ba->ba_key = mkey; 505 return ba; 506} 507 508struct keyword { 509 const char *word; 510 int token; 511}; 512 513int 514kw_cmp(const void *str, const void *xkw) 515{ 516 return (strcmp(str, ((const struct keyword *)xkw)->word)); 517} 518 519int 520lookup(char *s) 521{ 522 static const struct keyword kws[] = { 523 { "!=", OP_NEQ }, 524 { "==", OP_EQ }, 525 { "BEGIN", BEGIN }, 526 { "END", END }, 527 { "arg0", ARG0 }, 528 { "arg1", ARG1 }, 529 { "arg2", ARG2 }, 530 { "arg3", ARG3 }, 531 { "arg4", ARG4 }, 532 { "arg5", ARG5 }, 533 { "arg6", ARG6 }, 534 { "arg7", ARG7 }, 535 { "arg8", ARG8 }, 536 { "arg9", ARG9 }, 537 { "clear", F_CLEAR }, 538 { "comm", COMM }, 539 { "count", M_COUNT }, 540 { "delete", F_DELETE }, 541 { "exit", F_EXIT }, 542 { "hz", HZ }, 543 { "kstack", KSTACK }, 544 { "max", M_MAX }, 545 { "min", M_MIN }, 546 { "nsecs", NSECS }, 547 { "pid", PID }, 548 { "print", F_PRINT }, 549 { "printf", F_PRINTF }, 550 { "retval", RETVAL }, 551 { "sum", M_SUM }, 552 { "tid", TID }, 553 { "time", F_TIME }, 554 { "ustack", USTACK }, 555 { "zero", F_ZERO }, 556 }; 557 const struct keyword *kw; 558 559 kw = bsearch(s, kws, nitems(kws), sizeof(kws[0]), kw_cmp); 560 if (kw != NULL) { 561 return kw->token; 562 } else { 563 return STRING; 564 } 565} 566 567int 568peek(void) 569{ 570 if (pbuf != NULL) { 571 if (pindex < plen) 572 return pbuf[pindex]; 573 } 574 return EOF; 575} 576 577int 578lgetc(void) 579{ 580 if (pbuf != NULL) { 581 if (pindex < plen) { 582 yylval.colno++; 583 return pbuf[pindex++]; 584 } 585 } 586 return EOF; 587} 588 589void 590lungetc(void) 591{ 592 if (pbuf != NULL && pindex > 0) { 593 yylval.colno--; 594 pindex--; 595 } 596} 597 598int 599yylex(void) 600{ 601 unsigned char buf[1024]; 602 unsigned char *ebuf, *p, *str; 603 int c, token; 604 605 ebuf = buf + sizeof(buf); 606 p = buf; 607 608again: 609 /* skip whitespaces */ 610 for (c = lgetc(); isspace(c); c = lgetc()) { 611 if (c == '\n') { 612 yylval.lineno++; 613 yylval.colno = 0; 614 } 615 } 616 617 /* skip single line comments and shell magic */ 618 if ((c == '/' && peek() == '/') || 619 (yylval.lineno == 1 && yylval.colno == 1 && c == '#' && 620 peek() == '!')) { 621 for (c = lgetc(); c != EOF; c = lgetc()) { 622 if (c == '\n') { 623 yylval.lineno++; 624 yylval.colno = 0; 625 goto again; 626 } 627 } 628 } 629 630 /* skip multi line comments */ 631 if (c == '/' && peek() == '*') { 632 int pc; 633 634 for (pc = 0, c = lgetc(); c != EOF; c = lgetc()) { 635 if (pc == '*' && c == '/') 636 goto again; 637 pc = c; 638 } 639 } 640 641 switch (c) { 642 case '=': 643 if (peek() == '=') 644 break; 645 case ',': 646 case '(': 647 case ')': 648 case '{': 649 case '}': 650 case ':': 651 case ';': 652 case '/': 653 return c; 654 case EOF: 655 return 0; 656 case '"': 657 /* parse C-like string */ 658 while ((c = lgetc()) != EOF && c != '"') { 659 if (c == '\\') { 660 c = lgetc(); 661 switch (c) { 662 case '\\': c = '\\'; break; 663 case '\'': c = '\''; break; 664 case '"': c = '"'; break; 665 case 'a': c = '\a'; break; 666 case 'b': c = '\b'; break; 667 case 'e': c = 033; break; 668 case 'f': c = '\f'; break; 669 case 'n': c = '\n'; break; 670 case 'r': c = '\r'; break; 671 case 't': c = '\t'; break; 672 case 'v': c = '\v'; break; 673 default: 674 yyerror("'%c' unsuported escape", c); 675 return ERROR; 676 } 677 } 678 *p++ = c; 679 if (p == ebuf) { 680 yyerror("too long line"); 681 return ERROR; 682 } 683 } 684 if (c == EOF) { 685 yyerror("\"%s\" invalid EOF", buf); 686 return ERROR; 687 } 688 *p++ = '\0'; 689 if ((str = strdup(buf)) == NULL) 690 err(1, "%s", __func__); 691 yylval.v.string = str; 692 return CSTRING; 693 default: 694 break; 695 } 696 697#define allowed_to_end_number(x) \ 698 (isspace(x) || x == ')' || x == '/' || x == '{' || x == ';' || x == ']' || x == ',') 699 700 /* parsing number */ 701 if (isdigit(c)) { 702 do { 703 *p++ = c; 704 if (p == ebuf) { 705 yyerror("too long line"); 706 return ERROR; 707 } 708 } while ((c = lgetc()) != EOF && isdigit(c)); 709 lungetc(); 710 if (c == EOF || allowed_to_end_number(c)) { 711 const char *errstr = NULL; 712 713 *p = '\0'; 714 yylval.v.number = strtonum(buf, LONG_MIN, LONG_MAX, 715 &errstr); 716 if (errstr) { 717 yyerror("invalid number '%s' (%s)", buf, 718 errstr); 719 return ERROR; 720 } 721 return NUMBER; 722 } else { 723 while (p > buf + 1) { 724 --p; 725 lungetc(); 726 } 727 c = *--p; 728 } 729 } 730 731#define allowed_in_string(x) (isalnum(c) || c == '!' || c == '=' || c == '_') 732 733 /* parsing next word */ 734 if (allowed_in_string(c)) { 735 do { 736 *p++ = c; 737 if (p == ebuf) { 738 yyerror("too long line"); 739 return ERROR; 740 } 741 } while ((c = lgetc()) != EOF && (allowed_in_string(c))); 742 lungetc(); 743 *p = '\0'; 744 if ((token = lookup(buf)) == STRING) 745 if ((yylval.v.string = strdup(buf)) == NULL) 746 err(1, "%s", __func__); 747 return token; 748 } 749 750 if (c == '\n') { 751 yylval.lineno++; 752 yylval.colno = 0; 753 } 754 if (c == EOF) 755 return 0; 756 return c; 757} 758 759void 760pprint_syntax_error(void) 761{ 762 char line[BUFSIZ]; 763 int c, indent = yylval.colno; 764 size_t i; 765 766 strlcpy(line, &pbuf[pindex - yylval.colno], sizeof(line)); 767 768 for (i = 0; line[i] != '\0' && (c = line[i]) != '\n'; i++) { 769 if (c == '\t') 770 indent += (8 - 1); 771 fputc(c, stderr); 772 } 773 774 fprintf(stderr, "\n%*c\n", indent, '^'); 775} 776 777void 778yyerror(const char *fmt, ...) 779{ 780 const char *prefix; 781 va_list va; 782 783 prefix = (yylval.filename != NULL) ? yylval.filename : getprogname(); 784 785 fprintf(stderr, "%s:%d:%d: ", prefix, yylval.lineno, yylval.colno); 786 va_start(va, fmt); 787 vfprintf(stderr, fmt, va); 788 va_end(va); 789 fprintf(stderr, ":\n"); 790 791 pprint_syntax_error(); 792 793 perrors++; 794} 795 796int 797btparse(const char *str, size_t len, const char *filename, int debug) 798{ 799 if (debug > 0) 800 yydebug = 1; 801 pbuf = str; 802 plen = len; 803 pindex = 0; 804 yylval.filename = filename; 805 yylval.lineno = 1; 806 807 yyparse(); 808 809 return perrors; 810} 811