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