diff --git a/mulang.cabal b/mulang.cabal index 9edd344f4..b2ecdfd28 100644 --- a/mulang.cabal +++ b/mulang.cabal @@ -76,11 +76,13 @@ 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.ExpectationsEvaluator 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/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/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..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,15 +29,25 @@ 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 $ 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 evaluator ast (expectations spec)) + (analyseCustomExpectations evaluator ast (customExpectations spec)) + (analyseSmells ast domaingLang (smellsSet spec)) + (analyseSignatures ast (signatureAnalysisType spec)) + testResults + (analyzeIntermediateLanguage ast spec) + +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 | 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 1fe1e83f8..d51c72dee 100644 --- a/src/Language/Mulang/Analyzer/CustomExpectationsAnalyzer.hs +++ b/src/Language/Mulang/Analyzer/CustomExpectationsAnalyzer.hs @@ -4,14 +4,19 @@ module Language.Mulang.Analyzer.CustomExpectationsAnalyzer ( 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 -> [ExpectationResult] -analyseCustomExpectations ast = fromMaybe [] . fmap (map (runExpectation ast) . parseExpectations) +analyseCustomExpectations :: ExpectationsEvaluator -> Expression -> Maybe String -> Finding [ExpectationResult] +analyseCustomExpectations evaluator ast = fromMaybe (return []) . fmap (analyseCustomExpectations' evaluator ast) -runExpectation :: Expression -> Expectation -> ExpectationResult -runExpectation ast (Expectation name q) = customExpectationResult name (compileTopQuery q ast) +analyseCustomExpectations' :: ExpectationsEvaluator -> Expression -> String -> Finding [ExpectationResult] +analyseCustomExpectations' evaluator ast = mapFindings (analyseExpectation evaluator ast) . parseExpectations +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/EdlQueryCompiler.hs b/src/Language/Mulang/Analyzer/EdlQueryCompiler.hs index d7c4aeafe..6c4f28cde 100644 --- a/src/Language/Mulang/Analyzer/EdlQueryCompiler.hs +++ b/src/Language/Mulang/Analyzer/EdlQueryCompiler.hs @@ -10,21 +10,23 @@ 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 -import Data.Maybe (fromMaybe) +import Data.Either (either) +import Control.Monad.Except import Data.List.Split (splitOn) type Scope = (ContextualizedInspection -> ContextualizedInspection, IdentifierPredicate -> IdentifierPredicate) -compileTopQuery :: E.Query -> Inspection -compileTopQuery = fromMaybe (const True) . compileQuery +compileTopQuery :: E.Query -> Finding Inspection +compileTopQuery = compileQuery -compileQuery :: E.Query -> Maybe 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) @@ -40,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 -> Maybe 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 @@ -49,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 -> Maybe 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) @@ -69,7 +71,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 -> Finding (ContextualizedBoundCounter) compileCounter = f where f "UsesIf" m = plainMatching countIfs m @@ -86,8 +88,9 @@ compileCounter = f f "DeclaresProcedure" m = boundMatching countProcedures m f "DeclaresVariable" m = boundMatching countVariables m f "Calls" m = boundMatching countCalls m + f other m = throwError $ "Can not count over " ++ other ++ " with matcher " ++ show m -compileInspection :: String -> E.Matcher -> Maybe ContextualizedBoundInspection +compileInspection :: String -> E.Matcher -> Finding ContextualizedBoundInspection compileInspection = f where f "Assigns" m = boundMatching assignsMatching m @@ -165,49 +168,49 @@ 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 = throwError $ "Unknown inspection " ++ other ++ " with matcher " ++ show m primitiveUsage = decodeUsageInspection primitiveDeclaration = decodeDeclarationInspection -contextual :: ContextualizedConsult a -> Maybe (ContextualizedBoundConsult a) -contextual = Just . contextualizedBind +contextual :: ContextualizedConsult a -> Finding (ContextualizedBoundConsult a) +contextual = return . contextualizedBind -contextualizedBound :: ContextualizedBoundConsult a -> Maybe (ContextualizedBoundConsult a) -contextualizedBound = Just +contextualizedBound :: ContextualizedBoundConsult a -> Finding (ContextualizedBoundConsult a) +contextualizedBound = return -plain :: Consult a -> Maybe (ContextualizedBoundConsult a) -plain = Just . contextualizedBind . contextualize +plain :: Consult a -> Finding (ContextualizedBoundConsult a) +plain = return . contextualizedBind . contextualize -bound :: BoundConsult a -> Maybe (ContextualizedBoundConsult a) -bound = Just . boundContextualize +bound :: BoundConsult a -> Finding (ContextualizedBoundConsult a) +bound = return . boundContextualize -boundMatching :: (Matcher -> BoundConsult a) -> E.Matcher -> Maybe (ContextualizedBoundConsult a) -boundMatching f m = bound (f (compileMatcher m)) +boundMatching :: (Matcher -> BoundConsult a) -> E.Matcher -> Finding (ContextualizedBoundConsult a) +boundMatching f m = (fmap f . compileMatcher) m >>= bound -plainMatching :: (Matcher -> Consult a) -> E.Matcher -> Maybe (ContextualizedBoundConsult a) -plainMatching f m = plain (f (compileMatcher m)) +plainMatching :: (Matcher -> Consult a) -> E.Matcher -> Finding (ContextualizedBoundConsult a) +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) = isAnything : (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/ExpectationsAnalyzer.hs b/src/Language/Mulang/Analyzer/ExpectationsAnalyzer.hs index f94c280ef..a677e4854 100644 --- a/src/Language/Mulang/Analyzer/ExpectationsAnalyzer.hs +++ b/src/Language/Mulang/Analyzer/ExpectationsAnalyzer.hs @@ -4,13 +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] -> [ExpectationResult] -analyseExpectations content = map (analyseExpectation content) . (fromMaybe []) +analyseExpectations :: ExpectationsEvaluator -> Expression -> Maybe [Expectation] -> Finding [ExpectationResult] +analyseExpectations evaluator ast = mapFindings (analyseExpectation evaluator ast) . (fromMaybe []) -analyseExpectation :: Expression -> Expectation -> ExpectationResult -analyseExpectation ast e = ExpectationResult e (compileAndEval e) - where compileAndEval e = (compileExpectation e) ast +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/ExpectationsCompiler.hs b/src/Language/Mulang/Analyzer/ExpectationsCompiler.hs index be83a8788..6aee40045 100644 --- a/src/Language/Mulang/Analyzer/ExpectationsCompiler.hs +++ b/src/Language/Mulang/Analyzer/ExpectationsCompiler.hs @@ -8,12 +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 -compileExpectation :: A.Expectation -> Inspection -compileExpectation (A.Expectation s i) = compileTopQuery . negator . scope $ baseQuery +compileExpectation :: A.Expectation -> Finding Inspection +compileExpectation (A.Expectation s i) = baseQuery >>= compileTopQuery . negator . scope where (inspectionParts, negator) = compileInspectionPartsAndNegator (splitOn ":" i) scope = compileScope (splitOn ":" s) @@ -29,7 +31,7 @@ compileScope ["Intransitive",name] q = Within name q compileScope [name] q = Through name q compileScope _ q = Decontextualize q -compileCQuery :: [String] -> CQuery +compileCQuery :: [String] -> Finding CQuery compileCQuery [] = compileCQuery ["Parses","*"] compileCQuery [verb] = compileCQuery [verb,"*"] compileCQuery [verb,name] | Map.member name nullaryMatchers = compileCQuery [verb,"*",name] @@ -37,7 +39,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 +48,25 @@ compileBinding ('~':name) = Like name compileBinding ('=':name) = Named name compileBinding name = Named name -compileMatcher :: [String] -> Matcher -compileMatcher = matching . f +compileMatcher :: [String] -> Finding 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] -> 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 [] + 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 [ 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 new file mode 100644 index 000000000..310071359 --- /dev/null +++ b/src/Language/Mulang/Analyzer/Finding.hs @@ -0,0 +1,17 @@ +module Language.Mulang.Analyzer.Finding ( + mock, + mapFindings, + Finding) where + +type Finding e = Either String e + +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 + 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