سؤال

لدي قائمة أحتاج إلى تحليل المكان الذي لا يحتاج إليه العنق الأخير ولكن يجب تحليله بواسطة محلل واحد، ويجب تحليل العنصر الأخير من قبل محلل آخر.

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)
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top