synt (empty) → 0.1.0
raw patch · 11 files changed
+307/−0 lines, 11 filesdep +Syntdep +argparserdep +basesetup-changed
Dependencies added: Synt, argparser, base, haskell-src-exts, hpc, hspec, regexpr, split
Files
- LICENSE +13/−0
- Setup.hs +2/−0
- readme.md +50/−0
- src/Main.hs +6/−0
- src/Synt/CLI.hs +73/−0
- src/Synt/Parser.hs +15/−0
- src/Synt/Similar.hs +49/−0
- src/Synt/Similar/Jaccard.hs +12/−0
- src/Synt/Similar/Tanimoto.hs +11/−0
- synt.cabal +66/−0
- test/Spec.hs +10/−0
+ LICENSE view
@@ -0,0 +1,13 @@+Copyright (c) 2014, Brent Lintner <brent.lintner@gmail.com>++Permission to use, copy, modify, and/or distribute this software for any+purpose with or without fee is hereby granted, provided that the above+copyright notice and this permission notice appear in all copies.++THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY+SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER+RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,+NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE+USE OR PERFORMANCE OF THIS SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ readme.md view
@@ -0,0 +1,50 @@+## Synt.hs++[](https://hackage.haskell.org/package/synt)++This is the Haskell implementation of Synt.++### Supported Languages++* Haskel++For more languages, see the top level [Synt](http://github.com/brentlintner/synt) project.++### Installation++ cabal install synt++### Usage++ synt -h++#### Reading In Files++ synt -c Foo.hs -t Bar.hs++#### Comparing Strings++ synt -s -c "x = x ^ 2" -t "x = x * 2"++### Hacking++ cabal sandbox init+ ./bin/configure 1+ ./bin/build 1++### Testing++This is your go to:++ cabal configure --enable-tests+ cabal test++This also runs the tests without compiling, etc:++ ./bin/test++### Using In Code++This is a TODO. :-)++In the meantime, please figure out at your own risk, or use the top level project.
+ src/Main.hs view
@@ -0,0 +1,6 @@+module Main where++import Synt.CLI++main :: IO ()+main = run
+ src/Synt/CLI.hs view
@@ -0,0 +1,73 @@+module Synt.CLI (run) where++import System.Exit+import System.IO+import System.Console.ArgParser+import Synt.Similar++data API = API {+ compareOpt :: String,+ toOpt :: String,+ stringCompareOpt :: Bool,+ algorithmOpt :: String,+ ngramOpt :: String,+ thresholdOpt :: Int+} deriving (Show)++parser :: ParserSpec API+parser = API+ `parsedBy` reqFlag "compare"+ `Descr` "String to compare against."++ `andBy` reqFlag "to"+ `Descr` "String to compare to."++ `andBy` boolFlag "string-compare"+ `Descr` "Consider -c and -t options to be string values, " +++ "instead of file paths to read in."++ `andBy` optFlag "jaccard" "algorithm"+ `Descr` "Similarity algorithm " +++ "[default=jaccard,tanimoto]."++ `andBy` optFlag "1" "ngram"+ `Descr` "Specify what ngrams are generated " +++ "and used for comparing token sequences." +++ " [default=1,2,4..5,10,...,all]"++ `andBy` optFlag 0 "min-threshold"+ `Descr` "Similarity threshold (ex: -m 70). " +++ "Exit with error when similarity % is below value."++pastThreshold :: Int -> Float -> Bool+pastThreshold t d = d < fromIntegral t++exitWithLimit :: Int -> Float -> IO ()+exitWithLimit limit num =+ if pastThreshold limit num+ then do+ putStrLn ("Fail! Threshold of " ++ show limit ++ "% reached.")+ exitFailure+ else do+ putStrLn ("Inputs are " ++ show (round num) ++ "% similar.")+ exitSuccess++interpret :: API -> IO ()+interpret api = do+ let cmp = compareOpt api+ let to = toOpt api+ let algo = algorithmOpt api+ let ngram = ngramOpt api+ let limit = thresholdOpt api+ let readingFromString = stringCompareOpt api+ let compareItems c t = exitWithLimit limit (sim c t algo ngram)++ if readingFromString+ then compareItems cmp to+ else do+ fcmp <- readFile cmp+ fto <- readFile to+ compareItems fcmp fto++run :: IO ()+run = withParseResult parser interpret
+ src/Synt/Parser.hs view
@@ -0,0 +1,15 @@+module Synt.Parser (tokenize) where++import Data.List+import Language.Haskell.Exts.Lexer+import Language.Haskell.Exts.Parser++normalize :: [Loc Token] -> [String]+normalize = map $ showToken . unLoc++tokens :: String -> [Loc Token]+tokens x = fromParseResult $ lexTokenStream x++tokenize :: String -> [String]+tokenize x = normalize $ tokens x+
+ src/Synt/Similar.hs view
@@ -0,0 +1,49 @@+module Synt.Similar (sim, ngramRange, ngram, ngrams) where++import Synt.Parser+import Data.List+import Data.List.Split+import Data.Ix+import Data.Maybe+import Text.RegexPR+import Synt.Similar.Jaccard+import Synt.Similar.Tanimoto++int :: String -> Int+int x = read x :: Int++tupint :: [String] -> (Int, Int)+tupint [x, y] = (int x, int y)++join :: [[String]] -> [String]+join = map (foldr (++) "")++ngramRange :: String -> [a] -> (Int, Int)+ngramRange n l+ | null n || n == "" = (1, 1)+ | n == "all" = (1, length l)+ | isJust (matchRegexPR "\\.\\." n) = tupint $ splitOn ".." n+ | not (null n) = (int n, int n)++ngram :: Int -> [String] -> [[String]]+ngram _ [_] = []+ngram 1 list = map (: []) list+ngram n list = take n list : if length list - 1 >= n+ then ngram n (tail list)+ else []++ngrams :: (Int, Int) -> [String] -> [String]+ngrams r list = concatMap (\x -> join $ ngram x list) (range r)++sim' :: [String] -> [String] -> String -> String -> Float+sim' cmp to algo n = do+ let r = ngramRange n cmp+ let a = ngrams r cmp+ let b = ngrams r to++ if algo == "tanimoto"+ then tanimoto a b+ else jaccard a b++sim :: String -> String -> String -> String -> Float+sim from to = sim' (tokenize from) (tokenize to)
+ src/Synt/Similar/Jaccard.hs view
@@ -0,0 +1,12 @@+module Synt.Similar.Jaccard (jaccard) where++import Data.List++float :: Int -> Float+float = fromIntegral++jaccard :: [String] -> [String] -> Float+jaccard a b = do+ let i = a `intersect` b+ let u = a `union` b+ float (length i) / float (length u) * 100
+ src/Synt/Similar/Tanimoto.hs view
@@ -0,0 +1,11 @@+module Synt.Similar.Tanimoto (tanimoto) where++import Data.List++float :: Int -> Float+float = fromIntegral++tanimoto :: [String] -> [String] -> Float+tanimoto a b = do+ let i = length $ a `intersect` b+ float i / float (length a + length b - i) * 100
+ synt.cabal view
@@ -0,0 +1,66 @@+name: synt+version: 0.1.0+synopsis: Similar code analysis.+description: Calculate percentage of similarity between two pieces of code.+homepage: http://github.com/brentlintner/synt+bug-reports: http://github.com/brentlintner/synt/issues+author: Brent Lintner <brent.lintner@gmail.com>+maintainer: Brent Lintner <brent.lintner@gmail.com>+license: OtherLicense+license-file: LICENSE+stability: experimental+category: Language+build-type: Simple+cabal-version: >=1.10++extra-source-files:+ LICENSE+ readme.md+ synt.cabal++source-repository head+ type: git+ location: https://github.com/hspec/hspec++library+ default-language: Haskell2010+ hs-source-dirs: src+ build-depends:+ base == 4.*,+ haskell-src-exts == 1.16.*,+ split == 0.2.*,+ regexpr == 0.5.*,+ argparser == 0.3.*++ exposed-modules:+ Synt.CLI+ Synt.Parser+ Synt.Similar+ Synt.Similar.Jaccard+ Synt.Similar.Tanimoto++executable synt+ default-language: Haskell2010+ hs-source-dirs: src+ main-is: Main.hs+ build-depends:+ base == 4.*,+ haskell-src-exts == 1.16.*,+ split == 0.2.*,+ regexpr == 0.5.*,+ argparser == 0.3.*++Test-Suite tests+ default-language: Haskell2010+ type: exitcode-stdio-1.0+ hs-source-dirs: test, src+ main-is: Spec.hs+ build-depends:+ Synt,+ base == 4.*,+ haskell-src-exts == 1.16.*,+ split == 0.2.*,+ regexpr == 0.5.*,+ argparser == 0.3.*,+ hspec == 1.*,+ hpc == 0.6.*
+ test/Spec.hs view
@@ -0,0 +1,10 @@+module Main where++import Test.Hspec+import Synt.SimilarSpec++spec :: Spec+spec = similarSpec++main :: IO ()+main = hspec spec