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