Haskell Parsec Parsing سلسلة من العناصر
سؤال
لدي قائمة أحتاج إلى تحليل المكان الذي لا يحتاج إليه العنق الأخير ولكن يجب تحليله بواسطة محلل واحد، ويجب تحليل العنصر الأخير من قبل محلل آخر.
a = "p1 p1b ... p2"
or
a = "p2"
في الأصل حاولت
parser = do parse1 <- many parser1
parse2 <- parser2
return AParse parse1 parse2
المشكلة هي أن PARSE1 يمكن أن تستهلك إدخال PARSE2. لذلك Parse1 تستهلك دائما القائمة بأكملها، وترك parse2 مع أي شيء.
هل هناك طريقة للقول لتطبيق PARSE1 على كل شيء إلى جانب العنصر الأخير في سلسلة، ثم قم بتطبيق PARSE2؟
المحلول
ماذا عن:
parseTrain car caboose = choice
[ fmap (:[]) $ try (caboose `endBy` eof),
, liftM2 (:) car (parseTrain car caboose)
[
أخطاء EOF لي، لأن هذا يجعل هذا المحلل لا تتراكم. أي لا يمكنك القول:
char '(' >> parseTrain p1 p2 >> char ')'
القيام بهذا الكوكبيا صعب للغاية بالنسبة للمحافظة. كيف من المفترض أن تعرف الانتقال إلى Char ')'، دون محاولة في كل فرصة ورؤية ما إذا كانت تفشل؟ القيام بذلك يمكن أن يكون الوقت الأسي.
إذا كنت في حاجة إليها أن تكون مركوبة، فهل مشكلتك لديها بعض الهيكل الإضافي الذي يمكنك استغلاله؟ يمكنك، على سبيل المثال، تحليل قائمة بجميع العناصر ثم معالجة واحدة الأخيرة بعد الحقيقة؟
نصائح أخرى
إذا كنت تستطيع العامل parser1
بحيث يتم تعريفه مثل ذلك:
parser1 = (try parser2) <|> parser1extra
ثم تصبح المشكلة قائمة parser1extra
أو parser2
التي يجب أن تنتهي في وقت لاحق. يمكنك رمز ذلك كما:
parserList =
liftM2 (:) (try parser1extra) parserList
<|>
liftM2 (:) (try parser2) (option [] parserList)
قد تحتاج أو لا تحتاج إلى try
المكالمات اعتمادا على ما إذا كان لدى هذه المحللين أي تداخل بادئة.
إذا كنت لا تريد أن تكون قيمة الإرجاع قائمة، ولكن بدلا من ذلك، فستتمكن من إعادة الكتابة بهذه الطريقة:
parserList =
do
a <- try parser1extra
prefix a parserList
<|>
do
a <- try parser2
option (AParse [] a) (prefix a parserList)
where prefix a p = do
(AParse as t) <- p
return $ (AParse (a:as) t)
أو مثال كامل:
import Control.Monad
import Text.ParserCombinators.Parsec
parseNum = do { v <- many1 digit; spaces; return v }
parseWord = do { v <- many1 letter; spaces; return v }
parsePart = parseNum <|> parseWord
parsePartListEndingInWord =
liftM2 (:) (try parseNum) parsePartListEndingInWord
<|>
liftM2 (:) (try parseWord) (option [] parsePartListEndingInWord)
في الواقع، فإن المكالمات لمحاولة ليست حاجة في هذه الحالة، كما parseNum
و parseWord
تطابق لا بادئة مشتركة. لاحظ أن parsePartListEndingInWord
لا يشير في الواقع parsePart
, ، ولكن بدلا من ذلك، الخياران الذي تشكل parsePart
تعريف
(الإجابة الأصلية، حل موقف مختلف إلى حد ما :)
ماذا عن شيء مثل:
parserTest = between (char '[') (char ']') $ do
p1s <- try parser1 `endBy` char ','
p2 <- parser2
return $ AParse p1s p2
أخذ علامات الترقيم من المحللين الخاص بك وما يصل إلى pedsetest يسمح لك باستخدام المحرك between
و endBy
للقيام بالعمل من أجلك. أخيرا، try
هناك حتى إذا parser1
و parser2
تطابق بادئة مشتركة، endBy
سيؤدي النسخ الاحتياطي الكامل الصحيح إلى بداية البادئة المشتركة.
اعتمادا على المحللين الخاص بك، من الممكن أن تتمكن من ترك مطابقة الترقيم داخل المحللين الفرعي، وكل ما تحتاجه قد يكون try
حول parser1
:
parseTest = do parse1 <- many (try parser1)
parse2 <- parser2
return AParse parse1 parse2
أنا نوع من الجمع بين النهجين:
parserList = try (do a <- parser2
eof
return $ AParse [] a)
<|>
do a <- parser1
prefix a parserList
where
prefix a p = do
(AParse as t) <- p
return $ AParse a:as t
أعتقد أن هذا سيعمل لأغراضي. شكرًا!
هذا وسوف تفعل خدعة:
parser1 `manyTill` (try parser2)