Parsing Haskell解析器到AST数据类型,赋值
我在互联网上搜索了几天,试图找到我问题的答案,最后我承认失败了。Parsing Haskell解析器到AST数据类型,赋值,parsing,haskell,abstract-syntax-tree,Parsing,Haskell,Abstract Syntax Tree,我在互联网上搜索了几天,试图找到我问题的答案,最后我承认失败了。 我得到了一个语法: Dig::=0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 Int::=Dig | Dig Int 变量::=a | b |。。。z | A | B | C |……|Z Expr::=Int |-Expr |+Expr Expr |*Expr Expr | Var |让Var=Expr在Expr中 我被告知使用这种语法解析、计算和打印表达式 其中运算符*+-具有其正常含义 具体任务是
我得到了一个语法:
Dig::=0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
Int::=Dig | Dig Int
变量::=a | b |。。。z | A | B | C |……|Z
Expr::=Int |-Expr |+Expr Expr |*Expr Expr | Var |让Var=Expr在Expr中
我被告知使用这种语法解析、计算和打印表达式其中运算符
*+-
具有其正常含义具体任务是编写一个函数
parse::String->AST
它接受一个字符串作为输入,并在输入格式正确时返回一个抽象语法树(我可以估计它是正确的)
我被告知,我可能需要一个合适的数据类型,该数据类型可能需要从其他一些类派生
遵循示例输出data AST=Leaf Int | Sum AST | Min AST |……
更进一步,我应该考虑写一个函数BR>
tokens::String->[String]
将输入字符串拆分为标记列表
应使用
ast::[String]->(ast[String])
其中输入是一个令牌列表,它输出一个AST,为了解析子表达式,我应该递归地使用AST函数 我还应该制作一个printExpr方法来打印结果,以便
printE:AST->String
printE(解析“*5”)
生成“5*5”
或“(5*5)”
也是一个计算表达式的函数
evali::AST->Int
我只想被指在正确的方向,我可能会开始。一般来说,我对Haskell和FP知之甚少,为了解决这个问题,我用Java编写了一些字符串处理函数,这让我意识到我偏离了正轨。因此,一个指向正确方向的小指针,也许可以解释一下AST应该是什么样子的
连续第三天仍然没有运行代码,我真的很感谢任何帮助我找到解决方案的尝试! 提前谢谢
编辑 我可能不清楚:
我想知道我应该如何从阅读输入字符串并对其进行标记到制作AST。我建议从Haskell本身开始,这是一本写得很好、很有趣的指南。这是另一个经常被推荐的起点 编辑:解析的一个更基本的介绍是。我想知道如何用菲利普·瓦德勒的一系列成功来取代失败。遗憾的是,它似乎无法在网上获得 要开始Haskell中的解析,我认为您应该首先阅读,然后可能,但也应该阅读 你的语法
Dig ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
Int ::= Dig | Dig Int
Var ::= a | b | ... z | A | B | C | ... | Z
Expr ::= Int | - Expr | + Expr Expr | * Expr Expr | Var | let Var = Expr in Expr
Dig ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
Int ::= Dig | Dig Int
Var ::= a | b | ... z | A | B | C | ... | Z
Expr ::= Int | - Expr | + Expr Expr | * Expr Expr | Var | let Var = Expr in Expr
建议几种抽象数据类型:
data Dig = Dig_0 | Dig_1 | Dig_2 | Dig_3 | Dig_4 | Dig_5 | Dig_6 | Dig_7 | Dig_8 | Dig_9
data Integ = I_Dig Dig | I_DigInt Dig Integ
data Var = Var_a | Var_b | ... Var_z | Var_A | Var_B | Var_C | ... | Var_Z
data Expr = Expr_I Integ
| Expr_Neg Expr
| Expr_Plus Expr Expr
| Expr_Times Expr Expr Var
| Expr_Var Var
| Expr_let Var Expr Expr
这本质上是一个递归定义的语法树,不需要再创建一个。
对不起,那些笨重的Dig_
和Integ_
东西-它们必须以大写字母开头
(就我个人而言,我想立即将Integ
s转换为Int
s,因此我会做newtype Integ=Integ Int
,也可能会做newtype Var=Var Char
,但这可能不适合您。)
一旦你完成了基本的操作-dig
和var
,和neg
,plus
,中的将使用应用程序界面来构建它们,例如expr
的解析器
expr = Expr_I <$> integ
<|> Expr_Neg <$> neg_ *> expr
<|> Expr_Plus <$> plus_ *> expr <*> expr
<|> Expr_Times <$> times_ *> expr <*> expr
<|> Expr_Var <$> var
<|> Expr_let <$> let_ *> var <*> equals_ *> expr <*> in_ *> expr
evali :: AST -> Int
evali (Leaf x ) = x
evali (Sum x y) = (evali x) + (evali y)
evali (Minus x ) = 0 - (evali x)
...
expr=expr\u I整数
Expr_Neg Neg*>Expr
Expr\u Plus\u*>Expr Expr
Expr_次次*>Expr Expr
Expr_Var
Expr\u let\u*>var等于\u*>Expr in\u*>Expr
因此,几乎所有的时候,Haskell代码都是干净的,并且非常类似于给定的语法。好吧,看来你正在尝试构建很多东西,但你并不确定这些东西到底要去哪里。我建议正确定义AST
,然后尝试实现evali
将是一个良好的开端
你列出的语法很有趣。。。您似乎想输入*5 5
,但输出5*5
,这是一个奇怪的选择。这真的应该是一元负数,而不是二元负数吗?同样地,*Expr Expr Var
看起来可能是您想要键入*Expr Expr|Var
不管怎样,假设一下你想说什么,你的AST会是这样的:
data AST = Leaf Int | Sum AST AST | Minus AST | Var String | Let String AST AST
现在,让我们尝试执行printE
。它接受AST并给我们一个字符串。根据上面的定义,AST必须是五种可能的事物之一。你只需要弄清楚每一张要打印什么
printE :: AST -> String
printE (Leaf x ) = show x
printE (Sum x y) = printE x ++ " + " ++ printE y
printE (Minus x ) = "-" ++ printE x
...
show
将Int
转换为字符串
<代码>++
将两个字符串连接在一起。我会让你算出剩下的函数。(棘手的是,如果你想让它打印括号来正确显示子表达式的顺序……因为你的语法没有提到括号,我想没有。)
现在,evali怎么样?嗯,这将是一个类似的交易。如果AST是一个Leaf x
,那么x
是一个Int
,您只需返回它即可。如果你有,比如说,减去x
,那么x
不是一个整数,它是一个AST,所以你需要用evali
将它转换成一个整数。函数看起来像
expr = Expr_I <$> integ
<|> Expr_Neg <$> neg_ *> expr
<|> Expr_Plus <$> plus_ *> expr <*> expr
<|> Expr_Times <$> times_ *> expr <*> expr
<|> Expr_Var <$> var
<|> Expr_let <$> let_ *> var <*> equals_ *> expr <*> in_ *> expr
evali :: AST -> Int
evali (Leaf x ) = x
evali (Sum x y) = (evali x) + (evali y)
evali (Minus x ) = 0 - (evali x)
...
到目前为止效果很好。但是等等!看起来您应该能够使用Let
定义新变量,并在以后使用Var
引用它们。那么,在这种情况下,您需要将这些变量存储在某个地方。这将使函数更加复杂
我的建议是
tokenised_input' = ["-","6","+","4","5","let","x","=","-","5","in","*","x","x"]
tokenised_input = ["-","6","+","45","let","x","=","-","5","in","*","x","x"]
data AST = Leaf Int | Sum AST AST | Min AST | ...
data Expr = E_Int Int | E_Neg Expr | E_Sum Expr Expr | E_Prod Expr Expr | E_Var Char
| E_Let {letvar::Char,letequal:: Expr,letin::Expr}
deriving Show
expr :: [String] -> (Expr,[String])
expr [] = error "unexpected end of input"
expr (s:ss) | all isDigit s = (E_Int (read s),ss)
| s == "-" = let (e,ss') = expr ss in (E_Neg e,ss')
| s == "+" = (E_Sum e e',ss'') where
(e,ss') = expr ss
(e',ss'') = expr ss'
-- more cases
pmap :: (a -> b) -> ([String] -> (a,[String])) -> ([String] -> (b,[String]))
type Parser a = [String] -> (a,[String])
pmap :: (a -> b) -> Parser a -> Parser b
pmap f p = \ss -> let (a,ss') = p ss
in (f a,ss')
data Parser a = Par [String] -> (a,[String])
instance Functor Parser where
fmap f (Par p) = Par (pmap f p)
liftP2 :: (a -> b -> c) -> Parser a -> Parser b -> Parser c
liftP2 f p1 p2 = \ss0 -> let
(a,ss1) = p1 ss0
(b,ss2) = p2 ss1
in (f a b,ss2)
liftP3 :: (a -> b -> c -> d) -> Parser a -> Parser b -> Parser c -> Parser d
equals_ :: Parser ()
equals_ [] = error "equals_: expected = but got end of input"
equals_ ("=":ss) = ((),ss)
equals_ (s:ss) = error $ "equals_: expected = but got "++s
expr :: [String] -> (Expr,[String])
expr [] = error "unexpected end of input"
expr (s:ss) | all isDigit s = (E_Int (read s),ss)
| s == "-" = pmap E_Neg expr ss
| s == "+" = liftP2 E_Sum expr expr ss
-- more cases
data Parser a = Par [String] -> (a,[String])
expr (s:ss) | s == "let" = E_Let <$> var *> equals_ <*> expr <*> in_ *> expr