Làm cách nào để tạo ngôn ngữ lập trình của riêng tôi và trình biên dịch cho nó [đã đóng]


427

Tôi kỹ lưỡng với lập trình và đã bắt gặp các ngôn ngữ bao gồm BASIC, FORTRAN, COBOL, LISP, LOGO, Java, C ++, C, MATLAB, Mathematica, Python, Ruby, Perl, JavaScript, hội, v.v. Tôi không thể hiểu cách mọi người tạo ngôn ngữ lập trình và nghĩ ra trình biên dịch cho nó. Tôi cũng không thể hiểu cách mọi người tạo ra HĐH như Windows, Mac, UNIX, DOS, v.v. Một điều khác bí ẩn đối với tôi là cách mọi người tạo các thư viện như OpenGL, OpenCL, OpenCV, Ca cao, MFC, v.v. Điều cuối cùng tôi không thể tìm ra là làm thế nào các nhà khoa học nghĩ ra ngôn ngữ lắp ráp và trình biên dịch cho bộ vi xử lý. Tôi thực sự muốn học tất cả những thứ này và tôi 15 tuổi. Tôi luôn muốn trở thành một nhà khoa học máy tính như Babbage, Turing, Shannon hoặc Dennis Ritchie.


Tôi đã đọc cuốn sách về khái niệm hệ điều hành của Aho và Tanenbaum và tất cả họ chỉ thảo luận về các khái niệm và mã ở mức cao. Họ không đi sâu vào chi tiết và sắc thái và cách tạo ra trình biên dịch hoặc hệ điều hành. Tôi muốn có một sự hiểu biết cụ thể để tôi có thể tự tạo ra một thứ chứ không chỉ là sự hiểu biết về một chủ đề, semaphore, process hay phân tích cú pháp là gì. Tôi hỏi anh tôi về tất cả điều này. Anh ta là một sinh viên SB trong EECS tại MIT và không biết làm thế nào để thực sự tạo ra tất cả những thứ này trong thế giới thực. Tất cả những gì anh ta biết chỉ là sự hiểu biết về các khái niệm Thiết kế trình biên dịch và hệ điều hành giống như các khái niệm mà các bạn đã đề cập (ví dụ như Chủ đề, Đồng bộ hóa, Đồng thời, quản lý bộ nhớ, Phân tích từ điển, tạo mã trung gian, v.v.)


Nếu bạn đang ở trên Unix / Linux, bạn có thể nhận được thông tin về các công cụ chuyên dụng: lex, yaccbison.
mouviciel

Gợi ý đầu tiên của tôi là Đọc Sách Rồng của Aho. amazon.com/Compilers-Principles-Techniques-Alfred-Aho/dp/...
Julian

1
Có thể không quá hữu ích, nhưng tôi khuyên bạn nên truy cập các trang web.google.com/site/steveyegge2/blog-rant (blog của Steve Yegge) và steve-yegge.blogspot.com/ (blog khác của Steve Yegge).
KK.

3
Học càng nhiều ngôn ngữ lập trình càng tốt. Bằng cách đó bạn sẽ học được từ các khái niệm cũng như sai lầm của họ. Tại sao phải bằng lòng với những người lùn, khi bạn có thể đứng trên vai những người khổng lồ?
sbi

1
gợi ý: một trình thông dịch dễ hơn trình biên dịch; nó chỉ là một lớp "làm một cái gì đó" dựa trên văn bản đầu vào mà nó đọc từng dòng. một gợi ý khác: gắn kết điều này với sự phản chiếu và bạn có thể điều khiển các đối tượng tùy ý bằng tập lệnh của mình.
Dave Cousineau

Câu trả lời:


407

Về cơ bản, câu hỏi của bạn là "chip máy tính, bộ hướng dẫn, hệ điều hành, ngôn ngữ, thư viện và các ứng dụng được thiết kế và triển khai như thế nào?" Đó là một ngành công nghiệp trị giá hàng tỷ đô la trên toàn thế giới sử dụng hàng triệu người, nhiều người trong số họ là chuyên gia. Bạn có thể muốn tập trung câu hỏi của bạn hơn một chút.

Điều đó nói rằng, tôi có thể có một vết nứt tại:

Tôi không thể hiểu cách mọi người tạo ngôn ngữ lập trình và nghĩ ra trình biên dịch cho nó.

Đó là điều đáng ngạc nhiên đối với tôi, nhưng nhiều người xem ngôn ngữ lập trình là kỳ diệu. Khi tôi gặp mọi người trong các bữa tiệc hoặc bất cứ điều gì, nếu họ hỏi tôi làm gì, tôi nói với họ rằng tôi thiết kế ngôn ngữ lập trình và thực hiện các trình biên dịch và công cụ, và thật đáng ngạc nhiên về số lần mọi người - lập trình viên chuyên nghiệp, nhớ bạn - nói "Wow, tôi chưa bao giờ nghĩ về nó, nhưng ừ, ai đó phải thiết kế những thứ đó". Có vẻ như họ nghĩ rằng các ngôn ngữ vừa mới hình thành hoàn toàn với cơ sở hạ tầng công cụ xung quanh chúng.

Họ không chỉ xuất hiện. Ngôn ngữ được thiết kế giống như bất kỳ sản phẩm nào khác: bằng cách cẩn thận thực hiện một loạt các sự đánh đổi giữa các khả năng cạnh tranh. Các trình biên dịch và các công cụ được xây dựng giống như bất kỳ sản phẩm phần mềm chuyên nghiệp nào khác: bằng cách phá vỡ vấn đề, viết một dòng mã tại một thời điểm, và sau đó kiểm tra cái quái gì trong chương trình kết quả.

Thiết kế ngôn ngữ là một chủ đề rất lớn. Nếu bạn quan tâm đến việc thiết kế một ngôn ngữ, một nơi tốt để bắt đầu là suy nghĩ về những thiếu sót trong ngôn ngữ mà bạn đã biết. Các quyết định thiết kế thường phát sinh từ việc xem xét một khiếm khuyết thiết kế trong một sản phẩm khác.

Ngoài ra, hãy xem xét một tên miền mà bạn quan tâm, sau đó thiết kế một ngôn ngữ dành riêng cho tên miền (DSL) chỉ định giải pháp cho các vấn đề trong miền đó. Bạn đã đề cập đến LOGO; đó là một ví dụ tuyệt vời về DSL cho miền "vẽ đường". Biểu thức chính quy là DSL cho miền "tìm mẫu trong chuỗi". LINQ trong C # / VB là DSL cho miền "bộ lọc, nối, sắp xếp và dự án dữ liệu". HTML là một DSL cho miền "mô tả bố cục văn bản trên một trang", v.v. Có rất nhiều tên miền phù hợp với các giải pháp dựa trên ngôn ngữ. Một trong những mục yêu thích của tôi là Inform7, là DSL cho miền "trò chơi phiêu lưu dựa trên văn bản"; nó có lẽ là ngôn ngữ lập trình nghiêm túc cấp cao nhất mà tôi từng thấy.

Một khi bạn đã phác thảo ra những gì bạn muốn ngôn ngữ của bạn trông như thế nào, hãy cố gắng viết chính xác các quy tắc để xác định thế nào là một chương trình hợp pháp và bất hợp pháp. Thông thường bạn sẽ muốn làm điều này ở ba cấp độ:

  1. từ vựng : các quy tắc cho các từ trong ngôn ngữ là gì, các ký tự là hợp pháp, các con số trông như thế nào, v.v.
  2. cú pháp : làm thế nào để các từ của ngôn ngữ kết hợp thành các đơn vị lớn hơn? Trong C # các đơn vị lớn hơn là những thứ như biểu thức, câu lệnh, phương thức, lớp, v.v.
  3. ngữ nghĩa : đưa ra một chương trình pháp lý cú pháp, làm thế nào để bạn tìm ra những gì chương trình làm ?

Viết ra những quy tắc này chính xác nhất có thể . Nếu bạn làm tốt công việc đó thì bạn có thể sử dụng nó làm cơ sở để viết trình biên dịch hoặc trình thông dịch. Hãy xem đặc tả C # hoặc đặc tả ECMAScript để biết ý tôi là gì; chúng chứa đầy những quy tắc rất chính xác mô tả những gì tạo nên một chương trình pháp lý và làm thế nào để tìm ra những gì chúng làm.

Một trong những cách tốt nhất để bắt đầu viết trình biên dịch là viết trình biên dịch ngôn ngữ từ cấp độ cao đến ngôn ngữ cấp cao . Viết một trình biên dịch lấy các chuỗi trong ngôn ngữ của bạn và tạo ra các chuỗi trong C # hoặc JavaScript hoặc bất kỳ ngôn ngữ nào bạn biết; hãy để trình biên dịch cho ngôn ngữ đó sau đó đảm nhiệm công việc nặng nề để biến nó thành mã có thể chạy được.

Tôi viết một blog về thiết kế của C #, VB, VBScript, JavaScript và các ngôn ngữ và công cụ khác; nếu chủ đề này làm bạn quan tâm, hãy kiểm tra nó http://bloss.msdn.com/ericlippert (lịch sử) và http://ericlippert.com (hiện tại)

Đặc biệt bạn có thể thấy bài đăng này thú vị; Ở đây tôi liệt kê hầu hết các tác vụ mà trình biên dịch C # thực hiện cho bạn trong quá trình phân tích ngữ nghĩa của nó. Như bạn có thể thấy, có rất nhiều bước. Chúng tôi chia vấn đề phân tích lớn thành một loạt các vấn đề mà chúng tôi có thể giải quyết riêng lẻ.

http://bloss.msdn.com/b/ericlippert/archive/2010/02/04/how-many-passes.aspx

Cuối cùng, nếu bạn đang tìm kiếm một công việc làm công cụ này khi bạn lớn tuổi hơn, hãy xem xét đến Microsoft với tư cách là một thực tập sinh đại học và cố gắng tham gia vào bộ phận nhà phát triển. Đó là cách tôi kết thúc với công việc của mình ngày hôm nay!


Bạn đã viết về việc tối ưu hóa trình biên dịch mức độ nào sẽ không được thực hiện nữa vì CLR có thể tự động thực hiện chúng?

6
@ Thorbjørn: Hãy rõ ràng về thuật ngữ. "Trình biên dịch" là bất kỳ thiết bị nào dịch từ ngôn ngữ lập trình này sang ngôn ngữ lập trình khác. Một trong những điều tuyệt vời khi có trình biên dịch C # biến C # thành IL và trình biên dịch IL ("jitter") biến IL thành mã máy, là bạn có thể viết trình biên dịch C # sang IL (dễ dàng!) Và đặt tối ưu hóa cụ thể của bộ xử lý trong jitter. Không phải là tối ưu hóa trình biên dịch là "không được thực hiện", đó là nhóm trình biên dịch jit thực hiện chúng cho chúng tôi. Xem blog.msdn.com/b/ericlippert/archive/2009/06/11/ từ
Eric Lippert

6
@ Cyclotis04: Inform6 biên dịch thành mã Z, đây là một ví dụ cực kỳ nổi tiếng về máy ảo dựa trên mã byte. Đó là cách mà tất cả các trò chơi Infocom trong những năm 1980 có thể lớn hơn cả bộ nhớ và di động cho nhiều kiến ​​trúc; các trò chơi được biên dịch thành mã z và sau đó các trình thông dịch mã z với phân trang bộ nhớ mã được triển khai cho nhiều máy. Ngày nay tất nhiên bạn có thể chạy trình thông dịch zcode trên đồng hồ đeo tay nếu bạn cần, nhưng trở lại vào thời đó là công nghệ cao . Xem en.wikipedia.org/wiki/Z-machine để biết chi tiết.
Eric Lippert

@EricLippert Trình biên dịch không phải là một thiết bị, thiết bị là thứ chứa phần cứng. Chúng tôi có thể nói một chương trình được xác định trước có một bộ quy tắc để chuyển đổi dữ liệu đầu vào thành mã máy
dharam

2
@dhams: Một thiết bị là bất kỳ thứ gì được tạo ra cho một mục đích cụ thể. Mọi trình biên dịch tôi từng viết đều chạy trên phần cứng được xây dựng có mục đích để cho phép trình biên dịch tồn tại.
Eric Lippert

127

Bạn có thể tìm thấy Lets Build a Compiler của Jack Crenshaw một giới thiệu thú vị về cách viết trình biên dịch và ngôn ngữ hợp ngữ.

Tác giả giữ nó rất đơn giản và tập trung vào việc xây dựng chức năng thực tế.


2
Điều thú vị về phần giới thiệu của Crenshaw là nó kết thúc (spoiler: nó chưa hoàn chỉnh) chỉ là về thời gian bạn gặp phải những vấn đề khiến bạn nhận ra, hey, tôi thực sự nên thiết kế ngôn ngữ của mình trước khi bắt đầu thực hiện nó. Và sau đó bạn nói, này, nếu tôi phải viết một đặc tả ngôn ngữ đầy đủ, tại sao không làm điều đó trong một ký hiệu chính thức mà sau đó tôi có thể đưa vào một công cụ để tạo một trình phân tích cú pháp? Và sau đó bạn đang làm điều đó như những người khác.
chào

3
@kindall, bạn cần phải thực hiện bằng tay để nhận ra rằng có một lý do để sử dụng các công cụ.

72

"Tôi thực sự muốn học những thứ này". Nếu bạn nghiêm túc lâu dài:

  • Vào đại học, chuyên về kỹ thuật phần mềm. Lấy mọi lớp biên dịch bạn có thể nhận được. Những người cung cấp các lớp học được giáo dục tốt hơn và có nhiều kinh nghiệm hơn bạn; thật tốt khi có quan điểm chuyên môn của họ được sử dụng để trình bày thông tin cho bạn theo những cách bạn sẽ không bao giờ nhận được từ việc đọc mã.

  • Gắn bó với các lớp toán qua trung học và tiếp tục học đại học trong cả 4 năm. Tập trung vào toán học không chuẩn: logic, lý thuyết nhóm, siêu toán học. Điều này sẽ buộc bạn phải suy nghĩ trừu tượng. Nó sẽ cho phép bạn đọc các tài liệu lý thuyết nâng cao về biên dịch và hiểu lý do tại sao những lý thuyết đó thú vị và hữu ích. Bạn có thể bỏ qua những lý thuyết tiên tiến đó, nếu bạn mãi mãi muốn đứng sau trạng thái của nghệ thuật.

  • Thu thập / đọc các văn bản trình biên dịch chuẩn: Aho / Ullman, v.v ... Chúng chứa những gì cộng đồng thường đồng ý là những thứ cơ bản. Bạn có thể không sử dụng mọi thứ từ những cuốn sách đó, nhưng bạn nên biết nó tồn tại và bạn nên biết lý do tại sao bạn không sử dụng nó. Tôi nghĩ rằng Manynick là tuyệt vời, nhưng nó là dành cho các chủ đề khá tiên tiến.

  • Xây dựng một trình biên dịch. Bắt đầu NGAY BÂY GIỜ bằng cách xây dựng một cái thối. Điều này sẽ dạy cho bạn một số vấn đề. Xây dựng cái thứ hai. Nói lại. Kinh nghiệm này xây dựng sức mạnh tổng hợp rất lớn với việc học sách của bạn.

  • Một nơi thực sự tốt để bắt đầu là tìm hiểu về BNF (Backus Naur Form), trình phân tích cú pháp và trình tạo trình phân tích cú pháp. BNF được sử dụng phổ biến trong vùng đất của trình biên dịch và bạn không thể nói chuyện thực tế với các loại trình biên dịch đồng nghiệp của mình nếu bạn không biết điều đó.

Nếu bạn muốn có phần giới thiệu tuyệt vời đầu tiên về biên dịch và giá trị trực tiếp của BNF không chỉ là tài liệu mà là một ngôn ngữ kim loại có thể xử lý công cụ, hãy xem hướng dẫn này (không phải của tôi) về xây dựng trình biên dịch "meta" (trình biên dịch xây dựng trình biên dịch) dựa trên giấy từ năm 1964 (vâng, bạn đọc đúng) ["META II một ngôn ngữ viết trình biên dịch hướng cú pháp" của Val Schorre. (http://doi.acm.org/10.1145/800257.808896)] IMHO này là một trong những bài viết comp-sci tốt nhất từng được viết: nó dạy bạn xây dựng trình biên dịch trong 10 trang. Tôi đã học ban đầu từ bài báo này.

Những gì tôi đã viết ở trên là rất nhiều từ kinh nghiệm cá nhân, và tôi nghĩ nó đã phục vụ tôi khá tốt. YMMV, nhưng IMHO, không nhiều.


54
-1 Không có điều nào ở trên là cần thiết.
Neil Butterworth

77
@nbt Không có điều nào ở trên là cần thiết. Nhưng tất cả những điều trên giúp. Thực sự rất nhiều.
Konrad Rudolph

1
Tôi đặc biệt không đồng ý với "Học toán để suy nghĩ trừu tượng!" gợi ý. Ngay cả khi bạn nghĩ rằng "học cách suy nghĩ trừu tượng" đặc biệt hữu ích trong việc tạo ngôn ngữ lập trình và trình biên dịch của riêng bạn (tôi không - tôi thấy việc học bằng cách thực hiện sẽ hữu ích hơn nhiều so với thực hiện các đường vòng gián tiếp, vô cùng gián tiếp) , toán học không phải là lĩnh vực duy nhất có tư tưởng trừu tượng! (Tôi là một nhà toán học btw, vì vậy tôi không phủ nhận việc sử dụng toán học nói chung, chỉ là khả năng ứng dụng của nó trong trường hợp cụ thể này ...)
grautur

26
Nếu bạn muốn đọc các tài liệu kỹ thuật nâng cao về lý thuyết trình biên dịch, tốt hơn bạn nên thành thạo về mặt toán học. Bạn có thể quyết định bỏ qua tài liệu đó, và lý thuyết của bạn và do đó trình biên dịch sẽ kém hơn cho nó. Tất cả những người không tán thành ở đây đều đưa ra quan điểm rằng bạn có thể xây dựng một trình biên dịch mà không cần nhiều sự giáo dục chính thức, và tôi đồng ý. Chúng dường như ngụ ý bạn có thể xây dựng các trình biên dịch thực sự tốt mà không cần nó. Đó không phải là một vụ cá cược mà tôi quan tâm.
Ira Baxter

7
CS là một môn học thực sự hữu ích cho việc thiết kế và thực hiện ngôn ngữ. Tất nhiên không bắt buộc, nhưng đã có nhiều thập kỷ nghiên cứu có thể và nên được tận dụng, và không có lý do nào để lặp lại sai lầm của người khác.
Donal Fellows

46

Đây là một cuốn sách / khóa học trực tuyến mà bạn có thể theo dõi được gọi là Các yếu tố của hệ thống máy tính: Xây dựng một máy tính hiện đại từ các nguyên tắc đầu tiên .

Sử dụng trình giả lập, bạn thực sự xây dựng một hệ thống máy tính hoàn chỉnh từ đầu. Trong khi nhiều người bình luận đã nói rằng câu hỏi của bạn quá rộng, cuốn sách này thực sự trả lời nó trong khi vẫn rất dễ quản lý. Khi bạn hoàn thành, bạn sẽ viết một trò chơi bằng ngôn ngữ cấp cao (do bạn thiết kế), sử dụng chức năng của hệ điều hành của riêng bạn, được biên dịch thành ngôn ngữ VM (do bạn thiết kế), trình biên dịch của bạn được dịch sang ngôn ngữ hợp ngữ (do bạn thiết kế) bởi trình dịch VM của bạn, được biên dịch thành mã máy (do bạn thiết kế), chạy trên hệ thống máy tính của bạn mà bạn kết hợp từ các chip mà bạn thiết kế bằng logic boolean và một ngôn ngữ mô tả phần cứng đơn giản.

Các chương:

  1. Tổng quan về nhiên
  2. Logic Boolean
  3. Chip kết hợp
  4. Chip tuần tự
  5. Ngôn ngữ máy
  6. Kiến trúc máy tính
  7. Nhà lắp ráp
  8. Máy ảo I: Số học
  9. Máy ảo II: Điều khiển
  10. Ngôn ngữ lập trình
  11. Trình biên dịch I: Phân tích cú pháp
  12. Trình biên dịch II: Tạo mã
  13. Hệ điều hành
  14. Danh sách mục

Vui hơn để đi


Cảm ơn các chỉnh sửa, người chưa biết. Tôi đã thử một vài lần nhưng không thể tập trung suy nghĩ của mình đủ để mô tả ... nhưng tôi không muốn đề cập đến cuốn sách. Cuốn sách hiện đang trực tuyến tại liên kết Kế hoạch học tập: www1.idc.ac.il/tecs/plan.html . Nó cũng có giá rất hợp lý trực tuyến. Tận hưởng mọi người.
Joe Internet

Tôi sẽ tự đề xuất điều này ... cho người lười biếng, hãy xem phần giới thiệu 10 phút: Từ NAND đến Tetris trong 12 bước @ youtube.com/watch?v=JtXvUoPx4Qs
Richard Anthony Hein

46

Lùi lại một bước. Trình biên dịch đơn giản là một chương trình dịch tài liệu bằng một ngôn ngữ thành tài liệu bằng ngôn ngữ khác. Cả hai ngôn ngữ nên được xác định rõ ràng và cụ thể.

Các ngôn ngữ không phải là ngôn ngữ lập trình. Chúng có thể là bất kỳ ngôn ngữ nào có quy tắc có thể được viết ra. Bạn có thể đã thấy Google Dịch ; đó là trình biên dịch vì nó có thể dịch một ngôn ngữ (nói tiếng Đức) sang ngôn ngữ khác (tiếng Nhật, có lẽ).

Một ví dụ khác về trình biên dịch là một công cụ kết xuất HTML. Đầu vào của nó là một tệp HTML và đầu ra là một chuỗi các hướng dẫn để vẽ các pixel trên màn hình.

Khi hầu hết mọi người nói về trình biên dịch, họ thường đề cập đến một chương trình dịch ngôn ngữ lập trình cấp cao (như Java, C, Prolog) sang ngôn ngữ cấp thấp (mã lắp ráp hoặc mã máy). Điều đó có thể làm nản chí. Nhưng nó không tệ khi bạn nhìn nhận một cách tổng quát rằng trình biên dịch là một chương trình dịch một ngôn ngữ này sang ngôn ngữ khác.

Bạn có thể viết một chương trình đảo ngược mọi từ trong một chuỗi không? Ví dụ:

When the cat's away, the mice will play.

trở thành

nehW eht s'tac yawa, eht ecim lliw yalp.

Đó không phải là một chương trình khó viết, nhưng bạn cần suy nghĩ về một số điều:

  • Một "từ" là gì? Bạn có thể định nghĩa những ký tự tạo nên một từ?
  • Từ bắt đầu và kết thúc ở đâu?
  • Là những từ được phân tách bởi chỉ một không gian, hoặc có thể có nhiều hơn - hoặc ít hơn?
  • Có dấu chấm câu cũng cần phải được đảo ngược?
  • Điều gì về dấu câu trong một từ?
  • Điều gì xảy ra với chữ in hoa?

Các câu trả lời cho những câu hỏi này giúp ngôn ngữ được xác định rõ. Bây giờ hãy tiếp tục và viết chương trình. Xin chúc mừng, bạn vừa viết một trình biên dịch.

Làm thế nào về điều này: Bạn có thể viết một chương trình có một loạt các hướng dẫn vẽ và xuất ra một tệp PNG (hoặc JPEG) không? Có lẽ một cái gì đó như thế này:

image 100 100
background black
color red
line 20 55 93 105
color green
box 0 0 99 99

Một lần nữa, bạn sẽ cần thực hiện một số suy nghĩ để xác định ngôn ngữ:

  • Các hướng dẫn nguyên thủy là gì?
  • Điều gì đến sau từ "dòng"? Điều gì đến sau "màu sắc"? Tương tự như vậy đối với "nền", "hộp", v.v.
  • Một số là gì?
  • Là một tập tin đầu vào trống được cho phép?
  • Có thể viết hoa từ này không?
  • Số âm có được phép không?
  • Điều gì xảy ra nếu bạn không đưa ra chỉ thị "hình ảnh"?
  • Có ổn không khi chỉ định một màu?

Tất nhiên, có nhiều câu hỏi hơn để trả lời nhưng nếu bạn có thể đóng chúng xuống, bạn đã xác định một ngôn ngữ. Chương trình bạn viết để thực hiện bản dịch là, bạn đoán nó, một trình biên dịch.

Bạn thấy đấy, viết một trình biên dịch không khó lắm. Các trình biên dịch bạn đã sử dụng trong Java hoặc C chỉ là các phiên bản lớn hơn của hai ví dụ này. Vì vậy, đi cho nó! Xác định một ngôn ngữ đơn giản và viết một chương trình để làm cho ngôn ngữ đó làm một cái gì đó. Sớm muộn gì bạn cũng muốn mở rộng ngôn ngữ của mình. Chẳng hạn, bạn có thể muốn thêm các biến hoặc biểu thức số học. Trình biên dịch của bạn sẽ trở nên phức tạp hơn nhưng bạn sẽ hiểu từng bit của nó vì bạn đã tự viết nó. Đó là cách ngôn ngữ và trình biên dịch ra đời.


7
myFirstCompiler = (str) -> ("" + (str || "")). split (''). Reverse (). tham gia (''); jsfiddle.net/L7qSr
Trận chiến

21

Nếu bạn quan tâm đến thiết kế trình biên dịch, hãy xem Dragon Book (tên chính thức: Trình biên dịch: Nguyên tắc, Kỹ thuật và Công cụ). Nó được coi là một cuốn sách kinh điển về chủ đề này.


4
Lưu ý, bạn có thể cần thêm một chút kinh nghiệm thực tế để tận dụng tối đa cuốn sách này. Tham khảo tuyệt vời, mặc dù.

13
-1 Chỉ có ai chưa đọc nó mới có thể nghĩ cuốn sách rồng là tốt. và nó đặc biệt không giải quyết câu hỏi.
Neil Butterworth

33
Cuốn sách rồng? Đối với một mười lăm tuổi nhiệt tình? Tôi muốn anh ấy giữ sự nhiệt tình của mình lâu hơn.
David Thornley

1
Một cách khác dễ tiếp cận hơn: 'Ngôn ngữ lập trình thực dụng' 3e .
willjcroz

@DavidThornley Đừng tính anh ta hoàn toàn (Vâng, tôi nhận ra đây là một bài viết rất cũ). Tôi bắt đầu nghiên cứu cách các ngôn ngữ hoạt động ở tuổi 15 và đặc biệt tập trung vào các máy ảo. Bây giờ tôi 16 tuổi và sau nhiều tháng nghiên cứu, viết và viết lại, tôi đã có một trình thông dịch và trình biên dịch làm việc mà tôi hài lòng.
David


10

Đừng tin rằng có bất cứ điều gì kỳ diệu về trình biên dịch hoặc HĐH: không có. Ghi nhớ các chương trình bạn đã viết để đếm tất cả các nguyên âm trong một chuỗi, hoặc cộng các số trong một mảng? Một trình biên dịch không khác nhau về khái niệm; nó chỉ lớn hơn nhiều

Mỗi chương trình có ba giai đoạn:

  1. đọc một số thứ
  2. xử lý công cụ đó: dịch dữ liệu đầu vào sang dữ liệu đầu ra
  3. viết một số thứ khác - dữ liệu đầu ra

Hãy nghĩ về nó: đầu vào của trình biên dịch là gì? Một chuỗi các ký tự từ một tệp nguồn.

Đầu ra từ trình biên dịch là gì? Một chuỗi byte đại diện cho các hướng dẫn máy cho máy tính đích.

Vậy giai đoạn "quy trình" của nhà soạn nhạc là gì? Giai đoạn đó làm gì?

Nếu bạn xem xét rằng trình biên dịch - giống như bất kỳ chương trình nào khác - bao gồm ba giai đoạn, bạn sẽ có một ý tưởng tốt như thế nào một trình biên dịch được xây dựng.


3
Như Neil nói, đúng nhưng không hữu ích. Các khía cạnh trình biên dịch cơ bản như một ngữ pháp đệ quy và bảng biểu tượng không rõ ràng bằng trực giác.
Mason Wheeler

1
@Mason Wheeler: Tôi nghĩ rằng bất kỳ ai thực sự khao khát viết một trình biên dịch (và thiết kế ngôn ngữ đích?) Rất có thể nghĩ rằng ngữ pháp đệ quybảng biểu tượng là những khái niệm khá cơ bản.
FumbleFingers

8

Tôi không phải là một chuyên gia, nhưng đây là cú đâm của tôi:

Bạn dường như không hỏi về việc viết một trình biên dịch, chỉ là một trình biên dịch. Đây không phải là phép thuật.

Ăn cắp câu trả lời của ai đó từ SO ( https://stackoverflow.com/questions/3826692/how-do-i-translate-assinstall-to-binary ), lắp ráp trông như thế này:

label:  LDA #$00
        JMP label

Sau đó, bạn chạy nó thông qua một trình biên dịch chương trình, và biến thành một cái gì đó như thế này:

$A9 $00
$4C $10 $00

Chỉ có tất cả bị đè bẹp, như thế này:

$A9 $00 $4C $10 $00

Nó thực sự không phải là phép thuật.

Bạn không thể viết nó trong notepad, vì notepad sử dụng ASCII (không phải hex). Bạn sẽ sử dụng trình soạn thảo hex hoặc đơn giản là viết các byte ra theo chương trình. Bạn viết hex đó ra một tệp, đặt tên là "a.exe" hoặc "a.out", sau đó báo cho HĐH chạy nó.

Tất nhiên, CPU và hệ điều hành hiện đại thực sự khá phức tạp, nhưng đó là ý tưởng cơ bản.

Nếu bạn muốn viết một trình biên dịch mới, đây là cách nó được thực hiện:

1) Viết một ngôn ngữ được giải thích bằng cách sử dụng một cái gì đó giống như ví dụ máy tính trong pyparsing (hoặc bất kỳ khung phân tích cú pháp tốt nào khác). Điều đó sẽ giúp bạn tăng tốc về những điều cơ bản của phân tích cú pháp.

2) Viết một dịch giả. Dịch ngôn ngữ của bạn sang, nói, Javascript. Bây giờ ngôn ngữ của bạn sẽ chạy trong một trình duyệt.

3) Viết một trình dịch sang một cấp độ thấp hơn, như LLVM, C hoặc hội.

Bạn có thể dừng ở đây, đây là một trình biên dịch. Nó không phải là một trình biên dịch tối ưu hóa, nhưng đó không phải là câu hỏi. Bạn cũng có thể cần xem xét việc viết một trình liên kết và trình biên dịch, nhưng bạn có thực sự muốn không?

4) (Mất trí) Viết trình tối ưu hóa. Các nhóm lớn làm việc trong nhiều thập kỷ về điều này.

4) (Sane) Tham gia vào một cộng đồng hiện có. GCC, LLVM, PyPy, nhóm nòng cốt làm việc trên bất kỳ trình thông dịch nào.


8

Một số người khác đã đưa ra câu trả lời tuyệt vời. Tôi sẽ chỉ thêm một vài gợi ý. Đầu tiên, một cuốn sách hay cho những gì bạn đang cố gắng làm là các văn bản Thực hiện Trình biên dịch hiện đại của Appel (hãy chọn C , Java hoặc ML chuẩn ). Cuốn sách này sẽ đưa bạn qua việc thực hiện đầy đủ trình biên dịch cho một ngôn ngữ đơn giản, Tiger, đến MIPS lắp ráp có thể chạy trong trình giả lập, cùng với thư viện hỗ trợ thời gian chạy tối thiểu. Đối với một lần vượt qua mọi thứ cần thiết để làm cho một ngôn ngữ được biên dịch hoạt động, đó là một cuốn sách khá hay 1 .

Appel sẽ hướng dẫn bạn cách biên dịch một ngôn ngữ được thiết kế sẵn, nhưng không dành nhiều thời gian cho các tính năng ngôn ngữ khác nhau hoặc cách nghĩ về chúng theo giá trị tương đối của chúng để thiết kế ngôn ngữ của bạn. Đối với khía cạnh đó, Ngôn ngữ lập trình: Khái niệm & Xây dựng là khá. Các khái niệm, kỹ thuật và mô hình lập trình máy tính cũng là một cuốn sách tốt để suy nghĩ sâu sắc về thiết kế ngôn ngữ, mặc dù nó làm như vậy trong bối cảnh của một ngôn ngữ duy nhất ( Oz ).

Cuối cùng, tôi đã đề cập rằng Appel có văn bản của mình bằng C, Java và ML chuẩn - nếu bạn nghiêm túc về ngôn ngữ lập trình và xây dựng trình biên dịch, tôi khuyên bạn nên học ML và sử dụng phiên bản Appel đó. Các ngôn ngữ gia đình ML có hệ thống loại mạnh chủ yếu là chức năng - các tính năng sẽ khác với nhiều ngôn ngữ khác, vì vậy việc học chúng nếu bạn chưa biết một ngôn ngữ chức năng sẽ trau dồi ngôn ngữ của bạn. Ngoài ra, các tư duy chức năng và khớp mẫu của chúng rất phù hợp với các loại thao tác bạn cần thực hiện trong trình biên dịch, do đó trình biên dịch viết bằng ngôn ngữ dựa trên ML thường ngắn hơn và dễ hiểu hơn trình biên dịch viết bằng C, Java, hoặc các ngôn ngữ tương tự. Cuốn sách của Harpertrên Standard ML là một hướng dẫn khá hay để bạn bắt đầu; làm việc thông qua đó sẽ chuẩn bị cho bạn đưa vào cuốn sách triển khai Trình biên dịch ML chuẩn của Appel. Nếu bạn học Standard ML, thì cũng sẽ khá dễ dàng để chọn OCaml cho công việc sau này; IMO, nó có công cụ tốt hơn cho lập trình viên làm việc (tích hợp sạch hơn với môi trường HĐH xung quanh, tạo ra các chương trình thực thi dễ dàng và có một số công cụ xây dựng trình biên dịch ngoạn mục như ulex và Menhir).


1 Để tham khảo lâu dài, tôi thích Sách Rồng hơn, vì nó có nhiều chi tiết hơn về những điều tôi có thể đề cập đến như hoạt động bên trong của các thuật toán phân tích cú pháp và có phạm vi bao quát rộng hơn về các cách tiếp cận khác nhau, nhưng cuốn sách của Appel rất tốt cho một vượt qua đầu tiên. Về cơ bản, Appel dạy cho bạn một cách để làm mọi thứ toàn bộ thông qua trình biên dịch và hướng dẫn bạn thông qua nó. Dragon Book bao gồm các phương án thiết kế khác nhau chi tiết hơn, nhưng cung cấp ít hướng dẫn hơn về cách làm cho một cái gì đó hoạt động.


Đã chỉnh sửa : thay thế tham chiếu Aho không chính xác bằng Sethi, đề cập đến CTMCP.


Ugh, tôi đã có Essentials Of Languages ​​Languages ​​cho lớp phiên dịch viên đại học của tôi. Nó quá tệ. Tôi thậm chí thích chương trình cá nhân và không bận tâm đến cú pháp, chính các tác giả giải thích kém về các khái niệm đã hủy hoại nó cho tôi.
Greg Guida

Tôi thích việc biên soạn Appel với các phần tiếp theo nhưng tôi đã tìm thấy những cuốn sách của ông được thừa nhận rất nhiều kiến ​​thức trước đó.
Jon Harrop

6

Tôi đã phải tạo một trình biên dịch cho lớp học ở trường đại học.

Những điều cơ bản để thực hiện điều này không phức tạp như bạn nghĩ. Bước đầu tiên là tạo ngữ pháp của bạn. Hãy nghĩ về ngữ pháp tiếng Anh. Theo cùng một cách bạn có thể phân tích một câu nếu nó có chủ ngữ và vị ngữ. Để biết thêm về điều đó đọc về Ngữ pháp miễn phí ngữ cảnh .

Một khi bạn có ngữ pháp (quy tắc ngôn ngữ của bạn), viết một trình biên dịch cũng đơn giản như chỉ tuân theo các quy tắc đó. Trình biên dịch thường dịch sang mã máy, nhưng trừ khi bạn muốn học x86, tôi khuyên bạn có thể xem MIPS hoặc tạo Máy ảo của riêng bạn.

Trình biên dịch thường có hai phần, trình quét và trình phân tích cú pháp. Về cơ bản, máy quét đọc mã và tách nó ra thành các mã thông báo. Trình phân tích cú pháp xem xét cấu trúc của các mã thông báo đó. Sau đó, trình biên dịch đi qua và tuân theo một số quy tắc khá đơn giản để chuyển đổi nó thành bất kỳ mã nào bạn cần có trong đó (lắp ráp, mã trung gian như mã byte, v.v.). Nếu bạn chia nó thành các phần nhỏ hơn và nhỏ hơn, điều này cuối cùng sẽ không gây khó khăn chút nào.

Chúc may mắn!


8
Khái niệm đơn giản? Đúng. Thật ra đơn giản? Số
Neil Butterworth

7
Ừm. Trình biên dịch, sau khi quét / phân tích cú pháp cần thực hiện kiểm tra kiểu / suy luận, tối ưu hóa, phân bổ đăng ký, v.v. Các bước này là bất cứ điều gì ngoài việc đơn giản. (Khi sử dụng mã được giải thích, bạn chỉ cần trì hoãn các phần này đến giai đoạn thời gian chạy.)
Macke

Không có phiếu bầu nào từ tôi: trong khi các trình biên dịch có hai phần cơ bản, một trong số đó là xây dựng một mô tả trừu tượng của chương trình (thường được chia thành quét và phân tích cú pháp) và phần còn lại để viết lại một phiên bản mô tả trừu tượng đó trong một số hình thức khác (ví dụ, mã máy). (Lưu ý bên lề: Tối ưu hóa trình biên dịch thường cố gắng cải thiện mô tả trừu tượng trước khi viết nó ra, nhưng đó là một sàng lọc.)
Donal Fellows

6

sách của Petzold là một giới thiệu tuyệt vời cho những người không chuyên về kỹ thuật và công nghệ bắt đầu từ những nguyên tắc đầu tiên. Nó rất dễ đọc và rộng lớn trong phạm vi của nó mà không bị sa lầy quá nhiều.

Bây giờ tôi đã viết nó, tôi sẽ phải đọc lại nó.



5

Có những câu trả lời tuyệt vời trong chủ đề này, nhưng tôi chỉ muốn thêm câu hỏi của mình vì tôi cũng đã từng có cùng một câu hỏi. (Ngoài ra, tôi muốn chỉ ra rằng cuốn sách được đề xuất bởi Joe - Internet là một tài nguyên tuyệt vời.)

Đầu tiên là câu hỏi làm thế nào để một máy tính làm việc? Đây là cách thực hiện: Đầu vào -> Tính toán -> Đầu ra.

Đầu tiên hãy xem xét phần Compute của người Bỉ. Chúng ta sẽ xem xét cách thức đầu vào và đầu ra hoạt động sau này.

Một máy tính về cơ bản bao gồm bộ xử lý (hoặc CPU) và một số bộ nhớ (hoặc RAM). Bộ nhớ là tập hợp các vị trí mà mỗi vị trí có thể lưu trữ một số bit hữu hạn và mỗi vị trí bộ nhớ như vậy có thể được tham chiếu bằng một số, đây được gọi là địa chỉ của vị trí bộ nhớ. Bộ xử lý là một tiện ích có thể tìm nạp dữ liệu từ bộ nhớ, thực hiện một số thao tác dựa trên dữ liệu và ghi lại một số dữ liệu trở lại bộ nhớ. Làm thế nào để bộ xử lý tìm ra những gì cần đọc và phải làm gì sau khi đọc dữ liệu từ bộ nhớ?

Để trả lời điều này, chúng ta cần hiểu cấu trúc của bộ xử lý. Sau đây là một cái nhìn khá đơn giản. Một bộ xử lý về cơ bản bao gồm hai phần. Một là tập hợp các vị trí bộ nhớ được xây dựng bên trong bộ xử lý đóng vai trò là bộ nhớ hoạt động. Chúng được gọi là đăng ký thành công. Thứ hai là một loạt các máy móc điện tử được chế tạo để thực hiện một số thao tác nhất định bằng cách sử dụng dữ liệu trong các thanh ghi. Có hai thanh ghi đặc biệt được gọi là Chương trình Counter Counter hoặc máy tính và Đăng ký Hướng dẫn Đăng ký hoặc ir. Bộ xử lý coi bộ nhớ sẽ được phân chia thành ba phần. Phần đầu tiên là bộ nhớ chương trình trên mạng, nơi lưu trữ chương trình máy tính đang được thực thi. Thứ hai là bộ nhớ dữ liệu trên mạng. Thứ ba được sử dụng cho một số mục đích đặc biệt, chúng ta sẽ nói về nó sau. Bộ đếm chương trình chứa vị trí của lệnh tiếp theo để đọc từ Bộ nhớ chương trình. Bộ đếm lệnh chứa một số tham chiếu đến thao tác hiện tại đang được thực hiện. Mỗi hoạt động mà bộ xử lý có thể thực hiện được giới thiệu bởi một số gọi là opcode của hoạt động. Cách thức hoạt động của một máy tính là nó đọc vị trí bộ nhớ được Bộ đếm chương trình tham chiếu vào Thanh ghi lệnh (và nó tăng Bộ đếm chương trình để nó trỏ đến vị trí bộ nhớ của lệnh tiếp theo). Tiếp theo, nó đọc Sổ đăng ký hướng dẫn và thực hiện thao tác mong muốn. Ví dụ, hướng dẫn có thể là đọc một vị trí bộ nhớ cụ thể vào một thanh ghi hoặc ghi vào một số thanh ghi hoặc thực hiện một số thao tác bằng cách sử dụng các giá trị của hai thanh ghi và ghi đầu ra vào thanh ghi thứ ba. Bộ đếm lệnh chứa một số tham chiếu đến thao tác hiện tại đang được thực hiện. Mỗi hoạt động mà bộ xử lý có thể thực hiện được giới thiệu bởi một số gọi là opcode của hoạt động. Cách thức hoạt động của một máy tính là nó đọc vị trí bộ nhớ được Bộ đếm chương trình tham chiếu vào Thanh ghi lệnh (và nó tăng Bộ đếm chương trình để nó trỏ đến vị trí bộ nhớ của lệnh tiếp theo). Tiếp theo, nó đọc Sổ đăng ký hướng dẫn và thực hiện thao tác mong muốn. Ví dụ, hướng dẫn có thể là đọc một vị trí bộ nhớ cụ thể vào một thanh ghi hoặc ghi vào một số thanh ghi hoặc thực hiện một số thao tác bằng cách sử dụng các giá trị của hai thanh ghi và ghi đầu ra vào thanh ghi thứ ba. Bộ đếm lệnh chứa một số tham chiếu đến thao tác hiện tại đang được thực hiện. Mỗi hoạt động mà bộ xử lý có thể thực hiện được giới thiệu bởi một số gọi là opcode của hoạt động. Cách thức hoạt động của một máy tính là nó đọc vị trí bộ nhớ được Bộ đếm chương trình tham chiếu vào Thanh ghi lệnh (và nó tăng Bộ đếm chương trình để nó trỏ đến vị trí bộ nhớ của lệnh tiếp theo). Tiếp theo, nó đọc Sổ đăng ký hướng dẫn và thực hiện thao tác mong muốn. Ví dụ, hướng dẫn có thể là đọc một vị trí bộ nhớ cụ thể vào một thanh ghi hoặc ghi vào một số thanh ghi hoặc thực hiện một số thao tác bằng cách sử dụng các giá trị của hai thanh ghi và ghi đầu ra vào thanh ghi thứ ba. Mỗi hoạt động mà bộ xử lý có thể thực hiện được giới thiệu bởi một số gọi là opcode của hoạt động. Cách thức hoạt động của một máy tính là nó đọc vị trí bộ nhớ được Bộ đếm chương trình tham chiếu vào Thanh ghi lệnh (và nó tăng Bộ đếm chương trình để nó trỏ đến vị trí bộ nhớ của lệnh tiếp theo). Tiếp theo, nó đọc Sổ đăng ký hướng dẫn và thực hiện thao tác mong muốn. Ví dụ, hướng dẫn có thể là đọc một vị trí bộ nhớ cụ thể vào một thanh ghi hoặc ghi vào một số thanh ghi hoặc thực hiện một số thao tác bằng cách sử dụng các giá trị của hai thanh ghi và ghi đầu ra vào thanh ghi thứ ba. Mỗi hoạt động mà bộ xử lý có thể thực hiện được giới thiệu bởi một số gọi là opcode của hoạt động. Cách thức hoạt động của một máy tính là nó đọc vị trí bộ nhớ được Bộ đếm chương trình tham chiếu vào Thanh ghi lệnh (và nó tăng Bộ đếm chương trình để nó trỏ đến vị trí bộ nhớ của lệnh tiếp theo). Tiếp theo, nó đọc Sổ đăng ký hướng dẫn và thực hiện thao tác mong muốn. Ví dụ, hướng dẫn có thể là đọc một vị trí bộ nhớ cụ thể vào một thanh ghi hoặc ghi vào một số thanh ghi hoặc thực hiện một số thao tác bằng cách sử dụng các giá trị của hai thanh ghi và ghi đầu ra vào thanh ghi thứ ba. Cách thức hoạt động của một máy tính là nó đọc vị trí bộ nhớ được Bộ đếm chương trình tham chiếu vào Thanh ghi lệnh (và nó tăng Bộ đếm chương trình để nó trỏ đến vị trí bộ nhớ của lệnh tiếp theo). Tiếp theo, nó đọc Sổ đăng ký hướng dẫn và thực hiện thao tác mong muốn. Ví dụ, hướng dẫn có thể là đọc một vị trí bộ nhớ cụ thể vào một thanh ghi hoặc ghi vào một số thanh ghi hoặc thực hiện một số thao tác bằng cách sử dụng các giá trị của hai thanh ghi và ghi đầu ra vào thanh ghi thứ ba. Cách thức hoạt động của một máy tính là nó đọc vị trí bộ nhớ được Bộ đếm chương trình tham chiếu vào Thanh ghi lệnh (và nó tăng Bộ đếm chương trình để nó trỏ đến vị trí bộ nhớ của lệnh tiếp theo). Tiếp theo, nó đọc Sổ đăng ký hướng dẫn và thực hiện thao tác mong muốn. Ví dụ, hướng dẫn có thể là đọc một vị trí bộ nhớ cụ thể vào một thanh ghi hoặc ghi vào một số thanh ghi hoặc thực hiện một số thao tác bằng cách sử dụng các giá trị của hai thanh ghi và ghi đầu ra vào thanh ghi thứ ba.

Bây giờ làm thế nào để máy tính thực hiện đầu vào / đầu ra? Tôi sẽ cung cấp một câu trả lời rất đơn giản. Xem http://en.wikipedia.org/wiki/Input/output http://en.wikipedia.org/wiki/Interrupt. để biết thêm Nó sử dụng hai thứ, phần thứ ba của bộ nhớ và thứ gọi là Ngắt. Mỗi thiết bị được gắn vào máy tính phải có khả năng trao đổi dữ liệu với bộ xử lý. Nó làm như vậy bằng cách sử dụng phần thứ ba của bộ nhớ được đề cập trước đó. Bộ xử lý phân bổ một lát bộ nhớ cho mỗi thiết bị và thiết bị và bộ xử lý giao tiếp qua lát bộ nhớ đó. Nhưng làm thế nào để bộ xử lý biết vị trí nào đề cập đến thiết bị nào và khi nào thiết bị cần trao đổi dữ liệu? Đây là nơi gián đoạn xuất hiện. Một ngắt về cơ bản là tín hiệu cho bộ xử lý để tạm dừng những gì hiện tại và lưu tất cả các thanh ghi của nó vào một vị trí đã biết và sau đó bắt đầu làm việc khác. Có nhiều ngắt, mỗi ngắt được xác định bởi một số duy nhất. Đối với mỗi ngắt, có một chương trình đặc biệt liên quan đến nó. Khi ngắt xảy ra, bộ xử lý thực thi chương trình tương ứng với ngắt. Bây giờ tùy thuộc vào bios và cách các thiết bị phần cứng được kết nối với bo mạch chủ máy tính, mọi thiết bị đều có một ngắt duy nhất và một lát bộ nhớ. Trong khi khởi động hệ điều hành với sự trợ giúp của bios xác định vị trí ngắt và bộ nhớ của từng thiết bị và thiết lập các chương trình đặc biệt cho ngắt để xử lý đúng thiết bị. Vì vậy, khi một thiết bị cần một số dữ liệu hoặc muốn gửi một số dữ liệu, nó báo hiệu một sự gián đoạn. Bộ xử lý tạm dừng những gì nó đang làm, xử lý ngắt và sau đó quay lại với những gì nó đang làm. Có rất nhiều loại ngắt, chẳng hạn như đối với hdd, bàn phím, v.v. Một điều quan trọng là bộ đếm thời gian hệ thống, gọi một ngắt trong khoảng thời gian đều đặn. Ngoài ra có các opcodes có thể kích hoạt các ngắt, được gọi là ngắt phần mềm.

Bây giờ chúng ta gần như có thể hiểu làm thế nào một hệ điều hành hoạt động. Khi nó khởi động, os sẽ thiết lập ngắt hẹn giờ, để nó kiểm soát os một cách đều đặn. Nó cũng thiết lập các ngắt khác để xử lý các thiết bị khác, v.v ... Bây giờ khi máy tính đang chạy một loạt các chương trình và ngắt hẹn giờ xảy ra, os sẽ kiểm soát và thực hiện các tác vụ quan trọng như quản lý quy trình, quản lý bộ nhớ, v.v. một cách trừu tượng để các chương trình truy cập vào các thiết bị phần cứng, thay vì để chúng truy cập trực tiếp vào các thiết bị. Khi một chương trình muốn truy cập vào một thiết bị, nó sẽ gọi một số mã do os cung cấp để sau đó nói chuyện với thiết bị. Có rất nhiều lý thuyết liên quan đến những vấn đề liên quan đến đồng thời, chủ đề, khóa, quản lý bộ nhớ, v.v.

Bây giờ, về mặt lý thuyết, người ta có thể viết một chương trình trực tiếp bằng cách sử dụng opcodes. Đây là những gì được gọi là mã máy. Điều này rõ ràng là rất đau đớn. Bây giờ, một ngôn ngữ lắp ráp cho bộ xử lý không có gì khác ngoài việc ghi nhớ các mã này, giúp việc viết chương trình dễ dàng hơn. Trình biên dịch đơn giản là một chương trình lấy một chương trình được viết bằng cách lắp ráp và thay thế các ký hiệu bằng các opcodes thích hợp.

Làm thế nào để đi về thiết kế một bộ xử lý và ngôn ngữ lắp ráp. Để biết rằng bạn phải đọc một số sách về kiến ​​trúc máy tính. (xem chương 1-7 của cuốn sách được giới thiệu bởi joe-internet). Điều này liên quan đến việc tìm hiểu về đại số boolean, cách xây dựng các mạch tổ hợp đơn giản để thêm, nhân v.v., cách xây dựng bộ nhớ và mạch tuần tự, cách xây dựng bộ vi xử lý, v.v.

Bây giờ làm thế nào để viết langauges máy tính. Người ta có thể bắt đầu bằng cách viết một trình biên dịch mã đơn giản trong mã máy. Sau đó sử dụng trình biên dịch chương trình đó để viết trình biên dịch cho một tập hợp con đơn giản của C. Sau đó sử dụng tập hợp con đó của C để viết một phiên bản hoàn chỉnh hơn của C. Cuối cùng sử dụng C để viết một ngôn ngữ phức tạp hơn như python hoặc C ++. Tất nhiên để viết một ngôn ngữ, trước tiên bạn phải thiết kế nó (giống như cách bạn xem xét bộ xử lý). Một lần nữa nhìn vào một số sách giáo khoa về điều đó.

Và làm thế nào để viết một os. Đầu tiên bạn nhắm mục tiêu một nền tảng như x86. Sau đó, bạn tìm ra cách nó khởi động và khi nào os của bạn sẽ được gọi. Một pc điển hình khởi động theo cách này. Nó khởi động và bios thực hiện một số thử nghiệm. Sau đó, bios đọc khu vực đầu tiên của hdd và tải nội dung đến một vị trí cụ thể trong bộ nhớ. Sau đó, nó thiết lập cpu để bắt đầu thực hiện dữ liệu được tải này. Đây là điểm bạn os được gọi. Một os điển hình tại thời điểm này tải phần còn lại của bộ nhớ. Sau đó, nó khởi tạo các thiết bị và thiết lập những thứ khác và cuối cùng nó chào đón bạn bằng màn hình đăng nhập.

Vì vậy, để viết một hệ điều hành, bạn phải viết chương trình khởi động bộ nạp. Sau đó, bạn phải viết mã để xử lý các ngắt và thiết bị. Sau đó, bạn phải viết tất cả mã cho quản lý quy trình, quản lý thiết bị, vv Sau đó, bạn phải viết một api cho phép các chương trình chạy trong hệ điều hành của bạn để truy cập các thiết bị và các tài nguyên khác. Và cuối cùng, bạn phải viết mã đọc một chương trình từ đĩa, thiết lập nó thành một quá trình và bắt đầu thực thi nó.

Tất nhiên câu trả lời của tôi được đơn giản hóa quá mức và có lẽ ít sử dụng thực tế. Để bảo vệ tôi bây giờ tôi là một sinh viên tốt nghiệp về lý thuyết, vì vậy tôi đã quên rất nhiều những điều này. Nhưng bạn có thể google rất nhiều những thứ này và tìm hiểu thêm.


4

Tôi có thể nhớ một điểm trong sự nghiệp lập trình của mình khi tôi ở trong tình trạng bối rối tương tự với bạn: Tôi đã đọc về lý thuyết khá nhiều, cuốn sách Rồng, cuốn sách Tiger (màu đỏ), nhưng vẫn không có nhiều một đầu mối làm thế nào để đặt tất cả lại với nhau.

Những gì đã gắn kết nó với nhau là tìm một dự án cụ thể để làm (và sau đó phát hiện ra rằng tôi chỉ cần một tập hợp nhỏ của tất cả các lý thuyết).

Máy ảo Java cung cấp cho tôi một điểm khởi đầu tốt: về mặt khái niệm nó là một "bộ xử lý" nhưng nó được trừu tượng hóa cao từ các chi tiết lộn xộn của các CPU thực tế. Nó cũng có một phần quan trọng và thường bị bỏ qua trong quá trình học tập: tách rời mọi thứ trước khi đặt chúng lại với nhau (giống như những đứa trẻ thường làm với các đài phát thanh ngày xưa).

Chơi xung quanh với trình dịch ngược và lớp Hello, World trong Java. Đọc đặc tả JVM và cố gắng hiểu những gì đang diễn ra. Điều này sẽ cung cấp cho bạn cái nhìn sâu sắc có căn cứ về những gì trình biên dịch đang làm .

Sau đó chơi xung quanh với mã tạo ra lớp Hello, World. (Thực tế, bạn đang tạo một trình biên dịch dành riêng cho ứng dụng, cho một ngôn ngữ chuyên môn cao mà bạn chỉ có thể nói Hello, World.)

Hãy thử viết mã sẽ có thể đọc trong Hello, World được viết bằng một số ngôn ngữ khác và xuất ra cùng một lớp. Làm cho nó để bạn có thể thay đổi chuỗi từ "Xin chào, Thế giới" sang một thứ khác.

Bây giờ hãy thử biên dịch (trong Java) một lớp tính toán một số biểu thức số học, như "2 * (3 + 4)". Tách lớp này ra, viết một "trình biên dịch đồ chơi" có thể đặt nó lại với nhau.


3

1) Các bài giảng video tuyệt vời từ Đại học Washington:

Xây dựng trình biên dịch CSE P 501 - Mùa thu 2009 www.cs.washington.edu/education/cifts/csep501/09au/lectures/video.html *

2) SICP http://groups.csail.mit.edu/mac/groupes/6.001/abelson-sussman-lectures/ Và cuốn sách có cùng tên. Đây thực sự là một điều bắt buộc đối với bất kỳ kỹ sư phần mềm nào ngoài kia.

3) Ngoài ra, về lập trình chức năng, Haskell, tính toán lambda, ngữ nghĩa (bao gồm cả biểu thị) và triển khai trình biên dịch cho các ngôn ngữ chức năng. Bạn có thể bắt đầu từ 2005-SS-FP.V10.2005-05-24.HDV nếu bạn đã biết Haskell. Video Uxx là câu trả lời. Vui lòng theo dõi video Vxx trước.

http://video.s-inf.de/#FP.2005-SS-Giesl.(COt).HD_Videoaufzeichnung

(video bằng tiếng Anh, các khóa học khác bằng tiếng Đức.)

  • người dùng mới chỉ có thể đăng tối đa hai siêu liên kết.

3

ANTLR là một điểm khởi đầu tốt. Đó là một khung tạo ngôn ngữ, tương tự như Lex và Yacc. Có một gui gọi là ANTLRWorks giúp đơn giản hóa quy trình.

Trong thế giới .NET có Dynamic Language Runtime có thể được sử dụng để tạo mã trong thế giới .NET. Tôi đã viết một ngôn ngữ biểu thức gọi là Zentrum tạo mã bằng DLR. Nó sẽ chỉ cho bạn cách phân tích cú pháp và thực hiện các biểu thức gõ tĩnh và động.


2

Để có một giới thiệu đơn giản về cách trình biên dịch hoạt động và cách tạo ngôn ngữ lập trình của riêng bạn, tôi muốn giới thiệu cuốn sách mới http://createyourproglang.com tập trung nhiều hơn vào lý thuyết thiết kế ngôn ngữ mà không cần phải biết về các bộ điều khiển CPU / CPU, ví dụ như từ vựng, bộ phân tích cú pháp , thông dịch viên, vv

Nó sử dụng các công cụ tương tự đã được sử dụng để tạo ra các ngôn ngữ lập trình Coffee ScriptFancy phổ biến gần đây .


2

Nếu tất cả những gì bạn nói là đúng, bạn có hồ sơ của một nhà nghiên cứu đầy triển vọng và một sự hiểu biết cụ thể chỉ có thể có được một cách: nghiên cứu. Và tôi không nói " Đọc tất cả những cuốn sách khoa học máy tính cấp cao (đặc biệt là những cuốn sách này ) được viết bởi thiên tài này !"; Ý tôi là: bạn phải ở cùng với những người có trình độ cao để trở thành một nhà khoa học máy tính như Charles Babbage, Alan Turing, Claude Shannon hoặc Dennis Ritchie. Tôi không khinh thường những người tự học (tôi là một trong số họ) nhưng không có nhiều người như bạn ngoài kia. Tôi nghiêm túc giới thiệu Chương trình Hệ thống Tượng trưng (SSP) tại Đại học Stanford . Như trang web của họ nói:

Chương trình Hệ thống biểu tượng (SSP) tại Đại học Stanford tập trung vào máy tính và trí tuệ: hệ thống nhân tạo và tự nhiên sử dụng các biểu tượng để thể hiện thông tin. SSP tập hợp các sinh viên và giảng viên quan tâm đến các khía cạnh khác nhau của mối quan hệ giữa người và máy tính, bao gồm ...

  • khoa học nhận thức : nghiên cứu trí thông minh của con người, ngôn ngữ tự nhiên và bộ não như các quá trình tính toán;
  • trí tuệ nhân tạo : ban cho các máy tính có hành vi và hiểu biết giống con người;
  • tương tác giữa người và máy tính : thiết kế phần mềm máy tính và giao diện hoạt động tốt với người dùng.

2

Tôi sẽ đề xuất một cái gì đó ngoài lĩnh vực bên trái: tìm hiểu Python (hoặc có lẽ là Ruby, nhưng tôi có nhiều kinh nghiệm hơn về Python nên đó là những gì tôi sẽ thảo luận). Và không chỉ đơn thuần là lao vào nó, mà thực sự làm quen với nó ở mức độ sâu sắc.

Có một số lý do tôi đề nghị điều này:

  1. Python là một ngôn ngữ được thiết kế đặc biệt. Mặc dù nó có một vài mụn cóc, nhưng nó có ít IMHO hơn nhiều ngôn ngữ khác. Nếu bạn là một nhà thiết kế ngôn ngữ vừa chớm nở, thật tốt khi tiếp xúc với nhiều ngôn ngữ tốt nhất có thể.

  2. Việc triển khai tiêu chuẩn của Python (CPython) là nguồn mở và được ghi chép tốt, giúp dễ hiểu ngôn ngữ hoạt động dưới mui xe.

  3. Python được biên dịch thành một mã byte đơn giản, dễ hiểu hơn lắp ráp và hoạt động tương tự trên tất cả các nền tảng mà Python chạy trên đó. Vì vậy, bạn sẽ tìm hiểu về biên dịch (vì Python biên dịch mã nguồn của bạn thành mã byte) và giải thích (vì mã byte này được diễn giải trong máy ảo Python).

  4. Python có rất nhiều tính năng mới được đề xuất, được ghi lại trong các PEP được đánh số (Đề xuất cải tiến Python). PEP thú vị khi đọc để xem các nhà thiết kế ngôn ngữ đã cân nhắc việc triển khai một tính năng như thế nào trước khi chọn cách họ thực sự làm điều đó. (PEP vẫn đang được xem xét đặc biệt thú vị về vấn đề này.)

  5. Python có sự kết hợp các tính năng từ các mô hình lập trình khác nhau, vì vậy bạn sẽ tìm hiểu về nhiều cách khác nhau để tiếp cận giải quyết vấn đề và có nhiều công cụ để xem xét bao gồm cả ngôn ngữ của bạn.

  6. Python giúp dễ dàng mở rộng ngôn ngữ theo nhiều cách khác nhau với các công cụ trang trí, siêu dữ liệu, móc nhập, v.v. để bạn có thể chơi với các tính năng ngôn ngữ mới đến một mức độ mà không thực sự rời khỏi ngôn ngữ. (Bên cạnh: các khối mã là một đối tượng hạng nhất trong Ruby, vì vậy bạn thực sự có thể viết các cấu trúc điều khiển mới như các vòng lặp! Tôi có ấn tượng rằng các lập trình viên Ruby không nhất thiết phải xem xét việc mở rộng ngôn ngữ, đó chỉ là cách bạn lập trình trong Ruby. Nhưng nó khá tuyệt.)

  7. Trong Python, bạn thực sự có thể tháo rời mã byte do trình biên dịch tạo ra, hoặc thậm chí tự viết từ đầu và yêu cầu trình thông dịch thực hiện nó (tôi đã tự làm điều này, và nó rất đơn giản nhưng thú vị).

  8. Python có các thư viện tốt để phân tích cú pháp. Bạn có thể phân tích mã Python thành một cây cú pháp trừu tượng và sau đó thao tác nó bằng mô-đun AST. Mô-đun PyParsing rất hữu ích để phân tích các ngôn ngữ tùy ý, chẳng hạn như các ngôn ngữ bạn thiết kế. Về lý thuyết, bạn có thể viết trình biên dịch ngôn ngữ đầu tiên của mình bằng Python nếu bạn muốn (và nó có thể tạo ra đầu ra C, lắp ráp hoặc thậm chí là Python).

Cách tiếp cận điều tra này có thể phù hợp với cách tiếp cận chính thức hơn, vì bạn sẽ bắt đầu nhận ra các khái niệm bạn đã học bằng ngôn ngữ bạn đang làm việc và ngược lại.

Chúc vui vẻ!


Không đào tại trăn, nhưng nó bên cạnh điểm. Đứa trẻ đã có N ngôn ngữ cho N lớn; tăng N sẽ không làm cho nhiều sự khác biệt. Lấy C làm ví dụ. Đó là tiêu chuẩn. Nó có rất nhiều thư viện. Đó là nền tảng chéo (khi bạn tuân thủ tiêu chuẩn). Bạn có thể tháo rời đầu ra. Bạn có thể viết CFront. V.v ...
Ian

1

Chà, tôi nghĩ rằng câu hỏi của bạn có thể được viết lại thành "Các khái niệm thực tiễn cốt lõi của bằng cấp khoa học máy tính" là gì, và tất nhiên, câu trả lời là, tất nhiên, để lấy bằng Cử nhân Khoa học Máy tính của riêng bạn.

Về cơ bản, bạn tạo trình biên dịch ngôn ngữ lập trình của riêng mình bằng cách đọc tệp văn bản, trích xuất thông tin từ nó và thực hiện các phép biến đổi trên văn bản dựa trên thông tin bạn đã đọc từ đó, cho đến khi bạn chuyển đổi nó thành byte có thể đọc được trình tải (cf, Trình liên kết và Trình tải của Levine). Một trình biên dịch tầm thường là một dự án khá nghiêm ngặt khi được thực hiện lần đầu tiên.

Trái tim của hệ điều hành là hạt nhân, quản lý các tài nguyên (ví dụ: cấp phát / phân bổ bộ nhớ) và chuyển đổi giữa các tác vụ / quy trình / chương trình.

Trình biên dịch là một biến đổi văn bản-> byte.

Nếu bạn quan tâm đến công cụ này, tôi khuyên bạn nên viết một trình biên dịch X86, trong Linux, hỗ trợ một số tập hợp con của lắp ráp X86 tiêu chuẩn. Đó sẽ là một điểm vào khá đơn giản và giới thiệu cho bạn những vấn đề này. Nó không phải là một dự án trẻ con, và sẽ dạy cho bạn nhiều điều.

Tôi khuyên bạn nên viết nó bằng C; C là ngôn ngữ chung cho mức độ công việc đó.


1
Mặt khác, đây là một nơi tốt cho một ngôn ngữ rất cao cấp. Miễn là bạn có thể ra lệnh cho từng byte riêng lẻ trong một tệp, bạn có thể tạo trình biên dịch / trình biên dịch (dễ dàng hơn) bằng bất kỳ ngôn ngữ nào. Nói, perl. Hoặc VBA. Trời, khả năng!
Ian

1

Xem cuốn sách "Xây dựng trình biên dịch" của Kenneth Louden

http://www.cs.sjsu.edu/~louden/cmptext/

Nó cung cấp một cách tiếp cận thực hành tốt hơn để phát triển trình biên dịch.

Mọi người học bằng cách làm. Chỉ một số nhỏ có thể thấy các biểu tượng được vẽ nguệch ngoạc trên bảng và nhảy ngay từ lý thuyết sang thực hành. Thật không may, những người đó thường giáo điều, chủ nghĩa cơ bản và ồn ào nhất về nó.


1

Tôi may mắn được tiếp xúc với PDP-8 như ngôn ngữ lắp ráp đầu tiên của tôi. PDP-8 chỉ có sáu hướng dẫn, rất đơn giản, thật dễ dàng để tưởng tượng chúng được thực hiện bởi một vài thành phần kín đáo, trên thực tế chúng là như vậy. Nó thực sự loại bỏ "ma thuật" khỏi máy tính.

Một cổng khác cho sự mặc khải tương tự là ngôn ngữ lắp ráp "hỗn hợp" mà Knuth sử dụng trong các ví dụ của mình. "Mix" có vẻ cổ xưa ngày nay, nhưng nó vẫn có hiệu ứng DE-mystifying.


0

Trình biên dịch và ngôn ngữ lập trình (và tất cả mọi thứ bao gồm cả việc xây dựng một ngôn ngữ - chẳng hạn như xác định ngữ pháp hữu hạn và chuyển đổi sang lắp ráp) là một nhiệm vụ rất phức tạp đòi hỏi rất nhiều sự hiểu biết về toàn bộ hệ thống. Loại khóa học này thường được cung cấp dưới dạng lớp Comp Sci năm thứ 3/4 trong Đại học.

Tôi đặc biệt khuyên bạn trước tiên nên hiểu rõ hơn về Hệ điều hành nói chung và cách các ngôn ngữ hiện có được biên dịch / thực thi (nghĩa là (C / C ++), trong VM (Java) hoặc bởi trình thông dịch (Python / Javascript)).

Tôi tin rằng chúng tôi đã sử dụng cuốn sách Khái niệm hệ điều hành của Abraham Silberschatz, Peter B. Galvin, Greg Gagne trong khóa học Hệ điều hành của tôi (năm thứ 2). Đây là một cuốn sách tuyệt vời đã đưa ra hướng dẫn kỹ lưỡng về từng thành phần của một hệ điều hành - hơi đắt tiền nhưng cũng đáng giá và các bản sao cũ / đã sử dụng sẽ nổi xung quanh.


Khái niệm hệ điều hành? Rất ít trong số đó là cần thiết để xây dựng một trình biên dịch. Điều cần thiết là sự hiểu biết về kiến ​​trúc phần mềm: giải quyết các không gian, ngăn xếp, luồng (nếu anh ta muốn học trình biên dịch, anh ta nên tìm hiểu về song song, tương lai của anh ta).
Ira Baxter

Ngay sau khi nói rằng anh ấy muốn học thiết kế ngôn ngữ và trình biên dịch, anh ấy nói rằng anh ấy muốn tìm hiểu về các hệ điều hành.
David Thornley

@Ira - đồng ý. Tôi chưa bao giờ nói rằng việc hiểu hệ điều hành là cần thiết để xây dựng trình biên dịch / ngôn ngữ, giải thích đơn giản rằng nó có thể là một điểm khởi đầu dễ dàng hơn. Mọi người đang tập trung vào khía cạnh 'trình biên dịch' trong câu hỏi của anh ấy nhưng anh ấy cũng đề cập rằng anh ấy muốn hiểu rõ hơn về hệ điều hành 'và các thư viện. Đối với một đứa trẻ 15 tuổi vẫn học về kiến ​​trúc, việc hiểu quản lý bộ nhớ, xâu chuỗi, khóa, i / o, v.v. sẽ hữu ích hơn nhiều so với việc học cách định nghĩa một ngữ pháp với yacc (IMHO)
plafond

Xin lỗi ... đã bỏ lỡ quan điểm về việc muốn tìm hiểu về (xây dựng?) Hệ điều hành. Quan điểm của tôi là: anh ta không cần nhiều kiến ​​thức về hệ điều hành cho trình biên dịch. Trong thực tế, đây là một chủ đề hoàn toàn khác, ngoại trừ trình biên dịch và HĐH tương tác để đạt được mục đích chung nào đó. (Ví dụ, Multics yêu cầu trình biên dịch PL / 1 của nó để xây dựng các lệnh gọi hàm theo một số cách nhất định để kích hoạt VM toàn cầu).
Ira Baxter

0

Đó là một chủ đề lớn nhưng thay vì gạt bạn ra một cách hào hoa "đi đọc sách đi, nhóc" thay vào đó tôi sẽ vui lòng đưa ra gợi ý cho bạn để giúp bạn quấn đầu xung quanh nó.

Hầu hết các trình biên dịch và / hoặc trình thông dịch đều hoạt động như thế này:

Tokenize : Quét văn bản mã và chia nó thành một danh sách các mã thông báo.

Bước này có thể khó khăn vì bạn không thể phân tách chuỗi trên các khoảng trắng, bạn phải nhận ra đó if (bar) foo += "a string";là danh sách 8 mã thông báo: WORD, OPEN_PAREN, WORD, CLOSE_PAREN, WORD, ASIGNMENT_ADD, STRING_LITITH, TERMINATOR. Như bạn có thể thấy, chỉ cần tách mã nguồn trên các khoảng trắng sẽ không hoạt động, bạn phải đọc từng ký tự thành một chuỗi, vì vậy nếu bạn gặp một ký tự chữ và số, bạn tiếp tục đọc các ký tự cho đến khi bạn nhấn một ký tự không phải là chữ và số chỉ cần đọc là một WORD để được phân loại thêm sau này. Bạn có thể tự quyết định mức độ tokenizer của mình như thế nào: liệu nó có nuốt như OPEN_QUOTE, UNPARSED_TEXT, CLOSE_QUOTE hay bất cứ điều gì, đây chỉ là một trong nhiều lựa chọn mà bạn phải tự quyết định khi mã hóa nó."a string" thông báo như một mã thông báo có tên là STRING_LITITH để được phân tích cú pháp sau hay không"a string"

Lex : Vì vậy, bây giờ bạn có một danh sách các mã thông báo. Bạn có thể đã gắn thẻ một số mã thông báo với phân loại mơ hồ như WORD vì trong lần đầu tiên bạn không mất quá nhiều nỗ lực để tìm ra bối cảnh của từng chuỗi ký tự. Vì vậy, bây giờ hãy đọc lại danh sách mã thông báo của bạn và phân loại lại từng mã thông báo mơ hồ với loại mã thông báo cụ thể hơn dựa trên các từ khóa trong ngôn ngữ của bạn. Vì vậy, bạn có một WORD như "if" và "if" nằm trong danh sách các từ khóa đặc biệt được gọi là biểu tượng IF để bạn thay đổi loại biểu tượng của mã thông báo đó từ WORD thành IF và bất kỳ WORD nào không có trong danh sách từ khóa đặc biệt của bạn , chẳng hạn như WORD foo, là một IDENTIFIER.

Phân tích : Vì vậy, bây giờ bạn quay if (bar) foo += "a string";một danh sách các thẻ lexed trông như thế này: NẾU OPEN_PAREN số nhận dạng CLOSE_PAREN IDENTIFIER ASIGN_ADD STRING_LITERAL TERMINATOR. Bước này là nhận ra các chuỗi mã thông báo dưới dạng câu lệnh. Đây là phân tích cú pháp. Bạn làm điều này bằng cách sử dụng một ngữ pháp như:

TUYÊN BỐ: = ASIGN_EXPRESSION | NỀN TẢNG

IF_STATMENT: = IF, PAREN_EXPRESSION, STATMENT

ASIGN_EXPRESSION: = IDENTIFIER, ASIGN_OP, VALUE

PAREN_EXPRESSSION: = OPEN_PAREN, GIÁ TRỊ, CLOSE_PAREN

GIÁ TRỊ: = IDENTIFIER | STRING_LITITH | PAREN_EXPRESSION

ASIGN_OP: = THIẾT BỊ | ASIGN_ADD | ASIGN_SUBTRACT | ASIGN_MULT

Các sản phẩm sử dụng "|" giữa các thuật ngữ có nghĩa là "khớp với bất kỳ từ nào trong số này", nếu nó có dấu phẩy giữa các thuật ngữ thì có nghĩa là "khớp với chuỗi thuật ngữ này"

bạn sử dụng cái này như thế nào? Bắt đầu với mã thông báo đầu tiên, hãy thử kết hợp chuỗi mã thông báo của bạn với các sản phẩm này. Vì vậy, trước tiên, bạn cố gắng khớp danh sách mã thông báo của mình với TUYÊN BỐ, vì vậy bạn đã đọc quy tắc TUYÊN BỐ và thông báo "TUYÊN BỐ là một ASIGN_EXPRESSION hoặc IF_STATMENT" để bạn tìm cách khớp với ASIGN_EXPRESSION trước, vì vậy bạn hãy tìm quy tắc ngữ pháp cho ASIGN_EXPRESSION và thông báo "ASIGN_EXPRESSION là IDENTIFIER, theo sau là ASIGN_OP, theo sau là VALUE, vì vậy bạn tra cứu quy tắc ngữ pháp cho IDENTIFIER và bạn thấy không có lỗi ngữ pháp nào cho IDENTIFIER vì vậy điều đó có nghĩa là IDENTifyER là" thiết bị đầu cuối " phân tích cú pháp để khớp với nó để bạn có thể thử khớp trực tiếp với mã thông báo của mình. Nhưng mã thông báo nguồn đầu tiên của bạn là IF và IF không giống với IDENTIFIER nên không khớp. Gì bây giờ? Bạn quay lại quy tắc TUYÊN BỐ và cố gắng khớp với thuật ngữ tiếp theo: IF_STATMENT. Bạn tra cứu IF_STATMENT, nó bắt đầu bằng IF, tra cứu IF, IF là thiết bị đầu cuối, so sánh thiết bị đầu cuối với mã thông báo đầu tiên của bạn, mã thông báo IF phù hợp, tiếp tục tuyệt vời, thuật ngữ tiếp theo là PAREN_EXPRESSION, tra cứu PAREN_EXPRESSION, đây không phải là thiết bị đầu cuối, đó là thuật ngữ đầu tiên, PAREN_EXPRESSION bắt đầu với OPEN_PAREN, tra cứu OPEN_PAREN, đó là một thiết bị đầu cuối, khớp OPEN_PAREN với mã thông báo tiếp theo của bạn, nó khớp, .... v.v.

Cách dễ nhất để tiếp cận bước này là bạn có một hàm gọi là parse () mà bạn chuyển mã thông báo mã nguồn mà bạn đang cố khớp và thuật ngữ ngữ pháp bạn đang cố gắng khớp với nó. Nếu thuật ngữ ngữ pháp không phải là một thiết bị đầu cuối thì bạn lặp lại: bạn gọi parse () một lần nữa chuyển nó cùng mã thông báo nguồn và thuật ngữ đầu tiên của quy tắc ngữ pháp này. Đây là lý do tại sao nó được gọi là "trình phân tích cú pháp gốc đệ quy" Hàm parse () trả về (hoặc sửa đổi) vị trí hiện tại của bạn khi đọc mã thông báo nguồn, về cơ bản nó sẽ trả lại mã thông báo cuối cùng trong chuỗi phù hợp và bạn tiếp tục cuộc gọi tiếp theo phân tích () từ đó.

Mỗi lần phân tích cú pháp () khớp với một sản phẩm như ASIGN_EXPRESSION, bạn tạo một cấu trúc đại diện cho đoạn mã đó. Cấu trúc này chứa các tham chiếu đến các mã thông báo nguồn gốc. Bạn bắt đầu xây dựng một danh sách các cấu trúc này. Chúng ta sẽ gọi toàn bộ cấu trúc này là Cây Cú pháp Trừu tượng (AST)

Biên dịch và / hoặc Thực thi : Đối với một số sản phẩm nhất định trong ngữ pháp của bạn, bạn đã tạo các hàm xử lý mà nếu được cung cấp cấu trúc AST, nó sẽ biên dịch hoặc thực thi đoạn AST đó.

Vì vậy, hãy nhìn vào mảnh AST của bạn có loại ASIGN_ADD. Vì vậy, với tư cách là một trình thông dịch, bạn có hàm ASIGN_ADD_execute (). Hàm này được truyền dưới dạng một phần của AST tương ứng với cây phân tích cú pháp foo += "a string", vì vậy hàm này nhìn vào cấu trúc đó và nó biết rằng thuật ngữ đầu tiên trong cấu trúc phải là IDENTIFIER và thuật ngữ thứ hai là VALUE, vì vậy ASIGN_ADD_execute () chuyển thuật ngữ VALUE cho hàm VALUE_eval () trả về một đối tượng biểu thị giá trị được đánh giá trong bộ nhớ, sau đó ASIGN_ADD_execute () thực hiện tra cứu "foo" trong bảng biến của bạn và lưu trữ một tham chiếu đến bất cứ thứ gì được trả về bởi eval_value () chức năng.

Đó là một thông dịch viên. Thay vào đó, một trình biên dịch sẽ có các hàm xử lý dịch AST thành mã byte hoặc mã máy thay vì thực thi nó.

Bước 1 đến 3 và một số 4, có thể được thực hiện dễ dàng hơn bằng cách sử dụng các công cụ như Flex và Bison. (còn gọi là Lex và Yacc) nhưng tự mình viết một thông dịch viên từ đầu có lẽ là bài tập có sức mạnh nhất mà bất kỳ lập trình viên nào cũng có thể đạt được. Tất cả các thách thức lập trình khác có vẻ tầm thường sau khi lên đỉnh này.

Lời khuyên của tôi là bắt đầu nhỏ: một ngôn ngữ nhỏ, với một ngữ pháp nhỏ, và thử phân tích cú pháp và thực hiện một vài câu đơn giản, sau đó phát triển từ đó.

Đọc những điều này, và chúc may mắn!

http://www.iro.umontreal.ca/~felipe/IFT2030-Automne2002/Complements/tinyc.c

http://en.wikipedia.org/wiki/Recursive_descent_parser


2
Bạn thực hiện những gì tôi coi là một sai lầm kinh điển khi mọi người nghĩ về việc biên dịch: đó là tin rằng vấn đề là về phân tích cú pháp. PARSING LÀ KỸ THUẬT DỄ DÀNG; Có những công nghệ tuyệt vời để làm điều đó. Phần khó về biên dịch là phân tích ngữ nghĩa, tối ưu hóa ở mức độ đại diện chương trình cao và thấp và tạo mã, với sự nhấn mạnh ngày càng tăng trong những ngày này về mã PARALLEL. Bạn tầm thường hóa điều này hoàn toàn trong câu trả lời của bạn: "một trình biên dịch sẽ có các hàm xử lý để dịch AST thành mã byte". Có 50 năm trôi qua về lý thuyết biên dịch và kỹ thuật ẩn trong đó.
Ira Baxter

0

Lĩnh vực máy tính chỉ phức tạp vì nó đã có thời gian để phát triển theo nhiều hướng. Tại trung tâm của nó, nó chỉ là về máy tính.

Máy tính rất cơ bản yêu thích của tôi là Máy tính Rơle của Harry . Nó mang đến một hương vị về cách một máy tính hoạt động ở cấp độ cơ sở. Sau đó, bạn có thể bắt đầu đánh giá cao tại sao những thứ như ngôn ngữ và hệ điều hành là cần thiết.

Vấn đề là, thật khó để hiểu bất cứ điều gì mà không hiểu những gì cần nó . Chúc may mắn và đừng chỉ đọc những thứ đó. Làm công cụ.



-1

Một cuốn sách giới thiệu hay khác là "Compilerbau" của N. Wirth từ năm 1986 (xây dựng trình biên dịch) dài khoảng 100 trang và giải thích ngắn gọn, mã được thiết kế tốt cho ngôn ngữ đồ chơi PL / 0, bao gồm trình phân tích cú pháp, trình tạo mã và máy ảo. Nó cũng chỉ ra cách viết một trình phân tích cú pháp đọc trong ngữ pháp để phân tích cú pháp trong ký hiệu EBNF. Cuốn sách bằng tiếng Đức nhưng tôi đã viết một bản tóm tắt và dịch mã sang Python dưới dạng bài tập, xem http://www.d12k.org/cmplr/w86/intro.html .


-1

Nếu bạn quan tâm đến việc hiểu bản chất của ngôn ngữ lập trình, tôi khuyên bạn nên làm việc thông qua cuốn sách PLAI (http://www.cs.brown.edu/~sk/Publications/Books/ProgLangs/) để hiểu các khái niệm và thực hiện của họ. Nó cũng sẽ giúp bạn với việc thiết kế ngôn ngữ của riêng bạn.


-1

Nếu bạn thực sự có hứng thú với trình biên dịch và chưa từng làm việc này trước đây, bạn có thể bắt đầu bằng cách thiết kế một máy tính để tính toán các công thức số học (một loại DSL như Eric đã đề cập). Có nhiều khía cạnh bạn cần xem xét cho loại trình biên dịch này:

  • Số được phép
  • Toán tử được phép
  • Các ưu tiên của nhà điều hành
  • Xác thực cú pháp
  • Cơ chế tra cứu biến
  • Phát hiện chu kỳ
  • Tối ưu hóa

Ví dụ: bạn có các công thức sau, máy tính của bạn sẽ có thể tính giá trị của x:

a = 1
b = 2
c = a + b
d = (3 + b) * c
x = a - d / b

Nó không phải là một trình biên dịch cực kỳ khó để bắt đầu, nhưng có thể khiến bạn suy nghĩ nhiều hơn về một số ý tưởng cơ bản về trình biên dịch là gì và cũng giúp bạn cải thiện kỹ năng lập trình và kiểm soát chất lượng mã của bạn (đây thực sự là một vấn đề hoàn hảo. Kiểm tra hướng phát triển TDD có thể áp dụng để cải thiện chất lượng phần mềm).

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.