1 #include "stdafx.h"
2 #include <climits>
3 #include <algorithm>
4 #include "ExpressionEvaluator.h"
5 #include "Console.h"
6 #include "Debugger.h"
7 #include "MemoryDumper.h"
8 #include "Disassembler.h"
9 #include "LabelManager.h"
10 #include "../Utilities/HexUtilities.h"
11 
12 const vector<string> ExpressionEvaluator::_binaryOperators = { { "*", "/", "%", "+", "-", "<<", ">>", "<", "<=", ">", ">=", "==", "!=", "&", "^", "|", "&&", "||" } };
13 const vector<int> ExpressionEvaluator::_binaryPrecedence = { { 10,  10,  10,   9,   9,    8,    8,   7,   7,    7,    7,    6,    6,   5,   4,   3,    2,    1 } };
14 const vector<string> ExpressionEvaluator::_unaryOperators = { { "+", "-", "~", "!" } };
15 const vector<int> ExpressionEvaluator::_unaryPrecedence = { { 11,  11,  11,  11 } };
16 const std::unordered_set<string> ExpressionEvaluator::_operators = { { "*", "/", "%", "+", "-", "<<", ">>", "<", "<=", ">", ">=", "==", "!=", "&", "^", "|", "&&", "||", "~", "!", "(", ")", "{", "}", "[", "]" } };
17 
IsOperator(string token,int & precedence,bool unaryOperator)18 bool ExpressionEvaluator::IsOperator(string token, int &precedence, bool unaryOperator)
19 {
20 	if(unaryOperator) {
21 		for(size_t i = 0, len = _unaryOperators.size(); i < len; i++) {
22 			if(token.compare(_unaryOperators[i]) == 0) {
23 				precedence = _unaryPrecedence[i];
24 				return true;
25 			}
26 		}
27 	} else {
28 		for(size_t i = 0, len = _binaryOperators.size(); i < len; i++) {
29 			if(token.compare(_binaryOperators[i]) == 0) {
30 				precedence = _binaryPrecedence[i];
31 				return true;
32 			}
33 		}
34 	}
35 	return false;
36 }
37 
GetOperator(string token,bool unaryOperator)38 EvalOperators ExpressionEvaluator::GetOperator(string token, bool unaryOperator)
39 {
40 	if(unaryOperator) {
41 		for(size_t i = 0, len = _unaryOperators.size(); i < len; i++) {
42 			if(token.compare(_unaryOperators[i]) == 0) {
43 				return (EvalOperators)(EvalOperators::Plus + i);
44 			}
45 		}
46 	} else {
47 		for(size_t i = 0, len = _binaryOperators.size(); i < len; i++) {
48 			if(token.compare(_binaryOperators[i]) == 0) {
49 				return (EvalOperators)(EvalOperators::Multiplication + i);
50 			}
51 		}
52 	}
53 	return EvalOperators::Addition;
54 }
55 
CheckSpecialTokens(string expression,size_t & pos,string & output,ExpressionData & data)56 bool ExpressionEvaluator::CheckSpecialTokens(string expression, size_t &pos, string &output, ExpressionData &data)
57 {
58 	string token;
59 	size_t initialPos = pos;
60 	size_t len = expression.size();
61 	do {
62 		char c = std::tolower(expression[pos]);
63 		if((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_' || c == '@') {
64 			//Only letters, numbers and underscore are allowed in code labels
65 			token += c;
66 			pos++;
67 		} else {
68 			break;
69 		}
70 	} while(pos < len);
71 
72 	if(token == "a") {
73 		output += std::to_string((int64_t)EvalValues::RegA);
74 	} else if(token == "x") {
75 		output += std::to_string((int64_t)EvalValues::RegX);
76 	} else if(token == "y") {
77 		output += std::to_string((int64_t)EvalValues::RegY);
78 	} else if(token == "ps") {
79 		output += std::to_string((int64_t)EvalValues::RegPS);
80 	} else if(token == "sp") {
81 		output += std::to_string((int64_t)EvalValues::RegSP);
82 	} else if(token == "pc") {
83 		output += std::to_string((int64_t)EvalValues::RegPC);
84 	} else if(token == "oppc") {
85 		output += std::to_string((int64_t)EvalValues::RegOpPC);
86 	} else if(token == "previousoppc") {
87 		output += std::to_string((int64_t)EvalValues::PreviousOpPC);
88 	} else if(token == "frame") {
89 		output += std::to_string((int64_t)EvalValues::PpuFrameCount);
90 	} else if(token == "cycle") {
91 		output += std::to_string((int64_t)EvalValues::PpuCycle);
92 	} else if(token == "scanline") {
93 		output += std::to_string((int64_t)EvalValues::PpuScanline);
94 	} else if(token == "irq") {
95 		output += std::to_string((int64_t)EvalValues::Irq);
96 	} else if(token == "nmi") {
97 		output += std::to_string((int64_t)EvalValues::Nmi);
98 	} else if(token == "verticalblank") {
99 		output += std::to_string((int64_t)EvalValues::VerticalBlank);
100 	} else if(token == "sprite0hit") {
101 		output += std::to_string((int64_t)EvalValues::Sprite0Hit);
102 	} else if(token == "spriteoverflow") {
103 		output += std::to_string((int64_t)EvalValues::SpriteOverflow);
104 	} else if(token == "value") {
105 		output += std::to_string((int64_t)EvalValues::Value);
106 	} else if(token == "address") {
107 		output += std::to_string((int64_t)EvalValues::Address);
108 	} else if(token == "romaddress") {
109 		output += std::to_string((int64_t)EvalValues::AbsoluteAddress);
110 	} else if(token == "iswrite") {
111 		output += std::to_string((int64_t)EvalValues::IsWrite);
112 	} else if(token == "isread") {
113 		output += std::to_string((int64_t)EvalValues::IsRead);
114 	} else if(token == "branched") {
115 		output += std::to_string((int64_t)EvalValues::Branched);
116 	} else {
117 		string originalExpression = expression.substr(initialPos, pos - initialPos);
118 		bool validLabel = _debugger->GetLabelManager()->ContainsLabel(originalExpression);
119 		if(!validLabel) {
120 			//Check if a multi-byte label exists for this name
121 			string label = originalExpression + "+0";
122 			validLabel = _debugger->GetLabelManager()->ContainsLabel(label);
123 		}
124 
125 		if(validLabel) {
126 			data.Labels.push_back(originalExpression);
127 			output += std::to_string(EvalValues::FirstLabelIndex + data.Labels.size() - 1);
128 		} else {
129 			return false;
130 		}
131 	}
132 
133 	return true;
134 }
135 
GetNextToken(string expression,size_t & pos,ExpressionData & data,bool & success)136 string ExpressionEvaluator::GetNextToken(string expression, size_t &pos, ExpressionData &data, bool &success)
137 {
138 	string output;
139 	success = true;
140 
141 	char c = std::tolower(expression[pos]);
142 	if(c == '$') {
143 		//Hex numbers
144 		pos++;
145 		for(size_t len = expression.size(); pos < len; pos++) {
146 			c = std::tolower(expression[pos]);
147 			if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')) {
148 				output += c;
149 			} else {
150 				break;
151 			}
152 		}
153 		if(output.empty()) {
154 			//No numbers followed the hex mark, this isn't a valid expression
155 			success = false;
156 		}
157 		output = std::to_string((uint32_t)HexUtilities::FromHex(output));
158 	} else if(c == '%') {
159 		//Binary numbers
160 		pos++;
161 		for(size_t len = expression.size(); pos < len; pos++) {
162 			c = std::tolower(expression[pos]);
163 			if(c == '0' || c <= '1') {
164 				output += c;
165 			} else {
166 				break;
167 			}
168 		}
169 		if(output.empty()) {
170 			//No numbers followed the binary mark, this isn't a valid expression
171 			success = false;
172 		}
173 
174 		uint32_t value = 0;
175 		for(size_t i = 0; i < output.size(); i++) {
176 			value <<= 1;
177 			value |= output[i] == '1' ? 1 : 0;
178 		}
179 		output = std::to_string(value);
180 	} else if(c >= '0' && c <= '9') {
181 		//Regular numbers
182 		for(size_t len = expression.size(); pos < len; pos++) {
183 			c = std::tolower(expression[pos]);
184 			if(c >= '0' && c <= '9') {
185 				output += c;
186 			} else {
187 				break;
188 			}
189 		}
190 	} else if((c < 'a' || c > 'z') && c != '_' && c != '@') {
191 		//Operators
192 		string operatorToken;
193 		for(size_t len = expression.size(); pos < len; pos++) {
194 			c = std::tolower(expression[pos]);
195 			operatorToken += c;
196 			if(output.empty() || _operators.find(operatorToken) != _operators.end()) {
197 				//If appending the next char results in a valid operator, append it (or if this is the first character)
198 				output += c;
199 			} else {
200 				//Reached the end of the operator, return
201 				break;
202 			}
203 		}
204 	} else {
205 		//Special tokens and labels
206 		success = CheckSpecialTokens(expression, pos, output, data);
207 	}
208 
209 	return output;
210 }
211 
ProcessSpecialOperator(EvalOperators evalOp,std::stack<EvalOperators> & opStack,std::stack<int> & precedenceStack,vector<int64_t> & outputQueue)212 bool ExpressionEvaluator::ProcessSpecialOperator(EvalOperators evalOp, std::stack<EvalOperators> &opStack, std::stack<int> &precedenceStack, vector<int64_t> &outputQueue)
213 {
214 	if(opStack.empty()) {
215 		return false;
216 	}
217 	while(opStack.top() != evalOp) {
218 		outputQueue.push_back(opStack.top());
219 		opStack.pop();
220 		precedenceStack.pop();
221 
222 		if(opStack.empty()) {
223 			return false;
224 		}
225 	}
226 	if(evalOp != EvalOperators::Parenthesis) {
227 		outputQueue.push_back(opStack.top());
228 	}
229 	opStack.pop();
230 	precedenceStack.pop();
231 
232 	return true;
233 }
234 
ToRpn(string expression,ExpressionData & data)235 bool ExpressionEvaluator::ToRpn(string expression, ExpressionData &data)
236 {
237 	std::stack<EvalOperators> opStack = std::stack<EvalOperators>();
238 	std::stack<int> precedenceStack;
239 
240 	size_t position = 0;
241 	int parenthesisCount = 0;
242 	int bracketCount = 0;
243 	int braceCount = 0;
244 
245 	bool previousTokenIsOp = true;
246 	bool operatorExpected = false;
247 	bool operatorOrEndTokenExpected = false;
248 	while(true) {
249 		bool success = true;
250 		string token = GetNextToken(expression, position, data, success);
251 		if(!success) {
252 			return false;
253 		}
254 
255 		if(token.empty()) {
256 			break;
257 		}
258 
259 		bool requireOperator = operatorExpected;
260 		bool requireOperatorOrEndToken = operatorOrEndTokenExpected;
261 		bool unaryOperator = previousTokenIsOp;
262 
263 		operatorExpected = false;
264 		operatorOrEndTokenExpected = false;
265 		previousTokenIsOp = false;
266 
267 		int precedence = 0;
268 		if(IsOperator(token, precedence, unaryOperator)) {
269 			EvalOperators op = GetOperator(token, unaryOperator);
270 			bool rightAssociative = unaryOperator;
271 			while(!opStack.empty() && ((rightAssociative && precedence < precedenceStack.top()) || (!rightAssociative && precedence <= precedenceStack.top()))) {
272 				//Pop operators from the stack until we find something with higher priority (or empty the stack)
273 				data.RpnQueue.push_back(opStack.top());
274 				opStack.pop();
275 				precedenceStack.pop();
276 			}
277 			opStack.push(op);
278 			precedenceStack.push(precedence);
279 
280 			previousTokenIsOp = true;
281 		} else if(requireOperator) {
282 			//We needed an operator, and got something else, this isn't a valid expression (e.g "(3)4" or "[$00]22")
283 			return false;
284 		} else if(requireOperatorOrEndToken && token[0] != ')' && token[0] != ']' && token[0] != '}') {
285 			//We needed an operator or close token - this isn't a valid expression (e.g "%1134")
286 			return false;
287 		} else if(token[0] == '(') {
288 			parenthesisCount++;
289 			opStack.push(EvalOperators::Parenthesis);
290 			precedenceStack.push(0);
291 			previousTokenIsOp = true;
292 		} else if(token[0] == ')') {
293 			parenthesisCount--;
294 			if(!ProcessSpecialOperator(EvalOperators::Parenthesis, opStack, precedenceStack, data.RpnQueue)) {
295 				return false;
296 			}
297 			operatorExpected = true;
298 		} else if(token[0] == '[') {
299 			bracketCount++;
300 			opStack.push(EvalOperators::Bracket);
301 			precedenceStack.push(0);
302 		} else if(token[0] == ']') {
303 			bracketCount--;
304 			if(!ProcessSpecialOperator(EvalOperators::Bracket, opStack, precedenceStack, data.RpnQueue)) {
305 				return false;
306 			}
307 			operatorExpected = true;
308 		} else if(token[0] == '{') {
309 			braceCount++;
310 			opStack.push(EvalOperators::Braces);
311 			precedenceStack.push(0);
312 		} else if(token[0] == '}') {
313 			braceCount--;
314 			if(!ProcessSpecialOperator(EvalOperators::Braces, opStack, precedenceStack, data.RpnQueue)){
315 				return false;
316 			}
317 			operatorExpected = true;
318 		} else {
319 			if(token[0] < '0' || token[0] > '9') {
320 				return false;
321 			} else {
322 				data.RpnQueue.push_back(std::stoll(token));
323 				operatorOrEndTokenExpected = true;
324 			}
325 		}
326 	}
327 
328 	if(braceCount || bracketCount || parenthesisCount) {
329 		//Mismatching number of brackets/braces/parenthesis
330 		return false;
331 	}
332 
333 	while(!opStack.empty()) {
334 		data.RpnQueue.push_back(opStack.top());
335 		opStack.pop();
336 	}
337 
338 	return true;
339 }
340 
Evaluate(ExpressionData & data,DebugState & state,EvalResultType & resultType,OperationInfo & operationInfo)341 int32_t ExpressionEvaluator::Evaluate(ExpressionData &data, DebugState &state, EvalResultType &resultType, OperationInfo &operationInfo)
342 {
343 	if(data.RpnQueue.empty()) {
344 		resultType = EvalResultType::Invalid;
345 		return 0;
346 	}
347 
348 	int pos = 0;
349 	int64_t right = 0;
350 	int64_t left = 0;
351 	resultType = EvalResultType::Numeric;
352 
353 	for(size_t i = 0, len = data.RpnQueue.size(); i < len; i++) {
354 		int64_t token = data.RpnQueue[i];
355 
356 		if(token >= EvalValues::RegA) {
357 			//Replace value with a special value
358 			if(token >= EvalValues::FirstLabelIndex) {
359 				int64_t labelIndex = token - EvalValues::FirstLabelIndex;
360 				if((size_t)labelIndex < data.Labels.size()) {
361 					token = _debugger->GetLabelManager()->GetLabelRelativeAddress(data.Labels[(uint32_t)labelIndex]);
362 					if(token < -1) {
363 						//Label doesn't exist, try to find a matching multi-byte label
364 						string label = data.Labels[(uint32_t)labelIndex] + "+0";
365 						token = _debugger->GetLabelManager()->GetLabelRelativeAddress(label);
366 					}
367 				} else {
368 					token = -2;
369 				}
370 				if(token < 0) {
371 					//Label is no longer valid
372 					resultType = token == -1 ? EvalResultType::OutOfScope : EvalResultType::Invalid;
373 					return 0;
374 				}
375 			} else {
376 				switch(token) {
377 					case EvalValues::RegA: token = state.CPU.A; break;
378 					case EvalValues::RegX: token = state.CPU.X; break;
379 					case EvalValues::RegY: token = state.CPU.Y; break;
380 					case EvalValues::RegSP: token = state.CPU.SP; break;
381 					case EvalValues::RegPS: token = state.CPU.PS; break;
382 					case EvalValues::RegPC: token = state.CPU.PC; break;
383 					case EvalValues::RegOpPC: token = state.CPU.DebugPC; break;
384 					case EvalValues::PpuFrameCount: token = state.PPU.FrameCount; break;
385 					case EvalValues::PpuCycle: token = state.PPU.Cycle; break;
386 					case EvalValues::PpuScanline: token = state.PPU.Scanline; break;
387 					case EvalValues::Nmi: token = state.CPU.NMIFlag; resultType = EvalResultType::Boolean; break;
388 					case EvalValues::Irq: token = state.CPU.IRQFlag; resultType = EvalResultType::Boolean; break;
389 					case EvalValues::Value: token = operationInfo.Value; break;
390 					case EvalValues::Address: token = operationInfo.Address; break;
391 					case EvalValues::AbsoluteAddress: token = _debugger->GetAbsoluteAddress(operationInfo.Address); break;
392 					case EvalValues::IsWrite: token = operationInfo.OperationType == MemoryOperationType::Write || operationInfo.OperationType == MemoryOperationType::DummyWrite; break;
393 					case EvalValues::IsRead: token = operationInfo.OperationType == MemoryOperationType::Read || operationInfo.OperationType == MemoryOperationType::DummyRead; break;
394 					case EvalValues::PreviousOpPC: token = state.CPU.PreviousDebugPC; break;
395 					case EvalValues::Sprite0Hit: token = state.PPU.StatusFlags.Sprite0Hit; resultType = EvalResultType::Boolean; break;
396 					case EvalValues::SpriteOverflow: token = state.PPU.StatusFlags.SpriteOverflow; resultType = EvalResultType::Boolean; break;
397 					case EvalValues::VerticalBlank: token = state.PPU.StatusFlags.VerticalBlank; resultType = EvalResultType::Boolean; break;
398 					case EvalValues::Branched: token = Disassembler::IsJump(_debugger->GetMemoryDumper()->GetMemoryValue(DebugMemoryType::CpuMemory, state.CPU.PreviousDebugPC, true)); resultType = EvalResultType::Boolean; break;
399 				}
400 			}
401 		} else if(token >= EvalOperators::Multiplication) {
402 			right = operandStack[--pos];
403 			if(pos > 0 && token <= EvalOperators::LogicalOr) {
404 				//Only do this for binary operators
405 				left = operandStack[--pos];
406 			}
407 
408 			resultType = EvalResultType::Numeric;
409 			switch(token) {
410 				case EvalOperators::Multiplication: token = left * right; break;
411 				case EvalOperators::Division:
412 					if(right == 0) {
413 						resultType = EvalResultType::DivideBy0;
414 						return 0;
415 					}
416 					token = left / right; break;
417 				case EvalOperators::Modulo:
418 					if(right == 0) {
419 						resultType = EvalResultType::DivideBy0;
420 						return 0;
421 					}
422 					token = left % right;
423 					break;
424 				case EvalOperators::Addition: token = left + right; break;
425 				case EvalOperators::Substration: token = left - right; break;
426 				case EvalOperators::ShiftLeft: token = left << right; break;
427 				case EvalOperators::ShiftRight: token = left >> right; break;
428 				case EvalOperators::SmallerThan: token = left < right; resultType = EvalResultType::Boolean; break;
429 				case EvalOperators::SmallerOrEqual: token = left <= right; resultType = EvalResultType::Boolean; break;
430 				case EvalOperators::GreaterThan: token = left > right; resultType = EvalResultType::Boolean; break;
431 				case EvalOperators::GreaterOrEqual: token = left >= right; resultType = EvalResultType::Boolean; break;
432 				case EvalOperators::Equal: token = left == right; resultType = EvalResultType::Boolean; break;
433 				case EvalOperators::NotEqual: token = left != right; resultType = EvalResultType::Boolean; break;
434 				case EvalOperators::BinaryAnd: token = left & right; break;
435 				case EvalOperators::BinaryXor: token = left ^ right; break;
436 				case EvalOperators::BinaryOr: token = left | right; break;
437 				case EvalOperators::LogicalAnd: token = left && right; resultType = EvalResultType::Boolean; break;
438 				case EvalOperators::LogicalOr: token = left || right; resultType = EvalResultType::Boolean; break;
439 
440 				//Unary operators
441 				case EvalOperators::Plus: token = right; break;
442 				case EvalOperators::Minus: token = -right; break;
443 				case EvalOperators::BinaryNot: token = ~right; break;
444 				case EvalOperators::LogicalNot: token = !right; break;
445 				case EvalOperators::Bracket: token = _debugger->GetMemoryDumper()->GetMemoryValue(DebugMemoryType::CpuMemory, (uint32_t)right); break;
446 				case EvalOperators::Braces: token = _debugger->GetMemoryDumper()->GetMemoryValueWord(DebugMemoryType::CpuMemory, (uint32_t)right); break;
447 				default: throw std::runtime_error("Invalid operator");
448 			}
449 		}
450 		operandStack[pos++] = token;
451 	}
452 	return (int32_t)operandStack[0];
453 }
454 
ExpressionEvaluator(Debugger * debugger)455 ExpressionEvaluator::ExpressionEvaluator(Debugger* debugger)
456 {
457 	_debugger = debugger;
458 }
459 
GetRpnList(string expression,bool & success)460 ExpressionData ExpressionEvaluator::GetRpnList(string expression, bool &success)
461 {
462 	ExpressionData* cachedData = PrivateGetRpnList(expression, success);
463 	if(cachedData) {
464 		return *cachedData;
465 	} else {
466 		return ExpressionData();
467 	}
468 }
469 
PrivateGetRpnList(string expression,bool & success)470 ExpressionData* ExpressionEvaluator::PrivateGetRpnList(string expression, bool& success)
471 {
472 	ExpressionData *cachedData = nullptr;
473 	{
474 		LockHandler lock = _cacheLock.AcquireSafe();
475 
476 		auto result = _cache.find(expression);
477 		if(result != _cache.end()) {
478 			cachedData = &(result->second);
479 		}
480 	}
481 
482 	if(cachedData == nullptr) {
483 		string fixedExp = expression;
484 		fixedExp.erase(std::remove(fixedExp.begin(), fixedExp.end(), ' '), fixedExp.end());
485 		ExpressionData data;
486 		success = ToRpn(fixedExp, data);
487 		if(success) {
488 			LockHandler lock = _cacheLock.AcquireSafe();
489 			_cache[expression] = data;
490 			cachedData = &_cache[expression];
491 		}
492 	} else {
493 		success = true;
494 	}
495 
496 	return cachedData;
497 }
498 
PrivateEvaluate(string expression,DebugState & state,EvalResultType & resultType,OperationInfo & operationInfo,bool & success)499 int32_t ExpressionEvaluator::PrivateEvaluate(string expression, DebugState &state, EvalResultType &resultType, OperationInfo &operationInfo, bool& success)
500 {
501 	success = true;
502 	ExpressionData *cachedData = PrivateGetRpnList(expression, success);
503 
504 	if(!success) {
505 		resultType = EvalResultType::Invalid;
506 		return 0;
507 	}
508 
509 	return Evaluate(*cachedData, state, resultType, operationInfo);
510 }
511 
Evaluate(string expression,DebugState & state,EvalResultType & resultType,OperationInfo & operationInfo)512 int32_t ExpressionEvaluator::Evaluate(string expression, DebugState &state, EvalResultType &resultType, OperationInfo &operationInfo)
513 {
514 	try {
515 		bool success;
516 		int32_t result = PrivateEvaluate(expression, state, resultType, operationInfo, success);
517 		if(success) {
518 			return result;
519 		}
520 	} catch(std::exception e) {
521 	}
522 	resultType = EvalResultType::Invalid;
523 	return 0;
524 }
525 
Validate(string expression)526 bool ExpressionEvaluator::Validate(string expression)
527 {
528 	try {
529 		DebugState state;
530 		EvalResultType type;
531 		OperationInfo operationInfo;
532 		bool success;
533 		PrivateEvaluate(expression, state, type, operationInfo, success);
534 		return success;
535 	} catch(std::exception e) {
536 		return false;
537 	}
538 }
539 
540 #if _DEBUG
541 #include <assert.h>
RunTests()542 void ExpressionEvaluator::RunTests()
543 {
544 	//Some basic unit tests to run in debug mode
545 	auto test = [=](string expr, EvalResultType expectedType, int expectedResult) {
546 		DebugState state = { 0 };
547 		OperationInfo opInfo = { 0 };
548 		EvalResultType type;
549 		int32_t result = Evaluate(expr, state, type, opInfo);
550 
551 		assert(type == expectedType);
552 		assert(result == expectedResult);
553 	};
554 
555 	test("1 - -1", EvalResultType::Numeric, 2);
556 	test("1 - (-1)", EvalResultType::Numeric, 2);
557 	test("1 - -(-1)", EvalResultType::Numeric, 0);
558 	test("(0 - 1) == -1 && 5 < 10", EvalResultType::Boolean, true);
559 	test("(0 - 1) == 0 || 5 < 10", EvalResultType::Boolean, true);
560 	test("(0 - 1) == 0 || 5 < -10", EvalResultType::Boolean, false);
561 	test("(0 - 1) == 0 || 15 < 10", EvalResultType::Boolean, false);
562 
563 	test("10 != $10", EvalResultType::Boolean, true);
564 	test("10 == $A", EvalResultType::Boolean, true);
565 	test("10 == $0A", EvalResultType::Boolean, true);
566 
567 	test("(0 - 1 == 0 || 15 < 10", EvalResultType::Invalid, 0);
568 	test("10 / 0", EvalResultType::DivideBy0, 0);
569 
570 	test("x + 5", EvalResultType::Numeric, 5);
571 	test("x == 0", EvalResultType::Boolean, true);
572 	test("x == y", EvalResultType::Boolean, true);
573 	test("x == y == scanline", EvalResultType::Boolean, false); //because (x == y) is true, and true != scanline
574 	test("x == y && !(a == x)", EvalResultType::Boolean, false);
575 
576 	test("(~0 & ~1) & $FFF == $FFE", EvalResultType::Numeric, 0); //because of operator priority (& is done after ==)
577 	test("((~0 & ~1) & $FFF) == $FFE", EvalResultType::Boolean, true);
578 
579 	test("1+3*3+10/(3+4)", EvalResultType::Numeric, 11);
580 	test("(1+3*3+10)/(3+4)", EvalResultType::Numeric, 2);
581 	test("(1+3*3+10)/3+4", EvalResultType::Numeric, 10);
582 
583 	test("{$4500}", EvalResultType::Numeric, 0x4545);
584 	test("[$4500]", EvalResultType::Numeric, 0x45);
585 
586 	test("[$45]3", EvalResultType::Invalid, 0);
587 	test("($45)3", EvalResultType::Invalid, 0);
588 	test("($45]", EvalResultType::Invalid, 0);
589 
590 	test("%11", EvalResultType::Numeric, 3);
591 	test("%011", EvalResultType::Numeric, 3);
592 	test("%1011", EvalResultType::Numeric, 11);
593 	test("%12", EvalResultType::Invalid, 0);
594 }
595 #endif