如果给出一个字符串“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"
答案 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