簡體   English   中英

AST和Harsell中的解析

[英]AST and Parsing in Haskell

我有一個任務,我無法弄清楚如何定義答案。

分配

寫函數exp:: [String] -> (AST, [String])

AST

  • 如果x是數字,則應該表示Number x
  • 如果是“+”og a“ - ”則應該說是Atom x
  • 如果它讀取“(”,那么“(”直到它出現“)后面的所有內容”“應該是List [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 :: String -> [String] ,它就是一個列表。

例:
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`

exp函數如下所示:

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`

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)

我必須隱藏exp因為我們想將它用作函數名。

然后我想要一個Maybe fmap ,所以我包含來自Control.Applicative的運算符。 這真的就是這個,如果您之前沒有看到它:

f <$> Nothing = Nothing
f <$> Just a  = Just (f a)

我想為Maybe一些幫手:

  • isJust要檢查是否Just _
  • 而且fromJust為了得到a來自Just a

最后我需要這個幫助函數來read更安全的東西:

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

這將嘗試在這里讀到一個數字- returing 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)

正如你所看到的那樣,我只是根據lookat分支,但略有不同:

如果我看到一個數字,我會返回數字和rest-token-list。 如果我看到一個(我啟動另一個解析器parseList

parseList將執行相同的操作: - 它查看第一個標記 - 如果標記是a )它完成當前列表(它使用累加器技術)並返回。 - 如果不是,它使用現有的exp解析器遞歸獲取列表的元素。

這是一個示例運行:

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

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

去做

還有一些邊界情況你必須決定(如果沒有輸入令牌怎么辦?)。

當然,你必須為Atom添加案例 - 完成這個例外。

完整解決方案

好的 - 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

示例運行

λ> :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"])

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM