我正在为类似于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
规则的第二个元素引起的,但我不明白为什么。
答案 0 :(得分:1)
这里有两点需要注意。
myParse
的顶级生产。您的第一个生产规则是ProcedureDeclaration
,所以它将尝试解析。您可能希望将DeclarationSequence
作为第一条规则。
您的作品的返回类型是IO动作,而在Haskell中,IO动作是值。它们在成为main
的一部分之前不会被“执行”。这意味着你需要编写这样的作品:
DeclarationSequence : ProcedureDeclaration
{ do $1; putStrLn("G") }
ProcedureDeclaration : ProcedureHeading ';' ProcedureBody identifier
{ do $1; $3; putStrLn("C") }
也就是说,DeclarationSequence
规则的返回值是ProcedureDeclaration
后跟putStrLn "G"
返回的IO操作。
ProducedureDeclaration
规则的返回值是ProcudureHeading
返回的操作,后跟ProcedureBody
后跟putStrLn "C"
返回的操作。
您还可以使用>>
运算符编写规则的RHS:
{ $1 >> putStrLn "G" }
{ $1 >> $3 >> putStrLn "C" }
请注意,您必须决定对操作进行排序的顺序 - 即前/后/后。
答案 1 :(得分:0)
好像你的表达式已被解析好了。检查myParse
的返回类型,我猜它将是IO ()
,实际操作将是putStrLn("D")
- 是您在ProcedureDeclaration
中写的内容。接下来,在do块中对myParse
的put调用,它将被解释为print .. >> myParse (..) >> putStrLn ..
或仅链接monadic动作。 myParse
将返回一个将打印“D”的动作,因此输出正是人们所期望的。
您在ProcedureBody
和DeclarationSequence
中定义了其他操作。但是你从不以任何方式使用这些动作,就像你会写:
do
let a = putStrLn "E"
putStrLn("C")
哪个输出“C”,a
无论如何都不会被使用。与您的解析器相同。如果要调用这些操作,请尝试在$1 >> putStrLn("C") >> $2
相关代码中编写ProcedureDeclaration
。