| RE2V(1) | RE2V(1) |
نام (NAME)
re2v - تولید تحلیلگرهای لغوی سریع برای V
خلاصه دستور (SYNOPSIS)
re2v [ OPTIONS ] [ WARNINGS ] INPUT
ورودی میتواند یک فایل یا - برای stdin باشد.
مقدمه (INTRODUCTION)
ابزار re2v به عنوان یک پیشپردازنده عمل میکند. این ابزار فایل ورودی (که معمولاً یک برنامه به زبان V است، اما میتواند هر چیز دیگری نیز باشد) را خوانده و به دنبال بلوکهای کدی میگردد که درون نشانگرهای آغاز/پایان با قالب خاص قرار گرفتهاند. متن خارج از این بلوکها عیناً به فایل خروجی کپی میشود. محتویات درون بلوکها توسط re2v پردازش میگردد. این ابزار آنها را به کد V ترجمه کرده و کد تولیدشده را به جای آن بلوک در خروجی قرار میدهد.
در اینجا مثالی از یک برنامه کوچک آورده شده است که بررسی میکند آیا یک رشته دادهشده حاوی عدد بر مبنای ۱۰ (دهدهی) است یا خیر:
// re2v $INPUT -o $OUTPUT -i
fn lex(yyinput string) {
mut yycursor := 0
/*!re2c
re2c:yyfill:enable = 0;
[1-9][0-9]* { return }
* { panic("error!") }
*/
}
fn main() {
lex("1234\x00")
}
در خروجی، re2v بلوک میانی را با کد تولیدشده جایگزین کرده است:
// Code generated by re2v, DO NOT EDIT.
// re2v $INPUT -o $OUTPUT -i
fn lex(yyinput string) {
mut yycursor := 0
mut yych := u8(0)
yych = yyinput[yycursor]
match yych {
0x31...0x39 { unsafe { goto yy2 } }
else { unsafe { goto yy1 } }
}
yy1:
yycursor += 1
panic("error!")
yy2:
yycursor += 1
yych = yyinput[yycursor]
match yych {
0x30...0x39 { unsafe { goto yy2 } }
else { unsafe { goto yy3 } }
}
yy3:
return
}
fn main() {
lex("1234\x00")
}
یک برنامه re2v از دنبالهای از بلاکها درآمیخته با کدهایی به زبان مقصد تشکیل میشود. یک بلاک میتواند شامل تعاریف، پیکربندیها، قواعد، کنشها و دستورالعملها با هر ترتیبی باشد:
- name = regular-expression ;
- یک تعریف، name را به regular-expression مقید میکند. نامها میتوانند شامل نویسههای الفبایی-عددی و زیرخط باشند. بخش regular expressions نمایی کلی از نحو re2v برای عبارتهای منظم ارائه میدهد. پس از تعریف، name میتواند در سایر عبارتهای منظم و قواعد استفاده شود. بازگشت در تعاریف نامگذاریشده مجاز نیست و هر نام باید پیش از استفاده تعریف شود. یک بلاک تعاریف نامگذاریشده را از حوزه سراسری (global scope) به ارث میبرد. بازتعریف نامی که در حوزه فعلی وجود دارد خطا است.
- configuration = value ;
- یک پیکربندی امکان تغییر رفتار re2v و سفارشیسازی کد تولیدشده را فراهم میکند. برای مشاهده فهرست کامل پیکربندیهای پشتیبانیشده توسط re2v به بخش configurations مراجعه کنید. بسته به نوع پیکربندی، value میتواند یک کلیدواژه، یک عدد صحیح غیرمنفی یا یک رشته تکخطی باشد که در صورت شامل نبودن نویسههای الفبایی-عددی باید درون گیومه تکی یا دوتایی قرار گیرد. یک بلاک پیکربندیها را از حوزه سراسری به ارث میبرد و میتواند آنها را بازتعریف کرده یا موارد جدیدی بیفزاید. پیکربندیهای تعریفشده درون یک بلاک بر کل آن بلاک اثر میگذارند، حتی اگر در انتهای آن قرار گرفته باشند.
- regular-expression code
- یک قاعده، regular-expression را به کنش معنایی آن مقید میکند (یک بلوک کد درون آکوالاد، یا یک بلوک کد که با := شروع شده و به یک خط جدید ختم میشود که در ادامه آن نویسهای غیرفاصلهای قرار دارد). اگر regular-expression تطابق یابد، code مرتبط اجرا میشود. اگر چندین قاعده تطابق یابند، طولانیترین تطابق اولویت دارد. اگر چندین قاعده با رشته یکسانی تطابق یابند، اولین قاعده اولویت خواهد داشت. دو قاعده ویژه وجود دارد: قاعده پیشفرض * و قاعده پایان ورودی $. قاعده پیشفرض همواره باید تعریف شود، بدون توجه به جایگاهش در بلاک کمترین اولویت را دارد و با هر واحد کد (code unit) تطابق مییابد (نه لزوماً یک نویسه معتبر، به بخش encoding support مراجعه کنید). قاعده پایان ورودی در صورتی باید تعریف شود که روش متناظر برای handling the end of input استفاده شده باشد. با start conditions قواعد نحو پیچیدهتری دارند.
- !action code
- یک کنش یک بلوک تعریفشده توسط کاربر از code را به نقطهای خاص در ماشین حالت متناهی تولیدشده مقید میسازد (مشابه روشی که کنشهای معنایی کد را به وضعیتهای پایانی مقید میکنند). برای فهرست کامل کنشهای از پیشتعریفشده به بخش actions مراجعه کنید.
- !directive ;
- یک دستورالعمل یکی از عبارتهای ویژه از پیشتعریفشده است. هر دستورالعمل هدف منحصربهفردی دارد. برای جزئیات به بخش directives مراجعه کنید.
بلاکها (Blocks)
نشانگرهای آغاز و پایان بلاک یا /*!re2c و */ هستند، یا %{ و %} (از هر دو سبک پشتیبانی میشود). از نسخه 2.2 به بعد، بلاکها میتوانند نامهای اختیاری داشته باشند که امکان ارجاع به آنها در سایر بلاکها را ممکن میسازد. انواع گوناگونی از بلاکها وجود دارد:
- /*!re2c[:<name>] ... */ or %{[:<name>] ... %}
- یک بلاک سراسری شامل تعاریف، پیکربندیها، قواعد و دستورالعملها است. re2v عبارتهای منظم مرتبط با هر قاعده را به یک اتوماتون متناهی قطعی کامپایل میکند، آن را در قالب پرشهای شرطی در زبان مقصد کدگذاری کرده و بلاک را با کد تولیدشده جایگزین میکند. نامها و پیکربندیهای تعریفشده در یک بلاک سراسری به حوزه سراسری افزوده میشوند و برای بلاکهای بعدی قابل مشاهده خواهند بود. در آغاز برنامه، حوزه سراسری با options خط فرمان مقداردهی اولیه میشود.
- /*!local:re2c[:<name>] ... */ or %{local[:<name>] ... %}
- یک بلاک محلی مانند یک بلاک سراسری است، اما نامها و پیکربندیهای درون آن حوزه محلی دارند (بر سایر بلاکها تأثیر نمیگذارند).
- /*!rules:re2c[:<name>] ... */ or %{rules[:<name>] ... %}
- یک بلاک قواعد شبیه به یک بلاک محلی است، اما بهتنهایی هیچ کدی تولید نمیکند و هیچ تعریفی نیز به حوزه سراسری نمیافزاید -- این بلاک برای استفاده مجدد در سایر بلاکها در نظر گرفته شده است. این روشی برای اشتراکگذاری کد است (جزئیات بیشتر در بخش reusable blocks). پیش از نسخه 2.2 برنامه re2v، بلاکهای قواعد به گزینه -r --reusable نیاز داشتند.
- /*!use:re2c[:<name>] ... */ or %{use[:<name>] ... %}
- یک بلاک use که به یک بلاک قواعد تعریفشده از قبل ارجاع میدهد. اگر نام مشخص شده باشد، re2v به دنبال یک بلاک قواعد با این نام میگردد. در غیر این صورت، از جدیدترین بلاک قواعد استفاده میشود (خواه دارای نام باشد یا بدون نام). یک بلاک use میتواند تعاریف، پیکربندیها و قواعد خاص خود را بیفزاید که به موارد موجود در بلاک قواعد مورد ارجاع اضافه میشوند. پیش از نسخه 2.2 برنامه re2v، بلاکهای use به گزینه -r --reusable نیاز داشتند.
- /*!max:re2c[:<name1>[:<name2>...]] ... */ or %{max[:<name1>[:<name2>...]] ... %}
- بلاکی که تعریف YYMAXFILL را تولید میکند. یک فهرست اختیاری از نامهای بلاک مشخص میکند که کدام بلاکها هنگام محاسبه مقدار YYMAXFILL باید لحاظ شوند (اگر فهرست خالی باشد، همه بلاکها لحاظ میشوند). بهطور پیشفرض، کد تولیدشده یک تعریف ماکرو برای C است (#define YYMAXFILL <n>) یا یک متغیر سراسری برای Go (var YYMAXFILL int = <n>). این رفتار میتواند با پیکربندی اختیاری format سفارشی شود که یک رشته الگو را مشخص میکند که در آن @@{max} (یا به اختصار @@) با مقدار عددی YYMAXFILL جایگزین میشود.
- /*!maxnmatch:re2c[:<name1>[:<name2>...]] ... */ or %{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>...]] ... */ or %{stags[:<name1>[:<name2>...]] ... %}, %{mtags[:<name1>[:<name2>...]] ... %{
- بلاکهایی که یک قطعه کد الگو را مشخص میکنند که برای هر متغیر s-tag/m-tag تولیدشده توسط re2v بسط داده میشود. یک فهرست اختیاری از نامهای بلاک مشخص میکند که هنگام محاسبه مجموعه متغیرهای برچسب، کدام بلاکها باید لحاظ شوند (اگر فهرست خالی باشد، تمام بلاکها لحاظ میشوند). دو پیکربندی اختیاری وجود دارد: format و separator. پیکربندی format یک رشته الگو را تعیین میکند که در آن @@{tag} (یا به اختصار @@) با نام هر متغیر برچسب جایگزین میشود. پیکربندی separator قطعه کدی را مشخص میکند که برای پیوند دادن قطعات تولیدشده format برای متغیرهای برچسب مختلف به کار میرود.
- /*!svars:re2c[:<name1>[:<name2>...]] ... */, /*!mvars:re2c[:<name1>[:<name2>...]] ... */ or %{svars[:<name1>[:<name2>...]] ... %}, %{mvars[:<name1>[:<name2>...]] ... %{
- بلاکهایی که یک قطعه کد الگو را مشخص میکنند که برای هر s-tag/m-tag که یا صراحتاً توسط قواعد ذکر شده است (با گزینه --tags) یا بهطور ضمنی توسط re2v تولید شده است (با گزینههای --captvars یا --posix-captvars)، بسط مییابد. یک فهرست اختیاری از نامهای بلاک مشخص میکند که هنگام محاسبه مجموعه برچسبها کدام بلاکها باید لحاظ شوند (اگر فهرست خالی باشد، تمام بلاکها لحاظ میشوند). دو پیکربندی اختیاری وجود دارد: format و separator. پیکربندی format یک رشته الگو را مشخص میکند که در آن @@{tag} (یا به اختصار @@) با نام هر برچسب جایگزین میشود. پیکربندی separator قطعه کدی را مشخص میکند که برای پیوند دادن قطعات تولیدشده format برای برچسبهای مختلف به کار میرود.
- /*!getstate:re2c[:<name1>[:<name2>...]] ... */ or %{getstate[:<name1>[:<name2>...]] ... %}
- بلاکی که توزیع شرطی بر اساس وضعیت تحلیلگر لغوی را تولید میکند (به گزینه --storable-state نیاز دارد). یک فهرست اختیاری از نامهای بلاک مشخص میکند که کدام بلاکها باید در توزیع وضعیت گنجانده شوند. گذار پیشفرض به برچسب آغازین اولین بلاک در فهرست هدایت میشود. اگر فهرست خالی باشد، همه بلاکها لحاظ میشوند و گذار پیشفرض به اولین بلاک در فایل هدایت میشود که دارای برچسب آغازین باشد. این نوع بلاک با گزینه --loop-switch ناسازگار است، چرا که نیازمند گذارهای بینبلاکی است که بدون دستور goto یا فراخوانیهای تابع پشتیبانی نمیشوند.
- /*!conditions:re2c[:<name1>[:<name2>...]] ... */, /*!types:re2c... */ or %{conditions[:<name1>[:<name2>...]] ... %}, %{types... %}
- بلاکی که شمارش وضعیتها را تولید میکند (به گزینه --conditions نیاز دارد). یک فهرست اختیاری از نامهای بلاک مشخص میکند که هنگام محاسبه مجموعه وضعیتها کدام بلاکها باید لحاظ شوند (اگر فهرست خالی باشد، همه بلاکها لحاظ میشوند). بهطور پیشفرض، کد تولیدشده یک شمارش YYCONDTYPE است. این کد میتواند با پیکربندیهای اختیاری format و separator سفارشیسازی شود. پیکربندی format یک رشته الگو را مشخص میکند که در آن @@{cond} (یا به اختصار @@) با نام هر وضعیت و @@{num} با نمایه عددی آن وضعیت جایگزین میشود. پیکربندی separator قطعه کدی را مشخص میکند که برای پیوند دادن قطعات تولیدشده format برای وضعیتهای مختلف به کار میرود.
- /*!include:re2c <file> */ or %{include <file> %}
- این بلاک امکان درج <file> را فراهم میکند که باید مسیر فایلی محصور در گیومه دوتایی باشد. محتویات فایل دقیقاً به جای بلاک جایگزین میشوند، درست مانند نحوه کار #include در C/C++. این بلاک میتواند همراه با گزینه --depfile برای تولید وابستگیهای سامانه ساخت بر روی فایلهای درجشده استفاده شود.
- /*!header:re2c:on*/ or %{header:on %}
- این بلاک آغاز فایل سرایند را مشخص میکند. تمام موارد پس از آن و تا بلاک header:off بعدی، توسط re2v پردازش شده و در فایل سرایندی که با گزینه -t --type-header مشخص شده نوشته میشود.
- /*!header:re2c:off*/ or %{header:off %}
- این بلاک پایان فایل سرایند را که با بلاک header:on*/ آغاز شده بود، مشخص میکند.
- /*!ignore:re2c ... */ or %{ignore ... %}
- بلاکی که
محتوای آن
نادیده
گرفته شده و
از فایل
خروجی حذف
میشود.
در ادامه فهرست کاملی از پیکربندیهای پشتیبانیشده توسط re2v آمده است:
- re2c:api, re2c:input
- همانند گزینهٔ --api.
- re2c:api:sigil
- نشانهگذاری ("سیجیل" یا "sigil") را مشخص میکند که برای جاینگهدارهای آرگومان در عناصر پایهٔ API استفاده میشود. مقدار پیشفرض @@ است. یک جاینگهدار با سیجیل آغاز شده و پس از آن نام آرگومان درون آکولاد میآید. برای مثال، اگر سیجیل روی $ تنظیم شود، جاینگهدارها به شکل ${name} خواهند بود. رابطهای برنامهنویسی دارای یک آرگومان میتوانند از نمادگذاری کوتاه بدون درج نام در آکولاد استفاده کنند. این گزینه میتواند با گزینههای مربوط به هر یک از عناصر پایهٔ مجزای 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) در توزیع شرایط تولیدشده اجرای برنامه را متوقف (abort) میکند.
- re2c:cond:goto
- قطعهکد مورداستفاده برای قواعد میانبر خودکار تولیدشدهٔ :=> در شرایط را مشخص میکند. مقدار پیشفرض goto @@; است. جاینگهدار @@ با نام شرط جایگزین میشود (پیکربندیهای re2c:api:sigil و re2c:cond:goto@cond را ببینید).
- re2c:cond:goto@cond
- سیجیل مورداستفاده برای جایگزینی آرگومان در تعریف re2c:cond:goto را تعیین میکند. مقدار پیشفرض @@ است. این گزینه پیکربندی عمومیتر re2c:api:sigil را لغو میکند.
- re2c:cond:divider
- جداکنندهٔ بلوکهای شرایط را تعیین میکند. مقدار پیشفرض /* *********************************** */ است. جاینگهدارها با نام شرط جایگزین میشوند (پیکربندیهای re2c:api;sigil و re2c:cond:divider@cond را ببینید).
- re2c:cond:divider@cond
- سیجیل مورداستفاده برای جایگزینی آرگومان در تعریف 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) مورداستفاده به همراه قاعدهٔ پایان ورودی $ را تعیین میکند. مقدار پیشفرض -1 است (قاعدهٔ $ استفاده نمیشود). مقادیر دیگر شامل تمام واحدهای کد معتبر هستند. تنها اعداد دهدهی پشتیبانی میشوند.
- re2c:header, re2c:flags:type-header, re2c:flags:t
- نام فایل سرایند (header) تولیدشده را نسبت به دایرکتوری فایل خروجی مشخص میکند. همانند گزینهٔ --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 و در حالت وضعیت ذخیرهپذیر را تعیین میکند.
- re2c:label:yyloop
- نام برچسب نشاندهندهٔ آغاز حلقهٔ تحلیلگر واژگانی را هنگام استفاده از گزینهٔ --loop-switch تعیین میکند. مقدار پیشفرض yyloop است.
- re2c:label:yyNext
- نام برچسب اختیاری را مشخص میکند که بعد از سوییچ YYGETSTATE در حالت وضعیت ذخیرهپذیر میآید (فعالشده با re2c:state:nextlabel). مقدار پیشفرض yyNext است.
- re2c:lookahead, re2c:flags:lookahead
- منسوخ شده است (گزینهٔ منسوخشدهٔ --no-lookahead را ببینید).
- re2c:monadic
- در صورت تنظیم روی مقداری غیر از صفر، تحلیلگر واژگانی تولیدشده از نمادگذاری مونادیک استفاده خواهد کرد (این پیکربندی مخصوص به زبان 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، اما میتواند بر مبنای هر بلوک پیکربندی شود.
- عبارت مورد استفاده برای متغیرهای برچسب را مشخص میکند. بهطور پیشفرض re2v عبارتهایی به شکل yyt<N> تولید میکند. این موضوع ممکن است ناخوشایند باشد، برای مثال اگر متغیرهای برچسب به صورت فیلدهایی در یک struct تعریف شده باشند. تمام رخدادهای @@{tag} یا @@ با نام واقعی برچسب جایگزین میشوند. برای نمونه، re2c:tags:expression = "s.@@"; منجر به تولید عبارتهایی به شکل s.yyt<N> در کد تولیدشده خواهد شد. همچنین پیکربندی re2c:api:sigil را ببینید.
- عبارت ثابتی را مشخص میکند که برای مقدار منفی برچسب استفاده میشود (معمولاً اگر برچسبها آفستهای صحیح در رشتهٔ ورودی باشند این مقدار -1 است، و در صورت اشارهگر بودن، اشارهگر تهی (null pointer) خواهد بود).
- پیشوند نام متغیرهای برچسب را مشخص میکند. پیشفرض yyt است.
- re2c:sentinel
- نماد نگهبان (sentinel) مورد استفاده برای بررسیهای پایان ورودی را مشخص میکند (زمانی که بررسیهای مرزی با re2c:yyfill:enable = 0; غیرفعال شده و re2c:eof تنظیم نشده است). این پیکربندی بر تولید کد تأثیری ندارد: هدف آن بررسی این است که نماد نگهبان در میانهٔ یک قاعده مجاز نباشد، و اطمینان حاصل شود که تحلیلگر لغوی فراتر از پایان بافر را نخواند. مقدار پیشفرض -1` است (در این حالت re2v فرض میکند که نماد نگهبان صفر است که متداولترین حالت است). تنها اعداد دهدهی تشخیص داده میشوند.
- re2c:state:abort
- اگر روی یک مقدار عددی صحیح مثبت تنظیم شود، شاخهٔ پیشفرض (default case) در توزیع وضعیت (state dispatch) تولیدشده، اجرای برنامه را لغو (abort) میکند، و یک شاخهٔ صریح -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 بازنویسی میکند. مقدار صفر متناظر با سبک فرم آزاد (free-form) برای API است.
- 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 بازنویسی میکند. مقدار صفر متناظر با سبک فرم آزاد (free-form) برای API است.
- re2c:YYGETSTATE, re2c:define:YYGETSTATE
- پریمیتیو API به نام YYGETSTATE را تعریف میکند.
- re2c:YYGETSTATE:naked, re2c:define:YYGETSTATE:naked
- پیکربندی عمومیتر re2c:api:style را برای YYGETSTATE بازنویسی میکند. مقدار صفر متناظر با سبک فرم آزاد (free-form) برای API است.
- 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 بازنویسی میکند. مقدار صفر متناظر با سبک فرم آزاد (free-form) برای API است.
- 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 بازنویسی میکند. مقدار صفر متناظر با سبک فرم آزاد (free-form) برای API است.
- 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
- اگر روی مقداری غیر از صفر تنظیم شود، نگاشتهای بیتی برای گزینهٔ --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
- اگر روی مقداری غیر از صفر تنظیم شود، re2v هر بار که 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 وابسته نباشند (برای مثال، اگر از یک نویسهٔ نگهبان استفاده شود). برای اطمینان از این که تحلیلگر واژگانی (lexer) تولیدشده فراتر از انتهای ورودی را نمیخواند، از هشدارها (گزینهٔ -W) و پیکربندی re2c:sentinel استفاده کنید. مقدار پیشفرض مقداری غیر از صفر است (YYFILL فعال است).
- re2c:yyfill:parameter
- اگر روی صفر تنظیم شود، از تولید پارامتر ارسالی به YYFILL جلوگیری میکند. این پارامتر، حداقل تعداد نویسههایی است که باید تأمین شوند. پیشفرض مقداری غیر از صفر است (پارامتر تولید میشود). این پیکربندی میتواند توسط re2c:YYFILL:naked یا re2c:api:style بازنویسی شود.
- re2c:yyfn:sep
- جداکنندهٔ مورد استفاده در عنصرهای YYFN را مشخص میکند (پیشفرض نقطهویرگول است).
- re2c:yyfn:throw
- استثناهای
پرتابشده
توسط تابع
YYFN را مشخص
میکند
(پیشفرض
خالی است،
به معنای
نبود هیچ
استثنایی).
ابزار re2v از ساختار نحوی زیر برای عبارتهای باقاعده استفاده میکند:
- "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؛ از پرانتزها برای تغییر اولویت استفاده میشود. در صورت فعال بودن استخراج تطابق فرعی، بسته به گزینهٔ --invert-captures، نماد (R) یک گروه ضبطکننده یا غیرضبطکننده خواهد بود.
- (!R)
- در صورت فعال بودن استخراج تطابق فرعی، بسته به گزینهٔ --invert-captures، نماد (!R) یک گروه غیرضبطکننده یا ضبطکننده خواهد بود.
- 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)
در اینجا فهرستی از کنشهای از پیش تعریفشده که توسط re2v پشتیبانی میشوند آمده است:
- !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) به
منظور
محافظت در
برابر
جریان
کنترل
ناخواسته
استفاده
کرد.
اینجا فهرست کاملی از دستورالعملهای پشتیبانیشده توسط re2v آمده است:
- !use:name ;
- یک دستورالعمل use درونبلوکی که یک بلوک قواعد از قبل تعریفشده با name مشخصشده را در بلوک فعلی ادغام میکند. تعاریف نامگذاریشده، پیکربندیها و قواعد بلوک ارجاعدادهشده به موارد فعلی افزوده میشوند. تداخل میان قواعد و پیکربندیهای همپوشان به روش معمول حلوفصل میشود: قاعده نخست اولویت دارد، و آخرین پیکربندی بر موارد قبلی اعمال میشود. یک استثنا قواعد خاص *، $ و <!> هستند که برای آنها تعریف محلی بلوک همیشه اولویت دارد. یک دستورالعمل use میتواند در هر کجای یک بلوک قرار گیرد، و استفاده از چندین دستورالعمل use مجاز است.
- !include file ;
- این دستورالعمل همانند بلوک include است: محتویات file را بهصورت عیناً در محل دستورالعمل درج میکند.
رابط برنامه (Program interface)
کد تولیدشده به کمک پریمیتیوها (primitives)، که در مجموع با عنوان API شناخته میشوند، با برنامه بیرونی ارتباط برقرار میکند. اینکه کدام پریمیتیوها باید برای یک برنامه خاص تعریف شوند به عوامل متعددی بستگی دارد، از جمله پیچیدگی عبارات منظم، نحوه نمایش ورودی، بافرینگ و استفاده از ویژگیهای گوناگون. تمام پریمیتیوهای ضروری باید توسط کاربر در قالب ماکروها، توابع، متغیرها یا هر شکل مناسب دیگری که کد تولیدشده را از نظر نحوی و معنایی درست میسازد، تعریف شوند. re2v تعاریف را بررسی نمیکند (و نمیتواند بررسی کند)، بنابراین اگر چیزی وجود نداشته باشد یا نادرست تعریف شده باشد، ممکن است برنامه تولیدشده خطاهای زمان کامپایل (compile-time) یا زمان اجرا (run-time) داشته باشد. این راهنما مثالهایی از تعاریف API در رایجترین موارد ارائه میدهد.
برنامه re2v دارای سه حالت (flavor) برای API است که مجموعه اصلی پریمیتیوهای استفادهشده توسط برنامه را مشخص میکنند:
- Simple API
- این API پیشفرض برای بکاند V است. این رابط از پریمیتیوهای زیر تشکیل شده است: YYINPUT (که باید به عنوان توالیای از واحدهای کد، مانند یک رشته تعریف شود) و YYCURSOR، YYMARKER، YYCTXMARKER، YYLIMIT (که باید بهصورت اندیسهایی در YYINPUT تعریف شوند).
- Record API
- رابط Record API در مواردی مفید است که وضعیت لکسر باید در یک struct ذخیره شود. این رابط با گزینه --api record یا پیکربندی re2c:api = record فعال میشود. این API شامل متغیر yyrecord است (این نام را میتوان با re2c:yyrecord بازنویسی کرد) که باید به عنوان یک struct با فیلدهای yyinput، yycursor، yymarker، yyctxmarker، yylimit تعریف شود (تنها فیلدهایی که توسط کد تولیدشده استفاده میشوند باید تعریف شوند، و نام آنها نیز قابل پیکربندی است).
- Generic API
- این انعطافپذیرترین API است. با گزینه --api generic یا پیکربندی re2c:api = generic فعال میشود. این رابط شامل پریمیتیوهایی برای عملیات عمومی است: YYPEEK، YYSKIP، YYBACKUP، YYBACKUPCTX، YYSTAGP، YYSTAGN، YYMTAGP، YYMTAGN، YYRESTORE، YYRESTORECTX، YYRESTORETAG، YYSHIFT، YYSHIFTSTAG، YYSHIFTMTAG، YYLESSTHAN، YYEND.
اینجا فهرست کاملی از پریمیتیوهای 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 مقایسه میکند تا تشخیص دهد که آیا کاراکترهای ورودی کافی باقی مانده است یا خیر.
- YYMARKER
- یک l-value که موقعیت آخرین قاعده تطبیقیافته را ذخیره میکند (یک اشارهگر یا آفست عدد صحیح در YYINPUT). این مقدار برای بازیابی موقعیت YYCURSOR در صورتی استفاده میشود که تطبیق طولانیتر شکست بخورد و لکسر نیاز به بازگشت به عقب (rollback) داشته باشد. نیازی به مقداردهی اولیه نیست.
- YYCTXMARKER
- یک l-value که موقعیت بافت پسآیند (trailing context) را ذخیره میکند (یک اشارهگر یا آفست عدد صحیح در YYINPUT). نیازی به مقداردهی اولیه نیست. YYCTXMARKER تنها در صورتی نیاز است که عملگر پیشنگری / استفاده شده باشد.
- YYFILL
- یک پریمیتیو Generic API با یک متغیر len. دستور YYFILL باید دستکم len کاراکتر ورودی دیگر فراهم کند یا شکست بخورد. اگر re2c:eof استفاده شود، آنگاه len همیشه 1 است و YYFILL باید همیشه به تابع فراخواننده بازگردد؛ مقدار بازگشتی صفر نشاندهنده موفقیت است. اگر re2c:eof استفاده نشود، مقدار بازگشتی YYFILL نادیده گرفته میشود و در صورت شکست نباید بازگردد. حداکثر مقدار len برابر YYMAXFILL است.
- YYFN
- یک پریمیتیو که پیشنمونه تابع (function prototype) را در مدل کد --recursive-functions تعریف میکند. مقدار آن باید آرایهای از یک یا چند رشته باشد، که در آن هر رشته شامل دو یا سه مؤلفه است که با رشته مشخصشده در پیکربندی re2c:fn:sep (معمولاً یک نقطهویرگول) از یکدیگر جدا شدهاند. اولین عنصر آرایه، نام تابع و نوع بازگشتی را مشخص میکند (برای یک تابع void خالی است). عناصر بعدی آرگومانهای تابع را تعریف میکنند: نخست، عبارتی برای آرگومانِ استفادهشده در بدنه تابع (معمولاً فقط یک نام)؛ دوم، نوع آرگومان؛ سوم، یک پارامتر صوری اختیاری (به طور پیشفرض مؤلفه اول است - معمولاً هم آرگومان و هم پارامتر یک شناسه یکسان هستند).
- YYINPUT
- یک r-value که توالی کاراکتر ورودی فعلی (رشته، بافر و غیره) را ذخیره میکند.
- YYMAXFILL
- یک ثابت عدد صحیح برابر با حداکثر مقدار آرگومان ورودی به YYFILL. این مقدار میتواند با یک بلوک max تولید شود.
- YYLESSTHAN
- یک پریمیتیو Generic API با یک متغیر len. باید به عنوان یک r-value از نوع بولی تعریف شود که برابر با true است اگر و تنها اگر کمتر از len کاراکتر ورودی باقی مانده باشد.
- YYEND
- یک پریمیتیو Generic API بدون متغیر. باید به عنوان یک r-value از نوع بولی تعریف شود که برابر با true است اگر و تنها اگر انتهای منطقی ورودی فرارسیده باشد (بدون در نظر گرفتن هرگونه نویسههای فاصلهپرکن یا نشانگرهای نگهبان). YYEND برای پیادهسازی نماد $ در عبارات منظم به کار میرود. این پریمیتیو با YYLESSTHAN که برای اطمینان از فراتر نرفتن خواندن لکسر از انتهای بافر استفاده میشود، تفاوت دارد.
- YYPEEK
- یک پریمیتیو Generic API بدون متغیر. باید به عنوان یک r-value از نوع YYCTYPE تعریف شود که برابر با کاراکتر موجود در موقعیت فعلی ورودی است.
- YYSKIP
- یک پریمیتیو Generic API که باید موقعیت فعلی ورودی را به اندازه یک واحد کد به جلو ببرد.
- YYBACKUP
- یک پریمیتیو Generic API که باید موقعیت فعلی ورودی را ذخیره کند (تا بعداً با YYRESTORE بازیابی شود).
- YYRESTORE
- یک پریمیتیو Generic API که باید موقعیت ورودی فعلی را به مقدار ذخیرهشده توسط YYBACKUP بازیابی کند.
- YYBACKUPCTX
- یک پریمیتیو Generic API که باید موقعیت ورودی فعلی را به عنوان موقعیت بافت پسآیند (trailing context) ذخیره کند (تا بعداً با YYRESTORECTX بازیابی شود).
- YYRESTORECTX
- یک پریمیتیو Generic API که باید موقعیت بافت پسآیند ذخیرهشده توسط YYBACKUPCTX را بازیابی کند..TP YYRESTORETAG یک پریمیتیو عمومی API با یک متغیر tag که باید موقعیت زمینهٔ پسین (trailing context) را به مقدار 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 واحد کد (code unit) جابهجا کند (مقدار جابهجایی ممکن است منفی باشد).
- YYSHIFTMTAG
- یک پریمیتیو عمومی API با دو متغیر tag و shift که باید آخرین مقدار در تاریخچهٔ tag را به اندازهٔ shift واحد کد جابهجا کند (مقدار جابهجایی ممکن است منفی باشد).
- YYMAXNMATCH
- یک ثابت عدد صحیح برابر با حداکثر تعداد گروههای ضبط POSIX در یک قاعده. این مقدار با یک بلوک maxnmatch تولید میشود.
- YYCONDTYPE
- نوع دادهٔ شمارشی وضعیت (condition enum). این نوع میتواند با بلوک 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-ام درست (true) است اگر و تنها اگر واحد کد n-ام در محدودهٔ گذار k-ام باشد. ایدهٔ این نگاشت بیتی جایگزینی بسیاری از شاخههای if یا حالتهای switch با بررسی یک بیت واحد در جدول است.
- 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
فعال
میشود.
برخی از گزینهها دارای پیکربندیهای متناظر هستند، برخی دیگر سراسری بوده و پس از شروع خواندن فایل ورودی توسط re2c قابل تغییر نیستند. گزینههای اشکالزدایی عموماً مستلزم ساخت re2c در پیکربندی اشکالزدایی هستند. گزینههای داخلی برای آزمایش با الگوریتمهای بهکاررفته در re2c مفید هستند.
- -? --help -h
- نمایش پیام راهنما.
- --api <simple | record | generic>
- مشخص کردن API استفادهشده توسط کد تولیدشده برای برقراری ارتباط با کد تعریفشده توسط کاربر. گزینهٔ simple باید در موارد سادهای استفاده شود که نیازی به پر کردن مجدد بافر و ذخیرهٔ وضعیت تحلیلگر واژگانی نیست. گزینهٔ record باید زمانی استفاده شود که وضعیت تحلیلگر واژگانی باید در یک رکورد (مانند struct، class و غیره) ذخیره شود. گزینهٔ generic باید در موارد پیچیدهای استفاده شود که دو API دیگر به اندازهٔ کافی انعطافپذیر نیستند.
- --bit-vectors -b
- بهینهسازی پرشهای شرطی با استفاده از ماسکهای بیتی. این گزینه مستلزم --nested-ifs است.
- --captures, --leftmost-captures
- فعالسازی استخراج تطبیق فرعی با گروههای تسخیرکنندهٔ حریصانه از چپترین حالت (leftmost greedy). نتیجه در آرایهٔ yybmatch با ظرفیت 2 * YYMAXNMATCH جمعآوری شده، و yynmatch به تعداد گروهها برای قاعدهٔ منطبق تنظیم میشود.
- --captvars, --leftmost-captvars
- فعالسازی استخراج تطبیق فرعی با گروههای تسخیرکنندهٔ حریصانه از چپترین حالت. نتیجه برای k-امین گروه تسخیرکننده در متغیرهای yytl<k> و yytr<k> جمعآوری میشود.
- --case-insensitive
- رفتار با رشتههای داخل گیومهٔ تکی و جفتی به صورت غیرحساس به بزرگی و کوچکی حروف.
- --case-inverted
- معکوس کردن مفهوم رشتههای داخل گیومهٔ تکی و جفتی: رفتار با رشتههای داخل گیومهٔ تکی به صورت حساس به بزرگی و کوچکی حروف و رشتههای داخل گیومهٔ جفتی به صورت غیرحساس به بزرگی و کوچکی حروف.
- --case-ranges
- فشردهسازی caseهای متوالی در دستورهای switch به یک بازه با قالب low ... high. این نحو یک افزونهٔ زبان C/C++ است که توسط کامپایلرهایی مانند GCC، Clang و Tcc پشتیبانی میشود. مزیت اصلی نسبت به استفاده از caseهای تکی، حجم کمتر کد تولیدشده و زمان تولید سریعتر است، اگرچه برای برخی از کامپایلرها مانند Tcc منجر به حجم باینری کوچکتر نیز میشود.
- --computed-gotos -g
- بهینهسازی پرشهای شرطی با استفاده از افزونهٔ غیراستاندارد "computed goto" (که باید توسط کامپایلر پشتیبانی شود). re2v جدولهای پرش را تنها در موارد پیچیده با انشعابهای شرطی فراوان تولید میکند. آستانهٔ پیچیدگی را میتوان با پیکربندی cgoto:threshold تعیین کرد. انحرافهای نسبی را میتوان با پیکربندی cgoto:relative فعال کرد. این گزینه مستلزم --bit-vectors است.
- --computed-gotos-relative
- مشابه --computed-gotos اما در عوض تولید انحرافهای نسبی برای جدولهای پرش (که باید توسط کامپایلر پشتیبانی شود). این گزینه مستلزم --computed-gotos است.
- --conditions --start-conditions -c
- فعالسازی پشتیبانی از "conditions" مشابه Flex: چندین تحلیلگر واژگانی مرتبط با یکدیگر در یک بلوک. این گزینه جایگزینی برای تعیین دستی بلوکهای مختلف re2v متصلشده با goto یا فراخوانیهای تابع است.
- --depfile FILE
- نوشتن اطلاعات وابستگی در FILE در قالب یک قاعدهٔ Makefile به صورت <output-file> : <input-file> [include-file ...]. این قابلیت امکان ردیابی وابستگیهای ساخت را در حضور بلوکها/دستورالعملهای include فراهم میکند، به طوری که بهروزرسانی فایلهای include باعث تولید مجدد فایل خروجی میشود. این گزینه به گزینهٔ --output وابسته است.
- --ebcdic --ecb -e
- تولید یک تحلیلگر واژگانی که ورودی را با کدگذاری EBCDIC میخواند. re2v فرض میکند که بازهٔ نویسهها 0 -- 0xFF و اندازهٔ نویسه ۱ بایت است.
- --empty-class <match-empty | match-none | error>
- تعیین نحوهٔ برخورد re2v با کلاسهای نویسهٔ خالی. با match-empty (پیشفرض)، کلاس خالی با ورودی خالی تطبیق مییابد (که غیرمنطقی است، اما سازگار با گذشته است). با match-none کلاس خالی همواره در تطبیق ناموفق است. با error کلاس خالی یک خطای کامپایل ایجاد میکند.
- --encoding-policy <fail | substitute | ignore>
- تعیین نحوهٔ برخورد re2v با جانشینهای یونیکد (Unicode surrogates). با fail در صورت مواجهه با یک جانشین، re2v با خطا متوقف میشود. با substitute برنامهٔ re2v بدون هشدار جانشینها را با نقطه کد خطای 0xFFFD جایگزین میکند. با ignore (پیشفرض) re2v با جانشینها به عنوان نقاط کد معمولی رفتار میکند. استاندارد یونیکد بیان میکند که جانشینهای مستقل نامعتبر هستند، اما کتابخانهها و برنامههای دنیای واقعی رفتارهای متفاوتی دارند.
- --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 مفید است. re2v فایل FILE را در دایرکتوری فایل والد و در مکانهای include مشخصشده با گزینهٔ -I جستجو میکند.
- --input <default | custom>
- نام مستعار منسوخشده برای --api. گزینهٔ default متناظر با simple است (در واقع برای بیشتر backendها پیشفرض است، اما نه برای همه). گزینهٔ custom متناظر با generic است.
- --input-encoding <ascii | utf8>
- مشخص کردن نحوهٔ تجزیهٔ عبارتهای باقاعده توسط re2v. با ascii (پیشفرض) re2v با ورودی به عنوان کدگذاریشده با ASCII رفتار میکند: هر دنبالهای از واحدهای کد، دنبالهای از نویسههای ۱ بایتی مستقل است. با utf8 برنامهٔ re2v با ورودی به عنوان کدگذاریشده با 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، که در آن وضعیتهای جداگانه caseهای switch هستند. وضعیت در متغیر yystate ذخیره میشود. گذارهای بین وضعیتها، yystate را به برچسب case وضعیت مقصد بهروزرسانی کرده و اجرا را به ابتدای حلقه ادامه میدهند.
- --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) خودداری کند، چرا که تحلیلگر واژگانی از قبل بررسی پایان ورودی را به شکلی کارآمدتر انجام میدهد. کاربر ممکن است تصمیم بگیرد تعریفی امن برای YYPEEK ارائه دهد، یا تعریفی که تنها در ساختهای انتشار (release) ناامن است، که در این حالت گزینهٔ --no-unsafe به جلوگیری از هشدارهای مربوط به بلوکهای زائد unsafe کمک میکند.
- --output -o OUTPUT
- مشخص کردن فایل OUTPUT.
- --posix-captures, -P
- فعالسازی استخراج تطبیق فرعی با گروههای تسخیرکننده به سبک POSIX. نتیجه در آرایهٔ yybmatch با ظرفیت 2 * YYMAXNMATCH جمعآوری شده، و yynmatch به تعداد گروهها برای قاعدهٔ منطبق تنظیم میشود..TP --posix-captvars فعالسازی استخراج زیرتطابق با گروههای گیرانداز به سبک POSIX. نتیجه در متغیرهای yytl<k> و yytr<k> برای k-اُمین گروه گیرانداز جمعآوری میشود.
- --recursive-functions
- استفاده از مدل کد مبتنی بر توابع بازگشتی همکار (co-recursive)، که در آن هر وضعیت DFA یک تابع مجزا است که میتواند توابع وضعیتهای دیگر یا خودش را فراخوانی کند.
- --reusable -r
- از نسخهٔ 2.2 منسوخ شده است (بلوکهای چندبارمصرف اکنون بهطور پیشفرض مجاز هستند).
- --skeleton -S
- نادیدهگرفتن کد رابط تعریفشده توسط کاربر و تولید یک برنامهٔ "اسکلتی" خودکفا. علاوه بر این، فایلهای ورودی را با رشتههای مشتقشده از گرامر باقاعده و نتایج تطابق فشردهشده تولید میکند که برای تأیید رفتار برنامهٔ "اسکلتی" روی تمام ورودیها استفاده میشوند. این گزینه برای یافتن باگها در بهینهسازیها و تولید کد مفید است. این گزینه تنها برای C پشتیبانی میشود.
- --storable-state -f
- تولید یک تحلیلگر لغوی که میتواند وضعیت درونی خود را ذخیره کند. این ویژگی در تحلیلگرهای لغوی مدل push مفید است که در هنگام ناکافی بودن ورودی، توسط یک برنامهٔ بیرونی متوقف شده و سپس با دردسترس قرار گرفتن ورودی بیشتر، ادامه مییابند. در این حالت، کاربران باید عملگرهای اولیهٔ YYGETSTATE و YYSETSTATE را نیز تعریف کنند، و متغیرهای yych، yyaccept و state باید بخشی از وضعیت ذخیرهشدهٔ تحلیلگر لغوی باشند.
- --syntax FILE
- بارگذاری پیکربندیها از FILE مشخصشده و اعمال آنها روی فایل نحو پیشفرض. توجه داشته باشید که FILE میتواند تنها چند پیکربندی را تعریف کند (اگر برای اصلاح فایل نحو پیشفرض استفاده شود)، یا میتواند یک بکاند کامل زبان جدید را تعریف کند (در مورد اخیر توصیه میشود از گزینهٔ --lang none استفاده شود).
- فعالسازی استخراج زیرتطابق با برچسبها.
- --ucs2 --wide-chars -w
- تولید یک تحلیلگر لغوی که ورودی با کدگذاری UCS2 را میخواند. re2v فرض میکند که بازهٔ نویسهها 0 -- 0xFFFF و اندازهٔ نویسه ۲ بایت است. این گزینه مستلزم --nested-ifs است.
- --utf8 --utf-8 -8
- تولید یک تحلیلگر لغوی که ورودی را در کدگذاری UTF-8 میخواند. re2v فرض میکند که بازهٔ نویسهها 0 -- 0x10FFFF و اندازهٔ نویسه ۱ بایت است.
- --utf16 --utf-16 -x
- تولید یک تحلیلگر لغوی که ورودی با کدگذاری UTF16 را میخواند. re2v فرض میکند که بازهٔ نویسهها 0 -- 0x10FFFF و اندازهٔ نویسه ۲ بایت است. این گزینه مستلزم --nested-ifs است.
- --utf32 --unicode -u
- تولید یک تحلیلگر لغوی که ورودی با کدگذاری UTF32 را میخواند. re2v فرض میکند که بازهٔ نویسهها 0 -- 0x10FFFF و اندازهٔ نویسه ۴ بایت است. این گزینه مستلزم --nested-ifs است.
- --verbose
- چاپ یک پیام کوتاه در صورت موفقیت.
- --vernum -V
- نمایش اطلاعات نسخه در قالب MMmmpp (اصلی، فرعی، وصله).
- --version -v
- نمایش اطلاعات نسخه.
- --single-pass -1
- منسوخ شده است. کاری انجام نمیدهد (تکگذره اکنون پیشفرض است).
- --debug-output -d
- درج فراخوانیهای YYDEBUG در کد تولیدشده. این برای ردیابی اجرای تحلیلگر لغوی مفید است.
- --dump-adfa
- گزینهٔ اشکالزدایی: خروجی دادن DFA پس از تونلزنی (در قالب .dot).
- --dump-cfg
- گزینهٔ اشکالزدایی: خروجی دادن گراف جریان کنترل متغیرهای برچسب (در قالب .dot).
- --dump-closure-stats
- گزینهٔ اشکالزدایی: خروجی دادن آمار مربوط به تعداد وضعیتها در بستار.
- --dump-dfa-det
- گزینهٔ اشکالزدایی: خروجی دادن DFA بلافاصله پس از قطعیسازی (در قالب .dot).
- --dump-dfa-min
- گزینهٔ اشکالزدایی: خروجی دادن DFA پس از کمینهسازی (در قالب .dot).
- --dump-dfa-tagopt
- گزینهٔ اشکالزدایی: خروجی دادن DFA پس از بهینهسازیهای برچسب (در قالب .dot).
- --dump-dfa-tree
- گزینهٔ اشکالزدایی: خروجی دادن DFA در حال ساخت با وضعیتهایی که به صورت درختهای تاریخچهٔ برچسب نمایش داده شدهاند (در قالب .dot).
- --dump-dfa-raw
- گزینهٔ اشکالزدایی: خروجی دادن DFA در حال ساخت با مجموعهوضعیتهای گسترشیافته (در قالب .dot).
- --dump-interf
- گزینهٔ اشکالزدایی: خروجی دادن جدول تداخل تولیدشده توسط تحلیل زندهبودن متغیرهای برچسب.
- --dump-nfa
- گزینهٔ اشکالزدایی: خروجی دادن NFA (در قالب .dot).
- --emit-dot -D
- به جای خروجی عادی، گراف تحلیلگر لغوی را در قالب .dot تولید میکند. خروجی را میتوان با کمک Graphviz به یک تصویر تبدیل کرد (مانند دستوری شبیه به dot -Tpng -odfa.png dfa.dot).
- --dfa-minimization <moore | table>
- گزینهٔ داخلی: الگوریتم کمینهسازی DFA مورد استفاده توسط re2v. گزینهٔ moore الگوریتم مور (پیشفرض) است. گزینهٔ table الگوریتم "پر کردن جدول" است. هر دو الگوریتم باید تا حد برچسبگذاری مجدد وضعیتها، DFA یکسانی تولید کنند؛ الگوریتم پر کردن جدول سادهتر و بسیار کُندتر بوده و به عنوان یک پیادهسازی مرجع عمل میکند.
- --eager-skip
- گزینهٔ داخلی: وادار کردن تحلیلگر لغوی تولیدشده به پیشروی مشتاقانهٔ موقعیت ورودی -- بلافاصله پس از خواندن نماد ورودی. این گزینه رفتار پیشفرض را که در آن موقعیت ورودی با تأخیر (lazily) -- پس از انتقال به وضعیت بعدی -- پیش میرود تغییر میدهد.
- --no-lookahead
- گزینهٔ داخلی، منسوخشده. در گذشته برای فعالسازی الگوریتم TDFA(0) به کار میرفت. بر خلاف TDFA(1)، الگوریتم TDFA(0) از پیشنگری تکنمادی استفاده نمیکند. این الگوریتم عملیاتهای ثبات را به جای انتقالهای خروجی، روی انتقالهای ورودی اعمال میکند. محکها نشان دادند که الگوریتم TDFA(0) کارایی کمتری نسبت به TDFA(1) دارد.
- --no-optimize-tags
- گزینهٔ داخلی: جلوگیری از بهینهسازی متغیرهای برچسب (مفید برای اشکالزدایی).
- --posix-closure <gor1 | gtop>
- گزینهٔ داخلی: تعیین الگوریتم کوتاهترین مسیر مورد استفاده برای ساخت بستار اپسیلون با معناشناسی رفع ابهام POSIX: گزینهٔ gor1 (پیشفرض) نشاندهندهٔ الگوریتم گلدبرگ-رادزیک، و gtop نشاندهندهٔ الگوریتم "ترتیب توپولوژیکی سراسری" است.
- --posix-prectable <complex | naive>
- گزینهٔ داخلی: تعیین الگوریتم مورد استفاده برای محاسبهٔ جدول تقدم POSIX. الگوریتم complex جدول تقدم را در یک پیمایش از درخت تاریخچهٔ برچسب محاسبه میکند و دارای پیچیدگی درجه دو نسبت به تعداد وضعیتهای TNFA است؛ این گزینه پیشفرض میباشد. الگوریتم naive در بدترین حالت دارای پیچیدگی درجه سه نسبت به تعداد وضعیتهای TNFA است، اما بسیار سادهتر از complex بوده و ممکن است در موارد غیربحرانی کمی سریعتر باشد.
- --stadfa
- گزینهٔ داخلی، منسوخشده. در گذشته برای فعالسازی الگوریتم staDFA به کار میرفت، که تفاوت آن با TDFA در این است که عملیاتهای ثبات به جای قرار گرفتن روی انتقالها، در وضعیتها قرار میگیرند. محکها نشان دادند که الگوریتم staDFA کارایی کمتری نسبت به TDFA دارد.
- --fixed-tags <none | toplevel | all>
- گزینهٔ
داخلی:
تعیین
اینکه آیا
بهینهسازی
برچسبِثابت
باید روی
همهٔ
برچسبها
(all)،
هیچکدام
از آنها
(none)، یا فقط
برچسبهای
موجود در
الحاق
بالاترین
سطح (toplevel)
اعمال شود
یا خیر.
پیشفرض all
است.
برچسبهای
"ثابت"
برچسبهایی
هستند که در
فاصلهٔ
مشخص و
ثابتی نسبت
به برچسب
دیگری (به
نام "پایه")
قرار دارند.
در چنین
مواردی فقط
برچسب پایه
نیاز به
ردیابی
دارد، و
مقدار
برچسب ثابت
میتواند
از روی
مقدار
برچسب پایه
بهعلاوهٔ
یک انحراف (offset)
ایستا
محاسبه شود.
برای
برچسبهایی
که تحت شروط
جایگزین یا
تکرار
هستند،
بررسی
اینکه آیا
برچسب پایه
مقدار عدم
تطابق دارد
یا خیر نیز
لازم است
(در این
صورت برچسب
ثابت نیز
باید بدون
در نظر
گرفتن
مقدار
انحراف،
روی عدم
تطابق
تنظیم شود).
برای
برچسبهای
موجود در
الحاق
بالاترین
سطح، این
بررسی لازم
نیست زیرا
آنها
همیشه
مطابقت
دارند.
هشدارها را میتوان بهصورت جداگانه فعال، غیرفعال یا به یک خطا تبدیل کرد.
- -W
- فعال کردن تمام هشدارها.
- -Werror
- تبدیل هشدارها به خطا. توجه داشته باشید که این گزینه بهتنهایی هیچ هشداری را فعال نمیکند؛ تنها بر هشدارهایی اثر میگذارد که تاکنون فعال شدهاند یا بعداً فعال خواهند شد.
- -W<warning>
- فعال کردن warning.
- -Wno-<warning>
- غیرفعال کردن warning.
- -Werror-<warning>
- فعال کردن warning و در نظر گرفتن آن بهعنوان خطا (شامل -W<warning> میشود).
- -Wno-error-<warning>
- در نظر نگرفتن این warning خاص بهعنوان خطا. این کار خود هشدار را غیرفعال نمیکند.
- -Wcondition-order
- هشدار در صورتی که برنامهٔ تولیدشده فرضیات ضمنی دربارهٔ شمارهگذاری وضعیتها داشته باشد. کاربر باید یا از گزینهٔ --header و یا از بلوک conditions برای تولید نگاشت نامهای وضعیت به شمارهها استفاده کرده و سپس از نامهای وضعیت بهطور خودکار تولیدشده استفاده کند.
- -Wempty-character-class
- هشدار در صورتی که یک عبارت باقاعده شامل یک ردهٔ نویسهای خالی باشد. تلاش برای تطبیق یک ردهٔ نویسهای خالی معنایی ندارد: همیشه باید با شکست مواجه شود. با این حال، به دلایل سازگاری با گذشته، re2v ردههای نویسهای خالی را مجاز دانسته و با آنها مانند رشتههای خالی رفتار میکند. برای تغییر رفتار پیشفرض از گزینهٔ --empty-class استفاده کنید.
- -Wmatch-empty-string
- هشدار در صورتی که یک قاعده تهیپذیر باشد (با یک رشتهٔ خالی تطبیق یابد). اگر تحلیلگر لغوی در یک حلقه اجرا شود و تطبیق خالی ناخواسته باشد، تحلیلگر لغوی ممکن است بهطور غیرمنتظرهای در یک حلقهٔ بیپایان گیر کند.
- -Wswapped-range
- هشدار در صورتی که حد پایین یک بازه از حد بالای آن بزرگتر باشد. رفتار پیشفرض این است که بدون صدا مرزهای بازه جابهجا شوند.
- -Wundefined-control-flow
- هشدار در صورتی که برخی رشتههای ورودی باعث جریان کنترل تعریفنشده در تحلیلگر لغوی شوند (الگوهای معیوب گزارش میشوند). این یک اشتباه خطرناک و رایج است. میتوان آن را بهسادگی با افزودن قاعدهٔ پیشفرض * برطرف کرد که کمترین اولویت را دارد، با هر واحد کد تطبیق مییابد و همیشه یک واحد کد را مصرف میکند.
- -Wunreachable-rules
- هشدار دربارهٔ قواعدی که توسط قواعد دیگر پوشانده شدهاند و هرگز تطبیق نخواهند یافت.
- -Wdeprecated-eof_rule
- هشدار دربارهٔ قواعد مستقل پایان ورودی $ که با تغییرات آینده از کار خواهند افتاد و نیاز به اصلاح دارند. در حال حاضر این قواعد در هنگام تداخل با سایر قواعد اولویت دارند، اما پس از معرفی نماد تعمیمیافتهٔ پایان ورودی $ ترتیب اولویت تغییر خواهد کرد و این قواعد توسط قواعد دیگر پوشانده خواهند شد.
- -Wuseless-escape
- هشدار در صورتی که یک نماد گریز (escape) داده شود در حالی که نیازی به آن نبوده است. بهطور پیشفرض، re2v چنین گریزهایی را بدون صدا نادیده میگیرد، اما این موضوع میتواند نشاندهندهٔ یک غلط املایی یا خطا در توالی گریز باشد.
- -Wnondeterministic-tags
- هشدار در صورتی که یک برچسب دارای درجهٔ n-ام از غیرقطعی بودن باشد، جایی که n بزرگتر از ۱ است.
- -Wsentinel-in-midrule
- هشدار در صورتی که نماد نگهبان در میانهٔ یک قاعده رخ دهد --- این امر ممکن است باعث خواندن فراتر از انتهای بافر، خرابی برنامه یا تخریب حافظه در تحلیلگر لغوی تولیدشده شود. این هشدار تنها در صورتی کاربرد دارد که از روش نماد نگهبان برای بررسی پایان ورودی استفاده شود. اگر از پیکربندی re2c:sentinel استفاده شود، این هشدار به خطا تبدیل میشود.
- -Wundefined-syntax-config
- هشدار در
صورتی که
فایل نحو
مشخصشده
با گزینهٔ
--syntax فاقد
تعاریف
برخی از
پیکربندیها
باشد. این
موضوع به
نگهداری
فایلهای
نحو
تعریفشده
توسط کاربر
کمک میکند:
اگر در
نسخهٔ
جدیدی
پیکربندیهایی
افزوده
شود، فایل
نحو قدیمی
هشداری
صادر خواهد
کرد و کاربر
مطلع خواهد
شد. اگر
برخی از
پیکربندیها
استفاده
نمیشوند و
نیازی به
تعریف
ندارند،
باید
صراحتاً
روی <undefined>
تنظیم شوند.
پشتیبانی از زبانهای گوناگون در re2c بر پایه ایده فایلهای نحو (syntax files) بنا شده است. یک فایل نحو، فایلی پیکربندی است که نحو زبان مقصد را تعریف میکند -- نه کل زبان، بلکه بخش کوچکی از آن را که توسط کد تولیدشده استفاده میشود. فایلهای نحو re2c را بسیار انعطافپذیر میکنند، اما نباید به عنوان جایگزینی برای پیکربندیهای re2c: استفاده شوند: هدف آنها تعریف نحو زبان مقصد است، نه سفارشیسازی یک تحلیلگر لغوی (lexer) خاص. تمام زبانهای پشتیبانیشده دارای فایلهای نحو پیشفرضی هستند که بخشی از توزیع به شمار میروند (زیردایرکتوری include/syntax را ببینید)؛ آنها همچنین در فایل باینری re2v نیز تعبیه شدهاند. کاربران میتوانند یک فایل نحو سفارشی ارائه دهند که چند پیکربندی را برای یکی از زبانهای پشتیبانیشده بازنویسی میکند، یا میتوانند همه پیکربندیها را بازتعریف کنند (در این حالت گزینه --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
- فهرستی از مقصدهای تولید کد (codegen) پشتیبانیشده با عناصر ممکن code، dot، skeleton.
- supported_features
- فهرستی از ویژگیهای پشتیبانیشده با عناصر ممکن nested-ifs، bitmaps، computed-gotos، case-ranges، monadic، unsafe، tags، captures، captvars.
پیکربندیهای زبان (Language configurations)
- semicolons
- غیرصفر در صورتی که زبان پس از دستورها (statements) از نقطه-ویرگول استفاده کند.
- backtick_quoted_strings
- غیرصفر در صورتی که زبان دارای رشتههای محصور در بکتیک باشد.
- single_quoted_strings
- غیرصفر در صورتی که زبان دارای رشتههای محصور در کوتیشن تکی باشد.
- indentation_sensitive
- غیرصفر در صورتی که زبان به تورفتگی حساس باشد.
- 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> است. متغیرها جاینماهایی (placeholders) هستند که در طول فاز تولید کد جایگزین میشوند. متغیرهای فهرست خاص هستند: هنگام بسط دادن الگوهای فهرست، re2v عبارتهای سمت راست دونقطه را چند بار تکرار میکند و هر بار رخدادهای متغیر فهرست را با مقداری مخصوص این تکرار جایگزین میسازد. فهرستها دارای کرانهای اختیاری هستند (مقادیر منفی از انتها شمارش میشوند، برای نمونه -1 به معنای آخرین عنصر است). نامهای شرطی با یک نقطه شروع میشوند. هر دوی شرطها و متغیرها میتوانند محلی (مخصوص الگوی کد دادهشده) یا سراسری (مجاز در تمام الگوهای کد) باشند. هنگامی که re2v فایل نحو را میخواند، بررسی میکند که هر الگوی کد فقط از متغیرها و شرطهایی استفاده کند که در آن مجاز هستند.
برای نمونه، الگوی کد زیر ساختار 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
}
در ادامه فهرستی از تمام الگوهای کد پشتیبانیشده توسط re2v به همراه متغیرها و شرطهای محلی آنها آورده شده است. توجه داشته باشید که یک تعریف خاص ممکن است، اما مجبور نیست که از متغیرها و شرطهای محلی استفاده کند. هر الگوی کد استفادهنشده باید روی <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
- تعریف یک شمارش (ممکن است با استفاده از ساختار زبانی ویژهای برای شمارشها تعریف شود، یا صرفاً بهصورت چند مقدار ثابت مستقل). متغیرهای پشتیبانیشده عبارتند از type (نوع شمارشی تعریفشده توسط کاربر یا نوع مقادیر ثابت)، elem (متغیر فهرستی که به نام هر عضو بسط مییابد) و init (مقداردهی اولیه برای هر عضو). شرطها: .init (در صورت وجود مقداردهی اولیه، درست است).
- code:enum_elem
- عنصر شمارش (عضوی از نوع شمارشی تعریفشده توسط کاربر یا نام یک مقدار ثابت، بسته به نحوه تعریف code:enum). متغیرهای پشتیبانیشده عبارتند از name (نام عنصر) و type (نوع آن).
- code:assign
- دستور انتساب. متغیرهای پشتیبانیشده عبارتند از lhs (سمت چپ) و rhs (سمت راست).
- code:type_int
- نوع عدد صحیح علامتدار.
- code:type_uint
- نوع عدد صحیح بدون علامت.
- code:type_yybm
- نوع عناصر در جدول yybm.
- code:type_yytarget
- نوع عناصر در جدول yytarget.
- code:type_yyctable
- نوع عناصر در جدول yyctable.
- code:cmp_eq
- عملگر "برابر".
- code:cmp_ne
- عملگر "نابرابر".
- code:cmp_lt
- عملگر "کوچکتر از".
- code:cmp_gt
- عملگر "بزرگتر از".
- code:cmp_le
- عملگر "کوچکتر یا مساوی".
- code:cmp_ge
- عملگر "بزرگتر یا مساوی".
- 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
- مورد پیشفرض switch (بخش default).
- code:loop
- حلقهای که تا ابد اجرا میشود (مگر اینکه از درون بدنه حلقه متوقف شود). متغیرهای پشتیبانیشده: label (برچسب حلقه)، stmt (یک متغیر فهرستی که به تمام دستورات موجود در بدنه حلقه بسط مییابد).
- code:continue
- دستور continue. متغیرهای پشتیبانیشده: label (برچسبی که اجرای برنامه از آن ادامه مییابد).
- code:goto
- دستور goto. متغیرهای پشتیبانیشده: label (برچسب هدف پرش).
- code:cgoto
- دستور goto محاسبهشده. متغیرهای پشتیبانیشده: array (جدول حاوی اطلاعات goto محاسبهشده)، index (شاخص عنصر در جدول) و base (برچسب پایه، تنها در صورتی استفاده میشود که .cgoto.relative درست باشد).
- code:cgoto:data
- عبارت مقداردهی اولیه برای یک عنصر واحد در جدول goto محاسبهشده. متغیرهای پشتیبانیشده: label (برچسبی که برای مقداردهی اولیه عنصر جاری استفاده میشود)، type (نوع زیرین عناصر در جدول) و base (برچسب پایه - تنها در صورتی استفاده میشود که .cgoto.relative درست باشد).
- code:fndecl
- اعلان تابع. متغیرهای پشتیبانیشده: 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 اثر انگشت در بالای فایل خروجی تولیدشده. متغیرهای پشتیبانیشده: ver (نسخه re2v که برای تولید این فایل استفاده شد) و 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 نگاشت میشود).
در اینجا فهرستی از تمام متغیرهای سراسری مجاز در فایلهای نحو آمده است:
در اینجا فهرستی از تمام شرطهای سراسری مجاز در فایلهای نحو آمده است:
- .api.simple
- درست است اگر از API ساده استفاده شود (--api simple یا re2c:api = simple).
- .api.generic
- درست است اگر از API عمومی استفاده شود (--api generic یا re2c:api = generic).
- .api.record
- درست است اگر از API رکورد استفاده شود (--api record یا re2c:api = record).
- .api_style.functions
- درست است اگر سبک API تابعمانند استفاده شود (re2c:api-style = functions).
- .api_style.freeform
- درست است اگر از سبک API فرمآزاد استفاده شود (re2c:api-style = free-form).
- .case_ranges
- درست است اگر ویژگی بازههای case فعال باشد (--case-ranges یا re2c:case-ranges = 1).
- .cgoto.relative
- درست است اگر شکل نسبی goto محاسباتی استفاده شود (--computed-gotos-relative یا re2c:cgoto:relative = 1).
- .code_model.goto_label
- درست است اگر مدل کد مبتنی بر goto/label استفاده شود (--goto-label).
- .code_model.loop_switch
- درست است اگر مدل کد مبتنی بر loop/switch استفاده شود (--loop-switch).
- .code_model.recursive_functions
- درست است اگر مدل کد مبتنی بر توابع بازگشتی استفاده شود (--recursive-function).
- .date
- درست است اگر اثرانگشت تولیدشده باید حاوی تاریخ تولید باشد.
- .loop_label
- درست است اگر حلقههای تولیدشده توسط re2v باید دارای برچسب باشند (re2c:label:yyloop بر روی یک رشتهٔ غیرخالی تنظیم شده باشد).
- .monadic
- درست است اگر کد تولیدشده باید مونادیک باشد (re2c:monadic = 1). این گزینه تنها برای زبانهای تابعی محض کاربرد دارد.
- .start_conditions
- درست است اگر شرایط شروع فعال باشند (--start-conditions).
- .storable_state
- درست است اگر وضعیت قابل ذخیرهسازی فعال باشد (--storable-state).
- .unsafe
- درست است اگر re2v برای تولید کد سریعتر باید از بلوکهای "unsafe" استفاده کند (--unsafe، re2c:unsafe = 1). این تنها برای زبانهایی که دارای ویژگی "unsafe" هستند کاربرد دارد.
- .version
- درست است اگر اثرانگشت تولیدشده باید حاوی نسخهٔ re2v باشد.
- .yyfill.enable
- درست است اگر YYFILL فعال باشد (re2c:yyfill:enable = 1).
- .yyfn.throw
- درست است اگر پیکربندی re2c:yyfn:throw روی یک رشتهٔ غیرخالی تعریف شده باشد.
یکی از مسائل اصلی تحلیلگر لغوی (lexer)، تشخیص زمان توقف است. چندین شرط خاتمهدهنده وجود دارد:
- تحلیلگر ممکن است با قاعدهای (شامل قاعده پیشفرض *) تطبیق یافته و به یک وضعیت نهایی برسد
- تحلیلگر ممکن است در تطبیق با هر قاعدهای شکست بخورد و به یک وضعیت پیشفرض برسد
- تحلیلگر ممکن است به پایان ورودی برسد
دو شرط نخست، تحلیلگر را به روشی «طبیعی» خاتمه میدهند: تحلیلگر به وضعیتی بدون هیچ گذار خروجی میرسد و تطبیق بهطور خودکار متوقف میشود. شرط سوم، یعنی پایان ورودی، متفاوت است: این حالت ممکن است در هر وضعیتی رخ دهد و تحلیلگر باید قادر به مدیریت آن باشد. بررسی پایان ورودی، روند عادی کار تحلیلگر را متوقف میکند و انشعابهای شرطی به برنامه تولیدشده میافزاید؛ بنابراین لازم است تعداد این بررسیها به حداقل برسد. re2v از چندین روش مختلف برای مدیریت پایان ورودی پشتیبانی میکند. اینکه از کدام روش استفاده شود، به پیچیدگی عبارات منظم (regular expressions)، نیاز به بافرسازی، ملاحظات کارایی و عوامل دیگر بستگی دارد. در ادامه فهرستی از این روشها آورده شده است:
- Sentinel (دیدهبان). این روش نیاز به بررسیهای پایان ورودی را بهکلی برطرف میکند. این روش ساده و کارآمد است، اما محدود به مواردی است که یک نویسهٔ «دیدهبان» طبیعی وجود داشته باشد که هرگز در ورودی معتبر رخ ندهد. این نویسه ممکن است همچنان در ورودیهای نامعتبر وجود داشته باشد، اما نباید در عبارات منظم مجاز شمرده شود، مگر شاید به عنوان آخرین نویسهٔ یک قاعده. نویسهٔ دیدهبان به انتهای ورودی الحاق میشود و نقش یک سیگنال توقف را ایفا میکند: هنگامی که تحلیلگر لغوی این نویسه را بخواند، یا یک خطای نحوی رخ داده است یا پایان ورودی فرا رسیده است. در هر دو حالت، تحلیلگر باید متوقف شود. این روش در صورتی به کار میرود که YYFILL با re2c:yyfill:enable = 0; غیرفعال شده و re2c:eof دارای مقدار پیشفرض -1 باشد.
- Sentinel with bounds checks (دیدهبان با بررسی مرزها). این روش عمومی است: امکان مدیریت هر ورودی را بدون محدودیت روی عبارات منظم فراهم میکند. ایدهٔ این روش کاهش تعداد بررسیهای پایان ورودی با انجام آنها تنها روی نویسههایی مشخص است. مشابه روش «دیدهبان»، یکی از نویسهها به عنوان «دیدهبان» انتخاب شده و به انتهای ورودی الحاق میشود. با این حال، هیچ محدودیتی برای محل وقوع دیدهبان وجود ندارد (در واقع هر نویسهای میتواند به عنوان دیدهبان انتخاب شود). هنگامی که تحلیلگر این نویسه را بخواند، علاوه بر آن بررسی مرز (bounds check) را نیز انجام میدهد. اگر موقعیت فعلی درون مرزها باشد، تحلیلگر تطبیق را از سر میگیرد و با نویسهٔ دیدهبان مانند یک نویسهٔ عادی رفتار میکند. در غیر این صورت، YYFILL را فراخوانی میکند (مگر اینکه غیرفعال شده باشد). اگر ورودی بیشتری ارائه شود، تحلیلگر نویسهٔ آخر را دوباره تطبیق داده و چنان ادامه میدهد که گویی دیدهبانی وجود نداشته است. در غیر این صورت، این حالت باید پایان واقعی ورودی باشد و تحلیلگر متوقف میشود. این روش زمانی استفاده میشود که re2c:eof مقداری غیرمنفی داشته باشد (باید روی مقدار عددی دیدهبان تنظیم شود). استفاده از YYFILL اختیاری است.
- Bounds checks with padding (بررسی مرزها با لایهگذاری). این روش عمومی است و ممکن است سریعتر از روش «دیدهبان با بررسی مرزها» باشد، اما پیچیدهتر نیز هست. ایدهٔ این روش، افراز وضعیتهای DFA به مؤلفههای قویاً متصل (SCCها) و ایجاد یک بررسی واحد برای هر SCC به تعداد نویسههای کافی است تا طولانیترین مسیر بدون حلقه در این SCC پوشش داده شود. این کار تعداد بررسیها را کاهش میدهد، اما برای نشانههای لغوی (lexemes) کوتاه در پایان ورودی مشکلی ایجاد میکند، زیرا بررسی به نویسههای کافی برای پوشش طولانیترین نشانه نیاز دارد. این مشکل را میتوان با لایهگذاری (padding) ورودی با چند نویسهٔ ساختگی که پسوند نشانهای معتبر تشکیل نمیدهند (بهطوری که تحلیلگر نتواند آنها را تطبیق دهد) برطرف کرد. طول لایهگذاری باید برابر با YYMAXFILL باشد که با یک بلوک max تولید میشود. اگر ورودی کافی نباشد، تحلیلگر YYFILL را فراخوانی میکند که باید حداقل تعداد نویسههای مورد نیاز را فراهم سازد یا اینکه بازگشتی نداشته باشد. این روش در صورتی به کار میرود که YYFILL فعال باشد و re2c:eof برابر -1 باشد (این پیکربندی پیشفرض است).
- Custom checks (بررسیهای سفارشی). رابط برنامهنویسی عمومی (Generic API) امکان بازنویسی عملیات پایه مانند خواندن یک نویسه را فراهم میکند، که این امر گنجاندن بررسیهای پایان ورودی را به عنوان بخشی از آنها ممکن میسازد. این رویکرد مستعد خطا است و باید با احتیاط استفاده شود. برای استفاده از یک روش سفارشی، API عمومی را با --api custom یا re2c:api = custom; فعال کرده و بررسیهای پیشفرض مرز را با re2c:yyfill:enable = 0; یا re2c:yyfill:check = 0; غیرفعال کنید.
زیربخشهای بعدی شامل مثالی از هر روش هستند..SS سنتینل (Sentinel)
این مثال از یک نویسهٔ سنتینل (sentinel) برای مدیریت پایان ورودی استفاده میکند. این برنامه کلمات جداشده با فاصله را در یک رشتهٔ پایانیافته با null میشمارد. سنتینل همان null است: آخرین نویسهٔ هر رشتهٔ ورودی است و طبق هیچیک از قواعد در میانهٔ یک جزء واژگانی (lexeme) مجاز نیست (بهویژه در بازههای نویسهای که نادیده گرفتن آن آسان است گنجانده نشده است). اگر یک null در میانهٔ یک رشته رخ دهد، یک خطای نحوی است و تحلیلگر لغوی با قاعدهٔ پیشفرض * تطابق مییابد، اما فراتر از انتهای ورودی را نمیخواند و دچار فروپاشی (crash) نمیشود (از هشدار -Wsentinel-in-midrule و پیکربندی re2c:sentinel برای بررسی این موضوع استفاده کنید). پیکربندی re2c:yyfill:enable = 0; از تولید بررسیهای محدوده و فراخوانیهای YYFILL جلوگیری میکند.
// re2v $INPUT -o $OUTPUT
// Expect a null-terminated string.
fn lex(yyinput string) int {
mut yycursor := 0
mut count := 0
loop: /*!re2c
re2c:yyfill:enable = 0;
* { return -1 }
[\x00] { return count }
[a-z]+ { count += 1; unsafe { goto loop } }
[ ]+ { unsafe { goto loop } }
*/
}
fn main() {
assert lex("\0") == 0
assert lex("one two three\0") == 3
assert lex("f0ur\0") == -1
}
سنتینل با بررسی محدوده (Sentinel with bounds checks)
این مثال از سنتینل به همراه بررسیهای محدوده برای مدیریت پایان ورودی استفاده میکند (این روش در نسخهٔ 1.2 اضافه شد). این برنامه رشتههای محصور در علامت نقلقول تکی (single-quoted) جداشده با فاصله را میشمارد. نویسهٔ سنتینل، null است که با پیکربندی re2c:eof = 0; مشخص میشود. همانند روش sentinel، نویسهٔ null آخرین نویسهٔ هر رشتهٔ ورودی است، اما در میانهٔ یک قاعده مجاز شمرده میشود (بهعنوان مثال، 'aaa\0aa'\0 ورودی معتبر است، اما 'aaa\0 یک خطای نحوی است). بررسیهای محدوده در هر وضعیتی که با یک نویسهٔ ورودی تطابق دارد تولید میشوند، اما دامنهٔ آنها محدود به شاخهای است که null را مدیریت میکند. بررسیهای محدوده در API عمومی به صورت YYLIMIT <= YYCURSOR یا YYLESSTHAN(1) هستند. اگر شرط بررسی درست باشد، تحلیلگر لغوی به پایان ورودی رسیده است و باید متوقف شود (YYFILL با re2c:yyfill:enable = 0; غیرفعال میشود، زیرا ورودی در یک بافر میگنجد؛ برای مثالی که از YYFILL استفاده میکند، بخش YYFILL with sentinel را ببینید). رسیدن به پایان ورودی سه حالت را ایجاد میکند: اگر تحلیلگر لغوی در وضعیت اولیه باشد، با قاعدهٔ پایان ورودی $ مطابقت مییابد، در غیر این صورت ممکن است به قاعدهٔ تطبیقیافتهٔ قبلی بازگردد (شامل قاعدهٔ پیشفرض *) یا به یک وضعیت پیشفرض برود که منجر به هشدار -Wundefined-control-flow میشود.
// re2v $INPUT -o $OUTPUT
// Expects a null-terminated string.
fn lex(yyinput string) int {
mut yycursor, mut yymarker := 0, 0
yylimit := yyinput.len - 1 // yylimit points at the terminating null
mut count := 0
loop: /*!re2c
re2c:eof = 0;
re2c:yyfill:enable = 0;
str = ['] ([^'\\] | [\\][^])* ['];
* { return -1 }
$ { return count }
str { count += 1; unsafe { goto loop } }
[ ]+ { unsafe { goto loop } }
*/
}
fn main() {
assert lex("\0") == 0
assert lex("'qu\0tes' 'are' 'fine: \\'' \0") == 3
assert lex("'unterminated\\'\0") == -1
}
این مثال از بررسی مرزها با لایهگذاری برای مدیریت پایان ورودی استفاده میکند (این روش بهطور پیشفرض فعال است). این برنامه رشتههای محصور در نقلقول تکی را که با فاصله از هم جدا شدهاند، میشمارد. یک لایهگذاری به اندازهٔ YYMAXFILL نویسهٔ null به انتهای ورودی افزوده شده است، که مقدار YYMAXFILL در آن بهطور خودکار با یک بلوک max تولید میشود. استفاده از null برای لایهگذاری الزامی نیست --- تا زمانی که نویسهها پسوند یک واژهٔ (lexeme) معتبر را نسازند، میتوان از هر نویسهای استفاده کرد (در این مثال، لایهگذاری نباید شامل نقلقول تکی باشد، چرا که ممکن است با پسوند یک رشتهٔ محصور در نقلقول تکی اشتباه گرفته شود). یک قاعدهٔ «توقف» ("stop") وجود دارد که با نخستین نویسهٔ لایهگذاری (null) تطبیق مییابد و تحلیلگر لغوی (lexer) را پایان میدهد (دقت کنید این قاعده بررسی میکند که آیا null در ابتدای لایهگذاری قرار دارد یا خیر، در غیر این صورت یک خطای نحو خواهد بود). بررسیهای مرز تنها در برخی حالتها تولید میشوند که توسط مؤلفههای قویاً همبند خودکارهٔ (اتوماتون) زیرین تعیین میگردند. بررسیها به شکل (YYLIMIT - YYCURSOR) < n یا در API عمومی به صورت YYLESSTHAN(n) هستند، که در آن n حداقل تعداد نویسههای مورد نیاز لکسر برای ادامهٔ کار است (این بدان معنا نیز هست که بررسی مرز بعدی حداکثر در n نویسهٔ دیگر رخ خواهد داد). اگر شرط بررسی برقرار (درست) باشد، لکسر به پایان ورودی رسیده است و YYFILL(n) را فراخوانی میکند که باید یا حداقل n نویسهٔ ورودی را فراهم آورد یا بازگشتی نداشته باشد. در این مثال YYFILL همواره شکست میخورد و لکسر را با یک خطا متوقف میکند (که ایرادی ندارد، زیرا کل ورودی درون یک بافر جا میشود). برای دیدن مثالی که بافر ورودی را با YYFILL دوباره پر میکند، بخش YYFILL with padding را ببینید.
// re2v $INPUT -o $OUTPUT
/*!max:re2c*/
// Expects yymaxfill-padded string.
fn lex(str string) int {
// Pad string with yymaxfill zeroes at the end.
mut yyinput := []u8{len: str.len + yymaxfill}
copy(mut &yyinput, str.bytes())
mut yycursor := 0
yylimit := yyinput.len
mut count := 0
loop: /*!re2c
re2c:YYFILL = "return -1";
str = ['] ([^'\\] | [\\][^])* ['];
[\x00] {
// Check that it is the sentinel, not some unexpected null.
if yycursor - 1 == str.len { return count } else { return -1 }
}
str { count += 1; unsafe { goto loop } }
[ ]+ { unsafe { goto loop } }
* { return -1 }
*/
}
fn main() {
assert lex("") == 0
assert lex("'qu\0tes' 'are' 'fine: \\'' ") == 3
assert lex("'unterminated\\'") == -1
assert lex("'unexpected \00 null\\'") == -1
}
بررسیهای سفارشی (Custom checks)
این مثال از یک روش سفارشی برای مدیریت پایان ورودی بر پایهٔ API عمومی استفاده میکند. این برنامه رشتههای محصور در نقلقول تکی را که با فاصله از هم جدا شدهاند، میشمارد. این برنامه مشابه مثال sentinel است، با این تفاوت که ورودی با null خاتمه نیافته است. برای جبران نبودِ نویسهٔ نگهبان (sentinel) در انتهای ورودی، YYPEEK بازتعریف شده است تا پیش از خواندن نویسهٔ بعدی ورودی، یک بررسی مرز انجام دهد. این روش ناکارآمد است زیرا بررسیها بسیار مکرر انجام میشوند. اگر شرط بررسی برقرار نباشد، YYPEEK نویسهٔ واقعی را برمیگرداند؛ در غیر این صورت یک نویسهٔ نگهبان ساختگی برمیگرداند.
// re2v $INPUT -o $OUTPUT
// Returns "fake" terminating null if cursor has reached limit.
fn peek(str string, cur int) u8 {
return if cur >= str.len { u8(0) } /* fake null */ else { return str[cur] }
}
// Expects a string without terminating null.
fn lex(str string) int {
mut cur := 0
mut count := 0
loop: /*!re2c
re2c:api = generic;
re2c:yyfill:enable = 0;
re2c:YYPEEK = "peek(str, cur)";
re2c:YYSKIP = "cur += 1";
* { return -1 }
[\x00] { return count }
[a-z]+ { count += 1; unsafe { goto loop } }
[ ]+ { unsafe { goto loop } }
*/
}
fn main() {
assert lex("") == 0
assert lex("one two three") == 3
assert lex("f0ur") == -1
}
نیاز به بافرسازی زمانی رخ میدهد که ورودی را نتوان بهطور یکجا در حافظه نگاشت کرد: یا ورودی بسیار بزرگ است، یا به صورت جریانی دریافت میشود (مانند خواندن از یک سوکت). روش معمول در چنین مواردی، تخصیص یک بافر حافظه با اندازهٔ ثابت و پردازش ورودی در تکههایی است که درون بافر جای میگیرند. هنگامی که تکهٔ فعلی پردازش شد، به بیرون منتقل شده و دادههای جدید وارد میشوند. در عمل این موضوع تا حدی پیچیدهتر است، زیرا وضعیت تحلیلگر واژگانی (lexer) تنها از یک موقعیت ورودی تشکیل نشده، بلکه شامل مجموعهای از موقعیتهای وابسته به یکدیگر است:
- مکاننما (cursor): نویسهٔ بعدی ورودی برای خوانده شدن (YYCURSOR در API اشارهگر C یا YYSKIP/YYPEEK در API عمومی)
- حد (limit): موقعیت پس از آخرین نویسهٔ در دسترس ورودی (YYLIMIT در API اشارهگر C، که بهطور ضمنی توسط YYLESSTHAN در API عمومی مدیریت میشود)
- نشانگر (marker): موقعیت تازهترین تطابق، در صورت وجود (YYMARKER در API پیشفرض یا YYBACKUP/YYRESTORE در API عمومی)
- توکن (token): آغاز تکواژهٔ (lexeme) کنونی (در API مربوط به re2v ضمنی است، زیرا برای عملکرد عادی لکسر نیازی به آن نیست و میتواند توسط کاربر تعریف و بهروزرسانی شود)
- نشانگر زمینه (context marker): موقعیت زمینهٔ انتهایی (YYCTXMARKER در API اشارهگر C یا YYBACKUPCTX/YYRESTORECTX در API عمومی)
- متغیرهای برچسب (tag variables): موقعیتهای زیرتطابق (تعریفشده با بلوکهای stags و mtags و عملگرهای پایهٔ API عمومی شامل YYSTAGP/YYSTAGN/YYMTAGP/YYMTAGN)
همهٔ این موارد در هر حالتی به کار نمیروند، اما در صورت استفاده، باید توسط YYFILL بهروزرسانی شوند. تمام موقعیتهای فعال در بخشی بین token و cursor قرار دارند، بنابراین هر چیزی که بین ابتدای بافر و token قرار دارد را میتوان دور ریخت، بخش بین token تا limit باید به ابتدای بافر منتقل شود، و فضای خالی انتهای بافر باید با دادههای جدید پر شود. برای جلوگیری از فراخوانیهای مکرر YYFILL بهتر است تا جای ممکن نویسههای ورودی بیشتری پر شود (حتی اگر نویسههای کمتری برای از سرگیری لکسر کافی باشد). جزئیات پیادهسازی YYFILL بسته به روش مدیریت EOF مورد استفاده کمی متفاوت است: حالت قاعدهٔ EOF تا حدی سادهتر از حالت بررسی مرزها با پدینگ (bounds-checking with padding) است. همچنین توجه داشته باشید که در صورت استفاده از گزینهٔ -f --storable-state، دستور YYFILL بار معنایی کمی متفاوتی خواهد داشت (که در بخش وضعیت قابل ذخیره شرح داده شده است).
YYFILL با نگهبان (YYFILL with sentinel)
اگر از قاعدهٔ EOF استفاده شود، YYFILL یک سازوکار تابعی پایه (function-like primitive) است که هیچ آرگومانی نمیپذیرد و مقداری را برمیگرداند که در برابر صفر بررسی میشود. فراخوانی YYFILL توسط شرط YYLIMIT <= YYCURSOR در API اشارهگر C و YYLESSTHAN() در API عمومی فعال میشود. مقدار بازگشتی غیرصفر به این معناست که YYFILL ناموفق بوده است. یک فراخوانی موفق YYFILL باید حداقل یک نویسه را فراهم کرده و موقعیتهای ورودی را بر این اساس تنظیم کند. حد (Limit) همیشه باید بر روی یک موقعیت پس از آخرین موقعیت ورودی در بافر تنظیم شود، و نویسهٔ موجود در موقعیت limit باید نماد نگهبان (sentinel) مشخصشده توسط پیکربندی 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 استفاده میکند.
// re2v $INPUT -o $OUTPUT
import os
import strings
const bufsize = 4096
struct State {
file os.File
mut:
yyinput []u8
yycursor int
yymarker int
yylimit int
token int
eof bool
}
fn fill(mut st &State) int {
if st.eof { return -1 } // unexpected EOF
// Error: lexeme too long. In real life can reallocate a larger buffer.
if st.token < 1 { return -2 }
// Shift buffer contents (discard everything up to the current token).
copy(mut &st.yyinput, st.yyinput[st.token..st.yylimit])
st.yycursor -= st.token
st.yymarker -= st.token
st.yylimit -= st.token
st.token = 0
// Fill free space at the end of buffer with new data from file.
pos := st.file.tell() or { 0 }
if n := st.file.read_bytes_into(u64(pos), mut st.yyinput[st.yylimit..bufsize]) {
st.yylimit += n
}
st.yyinput[st.yylimit] = 0 // append sentinel symbol
// If read less than expected, this is the end of input.
st.eof = st.yylimit < bufsize
return 0
}
fn lex(mut yyrecord &State) int {
mut count := 0
loop:
yyrecord.token = yyrecord.yycursor
/*!re2c
re2c:api = record;
re2c:eof = 0;
re2c:YYFILL = "fill(mut yyrecord) == 0";
str = ['] ([^'\\] | [\\][^])* ['];
* { return -1 }
$ { return count }
str { count += 1; unsafe { goto loop } }
[ ]+ { unsafe { goto loop } }
*/
}
fn main() {
fname := "input"
content := "'qu\0tes' 'are' 'fine: \\'' ";
// Prepare input file: a few times the size of the buffer, containing
// strings with zeroes and escaped quotes.
mut fw := os.create(fname)!
fw.write_string(strings.repeat_string(content, bufsize))!
fw.close()
count := 3 * bufsize // number of quoted strings written to file
// Prepare lexer state: all offsets are at the end of buffer.
mut fr := os.open(fname)!
mut st := &State{
file: fr,
// Sentinel at `yylimit` offset is set to zero, which triggers YYFILL.
yyinput: []u8{len: bufsize + 1},
yycursor: bufsize,
yymarker: bufsize,
yylimit: bufsize,
token: bufsize,
eof: false,
}
// Run the lexer.
n := lex(mut &st)
if n != count { panic("expected $count, got $n") }
// Cleanup: remove input file.
fr.close()
os.rm(fname)!
}
در حالت پیشفرض (زمانی که از قاعدهٔ EOF استفاده نمیشود)، YYFILL یک سازهٔ اولیه شبهتابع است که یک آرگومان میپذیرد و هیچ مقداری را برنمیگرداند. فراخوانی YYFILL توسط شرط (YYLIMIT - YYCURSOR) < n در رابط برنامهنویسی اشارهگر C و YYLESSTHAN(n) در رابط کاربری عمومی (generic 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) به همراه پدینگ استفاده میکند.
// re2v $INPUT -o $OUTPUT
import os
import strings
/*!max:re2c*/
const bufsize = 4096
struct State {
file os.File
mut:
yyinput []u8
yycursor int
yylimit int
token int
eof bool
}
fn fill(mut st &State, need int) int {
if st.eof { return -1 } // unexpected EOF
// Error: lexeme too long. In real life can reallocate a larger buffer.
if st.token < need { return -2 }
// Shift buffer contents (discard everything up to the current token).
copy(mut &st.yyinput, st.yyinput[st.token..st.yylimit])
st.yycursor -= st.token
st.yylimit -= st.token
st.token = 0
// Fill free space at the end of buffer with new data from file.
pos := st.file.tell() or { 0 }
if n := st.file.read_bytes_into(u64(pos), mut st.yyinput[st.yylimit..bufsize]) {
st.yylimit += n
}
// If read less than expected, this is the end of input.
if st.yylimit < bufsize {
st.eof = true
for i := 0; i < yymaxfill; i += 1 { st.yyinput[st.yylimit + i] = 0 }
st.yylimit += yymaxfill
}
return 0
}
fn lex(mut yyrecord &State) int {
mut count := 0
loop:
yyrecord.token = yyrecord.yycursor
/*!re2c
re2c:api = record;
re2c:YYFILL = "r := fill(mut yyrecord, @@); if r != 0 { return r }";
str = ['] ([^'\\] | [\\][^])* ['];
[\x00] {
// Check that it is the sentinel, not some unexpected null.
return if yyrecord.token == (yyrecord.yylimit - yymaxfill) { count } else { -1 }
}
str { count += 1; unsafe { goto loop } }
[ ]+ { unsafe { goto loop } }
* { return -1 }
*/
}
fn main() {
fname := "input"
content := "'qu\0tes' 'are' 'fine: \\'' ";
// Prepare input file: a few times the size of the buffer, containing
// strings with zeroes and escaped quotes.
mut fw := os.create(fname)!
fw.write_string(strings.repeat_string(content, bufsize))!
fw.close()
count := 3 * bufsize // number of quoted strings written to file
// Prepare lexer state: all offsets are at the end of buffer.
// This immediately triggers YYFILL, as the YYLESSTHAN condition is true.
mut fr := os.open(fname)!
mut st := &State{
file: fr,
yyinput: []u8{len: bufsize + yymaxfill},
yycursor: bufsize,
yylimit: bufsize,
token: bufsize,
eof: false,
}
// Run the lexer.
n := lex(mut &st)
if n != count { panic("expected $count, got $n") }
// Cleanup: remove input file.
fr.close()
os.rm(fname)!
}
بلوکهای چندگانه
گاهی اوقات لازم است چندین تحلیلگر لغوی (lexer) به هم مرتبط وجود داشته باشند (برای مثال، اگر یک ماشین حالت سطحبالا وجود داشته باشد که بین حالتهای مختلف لکسر جابهجا میشود). این کار را میتوان با استفاده از چندین بلوک متصل به هم در re2v پیادهسازی کرد. گزینه دیگر استفاده از start conditions است.
پیادهسازی اتصالات بین بلوکها به زبان مقصد بستگی دارد. در زبانهایی که دارای دستور goto هستند (مانند C/C++ و Go) میتوان همه بلوکها را در یک تابع قرار داد و پیش از هر کدام یک برچسب (label) مشخص کرد. انتقال از یک بلوک به بلوک دیگر با یک goto ساده انجام میشود. در زبانهایی که فاقد goto هستند (مانند Rust)، استفاده از یک حلقه به همراه switch روی متغیر حالت ضروری است، مشابه حلقه/switch مربوط به yystate که توسط re2v تولید میشود؛ یا اینکه هر بلوک داخل یک تابع قرار داده شود و از فراخوانی توابع استفاده گردد.
مثال زیر از چندین بلوک برای تجزیه اعداد دودویی (binary)، هشتهشتی (octal)، دهدهی (decimal) و شانزدهشانزدهی (hexadecimal) استفاده میکند. هر مبنا بلوک مخصوص به خود را دارد. بلوک اولیه مبنا را تعیین کرده و کنترل را به سایر بلوکها هدایت میکند. پیکربندیهای مشترک در یک بلوک جداگانه در ابتدای برنامه تعریف شدهاند؛ این پیکربندیها توسط سایر بلوکها به ارث برده میشوند.
// re2v $INPUT -o $OUTPUT -i
const u32_lim = u64(1) << 32
fn parse_u32(yyinput string) ?u32 {
mut yycursor, mut yymarker := 0, 0
mut n := u64(0)
mut yych := 0
adddgt := fn (num u64, base u64, digit u8) u64 {
n := num * base + u64(digit)
return if n >= u32_lim { u32_lim } else { n }
}
/*!re2c
re2c:yyfill:enable = 0;
re2c:yych:emit = 0;
end = "\x00";
'0b' / [01] { unsafe{ goto bin } }
"0" { unsafe{ goto oct } }
"" / [1-9] { unsafe{ goto dec } }
'0x' / [0-9a-fA-F] { unsafe{ goto hex } }
* { return none }
*/
bin:
/*!re2c
end { unsafe{ goto end } }
[01] { n = adddgt(n, 2, yyinput[yycursor-1] - 48); unsafe{ goto bin } }
* { return none }
*/
oct:
/*!re2c
end { unsafe{ goto end } }
[0-7] { n = adddgt(n, 8, yyinput[yycursor-1] - 48); unsafe{ goto oct } }
* { return none }
*/
dec:
/*!re2c
end { unsafe{ goto end } }
[0-9] { n = adddgt(n, 10, yyinput[yycursor-1] - 48); unsafe{ goto dec } }
* { return none }
*/
hex:
/*!re2c
end { unsafe{ goto end } }
[0-9] { n = adddgt(n, 16, yyinput[yycursor-1] - 48); unsafe{ goto hex } }
[a-f] { n = adddgt(n, 16, yyinput[yycursor-1] - 87); unsafe{ goto hex } }
[A-F] { n = adddgt(n, 16, yyinput[yycursor-1] - 55); unsafe{ goto hex } }
* { return none }
*/
end:
if n < u32_lim {
return u32(n)
}
return none
}
fn main() {
test := fn (num ?u32, str string) {
if n := parse_u32(str) {
if m := num { if n != m { panic("wrong number") } }
} else {
if _ := num { panic("expected none") }
}
}
test(1234567890, "1234567890\0")
test(13, "0b1101\0")
test(0x7fe, "0x007Fe\0")
test(0o644, "0644\0")
test(none, "9999999999\0")
test(none, "123??\0")
}
شرایط آغازین با گزینهٔ --start-conditions فعال میشوند. آنها روشی برای کدگذاری چندین اتوماتون مرتبط در یک بلوک re2v واحد فراهم میکنند.
هر شرط متناظر با یک اتوماتون منفرد است و یک نام یکتا دارد که توسط کاربر مشخص میشود و یک شمارهٔ داخلی یکتا دارد که توسط re2v تعریف میگردد. این شمارهها برای جابهجایی میان شرایط استفاده میشوند: کد تولیدشده از اعمال اولیهٔ YYGETCOND و YYSETCOND برای دریافت شرط کنونی یا تنظیم آن روی شمارهٔ دادهشده بهره میبرد. از بلوک conditions، گزینهٔ --header یا پیکربندی re2c:header برای تولید شناسههای عددی شرایط استفاده کنید. پیکربندی re2c:cond:enumprefix پیشوند شناسههای تولیدشده را مشخص میکند.
در حالت شرط، هر قاعده باید دارای پیشوندی از فهرستی از نامهای شرایط جداشده با ویرگول در قلابهای شکسته باشد، یا یک نویسهٔ عام به صورت <*> برای مشخص کردن تمام شرایط. نحو قاعده به صورت زیر گسترش مییابد:
- < 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 code
- مشابه قاعدهٔ پیشین، با این تفاوت که condition بعدی را تعیین میکند.
- < > :=> condition
- مشابه قاعدهٔ پیشین، با این تفاوت که هیچ کد مرتبطی ندارد و بلافاصله به condition پرش میکند.
کدی که re2v برای شرایط تولید میکند بستگی به این دارد که آیا re2v از رویکرد goto/label یا رویکرد loop/switch برای کدگذاری اتوماتا استفاده میکند یا خیر.
در زبانهایی که دارای دستور goto هستند (مانند C/C++ و Go)، شرایط بهطور طبیعی به صورت بلوکهایی از کد با برچسبهایی به شکل yyc_<cond> پیادهسازی میشوند، که در آن cond نام شرط است (پیشوند برچسب را میتوان با re2c:cond:prefix تغییر داد). گذارها میان شرایط با استفاده از goto و برچسبهای شرایط پیادهسازی میشوند. پیش از تمام شرایط، re2v یک switch اولیه روی YYGETSTATE تولید میکند که به وضعیت آغازین شرط کنونی پرش میکند. قواعد میانبر :=> از switch اولیه گذر کرده و مستقیماً به شرط تعیینشده پرش میکنند (میتوان از re2c:cond:goto برای تغییر رفتار پیشفرض استفاده کرد). قواعد دارای اعمال معنایی بهطور خودکار به شرط بعدی پرش نمیکنند؛ این کار باید توسط کد عمل تعریفشده توسط کاربر انجام شود.
در زبانهایی که فاقد goto هستند (مانند Rust)، ابزار re2v متغیر yystate را برای ذخیرهٔ شمارههای شرایط بازاستفاده میکند. هر شرط یک شناسهٔ عددی برابر با شمارهٔ وضعیت آغازین خود دریافت میکند، و سوئیچ میان شرایط هیچ تفاوتی با سوئیچ میان وضعیتهای DFA یک شرط منفرد ندارد. نیازی به یک switch اولیهٔ جداگانه برای شرایط وجود ندارد. (از آنجا که از همین رویکرد برای پیادهسازی وضعیتهای قابلذخیره استفاده میشود، در صورتی که هم از وضعیتهای قابلذخیره و هم از شرایط استفاده شود، دستورهای YYGETCOND/YYSETCOND مازاد خواهند بود).
برنامهٔ زیر از شرایط آغازین برای تجزیهٔ اعداد باینری، اوکتال، دسیمال و هگزادسیمال استفاده میکند. یک بلوک واحد وجود دارد که در آن هر مبنا شرط مختص به خود را دارد، و شرط اولیه به همهٔ آنها متصل است. متغیر تعریفشده توسط کاربر با نام cond، شمارهٔ شرط فعلی را نگهداری میکند؛ این متغیر با شمارهٔ شرط اولیه که با بلوک conditions تولید شده مقداردهی اولیه میشود.
// re2v $INPUT -o $OUTPUT -ci
/*!conditions:re2c*/
const u32_lim = u64(1) << 32
fn parse_u32(yyinput string) ?u32 {
mut yycursor, mut yymarker := 0, 0
mut n := u64(0)
mut yycond := YYCONDTYPE.yycinit
adddgt := fn (num u64, base u64, digit u8) u64 {
n := num * base + u64(digit)
return if n >= u32_lim { u32_lim } else { n }
}
/*!re2c
re2c:yyfill:enable = 0;
<*> * { return none }
<init> '0b' / [01] :=> bin
<init> "0" :=> oct
<init> "" / [1-9] :=> dec
<init> '0x' / [0-9a-fA-F] :=> hex
<bin, oct, dec, hex> "\x00" { return if n < u32_lim { u32(n) } else { none } }
<bin> [01] { n = adddgt(n, 2, yyinput[yycursor-1] - 48); unsafe{ goto yyc_bin } }
<oct> [0-7] { n = adddgt(n, 8, yyinput[yycursor-1] - 48); unsafe{ goto yyc_oct } }
<dec> [0-9] { n = adddgt(n, 10, yyinput[yycursor-1] - 48); unsafe{ goto yyc_dec } }
<hex> [0-9] { n = adddgt(n, 16, yyinput[yycursor-1] - 48); unsafe{ goto yyc_hex } }
<hex> [a-f] { n = adddgt(n, 16, yyinput[yycursor-1] - 87); unsafe{ goto yyc_hex } }
<hex> [A-F] { n = adddgt(n, 16, yyinput[yycursor-1] - 55); unsafe{ goto yyc_hex } }
*/
}
fn main() {
test := fn (num ?u32, str string) {
if n := parse_u32(str) {
if m := num { if n != m { panic("wrong number") } }
} else {
if _ := num { panic("expected none") }
}
}
test(1234567890, "1234567890\0")
test(13, "0b1101\0")
test(0x7fe, "0x007Fe\0")
test(0o644, "0644\0")
test(none, "9999999999\0")
test(none, "123??\0")
}
با گزینهٔ --storable-state، ابزار re2v یک تحلیلگر لغوی (lexer) تولید میکند که میتواند وضعیت فعلی خود را ذخیره کند، به فراخواننده بازگردد، و بعداً عملیات را دقیقاً از همانجایی که متوقف شده بود از سر بگیرد. حالت پیشفرض عملکرد در re2v یک مدل «کِششی» ("pull") است که در آن تحلیلگر لغوی هر زمان که نیاز داشته باشد، ورودی بیشتری را «میکِشد». این حالت ممکن است در مواردی که ورودی بهصورت تکهتکه در دسترس قرار میگیرد غیرقابلقبول باشد (برای نمونه، اگر تحلیلگر لغوی توسط تجزیهکننده (parser) فراخوانی شود، یا اگر برنامهٔ تحلیلگر لغوی از طریق پروتکل سوکت با برنامهٔ دیگری ارتباط برقرار کند که باید پیش از ارسال پیام بعدی، منتظر پاسخ از سوی تحلیلگر لغوی بماند). ویژگی وضعیت ذخیرهپذیر دقیقاً برای چنین مواردی در نظر گرفته شده است: این ویژگی امکان تولید تحلیلگرهای لغوی را فراهم میکند که در یک مدل «رانشی» ("push") کار میکنند. هنگامی که تحلیلگر لغوی به ورودی بیشتری نیاز داشته باشد، وضعیت خود را ذخیره کرده و به فراخواننده بازمیگردد. بعداً، زمانی که ورودی بیشتری در دسترس قرار گرفت، فراخواننده تحلیلگر لغوی را دقیقاً از همانجایی که متوقف شده بود از سر میگیرد. در مقایسه با مدل «کِششی» ("pull")، چند تغییر ضروری وجود دارد:
- تعریف دستورهای اولیهٔ YYSETSTATE() و YYGETSTATE(state).
- تعریف متغیرهای yych، yyaccept (در صورت استفاده) و state به عنوان بخشی از وضعیت ماندگار تحلیلگر لغوی. متغیر state باید با مقدار -1 مقداردهی اولیه شود.
- دستور YYFILL باید به جای تلاش برای تأمین ورودی بیشتر، به برنامهٔ بیرونی بازگردد. کد بازگشتی باید نشان دهد که تحلیلگر لغوی به ورودی بیشتری نیاز دارد.
- برنامهٔ بیرونی باید موقعیتهایی را که تحلیلگر لغوی به ورودی بیشتری نیاز دارد تشخیص داده و واکنش مناسب نشان دهد.
- استفادهٔ اختیاری از بلوک getstate برای تولید switch مربوط به YYGETSTATE بهصورت مجزا از تحلیلگر لغوی اصلی. این ویژگی تنها برای زبانهایی کار میکند که دارای goto هستند (نه در حالت --loop-switch).
- استفاده از re2c:eof و روش sentinel with bounds checks برای مدیریت انتهای ورودی. روش مبتنی بر فاصلهگذاری (padding) ممکن است کار نکند، چرا که زمان الحاق padding نامشخص است: انتهای فعلی ورودی ممکن است انتهای نهایی ورودی نباشد، و الحاق زودهنگام padding ممکن است یک نشانهٔ لغوی حریصانه (greedy lexeme) را که بخشی از آن خوانده شده است قطع کند. افزون بر این، با توجه به منطق سطحبالای برنامه، دریافت ورودی بیشتر ممکن است به پردازش نشانهٔ لغوی در انتهای بافر وابسته باشد (که از پیش به دلیل شرط انتهای ورودی مسدود شده است).
در اینجا نمونهای از یک تحلیلگر لغوی مدل «رانشی» ("push") آورده شده است که خواندن بستهها از یک سوکت را شبیهسازی میکند. تحلیلگر لغوی تا زمانی که با انتهای ورودی مواجه شود در حلقه اجرا میشود و به تابع فراخواننده بازمیگردد. تابع فراخواننده با «ارسال» بستهٔ بعدی، ورودی بیشتری را فراهم کرده و تحلیل لغوی را از سر میگیرد. این فرآیند زمانی متوقف میشود که همهٔ بستهها ارسال شده باشند، یا خطایی رخ دهد.
// re2v -f $INPUT -o $OUTPUT
import log
import os
// Use a small buffer to cover the case when a lexeme doesn't fit.
// In real world use a larger buffer.
const bufsize = 10
struct State {
mut:
file os.File
yyinput []u8
yycursor int
yymarker int
yylimit int
token int
yystate int
}
enum Status {
lex_end
lex_ready
lex_waiting
lex_bad_packet
lex_big_packet
}
fn fill(mut st &State) Status {
shift := st.token
used := st.yylimit - st.token
free := bufsize - used
// Error: no space. In real life can reallocate a larger buffer.
if free < 1 { return .lex_big_packet }
// Shift buffer contents (discard already processed data).
copy(mut &st.yyinput, st.yyinput[shift..shift+used])
st.yycursor -= shift
st.yymarker -= shift
st.yylimit -= shift
st.token -= shift
// Fill free space at the end of buffer with new data.
pos := st.file.tell() or { 0 }
if n := st.file.read_bytes_into(u64(pos), mut st.yyinput[st.yylimit..bufsize]) {
st.yylimit += n
}
st.yyinput[st.yylimit] = 0 // append sentinel symbol
return .lex_ready
}
fn lex(mut yyrecord &State, mut recv &int) Status {
mut yych := u8(0)
/*!getstate:re2c*/
loop:
yyrecord.token = yyrecord.yycursor
/*!re2c
re2c:api = record;
re2c:eof = 0;
re2c:YYFILL = "return .lex_waiting";
packet = [a-z]+[;];
* { return .lex_bad_packet }
$ { return .lex_end }
packet { recv += 1; unsafe{ goto loop } }
*/
}
fn test(expect Status, packets []string) {
// Create a pipe (open the same file for reading and writing).
fname := "pipe"
mut fw := os.create(fname) or { panic("cannot create file") }
mut fr := os.open(fname) or { panic("cannot open file") }
// Initialize lexer state: `state` value is -1, all offsets are at the end
// of buffer.
mut st := &State{
file: fr,
// Sentinel at `yylimit` offset is set to zero, which triggers YYFILL.
yyinput: []u8{len: bufsize + 1},
yycursor: bufsize,
yymarker: bufsize,
yylimit: bufsize,
token: bufsize,
yystate: -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.
mut status := Status.lex_ready
mut send := 0
mut recv := 0
for {
status = lex(mut &st, mut &recv)
if status == .lex_end {
break
} else if status == .lex_waiting {
if send < packets.len {
log.debug("sending packet $send")
fw.write_string(packets[send]) or { panic("cannot write to file") }
fw.flush()
send += 1
}
status = fill(mut &st)
log.debug("filled buffer $st.yyinput, status $status")
if status != .lex_ready {
break
}
} else if status == .lex_bad_packet {
break
}
}
// Check results.
if status != expect || (status == .lex_end && recv != send) {
panic("expected $expect with $send packet(s), got $status with $recv packet(s)")
}
// Cleanup: remove input file.
fr.close()
fw.close()
os.rm(fname) or { panic("cannot remove file") }
}
fn main() {
//log.set_level(.debug)
test(.lex_end, [])
test(.lex_end, ["zero;", "one;", "two;", "three;", "four;"])
test(.lex_bad_packet, ["??;"])
test(.lex_big_packet, ["looooooooooooong;"])
}
بلوکهای با قابلیت استفاده مجدد به فرم /*!rules:re2c[:<name>] ... */ یا %{rules[:<name>] ... %} را میتوان به هر تعداد بار دوباره استفاده کرد و با دیگر بلوکهای re2v ترکیب نمود. بخش <name> اختیاری است. یک بلوک rules میتواند در یک بلوک یا دستورالعمل use به کار رود. کدِ مربوط به یک بلوک rules در هر نقطه از استفاده تولید میشود.
بلوکهای use با /*!use:re2c[:<name>] ... */ یا %{use[:<name>] ... %} تعریف میشوند. بخش <name> اختیاری است: اگر مشخص نشده باشد، بلوک rules مرتبط، نزدیکترین بلوک پیشین خواهد بود (خواه نامدار باشد یا بینام). یک بلوک use میتواند تعاریف نامدار، پیکربندیها و قواعد مخصوص به خود را اضافه کند. یک مورد کاربرد مهم برای بلوکهای use، تحلیلگر لغوی (lexer) است که از چندین کدگذاری ورودی پشتیبانی میکند: همان بلوک rules چندین بار با پیکربندیهای مختصِ کدگذاری مجدداً استفاده میشود (مثال زیر را ببینید).
دستورالعمل درونبلوکی !use:<name>; میتواند از داخل یک بلوک re2v استفاده شود. این دستورالعمل، بلوک ارجاعدادهشدهی <name> را در بلوک فعلی ادغام میکند. اگر برخی از قواعد و پیکربندیهای ادغامشده با موارد از پیش تعریفشده همپوشانی داشته باشند، تداخلها به شیوه معمول برطرف میشوند: نخستین قاعده اولویت دارد و آخرین پیکربندی بر موارد قبلی اعمال میشود. یک استثنا، قواعد ویژه *، $ و (در حالت شرطی) <!> هستند که برای آنها تعریف محلی درون بلوک، بر هر تعریف موروثی اولویت دارد. دستورالعمل use به شما امکان میدهد بلوکهای مختلف re2v را در یک بلوک با هم ترکیب کنید (مثال زیر را ببینید).
بلوکهای نامدار و دستورالعمل درونبلوکی use در نگارش 2.2 از re2v اضافه شدند. از آن نگارش به بعد، بلوکهای با قابلیت استفاده مجدد به صورت پیشفرض مجاز هستند (به گزینه خاصی نیاز نیست). پیش از نگارش 2.2، حالت استفاده مجدد با گزینه -r --reusable فعال میشد. پیش از نگارش 1.2، امکان ترکیب بلوکهای با قابلیت استفاده مجدد با بلوکهای عادی وجود نداشت.
مثالی از یک دستورالعمل !use (Example of a !use directive)
// re2v $INPUT -o $OUTPUT
// 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.
enum What {
color
fish
dunno
}
/*!rules:re2c:colors
* { panic("eh!") }
"red" | "salmon" | "magenta" { return .color }
*/
/*!rules:re2c:fish
* { panic("oh!") }
"haddock" | "salmon" | "eel" { return .fish }
*/
fn lex(yyinput string) What {
mut yycursor, mut yymarker := 0, 0
/*!re2c
re2c:yyfill:enable = 0;
!use:fish;
!use:colors;
* { return .dunno } // overrides inherited '*' rules
*/
}
fn main() {
assert lex("salmon") == .fish
assert lex("what?") == .dunno
}
// re2v $INPUT -o $OUTPUT --input-encoding utf8
// 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 }
*/
fn lex_utf8(yyinput []u8) int {
mut yycursor, mut yymarker := 0, 0
/*!use:re2c
re2c:encoding:utf8 = 1;
re2c:YYCTYPE = u8; // the default
*/
}
fn lex_utf32(yyinput []u32) int {
mut yycursor, mut yymarker := 0, 0
/*!use:re2c
re2c:encoding:utf32 = 1;
re2c:YYCTYPE = u32;
*/
}
fn main() {
s8 := [u8(0xe2), u8(0x88), u8(0x80), u8(0x78), u8(0x20), u8(0xe2), u8(0x88), u8(0x83), u8(0x79)]
s32 := [u32(0x2200), u32(0x78), u32(0x20), u32(0x2203), u32(0x79)]
assert lex_utf8(s8) == 0
assert lex_utf32(s32) == 0
}
استخراج زیرتطابق (Submatch extraction)
ابزار re2v دو گزینه برای استخراج زیرتطابق دارد.
- 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 (حالت پیشفرض) برای سیاست حریصانهٔ چپترین و --posix-captures برای سیاست طولانیترین تطابق POSIX است. در این حالت تمام زیرعبارتهای داخل پرانتز به عنوان گروههای گیرانداز در نظر گرفته میشوند، و میتوان از علامت تعجب برای مشخص کردن گروههای غیرگیرانداز استفاده کرد: (! ... ). با گزینهٔ --invert-captures یا پیکربندی re2c:invert-captures = 1 معنای علامت تعجب معکوس میشود. تعداد گروهها برای قاعدهٔ تطبیقیافته در متغیر yynmatch ذخیره میشود (کل عبارت منظم گروه شمارهٔ صفر است)، و نتایج زیرتطابق در آرایهٔ yypmatch ذخیره میشوند. هر دوی yynmatch و yypmatch باید توسط کاربر تعریف شوند، و اندازهٔ yypmatch باید حداقل [yynmatch * 2] باشد. از بلوک maxnmatch برای تعریف YYMAXNMATCH استفاده کنید؛ ثابتی که برابر با بیشینهٔ مقدار yynmatch در میان تمام قواعد است.
- Captvars
- راه دیگر برای استفاده از گروههای گیرانداز، گزینهٔ --captvars یا پیکربندی re2c:captvars = 1 است. تنها تفاوت با --captures در نحوهٔ ذخیرهٔ نتایج زیرتطابق توسط کد تولیدشده است: بهجای yynmatch و yypmatch، ابزار re2v متغیرهای yytl<k> و yytr<k> را برای k-اُمین گروه گیرانداز تولید میکند (کاربر باید این متغیرها را با استفاده از یک بلوک svars اعلان کند). گیراندازی با متغیرها از دو سیاست رفع ابهام پشتیبانی میکند: --leftmost-captvars یا re2c:leftmost-captvars = 1 برای سیاست حریصانهٔ چپترین (پیشفرض) و --posix-captvars یا re2c:posix-captvars برای سیاست طولانیترین تطابق POSIX.
در پشت صحنه، تمام این گزینهها به برچسبها و Tagged Deterministic Finite Automata with Lookahead ترجمه میشوند. ایدهٔ اصلی TDFA به حداقل رساندن بار اضافی (overhead) بر روی استخراج زیرتطابق است. در حالت حدی، اگر هیچ برچسب یا گیراندازی در یک عبارت منظم وجود نداشته باشد، TDFA صرفاً یک DFA معمولی است. اگر تعداد برچسبها متوسط باشد، بار اضافی به سختی قابل توجه خواهد بود. اتوماتون TDFA تولیدشده از تعدادی متغیر برچسب استفاده میکند که مستقیماً به برچسبها نگاشت نمیشوند: یک متغیر واحد ممکن است برای برچسبهای متفاوتی استفاده شود، و یک برچسب ممکن است برای نگهداری تمام مقادیر احتمالی خود به چندین متغیر نیاز داشته باشد. در نهایت ابهام برطرف میشود و تنها یک متغیر نهایی به ازای هر برچسب باقی میماند. متغیرهای برچسب باید با استفاده از بلوکهای stags یا mtags تعریف شوند. اگر وضعیت تحلیلگر لغوی (lexer state) ذخیره میشود، متغیرهای برچسب نیز باید بخشی از آن باشند. آنها همچنین باید توسط YYFILL بهروزرسانی شوند.
برچسبهای S-tag از عملیاتهای زیر پشتیبانی میکنند:
- ذخیرهٔ موقعیت ورودی در یک s-tag: عبارت t = YYCURSOR با API اشارهگر C یا یک عملیات تعریفشده توسط کاربر YYSTAGP(t) با API عمومی
- ذخیرهٔ مقدار پیشفرض در یک s-tag: مقدار t = NULL با API اشارهگر C یا یک عملیات تعریفشده توسط کاربر YYSTAGN(t) با API عمومی
- کپی کردن یک s-tag در دیگری: t1 = t2
برچسبهای M-tag از عملیاتهای زیر پشتیبانی میکنند:
- افزودن موقعیت ورودی به یک m-tag: عملیات تعریفشده توسط کاربر YYMTAGP(t) با هر دو API پیشفرض و عمومی
- افزودن مقدار پیشفرض به یک m-tag: عملیات تعریفشده توسط کاربر YYMTAGN(t) با هر دو API پیشفرض و عمومی
- کپی کردن یک m-tag در دیگری: t1 = t2
برچسبهای S-tag میتوانند بهصورت مقادیر اسکالر (اشارهگرها یا آفستها) پیادهسازی شوند. برچسبهای M-tag به نمایشی پیچیدهتر نیاز دارند، چرا که باید دنبالهای از مقادیر برچسب را ذخیره کنند. سادهترین و ناکارآمدترین نمایش برای یک m-tag، یک فهرست (آرایه، بردار) از مقادیر برچسب است؛ نمایشی کارآمدتر، ذخیرهٔ تمام m-tagها در یک درخت پیشوندی (prefix-tree) است که بهصورت آرایهای از گرههای (v, p) نمایش داده میشود، که در آن v مقدار برچسب و p اشارهگری به گره والد است.
در اینجا یک نمونهٔ ساده از بهکارگیری s-tagها برای تجزیهٔ نگارشهای معنایی متشکل از سه مؤلفهٔ عددی آورده شده است: major، minor و patch (مورد آخر اختیاری است). برای مشاهدهٔ مثالی پیچیدهتر که از YYFILL استفاده میکند، بخش زیر را ببینید.
// re2v $INPUT -o $OUTPUT
struct SemVer {
major int
minor int
patch int
}
fn s2n(s string) int { // convert pre-parsed string to number
mut n := 0
for c in s { n = n * 10 + int(c - 48) }
return n
}
fn parse(yyinput string) ?SemVer {
mut yycursor, mut yymarker := 0, 0
// Final tag variables available in semantic action.
/*!svars:re2c format = 'mut @@ := 0\n'; */
// Intermediate tag variables used by the lexer (must be autogenerated).
/*!stags:re2c format = 'mut @@ := -1\n'; */
/*!re2c
re2c:yyfill:enable = 0;
re2c:tags = 1;
num = [0-9]+;
@t1 num @t2 "." @t3 num @t4 ("." @t5 num)? [\x00] {
return SemVer{
major: s2n(yyinput[t1..t2]),
minor: s2n(yyinput[t3..t4]),
patch: if t5 == -1 { 0 } else { s2n(yyinput[t5..yycursor - 1]) }
}
}
* { return none }
*/
}
fn main() {
test := fn (result ?SemVer, expect ?SemVer) {
if r := result {
if e := expect { if r != e { panic("expected $e, got $r") } }
} else {
if _ := result { panic("expected none") }
}
}
test(parse("23.34\0"), SemVer{23, 34, 0})
test(parse("1.2.9999\0"), SemVer{1, 2, 9999})
test(parse("1.a\0"), none)
}
// re2v $INPUT -o $OUTPUT
import arrays
import os
import strings
const bufsize = 4096
const tag_none = -1
struct State {
file os.File
mut:
yyinput []u8
yycursor int
yymarker int
yylimit int
token int
// Intermediate 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 = "\t@@ int\n"; */
eof bool
}
struct SemVer {
major int
minor int
patch int
}
fn s2n(s []u8) int { // convert pre-parsed string to number
mut n := 0
for c in s { n = n * 10 + int(c - 48) }
return n
}
fn fill(mut st &State) int {
if st.eof { return -1 } // unexpected EOF
// Error: lexeme too long. In real life can reallocate a larger buffer.
if st.token < 1 { return -2 }
// Shift buffer contents (discard everything up to the current token).
copy(mut &st.yyinput, st.yyinput[st.token..st.yylimit])
st.yycursor -= st.token
st.yymarker -= st.token
st.yylimit -= st.token
// Tag variables need to be shifted like other input positions. The check
// for -1 is only needed if some tags are nested inside of alternative or
// repetition, so that they can have -1 value.
/*!stags:re2c format = "\tif st.@@ != -1 { st.@@ -= st.token }\n"; */
st.token = 0
// Fill free space at the end of buffer with new data from file.
pos := st.file.tell() or { 0 }
if n := st.file.read_bytes_into(u64(pos), mut st.yyinput[st.yylimit..bufsize]) {
st.yylimit += n
}
st.yyinput[st.yylimit] = 0 // append sentinel symbol
// If read less than expected, this is the end of input.
st.eof = st.yylimit < bufsize
return 0
}
fn parse(mut st &State) ?[]SemVer {
// Final tag variables available in semantic action.
/*!svars:re2c format = "mut @@ := tag_none\n"; */
mut vers := []SemVer{}
loop:
st.token = st.yycursor
/*!re2c
re2c:api = record;
re2c:yyrecord = st;
re2c:YYFILL = "fill(mut st) == 0";
re2c:tags = 1;
re2c:eof = 0;
num = [0-9]+;
num @t1 "." @t2 num @t3 ("." @t4 num)? [\n] {
ver := SemVer {
major: s2n(st.yyinput[st.token..t1]),
minor: s2n(st.yyinput[t2..t3]),
patch: if t4 == -1 { 0 } else { s2n(st.yyinput[t4..st.yycursor - 1]) }
}
vers = arrays.concat(vers, ver)
unsafe { goto loop }
}
$ { return vers }
* { return none }
*/
}
fn main() {
fname := "input"
content := "1.22.333\n";
// Prepare input file: a few times the size of the buffer, containing
// strings with zeroes and escaped quotes.
mut fw := os.create(fname)!
fw.write_string(strings.repeat_string(content, bufsize))!
fw.close()
// Prepare lexer state: all offsets are at the end of buffer.
mut fr := os.open(fname)!
mut st := &State{
file: fr,
// Sentinel at `yylimit` offset is set to zero, which triggers YYFILL.
yyinput: []u8{len: bufsize + 1},
yycursor: bufsize,
yymarker: bufsize,
yylimit: bufsize,
token: bufsize,
eof: false,
}
// Run the lexer.
expect := []SemVer{len: bufsize, init: SemVer{1, 22, 333}}
result := parse(mut &st) or { panic("parse failed") }
if result != expect { panic("error") }
// Cleanup: remove input file.
fr.close()
os.rm(fname)!
}
در اینجا مثالی از بهکارگیری گروههای ضبط (capturing groups) برای تجزیه نسخهبندیهای معنایی آمده است.
// re2v $INPUT -o $OUTPUT
struct SemVer {
major int
minor int
patch int
}
fn s2n(s string) int { // convert pre-parsed string to number
mut n := 0
for c in s { n = n * 10 + int(c - 48) }
return n
}
fn parse(yyinput string) ?SemVer {
mut yycursor, mut yymarker := 0, 0
// Final tag variables available in semantic action.
/*!svars:re2c format = 'mut @@ := 0\n'; */
// Intermediate tag variables used by the lexer (must be autogenerated).
/*!stags:re2c format = 'mut @@ := 0\n'; */
/*!re2c
re2c:yyfill:enable = 0;
re2c:captvars = 1;
num = [0-9]+;
(num) "." (num) ("." num)? [\x00] {
_ := yytl0; _ := yytr0 // some variables are unused
return SemVer {
major: s2n(yyinput[yytl1..yytr1]),
minor: s2n(yyinput[yytl2..yytr2]),
patch: if yytl3 == -1 {0} else {s2n(yyinput[yytl3 + 1..yytr3])}
}
}
* { return none }
*/
}
fn main() {
test := fn (result ?SemVer, expect ?SemVer) {
if r := result {
if e := expect { if r != e { panic("expected $e, got $r") } }
} else {
if _ := result { panic("expected none") }
}
}
test(parse("23.34\0"), SemVer{23, 34, 0})
test(parse("1.2.9999\0"), SemVer{1, 2, 9999})
test(parse("1.a\0"), none)
}
// re2v $INPUT -o $OUTPUT
import arrays
const mtag_root = -1
const tag_none = -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 MtagElem {
elem int
pred int
}
type MtagTrie = []MtagElem
// Append a single value to an m-tag history.
fn add_mtag(mut trie MtagTrie, mtag int, value int) int {
trie = arrays.concat(trie, MtagElem{value, mtag})
return trie.len - 1
}
// Recursively unwind tag histories and collect version components.
fn unwind(trie MtagTrie, x int, y int, str string) []int {
// Reached the root of the m-tag tree, stop recursion.
if x == mtag_root && y == mtag_root {
return []
}
// Unwind history further.
mut result := unwind(trie, trie[x].pred, trie[y].pred, str)
// Get tag values. Tag histories must have equal length.
if x == mtag_root || y == mtag_root {
panic("tag histories have different length")
}
ex := trie[x].elem
ey := trie[y].elem
if ex != tag_none && ey != tag_none {
// Both tags are valid string indices, extract component.
result = arrays.concat(result, s2n(str[ex..ey]))
} else if !(ex == tag_none && ey == tag_none) {
panic("both tags should be tag_none")
}
return result
}
fn s2n(s string) int { // convert pre-parsed string to number
mut n := 0
for c in s { n = n * 10 + int(c - 48) }
return n
}
fn parse(yyinput string) ?[]int {
mut yycursor, mut yymarker := 0, 0
// mut trie := []MtagElem{}
mut trie := MtagTrie{}
// Final tag variables available in semantic action.
/*!svars:re2c format = 'mut @@ := tag_none\n'; */
/*!mvars:re2c format = "mut @@ := mtag_root\n"; */
// Intermediate tag variables used by the lexer (must be autogenerated).
/*!stags:re2c format = 'mut @@ := tag_none\n'; */
/*!mtags:re2c format = "mut @@ := mtag_root\n"; */
/*!re2c
re2c:tags = 1;
re2c:yyfill:enable = 0;
re2c:YYMTAGP = "@@ = add_mtag(mut trie, @@, yycursor)";
re2c:YYMTAGN = "@@ = add_mtag(mut trie, @@, tag_none)";
num = [0-9]+;
@t1 num @t2 ("." #t3 num #t4)* [\x00] {
mut ver := []int{}
ver = arrays.concat(ver, s2n(yyinput[t1..t2]))
ver = arrays.append(ver, unwind(trie, t3, t4, yyinput))
return ver
}
* { return none }
*/
}
fn main() {
test := fn (result ?[]int, expect ?[]int) {
if r := result {
if e := expect { if r != e { panic("expected $e, got $r") } }
} else {
if _ := result { panic("expected none") }
}
}
test(parse("1\0"), [1])
test(parse("1.2.3.4.5.6.7\0"), [1, 2, 3, 4, 5, 6, 7])
test(parse("1.\0"), none)
}
درک تفاوت میان نقطهکدها (code points) و واحدهای کد (code units) ضروری است. نقطهکد یک شناسه عددی برای یک نماد است. واحد کد کوچکترین واحد ذخیرهسازی در متن کدگذاریشده است. یک نقطهکد منفرد ممکن است با یک یا چند واحد کد نشان داده شود. در کدگذاری با طول ثابت، تمام نقطهکدها با تعداد یکسانی از واحدهای کد نمایش داده میشوند. در کدگذاری با طول متغیر، ممکن است نقطهکدها با تعداد متفاوتی از واحدهای کد نشان داده شوند. توجه داشته باشید که قاعدهٔ «هر نویسه» [^] با هر نقطهکدی مطابقت دارد، اما لزوماً با هر واحد کدی تطبیق پیدا نمیکند (تنها راه تطبیق با هر واحد کد صرفنظر از نوع کدگذاری، قاعدهٔ پیشفرض * است). تحلیلگر لغوی تولیدشده با جریانی از واحدهای کد کار میکند: yych یک واحد کد را ذخیره میکند و YYCTYPE نوع واحد کد است. از سوی دیگر، عبارتهای منظم بر اساس نقطهکدها مشخص میشوند. هنگامی که re2v عبارتهای منظم را به اتوماتا کامپایل میکند، نقطهکدها را به واحدهای کد ترجمه مینماید. این فرایند معمولاً یک نگاشت ساده نیست: در کدگذاریهای با طول متغیر، یک محدوده نقطهکد ممکن است به یک گراف پیچیده از واحدهای کد ترجمه شود. کدگذاریهای زیر پشتیبانی میشوند:
- 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 تعاریف re2v را برای بلوکها، دستهبندیها و ویژگیهای شرحدادهشده در استاندارد یونیکد فراهم میکنند.
گزینهٔ --input-encoding کدگذاری فایل مبدأ را مشخص میکند که میتواند برای فعالسازی لیترالهای یونیکد در عبارتهای منظم استفاده شود. به عنوان مثال --input-encoding utf8 به re2v اعلام میکند که فایل منبع با کدگذاری UTF8 است (این با --utf8 که کدگذاری متن ورودی را تعیین میکند متفاوت است). گزینهٔ --encoding-policy نحوهٔ مدیریت جانشینهای یونیکد (Unicode surrogates؛ نقطهکدها در محدودهٔ [0xD800-0xDFFF]) را توسط re2v مشخص میکند.
در زیر مثالی از یک تحلیلگر لغوی برای شناسههای یونیکد کدگذاریشده با UTF8 آمده است.
// re2v $INPUT -o $OUTPUT --utf8 -si
/*!include:re2c "unicode_categories.re" */
fn lex(yyinput string) int {
mut yycursor, mut yymarker := 0, 0
/*!re2c
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 }
*/
}
fn main() {
if lex("_Ыдентификатор\0") != 0 {
panic("error")
}
}
ابزار re2v به کاربر اجازه میدهد با استفاده از بلوکی به قالب /*!include:re2c FILE */ یا %{include FILE %}، یا یک دستورالعمل درونبلوکی !include FILE ;، فایلهای دیگر را ضمیمه کند؛ که در آن FILE مسیر فایل مورد نظر برای ضمیمهسازی است. re2v فایلهای ضمیمه را در پوشهٔ فایلِ دربرگیرنده و همچنین در مسیرهای ضمیمهسازی که میتوان با گزینهٔ -I تعیین کرد، جستجو میکند. بلوکها/دستورالعملهای include در re2v مانند #include در C/C++ کار میکنند: محتویات FILE عیناً در محل بلوک/دستورالعمل جایگذاری میشوند. فایلهای ضمیمه میتوانند شامل includeهای دیگری نیز باشند. از گزینهٔ --depfile برای ردیابی وابستگیهای ساختِ فایل خروجی به فایلهای ضمیمه استفاده کنید. ابزار re2v تعدادی فایل ضمیمهٔ از پیش تعریفشده ارائه میدهد که در زیرپوشهٔ include/ پروژه قرار دارند. این فایلها حاوی تعاریفی هستند که ممکن است برای پروژههای دیگر نیز مفید باشند (مانند دستهبندیهای یونیکد) و چیزی شبیه به یک کتابخانهٔ استاندارد برای re2v تشکیل میدهند. در ادامه نمونهای از بهکارگیری فایلهای ضمیمه آورده شده است.
فایل ضمیمه ۱ (definitions.v)
enum Result {
ok
fail
}
/*!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)
// re2v $INPUT -o $OUTPUT -i
/*!include:re2c "definitions.v" */
fn lex(yyinput string) Result {
mut yycursor, mut yymarker := 0, 0
/*!re2c
re2c:yyfill:enable = 0;
* { return .fail }
number { return .ok }
!include "extra_rules.re.inc";
*/
}
fn main() {
assert lex("123\0") == .ok
assert lex("123.4567\0") == .ok
}
ابزار re2v این امکان را فراهم میکند که با استفاده از گزینهٔ --header یا پیکربندی re2c:header و جفتبلوکهایی به شکل /*!header:re2c:on*/ و /*!header:re2c:off*/، یا %{header:on%} و %{header:off%}، از فایل ورودی .re فایل سرایند تولید شود. بلوک نخست آغاز فایل سرایند و بلوک دوم پایان آن را مشخص میکند. تمام محتوای میان این بلوکها توسط re2v پردازش شده و کد تولیدشده در فایلی که با گزینهٔ --header یا پیکربندی re2c:header تعیین شده است (یا stdout اگر هیچکدام از گزینه یا پیکربندی استفاده نشده باشد) نوشته میشود. ممکن است در مواردی که از re2v برای تولید تعاریفی استفاده میشود که باید از سایر واحدهای ترجمه (translation units) قابل مشاهده باشند، به فایل سرایندِ تولیدشده بهصورت خودکار نیاز باشد.
در ادامه مثالی از تولید یک فایل سرایند آمده است که شامل تعریف وضعیت تحلیلگر لغوی (lexer state) به همراه متغیرهای برچسب (tag variables) است (تعداد متغیرها به گرامر منظم بستگی دارد و برای برنامهنویس نامشخص است).
فایل ورودی (Input file)
// re2v $INPUT -o $OUTPUT -i --header lexer/state.v
module main
import lexer // the package is generated by re2c
/*!header:re2c:on*/
module lexer
pub struct State {
pub mut:
yyinput string
yycursor int
/*!stags:re2c format="@@ int\n"; */
}
/*!header:re2c:off*/
fn lex(mut yyrecord &lexer.State) int {
mut t := 0
/*!re2c
re2c:header = "lexer/state.v";
re2c:api = record;
re2c:yyfill:enable = 0;
re2c:tags = 1;
[a]* @t [b]* { return t }
*/
}
fn main() {
mut st := &lexer.State{yyinput:"ab\0",}
if lex(mut st) != 1 {
panic("error")
}
}
فایل سرایند (Header file)
// Code generated by re2c, DO NOT EDIT.
module lexer
pub struct State {
pub mut:
yyinput string
yycursor int
yyt1 int
}
با گزینهٔ -S, --skeleton، برنامهٔ re2v تمام کدهای غیر re2v را نادیده گرفته و یک برنامهٔ C خودکفا تولید میکند که میتواند در ادامه کامپایل و اجرا شود. این برنامه شامل کد تحلیلگر لغوی (lexer) و دادههای ورودی است. بهازای هر DFA ساختهشده (بلوک یا شرط)، re2v یک لکسر مستقل و دو فایل تولید میکند: یک فایل .input با رشتههای برگرفته از DFA و یک فایل .keys با نتایج تطابق مورد انتظار. برنامه هر لکسر را روی فایل .input متناظر اجرا کرده و نتایج را با موارد مورد انتظار مقایسه میکند. برنامههای اسکلتی به چند دلیل بسیار مفید هستند:
- آنها میتوانند درستی بهینهسازیهای گوناگون re2v را بسنجند (دادهها در مراحل اولیهٔ فرایند، پیش از انجام هرگونه تبدیل DFA تولید میشوند).
- تولید مجموعهای از دادههای ورودی با پوشش مناسب میتواند هم برای آزمون و هم برای محکزنی (benchmarking) سودمند باشد.
- تولید برنامههای اجرایی خودکفا به فرد امکان میدهد تا به نمونههای آزمون کمینهشده دست یابد (چرا که کد اصلی ممکن است حجیم بوده یا وابستگیهای زیادی داشته باشد).
دشواریِ تولید دادههای ورودی در این است که بهجز در سادهترین حالتها، تعداد رشتههای ورودیِ ممکن بیش از حد زیاد است (حتی اگر طول رشته محدود باشد). re2v این مشکل را با تولید رشتههای کافی برای پوشش تقریباً تمام گذارهای DFA حل میکند. این ابزار از الگوریتم زیر استفاده میکند: ابتدا، اسکلتی از DFA را میسازد. برای کدگذاریهایی با اندازهٔ واحد کد ۱ بایتی (مانند ASCII، UTF-8 و EBCDIC) اسکلت دقیقاً رونوشتی برابر اصل از DFA نخستین است. برای کدگذاریهایی با واحدهای کد چندبایتی، اسکلت رونوشتی از DFA با حذف برخی گذارها است: به این صورت که re2v برای هر بازهٔ پیوسته و مجزا که متناظر با یک گذار DFA است، حداکثر ۲۵۶ واحد کد را در نظر میگیرد. مقادیر انتخابشده بهطور یکنواخت توزیع میشوند و شامل مرزهای بازه نیز هستند. re2v بهجای تلاش برای پوشش تمام مسیرهای ممکن در اسکلت (که کاری ناممکن است)، مسیرهای کافی برای پوشش تمام گذارهای اسکلت را تولید میکند تا پرشهای شرطی متناظر در لکسر فعال شوند. پیادهسازی این الگوریتم به حدود ۱ گیگابایت گذار محدود است و میزان ثابتی از حافظه را مصرف میکند (re2v دادهها را به محض تولید در فایل مینویسد).
بصریسازی و اشکالزدایی (Visualization and debug)
با گزینهٔ -D, --emit-dot، برنامهٔ re2v کدی تولید نمیکند؛ در عوض، DFA تولیدشده را در قالب DOT ذخیره (dump) میکند. میتوان این خروجی را با استفاده از Graphviz یا کتابخانهای دیگر به یک تصویر از DFA تبدیل کرد. توجه داشته باشید که این گزینه DFA نهایی را پس از گذراندن چندین مرحله بهینهسازی و تبدیل نمایش میدهد. مراحل اولیه را میتوان با گزینههای گوناگون اشکالزدایی مانند --dump-nfa، --dump-dfa-raw و غیره استخراج کرد (فهرست کامل گزینهها را ببینید).
همچنین ببینید (SEE ALSO)
میتوانید اطلاعات بیشتری دربارهٔ re2c در وبگاه رسمی آن بیابید: http://re2c.org. برنامههای مشابه عبارتند از flex(1)، lex(1)، quex(http://quex.sourceforge.net).
نویسندگان (AUTHORS)
برنامهٔ re2v در ابتدا توسط 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.