Những tính chất nào của ngôn ngữ lập trình làm cho việc biên dịch là không thể?


72

Câu hỏi:

"Một số thuộc tính của ngôn ngữ lập trình có thể yêu cầu cách duy nhất để có được mã được viết trong đó là bằng cách diễn giải. Nói cách khác, việc biên dịch thành mã máy nguyên gốc của CPU truyền thống là không thể. Các thuộc tính này là gì?"

Trình biên dịch: Nguyên tắc và thực hành của Parag H. Dave và Himanshu B. Dave (ngày 2 tháng 5 năm 2012)

Cuốn sách không đưa ra manh mối về câu trả lời. Tôi đã cố gắng tìm câu trả lời về các khái niệm về ngôn ngữ lập trình (SEBESTA), nhưng không có kết quả. Tìm kiếm trên web cũng rất ít. Bạn có chút manh mối nào không?


31
Nổi tiếng, Perl thậm chí không thể được phân tích cú pháp . Ngoài ra, yêu cầu đó dường như là sai lầm tầm thường mà không cần giả định thêm: nếu có một thông dịch viên, tôi luôn có thể bó trình thông dịch và mã trong một tệp thực thi, voila.
Raphael

4
@Raphael: Ý tưởng hay, nhưng ... 1) Bạn đang giả sử mã có sẵn trước khi được thực thi. Điều đó không giữ cho sử dụng tương tác. Chắc chắn, bạn có thể sử dụng trình biên dịch đúng lúc để mã gốc trên các câu lệnh bash hoặc nội dung ngăn xếp PostScript, nhưng đó là một ý tưởng khá điên rồ. 2) Ý tưởng của bạn không thực sự biên dịch mã: gói không phải là phiên bản được biên dịch của mã, nhưng vẫn là một trình thông dịch cho mã.
Revierpost

7
Ngày xưa, tôi đã tự chỉnh sửa các chương trình gwbasic (gwbasic lưu trữ các chương trình cơ bản trong một loại mã byte). Hiện tại tôi không thể nghĩ ra một cách lành mạnh để biên dịch chúng thành mã máy gốc mà vẫn giữ được khả năng tự chỉnh sửa.
PlasmaHH

15
@PlasmaHH: Mã tự sửa đổi trở lại năm 1948. Trình biên dịch đầu tiên được viết vào năm 1952. Khái niệm mã tự sửa đổi được phát minh trong mã máy gốc.
Vịt Mooing

10
@reinierpost Raphael đang có lập trường lý thuyết về vấn đề này. Nó có giá trị cho thấy những hạn chế về khái niệm của câu hỏi. Biên dịch là dịch từ ngôn ngữ S sang ngôn ngữ T. Ngôn ngữ T có thể là phần mở rộng của S để có thể thêm mã phiên dịch trong một số ngôn ngữ khác. Vì vậy, gói S và trình thông dịch của nó là một chương trình bằng ngôn ngữ T. Có vẻ vô lý với một kỹ sư, nhưng nó cho thấy rằng không dễ để tạo ra câu hỏi một cách có ý nghĩa. Làm thế nào để bạn phân biệt một quy trình biên dịch có thể chấp nhận được với một quy trình không thể chấp nhận (như của Raphael) từ quan điểm kỹ thuật?
babou

Câu trả lời:


61

Sự khác biệt giữa mã được giải thích và biên dịch có lẽ là một hư cấu, như được nhấn mạnh bởi nhận xét của Raphael :

the claim seems to be trivially wrong without further assumptions: if there is
an interpreter, I can always bundle interpreter and code in one executable ...

Thực tế là mã luôn được diễn giải, bằng phần mềm, bằng phần cứng hoặc kết hợp cả hai và quá trình biên dịch không thể biết nó sẽ là gì.

Những gì bạn cho là biên dịch là một quá trình dịch từ một ngôn ngữ (cho nguồn) sang ngôn ngữ khác (cho mục tiêu). Và, người phiên dịch cho thường khác với người phiên dịch cho .STST

Chương trình được biên dịch được dịch từ một dạng cú pháp sang một dạng cú pháp khác , như vậy, với ngữ nghĩa dự định của các ngôn ngữ và , và có cùng một hành vi tính toán, cho đến một vài điều mà bạn thường cố gắng thay đổi, có thể để tối ưu hóa, chẳng hạn như độ phức tạp hoặc hiệu quả đơn giản (thời gian, không gian, bề mặt, mức tiêu thụ năng lượng). Tôi đang cố gắng không nói về sự tương đương chức năng, vì nó sẽ đòi hỏi các định nghĩa chính xác.PSPTSTPSPT

Một số trình biên dịch đã thực sự được sử dụng đơn giản để giảm kích thước của mã, chứ không phải để "cải thiện" việc thực thi. Đây là trường hợp ngôn ngữ được sử dụng trong hệ thống Plato (mặc dù họ không gọi nó là biên dịch).

Bạn có thể xem xét mã của bạn được biên soạn đầy đủ nếu, sau quá trình biên dịch, bạn không còn cần người phiên dịch cho . Ít nhất, đó là cách duy nhất tôi có thể đọc câu hỏi của bạn, với tư cách là một kỹ thuật thay vì câu hỏi lý thuyết (vì về mặt lý thuyết, tôi luôn có thể xây dựng lại trình thông dịch).S

Một điều có thể gây ra vấn đề, afaik, là siêu vòng tròn . Đó là khi một chương trình sẽ thao tác các cấu trúc cú pháp bằng ngôn ngữ nguồn của chính nó , tạo ra đoạn chương trình mà sau đó được đặt vào trong như thể chúng là một phần của chương trình gốc. Vì bạn có thể tạo ra các đoạn chương trình tùy ý bằng ngôn ngữ là kết quả của việc tính toán tùy ý thao túng các đoạn cú pháp vô nghĩa, tôi đoán bạn có thể làm cho nó gần như không thể (từ quan điểm kỹ thuật) để biên dịch chương trình sang ngôn ngữ , do đó nó bây giờ tạo ra mảnh vỡ của . Do đó, trình thông dịch cho sẽ là cần thiết, hoặc ít nhất là trình biên dịch từ đếnSSTTSST để biên dịch nhanh chóng các đoạn được tạo trong (xem thêm tài liệu này ).S

Nhưng tôi không chắc làm thế nào điều này có thể được chính thức hóa đúng cách (và không có thời gian ngay bây giờ cho nó). Và không thể là một từ lớn cho một vấn đề không được chính thức hóa.

Nhận xét xa hơn

Đã thêm sau 36 giờ. Bạn có thể muốn bỏ qua phần tiếp theo rất dài này.

Nhiều ý kiến ​​cho câu hỏi này cho thấy hai quan điểm của vấn đề: một quan điểm lý thuyết xem nó là vô nghĩa, và một quan điểm kỹ thuật không may là không dễ dàng được chính thức hóa.

Có nhiều cách để xem xét diễn giải và biên dịch, và tôi sẽ cố gắng phác thảo một vài cách. Tôi sẽ cố gắng để trở thành không chính thức như tôi có thể quản lý

Sơ đồ bia mộ

Một trong những hình thức chính thức sớm (đầu những năm 1960 đến cuối năm 1990) là sơ đồ T hoặc Tombstone . Các sơ đồ này được trình bày trong các yếu tố đồ họa có thể kết hợp, ngôn ngữ triển khai của trình thông dịch hoặc trình biên dịch, ngôn ngữ nguồn được giải thích hoặc biên dịch và ngôn ngữ đích trong trường hợp trình biên dịch. Phiên bản phức tạp hơn có thể thêm thuộc tính. Các biểu diễn đồ họa này có thể được xem như là tiên đề, quy tắc suy luận, có thể sử dụng để tạo ra bộ xử lý cơ học từ một bằng chứng về sự tồn tại của chúng từ các tiên đề, à la Curry-Howard (mặc dù tôi không chắc điều đó đã được thực hiện vào những năm sáu mươi :).

Đánh giá một phần

Một quan điểm thú vị khác là mô hình đánh giá một phần . Tôi đang xem một chương trình đơn giản như là một kiểu triển khai chức năng tính toán một câu trả lời cho một số dữ liệu đầu vào. Sau đó, một thông dịch viên cho ngôn ngữ là một chương trình mà phải mất một chương trình viết bằng và dữ liệu cho chương trình đó, và tính toán kết quả theo ngữ nghĩa của . Đánh giá một phần là một kỹ thuật để chuyên một chương trình của hai đối số và , khi chỉ có một đối số, nói , được biết đến. Mục đích là để đánh giá nhanh hơn khi cuối cùng bạn nhận được đối số thứ haiISSpSSdSa1a2a1a2 . Điều này đặc biệt hữu ích nếu thay đổi thường xuyên hơn vì chi phí đánh giá một phần với có thể được khấu hao trên tất cả các tính toán trong đó chỉ có thay đổi.a2a1a1a2

Đây là một tình huống thường gặp trong thiết kế thuật toán (thường là chủ đề của nhận xét đầu tiên về SE-CS), khi một phần tĩnh hơn của dữ liệu được xử lý trước, do đó chi phí xử lý trước có thể được khấu hao trên tất cả các ứng dụng của thuật toán với nhiều phần khác nhau của dữ liệu đầu vào.

Đây cũng chính là tình huống của các trình thông dịch, vì đối số đầu tiên là chương trình được thực thi và thường được thực thi nhiều lần với các dữ liệu khác nhau (hoặc có các nhánh con được thực thi nhiều lần với các dữ liệu khác nhau). Do đó, nó trở thành một ý tưởng tự nhiên để chuyên gia phiên dịch để đánh giá nhanh hơn một chương trình nhất định bằng cách đánh giá một phần nó trên chương trình này như là đối số đầu tiên. Đây có thể được coi là một cách biên dịch chương trình, và đã có công việc nghiên cứu quan trọng về việc biên dịch bằng cách đánh giá một phần trình thông dịch về đối số (chương trình) đầu tiên của nó.

Định lý Smn

Điểm hay của phương pháp đánh giá một phần là nó có nguồn gốc từ lý thuyết (mặc dù lý thuyết có thể là kẻ nói dối), đáng chú ý là trong định lý Smn của Kleene . Tôi đang cố gắng trình bày trực quan về nó, hy vọng nó sẽ không làm đảo lộn các nhà lý thuyết thuần túy.

Đưa ra một số thứ tự của các hàm đệ quy, bạn có thể xem là phần cứng của mình, do đó, với số (đọc mã đối tượng ) của chương trình là hàm được xác định bởi (nghĩa là được tính bởi mã đối tượng trên phần cứng của bạn).φφpφpp

Ở dạng đơn giản nhất, định lý được nêu trong wikipedia như sau (tối đa một thay đổi nhỏ trong ký hiệu):

Cho một số thứ tự của Gôdel của các hàm đệ quy, có một hàm đệ quy nguyên thủy của hai đối số có thuộc tính sau: với mỗi số Gôdel của một hàm tính toán một phần với hai đối số, các biểu thức và được xác định cho cùng một tổ hợp số tự nhiên và và giá trị của chúng bằng nhau cho bất kỳ kết hợp nào như vậy. Nói cách khác, các hàm bằng nhau mở rộng sau đây giữ cho mọi : φσqfφσ(q,x)(y)f(x,y)xyxφσ(q,x)λy.φq(x,y).

Bây giờ, lấy làm trình thông dịch , làm mã nguồn của chương trình và làm dữ liệu cho chương trình đó, chúng ta có thể viết: qISxpSydφσ(IS,pS)λd.φIS(pS,d).

φIS có thể được xem như thực hiện các dịch trên phần cứng, tức là, như một hộp đen sẵn sàng để giải thích các chương trình viết bằng ngôn ngữ .ISS

Hàm có thể được xem là một hàm chuyên về trình thông dịch cho chương trình , như trong đánh giá một phần. Do đó, số có thể được nhìn thấy có mã đối tượng là phiên bản được biên dịch của chương trình .σISPSσ(IS,pS)pS

Vì vậy, hàm có thể được xem như là một hàm lấy đối số mã nguồn của chương trình được viết bằng ngôn ngữ và trả về phiên bản mã đối tượng cho chương trình đó Vì vậy, là cái thường được gọi là trình biên dịch.CS=λqS.σ((IS,qS)qSSCS

Một số kết luận

Tuy nhiên, như tôi đã nói: "lý thuyết có thể là kẻ nói dối", hoặc thực sự có vẻ là một. Vấn đề là chúng ta không biết gì về hàm . Thực tế có rất nhiều hàm như vậy, và tôi đoán là bằng chứng của định lý có thể sử dụng một định nghĩa rất đơn giản cho nó, có thể không tốt hơn, theo quan điểm kỹ thuật, hơn là giải pháp do Raphael đề xuất: chỉ đơn giản là bó mã nguồn với trình thông dịch . Điều này luôn có thể được thực hiện, để chúng ta có thể nói: biên dịch luôn luôn có thể.q S I SσqSIS

Chính thức hóa một khái niệm hạn chế hơn về trình biên dịch sẽ đòi hỏi một cách tiếp cận lý thuyết tinh tế hơn. Tôi không biết những gì có thể đã được thực hiện theo hướng đó. Các công việc rất thực tế được thực hiện trên đánh giá một phần là thực tế hơn từ quan điểm kỹ thuật. Và tất nhiên còn có các kỹ thuật khác để viết trình biên dịch, bao gồm trích xuất các chương trình từ bằng chứng về đặc điểm kỹ thuật của chúng, được phát triển trong bối cảnh của lý thuyết loại, dựa trên sự đồng hình của Curry-Howard (nhưng tôi đang vượt ra khỏi phạm vi năng lực của mình) .

Mục đích của tôi ở đây là để cho thấy rằng nhận xét của Raphael không phải là "điên rồ", nhưng một lời nhắc nhở lành mạnh rằng mọi thứ không rõ ràng, và thậm chí không đơn giản. Nói rằng điều gì đó là không thể là một tuyên bố mạnh mẽ đòi hỏi phải có định nghĩa chính xác và bằng chứng, nếu chỉ để có một sự hiểu biết chính xác về cách thức và lý do tại sao nó là không thể . Nhưng xây dựng một chính thức hóa thích hợp để thể hiện một bằng chứng như vậy có thể khá khó khăn.

Điều này cho biết, ngay cả khi một tính năng cụ thể không thể biên dịch được, theo nghĩa được hiểu bởi các kỹ sư, các kỹ thuật biên dịch tiêu chuẩn luôn có thể được áp dụng cho các phần của chương trình không sử dụng tính năng đó, như được nhận xét bởi câu trả lời của Gilles.

Để làm theo nhận xét quan trọng của Gilles rằng, tùy thuộc vào ngôn ngữ, một số điều có thể được thực hiện vào thời gian biên dịch, trong khi những việc khác phải được thực hiện vào thời gian chạy, do đó cần có mã cụ thể, chúng ta có thể thấy rằng khái niệm biên dịch thực sự là không xác định, và có lẽ không thể xác định theo bất kỳ cách nào thỏa đáng. Quá trình biên dịch chỉ là một quá trình tối ưu hóa, như tôi đã cố gắng hiển thị trong phần đánh giá một phần, khi tôi so sánh nó với tiền xử lý dữ liệu tĩnh trong một số thuật toán.

Là một quá trình tối ưu hóa phức tạp, khái niệm biên dịch thực sự thuộc về một sự liên tục. Tùy thuộc vào đặc tính của ngôn ngữ hoặc chương trình, một số thông tin có thể có sẵn tĩnh và cho phép tối ưu hóa tốt hơn. Những thứ khác phải được hoãn lại để chạy. Khi mọi thứ trở nên thực sự tồi tệ, mọi thứ phải được thực hiện vào thời gian chạy ít nhất là đối với một số phần của chương trình và gói mã nguồn với trình thông dịch là tất cả những gì bạn có thể làm. Vì vậy, gói này chỉ là kết thúc thấp của liên tục biên dịch này. Phần lớn các nghiên cứu về trình biên dịch là về việc tìm cách thực hiện tĩnh những gì đã từng được thực hiện một cách linh hoạt. Bộ sưu tập rác thời gian biên dịch có vẻ là một ví dụ tốt.

Lưu ý rằng nói rằng quá trình biên dịch nên tạo mã máy là không có ích. Đó là chính xác những gì gói có thể làm như trình thông dịch là mã máy (tốt, điều có thể phức tạp hơn một chút với biên dịch chéo).


3
" Không thể là một từ lớn" Một từ rất lớn. =)
Brian S

3
Nếu người ta định nghĩa "biên dịch" để chỉ một chuỗi các bước diễn ra hoàn toàn trước khi chương trình thực thi nhận được đầu vào đầu tiên và diễn giải là quá trình có luồng chương trình điều khiển dữ liệu thông qua các phương tiện không phải là một phần của mô hình máy trừu tượng của chương trình , sau đó để một ngôn ngữ được biên dịch, trình biên dịch phải có khả năng xác định, trước khi bắt đầu thực thi, mọi ý nghĩa có thể có của một ngôn ngữ có thể có. Trong các ngôn ngữ mà cấu trúc ngôn ngữ có thể có số lượng ý nghĩa không giới hạn, việc biên dịch sẽ không hoạt động.
supercat

@BrianS Không, không, và không thể chứng minh điều gì khác;)
Michael Gazonda

@supercat Đó vẫn chưa phải là một định nghĩa. "Ý nghĩa" của cấu trúc ngôn ngữ là gì?
Rhymoid

Tôi thích khái niệm xem trình biên dịch / trình thông dịch như một kiểu thực thi một phần!
Bergi

17

Câu hỏi không thực sự về việc biên dịch là không thể . Nếu một ngôn ngữ có thể được giải thích¹, thì nó có thể được biên dịch theo cách tầm thường, bằng cách kết hợp trình thông dịch với mã nguồn. Câu hỏi là hỏi những tính năng ngôn ngữ làm cho điều này về cơ bản là cách duy nhất.

Trình thông dịch là một chương trình lấy mã nguồn làm đầu vào và hoạt động như được chỉ định bởi ngữ nghĩa của mã nguồn đó. Nếu một thông dịch viên là cần thiết, điều này có nghĩa là ngôn ngữ bao gồm một cách để giải thích mã nguồn. Tính năng này được gọi là eval. Nếu một trình thông dịch được yêu cầu như một phần của môi trường thời gian chạy của ngôn ngữ, điều đó có nghĩa là ngôn ngữ bao gồm eval: evaltồn tại dưới dạng nguyên thủy hoặc có thể được mã hóa theo một cách nào đó. Các ngôn ngữ được gọi là ngôn ngữ kịch bản thường bao gồm một evaltính năng, cũng như hầu hết các phương ngữ Lisp .

Chỉ vì một ngôn ngữ bao gồm evalkhông có nghĩa là phần lớn ngôn ngữ không thể được biên dịch thành mã gốc. Ví dụ, có tối ưu hóa trình biên dịch Lisp, tạo ra mã gốc tốt và dù sao cũng hỗ trợ eval; evalMã ed có thể được giải thích hoặc có thể được biên dịch nhanh chóng.

evallà tính năng nhu cầu cuối cùng của trình thông dịch, nhưng có những tính năng khác yêu cầu thiếu thông dịch viên. Hãy xem xét một số giai đoạn điển hình của trình biên dịch:

  1. Phân tích cú pháp
  2. Kiểm tra loại
  3. Tạo mã
  4. Liên kết

evalcó nghĩa là tất cả các giai đoạn này phải được thực hiện trong thời gian chạy. Có những tính năng khác làm cho việc biên dịch bản địa trở nên khó khăn. Lấy từ dưới lên, một số ngôn ngữ khuyến khích liên kết muộn bằng cách cung cấp các cách thức theo đó các hàm (phương thức, thủ tục, v.v.) và các biến (đối tượng, tham chiếu, v.v.) có thể phụ thuộc vào thay đổi mã không cục bộ. Điều này gây khó khăn (nhưng không phải là không thể) để tạo mã gốc hiệu quả: dễ dàng giữ các tham chiếu đối tượng như các cuộc gọi trong máy ảo và để công cụ VM xử lý các ràng buộc một cách nhanh chóng.

Nói chung, sự phản chiếu có xu hướng làm cho các ngôn ngữ khó biên dịch thành mã gốc. Một nguyên thủy eval là một trường hợp cực đoan của sự phản ánh; nhiều ngôn ngữ không đi xa đến thế, nhưng dù sao cũng có một ngữ nghĩa được định nghĩa theo một máy ảo, cho phép mã ví dụ để lấy một lớp theo tên, kiểm tra sự kế thừa của nó, liệt kê các phương thức của nó, gọi một phương thức, v.v. Java với JVMC # với .NET là hai ví dụ nổi tiếng. Cách đơn giản nhất để thực hiện các ngôn ngữ này là biên dịch chúng thành mã byte , nhưng dù sao cũng có các trình biên dịch riêng (nhiều thời gian ) biên dịch ít nhất các đoạn chương trình không sử dụng các phương tiện phản chiếu nâng cao.

Kiểm tra loại xác định xem một chương trình là hợp lệ. Các ngôn ngữ khác nhau có các tiêu chuẩn khác nhau về mức độ phân tích được thực hiện trong thời gian biên dịch so với thời gian chạy: một ngôn ngữ được gọi là gõ tĩnh tĩnh nếu nó thực hiện nhiều kiểm tra trước khi bắt đầu chạy mã và gõ động một cách linh hoạt nếu không. Một số ngôn ngữ bao gồm một tính năng diễn viên động hoặc tính năng unmarshall-and-typecheck; các tính năng này yêu cầu nhúng một máy đánh chữ trong môi trường thời gian chạy. Điều này là trực giao với các yêu cầu bao gồm trình tạo mã hoặc trình thông dịch trong môi trường thời gian chạy.

¹ Tập thể dục: xác định một ngôn ngữ mà không thể được giải thích.


(1) Tôi không đồng ý về việc đóng gói một trình thông dịch với mã nguồn được tính là biên dịch, nhưng phần còn lại của bài viết của bạn là tuyệt vời. (2) Hoàn toàn đồng ý về eval. (3) Tôi không thấy lý do tại sao sự phản chiếu sẽ khiến các ngôn ngữ khó biên dịch thành mã gốc. Objective-C có sự phản chiếu và (tôi giả sử) nó thường được biên dịch. (4) Lưu ý liên quan mơ hồ, siêu dữ liệu mẫu C ++ thường được diễn giải thay vì được biên dịch sau đó được thực thi.
Vịt Mooing

Chỉ cần xảy ra với tôi, Lua được biên soạn. Đơn evalgiản chỉ cần biên dịch mã byte, và sau đó là một bước riêng biệt nhị phân thực thi mã byte. Và nó chắc chắn có sự phản ánh trong nhị phân biên dịch.
Vịt Mooing

Trên máy Harvard Architecture, việc biên dịch sẽ mang lại mã không bao giờ phải truy cập dưới dạng "dữ liệu". Tôi sẽ đưa ra thông tin từ tệp nguồn cuối cùng phải được lưu trữ dưới dạng dữ liệu thay vì mã không thực sự được "biên dịch". Không có gì sai với trình biên dịch lấy khai báo như int arr[] = {1,2,5};và tạo phần dữ liệu khởi tạo có chứa [1,2,5], nhưng tôi sẽ không mô tả hành vi của nó là dịch [1,2,5] sang mã máy. Nếu gần như tất cả chương trình phải được lưu trữ dưới dạng dữ liệu, phần nào của chương trình sẽ thực sự được "biên dịch"?
supercat

2
@supercat Đó là những gì các nhà toán học và nhà khoa học máy tính có nghĩa là tầm thường. Nó phù hợp với định nghĩa toán học, nhưng không có gì thú vị xảy ra.
Gilles

@Gilles: Nếu thuật ngữ "biên dịch" được dành riêng cho dịch theo hướng dẫn của máy (không lưu giữ "dữ liệu" liên quan) và chấp nhận rằng trong ngôn ngữ biên dịch, hành vi của khai báo mảng sẽ không "biên dịch" mảng, sau đó có một số ngôn ngữ trong đó không thể biên dịch bất kỳ phần có ý nghĩa nào của mã.
supercat

13

Tôi nghĩ rằng các tác giả đang giả định rằng biên dịch có nghĩa là

  • chương trình nguồn không cần phải có mặt vào thời gian chạy và
  • không có trình biên dịch hoặc trình thông dịch cần phải có mặt tại thời gian chạy.

Dưới đây là một số tính năng mẫu sẽ khiến nó gặp vấn đề nếu không "không thể" cho sơ đồ như vậy:

  1. Nếu bạn có thể thẩm vấn giá trị của một biến trong thời gian chạy, bằng cách tham chiếu biến đó bằng tên của nó (đó là một chuỗi), thì bạn sẽ cần các tên biến xung quanh trong thời gian chạy.

  2. Nếu bạn có thể gọi một hàm / thủ tục vào thời gian chạy, bằng cách gọi nó bằng tên của nó (là một chuỗi), thì bạn sẽ cần các tên hàm / thủ tục trong thời gian chạy.

  3. Nếu bạn có thể xây dựng một phần chương trình vào thời gian chạy (dưới dạng chuỗi), giả sử bằng cách chạy chương trình khác hoặc đọc nó từ kết nối mạng, v.v., thì bạn sẽ cần một trình thông dịch hoặc trình biên dịch vào thời gian chạy chạy đoạn chương trình này

Lisp có tất cả ba tính năng. Vì vậy, các hệ thống Lisp luôn có một trình thông dịch được tải vào thời gian chạy. Các ngôn ngữ Java và C # có sẵn các tên hàm trong thời gian chạy và các bảng để tìm hiểu ý nghĩa của chúng. Có lẽ các ngôn ngữ như Basic và Python cũng có tên biến trong thời gian chạy. (Tôi không chắc chắn 100% về điều đó).


Điều gì xảy ra nếu "trình thông dịch" được biên dịch thành mã? Ví dụ, sử dụng các bảng điều phối để gọi các phương thức ảo, đây có phải là một ví dụ về giải thích hoặc biên dịch không?
Erwin Bolwidt

2
"Không có trình biên dịch hoặc trình thông dịch cần phải có mặt vào thời gian chạy", eh? Chà, nếu đó là sự thật, thì theo nghĩa sâu sắc, C cũng không thể được "biên dịch" trên hầu hết các nền tảng. Thời gian chạy C không có nhiều việc phải làm: khởi động, để thiết lập ngăn xếp và vv, và tắt để atexitxử lý. Nhưng nó vẫn phải ở đó.
Bút danh

1
"Các hệ thống Lisp luôn có một trình thông dịch được tải vào thời gian chạy." - Không cần thiết. Nhiều hệ thống Lisp có trình biên dịch khi chạy. Một số thậm chí không có một thông dịch viên.
Jörg W Mittag

2
Hãy thử, nhưng en.wikipedia.org/wiki/Lisp_machine#Technical_overview . Họ biên dịch Lisp và được thiết kế để thực hiện kết quả một cách hiệu quả.
Peter - Tái lập Monica

@Pseudonymous: Thời gian chạy C là một thư viện, không phải là trình biên dịch cũng như trình thông dịch.
Vịt Mooing

8

có thể các câu trả lời hiện tại đang "lật đổ" câu nói / câu trả lời. có thể những gì các tác giả đang đề cập đến là hiện tượng sau đây. nhiều ngôn ngữ có lệnh "eval" như lệnh; ví dụ: xem javascript eval và hành vi của nó thường được nghiên cứu như một phần đặc biệt của lý thuyết CS (ví dụ: Lisp). chức năng của lệnh này là đánh giá chuỗi trong ngữ cảnh của định nghĩa ngôn ngữ. do đó về mặt hiệu quả, nó có sự tương đồng với "trình biên dịch tích hợp". trình biên dịch không thể biết nội dung của chuỗi cho đến khi runtime. do đó, việc biên dịch kết quả eval thành mã máy là không thể tại thời điểm biên dịch.

các câu trả lời khác chỉ ra rằng sự khác biệt giữa các ngôn ngữ được dịch và các ngôn ngữ được biên dịch có thể làm mờ đi đáng kể trong nhiều trường hợp với các ngôn ngữ hiện đại hơn như nói Java với "trình biên dịch đúng lúc" hay còn gọi là "Hotspot" (các công cụ javascript, ví dụ V8 ngày càng sử dụng kỹ thuật này). Chức năng "giống như eval" chắc chắn là một trong số đó.


2
V8 là một ví dụ tốt. Nó là một trình biên dịch thuần túy, không bao giờ có bất kỳ diễn giải nào đang diễn ra. Tuy nhiên, nó vẫn hỗ trợ đầy đủ ngữ nghĩa của ECMAScript, bao gồm cả không bị hạn chế eval.
Jörg W Mittag

1
Lua làm điều tương tự.
Vịt Mooing

3

LISP là một ví dụ tồi tệ, vì nó được hình thành như một loại ngôn ngữ "máy" cấp cao hơn làm cơ sở cho ngôn ngữ "thực". Nói "thực" ngôn ngữ không bao giờ được thực hiện. Các máy LISP được xây dựng trên ý tưởng thực hiện (phần lớn) LISP trong phần cứng. Vì một trình thông dịch LISP chỉ là một chương trình, về nguyên tắc có thể thực hiện nó trong mạch. Không thực tế, có lẽ; nhưng xa không thể.

Hơn nữa, có rất nhiều trình thông dịch được lập trình bằng silicon, thường được gọi là "CPU". Và nó thường hữu ích để giải thích (chưa tồn tại, không có trong tay, ...) mã máy. Ví dụ: Linux 'x86_64 lần đầu tiên được viết và thử nghiệm trên trình giả lập. Có các bản phân phối đầy đủ trong tay khi các chip xuất hiện trên thị trường, thậm chí chỉ dành cho những người dùng / thử nghiệm sớm. Java thường được biên dịch thành mã JVM, đây là một trình thông dịch không quá khó để viết bằng silicon.

Hầu hết các ngôn ngữ "diễn giải" được biên dịch thành một hình thức nội bộ, được tối ưu hóa và sau đó được giải thích. Đây là ví dụ những gì Perl và Python làm. Ngoài ra còn có các trình biên dịch cho các ngôn ngữ có nghĩa là được giải thích, như shell Unix. Mặt khác, có thể giải thích các ngôn ngữ được biên dịch theo truyền thống. Một ví dụ cực kỳ tôi thấy là một trình soạn thảo sử dụng C được hiểu là ngôn ngữ mở rộng. Đó là C có thể chạy các chương trình bình thường, nhưng đơn giản, không có vấn đề.

Mặt khác, các CPU hiện đại thậm chí còn lấy đầu vào "ngôn ngữ máy" và dịch nó thành các hướng dẫn cấp thấp hơn, sau đó được sắp xếp lại và tối ưu hóa (nghĩa là "được biên dịch") trước khi thực hiện.

Toàn bộ phân biệt "trình biên dịch" và "trình thông dịch" này thực sự rất khó, ở đâu đó trong ngăn xếp có một trình thông dịch cuối cùng lấy "mã" và thực thi nó "trực tiếp". Đầu vào từ lập trình viên trải qua các phép biến đổi dọc theo dòng, cái được gọi là "biên dịch" chỉ là vẽ một đường tùy ý trên cát.


1

Thực tế là có một sự khác biệt lớn giữa việc diễn giải một số chương trình cơ bản và thực thi trình biên dịch chương trình. Và có các khu vực ở giữa với trình biên dịch mã P / mã byte có hoặc không có trình biên dịch (chỉ trong thời gian). Vì vậy, tôi sẽ cố gắng tóm tắt một số điểm trong bối cảnh của thực tế này.

  • Nếu cách mã nguồn được phân tích cú pháp phụ thuộc vào điều kiện thời gian chạy, việc viết trình biên dịch có thể trở nên không thể, hoặc khó đến mức không ai làm phiền.

  • Mã tự sửa đổi là trong trường hợp chung không thể biên dịch.

  • Một chương trình sử dụng hàm giống như eval thường không thể được biên dịch hoàn toàn trước (nếu bạn coi chuỗi được cung cấp cho nó như một phần của chương trình), mặc dù nếu bạn sẽ chạy mã eval'ed nhiều lần thì nó vẫn có thể hữu ích để có chức năng giống như eval của bạn gọi trình biên dịch. Một số ngôn ngữ cung cấp API cho trình biên dịch để thực hiện điều này dễ dàng.

  • Khả năng đề cập đến những thứ theo tên không loại trừ việc biên dịch, nhưng bạn cần các bảng (như đã đề cập). Gọi các chức năng theo tên (như IDispatch) đòi hỏi rất nhiều hệ thống ống nước, đến mức tôi nghĩ rằng hầu hết mọi người sẽ đồng ý rằng chúng tôi đang nói về một trình thông dịch cuộc gọi chức năng một cách hiệu quả.

  • Gõ yếu (bất kể định nghĩa của bạn) làm cho việc biên dịch khó hơn và có lẽ kết quả kém hiệu quả hơn, nhưng thường là không thể, trừ khi các giá trị khác nhau kích hoạt các phân tích khác nhau. Có một thang trượt ở đây: nếu trình biên dịch không thể suy ra kiểu thực tế, thì nó sẽ cần phải phát ra các nhánh, các hàm gọi và nếu không thì sẽ có, thực sự nhúng các bit của trình thông dịch vào tệp thực thi.


1

tôi sẽ cho rằng tính năng chính của ngôn ngữ lập trình làm cho trình biên dịch cho ngôn ngữ là không thể (theo nghĩa chặt chẽ, xem thêm tự lưu trữ ) là tính năng tự sửa đổi . Có nghĩa là ngôn ngữ cho phép thay đổi mã nguồn trong thời gian chạy (sth một trình biên dịch tạo, cố định và tĩnh, mã đối tượng không thể làm được). Một ví dụ kinh điển là Lisp (xem thêm Homoiconicity ). Chức năng tương tự được cung cấp bằng cách sử dụng cấu trúc ngôn ngữ như eval , được bao gồm trong nhiều ngôn ngữ (ví dụ javaScript). Eval thực sự gọi trình thông dịch (dưới dạng hàm) vào thời gian chạy .

Nói cách khác, ngôn ngữ có thể đại diện cho hệ thống meta của chính nó (xem thêm Siêu lập trình )

Lưu ý rằng sự phản ánh ngôn ngữ , theo nghĩa truy vấn về dữ liệu meta của một mã nguồn nhất định và có thể chỉ sửa đổi dữ liệu meta, (giống như cơ chế phản chiếu của Java hoặc PHP) không phảivấn đề đối với trình biên dịch, vì nó đã có siêu dữ liệu tại thời gian biên dịch và có thể cung cấp chúng cho chương trình được biên dịch, nếu cần, nếu cần.

Một tính năng khác làm cho việc biên dịch trở nên khó khăn hoặc không phải là lựa chọn tốt nhất (nhưng không phải là không thể) là lược đồ gõ được sử dụng trong ngôn ngữ (tức là gõ động so với gõ tĩnh và gõ mạnh so với gõ lỏng). Điều này gây khó khăn cho trình biên dịch có tất cả các ngữ nghĩa tại thời gian biên dịch, do đó, một phần của trình biên dịch (nói cách khác là trình thông dịch) trở thành một phần của mã được tạo để xử lý ngữ nghĩa trong thời gian chạy . Nói cách khác, đây không phải là biên dịch mà là giải thích .


LISP là một ví dụ khủng khiếp, vì nó được hình thành như một sprt
vonbrand

@vonbrand, có thể nhưng hiển thị cả khái niệm đồng âm và tính đối ngẫu mã dữ liệu thống nhất
Nikos M.

-1

Tôi cảm thấy câu hỏi ban đầu không được hình thành tốt. Các tác giả của câu hỏi có thể có ý định hỏi một câu hỏi hơi khác: Tính chất nào của ngôn ngữ progamming tạo điều kiện thuận lợi cho việc viết trình biên dịch cho nó?

Ví dụ, viết trình biên dịch cho ngôn ngữ không ngữ cảnh sẽ dễ hơn ngôn ngữ nhạy ngữ cảnh. Ngữ pháp xác định một ngôn ngữ cũng có thể có những vấn đề khiến nó khó biên dịch, chẳng hạn như sự mơ hồ. Những vấn đề như vậy có thể được giải quyết nhưng đòi hỏi nỗ lực thêm. Tương tự, các ngôn ngữ được xác định bởi các ngữ pháp không giới hạn sẽ khó phân tích hơn các ngôn ngữ nhạy cảm theo ngữ cảnh (xem Phân cấp Chomsky ). Theo hiểu biết của tôi, hầu hết các ngôn ngữ lập trình thủ tục được sử dụng rộng rãi đều gần với ngữ cảnh, nhưng có một vài yếu tố nhạy cảm với ngữ cảnh, khiến chúng tương đối dễ biên dịch.


2
Câu hỏi rõ ràng là có ý định chống lại / so sánh trình biên dịch và phiên dịch. Mặc dù chúng có thể hoạt động khác nhau và thường làm ngoại trừ trường hợp giới hạn @Raphael ở trên, chúng có chính xác các vấn đề tương tự về phân tích cú pháp và sự mơ hồ. Vì vậy, cú pháp không thể là vấn đề. Tôi cũng tin rằng vấn đề cú pháp thường không phải là mối quan tâm chính trong việc viết trình biên dịch ngày nay, mặc dù nó đã có trong quá khứ. Tôi không phải là downvoter: Tôi thích bình luận.
babou

-1

Câu hỏi có một câu trả lời chính xác rõ ràng đến mức nó thường bị bỏ qua là tầm thường. Nhưng nó có vấn đề trong nhiều bối cảnh và là lý do chính khiến các ngôn ngữ được giải thích tồn tại:

Không thể biên dịch mã nguồn thành mã máy nếu bạn chưa có mã nguồn.

Thông dịch viên thêm tính linh hoạt và đặc biệt họ thêm tính linh hoạt của mã chạy không khả dụng khi dự án cơ bản được biên dịch.


2
"Tôi bị mất mã nguồn" không phải là một tài sản của ngôn ngữ lập trình mà là một chương trình cụ thể, do đó không trả lời được câu hỏi. Và bạn chắc chắn cần một trích dẫn cho tuyên bố rằng việc tránh mất mã nguồn là "lý do chính khiến các ngôn ngữ được giải thích tồn tại", hoặc thậm chí là một lý do tại sao chúng tồn tại.
David Richerby

1
@DavidR Richby Tôi đoán trường hợp sử dụng tyleri có trong tâm trí là giải thích tương tác, tức là mã được nhập khi chạy. Tuy nhiên, tôi đồng ý rằng điều đó nằm ngoài phạm vi của câu hỏi vì nó không phải là một tính năng của ngôn ngữ.
Raphael

@DavidR Richby và Raphael, tôi nói rằng tác giả của bài đăng này ngụ ý (những gì tôi mô tả trong câu trả lời của tôi) là tính năng tự sửa đổi, tất nhiên là một ngôn ngữ được thiết kế theo thiết kế và không phải là một tạo tác của một chương trình cụ thể
Nikos 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.