Parsing Haskell中的AST和语法分析

Parsing Haskell中的AST和语法分析,parsing,haskell,abstract-syntax-tree,Parsing,Haskell,Abstract Syntax Tree,我有一个作业,我不知道如何定义答案 任务 编写函数exp::[String]->(AST[String]) AST: 如果x是一个数字,它应该说number x 如果它是一个“+”oga“-”,它应该说atomx 如果它读取一个“(”,那么“(“直到它出现a”)”后面的所有内容都应该是一个列表[AST] 因此,输出将: exp (token "(hi (4) 32)") > (List [Atom "hi", List [Number 4], Number 32], []) exp

我有一个作业,我不知道如何定义答案

任务 编写函数
exp::[String]->(AST[String])

AST

  • 如果
    x
    是一个数字,它应该说
    number x
  • 如果它是一个“+”oga“-”,它应该说
    atomx
  • 如果它读取一个“(”,那么“(“直到它出现a”)”后面的所有内容都应该是一个列表
    [AST]
因此,输出将:

exp (token "(hi (4) 32)")
> (List [Atom "hi", List [Number 4], Number 32], [])

exp (token "(+ 3 42 654 2)") 
> (List [Atom "+", Number 3, Number 42, Number 654, Number 2], [])

exp (token "(+ 21 444) junk") 
> (List [Atom "+", Number 21, Number 444], ["junk"])
到目前为止我所拥有的 我已经有了token函数,
token::String->[String]
,它可以生成一个列表

例子:
exp
函数如下所示:

exp :: [String] -> (AST, [String])
exp [] = error "Empty list"
exp (x:xs)  | x == ")"      = error ""
            | x == "("      = let (e, ss') = exp xs in (List [getAst xs], ss')
            | x == "+"      = let (e, ss') = exp xs in (Atom (read x), ss')
            | x == "-"      = let (e, ss') = exp xs in (Atom (read x), ss')
            | otherwise     = exp xs`
其中
getAst
函数:

getAst :: [String] -> AST
getAst [] = error ""
getAst (x:xs)
            | x == ")"  = error ""
            | x == "("  = (List [getAst xs])
            | isAtom x  = (Atom x) 
            | isNum x   = (Number (read x))
            | otherwise = getAst xs`

(是的,我是Haskell的初学者。)

我想我可以帮你一点忙

问题的表现方式应该是,只要看下一个问题,就可以做到这一点 输入/标记并从那里决定去哪里

一些假设 数据表示为
[String]->(Ast[String])
的方式我假设它是一个公共解析器,其中 解析器尝试读取输入的某些部分,并将解析/转换后的输出与未转换的其余输入一起返回(因此仅返回元组的两个部分--
Ast
和输入的其余部分)

AST类型 由于您没有将其包括在内,我认为:

data Ast
  = Number Int
  | Atom String
  | List [Ast]
  deriving Show
一些我需要的东西 我需要一些东西:

import Prelude hiding (exp)

import Control.Applicative ((<$>))
import Data.Maybe (fromJust, isJust)
我想要一些帮手,也许可以帮我:

  • isJust
    检查
    Just\uu
  • fromJust
    Just a
最后,我需要这个helper函数来
阅读
更加安全:

tryRead :: (Read a) => String -> Maybe a
tryRead input =
  case readsPrec 0 input of
    (a,_):_ -> Just a
    _       -> Nothing
这将尝试在此处读取一个数字-如果n是一个数字,则返回
仅n
,否则返回

先试 以下是一个未完成的问题,请先解决您的问题:

exp :: [String] -> (Ast, [String])
exp (lookat:rest)
  | isJust number = (fromJust number, rest)
  | lookat == "("  = parseList rest []
  where number = Number <$> tryRead lookat

parseList :: [String] -> [Ast] -> (Ast, [String])
parseList inp@(lookat:rest) acc
  | lookat == ")" = (List (reverse acc), rest)
  | otherwise    = let (el, rest') = exp inp
                   in parseList rest' (el:acc)
待办事项 还有一些边界案例需要您决定(如果没有输入令牌怎么办?)

当然,为了完成这个练习,您必须添加
Atom
s-的案例

全解 好的-3小时后,OP没有再次签入,所以我想我可以发布一个完整的解决方案。 我希望我没有忘记任何边缘案例,而这个sureley并不是最有效的实现(
tokens
),但OP给出的示例都是匹配的:

module Ast where

import Prelude hiding (exp)

import Control.Applicative ((<$>))
import Data.Char (isSpace, isControl)
import Data.Maybe (fromJust, isJust)

data Ast
  = Number Int
  | Atom String
  | List [Ast]
  | Empty
  deriving Show

type Token = String

main :: IO ()
main = do
  print $ parse "(hi (4) 32)"
  print $ parse "(+ 3 42 654 2)"
  print $ parseAst . tokens $ "(+ 21 444) junk"

parse :: String -> Ast
parse = fst . parseAst . tokens

parseAst :: [Token] -> (Ast, [Token])
parseAst [] = (Empty, [])
parseAst (lookat:rest)
  | isJust number = (fromJust number, rest)
  | lookat == "("  = parseList rest []
  | otherwise     = (Atom lookat, rest)
  where number = Number <$> tryRead lookat

parseList :: [Token] -> [Ast] -> (Ast, [Token])
parseList [] _ = error "Syntax error: `)` not found"
parseList inp@(lookat:rest) acc
  | lookat == ")" = (List (reverse acc), rest)
  | otherwise    = let (el, rest') = parseAst inp
                   in parseList rest' (el:acc)
tokens :: String -> [Token]
tokens = split ""
  where split tok "" = add tok []
        split tok (c:cs)
          | c == '(' || c == ')' = add tok $ [c] : split "" cs
          | isSpace c || isControl c = add tok $ split "" cs
          | otherwise = split (tok ++ [c]) cs
        add "" tks = tks
        add t tks =  t : tks

tryRead :: (Read a) => Token -> Maybe a
tryRead input =
  case readsPrec 0 input of
    (a,_):_ -> Just a
    _       -> Nothing

令牌函数做得很好。你对
exp
函数有一个开始,或者知道如何处理它吗?
exp::[String]->(AST[String])exp[]=error“Empty list”exp(x:xs)| x==”=error“”| x==”(“=let(e,ss')=exp xs in(LList[getAst xs],ss'))|x==”=let(e,ss')=exp xs in)(读取x),ss')| x==“-”=let(e,ss')=exp-xs-in(Atom(read x),ss')|否则=exp-xs
这是我到目前为止使用的代码,有一个名为getAst的帮助方法,我在其中尝试了[String]->AST,但未成功。无法读取。请将其发布到您的问题中。抱歉,以前未使用Stackoverflow。请立即编辑我的问题。@notgoodinhaskell能否发布您的
令牌
函数,以便我稍后将其包含在最终解决方案中?而不是
| isJust number=(fromJust number,rest)
,你应该使用
| Just x@user2407038我认为这取决于-虽然模式匹配很好,但我认为
只是数字
在这里读得相当好,但如果你想为你更改它
exp :: [String] -> (Ast, [String])
exp (lookat:rest)
  | isJust number = (fromJust number, rest)
  | lookat == "("  = parseList rest []
  where number = Number <$> tryRead lookat

parseList :: [String] -> [Ast] -> (Ast, [String])
parseList inp@(lookat:rest) acc
  | lookat == ")" = (List (reverse acc), rest)
  | otherwise    = let (el, rest') = exp inp
                   in parseList rest' (el:acc)
λ> let input = ["(", "2", "(", "3", "4", ")", "5", ")"]

λ> exp input
(List [Number 2,List [Number 3,Number 4],Number 5],[])
module Ast where

import Prelude hiding (exp)

import Control.Applicative ((<$>))
import Data.Char (isSpace, isControl)
import Data.Maybe (fromJust, isJust)

data Ast
  = Number Int
  | Atom String
  | List [Ast]
  | Empty
  deriving Show

type Token = String

main :: IO ()
main = do
  print $ parse "(hi (4) 32)"
  print $ parse "(+ 3 42 654 2)"
  print $ parseAst . tokens $ "(+ 21 444) junk"

parse :: String -> Ast
parse = fst . parseAst . tokens

parseAst :: [Token] -> (Ast, [Token])
parseAst [] = (Empty, [])
parseAst (lookat:rest)
  | isJust number = (fromJust number, rest)
  | lookat == "("  = parseList rest []
  | otherwise     = (Atom lookat, rest)
  where number = Number <$> tryRead lookat

parseList :: [Token] -> [Ast] -> (Ast, [Token])
parseList [] _ = error "Syntax error: `)` not found"
parseList inp@(lookat:rest) acc
  | lookat == ")" = (List (reverse acc), rest)
  | otherwise    = let (el, rest') = parseAst inp
                   in parseList rest' (el:acc)
tokens :: String -> [Token]
tokens = split ""
  where split tok "" = add tok []
        split tok (c:cs)
          | c == '(' || c == ')' = add tok $ [c] : split "" cs
          | isSpace c || isControl c = add tok $ split "" cs
          | otherwise = split (tok ++ [c]) cs
        add "" tks = tks
        add t tks =  t : tks

tryRead :: (Read a) => Token -> Maybe a
tryRead input =
  case readsPrec 0 input of
    (a,_):_ -> Just a
    _       -> Nothing
λ> :main
List [Atom "hi",List [Number 4],Number 32]
List [Atom "+",Number 3,Number 42,Number 654,Number 2]
(List [Atom "+",Number 21,Number 444],["junk"])