۲۳ - ۵اکوسیستم پارسینگ هسکل
هسکل کتابخونههای پارسینگ ِ خیلی خوب و زیادی داره. احتمالاً معروفترینها parsec
و attoparsec
هستن، ولی کتابخونههای دیگهای مثل megaparsec
هم وجود دارن. aeson
و cassava
از کتابخونههایی اند که برای پارس ِ انواعِ بخصوصی از دادهها درست شدن (به ترتیب برای دادهها ِ JSON و دادهها ِ CSV).
همونطور که دیدین ما برای این فصل trifecta
رو انتخاب کردیم. یکی از دلایلش اینه که trifecta
برخلافِ بعضی کتابخونههای دیگه پیغامهای خطای خیلی ساده و گویایی داره. از طرفِ دیگه به نظر نمیرسه اساسِ trifecta
تغییرات زیادی کنه. طراحیش یه کم غیرمعمول و پیچیدهست، ولی بیشتر چیزهایی که پیچیدهش میکنن، تو این فصل تأثیری به حال شما ندارن. اگه قصد دارین خیلی زیاد پارس کنین، بهتره با attoparsec
آشنا بشین؛ سرعتِ پارس کردنِ این کتابخونه دلیلِ اصلیِ شهرتشه. جلوتر در این فصل کمی با attoparsec
و aeson
کار میکنیم.
trifecta
طوری تکامل پیدا کرده که API ِش* بین دوتا کتابخونه ِ parsers
و trifecta
تقسیم شده. دلیلش اینه که خودِ پکیج ِ trifecta
، پیادهسازیهای بخصوصِ پارسر ِ trifecta
و کاربردهای مختصِ trifecta
رو داره، ولی parsers
مجموعهای از تایپکلاسهاییه که قابلیتهای کلیِ پارسرها رو انتزاعی میکنن. ماژول ِ Text.Trifecta
چیزهایی که از هر دو پکیج لازم هستن رو در اختیارتون میذاره. اینها رو گفتیم که اگه یه وقت خواستین غارنوردی کنین، بدونین از کجا شروع کنین.
API مخففِ application programming interface و به معنای رابط برنامهنویسی کاربردی هست. وقتی یه برنامه رو با استفاده از یه کتابخونه مینویسیم، یا برنامهمون به یه سرویس مثل توئیتر درخواست میفرسته (یا بطور کلی، نرمافزاری رو با اتکا به یه نرمافزارِ دیگه درست میکنیم)، از یه سری توابعِ تعریف شده استفاده میکنیم. API همون توابعی هستن که برای تعامل با نرمافزارِ دوم ازشون استفاده میکنیم؛ بدون اینکه نگرانِ کدِ منبعِشون باشیم. هر وقت یه کتابخونه رو روی Hackage نگاه میکنین، دارین API ِ اون کتابخونه رو میبینین (مگر اینکه روی کدِ منبعِش کلیک کنین).
تایپکلاسهای parsers
همونطور که بالاتر گفتیم، trifecta
به یه سری تایپکلاسهای کتابخونه ِ parsers
اتکا داره. این تایپکلاسها کارهای رایج و مشترکی که پارسرها انجام میدن رو انتزاعی میکنن. به چندتاشون که تو این فصل استفاده میکنیم اشاره میکنیم تا یه شِمای کلی ازشون پیدا کنین.
دقت کنین که توضیحات زیر مربوط به کُدهایی هستن که کتابخونه ِ parsers
براتون تأمین کرده، لازم نیست بنویسینشون!
۱.
تایپکلاسِ Alternative
یه سوپرکلاس برای تایپکلاسِ Parsing
ِه. یه ذره جلوتر از Alternative
بیشتر صحبت میکنیم. تایپکلاسِ Parsing
قابلیتهایی رو تأمین میکنه که برای توصیفِ پارسرها لازماند (مستقل از ورودیشون). حداقل توابعِ لازم برای تعریف یه نمونه ِ کامل از این تایپکلاس try
، (<?>)
، و notFollowedBy
هستن. با try
شروع میکنیم:
-- Text.Parser.Combinators
class Alternative m => Parsing m where
try :: m a -> m a
این تابع یه پارسر میگیره که ممکنه ورودی مصرف کنه، و در صورتِ شکست، برمیگرده به جایی که شروع کرده بوده، در صورتِ مصرف نکردنِ ورودی هم شکست میخوره.
با تابعِ notFollowedBy
(م. به معنای "دنبال نشده توسطِ") از این تایپکلاس میشه کلیدواژهها رو پیدا کرد؛ یه رشته از حروف که میخوایم رو بهش میدیم و تعیین میکنیم که چه چیزی به دنبال اون حروف نباشه:
notFollowedBy :: Show a => m a -> m ()
-- م. مثال برای پارسری که
-- .رو پیدا میکنه let کلیدواژهی
-- > noAlpha = notFollowedBy alphaNum
-- > keywordLet =
-- try $ string "let" <* noAlpha
۲.
تایپکلاسِ Parsing
شاملِ unexpected
(م. به معنای "غیرمنتظره") هم میشه. از این تابع برای صدور خطا روی یه توکِن ِ غیرمنتظره به کار میره (اوایل فصل هم دیدیم). تابعِ eof
فقط در آخرِ ورودی موفق میشه:
eof :: m ()
-- > eof =
-- notFollowedBy anyChar
-- <?> "end of input"
در بخشهای بعدی این تابع رو بیشتر میبینیم.
۳.
این کتابخونه تایپکلاسِ CharParsing
هم با سوپرکلاس ِ Parsing
تعریف میکنه که برای پارس ِ حروف مجرد به کار میره.
-- Text.Parser.Char
class Parsing m => CharParsing m where
از این کلاس قبلاً char
رو دیدیم، ولی تابعهای زیر هم شامل میشه:
-- هر کاراکتری غیر از اونی
-- که بهش داده شده رو پارس
-- میکنه. کاراکتر پارسشده
-- .رو برمیگردونه
notChar :: Char -> m Char
-- پارسر برای هر کاراکتری موفق
-- میشه. کاراکتر پارسشده
-- .رو برمیگردونه
anyChar :: m Char
-- ،تسلسلی از کاراکترها رو پارس میکنه
-- .پارسشده رو برمیگردونه string
string :: String -> m String
-- تسلسلی از کاراکترها با تایپ
-- ای Text رو پارس میکنه، تیکه Text
-- .که پارس شده رو برمیگردونه
text :: Text -> m Text