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