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 */
3B0F69A919028C6000817707 /* MPException.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B0F69A819028C6000817707 /* MPException.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 */; };
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 */; };
3B87E3591900857C00259938 /* MPExpression.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E3581900857C00259938 /* MPExpression.m */; };
3B87E36019009D5F00259938 /* MPFunction.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E35F19009D5F00259938 /* MPFunction.m */; };
3BB09EB21905DE500080A5ED /* MPExpressionStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB09EB11905DE500080A5ED /* MPExpressionStorage.m */; };
3BB09EC91906FD830080A5ED /* MPSumFunction.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB09EC81906FD830080A5ED /* MPSumFunction.m */; };
3BB09ED0190713F00080A5ED /* MPExpressionLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB09ECF190713F00080A5ED /* MPExpressionLayout.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 */; };
3BB09EDE190728220080A5ED /* NSIndexPath+MPAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB09EDD190728220080A5ED /* NSIndexPath+MPAdditions.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 */; };
3BBBA35F1903FD3600824E74 /* MPRangePath.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E35C1900933200259938 /* MPRangePath.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 */; };
3BF9979218DE623E009CF6C4 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BF9976E18DE623E009CF6C4 /* Cocoa.framework */; };
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 */
/* Begin PBXContainerItemProxy section */
@@ -54,10 +55,14 @@
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>"; };
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>"; };
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>"; };
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>"; };
@@ -66,18 +71,11 @@
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>"; };
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>"; };
3BB09ECF190713F00080A5ED /* MPExpressionLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPExpressionLayout.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>"; };
3BB09EDC190728220080A5ED /* NSIndexPath+MPAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSIndexPath+MPAdditions.h"; sourceTree = "<group>"; };
3BB09EDD190728220080A5ED /* NSIndexPath+MPAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSIndexPath+MPAdditions.m"; 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>"; };
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>"; };
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>"; };
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; };
@@ -99,8 +97,11 @@
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>"; };
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>"; };
3BFCFF481905AAF30001FE33 /* NSTextStorage+MPSetContents.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTextStorage+MPSetContents.m"; sourceTree = "<group>"; };
3BFAC38D1997B61300B3EF67 /* MPExpression.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPExpression.h; 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 */
/* Begin PBXFrameworksBuildPhase section */
@@ -127,14 +128,18 @@
3B87E350190082B300259938 /* Model */ = {
isa = PBXGroup;
children = (
3BBBA35A1903F89100824E74 /* Categories */,
3BBBA3591903EA9B00824E74 /* MPModel.h */,
3B87E3571900857C00259938 /* MPExpression.h */,
3B87E3581900857C00259938 /* MPExpression.m */,
3BFAC38D1997B61300B3EF67 /* MPExpression.h */,
3BFAC38E1997B61300B3EF67 /* MPExpression.m */,
3BFAC3961997B67400B3EF67 /* MPExpressionElement.h */,
3B87E35E19009D5F00259938 /* MPFunction.h */,
3B87E35F19009D5F00259938 /* MPFunction.m */,
3BFAC39A1997BC7600B3EF67 /* NSString+MPExpressionElement.h */,
3BFAC39B1997BC7600B3EF67 /* NSString+MPExpressionElement.m */,
3B87E35B1900933200259938 /* MPRangePath.h */,
3B87E35C1900933200259938 /* MPRangePath.m */,
3BB09EDC190728220080A5ED /* NSIndexPath+MPAdditions.h */,
3BB09EDD190728220080A5ED /* NSIndexPath+MPAdditions.m */,
3B0F69A719028BC600817707 /* MPException.h */,
3B0F69A819028C6000817707 /* MPException.m */,
3BB09EBC1905EF210080A5ED /* Functions */,
@@ -194,10 +199,12 @@
3BB09ED819071C270080A5ED /* Private */ = {
isa = PBXGroup;
children = (
3BB09ECE190713F00080A5ED /* MPExpressionLayout.h */,
3BB09ECF190713F00080A5ED /* MPExpressionLayout.m */,
3BB09ED1190713FC0080A5ED /* MPFunctionLayout.h */,
3BB09ED2190713FC0080A5ED /* MPFunctionLayout.m */,
3B528D0D199417740054DB5F /* MPLayout.h */,
3B688D9819982DF50006B4AB /* MPLayout.m */,
3B528D0E199417E10054DB5F /* MPExpressionLayout.h */,
3B528D0F199417E10054DB5F /* MPExpressionLayout.m */,
3B528D11199417E90054DB5F /* MPFunctionLayout.h */,
3B528D12199417E90054DB5F /* MPFunctionLayout.m */,
);
name = Private;
sourceTree = "<group>";
@@ -211,19 +218,6 @@
name = "Function Layouts";
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 = {
isa = PBXGroup;
children = (
@@ -292,7 +286,6 @@
isa = PBXGroup;
children = (
3B0F69AB1902A82C00817707 /* MPExpressionTests.m */,
3BBBA357190327B700824E74 /* MPMutableExpressionTests.m */,
3BBBA3941905704200824E74 /* MPRangeTests.m */,
3BF9979618DE623E009CF6C4 /* Supporting Files */,
);
@@ -408,17 +401,17 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
3B688D9919982DF50006B4AB /* MPLayout.m in Sources */,
3BB09EE1190736160080A5ED /* MPSumFunctionLayout.m in Sources */,
3B87E3561900856F00259938 /* MPExpressionView.m in Sources */,
3BFCFF491905AAF30001FE33 /* NSTextStorage+MPSetContents.m in Sources */,
3BB09EC91906FD830080A5ED /* MPSumFunction.m in Sources */,
3BB09EB21905DE500080A5ED /* MPExpressionStorage.m in Sources */,
3BF9977B18DE623E009CF6C4 /* main.m in Sources */,
3B87E3591900857C00259938 /* MPExpression.m in Sources */,
3BB09ED3190713FC0080A5ED /* MPFunctionLayout.m in Sources */,
3BB09ED0190713F00080A5ED /* MPExpressionLayout.m in Sources */,
3BBBA35D1903F8A700824E74 /* NSObject+MPStringTest.m in Sources */,
3BB09EDE190728220080A5ED /* NSIndexPath+MPReverseIndexPath.m in Sources */,
3BFAC38F1997B61300B3EF67 /* MPExpression.m in Sources */,
3BFAC39C1997BC7600B3EF67 /* NSString+MPExpressionElement.m in Sources */,
3B528D10199417E10054DB5F /* MPExpressionLayout.m in Sources */,
3B528D13199417E90054DB5F /* MPFunctionLayout.m in Sources */,
3BB09EDE190728220080A5ED /* NSIndexPath+MPAdditions.m in Sources */,
3B87E36019009D5F00259938 /* MPFunction.m in Sources */,
3B0F69A919028C6000817707 /* MPException.m in Sources */,
3BBBA35E1903FD3600824E74 /* MPRangePath.m in Sources */,
@@ -430,13 +423,14 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
3BBBA358190327B700824E74 /* MPMutableExpressionTests.m in Sources */,
3B528D1619941F5B0054DB5F /* MPExpressionLayout.m in Sources */,
3B528D1819941F5B0054DB5F /* MPFunctionLayout.m in Sources */,
3B0F69AE1902AD2800817707 /* MPException.m in Sources */,
3B0F69AC1902A82C00817707 /* MPExpressionTests.m in Sources */,
3BBBA3951905704200824E74 /* MPRangeTests.m in Sources */,
3B0F69B01902AD2E00817707 /* MPFunction.m in Sources */,
3B53AD5F1997E0FB00C925C4 /* MPExpression.m in Sources */,
3BBBA35F1903FD3600824E74 /* MPRangePath.m in Sources */,
3B0F69AD1902AD2200817707 /* MPExpression.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -1,5 +1,5 @@
<?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>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/>
</dependencies>
@@ -7,7 +7,6 @@
<customObject id="-2" userLabel="File's Owner" customClass="MPDocument">
<connections>
<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"/>
</connections>
</customObject>
@@ -17,22 +16,18 @@
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="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="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"/>
<view key="contentView" id="gIp-Ho-8D9">
<rect key="frame" x="0.0" y="0.0" width="507" height="240"/>
<autoresizingMask key="autoresizingMask"/>
<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"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</customView>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="lcd-Ip-jjR" customClass="MPExpressionView">
<rect key="frame" x="20" y="20" width="467" height="96"/>
<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"/>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="IMg-L0-qdu">
<rect key="frame" x="209" y="13" width="88" height="32"/>
<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">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
@@ -44,14 +39,12 @@
</button>
</subviews>
<constraints>
<constraint firstItem="lcd-Ip-jjR" firstAttribute="top" secondItem="fqc-IQ-ceJ" secondAttribute="bottom" constant="8" symbolic="YES" id="GTC-QI-4wc"/>
<constraint firstItem="fqc-IQ-ceJ" firstAttribute="trailing" secondItem="lcd-Ip-jjR" secondAttribute="trailing" id="Pth-4c-GNR"/>
<constraint firstAttribute="bottom" secondItem="lcd-Ip-jjR" secondAttribute="bottom" constant="20" symbolic="YES" id="VNk-bs-YOH"/>
<constraint firstAttribute="trailing" secondItem="fqc-IQ-ceJ" secondAttribute="trailing" constant="20" symbolic="YES" id="fER-QH-XqN"/>
<constraint firstItem="fqc-IQ-ceJ" firstAttribute="top" secondItem="gIp-Ho-8D9" secondAttribute="top" constant="20" symbolic="YES" id="kpD-MX-yOS"/>
<constraint firstItem="fqc-IQ-ceJ" firstAttribute="leading" secondItem="gIp-Ho-8D9" secondAttribute="leading" constant="20" symbolic="YES" id="to2-nb-CUV"/>
<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"/>
<constraint firstAttribute="trailing" secondItem="lcd-Ip-jjR" secondAttribute="trailing" constant="20" symbolic="YES" id="3tX-J3-Wte"/>
<constraint firstItem="IMg-L0-qdu" firstAttribute="top" secondItem="lcd-Ip-jjR" secondAttribute="bottom" constant="83" id="62S-rU-IWO"/>
<constraint firstItem="lcd-Ip-jjR" firstAttribute="leading" secondItem="gIp-Ho-8D9" secondAttribute="leading" constant="20" symbolic="YES" id="XN3-k3-tOU"/>
<constraint firstItem="lcd-Ip-jjR" firstAttribute="top" secondItem="gIp-Ho-8D9" secondAttribute="top" constant="20" symbolic="YES" id="gqS-BG-xpS"/>
<constraint firstItem="IMg-L0-qdu" firstAttribute="centerX" secondItem="lcd-Ip-jjR" secondAttribute="centerX" id="sCM-Pj-4wd"/>
<constraint firstAttribute="bottom" secondItem="IMg-L0-qdu" secondAttribute="bottom" constant="20" symbolic="YES" id="tNe-R6-QlG"/>
</constraints>
</view>
<connections>

View File

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

View File

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

View File

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

View File

@@ -2,124 +2,82 @@
// MPExpression.h
// 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.
//
@import Foundation;
#import "NSString+MPExpressionElement.h"
@class MPExpression, MPFunction, MPRangePath;
@protocol MPExpressionElement;
extern NSString *MPAdditionOperator;
extern NSString *MPSubtractionOperator;
extern NSString *MPMultiplicationOperator;
extern NSString *MPDivisionOperator;
@interface MPExpression : NSObject <NSCopying, NSMutableCopying, NSCoding>
@interface MPExpression : NSObject <NSCopying, NSCoding>
#pragma mark Creation Methods
- (instancetype)initWithSymbols:(NSArray *)symbols;
- (instancetype)init; // Convenience
- (instancetype)initWithElement:(id<MPExpressionElement>)element; // Convenience
- (instancetype)initWithElements:(NSArray *)elements; // Designated Initializer
#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)fixSymbols;
- (void)fixElements; // Called automatically, removes empty elements, joins subsequent strings
#pragma mark Primitive Methods
- (NSUInteger)numberOfSymbols;
- (id)symbolAtIndex:(NSUInteger)index; // Either an NSString or a MPFunction (which can be mutated)
- (NSUInteger)length;
- (NSUInteger)numberOfElements;
- (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
#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
@interface MPExpression (MPExpressionExtensionMethods)
+ (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;
@interface MPExpression (MPExpressionExtension)
#pragma mark Working With the Expression Tree
- (NSUInteger)indexOfSymbol:(id)symbol;
- (id)symbolAtIndexPath:(NSIndexPath *)indexPath; // May also return MPExpression
- (id)elementAtIndexPath:(NSIndexPath *)indexPath; // Returns an MPExpression or id<MPExpressionElement>
- (NSArray *)elementsInRangePath:(MPRangePath *)rangePath;
#pragma mark Working With Expressions
- (NSUInteger)length;
- (MPExpression *)subexpressionFromIndex:(NSUInteger)from;
- (MPExpression *)subexpressionToIndex:(NSUInteger)to;
- (MPExpression *)subexpressionFromLocation:(NSUInteger)from;
- (MPExpression *)subexpressionToLocation:(NSUInteger)to;
- (MPExpression *)subexpressionWithRange:(NSRange)range;
- (BOOL)isEqualToExpression:(MPExpression *)anExpression;
- (MPExpression *)expressionByAppendingString:(NSString *)aString;
- (MPExpression *)expressionByAppendingFunction:(MPFunction *)aFunction;
- (MPExpression *)expressionByAppendingExpression:(MPExpression *)anExpression;
- (MPExpression *)expressionByAppendingSymbols:(NSArray *)symbols;
#pragma mark Mutating Expressions
- (void)appendElement:(id<MPExpressionElement>)anElement;
- (void)appendElements:(NSArray *)elements;
- (void)insertElement:(id<MPExpressionElement>)anElement atLocation:(NSUInteger)index;
- (void)insertElements:(NSArray *)elements atLocation:(NSUInteger)index;
- (void)deleteElementsInRange:(NSRange)range;
#pragma mark Evaluating Expressions
- (float)floatValue;
- (int)intValue;
- (NSInteger)integerValue;
- (long long)longLongValue;
#pragma mark Querying an Expression's Contents
- (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;
#pragma mark Querying Expressions
- (NSArray *)elements;
@end

View File

@@ -2,88 +2,228 @@
// MPExpression.m
// 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.
//
#import "MPExpression.h"
#import "MPFunction.h"
#import "MPException.h"
#import "MPRangePath.h"
#import "NSObject+MPStringTest.h"
#import "NSIndexPath+MPReverseIndexPath.h"
#import "NSIndexPath+MPAdditions.h"
#import "MPException.h"
NSString *MPAdditionOperator = @"+";
NSString *MPSubtractionOperator = @"-";
NSString *MPMultiplicationOperator = @"*";
NSString *MPDivisionOperator = @"/";
@interface MPExpression ()
@property (readonly, nonatomic, strong) NSMutableArray *elements;
@end
@interface MPExpression (MPExpressionPrivate)
- (NSInteger)lengthOfSymbol:(id)symbol;
- (void)validateSymbols:(NSArray *)symbols;
- (void)getSplitOffset:(out NSUInteger *)offset
inSymbolAtIndex:(out NSUInteger *)symbolIndex
forSplitLocation:(NSUInteger)loc;
- (void)validateElements:(NSArray *)elements;
- (BOOL)splitElementsAtLocation:(NSUInteger)location
insertionIndex:(out NSUInteger *)insertionIndex;
- (NSUInteger)calculateSplitOffsetForSplitLocation:(NSUInteger)location
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
@implementation MPExpression {
@package
__strong NSArray *_symbols;
NSInteger _length;
NSUInteger _cachedLength;
NSRange editedRange;
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];
if (self) {
_symbols = [[NSArray alloc] initWithArray:symbols
_cachedLength = 0;
_elements = [[NSMutableArray alloc] initWithCapacity:elements.count];
_elements = [[NSMutableArray alloc] initWithArray:elements
copyItems:YES];
[self fixSymbols];
[self fixElements];
}
return self;
}
#pragma mark Working With the Expression Tree
- (void)fixSymbols
- (void)fixElements
{
NSMutableArray *mutableSymbols = [_symbols mutableCopy];
for (NSInteger index = 0; index < mutableSymbols.count; index++) {
id next = index+1 < mutableSymbols.count ? mutableSymbols[index+1] : nil;
id current = mutableSymbols[index];
for (NSUInteger index = 0; index < self.elements.count; index++) {
id<MPExpressionElement> next = index+1 < self.elements.count ? self.elements[index+1] :nil;
id<MPExpressionElement> current = self.elements[index];
if ([current isString]) {
if ([current length] == 0) {
[mutableSymbols removeObjectAtIndex:index];
index--;
if (current.length == 0) {
[self.elements 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];
index--;
[self.elements replaceObjectsInRange:NSMakeRange(index, 2)
withObjectsFromArray:@[new]];
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];
}
}
_symbols = [mutableSymbols copy];
}
#pragma mark Primitive Methods
- (NSUInteger)numberOfSymbols
- (NSUInteger)length
{
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
@@ -92,218 +232,18 @@ NSString *MPDivisionOperator = @"/";
return 0;
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone
#pragma mark Notifications
- (void)didChangeElementsInRangePath:(MPRangePath *)rangePath
replacementLength:(NSUInteger)replacementLength
{
MPExpression *copy = [[MPExpression allocWithZone:zone] initWithSymbols:_symbols];
return copy;
NSUInteger selfIndex = [self.parent indexOfChild:self];
MPRangePath *newPath = rangePath.copy;
newPath.location = [newPath.location indexPathByPreceedingIndex:selfIndex];
[self.parent didChangeElementsInRangePath:newPath
replacementLength:replacementLength];
}
#pragma mark - NSMutableCopying
- (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];
}
#pragma mark Basic NSObject Methods
- (BOOL)isEqual:(id)object
{
@@ -321,32 +261,141 @@ NSString *MPDivisionOperator = @"/";
- (BOOL)isEqualToExpression:(MPExpression *)anExpression
{
// TODO: Use ->_symbols or .symbols
return [_symbols isEqualToArray:anExpression->_symbols];
return [self.elements isEqualToArray:anExpression.elements];
}
- (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
- (float)floatValue
{
return (float)[self doubleValue];
@@ -367,290 +416,4 @@ NSString *MPDivisionOperator = @"/";
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

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
// 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.
//
#import <Foundation/Foundation.h>
@import Cocoa;
#import "MPLayout.h"
#import "MPExpression.h"
@class MPExpressionLayout, MPFunctionLayout, MPExpressionStorage, MPExpressionView, MPExpression;
@interface MPExpressionLayout : MPLayout
@interface MPExpressionLayout : NSObject {
BOOL _valid;
NSSize _cachedSize;
NSMutableArray *_symbolCache;
}
- (instancetype)initRootLayoutWithExpressionStorage:(MPExpressionStorage *)expressionStorage;
#pragma mark Creation Methods
// -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;
@property (readonly, nonatomic, weak) MPExpression *expression; // Convenience
@end

View File

@@ -2,45 +2,69 @@
// MPExpressionLayout.m
// 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.
//
#import "MPExpressionLayout.h"
#import "MPExpressionStorage.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
#pragma mark Creation Methods
# pragma mark Creation Methods
- (instancetype)initRootLayoutWithExpressionStorage:(MPExpressionStorage *)expressionStorage
{
self = [super init];
if (self) {
_symbolCache = [[NSMutableArray alloc] init];
_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;
}
#pragma mark Properties
@synthesize expressionStorage = _expressionStorage;
- (MPExpressionStorage *)expressionStorage
{
@@ -52,172 +76,50 @@
- (MPExpression *)expression
{
return [self.expressionStorage symbolAtIndexPath:self.expressionPath];
}
- (NSLayoutManager *)layoutManager
{
return self.expressionStorage.layoutManager;
}
- (NSTextContainer *)textContainer
{
return self.expressionStorage.textContainer;
}
- (NSTextStorage *)textStorage
{
return self.expressionStorage.textStorage;
return [self.expressionStorage elementAtIndexPath:self.path];
}
#pragma mark Cache Methods
// TODO: Return nil from caching with illegal index
- (void)invalidate
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index
{
_valid = NO;
[self.parent invalidate];
[self.expressionView setNeedsDisplay:YES];
}
- (void)editedExpressionInRange:(NSRange)range replacementLength:(NSUInteger)length
{
// TODO: New symbols may also be inserted in the middle or at the beginning
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]]) {
id cachedObject = [self cachableObjectForIndex:index generator:^id{
NSIndexPath *indexPath = [self.path indexPathByAddingIndex:index];
MPFunctionLayout *layout = [MPFunctionLayout functionLayoutForFunctionAtIndexPath:indexPath
parent:self];
return layout;
}];
if ([cachedObject isKindOfClass:[NSBezierPath 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;
return cachedObject;
}
- (NSSize)cachedSizeForSymbolAtIndex:(NSUInteger)index
- (NSSize)sizeForChildAtIndex:(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];
id symbol = [self.expression elementAtIndex:index];
if ([symbol isString]) {
[self.textStorage setString:symbol];
NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer];
NSSize symbolSize = [self.layoutManager boundingRectForGlyphRange:glyphRange
inTextContainer:self.textContainer].size;
[self cacheSize:symbolSize
forSymbolAtIndex:index];
return symbolSize;
return [self bezierPathForChildAtIndex:index].bounds.size;
} else {
MPFunctionLayout *layout = [self functionLayoutForFunctionAtIndex:index];
return [layout sizeOfFunction];
return [self childLayoutAtIndex:index].size;
}
}
#pragma mark Drawing Methods
- (void)drawSymbolAtIndex:(NSUInteger)index
atPoint:(NSPoint)point
- (NSBezierPath *)generateBezierPath
{
id symbol = [self.expression symbolAtIndex:index];
// point.x = point.y = 0;
NSLog(@"draw Symbol: %@ at Point: (x: %f, y: %f)", symbol, point.x, point.y);
if ([symbol isString]) {
[self.textStorage setString:symbol];
NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer];
[self.layoutManager drawGlyphsForGlyphRange:glyphRange
atPoint:point];
} else {
MPFunctionLayout *layout = [self functionLayoutForFunctionAtIndex:index];
NSLog(@"layout: %@, index: %ld", layout, index);
[layout drawFunctionAtPoint:point];
NSBezierPath *fullPath = [NSBezierPath bezierPath];
[fullPath moveToPoint:NSZeroPoint];
NSUInteger x = 0;
for (NSInteger index = 0; index < self.expression.numberOfElements; ++index) {
NSAffineTransform *transform = [NSAffineTransform transform];
// TODO: Translate by the right amount
[transform translateXBy:x yBy:0];
NSBezierPath *path = [self bezierPathForChildAtIndex:index].copy;
[path transformUsingAffineTransform:transform];
[fullPath appendBezierPath:path];
x += path.bounds.size.width;
}
}
- (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];
return fullPath;
}
@end

View File

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

View File

@@ -19,11 +19,11 @@
@implementation MPExpressionStorage
- (instancetype)initWithSymbols:(NSArray *)symbols
- (instancetype)initWithElements:(NSArray *)elements
{
self = [super initWithSymbols:symbols];
self = [super initWithElements:elements];
if (self) {
_expressionLayout = [[MPExpressionLayout alloc] initRootLayoutWithExpressionStorage:self];
_rootLayout = [[MPExpressionLayout alloc] initRootLayoutWithExpressionStorage: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) {
return;
}
id current = self.expressionLayout;
for (NSUInteger position = 1; position < rangePath.location.length-1; position++) {
if ([current isKindOfClass:[MPExpressionLayout class]]) {
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]];
MPLayout *current = self.rootLayout;
for (NSUInteger index = 1; index < rangePath.location.length-1; index++) {
current = [current childLayoutAtIndex:index];
}
[current clearCacheInRange:rangePath.rangeAtLastIndex replacementLength:replacementLength];
}
@end

View File

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

View File

@@ -7,13 +7,15 @@
//
#import "MPExpressionView.h"
#import "MPExpressionLayout.h"
#import "MPExpressionStorage.h"
#import "NSObject+MPStringTest.h"
#import "MPExpressionLayout.h"
#import "MPSumFunction.h"
@interface MPExpressionView (MPCursor)
@property (nonatomic, strong) NSTimer *cursorTimer;
@end
@implementation MPExpressionView
#pragma mark Creation Methods
@@ -47,23 +49,20 @@
- (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;
[self.expressionLayout setExpressionView:self];
}
#pragma mark Properties
- (void)setExpressionStorage:(MPExpressionStorage *)expressionStorage
- (BOOL)isFlipped
{
[_expressionStorage.expressionLayout setExpressionView:nil];
_expressionStorage = expressionStorage;
[_expressionStorage.expressionLayout setExpressionView:self];
return NO;
}
- (MPExpressionLayout *)expressionLayout
- (void)setExpressionStorage:(MPExpressionStorage *)expressionStorage
{
return self.expressionStorage.expressionLayout;
_expressionStorage = expressionStorage;
}
#pragma mark Drawing Methods
@@ -74,11 +73,10 @@
[[NSColor whiteColor] set];
NSRectFill(self.bounds);
[[NSColor blackColor] set];
NSSize expressionSize = [self.expressionLayout sizeForAllSymbols];
NSSize expressionSize = [self.expressionStorage.rootLayout size];
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);
[self.expressionLayout drawAllSymbolsAtPoint:point];
[self.expressionStorage.rootLayout drawAtPoint:point];
}
@end

View File

@@ -6,58 +6,47 @@
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
@import Foundation;
#import "MPExpressionElement.h"
@class MPFunction, MPExpression, MPRangePath;
@interface MPFunction : NSObject <NSCopying, NSCoding>
@interface MPFunction : NSObject <NSCoding, NSCopying, MPExpressionElement>
#pragma mark Creation Methods
- (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
- (NSUInteger)numberOfChildren;
- (MPExpression *)childAtIndex:(NSUInteger)index;
- (NSUInteger)numberOfChildren; // Override
- (MPExpression *)childAtIndex:(NSUInteger)index; // Override
- (void)setChild:(MPExpression *)child
atIndex:(NSUInteger)index;
#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
atIndex:(NSUInteger)index; // Override
// May be overridden for performance improvements
- (NSArray *)children;
- (NSArray *)children; // Indexes must equal the ones from the native methods
- (NSUInteger)indexOfChild:(MPExpression *)child;
- (id)symbolAtIndexPath:(NSIndexPath *)indexPath;
- (id)elementAtIndexPath:(NSIndexPath *)indexPath;
#pragma mark Evaluating Functions
- (double)doubleValue; // Override
- (float)floatValue;
- (int)intValue;
- (NSInteger)integerValue;
- (long long)longLongValue;
#pragma mark Messages
- (void)didChangeElementsInRangePath:(MPRangePath *)rangePath
replacementLength:(NSUInteger)replacementLength;
- (void)didChangeChild:(MPExpression *)child;
- (void)didChangeChildAtIndex:(NSUInteger)index;
- (NSString *)description;
#pragma mark Working With Functions
- (BOOL)isEqualToFunction:(MPFunction *)aFunction; // Override
- (NSUInteger)hash;
@end
@interface MPFunction (MPDisplayExtension)
- (void)symbolsChangedInRangePath:(MPRangePath *)rangePath replacementLength:(NSUInteger)length;
- (NSString *)description; // Should be overridden
- (NSUInteger)hash;// Override
@end

View File

@@ -10,12 +10,11 @@
#import "MPExpression.h"
#import "MPRangePath.h"
#import "NSIndexPath+MPReverseIndexPath.h"
#import "NSIndexPath+MPAdditions.h"
@implementation MPFunction
#pragma mark Creation Methods
- (instancetype)init
{
self = [super init];
@@ -25,7 +24,6 @@
}
#pragma mark Working With the Expression Tree
- (NSUInteger)numberOfChildren
{
return 0;
@@ -38,63 +36,10 @@
- (void)setChild:(MPExpression *)child
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
{
NSUInteger childCount = [self numberOfChildren];
@@ -116,16 +61,115 @@
return NSNotFound;
}
- (id)symbolAtIndexPath:(NSIndexPath *)indexPath
- (id)elementAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.length == 0) {
return self;
}
MPExpression *child = [self childAtIndex:[indexPath indexAtPosition:0]];
return [child symbolAtIndexPath:[indexPath indexPathByRemovingFirstIndex]];
return [child elementAtIndexPath:[indexPath indexPathByRemovingFirstIndex]];
}
#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
{
@@ -147,26 +191,4 @@
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

View File

@@ -2,56 +2,30 @@
// MPFunctionLayout.h
// 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.
//
#import <Foundation/Foundation.h>
@import Cocoa;
#import "MPLayout.h"
@class MPFunctionLayout, MPExpressionLayout, MPExpressionStorage, MPFunction;
@interface MPFunctionLayout : MPLayout
@interface MPFunctionLayout : NSObject {
@protected
BOOL _valid;
NSSize _cachedSize;
NSMutableArray *_childCache;
}
#pragma mark Creation Methods
+ (instancetype)functionLayoutForFunctionAtIndexPath:(NSIndexPath *)functionPath
+ (MPFunctionLayout *)functionLayoutForFunctionAtIndexPath:(NSIndexPath *)path
parent:(MPExpressionLayout *)parent;
- (id)initWithFunctionPath:(NSIndexPath *)functionPath
parent:(MPExpressionLayout *)parent;
#pragma mark Properties
@property (readonly, nonatomic, weak) MPExpressionLayout *parent;
@property (readonly, nonatomic, strong) NSIndexPath *functionPath;
- (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;
@property (readonly, nonatomic, weak) MPFunction *function; // Convenience
@end
@interface MPFunctionLayout (MPSubclassOverride)
// Should also implement accessor method for special function type:
// - (MPCustomFunction *)customFunction
// {
// return (MPCustomFunction *)self.function;
// }
- (NSBezierPath *)generateBezierPath;
@end

View File

@@ -2,14 +2,13 @@
// MPFunctionLayout.m
// 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.
//
#import "MPFunctionLayout.h"
#import "MPExpressionLayout.h"
#import "MPExpressionStorage.h"
#import "MPFunction.h"
#import "MPExpressionLayout.h"
#import "MPSumFunction.h"
#import "MPSumFunctionLayout.h"
@@ -17,114 +16,43 @@
@implementation MPFunctionLayout
#pragma mark Creation Methods
+ (instancetype)functionLayoutForFunctionAtIndexPath:(NSIndexPath *)functionPath
+ (MPFunctionLayout *)functionLayoutForFunctionAtIndexPath:(NSIndexPath *)path
parent:(MPExpressionLayout *)parent
{
MPFunction *function = [parent.expressionStorage symbolAtIndexPath:functionPath];
MPFunction *function = [parent.expressionStorage elementAtIndexPath:path];
Class class = [function class];
if (class == [MPSumFunction class]) {
return [[MPSumFunctionLayout alloc] initWithFunctionPath:functionPath parent:parent];
return [[MPSumFunctionLayout alloc] initWithPath:path parent:parent];
}
return nil;
}
- (id)initWithFunctionPath:(NSIndexPath *)functionPath
parent:(MPExpressionLayout *)parent
{
self = [super init];
if (self) {
_functionPath = functionPath;
_parent = parent;
_childCache = [[NSMutableArray alloc] init];
}
return self;
return [[self alloc] initWithPath:path parent:parent];
}
#pragma mark Properties
- (MPExpressionStorage *)expressionStorage
{
return self.parent.expressionStorage;
}
- (MPFunction *)function
{
return [self.expressionStorage symbolAtIndexPath:self.functionPath];
}
- (NSLayoutManager *)layoutManager
{
return self.expressionStorage.layoutManager;
}
- (NSTextContainer *)textContainer
{
return self.expressionStorage.textContainer;
}
- (NSTextStorage *)textStorage
{
return self.expressionStorage.textStorage;
return [self.expressionStorage elementAtIndexPath:self.path];
}
#pragma mark Cache Methods
- (void)invalidate
- (MPLayout *)childLayoutAtIndex:(NSUInteger)index
{
_valid = NO;
[self.parent invalidate];
return [self cachableObjectForIndex:index generator:^id{
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]) {
_childCache[index] = [NSNull null];
}
[self invalidate];
MPLayout *childLayout = [self childLayoutAtIndex:index];
return [childLayout size];
}
- (BOOL)hasCacheForChildAtIndex:(NSUInteger)index
{
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
- (NSBezierPath *)generateBezierPath
{
return [NSBezierPath bezierPath];
}
@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 "MPRangePath.h"
#import "NSObject+MPStringTest.h"
#import "NSTextStorage+MPSetContents.h"
#import "NSIndexPath+MPReverseIndexPath.h"
#import "NSIndexPath+MPAdditions.h"
#endif

View File

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

View File

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

View File

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

View File

@@ -12,7 +12,6 @@
@implementation MPSumFunction
#pragma mark Creation Methods
- (instancetype)init
{
self = [super init];
@@ -27,8 +26,32 @@
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
{
return 3;
@@ -48,36 +71,32 @@
}
}
- (NSArray *)children
{
return @[self.startExpression, self.targetExpression, self.sumExpression];
}
- (void)setChild:(MPExpression *)child
atIndex:(NSUInteger)index
{
switch (index) {
// TODO: Copy child?
case 0:
self.startExpression.parent = nil;
self.startExpression = child;
break;
case 1:
self.targetExpression.parent = nil;
self.targetExpression = child;
break;
case 2:
self.sumExpression.parent = nil;
self.sumExpression = child;
break;
default:
return;
}
child.parent = self;
[self didChangeChildAtIndex:index];
}
- (NSArray *)children
{
return @[self.startExpression, self.targetExpression, self.sumExpression];
}
#pragma mark Evaluating Functions
- (double)doubleValue
{
#warning Implementation
@@ -85,7 +104,6 @@
}
#pragma mark Working With Functions
- (BOOL)isEqualToFunction:(MPFunction *)aFunction
{
if (![aFunction isKindOfClass:[MPSumFunction class]]) {
@@ -106,8 +124,13 @@
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
{
MPSumFunction *copy = [[MPSumFunction allocWithZone:zone] init];

View File

@@ -16,20 +16,21 @@
return (MPSumFunction *)self.function;
}
- (NSSize)calculateSize
- (NSBezierPath *)generateBezierPath
{
NSAttributedString *text = [[NSAttributedString alloc] initWithString:@"∑" attributes:@{NSFontAttributeName: [NSFont fontWithName:@"HelveticaNeue" size:50.0]}];
[self.textStorage setAttributedString:text];
NSAttributedString *text = [[NSAttributedString alloc] initWithString:@"∑"
attributes:@{NSFontAttributeName: [NSFont fontWithName:@"Lucida Grande" size:18.0]}];
self.textStorage.attributedString = text;
NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer];
return [self.layoutManager boundingRectForGlyphRange:glyphRange inTextContainer:self.textContainer].size;
}
- (void)drawFunctionAtPoint:(NSPoint)point
{
NSAttributedString *text = [[NSAttributedString alloc] initWithString:@"∑" attributes:@{NSFontAttributeName: [NSFont fontWithName:@"HelveticaNeue" size:50.0]}];
[self.textStorage setAttributedString:text];
NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer];
[self.layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:point];
NSGlyph glyphs[glyphRange.length+1];
NSUInteger actualGylphCount = [self.layoutManager getGlyphs:glyphs
range:glyphRange];
NSBezierPath *path = [NSBezierPath bezierPath];
[path moveToPoint:NSZeroPoint];
[path appendBezierPathWithGlyphs:glyphs
count:actualGylphCount
inFont:[NSFont fontWithName:@"Lucida Grande" size:18.0]];
return path;
}
@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 {
// Test empty expression
MPExpression *testExpression = [[MPExpression alloc] init];
XCTAssertEqual([testExpression numberOfSymbols], 0);
XCTAssertEqual(testExpression.numberOfElements, 0);
// Test expression with string
testExpression = [[MPExpression alloc] initWithString:@"1234+5678"];
XCTAssertEqual([testExpression numberOfSymbols], 1);
XCTAssertEqualObjects([testExpression symbolAtIndex:0], @"1234+5678");
testExpression = [[MPExpression alloc] initWithElement:@"1234+5678"];
XCTAssertEqual(testExpression.numberOfElements, 1);
XCTAssertEqualObjects([testExpression elementAtIndex:0], @"1234+5678");
// Test expression with function
testExpression = [[MPExpression alloc] initWithFunction:[[MPFunction alloc] init]];
XCTAssertEqual([testExpression numberOfSymbols], 1);
XCTAssertEqualObjects([testExpression symbolAtIndex:0], [[MPFunction alloc] init]);
testExpression = [[MPExpression alloc] initWithElement:[[MPFunction alloc] init]];
XCTAssertEqual([testExpression numberOfElements], 1);
XCTAssertEqualObjects([testExpression elementAtIndex:0], [[MPFunction alloc] init]);
testExpression = [[MPExpression alloc] initWithSymbols:@[@"1234", [[MPFunction alloc] init], @"17", [[MPFunction alloc] init]]];
XCTAssertEqual([testExpression numberOfSymbols], 4);
XCTAssertEqualObjects([testExpression symbolAtIndex:2], @"17");
testExpression = [[MPExpression alloc] initWithElements:@[@"1234", [[MPFunction alloc] init], @"17", [[MPFunction alloc] init]]];
XCTAssertEqual([testExpression numberOfElements], 4);
XCTAssertEqualObjects([testExpression elementAtIndex:2], @"17");
// Test expression with subsequent strings
testExpression = [[MPExpression alloc] initWithSymbols:@[@"1234", @"5678"]];
XCTAssertEqual([testExpression numberOfSymbols], 1);
XCTAssertEqualObjects([testExpression symbolAtIndex:0], @"12345678");
testExpression = [[MPExpression alloc] initWithElements:@[@"1234", @"5678"]];
XCTAssertEqual([testExpression numberOfElements], 1);
XCTAssertEqualObjects([testExpression elementAtIndex:0], @"12345678");
// Test expression with only empty string
testExpression = [[MPExpression alloc] initWithString:@""];
XCTAssertEqual([testExpression numberOfSymbols], 0);
testExpression = [[MPExpression alloc] initWithElement:@""];
XCTAssertEqual([testExpression numberOfElements], 0);
}
- (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: **********/
// Test with start index at front
MPExpression *subexpression = [testExpression subexpressionFromIndex:0];
XCTAssertEqual([subexpression numberOfSymbols], 4);
XCTAssertEqualObjects([subexpression symbolAtIndex:0], @"1234");
MPExpression *subexpression = [testExpression subexpressionFromLocation:0];
XCTAssertEqual([subexpression numberOfElements], 4);
XCTAssertEqualObjects([subexpression elementAtIndex:0], @"1234");
// Test with start index in first symbol
subexpression = [testExpression subexpressionFromIndex:2];
XCTAssertEqual([subexpression numberOfSymbols], 4);
XCTAssertEqualObjects([subexpression symbolAtIndex:0], @"34");
// Test with start index in first element
subexpression = [testExpression subexpressionFromLocation:2];
XCTAssertEqual([subexpression numberOfElements], 4);
XCTAssertEqualObjects([subexpression elementAtIndex:0], @"34");
// Test with start index in middle symbol starting with a literal
subexpression = [testExpression subexpressionFromIndex:6];
XCTAssertEqual([subexpression numberOfSymbols], 2);
XCTAssertEqualObjects([subexpression symbolAtIndex:0], @"7");
// Test with start index in middle element starting with a literal
subexpression = [testExpression subexpressionFromLocation:6];
XCTAssertEqual([subexpression numberOfElements], 2);
XCTAssertEqualObjects([subexpression elementAtIndex:0], @"7");
// Test with start index in middle symbol starting with a function
subexpression = [testExpression subexpressionFromIndex:4];
XCTAssertEqual([subexpression numberOfSymbols], 3);
XCTAssertEqualObjects([subexpression symbolAtIndex:0], [[MPFunction alloc] init]);
// Test with start index in middle element starting with a function
subexpression = [testExpression subexpressionFromLocation:4];
XCTAssertEqual([subexpression numberOfElements], 3);
XCTAssertEqualObjects([subexpression elementAtIndex:0], [[MPFunction alloc] init]);
// Test with start index in last symbol
subexpression = [testExpression subexpressionFromIndex:7];
XCTAssertEqual([subexpression numberOfSymbols], 1);
XCTAssertEqualObjects([subexpression symbolAtIndex:0], [[MPFunction alloc] init]);
// Test with start index in last element
subexpression = [testExpression subexpressionFromLocation:7];
XCTAssertEqual([subexpression numberOfElements], 1);
XCTAssertEqualObjects([subexpression elementAtIndex:0], [[MPFunction alloc] init]);
// Test with start index at end
subexpression = [testExpression subexpressionFromIndex:8];
XCTAssertEqual([subexpression numberOfSymbols], 0);
subexpression = [testExpression subexpressionFromLocation:8];
XCTAssertEqual([subexpression numberOfElements], 0);
/********** subexpressionToIndex: **********/
// Test with end index at front
subexpression = [testExpression subexpressionToIndex:0];
XCTAssertEqual([subexpression numberOfSymbols], 0);
subexpression = [testExpression subexpressionToLocation:0];
XCTAssertEqual([subexpression numberOfElements], 0);
// Test with end index in first symbol
subexpression = [testExpression subexpressionToIndex:2];
XCTAssertEqual([subexpression numberOfSymbols], 1);
XCTAssertEqualObjects([subexpression symbolAtIndex:0], @"12");
// Test with end index in first Element
subexpression = [testExpression subexpressionToLocation:2];
XCTAssertEqual([subexpression numberOfElements], 1);
XCTAssertEqualObjects([subexpression elementAtIndex:0], @"12");
// Test with end index in middle symbol ending with a literal
subexpression = [testExpression subexpressionToIndex:6];
XCTAssertEqual([subexpression numberOfSymbols], 3);
XCTAssertEqualObjects([subexpression symbolAtIndex:2], @"1");
// Test with end index in middle Element ending with a literal
subexpression = [testExpression subexpressionToLocation:6];
XCTAssertEqual([subexpression numberOfElements], 3);
XCTAssertEqualObjects([subexpression elementAtIndex:2], @"1");
// Test with end index in middle symbol ending with a function
subexpression = [testExpression subexpressionToIndex:5];
XCTAssertEqual([subexpression numberOfSymbols], 2);
XCTAssertEqualObjects([subexpression symbolAtIndex:1], [[MPFunction alloc] init]);
// Test with end index in middle Element ending with a function
subexpression = [testExpression subexpressionToLocation:5];
XCTAssertEqual([subexpression numberOfElements], 2);
XCTAssertEqualObjects([subexpression elementAtIndex:1], [[MPFunction alloc] init]);
// Test with end index at end
subexpression = [testExpression subexpressionToIndex:8];
XCTAssertEqual([subexpression numberOfSymbols], 4);
XCTAssertEqualObjects([subexpression symbolAtIndex:3], [[MPFunction alloc] init]);
subexpression = [testExpression subexpressionToLocation:8];
XCTAssertEqual([subexpression numberOfElements], 4);
XCTAssertEqualObjects([subexpression elementAtIndex:3], [[MPFunction alloc] init]);
/********** subexpressionWithRange: **********/
// Test with empty range
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)];
XCTAssertEqual([subexpression numberOfSymbols], 1);
XCTAssertEqualObjects([subexpression symbolAtIndex:0], @"23");
XCTAssertEqual([subexpression numberOfElements], 1);
XCTAssertEqualObjects([subexpression elementAtIndex:0], @"23");
// Test with start in first and end in middle after function
subexpression = [testExpression subexpressionWithRange:NSMakeRange(2, 3)];
XCTAssertEqual([subexpression numberOfSymbols], 2);
XCTAssertEqualObjects([subexpression symbolAtIndex:0], @"34");
XCTAssertEqualObjects([subexpression symbolAtIndex:1], [[MPFunction alloc] init]);
XCTAssertEqual([subexpression numberOfElements], 2);
XCTAssertEqualObjects([subexpression elementAtIndex:0], @"34");
XCTAssertEqualObjects([subexpression elementAtIndex:1], [[MPFunction alloc] init]);
// Test with start in first and end in middle after literal
subexpression = [testExpression subexpressionWithRange:NSMakeRange(2, 4)];
XCTAssertEqual([subexpression numberOfSymbols], 3);
XCTAssertEqualObjects([subexpression symbolAtIndex:0], @"34");
XCTAssertEqualObjects([subexpression symbolAtIndex:2], @"1");
XCTAssertEqual([subexpression numberOfElements], 3);
XCTAssertEqualObjects([subexpression elementAtIndex:0], @"34");
XCTAssertEqualObjects([subexpression elementAtIndex:2], @"1");
}
- (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
@try {
[testExpression subexpressionFromIndex:10];
[testExpression subexpressionFromLocation:10];
XCTFail(@"Should have raised an exception.");
}
@catch (NSException *exception) {}
@@ -146,11 +146,11 @@
}
- (void)testEqualExpressions {
MPExpression *expression1 = [[MPExpression alloc] initWithString:@"123"];
MPExpression *expression2 = [[MPExpression alloc] initWithString:@"1234"];
MPExpression *expression3 = [[MPExpression alloc] initWithFunction:[[MPFunction alloc] init]];
MPExpression *expression4 = [[MPExpression alloc] initWithSymbols:@[[[MPFunction alloc] init], @"123"]];
MPExpression *expression5 = [[MPExpression alloc] initWithSymbols:@[[[MPFunction alloc] init], @"123"]];
MPExpression *expression1 = [[MPExpression alloc] initWithElement:@"123"];
MPExpression *expression2 = [[MPExpression alloc] initWithElement:@"1234"];
MPExpression *expression3 = [[MPExpression alloc] initWithElement:[[MPFunction alloc] init]];
MPExpression *expression4 = [[MPExpression alloc] initWithElements:@[[[MPFunction alloc] init], @"123"]];
MPExpression *expression5 = [[MPExpression alloc] initWithElements:@[[[MPFunction alloc] init], @"123"]];
XCTAssertNotEqualObjects(expression1, expression2);
XCTAssertNotEqualObjects(expression1, expression3);
@@ -159,68 +159,83 @@
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 {
// Test Simple Expressions
MPExpression *testExpression = [[MPExpression alloc] initWithString:@"1234"];
MPExpression *testExpression = [[MPExpression alloc] initWithElement:@"1234"];
XCTAssertEqualObjects([testExpression description], @"1234");
testExpression = [[MPExpression alloc] initWithFunction:[[MPFunction alloc] init]];
testExpression = [[MPExpression alloc] initWithElement:[[MPFunction alloc] init]];
XCTAssertEqualObjects([testExpression description], @"[]");
// 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*[]");
// 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+[]");
// 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");
// 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");
// 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], @"[]*[]");
// 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], @"[]-[]");
// Test whitespaces in literal
testExpression = [[MPExpression alloc] initWithSymbols:@[@" 123 + ", [[MPFunction alloc] init]]];
testExpression = [[MPExpression alloc] initWithElements:@[@" 123 + ", [[MPFunction alloc] init]]];
XCTAssertEqualObjects([testExpression description], @"123 +[]");
}
- (void)testCopying {
MPExpression *baseExpression = [[MPExpression alloc] initWithFunction:[[MPFunction alloc] init]];
MPExpression *baseExpression = [[MPExpression alloc] initWithElement:[[MPFunction alloc] init]];
MPExpression *copy = [baseExpression copy];
XCTAssertEqual(baseExpression.numberOfElements, copy.numberOfElements);
XCTAssertNotEqual(copy, baseExpression);
XCTAssertNotEqual([baseExpression symbolAtIndex:0], [copy symbolAtIndex:0]);
XCTAssertNotEqual([baseExpression elementAtIndex:0], [copy elementAtIndex:0]);
XCTAssertEqualObjects(baseExpression, copy);
}
- (void)testMutableCopying {
MPExpression *baseExpression = [[MPExpression alloc] initWithString:@"123"];
MPMutableExpression *expression1 = [baseExpression mutableCopy];
[expression1 appendFunction:[[MPFunction alloc] init]];
MPExpression *expression2 = [[MPExpression alloc] initWithSymbols:@[@"123", [[MPFunction alloc] init]]];
- (void)testMutating {
MPExpression *testExpression = [[MPExpression alloc] initWithElements:@[@"1234", [[MPFunction alloc] init], @"5678", [[MPFunction alloc] init]]];
XCTAssertEqualObjects(expression1, expression2);
XCTAssertNotEqualObjects(baseExpression, expression1);
[testExpression appendElement:@"90"];
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