Archived
1

Internal Redesign:

- Combined MPExpression and MPMutableExpression
- Abstracted children of MPExpression into MPExpressionElement protocol
- Abstracted most of MPExpressionLayout and MPFunctionLayout into common superclass MPLayout
This commit is contained in:
Kim Wittenburg
2014-08-11 13:57:48 +02:00
parent 740c3fd80a
commit 60760b8b3d
31 changed files with 1222 additions and 1343 deletions

View File

@@ -9,20 +9,20 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
3B0F69A919028C6000817707 /* MPException.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B0F69A819028C6000817707 /* MPException.m */; }; 3B0F69A919028C6000817707 /* MPException.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B0F69A819028C6000817707 /* MPException.m */; };
3B0F69AC1902A82C00817707 /* MPExpressionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B0F69AB1902A82C00817707 /* MPExpressionTests.m */; }; 3B0F69AC1902A82C00817707 /* MPExpressionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B0F69AB1902A82C00817707 /* MPExpressionTests.m */; };
3B0F69AD1902AD2200817707 /* MPExpression.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E3581900857C00259938 /* MPExpression.m */; };
3B0F69AE1902AD2800817707 /* MPException.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B0F69A819028C6000817707 /* MPException.m */; }; 3B0F69AE1902AD2800817707 /* MPException.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B0F69A819028C6000817707 /* MPException.m */; };
3B0F69B01902AD2E00817707 /* MPFunction.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E35F19009D5F00259938 /* MPFunction.m */; }; 3B0F69B01902AD2E00817707 /* MPFunction.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E35F19009D5F00259938 /* MPFunction.m */; };
3B528D10199417E10054DB5F /* MPExpressionLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B528D0F199417E10054DB5F /* MPExpressionLayout.m */; };
3B528D13199417E90054DB5F /* MPFunctionLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B528D12199417E90054DB5F /* MPFunctionLayout.m */; };
3B528D1619941F5B0054DB5F /* MPExpressionLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B528D0F199417E10054DB5F /* MPExpressionLayout.m */; };
3B528D1819941F5B0054DB5F /* MPFunctionLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B528D12199417E90054DB5F /* MPFunctionLayout.m */; };
3B53AD5F1997E0FB00C925C4 /* MPExpression.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BFAC38E1997B61300B3EF67 /* MPExpression.m */; };
3B688D9919982DF50006B4AB /* MPLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B688D9819982DF50006B4AB /* MPLayout.m */; };
3B87E3561900856F00259938 /* MPExpressionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E3551900856F00259938 /* MPExpressionView.m */; }; 3B87E3561900856F00259938 /* MPExpressionView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E3551900856F00259938 /* MPExpressionView.m */; };
3B87E3591900857C00259938 /* MPExpression.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E3581900857C00259938 /* MPExpression.m */; };
3B87E36019009D5F00259938 /* MPFunction.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E35F19009D5F00259938 /* MPFunction.m */; }; 3B87E36019009D5F00259938 /* MPFunction.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E35F19009D5F00259938 /* MPFunction.m */; };
3BB09EB21905DE500080A5ED /* MPExpressionStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB09EB11905DE500080A5ED /* MPExpressionStorage.m */; }; 3BB09EB21905DE500080A5ED /* MPExpressionStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB09EB11905DE500080A5ED /* MPExpressionStorage.m */; };
3BB09EC91906FD830080A5ED /* MPSumFunction.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB09EC81906FD830080A5ED /* MPSumFunction.m */; }; 3BB09EC91906FD830080A5ED /* MPSumFunction.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB09EC81906FD830080A5ED /* MPSumFunction.m */; };
3BB09ED0190713F00080A5ED /* MPExpressionLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB09ECF190713F00080A5ED /* MPExpressionLayout.m */; }; 3BB09EDE190728220080A5ED /* NSIndexPath+MPAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB09EDD190728220080A5ED /* NSIndexPath+MPAdditions.m */; };
3BB09ED3190713FC0080A5ED /* MPFunctionLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB09ED2190713FC0080A5ED /* MPFunctionLayout.m */; };
3BB09EDE190728220080A5ED /* NSIndexPath+MPReverseIndexPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB09EDD190728220080A5ED /* NSIndexPath+MPReverseIndexPath.m */; };
3BB09EE1190736160080A5ED /* MPSumFunctionLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB09EE0190736160080A5ED /* MPSumFunctionLayout.m */; }; 3BB09EE1190736160080A5ED /* MPSumFunctionLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB09EE0190736160080A5ED /* MPSumFunctionLayout.m */; };
3BBBA358190327B700824E74 /* MPMutableExpressionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BBBA357190327B700824E74 /* MPMutableExpressionTests.m */; };
3BBBA35D1903F8A700824E74 /* NSObject+MPStringTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BBBA35C1903F8A700824E74 /* NSObject+MPStringTest.m */; };
3BBBA35E1903FD3600824E74 /* MPRangePath.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E35C1900933200259938 /* MPRangePath.m */; }; 3BBBA35E1903FD3600824E74 /* MPRangePath.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E35C1900933200259938 /* MPRangePath.m */; };
3BBBA35F1903FD3600824E74 /* MPRangePath.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E35C1900933200259938 /* MPRangePath.m */; }; 3BBBA35F1903FD3600824E74 /* MPRangePath.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E35C1900933200259938 /* MPRangePath.m */; };
3BBBA3951905704200824E74 /* MPRangeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BBBA3941905704200824E74 /* MPRangeTests.m */; }; 3BBBA3951905704200824E74 /* MPRangeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BBBA3941905704200824E74 /* MPRangeTests.m */; };
@@ -37,7 +37,8 @@
3BF9979118DE623E009CF6C4 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BF9979018DE623E009CF6C4 /* XCTest.framework */; }; 3BF9979118DE623E009CF6C4 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BF9979018DE623E009CF6C4 /* XCTest.framework */; };
3BF9979218DE623E009CF6C4 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BF9976E18DE623E009CF6C4 /* Cocoa.framework */; }; 3BF9979218DE623E009CF6C4 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BF9976E18DE623E009CF6C4 /* Cocoa.framework */; };
3BF9979A18DE623E009CF6C4 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3BF9979818DE623E009CF6C4 /* InfoPlist.strings */; }; 3BF9979A18DE623E009CF6C4 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3BF9979818DE623E009CF6C4 /* InfoPlist.strings */; };
3BFCFF491905AAF30001FE33 /* NSTextStorage+MPSetContents.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BFCFF481905AAF30001FE33 /* NSTextStorage+MPSetContents.m */; }; 3BFAC38F1997B61300B3EF67 /* MPExpression.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BFAC38E1997B61300B3EF67 /* MPExpression.m */; };
3BFAC39C1997BC7600B3EF67 /* NSString+MPExpressionElement.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BFAC39B1997BC7600B3EF67 /* NSString+MPExpressionElement.m */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@@ -54,10 +55,14 @@
3B0F69A719028BC600817707 /* MPException.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPException.h; sourceTree = "<group>"; }; 3B0F69A719028BC600817707 /* MPException.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPException.h; sourceTree = "<group>"; };
3B0F69A819028C6000817707 /* MPException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPException.m; sourceTree = "<group>"; }; 3B0F69A819028C6000817707 /* MPException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPException.m; sourceTree = "<group>"; };
3B0F69AB1902A82C00817707 /* MPExpressionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPExpressionTests.m; sourceTree = "<group>"; }; 3B0F69AB1902A82C00817707 /* MPExpressionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPExpressionTests.m; sourceTree = "<group>"; };
3B528D0D199417740054DB5F /* MPLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPLayout.h; sourceTree = "<group>"; };
3B528D0E199417E10054DB5F /* MPExpressionLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPExpressionLayout.h; sourceTree = "<group>"; };
3B528D0F199417E10054DB5F /* MPExpressionLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPExpressionLayout.m; sourceTree = "<group>"; };
3B528D11199417E90054DB5F /* MPFunctionLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFunctionLayout.h; sourceTree = "<group>"; };
3B528D12199417E90054DB5F /* MPFunctionLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPFunctionLayout.m; sourceTree = "<group>"; };
3B688D9819982DF50006B4AB /* MPLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPLayout.m; sourceTree = "<group>"; };
3B87E3541900856F00259938 /* MPExpressionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPExpressionView.h; sourceTree = "<group>"; }; 3B87E3541900856F00259938 /* MPExpressionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPExpressionView.h; sourceTree = "<group>"; };
3B87E3551900856F00259938 /* MPExpressionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPExpressionView.m; sourceTree = "<group>"; }; 3B87E3551900856F00259938 /* MPExpressionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPExpressionView.m; sourceTree = "<group>"; };
3B87E3571900857C00259938 /* MPExpression.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPExpression.h; sourceTree = "<group>"; };
3B87E3581900857C00259938 /* MPExpression.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPExpression.m; sourceTree = "<group>"; };
3B87E35B1900933200259938 /* MPRangePath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPRangePath.h; sourceTree = "<group>"; }; 3B87E35B1900933200259938 /* MPRangePath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPRangePath.h; sourceTree = "<group>"; };
3B87E35C1900933200259938 /* MPRangePath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPRangePath.m; sourceTree = "<group>"; }; 3B87E35C1900933200259938 /* MPRangePath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPRangePath.m; sourceTree = "<group>"; };
3B87E35E19009D5F00259938 /* MPFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFunction.h; sourceTree = "<group>"; }; 3B87E35E19009D5F00259938 /* MPFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFunction.h; sourceTree = "<group>"; };
@@ -66,18 +71,11 @@
3BB09EB11905DE500080A5ED /* MPExpressionStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPExpressionStorage.m; sourceTree = "<group>"; }; 3BB09EB11905DE500080A5ED /* MPExpressionStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPExpressionStorage.m; sourceTree = "<group>"; };
3BB09EC71906FD830080A5ED /* MPSumFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSumFunction.h; sourceTree = "<group>"; }; 3BB09EC71906FD830080A5ED /* MPSumFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSumFunction.h; sourceTree = "<group>"; };
3BB09EC81906FD830080A5ED /* MPSumFunction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSumFunction.m; sourceTree = "<group>"; }; 3BB09EC81906FD830080A5ED /* MPSumFunction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSumFunction.m; sourceTree = "<group>"; };
3BB09ECE190713F00080A5ED /* MPExpressionLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPExpressionLayout.h; sourceTree = "<group>"; }; 3BB09EDC190728220080A5ED /* NSIndexPath+MPAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSIndexPath+MPAdditions.h"; sourceTree = "<group>"; };
3BB09ECF190713F00080A5ED /* MPExpressionLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPExpressionLayout.m; sourceTree = "<group>"; }; 3BB09EDD190728220080A5ED /* NSIndexPath+MPAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSIndexPath+MPAdditions.m"; sourceTree = "<group>"; };
3BB09ED1190713FC0080A5ED /* MPFunctionLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFunctionLayout.h; sourceTree = "<group>"; };
3BB09ED2190713FC0080A5ED /* MPFunctionLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPFunctionLayout.m; sourceTree = "<group>"; };
3BB09EDC190728220080A5ED /* NSIndexPath+MPReverseIndexPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSIndexPath+MPReverseIndexPath.h"; sourceTree = "<group>"; };
3BB09EDD190728220080A5ED /* NSIndexPath+MPReverseIndexPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSIndexPath+MPReverseIndexPath.m"; sourceTree = "<group>"; };
3BB09EDF190736160080A5ED /* MPSumFunctionLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSumFunctionLayout.h; sourceTree = "<group>"; }; 3BB09EDF190736160080A5ED /* MPSumFunctionLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSumFunctionLayout.h; sourceTree = "<group>"; };
3BB09EE0190736160080A5ED /* MPSumFunctionLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSumFunctionLayout.m; sourceTree = "<group>"; }; 3BB09EE0190736160080A5ED /* MPSumFunctionLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSumFunctionLayout.m; sourceTree = "<group>"; };
3BBBA357190327B700824E74 /* MPMutableExpressionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPMutableExpressionTests.m; sourceTree = "<group>"; };
3BBBA3591903EA9B00824E74 /* MPModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPModel.h; sourceTree = "<group>"; }; 3BBBA3591903EA9B00824E74 /* MPModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPModel.h; sourceTree = "<group>"; };
3BBBA35B1903F8A700824E74 /* NSObject+MPStringTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+MPStringTest.h"; sourceTree = "<group>"; };
3BBBA35C1903F8A700824E74 /* NSObject+MPStringTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+MPStringTest.m"; sourceTree = "<group>"; };
3BBBA38419047FC900824E74 /* MPView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPView.h; sourceTree = "<group>"; }; 3BBBA38419047FC900824E74 /* MPView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPView.h; sourceTree = "<group>"; };
3BBBA3941905704200824E74 /* MPRangeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPRangeTests.m; sourceTree = "<group>"; }; 3BBBA3941905704200824E74 /* MPRangeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPRangeTests.m; sourceTree = "<group>"; };
3BF9976B18DE623E009CF6C4 /* MathPad.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MathPad.app; sourceTree = BUILT_PRODUCTS_DIR; }; 3BF9976B18DE623E009CF6C4 /* MathPad.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MathPad.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -99,8 +97,11 @@
3BF9979018DE623E009CF6C4 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 3BF9979018DE623E009CF6C4 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
3BF9979718DE623E009CF6C4 /* MathPadTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "MathPadTests-Info.plist"; sourceTree = "<group>"; }; 3BF9979718DE623E009CF6C4 /* MathPadTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "MathPadTests-Info.plist"; sourceTree = "<group>"; };
3BF9979918DE623E009CF6C4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; }; 3BF9979918DE623E009CF6C4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
3BFCFF471905AAF30001FE33 /* NSTextStorage+MPSetContents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSTextStorage+MPSetContents.h"; sourceTree = "<group>"; }; 3BFAC38D1997B61300B3EF67 /* MPExpression.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPExpression.h; sourceTree = "<group>"; };
3BFCFF481905AAF30001FE33 /* NSTextStorage+MPSetContents.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTextStorage+MPSetContents.m"; sourceTree = "<group>"; }; 3BFAC38E1997B61300B3EF67 /* MPExpression.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPExpression.m; sourceTree = "<group>"; };
3BFAC3961997B67400B3EF67 /* MPExpressionElement.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPExpressionElement.h; sourceTree = "<group>"; };
3BFAC39A1997BC7600B3EF67 /* NSString+MPExpressionElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+MPExpressionElement.h"; sourceTree = "<group>"; };
3BFAC39B1997BC7600B3EF67 /* NSString+MPExpressionElement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+MPExpressionElement.m"; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@@ -127,14 +128,18 @@
3B87E350190082B300259938 /* Model */ = { 3B87E350190082B300259938 /* Model */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
3BBBA35A1903F89100824E74 /* Categories */,
3BBBA3591903EA9B00824E74 /* MPModel.h */, 3BBBA3591903EA9B00824E74 /* MPModel.h */,
3B87E3571900857C00259938 /* MPExpression.h */, 3BFAC38D1997B61300B3EF67 /* MPExpression.h */,
3B87E3581900857C00259938 /* MPExpression.m */, 3BFAC38E1997B61300B3EF67 /* MPExpression.m */,
3BFAC3961997B67400B3EF67 /* MPExpressionElement.h */,
3B87E35E19009D5F00259938 /* MPFunction.h */, 3B87E35E19009D5F00259938 /* MPFunction.h */,
3B87E35F19009D5F00259938 /* MPFunction.m */, 3B87E35F19009D5F00259938 /* MPFunction.m */,
3BFAC39A1997BC7600B3EF67 /* NSString+MPExpressionElement.h */,
3BFAC39B1997BC7600B3EF67 /* NSString+MPExpressionElement.m */,
3B87E35B1900933200259938 /* MPRangePath.h */, 3B87E35B1900933200259938 /* MPRangePath.h */,
3B87E35C1900933200259938 /* MPRangePath.m */, 3B87E35C1900933200259938 /* MPRangePath.m */,
3BB09EDC190728220080A5ED /* NSIndexPath+MPAdditions.h */,
3BB09EDD190728220080A5ED /* NSIndexPath+MPAdditions.m */,
3B0F69A719028BC600817707 /* MPException.h */, 3B0F69A719028BC600817707 /* MPException.h */,
3B0F69A819028C6000817707 /* MPException.m */, 3B0F69A819028C6000817707 /* MPException.m */,
3BB09EBC1905EF210080A5ED /* Functions */, 3BB09EBC1905EF210080A5ED /* Functions */,
@@ -194,10 +199,12 @@
3BB09ED819071C270080A5ED /* Private */ = { 3BB09ED819071C270080A5ED /* Private */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
3BB09ECE190713F00080A5ED /* MPExpressionLayout.h */, 3B528D0D199417740054DB5F /* MPLayout.h */,
3BB09ECF190713F00080A5ED /* MPExpressionLayout.m */, 3B688D9819982DF50006B4AB /* MPLayout.m */,
3BB09ED1190713FC0080A5ED /* MPFunctionLayout.h */, 3B528D0E199417E10054DB5F /* MPExpressionLayout.h */,
3BB09ED2190713FC0080A5ED /* MPFunctionLayout.m */, 3B528D0F199417E10054DB5F /* MPExpressionLayout.m */,
3B528D11199417E90054DB5F /* MPFunctionLayout.h */,
3B528D12199417E90054DB5F /* MPFunctionLayout.m */,
); );
name = Private; name = Private;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -211,19 +218,6 @@
name = "Function Layouts"; name = "Function Layouts";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
3BBBA35A1903F89100824E74 /* Categories */ = {
isa = PBXGroup;
children = (
3BBBA35B1903F8A700824E74 /* NSObject+MPStringTest.h */,
3BBBA35C1903F8A700824E74 /* NSObject+MPStringTest.m */,
3BFCFF471905AAF30001FE33 /* NSTextStorage+MPSetContents.h */,
3BFCFF481905AAF30001FE33 /* NSTextStorage+MPSetContents.m */,
3BB09EDC190728220080A5ED /* NSIndexPath+MPReverseIndexPath.h */,
3BB09EDD190728220080A5ED /* NSIndexPath+MPReverseIndexPath.m */,
);
name = Categories;
sourceTree = "<group>";
};
3BF9976218DE623E009CF6C4 = { 3BF9976218DE623E009CF6C4 = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -292,7 +286,6 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
3B0F69AB1902A82C00817707 /* MPExpressionTests.m */, 3B0F69AB1902A82C00817707 /* MPExpressionTests.m */,
3BBBA357190327B700824E74 /* MPMutableExpressionTests.m */,
3BBBA3941905704200824E74 /* MPRangeTests.m */, 3BBBA3941905704200824E74 /* MPRangeTests.m */,
3BF9979618DE623E009CF6C4 /* Supporting Files */, 3BF9979618DE623E009CF6C4 /* Supporting Files */,
); );
@@ -408,17 +401,17 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
3B688D9919982DF50006B4AB /* MPLayout.m in Sources */,
3BB09EE1190736160080A5ED /* MPSumFunctionLayout.m in Sources */, 3BB09EE1190736160080A5ED /* MPSumFunctionLayout.m in Sources */,
3B87E3561900856F00259938 /* MPExpressionView.m in Sources */, 3B87E3561900856F00259938 /* MPExpressionView.m in Sources */,
3BFCFF491905AAF30001FE33 /* NSTextStorage+MPSetContents.m in Sources */,
3BB09EC91906FD830080A5ED /* MPSumFunction.m in Sources */, 3BB09EC91906FD830080A5ED /* MPSumFunction.m in Sources */,
3BB09EB21905DE500080A5ED /* MPExpressionStorage.m in Sources */, 3BB09EB21905DE500080A5ED /* MPExpressionStorage.m in Sources */,
3BF9977B18DE623E009CF6C4 /* main.m in Sources */, 3BF9977B18DE623E009CF6C4 /* main.m in Sources */,
3B87E3591900857C00259938 /* MPExpression.m in Sources */, 3BFAC38F1997B61300B3EF67 /* MPExpression.m in Sources */,
3BB09ED3190713FC0080A5ED /* MPFunctionLayout.m in Sources */, 3BFAC39C1997BC7600B3EF67 /* NSString+MPExpressionElement.m in Sources */,
3BB09ED0190713F00080A5ED /* MPExpressionLayout.m in Sources */, 3B528D10199417E10054DB5F /* MPExpressionLayout.m in Sources */,
3BBBA35D1903F8A700824E74 /* NSObject+MPStringTest.m in Sources */, 3B528D13199417E90054DB5F /* MPFunctionLayout.m in Sources */,
3BB09EDE190728220080A5ED /* NSIndexPath+MPReverseIndexPath.m in Sources */, 3BB09EDE190728220080A5ED /* NSIndexPath+MPAdditions.m in Sources */,
3B87E36019009D5F00259938 /* MPFunction.m in Sources */, 3B87E36019009D5F00259938 /* MPFunction.m in Sources */,
3B0F69A919028C6000817707 /* MPException.m in Sources */, 3B0F69A919028C6000817707 /* MPException.m in Sources */,
3BBBA35E1903FD3600824E74 /* MPRangePath.m in Sources */, 3BBBA35E1903FD3600824E74 /* MPRangePath.m in Sources */,
@@ -430,13 +423,14 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
3BBBA358190327B700824E74 /* MPMutableExpressionTests.m in Sources */, 3B528D1619941F5B0054DB5F /* MPExpressionLayout.m in Sources */,
3B528D1819941F5B0054DB5F /* MPFunctionLayout.m in Sources */,
3B0F69AE1902AD2800817707 /* MPException.m in Sources */, 3B0F69AE1902AD2800817707 /* MPException.m in Sources */,
3B0F69AC1902A82C00817707 /* MPExpressionTests.m in Sources */, 3B0F69AC1902A82C00817707 /* MPExpressionTests.m in Sources */,
3BBBA3951905704200824E74 /* MPRangeTests.m in Sources */, 3BBBA3951905704200824E74 /* MPRangeTests.m in Sources */,
3B0F69B01902AD2E00817707 /* MPFunction.m in Sources */, 3B0F69B01902AD2E00817707 /* MPFunction.m in Sources */,
3B53AD5F1997E0FB00C925C4 /* MPExpression.m in Sources */,
3BBBA35F1903FD3600824E74 /* MPRangePath.m in Sources */, 3BBBA35F1903FD3600824E74 /* MPRangePath.m in Sources */,
3B0F69AD1902AD2200817707 /* MPExpression.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="13C1021" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"> <document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="13E28" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies> <dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/> <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/>
</dependencies> </dependencies>
@@ -7,7 +7,6 @@
<customObject id="-2" userLabel="File's Owner" customClass="MPDocument"> <customObject id="-2" userLabel="File's Owner" customClass="MPDocument">
<connections> <connections>
<outlet property="resultExpressionView" destination="lcd-Ip-jjR" id="1NN-l5-30k"/> <outlet property="resultExpressionView" destination="lcd-Ip-jjR" id="1NN-l5-30k"/>
<outlet property="termExpressionView" destination="fqc-IQ-ceJ" id="br9-0u-qYk"/>
<outlet property="window" destination="xOd-HO-29H" id="JIz-fz-R2o"/> <outlet property="window" destination="xOd-HO-29H" id="JIz-fz-R2o"/>
</connections> </connections>
</customObject> </customObject>
@@ -17,22 +16,18 @@
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/> <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/> <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="133" y="235" width="507" height="240"/> <rect key="contentRect" x="133" y="235" width="507" height="240"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1080"/> <rect key="screenRect" x="0.0" y="0.0" width="1920" height="1178"/>
<value key="minSize" type="size" width="94" height="86"/> <value key="minSize" type="size" width="94" height="86"/>
<view key="contentView" id="gIp-Ho-8D9"> <view key="contentView" id="gIp-Ho-8D9">
<rect key="frame" x="0.0" y="0.0" width="507" height="240"/> <rect key="frame" x="0.0" y="0.0" width="507" height="240"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="fqc-IQ-ceJ" customClass="MPExpressionView"> <customView translatesAutoresizingMaskIntoConstraints="NO" id="lcd-Ip-jjR" customClass="MPExpressionView">
<rect key="frame" x="20" y="124" width="467" height="96"/> <rect key="frame" x="20" y="124" width="467" height="96"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</customView> </customView>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="lcd-Ip-jjR" customClass="MPExpressionView"> <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="IMg-L0-qdu">
<rect key="frame" x="20" y="20" width="467" height="96"/> <rect key="frame" x="209" y="13" width="88" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</customView>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="IMg-L0-qdu">
<rect key="frame" x="405" y="-7" width="88" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Change" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Xxz-j2-fsI"> <buttonCell key="cell" type="push" title="Change" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Xxz-j2-fsI">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
@@ -44,14 +39,12 @@
</button> </button>
</subviews> </subviews>
<constraints> <constraints>
<constraint firstItem="lcd-Ip-jjR" firstAttribute="top" secondItem="fqc-IQ-ceJ" secondAttribute="bottom" constant="8" symbolic="YES" id="GTC-QI-4wc"/> <constraint firstAttribute="trailing" secondItem="lcd-Ip-jjR" secondAttribute="trailing" constant="20" symbolic="YES" id="3tX-J3-Wte"/>
<constraint firstItem="fqc-IQ-ceJ" firstAttribute="trailing" secondItem="lcd-Ip-jjR" secondAttribute="trailing" id="Pth-4c-GNR"/> <constraint firstItem="IMg-L0-qdu" firstAttribute="top" secondItem="lcd-Ip-jjR" secondAttribute="bottom" constant="83" id="62S-rU-IWO"/>
<constraint firstAttribute="bottom" secondItem="lcd-Ip-jjR" secondAttribute="bottom" constant="20" symbolic="YES" id="VNk-bs-YOH"/> <constraint firstItem="lcd-Ip-jjR" firstAttribute="leading" secondItem="gIp-Ho-8D9" secondAttribute="leading" constant="20" symbolic="YES" id="XN3-k3-tOU"/>
<constraint firstAttribute="trailing" secondItem="fqc-IQ-ceJ" secondAttribute="trailing" constant="20" symbolic="YES" id="fER-QH-XqN"/> <constraint firstItem="lcd-Ip-jjR" firstAttribute="top" secondItem="gIp-Ho-8D9" secondAttribute="top" constant="20" symbolic="YES" id="gqS-BG-xpS"/>
<constraint firstItem="fqc-IQ-ceJ" firstAttribute="top" secondItem="gIp-Ho-8D9" secondAttribute="top" constant="20" symbolic="YES" id="kpD-MX-yOS"/> <constraint firstItem="IMg-L0-qdu" firstAttribute="centerX" secondItem="lcd-Ip-jjR" secondAttribute="centerX" id="sCM-Pj-4wd"/>
<constraint firstItem="fqc-IQ-ceJ" firstAttribute="leading" secondItem="gIp-Ho-8D9" secondAttribute="leading" constant="20" symbolic="YES" id="to2-nb-CUV"/> <constraint firstAttribute="bottom" secondItem="IMg-L0-qdu" secondAttribute="bottom" constant="20" symbolic="YES" id="tNe-R6-QlG"/>
<constraint firstItem="fqc-IQ-ceJ" firstAttribute="leading" secondItem="lcd-Ip-jjR" secondAttribute="leading" id="xJa-Qd-Ytg"/>
<constraint firstItem="lcd-Ip-jjR" firstAttribute="top" secondItem="gIp-Ho-8D9" secondAttribute="top" constant="124" id="xrY-1x-QCm"/>
</constraints> </constraints>
</view> </view>
<connections> <connections>

View File

@@ -59,9 +59,9 @@
} }
#pragma mark Actions #pragma mark Actions
- (IBAction)changeExpression:(id)sender { - (IBAction)changeExpression:(id)sender {
[self.resultExpressionView.expressionStorage insertString:@"abc" atIndex:6]; [self.resultExpressionView.expressionStorage insertElement:@"abc" atLocation:6];
self.resultExpressionView.needsDisplay = YES;
} }
@end @end

View File

@@ -9,7 +9,7 @@
#ifndef MathPad_MPException_h #ifndef MathPad_MPException_h
#define MathPad_MPException_h #define MathPad_MPException_h
extern NSString *MPIllegalSymbolException; extern NSString *MPIllegalElementException;
extern NSString *MPIllegalSymbolExceptionSymbolKey; extern NSString *MPIllegalElementExceptionElementKey;
#endif #endif

View File

@@ -8,5 +8,5 @@
#import "MPException.h" #import "MPException.h"
NSString *MPIllegalSymbolException = @"MPIllegalSymbolException"; NSString *MPIllegalElementException = @"MPIllegalSymbolException";
NSString *MPIllegalSymbolExceptionSymbolKey = @"MPIllegalSymbolExceptionSymbolKey"; NSString *MPIllegalElementExceptionElementKey = @"MPIllegalSymbolExceptionSymbolKey";

View File

@@ -2,124 +2,82 @@
// MPExpression.h // MPExpression.h
// MathPad // MathPad
// //
// Created by Kim Wittenburg on 17.04.14. // Created by Kim Wittenburg on 10.08.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved. // Copyright (c) 2014 Kim Wittenburg. All rights reserved.
// //
@import Foundation;
#import "NSString+MPExpressionElement.h"
@class MPExpression, MPFunction, MPRangePath; @class MPExpression, MPFunction, MPRangePath;
@protocol MPExpressionElement;
extern NSString *MPAdditionOperator; @interface MPExpression : NSObject <NSCopying, NSCoding>
extern NSString *MPSubtractionOperator;
extern NSString *MPMultiplicationOperator;
extern NSString *MPDivisionOperator;
@interface MPExpression : NSObject <NSCopying, NSMutableCopying, NSCoding>
#pragma mark Creation Methods #pragma mark Creation Methods
- (instancetype)init; // Convenience
- (instancetype)initWithSymbols:(NSArray *)symbols; - (instancetype)initWithElement:(id<MPExpressionElement>)element; // Convenience
- (instancetype)initWithElements:(NSArray *)elements; // Designated Initializer
#pragma mark Working With the Expression Tree #pragma mark Working With the Expression Tree
@property (nonatomic, weak) MPFunction *parent; // Set automatically, nil for root expression
@property (nonatomic, weak) MPFunction *parent; // Documentation: Do not set, may be nil - (void)fixElements; // Called automatically, removes empty elements, joins subsequent strings
- (void)fixSymbols;
#pragma mark Primitive Methods #pragma mark Primitive Methods
- (NSUInteger)length;
- (NSUInteger)numberOfSymbols; - (NSUInteger)numberOfElements;
- (id)symbolAtIndex:(NSUInteger)index; // Either an NSString or a MPFunction (which can be mutated) - (id<MPExpressionElement>)elementAtIndex:(NSUInteger)index;
- (NSArray *)elementsInRange:(NSRange)range;
- (NSUInteger)indexOfElement:(id<MPExpressionElement>)element;
- (void)replaceElementsInRange:(NSRange)range withElements:(NSArray *)elements;
// TODO: - (NSUInteger)indexOfElementAtLocation:(NSUInteger)location;
- (double)doubleValue; // Evaluates Expression - (double)doubleValue; // Evaluates Expression
#pragma mark Notifications
// All notification methods should create a new rangePath with the receiver's index added to the beginning of the path and then ascend the message to it's parent
// TODO: More notifications
- (void)didChangeElementsInRangePath:(MPRangePath *)rangePath
replacementLength:(NSUInteger)replacementLength;
#pragma mark Basic NSObject Methods
- (BOOL)isEqualToExpression:(MPExpression *)anExpression;
- (NSString *)description;
- (NSUInteger)hash;
@end @end
@interface MPExpression (MPExpressionExtensionMethods) @interface MPExpression (MPExpressionExtension)
+ (NSArray *)operators;
#pragma mark Creation Methods
- (id)init;
- (instancetype)initWithString:(NSString *)aString;
- (instancetype)initWithFunction:(MPFunction *)aFunction;
+ (instancetype)expression;
+ (instancetype)expressionWithString:(NSString *)aString;
+ (instancetype)expressionWithFunction:(MPFunction *)aFunction;
+ (instancetype)expressionWithSymbols:(NSArray *)symbols;
#pragma mark Working With the Expression Tree #pragma mark Working With the Expression Tree
- (id)elementAtIndexPath:(NSIndexPath *)indexPath; // Returns an MPExpression or id<MPExpressionElement>
- (NSUInteger)indexOfSymbol:(id)symbol; - (NSArray *)elementsInRangePath:(MPRangePath *)rangePath;
- (id)symbolAtIndexPath:(NSIndexPath *)indexPath; // May also return MPExpression
#pragma mark Working With Expressions #pragma mark Working With Expressions
- (NSUInteger)length; - (MPExpression *)subexpressionFromLocation:(NSUInteger)from;
- (MPExpression *)subexpressionToLocation:(NSUInteger)to;
- (MPExpression *)subexpressionFromIndex:(NSUInteger)from;
- (MPExpression *)subexpressionToIndex:(NSUInteger)to;
- (MPExpression *)subexpressionWithRange:(NSRange)range; - (MPExpression *)subexpressionWithRange:(NSRange)range;
- (BOOL)isEqualToExpression:(MPExpression *)anExpression;
- (MPExpression *)expressionByAppendingString:(NSString *)aString; #pragma mark Mutating Expressions
- (MPExpression *)expressionByAppendingFunction:(MPFunction *)aFunction; - (void)appendElement:(id<MPExpressionElement>)anElement;
- (MPExpression *)expressionByAppendingExpression:(MPExpression *)anExpression; - (void)appendElements:(NSArray *)elements;
- (MPExpression *)expressionByAppendingSymbols:(NSArray *)symbols;
- (void)insertElement:(id<MPExpressionElement>)anElement atLocation:(NSUInteger)index;
- (void)insertElements:(NSArray *)elements atLocation:(NSUInteger)index;
- (void)deleteElementsInRange:(NSRange)range;
#pragma mark Evaluating Expressions #pragma mark Evaluating Expressions
- (float)floatValue; - (float)floatValue;
- (int)intValue; - (int)intValue;
- (NSInteger)integerValue; - (NSInteger)integerValue;
- (long long)longLongValue; - (long long)longLongValue;
#pragma mark Querying an Expression's Contents #pragma mark Querying Expressions
- (NSArray *)elements;
- (NSArray *)symbols;
- (NSString *)description;
- (NSUInteger)hash;
@end
@interface MPExpression (MPChangeNotificationExtension)
- (void)symbolsChangedInRangePath:(MPRangePath *)rangePath replacementLength:(NSUInteger)length;
@end
@interface MPMutableExpression : MPExpression
- (void)replaceSymbolsInRange:(NSRange)range
withSymbols:(NSArray *)symbols;
@end
@interface MPMutableExpression (MPMutableExpressionExtensionMethods)
- (void)insertString:(NSString *)aString
atIndex:(NSUInteger)loc;
- (void)insertFunction:(MPFunction *)aFunction
atIndex:(NSUInteger)loc;
- (void)insertExpression:(MPExpression *)anExpression
atIndex:(NSUInteger)loc;
- (void)insertSymbols:(NSArray *)symbols
atIndex:(NSUInteger)loc;
- (void)deleteSymbolsInRange:(NSRange)range;
- (void)appendString:(NSString *)aString;
- (void)appendFunction:(MPFunction *)aFunction;
- (void)appendExpression:(MPExpression *)anExpression;
- (void)appendSymbols:(NSArray *)symbols;
- (void)setString:(NSString *)aString;
- (void)setFunction:(MPFunction *)aFunction;
- (void)setExpression:(MPExpression *)anExpression;
- (void)setSymbols:(NSArray *)symbols;
@end @end

View File

@@ -2,88 +2,228 @@
// MPExpression.m // MPExpression.m
// MathPad // MathPad
// //
// Created by Kim Wittenburg on 17.04.14. // Created by Kim Wittenburg on 10.08.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved. // Copyright (c) 2014 Kim Wittenburg. All rights reserved.
// //
#import "MPExpression.h" #import "MPExpression.h"
#import "MPFunction.h" #import "MPFunction.h"
#import "MPException.h"
#import "MPRangePath.h" #import "MPRangePath.h"
#import "NSObject+MPStringTest.h" #import "NSIndexPath+MPAdditions.h"
#import "NSIndexPath+MPReverseIndexPath.h" #import "MPException.h"
NSString *MPAdditionOperator = @"+"; @interface MPExpression ()
NSString *MPSubtractionOperator = @"-"; @property (readonly, nonatomic, strong) NSMutableArray *elements;
NSString *MPMultiplicationOperator = @"*"; @end
NSString *MPDivisionOperator = @"/";
@interface MPExpression (MPExpressionPrivate) @interface MPExpression (MPExpressionPrivate)
- (NSInteger)lengthOfSymbol:(id)symbol; - (void)validateElements:(NSArray *)elements;
- (void)validateSymbols:(NSArray *)symbols; - (BOOL)splitElementsAtLocation:(NSUInteger)location
- (void)getSplitOffset:(out NSUInteger *)offset insertionIndex:(out NSUInteger *)insertionIndex;
inSymbolAtIndex:(out NSUInteger *)symbolIndex - (NSUInteger)calculateSplitOffsetForSplitLocation:(NSUInteger)location
forSplitLocation:(NSUInteger)loc; inElementAtIndex:(out NSUInteger *)elementIndex;
@end
@implementation MPExpression (MPExpressionPrivate)
- (void)validateElements:(NSArray *)elements
{
for (id element in elements) {
if (![element conformsToProtocol:@protocol(MPExpressionElement)]) {
@throw [NSException exceptionWithName:MPIllegalElementException
reason:@"Elements must conform to the MPExpressionElement protocol."
userInfo:@{MPIllegalElementExceptionElementKey: element}];
}
}
}
- (BOOL)splitElementsAtLocation:(NSUInteger)location
insertionIndex:(out NSUInteger *)insertionIndex
{
if (location == 0) {
*insertionIndex = 0;
return NO;
}
NSUInteger splitElementIndex;
NSUInteger splitOffset = [self calculateSplitOffsetForSplitLocation:location
inElementAtIndex:&splitElementIndex];
id<MPExpressionElement> splitElement = self.elements[splitElementIndex];
if (splitOffset == splitElement.length) {
splitOffset = 0;
splitElementIndex++;
}
if (splitOffset != 0) {
NSString *stringElement = (NSString *)splitElement;
NSString *leftPart = [stringElement substringToIndex:splitOffset];
NSString *rightPart = [stringElement substringFromIndex:splitOffset];
[self.elements replaceObjectsInRange:NSMakeRange(splitElementIndex, 1)
withObjectsFromArray:@[leftPart, rightPart]];
++splitElementIndex;
}
*insertionIndex = splitElementIndex;
return splitOffset != 0;
}
- (NSUInteger)calculateSplitOffsetForSplitLocation:(NSUInteger)location
inElementAtIndex:(out NSUInteger *)elementIndex
{
NSUInteger length = 0;
NSUInteger index = 0;
NSUInteger elementLength = 0;
for (id<MPExpressionElement> element in self.elements) {
elementLength = element.length;
length += elementLength;
if (length >= location) {
break;
}
++index;
}
*elementIndex = index;
return elementLength - (length - location);
}
@end @end
@implementation MPExpression { @implementation MPExpression {
@package NSUInteger _cachedLength;
__strong NSArray *_symbols; NSRange editedRange;
NSInteger _length; NSRange replacementRange;
} }
#pragma mark Creation Methods @synthesize elements = _elements;
- (instancetype)initWithSymbols:(NSArray *)symbols #pragma mark Creation Methods
- (instancetype)init
{
return [self initWithElements:@[]];
}
- (instancetype)initWithElement:(id<MPExpressionElement>)element
{
return [self initWithElements:@[element]];
}
- (instancetype)initWithElements:(NSArray *)elements
{ {
[self validateSymbols:symbols];
self = [super init]; self = [super init];
if (self) { if (self) {
_symbols = [[NSArray alloc] initWithArray:symbols _cachedLength = 0;
copyItems:YES]; _elements = [[NSMutableArray alloc] initWithCapacity:elements.count];
[self fixSymbols]; _elements = [[NSMutableArray alloc] initWithArray:elements
copyItems:YES];
[self fixElements];
} }
return self; return self;
} }
#pragma mark Working With the Expression Tree #pragma mark Working With the Expression Tree
- (void)fixElements
- (void)fixSymbols
{ {
NSMutableArray *mutableSymbols = [_symbols mutableCopy]; for (NSUInteger index = 0; index < self.elements.count; index++) {
for (NSInteger index = 0; index < mutableSymbols.count; index++) { id<MPExpressionElement> next = index+1 < self.elements.count ? self.elements[index+1] :nil;
id next = index+1 < mutableSymbols.count ? mutableSymbols[index+1] : nil; id<MPExpressionElement> current = self.elements[index];
id current = mutableSymbols[index];
if ([current isString]) { if ([current isString]) {
if ([current length] == 0) { if (current.length == 0) {
[mutableSymbols removeObjectAtIndex:index]; [self.elements removeObjectAtIndex:index];
index--; if (index < replacementRange.location) {
replacementRange.location--;
} else if (index < NSMaxRange(replacementRange)-1) {
replacementRange.length--;
} else if (index == NSMaxRange(replacementRange)) {
editedRange.length++;
}
--index;
} else if ([next isString]) { } else if ([next isString]) {
NSString *new = [NSString stringWithFormat:@"%@%@", current, next]; NSString *new = [NSString stringWithFormat:@"%@%@", current, next];
[mutableSymbols replaceObjectAtIndex:index withObject:new]; [self.elements replaceObjectsInRange:NSMakeRange(index, 2)
[mutableSymbols removeObjectAtIndex:index+1]; withObjectsFromArray:@[new]];
index--; if (index < replacementRange.location) {
replacementRange.location--;
} else if (index < NSMaxRange(replacementRange)-1) {
replacementRange.length--;
} else if (index == NSMaxRange(replacementRange)-1) {
editedRange.length++;
}
--index;
} }
} else { } else {
[(MPFunction *)current setParent:self]; [(MPFunction *)current setParent:self];
} }
} }
_symbols = [mutableSymbols copy];
} }
#pragma mark Primitive Methods #pragma mark Primitive Methods
- (NSUInteger)length
- (NSUInteger)numberOfSymbols
{ {
return [_symbols count]; if (_cachedLength == 0) {
for (id<MPExpressionElement> element in self.elements) {
_cachedLength += element.length;
}
}
return _cachedLength;
} }
- (id)symbolAtIndex:(NSUInteger)index - (NSUInteger)numberOfElements
{ {
return _symbols[index]; return self.elements.count;
}
- (id<MPExpressionElement>)elementAtIndex:(NSUInteger)index
{
return self.elements[index];
}
- (NSArray *)elementsInRange:(NSRange)range
{
return [self.elements subarrayWithRange:range];
}
- (NSUInteger)indexOfElement:(id<MPExpressionElement>)element
{
return [self.elements indexOfObject:element];
}
- (void)replaceElementsInRange:(NSRange)range
withElements:(NSArray *)elements
{
if (NSMaxRange(range) > self.length) {
@throw [NSException exceptionWithName:NSRangeException
reason:@"Range out of bounds of expression"
userInfo:nil];
}
[self validateElements:elements];
// Locate the position, split the elements
NSUInteger startIndex;
BOOL didSplitStart = NO;
if ([self numberOfElements] == 0) {
startIndex = 0;
} else {
didSplitStart = [self splitElementsAtLocation:range.location
insertionIndex:&startIndex];
}
NSUInteger endIndex;
BOOL didSplitEnd = [self splitElementsAtLocation:NSMaxRange(range)
insertionIndex:&endIndex];
// Perform the replacement
NSArray *newElements = [[NSArray alloc] initWithArray:elements
copyItems:YES];
[self.elements replaceObjectsInRange:NSMakeRange(startIndex, endIndex-startIndex)
withObjectsFromArray:newElements];
_cachedLength = 0;
NSUInteger editingStart = startIndex - (didSplitStart?1:0);
NSUInteger editingLength = endIndex - startIndex + (didSplitStart?1:0) + (didSplitEnd?1:0);
editedRange = NSMakeRange(editingStart, editingLength);
replacementRange = NSMakeRange(startIndex, elements.count);
[self fixElements];
MPRangePath *changePath = [[MPRangePath alloc] initWithRange:editedRange];
[self didChangeElementsInRangePath:changePath replacementLength:replacementRange.length];
} }
- (double)doubleValue - (double)doubleValue
@@ -92,218 +232,18 @@ NSString *MPDivisionOperator = @"/";
return 0; return 0;
} }
#pragma mark - NSCopying #pragma mark Notifications
- (void)didChangeElementsInRangePath:(MPRangePath *)rangePath
- (id)copyWithZone:(NSZone *)zone replacementLength:(NSUInteger)replacementLength
{ {
MPExpression *copy = [[MPExpression allocWithZone:zone] initWithSymbols:_symbols]; NSUInteger selfIndex = [self.parent indexOfChild:self];
return copy; MPRangePath *newPath = rangePath.copy;
newPath.location = [newPath.location indexPathByPreceedingIndex:selfIndex];
[self.parent didChangeElementsInRangePath:newPath
replacementLength:replacementLength];
} }
#pragma mark - NSMutableCopying #pragma mark Basic NSObject Methods
- (id)mutableCopyWithZone:(NSZone *)zone
{
MPMutableExpression *mutableCopy = [[MPMutableExpression allocWithZone:zone] initWithSymbols:_symbols];
return mutableCopy;
}
#pragma mark - NSCoding
- (id)initWithCoder:(NSCoder *)aDecoder
{
// TODO: Test Coding
return [self initWithSymbols:[aDecoder decodeObject]];
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:_symbols];
}
@end
@implementation MPExpression (MPExpressionPrivate)
- (NSInteger)lengthOfSymbol:(id)symbol
{
if ([symbol isString]) {
return [symbol length];
}
return 1;
}
- (void)validateSymbols:(NSArray *)symbols
{
for (id symbol in symbols) {
if (!([symbol isString]
|| [symbol isKindOfClass:[MPFunction class]])) {
@throw [NSException exceptionWithName:MPIllegalSymbolException
reason:@"Only NSString and MPFunction objects are valid symbols."
userInfo:@{MPIllegalSymbolExceptionSymbolKey: symbol}];
}
}
}
- (void)getSplitOffset:(out NSUInteger *)offset
inSymbolAtIndex:(out NSUInteger *)symbolIndex
forSplitLocation:(NSUInteger)loc
{
NSUInteger length = 0;
NSUInteger index = 0;
NSUInteger symbolLength = 0;
for (id symbol in _symbols) {
symbolLength = [self lengthOfSymbol:symbol];
length += symbolLength;
if (length >= loc) {
break;
}
index++;
}
*offset = symbolLength - (length - loc);
*symbolIndex = index;
}
@end
@implementation MPExpression (MPExpressionExtensionMethods)
+ (NSArray *)operators
{
return @[MPAdditionOperator, MPSubtractionOperator, MPMultiplicationOperator, MPDivisionOperator];
}
#pragma mark Creation Methods
- (instancetype)init
{
return [self initWithSymbols:@[]];
}
- (instancetype)initWithString:(NSString *)aString
{
return [self initWithSymbols:@[aString]];
}
- (instancetype)initWithFunction:(MPFunction *)aFunction
{
return [self initWithSymbols:@[aFunction]];
}
+ (instancetype)expression
{
return [[self alloc] init];
}
+ (instancetype)expressionWithString:(NSString *)aString
{
return [[self alloc] initWithString:aString];
}
+ (instancetype)expressionWithFunction:(MPFunction *)aFunction
{
return [[self alloc] initWithFunction:aFunction];
}
+ (instancetype)expressionWithSymbols:(NSArray *)symbols
{
return [[self alloc] initWithSymbols:symbols];
}
#pragma mark Working With the Expression Tree
- (NSUInteger)indexOfSymbol:(id)symbol
{
return [_symbols indexOfObject:symbol];
}
- (id)symbolAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.length == 0) {
return self;
}
id symbol = [self symbolAtIndex:[indexPath indexAtPosition:0]];
if (indexPath.length == 1) {
return symbol;
}
if ([symbol isKindOfClass:[MPFunction class]]) {
return [symbol symbolAtIndexPath:[indexPath indexPathByRemovingFirstIndex]];
}
return nil;
}
#pragma mark Working With Expressions
- (NSUInteger)length
{
if (_length == 0) {
for (id symbol in _symbols) {
_length += [self lengthOfSymbol:symbol];
}
}
return _length;
}
- (MPExpression *)subexpressionFromIndex:(NSUInteger)from
{
return [self subexpressionWithRange:NSMakeRange(from, [self length] - from)];
}
- (MPExpression *)subexpressionToIndex:(NSUInteger)to
{
return [self subexpressionWithRange:NSMakeRange(0, to)];
}
- (MPExpression *)subexpressionWithRange:(NSRange)range
{
if (NSMaxRange(range) > self.length) {
@throw [NSException exceptionWithName:NSRangeException
reason:@"Range outside bounds of expression."
userInfo:nil];
}
if (range.location == self.length || NSMaxRange(range) == 0 || range.length == 0) {
// Speed this up
return [[MPExpression alloc] init];
}
NSUInteger startOffset;
NSUInteger startSymbolIndex;
[self getSplitOffset:&startOffset
inSymbolAtIndex:&startSymbolIndex
forSplitLocation:range.location];
id startSymbol = _symbols[startSymbolIndex];
if (startOffset == [self lengthOfSymbol:startSymbol]) {
startOffset = 0;
startSymbolIndex++;
startSymbol = _symbols[startSymbolIndex];
} else if ([startSymbol isString]) {
startSymbol = [startSymbol substringFromIndex:startOffset];
}
NSUInteger endOffset;
NSUInteger endSymbolIndex;
[self getSplitOffset:&endOffset
inSymbolAtIndex:&endSymbolIndex
forSplitLocation:NSMaxRange(range)];
id endSymbol = _symbols[endSymbolIndex];
if ([endSymbol isString]) {
endSymbol = [endSymbol substringToIndex:endOffset];
}
NSMutableArray *symbols = [[NSMutableArray alloc] initWithCapacity:endSymbolIndex-startSymbolIndex+1];
[symbols addObject:startSymbol];
if (endSymbolIndex > startSymbolIndex + 1) {
NSInteger restLength = endSymbolIndex - startSymbolIndex - 1;
[symbols addObjectsFromArray:[_symbols subarrayWithRange:NSMakeRange(startSymbolIndex+1, restLength)]];
}
if (endSymbolIndex > startSymbolIndex) {
[symbols addObject:endSymbol];
} else if (endSymbolIndex == startSymbolIndex && [startSymbol isString]) {
NSString *result = [_symbols[startSymbolIndex] substringWithRange:NSMakeRange(startOffset, endOffset-startOffset)];
[symbols replaceObjectAtIndex:0
withObject:result];
}
return [[MPExpression alloc] initWithSymbols:symbols];
}
- (BOOL)isEqual:(id)object - (BOOL)isEqual:(id)object
{ {
@@ -321,32 +261,141 @@ NSString *MPDivisionOperator = @"/";
- (BOOL)isEqualToExpression:(MPExpression *)anExpression - (BOOL)isEqualToExpression:(MPExpression *)anExpression
{ {
// TODO: Use ->_symbols or .symbols return [self.elements isEqualToArray:anExpression.elements];
return [_symbols isEqualToArray:anExpression->_symbols];
} }
- (MPExpression *)expressionByAppendingString:(NSString *)aString - (NSString *)description
{ {
return [self expressionByAppendingSymbols:@[aString]]; NSMutableString *description = [[NSMutableString alloc] init];
NSUInteger index = 0;
for (id element in self.elements) {
if ([element isString]) {
NSMutableString *correctedSymbol = [[element stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] mutableCopy];
// Prefix operator
if (element != self.elements[0]) {
unichar prefix = [correctedSymbol characterAtIndex:0];
if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:prefix]) {
[correctedSymbol insertString:@"*"
atIndex:0];
}
}
// Suffix operator
if (element != [self.elements lastObject]) {
unichar suffix = [correctedSymbol characterAtIndex:correctedSymbol.length-1];
if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:suffix]) {
[correctedSymbol appendString:@"*"];
}
}
[description appendString:correctedSymbol];
} else if (index > 0 && [self.elements[index-1] isKindOfClass:[MPFunction class]]) {
[description appendFormat:@"*%@", [element description]];
} else {
[description appendString:[element description]];
}
index++;
}
return description;
} }
- (MPExpression *)expressionByAppendingFunction:(MPFunction *)aFunction - (NSUInteger)hash
{ {
return [self expressionByAppendingSymbols:@[aFunction]]; return [self.elements hash];
} }
- (MPExpression *)expressionByAppendingExpression:(MPExpression *)anExpression #pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone
{ {
return [self expressionByAppendingSymbols:anExpression.symbols]; MPExpression *copy = [[MPExpression allocWithZone:zone] initWithElements:self.elements];
return copy;
} }
- (MPExpression *)expressionByAppendingSymbols:(NSArray *)symbols #pragma mark - NSCoding
- (id)initWithCoder:(NSCoder *)aDecoder
{ {
return [[MPExpression alloc] initWithSymbols:[_symbols arrayByAddingObjectsFromArray:symbols]]; // TODO: Test Coding
return [self initWithElements:[aDecoder decodeObject]];
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.elements];
}
@end
@implementation MPExpression (MPExpressionExtension)
#pragma mark Working With the Expression Tree
- (id)elementAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.length == 0) {
return self;
}
id<MPExpressionElement> element = [self elementAtIndex:[indexPath indexAtPosition:0]];
if (indexPath.length == 1) {
return element;
}
if ([element isFunction]) {
return [(MPFunction *)element elementAtIndexPath:[indexPath indexPathByRemovingLastIndex]];
}
return nil;
}
- (NSArray *)elementsInRangePath:(MPRangePath *)rangePath
{
MPExpression *targetExpression = [self elementAtIndexPath:[rangePath.location indexPathByRemovingLastIndex]];
return [targetExpression elementsInRange:rangePath.rangeAtLastIndex];
}
#pragma mark Working With Expressions
- (MPExpression *)subexpressionFromLocation:(NSUInteger)from
{
return [self subexpressionWithRange:NSMakeRange(from, self.length - from)];
}
- (MPExpression *)subexpressionToLocation:(NSUInteger)to
{
return [self subexpressionWithRange:NSMakeRange(0, to)];
}
- (MPExpression *)subexpressionWithRange:(NSRange)range
{
MPExpression *subexpression = [self copy];
NSRange preceedingRange = NSMakeRange(0, range.location);
NSUInteger firstOut = NSMaxRange(range);
NSRange exceedingRange = NSMakeRange(firstOut, self.length-firstOut);
[subexpression deleteElementsInRange:exceedingRange];
[subexpression deleteElementsInRange:preceedingRange];
return subexpression;
}
#pragma mark Mutating Expressions
- (void)appendElement:(id<MPExpressionElement>)anElement
{
[self appendElements:@[anElement]];
}
- (void)appendElements:(NSArray *)elements
{
[self replaceElementsInRange:NSMakeRange(self.length, 0) withElements:elements];
}
- (void)insertElement:(id<MPExpressionElement>)anElement atLocation:(NSUInteger)index
{
[self insertElements:@[anElement] atLocation:index];
}
- (void)insertElements:(NSArray *)elements atLocation:(NSUInteger)index
{
[self replaceElementsInRange:NSMakeRange(index, 0) withElements:elements];
}
- (void)deleteElementsInRange:(NSRange)range
{
[self replaceElementsInRange:range withElements:@[]];
} }
#pragma mark Evaluating Expressions #pragma mark Evaluating Expressions
- (float)floatValue - (float)floatValue
{ {
return (float)[self doubleValue]; return (float)[self doubleValue];
@@ -367,290 +416,4 @@ NSString *MPDivisionOperator = @"/";
return (long long)[self doubleValue]; return (long long)[self doubleValue];
} }
#pragma mark Querying an Expression's Contents
- (NSArray *)symbols
{
// _symbols is immutable so it is ok to just return it instead of making a copy
return _symbols;
}
- (NSString *)description
{
NSMutableString *description = [[NSMutableString alloc] init];
NSUInteger index = 0;
for (id symbol in _symbols) {
if ([symbol isString]) {
NSMutableString *correctedSymbol = [[symbol stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] mutableCopy];
// Prefix operator
if (symbol != _symbols[0]) {
unichar prefix = [correctedSymbol characterAtIndex:0];
if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:prefix]) {
[correctedSymbol insertString:@"*"
atIndex:0];
}
}
// Suffix operator
if (symbol != [_symbols lastObject]) {
unichar suffix = [correctedSymbol characterAtIndex:correctedSymbol.length-1];
if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:suffix]) {
[correctedSymbol appendString:@"*"];
}
}
[description appendString:correctedSymbol];
} else if (index > 0 && [_symbols[index-1] isKindOfClass:[MPFunction class]]) {
[description appendFormat:@"*%@", [symbol description]];
} else {
[description appendString:[symbol description]];
}
index++;
}
return description;
}
- (NSUInteger)hash
{
return [_symbols hash];
}
@end
@implementation MPExpression (MPChangeNotificationExtension)
- (void)symbolsChangedInRangePath:(MPRangePath *)rangePath replacementLength:(NSUInteger)length
{
if (!self.parent) {
return;
}
NSUInteger selfIndex = [self.parent indexOfChild:self];
NSIndexPath *newLocation = [rangePath.location indexPathByPrecedingIndex:selfIndex];
MPRangePath *newPath = [[MPRangePath alloc] initWithLocation:newLocation length:rangePath.length];
[self.parent symbolsChangedInRangePath:newPath replacementLength:length];
}
@end
@implementation MPMutableExpression {
NSRange editedRange;
NSRange replacementRange;
}
- (instancetype)initWithSymbols:(NSArray *)symbols
{
[self validateSymbols:symbols];
self = [super initWithSymbols:nil];
if (self) {
editedRange = NSMakeRange(0, 0);
replacementRange = NSMakeRange(0, 0);
_symbols = [[NSMutableArray alloc] initWithArray:symbols
copyItems:YES];
[self fixSymbols];
}
return self;
}
- (void)fixSymbols
{
NSMutableArray *mutableSymbols = (NSMutableArray *)_symbols;
for (NSInteger index = 0; index < mutableSymbols.count; index++) {
id next = index+1 < mutableSymbols.count ? mutableSymbols[index+1] : nil;
id current = mutableSymbols[index];
if ([current isString]) {
if ([current length] == 0) {
[mutableSymbols removeObjectAtIndex:index];
if (index < replacementRange.location) {
replacementRange.location--;
} else if (index < NSMaxRange(replacementRange)-1) {
replacementRange.length--;
} else if (index == NSMaxRange(replacementRange)) {
editedRange.length++;
}
index--;
} else if ([next isString]) {
NSString *new = [NSString stringWithFormat:@"%@%@", current, next];
[mutableSymbols replaceObjectAtIndex:index withObject:new];
[mutableSymbols removeObjectAtIndex:index+1];
if (index < replacementRange.location) {
replacementRange.location--;
} else if (index < NSMaxRange(replacementRange)-1) {
replacementRange.length--;
} else if (index == NSMaxRange(replacementRange)-1) {
editedRange.length++;
}
index--;
}
} else {
[(MPFunction *)current setParent:self];
}
}
}
- (NSArray *)symbols
{
// Return an immutable array:
return [_symbols copy];
}
- (void)replaceSymbolsInRange:(NSRange)range
withSymbols:(NSArray *)symbols
{
if (NSMaxRange(range) > self.length) {
@throw [NSException exceptionWithName:NSRangeException
reason:@"Range out of bounds of expression."
userInfo:nil];
}
[self validateSymbols:symbols];
// Locate the position, split the symbols
NSUInteger startIndex;
BOOL didSplitStart = NO;
if ([self numberOfSymbols] == 0) {
startIndex = 0;
} else {
[self splitSymbolsAtLocation:range.location
insertionIndex:&startIndex
didSplit:&didSplitStart];
}
// Perform the deletion
NSUInteger endIndex;
BOOL didSplitEnd = NO;
[self splitSymbolsAtLocation:NSMaxRange(range)
insertionIndex:&endIndex
didSplit:&didSplitEnd];
if (range.length > 0) {
NSMutableIndexSet *indexes = [[NSMutableIndexSet alloc] init];
for (NSUInteger index = startIndex; index < endIndex; index++) {
[indexes addIndex:index];
}
// TODO: Replace with removeObjectsInRange:
[(NSMutableArray *)_symbols removeObjectsAtIndexes:indexes];
}
// Perform the insertion
if (symbols.count > 0) {
NSArray *newSymbols = [[NSArray alloc] initWithArray:symbols copyItems:YES];
[(NSMutableArray *)_symbols replaceObjectsInRange:NSMakeRange(startIndex, 0)
withObjectsFromArray:newSymbols];
}
// Invalidate length and revalidate structure
_length = 0;
NSUInteger editingStart = startIndex - (didSplitStart?1:0);
NSUInteger editingLength = endIndex - startIndex + (didSplitStart?1:0) + (didSplitEnd?1:0);
editedRange = NSMakeRange(editingStart, editingLength);
replacementRange = NSMakeRange(startIndex, symbols.count);
[self fixSymbols];
MPRangePath *changePath = [[MPRangePath alloc] initWithRange:editedRange];
[self symbolsChangedInRangePath:changePath replacementLength:replacementRange.length];
}
- (void)splitSymbolsAtLocation:(NSUInteger)loc
insertionIndex:(out NSUInteger *)insertionIndex
didSplit:(out BOOL *)flag;
{
NSUInteger splitSymbolIndex;
NSUInteger splitOffset;
[self getSplitOffset:&splitOffset
inSymbolAtIndex:&splitSymbolIndex
forSplitLocation:loc];
id splitSymbol = _symbols[splitSymbolIndex];
NSInteger splitSymbolLength = [self lengthOfSymbol:splitSymbol];
if (splitOffset == splitSymbolLength) {
splitOffset = 0;
splitSymbolIndex++;
}
if (splitOffset != 0) {
NSString *leftPart = [splitSymbol substringToIndex:splitOffset];
NSString *rightPart = [splitSymbol substringFromIndex:splitOffset];
[(NSMutableArray *)_symbols replaceObjectAtIndex:splitSymbolIndex
withObject:leftPart];
splitSymbolIndex++;
[(NSMutableArray *)_symbols insertObject:rightPart
atIndex:splitSymbolIndex];
}
*flag = splitOffset != 0;
*insertionIndex = splitSymbolIndex;
}
@end
@implementation MPMutableExpression (MPMutableExpressionExtensionMethods)
- (void)insertString:(NSString *)aString
atIndex:(NSUInteger)loc
{
[self insertSymbols:@[aString]
atIndex:loc];
}
- (void)insertFunction:(MPFunction *)aFunction
atIndex:(NSUInteger)loc
{
[self insertSymbols:@[aFunction]
atIndex:loc];
}
- (void)insertExpression:(MPExpression *)anExpression
atIndex:(NSUInteger)loc
{
[self insertSymbols:anExpression.symbols
atIndex:loc];
}
- (void)insertSymbols:(NSArray *)symbols
atIndex:(NSUInteger)loc
{
[self replaceSymbolsInRange:NSMakeRange(loc, 0)
withSymbols:symbols];
}
- (void)deleteSymbolsInRange:(NSRange)range
{
[self replaceSymbolsInRange:range
withSymbols:@[]];
}
- (void)appendString:(NSString *)aString
{
[self appendSymbols:@[aString]];
}
- (void)appendFunction:(MPFunction *)aFunction
{
[self appendSymbols:@[aFunction]];
}
- (void)appendExpression:(MPExpression *)anExpression
{
[self appendSymbols:anExpression.symbols];
}
- (void)appendSymbols:(NSArray *)symbols
{
[self replaceSymbolsInRange:NSMakeRange(self.length, 0)
withSymbols:symbols];
}
- (void)setString:(NSString *)aString
{
[self setSymbols:@[aString]];
}
- (void)setFunction:(MPFunction *)aFunction
{
[self setSymbols:@[aFunction]];
}
- (void)setExpression:(MPExpression *)anExpression
{
[self setSymbols:anExpression.symbols];
}
- (void)setSymbols:(NSArray *)symbols
{
[self replaceSymbolsInRange:NSMakeRange(0, self.length)
withSymbols:symbols];
}
@end @end

View File

@@ -0,0 +1,24 @@
//
// MPExpressionElement.h
// MathPad
//
// Created by Kim Wittenburg on 10.08.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
@import Foundation;
@protocol MPExpressionElement <NSObject, NSCoding>
- (BOOL)isString;
- (BOOL)isFunction;
- (NSUInteger)length;
- (double)doubleValue;
- (float)floatValue;
- (int)intValue;
- (NSInteger)integerValue;
- (long long)longLongValue;
@end

View File

@@ -2,64 +2,18 @@
// MPExpressionLayout.h // MPExpressionLayout.h
// MathPad // MathPad
// //
// Created by Kim Wittenburg on 22.04.14. // Created by Kim Wittenburg on 07.08.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved. // Copyright (c) 2014 Kim Wittenburg. All rights reserved.
// //
#import <Foundation/Foundation.h> @import Cocoa;
#import "MPLayout.h"
#import "MPExpression.h"
@class MPExpressionLayout, MPFunctionLayout, MPExpressionStorage, MPExpressionView, MPExpression; @interface MPExpressionLayout : MPLayout
@interface MPExpressionLayout : NSObject { - (instancetype)initRootLayoutWithExpressionStorage:(MPExpressionStorage *)expressionStorage;
BOOL _valid;
NSSize _cachedSize;
NSMutableArray *_symbolCache;
}
#pragma mark Creation Methods @property (readonly, nonatomic, weak) MPExpression *expression; // Convenience
// -init not supported
- (id)initRootLayoutWithExpressionStorage:(MPExpressionStorage *)expressionStorage;
- (id)initWithExpressionPath:(NSIndexPath *)expressionPath
parent:(MPFunctionLayout *)parent;
#pragma mark Properties
@property (readonly, nonatomic, weak) MPFunctionLayout *parent;
@property (readonly, nonatomic, weak) MPExpressionStorage *expressionStorage;
@property (readonly, nonatomic, strong) NSIndexPath *expressionPath;
@property (nonatomic, weak) MPExpressionView *expressionView;
- (MPExpression *)expression; // Convenience
- (NSLayoutManager *)layoutManager;
- (NSTextContainer *)textContainer;
- (NSTextStorage *)textStorage;
#pragma mark Cache Methods
- (void)invalidate;
- (void)editedExpressionInRange:(NSRange)range
replacementLength:(NSUInteger)length;
- (BOOL)hasCacheForSymbolAtIndex:(NSUInteger)index;
- (MPFunctionLayout *)functionLayoutForFunctionAtIndex:(NSUInteger)index;
- (NSSize)cachedSizeForSymbolAtIndex:(NSUInteger)index;
- (void)cacheSize:(NSSize)size forSymbolAtIndex:(NSUInteger)index;
#pragma mark Sizes Calculation Methods
- (NSSize)sizeForAllSymbols;
- (NSSize)sizeForSymbolAtIndex:(NSUInteger)index;
- (NSSize)sizeForSymbolsInRange:(NSRange)range;
#pragma mark Drawing Methods
- (void)drawSymbolAtIndex:(NSUInteger)index
atPoint:(NSPoint)point;
- (void)drawSymbolsInRange:(NSRange)range
atPoint:(NSPoint)point;
- (void)drawAllSymbolsAtPoint:(NSPoint)point;
@end @end

View File

@@ -2,45 +2,69 @@
// MPExpressionLayout.m // MPExpressionLayout.m
// MathPad // MathPad
// //
// Created by Kim Wittenburg on 22.04.14. // Created by Kim Wittenburg on 07.08.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved. // Copyright (c) 2014 Kim Wittenburg. All rights reserved.
// //
#import "MPExpressionLayout.h" #import "MPExpressionLayout.h"
#import "MPExpressionStorage.h"
#import "MPFunctionLayout.h" #import "MPFunctionLayout.h"
#import "MPModel.h"
#import "MPExpressionView.h" @interface MPExpressionLayout (MPPathGeneration)
- (NSBezierPath *)bezierPathForChildAtIndex:(NSUInteger)index;
- (NSBezierPath *)generateBezierPathForString:(NSString *)aString;
@end
@implementation MPExpressionLayout (MPPathGeneration)
- (NSBezierPath *)bezierPathForChildAtIndex:(NSUInteger)index
{
id symbol = [self.expression elementAtIndex:index];
if ([symbol isString]) {
return [self cachableObjectForIndex:index
generator:^id{
return [self generateBezierPathForString:symbol];
}];
} else {
MPLayout *layout = [self childLayoutAtIndex:index];
return layout.bezierPath;
}
}
- (NSBezierPath *)generateBezierPathForString:(NSString *)aString
{
NSAttributedString *text = [[NSAttributedString alloc] initWithString:aString
attributes:@{NSFontAttributeName: [NSFont fontWithName:@"Lucida Grande" size:18.0]}];
self.textStorage.attributedString = text;
NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer];
NSGlyph glyphs[glyphRange.length+1];
NSUInteger actualGlyphCount = [self.layoutManager getGlyphs:glyphs
range:glyphRange];
NSBezierPath *path = [NSBezierPath bezierPath];
[path moveToPoint:NSZeroPoint];
[path appendBezierPathWithGlyphs:glyphs
count:actualGlyphCount
inFont:[NSFont fontWithName:@"Lucida Grande" size:18.0]];
return path;
}
@end
@implementation MPExpressionLayout @implementation MPExpressionLayout
#pragma mark Creation Methods # pragma mark Creation Methods
- (instancetype)initRootLayoutWithExpressionStorage:(MPExpressionStorage *)expressionStorage - (instancetype)initRootLayoutWithExpressionStorage:(MPExpressionStorage *)expressionStorage
{ {
self = [super init]; self = [super init];
if (self) { if (self) {
_symbolCache = [[NSMutableArray alloc] init];
_expressionStorage = expressionStorage; _expressionStorage = expressionStorage;
_expressionPath = [[NSIndexPath alloc] init];
}
return self;
}
- (instancetype)initWithExpressionPath:(NSIndexPath *)expressionPath
parent:(MPFunctionLayout *)parent
{
self = [super init];
if (self) {
_symbolCache = [[NSMutableArray alloc] init];
_expressionPath = expressionPath;
_parent = parent;
} }
return self; return self;
} }
#pragma mark Properties #pragma mark Properties
@synthesize expressionStorage = _expressionStorage; @synthesize expressionStorage = _expressionStorage;
- (MPExpressionStorage *)expressionStorage - (MPExpressionStorage *)expressionStorage
{ {
@@ -52,172 +76,50 @@
- (MPExpression *)expression - (MPExpression *)expression
{ {
return [self.expressionStorage symbolAtIndexPath:self.expressionPath]; return [self.expressionStorage elementAtIndexPath:self.path];
}
- (NSLayoutManager *)layoutManager
{
return self.expressionStorage.layoutManager;
}
- (NSTextContainer *)textContainer
{
return self.expressionStorage.textContainer;
}
- (NSTextStorage *)textStorage
{
return self.expressionStorage.textStorage;
} }
#pragma mark Cache Methods #pragma mark Cache Methods
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index
// TODO: Return nil from caching with illegal index
- (void)invalidate
{ {
_valid = NO; id cachedObject = [self cachableObjectForIndex:index generator:^id{
[self.parent invalidate]; NSIndexPath *indexPath = [self.path indexPathByAddingIndex:index];
[self.expressionView setNeedsDisplay:YES]; MPFunctionLayout *layout = [MPFunctionLayout functionLayoutForFunctionAtIndexPath:indexPath
parent:self];
return layout;
}];
if ([cachedObject isKindOfClass:[NSBezierPath class]]) {
return nil;
}
return cachedObject;
} }
- (void)editedExpressionInRange:(NSRange)range replacementLength:(NSUInteger)length - (NSSize)sizeForChildAtIndex:(NSUInteger)index
{ {
// TODO: New symbols may also be inserted in the middle or at the beginning id symbol = [self.expression elementAtIndex:index];
NSInteger changeInLength = length - range.length;
while (_symbolCache.count < (self.expression.numberOfSymbols + changeInLength)) {
[_symbolCache addObject:[NSNull null]];
}
NSMutableArray *newPlaceholders = [[NSMutableArray alloc] initWithCapacity:length];
while (newPlaceholders.count < length) {
[newPlaceholders addObject:[NSNull null]];
}
[_symbolCache replaceObjectsInRange:range withObjectsFromArray:newPlaceholders];
[self invalidate];
}
- (BOOL)hasCacheForSymbolAtIndex:(NSUInteger)index
{
if (index >= _symbolCache.count) {
return NO;
}
return _symbolCache[index] != [NSNull null];
}
- (MPFunctionLayout *)functionLayoutForFunctionAtIndex:(NSUInteger)index;
{
if ([self hasCacheForSymbolAtIndex:index]) {
id cacheObject = _symbolCache[index];
if ([cacheObject isKindOfClass:[NSValue class]]) {
return nil;
}
return cacheObject;
}
MPFunctionLayout *layout = [MPFunctionLayout functionLayoutForFunctionAtIndexPath:[self.expressionPath indexPathByAddingIndex:index] parent:self];
while (index >= _symbolCache.count) {
[_symbolCache addObject:[NSNull null]];
}
_symbolCache[index] = layout;
return layout;
}
- (NSSize)cachedSizeForSymbolAtIndex:(NSUInteger)index
{
id cachedSymbol = _symbolCache[index];
if ([cachedSymbol isKindOfClass:[NSValue class]]) {
return [cachedSymbol sizeValue];
}
return [(MPFunctionLayout *)cachedSymbol sizeOfFunction];
}
- (void)cacheSize:(NSSize)size forSymbolAtIndex:(NSUInteger)index
{
while (index >= _symbolCache.count) {
[_symbolCache addObject:[NSNull null]];
}
_symbolCache[index] = [NSValue valueWithSize:size];
}
#pragma mark Size Calculation Methods
- (NSSize)sizeForAllSymbols
{
if (!_valid) {
_cachedSize = [self sizeForSymbolsInRange:NSMakeRange(0, self.expression.numberOfSymbols)];
_valid = YES;
}
return _cachedSize;
}
- (NSSize)sizeForSymbolsInRange:(NSRange)range
{
NSSize size = NSMakeSize(0, 0);
for (NSUInteger index = range.location; index < NSMaxRange(range); index++) {
NSSize symbolSize = [self sizeForSymbolAtIndex:index];
size.width += symbolSize.width;
size.height = MAX(size.height, symbolSize.height);
}
return size;
}
- (NSSize)sizeForSymbolAtIndex:(NSUInteger)index
{
if ([self hasCacheForSymbolAtIndex:index]) {
return [self cachedSizeForSymbolAtIndex:index];
}
id symbol = [self.expression symbolAtIndex:index];
if ([symbol isString]) { if ([symbol isString]) {
[self.textStorage setString:symbol]; return [self bezierPathForChildAtIndex:index].bounds.size;
NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer];
NSSize symbolSize = [self.layoutManager boundingRectForGlyphRange:glyphRange
inTextContainer:self.textContainer].size;
[self cacheSize:symbolSize
forSymbolAtIndex:index];
return symbolSize;
} else { } else {
MPFunctionLayout *layout = [self functionLayoutForFunctionAtIndex:index]; return [self childLayoutAtIndex:index].size;
return [layout sizeOfFunction];
} }
} }
#pragma mark Drawing Methods #pragma mark Drawing Methods
- (NSBezierPath *)generateBezierPath
- (void)drawSymbolAtIndex:(NSUInteger)index
atPoint:(NSPoint)point
{ {
id symbol = [self.expression symbolAtIndex:index]; NSBezierPath *fullPath = [NSBezierPath bezierPath];
// point.x = point.y = 0; [fullPath moveToPoint:NSZeroPoint];
NSLog(@"draw Symbol: %@ at Point: (x: %f, y: %f)", symbol, point.x, point.y); NSUInteger x = 0;
if ([symbol isString]) { for (NSInteger index = 0; index < self.expression.numberOfElements; ++index) {
[self.textStorage setString:symbol]; NSAffineTransform *transform = [NSAffineTransform transform];
NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer]; // TODO: Translate by the right amount
[self.layoutManager drawGlyphsForGlyphRange:glyphRange [transform translateXBy:x yBy:0];
atPoint:point]; NSBezierPath *path = [self bezierPathForChildAtIndex:index].copy;
} else { [path transformUsingAffineTransform:transform];
MPFunctionLayout *layout = [self functionLayoutForFunctionAtIndex:index]; [fullPath appendBezierPath:path];
NSLog(@"layout: %@, index: %ld", layout, index); x += path.bounds.size.width;
[layout drawFunctionAtPoint:point];
} }
} return fullPath;
- (void)drawSymbolsInRange:(NSRange)range
atPoint:(NSPoint)point
{
NSSize overallSize = [self sizeForSymbolsInRange:range];
CGFloat x = point.x;
for (NSUInteger index = range.location; index < NSMaxRange(range); index++) {
NSSize symbolSize = [self sizeForSymbolAtIndex:index];
CGFloat dy = (overallSize.height - symbolSize.height) / 2;
[self drawSymbolAtIndex:index
atPoint:NSMakePoint(x, point.y + dy)];
x += symbolSize.width;
}
}
- (void)drawAllSymbolsAtPoint:(NSPoint)point
{
[self drawSymbolsInRange:NSMakeRange(0, [self.expression numberOfSymbols]) atPoint:point];
} }
@end @end

View File

@@ -10,9 +10,9 @@
@class MPExpressionStorage, MPExpressionLayout; @class MPExpressionStorage, MPExpressionLayout;
@interface MPExpressionStorage : MPMutableExpression @interface MPExpressionStorage : MPExpression
@property (nonatomic, strong) MPExpressionLayout *expressionLayout; @property (nonatomic, strong) MPExpressionLayout *rootLayout;
- (NSLayoutManager *)layoutManager; - (NSLayoutManager *)layoutManager;
- (NSTextContainer *)textContainer; - (NSTextContainer *)textContainer;

View File

@@ -19,11 +19,11 @@
@implementation MPExpressionStorage @implementation MPExpressionStorage
- (instancetype)initWithSymbols:(NSArray *)symbols - (instancetype)initWithElements:(NSArray *)elements
{ {
self = [super initWithSymbols:symbols]; self = [super initWithElements:elements];
if (self) { if (self) {
_expressionLayout = [[MPExpressionLayout alloc] initRootLayoutWithExpressionStorage:self]; _rootLayout = [[MPExpressionLayout alloc] initRootLayoutWithExpressionStorage:self];
} }
return self; return self;
} }
@@ -59,24 +59,17 @@
} }
} }
- (void)symbolsChangedInRangePath:(MPRangePath *)rangePath replacementLength:(NSUInteger)length - (void)didChangeElementsInRangePath:(MPRangePath *)rangePath
replacementLength:(NSUInteger)replacementLength
{ {
if (rangePath.location.length == 0) { if (rangePath.location.length == 0) {
return; return;
} }
id current = self.expressionLayout; MPLayout *current = self.rootLayout;
for (NSUInteger position = 1; position < rangePath.location.length-1; position++) { for (NSUInteger index = 1; index < rangePath.location.length-1; index++) {
if ([current isKindOfClass:[MPExpressionLayout class]]) { current = [current childLayoutAtIndex:index];
current = [(MPExpressionLayout *)current functionLayoutForFunctionAtIndex:position];
} else {
current = [(MPFunctionLayout *)current expressionLayoutForChildAtIndex:position];
}
}
if ([current isKindOfClass:[MPExpressionLayout class]]) {
[(MPExpressionLayout *)current editedExpressionInRange:rangePath.rangeAtLastIndex replacementLength:length];
} else {
[(MPFunctionLayout *)current editedChildAtIndex:[rangePath.location indexAtPosition:rangePath.location.length-1]];
} }
[current clearCacheInRange:rangePath.rangeAtLastIndex replacementLength:replacementLength];
} }
@end @end

View File

@@ -21,7 +21,6 @@
#pragma mark Properties #pragma mark Properties
@property (readonly, nonatomic, strong) MPExpressionStorage *expressionStorage; @property (readonly, nonatomic, strong) MPExpressionStorage *expressionStorage;
- (MPExpressionLayout *)expressionLayout; // Convenience Method
@property (nonatomic, getter = isEditable) BOOL editable; @property (nonatomic, getter = isEditable) BOOL editable;
@property (nonatomic, strong) MPRangePath *selection; @property (nonatomic, strong) MPRangePath *selection;

View File

@@ -7,13 +7,15 @@
// //
#import "MPExpressionView.h" #import "MPExpressionView.h"
#import "MPExpressionLayout.h"
#import "MPExpressionStorage.h" #import "MPExpressionStorage.h"
#import "MPExpressionLayout.h"
#import "NSObject+MPStringTest.h"
#import "MPSumFunction.h" #import "MPSumFunction.h"
@interface MPExpressionView (MPCursor)
@property (nonatomic, strong) NSTimer *cursorTimer;
@end
@implementation MPExpressionView @implementation MPExpressionView
#pragma mark Creation Methods #pragma mark Creation Methods
@@ -47,23 +49,20 @@
- (void)initializeObjects - (void)initializeObjects
{ {
MPExpressionStorage *expressionStorage = [[MPExpressionStorage alloc] initWithSymbols:@[@"12 345", [[MPSumFunction alloc] init], [[MPSumFunction alloc] init]]]; MPExpressionStorage *expressionStorage = [[MPExpressionStorage alloc] initWithElements:@[@"12345", [[MPSumFunction alloc] init], [[MPSumFunction alloc] init]]];
_expressionStorage = expressionStorage; _expressionStorage = expressionStorage;
[self.expressionLayout setExpressionView:self];
} }
#pragma mark Properties #pragma mark Properties
- (void)setExpressionStorage:(MPExpressionStorage *)expressionStorage - (BOOL)isFlipped
{ {
[_expressionStorage.expressionLayout setExpressionView:nil]; return NO;
_expressionStorage = expressionStorage;
[_expressionStorage.expressionLayout setExpressionView:self];
} }
- (MPExpressionLayout *)expressionLayout - (void)setExpressionStorage:(MPExpressionStorage *)expressionStorage
{ {
return self.expressionStorage.expressionLayout; _expressionStorage = expressionStorage;
} }
#pragma mark Drawing Methods #pragma mark Drawing Methods
@@ -74,11 +73,10 @@
[[NSColor whiteColor] set]; [[NSColor whiteColor] set];
NSRectFill(self.bounds); NSRectFill(self.bounds);
[[NSColor blackColor] set]; [[NSColor blackColor] set];
NSSize expressionSize = [self.expressionLayout sizeForAllSymbols]; NSSize expressionSize = [self.expressionStorage.rootLayout size];
CGFloat y = (self.bounds.size.height - expressionSize.height) / 2; CGFloat y = (self.bounds.size.height - expressionSize.height) / 2;
NSLog(@"%f", self.bounds.origin.y);
NSPoint point = NSMakePoint(self.bounds.origin.x, self.bounds.origin.y + y); NSPoint point = NSMakePoint(self.bounds.origin.x, self.bounds.origin.y + y);
[self.expressionLayout drawAllSymbolsAtPoint:point]; [self.expressionStorage.rootLayout drawAtPoint:point];
} }
@end @end

View File

@@ -6,58 +6,47 @@
// Copyright (c) 2014 Kim Wittenburg. All rights reserved. // Copyright (c) 2014 Kim Wittenburg. All rights reserved.
// //
@import Foundation;
#import "MPExpressionElement.h"
@class MPFunction, MPExpression, MPRangePath; @class MPFunction, MPExpression, MPRangePath;
@interface MPFunction : NSObject <NSCopying, NSCoding> @interface MPFunction : NSObject <NSCoding, NSCopying, MPExpressionElement>
#pragma mark Creation Methods #pragma mark Creation Methods
- (instancetype)init; - (instancetype)init;
#pragma mark Working With the Expression Tree #pragma mark Properties
// Subclasses should define accessor properties for all sub expressions, which, in the setter, should send didChangeElementAtRangePath:replacementLength: to self with a replacement length of 1.
#pragma mark Working With the Expression Tree
@property (nonatomic, weak) MPExpression *parent; // Documentation: Do not set @property (nonatomic, weak) MPExpression *parent; // Documentation: Do not set
- (NSUInteger)numberOfChildren; - (NSUInteger)numberOfChildren; // Override
- (MPExpression *)childAtIndex:(NSUInteger)index; - (MPExpression *)childAtIndex:(NSUInteger)index; // Override
- (void)setChild:(MPExpression *)child - (void)setChild:(MPExpression *)child
atIndex:(NSUInteger)index; atIndex:(NSUInteger)index; // Override
#pragma mark Evaluating Functions
- (double)doubleValue;
#pragma mark Working With Functions
- (BOOL)isEqualToFunction:(MPFunction *)aFunction;
@end
@interface MPFunction (MPFunctionExtensionMethods)
#pragma mark Working With the Expression Tree
// May be overridden for performance improvements // May be overridden for performance improvements
- (NSArray *)children; - (NSArray *)children; // Indexes must equal the ones from the native methods
- (NSUInteger)indexOfChild:(MPExpression *)child; - (NSUInteger)indexOfChild:(MPExpression *)child;
- (id)symbolAtIndexPath:(NSIndexPath *)indexPath; - (id)elementAtIndexPath:(NSIndexPath *)indexPath;
#pragma mark Evaluating Functions #pragma mark Evaluating Functions
- (double)doubleValue; // Override
- (float)floatValue; #pragma mark Messages
- (int)intValue; - (void)didChangeElementsInRangePath:(MPRangePath *)rangePath
- (NSInteger)integerValue; replacementLength:(NSUInteger)replacementLength;
- (long long)longLongValue; - (void)didChangeChild:(MPExpression *)child;
- (void)didChangeChildAtIndex:(NSUInteger)index;
- (NSString *)description; #pragma mark Working With Functions
- (BOOL)isEqualToFunction:(MPFunction *)aFunction; // Override
- (NSUInteger)hash; - (NSString *)description; // Should be overridden
- (NSUInteger)hash;// Override
@end
@interface MPFunction (MPDisplayExtension)
- (void)symbolsChangedInRangePath:(MPRangePath *)rangePath replacementLength:(NSUInteger)length;
@end @end

View File

@@ -10,12 +10,11 @@
#import "MPExpression.h" #import "MPExpression.h"
#import "MPRangePath.h" #import "MPRangePath.h"
#import "NSIndexPath+MPReverseIndexPath.h" #import "NSIndexPath+MPAdditions.h"
@implementation MPFunction @implementation MPFunction
#pragma mark Creation Methods #pragma mark Creation Methods
- (instancetype)init - (instancetype)init
{ {
self = [super init]; self = [super init];
@@ -25,7 +24,6 @@
} }
#pragma mark Working With the Expression Tree #pragma mark Working With the Expression Tree
- (NSUInteger)numberOfChildren - (NSUInteger)numberOfChildren
{ {
return 0; return 0;
@@ -38,63 +36,10 @@
- (void)setChild:(MPExpression *)child - (void)setChild:(MPExpression *)child
atIndex:(NSUInteger)index atIndex:(NSUInteger)index
{}
#pragma mark Evaluating Functions
- (double)doubleValue
{ {
return 0; [self didChangeChildAtIndex:index];
} }
#pragma mark Working With Functions
- (BOOL)isEqual:(id)object
{
if (self == object) {
return YES;
}
if (object == nil) {
return NO;
}
if (![object isKindOfClass:[MPFunction class]]) {
return NO;
}
return [self isEqualToFunction:(MPFunction *)object];
}
- (BOOL)isEqualToFunction:(MPFunction *)aFunction
{
return [aFunction isMemberOfClass:[MPFunction class]] && [self isMemberOfClass:[MPFunction class]];
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone
{
return [[MPFunction allocWithZone:zone] init];
}
#pragma mark - NSCoding
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self) {
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{}
@end
@implementation MPFunction (MPFunctionExtensionMethods)
#pragma mark Working With the Expression Tree
- (NSArray *)children - (NSArray *)children
{ {
NSUInteger childCount = [self numberOfChildren]; NSUInteger childCount = [self numberOfChildren];
@@ -116,16 +61,115 @@
return NSNotFound; return NSNotFound;
} }
- (id)symbolAtIndexPath:(NSIndexPath *)indexPath - (id)elementAtIndexPath:(NSIndexPath *)indexPath
{ {
if (indexPath.length == 0) { if (indexPath.length == 0) {
return self; return self;
} }
MPExpression *child = [self childAtIndex:[indexPath indexAtPosition:0]]; MPExpression *child = [self childAtIndex:[indexPath indexAtPosition:0]];
return [child symbolAtIndexPath:[indexPath indexPathByRemovingFirstIndex]]; return [child elementAtIndexPath:[indexPath indexPathByRemovingFirstIndex]];
} }
#pragma mark Evaluating Functions #pragma mark Evaluating Functions
- (double)doubleValue
{
return 0;
}
#pragma mark Notifications
- (void)didChangeElementsInRangePath:(MPRangePath *)rangePath
replacementLength:(NSUInteger)replacementLength
{
NSUInteger selfIndex = [self.parent indexOfElement:self];
MPRangePath *newPath = rangePath.copy;
newPath.location = [newPath.location indexPathByPreceedingIndex:selfIndex];
[self.parent didChangeElementsInRangePath:newPath
replacementLength:replacementLength];
}
- (void)didChangeChild:(MPExpression *)child
{
[self didChangeChildAtIndex:[self indexOfChild:child]];
}
- (void)didChangeChildAtIndex:(NSUInteger)index
{
MPRangePath *path = [[MPRangePath alloc] initWithRange:NSMakeRange(index, 1)];
[self didChangeElementsInRangePath:path
replacementLength:1];
}
#pragma mark Working With Functions
- (BOOL)isEqual:(id)object
{
if (self == object) {
return YES;
}
if (object == nil) {
return NO;
}
if (![object isKindOfClass:[MPFunction class]]) {
return NO;
}
return [self isEqualToFunction:(MPFunction *)object];
}
- (BOOL)isEqualToFunction:(MPFunction *)aFunction
{
return [aFunction isMemberOfClass:[MPFunction class]] && [self isMemberOfClass:[MPFunction class]];
}
- (NSString *)description
{
return @"[]";
}
- (NSUInteger)hash
{
return 0;
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone
{
return [[MPFunction allocWithZone:zone] init];
}
#pragma mark - NSCoding
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self) {
NSArray *children = [aDecoder decodeObject];
NSInteger index = 0;
for (MPExpression *child in children) {
[self setChild:child atIndex:index];
++index;
}
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.children];
}
#pragma mark - MPExpressionElement
- (BOOL)isString
{
return NO;
}
- (BOOL)isFunction
{
return YES;
}
- (NSUInteger)length
{
return 1;
}
- (float)floatValue - (float)floatValue
{ {
@@ -147,26 +191,4 @@
return (long long)[self doubleValue]; return (long long)[self doubleValue];
} }
- (NSString *)description
{
return @"[]";
}
- (NSUInteger)hash
{
return 0;
}
@end
@implementation MPFunction (MPDisplayExtension)
- (void)symbolsChangedInRangePath:(MPRangePath *)rangePath replacementLength:(NSUInteger)length
{
NSUInteger index = [self.parent indexOfSymbol:self];
NSIndexPath *newLocation = [rangePath.location indexPathByPrecedingIndex:index];
MPRangePath *newRangePath = [[MPRangePath alloc] initWithLocation:newLocation length:rangePath.length];
[self.parent symbolsChangedInRangePath:newRangePath replacementLength:length];
}
@end @end

View File

@@ -2,56 +2,30 @@
// MPFunctionLayout.h // MPFunctionLayout.h
// MathPad // MathPad
// //
// Created by Kim Wittenburg on 22.04.14. // Created by Kim Wittenburg on 07.08.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved. // Copyright (c) 2014 Kim Wittenburg. All rights reserved.
// //
#import <Foundation/Foundation.h> @import Cocoa;
#import "MPLayout.h"
@class MPFunctionLayout, MPExpressionLayout, MPExpressionStorage, MPFunction; @interface MPFunctionLayout : MPLayout
@interface MPFunctionLayout : NSObject { + (MPFunctionLayout *)functionLayoutForFunctionAtIndexPath:(NSIndexPath *)path
@protected parent:(MPExpressionLayout *)parent;
BOOL _valid;
NSSize _cachedSize;
NSMutableArray *_childCache;
}
#pragma mark Creation Methods @property (readonly, nonatomic, weak) MPFunction *function; // Convenience
+ (instancetype)functionLayoutForFunctionAtIndexPath:(NSIndexPath *)functionPath @end
parent:(MPExpressionLayout *)parent;
@interface MPFunctionLayout (MPSubclassOverride)
- (id)initWithFunctionPath:(NSIndexPath *)functionPath
parent:(MPExpressionLayout *)parent; // Should also implement accessor method for special function type:
// - (MPCustomFunction *)customFunction
#pragma mark Properties // {
// return (MPCustomFunction *)self.function;
@property (readonly, nonatomic, weak) MPExpressionLayout *parent; // }
@property (readonly, nonatomic, strong) NSIndexPath *functionPath; - (NSBezierPath *)generateBezierPath;
- (MPExpressionStorage *)expressionStorage;
- (MPFunction *)function; // Convenience
- (NSLayoutManager *)layoutManager;
- (NSTextContainer *)textContainer;
- (NSTextStorage *)textStorage;
#pragma mark Cache Methods
- (void)invalidate;
- (void)editedChildAtIndex:(NSUInteger)index;
- (BOOL)hasCacheForChildAtIndex:(NSUInteger)index;
- (MPExpressionLayout *)expressionLayoutForChildAtIndex:(NSUInteger)index;
#pragma mark Size Calculation Methods
- (NSSize)sizeOfFunction;
- (NSSize)calculateSize;
#pragma mark Drawing Methods
- (void)drawFunctionAtPoint:(NSPoint)point;
@end @end

View File

@@ -2,14 +2,13 @@
// MPFunctionLayout.m // MPFunctionLayout.m
// MathPad // MathPad
// //
// Created by Kim Wittenburg on 22.04.14. // Created by Kim Wittenburg on 07.08.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved. // Copyright (c) 2014 Kim Wittenburg. All rights reserved.
// //
#import "MPFunctionLayout.h" #import "MPFunctionLayout.h"
#import "MPExpressionLayout.h"
#import "MPExpressionStorage.h"
#import "MPFunction.h" #import "MPFunction.h"
#import "MPExpressionLayout.h"
#import "MPSumFunction.h" #import "MPSumFunction.h"
#import "MPSumFunctionLayout.h" #import "MPSumFunctionLayout.h"
@@ -17,114 +16,43 @@
@implementation MPFunctionLayout @implementation MPFunctionLayout
#pragma mark Creation Methods #pragma mark Creation Methods
+ (MPFunctionLayout *)functionLayoutForFunctionAtIndexPath:(NSIndexPath *)path
+ (instancetype)functionLayoutForFunctionAtIndexPath:(NSIndexPath *)functionPath parent:(MPExpressionLayout *)parent
parent:(MPExpressionLayout *)parent
{ {
MPFunction *function = [parent.expressionStorage symbolAtIndexPath:functionPath]; MPFunction *function = [parent.expressionStorage elementAtIndexPath:path];
Class class = [function class]; Class class = [function class];
if (class == [MPSumFunction class]) { if (class == [MPSumFunction class]) {
return [[MPSumFunctionLayout alloc] initWithFunctionPath:functionPath parent:parent]; return [[MPSumFunctionLayout alloc] initWithPath:path parent:parent];
} }
return nil; return [[self alloc] initWithPath:path parent:parent];
}
- (id)initWithFunctionPath:(NSIndexPath *)functionPath
parent:(MPExpressionLayout *)parent
{
self = [super init];
if (self) {
_functionPath = functionPath;
_parent = parent;
_childCache = [[NSMutableArray alloc] init];
}
return self;
} }
#pragma mark Properties #pragma mark Properties
- (MPExpressionStorage *)expressionStorage
{
return self.parent.expressionStorage;
}
- (MPFunction *)function - (MPFunction *)function
{ {
return [self.expressionStorage symbolAtIndexPath:self.functionPath]; return [self.expressionStorage elementAtIndexPath:self.path];
}
- (NSLayoutManager *)layoutManager
{
return self.expressionStorage.layoutManager;
}
- (NSTextContainer *)textContainer
{
return self.expressionStorage.textContainer;
}
- (NSTextStorage *)textStorage
{
return self.expressionStorage.textStorage;
} }
#pragma mark Cache Methods #pragma mark Cache Methods
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index
- (void)invalidate
{ {
_valid = NO; return [self cachableObjectForIndex:index generator:^id{
[self.parent invalidate]; NSIndexPath *childPath = [self.path indexPathByAddingIndex:index];
MPExpressionLayout *layout = [[MPExpressionLayout alloc] initWithPath:childPath
parent:self];
return layout;
}];
} }
- (void)editedChildAtIndex:(NSUInteger)index - (NSSize)sizeForChildAtIndex:(NSUInteger)index
{ {
if ([self hasCacheForChildAtIndex:index]) { MPLayout *childLayout = [self childLayoutAtIndex:index];
_childCache[index] = [NSNull null]; return [childLayout size];
}
[self invalidate];
} }
- (BOOL)hasCacheForChildAtIndex:(NSUInteger)index - (NSBezierPath *)generateBezierPath
{
if (index >= _childCache.count) {
return NO;
}
return _childCache[index] != [NSNull null];
}
- (MPExpressionLayout *)expressionLayoutForChildAtIndex:(NSUInteger)index
{
if ([self hasCacheForChildAtIndex:index]) {
return _childCache[index];
}
while (index >= _childCache.count) {
[_childCache addObject:[NSNull null]];
}
MPExpressionLayout *expressionLayout = [[MPExpressionLayout alloc] initWithExpressionPath:[self.functionPath indexPathByAddingIndex:index] parent:self];
_childCache[index] = expressionLayout;
return expressionLayout;
}
#pragma mark Size Calculation Methods
- (NSSize)sizeOfFunction
{
if (!_valid) {
_cachedSize = [self calculateSize];
_valid = YES;
}
return _cachedSize;
}
- (NSSize)calculateSize
{
return NSMakeSize(0, 0);
}
#pragma mark Drawing Methods
- (void)drawFunctionAtPoint:(NSPoint)point
{ {
return [NSBezierPath bezierPath];
} }
@end @end

56
MathPad/MPLayout.h Normal file
View File

@@ -0,0 +1,56 @@
//
// MPDrawable.h
// MathPad
//
// Created by Kim Wittenburg on 07.08.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
@import Cocoa;
#import "MPExpressionStorage.h"
#define MPNull [NSNull null]
@interface MPLayout : NSObject
#pragma mark Creation Methods
- (instancetype)init;
- (instancetype)initWithPath:(NSIndexPath *)path
parent:(MPLayout *)parent;
#pragma mark Text System Objects
@property (readonly, nonatomic, weak) MPExpressionStorage *expressionStorage;
@property (readonly, nonatomic, weak) NSLayoutManager *layoutManager;
@property (readonly, nonatomic, weak) NSTextContainer *textContainer;
@property (readonly, nonatomic, weak) NSTextStorage *textStorage;
#pragma mark Cache Tree
@property (readonly, nonatomic, weak) MPLayout *parent;
@property (readonly, nonatomic, strong) NSIndexPath *path;
#pragma mark Cache Methods
// Querying Caches
- (id)cachableObjectForIndex:(NSUInteger)index
generator:(id(^)())generator;
// Clearing Caches
- (void)clearCacheInRange:(NSRange)range
replacementLength:(NSUInteger)replacementLength;
- (void)invalidate;
#pragma mark Calculation and Drawing Methods
// @property (nonatomic) BOOL usesSmallSize;
- (NSSize)size;
- (NSBezierPath *)bezierPath;
- (NSBezierPath *)bezierPathAtOrigin:(NSPoint)point;
- (void)drawAtPoint:(NSPoint)point;
@end
@interface MPLayout (MPSubclassImplement)
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index; // To be implemented
- (NSSize)sizeForChildAtIndex:(NSUInteger)index; // To be implemented
- (NSBezierPath *)generateBezierPath; // To be implemented
@end

159
MathPad/MPLayout.m Normal file
View File

@@ -0,0 +1,159 @@
//
// MPLayout.m
// MathPad
//
// Created by Kim Wittenburg on 11.08.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPLayout.h"
@interface MPLayout ()
// Querying Caches
- (BOOL)hasCacheForElementAtIndex:(NSUInteger)index;
// Storing Caches
- (void)cacheObject:(id)anObject
forElementAtIndex:(NSUInteger)index;
- (void)ensureCacheSizeForIndex:(NSUInteger)index;
@end
@implementation MPLayout {
NSMutableArray *_cache;
NSBezierPath *_cachedPath;
}
#pragma mark Creation Methods
- (id)init
{
self = [super init];
if (self) {
_cache = [[NSMutableArray alloc] init];
_cachedPath = nil;
_path = [[NSIndexPath alloc] init];
}
return self;
}
- (id)initWithPath:(NSIndexPath *)path
parent:(MPLayout *)parent
{
self = [self init];
if (self) {
_path = path;
_parent = parent;
}
return self;
}
#pragma Text System Objects
- (MPExpressionStorage *)expressionStorage
{
return self.parent.expressionStorage;
}
- (NSLayoutManager *)layoutManager
{
return self.expressionStorage.layoutManager;
}
- (NSTextContainer *)textContainer
{
return self.expressionStorage.textContainer;
}
- (NSTextStorage *)textStorage
{
return self.expressionStorage.textStorage;
}
#pragma mark Cache Tree
// Querying Caches
- (BOOL)hasCacheForElementAtIndex:(NSUInteger)index
{
if (index >= _cache.count) {
return NO;
}
return _cache[index] != MPNull;
}
- (id)cachableObjectForIndex:(NSUInteger)index
generator:(id (^)())generator
{
if ([self hasCacheForElementAtIndex:index]) {
return _cache[index];
}
id object = generator();
[self cacheObject:object
forElementAtIndex:index];
return object;
}
// Storing Caches
- (void)cacheObject:(id)anObject
forElementAtIndex:(NSUInteger)index
{
[self ensureCacheSizeForIndex:index];
_cache[index] = anObject;
}
- (void)ensureCacheSizeForIndex:(NSUInteger)index
{
while (index >= _cache.count) {
[_cache addObject:MPNull];
}
}
// Clearing Caches
- (void)clearCacheInRange:(NSRange)range
replacementLength:(NSUInteger)replacementLength
{
NSMutableArray *placeholders = [[NSMutableArray alloc] initWithCapacity:replacementLength];
while (placeholders.count < replacementLength) {
[placeholders addObject:MPNull];
}
[_cache replaceObjectsInRange:range
withObjectsFromArray:placeholders];
[self invalidate];
}
- (void)invalidate
{
_cachedPath = nil;
[self.parent invalidate];
}
#pragma mark Calculation Methods
- (NSSize)size
{
return self.bezierPath.bounds.size;
}
- (NSBezierPath *)bezierPath
{
if (!_cachedPath) {
_cachedPath = [self generateBezierPath];
}
return _cachedPath;
}
- (NSBezierPath *)bezierPathAtOrigin:(NSPoint)point
{
NSAffineTransform *transform = [NSAffineTransform transform];
[transform translateXBy:point.x
yBy:point.y];
NSBezierPath *path = [NSBezierPath bezierPath];
[path appendBezierPath:self.bezierPath];
[path transformUsingAffineTransform:transform];
return path;
}
- (void)drawAtPoint:(NSPoint)point
{
NSBezierPath *path = [self bezierPathAtOrigin:point];
[path fill];
}
@end

View File

@@ -18,8 +18,6 @@
#import "MPFunction.h" #import "MPFunction.h"
#import "MPRangePath.h" #import "MPRangePath.h"
#import "NSObject+MPStringTest.h" #import "NSIndexPath+MPAdditions.h"
#import "NSTextStorage+MPSetContents.h"
#import "NSIndexPath+MPReverseIndexPath.h"
#endif #endif

View File

@@ -6,8 +6,12 @@
// Copyright (c) 2014 Kim Wittenburg. All rights reserved. // Copyright (c) 2014 Kim Wittenburg. All rights reserved.
// //
@import Foundation;
#import "MPExpression.h" #import "MPExpression.h"
@class MPRangePath, MPExpression;
@interface MPRangePath : NSObject <NSCopying, NSCoding> @interface MPRangePath : NSObject <NSCopying, NSCoding>
#pragma mark Creation Methods #pragma mark Creation Methods

View File

@@ -7,6 +7,7 @@
// //
#import "MPRangePath.h" #import "MPRangePath.h"
#import "MPExpression.h"
@implementation MPRangePath @implementation MPRangePath
@@ -143,7 +144,7 @@
- (MPExpression *)subexpressionWithRangePath:(MPRangePath *)aRangePath - (MPExpression *)subexpressionWithRangePath:(MPRangePath *)aRangePath
{ {
MPExpression *targetExpression = [self symbolAtIndexPath:[aRangePath.location indexPathByRemovingLastIndex]]; MPExpression *targetExpression = [self elementAtIndexPath:[aRangePath.location indexPathByRemovingLastIndex]];
if (![targetExpression isKindOfClass:[MPExpression class]]) { if (![targetExpression isKindOfClass:[MPExpression class]]) {
return nil; return nil;
} }

View File

@@ -6,6 +6,7 @@
// Copyright (c) 2014 Kim Wittenburg. All rights reserved. // Copyright (c) 2014 Kim Wittenburg. All rights reserved.
// //
@import Foundation;
#import "MPFunction.h" #import "MPFunction.h"
@class MPSumFunction, MPExpression; @class MPSumFunction, MPExpression;

View File

@@ -12,7 +12,6 @@
@implementation MPSumFunction @implementation MPSumFunction
#pragma mark Creation Methods #pragma mark Creation Methods
- (instancetype)init - (instancetype)init
{ {
self = [super init]; self = [super init];
@@ -27,8 +26,32 @@
return self; return self;
} }
#pragma mark Working With the Expression Tree #pragma mark Properties
- (void)setStartExpression:(MPExpression *)startExpression
{
_startExpression.parent = nil;
_startExpression = startExpression;
_startExpression.parent = self;
[self didChangeChildAtIndex:0];
}
- (void)setTargetExpression:(MPExpression *)targetExpression
{
_targetExpression.parent = nil;
_targetExpression = targetExpression;
_targetExpression.parent = self;
[self didChangeChildAtIndex:1];
}
- (void)setSumExpression:(MPExpression *)sumExpression
{
_sumExpression.parent = nil;
_sumExpression = sumExpression;
_sumExpression.parent = self;
[self didChangeChildAtIndex:2];
}
#pragma mark Working With the Expression Tree
- (NSUInteger)numberOfChildren - (NSUInteger)numberOfChildren
{ {
return 3; return 3;
@@ -48,36 +71,32 @@
} }
} }
- (NSArray *)children
{
return @[self.startExpression, self.targetExpression, self.sumExpression];
}
- (void)setChild:(MPExpression *)child - (void)setChild:(MPExpression *)child
atIndex:(NSUInteger)index atIndex:(NSUInteger)index
{ {
switch (index) { switch (index) {
// TODO: Copy child? // TODO: Copy child?
case 0: case 0:
self.startExpression.parent = nil;
self.startExpression = child; self.startExpression = child;
break; break;
case 1: case 1:
self.targetExpression.parent = nil;
self.targetExpression = child; self.targetExpression = child;
break; break;
case 2: case 2:
self.sumExpression.parent = nil;
self.sumExpression = child; self.sumExpression = child;
break; break;
default: default:
return; return;
} }
child.parent = self; [self didChangeChildAtIndex:index];
}
- (NSArray *)children
{
return @[self.startExpression, self.targetExpression, self.sumExpression];
} }
#pragma mark Evaluating Functions #pragma mark Evaluating Functions
- (double)doubleValue - (double)doubleValue
{ {
#warning Implementation #warning Implementation
@@ -85,7 +104,6 @@
} }
#pragma mark Working With Functions #pragma mark Working With Functions
- (BOOL)isEqualToFunction:(MPFunction *)aFunction - (BOOL)isEqualToFunction:(MPFunction *)aFunction
{ {
if (![aFunction isKindOfClass:[MPSumFunction class]]) { if (![aFunction isKindOfClass:[MPSumFunction class]]) {
@@ -106,8 +124,13 @@
return [NSString stringWithFormat:@"Sum(From: %@; To: %@; Using: %@)", self.startExpression, self.targetExpression, self.sumExpression]; return [NSString stringWithFormat:@"Sum(From: %@; To: %@; Using: %@)", self.startExpression, self.targetExpression, self.sumExpression];
} }
#pragma mark - NSCopying - (NSUInteger)hash
{
#warning Unimplemented Method
return [super hash];
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone - (id)copyWithZone:(NSZone *)zone
{ {
MPSumFunction *copy = [[MPSumFunction allocWithZone:zone] init]; MPSumFunction *copy = [[MPSumFunction allocWithZone:zone] init];

View File

@@ -16,20 +16,21 @@
return (MPSumFunction *)self.function; return (MPSumFunction *)self.function;
} }
- (NSSize)calculateSize - (NSBezierPath *)generateBezierPath
{ {
NSAttributedString *text = [[NSAttributedString alloc] initWithString:@"∑" attributes:@{NSFontAttributeName: [NSFont fontWithName:@"HelveticaNeue" size:50.0]}]; NSAttributedString *text = [[NSAttributedString alloc] initWithString:@"∑"
[self.textStorage setAttributedString:text]; attributes:@{NSFontAttributeName: [NSFont fontWithName:@"Lucida Grande" size:18.0]}];
self.textStorage.attributedString = text;
NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer]; NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer];
return [self.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:self.textContainer].size; NSGlyph glyphs[glyphRange.length+1];
} NSUInteger actualGylphCount = [self.layoutManager getGlyphs:glyphs
range:glyphRange];
- (void)drawFunctionAtPoint:(NSPoint)point NSBezierPath *path = [NSBezierPath bezierPath];
{ [path moveToPoint:NSZeroPoint];
NSAttributedString *text = [[NSAttributedString alloc] initWithString:@"∑" attributes:@{NSFontAttributeName: [NSFont fontWithName:@"HelveticaNeue" size:50.0]}]; [path appendBezierPathWithGlyphs:glyphs
[self.textStorage setAttributedString:text]; count:actualGylphCount
NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer]; inFont:[NSFont fontWithName:@"Lucida Grande" size:18.0]];
[self.layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:point]; return path;
} }
@end @end

View File

@@ -0,0 +1,30 @@
//
// NSIndexPath+MPRemoveFirstIndex.h
// MathPad
//
// Created by Kim Wittenburg on 23.04.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
@import Foundation;
@interface NSIndexPath (MPAdditions)
/*!
@method indexPathByRemovingFirstIndex
@brief Provides an index path with the indexes in the receiving index path, excluding the first one.
@discussion Returns an empty NSIndexPath instance if the receiving index paths length is 1 or less.
@return A new index path with the receiving index paths indexes, excluding the first one.
*/
- (NSIndexPath *)indexPathByRemovingFirstIndex;
/*!
@method indexPathByPreceedingIndex:
@brief Provides an index path with the given index followed by the indexes of the receiver.
@discussion If the receiver does not contain any indexes the given index is the only index contained in the returned index path.
@param index The index new index preceeding all others
@return A new index path with all the receiver's indexes preceeded by @c index.
*/
- (NSIndexPath *)indexPathByPreceedingIndex:(NSUInteger)index;
@end

View File

@@ -0,0 +1,37 @@
//
// NSIndexPath+MPRemoveFirstIndex.m
// MathPad
//
// Created by Kim Wittenburg on 23.04.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "NSIndexPath+MPAdditions.h"
@implementation NSIndexPath (MPAdditions)
- (NSIndexPath *)indexPathByRemovingFirstIndex
{
if (self.length <= 1) {
return [[NSIndexPath alloc] init];
}
NSUInteger indexes[self.length];
[self getIndexes:indexes];
NSUInteger newIndexes[self.length-1];
for (NSUInteger i = 0; i < self.length-1; i++) {
newIndexes[i] = indexes[i+1];
}
return [[NSIndexPath alloc] initWithIndexes:newIndexes length:self.length-1];
}
- (NSIndexPath *)indexPathByPreceedingIndex:(NSUInteger)index
{
NSUInteger newIndexes[self.length+1];
newIndexes[0] = index;
for (NSUInteger i = 0; i < self.length; i++) {
newIndexes[i+1] = [self indexAtPosition:i];
}
return [[NSIndexPath alloc] initWithIndexes:newIndexes length:self.length+1];
}
@end

View File

@@ -0,0 +1,14 @@
//
// NSString+MPExpressionElement.h
// MathPad
//
// Created by Kim Wittenburg on 10.08.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
@import Foundation;
#import "MPExpressionElement.h"
@interface NSString (MPExpressionElement) <MPExpressionElement>
@end

View File

@@ -0,0 +1,49 @@
//
// NSString+MPExpressionElement.m
// MathPad
//
// Created by Kim Wittenburg on 10.08.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "NSString+MPExpressionElement.h"
@implementation NSString (MPExpressionElement)
- (BOOL)isString
{
return YES;
}
- (BOOL)isFunction
{
return NO;
}
- (double)doubleValue
{
#warning Unimplemented Method
return 0;
}
- (float)floatValue
{
return (float)[self doubleValue];
}
- (int)intValue
{
return (int)[self doubleValue];
}
- (NSInteger)integerValue
{
return (NSInteger)[self doubleValue];
}
- (long long)longLongValue
{
return (long long)[self doubleValue];
}
@end

View File

@@ -20,119 +20,119 @@
- (void)testInitialization { - (void)testInitialization {
// Test empty expression // Test empty expression
MPExpression *testExpression = [[MPExpression alloc] init]; MPExpression *testExpression = [[MPExpression alloc] init];
XCTAssertEqual([testExpression numberOfSymbols], 0); XCTAssertEqual(testExpression.numberOfElements, 0);
// Test expression with string // Test expression with string
testExpression = [[MPExpression alloc] initWithString:@"1234+5678"]; testExpression = [[MPExpression alloc] initWithElement:@"1234+5678"];
XCTAssertEqual([testExpression numberOfSymbols], 1); XCTAssertEqual(testExpression.numberOfElements, 1);
XCTAssertEqualObjects([testExpression symbolAtIndex:0], @"1234+5678"); XCTAssertEqualObjects([testExpression elementAtIndex:0], @"1234+5678");
// Test expression with function // Test expression with function
testExpression = [[MPExpression alloc] initWithFunction:[[MPFunction alloc] init]]; testExpression = [[MPExpression alloc] initWithElement:[[MPFunction alloc] init]];
XCTAssertEqual([testExpression numberOfSymbols], 1); XCTAssertEqual([testExpression numberOfElements], 1);
XCTAssertEqualObjects([testExpression symbolAtIndex:0], [[MPFunction alloc] init]); XCTAssertEqualObjects([testExpression elementAtIndex:0], [[MPFunction alloc] init]);
testExpression = [[MPExpression alloc] initWithSymbols:@[@"1234", [[MPFunction alloc] init], @"17", [[MPFunction alloc] init]]]; testExpression = [[MPExpression alloc] initWithElements:@[@"1234", [[MPFunction alloc] init], @"17", [[MPFunction alloc] init]]];
XCTAssertEqual([testExpression numberOfSymbols], 4); XCTAssertEqual([testExpression numberOfElements], 4);
XCTAssertEqualObjects([testExpression symbolAtIndex:2], @"17"); XCTAssertEqualObjects([testExpression elementAtIndex:2], @"17");
// Test expression with subsequent strings // Test expression with subsequent strings
testExpression = [[MPExpression alloc] initWithSymbols:@[@"1234", @"5678"]]; testExpression = [[MPExpression alloc] initWithElements:@[@"1234", @"5678"]];
XCTAssertEqual([testExpression numberOfSymbols], 1); XCTAssertEqual([testExpression numberOfElements], 1);
XCTAssertEqualObjects([testExpression symbolAtIndex:0], @"12345678"); XCTAssertEqualObjects([testExpression elementAtIndex:0], @"12345678");
// Test expression with only empty string // Test expression with only empty string
testExpression = [[MPExpression alloc] initWithString:@""]; testExpression = [[MPExpression alloc] initWithElement:@""];
XCTAssertEqual([testExpression numberOfSymbols], 0); XCTAssertEqual([testExpression numberOfElements], 0);
} }
- (void)testSubexpressions { - (void)testSubexpressions {
MPExpression *testExpression = [MPExpression expressionWithSymbols:@[@"1234", [[MPFunction alloc] init], @"17", [[MPFunction alloc] init]]]; MPExpression *testExpression = [[MPExpression alloc] initWithElements:@[@"1234", [[MPFunction alloc] init], @"17", [[MPFunction alloc] init]]];
/********** subexpressionFromIndex: **********/ /********** subexpressionFromIndex: **********/
// Test with start index at front // Test with start index at front
MPExpression *subexpression = [testExpression subexpressionFromIndex:0]; MPExpression *subexpression = [testExpression subexpressionFromLocation:0];
XCTAssertEqual([subexpression numberOfSymbols], 4); XCTAssertEqual([subexpression numberOfElements], 4);
XCTAssertEqualObjects([subexpression symbolAtIndex:0], @"1234"); XCTAssertEqualObjects([subexpression elementAtIndex:0], @"1234");
// Test with start index in first symbol // Test with start index in first element
subexpression = [testExpression subexpressionFromIndex:2]; subexpression = [testExpression subexpressionFromLocation:2];
XCTAssertEqual([subexpression numberOfSymbols], 4); XCTAssertEqual([subexpression numberOfElements], 4);
XCTAssertEqualObjects([subexpression symbolAtIndex:0], @"34"); XCTAssertEqualObjects([subexpression elementAtIndex:0], @"34");
// Test with start index in middle symbol starting with a literal // Test with start index in middle element starting with a literal
subexpression = [testExpression subexpressionFromIndex:6]; subexpression = [testExpression subexpressionFromLocation:6];
XCTAssertEqual([subexpression numberOfSymbols], 2); XCTAssertEqual([subexpression numberOfElements], 2);
XCTAssertEqualObjects([subexpression symbolAtIndex:0], @"7"); XCTAssertEqualObjects([subexpression elementAtIndex:0], @"7");
// Test with start index in middle symbol starting with a function // Test with start index in middle element starting with a function
subexpression = [testExpression subexpressionFromIndex:4]; subexpression = [testExpression subexpressionFromLocation:4];
XCTAssertEqual([subexpression numberOfSymbols], 3); XCTAssertEqual([subexpression numberOfElements], 3);
XCTAssertEqualObjects([subexpression symbolAtIndex:0], [[MPFunction alloc] init]); XCTAssertEqualObjects([subexpression elementAtIndex:0], [[MPFunction alloc] init]);
// Test with start index in last symbol // Test with start index in last element
subexpression = [testExpression subexpressionFromIndex:7]; subexpression = [testExpression subexpressionFromLocation:7];
XCTAssertEqual([subexpression numberOfSymbols], 1); XCTAssertEqual([subexpression numberOfElements], 1);
XCTAssertEqualObjects([subexpression symbolAtIndex:0], [[MPFunction alloc] init]); XCTAssertEqualObjects([subexpression elementAtIndex:0], [[MPFunction alloc] init]);
// Test with start index at end // Test with start index at end
subexpression = [testExpression subexpressionFromIndex:8]; subexpression = [testExpression subexpressionFromLocation:8];
XCTAssertEqual([subexpression numberOfSymbols], 0); XCTAssertEqual([subexpression numberOfElements], 0);
/********** subexpressionToIndex: **********/ /********** subexpressionToIndex: **********/
// Test with end index at front // Test with end index at front
subexpression = [testExpression subexpressionToIndex:0]; subexpression = [testExpression subexpressionToLocation:0];
XCTAssertEqual([subexpression numberOfSymbols], 0); XCTAssertEqual([subexpression numberOfElements], 0);
// Test with end index in first symbol // Test with end index in first Element
subexpression = [testExpression subexpressionToIndex:2]; subexpression = [testExpression subexpressionToLocation:2];
XCTAssertEqual([subexpression numberOfSymbols], 1); XCTAssertEqual([subexpression numberOfElements], 1);
XCTAssertEqualObjects([subexpression symbolAtIndex:0], @"12"); XCTAssertEqualObjects([subexpression elementAtIndex:0], @"12");
// Test with end index in middle symbol ending with a literal // Test with end index in middle Element ending with a literal
subexpression = [testExpression subexpressionToIndex:6]; subexpression = [testExpression subexpressionToLocation:6];
XCTAssertEqual([subexpression numberOfSymbols], 3); XCTAssertEqual([subexpression numberOfElements], 3);
XCTAssertEqualObjects([subexpression symbolAtIndex:2], @"1"); XCTAssertEqualObjects([subexpression elementAtIndex:2], @"1");
// Test with end index in middle symbol ending with a function // Test with end index in middle Element ending with a function
subexpression = [testExpression subexpressionToIndex:5]; subexpression = [testExpression subexpressionToLocation:5];
XCTAssertEqual([subexpression numberOfSymbols], 2); XCTAssertEqual([subexpression numberOfElements], 2);
XCTAssertEqualObjects([subexpression symbolAtIndex:1], [[MPFunction alloc] init]); XCTAssertEqualObjects([subexpression elementAtIndex:1], [[MPFunction alloc] init]);
// Test with end index at end // Test with end index at end
subexpression = [testExpression subexpressionToIndex:8]; subexpression = [testExpression subexpressionToLocation:8];
XCTAssertEqual([subexpression numberOfSymbols], 4); XCTAssertEqual([subexpression numberOfElements], 4);
XCTAssertEqualObjects([subexpression symbolAtIndex:3], [[MPFunction alloc] init]); XCTAssertEqualObjects([subexpression elementAtIndex:3], [[MPFunction alloc] init]);
/********** subexpressionWithRange: **********/ /********** subexpressionWithRange: **********/
// Test with empty range // Test with empty range
subexpression = [testExpression subexpressionWithRange:NSMakeRange(4, 0)]; subexpression = [testExpression subexpressionWithRange:NSMakeRange(4, 0)];
XCTAssertEqual([subexpression numberOfSymbols], 0); XCTAssertEqual([subexpression numberOfElements], 0);
// Test with start and end in first symbol // Test with start and end in first element
subexpression = [testExpression subexpressionWithRange:NSMakeRange(1, 2)]; subexpression = [testExpression subexpressionWithRange:NSMakeRange(1, 2)];
XCTAssertEqual([subexpression numberOfSymbols], 1); XCTAssertEqual([subexpression numberOfElements], 1);
XCTAssertEqualObjects([subexpression symbolAtIndex:0], @"23"); XCTAssertEqualObjects([subexpression elementAtIndex:0], @"23");
// Test with start in first and end in middle after function // Test with start in first and end in middle after function
subexpression = [testExpression subexpressionWithRange:NSMakeRange(2, 3)]; subexpression = [testExpression subexpressionWithRange:NSMakeRange(2, 3)];
XCTAssertEqual([subexpression numberOfSymbols], 2); XCTAssertEqual([subexpression numberOfElements], 2);
XCTAssertEqualObjects([subexpression symbolAtIndex:0], @"34"); XCTAssertEqualObjects([subexpression elementAtIndex:0], @"34");
XCTAssertEqualObjects([subexpression symbolAtIndex:1], [[MPFunction alloc] init]); XCTAssertEqualObjects([subexpression elementAtIndex:1], [[MPFunction alloc] init]);
// Test with start in first and end in middle after literal // Test with start in first and end in middle after literal
subexpression = [testExpression subexpressionWithRange:NSMakeRange(2, 4)]; subexpression = [testExpression subexpressionWithRange:NSMakeRange(2, 4)];
XCTAssertEqual([subexpression numberOfSymbols], 3); XCTAssertEqual([subexpression numberOfElements], 3);
XCTAssertEqualObjects([subexpression symbolAtIndex:0], @"34"); XCTAssertEqualObjects([subexpression elementAtIndex:0], @"34");
XCTAssertEqualObjects([subexpression symbolAtIndex:2], @"1"); XCTAssertEqualObjects([subexpression elementAtIndex:2], @"1");
} }
- (void)testSubexpressionsIllegalRange { - (void)testSubexpressionsIllegalRange {
MPExpression *testExpression = [MPExpression expressionWithSymbols:@[@"1234", [[MPFunction alloc] init], @"17", [[MPFunction alloc] init]]]; MPExpression *testExpression = [[MPExpression alloc] initWithElements:@[@"1234", [[MPFunction alloc] init], @"17", [[MPFunction alloc] init]]];
// Test with start index beyond end // Test with start index beyond end
@try { @try {
[testExpression subexpressionFromIndex:10]; [testExpression subexpressionFromLocation:10];
XCTFail(@"Should have raised an exception."); XCTFail(@"Should have raised an exception.");
} }
@catch (NSException *exception) {} @catch (NSException *exception) {}
@@ -146,11 +146,11 @@
} }
- (void)testEqualExpressions { - (void)testEqualExpressions {
MPExpression *expression1 = [[MPExpression alloc] initWithString:@"123"]; MPExpression *expression1 = [[MPExpression alloc] initWithElement:@"123"];
MPExpression *expression2 = [[MPExpression alloc] initWithString:@"1234"]; MPExpression *expression2 = [[MPExpression alloc] initWithElement:@"1234"];
MPExpression *expression3 = [[MPExpression alloc] initWithFunction:[[MPFunction alloc] init]]; MPExpression *expression3 = [[MPExpression alloc] initWithElement:[[MPFunction alloc] init]];
MPExpression *expression4 = [[MPExpression alloc] initWithSymbols:@[[[MPFunction alloc] init], @"123"]]; MPExpression *expression4 = [[MPExpression alloc] initWithElements:@[[[MPFunction alloc] init], @"123"]];
MPExpression *expression5 = [[MPExpression alloc] initWithSymbols:@[[[MPFunction alloc] init], @"123"]]; MPExpression *expression5 = [[MPExpression alloc] initWithElements:@[[[MPFunction alloc] init], @"123"]];
XCTAssertNotEqualObjects(expression1, expression2); XCTAssertNotEqualObjects(expression1, expression2);
XCTAssertNotEqualObjects(expression1, expression3); XCTAssertNotEqualObjects(expression1, expression3);
@@ -159,68 +159,83 @@
XCTAssertEqualObjects(expression4, expression5); XCTAssertEqualObjects(expression4, expression5);
} }
- (void)testAppendSymbols {
MPExpression *expression1 = [[MPExpression alloc] initWithString:@"123"];
MPExpression *expression2 = [expression1 expressionByAppendingFunction:[[MPFunction alloc] init]];
MPExpression *expression3 = [[MPExpression alloc] initWithSymbols:@[@"123", [[MPFunction alloc] init]]];
XCTAssertEqualObjects(expression2, expression3);
}
- (void)testDescription { - (void)testDescription {
// Test Simple Expressions // Test Simple Expressions
MPExpression *testExpression = [[MPExpression alloc] initWithString:@"1234"]; MPExpression *testExpression = [[MPExpression alloc] initWithElement:@"1234"];
XCTAssertEqualObjects([testExpression description], @"1234"); XCTAssertEqualObjects([testExpression description], @"1234");
testExpression = [[MPExpression alloc] initWithFunction:[[MPFunction alloc] init]]; testExpression = [[MPExpression alloc] initWithElement:[[MPFunction alloc] init]];
XCTAssertEqualObjects([testExpression description], @"[]"); XCTAssertEqualObjects([testExpression description], @"[]");
// Test function after literal without explicit operator // Test function after literal without explicit operator
testExpression = [[MPExpression alloc] initWithSymbols:@[@"123", [[MPFunction alloc] init]]]; testExpression = [[MPExpression alloc] initWithElements:@[@"123", [[MPFunction alloc] init]]];
XCTAssertEqualObjects([testExpression description], @"123*[]"); XCTAssertEqualObjects([testExpression description], @"123*[]");
// Test function after literal with explicit operator // Test function after literal with explicit operator
testExpression = [[MPExpression alloc] initWithSymbols:@[@"123+", [[MPFunction alloc] init]]]; testExpression = [[MPExpression alloc] initWithElements:@[@"123+", [[MPFunction alloc] init]]];
XCTAssertEqualObjects([testExpression description], @"123+[]"); XCTAssertEqualObjects([testExpression description], @"123+[]");
// Test literal after function without explicit operator // Test literal after function without explicit operator
testExpression = [[MPExpression alloc] initWithSymbols:@[[[MPFunction alloc] init], @"123"]]; testExpression = [[MPExpression alloc] initWithElements:@[[[MPFunction alloc] init], @"123"]];
XCTAssertEqualObjects([testExpression description], @"[]*123"); XCTAssertEqualObjects([testExpression description], @"[]*123");
// Test literal after function with explicit operator // Test literal after function with explicit operator
testExpression = [[MPExpression alloc] initWithSymbols:@[[[MPFunction alloc] init], @"-123"]]; testExpression = [[MPExpression alloc] initWithElements:@[[[MPFunction alloc] init], @"-123"]];
XCTAssertEqualObjects([testExpression description], @"[]-123"); XCTAssertEqualObjects([testExpression description], @"[]-123");
// Test function after function without explicit operator // Test function after function without explicit operator
testExpression = [[MPExpression alloc] initWithSymbols:@[[[MPFunction alloc] init], [[MPFunction alloc] init]]]; testExpression = [[MPExpression alloc] initWithElements:@[[[MPFunction alloc] init], [[MPFunction alloc] init]]];
XCTAssertEqualObjects([testExpression description], @"[]*[]"); XCTAssertEqualObjects([testExpression description], @"[]*[]");
// Test function after function with explicit operator // Test function after function with explicit operator
testExpression = [[MPExpression alloc] initWithSymbols:@[[[MPFunction alloc] init], @"-", [[MPFunction alloc] init]]]; testExpression = [[MPExpression alloc] initWithElements:@[[[MPFunction alloc] init], @"-", [[MPFunction alloc] init]]];
XCTAssertEqualObjects([testExpression description], @"[]-[]"); XCTAssertEqualObjects([testExpression description], @"[]-[]");
// Test whitespaces in literal // Test whitespaces in literal
testExpression = [[MPExpression alloc] initWithSymbols:@[@" 123 + ", [[MPFunction alloc] init]]]; testExpression = [[MPExpression alloc] initWithElements:@[@" 123 + ", [[MPFunction alloc] init]]];
XCTAssertEqualObjects([testExpression description], @"123 +[]"); XCTAssertEqualObjects([testExpression description], @"123 +[]");
} }
- (void)testCopying { - (void)testCopying {
MPExpression *baseExpression = [[MPExpression alloc] initWithFunction:[[MPFunction alloc] init]]; MPExpression *baseExpression = [[MPExpression alloc] initWithElement:[[MPFunction alloc] init]];
MPExpression *copy = [baseExpression copy]; MPExpression *copy = [baseExpression copy];
XCTAssertEqual(baseExpression.numberOfElements, copy.numberOfElements);
XCTAssertNotEqual(copy, baseExpression); XCTAssertNotEqual(copy, baseExpression);
XCTAssertNotEqual([baseExpression symbolAtIndex:0], [copy symbolAtIndex:0]); XCTAssertNotEqual([baseExpression elementAtIndex:0], [copy elementAtIndex:0]);
XCTAssertEqualObjects(baseExpression, copy);
} }
- (void)testMutableCopying { - (void)testMutating {
MPExpression *baseExpression = [[MPExpression alloc] initWithString:@"123"]; MPExpression *testExpression = [[MPExpression alloc] initWithElements:@[@"1234", [[MPFunction alloc] init], @"5678", [[MPFunction alloc] init]]];
MPMutableExpression *expression1 = [baseExpression mutableCopy];
[expression1 appendFunction:[[MPFunction alloc] init]];
MPExpression *expression2 = [[MPExpression alloc] initWithSymbols:@[@"123", [[MPFunction alloc] init]]];
XCTAssertEqualObjects(expression1, expression2); [testExpression appendElement:@"90"];
XCTAssertNotEqualObjects(baseExpression, expression1); XCTAssertEqual([testExpression numberOfElements], 5);
// 1234 [] 5678 [] 90
[testExpression deleteElementsInRange:NSMakeRange(2, 4)];
XCTAssertEqual([testExpression numberOfElements], 3);
// 12678 [] 90
[testExpression insertElement:[[MPFunction alloc] init]
atLocation:2];
XCTAssertEqual([testExpression numberOfElements], 5);
// 12 [] 678 [] 90
[testExpression replaceElementsInRange:NSMakeRange(2, 5)
withElements:@[[[MPFunction alloc] init]]];
XCTAssertEqual([testExpression numberOfElements], 3);
// 12 [] 90
}
- (void)testInvalidMutatingRange {
@try {
MPExpression *testExpression = [[MPExpression alloc] initWithElements:@[@"1234", [[MPFunction alloc] init], @"5678", [[MPFunction alloc] init]]];
[testExpression deleteElementsInRange:NSMakeRange(5, 17)];
XCTFail(@"Should have raised an exception");
}
@catch (NSException *exception) {}
} }
// TODO: Test evaluating expressions // TODO: Test evaluating expressions