Haskell Happy解析器不会更进一步

时间:2016-05-04 14:42:16

标签: parsing haskell happy

我正在为类似于Oberon的语言实现解析器。 我已经使用Alex成功编写了词法分析器,因为我可以看到词法分析器返回的标记列表是正确的。

当我将令牌列表提供给解析器时,它会在第一个令牌处停止。

这是我的解析器:

...

%name myParse
%error { parseError }

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

%%

ProcedureDeclaration    :   ProcedureHeading ';' ProcedureBody identifier     { putStrLn("C") }

ProcedureHeading        :   KW_PROCEDURE identifier { putStrLn("D") }

ProcedureBody           :   KW_END                      { putStrLn("E") }   
                        | DeclarationSequence KW_END    { putStrLn("F") }

DeclarationSequence     :   ProcedureDeclaration        { putStrLn("G") }

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

main = do
  inStr <- getContents
  print (alexScanTokens inStr)
  myParse (alexScanTokens inStr)
  putStrLn("DONE")
}

这是我给解析器的测试代码:

PROCEDURE proc;
END proc

这是词法分析器返回的令牌列表:

[KW_TokenProcedure,TokenVariableIdentifier "proc",KW_TokenSemiColon,KW_TokenEnd,TokenVariableIdentifier "proc"]

解析器不会给出任何错误,但它符合我的ProcedureDeclaration规则,只打印C。

这是输出的样子:

C
DONE

知道为什么吗?

更新:

我向前迈出了第一步,我能够解析之前给出的测试输入。现在我改变了我的解析器以识别同一级别上的多个过程的声明。为此,这就是我的新解析的样子:

...

%name myParse
%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       { addProcedureToProcedure $1 $3 }

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 = myParse (alexScanTokens inStr)
  putStrLn ("result: " ++ show(result))
}

问题是,它无法编译给我这个错误:

Occurs check: cannot construct the infinite type: t5 ~ [t5]
    Expected type: HappyAbsSyn t5 t5 t6 t7 t8 t9
                   -> HappyAbsSyn t5 t5 t6 t7 t8 t9
                   -> HappyAbsSyn t5 t5 t6 t7 t8 t9
                   -> HappyAbsSyn t5 t5 t6 t7 t8 t9
      Actual type: HappyAbsSyn t5 t5 t6 t7 t8 t9
                   -> HappyAbsSyn t5 t5 t6 t7 t8 t9
                   -> HappyAbsSyn t5 t5 t6 t7 t8 t9
                   -> HappyAbsSyn [t5] t5 t6 t7 t8 t9
    ...

我确信这是由ProcedureDeclarationsList规则的第二个元素引起的,但我不明白为什么。

2 个答案:

答案 0 :(得分:1)

这里有两点需要注意。

  1. happy使用第一个生产规则作为myParse的顶级生产。
  2. 您的第一个生产规则是ProcedureDeclaration,所以它将尝试解析。您可能希望将DeclarationSequence作为第一条规则。

    1. 您的作品的返回类型是IO动作,而在Haskell中,IO动作是值。它们在成为main的一部分之前不会被“执行”。这意味着你需要编写这样的作品:

      DeclarationSequence : ProcedureDeclaration
          { do $1; putStrLn("G") }
      ProcedureDeclaration : ProcedureHeading ';' ProcedureBody identifier
          { do $1; $3; putStrLn("C") }
      
    2. 也就是说,DeclarationSequence规则的返回值是ProcedureDeclaration后跟putStrLn "G"返回的IO操作。

      ProducedureDeclaration规则的返回值是ProcudureHeading返回的操作,后跟ProcedureBody后跟putStrLn "C"返回的操作。

      您还可以使用>>运算符编写规则的RHS:

      { $1 >> putStrLn "G" }
      { $1 >> $3 >> putStrLn "C" }
      

      请注意,您必须决定对操作进行排序的顺序 - 即前/后/后。

      工作示例:http://lpaste.net/162432

答案 1 :(得分:0)

好像你的表达式已被解析好了。检查myParse的返回类型,我猜它将是IO (),实际操作将是putStrLn("D") - 是您在ProcedureDeclaration中写的内容。接下来,在do块中对myParse的put调用,它将被解释为print .. >> myParse (..) >> putStrLn ..或仅链接monadic动作。 myParse将返回一个将打印“D”的动作,因此输出正是人们所期望的。

您在ProcedureBodyDeclarationSequence中定义了其他操作。但是你从不以任何方式使用这些动作,就像你会写:

    do
      let a = putStrLn "E"
      putStrLn("C")

哪个输出“C”,a无论如何都不会被使用。与您的解析器相同。如果要调用这些操作,请尝试在$1 >> putStrLn("C") >> $2相关代码中编写ProcedureDeclaration