对输入字符串的lexeme进行分类

时间:2013-11-28 20:01:43

标签: haskell

如果给出一个字符串“3 + a * 6”,我如何逐个确定词汇?我知道我的代码缺少classify xs部分,但我不知道在哪里放。谁能帮我这个? (该语言在Haskell中)

classify :: String -> String
classify (x:xs)
        |x == '+' = "PLUS"
        |x == '-' = "MINUS"
        |x == '*' = "MULT"
        |x == '/' = "DIV"
        |x == '(' = "LP"
        |x == ')' = "RP"
        |isAlpha x = "VAR"
        |isDigit x = "CONST"
        |otherwise = error "Cannot determine lexeme"

3 个答案:

答案 0 :(得分:1)

这种标记化最好留给词法分析器生成器或解析器组合器。您可以在http://www.haskell.org/alex/尝试Alex,或在http://www.haskell.org/haskellwiki/Parsec尝试Parsec。

这些工具专门用于使标记化/扫描(在Parsec的情况下解析)易于使用。

答案 1 :(得分:0)

如果你真的只需要一个标记器,那么你可以在没有parsec的情况下完成它。我为令牌类型定义了一个额外的ADT(您当然可以将其转换回字符串),并且必须更改返回类型,因为您获得了一系列令牌。

type Error = String
data Token = Plus | Minus | Mult | Div | Lp | Rp 
           | Var | Const | Whitespace deriving (Show, Eq)

tokenTable = [('+', Plus), ('-', Minus), ('*', Mult), ('/', Div), ('(', Lp), (')', Rp)]

tokenize :: String -> Either Error [Token]
tokenize "" = Right []
tokenize (x:xs) = case lookup x tokenTable of
  Just t -> fmap (t:) (tokenize xs)
  Nothing -> recognize x where
    recognize x 
      | isAlpha x = fmap (Var:) (tokenize xs)
      | isDigit x = fmap (Const:) (tokenize xs)
      | isSeparator x = fmap (Whitespace:) (tokenize xs)
      | otherwise = Left "Cannot determine lexeme"

然而,这很快变得乏味。它已经是某种程度上,因为我们必须使用Either将列表提升到fmap。想象一下如何实现指示错误的位置?进一步必要的是实现monad堆栈并重新实现像Parsec这样的解析器组合器。这就是为什么它经常被建议直接使用组合器库,并让它做lexing。

如果您不能或不想使用完整的Parsec,那么自己实施basic functionality并不困难。

答案 2 :(得分:0)

一般情况下,您不需要解析空格。以下是您和phg解决方案的组合:

import Data.Char

data Token = Plus | Minus | Mult | Div | Lp | Rp | Var | Digit | Undefined
  deriving Show

tokenMap :: String -> Token
tokenMap "+" = Plus
tokenMap "-" = Minus
tokenMap "*" = Mult
tokenMap "/" = Div
tokenMap "(" = Lp
tokenMap ")" = Rp
tokenMap [c]
  | isAlpha c = Var
  | isDigit c = Digit
tokenMap _ = Undefined

classify :: String -> [Token]
classify = map tokenMap . words