0

我正在为类似于 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规则的第二个元素引起的,但我不明白为什么。

4

2 回答 2

1

这里有两点需要注意。

  1. happy 使用第一个产生式规则作为 的顶级产生式myParse

您的第一个生产规则是ProcedureDeclaration,所以这就是它要尝试解析的全部内容。您可能想要制定DeclarationSequence第一条规则。

  1. 您的产品的返回类型是 IO-actions,而在 Haskell 中,IO-actions 是值。在成为main. 这意味着您需要像这样编写您的作品:

    DeclarationSequence : ProcedureDeclaration
        { do $1; putStrLn("G") }
    ProcedureDeclaration : ProcedureHeading ';' ProcedureBody identifier
        { do $1; $3; putStrLn("C") }
    

DeclarationSequence规则的返回值是返回的 IO-actionProcedureDeclaration后跟putStrLn "G".

并且ProducedureDeclaration规则的返回值是后面跟着ProcudureHeading后面的动作返回的动作。ProcedureBodyputStrLn "C"

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

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

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

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

于 2016-05-04T16:00:30.233 回答
0

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

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

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

将输出“C”,a不以任何方式使用。与您的解析器相同。如果要调用这些操作,请尝试编写$1 >> putStrLn("C") >> $2相关ProcedureDeclaration代码。

于 2016-05-04T15:22:01.650 回答