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