Archived
1

Model Redesign: Added Reference Frames

Added Inverse Functions
UI Redesign
Cleaned Code
This commit is contained in:
Kim Wittenburg
2014-10-07 20:25:54 +02:00
parent 8f2f773909
commit 82259f87e2
40 changed files with 1124 additions and 998 deletions

View File

@@ -14,6 +14,10 @@
3B52CEDD19BEE63000CEDCFC /* NSRegularExpression+MPParsingAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B52CEDB19BEE63000CEDCFC /* NSRegularExpression+MPParsingAdditions.m */; }; 3B52CEDD19BEE63000CEDCFC /* NSRegularExpression+MPParsingAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B52CEDB19BEE63000CEDCFC /* NSRegularExpression+MPParsingAdditions.m */; };
3B591BBB19C58D000061D86B /* MPMathRules.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B591BB919C58D000061D86B /* MPMathRules.h */; }; 3B591BBB19C58D000061D86B /* MPMathRules.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B591BB919C58D000061D86B /* MPMathRules.h */; };
3B591BBC19C58D000061D86B /* MPMathRules.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B591BBA19C58D000061D86B /* MPMathRules.m */; }; 3B591BBC19C58D000061D86B /* MPMathRules.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B591BBA19C58D000061D86B /* MPMathRules.m */; };
3B5FF73B19DB2FF500C8348A /* MPPowerFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B5FF73919DB2FF500C8348A /* MPPowerFunction.h */; };
3B5FF73C19DB2FF500C8348A /* MPPowerFunction.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B5FF73A19DB2FF500C8348A /* MPPowerFunction.m */; };
3B69B66C19DB41B90028E608 /* MPPowerFunctionLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B69B66A19DB41B90028E608 /* MPPowerFunctionLayout.h */; };
3B69B66D19DB41B90028E608 /* MPPowerFunctionLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B69B66B19DB41B90028E608 /* MPPowerFunctionLayout.m */; };
3B7172EA19C7147000FEAA5B /* FunctionsButtonDisclosure@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3B7172E819C7147000FEAA5B /* FunctionsButtonDisclosure@2x.png */; }; 3B7172EA19C7147000FEAA5B /* FunctionsButtonDisclosure@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3B7172E819C7147000FEAA5B /* FunctionsButtonDisclosure@2x.png */; };
3B7172EB19C7147000FEAA5B /* FunctionsButtonDisclosure.png in Resources */ = {isa = PBXBuildFile; fileRef = 3B7172E919C7147000FEAA5B /* FunctionsButtonDisclosure.png */; }; 3B7172EB19C7147000FEAA5B /* FunctionsButtonDisclosure.png in Resources */ = {isa = PBXBuildFile; fileRef = 3B7172E919C7147000FEAA5B /* FunctionsButtonDisclosure.png */; };
3B7172EE19C9FA8E00FEAA5B /* MPParenthesisFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B7172EC19C9FA8E00FEAA5B /* MPParenthesisFunction.h */; }; 3B7172EE19C9FA8E00FEAA5B /* MPParenthesisFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B7172EC19C9FA8E00FEAA5B /* MPParenthesisFunction.h */; };
@@ -143,7 +147,11 @@
3B52CEDB19BEE63000CEDCFC /* NSRegularExpression+MPParsingAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSRegularExpression+MPParsingAdditions.m"; sourceTree = "<group>"; }; 3B52CEDB19BEE63000CEDCFC /* NSRegularExpression+MPParsingAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSRegularExpression+MPParsingAdditions.m"; sourceTree = "<group>"; };
3B591BB919C58D000061D86B /* MPMathRules.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMathRules.h; sourceTree = "<group>"; }; 3B591BB919C58D000061D86B /* MPMathRules.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMathRules.h; sourceTree = "<group>"; };
3B591BBA19C58D000061D86B /* MPMathRules.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPMathRules.m; sourceTree = "<group>"; }; 3B591BBA19C58D000061D86B /* MPMathRules.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPMathRules.m; sourceTree = "<group>"; };
3B5FF73919DB2FF500C8348A /* MPPowerFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPowerFunction.h; sourceTree = "<group>"; };
3B5FF73A19DB2FF500C8348A /* MPPowerFunction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPowerFunction.m; sourceTree = "<group>"; };
3B688D9819982DF50006B4AB /* MPLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPLayout.m; sourceTree = "<group>"; }; 3B688D9819982DF50006B4AB /* MPLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPLayout.m; sourceTree = "<group>"; };
3B69B66A19DB41B90028E608 /* MPPowerFunctionLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPowerFunctionLayout.h; sourceTree = "<group>"; };
3B69B66B19DB41B90028E608 /* MPPowerFunctionLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPowerFunctionLayout.m; sourceTree = "<group>"; };
3B7172E819C7147000FEAA5B /* FunctionsButtonDisclosure@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "FunctionsButtonDisclosure@2x.png"; sourceTree = "<group>"; }; 3B7172E819C7147000FEAA5B /* FunctionsButtonDisclosure@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "FunctionsButtonDisclosure@2x.png"; sourceTree = "<group>"; };
3B7172E919C7147000FEAA5B /* FunctionsButtonDisclosure.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = FunctionsButtonDisclosure.png; sourceTree = "<group>"; }; 3B7172E919C7147000FEAA5B /* FunctionsButtonDisclosure.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = FunctionsButtonDisclosure.png; sourceTree = "<group>"; };
3B7172EC19C9FA8E00FEAA5B /* MPParenthesisFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPParenthesisFunction.h; sourceTree = "<group>"; }; 3B7172EC19C9FA8E00FEAA5B /* MPParenthesisFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPParenthesisFunction.h; sourceTree = "<group>"; };
@@ -370,6 +378,8 @@
3BB09EC81906FD830080A5ED /* MPSumFunction.m */, 3BB09EC81906FD830080A5ED /* MPSumFunction.m */,
3B7172EC19C9FA8E00FEAA5B /* MPParenthesisFunction.h */, 3B7172EC19C9FA8E00FEAA5B /* MPParenthesisFunction.h */,
3B7172ED19C9FA8E00FEAA5B /* MPParenthesisFunction.m */, 3B7172ED19C9FA8E00FEAA5B /* MPParenthesisFunction.m */,
3B5FF73919DB2FF500C8348A /* MPPowerFunction.h */,
3B5FF73A19DB2FF500C8348A /* MPPowerFunction.m */,
); );
name = Functions; name = Functions;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -401,6 +411,8 @@
3BB09EE0190736160080A5ED /* MPSumFunctionLayout.m */, 3BB09EE0190736160080A5ED /* MPSumFunctionLayout.m */,
3B7172F019C9FC6700FEAA5B /* MPParenthesisFunctionLayout.h */, 3B7172F019C9FC6700FEAA5B /* MPParenthesisFunctionLayout.h */,
3B7172F119C9FC6700FEAA5B /* MPParenthesisFunctionLayout.m */, 3B7172F119C9FC6700FEAA5B /* MPParenthesisFunctionLayout.m */,
3B69B66A19DB41B90028E608 /* MPPowerFunctionLayout.h */,
3B69B66B19DB41B90028E608 /* MPPowerFunctionLayout.m */,
); );
name = "Function Layouts"; name = "Function Layouts";
sourceTree = "<group>"; sourceTree = "<group>";
@@ -571,10 +583,12 @@
3B7172F219C9FC6700FEAA5B /* MPParenthesisFunctionLayout.h in Headers */, 3B7172F219C9FC6700FEAA5B /* MPParenthesisFunctionLayout.h in Headers */,
3B52CEDC19BEE63000CEDCFC /* NSRegularExpression+MPParsingAdditions.h in Headers */, 3B52CEDC19BEE63000CEDCFC /* NSRegularExpression+MPParsingAdditions.h in Headers */,
3B85833A19BB63D400D76A8D /* MPFunction.h in Headers */, 3B85833A19BB63D400D76A8D /* MPFunction.h in Headers */,
3B69B66C19DB41B90028E608 /* MPPowerFunctionLayout.h in Headers */,
3B85834319BB653700D76A8D /* MPSumFunction.h in Headers */, 3B85834319BB653700D76A8D /* MPSumFunction.h in Headers */,
3B85834119BB651E00D76A8D /* MPRangePath.h in Headers */, 3B85834119BB651E00D76A8D /* MPRangePath.h in Headers */,
3B85834219BB652900D76A8D /* MPException.h in Headers */, 3B85834219BB652900D76A8D /* MPException.h in Headers */,
3B85834519BB655200D76A8D /* NSIndexPath+MPAdditions.h in Headers */, 3B85834519BB655200D76A8D /* NSIndexPath+MPAdditions.h in Headers */,
3B5FF73B19DB2FF500C8348A /* MPPowerFunction.h in Headers */,
3B52CED019BE509C00CEDCFC /* MPParseError.h in Headers */, 3B52CED019BE509C00CEDCFC /* MPParseError.h in Headers */,
3B7172EE19C9FA8E00FEAA5B /* MPParenthesisFunction.h in Headers */, 3B7172EE19C9FA8E00FEAA5B /* MPParenthesisFunction.h in Headers */,
3B85834619BB655C00D76A8D /* MPExpressionView.h in Headers */, 3B85834619BB655C00D76A8D /* MPExpressionView.h in Headers */,
@@ -758,6 +772,7 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
3B5FF73C19DB2FF500C8348A /* MPPowerFunction.m in Sources */,
3BBEA94219BB79A700133766 /* MPExpressionLayout.m in Sources */, 3BBEA94219BB79A700133766 /* MPExpressionLayout.m in Sources */,
3B7B3A1919CC44E4005849E5 /* MPExpressionTokenizer.m in Sources */, 3B7B3A1919CC44E4005849E5 /* MPExpressionTokenizer.m in Sources */,
3B591BBC19C58D000061D86B /* MPMathRules.m in Sources */, 3B591BBC19C58D000061D86B /* MPMathRules.m in Sources */,
@@ -781,6 +796,7 @@
3BF59AFF19D80ECC00E54292 /* MPFunctionsViewController.m in Sources */, 3BF59AFF19D80ECC00E54292 /* MPFunctionsViewController.m in Sources */,
3B52CED119BE509C00CEDCFC /* MPParseError.m in Sources */, 3B52CED119BE509C00CEDCFC /* MPParseError.m in Sources */,
3BB18AA619CDB3A900986DA0 /* MPTokenStream.m in Sources */, 3BB18AA619CDB3A900986DA0 /* MPTokenStream.m in Sources */,
3B69B66D19DB41B90028E608 /* MPPowerFunctionLayout.m in Sources */,
3BBEA93619BB79A700133766 /* MPFunction.m in Sources */, 3BBEA93619BB79A700133766 /* MPFunction.m in Sources */,
3BBEA93519BB79A700133766 /* MPExpression.m in Sources */, 3BBEA93519BB79A700133766 /* MPExpression.m in Sources */,
3BBEA93B19BB79A700133766 /* MPRangePath.m in Sources */, 3BBEA93B19BB79A700133766 /* MPRangePath.m in Sources */,

View File

@@ -6,9 +6,8 @@
<objects> <objects>
<customObject id="-2" userLabel="File's Owner" customClass="MPDocument"> <customObject id="-2" userLabel="File's Owner" customClass="MPDocument">
<connections> <connections>
<outlet property="errorLabel" destination="fw3-bj-cPR" id="vaC-tD-zlq"/>
<outlet property="expressionView" destination="lcd-Ip-jjR" id="Vww-eh-hP7"/> <outlet property="expressionView" destination="lcd-Ip-jjR" id="Vww-eh-hP7"/>
<outlet property="resultLabel" destination="B5H-rE-1e9" id="Z5D-Co-tV1"/> <outlet property="resultLabel" destination="Cdb-3b-4iC" id="7O2-I6-gqP"/>
<outlet property="window" destination="xOd-HO-29H" id="JIz-fz-R2o"/> <outlet property="window" destination="xOd-HO-29H" id="JIz-fz-R2o"/>
</connections> </connections>
</customObject> </customObject>
@@ -16,48 +15,18 @@
<customObject id="-3" userLabel="Application" customClass="NSObject"/> <customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="MathPad" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="xOd-HO-29H" userLabel="Window"> <window title="MathPad" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="xOd-HO-29H" userLabel="Window">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/> <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<rect key="contentRect" x="525" y="411" width="507" height="249"/> <rect key="contentRect" x="525" y="411" width="432" height="181"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1178"/> <rect key="screenRect" x="0.0" y="0.0" width="1920" height="1178"/>
<view key="contentView" id="gIp-Ho-8D9"> <view key="contentView" id="gIp-Ho-8D9">
<rect key="frame" x="0.0" y="0.0" width="507" height="249"/> <rect key="frame" x="0.0" y="0.0" width="432" height="181"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="lcd-Ip-jjR" customClass="MPExpressionView"> <customView translatesAutoresizingMaskIntoConstraints="NO" id="lcd-Ip-jjR" customClass="MPExpressionView">
<rect key="frame" x="20" y="70" width="467" height="159"/> <rect key="frame" x="0.0" y="0.0" width="432" height="181"/>
</customView> <subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ar2-1O-Kl1"> <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Cdb-3b-4iC">
<rect key="frame" x="18" y="45" width="47" height="17"/> <rect key="frame" x="410" y="20" width="4" height="17"/>
<constraints> <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" id="I9X-Yv-EiR">
<constraint firstAttribute="width" constant="43" id="EI3-gZ-BdS"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Result:" id="lYc-e4-5j0">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="jQo-M8-to6">
<rect key="frame" x="18" y="20" width="40" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="36" id="v3s-bP-5SY"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Error:" id="0q9-PK-glz">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="B5H-rE-1e9">
<rect key="frame" x="71" y="45" width="418" height="17"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" id="KBm-kx-spX">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fw3-bj-cPR">
<rect key="frame" x="75" y="20" width="414" height="17"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" id="uaX-CN-Uoz">
<font key="font" metaFont="system"/> <font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
@@ -65,26 +34,22 @@
</textField> </textField>
</subviews> </subviews>
<constraints> <constraints>
<constraint firstItem="lcd-Ip-jjR" firstAttribute="top" secondItem="gIp-Ho-8D9" secondAttribute="top" constant="20" symbolic="YES" id="3g8-dx-OX8"/> <constraint firstAttribute="bottom" secondItem="Cdb-3b-4iC" secondAttribute="bottom" constant="20" id="3T9-HB-OrZ"/>
<constraint firstItem="B5H-rE-1e9" firstAttribute="trailing" secondItem="fw3-bj-cPR" secondAttribute="trailing" id="61b-ou-IhC"/> <constraint firstAttribute="trailing" secondItem="Cdb-3b-4iC" secondAttribute="trailing" constant="20" id="toz-uq-kuq"/>
<constraint firstItem="fw3-bj-cPR" firstAttribute="baseline" secondItem="jQo-M8-to6" secondAttribute="baseline" id="85V-7u-zhv"/> </constraints>
<constraint firstItem="fw3-bj-cPR" firstAttribute="leading" secondItem="jQo-M8-to6" secondAttribute="trailing" constant="21" id="C53-c9-INb"/> </customView>
<constraint firstItem="ar2-1O-Kl1" firstAttribute="baseline" secondItem="B5H-rE-1e9" secondAttribute="baseline" id="K1d-om-K23"/> </subviews>
<constraint firstAttribute="bottom" secondItem="jQo-M8-to6" secondAttribute="bottom" constant="20" symbolic="YES" id="Oet-xR-WPz"/> <constraints>
<constraint firstItem="jQo-M8-to6" firstAttribute="leading" secondItem="ar2-1O-Kl1" secondAttribute="leading" id="UyW-dW-kNf"/> <constraint firstItem="lcd-Ip-jjR" firstAttribute="leading" secondItem="gIp-Ho-8D9" secondAttribute="leading" id="5G8-rw-mds"/>
<constraint firstItem="B5H-rE-1e9" firstAttribute="trailing" secondItem="lcd-Ip-jjR" secondAttribute="trailing" id="VN8-ni-28x"/> <constraint firstAttribute="bottom" secondItem="lcd-Ip-jjR" secondAttribute="bottom" id="JAt-GL-atf"/>
<constraint firstItem="ar2-1O-Kl1" firstAttribute="leading" secondItem="lcd-Ip-jjR" secondAttribute="leading" id="X5m-hl-tOG"/> <constraint firstAttribute="trailing" secondItem="lcd-Ip-jjR" secondAttribute="trailing" id="Wi2-6P-2Li"/>
<constraint firstItem="jQo-M8-to6" firstAttribute="top" secondItem="ar2-1O-Kl1" secondAttribute="bottom" constant="8" symbolic="YES" id="Zv5-DC-xhH"/> <constraint firstItem="lcd-Ip-jjR" firstAttribute="top" secondItem="gIp-Ho-8D9" secondAttribute="top" id="hvP-0r-1OZ"/>
<constraint firstItem="B5H-rE-1e9" firstAttribute="leading" secondItem="ar2-1O-Kl1" secondAttribute="trailing" constant="10" id="a1q-H8-Mfg"/>
<constraint firstItem="ar2-1O-Kl1" firstAttribute="top" secondItem="lcd-Ip-jjR" secondAttribute="bottom" constant="8" symbolic="YES" id="bDG-CZ-mMi"/>
<constraint firstItem="lcd-Ip-jjR" firstAttribute="leading" secondItem="gIp-Ho-8D9" secondAttribute="leading" constant="20" symbolic="YES" id="kTW-r9-ulq"/>
<constraint firstAttribute="trailing" secondItem="lcd-Ip-jjR" secondAttribute="trailing" constant="20" symbolic="YES" id="kl8-mR-t1l"/>
</constraints> </constraints>
</view> </view>
<connections> <connections>
<outlet property="delegate" destination="-2" id="0bl-1N-x8E"/> <outlet property="delegate" destination="-2" id="0bl-1N-x8E"/>
</connections> </connections>
<point key="canvasLocation" x="139.5" y="146.5"/> <point key="canvasLocation" x="102" y="112.5"/>
</window> </window>
<collectionViewItem id="J9S-PW-LCL"> <collectionViewItem id="J9S-PW-LCL">
<connections> <connections>

View File

@@ -12,7 +12,6 @@
@property (weak) IBOutlet MPExpressionView *expressionView; @property (weak) IBOutlet MPExpressionView *expressionView;
@property (weak) IBOutlet NSTextField *resultLabel; @property (weak) IBOutlet NSTextField *resultLabel;
@property (weak) IBOutlet NSTextField *errorLabel;
- (IBAction)evaluateExpression:(id)sender; - (IBAction)evaluateExpression:(id)sender;

View File

@@ -62,11 +62,8 @@
- (IBAction)evaluateExpression:(id)sender { - (IBAction)evaluateExpression:(id)sender {
MPParseError *error; MPParseError *error;
NSDecimalNumber *result = [self.expressionView.expressionStorage evaluateWithError:&error]; NSDecimalNumber *result = [self.expressionView.expressionStorage evaluateWithError:&error];
self.resultLabel.stringValue = result != nil ? result.description : @"Error!";
self.errorLabel.stringValue = error != nil ? [NSString stringWithFormat:@"%@, %@", error, error.pathToExpression] : @"No Error";
if (error) {
self.expressionView.error = error; self.expressionView.error = error;
} self.resultLabel.stringValue = result != nil ? [result descriptionWithLocale:[NSLocale currentLocale]] : @"";
} }
@end @end

View File

@@ -15,7 +15,7 @@
- (void)push; - (void)push;
- (void)pop; - (void)pop;
- (void)defineVariable:(NSString *)variable withValue:(id)value; - (void)defineVariable:(NSString *)variable withValue:(NSDecimalNumber *)value;
- (void)undefineVariable:(NSString *)variable; - (void)undefineVariable:(NSString *)variable;
- (BOOL)isVariableDefined:(NSString *)variable; - (BOOL)isVariableDefined:(NSString *)variable;

View File

@@ -31,6 +31,9 @@ static MPEvaluationContext *sharedContext;
self = [super init]; self = [super init];
if (self) { if (self) {
_stack = [[NSMutableArray alloc] init]; _stack = [[NSMutableArray alloc] init];
[self push];
[self defineVariable:@"e" withValue:[[NSDecimalNumber alloc] initWithDouble:M_E]];
[self defineVariable:@"π" withValue:[[NSDecimalNumber alloc] initWithDouble:M_PI]];
} }
return self; return self;
} }
@@ -45,7 +48,7 @@ static MPEvaluationContext *sharedContext;
[self.stack removeLastObject]; [self.stack removeLastObject];
} }
- (void)defineVariable:(NSString *)variable withValue:(id)value - (void)defineVariable:(NSString *)variable withValue:(NSDecimalNumber *)value
{ {
NSMutableDictionary *currentBindings = self.stack.lastObject; NSMutableDictionary *currentBindings = self.stack.lastObject;
currentBindings[variable] = value; currentBindings[variable] = value;
@@ -59,8 +62,14 @@ static MPEvaluationContext *sharedContext;
- (NSDecimalNumber *)valueForVariable:(NSString *)variable - (NSDecimalNumber *)valueForVariable:(NSString *)variable
{ {
NSMutableDictionary *currentBindings = self.stack.lastObject; NSUInteger currentIndex = self.stack.count;
return currentBindings[variable]; NSDictionary *currentBindings;
NSDecimalNumber *value = nil;
while (!value && currentIndex > 0) {
currentBindings = self.stack[--currentIndex];
value = currentBindings[variable];
}
return value;
} }
- (BOOL)isVariableDefined:(NSString *)variable - (BOOL)isVariableDefined:(NSString *)variable

View File

@@ -8,6 +8,13 @@
@import Foundation; @import Foundation;
#import "NSString+MPExpressionElement.h" #import "NSString+MPExpressionElement.h"
#import "MPToken.h"
typedef NS_ENUM(NSUInteger, MPReferenceFrame) {
MPElementReferenceFrame,
MPSymbolReferenceFrame,
MPTokenReferenceFrame
};
@class MPExpression, MPFunction, MPRangePath, MPExpressionEvaluator, MPParseError; @class MPExpression, MPFunction, MPRangePath, MPExpressionEvaluator, MPParseError;
@protocol MPExpressionElement; @protocol MPExpressionElement;
@@ -127,6 +134,34 @@
@property (nonatomic, weak) MPFunction *parent; @property (nonatomic, weak) MPFunction *parent;
/*!
@method rootExpression
@brief Returns the root expression from the receiver's expression tree.
@discussion The root expression is the ultimate parent of all expressions and
functions in the expression tree. A root expression does not have
a parent.
@return The root expression from the receiver's expression tree.
*/
- (MPExpression *)rootExpression;
/*!
@method indexPath
@brief Returns the index path of the receiver in the expression tree.
@discussion The index path is calculated by going up the expression tree
collecting the respective index of the receiver. The indexes are
expressed in the indexed reference frame. If any of the indexes
exceed the respective receiver's bounds a @c NSRangeException is
raised.
@return The index path of the receiver in the expression tree.
*/
- (NSIndexPath *)indexPath;
/*! /*!
@method numberOfElements @method numberOfElements
@brief Returns the number of elements in the receiver. @brief Returns the number of elements in the receiver.
@@ -139,7 +174,7 @@
@return The current number of elements in the receiver. @return The current number of elements in the receiver.
*/ */
- (NSUInteger)numberOfElements; - (NSUInteger)countItemsInReferenceFrame:(MPReferenceFrame)referenceFrame;
/*! /*!
@@ -160,7 +195,23 @@
@return The element at @c anIndex. @return The element at @c anIndex.
*/ */
- (id<MPExpressionElement>)elementAtIndex:(NSUInteger)anIndex; - (id)itemAtIndex:(NSUInteger)anIndex
referenceFrame:(MPReferenceFrame)referenceFrame;
- (id<MPExpressionElement>)elementAtIndex:(NSUInteger)anIndex
referenceFrame:(MPReferenceFrame)referenceFrame;
/*!
@method indexOfElement:
@brief Returns the index of @c element or @c NSNotFound if it was not
found.
@param element
The element to find.
@return The index of @c element expressed in the indexed reference frame.
*/
- (NSUInteger)indexOfElement:(id<MPExpressionElement>)element;
/*! /*!
@@ -185,7 +236,8 @@
The length of the returned array is equal to the length of the The length of the returned array is equal to the length of the
specified range. specified range.
*/ */
- (NSArray *)elementsInIndexedRange:(NSRange)range; - (NSArray *)itemsInRange:(NSRange)range
referenceFrame:(MPReferenceFrame)referenceFrame;
/*! /*!
@@ -197,7 +249,51 @@
@return An array of all elements from the receiver. @return An array of all elements from the receiver.
*/ */
- (NSArray *)elements; - (NSArray *)allItemsInReferenceFrame:(MPReferenceFrame)referenceFrame;
/*!
@method elementAtIndexPath:
@brief Returns the element at the specified index path.
@discussion This method @em walks down the expression tree (including
functions) using the specified index path and finds the
corresponding element. The returned object can be an @c NSString,
a @c MPFunction or an @c MPExpression depending on the element @c
indexPath points to. If any of the indexes exceed the bounds of
the respective receiver an @c NSRangeException is raised.
If the index path does not contain any indexes the receiver
itself is returned.
@param indexPath
The index path the required object is located at. The indexes are
expressed in the indexed reference frame.
@return The element located at @c indexPath. The element is not copied
before it is returned. Be aware of the fact that any mutations
made to the returned object are reflected in the receiver.
*/
- (id)elementAtIndexPath:(NSIndexPath *)indexPath;
- (NSUInteger)convertIndex:(NSUInteger)index
fromReferenceFrame:(MPReferenceFrame)fromReferenceFrame
toReferenceFrame:(MPReferenceFrame)toReferenceFrame;
- (NSUInteger)convertIndex:(NSUInteger)index
fromReferenceFrame:(MPReferenceFrame)fromReferenceFrame
toReferenceFrame:(MPReferenceFrame)toReferenceFrame
offset:(NSUInteger *)offset;
- (NSRange)convertRange:(NSRange)aRange
fromReferenceFrame:(MPReferenceFrame)fromReferenceFrame
toReferenceFrame:(MPReferenceFrame)toReferenceFrame;
- (NSRange)convertRange:(NSRange)aRange
fromReferenceFrame:(MPReferenceFrame)fromReferenceFrame
toReferenceFrame:(MPReferenceFrame)toReferenceFrame
leadingOffset:(NSUInteger *)leadingOffset
trailingOffset:(NSUInteger *)trailingOffset;
#pragma mark Mutating Expressions #pragma mark Mutating Expressions
@@ -228,9 +324,16 @@
The elements that should replace the symbols specified by @c The elements that should replace the symbols specified by @c
range. range.
*/ */
- (void)replaceSymbolsInRange:(NSRange)range - (void)replaceItemsInRange:(NSRange)range
referenceFrame:(MPReferenceFrame)referenceFrame
withElements:(NSArray *)elements; withElements:(NSArray *)elements;
- (MPExpression *)subexpressionFromIndex:(NSUInteger)from
referenceFrame:(MPReferenceFrame)referenceFrame;
- (MPExpression *)subexpressionToIndex:(NSUInteger)to
referenceFrame:(MPReferenceFrame)referenceFrame;
- (MPExpression *)subexpressionWithRange:(NSRange)range
referenceFrame:(MPReferenceFrame)referenceFrame;
#pragma mark Evaluating Expressions #pragma mark Evaluating Expressions
@@ -256,17 +359,6 @@
- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error; - (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error;
/*!
@property evaluator
@brief Returns an object that can evaluate the receiver.
@discussion To just evaluate an expression it is recommended to send it an
@c evaluateWithError: message. You can however use this property
instead if you need more control over the evaluation process.
*/
@property (readonly, nonatomic, strong) MPExpressionEvaluator *evaluator;
#pragma mark Notifications #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 // 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
@@ -291,7 +383,7 @@
The number of elements replacing the elements specified by @c The number of elements replacing the elements specified by @c
rangePath. rangePath.
*/ */
- (void)didChangeElementsInIndexedRangePath:(MPRangePath *)rangePath - (void)didChangeElementsInRangePath:(MPRangePath *)rangePath
replacementLength:(NSUInteger)replacementLength; replacementLength:(NSUInteger)replacementLength;
@@ -301,312 +393,22 @@
@end @end
@interface MPExpression (MPExpressionConvenience)
/* --------------------------------------------------------------------------- */
/* Extension Methods */
/* --------------------------------------------------------------------------- */
@interface MPExpression (MPExpressionExtension)
#pragma mark Querying Expressions #pragma mark Querying Expressions
- (NSUInteger)countElements;
- (NSUInteger)countSymbols;
- (NSUInteger)countTokens;
/*! - (id<MPExpressionElement>)elementAtIndex:(NSUInteger)index;
@method length - (id<MPExpressionElement>)symbolAtIndex:(NSUInteger)index;
@brief Returns the length of the receiver. - (id<MPToken>)tokenAtIndex:(NSUInteger)index;
@discussion The length of an expression is calculated by going over each
element in the receiver and sending it a @c -length message. This
method should be used to determine the number of digits or
symbols in an expression.
The result of this method is expressed in the located reference
frame. The respective method for the indexed reference frame is
@c -numberOfSymbols.
@return The length of the receiver. This is the number of symbols in all
elements in the receiver where a function element is counted as a
single symbol.
*/
- (NSUInteger)length;
/*!
@method rootExpression
@brief Returns the root expression from the receiver's expression tree.
@discussion The root expression is the ultimate parent of all expressions and
functions in the expression tree. A root expression does not have
a parent.
@return The root expression from the receiver's expression tree.
*/
- (MPExpression *)rootExpression;
/*!
@method indexPath
@brief Returns the index path of the receiver in the expression tree.
@discussion The index path is calculated by going up the expression tree
collecting the respective index of the receiver. The indexes are
expressed in the indexed reference frame. If any of the indexes
exceed the respective receiver's bounds a @c NSRangeException is
raised.
@return The index path of the receiver in the expression tree.
*/
- (NSIndexPath *)indexPath;
// Subscripting is supported in the indexed reference frame
- (id)objectAtIndexedSubscript:(NSUInteger)idx;
#pragma mark Working With Expressions
/*!
@method elementAtLocation:
@brief Returns the element that is located at @c location.
@discussion This method finds an element in the located reference frame. If
@c location is greater or equal to the @c length of the receiver
a @c NSRangeException is raised.
@param location
The location of the element to find expressed in the located
reference frame.
@return The element located at @c location.
*/
- (id<MPExpressionElement>)elementAtLocation:(NSUInteger)location;
/*!
@method elementAtIndexPath:
@brief Returns the element at the specified index path.
@discussion This method @em walks down the expression tree (including
functions) using the specified index path and finds the
corresponding element. The returned object can be an @c NSString,
a @c MPFunction or an @c MPExpression depending on the element @c
indexPath points to. If any of the indexes exceed the bounds of
the respective receiver an @c NSRangeException is raised.
If the index path does not contain any indexes the receiver
itself is returned.
@param indexPath
The index path the required object is located at. The indexes are
expressed in the indexed reference frame.
@return The element located at @c indexPath. The element is not copied
before it is returned. Be aware of the fact that any mutations
made to the returned object are reflected in the receiver.
*/
- (id)elementAtIndexPath:(NSIndexPath *)indexPath;
/*!
@method elementsInIndexedRangePath:
@brief Returns the elements in the specified range path.
@discussion This method works similar to @c elementAtIndexPath: except that
it queries multiple elements at once.
@param rangePath
The range path the requested objects are located at. The complete
range path is expressed in the indexed reference frame.
@return An array of objects specified by the range path. The returned
elements are not copied before they are returned. Be aware that
any mutations made to the returned objects are reflected in the
receiver.
*/
- (NSArray *)elementsInIndexedRangePath:(MPRangePath *)rangePath;
/*!
@method indexOfElement:
@brief Returns the index of @c element or @c NSNotFound if it was not
found.
@param element
The element to find.
@return The index of @c element expressed in the indexed reference frame.
*/
- (NSUInteger)indexOfElement:(id<MPExpressionElement>)element;
#pragma mark Converting Between Indexes and Locations
/*!
@method indexOfElementAtLocation:offset:
@brief Calculates the index of the element the specified location points
to.
@discussion The @c location is in the located reference frame whereas the
returned value is an index. This method converts from the former
to the latter.
This method prefers higher indexes. This means that if the
returned @c offset would be equal to the length of the element at
the calculated index, insead index+1 is returned and the @c
offset is set to @c 0.
If the @c location exceeds the receiver's bounds a @c
NSRangeException will be raised.
@param location
The location of which you want the corresponding element index.
@param offset
An output parameter that gets set to the offst into the symbol
whose index is returned. If location for example points to the
symbol @c '2' in the string element @c '123' the offset @c would
be set to @c 1.
@return The index of the element the location points to.
*/
- (NSUInteger)indexOfElementAtLocation:(NSUInteger)location offset:(out NSUInteger *)offset;
/*!
@method locationOfElementAtIndex:
@brief Calculates the location of the element at @c index.
@discussion @c index is expressed in the indexed reference frame. Use this
method to convert an index into the located reference frame.
If the index exceeds the receiver's number of elements a @c
NSRangeException will be raised.
@param index
The index of the element that is to be converted into the length
reference frame.
@return The number of symbols (in the length reference frame) before the
element at @c index.
*/
- (NSUInteger)locationOfElementAtIndex:(NSUInteger)index;
/*!
@method indexedRangeForRange:
@brief Converts @c aRange from the located reference frame into the
indexed reference frame.
@discussion If the range exceeds the receiver's bounds a @c NSRangeException
is raised.
@param aRange
The range to be converted. Expressed in the located reference
frame.
@return @c aRange converted into the indexed reference frame.
*/
- (NSRange)indexedRangeForRange:(NSRange)aRange;
/*!
@method rangeForIndexedRange:
@brief Converts @c aRange from the indexed reference frame into the
located reference frame.
@discussion In the range exceeds the receiver's bounds a @c NSRangeException
is raised.
@param aRange
The range to be converted. Expressed in the indexed reference
frame.
@return @c aRange converted into the located reference frame.
*/
- (NSRange)rangeForIndexedRange:(NSRange)aRange;
#pragma mark Mutating Expressions #pragma mark Mutating Expressions
- (MPExpression *)subexpressionFromIndex:(NSUInteger)from;
- (MPExpression *)subexpressionToIndex:(NSUInteger)to;
- (MPExpression *)subexpressionWithIndexedRange:(NSRange)range;
/*!
@method subexpressionFromLocation:
@brief Creates a new expression from the specified location (inclusive)
to the end of the receiver.
@discussion The elements in the newly created expression are copied to the
new expression. The location is specified in the located
reference frame.
If the given location exceeds the receiver's bounds a @c
NSRangeException is raised.
@param from
The first location to be included in the new expression.
@return A new expression from the given location to the end of the
receiver.
*/
- (MPExpression *)subexpressionFromLocation:(NSUInteger)from;
/*!
@method subexpressionToLocation:
@brief Creates a new expression from the beginning to the specified
location (exclusive).
@discussion The elements in the newly created expression are copied to the
new expression. The location is specified in the located
reference frame.
If the given location exceeds the receiver's bounds a @c
NSRangeException is raised.
@param to
The first location not to be included in the new expression (or
the length of the new expression).
@return A new expression with the first @c to symbols of the receiver.
*/
- (MPExpression *)subexpressionToLocation:(NSUInteger)to;
/*!
@method subexpressionWithRange:
@brief Creates a new expression with the symbols in the specified range.
@discussion The elements in the newly created expression are copied to the
new exoression. The range is specified in the located reference
frame.
If the given range exceeds the receiver's bounds a @c
NSRangeException is raised.
@param range
The range from which to create the new expression.
@return A new expression with the symbols in the specified range.
*/
- (MPExpression *)subexpressionWithRange:(NSRange)range;
- (void)replaceElementsInIndexedRange:(NSRange)range
withElements:(NSArray *)elements;
- (void)replaceSymbolsInRangePath:(MPRangePath *)rangePath
withElements:(NSArray *)elements;
/*! /*!
@method appendElement: @method appendElement:
@brief Appends @c anElement to the receiver. @brief Appends @c anElement to the receiver.
@@ -626,56 +428,15 @@
*/ */
- (void)appendElements:(NSArray *)elements; - (void)appendElements:(NSArray *)elements;
- (void)insertElement:(id<MPExpressionElement>)anElement - (void)insertElement:(id<MPExpressionElement>)anElement
atIndex:(NSUInteger)index; atIndex:(NSUInteger)index
referenceFrame:(MPReferenceFrame)referenceFrame;
- (void)insertElements:(NSArray *)elements - (void)insertElements:(NSArray *)elements
atIndex:(NSUInteger)index; atIndex:(NSUInteger)index
referenceFrame:(MPReferenceFrame)referenceFrame;
/*!
@method insertElement:atLocation:
@brief Inserts @c anElement at @c location.
@discussion The location is specified in the length reference frame.
If the given location exceeds the receiver's bounds a @c
NSRangeException is raised.
@param anElement
The element to be inserted into the receiver.
@param location
The location @c anElement should be inserted at.
*/
- (void)insertElement:(id<MPExpressionElement>)anElement
atLocation:(NSUInteger)location;
/*!
@method insertElements:atLocation:
@brief Inserts the elements from @c elements at @c location.
@discussion The location is specified in the length reference frame.
If the given location exceeds the receiver's bounds a @c
NSRangeException is raised.
@param elements
The elements to be inserted into the receiver.
@param location
The location the elements in @c elements should be inserted into
the receiver.
*/
- (void)insertElements:(NSArray *)elements
atLocation:(NSUInteger)location;
- (void)deleteElementsInIndexedRange:(NSRange)range;
/*! /*!
@method deleteElementsInRange: @method deleteElementsInRange:
@brief Removes the elements specified by @c range from the receiver. @brief Removes the elements specified by @c range from the receiver.
@@ -688,25 +449,7 @@
@param range @param range
The range to remove from the receiver. The range to remove from the receiver.
*/ */
- (void)deleteElementsInRange:(NSRange)range; - (void)deleteElementsInRange:(NSRange)range
referenceFrame:(MPReferenceFrame)referenceFrame;
/*!
@method mutableElements
@brief Returns a proxy mutable array object that responds to all methods
defined by @c NSMutableArray.
@discussion Mutations on the proxy object also change the receiver. The proxy
object does not respond to coding methods. Copying the proxy
object will not duplicate it.
@return A proxy object that responds to all methods defined by @c
NSMutableArray.
*/
// - (NSMutableArray *)mutableElements;
/* Subscripting is supported for elements in the indexed reference frame */
- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx;
@end @end

View File

@@ -13,84 +13,32 @@
#import "NSIndexPath+MPAdditions.h" #import "NSIndexPath+MPAdditions.h"
#import "MPException.h" #import "MPException.h"
#import "MPExpressionTokenizer.h"
#import "MPExpressionEvaluator.h" #import "MPExpressionEvaluator.h"
#import "MPToken.h"
@interface MPExpression () { @interface MPExpression () {
NSMutableArray *__strong _elements; NSMutableArray * _elements;
} }
- (void)fixElements; - (NSArray *)tokens;
@end
@interface MPExpression (MPExpressionPrivate)
- (void)validateElements:(NSArray *)elements; - (void)validateElements:(NSArray *)elements;
- (void)fixElements;
- (NSUInteger)lengthOfElements:(NSArray *)elements; - (void)_replaceSymbolsInRange:(NSRange)range
withElements:(NSArray *)elements;
- (BOOL)splitElementsAtLocation:(NSUInteger)location - (BOOL)_splitElementsAtLocation:(NSUInteger)location
insertionIndex:(out NSUInteger *)insertionIndex; insertionIndex:(out NSUInteger *)insertionIndex;
@end @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}];
}
}
}
- (NSUInteger)lengthOfElements:(NSArray *)elements
{
NSUInteger length = 0;
for (id<MPExpressionElement> element in elements) {
length += element.length;
}
return length;
}
- (BOOL)splitElementsAtLocation:(NSUInteger)location
insertionIndex:(out NSUInteger *)insertionIndex
{
if (location == 0) {
*insertionIndex = 0;
return NO;
}
NSUInteger splitOffset;
NSUInteger splitElementIndex = [self indexOfElementAtLocation:location
offset:&splitOffset];
if (splitOffset != 0) {
NSString *splitElement = (NSString *)self.elements[splitElementIndex];
NSString *leftPart = [splitElement substringToIndex:splitOffset];
NSString *rightPart = [splitElement substringFromIndex:splitOffset];
[_elements replaceObjectsInRange:NSMakeRange(splitElementIndex, 1)
withObjectsFromArray:@[leftPart, rightPart]];
++splitElementIndex;
}
*insertionIndex = splitElementIndex;
return splitOffset != 0;
}
@end
@implementation MPExpression { @implementation MPExpression {
NSUInteger _cachedLength; NSArray *_tokenCache;
NSRange _editedRange; NSRange _editedRange;
BOOL _didSplitStartOnEditing; BOOL _didSplitStartOnEditing;
@@ -117,7 +65,6 @@
self = [super init]; self = [super init];
if (self) { if (self) {
[self validateElements:elements]; [self validateElements:elements];
_cachedLength = 0;
_elements = [[NSMutableArray alloc] initWithArray:elements _elements = [[NSMutableArray alloc] initWithArray:elements
copyItems:YES]; copyItems:YES];
[self fixElements]; [self fixElements];
@@ -129,11 +76,32 @@
#pragma mark Private Methods #pragma mark Private Methods
- (NSArray *)tokens
{
if (!_tokenCache) {
_tokenCache = [MPExpressionTokenizer tokenizeExpression:self];
}
return _tokenCache;
}
- (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}];
}
}
}
- (void)fixElements - (void)fixElements
{ {
for (NSUInteger index = 0; index < self.elements.count; index++) { for (NSUInteger index = 0; index < _elements.count; index++) {
id<MPExpressionElement> next = index+1 < self.elements.count ? self.elements[index+1] : nil; id<MPExpressionElement> next = index+1 < _elements.count ? _elements[index+1] : nil;
id<MPExpressionElement> current = self.elements[index]; id<MPExpressionElement> current = _elements[index];
if ([current isString]) { if ([current isString]) {
if (current.length == 0) { if (current.length == 0) {
[_elements removeObjectAtIndex:index]; [_elements removeObjectAtIndex:index];
@@ -166,38 +134,283 @@
} }
} }
#pragma mark Querying Expressions #pragma mark Querying Expressions
- (NSUInteger)numberOfElements - (MPExpression *)rootExpression
{ {
return self.elements.count; if (self.parent == nil) {
return self;
}
return [self.parent rootExpression];
}
- (NSIndexPath *)indexPath
{
if (self.parent) {
NSUInteger selfIndex = [self.parent indexOfChild:self];
return [[self.parent indexPath] indexPathByAddingIndex:selfIndex];
} else {
return [[NSIndexPath alloc] init];
}
}
- (NSUInteger)countItemsInReferenceFrame:(MPReferenceFrame)referenceFrame
{
switch (referenceFrame) {
case MPElementReferenceFrame:
return _elements.count;
case MPSymbolReferenceFrame:
{
NSUInteger count = 0;
for (id<MPExpressionElement> element in _elements) {
count += element.length;
}
return count;
}
case MPTokenReferenceFrame:
return self.tokens.count;
}
}
- (id)itemAtIndex:(NSUInteger)anIndex
referenceFrame:(MPReferenceFrame)referenceFrame
{
switch (referenceFrame) {
case MPElementReferenceFrame:
return _elements[anIndex];
case MPSymbolReferenceFrame:
{
NSUInteger location = 0;
NSUInteger elementIndex = 0;
id <MPExpressionElement> element = nil;
while (location < anIndex) {
element = _elements[elementIndex++];
location += element.length;
}
if (location == anIndex && element.isFunction) {
return element;
}
NSUInteger indexInString = location - element.length + anIndex;
return [((NSString *)element) substringWithRange:NSMakeRange(indexInString, 1)];
}
case MPTokenReferenceFrame:
return self.tokens[anIndex];
}
} }
- (id<MPExpressionElement>)elementAtIndex:(NSUInteger)anIndex - (id<MPExpressionElement>)elementAtIndex:(NSUInteger)anIndex
referenceFrame:(MPReferenceFrame)referenceFrame
{ {
return self.elements[anIndex]; NSUInteger elementIndex = [self convertIndex:anIndex
fromReferenceFrame:referenceFrame
toReferenceFrame:MPElementReferenceFrame];
return _elements[elementIndex];
} }
- (NSArray *)elementsInIndexedRange:(NSRange)range #warning If multiple equal expressions exist errors may occur...
- (NSUInteger)indexOfElement:(id<MPExpressionElement>)element
{ {
return [self.elements subarrayWithRange:range]; return [_elements indexOfObject:element];
} }
- (NSArray *)elements - (NSArray *)itemsInRange:(NSRange)range
referenceFrame:(MPReferenceFrame)referenceFrame
{ {
MPExpression *subexpression = [self subexpressionWithRange:range
referenceFrame:referenceFrame];
return [subexpression allItemsInReferenceFrame:referenceFrame];
}
- (NSArray *)allItemsInReferenceFrame:(MPReferenceFrame)referenceFrame
{
switch (referenceFrame) {
case MPElementReferenceFrame:
return _elements; return _elements;
case MPSymbolReferenceFrame:
{
NSMutableArray *symbols = [[NSMutableArray alloc] init];
for (id<MPExpressionElement> element in _elements) {
if ([element isString]) {
for (NSUInteger i = 0; i < [element length]; i++) {
NSString *ichar = [NSString stringWithFormat:@"%c", [((NSString *)element) characterAtIndex:i]];
[symbols addObject:ichar];
}
} else {
[symbols addObject:element];
}
}
}
case MPTokenReferenceFrame:
return self.tokens;
}
}
- (id)elementAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.length == 0) {
return self;
}
id<MPExpressionElement> element = _elements[[indexPath indexAtPosition:0]];
if (indexPath.length == 1) {
return element;
}
if ([element isFunction]) {
return [(MPFunction *)element elementAtIndexPath:[indexPath indexPathByRemovingFirstIndex]];
}
// TODO: Raise appropriate exeption.
return nil;
}
- (NSUInteger)convertIndex:(NSUInteger)index
fromReferenceFrame:(MPReferenceFrame)fromReferenceFrame
toReferenceFrame:(MPReferenceFrame)toReferenceFrame
{
return [self convertIndex:index
fromReferenceFrame:fromReferenceFrame
toReferenceFrame:toReferenceFrame
offset:NULL];
}
- (NSUInteger)convertIndex:(NSUInteger)index
fromReferenceFrame:(MPReferenceFrame)fromReferenceFrame
toReferenceFrame:(MPReferenceFrame)toReferenceFrame
offset:(NSUInteger *)offset
{
if (fromReferenceFrame == toReferenceFrame || index == 0) {
if (offset) {
*offset = 0;
}
return index;
}
NSUInteger symbolIndex __block = 0;
switch (fromReferenceFrame) {
case MPElementReferenceFrame:
[_elements enumerateObjectsUsingBlock:^(id<MPExpressionElement> obj, NSUInteger idx, BOOL *stop) {
symbolIndex += obj.length;
*stop = idx >= index - 1;
}];
break;
case MPSymbolReferenceFrame:
symbolIndex = index;
break;
case MPTokenReferenceFrame:
[_elements enumerateObjectsUsingBlock:^(id<MPToken> obj, NSUInteger idx, BOOL *stop) {
symbolIndex = NSMaxRange(obj.range);
*stop = idx >= index - 1;
}];
break;
}
switch (toReferenceFrame) {
case MPElementReferenceFrame:
{
NSUInteger totalLength = 0;
NSUInteger elementIndex = 0;
id<MPExpressionElement> element;
while (totalLength < symbolIndex) {
element = _elements[elementIndex++];
totalLength += element.length;
}
--elementIndex;
NSUInteger offsetInElement = element.length - totalLength + symbolIndex;
if (offsetInElement == element.length) {
offsetInElement = 0;
elementIndex++;
}
if (offset) {
*offset = offsetInElement;
}
return elementIndex;
}
case MPSymbolReferenceFrame:
if (offset) {
*offset = 0;
}
return symbolIndex;
case MPTokenReferenceFrame:
{
NSUInteger totalLength = 0;
NSUInteger tokenIndex = 0;
id<MPToken> token;
while (totalLength < symbolIndex) {
token = self.tokens[tokenIndex++];
totalLength = NSMaxRange(token.range);
}
NSUInteger offsetInToken = token.range.length - totalLength + symbolIndex;
if (offsetInToken == token.range.length) {
offsetInToken = 0;
tokenIndex++;
}
if (offset) {
*offset = offsetInToken;
}
return tokenIndex;
}
}
}
- (NSRange)convertRange:(NSRange)aRange
fromReferenceFrame:(MPReferenceFrame)fromReferenceFrame
toReferenceFrame:(MPReferenceFrame)toReferenceFrame
{
return [self convertRange:aRange
fromReferenceFrame:fromReferenceFrame
toReferenceFrame:toReferenceFrame
leadingOffset:NULL
trailingOffset:NULL];
}
- (NSRange)convertRange:(NSRange)aRange
fromReferenceFrame:(MPReferenceFrame)fromReferenceFrame
toReferenceFrame:(MPReferenceFrame)toReferenceFrame
leadingOffset:(NSUInteger *)leadingOffset
trailingOffset:(NSUInteger *)trailingOffset
{
NSUInteger start = [self convertIndex:aRange.location
fromReferenceFrame:fromReferenceFrame
toReferenceFrame:toReferenceFrame
offset:leadingOffset];
NSUInteger end = [self convertIndex:NSMaxRange(aRange)
fromReferenceFrame:fromReferenceFrame
toReferenceFrame:toReferenceFrame
offset:trailingOffset];
return NSMakeRange(start, end - start);
} }
#pragma mark Mutating Expressions #pragma mark Mutating Expressions
- (void)replaceSymbolsInRange:(NSRange)range - (void)replaceItemsInRange:(NSRange)range
referenceFrame:(MPReferenceFrame)referenceFrame
withElements:(NSArray *)elements withElements:(NSArray *)elements
{ {
if (NSMaxRange(range) > self.length) { NSUInteger start = [self convertIndex:range.location
fromReferenceFrame:referenceFrame
toReferenceFrame:MPSymbolReferenceFrame];
NSUInteger end = [self convertIndex:NSMaxRange(range)
fromReferenceFrame:referenceFrame
toReferenceFrame:MPSymbolReferenceFrame];
[self _replaceSymbolsInRange:NSMakeRange(start, end - start)
withElements:elements];
}
- (void)_replaceSymbolsInRange:(NSRange)range
withElements:(NSArray *)elements
{
if (NSMaxRange(range) > [self countItemsInReferenceFrame:MPSymbolReferenceFrame]) {
@throw [NSException exceptionWithName:NSRangeException @throw [NSException exceptionWithName:NSRangeException
reason:@"Range out of bounds of expression" reason:@"Range out of bounds of expression"
userInfo:nil]; userInfo:nil];
@@ -207,14 +420,14 @@
// Locate the position, split the elements // Locate the position, split the elements
NSUInteger startIndex; // startIndex is inclusive NSUInteger startIndex; // startIndex is inclusive
BOOL didSplitStart = NO; BOOL didSplitStart = NO;
if ([self numberOfElements] == 0) { if (_elements.count == 0) {
startIndex = 0; startIndex = 0;
} else { } else {
didSplitStart = [self splitElementsAtLocation:range.location didSplitStart = [self _splitElementsAtLocation:range.location
insertionIndex:&startIndex]; insertionIndex:&startIndex];
} }
NSUInteger endIndex; // endIndex is exclusive NSUInteger endIndex; // endIndex is exclusive
BOOL didSplitEnd = [self splitElementsAtLocation:NSMaxRange(range) BOOL didSplitEnd = [self _splitElementsAtLocation:NSMaxRange(range)
insertionIndex:&endIndex]; insertionIndex:&endIndex];
// Perform the replacement // Perform the replacement
@@ -223,7 +436,7 @@
[_elements replaceObjectsInRange:NSMakeRange(startIndex, endIndex-startIndex) [_elements replaceObjectsInRange:NSMakeRange(startIndex, endIndex-startIndex)
withObjectsFromArray:newElements]; withObjectsFromArray:newElements];
_cachedLength = 0; _tokenCache = nil;
NSUInteger editLocation = startIndex - (didSplitStart ? 1 : 0); NSUInteger editLocation = startIndex - (didSplitStart ? 1 : 0);
NSUInteger editLength = endIndex - startIndex; NSUInteger editLength = endIndex - startIndex;
@@ -246,37 +459,78 @@
} }
[self fixElements]; [self fixElements];
[self.evaluator expressionDidChangeInRange:_editedRange [self didChangeElementsInRangePath:[[MPRangePath alloc] initWithRange:_editedRange]
replacementLength:_replacementLength];
[self didChangeElementsInIndexedRangePath:[[MPRangePath alloc] initWithRange:_editedRange]
replacementLength:_replacementLength]; replacementLength:_replacementLength];
} }
- (BOOL)_splitElementsAtLocation:(NSUInteger)location
insertionIndex:(out NSUInteger *)insertionIndex
{
if (location == 0) {
*insertionIndex = 0;
return NO;
}
NSUInteger splitOffset;
NSUInteger splitElementIndex = [self convertIndex:location
fromReferenceFrame:MPSymbolReferenceFrame
toReferenceFrame:MPElementReferenceFrame
offset:&splitOffset];
if (splitOffset != 0) {
NSString *splitElement = (NSString *)_elements[splitElementIndex];
NSString *leftPart = [splitElement substringToIndex:splitOffset];
NSString *rightPart = [splitElement substringFromIndex:splitOffset];
[_elements replaceObjectsInRange:NSMakeRange(splitElementIndex, 1)
withObjectsFromArray:@[leftPart, rightPart]];
++splitElementIndex;
}
*insertionIndex = splitElementIndex;
return splitOffset != 0;
}
- (MPExpression *)subexpressionFromIndex:(NSUInteger)from
referenceFrame:(MPReferenceFrame)referenceFrame
{
return [self subexpressionWithRange:NSMakeRange(from, [self countItemsInReferenceFrame:referenceFrame] - from)
referenceFrame:referenceFrame];
}
- (MPExpression *)subexpressionToIndex:(NSUInteger)to
referenceFrame:(MPReferenceFrame)referenceFrame
{
return [self subexpressionWithRange:NSMakeRange(0, to)
referenceFrame:referenceFrame];
}
- (MPExpression *)subexpressionWithRange:(NSRange)range
referenceFrame:(MPReferenceFrame)referenceFrame
{
MPExpression *subexpression = [self copy];
NSRange preceedingRange = NSMakeRange(0, range.location);
NSUInteger firstOut = NSMaxRange(range);
NSRange exceedingRange = NSMakeRange(firstOut, [self countItemsInReferenceFrame:referenceFrame] - firstOut);
[subexpression deleteElementsInRange:exceedingRange
referenceFrame:referenceFrame];
[subexpression deleteElementsInRange:preceedingRange
referenceFrame:referenceFrame];
return subexpression;
}
#pragma mark Evaluating Expressions #pragma mark Evaluating Expressions
- (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error - (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error
{ {
MPTerm *term = [self.evaluator parseExpectingVariable:NO MPExpressionEvaluator *evaluator = [[MPExpressionEvaluator alloc] initWithExpression:self];
MPTerm *term = [evaluator parseExpectingVariable:NO
error:error]; error:error];
return [term evaluate]; return [term evaluate];
} }
@synthesize evaluator = _evaluator;
- (MPExpressionEvaluator *)evaluator
{
if (!_evaluator) {
_evaluator = [[MPExpressionEvaluator alloc] initWithExpression:self];
}
return _evaluator;
}
#pragma mark Notifications #pragma mark Notifications
- (void)didChangeElementsInIndexedRangePath:(MPRangePath *)rangePath - (void)didChangeElementsInRangePath:(MPRangePath *)rangePath
replacementLength:(NSUInteger)replacementLength replacementLength:(NSUInteger)replacementLength
{ {
NSUInteger selfIndex = [self.parent indexOfChild:self]; NSUInteger selfIndex = [self.parent indexOfChild:self];
@@ -315,11 +569,11 @@
#warning Bad Implementation #warning Bad Implementation
NSMutableString *description = [[NSMutableString alloc] init]; NSMutableString *description = [[NSMutableString alloc] init];
NSUInteger index = 0; NSUInteger index = 0;
for (id element in self.elements) { for (id element in _elements) {
if ([element isString]) { if ([element isString]) {
NSMutableString *correctedSymbol = [[element stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] mutableCopy]; NSMutableString *correctedSymbol = [[element stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] mutableCopy];
// Prefix operator // Prefix operator
if (element != self.elements[0]) { if (element != _elements[0]) {
unichar prefix = [correctedSymbol characterAtIndex:0]; unichar prefix = [correctedSymbol characterAtIndex:0];
if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:prefix]) { if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:prefix]) {
[correctedSymbol insertString:@"*" [correctedSymbol insertString:@"*"
@@ -327,14 +581,14 @@
} }
} }
// Suffix operator // Suffix operator
if (element != [self.elements lastObject]) { if (element != [_elements lastObject]) {
unichar suffix = [correctedSymbol characterAtIndex:correctedSymbol.length-1]; unichar suffix = [correctedSymbol characterAtIndex:correctedSymbol.length-1];
if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:suffix]) { if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:suffix]) {
[correctedSymbol appendString:@"*"]; [correctedSymbol appendString:@"*"];
} }
} }
[description appendString:correctedSymbol]; [description appendString:correctedSymbol];
} else if (index > 0 && [self.elements[index-1] isKindOfClass:[MPFunction class]]) { } else if (index > 0 && [_elements[index-1] isKindOfClass:[MPFunction class]]) {
[description appendFormat:@"*%@", [element description]]; [description appendFormat:@"*%@", [element description]];
} else { } else {
[description appendString:[element description]]; [description appendString:[element description]];
@@ -346,7 +600,7 @@
- (NSUInteger)hash - (NSUInteger)hash
{ {
return [self.elements hash]; return [_elements hash];
} }
@@ -355,7 +609,7 @@
- (id)copyWithZone:(NSZone *)zone - (id)copyWithZone:(NSZone *)zone
{ {
MPExpression *copy = [[MPExpression allocWithZone:zone] initWithElements:self.elements]; MPExpression *copy = [[MPExpression allocWithZone:zone] initWithElements:_elements];
return copy; return copy;
} }
@@ -371,211 +625,52 @@
- (void)encodeWithCoder:(NSCoder *)aCoder - (void)encodeWithCoder:(NSCoder *)aCoder
{ {
[aCoder encodeObject:self.elements]; [aCoder encodeObject:_elements];
} }
@end @end
@implementation MPExpression (MPExpressionConvenience)
@implementation MPExpression (MPExpressionExtension)
#pragma mark Querying Expressions #pragma mark Querying Expressions
- (NSUInteger)countElements
- (NSUInteger)length
{ {
if (_cachedLength == 0) { return [self countItemsInReferenceFrame:MPElementReferenceFrame];
_cachedLength = [self lengthOfElements:self.elements];
}
return _cachedLength;
} }
- (MPExpression *)rootExpression - (NSUInteger)countSymbols
{ {
if (self.parent == nil) { return [self countItemsInReferenceFrame:MPSymbolReferenceFrame];
return self;
}
return [self.parent rootExpression];
} }
- (NSIndexPath *)indexPath - (NSUInteger)countTokens
{ {
if (self.parent) { return [self countItemsInReferenceFrame:MPTokenReferenceFrame];
NSUInteger selfIndex = [self.parent indexOfChild:self];
return [[self.parent indexPath] indexPathByAddingIndex:selfIndex];
} else {
return [[NSIndexPath alloc] init];
}
} }
- (id)objectAtIndexedSubscript:(NSUInteger)idx - (id<MPExpressionElement>)elementAtIndex:(NSUInteger)index
{ {
return [self elementAtIndex:idx]; return [self itemAtIndex:index
referenceFrame:MPElementReferenceFrame];
} }
- (id<MPExpressionElement>)symbolAtIndex:(NSUInteger)index
#pragma mark Working With Expressions
- (id<MPExpressionElement>)elementAtLocation:(NSUInteger)location
{ {
NSUInteger index = [self indexOfElementAtLocation:location offset:NULL]; return [self itemAtIndex:index
return [self elementAtIndex:index]; referenceFrame:MPSymbolReferenceFrame];
} }
- (id)elementAtIndexPath:(NSIndexPath *)indexPath - (id<MPToken>)tokenAtIndex:(NSUInteger)index
{ {
if (indexPath.length == 0) { return [self itemAtIndex:index
return self; referenceFrame:MPTokenReferenceFrame];
}
id<MPExpressionElement> element = [self elementAtIndex:[indexPath indexAtPosition:0]];
if (indexPath.length == 1) {
return element;
}
if ([element isFunction]) {
return [(MPFunction *)element elementAtIndexPath:[indexPath indexPathByRemovingFirstIndex]];
}
return nil;
}
- (NSArray *)elementsInIndexedRangePath:(MPRangePath *)rangePath
{
MPExpression *targetExpression = [self elementAtIndexPath:[rangePath.location indexPathByRemovingLastIndex]];
return [targetExpression elementsInIndexedRange:rangePath.rangeAtLastIndex];
}
#warning If multiple equal expressions exist errors may occur...
- (NSUInteger)indexOfElement:(id<MPExpressionElement>)element
{
return [self.elements indexOfObject:element];
}
#pragma mark Converting Between Indexes and Locations
- (NSUInteger)indexOfElementAtLocation:(NSUInteger)location
offset:(out NSUInteger *)offset
{
if (location == 0) {
if (offset != NULL) {
*offset = 0;
}
return 0;
}
// Calculating elementIndex and splitOffset
NSUInteger totalLength = 0;
NSUInteger elementIndex = 0;
NSUInteger elementLength = 0;
for (id<MPExpressionElement> element in self.elements) {
elementLength = element.length;
totalLength += elementLength;
if (totalLength >= location) {
break;
}
++elementIndex;
}
NSUInteger elementOffset = elementLength - (totalLength - location);
id<MPExpressionElement> element = self.elements[elementIndex];
if (elementOffset == element.length) {
elementOffset = 0;
elementIndex++;
}
if (offset != NULL) {
*offset = elementOffset;
}
return elementIndex;
}
- (NSUInteger)locationOfElementAtIndex:(NSUInteger)index
{
NSUInteger location = 0;
for (NSUInteger i = 0; i < index; i++) {
location += [self elementAtIndex:i].length;
}
return location;
}
- (NSRange)indexedRangeForRange:(NSRange)aRange
{
NSUInteger startLocation = aRange.location;
NSUInteger endLocation = NSMaxRange(aRange);
NSUInteger startIndex = [self indexOfElementAtLocation:startLocation offset:NULL];
NSUInteger endIndex = [self indexOfElementAtLocation:endLocation offset:NULL];
return NSMakeRange(startIndex, endIndex-startIndex);
}
- (NSRange)rangeForIndexedRange:(NSRange)aRange
{
NSUInteger startIndex = aRange.location;
NSUInteger endIndex = NSMaxRange(aRange);
NSUInteger startLocation = [self locationOfElementAtIndex:startIndex];
NSUInteger endLocation = [self locationOfElementAtIndex:endIndex];
return NSMakeRange(startLocation, endLocation-startLocation);
} }
#pragma mark Mutating Expressions #pragma mark Mutating Expressions
- (MPExpression *)subexpressionFromIndex:(NSUInteger)from
{
NSUInteger fromLocation = [self locationOfElementAtIndex:from];
return [self subexpressionFromLocation:fromLocation];
}
- (MPExpression *)subexpressionToIndex:(NSUInteger)to
{
NSUInteger toLocation = [self locationOfElementAtIndex:to];
return [self subexpressionToLocation:toLocation];
}
- (MPExpression *)subexpressionWithIndexedRange:(NSRange)range
{
NSRange locationRange = [self rangeForIndexedRange:range];
return [self subexpressionWithRange:locationRange];
}
- (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;
}
- (void)replaceElementsInIndexedRange:(NSRange)range
withElements:(NSArray *)elements
{
[self replaceSymbolsInRange:[self rangeForIndexedRange:range]
withElements:elements];
}
- (void)replaceSymbolsInRangePath:(MPRangePath *)rangePath
withElements:(NSArray *)elements
{
MPExpression *targetExpression = [self elementAtIndexPath:[rangePath.location indexPathByRemovingLastIndex]];
[targetExpression replaceSymbolsInRange:rangePath.rangeAtLastIndex
withElements:elements];
}
- (void)appendElement:(id<MPExpressionElement>)anElement - (void)appendElement:(id<MPExpressionElement>)anElement
{ {
[self appendElements:@[anElement]]; [self appendElements:@[anElement]];
@@ -583,49 +678,35 @@
- (void)appendElements:(NSArray *)elements - (void)appendElements:(NSArray *)elements
{ {
[self replaceSymbolsInRange:NSMakeRange(self.length, 0) withElements:elements]; [self replaceItemsInRange:NSMakeRange([self countItemsInReferenceFrame:MPSymbolReferenceFrame], 0)
referenceFrame:MPSymbolReferenceFrame
withElements:elements];
} }
- (void)insertElement:(id<MPExpressionElement>)anElement - (void)insertElement:(id<MPExpressionElement>)anElement
atIndex:(NSUInteger)index atIndex:(NSUInteger)index
referenceFrame:(MPReferenceFrame)referenceFrame
{ {
[self insertElement:anElement [self insertElements:@[anElement]
atLocation:[self locationOfElementAtIndex:index]]; atIndex:index
referenceFrame:referenceFrame];
} }
- (void)insertElements:(NSArray *)elements - (void)insertElements:(NSArray *)elements
atIndex:(NSUInteger)index atIndex:(NSUInteger)index
referenceFrame:(MPReferenceFrame)referenceFrame
{ {
[self insertElements:elements [self replaceItemsInRange:NSMakeRange(index, 0)
atLocation:[self locationOfElementAtIndex:index]]; referenceFrame:referenceFrame
} withElements:elements];
- (void)insertElement:(id<MPExpressionElement>)anElement
atLocation:(NSUInteger)location
{
[self insertElements:@[anElement] atLocation:location];
}
- (void)insertElements:(NSArray *)elements
atLocation:(NSUInteger)location
{
[self replaceSymbolsInRange:NSMakeRange(location, 0) withElements:elements];
}
- (void)deleteElementsInIndexedRange:(NSRange)range
{
[self deleteElementsInIndexedRange:[self rangeForIndexedRange:range]];
} }
- (void)deleteElementsInRange:(NSRange)range - (void)deleteElementsInRange:(NSRange)range
referenceFrame:(MPReferenceFrame)referenceFrame
{ {
[self replaceSymbolsInRange:range withElements:@[]]; [self replaceItemsInRange:range
} referenceFrame:referenceFrame
withElements:@[]];
- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx
{
[self replaceSymbolsInRange:NSMakeRange(idx, 1)
withElements:@[obj]];
} }
@end @end

View File

@@ -15,15 +15,9 @@
@interface MPExpressionEvaluator : NSObject @interface MPExpressionEvaluator : NSObject
// Do not instanciate yourself, use evaluator property of MPExpression instead
- (instancetype)initWithExpression:(MPExpression *)expression; - (instancetype)initWithExpression:(MPExpression *)expression;
@property (readonly, nonatomic, weak) MPExpression *expression; @property (readonly, nonatomic, weak) MPExpression *expression;
@property (nonatomic, strong) MPExpressionTokenizer *lexer;
- (void)expressionDidChangeInRange:(NSRange)range
replacementLength:(NSUInteger)replacementLength;
#pragma mark Evaluating Expressions #pragma mark Evaluating Expressions
@property (readonly, nonatomic, copy) NSString *definedVariable; @property (readonly, nonatomic, copy) NSString *definedVariable;

View File

@@ -21,7 +21,7 @@
@interface MPExpressionEvaluator () @interface MPExpressionEvaluator ()
@property (readwrite, nonatomic, copy) NSString *definedVariable; @property (readwrite, nonatomic, copy) NSString *definedVariable;
@property (nonatomic, strong) NSArray *tokens;
- (void)setError:(MPParseError *)error; - (void)setError:(MPParseError *)error;
@@ -41,36 +41,6 @@
return self; return self;
} }
@synthesize lexer = _lexer;
- (void)setLexer:(MPExpressionTokenizer *)lexer
{
_lexer = lexer;
self.tokens = nil;
}
- (MPExpressionTokenizer *)lexer
{
if (!_lexer) {
_lexer = [[MPExpressionTokenizer alloc] init];
}
return _lexer;
}
- (NSArray *)tokens
{
if (!_tokens) {
_tokens = [self.lexer tokenizeExpression:self.expression];
}
return _tokens;
}
- (void)expressionDidChangeInRange:(NSRange)range
replacementLength:(NSUInteger)replacementLength
{
self.tokens = nil;
}
#pragma mark Evaluating Expressions #pragma mark Evaluating Expressions
- (void)setError:(MPParseError *)error - (void)setError:(MPParseError *)error
{ {
@@ -84,7 +54,7 @@
error:(MPParseError *__autoreleasing *)error error:(MPParseError *__autoreleasing *)error
{ {
_error = error; _error = error;
tokenStream = [[MPTokenStream alloc] initWithTokens:self.tokens]; tokenStream = [[MPTokenStream alloc] initWithTokens:[self.expression allItemsInReferenceFrame:MPTokenReferenceFrame]];
if (!tokenStream.hasMoreTokens) { if (!tokenStream.hasMoreTokens) {
self.error = MPParseError(NSMakeRange(0, 0), @"Empty Expression"); self.error = MPParseError(NSMakeRange(0, 0), @"Empty Expression");
return nil; return nil;
@@ -186,11 +156,16 @@
case MPGenericFunctionToken: case MPGenericFunctionToken:
{ {
if ([token isKindOfClass:[MPPowerFunction class]]) {
self.error = MPParseError(NSMakeRange(token.range.location, 0), @"No Base for Power");
return nil;
}
return [self decoratedTerm:[((MPFunction *)token) parseWithError:_error]]; return [self decoratedTerm:[((MPFunction *)token) parseWithError:_error]];
} }
case MPSinToken: case MPSinToken:
{ {
BOOL inverse = [self inverseFunction];
NSRange sinTermRange; NSRange sinTermRange;
MPTerm *sinTerm = [self nextValue:&sinTermRange]; MPTerm *sinTerm = [self nextValue:&sinTermRange];
if (!sinTerm) { if (!sinTerm) {
@@ -199,11 +174,12 @@
} }
return nil; return nil;
} }
return [[MPTerm alloc] initWithSinOfTerm:sinTerm]; return inverse ? [[MPTerm alloc] initWithInverseSinOfTerm:sinTerm] : [[MPTerm alloc] initWithSinOfTerm:sinTerm];
} }
case MPCosToken: case MPCosToken:
{ {
BOOL inverse = [self inverseFunction];
NSRange cosTermRange; NSRange cosTermRange;
MPTerm *cosTerm = [self nextValue:&cosTermRange]; MPTerm *cosTerm = [self nextValue:&cosTermRange];
if (!cosTerm) { if (!cosTerm) {
@@ -212,11 +188,12 @@
} }
return nil; return nil;
} }
return [[MPTerm alloc] initWithSinOfTerm:cosTerm]; return inverse ? [[MPTerm alloc] initWithInverseCosOfTerm:cosTerm] : [[MPTerm alloc] initWithCosOfTerm:cosTerm];
} }
case MPTanToken: case MPTanToken:
{ {
BOOL inverse = [self inverseFunction];
NSRange tanTermRange; NSRange tanTermRange;
MPTerm *tanTerm = [self nextValue:&tanTermRange]; MPTerm *tanTerm = [self nextValue:&tanTermRange];
if (!tanTerm) { if (!tanTerm) {
@@ -225,7 +202,7 @@
} }
return nil; return nil;
} }
return [[MPTerm alloc] initWithTanOfTerm:tanTerm]; return inverse ? [[MPTerm alloc] initWithInverseTanOfTerm:tanTerm] : [[MPTerm alloc] initWithTanOfTerm:tanTerm];
} }
case MPEOFToken: case MPEOFToken:
@@ -254,10 +231,31 @@
MPPowerFunction *powerFunction = (MPPowerFunction *)powerToken; MPPowerFunction *powerFunction = (MPPowerFunction *)powerToken;
powerFunction.baseTerm = decoratedTerm; powerFunction.baseTerm = decoratedTerm;
return [powerFunction parseWithError:_error]; return [powerFunction parseWithError:_error];
} else { } else if (powerToken) {
tokenStream.currentLocation--; tokenStream.currentTokenIndex--;
} }
return decoratedTerm; return decoratedTerm;
} }
- (BOOL)inverseFunction
{
MPToken *powerToken = [tokenStream nextTokenOfType:MPGenericFunctionToken];
if ([powerToken isKindOfClass:[MPPowerFunction class]]) {
MPPowerFunction *powerFunction = (MPPowerFunction *)powerToken;
if (powerFunction.exponentExpression.countElements == 1) {
id<MPExpressionElement> element = [powerFunction.exponentExpression elementAtIndex:0];
if ([element isString]) {
NSString *exponent = [[((NSString *)element) componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] componentsJoinedByString:@""];
if ([exponent isEqualToString:@"-1"]) {
return YES;
}
}
}
}
if(powerToken) {
tokenStream.currentTokenIndex--;
}
return NO;
}
@end @end

View File

@@ -8,6 +8,8 @@
#import "MPExpressionLayout.h" #import "MPExpressionLayout.h"
#import "MPFunctionLayout.h" #import "MPFunctionLayout.h"
#import "MPPowerFunction.h"
#import "MPPowerFunctionLayout.h"
#import "NSString+MPExpressionElement.h" #import "NSString+MPExpressionElement.h"
#import "NSIndexPath+MPAdditions.h" #import "NSIndexPath+MPAdditions.h"
@@ -91,11 +93,11 @@
#pragma mark Drawing Methods #pragma mark Drawing Methods
- (NSRect)generateBounds - (NSRect)generateBounds
{ {
if (self.expression.numberOfElements == 0) { if (self.expression.countElements == 0) {
return NSMakeRect(0, kMPEmptyBoxYOrigin, kMPEmptyBoxWidth, kMPEmptyBoxHeight); return NSMakeRect(0, kMPEmptyBoxYOrigin, kMPEmptyBoxWidth, kMPEmptyBoxHeight);
} }
CGFloat x = 0, y = 0, width = 0, height = 0; CGFloat x = 0, y = 0, width = 0, height = 0;
for (NSUInteger index = 0; index < self.expression.numberOfElements; index++) { for (NSUInteger index = 0; index < self.expression.countElements; index++) {
NSRect elementBounds = [self boundsOfElementAtIndex:index]; NSRect elementBounds = [self boundsOfElementAtIndex:index];
width += elementBounds.size.width; width += elementBounds.size.width;
height = MAX(height, elementBounds.size.height); height = MAX(height, elementBounds.size.height);
@@ -107,7 +109,9 @@
- (NSRect)boundingRectForRange:(NSRange)range - (NSRect)boundingRectForRange:(NSRange)range
{ {
NSUInteger startOffset; NSUInteger startOffset;
NSUInteger startElementIndex = [self.expression indexOfElementAtLocation:range.location NSUInteger startElementIndex = [self.expression convertIndex:range.location
fromReferenceFrame:MPSymbolReferenceFrame
toReferenceFrame:MPElementReferenceFrame
offset:&startOffset]; offset:&startOffset];
// Calculate x position // Calculate x position
CGFloat x = 0, width = 0; CGFloat x = 0, width = 0;
@@ -122,7 +126,7 @@
x += xOffset; x += xOffset;
width += CTLineGetBoundsWithOptions(line, 0).size.width - xOffset; width += CTLineGetBoundsWithOptions(line, 0).size.width - xOffset;
CFRelease(line); CFRelease(line);
} else if (startElementIndex < self.expression.numberOfElements) { // Otherwise the selection is after the last symbol } else if (startElementIndex < self.expression.countElements) { // Otherwise the selection is after the last symbol
width += [self boundsOfElementAtIndex:startElementIndex].size.width; width += [self boundsOfElementAtIndex:startElementIndex].size.width;
} }
@@ -132,7 +136,9 @@
} }
NSUInteger endOffset; NSUInteger endOffset;
NSUInteger endElementIndex = [self.expression indexOfElementAtLocation:NSMaxRange(range) NSUInteger endElementIndex = [self.expression convertIndex:NSMaxRange(range)
fromReferenceFrame:MPSymbolReferenceFrame
toReferenceFrame:MPElementReferenceFrame
offset:&endOffset]; offset:&endOffset];
// Selection is inside of one string element // Selection is inside of one string element
@@ -171,7 +177,7 @@
- (NSIndexPath *)indexPathForMousePoint:(NSPoint)point - (NSIndexPath *)indexPathForMousePoint:(NSPoint)point
{ {
NSUInteger currentPosition = 0; NSUInteger currentPosition = 0;
for (NSUInteger index = 0; index < self.expression.numberOfElements; index++) { for (NSUInteger index = 0; index < self.expression.countElements; index++) {
NSRect elementBounds = [self boundsOfElementAtIndex:index]; NSRect elementBounds = [self boundsOfElementAtIndex:index];
NSPoint elementOffset = [self offsetOfChildLayoutAtIndex:index]; NSPoint elementOffset = [self offsetOfChildLayoutAtIndex:index];
elementBounds.origin.x += elementOffset.x; elementBounds.origin.x += elementOffset.x;
@@ -206,7 +212,7 @@
if (point.x < self.bounds.size.width / 2) { if (point.x < self.bounds.size.width / 2) {
return [NSIndexPath indexPathWithIndex:0]; return [NSIndexPath indexPathWithIndex:0];
} else { } else {
return [NSIndexPath indexPathWithIndex:self.expression.length]; return [NSIndexPath indexPathWithIndex:self.expression.countSymbols];
} }
} }
@@ -217,21 +223,8 @@
(CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
CGContextSaveGState(context); CGContextSaveGState(context);
#ifdef MPDEBUG_DRAW_ORIGIN
[[NSColor blueColor] set];
[[NSBezierPath bezierPathWithOvalInRect:NSMakeRect(-2, -2, 4, 4)] fill];
#endif
[[NSColor textColor] set]; [[NSColor textColor] set];
if (self.expression.countElements == 0) {
#ifdef MPDEBUG_DRAW_BOUNDS
[[NSColor greenColor] set];
[[NSBezierPath bezierPathWithRect:self.bounds] stroke];
[[NSColor textColor] set];
#endif
if (self.expression.numberOfElements == 0) {
CGContextRestoreGState(context); CGContextRestoreGState(context);
NSBezierPath *path = [NSBezierPath bezierPathWithRect:NSMakeRect(0, kMPEmptyBoxDrawingYOrigin, kMPEmptyBoxDrawingWidth, kMPEmptyBoxDrawingHeight)]; NSBezierPath *path = [NSBezierPath bezierPathWithRect:NSMakeRect(0, kMPEmptyBoxDrawingYOrigin, kMPEmptyBoxDrawingWidth, kMPEmptyBoxDrawingHeight)];
path.lineWidth = 0.5; path.lineWidth = 0.5;
@@ -245,7 +238,7 @@
// Track the x position // Track the x position
CGFloat x = 0; CGFloat x = 0;
for (NSUInteger index = 0; index < self.expression.numberOfElements; index++) { for (NSUInteger index = 0; index < self.expression.countElements; index++) {
// The current element // The current element
id element = [self.expression elementAtIndex:index]; id element = [self.expression elementAtIndex:index];
NSRect elementBounds = [self boundsOfElementAtIndex:index]; NSRect elementBounds = [self boundsOfElementAtIndex:index];
@@ -258,16 +251,15 @@
// Move to the appropriate position // Move to the appropriate position
CGContextSetTextPosition(context, x, 0); CGContextSetTextPosition(context, x, 0);
#ifdef MPDEBUG_DRAW_BASELINE
[[NSColor redColor] set];
NSRectFill(NSMakeRect(x, -1, elementBounds.size.width, 1));
[[NSColor textColor] set];
#endif
// Perform the drawing // Perform the drawing
CTLineDraw(line, context); CTLineDraw(line, context);
CFRelease(line); CFRelease(line);
if (index < self.expression.countElements-1 && [[self.expression elementAtIndex:index+1] isKindOfClass:[MPPowerFunction class]]) {
MPPowerFunctionLayout *layout = (MPPowerFunctionLayout *)[self childLayoutAtIndex:index+1];
layout.baseBounds = elementBounds;
}
} else { } else {
// Let the child layout draw itself // Let the child layout draw itself
MPLayout *layout = [self childLayoutAtIndex:index]; MPLayout *layout = [self childLayoutAtIndex:index];

View File

@@ -36,7 +36,7 @@
self.rootLayout.flipped = expressionView.isFlipped; self.rootLayout.flipped = expressionView.isFlipped;
} }
- (void)didChangeElementsInIndexedRangePath:(MPRangePath *)rangePath - (void)didChangeElementsInRangePath:(MPRangePath *)rangePath
replacementLength:(NSUInteger)replacementLength replacementLength:(NSUInteger)replacementLength
{ {
if (rangePath.location.length == 0) { if (rangePath.location.length == 0) {

View File

@@ -11,6 +11,6 @@
@interface MPExpressionTokenizer : NSObject @interface MPExpressionTokenizer : NSObject
- (NSArray *)tokenizeExpression:(MPExpression *)expression; // Returns MPToken's + (NSArray *)tokenizeExpression:(MPExpression *)expression; // Returns MPToken's
@end @end

View File

@@ -18,21 +18,25 @@
@implementation MPExpressionTokenizer @implementation MPExpressionTokenizer
- (NSArray *)tokenizeExpression:(MPExpression *)expression + (NSArray *)tokenizeExpression:(MPExpression *)expression
{ {
NSMutableArray *tokens = [[NSMutableArray alloc] init]; NSMutableArray *tokens = [[NSMutableArray alloc] init];
for (NSUInteger index = 0; index < expression.numberOfElements; index++) { NSUInteger symbolIndex = 0;
id <MPExpressionElement> element = [expression elementAtIndex:index]; for (NSUInteger index = 0; index < [expression countItemsInReferenceFrame:MPElementReferenceFrame]; index++) {
id <MPExpressionElement> element = [expression itemAtIndex:index referenceFrame:MPElementReferenceFrame];
if ([element isFunction]) { if ([element isFunction]) {
[tokens addObject:element]; [tokens addObject:element];
} else { } else {
[tokens addObjectsFromArray:[self tokenizeElement:(NSString *)element]]; [tokens addObjectsFromArray:[self tokenizeElement:(NSString *)element
elementSymbolIndex:symbolIndex]];
} }
symbolIndex += element.length;
} }
return tokens; return tokens;
} }
- (NSArray *)tokenizeElement:(NSString *)element + (NSArray *)tokenizeElement:(NSString *)element
elementSymbolIndex:(NSUInteger)symbolIndex
{ {
NSUInteger lexLocation = 0; NSUInteger lexLocation = 0;
@@ -40,7 +44,7 @@
NSString *regexStringFormat = @"\\A(?:" NSString *regexStringFormat = @"\\A(?:"
@"(\\*)|" @"(\\*)|"
@"([+-](?:\\s*[+-])*)|" @"([+-](?:\\s*[+-])*)|"
@"((?:\\d+(?:%@\\d+)?)|(?:\\s%@\\d+))|" @"((?:\\d+(?:%@\\d+)?)|(?:%@\\d+))|"
@"(sin)|" @"(sin)|"
@"(cos)|" @"(cos)|"
@"(tan)|" @"(tan)|"
@@ -109,9 +113,11 @@
} }
lexLocation = NSMaxRange(range); lexLocation = NSMaxRange(range);
NSString *tokenStringValue = [element substringWithRange:range];
range.location += symbolIndex;
[tokens addObject:[[MPToken alloc] initWithTokenType:tokenType [tokens addObject:[[MPToken alloc] initWithTokenType:tokenType
range:range range:range
inString:element]]; stringValue:tokenStringValue]];
} }
return tokens; return tokens;

View File

@@ -24,9 +24,6 @@
@property (readonly, nonatomic, strong) MPExpressionStorage *expressionStorage; @property (readonly, nonatomic, strong) MPExpressionStorage *expressionStorage;
// @property (nonatomic, getter = isEditable) BOOL editable;
@property (nonatomic) BOOL allowsIntelligentReplacements;
@property (nonatomic, strong) MPRangePath *selection; @property (nonatomic, strong) MPRangePath *selection;
@property (nonatomic, strong) MPParseError *error; @property (nonatomic, strong) MPParseError *error;
@@ -34,6 +31,7 @@
@property (nonatomic) SEL action; @property (nonatomic) SEL action;
#pragma mark Actions #pragma mark Actions
- (IBAction)switchRadiansDegrees:(id)sender;
- (IBAction)showFunctions:(id)sender; - (IBAction)showFunctions:(id)sender;
@end @end

View File

@@ -11,8 +11,11 @@
#import "MPExpressionLayout.h" #import "MPExpressionLayout.h"
#import "MPFunctionLayout.h" #import "MPFunctionLayout.h"
#import "MPPowerFunction.h"
#import "MPRangePath.h" #import "MPRangePath.h"
#import "MPMathRules.h"
#import "NSIndexPath+MPAdditions.h" #import "NSIndexPath+MPAdditions.h"
#import "MPSumFunction.h" #import "MPSumFunction.h"
@@ -24,10 +27,14 @@
@interface MPExpressionView () @interface MPExpressionView ()
@property (nonatomic, strong) NSButton *radiansDegreesButton;
@property (nonatomic, strong) NSButton *functionsButton; @property (nonatomic, strong) NSButton *functionsButton;
@property (nonatomic, strong) NSPopover *functionsPopover; @property (nonatomic, strong) NSPopover *functionsPopover;
@property (nonatomic, strong) MPFunctionsViewController *functionsViewController; @property (nonatomic, strong) MPFunctionsViewController *functionsViewController;
@property (nonatomic, strong) NSTextField *errorLabel;
@property (nonatomic, strong) NSTimer *caretTimer; @property (nonatomic, strong) NSTimer *caretTimer;
@property (nonatomic) NSTimeInterval caretBlinkRate; @property (nonatomic) NSTimeInterval caretBlinkRate;
@property (nonatomic) BOOL caretVisible; @property (nonatomic) BOOL caretVisible;
@@ -117,20 +124,22 @@
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:targetExpressionPath]; MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:targetExpressionPath];
NSUInteger locationInTarget = selectionPath.lastIndex; NSUInteger locationInTarget = selectionPath.lastIndex;
NSUInteger locationInElement; NSUInteger locationInElement;
NSUInteger targetElementIndex = [targetExpression indexOfElementAtLocation:locationInTarget NSUInteger targetElementIndex = [targetExpression convertIndex:locationInTarget
fromReferenceFrame:MPSymbolReferenceFrame
toReferenceFrame:MPElementReferenceFrame
offset:&locationInElement]; offset:&locationInElement];
id<MPExpressionElement> targetElement; id<MPExpressionElement> targetElement;
// There is only a target element if the selection is not the last location in an expression // There is only a target element if the selection is not the last location in an expression
if (targetElementIndex < targetExpression.numberOfElements) { if (targetElementIndex < targetExpression.countElements) {
targetElement = [targetExpression elementAtIndex:targetElementIndex]; targetElement = [targetExpression elementAtIndex:targetElementIndex];
} }
if (!selectWords && !extendingSelection && (locationInElement == 0 || locationInTarget == targetExpression.length)) { if (!selectWords && !extendingSelection && (locationInElement == 0 || locationInTarget == targetExpression.countSymbols)) {
// First or last index in an element or expression // First or last index in an element or expression
// Last element in the expression // Last element in the expression
if (locationInTarget == targetExpression.length) { if (locationInTarget == targetExpression.countSymbols) {
// The selection is inside a function and should proceed // The selection is inside a function and should proceed
if (selectionPath.length > 1) { if (selectionPath.length > 1) {
NSIndexPath *functionPath = [[selectionPath indexPathByRemovingLastIndex] indexPathByRemovingLastIndex]; NSIndexPath *functionPath = [[selectionPath indexPathByRemovingLastIndex] indexPathByRemovingLastIndex];
@@ -141,7 +150,9 @@
// The function is to be exited // The function is to be exited
if (newChildIndex == NSNotFound) { if (newChildIndex == NSNotFound) {
targetExpression = [self.expressionStorage elementAtIndexPath:[functionPath indexPathByRemovingLastIndex]]; targetExpression = [self.expressionStorage elementAtIndexPath:[functionPath indexPathByRemovingLastIndex]];
NSUInteger functionLocationInExpression = [targetExpression locationOfElementAtIndex:functionPath.lastIndex]; NSUInteger functionLocationInExpression = [targetExpression convertIndex:functionPath.lastIndex
fromReferenceFrame:MPElementReferenceFrame
toReferenceFrame:MPSymbolReferenceFrame];
return [functionPath indexPathByReplacingLastIndexWithIndex:functionLocationInExpression+1]; return [functionPath indexPathByReplacingLastIndexWithIndex:functionLocationInExpression+1];
} else { } else {
return [[targetExpressionPath indexPathByReplacingLastIndexWithIndex:newChildIndex] indexPathByAddingIndex:0]; return [[targetExpressionPath indexPathByReplacingLastIndexWithIndex:newChildIndex] indexPathByAddingIndex:0];
@@ -159,9 +170,11 @@
return [[targetFunctionPath indexPathByAddingIndex:leadingChildIndex] indexPathByAddingIndex:0]; return [[targetFunctionPath indexPathByAddingIndex:leadingChildIndex] indexPathByAddingIndex:0];
} }
} }
} else if (locationInTarget < targetExpression.length) { } else if (locationInTarget < targetExpression.countSymbols) {
if (selectWords) { if (selectWords) {
locationInTarget = [targetExpression locationOfElementAtIndex:targetElementIndex+1]; locationInTarget = [targetExpression convertIndex:targetElementIndex+1
fromReferenceFrame:MPElementReferenceFrame
toReferenceFrame:MPSymbolReferenceFrame];
} else { } else {
locationInTarget++; locationInTarget++;
} }
@@ -177,7 +190,9 @@
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:targetExpressionPath]; MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:targetExpressionPath];
NSUInteger locationInTarget = selectionPath.lastIndex; NSUInteger locationInTarget = selectionPath.lastIndex;
NSUInteger locationInElement; NSUInteger locationInElement;
NSUInteger targetElementIndex = [targetExpression indexOfElementAtLocation:locationInTarget NSUInteger targetElementIndex = [targetExpression convertIndex:locationInTarget
fromReferenceFrame:MPSymbolReferenceFrame
toReferenceFrame:MPElementReferenceFrame
offset:&locationInElement]; offset:&locationInElement];
NSUInteger previousElementIndex = targetElementIndex - (locationInElement == 0 ? 1 : 0); NSUInteger previousElementIndex = targetElementIndex - (locationInElement == 0 ? 1 : 0);
@@ -198,12 +213,14 @@
// The function is to be exited // The function is to be exited
if (newChildIndex == NSNotFound) { if (newChildIndex == NSNotFound) {
targetExpression = [self.expressionStorage elementAtIndexPath:[functionPath indexPathByRemovingLastIndex]]; targetExpression = [self.expressionStorage elementAtIndexPath:[functionPath indexPathByRemovingLastIndex]];
NSUInteger functionLocationInExpression = [targetExpression locationOfElementAtIndex:functionPath.lastIndex]; NSUInteger functionLocationInExpression = [targetExpression convertIndex:functionPath.lastIndex
fromReferenceFrame:MPElementReferenceFrame
toReferenceFrame:MPSymbolReferenceFrame];
return [functionPath indexPathByReplacingLastIndexWithIndex:functionLocationInExpression]; return [functionPath indexPathByReplacingLastIndexWithIndex:functionLocationInExpression];
} else { } else {
targetExpressionPath = [targetExpressionPath indexPathByReplacingLastIndexWithIndex:newChildIndex]; targetExpressionPath = [targetExpressionPath indexPathByReplacingLastIndexWithIndex:newChildIndex];
targetExpression = [self.expressionStorage elementAtIndexPath:targetExpressionPath]; targetExpression = [self.expressionStorage elementAtIndexPath:targetExpressionPath];
return [targetExpressionPath indexPathByAddingIndex:targetExpression.length]; return [targetExpressionPath indexPathByAddingIndex:targetExpression.countSymbols];
} }
} // else the selection does not change } // else the selection does not change
@@ -218,7 +235,7 @@
targetExpressionPath = [targetFunctionPath indexPathByAddingIndex:trailingChildIndex]; targetExpressionPath = [targetFunctionPath indexPathByAddingIndex:trailingChildIndex];
targetExpression = [self.expressionStorage elementAtIndexPath:targetExpressionPath]; targetExpression = [self.expressionStorage elementAtIndexPath:targetExpressionPath];
return [targetExpressionPath indexPathByAddingIndex:targetExpression.length]; return [targetExpressionPath indexPathByAddingIndex:targetExpression.countSymbols];
} }
} }
} else if (locationInTarget > 0) { } else if (locationInTarget > 0) {
@@ -226,7 +243,9 @@
if (locationInElement == 0) { if (locationInElement == 0) {
targetElementIndex--; targetElementIndex--;
} }
locationInTarget = [targetExpression locationOfElementAtIndex:targetElementIndex]; locationInTarget = [targetExpression convertIndex:targetElementIndex
fromReferenceFrame:MPElementReferenceFrame
toReferenceFrame:MPSymbolReferenceFrame];
} else { } else {
locationInTarget--; locationInTarget--;
} }
@@ -258,10 +277,14 @@
NSUInteger newIndex = [newSelectionPath indexAtPosition:commonPath.length]; NSUInteger newIndex = [newSelectionPath indexAtPosition:commonPath.length];
if (commonPath.length < anchorPath.length-1) { if (commonPath.length < anchorPath.length-1) {
anchorIndex = [closestCommonAncestor locationOfElementAtIndex:anchorIndex]; anchorIndex = [closestCommonAncestor convertIndex:anchorIndex
fromReferenceFrame:MPElementReferenceFrame
toReferenceFrame:MPSymbolReferenceFrame];
} }
if (commonPath.length < newSelectionPath.length-1) { if (commonPath.length < newSelectionPath.length-1) {
newIndex = [closestCommonAncestor locationOfElementAtIndex:newIndex]; newIndex = [closestCommonAncestor convertIndex:newIndex
fromReferenceFrame:MPElementReferenceFrame
toReferenceFrame:MPSymbolReferenceFrame];
} }
NSUInteger minIndex = MIN(anchorIndex, newIndex); NSUInteger minIndex = MIN(anchorIndex, newIndex);
@@ -313,6 +336,7 @@
_expressionStorage = expressionStorage; _expressionStorage = expressionStorage;
[self initializeButtons]; [self initializeButtons];
[self initializeErrorLabel];
self.selection = [[MPRangePath alloc] initWithRange:NSMakeRange(0, 0)]; self.selection = [[MPRangePath alloc] initWithRange:NSMakeRange(0, 0)];
self.caretBlinkRate = 1.0; self.caretBlinkRate = 1.0;
@@ -321,10 +345,23 @@
- (void)initializeButtons - (void)initializeButtons
{ {
NSButton *radiansDegreesButton = self.radiansDegreesButton;
[self addSubview:radiansDegreesButton];
NSButton *functionsButton = self.functionsButton; NSButton *functionsButton = self.functionsButton;
[self addSubview:functionsButton]; [self addSubview:functionsButton];
NSDictionary *variableBindings = NSDictionaryOfVariableBindings(functionsButton);
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"[functionsButton]-10-|" NSDictionary *variableBindings = NSDictionaryOfVariableBindings(radiansDegreesButton, functionsButton);
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-10-[radiansDegreesButton]"
options:0
metrics:nil
views:variableBindings]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-10-[radiansDegreesButton]"
options:0
metrics:nil
views:variableBindings]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[functionsButton]-10-|"
options:0 options:0
metrics:nil metrics:nil
views:variableBindings]]; views:variableBindings]];
@@ -337,6 +374,21 @@
constant:0]]; constant:0]];
} }
- (void)initializeErrorLabel
{
NSTextField *errorLabel = self.errorLabel;
[self addSubview:errorLabel];
NSDictionary *variableBindings = NSDictionaryOfVariableBindings(errorLabel);
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-10-[errorLabel]"
options:0
metrics:nil
views:variableBindings]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[errorLabel]-10-|"
options:0
metrics:nil
views:variableBindings]];
}
#pragma mark Properties #pragma mark Properties
- (void)setExpressionStorage:(MPExpressionStorage *)expressionStorage - (void)setExpressionStorage:(MPExpressionStorage *)expressionStorage
{ {
@@ -356,16 +408,36 @@
- (void)setError:(MPParseError *)error - (void)setError:(MPParseError *)error
{ {
_error = error; _error = error;
self.errorLabel.stringValue = error.localizedErrorMessage != nil ? error.localizedErrorMessage : @"";
self.needsDisplay = YES; self.needsDisplay = YES;
} }
- (NSButton *)radiansDegreesButton
{
if (!_radiansDegreesButton) {
NSButton *radiansDegreesButton = [[NSButton alloc] initWithFrame:NSZeroRect];
radiansDegreesButton.translatesAutoresizingMaskIntoConstraints = NO;
radiansDegreesButton.buttonType = NSMomentaryPushInButton;
radiansDegreesButton.bordered = YES;
radiansDegreesButton.bezelStyle = NSRoundedBezelStyle;
radiansDegreesButton.imagePosition = NSNoImage;
radiansDegreesButton.alignment = NSCenterTextAlignment;
radiansDegreesButton.title = NSLocalizedString([MPMathRules sharedRules].isUsingDegrees ? @"Deg" : @"Rad", nil);
radiansDegreesButton.font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]];
radiansDegreesButton.target = self;
radiansDegreesButton.action = @selector(switchRadiansDegrees:);
_radiansDegreesButton = radiansDegreesButton;
}
return _radiansDegreesButton;
}
- (NSButton *)functionsButton - (NSButton *)functionsButton
{ {
if (!_functionsButton) { if (!_functionsButton) {
NSBundle *frameworkBundle = [NSBundle bundleForClass:[self class]]; NSBundle *frameworkBundle = [NSBundle bundleForClass:[self class]];
NSImage *image = [frameworkBundle imageForResource:@"FunctionsButtonDisclosure"]; NSImage *image = [frameworkBundle imageForResource:@"FunctionsButtonDisclosure"];
[image setName:@"FunctionsButtonDisclosure"]; [image setName:@"FunctionsButtonDisclosure"];
NSButton *button = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]; NSButton *button = [[NSButton alloc] initWithFrame:NSZeroRect];
button.translatesAutoresizingMaskIntoConstraints = NO; button.translatesAutoresizingMaskIntoConstraints = NO;
button.target = self; button.target = self;
button.action = @selector(showFunctions:); button.action = @selector(showFunctions:);
@@ -382,7 +454,28 @@
return _functionsButton; return _functionsButton;
} }
- (NSTextField *)errorLabel
{
if (!_errorLabel) {
NSTextField *label = [[NSTextField alloc] initWithFrame:NSZeroRect];
label.textColor = [NSColor redColor];
label.translatesAutoresizingMaskIntoConstraints = NO;
label.bezeled = NO;
label.drawsBackground = NO;
label.editable = NO;
label.selectable = NO;
_errorLabel = label;
}
return _errorLabel;
}
#pragma mark Actions #pragma mark Actions
- (void)switchRadiansDegrees:(id)sender
{
[MPMathRules sharedRules].isUsingDegrees = ![MPMathRules sharedRules].isUsingDegrees;
self.radiansDegreesButton.title = NSLocalizedString([MPMathRules sharedRules].isUsingDegrees ? @"Deg" : @"Rad", nil);
}
- (void)showFunctions:(id)sender - (void)showFunctions:(id)sender
{ {
if (self.functionsPopover == nil || self.functionsViewController == nil) { if (self.functionsPopover == nil || self.functionsViewController == nil) {
@@ -404,10 +497,14 @@
- (void)insertFunction:(MPFunction *)function - (void)insertFunction:(MPFunction *)function
{ {
[self.functionsPopover close]; [self.functionsPopover close];
[self.expressionStorage replaceSymbolsInRangePath:self.selection withElements:@[function]]; [self.expressionStorage replaceItemsInRangePath:self.selection
referenceFrame:MPSymbolReferenceFrame
withElements:@[function]];
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]]; MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
NSIndexPath *functionPath = [self.selection.location indexPathByReplacingLastIndexWithIndex:[targetExpression indexOfElementAtLocation:self.selection.location.lastIndex NSUInteger functionElementIndex = [targetExpression convertIndex:self.selection.location.lastIndex
offset:NULL]]; fromReferenceFrame:MPSymbolReferenceFrame
toReferenceFrame:MPElementReferenceFrame];
NSIndexPath *functionPath = [self.selection.location indexPathByReplacingLastIndexWithIndex:functionElementIndex];
MPFunctionLayout *functionLayout = (MPFunctionLayout *)[self.expressionStorage.rootLayout childLayoutAtIndexPath:functionPath]; MPFunctionLayout *functionLayout = (MPFunctionLayout *)[self.expressionStorage.rootLayout childLayoutAtIndexPath:functionPath];
self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:functionLayout.indexOfLeadingChild] indexPathByAddingIndex:0], 0); self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:functionLayout.indexOfLeadingChild] indexPathByAddingIndex:0], 0);
} }
@@ -475,15 +572,19 @@
[self insertParenthesisFunction:nil]; [self insertParenthesisFunction:nil];
return; return;
} }
if (theEvent.keyCode == 10) {
[self insertPowerFunction];
return;
}
NSString *decimalSeparator = [NSRegularExpression escapedPatternForString:[[NSLocale currentLocale] objectForKey:NSLocaleDecimalSeparator]]; NSString *decimalSeparator = [NSRegularExpression escapedPatternForString:[[NSLocale currentLocale] objectForKey:NSLocaleDecimalSeparator]];
NSMutableCharacterSet *allowedCharacters = [NSMutableCharacterSet alphanumericCharacterSet]; NSMutableCharacterSet *allowedCharacters = [NSMutableCharacterSet alphanumericCharacterSet];
[allowedCharacters addCharactersInString:[NSString stringWithFormat:@"+-*= !%@", decimalSeparator]]; [allowedCharacters addCharactersInString:[NSString stringWithFormat:@"+-*= !%@", decimalSeparator]];
if (characters.length == 1 && [characters stringByTrimmingCharactersInSet:allowedCharacters].length == 0) { if (characters.length == 1 && [characters stringByTrimmingCharactersInSet:allowedCharacters].length == 0) {
[self.expressionStorage replaceSymbolsInRangePath:self.selection withElements:@[characters]]; [self.expressionStorage replaceItemsInRangePath:self.selection
referenceFrame:MPSymbolReferenceFrame
withElements:@[characters]];
self.selection = MPMakeRangePath([self.selection.location indexPathByIncrementingLastIndex], 0); self.selection = MPMakeRangePath([self.selection.location indexPathByIncrementingLastIndex], 0);
} else { } else {
[self interpretKeyEvents:@[theEvent]]; [self interpretKeyEvents:@[theEvent]];
@@ -492,15 +593,37 @@
- (void)insertParenthesisFunction:(id)sender - (void)insertParenthesisFunction:(id)sender
{ {
MPExpression *selectedElementsExpression = [self.expressionStorage subexpressionWithRangePath:self.selection]; MPExpression *selectedElementsExpression = [self.expressionStorage subexpressionWithRangePath:self.selection
referenceFrame:MPSymbolReferenceFrame];
MPParenthesisFunction *function = [[MPParenthesisFunction alloc] init]; MPParenthesisFunction *function = [[MPParenthesisFunction alloc] init];
function.expression = selectedElementsExpression; function.expression = selectedElementsExpression;
[self.expressionStorage replaceSymbolsInRangePath:self.selection [self.expressionStorage replaceItemsInRangePath:self.selection
referenceFrame:MPSymbolReferenceFrame
withElements:@[function]]; withElements:@[function]];
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]]; MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
NSIndexPath *functionPath = [self.selection.location indexPathByReplacingLastIndexWithIndex:[targetExpression indexOfElementAtLocation:self.selection.location.lastIndex NSUInteger functionElementIndex = [targetExpression convertIndex:self.selection.location.lastIndex
offset:NULL]]; fromReferenceFrame:MPSymbolReferenceFrame
toReferenceFrame:MPElementReferenceFrame];
NSIndexPath *functionPath = [self.selection.location indexPathByReplacingLastIndexWithIndex:functionElementIndex];
self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:0] indexPathByAddingIndex:0], self.selection.length);
}
- (void)insertPowerFunction
{
MPExpression *selectedElementsExpression = [self.expressionStorage subexpressionWithRangePath:self.selection
referenceFrame:MPSymbolReferenceFrame];
MPPowerFunction *function = [[MPPowerFunction alloc] init];
function.exponentExpression = selectedElementsExpression;
[self.expressionStorage replaceItemsInRangePath:self.selection
referenceFrame:MPSymbolReferenceFrame
withElements:@[function]];
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
NSUInteger functionElementIndex = [targetExpression convertIndex:self.selection.location.lastIndex
fromReferenceFrame:MPSymbolReferenceFrame
toReferenceFrame:MPElementReferenceFrame];
NSIndexPath *functionPath = [self.selection.location indexPathByReplacingLastIndexWithIndex:functionElementIndex];
self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:0] indexPathByAddingIndex:0], self.selection.length); self.selection = MPMakeRangePath([[functionPath indexPathByAddingIndex:0] indexPathByAddingIndex:0], self.selection.length);
} }
@@ -563,7 +686,7 @@
- (void)moveToEndOfLine:(id)sender - (void)moveToEndOfLine:(id)sender
{ {
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]]; MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
self.selection = MPMakeRangePath([self.selection.location indexPathByReplacingLastIndexWithIndex:targetExpression.length], 0); self.selection = MPMakeRangePath([self.selection.location indexPathByReplacingLastIndexWithIndex:targetExpression.countSymbols], 0);
} }
- (void)moveLeftAndModifySelection:(id)sender - (void)moveLeftAndModifySelection:(id)sender
@@ -676,22 +799,27 @@
- (void)selectAll:(id)sender - (void)selectAll:(id)sender
{ {
NSIndexPath *location = [NSIndexPath indexPathWithIndex:0]; NSIndexPath *location = [NSIndexPath indexPathWithIndex:0];
self.selection = MPMakeRangePath(location, self.expressionStorage.length); self.selection = MPMakeRangePath(location, self.expressionStorage.countSymbols);
} }
- (void)deleteBackward:(id)sender - (void)deleteBackward:(id)sender
{ {
if (self.selection.length > 0) { if (self.selection.length > 0) {
[self.expressionStorage replaceSymbolsInRangePath:self.selection withElements:@[]]; [self.expressionStorage replaceItemsInRangePath:self.selection
referenceFrame:MPSymbolReferenceFrame
withElements:@[]];
self.selection = MPMakeRangePath(self.selection.location, 0); self.selection = MPMakeRangePath(self.selection.location, 0);
} else if (self.selection.location.lastIndex > 0) { } else if (self.selection.location.lastIndex > 0) {
MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]]; MPExpression *targetExpression = [self.expressionStorage elementAtIndexPath:[self.selection.location indexPathByRemovingLastIndex]];
id <MPExpressionElement> elementToDelete = [targetExpression elementAtIndex:[targetExpression indexOfElementAtLocation:self.selection.location.lastIndex-1 id<MPExpressionElement> elementToDelete = [targetExpression elementAtIndex:[targetExpression convertIndex:self.selection.location.lastIndex-1
offset:NULL]]; fromReferenceFrame:MPSymbolReferenceFrame
toReferenceFrame:MPElementReferenceFrame]];
if ([elementToDelete isFunction]) { if ([elementToDelete isFunction]) {
self.selection = MPMakeRangePath(self.selection.location.indexPathByDecrementingLastIndex, 1); self.selection = MPMakeRangePath(self.selection.location.indexPathByDecrementingLastIndex, 1);
} else { } else {
[targetExpression replaceSymbolsInRange:NSMakeRange(self.selection.location.lastIndex-1, 1) withElements:@[]]; [targetExpression replaceItemsInRange:NSMakeRange(self.selection.location.lastIndex-1, 1)
referenceFrame:MPSymbolReferenceFrame
withElements:@[]];
self.selection = MPMakeRangePath([self.selection.location indexPathByDecrementingLastIndex], 0); self.selection = MPMakeRangePath([self.selection.location indexPathByDecrementingLastIndex], 0);
} }
} else { } else {
@@ -707,14 +835,20 @@
for (NSUInteger index = remainingIndexes.firstIndex; for (NSUInteger index = remainingIndexes.firstIndex;
index <= remainingIndexes.lastIndex; index <= remainingIndexes.lastIndex;
index = [remainingIndexes indexGreaterThanIndex:index]) { index = [remainingIndexes indexGreaterThanIndex:index]) {
[remainder addObjectsFromArray:[function childAtIndex:index].elements]; MPExpression *expression = [function childAtIndex:index];
[remainder addObjectsFromArray:[expression allItemsInReferenceFrame:MPElementReferenceFrame]];
} }
NSIndexPath *newTargetExpressionPath = [functionPath indexPathByRemovingLastIndex]; NSIndexPath *newTargetExpressionPath = [functionPath indexPathByRemovingLastIndex];
MPExpression *newTargetExpression = [self.expressionStorage elementAtIndexPath:newTargetExpressionPath]; MPExpression *newTargetExpression = [self.expressionStorage elementAtIndexPath:newTargetExpressionPath];
NSIndexPath *newSelectionLocation = [functionPath indexPathByReplacingLastIndexWithIndex:[newTargetExpression locationOfElementAtIndex:functionPath.lastIndex]]; NSUInteger newSelectionElementIndex = [newTargetExpression convertIndex:functionPath.lastIndex
fromReferenceFrame:MPElementReferenceFrame
toReferenceFrame:MPSymbolReferenceFrame];
NSIndexPath *newSelectionLocation = [functionPath indexPathByReplacingLastIndexWithIndex:newSelectionElementIndex];
[self.expressionStorage replaceSymbolsInRangePath:MPMakeRangePath(newSelectionLocation, 1) withElements:remainder]; [self.expressionStorage replaceItemsInRangePath:MPMakeRangePath(newSelectionLocation, 1)
referenceFrame:MPSymbolReferenceFrame
withElements:remainder];
self.selection = MPMakeRangePath(newSelectionLocation, 0); self.selection = MPMakeRangePath(newSelectionLocation, 0);
} }
} }
@@ -762,6 +896,24 @@
yBy:expressionOrigin.y]; yBy:expressionOrigin.y];
[transform concat]; [transform concat];
// Draw the error
if (self.error) {
[[NSColor redColor] set];
NSRect rect = [self.expressionStorage.rootLayout boundingRectForRangePath:self.error.rangePath];
if (self.error.rangePath.length == 0) {
NSBezierPath *bezierPath = [NSBezierPath bezierPath];
[bezierPath moveToPoint:rect.origin];
[bezierPath lineToPoint:NSMakePoint(rect.origin.x - 5, rect.origin.y - 5)];
[bezierPath moveToPoint:rect.origin];
[bezierPath lineToPoint:NSMakePoint(rect.origin.x + 5, rect.origin.y - 5)];
bezierPath.lineWidth = 2.0;
[bezierPath stroke];
} else {
NSRect underlineRect = NSMakeRect(rect.origin.x, rect.origin.y + 2, rect.size.width, 2);
NSRectFill(underlineRect);
}
}
// Draw the selection // Draw the selection
if (self.caretVisible || self.selection.length > 0) { if (self.caretVisible || self.selection.length > 0) {
if (self.selection.length == 0) { if (self.selection.length == 0) {

View File

@@ -18,7 +18,10 @@
- (NSRange)range - (NSRange)range
{ {
NSUInteger selfIndex = [self.parent indexOfElement:self]; NSUInteger selfIndex = [self.parent indexOfElement:self];
return NSMakeRange([self.parent locationOfElementAtIndex:selfIndex], 1); return NSMakeRange([self.parent convertIndex:selfIndex
fromReferenceFrame:MPElementReferenceFrame
toReferenceFrame:MPSymbolReferenceFrame],
1);
} }
- (BOOL)exists - (BOOL)exists

View File

@@ -99,7 +99,7 @@
{ {
NSUInteger selfIndex = [self.parent indexOfElement:self]; NSUInteger selfIndex = [self.parent indexOfElement:self];
MPRangePath *newPath = MPMakeRangePath([rangePath.location indexPathByPreceedingIndex:selfIndex], rangePath.length); MPRangePath *newPath = MPMakeRangePath([rangePath.location indexPathByPreceedingIndex:selfIndex], rangePath.length);
[self.parent didChangeElementsInIndexedRangePath:newPath [self.parent didChangeElementsInRangePath:newPath
replacementLength:replacementLength]; replacementLength:replacementLength];
} }

View File

@@ -14,6 +14,7 @@
#import "MPSumFunctionLayout.h" #import "MPSumFunctionLayout.h"
#import "MPParenthesisFunction.h" #import "MPParenthesisFunction.h"
#import "MPParenthesisFunctionLayout.h" #import "MPParenthesisFunctionLayout.h"
#import "MPPowerFunctionLayout.h"
#import "NSIndexPath+MPAdditions.h" #import "NSIndexPath+MPAdditions.h"
@@ -28,6 +29,8 @@
return [[MPSumFunctionLayout alloc] initWithFunction:function parent:parent]; return [[MPSumFunctionLayout alloc] initWithFunction:function parent:parent];
} else if (class == [MPParenthesisFunction class]) { } else if (class == [MPParenthesisFunction class]) {
return [[MPParenthesisFunctionLayout alloc] initWithFunction:function parent:parent]; return [[MPParenthesisFunctionLayout alloc] initWithFunction:function parent:parent];
} else if (class == [MPPowerFunction class]) {
return [[MPPowerFunctionLayout alloc] initWithFunction:function parent:parent];
} }
return [[self alloc] initWithFunction:function return [[self alloc] initWithFunction:function
parent:parent]; parent:parent];
@@ -101,6 +104,16 @@
return [self indexPathForLocalMousePoint:point]; return [self indexPathForLocalMousePoint:point];
} }
- (NSUInteger)indexOfLeadingChild
{
return 0;
}
- (NSUInteger)indexOfTrailingChild
{
return 0;
}
- (NSUInteger)indexOfChildBeforeChildAtIndex:(NSUInteger)index - (NSUInteger)indexOfChildBeforeChildAtIndex:(NSUInteger)index
{ {
return NSNotFound; return NSNotFound;

View File

@@ -12,6 +12,7 @@
#import "MPSumFunction.h" #import "MPSumFunction.h"
#import "MPParenthesisFunction.h" #import "MPParenthesisFunction.h"
#import "MPPowerFunction.h"
@class MPFunctionTemplateItem; @class MPFunctionTemplateItem;
@@ -197,11 +198,24 @@ static void *MPFunctionTemplateViewMouseOverContext = @"MPFunctionTemplateViewMo
- (void)awakeFromNib - (void)awakeFromNib
{ {
MPFunction *sumFunction = [[MPSumFunction alloc] init];
MPFunction *parenthesisFunction = [[MPParenthesisFunction alloc] init];
MPFunction *powerFunction = [[MPPowerFunction alloc] init];
MPPowerFunction *squareFunction = [[MPPowerFunction alloc] init];
squareFunction.exponentExpression = [[MPExpression alloc] initWithElement:@"2"];
MPPowerFunction *cubicFunction = [[MPPowerFunction alloc] init];
cubicFunction.exponentExpression = [[MPExpression alloc] initWithElement:@"3"];
self.functionPrototypes = @[ self.functionPrototypes = @[
@{@"function": [[MPSumFunction alloc] init], @{@"function": sumFunction,
@"name": NSLocalizedString(@"Sum", @"Sum Function Name")}, @"name": NSLocalizedString(@"Sum", @"Sum Function Name")},
@{@"function": [[MPParenthesisFunction alloc] init], @{@"function": parenthesisFunction,
@"name": NSLocalizedString(@"Parenthesis", @"Parenthesis Function Name")} @"name": NSLocalizedString(@"Parenthesis", @"Parenthesis Function Name")},
@{@"function": squareFunction,
@"name": NSLocalizedString(@"Square", @"Square Function Name")},
@{@"function": cubicFunction,
@"name": NSLocalizedString(@"Cubic", @"Cubic Function Name")},
@{@"function": powerFunction,
@"name": NSLocalizedString(@"Power", @"Power Function Name")}
]; ];
[self.collectionView addObserver:self [self.collectionView addObserver:self
forKeyPath:@"hoverItem" forKeyPath:@"hoverItem"

View File

@@ -12,6 +12,7 @@ FOUNDATION_EXPORT NSString *MPMathRulesAllowsImplicitMultiplicationKey;
FOUNDATION_EXPORT NSString *MPMathRulesMaximumOperatorChainLengthKey; FOUNDATION_EXPORT NSString *MPMathRulesMaximumOperatorChainLengthKey;
FOUNDATION_EXPORT NSString *MPMathRulesMaximumOperatorChainLengthInMultiplicationKey; FOUNDATION_EXPORT NSString *MPMathRulesMaximumOperatorChainLengthInMultiplicationKey;
FOUNDATION_EXPORT NSString *MPMathRulesMaximumOperatorChainLengthInFunctionKey; FOUNDATION_EXPORT NSString *MPMathRulesMaximumOperatorChainLengthInFunctionKey;
FOUNDATION_EXPORT NSString *MPMathRulesIsUsingDegreesKey;
@interface MPMathRules : NSObject @interface MPMathRules : NSObject
@@ -25,4 +26,6 @@ FOUNDATION_EXPORT NSString *MPMathRulesMaximumOperatorChainLengthInFunctionKey;
@property (nonatomic) NSUInteger maximumOperatorChainLengthInMultiplication; // Default: 1, 0 means actually 0 @property (nonatomic) NSUInteger maximumOperatorChainLengthInMultiplication; // Default: 1, 0 means actually 0
@property (nonatomic) NSUInteger maximumOperatorChainLengthInFunction; // For sin, cos, tan. Default: 1 @property (nonatomic) NSUInteger maximumOperatorChainLengthInFunction; // For sin, cos, tan. Default: 1
@property (nonatomic) BOOL isUsingDegrees;
@end @end

View File

@@ -12,6 +12,7 @@ NSString *MPMathRulesAllowsImplicitMultiplicationKey = @"MPMathRulesAllowsImplic
NSString *MPMathRulesMaximumOperatorChainLengthKey = @"MPMathRulesMaximumOperatorChainLengthKey"; NSString *MPMathRulesMaximumOperatorChainLengthKey = @"MPMathRulesMaximumOperatorChainLengthKey";
NSString *MPMathRulesMaximumOperatorChainLengthInMultiplicationKey = @"MPMathRulesMaximumOperatorChainLengthInMultiplicationKey"; NSString *MPMathRulesMaximumOperatorChainLengthInMultiplicationKey = @"MPMathRulesMaximumOperatorChainLengthInMultiplicationKey";
NSString *MPMathRulesMaximumOperatorChainLengthInFunctionKey = @"MPMathRulesMaximumOperatorChainLengthInFunctionKey"; NSString *MPMathRulesMaximumOperatorChainLengthInFunctionKey = @"MPMathRulesMaximumOperatorChainLengthInFunctionKey";
NSString *MPMathRulesIsUsingDegreesKey = @"MPMathRulesIsUsingDegreesKey";
@implementation MPMathRules @implementation MPMathRules
@@ -33,15 +34,18 @@ static MPMathRules *sharedRules;
self = [super init]; self = [super init];
if (self) { if (self) {
_usingUserDefaultValues = YES; _usingUserDefaultValues = YES;
NSNumber *userDefaultsAllowImplicitMultiplication = [[NSUserDefaults standardUserDefaults] objectForKey:MPMathRulesAllowsImplicitMultiplicationKey]; NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSNumber *userDefaultsMaximumOperatorChainLength = [[NSUserDefaults standardUserDefaults] objectForKey:MPMathRulesMaximumOperatorChainLengthKey]; NSNumber *userDefaultsAllowImplicitMultiplication = [userDefaults objectForKey:MPMathRulesAllowsImplicitMultiplicationKey];
NSNumber *userDefaultsMaximumOperatorChainLengthInMultiplication = [[NSUserDefaults standardUserDefaults] objectForKey:MPMathRulesMaximumOperatorChainLengthInMultiplicationKey]; NSNumber *userDefaultsMaximumOperatorChainLength = [userDefaults objectForKey:MPMathRulesMaximumOperatorChainLengthKey];
NSNumber *userDefaultsMaximumOperatorChainLengthInFunction = [[NSUserDefaults standardUserDefaults] objectForKey:MPMathRulesMaximumOperatorChainLengthInFunctionKey]; NSNumber *userDefaultsMaximumOperatorChainLengthInMultiplication = [userDefaults objectForKey:MPMathRulesMaximumOperatorChainLengthInMultiplicationKey];
NSNumber *userDefaultsMaximumOperatorChainLengthInFunction = [userDefaults objectForKey:MPMathRulesMaximumOperatorChainLengthInFunctionKey];
NSNumber *userDefaultsIsUsingDegrees = [userDefaults objectForKey:MPMathRulesIsUsingDegreesKey];
_allowsImplicitMultiplication = userDefaultsAllowImplicitMultiplication != nil ? userDefaultsAllowImplicitMultiplication.boolValue : NO; _allowsImplicitMultiplication = userDefaultsAllowImplicitMultiplication.boolValue;
_maximumOperatorChainLength = userDefaultsMaximumOperatorChainLength != nil ? userDefaultsMaximumOperatorChainLength.unsignedIntegerValue : 2; _maximumOperatorChainLength = userDefaultsMaximumOperatorChainLength != nil ? userDefaultsMaximumOperatorChainLength.unsignedIntegerValue : 2;
_maximumOperatorChainLengthInMultiplication = userDefaultsMaximumOperatorChainLengthInMultiplication != nil ? userDefaultsMaximumOperatorChainLengthInMultiplication.unsignedIntegerValue : 1; _maximumOperatorChainLengthInMultiplication = userDefaultsMaximumOperatorChainLengthInMultiplication != nil ? userDefaultsMaximumOperatorChainLengthInMultiplication.unsignedIntegerValue : 1;
_maximumOperatorChainLengthInFunction = userDefaultsMaximumOperatorChainLengthInFunction != nil ? userDefaultsMaximumOperatorChainLengthInFunction.unsignedIntegerValue : 1; _maximumOperatorChainLengthInFunction = userDefaultsMaximumOperatorChainLengthInFunction != nil ? userDefaultsMaximumOperatorChainLengthInFunction.unsignedIntegerValue : 1;
_isUsingDegrees = userDefaultsIsUsingDegrees.boolValue;
} }
return self; return self;
} }
@@ -54,6 +58,7 @@ static MPMathRules *sharedRules;
self.maximumOperatorChainLength = self.maximumOperatorChainLength; self.maximumOperatorChainLength = self.maximumOperatorChainLength;
self.maximumOperatorChainLengthInMultiplication = self.maximumOperatorChainLengthInMultiplication; self.maximumOperatorChainLengthInMultiplication = self.maximumOperatorChainLengthInMultiplication;
self.maximumOperatorChainLengthInFunction = self.maximumOperatorChainLengthInFunction; self.maximumOperatorChainLengthInFunction = self.maximumOperatorChainLengthInFunction;
self.isUsingDegrees = self.isUsingDegrees;
} }
- (void)setAllowsImplicitMultiplication:(BOOL)allowsImplicitMultiplication - (void)setAllowsImplicitMultiplication:(BOOL)allowsImplicitMultiplication
@@ -69,7 +74,7 @@ static MPMathRules *sharedRules;
{ {
_maximumOperatorChainLength = maximumOperatorChainLength; _maximumOperatorChainLength = maximumOperatorChainLength;
if (self.isUsingUserDefaultValues) { if (self.isUsingUserDefaultValues) {
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithUnsignedInteger:maximumOperatorChainLength] [[NSUserDefaults standardUserDefaults] setObject:@(maximumOperatorChainLength)
forKey:MPMathRulesMaximumOperatorChainLengthKey]; forKey:MPMathRulesMaximumOperatorChainLengthKey];
} }
} }
@@ -78,7 +83,7 @@ static MPMathRules *sharedRules;
{ {
_maximumOperatorChainLengthInMultiplication = maximumOperatorChainLengthInMultiplication; _maximumOperatorChainLengthInMultiplication = maximumOperatorChainLengthInMultiplication;
if (self.isUsingUserDefaultValues) { if (self.isUsingUserDefaultValues) {
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithUnsignedInteger:maximumOperatorChainLengthInMultiplication] [[NSUserDefaults standardUserDefaults] setObject:@(maximumOperatorChainLengthInMultiplication)
forKey:MPMathRulesMaximumOperatorChainLengthInMultiplicationKey]; forKey:MPMathRulesMaximumOperatorChainLengthInMultiplicationKey];
} }
} }
@@ -87,9 +92,18 @@ static MPMathRules *sharedRules;
{ {
_maximumOperatorChainLengthInFunction = maximumOperatorChainLengthInFunction; _maximumOperatorChainLengthInFunction = maximumOperatorChainLengthInFunction;
if (self.isUsingUserDefaultValues) { if (self.isUsingUserDefaultValues) {
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithUnsignedInteger:maximumOperatorChainLengthInFunction] [[NSUserDefaults standardUserDefaults] setObject:@(maximumOperatorChainLengthInFunction)
forKey:MPMathRulesMaximumOperatorChainLengthInFunctionKey]; forKey:MPMathRulesMaximumOperatorChainLengthInFunctionKey];
} }
} }
- (void)setIsUsingDegrees:(BOOL)isUsingDegrees
{
_isUsingDegrees = isUsingDegrees;
if (self.isUsingUserDefaultValues) {
[[NSUserDefaults standardUserDefaults] setBool:isUsingDegrees
forKey:MPMathRulesIsUsingDegreesKey];
}
}
@end @end

View File

@@ -14,13 +14,6 @@
MPFunctionAccessorImplementation(Expression, _expression) MPFunctionAccessorImplementation(Expression, _expression)
//- (void)setExpression:(MPExpression *)expression
//{
// _expression.parent = nil;
// _expression = expression;
// _expression.parent = self;
//}
- (NSArray *)childrenAccessors - (NSArray *)childrenAccessors
{ {
return @[@"expression"]; return @[@"expression"];
@@ -28,7 +21,7 @@ MPFunctionAccessorImplementation(Expression, _expression)
- (MPTerm *)parseWithError:(MPParseError *__autoreleasing *)error - (MPTerm *)parseWithError:(MPParseError *__autoreleasing *)error
{ {
MPExpressionEvaluator *evaluator = self.expression.evaluator; MPExpressionEvaluator *evaluator = [[MPExpressionEvaluator alloc] initWithExpression:self.expression];
return [evaluator parseExpectingVariable:NO return [evaluator parseExpectingVariable:NO
error:error]; error:error];
} }

View File

@@ -128,16 +128,6 @@
return [NSIndexPath indexPathWithIndex:0]; return [NSIndexPath indexPathWithIndex:0];
} }
- (NSUInteger)indexOfLeadingChild
{
return 0;
}
- (NSUInteger)indexOfTrailingChild
{
return 0;
}
- (NSIndexSet *)indexesOfRemainingChildren - (NSIndexSet *)indexesOfRemainingChildren
{ {
return [NSIndexSet indexSetWithIndex:0]; return [NSIndexSet indexSetWithIndex:0];

View File

@@ -36,7 +36,7 @@
- (MPRangePath *)rangePath - (MPRangePath *)rangePath
{ {
NSIndexPath *location = self.pathToExpression; NSIndexPath *location = self.pathToExpression;
location = [location indexPathByPreceedingIndex:self.errorRange.location]; location = [location indexPathByAddingIndex:self.errorRange.location];
MPRangePath *rangePath = [MPRangePath rangePathWithLocation:location length:self.errorRange.length]; MPRangePath *rangePath = [MPRangePath rangePathWithLocation:location length:self.errorRange.length];
return rangePath; return rangePath;
} }

View File

@@ -10,8 +10,7 @@
@interface MPPowerFunction : MPFunction @interface MPPowerFunction : MPFunction
@property (nonatomic, strong) MPTerm *baseTerm;
@property (nonatomic, strong) MPExpression *exponentExpression; @property (nonatomic, strong) MPExpression *exponentExpression;
@property (nonatomic, strong) MPTerm *baseTerm;
@end @end

View File

@@ -20,7 +20,8 @@ MPFunctionAccessorImplementation(ExponentExpression, _exponentExpression)
- (MPTerm *)parseWithError:(MPParseError *__autoreleasing *)error - (MPTerm *)parseWithError:(MPParseError *__autoreleasing *)error
{ {
MPTerm *exponentTerm = [self.exponentExpression.evaluator parseExpectingVariable:NO MPExpressionEvaluator *exponent = [[MPExpressionEvaluator alloc] initWithExpression:self.exponentExpression];
MPTerm *exponentTerm = [exponent parseExpectingVariable:NO
error:error]; error:error];
if (exponentTerm == nil) { if (exponentTerm == nil) {
return nil; return nil;

View File

@@ -0,0 +1,19 @@
//
// MPPowerFunctionLayout.h
// MathPad
//
// Created by Kim Wittenburg on 30.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPFunctionLayout.h"
#import "MPPowerFunction.h"
@interface MPPowerFunctionLayout : MPFunctionLayout
@property (nonatomic) NSRect baseBounds;
- (MPPowerFunction *)powerFunction;
@end

View File

@@ -0,0 +1,68 @@
//
// MPPowerFunctionLayout.m
// MathPad
//
// Created by Kim Wittenburg on 30.09.14.
// Copyright (c) 2014 Kim Wittenburg. All rights reserved.
//
#import "MPPowerFunctionLayout.h"
#define kMPEmptyBoxHeight (CTFontGetDescent((CTFontRef)self.font) + CTFontGetAscent((CTFontRef)self.font) + CTFontGetLeading((CTFontRef)self.font))
#define kMPEmptyBoxYOrigin (-(CTFontGetDescent((CTFontRef)self.font) + CTFontGetLeading((CTFontRef)self.font)))
#define kPowerFunctionExponentXOffset 1
#define kPowerFunctionTrailingOffset 2
@implementation MPPowerFunctionLayout
- (NSRect)baseBounds
{
if (NSEqualRects(_baseBounds, NSZeroRect)) {
return NSMakeRect(0, kMPEmptyBoxYOrigin, 0, kMPEmptyBoxHeight);
}
return _baseBounds;
}
- (MPPowerFunction *)powerFunction
{
return (MPPowerFunction *)self.function;
}
- (NSIndexSet *)indexesOfRemainingChildren
{
return [NSIndexSet indexSetWithIndex:0];
}
- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index
{
CGFloat y = self.baseBounds.size.height / 2;
return NSMakePoint(kPowerFunctionExponentXOffset, y);
}
- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index
{
return YES;
}
- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point
{
return [[NSIndexPath indexPathWithIndex:0] indexPathByAddingIndex:0];
}
- (NSRect)generateBounds
{
NSRect exponentBounds = [self childLayoutAtIndex:0].bounds;
CGFloat y = self.baseBounds.origin.y;
CGFloat height = -y + [self offsetOfChildLayoutAtIndex:0].y + exponentBounds.size.height + exponentBounds.origin.y;
CGFloat width = kPowerFunctionExponentXOffset + exponentBounds.size.width + kPowerFunctionTrailingOffset;
return NSMakeRect(0, y, width, height);
}
- (void)draw
{
MPLayout *exponentLayout = [self childLayoutAtIndex:0];
[exponentLayout drawAtPoint:[self offsetOfChildLayoutAtIndex:0]];
}
@end

View File

@@ -251,6 +251,11 @@
@return A new expression. @return A new expression.
*/ */
- (MPExpression *)subexpressionWithRangePath:(MPRangePath *)aRangePath; // If location points to expression (in function) nil is returned - (MPExpression *)subexpressionWithRangePath:(MPRangePath *)aRangePath
referenceFrame:(MPReferenceFrame)referenceFrame;
- (void)replaceItemsInRangePath:(MPRangePath *)rangePath
referenceFrame:(MPReferenceFrame)referenceFrame
withElements:(NSArray *)elements;
@end @end

View File

@@ -156,13 +156,33 @@
@implementation MPExpression (MPRangeExtension) @implementation MPExpression (MPRangeExtension)
- (NSArray *)itemsInRangePath:(MPRangePath *)rangePath
referenceFrame:(MPReferenceFrame)referenceFrame
{
MPExpression *targetExpression = [self elementAtIndexPath:[rangePath.location indexPathByRemovingLastIndex]];
return [targetExpression itemsInRange:rangePath.rangeAtLastIndex referenceFrame:referenceFrame];
}
- (MPExpression *)subexpressionWithRangePath:(MPRangePath *)aRangePath - (MPExpression *)subexpressionWithRangePath:(MPRangePath *)aRangePath
referenceFrame:(MPReferenceFrame)referenceFrame
{ {
MPExpression *targetExpression = [self elementAtIndexPath:[aRangePath.location indexPathByRemovingLastIndex]]; MPExpression *targetExpression = [self elementAtIndexPath:[aRangePath.location indexPathByRemovingLastIndex]];
if (![targetExpression isKindOfClass:[MPExpression class]]) { if (![targetExpression isKindOfClass:[MPExpression class]]) {
// TODO: Raise appropriate exception
return nil; return nil;
} }
return [targetExpression subexpressionWithRange:aRangePath.rangeAtLastIndex]; return [targetExpression subexpressionWithRange:aRangePath.rangeAtLastIndex
referenceFrame:referenceFrame];
}
- (void)replaceItemsInRangePath:(MPRangePath *)rangePath
referenceFrame:(MPReferenceFrame)referenceFrame
withElements:(NSArray *)elements
{
MPExpression *targetExpression = [self elementAtIndexPath:[rangePath.location indexPathByRemovingLastIndex]];
[targetExpression replaceItemsInRange:rangePath.rangeAtLastIndex
referenceFrame:referenceFrame
withElements:elements];
} }
@end @end

View File

@@ -29,12 +29,12 @@ MPFunctionAccessorImplementation(SumExpression, _sumExpression)
#pragma mark Evaluating Functions #pragma mark Evaluating Functions
- (MPTerm *)parseWithError:(MPParseError *__autoreleasing *)error - (MPTerm *)parseWithError:(MPParseError *__autoreleasing *)error
{ {
MPExpressionEvaluator *startEvaluator = self.startExpression.evaluator; MPExpressionEvaluator *startEvaluator = [[MPExpressionEvaluator alloc] initWithExpression:self.startExpression];
MPTerm *start = [startEvaluator parseExpectingVariable:YES MPTerm *start = [startEvaluator parseExpectingVariable:YES
error:error]; error:error];
ReturnIfNil(start); ReturnIfNil(start);
MPExpressionEvaluator *targetEvaluator = self.targetExpression.evaluator; MPExpressionEvaluator *targetEvaluator = [[MPExpressionEvaluator alloc] initWithExpression:self.targetExpression];
MPTerm *target = [targetEvaluator parseExpectingVariable:NO MPTerm *target = [targetEvaluator parseExpectingVariable:NO
error:error]; error:error];
ReturnIfNil(target); ReturnIfNil(target);
@@ -43,7 +43,7 @@ MPFunctionAccessorImplementation(SumExpression, _sumExpression)
[[MPEvaluationContext sharedContext] push]; [[MPEvaluationContext sharedContext] push];
[[MPEvaluationContext sharedContext] defineVariable:variable withValue:[NSNull null]]; [[MPEvaluationContext sharedContext] defineVariable:variable withValue:[NSNull null]];
MPExpressionEvaluator *sumEvaluator = self.sumExpression.evaluator; MPExpressionEvaluator *sumEvaluator = [[MPExpressionEvaluator alloc] initWithExpression:self.sumExpression];
MPTerm *sum = [sumEvaluator parseExpectingVariable:NO MPTerm *sum = [sumEvaluator parseExpectingVariable:NO
error:error]; error:error];
ReturnIfNil(sum); ReturnIfNil(sum);

View File

@@ -142,12 +142,6 @@
- (void)draw - (void)draw
{ {
#ifdef MPDEBUG_DRAW_BASELINE
[[NSColor redColor] set];
NSRectFill(NSMakeRect(0, -1, self.bounds.size.width, 1));
[[NSColor textColor] set];
#endif
// Get the current context // Get the current context
CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
@@ -161,12 +155,6 @@
CGContextSetTextPosition(context, localLineBounds.origin.x, 0); CGContextSetTextPosition(context, localLineBounds.origin.x, 0);
CTLineDraw(line, context); CTLineDraw(line, context);
#ifdef MPDEBUG_DRAW_FUNCTION_BOUNDS
[[NSColor blueColor] set];
[[NSBezierPath bezierPathWithRect:localLineBounds] stroke];
[[NSColor textColor] set];
#endif
// Draw the start function // Draw the start function
MPLayout *startExpressionLayout = [self childLayoutAtIndex:0]; MPLayout *startExpressionLayout = [self childLayoutAtIndex:0];
NSPoint startExpressionLocation = [self offsetOfChildLayoutAtIndex:0]; NSPoint startExpressionLocation = [self offsetOfChildLayoutAtIndex:0];

View File

@@ -23,6 +23,10 @@
- (instancetype)initWithCosOfTerm:(MPTerm *)term; - (instancetype)initWithCosOfTerm:(MPTerm *)term;
- (instancetype)initWithTanOfTerm:(MPTerm *)term; - (instancetype)initWithTanOfTerm:(MPTerm *)term;
- (instancetype)initWithInverseSinOfTerm:(MPTerm *)term;
- (instancetype)initWithInverseCosOfTerm:(MPTerm *)term;
- (instancetype)initWithInverseTanOfTerm:(MPTerm *)term;
- (NSDecimalNumber *)evaluate; - (NSDecimalNumber *)evaluate;
@end @end

View File

@@ -8,6 +8,7 @@
#import "MPTerm.h" #import "MPTerm.h"
#import "MPEvaluationContext.h" #import "MPEvaluationContext.h"
#import "MPMathRules.h"
@interface MPTerm () @interface MPTerm ()
@@ -66,7 +67,6 @@
{ {
return [self initWithBlock:^NSDecimalNumber *{ return [self initWithBlock:^NSDecimalNumber *{
NSDecimalNumber *termValue = [term evaluate]; NSDecimalNumber *termValue = [term evaluate];
NSLog(@"Factorial of %@ = %f", termValue, tgamma(termValue.doubleValue + 1));
return [[NSDecimalNumber alloc] initWithDouble:tgamma(termValue.doubleValue + 1)]; return [[NSDecimalNumber alloc] initWithDouble:tgamma(termValue.doubleValue + 1)];
}]; }];
} }
@@ -75,6 +75,9 @@
{ {
return [self initWithBlock:^NSDecimalNumber *{ return [self initWithBlock:^NSDecimalNumber *{
NSDecimalNumber *termValue = [term evaluate]; NSDecimalNumber *termValue = [term evaluate];
if ([MPMathRules sharedRules].isUsingDegrees) {
termValue = [[termValue decimalNumberByMultiplyingBy:[[NSDecimalNumber alloc] initWithDouble:M_PI]] decimalNumberByDividingBy:[[NSDecimalNumber alloc] initWithInteger:180]];
}
return [[NSDecimalNumber alloc] initWithDouble:sin(termValue.doubleValue)]; return [[NSDecimalNumber alloc] initWithDouble:sin(termValue.doubleValue)];
}]; }];
} }
@@ -83,6 +86,8 @@
{ {
return [self initWithBlock:^NSDecimalNumber *{ return [self initWithBlock:^NSDecimalNumber *{
NSDecimalNumber *termValue = [term evaluate]; NSDecimalNumber *termValue = [term evaluate];
if ([MPMathRules sharedRules].isUsingDegrees) {
termValue = [[termValue decimalNumberByMultiplyingBy:[[NSDecimalNumber alloc] initWithDouble:M_PI]] decimalNumberByDividingBy:[[NSDecimalNumber alloc] initWithInteger:180]]; }
return [[NSDecimalNumber alloc] initWithDouble:cos(termValue.doubleValue)]; return [[NSDecimalNumber alloc] initWithDouble:cos(termValue.doubleValue)];
}]; }];
} }
@@ -91,13 +96,54 @@
{ {
return [self initWithBlock:^NSDecimalNumber *{ return [self initWithBlock:^NSDecimalNumber *{
NSDecimalNumber *termValue = [term evaluate]; NSDecimalNumber *termValue = [term evaluate];
if ([MPMathRules sharedRules].isUsingDegrees) {
termValue = [[termValue decimalNumberByMultiplyingBy:[[NSDecimalNumber alloc] initWithDouble:M_PI]] decimalNumberByDividingBy:[[NSDecimalNumber alloc] initWithInteger:180]]; }
return [[NSDecimalNumber alloc] initWithDouble:tan(termValue.doubleValue)]; return [[NSDecimalNumber alloc] initWithDouble:tan(termValue.doubleValue)];
}]; }];
} }
- (instancetype)initWithInverseSinOfTerm:(MPTerm *)term
{
return [self initWithBlock:^NSDecimalNumber *{
NSDecimalNumber *termValue = [term evaluate];
NSDecimalNumber *result = [[NSDecimalNumber alloc] initWithDouble:asin(termValue.doubleValue)];
if ([MPMathRules sharedRules].isUsingDegrees) {
result = [[result decimalNumberByMultiplyingBy:[[NSDecimalNumber alloc] initWithInteger:180]] decimalNumberByDividingBy:[[NSDecimalNumber alloc] initWithDouble:M_PI]];
}
return result;
}];
}
- (instancetype)initWithInverseCosOfTerm:(MPTerm *)term
{
return [self initWithBlock:^NSDecimalNumber *{
NSDecimalNumber *termValue = [term evaluate];
NSDecimalNumber *result = [[NSDecimalNumber alloc] initWithDouble:acos(termValue.doubleValue)];
if ([MPMathRules sharedRules].isUsingDegrees) {
result = [[result decimalNumberByMultiplyingBy:[[NSDecimalNumber alloc] initWithInteger:180]] decimalNumberByDividingBy:[[NSDecimalNumber alloc] initWithDouble:M_PI]];
}
return result; }];
}
- (instancetype)initWithInverseTanOfTerm:(MPTerm *)term
{
return [self initWithBlock:^NSDecimalNumber *{
NSDecimalNumber *termValue = [term evaluate];
NSDecimalNumber *result = [[NSDecimalNumber alloc] initWithDouble:atan(termValue.doubleValue)];
if ([MPMathRules sharedRules].isUsingDegrees) {
result = [[result decimalNumberByMultiplyingBy:[[NSDecimalNumber alloc] initWithInteger:180]] decimalNumberByDividingBy:[[NSDecimalNumber alloc] initWithDouble:M_PI]];
}
return result; }];
}
- (NSDecimalNumber *)evaluate - (NSDecimalNumber *)evaluate
{ {
@try {
return self.block(); return self.block();
} }
@catch (NSException *exception) {
return [NSDecimalNumber notANumber];
}
}
@end @end

View File

@@ -41,10 +41,11 @@ typedef NS_ENUM(NSUInteger, MPTokenType) {
@interface MPToken : NSObject <MPToken> @interface MPToken : NSObject <MPToken>
- (instancetype)initEOFTokenAtLocation:(NSUInteger)eofLocation; - (instancetype)initEOFTokenAtLocation:(NSUInteger)eofLocation;
- (instancetype)initWithRange:(NSRange)range inString:(NSString *)input; - (instancetype)initWithRange:(NSRange)range
stringValue:(NSString *)input;
- (instancetype)initWithTokenType:(MPTokenType)tokenType - (instancetype)initWithTokenType:(MPTokenType)tokenType
range:(NSRange)range range:(NSRange)range
inString:(NSString *)input; stringValue:(NSString *)input;
@end @end

View File

@@ -23,22 +23,22 @@
return self; return self;
} }
- (instancetype)initWithRange:(NSRange)range inString:(NSString *)input - (instancetype)initWithRange:(NSRange)range stringValue:(NSString *)input
{ {
self = [super init]; self = [super init];
if (self) { if (self) {
_range = range; _range = range;
_stringValue = [input substringWithRange:range].copy; _stringValue = input.copy;
} }
return self; return self;
} }
- (instancetype)initWithTokenType:(MPTokenType)tokenType - (instancetype)initWithTokenType:(MPTokenType)tokenType
range:(NSRange)range range:(NSRange)range
inString:(NSString *)input stringValue:(NSString *)input
{ {
self = [self initWithRange:range self = [self initWithRange:range
inString:input]; stringValue:input];
if (self) { if (self) {
_tokenType = tokenType; _tokenType = tokenType;
} }

View File

@@ -16,7 +16,7 @@
@property (nonatomic, copy) NSArray *tokens; @property (nonatomic, copy) NSArray *tokens;
@property (nonatomic, getter=isIgnoringWhitespaceTokens) BOOL ignoringWhitespaceTokens; // Default: YES @property (nonatomic, getter=isIgnoringWhitespaceTokens) BOOL ignoringWhitespaceTokens; // Default: YES
@property (nonatomic) NSUInteger currentLocation; @property (nonatomic) NSUInteger currentTokenIndex;
- (void)reset; - (void)reset;
- (BOOL)hasMoreTokens; - (BOOL)hasMoreTokens;

View File

@@ -9,7 +9,6 @@
#import "MPTokenStream.h" #import "MPTokenStream.h"
@implementation MPTokenStream { @implementation MPTokenStream {
NSUInteger currentTokenIndex;
NSUInteger eofLocation; NSUInteger eofLocation;
} }
@@ -32,37 +31,34 @@
if (!self.isIgnoringWhitespaceTokens) { if (!self.isIgnoringWhitespaceTokens) {
return; return;
} }
while (currentTokenIndex < self.tokens.count) { while (self.currentTokenIndex < self.tokens.count) {
MPToken *token = self.tokens[currentTokenIndex]; MPToken *token = self.tokens[self.currentTokenIndex];
if (token.tokenType != MPWhitespaceToken) { if (token.tokenType != MPWhitespaceToken) {
return; return;
} }
self.currentLocation = NSMaxRange(token.range); ++self.currentTokenIndex;
++currentTokenIndex;
} }
} }
- (void)reset - (void)reset
{ {
currentTokenIndex = 0; self.currentTokenIndex = 0;
self.currentLocation = 0;
[self skipWhitespaces]; [self skipWhitespaces];
} }
- (BOOL)hasMoreTokens - (BOOL)hasMoreTokens
{ {
[self skipWhitespaces]; [self skipWhitespaces];
return currentTokenIndex < self.tokens.count; return self.currentTokenIndex < self.tokens.count;
} }
- (MPToken *)nextToken - (MPToken *)nextToken
{ {
[self skipWhitespaces]; [self skipWhitespaces];
if (currentTokenIndex >= self.tokens.count) { if (self.currentTokenIndex >= self.tokens.count) {
return [[MPToken alloc] initEOFTokenAtLocation:eofLocation]; return [[MPToken alloc] initEOFTokenAtLocation:eofLocation];
} else { } else {
MPToken *token = self.tokens[currentTokenIndex++]; MPToken *token = self.tokens[self.currentTokenIndex++];
self.currentLocation = NSMaxRange(token.range);
return token; return token;
} }
} }
@@ -70,15 +66,14 @@
- (MPToken *)nextTokenOfType:(MPTokenType)type - (MPToken *)nextTokenOfType:(MPTokenType)type
{ {
[self skipWhitespaces]; [self skipWhitespaces];
if (currentTokenIndex >= self.tokens.count) { if (self.currentTokenIndex >= self.tokens.count) {
return nil; return nil;
} else { } else {
MPToken *token = self.tokens[currentTokenIndex]; MPToken *token = self.tokens[self.currentTokenIndex];
if (token.tokenType != type) { if (token.tokenType != type) {
return nil; return nil;
} }
++currentTokenIndex; ++self.currentTokenIndex;
self.currentLocation = NSMaxRange(token.range);
return token; return token;
} }
} }

View File

@@ -29,21 +29,21 @@
// Test expression with function // Test expression with function
testExpression = [[MPExpression alloc] initWithElement:[[MPFunction alloc] init]]; testExpression = [[MPExpression alloc] initWithElement:[[MPFunction alloc] init]];
XCTAssertEqual([testExpression numberOfElements], 1); XCTAssertEqual([testExpression countElements], 1);
XCTAssertEqualObjects([testExpression elementAtIndex:0], [[MPFunction alloc] init]); XCTAssertEqualObjects([testExpression elementAtIndex:0], [[MPFunction alloc] init]);
testExpression = [[MPExpression alloc] initWithElements:@[@"1234", [[MPFunction alloc] init], @"17", [[MPFunction alloc] init]]]; testExpression = [[MPExpression alloc] initWithElements:@[@"1234", [[MPFunction alloc] init], @"17", [[MPFunction alloc] init]]];
XCTAssertEqual([testExpression numberOfElements], 4); XCTAssertEqual([testExpression countElements], 4);
XCTAssertEqualObjects([testExpression elementAtIndex:2], @"17"); XCTAssertEqualObjects([testExpression elementAtIndex:2], @"17");
// Test expression with subsequent strings // Test expression with subsequent strings
testExpression = [[MPExpression alloc] initWithElements:@[@"1234", @"5678"]]; testExpression = [[MPExpression alloc] initWithElements:@[@"1234", @"5678"]];
XCTAssertEqual([testExpression numberOfElements], 1); XCTAssertEqual([testExpression countElements], 1);
XCTAssertEqualObjects([testExpression elementAtIndex:0], @"12345678"); XCTAssertEqualObjects([testExpression elementAtIndex:0], @"12345678");
// Test expression with only empty string // Test expression with only empty string
testExpression = [[MPExpression alloc] initWithElement:@""]; testExpression = [[MPExpression alloc] initWithElement:@""];
XCTAssertEqual([testExpression numberOfElements], 0); XCTAssertEqual([testExpression countElements], 0);
} }
- (void)testSubexpressions { - (void)testSubexpressions {
@@ -52,77 +52,77 @@
/********** subexpressionFromIndex: **********/ /********** subexpressionFromIndex: **********/
// Test with start index at front // Test with start index at front
MPExpression *subexpression = [testExpression subexpressionFromLocation:0]; MPExpression *subexpression = [testExpression subexpressionFromLocation:0];
XCTAssertEqual([subexpression numberOfElements], 4); XCTAssertEqual([subexpression countElements], 4);
XCTAssertEqualObjects([subexpression elementAtIndex:0], @"1234"); XCTAssertEqualObjects([subexpression elementAtIndex:0], @"1234");
// Test with start index in first element // Test with start index in first element
subexpression = [testExpression subexpressionFromLocation:2]; subexpression = [testExpression subexpressionFromLocation:2];
XCTAssertEqual([subexpression numberOfElements], 4); XCTAssertEqual([subexpression countElements], 4);
XCTAssertEqualObjects([subexpression elementAtIndex:0], @"34"); XCTAssertEqualObjects([subexpression elementAtIndex:0], @"34");
// Test with start index in middle element starting with a literal // Test with start index in middle element starting with a literal
subexpression = [testExpression subexpressionFromLocation:6]; subexpression = [testExpression subexpressionFromLocation:6];
XCTAssertEqual([subexpression numberOfElements], 2); XCTAssertEqual([subexpression countElements], 2);
XCTAssertEqualObjects([subexpression elementAtIndex:0], @"7"); XCTAssertEqualObjects([subexpression elementAtIndex:0], @"7");
// Test with start index in middle element starting with a function // Test with start index in middle element starting with a function
subexpression = [testExpression subexpressionFromLocation:4]; subexpression = [testExpression subexpressionFromLocation:4];
XCTAssertEqual([subexpression numberOfElements], 3); XCTAssertEqual([subexpression countElements], 3);
XCTAssertEqualObjects([subexpression elementAtIndex:0], [[MPFunction alloc] init]); XCTAssertEqualObjects([subexpression elementAtIndex:0], [[MPFunction alloc] init]);
// Test with start index in last element // Test with start index in last element
subexpression = [testExpression subexpressionFromLocation:7]; subexpression = [testExpression subexpressionFromLocation:7];
XCTAssertEqual([subexpression numberOfElements], 1); XCTAssertEqual([subexpression countElements], 1);
XCTAssertEqualObjects([subexpression elementAtIndex:0], [[MPFunction alloc] init]); XCTAssertEqualObjects([subexpression elementAtIndex:0], [[MPFunction alloc] init]);
// Test with start index at end // Test with start index at end
subexpression = [testExpression subexpressionFromLocation:8]; subexpression = [testExpression subexpressionFromLocation:8];
XCTAssertEqual([subexpression numberOfElements], 0); XCTAssertEqual([subexpression countElements], 0);
/********** subexpressionToIndex: **********/ /********** subexpressionToIndex: **********/
// Test with end index at front // Test with end index at front
subexpression = [testExpression subexpressionToLocation:0]; subexpression = [testExpression subexpressionToLocation:0];
XCTAssertEqual([subexpression numberOfElements], 0); XCTAssertEqual([subexpression countElements], 0);
// Test with end index in first Element // Test with end index in first Element
subexpression = [testExpression subexpressionToLocation:2]; subexpression = [testExpression subexpressionToLocation:2];
XCTAssertEqual([subexpression numberOfElements], 1); XCTAssertEqual([subexpression countElements], 1);
XCTAssertEqualObjects([subexpression elementAtIndex:0], @"12"); XCTAssertEqualObjects([subexpression elementAtIndex:0], @"12");
// Test with end index in middle Element ending with a literal // Test with end index in middle Element ending with a literal
subexpression = [testExpression subexpressionToLocation:6]; subexpression = [testExpression subexpressionToLocation:6];
XCTAssertEqual([subexpression numberOfElements], 3); XCTAssertEqual([subexpression countElements], 3);
XCTAssertEqualObjects([subexpression elementAtIndex:2], @"1"); XCTAssertEqualObjects([subexpression elementAtIndex:2], @"1");
// Test with end index in middle Element ending with a function // Test with end index in middle Element ending with a function
subexpression = [testExpression subexpressionToLocation:5]; subexpression = [testExpression subexpressionToLocation:5];
XCTAssertEqual([subexpression numberOfElements], 2); XCTAssertEqual([subexpression countElements], 2);
XCTAssertEqualObjects([subexpression elementAtIndex:1], [[MPFunction alloc] init]); XCTAssertEqualObjects([subexpression elementAtIndex:1], [[MPFunction alloc] init]);
// Test with end index at end // Test with end index at end
subexpression = [testExpression subexpressionToLocation:8]; subexpression = [testExpression subexpressionToLocation:8];
XCTAssertEqual([subexpression numberOfElements], 4); XCTAssertEqual([subexpression countElements], 4);
XCTAssertEqualObjects([subexpression elementAtIndex:3], [[MPFunction alloc] init]); XCTAssertEqualObjects([subexpression elementAtIndex:3], [[MPFunction alloc] init]);
/********** subexpressionWithRange: **********/ /********** subexpressionWithRange: **********/
// Test with empty range // Test with empty range
subexpression = [testExpression subexpressionWithRange:NSMakeRange(4, 0)]; subexpression = [testExpression subexpressionWithRange:NSMakeRange(4, 0)];
XCTAssertEqual([subexpression numberOfElements], 0); XCTAssertEqual([subexpression countElements], 0);
// Test with start and end in first element // Test with start and end in first element
subexpression = [testExpression subexpressionWithRange:NSMakeRange(1, 2)]; subexpression = [testExpression subexpressionWithRange:NSMakeRange(1, 2)];
XCTAssertEqual([subexpression numberOfElements], 1); XCTAssertEqual([subexpression countElements], 1);
XCTAssertEqualObjects([subexpression elementAtIndex:0], @"23"); XCTAssertEqualObjects([subexpression elementAtIndex:0], @"23");
// Test with start in first and end in middle after function // Test with start in first and end in middle after function
subexpression = [testExpression subexpressionWithRange:NSMakeRange(2, 3)]; subexpression = [testExpression subexpressionWithRange:NSMakeRange(2, 3)];
XCTAssertEqual([subexpression numberOfElements], 2); XCTAssertEqual([subexpression countElements], 2);
XCTAssertEqualObjects([subexpression elementAtIndex:0], @"34"); XCTAssertEqualObjects([subexpression elementAtIndex:0], @"34");
XCTAssertEqualObjects([subexpression elementAtIndex:1], [[MPFunction alloc] init]); XCTAssertEqualObjects([subexpression elementAtIndex:1], [[MPFunction alloc] init]);
// Test with start in first and end in middle after literal // Test with start in first and end in middle after literal
subexpression = [testExpression subexpressionWithRange:NSMakeRange(2, 4)]; subexpression = [testExpression subexpressionWithRange:NSMakeRange(2, 4)];
XCTAssertEqual([subexpression numberOfElements], 3); XCTAssertEqual([subexpression countElements], 3);
XCTAssertEqualObjects([subexpression elementAtIndex:0], @"34"); XCTAssertEqualObjects([subexpression elementAtIndex:0], @"34");
XCTAssertEqualObjects([subexpression elementAtIndex:2], @"1"); XCTAssertEqualObjects([subexpression elementAtIndex:2], @"1");
} }
@@ -211,25 +211,25 @@
MPExpression *testExpression = [[MPExpression alloc] initWithElements:@[@"1234", [[MPFunction alloc] init], @"5678", [[MPFunction alloc] init]]]; MPExpression *testExpression = [[MPExpression alloc] initWithElements:@[@"1234", [[MPFunction alloc] init], @"5678", [[MPFunction alloc] init]]];
[testExpression appendElement:@"90"]; [testExpression appendElement:@"90"];
XCTAssertEqual([testExpression numberOfElements], 5); XCTAssertEqual([testExpression countElements], 5);
// 1234 [] 5678 [] 90 // 1234 [] 5678 [] 90
[testExpression deleteElementsInRange:NSMakeRange(2, 4)]; [testExpression deleteElementsInRange:NSMakeRange(2, 4)];
XCTAssertEqual([testExpression numberOfElements], 3); XCTAssertEqual([testExpression countElements], 3);
// 12678 [] 90 // 12678 [] 90
[testExpression deleteElementsInRange:NSMakeRange(0, 2)]; [testExpression deleteElementsInRange:NSMakeRange(0, 2)];
XCTAssertEqual([testExpression numberOfElements], 3); XCTAssertEqual([testExpression countElements], 3);
XCTAssertEqualObjects([testExpression elementAtIndex:0], @"678"); XCTAssertEqualObjects([testExpression elementAtIndex:0], @"678");
[testExpression insertElement:[[MPFunction alloc] init] [testExpression insertElement:[[MPFunction alloc] init]
atLocation:2]; atLocation:2];
XCTAssertEqual([testExpression numberOfElements], 5); XCTAssertEqual([testExpression countElements], 5);
// 67 [] 8 [] 90 // 67 [] 8 [] 90
[testExpression replaceSymbolsInRange:NSMakeRange(2, 5) [testExpression replaceSymbolsInRange:NSMakeRange(2, 5)
withElements:@[[[MPFunction alloc] init]]]; withElements:@[[[MPFunction alloc] init]]];
XCTAssertEqual([testExpression numberOfElements], 2); XCTAssertEqual([testExpression countElements], 2);
XCTAssertEqualObjects([testExpression elementAtIndex:0], @"67"); XCTAssertEqualObjects([testExpression elementAtIndex:0], @"67");
// 67 [] // 67 []
} }