Haskell - Parsec을 사용하여 명명 규칙 적용
21023 단어 haskellfunctionalparsing
V<version number>__<file name>.sql
+ + + + + +
| | | | | |
| | | | | v
| | | | | Enforce sql extension.
| | | | v
| | | | Enforce this dot to indicate the end of the file name.
| | | v
| | | File name need to be alpha numeric. No other symbols, except underscore
| | v
| | Enforce the double underscore
| v
| Version number have to be numbers.
v
Enforce the V character to signify "version"
내가 할 수 있는 방법은 다음과 같습니다.
솔직히 말해서 매우 간단한 정규식을 알고 있고 regex license 이 없기 때문에 정규식으로 이 작업을 수행하는 방법을 모르겠습니다. 저는 파서 조합기에 능숙하며 이 블로그 게시물의 제목이기도 합니다. 그래서 이것이 제가 설정한 작업을 수행하는 데 사용할 것입니다.
파서 결합자는
json
와 같은 재귀적인 것을 파싱할 때 정말 빛납니다. json
배열과 개체를 포함할 수 있고 이러한 개체와 배열이 배열과 개체 등을 포함할 수 있는 방법을 알고 있습니까? 예, 파서 결합기는 정말 깔끔합니다. 따라서 이 블로그 게시물에서만 파서 결합자를 판단하지 마십시오. 나는 파서 조합기를 선택했는데, 그것이 내가 파싱을 할 때 가장 많이 사용하는 방법이고 개인적으로 편리하다고 생각하기 때문입니다.구문 분석을 하기 전에 위의 ASCII 다이어그램을 기반으로 파일 이름의 형식 표현을 만들어 보겠습니다.
data FilenameStructure
= FilenameStructure FileVersion Underscore Underscore FileName Dot FileExtension
deriving ( Eq, Show )
newtype FileVersion = FileVersion Text
deriving ( Eq, Show )
newtype Underscore = Underscore Text
deriving ( Eq, Show )
newtype FileName = FileName Text
deriving ( Eq, Show )
newtype Dot = Dot Text
deriving ( Eq, Show )
newtype FileExtension = FileExtension Text
deriving ( Eq, Show )
이제 전체 파일 이름을 구문 분석하는 대신 파일 이름의 특정 부분에 집중할 수 있습니다. 그게 좀 더 수월해지는 것 같아요. 적어도 나에게는 그렇습니다. 파일 버전, 밑줄, 파일 이름 등을 개별적으로 구문 분석하는 것을 생각할 수 있습니다.
수입품부터 시작하겠습니다. 마음에 떠오르는 인기있는 라이브러리는 parsec, megaparsec 및 attoparsec입니다. 어느 것이 귀하의 프로젝트에 적합한지 평가하십시오. 그러나 이 블로그 게시물에서는 parsec을 사용하고 있습니다.
-- parsec
import Text.ParserCombinators.Parsec (GenParser, ParseError)
import qualified Text.ParserCombinators.Parsec as Parsec
-- text
import qualified Data.Text as T
파일 버전 파서부터 시작하겠습니다. 우리의 규칙에 따르면
V
문자로 시작해야 하며 그 뒤에 숫자가 와야 합니다.fileVersionParser :: GenParser Char st FileVersion
fileVersionParser = do
vChar <- Parsec.char 'V'
vNum <- Parsec.digit
pure $ FileVersion $ T.pack $ ( vChar : vNum : [] )
ghci
에서 이것을 시도하면 다음과 같이 보일 것입니다.ghci> import Text.ParserCombinators.Parsec
ghci> parse fileVersionParser "" "V69"
ghci> Right ( FileVersion "V69" )
Right
를 일치시키고 FileVersion
를 반환하는 것이 우리가 원하는 것입니다.텍스트 "V69".
ghci> parse fileVersionParser "" "Vwhat"
ghci> Left (line 1, column2):
unexpected "w"
맞습니다. 버전 번호가 없으면 실패해야 합니다.
ghci> parse fileVersionParser "" "V1what"
ghci> Right ( FileVersion "V1" )
숫자가 아닌 입력을 삭제합니다.
ghci> parse fileVersionParser "" "what"
ghci> Left (line 1, column 1):
unexpected "w"
expecting "V"
마지막으로 "V"문자를 찾지 못하면 실패합니다.
우후! 하나의 파서가 다운되고 4개가 남았습니다!
나머지 파서를 수행하고
ghci
에서 자유롭게 시도해 보겠습니다.underscoreParser :: GenParser Char st Underscore
underscoreParser = Underscore . T.pack . pure <$> Parsec.satisfy isUnderscore
isUnderscore :: Char -> Bool
isUnderscore char = any ( char== ) ( "_" :: String )
fileNameParser :: GenParser Char st FileName
fileNameParser = FileName . T.pack
<$> Parsec.many ( Parsec.alphaNum <|> Parsec.satisfy isUnderscore )
dotParser :: GenParser Char st Dot
dotParser = Dot . T.pack . pure <$> Parser.char '.'
fileExtensionParser :: GenParser Char st FileExtension
fileExtensionParser = FileExtension. T.pack <$> Parsec.string "sql"
그런 다음 전체 파일 이름에 대한 파서를 만들기 위해 나머지 파서를 결합합니다.
filenameStructureParser :: GenParser Char st FilenameStructure
filenameStructureParser = FilenameStructure
<$> fileVersionParser
<*> underscoreParser
<*> underscoreParser
<*> fileNameParser
<*> dotParser
<*> fileExtensionParser
더 편리한 경우
do
구문을 사용하여 이 작업을 수행할 수도 있습니다.filenameStructureParser :: GenParser Char st FilenameStructure
filenameStructureParser = do
version <- fileVersionParser
u1 <- underscoreParser
u2 <- underscoreParser
fileName <- fileNameParser
dot <- dotParser
ext <- fileExtensionParser
pure $ FilenameStructure version u1 u2 fileName dot ext
마지막으로 파서 "runner"
namingConvention :: String -> Either ParseError FilenameStructure
namingConvention =
Parsec.parse filenameStructureParser "Error: Not following naming convention"
ghci
에서 수동으로 시도하는 대신 일부 단위 테스트를 수행할 수 있습니다. 따라서 우리는 터미널을 계속해서 어지럽힐 필요가 없습니다.module NamingConventionSpec where
import Test.Hspec
spec :: Spec
spec = do
desribe "Filename" $ do
it "follows naming convention" $ do
successResult <- pure $ namingConvention "V1__uuid_extension.sql"
emptyFileName <- pure $ namingConvention ""
noExtension <- pure $ namingConvention "V1__uuid_extension"
noVersion <- pure $ namingConvention "uuid_extension.sql"
upperCaseFileName <- pure $ namingConvention "V1_UUID_extension.sql"
symbolFileName <- pure $ namingConvention "V1_UUID+extension.sql"
-- success cases
shouldBe successResult ( Right "V1__uuid_extension.sql" )
shouldBe upperCaseFileName ( Right "V1__UUID_extension.sql" )
-- failure cases
shouldBe ( isLeft emptyFileName ) True
shouldBe ( isLeft noFileExt ) True
shouldBe ( isLeft noVersion ) True
shouldBe ( isLeft symbolFilename ) True
이제 파서가 있으므로 중복 검사를 사용할 수 있습니다. 유형에
Eq
인스턴스가 있으므로 동등성 검사를 수행할 수 있기 때문입니다.fileVersion :: FilenameStructure -> FileVersion
fileVersion ( FilenameStructure v _ _ _ _ _ ) = v
getVersion :: Either ParseError FilenameStructure -> Either ParseError FileVersion
getVersion res = case res of
Left err -> Left err
Right fs -> Right $ fileVersion fs
checkDuplicate :: [ String ] -> Either ParseError [ String ]
checkDuplicate filenames = do
versions <- traverse getVersion ( namingConvention <$> filenames )
if anySame versions
-- Express your errors however you want, this is just to show that this is
-- the error branch.
then error "Duplicate migration file"
else Right filenames
where
anySame :: Eq a => [ a ] -> Bool
anySame = isSeen []
isSeen :: Eq a => [ a ] -> [ a ] -> Bool
isSeen seen ( x:xs ) = x `elem` seen || isSeen ( x:seen ) xs
isSeen _ [] = False
파서를 생성할 때 유효성 검사 또는 "스마트"논리를 수행하지 마십시오. 입력 구문 분석에 집중하고 더 이상 아무것도 하지 마십시오. 파서가 있으면 출력으로 원하는 모든 작업을 수행할 수 있습니다.
참고문헌
Haskell from First Principles
Monadic Parser Combinators
Reference
이 문제에 관하여(Haskell - Parsec을 사용하여 명명 규칙 적용), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/piq9117/haskell-enforcing-naming-convention-with-parsec-1f2h
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
Reference
이 문제에 관하여(Haskell - Parsec을 사용하여 명명 규칙 적용), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/piq9117/haskell-enforcing-naming-convention-with-parsec-1f2h텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)