db_lex.c revision 1.24 1 /* $NetBSD: db_lex.c,v 1.24 2019/10/02 09:36:30 rin 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 (at) 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.24 2019/10/02 09:36:30 rin 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 MULTIPROCESSOR
66 db_printf("db{%ld}> ", (long)cpu_number());
67 #else
68 db_printf("db> ");
69 #endif
70 i = db_readline(db_line, sizeof(db_line));
71 if (i == 0)
72 return (0); /* EOI */
73 db_set_line(db_line, db_line + i);
74 return (i);
75 }
76
77 void
78 db_set_line(const char *sp, const char *ep)
79 {
80
81 db_lp = sp;
82 db_endlp = ep;
83 }
84
85 static void
86 db_flush_line(void)
87 {
88
89 db_lp = db_line;
90 db_endlp = db_line;
91 }
92
93 static int
94 db_read_char(void)
95 {
96 int c;
97
98 if (db_look_char != 0) {
99 c = db_look_char;
100 db_look_char = 0;
101 }
102 else if (db_lp >= db_endlp)
103 c = -1;
104 else
105 c = *db_lp++;
106 return (c);
107 }
108
109 static void
110 db_unread_char(int c)
111 {
112
113 db_look_char = c;
114 }
115
116 void
117 db_unread_token(int t)
118 {
119
120 db_look_token = t;
121 }
122
123 int
124 db_read_token(void)
125 {
126 int t;
127
128 if (db_look_token) {
129 t = db_look_token;
130 db_look_token = 0;
131 }
132 else
133 t = db_lex();
134 return (t);
135 }
136
137 int db_radix = 16;
138
139 /*
140 * Convert the number to a string in the current radix.
141 * This replaces the non-standard %n printf() format.
142 */
143
144 char *
145 db_num_to_str(db_expr_t val)
146 {
147
148 /*
149 * 2 chars for "0x", 1 for a sign ("-")
150 * up to 21 chars for a 64-bit number:
151 * % echo 2^64 | bc | wc -c
152 * 21
153 * and 1 char for a terminal NUL
154 * 2+1+21+1 => 25
155 */
156 static char buf[25];
157
158 if (db_radix == 16)
159 snprintf(buf, sizeof(buf), "%" DDB_EXPR_FMT "x", val);
160 else if (db_radix == 8)
161 snprintf(buf, sizeof(buf), "%" DDB_EXPR_FMT "o", val);
162 else
163 snprintf(buf, sizeof(buf), "%" DDB_EXPR_FMT "u", val);
164
165 return (buf);
166 }
167
168 void
169 db_flush_lex(void)
170 {
171
172 db_flush_line();
173 db_look_char = 0;
174 db_look_token = 0;
175 }
176
177 static int
178 db_lex(void)
179 {
180 int c;
181
182 c = db_read_char();
183 while (c <= ' ' || c > '~') {
184 if (c == '\n' || c == -1)
185 return (tEOL);
186 c = db_read_char();
187 }
188
189 if (c >= '0' && c <= '9') {
190 /* number */
191 db_expr_t r, digit = 0;
192
193 if (c > '0')
194 r = db_radix;
195 else {
196 c = db_read_char();
197 if (c == 'O' || c == 'o')
198 r = 8;
199 else if (c == 'T' || c == 't')
200 r = 10;
201 else if (c == 'X' || c == 'x')
202 r = 16;
203 else {
204 r = db_radix;
205 db_unread_char(c);
206 }
207 c = db_read_char();
208 }
209 db_tok_number = 0;
210 for (;;) {
211 if (c >= '0' && c <= ((r == 8) ? '7' : '9'))
212 digit = c - '0';
213 else if (r == 16) {
214 if (c >= 'A' && c <= 'F')
215 digit = c - 'A' + 10;
216 else if (c >= 'a' && c <= 'f')
217 digit = c - 'a' + 10;
218 else
219 break;
220 } else
221 break;
222 db_tok_number = db_tok_number * r + digit;
223 c = db_read_char();
224 }
225 if ((c >= '0' && c <= '9') ||
226 (c >= 'A' && c <= 'Z') ||
227 (c >= 'a' && c <= 'z') ||
228 (c == '_')) {
229 db_error("Bad character in number\n");
230 /*NOTREACHED*/
231 }
232 db_unread_char(c);
233 return (tNUMBER);
234 }
235 if ((c >= 'A' && c <= 'Z') ||
236 (c >= 'a' && c <= 'z') ||
237 c == '_' || c == '\\') {
238 /* string */
239 char *cp;
240
241 cp = db_tok_string;
242 if (c == '\\') {
243 c = db_read_char();
244 if (c == '\n' || c == -1) {
245 db_error("Bad escape\n");
246 /*NOTREACHED*/
247 }
248 }
249 *cp++ = c;
250 while (1) {
251 c = db_read_char();
252 if ((c >= 'A' && c <= 'Z') ||
253 (c >= 'a' && c <= 'z') ||
254 (c >= '0' && c <= '9') ||
255 c == '_' || c == '\\' || c == ':') {
256 if (c == '\\') {
257 c = db_read_char();
258 if (c == '\n' || c == -1) {
259 db_error("Bad escape\n");
260 /*NOTREACHED*/
261 }
262 }
263 *cp++ = c;
264 if (cp == db_tok_string+sizeof(db_tok_string)) {
265 db_error("String too long\n");
266 /*NOTREACHED*/
267 }
268 continue;
269 } else {
270 *cp = '\0';
271 break;
272 }
273 }
274 db_unread_char(c);
275 return (tIDENT);
276 }
277
278 switch (c) {
279 case '+':
280 return (tPLUS);
281 case '-':
282 return (tMINUS);
283 case '.':
284 c = db_read_char();
285 if (c == '.')
286 return (tDOTDOT);
287 db_unread_char(c);
288 return (tDOT);
289 case '*':
290 return (tSTAR);
291 case '/':
292 return (tSLASH);
293 case '=':
294 return (tEQ);
295 case '%':
296 return (tPCT);
297 case '#':
298 return (tHASH);
299 case '(':
300 return (tLPAREN);
301 case ')':
302 return (tRPAREN);
303 case ',':
304 return (tCOMMA);
305 case '"':
306 return (tDITTO);
307 case '$':
308 return (tDOLLAR);
309 case '!':
310 return (tEXCL);
311 case '<':
312 c = db_read_char();
313 if (c == '<')
314 return (tSHIFT_L);
315 db_unread_char(c);
316 break;
317 case '>':
318 c = db_read_char();
319 if (c == '>')
320 return (tSHIFT_R);
321 db_unread_char(c);
322 break;
323 case -1:
324 return (tEOF);
325 }
326 db_printf("Bad character\n");
327 db_flush_lex();
328 return (tEOF);
329 }
330
331 /*
332 * Utility routine - discard tokens through end-of-line.
333 */
334 void
335 db_skip_to_eol(void)
336 {
337 int t;
338
339 do {
340 t = db_read_token();
341 } while (t != tEOL);
342 }
343
344 void
345 db_error(const char *s)
346 {
347
348 if (s)
349 db_printf("%s", s);
350 db_flush_lex();
351 longjmp(db_recover);
352 }
353