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