یک مثال عملی از RakuAST

چندی پیش شخصی در #raku پرسید که آیا امکان ایجاد کلاس کاراکتر Raku با کاراکترهای معتبر توسط یک رشته وجود دارد یا خیر. این هست نه در حال حاضر در راکو امکان پذیر است. اما آن است با استفاده از RakuAST امکان پذیر است!
بیایید ابتدا ببینیم که چگونه می توان با استفاده از RakuAST کلاس های کاراکتر ایجاد کرد .AST
روش به یک مثال
به هر حال، همه این مثال ها فرض می کنند که الف
use experimental :rakuast;
یاuse v6.e.PREVIEW
فعال است.
say 'my token {<[abc]>}'.AST.statements.head.expression;
کاری که این می کند این است
- ایجاد AST (
.AST
) برای یک ناشناسtoken
با یک charclas برای حروف “a”، “b” و “c” - سپس به عبارت اول میرود (
.statements.head
) - سپس به آن میپرد
.expression
زیرا .AST
یک لیست عبارت را برمی گرداند، و ما فقط به بیان عبارت اول علاقه مند هستیم.
نتیجه این است:
RakuAST::TokenDeclaration.new(
body => RakuAST::Regex::Assertion::CharClass.new(
RakuAST::Regex::CharClassElement::Enumeration.new(
elements => (
RakuAST::Regex::CharClassEnumerationElement::Character.new("a"),
RakuAST::Regex::CharClassEnumerationElement::Character.new("b"),
RakuAST::Regex::CharClassEnumerationElement::Character.new("c"),
)
)
)
);
بنابراین به نظر می رسد که هر شخصیت در charclass جداگانه است RakuAST::Regex::CharClassEnumerationElement::Character
هدف – شی. با این دانش، ساختن سفارشی بسیار آسان است token
برای کاراکترهای یک رشته داده شده بیایید یک زیر روال “chars-matcher” ایجاد کنیم که یک توکن با یک charclas از کاراکترها برای یک رشته معین ایجاد می کند:
sub chars-matcher($string) {
my @elements = $string.comb.unique.map: {
RakuAST::Regex::CharClassEnumerationElement::Character.new($_)
}
RakuAST::TokenDeclaration.new(
body => RakuAST::Regex::Assertion::CharClass.new(
RakuAST::Regex::CharClassElement::Enumeration.new(:@elements)
)
).EVAL
}
ابتدا یک آرایه “@elements” ایجاد می کنیم و آن را با یک شیء شمارش برای هر کاراکتر منحصر به فرد در رشته داده شده پر می کنیم ($string.comb.unique
). و سپس شی TokenDeclaration را مانند مثال ایجاد می کنیم، اما با @elements
آرایه به عنوان مشخصات کاراکترها. و سپس آن را به یک قابل استفاده واقعی تبدیل می کنیم token
با دویدن .EVAL
بر روی آن.
نمونه ای از کاربرد آن:
my $matcher = chars-matcher("Anna Mae Bullock");
say "Tina Turner" ~~ $matcher; # 「n」
say "Tina Turner" ~~ / $matcher+ /; # 「na 」
همانطور که می بینید، می توانید از تولید شده استفاده کنید token
مستقیم در یک مسابقه هوشمند یا می توانید از آن به عنوان بخشی از یک regex پیچیده تر استفاده کنید.
اگر آماده هستید تا با RakuAST بیشتر آشنا شوید، احتمالاً ایده خوبی است که کمی از نحوه اجرای آن در حال حاضر بدانید. پس بیایید کمی به آن بپردازیم تا برخی از خطاهایی را که ممکن است هنگام نوشتن کد زبان برنامه نویسی Raku برای ایجاد AST با آنها مواجه شوید، بهتر درک کنیم.
پیش نیازهای RakuAST
هدف این است که تمام کد منبع Raku در آینده توسط دستور زبان Raku (و اقدامات مرتبط) (جدید) تجزیه شود، درست همانطور که اکنون در گرامر قدیمی انجام می شود. از آنجایی که تنظیمات هسته Raku (که شامل کد Raku برای اکثر پیاده سازی زبان برنامه نویسی Raku است) نیز باید توسط این تجزیه و تحلیل شود، به این معنی است که کلاس های RakuAST باید وجود داشته باشند. قبل از هر زبان برنامه نویسی Raku وجود دارد.
این یک مشکل مرغ و تخم مرغ است که در راکودو با اصطلاح “بوت استرپ” حل می شود. این بخش کاملاً قابل توجهی از کد NQP است که “به صورت دستی” عملکرد کافی را ایجاد می کند تا به تنظیمات هسته Raku اجازه دهد تا خود را به یک پیاده سازی کاملاً کاربردی از زبان برنامه نویسی Raku بسازد.
چه زمانی جاناتان ورتینگتون پروژه RakuAST را شروع کردند، آنها نمیخواستند دوباره همه این قابلیتها را در NQP پیادهسازی کنند. بنابراین آنها با ایجاد یک تجزیه کننده نسبتاً ساده که کدهای Raku مانند را می خواند و آن را به کد منبع NQP تبدیل می کرد که همه کدها را ایجاد می کرد، یک هک دقیق ابداع کردند. 360+ کلاس های RakuAST هنگام اجرا. البته، آن کد شبیه به راکو هنوز قابلیتهای کامل راکو را ندارد، اما کار پیادهسازی را بسیار سادهتر از آنچه که اگر همه آنها در NQP نوشته میشد، آسانتر میکند.
نمونه ای از داخلی های کلاس RakuAST
بیایید یک مثال ساده از چنین کلاس RakuAST را بررسی کنیم. به عنوان مثال، RakuAST::StrLiteral
کلاسی که قبلاً در قسمت اول دیده بودیم.
class RakuAST::StrLiteral is RakuAST::Literal {
has Str $.value;
method new(Str $value) {
my $obj := nqp::create(self);
nqp::bindattr($obj, RakuAST::StrLiteral, '$!value', $value);
$obj
}
# other methods
method IMPL-EXPR-QAST(RakuAST::IMPL::QASTContext $context) {
my $value := $!value;
$context.ensure-sc($value);
my $wval := QAST::WVal.new( :$value );
QAST::Want.new($wval,'Ss', QAST::SVal.new(:value(nqp::unbox_s($value))))
}
}
برای سادگی، تنها دو روش نشان داده شده است. این new
روش، گرفتن یک موقعیت واحد Str $value
. که به مقداری NQP برای ایجاد شی و پیوند دادن مقدار به آن نیاز دارد $!value
صفت.
و ما یک IMPL-EXPR-QAST
روش. هر زمان که آن شی RakuAST نیاز به تولید QAST (پیش ساز بایت کد واقعی) داشته باشد، این روش فراخوانی می شود.
اگر به نظر شما این بسیار جالب است، احتمالاً می خواهید RakuAST README را بخوانید. و کد منبع واقعی کلاس های RakuAST را می توان در همان دایرکتوری یافت. و اگر واقعاً احساس ماجراجویی میکنید و مخزن Rakudo را بررسی کردهاید، میتوانید به کد NQP تولید شده نگاهی بیندازید. gen/moar/ast.nqp
.
چه فایده ای دارد؟
پس چرا من این را ذکر می کنم؟ از آنجا که RakuAST
کلاس ها نگاه کن مثل کلاسهای Raku واقعی هستند، اما واقعاً زیرروالهای NQP هستند که شبیه کلاسهای Raku به نظر میرسند. که در صورت بروز خطا در تماسهای شما، منجر به حالتهای شکست غیرمنتظره میشود RakuAST
کلاس ها. به عبارت دیگر: لبهها با کلاسهای RakuAST کمی واضحتر هستند و پیامهای خطای LTA میتوانند اتفاق بیفتند. این یکی از “مزایای” زندگی در حاشیه است!
البته، به عنوان کاربر کلاس های RakuAST، فقط باید به آن علاقه مند باشید new
روش، و هر روش غیر داخلی دیگر. متاسفانه راهش هست خیلی زود در بوت استرپ برای علامت گذاری متدهای داخلی با علامت is implementation-detail
ویژگی، بنابراین اکتشافی دیگری مورد نیاز است. و این چنین خواهد بود: “هر روش ALL-CAPS را غیرمجاز در نظر بگیرید”.
نتیجه
این قسط یک مثال واقعی از نحوه استفاده از RakuAST در کد امروز ارائه می دهد. و برخی پیش زمینه های فنی در مورد اجرای کلاس های RakuAST ارائه می دهد که هنوز در لبه ها کمی واضح است.
مخاطبان مورد نظر آن دسته از افرادی هستند که مایلند اولین پذیرندگان این ویژگی های جدید هیجان انگیز در زبان برنامه نویسی Raku باشند.