xref: /haiku/src/system/kernel/debug/debug_parser.cpp (revision b55a57da7173b9af0432bd3e148d03f06161d036)
1 /*
2  * Copyright 2008-2009, Ingo Weinhold, ingo_weinhold@gmx.de
3  * Copyright 2006, Stephan Aßmus, superstippi@gmx.de
4  * Distributed under the terms of the MIT License.
5  */
6 
7 
8 #include <debug.h>
9 
10 #include <ctype.h>
11 #include <setjmp.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 
16 #include <KernelExport.h>
17 
18 #include <debug_heap.h>
19 
20 #include "debug_commands.h"
21 #include "debug_variables.h"
22 
23 
24 /*
25 	Grammar:
26 
27 	commandLine	:= commandPipe | ( "(" expression ")" )
28 	expression	:= term | assignment
29 	assignment	:= lhs ( "=" | "+=" | "-=" | "*=" | "/=" | "%=" )
30 				   expression
31 	lhs			:= variable | dereference
32 	term		:= sum
33 	sum			:= product ( ( "+" | "-" ) product )*
34 	product		:= unary ( ( "*" | "/" | "%" ) unary )*
35 	unary		:= atom | ( "-"  unary ) | dereference
36 	dereference	:= "*" [ "{" expression "}" ] unary
37 	atom		:= variable | ( "(" expression ")" ) | ( "[" command "]" )
38 	variable	:= identifier
39 	identifier	:= ( "$" | "@" | "_" | "a" - "z" | "A" - "Z" )
40 				   ( "_" | "a" - "z" | "A" - "Z" | "0" - "9" )*
41 	commandPipe	:= command ( "|" command )*
42 	command		:= identifier argument*
43 	argument	:= ( "(" expression ")" ) | ( "[" commandLine "]" )
44 				   | unquotedString | quotedString
45 */
46 
47 
48 static const int kMaxTokenLength = 128;
49 static const int kJumpBufferCount = 10;
50 
51 static const int kMaxArgumentCount = 64;
52 
53 static jmp_buf sJumpBuffers[kJumpBufferCount];
54 static int sNextJumpBufferIndex = 0;
55 
56 static char sExceptionMessage[128];
57 static int	sExceptionPosition;
58 
59 static char sTempBuffer[128];
60 	// for composing debug output etc.
61 
62 enum {
63 	TOKEN_ASSIGN_FLAG			= 0x100,
64 	TOKEN_FLAGS					= TOKEN_ASSIGN_FLAG,
65 
66 	TOKEN_IDENTIFIER			= 'a',
67 
68 	TOKEN_CONSTANT				= '0',
69 
70 	TOKEN_PLUS					= '+',
71 	TOKEN_MINUS					= '-',
72 
73 	TOKEN_STAR					= '*',
74 	TOKEN_SLASH					= '/',
75 	TOKEN_MODULO				= '%',
76 
77 	TOKEN_ASSIGN				= '='			| TOKEN_ASSIGN_FLAG,
78 	TOKEN_PLUS_ASSIGN			= TOKEN_PLUS	| TOKEN_ASSIGN_FLAG,
79 	TOKEN_MINUS_ASSIGN			= TOKEN_MINUS	| TOKEN_ASSIGN_FLAG,
80 	TOKEN_STAR_ASSIGN			= TOKEN_STAR	| TOKEN_ASSIGN_FLAG,
81 	TOKEN_SLASH_ASSIGN			= TOKEN_SLASH	| TOKEN_ASSIGN_FLAG,
82 	TOKEN_MODULO_ASSIGN			= TOKEN_MODULO	| TOKEN_ASSIGN_FLAG,
83 
84 	TOKEN_OPENING_PARENTHESIS	= '(',
85 	TOKEN_CLOSING_PARENTHESIS	= ')',
86 	TOKEN_OPENING_BRACKET		= '[',
87 	TOKEN_CLOSING_BRACKET		= ']',
88 	TOKEN_OPENING_BRACE			= '{',
89 	TOKEN_CLOSING_BRACE			= '}',
90 
91 	TOKEN_PIPE					= '|',
92 
93 	TOKEN_STRING				= '"',
94 	TOKEN_UNKNOWN				= '?',
95 	TOKEN_NONE					= ' ',
96 	TOKEN_END_OF_LINE			= '\n',
97 };
98 
99 struct Token {
100 	char	string[kMaxTokenLength];
101 	uint64	value;
102 	int32	type;
103 	int32	position;
104 
105 	void SetTo(const char* string, int32 length, int32 position, int32 type)
106 	{
107 		length = min_c((size_t)length, (sizeof(this->string) - 1));
108 		strlcpy(this->string, string, length + 1);
109 		this->type = type;
110 		this->value = 0;
111 		this->position = position;
112 	}
113 
114 	void Unset()
115 	{
116 		string[0] = '\0';
117 		value = 0;
118 		type = TOKEN_NONE;
119 		position = 0;
120 	}
121 };
122 
123 
124 // #pragma mark - exceptions
125 
126 
127 static void
128 parse_exception(const char* message, int32 position)
129 {
130 	if (sNextJumpBufferIndex == 0) {
131 		kprintf_unfiltered("parse_exception(): No jump buffer!\n");
132 		kprintf_unfiltered("exception: \"%s\", position: %lu\n", message,
133 			position);
134 		return;
135 	}
136 
137 	strlcpy(sExceptionMessage, message, sizeof(sExceptionMessage));
138 	sExceptionPosition = position;
139 
140 	longjmp(sJumpBuffers[sNextJumpBufferIndex - 1], 1);
141 }
142 
143 
144 static void*
145 checked_malloc(size_t size)
146 {
147 	void* address = debug_malloc(size);
148 	if (address == NULL) {
149 		parse_exception("out of memory for command execution", -1);
150 		return NULL;
151 	}
152 
153 	return address;
154 }
155 
156 
157 // #pragma mark - Tokenizer
158 
159 
160 class Tokenizer {
161 public:
162 	Tokenizer(const char* string)
163 		: fCommandMode(false)
164 	{
165 		SetTo(string);
166 	}
167 
168 	void SetTo(const char* string)
169 	{
170 		fString = fCurrentChar = string;
171 		fCurrentToken.Unset();
172 		fReuseToken = false;
173 	}
174 
175 	void SetPosition(int32 position)
176 	{
177 		fCurrentChar = fString + position;
178 		fCurrentToken.Unset();
179 		fReuseToken = false;
180 	}
181 
182 	void SetCommandMode(bool commandMode)
183 	{
184 		fCommandMode = commandMode;
185 	}
186 
187 	const char* String() const
188 	{
189 		return fString;
190 	}
191 
192 	const Token& NextToken()
193 	{
194 		if (fCurrentToken.type == TOKEN_END_OF_LINE)
195 			return fCurrentToken;
196 
197 		if (fReuseToken) {
198 			fReuseToken = false;
199 			return fCurrentToken;
200 		}
201 
202 		while (*fCurrentChar != 0 && isspace(*fCurrentChar))
203 			fCurrentChar++;
204 
205 		if (*fCurrentChar == 0) {
206 			fCurrentToken.SetTo("", 0, _CurrentPos(), TOKEN_END_OF_LINE);
207 			return fCurrentToken;
208 		}
209 
210 		return (fCommandMode ? _NextTokenCommand() : _NextTokenExpression());
211 	}
212 
213 	const Token& CurrentToken() const
214 	{
215 		return fCurrentToken;
216 	}
217 
218 	void RewindToken()
219 	{
220 		fReuseToken = true;
221 	}
222 
223  private:
224 	const Token& _NextTokenExpression()
225 	{
226 		if (isdigit(*fCurrentChar)) {
227 			// number
228 			const char* begin = fCurrentChar++;
229 
230 			if (*fCurrentChar == 'x') {
231 				// hex number
232 				fCurrentChar++;
233 				while (*fCurrentChar != 0
234 					&& (isdigit(*fCurrentChar)
235 						|| strchr("abcdefABCDEF", *fCurrentChar))) {
236 					fCurrentChar++;
237 				}
238 
239 				if (fCurrentChar - begin == 2)
240 					parse_exception("invalid hex number", begin - fString);
241 
242 			} else {
243 				// decimal number
244 				while (*fCurrentChar != 0 && isdigit(*fCurrentChar))
245 					fCurrentChar++;
246 			}
247 
248 			int32 length = fCurrentChar - begin;
249 			fCurrentToken.SetTo(begin, length, _CurrentPos() - length,
250 				TOKEN_CONSTANT);
251 			fCurrentToken.value = strtoull(fCurrentToken.string, NULL, 0);
252 
253 		} else if (isalpha(*fCurrentChar) || *fCurrentChar == '_'
254 				|| *fCurrentChar == '$' || *fCurrentChar == '@') {
255 			// identifier
256 			const char* begin = fCurrentChar;
257 			fCurrentChar++;
258 			while (*fCurrentChar != 0
259 				&& (isalpha(*fCurrentChar) || *fCurrentChar == '_'
260 					|| isdigit(*fCurrentChar))) {
261 				fCurrentChar++;
262 			}
263 
264 			int32 length = fCurrentChar - begin;
265 			fCurrentToken.SetTo(begin, length, _CurrentPos() - length,
266 				TOKEN_IDENTIFIER);
267 
268 		} else {
269 			const char* begin = fCurrentChar;
270 			char c = *fCurrentChar;
271 			fCurrentChar++;
272 			int32 flags = 0;
273 
274 			switch (c) {
275 				case '=':
276 					fCurrentChar--;
277 				case '+':
278 				case '-':
279 				case '*':
280 				case '/':
281 				case '%':
282 					if (*fCurrentChar == '=') {
283 						fCurrentChar++;
284 						flags = TOKEN_ASSIGN_FLAG;
285 					}
286 
287 				case '(':
288 				case ')':
289 				case '[':
290 				case ']':
291 				case '{':
292 				case '}':
293 				{
294 					int32 length = fCurrentChar - begin;
295 					fCurrentToken.SetTo(begin, length, _CurrentPos() - length,
296 						c | flags);
297 					break;
298 				}
299 
300 				case '"':
301 				{
302 					fCurrentChar--;
303 					_QuotedString();
304 					break;
305 				}
306 
307 				default:
308 				{
309 					fCurrentChar--;
310 					_UnquotedString();
311 					break;
312 				}
313 			}
314 		}
315 
316 		return fCurrentToken;
317 	}
318 
319 	const Token& _NextTokenCommand()
320 	{
321 		switch (*fCurrentChar) {
322 			case '(':
323 			case ')':
324 			case '[':
325 			case ']':
326 			case '|':
327 				fCurrentToken.SetTo(fCurrentChar, 1, _CurrentPos(),
328 					*fCurrentChar);
329 				fCurrentChar++;
330 				return fCurrentToken;
331 			case '"':
332 				return _QuotedString();
333 
334 			default:
335 				return _UnquotedString();
336 		}
337 	}
338 
339 	const Token& _QuotedString()
340 	{
341 		const char* begin = fCurrentChar++;
342 		int32 length = 0;
343 
344 		while (*fCurrentChar != '\0' && *fCurrentChar != '"') {
345 			char c = *fCurrentChar;
346 			fCurrentChar++;
347 
348 			if (c == '\\') {
349 				// an escaped char
350 				c = *fCurrentChar;
351 				fCurrentChar++;
352 
353 				if (c == '\0')
354 					break;
355 			}
356 
357 			if ((size_t)length
358 					>= sizeof(fCurrentToken.string) - 1) {
359 				parse_exception("quoted string too long", begin - fString);
360 			}
361 
362 			fCurrentToken.string[length++] = c;
363 		}
364 
365 		if (*fCurrentChar == '\0') {
366 			parse_exception("unexpected end of line while "
367 				"parsing quoted string", begin - fString);
368 		}
369 
370 		fCurrentChar++;
371 
372 		fCurrentToken.string[length] = '\0';
373 		fCurrentToken.value = 0;
374 		fCurrentToken.type = TOKEN_STRING;
375 		fCurrentToken.position = begin - fString;
376 
377 		return fCurrentToken;
378 	}
379 
380 	const Token& _UnquotedString()
381 	{
382 		const char* begin = fCurrentChar;
383 
384 		while (*fCurrentChar != 0 && !_IsUnquotedDelimitingChar(*fCurrentChar))
385 			fCurrentChar++;
386 
387 		int32 length = fCurrentChar - begin;
388 		fCurrentToken.SetTo(begin, length, _CurrentPos() - length,
389 			TOKEN_UNKNOWN);
390 
391 		return fCurrentToken;
392 	}
393 
394 	bool _IsUnquotedDelimitingChar(char c)
395 	{
396 		if (isspace(c))
397 			return true;
398 
399 		switch (c) {
400 			case '(':
401 			case ')':
402 			case '[':
403 			case ']':
404 			case '"':
405 				return true;
406 
407 			case '{':
408 			case '}':
409 			case '=':
410 			case '+':
411 			case '-':
412 			case '*':
413 			case '/':
414 			case '%':
415 				return !fCommandMode;
416 
417 			default:
418 				return false;
419 		}
420 	}
421 
422 	int32 _CurrentPos() const
423 	{
424 		return fCurrentChar - fString;
425 	}
426 
427 private:
428 	const char*	fString;
429 	const char*	fCurrentChar;
430 	Token		fCurrentToken;
431 	bool		fReuseToken;
432 	bool		fCommandMode;
433 };
434 
435 
436 // #pragma mark - ExpressionParser
437 
438 
439 class ExpressionParser {
440  public:
441 								ExpressionParser();
442 								~ExpressionParser();
443 
444 			uint64				EvaluateExpression(
445 									const char* expressionString);
446 			uint64				EvaluateCommand(
447 									const char* expressionString,
448 									int& returnCode);
449 			status_t			ParseNextCommandArgument(
450 									const char** expressionString, char* buffer,
451 									size_t bufferSize);
452 
453  private:
454 			uint64				_ParseExpression(bool expectAssignment = false);
455 			uint64				_ParseCommandPipe(int& returnCode);
456 			void				_ParseCommand(
457 									debugger_command_pipe_segment& segment);
458 			bool				_ParseArgument(int& argc, char** argv);
459 			void				_GetUnparsedArgument(int& argc, char** argv);
460 			void				_AddArgument(int& argc, char** argv,
461 									const char* argument, int32 length = -1);
462 			uint64				_ParseSum(bool useValue, uint64 value);
463 			uint64				_ParseProduct();
464 			uint64				_ParsePower();
465 			uint64				_ParseUnary();
466 			uint64				_ParseDereference(void** _address,
467 									uint32* _size);
468 			uint64				_ParseAtom();
469 
470 			const Token&		_EatToken(int32 type);
471 
472 			Tokenizer			fTokenizer;
473 };
474 
475 
476 ExpressionParser::ExpressionParser()
477 	: fTokenizer("")
478 {
479 }
480 
481 
482 ExpressionParser::~ExpressionParser()
483 {
484 }
485 
486 
487 uint64
488 ExpressionParser::EvaluateExpression(const char* expressionString)
489 {
490 	fTokenizer.SetTo(expressionString);
491 
492 	uint64 value = _ParseExpression();
493 	const Token& token = fTokenizer.NextToken();
494 	if (token.type != TOKEN_END_OF_LINE)
495 		parse_exception("parse error", token.position);
496 
497 	return value;
498 }
499 
500 
501 uint64
502 ExpressionParser::EvaluateCommand(const char* expressionString, int& returnCode)
503 {
504 	fTokenizer.SetTo(expressionString);
505 
506 	// Allowed are command or assignment. A command always starts with an
507 	// identifier, an assignment either with an identifier (variable name) or
508 	// a dereferenced address.
509 	const Token& token = fTokenizer.NextToken();
510 	uint64 value = 0;
511 
512 	if (token.type == TOKEN_IDENTIFIER) {
513 		fTokenizer.NextToken();
514 		if (token.type & TOKEN_ASSIGN_FLAG) {
515 			// an assignment
516 			fTokenizer.SetTo(expressionString);
517 			value =  _ParseExpression(true);
518 			returnCode = 0;
519 		} else {
520 			// no assignment, so let's assume it's a command
521 			fTokenizer.SetTo(expressionString);
522 			fTokenizer.SetCommandMode(true);
523 			value = _ParseCommandPipe(returnCode);
524 		}
525 	} else if (token.type == TOKEN_STAR) {
526 		// dereferenced address -- assignment
527 		fTokenizer.SetTo(expressionString);
528 		value =  _ParseExpression(true);
529 		returnCode = 0;
530 	} else
531 		parse_exception("expected command or assignment", 0);
532 
533 	if (token.type != TOKEN_END_OF_LINE)
534 		parse_exception("parse error", token.position);
535 
536 	return value;
537 }
538 
539 
540 status_t
541 ExpressionParser::ParseNextCommandArgument(const char** expressionString,
542 	char* buffer, size_t bufferSize)
543 {
544 	fTokenizer.SetTo(*expressionString);
545 	fTokenizer.SetCommandMode(true);
546 
547 	if (fTokenizer.NextToken().type == TOKEN_END_OF_LINE)
548 		return B_ENTRY_NOT_FOUND;
549 
550 	fTokenizer.RewindToken();
551 
552 	char* argv[2];
553 	int argc = 0;
554 	if (!_ParseArgument(argc, argv))
555 		return B_BAD_VALUE;
556 
557 	strlcpy(buffer, argv[0], bufferSize);
558 
559 	const Token& token = fTokenizer.NextToken();
560 	if (token.type == TOKEN_END_OF_LINE)
561 		*expressionString = NULL;
562 	else
563 		*expressionString += token.position;
564 
565 	return B_OK;
566 }
567 
568 
569 uint64
570 ExpressionParser::_ParseExpression(bool expectAssignment)
571 {
572 	const Token& token = fTokenizer.NextToken();
573 	int32 position = token.position;
574 	if (token.type == TOKEN_IDENTIFIER) {
575 		char variable[MAX_DEBUG_VARIABLE_NAME_LEN];
576 		strlcpy(variable, token.string, sizeof(variable));
577 
578 		int32 assignmentType = fTokenizer.NextToken().type;
579 		if (assignmentType & TOKEN_ASSIGN_FLAG) {
580 			// an assignment
581 			uint64 rhs = _ParseExpression();
582 
583 			// handle the standard assignment separately -- the other kinds
584 			// need the variable to be defined
585 			if (assignmentType == TOKEN_ASSIGN) {
586 				if (!set_debug_variable(variable, rhs)) {
587 					snprintf(sTempBuffer, sizeof(sTempBuffer),
588 						"failed to set value for variable \"%s\"",
589 						variable);
590 					parse_exception(sTempBuffer, position);
591 				}
592 
593 				return rhs;
594 			}
595 
596 			// variable must be defined
597 			if (!is_debug_variable_defined(variable)) {
598 				snprintf(sTempBuffer, sizeof(sTempBuffer),
599 					"variable \"%s\" not defined in modifying assignment",
600 					variable);
601 				parse_exception(sTempBuffer, position);
602 			}
603 
604 			uint64 variableValue = get_debug_variable(variable, 0);
605 
606 			// check for division by zero for the respective assignment types
607 			if ((assignmentType == TOKEN_SLASH_ASSIGN
608 					|| assignmentType == TOKEN_MODULO_ASSIGN)
609 				&& rhs == 0) {
610 				parse_exception("division by zero", position);
611 			}
612 
613 			// compute the new variable value
614 			switch (assignmentType) {
615 				case TOKEN_PLUS_ASSIGN:
616 					variableValue += rhs;
617 					break;
618 				case TOKEN_MINUS_ASSIGN:
619 					variableValue -= rhs;
620 					break;
621 				case TOKEN_STAR_ASSIGN:
622 					variableValue *= rhs;
623 					break;
624 				case TOKEN_SLASH_ASSIGN:
625 					variableValue /= rhs;
626 					break;
627 				case TOKEN_MODULO_ASSIGN:
628 					variableValue %= rhs;
629 					break;
630 				default:
631 					parse_exception("internal error: unknown assignment token",
632 						position);
633 					break;
634 			}
635 
636 			set_debug_variable(variable, variableValue);
637 			return variableValue;
638 		}
639 	} else if (token.type == TOKEN_STAR) {
640 		void* address;
641 		uint32 size;
642 		uint64 value = _ParseDereference(&address, &size);
643 
644 		int32 assignmentType = fTokenizer.NextToken().type;
645 		if (assignmentType & TOKEN_ASSIGN_FLAG) {
646 			// an assignment
647 			uint64 rhs = _ParseExpression();
648 
649 			// check for division by zero for the respective assignment types
650 			if ((assignmentType == TOKEN_SLASH_ASSIGN
651 					|| assignmentType == TOKEN_MODULO_ASSIGN)
652 				&& rhs == 0) {
653 				parse_exception("division by zero", position);
654 			}
655 
656 			// compute the new value
657 			switch (assignmentType) {
658 				case TOKEN_ASSIGN:
659 					value = rhs;
660 					break;
661 				case TOKEN_PLUS_ASSIGN:
662 					value += rhs;
663 					break;
664 				case TOKEN_MINUS_ASSIGN:
665 					value -= rhs;
666 					break;
667 				case TOKEN_STAR_ASSIGN:
668 					value *= rhs;
669 					break;
670 				case TOKEN_SLASH_ASSIGN:
671 					value /= rhs;
672 					break;
673 				case TOKEN_MODULO_ASSIGN:
674 					value %= rhs;
675 					break;
676 				default:
677 					parse_exception("internal error: unknown assignment token",
678 						position);
679 					break;
680 			}
681 
682 			// convert the value for writing to the address
683 			uint64 buffer = 0;
684 			switch (size) {
685 				case 1:
686 					*(uint8*)&buffer = value;
687 					break;
688 				case 2:
689 					*(uint16*)&buffer = value;
690 					break;
691 				case 4:
692 					*(uint32*)&buffer = value;
693 					break;
694 				case 8:
695 					value = buffer;
696 					break;
697 			}
698 
699 			if (debug_memcpy(address, &buffer, size) != B_OK) {
700 				snprintf(sTempBuffer, sizeof(sTempBuffer),
701 					"failed to write to address %p", address);
702 				parse_exception(sTempBuffer, position);
703 			}
704 
705 			return value;
706 		}
707 	}
708 
709 	if (expectAssignment) {
710 		parse_exception("expected assignment",
711 			fTokenizer.CurrentToken().position);
712 	}
713 
714 	// no assignment -- reset to the identifier position and parse a sum
715 	fTokenizer.SetPosition(position);
716 	return _ParseSum(false, 0);
717 }
718 
719 
720 uint64
721 ExpressionParser::_ParseCommandPipe(int& returnCode)
722 {
723 	debugger_command_pipe* pipe = (debugger_command_pipe*)checked_malloc(
724 		sizeof(debugger_command_pipe));
725 
726 	pipe->segment_count = 0;
727 	pipe->broken = false;
728 
729 	do {
730 		if (pipe->segment_count >= MAX_DEBUGGER_COMMAND_PIPE_LENGTH)
731 			parse_exception("Pipe too long", fTokenizer.NextToken().position);
732 
733 		debugger_command_pipe_segment& segment
734 			= pipe->segments[pipe->segment_count];
735 		segment.index = pipe->segment_count++;
736 
737 		_ParseCommand(segment);
738 
739 	} while (fTokenizer.NextToken().type == TOKEN_PIPE);
740 
741 	fTokenizer.RewindToken();
742 
743 	// invoke the pipe
744 	returnCode = invoke_debugger_command_pipe(pipe);
745 
746 	debug_free(pipe);
747 
748 	return get_debug_variable("_", 0);
749 }
750 
751 
752 void
753 ExpressionParser::_ParseCommand(debugger_command_pipe_segment& segment)
754 {
755 	fTokenizer.SetCommandMode(false);
756 	const Token& token = _EatToken(TOKEN_IDENTIFIER);
757 	fTokenizer.SetCommandMode(true);
758 
759 	bool ambiguous;
760 	debugger_command* command = find_debugger_command(token.string, true,
761 		ambiguous);
762 
763 	if (command == NULL) {
764 		if (ambiguous) {
765 			snprintf(sTempBuffer, sizeof(sTempBuffer),
766 				"Ambiguous command \"%s\". Use tab completion or enter "
767 				"\"help %s\" get a list of matching commands.\n", token.string,
768 				token.string);
769 		} else {
770 			snprintf(sTempBuffer, sizeof(sTempBuffer),
771 				"Unknown command \"%s\". Enter \"help\" to get a list of "
772 				"all supported commands.\n", token.string);
773 		}
774 
775 		parse_exception(sTempBuffer, -1);
776 	}
777 
778 	// allocate temporary buffer for the argument vector
779 	char** argv = (char**)checked_malloc(kMaxArgumentCount * sizeof(char*));
780 	int argc = 0;
781 	argv[argc++] = (char*)command->name;
782 
783 	// get the arguments
784 	if ((command->flags & B_KDEBUG_DONT_PARSE_ARGUMENTS) != 0) {
785 		_GetUnparsedArgument(argc, argv);
786 	} else {
787 		while (fTokenizer.NextToken().type != TOKEN_END_OF_LINE) {
788 			fTokenizer.RewindToken();
789 			if (!_ParseArgument(argc, argv))
790 				break;
791 		}
792 	}
793 
794 	if (segment.index > 0) {
795 		if (argc >= kMaxArgumentCount)
796 			parse_exception("too many arguments for command", 0);
797 		else
798 			argc++;
799 	}
800 
801 	segment.command = command;
802 	segment.argc = argc;
803 	segment.argv = argv;
804 	segment.invocations = 0;
805 }
806 
807 
808 bool
809 ExpressionParser::_ParseArgument(int& argc, char** argv)
810 {
811 	const Token& token = fTokenizer.NextToken();
812 	switch (token.type) {
813 		case TOKEN_OPENING_PARENTHESIS:
814 		{
815 			// this starts an expression
816 			fTokenizer.SetCommandMode(false);
817 			uint64 value = _ParseExpression();
818 			fTokenizer.SetCommandMode(true);
819 			_EatToken(TOKEN_CLOSING_PARENTHESIS);
820 
821 			snprintf(sTempBuffer, sizeof(sTempBuffer), "%llu", value);
822 			_AddArgument(argc, argv, sTempBuffer);
823 			return true;
824 		}
825 
826 		case TOKEN_OPENING_BRACKET:
827 		{
828 			// this starts a sub command
829 			int returnValue;
830 			uint64 value = _ParseCommandPipe(returnValue);
831 			_EatToken(TOKEN_CLOSING_BRACKET);
832 
833 			snprintf(sTempBuffer, sizeof(sTempBuffer), "%llu", value);
834 			_AddArgument(argc, argv, sTempBuffer);
835 			return true;
836 		}
837 
838 		case TOKEN_STRING:
839 		case TOKEN_UNKNOWN:
840 			_AddArgument(argc, argv, token.string);
841 			return true;
842 
843 		case TOKEN_CLOSING_PARENTHESIS:
844 		case TOKEN_CLOSING_BRACKET:
845 		case TOKEN_PIPE:
846 			// those don't belong to us
847 			fTokenizer.RewindToken();
848 			return false;
849 
850 		default:
851 		{
852 			snprintf(sTempBuffer, sizeof(sTempBuffer), "unexpected token "
853 				"\"%s\"", token.string);
854 			parse_exception(sTempBuffer, token.position);
855 			return false;
856 		}
857 	}
858 }
859 
860 
861 void
862 ExpressionParser::_GetUnparsedArgument(int& argc, char** argv)
863 {
864 	int32 startPosition = fTokenizer.NextToken().position;
865 	fTokenizer.RewindToken();
866 
867 	// match parentheses and brackets, but otherwise skip all tokens
868 	int32 parentheses = 0;
869 	int32 brackets = 0;
870 	bool done = false;
871 	while (!done) {
872 		const Token& token = fTokenizer.NextToken();
873 		switch (token.type) {
874 			case TOKEN_OPENING_PARENTHESIS:
875 				parentheses++;
876 				break;
877 			case TOKEN_OPENING_BRACKET:
878 				brackets++;
879 				break;
880 			case TOKEN_CLOSING_PARENTHESIS:
881 				if (parentheses > 0)
882 					parentheses--;
883 				else
884 					done = true;
885 				break;
886 			case TOKEN_CLOSING_BRACKET:
887 				if (brackets > 0)
888 					brackets--;
889 				else
890 					done = true;
891 				break;
892 			case TOKEN_PIPE:
893 				if (parentheses == 0 && brackets == 0)
894 					done = true;
895 				break;
896 			case TOKEN_END_OF_LINE:
897 				done = true;
898 				break;
899 		}
900 	}
901 
902 	int32 endPosition = fTokenizer.CurrentToken().position;
903 	fTokenizer.RewindToken();
904 
905 	// add the argument only, if it's not just all spaces
906 	const char* arg = fTokenizer.String() + startPosition;
907 	int32 argLen = endPosition - startPosition;
908 	bool allSpaces = true;
909 	for (int32 i = 0; allSpaces && i < argLen; i++)
910 		allSpaces = isspace(arg[i]);
911 
912 	if (!allSpaces)
913 		_AddArgument(argc, argv, arg, argLen);
914 }
915 
916 
917 void
918 ExpressionParser::_AddArgument(int& argc, char** argv, const char* argument,
919 	int32 length)
920 {
921 	if (argc == kMaxArgumentCount)
922 		parse_exception("too many arguments for command", 0);
923 
924 	if (length < 0)
925 		length = strlen(argument);
926 	length++;
927 	char* buffer = (char*)checked_malloc(length);
928 	strlcpy(buffer, argument, length);
929 
930 	argv[argc++] = buffer;
931 }
932 
933 
934 uint64
935 ExpressionParser::_ParseSum(bool useValue, uint64 value)
936 {
937 	if (!useValue)
938 		value = _ParseProduct();
939 
940 	while (true) {
941 		const Token& token = fTokenizer.NextToken();
942 		switch (token.type) {
943 			case TOKEN_PLUS:
944 				value = value + _ParseProduct();
945 				break;
946 			case TOKEN_MINUS:
947 				value = value - _ParseProduct();
948 				break;
949 
950 			default:
951 				fTokenizer.RewindToken();
952 				return value;
953 		}
954 	}
955 }
956 
957 
958 uint64
959 ExpressionParser::_ParseProduct()
960 {
961 	uint64 value = _ParseUnary();
962 
963 	while (true) {
964 		Token token = fTokenizer.NextToken();
965 		switch (token.type) {
966 			case TOKEN_STAR:
967 				value = value * _ParseUnary();
968 				break;
969 			case TOKEN_SLASH: {
970 				uint64 rhs = _ParseUnary();
971 				if (rhs == 0)
972 					parse_exception("division by zero", token.position);
973 				value = value / rhs;
974 				break;
975 			}
976 			case TOKEN_MODULO: {
977 				uint64 rhs = _ParseUnary();
978 				if (rhs == 0)
979 					parse_exception("modulo by zero", token.position);
980 				value = value % rhs;
981 				break;
982 			}
983 
984 			default:
985 				fTokenizer.RewindToken();
986 				return value;
987 		}
988 	}
989 }
990 
991 
992 uint64
993 ExpressionParser::_ParseUnary()
994 {
995 	switch (fTokenizer.NextToken().type) {
996 		case TOKEN_MINUS:
997 			return -_ParseUnary();
998 
999 		case TOKEN_STAR:
1000 			return _ParseDereference(NULL, NULL);
1001 
1002 		default:
1003 			fTokenizer.RewindToken();
1004 			return _ParseAtom();
1005 	}
1006 
1007 	return 0;
1008 }
1009 
1010 
1011 uint64
1012 ExpressionParser::_ParseDereference(void** _address, uint32* _size)
1013 {
1014 	int32 starPosition = fTokenizer.CurrentToken().position;
1015 
1016 	// optional "{ ... }" specifying the size to read
1017 	uint64 size = 4;
1018 	if (fTokenizer.NextToken().type == TOKEN_OPENING_BRACE) {
1019 		int32 position = fTokenizer.CurrentToken().position;
1020 		size = _ParseExpression();
1021 
1022 		if (size != 1 && size != 2 && size != 4 && size != 8) {
1023 			snprintf(sTempBuffer, sizeof(sTempBuffer),
1024 				"invalid size (%llu) for unary * operator", size);
1025 			parse_exception(sTempBuffer, position);
1026 		}
1027 
1028 		_EatToken(TOKEN_CLOSING_BRACE);
1029 	} else
1030 		fTokenizer.RewindToken();
1031 
1032 	const void* address = (const void*)(addr_t)_ParseUnary();
1033 
1034 	// read bytes from address into a tempory buffer
1035 	uint64 buffer;
1036 	if (debug_memcpy(&buffer, address, size) != B_OK) {
1037 		snprintf(sTempBuffer, sizeof(sTempBuffer),
1038 			"failed to dereference address %p", address);
1039 		parse_exception(sTempBuffer, starPosition);
1040 	}
1041 
1042 	// convert the value to uint64
1043 	uint64 value = 0;
1044 	switch (size) {
1045 		case 1:
1046 			value = *(uint8*)&buffer;
1047 			break;
1048 		case 2:
1049 			value = *(uint16*)&buffer;
1050 			break;
1051 		case 4:
1052 			value = *(uint32*)&buffer;
1053 			break;
1054 		case 8:
1055 			value = buffer;
1056 			break;
1057 	}
1058 
1059 	if (_address != NULL)
1060 		*_address = (void*)address;
1061 	if (_size != NULL)
1062 		*_size = size;
1063 
1064 	return value;
1065 }
1066 
1067 
1068 uint64
1069 ExpressionParser::_ParseAtom()
1070 {
1071 	const Token& token = fTokenizer.NextToken();
1072 	if (token.type == TOKEN_END_OF_LINE)
1073 		parse_exception("unexpected end of expression", token.position);
1074 
1075 	if (token.type == TOKEN_CONSTANT)
1076 		return token.value;
1077 
1078 	if (token.type == TOKEN_IDENTIFIER) {
1079 		if (!is_debug_variable_defined(token.string)) {
1080 			snprintf(sTempBuffer, sizeof(sTempBuffer),
1081 				"variable '%s' undefined", token.string);
1082 			parse_exception(sTempBuffer, token.position);
1083 		}
1084 
1085 		return get_debug_variable(token.string, 0);
1086 	}
1087 
1088 	if (token.type == TOKEN_OPENING_PARENTHESIS) {
1089 		uint64 value = _ParseExpression();
1090 
1091 		_EatToken(TOKEN_CLOSING_PARENTHESIS);
1092 
1093 		return value;
1094 	}
1095 
1096 	// it can only be a "[ command ]" expression now
1097 	fTokenizer.RewindToken();
1098 
1099 	_EatToken(TOKEN_OPENING_BRACKET);
1100 
1101 	fTokenizer.SetCommandMode(true);
1102 	int returnValue;
1103 	uint64 value = _ParseCommandPipe(returnValue);
1104 	fTokenizer.SetCommandMode(false);
1105 
1106 	_EatToken(TOKEN_CLOSING_BRACKET);
1107 
1108 	return value;
1109 }
1110 
1111 
1112 const Token&
1113 ExpressionParser::_EatToken(int32 type)
1114 {
1115 	const Token& token = fTokenizer.NextToken();
1116 	if (token.type != type) {
1117 		snprintf(sTempBuffer, sizeof(sTempBuffer), "expected token type '%c', "
1118 			"got token '%s'", char(type & ~TOKEN_FLAGS), token.string);
1119 		parse_exception(sTempBuffer, token.position);
1120 	}
1121 
1122 	return token;
1123 }
1124 
1125 
1126 
1127 // #pragma mark -
1128 
1129 
1130 bool
1131 evaluate_debug_expression(const char* expression, uint64* _result, bool silent)
1132 {
1133 	if (sNextJumpBufferIndex >= kJumpBufferCount) {
1134 		kprintf_unfiltered("evaluate_debug_expression(): Out of jump buffers "
1135 			"for exception handling\n");
1136 		return 0;
1137 	}
1138 
1139 	bool success;
1140 	uint64 result;
1141 	DebugAllocPoolScope allocPoolScope;
1142 		// Will clean up all allocations when we return.
1143 
1144 	if (setjmp(sJumpBuffers[sNextJumpBufferIndex++]) == 0) {
1145 		result = ExpressionParser().EvaluateExpression(expression);
1146 		success = true;
1147 	} else {
1148 		result = 0;
1149 		success = false;
1150 		if (!silent) {
1151 			if (sExceptionPosition >= 0) {
1152 				kprintf_unfiltered("%s, at position: %d, in expression: %s\n",
1153 					sExceptionMessage, sExceptionPosition, expression);
1154 			} else
1155 				kprintf_unfiltered("%s", sExceptionMessage);
1156 		}
1157 	}
1158 
1159 	sNextJumpBufferIndex--;
1160 
1161 	if (success && _result != NULL)
1162 		*_result = result;
1163 
1164 	return success;
1165 }
1166 
1167 
1168 int
1169 evaluate_debug_command(const char* commandLine)
1170 {
1171 	if (sNextJumpBufferIndex >= kJumpBufferCount) {
1172 		kprintf_unfiltered("evaluate_debug_command(): Out of jump buffers for "
1173 			"exception handling\n");
1174 		return 0;
1175 	}
1176 
1177 	int returnCode = 0;
1178 	DebugAllocPoolScope allocPoolScope;
1179 		// Will clean up all allocations when we return.
1180 
1181 	if (setjmp(sJumpBuffers[sNextJumpBufferIndex++]) == 0) {
1182 		ExpressionParser().EvaluateCommand(commandLine, returnCode);
1183 	} else {
1184 		if (sExceptionPosition >= 0) {
1185 			kprintf_unfiltered("%s, at position: %d, in command line: %s\n",
1186 				sExceptionMessage, sExceptionPosition, commandLine);
1187 		} else
1188 			kprintf_unfiltered("%s", sExceptionMessage);
1189 	}
1190 
1191 	sNextJumpBufferIndex--;
1192 
1193 	return returnCode;
1194 }
1195 
1196 
1197 status_t
1198 parse_next_debug_command_argument(const char** expressionString, char* buffer,
1199 	size_t bufferSize)
1200 {
1201 	if (sNextJumpBufferIndex >= kJumpBufferCount) {
1202 		kprintf_unfiltered("parse_next_debug_command_argument(): Out of jump "
1203 			"buffers for exception handling\n");
1204 		return B_ERROR;
1205 	}
1206 
1207 	status_t error;
1208 	DebugAllocPoolScope allocPoolScope;
1209 		// Will clean up all allocations when we return.
1210 
1211 	if (setjmp(sJumpBuffers[sNextJumpBufferIndex++]) == 0) {
1212 		error = ExpressionParser().ParseNextCommandArgument(expressionString,
1213 			buffer, bufferSize);
1214 	} else {
1215 		if (sExceptionPosition >= 0) {
1216 			kprintf_unfiltered("%s, at position: %d, in command line: %s\n",
1217 				sExceptionMessage, sExceptionPosition, *expressionString);
1218 		} else
1219 			kprintf_unfiltered("%s", sExceptionMessage);
1220 		error = B_BAD_VALUE;
1221 	}
1222 
1223 	sNextJumpBufferIndex--;
1224 
1225 	return error;
1226 }
1227