Parsec: расширение работающего парсера дает странные результаты

Для проекта нам поручили создать парсер/оценщик haskell, который управляет крошечным роботом arduino.

Итак, для начала я провел некоторое исследование уже реализованных настроек парсера и наткнулся на этот: https://wiki.haskell.org/Parsing_a_simple_imperative_language.

После (по общему признанию) копирования и вставки кода я начал его тестировать. И это сработало\о/. Теперь пришло время расширить его функциональность.

Отредактированный код:

data Stmt = Seq [Stmt]
     | Assign String AExpr
     | If BExpr Stmt Stmt
     | While BExpr Stmt
     | Motor String AExpr
     | Skip
       deriving (Show)

Token.reservedNames   = [ "if"
                                , "then"
                                , "else"
                                , "while"
                                , "do"
                                , "skip"
                                , "true"
                                , "false"
                                , "not"
                                , "and"
                                , "or", "set" , "to"
                                ]
statement' :: Parser Stmt
statement' =   ifStmt
      <|> whileStmt
      <|> skipStmt
      <|> assignStmt
      <|> motorStatement

motorStatement :: Parser Stmt
motorStatement =
  do reserved "set"
     var <- identifier
     reserved "to"
     expr <- aExpression
     return $ Motor var expr

Это единственные фрагменты кода, которые я редактировал. Чтобы проверить все это, я сделал небольшой тестовый файл:

x := 4;
x := 6;

Приведенный выше код прекрасно проанализирован до моих изменений, но после того, как я добавил изменения, я получаю следующую ошибку

< (line 3, column 1):
< unexpected end of input
< expecting "if", "while", "skip", identifier or "set"

С моими ограниченными знаниями о Haskell я не могу понять, почему происходит это "unexpected end of input".

Возможно, какой-нибудь Haskeller здесь сможет указать мне на ошибку.


person MrKickkiller    schedule 14.05.2016    source источник


Ответы (1)


Я не уверен, что ваш ввод был правильно проанализирован до того, как вы внесли изменения. Список операторов анализируется с использованием sepBy1:

sequenceOfStmt =
  do list <- (sepBy1 statement' semi)
     -- If there's only one statement return it without using Seq.
     return $ if length list == 1 then head list else Seq list

sequenceOfStmt является жадным в том смысле, что если он увидит точку с запятой, он будет ожидать увидеть еще один statement'. Таким образом, точку с запятой следует рассматривать как разделитель операторов, а не как терминатор операторов.

Попробуйте запустить эти тесты, чтобы увидеть, как ведет себя sepBy1:

import Text.Parsec
import Text.Parsec.Combinator

p1 = sepBy1 (char 'a') (char ';')

test1 = parseTest p1 "a;a"      -- OK
test2 = parseTest p1 "a;a;"     -- FAILS

Чтобы помочь в изучении этой проблемы, я поместил исходный код языка ParseWhile на lpaste.net: http://lpaste.net/163332

person ErikR    schedule 14.05.2016
comment
Прочитав ваш ответ, я быстро осознал свою ошибку. Каждая точка с запятой делает так, что ей нужна/требуется другая строка/оператор. А поскольку пустую строку нельзя преобразовать в тип, возникает ошибка. Спасибо, сэр! - person MrKickkiller; 14.05.2016