From 245468a559a0fd041e338c70907a1b1f255f0f1d Mon Sep 17 00:00:00 2001 From: Kim Wittenburg Date: Thu, 11 Sep 2014 22:17:29 +0200 Subject: [PATCH] Implemented Expression Evaluation/Parsing with Proper Error Handling --- MathPad.xcodeproj/project.pbxproj | 64 ++- MathPad/MPElementParser.h | 27 +- MathPad/MPElementParser.m | 469 ++++++++++++------ MathPad/MPExpressionElement.h | 2 - MathPad/MPExpressionEvaluator.h | 7 +- MathPad/MPExpressionEvaluator.m | 172 ++----- MathPad/MPParseError.h | 19 +- MathPad/MPParseError.m | 20 +- MathPad/MPParsedFactor.h | 22 + MathPad/MPParsedFactor.m | 60 +++ MathPad/MPParsedProduct.h | 20 + MathPad/MPParsedProduct.m | 51 ++ MathPad/MPSumFunctionLayout.m | 8 + .../NSRegularExpression+MPParsingAdditions.h | 21 + .../NSRegularExpression+MPParsingAdditions.m | 33 ++ MathPad/NSString+MPExpressionElement.m | 5 - 16 files changed, 670 insertions(+), 330 deletions(-) create mode 100644 MathPad/MPParsedFactor.h create mode 100644 MathPad/MPParsedFactor.m create mode 100644 MathPad/MPParsedProduct.h create mode 100644 MathPad/MPParsedProduct.m create mode 100644 MathPad/NSRegularExpression+MPParsingAdditions.h create mode 100644 MathPad/NSRegularExpression+MPParsingAdditions.m diff --git a/MathPad.xcodeproj/project.pbxproj b/MathPad.xcodeproj/project.pbxproj index 1784896..dd916ec 100644 --- a/MathPad.xcodeproj/project.pbxproj +++ b/MathPad.xcodeproj/project.pbxproj @@ -8,6 +8,16 @@ /* Begin PBXBuildFile section */ 3B52CEC119BB9FA900CEDCFC /* MathKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B85830D19BB5E5500D76A8D /* MathKit.framework */; }; + 3B52CED019BE509C00CEDCFC /* MPParseError.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B52CECE19BE509C00CEDCFC /* MPParseError.h */; }; + 3B52CED119BE509C00CEDCFC /* MPParseError.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B52CECF19BE509C00CEDCFC /* MPParseError.m */; }; + 3B52CEDC19BEE63000CEDCFC /* NSRegularExpression+MPParsingAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B52CEDA19BEE63000CEDCFC /* NSRegularExpression+MPParsingAdditions.h */; }; + 3B52CEDD19BEE63000CEDCFC /* NSRegularExpression+MPParsingAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B52CEDB19BEE63000CEDCFC /* NSRegularExpression+MPParsingAdditions.m */; }; + 3B52CEE219C070B900CEDCFC /* MPParsedProduct.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B52CEE019C070B900CEDCFC /* MPParsedProduct.h */; }; + 3B52CEE319C070B900CEDCFC /* MPParsedProduct.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B52CEE119C070B900CEDCFC /* MPParsedProduct.m */; }; + 3B52CEE619C0755300CEDCFC /* MPElementParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B52CEE419C0755300CEDCFC /* MPElementParser.h */; }; + 3B52CEE719C0755300CEDCFC /* MPElementParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B52CEE519C0755300CEDCFC /* MPElementParser.m */; }; + 3B71432D19C0A1FC005A184E /* MPParsedFactor.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B71432B19C0A1FC005A184E /* MPParsedFactor.h */; }; + 3B71432E19C0A1FC005A184E /* MPParsedFactor.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B71432C19C0A1FC005A184E /* MPParsedFactor.m */; }; 3B74BFB319A4C51800E5B5DE /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B74BFB219A4C51800E5B5DE /* CoreText.framework */; }; 3B85830E19BB5E5500D76A8D /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BF9976E18DE623E009CF6C4 /* Cocoa.framework */; }; 3B85831419BB5E5500D76A8D /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3B85831219BB5E5500D76A8D /* InfoPlist.strings */; }; @@ -19,8 +29,6 @@ 3B85833919BB63C500D76A8D /* MPExpressionElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 3BFAC3961997B67400B3EF67 /* MPExpressionElement.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3B85833A19BB63D400D76A8D /* MPFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B87E35E19009D5F00259938 /* MPFunction.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3B85833E19BB651400D76A8D /* MPExpressionEvaluator.h in Headers */ = {isa = PBXBuildFile; fileRef = 3BC46B4719B38C980033F13A /* MPExpressionEvaluator.h */; }; - 3B85833F19BB651400D76A8D /* MPElementParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B85830519BB573A00D76A8D /* MPElementParser.h */; }; - 3B85834019BB651400D76A8D /* MPParsedElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 3BE9C4B119B9CC70002CC508 /* MPParsedElement.h */; }; 3B85834119BB651E00D76A8D /* MPRangePath.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B87E35B1900933200259938 /* MPRangePath.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3B85834219BB652900D76A8D /* MPException.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B0F69A719028BC600817707 /* MPException.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3B85834319BB653700D76A8D /* MPSumFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 3BB09EC71906FD830080A5ED /* MPSumFunction.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -37,8 +45,6 @@ 3BBEA93519BB79A700133766 /* MPExpression.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BFAC38E1997B61300B3EF67 /* MPExpression.m */; }; 3BBEA93619BB79A700133766 /* MPFunction.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E35F19009D5F00259938 /* MPFunction.m */; }; 3BBEA93719BB79A700133766 /* MPExpressionEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BC46B4819B38C980033F13A /* MPExpressionEvaluator.m */; }; - 3BBEA93819BB79A700133766 /* MPElementParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B85830619BB573A00D76A8D /* MPElementParser.m */; }; - 3BBEA93919BB79A700133766 /* MPParsedElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BE9C4B219B9CC70002CC508 /* MPParsedElement.m */; }; 3BBEA93A19BB79A700133766 /* MPSumFunction.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB09EC81906FD830080A5ED /* MPSumFunction.m */; }; 3BBEA93B19BB79A700133766 /* MPRangePath.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E35C1900933200259938 /* MPRangePath.m */; }; 3BBEA93C19BB79A700133766 /* MPException.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B0F69A819028C6000817707 /* MPException.m */; }; @@ -52,8 +58,6 @@ 3BBEA94419BB79A700133766 /* MPSumFunctionLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB09EE0190736160080A5ED /* MPSumFunctionLayout.m */; }; 3BBEA94F19BB79EF00133766 /* MPExpressionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B0F69AB1902A82C00817707 /* MPExpressionTests.m */; }; 3BBEA95019BB79EF00133766 /* MPRangeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BBBA3941905704200824E74 /* MPRangeTests.m */; }; - 3BBEA95519BB9B3500133766 /* MPParseError.h in Headers */ = {isa = PBXBuildFile; fileRef = 3BBEA95319BB9B3500133766 /* MPParseError.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3BBEA95619BB9B3500133766 /* MPParseError.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BBEA95419BB9B3500133766 /* MPParseError.m */; }; 3BC4660B19B2425A0033F13A /* MPDocument.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3BF9978318DE623E009CF6C4 /* MPDocument.xib */; }; 3BC4661419B245C60033F13A /* Fonts in Resources */ = {isa = PBXBuildFile; fileRef = 3BC4661319B245C60033F13A /* Fonts */; }; 3BF9976F18DE623E009CF6C4 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BF9976E18DE623E009CF6C4 /* Cocoa.framework */; }; @@ -114,10 +118,18 @@ 3B528D0F199417E10054DB5F /* MPExpressionLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPExpressionLayout.m; sourceTree = ""; }; 3B528D11199417E90054DB5F /* MPFunctionLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFunctionLayout.h; sourceTree = ""; }; 3B528D12199417E90054DB5F /* MPFunctionLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPFunctionLayout.m; sourceTree = ""; }; + 3B52CECE19BE509C00CEDCFC /* MPParseError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPParseError.h; sourceTree = ""; }; + 3B52CECF19BE509C00CEDCFC /* MPParseError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPParseError.m; sourceTree = ""; }; + 3B52CEDA19BEE63000CEDCFC /* NSRegularExpression+MPParsingAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSRegularExpression+MPParsingAdditions.h"; sourceTree = ""; }; + 3B52CEDB19BEE63000CEDCFC /* NSRegularExpression+MPParsingAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSRegularExpression+MPParsingAdditions.m"; sourceTree = ""; }; + 3B52CEE019C070B900CEDCFC /* MPParsedProduct.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPParsedProduct.h; sourceTree = ""; }; + 3B52CEE119C070B900CEDCFC /* MPParsedProduct.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPParsedProduct.m; sourceTree = ""; }; + 3B52CEE419C0755300CEDCFC /* MPElementParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementParser.h; sourceTree = ""; }; + 3B52CEE519C0755300CEDCFC /* MPElementParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementParser.m; sourceTree = ""; }; 3B688D9819982DF50006B4AB /* MPLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPLayout.m; sourceTree = ""; }; + 3B71432B19C0A1FC005A184E /* MPParsedFactor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPParsedFactor.h; sourceTree = ""; }; + 3B71432C19C0A1FC005A184E /* MPParsedFactor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPParsedFactor.m; sourceTree = ""; }; 3B74BFB219A4C51800E5B5DE /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; }; - 3B85830519BB573A00D76A8D /* MPElementParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPElementParser.h; sourceTree = ""; }; - 3B85830619BB573A00D76A8D /* MPElementParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPElementParser.m; sourceTree = ""; }; 3B85830D19BB5E5500D76A8D /* MathKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MathKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3B85831119BB5E5500D76A8D /* MathKit-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "MathKit-Info.plist"; sourceTree = ""; }; 3B85831319BB5E5500D76A8D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; @@ -141,15 +153,11 @@ 3BB09EDF190736160080A5ED /* MPSumFunctionLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSumFunctionLayout.h; sourceTree = ""; }; 3BB09EE0190736160080A5ED /* MPSumFunctionLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSumFunctionLayout.m; sourceTree = ""; }; 3BBBA3941905704200824E74 /* MPRangeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MPRangeTests.m; path = ../MathPadTests/MPRangeTests.m; sourceTree = ""; }; - 3BBEA95319BB9B3500133766 /* MPParseError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPParseError.h; sourceTree = ""; }; - 3BBEA95419BB9B3500133766 /* MPParseError.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPParseError.m; sourceTree = ""; }; 3BC4661319B245C60033F13A /* Fonts */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Fonts; sourceTree = ""; }; 3BC4661519B365070033F13A /* MPArrayCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPArrayCache.h; sourceTree = ""; }; 3BC4661619B365070033F13A /* MPArrayCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPArrayCache.m; sourceTree = ""; }; 3BC46B4719B38C980033F13A /* MPExpressionEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPExpressionEvaluator.h; sourceTree = ""; }; 3BC46B4819B38C980033F13A /* MPExpressionEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPExpressionEvaluator.m; sourceTree = ""; }; - 3BE9C4B119B9CC70002CC508 /* MPParsedElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPParsedElement.h; sourceTree = ""; }; - 3BE9C4B219B9CC70002CC508 /* MPParsedElement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPParsedElement.m; sourceTree = ""; }; 3BF9976B18DE623E009CF6C4 /* MathPad.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MathPad.app; sourceTree = BUILT_PRODUCTS_DIR; }; 3BF9976E18DE623E009CF6C4 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 3BF9977118DE623E009CF6C4 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; @@ -365,6 +373,8 @@ 3BFAC39B1997BC7600B3EF67 /* NSString+MPExpressionElement.m */, 3BB09EDC190728220080A5ED /* NSIndexPath+MPAdditions.h */, 3BB09EDD190728220080A5ED /* NSIndexPath+MPAdditions.m */, + 3B52CEDA19BEE63000CEDCFC /* NSRegularExpression+MPParsingAdditions.h */, + 3B52CEDB19BEE63000CEDCFC /* NSRegularExpression+MPParsingAdditions.m */, ); name = Helpers; sourceTree = ""; @@ -374,12 +384,14 @@ children = ( 3BC46B4719B38C980033F13A /* MPExpressionEvaluator.h */, 3BC46B4819B38C980033F13A /* MPExpressionEvaluator.m */, - 3B85830519BB573A00D76A8D /* MPElementParser.h */, - 3B85830619BB573A00D76A8D /* MPElementParser.m */, - 3BBEA95319BB9B3500133766 /* MPParseError.h */, - 3BBEA95419BB9B3500133766 /* MPParseError.m */, - 3BE9C4B119B9CC70002CC508 /* MPParsedElement.h */, - 3BE9C4B219B9CC70002CC508 /* MPParsedElement.m */, + 3B52CEE419C0755300CEDCFC /* MPElementParser.h */, + 3B52CEE519C0755300CEDCFC /* MPElementParser.m */, + 3B52CEE019C070B900CEDCFC /* MPParsedProduct.h */, + 3B52CEE119C070B900CEDCFC /* MPParsedProduct.m */, + 3B71432B19C0A1FC005A184E /* MPParsedFactor.h */, + 3B71432C19C0A1FC005A184E /* MPParsedFactor.m */, + 3B52CECE19BE509C00CEDCFC /* MPParseError.h */, + 3B52CECF19BE509C00CEDCFC /* MPParseError.m */, ); name = Evaluation; sourceTree = ""; @@ -481,21 +493,23 @@ 3B85833819BB63BD00D76A8D /* MPExpression.h in Headers */, 3B85833919BB63C500D76A8D /* MPExpressionElement.h in Headers */, 3B85834419BB654D00D76A8D /* NSString+MPExpressionElement.h in Headers */, - 3BBEA95519BB9B3500133766 /* MPParseError.h in Headers */, + 3B52CEDC19BEE63000CEDCFC /* NSRegularExpression+MPParsingAdditions.h in Headers */, 3B85833A19BB63D400D76A8D /* MPFunction.h in Headers */, 3B85834319BB653700D76A8D /* MPSumFunction.h in Headers */, 3B85834119BB651E00D76A8D /* MPRangePath.h in Headers */, 3B85834219BB652900D76A8D /* MPException.h in Headers */, 3B85834519BB655200D76A8D /* NSIndexPath+MPAdditions.h in Headers */, + 3B52CED019BE509C00CEDCFC /* MPParseError.h in Headers */, 3B85834619BB655C00D76A8D /* MPExpressionView.h in Headers */, 3B85834719BB655F00D76A8D /* MPExpressionStorage.h in Headers */, + 3B52CEE219C070B900CEDCFC /* MPParsedProduct.h in Headers */, 3B85833E19BB651400D76A8D /* MPExpressionEvaluator.h in Headers */, - 3B85833F19BB651400D76A8D /* MPElementParser.h in Headers */, - 3B85834019BB651400D76A8D /* MPParsedElement.h in Headers */, + 3B71432D19C0A1FC005A184E /* MPParsedFactor.h in Headers */, 3B85834819BB65B600D76A8D /* MPLayout.h in Headers */, 3B85834919BB65B600D76A8D /* MPExpressionLayout.h in Headers */, 3B85834A19BB65B600D76A8D /* MPFunctionLayout.h in Headers */, 3B85834B19BB65B600D76A8D /* MPSumFunctionLayout.h in Headers */, + 3B52CEE619C0755300CEDCFC /* MPElementParser.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -660,23 +674,25 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 3B52CEE719C0755300CEDCFC /* MPElementParser.m in Sources */, 3BBEA94219BB79A700133766 /* MPExpressionLayout.m in Sources */, 3BBEA94119BB79A700133766 /* MPLayout.m in Sources */, 3BBEA93D19BB79A700133766 /* NSString+MPExpressionElement.m in Sources */, - 3BBEA93819BB79A700133766 /* MPElementParser.m in Sources */, 3BBEA94019BB79A700133766 /* MPExpressionStorage.m in Sources */, 3BBEA93F19BB79A700133766 /* MPExpressionView.m in Sources */, 3BBEA94419BB79A700133766 /* MPSumFunctionLayout.m in Sources */, + 3B71432E19C0A1FC005A184E /* MPParsedFactor.m in Sources */, 3BBEA93719BB79A700133766 /* MPExpressionEvaluator.m in Sources */, 3BBEA93A19BB79A700133766 /* MPSumFunction.m in Sources */, - 3BBEA93919BB79A700133766 /* MPParsedElement.m in Sources */, 3BBEA93E19BB79A700133766 /* NSIndexPath+MPAdditions.m in Sources */, - 3BBEA95619BB9B3500133766 /* MPParseError.m in Sources */, + 3B52CEDD19BEE63000CEDCFC /* NSRegularExpression+MPParsingAdditions.m in Sources */, 3BBEA93C19BB79A700133766 /* MPException.m in Sources */, + 3B52CED119BE509C00CEDCFC /* MPParseError.m in Sources */, 3BBEA93619BB79A700133766 /* MPFunction.m in Sources */, 3BBEA93519BB79A700133766 /* MPExpression.m in Sources */, 3BBEA93B19BB79A700133766 /* MPRangePath.m in Sources */, 3BBEA94319BB79A700133766 /* MPFunctionLayout.m in Sources */, + 3B52CEE319C070B900CEDCFC /* MPParsedProduct.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/MathPad/MPElementParser.h b/MathPad/MPElementParser.h index 3de2111..8493765 100644 --- a/MathPad/MPElementParser.h +++ b/MathPad/MPElementParser.h @@ -2,17 +2,36 @@ // MPElementParser.h // MathPad // -// Created by Kim Wittenburg on 06.09.14. +// Created by Kim Wittenburg on 10.09.14. // Copyright (c) 2014 Kim Wittenburg. All rights reserved. // #import -#import "MPParsedElement.h" +#import "MPParsedProduct.h" #import "MPParseError.h" +#define MPMaximumOperatorChainLength NSUIntegerMax + @interface MPElementParser : NSObject -- (MPParsedElement *)parseElement:(NSString *)string error:(MPParseError *__autoreleasing *)error; ++ (BOOL)isUsingDefaultValuesFromUserDefaults; ++ (void)setUsingDefaultValuesFromUserDefaults:(BOOL)flag; + +@property (nonatomic) BOOL allowsImplicitMultiplications; // wether 2 3 is equal to 6 or error +// Default value uses the key "..." in NSUserDefaults or ... if the key does not exist. +@property (nonatomic) NSUInteger maximumOperatorChainLength; // +--++-5 -> Chain length: 6, 2 default (sign of number and operator) (0 is invalid?) +@property (nonatomic) NSUInteger maximumOperatorChainLengthInMultiplication; // Default: 1, 0 means actually 0 +@property (nonatomic) NSUInteger maximumOperatorChainLengthInFunction; // For sin, cos, tan. Default: 1 + +- (NSArray *)parseElement:(NSString *)string + previousProduct:(MPParsedProduct *)previousProduct + nextFactor:(MPParsedFactor *)nextFactor + definesVariable:(BOOL)flag + definedVariable:(NSString *__autoreleasing *)variableName + error:(out MPParseError *__autoreleasing *)error; +- (NSArray *)parseElement:(NSString *)string + previousProduct:(MPParsedProduct *)previousProduct + nextFactor:(MPParsedFactor *)nextFactor + error:(out MPParseError *__autoreleasing *)error; @end - diff --git a/MathPad/MPElementParser.m b/MathPad/MPElementParser.m index 059e6b2..83a329d 100644 --- a/MathPad/MPElementParser.m +++ b/MathPad/MPElementParser.m @@ -2,184 +2,335 @@ // MPElementParser.m // MathPad // -// Created by Kim Wittenburg on 06.09.14. +// Created by Kim Wittenburg on 10.09.14. // Copyright (c) 2014 Kim Wittenburg. All rights reserved. // #import "MPElementParser.h" +#import "NSRegularExpression+MPParsingAdditions.h" + +#define MPMultiplicationSymbol @"*" + +@interface MPElementParser () + +@property (nonatomic) NSString *input; +@property (nonatomic) NSUInteger parsePosition; + +- (void)setError:(MPParseError *)error; + +- (BOOL)isAtEnd; + +- (NSRange)parseVariableDefinition:(NSString *__autoreleasing *)variableName; + +- (NSRange)parseWhitespaces; +- (NSRange)parseMultiplicationSymbol; +- (NSRange)parseOperators; +- (NSUInteger)countOperatorsInRange:(NSRange)range + multiplicator:(out NSDecimalNumber *__autoreleasing *)multiplicator; +- (NSRange)parseNumber; +- (NSDecimalNumber *)numberInRange:(NSRange)range; + +@end @implementation MPElementParser { - NSScanner *scanner; + MPParseError *__autoreleasing *outError; } -- (MPParsedElement *)parseElement:(NSString *)string error:(MPParseError *__autoreleasing *)error -{ - // Setup the Scanner - scanner = [[NSScanner alloc] initWithString:string]; - scanner.charactersToBeSkipped = [NSCharacterSet whitespaceAndNewlineCharacterSet]; - scanner.caseSensitive = YES; - - MPParsedElement *parsedElement = [[MPParsedElement alloc] init]; - - // Scan the defined variable - NSString *firstCharacters = nil; - BOOL hasLettersInFront = [scanner scanCharactersFromSet:[NSCharacterSet letterCharacterSet] - intoString:&firstCharacters]; - BOOL foundEquals = [scanner scanString:@"=" - intoString:NULL]; - - if (hasLettersInFront && firstCharacters.length == 1 && foundEquals) { - // Found variable definition - parsedElement.definedVariable = firstCharacters; - parsedElement.afterVariableDefinitionIndex = scanner.scanLocation; - } else { - // No variable definition found, reset the scanner - parsedElement.definedVariable = nil; - scanner.scanLocation = 0; - } - - // Scan the Prefix - BOOL startsWithAsterisk = [scanner scanString:@"*" - intoString:NULL]; - BOOL productClosed; - NSDecimalNumber *prefix = [self scanClosedProduct:&productClosed - error:NULL]; - - // Simple Factor - if (scanner.isAtEnd) { - parsedElement.isFactor = YES; - parsedElement.value = prefix == nil ? [NSDecimalNumber one] : prefix; - parsedElement.prefixOperatorExplicit = startsWithAsterisk; - parsedElement.suffixOperatorExplicit = !productClosed; - parsedElement.suffixIndex = scanner.scanLocation; - return parsedElement; - } else if (!productClosed) { - if (error) { - *error = MPParseError(scanner.scanLocation, @"Missing Number"); - } - return nil; - } - - parsedElement.prefixOperatorExplicit = startsWithAsterisk; - parsedElement.prefixValueExplicit = YES; - parsedElement.prefixMultiplicator = prefix; - - parsedElement.suffixValueExplicit = YES; - - // Scan Summands - NSDecimalNumber *value = [NSDecimalNumber zero]; - NSDecimalNumber *currentSummand; - while (!scanner.isAtEnd) { +static BOOL useUserDefaults; - // Add every summand but the last one - if (currentSummand != nil) { - value = [value decimalNumberByAdding:currentSummand]; - } - - // Find the operator (+ or -) - NSString *operator; - BOOL found = [scanner scanString:@"+" - intoString:&operator]; - if (!found) { - found = [scanner scanString:@"-" - intoString:&operator]; - } - if (!found && !scanner.isAtEnd) { - // Two numbers separated by just a space - if (error) { - *error = MPParseError(scanner.scanLocation, @"Missing Operator"); - } +#pragma mark Creation Methods + ++ (void)initialize +{ + useUserDefaults = YES; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + self.allowsImplicitMultiplications = NO; + self.maximumOperatorChainLength = 2; + self.maximumOperatorChainLengthInMultiplication = 1; + } + return self; +} + +#pragma mark Properties + ++ (BOOL)isUsingDefaultValuesFromUserDefaults +{ + return useUserDefaults; +} + ++ (void)setUsingDefaultValuesFromUserDefaults:(BOOL)flag +{ + useUserDefaults = flag; +} + +#pragma mark Parsing Methods + +- (BOOL)isAtEnd +{ + return self.parsePosition >= self.input.length; +} + +- (void)setError:(MPParseError *)error +{ + if (outError != NULL) { + *outError = error; + } +} + +- (NSArray *)parseElement:(NSString *)string + previousProduct:(MPParsedProduct *)previousProduct + nextFactor:(MPParsedFactor *)nextFactor + error:(out MPParseError *__autoreleasing *)error +{ + return [self parseElement:string + previousProduct:previousProduct + nextFactor:nextFactor + definesVariable:NO + definedVariable:NULL + error:error]; +} + +- (NSArray *)parseElement:(NSString *)string + previousProduct:(MPParsedProduct *)previousProduct + nextFactor:(MPParsedFactor *)nextFactor + definesVariable:(BOOL)flag + definedVariable:(NSString *__autoreleasing *)variableName + error:(out MPParseError *__autoreleasing *)error +{ + self.input = string; + outError = error; + self.parsePosition = 0; + + NSString *definedVariable; + NSRange variableDefinitionRange = [self parseVariableDefinition:&definedVariable]; + if (flag) { + if (!definedVariable) { + self.error = MPParseError(NSMakeRange(0, 0), @"Expected Variable Definition"); return nil; - } else if (!found) { - // Reached end of string - break; - } - - currentSummand = [self scanClosedProduct:&productClosed - error:error]; - - // No number was found - if (currentSummand == nil) { - // The string ends wit + or - - if (scanner.isAtEnd) { - productClosed = NO; - parsedElement.suffixValueExplicit = NO; - NSInteger suffix = [operator isEqualToString:@"+"] ? 1 : -1; - currentSummand = [[NSDecimalNumber alloc] initWithInteger:suffix]; - if (error) { - *error = nil; - } - break; - // Something else instead of a number - } else { - if (error) { - *error = MPParseError(scanner.scanLocation, @"Expected Number"); - } - return nil; - } - } - - // Process the current summand - if ([operator isEqualToString:@"-"]) { - currentSummand = [currentSummand decimalNumberByMultiplyingBy:[[NSDecimalNumber alloc] initWithInteger:-1]]; - } - - // The summand ends with a * - if (!productClosed) { - // The string ends with a * - if (scanner.isAtEnd) { - parsedElement.suffixValueExplicit = YES; - break; - // After a * followed something else than a number - } else { - if (error) { - *error = MPParseError(scanner.scanLocation, @"Unexpected Symbol"); - } - return nil; - } - } - } - - parsedElement.value = value; - parsedElement.suffixOperatorExplicit = !productClosed; - parsedElement.suffixMultiplicator = currentSummand; - parsedElement.suffixIndex = scanner.scanLocation; - - return parsedElement; -} - -- (NSDecimalNumber *)scanClosedProduct:(BOOL *)closed error:(MPParseError **)error -{ - NSDecimal decimal; - BOOL found = [scanner scanDecimal:&decimal]; - if (!found) { - if (error != NULL) { - *error = MPParseError(scanner.scanLocation, @"Unexpected Symbol"); + } else if (variableName) { + *variableName = definedVariable; } + } else if (definedVariable) { + self.error = MPParseError(variableDefinitionRange, @"Unexpected Variable Definition"); return nil; } - NSDecimalNumber *product = [NSDecimalNumber decimalNumberWithDecimal:decimal]; - while (true) { - found = [scanner scanString:@"*" - intoString:NULL]; - if (found) { - found = [scanner scanDecimal:&decimal]; - if (found) { - product = [product decimalNumberByMultiplyingBy:[NSDecimalNumber decimalNumberWithDecimal:decimal]]; - } else { - if (closed != NULL) { - *closed = NO; + + NSMutableArray *products = [[NSMutableArray alloc] init]; + MPParsedProduct *currentProduct = previousProduct; + + while (![self isAtEnd]) { + [self parseWhitespaces]; + + NSRange multiplicationSymbolRange = [self parseMultiplicationSymbol]; + BOOL hasMultiplicationSymbol = multiplicationSymbolRange.location != NSNotFound; + + NSRange operatorsRange = [self parseOperators]; + BOOL hasOperators = operatorsRange.location != NSNotFound; + + // NSRange functionRange = ... + // BOOL hasFunction = functionRange.location != NSNotFound; + + NSRange numberRange = [self parseNumber]; + BOOL hasNumber = numberRange.location != NSNotFound; + + + NSDecimalNumber *operatorMultiplicator; + NSUInteger operatorCount = [self countOperatorsInRange:operatorsRange + multiplicator:&operatorMultiplicator]; + + if (!hasNumber) { + if ([self isAtEnd] && nextFactor != nil) { + if (hasMultiplicationSymbol) { + if (operatorCount > self.maximumOperatorChainLengthInFunction) { + self.error = MPParseError(operatorsRange, @"Too many operators in multiplication."); + return nil; + } + [currentProduct addFactor:[MPParsedFactor factorWithDecimalNumber:operatorMultiplicator]]; + } else if (hasOperators) { + if (operatorCount > self.maximumOperatorChainLength) { + self.error = MPParseError(operatorsRange, @"Too many operators."); + return nil; + } + [products addObject:currentProduct]; + currentProduct = [[MPParsedProduct alloc] init]; + [currentProduct addFactor:[MPParsedFactor factorWithDecimalNumber:[[NSDecimalNumber alloc] initWithUnsignedInteger:operatorCount]]]; + } else if (self.allowsImplicitMultiplications) { + if (!currentProduct) { + currentProduct = [[MPParsedProduct alloc] init]; + } + } else { + self.error = MPParseError(NSMakeRange(self.parsePosition, 0), @"Implicit Multiplication not allowed."); + return nil; } - return product; + break; + } else if ([self isAtEnd]) { + self.error = MPParseError(NSMakeRange(self.parsePosition, 0), @"Unexpected End. Expected Number."); + return nil; + } else { + self.error = MPParseError(NSMakeRange(self.parsePosition, 1), @"Unexpected Symbol. Expected Number."); + return nil; } } else { - break; + NSDecimalNumber *number = [self numberInRange:numberRange]; + NSDecimalNumber *value = [operatorMultiplicator decimalNumberByMultiplyingBy:number]; + + if (hasMultiplicationSymbol) { + if (currentProduct) { + if (operatorCount > self.maximumOperatorChainLengthInMultiplication) { + self.error = MPParseError(operatorsRange, @"Too many operators in multiplication."); + return nil; + } + [currentProduct addFactor:[MPParsedFactor factorWithDecimalNumber:value]]; + } else { + self.error = MPParseError(multiplicationSymbolRange, @"Unexpected Symbol. Expected Number."); + return nil; + } + } else if (hasOperators) { + if (operatorCount > self.maximumOperatorChainLength) { + self.error = MPParseError(operatorsRange, @"Too many operators."); + return nil; + } + if (currentProduct) { + [products addObject:currentProduct]; + } + currentProduct = [[MPParsedProduct alloc] init]; + [currentProduct addFactor:[MPParsedFactor factorWithDecimalNumber:value]]; + } else if (!currentProduct) { + currentProduct = [[MPParsedProduct alloc] init]; + [currentProduct addFactor:[MPParsedFactor factorWithDecimalNumber:value]]; + } else if (self.allowsImplicitMultiplications) { + [currentProduct addFactor:[MPParsedFactor factorWithDecimalNumber:value]]; + } else { + self.error = MPParseError(NSMakeRange(numberRange.location, 0), @"Implicit Multiplication not allowed."); + return nil; + } } } - if (closed != NULL) { - *closed = YES; + if (nextFactor) { + [currentProduct addFactor:nextFactor]; } - return product; + [products addObject:currentProduct]; + return products; +} + +static NSRegularExpression *variableDefinitionRegex; +- (NSRange)parseVariableDefinition:(NSString *__autoreleasing *)variableName +{ + if (!variableDefinitionRegex) { + variableDefinitionRegex = [NSRegularExpression regularExpressionWithPattern:@"\\A\\s*([A-Za-z])\\s*=\\s*" + options:0 + error:NULL]; + } + + NSTextCheckingResult *match = [variableDefinitionRegex firstMatchInString:self.input + fromIndex:self.parsePosition]; + if (!match) { + return NSMakeRange(NSNotFound, 0); + } + self.parsePosition = NSMaxRange(match.range); + NSRange variableDefinitionRange = [match rangeAtIndex:1]; + *variableName = [self.input substringWithRange:variableDefinitionRange]; + return variableDefinitionRange; +} + +static NSRegularExpression *whitespaceRegex; +- (NSRange)parseWhitespaces +{ + if (!whitespaceRegex) { + whitespaceRegex = [NSRegularExpression regularExpressionWithPattern:@"\\A\\s*" + options:0 + error:NULL]; + } + NSTextCheckingResult *match = [whitespaceRegex firstMatchInString:self.input + fromIndex:self.parsePosition]; + if (match) { + self.parsePosition = NSMaxRange(match.range); + return match.range; + } else { + return NSMakeRange(NSNotFound, 0); + } +} + +static NSRegularExpression *multiplicationSymbolRegex; +- (NSRange)parseMultiplicationSymbol +{ + if (!multiplicationSymbolRegex) { + NSString *multiplicationSymbolString = [NSRegularExpression escapedPatternForString:MPMultiplicationSymbol]; + NSString *multiplicationSymbolRegexString = [NSString stringWithFormat:@"\\A\\s*(%@)\\s*", multiplicationSymbolString]; + multiplicationSymbolRegex = [NSRegularExpression regularExpressionWithPattern:multiplicationSymbolRegexString + options:0 + error:NULL]; + } + NSTextCheckingResult *match = [multiplicationSymbolRegex firstMatchInString:self.input + fromIndex:self.parsePosition]; + if (!match) { + return NSMakeRange(NSNotFound, 0); + } + self.parsePosition = NSMaxRange(match.range); + return [match rangeAtIndex:1]; +} + +static NSRegularExpression *operatorsRegex; +- (NSRange)parseOperators +{ + if (!operatorsRegex) { + operatorsRegex = [NSRegularExpression regularExpressionWithPattern:@"\\A\\s*([+-](?:\\s*[+-])*)\\s*" + options:0 + error:NULL]; + } + NSTextCheckingResult *match = [operatorsRegex firstMatchInString:self.input + fromIndex:self.parsePosition]; + if (match == nil) { + return NSMakeRange(NSNotFound, 0); + } + self.parsePosition = NSMaxRange(match.range); + return [match rangeAtIndex:1]; +} + +- (NSUInteger)countOperatorsInRange:(NSRange)range + multiplicator:(out NSDecimalNumber *__autoreleasing *)outMultiplicator +{ + if (range.location == NSNotFound) { + *outMultiplicator = [NSDecimalNumber one]; + return 0; + } + NSString *operatorsString = [self.input substringWithRange:range]; + NSString *operators = [[operatorsString componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] componentsJoinedByString:@""]; + NSInteger multiplicator = 1; + for (NSUInteger characterIndex; characterIndex < operators.length; characterIndex++) { + if ([[operators substringWithRange:NSMakeRange(characterIndex, 1)] isEqualToString:@"-"]) { + multiplicator *= -1; + } + } + *outMultiplicator = [[NSDecimalNumber alloc] initWithInteger:multiplicator]; + return operators.length; +} + +- (NSRange)parseNumber +{ + NSString *decimalSeparatorRegexString = [NSRegularExpression escapedPatternForString:[[NSLocale currentLocale] objectForKey:NSLocaleDecimalSeparator]]; + NSString *numberRegexFormat = [NSString stringWithFormat:@"\\A\\s*((?:\\d+(?:%@\\d+)?)|(?:%@\\d+))\\s*", decimalSeparatorRegexString, decimalSeparatorRegexString]; + NSRegularExpression *numberRegex = [NSRegularExpression regularExpressionWithPattern:numberRegexFormat + options:0 + error:NULL]; + NSTextCheckingResult *match = [numberRegex firstMatchInString:self.input + fromIndex:self.parsePosition]; + if (!match) { + return NSMakeRange(NSNotFound, 0); + } + self.parsePosition = NSMaxRange(match.range); + return [match rangeAtIndex:1]; +} + +- (NSDecimalNumber *)numberInRange:(NSRange)range +{ + NSString *numberString = [self.input substringWithRange:range]; + return [NSDecimalNumber decimalNumberWithString:numberString]; } @end diff --git a/MathPad/MPExpressionElement.h b/MathPad/MPExpressionElement.h index 652b1db..41f8332 100644 --- a/MathPad/MPExpressionElement.h +++ b/MathPad/MPExpressionElement.h @@ -17,6 +17,4 @@ - (NSUInteger)length; -- (NSDecimalNumber *)evaluate:(MPParseError *__autoreleasing *)error; - @end diff --git a/MathPad/MPExpressionEvaluator.h b/MathPad/MPExpressionEvaluator.h index 7313660..56a894d 100644 --- a/MathPad/MPExpressionEvaluator.h +++ b/MathPad/MPExpressionEvaluator.h @@ -10,7 +10,7 @@ #import "MPExpression.h" #import "MPElementParser.h" -@class MPExpressionEvaluator, MPExpression, MPParsedElement; +@class MPExpressionEvaluator, MPExpression, MPParsedElementOld; @interface MPExpressionEvaluator : NSObject @@ -25,8 +25,9 @@ - (void)bindValue:(NSDecimalNumber *)value toVariableName:(NSString *)name; - (void)unbindVariableName:(NSString *)name; -- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error; -- (NSDecimalNumber *)evaluateVariableDefinition:(BOOL)flag error:(MPParseError *__autoreleasing *)error; @property (readonly, nonatomic, strong) NSString *definedVariable; +- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error; +- (NSDecimalNumber *)evaluateVariableDefinition:(BOOL)flag error:(MPParseError *__autoreleasing *)error; + @end diff --git a/MathPad/MPExpressionEvaluator.m b/MathPad/MPExpressionEvaluator.m index 1ab29df..accc3ae 100644 --- a/MathPad/MPExpressionEvaluator.m +++ b/MathPad/MPExpressionEvaluator.m @@ -8,9 +8,11 @@ #import "MPExpressionEvaluator.h" #import "MPExpression.h" +#import "MPFunction.h" @interface MPExpressionEvaluator () @property (readwrite, nonatomic, strong) NSString *definedVariable; + @end @implementation MPExpressionEvaluator { @@ -52,146 +54,74 @@ - (NSDecimalNumber *)evaluateVariableDefinition:(BOOL)flag error:(MPParseError *__autoreleasing *)error { -#warning Implement Cache // Empty Expression if (self.expression.numberOfElements == 0) { - if (error) { - *error = MPParseError(0, @"Empty Expression"); - } + *error = MPParseError(NSMakeRange(0, 0), @"Expected Expression"); return nil; } - - // Expression of just one element - if (self.expression.numberOfElements == 1) { - id singleElement = [self.expression elementAtIndex:0]; - if ([singleElement isString]) { - MPParsedElement *parseResult = [parser parseElement:(NSString *)singleElement - error:error]; - if ([parseResult isValidStandaloneElement:error]) { - if (flag && parseResult.definedVariable) { - self.definedVariable = parseResult.definedVariable; - } else if (flag) { - if (error) { - *error = MPParseError(0, @"Expected Variable Definition"); - } - return nil; - } else if (parseResult.definedVariable) { - if (error) { - *error = MPParseError(0, @"Unexpected Variable Definition"); - } - return nil; - } - return parseResult.standaloneValue; - } else { - return nil; - } - } else { - return [singleElement evaluate:error]; - } - } - // Expression with any number of elements - NSDecimalNumber *value = [NSDecimalNumber zero]; - NSDecimalNumber *currentSummand = nil; + NSMutableArray *products = [[NSMutableArray alloc] init]; + MPParsedProduct *currentProduct = nil; - // Process the first element - id firstElement = [self.expression elementAtIndex:0]; - if ([firstElement isString]) { - MPParsedElement *parseResult = [parser parseElement:(NSString *)firstElement - error:error]; - if (parseResult && [parseResult isValidElementAtBeginning:error]) { - if (flag && parseResult.definedVariable) { - self.definedVariable = parseResult.definedVariable; - } else if (flag) { - if (error) { - *error = MPParseError(0, @"Expected Variable Definition"); - } - return nil; - } else if (parseResult.definedVariable) { - if (error) { - *error = MPParseError(0, @"Unexpected Variable Definition"); - } - return nil; - } - if ([parseResult isFactor]) { - currentSummand = parseResult.value; - } else { - value = [parseResult valueAtBeginning]; - currentSummand = parseResult.suffixMultiplicator; - } - } - } else { - currentSummand = [firstElement evaluate:error]; - } - if (!currentSummand) { - return nil; - } - - // Process the elements between the first and last element - for (NSUInteger index = 1; index < self.expression.numberOfElements-1; index++) { - id element = [self.expression elementAtIndex:index]; + for (NSUInteger elementIndex = 0; elementIndex < self.expression.numberOfElements; elementIndex++) { + + id element = [self.expression elementAtIndex:elementIndex]; + if ([element isString]) { - MPParsedElement *parseResult = [parser parseElement:(NSString *)element - error:error]; - if (parseResult) { - if (parseResult.definedVariable) { - if (error) { - *error = MPParseError(index, @"Unexpected Variable Definition"); - } + MPParsedFactor *nextFactor = nil; + if (elementIndex < self.expression.numberOfElements - 1) { + MPFunction *nextFunction = (MPFunction *)[self.expression elementAtIndex:elementIndex+1]; + NSDecimalNumber *functionValue = [nextFunction evaluate:error]; + if (!functionValue) { return nil; } - if ([parseResult isFactor]) { - currentSummand = [currentSummand decimalNumberByMultiplyingBy:parseResult.value]; - } else { - currentSummand = [currentSummand decimalNumberByMultiplyingBy:parseResult.prefixMultiplicator]; - value = [[value decimalNumberByAdding:currentSummand] decimalNumberByAdding:parseResult.value]; - currentSummand = parseResult.suffixMultiplicator; - } + nextFactor = [MPParsedFactor factorWithDecimalNumber:functionValue]; + } + + NSArray *newProducts; + if (elementIndex == 0 && flag) { + NSString *definedVariable; + newProducts = [parser parseElement:(NSString *)element + previousProduct:currentProduct + nextFactor:nextFactor + definesVariable:YES + definedVariable:&definedVariable + error:error]; + self.definedVariable = definedVariable; } else { + newProducts = [parser parseElement:(NSString *)element + previousProduct:currentProduct + nextFactor:nextFactor + error:error]; + } + if (!newProducts) { return nil; } + + for (NSUInteger productIndex = 0; productIndex < newProducts.count-1; productIndex++) { + [products addObject:newProducts[productIndex]]; + } + currentProduct = newProducts.lastObject; + + elementIndex++; } else { - NSDecimalNumber *result = [element evaluate:error]; - if (result) { - currentSummand = [currentSummand decimalNumberByMultiplyingBy:result]; - } else { + NSDecimalNumber *functionValue = [(MPFunction *)element evaluate:error]; + if (!functionValue) { return nil; } + if (!currentProduct) { + currentProduct = [[MPParsedProduct alloc] init]; + } + [currentProduct addFactor:[MPParsedFactor factorWithDecimalNumber:functionValue]]; } } - // Process the last element - id lastElement = [self.expression elementAtIndex:self.expression.numberOfElements-1]; - if ([lastElement isString]) { - MPParsedElement *parseResult = [parser parseElement:(NSString *)lastElement - error:error]; - if (parseResult && [parseResult isValidElementAtEnd:error]) { - if (parseResult.definedVariable) { - if (error) { - *error = MPParseError(0, @"Unexpected Variable Definition"); - } - return nil; - } - if ([parseResult isFactor]) { - currentSummand = [currentSummand decimalNumberByMultiplyingBy:parseResult.value]; - } else { - currentSummand = [currentSummand decimalNumberByMultiplyingBy:parseResult.prefixMultiplicator]; - value = [[value decimalNumberByAdding:currentSummand] decimalNumberByAdding:parseResult.value]; - currentSummand = parseResult.suffixMultiplicator; - } - } else { - return nil; - } - } else { - NSDecimalNumber *result = [lastElement evaluate:error]; - if (result) { - currentSummand = [currentSummand decimalNumberByMultiplyingBy:result]; - } else { - return nil; - } - } - value = [value decimalNumberByAdding:currentSummand]; + [products addObject:currentProduct]; + NSDecimalNumber *value = [NSDecimalNumber zero]; + for (MPParsedProduct *product in products) { + value = [value decimalNumberByAdding:product.value]; + } return value; } diff --git a/MathPad/MPParseError.h b/MathPad/MPParseError.h index 4308536..4c60175 100644 --- a/MathPad/MPParseError.h +++ b/MathPad/MPParseError.h @@ -2,20 +2,25 @@ // MPParseError.h // MathPad // -// Created by Kim Wittenburg on 06.09.14. +// Created by Kim Wittenburg on 08.09.14. // Copyright (c) 2014 Kim Wittenburg. All rights reserved. // #import -#define MPParseError(index,key) [[MPParseError alloc] initWithErrorIndex:index errorMessageKey:key] +#define MPParseError(range,key) [[MPParseError alloc] initWithErrorRange:range errorMessageKey:key] @interface MPParseError : NSObject -- (instancetype)initWithErrorIndex:(NSUInteger)errorIndex - errorMessageKey:(NSString *)errorMessageKey; +- (instancetype)initWithErrorRange:(NSRange)errorRange + errorMessageKey:(NSString *)key; -@property (nonatomic) NSUInteger errorIndex; -@property (nonatomic) NSString *localizedErrorMessage; +- (instancetype)initWithErrorRange:(NSRange)errorRange + localizederrorMessage:(NSString *)errorMessage; -@end \ No newline at end of file +@property (nonatomic, strong) NSIndexPath *pathToExpression; + +@property (nonatomic) NSRange errorRange; +@property (nonatomic, copy) NSString *localizedErrorMessage; + +@end diff --git a/MathPad/MPParseError.m b/MathPad/MPParseError.m index 751ec20..1ca6f72 100644 --- a/MathPad/MPParseError.m +++ b/MathPad/MPParseError.m @@ -2,7 +2,7 @@ // MPParseError.m // MathPad // -// Created by Kim Wittenburg on 06.09.14. +// Created by Kim Wittenburg on 08.09.14. // Copyright (c) 2014 Kim Wittenburg. All rights reserved. // @@ -10,19 +10,29 @@ @implementation MPParseError -- (instancetype)initWithErrorIndex:(NSUInteger)errorIndex errorMessageKey:(NSString *)errorMessageKey +- (instancetype)initWithErrorRange:(NSRange)errorRange errorMessageKey:(NSString *)key { self = [super init]; if (self) { - _errorIndex = errorIndex; - _localizedErrorMessage = NSLocalizedString(errorMessageKey, nil); + _errorRange = errorRange; + _localizedErrorMessage = NSLocalizedString(key, nil); + } + return self; +} + +- (instancetype)initWithErrorRange:(NSRange)errorRange localizederrorMessage:(NSString *)errorMessage +{ + self = [super init]; + if (self) { + _errorRange = errorRange; + _localizedErrorMessage = errorMessage; } return self; } - (NSString *)description { - return [NSString stringWithFormat:@"MPParseError", self.errorIndex, self.localizedErrorMessage]; + return [NSString stringWithFormat:@"MPParseError", self.errorRange.location, self.errorRange.length, self.localizedErrorMessage]; } @end diff --git a/MathPad/MPParsedFactor.h b/MathPad/MPParsedFactor.h new file mode 100644 index 0000000..280e4b3 --- /dev/null +++ b/MathPad/MPParsedFactor.h @@ -0,0 +1,22 @@ +// +// MPParsedFactor.h +// MathPad +// +// Created by Kim Wittenburg on 10.09.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import + +@interface MPParsedFactor : NSObject + ++ (MPParsedFactor *)factorWithDecimalNumber:(NSDecimalNumber *)number; ++ (MPParsedFactor *)sinFactorWithFactor:(MPParsedFactor *)factor; ++ (MPParsedFactor *)cosFactorWithFactor:(MPParsedFactor *)factor; ++ (MPParsedFactor *)tanFactorWithFactor:(MPParsedFactor *)factor; + +- (instancetype)initWithDecimalNumber:(NSDecimalNumber *)number; + +- (NSDecimalNumber *)value; + +@end diff --git a/MathPad/MPParsedFactor.m b/MathPad/MPParsedFactor.m new file mode 100644 index 0000000..9f4aff2 --- /dev/null +++ b/MathPad/MPParsedFactor.m @@ -0,0 +1,60 @@ +// +// MPParsedFactor.m +// MathPad +// +// Created by Kim Wittenburg on 10.09.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import "MPParsedFactor.h" + +@implementation MPParsedFactor { + NSDecimalNumber *_value; +} + ++ (MPParsedFactor *)factorWithDecimalNumber:(NSDecimalNumber *)number +{ + return [[MPParsedFactor alloc] initWithDecimalNumber:number]; +} + ++ (MPParsedFactor *)sinFactorWithFactor:(MPParsedFactor *)factor +{ + double value = factor.value.doubleValue; + NSDecimalNumber *actualNumber = [[NSDecimalNumber alloc] initWithDouble:sin(value)]; + return [[MPParsedFactor alloc] initWithDecimalNumber:actualNumber]; +} + ++ (MPParsedFactor *)cosFactorWithFactor:(MPParsedFactor *)factor +{ + double value = factor.value.doubleValue; + NSDecimalNumber *actualNumber = [[NSDecimalNumber alloc] initWithDouble:cos(value)]; + return [[MPParsedFactor alloc] initWithDecimalNumber:actualNumber]; +} + ++ (MPParsedFactor *)tanFactorWithFactor:(MPParsedFactor *)factor +{ + double value = factor.value.doubleValue; + NSDecimalNumber *actualNumber = [[NSDecimalNumber alloc] initWithDouble:tan(value)]; + return [[MPParsedFactor alloc] initWithDecimalNumber:actualNumber]; +} + +- (instancetype)initWithDecimalNumber:(NSDecimalNumber *)number +{ + self = [super init]; + if (self) { + _value = number; + } + return self; +} + +- (NSDecimalNumber *)value +{ + return _value; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"MPParsedFactor<%@>", _value]; +} + +@end \ No newline at end of file diff --git a/MathPad/MPParsedProduct.h b/MathPad/MPParsedProduct.h new file mode 100644 index 0000000..e9cb974 --- /dev/null +++ b/MathPad/MPParsedProduct.h @@ -0,0 +1,20 @@ +// +// MPParsedSummand.h +// MathPad +// +// Created by Kim Wittenburg on 10.09.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import +#import "MPParsedFactor.h" + +@interface MPParsedProduct : NSObject + +@property (readonly, nonatomic, strong) NSArray *factors; + +- (void)addFactor:(MPParsedFactor *)factor; + +- (NSDecimalNumber *)value; + +@end diff --git a/MathPad/MPParsedProduct.m b/MathPad/MPParsedProduct.m new file mode 100644 index 0000000..738fc91 --- /dev/null +++ b/MathPad/MPParsedProduct.m @@ -0,0 +1,51 @@ +// +// MPParsedSummand.m +// MathPad +// +// Created by Kim Wittenburg on 10.09.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import "MPParsedProduct.h" + +@implementation MPParsedProduct { + NSMutableArray *_factors; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + _factors = [[NSMutableArray alloc] init]; + } + return self; +} + +- (NSArray *)factors +{ + return _factors; +} + +- (void)addFactor:(MPParsedFactor *)factor +{ + [_factors addObject:factor]; +} + +- (NSDecimalNumber *)value +{ + if (_factors.count == 0) { + return [NSDecimalNumber zero]; + } + NSDecimalNumber *value = [NSDecimalNumber one]; + for (MPParsedFactor *factor in _factors) { + value = [value decimalNumberByMultiplyingBy:factor.value]; + } + return value; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"MPParsedProduct<%@>", [_factors componentsJoinedByString:@"*"]]; +} + +@end diff --git a/MathPad/MPSumFunctionLayout.m b/MathPad/MPSumFunctionLayout.m index 8f64c47..de60ed0 100644 --- a/MathPad/MPSumFunctionLayout.m +++ b/MathPad/MPSumFunctionLayout.m @@ -33,6 +33,14 @@ return 2; } +- (NSUInteger)indexOfChildAfterChildAtIndex:(NSUInteger)index +{ + if (index != 2) { + return 2; + } + return [super indexOfChildAfterChildAtIndex:index]; +} + - (CTLineRef)line { CTLineRef line = [self lineForPrivateCacheIndex:0 generator:^CTLineRef{ diff --git a/MathPad/NSRegularExpression+MPParsingAdditions.h b/MathPad/NSRegularExpression+MPParsingAdditions.h new file mode 100644 index 0000000..58f4c18 --- /dev/null +++ b/MathPad/NSRegularExpression+MPParsingAdditions.h @@ -0,0 +1,21 @@ +// +// NSRegularExpression+MPParsingAdditions.h +// MathPad +// +// Created by Kim Wittenburg on 09.09.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import + +#define MPStringRange(string) NSMakeRange(0, [string length]) +#define MPStringRangeFrom(from, string) NSMakeRange(from, [string length]-from) + +@interface NSRegularExpression (MPParsingAdditions) + +- (NSTextCheckingResult *)firstMatchInString:(NSString *)string; +- (NSTextCheckingResult *)firstMatchInString:(NSString *)string fromIndex:(NSUInteger)start; +- (NSTextCheckingResult *)firstMatchInString:(NSString *)string options:(NSMatchingOptions)options; +- (NSTextCheckingResult *)firstMatchInString:(NSString *)string options:(NSMatchingOptions)options fromIndex:(NSUInteger)start; + +@end diff --git a/MathPad/NSRegularExpression+MPParsingAdditions.m b/MathPad/NSRegularExpression+MPParsingAdditions.m new file mode 100644 index 0000000..d3f0f42 --- /dev/null +++ b/MathPad/NSRegularExpression+MPParsingAdditions.m @@ -0,0 +1,33 @@ +// +// NSRegularExpression+MPParsingAdditions.m +// MathPad +// +// Created by Kim Wittenburg on 09.09.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import "NSRegularExpression+MPParsingAdditions.h" + +@implementation NSRegularExpression (MPParsingAdditions) + +- (NSTextCheckingResult *)firstMatchInString:(NSString *)string +{ + return [self firstMatchInString:string options:0 range:MPStringRange(string)]; +} + +- (NSTextCheckingResult *)firstMatchInString:(NSString *)string fromIndex:(NSUInteger)start +{ + return [self firstMatchInString:string options:0 range:NSMakeRange(start, [string length]-start)]; +} + +- (NSTextCheckingResult *)firstMatchInString:(NSString *)string options:(NSMatchingOptions)options +{ + return [self firstMatchInString:string options:options range:MPStringRange(string)]; +} + +- (NSTextCheckingResult *)firstMatchInString:(NSString *)string options:(NSMatchingOptions)options fromIndex:(NSUInteger)start +{ + return [self firstMatchInString:string options:options range:NSMakeRange(start, [string length]-start)]; +} + +@end diff --git a/MathPad/NSString+MPExpressionElement.m b/MathPad/NSString+MPExpressionElement.m index 9102518..ba86447 100644 --- a/MathPad/NSString+MPExpressionElement.m +++ b/MathPad/NSString+MPExpressionElement.m @@ -21,9 +21,4 @@ return NO; } -- (NSDecimalNumber *)evaluate:(MPParseError *__autoreleasing *)error -{ - return [[[MPElementParser alloc] init] parseElement:self error:error].standaloneValue; -} - @end