2010-05-07 34 views
20

Tim Sweeney của Epic MegaGames là nhà phát triển chính cho Unreal và programming language geek. Nhiều năm trước đây đăng tải ảnh chụp màn hình sau đây để VoodooExtreme:Tim Sweeney nghĩ gì? (Trình phân tích cú pháp C++ này hoạt động như thế nào?)

Tim Sweeney's screenshot

Là một lập trình viên C++ và quạt Sweeney, tôi đã quyến rũ của thành viên này. Nó cho thấy mã C++ chung thực hiện một số loại ngôn ngữ kịch bản mà bản thân ngôn ngữ đó có vẻ là chung chung theo nghĩa là nó có thể định nghĩa ngữ pháp riêng của nó.

Ông Sweeney chưa bao giờ giải thích. Rất hiếm khi thấy cấp độ lập trình mẫu này, nhưng thỉnh thoảng bạn thấy nó khi mọi người muốn đẩy trình biên dịch tạo mã lớn hoặc vì họ muốn tạo mã chung (ví dụ: Modern C++ Design)).

Tim dường như đang sử dụng nó để tạo ngữ pháp trong Parser.cpp - bạn có thể thấy những gì trông giống như toán tử nhị phân ưu tiên. Nếu đúng như vậy, thì tại sao Test.ae lại có vẻ như là cũng là xác định ngữ pháp?

Rõ ràng đây là một câu đố cần được giải quyết. Chiến thắng đi đến câu trả lời với một phiên bản làm việc của mã này, hoặc lời giải thích hợp lý nhất, hoặc cho chính Tim Sweeney nếu anh ta viết một câu trả lời. :-)

+9

Tôi nghĩ chủ đề/màu sắc gây sốc. Nếu không, câu hỏi thú vị. – ChristopheD

+1

@ChristopheD Haha yeah. Tôi đã là một fan hâm mộ vào thời điểm đó mặc dù tôi đã thay đổi máy tính để bàn của tôi là như nhau - nó phải có được như thế nào * thực * lập trình làm việc! Tôi luôn tự hỏi liệu anh ta có đang đùa hay không. Và hình ảnh tôi đăng được cắt, đây là toàn bộ: http://praeclarum.org/so/sweeney-full.png –

+2

Có thể anh ấy đang làm việc trên phiên bản thế hệ tiếp theo của ZZT-OOP ... :) http : //en.wikipedia.org/wiki/ZZT-oop –

Trả lời

3

Không thể biết chắc chắn, nhưng mã C++ phân loại trông giống như Spirit, trình tạo trình phân tích cú pháp C++ sử dụng rộng rãi các mẫu. Test.ae trông giống như metaprogramming (định nghĩa ngôn ngữ trong chính ngôn ngữ), khó thực hiện hơn trong C++ (các mẫu là khởi đầu, nhưng dễ bị lỗi và xấu xí) so với ngôn ngữ đích khác (ví dụ, UnrealScript) , đó là những gì tôi giả định test.ae được viết trong).

Vì vậy - có vẻ như Parser.cpp xác định ngữ pháp cơ sở cho UnrealScript (sử dụng Spirit) và Test.ae đang xác định tiện ích mở rộng cho UnrealScript.

+0

Trong khi cả Spirit và mã này sử dụng các mẫu, chúng dường như không phải là cùng một thư viện. Hay tôi đang thiếu một cái gì đó? Nó không giống như thế này là cách mà Tinh Linh giải quyết quyền ưu tiên của nhà điều hành. Và không có giả định của bạn là sai. Các tệp AE này là một cách lâu dài từ UnrealScript. Tại thời điểm này, Tim đã nói công khai về việc thay thế trình biên dịch của UnrealScript, nhưng điều này chắc chắn là một sự phá vỡ từ cú pháp đó. –

+0

Tôi biết * không có gì * về UnrealScript hoặc Spirit, cho vấn đề đó; có một số trình tạo trình phân tích cú pháp dựa trên mẫu cho C++ theo kiểu của Spirit. Điều đó đang được nói, tuy nhiên, tôi tin rằng tôi đã minh họa ý tưởng chung về lập trình meta chính xác; Câu trả lời của Ira rằng việc sử dụng các mẫu C++ thuần túy có thể chậm để biên dịch và tạo ra các thông báo lỗi rất khó đọc là một lý do khác để xác định ngôn ngữ cơ bản trong C++ và sau đó xác định ngôn ngữ nâng cao về ngôn ngữ cơ bản. Kỹ thuật này còn được gọi là 'bootstrapping'. –

1

Tôi không biết Sweeney đã làm gì, và tôi sẽ giả định rằng các câu trả lời khác về việc sử dụng Thánh Linh ở trong, uh, đúng tinh thần. Tôi không có kinh nghiệm với các mẫu Spirit, nhưng sự hiểu biết của tôi là nếu bạn định nghĩa một ngữ pháp phức tạp với nó, nó trở nên khá khó xử lý (cũng như chậm để biên dịch). Kinh nghiệm thực tế của người khác nên được sử dụng để nói lên sự thật về điều này.

Có nhiều cách khác để triển khai tiện ích mở rộng cho C++, ví dụ: bằng cách sử dụng phép biến đổi chương trình và ngữ pháp có thể mở rộng. Xem this SO answer on augmenting the C++ grammar chính nó với các phần mở rộng tùy ý, trong đó các phần mở rộng rất phức tạp có thể thực tế và được sử dụng.

Lập trình meta mẫu tạo ra mã thú vị trong đó các mẫu được gọi cụ thể. Sử dụng các phép biến đổi chương trình, bạn có thể tạo ra mã ngẫu nhiên thú vị tại bất kỳ điểm nào trong chương trình, ví dụ, nếu như "các mẫu" (cú pháp bổ sung) thay đổi ngữ nghĩa theo bất kỳ cách nào bạn nghĩ là hữu ích.

1

Ảnh chụp màn hình rõ ràng là từ khung thời gian MSVC 6.0 hoặc trước đó, không thực sự đánh giá cao các mẫu phức tạp (và chắc chắn không hỗ trợ chuyên môn mẫu từng phần). Tôi đã không sử dụng tinh thần. Từ những ảnh chụp màn hình này, không thể nói Sweeney thực sự đang làm gì ngoài việc xác định những gì có vẻ là một DSL trong Test.ae.

Chỉ có các câu lệnh C++ đầy đủ mà bạn có thể thấy trong Parser.cpp - và chúng không cho bạn biết nhiều thứ ngoại trừ việc anh ta khai báo 3 kiểu. Bạn thực sự không thể nói nhiều về bất cứ điều gì - quá nhiều bị che khuất bởi 'Bài kiểm tra.ae 'cửa sổ.

8

Tôi hỏi ông Sweeney qua email và nhận được câu trả lời này:

C++ đang sử dụng các lớp mẫu tôi đã viết rằng thực hiện bộ phối hợp phân tích cú pháp. Ý tưởng là bắt đầu với một số trình phân tích cú pháp cơ bản, chẳng hạn như literals nơi PLit < 'A'> phân tích cú pháp một ký tự chữ 'A', PAny <> phân tích bất kỳ ký tự nào nhưng không thành công nếu kết thúc thất bại, PEof bị lỗi trừ khi chúng tôi không ở cuối tập tin, vv Và sau đó chúng ta kết hợp chúng thành các cây tùy ý bằng cách sử dụng các bộ phối hợp như PAnd phân tích cú pháp a và b, và chỉ thành công nếu cả hai thành công - nếu không nó sẽ không có điểm phân tích. Và nếu nó thành công, kết quả là một cấu trúc chứa hai trường, một cho kết quả của một và một cho kết quả của b. Và cứ thế. Việc triển khai thực hiện lộn xộn trong C++ vì một số lý do, bao gồm các mẫu đó không hỗ trợ các tham số tùy ý variadic và không có lambdas hạng nhất, chúng tôi không thể dễ dàng xử lý các kết quả trực tiếp với trình phân tích cú pháp.

Dưới đây là một số đoạn mã mẫu, từ đó bạn có thể tìm hiểu chi tiết của khuôn khổ.

// Parses a literal item. 
UBOOL LiteralEvaluate (UClass* C,class FParseInBase& In,class FParseOutBase& Out) 
{ 
FParseInMark M(In); 
NNode* e = In.GetNextSource(); 
if (e && e->IsA(C)) 
{ 
    Out.Callback(e); 
    return 1; 
} 
M.Restore(In); 
return 0; 
} 
// Optional item of the specified type. 
template <class U> struct Optional 
{ 
static UBOOL Evaluate (class FParseInBase& In,FParseOutBase& Out) 
{ 
    U::Evaluate(In,Out); 
    return 1; 
} 
}; 
// Ignore items by absorbing them; retains boolean logic but not callback. 
template <class T> struct Ignore 
{ 
static UBOOL Evaluate (class FParseInBase& In,FParseOutBase& Out) 
{ 
    return T::Evaluate(In,GIgnore); 
} 
}; 
// Zero or more items of the specified type. 
template <class T> struct ZeroOrMore 
{ 
static UBOOL Evaluate (class FParseInBase& In,FParseOutBase& Out) 
{ 
    while (T::Evaluate(In,Out)); 
    return 1; 
} 
}; 
// One or more items of the specified type. 
template <class T> struct OneOrMore 
{ 
static UBOOL Evaluate (class FParseInBase& In,FParseOutBase& Out) 
{ 
    for(INT i=0; T::Evaluate(In,Out); i++); 
    return i>0; 
} 
}; 
// Always fails. 
struct RFalse 
{ 
static UBOOL Evaluate (class FParseInBase& In,FParseOutBase& Out) 
{ 
    return 0; 
} 
}; 
// Always succeeds. 
struct RTrue 
{ 
static UBOOL Evaluate (class FParseInBase& In,FParseOutBase& Out) 
{ 
    return 1; 
} 
}; 
// Parses the first matching items of the specified subtypes of T. 
template <class A,class B=RFalse,class C=RFalse,class D=RFalse > struct Or 
{ 
static UBOOL Evaluate (class FParseInBase& In,FParseOutBase& Out) 
{ 
    return A::Evaluate(In,Out) || B::Evaluate(In,Out) || C::Evaluate(In,Out) || D::Evaluate(In,Out); 
} 
}; 
// Parses all the specified items. 
template <class A,class B=RTrue,class C=RTrue,class D=RTrue> struct And 
{ 
static UBOOL Evaluate (class FParseInBase& In,FParseOutBase& Out) 
{ 
    FParseInMark Mark(In); 
    Conjunction<NNode> Q; 
    if(A::Evaluate(In,Q) && B::Evaluate(In,Q) && C::Evaluate(In,Q) && D::Evaluate(In,Q)) 
    { 
    Q.Forward(Out); 
    return 1; 
    } 
    Mark.Restore(In); 
    return 0; 
} 
}; 
// A separated list. 
template <class A,class B> class SeparatedList : public Or<And<A,B,SeparatedList>,A> {}; 
// Integer comparison. 
template <INT A,INT B> struct IsAtLeast 
{ 
static UBOOL Evaluate (class FParseInBase& In,FParseOutBase& Out) 
{ 
    return A>=B; 
} 
}; 

Test.ae là ngôn ngữ kịch bản thử nghiệm mà tôi đã triển khai trong giai đoạn 1999-2001 - phối màu đó là hợp thời trang, tôi xin thề. :-)

Mã được hiển thị xác định siêu dữ liệu cho cấu trúc ngôn ngữ. Ngôn ngữ đã đi một chặng đường dài trên con đường Smalltalk "mọi thứ là một đối tượng", đối phó với metaclasses hạng nhất và các vấn đề liên quan, nhưng cuối cùng tôi đã từ bỏ nó khi tôi trở nên quen thuộc với hệ thống kiểu tiên tiến làm việc trong Haskell, Cayenne, Coq và ngôn ngữ.

Ngày nay -

Tôi không phải là một fan hâm mộ thực hiện phân tích cú pháp hoặc các trình biên dịch C++, vì mã có xu hướng được cồng kềnh bằng 70-80% so với thực hiện tương tự trong một ngôn ngữ chức năng hiện đại như Haskell. Đọc trên các bộ phối hợp trình phân tích cú pháp Haskell để biết thêm chi tiết - kết quả đơn giản và trực tiếp là mẫu mực và nó được thực hiện theo cách an toàn, an toàn.

+0

Ồ, tôi bị thổi bay đi. Tôi không biết Sweeney là một người đam mê ngôn ngữ, nhưng ở đây anh ta đang làm các bộ phối hợp phân tích cú pháp vào năm 1999 và bây giờ giới thiệu Haskell để viết các trình biên dịch. Anh hùng mới của tôi! – CodexArcanum

Các vấn đề liên quan