Viết thông dịch viên Haskell trong Haskell


90

Một bài tập lập trình cổ điển là viết trình thông dịch Lisp / Scheme trong Lisp / Scheme. Sức mạnh của ngôn ngữ đầy đủ có thể được tận dụng để tạo ra một trình thông dịch cho một tập hợp con của ngôn ngữ.

Có một bài tập tương tự cho Haskell không? Tôi muốn triển khai một tập hợp con của Haskell bằng cách sử dụng Haskell làm công cụ. Tất nhiên nó có thể được thực hiện, nhưng có bất kỳ nguồn trực tuyến nào có sẵn để xem xét không?


Đây là câu chuyện cơ bản.

Tôi đang khám phá ý tưởng sử dụng Haskell làm ngôn ngữ để khám phá một số khái niệm trong khóa học Cấu trúc rời rạc mà tôi đang dạy. Trong học kỳ này, tôi đã học tiếng Miranda , một ngôn ngữ nhỏ hơn đã truyền cảm hứng cho Haskell. Miranda làm khoảng 90% những gì tôi muốn, nhưng Haskell làm khoảng 2000%. :)

Vì vậy, ý tưởng của tôi là tạo ra một ngôn ngữ có chính xác các tính năng của Haskell mà tôi muốn và không cho phép mọi thứ khác. Khi học sinh tiến bộ, tôi có thể "bật" một cách chọn lọc các tính năng khác nhau khi chúng đã nắm vững kiến ​​thức cơ bản.

Các "cấp độ ngôn ngữ" sư phạm đã được sử dụng thành công để dạy JavaScheme . Bằng cách hạn chế những gì chúng có thể làm, bạn có thể ngăn chúng tự bắn vào chân mình trong khi chúng vẫn đang nắm vững cú pháp và khái niệm mà bạn đang cố gắng dạy. Và bạn có thể đưa ra các thông báo lỗi tốt hơn.


Tôi có một phương ngữ WIP Haskell được triển khai với Nhập Haskell trong Haskell làm cơ sở. Có một bản demo của nó ở đây chrisdone.com/toys/duet-delta Nó chưa sẵn sàng để phát hành mã nguồn mở công khai, nhưng tôi có thể chia sẻ nguồn với bạn nếu quan tâm.
Christopher Done,

Câu trả lời:


76

Tôi thích mục tiêu của bạn, nhưng đó là một công việc lớn. Một vài gợi ý:

  • Tôi đã làm việc trên GHC, và bạn không muốn bất kỳ phần nào của nguồn. Hugs là cách triển khai đơn giản hơn, gọn gàng hơn nhiều nhưng tiếc là nó ở C.

  • Đó là một phần nhỏ của câu đố, nhưng Mark Jones đã viết một bài báo đẹp có tên là Nhập Haskell bằng Haskell , đây sẽ là một điểm khởi đầu tuyệt vời cho giao diện người dùng của bạn.

Chúc may mắn! Xác định trình độ ngôn ngữ cho Haskell, với bằng chứng hỗ trợ từ lớp học, sẽ mang lại lợi ích to lớn cho cộng đồng và chắc chắn là một kết quả có thể công bố!


2
Không biết nhận xét về GHC có còn chính xác không. GHC rất phức tạp, nhưng nó được ghi chép khá đầy đủ. Đặc biệt, nội dung Noteshữu ích trong việc hiểu các chi tiết cấp thấp và chương về GHC trong Kiến trúc của các ứng dụng mã nguồn mở cung cấp một cái nhìn tổng quan cấp cao tuyệt vời.
sjy

37

Có một trình phân tích cú pháp Haskell hoàn chỉnh: http://hackage.haskell.org/package/haskell-src-exts

Khi bạn đã phân tích cú pháp, việc loại bỏ hoặc không cho phép một số thứ thật dễ dàng. Tôi đã làm điều này cho tryhaskell.org để không cho phép các câu lệnh nhập, để hỗ trợ các định nghĩa cấp cao nhất, v.v.

Chỉ cần phân tích cú pháp mô-đun:

parseModule :: String -> ParseResult Module

Sau đó, bạn có AST cho một mô-đun:

Module SrcLoc ModuleName [ModulePragma] (Maybe WarningText) (Maybe [ExportSpec]) [ImportDecl] [Decl]    

Loại Decl rất rộng rãi: http://hackage.haskell.org/packages/archive/haskell-src-exts/1.9.0/doc/html/Language-Haskell-Exts-Syntax.html#t%3ADecl

Tất cả những gì bạn cần làm là xác định một danh sách trắng - gồm những khai báo, nhập khẩu, ký hiệu, cú pháp nào có sẵn, sau đó xem AST và đưa ra "lỗi phân tích cú pháp" cho bất kỳ thứ gì bạn chưa muốn họ biết. Bạn có thể sử dụng giá trị SrcLoc được gắn vào mọi nút trong AST:

data SrcLoc = SrcLoc
     { srcFilename :: String
     , srcLine :: Int
     , srcColumn :: Int
     }

Không cần phải triển khai lại Haskell. Nếu bạn muốn cung cấp các lỗi biên dịch thân thiện hơn, chỉ cần phân tích cú pháp mã, lọc nó, gửi nó đến trình biên dịch và phân tích cú pháp đầu ra của trình biên dịch. Nếu đó là "không thể so khớp mong đợi loại a với được suy ra a -> b" thì bạn biết rằng có thể có quá ít đối số cho một hàm.

Trừ khi bạn thực sự thực sự muốn dành thời gian triển khai Haskell từ đầu hoặc gây rối với nội bộ của Hugs, hoặc một số triển khai ngu ngốc, tôi nghĩ bạn chỉ nên lọc những gì được chuyển đến GHC. Bằng cách đó, nếu sinh viên của bạn muốn lấy cơ sở mã của họ và thực hiện bước tiếp theo và viết một số mã Haskell thực sự hoàn chỉnh, quá trình chuyển đổi là minh bạch.


24

Bạn có muốn xây dựng trình thông dịch của mình từ đầu không? Bắt đầu với việc triển khai một ngôn ngữ hàm dễ dàng hơn như phép tính lambda hoặc một biến thể nói ngọng. Đối với phần sau, có một cuốn sách wikibook khá hay có tên là Viết cho mình một Đề án trong 48 giờ , giới thiệu rất hay và thực dụng về các kỹ thuật phân tích cú pháp và giải thích.

Phiên dịch Haskell bằng tay sẽ phức tạp hơn nhiều vì bạn sẽ phải xử lý các tính năng phức tạp cao như kính đánh máy, một hệ thống kiểu cực kỳ mạnh mẽ (kiểu suy luận!) Và đánh giá lười biếng (kỹ thuật giảm thiểu).

Vì vậy, bạn nên xác định một tập con khá nhỏ của Haskell để làm việc và sau đó có thể bắt đầu bằng cách mở rộng theo từng bước của Scheme-example.

Thêm vào:

Lưu ý rằng trong Haskell, bạn có toàn quyền truy cập vào API thông dịch viên (ít nhất là theo GHC) bao gồm trình phân tích cú pháp, trình biên dịch và tất nhiên là trình thông dịch.

Gói để sử dụng là gợi ý (Language.Haskell. *) . Thật không may, tôi không tìm thấy hướng dẫn trực tuyến về điều này và cũng không tự mình thử nhưng nó có vẻ khá hứa hẹn.


12
Lưu ý rằng kiểu suy luận thực sự là một thuật toán 20-30 dòng thực sự dễ dàng. nó đẹp trong sự đơn giản của nó. Đánh giá lười biếng cũng không quá khó để mã hóa. Tôi muốn nói rằng khó khăn nằm ở cú pháp điên rồ, khớp mẫu và chỉ là một lượng lớn nội dung trong ngôn ngữ.
Claudiu 28/09/09

Thú vị - Bạn có thể đăng các liên kết cho các bí danh kiểu suy luận không?
Dario

5
Vâng, hãy xem cuốn sách miễn phí này - cs.brown.edu/~sk/Publications/Books/ProgLangs/2007-04-26 -, nó ở trang 273 (289 của pdf). Mã giả alg nằm trên P296.
Claudiu 28/09/09

1
Ngoài ra còn có việc triển khai thuật toán kiểm tra / suy luận kiểu (the?) Trong " Triển khai các ngôn ngữ lập trình chức năng ".
Phil Armstrong,

1
Tuy nhiên, suy luận kiểu với các lớp kiểu không đơn giản.
Christopher Done,

20

tạo một ngôn ngữ có chính xác các tính năng của Haskell mà tôi muốn và không cho phép mọi thứ khác. Khi học sinh tiến bộ, tôi có thể "bật" một cách chọn lọc các tính năng khác nhau khi chúng đã nắm vững kiến ​​thức cơ bản.

Tôi đề xuất một giải pháp đơn giản hơn (vì ít liên quan đến công việc hơn) cho vấn đề này. Thay vì tạo một triển khai Haskell nơi bạn có thể tắt các tính năng, hãy bọc trình biên dịch Haskell bằng một chương trình trước tiên kiểm tra xem mã không sử dụng bất kỳ tính năng nào mà bạn không cho phép, sau đó sử dụng trình biên dịch tạo sẵn để biên dịch.

Điều đó sẽ tương tự với HLint (và cũng tương tự như vậy ):

HLint (trước đây là Tiến sĩ Haskell) đọc các chương trình Haskell và đề xuất những thay đổi với hy vọng làm cho chúng dễ đọc hơn. HLint cũng giúp bạn dễ dàng tắt các đề xuất không mong muốn và thêm các đề xuất tùy chỉnh của riêng bạn.

  • Triển khai các "đề xuất" HLint của riêng bạn để không sử dụng các tính năng bạn không cho phép
  • Tắt tất cả các đề xuất HLint tiêu chuẩn.
  • Làm cho trình bao bọc của bạn chạy HLint đã sửa đổi của bạn như một bước đầu tiên
  • Coi các đề xuất của HLint là lỗi. Tức là nếu HLint "phàn nàn" thì chương trình sẽ không chuyển sang giai đoạn biên dịch


6

Loạt trình biên dịch EHC có lẽ là đặt cược tốt nhất: nó được phát triển tích cực và dường như chính xác là những gì bạn muốn - một loạt trình biên dịch / thông dịch lambda Calculi nhỏ với đỉnh cao là Haskell '98.

Nhưng bạn cũng có thể xem các ngôn ngữ khác nhau được phát triển trong Các loại và Ngôn ngữ Lập trình của Pierce , hoặc trình thông dịch Helium (một Haskell tàn tật dành cho sinh viên http://en.wikipedia.org/wiki/Helium_(Haskell) ).


6

Nếu bạn đang tìm kiếm một tập hợp con của Haskell dễ triển khai, bạn có thể loại bỏ các lớp kiểu và kiểm tra kiểu. Nếu không có các lớp kiểu, bạn không cần suy luận kiểu để đánh giá mã Haskell.

Tôi đã viết một trình biên dịch tập hợp con Haskell tự biên dịch cho một thử thách Code Golf. Nó lấy mã tập hợp con Haskell trên đầu vào và tạo ra mã C trên đầu ra. Tôi rất tiếc không có phiên bản dễ đọc hơn; Tôi đã gỡ bỏ các định nghĩa lồng nhau bằng tay trong quá trình làm cho nó tự biên dịch.

Đối với một sinh viên quan tâm đến việc triển khai trình thông dịch cho một tập hợp con của Haskell, tôi khuyên bạn nên bắt đầu với các tính năng sau:

  • Đánh giá lười biếng. Nếu thông dịch viên ở Haskell, bạn có thể không phải làm gì cho việc này.

  • Định nghĩa hàm với các đối số và bảo vệ phù hợp với mẫu. Chỉ lo lắng về biến, khuyết điểm, nil và _các mẫu.

  • Cú pháp biểu thức đơn giản:

    • Chữ số nguyên

    • Chữ ký tự

    • [] (không)

    • Ứng dụng hàm (liên kết bên trái)

    • Infix :(khuyết điểm, liên kết phải)

    • Dấu ngoặc đơn

    • Tên biến

    • Tên hàm

Cụ thể hơn, hãy viết một trình thông dịch có thể chạy điều này:

-- tail :: [a] -> [a]
tail (_:xs) = xs

-- append :: [a] -> [a] -> [a]
append []     ys = ys
append (x:xs) ys = x : append xs ys

-- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith f (a:as) (b:bs) = f a b : zipWith f as bs
zipWith _ _      _      = []

-- showList :: (a -> String) -> [a] -> String
showList _    []     = '[' : ']' : []
showList show (x:xs) = '[' : append (show x) (showItems show xs)

-- showItems :: (a -> String) -> [a] -> String
showItems show []     = ']' : []
showItems show (x:xs) = ',' : append (show x) (showItems show xs)

-- fibs :: [Int]
fibs = 0 : 1 : zipWith add fibs (tail fibs)

-- main :: String
main = showList showInt (take 40 fibs)

Kiểm tra kiểu là một tính năng quan trọng của Haskell. Tuy nhiên, việc chuyển từ con số không sang trình biên dịch Haskell kiểm tra kiểu là rất khó. Nếu bạn bắt đầu bằng cách viết một trình thông dịch cho phần trên, việc thêm kiểm tra kiểu vào nó sẽ bớt khó khăn hơn.


"Đánh giá lười biếng. Nếu thông dịch viên ở Haskell, bạn có thể không phải làm bất cứ điều gì cho việc này." Điều này có thể không đúng. Xem bài viết của Naylor trong haskell.org/wikiupload/0/0a/TMR-Issue10.pdf để biết thêm thông tin về cách triển khai trình thông dịch lười biếng trong Haskell.
Jared Updike

3

Bạn có thể nhìn vào Happy (một trình phân tích cú pháp giống yacc trong Haskell) có trình phân tích cú pháp Haskell.


3

Đây có thể là một ý tưởng hay - hãy tạo một phiên bản nhỏ của NetLogo trong Haskell. Đây là phiên dịch nhỏ.


Các liên kết đã chết. Bất kỳ cơ hội nào nội dung này vẫn tồn tại ở một nơi khác? Tôi rất tò mò ...
Nicolas Payette

hmm, đó là một bài đăng trên blog và tôi không biết phải sử dụng từ khóa nào để tìm kiếm nó. Một bài học tốt để bao gồm thông tin đáng kể hơn khi cung cấp một liên kết ...
Claudiu

1
Một tìm kiếm trên Google cho "netlogo haskell" xuất hiện ... câu hỏi này. Dù sao, không có vấn đề lớn. Cảm ơn!
Nicolas Payette

2

xem liệu heli có tạo cơ sở tốt hơn để xây dựng dựa trên haskell tiêu chuẩn hay không.



2

Tôi được biết rằng Idris có một trình phân tích cú pháp khá nhỏ gọn, không chắc liệu nó có thực sự phù hợp để thay đổi hay không, nhưng nó được viết bằng Haskell.


2

Vườn thú ngôn ngữ lập trình của Andrej Bauer có một triển khai nhỏ của một ngôn ngữ lập trình chức năng thuần túy có tên gọi là "minihaskell". Nó chứa khoảng 700 dòng OCaml, rất dễ tiêu hóa.

Trang web cũng chứa các phiên bản đồ chơi của các ngôn ngữ lập trình kiểu ML, kiểu Prolog và OO.


1

Bạn có nghĩ rằng việc lấy các nguồn GHC và loại bỏ những gì bạn không muốn sẽ dễ dàng hơn là viết trình thông dịch Haskell của riêng bạn từ đầu? Nói chung, cần có một rất nhiều nỗ lực ít hơn có liên quan đến việc loại bỏ các tính năng như trái ngược với tạo / thêm các tính năng.

GHC dù sao cũng được viết bằng Haskell, vì vậy về mặt kỹ thuật, câu hỏi của bạn về trình thông dịch Haskell được viết bằng Haskell.

Có lẽ sẽ không quá khó để làm cho toàn bộ mọi thứ được liên kết tĩnh và sau đó chỉ phân phối GHCi tùy chỉnh của bạn, để sinh viên không thể tải các mô-đun nguồn Haskell khác. Về việc sẽ mất bao nhiêu công để ngăn chúng tải các tệp đối tượng Haskell khác, tôi không biết. Bạn cũng có thể muốn tắt FFI, nếu bạn có nhiều kẻ gian lận trong lớp học của mình :)


1
Điều này không hề dễ dàng vì nhiều tính năng phụ thuộc vào những tính năng khác. Nhưng có lẽ OP chỉ muốn không nhập Prelude và thay vào đó cung cấp của riêng mình. Hầu hết Haskell mà bạn thấy là các chức năng bình thường, không phải là các tính năng cụ thể của thời gian chạy. (Nhưng tất nhiên, rất nhiều là như vậy .)
jrockway 23/09/09

0

Lý do tại sao có rất nhiều trình thông dịch LISP là vì LISP về cơ bản là tiền thân của JSON: một định dạng đơn giản để mã hóa dữ liệu. Điều này làm cho phần giao diện người dùng khá dễ dàng để xử lý. So với điều đó, Haskell, đặc biệt là với Tiện ích mở rộng ngôn ngữ, không phải là ngôn ngữ dễ phân tích cú pháp nhất. Đây là một số cấu trúc cú pháp nghe có vẻ phức tạp để làm đúng:

  • toán tử có mức độ ưu tiên, liên kết và cố định có thể định cấu hình,
  • bình luận lồng nhau
  • quy tắc bố cục
  • cú pháp mẫu
  • do- khối và gỡ rối sang mã đơn nguyên

Mỗi người trong số này, ngoại trừ các toán tử, có thể được học viên giải quyết sau Khóa học Xây dựng Trình biên dịch của họ, nhưng nó sẽ tập trung vào cách Haskell thực sự hoạt động. Ngoài ra, bạn có thể không muốn triển khai trực tiếp tất cả các cấu trúc cú pháp của Haskell, mà thay vào đó, thực hiện các đường chuyền để loại bỏ chúng. Điều này đưa chúng ta đến cốt lõi nghĩa đen của vấn đề, chơi chữ hoàn toàn có mục đích.

Đề xuất của tôi là thực hiện đánh máy và trình thông dịch Corethay vì Haskell đầy đủ. Cả hai nhiệm vụ này đều khá phức tạp. Ngôn ngữ này, mặc dù vẫn là một ngôn ngữ chức năng được đánh máy mạnh, nhưng ít phức tạp hơn để xử lý về mặt tối ưu hóa và tạo mã. Tuy nhiên, nó vẫn độc lập với máy bên dưới. Do đó, GHC sử dụng nó như một ngôn ngữ trung gian và dịch hầu hết các cấu trúc cú pháp của Haskell sang nó.

Ngoài ra, bạn không nên tránh sử dụng giao diện người dùng của GHC (hoặc của trình biên dịch khác). Tôi không coi đó là gian lận vì các LISP tùy chỉnh sử dụng trình phân tích cú pháp của hệ thống LISP máy chủ (ít nhất là trong quá trình khởi động). Việc dọn dẹp Corecác đoạn mã và trình bày chúng cho sinh viên, cùng với mã gốc, sẽ cho phép bạn cung cấp cái nhìn tổng quan về những gì giao diện người dùng làm và tại sao không nên thực hiện lại nó.

Dưới đây là một vài liên kết đến tài liệu Coređược sử dụng trong GHC:

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.