You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何处理Happy+Alex解析器中标识符与关键字的冲突问题

关键字与标识符冲突的解析器处理方案问题

示例输入输出

IN:  select TOP [1]
OUT: Select TOP (ConstInt 1)

IN:  select MIN [AVG(2,3)]
OUT: Select MIN (Function "AVG" [ConstInt 2,ConstInt 3])

IN:  select TOP [MIN(2,3)]
OUT: Parse error. Unexpected token TokAssumption "MIN"

问题原因

该语法允许MIN同时作为select后的**预设关键字(assumption)**和函数名。在最后一个示例中,MIN被词法分析器优先识别为预设关键字TokAssumption,但此时它处于表达式上下文,本应被解析为对应函数名的TokIdentifier,最终导致解析错误。

疑问与思路

最简单的解决方式是让预设关键字和函数名不重叠,但在我实际要修复的解析器中这无法实现。

我的直觉是通过解析器的monadic动作检查前瞻token:当前瞻token为(时,将当前token词法分析为TokIdentifier。这个思路是否正确?有没有更优的处理这类情况的方法?


相关代码

Parser.hs(Happy解析器代码)

{
module Parser (
  parser,
  L.runAlex
) where

import Data
import qualified Lexer as L
}

%name parser algexpr
%tokentype { L.Token }
%error { parseError }
%monad { L.Alex } { >>= } { pure }
%lexer { lexer } { L.EOF }

%token

'('       { L.TokLParen }
')'       { L.TokRParen }
'['       { L.TokLSquare }
']'       { L.TokRSquare }
','       { L.TokComma }
'select'  { L.TokSelect }
int       { L.TokConstInt $$ }
ident     { L.TokIdentifier $$ }
ass       { L.TokAssumption $$ }

%%

commaSep(p)
  : commaSep_(p) { reverse $1 }
commaSep_(p)
  : {- empty -} { [] }
  | p { [$1] }
  | commaSep_(p) ',' p { $3 : $1 }

algexpr :: { AlgExpr }
  : 'select' assumption '[' expr ']' { Select $2 $4 }

assumption :: { Assumption }
  : ass { read $1 }

expr :: { Expr }
  : int { ConstInt $1 }
  | ident '(' commaSep(expr) ')' { Function $1 $3 }

{
parseError :: L.Token -> L.Alex a
parseError tok = do
  (L.AlexPn _ line column, _, _, _) <- L.alexGetInput
  L.alexError $ "Parse error. Unexpected token " <> show tok <> " at line " <> show line <> ", column " <> show column

lexer :: (L.Token -> L.Alex a) -> L.Alex a
lexer = (=<< L.alexMonadScan)
}

Lexer.hs(Alex词法分析器代码)

{
module Lexer (
    Alex
  , AlexPosn (..)
  , alexGetInput
  , alexError
  , runAlex
  , alexMonadScan
  , Token (..)
) where

}

%wrapper "monad"

$digit = 0-9
$alpha = [a-zA-Z]
@ident = $alpha [$alpha $digit _]*

tokens :-

<0> $white+     { skip }

<0> "("       { tok TokLParen }
<0> ")"       { tok TokRParen }
<0> "["       { tok TokLSquare }
<0> "]"       { tok TokRSquare }
<0> ","       { tok TokComma }

<0> select    { tok TokSelect }

<0> MAX       { tokS TokAssumption }
<0> MIN       { tokS TokAssumption }
<0> TOP       { tokS TokAssumption }

<0> $digit+   { tokInt }
<0> @ident    { tokS TokIdentifier }

{
data Token
  = TokLParen
  | TokRParen
  | TokLSquare
  | TokRSquare
  | TokComma
  | TokAssumption String
  | TokSelect
  | TokConstInt Int
  | TokIdentifier String
  | EOF
  deriving (Eq, Show)

alexEOF :: Alex Token
alexEOF = pure EOF

tok :: Token -> AlexAction Token
tok ctor _ _ = pure ctor

tokS :: (String -> Token) -> AlexAction Token
tokS f (_, _, _, str) len = pure $ f $ take len str

tokInt :: AlexAction Token
tokInt (_, _, _, str) len = pure $ TokConstInt $ read $ take len str

}

Data.hs

module Data( AlgExpr(..), Expr(..), Assumption(..) ) where

data AlgExpr = Select Assumption Expr
             deriving (Eq,Show)

data Expr = ConstInt    Int
          | Function    String [Expr]
          deriving (Eq,Show)

data Assumption = MAX | MIN | TOP
                deriving (Eq,Show,Read)

内容的提问来源于stack exchange,提问作者swingbit

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 04:27:34