1

I'm programming a calculator in Xcode with Objective C for learning (I am a beginner.). This is inter alia the code I am using (to calculate a NSString).

- (IBAction)resultButtonPressed:(id)sender {

NSExpression *expression = [NSExpression expressionWithFormat:label_result.text];

label_result.text = [NSString stringWithFormat:@"%@", [expression expressionValueWithObject:nil context:nil]];

}

The problem is, that, whenever a user types in something 'uncalculable' like '6(5+1)' instead of '6 * (5+1)', the program crashes and I'm getting this:

2014-11-01 02:36:10.577 The Calculator[1515:575020] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unable to parse the format string "6(5+1) == 1"'
*** First throw call stack:
(0x18672de48 0x196e280e4 0x18753ad98 0x187538e2c 0x18764b128 0x18764b0c4 0x100062244 0x18af110f8 0x18aefa22c 0x18af10a94 0x18af10720 0x18af09c74 0x18aedd38c 0x18b17c1b4 0x18aedb8f4 0x1866e60e8 0x1866e538c 0x1866e343c 0x1866111f4 0x18f7a75a4 0x18af42784 0x10006286c 0x197496a08)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb) 

So for the last half hour I was searching for a method/function which allows you to first check whether the NSString is calculable or not so that I could (if necessary) create a warning-label or something like that, but I didn't find one. Is there anyone who could help me?

Thanks!

Nooyi
  • 316
  • 2
  • 12

3 Answers3

5

NSExpression is not designed to be an error detecting, user correctable, expression parser. It is intended to be used by developers to evaluate expressions that are ensured to be correctly formed. Nor is there built in API to do this.

You'll have to look elsewhere for an expression parser with user-friendly error handling. Given that expression parsing is a typical Comp Sci 101 problem, googling something like "C math expression parser" or "iOS math expression parser" or any of several dozen other keyword soup combinations will yield both code and discussion.

Note: Don't bother going down the exception catch path. Exceptions in iOS/OS X are intended purely to indicate programmer error and are not designed to be recoverable.

bbum
  • 162,346
  • 23
  • 271
  • 359
  • 1
    Although it's funny how setting an exceptions breakpoint proves that Cocoa itself throws and catches exceptions all the freaking time. Drives me crazy because my code seems to crash and then I realize, no, it's just that I'm pausing at one of Cocoa's internal exceptions... – matt Nov 01 '14 at 19:51
  • Is there a good reference about not using try/catch? It seems like a perfectly valid use in a case like in this question. It's not programmer error if the user enters a bad expression. And it's a perfectly recoverable exception. – rmaddy Nov 01 '14 at 23:39
  • @matt I haven't run into an internal exception in a long time and I have exception breakpoints on all the time. I'm almost always on iOS, though, and it likely has less of an exception rich legacy like Cocoa/AppKit. Regardless, it is an internal implementation detail. – bbum Nov 02 '14 at 21:03
  • @maddy See the answers here, including documentation references. http://stackoverflow.com/questions/4648952/objective-c-exceptions It is a programmer error; `NSExpression` is not designed to be a parser of user input. It is designed to be used by programmers only. If it were designed for users it'd barf up actual error messages with some indication of where the expression is erroneous (and those would likely be localized). – bbum Nov 02 '14 at 21:04
  • 2
    @bbum I'm talking about iOS. There are entire frameworks, such as AVFoundation / AVKit, where you won't get _anywhere_ if you've got an exception breakpoint; you'll be lucky to get launched. Are you suggesting that instead of laughing and swearing I should be reporting these as bugs? – matt Nov 02 '14 at 22:17
  • 1
    Sadly, laughing and swearing is probably the best option (I haven't run into this, which surprises me given that Id think I would regularly). – bbum Nov 03 '14 at 14:41
2

try that!

@try {
NSExpression *expression = [NSExpression expressionWithFormat:label_result.text];
label_result.text = [NSString stringWithFormat:@"%@", [expression expressionValueWithObject:nil context:nil]];
} @catch (NSException *exception) {
if ([[exception name] isEqualToString:NSInvalidArgumentException]) {
label_result.text = @"Invalid Expression";
}

Note: Try/catching exception like that are not really recommended in Cocoa. Exceptions should be used to unexpected errors. You can also use a Math Parser like this one https://github.com/davedelong/DDMathParser . Sorry for my english, I am french.

LastMove
  • 2,482
  • 1
  • 15
  • 25
  • That may not actually work. There is no guarantee that the internals of `expressionWithFormat:` will recover gracefully from a parse error. It probably will, but maybe not. – bbum Nov 02 '14 at 21:02
  • 1
    See the answers & documentation on this question: http://stackoverflow.com/questions/4648952/objective-c-exceptions – bbum Nov 02 '14 at 21:05
0

If you would like to use it, I have a method in my Calculator that does exactly what you are looking to achieve.

https://github.com/iMillJoe/Calc7.2.1

See IMCalculatorBrain (and IMShunting Token), The calculator takes an NSString expression, and returns an NSString with either a number or a syntax error. The syntax is styled around the TI Graphic Calculators.

Joe Million
  • 147
  • 9