api_mode.c (10668B)
1 /* 2 * 3 * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file. 4 * 5 * Copyright 2007-2019 Broadcom Inc. All rights reserved. 6 * 7 * File: api_mode.c 8 * Purpose: API mode grammar implementation 9 */ 10 11 #include "sal/core/alloc.h" 12 #include "sal/core/libc.h" 13 #include "sal/appl/io.h" 14 #include "tokenizer.h" 15 #include "context.h" 16 #include "completion.h" 17 #include "api_mode_yy.h" 18 #include "api_grammar.tab.h" 19 20 21 /* 22 * API Mode grammar handler functions 23 */ 24 25 26 /* allocate a grammar node */ 27 STATIC api_mode_arg_t * 28 alloc_node(api_mode_parse_t *prs, 29 const char *value, int kind, 30 api_mode_token_t *token, const cint_datatype_t *dt) 31 { 32 api_mode_arg_t *arg; 33 34 arg = sal_alloc(sizeof(*arg),"api_mode"); 35 if (!arg) { 36 return NULL; 37 } 38 sal_memset(arg,0,sizeof(*arg)); 39 arg->kind = kind; 40 arg->value = sal_strdup(value); 41 arg->token = token; 42 arg->dt = dt; 43 arg->mm = prs->root; 44 prs->root = arg; 45 if (prs->base == NULL) { 46 prs->base = arg; 47 } 48 49 return arg; 50 } 51 52 /* free a grammar node */ 53 STATIC void 54 free_node(api_mode_arg_t *arg) 55 { 56 sal_free((void *)arg->value); 57 sal_memset(arg, 0, sizeof(*arg)); 58 sal_free(arg); 59 } 60 61 /* free a list of grammar nodes */ 62 STATIC void 63 free_node_list(api_mode_arg_t *arg) 64 { 65 api_mode_arg_t *next; 66 while (arg) { 67 next = arg->mm; 68 free_node(arg); 69 arg = next; 70 } 71 } 72 73 /* return a string corresponding to a node kind id */ 74 STATIC char * 75 node_kind(int x) 76 { 77 char *s = "???"; 78 79 switch (x) { 80 case IDENT: s="IDENT"; break; 81 case KEY: s="KEY"; break; 82 case CONSTANT: s="CONSTANT"; break; 83 case AGGREGATE: s="{}"; break; 84 case PROMPT: s="PROMPT"; break; 85 case EMPTY: s="<>"; break; 86 case KEY_VALUE: s="="; break; 87 case RANGE: s="RANGE"; break; 88 case ITEM: s="ITEM"; break; 89 case ASSIGN: s="ASSIGN"; break; 90 case VALUE: s="VALUE"; break; 91 case PRINT: s="PRINT"; break; 92 case VAR: s="VAR"; break; 93 case CREATE: s="CREATE"; break; 94 case '!': s="INFO"; break; 95 case '=': s="="; break; 96 case ';': s=";"; break; 97 case '?': s="?"; break; 98 case '.': s="."; break; 99 case ',': s=","; break; 100 case '{': s="{"; break; 101 case '}': s="}"; break; 102 } 103 104 return s; 105 } 106 107 /* print node information */ 108 STATIC void 109 show_node(int indent, api_mode_arg_t *arg) 110 { 111 sal_printf("%*s%s %s %s\n", 112 indent*2,"", 113 node_kind(arg->kind), 114 arg->value, 115 arg->flags&IS_FIRST?"+":""); 116 } 117 118 /* set the parent for a list of child nodes */ 119 STATIC void 120 set_parent(api_mode_arg_t *arg, api_mode_arg_t *parent) 121 { 122 for (; arg; arg=arg->next) { 123 arg->parent = parent; 124 } 125 } 126 127 /* print node information for a tree of nodes */ 128 void 129 api_mode_show(int indent, api_mode_arg_t *start) 130 { 131 api_mode_arg_t *arg; 132 133 for (arg=start; arg; arg=arg->next) { 134 show_node(indent,arg); 135 if (arg->sub) { 136 api_mode_show(indent+1, arg->sub); 137 } 138 } 139 } 140 141 /* allocate a node of a certain kind with a value */ 142 api_mode_arg_t * 143 api_mode_node(void *prs, const char *value, int kind) 144 { 145 return alloc_node((api_mode_parse_t *)prs, value, kind, NULL, NULL); 146 } 147 148 /* execute a grammar tree via a parser callback */ 149 api_mode_arg_t * 150 api_mode_execute(void *p, api_mode_arg_t *arg) 151 { 152 api_mode_parse_t *prs = (api_mode_parse_t *)p; 153 154 if (prs->verbose) { 155 prs->result = prs->callback(arg, prs->user_data); 156 } 157 158 return arg; 159 } 160 161 /* set node flags */ 162 api_mode_arg_t * 163 api_mode_mark(api_mode_arg_t *arg, int flag) 164 { 165 arg->flags |= flag; 166 167 return arg; 168 } 169 170 /* set 'sec' as a sublist of 'pri' */ 171 api_mode_arg_t * 172 api_mode_sub(api_mode_arg_t *pri, api_mode_arg_t *sec) 173 { 174 pri->sub = sec; 175 set_parent(sec, pri); 176 177 return pri; 178 } 179 180 /* create a key/value node */ 181 api_mode_arg_t * 182 api_mode_key_value(api_mode_arg_t *key, api_mode_arg_t *value) 183 { 184 key->kind = KEY_VALUE; 185 api_mode_sub(key, value); 186 return key; 187 } 188 189 /* create a range node */ 190 api_mode_arg_t * 191 api_mode_range(api_mode_arg_t *from, api_mode_arg_t *to, 192 api_mode_arg_t *times, api_mode_arg_t *incr) 193 { 194 from->kind = RANGE; 195 api_mode_append(incr, times); 196 api_mode_append(times, to); 197 api_mode_sub(from, to); 198 return from; 199 } 200 201 /* append 'arg' to node list 'src' */ 202 api_mode_arg_t * 203 api_mode_append(api_mode_arg_t *src, api_mode_arg_t *arg) 204 { 205 api_mode_arg_t *target; 206 207 for (target=src; target->next; target=target->next) { 208 if (target == target->next) { 209 (void)api_mode_unexpected(); 210 break; 211 } 212 } 213 214 target->next = arg; 215 if (src->parent != NULL && arg->parent == NULL) { 216 arg->parent = src->parent; 217 } 218 219 return src; 220 } 221 222 /* append 'arg' to child node list of 'src' */ 223 api_mode_arg_t * 224 api_mode_sub_append(api_mode_arg_t *src, api_mode_arg_t *arg) 225 { 226 if (src->sub == NULL) { 227 src->sub = arg; 228 } else { 229 api_mode_append(src->sub, arg); 230 } 231 set_parent(arg, src); 232 233 return src; 234 } 235 236 /* display a parse error */ 237 void 238 api_mode_error(yyltype *loc, yyscan_t yyscanner, void *p, char const *s) 239 { 240 api_mode_parse_t *prs = (api_mode_parse_t *)p; 241 242 COMPILER_REFERENCE(loc); 243 COMPILER_REFERENCE(yyscanner); 244 sal_printf("%s\n", s); 245 prs->done = 1; 246 } 247 248 /* return the next node after 'arg'. if arg is the last child node, 249 recurse back up to the next parent node. */ 250 api_mode_arg_t * 251 api_mode_arg_next(api_mode_arg_t *arg) 252 { 253 api_mode_arg_t *next = NULL; 254 255 if (arg->next) { 256 next = arg->next; 257 } else if (arg->parent) { 258 next = api_mode_arg_next(arg->parent); 259 } 260 261 return next; 262 } 263 264 265 /* Pull parser lex function - 266 not used, but still referenced by the pull parser */ 267 268 int 269 api_mode_lex(YYSTYPE *lvalp, YYLTYPE *llocp, yyscan_t scanner) 270 { 271 COMPILER_REFERENCE(lvalp); 272 COMPILER_REFERENCE(llocp); 273 COMPILER_REFERENCE(scanner); 274 275 return api_mode_unexpected(); 276 } 277 278 /* show scanner tokens for diagnostic purposes */ 279 STATIC void 280 show_scanner_tokens(yyscan_t scanner, api_mode_context_t *ctx) 281 { 282 int i; 283 284 for (i=0; i<scanner->tok.len; i++) { 285 sal_printf("%s ", node_kind(ctx->info[i].grammar_type)); 286 } 287 sal_printf("\n"); 288 } 289 290 /* 291 * 292 * API Mode parser 293 */ 294 295 /* 296 parse the output of the scanner 297 */ 298 STATIC int 299 api_mode_parser(yyscan_t scanner, api_mode_context_t *ctx, int flags, 300 api_mode_arg_cb_t cb, void *user_data) 301 { 302 int status; 303 api_mode_pstate *ps; 304 enum yytokentype code; 305 api_mode_parse_t parser; 306 api_mode_arg_t *yylval; 307 yyltype_t yylloc; 308 api_mode_token_t *token; 309 const cint_datatype_t *dt; 310 311 /* Setup parser context */ 312 sal_memset(&parser, 0, sizeof(parser)); 313 314 if (flags & PARSE_VERBOSE) { 315 parser.verbose = 1; 316 } 317 318 if (flags & PARSE_DEBUG) { 319 api_mode_debug = 1; 320 } 321 322 parser.callback = cb; 323 parser.user_data = user_data; 324 scanner->idx = 0; 325 status = 0; 326 327 if (api_mode_debug) { 328 show_scanner_tokens(scanner, ctx); 329 } 330 /* Call scanner and parser */ 331 ps = api_mode_pstate_new(); 332 do { 333 if (scanner->idx < scanner->tok.len) { 334 token = &scanner->tok.token[scanner->idx]; 335 code = ctx->info[scanner->idx].grammar_type; 336 dt = ctx->info[scanner->idx].dt; 337 yylval = alloc_node(&parser, token->str, code, token, dt); 338 if (yylval == NULL) { 339 status = API_MODE_E_FAIL; 340 break; 341 } 342 /* location */ 343 yylloc.first_line = token->first_line; 344 yylloc.first_column = token->first_column; 345 yylloc.last_line = token->last_line; 346 yylloc.last_column = token->last_column; 347 /* next token */ 348 scanner->idx++; 349 } else { 350 /* no more tokens; signal end-of-input */ 351 code = 0; 352 yylval = NULL; 353 yylloc.first_line = -1; 354 yylloc.first_column = -1; 355 yylloc.last_line = -1; 356 yylloc.last_column = -1; 357 } 358 359 status = api_mode_push_parse(ps, code, &yylval, &yylloc, 360 scanner, &parser); 361 362 } while (status == YYPUSH_MORE); 363 api_mode_pstate_delete(ps); 364 365 /* free resources */ 366 free_node_list(parser.root); 367 368 if (scanner->idx < scanner->tok.len) { 369 printf("api_mode_parser: %d tokens left over\n", 370 scanner->tok.len-scanner->idx); 371 } 372 373 return status; 374 } 375 376 /* 377 Parse an API mode string. This is the top level interface to the 378 API mode parser. 379 380 The string 'input' is parsed, and the callback is called when the 381 parse input is exhausted. 382 383 In practice, this function is called either in some sort of 384 completion context, where the callback is used to facilitate command 385 or argument completion, or an execution context, where the callback 386 is used to execute the parsed string. 387 388 In principle, it's possible to reuse parse information from a 389 completion, it's not clear that it's worth the complexity, so 390 completion and execution are considered separate contexts, where 391 completion just facilitates building up the command line, and 392 execution parses the final result. This assumes that parsing is not 393 very expensive. If it is, then some sort of caching between completion 394 and execution will be needed. 395 396 */ 397 int 398 api_mode_parse_string(const char *input, int flags, 399 api_mode_arg_cb_t cb, 400 void *user_data) 401 { 402 int rv = API_MODE_E_FAIL; 403 api_mode_scanner_t scan; 404 api_mode_context_t ctx; 405 api_mode_completion_type_t ctype; 406 407 rv = api_mode_tokenizer(input, &scan.tok); 408 if (!rv) { 409 /* tokenized OK, now contextualize */ 410 if (scan.tok.len > 0) { 411 rv = api_mode_contextualizer(&scan.tok, &ctx); 412 if (!rv) { 413 /* ignore errors at this point, because if the parser errors 414 out, don't pass the command to the shell. */ 415 ctype = api_mode_completion_set(api_mode_completion_none); 416 (void)api_mode_parser(&scan, &ctx, flags, cb, user_data); 417 (void)api_mode_completion_set(ctype); 418 } 419 api_mode_contextualizer_free(&ctx); 420 } 421 } 422 423 (void)api_mode_tokenizer_free(&scan.tok); 424 425 return rv; 426 } 427 428 /* called for unexpected conditions */ 429 int 430 api_mode_unexpected(void) 431 { 432 sal_printf("Unexpected condition.\n"); 433 434 return API_MODE_E_INTERNAL; 435 } 436