| RE2C(1) | RE2C(1) |
نام (NAME)
re2c - تولید تحلیلگرهای لغوی سریع برای C/C++
خلاصه دستور (SYNOPSIS)
نکته: برخی از مثالها به زبان C++ هستند (اما میتوان آنها را با C تطبیق داد).
re2c [ OPTIONS ] [ WARNINGS ] INPUT
ورودی میتواند یک فایل یا - برای stdin باشد.
مقدمه (INTRODUCTION)
ابزار re2c به عنوان یک پیشپردازنده عمل میکند. این ابزار فایل ورودی (که معمولاً برنامهای به زبان C/C++ است، اما میتواند هر چیز دیگری باشد) را میخواند و به دنبال بلوکهایی از کد میگردد که در نشانگرهای آغاز/پایان با قالب خاص محصور شدهاند. متنِ خارج از این بلوکها عیناً به فایل خروجی کپی میشود. محتویات این بلوکها توسط re2c پردازش میشوند؛ ابزار آنها را به کدهای C/C++ ترجمه کرده و کد تولیدشده را در خروجی به جای بلوک مربوطه قرار میدهد.
در ادامه مثالی از یک برنامه کوچک آورده شده است که بررسی میکند آیا یک رشته دادهشده حاوی یک عدد دهدهی است یا خیر:
// re2c $INPUT -o $OUTPUT -i --case-ranges
#include <assert.h>
int lex(const char* YYCURSOR) {
/*!re2c
re2c:yyfill:enable = 0;
re2c:define:YYCTYPE = "unsigned char";
[1-9][0-9]* { return 0; }
* { return 1; }
*/
}
int main() {
assert(lex("1234") == 0);
return 0;
}
در خروجی، re2c بلوک میانی را با کد تولیدشده جایگزین کرده است:
/* Generated by re2c */
// re2c $INPUT -o $OUTPUT -i --case-ranges
#include <assert.h>
int lex(const char* YYCURSOR) {
{
unsigned char yych;
yych = *YYCURSOR;
switch (yych) {
case '1' ... '9': goto yy2;
default: goto yy1;
}
yy1:
++YYCURSOR;
{ return 1; }
yy2:
yych = *++YYCURSOR;
switch (yych) {
case '0' ... '9': goto yy2;
default: goto yy3;
}
yy3:
{ return 0; }
}
}
int main() {
assert(lex("1234") == 0);
return 0;
}
یک برنامه re2c از دنبالهای از بلوکها ترکیبشده با کد به زبان مقصد تشکیل شده است. یک بلوک میتواند شامل تعاریف، پیکربندیها، قواعد، کنشها و دستورالعملها با هر ترتیبی باشد:
- name = regular-expression ;
- یک تعریف، name را به regular-expression مقید میکند. نامها میتوانند شامل نویسههای الفباییعددی و خط زیر باشند. بخش عبارات باقاعده نمای کلی نحو re2c برای عبارات باقاعده را ارائه میدهد. پس از تعریف، name میتواند در سایر عبارات باقاعده و در قواعد استفاده شود. بازگشت در تعاریف نامگذاریشده مجاز نیست، و هر نام باید پیش از استفاده تعریف شود. یک بلوک تعاریف نامگذاریشده را از دامنه سراسری به ارث میبرد. تعریف مجدد نامی که در دامنه فعلی وجود دارد خطا است.
- configuration = value ;
- یک پیکربندی امکان تغییر رفتار re2c و سفارشیسازی کد تولیدشده را فراهم میکند. برای مشاهده فهرست کامل پیکربندیهای پشتیبانیشده توسط re2c، بخش پیکربندیها را ببینید. بسته به پیکربندی مشخص، value میتواند یک کلیدواژه، یک عدد صحیح غیرمنفی یا یک رشته تکخطی باشد که باید در میان گیومه تکی یا دوتایی قرار گیرد، مگر اینکه فقط شامل نویسههای الفباییعددی باشد. یک بلوک پیکربندیها را از دامنه سراسری به ارث میبرد و میتواند آنها را مجدداً تعریف کرده یا موارد جدیدی اضافه کند. پیکربندیهای تعریفشده درون یک بلوک بر کل بلوک تأثیر میگذارند، حتی اگر در انتهای آن قرار گرفته باشند.
- regular-expression code
- یک قاعده، regular-expression را به کنش معنایی آن مقید میکند (بلوکی از کد درون آکوالادها، یا بلوکی از کد که با := شروع شده و به یک خط جدید به همراه هر نویسه غیر فاصله ختم میشود). اگر regular-expression مطابقت داشته باشد، code مرتبط اجرا میشود. اگر چندین قاعده مطابقت داشته باشند، طولانیترین تطابق اولویت دارد. اگر چندین قاعده با رشته یکسانی مطابقت داشته باشند، قاعدهای که زودتر آمده اولویت دارد. دو قاعده ویژه وجود دارد: قاعده پیشفرض * و قاعده پایان ورودی $. قاعده پیشفرض باید همیشه تعریف شود، صرفنظر از جایگاهش در بلوک دارای پایینترین اولویت است، و با هر واحد کد مطابقت مییابد (نه لزوماً یک نویسه معتبر، به بخش پشتیبانی از کدگذاری مراجعه کنید). قاعده پایان ورودی در صورتی باید تعریف شود که روش متناظر برای مدیریت پایان ورودی به کار گرفته شده باشد. به همراه شرایط شروع، قواعد نحو پیچیدهتری دارند.
- !action code
- یک کنش یک بلوک code تعریفشده توسط کاربر را به نقطه مشخصی در ماشین وضعیت متناهی تولیدشده متصل میکند (به همان شیوهای که کنشهای معنایی کد را به وضعیتهای پایانی مقید میکنند). برای فهرست کاملی از کنشهای از پیش تعریفشده، بخش کنشها را ببینید.
- !directive ;
- یک دستورالعمل یکی از گزارههای ویژه از پیش تعریفشده است. هر دستورالعمل هدف منحصربهفردی دارد. برای جزئیات به بخش دستورالعملها مراجعه کنید.
بلوکها (Blocks)
نشانگرهای آغاز و پایان بلوک یا /*!re2c و */ هستند، یا %{ و %} (از هر دو سبک پشتیبانی میشود). از نسخه 2.2 به بعد، بلوکها میتوانند نامهای اختیاری داشته باشند که امکان ارجاع به آنها را در سایر بلوکها فراهم میکند. انواع مختلفی از بلوکها وجود دارد:
- /*!re2c[:<name>] ... */ یا %{[:<name>] ... %}
- یک بلوک سراسری شامل تعاریف، پیکربندیها، قواعد و دستورالعملها است. re2c عبارات باقاعده مرتبط با هر قاعده را به یک اتوماتون متناهی معین کامپایل میکند، آن را در قالب پرشهای شرطی در زبان مقصد کدگذاری کرده و بلوک را با کد تولیدشده جایگزین مینماید. نامها و پیکربندیهای تعریفشده در یک بلوک سراسری به دامنه سراسری اضافه شده و برای بلوکهای بعدی قابل مشاهده میشوند. در آغاز برنامه، دامنه سراسری با گزینههای خط فرمان مقداردهی اولیه میشود.
- /*!local:re2c[:<name>] ... */ یا %{local[:<name>] ... %}
- یک بلوک محلی مانند یک بلوک سراسری است، اما نامها و پیکربندیهای موجود در آن دارای دامنه محلی هستند (بر سایر بلوکها تأثیری نمیگذارند).
- /*!rules:re2c[:<name>] ... */ یا %{rules[:<name>] ... %}
- یک بلوک قواعد مانند یک بلوک محلی است، اما خودش به تنهایی هیچ کدی تولید نمیکند و هیچ تعریفی را هم به دامنه سراسری اضافه نمیکند -- هدف آن، استفاده مجدد در سایر بلوکها است. این روشی برای به اشتراکگذاری کد است (جزئیات بیشتر در بخش بلوکهای قابل استفاده مجدد). پیش از نسخه 2.2 re2c، بلوکهای قواعد به گزینه -r --reusable نیاز داشتند.
- /*!use:re2c[:<name>] ... */ یا %{use[:<name>] ... %}
- یک بلوک استفاده که به یک بلوک قواعد از پیش تعریفشده ارجاع میدهد. اگر نام مشخص شده باشد، re2c به دنبال بلوک قواعدی با این نام میگردد. در غیر این صورت، جدیدترین بلوک قواعد استفاده میشود (خواه نامگذاریشده باشد یا بدون نام). یک بلوک استفاده میتواند تعاریف، پیکربندیها و قواعد خاص خود را اضافه کند، که به موارد موجود در بلوک قواعد مرجع افزوده میشوند. پیش از نسخه 2.2 re2c، بلوکهای استفاده به گزینه -r --reusable نیاز داشتند.
- /*!max:re2c[:<name1>[:<name2>...]] ... */ یا %{max[:<name1>[:<name2>...]] ... %}
- بلوکی که تعریف YYMAXFILL را تولید میکند. یک فهرست اختیاری از نامهای بلوک مشخص میکند هنگام محاسبه مقدار YYMAXFILL کدام بلوکها باید لحاظ شوند (اگر فهرست خالی باشد، همه بلوکها لحاظ میشوند). به طور پیشفرض، کد تولیدشده یک تعریف ماکرو برای C (#define YYMAXFILL <n>) یا یک متغیر سراسری برای Go (var YYMAXFILL int = <n>) است. این مقدار را میتوان با پیکربندی اختیاری format سفارشی کرد که یک رشته الگو را مشخص میکند که در آن @@{max} (یا به اختصار @@) با مقدار عددی YYMAXFILL جایگزین میشود.
- /*!maxnmatch:re2c[:<name1>[:<name2>...]] ... */ یا %{maxnmatch[:<name1>[:<name2>...]] ... %}
- بلوکی که تعریف YYMAXNMATCH را تولید میکند (به گزینه -P --posix-captures نیاز دارد). یک فهرست اختیاری از نامهای بلوک مشخص میکند هنگام محاسبه مقدار YYMAXNMATCH کدام بلوکها باید لحاظ شوند (اگر فهرست خالی باشد، همه بلوکها لحاظ میشوند). به طور پیشفرض، کد تولیدشده یک تعریف ماکرو برای C (#define YYMAXNMATCH <n>) یا یک متغیر سراسری برای Go (var YYMAXNMATCH int = <n>) است. این مقدار را میتوان با پیکربندی اختیاری format سفارشی کرد که یک رشته الگو را مشخص میکند که در آن @@{max} (یا به اختصار @@) با مقدار عددی YYMAXNMATCH جایگزین میشود.
- /*!stags:re2c[:<name1>[:<name2>...]] ... */, /*!mtags:re2c[:<name1>[:<name2>...]] ... */ یا %{stags[:<name1>[:<name2>...]] ... %}, %{mtags[:<name1>[:<name2>...]] ... %{
- بلوکهایی که قطعهکدی الگویی را مشخص میکنند که به ازای هر متغیر s-tag/m-tag تولیدشده توسط re2c بسط مییابد. یک فهرست اختیاری از نامهای بلوک مشخص میکند هنگام محاسبه مجموعه متغیرهای تگ کدام بلوکها باید لحاظ شوند (اگر فهرست خالی باشد، همه بلوکها لحاظ میشوند). دو پیکربندی اختیاری وجود دارد: format و separator. پیکربندی format یک رشته الگو را تعیین میکند که در آن @@{tag} (یا به اختصار @@) با نام هر متغیر تگ جایگزین میشود. پیکربندی separator قطعهکدی را مشخص میکند که برای پیوند دادن قطعات تولیدشده format برای متغیرهای مختلف تگ استفاده میشود.
- /*!svars:re2c[:<name1>[:<name2>...]] ... */, /*!mvars:re2c[:<name1>[:<name2>...]] ... */ یا %{svars[:<name1>[:<name2>...]] ... %}, %{mvars[:<name1>[:<name2>...]] ... %{
- بلوکهایی که قطعهکدی الگویی را مشخص میکنند که به ازای هر s-tag/m-tag که یا به صراحت در قواعد ذکر شده است (با گزینه --tags) یا به طور ضمنی توسط re2c تولید شده است (با گزینههای --captvars یا --posix-captvars) بسط مییابد. یک فهرست اختیاری از نامهای بلوک مشخص میکند هنگام محاسبه مجموعه تگها کدام بلوکها باید لحاظ شوند (اگر فهرست خالی باشد، همه بلوکها لحاظ میشوند). دو پیکربندی اختیاری وجود دارد: format و separator. پیکربندی format یک رشته الگو را تعیین میکند که در آن @@{tag} (یا به اختصار @@) با نام هر تگ جایگزین میشود. پیکربندی separator قطعهکدی را مشخص میکند که برای پیوند دادن قطعات تولیدشده format برای تگهای مختلف استفاده میشود.
- /*!getstate:re2c[:<name1>[:<name2>...]] ... */ یا %{getstate[:<name1>[:<name2>...]] ... %}
- بلوکی که هدایت شرطی بر اساس وضعیت تحلیلگر لغوی را تولید میکند (به گزینه --storable-state نیاز دارد). یک فهرست اختیاری از نامهای بلوک مشخص میکند کدام بلوکها باید در هدایت وضعیت گنجانده شوند. گذار پیشفرض به برچسب شروع اولین بلوک در فهرست میرود. اگر فهرست خالی باشد، همه بلوکها گنجانده میشوند، و گذار پیشفرض به اولین بلوکی در فایل که برچسب شروع دارد میرود. این نوع بلوک با گزینه --loop-switch ناسازگار است، زیرا به گذارهای بینبلوکی نیاز دارد که بدون goto یا فراخوانیهای تابع پشتیبانی نمیشوند.
- /*!conditions:re2c[:<name1>[:<name2>...]] ... */, /*!types:re2c... */ یا %{conditions[:<name1>[:<name2>...]] ... %}, %{types... %}
- بلوکی که شمارش شرطها را تولید میکند (به گزینه --conditions نیاز دارد). یک فهرست اختیاری از نامهای بلوک مشخص میکند هنگام محاسبه مجموعه شرایط کدام بلوکها باید لحاظ شوند (اگر فهرست خالی باشد، همه بلوکها لحاظ میشوند). به طور پیشفرض کد تولیدشده یک شمارش YYCONDTYPE است. این کد را میتوان با پیکربندیهای اختیاری format و separator سفارشی کرد. پیکربندی format یک رشته الگو را مشخص میکند که در آن @@{cond} (یا به اختصار @@) با نام هر شرط، و @@{num} با اندیس عددی آن شرط جایگزین میشود. پیکربندی separator قطعهکدی را مشخص میکند که برای پیوند دادن قطعات تولیدشده format برای شرایط مختلف استفاده میشود.
- /*!include:re2c <file> */ یا %{include <file> %}
- این بلوک امکان گنجاندن <file> را فراهم میکند، که باید یک مسیر فایل درون گیومه دوتایی باشد. محتویات فایل به معنای واقعی کلمه در جای بلوک جایگزین میشود، دقیقاً به همان روشی که #include در C/C++ کار میکند. این بلوک میتواند همراه با گزینه --depfile برای تولید وابستگیهای سیستم ساخت بر روی فایلهای گنجاندهشده استفاده شود.
- /*!header:re2c:on*/ یا %{header:on %}
- این بلوک آغاز فایل سرآیند را مشخص میکند. تمام موارد پس از آن و تا بلوک header:off بعدی توسط re2c پردازش شده و در فایل سرآیندی که با گزینه -t --type-header مشخص شده نوشته میشود.
- /*!header:re2c:off*/ یا %{header:off %}
- این بلوک پایان فایل سرآیندی را که با بلوک header:on*/ آغاز شده بود مشخص میکند.
- /*!ignore:re2c ... */ یا %{ignore ... %}
- بلوکی که
محتویات آن
نادیده
گرفته شده و
از فایل
خروجی حذف
میشود.
در اینجا فهرست کاملی از پیکربندیهای پشتیبانیشده توسط re2c آورده شده است:
- re2c:api, re2c:input
- مشابه گزینه --api.
- re2c:api:sigil
- علامتگذار ("نشانگر" یا "sigil") را مشخص میکند که برای جانگهدارندههای آرگومان در ساختارهای اولیه API استفاده میشود. مقدار پیشفرض @@ است. یک جانگهدارنده با sigil آغاز شده و در ادامه نام آرگومان در آکولادها میآید. به عنوان مثال، اگر sigil روی $ تنظیم شود، جانگهدارندهها به فرم ${name} خواهند بود. APIهای تکآرگومانی میتوانند از نمادگذاری مختصر بدون نام در آکولاد استفاده کنند. این گزینه را میتوان با گزینههای مربوط به ساختارهای اولیه جداگانه API بازنویسی کرد، مانند re2c:YYFILL@len برای YYFILL.
- re2c:api:style
- سبک API را مشخص میکند. مقادیر ممکن عبارتند از functions (پیشفرض برای C) و free-form (پیشفرض برای Go و Rust). در سبک functions، ساختارهای اولیه API همراه با فهرستی از آرگومانها درون پرانتز که پس از نام ساختار اولیه میآید تولید میشوند. آرگومانها تنها برای پارامترهای خودکار تولیدشده فراهم میشوند (مانند تعداد نویسههای ارسالشده به YYFILL)، نه برای زمینه عمومی تحلیلگر واژگانی؛ بنابراین این ساختارهای اولیه بیشتر رفتاری شبیه به ماکروها در C/C++ یا بستارها (closures) در Go و Rust دارند. در سبک free-form ساختارهای اولیه API فرم ثابتی ندارند: آنها باید به صورت رشتههایی شامل قطعه کدهای آزاد به همراه متغیرهای درونیابیشده با فرمت @@{var} یا @@ تعریف شوند (که با آرگومانها در سبک تابعگونه مطابقت دارند). این پیکربندی ممکن است برای ساختارهای اولیه جداگانه API بازنویسی شود؛ برای نمونه، پیکربندی re2c:YYFILL:naked را برای YYFILL ببینید.
- re2c:bit-vectors, re2c:flags:bit-vectors, re2c:flags:b
- مشابه گزینه --bit-vectors، اما به تفکیک هر بلوک قابل پیکربندی است.
- re2c:captures, re2c:leftmost-captures
- مشابه گزینه --leftmost-captures، اما به تفکیک هر بلوک قابل پیکربندی است.
- re2c:captvars, re2c:leftmost-captvars
- مشابه گزینه --leftmost-captvars، اما به تفکیک هر بلوک قابل پیکربندی است.
- re2c:case-insensitive, re2c:flags:case-insensitive
- مشابه گزینه --case-insensitive، اما به تفکیک هر بلوک قابل پیکربندی است.
- re2c:case-inverted, re2c:flags:case-inverted
- مشابه گزینه --case-inverted، اما به تفکیک هر بلوک قابل پیکربندی است.
- re2c:case-ranges, re2c:flags:case-ranges
- مشابه گزینه --case-ranges، اما به تفکیک هر بلوک قابل پیکربندی است.
- re2c:computed-gotos, re2c:flags:computed-gotos, re2c:flags:g
- مشابه گزینه --computed-gotos، اما به تفکیک هر بلوک قابل پیکربندی است.
- re2c:computed-gotos:relative, re2c:cgoto:relative
- مشابه گزینه --computed-gotos-relative، اما به تفکیک هر بلوک قابل پیکربندی است.
- re2c:computed-gotos:threshold, re2c:cgoto:threshold
- در صورت استفاده از gotoی محاسبهشده، این پیکربندی آستانه پیچیدگیای را مشخص میکند که باعث تولید جدولهای پرش بهجای دستورهای if تو در تو و نقشههای بیتی (bitmaps) میشود. مقدار پیشفرض 9 است.
- re2c:cond:abort
- اگر روی یک مقدار عددی صحیح مثبت تنظیم شود، حالت پیشفرض (default case) در توزیع شرطیِ تولیدشده، اجرای برنامه را متوقف میکند.
- re2c:cond:goto
- قطعه کدی را مشخص میکند که برای قواعد میانبر خودکار تولیدشده :=> در شرایط استفاده میشود. مقدار پیشفرض goto @@; است. جانگهدارنده @@ با نام شرط جایگزین میشود (پیکربندیهای re2c:api:sigil و re2c:cond:goto@cond را ببینید).
- re2c:cond:goto@cond
- نشانگر (sigil) مورد استفاده برای جایگزینی آرگومان در تعریف re2c:cond:goto را مشخص میکند. مقدار پیشفرض @@ است. پیکربندی عمومیتر re2c:api:sigil را بازنویسی میکند.
- re2c:cond:divider
- جداکننده بلوکهای شرط را تعریف میکند. مقدار پیشفرض /* *********************************** */ است. جانگهدارندهها با نام شرط جایگزین میشوند (نگاه کنید به re2c:api;sigil و re2c:cond:divider@cond).
- re2c:cond:divider@cond
- نشانگر (sigil) مورد استفاده برای جایگزینی آرگومان در تعریف re2c:cond:divider را مشخص میکند. مقدار پیشفرض @@ است. پیکربندی عمومیتر re2c:api:sigil را بازنویسی میکند.
- re2c:cond:prefix, re2c:condprefix
- پیشوند مورد استفاده برای برچسبهای شرط را مشخص میکند. مقدار پیشفرض yyc_ است.
- re2c:cond:enumprefix, re2c:condenumprefix
- پیشوند مورد استفاده برای شناسههای شرط را مشخص میکند. مقدار پیشفرض yyc است.
- re2c:debug-output, re2c:flags:debug-output, re2c:flags:d
- مشابه گزینه --debug-output، اما به تفکیک هر بلوک قابل پیکربندی است.
- re2c:empty-class, re2c:flags:empty-class
- مشابه گزینه --empty-class، اما به تفکیک هر بلوک قابل پیکربندی است.
- re2c:encoding:ebcdic, re2c:flags:ecb, re2c:flags:e
- مشابه گزینه --ebcdic، اما به تفکیک هر بلوک قابل پیکربندی است.
- re2c:encoding:ucs2, re2c:flags:wide-chars, re2c:flags:w
- مشابه گزینه --ucs2، اما به تفکیک هر بلوک قابل پیکربندی است.
- re2c:encoding:utf8, re2c:flags:utf-8, re2c:flags:8
- مشابه گزینه --utf8، اما به تفکیک هر بلوک قابل پیکربندی است.
- re2c:encoding:utf16, re2c:flags:utf-16, re2c:flags:x
- مشابه گزینه --utf16، اما به تفکیک هر بلوک قابل پیکربندی است.
- re2c:encoding:utf32, re2c:flags:unicode, re2c:flags:u
- مشابه گزینه --utf32، اما به تفکیک هر بلوک قابل پیکربندی است.
- re2c:encoding-policy, re2c:flags:encoding-policy
- مشابه گزینه --encoding-policy، اما به تفکیک هر بلوک قابل پیکربندی است.
- re2c:eof
- نماد نشانگر انتها (sentinel symbol) مورد استفاده با قاعده پایان ورودی $ را مشخص میکند. مقدار پیشفرض -1 است (قاعده $ استفاده نمیشود). سایر مقادیر ممکن شامل تمام واحدهای کد (code units) معتبر است. تنها اعداد دهدهی تشخیص داده میشوند.
- re2c:header, re2c:flags:type-header, re2c:flags:t
- نام فایل سرآیند تولیدشده را به صورت نسبی نسبت به دایرکتوری فایل خروجی مشخص میکند. مشابه گزینه --header، با این تفاوت که مسیر فایل نسبی است.
- re2c:indent:string
- رشته مورد استفاده برای تورفتگی را مشخص میکند. مقدار پیشفرض یک نویسه تب تکی "\t" است. رشته تورفتگی فقط باید شامل نویسههای فاصله خالی (whitespace) باشد. برای غیرفعال کردن کامل تورفتگی، این پیکربندی را روی یک رشته خالی تنظیم کنید.
- re2c:indent:top
- حداقل میزان تورفتگی مورد استفاده را مشخص میکند. مقدار پیشفرض صفر است. این مقدار باید یک عدد صحیح غیرمنفی باشد.
- re2c:invert-captures
- مشابه گزینه --invert-captures، اما به تفکیک هر بلوک قابل پیکربندی است.
- re2c:label:prefix, re2c:labelprefix
- پیشوند مورد استفاده برای برچسبهای وضعیت DFA را مشخص میکند. مقدار پیشفرض yy است.
- re2c:label:start, re2c:startlabel
- تولید برچسب آغاز بلوک را کنترل میکند. مقدار پیشفرض صفر است، به این معنی که برچسب آغاز تنها در صورتی تولید میشود که استفاده شده باشد. یک مقدار عددی صحیح بزرگتر از صفر، تولید برچسب آغاز را حتی اگر توسط تحلیلگر واژگانی استفاده نشده باشد اجباری میکند. یک مقدار رشتهای نیز تولید برچسب آغاز را اجباری کرده و نام برچسب را روی رشته مشخصشده تنظیم میکند. این پیکربندی تنها برای بلوک فعلی اعمال میشود (برای بلوک بعدی به مقدار پیشفرض بازنشانی میشود).
- re2c:label:yyFillLabel
- پیشوند برچسبهای YYFILL مورد استفاده با re2c:eof و در حالت وضعیت قابل ذخیرهسازی (storable state mode) را مشخص میکند.
- re2c:label:yyloop
- نام برچسب نشاندهنده شروع حلقه تحلیلگر واژگانی همراه با گزینه --loop-switch را مشخص میکند. مقدار پیشفرض yyloop است.
- re2c:label:yyNext
- نام برچسب اختیاریای را مشخص میکند که پس از سوییچ YYGETSTATE در حالت وضعیت قابل ذخیرهسازی (که با re2c:state:nextlabel فعال میشود) میآید. مقدار پیشفرض yyNext است.
- re2c:lookahead, re2c:flags:lookahead
- منسوخشده (گزینه منسوخشده --no-lookahead را ببینید).
- re2c:monadic
- اگر روی مقداری غیر از صفر تنظیم شود، تحلیلگر واژگانی تولیدشده از نمادگذاری مونادیک (monadic notation) استفاده خواهد کرد (این پیکربندی مخصوص Haskell است)..TP re2c:nested-ifs, re2c:flags:nested-ifs, re2c:flags:s مشابه گزینهٔ --nested-ifs است، اما میتواند بهازای هر بلوک پیکربندی شود.
- re2c:posix-captures, re2c:flags:posix-captures, re2c:flags:P
- مشابه گزینهٔ --posix-captures است، اما میتواند بهازای هر بلوک پیکربندی شود.
- re2c:posix-captvars
- مشابه گزینهٔ --posix-captvars است، اما میتواند بهازای هر بلوک پیکربندی شود.
- مشابه گزینهٔ --tags است، اما میتواند بهازای هر بلوک پیکربندی شود.
- عبارت مورد استفاده برای متغیرهای برچسب را مشخص میکند. بهطور پیشفرض re2c عبارتهایی به فرم yyt<N> تولید میکند. این ممکن است ناخوشایند باشد، برای مثال اگر متغیرهای برچسب بهصورت فیلدهایی در یک struct تعریف شده باشند. تمامی رخدادهای @@{tag} یا @@ با نام واقعی برچسب جایگزین میشوند. برای مثال، re2c:tags:expression = "s.@@"; منجر به عبارتهایی به فرم s.yyt<N> در کد تولیدشده خواهد شد. همچنین پیکربندی re2c:api:sigil را ببینید.
- عبارت ثابتی را مشخص میکند که برای مقدار منفی برچسب استفاده میشود (بهطور معمول اگر برچسبها آفستهای صحیح در رشتهٔ ورودی باشند این مقدار -1 است، یا اگر اشارهگر باشند اشارهگر تهی خواهد بود).
- پیشوند نام متغیرهای برچسب را مشخص میکند. مقدار پیشفرض yyt است.
- re2c:sentinel
- نماد دیدهبان (sentinel) مورد استفاده برای بررسیهای پایان ورودی را مشخص میکند (زمانی که بررسیهای محدوده با re2c:yyfill:enable = 0; غیرفعال شده و re2c:eof تنظیم نشده باشد). این پیکربندی تأثیری بر تولید کد نمیگذارد: هدف آن راستیآزمایی این نکته است که نماد دیدهبان در میانهٔ یک قاعده مجاز نباشد، و تضمین کند که تحلیلگر لغوی فراتر از انتهای بافر را نخواند. مقدار پیشفرض -1` است (در این حالت re2c فرض میکند که نماد دیدهبان صفر است، که رایجترین حالت میباشد). فقط اعداد دهدهی شناسایی میشوند.
- re2c:state:abort
- اگر روی یک مقدار صحیح مثبت تنظیم شود، حالت پیشفرض (default case) در توزیع وضعیت تولیدشده، اجرای برنامه را متوقف میکند، و یک حالت صریح -1 شامل انتقال به ابتدای بلوک خواهد بود.
- re2c:state:nextlabel
- کنترل میکند که آیا بعد از سوییچ YYGETSTATE یک برچسب yyNext بیاید یا خیر (مقدار پیشفرض صفر است، که متناظر با نبود برچسب میباشد). بهعنوان جایگزین میتوان از re2c:label:start برای تولید یک برچسب شروع مشخص استفاده کرد، یا از یک بلوک صریح getstate برای تولید سوییچ YYGETSTATE بهطور جداگانه از بلوک تحلیلگر لغوی بهره برد.
- re2c:unsafe, re2c:flags:unsafe
- مشابه گزینهٔ --no-unsafe است، اما میتواند بهازای هر بلوک پیکربندی شود. اگر روی صفر تنظیم شود، از تولید پوششهای unsafe در اطراف YYPEEK جلوگیری میکند. مقدار پیشفرض غیرصفر است (پوششها تولید میشوند). این پیکربندی مختص Rust است.
- re2c:YYBACKUP, re2c:define:YYBACKUP
- عملگر اولیهٔ عمومی API به نام YYBACKUP را تعریف میکند.
- re2c:YYBACKUPCTX, re2c:define:YYBACKUPCTX
- عملگر اولیهٔ عمومی API به نام YYBACKUPCTX را تعریف میکند.
- re2c:YYCONDTYPE, re2c:define:YYCONDTYPE
- عملگر اولیهٔ API به نام YYCONDTYPE را تعریف میکند.
- re2c:YYCTYPE, re2c:define:YYCTYPE
- عملگر اولیهٔ API به نام YYCTYPE را تعریف میکند.
- re2c:YYCTXMARKER, re2c:define:YYCTXMARKER
- عملگر اولیهٔ API به نام YYCTXMARKER را تعریف میکند.
- re2c:YYCURSOR, re2c:define:YYCURSOR
- عملگر اولیهٔ API به نام YYCURSOR را تعریف میکند.
- re2c:YYDEBUG, re2c:define:YYDEBUG
- عملگر اولیهٔ API به نام YYDEBUG را تعریف میکند.
- re2c:YYFILL, re2c:define:YYFILL
- عملگر اولیهٔ API به نام YYFILL را تعریف میکند.
- re2c:YYFILL@len, re2c:define:YYFILL@len
- نماد (sigil) مورد استفاده برای جایگزینی آرگومان در تعریف YYFILL را مشخص میکند. مقدار پیشفرض @@ است. پیکربندی عمومیترِ re2c:api:sigil را بازنویسی میکند.
- re2c:YYFILL:naked, re2c:define:YYFILL:naked
- پیکربندی عمومیترِ re2c:api:style را برای YYFILL بازنویسی میکند. مقدار صفر متناظر با سبک API از نوع free-form است.
- re2c:YYFN
- عملگر اولیهٔ API به نام YYFN را تعریف میکند.
- re2c:YYINPUT
- عملگر اولیهٔ API به نام YYINPUT را تعریف میکند.
- re2c:YYGETCOND, re2c:define:YYGETCONDITION
- عملگر اولیهٔ API به نام YYGETCOND را تعریف میکند.
- re2c:YYGETCOND:naked, re2c:define:YYGETCONDITION:naked
- پیکربندی عمومیترِ re2c:api:style را برای YYGETCOND بازنویسی میکند. مقدار صفر متناظر با سبک API از نوع free-form است.
- re2c:YYGETSTATE, re2c:define:YYGETSTATE
- عملگر اولیهٔ API به نام YYGETSTATE را تعریف میکند.
- re2c:YYGETSTATE:naked, re2c:define:YYGETSTATE:naked
- پیکربندی عمومیترِ re2c:api:style را برای YYGETSTATE بازنویسی میکند. مقدار صفر متناظر با سبک API از نوع free-form است.
- re2c:YYGETACCEPT, re2c:define:YYGETACCEPT
- عملگر اولیهٔ API به نام YYGETACCEPT را تعریف میکند.
- re2c:YYLESSTHAN, re2c:define:YYLESSTHAN
- عملگر اولیهٔ عمومی API به نام YYLESSTHAN را تعریف میکند.
- re2c:YYLIMIT, re2c:define:YYLIMIT
- عملگر اولیهٔ API به نام YYLIMIT را تعریف میکند.
- re2c:YYMARKER, re2c:define:YYMARKER
- عملگر اولیهٔ API به نام YYMARKER را تعریف میکند.
- re2c:YYMTAGN, re2c:define:YYMTAGN
- عملگر اولیهٔ عمومی API به نام YYMTAGN را تعریف میکند.
- re2c:YYMTAGP, re2c:define:YYMTAGP
- عملگر اولیهٔ عمومی API به نام YYMTAGP را تعریف میکند.
- re2c:YYPEEK, re2c:define:YYPEEK
- عملگر اولیهٔ عمومی API به نام YYPEEK را تعریف میکند.
- re2c:YYRESTORE, re2c:define:YYRESTORE
- عملگر اولیهٔ عمومی API به نام YYRESTORE را تعریف میکند.
- re2c:YYRESTORECTX, re2c:define:YYRESTORECTX
- عملگر اولیهٔ عمومی API به نام YYRESTORECTX را تعریف میکند.
- re2c:YYRESTORETAG, re2c:define:YYRESTORETAG
- عملگر اولیهٔ عمومی API به نام YYRESTORETAG را تعریف میکند.
- re2c:YYSETCOND, re2c:define:YYSETCONDITION
- عملگر اولیهٔ API به نام YYSETCOND را تعریف میکند.
- re2c:YYSETCOND@cond, re2c:define:YYSETCONDITION@cond
- نماد (sigil) مورد استفاده برای جایگزینی آرگومان در تعریف YYSETCOND را مشخص میکند. مقدار پیشفرض @@ است. پیکربندی عمومیترِ re2c:api:sigil را بازنویسی میکند.
- re2c:YYSETCOND:naked, re2c:define:YYSETCONDITION:naked
- پیکربندی عمومیترِ re2c:api:style را برای YYSETCOND بازنویسی میکند. مقدار صفر متناظر با سبک API از نوع free-form است.
- re2c:YYSETSTATE, re2c:define:YYSETSTATE
- عملگر اولیهٔ API به نام YYSETSTATE را تعریف میکند.
- re2c:YYSETSTATE@state, re2c:define:YYSETSTATE@state
- نماد (sigil) مورد استفاده برای جایگزینی آرگومان در تعریف YYSETSTATE را مشخص میکند. مقدار پیشفرض @@ است. پیکربندی عمومیترِ re2c:api:sigil را بازنویسی میکند.
- re2c:YYSETSTATE:naked, re2c:define:YYSETSTATE:naked
- پیکربندی عمومیترِ re2c:api:style را برای YYSETSTATE بازنویسی میکند. مقدار صفر متناظر با سبک API از نوع free-form است.
- re2c:YYSETACCEPT, re2c:define:YYSETACCEPT
- عملگر اولیهٔ API به نام YYSETACCEPT را تعریف میکند.
- re2c:YYSKIP, re2c:define:YYSKIP
- عملگر اولیهٔ عمومی API به نام YYSKIP را تعریف میکند.
- re2c:YYSHIFT, re2c:define:YYSHIFT
- عملگر اولیهٔ عمومی API به نام YYSHIFT را تعریف میکند.
- re2c:YYCOPYMTAG, re2c:define:YYCOPYMTAG
- عملگر اولیهٔ عمومی API به نام YYCOPYMTAG را تعریف میکند.
- re2c:YYCOPYSTAG, re2c:define:YYCOPYSTAG
- عملگر اولیهٔ عمومی API به نام YYCOPYSTAG را تعریف میکند..TP re2c:YYSHIFTMTAG, re2c:define:YYSHIFTMTAG عنصر پایه generic API به نام YYSHIFTMTAG را تعریف میکند.
- re2c:YYSHIFTSTAG, re2c:define:YYSHIFTSTAG
- عنصر پایه generic API به نام YYSHIFTSTAG را تعریف میکند.
- re2c:YYSTAGN, re2c:define:YYSTAGN
- عنصر پایه generic API به نام YYSTAGN را تعریف میکند.
- re2c:YYSTAGP, re2c:define:YYSTAGP
- عنصر پایه generic API به نام YYSTAGP را تعریف میکند.
- re2c:yyaccept, re2c:variable:yyaccept
- عنصر پایه API به نام yyaccept را تعریف میکند.
- re2c:yybm, re2c:variable:yybm
- عنصر پایه API به نام yybm را تعریف میکند.
- re2c:yybm:hex, re2c:variable:yybm:hex
- اگر روی غیرصفر تنظیم شود، نگاشتهای بیتی (bitmaps) برای گزینهٔ --bit-vectors در قالب شانزدهشانزدهی (hexadecimal) تولید میشوند. پیشفرض صفر است (نگاشتهای بیتی در قالب دهدهی هستند).
- re2c:yych, re2c:variable:yych
- عنصر پایه API به نام yych را تعریف میکند.
- re2c:yych:emit, re2c:variable:yych:emit
- اگر روی صفر تنظیم شود، تعریف yych تولید نمیشود. پیشفرض غیرصفر است.
- re2c:yych:conversion, re2c:variable:yych:conversion
- اگر روی غیرصفر تنظیم شود، re2c هر بار که yych خوانده میشود، بهطور خودکار یک تبدیل به YYCTYPE تولید میکند. پیشفرض صفر است (بدون تبدیل).
- re2c:yych:literals, re2c:variable:yych:literals
- قالب لیترالهایی که yych با آنها مطابقت داده میشود را مشخص میکند. مقادیر ممکن عبارتند از: char (لیترالهای نویسهای در نقلقول تکی، که نویسههای غیرقابلچاپ در آن از توالیهای گریزِ شروعشونده با بکاسلش استفاده میکنند)، hex (اعداد صحیح شانزدهشانزدهی) و char_or_hex (ترکیبی از هر دو؛ لیترالهای نویسهای برای نویسههای قابلچاپ و اعداد صحیح شانزدهشانزدهی برای سایر موارد).
- re2c:yyctable, re2c:variable:yyctable
- عنصر پایه API به نام yyctable را تعریف میکند.
- re2c:yynmatch, re2c:variable:yynmatch
- عنصر پایه API به نام yynmatch را تعریف میکند.
- re2c:yypmatch, re2c:variable:yypmatch
- عنصر پایه API به نام yypmatch را تعریف میکند.
- re2c:yytarget, re2c:variable:yytarget
- عنصر پایه API به نام yytarget را تعریف میکند.
- re2c:yystable, re2c:variable:yystable
- منسوخ شده است.
- re2c:yystate, re2c:variable:yystate
- عنصر پایه API به نام yystate را تعریف میکند.
- re2c:yyfill, re2c:variable:yyfill
- عنصر پایه API به نام yyfill را تعریف میکند.
- re2c:yyfill:check
- اگر روی صفر تنظیم شود، از تولید بررسی پیش از YYFILL برای تعداد نویسههای ورودی (تعریف YYLESSTHAN در generic API و مقایسه مبتنی بر YYLIMIT در C pointer API) جلوگیری میکند. پیشفرض غیرصفر است (بررسی تولید میشود).
- re2c:yyfill:enable
- اگر روی صفر تنظیم شود، از تولید YYFILL (همراه با بررسی آن) جلوگیری میکند. این تنظیم زمانی باید استفاده شود که کل ورودی در یک قطعه از حافظه جا شود (نیازی به بافرسازی نیست) و بررسیهای پایان ورودی به بررسیهای YYFILL وابسته نباشند (مثلاً زمانی که از یک نویسهٔ نشانگر پایان/sentinel استفاده میشود). از هشدارها (گزینهٔ -W) و پیکربندی re2c:sentinel استفاده کنید تا مطمئن شوید تحلیلگر لغوی تولیدشده نمیتواند فراتر از انتهای ورودی را بخواند. پیشفرض غیرصفر است (YYFILL فعال است).
- re2c:yyfill:parameter
- اگر روی صفر تنظیم شود، از تولید پارامتر ارسالی به YYFILL جلوگیری میکند. این پارامتر نشاندهندهٔ حداقل تعداد نویسههایی است که باید تأمین شوند. پیشفرض غیرصفر است (پارامتر تولید میشود). این پیکربندی میتواند توسط re2c:YYFILL:naked یا re2c:api:style بازنویسی شود.
- re2c:yyfn:sep
- جداکنندهٔ مورد استفاده در عناصر YYFN را مشخص میکند (پیشفرض نقطهویرگول است).
- re2c:yyfn:throw
- استثناهای
پرتابشده
توسط تابع
YYFN را مشخص
میکند
(پیشفرض
خالی است،
که به معنای
عدم وجود
هیچ
استثنایی
است).
ابزار re2c از ساختار دستوری زیر برای عبارتهای منظم استفاده میکند:
- "foo"
- رشته لیترال حساس به بزرگ و کوچکی حروف.
- 'foo'
- رشته لیترال غیرحساس به بزرگ و کوچکی حروف.
- [a-xyz], [^a-xyz]
- کلاس نویسه (احتمالاً نفیشده).
- .
- هر نویسهای به جز خط جدید.
- R \ S
- تفاضل کلاسهای نویسهٔ R و S.
- R*
- صفر یا چند بار تکرار R.
- R+
- یک یا چند بار تکرار R.
- R?
- مورد اختیاری R.
- R{n}
- تکرار R دقیقاً به تعداد n بار.
- R{n,}
- تکرار R دستکم به تعداد n بار.
- R{n,m}
- تکرار R از n تا m بار.
- (R)
- صرفاً R؛ پرانتزها برای تغییر اولویت استفاده میشوند. اگر استخراج زیرتطابق فعال باشد، (R) بسته به گزینهٔ --invert-captures یک گروه ضبطکننده یا غیرضبطکننده است.
- (!R)
- اگر استخراج زیرتطابق فعال باشد، (!R) بسته به گزینهٔ --invert-captures یک گروه غیرضبطکننده یا ضبطکننده است.
- R S
- الحاق: R و به دنبال آن S.
- R | S
- جایگزین: R یا S.
- R / S
- پیشنگری (Lookahead): R و به دنبال آن S، اما S مصرف نمیشود.
- name
- عبارت منظم تعریفشده با نام name (یا رشته لیترال "name" در حالت سازگاری با Flex).
- {name}
- عبارت منظم تعریفشده با نام name در حالت سازگاری با Flex.
- @stag
- یک s-tag: آخرین موقعیت ورودی را که در آن @stag مطابقت دارد در یک متغیر به نام stag ذخیره میکند.
- #mtag
- یک m-tag: تمام موقعیتهای ورودی را که در آنها #mtag مطابقت دارد در یک متغیر به نام mtag ذخیره میکند.
- $
- پایان ورودی.
کلاسهای نویسه و رشتههای لیترال ممکن است شامل دنبالههای گریز زیر باشند: \a، \b، \f، \n، \r، \t، \v، \\، گریزهای هشتهشتی \ooo و گریزهای شانزدهشانزدهی \xhh، \uhhhh و \Uhhhhhhhh.
کنشها (Actions)
در اینجا فهرستی از کنشهای از پیش تعریفشده که توسط re2c پشتیبانی میشوند آورده شده است:
- !entry code
- کنش ورودی (Entry action) یک بلوک code تعریفشده توسط کاربر را به وضعیت آغازین ماشین حالت متناهی فعلی پیوند میدهد. اگر از start conditions (شرایط شروع) استفاده شود، کنش ورودی را میتوان به طور جداگانه برای هر شرط تنظیم کرد. این کنش ممکن است برای انجام مقداردهی اولیه، مثلاً برای ذخیره موقعیت شروع یک نشانه (lexeme)، استفاده شود.
- !pre_rule code
- کنش پیشقاعده (Pre-rule action) یک بلوک code تعریفشده توسط کاربر را به ابتدای کنشهای معنایی تمام قواعد در بلوک فعلی (یا شرط، در صورت استفاده از start conditions) اضافه میکند. این کنش میتواند برای فاکتور گرفتن بخش مشترک تمام کنشهای معنایی (مانند ذخیره موقعیت پایانی یک نشانه) به کار رود.
- !post_rule code
- کنش
پسقاعده
(Post-rule action) یک بلوک
code
تعریفشده
توسط کاربر
را به
انتهای
کنشهای
معنایی
تمام قواعد
در بلوک
فعلی (یا
شرط، در
صورت
استفاده از
start conditions) ضمیمه
میکند. این
کنش
میتواند
برای درج
عبارتهای
تله (trap statements) جهت
جلوگیری از
جریان
کنترل
ناخواسته
استفاده
شود.
در اینجا فهرست کاملی از دستورالعملهای پشتیبانیشده توسط re2c آورده شده است:
- !use:name ;
- یک دستورالعمل use درونبلوکی که یک بلوک قواعد از پیش تعریفشده با name مشخص را در بلوک فعلی ادغام میکند. تعاریف نامگذاریشده، پیکربندیها و قواعد بلوک ارجاعدادهشده به موارد فعلی افزوده میشوند. تداخلهای میان قواعد و پیکربندیهای همپوشان به روش معمول حلوفصل میشوند: نخستین قاعده اولویت دارد، و آخرین پیکربندی بر موارد پیشین چیره میشود. یک استثنا، قواعد ویژهٔ *، $ و <!> هستند که برای آنها تعریف محلی درون بلوک همیشه اولویت دارد. دستورالعمل use میتواند در هر جایی درون یک بلوک قرار گیرد، و استفاده از چندین دستورالعمل use مجاز است.
- !include file ;
- این دستورالعمل همانند بلوک include عمل میکند: محتویات file را عیناً در محل دستورالعمل درج میکند.
رابط برنامه (Program interface)
کد تولیدشده به کمک بنیادینهها (primitives)، که در مجموع به عنوان API نامیده میشوند، با برنامهٔ بیرونی ارتباط برقرار میکند. اینکه کدام بنیادینهها باید برای یک برنامهٔ خاص تعریف شوند به عوامل متعددی بستگی دارد، از جمله پیچیدگی عبارات باقاعده، نحوهٔ نمایش ورودی، بافرینگ و استفاده از ویژگیهای گوناگون. تمام بنیادینههای لازم باید توسط کاربر در قالب ماکروها، توابع، متغیرها یا هر شکل مناسب دیگری که کد تولیدشده را از نظر نحوی و معنایی درست کند، تعریف شوند. re2c تعاریف را بررسی نمیکند (و نمیتواند بررسی کند)، بنابراین اگر چیزی از قلم افتاده باشد یا به نادرستی تعریف شده باشد، ممکن است برنامهٔ تولیدشده با خطاهای زمان کامپایل یا زمان اجرا مواجه شود. این راهنما مثالهایی از تعاریف API را در متداولترین حالتها ارائه میدهد.
ابزار re2c دارای سه شیوهٔ مختلف API است که مجموعهٔ هستهٔ بنیادینههای مورداستفاده در یک برنامه را تعیین میکنند:
- Simple API
- این شیوه، API پیشفرض برای بکاند C/C++ است. این رابط شامل بنیادینههای YYCURSOR، YYMARKER، YYCTXMARKER و YYLIMIT است که باید به عنوان اشارهگرهایی از نوع YYCTYPE* تعریف شوند.
- Record API
- (در نسخهٔ 4.0 اضافه شد) رابط Record API در مواردی مفید است که وضعیت تحلیلگر لغوی (lexer) باید در یک struct ذخیره شود. این رابط با گزینهٔ --api record یا پیکربندی re2c:api = record فعال میشود. این API شامل یک متغیر yyrecord است (این نام را میتوان با re2c:yyrecord بازنویسی و جایگزین کرد) که باید به عنوان یک struct با فیلدهای yycursor، yymarker، yyctxmarker و yylimit تعریف شود (تنها فیلدهایی که توسط کد تولیدشده استفاده میشوند باید تعریف شوند، و نام آنها نیز قابل پیکربندی است).
- Generic API
- (در نسخهٔ 0.14 اضافه شد) این شیوه منعطفترین API است. این رابط با گزینهٔ --api generic یا پیکربندی re2c:api = generic فعال میشود. این API شامل بنیادینههایی برای عملیاتهای عمومی است: YYPEEK، YYSKIP، YYBACKUP، YYBACKUPCTX، YYSTAGP، YYSTAGN، YYMTAGP، YYMTAGN، YYRESTORE، YYRESTORECTX، YYRESTORETAG، YYSHIFT، YYSHIFTSTAG، YYSHIFTMTAG، YYLESSTHAN. YYEND. رابط Generic API از دو سبک پشتیبانی میکند که قالبی را که در آن بنیادینهها باید تعریف شوند مشخص میسازند:
- Free-form
- سبک آزاد (Free-form) با پیکربندی re2c:api:style = free-form فعال میشود. در این سبک، بنیادینههای رابط باید به صورت قطعهکدهای آزاد با متغیرهای درونیابیشده به شکل @@{var}، یا در صورت وجود تنها یک متغیر، به صورت اختیاری فقط به صورت @@ تعریف شوند. مجموعهٔ متغیرها مختص هر بنیادینه است. در اینجا نحوهٔ تعریف Generic API به سبک آزاد بر حسب اشارهگرهای cursor، limit، marker و ctxmarker آورده شده است:
/*!re2c
re2c:YYPEEK = "*cursor";
re2c:YYSKIP = "++cursor;";
re2c:YYBACKUP = "marker = cursor;";
re2c:YYRESTORE = "cursor = marker;";
re2c:YYBACKUPCTX = "ctxmarker = cursor;";
re2c:YYRESTORECTX = "cursor = ctxmarker;";
re2c:YYRESTORETAG = "cursor = ${tag};";
re2c:YYLESSTHAN = "limit - cursor < @@{len}";
re2c:YYEND = "limit == cursor";
re2c:YYSTAGP = "@@{tag} = cursor;";
re2c:YYSTAGN = "@@{tag} = NULL;";
re2c:YYSHIFT = "cursor += @@{shift};";
re2c:YYSHIFTSTAG = "@@{tag} += @@{shift};";
*/
- Function-like
- از نظر تاریخی، سبک تابعگونه (function-like) سبک پیشفرض است. این سبک همچنین میتواند با پیکربندی re2c:api:style = functions فعال شود. در این سبک، بنیادینهها باید به عنوان توابع یا ماکروهای دارای پرانتز که آرگومانهای لازم را دریافت میکنند تعریف شوند. در اینجا نحوهٔ تعریف Generic API به سبک تابعگونه بر حسب اشارهگرهای cursor، limit، marker و ctxmarker با استفاده از ماکروهای پیشپردازنده آورده شده است:
#define YYPEEK() *cursor #define YYSKIP() ++cursor #define YYBACKUP() marker = cursor #define YYRESTORE() cursor = marker #define YYBACKUPCTX() ctxmarker = cursor #define YYRESTORECTX() cursor = ctxmarker #define YYRESTORETAG(tag) cursor = tag #define YYLESSTHAN(len) limit - cursor < len #define YYEND() limit == cursor #define YYSTAGP(tag) tag = cursor #define YYSTAGN(tag) tag = NULL #define YYSHIFT(shift) cursor += shift #define YYSHIFTSTAG(tag, shift) tag += shift
در اینجا فهرست کاملی از بنیادینههای API که ممکن است توسط کد تولیدشده جهت ارتباط با برنامهٔ بیرونی استفاده شوند آورده شده است.
- YYCTYPE
- نوع نویسههای ورودی (واحدهای کد). برای رمزگذاریهای ASCII، EBCDIC و UTF-8 باید یک عدد صحیح بدون علامت ۱ بایتی باشد. برای UTF-16 یا UCS-2 باید یک عدد صحیح بدون علامت ۲ بایتی باشد. برای UTF-32 باید یک عدد صحیح بدون علامت ۴ بایتی باشد.
- YYCURSOR
- یک l-value که موقعیت فعلی ورودی را ذخیره میکند (یک اشارهگر یا یک آفست عدد صحیح در YYINPUT). در ابتدا YYCURSOR باید به نخستین نویسهٔ ورودی اشاره کند، و سپس توسط کد تولیدشده به جلو برده میشود. هنگامی که یک قاعده تطبیق مییابد، موقعیت YYCURSOR برابر با موقعیت پس از آخرین نویسهٔ تطبیقیافته است.
- YYLIMIT
- یک r-value که موقعیت انتهای ورودی را ذخیره میکند (یک اشارهگر یا یک آفست عدد صحیح در YYINPUT). در ابتدا YYLIMIT باید به موقعیتِ پس از آخرین نویسهٔ موجود در ورودی اشاره کند. این مقدار توسط کد تولیدشده تغییر نمیکند. تحلیلگر لغوی YYCURSOR را با YYLIMIT مقایسه میکند تا تشخیص دهد آیا نویسههای ورودیِ کافی باقی مانده است یا خیر..TP YYMARKER یک l-value که موقعیت آخرین قاعدهٔ تطبیقیافته را ذخیره میکند (یک اشارهگر یا یک آفست عدد صحیح در YYINPUT). برای بازیابی موقعیت YYCURSOR در صورتی که تطبیق طولانیتر ناموفق باشد و تحلیلگر لغوی نیاز به بازگشت به عقب (rollback) داشته باشد، استفاده میشود. مقداردهی اولیه لازم نیست.
- YYCTXMARKER
- یک l-value که موقعیت بافت پسرو (trailing context) را ذخیره میکند (یک اشارهگر یا یک آفست عدد صحیح در YYINPUT). هیچ مقداردهی اولیهای لازم نیست. YYCTXMARKER تنها در صورتی نیاز است که عملگر پیشنگری / استفاده شده باشد.
- YYFILL
- یک پایهٔ عمومی API با یک متغیر len. YYFILL باید حداقل len نویسهٔ ورودی بیشتر فراهم کند یا با شکست مواجه شود. اگر re2c:eof استفاده شود، len همیشه 1 است و YYFILL باید همیشه به تابع فراخوان بازگردد؛ مقدار بازگشتی صفر نشاندهندهٔ موفقیت است. اگر re2c:eof استفاده نشود، مقدار بازگشتی YYFILL نادیده گرفته میشود و در صورت شکست نباید بازگردد. حداکثر مقدار len برابر YYMAXFILL است.
- YYFN
- یک پایه که پیشنمونهٔ تابع را در مدل کد --recursive-functions تعریف میکند. مقدار آن باید آرایهای از یک یا چند رشته باشد، که در آن هر رشته شامل دو یا سه مؤلفه است که توسط رشتهٔ مشخصشده در پیکربندی re2c:fn:sep (معمولاً یک نقطهویرگول) از هم جدا شدهاند. اولین عنصر آرایه نام تابع و نوع بازگشتی را مشخص میکند (برای یک تابع void خالی است). عناصر بعدی آرگومانهای تابع را تعریف میکنند: اول، عبارت برای آرگومان مورداستفاده در بدنهٔ تابع (معمولاً فقط یک نام)؛ دوم، نوع آرگومان؛ سوم، یک پارامتر رسمی اختیاری (پیشفرض آن مؤلفهٔ اول است - معمولاً هر دو آرگومان و پارامتر شناسهٔ یکسانی دارند).
- YYINPUT
- یک r-value که دنبالهٔ نویسههای ورودی فعلی (رشته، بافر، و غیره) را ذخیره میکند.
- YYMAXFILL
- یک ثابت عدد صحیح برابر با حداکثر مقدار آرگومانِ YYFILL. این مقدار میتواند با یک بلوک max تولید شود.
- YYLESSTHAN
- یک پایهٔ عمومی API با یک متغیر len. باید به عنوان یک r-value از نوع بولی تعریف شود که اگر و تنها اگر کمتر از len نویسهٔ ورودی باقی مانده باشد، برابر با true است.
- YYEND
- یک پایهٔ عمومی API بدون هیچ متغیری. باید به عنوان یک r-value از نوع بولی تعریف شود که اگر و تنها اگر به انتهای منطقی ورودی رسیده باشد (به استثنای هرگونه نماد فاصلهگذاری یا نگهبان)، برابر با true است. YYEND برای پیادهسازی نماد $ در عبارات باقاعده استفاده میشود. این با YYLESSTHAN تفاوت دارد که برای اطمینان از اینکه تحلیلگر لغوی فراتر از انتهای بافر نخواند، به کار میرود.
- YYPEEK
- یک پایهٔ عمومی API بدون متغیر. باید به عنوان یک r-value از نوع YYCTYPE تعریف شود که برابر با نویسه در موقعیت ورودی فعلی است.
- YYSKIP
- یک پایهٔ عمومی API که باید موقعیت ورودی فعلی را به اندازهٔ یک واحد کد به جلو ببرد.
- YYBACKUP
- یک پایهٔ عمومی API که باید موقعیت ورودی فعلی را ذخیره کند (تا بعداً با YYRESTORE بازیابی شود).
- YYRESTORE
- یک پایهٔ عمومی API که باید موقعیت ورودی فعلی را به مقدار ذخیرهشده توسط YYBACKUP بازیابی کند.
- YYBACKUPCTX
- یک پایهٔ عمومی API که باید موقعیت ورودی فعلی را به عنوان موقعیت بافت پسرو ذخیره کند (تا بعداً با YYRESTORECTX بازیابی شود).
- YYRESTORECTX
- یک پایهٔ عمومی API که باید موقعیت بافت پسرو ذخیرهشده توسط YYBACKUPCTX را بازیابی کند.
- YYRESTORETAG
- یک پایهٔ عمومی API با یک متغیر tag که باید موقعیت بافت پسرو را به مقدار tag بازیابی کند.
- YYSTAGP
- یک پایهٔ عمومی API با یک متغیر tag، که در آن tag میتواند یک اشارهگر یا یک آفست در YYINPUT باشد (برای جزئیات به بخش استخراج تطبیق فرعی مراجعه کنید). YYSTAGP باید tag را روی موقعیت ورودی فعلی تنظیم کند.
- YYSTAGN
- یک پایهٔ عمومی API با یک متغیر tag، که در آن tag میتواند یک اشارهگر یا یک آفست در YYINPUT باشد (برای جزئیات به بخش استخراج تطبیق فرعی مراجعه کنید). YYSTAGN باید tag را روی مقداری تنظیم کند که نشاندهندهٔ موقعیت ورودیِ ناموجود است.
- YYMTAGP
- یک پایهٔ عمومی API با یک متغیر tag. YYMTAGP باید موقعیت فعلی را به تاریخچهٔ تطبیق فرعی tag اضافه کند (برای جزئیات به بخش استخراج تطبیق فرعی مراجعه کنید).
- YYMTAGN
- یک پایهٔ عمومی API با یک متغیر tag. YYMTAGN باید مقداری را که نشاندهندهٔ موقعیت ورودی ناموجود است به تاریخچهٔ تطبیق فرعی tag اضافه کند (برای جزئیات به بخش استخراج تطبیق فرعی مراجعه کنید).
- YYSHIFT
- یک پایهٔ عمومی API با یک متغیر shift که باید موقعیت ورودی فعلی را به اندازهٔ shift نویسه تغییر مکان دهد (مقدار جابهجایی ممکن است منفی باشد).
- YYCOPYSTAG
- یک پایهٔ عمومی API با دو متغیر lhs و rhs که باید متغیر سمت راستِ s-tag یعنی rhs را در متغیر سمت چپِ s-tag یعنی lhs کپی کند. برای بیشتر زبانها این پایه دارای تعریفی پیشفرض است که lhs را به rhs تخصیص میدهد.
- YYCOPYMTAG
- یک پایهٔ عمومی API با دو متغیر lhs و rhs که باید متغیر سمت راستِ m-tag یعنی rhs را در متغیر سمت چپِ m-tag یعنی lhs کپی کند. برای بیشتر زبانها این پایه دارای تعریفی پیشفرض است که lhs را به rhs تخصیص میدهد.
- YYSHIFTSTAG
- یک پایهٔ عمومی API با دو متغیر tag و shift که باید tag را به اندازهٔ shift واحد کد جابهجا کند (مقدار جابهجایی ممکن است منفی باشد).
- YYSHIFTMTAG
- یک پایهٔ عمومی API با دو متغیر tag و shift که باید آخرین مقدار در تاریخچهٔ tag را به اندازهٔ shift واحد کد جابهجا کند (مقدار جابهجایی ممکن است منفی باشد).
- YYMAXNMATCH
- یک ثابت عدد صحیح برابر با حداکثر تعداد گروههای تسخیرکنندهٔ POSIX در یک قاعده. این مقدار با یک بلوک maxnmatch تولید میشود.
- YYCONDTYPE
- نوع شمارش شرط. میتواند با بلوک conditions یا گزینهٔ --header تولید شود.
- YYGETACCEPT
- یک پایه با یک متغیر var که انتخابکنندهٔ عددی قاعدهٔ پذیرفتهشده را ذخیره میکند. برای بیشتر زبانها این پایه دارای تعریفی پیشفرض است که از var میخواند.
- YYSETACCEPT
- یک پایه با دو متغیر: var (یک l-value که انتخابکنندهٔ عددی قاعدهٔ پذیرفتهشده را ذخیره میکند)، و val (مقدار انتخابکننده). برای بیشتر زبانها این پایه دارای تعریفی پیشفرض است که var را به val تخصیص میدهد.
- YYGETCOND
- یک r-value از نوع YYCONDTYPE که برابر با شناسهٔ شرط فعلی است.
- YYSETCOND
- یک پایه با یک متغیر cond که باید شناسهٔ شرط فعلی را به cond تنظیم کند.
- YYGETSTATE
- یک r-value از نوع عدد صحیح که برابر با وضعیت فعلی تحلیلگر لغوی است. باید با مقدار -1 مقداردهی اولیه شود.
- YYSETSTATE
- یک پایه با یک متغیر state که باید وضعیت فعلی تحلیلگر لغوی را روی state تنظیم کند.
- YYDEBUG
- این پایه فقط با گزینهٔ -d یا --debug-output تولید میشود. هدف آن افزودن گزارشگیری به کد تولیدشده است (تعریف معمول YYDEBUG یک دستور چاپ است). دستورات YYDEBUG در هر وضعیت تولید میشوند و دو متغیر دارند: state (یک نمایهٔ وضعیت DFA یا -1) و symbol (نماد ورودی فعلی).
- yyaccept
- یک l-value از نوع عدد صحیح بدون علامت که شمارهٔ آخرین قاعدهٔ تطبیقیافته را ذخیره میکند. تعریف کاربر فقط با گزینهٔ --storable-state ضروری است.
- yybm
- جدولی حاوی نقشههای بیتی فشردهشده برای حداکثر ۸ انتقال (مورداستفاده با گزینهٔ --bitmaps). جدول شامل ۲۵۶ عنصر است و توسط واحدهای کد ۱-بایتی نمایهگذاری میشود. هر عنصر ۸-بیتی مقادیر بولی را برای حداکثر ۸ انتقال با هم ترکیب میکند. بیت k-اُم از عنصر n-اُم درست است اگر و تنها اگر واحد کد n-اُم در محدودهٔ انتقال k-اُم باشد. ایدهٔ این نقشهٔ بیتی جایگزینی چندین شاخهٔ if یا موارد switch با یک بررسیِ تکبیت در جدول است..TP yych یک l-value از نوع YYCTYPE که نویسه ورودی فعلی را ذخیره میکند. تعریف آن توسط کاربر تنها با گزینه -f --storable-state ضروری است.
- yyctable
- جدول پرش تولیدشده برای اعزام شرایط اولیه (با ترکیب گزینههای --conditions و --computed-gotos فعال میشود).
- yyfill
- یک l-value که نتیجه فراخوانی YYFILL را ذخیره میکند (این ممکن است برای زبانهای تابعی محض که در آنها YYFILL یک تابع مونادیک با مقدار بازگشتی پیچیده است، ضروری باشد).
- yynmatch
- یک l-value از نوع عددی صحیح بدون علامت که تعداد گروههای گیرانداز POSIX را در قاعده منطبقشده ذخیره میکند. تنها با گزینه -P --posix-captures استفاده میشود.
- yypmatch
- آرایهای از l-valueها که برای نگهداری مقادیر برچسب متناظر با پرانتزهای گیرانداز در قاعده منطبقشده استفاده میشوند. طول آرایه باید حداقل yynmatch * 2 باشد (معمولاً YYMAXNMATCH * 2 انتخاب خوبی است). تنها با گزینه -P --posix-captures استفاده میشود.
- yystable
- منسوخ شده است.
- yystate
- یک l-value که همراه با گزینه --loop-switch برای ذخیره وضعیت کنونی DFA استفاده میشود.
- yytarget
- جدول پرش شامل مقصدهای پرش (نشانیهای برچسب) برای تمامی گذارها از یک وضعیت. این جدول برای هر وضعیت محلی است. تولید جدولهای yytarget با گزینه --computed-gotos فعال میشود.
گزینهها (OPTIONS)
برخی از گزینهها دارای پیکربندیهای متناظر هستند، برخی دیگر سراسری بوده و پس از شروع خواندن فایل ورودی توسط re2c قابل تغییر نیستند. گزینههای اشکالزدایی معمولاً نیازمند ساختن re2c در پیکربندی اشکالزدایی هستند. گزینههای داخلی برای آزمایش الگوریتمهای بهکاررفته در re2c مفید میباشند.
- -? --help -h
- نمایش پیام راهنما.
- --api <simple | record | generic>
- مشخص کردن API استفادهشده توسط کد تولیدی برای برقراری ارتباط با کد تعریفشده توسط کاربر. گزینه simple باید در حالتهای ساده استفاده شود که نیازی به پر کردن مجدد بافر و ذخیره وضعیت تحلیلگر واژگانی وجود ندارد. گزینه record باید زمانی استفاده شود که وضعیت تحلیلگر واژگانی باید در یک رکورد (struct، class و غیره) ذخیره شود. گزینه generic باید در حالتهای پیچیدهای که دو API دیگر به اندازه کافی منعطف نیستند، استفاده شود.
- --bit-vectors -b
- بهینهسازی پرشهای شرطی با استفاده از ماسکهای بیتی. این گزینه مستلزم --nested-ifs است.
- --captures, --leftmost-captures
- فعالسازی استخراج تطبیق فرعی با چپترین گروههای گیرانداز حریصانه. نتیجه در یک آرایه yybmatch با گنجایش 2 * YYMAXNMATCH جمعآوری شده، و yynmatch روی تعداد گروههای قاعده منطبقشده تنظیم میشود.
- --captvars, --leftmost-captvars
- فعالسازی استخراج تطبیق فرعی با چپترین گروههای گیرانداز حریصانه. نتیجه در متغیرهای yytl<k> و yytr<k> برای k-امین گروه گیرانداز جمعآوری میشود.
- --case-insensitive
- در نظر گرفتن رشتههای درون گیومه تکی و دوتایی بدون حساسیت به حروف کوچک و بزرگ.
- --case-inverted
- معکوس کردن معنای رشتههای درون گیومه تکی و دوتایی: در نظر گرفتن رشتههای درون گیومه تکی به عنوان حساس به حروف کوچک و بزرگ و رشتههای درون گیومه دوتایی به عنوان غیرحساس به حروف کوچک و بزرگ.
- --case-ranges
- ادغام حالتهای متوالی در یک دستور switch در قالب یک بازه به شکل low ... high. این نحو، یک افزونه زبان C/C++ است که توسط کامپایلرهایی مانند GCC، Clang و Tcc پشتیبانی میشود. مزیت اصلی نسبت به استفاده از حالتهای تکی، تولید کد کوچکتر و زمان تولید سریعتر است، هرچند برای برخی از کامپایلرها مانند Tcc به اندازه باینری کوچکتر نیز منجر میشود.
- --computed-gotos -g
- بهینهسازی پرشهای شرطی با استفاده از افزونه غیراستاندارد "computed goto" (که باید توسط کامپایلر پشتیبانی شود). re2c تنها در موارد پیچیده با انشعابهای شرطی فراوان جدولهای پرش را تولید میکند. آستانه پیچیدگی را میتوان با پیکربندی cgoto:threshold تنظیم کرد. آفستهای نسبی را میتوان با پیکربندی cgoto:relative فعال نمود. این گزینه مستلزم --bit-vectors است.
- --computed-gotos-relative
- مشابه --computed-gotos اما در عوض آفستهای نسبی را برای جدولهای پرش تولید میکند (که باید توسط کامپایلر پشتیبانی شود). این گزینه مستلزم --computed-gotos است.
- --conditions --start-conditions -c
- فعالسازی پشتیبانی از "conditions" مشابه Flex: چندین تحلیلگر واژگانی بههمپیوسته در یک بلوک. این یک جایگزین برای مشخص کردن دستی بلوکهای مختلف re2c متصلشده با goto یا فراخوانیهای تابع است.
- --depfile FILE
- نوشتن اطلاعات وابستگی در FILE در قالب یک قاعده Makefile به شکل <output-file> : <input-file> [include-file ...]. این امکان رهگیری وابستگیهای ساخت را در حضور بلوکها/دستورالعملهای include فراهم میکند، بهطوری که بهروزرسانی فایلهای include باعث بازتولید فایل خروجی میشود. این گزینه به گزینه --output وابسته است.
- --ebcdic --ecb -e
- تولید یک تحلیلگر واژگانی که ورودی را در کدگذاری EBCDIC میخواند. re2c فرض میکند که محدوده نویسهها 0 -- 0xFF و اندازه هر نویسه ۱ بایت است.
- --empty-class <match-empty | match-none | error>
- تعریف شیوه برخورد re2c با کلاسهای نویسه خالی. با match-empty (پیشفرض)، کلاس خالی با ورودی خالی تطبیق مییابد (که غیرمنطقی است اما سازگار با گذشته است). با match-none کلاس خالی همیشه در تطبیق شکست میخورد. با error کلاس خالی یک خطای کامپایل ایجاد میکند.
- --encoding-policy <fail | substitute | ignore>
- تعریف شیوه برخورد re2c با جانشینهای یونیکد. با fail برنامه re2c در صورت مواجهه با یک جانشین با خطا متوقف میشود. با substitute برنامه re2c بیصدا جانشینها را با نقطه کد خطای 0xFFFD جایگزین میکند. با ignore (پیشفرض) re2c با جانشینها مانند نقاط کد عادی رفتار میکند. استاندارد یونیکد میگوید که جانشینهای تنها نامعتبرند، اما کتابخانهها و برنامههای دنیای واقعی رفتارهای متفاوتی دارند.
- --flex-syntax -F
- پشتیبانی جزئی از نحو Flex: در این حالت، تعاریف نامگذاریشده نیازی به علامت مساوی و نقطه-ویرگول پایانی ندارند، و هنگام استفاده باید درون آکولاد قرار گیرند. نامهای بدون آکولاد به عنوان رشتههای درون گیومه دوتایی تلقی میشوند.
- --goto-label
- استفاده از مدل کد "goto/label": کدگذاری DFA در قالب بلوکهای کد برچسبدار متصلشده با گذارهای goto میان بلوکها. این تنها برای زبانهایی پشتیبانی میشود که دارای دستور goto هستند.
- --header --type-header -t HEADER
- تولید یک فایل HEADER. محتوای این فایل را میتوان با استفاده از بلوکهای ویژه header:on و header:off تعیین کرد. اگر از شرایط استفاده شود، سربرگ تولیدشده بهطور خودکار یک enum شرطی به آن الصاق خواهد داشت (مگر اینکه یک بلوک صریح conditions وجود داشته باشد).
- -I PATH
- افزودن PATH به فهرست مکانهایی که هنگام جستجوی فایلهای include استفاده میشوند. این گزینه در ترکیب با بلوک یا دستورالعمل include مفید است. re2c به دنبال FILE در دایرکتوری فایل والد و در مکانهای include مشخصشده با گزینه -I میگردد.
- --input <default | custom>
- نام مستعار منسوخشده برای --api. گزینه default متناظر با simple است (در واقع برای بیشتر پیشرانهها پیشفرض است، اما نه برای همه). گزینه custom متناظر با generic است.
- --input-encoding <ascii | utf8>
- تعیین شیوه تجزیه عبارتهای منظم توسط re2c. با ascii (پیشفرض) re2c ورودی را با کدگذاری ASCII مدیریت میکند: هر دنبالهای از واحدهای کد، دنبالهای از نویسههای مستقل ۱ بایتی است. با utf8 re2c ورودی را با کدگذاری UTF8 مدیریت کرده و نویسههای چندبایتی را تشخیص میدهد.
- --invert-captures
- معکوس کردن معنای گروههای گیرانداز و غیرگیرانداز. بهطور پیشفرض (...) گیرانداز و (! ...) غیرگیرانداز است. با این گزینه (! ...) گیرانداز و (...) غیرگیرانداز خواهد بود.
- --lang <none | c | d | go | haskell | java | js | ocaml | python | rust | swift | v | zig>
- مشخص کردن زبان مقصد. زبانهای پشتیبانیشده عبارتند از C، D، Go، Haskell، Java، JS، OCaml، Python، Rust، Swift، V، Zig (زبانهای بیشتری را میتوان از طریق فایلهای نحو تعریفشده توسط کاربر اضافه کرد، گزینه --syntax را ببینید). گزینه none پیکربندیهای نحو پیشفرض را غیرفعال میکند تا زبان مقصد تعریفنشده باقی بماند.
- --location-format <gnu | msvc>
- مشخص کردن قالب مکان در پیامها. با gnu مکانها بهصورت 'filename:line:column: ...' چاپ میشوند. با msvc مکانها بهصورت 'filename(line,column) ...' چاپ میشوند. پیشفرض gnu است.
- --loop-switch
- استفاده از مدل کد "loop/switch": کدگذاری DFA در قالب یک حلقه بر روی دستور switch، جایی که وضعیتهای منفرد همان حالتهای دستور switch هستند. وضعیت در یک متغیر yystate ذخیره میشود. گذارها میان وضعیتها yystate را به برچسب case وضعیت مقصد بهروزرسانی کرده و اجرا را به ابتدای حلقه ادامه میدهند..TP --nested-ifs -s استفاده از دستورهای if تودرتو به جای دستورهای switch در پرشهای شرطی. این کار معمولاً در کامپایلرهای فاقد بهینهسازی منجر به ایجاد کد کارآمدتری میشود.
- --no-debug-info -i
- دستورالعملهای خط (line directives) را در خروجی قرار نده. این گزینه ممکن است هنگامی که کد تولیدشده در یک سیستم کنترل نسخه نگهداری میشود مفید باشد (برای جلوگیری از تفاوتها یا diffهای عظیمِ تولیدشده بهصورت خودکار بر اثر تغییرات کوچک).
- --no-generation-date
- جلوگیری از درج تاریخ در فایل تولیدشده.
- --no-version
- جلوگیری از درج نسخه در فایل تولیدشده.
- --no-unsafe
- پوشش unsafe را روی YYPEEK تولید نکن (این گزینه مخصوص Rust است). به دلایل عملکردی، YYPEEK باید از بررسی مرزها (bounds-checking) خودداری کند، زیرا تحلیلگر لغوی (lexer) از قبل بررسیهای انتهای ورودی را به شیوهای کارآمدتر انجام میدهد. کاربر میتواند تعریف امنی از YYPEEK ارائه دهد، یا تعریفی که تنها در بیلدهای release ناامن است؛ در چنین حالتی، گزینه --no-unsafe به جلوگیری از هشدارهای مربوط به بلوکهای unsafe زائد کمک میکند.
- --output -o OUTPUT
- فایل OUTPUT را مشخص میکند.
- --posix-captures, -P
- استخراج زیرتطابق (submatch) را با گروههای ضبط به سبک POSIX فعال میکند. نتیجه در یک آرایه yybmatch با ظرفیت 2 * YYMAXNMATCH جمعآوری میشود، و yynmatch روی تعداد گروهها برای قاعده منطبقشده تنظیم میگردد.
- --posix-captvars
- استخراج زیرتطابق را با گروههای ضبط به سبک POSIX فعال میکند. نتیجه در متغیرهای yytl<k> و yytr<k> برای k-امین گروه ضبط جمعآوری میشود.
- --recursive-functions
- استفاده از مدل کدی مبتنی بر توابع همبازگشتی (co-recursive)، که در آن هر حالت DFA یک تابع مجزا است که میتواند توابع حالتهای دیگر یا خودش را فراخوانی کند.
- --reusable -r
- از نسخه 2.2 منسوخ شده است (بلوکهای قابل استفاده مجدد اکنون بهطور پیشفرض مجاز هستند).
- --skeleton -S
- کد رابط تعریفشده توسط کاربر را نادیده گرفته و یک برنامه خودکفای "skeleton" (اسکلت) تولید میکند. علاوهبر این، فایلهای ورودی را با رشتههای مشتقشده از دستور زبان باقاعده و نتایج تطابق فشردهشده تولید میکند که برای تأیید رفتار "skeleton" روی تمام ورودیها استفاده میشوند. این گزینه برای یافتن باگها در بهینهسازیها و تولید کد مفید است. این گزینه تنها برای C پشتیبانی میشود.
- --storable-state -f
- تولید یک تحلیلگر لغوی که میتواند وضعیت درونی خود را ذخیره کند. این قابلیت در تحلیلگرهای لغوی مدل push مفید است که وقتی ورودی کافی وجود ندارد توسط یک برنامه بیرونی متوقف میشوند و سپس با دردسترس قرار گرفتن ورودی بیشتر از سر گرفته میشوند. در این حالت کاربران باید علاوهبر این، دستورهای اولیه YYGETSTATE و YYSETSTATE را تعریف کنند، و متغیرهای yych، yyaccept و state باید بخشی از وضعیت ذخیرهشده لکسر باشند.
- --syntax FILE
- پیکربندیها را از FILE مشخصشده بارگذاری کرده و آنها را بر روی فایل نحو (syntax) پیشفرض اعمال میکند. توجه داشته باشید که FILE میتواند تنها چند پیکربندی را تعریف کند (اگر برای اصلاح فایل نحو پیشفرض استفاده شود)، یا میتواند یک بکاند زبانی کاملاً جدید را تعریف نماید (در مورد اخیر توصیه میشود از گزینه --lang none استفاده کنید).
- استخراج زیرتطابق را با برچسبها (tags) فعال میکند.
- --ucs2 --wide-chars -w
- تولید لکسری که ورودی کدگذاریشده با UCS2 را میخواند. re2c فرض میکند که محدوده نویسهها 0 -- 0xFFFF و اندازه نویسه ۲ بایت است. این گزینه مستلزم --nested-ifs است.
- --utf8 --utf-8 -8
- تولید لکسری که ورودی با کدگذاری UTF-8 را میخواند. re2c فرض میکند که محدوده نویسهها 0 -- 0x10FFFF و اندازه نویسه ۱ بایت است.
- --utf16 --utf-16 -x
- تولید لکسری که ورودی کدگذاریشده با UTF16 را میخواند. re2c فرض میکند که محدوده نویسهها 0 -- 0x10FFFF و اندازه نویسه ۲ بایت است. این گزینه مستلزم --nested-ifs است.
- --utf32 --unicode -u
- تولید لکسری که ورودی کدگذاریشده با UTF32 را میخواند. re2c فرض میکند که محدوده نویسهها 0 -- 0x10FFFF و اندازه نویسه ۴ بایت است. این گزینه مستلزم --nested-ifs است.
- --verbose
- نمایش یک پیام کوتاه در صورت موفقیت.
- --vernum -V
- نمایش اطلاعات نسخه در قالب MMmmpp (اصلی، فرعی، وصله).
- --version -v
- نمایش اطلاعات نسخه.
- --single-pass -1
- منسوخ شده است. کاری انجام نمیدهد (تکگذر بودن اکنون رفتار پیشفرض است).
- --debug-output -d
- درج فراخوانیهای YYDEBUG در کد تولیدشده. این مورد برای ردیابی اجرای لکسر مفید است.
- --dump-adfa
- گزینه اشکالزدایی: خروجی دادن DFA پس از تونلزنی (tunneling) (در قالب .dot).
- --dump-cfg
- گزینه اشکالزدایی: خروجی دادن گراف جریان کنترل متغیرهای برچسب (در قالب .dot).
- --dump-closure-stats
- گزینه اشکالزدایی: خروجی دادن آمار مربوط به تعداد حالتها در بستار (closure).
- --dump-dfa-det
- گزینه اشکالزدایی: خروجی دادن DFA بلافاصله پس از قطعیسازی (determinization) (در قالب .dot).
- --dump-dfa-min
- گزینه اشکالزدایی: خروجی دادن DFA پس از کمینهسازی (minimization) (در قالب .dot).
- --dump-dfa-tagopt
- گزینه اشکالزدایی: خروجی دادن DFA پس از بهینهسازیهای برچسب (در قالب .dot).
- --dump-dfa-tree
- گزینه اشکالزدایی: خروجی دادن DFA در حال ساخت با حالتهایی که به عنوان درختهای تاریخچه برچسب نمایش داده میشوند (در قالب .dot).
- --dump-dfa-raw
- گزینه اشکالزدایی: خروجی دادن DFA در حال ساخت با مجموعهحالتهای بسطیافته (در قالب .dot).
- --dump-interf
- گزینه اشکالزدایی: خروجی دادن جدول تداخل حاصل از تحلیل زنده بودن (liveness analysis) متغیرهای برچسب.
- --dump-nfa
- گزینه اشکالزدایی: خروجی دادن NFA (در قالب .dot).
- --emit-dot -D
- به جای خروجی عادی، گراف لکسر را در قالب .dot تولید میکند. خروجی میتواند به کمک Graphviz به یک تصویر تبدیل شود (مثلاً دستورهایی مانند dot -Tpng -odfa.png dfa.dot).
- --dfa-minimization <moore | table>
- گزینه داخلی: الگوریتم کمینهسازی DFA مورد استفاده توسط re2c. گزینه moore نشاندهنده الگوریتم مور (Moore) است (که پیشفرض میباشد). گزینه table نشاندهنده الگوریتم "پر کردن جدول" (table filling) است. هر دو الگوریتم باید صرفنظر از برچسبگذاری مجدد حالتها، DFA یکسانی تولید کنند؛ الگوریتم پر کردن جدول سادهتر و بسیار کندتر است و به عنوان پیادهسازی مرجع عمل میکند.
- --eager-skip
- گزینه داخلی: باعث میشود لکسر تولیدشده موقعیت ورودی را بهصورت مشتاقانه (eagerly) پیش ببرد -- بلافاصله پس از خواندن نماد ورودی. این گزینه رفتار پیشفرض را که در آن موقعیت ورودی بهصورت تنبل (lazily) -- پس از گذار به حالت بعدی -- پیش میرود، تغییر میدهد.
- --no-lookahead
- گزینه داخلی، منسوخشده. قبلاً برای فعال کردن الگوریتم TDFA(0) استفاده میشد. بر خلاف TDFA(1)، الگوریتم TDFA(0) از پیشنگری یکنمادی (one-symbol lookahead) استفاده نمیکند. این الگوریتم عملیاتهای ثبات را به جای گذارهای خروجی، روی گذارهای ورودی اعمال میکند. ارزیابیها نشان دادند که الگوریتم TDFA(0) نسبت به TDFA(1) کارایی کمتری دارد.
- --no-optimize-tags
- گزینه داخلی: جلوگیری از بهینهسازی متغیرهای برچسب (مفید برای اشکالزدایی).
- --posix-closure <gor1 | gtop>
- گزینه داخلی: تعیین الگوریتم کوتاهترین مسیر مورد استفاده برای ساخت بستار اپسیلون (epsilon-closure) با معناشناسی رفع ابهام POSIX: گزینه gor1 (پیشفرض) نشاندهنده الگوریتم گلدبرگ-رادزیک (Goldberg-Radzik) است، و gtop نشاندهنده الگوریتم "ترتیب توپولوژیکی سراسری" (global topological order) است.
- --posix-prectable <complex | naive>
- گزینه داخلی: مشخص کردن الگوریتم مورد استفاده برای محاسبه جدول اولویت POSIX. الگوریتم complex جدول اولویت را در یک پیمایش از درخت تاریخچه برچسب محاسبه میکند و دارای پیچیدگی مرتبه دوم در تعداد حالتهای TNFA است؛ این گزینه پیشفرض است. الگوریتم naive در بدترین حالت دارای پیچیدگی مرتبه سوم در تعداد حالتهای TNFA است، اما بسیار سادهتر از complex بوده و در موارد عادی ممکن است کمی سریعتر باشد..TP --stadfa گزینهٔ داخلی، منسوخشده. در گذشته برای فعالسازی الگوریتم staDFA به کار میرفت، که تفاوت آن با TDFA در این است که عملیات ثباتها به جای انتقالها درون وضعیتها قرار میگیرد. بنچمارکها نشان دادند که الگوریتم staDFA کارایی کمتری نسبت به TDFA دارد.
- --fixed-tags <none | toplevel | all>
- گزینهٔ داخلی: مشخص میکند که آیا بهینهسازی برچسب ثابت (fixed-tag) باید روی همهٔ برچسبها اعمال شود (all)، هیچیک از آنها (none)، یا فقط روی برچسبهای موجود در الحاق بالاترین سطح (toplevel). مقدار پیشفرض all است. برچسبهای "ثابت" برچسبهایی هستند که در فاصلهٔ ثابتی از برچسبی دیگر (موسوم به "پایه") قرار دارند. در چنین مواردی تنها نیاز به ردیابی برچسب پایه است، و مقدار برچسب ثابت میتواند از مقدار برچسب پایه به علاوهٔ یک آفست ایستا محاسبه شود. برای برچسبهایی که تحت جایگزینی یا تکرار هستند، بررسی اینکه آیا برچسب پایه مقدار عدمتطابق (no-match) دارد نیز ضروری است (در این صورت برچسب ثابت نیز صرفنظر از آفست، باید روی no-match تنظیم شود). برای برچسبهای موجود در الحاق بالاترین سطح، این بررسی لازم نیست، زیرا آنها همیشه تطبیق مییابند.
هشدارها (Warnings)
هشدارها را میتوان بهصورت جداگانه فعال یا غیرفعال کرد و یا به یک خطا تبدیل نمود.
- -W
- فعال کردن تمام هشدارها.
- -Werror
- تبدیل هشدارها به خطا. توجه داشته باشید که این گزینه به تنهایی هیچ هشداری را فعال نمیکند؛ بلکه تنها بر هشدارهایی اثر میگذارد که تا کنون فعال شدهاند یا بعداً فعال خواهند شد.
- -W<warning>
- فعال کردن warning.
- -Wno-<warning>
- غیرفعال کردن warning.
- -Werror-<warning>
- فعال کردن warning و در نظر گرفتن آن به عنوان خطا (این گزینه متضمن -W<warning> است).
- -Wno-error-<warning>
- با این warning خاص به عنوان خطا برخورد نکن. این گزینه خودِ هشدار را غیرفعال نمیکند.
- -Wcondition-order
- هشدار در صورتی که برنامهٔ تولیدشده فرضیات ضمنی دربارهٔ شمارهگذاری شرطها داشته باشد. باید از گزینهٔ --header یا بلوک conditions برای تولید نگاشتی از نام شرطها به شمارهها استفاده شود و سپس نامهای شرطِ تولیدشده بهصورت خودکار به کار روند.
- -Wempty-character-class
- هشدار در صورتی که یک عبارت منظم حاوی یک کلاس نویسهٔ خالی باشد. تلاش برای تطبیق یک کلاس نویسهٔ خالی بیمعنی است: همیشه باید ناموفق باشد. با این حال، به دلایل سازگاری عقبرو، re2c کلاسهای نویسهٔ خالی را مجاز دانسته و با آنها مانند رشتههای خالی رفتار میکند. از گزینهٔ --empty-class برای تغییر رفتار پیشفرض استفاده کنید.
- -Wmatch-empty-string
- هشدار در صورتی که یک قاعده پوچپذیر باشد (با یک رشتهٔ خالی تطبیق یابد). اگر تحلیلگر لغوی درون یک حلقه اجرا شود و تطبیق رشتهٔ خالی غیرعمدی باشد، تحلیلگر لغوی ممکن است بهطور غیرمنتظره در یک حلقهٔ بیپایان متوقف شود.
- -Wswapped-range
- هشدار در صورتی که کران پایین یک بازه بزرگتر از کران بالای آن باشد. رفتار پیشفرض این است که بدون هشدار، جای کرانهای بازه جابهجا میشود.
- -Wundefined-control-flow
- هشدار در صورتی که برخی از رشتههای ورودی باعث جریان کنترل تعریفنشده در تحلیلگر لغوی شوند (الگوهای دارای اشکال گزارش میشوند). این یک اشتباه خطرناک و رایج است. این مشکل را میتوان به سادگی با افزودن قاعدهٔ پیشفرض * که کمترین اولویت را دارد، با هر واحد کد مطابقت دارد و همیشه یک واحد کد را مصرف میکند، برطرف ساخت.
- -Wunreachable-rules
- هشدار دربارهٔ قواعدی که توسط قواعد دیگر پوشانده شدهاند (shadowed) و هرگز تطبیق نخواهند یافت.
- -Wdeprecated-eof_rule
- هشدار دربارهٔ قواعد مستقل پایان ورودی $ که بر اثر تغییرات آینده از کار خواهند افتاد و نیاز به اصلاح دارند. در حال حاضر این قواعد هنگام تداخل با قواعد دیگر دارای اولویت هستند، اما پس از معرفی نماد عمومی پایان ورودی $، ترتیب اولویت تغییر خواهد کرد و این قواعد توسط قواعد دیگر پوشانده خواهند شد.
- -Wuseless-escape
- هشدار در صورتی که یک نماد به اشتباه گریزدهی شده باشد (دارای escape باشد). بهطور پیشفرض، re2c چنین گریزهایی را بیصدا نادیده میگیرد، اما این حالت میتواند نشاندهندهٔ یک اشتباه تایپی یا خطا در توالی گریز (escape sequence) باشد.
- -Wnondeterministic-tags
- هشدار در صورتی که یک برچسب دارای درجهٔ nام از غیرقطعی بودن باشد، که در آن n بزرگتر از ۱ است.
- -Wsentinel-in-midrule
- هشدار در صورتی که نماد نگهبان در میانهٔ یک قاعده رخ دهد --- این حالت ممکن است باعث خواندن دادهها فراتر از انتهای بافر، کرش کردن یا آسیب دیدن حافظه در تحلیلگر لغوی تولیدشده شود. این هشدار تنها زمانی اعمال میشود که روش نگهبان برای بررسی پایان ورودی استفاده شده باشد. در صورتی که از پیکربندی re2c:sentinel استفاده شود، این مورد به یک خطا تبدیل میگردد.
- -Wundefined-syntax-config
- هشدار در
صورتی که
فایل نحو
مشخصشده
با گزینهٔ
--syntax فاقد
تعاریف
برخی از
پیکربندیها
باشد. این
ویژگی به
نگهداری
فایلهای
نحو
تعریفشده
توسط کاربر
کمک میکند:
اگر انتشار
جدیدی
پیکربندیهای
جدیدی
اضافه کند،
فایل نحو
قدیمی یک
هشدار صادر
خواهد کرد و
کاربر مطلع
میشود. اگر
برخی
پیکربندیها
استفاده
نمیشوند و
نیازی به
تعریف
ندارند،
باید به
صراحت روی
<undefined> تنظیم
شوند.
پشتیبانی از زبانهای گوناگون در re2c بر پایه ایده syntax files (فایلهای نحو) استوار است. یک فایل نحو، یک فایل پیکربندی است که نحو زبان مقصد را تعریف میکند -- نه تمام زبان، بلکه بخش کوچکی از آن را که توسط کد تولیدشده استفاده میشود. فایلهای نحو re2c را بسیار انعطافپذیر میسازند، اما نباید به عنوان جایگزینی برای پیکربندیهای re2c: استفاده شوند: هدف آنها تعریف نحو زبان مقصد است، نه سفارشیسازی یک تحلیلگر لغوی (lexer) خاص. تمام زبانهای پشتیبانیشده دارای فایلهای نحو پیشفرضی هستند که بخشی از توزیع برنامه میباشند (زیردایرکتوری include/syntax را ببینید)؛ آنها همچنین در باینری re2c تعبیه شدهاند. کاربران میتوانند یک فایل نحو سفارشی ارائه دهند که چند پیکربندی را برای یکی از زبانهای پشتیبانیشده بازنویسی کند، یا میتوانند تمام پیکربندیها را از نو تعریف کنند (در این حالت باید از گزینه --lang none استفاده شود). فایلهای نحو حاوی چهار نوع پیکربندی مختلف هستند: فهرستهای ویژگی، پیکربندیهای زبان، پیکربندیهای درجا و الگوهای کد.
فهرستهای ویژگی (Feature lists)
- supported_apis
- فهرستی از APIهای پشتیبانیشده با مقادیر ممکن simple، record، generic.
- supported_api_styles
- فهرستی از سبکهای API پشتیبانیشده با مقادیر ممکن functions، free-form.
- supported_code_models
- فهرستی از مدلهای کد پشتیبانیشده با مقادیر ممکن goto-label، loop-switch، recursive-functions.
- supported_targets
- فهرستی از مقصدهای تولید کد پشتیبانیشده با مقادیر ممکن code، dot، skeleton.
- supported_features
- فهرستی از ویژگیهای پشتیبانیشده با مقادیر ممکن nested-ifs، bitmaps، computed-gotos، case-ranges، monadic، unsafe، tags، captures، captvars.
پیکربندیهای زبان (Language configurations)
- semicolons
- غیر صفر اگر زبان پس از دستورها از سمیکالن (نقطهویرگول) استفاده میکند.
- backtick_quoted_strings
- غیر صفر اگر زبان رشتههای محصور در بکتیک دارد.
- single_quoted_strings
- غیر صفر اگر زبان رشتههای محصور در نقلقول تکی دارد.
- indentation_sensitive
- غیر صفر اگر زبان به دندانهگذاری (indentation) حساس است.
- wrap_blocks_in_braces
- غیر صفر اگر دستورهای مرکب باید درون آکوالاد محصور شوند.
پیکربندیهای درجا (Inplace configurations)
الگوهای کد (Code templates)
code-template ::
name '=' code-exprs ';'
| CODE_TEMPLATE ';'
| '<undefined>' ';'
code-exprs ::
<EMPTY>
| code-exprs code-expr
code-expr ::
STRING
| VARIABLE
| optional
| list
optional ::
'(' CONDITIONAL '?' code-exprs ')'
| '(' CONDITIONAL '?' code-exprs ':' code-exprs ')'
list ::
'[' VARIABLE ':' code-exprs ']'
| '[' VARIABLE '{' NUMBER '}' ':' code-exprs ']'
| '[' VARIABLE '{' NUMBER ',' NUMBER '}' ':' code-exprs ']'
یک الگوی کد دنبالهای از رشتههای لفظی، متغیرها، عناصر اختیاری و فهرستها، یا ارجاعی به یک الگوی کد دیگر، یا مقدار ویژه <undefined> است. متغیرها جاینگهدارهایی هستند که در طول مرحله تولید کد جایگزین میشوند. متغیرهای فهرستی خاص هستند: هنگام بسط الگوهای فهرستی، re2c عبارتهای سمت راست دونقطه را چند بار تکرار میکند، و هر بار رخدادهای متغیر فهرستی را با مقداری مختص به این تکرار جایگزین مینماید. فهرستها دارای کرانهای اختیاری هستند (مقادیر منفی از انتها شمرده میشوند، برای نمونه -1 به معنای آخرین عنصر است). نامهای شرطی با یک نقطه آغاز میشوند. هر دو مورد شرطها و متغیرها میتوانند محلی (مختص به الگوی کد دادهشده) یا سراسری (مجاز در تمام الگوهای کد) باشند. هنگامی که re2c یک فایل نحو را میخواند، بررسی میکند که هر الگوی کد فقط از متغیرها و شرطهایی استفاده کند که در آن مجاز هستند.
برای نمونه، الگوی کد زیر ساختار if-then-else را برای یک زبان شبه C تعریف میکند:
code:if_then_else =
[branch{0}: topindent "if " cond " {" nl
indent [stmt: stmt] dedent]
[branch{1:-1}: topindent "} else" (.cond ? " if " cond) " {" nl
indent [stmt: stmt] dedent]
topindent "}" nl;
در اینجا branch یک متغیر فهرستی است: branch{0} به شاخه نخست بسط مییابد (که خاص است، زیرا بخش else ندارد)، و branch{1:-1} به تمام شاخههای باقیمانده (در صورت وجود) بسط مییابد. stmt نیز یک متغیر فهرستی است: [stmt: stmt] یک فهرست تودرتو است که به فهرستی از دستورها در بدنه شاخه جاری بسط مییابد. topindent، indent، dedent و nl متغیرهای سراسری هستند، و .cond یک شرط محلی است (معنای آنها در ادامه شرح داده شده است). این الگوی کد میتواند کد زیر را تولید کند:
if x {
// do something
} else if y {
// do something else
} else {
// don't do anything
}
در اینجا فهرستی از تمام الگوهای کد پشتیبانیشده توسط re2c همراه با متغیرها و شرطهای محلی آنها آمده است. توجه داشته باشید که یک تعریف خاص ممکن است از متغیرها و شرطهای محلی استفاده کند، اما الزامی به آن ندارد. هر الگوی کد استفادهنشده باید بر روی <undefined> تنظیم شود.
- code:var_global
- مشابه code:var_local، با این تفاوت که در بالاترین سطح (top-level) استفاده میشود.
- code:const_local
- تعریف یک ثابت محلی. متغیرهای پشتیبانیشده: type (نوع ثابت)، name (نام آن) و init (مقدار اولیه).
- code:const_global
- مشابه code:const_local، با این تفاوت که در بالاترین سطح (top-level) استفاده میشود.
- code:array_local
- تعریف یک آرایه (جدول) محلی. متغیرهای پشتیبانیشده: type (نوع عناصر آرایه)، name (نام آرایه)، size (اندازه آن)، row (یک متغیر فهرستی که به خودی خود کدی تولید نمیکند، بلکه عبارت فهرست را به تعداد سطرهای جدول بسط میدهد) و elem (یک متغیر فهرستی که به تمام عناصر جدول در سطر جاری بسط مییابد -- برای تودرتو شدن درون فهرست row در نظر گرفته شده است). شرط پشتیبانیشده: .const (اگر آرایه تغییرناپذیر باشد درست است).
- code:array_global
- مشابه code:array_local، با این تفاوت که در بالاترین سطح (top-level) استفاده میشود.
- code:array_elem
- ارجاع به عنصری از یک آرایه (جدول). متغیرهای پشتیبانیشده: array (نام آرایه) و index (شاخص عنصر).
- code:enum
- تعریف یک شمارش (enumeration) (میتواند با استفاده از یک ساختار زبانی خاص برای شمارشها، یا صرفاً به صورت چند ثابت مستقل تعریف شود). متغیرهای پشتیبانیشده عبارتند از type (نوع شمارش تعریفشده توسط کاربر یا نوع ثابتها)، elem (متغیر فهرستی که به نام هر عضو بسط مییابد) و init (مقداردهی اولیه برای هر عضو). شرطها: .init (در صورت وجود مقداردهی اولیه درست است).
- code:enum_elem
- عنصر شمارش (عضوی از یک نوع شمارشی تعریفشده توسط کاربر یا نام یک ثابت، بسته به نحوه تعریف code:enum). متغیرهای پشتیبانیشده عبارتند از name (نام عنصر) و type (نوع آن).
- code:assign
- دستور انتساب (assignment). متغیرهای پشتیبانیشده عبارتند از lhs (سمت چپ) و rhs (سمت راست).
- code:type_int
- نوع عدد صحیح علامتدار (signed integer).
- code:type_uint
- نوع عدد صحیح بدون علامت (unsigned integer).
- code:type_yybm
- نوع عناصر موجود در جدول yybm.
- code:type_yytarget
- نوع عناصر موجود در جدول yytarget.
- code:type_yyctable
- نوع عناصر موجود در جدول yyctable.
- code:cmp_eq
- عملگر «برابر» ("equals").
- code:cmp_ne
- عملگر «نابرابر» ("not equals").
- code:cmp_lt
- عملگر «کوچکتر از» ("less than").
- code:cmp_gt
- عملگر «بزرگتر از» ("greater than").
- code:cmp_le
- عملگر «کوچکتر یا مساوی» ("less or equal").
- code:cmp_ge
- عملگر «بزرگتر یا مساوی» ("greater or equal").
- code:if_then_else
- دستور if-then-else با یک یا چند شاخه. متغیرهای پشتیبانیشده: branch (یک متغیر فهرستی که خود کدی تولید نمیکند، بلکه عبارت فهرست را به تعداد شاخهها بسط میدهد)، cond (شرط شاخه جاری) و stmt (یک متغیر فهرستی که به تمام دستورهای شاخه جاری بسط مییابد). شرطها: .cond (اگر شاخه جاری دارای شرط باشد درست است)، .many (اگر بیش از یک شاخه وجود داشته باشد درست است).
- code:if_then_else_oneline
- یک مورد خاص از code:if_then_else برای زمانی که تمام شاخهها دستورهای تکخطی دارند. اگر این مقدار <undefined> باشد، به جای آن از code:if_then_else استفاده میشود.
- code:switch
- یک دستور switch با یک یا چند مورد (case). متغیرهای پشتیبانیشده: expr (عبارتی که switch روی آن انجام میشود) و case (یک متغیر فهرستی که به تمام گروههای case به همراه بلوکهای کدشان بسط مییابد).
- code:switch_cases
- گروهی از موارد switch که به یک بلوک کد واحد نگاشت میشوند. متغیرهای پشتیبانیشده عبارتند از case (یک متغیر فهرستی که به تمام موارد در این گروه بسط مییابد) و stmt (یک متغیر فهرستی که به تمام دستورها در بلوک کد بسط مییابد).
- code:switch_cases_oneline
- یک مورد خاص از code:switch_cases برای زمانی که بلوک کد شامل یک دستور تکخطی واحد است. اگر این مقدار <undefined> باشد، به جای آن از code:switch_cases استفاده میشود.
- code:switch_case_range
- یک مورد (case) منفرد از switch که محدودهای از مقادیر را پوشش میدهد (احتمالاً شامل تنها یک مقدار). متغیر پشتیبانیشده: val (یک متغیر فهرستی که به تمام مقادیر موجود در محدوده بسط مییابد). شرطهای پشتیبانیشده: .many (اگر بیش از یک مقدار در محدوده وجود داشته باشد درست است) و .char_literals (اگر یک switch روی لیترالهای نویسهای باشد درست است -- برخی زبانها نحو ویژهای برای این مورد ارائه میدهند).
- code:switch_case_default
- مورد پیشفرض (default) در دستور switch.
- code:loop
- حلقهای که تا ابد اجرا میشود (مگر اینکه از درون بدنه حلقه متوقف شود). متغیرهای پشتیبانیشده: label (برچسب حلقه)، stmt (یک متغیر فهرستی که به تمام دستورهای درون بدنه حلقه بسط مییابد).
- code:continue
- دستور continue. متغیرهای پشتیبانیشده: label (برچسبی که اجرا باید از آن ادامه یابد).
- code:goto
- دستور goto. متغیرهای پشتیبانیشده: label (برچسب مقصد پرش).
- code:cgoto
- دستور goto محاسباتی (computed). متغیرهای پشتیبانیشده: array (جدول حاوی اطلاعات goto محاسباتی)، index (شاخص عنصر در جدول) و base (برچسب پایه، فقط در صورتی استفاده میشود که .cgoto.relative درست باشد).
- code:cgoto:data
- عبارت مقداردهی اولیه برای یک عنصر منفرد در جدول goto محاسباتی. متغیرهای پشتیبانیشده: label (برچسبی که برای مقداردهی اولیه به عنصر جاری استفاده میشود)، type (نوع زیربنایی عناصر موجود در جدول) و base (برچسب پایه - فقط در صورتی استفاده میشود که .cgoto.relative درست باشد).
- code:fndecl
- اعلان تابع (function declaration). متغیرهای پشتیبانیشده: name (نام تابع)، type (نوع بازگشتی)، throw (استثناهای پرتابشده توسط این تابع، که به پیکربندی re2c:yyfn:throw نگاشت میشود)، arg (یک متغیر فهرستی که خود کدی تولید نمیکند، بلکه عبارت فهرست را به تعداد آرگومانهای تابع بسط میدهد)، argname (نام آرگومان جاری)، argtype (نوع آرگومان جاری). شرط: .type (درست است اگر این تابع غیر void باشد).
- code:fndef
- مشابه code:fndecl، اما برای تعریف تابع به کار میرود؛ بنابراین دارای یک متغیر فهرستی اضافی به نام stmt است که به تمام دستورهای درون بدنه تابع بسط مییابد.
- code:fncall
- دستور فراخوانی تابع. متغیرهای پشتیبانیشده: name (نام تابع)، retval (مقدار چپ یا l-value که مقدار بازگشتی در آن ذخیره میشود، در صورت وجود) و arg (یک متغیر فهرستی که به تمام آرگومانهای تابع بسط مییابد). شرطها: .args (درست است اگر تابع دارای آرگومان باشد) و .retval (درست است اگر مقدار بازگشتی نیاز به ذخیره شدن داشته باشد).
- code:tailcall
- دستور فراخوانی دم (tail call). متغیرهای پشتیبانیشده: name (نام تابع)، و arg (یک متغیر فهرستی که به تمام آرگومانهای تابع بسط مییابد). شرطها: .args (درست است اگر تابع دارای آرگومان باشد) و .retval (درست است اگر این یک تابع غیر void باشد).
- code:recursive_functions
- بدنه برنامه با مدل کد --recursive-functions. متغیرهای پشتیبانیشده: fn (یک متغیر فهرستی که خود کدی تولید نمیکند، بلکه عبارت فهرست را به تعداد توابع بسط میدهد)، fndecl (اعلان تابع جاری) و fndef (تعریف تابع جاری)..TP code:fingerprint اثرانگشت (fingerprint) در بالای فایل خروجی تولیدشده. متغیرهای پشتیبانیشده: ver (نسخهٔ re2c که برای تولید این خروجی استفاده شده است) و date (تاریخ تولید).
- code:line_info
- قالب دستورالعملهای خط (اگر روی <undefined> تنظیم شود، هیچ دستورالعملی تولید نمیشود). متغیرهای پشتیبانیشده: line (شمارهٔ خط) و file (نام فایل).
- code:abort
- عبارتی که اجرای برنامه را متوقف (abort) میکند.
- code:yydebug
- عبارت YYDEBUG، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYDEBUG، yyrecord، yych (که با پیکربندیهای متناظر re2c: مطابقت دارند)، state (شمارهٔ وضعیت DFA).
- code:yypeek
- عبارت YYPEEK، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYPEEK، YYCTYPE، YYINPUT، YYCURSOR، yyrecord، yych (که با پیکربندیهای متناظر re2c: مطابقت دارند). شرطها: .cast (درست درصورتیکه re2c:yych:conversion روی مقداری غیر از صفر تنظیم شده باشد).
- code:yyskip
- عبارت YYSKIP، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSKIP، YYCURSOR، yyrecord (که با پیکربندیهای متناظر re2c: مطابقت دارند).
- code:yybackup
- عبارت YYBACKUP، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYBACKUP، YYCURSOR، YYMARKER، yyrecord (که با پیکربندیهای متناظر re2c: مطابقت دارند).
- code:yybackupctx
- عبارت YYBACKUPCTX، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYBACKUPCTX، YYCURSOR، YYCTXMARKER، yyrecord (که با پیکربندیهای متناظر re2c: مطابقت دارند).
- code:yyskip_yypeek
- عبارت ترکیبی code:yyskip و code:yypeek (بهطور پیشفرض code:yyskip و بهدنبال آن code:yypeek).
- code:yypeek_yyskip
- عبارت ترکیبی code:yypeek و code:yyskip (بهطور پیشفرض code:yypeek و بهدنبال آن code:yyskip).
- code:yyskip_yybackup
- عبارت ترکیبی code:yyskip و code:yybackup (بهطور پیشفرض code:yyskip و بهدنبال آن code:yybackup).
- code:yybackup_yyskip
- عبارت ترکیبی code:yybackup و code:yyskip (بهطور پیشفرض code:yybackup و بهدنبال آن code:yyskip).
- code:yybackup_yypeek
- عبارت ترکیبی code:yybackup و code:yypeek (بهطور پیشفرض code:yybackup و بهدنبال آن code:yypeek).
- code:yyskip_yybackup_yypeek
- عبارت ترکیبی code:yyskip، code:yybackup و code:yypeek (بهطور پیشفرض ``code:yyskip`` و بهدنبال آن code:yybackup و پس از آن code:yypeek).
- code:yybackup_yypeek_yyskip
- عبارت ترکیبی code:yybackup، code:yypeek و code:yyskip (بهطور پیشفرض ``code:yybackup`` و بهدنبال آن code:yypeek و پس از آن code:yyskip).
- code:yyrestore
- عبارت YYRESTORE، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYRESTORE، YYCURSOR، YYMARKER، yyrecord (که با پیکربندیهای متناظر re2c: مطابقت دارند).
- code:yyrestorectx
- عبارت YYRESTORECTX، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYRESTORECTX، YYCURSOR، YYCTXMARKER، yyrecord (که با پیکربندیهای متناظر re2c: مطابقت دارند).
- code:yyrestoretag
- عبارت YYRESTORETAG، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYRESTORETAG، YYCURSOR، yyrecord (که با پیکربندیهای متناظر re2c: مطابقت دارند)، tag (نام متغیر برچسب که برای بازیابی موقعیت استفاده میشود).
- code:yyshift
- عبارت YYSHIFT، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSHIFT، YYCURSOR، yyrecord (که با پیکربندیهای متناظر re2c: مطابقت دارند)، offset (تعداد واحدهای کد برای تغییر مکان موقعیت فعلی).
- code:yyshiftstag
- عبارت YYSHIFTSTAG، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSHIFTSTAG، yyrecord، negative (که با پیکربندیهای متناظر re2c: مطابقت دارند)، tag (متغیر برچسب که باید تغییر مکان داده شود)، offset (تعداد واحدهای کد برای تغییر مکان). شرطها: .nested (درست درصورتیکه این یک برچسب تودرتو باشد -- در این حالت مقدار آن ممکن است برابر با re2c:tags:negative باشد که نباید تغییر مکان یابد).
- code:yyshiftmtag
- عبارت YYSHIFTMTAG، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSHIFTMTAG (که با پیکربندی متناظر re2c: مطابقت دارد)، tag (متغیر برچسب که باید تغییر مکان یابد)، offset (تعداد واحدهای کد برای تغییر مکان).
- code:yystagp
- عبارت YYSTAGP، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSTAGP، YYCURSOR، yyrecord (که با پیکربندیهای متناظر re2c: مطابقت دارند)، tag (متغیر برچسب که باید بهروزرسانی شود).
- code:yymtagp
- عبارت YYMTAGP، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYMTAGP (که با پیکربندی متناظر re2c: مطابقت دارد)، tag (متغیر برچسب که باید بهروزرسانی شود).
- code:yystagn
- عبارت YYSTAGN، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSTAGN، negative، yyrecord (که با پیکربندیهای متناظر re2c: مطابقت دارند)، tag (متغیر برچسب که باید بهروزرسانی شود).
- code:yymtagn
- عبارت YYMTAGN، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYMTAGN (که با پیکربندی متناظر re2c: مطابقت دارد)، tag (متغیر برچسب که باید بهروزرسانی شود).
- code:yycopystag
- عبارت YYCOPYSTAG، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYCOPYSTAG، yyrecord (که با پیکربندیهای متناظر re2c: مطابقت دارند)، lhs، rhs (متغیرهای برچسب سمت چپ و راستِ عملیات کپی).
- code:yycopymtag
- عبارت YYCOPYMTAG، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYCOPYMTAG، yyrecord (که با پیکربندیهای متناظر re2c: مطابقت دارند)، lhs، rhs (متغیرهای برچسب سمت چپ و راستِ عملیات کپی).
- code:yygetaccept
- عبارت YYGETACCEPT، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYGETACCEPT، yyrecord (که با پیکربندیهای متناظر re2c: مطابقت دارند)، var (که با پیکربندی re2c:yyaccept مطابقت دارد).
- code:yysetaccept
- عبارت YYSETACCEPT، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSETACCEPT، yyrecord (که با پیکربندیهای متناظر re2c: مطابقت دارند)، var (که با پیکربندی re2c:yyaccept مطابقت دارد) و val (مقدار عددی قاعدهٔ پذیرفتهشده).
- code:yygetcond
- عبارت YYGETCOND، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYGETCOND، yyrecord (که با پیکربندیهای متناظر re2c: مطابقت دارند)، var (که با پیکربندی re2c:yycond مطابقت دارد).
- code:yysetcond
- عبارت YYSETCOND، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSETCOND، yyrecord (که با پیکربندیهای متناظر re2c: مطابقت دارند)، var (که با پیکربندی re2c:yycond مطابقت دارد) و val (شناسهٔ عددی شرط).
- code:yygetstate
- عبارت YYGETSTATE، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYGETSTATE، yyrecord (که با پیکربندیهای متناظر re2c: مطابقت دارند)، var (که با پیکربندی re2c:yystate مطابقت دارد).
- code:yysetstate
- عبارت YYSETSTATE، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSETSTATE، yyrecord (که با پیکربندیهای متناظر re2c: مطابقت دارند)، var (که با پیکربندی re2c:yystate مطابقت دارد) و val (شمارهٔ وضعیت).
- code:yylessthan
- عبارت YYLESSTHAN، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYLESSTHAN، YYCURSOR، YYLIMIT، yyrecord (که با پیکربندیهای متناظر re2c: مطابقت دارند)، need (تعداد واحدهای کد برای بررسی در برابر آن). شرط: .many (درست درصورتیکه need بیش از یک باشد)..TP code:yyend عبارت YYEND، احتمالاً سفارشیشده برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYEND، YYCURSOR، YYLIMIT.
- code:yybm_filter
- شرطی که برای فیلتر کردن مقادیر yych که توسط جدول yybm پوشش داده نشدهاند استفاده میشود (همراه با گزینهٔ --bitmaps به کار میرود). متغیر پشتیبانیشده: yych (به پیکربندی re2c:yych نگاشت میشود).
- code:yybm_match
- قالب بررسی جدول yybm (تولیدشده با گزینهٔ --bitmaps). متغیرهای پشتیبانیشده: yybm، yych (به پیکربندیهای متناظر re2c: نگاشت میشوند)، offset (آفست در جدول yybm که باید به yych افزوده شود) و mask (ماسک بیتی که باید روی مدخل جدول اعمال شود تا مقدار بولی مورد نیاز برای بررسی به دست آید).
- code:yytarget_filter
- شرطی که برای فیلتر کردن مقادیر yych که توسط جدول yytarget پوشش داده نشدهاند استفاده میشود (همراه با گزینهٔ --computed-gotos به کار میرود). متغیر پشتیبانیشده: yych (به پیکربندی re2c:yych نگاشت میشود).
در اینجا فهرستی از تمام متغیرهای سراسری مجاز در فایلهای نحو آمده است:
- nl
- یک خط جدید (newline).
- indent
- متغیری که هیچ کدی تولید نمیکند، اما دارای اثر جانبی افزایش سطح دندانهگذاری است.
- dedent
- متغیری که هیچ کدی تولید نمیکند، اما دارای اثر جانبی کاهش سطح دندانهگذاری است.
- topindent
- رشتهٔ دندانهگذاری برای عبارت فعلی. سطح دندانهگذاری توسط تولیدکنندهٔ کد ردیابی شده و بهطور خودکار بهروزرسانی میشود.
در اینجا فهرستی از تمام شرطهای سراسری مجاز در فایلهای نحو آمده است:
- .api.simple
- True است اگر از API ساده استفاده شود (--api simple یا re2c:api = simple).
- .api.generic
- True است اگر از API عمومی استفاده شود (--api generic یا re2c:api = generic).
- .api.record
- True است اگر از API رکورد استفاده شود (--api record یا re2c:api = record).
- .api_style.functions
- True است اگر سبک API تابعگونه استفاده شود (re2c:api-style = functions).
- .api_style.freeform
- True است اگر سبک API آزاد استفاده شود (re2c:api-style = free-form).
- .case_ranges
- True است اگر قابلیت بازههای case فعال باشد (--case-ranges یا re2c:case-ranges = 1).
- .cgoto.relative
- True است اگر حالت نسبی دستور goto محاسباتی استفاده شود (--computed-gotos-relative یا re2c:cgoto:relative = 1).
- .code_model.goto_label
- True است اگر مدل کد مبتنی بر goto/label استفاده شود (--goto-label).
- .code_model.loop_switch
- True است اگر مدل کد مبتنی بر loop/switch استفاده شود (--loop-switch).
- .code_model.recursive_functions
- True است اگر مدل کد مبتنی بر توابع بازگشتی استفاده شود (--recursive-function).
- .date
- True است اگر اثرانگشت (fingerprint) تولیدشده باید حاوی تاریخ تولید باشد.
- .loop_label
- True است اگر حلقههای تولیدشده توسط re2c باید دارای برچسب باشند (re2c:label:yyloop روی یک رشتهٔ غیرخالی تنظیم شده باشد).
- .monadic
- True است اگر کد تولیدشده باید بهصورت مونادیک باشد (re2c:monadic = 1). این مورد تنها برای زبانهای تابعی محض کاربرد دارد.
- .start_conditions
- True است اگر شرایط شروع فعال باشند (--start-conditions).
- .storable_state
- True است اگر وضعیت قابل ذخیرهسازی فعال باشد (--storable-state).
- .unsafe
- True است اگر re2c برای تولید کد سریعتر باید از بلوکهای "unsafe" استفاده کند (--unsafe، re2c:unsafe = 1). این مورد تنها برای زبانهایی مرتبط است که دارای قابلیت "unsafe" هستند.
- .version
- True است اگر اثرانگشت تولیدشده باید حاوی نسخهٔ re2c باشد.
- .yyfill.enable
- True است اگر YYFILL فعال باشد (re2c:yyfill:enable = 1).
- .yyfn.throw
- True است اگر پیکربندی re2c:yyfn:throw روی یک رشتهٔ غیرخالی تعریف شده باشد.
یکی از چالشهای اصلی تحلیلگر لغوی (lexer)، دانستن زمان توقف است. چندین شرط پایاندهنده وجود دارد:
- تحلیلگر لغوی ممکن است با قاعدهای (شامل قاعده پیشفرض *) تطابق یابد و به یک وضعیت نهایی برسد
- تحلیلگر لغوی ممکن است در تطبیق با هر قاعدهای شکست بخورد و به یک وضعیت پیشفرض برسد
- تحلیلگر لغوی ممکن است به پایان ورودی برسد
دو شرط نخست تحلیلگر لغوی را به روشی "طبیعی" خاتمه میدهند: تحلیلگر به وضعیتی بدون هیچ انتقال خروجی میرسد و تطبیق به طور خودکار متوقف میشود. اما شرط سوم، یعنی پایان ورودی، متفاوت است: این حالت ممکن است در هر وضعیتی رخ دهد و تحلیلگر لغوی باید قادر به مدیریت آن باشد. بررسی پایان ورودی، روند کار عادی تحلیلگر لغوی را مختل کرده و شاخههای شرطی را به برنامه تولیدشده میافزاید؛ بنابراین به حداقل رساندن تعداد این بررسیها ضروری است. ابزار re2c از چندین روش مختلف برای مدیریت پایان ورودی پشتیبانی میکند. انتخاب روش مناسب به پیچیدگی عبارات باقاعده، نیاز به بافرسازی، ملاحظات کارایی و عوامل دیگر بستگی دارد. در ادامه فهرستی از این روشها آورده شده است:
- نگهبان (Sentinel). این روش نیاز به بررسیهای پایان ورودی را به طور کامل برطرف میکند. این روش ساده و کارآمد است، اما محدود به مواردی است که یک نویسه "نگهبان" طبیعی وجود داشته باشد که هرگز در ورودی معتبر رخ ندهد. این نویسه همچنان ممکن است در ورودی نامعتبر ظاهر شود، اما نباید در عبارات باقاعده مجاز باشد، مگر شاید به عنوان آخرین نویسه یک قاعده. نگهبان به انتهای ورودی ضمیمه میشود و به عنوان سیگنال توقف عمل میکند: زمانی که تحلیلگر لغوی این نویسه را بخواند، یا خطای نحوی رخ داده یا به پایان ورودی رسیده است. در هر دو حالت، تحلیلگر باید متوقف شود. این روش زمانی استفاده میشود که YYFILL با re2c:yyfill:enable = 0; غیرفعال شده باشد و re2c:eof مقدار پیشفرض -1 را داشته باشد.
- نگهبان با بررسی مرزها (Sentinel with bounds checks). این روش عمومی (generic) است: امکان مدیریت هر ورودی را بدون محدودیت روی عبارات باقاعده فراهم میکند. ایده این روش، کاهش تعداد بررسیهای پایان ورودی از طریق انجام آنها تنها روی نویسههایی خاص است. مشابه روش "نگهبان"، یکی از نویسهها به عنوان "نگهبان" انتخاب شده و به انتهای ورودی ضمیمه میشود. با این حال، هیچ محدودیتی در مورد محل وقوع نگهبان وجود ندارد (در واقع، هر نویسهای را میتوان برای نگهبان انتخاب کرد). هنگامی که تحلیلگر لغوی این نویسه را میخواند، علاوه بر آن بررسی مرز را نیز انجام میدهد. اگر موقعیت فعلی در محدوده مجاز باشد، تحلیلگر تطبیق را از سر گرفته و با نگهبان مانند یک نویسه معمولی رفتار میکند. در غیر این صورت، تابع YYFILL را فراخوانی میکند (مگر اینکه غیرفعال شده باشد). اگر ورودی بیشتری ارائه شود، تحلیلگر لغوی نویسه آخر را مجدداً تطبیق داده و چنان ادامه میدهد که گویی نویسه نگهبان اصلاً وجود نداشته است. در غیر این صورت باید پایان واقعی ورودی باشد و تحلیلگر متوقف میشود. این روش زمانی به کار میرود که re2c:eof مقداری غیرمنفی داشته باشد (باید روی مقدار عددی نویسه نگهبان تنظیم شود). استفاده از YYFILL اختیاری است.
- بررسی مرزها همراه با پدینگ (Bounds checks with padding). این روش عمومی است، و ممکن است سریعتر از روش "نگهبان با بررسی مرزها" باشد، اما پیچیدهتر نیز هست. ایده این است که وضعیتهای DFA به مؤلفههای قویاً همبند (SCCs) افراز شوند و به ازای هر SCC یک بررسی منفرد برای نویسههایی به تعداد کافی جهت پوشش طولانیترین مسیر بدون حلقه در آن SCC ایجاد گردد. این کار تعداد بررسیها را کاهش میدهد، اما مشکلی در رابطه با لکسمهای (lexemes) کوتاه در پایان ورودی به وجود میآورد، زیرا بررسی نیازمند نویسههای کافی برای پوشش طولانیترین لکسم است. این مشکل را میتوان با پد کردن (تکمیل) ورودی با چند نویسه ساختگی که پسوند لکسم معتبری تشکیل ندهند (تا تحلیلگر نتواند آنها را تطبیق دهد) برطرف کرد. طول این پدینگ باید YYMAXFILL باشد که با یک بلوک max تولید میشود. اگر ورودی کافی نباشد، تحلیلگر لغوی YYFILL را فراخوانی میکند که باید حداقل تعداد نویسههای مورد نیاز را تأمین کند یا بازگشتی نداشته باشد. این روش زمانی استفاده میشود که YYFILL فعال باشد و re2c:eof برابر -1 باشد (این پیکربندی پیشفرض است).
- بررسیهای سفارشی (Custom checks). رابط عمومی (Generic API) امکان بازتعریف عملیات پایهای مانند خواندن یک نویسه را فراهم میکند، که گنجاندن بررسیهای پایان ورودی را به عنوان بخشی از آنها ممکن میسازد. این رویکرد مستعد خطاست و باید با احتیاط استفاده شود. برای استفاده از یک روش سفارشی، Generic API را با --api custom یا re2c:api = custom; فعال کرده و بررسیهای پیشفرض مرزها را با re2c:yyfill:enable = 0; یا re2c:yyfill:check = 0; غیرفعال کنید.
زیربخشهای بعدی شامل نمونهای برای هر یک از روشها هستند..SS نگهبان (Sentinel)
این مثال از یک کاراکتر نگهبان (sentinel) برای مدیریت پایان ورودی استفاده میکند. این برنامه کلمات جداشده با فاصله را در یک رشتهٔ پایانیافته با null شمارش میکند. کاراکتر نگهبان همان null است: آخرین کاراکتر هر رشتهٔ ورودی است و توسط هیچیک از قواعد در میانهٔ یک نشانه (lexeme) مجاز نیست (بهویژه، در بازههای کاراکتری که ممکن است نادیده گرفته شوند، گنجانده نشده است). اگر یک null در میانهٔ یک رشته رخ دهد، یک خطای نحو (syntax error) محسوب شده و تحلیلگر لغوی قاعدهٔ پیشفرض * را مطابقت خواهد داد، اما فراتر از پایان ورودی نخواهد خواند و متوقف (crash) نخواهد شد (برای بررسی این موضوع از هشدار -Wsentinel-in-midrule و پیکربندی re2c:sentinel استفاده کنید). پیکربندی re2c:yyfill:enable = 0; از تولید بررسیهای مرزی و فراخوانیهای YYFILL جلوگیری میکند.
// re2c $INPUT -o $OUTPUT
#include <assert.h>
// Expect a null-terminated string.
static int lex(const char *YYCURSOR) {
int count = 0;
for (;;) {
/*!re2c
re2c:define:YYCTYPE = "unsigned char";
re2c:yyfill:enable = 0;
* { return -1; }
[\x00] { return count; }
[a-z]+ { ++count; continue; }
[ ]+ { continue; }
*/
}
}
int main() {
assert(lex("") == 0);
assert(lex("one two three") == 3);
assert(lex("f0ur") == -1);
return 0;
}
نگهبان به همراه بررسیهای مرزی (Sentinel with bounds checks)
این مثال از روش نگهبان به همراه بررسیهای مرزی (bounds checks) برای مدیریت پایان ورودی استفاده میکند (این روش در نسخهٔ 1.2 اضافه شد). این برنامه رشتههای محصور در علامت نقلقول تکی (single-quoted) جداشده با فاصله را شمارش میکند. کاراکتر نگهبان null است که با پیکربندی re2c:eof = 0; مشخص میشود. همانند روش sentinel، کاراکتر null آخرین کاراکتر هر رشتهٔ ورودی است، اما در میانهٔ یک قاعده مجاز شمرده میشود (برای نمونه، 'aaa\0aa'\0 یک ورودی معتبر است، اما 'aaa\0 یک خطای نحو است). بررسیهای مرزی در هر وضعیتی که با یک کاراکتر ورودی مطابقت دارد تولید میشوند، اما دامنهٔ آنها به شاخهای محدود است که null را مدیریت میکند. بررسیهای مرزی به فرم YYLIMIT <= YYCURSOR یا YYLESSTHAN(1) با API عمومی (generic API) هستند. اگر شرط بررسی برقرار باشد، تحلیلگر لغوی به پایان ورودی رسیده است و باید متوقف شود (YYFILL با re2c:yyfill:enable = 0; غیرفعال شده است زیرا ورودی درون یک بافر جا میگیرد؛ برای مثالی که از YYFILL استفاده میکند بخش YYFILL with sentinel را ببینید). رسیدن به پایان ورودی سه احتمال ایجاد میکند: اگر تحلیلگر لغوی در وضعیت اولیه باشد با قاعدهٔ پایان ورودی $ مطابقت پیدا خواهد کرد، در غیر این صورت ممکن است به قاعدهای که قبلاً مطابقت یافته (از جمله قاعدهٔ پیشفرض *) بازگردد یا به یک وضعیت پیشفرض برود که منجر به هشدار -Wundefined-control-flow میشود.
// re2c $INPUT -o $OUTPUT
#include <assert.h>
// Expect a null-terminated string.
static int lex(const char *str, unsigned int len) {
const char *YYCURSOR = str, *YYLIMIT = str + len, *YYMARKER;
int count = 0;
for (;;) {
/*!re2c
re2c:define:YYCTYPE = "unsigned char";
re2c:yyfill:enable = 0;
re2c:eof = 0;
str = ['] ([^'\\] | [\\][^])* ['];
* { return -1; }
$ { return count; }
str { ++count; continue; }
[ ]+ { continue; }
*/
}
}
#define TEST(s, r) assert(lex(s, sizeof(s) - 1) == r)
int main() {
TEST("", 0);
TEST("'qu\0tes' 'are' 'fine: \\'' ", 3);
TEST("'unterminated\\'", -1);
return 0;
}
این مثال از بررسیهای محدوده با پدینگ برای مدیریت انتهای ورودی استفاده میکند (این روش بهطور پیشفرض فعال است). این برنامه تعداد رشتههای تکنقلقولیِ جداشده با فاصله را میشمارد. پدینگی شامل YYMAXFILL نویسهٔ null به انتهای ورودی الحاق شده است، که مقدار YYMAXFILL بهطور خودکار توسط یک بلوک max تولید میشود. استفاده از null برای پدینگ الزامی نیست --- از هر نویسهای میتوان استفاده کرد تا زمانی که یک پسوند معتبر برای نشانه (lexeme) تشکیل ندهد (در این مثال پدینگ نباید شامل تکنقلقول باشد، زیرا ممکن است با پسوند یک رشتهٔ تکنقلقولی اشتباه گرفته شود). یک قاعدهٔ "stop" وجود دارد که با نخستین نویسهٔ پدینگ (null) تطابق یافته و تحلیلگر لغوی را خاتمه میدهد (توجه داشته باشید که بررسی میکند آیا null در ابتدای پدینگ قرار دارد یا خیر، در غیر این صورت یک خطای نحوی است). بررسیهای محدوده تنها در حالتهایی تولید میشوند که توسط مؤلفههای قویاً همبندِ اتوماتون زیرین تعیین شده باشند. این بررسیها به شکل (YYLIMIT - YYCURSOR) < n یا با API عمومی بهصورت YYLESSTHAN(n) هستند، که در آن n حداقل تعداد نویسههای مورد نیاز برای ادامهٔ کار تحلیلگر لغوی است (همچنین به این معنی است که بررسی بعدی محدوده حداکثر در n نویسهٔ آینده رخ خواهد داد). اگر شرط بررسی درست باشد، تحلیلگر لغوی به انتهای ورودی رسیده است و YYFILL(n) را فراخوانی میکند که باید یا حداقل n نویسهٔ ورودی را فراهم سازد یا بازگشتی نداشته باشد. در این مثال YYFILL همیشه با شکست مواجه میشود و تحلیلگر لغوی را با یک خطا پایان میدهد (که مشکلی ایجاد نمیکند زیرا تمام ورودی در یک بافر جای میگیرد). برای مشاهدهٔ مثالی که بافر ورودی را با استفاده از YYFILL بازپر میکند، بخش YYFILL with padding را ببینید.
// re2c $INPUT -o $OUTPUT
#include <assert.h>
#include <stdlib.h>
#include <string.h>
/*!max:re2c*/
static int lex(const char *str, unsigned int len) {
// Make a copy of the string with YYMAXFILL zeroes at the end.
char *buf = (char*) malloc(len + YYMAXFILL);
memcpy(buf, str, len);
memset(buf + len, 0, YYMAXFILL);
const char *YYCURSOR = buf, *YYLIMIT = buf + len + YYMAXFILL;
int count = 0;
loop:
/*!re2c
re2c:api:style = free-form;
re2c:define:YYCTYPE = "unsigned char";
re2c:define:YYFILL = "goto fail;";
str = ['] ([^'\\] | [\\][^])* ['];
[\x00] {
// Check that it is the sentinel, not some unexpected null.
if (YYCURSOR - 1 == buf + len) goto exit; else goto fail;
}
str { ++count; goto loop; }
[ ]+ { goto loop; }
* { goto fail; }
*/
fail:
count = -1;
exit:
free(buf);
return count;
}
#define TEST(s, r) assert(lex(s, sizeof(s) - 1) == r)
int main() {
TEST("", 0);
TEST("'qu\0tes' 'are' 'fine: \\'' ", 3);
TEST("'unterminated\\'", -1);
TEST("'unexpected \0 null\\'", -1);
return 0;
}
این مثال از یک روش سفارشی برای مدیریت پایان ورودی بر پایه generic API استفاده میکند. این برنامه رشتههای محصور در نقلقول تکیِ جداشده با فاصله را شمارش میکند. این مثال مانند مثال sentinel است، بهجز اینکه ورودی با null خاتمه نیافته است. برای جبران عدم وجود نویسهٔ نگهبان در انتهای ورودی، YYPEEK بازتعریف شده است تا پیش از خواندن نویسهٔ ورودی بعدی، یک بررسی محدوده انجام دهد. این روش ناکارآمد است زیرا بررسیها به دفعات بسیار زیاد انجام میشوند. اگر شرط بررسی شکست بخورد، YYPEEK نویسهٔ واقعی را برمیگرداند، در غیر این صورت یک نویسهٔ نگهبان ساختگی بازمیگرداند.
// re2c $INPUT -o $OUTPUT
#include <assert.h>
#include <stdlib.h>
#include <string.h>
static int lex(const char *str, unsigned int len) {
// For the sake of example create a string without terminating null.
char *buf = (char*) malloc(len);
memcpy(buf, str, len);
const char *cur = buf, *lim = buf + len;
int count = 0;
for (;;) {
/*!re2c
re2c:yyfill:enable = 0;
re2c:api = custom;
re2c:api:style = free-form;
re2c:define:YYCTYPE = "unsigned char";
re2c:define:YYPEEK = "cur < lim ? *cur : 0"; // fake null
re2c:define:YYSKIP = "++cur;";
* { count = -1; break; }
[\x00] { break;; }
[a-z]+ { ++count; continue;; }
[ ]+ { continue; }
*/
}
free(buf);
return count;
}
#define TEST(s, r) assert(lex(s, sizeof(s) - 1) == r)
int main() {
TEST("", 0);
TEST("one two three ", 3);
TEST("f0ur", -1);
return 0;
}
بازپر کردن بافر (BUFFER REFILLING)
نیاز به بافرینگ زمانی رخ میدهد که نتوان تمام ورودی را بهیکباره در حافظه نگاشت کرد: یا ورودی بسیار بزرگ است، یا به صورت جریانی (مانند خواندن از یک سوکت) دریافت میشود. روش معمول در چنین مواردی تخصیص یک بافر حافظه با اندازهٔ ثابت و پردازش ورودی در تکههایی است که در بافر جا میشوند. هنگامی که تکهٔ فعلی پردازش شد، به بیرون منتقل شده و دادههای جدید وارد میشوند. در عمل این امر تا حدی پیچیدهتر است، زیرا وضعیت تحلیلگر واژگانی صرفاً از یک موقعیت ورودی تشکیل نشده، بلکه مجموعهای از موقعیتهای بههمپیوسته است:
- مکاننما (cursor): نویسهٔ ورودی بعدی برای خوانده شدن (YYCURSOR در C pointer API یا YYSKIP/YYPEEK در generic API)
- حد (limit): موقعیت پس از آخرین نویسهٔ ورودیِ در دسترس (YYLIMIT در C pointer API، که بهطور ضمنی توسط YYLESSTHAN در generic API مدیریت میشود)
- نشانهگذار (marker): موقعیت جدیدترین تطابق، در صورت وجود (YYMARKER در default API یا YYBACKUP/YYRESTORE در generic API)
- توکن (token): ابتدای جزء واژگانی (lexeme) فعلی (در re2c API ضمنی است، زیرا برای عملکرد عادی لکسر به آن نیازی نیست و میتواند توسط کاربر تعریف و بهروزرسانی شود)
- نشانهگذار زمینه (context marker): موقعیت زمینهٔ پسآیند (YYCTXMARKER در C pointer API یا YYBACKUPCTX/YYRESTORECTX در generic API)
- متغیرهای برچسب (tag variables): موقعیتهای زیرتطابق (تعریفشده با بلوکهای stags و mtags و دستورات اولیهٔ generic API یعنی YYSTAGP/YYSTAGN/YYMTAGP/YYMTAGN)
همهٔ اینها در هر حالتی استفاده نمیشوند، اما در صورت استفاده، باید توسط YYFILL بهروزرسانی شوند. تمام موقعیتهای فعال در بخشی میان token و cursor قرار دارند؛ بنابراین هر چیزی بین ابتدای بافر و token را میتوان دور ریخت، بخش بین token تا limit باید به ابتدای بافر منتقل شود، و فضای خالی در انتهای بافر باید با دادههای جدید پر شود. به منظور جلوگیری از فراخوانیهای مکرر YYFILL، بهتر است تا حد امکان نویسههای ورودی بیشتری پر شوند (حتی اگر نویسههای کمتری برای از سرگیری کار لکسر کافی باشند). جزئیات پیادهسازی YYFILL بسته به اینکه کدام روش مدیریت EOF استفاده میشود کمی متفاوت است: حالت قاعدهٔ EOF تا حدی سادهتر از حالت بررسی محدوده همراه با پدینگ (padding) است. همچنین توجه داشته باشید که اگر گزینهٔ -f --storable-state استفاده شود، YYFILL معناشناسی اندکی متفاوت خواهد داشت (که در بخش مربوط به وضعیت قابل ذخیره شرح داده شده است)..SS YYFILL همراه با نگهبان (sentinel)
اگر از قاعدهٔ EOF استفاده شود، YYFILL یک سازوکار پایهای تابعگونه است که هیچ آرگومانی نمیپذیرد و مقداری را بازمیگرداند که با صفر مقایسه میشود. فراخوانی YYFILL با شرط YYLIMIT <= YYCURSOR در API اشارهگر C و YYLESSTHAN() در API عمومی راهاندازی میشود. یک مقدار بازگشتی غیرصفر به این معنی است که YYFILL با شکست مواجه شده است. یک فراخوانی موفق YYFILL باید دستکم یک نویسه را تأمین کرده و موقعیتهای ورودی را بر همین اساس تنظیم کند. متغیر Limit باید همیشه روی یک خانه پس از آخرین موقعیت ورودی در بافر تنظیم شود، و نویسهٔ موجود در موقعیت limit باید نماد نگهبان مشخصشده با پیکربندی re2c:eof باشد. تصاویر زیر موقعیتهای نسبی ورودی در بافر را پیش و پس از فراخوانی YYFILL نشان میدهند (نماد نگهبان با # مشخص شده است، و تصویر دوم حالتی را نشان میدهد که ورودی کافی برای پر کردن کل بافر وجود ندارد).
<-- shift -->
>-A------------B---------C-------------D#-----------E->
buffer token marker limit,
cursor
>-A------------B---------C-------------D------------E#->
buffer, marker cursor limit
token
<-- shift -->
>-A------------B---------C-------------D#--E (EOF)
buffer token marker limit,
cursor
>-A------------B---------C-------------D---E#........
buffer, marker cursor limit
token
در ادامه مثالی از برنامهای آمده است که فایل ورودی input.txt را در تکههای ۴۰۹۶ بایتی میخواند و از قاعدهٔ EOF استفاده میکند.
// re2c $INPUT -o $OUTPUT
#include <assert.h>
#include <stdio.h>
#include <string.h>
#define BUFSIZE 4095
typedef struct {
FILE *file;
char buf[BUFSIZE + 1], *lim, *cur, *mar, *tok; // +1 for sentinel
int eof;
} Input;
static int fill(Input *in) {
if (in->eof) return 1;
const size_t shift = in->tok - in->buf;
const size_t used = in->lim - in->tok;
// Error: lexeme too long. In real life could reallocate a larger buffer.
if (shift < 1) return 2;
// Shift buffer contents (discard everything up to the current token).
memmove(in->buf, in->tok, used);
in->lim -= shift;
in->cur -= shift;
in->mar -= shift;
in->tok -= shift;
// Fill free space at the end of buffer with new data from file.
in->lim += fread(in->lim, 1, BUFSIZE - used, in->file);
in->lim[0] = 0;
in->eof = in->lim < in->buf + BUFSIZE;
return 0;
}
static int lex(Input *in) {
int count = 0;
loop:
in->tok = in->cur;
/*!re2c
re2c:api:style = free-form;
re2c:define:YYCTYPE = "unsigned char";
re2c:define:YYCURSOR = in->cur;
re2c:define:YYMARKER = in->mar;
re2c:define:YYLIMIT = in->lim;
re2c:define:YYFILL = "fill(in) == 0";
re2c:eof = 0;
str = ['] ([^'\\] | [\\][^])* ['];
* { return -1; }
$ { return count; }
str { ++count; goto loop; }
[ ]+ { goto loop; }
*/
}
int main() {
const char *fname = "input";
const char content[] = "'qu\0tes' 'are' 'fine: \\'' ";
// Prepare input file: a few times the size of the buffer, containing
// strings with zeroes and escaped quotes.
FILE *f = fopen(fname, "w");
for (int i = 0; i < BUFSIZE; ++i) {
fwrite(content, 1, sizeof(content) - 1, f);
}
fclose(f);
int count = 3 * BUFSIZE; // number of quoted strings written to file
// Initialize lexer state: all pointers are at the end of buffer.
Input in;
in.file = fopen(fname, "r");
in.cur = in.mar = in.tok = in.lim = in.buf + BUFSIZE;
in.eof = 0;
// Sentinel (at YYLIMIT pointer) is set to zero, which triggers YYFILL.
in.lim[0] = 0;
// Run the lexer.
assert(lex(&in) == count);
// Cleanup: remove input file.
fclose(in.file);
remove(fname);
return 0;
}
در حالت پیشفرض (زمانی که قاعدهٔ EOF استفاده نمیشود)، YYFILL یک دستور اولیهٔ (primitive) تابعمانند است که یک آرگومان میپذیرد و هیچ مقداری بازنمیگرداند. فراخوانی YYFILL توسط شرط (YYLIMIT - YYCURSOR) < n در API اشارهگر C و YYLESSTHAN(n) در API عمومی تحریک میشود. آرگومان فرستادهشده به YYFILL، کمینهٔ تعداد نویسههایی است که باید تأمین شود. اگر در انجام این کار ناموفق باشد، YYFILL نباید کنترل را به تحلیلگر لغوی (lexer) بازگرداند (به همین دلیل بهتر است به صورت ماکرویی پیادهسازی شود که در صورت شکست، از تابع فراخواننده خارج شود). در صورت فراخوانی موفقیتآمیز YYFILL، موقعیت حد (limit) باید یا روی یکی پس از آخرین موقعیت ورودی در بافر تنظیم شود، یا روی انتهای لایهگذاری YYMAXFILL (در حالتی که YYFILL دستکم n نویسه را با موفقیت خوانده باشد، اما این مقدار برای پر کردن کل بافر کافی نباشد). تصاویر زیر موقعیتهای نسبی مکانهای ورودی در بافر را قبل و بعد از فراخوانی YYFILL نشان میدهند (لایهگذاری YYMAXFILL در تصویر دوم با نمادهای # مشخص شده است).
<-- shift --> <-- need -->
>-A------------B---------C-----D-------E---F--------G->
buffer token marker cursor limit
>-A------------B---------C-----D-------E---F--------G->
buffer, marker cursor limit
token
<-- shift --> <-- need -->
>-A------------B---------C-----D-------E-F (EOF)
buffer token marker cursor limit
>-A------------B---------C-----D-------E-F###############
buffer, marker cursor limit
token <- YYMAXFILL ->
در ادامه نمونهای از برنامهای آورده شده است که فایل ورودی input.txt را در تکههای ۴۰۹۶ بایتی میخواند و از بررسی محدودهها (bounds-checking) به همراه لایهگذاری استفاده میکند.
// re2c $INPUT -o $OUTPUT
#include <assert.h>
#include <stdio.h>
#include <string.h>
/*!max:re2c*/
#define BUFSIZE (4096 - YYMAXFILL)
typedef struct {
FILE *file;
char buf[BUFSIZE + YYMAXFILL], *lim, *cur, *tok;
int eof;
} Input;
static int fill(Input *in, size_t need) {
if (in->eof) return 1;
const size_t shift = in->tok - in->buf;
const size_t used = in->lim - in->tok;
// Error: lexeme too long. In real life could reallocate a larger buffer.
if (shift < need) return 2;
// Shift buffer contents (discard everything up to the current token).
memmove(in->buf, in->tok, used);
in->lim -= shift;
in->cur -= shift;
in->tok -= shift;
// Fill free space at the end of buffer with new data from file.
in->lim += fread(in->lim, 1, BUFSIZE - used, in->file);
// If read less than expected, this is end of input => add zero padding
// so that the lexer can access characters at the end of buffer.
if (in->lim < in->buf + BUFSIZE) {
in->eof = 1;
memset(in->lim, 0, YYMAXFILL);
in->lim += YYMAXFILL;
}
return 0;
}
static int lex(Input *in) {
int count = 0;
loop:
in->tok = in->cur;
/*!re2c
re2c:api:style = free-form;
re2c:define:YYCTYPE = "unsigned char";
re2c:define:YYCURSOR = in->cur;
re2c:define:YYLIMIT = in->lim;
re2c:define:YYFILL = "if (fill(in, @@) != 0) return -1;";
str = ['] ([^'\\] | [\\][^])* ['];
[\x00] {
// Check that it is the sentinel, not some unexpected null.
return in->tok == in->lim - YYMAXFILL ? count : -1;
}
str { ++count; goto loop; }
[ ]+ { goto loop; }
* { return -1; }
*/
}
int main() {
const char *fname = "input";
const char content[] = "'qu\0tes' 'are' 'fine: \\'' ";
// Prepare input file: a few times the size of the buffer, containing
// strings with zeroes and escaped quotes.
FILE *f = fopen(fname, "w");
for (int i = 0; i < BUFSIZE; ++i) {
fwrite(content, 1, sizeof(content) - 1, f);
}
fclose(f);
int count = 3 * BUFSIZE; // number of quoted strings written to file
// Initialize lexer state: all pointers are at the end of buffer.
// This immediately triggers YYFILL, as the check `in->cur < in->lim` fails.
Input in;
in.file = fopen(fname, "r");
in.cur = in.tok = in.lim = in.buf + BUFSIZE;
in.eof = 0;
// Run the lexer.
assert(lex(&in) == count);
// Cleanup: remove input file.
fclose(in.file);
remove(fname);
return 0;
}
بلوکهای چندگانه (Multiple blocks)
گاهی داشتن چندین تحلیلگر لغوی (lexer) مرتبط با یکدیگر ضروری است (برای نمونه، اگر یک ماشین حالت سطحبالا وجود داشته باشد که بین حالتهای تحلیلگر جابهجا شود). این کار میتواند با استفاده از چندین بلوک متصل به هم re2c پیادهسازی شود. گزینه دیگر استفاده از start conditions است.
پیادهسازی اتصالات بین بلوکها به زبان مقصد بستگی دارد. در زبانهایی که دارای دستور goto هستند (مانند C/C++ و Go) میتوان تمام بلوکها را در یک تابع قرار داد، بهطوری که پیش از هر یک از آنها یک برچسب (label) قرار گیرد. انتقال از یک بلوک به بلوک دیگر با یک goto ساده انجام میشود. در زبانهایی که فاقد goto هستند (مانند Rust)، استفاده از یک حلقه به همراه switch روی متغیر حالت ضروری است، مشابه حلقه/switch متغیر yystate تولیدشده توسط re2c، یا اینکه هر بلوک درون یک تابع قرار گیرد و از فراخوانی توابع استفاده شود.
مثال زیر از چندین بلوک برای تجزیه اعداد دودویی (binary)، هشتهشتی (octal)، دهدهی (decimal) و شانزدهشانزدهی (hexadecimal) استفاده میکند. هر مبنا بلوک مختص به خود را دارد. بلوک اولیه مبنا را تعیین کرده و به بلوکهای دیگر هدایت میکند. پیکربندیهای مشترک در یک بلوک جداگانه در آغاز برنامه تعریف شدهاند؛ آنها توسط بلوکهای دیگر به ارث برده میشوند.
// re2c $INPUT -o $OUTPUT -i
#include <stdint.h>
#include <limits.h>
#include <assert.h>
static const uint64_t ERROR = UINT64_MAX;
#define CHECK(n) if (n > UINT32_MAX) return ERROR;
static uint64_t parse_u32(const char *s) {
const char *YYCURSOR = s, *YYMARKER;
uint64_t u = 0;
/*!re2c
re2c:yyfill:enable = 0;
re2c:define:YYCTYPE = "unsigned char";
end = "\x00";
'0b' / [01] { goto bin; }
"0" { goto oct; }
"" / [1-9] { goto dec; }
'0x' / [0-9a-fA-F] { goto hex; }
* { return ERROR; }
*/
bin:
/*!re2c
end { return u; }
[01] { u = u * 2 + (YYCURSOR[-1] - '0'); CHECK(u); goto bin; }
* { return ERROR; }
*/
oct:
/*!re2c
end { return u; }
[0-7] { u = u * 8 + (YYCURSOR[-1] - '0'); CHECK(u); goto oct; }
* { return ERROR; }
*/
dec:
/*!re2c
end { return u; }
[0-9] { u = u * 10 + (YYCURSOR[-1] - '0'); CHECK(u); goto dec; }
* { return ERROR; }
*/
hex:
/*!re2c
end { return u; }
[0-9] { u = u * 16 + (YYCURSOR[-1] - '0'); CHECK(u); goto hex; }
[a-f] { u = u * 16 + (YYCURSOR[-1] - 'a' + 10); CHECK(u); goto hex; }
[A-F] { u = u * 16 + (YYCURSOR[-1] - 'A' + 10); CHECK(u); goto hex; }
* { return ERROR; }
*/
}
int main() {
assert(parse_u32("") == ERROR);
assert(parse_u32("1234567890") == 1234567890);
assert(parse_u32("0b1101") == 13);
assert(parse_u32("0x7Fe") == 2046);
assert(parse_u32("0644") == 420);
assert(parse_u32("9999999999") == ERROR);
return 0;
}
شرطهای شروع با گزینهٔ --start-conditions فعال میشوند. آنها روشی برای کدگذاری چندین خودکارهٔ (automata) مرتبط با یکدیگر را درون یک بلوک re2c یکسان فراهم میکنند.
هر شرط متناظر با یک خودکارهٔ منفرد است و دارای یک نام منحصربهفردِ تعیینشده توسط کاربر و یک شمارهٔ داخلی یکتاست که توسط re2c تعریف میشود. این شمارهها برای جابهجایی میان شرطها به کار میروند: کد تولیدشده از تعاریف پایهایِ YYGETCOND و YYSETCOND برای دریافت شرط فعلی یا تنظیم آن بر روی شمارهٔ ارائهشده استفاده میکند. از بلوک conditions، گزینهٔ --header یا پیکربندی re2c:header برای تولید شناسههای عددی شرطها استفاده کنید. پیکربندی re2c:cond:enumprefix پیشوند شناسههای تولیدشده را مشخص میکند.
در حالت شرط (condition mode)، هر قاعده باید پیشوندی از فهرستی از نامهای شرطها داشته باشد که با کاما از هم جدا شده و درون قلابها (angle brackets) قرار گرفتهاند، یا از نویسهٔ عام <*> برای اشاره به تمام شرطها استفاده کند. نحو قواعد به شرح زیر گسترش مییابد:
- < condition-list > regular-expression code
- قاعدهای که در هر شرط موجود در condition-list ادغام میشود. این قاعده با regular-expression مطابقت مییابد و code مربوطه را اجرا میکند.
- < condition-list > regular-expression => condition code
- قاعدهای که در هر شرط موجود در condition-list ادغام میشود. این قاعده با regular-expression مطابقت یافته، شرط فعلی را به condition تنظیم میکند و code مربوطه را اجرا مینماید.
- < condition-list > regular-expression :=> condition
- قاعدهای که در هر شرط موجود در condition-list ادغام میشود. این قاعده با regular-expression مطابقت یافته و بلافاصله به condition انتقال مییابد (هیچ کنش معنایی وجود ندارد).
- < condition-list > !action code
- قاعدهای که code را به مکان تعریفشده توسط action در هر یک از شرطهای موجود در condition-list متصل میکند (برای انواع مختلف کنشها به بخش actions مراجعه کنید).
- <! condition-list > code
- قاعدهای که code را به ابتدای کنشهای معناییِ تمام قواعد برای هر شرط در condition-list اضافه میکند. این نحو منسوخ شده است و بهجای آن باید از کنش !pre_rule استفاده شود (دقیقاً همان عملکرد را دارد).
- < > code
- قاعدهای که یک شرط ورودی ویژه با شمارهٔ صفر و نام "0" ایجاد میکند که پیش از جهش به سایر شرطها، code را اجرا میکند. این نحو منسوخ شده است و بهجای آن باید از کنش !entry استفاده شود (کنترل دقیقتری فراهم میکند، زیرا کد میتواند بر مبنای هر شرط بهطور جداگانه تعیین شود، و میتوان مستقیماً بدون عبور از توزیعکنندهٔ شرط [condition dispatch] به ابتدای شرط جهش کرد).
- < > => condition code
- مشابه قاعدهٔ قبلی است، با این تفاوت که condition بعدی را تنظیم میکند.
- < > :=> condition
- مشابه قاعدهٔ قبلی است، با این تفاوت که هیچ کد مرتبطی ندارد و بلافاصله به condition جهش میکند.
کدی که re2c برای شرطها تولید میکند به این بستگی دارد که آیا re2c از رویکرد goto/label برای کدگذاری خودکارهها استفاده میکند یا از رویکرد loop/switch.
در زبانهایی که دارای دستور goto هستند (مانند C/C++ و Go)، شرطها بهطور طبیعی بهصورت بلوکهایی از کد با برچسبهایی به فرم yyc_<cond> پیادهسازی میشوند، که در آن cond نام شرط است (پیشوند برچسب را میتوان با re2c:cond:prefix تغییر داد). انتقال بین شرطها با استفاده از goto و برچسبهای شرط پیادهسازی میشود. پیش از تمام شرطها، re2c یک ساختار switch اولیه بر روی YYGETSTATE تولید میکند که به وضعیت شروعِ شرط فعلی جهش مینماید. قواعد میانبر :=> ساختار switch اولیه را دور میزنند و مستقیماً به شرط مشخصشده جهش میکنند (از re2c:cond:goto میتوان برای تغییر رفتار پیشفرض استفاده کرد). قواعد دارای کنشهای معنایی بهطور خودکار به شرط بعدی جهش نمیکنند؛ این کار باید توسط کد کنشِ تعریفشده توسط کاربر انجام شود.
در زبانهایی که دستور goto ندارند (مانند Rust)، re2c از متغیر yystate برای ذخیرهٔ شمارههای شرط مجدداً استفاده میکند. هر شرط یک شناسهٔ عددی برابر با شمارهٔ وضعیت شروع خود دریافت میکند، و switch میان شرطها تفاوتی با switch بین وضعیتهای DFA یک شرط واحد ندارد. در این حالت نیازی به switch اولیهٔ مجزا برای شرط نیست. (از آنجا که همین رویکرد برای پیادهسازی وضعیتهای قابلذخیره استفاده میشود، در صورت استفادهٔ همزمان از وضعیتهای قابلذخیره و شرطها، YYGETCOND/YYSETCOND زاید خواهند بود).
برنامهٔ زیر از شرطهای شروع برای تجزیهٔ اعداد در مبناهای دودویی (باینری)، هشتهشتی (اکتال)، دهدهی (دسیمال) و شانزدهشانزدهی (هگزادسیمال) استفاده میکند. یک بلوک واحد وجود دارد که هر مبنا در آن دارای شرط اختصاصی خود است و شرط اولیه به همهٔ آنها متصل است. متغیر تعریفشده توسط کاربر با نام cond شمارهٔ شرط فعلی را ذخیره میکند؛ این متغیر با شمارهٔ شرط اولیه که با بلوک conditions تولید شده مقداردهی اولیه میشود.
// re2c $INPUT -o $OUTPUT -ci
#include <stdint.h>
#include <limits.h>
#include <assert.h>
static const uint64_t ERROR = UINT64_MAX;
/*!conditions:re2c*/
static uint64_t parse_u32(const char *s) {
const char *YYCURSOR = s, *YYMARKER;
int c = yycinit;
uint64_t u = 0;
/*!re2c
re2c:api:style = free-form;
re2c:define:YYCTYPE = "unsigned char";
re2c:define:YYGETCONDITION = "c";
re2c:define:YYSETCONDITION = "c = @@;";
re2c:yyfill:enable = 0;
<*> * { return ERROR; }
<init> '0b' / [01] :=> bin
<init> "0" :=> oct
<init> "" / [1-9] :=> dec
<init> '0x' / [0-9a-fA-F] :=> hex
<bin, oct, dec, hex> "\x00" { return u; }
<bin> [01] { u = u * 2 + (YYCURSOR[-1] - '0'); goto yyc_bin; }
<oct> [0-7] { u = u * 8 + (YYCURSOR[-1] - '0'); goto yyc_oct; }
<dec> [0-9] { u = u * 10 + (YYCURSOR[-1] - '0'); goto yyc_dec; }
<hex> [0-9] { u = u * 16 + (YYCURSOR[-1] - '0'); goto yyc_hex; }
<hex> [a-f] { u = u * 16 + (YYCURSOR[-1] - 'a' + 10); goto yyc_hex; }
<hex> [A-F] { u = u * 16 + (YYCURSOR[-1] - 'A' + 10); goto yyc_hex; }
<!*> { if (u > UINT32_MAX) return ERROR; }
*/
}
int main() {
assert(parse_u32("") == ERROR);
assert(parse_u32("1234567890") == 1234567890);
assert(parse_u32("0b1101") == 13);
assert(parse_u32("0x7Fe") == 2046);
assert(parse_u32("0644") == 420);
assert(parse_u32("9999999999") == ERROR);
return 0;
}
با گزینهٔ --storable-state، ابزار re2c تحلیلگر لغویای (lexer) تولید میکند که میتواند وضعیت کنونی خود را ذخیره کرده، به فراخواننده بازگردد، و بعداً عملیات را دقیقاً از همانجایی که متوقف شده بود از سر بگیرد. حالت پیشفرض کارکرد در re2c یک مدل «کِششی» ("pull") است که در آن تحلیلگر لغوی هر زمان که نیاز داشته باشد ورودی بیشتری را «میکشد» (دریافت میکند). این حالت ممکن است در مواردی که ورودی به صورت تکهتکه در دسترس قرار میگیرد غیرقابل قبول باشد (برای نمونه، اگر تحلیلگر لغوی توسط تجزیهکننده (parser) فراخوانی شود، یا اگر برنامهٔ تحلیلگر لغوی از طریق پروتکل سوکت با برنامهٔ دیگری ارتباط برقرار کند که باید پیش از ارسال پیام بعدی، منتظر پاسخ از تحلیلگر لغوی بماند). قابلیت وضعیت قابل ذخیرهسازی دقیقاً برای چنین مواردی در نظر گرفته شده است: این ویژگی امکان تولید تحلیلگرهای لغوی با مدل «رانشی» ("push") را فراهم میسازد. هنگامی که تحلیلگر لغوی به ورودی بیشتری نیاز داشته باشد، وضعیت خود را ذخیره کرده و کنترل را به فراخواننده بازمیگرداند. سپس، وقتی ورودی بیشتری آماده شد، فراخواننده تحلیلگر لغوی را دقیقاً از همانجایی که متوقف شده بود به کار میاندازد. در مقایسه با مدل «کِششی»، چند تغییر ضروری است:
- تعریف دستورهای اولیهٔ YYSETSTATE() و YYGETSTATE(state).
- تعریف متغیرهای yych، yyaccept (در صورت استفاده) و state به عنوان بخشی از وضعیت پایدار تحلیلگر لغوی. متغیر state باید با مقدار -1 مقداردهی اولیه شود.
- دستور YYFILL باید به جای تلاش برای فراهم کردن ورودی بیشتر، به برنامهٔ بیرونی بازگردد. کد بازگشتی باید نشان دهد که تحلیلگر لغوی به ورودی بیشتری نیاز دارد.
- برنامهٔ بیرونی باید موقعیتهایی را که تحلیلگر لغوی به ورودی بیشتری نیاز دارد شناسایی کرده و واکنش مناسب نشان دهد.
- به صورت اختیاری، استفاده از بلوک getstate برای تولید سوییچ YYGETSTATE جدا از تحلیلگر لغوی اصلی. این مورد تنها برای زبانهایی کار میکند که از goto پشتیبانی میکنند (نه در حالت --loop-switch).
- استفاده از re2c:eof و روش sentinel with bounds checks برای مدیریت پایان ورودی. روش مبتنی بر لایهگذاری (padding) ممکن است کار نکند زیرا مشخص نیست چه زمانی باید لایهگذاری را اضافه کرد: پایان فعلی ورودی ممکن است پایان نهایی ورودی نباشد، و اضافه کردن زودهنگام لایهگذاری ممکن است نشانهٔ لغویِ (lexeme) حریصانهای را که بخشی از آن خوانده شده، ناقص قطع کند. علاوه بر این، با توجه به منطق سطحبالای برنامه، دریافت ورودی بیشتر ممکن است به پردازش نشانهٔ لغوی انتهای بافر وابسته باشد (که از پیش به خاطر شرایط پایان ورودی مسدود شده است).
در ادامه نمونهای از یک تحلیلگر لغوی مدل «رانشی» ("push") آمده است که خواندن بستهها از یک سوکت را شبیهسازی میکند. تحلیلگر لغوی حلقه میزند تا به پایان ورودی برسد و به تابع فراخواننده بازگردد. تابع فراخواننده با «ارسال» بستهٔ بعدی، ورودی بیشتری فراهم میکند و تحلیل لغوی را ادامه میدهد. این فرایند زمانی متوقف میشود که همهٔ بستهها ارسال شده باشند یا خطایی رخ دهد.
// re2c $INPUT -o $OUTPUT -f
#include <assert.h>
#include <stdio.h>
#include <string.h>
#define DEBUG 0
#define LOG(...) if (DEBUG) fprintf(stderr, __VA_ARGS__);
// Use a small buffer to cover the case when a lexeme doesn't fit.
// In real world use a larger buffer.
#define BUFSIZE 10
typedef struct {
FILE *file;
char buf[BUFSIZE + 1], *lim, *cur, *mar, *tok;
int state;
} State;
typedef enum {END, READY, WAITING, BAD_PACKET, BIG_PACKET} Status;
static Status fill(State *st) {
const size_t shift = st->tok - st->buf;
const size_t used = st->lim - st->tok;
const size_t free = BUFSIZE - used;
// Error: no space. In real life can reallocate a larger buffer.
if (free < 1) return BIG_PACKET;
// Shift buffer contents (discard already processed data).
memmove(st->buf, st->tok, used);
st->lim -= shift;
st->cur -= shift;
st->mar -= shift;
st->tok -= shift;
// Fill free space at the end of buffer with new data.
const size_t read = fread(st->lim, 1, free, st->file);
st->lim += read;
st->lim[0] = 0; // append sentinel symbol
return READY;
}
static Status lex(State *st, unsigned int *recv) {
char yych;
/*!getstate:re2c*/
for (;;) {
st->tok = st->cur;
/*!re2c
re2c:api:style = free-form;
re2c:define:YYCTYPE = "unsigned char";
re2c:define:YYCURSOR = "st->cur";
re2c:define:YYMARKER = "st->mar";
re2c:define:YYLIMIT = "st->lim";
re2c:define:YYGETSTATE = "st->state";
re2c:define:YYSETSTATE = "st->state = @@;";
re2c:define:YYFILL = "return WAITING;";
re2c:eof = 0;
packet = [a-z]+[;];
* { return BAD_PACKET; }
$ { return END; }
packet { *recv = *recv + 1; continue; }
*/
}
}
void test(const char **packets, Status expect) {
// Create a "socket" (open the same file for reading and writing).
const char *fname = "pipe";
FILE *fw = fopen(fname, "w");
FILE *fr = fopen(fname, "r");
setvbuf(fw, NULL, _IONBF, 0);
setvbuf(fr, NULL, _IONBF, 0);
// Initialize lexer state: `state` value is -1, all pointers are at the end
// of buffer.
State st;
st.file = fr;
st.cur = st.mar = st.tok = st.lim = st.buf + BUFSIZE;
// Sentinel (at YYLIMIT pointer) is set to zero, which triggers YYFILL.
st.lim[0] = 0;
st.state = -1;
// Main loop. The buffer contains incomplete data which appears packet by
// packet. When the lexer needs more input it saves its internal state and
// returns to the caller which should provide more input and resume lexing.
Status status;
unsigned int send = 0, recv = 0;
for (;;) {
status = lex(&st, &recv);
if (status == END) {
LOG("done: got %u packets\n", recv);
break;
} else if (status == WAITING) {
LOG("waiting...\n");
if (*packets) {
LOG("sent packet %u\n", send);
fprintf(fw, "%s", *packets++);
++send;
}
status = fill(&st);
LOG("queue: '%s'\n", st.buf);
if (status == BIG_PACKET) {
LOG("error: packet too big\n");
break;
}
assert(status == READY);
} else {
assert(status == BAD_PACKET);
LOG("error: ill-formed packet\n");
break;
}
}
// Check results.
assert(status == expect);
if (status == END) assert(recv == send);
// Cleanup: remove input file.
fclose(fw);
fclose(fr);
remove(fname);
}
int main() {
const char *packets1[] = {0};
const char *packets2[] = {"zero;", "one;", "two;", "three;", "four;", 0};
const char *packets3[] = {"zer0;", 0};
const char *packets4[] = {"looooooooooong;", 0};
test(packets1, END);
test(packets2, END);
test(packets3, BAD_PACKET);
test(packets4, BIG_PACKET);
return 0;
}
بلوکهای قابل استفاده مجدد در قالب /*!rules:re2c[:<name>] ... */ یا %{rules[:<name>] ... %} میتوانند به هر تعداد بار مورد استفادهٔ مجدد قرار گیرند و با سایر بلوکهای re2c ترکیب شوند. بخش <name> اختیاری است. یک بلوک rules میتواند در یک بلوک یا دستورالعمل use استفاده شود. کد مربوط به یک بلوک rules در هر نقطه از استفاده تولید میشود.
بلوکهای use با /*!use:re2c[:<name>] ... */ یا %{use[:<name>] ... %} تعریف میشوند. بخش <name> اختیاری است: اگر مشخص نشود، بلوک rules مرتبط جدیدترین بلوک تعریفشده خواهد بود (چه نامدار باشد و چه بینام). یک بلوک use میتواند تعاریف نامدار، پیکربندیها و قواعد مختص به خود را اضافه کند. یک مورد کاربرد مهم برای بلوکهای use، تحلیلگر لغوی است که از چندین کدگذاری ورودی پشتیبانی میکند: همان بلوک rules چندین بار با پیکربندیهای مختص هر کدگذاری (encoding-specific) مجدداً استفاده میشود (مثال زیر را ببینید).
دستورالعمل درونبلوکی use با ساختار !use:<name>; میتواند از داخل یک بلوک re2c استفاده شود. این دستور، بلوک ارجاعدادهشده با نام <name> را در بلوک فعلی ادغام میکند. اگر برخی از قواعد و پیکربندیهای ادغامشده با موارد تعریفشدهٔ پیشین همپوشانی داشته باشند، تداخلها به روش معمول برطرف میشوند: نخستین قاعده اولویت دارد، و جدیدترین پیکربندی موارد قبلی را بازنویسی میکند. یک استثنا، قواعد ویژهٔ *، $ و (در حالت شرطی) <!> هستند که برای آنها تعریف محلی درون بلوک، بر هر تعریف به ارثرسیدهای اولویت دارد. دستورالعمل use به شما اجازه میدهد بلوکهای مختلف re2c را با یکدیگر در یک بلوک ترکیب کنید (مثال زیر را ببینید).
بلوکهای نامدار و دستورالعمل use درونبلوکی در نسخهٔ 2.2 ابزار re2c اضافه شدند. از آن نسخه به بعد، بلوکهای قابل استفاده مجدد بهطور پیشفرض مجاز هستند (نیازی به گزینهٔ خاصی نیست). پیش از نسخهٔ 2.2، حالت استفادهٔ مجدد با گزینهٔ -r --reusable فعال میشد. پیش از نسخهٔ 1.2، بلوکهای قابل استفاده مجدد نمیتوانستند با بلوکهای معمولی ترکیب شوند.
مثالی از دستورالعمل !use (Example of a !use directive)
// re2c $INPUT -o $OUTPUT
#include <assert.h>
// This example shows how to combine reusable re2c blocks: two blocks
// ('colors' and 'fish') are merged into one. The 'salmon' rule occurs
// in both blocks; the 'fish' block takes priority because it is used
// earlier. Default rule * occurs in all three blocks; the local (not
// inherited) definition takes priority.
typedef enum { COLOR, FISH, DUNNO } What;
/*!rules:re2c:colors
* { assert(false); }
"red" | "salmon" | "magenta" { return COLOR; }
*/
/*!rules:re2c:fish
* { assert(false); }
"haddock" | "salmon" | "eel" { return FISH; }
*/
static What lex(const char *s) {
const char *YYCURSOR = s, *YYMARKER;
/*!re2c
re2c:yyfill:enable = 0;
re2c:define:YYCTYPE = "unsigned char";
!use:fish;
!use:colors;
* { return DUNNO; } // overrides inherited '*' rules
*/
}
int main() {
assert(lex("salmon") == FISH);
assert(lex("what?") == DUNNO);
return 0;
}
مثالی از بلوک /*!use:re2c ... */ (Example of a /*!use:re2c ... */ block)
// re2c $INPUT -o $OUTPUT --input-encoding utf8
#include <assert.h>
#include <stdint.h>
// This example supports multiple input encodings: UTF-8 and UTF-32.
// Both lexers are generated from the same rules block, and the use
// blocks add only encoding-specific configurations.
/*!rules:re2c
re2c:yyfill:enable = 0;
"∀x ∃y" { return 0; }
* { return 1; }
*/
static int lex_utf8(const uint8_t *s) {
const uint8_t *YYCURSOR = s, *YYMARKER;
/*!use:re2c
re2c:define:YYCTYPE = uint8_t;
re2c:encoding:utf8 = 1;
*/
}
static int lex_utf32(const uint32_t *s) {
const uint32_t *YYCURSOR = s, *YYMARKER;
/*!use:re2c
re2c:define:YYCTYPE = uint32_t;
re2c:encoding:utf32 = 1;
*/
}
int main() {
static const uint8_t s8[] = // UTF-8
{ 0xe2, 0x88, 0x80, 0x78, 0x20, 0xe2, 0x88, 0x83, 0x79 };
static const uint32_t s32[] = // UTF32
{ 0x00002200, 0x00000078, 0x00000020, 0x00002203, 0x00000079 };
assert(lex_utf8(s8) == 0);
assert(lex_utf32(s32) == 0);
return 0;
}
ابزار re2c دو روش برای استخراج تطابقهای فرعی دارد.
- Tags
- روش نخست، استفاده از برچسبهای (tags) مستقل به فرم @stag یا #mtag است، که در آن stag و mtag نامهای دلخواه تعریفشده توسط کاربر هستند. برچسبها با گزینهٔ -T --tags یا پیکربندی re2c:tags = 1 فعال میشوند. از نظر معنایی، برچسبها نشانگرهای موقعیت هستند: آنها میتوانند در هر کجای یک عبارت منظم درج شوند، و به موقعیت (یا چندین موقعیت) متناظر در رشتهٔ ورودی مقید گردند. S-tagها به آخرین موقعیت تطبیقیافته مقید میشوند، و m-tagها به فهرستی از موقعیتها مقید میگردند (میتوان از آنها در زیرعبارتهای تکرار شونده استفاده کرد، جایی که یک موقعیت منفرد در یک عبارت منظم با چندین موقعیت در رشتهٔ ورودی مطابقت دارد). تمام برچسبها باید توسط کاربر، بهصورت دستی یا با کمک بلوکهای svars و mvars تعریف شوند. اگر بیش از یک روش برای تطبیق برچسبها با ورودی وجود داشته باشد، رفع ابهام با استفاده از راهبرد چپترین حریصانه (leftmost greedy) انجام میگیرد.
- Captures
- روش دوم، استفاده از گروههای گیرانداز (capturing groups) است. آنها با گزینهٔ --captures یا پیکربندی re2c:captures = 1 فعال میشوند. دو حالت مختلف برای سیاستهای رفع ابهام وجود دارد؛ --leftmost-captures (پیشفرض) برای سیاست چپترین حریصانه (leftmost greedy)، و --posix-captures برای سیاست طولانیترین تطابق پازیکس (POSIX longest-match) است. در این حالت تمام زیرعبارتهای داخل پرانتز به عنوان گروههای گیرانداز در نظر گرفته میشوند، و میتوان از علامت تعجب برای مشخص کردن گروههای غیرگیرانداز استفاده کرد: (! ... ). با گزینهٔ --invert-captures یا پیکربندی re2c:invert-captures = 1، معنای علامت تعجب معکوس میشود. تعداد گروهها برای قاعدهٔ تطبیقیافته در متغیر yynmatch ذخیره میشود (کل عبارت منظم، گروه شماره صفر است)، و نتایج تطابق فرعی در آرایهٔ yypmatch ذخیره میگردند. هر دو متغیر yynmatch و yypmatch باید توسط کاربر تعریف شوند، و اندازهٔ yypmatch باید حداقل [yynmatch * 2] باشد. از بلوک maxnmatch برای تعریف YYMAXNMATCH استفاده کنید؛ ثابتی که برابر با بیشینهٔ مقدار yynmatch در میان تمام قواعد است.
- Captvars
- روش دیگر برای استفاده از گروههای گیرانداز، گزینهٔ --captvars یا پیکربندی re2c:captvars = 1 است. تنها تفاوت با --captures در شیوهٔ ذخیره نتایج تطابق فرعی توسط کد تولیدشده است: بهجای yynmatch و yypmatch، ابزار re2c متغیرهای yytl<k> و yytr<k> را برای k-امین گروه گیرانداز تولید میکند (کاربر باید این متغیرها را با استفاده از یک بلوک svars اعلان کند). گیراندازی با متغیرها از دو سیاست رفع ابهام پشتیبانی میکند: --leftmost-captvars یا re2c:leftmost-captvars = 1 برای سیاست چپترین حریصانه (حالت پیشفرض) و --posix-captvars یا re2c:posix-captvars برای سیاست طولانیترین تطابق پازیکس.
در پشت صحنه، تمام این گزینهها به برچسبها و ماشینهای خودکاره متناهی معین برچسبدار با پیشنگری (Tagged Deterministic Finite Automata with Lookahead) ترجمه میشوند. ایدهٔ اصلی TDFA، به حداقل رساندن سربار استخراج تطابق فرعی است. در حالت حدی، اگر هیچ برچسب یا گروه گیراندازی در عبارت منظم وجود نداشته باشد، TDFA صرفاً یک DFA معمولی خواهد بود. اگر تعداد برچسبها متعادل باشد، این سربار بهندرت محسوس است. ماشین TDFA تولیدشده از تعدادی متغیر برچسب (tag variables) استفاده میکند که مستقیماً به برچسبها نگاشت نمیشوند: یک متغیر واحد ممکن است برای برچسبهای متفاوتی به کار رود، و یک برچسب ممکن است برای نگهداری تمام مقادیر احتمالی خود به چندین متغیر نیاز داشته باشد. در نهایت، ابهام برطرف شده و برای هر برچسب تنها یک متغیر نهایی باقی میماند. متغیرهای برچسب باید با استفاده از بلوکهای stags یا mtags تعریف شوند. اگر وضعیت تحلیلگر لغوی (lexer) ذخیره میشود، متغیرهای برچسب نیز باید بخشی از آن باشند. آنها همچنین باید توسط YYFILL بهروزرسانی شوند.
برچسبهای S از عملیاتهای زیر پشتیبانی میکنند:
- ذخیرهٔ موقعیت ورودی در یک s-tag: عبارت t = YYCURSOR با API اشارهگر C یا عملیات تعریفشده توسط کاربر YYSTAGP(t) با API عمومی (generic)
- ذخیرهٔ مقدار پیشفرض در یک s-tag: عبارت t = NULL با API اشارهگر C یا عملیات تعریفشده توسط کاربر YYSTAGN(t) با API عمومی
- کپی کردن یک s-tag در دیگری: t1 = t2
برچسبهای M از عملیاتهای زیر پشتیبانی میکنند:
- افزودن موقعیت ورودی به یک m-tag: عملیات تعریفشده توسط کاربر YYMTAGP(t) با هر دو API پیشفرض و عمومی
- افزودن مقدار پیشفرض به یک m-tag: عملیات تعریفشده توسط کاربر YYMTAGN(t) با هر دو API پیشفرض و عمومی
- کپی کردن یک m-tag در دیگری: t1 = t2
برچسبهای S میتوانند به صورت مقادیر اسکالر (اشارهگرها یا آفستها) پیادهسازی شوند. برچسبهای M به نمایش پیچیدهتری نیاز دارند، چرا که باید دنبالهای از مقادیر برچسب را ذخیره کنند. سادهترین و ناکارآمدترین نمایش برای یک m-tag، فهرستی (آرایه، بردار) از مقادیر برچسب است؛ روش کارآمدتر، ذخیرهٔ تمام m-tagها در یک درخت پیشوندی (prefix-tree) است که به صورت آرایهای از گرههای (v, p) نمایش داده میشود، که در آن v مقدار برچسب و p اشارهگری به گره والد است.
در اینجا یک مثال ساده از کاربرد s-tagها برای تجزیهٔ نگارشهای معنایی (semantic versions) متشکل از سه مؤلفهٔ عددی آورده شده است: major، minor و patch (مورد آخر اختیاری است). برای مثال پیچیدهتری که از YYFILL استفاده میکند، به ادامه مراجعه نمایید.
// re2c $INPUT -o $OUTPUT
#include <assert.h>
#include <stddef.h>
typedef struct { int major, minor, patch; } SemVer;
static int s2n(const char *s, const char *e) { // pre-parsed string to number
int n = 0;
for (; s < e; ++s) n = n * 10 + (*s - '0');
return n;
}
static int lex(const char *str, SemVer *ver) {
const char *YYCURSOR = str, *YYMARKER;
// User-defined tag variables that are available in semantic action.
const char *t1, *t2, *t3, *t4, *t5;
// Autogenerated tag variables used by the lexer to track tag values.
/*!stags:re2c format = 'const char *@@;\n'; */
/*!re2c
re2c:yyfill:enable = 0;
re2c:define:YYCTYPE = "unsigned char";
re2c:tags = 1;
num = [0-9]+;
@t1 num @t2 "." @t3 num @t4 ("." @t5 num)? [\x00] {
ver->major = s2n(t1, t2);
ver->minor = s2n(t3, t4);
ver->patch = t5 != NULL ? s2n(t5, YYCURSOR - 1) : 0;
return 0;
}
* { return 1; }
*/
}
int main() {
SemVer v;
assert(lex("23.34", &v) == 0 && v.major == 23 && v.minor == 34 && v.patch == 0);
assert(lex("1.2.999", &v) == 0 && v.major == 1 && v.minor == 2 && v.patch == 999);
assert(lex("1.a", &v) == 1);
return 0;
}
در اینجا مثال پیچیدهتری از کاربرد s-tagها همراه با YYFILL برای تجزیهٔ یک فایل با نگارشهای معنایی جداشده با خط جدید آمده است. متغیرهای برچسب بخشی از وضعیت تحلیلگر لغوی هستند، و همانند سایر موقعیتهای ورودی در YYFILL تنظیم میشوند. توجه داشته باشید که این کار برای s-tagها ضروری است، زیرا مقادیر آنها پس از جابهجایی محتویات بافر نامعتبر میشوند. این امر ممکن است در پیادهسازیهای سفارشی که متغیرهای برچسب در آنها آفستهای نسبی نسبت به ابتدای رشتهٔ ورودی (بهجای بافر) را ذخیره میکنند ضروری نباشد، که میتواند برای m-tagها اینگونه باشد.
// re2c $INPUT -o $OUTPUT --tags
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <vector>
#define BUFSIZE 4095
struct Input {
FILE *file;
char buf[BUFSIZE + 1], *lim, *cur, *mar, *tok;
// Tag variables must be part of the lexer state passed to YYFILL.
// They don't correspond to tags and should be autogenerated by re2c.
/*!stags:re2c format = 'const char *@@;'; */
bool eof;
};
struct SemVer { int major, minor, patch; };
static bool operator==(const SemVer &x, const SemVer &y) {
return x.major == y.major && x.minor == y.minor && x.patch == y.patch;
}
static int s2n(const char *s, const char *e) { // pre-parsed string to number
int n = 0;
for (; s < e; ++s) n = n * 10 + (*s - '0');
return n;
}
static int fill(Input &in) {
if (in.eof) return 1;
const size_t shift = in.tok - in.buf;
const size_t used = in.lim - in.tok;
// Error: lexeme too long. In real life could reallocate a larger buffer.
if (shift < 1) return 2;
// Shift buffer contents (discard everything up to the current token).
memmove(in.buf, in.tok, used);
in.lim -= shift;
in.cur -= shift;
in.mar -= shift;
in.tok -= shift;
// Tag variables need to be shifted like other input positions. The check
// for non-NULL is only needed if some tags are nested inside of alternative
// or repetition, so that they can have NULL value.
/*!stags:re2c format = "if (in.@@) in.@@ -= shift;\n"; */
// Fill free space at the end of buffer with new data from file.
in.lim += fread(in.lim, 1, BUFSIZE - used, in.file);
in.lim[0] = 0;
in.eof = in.lim < in.buf + BUFSIZE;
return 0;
}
static bool lex(Input &in, std::vector<SemVer> &vers) {
// User-defined local variables that store final tag values.
// They are different from tag variables autogenerated with `stags:re2c`,
// as they are set at the end of match and used only in semantic actions.
const char *t1, *t2, *t3, *t4;
for (;;) {
in.tok = in.cur;
/*!re2c
re2c:eof = 0;
re2c:api:style = free-form;
re2c:define:YYCTYPE = "unsigned char";
re2c:define:YYCURSOR = in.cur;
re2c:define:YYMARKER = in.mar;
re2c:define:YYLIMIT = in.lim;
re2c:define:YYFILL = "fill(in) == 0";
re2c:tags:expression = "in.@@";
num = [0-9]+;
num @t1 "." @t2 num @t3 ("." @t4 num)? [\n] {
int major = s2n(in.tok, t1);
int minor = s2n(t2, t3);
int patch = t4 != NULL ? s2n(t4, in.cur - 1) : 0;
SemVer ver = {major, minor, patch};
vers.push_back(ver);
continue;
}
$ { return true; }
* { return false; }
*/}
}int main() {
const char *fname = "input";
const SemVer semver = {1, 22, 333};
std::vector<SemVer> expect(BUFSIZE, semver), actual;
// Prepare input file (make sure it exceeds buffer size).
FILE *f = fopen(fname, "w");
for (int i = 0; i < BUFSIZE; ++i) fprintf(f, "1.22.333\n");
fclose(f);
// Reopen input file for reading.
f = fopen(fname, "r");
// Initialize lexer state: all pointers are at the end of buffer.
Input in;
in.file = f;
in.cur = in.mar = in.tok = in.lim = in.buf + BUFSIZE;
/*!stags:re2c format = "in.@@ = in.lim;\n"; */
in.eof = false;
// Sentinel (at YYLIMIT pointer) is set to zero, which triggers YYFILL.
*in.lim = 0;
// Run the lexer and check results.
assert(lex(in, actual) && expect == actual);
// Cleanup: remove input file.
fclose(f);
remove(fname);
return 0;
}
در اینجا مثالی از بهکارگیری گروههای گیرانداز (capturing groups) برای تجزیه نگارشهای معنایی آورده شده است.
// re2c $INPUT -o $OUTPUT
#include <assert.h>
#include <stddef.h>
typedef struct { int major, minor, patch; } SemVer;
static int s2n(const char *s, const char *e) { // pre-parsed string to number
int n = 0;
for (; s < e; ++s) n = n * 10 + (*s - '0');
return n;
}
static int lex(const char *str, SemVer *ver) {
const char *YYCURSOR = str, *YYMARKER;
// Final tag variables available in semantic action.
/*!svars:re2c format = 'const char *@@;\n'; */
// Intermediate tag variables used by the lexer (must be autogenerated).
/*!stags:re2c format = 'const char *@@;\n'; */
/*!re2c
re2c:yyfill:enable = 0;
re2c:define:YYCTYPE = "unsigned char";
re2c:captvars = 1;
num = [0-9]+;
(num) "." (num) ("." num)? [\x00] {
(void) yytl0; (void) yytr0; // some variables are unused
ver->major = s2n(yytl1, yytr1);
ver->minor = s2n(yytl2, yytr2);
ver->patch = yytl3 ? s2n(yytl3 + 1, yytr3) : 0;
return 0;
}
* { return 1; }
*/
}
int main() {
SemVer v;
assert(lex("23.34", &v) == 0 && v.major == 23 && v.minor == 34 && v.patch == 0);
assert(lex("1.2.999", &v) == 0 && v.major == 1 && v.minor == 2 && v.patch == 999);
assert(lex("1.a", &v) == 1);
return 0;
}
در اینجا مثالی از بهکارگیری m-tagها برای تجزیه نگارشی با تعداد متغیری از مؤلفهها آورده شده است. متغیرهای برچسب در یک trie ذخیره میشوند.
// re2c $INPUT -o $OUTPUT
#include <assert.h>
#include <stddef.h>
#include <vector>
static const int MTAG_ROOT = -1;
// An m-tag tree is a way to store histories with an O(1) copy operation.
// Histories naturally form a tree, as they have common start and fork at some
// point. The tree is stored as an array of pairs (tag value, link to parent).
// An m-tag is represented with a single link in the tree (array index).
struct Mtag {
const char *elem; // tag value
int pred; // index of the predecessor node or root
};
typedef std::vector<Mtag> MtagTrie;
typedef std::vector<int> Ver; // unbounded number of version components
static int s2n(const char *s, const char *e) { // pre-parsed string to number
int n = 0;
for (; s < e; ++s) n = n * 10 + (*s - '0');
return n;
}
// Append a single value to an m-tag history.
static void add_mtag(MtagTrie &trie, int &mtag, const char *value) {
Mtag m = {value, mtag};
mtag = (int)trie.size();
trie.push_back(m);
}
// Recursively unwind tag histories and collect version components.
static void unfold(const MtagTrie &trie, int x, int y, Ver &ver) {
// Reached the root of the m-tag tree, stop recursion.
if (x == MTAG_ROOT && y == MTAG_ROOT) return;
// Unwind history further.
unfold(trie, trie[x].pred, trie[y].pred, ver);
// Get tag values. Tag histories must have equal length.
assert(x != MTAG_ROOT && y != MTAG_ROOT);
const char *ex = trie[x].elem, *ey = trie[y].elem;
if (ex != NULL && ey != NULL) {
// Both tags are valid pointers, extract component.
ver.push_back(s2n(ex, ey));
} else {
// Both tags are NULL (this corresponds to zero repetitions).
assert(ex == NULL && ey == NULL);
}
}
static bool parse(const char *str, Ver &ver) {
const char *YYCURSOR = str, *YYMARKER;
MtagTrie mt;
// User-defined tag variables that are available in semantic action.
const char *t1, *t2;
int t3, t4;
// Autogenerated tag variables used by the lexer to track tag values.
/*!stags:re2c format = 'const char *@@ = NULL;'; */
/*!mtags:re2c format = 'int @@ = MTAG_ROOT;'; */
/*!re2c
re2c:api:style = free-form;
re2c:define:YYCTYPE = "unsigned char";
re2c:define:YYSTAGP = "@@ = YYCURSOR;";
re2c:define:YYSTAGN = "@@ = NULL;";
re2c:define:YYMTAGP = "add_mtag(mt, @@, YYCURSOR);";
re2c:define:YYMTAGN = "add_mtag(mt, @@, NULL);";
re2c:yyfill:enable = 0;
re2c:tags = 1;
num = [0-9]+;
@t1 num @t2 ("." #t3 num #t4)* [\x00] {
ver.clear();
ver.push_back(s2n(t1, t2));
unfold(mt, t3, t4, ver);
return true;
}
* { return false; }
*/
}
int main() {
Ver v;
assert(parse("1", v) && v == Ver({1}));
assert(parse("1.2.3.4.5.6.7", v) && v == Ver({1, 2, 3, 4, 5, 6, 7}));
assert(!parse("1.2.", v));
return 0;
}
درک تفاوت میان نقطهکدها (code points) و واحدهای کد (code units) ضروری است. نقطهکد، یک شناسهٔ عددی برای یک نماد است. واحد کد، کوچکترین واحد ذخیرهسازی در متن کدگذاریشده است. یک نقطهکد میتواند با یک یا چند واحد کد نمایش داده شود. در کدگذاری با طول ثابت (fixed-length)، تمامی نقطهکدها با تعداد یکسانی از واحدهای کد نمایش مییابند. در کدگذاری با طول متغیر (variable-length)، نقطهکدها ممکن است با تعداد متفاوتی از واحدهای کد نمایش داده شوند. توجه داشته باشید که قاعدهٔ "any" (هر چیز) [^] با هر نقطهکدی مطابقت مییابد، اما لزوماً با هر واحد کد مطابقت ندارد (تنها روش برای تطبیق با هر واحد کد بدون در نظر گرفتن نوع کدگذاری، قاعدهٔ پیشفرض * است). تحلیلگر لغوی تولیدشده با جریانی از واحدهای کد کار میکند: yych یک واحد کد را ذخیره میکند و YYCTYPE نوع دادهٔ واحد کد است. در مقابل، عبارتهای منظم بر اساس نقطهکدها مشخص میشوند. هنگامی که re2c عبارتهای منظم را به اتوماتا کامپایل میکند، نقطهکدها را به واحدهای کد ترجمه میکند. این امر عموماً یک نگاشت ساده نیست: در کدگذاریهای با طول متغیر، بازهای از یک نقطهکد ممکن است به گرافی پیچیده از واحدهای کد ترجمه شود. کدگذاریهای زیر پشتیبانی میشوند:
- ASCII (بهطور پیشفرض فعال است). یک کدگذاری با طول ثابت با فضای کد [0-255] و نقطهکدها و واحدهای کد ۱ بایتی است.
- EBCDIC (با --ebcdic یا re2c:encoding:ebcdic فعال میشود). یک کدگذاری با طول ثابت با فضای کد [0-255] و نقطهکدها و واحدهای کد ۱ بایتی است.
- UCS2 (با --ucs2 یا re2c:encoding:ucs2 فعال میشود). یک کدگذاری با طول ثابت با فضای کد [0-0xFFFF] و نقطهکدها و واحدهای کد ۲ بایتی است.
- UTF8 (با --utf8 یا re2c:encoding:utf8 فعال میشود). یک کدگذاری یونیکد با طول متغیر است. اندازهٔ واحد کد ۱ بایت است. نقطهکدها با ۱ تا ۴ واحد کد نمایش داده میشوند.
- UTF16 (با --utf16 یا re2c:encoding:utf16 فعال میشود). یک کدگذاری یونیکد با طول متغیر است. اندازهٔ واحد کد ۲ بایت است. نقطهکدها با ۱ تا ۲ واحد کد نمایش داده میشوند.
- UTF32 (با --utf32 یا re2c:encoding:utf32 فعال میشود). یک کدگذاری یونیکد با طول ثابت با فضای کد [0-0x10FFFF] و نقطهکدها و واحدهای کد ۴ بایتی است.
فایلهای ضمیمهٔ include/unicode_blocks.re، include/unicode_categories.re و include/unicode_properties.re تعاریف re2c را برای بلوکها، دستهبندیها و ویژگیهای توصیفشده در استاندارد Unicode فراهم میکنند.
گزینهٔ --input-encoding کدگذاری فایل مبدأ را مشخص میکند که میتواند برای فعالسازی نویسهواژههای (literals) یونیکد در عبارتهای منظم استفاده شود. برای مثال --input-encoding utf8 به re2c اعلام میکند که فایل منبع در قالب UTF8 است (این با گزینهٔ --utf8 که کدگذاری متن ورودی را تنظیم میکند متفاوت است). گزینهٔ --encoding-policy نحوهٔ برخورد re2c با جانشینهای یونیکد (Unicode surrogates، نقطهکدهای موجود در بازهٔ [0xD800-0xDFFF]) را تعیین میکند.
در زیر مثالی از یک تحلیلگر لغوی برای شناسههای یونیکد با کدگذاری UTF8 آمده است.
// re2c $INPUT -o $OUTPUT -8 --case-ranges -i
#include <assert.h>
#include <stdint.h>
/*!include:re2c "unicode_categories.re" */
static int lex(const char *s) {
const char *YYCURSOR = s, *YYMARKER;
/*!re2c
re2c:define:YYCTYPE = "unsigned char";
re2c:yyfill:enable = 0;
// Simplified "Unicode Identifier and Pattern Syntax"
// (see https://unicode.org/reports/tr31)
id_start = L | Nl | [$_];
id_continue = id_start | Mn | Mc | Nd | Pc | [\u200D\u05F3];
identifier = id_start id_continue*;
identifier { return 0; }
* { return 1; }
*/
}
int main() {
assert(lex("_Ыдентификатор") == 0);
return 0;
}
ابزار re2c به شما اجازه میدهد فایلهای دیگر را با استفاده از بلوکی به قالب /*!include:re2c FILE */ یا %{include FILE %}، یا یک دستورالعمل درونبلوکیِ !include FILE ; ضمیمه کنید، که در آن FILE مسیر فایلی است که باید ضمیمه شود. re2c فایلهای ضمیمه را در دایرکتوری فایلی که آنها را ضمیمه کرده و همچنین در مکانهای include (که میتوان با گزینهٔ -I تعیین کرد) جستجو میکند. بلوکها/دستورالعملهای include در re2c همانند #include در C/C++ عمل میکنند: محتوای FILE عیناً به جای بلوک/دستورالعمل جایگذاری میشود. فایلهای ضمیمه ممکن است خود دارای فایلهای ضمیمهٔ دیگری باشند. برای ردیابی وابستگیهای ساخت فایل خروجی به فایلهای ضمیمه، از گزینهٔ --depfile استفاده کنید. re2c چند فایل include از پیش تعریفشده ارائه میدهد که در زیردایرکتوری include/ پروژه یافت میشوند. این فایلها شامل تعاریفی هستند که ممکن است برای پروژههای دیگر نیز مفید باشند (مانند دستهبندیهای Unicode) و چیزی شبیه به یک کتابخانهٔ استاندارد برای re2c میسازند. در ادامه مثالی از بهکارگیری فایلهای include آمده است.
فایل ضمیمه ۱ (definitions.h)
typedef enum { OK, FAIL } Result;
/*!re2c
number = [1-9][0-9]*;
*/
فایل ضمیمه ۲ (extra_rules.re.inc)
// floating-point numbers
frac = [0-9]* "." [0-9]+ | [0-9]+ ".";
exp = 'e' [+-]? [0-9]+;
float = frac exp? | [0-9]+ exp;
float { return OK; }
فایل ورودی (Input file)
// re2c $INPUT -o $OUTPUT -i
#include <assert.h>
/*!include:re2c "definitions.h" */
Result lex(const char *s) {
const char *YYCURSOR = s, *YYMARKER;
/*!re2c
re2c:define:YYCTYPE = "unsigned char";
re2c:yyfill:enable = 0;
* { return FAIL; }
number { return OK; }
!include "extra_rules.re.inc";
*/
}
int main() {
assert(lex("123") == OK);
assert(lex("123.4567") == OK);
return 0;
}
ابزار re2c به شما اجازه میدهد با استفاده از گزینهٔ --header یا پیکربندی re2c:header و جفتبلوکهایی به شکل /*!header:re2c:on*/ و /*!header:re2c:off*/، یا %{header:on%} و %{header:off%}، از فایل ورودی .re فایل سرآیند تولید کنید. بلوک اول آغاز فایل سرآیند را مشخص میکند و بلوک دوم پایان آن را تعیین مینماید. هر آنچه بین این دو بلوک قرار گیرد توسط re2c پردازش میشود و کد تولیدشده در فایلی که با گزینهٔ --header یا پیکربندی re2c:header مشخص شده (یا در صورت عدم استفاده از گزینه یا پیکربندی، در stdout) نوشته خواهد شد. تولید خودکار فایل سرآیند ممکن است در مواردی نیاز باشد که re2c برای تولید تعاریفی به کار میرود که باید از سایر واحدهای ترجمه (translation units) قابل مشاهده باشند.
در ادامه، مثالی از تولید یک فایل سرآیند آورده شده است که شامل تعریف وضعیت تحلیلگر با متغیرهای برچسب است (تعداد متغیرها به گرامر باقاعده بستگی دارد و برای برنامهنویس نامشخص است).
فایل ورودی (Input file)
// re2c $INPUT -o $OUTPUT -i --header lexer/state.h
#include <assert.h>
#include <stddef.h>
#include "lexer/state.h" // the header is generated by re2c
/*!header:re2c:on*/
typedef struct {
const char *str, *cur;
/*!stags:re2c format = "const char *@@;"; */
} LexerState;
/*!header:re2c:off*/
long lex(LexerState* st) {
const char *t;
/*!re2c
re2c:header = "lexer/state.h";
re2c:yyfill:enable = 0;
re2c:define:YYCTYPE = "unsigned char";
re2c:define:YYCURSOR = "st->cur";
re2c:tags = 1;
re2c:tags:expression = "st->@@";
[a]* @t [b]* { return t - st->str; }
*/
}
int main() {
const char *s = "ab";
LexerState st = { s, s /*!stags:re2c format = ", NULL"; */ };
assert(lex(&st) == 1);
return 0;
}
فایل سرآیند (Header file)
/* Generated by re2c */
typedef struct {
const char *str, *cur;
const char *yyt1;
} LexerState;
برنامههای اسکلتی (Skeleton programs)
با گزینهٔ -S, --skeleton، ابزار re2c تمام کدهای غیر re2c را نادیده گرفته و یک برنامهٔ مستقل به زبان C تولید میکند که میتواند در ادامه کامپایل و اجرا شود. این برنامه شامل کد تحلیلگر و دادههای ورودی است. برای هر DFA ساختهشده (بلوک یا شرط)، re2c یک تحلیلگر مستقل و دو فایل تولید میکند: یک فایل .input با رشتههای مشتقشده از DFA و یک فایل .keys با نتایج تطابق مورد انتظار. برنامه هر تحلیلگر را بر روی فایل .input متناظر اجرا کرده و نتایج را با انتظارات مقایسه میکند. برنامههای اسکلتی به چند دلیل بسیار مفید هستند:
- میتوانند درستی بهینهسازیهای گوناگون re2c را بررسی کنند (دادهها در اوایل فرایند و پیش از انجام هرگونه تبدیل DFA تولید میشوند).
- تولید مجموعهای از دادههای ورودی با پوشش مناسب میتواند هم برای آزمون و هم برای محکزنی (benchmarking) مفید باشد.
- تولید برنامههای اجرایی مستقل اجازه میدهد موارد آزمون کمینهشده (minimized test cases) به دست آیند (کد اصلی ممکن است بزرگ بوده یا وابستگیهای متعددی داشته باشد).
دشواری تولید دادههای ورودی در این است که به جز در سادهترین حالتها، تعداد رشتههای ورودیِ ممکن بسیار زیاد است (حتی اگر طول رشته محدود باشد). re2c این دشواری را با تولید رشتههای کافی به منظور پوشش دادن تقریباً تمام انتقالهای DFA برطرف میکند. این ابزار از الگوریتم زیر استفاده میکند: ابتدا، اسکلتی از DFA میسازد. برای رمزگذاریهایی با اندازهٔ واحد کد ۱ بایتی (مانند ASCII، UTF-8 و EBCDIC) اسکلت دقیقاً رونوشتی از DFA اصلی است. برای رمزگذاریهایی با واحدهای کد چندبایتی، اسکلت کپیای از DFA با حذف برخی انتقالها است: بدین صورت که re2c برای هر بازهٔ پیوسته و مجزا که با یک انتقال DFA متناظر است، حداکثر ۲۵۶ واحد کد برمیدارد. مقادیر انتخابشده بهطور یکنواخت توزیع شده و شامل کرانهای بازه نیز هستند. re2c بهجای تلاش برای پوشش تمام مسیرهای ممکن در اسکلت (که کاری غیرعملی است)، مسیرهای کافی برای پوشش همهٔ انتقالهای اسکلت را تولید میکند و بدین ترتیب پرشهای شرطی مربوطه را در تحلیلگر فعال میسازد. پیادهسازی این الگوریتم به حدود ۱ گیگابایت انتقال محدود شده است و مقدار ثابتی از حافظه مصرف میکند (re2c دادهها را بلافاصله پس از تولید در فایل مینویسد)..SS "تصویرسازی و اشکالزدایی (Visualization and debug)"
با گزینهٔ -D, --emit-dot، ابزار re2c کدی تولید نمیکند؛ در عوض، DFA تولیدشده را در قالب DOT خروجی میدهد (دامپ میکند). میتوان این خروجی را با استفاده از Graphviz یا کتابخانهای دیگر به تصویری از DFA تبدیل کرد. توجه داشته باشید که این گزینه، DFA نهایی را پس از گذر از چندین بهینهسازی و تبدیل نشان میدهد. مراحل پیشین را میتوان با گزینههای اشکالزدایی مختلفی مانند --dump-nfa، --dump-dfa-raw و غیره ذخیره کرد (فهرست کامل گزینهها را ببینید).
همچنین ببینید (SEE ALSO)
میتوانید اطلاعات بیشتری دربارهٔ re2c در وبگاه رسمی آن بیابید: http://re2c.org. برنامههای مشابه عبارتند از flex(1)، lex(1)، quex(http://quex.sourceforge.net).
نویسندگان (AUTHORS)
ابزار re2c در اصل در سال ۱۹۹۳ توسط Peter Bumbulis (peter@csg.uwaterloo.ca) نوشته شد. Marcus Boerger و Dan Nuffer چندین سال وقت صرف کردند تا ایدهٔ اولیه را به یک تولیدکنندهٔ کد آماده برای بهرهبرداری تبدیل کنند. از آن زمان به بعد، این پروژه توسط داوطلبان متعددی نگهداری و توسعه یافته است، بهویژه: Brian Young (bayoung@acm.org)، Marcus Boerger، Dan Nuffer (nuffer@users.sourceforge.net)، Ulya Trofimovich (skvadrik@gmail.com)، Serghei Iakovlev، Sergei Trofimovich، Petr Skocik، ligfx raekye و PolarGoose.