1 điểm bởi GN⁺ 2024-10-07 | 1 bình luận | Chia sẻ qua WhatsApp

Nguồn gốc của \n

  • Khi chạy lệnh just foo, justfile sẽ ghi byte 0x0A vào một tệp có tên bar
  • just được viết bằng Rust, và trình phân tích của just chuyển đổi token chuỗi just có chứa escape sequence thành chuỗi UTF-8 thông qua hàm cook_string

Cách Rust xử lý

  • rustc xử lý mã escape trong hàm scan_escape
  • rustc được viết bằng Rust và tự biên dịch, nên việc hiểu ý nghĩa của '\n' được ủy thác cho rustc
  • Các phiên bản đầu tiên của rustc được viết bằng OCaml, và phiên bản rustc bằng OCaml xử lý escape ký tự trong lexer

Cách OCaml xử lý

  • Trình biên dịch OCaml đánh giá \n thành \010 rồi chèn kết quả đó vào
  • 0x0A là 10, nên khi trình biên dịch OCaml xử lý \n, nó thu được giá trị byte 0x0A

Kết luận

  • Khi có escape ký tự \n trong justfile, binary just sẽ ghi vào chuỗi cuối cùng với byte 0x0A được bao gồm
  • Byte 0x0A này được rustc chèn vào, và điều đó bắt đầu từ việc trình biên dịch OCaml lần đầu chèn byte 0x0A vào binary rustc

Tóm tắt của GN⁺

  • Bài viết này giải thích cách escape ký tự \n được chuyển đổi thành byte 0x0A
  • Lần theo nguồn gốc của byte 0x0A thông qua bối cảnh lịch sử của trình biên dịch Rust và OCaml
  • Cung cấp một góc nhìn thú vị về cách trình biên dịch của các ngôn ngữ lập trình xử lý escape ký tự
  • Là bài viết hữu ích để hiểu cách hoạt động của trình biên dịch Rust và OCaml

1 bình luận

 
GN⁺ 2024-10-07
Ý kiến trên Hacker News
  • Một người dùng nhắc rằng nơi đầu tiên họ đọc về ý tưởng này là ngày thứ 42 của bài viết "How I wrote a self-hosting C compiler in 40 days"

    • Bài viết đó giải thích cách trình biên dịch diễn giải "\\n" trong literal chuỗi
    • "\\n" không tự chứa thông tin mã ký tự ASCII thực tế, mà được truyền vào khi trình biên dịch biên dịch chính trình biên dịch
    • Họ nhắc rằng ký tự xuống dòng của trình biên dịch này có nguồn gốc từ GCC
  • Có ý kiến cho rằng với các hệ thống EBCDIC, cần lưu ý rằng các trình biên dịch C đời đầu đã xuất hiện trên những hệ thống không dùng ASCII

    • EBCDIC có các ký tự NextLine và LineFeed tách biệt rõ ràng
    • Đoạn mã đơn giản chạy được trên ASCII có thể thất bại trên EBCDIC
    • Trong EBCDIC, chữ thường đứng trước chữ hoa, và chữ cái đứng trước chữ số, tức thứ tự sắp xếp gần như ngược với ASCII
  • Trong chuẩn C, bảo đảm duy nhất về mã hóa ký tự là các chữ số '0'-'9' phải được ánh xạ liên tiếp theo thứ tự tăng dần

    • Về lý thuyết, một chương trình C đơn giản phải có thể biên dịch cùng một mã nguồn trên hệ ASCII hoặc EBCDIC và tạo ra cùng một đầu ra
  • Một người dùng nhắc đến bài diễn thuyết nhận giải Turing của Ken Thompson, "Reflections on Trusting Trust", và đoán rằng bài viết này có thể đã lấy cảm hứng từ đó

  • Có người thắc mắc liệu trình biên dịch clang có cùng đặc tính này không, và cho biết trong lib/Lex/LiteralSupport.cpp nó được mã hóa tường minh là 10

  • Một người dùng tự hỏi vì sao lại cần đào sâu để hiểu lý do "\\n" được mã hóa thành 10, vì điều đó với họ là điều hiển nhiên

  • Có ý kiến nói bài viết này đọc như giao điểm giữa lập trình văn chương và thơ ca, khi cố gắng mô tả quá trình tạo ra byte 0x0A qua hàng trăm chu kỳ sinh mã

  • Một người dùng nói rằng vì ngôn ngữ C nên họ từng nghĩ "\\0???" là escape bát phân, và hiểu "\\012""\\x0a" hoặc "0x0a", còn "\\010""0x08"

    • Họ đoán rằng OCaml có thể dùng escape thập phân thay vì escape bát phân
  • Có người đặt ra câu hỏi thú vị rằng nếu ASCII hoặc chuỗi không có mã escape thì mã nguồn của chúng ta sẽ trông như thế nào

  • Một người dùng nhắc đến một quy tắc trong lập trình: khi có hai cách làm, và xác suất một cách đúng còn cách kia sai là 50/50, thì lúc đầu bạn nhiều khả năng sẽ chọn nhầm cách