1
1
mirror of https://github.com/github/semantic.git synced 2024-12-03 22:54:16 +03:00
semantic/test/Matching/Python/Spec.hs

40 lines
1.4 KiB
Haskell
Raw Normal View History

Give Control.Matching API better ergonomics. Given that @tclem and I have found the matcher API frustrating, I've taken a stab at improving its ergonomics, and I've found some success in separating composition of matchers from predicate-based narrowing thereof. The biggest change here is the elimination of the old `match` combinator, which proved to be clumsy in that it complected narrowing and composition. Top-down matching combinators are now written with the `need` combinator and the `>>>` combinator, which is more readable and more versatile. Here's a matcher that accepts functions with Python docstrings: ```haskell docstringMatcher :: ( Decl.Function :< fs , [] :< fs , Lit.TextElement :< fs , term ~ Term (Sum fs) ann ) => Matcher term term docstringMatcher = target <* (need Decl.functionBody >>> narrow @[] >>> mhead >>> narrow @Lit.TextElement >>> ensure Lit.isTripleQuoted)) ``` Pretty readable, right? Each step of the tree regular expression - choosing function bodies, ensuring said bodies are lists, examining the first element, and choosing only TextElements containing triple-quoted strings - is made implicit. The old way would have looked something like this: ```haskell docstringMatcher = target <* match Decl.functionBody $ narrow $ matchM listToMaybe $ target <* ensure Lit.isTripleQuoted ``` which is a good deal more disorganized and less flexible in the quite-common case of applying functions during a matching pass. Separating the act of composition from function application is a big win here. Further comments are inline.
2018-11-03 02:25:29 +03:00
{-# LANGUAGE TypeOperators, TypeFamilies #-}
module Matching.Python.Spec (spec) where
import Control.Arrow
import Control.Matching
import Data.Abstract.Module
import Data.List
import Data.Sum
import qualified Data.Syntax.Declaration as Decl
import qualified Data.Syntax.Literal as Lit
import qualified Data.Syntax.Statement as Stmt
import Data.Text (Text)
import SpecHelpers
-- This gets the Text contents of all integers
docstringMatcher :: ( Decl.Function :< fs
, [] :< fs
, Lit.TextElement :< fs
, term ~ Term (Sum fs) ann
) => Matcher term (TermF Decl.Function ann term)
docstringMatcher =
2018-11-06 00:56:49 +03:00
narrowF <* (enter Decl.functionBody
>>> narrow @[]
Give Control.Matching API better ergonomics. Given that @tclem and I have found the matcher API frustrating, I've taken a stab at improving its ergonomics, and I've found some success in separating composition of matchers from predicate-based narrowing thereof. The biggest change here is the elimination of the old `match` combinator, which proved to be clumsy in that it complected narrowing and composition. Top-down matching combinators are now written with the `need` combinator and the `>>>` combinator, which is more readable and more versatile. Here's a matcher that accepts functions with Python docstrings: ```haskell docstringMatcher :: ( Decl.Function :< fs , [] :< fs , Lit.TextElement :< fs , term ~ Term (Sum fs) ann ) => Matcher term term docstringMatcher = target <* (need Decl.functionBody >>> narrow @[] >>> mhead >>> narrow @Lit.TextElement >>> ensure Lit.isTripleQuoted)) ``` Pretty readable, right? Each step of the tree regular expression - choosing function bodies, ensuring said bodies are lists, examining the first element, and choosing only TextElements containing triple-quoted strings - is made implicit. The old way would have looked something like this: ```haskell docstringMatcher = target <* match Decl.functionBody $ narrow $ matchM listToMaybe $ target <* ensure Lit.isTripleQuoted ``` which is a good deal more disorganized and less flexible in the quite-common case of applying functions during a matching pass. Separating the act of composition from function application is a big win here. Further comments are inline.
2018-11-03 02:25:29 +03:00
>>> mhead
>>> narrow @Lit.TextElement
>>> ensure Lit.isTripleQuoted)
spec :: Spec
spec = describe "matching/python" $ do
it "matches top-level docstrings" $ do
parsed <- parseFile pythonParser "test/fixtures/python/matching/docstrings.py"
let matched = matchRecursively @[] docstringMatcher parsed
length matched `shouldBe` 2
it "matches docstrings recursively" $ do
parsed <- parseFile pythonParser "test/fixtures/python/matching/docstrings_nested.py"
let matched = matchRecursively @[] docstringMatcher parsed
length matched `shouldBe` 3