AST和Harsell中的解析

anf*_*nfo 5 parsing haskell abstract-syntax-tree

我有一个任务,我无法弄清楚如何定义答案.

分配

写功能 exp:: [String] -> (AST, [String])

AST:

  • 如果x是数字,应该说Number x.
  • 如果它是"+"og和" - "应该说Atom x.
  • 如果它读取"(",那么"("直到它出现")后面的所有内容""应该是一个列表[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"])
Run Code Online (Sandbox Code Playgroud)

到目前为止我有什么

我已经有了令牌功能,token :: String -> [String]它就是一个列表.

例:
`token "( + 2 ( + 2 3 ) )"
> ["(","+","2","(","+","2","3",")",")"]`
Run Code Online (Sandbox Code Playgroud)

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`
Run Code Online (Sandbox Code Playgroud)

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`
Run Code Online (Sandbox Code Playgroud)

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

Car*_*ten 6

我想我可以试着帮助你一点.

表示问题的方式你应该能够通过查看下一个输入/令牌并从那里决定去哪里来做到这一点.

一些假设

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

AST类型

因为你没有包括它我认为它是:

data Ast
  = Number Int
  | Atom String
  | List [Ast]
  deriving Show
Run Code Online (Sandbox Code Playgroud)

我需要的一些东西

我需要一些东西:

import Prelude hiding (exp)

import Control.Applicative ((<$>))
import Data.Maybe (fromJust, isJust)
Run Code Online (Sandbox Code Playgroud)

我必须隐藏,exp因为我们想将它用作函数名.

然后我想fmap过来,Maybe所以我包括了运营商Control.Applicative.这真的就是这个,如果您之前没有看到它:

f <$> Nothing = Nothing
f <$> Just a  = Just (f a)
Run Code Online (Sandbox Code Playgroud)

我想要一些帮助Maybe:

  • isJust 检查是否 Just _
  • fromJust得到aJust a

最后,我需要这个辅助函数read更安全一点:

tryRead :: (Read a) => String -> Maybe a
tryRead input =
  case readsPrec 0 input of
    (a,_):_ -> Just a
    _       -> Nothing
Run Code Online (Sandbox Code Playgroud)

这将尝试在这里读取一个数字 - Just n如果n是数字则返回,Nothing否则.

第一次去

这是一个未完成的第一个问题:

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)
Run Code Online (Sandbox Code Playgroud)

正如你所看到的那样,我只是基于分支lookat而略微扭曲:

如果我看到一个数字,我会返回数字和rest-token-list.如果我看到一个(我启动另一个解析器parseList.

parseList将做同样的事情: - 它查看第一个令牌 - 如果令牌是一个)它完成当前列表(它使用累加器技术)并返回.- 如果不是,它使用现有的exp解析器递归地获取列表的元素.

这是一个示例运行:

?> let input = ["(", "2", "(", "3", "4", ")", "5", ")"]

?> exp input
(List [Number 2,List [Number 3,Number 4],Number 5],[])
Run Code Online (Sandbox Code Playgroud)

去做

还有一些边界情况你必须决定(如果没有输入令牌怎么办?).

当然,你必须为Atoms 添加案例- 完成这个例外.

完整解决方案

好的 - 3小时后,OP没有再次办理登机手续,所以我想我可以发布一个完整的解决方案.我希望我没有忘记任何边缘情况,这肯定不是最有效的实现(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
Run Code Online (Sandbox Code Playgroud)

示例运行

?> :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"])
Run Code Online (Sandbox Code Playgroud)