initial commit
This commit is contained in:
commit
13b74b03f3
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#### joe made this: http://goel.io/joe
|
||||||
|
#### haskell ####
|
||||||
|
dist
|
||||||
|
dist-*
|
||||||
|
cabal-dev
|
||||||
|
*.o
|
||||||
|
*.hi
|
||||||
|
*.chi
|
||||||
|
*.chs.h
|
||||||
|
*.dyn_o
|
||||||
|
*.dyn_hi
|
||||||
|
.hpc
|
||||||
|
.hsenv
|
||||||
|
.cabal-sandbox/
|
||||||
|
cabal.sandbox.config
|
||||||
|
*.prof
|
||||||
|
*.aux
|
||||||
|
*.hp
|
||||||
|
*.eventlog
|
||||||
|
.stack-work/
|
||||||
|
cabal.project.local
|
||||||
|
.HTF/
|
||||||
|
|
101
LICENSE
Normal file
101
LICENSE
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy,
|
41
README.md
Normal file
41
README.md
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
mathexpr
|
||||||
|
========
|
||||||
|
Ever wanted to take as input a mathematical expression to fill in values and use it? `mathexpr` is exactly what you need.
|
||||||
|
|
||||||
|
_I also wrote this in JavaScript some time ago: [Equation](https://github.com/mdibaiee/Equation)_
|
||||||
|
|
||||||
|
Examples
|
||||||
|
--------
|
||||||
|
Simple evaluation of expressions:
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
import Data.MathExpr
|
||||||
|
|
||||||
|
main = do
|
||||||
|
expr <- getLine -- a mathematical expression, e.g. sin x + y ^ 2
|
||||||
|
-- replace x and y with the specified values and evaluate: sin 2 + 5 ^ 2 = 25.909..
|
||||||
|
print $ evaluate def expr [("x", 2), ("y", 5)]
|
||||||
|
```
|
||||||
|
|
||||||
|
Using custom operators and functions:
|
||||||
|
|
||||||
|
```haskell
|
||||||
|
import Data.MathExpr
|
||||||
|
|
||||||
|
|
||||||
|
-- operators are in the form (character, precedence, function)
|
||||||
|
-- example: ('+', 0, (+)), ('*', 1, (*))
|
||||||
|
-- the function should have the type (Double -> Double -> Double)
|
||||||
|
-- the higher the precedence, the sooner the operator operates
|
||||||
|
|
||||||
|
-- functions are in the form (name, function)
|
||||||
|
-- example: ("ln", log)
|
||||||
|
-- the function should have the type (Double -> Double)
|
||||||
|
|
||||||
|
main =
|
||||||
|
let avg a b = (a + b) / 2
|
||||||
|
let settings = Settings { operators = defaultOperators ++ [('~', 0, avg)]
|
||||||
|
, functions = defaultFunctions ++ [("trunc", fromIntegral . truncate)]
|
||||||
|
evaluate settings "3 ~ 5" [] -- 4
|
||||||
|
evaluate settings "trunc 1.1" [] -- 1
|
||||||
|
```
|
25
mathexpr.cabal
Normal file
25
mathexpr.cabal
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
name: mathexpr
|
||||||
|
version: 0.1.0.0
|
||||||
|
synopsis: Parse and evaluate math expressions with variables and functions
|
||||||
|
description: A simple tool to evaluate math expressions as strings with support for custom functions and operators
|
||||||
|
homepage: https://github.com/mdibaiee/mathexpr
|
||||||
|
license: GPL-3
|
||||||
|
license-file: LICENSE
|
||||||
|
author: Mahdi Dibaiee
|
||||||
|
maintainer: mdibaiee@aol.com
|
||||||
|
copyright: 2016 Mahdi Dibaiee
|
||||||
|
category: Math
|
||||||
|
build-type: Simple
|
||||||
|
-- extra-source-files:
|
||||||
|
cabal-version: >=1.10
|
||||||
|
|
||||||
|
library
|
||||||
|
hs-source-dirs: src
|
||||||
|
exposed-modules: Data.MathExpr
|
||||||
|
build-depends: base >= 4.7 && < 5,
|
||||||
|
data-default-class
|
||||||
|
default-language: Haskell2010
|
||||||
|
|
||||||
|
source-repository head
|
||||||
|
type: git
|
||||||
|
location: https://github.com/mdibaiee/mathexpr
|
108
src/Data/MathExpr.hs
Normal file
108
src/Data/MathExpr.hs
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
module Data.MathExpr
|
||||||
|
( evaluate
|
||||||
|
, Settings (..)
|
||||||
|
, defaultFunctions
|
||||||
|
, defaultOperators
|
||||||
|
) where
|
||||||
|
import Data.Default.Class
|
||||||
|
import Data.Maybe (isJust, fromJust)
|
||||||
|
import Debug.Trace
|
||||||
|
import Data.List (find)
|
||||||
|
|
||||||
|
data Settings = Settings { operators :: [(Char, Int, Double -> Double -> Double)]
|
||||||
|
, functions :: [(String, Double -> Double)]
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultOperators = [
|
||||||
|
('+', 0, (+)), ('-', 0, (-)),
|
||||||
|
('*', 1, (*)), ('/', 1, (/)),
|
||||||
|
('^', 2, (**))
|
||||||
|
]
|
||||||
|
defaultFunctions = [("ln", log), ("sin", sin), ("cos", cos)]
|
||||||
|
|
||||||
|
instance Default Settings where
|
||||||
|
def = Settings { operators = defaultOperators
|
||||||
|
, functions = defaultFunctions
|
||||||
|
}
|
||||||
|
toPostfix :: Settings -> String -> String
|
||||||
|
toPostfix settings s = helper (tokenize s) [] []
|
||||||
|
where
|
||||||
|
ops = operators settings
|
||||||
|
fns = functions settings
|
||||||
|
|
||||||
|
helper :: [String] -> [String] -> String -> String
|
||||||
|
helper [] os out = out ++ concat os
|
||||||
|
helper (c:cs) os out
|
||||||
|
| head c == '(' = helper cs (c:os) out
|
||||||
|
| head c == ')' && head os == "(" = helper cs (tail os) out
|
||||||
|
| head c == ')' = helper (c:cs) (tail os) (out ++ pad (head os))
|
||||||
|
| isOperator c && (null os || precedence c > precedence (head os)) = helper cs (c:os) out
|
||||||
|
| isOperator c = helper (c:cs) (tail os) (out ++ pad (head os))
|
||||||
|
| otherwise = helper cs os (out ++ pad c)
|
||||||
|
|
||||||
|
isOperator cs = isOp cs || isFunction cs
|
||||||
|
isOp cs = isJust $ (head cs) `triLookup` ops
|
||||||
|
isFunction cs = isJust $ cs `lookup` fns
|
||||||
|
precedence cs
|
||||||
|
| isFunction cs = Just 999
|
||||||
|
| otherwise = (head cs) `triLookup` ops
|
||||||
|
|
||||||
|
tokenize :: String -> [String]
|
||||||
|
tokenize str = words $ helper str
|
||||||
|
where
|
||||||
|
helper :: String -> String
|
||||||
|
helper [] = []
|
||||||
|
helper (c:cs)
|
||||||
|
| isAlphanumeric c = c : helper cs
|
||||||
|
| isSymbol c = pad [c] ++ helper cs
|
||||||
|
|
||||||
|
replaceVariables :: String -> [(String, Double)] -> String
|
||||||
|
replaceVariables str [] = str
|
||||||
|
replaceVariables str vars = concatMap replace (tokenize str)
|
||||||
|
where
|
||||||
|
replace c
|
||||||
|
| isVariable c = pad $ show $ fromJust $ c `lookup` vars
|
||||||
|
| otherwise = c
|
||||||
|
|
||||||
|
isVariable c = isJust $ c `lookup` vars
|
||||||
|
|
||||||
|
-- | Evaluate an expression
|
||||||
|
-- Example: `evaluate def "x + y ^ 2" [("x", 1), ("y", 2)]
|
||||||
|
evaluate :: Settings -> String -> [(String, Double)] -> Double
|
||||||
|
evaluate settings expr vars =
|
||||||
|
let postfix = toPostfix settings expr
|
||||||
|
replaced = replaceVariables postfix vars
|
||||||
|
in helper (tokenize replaced) []
|
||||||
|
where
|
||||||
|
ops = operators settings
|
||||||
|
fns = functions settings
|
||||||
|
|
||||||
|
helper :: [String] -> [String] -> Double
|
||||||
|
helper [] [o] = read o
|
||||||
|
helper (c:cs) os
|
||||||
|
| isOperator c =
|
||||||
|
let result = (operatorFunction c) (read . head . tail $ os) (read . head $ os)
|
||||||
|
in helper cs $ (show result) : drop 2 os
|
||||||
|
| isFunction c =
|
||||||
|
let result = (function c) (read . head $ os)
|
||||||
|
in helper cs $ (show result) : tail os
|
||||||
|
| otherwise = helper cs (c:os)
|
||||||
|
|
||||||
|
isOperator cs = isJust $ (head cs) `triLookup` ops
|
||||||
|
isFunction cs = isJust $ cs `lookup` fns
|
||||||
|
|
||||||
|
function cs = fromJust $ cs `lookup` fns
|
||||||
|
operatorFunction cs = case find (\(a, _, _) -> a == head cs) ops of
|
||||||
|
Just (_, _, c) -> c
|
||||||
|
Nothing -> const (const 0)
|
||||||
|
isParen cs = head cs `elem` ['(', ')']
|
||||||
|
|
||||||
|
alphanumeric = '.' : ['a'..'z'] ++ ['0'..'9']
|
||||||
|
isAlphanumeric = (`elem` alphanumeric)
|
||||||
|
isSymbol = not . (`elem` alphanumeric)
|
||||||
|
|
||||||
|
triLookup :: (Eq a) => a -> [(a, b, c)] -> Maybe b
|
||||||
|
triLookup a x = lookup a $ map (\(a, b, _) -> (a, b)) x
|
||||||
|
|
||||||
|
pad :: String -> String
|
||||||
|
pad x = ' ' : x ++ [' ']
|
66
stack.yaml
Normal file
66
stack.yaml
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# This file was automatically generated by 'stack init'
|
||||||
|
#
|
||||||
|
# Some commonly used options have been documented as comments in this file.
|
||||||
|
# For advanced use and comprehensive documentation of the format, please see:
|
||||||
|
# http://docs.haskellstack.org/en/stable/yaml_configuration/
|
||||||
|
|
||||||
|
# Resolver to choose a 'specific' stackage snapshot or a compiler version.
|
||||||
|
# A snapshot resolver dictates the compiler version and the set of packages
|
||||||
|
# to be used for project dependencies. For example:
|
||||||
|
#
|
||||||
|
# resolver: lts-3.5
|
||||||
|
# resolver: nightly-2015-09-21
|
||||||
|
# resolver: ghc-7.10.2
|
||||||
|
# resolver: ghcjs-0.1.0_ghc-7.10.2
|
||||||
|
# resolver:
|
||||||
|
# name: custom-snapshot
|
||||||
|
# location: "./custom-snapshot.yaml"
|
||||||
|
resolver: lts-7.7
|
||||||
|
|
||||||
|
# User packages to be built.
|
||||||
|
# Various formats can be used as shown in the example below.
|
||||||
|
#
|
||||||
|
# packages:
|
||||||
|
# - some-directory
|
||||||
|
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
|
||||||
|
# - location:
|
||||||
|
# git: https://github.com/commercialhaskell/stack.git
|
||||||
|
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
|
||||||
|
# - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a
|
||||||
|
# extra-dep: true
|
||||||
|
# subdirs:
|
||||||
|
# - auto-update
|
||||||
|
# - wai
|
||||||
|
#
|
||||||
|
# A package marked 'extra-dep: true' will only be built if demanded by a
|
||||||
|
# non-dependency (i.e. a user package), and its test suites and benchmarks
|
||||||
|
# will not be run. This is useful for tweaking upstream packages.
|
||||||
|
packages:
|
||||||
|
- '.'
|
||||||
|
# Dependency packages to be pulled from upstream that are not in the resolver
|
||||||
|
# (e.g., acme-missiles-0.3)
|
||||||
|
extra-deps: []
|
||||||
|
|
||||||
|
# Override default flag values for local packages and extra-deps
|
||||||
|
flags: {}
|
||||||
|
|
||||||
|
# Extra package databases containing global packages
|
||||||
|
extra-package-dbs: []
|
||||||
|
|
||||||
|
# Control whether we use the GHC we find on the path
|
||||||
|
# system-ghc: true
|
||||||
|
#
|
||||||
|
# Require a specific version of stack, using version ranges
|
||||||
|
# require-stack-version: -any # Default
|
||||||
|
# require-stack-version: ">=1.1"
|
||||||
|
#
|
||||||
|
# Override the architecture used by stack, especially useful on Windows
|
||||||
|
# arch: i386
|
||||||
|
# arch: x86_64
|
||||||
|
#
|
||||||
|
# Extra directories used by stack for building
|
||||||
|
# extra-include-dirs: [/path/to/dir]
|
||||||
|
# extra-lib-dirs: [/path/to/dir]
|
||||||
|
#
|
||||||
|
# Allow a newer minor version of GHC than the snapshot specifies
|
||||||
|
# compiler-check: newer-minor
|
Loading…
Reference in New Issue
Block a user