【问题标题】:Haskell Happy parser error mismatching types and infinite typeHaskell Happy 解析器错误不匹配类型和无限类型
【发布时间】:2016-05-04 19:09:57
【问题描述】:

编写一个类似于 Oberon 的语言解析器,我在更新解析器后编译它时遇到了麻烦,以便能够在同一级别定义更多过程,而不仅仅是一个嵌套在另一个过程中。

这是我的词法分析器:

{
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]’

【问题讨论】:

    标签: haskell happy


    【解决方案1】:

    使用 cmets 跟踪每个产生式的类型将帮助您跟踪类型错误。

    这里每个产品应该有的类型:

    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 ProcedureKW_END 的规则很好,但第二条规则需要一些工作:

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

    DeclarationSequence(即[Procedure])我们必须生成Maybe Procedure。这就是你的问题所在。 Just $1 的类型为 Maybe [Procedure],所以这在这里不起作用。

    【讨论】:

    • 我想我已经明白你在说什么了。也许我可以用不同的方式克服这个问题,我的意思是,我使用了Maybe Procedure,因为我想测试程序是否在体内有东西。尽管进一步实现,在过程主体内部可以声明变量而不是简单的其他过程,所以我正在考虑用{ [] }替换{ Nothing },用{ $1 }替换{ Just $1 },这样在DeclarationSequence我可以返回一个可能包含过程或变量的列表。问题可能是:如何将两种类型存储在同一个列表中?
    • 您不能在同一个列表中存储两种不同的类型。虽然这并不完全正确,但当你开始这样思考时,通常意味着你走错了路。顺便说一句 - 如果您发现我写的任何内容对您有帮助,那么点赞是适当的并且值得赞赏。
    • 我已将数据结构更改为不再使用Maybe Procedure,并且不再需要包含两种类型的列表。我已经更新了我的问题,因为由于类型不匹配,我仍然遇到了一些错误。我一直在跟踪生产类型,但没有太大帮助。
    • 使用更新的解析器时,我收到错误 unknown identifier 'KW_VAR' 以及其他一些错误。
    • 是的对不起,我现在更新了解析器并添加了更新的词法分析器
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-01
    • 2021-09-15
    • 1970-01-01
    • 1970-01-01
    • 2014-03-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多