开心:这不是关键字
Happy: This is not a keyword
当你写一个快乐的描述时,你必须定义所有可能出现的标记类型。但是您只能匹配标记 types,而不是单个标记 values...
这有点问题。例如,考虑 data
关键字。根据 Haskell 报告,此令牌是 "reservedid"。所以我的 tokeniser 识别它并将其标记为这样。但是,请考虑 as
关键字。现在发现这是不是一个reservedid;这是一个普通的varid。它只在一种情况下是特殊的。您完全可以声明一个名为 as
的普通变量,这很好。
所以这里有一个问题:如何具体解析 as
?
起初我并没有真正考虑过。我刚刚定义了一个新的令牌类型,它代表文本恰好是 as
.
的任何可变令牌
...然后我花了大约 2 个小时来弄清楚为什么 我的语法实际上不起作用。是的,事实证明,由于此令牌类型与现有令牌类型重叠,因此声明顺序很重要。 (!!!)从字面上看,改变声明的顺序使语法解析完美。
但现在我很担心。我担心 as
永远不会作为 varid 匹配,只会匹配它自己。所以所有说 varid 的语法规则都会拒绝 as
标记——这是完全错误的!
解决此问题的正确方法是什么?
GHC 在其 Parser.y
中所做的是定义一个非终结符类型 special_id
,它列出了许多特殊的非关键字,例如 as
,然后定义 tyvarid
和 varid
(非终端)标记将其作为终端 VARID
之外的一个选项(以及其他一些,尽管在我看来他们中的大多数人认为他们 应该 也被放入 special_id
)。
节选:
varid :: { Located RdrName }
: VARID { sL1 $! mkUnqual varName (getVARID ) }
| special_id { sL1 $! mkUnqual varName (unLoc ) }
| 'unsafe' { sL1 $! mkUnqual varName (fsLit "unsafe") }
...
special_id :: { Located FastString }
special_id
: 'as' { sL1 (fsLit "as") }
| 'qualified' { sL1 (fsLit "qualified") }
| 'hiding' { sL1 (fsLit "hiding") }
| 'export' { sL1 (fsLit "export") }
...
当你写一个快乐的描述时,你必须定义所有可能出现的标记类型。但是您只能匹配标记 types,而不是单个标记 values...
这有点问题。例如,考虑 data
关键字。根据 Haskell 报告,此令牌是 "reservedid"。所以我的 tokeniser 识别它并将其标记为这样。但是,请考虑 as
关键字。现在发现这是不是一个reservedid;这是一个普通的varid。它只在一种情况下是特殊的。您完全可以声明一个名为 as
的普通变量,这很好。
所以这里有一个问题:如何具体解析 as
?
起初我并没有真正考虑过。我刚刚定义了一个新的令牌类型,它代表文本恰好是 as
.
...然后我花了大约 2 个小时来弄清楚为什么 我的语法实际上不起作用。是的,事实证明,由于此令牌类型与现有令牌类型重叠,因此声明顺序很重要。 (!!!)从字面上看,改变声明的顺序使语法解析完美。
但现在我很担心。我担心 as
永远不会作为 varid 匹配,只会匹配它自己。所以所有说 varid 的语法规则都会拒绝 as
标记——这是完全错误的!
解决此问题的正确方法是什么?
GHC 在其 Parser.y
中所做的是定义一个非终结符类型 special_id
,它列出了许多特殊的非关键字,例如 as
,然后定义 tyvarid
和 varid
(非终端)标记将其作为终端 VARID
之外的一个选项(以及其他一些,尽管在我看来他们中的大多数人认为他们 应该 也被放入 special_id
)。
节选:
varid :: { Located RdrName }
: VARID { sL1 $! mkUnqual varName (getVARID ) }
| special_id { sL1 $! mkUnqual varName (unLoc ) }
| 'unsafe' { sL1 $! mkUnqual varName (fsLit "unsafe") }
...
special_id :: { Located FastString }
special_id
: 'as' { sL1 (fsLit "as") }
| 'qualified' { sL1 (fsLit "qualified") }
| 'hiding' { sL1 (fsLit "hiding") }
| 'export' { sL1 (fsLit "export") }
...