Ошибка синтаксического анализатора Haskell Happy, несоответствие типов и бесконечный тип

При написании синтаксического анализатора языка, подобного Оберону, у меня возникли проблемы с компиляцией синтаксического анализатора после того, как я обновил его, чтобы иметь возможность определять больше процедур на одном уровне, а не только вкладывать одну в другую.

Это мой лексер:

{
module Lexer where
}

%wrapper "basic"

$alpha      = [a-zA-Z]
$digit      = [0-9]
$validChar  = [^\"]

tokens :-

  $white+                             ;
  "PROCEDURE"                 { \s -> KW_TokenProcedure }
  "END"                       { \s -> KW_TokenEnd }
  ";"                         { \s -> KW_TokenSemiColon }
  $alpha [$alpha $digit \_]*  { \s -> TokenVariableIdentifier s }

{

-- The token type:
data Token =
  KW_TokenProcedure               |
  KW_TokenEnd                     |
  KW_TokenSemiColon               |
  TokenVariableIdentifier String  |
    deriving (Eq,Show)
}

Это мой парсер:

{
module Main where
import Lexer
import Tools
}

%name myParse
%tokentype { Token }
%error { parseError }

%token
  KW_PROCEDURE          { KW_TokenProcedure }
  KW_END                { KW_TokenEnd }
  ';'                   { KW_TokenSemiColon }
  identifier            { TokenVariableIdentifier $$ }

%%

ProcedureDeclarationList  :   ProcedureDeclaration                              { $1 }
                          |   ProcedureDeclaration ';' ProcedureDeclarationList { $3 : $1 }

ProcedureDeclaration  : ProcedureHeading ';' ProcedureBody identifier   {
                                                                          do
                                                                            let newProc = $1  -- Crea la nuova procedura
                                                                            let procBody = $3
                                                                            addProcedureToProcedure newProc procBody
                                                                        }

ProcedureHeading        :   KW_PROCEDURE identifier { defaultProcedure { procedureName = $2 } }

ProcedureBody           : KW_END                                    { Nothing }
                        | DeclarationSequence KW_END                { Just $1 }

DeclarationSequence     :    ProcedureDeclarationList                 { $1 }

{
parseError :: [Token] -> a
parseError _ = error "Parse error"

main = do
  inStr <- getContents
  let result = oLikeParse (alexScanTokens inStr)
  putStrLn ("result: " ++ show(result))
}

А это модуль, в котором определяются типы и некоторые служебные функции:

module Tools where

data Procedure = Procedure {    procedureName :: String,
                                procedureProcedures :: [Procedure] } deriving (Show)

defaultProcedure = Procedure {  procedureName = "",
                                procedureProcedures = [] }

addProcedureToProcedure :: Procedure -> Maybe Procedure -> Procedure
addProcedureToProcedure procDest Nothing            = Procedure {   procedureName = (procedureName procDest),
                                                                    procedureProcedures = (procedureProcedures procDest) }
addProcedureToProcedure procDest (Just procToAdd)   = Procedure {   procedureName = (procedureName procDest),
                                                                    procedureProcedures = (procedureProcedures procDest) ++ [procToAdd] }

Ошибки, которые компилятор дает мне, следующие:

  • Couldn't match type ‘[Procedure]’ with ‘Procedure’
  • Occurs check: cannot construct the infinite type: t4 ~ [t4]

Я изолировал проблему и точно знаю, что если я удалю второй случай моего ProcedureDeclarationList, все будет нормально компилироваться, но я не могу распознать больше процедур на том же уровне.


ОБНОВИТЬ

Я изменил свою структуру данных, так что я больше не использую Maybe Procedure, и мне не нужен список из двух типов, но у меня все еще есть проблема с несоответствием типов.

Это мой обновленный парсер:

{
module Main where
import Lexer
import Tools
}

%name myParse
%tokentype { Token }
%error { parseError }

%token
  KW_INTEGER            { KW_TokenInteger }
  KW_REAL               { KW_TokenReal }
  KW_BOOLEAN            { KW_TokenBoolean }
  KW_CHAR               { KW_TokenChar }
  KW_PROCEDURE          { KW_TokenProcedure }
  KW_END                { KW_TokenEnd }
  KW_VAR                { KW_TokenVar }
  ';'                   { KW_TokenSemiColon }
  ','                   { KW_TokenComa }
  ':'                   { KW_TokenColon }
  identifier            { TokenVariableIdentifier $$ }

%%

ProcedureDeclarationList  :   ProcedureDeclaration                              { [$1] }
                          |   ProcedureDeclaration ';' ProcedureDeclarationList { $1:$3 }

ProcedureDeclaration  : ProcedureHeading ';' ProcedureBody identifier { defaultDeclaration { declarationType = DT_Procedure, procedureDeclared = (addBodyToProcedure $1 $3)} }

IdentifiersList     :   identifier                      { [$1] }
                    |   identifier ',' IdentifiersList  { $1:$3 }

VariableDeclaration : IdentifiersList ':' type          { createVariablesDefinitionsOfType $1 $3 }

ProcedureHeading    : KW_PROCEDURE identifier { defaultProcedure { procedureName = $2 } }

ProcedureBody     : KW_END                                      { [] }
                  | DeclarationSequence KW_END                  { $1 }

DeclarationSequence   : KW_VAR VariableDeclarationList ';'      { $2 }
                      | ProcedureDeclarationList                { $1 }

VariableDeclarationList : VariableDeclaration                             { [$1] }
                        | VariableDeclaration ';' VariableDeclarationList { $1:$3 }

type        :   KW_INTEGER    { Integer }
            |   KW_REAL       { Float }
            |   KW_BOOLEAN    { Boolean }
            |   KW_CHAR       { Char }

{
parseError :: [Token] -> a
parseError _ = error "Parse error"

main = do
  inStr <- getContents
  let result = oLikeParse (alexScanTokens inStr)
  putStrLn ("result: " ++ show(result))
}

Это мой обновленный лексер:

{
module Lexer where
}

%wrapper "basic"

$alpha      = [a-zA-Z]
$digit      = [0-9]
$validChar  = [^\"]

tokens :-

  $white+                             ;
  "INTEGER"                   { \s -> KW_TokenInteger }
  "REAL"                      { \s -> KW_TokenReal }
  "BOOLEAN"                   { \s -> KW_TokenBoolean }
  "CHAR"                      { \s -> KW_TokenChar }
  "PROCEDURE"                 { \s -> KW_TokenProcedure }
  "END"                       { \s -> KW_TokenEnd }
  "VAR"                       { \s -> KW_TokenVar }
  ";"                         { \s -> KW_TokenSemiColon }
  ","                         { \s -> KW_TokenComa }
  ":"                         { \s -> KW_TokenColon }
  $alpha [$alpha $digit \_]*  { \s -> TokenVariableIdentifier s }

{

-- The token type:
data Token =
  KW_TokenInteger                 |
  KW_TokenReal                    |
  KW_TokenBoolean                 |
  KW_TokenChar                    |
  KW_TokenVar                     |
  KW_TokenProcedure               |
  KW_TokenEnd                     |
  KW_TokenSemiColon               |
  KW_TokenComa                    |
  KW_TokenColon                   |
  TokenVariableIdentifier String  |
    deriving (Eq,Show)
}

А это мой обновленный модуль инструментов:

module Tools where

data AttributeType  = String
                    | Float
                    | Char
                    | Integer
                    | Boolean
                    deriving (Show, Eq)

data Attribute = Attribute {    attributeName :: String,
                                attributeType :: AttributeType,
                                stringValue :: String,
                                floatValue :: Float,
                                integerValue :: Integer,
                                charValue :: Char,
                                booleanValue :: Bool } deriving (Show)

data Procedure = Procedure {    procedureName :: String,
                                attributes :: [Attribute],
                                procedureProcedures :: [Procedure] } deriving (Show)

data DeclarationType    = DT_Variable
                        | DT_Constant
                        | DT_Procedure
                        deriving (Show, Eq)

data Declaration = Declaration {    declarationType     :: DeclarationType,
                                    attributeDeclared   :: Attribute,
                                    procedureDeclared   :: Procedure } deriving (Show)

defaultAttribute = Attribute {  attributeName = "",
                                attributeType = Integer,
                                stringValue = "",
                                floatValue = 0.0,
                                integerValue = 0,
                                charValue = ' ',
                                booleanValue = False }

defaultProcedure = Procedure {  procedureName = "",
                                attributes = [],
                                procedureProcedures = [] }

defaultDeclaration = Declaration {  declarationType = DT_Variable,
                                    attributeDeclared = defaultAttribute,
                                    procedureDeclared = defaultProcedure }

addAttributeToProcedure :: Procedure -> Attribute -> Procedure
addAttributeToProcedure proc att = Procedure {  procedureName = (procedureName proc),
                                                attributes = (attributes proc) ++ [att],
                                                procedureProcedures = (procedureProcedures proc) }

addProcedureToProcedure :: Procedure -> Procedure -> Procedure
addProcedureToProcedure procDest procToAdd  = Procedure {   procedureName = (procedureName procDest),
                                                            attributes = (attributes procDest),
                                                            procedureProcedures = (procedureProcedures procDest) ++ [procToAdd] }

addBodyToProcedure :: Procedure -> [Declaration] -> Procedure
addBodyToProcedure procDest []          =   procDest
addBodyToProcedure procDest declList    = do 
                                            let decl = head declList
                                            let declType = declarationType decl

                                            if declType == DT_Variable || declType == DT_Constant then 
                                                addBodyToProcedure (addAttributeToProcedure procDest (attributeDeclared decl)) (tail declList)
                                            else
                                                addBodyToProcedure (addProcedureToProcedure procDest (procedureDeclared decl)) (tail declList)

createVariablesDefinitionsOfType :: [String] -> AttributeType -> [Declaration]
createVariablesDefinitionsOfType namesList t = map (\x -> defaultDeclaration { declarationType = DT_Variable, attributeDeclared = (defaultAttribute {attributeName = x, attributeType = t})} ) namesList

Это схема типов производства:

PRODUCTION                  TYPE
---------------             ---------------
ProcedureDeclarationList    [Declaration]
ProcedureDeclaration        Declaration
IdentifiersList             [String]
VariableDeclaration         [Declaration]
ProcedureHeading            Procedure
ProcedureBody               [Declaration]
DeclarationSequence         [Declaration]
VariableDeclarationList     [Declaration]
type                        AttributeType

Это единственные 3 ошибки, которые я получаю сейчас:

  • Couldn't match type ‘[Declaration]’ with ‘Declaration’ x2
  • Couldn't match type ‘Declaration’ with ‘[Declaration]’

person zeb    schedule 04.05.2016    source источник


Ответы (1)


Отслеживание типов каждой продукции с комментариями поможет вам отследить ошибки типов.

Вот типы, которые должны быть у каждого производства:

Production                            Type
--------------                        ---------
ProcedureHeading                      Procedure
ProcedureDeclaration                  Procedure
ProcedureDeclarationList              [ Procedure ]
DeclarationSequence                   [ Procedure ]
ProcedureBody                         Maybe Procedure

Теперь проверьте каждое из ваших производственных правил, чтобы убедиться, что они имеют правильный тип.

  1. ProcedureHeading возвращает defaultProcedure с измененным именем, так что все в порядке.

  2. ProcedureDeclaration возвращает результат вызова addProcedureToProcedure, так что это проверяется. Обратите внимание, что вторым аргументом вызова addProcedureToProcedure является $3, который ссылается на результат производства ProcedureBody, поэтому это означает, что возвращаемый тип этого производства должен быть Maybe Procedure.

  3. ProcedureDeclarationList имеет проблемы. Производственные правила должны гласить:

    ProcedureDeclarationList
      : ProcedureDeclaration                              { [ $1 ] }
      | ProcedureDeclaration ';' ProcedureDeclarationList { $1 : $3 }
    

[$1] составляет список из одной процедуры, а $1:$3 добавляет одну процедуру в начало списка процедур.

  1. DeclarationSequence — это просто ProcedureDeclarationList, так что это проверено.

  2. Как отмечалось на шаге 2, ProcedureBody должно быть Maybe Procedure. Правило для KW_END прекрасно, но второе правило нуждается в доработке:

    ProcedureBody
        | KW_END                     { Nothing }
        | DeclarationSequence KW_END { ??? }
    

Из DeclarationSequence (то есть [Procedure]) мы должны создать Maybe Procedure. Вот где твоя проблема. Just $1 имеет тип Maybe [Procedure], так что здесь это не сработает.

person ErikR    schedule 04.05.2016
comment
Кажется, я понял, о чем вы говорите. Возможно, я смогу решить эту проблему по-другому, я имею в виду, что я использовал Maybe Procedure, потому что хотел проверить, есть ли у процедуры что-то внутри тела или нет. Однако, продолжая реализацию, внутри тела процедуры могут быть объявлены переменные, отличные от просто других процедур, поэтому я думаю заменить { Nothing } на { [] } и { Just $1 } на { $1 }, чтобы внутри DeclarationSequence я мог вернуть список, который может содержать процедуры или переменные . Проблема может заключаться в следующем: как я могу хранить два типа в одном списке? - person zeb; 05.05.2016
comment
Вы не можете хранить два разных типа в одном списке. Хотя это не совсем так, когда вы начинаете так думать, это обычно означает, что вы идете по ложному пути. Кстати, если вы нашли что-то из того, что я написал, полезным, голосование уместно и приветствуется. - person ErikR; 05.05.2016
comment
Я изменил свою структуру данных, чтобы больше не использовать Maybe Procedure и что мне больше не нужен список из двух типов. Я обновил свой вопрос, так как все еще получаю некоторые ошибки из-за несоответствия типов. Я отслеживал типы производства, но это не слишком помогло. - person zeb; 05.05.2016
comment
С вашим обновленным синтаксическим анализатором я получаю ошибку unknown identifier 'KW_VAR' плюс некоторые другие при работе с удовольствием. - person ErikR; 05.05.2016
comment
Да, извините, я обновил парсер и добавил обновленный лексер. - person zeb; 05.05.2016
comment
Наконец-то мне удалось решить проблему, и я продолжил реализацию этого «компилятора». Сейчас я изо всех сил пытаюсь отправить пользовательские ошибки при синтаксическом анализе, и я разместил вопрос здесь Большое спасибо за все помощь, которую вы мне оказали! - person zeb; 05.05.2016