Mã cú pháp Roslyn có được sử dụng lại không?


124

Tôi đã xem xét Roslyn CTP và trong khi nó giải quyết một vấn đề tương tự với API cây biểu thức , cả hai đều không thay đổi nhưng Roslyn làm như vậy theo một cách hoàn toàn khác:

  • Expressioncác nút không có tham chiếu đến nút cha, được sửa đổi bằng cách sử dụng a ExpressionVisitorvà đó là lý do tại sao các phần lớn có thể được sử dụng lại.

  • SyntaxNodeMặt khác, Roslyn's có tham chiếu đến cha mẹ của nó, vì vậy tất cả các nút thực sự trở thành một khối không thể sử dụng lại. Các phương pháp như Update, ReplaceNodev.v., được cung cấp để thực hiện các sửa đổi.

Điều này kết thúc ở đâu? Document? Project? ISolution? API thúc đẩy thay đổi từng bước của cây (thay vì nút lên), nhưng mỗi bước có tạo ra một bản sao đầy đủ không?

Tại sao họ lại lựa chọn như vậy? Có một số mẹo thú vị mà tôi đang bỏ lỡ?

Câu trả lời:


181

CẬP NHẬT: Câu hỏi này là chủ đề trên blog của tôi vào ngày 8 tháng 6 năm 2012 . Cảm ơn vì câu hỏi tuyệt vời của bạn!


Câu hỏi tuyệt vời. Chúng tôi đã tranh luận về các vấn đề bạn nêu ra trong một thời gian dài.

Chúng tôi muốn có một cấu trúc dữ liệu có các đặc điểm sau:

  • Bất biến.
  • Các hình thức của một cái cây.
  • Truy cập rẻ vào các nút cha từ các nút con.
  • Có thể ánh xạ từ một nút trong cây sang một ký tự bù đắp trong văn bản.
  • Kiên trì .

Tính bền bỉ, ý tôi là khả năng sử dụng lại hầu hết các nút hiện có trong cây khi chỉnh sửa được thực hiện đối với bộ đệm văn bản. Vì các nút là bất biến, không có rào cản nào đối với việc sử dụng lại chúng. Chúng tôi cần điều này cho hiệu suất; chúng tôi không thể phân tích cú pháp lại các wod rất lớn của tệp mỗi khi bạn nhấn một phím. Chúng tôi cần phải tái lex và chỉ phân tích cú pháp lại các phần của cây đã bị ảnh hưởng bởi chỉnh sửa.

Bây giờ khi bạn cố gắng đặt tất cả năm thứ đó vào một cấu trúc dữ liệu, bạn ngay lập tức gặp vấn đề:

  • Làm thế nào để bạn xây dựng một nút ngay từ đầu? Cha mẹ và con đều tham chiếu lẫn nhau, và là bất biến, vậy cái nào được xây dựng trước?
  • Giả sử bạn xoay sở để giải quyết vấn đề đó: bạn làm cách nào để khiến nó tồn tại lâu dài? Bạn không thể sử dụng lại một nút con trong một nút cha khác vì điều đó sẽ liên quan đến việc nói với nút con rằng nó có nút cha mới. Nhưng đứa trẻ là bất biến.
  • Giả sử bạn có thể giải quyết vấn đề đó: khi bạn chèn một ký tự mới vào bộ đệm chỉnh sửa, vị trí tuyệt đối của mọi nút được ánh xạ tới một vị trí sau điểm đó sẽ thay đổi. Điều này gây khó khăn cho việc tạo cấu trúc dữ liệu liên tục, vì bất kỳ chỉnh sửa nào cũng có thể thay đổi nhịp của hầu hết các nút!

Nhưng trong đội Roslyn, chúng tôi thường xuyên làm những điều không thể. Chúng tôi thực sự làm điều không thể bằng cách giữ hai cây phân tích cú pháp. Cây "xanh" là bất biến, bền bỉ, không có tham chiếu cha, được xây dựng "từ dưới lên" và mọi nút đều theo dõi chiều rộng nhưng không phải vị trí tuyệt đối của nó . Khi một chỉnh sửa xảy ra, chúng tôi chỉ xây dựng lại các phần của cây xanh bị ảnh hưởng bởi chỉnh sửa, thường là khoảng O (log n) của tổng số nút phân tích cú pháp trong cây.

Cây "đỏ" là mặt tiền bất di bất dịch được xây dựng xung quanh cây xanh; nó được xây dựng "từ trên xuống" theo yêu cầu và được loại bỏ sau mỗi lần chỉnh sửa. Nó tính toán các tham chiếu gốc bằng cách sản xuất chúng theo yêu cầu khi bạn đi xuống cây từ trên cùng . Nó tạo ra các vị trí tuyệt đối bằng cách tính toán chúng từ độ rộng, một lần nữa, khi bạn đi xuống.

Bạn, người dùng, chỉ bao giờ nhìn thấy cây đỏ; cây xanh là một chi tiết thực hiện. Nếu bạn nhìn vào trạng thái bên trong của một nút phân tích cú pháp, trên thực tế, bạn sẽ thấy rằng có một tham chiếu đến một nút phân tích cú pháp khác trong đó thuộc loại khác; đó là nút cây xanh.

Ngẫu nhiên, chúng được gọi là "cây đỏ / xanh" bởi vì đó là những màu đánh dấu trên bảng trắng mà chúng tôi sử dụng để vẽ cấu trúc dữ liệu trong cuộc họp thiết kế. Không có ý nghĩa nào khác đối với màu sắc.

Lợi ích của chiến lược này là chúng tôi nhận được tất cả những điều tuyệt vời đó: tính bất biến, tính bền bỉ, tài liệu tham khảo dành cho cha mẹ, v.v. Cái giá phải trả là hệ thống này phức tạp và có thể tiêu tốn nhiều bộ nhớ nếu các mặt "đỏ" trở nên lớn. Hiện tại, chúng tôi đang thực hiện các thí nghiệm để xem liệu chúng tôi có thể giảm một số chi phí mà không làm mất đi lợi ích hay không.


3
Và để giải quyết một phần câu hỏi của bạn về IProject và IDocuments: chúng tôi sử dụng một mô hình tương tự trong lớp dịch vụ. Bên trong có các loại "DocumentState" và "ProjectState" tương đương về mặt đạo đức với các nút xanh của cây cú pháp. Các đối tượng IProject / IDocument mà bạn nhận được là mặt tiền nút đỏ cho những đối tượng này. Nếu bạn nhìn vào việc triển khai Roslyn.Services.Project trong một trình dịch ngược, bạn sẽ thấy rằng hầu hết tất cả các lệnh gọi đều chuyển tiếp đến các đối tượng trạng thái bên trong.
Jason Malinowski

@Eric xin lỗi vì nhận xét, nhưng bạn đang tự mâu thuẫn với chính mình. The expense and difficulty of building a complex persistent data structure doesn't pay for itself.ref: stackoverflow.com/questions/6742923/… Nếu bạn có mục tiêu hiệu suất cao, tại sao ngay từ đầu bạn đã đặt mục tiêu đó là bất biến? Chỉ có lý do nào khác ngoài những lý do rõ ràng? Ví dụ: dễ dàng hơn để tạo threadsafe, để suy luận về v.v.
Lukasz Madon

2
@lukas Bạn đang lấy câu nói đó ra khỏi ngữ cảnh. Câu trước đó là "Bởi vì khi bạn nhìn vào các hoạt động thường được thực hiện trên chuỗi trong các chương trình .NET, theo mọi cách có liên quan hầu như không tệ hơn chút nào nếu chỉ cần tạo một chuỗi hoàn toàn mới." OTOH, khi bạn xem xét các thao tác thường được thực hiện trên cây biểu thức - ví dụ: nhập một vài ký tự vào tệp nguồn - việc xây dựng một cây biểu thức hoàn toàn mới sẽ tệ hơn đáng kể. Vì vậy, họ chỉ xây dựng một nửa của nó.
Timbo

1
@lukas Suy đoán của tôi: Do Roslyn phải hoạt động trên các luồng nền, tính bất biến cho phép nhiều luồng phân tích cùng một mã nguồn cùng một lúc mà không cần lo lắng rằng nó sẽ bị thay đổi khi người dùng nhấn một phím. Để đáp ứng với đầu vào của người dùng, các cây bất biến có thể được cập nhật mà không cần dừng các tác vụ phân tích đang chạy. Vì vậy, tôi tưởng tượng rằng mục tiêu chính của Immutability là làm cho Roslyn dễ viết hơn (và có lẽ dễ sử dụng hơn cho khách hàng).
Qwertie

3
@lukas Cấu trúc dữ liệu liên tục hiệu quả hơn sao chép, khi cấu trúc dữ liệu thường lớn hơn nhiều so với những thay đổi đối với nó. Điểm của bạn, nếu bạn có, sẽ bị mất vào tôi.
Qwertie
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.