1 /* $NetBSD: db_lex.c,v 1.25 2019/11/22 23:01:49 ad Exp $ */ 2 3 /* 4 * Mach Operating System 5 * Copyright (c) 1991,1990 Carnegie Mellon University 6 * All Rights Reserved. 7 * 8 * Permission to use, copy, modify and distribute this software and its 9 * documentation is hereby granted, provided that both the copyright 10 * notice and this permission notice appear in all copies of the 11 * software, derivative works or modified versions, and any portions 12 * thereof, and that both notices appear in supporting documentation. 13 * 14 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 15 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 16 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 17 * 18 * Carnegie Mellon requests users of this software to return to 19 * 20 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 21 * School of Computer Science 22 * Carnegie Mellon University 23 * Pittsburgh PA 15213-3890 24 * 25 * any improvements or extensions that they make and grant Carnegie the 26 * rights to redistribute these changes. 27 * 28 * Author: David B. Golub, Carnegie Mellon University 29 * Date: 7/90 30 */ 31 32 /* 33 * Lexical analyzer. 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: db_lex.c,v 1.25 2019/11/22 23:01:49 ad Exp $"); 38 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/cpu.h> 42 43 #include <ddb/ddb.h> 44 45 db_expr_t db_tok_number; 46 char db_tok_string[TOK_STRING_SIZE]; 47 48 static char db_line[DB_LINE_MAXLEN]; 49 static const char *db_lp; 50 static const char *db_endlp; 51 52 static int db_look_char = 0; 53 static int db_look_token = 0; 54 55 static void db_flush_line(void); 56 static int db_read_char(void); 57 static void db_unread_char(int); 58 static int db_lex(void); 59 60 int 61 db_read_line(void) 62 { 63 int i; 64 65 #ifdef _KERNEL 66 /* 67 * crash(8) prints the prompt using libedit. That's why we used to 68 * print it in db_readline(). But now people are using db_read_line() 69 * for general purpose input, so.. 70 */ 71 #ifdef MULTIPROCESSOR 72 db_printf("db{%ld}> ", (long)cpu_number()); 73 #else 74 db_printf("db> "); 75 #endif 76 #endif 77 i = db_readline(db_line, sizeof(db_line)); 78 if (i == 0) 79 return (0); /* EOI */ 80 db_set_line(db_line, db_line + i); 81 return (i); 82 } 83 84 void 85 db_set_line(const char *sp, const char *ep) 86 { 87 88 db_lp = sp; 89 db_endlp = ep; 90 } 91 92 static void 93 db_flush_line(void) 94 { 95 96 db_lp = db_line; 97 db_endlp = db_line; 98 } 99 100 static int 101 db_read_char(void) 102 { 103 int c; 104 105 if (db_look_char != 0) { 106 c = db_look_char; 107 db_look_char = 0; 108 } 109 else if (db_lp >= db_endlp) 110 c = -1; 111 else 112 c = *db_lp++; 113 return (c); 114 } 115 116 static void 117 db_unread_char(int c) 118 { 119 120 db_look_char = c; 121 } 122 123 void 124 db_unread_token(int t) 125 { 126 127 db_look_token = t; 128 } 129 130 int 131 db_read_token(void) 132 { 133 int t; 134 135 if (db_look_token) { 136 t = db_look_token; 137 db_look_token = 0; 138 } 139 else 140 t = db_lex(); 141 return (t); 142 } 143 144 int db_radix = 16; 145 146 /* 147 * Convert the number to a string in the current radix. 148 * This replaces the non-standard %n printf() format. 149 */ 150 151 char * 152 db_num_to_str(db_expr_t val) 153 { 154 155 /* 156 * 2 chars for "0x", 1 for a sign ("-") 157 * up to 21 chars for a 64-bit number: 158 * % echo 2^64 | bc | wc -c 159 * 21 160 * and 1 char for a terminal NUL 161 * 2+1+21+1 => 25 162 */ 163 static char buf[25]; 164 165 if (db_radix == 16) 166 snprintf(buf, sizeof(buf), "%" DDB_EXPR_FMT "x", val); 167 else if (db_radix == 8) 168 snprintf(buf, sizeof(buf), "%" DDB_EXPR_FMT "o", val); 169 else 170 snprintf(buf, sizeof(buf), "%" DDB_EXPR_FMT "u", val); 171 172 return (buf); 173 } 174 175 void 176 db_flush_lex(void) 177 { 178 179 db_flush_line(); 180 db_look_char = 0; 181 db_look_token = 0; 182 } 183 184 static int 185 db_lex(void) 186 { 187 int c; 188 189 c = db_read_char(); 190 while (c <= ' ' || c > '~') { 191 if (c == '\n' || c == -1) 192 return (tEOL); 193 c = db_read_char(); 194 } 195 196 if (c >= '0' && c <= '9') { 197 /* number */ 198 db_expr_t r, digit = 0; 199 200 if (c > '0') 201 r = db_radix; 202 else { 203 c = db_read_char(); 204 if (c == 'O' || c == 'o') 205 r = 8; 206 else if (c == 'T' || c == 't') 207 r = 10; 208 else if (c == 'X' || c == 'x') 209 r = 16; 210 else { 211 r = db_radix; 212 db_unread_char(c); 213 } 214 c = db_read_char(); 215 } 216 db_tok_number = 0; 217 for (;;) { 218 if (c >= '0' && c <= ((r == 8) ? '7' : '9')) 219 digit = c - '0'; 220 else if (r == 16) { 221 if (c >= 'A' && c <= 'F') 222 digit = c - 'A' + 10; 223 else if (c >= 'a' && c <= 'f') 224 digit = c - 'a' + 10; 225 else 226 break; 227 } else 228 break; 229 db_tok_number = db_tok_number * r + digit; 230 c = db_read_char(); 231 } 232 if ((c >= '0' && c <= '9') || 233 (c >= 'A' && c <= 'Z') || 234 (c >= 'a' && c <= 'z') || 235 (c == '_')) { 236 db_error("Bad character in number\n"); 237 /*NOTREACHED*/ 238 } 239 db_unread_char(c); 240 return (tNUMBER); 241 } 242 if ((c >= 'A' && c <= 'Z') || 243 (c >= 'a' && c <= 'z') || 244 c == '_' || c == '\\') { 245 /* string */ 246 char *cp; 247 248 cp = db_tok_string; 249 if (c == '\\') { 250 c = db_read_char(); 251 if (c == '\n' || c == -1) { 252 db_error("Bad escape\n"); 253 /*NOTREACHED*/ 254 } 255 } 256 *cp++ = c; 257 while (1) { 258 c = db_read_char(); 259 if ((c >= 'A' && c <= 'Z') || 260 (c >= 'a' && c <= 'z') || 261 (c >= '0' && c <= '9') || 262 c == '_' || c == '\\' || c == ':') { 263 if (c == '\\') { 264 c = db_read_char(); 265 if (c == '\n' || c == -1) { 266 db_error("Bad escape\n"); 267 /*NOTREACHED*/ 268 } 269 } 270 *cp++ = c; 271 if (cp == db_tok_string+sizeof(db_tok_string)) { 272 db_error("String too long\n"); 273 /*NOTREACHED*/ 274 } 275 continue; 276 } else { 277 *cp = '\0'; 278 break; 279 } 280 } 281 db_unread_char(c); 282 return (tIDENT); 283 } 284 285 switch (c) { 286 case '+': 287 return (tPLUS); 288 case '-': 289 return (tMINUS); 290 case '.': 291 c = db_read_char(); 292 if (c == '.') 293 return (tDOTDOT); 294 db_unread_char(c); 295 return (tDOT); 296 case '*': 297 return (tSTAR); 298 case '/': 299 return (tSLASH); 300 case '=': 301 return (tEQ); 302 case '%': 303 return (tPCT); 304 case '#': 305 return (tHASH); 306 case '(': 307 return (tLPAREN); 308 case ')': 309 return (tRPAREN); 310 case ',': 311 return (tCOMMA); 312 case '"': 313 return (tDITTO); 314 case '$': 315 return (tDOLLAR); 316 case '!': 317 return (tEXCL); 318 case '<': 319 c = db_read_char(); 320 if (c == '<') 321 return (tSHIFT_L); 322 db_unread_char(c); 323 break; 324 case '>': 325 c = db_read_char(); 326 if (c == '>') 327 return (tSHIFT_R); 328 db_unread_char(c); 329 break; 330 case -1: 331 return (tEOF); 332 } 333 db_printf("Bad character\n"); 334 db_flush_lex(); 335 return (tEOF); 336 } 337 338 /* 339 * Utility routine - discard tokens through end-of-line. 340 */ 341 void 342 db_skip_to_eol(void) 343 { 344 int t; 345 346 do { 347 t = db_read_token(); 348 } while (t != tEOL); 349 } 350 351 void 352 db_error(const char *s) 353 { 354 355 if (s) 356 db_printf("%s", s); 357 db_flush_lex(); 358 longjmp(db_recover); 359 } 360