diff --git a/MathPad.xcodeproj/project.pbxproj b/MathPad.xcodeproj/project.pbxproj index 152396c..7a4e4ee 100644 --- a/MathPad.xcodeproj/project.pbxproj +++ b/MathPad.xcodeproj/project.pbxproj @@ -18,6 +18,37 @@ 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 */; }; + 3B69B67C19E4915E0028E608 /* MPFractionFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B69B67A19E4915E0028E608 /* MPFractionFunction.h */; }; + 3B69B67D19E4915E0028E608 /* MPFractionFunction.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B69B67B19E4915E0028E608 /* MPFractionFunction.m */; }; + 3B69B68019E493630028E608 /* MPFractionFunctionLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B69B67E19E493630028E608 /* MPFractionFunctionLayout.h */; }; + 3B69B68119E493630028E608 /* MPFractionFunctionLayout.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B69B67F19E493630028E608 /* MPFractionFunctionLayout.m */; }; + 3B69B68519E6E8B20028E608 /* MPExpressionTree.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B69B68319E6E8B20028E608 /* MPExpressionTree.h */; }; + 3B69B68619E6E8B20028E608 /* MPExpressionTree.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B69B68419E6E8B20028E608 /* MPExpressionTree.m */; }; + 3B69B68919E6E9280028E608 /* MPSummand.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B69B68719E6E9280028E608 /* MPSummand.h */; }; + 3B69B68A19E6E9280028E608 /* MPSummand.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B69B68819E6E9280028E608 /* MPSummand.m */; }; + 3B69B68D19E6EB0D0028E608 /* MPProduct.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B69B68B19E6EB0D0028E608 /* MPProduct.h */; }; + 3B69B68E19E6EB0D0028E608 /* MPProduct.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B69B68C19E6EB0D0028E608 /* MPProduct.m */; }; + 3B69B69019E6ED6E0028E608 /* MPExpressionTreeElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B69B68F19E6ED6E0028E608 /* MPExpressionTreeElement.h */; }; + 3B69B69719E6F0780028E608 /* MPValueGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B69B69519E6F0780028E608 /* MPValueGroup.h */; }; + 3B69B69819E6F0780028E608 /* MPValueGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B69B69619E6F0780028E608 /* MPValueGroup.m */; }; + 3B69B69D19E6F2110028E608 /* MPNumber.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B69B69B19E6F2110028E608 /* MPNumber.h */; }; + 3B69B69E19E6F2110028E608 /* MPNumber.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B69B69C19E6F2110028E608 /* MPNumber.m */; }; + 3B69B6A119E6F2420028E608 /* MPVariable.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B69B69F19E6F2420028E608 /* MPVariable.h */; }; + 3B69B6A219E6F2420028E608 /* MPVariable.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B69B6A019E6F2420028E608 /* MPVariable.m */; }; + 3B69B6A919E6F2AF0028E608 /* MPFunction+MPValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B69B6A719E6F2AF0028E608 /* MPFunction+MPValue.h */; }; + 3B69B6AA19E6F2AF0028E608 /* MPFunction+MPValue.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B69B6A819E6F2AF0028E608 /* MPFunction+MPValue.m */; }; + 3B69B6B119E8175F0028E608 /* MPFactor.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B69B6AF19E8175F0028E608 /* MPFactor.h */; }; + 3B69B6B219E8175F0028E608 /* MPFactor.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B69B6B019E8175F0028E608 /* MPFactor.m */; }; + 3B69B6B519E88E750028E608 /* MPUnexpectedSymbolValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B69B6B319E88E750028E608 /* MPUnexpectedSymbolValue.h */; }; + 3B69B6B619E88E750028E608 /* MPUnexpectedSymbolValue.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B69B6B419E88E750028E608 /* MPUnexpectedSymbolValue.m */; }; + 3B69B6B919E935E20028E608 /* MPSuffixFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B69B6B719E935E20028E608 /* MPSuffixFunction.h */; }; + 3B69B6BA19E935E20028E608 /* MPSuffixFunction.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B69B6B819E935E20028E608 /* MPSuffixFunction.m */; }; + 3B69B6BD19E948740028E608 /* MPFunctionValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B69B6BB19E948740028E608 /* MPFunctionValue.h */; }; + 3B69B6BE19E948740028E608 /* MPFunctionValue.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B69B6BC19E948740028E608 /* MPFunctionValue.m */; }; + 3B69B6C119E953590028E608 /* MPFactorial.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B69B6BF19E953590028E608 /* MPFactorial.h */; }; + 3B69B6C219E953590028E608 /* MPFactorial.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B69B6C019E953590028E608 /* MPFactorial.m */; }; + 3B69B6C519E95B250028E608 /* MPOperatorChain.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B69B6C319E95B250028E608 /* MPOperatorChain.h */; }; + 3B69B6C619E95B250028E608 /* MPOperatorChain.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B69B6C419E95B250028E608 /* MPOperatorChain.m */; }; 3B7172EA19C7147000FEAA5B /* FunctionsButtonDisclosure@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3B7172E819C7147000FEAA5B /* FunctionsButtonDisclosure@2x.png */; }; 3B7172EB19C7147000FEAA5B /* FunctionsButtonDisclosure.png in Resources */ = {isa = PBXBuildFile; fileRef = 3B7172E919C7147000FEAA5B /* FunctionsButtonDisclosure.png */; }; 3B7172EE19C9FA8E00FEAA5B /* MPParenthesisFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B7172EC19C9FA8E00FEAA5B /* MPParenthesisFunction.h */; }; @@ -42,7 +73,6 @@ 3B85833819BB63BD00D76A8D /* MPExpression.h in Headers */ = {isa = PBXBuildFile; fileRef = 3BFAC38D1997B61300B3EF67 /* MPExpression.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3B85833919BB63C500D76A8D /* MPExpressionElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 3BFAC3961997B67400B3EF67 /* MPExpressionElement.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3B85833A19BB63D400D76A8D /* MPFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B87E35E19009D5F00259938 /* MPFunction.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3B85833E19BB651400D76A8D /* MPExpressionEvaluator.h in Headers */ = {isa = PBXBuildFile; fileRef = 3BC46B4719B38C980033F13A /* MPExpressionEvaluator.h */; }; 3B85834119BB651E00D76A8D /* MPRangePath.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B87E35B1900933200259938 /* MPRangePath.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3B85834219BB652900D76A8D /* MPException.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B0F69A719028BC600817707 /* MPException.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3B85834319BB653700D76A8D /* MPSumFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = 3BB09EC71906FD830080A5ED /* MPSumFunction.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -55,14 +85,11 @@ 3B85834A19BB65B600D76A8D /* MPFunctionLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B528D11199417E90054DB5F /* MPFunctionLayout.h */; }; 3B85834B19BB65B600D76A8D /* MPSumFunctionLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 3BB09EDF190736160080A5ED /* MPSumFunctionLayout.h */; }; 3B85834C19BB661100D76A8D /* MathKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 3B85833219BB5F2D00D76A8D /* MathKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3BAF938D19D6D508002C5EC6 /* MPTerm.h in Headers */ = {isa = PBXBuildFile; fileRef = 3BAF938B19D6D508002C5EC6 /* MPTerm.h */; }; - 3BAF938E19D6D508002C5EC6 /* MPTerm.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BAF938C19D6D508002C5EC6 /* MPTerm.m */; }; 3BB18AA519CDB3A900986DA0 /* MPTokenStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 3BB18AA319CDB3A900986DA0 /* MPTokenStream.h */; }; 3BB18AA619CDB3A900986DA0 /* MPTokenStream.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB18AA419CDB3A900986DA0 /* MPTokenStream.m */; }; 3BBEA92C19BB680B00133766 /* MathKit.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3B85830D19BB5E5500D76A8D /* MathKit.framework */; }; 3BBEA93519BB79A700133766 /* MPExpression.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BFAC38E1997B61300B3EF67 /* MPExpression.m */; }; 3BBEA93619BB79A700133766 /* MPFunction.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E35F19009D5F00259938 /* MPFunction.m */; }; - 3BBEA93719BB79A700133766 /* MPExpressionEvaluator.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BC46B4819B38C980033F13A /* MPExpressionEvaluator.m */; }; 3BBEA93A19BB79A700133766 /* MPSumFunction.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BB09EC81906FD830080A5ED /* MPSumFunction.m */; }; 3BBEA93B19BB79A700133766 /* MPRangePath.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B87E35C1900933200259938 /* MPRangePath.m */; }; 3BBEA93C19BB79A700133766 /* MPException.m in Sources */ = {isa = PBXBuildFile; fileRef = 3B0F69A819028C6000817707 /* MPException.m */; }; @@ -152,6 +179,37 @@ 3B688D9819982DF50006B4AB /* MPLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPLayout.m; sourceTree = ""; }; 3B69B66A19DB41B90028E608 /* MPPowerFunctionLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPowerFunctionLayout.h; sourceTree = ""; }; 3B69B66B19DB41B90028E608 /* MPPowerFunctionLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPPowerFunctionLayout.m; sourceTree = ""; }; + 3B69B67A19E4915E0028E608 /* MPFractionFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFractionFunction.h; sourceTree = ""; }; + 3B69B67B19E4915E0028E608 /* MPFractionFunction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPFractionFunction.m; sourceTree = ""; }; + 3B69B67E19E493630028E608 /* MPFractionFunctionLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFractionFunctionLayout.h; sourceTree = ""; }; + 3B69B67F19E493630028E608 /* MPFractionFunctionLayout.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPFractionFunctionLayout.m; sourceTree = ""; }; + 3B69B68319E6E8B20028E608 /* MPExpressionTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPExpressionTree.h; sourceTree = ""; }; + 3B69B68419E6E8B20028E608 /* MPExpressionTree.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPExpressionTree.m; sourceTree = ""; }; + 3B69B68719E6E9280028E608 /* MPSummand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSummand.h; sourceTree = ""; }; + 3B69B68819E6E9280028E608 /* MPSummand.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSummand.m; sourceTree = ""; }; + 3B69B68B19E6EB0D0028E608 /* MPProduct.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPProduct.h; sourceTree = ""; }; + 3B69B68C19E6EB0D0028E608 /* MPProduct.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPProduct.m; sourceTree = ""; }; + 3B69B68F19E6ED6E0028E608 /* MPExpressionTreeElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPExpressionTreeElement.h; sourceTree = ""; }; + 3B69B69519E6F0780028E608 /* MPValueGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPValueGroup.h; sourceTree = ""; }; + 3B69B69619E6F0780028E608 /* MPValueGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPValueGroup.m; sourceTree = ""; }; + 3B69B69B19E6F2110028E608 /* MPNumber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPNumber.h; sourceTree = ""; }; + 3B69B69C19E6F2110028E608 /* MPNumber.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPNumber.m; sourceTree = ""; }; + 3B69B69F19E6F2420028E608 /* MPVariable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPVariable.h; sourceTree = ""; }; + 3B69B6A019E6F2420028E608 /* MPVariable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPVariable.m; sourceTree = ""; }; + 3B69B6A719E6F2AF0028E608 /* MPFunction+MPValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MPFunction+MPValue.h"; sourceTree = ""; }; + 3B69B6A819E6F2AF0028E608 /* MPFunction+MPValue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MPFunction+MPValue.m"; sourceTree = ""; }; + 3B69B6AF19E8175F0028E608 /* MPFactor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFactor.h; sourceTree = ""; }; + 3B69B6B019E8175F0028E608 /* MPFactor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPFactor.m; sourceTree = ""; }; + 3B69B6B319E88E750028E608 /* MPUnexpectedSymbolValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUnexpectedSymbolValue.h; sourceTree = ""; }; + 3B69B6B419E88E750028E608 /* MPUnexpectedSymbolValue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUnexpectedSymbolValue.m; sourceTree = ""; }; + 3B69B6B719E935E20028E608 /* MPSuffixFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSuffixFunction.h; sourceTree = ""; }; + 3B69B6B819E935E20028E608 /* MPSuffixFunction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPSuffixFunction.m; sourceTree = ""; }; + 3B69B6BB19E948740028E608 /* MPFunctionValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFunctionValue.h; sourceTree = ""; }; + 3B69B6BC19E948740028E608 /* MPFunctionValue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPFunctionValue.m; sourceTree = ""; }; + 3B69B6BF19E953590028E608 /* MPFactorial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFactorial.h; sourceTree = ""; }; + 3B69B6C019E953590028E608 /* MPFactorial.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPFactorial.m; sourceTree = ""; }; + 3B69B6C319E95B250028E608 /* MPOperatorChain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPOperatorChain.h; sourceTree = ""; }; + 3B69B6C419E95B250028E608 /* MPOperatorChain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPOperatorChain.m; sourceTree = ""; }; 3B7172E819C7147000FEAA5B /* FunctionsButtonDisclosure@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "FunctionsButtonDisclosure@2x.png"; sourceTree = ""; }; 3B7172E919C7147000FEAA5B /* FunctionsButtonDisclosure.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = FunctionsButtonDisclosure.png; sourceTree = ""; }; 3B7172EC19C9FA8E00FEAA5B /* MPParenthesisFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPParenthesisFunction.h; sourceTree = ""; }; @@ -181,8 +239,6 @@ 3B87E35C1900933200259938 /* MPRangePath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPRangePath.m; sourceTree = ""; }; 3B87E35E19009D5F00259938 /* MPFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFunction.h; sourceTree = ""; }; 3B87E35F19009D5F00259938 /* MPFunction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPFunction.m; sourceTree = ""; }; - 3BAF938B19D6D508002C5EC6 /* MPTerm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPTerm.h; sourceTree = ""; }; - 3BAF938C19D6D508002C5EC6 /* MPTerm.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPTerm.m; sourceTree = ""; }; 3BB09EB01905DE500080A5ED /* MPExpressionStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MPExpressionStorage.h; path = ../MathPad/MPExpressionStorage.h; sourceTree = ""; }; 3BB09EB11905DE500080A5ED /* MPExpressionStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MPExpressionStorage.m; path = ../MathPad/MPExpressionStorage.m; sourceTree = ""; }; 3BB09EC71906FD830080A5ED /* MPSumFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPSumFunction.h; sourceTree = ""; }; @@ -197,8 +253,6 @@ 3BC4661319B245C60033F13A /* Fonts */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Fonts; sourceTree = ""; }; 3BC4661519B365070033F13A /* MPArrayCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPArrayCache.h; sourceTree = ""; }; 3BC4661619B365070033F13A /* MPArrayCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPArrayCache.m; sourceTree = ""; }; - 3BC46B4719B38C980033F13A /* MPExpressionEvaluator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPExpressionEvaluator.h; sourceTree = ""; }; - 3BC46B4819B38C980033F13A /* MPExpressionEvaluator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPExpressionEvaluator.m; sourceTree = ""; }; 3BF59AFB19D80ECC00E54292 /* MPFunctionsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFunctionsViewController.h; sourceTree = ""; }; 3BF59AFC19D80ECC00E54292 /* MPFunctionsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPFunctionsViewController.m; sourceTree = ""; }; 3BF59AFD19D80ECC00E54292 /* MPFunctionsViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MPFunctionsViewController.xib; sourceTree = ""; }; @@ -271,6 +325,38 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 3B69B68219E6E8A50028E608 /* Expression Tree */ = { + isa = PBXGroup; + children = ( + 3B69B68F19E6ED6E0028E608 /* MPExpressionTreeElement.h */, + 3B69B68319E6E8B20028E608 /* MPExpressionTree.h */, + 3B69B68419E6E8B20028E608 /* MPExpressionTree.m */, + 3B69B68719E6E9280028E608 /* MPSummand.h */, + 3B69B68819E6E9280028E608 /* MPSummand.m */, + 3B69B6C319E95B250028E608 /* MPOperatorChain.h */, + 3B69B6C419E95B250028E608 /* MPOperatorChain.m */, + 3B69B68B19E6EB0D0028E608 /* MPProduct.h */, + 3B69B68C19E6EB0D0028E608 /* MPProduct.m */, + 3B69B6AF19E8175F0028E608 /* MPFactor.h */, + 3B69B6B019E8175F0028E608 /* MPFactor.m */, + 3B69B69519E6F0780028E608 /* MPValueGroup.h */, + 3B69B69619E6F0780028E608 /* MPValueGroup.m */, + 3B69B69B19E6F2110028E608 /* MPNumber.h */, + 3B69B69C19E6F2110028E608 /* MPNumber.m */, + 3B69B69F19E6F2420028E608 /* MPVariable.h */, + 3B69B6A019E6F2420028E608 /* MPVariable.m */, + 3B69B6A719E6F2AF0028E608 /* MPFunction+MPValue.h */, + 3B69B6A819E6F2AF0028E608 /* MPFunction+MPValue.m */, + 3B69B6BB19E948740028E608 /* MPFunctionValue.h */, + 3B69B6BC19E948740028E608 /* MPFunctionValue.m */, + 3B69B6B319E88E750028E608 /* MPUnexpectedSymbolValue.h */, + 3B69B6B419E88E750028E608 /* MPUnexpectedSymbolValue.m */, + 3B69B6BF19E953590028E608 /* MPFactorial.h */, + 3B69B6C019E953590028E608 /* MPFactorial.m */, + ); + name = "Expression Tree"; + sourceTree = ""; + }; 3B7172E519C7112C00FEAA5B /* Resources */ = { isa = PBXGroup; children = ( @@ -356,21 +442,6 @@ name = Resources; sourceTree = ""; }; - 3BAF938F19D6D535002C5EC6 /* Tokenizer */ = { - isa = PBXGroup; - children = ( - 3B7B3A1619CC44E4005849E5 /* MPExpressionTokenizer.h */, - 3B7B3A1719CC44E4005849E5 /* MPExpressionTokenizer.m */, - 3B7B3A2A19CC467E005849E5 /* MPToken.h */, - 3B7B3A2B19CC467E005849E5 /* MPToken.m */, - 3B7B3A5219CC50B1005849E5 /* MPFunction+MPToken.h */, - 3B7B3A5319CC50B1005849E5 /* MPFunction+MPToken.m */, - 3BB18AA319CDB3A900986DA0 /* MPTokenStream.h */, - 3BB18AA419CDB3A900986DA0 /* MPTokenStream.m */, - ); - name = Tokenizer; - sourceTree = ""; - }; 3BB09EBC1905EF210080A5ED /* Functions */ = { isa = PBXGroup; children = ( @@ -380,6 +451,8 @@ 3B7172ED19C9FA8E00FEAA5B /* MPParenthesisFunction.m */, 3B5FF73919DB2FF500C8348A /* MPPowerFunction.h */, 3B5FF73A19DB2FF500C8348A /* MPPowerFunction.m */, + 3B69B67A19E4915E0028E608 /* MPFractionFunction.h */, + 3B69B67B19E4915E0028E608 /* MPFractionFunction.m */, ); name = Functions; sourceTree = ""; @@ -413,6 +486,8 @@ 3B7172F119C9FC6700FEAA5B /* MPParenthesisFunctionLayout.m */, 3B69B66A19DB41B90028E608 /* MPPowerFunctionLayout.h */, 3B69B66B19DB41B90028E608 /* MPPowerFunctionLayout.m */, + 3B69B67E19E493630028E608 /* MPFractionFunctionLayout.h */, + 3B69B67F19E493630028E608 /* MPFractionFunctionLayout.m */, ); name = "Function Layouts"; sourceTree = ""; @@ -434,6 +509,17 @@ 3BFAC3961997B67400B3EF67 /* MPExpressionElement.h */, 3B87E35E19009D5F00259938 /* MPFunction.h */, 3B87E35F19009D5F00259938 /* MPFunction.m */, + 3B69B6B719E935E20028E608 /* MPSuffixFunction.h */, + 3B69B6B819E935E20028E608 /* MPSuffixFunction.m */, + 3B7B3A1619CC44E4005849E5 /* MPExpressionTokenizer.h */, + 3B7B3A1719CC44E4005849E5 /* MPExpressionTokenizer.m */, + 3B7B3A2A19CC467E005849E5 /* MPToken.h */, + 3B7B3A2B19CC467E005849E5 /* MPToken.m */, + 3B7B3A5219CC50B1005849E5 /* MPFunction+MPToken.h */, + 3B7B3A5319CC50B1005849E5 /* MPFunction+MPToken.m */, + 3BB18AA319CDB3A900986DA0 /* MPTokenStream.h */, + 3BB18AA419CDB3A900986DA0 /* MPTokenStream.m */, + 3B69B68219E6E8A50028E608 /* Expression Tree */, 3BC46B4F19B506F20033F13A /* Evaluation */, ); name = "Base Expression Classes"; @@ -466,15 +552,10 @@ 3BC46B4F19B506F20033F13A /* Evaluation */ = { isa = PBXGroup; children = ( - 3BC46B4719B38C980033F13A /* MPExpressionEvaluator.h */, - 3BC46B4819B38C980033F13A /* MPExpressionEvaluator.m */, 3B78C85219C2DA5300516335 /* MPEvaluationContext.h */, 3B78C85319C2DA5300516335 /* MPEvaluationContext.m */, 3B591BB919C58D000061D86B /* MPMathRules.h */, 3B591BBA19C58D000061D86B /* MPMathRules.m */, - 3BAF938F19D6D535002C5EC6 /* Tokenizer */, - 3BAF938B19D6D508002C5EC6 /* MPTerm.h */, - 3BAF938C19D6D508002C5EC6 /* MPTerm.m */, 3B52CECE19BE509C00CEDCFC /* MPParseError.h */, 3B52CECF19BE509C00CEDCFC /* MPParseError.m */, ); @@ -574,19 +655,27 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 3B69B6C519E95B250028E608 /* MPOperatorChain.h in Headers */, 3B85834C19BB661100D76A8D /* MathKit.h in Headers */, 3B85833819BB63BD00D76A8D /* MPExpression.h in Headers */, 3B85833919BB63C500D76A8D /* MPExpressionElement.h in Headers */, - 3BAF938D19D6D508002C5EC6 /* MPTerm.h in Headers */, + 3B69B68D19E6EB0D0028E608 /* MPProduct.h in Headers */, + 3B69B6B919E935E20028E608 /* MPSuffixFunction.h in Headers */, + 3B69B67C19E4915E0028E608 /* MPFractionFunction.h in Headers */, 3B78C85419C2DA5300516335 /* MPEvaluationContext.h in Headers */, 3B85834419BB654D00D76A8D /* NSString+MPExpressionElement.h in Headers */, + 3B69B6B519E88E750028E608 /* MPUnexpectedSymbolValue.h in Headers */, 3B7172F219C9FC6700FEAA5B /* MPParenthesisFunctionLayout.h in Headers */, 3B52CEDC19BEE63000CEDCFC /* NSRegularExpression+MPParsingAdditions.h in Headers */, 3B85833A19BB63D400D76A8D /* MPFunction.h in Headers */, + 3B69B6B119E8175F0028E608 /* MPFactor.h in Headers */, 3B69B66C19DB41B90028E608 /* MPPowerFunctionLayout.h in Headers */, + 3B69B69719E6F0780028E608 /* MPValueGroup.h in Headers */, 3B85834319BB653700D76A8D /* MPSumFunction.h in Headers */, 3B85834119BB651E00D76A8D /* MPRangePath.h in Headers */, + 3B69B6C119E953590028E608 /* MPFactorial.h in Headers */, 3B85834219BB652900D76A8D /* MPException.h in Headers */, + 3B69B69D19E6F2110028E608 /* MPNumber.h in Headers */, 3B85834519BB655200D76A8D /* NSIndexPath+MPAdditions.h in Headers */, 3B5FF73B19DB2FF500C8348A /* MPPowerFunction.h in Headers */, 3B52CED019BE509C00CEDCFC /* MPParseError.h in Headers */, @@ -596,13 +685,19 @@ 3BB18AA519CDB3A900986DA0 /* MPTokenStream.h in Headers */, 3BF59AFE19D80ECC00E54292 /* MPFunctionsViewController.h in Headers */, 3B7B3A2C19CC467E005849E5 /* MPToken.h in Headers */, + 3B69B68919E6E9280028E608 /* MPSummand.h in Headers */, 3B7B3A1819CC44E4005849E5 /* MPExpressionTokenizer.h in Headers */, - 3B85833E19BB651400D76A8D /* MPExpressionEvaluator.h in Headers */, + 3B69B69019E6ED6E0028E608 /* MPExpressionTreeElement.h in Headers */, 3BF59B0319D81EE600E54292 /* MPWhiteView.h in Headers */, 3B85834819BB65B600D76A8D /* MPLayout.h in Headers */, + 3B69B6A919E6F2AF0028E608 /* MPFunction+MPValue.h in Headers */, + 3B69B68519E6E8B20028E608 /* MPExpressionTree.h in Headers */, 3B85834919BB65B600D76A8D /* MPExpressionLayout.h in Headers */, 3B7B3A5419CC50B1005849E5 /* MPFunction+MPToken.h in Headers */, + 3B69B6BD19E948740028E608 /* MPFunctionValue.h in Headers */, 3B591BBB19C58D000061D86B /* MPMathRules.h in Headers */, + 3B69B68019E493630028E608 /* MPFractionFunctionLayout.h in Headers */, + 3B69B6A119E6F2420028E608 /* MPVariable.h in Headers */, 3B85834A19BB65B600D76A8D /* MPFunctionLayout.h in Headers */, 3B85834B19BB65B600D76A8D /* MPSumFunctionLayout.h in Headers */, ); @@ -776,23 +871,35 @@ 3BBEA94219BB79A700133766 /* MPExpressionLayout.m in Sources */, 3B7B3A1919CC44E4005849E5 /* MPExpressionTokenizer.m in Sources */, 3B591BBC19C58D000061D86B /* MPMathRules.m in Sources */, + 3B69B6BE19E948740028E608 /* MPFunctionValue.m in Sources */, 3B7172EF19C9FA8E00FEAA5B /* MPParenthesisFunction.m in Sources */, 3B7B3A5519CC50B1005849E5 /* MPFunction+MPToken.m in Sources */, 3BBEA94119BB79A700133766 /* MPLayout.m in Sources */, - 3BAF938E19D6D508002C5EC6 /* MPTerm.m in Sources */, + 3B69B6B219E8175F0028E608 /* MPFactor.m in Sources */, + 3B69B6BA19E935E20028E608 /* MPSuffixFunction.m in Sources */, 3BBEA93D19BB79A700133766 /* NSString+MPExpressionElement.m in Sources */, + 3B69B68119E493630028E608 /* MPFractionFunctionLayout.m in Sources */, + 3B69B68619E6E8B20028E608 /* MPExpressionTree.m in Sources */, 3BF59B0419D81EE600E54292 /* MPWhiteView.m in Sources */, 3BBEA94019BB79A700133766 /* MPExpressionStorage.m in Sources */, 3BBEA93F19BB79A700133766 /* MPExpressionView.m in Sources */, + 3B69B6AA19E6F2AF0028E608 /* MPFunction+MPValue.m in Sources */, 3B7B3A2D19CC467E005849E5 /* MPToken.m in Sources */, + 3B69B6C619E95B250028E608 /* MPOperatorChain.m in Sources */, 3B78C85519C2DA5300516335 /* MPEvaluationContext.m in Sources */, + 3B69B6A219E6F2420028E608 /* MPVariable.m in Sources */, + 3B69B69819E6F0780028E608 /* MPValueGroup.m in Sources */, 3BBEA94419BB79A700133766 /* MPSumFunctionLayout.m in Sources */, + 3B69B6C219E953590028E608 /* MPFactorial.m in Sources */, + 3B69B69E19E6F2110028E608 /* MPNumber.m in Sources */, 3B7172F319C9FC6700FEAA5B /* MPParenthesisFunctionLayout.m in Sources */, - 3BBEA93719BB79A700133766 /* MPExpressionEvaluator.m in Sources */, 3BBEA93A19BB79A700133766 /* MPSumFunction.m in Sources */, 3BBEA93E19BB79A700133766 /* NSIndexPath+MPAdditions.m in Sources */, 3B52CEDD19BEE63000CEDCFC /* NSRegularExpression+MPParsingAdditions.m in Sources */, + 3B69B67D19E4915E0028E608 /* MPFractionFunction.m in Sources */, 3BBEA93C19BB79A700133766 /* MPException.m in Sources */, + 3B69B68E19E6EB0D0028E608 /* MPProduct.m in Sources */, + 3B69B6B619E88E750028E608 /* MPUnexpectedSymbolValue.m in Sources */, 3BF59AFF19D80ECC00E54292 /* MPFunctionsViewController.m in Sources */, 3B52CED119BE509C00CEDCFC /* MPParseError.m in Sources */, 3BB18AA619CDB3A900986DA0 /* MPTokenStream.m in Sources */, @@ -800,6 +907,7 @@ 3BBEA93619BB79A700133766 /* MPFunction.m in Sources */, 3BBEA93519BB79A700133766 /* MPExpression.m in Sources */, 3BBEA93B19BB79A700133766 /* MPRangePath.m in Sources */, + 3B69B68A19E6E9280028E608 /* MPSummand.m in Sources */, 3BBEA94319BB79A700133766 /* MPFunctionLayout.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/MathPad/MPExpression.h b/MathPad/MPExpression.h index f89ad28..9e934fe 100644 --- a/MathPad/MPExpression.h +++ b/MathPad/MPExpression.h @@ -8,6 +8,7 @@ @import Foundation; #import "NSString+MPExpressionElement.h" +#import "MPExpressionTree.h" #import "MPToken.h" typedef NS_ENUM(NSUInteger, MPReferenceFrame) { @@ -77,6 +78,8 @@ typedef NS_ENUM(NSUInteger, MPReferenceFrame) { */ - (instancetype)init; +- (instancetype)initWithExpressionTree:(MPExpressionTree *)expressionTree; + /*! @method initWithElement: @@ -358,6 +361,7 @@ typedef NS_ENUM(NSUInteger, MPReferenceFrame) { */ - (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error; +- (MPExpressionTree *)parse; #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 diff --git a/MathPad/MPExpression.m b/MathPad/MPExpression.m index 6c57f58..9b100fa 100644 --- a/MathPad/MPExpression.m +++ b/MathPad/MPExpression.m @@ -14,7 +14,6 @@ #import "MPException.h" #import "MPExpressionTokenizer.h" -#import "MPExpressionEvaluator.h" #import "MPToken.h" @@ -55,6 +54,11 @@ return [self initWithElements:@[]]; } +- (instancetype)initWithExpressionTree:(MPExpressionTree *)expressionTree +{ + return [self initWithElements:expressionTree.expressionElements]; +} + - (instancetype)initWithElement:(id)element { return [self initWithElements:@[element]]; @@ -518,13 +522,22 @@ #pragma mark Evaluating Expressions - - (NSDecimalNumber *)evaluateWithError:(MPParseError *__autoreleasing *)error { - MPExpressionEvaluator *evaluator = [[MPExpressionEvaluator alloc] initWithExpression:self]; - MPTerm *term = [evaluator parseExpectingVariable:NO - error:error]; - return [term evaluate]; + MPExpressionTree *tree = [self parse]; + if ([tree validate:error]) { + return [tree evaluate]; + } + if (error) { + (*error).pathToExpression = self.indexPath; + } + return nil; +} + +- (MPExpressionTree *)parse +{ + MPTokenStream *tokenStream = [[MPTokenStream alloc] initWithTokens:self.tokens]; + return [[MPExpressionTree alloc] initWithTokenStream:tokenStream]; } #pragma mark Notifications diff --git a/MathPad/MPExpressionEvaluator.h b/MathPad/MPExpressionEvaluator.h deleted file mode 100644 index 1ed91cf..0000000 --- a/MathPad/MPExpressionEvaluator.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// MPExpressionEvaluator.h -// MathPad -// -// Created by Kim Wittenburg on 31.08.14. -// Copyright (c) 2014 Kim Wittenburg. All rights reserved. -// - -#import -#import "MPExpression.h" -#import "MPExpressionTokenizer.h" -#import "MPTerm.h" - -@class MPExpressionEvaluator, MPExpression, MPParsedElementOld; - -@interface MPExpressionEvaluator : NSObject - -- (instancetype)initWithExpression:(MPExpression *)expression; -@property (readonly, nonatomic, weak) MPExpression *expression; - -#pragma mark Evaluating Expressions -@property (readonly, nonatomic, copy) NSString *definedVariable; - -- (MPTerm *)parseExpectingVariable:(BOOL)flag error:(MPParseError *__autoreleasing *)error; - -@end diff --git a/MathPad/MPExpressionEvaluator.m b/MathPad/MPExpressionEvaluator.m deleted file mode 100644 index fb49bf2..0000000 --- a/MathPad/MPExpressionEvaluator.m +++ /dev/null @@ -1,261 +0,0 @@ -// -// MPExpressionEvaluator.m -// MathPad -// -// Created by Kim Wittenburg on 31.08.14. -// Copyright (c) 2014 Kim Wittenburg. All rights reserved. -// - -#import "MPExpressionEvaluator.h" -#import "MPExpression.h" -#import "MPFunction.h" -#import "MPMathRules.h" -#import "MPToken.h" -#import "MPFunction+MPToken.h" -#import "MPTokenStream.h" - -#import "MPEvaluationContext.h" -#import "MPPowerFunction.h" - - - -@interface MPExpressionEvaluator () -@property (readwrite, nonatomic, copy) NSString *definedVariable; - - -- (void)setError:(MPParseError *)error; - -@end - -@implementation MPExpressionEvaluator { - MPParseError *__autoreleasing *_error; - MPTokenStream *tokenStream; -} - -- (id)initWithExpression:(MPExpression *)expression -{ - self = [super init]; - if (self) { - _expression = expression; - } - return self; -} - -#pragma mark Evaluating Expressions -- (void)setError:(MPParseError *)error -{ - if (_error) { - error.pathToExpression = self.expression.indexPath; - *_error = error; - } -} - -- (MPTerm *)parseExpectingVariable:(BOOL)flag - error:(MPParseError *__autoreleasing *)error -{ - _error = error; - tokenStream = [[MPTokenStream alloc] initWithTokens:[self.expression allItemsInReferenceFrame:MPTokenReferenceFrame]]; - if (!tokenStream.hasMoreTokens) { - self.error = MPParseError(NSMakeRange(0, 0), @"Empty Expression"); - return nil; - } - - // Variable Definition - if (flag) { - MPToken *variableToken = [tokenStream nextToken]; - MPToken *equalsToken = [tokenStream nextTokenOfType:MPEqualsToken]; - - if (variableToken.tokenType != MPVariableToken) { - self.error = MPParseError(variableToken.range, @"Expected Variable Definition."); - return nil; - } - if (equalsToken.tokenType != MPEqualsToken) { - self.error = MPParseError(equalsToken.range, @"Expected \"=\"."); - return nil; - } - self.definedVariable = variableToken.stringValue; - } - - BOOL isAtBeginning = YES; - NSMutableArray *summands = [[NSMutableArray alloc] init]; - NSMutableArray *currentSummand = [[NSMutableArray alloc] init]; - - while (tokenStream.hasMoreTokens) { - MPToken *multiplicationToken = [tokenStream nextTokenOfType:MPMultiplicationSymbolToken]; - MPToken *operatorToken = [tokenStream nextTokenOfType:MPOperatorListToken]; - NSRange valueRange; - MPTerm *value = [self nextValue:&valueRange]; - - if (multiplicationToken) { - if (isAtBeginning) { - self.error = MPParseError(multiplicationToken.range, @"Unexpected Symbol. Expected Number."); - return nil; - } - if (operatorToken) { - if (operatorToken.numberOfOperators > [[MPMathRules sharedRules] maximumOperatorChainLengthInMultiplication]) { - self.error = MPParseError(operatorToken.range, @"Too many operators in multiplication"); - return nil; - } - [currentSummand addObject:[[MPTerm alloc] initWithNumber:operatorToken.operatorValue]]; - } - if (value) { - [currentSummand addObject:value]; - } else { - return nil; - } - } else { - if (operatorToken) { - if (operatorToken.numberOfOperators > [[MPMathRules sharedRules] maximumOperatorChainLength]) { - self.error = MPParseError(operatorToken.range, @"Too many operators."); - return nil; - } - [summands addObject:[[MPTerm alloc] initWithFactors:currentSummand.copy]]; - currentSummand = [[NSMutableArray alloc] init]; - [currentSummand addObject:[[MPTerm alloc] initWithNumber:operatorToken.operatorValue]]; - if (value) { - [currentSummand addObject:value]; - } else { - return nil; - } - } else if (!value) { - return nil; - } else if (isAtBeginning || [[MPMathRules sharedRules] allowsImplicitMultiplication]) { - [currentSummand addObject:value]; - } else { - self.error = MPParseError(NSMakeRange(valueRange.location, 0), @"Implicit Multiplication not allowed here"); - return nil; - } - } - - isAtBeginning = NO; - } - [summands addObject:[[MPTerm alloc] initWithFactors:currentSummand.copy]]; - return [[MPTerm alloc] initWithSummands:summands.copy]; -} - -- (MPTerm *)nextValue:(NSRangePointer)range -{ - MPToken *token = [tokenStream nextToken]; - if (range) { - *range = token.range; - } - switch (token.tokenType) { - case MPNumberToken: - { - return [self decoratedTerm:[[MPTerm alloc] initWithNumber:token.number]]; - } - - case MPVariableToken: - { - if(![[MPEvaluationContext sharedContext] isVariableDefined:token.variable]) { - self.error = MPParseError(token.range, @"Undefined Variable"); - return nil; - } - return [self decoratedTerm:[[MPTerm alloc] initWithVariable:token.variable]]; - } - - 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]]; - } - - case MPSinToken: - { - BOOL inverse = [self inverseFunction]; - NSRange sinTermRange; - MPTerm *sinTerm = [self nextValue:&sinTermRange]; - if (!sinTerm) { - if (range) { - *range = NSUnionRange(token.range, sinTermRange); - } - return nil; - } - return inverse ? [[MPTerm alloc] initWithInverseSinOfTerm:sinTerm] : [[MPTerm alloc] initWithSinOfTerm:sinTerm]; - } - - case MPCosToken: - { - BOOL inverse = [self inverseFunction]; - NSRange cosTermRange; - MPTerm *cosTerm = [self nextValue:&cosTermRange]; - if (!cosTerm) { - if (range) { - *range = NSUnionRange(token.range, cosTermRange); - } - return nil; - } - return inverse ? [[MPTerm alloc] initWithInverseCosOfTerm:cosTerm] : [[MPTerm alloc] initWithCosOfTerm:cosTerm]; - } - - case MPTanToken: - { - BOOL inverse = [self inverseFunction]; - NSRange tanTermRange; - MPTerm *tanTerm = [self nextValue:&tanTermRange]; - if (!tanTerm) { - if (range) { - *range = NSUnionRange(token.range, tanTermRange); - } - return nil; - } - return inverse ? [[MPTerm alloc] initWithInverseTanOfTerm:tanTerm] : [[MPTerm alloc] initWithTanOfTerm:tanTerm]; - } - - case MPEOFToken: - { - self.error = MPParseError(token.range, @"Unexpected End. Expected Number."); - return nil; - } - - default: - { - self.error = MPParseError(token.range, @"Unexpected Symbol. Expected Number."); - return nil; - } - } -} - -- (MPTerm *)decoratedTerm:(MPTerm *)term -{ - MPTerm *decoratedTerm = term; - MPToken *facultyToken = [tokenStream nextTokenOfType:MPFactorialToken]; - if (facultyToken) { - decoratedTerm = [[MPTerm alloc] initWithFactorialOfTerm:term]; - } - MPToken *powerToken = [tokenStream nextTokenOfType:MPGenericFunctionToken]; - if ([powerToken isKindOfClass:[MPPowerFunction class]]) { - MPPowerFunction *powerFunction = (MPPowerFunction *)powerToken; - powerFunction.baseTerm = decoratedTerm; - return [powerFunction parseWithError:_error]; - } else if (powerToken) { - tokenStream.currentTokenIndex--; - } - return decoratedTerm; -} - -- (BOOL)inverseFunction -{ - MPToken *powerToken = [tokenStream nextTokenOfType:MPGenericFunctionToken]; - if ([powerToken isKindOfClass:[MPPowerFunction class]]) { - MPPowerFunction *powerFunction = (MPPowerFunction *)powerToken; - if (powerFunction.exponentExpression.countElements == 1) { - id 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 diff --git a/MathPad/MPExpressionLayout.h b/MathPad/MPExpressionLayout.h index 7e9a8f9..136b93b 100644 --- a/MathPad/MPExpressionLayout.h +++ b/MathPad/MPExpressionLayout.h @@ -11,6 +11,10 @@ #import "MPExpression.h" #import "MPFunctionLayout.h" +#define kMPEmptyBoxWidth (self.usesSmallSize ? 2.0 : 3.0) +#define kMPEmptyBoxHeight (CTFontGetDescent((CTFontRef)self.font) + CTFontGetAscent((CTFontRef)self.font) + CTFontGetLeading((CTFontRef)self.font)) +#define kMPEmptyBoxYOrigin (-(CTFontGetDescent((CTFontRef)self.font) + CTFontGetLeading((CTFontRef)self.font))) + @interface MPExpressionLayout : MPLayout - (instancetype)initWithExpression:(MPExpression *)expression diff --git a/MathPad/MPExpressionLayout.m b/MathPad/MPExpressionLayout.m index ffa8566..31da0ca 100644 --- a/MathPad/MPExpressionLayout.m +++ b/MathPad/MPExpressionLayout.m @@ -14,10 +14,6 @@ #import "NSString+MPExpressionElement.h" #import "NSIndexPath+MPAdditions.h" -#define kMPEmptyBoxWidth (self.usesSmallSize ? 2.0 : 3.0) -#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 kMPEmptyBoxDrawingWidth kMPEmptyBoxWidth #define kMPEmptyBoxDrawingHeight (CTFontGetDescent((CTFontRef)self.font) + CTFontGetAscent((CTFontRef)self.font)) #define kMPEmptyBoxDrawingYOrigin (-(CTFontGetDescent((CTFontRef)self.font) + CTFontGetLeading((CTFontRef)self.font)/2)) @@ -60,6 +56,11 @@ } #pragma mark Cache Methods +- (NSUInteger)numberOfChildren +{ + return self.expression.countElements; +} + - (MPLayout *)childLayoutAtIndex:(NSUInteger)index { id cachedObject = [self cachableObjectForIndex:index generator:^id{ @@ -216,6 +217,11 @@ } } +- (BOOL)drawsChildrenManually +{ + return YES; +} + - (void)draw { // Get the current context diff --git a/MathPad/MPExpressionTokenizer.m b/MathPad/MPExpressionTokenizer.m index 7cd7ec6..39ad08f 100644 --- a/MathPad/MPExpressionTokenizer.m +++ b/MathPad/MPExpressionTokenizer.m @@ -42,16 +42,19 @@ NSString *decimalSeparator = [NSRegularExpression escapedPatternForString:[[NSLocale currentLocale] objectForKey:NSLocaleDecimalSeparator]]; NSString *regexStringFormat = @"\\A(?:" - @"(\\*)|" + @"([\\*∙⋅])|" @"([+-](?:\\s*[+-])*)|" @"((?:\\d+(?:%@\\d+)?)|(?:%@\\d+))|" @"(sin)|" @"(cos)|" @"(tan)|" + @"(asin)|" + @"(acos)|" + @"(atan)|" @"([A-Za-z])|" @"(!)|" @"(=)|" - @"(\\s)" + @"(\\s+)" @")"; NSString *regexString = [NSString stringWithFormat:regexStringFormat, decimalSeparator, decimalSeparator]; NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regexString @@ -70,10 +73,13 @@ NSRange sinRange = [match rangeAtIndex:4]; NSRange cosRange = [match rangeAtIndex:5]; NSRange tanRange = [match rangeAtIndex:6]; - NSRange variableRange = [match rangeAtIndex:7]; - NSRange factorialRange = [match rangeAtIndex:8]; - NSRange equalsRange = [match rangeAtIndex:9]; - NSRange whitespaceRange = [match rangeAtIndex:10]; + NSRange asinRange = [match rangeAtIndex:7]; + NSRange acosRange = [match rangeAtIndex:8]; + NSRange atanRange = [match rangeAtIndex:9]; + NSRange variableRange = [match rangeAtIndex:10]; + NSRange factorialRange = [match rangeAtIndex:11]; + NSRange equalsRange = [match rangeAtIndex:12]; + NSRange whitespaceRange = [match rangeAtIndex:13]; if (MPRangeExists(multiplicationSymbolRange)) { range = multiplicationSymbolRange; @@ -93,6 +99,15 @@ } else if (MPRangeExists(tanRange)) { range = tanRange; tokenType = MPTanToken; + } else if (MPRangeExists(asinRange)) { + range = asinRange; + tokenType = MPASinToken; + } else if (MPRangeExists(acosRange)) { + range = acosRange; + tokenType = MPACosToken; + } else if (MPRangeExists(atanRange)) { + range = atanRange; + tokenType = MPATanToken; } else if (MPRangeExists(variableRange)) { range = variableRange; tokenType = MPVariableToken; diff --git a/MathPad/MPExpressionTree.h b/MathPad/MPExpressionTree.h new file mode 100644 index 0000000..4690b01 --- /dev/null +++ b/MathPad/MPExpressionTree.h @@ -0,0 +1,28 @@ +// +// MPExpressionTree.h +// MathPad +// +// Created by Kim Wittenburg on 09.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import +#import "MPSummand.h" +#import "MPExpressionTreeElement.h" + +@interface MPExpressionTree : NSObject + +@property (nonatomic, copy) NSString *definedVariable; + +- (NSArray *)summands; +- (void)appendSummand:(MPSummand *)summand; +- (void)insertSummand:(MPSummand *)summand + atIndex:(NSUInteger)index; +- (void)removeSummand:(MPSummand *)summand; +- (void)removeSummandAtIndex:(NSUInteger)index; +- (MPSummand *)summandAtIndex:(NSUInteger)index; + +- (BOOL)validateExpectingVariableDefinition:(BOOL)flag + error:(MPParseError *__autoreleasing *)error; + +@end diff --git a/MathPad/MPExpressionTree.m b/MathPad/MPExpressionTree.m new file mode 100644 index 0000000..61b01fd --- /dev/null +++ b/MathPad/MPExpressionTree.m @@ -0,0 +1,158 @@ +// +// MPExpressionTree.m +// MathPad +// +// Created by Kim Wittenburg on 09.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import "MPExpressionTree.h" + +@implementation MPExpressionTree { + NSRange _variableDefinitionRange; + NSMutableArray *_summands; + NSRange _range; +} + +- (id)init +{ + self = [super init]; + if (self) { + _summands = [[NSMutableArray alloc] init]; + } + return self; +} + +- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream +{ + self = [self init]; + if (self) { + MPTokenStreamRecordCurrentLocation(tokenStream); + + [tokenStream beginIgnoringWhitespaceTokens]; + + _variableDefinitionRange = NSMakeRange(NSNotFound, 0); + self.definedVariable = nil; + if (tokenStream.currentToken.tokenType == MPVariableToken) { + if (tokenStream.peekNextToken.tokenType == MPEqualsToken) { + self.definedVariable = tokenStream.currentToken.stringValue; + NSRange variableRange = tokenStream.currentToken.range; + [tokenStream currentTokenConsumed]; + NSRange equalsRange = tokenStream.currentToken.range; + [tokenStream currentTokenConsumed]; + _variableDefinitionRange = NSUnionRange(variableRange, equalsRange); + } + } + while ([tokenStream hasMoreTokens]) { + [_summands addObject:[[MPSummand alloc] initWithTokenStream:tokenStream]]; + } + + [tokenStream endIgnoringOrAcceptingWhitespaceTokens]; + _range = MPTokenStreamRecordedRange(tokenStream); + } + return self; +} + +- (NSRange)range +{ + return _range; +} + +- (BOOL)validate:(MPParseError *__autoreleasing *)error +{ + return [self validateExpectingVariableDefinition:NO + error:error]; +} + +- (BOOL)validateExpectingVariableDefinition:(BOOL)flag + error:(MPParseError *__autoreleasing *)error +{ + if (flag) { + if (!self.definedVariable) { + if (error) { + *error = MPParseError(NSMakeRange(self.range.location, 0), @"Expected Variable Definition."); + } + return NO; + } + } else { + if (self.definedVariable) { + if (error) { + *error = MPParseError(_variableDefinitionRange, @"Unexpected Variable Definition"); + } + return NO; + } + } + if (_summands.count == 0) { + if (error) { + *error = MPParseError(self.range, @"Empty Expression."); + } + return NO; + } + for (MPSummand *summand in _summands) { + if (![summand validate:error]) { + return NO; + } + } + return YES; +} + +- (NSDecimalNumber *)evaluate +{ + NSDecimalNumber *value = [NSDecimalNumber zero]; + for (MPSummand *summand in _summands) { + NSDecimalNumber *currentValue = [summand evaluate]; + if ([currentValue isEqualToNumber:[NSDecimalNumber notANumber]]) { + value = currentValue; + break; + } + value = [value decimalNumberByAdding:currentValue]; + } + return value; +} + + +- (NSArray *)expressionElements +{ + NSMutableArray *elements = [[NSMutableArray alloc] init]; + if (self.definedVariable) { + [elements addObject:[NSString stringWithFormat:@"%@=", self.definedVariable]]; + } + for (MPSummand *summand in _summands) { + [elements addObjectsFromArray:summand.expressionElements]; + } + return elements; +} + +- (NSArray *)summands +{ + return _summands; +} + +- (void)appendSummand:(MPSummand *)summand +{ + [_summands addObject:summand]; +} + +- (void)insertSummand:(MPSummand *)summand + atIndex:(NSUInteger)index +{ + [_summands insertObject:summand + atIndex:index]; +} + +- (void)removeSummand:(MPSummand *)summand +{ + [_summands removeObject:summand]; +} + +- (void)removeSummandAtIndex:(NSUInteger)index +{ + [_summands removeObjectAtIndex:index]; +} + +- (MPSummand *)summandAtIndex:(NSUInteger)index +{ + return [_summands objectAtIndex:index]; +} + +@end diff --git a/MathPad/MPExpressionTreeElement.h b/MathPad/MPExpressionTreeElement.h new file mode 100644 index 0000000..cf9e97e --- /dev/null +++ b/MathPad/MPExpressionTreeElement.h @@ -0,0 +1,30 @@ +// +// MPExpressionTreeElement.h +// MathPad +// +// Created by Kim Wittenburg on 09.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import +#import "MPTokenStream.h" +#import "MPParseError.h" +#import "MPTokenStream.h" + +#define MPTokenStreamRecordCurrentLocation(tokenStream) NSUInteger __tokenStreamStart = tokenStream.currentToken.range.location +#define MPTokenStreamRecordedRange(tokenStream) NSMakeRange(__tokenStreamStart, tokenStream.currentToken.range.location - __tokenStreamStart) + +@protocol MPExpressionTreeElement +@required + +- (instancetype)init; +- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream; + +- (NSRange)range; + +- (BOOL)validate:(MPParseError *__autoreleasing *)error; +- (NSDecimalNumber *)evaluate; + +- (NSArray *)expressionElements; // Converts + +@end diff --git a/MathPad/MPExpressionView.m b/MathPad/MPExpressionView.m index 989875b..077ffa4 100644 --- a/MathPad/MPExpressionView.m +++ b/MathPad/MPExpressionView.m @@ -582,6 +582,9 @@ [allowedCharacters addCharactersInString:[NSString stringWithFormat:@"+-*= !%@", decimalSeparator]]; if (characters.length == 1 && [characters stringByTrimmingCharactersInSet:allowedCharacters].length == 0) { + if ([characters isEqualToString:@"*"]) { + characters = @"⋅"; + } [self.expressionStorage replaceItemsInRangePath:self.selection referenceFrame:MPSymbolReferenceFrame withElements:@[characters]]; diff --git a/MathPad/MPFactor.h b/MathPad/MPFactor.h new file mode 100644 index 0000000..9d3e73a --- /dev/null +++ b/MathPad/MPFactor.h @@ -0,0 +1,22 @@ +// +// MPFactor.h +// MathPad +// +// Created by Kim Wittenburg on 09.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import +#import "MPExpressionTreeElement.h" +#import "MPValueGroup.h" +#import "MPOperatorChain.h" + +@interface MPFactor : NSObject + +@property (nonatomic) BOOL hasMultiplicationSymbol; +@property (nonatomic, strong) MPOperatorChain *operatorChain; +@property (nonatomic, strong) id value; + +- (NSRange)multiplicationSymbolRange; + +@end diff --git a/MathPad/MPFactor.m b/MathPad/MPFactor.m new file mode 100644 index 0000000..94d9503 --- /dev/null +++ b/MathPad/MPFactor.m @@ -0,0 +1,95 @@ +// +// MPFactor.m +// MathPad +// +// Created by Kim Wittenburg on 09.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import "MPFactor.h" +#import "MPMathRules.h" + +@implementation MPFactor { + NSRange _multiplicationSymbolRange; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + + } + return self; +} + +- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream +{ + self = [self init]; + if (self) { + [tokenStream beginIgnoringWhitespaceTokens]; + MPToken *token = tokenStream.currentToken; + if (token.tokenType == MPMultiplicationSymbolToken) { + _multiplicationSymbolRange = token.range; + [tokenStream currentTokenConsumed]; + } else { + _multiplicationSymbolRange = NSMakeRange(NSNotFound, 0); + } + if (tokenStream.currentToken.tokenType == MPOperatorListToken) { + _operatorChain = [[MPOperatorChain alloc] initWithTokenStream:tokenStream]; + } + MPValueGroup *valueGroup = [[MPValueGroup alloc] initWithTokenStream:tokenStream]; + _value = [tokenStream consumeSuffixesForValue:valueGroup]; + [tokenStream endIgnoringOrAcceptingWhitespaceTokens]; + } + return self; +} + +- (NSRange)range +{ + if (_multiplicationSymbolRange.location == NSNotFound) { + return self.value.range; + } + return NSUnionRange(_multiplicationSymbolRange, self.value.range); +} + +- (NSRange)multiplicationSymbolRange +{ + return _multiplicationSymbolRange; +} + +- (BOOL)hasMultiplicationSymbol +{ + return _multiplicationSymbolRange.location != NSNotFound; +} + +- (BOOL)validate:(MPParseError *__autoreleasing *)error +{ + if (self.operatorChain.numberOfOperators > [[MPMathRules sharedRules] maximumOperatorChainLengthInMultiplication]) { + if (error) { + *error = MPParseError(self.operatorChain.range, @"Too many operators in Multiplication"); + } + return NO; + } + return [self.value validate:error]; +} + +- (NSDecimalNumber *)evaluate +{ + NSDecimalNumber *value = [self.value evaluate]; + if ([value isNotEqualTo:[NSDecimalNumber notANumber]] && self.operatorChain) { + value = [value decimalNumberByMultiplyingBy:[self.operatorChain evaluate]]; + } + return value; +} + +- (NSArray *)expressionElements +{ + NSMutableArray *elements = [[NSMutableArray alloc] init]; + if (self.hasMultiplicationSymbol) { + [elements addObject:@"*"]; + } + [elements addObjectsFromArray:self.value.expressionElements]; + return elements; +} + +@end diff --git a/MathPad/MPFactorial.h b/MathPad/MPFactorial.h new file mode 100644 index 0000000..457f49e --- /dev/null +++ b/MathPad/MPFactorial.h @@ -0,0 +1,16 @@ +// +// MPFactorial.h +// MathPad +// +// Created by Kim Wittenburg on 11.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import +#import "MPValueGroup.h" + +@interface MPFactorial : NSObject + +@property (nonatomic, strong) id value; + +@end diff --git a/MathPad/MPFactorial.m b/MathPad/MPFactorial.m new file mode 100644 index 0000000..5fff083 --- /dev/null +++ b/MathPad/MPFactorial.m @@ -0,0 +1,58 @@ +// +// MPFactorial.m +// MathPad +// +// Created by Kim Wittenburg on 11.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import "MPFactorial.h" + +@implementation MPFactorial + +- (instancetype)init +{ + self = [super init]; + if (self) { + } + return self; +} + +- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream +{ + self = [self init]; + if (self) { + [tokenStream beginIgnoringWhitespaceTokens]; + if (tokenStream.currentToken.tokenType != MPFactorialToken) { + [tokenStream endIgnoringOrAcceptingWhitespaceTokens]; + return nil; + } else { + [tokenStream currentTokenConsumed]; + } + [tokenStream endIgnoringOrAcceptingWhitespaceTokens]; + } + return self; +} + +- (NSRange)range +{ + return NSMakeRange(0, 0); +} + +- (BOOL)validate:(MPParseError *__autoreleasing *)error +{ + return [self.value validate:error]; +} + +- (NSDecimalNumber *)evaluate +{ + double value = [self.value evaluate].doubleValue; + return [[NSDecimalNumber alloc] initWithDouble:tgamma(value+1)]; +} + +- (NSArray *)expressionElements +{ + return @[@"!"]; +} + +@end diff --git a/MathPad/MPFractionFunction.h b/MathPad/MPFractionFunction.h new file mode 100644 index 0000000..bfee7ed --- /dev/null +++ b/MathPad/MPFractionFunction.h @@ -0,0 +1,16 @@ +// +// MPFractionFunction.h +// MathPad +// +// Created by Kim Wittenburg on 07.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import + +@interface MPFractionFunction : MPFunction + +@property (nonatomic, strong) MPExpression *nominatorExpression; +@property (nonatomic, strong) MPExpression *denominatorExpression; + +@end diff --git a/MathPad/MPFractionFunction.m b/MathPad/MPFractionFunction.m new file mode 100644 index 0000000..f035cb7 --- /dev/null +++ b/MathPad/MPFractionFunction.m @@ -0,0 +1,36 @@ +// +// MPFractionFunction.m +// MathPad +// +// Created by Kim Wittenburg on 07.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import "MPFractionFunction.h" + +@implementation MPFractionFunction + +MPFunctionAccessorImplementation(NominatorExpression, _nominatorExpression) +MPFunctionAccessorImplementation(DenominatorExpression, _denominatorExpression) + +- (NSArray *)childrenAccessors +{ + return @[@"nominatorExpression", @"denominatorExpression"]; +} + +- (BOOL)validate:(MPParseError *__autoreleasing *)error +{ + return [[self.nominatorExpression parse] validate:error] && [[self.denominatorExpression parse] validate:error]; +} + +- (NSDecimalNumber *)evaluate +{ + return [[[self.nominatorExpression parse] evaluate] decimalNumberByDividingBy:[[self.denominatorExpression parse] evaluate]]; +} + +- (NSString *)description +{ + return [NSString stringWithFormat:@"%@ / %@", self.nominatorExpression, self.denominatorExpression]; +} + +@end diff --git a/MathPad/MPFractionFunctionLayout.h b/MathPad/MPFractionFunctionLayout.h new file mode 100644 index 0000000..d35d134 --- /dev/null +++ b/MathPad/MPFractionFunctionLayout.h @@ -0,0 +1,16 @@ +// +// MPFractionFunctionLayout.h +// MathPad +// +// Created by Kim Wittenburg on 07.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import "MPFunctionLayout.h" +#import "MPFractionFunction.h" + +@interface MPFractionFunctionLayout : MPFunctionLayout + +- (MPFractionFunction *)fractionFunction; + +@end diff --git a/MathPad/MPFractionFunctionLayout.m b/MathPad/MPFractionFunctionLayout.m new file mode 100644 index 0000000..e66e7ac --- /dev/null +++ b/MathPad/MPFractionFunctionLayout.m @@ -0,0 +1,87 @@ +// +// MPFractionFunctionLayout.m +// MathPad +// +// Created by Kim Wittenburg on 07.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import "MPFractionFunctionLayout.h" +#import "MPExpressionLayout.h" + +#define kFractionFunctionLineWidth 2 +#define kFractionFunctionHorizontalInset 2 +#define kFractionFunctionNominatorOffset 0 +#define kFractionFunctionDenominatorOffset 0 + +#define MPFractionMiddle (CTFontGetAscent((CTFontRef)self.font) / 2) + +@implementation MPFractionFunctionLayout + +- (MPFractionFunction *)fractionFunction +{ + return (MPFractionFunction *)self.function; +} + +- (NSUInteger)indexOfChildBelowChildAtIndex:(NSUInteger)index +{ + return 1; +} + +- (NSUInteger)indexOfChildAboveChildAtIndex:(NSUInteger)index +{ + return 0; +} + +- (NSIndexSet *)indexesOfRemainingChildren +{ + return [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 1)]; +} + +- (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index +{ + if (index == 0) { + CGFloat y = MPFractionMiddle + kFractionFunctionLineWidth / 2; + return NSMakePoint(0, y); + } else { + NSRect denominatorBounds = [self childLayoutAtIndex:1].bounds; + CGFloat y = CTFontGetAscent((CTFontRef)self.font) / 2 - kFractionFunctionLineWidth / 2; + y -= denominatorBounds.size.height - denominatorBounds.origin.y; + return NSMakePoint(0, y); + } +} + +- (BOOL)childAtIndexUsesSmallSize:(NSUInteger)index +{ + return YES; +} + +- (NSIndexPath *)indexPathForLocalMousePoint:(NSPoint)point +{ + if (point.x < self.bounds.size.width / 2) { + return [NSIndexPath indexPathWithIndex:0]; + } else { + return [NSIndexPath indexPathWithIndex:1]; + } +} + +- (NSRect)generateBounds +{ + NSRect nominatorBounds = [self childLayoutAtIndex:0].bounds; + NSRect denominatorBounds = [self childLayoutAtIndex:1].bounds; + NSRect bounds; + bounds.origin.x = 0; + CGFloat y = CTFontGetAscent((CTFontRef)self.font) / 2 - kFractionFunctionLineWidth / 2; + y -= denominatorBounds.size.height; + bounds.size.width = MAX(nominatorBounds.size.width, denominatorBounds.size.width); + bounds.size.height = nominatorBounds.size.height + denominatorBounds.size.height + kFractionFunctionLineWidth; + return bounds; +} + +- (void)draw +{ + CGFloat y = CTFontGetAscent((CTFontRef)self.font) / 2 - kFractionFunctionLineWidth / 2; + NSRectFill(NSMakeRect(0, y, self.bounds.size.width, kFractionFunctionLineWidth)); +} + +@end diff --git a/MathPad/MPFunction+MPValue.h b/MathPad/MPFunction+MPValue.h new file mode 100644 index 0000000..d2e2b07 --- /dev/null +++ b/MathPad/MPFunction+MPValue.h @@ -0,0 +1,15 @@ +// +// MPFunction+MPValue.h +// MathPad +// +// Created by Kim Wittenburg on 09.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import +#import "MPValueGroup.h" +#import "MPFunction+MPToken.h" + +@interface MPFunction (MPValue) + +@end diff --git a/MathPad/MPFunction+MPValue.m b/MathPad/MPFunction+MPValue.m new file mode 100644 index 0000000..0192bac --- /dev/null +++ b/MathPad/MPFunction+MPValue.m @@ -0,0 +1,28 @@ +// +// MPFunction+MPValue.m +// MathPad +// +// Created by Kim Wittenburg on 09.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import "MPFunction+MPValue.h" + +@implementation MPFunction (MPValue) + +- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream +{ + [tokenStream beginIgnoringWhitespaceTokens]; + if (tokenStream.currentToken.tokenType != MPGenericFunctionToken) { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Expected Function" userInfo:nil]; + } + [tokenStream endIgnoringOrAcceptingWhitespaceTokens]; + return [self init]; +} + +- (NSArray *)expressionElements +{ + return @[self]; +} + +@end diff --git a/MathPad/MPFunction.h b/MathPad/MPFunction.h index def046e..23a0685 100644 --- a/MathPad/MPFunction.h +++ b/MathPad/MPFunction.h @@ -9,7 +9,6 @@ @import Foundation; #import "MPExpressionElement.h" #import "MPParseError.h" -#import "MPTerm.h" #define MPFunctionAccessorImplementation(Accessor, variableName) \ - (void)set##Accessor:(MPExpression *)value \ @@ -50,7 +49,8 @@ #pragma mark Evaluating Functions -- (MPTerm *)parseWithError:(MPParseError *__autoreleasing *)error; // Override +- (BOOL)validate:(MPParseError *__autoreleasing *)error; // Override +- (NSDecimalNumber *)evaluate; // Override #pragma mark Messages - (void)didChangeElementsInRangePath:(MPRangePath *)rangePath diff --git a/MathPad/MPFunctionLayout.m b/MathPad/MPFunctionLayout.m index 1b48727..54a3e67 100644 --- a/MathPad/MPFunctionLayout.m +++ b/MathPad/MPFunctionLayout.m @@ -15,6 +15,8 @@ #import "MPParenthesisFunction.h" #import "MPParenthesisFunctionLayout.h" #import "MPPowerFunctionLayout.h" +#import "MPFractionFunctionLayout.h" +#import "MPFractionFunction.h" #import "NSIndexPath+MPAdditions.h" @@ -31,6 +33,8 @@ return [[MPParenthesisFunctionLayout alloc] initWithFunction:function parent:parent]; } else if (class == [MPPowerFunction class]) { return [[MPPowerFunctionLayout alloc] initWithFunction:function parent:parent]; + } else if (class == [MPFractionFunction class]) { + return [[MPFractionFunctionLayout alloc] initWithFunction:function parent:parent]; } return [[self alloc] initWithFunction:function parent:parent]; @@ -67,6 +71,11 @@ return [self cachableObjectForIndex:actualIndex generator:generator]; } +- (NSUInteger)numberOfChildren +{ + return self.function.numberOfChildren; +} + - (MPLayout *)childLayoutAtIndex:(NSUInteger)index { return [self cachableObjectForIndex:index generator:^id{ diff --git a/MathPad/MPFunctionValue.h b/MathPad/MPFunctionValue.h new file mode 100644 index 0000000..8bd73e7 --- /dev/null +++ b/MathPad/MPFunctionValue.h @@ -0,0 +1,19 @@ +// +// MPFunctionValue.h +// MathPad +// +// Created by Kim Wittenburg on 11.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import +#import "MPValueGroup.h" + +@interface MPFunctionValue : NSObject + +- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream; // May return an MPFunctionValue or a powered function + +@property (nonatomic, copy) NSString *functionName; +@property (nonatomic, strong) MPValueGroup *valueGroup; + +@end \ No newline at end of file diff --git a/MathPad/MPFunctionValue.m b/MathPad/MPFunctionValue.m new file mode 100644 index 0000000..0a628ad --- /dev/null +++ b/MathPad/MPFunctionValue.m @@ -0,0 +1,151 @@ +// +// MPFunctionValue.m +// MathPad +// +// Created by Kim Wittenburg on 11.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import "MPFunctionValue.h" +#import "MPMathRules.h" + +double defaultFunction(double value) {return value;}; + +@implementation MPFunctionValue { + NSDecimalNumber *(^_function)(NSDecimalNumber *); +} + +- (id)init +{ + self = [super init]; + if (self) { + + } + return self; +} + +- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream +{ + switch (tokenStream.currentToken.tokenType) { + case MPSinToken: + [tokenStream currentTokenConsumed]; + self.functionName = @"sin"; + return [self initWithTokenStream:tokenStream + function:&sin + takesArcValue:YES + returnsArcValue:NO]; + + case MPCosToken: + [tokenStream currentTokenConsumed]; + self.functionName = @"cos"; + return [self initWithTokenStream:tokenStream + function:&cos + takesArcValue:YES + returnsArcValue:NO]; + + case MPTanToken: + [tokenStream currentTokenConsumed]; + self.functionName = @"tan"; + return [self initWithTokenStream:tokenStream + function:&tan + takesArcValue:YES + returnsArcValue:NO]; + + case MPASinToken: + [tokenStream currentTokenConsumed]; + self.functionName = @"asin"; + return [self initWithTokenStream:tokenStream + function:&asin + takesArcValue:YES + returnsArcValue:NO]; + + case MPACosToken: + [tokenStream currentTokenConsumed]; + self.functionName = @"acos"; + return [self initWithTokenStream:tokenStream + function:&acos + takesArcValue:YES + returnsArcValue:NO]; + + case MPATanToken: + [tokenStream currentTokenConsumed]; + self.functionName = @"atan"; + return [self initWithTokenStream:tokenStream + function:&atan + takesArcValue:YES + returnsArcValue:NO]; + + default: + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Expected Function" userInfo:nil]; + break; + } + return self; +} + +- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream + function:(double (*)(double))function + takesArcValue:(BOOL)takesArc + returnsArcValue:(BOOL)returnsArc +{ + self = [self init]; + if (self) { + _valueGroup = [[MPValueGroup alloc] initWithTokenStream:tokenStream]; + id __weak weakSelf = self; + _function = ^(NSDecimalNumber *value){ + if (takesArc) { + value = [weakSelf convertToRadiantsIfNecessary:value]; + } + NSDecimalNumber *returnValue = [[NSDecimalNumber alloc] initWithDouble:function(value.doubleValue)]; + if (returnsArc) { + returnValue = [weakSelf convertToDegreesIfNecessary:returnValue]; + } + return returnValue; + }; + } + return self; +} + +- (NSDecimalNumber *)convertToRadiantsIfNecessary:(NSDecimalNumber *)degrees +{ + if ([MPMathRules sharedRules].isUsingDegrees) { + // * M_PI / 180 + return [[degrees decimalNumberByMultiplyingBy:[[NSDecimalNumber alloc] initWithDouble:M_PI]] decimalNumberByDividingBy:[[NSDecimalNumber alloc] initWithInteger:180]]; + } else { + return degrees; + } +} + +- (NSDecimalNumber *)convertToDegreesIfNecessary:(NSDecimalNumber *)radiants +{ + if ([MPMathRules sharedRules].isUsingDegrees) { + // * 180 / M_PI + return [[radiants decimalNumberByMultiplyingBy:[[NSDecimalNumber alloc] initWithInteger:180]] decimalNumberByDividingBy:[[NSDecimalNumber alloc] initWithDouble:M_PI]]; + } else { + return radiants; + } +} + +- (NSRange)range +{ + return NSMakeRange(0, 0); +} + +- (BOOL)validate:(MPParseError *__autoreleasing *)error +{ + return [self.valueGroup validate:error]; +} + +- (NSDecimalNumber *)evaluate +{ + return _function([self.valueGroup evaluate]); +} + +- (NSArray *)expressionElements +{ + NSMutableArray *elements = [[NSMutableArray alloc] init]; + [elements addObject:self.functionName]; + [elements addObjectsFromArray:self.valueGroup.expressionElements]; + return elements; +} + +@end diff --git a/MathPad/MPFunctionsViewController.m b/MathPad/MPFunctionsViewController.m index 14d9b69..bd8da4c 100644 --- a/MathPad/MPFunctionsViewController.m +++ b/MathPad/MPFunctionsViewController.m @@ -13,6 +13,7 @@ #import "MPSumFunction.h" #import "MPParenthesisFunction.h" #import "MPPowerFunction.h" +#import "MPFractionFunction.h" @class MPFunctionTemplateItem; @@ -205,6 +206,7 @@ static void *MPFunctionTemplateViewMouseOverContext = @"MPFunctionTemplateViewMo squareFunction.exponentExpression = [[MPExpression alloc] initWithElement:@"2"]; MPPowerFunction *cubicFunction = [[MPPowerFunction alloc] init]; cubicFunction.exponentExpression = [[MPExpression alloc] initWithElement:@"3"]; + MPFractionFunction *fractionFunction = [[MPFractionFunction alloc] init]; self.functionPrototypes = @[ @{@"function": sumFunction, @"name": NSLocalizedString(@"Sum", @"Sum Function Name")}, @@ -215,7 +217,9 @@ static void *MPFunctionTemplateViewMouseOverContext = @"MPFunctionTemplateViewMo @{@"function": cubicFunction, @"name": NSLocalizedString(@"Cubic", @"Cubic Function Name")}, @{@"function": powerFunction, - @"name": NSLocalizedString(@"Power", @"Power Function Name")} + @"name": NSLocalizedString(@"Power", @"Power Function Name")}, + @{@"function": fractionFunction, + @"name": NSLocalizedString(@"Fraction", @"Fraction Function Name")} ]; [self.collectionView addObserver:self forKeyPath:@"hoverItem" diff --git a/MathPad/MPLayout.h b/MathPad/MPLayout.h index 3f07d49..93c149f 100644 --- a/MathPad/MPLayout.h +++ b/MathPad/MPLayout.h @@ -55,6 +55,8 @@ @end @interface MPLayout (MPSubclassImplement) +- (NSUInteger)numberOfChildren; +- (BOOL)drawsChildrenManually; - (MPLayout *)childLayoutAtIndex:(NSUInteger)index; // To be implemented - (NSRect)generateBounds; // To be implemented - (NSRect)boundingRectForRange:(NSRange)range; // To be implemented, use rangePath instead, this one has wrong origin diff --git a/MathPad/MPLayout.m b/MathPad/MPLayout.m index ecc7746..1f4c174 100644 --- a/MathPad/MPLayout.m +++ b/MathPad/MPLayout.m @@ -174,6 +174,11 @@ return bounds; } +- (BOOL)drawsChildrenManually +{ + return NO; +} + - (void)drawAtPoint:(NSPoint)point { NSAffineTransform *transform = [NSAffineTransform transform]; @@ -181,6 +186,13 @@ yBy:point.y]; [transform concat]; [self draw]; + if (!self.drawsChildrenManually) { + for (NSUInteger index = 0; index < [self numberOfChildren]; index++) { + MPLayout *childLayout = [self childLayoutAtIndex:index]; + NSPoint offset = [self offsetOfChildLayoutAtIndex:index]; + [childLayout drawAtPoint:offset]; + } + } [transform invert]; [transform concat]; } diff --git a/MathPad/MPMathRules.h b/MathPad/MPMathRules.h index 989cf91..d14010e 100644 --- a/MathPad/MPMathRules.h +++ b/MathPad/MPMathRules.h @@ -8,10 +8,8 @@ #import -FOUNDATION_EXPORT NSString *MPMathRulesAllowsImplicitMultiplicationKey; FOUNDATION_EXPORT NSString *MPMathRulesMaximumOperatorChainLengthKey; FOUNDATION_EXPORT NSString *MPMathRulesMaximumOperatorChainLengthInMultiplicationKey; -FOUNDATION_EXPORT NSString *MPMathRulesMaximumOperatorChainLengthInFunctionKey; FOUNDATION_EXPORT NSString *MPMathRulesIsUsingDegreesKey; @interface MPMathRules : NSObject @@ -20,11 +18,9 @@ FOUNDATION_EXPORT NSString *MPMathRulesIsUsingDegreesKey; @property (nonatomic, getter = isUsingUserDefaultValues) BOOL usingUserDefaultValues; -@property (nonatomic) BOOL allowsImplicitMultiplication; // wether 2 3 is equal to 6 or error // Default value uses the key "..." in NSUserDefaults or ... if the key does not exist. @property (nonatomic) NSUInteger maximumOperatorChainLength; // +--++-5 -> Chain length: 6, 2 default (sign of number and operator) (0 is invalid?) @property (nonatomic) NSUInteger maximumOperatorChainLengthInMultiplication; // Default: 1, 0 means actually 0 -@property (nonatomic) NSUInteger maximumOperatorChainLengthInFunction; // For sin, cos, tan. Default: 1 @property (nonatomic) BOOL isUsingDegrees; diff --git a/MathPad/MPMathRules.m b/MathPad/MPMathRules.m index 90936f8..e8654a7 100644 --- a/MathPad/MPMathRules.m +++ b/MathPad/MPMathRules.m @@ -8,10 +8,8 @@ #import "MPMathRules.h" -NSString *MPMathRulesAllowsImplicitMultiplicationKey = @"MPMathRulesAllowsImplicitMultiplicationKey"; NSString *MPMathRulesMaximumOperatorChainLengthKey = @"MPMathRulesMaximumOperatorChainLengthKey"; NSString *MPMathRulesMaximumOperatorChainLengthInMultiplicationKey = @"MPMathRulesMaximumOperatorChainLengthInMultiplicationKey"; -NSString *MPMathRulesMaximumOperatorChainLengthInFunctionKey = @"MPMathRulesMaximumOperatorChainLengthInFunctionKey"; NSString *MPMathRulesIsUsingDegreesKey = @"MPMathRulesIsUsingDegreesKey"; @implementation MPMathRules @@ -35,16 +33,12 @@ static MPMathRules *sharedRules; if (self) { _usingUserDefaultValues = YES; NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; - NSNumber *userDefaultsAllowImplicitMultiplication = [userDefaults objectForKey:MPMathRulesAllowsImplicitMultiplicationKey]; NSNumber *userDefaultsMaximumOperatorChainLength = [userDefaults objectForKey:MPMathRulesMaximumOperatorChainLengthKey]; NSNumber *userDefaultsMaximumOperatorChainLengthInMultiplication = [userDefaults objectForKey:MPMathRulesMaximumOperatorChainLengthInMultiplicationKey]; - NSNumber *userDefaultsMaximumOperatorChainLengthInFunction = [userDefaults objectForKey:MPMathRulesMaximumOperatorChainLengthInFunctionKey]; NSNumber *userDefaultsIsUsingDegrees = [userDefaults objectForKey:MPMathRulesIsUsingDegreesKey]; - _allowsImplicitMultiplication = userDefaultsAllowImplicitMultiplication.boolValue; _maximumOperatorChainLength = userDefaultsMaximumOperatorChainLength != nil ? userDefaultsMaximumOperatorChainLength.unsignedIntegerValue : 2; _maximumOperatorChainLengthInMultiplication = userDefaultsMaximumOperatorChainLengthInMultiplication != nil ? userDefaultsMaximumOperatorChainLengthInMultiplication.unsignedIntegerValue : 1; - _maximumOperatorChainLengthInFunction = userDefaultsMaximumOperatorChainLengthInFunction != nil ? userDefaultsMaximumOperatorChainLengthInFunction.unsignedIntegerValue : 1; _isUsingDegrees = userDefaultsIsUsingDegrees.boolValue; } return self; @@ -54,22 +48,11 @@ static MPMathRules *sharedRules; { _usingUserDefaultValues = usingUserDefaultValues; // Save the current values - self.allowsImplicitMultiplication = self.allowsImplicitMultiplication; self.maximumOperatorChainLength = self.maximumOperatorChainLength; self.maximumOperatorChainLengthInMultiplication = self.maximumOperatorChainLengthInMultiplication; - self.maximumOperatorChainLengthInFunction = self.maximumOperatorChainLengthInFunction; self.isUsingDegrees = self.isUsingDegrees; } -- (void)setAllowsImplicitMultiplication:(BOOL)allowsImplicitMultiplication -{ - _allowsImplicitMultiplication = allowsImplicitMultiplication; - if (self.isUsingUserDefaultValues) { - [[NSUserDefaults standardUserDefaults] setBool:allowsImplicitMultiplication - forKey:MPMathRulesAllowsImplicitMultiplicationKey]; - } -} - - (void)setMaximumOperatorChainLength:(NSUInteger)maximumOperatorChainLength { _maximumOperatorChainLength = maximumOperatorChainLength; @@ -88,15 +71,6 @@ static MPMathRules *sharedRules; } } -- (void)setMaximumOperatorChainLengthInFunction:(NSUInteger)maximumOperatorChainLengthInFunction -{ - _maximumOperatorChainLengthInFunction = maximumOperatorChainLengthInFunction; - if (self.isUsingUserDefaultValues) { - [[NSUserDefaults standardUserDefaults] setObject:@(maximumOperatorChainLengthInFunction) - forKey:MPMathRulesMaximumOperatorChainLengthInFunctionKey]; - } -} - - (void)setIsUsingDegrees:(BOOL)isUsingDegrees { _isUsingDegrees = isUsingDegrees; diff --git a/MathPad/MPNumber.h b/MathPad/MPNumber.h new file mode 100644 index 0000000..d74f910 --- /dev/null +++ b/MathPad/MPNumber.h @@ -0,0 +1,16 @@ +// +// MPNumber.h +// MathPad +// +// Created by Kim Wittenburg on 09.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import +#import "MPValueGroup.h" + +@interface MPNumber : NSObject + +@property (nonatomic, strong) NSDecimalNumber *number; + +@end diff --git a/MathPad/MPNumber.m b/MathPad/MPNumber.m new file mode 100644 index 0000000..ebb0f8c --- /dev/null +++ b/MathPad/MPNumber.m @@ -0,0 +1,61 @@ +// +// MPNumber.m +// MathPad +// +// Created by Kim Wittenburg on 09.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import "MPNumber.h" + +@implementation MPNumber { + NSRange _range; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + } + return self; +} + +- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream +{ + self = [self init]; + if (self) { + [tokenStream beginIgnoringWhitespaceTokens]; + MPToken *token = tokenStream.currentToken; + if (token.tokenType == MPNumberToken) { + self.number = [NSDecimalNumber decimalNumberWithString:token.stringValue locale:[NSLocale currentLocale]]; + _range = token.range; + } else { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Expected Number" userInfo:nil]; + } + [tokenStream currentTokenConsumed]; + [tokenStream endIgnoringOrAcceptingWhitespaceTokens]; + } + return self; +} + +- (NSRange)range +{ + return _range; +} + +- (BOOL)validate:(MPParseError *__autoreleasing *)error +{ + return YES; +} + +- (NSDecimalNumber *)evaluate +{ + return self.number; +} + +- (NSArray *)expressionElements +{ + return @[[self.number descriptionWithLocale:[NSLocale currentLocale]]]; +} + +@end diff --git a/MathPad/MPOperatorChain.h b/MathPad/MPOperatorChain.h new file mode 100644 index 0000000..d21ba16 --- /dev/null +++ b/MathPad/MPOperatorChain.h @@ -0,0 +1,17 @@ +// +// MPOperatorChain.h +// MathPad +// +// Created by Kim Wittenburg on 11.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import +#import "MPExpressionTreeElement.h" + +@interface MPOperatorChain : NSObject + +@property (nonatomic) BOOL negating; +@property (nonatomic) NSUInteger numberOfOperators; + +@end diff --git a/MathPad/MPOperatorChain.m b/MathPad/MPOperatorChain.m new file mode 100644 index 0000000..33c2439 --- /dev/null +++ b/MathPad/MPOperatorChain.m @@ -0,0 +1,75 @@ +// +// MPOperatorChain.m +// MathPad +// +// Created by Kim Wittenburg on 11.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import "MPOperatorChain.h" + +@implementation MPOperatorChain { + NSRange _range; +} + +- (id)init +{ + self = [super init]; + if (self) { + } + return self; +} + +- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream +{ + self = [self init]; + if (self) { + [tokenStream beginIgnoringWhitespaceTokens]; + MPToken *currentToken = tokenStream.currentToken; + if (currentToken.tokenType != MPOperatorListToken) { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Expected Operators" userInfo:nil]; + } + NSString *operatorString = currentToken.stringValue; + _range = currentToken.range; + operatorString = [[operatorString componentsSeparatedByString:@" "] componentsJoinedByString:@""]; + _negating = NO; + _numberOfOperators = operatorString.length; + for (NSUInteger index = 0; index < _numberOfOperators; index++) { + if ([[operatorString substringWithRange:NSMakeRange(index, 1)] isEqualToString:@"-"]) { + _negating = !_negating; + } + } + [tokenStream currentTokenConsumed]; + [tokenStream endIgnoringOrAcceptingWhitespaceTokens]; + } + return self; +} + +- (NSRange)range +{ + return _range; +} + +- (BOOL)validate:(MPParseError *__autoreleasing *)error +{ + return YES; +} + +- (NSDecimalNumber *)evaluate +{ + return self.negating ? [[NSDecimalNumber alloc] initWithInteger:-1] : [NSDecimalNumber one]; +} + +- (NSArray *)expressionElements +{ + NSMutableString *text = [[NSMutableString alloc] init]; + for (NSUInteger index = 1; index < self.numberOfOperators; index++) { + [text appendString:@"+"]; + } + if (self.negating) { + [text appendString:@"-"]; + } + return @[text.copy]; +} + +@end diff --git a/MathPad/MPParenthesisFunction.m b/MathPad/MPParenthesisFunction.m index 6093e7b..b08383b 100644 --- a/MathPad/MPParenthesisFunction.m +++ b/MathPad/MPParenthesisFunction.m @@ -8,7 +8,6 @@ // #import "MPParenthesisFunction.h" -#import "MPExpressionEvaluator.h" @implementation MPParenthesisFunction @@ -19,11 +18,14 @@ MPFunctionAccessorImplementation(Expression, _expression) return @[@"expression"]; } -- (MPTerm *)parseWithError:(MPParseError *__autoreleasing *)error +- (BOOL)validate:(MPParseError *__autoreleasing *)error { - MPExpressionEvaluator *evaluator = [[MPExpressionEvaluator alloc] initWithExpression:self.expression]; - return [evaluator parseExpectingVariable:NO - error:error]; + return [[self.expression parse] validate:error]; +} + +- (NSDecimalNumber *)evaluate +{ + return [[self.expression parse] evaluate]; } - (NSString *)description diff --git a/MathPad/MPParenthesisFunctionLayout.m b/MathPad/MPParenthesisFunctionLayout.m index 7894a31..7373550 100644 --- a/MathPad/MPParenthesisFunctionLayout.m +++ b/MathPad/MPParenthesisFunctionLayout.m @@ -154,6 +154,12 @@ return bounds; } +#warning Let the children be drawn automatically +- (BOOL)drawsChildrenManually +{ + return YES; +} + - (void)draw { NSBezierPath *openingParens = self.transformedOpeningParens; diff --git a/MathPad/MPPowerFunction.h b/MathPad/MPPowerFunction.h index cfb8131..0ae7c2f 100644 --- a/MathPad/MPPowerFunction.h +++ b/MathPad/MPPowerFunction.h @@ -7,10 +7,10 @@ // #import +#import "MPSuffixFunction.h" -@interface MPPowerFunction : MPFunction +@interface MPPowerFunction : MPSuffixFunction -@property (nonatomic, strong) MPTerm *baseTerm; @property (nonatomic, strong) MPExpression *exponentExpression; @end diff --git a/MathPad/MPPowerFunction.m b/MathPad/MPPowerFunction.m index 55807d7..1b261ee 100644 --- a/MathPad/MPPowerFunction.m +++ b/MathPad/MPPowerFunction.m @@ -7,7 +7,7 @@ // #import "MPPowerFunction.h" -#import "MPExpressionEvaluator.h" +#import "MPExpression.h" @implementation MPPowerFunction @@ -18,19 +18,24 @@ MPFunctionAccessorImplementation(ExponentExpression, _exponentExpression) return @[@"exponentExpression"]; } -- (MPTerm *)parseWithError:(MPParseError *__autoreleasing *)error +- (BOOL)validate:(MPParseError *__autoreleasing *)error { - MPExpressionEvaluator *exponent = [[MPExpressionEvaluator alloc] initWithExpression:self.exponentExpression]; - MPTerm *exponentTerm = [exponent parseExpectingVariable:NO - error:error]; - if (exponentTerm == nil) { - return nil; + if (!self.baseValue) { + if (error) { + *error = MPParseError(NSMakeRange([self.parent convertIndex:[self.parent indexOfElement:self] + fromReferenceFrame:MPElementReferenceFrame + toReferenceFrame:MPSymbolReferenceFrame], 0), @"No Base for Power"); + } + return NO; } - - return [[MPTerm alloc] initWithBlock:^NSDecimalNumber *{ - double power = pow([self.baseTerm evaluate].doubleValue, [exponentTerm evaluate].doubleValue); - return [[NSDecimalNumber alloc] initWithDouble:power]; - }]; + return [self.baseValue validate:error] && [[self.exponentExpression parse] validate:error]; +} + +- (NSDecimalNumber *)evaluate +{ + NSDecimalNumber *base = [self.baseValue evaluate]; + NSDecimalNumber *exponent = [[self.exponentExpression parse] evaluate]; + return [[NSDecimalNumber alloc] initWithDouble:pow(base.doubleValue, exponent.doubleValue)]; } - (NSString *)description diff --git a/MathPad/MPPowerFunctionLayout.m b/MathPad/MPPowerFunctionLayout.m index b89b5ae..506f3cd 100644 --- a/MathPad/MPPowerFunctionLayout.m +++ b/MathPad/MPPowerFunctionLayout.m @@ -7,9 +7,7 @@ // #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))) +#import "MPExpressionLayout.h" #define kPowerFunctionExponentXOffset 1 #define kPowerFunctionTrailingOffset 2 @@ -36,7 +34,7 @@ - (NSPoint)offsetOfChildLayoutAtIndex:(NSUInteger)index { - CGFloat y = self.baseBounds.size.height / 2; + CGFloat y = (self.baseBounds.size.height + self.baseBounds.origin.y) / 2; return NSMakePoint(kPowerFunctionExponentXOffset, y); } @@ -60,9 +58,6 @@ } - (void)draw -{ - MPLayout *exponentLayout = [self childLayoutAtIndex:0]; - [exponentLayout drawAtPoint:[self offsetOfChildLayoutAtIndex:0]]; -} +{} @end diff --git a/MathPad/MPProduct.h b/MathPad/MPProduct.h new file mode 100644 index 0000000..f6aa72c --- /dev/null +++ b/MathPad/MPProduct.h @@ -0,0 +1,23 @@ +// +// MPProduct.h +// MathPad +// +// Created by Kim Wittenburg on 09.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import +#import "MPExpressionTreeElement.h" +#import "MPFactor.h" + +@interface MPProduct : NSObject + +- (NSArray *)factors; +- (void)appendFactor:(MPFactor *)factor; +- (void)insertFactor:(MPFactor *)factor + atIndex:(NSUInteger)index; +- (void)removeFactor:(MPFactor *)factor; +- (void)removeFactorAtIndex:(NSUInteger)index; +- (MPFactor *)factorAtIndex:(NSUInteger)index; + +@end diff --git a/MathPad/MPProduct.m b/MathPad/MPProduct.m new file mode 100644 index 0000000..c54cb49 --- /dev/null +++ b/MathPad/MPProduct.m @@ -0,0 +1,125 @@ +// +// MPProduct.m +// MathPad +// +// Created by Kim Wittenburg on 09.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import "MPProduct.h" +#import "MPValueGroup.h" + +@implementation MPProduct { + NSMutableArray *_factors; + NSRange _range; +} + +- (id)init +{ + self = [super init]; + if (self) { + _factors = [[NSMutableArray alloc] init]; + } + return self; +} + +- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream +{ + self = [self init]; + if (self) { + [tokenStream beginIgnoringWhitespaceTokens]; + + MPTokenStreamRecordCurrentLocation(tokenStream); + while (tokenStream.currentToken.tokenType != MPOperatorListToken && tokenStream.currentToken.tokenType != MPEOFToken) { + [_factors addObject:[[MPFactor alloc] initWithTokenStream:tokenStream]]; + } + _range = MPTokenStreamRecordedRange(tokenStream); + [tokenStream endIgnoringOrAcceptingWhitespaceTokens]; + } + return self; +} + +- (NSRange)range +{ + return _range; +} + +- (BOOL)validate:(MPParseError *__autoreleasing *)error +{ + MPFactor *factor = _factors[0]; + if (factor.hasMultiplicationSymbol) { + if (error) { + *error = MPParseError(factor.multiplicationSymbolRange, @"Unexpected Symbol."); + } + } + if (![factor.value validate:error]) { + return NO; + } + for (NSUInteger index = 1; index < _factors.count; index++) { + factor = _factors[index]; + if (![factor validate:error]) { + return NO; + } + } + return YES; +} + +- (NSDecimalNumber *)evaluate +{ + NSDecimalNumber *value = [NSDecimalNumber one]; + for (MPFactor *factor in _factors) { + NSDecimalNumber *currentValue = [factor evaluate]; + if ([currentValue isEqualToNumber:[NSDecimalNumber notANumber]]) { + value = currentValue; + break; + } + value = [value decimalNumberByMultiplyingBy:currentValue]; + if ([value compare:@(0)] == NSOrderedSame) { + break; + } + } + return value; +} + +- (NSArray *)expressionElements +{ + NSMutableArray *elements = [[NSMutableArray alloc] init]; + for (MPFactor *factor in _factors) { + [elements addObjectsFromArray:factor.expressionElements]; + } + return elements; +} + +- (NSArray *)factors +{ + return _factors; +} + +- (void)appendFactor:(MPFactor *)factor +{ + [_factors addObject:factor]; +} + +- (void)insertFactor:(MPFactor *)factor + atIndex:(NSUInteger)index +{ + [_factors insertObject:factor + atIndex:index]; +} + +- (void)removeFactor:(MPFactor *)factor +{ + [_factors removeObject:factor]; +} + +- (void)removeFactorAtIndex:(NSUInteger)index +{ + [_factors removeObjectAtIndex:index]; +} + +- (MPFactor *)factorAtIndex:(NSUInteger)index +{ + return [_factors objectAtIndex:index]; +} + +@end diff --git a/MathPad/MPSuffixFunction.h b/MathPad/MPSuffixFunction.h new file mode 100644 index 0000000..58de168 --- /dev/null +++ b/MathPad/MPSuffixFunction.h @@ -0,0 +1,15 @@ +// +// MPSuffixFunction.h +// MathPad +// +// Created by Kim Wittenburg on 11.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import + +@interface MPSuffixFunction : MPFunction + +@property (nonatomic, strong) id baseValue; + +@end diff --git a/MathPad/MPSuffixFunction.m b/MathPad/MPSuffixFunction.m new file mode 100644 index 0000000..993998b --- /dev/null +++ b/MathPad/MPSuffixFunction.m @@ -0,0 +1,13 @@ +// +// MPSuffixFunction.m +// MathPad +// +// Created by Kim Wittenburg on 11.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import "MPSuffixFunction.h" + +@implementation MPSuffixFunction + +@end diff --git a/MathPad/MPSumFunction.m b/MathPad/MPSumFunction.m index 3698e87..97e3227 100644 --- a/MathPad/MPSumFunction.m +++ b/MathPad/MPSumFunction.m @@ -9,12 +9,8 @@ #import "MPSumFunction.h" #import "MPExpression.h" -#import "MPExpressionEvaluator.h" #import "MPEvaluationContext.h" -// TODO: Use this macro elsewhere -#define ReturnIfNil(test) if(test == nil) return nil - @implementation MPSumFunction MPFunctionAccessorImplementation(StartExpression, _startExpression) @@ -27,42 +23,47 @@ MPFunctionAccessorImplementation(SumExpression, _sumExpression) } #pragma mark Evaluating Functions -- (MPTerm *)parseWithError:(MPParseError *__autoreleasing *)error -{ - MPExpressionEvaluator *startEvaluator = [[MPExpressionEvaluator alloc] initWithExpression:self.startExpression]; - MPTerm *start = [startEvaluator parseExpectingVariable:YES - error:error]; - ReturnIfNil(start); - MPExpressionEvaluator *targetEvaluator = [[MPExpressionEvaluator alloc] initWithExpression:self.targetExpression]; - MPTerm *target = [targetEvaluator parseExpectingVariable:NO - error:error]; - ReturnIfNil(target); +- (BOOL)validate:(MPParseError *__autoreleasing *)error +{ + MPExpressionTree *startTree = [self.startExpression parse]; + if (![startTree validateExpectingVariableDefinition:YES error:error]) { + return NO; + } - NSString *variable = startEvaluator.definedVariable.copy; + MPExpressionTree *targetTree = [self.targetExpression parse]; + if (![targetTree validate:error]) { + return NO; + } [[MPEvaluationContext sharedContext] push]; - [[MPEvaluationContext sharedContext] defineVariable:variable withValue:[NSNull null]]; - MPExpressionEvaluator *sumEvaluator = [[MPExpressionEvaluator alloc] initWithExpression:self.sumExpression]; - MPTerm *sum = [sumEvaluator parseExpectingVariable:NO - error:error]; - ReturnIfNil(sum); + [[MPEvaluationContext sharedContext] defineVariable:startTree.definedVariable withValue:[NSDecimalNumber notANumber]]; + MPExpressionTree *sumTree = [self.sumExpression parse]; + if (![sumTree validate:error]) { + return NO; + } [[MPEvaluationContext sharedContext] pop]; + return YES; +} + +- (NSDecimalNumber *)evaluate +{ + MPExpressionTree *startTree = [self.startExpression parse]; + NSDecimalNumber *start = [startTree evaluate]; + NSDecimalNumber *target = [[self.targetExpression parse] evaluate]; + MPExpressionTree *sumTree = [self.sumExpression parse]; - return [[MPTerm alloc] initWithBlock:^NSDecimalNumber *{ - [[MPEvaluationContext sharedContext] push]; - NSDecimalNumber *value = [NSDecimalNumber zero]; - NSDecimalNumber *startValue = [start evaluate]; - NSDecimalNumber *targetValue = [target evaluate]; - for (NSDecimalNumber *iterator = startValue; - [iterator compare:targetValue] <= 0 ; - iterator = [iterator decimalNumberByAdding:[[NSDecimalNumber alloc] initWithInteger:1]]) { - [[MPEvaluationContext sharedContext] defineVariable:variable withValue:iterator]; - value = [value decimalNumberByAdding:[sum evaluate]]; - } - [[MPEvaluationContext sharedContext] pop]; - return value; - }]; + NSDecimalNumber *value = [NSDecimalNumber zero]; + + [[MPEvaluationContext sharedContext] push]; + for (NSDecimalNumber *current = start; + [current compare:target] <= 0; + current = [current decimalNumberByAdding:[[NSDecimalNumber alloc] initWithInteger:1]]) { + [[MPEvaluationContext sharedContext] defineVariable:startTree.definedVariable withValue:current]; + value = [value decimalNumberByAdding:[sumTree evaluate]]; + } + [[MPEvaluationContext sharedContext] pop]; + return value; } #pragma mark Working With Functions diff --git a/MathPad/MPSumFunctionLayout.m b/MathPad/MPSumFunctionLayout.m index 3d6e40e..31ed1dc 100644 --- a/MathPad/MPSumFunctionLayout.m +++ b/MathPad/MPSumFunctionLayout.m @@ -154,20 +154,7 @@ NSRect localLineBounds = [self localLineBounds]; CGContextSetTextPosition(context, localLineBounds.origin.x, 0); CTLineDraw(line, context); - - // Draw the start function - MPLayout *startExpressionLayout = [self childLayoutAtIndex:0]; - NSPoint startExpressionLocation = [self offsetOfChildLayoutAtIndex:0]; - [startExpressionLayout drawAtPoint:startExpressionLocation]; - - // Draw the target function - MPLayout *targetExpressionLayout = [self childLayoutAtIndex:1]; - [targetExpressionLayout drawAtPoint:[self offsetOfChildLayoutAtIndex:1]]; - - // Draw the sum function - MPLayout *sumExpressionLayout = [self childLayoutAtIndex:2]; - [sumExpressionLayout drawAtPoint:[self offsetOfChildLayoutAtIndex:2]]; - + CFRelease(line); } diff --git a/MathPad/MPSummand.h b/MathPad/MPSummand.h new file mode 100644 index 0000000..bd913f0 --- /dev/null +++ b/MathPad/MPSummand.h @@ -0,0 +1,19 @@ +// +// MPSummand.h +// MathPad +// +// Created by Kim Wittenburg on 09.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import +#import "MPExpressionTreeElement.h" +#import "MPProduct.h" +#import "MPOperatorChain.h" + +@interface MPSummand : NSObject + +@property (nonatomic, strong) MPOperatorChain *operatorChain; +@property (nonatomic, strong) MPProduct *product; + +@end diff --git a/MathPad/MPSummand.m b/MathPad/MPSummand.m new file mode 100644 index 0000000..5ccaf8e --- /dev/null +++ b/MathPad/MPSummand.m @@ -0,0 +1,71 @@ +// +// MPSummand.m +// MathPad +// +// Created by Kim Wittenburg on 09.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import "MPSummand.h" +#import "MPMathRules.h" + +@implementation MPSummand + +- (id)init +{ + self = [super init]; + if (self) { + } + return self; +} + +- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream +{ + self = [self init]; + if (self) { + [tokenStream beginIgnoringWhitespaceTokens]; + if (tokenStream.currentToken.tokenType == MPOperatorListToken) { + _operatorChain = [[MPOperatorChain alloc] initWithTokenStream:tokenStream]; + } + _product = [[MPProduct alloc] initWithTokenStream:tokenStream]; + [tokenStream endIgnoringOrAcceptingWhitespaceTokens]; + } + return self; +} + +- (NSRange)range +{ + return NSUnionRange(self.operatorChain.range, self.product.range); +} + +- (BOOL)validate:(MPParseError *__autoreleasing *)error +{ + if (self.operatorChain.numberOfOperators > [MPMathRules sharedRules].maximumOperatorChainLength) { + if (error) { + *error = MPParseError(self.operatorChain.range, @"Too many operators."); + } + return NO; + } + return [self.product validate:error]; +} + +- (NSDecimalNumber *)evaluate +{ + NSDecimalNumber *value = [self.product evaluate]; + if ([value isNotEqualTo:[NSDecimalNumber notANumber]] && self.operatorChain) { + value = [value decimalNumberByMultiplyingBy:[self.operatorChain evaluate]]; + } + return value; +} + +-(NSArray *)expressionElements +{ + NSMutableArray *elements = [[NSMutableArray alloc] init]; + if (self.operatorChain) { + [elements addObjectsFromArray:self.operatorChain.expressionElements]; + } + [elements addObjectsFromArray:self.product.expressionElements]; + return @[self.operatorChain.expressionElements,]; +} + +@end diff --git a/MathPad/MPTerm.h b/MathPad/MPTerm.h deleted file mode 100644 index eb5f16c..0000000 --- a/MathPad/MPTerm.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// MPTerm.h -// MathPad -// -// Created by Kim Wittenburg on 27.09.14. -// Copyright (c) 2014 Kim Wittenburg. All rights reserved. -// - -@import Foundation; - -@interface MPTerm : NSObject - -- (instancetype)initWithBlock:(NSDecimalNumber *(^)())block; // Designated Initializer - -- (instancetype)initWithNumber:(NSDecimalNumber *)number; -- (instancetype)initWithSummands:(NSArray *)summands; // array of MPTerms -- (instancetype)initWithFactors:(NSArray *)factors; // array of MPTerms -- (instancetype)initWithVariable:(NSString *)variable; - -- (instancetype)initWithFactorialOfTerm:(MPTerm *)term; - -- (instancetype)initWithSinOfTerm:(MPTerm *)term; -- (instancetype)initWithCosOfTerm:(MPTerm *)term; -- (instancetype)initWithTanOfTerm:(MPTerm *)term; - -- (instancetype)initWithInverseSinOfTerm:(MPTerm *)term; -- (instancetype)initWithInverseCosOfTerm:(MPTerm *)term; -- (instancetype)initWithInverseTanOfTerm:(MPTerm *)term; - -- (NSDecimalNumber *)evaluate; - -@end diff --git a/MathPad/MPTerm.m b/MathPad/MPTerm.m deleted file mode 100644 index 8b1d46b..0000000 --- a/MathPad/MPTerm.m +++ /dev/null @@ -1,149 +0,0 @@ -// -// MPTerm.m -// MathPad -// -// Created by Kim Wittenburg on 27.09.14. -// Copyright (c) 2014 Kim Wittenburg. All rights reserved. -// - -#import "MPTerm.h" -#import "MPEvaluationContext.h" -#import "MPMathRules.h" - -@interface MPTerm () - -@property (nonatomic, copy) NSDecimalNumber *(^block)(); - -@end - -@implementation MPTerm - -- (instancetype)initWithBlock:(NSDecimalNumber *(^)())block -{ - self = [super init]; - if (self) { - self.block = block; - } - return self; -} - -- (instancetype)initWithNumber:(NSDecimalNumber *)number -{ - return [self initWithBlock:^NSDecimalNumber *{ - return number; - }]; -} - -- (instancetype)initWithSummands:(NSArray *)summands -{ - return [self initWithBlock:^NSDecimalNumber *{ - NSDecimalNumber *value = [NSDecimalNumber zero]; - for (MPTerm *term in summands) { - value = [value decimalNumberByAdding:[term evaluate]]; - } - return value; - }]; -} - -- (instancetype)initWithFactors:(NSArray *)factors -{ - return [self initWithBlock:^NSDecimalNumber *{ - NSDecimalNumber *value = [NSDecimalNumber one]; - for (MPTerm *term in factors) { - value = [value decimalNumberByMultiplyingBy:[term evaluate]]; - } - return value; - }]; -} - -- (instancetype)initWithVariable:(NSString *)variable -{ - return [self initWithBlock:^NSDecimalNumber *{ - return [[MPEvaluationContext sharedContext] valueForVariable:variable]; - }]; -} - -- (instancetype)initWithFactorialOfTerm:(MPTerm *)term -{ - return [self initWithBlock:^NSDecimalNumber *{ - NSDecimalNumber *termValue = [term evaluate]; - return [[NSDecimalNumber alloc] initWithDouble:tgamma(termValue.doubleValue + 1)]; - }]; -} - -- (instancetype)initWithSinOfTerm:(MPTerm *)term -{ - return [self initWithBlock:^NSDecimalNumber *{ - 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)]; - }]; -} - -- (instancetype)initWithCosOfTerm:(MPTerm *)term -{ - return [self initWithBlock:^NSDecimalNumber *{ - 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)]; - }]; -} - -- (instancetype)initWithTanOfTerm:(MPTerm *)term -{ - return [self initWithBlock:^NSDecimalNumber *{ - 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)]; - }]; -} - -- (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 -{ - @try { - return self.block(); - } - @catch (NSException *exception) { - return [NSDecimalNumber notANumber]; - } -} - -@end diff --git a/MathPad/MPToken.h b/MathPad/MPToken.h index 48ee6a8..58132d8 100644 --- a/MathPad/MPToken.h +++ b/MathPad/MPToken.h @@ -15,6 +15,9 @@ typedef NS_ENUM(NSUInteger, MPTokenType) { MPSinToken, MPCosToken, MPTanToken, + MPASinToken, + MPACosToken, + MPATanToken, MPNumberToken, MPVariableToken, MPFactorialToken, @@ -23,11 +26,8 @@ typedef NS_ENUM(NSUInteger, MPTokenType) { MPWhitespaceToken, MPUnidentifiedToken, - - MPCompoundToken }; - @protocol MPToken - (MPTokenType)tokenType; @@ -41,22 +41,8 @@ typedef NS_ENUM(NSUInteger, MPTokenType) { @interface MPToken : NSObject - (instancetype)initEOFTokenAtLocation:(NSUInteger)eofLocation; -- (instancetype)initWithRange:(NSRange)range - stringValue:(NSString *)input; - (instancetype)initWithTokenType:(MPTokenType)tokenType range:(NSRange)range stringValue:(NSString *)input; @end - -@interface MPToken (MPTokenExtension) -// Methods are only available for respective token type - -- (NSUInteger)numberOfOperators; -- (NSDecimalNumber *)operatorValue; - -- (NSDecimalNumber *)number; - -- (NSString *)variable; - -@end \ No newline at end of file diff --git a/MathPad/MPToken.m b/MathPad/MPToken.m index 4b919e6..8ada595 100644 --- a/MathPad/MPToken.m +++ b/MathPad/MPToken.m @@ -23,23 +23,14 @@ return self; } -- (instancetype)initWithRange:(NSRange)range stringValue:(NSString *)input +- (instancetype)initWithTokenType:(MPTokenType)tokenType + range:(NSRange)range + stringValue:(NSString *)input { self = [super init]; if (self) { _range = range; _stringValue = input.copy; - } - return self; -} - -- (instancetype)initWithTokenType:(MPTokenType)tokenType - range:(NSRange)range - stringValue:(NSString *)input -{ - self = [self initWithRange:range - stringValue:input]; - if (self) { _tokenType = tokenType; } return self; @@ -65,40 +56,4 @@ return self.stringValue; } -@end - - -@implementation MPToken (MPTokenExtension) - -- (NSUInteger)numberOfOperators -{ - NSString *operatorString = [[self.stringValue componentsSeparatedByString:@" "] componentsJoinedByString:@""]; - return operatorString.length; -} - -- (NSDecimalNumber *)operatorValue -{ - NSString *operatorString = [[self.stringValue componentsSeparatedByString:@" "] componentsJoinedByString:@""]; - NSDecimalNumber *value = [NSDecimalNumber one]; - for (NSUInteger charIndex = 0; charIndex < operatorString.length; charIndex++) { - NSString *operator = [operatorString substringWithRange:NSMakeRange(charIndex, 1)]; - if ([operator isEqualToString:@"-"]) { - value = [value decimalNumberByMultiplyingBy:[[NSDecimalNumber alloc] initWithInteger:-1]]; - } - } - return value; -} - -- (NSDecimalNumber *)number -{ - return [NSDecimalNumber decimalNumberWithString:self.stringValue - locale:[NSLocale currentLocale]]; -} - -- (NSString *)variable -{ - return self.stringValue; -} - - -@end +@end \ No newline at end of file diff --git a/MathPad/MPTokenStream.h b/MathPad/MPTokenStream.h index 0dd4768..3a06005 100644 --- a/MathPad/MPTokenStream.h +++ b/MathPad/MPTokenStream.h @@ -14,14 +14,15 @@ - (instancetype)initWithTokens:(NSArray *)tokens; @property (nonatomic, copy) NSArray *tokens; -@property (nonatomic, getter=isIgnoringWhitespaceTokens) BOOL ignoringWhitespaceTokens; // Default: YES -@property (nonatomic) NSUInteger currentTokenIndex; +- (void)beginIgnoringWhitespaceTokens; +- (void)beginAcceptingWhitespaceTokens; +- (void)endIgnoringOrAcceptingWhitespaceTokens; -- (void)reset; - (BOOL)hasMoreTokens; -- (MPToken *)nextToken; -- (MPToken *)nextTokenOfType:(MPTokenType)type; +- (MPToken *)currentToken; +- (MPToken *)peekNextToken; +- (void)currentTokenConsumed; @end diff --git a/MathPad/MPTokenStream.m b/MathPad/MPTokenStream.m index c258629..cc22572 100644 --- a/MathPad/MPTokenStream.m +++ b/MathPad/MPTokenStream.m @@ -9,7 +9,12 @@ #import "MPTokenStream.h" @implementation MPTokenStream { + NSUInteger _currentTokenIndex; NSUInteger eofLocation; + + BOOL *whitespaceIgnores; + NSUInteger maxWhitespaceIgnores; + NSUInteger currentWhitespaceState; } - (instancetype)initWithTokens:(NSArray *)tokens @@ -17,65 +22,96 @@ self = [super init]; if (self) { self.tokens = tokens; - self.ignoringWhitespaceTokens = YES; + whitespaceIgnores = malloc(10 * sizeof(BOOL)); + maxWhitespaceIgnores = 10; + currentWhitespaceState = 0; + [self beginAcceptingWhitespaceTokens]; + _currentTokenIndex = 0; if (tokens.count > 0) { eofLocation = NSMaxRange([tokens.lastObject range]); + } else { + eofLocation = 0; } - [self reset]; } return self; } +- (void)beginAcceptingWhitespaceTokens +{ + [self pushWhitespaceState:YES]; +} + +- (void)beginIgnoringWhitespaceTokens +{ + [self pushWhitespaceState:NO]; +} + +- (void)pushWhitespaceState:(BOOL)state +{ + currentWhitespaceState++; + if (currentWhitespaceState >= maxWhitespaceIgnores) { + maxWhitespaceIgnores += 10; + BOOL *reallocatedWhitespaceIgnores = realloc(whitespaceIgnores, maxWhitespaceIgnores); + if (reallocatedWhitespaceIgnores) { + whitespaceIgnores = reallocatedWhitespaceIgnores; + } else { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Realloc Failed" userInfo:nil]; + } + } + whitespaceIgnores[currentWhitespaceState] = state; +} + +- (void)endIgnoringOrAcceptingWhitespaceTokens +{ + currentWhitespaceState--; +} + - (void)skipWhitespaces { - if (!self.isIgnoringWhitespaceTokens) { + if (whitespaceIgnores[currentWhitespaceState]) { return; } - while (self.currentTokenIndex < self.tokens.count) { - MPToken *token = self.tokens[self.currentTokenIndex]; + while (_currentTokenIndex < self.tokens.count) { + MPToken *token = self.tokens[_currentTokenIndex]; if (token.tokenType != MPWhitespaceToken) { return; } - ++self.currentTokenIndex; + [self currentTokenConsumed]; } } -- (void)reset -{ - self.currentTokenIndex = 0; - [self skipWhitespaces]; -} - - (BOOL)hasMoreTokens { [self skipWhitespaces]; - return self.currentTokenIndex < self.tokens.count; + return _currentTokenIndex < self.tokens.count; } -- (MPToken *)nextToken +- (MPToken *)currentToken { [self skipWhitespaces]; - if (self.currentTokenIndex >= self.tokens.count) { + if (_currentTokenIndex >= _tokens.count) { return [[MPToken alloc] initEOFTokenAtLocation:eofLocation]; - } else { - MPToken *token = self.tokens[self.currentTokenIndex++]; - return token; } + return _tokens[_currentTokenIndex]; } -- (MPToken *)nextTokenOfType:(MPTokenType)type +- (MPToken *)peekNextToken { - [self skipWhitespaces]; - if (self.currentTokenIndex >= self.tokens.count) { - return nil; - } else { - MPToken *token = self.tokens[self.currentTokenIndex]; - if (token.tokenType != type) { - return nil; - } - ++self.currentTokenIndex; - return token; - } + NSUInteger currentTokenIndex = _currentTokenIndex; + [self currentTokenConsumed]; // Pretend the current token has been consumed + MPToken *token = [self currentToken]; + _currentTokenIndex = currentTokenIndex; // Undo the lookahead + return token; +} + +- (void)currentTokenConsumed +{ + ++_currentTokenIndex; +} + +- (void)dealloc +{ + free(whitespaceIgnores); } @end diff --git a/MathPad/MPUnexpectedSymbolValue.h b/MathPad/MPUnexpectedSymbolValue.h new file mode 100644 index 0000000..a714eef --- /dev/null +++ b/MathPad/MPUnexpectedSymbolValue.h @@ -0,0 +1,16 @@ +// +// MPUnidentifiedSymbolValue.h +// MathPad +// +// Created by Kim Wittenburg on 11.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import +#import "MPValueGroup.h" + +@interface MPUnexpectedSymbolValue : NSObject + +@property (nonatomic, copy) NSString *symbol; + +@end diff --git a/MathPad/MPUnexpectedSymbolValue.m b/MathPad/MPUnexpectedSymbolValue.m new file mode 100644 index 0000000..562cd46 --- /dev/null +++ b/MathPad/MPUnexpectedSymbolValue.m @@ -0,0 +1,59 @@ +// +// MPUnidentifiedSymbolValue.m +// MathPad +// +// Created by Kim Wittenburg on 11.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import "MPUnexpectedSymbolValue.h" + +@implementation MPUnexpectedSymbolValue { + NSRange _range; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + } + return self; +} + +- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream +{ + self = [self init]; + if (self) { + [tokenStream beginIgnoringWhitespaceTokens]; + _range = tokenStream.currentToken.range; + _symbol = tokenStream.currentToken.stringValue; + [tokenStream currentTokenConsumed]; + [tokenStream endIgnoringOrAcceptingWhitespaceTokens]; + } + return self; +} + +- (NSRange)range +{ + return _range; +} + +- (BOOL)validate:(MPParseError *__autoreleasing *)error +{ + if (error) { + *error = MPParseError(self.range, @"Unknown Symbol."); + } + return NO; +} + +- (NSDecimalNumber *)evaluate +{ + return [NSDecimalNumber notANumber]; +} + +- (NSArray *)expressionElements +{ + return @[self.symbol]; +} + +@end diff --git a/MathPad/MPValueGroup.h b/MathPad/MPValueGroup.h new file mode 100644 index 0000000..e77b4f6 --- /dev/null +++ b/MathPad/MPValueGroup.h @@ -0,0 +1,34 @@ +// +// MPValueGroup.h +// MathPad +// +// Created by Kim Wittenburg on 09.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import +#import "MPExpressionTreeElement.h" + +@protocol MPValue + +@end + + +@interface MPValueGroup : NSObject + +@property (nonatomic, strong) NSArray *values; +- (void)appendValue:(id)value; +- (void)insertValue:(id)value + atIndex:(NSUInteger)index; +- (void)removeValue:(id)value; +- (void)removeValueAtIndex:(NSUInteger)index; +- (id)valueAtIndex:(NSUInteger)index; + +@end + + +@interface MPTokenStream (MPValueSuffixes) + +- (id)consumeSuffixesForValue:(id)value; + +@end \ No newline at end of file diff --git a/MathPad/MPValueGroup.m b/MathPad/MPValueGroup.m new file mode 100644 index 0000000..75ca213 --- /dev/null +++ b/MathPad/MPValueGroup.m @@ -0,0 +1,197 @@ +// +// MPValueGroup.m +// MathPad +// +// Created by Kim Wittenburg on 09.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import "MPValueGroup.h" +#import "MPNumber.h" +#import "MPVariable.h" +#import "MPFunction+MPValue.h" +#import "MPUnexpectedSymbolValue.h" +#import "MPFunctionValue.h" +#import "MPFactorial.h" +#import "MPSuffixFunction.h" + +@implementation MPValueGroup { + NSMutableArray *_values; + NSRange _range; +} + +- (id)init +{ + self = [super init]; + if (self) { + _values = [[NSMutableArray alloc] init]; + } + return self; +} + +- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream +{ + self = [self init]; + if (self) { + [tokenStream beginIgnoringWhitespaceTokens]; + MPTokenStreamRecordCurrentLocation(tokenStream); // This will also skip leading whitespaces + [tokenStream endIgnoringOrAcceptingWhitespaceTokens]; + [tokenStream beginAcceptingWhitespaceTokens]; + + BOOL checkMoreTokens = YES; + while (tokenStream.currentToken.tokenType != MPWhitespaceToken && checkMoreTokens) { + id currentValue; + switch (tokenStream.currentToken.tokenType) { + case MPNumberToken: + { + currentValue = [[MPNumber alloc] initWithTokenStream:tokenStream]; + } + break; + + case MPVariableToken: + { + currentValue = [[MPVariable alloc] initWithTokenStream:tokenStream]; + } + break; + + case MPGenericFunctionToken: + { + currentValue = (id)tokenStream.currentToken; + [tokenStream currentTokenConsumed]; + } + break; + + case MPSinToken: + case MPCosToken: + case MPTanToken: + case MPASinToken: + case MPACosToken: + case MPATanToken: + { + [tokenStream currentTokenConsumed]; + currentValue = [[MPFunctionValue alloc] initWithTokenStream:tokenStream]; + } + break; + + case MPUnidentifiedToken: + case MPEqualsToken: + currentValue = [[MPUnexpectedSymbolValue alloc] initWithTokenStream:tokenStream]; + break; + + default: + checkMoreTokens = NO; + break; + } + if (checkMoreTokens) { + [_values addObject:[tokenStream consumeSuffixesForValue:currentValue]]; + } + } + + _range = MPTokenStreamRecordedRange(tokenStream); + + [tokenStream endIgnoringOrAcceptingWhitespaceTokens]; + } + return self; +} + +- (NSRange)range +{ + return _range; +} + +- (BOOL)validate:(MPParseError *__autoreleasing *)error +{ + if (_values.count == 0) { + if (error) { + *error = MPParseError(self.range, @"Expected Value"); + } + return NO; + } + for (id value in _values) { + if (![value validate:error]) { + return NO; + } + } + return YES; +} + +- (NSDecimalNumber *)evaluate +{ + NSDecimalNumber *result = [NSDecimalNumber one]; + for (id value in _values) { + NSDecimalNumber *currentValue = [value evaluate]; + if ([currentValue isEqualToNumber:[NSDecimalNumber notANumber]]) { + result = currentValue; + break; + } + result = [result decimalNumberByMultiplyingBy:currentValue]; + if ([result compare:@(0)] == NSOrderedSame) { + break; + } + } + return result; +} + +- (NSArray *)expressionElements +{ + NSMutableArray *elements = [[NSMutableArray alloc] init]; + for (id value in _values) { + [elements addObjectsFromArray:value.expressionElements]; + } + return elements; +} + +- (void)appendValue:(id)value +{ + [_values addObject:value]; +} + +- (void)insertValue:(id)value + atIndex:(NSUInteger)index +{ + [_values insertObject:value + atIndex:index]; +} + +- (void)removeValue:(id)value +{ + [_values removeObject:value]; +} + +- (void)removeValueAtIndex:(NSUInteger)index +{ + [_values removeObjectAtIndex:index]; +} + +- (id)valueAtIndex:(NSUInteger)index +{ + return [_values objectAtIndex:index]; +} + +@end + + +@implementation MPTokenStream (MPValueSuffixes) + +- (id)consumeSuffixesForValue:(id)value +{ + BOOL repeat = YES; + while (repeat) { + if (self.currentToken.tokenType == MPFactorialToken) { + MPFactorial *factorial = [[MPFactorial alloc] init]; + factorial.value = value; + value = factorial; + [self currentTokenConsumed]; + } else if (self.currentToken.tokenType == MPGenericFunctionToken && [self.currentToken isKindOfClass:[MPSuffixFunction class]]) { + MPSuffixFunction *token = (MPSuffixFunction *)self.currentToken; + token.baseValue = value; + value = token; + [self currentTokenConsumed]; + } else { + repeat = NO; + } + } + return value; +} + +@end \ No newline at end of file diff --git a/MathPad/MPVariable.h b/MathPad/MPVariable.h new file mode 100644 index 0000000..f85e1d2 --- /dev/null +++ b/MathPad/MPVariable.h @@ -0,0 +1,16 @@ +// +// MPVariable.h +// MathPad +// +// Created by Kim Wittenburg on 09.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import +#import "MPValueGroup.h" + +@interface MPVariable : NSObject + +@property (nonatomic, strong) NSString *variableName; + +@end diff --git a/MathPad/MPVariable.m b/MathPad/MPVariable.m new file mode 100644 index 0000000..415597c --- /dev/null +++ b/MathPad/MPVariable.m @@ -0,0 +1,68 @@ +// +// MPVariable.m +// MathPad +// +// Created by Kim Wittenburg on 09.10.14. +// Copyright (c) 2014 Kim Wittenburg. All rights reserved. +// + +#import "MPVariable.h" +#import "MPEvaluationContext.h" + +@implementation MPVariable { + NSRange _range; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + } + return self; +} + +- (instancetype)initWithTokenStream:(MPTokenStream *)tokenStream +{ + self = [self init]; + if (self) { + [tokenStream beginIgnoringWhitespaceTokens]; + MPToken *token = tokenStream.currentToken; + if (token.tokenType == MPVariableToken) { + self.variableName = token.stringValue; + _range = token.range; + } else { + @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Expected Variable" userInfo:nil]; + } + [tokenStream currentTokenConsumed]; + [tokenStream endIgnoringOrAcceptingWhitespaceTokens]; + } + return self; +} + +- (NSRange)range +{ + return _range; +} + +- (BOOL)validate:(MPParseError *__autoreleasing *)error +{ + if (![[MPEvaluationContext sharedContext] isVariableDefined:self.variableName]) { + if (error) { + *error = MPParseError(self.range, @"Undefined Variable"); + } + return NO; + } + return YES; +} + +- (NSDecimalNumber *)evaluate +{ + return [[MPEvaluationContext sharedContext] valueForVariable:self.variableName]; +} + +- (NSArray *)expressionElements +{ + return @[self.variableName]; +} + +@end