2013-03-06 34 views
7

Tôi có một cây cú pháp trừu tượng trong haskell được tạo từ Parsec. Tôi muốn có thể truy vấn cấu trúc của nó trong khi di chuyển nó cùng một lúc để dịch nó thành mã trung gian. Ví dụ, tôi cần phải biết có bao nhiêu tham số mà bất kỳ hàm nào của AST của tôi đã đưa ra để thực hiện bản dịch này. Những gì tôi hiện đang làm là truyền AST vào mọi chức năng duy nhất để tôi có thể gọi nó bất cứ khi nào tôi cần tra cứu và tôi có chức năng trợ giúp trong một tệp khác để thực hiện tra cứu cho tôi. Điều này gây ô nhiễm chữ ký của tôi. Đặc biệt là khi tôi bắt đầu thêm nhiều thứ hơn như một bộ tích lũy.Cách sử dụng Reader Monad khi duyệt qua một cây cú pháp trừu tượng

Thay vì chuyển qua AST cho mọi chức năng tôi đã nghe, đây sẽ là một công việc tốt cho Reader Monad (cho trạng thái không thay đổi, AST) và State Monad (trạng thái thay đổi, bộ tích lũy).

Làm thế nào tôi có thể lấy ast ra khỏi đơn nguyên IO (gulp) và sử dụng nó trong Reader Monad để thực hiện tra cứu toàn cầu?

main = do 
    putStrLn "Please enter the name of your jack file (i.e. Main)" 
    fileName <- getLine 
    file <- readFile (fileName++".jack") 
    let ast = parseString file 
    writeFile (fileName++".xml") (toClass ast) --I need to query this globally 
    putStrLn $ "Completed Parsing, " ++ fileName ++ ".vm created..." 

type VM = String 

toClass :: Jack -> VM 
toClass c = case c of 
    (Class ident decs) -> 
    toDecs decs 

toDecs ::[Declaration] -> VM -- I don't want to add the ast in every function arg... 
toDecs [] = "" 
toDecs (x:xs) = case x of 
    (SubDec keyword typ subname params subbody) -> 
    case keyword of 
     "constructor" -> --use the above ast to query the # of local variables here... 
    toSubBody subbody ++ 
    toDecs xs 
    otherwise -> [] 

UPDATE on Reader Tiến trình đơn nguyên: Tôi đã chuyển ví dụ trên thành một dạng như sau: (xem bên dưới). Nhưng bây giờ tôi đang tự hỏi vì tất cả sự tích tụ chuỗi đầu ra này, tôi có nên sử dụng một nhà văn Monad không? Và nếu có, tôi nên làm thế nào để sáng tác cả hai? Nên ReaderT đóng gói nhà văn? hoặc ngược lại? Tôi có nên tạo một loại chỉ chấp nhận một Reader và một Writer mà không cố gắng tạo chúng như một Biến Áp Monad không?

main = do 
    putStrLn "Please enter the name of your jack file (i.e. Main)" 
    fileName <- getLine 
    file <- readFile (fileName++".jack") 
    writeFile (fileName++".xml") (runReader toClass $ parseString file) 
    putStrLn $ "Completed Parsing, " ++ fileName ++ ".xml created..." 

toClass = do 
    env <- ask 
    case env of Class ident decs -> return $ toDecs decs env 

toDecs [] = return "" 
toDecs ((SubDec keyword typ subname params subbody):xs) = do 
    env <- ask 
    res <- (case keyword of 
      "method" -> do return "push this 0\n" 
      "constructor" -> do return "pop pointer 0\nMemory.alloc 1\n" 
      otherwise -> do return "") 
    return $ res ++ toSubBody subbody env ++ toDecs xs env 
toDecs (_:xs) = do 
    decs <- ask 
    return $ toDecs xs decs 

toSubBody (SubBodyStatement states) = do 
    return $ toStatement states 
toSubBody (SubBody _ states) = do 
    return $ toStatement states 

http://hpaste.org/83595 tờ khai --cho

+0

Bạn đã tìm thấy một cơ hội rất thích hợp để sử dụng trình đơn đọc. Thật vậy, bạn không muốn truyền xung quanh AST mọi lúc. Tuy nhiên, trong mã mẫu của bạn, tôi không thể thấy bất kỳ hàm nào (và tôi mong đợi nhiều) tham gia vào một AST. Có thể bạn có thể dán mã với ít chi tiết hơn về miền của mình? – Tarrasch

+0

Tarrasch, một ví dụ sẽ là trường hợp "hàm tạo". Kết quả của nó cần phải là: "push pointer" ++ (hiển thị $ getFieldCount subname ast) ++ "\ n" Tôi có nó được nêu trong đoạn mã trên. –

+0

trong 'toDecs' bạn ràng buộc' res', nhưng không bao giờ sử dụng nó. Tôi không chắc đó có phải là một sai lầm hay một lỗi đánh máy. Ngoài ra, loại 'toDecs' là gì? Trong 2 trường hợp đầu tiên, phải mất 1 đầu vào, nhưng trong trường hợp thứ 3, cần có 2. Sẽ dễ dàng hơn nếu bạn cung cấp đoạn mã biên dịch (ví dụ: với định nghĩa dữ liệu cho 'Lớp',' SubDec' , 'SubBody' vv ...) – cdk

Trả lời

1

Mà không biết thêm một chút về JackDeclaration loại thật khó để xem làm thế nào để biến nó thành một đơn nguyên Reader. Nếu ý tưởng này là để thực hiện một "bản đồ" hay "gấp" trên một cái gì đó trong khi có đối tượng ast :: Jack trong phạm vi, bạn có thể viết

f :: [Declaration] -> Reader Jack [Something] 
f decls = mapM go decls where 
    go :: Declaration -> Reader Jack Something 
    go (SubDec keyword typ subname params subbody) = 
    case keyword of 
     "constructor" -> do 
     ast <- ask 
     return (doSomething subbody ast) 

và sau đó thực hiện nó trong bối cảnh với bạn ast như runReader (f decls) ast.

+0

cảm ơn mã của bạn, nó hữu ích nhất. Tuyên bố Jack là một AST lớn. Về cơ bản có rất nhiều danh sách lồng nhau. [Tuyên bố] chỉ là một mức của AST đó. Một câu hỏi khác mặc dù. Làm thế nào tôi sẽ đi về bằng cách sử dụng monad Writer cũng như trong "constructor" -> làm phần. Tôi muốn vượt qua AST và giữ [[String]] sẽ tích lũy khi tôi đi qua AST xuống dưới. –

+0

Chỉ trong trường hợp.data Jack = Class Identifier [Khai báo] bắt nguồn (Hiển thị); Khai báo dữ liệu = Loại từ khóa ClassVarDec [VarName] | SubDec Keyword Type SubName [Params] SubBody deriving (Hiện) –

+0

Vâng, có một biến thể lớn như những gì bạn làm nếu 'VarName' hoặc' Params' chứa một 'Jack' khác. Trong cả hai trường hợp, bạn sẽ muốn xem xét các biến thế đơn nguyên và xem cách xây dựng một 'WriterT [[String]] (Reader Jack)' monad để bạn có thể vừa hỏi vừa 'tell'. Các gói bạn sẽ muốn xem là 'transformers' và' mtl', cả hai đều khá chuẩn. 'máy biến áp' là một chi tiết hơn một chút,' mtl' tận dụng lợi thế của typeclasses để tiết kiệm hơn một chút. –

Các vấn đề liên quan