Crafting Interpreters: Cuốn sách 640 trang hoàn thành sau 15 tháng
(journal.stuffwithstuff.com)- Dù loạt bài trên web đã kết thúc, Crafting Interpreters vẫn cần thêm 15 tháng làm việc để trở thành một cuốn sách thực sự, và cuối cùng được hoàn thiện dưới dạng bản in, ebook và PDF
- Để biến một tập hợp Markdown và PNG thành sách, tác giả phải xây dựng mới hệ thống build bằng Dart, quy trình nhập XML vào InDesign, tự động hóa bằng JavaScript và kiểm chứng dàn trang
- Thành phẩm cuối cùng có khổ 8×10 inch, 640 trang, hơn 200.000 từ, 1.133 đoạn mã và hàng trăm minh họa, với các ràng buộc bố cục khắt khe hơn nhiều so với nội dung web thông thường
- Tiếp đó là 5 tháng rà soát toàn bộ, biên tập sao chép chuyên nghiệp, 2 tháng dàn trang, 2 tuần làm chỉ mục, kiểm tra bản in thử và tự động hóa so sánh PDF
- Một cuốn sách kỹ thuật tự xuất bản không kết thúc ở việc viết; tự động hóa build, bố cục, kiểm chứng và phân phối quyết định chất lượng và mức độ hoàn thiện của sách
Những việc còn lại sau khi hoàn tất nội dung web
- Phần nội dung chính của Crafting Interpreters đã hoàn thành, nhưng khi đó kết quả vẫn là các tệp Markdown và PNG được mã Python chuyển đổi thành website
- Mục tiêu ngay từ đầu là một cuốn sách giấy thực sự, và sau khi đăng chương cuối lên web, tác giả nghỉ khoảng một tháng
- Sau gần 4 năm viết mỗi ngày, tác giả đã rất kiệt sức, và bối cảnh đầu năm 2020 cũng là thời điểm khó tiếp tục công việc
Xây lại hệ thống build bằng Dart
- Trước tiên, tác giả sửa các lỗi chính tả và lỗi nội dung do độc giả báo qua GitHub issue
- Sau đó, tác giả viết lại toàn bộ hệ thống build của sách bằng Dart
- Script build của cuốn sách đầu tiên là một script Python đơn lẻ, render các tệp Markdown theo chương thành HTML và chèn các mẩu mã vào
- Crafting Interpreters cần xây dựng dần dần mã của hai trình thông dịch hoàn chỉnh qua 30 chương, nên cần một quy trình build phức tạp hơn
- Hệ thống build mới có thể xuất mã của trình thông dịch bằng chương trình, tới một chương cụ thể hoặc một điểm cụ thể bên trong chương, rồi biên dịch mã đó và đưa vào chạy kiểm thử tự động
- Bộ công cụ dựa trên Python gây gánh nặng bảo trì lớn theo mức độ thành thạo của tác giả, và cũng chậm
- Phiên bản Dart tạo ra đúng HTML và mã tô sáng cú pháp như mong muốn, đồng thời nhanh hơn 10 lần so với phiên bản Python cũ
- Việc có nhiều quyền kiểm soát hơn đối với xử lý Markdown cũng hữu ích về sau khi xuất XML cho InDesign
Thiết kế sách và quyết định khổ sách
- Thiết kế sách gần giống việc trước tiên tạo một framework, như trong phát triển web hoặc game, rồi đổ nội dung vào đó
- Trong InDesign, tác giả thiết lập master để quy định lề trang và lưới, cũng như style để quy định phông chữ, kiểu chữ và màu sắc cho văn bản và đối tượng
- Crafting Interpreters có nhiều yếu tố khiến việc thiết kế khó hơn
- Có nhiều văn bản chính
- Có nhiều aside dài giải thích ngay bên cạnh các câu, đoạn mã hoặc minh họa cụ thể
- Có nhiều mã, và bên cạnh mỗi đoạn mã đều có mô tả vị trí của nó trong chương trình kết quả
- Chiều rộng ngang phải đồng thời xét đến các dòng mã dài, vùng aside và lề trong của một cuốn sách dày
- Các giáo trình CS thông thường trên kệ sách của tác giả thường rộng 7,5 inch, nhưng khó chứa mã, aside và lề, nên tác giả quyết định dùng chiều rộng 8 inch
- Khi tự xuất bản, tác giả phải dùng các khổ sách giới hạn mà KDP và IngramSpark hỗ trợ; với chiều rộng 8 inch, lựa chọn hợp lý là 8×10 inch
- Theo chiều dọc, văn bản được căn theo baseline grid 12pt cổ điển
Pipeline XML để chuyển sang InDesign
- InDesign không hiểu trực tiếp Markdown hay hệ thống build của tác giả, nên sao chép và dán thủ công là không thực tế
- InDesign hỗ trợ nhập XML và tự động áp dụng style theo từng thẻ
- Tuy nhiên, hỗ trợ XML có hạn chế trong xử lý thẻ lồng nhau, nên không xử lý tốt kiểu lồng thẻ in nghiêng bên trong header như HTML
- Vì có thể trực tiếp kiểm soát hệ thống build, tác giả đã viết một custom XML exporter để tạo ra các thẻ dễ được InDesign chấp nhận
Tự động hóa InDesign bằng JavaScript và các giới hạn
- Nhập XML tạo ra một “story” trong InDesign, tức một luồng văn bản liên tục duy nhất chạy theo hộp văn bản chính
- Phần nội dung chính và các đoạn mã đi vào luồng chính, nhưng aside và location marker phải được tách sang bên cạnh
- Ở cuốn sách trước, tác giả cắt aside thủ công rồi dán vào hộp văn bản mới, nhưng cuốn này có tới 1.133 đoạn mã nên không thể làm như vậy
- InDesign hỗ trợ scripting bằng JavaScript, nhưng tài liệu và môi trường gỡ lỗi rất nghèo nàn
- Không có debugger
- Không có stack trace
- Không có debug print thông thường
- Chỉ có thể dùng
alert(), và mỗi lần gọi thì script sẽ dừng lại
- Script JavaScript tìm các aside và location marker, tách chúng khỏi luồng văn bản chính rồi tạo thành các hộp văn bản riêng
- Tự động hóa việc định vị cuối cùng vẫn không hoàn thiện
- Tác giả đã thử bố trí bằng tính năng anchor của InDesign và Object Style, nhưng trong một số trường hợp lại phát sinh lỗi mất đường viền của đoạn mã lân cận
- Cuối cùng, một số location tag vẫn phải được đặt vị trí thủ công
Biên tập và copyediting
- Tác giả thực hiện một lượt biên tập đọc lại toàn bộ nội dung từ đầu đến cuối
- Mỗi chương trong quá trình viết đã trải qua ba bản nháp, nhưng tác giả vẫn rà soát thêm một lần để xem dòng chảy của toàn bộ cuốn sách
- Công việc này mất 5 tháng, và hầu hết các câu đùa bị lặp đã được chỉnh sửa
- Sau đó, tác giả thuê copyeditor chuyên nghiệp Kari Somerton
- Quy trình biên tập thông thường thường dùng Microsoft Word và Track Changes, nhưng tác giả muốn duy trì workflow dựa trên plaintext và Git
- Kari Somerton học Git và hệ thống build tùy biến rồi rà soát toàn bộ cuốn sách, phát hiện hàng trăm lỗi
- Dù đã có bốn bản nháp và hàng trăm issue từ độc giả, một copyeditor chuyên nghiệp vẫn tìm ra rất nhiều vấn đề
Các ràng buộc khi dàn trang 640 trang
- Sau khi đã trau chuốt đủ phần chữ, tác giả tiến hành dàn trang từng chương trong InDesign
- Công việc theo từng chương được lặp lại theo quy trình sau
- Tạo tệp InDesign mới
- Xuất XML
- Nhập XML vào InDesign
- Dùng JavaScript tách aside và location marker ra
- Thiết lập anchor cho các phần tử sidebar
- Điều chỉnh khoảng trắng ở cuối trang
- Năm bước đầu có thể xử lý trong khoảng 30 phút cho mỗi chương, nhưng điều chỉnh khoảng trắng cuối cùng là phần khó nhất
- Dàn trang sách có nhiều ràng buộc bố trí theo chiều dọc
- Không thể cắt minh họa ở giữa trang
- Aside sẽ dễ hiểu hơn nếu nằm gọn trong một trang
- Các đoạn mã cũng tốt nhất không bị tách qua trang nếu có thể
- Cần tránh tình huống chỉ còn header ở cuối trang
- Cũng nên tránh widows and orphans
- Trong những tình huống này, InDesign đẩy nội dung sang trang sau, nhưng khi đó phần dưới trang sẽ có khoảng trắng lớn
- Minh họa và các đoạn mã hoạt động như một bài toán bin-packing đan xen, và vì vậy việc dàn trang toàn bộ các chương mất 2 tháng
- Để giảm khoảng trắng, tác giả phải chia một đoạn mã thành hai phần, điều chỉnh lề quanh ảnh hoặc thay đổi chiều cao minh họa
Minh họa, chỉ mục và các phần phụ trước sau
- Minh họa được chọn theo phong cách tranh bút mực đen trắng thân thiện với in ấn, và khi quét lần đầu được đưa vào ở 1200 DPI
- Xuất ra bitmap độ phân giải cao thì dễ, nhưng đặt chúng vào bố cục trang lại khó
- Vì nội dung chính không dùng kiểu “tham khảo Figure 123” mà có cấu trúc câu trực tiếp chỉ vào minh họa ngay bên cạnh, nên minh họa phải nằm ở vị trí gần đó
- Không thuê người lập chỉ mục chuyên nghiệp, tác giả tự rà lại mọi chương trong 2 tuần để tạo chỉ mục
- Tính năng chỉ mục của InDesign có thể biến văn bản được chọn thành mục chỉ mục và tạo toàn bộ chỉ mục, nhưng bản thân việc thêm mục thì lặp đi lặp lại và nhàm chán
- Cuối sách có phần chỉ mục; đầu sách có trang tiêu đề, trang bản quyền, lời đề tặng, lời cảm ơn và mục lục do InDesign tạo
Thiết kế bìa
- Tác giả cho rằng tính nghệ thuật của bìa sách kỹ thuật có thể không quan trọng như tiểu thuyết, nhưng vì không ở vị thế giáo sư có thể bắt buộc mua sách nên đã dành nhiều thời gian cho bìa
- Ban đầu, tác giả định dùng ảnh tự chụp làm bìa, nhưng không tìm được ảnh phù hợp
- Cuối cùng, tác giả quyết định dùng minh họa bút mực, ngôn ngữ thị giác của cuốn sách
- Tác giả vẽ lại lớn hơn và chi tiết hơn bức tranh ngọn núi giải thích quá trình biên dịch, đồng thời tạo lại chữ tiêu đề theo cảm giác viết tay
- Tiêu đề được in bằng Acumin Pro Extra Condensed rồi đồ lại bằng tay để tạo cảm giác không hoàn hảo, và tác giả chọn bảng màu giống một cẩm nang hướng đạo in ronéo thập niên 1950
Bản in thử và kiểm chứng thay đổi PDF
- Tác giả tải PDF lên KDP và đặt bản in thử; một tuần sau nhận được một chiếc hộp nặng
- Chỉ khi nhìn thấy cuốn sách thật, tác giả mới cảm nhận được quy mô của dự án như một vật thể vật lý chứ không phải một tệp dữ liệu
- Vì quá trình dàn trang có nhiều thao tác thủ công, tác giả đọc trực tiếp bản in thử để tìm lỗi và đánh dấu bằng sticky note
- Các tệp InDesign được đưa vào kho Git, nhưng vì là các tệp nhị phân lớn và mờ đục nên không thể xem diff như mã nguồn
- InDesign đôi khi thay đổi tệp dù có vẻ không có thay đổi thực sự, khiến khó xác định điều gì đã đổi
- Tác giả viết một Dart script để trích xuất mọi trang của PDF cuốn sách và tạo thành một ảnh PNG dạng ô lớn
- Với mỗi commit, tác giả xuất PDF, tạo ảnh dạng ô, rồi dùng Photoshop action để vẽ viền đỏ quanh các pixel khác nhau giữa hai ảnh nhằm tìm các trang đã thay đổi
- Cách này không trực tiếp cho biết chi tiết thay đổi, nhưng chỉ ra trang nào cần kiểm tra bằng mắt và giúp xác nhận rằng chỉ các thay đổi dự kiến được đưa vào
Ebook và phát hành
- Sau khi sửa xong bản in thử của bản in, tác giả cũng tạo ebook Kindle và EPUB
- Tác giả sửa hệ thống build tự xây để có thể xuất XHTML cũ, metadata và manifest mà EPUB yêu cầu
- Sau vài lệnh command line, tác giả tạo được ebook Kindle và EPUB, rồi kiểm thử trên nhiều trình đọc và điều chỉnh CSS
- Khi các tệp cuối cùng đã sẵn sàng, tác giả cập nhật trang đầu của website sách để trỏ tới nơi mua, đồng thời chỉnh lại ảnh và bố cục responsive
- Sau khi hoàn tất tải lên các cửa hàng, cập nhật website và thông báo cho mailing list, cuốn sách mới ở trạng thái “thực sự” hoàn thành
Kế hoạch sau đó
- Ngay cả sau khi hoàn thành chương cuối, mọi người vẫn hỏi tác giả về việc tiếp theo hoặc chủ đề cuốn sách tiếp theo
- Sau 6 năm bám theo một dự án, tác giả không muốn lên kế hoạch cho sách mới trong một thời gian
- Cũng có nhiều việc bị trì hoãn trong đại dịch, và kế hoạch là nghỉ ngơi một thời gian mà chưa quyết định sẽ làm gì
- Tác giả nhắc đến các khả năng như làm nhạc, câu cá, dành thời gian với bạn bè và gia đình, làm dự án roguelike, nhưng không quyết định ngay sẽ làm gì
- Tác giả nói rằng một ngày nào đó có thể lại muốn làm một dự án lớn, nhưng không muốn dành thêm 6 năm nữa cho một dự án
1 bình luận
Ý kiến trên Hacker News
Trang này có cả liên kết mua sách lẫn liên kết tới phiên bản trực tuyến miễn phí: https://craftinginterpreters.com/
Cuốn sách này chắc chắn đáng mua. Chỉ riêng sự chăm chút mà Nystrom dành cho thiết kế sách giấy đã đủ hấp dẫn với những ai thích ấn phẩm in, cộng thêm minh họa vẽ tay và lối viết xuất sắc, tôi nghĩ nó còn hay hơn 99% sách kỹ thuật khác
Đây là một trong những cuốn sách kỹ thuật hay nhất tôi từng đọc. Cấu trúc mà ở mỗi chương, mã nguồn tiến triển dần dần và để lại một chương trình có thể chạy được là một ý tưởng tuyệt vời, và tôi rất khâm phục tác giả vì đã thực sự làm được điều đó
Trước đây tôi từng viết thứ tương tự, nên phần trình thông dịch duyệt cây tôi chỉ đọc lướt; nhưng khi đi theo trình thông dịch bytecode dựa trên C, tôi đã học được nhiều hơn rất nhiều
Tôi tưởng bài viết này mới gần đây và nghĩ rằng ấn bản thứ hai đã ra mắt
Tôi muốn chúc mừng tác giả. Cuốn sách này là một tài liệu tuyệt vời không chỉ ở chiều sâu kỹ thuật về lĩnh vực ngôn ngữ, mà còn ở những chi tiết nhỏ trong dàn trang và đồ họa khiến người đọc tiếp tục bị cuốn vào. Nó có cảm giác như một cuốn sách sẽ còn giá trị lâu dài
Tôi bắt đầu theo dõi Crafting Interpreters từ năm 2017, và khi đi qua nửa đầu cuốn sách, tôi đã viết phần triển khai lox bằng Scala thay vì Java; qua quá trình đó, tokenizer/bộ phân tích từ vựng/parser/trình thông dịch trở nên hoàn toàn không còn bí ẩn nữa
Trước đây tôi từng nghĩ đây là một lĩnh vực kiểu viễn kiến mà chỉ một nhóm lập trình viên nhất định mới xử lý được, nhưng tôi cho rằng đó là nhờ khả năng viết tuyệt vời của Nystrom và sự am hiểu sâu sắc của ông về chủ đề này. Tôi đã bắt đầu viết trình thông dịch thứ hai bằng Rust, nhưng vì cuộc sống bận rộn nên không tiến được xa và cũng không hoàn thành. Có lẽ đã đến lúc quay lại. Bài viết này đã từ vài năm trước, nhưng tôi không biết rằng nó đã được phát hành thành sách giấy; dù không phải cách học tối ưu nhất với tôi, tôi vẫn muốn mua một cuốn để sưu tầm và ủng hộ tác giả
Đây là một trong những cuốn sách kỹ thuật máy tính hay nhất tôi từng đọc. Tôi thực sự đã đọc rất vui và học được rất nhiều
Không chỉ có nội dung kỹ thuật xuất sắc, sách còn được viết hay, thú vị, và minh họa cũng đẹp. Tôi xem đây là một thành tựu mang tính cột mốc
Đây là một cuộc phỏng vấn rất hay trong đó Bob nói về cuốn sách, đáng để nghe: https://corecursive.com/032-bob-nystrom-on-building-an-inter...
Trớ trêu là tôi cũng mất 15 tháng để đọc hết và hoàn thành cuốn sách :). Tôi rất đồng cảm với hoàn cảnh của tác giả. Đây là một cuốn sách tuyệt vời được viết bởi một tác giả tận tâm và xuất sắc, và vì quá gắn bó với cuốn sách nên tôi còn tạo một trang dựa trên những gì đã học được: https://hexmos.com/compiler
Có phải tác giả đã từ nhà thiết kế đồ họa trở thành kỹ sư biên dịch không? Thật bất ngờ và ấn tượng
Cuốn sách này đang nằm trên kệ sách của tôi. Đây sẽ là cuốn về trình thông dịch tiếp theo tôi đọc sau “Writing an interpreter in Go”, và cuốn đó khoảng 200 trang nên tôi rất thích
Tôi vừa hoàn thành bộ phân tích từ vựng bằng Rust thay vì Java, và rất mong chờ các phần còn lại. Cho đến giờ đây là một cuốn sách xuất sắc