Haskell 词法分析器输入和返回的标记列表或错误
Haskell lexer input and returning list of tokens or error
您好,我正在处理词法分析器接受输入字符串和 return 标记列表或错误的问题。如果有人知道如何解决问题,请提供帮助。谢谢:)
lexer :: String -> Either Error [Token]
我在不使用任何一个的情况下工作时遇到了问题,但无法弄清楚 Haskell 的任何一个功能。我想向左给出一个错误,向右制作带有模式匹配的标记列表和多个条件。
这是不起作用的代码:
lexar (x:xs)
| isSpace x = Main.lexar xs
| isDigit x = Right (Literal (show x): Main.lexar xs)
| x == '+' = Right (Plus : Main.lexar xs)
| x == '-' = Right (Minus : Main.lexar xs)
| x == '*' = Right (Mult : Main.lexar xs)
| x == '/' = Right (Div : Main.lexar xs)
| x == '(' = Right (LeftP : Main.lexar xs)
| x == ')' = Right (RightP : Main.lexar xs)
| otherwise = Left (error "fail")
首先,不能将:
运算符与lexar
的return值一起使用。
此版本的 lexar
return 是一个 Either
, 不是 列表,因此不能作为运算符 :
.
每次你递归调用lexar
,它return给你一个Either
,它可能是Left
或Right
。如果它是 Left
,它包含错误,您只需将它向上传递到堆栈。如果它是 Right
,那么它包含下游词法分析的结果,您可以将当前标记添加到它前面,然后在 returning 之前再次将其包装在 Right
中。让我们记下来:
| isDigit x =
case lexar xs of
Left err -> Left err
Right tokens -> Right (Literal (show x) : tokens)
当然,如果您必须为每个标记编写一个完整的 case
表达式,这将变得非常乏味。因此,您可以将其包装在辅助函数中:
lexar (x:xs)
...
| isDigit x = prependToken (Literal (show x))
| x == '+' = prependToken Plus
...
where
prependToken t =
case lexar xs of
Left err -> Left err
Right tokens -> Right (t : tokens)
如果您对更抽象的概念足够满意,您还可以通过使用运算符 <$>
:
来利用 Either
的 Functor
实例
lexar (x:xs)
...
| isDigit x = (Literal (show x) :) <$> lexar xs
| x == '+' = (Plus :) <$> lexar xs
...
Either
的运算符 <$>
的标准库实现将给定函数应用于 Right
值或 return Left
值不变。本质上,这正是我的辅助函数 prependToken
正在做的事情。
最后,当 return 调用 Left
时,不要调用 error
函数。这个函数会在计算结果时使程序崩溃,这不是你想要的。只需将字符串包装在 Left
:
| otherwise = Left "fail"
您好,我正在处理词法分析器接受输入字符串和 return 标记列表或错误的问题。如果有人知道如何解决问题,请提供帮助。谢谢:)
lexer :: String -> Either Error [Token]
我在不使用任何一个的情况下工作时遇到了问题,但无法弄清楚 Haskell 的任何一个功能。我想向左给出一个错误,向右制作带有模式匹配的标记列表和多个条件。
这是不起作用的代码:
lexar (x:xs)
| isSpace x = Main.lexar xs
| isDigit x = Right (Literal (show x): Main.lexar xs)
| x == '+' = Right (Plus : Main.lexar xs)
| x == '-' = Right (Minus : Main.lexar xs)
| x == '*' = Right (Mult : Main.lexar xs)
| x == '/' = Right (Div : Main.lexar xs)
| x == '(' = Right (LeftP : Main.lexar xs)
| x == ')' = Right (RightP : Main.lexar xs)
| otherwise = Left (error "fail")
首先,不能将:
运算符与lexar
的return值一起使用。
此版本的 lexar
return 是一个 Either
, 不是 列表,因此不能作为运算符 :
.
每次你递归调用lexar
,它return给你一个Either
,它可能是Left
或Right
。如果它是 Left
,它包含错误,您只需将它向上传递到堆栈。如果它是 Right
,那么它包含下游词法分析的结果,您可以将当前标记添加到它前面,然后在 returning 之前再次将其包装在 Right
中。让我们记下来:
| isDigit x =
case lexar xs of
Left err -> Left err
Right tokens -> Right (Literal (show x) : tokens)
当然,如果您必须为每个标记编写一个完整的 case
表达式,这将变得非常乏味。因此,您可以将其包装在辅助函数中:
lexar (x:xs)
...
| isDigit x = prependToken (Literal (show x))
| x == '+' = prependToken Plus
...
where
prependToken t =
case lexar xs of
Left err -> Left err
Right tokens -> Right (t : tokens)
如果您对更抽象的概念足够满意,您还可以通过使用运算符 <$>
:
Either
的 Functor
实例
lexar (x:xs)
...
| isDigit x = (Literal (show x) :) <$> lexar xs
| x == '+' = (Plus :) <$> lexar xs
...
Either
的运算符 <$>
的标准库实现将给定函数应用于 Right
值或 return Left
值不变。本质上,这正是我的辅助函数 prependToken
正在做的事情。
最后,当 return 调用 Left
时,不要调用 error
函数。这个函数会在计算结果时使程序崩溃,这不是你想要的。只需将字符串包装在 Left
:
| otherwise = Left "fail"