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