Chuyển sang mã C là một thói quen được thiết lập rất tốt. C ban đầu với các lớp (và các triển khai C ++ đầu tiên, sau đó được gọi là Cfront ) đã thực hiện thành công. Một số triển khai của Lisp hoặc Scheme đang làm điều đó, ví dụ Chicken Scheme , Scheme48 , Bigloo . Một số người dịch Prolog với C . Và một số phiên bản của Mozart cũng vậy (và đã có những nỗ lực biên dịch mã byte Ocaml thành C ). Hệ thống CAIA trí tuệ nhân tạo của J.Pitrat cũng được khởi động và tạo ra tất cả mã C của nó. Vala cũng dịch sang C, cho mã liên quan đến GTK. Cuốn sách của Queinnec Lisp In Pieces có một số chương về dịch sang C.
Một trong những vấn đề khi dịch sang C là các cuộc gọi đệ quy đuôi . Chuẩn C không đảm bảo rằng trình biên dịch C đang dịch chúng đúng (sang "nhảy với đối số", tức là không ăn ngăn xếp cuộc gọi), ngay cả trong một số trường hợp, các phiên bản gần đây của GCC (hoặc Clang / LLVM) thực hiện tối ưu hóa đó .
Một vấn đề khác là thu gom rác . Một số triển khai chỉ sử dụng trình thu gom rác bảo thủ Boehm ( thân thiện với C ...). Nếu bạn muốn dọn rác thu thập mã (như một số triển khai Lisp làm, ví dụ SBCL) có thể là một cơn ác mộng (bạn muốn dlclose
trên Posix).
Tuy nhiên, một vấn đề khác là xử lý các phần tiếp theo hạng nhất và cuộc gọi / cc . Nhưng thủ thuật thông minh là có thể (nhìn vào bên trong Lược đồ gà). Truy cập ngăn xếp cuộc gọi có thể đòi hỏi rất nhiều thủ thuật (nhưng xem phần sau của GNU , v.v ....). Sự tồn tại trực giao của các phần tiếp theo (nghĩa là ngăn xếp hoặc luồng) sẽ khó khăn trong C.
Xử lý ngoại lệ thường là một vấn đề để phát ra các cuộc gọi thông minh đến longjmp, v.v ...
Bạn có thể muốn tạo (trong mã C được phát ra) của mình #line
. Điều này thật nhàm chán và tốn nhiều công sức (bạn sẽ muốn điều đó ví dụ như sản xuất gdb
mã dễ dàng hơn ).
My MELT lispy ngôn ngữ miền cụ thể (để tùy chỉnh hoặc mở rộng GCC ) được phiên dịch sang C (trên thực tế để C ++ nghèo bây giờ). Nó có bộ thu gom rác sao chép thế hệ riêng. (Bạn có thể quan tâm bởi Qish hoặc Ravenbrook MPS ). Trên thực tế, GC thế hệ dễ dàng hơn trong mã C được tạo bằng máy so với mã C viết tay (vì bạn sẽ điều chỉnh trình tạo mã C cho rào cản ghi và máy móc của bạn).
Tôi không biết bất kỳ triển khai ngôn ngữ nào dịch sang mã C ++ chính hãng , tức là sử dụng một số kỹ thuật "thu gom rác thời gian biên dịch" để phát ra mã C ++ bằng cách sử dụng nhiều mẫu STL và tôn trọng thành ngữ RAII . (xin vui lòng cho biết nếu bạn biết một).
Điều thú vị ngày nay là (trên máy tính để bàn Linux hiện tại) Trình biên dịch C có thể đủ nhanh để thực hiện một vòng lặp đọc-in-in-vòng-in tương tác được dịch sang C: bạn sẽ phát ra mã C (vài trăm dòng) cho mỗi người dùng tương tác, bạn sẽ fork
biên dịch nó thành một đối tượng chia sẻ, sau đó bạn sẽ thực hiện dlopen
. (MELT đang làm điều đó tất cả đã sẵn sàng, và nó thường đủ nhanh). Tất cả điều này có thể mất vài phần mười giây và được người dùng cuối chấp nhận.
Khi có thể, tôi khuyên bạn nên dịch sang C, chứ không phải C ++, đặc biệt vì quá trình biên dịch C ++ chậm.
Nếu bạn đang triển khai ngôn ngữ của mình, bạn cũng có thể xem xét (thay vì phát ra mã C) một số thư viện JIT như libjit , GNU sét , asmjit hoặc thậm chí LLVM hoặc GCCJIT . Nếu bạn muốn dịch sang C, đôi khi bạn có thể sử dụng tinycc : nó biên dịch rất nhanh mã C được tạo (ngay cả trong bộ nhớ) để làm chậm mã máy. Nhưng nói chung, bạn muốn tận dụng tối ưu hóa được thực hiện bởi trình biên dịch C thực sự như GCC
Nếu bạn dịch sang C ngôn ngữ của bạn, trước tiên hãy chắc chắn xây dựng toàn bộ AST của mã C được tạo trong bộ nhớ (điều này cũng giúp tạo ra tất cả các khai báo trước, sau đó là tất cả các định nghĩa và mã chức năng). Bạn sẽ có thể thực hiện một số tối ưu hóa / chuẩn hóa theo cách này. Ngoài ra, bạn có thể quan tâm đến một số tiện ích mở rộng GCC (ví dụ: gotos được tính toán). Có lẽ bạn sẽ muốn tránh tạo ra các hàm C khổng lồ - ví dụ như một trăm ngàn dòng C được tạo - (tốt hơn là bạn nên chia chúng thành các phần nhỏ hơn) vì tối ưu hóa trình biên dịch C rất không hài lòng với các hàm C rất lớn (trong thực tế và thực nghiệmgcc -O
thời gian biên dịch của các hàm lớn tỷ lệ với bình phương kích thước mã hàm). Vì vậy, giới hạn kích thước của các hàm C được tạo của bạn ở mức vài nghìn dòng mỗi hàm.
Lưu ý rằng cả trình biên dịch C & C ++ của Clang (thru LLVM ) và GCC (thru libgccjit ) cung cấp một số cách để phát ra một số biểu diễn bên trong phù hợp với các trình biên dịch này, nhưng làm như vậy có thể (hoặc không) khó hơn phát ra mã C (hoặc C ++), và là cụ thể cho từng trình biên dịch.
Nếu thiết kế một ngôn ngữ được dịch sang C, có lẽ bạn muốn có một số thủ thuật (hoặc cấu trúc) để tạo ra một hỗn hợp C với ngôn ngữ của bạn. Giấy DSL2011 của tôi MELT : Ngôn ngữ cụ thể miền được dịch được nhúng trong Trình biên dịch GCC sẽ cung cấp cho bạn các gợi ý hữu ích.