From de6f0255af63d203fe87957a3d48ba1a263bb245 Mon Sep 17 00:00:00 2001 From: Franco Bulgarelli Date: Sat, 5 Oct 2019 14:53:00 -0300 Subject: [PATCH 1/7] Improving error reporting internally --- .../Mulang/Analyzer/EdlQueryCompiler.hs | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/Language/Mulang/Analyzer/EdlQueryCompiler.hs b/src/Language/Mulang/Analyzer/EdlQueryCompiler.hs index d7c4aeafe..6f9fda054 100644 --- a/src/Language/Mulang/Analyzer/EdlQueryCompiler.hs +++ b/src/Language/Mulang/Analyzer/EdlQueryCompiler.hs @@ -16,15 +16,16 @@ import Language.Mulang.Analyzer.Synthesizer (decodeUsageInspection, decodeDeclar import qualified Language.Mulang.Edl.Expectation as E -import Data.Maybe (fromMaybe) +import Data.Either (either) import Data.List.Split (splitOn) type Scope = (ContextualizedInspection -> ContextualizedInspection, IdentifierPredicate -> IdentifierPredicate) +type Compilation e = Either String e compileTopQuery :: E.Query -> Inspection -compileTopQuery = fromMaybe (const True) . compileQuery +compileTopQuery = either (const (const True)) id . compileQuery -compileQuery :: E.Query -> Maybe Inspection +compileQuery :: E.Query -> Compilation Inspection compileQuery (E.Decontextualize query) = compileCQuery id query >>= (return . decontextualize) compileQuery (E.Within name query) | (scope, p) <- compileWithin name = fmap (decontextualize.scope) (compileCQuery p query) compileQuery (E.Through name query) | (scope, p) <- compileThrough name = fmap (decontextualize.scope) (compileCQuery p query) @@ -40,7 +41,7 @@ scopeFor :: ([Identifier] -> Inspection -> Inspection) -> Identifier -> Scope scopeFor f name = (contextualized (f names), andAlso (except (last names))) where names = splitOn "." name -compileCQuery :: (IdentifierPredicate -> IdentifierPredicate) -> E.CQuery -> Maybe ContextualizedInspection +compileCQuery :: (IdentifierPredicate -> IdentifierPredicate) -> E.CQuery -> Compilation ContextualizedInspection compileCQuery pm (E.Inspection i p m) = ($ (compilePredicate pm p)) <$> compileInspection (compileVerb i) m compileCQuery pm (E.AtLeast n q) = contextualized (atLeast (encode n)) <$> compileTQuery pm q compileCQuery pm (E.AtMost n q) = contextualized (atMost (encode n)) <$> compileTQuery pm q @@ -49,7 +50,7 @@ compileCQuery pm (E.CNot q) = contextualized never <$> compileCQuery pm (E.CAnd q1 q2) = contextualized2 andAlso <$> compileCQuery pm q1 <*> compileCQuery pm q2 compileCQuery pm (E.COr q1 q2) = contextualized2 orElse <$> compileCQuery pm q1 <*> compileCQuery pm q2 -compileTQuery :: (IdentifierPredicate -> IdentifierPredicate) -> E.TQuery -> Maybe ContextualizedCounter +compileTQuery :: (IdentifierPredicate -> IdentifierPredicate) -> E.TQuery -> Compilation ContextualizedCounter compileTQuery pm (E.Counter i p m) = ($ (compilePredicate pm p)) <$> compileCounter (compileVerb i) m compileTQuery pm (E.Plus q1 q2) = contextualized2 plus <$> (compileTQuery pm q1) <*> (compileTQuery pm q2) @@ -69,7 +70,7 @@ compileVerb :: String -> String compileVerb = concat . map headToUpper . words where headToUpper (x:xs) = toUpper x : xs -compileCounter :: String -> E.Matcher -> Maybe (ContextualizedBoundCounter) +compileCounter :: String -> E.Matcher -> Compilation (ContextualizedBoundCounter) compileCounter = f where f "UsesIf" m = plainMatching countIfs m @@ -86,8 +87,9 @@ compileCounter = f f "DeclaresProcedure" m = boundMatching countProcedures m f "DeclaresVariable" m = boundMatching countVariables m f "Calls" m = boundMatching countCalls m + f other m = Left $ "Can not count over " ++ other ++ " with matcher " ++ show m -compileInspection :: String -> E.Matcher -> Maybe ContextualizedBoundInspection +compileInspection :: String -> E.Matcher -> Compilation ContextualizedBoundInspection compileInspection = f where f "Assigns" m = boundMatching assignsMatching m @@ -165,27 +167,27 @@ compileInspection = f f "UsesYield" m = plainMatching usesYieldMatching m f (primitiveDeclaration -> Just p) E.Unmatching = plain (declaresPrimitive p) f (primitiveUsage -> Just p) E.Unmatching = plain (usesPrimitive p) - f _ _ = Nothing + f other m = Left $ "Unknown inspection " ++ other ++ " with matcher " ++ show m primitiveUsage = decodeUsageInspection primitiveDeclaration = decodeDeclarationInspection -contextual :: ContextualizedConsult a -> Maybe (ContextualizedBoundConsult a) -contextual = Just . contextualizedBind +contextual :: ContextualizedConsult a -> Compilation (ContextualizedBoundConsult a) +contextual = Right . contextualizedBind -contextualizedBound :: ContextualizedBoundConsult a -> Maybe (ContextualizedBoundConsult a) -contextualizedBound = Just +contextualizedBound :: ContextualizedBoundConsult a -> Compilation (ContextualizedBoundConsult a) +contextualizedBound = Right -plain :: Consult a -> Maybe (ContextualizedBoundConsult a) -plain = Just . contextualizedBind . contextualize +plain :: Consult a -> Compilation (ContextualizedBoundConsult a) +plain = Right . contextualizedBind . contextualize -bound :: BoundConsult a -> Maybe (ContextualizedBoundConsult a) -bound = Just . boundContextualize +bound :: BoundConsult a -> Compilation (ContextualizedBoundConsult a) +bound = Right . boundContextualize -boundMatching :: (Matcher -> BoundConsult a) -> E.Matcher -> Maybe (ContextualizedBoundConsult a) +boundMatching :: (Matcher -> BoundConsult a) -> E.Matcher -> Compilation (ContextualizedBoundConsult a) boundMatching f m = bound (f (compileMatcher m)) -plainMatching :: (Matcher -> Consult a) -> E.Matcher -> Maybe (ContextualizedBoundConsult a) +plainMatching :: (Matcher -> Consult a) -> E.Matcher -> Compilation (ContextualizedBoundConsult a) plainMatching f m = plain (f (compileMatcher m)) compileMatcher :: E.Matcher -> Matcher From 55462ac28422830e8e51f843281cd1e8d6b0377f Mon Sep 17 00:00:00 2001 From: Franco Bulgarelli Date: Sat, 5 Oct 2019 15:58:01 -0300 Subject: [PATCH 2/7] Improving errors internally in ExpectionsCompiler --- .../Mulang/Analyzer/EdlQueryCompiler.hs | 13 +++---- .../Mulang/Analyzer/ExpectationsCompiler.hs | 34 ++++++++++++------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/Language/Mulang/Analyzer/EdlQueryCompiler.hs b/src/Language/Mulang/Analyzer/EdlQueryCompiler.hs index 6f9fda054..c350f6cb8 100644 --- a/src/Language/Mulang/Analyzer/EdlQueryCompiler.hs +++ b/src/Language/Mulang/Analyzer/EdlQueryCompiler.hs @@ -17,6 +17,7 @@ import Language.Mulang.Analyzer.Synthesizer (decodeUsageInspection, decodeDeclar import qualified Language.Mulang.Edl.Expectation as E import Data.Either (either) +import Control.Monad.Except import Data.List.Split (splitOn) type Scope = (ContextualizedInspection -> ContextualizedInspection, IdentifierPredicate -> IdentifierPredicate) @@ -87,7 +88,7 @@ compileCounter = f f "DeclaresProcedure" m = boundMatching countProcedures m f "DeclaresVariable" m = boundMatching countVariables m f "Calls" m = boundMatching countCalls m - f other m = Left $ "Can not count over " ++ other ++ " with matcher " ++ show m + f other m = throwError $ "Can not count over " ++ other ++ " with matcher " ++ show m compileInspection :: String -> E.Matcher -> Compilation ContextualizedBoundInspection compileInspection = f @@ -167,22 +168,22 @@ compileInspection = f f "UsesYield" m = plainMatching usesYieldMatching m f (primitiveDeclaration -> Just p) E.Unmatching = plain (declaresPrimitive p) f (primitiveUsage -> Just p) E.Unmatching = plain (usesPrimitive p) - f other m = Left $ "Unknown inspection " ++ other ++ " with matcher " ++ show m + f other m = throwError $ "Unknown inspection " ++ other ++ " with matcher " ++ show m primitiveUsage = decodeUsageInspection primitiveDeclaration = decodeDeclarationInspection contextual :: ContextualizedConsult a -> Compilation (ContextualizedBoundConsult a) -contextual = Right . contextualizedBind +contextual = return . contextualizedBind contextualizedBound :: ContextualizedBoundConsult a -> Compilation (ContextualizedBoundConsult a) -contextualizedBound = Right +contextualizedBound = return plain :: Consult a -> Compilation (ContextualizedBoundConsult a) -plain = Right . contextualizedBind . contextualize +plain = return . contextualizedBind . contextualize bound :: BoundConsult a -> Compilation (ContextualizedBoundConsult a) -bound = Right . boundContextualize +bound = return . boundContextualize boundMatching :: (Matcher -> BoundConsult a) -> E.Matcher -> Compilation (ContextualizedBoundConsult a) boundMatching f m = bound (f (compileMatcher m)) diff --git a/src/Language/Mulang/Analyzer/ExpectationsCompiler.hs b/src/Language/Mulang/Analyzer/ExpectationsCompiler.hs index be83a8788..88a244a52 100644 --- a/src/Language/Mulang/Analyzer/ExpectationsCompiler.hs +++ b/src/Language/Mulang/Analyzer/ExpectationsCompiler.hs @@ -11,9 +11,13 @@ import qualified Language.Mulang.Analyzer.Analysis as A import qualified Data.Map.Strict as Map import Data.List.Split (splitOn) +import Control.Monad.Except +import Data.Either (either) + +type Compilation e = Either String e compileExpectation :: A.Expectation -> Inspection -compileExpectation (A.Expectation s i) = compileTopQuery . negator . scope $ baseQuery +compileExpectation (A.Expectation s i) = either (const (const True)) id (fmap (compileTopQuery . negator . scope) baseQuery) where (inspectionParts, negator) = compileInspectionPartsAndNegator (splitOn ":" i) scope = compileScope (splitOn ":" s) @@ -29,7 +33,7 @@ compileScope ["Intransitive",name] q = Within name q compileScope [name] q = Through name q compileScope _ q = Decontextualize q -compileCQuery :: [String] -> CQuery +compileCQuery :: [String] -> Compilation CQuery compileCQuery [] = compileCQuery ["Parses","*"] compileCQuery [verb] = compileCQuery [verb,"*"] compileCQuery [verb,name] | Map.member name nullaryMatchers = compileCQuery [verb,"*",name] @@ -37,7 +41,7 @@ compileCQuery (verb:"WithChar":args) = compileCQuery (verb:"*":"WithChar" compileCQuery (verb:"WithNumber":args) = compileCQuery (verb:"*":"WithNumber":args) compileCQuery (verb:"WithString":args) = compileCQuery (verb:"*":"WithString":args) compileCQuery (verb:"WithSymbol":args) = compileCQuery (verb:"*":"WithSymbol":args) -compileCQuery (verb:object:args) = Inspection verb (compileBinding object) (compileMatcher args) +compileCQuery (verb:object:args) = fmap (Inspection verb (compileBinding object)) (compileMatcher args) compileBinding :: String -> Predicate compileBinding "*" = Any @@ -46,19 +50,25 @@ compileBinding ('~':name) = Like name compileBinding ('=':name) = Named name compileBinding name = Named name -compileMatcher :: [String] -> Matcher -compileMatcher = matching . f +compileMatcher :: [String] -> Compilation Matcher +compileMatcher = fmap matching . f where matching [] = Unmatching matching xs = Matching xs - f :: [String] -> [Clause] - f (name:args) | Just matcher <- Map.lookup name nullaryMatchers = matcher : f args - f ("WithChar":value:args) = IsChar (read value) : f args - f ("WithNumber":value:args) = IsNumber (read value) : f args - f ("WithString":value:args) = IsString (read value) : f args - f ("WithSymbol":value:args) = IsSymbol (read ("\""++value++"\"")) : f args - f [] = [] + f :: [String] -> Compilation [Clause] + f (name:args) | Just matcher <- Map.lookup name nullaryMatchers = fmap (matcher :) (f args) + f (name:value:args) | Just matcher <- Map.lookup name binaryMatchers = fmap (matcher value :) (f args) + f [] = return [] + f (other:args) = throwError $ "unsupported matcher " ++ other ++ " with args " ++ show args + +binaryMatchers = + Map.fromList [ + ("WithChar", (IsChar . read)), + ("WithNumber", (IsNumber . read)), + ("WithString", (IsString . read)), + ("WithSymbol", (\ v-> IsSymbol (read ("\""++v++"\"")))) + ] nullaryMatchers = Map.fromList [ From 963f4a3652e061aafb65557fe2d08f0829ba5031 Mon Sep 17 00:00:00 2001 From: Franco Bulgarelli Date: Sat, 5 Oct 2019 17:10:47 -0300 Subject: [PATCH 3/7] Generating AnalysisFailed on wrong expections --- mulang.cabal | 5 ++-- spec/ExpectationsCompilerSpec.hs | 2 +- src/Language/Mulang/Analyzer.hs | 14 +++++---- .../Mulang/Analyzer/EdlQueryCompiler.hs | 30 +++++++++---------- .../Mulang/Analyzer/ExpectationsAnalyzer.hs | 11 +++---- .../Mulang/Analyzer/ExpectationsCompiler.hs | 14 ++++----- src/Language/Mulang/Analyzer/Finding.hs | 23 ++++++++++++++ src/Language/Mulang/Inspector/Literal.hs | 4 --- src/Language/Mulang/Inspector/Primitive.hs | 4 +++ 9 files changed, 67 insertions(+), 40 deletions(-) create mode 100644 src/Language/Mulang/Analyzer/Finding.hs diff --git a/mulang.cabal b/mulang.cabal index 9edd344f4..6d188188b 100644 --- a/mulang.cabal +++ b/mulang.cabal @@ -76,11 +76,12 @@ library Language.Mulang.Analyzer.Analysis Language.Mulang.Analyzer.Analysis.Json Language.Mulang.Analyzer.Autocorrector + Language.Mulang.Analyzer.CustomExpectationsAnalyzer Language.Mulang.Analyzer.DomainLanguageCompiler + Language.Mulang.Analyzer.EdlQueryCompiler Language.Mulang.Analyzer.ExpectationsAnalyzer Language.Mulang.Analyzer.ExpectationsCompiler - Language.Mulang.Analyzer.CustomExpectationsAnalyzer - Language.Mulang.Analyzer.EdlQueryCompiler + Language.Mulang.Analyzer.Finding Language.Mulang.Analyzer.FragmentParser Language.Mulang.Analyzer.SignaturesAnalyzer Language.Mulang.Analyzer.SignatureStyleCompiler diff --git a/spec/ExpectationsCompilerSpec.hs b/spec/ExpectationsCompilerSpec.hs index 514be3d5e..857539470 100644 --- a/spec/ExpectationsCompilerSpec.hs +++ b/spec/ExpectationsCompilerSpec.hs @@ -10,7 +10,7 @@ import Language.Mulang.Parsers.Java spec :: Spec spec = do - let run code scope inspection = compileExpectation (Expectation scope inspection) code + let run code scope inspection | Right f <- compileExpectation (Expectation scope inspection) = f code it "works with DeclaresEntryPoint" $ do run (hs "f x = 2") "*" "DeclaresEntryPoint" `shouldBe` False diff --git a/src/Language/Mulang/Analyzer.hs b/src/Language/Mulang/Analyzer.hs index c27411bce..17b68fa41 100644 --- a/src/Language/Mulang/Analyzer.hs +++ b/src/Language/Mulang/Analyzer.hs @@ -30,11 +30,15 @@ analyseAst :: Expression -> AnalysisSpec -> IO AnalysisResult analyseAst ast spec = do domaingLang <- compileDomainLanguage (domainLanguage spec) testResults <- analyseTests ast (testAnalysisType spec) - return $ AnalysisCompleted (analyseExpectations ast (expectations spec) ++ analyseCustomExpectations ast (customExpectations spec)) - (analyseSmells ast domaingLang (smellsSet spec)) - (analyseSignatures ast (signatureAnalysisType spec)) - testResults - (analyzeIntermediateLanguage ast spec) + return $ buildAnalysis (analyseExpectations ast (expectations spec)) + (analyseCustomExpectations ast (customExpectations spec)) + (analyseSmells ast domaingLang (smellsSet spec)) + (analyseSignatures ast (signatureAnalysisType spec)) + testResults + (analyzeIntermediateLanguage ast spec) + +buildAnalysis (Left m) _ _ _ _ _ = AnalysisFailed m +buildAnalysis (Right ers) ecrs s sg trs ilrs = AnalysisCompleted (ers ++ ecrs) s sg trs ilrs analyzeIntermediateLanguage :: Expression -> AnalysisSpec -> Maybe Expression analyzeIntermediateLanguage ast spec diff --git a/src/Language/Mulang/Analyzer/EdlQueryCompiler.hs b/src/Language/Mulang/Analyzer/EdlQueryCompiler.hs index c350f6cb8..e2030db18 100644 --- a/src/Language/Mulang/Analyzer/EdlQueryCompiler.hs +++ b/src/Language/Mulang/Analyzer/EdlQueryCompiler.hs @@ -10,9 +10,10 @@ import Data.Function.Extra (orElse, andAlso, never) import Language.Mulang import Language.Mulang.Consult (Consult) import Language.Mulang.Counter (plus) -import Language.Mulang.Inspector.Primitive (atLeast, atMost, exactly) +import Language.Mulang.Inspector.Primitive (lenient, atLeast, atMost, exactly) import Language.Mulang.Inspector.Literal (isNil, isNumber, isBool, isChar, isString, isSymbol, isSelf) import Language.Mulang.Analyzer.Synthesizer (decodeUsageInspection, decodeDeclarationInspection) +import Language.Mulang.Analyzer.Finding (Finding) import qualified Language.Mulang.Edl.Expectation as E @@ -21,12 +22,11 @@ import Control.Monad.Except import Data.List.Split (splitOn) type Scope = (ContextualizedInspection -> ContextualizedInspection, IdentifierPredicate -> IdentifierPredicate) -type Compilation e = Either String e compileTopQuery :: E.Query -> Inspection -compileTopQuery = either (const (const True)) id . compileQuery +compileTopQuery = either (const lenient) id . compileQuery -compileQuery :: E.Query -> Compilation Inspection +compileQuery :: E.Query -> Finding Inspection compileQuery (E.Decontextualize query) = compileCQuery id query >>= (return . decontextualize) compileQuery (E.Within name query) | (scope, p) <- compileWithin name = fmap (decontextualize.scope) (compileCQuery p query) compileQuery (E.Through name query) | (scope, p) <- compileThrough name = fmap (decontextualize.scope) (compileCQuery p query) @@ -42,7 +42,7 @@ scopeFor :: ([Identifier] -> Inspection -> Inspection) -> Identifier -> Scope scopeFor f name = (contextualized (f names), andAlso (except (last names))) where names = splitOn "." name -compileCQuery :: (IdentifierPredicate -> IdentifierPredicate) -> E.CQuery -> Compilation ContextualizedInspection +compileCQuery :: (IdentifierPredicate -> IdentifierPredicate) -> E.CQuery -> Finding ContextualizedInspection compileCQuery pm (E.Inspection i p m) = ($ (compilePredicate pm p)) <$> compileInspection (compileVerb i) m compileCQuery pm (E.AtLeast n q) = contextualized (atLeast (encode n)) <$> compileTQuery pm q compileCQuery pm (E.AtMost n q) = contextualized (atMost (encode n)) <$> compileTQuery pm q @@ -51,7 +51,7 @@ compileCQuery pm (E.CNot q) = contextualized never <$> compileCQuery pm (E.CAnd q1 q2) = contextualized2 andAlso <$> compileCQuery pm q1 <*> compileCQuery pm q2 compileCQuery pm (E.COr q1 q2) = contextualized2 orElse <$> compileCQuery pm q1 <*> compileCQuery pm q2 -compileTQuery :: (IdentifierPredicate -> IdentifierPredicate) -> E.TQuery -> Compilation ContextualizedCounter +compileTQuery :: (IdentifierPredicate -> IdentifierPredicate) -> E.TQuery -> Finding ContextualizedCounter compileTQuery pm (E.Counter i p m) = ($ (compilePredicate pm p)) <$> compileCounter (compileVerb i) m compileTQuery pm (E.Plus q1 q2) = contextualized2 plus <$> (compileTQuery pm q1) <*> (compileTQuery pm q2) @@ -71,7 +71,7 @@ compileVerb :: String -> String compileVerb = concat . map headToUpper . words where headToUpper (x:xs) = toUpper x : xs -compileCounter :: String -> E.Matcher -> Compilation (ContextualizedBoundCounter) +compileCounter :: String -> E.Matcher -> Finding (ContextualizedBoundCounter) compileCounter = f where f "UsesIf" m = plainMatching countIfs m @@ -90,7 +90,7 @@ compileCounter = f f "Calls" m = boundMatching countCalls m f other m = throwError $ "Can not count over " ++ other ++ " with matcher " ++ show m -compileInspection :: String -> E.Matcher -> Compilation ContextualizedBoundInspection +compileInspection :: String -> E.Matcher -> Finding ContextualizedBoundInspection compileInspection = f where f "Assigns" m = boundMatching assignsMatching m @@ -173,22 +173,22 @@ compileInspection = f primitiveUsage = decodeUsageInspection primitiveDeclaration = decodeDeclarationInspection -contextual :: ContextualizedConsult a -> Compilation (ContextualizedBoundConsult a) +contextual :: ContextualizedConsult a -> Finding (ContextualizedBoundConsult a) contextual = return . contextualizedBind -contextualizedBound :: ContextualizedBoundConsult a -> Compilation (ContextualizedBoundConsult a) +contextualizedBound :: ContextualizedBoundConsult a -> Finding (ContextualizedBoundConsult a) contextualizedBound = return -plain :: Consult a -> Compilation (ContextualizedBoundConsult a) +plain :: Consult a -> Finding (ContextualizedBoundConsult a) plain = return . contextualizedBind . contextualize -bound :: BoundConsult a -> Compilation (ContextualizedBoundConsult a) +bound :: BoundConsult a -> Finding (ContextualizedBoundConsult a) bound = return . boundContextualize -boundMatching :: (Matcher -> BoundConsult a) -> E.Matcher -> Compilation (ContextualizedBoundConsult a) +boundMatching :: (Matcher -> BoundConsult a) -> E.Matcher -> Finding (ContextualizedBoundConsult a) boundMatching f m = bound (f (compileMatcher m)) -plainMatching :: (Matcher -> Consult a) -> E.Matcher -> Compilation (ContextualizedBoundConsult a) +plainMatching :: (Matcher -> Consult a) -> E.Matcher -> Finding (ContextualizedBoundConsult a) plainMatching f m = plain (f (compileMatcher m)) compileMatcher :: E.Matcher -> Matcher @@ -199,7 +199,7 @@ compileClauses :: [E.Clause] -> Matcher compileClauses = withEvery . f where f :: [E.Clause] -> [Inspection] - f (E.IsAnything:args) = isAnything : (f args) + f (E.IsAnything:args) = lenient : (f args) f (E.IsChar value:args) = isChar value : (f args) f (E.IsFalse:args) = isBool False : (f args) f (E.IsLiteral:args) = isLiteral : (f args) diff --git a/src/Language/Mulang/Analyzer/ExpectationsAnalyzer.hs b/src/Language/Mulang/Analyzer/ExpectationsAnalyzer.hs index f94c280ef..a1a520bae 100644 --- a/src/Language/Mulang/Analyzer/ExpectationsAnalyzer.hs +++ b/src/Language/Mulang/Analyzer/ExpectationsAnalyzer.hs @@ -4,13 +4,14 @@ module Language.Mulang.Analyzer.ExpectationsAnalyzer ( import Data.Maybe (fromMaybe) import Language.Mulang +import Language.Mulang.Analyzer.Finding (Finding, mapFindings) import Language.Mulang.Analyzer.Analysis (Expectation, ExpectationResult(..)) import Language.Mulang.Analyzer.ExpectationsCompiler (compileExpectation) -analyseExpectations :: Expression -> Maybe [Expectation] -> [ExpectationResult] -analyseExpectations content = map (analyseExpectation content) . (fromMaybe []) +analyseExpectations :: Expression -> Maybe [Expectation] -> Finding [ExpectationResult] +analyseExpectations content = mapFindings (analyseExpectation content) . (fromMaybe []) -analyseExpectation :: Expression -> Expectation -> ExpectationResult -analyseExpectation ast e = ExpectationResult e (compileAndEval e) - where compileAndEval e = (compileExpectation e) ast +analyseExpectation :: Expression -> Expectation -> Finding ExpectationResult +analyseExpectation ast e = fmap (ExpectationResult e) (compileAndEval e) + where compileAndEval e = fmap ($ ast) (compileExpectation e) diff --git a/src/Language/Mulang/Analyzer/ExpectationsCompiler.hs b/src/Language/Mulang/Analyzer/ExpectationsCompiler.hs index 88a244a52..0f69fbe89 100644 --- a/src/Language/Mulang/Analyzer/ExpectationsCompiler.hs +++ b/src/Language/Mulang/Analyzer/ExpectationsCompiler.hs @@ -8,16 +8,14 @@ import Language.Mulang.Edl.Expectation import Language.Mulang (Inspection) import Language.Mulang.Analyzer.EdlQueryCompiler (compileTopQuery) import qualified Language.Mulang.Analyzer.Analysis as A +import Language.Mulang.Analyzer.Finding (Finding) import qualified Data.Map.Strict as Map import Data.List.Split (splitOn) import Control.Monad.Except -import Data.Either (either) -type Compilation e = Either String e - -compileExpectation :: A.Expectation -> Inspection -compileExpectation (A.Expectation s i) = either (const (const True)) id (fmap (compileTopQuery . negator . scope) baseQuery) +compileExpectation :: A.Expectation -> Finding Inspection +compileExpectation (A.Expectation s i) = fmap (compileTopQuery . negator . scope) baseQuery where (inspectionParts, negator) = compileInspectionPartsAndNegator (splitOn ":" i) scope = compileScope (splitOn ":" s) @@ -33,7 +31,7 @@ compileScope ["Intransitive",name] q = Within name q compileScope [name] q = Through name q compileScope _ q = Decontextualize q -compileCQuery :: [String] -> Compilation CQuery +compileCQuery :: [String] -> Finding CQuery compileCQuery [] = compileCQuery ["Parses","*"] compileCQuery [verb] = compileCQuery [verb,"*"] compileCQuery [verb,name] | Map.member name nullaryMatchers = compileCQuery [verb,"*",name] @@ -50,13 +48,13 @@ compileBinding ('~':name) = Like name compileBinding ('=':name) = Named name compileBinding name = Named name -compileMatcher :: [String] -> Compilation Matcher +compileMatcher :: [String] -> Finding Matcher compileMatcher = fmap matching . f where matching [] = Unmatching matching xs = Matching xs - f :: [String] -> Compilation [Clause] + f :: [String] -> Finding [Clause] f (name:args) | Just matcher <- Map.lookup name nullaryMatchers = fmap (matcher :) (f args) f (name:value:args) | Just matcher <- Map.lookup name binaryMatchers = fmap (matcher value :) (f args) f [] = return [] diff --git a/src/Language/Mulang/Analyzer/Finding.hs b/src/Language/Mulang/Analyzer/Finding.hs new file mode 100644 index 000000000..0d25e0d4a --- /dev/null +++ b/src/Language/Mulang/Analyzer/Finding.hs @@ -0,0 +1,23 @@ +module Language.Mulang.Analyzer.Finding ( + mock, + mockInspection, + mapFindings, + Finding) where + +import Language.Mulang.Inspector.Primitive (Inspection, lenient) + +type Finding e = Either String e + +mock :: e -> Finding e -> e +mock replacement = either (const replacement) id + +mockInspection :: Finding Inspection -> Inspection +mockInspection = mock lenient + +mapFindings :: (a -> Finding b) -> [a] -> Finding [b] +mapFindings f = foldr accum (return []) . map f + where + accum (Right x) (Right xs) = Right (x:xs) + accum (Left m) (Right _) = Left m + accum (Left m2) (Left m1) = Left (m1 ++ ";" ++ m2) + accum _ (Left m) = Left m diff --git a/src/Language/Mulang/Inspector/Literal.hs b/src/Language/Mulang/Inspector/Literal.hs index becab9db5..cf805228c 100644 --- a/src/Language/Mulang/Inspector/Literal.hs +++ b/src/Language/Mulang/Inspector/Literal.hs @@ -1,5 +1,4 @@ module Language.Mulang.Inspector.Literal ( - isAnything, isBool, isChar, isNil, @@ -16,9 +15,6 @@ import Language.Mulang.Ast import Language.Mulang.Ast.Operator (Operator (..)) import Language.Mulang.Inspector.Primitive (Inspection) -isAnything :: Inspection -isAnything = const True - isNil :: Inspection isNil = (==) MuNil diff --git a/src/Language/Mulang/Inspector/Primitive.hs b/src/Language/Mulang/Inspector/Primitive.hs index 9cb98dfde..34b6c6506 100644 --- a/src/Language/Mulang/Inspector/Primitive.hs +++ b/src/Language/Mulang/Inspector/Primitive.hs @@ -1,4 +1,5 @@ module Language.Mulang.Inspector.Primitive ( + lenient, containsExpression, containsDeclaration, containsBody, @@ -22,6 +23,9 @@ import Language.Mulang.Generator (expressions, equationBodies, declarations) type Inspection = Consult Bool +lenient :: Inspection +lenient = const True + containsExpression :: Inspection -> Inspection containsExpression f = has f expressions From d6cabd29f6e3600e2427b00538946371ccb76e6a Mon Sep 17 00:00:00 2001 From: Franco Bulgarelli Date: Sat, 5 Oct 2019 17:14:21 -0300 Subject: [PATCH 4/7] Refactor: removing unnecessary parameters --- src/Language/Mulang/Analyzer.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Language/Mulang/Analyzer.hs b/src/Language/Mulang/Analyzer.hs index 17b68fa41..5de9aaa30 100644 --- a/src/Language/Mulang/Analyzer.hs +++ b/src/Language/Mulang/Analyzer.hs @@ -37,8 +37,8 @@ analyseAst ast spec = do testResults (analyzeIntermediateLanguage ast spec) -buildAnalysis (Left m) _ _ _ _ _ = AnalysisFailed m -buildAnalysis (Right ers) ecrs s sg trs ilrs = AnalysisCompleted (ers ++ ecrs) s sg trs ilrs +buildAnalysis (Left message) _ = \_ _ _ _ -> AnalysisFailed message +buildAnalysis (Right expectationResults) customExpectationResults = AnalysisCompleted (expectationResults ++ customExpectationResults) analyzeIntermediateLanguage :: Expression -> AnalysisSpec -> Maybe Expression analyzeIntermediateLanguage ast spec From 9d134589408c9c22420c206c2562f30027620e5b Mon Sep 17 00:00:00 2001 From: Franco Bulgarelli Date: Sat, 5 Oct 2019 17:27:26 -0300 Subject: [PATCH 5/7] Refactor on composition --- src/Language/Mulang/Analyzer/EdlQueryCompiler.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Language/Mulang/Analyzer/EdlQueryCompiler.hs b/src/Language/Mulang/Analyzer/EdlQueryCompiler.hs index e2030db18..40dab72b8 100644 --- a/src/Language/Mulang/Analyzer/EdlQueryCompiler.hs +++ b/src/Language/Mulang/Analyzer/EdlQueryCompiler.hs @@ -186,10 +186,10 @@ bound :: BoundConsult a -> Finding (ContextualizedBoundConsult a) bound = return . boundContextualize boundMatching :: (Matcher -> BoundConsult a) -> E.Matcher -> Finding (ContextualizedBoundConsult a) -boundMatching f m = bound (f (compileMatcher m)) +boundMatching f m = bound . f . compileMatcher $ m plainMatching :: (Matcher -> Consult a) -> E.Matcher -> Finding (ContextualizedBoundConsult a) -plainMatching f m = plain (f (compileMatcher m)) +plainMatching f m = plain . f . compileMatcher $ m compileMatcher :: E.Matcher -> Matcher compileMatcher (E.Matching clauses) = compileClauses clauses From 6efc01a511d6f034ed230cb739ff82d13efdfebd Mon Sep 17 00:00:00 2001 From: Franco Bulgarelli Date: Sat, 5 Oct 2019 17:56:01 -0300 Subject: [PATCH 6/7] Exposing custom expectations errors --- spec/CustomExpectationsAnalyzerSpec.hs | 5 +- spec/ExpectationsAnalyzerSpec.hs | 4 +- src/Language/Mulang/Analyzer.hs | 5 +- .../Analyzer/CustomExpectationsAnalyzer.hs | 12 +++-- .../Mulang/Analyzer/EdlQueryCompiler.hs | 48 +++++++++---------- .../Mulang/Analyzer/ExpectationsCompiler.hs | 2 +- 6 files changed, 42 insertions(+), 34 deletions(-) diff --git a/spec/CustomExpectationsAnalyzerSpec.hs b/spec/CustomExpectationsAnalyzerSpec.hs index 05251f586..9eaa33380 100644 --- a/spec/CustomExpectationsAnalyzerSpec.hs +++ b/spec/CustomExpectationsAnalyzerSpec.hs @@ -158,9 +158,12 @@ spec = describe "ExpectationsAnalyzer" $ do (run JavaScript "if (true) {}; if(true) {}; if (false) {}" "expectation: count (UsesIf) >= 3") `shouldReturn` ok + it "rejects unsupported counters" $ do + (run JavaScript "if (true) {}; " "expectation: count (declares) >= 3") `shouldReturn` AnalysisFailed "Can not count over Declares with matcher Unmatching" + it "is case sensitive in standard syntax" $ do (run JavaScript "" "expectation: UsesIf") `shouldReturn` nok - (run JavaScript "" "expectation: usesif") `shouldReturn` ok + (run JavaScript "" "expectation: usesif") `shouldReturn` AnalysisFailed "Unknown inspection Usesif with matcher Unmatching" it "accepts titlecase in standard syntax" $ do (run JavaScript "" "expectation: UsesIf") `shouldReturn` nok diff --git a/spec/ExpectationsAnalyzerSpec.hs b/spec/ExpectationsAnalyzerSpec.hs index 2638761db..70244f9d2 100644 --- a/spec/ExpectationsAnalyzerSpec.hs +++ b/spec/ExpectationsAnalyzerSpec.hs @@ -20,11 +20,11 @@ spec = describe "ExpectationsAnalyzer" $ do it "evaluates unknown basic expectations" $ do let hasTurtle = Expectation "x" "HasTurtle" - (run Haskell "x = 2" [hasTurtle]) `shouldReturn` (result [passed hasTurtle] []) + (run Haskell "x = 2" [hasTurtle]) `shouldReturn` AnalysisFailed "Unknown inspection HasTurtle with matcher Unmatching" it "evaluates unknown basic negated expectations" $ do let notHasTurtle = Expectation "x" "Not:HasTurtle" - (run Haskell "x = 2" [notHasTurtle]) `shouldReturn` (result [passed notHasTurtle] []) + (run Haskell "x = 2" [notHasTurtle]) `shouldReturn` AnalysisFailed "Unknown inspection HasTurtle with matcher Unmatching" it "evaluates empty expectations" $ do (run Haskell "x = 2" []) `shouldReturn` (result [] []) diff --git a/src/Language/Mulang/Analyzer.hs b/src/Language/Mulang/Analyzer.hs index 5de9aaa30..8059f47f6 100644 --- a/src/Language/Mulang/Analyzer.hs +++ b/src/Language/Mulang/Analyzer.hs @@ -37,8 +37,9 @@ analyseAst ast spec = do testResults (analyzeIntermediateLanguage ast spec) -buildAnalysis (Left message) _ = \_ _ _ _ -> AnalysisFailed message -buildAnalysis (Right expectationResults) customExpectationResults = AnalysisCompleted (expectationResults ++ customExpectationResults) +buildAnalysis (Left message) _ = \_ _ _ _ -> AnalysisFailed message +buildAnalysis _ (Left message) = \_ _ _ _ -> AnalysisFailed message +buildAnalysis (Right expectationResults) (Right customExpectationResults) = AnalysisCompleted (expectationResults ++ customExpectationResults) analyzeIntermediateLanguage :: Expression -> AnalysisSpec -> Maybe Expression analyzeIntermediateLanguage ast spec diff --git a/src/Language/Mulang/Analyzer/CustomExpectationsAnalyzer.hs b/src/Language/Mulang/Analyzer/CustomExpectationsAnalyzer.hs index 1fe1e83f8..8418c0a45 100644 --- a/src/Language/Mulang/Analyzer/CustomExpectationsAnalyzer.hs +++ b/src/Language/Mulang/Analyzer/CustomExpectationsAnalyzer.hs @@ -4,14 +4,18 @@ module Language.Mulang.Analyzer.CustomExpectationsAnalyzer ( import Data.Maybe (fromMaybe) import Language.Mulang +import Language.Mulang.Analyzer.Finding import Language.Mulang.Analyzer.Analysis (customExpectationResult, ExpectationResult(..)) import Language.Mulang.Analyzer.EdlQueryCompiler (compileTopQuery) import Language.Mulang.Edl (parseExpectations, Expectation (..)) -analyseCustomExpectations :: Expression -> Maybe String -> [ExpectationResult] -analyseCustomExpectations ast = fromMaybe [] . fmap (map (runExpectation ast) . parseExpectations) +analyseCustomExpectations :: Expression -> Maybe String -> Finding [ExpectationResult] +analyseCustomExpectations ast = fromMaybe (return []) . fmap (analyseCustomExpectations' ast) -runExpectation :: Expression -> Expectation -> ExpectationResult -runExpectation ast (Expectation name q) = customExpectationResult name (compileTopQuery q ast) +analyseCustomExpectations' :: Expression -> String -> Finding [ExpectationResult] +analyseCustomExpectations' ast = mapFindings (analyseExpectation ast) . parseExpectations +analyseExpectation :: Expression -> Expectation -> Finding ExpectationResult +analyseExpectation ast (Expectation name q) = fmap (customExpectationResult name) compileAndEval + where compileAndEval = fmap ($ ast) (compileTopQuery q) diff --git a/src/Language/Mulang/Analyzer/EdlQueryCompiler.hs b/src/Language/Mulang/Analyzer/EdlQueryCompiler.hs index 40dab72b8..6c4f28cde 100644 --- a/src/Language/Mulang/Analyzer/EdlQueryCompiler.hs +++ b/src/Language/Mulang/Analyzer/EdlQueryCompiler.hs @@ -23,8 +23,8 @@ import Data.List.Split (splitOn) type Scope = (ContextualizedInspection -> ContextualizedInspection, IdentifierPredicate -> IdentifierPredicate) -compileTopQuery :: E.Query -> Inspection -compileTopQuery = either (const lenient) id . compileQuery +compileTopQuery :: E.Query -> Finding Inspection +compileTopQuery = compileQuery compileQuery :: E.Query -> Finding Inspection compileQuery (E.Decontextualize query) = compileCQuery id query >>= (return . decontextualize) @@ -186,31 +186,31 @@ bound :: BoundConsult a -> Finding (ContextualizedBoundConsult a) bound = return . boundContextualize boundMatching :: (Matcher -> BoundConsult a) -> E.Matcher -> Finding (ContextualizedBoundConsult a) -boundMatching f m = bound . f . compileMatcher $ m +boundMatching f m = (fmap f . compileMatcher) m >>= bound plainMatching :: (Matcher -> Consult a) -> E.Matcher -> Finding (ContextualizedBoundConsult a) -plainMatching f m = plain . f . compileMatcher $ m +plainMatching f m = (fmap f . compileMatcher) m >>= plain -compileMatcher :: E.Matcher -> Matcher +compileMatcher :: E.Matcher -> Finding Matcher compileMatcher (E.Matching clauses) = compileClauses clauses -compileMatcher _ = const True +compileMatcher _ = return $ const True -compileClauses :: [E.Clause] -> Matcher -compileClauses = withEvery . f +compileClauses :: [E.Clause] -> Finding Matcher +compileClauses = fmap withEvery . f where - f :: [E.Clause] -> [Inspection] - f (E.IsAnything:args) = lenient : (f args) - f (E.IsChar value:args) = isChar value : (f args) - f (E.IsFalse:args) = isBool False : (f args) - f (E.IsLiteral:args) = isLiteral : (f args) - f (E.IsLogic:args) = usesLogic : (f args) - f (E.IsMath:args) = usesMath : (f args) - f (E.IsNil:args) = isNil : (f args) - f (E.IsNonliteral:args) = isNonliteral : (f args) - f (E.IsSelf:args) = isSelf : (f args) - f (E.IsTrue:args) = isBool True : (f args) - f (E.IsNumber value:args) = isNumber value : (f args) - f (E.IsString value:args) = isString value : (f args) - f (E.IsSymbol value:args) = isSymbol value : (f args) - f (E.That expectation:args) = compileTopQuery expectation : (f args) - f [] = [] + f :: [E.Clause] -> Finding [Inspection] + f (E.IsAnything:args) = fmap (lenient :) (f args) + f (E.IsChar value:args) = fmap (isChar value :) (f args) + f (E.IsFalse:args) = fmap (isBool False :) (f args) + f (E.IsLiteral:args) = fmap (isLiteral :) (f args) + f (E.IsLogic:args) = fmap (usesLogic :) (f args) + f (E.IsMath:args) = fmap (usesMath :) (f args) + f (E.IsNil:args) = fmap (isNil :) (f args) + f (E.IsNonliteral:args) = fmap (isNonliteral :) (f args) + f (E.IsSelf:args) = fmap (isSelf :) (f args) + f (E.IsTrue:args) = fmap (isBool True :) (f args) + f (E.IsNumber value:args) = fmap (isNumber value :) (f args) + f (E.IsString value:args) = fmap (isString value :) (f args) + f (E.IsSymbol value:args) = fmap (isSymbol value :) (f args) + f (E.That expectation:args) = (:) <$> compileTopQuery expectation <*> (f args) + f [] = return [] diff --git a/src/Language/Mulang/Analyzer/ExpectationsCompiler.hs b/src/Language/Mulang/Analyzer/ExpectationsCompiler.hs index 0f69fbe89..6aee40045 100644 --- a/src/Language/Mulang/Analyzer/ExpectationsCompiler.hs +++ b/src/Language/Mulang/Analyzer/ExpectationsCompiler.hs @@ -15,7 +15,7 @@ import Data.List.Split (splitOn) import Control.Monad.Except compileExpectation :: A.Expectation -> Finding Inspection -compileExpectation (A.Expectation s i) = fmap (compileTopQuery . negator . scope) baseQuery +compileExpectation (A.Expectation s i) = baseQuery >>= compileTopQuery . negator . scope where (inspectionParts, negator) = compileInspectionPartsAndNegator (splitOn ":" i) scope = compileScope (splitOn ":" s) From 7d038368eca6f287a6911f189c1295cc0b4ef19d Mon Sep 17 00:00:00 2001 From: Franco Bulgarelli Date: Sat, 5 Oct 2019 18:57:51 -0300 Subject: [PATCH 7/7] Implementing EvaluatorModes --- mulang.cabal | 1 + src/Language/Mulang/Analyzer.hs | 10 ++++++++-- src/Language/Mulang/Analyzer/Analysis.hs | 14 ++++++++++---- src/Language/Mulang/Analyzer/Analysis/Json.hs | 1 + .../Mulang/Analyzer/CustomExpectationsAnalyzer.hs | 15 ++++++++------- .../Mulang/Analyzer/ExpectationsAnalyzer.hs | 11 ++++++----- .../Mulang/Analyzer/ExpectationsEvaluator.hs | 15 +++++++++++++++ src/Language/Mulang/Analyzer/Finding.hs | 12 +++--------- 8 files changed, 52 insertions(+), 27 deletions(-) create mode 100644 src/Language/Mulang/Analyzer/ExpectationsEvaluator.hs diff --git a/mulang.cabal b/mulang.cabal index 6d188188b..b2ecdfd28 100644 --- a/mulang.cabal +++ b/mulang.cabal @@ -79,6 +79,7 @@ library Language.Mulang.Analyzer.CustomExpectationsAnalyzer Language.Mulang.Analyzer.DomainLanguageCompiler Language.Mulang.Analyzer.EdlQueryCompiler + Language.Mulang.Analyzer.ExpectationsEvaluator Language.Mulang.Analyzer.ExpectationsAnalyzer Language.Mulang.Analyzer.ExpectationsCompiler Language.Mulang.Analyzer.Finding diff --git a/src/Language/Mulang/Analyzer.hs b/src/Language/Mulang/Analyzer.hs index 8059f47f6..f80588672 100644 --- a/src/Language/Mulang/Analyzer.hs +++ b/src/Language/Mulang/Analyzer.hs @@ -4,6 +4,7 @@ module Language.Mulang.Analyzer ( import Language.Mulang import Language.Mulang.Analyzer.Analysis hiding (Inspection) +import Language.Mulang.Analyzer.ExpectationsEvaluator (lenientMode, strictMode) import Language.Mulang.Analyzer.DomainLanguageCompiler (compileDomainLanguage) import Language.Mulang.Analyzer.ExpectationsAnalyzer (analyseExpectations) import Language.Mulang.Analyzer.CustomExpectationsAnalyzer (analyseCustomExpectations) @@ -28,10 +29,11 @@ analyse' (Analysis sample spec) = analyseSample . parseFragment $ sample analyseAst :: Expression -> AnalysisSpec -> IO AnalysisResult analyseAst ast spec = do + let evaluator = compileExpectationsEvaluator (expectationsEvaluator spec) domaingLang <- compileDomainLanguage (domainLanguage spec) testResults <- analyseTests ast (testAnalysisType spec) - return $ buildAnalysis (analyseExpectations ast (expectations spec)) - (analyseCustomExpectations ast (customExpectations spec)) + return $ buildAnalysis (analyseExpectations evaluator ast (expectations spec)) + (analyseCustomExpectations evaluator ast (customExpectations spec)) (analyseSmells ast domaingLang (smellsSet spec)) (analyseSignatures ast (signatureAnalysisType spec)) testResults @@ -45,3 +47,7 @@ analyzeIntermediateLanguage :: Expression -> AnalysisSpec -> Maybe Expression analyzeIntermediateLanguage ast spec | fromMaybe False (includeIntermediateLanguage spec) = Just ast | otherwise = Nothing + + +compileExpectationsEvaluator (Just StrictMode) = strictMode +compileExpectationsEvaluator _ = lenientMode diff --git a/src/Language/Mulang/Analyzer/Analysis.hs b/src/Language/Mulang/Analyzer/Analysis.hs index 4ecc56b57..957bdd1eb 100644 --- a/src/Language/Mulang/Analyzer/Analysis.hs +++ b/src/Language/Mulang/Analyzer/Analysis.hs @@ -36,6 +36,7 @@ module Language.Mulang.Analyzer.Analysis ( Smell, SmellsSet(..), TestAnalysisType(..), + ExpectationsEvaluator(..), AnalysisResult(..), ExpectationResult(..)) where @@ -78,7 +79,8 @@ data AnalysisSpec = AnalysisSpec { domainLanguage :: Maybe DomainLanguage, includeIntermediateLanguage :: Maybe Bool, originalLanguage :: Maybe Language, - autocorrectionRules :: Maybe AutocorrectionRules + autocorrectionRules :: Maybe AutocorrectionRules, + expectationsEvaluator :: Maybe ExpectationsEvaluator } deriving (Show, Eq, Generic) data DomainLanguage = DomainLanguage { @@ -88,6 +90,10 @@ data DomainLanguage = DomainLanguage { jargon :: Maybe [String] } deriving (Show, Eq, Generic) +data ExpectationsEvaluator + = LenientMode + | StrictMode deriving (Show, Eq, Generic) + data CaseStyle = CamelCase | SnakeCase @@ -175,7 +181,7 @@ allSmellsBut :: [Smell] -> Maybe SmellsSet allSmellsBut = Just . AllSmells . Just emptyAnalysisSpec :: AnalysisSpec -emptyAnalysisSpec = AnalysisSpec Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing +emptyAnalysisSpec = AnalysisSpec Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing emptyAnalysis :: Fragment -> Analysis emptyAnalysis code = Analysis code emptyAnalysisSpec @@ -184,10 +190,10 @@ domainLanguageAnalysis :: Fragment -> DomainLanguage -> Analysis domainLanguageAnalysis code domainLanguage = Analysis code (emptyAnalysisSpec { domainLanguage = Just domainLanguage, smellsSet = allSmells }) customExpectationsAnalysis :: Fragment -> String -> Analysis -customExpectationsAnalysis code es = Analysis code (emptyAnalysisSpec { customExpectations = Just es }) +customExpectationsAnalysis code es = Analysis code (emptyAnalysisSpec { customExpectations = Just es, expectationsEvaluator = Just StrictMode }) expectationsAnalysis :: Fragment -> [Expectation] -> Analysis -expectationsAnalysis code es = Analysis code (emptyAnalysisSpec { expectations = Just es }) +expectationsAnalysis code es = Analysis code (emptyAnalysisSpec { expectations = Just es, expectationsEvaluator = Just StrictMode }) smellsAnalysis :: Fragment -> Maybe SmellsSet -> Analysis smellsAnalysis code set = Analysis code (emptyAnalysisSpec { smellsSet = set }) diff --git a/src/Language/Mulang/Analyzer/Analysis/Json.hs b/src/Language/Mulang/Analyzer/Analysis/Json.hs index fffff044e..866120a8c 100644 --- a/src/Language/Mulang/Analyzer/Analysis/Json.hs +++ b/src/Language/Mulang/Analyzer/Analysis/Json.hs @@ -17,6 +17,7 @@ instance FromJSON CaseStyle instance FromJSON SignatureAnalysisType instance FromJSON SignatureStyle +instance FromJSON ExpectationsEvaluator instance FromJSON NormalizationOptions instance FromJSON SequenceSortMode diff --git a/src/Language/Mulang/Analyzer/CustomExpectationsAnalyzer.hs b/src/Language/Mulang/Analyzer/CustomExpectationsAnalyzer.hs index 8418c0a45..d51c72dee 100644 --- a/src/Language/Mulang/Analyzer/CustomExpectationsAnalyzer.hs +++ b/src/Language/Mulang/Analyzer/CustomExpectationsAnalyzer.hs @@ -5,17 +5,18 @@ import Data.Maybe (fromMaybe) import Language.Mulang import Language.Mulang.Analyzer.Finding +import Language.Mulang.Analyzer.ExpectationsEvaluator (ExpectationsEvaluator) import Language.Mulang.Analyzer.Analysis (customExpectationResult, ExpectationResult(..)) import Language.Mulang.Analyzer.EdlQueryCompiler (compileTopQuery) import Language.Mulang.Edl (parseExpectations, Expectation (..)) -analyseCustomExpectations :: Expression -> Maybe String -> Finding [ExpectationResult] -analyseCustomExpectations ast = fromMaybe (return []) . fmap (analyseCustomExpectations' ast) +analyseCustomExpectations :: ExpectationsEvaluator -> Expression -> Maybe String -> Finding [ExpectationResult] +analyseCustomExpectations evaluator ast = fromMaybe (return []) . fmap (analyseCustomExpectations' evaluator ast) -analyseCustomExpectations' :: Expression -> String -> Finding [ExpectationResult] -analyseCustomExpectations' ast = mapFindings (analyseExpectation ast) . parseExpectations +analyseCustomExpectations' :: ExpectationsEvaluator -> Expression -> String -> Finding [ExpectationResult] +analyseCustomExpectations' evaluator ast = mapFindings (analyseExpectation evaluator ast) . parseExpectations -analyseExpectation :: Expression -> Expectation -> Finding ExpectationResult -analyseExpectation ast (Expectation name q) = fmap (customExpectationResult name) compileAndEval - where compileAndEval = fmap ($ ast) (compileTopQuery q) +analyseExpectation :: ExpectationsEvaluator -> Expression -> Expectation -> Finding ExpectationResult +analyseExpectation evaluator ast (Expectation name q) = fmap (customExpectationResult name) compileAndEval + where compileAndEval = evaluator ast (compileTopQuery q) diff --git a/src/Language/Mulang/Analyzer/ExpectationsAnalyzer.hs b/src/Language/Mulang/Analyzer/ExpectationsAnalyzer.hs index a1a520bae..a677e4854 100644 --- a/src/Language/Mulang/Analyzer/ExpectationsAnalyzer.hs +++ b/src/Language/Mulang/Analyzer/ExpectationsAnalyzer.hs @@ -4,14 +4,15 @@ module Language.Mulang.Analyzer.ExpectationsAnalyzer ( import Data.Maybe (fromMaybe) import Language.Mulang +import Language.Mulang.Analyzer.ExpectationsEvaluator (ExpectationsEvaluator) import Language.Mulang.Analyzer.Finding (Finding, mapFindings) import Language.Mulang.Analyzer.Analysis (Expectation, ExpectationResult(..)) import Language.Mulang.Analyzer.ExpectationsCompiler (compileExpectation) -analyseExpectations :: Expression -> Maybe [Expectation] -> Finding [ExpectationResult] -analyseExpectations content = mapFindings (analyseExpectation content) . (fromMaybe []) +analyseExpectations :: ExpectationsEvaluator -> Expression -> Maybe [Expectation] -> Finding [ExpectationResult] +analyseExpectations evaluator ast = mapFindings (analyseExpectation evaluator ast) . (fromMaybe []) -analyseExpectation :: Expression -> Expectation -> Finding ExpectationResult -analyseExpectation ast e = fmap (ExpectationResult e) (compileAndEval e) - where compileAndEval e = fmap ($ ast) (compileExpectation e) +analyseExpectation :: ExpectationsEvaluator -> Expression -> Expectation -> Finding ExpectationResult +analyseExpectation evaluator ast e = fmap (ExpectationResult e) (compileAndEval e) + where compileAndEval e = evaluator ast (compileExpectation e) diff --git a/src/Language/Mulang/Analyzer/ExpectationsEvaluator.hs b/src/Language/Mulang/Analyzer/ExpectationsEvaluator.hs new file mode 100644 index 000000000..7d891fed2 --- /dev/null +++ b/src/Language/Mulang/Analyzer/ExpectationsEvaluator.hs @@ -0,0 +1,15 @@ +module Language.Mulang.Analyzer.ExpectationsEvaluator ( + lenientMode, + strictMode, + ExpectationsEvaluator) where + +import Language.Mulang.Analyzer.Finding (Finding, mock) +import Language.Mulang.Ast (Expression) +import Language.Mulang.Inspector.Primitive (Inspection, lenient) + + +type ExpectationsEvaluator = Expression -> Finding Inspection -> Finding Bool + +lenientMode, strictMode :: ExpectationsEvaluator +lenientMode ast = strictMode ast . mock lenient +strictMode ast = fmap ($ ast) diff --git a/src/Language/Mulang/Analyzer/Finding.hs b/src/Language/Mulang/Analyzer/Finding.hs index 0d25e0d4a..310071359 100644 --- a/src/Language/Mulang/Analyzer/Finding.hs +++ b/src/Language/Mulang/Analyzer/Finding.hs @@ -1,18 +1,12 @@ module Language.Mulang.Analyzer.Finding ( mock, - mockInspection, mapFindings, Finding) where -import Language.Mulang.Inspector.Primitive (Inspection, lenient) - type Finding e = Either String e -mock :: e -> Finding e -> e -mock replacement = either (const replacement) id - -mockInspection :: Finding Inspection -> Inspection -mockInspection = mock lenient +mock :: e -> Finding e -> Finding e +mock replacement = Right . either (const replacement) (id) mapFindings :: (a -> Finding b) -> [a] -> Finding [b] mapFindings f = foldr accum (return []) . map f @@ -20,4 +14,4 @@ mapFindings f = foldr accum (return []) . map f accum (Right x) (Right xs) = Right (x:xs) accum (Left m) (Right _) = Left m accum (Left m2) (Left m1) = Left (m1 ++ ";" ++ m2) - accum _ (Left m) = Left m + accum _ (Left m) = Left m