۲۳ - ۵اکوسیستم پارسینگ هسکل
هسکل کتابخونههای پارسینگ ِ خیلی خوب و زیادی داره. احتمالاً معروفترینها 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