| RE2JAVA(1) | RE2JAVA(1) |
نام (NAME)
re2java - تولید تحلیلگرهای لغوی سریع برای Java
خلاصه دستور (SYNOPSIS)
re2java [ OPTIONS ] [ WARNINGS ] INPUT
ورودی میتواند یک فایل یا - برای stdin باشد.
مقدمه (INTRODUCTION)
ابزار re2java بهعنوان یک پیشپردازنده عمل میکند. این ابزار فایل ورودی (که معمولاً برنامهای در Java است، اما میتواند هر چیز دیگری باشد) را خوانده و به دنبال بلوکهایی از کد میگردد که در نشانگرهای آغاز/پایان با قالبی خاص محصور شدهاند. متنهای خارج از این بلوکها عیناً به فایل خروجی منتقل میشوند. محتویات این بلوکها توسط re2java پردازش میشوند. این ابزار آنها را به کد Java ترجمه کرده و کد تولیدشده را به جای آن بلوک در خروجی قرار میدهد.
در اینجا مثالی از یک برنامه کوچک آورده شده است که بررسی میکند آیا یک رشته دادهشده شامل یک عدد دهدهی است یا خیر:
// re2java $INPUT -o $OUTPUT
class Main {
static boolean lex(String yyinput) {
int yycursor = 0;
/*!re2c
re2c:YYCTYPE = "char";
re2c:YYPEEK = "yyinput.charAt(yycursor)";
re2c:yyfill:enable = 0;
[1-9][0-9]* { return true; }
* { return false; }
*/
}
public static void main(String []args) {
assert lex("1234\0");
}
};
در خروجی، re2java بلوک میانی را با کد تولیدشده جایگزین کرده است:
// Generated by re2java
// re2java $INPUT -o $OUTPUT
class Main {
static boolean lex(String yyinput) {
int yycursor = 0;
{
char yych = 0;
int yystate = 0;
yyl: while (true) {
switch (yystate) {
case 0:
yych = yyinput.charAt(yycursor);
yycursor += 1;
switch (yych) {
case 0x31:
case 0x32:
case 0x33:
case 0x34:
case 0x35:
case 0x36:
case 0x37:
case 0x38:
case 0x39:
yystate = 2;
continue yyl;
default:
yystate = 1;
continue yyl;
}
case 1:
{ return false; }
case 2:
yych = yyinput.charAt(yycursor);
switch (yych) {
case 0x30:
case 0x31:
case 0x32:
case 0x33:
case 0x34:
case 0x35:
case 0x36:
case 0x37:
case 0x38:
case 0x39:
yycursor += 1;
yystate = 2;
continue yyl;
default:
yystate = 3;
continue yyl;
}
case 3:
{ return true; }
default:
throw new IllegalStateException("internal lexer error");
}
}
}
}
public static void main(String []args) {
assert lex("1234\0");
}
};
یک برنامهٔ re2java از دنبالهای از بلوکها (blocks) تشکیل شده است که با کدهای زبان مقصد درآمیخته شدهاند. یک بلوک میتواند شامل تعاریف، پیکربندیها، قواعد، کنشها و دستورالعملها با هر ترتیبی باشد:
- name = regular-expression ;
- یک تعریف، نام name را به عبارت منظم regular-expression مقید میکند. نامها میتوانند شامل نویسههای حرفی-عددی و زیرخط باشند. بخش عبارتهای منظم مروری کلی بر نحو re2java برای عبارتهای منظم ارائه میدهد. پس از تعریف، name میتواند در سایر عبارتهای منظم و در قواعد استفاده شود. بازگشت (توابع بازگشتی) در تعاریف نامگذاریشده مجاز نیست و هر نام باید پیش از استفاده تعریف شده باشد. یک بلوک، تعاریف نامگذاریشده را از حوزهٔ سراسری (global scope) به ارث میبرد. بازتعریف نامی که در حوزهٔ فعلی وجود دارد یک خطا محسوب میشود.
- configuration = value ;
- یک پیکربندی امکان تغییر رفتار re2java و سفارشیسازی کد تولیدشده را فراهم میکند. برای مشاهدهٔ فهرست کامل پیکربندیهای پشتیبانیشده توسط re2java به بخش پیکربندیها مراجعه کنید. بسته به هر پیکربندی مشخص، value میتواند یک کلیدواژه، یک عدد صحیح غیرمنفی یا یک رشتهٔ تکخطی باشد که اگر شامل نویسههای غیر حرفی-عددی باشد باید در کوتیشن تکی یا جفتی قرار گیرد. یک بلوک، پیکربندیها را از حوزهٔ سراسری به ارث میبرد و میتواند آنها را بازتعریف کرده یا موارد جدیدی اضافه کند. پیکربندیهای تعریفشده درون یک بلوک بر کل بلوک تأثیر میگذارند، حتی اگر در انتهای آن قرار گرفته باشند.
- regular-expression code
- یک قاعده، عبارت منظم regular-expression را به کنش معنایی آن (یک بلوک کد درون آکولاد، یا یک بلوک کد که با := شروع شده و به یک خط جدید به همراه هر نویسهٔ غیرفاصلهای ختم میشود) مقید میسازد. اگر regular-expression مطابقت داشته باشد، code مربوطه اجرا میشود. اگر چند قاعده مطابقت داشته باشند، طولانیترین تطابق اولویت دارد. اگر چند قاعده با یک رشتهٔ یکسان تطابق داشته باشند، قاعدهای که زودتر آمده اولویت دارد. دو قاعدهٔ خاص وجود دارد: قاعدهٔ پیشفرض * و قاعدهٔ پایان ورودی $. قاعدهٔ پیشفرض همواره باید تعریف شود؛ این قاعده صرفنظر از جایگاهش در بلوک، کمترین اولویت را دارد و با هر واحد کد (code unit) تطابق مییابد (نه لزوماً یک نویسهٔ معتبر، بخش پشتیبانی از کدگذاری را ببینید). اگر روش متناظر برای مدیریت پایان ورودی استفاده شده باشد، قاعدهٔ پایان ورودی باید تعریف شود. با وجود شرایط شروع، قواعد نحو پیچیدهتری خواهند داشت.
- !action code
- یک کنش، یک بلوک کدِ تعریفشده توسط کاربر (code) را به یک مکان خاص در ماشین حالت متناهی تولیدشده مقید میسازد (همانند کنشهای معنایی که کد را به وضعیتهای نهایی مقید میکنند). برای فهرست کامل کنشهای از پیش تعریفشده بخش کنشها را ببینید.
- !directive ;
- یک دستورالعمل یکی از دستورهای خاصِ از پیش تعریفشده است. هر دستورالعمل هدف منحصربهفردی دارد. برای جزئیات به بخش دستورالعملها مراجعه کنید.
بلوکها (Blocks)
نشانگرهای آغاز و پایان بلوک یا /*!re2c و */ هستند یا %{ و %} (از هر دو شیوه پشتیبانی میشود). از نسخهٔ 2.2 به بعد، بلوکها میتوانند نامهای اختیاری داشته باشند که امکان ارجاع به آنها را در سایر بلوکها فراهم میسازد. انواع مختلفی از بلوکها وجود دارد:
- /*!re2c[:<name>] ... */ or %{[:<name>] ... %}
- یک بلوک سراسری شامل تعاریف، پیکربندیها، قواعد و دستورالعملها است. re2java عبارتهای منظم مربوط به هر قاعده را در قالب یک اتوماتای متناهی معین (DFA) کامپایل میکند، آن را به شکل پرشهای شرطی در زبان مقصد کدگذاری کرده و بلوک را با کد تولیدشده جایگزین میکند. نامها و پیکربندیهای تعریفشده در یک بلوک سراسری به حوزهٔ سراسری افزوده شده و برای بلوکهای بعدی قابل مشاهده میشوند. در آغاز برنامه، حوزهٔ سراسری با گزینههای خط فرمان مقداردهی اولیه میشود.
- /*!local:re2c[:<name>] ... */ or %{local[:<name>] ... %}
- یک بلوک محلی شبیه یک بلوک سراسری است، اما نامها و پیکربندیهای موجود در آن دارای حوزهٔ محلی هستند (بر سایر بلوکها تأثیری نمیگذارند).
- /*!rules:re2c[:<name>] ... */ or %{rules[:<name>] ... %}
- یک بلوک قواعد مانند یک بلوک محلی است، اما خودش به تنهایی کدی تولید نمیکند و هیچ تعریفی نیز به حوزهٔ سراسری اضافه نمینماید -- این بلوک برای استفادهٔ مجدد در سایر بلوکها در نظر گرفته شده است. این روشی برای اشتراکگذاری کد است (جزئیات بیشتر در بخش بلوکهای قابل استفاده مجدد). پیش از نسخهٔ 2.2 نرمافزار re2java، بلوکهای قواعد نیازمند گزینهٔ -r --reusable بودند.
- /*!use:re2c[:<name>] ... */ or %{use[:<name>] ... %}
- یک بلوک use که به یک بلوک قواعد قبلاً تعریفشده ارجاع میدهد. اگر نام مشخص شده باشد، re2java به دنبال بلوک قواعدی با این نام میگردد. در غیر این صورت، نزدیکترین بلوک قواعد قبلی استفاده میشود (چه نامگذاریشده باشد چه بدون نام). یک بلوک use میتواند تعاریف، پیکربندیها و قواعد خاص خود را اضافه کند که به موارد موجود در بلوک قواعد مرجع افزوده میشوند. پیش از نسخهٔ 2.2 نرمافزار re2java، بلوکهای 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 تولیدشده توسط re2java بسط مییابد. یک فهرست اختیاری از نامهای بلوک مشخص میکند که هنگام محاسبهٔ مجموعهٔ متغیرهای برچسب، کدام بلوکها باید لحاظ شوند (اگر فهرست خالی باشد، تمام بلوکها لحاظ میشوند). دو پیکربندی اختیاری وجود دارد: 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) یا بهطور ضمنی توسط re2java تولید شده باشد (با گزینههای --captvars یا --posix-captvars)، بسط مییابد. یک فهرست اختیاری از نامهای بلوک مشخص میکند که هنگام محاسبهٔ مجموعهٔ برچسبها کدام بلوکها باید لحاظ شوند (اگر فهرست خالی باشد، تمام بلوکها لحاظ میشوند). دو پیکربندی اختیاری وجود دارد: format و separator. پیکربندی format یک رشتهٔ الگو را مشخص میکند که در آن @@{tag} (یا به اختصار @@) با نام هر برچسب جایگزین میشود. پیکربندی separator قطعه کدی را مشخص میکند که برای پیوند دادن بخشهای تولیدشدهٔ format برای برچسبهای گوناگون به کار میرود.
- /*!getstate:re2c[:<name1>[:<name2>...]] ... */ or %{getstate[:<name1>[:<name2>...]] ... %}
- بلوکی که ارسال شرطی (conditional dispatch) بر روی وضعیت تحلیلگر لغوی را تولید میکند (به گزینهٔ --storable-state نیاز دارد). یک فهرست اختیاری از نامهای بلوک تعیین میکند که کدام بلوکها باید در ارسال وضعیت گنجانده شوند. گذار پیشفرض به برچسب آغازین اولین بلوک در فهرست میرود. اگر فهرست خالی باشد، تمام بلوکها لحاظ میشوند و گذار پیشفرض به اولین بلوک دارای برچسب آغازین در فایل میرود. این نوع بلوک با گزینهٔ --loop-switch ناسازگار است، چرا که نیازمند گذارهای میانبلوکی است که بدون دستور goto یا فراخوانی توابع پشتیبانی نمیشوند.
- /*!conditions:re2c[:<name1>[:<name2>...]] ... */, /*!types:re2c... */ or %{conditions[:<name1>[:<name2>...]] ... %}, %{types... %}
- بلوکی که شمارش وضعیتها/شرایط (condition enumeration) را تولید میکند (نیازمند گزینهٔ --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 توسط re2java پردازش شده و در فایل سرآیند مشخصشده با گزینهٔ -t --type-header نوشته میشود.
- /*!header:re2c:off*/ or %{header:off %}
- این بلوک نشانگر پایان فایل سرآیندی است که با بلوک header:on*/ آغاز شده بود.
- /*!ignore:re2c ... */ or %{ignore ... %}
- بلوکی که
محتویات آن
نادیده
گرفته شده و
از فایل
خروجی حذف
میشود.
در اینجا فهرست کاملی از پیکربندیهای پشتیبانیشده توسط re2java آورده شده است:
- re2c:api, re2c:input
- مشابه گزینهٔ --api.
- re2c:api:sigil
- نشانگری ("sigil") را مشخص میکند که برای جاینگهدارهای آرگومان در عناصر اولیهٔ API استفاده میشود. مقدار پیشفرض @@ است. یک جاینگهدار با نشانه شروع میشود و پس از آن نام آرگومان در میان آکولادها میآید. برای نمونه، اگر نشانه روی $ تنظیم شده باشد، جاینگهدارها به شکل ${name} خواهند بود. APIهای تکآرگومانی میتوانند از شکل خلاصهشده بدون نام در آکولاد استفاده کنند. این گزینه میتواند با گزینههای مربوط به تکتک عناصر اولیهٔ API بازنویسی شود، مانند re2c:YYFILL@len برای YYFILL.
- re2c:api:style
- سبک API را مشخص میکند. مقادیر ممکن عبارتند از functions (پیشفرض برای C) و free-form (پیشفرض برای Go و Rust). در سبک functions، عناصر اولیهٔ API با فهرستی از آرگومانها درون پرانتز به دنبال نام عنصر اولیه تولید میشوند. آرگومانها تنها برای پارامترهای خودکار تولیدشده (مانند تعداد نویسههای ارسالی به YYFILL) ارائه میشوند و نه برای زمینهٔ عمومی لکسر، بنابراین عناصر اولیه رفتاری شبیه به ماکروها در C/C++ یا بستارها (closures) در Go و Rust دارند. در سبک free-form، عناصر اولیهٔ API شکل ثابتی ندارند: آنها باید بهصورت رشتههایی شامل قطعهکدهای آزاد با متغیرهای درونیابیشده به شکل @@{var} یا @@ تعریف شوند (که با آرگومانها در سبک تابعگونه مطابقت دارند). این پیکربندی میتواند برای تکتک عناصر اولیهٔ API بازنویسی شود؛ برای نمونه، پیکربندی re2c:YYFILL:naked را برای YYFILL ببینید.
- re2c:bit-vectors, re2c:flags:bit-vectors, re2c:flags:b
- مشابه گزینهٔ --bit-vectors، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- re2c:captures, re2c:leftmost-captures
- مشابه گزینهٔ --leftmost-captures، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- re2c:captvars, re2c:leftmost-captvars
- مشابه گزینهٔ --leftmost-captvars، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- re2c:case-insensitive, re2c:flags:case-insensitive
- مشابه گزینهٔ --case-insensitive، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- re2c:case-inverted, re2c:flags:case-inverted
- مشابه گزینهٔ --case-inverted، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- re2c:case-ranges, re2c:flags:case-ranges
- مشابه گزینهٔ --case-ranges، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- re2c:computed-gotos, re2c:flags:computed-gotos, re2c:flags:g
- مشابه گزینهٔ --computed-gotos، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- re2c:computed-gotos:relative, re2c:cgoto:relative
- مشابه گزینهٔ --computed-gotos-relative، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- re2c:computed-gotos:threshold, re2c:cgoto:threshold
- در صورت استفاده از goto محاسبهشده، این پیکربندی آستانهٔ پیچیدگی را مشخص میکند که موجب تولید جدولهای پرش به جای دستورهای شرطی if تودرتو و نگاشتهای بیتی میشود. مقدار پیشفرض 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، با این تفاوت که مسیر فایل نسبی است.
- re2c:indent:string
- رشتهٔ مورد استفاده برای تورفتگی را مشخص میکند. مقدار پیشفرض یک نویسهٔ تب منفرد "\t" است. رشتهٔ تورفتگی باید تنها شامل نویسههای فاصلهٔ خالی باشد. برای غیرفعال کردن کامل تورفتگی، این پیکربندی را روی یک رشتهٔ خالی تنظیم کنید.
- 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
- نام برچسب اختیاری را که پس از switch دستور 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، اما میتواند بر مبنای هر بلوک پیکربندی شود.
- عبارت مورد استفاده برای متغیرهای تگ را مشخص میکند. بهطور پیشفرض re2java عباراتی به شکل 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 تنظیم نشده باشد). این پیکربندی بر تولید کد تأثیری ندارد: هدف آن بررسی این است که نماد نگهبان در میانهٔ یک قاعده (rule) مجاز نباشد و اطمینان حاصل شود که تحلیلگر لغوی فراتر از انتهای بافر را نخواهد خواند. مقدار پیشفرض -1` است (در این حالت re2java فرض میکند که نگهبان صفر است، که رایجترین حالت میباشد). فقط اعداد دهدهی شناسایی میشوند.
- re2c:state:abort
- اگر روی یک مقدار عددی صحیح مثبت تنظیم شود، مورد (case) پیشفرض در توزیع وضعیت (state dispatch) تولیدشده، اجرای برنامه را متوقف (abort) میکند و یک مورد صریح -1 شامل انتقال به ابتدای بلوک خواهد بود.
- re2c:state:nextlabel
- کنترل میکند که آیا پس از ساختار چندشاخهٔ (switch) YYGETSTATE یک برچسب yyNext بیاید یا خیر (مقدار پیشفرض صفر است که متناظر با عدم وجود برچسب میباشد). به عنوان روش جایگزین میتوان از re2c:label:start برای تولید یک برچسب شروع مشخص، یا یک بلوک صریح getstate برای تولید ساختار switch مربوط به 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 پریمیتیو عمومی API یعنی YYSHIFTMTAG را تعریف میکند.
- re2c:YYSHIFTSTAG, re2c:define:YYSHIFTSTAG
- پریمیتیو عمومی API یعنی YYSHIFTSTAG را تعریف میکند.
- re2c:YYSTAGN, re2c:define:YYSTAGN
- پریمیتیو عمومی API یعنی YYSTAGN را تعریف میکند.
- re2c:YYSTAGP, re2c:define:YYSTAGP
- پریمیتیو عمومی 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 در قالب شانزدهشانزدهی (هگزادسیمال) تولید میشوند. مقدار پیشفرض صفر است (نگاشتهای بیتی در قالب دهدهی هستند).
- re2c:yych, re2c:variable:yych
- پریمیتیو API یعنی yych را تعریف میکند.
- re2c:yych:emit, re2c:variable:yych:emit
- اگر روی صفر تنظیم شود، تعریف yych تولید نمیشود. پیشفرض غیرصفر است.
- re2c:yych:conversion, re2c:variable:yych:conversion
- اگر روی غیرصفر تنظیم شود، re2java با هر بار خوانده شدن 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 وابسته نیستند (مثلاً در صورتی که از یک نویسهٔ دیدهبان استفاده شود). برای اطمینان از اینکه لکسر تولیدشده نمیتواند فراتر از پایان ورودی را بخواند، از هشدارها (گزینهٔ -W) و پیکربندی re2c:sentinel استفاده کنید. پیشفرض غیرصفر است (YYFILL فعال است).
- re2c:yyfill:parameter
- اگر روی صفر تنظیم شود، از تولید پارامتری که به YYFILL فرستاده میشود جلوگیری میکند. این پارامتر حداقل تعداد نویسههایی است که باید تأمین شوند. پیشفرض غیرصفر است (پارامتر تولید میشود). این پیکربندی میتواند توسط re2c:YYFILL:naked یا re2c:api:style بازنویسی شود.
- re2c:yyfn:sep
- جداکنندهٔ مورد استفاده در عناصر YYFN را تعیین میکند (پیشفرض نقطهویرگول است).
- re2c:yyfn:throw
- استثناهایی
را مشخص
میکند که
توسط تابع
YYFN پرتاب
میشوند
(پیشفرض
خالی است،
به معنای
نبود هیچ
استثنایی).
ابزار re2java از نحو زیر برای عبارات باقاعده استفاده میکند:
- "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؛ از پرانتزها برای تغییر اولویت استفاده میشود. اگر استخراج زیرتطابق (submatch extraction) فعال باشد، (R) بسته به گزینهٔ --invert-captures یک گروه ضبطکننده یا غیرضبطکننده خواهد بود.
- (!R)
- اگر استخراج زیرتطابق فعال باشد، (!R) بسته به گزینهٔ --invert-captures یک گروه غیرضبطکننده یا ضبطکننده خواهد بود.
- R S
- الحاق: R و سپس S.
- R | S
- جایگزین: R یا S.
- R / S
- پیشنگری (Lookahead): R و به دنبال آن S، اما S مصرف نمیشود.
- name
- عبارت باقاعدهٔ تعریفشده با نام name (یا رشتهٔ لیترال "name" در حالت سازگاری با Flex).
- {name}
- عبارت باقاعدهٔ تعریفشده با نام name در حالت سازگاری با Flex.
- @stag
- یک s-tag: آخرین موقعیت ورودی را که @stag در آن تطبیق مییابد، در متغیری به نام stag ذخیره میکند.
- #mtag
- یک m-tag: تمامی موقعیتهای ورودی را که #mtag در آنها تطبیق مییابد، در متغیری به نام mtag ذخیره میکند.
- $
- پایان ورودی.
کلاسهای کاراکتر و لیترالهای رشتهای میتوانند شامل توالیهای گریز زیر باشند: \a، \b، \f، \n، \r، \t، \v، \\، توالیهای گریز هشتهشتی \ooo و توالیهای گریز شانزدهشانزدهی \xhh، \uhhhh و \Uhhhhhhhh.
کنشها (Actions)
در اینجا فهرستی از کنشهای از پیش تعریفشده که توسط re2java پشتیبانی میشوند آمده است:
- !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) جهت
محافظت در
برابر
جریان
کنترل
ناخواسته
استفاده
کرد.
در اینجا فهرست کاملی از دستورالعملهای پشتیبانیشده توسط re2java آمده است:
- !use:name ;
- یک دستورالعمل use درونبلوکی که یک بلوک قواعد از پیش تعریفشده با name مشخص را در بلوک فعلی ادغام میکند. تعاریف، پیکربندیها و قواعد نامگذاریشده بلوک ارجاعدادهشده به موارد فعلی افزوده میشوند. تداخلها میان قواعد و پیکربندیهای همپوشان به روش معمول حلوفصل میشوند: نخستین قاعده اولویت دارد و آخرین پیکربندی بر موارد پیشین اعمال شده و آنها را بازنویسی میکند. یک استثنا، قواعد ویژه *، $ و <!> هستند که تعریف محلی درون بلوک همیشه برای آنها اولویت دارد. یک دستورالعمل use میتواند در هر کجای درون یک بلوک قرار گیرد و استفاده از چند دستورالعمل use مجاز است.
- !include file ;
- این دستورالعمل همانند بلوک include است: محتوای file را عیناً در محل دستورالعمل درج میکند.
رابط برنامه (Program interface)
کد تولیدشده به کمک پریمیتیوها (primitives) با برنامه بیرونی ارتباط برقرار میکند، که در مجموع به عنوان API شناخته میشوند. اینکه چه پریمیتیوهایی باید برای یک برنامه خاص تعریف شوند به عوامل متعددی بستگی دارد، از جمله پیچیدگی عبارات باقاعده، نحوه نمایش ورودی، بافرینگ و استفاده از قابلیتهای گوناگون. تمام پریمیتیوهای ضروری باید توسط کاربر در قالب ماکروها، توابع، متغیرها یا هر شکل مناسب دیگری تعریف شوند که کد تولیدشده را از نظر نحوی و معنایی صحیح سازد. ابزار re2java تعاریف را بررسی نمیکند (و نمیتواند بررسی کند)، بنابراین اگر چیزی جا افتاده باشد یا به نادرستی تعریف شده باشد، برنامه تولیدشده ممکن است خطاهای زمان کامپایل یا زمان اجرا داشته باشد. این راهنما مثالهایی از تعاریف API را در متداولترین حالتها ارائه میدهد.
ابزار re2java دارای سه مدل API است که مجموعه هسته پریمیتیوهای مورداستفاده در برنامه را تعریف میکنند:
- Simple API
- این API پیشفرض برای بکاند جاوا است. این رابط از پریمیتیوهای زیر تشکیل شده است: YYINPUT (که باید به عنوان دنبالهای از واحدهای کد، مانند یک رشته تعریف شود) و YYCURSOR، YYMARKER، YYCTXMARKER، YYLIMIT (که باید به عنوان اندیسهایی در YYINPUT تعریف شوند).
- Record API
- رابط Record API در مواردی مفید است که وضعیت لکسر باید در یک کلاس ذخیره شود. این رابط با گزینه --api record یا پیکربندی re2c:api = record فعال میشود. این API شامل متغیری به نام yyrecord است (نام آن را میتوان با re2c:yyrecord بازنویسی کرد) که باید به عنوان کلاسی با فیلدهای 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 در صورتی استفاده میشود که تطبیق طولانیتر شکست بخورد و لکسر نیاز به عقبگرد داشته باشد. نیازی به مقداردهی اولیه نیست.
- YYCTXMARKER
- یک مقدار چپ (l-value) که موقعیت زمینه دنبالهدار (trailing context) را ذخیره میکند (یک اشارهگر یا آفست عدد صحیح در YYINPUT). نیازی به مقداردهی اولیه نیست. YYCTXMARKER تنها در صورتی مورد نیاز است که عملگر پیشنگری / استفاده شده باشد.
- YYFILL
- یک پریمیتیو از Generic API با یک متغیر len. YYFILL باید حداقل len نویسه ورودی دیگر فراهم کند یا ناموفق باشد. اگر re2c:eof استفاده شود، len همیشه 1 است و YYFILL باید همواره به تابع فراخواننده بازگردد؛ مقدار بازگشتی صفر نشاندهنده موفقیت است. اگر re2c:eof استفاده نشود، مقدار بازگشتی YYFILL نادیده گرفته میشود و در صورت بروز شکست نباید بازگردد. بیشینه مقدار len برابر YYMAXFILL است.
- YYFN
- پریمیتیوی که پیشنمونه تابع را در مدل کد --recursive-functions تعریف میکند. مقدار آن باید آرایهای از یک یا چند رشته باشد، که در آن هر رشته شامل دو یا سه جزء جداشده با رشته مشخصشده در پیکربندی re2c:fn:sep (معمولاً یک نقطهویرگول) است. نخستین عنصر آرایه نام تابع و نوع بازگشتی را مشخص میکند (برای یک تابع void خالی است). عناصر بعدی آرگومانهای تابع را مشخص میکنند: نخست، عبارت مورد استفاده برای آرگومان در بدنه تابع (معمولاً فقط یک نام)؛ دوم، نوع آرگومان؛ سوم، یک پارامتر صوری اختیاری (به طور پیشفرض همان جزء نخست است - معمولاً هم آرگومان و هم پارامتر یک شناسه یکسان هستند).
- YYINPUT
- یک مقدار راست (r-value) که دنباله نویسههای ورودی کنونی را ذخیره میکند (رشته، بافر، و غیره).
- YYMAXFILL
- یک ثابت عدد صحیح برابر با بیشینه مقدار آرگومانِ YYFILL. میتوان آن را با یک بلوک max تولید کرد.
- YYLESSTHAN
- یک پریمیتیو از Generic API با یک متغیر len. باید به صورت یک مقدار راست (r-value) از نوع بولی تعریف شود که اگر و تنها اگر کمتر از len نویسه ورودی باقی مانده باشد، برابر با true گردد.
- 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 که باید موقعیت کنونی ورودی را به عنوان موقعیت زمینه دنبالهدار ذخیره کند (تا بعداً با 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
- فعالسازی استخراج تطبیقهای فرعی با گروههای گیرندهٔ حریصانهٔ چپترین. نتیجه در یک آرایه به نام yybmatch با ظرفیت 2 * YYMAXNMATCH جمعآوری میشود و yynmatch روی تعداد گروهها برای قاعدهٔ تطبیقیافته تنظیم میگردد.
- --captvars, --leftmost-captvars
- فعالسازی استخراج تطبیقهای فرعی با گروههای گیرندهٔ حریصانهٔ چپترین. نتیجه برای k-امین گروه گیرنده در متغیرهای yytl<k> و yytr<k> جمعآوری میشود.
- --case-insensitive
- در نظر گرفتن رشتههای درون نقلقول تکی و دوگانه به صورت غیرحساس به حروف کوچک و بزرگ.
- --case-inverted
- معکوس کردن مفهوم رشتههای درون نقلقول تکی و دوگانه: در نظر گرفتن رشتههای درون نقلقول تکی به صورت حساس به حروف و رشتههای درون نقلقول دوگانه به صورت غیرحساس به حروف.
- --case-ranges
- تجمیع موارد متوالی در دستورهای switch به یک بازه به شکل low ... high. این نحو یک افزونهٔ زبان C/C++ است که توسط کامپایلرهایی مانند GCC، Clang و Tcc پشتیبانی میشود. مزیت اصلی نسبت به استفاده از موارد تکی، تولید کد کوچکتر و زمان تولید سریعتر است، اگرچه برای برخی کامپایلرها مانند Tcc منجر به اندازهٔ باینری کوچکتر نیز میشود.
- --computed-gotos -g
- بهینهسازی پرشهای شرطی با استفاده از افزونهٔ غیراستاندارد "computed goto" (که باید توسط کامپایلر پشتیبانی شود). برنامهٔ re2java جداول پرش را تنها در موارد پیچیده با شاخههای شرطی فراوان تولید میکند. آستانهٔ پیچیدگی را میتوان با پیکربندی cgoto:threshold تنظیم کرد. جابهجاییهای نسبی را میتوان با پیکربندی cgoto:relative فعال کرد. این گزینه به طور ضمنی --bit-vectors را فعال میکند.
- --computed-gotos-relative
- مشابه --computed-gotos اما در عوض جابهجاییهای نسبی را برای جداول پرش تولید میکند (که باید توسط کامپایلر پشتیبانی شود). این گزینه به طور ضمنی --computed-gotos را فعال میکند.
- --conditions --start-conditions -c
- فعالسازی پشتیبانی از "conditions" مشابه Flex: چندین لکسر مرتبط با یکدیگر در یک بلوک واحد. این یک جایگزین برای مشخص کردن دستی بلوکهای مختلف re2java است که با goto یا فراخوانیهای توابع به هم متصل شدهاند.
- --depfile FILE
- نوشتن اطلاعات وابستگی در FILE در قالب یک قاعدهٔ Makefile به شکل <output-file> : <input-file> [include-file ...]. این قابلیت امکان ردیابی وابستگیهای ساخت را در حضور بلوکها/دستورالعملهای include فراهم میکند، به طوری که بهروزرسانی فایلهای include باعث بازتولید فایل خروجی میشود. این گزینه به گزینهٔ --output وابسته است.
- --ebcdic --ecb -e
- تولید لکسری که ورودی را با کدگذاری EBCDIC میخواند. re2java فرض میکند که محدودهٔ نویسهها 0 -- 0xFF و اندازهٔ نویسه ۱ بایت است.
- --empty-class <match-empty | match-none | error>
- تعریف نحوهٔ برخورد re2java با کلاسهای نویسهٔ خالی. با match-empty (پیشفرض)، کلاس خالی با ورودی خالی تطبیق مییابد (که غیرمنطقی است، اما با نسخههای پیشین سازگار است). با match-none کلاس خالی همیشه در تطبیق ناموفق است. با error کلاس خالی خطای کامپایل ایجاد میکند.
- --encoding-policy <fail | substitute | ignore>
- تعریف نحوهٔ برخورد re2java با جانشینهای یونیکد (Unicode surrogates). با fail برنامهٔ re2java در صورت مواجهه با یک جانشین با خطا متوقف میشود. با substitute برنامهٔ re2java بدون اخطار جانشینها را با نقطه کد خطای 0xFFFD جایگزین میکند. با ignore (پیشفرض) re2java با جانشینها مانند نقاط کد معمولی رفتار میکند. استاندارد یونیکد بیان میکند که جانشینهای مستقل نامعتبر هستند، اما کتابخانهها و برنامهها در دنیای واقعی رفتارهای متفاوتی نشان میدهند.
- --flex-syntax -F
- پشتیبانی جزئی از نحو Flex: در این حالت، تعاریف نامگذاریشده نیازی به علامت مساوی و نقطه ویرگول پایانی ندارند و هنگام استفاده باید در آکولاد محصور شوند. نامهای بدون آکولاد به عنوان رشتههای درون نقلقول دوگانه در نظر گرفته میشوند.
- --goto-label
- استفاده از مدل کد "goto/label": کدگذاری DFA در قالب بلوکهای کد برچسبدار متصلشده با انتقالهای goto میان بلوکها. این گزینه تنها برای زبانهایی که دستور goto دارند پشتیبانی میشود.
- --header --type-header -t HEADER
- تولید یک فایل HEADER. محتوای فایل را میتوان با استفاده از بلوکهای ویژهٔ header:on و header:off مشخص کرد. در صورت استفاده از شرایط، شمارشگر شرایط (condition enum) بهطور خودکار به انتهای هدر تولیدشده اضافه میشود (مگر اینکه بلوک صریح conditions وجود داشته باشد).
- -I PATH
- افزودن PATH به فهرست مکانهایی که هنگام جستجوی فایلهای include استفاده میشوند. این گزینه در ترکیب با بلوک یا دستورالعمل include مفید است. re2java به دنبال FILE در دایرکتوری فایل والد و مکانهای include مشخصشده با گزینهٔ -I میگردد.
- --input <default | custom>
- نام مستعار منسوخشده برای --api. گزینهٔ default متناظر با simple است (در واقع برای اکثر بکاندها پیشفرض است، اما نه همه). گزینهٔ custom متناظر با generic است.
- --input-encoding <ascii | utf8>
- مشخص کردن نحوهٔ تجزیهٔ عبارتهای منظم توسط re2java. با ascii (پیشفرض) re2java ورودی را با کدگذاری ASCII در نظر میگیرد: هر دنبالهای از واحدهای کد، دنبالهای از نویسههای مستقل ۱ بایتی است. با utf8 برنامهٔ re2java ورودی را با کدگذاری UTF8 در نظر گرفته و نویسههای چندبایتی را تشخیص میدهد.
- --invert-captures
- معکوس کردن مفهوم گروههای گیرنده (capturing) و غیرگیرنده. به صورت پیشفرض (...) گیرنده و (! ...) غیرگیرنده است. با این گزینه (! ...) گیرنده و (...) غیرگیرنده میشود.
- --lang <none | c | d | go | haskell | java | js | ocaml | python | rust | swift | v | zig>
- مشخص کردن زبان مقصد. زبانهای پشتیبانیشده عبارتند از C، D، Go، Haskell، Java، JS، OCaml، Python، Rust، Swift، V و Zig (زبانهای بیشتری را میتوان از طریق فایلهای نحو تعریفشده توسط کاربر اضافه کرد، گزینهٔ --syntax را ببینید). گزینهٔ none پیکربندیهای نحوی پیشفرض را غیرفعال میکند تا زبان مقصد تعریفنشده بماند.
- --location-format <gnu | msvc>
- مشخص کردن قالب مکان در پیامها. با gnu مکانها به صورت 'filename:line:column: ...' چاپ میشوند. با msvc مکانها به صورت 'filename(line,column) ...' چاپ میشوند. پیشفرض gnu است.
- --loop-switch
- استفاده از مدل کد "loop/switch": کدگذاری DFA در قالب یک حلقه روی یک دستور switch، به طوری که حالتهای منفرد همان موارد دستور switch هستند. وضعیت در یک متغیر به نام yystate ذخیره میشود. انتقالهای میان حالتها، yystate را به برچسب مورد مربوط به حالت مقصد بهروزرسانی کرده و اجرا را به ابتدای حلقه ادامه میدهند.
- --nested-ifs -s
- استفاده از دستورهای شرطی if تو در تو به جای دستورهای switch در پرشهای شرطی. این کار معمولاً در کامپایلرهای بدون بهینهسازی منجر به ایجاد کد کارآمدتری میشود.
- --no-debug-info -i
- عدم چاپ دستورالعملهای خط (line directives). این گزینه میتواند در زمانی که کد تولیدشده در یک سامانهٔ کنترل نسخه ذخیره میشود مفید باشد (برای جلوگیری از تفاوتهای حجیم ناشی از تغییرات کوچک).
- --no-generation-date
- عدم درج تاریخ در فایل تولیدشده.
- --no-version
- عدم درج نسخه در فایل تولیدشده.
- --no-unsafe
- عدم تولید بستهبند unsafe روی YYPEEK (این گزینه مختص Rust است). به دلایل کارایی، YYPEEK باید از بررسی محدوده خودداری کند، زیرا لکسر از قبل بررسیهای پایان ورودی را به روشی کارآمدتر انجام میدهد. کاربر میتواند یک تعریف ایمن برای YYPEEK ارائه دهد، یا تعریفی که تنها در ساختهای release ناامن باشد، که در این حالت گزینهٔ --no-unsafe به جلوگیری از هشدارهای مربوط به بلوکهای اضافی unsafe کمک میکند.
- --output -o OUTPUT
- مشخص کردن فایل خروجی OUTPUT.
- --posix-captures, -P
- فعالسازی استخراج تطبیقهای فرعی با گروههای گیرنده به سبک POSIX. نتیجه در یک آرایه به نام yybmatch با ظرفیت 2 * YYMAXNMATCH جمعآوری میشود و yynmatch روی تعداد گروهها برای قاعدهٔ تطبیقیافته تنظیم میگردد..TP --posix-captvars فعالسازی استخراج زیرتطبیق با گروههای گیرانداز به سبک POSIX. نتیجه برای k-امین گروه گیرانداز در متغیرهای yytl<k> و yytr<k> جمعآوری میشود.
- --recursive-functions
- استفاده از مدل کد مبتنی بر توابع همبازگشتی، که در آن هر وضعیت DFA یک تابع مجزا است که میتواند توابع وضعیتهای دیگر یا خودش را فراخوانی کند.
- --reusable -r
- از نسخه 2.2 منسوخ شده است (بلوکهای قابل استفاده مجدد اکنون بهطور پیشفرض مجاز هستند).
- --skeleton -S
- نادیدهگرفتن کد رابط تعریفشده توسط کاربر و تولید یک برنامه مستقل «اسکلتی» ("skeleton"). علاوه بر این، فایلهای ورودی حاوی رشتههای مشتقشده از دستور زبان منظم و نتایج فشردهشده تطبیق را تولید میکند که برای تأیید رفتار «اسکلت» روی تمام ورودیها استفاده میشوند. این گزینه برای یافتن باگها در بهینهسازیها و تولید کد مفید است. این گزینه فقط برای C پشتیبانی میشود.
- --storable-state -f
- تولید یک تحلیلگر لغوی که میتواند وضعیت درونی خود را ذخیره کند. این قابلیت در تحلیلگرهای مدل push که در هنگام نبود ورودی کافی توسط برنامهای بیرونی متوقف شده و سپس با دردسترس قرار گرفتن ورودی بیشتر از سر گرفته میشوند، مفید است. در این حالت، کاربران باید علاوه بر این، دستورهای اولیه YYGETSTATE و YYSETSTATE را تعریف کنند، و متغیرهای yych، yyaccept و state باید بخشی از وضعیت ذخیرهشدهی تحلیلگر باشند.
- --syntax FILE
- بارگذاری پیکربندیها از FILE مشخصشده و اعمال آنها روی فایل نحو پیشفرض. توجه داشته باشید که FILE میتواند تنها چند پیکربندی را تعریف کند (اگر برای اصلاح فایل نحو پیشفرض استفاده شود)، یا یک بکاند زبان کاملاً جدید را تعریف کند (در مورد دوم توصیه میشود از گزینه --lang none استفاده کنید).
- فعالسازی استخراج زیرتطبیق با تگها.
- --ucs2 --wide-chars -w
- تولید تحلیلگری که ورودی کدگذاریشده با UCS2 را میخواند. re2java فرض میکند که محدوده نویسهها 0 -- 0xFFFF و اندازه نویسه ۲ بایت است. این گزینه مستلزم --nested-ifs است.
- --utf8 --utf-8 -8
- تولید تحلیلگری که ورودی را با کدگذاری UTF-8 میخواند. re2java فرض میکند که محدوده نویسهها 0 -- 0x10FFFF و اندازه نویسه ۱ بایت است.
- --utf16 --utf-16 -x
- تولید تحلیلگری که ورودی کدگذاریشده با UTF16 را میخواند. re2java فرض میکند که محدوده نویسهها 0 -- 0x10FFFF و اندازه نویسه ۲ بایت است. این گزینه مستلزم --nested-ifs است.
- --utf32 --unicode -u
- تولید تحلیلگری که ورودی کدگذاریشده با UTF32 را میخواند. re2java فرض میکند که محدوده نویسهها 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
- گزینه اشکالزدایی: خروجی آمار تعداد وضعیتها در بستار (closure).
- --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 مورد استفاده توسط re2java. گزینه moore الگوریتم مور است (گزینه پیشفرض). گزینه table الگوریتم «پر کردن جدول» ("table filling") است. هر دو الگوریتم باید صرفنظر از برچسبگذاری مجدد وضعیتها DFA یکسانی تولید کنند؛ پر کردن جدول سادهتر و بسیار کندتر است و به عنوان یک پیادهسازی مرجع به کار میرود.
- --eager-skip
- گزینه درونی: واداشتن تحلیلگر لغوی تولیدشده به پیش بردن سریع موقعیت ورودی -- بلافاصله پس از خواندن نماد ورودی. این کار رفتار پیشفرض را تغییر میدهد که در آن موقعیت ورودی با تاخیر (lazy) -- پس از گذار به وضعیت بعدی پیش برده میشد.
- --no-lookahead
- گزینه درونی، منسوخ شده است. در گذشته برای فعالسازی الگوریتم TDFA(0) به کار میرفت. بر خلاف TDFA(1)، الگوریتم TDFA(0) از پیشنگری یکنمادی استفاده نمیکند. این الگوریتم عملیاتهای ثبات را به جای گذارهای خروجی، روی گذارهای ورودی اعمال میکند. ارزیابیها نشان دادهاند که کارایی الگوریتم TDFA(0) کمتر از TDFA(1) است.
- --no-optimize-tags
- گزینه درونی: جلوگیری از بهینهسازی متغیرهای تگ (مفید برای اشکالزدایی).
- --posix-closure <gor1 | gtop>
- گزینه درونی: تعیین الگوریتم کوتاهترین مسیر مورد استفاده برای ساخت بستار اپسیلون با معناشناسی رفع ابهام POSIX: مقدار gor1 (پیشفرض) نشاندهنده الگوریتم Goldberg-Radzik است، و gtop نشاندهنده الگوریتم «ترتیب توپولوژیکی سراسری» ("global topological order") است.
- --posix-prectable <complex | naive>
- گزینه درونی: تعیین الگوریتم مورد استفاده برای محاسبه جدول اولویت POSIX. الگوریتم complex جدول اولویت را در یک پیمایش از درخت تاریخچه تگ محاسبه میکند و از نظر تعداد وضعیتهای TNFA دارای پیچیدگی درجه دو است؛ این گزینه پیشفرض میباشد. الگوریتم naive در بدترین حالت دارای پیچیدگی درجه سه بر حسب تعداد وضعیتهای TNFA است، اما بسیار سادهتر از complex بوده و ممکن است در موارد غیربحرانی کمی سریعتر باشد.
- --stadfa
- گزینه درونی، منسوخ شده است. در گذشته برای فعالسازی الگوریتم staDFA استفاده میشد، که تفاوت آن با TDFA در این است که عملیاتهای ثبات به جای گذارها، در وضعیتها قرار میگیرند. ارزیابیها نشان دادند که کارایی الگوریتم staDFA کمتر از TDFA است.
- --fixed-tags <none | toplevel | all>
- گزینه
درونی: مشخص
میکند که
آیا
بهینهسازی
تگ ثابت
باید برای
همه تگها
(all)،
هیچکدام
از آنها
(none)، یا فقط
تگهای
موجود در
الحاق
بالاترین
سطح (toplevel)
اعمال شود
یا خیر.
مقدار
پیشفرض all
است. تگهای
«ثابت» ("Fixed")
تگهایی
هستند که در
فاصلهای
ثابت نسبت
به یک تگ
دیگر (به
نام «پایه»
یا "base") قرار
دارند. در
چنین
مواردی
تنها تگ
پایه نیاز
به ردیابی
دارد و
مقدار تگ
ثابت
میتواند
از مجموع
مقدار تگ
پایه به
علاوه یک
آفست ایستا
محاسبه شود.
برای
تگهایی که
تحت گزینش
یا تکرار
قرار دارند
نیز لازم
است بررسی
شود که آیا
تگ پایه
دارای
مقدار
عدمتطبیق
است یا خیر
(در این
صورت تگ
ثابت نیز
باید بدون
در نظر
گرفتن
آفست، روی
عدمتطبیق
تنظیم شود).
برای
تگهای
موجود در
الحاق
بالاترین
سطح به این
بررسی
نیازی
نیست، زیرا
آنها
همواره
مطابقت
دارند.
هشدارها را میتوان بهطور جداگانه فعال، غیرفعال یا به خطا تبدیل کرد.
- -W
- فعال کردن تمام هشدارها.
- -Werror
- تبدیل هشدارها به خطا. توجه داشته باشید که این گزینه بهتنهایی هیچ هشداری را فعال نمیکند؛ بلکه تنها بر هشدارهایی تأثیر میگذارد که تاکنون فعال شدهاند یا بعداً فعال خواهند شد.
- -W<warning>
- فعال کردن warning.
- -Wno-<warning>
- غیرفعال کردن warning.
- -Werror-<warning>
- فعال کردن warning و تلقی کردن آن به عنوان خطا (این گزینه مستلزم -W<warning> است).
- -Wno-error-<warning>
- با این warning خاص به عنوان یک خطا برخورد نکن. این گزینه خودِ هشدار را غیرفعال نمیکند.
- -Wcondition-order
- اگر برنامهٔ تولیدشده فرضیات ضمنی دربارهٔ شمارهگذاری وضعیتها داشته باشد، هشدار میدهد. باید از گزینهٔ --header یا بلوک conditions برای ایجاد نگاشتی از نام وضعیتها به اعداد استفاده کرده و سپس از نامهای وضعیتِ خودکار تولیدشده بهره برد.
- -Wempty-character-class
- اگر یک عبارت باقاعده حاوی یک کلاس نویسهٔ خالی باشد، هشدار میدهد. تلاش برای تطبیق یک کلاس نویسهٔ خالی بیمعنی است: این عمل همواره باید ناموفق باشد. با این حال، به دلایل سازگاری با نسخههای پیشین، re2java کلاسهای نویسهٔ خالی را مجاز دانسته و با آنها مانند رشتههای خالی رفتار میکند. از گزینهٔ --empty-class برای تغییر رفتار پیشفرض استفاده کنید.
- -Wmatch-empty-string
- اگر قاعدهای پوچپذیر باشد (با یک رشتهٔ خالی تطبیق یابد)، هشدار میدهد. اگر تحلیلگر واژگانی در یک حلقه اجرا شود و تطبیق تهی غیرعمدی باشد، ممکن است تحلیلگر بهطور غیرمنتظرهای در یک حلقهٔ بینهایت متوقف شود.
- -Wswapped-range
- اگر حد پایین یک بازه بزرگتر از حد بالای آن باشد، هشدار میدهد. رفتار پیشفرض این است که بدون هیچ پیامی جای حدود بازه تعویض میشود.
- -Wundefined-control-flow
- اگر برخی از رشتههای ورودی باعث جریان کنترل تعریفنشده در تحلیلگر واژگانی شوند (الگوهای معیوب گزارش میشوند)، هشدار میدهد. این یک اشتباه رایج و خطرناک است. این مشکل را میتوان بهراحتی با افزودن قاعدهٔ پیشفرض * برطرف کرد که کمترین اولویت را دارد، با هر واحد کد تطبیق مییابد و همیشه یک واحد کد را مصرف میکند.
- -Wunreachable-rules
- در مورد قاعدههایی که تحتالشعاع قاعدههای دیگر قرار گرفتهاند و هرگز تطبیق نخواهند یافت، هشدار میدهد.
- -Wdeprecated-eof_rule
- در مورد قاعدههای مستقل پایان ورودی $ که با تغییرات آینده شکسته خواهند شد و نیاز به اصلاح دارند، هشدار میدهد. در حال حاضر، این قاعدهها هنگام تداخل با قاعدههای دیگر اولویت دارند، اما پس از معرفی نماد تعمیمیافتهٔ پایان ورودی $، ترتیب اولویت تغییر خواهد کرد و این قاعدهها تحتالشعاع دیگر قاعدهها قرار خواهند گرفت.
- -Wuseless-escape
- اگر نویسهای در جایی که نباید، گریز داده شود (escape شود)، هشدار میدهد. بهطور پیشفرض، re2java چنین نویسههای گریزی را بدون هشدار نادیده میگیرد، اما این مورد میتواند نشاندهندهٔ یک غلط تایپی یا خطا در توالی گریز باشد.
- -Wnondeterministic-tags
- اگر یک برچسب دارای درجهٔ n-ام از غیرقطعی بودن باشد، به طوری که n بزرگتر از ۱ باشد، هشدار میدهد.
- -Wsentinel-in-midrule
- اگر نماد دیدهبان (sentinel) در میان یک قاعده رخ دهد، هشدار میدهد --- این امر ممکن است باعث خواندن فراتر از انتهای بافر، خرابیها یا فساد حافظه در تحلیلگر واژگانی تولیدشده شود. این هشدار تنها زمانی کاربرد دارد که روش دیدهبان برای بررسی پایان ورودی استفاده شود. اگر از پیکربندی re2c:sentinel استفاده شود، این مورد به عنوان خطا تلقی میشود.
- -Wundefined-syntax-config
- اگر فایل
نحو (syntax file)
مشخصشده
با گزینهٔ
--syntax فاقد
تعاریف
برخی از
پیکربندیها
باشد،
هشدار
میدهد. این
امر به
نگهداری
فایلهای
نحو
تعریفشده
توسط کاربر
کمک میکند:
اگر نسخهٔ
جدیدی
پیکربندیهایی
را اضافه
کند، فایل
نحو قدیمی
هشداری
صادر کرده و
به کاربر
اطلاع داده
خواهد شد.
اگر برخی از
پیکربندیها
استفاده
نمیشوند و
نیازی به
تعریف
ندارند،
باید
بهطور
صریح روی
<undefined> تنظیم
شوند.
پشتیبانی از زبانهای گوناگون در re2c بر پایه ایده فایلهای نحو استوار است. یک فایل نحو، فایلی پیکربندی است که نحو زبان هدف را تعریف میکند -- نه کل زبان، بلکه بخش کوچکی از آن را که توسط کد تولیدشده استفاده میشود. فایلهای نحو re2c را بسیار انعطافپذیر میکنند، اما نباید به عنوان جایگزینی برای پیکربندیهای re2c: استفاده شوند: هدف آنها تعریف نحو زبان هدف است، نه سفارشیسازی یک لکسر خاص. همه زبانهای پشتیبانیشده دارای فایلهای نحو پیشفرضی هستند که بخشی از توزیع برنامه میباشند (پوشه فرعی include/syntax را ببینید)؛ آنها همچنین در باینری re2java تعبیه شدهاند. کاربران میتوانند یک فایل نحو سفارشی ارائه دهند که چند پیکربندی را برای یکی از زبانهای پشتیبانیشده بازنویسی کند، یا میتوانند همه پیکربندیها را از نو تعریف کنند (در آن حالت باید از گزینه --lang none استفاده شود). فایلهای نحو شامل پیکربندیهایی در چهار نوع مختلف هستند: فهرستهای ویژگی، پیکربندیهای زبان، پیکربندیهای درجا و الگوهای کد.
فهرستهای ویژگی (Feature lists)
- supported_apis
- فهرستی از APIهای پشتیبانیشده با عناصر ممکن simple، record و generic.
- supported_api_styles
- فهرستی از شیوههای API پشتیبانیشده با عناصر ممکن functions و free-form.
- supported_code_models
- فهرستی از مدلهای کد پشتیبانیشده با عناصر ممکن goto-label، loop-switch و recursive-functions.
- supported_targets
- فهرستی از مقصدهای تولید کد پشتیبانیشده با عناصر ممکن code، dot و skeleton.
- supported_features
- فهرستی از ویژگیهای پشتیبانیشده با عناصر ممکن nested-ifs، bitmaps، computed-gotos، case-ranges، monadic، unsafe، tags، captures و captvars.
پیکربندیهای زبان (Language configurations)
- semicolons
- غیر صفر اگر زبان پس از دستورات از نقطهویرگول استفاده میکند.
- backtick_quoted_strings
- غیر صفر اگر زبان دارای رشتههای محصور در بکتیک است.
- single_quoted_strings
- غیر صفر اگر زبان دارای رشتههای محصور در تککوتیشن است.
- indentation_sensitive
- غیر صفر اگر زبان به تورفتگی حساس است.
- 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>. متغیرها جانگهدارندههایی هستند که در طول مرحله تولید کد جایگزین میشوند. متغیرهای فهرستی خاص هستند: هنگام بسط دادن الگوهای فهرستی، re2java عبارات سمت راست دونقطه را چند بار تکرار میکند، و هر بار رخدادهای متغیر فهرستی را با مقداری مختص به این تکرار جایگزین میسازد. فهرستها دارای کرانهای اختیاری هستند (مقادیر منفی از انتها شمارش میشوند، برای نمونه -1 به معنای عنصر آخر است). نامهای شرطی با یک نقطه شروع میشوند. هم شرطها و هم متغیرها میتوانند محلی (مختص به الگوی کد دادهشده) یا سراسری (مجاز در تمام الگوهای کد) باشند. هنگامی که re2java یک فایل نحو را میخواند، بررسی میکند که هر الگوی کد تنها از متغیرها و شرطهایی استفاده کند که در آن مجاز هستند.
برای نمونه، الگوی کد زیر ساختار 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
}
در اینجا فهرستی از تمام الگوهای کد پشتیبانیشده توسط re2java همراه با متغیرها و شرطهای محلی آنها آورده شده است. توجه داشته باشید که یک تعریف خاص میتواند، اما ملزم نیست که از متغیرها و شرطهای محلی استفاده کند. هر الگوی کد استفادهنشده باید روی <undefined> تنظیم شود.
- code:var_global
- مشابه code:var_local، با این تفاوت که در سطح بالا (top-level) استفاده میشود.
- code:const_local
- تعریف یک مقدار ثابت محلی. متغیرهای پشتیبانیشده: type (نوع مقدار ثابت)، name (نام آن) و init (مقدار اولیه).
- code:const_global
- مشابه code:const_local، با این تفاوت که در سطح بالا (top-level) استفاده میشود.
- code:array_local
- تعریف یک آرایهٔ محلی (جدول). متغیرهای پشتیبانیشده: type (نوع عناصر آرایه)، name (نام آرایه)، size (اندازهٔ آن)، row (یک متغیر فهرستی که به خودی خود هیچ کدی تولید نمیکند، اما عبارت فهرست را به تعداد سطرهای جدول بسط میدهد) و elem (یک متغیر فهرستی که به تمام عناصر جدول در سطر فعلی بسط مییابد -- هدف آن قرارگیری بهصورت تو در تو در فهرست row است). شرط پشتیبانیشده: .const (اگر آرایه تغییرناپذیر باشد، درست است).
- code:array_global
- مشابه code:array_local، با این تفاوت که در سطح بالا (top-level) استفاده میشود.
- code:array_elem
- ارجاع به عنصری از یک آرایه (جدول). متغیرهای پشتیبانیشده: array (نام آرایه) و index (شاخص عنصر).
- code:enum
- تعریف یک شمارشگر (enumeration) (ممکن است با استفاده از یک ساختار زبانی ویژه برای شمارشگرها تعریف شود، یا صرفاً بهصورت چند مقدار ثابت مستقل). متغیرهای پشتیبانیشده عبارتند از type (نوع شمارشی تعریفشده توسط کاربر یا نوع مقادیر ثابت)، elem (متغیر فهرستی که به نام هر عضو بسط مییابد) و init (مقداردهندهٔ اولیه برای هر عضو). شرطها: .init (اگر مقداردهندهٔ اولیه وجود داشته باشد، درست است).
- code:enum_elem
- عنصر شمارشگر (عضوی از نوع شمارشی تعریفشده توسط کاربر یا نام یک مقدار ثابت، بسته به چگونگی تعریف code:enum). متغیرهای پشتیبانیشده عبارتند از name (نام عنصر) و type (نوع آن).
- code:assign
- دستور انتساب. متغیرهای پشتیبانیشده عبارتند از lhs (سمت چپ) و rhs (سمت راست).
- code:type_int
- نوع عدد صحیح علامتدار (signed integer).
- code:type_uint
- نوع عدد صحیح بدون علامت (unsigned integer).
- code:type_yybm
- نوع عناصر در جدول yybm.
- code:type_yytarget
- نوع عناصر در جدول yytarget.
- code:type_yyctable
- نوع عناصر در جدول yyctable.
- code:cmp_eq
- عملگر "مساوی".
- 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
- گروهی از caseهای switch که به یک بلوک کد واحد نگاشت میشوند. متغیرهای پشتیبانیشده عبارتند از case (یک متغیر فهرستی که به تمام caseها در این گروه بسط مییابد) و stmt (یک متغیر فهرستی که به تمام دستورها در بلوک کد بسط مییابد).
- code:switch_cases_oneline
- حالت خاصی از code:switch_cases برای حالتی که بلوک کد شامل یک دستور تکخطی منفرد باشد. اگر این مقدار برابر با <undefined> باشد، در عوض از code:switch_cases استفاده میشود.
- code:switch_case_range
- یک case منفرد در switch که بازهای از مقادیر را پوشش میدهد (که ممکن است فقط شامل یک مقدار منفرد باشد). متغیر پشتیبانیشده: val (یک متغیر فهرستی که به تمام مقادیر موجود در بازه بسط مییابد). شرطهای پشتیبانیشده: .many (اگر بیش از یک مقدار در بازه وجود داشته باشد، درست است) و .char_literals (اگر این یک switch روی لیترالهای نویسهای باشد، درست است -- برخی زبانها نحو ویژهای برای این حالت ارائه میدهند).
- code:switch_case_default
- شاخهٔ پیشفرض (default) در دستور switch.
- code:loop
- حلقهای که بهطور نامحدود اجرا میشود (مگر اینکه از درون بدنهٔ حلقه متوقف شود). متغیرهای پشتیبانیشده: label (برچسب حلقه)، stmt (یک متغیر فهرستی که به تمامی دستورها در بدنهٔ حلقه بسط مییابد).
- code:continue
- دستور continue. متغیرهای پشتیبانیشده: label (برچسبی که اجرای برنامه از آن ادامه مییابد).
- code:goto
- دستور goto. متغیرهای پشتیبانیشده: label (برچسب مقصد پرش).
- code:cgoto
- دستور goto محاسباتی (computed 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 (اگر این یک تابع غیر تهی (non-void) باشد، درست است).
- code:fndef
- مشابه code:fndecl، اما برای تعریف تابع استفاده میشود؛ بنابراین یک متغیر فهرستی اضافی stmt دارد که به تمام دستورهای موجود در بدنهٔ تابع بسط مییابد.
- code:fncall
- دستور فراخوانی تابع. متغیرهای پشتیبانیشده: name (نام تابع)، retval (مقدار سمت چپ یا l-value که مقدار بازگشتی در آن ذخیره میشود، در صورت وجود) و arg (یک متغیر فهرستی که به تمام آرگومانهای تابع بسط مییابد). شرطها: .args (اگر تابع دارای آرگومان باشد، درست است) و .retval (اگر مقدار بازگشتی نیاز به ذخیره شدن داشته باشد، درست است).
- code:tailcall
- دستور فراخوانی دم (tail call). متغیرهای پشتیبانیشده: name (نام تابع)، و arg (یک متغیر فهرستی که به تمام آرگومانهای تابع بسط مییابد). شرطها: .args (اگر تابع دارای آرگومان باشد، درست است) و .retval (اگر این یک تابع غیر تهی (non-void) باشد، درست است).
- code:recursive_functions
- بدنهٔ برنامه با مدل کد --recursive-functions. متغیرهای پشتیبانیشده: fn (یک متغیر فهرستی که به خودی خود هیچ کدی تولید نمیکند، اما عبارت فهرست را به تعداد توابع بسط میدهد)، fndecl (اعلان تابع فعلی) و fndef (تعریف تابع فعلی)..TP code:fingerprint اثرانگشت در بالای فایل خروجی تولیدشده. متغیرهای پشتیبانیشده: ver (نسخهٔ re2java که برای تولید آن استفاده شده) و date (تاریخ تولید).
- code:line_info
- قالب دستورالعملهای خط (اگر روی <undefined> تنظیم شود، هیچ دستورالعملی تولید نمیشود). متغیرهای پشتیبانیشده: line (شمارهٔ خط) و file (نام فایل).
- code:abort
- دستوری که اجرای برنامه را متوقف (abort) میکند.
- code:yydebug
- دستور YYDEBUG، احتمالاً سفارشیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYDEBUG، yyrecord، yych (نگاشت به پیکربندیهای متناظر re2c:)، state (شمارهٔ وضعیت DFA).
- code:yypeek
- دستور YYPEEK، احتمالاً سفارشیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYPEEK، YYCTYPE، YYINPUT، YYCURSOR، yyrecord، yych (نگاشت به پیکربندیهای متناظر re2c:). شرطها: .cast (در صورتی که re2c:yych:conversion روی مقداری غیرصفر تنظیم شده باشد، درست است).
- code:yyskip
- دستور YYSKIP، احتمالاً سفارشیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSKIP، YYCURSOR، yyrecord (نگاشت به پیکربندیهای متناظر re2c:).
- code:yybackup
- دستور YYBACKUP، احتمالاً سفارشیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYBACKUP، YYCURSOR، YYMARKER، yyrecord (نگاشت به پیکربندیهای متناظر re2c:).
- code:yybackupctx
- دستور YYBACKUPCTX، احتمالاً سفارشیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYBACKUPCTX، YYCURSOR، YYCTXMARKER، yyrecord (نگاشت به پیکربندیهای متناظر re2c:).
- code:yyskip_yypeek
- دستور ترکیبی code:yyskip و code:yypeek (بهطور پیشفرض code:yyskip و به دنبال آن code:yypeek).
- code:yypeek_yyskip
- دستور ترکیبی code:yypeek و code:yyskip (بهطور پیشفرض code:yypeek و به دنبال آن code:yyskip).
- code:yyskip_yybackup
- دستور ترکیبی code:yyskip و code:yybackup (بهطور پیشفرض code:yyskip و به دنبال آن code:yybackup).
- code:yybackup_yyskip
- دستور ترکیبی code:yybackup و code:yyskip (بهطور پیشفرض code:yybackup و به دنبال آن code:yyskip).
- code:yybackup_yypeek
- دستور ترکیبی code:yybackup و code:yypeek (بهطور پیشفرض code:yybackup و به دنبال آن code:yypeek).
- code:yyskip_yybackup_yypeek
- دستور ترکیبی code:yyskip، code:yybackup و code:yypeek (بهطور پیشفرض ``code:yyskip`` و به دنبال آن code:yybackup و سپس code:yypeek).
- code:yybackup_yypeek_yyskip
- دستور ترکیبی code:yybackup، code:yypeek و code:yyskip (بهطور پیشفرض ``code:yybackup`` و به دنبال آن code:yypeek و سپس code:yyskip).
- code:yyrestore
- دستور YYRESTORE، احتمالاً سفارشیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYRESTORE، YYCURSOR، YYMARKER، yyrecord (نگاشت به پیکربندیهای متناظر re2c:).
- code:yyrestorectx
- دستور YYRESTORECTX، احتمالاً سفارشیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYRESTORECTX، YYCURSOR، YYCTXMARKER، yyrecord (نگاشت به پیکربندیهای متناظر re2c:).
- code:yyrestoretag
- دستور YYRESTORETAG، احتمالاً سفارشیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYRESTORETAG، YYCURSOR، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، tag (نام متغیر تگ که برای بازیابی موقعیت استفاده میشود).
- code:yyshift
- دستور YYSHIFT، احتمالاً سفارشیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSHIFT، YYCURSOR، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، offset (تعداد واحدهای کد برای جابهجایی موقعیت فعلی).
- code:yyshiftstag
- دستور YYSHIFTSTAG، احتمالاً سفارشیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSHIFTSTAG، yyrecord، negative (نگاشت به پیکربندیهای متناظر re2c:)، tag (متغیر تگی که باید جابهجا شود)، offset (تعداد واحدهای کد برای جابهجایی). شرطها: .nested (در صورتی که این یک تگ تودرتو باشد درست است -- در این حالت مقدار آن ممکن است برابر با re2c:tags:negative باشد، که نباید جابهجا شود).
- code:yyshiftmtag
- دستور YYSHIFTMTAG، احتمالاً سفارشیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSHIFTMTAG (نگاشت به پیکربندی متناظر re2c:)، tag (متغیر تگی که باید جابهجا شود)، offset (تعداد واحدهای کد برای جابهجایی).
- code:yystagp
- دستور YYSTAGP، احتمالاً سفارشیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSTAGP، YYCURSOR، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، tag (متغیر تگی که باید بهروزرسانی شود).
- code:yymtagp
- دستور YYMTAGP، احتمالاً سفارشیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYMTAGP (نگاشت به پیکربندی متناظر re2c:)، tag (متغیر تگی که باید بهروزرسانی شود).
- code:yystagn
- دستور YYSTAGN، احتمالاً سفارشیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSTAGN، negative، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، tag (متغیر تگی که باید بهروزرسانی شود).
- code:yymtagn
- دستور YYMTAGN، احتمالاً سفارشیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYMTAGN (نگاشت به پیکربندی متناظر re2c:)، tag (متغیر تگی که باید بهروزرسانی شود).
- code:yycopystag
- دستور YYCOPYSTAG، احتمالاً سفارشیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYCOPYSTAG، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، lhs، rhs (متغیرهای تگ سمت چپ و راست عملیات رونوشت).
- code:yycopymtag
- دستور YYCOPYMTAG، احتمالاً سفارشیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYCOPYMTAG، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، lhs، rhs (متغیرهای تگ سمت چپ و راست عملیات رونوشت).
- code:yygetaccept
- دستور YYGETACCEPT، احتمالاً سفارشیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYGETACCEPT، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، var (نگاشت به پیکربندی re2c:yyaccept).
- code:yysetaccept
- دستور YYSETACCEPT، احتمالاً سفارشیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSETACCEPT، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، var (نگاشت به پیکربندی re2c:yyaccept) و val (مقدار عددی قاعدهٔ پذیرفتهشده).
- code:yygetcond
- دستور YYGETCOND، احتمالاً سفارشیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYGETCOND، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، var (نگاشت به پیکربندی re2c:yycond).
- code:yysetcond
- دستور YYSETCOND، احتمالاً سفارشیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSETCOND، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، var (نگاشت به پیکربندی re2c:yycond) و val (شناسهٔ عددی شرط).
- code:yygetstate
- دستور YYGETSTATE، احتمالاً سفارشیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYGETSTATE، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، var (نگاشت به پیکربندی re2c:yystate).
- code:yysetstate
- دستور YYSETSTATE، احتمالاً سفارشیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSETSTATE، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، var (نگاشت به پیکربندی re2c:yystate) و val (شمارهٔ وضعیت).
- code:yylessthan
- دستور YYLESSTHAN، احتمالاً سفارشیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYLESSTHAN، YYCURSOR، YYLIMIT، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، need (تعداد واحدهای کد برای بررسی در برابر آن). شرط: .many (در صورتی که مقدار need بیشتر از یک باشد، درست است)..TP code:yyend عبارت YYEND، احتمالاً سفارشیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYEND، YYCURSOR، YYLIMIT.
- code:yybm_filter
- شرطی که برای پالایش مقادیر yych که تحت پوشش جدول yybm قرار ندارند به کار میرود (همراه با گزینهٔ --bitmaps استفاده میشود). متغیر پشتیبانیشده: yych (به پیکربندی re2c:yych نگاشت میشود).
- code:yybm_match
- قالب بررسی جدول yybm (تولیدشده با گزینهٔ --bitmaps). متغیرهای پشتیبانیشده: yybm، yych (به پیکربندیهای متناظر re2c: نگاشت میشوند)، offset (آفست در جدول yybm که باید به yych اضافه شود) و mask (ماسک بیتی که باید روی مدخل جدول اعمال شود تا مقدار بولی مورد بررسی بازیابی گردد).
- code:yytarget_filter
- شرطی که برای فیلتر کردن مقادیر yych که تحت پوشش جدول yytarget قرار ندارند به کار میرود (همراه با گزینهٔ --computed-gotos استفاده میشود). متغیر پشتیبانیشده: yych (به پیکربندی re2c:yych نگاشت میشود).
در اینجا فهرستی از تمام متغیرهای سراسری مجاز در فایلهای نحو آمده است:
- nl
- یک خط جدید (newline).
- indent
- متغیری که هیچ کدی تولید نمیکند، اما دارای اثر جانبی افزایش سطح تورفتگی است.
- dedent
- متغیری که هیچ کدی تولید نمیکند، اما دارای اثر جانبی کاهش سطح تورفتگی است.
- topindent
- رشتهٔ تورفتگی برای عبارت جاری. سطح تورفتگی توسط تولیدکنندهٔ کد ردیابی شده و بهطور خودکار بهروزرسانی میشود.
در اینجا فهرستی از تمام شرطیهای سراسری مجاز در فایلهای نحو آمده است:
- .api.simple
- درست (True) در صورتی که API ساده به کار رفته باشد (--api simple یا re2c:api = simple).
- .api.generic
- درست (True) در صورتی که API عمومی (generic) به کار رفته باشد (--api generic یا re2c:api = generic).
- .api.record
- درست (True) در صورتی که API رکورد به کار رفته باشد (--api record یا re2c:api = record).
- .api_style.functions
- درست (True) در صورتی که سبک API تابعگونه به کار رفته باشد (re2c:api-style = functions).
- .api_style.freeform
- درست (True) در صورتی که سبک API فرمآزاد به کار رفته باشد (re2c:api-style = free-form).
- .case_ranges
- درست (True) در صورتی که ویژگی بازههای case فعال باشد (--case-ranges یا re2c:case-ranges = 1).
- .cgoto.relative
- درست (True) در صورتی که شکل نسبی goto محاسبهشده به کار رفته باشد (--computed-gotos-relative یا re2c:cgoto:relative = 1).
- .code_model.goto_label
- درست (True) در صورتی که مدل کد مبتنی بر goto/label به کار رفته باشد (--goto-label).
- .code_model.loop_switch
- درست (True) در صورتی که مدل کد مبتنی بر loop/switch به کار رفته باشد (--loop-switch).
- .code_model.recursive_functions
- درست (True) در صورتی که مدل کد مبتنی بر توابع بازگشتی به کار رفته باشد (--recursive-function).
- .date
- درست (True) در صورتی که اثر انگشت (fingerprint) تولیدشده باید حاوی تاریخ تولید باشد.
- .loop_label
- درست (True) در صورتی که حلقههای تولیدشده توسط re2java باید برچسب داشته باشند (re2c:label:yyloop روی یک رشتهٔ غیرخالی تنظیم شده باشد).
- .monadic
- درست (True) در صورتی که کد تولیدشده باید مونادیک باشد (re2c:monadic = 1). این مورد فقط برای زبانهای تابعی محض مرتبط است.
- .start_conditions
- درست (True) در صورتی که شرایط شروع فعال باشند (--start-conditions).
- .storable_state
- درست (True) در صورتی که وضعیت قابل ذخیرهسازی فعال باشد (--storable-state).
- .unsafe
- درست (True) در صورتی که re2java برای تولید کد سریعتر باید از بلوکهای "unsafe" استفاده کند (--unsafe، re2c:unsafe = 1). این مورد تنها برای زبانهایی که ویژگی "unsafe" را دارند مرتبط است.
- .version
- درست (True) در صورتی که اثر انگشت تولیدشده باید شامل نسخهٔ re2java باشد.
- .yyfill.enable
- درست (True) در صورتی که YYFILL فعال باشد (re2c:yyfill:enable = 1).
- .yyfn.throw
- درست (True) در صورتی که پیکربندی re2c:yyfn:throw روی یک رشتهٔ غیرخالی تعریف شده باشد.
یکی از مشکلات اصلی لکسر این است که بداند چه زمانی باید متوقف شود. چندین شرط پایاندهنده وجود دارد:
- لکسر ممکن است با یک قاعده (شامل قاعده پیشفرض *) تطبیق یافته و به یک وضعیت نهایی برسد
- لکسر ممکن است در تطبیق با هر قاعدهای شکست بخورد و به یک وضعیت پیشفرض برسد
- لکسر ممکن است به پایان ورودی برسد
دو شرط اول لکسر را به روشی «طبیعی» ("natural") متوقف میکنند: لکسر به وضعیتی بدون انتقالهای خروجی میرسد و فرآیند تطبیق بهطور خودکار متوقف میشود. شرط سوم، یعنی پایان ورودی، متفاوت است: این حالت ممکن است در هر وضعیتی رخ دهد و لکسر باید قادر به مدیریت آن باشد. بررسی پایان ورودی، جریان کاری عادی لکسر را متوقف کرده و انشعابهای شرطی را به برنامه تولیدشده میافزاید؛ بنابراین لازم است تعداد این بررسیها به حداقل برسد. ابزار re2java از چند روش مختلف برای مدیریت پایان ورودی پشتیبانی میکند. انتخاب روش مناسب به پیچیدگی عبارات باقاعده، نیاز به بافرسازی، ملاحظات کارایی و سایر عوامل بستگی دارد. در ادامه فهرستی از این روشها آورده شده است:
- روش Sentinel (نگهبان). این روش نیاز به بررسیهای پایان ورودی را بهکلی برطرف میکند. این روش ساده و کارآمد است، اما محدود به حالتی است که یک نویسه «sentinel» طبیعی وجود داشته باشد که هرگز در ورودی معتبر رخ ندهد. این نویسه ممکن است در ورودی نامعتبر ظاهر شود، اما نباید توسط عبارات باقاعده مجاز باشد، مگر شاید به عنوان آخرین نویسه از یک قاعده. نویسه sentinel به انتهای ورودی ضمیمه شده و به عنوان یک سیگنال توقف عمل میکند: هنگامی که لکسر این نویسه را میخواند، یا یک خطای نحوی است یا پایان ورودی. در هر دو حالت لکسر باید متوقف شود. این روش در صورتی استفاده میشود که YYFILL با re2c:yyfill:enable = 0; غیرفعال شده و re2c:eof مقدار پیشفرض -1 را داشته باشد.
- روش Sentinel بههمراه بررسیهای مرزی (Sentinel with bounds checks). این روش عمومی است: به کاربر اجازه میدهد هر ورودی را بدون محدودیت در عبارات باقاعده مدیریت کند. ایده این است که تعداد بررسیهای پایان ورودی از طریق انجام آنها تنها روی نویسههای خاصی کاهش یابد. مشابه روش «sentinel»، یکی از نویسهها به عنوان «sentinel» انتخاب شده و به انتهای ورودی ضمیمه میشود. با این حال، هیچ محدودیتی در محل وقوع sentinel وجود ندارد (در واقع هر نویسهای میتواند به عنوان sentinel انتخاب شود). هنگامی که لکسر این نویسه را میخواند، علاوه بر آن یک بررسی مرزی انجام میدهد. اگر موقعیت فعلی در محدوده مجاز باشد، لکسر تطبیق را از سر گرفته و با نویسه sentinel مانند یک نویسه عادی رفتار میکند. در غیر این صورت، YYFILL را فراخوانی میکند (مگر اینکه غیرفعال شده باشد). اگر ورودی بیشتری فراهم شود، لکسر نویسه آخر را مجدداً تطبیق داده و طوری ادامه میدهد که گویی sentinel وجود نداشته است. در غیر این صورت، این باید پایان واقعی ورودی باشد و لکسر متوقف میشود. این روش زمانی استفاده میشود که re2c:eof مقداری غیرمنفی داشته باشد (باید روی مقدار عددی sentinel تنظیم شود). استفاده از YYFILL اختیاری است.
- بررسیهای مرزی با پدینگ (Bounds checks with padding). این روش عمومی است و ممکن است سریعتر از روش «sentinel بههمراه بررسیهای مرزی» باشد، اما پیچیدهتر نیز هست. ایده این است که وضعیتهای DFA به مؤلفههای قویاً همبند (SCCها) افراز شوند و برای هر SCC یک بررسی واحد برای تعداد نویسههای کافی جهت پوشش طولانیترین مسیر بدون حلقه در آن SCC ایجاد شود. این امر تعداد بررسیها را کاهش میدهد، اما برای لکسمهای کوتاه در انتهای ورودی مشکلی وجود دارد، زیرا بررسی مستلزم نویسههای کافی برای پوشش طولانیترین لکسم است. این مشکل را میتوان با پدینگ (لایهگذاری) ورودی با چند نویسه ساختگی که پسوند لکسم معتبری را تشکیل نمیدهند (به طوری که لکسر نتواند آنها را تطبیق دهد) برطرف کرد. طول پدینگ باید YYMAXFILL باشد که با یک بلوک max تولید میشود. اگر ورودی کافی نباشد، لکسر YYFILL را فراخوانی میکند که باید حداقل تعداد نویسههای مورد نیاز را تأمین کند یا بازگشتی نداشته باشد. این روش در صورتی استفاده میشود که YYFILL فعال بوده و re2c:eof برابر با -1 باشد (این پیکربندی پیشفرض است).
- بررسیهای سفارشی (Custom checks). رابط Generic API امکان بازنویسی عملیاتهای پایهای مانند خواندن یک نویسه را فراهم میکند، که گنجاندن بررسیهای پایان ورودی را به عنوان بخشی از آنها ممکن میسازد. این رویکرد مستعد خطا است و باید با احتیاط استفاده شود. برای استفاده از یک روش سفارشی، Generic API را با --api custom یا re2c:api = custom; فعال کرده و بررسیهای مرزی پیشفرض را با re2c:yyfill:enable = 0; یا re2c:yyfill:check = 0; غیرفعال کنید.
زیربخشهای بعدی حاوی یک مثال برای هر یک از این روشها هستند..SS نگهبان (Sentinel)
این مثال از یک نویسهٔ نگهبان (sentinel) برای مدیریت پایان ورودی استفاده میکند. این برنامه کلمات جداشده با فاصله را در یک رشتهٔ خاتمهیافته با null شمارش میکند. نگهبان مقدار null است: این آخرین نویسه از هر رشتهٔ ورودی است و طبق هیچیک از قواعد در میانهٔ یک لکسم (lexeme) مجاز نیست (بهویژه، در بازههای نویسهای که نادیدهگرفتن آنها آسان است گنجانده نشده است). اگر یک null در میانهٔ یک رشته رخ دهد، یک خطای نحوی است و تحلیلگر لغوی با قاعدهٔ پیشفرض * مطابقت پیدا میکند، اما فراتر از پایان ورودی را نمیخواند و دچار فروپاشی (crash) نمیشود (برای اطمینان از این موضوع از هشدار -Wsentinel-in-midrule و پیکربندی re2c:sentinel استفاده کنید). پیکربندی re2c:yyfill:enable = 0; تولید بررسیهای محدوده و فراخوانیهای YYFILL را غیرفعال میکند.
// re2java $INPUT -o $OUTPUT
class Main {
// Expects a null-terminated string.
static int lex(String yyinput) {
int yycursor = 0;
int count = 0;
loop: while (true) {
/*!re2c
re2c:YYCTYPE = "char";
re2c:YYPEEK = "yyinput.charAt(yycursor)";
re2c:yyfill:enable = 0;
* { return -1; }
[\x00] { return count; }
[a-z]+ { count += 1; continue loop; }
[ ]+ { continue loop; }
*/
}
}
public static void main(String []args) {
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 میشود.
// re2java $INPUT -o $OUTPUT
class Main {
// Expects a null-terminated string.
static int lex(String yyinput) {
int yycursor = 0;
int yymarker = 0;
int yylimit = yyinput.length() - 1; // yylimit points at the terminating null
int count = 0;
loop: while (true) {
/*!re2c
re2c:YYCTYPE = "char";
re2c:YYPEEK = "yyinput.charAt(yycursor)";
re2c:yyfill:enable = 0;
re2c:eof = 0;
str = ['] ([^'\\] | [\\][^])* ['];
* { return -1; }
$ { return count; }
str { count += 1; continue loop; }
[ ]+ { continue loop; }
*/
}
}
public static void main(String []args) {
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 یا با generic API به فرم YYLESSTHAN(n) هستند، که در آن n حداقل تعداد کاراکترهای لازم برای ادامهٔ کار lexer است (این موضوع همچنین بدان معناست که بررسی محدودهٔ بعدی حداکثر تا n کاراکتر دیگر رخ خواهد داد). اگر شرط بررسی برقرار باشد (true)، یعنی lexer به انتهای ورودی رسیده است و YYFILL(n) را فراخوانی میکند که باید یا حداقل n کاراکتر ورودی را تامین کند یا بازگشتی نداشته باشد. در این مثال، YYFILL همواره شکست میخورد و lexer را با یک خطا متوقف میکند (که مشکلی ایجاد نمیکند، زیرا ورودی در یک بافر میگنجد). برای نمونهای که بافر ورودی را با YYFILL دوباره پر میکند، بخش YYFILL with padding را ببینید.
// re2java $INPUT -o $OUTPUT
class Main {
/*!max:re2c*/
// Expects yymaxfill-padded string.
static int lex(byte[] str) {
// Pad string with yymaxfill zeroes at the end.
byte[] yyinput = new byte[str.length + YYMAXFILL];
System.arraycopy(str, 0, yyinput, 0, str.length);
int yycursor = 0;
int yylimit = yyinput.length;
int count = 0;
loop: while (true) {
/*!re2c
re2c:YYCTYPE = "int";
re2c:YYPEEK = "Byte.toUnsignedInt(yyinput[yycursor])";
re2c:YYFILL = "return -1;";
str = ['] ([^'\\] | [\\][^])* ['];
[\x00] {
// Check that it is the sentinel, not some unexpected null.
return (yycursor - 1 == str.length) ? count : -1;
}
str { count += 1; continue loop; }
[ ]+ { continue loop; }
* { return -1; }
*/
}
}
public static void main(String []args) {
assert lex("".getBytes()) == 0;
assert lex("'qu\0tes' 'are' 'fine: \\'' ".getBytes()) == 3;
assert lex("'unterminated\\'".getBytes()) == -1;
assert lex("'unexpected \00 null\\'".getBytes()) == -1;
}
};
بررسیهای سفارشی (Custom checks)
این مثال از یک روش سفارشی مدیریت انتهای ورودی مبتنی بر generic API استفاده میکند. برنامه رشتههای محصور در نقلقول تکی را که با فاصله از هم جدا شدهاند، میشمارد. این مثال همانند مثال sentinel است، با این تفاوت که ورودی با null خاتمه نمییابد. برای جبران نبود کاراکتر نگهبان (sentinel) در انتهای ورودی، YYPEEK مجدداً تعریف شده است تا قبل از خواندن کاراکتر بعدی ورودی، یک بررسی محدوده انجام دهد. این روش ناکارآمد است زیرا بررسیها بسیار مکرر انجام میشوند. اگر شرط بررسی برقرار نباشد، YYPEEK کاراکتر واقعی را بازمیگرداند، در غیر این صورت یک کاراکتر نگهبان ساختگی را برمیگرداند.
// re2java $INPUT -o $OUTPUT
class Main {
// Expects a string without terminating null.
static int lex(String str) {
byte[] yyinput = str.getBytes();
int yycursor = 0;
int count = 0;
loop: while (true) {
/*!re2c
re2c:api = generic;
re2c:YYCTYPE = "int";
re2c:YYPEEK = "(yycursor < yyinput.length)"
" ? Byte.toUnsignedInt(yyinput[yycursor]) : 0";
re2c:YYSKIP = "yycursor += 1;";
re2c:yyfill:enable = 0;
* { return -1; }
[\x00] { return count; }
[a-z]+ { count += 1; continue loop; }
[ ]+ { continue loop; }
*/
}
}
public static void main(String []args) {
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 مربوط به re2java ضمنی است، زیرا برای عملکرد عادی lexer نیازی به آن نیست و میتواند توسط کاربر تعریف و بهروزرسانی شود)
- نشانهگذار زمینه (context marker): موقعیت زمینهٔ انتهایی (YYCTXMARKER در API اشارهگر C یا YYBACKUPCTX/YYRESTORECTX در API عمومی)
- متغیرهای برچسب (tag variables): موقعیتهای تطابق فرعی (تعریفشده با بلوکهای stags و mtags و ساختارهای اولیه API عمومی YYSTAGP/YYSTAGN/YYMTAGP/YYMTAGN)
همهٔ اینها در هر حالتی استفاده نمیشوند، اما در صورت استفاده، باید توسط YYFILL بهروزرسانی شوند. تمام موقعیتهای فعال در بخشی بین token و cursor قرار دارند، بنابراین هر چیزی بین ابتدای بافر و token میتواند دور ریخته شود، بخشِ از token تا limit باید به ابتدای بافر منتقل شود، و فضای آزاد در انتهای بافر باید با دادههای جدید پر شود. برای جلوگیری از فراخوانیهای مکرر YYFILL، بهتر است تا حد امکان نویسههای ورودی بیشتری پر شوند (حتی اگر نویسههای کمتری برای از سرگیری کار lexer کافی باشند). جزئیات پیادهسازی YYFILL بسته به روشِ مدیریت EOF مورد استفاده کمی تفاوت دارد: حالت قاعدهٔ EOF تا حدودی سادهتر از حالت بررسی مرزها با پدگذاری (padding) است. همچنین توجه داشته باشید که اگر گزینهٔ -f --storable-state استفاده شود، YYFILL معناشناسی کمی متفاوتی خواهد داشت (که در بخش مربوط به storable state شرح داده شده است).
YYFILL با نشانهٔ نگهبان (YYFILL with sentinel)
اگر از قاعدهٔ EOF استفاده شود، YYFILL یک دستور اولیهٔ تابعمانند است که هیچ آرگومانی نمیپذیرد و مقداری بازمیگرداند که با صفر مقایسه میشود. فراخوانی YYFILL توسط شرط YYLIMIT <= YYCURSOR در API اشارهگر C و YYLESSTHAN() در API عمومی برانگیخته میشود. مقدار بازگشتی غیرصفر به این معنی است که YYFILL با شکست مواجه شده است. یک فراخوانی موفق YYFILL باید حداقل یک نویسه را تامین کرده و موقعیتهای ورودی را بر این اساس تنظیم کند. Limit همیشه باید روی یکی پس از آخرین موقعیت ورودی در بافر تنظیم شود، و نویسهٔ موجود در موقعیت limit باید نماد نگهبان مشخصشده توسط پیکربندی re2c:eof باشد. تصاویر زیر مکانهای نسبی موقعیتهای ورودی در بافر را قبل و بعد از فراخوانی YYFILL نشان میدهند (نماد نگهبان با # مشخص شده است، و تصویر دوم حالتی را نشان میدهد که ورودی کافی برای پر کردن کل بافر وجود ندارد).
<-- shift -->
>-A------------B---------C-------------D#-----------E->
buffer token marker limit,
cursor
>-A------------B---------C-------------D------------E#->
buffer, marker cursor limit
token
<-- shift -->
>-A------------B---------C-------------D#--E (EOF)
buffer token marker limit,
cursor
>-A------------B---------C-------------D---E#........
buffer, marker cursor limit
token
در اینجا نمونهای از برنامهای آورده شده است که فایل ورودی input.txt را در تکههای ۴۰۹۶ بایتی میخواند و از قاعدهٔ EOF استفاده میکند.
// re2java $INPUT -o $OUTPUT
import java.io.*;
import java.nio.file.*;
class Lexer {
public static final int BUFSIZE = 4096;
private BufferedInputStream stream;
private byte[] yyinput;
private int yycursor;
private int yymarker;
private int yylimit;
private int token;
private boolean eof;
public Lexer(File file) throws FileNotFoundException {
stream = new BufferedInputStream(new FileInputStream(file));
// Sentinel at `yylimit` offset is set to zero, which triggers YYFILL.
yyinput = new byte[BUFSIZE + 1];
yycursor = yymarker = yylimit = token = BUFSIZE;
eof = false;
}
private int fill() throws IOException {
if (eof) { return -1; } // unexpected EOF
// Error: lexeme too long. In real life can reallocate a larger buffer.
if (token < 1) { return -2; }
// Shift buffer contents (discard everything up to the current token).
System.arraycopy(yyinput, token, yyinput, 0, yylimit - token);
yycursor -= token;
yymarker -= token;
yylimit -= token;
token = 0;
// Fill free space at the end of buffer with new data from file.
yylimit += stream.read(yyinput, yylimit, BUFSIZE - yylimit);
yyinput[yylimit] = 0; // append sentinel symbol
// If read less than expected, this is the end of input.
eof = yylimit < BUFSIZE;
return 0;
}
// Expects a null-terminated string.
public int lex() throws IOException {
int count = 0;
loop: while (true) {
token = yycursor;
/*!re2c
re2c:YYCTYPE = "int";
re2c:YYPEEK = "Byte.toUnsignedInt(yyinput[yycursor])";
re2c:YYFILL = "fill() == 0";
re2c:eof = 0;
str = ['] ([^'\\] | [\\][^])* ['];
* { return -1; }
$ { return count; }
str { count += 1; continue loop; }
[ ]+ { continue loop; }
*/
}
}
public static void main(String []args) throws FileNotFoundException, IOException {
String fname = "input";
String content = "'qu\0tes' 'are' 'fine: \\'' ".repeat(Lexer.BUFSIZE);
// Prepare input file: a few times the size of the buffer, containing
// strings with zeroes and escaped quotes.
Files.writeString(Paths.get(fname), content);
int count = 3 * Lexer.BUFSIZE; // number of quoted strings written to file
// Prepare lexer state: all offsets are at the end of buffer.
File file = new File(".", fname);
Lexer lexer = new Lexer(file);
// Run the lexer.
int n = lexer.lex();
assert n == count;
// Cleanup: remove input file.
file.delete();
}
};
در حالت پیشفرض (زمانی که از قاعده EOF استفاده نمیشود) YYFILL یک دستور اولیهٔ شبهتابع (function-like primitive) است که یک آرگومان دریافت میکند و هیچ مقداری را برنمیگرداند. فراخوانی YYFILL با شرط (YYLIMIT - YYCURSOR) < n در API اشارهگر C و با YYLESSTHAN(n) در API عمومی فعال میشود. آرگومان ارسالی به YYFILL حداقل تعداد نویسههایی است که باید فراهم شوند. اگر در تأمین آن ناموفق باشد، YYFILL نباید کنترل را به تحلیلگر لغوی (lexer) بازگرداند (به همین دلیل بهتر است به صورت ماکرویی پیادهسازی شود که در صورت شکست، از تابع فراخواننده بازمیگردد). در صورت فراخوانی موفق YYFILL، موقعیت مرز (limit) باید یا روی یکی پس از آخرین موقعیت ورودی در بافر، یا روی انتهای پدینگ YYMAXFILL تنظیم شود (در شرایطی که YYFILL حداقل n نویسه را با موفقیت خوانده باشد، اما این مقدار برای پر کردن کل بافر کافی نباشد). تصاویر زیر موقعیتهای نسبی نقاط ورودی را در بافر قبل و بعد از فراخوانی YYFILL نشان میدهند (پدینگ YYMAXFILL در تصویر دوم با نمادهای # مشخص شده است).
<-- shift --> <-- need -->
>-A------------B---------C-----D-------E---F--------G->
buffer token marker cursor limit
>-A------------B---------C-----D-------E---F--------G->
buffer, marker cursor limit
token
<-- shift --> <-- need -->
>-A------------B---------C-----D-------E-F (EOF)
buffer token marker cursor limit
>-A------------B---------C-----D-------E-F###############
buffer, marker cursor limit
token <- YYMAXFILL ->
در اینجا مثالی از برنامهای آورده شده است که فایل ورودی input.txt را در تکههای ۴۰۹۶ بایتی میخواند و از بررسی مرزها (bounds-checking) به همراه پدینگ استفاده میکند.
// re2java $INPUT -o $OUTPUT
import java.io.*;
import java.nio.file.*;
import java.util.Arrays;
class Lexer {
/*!max:re2c*/
public static final int BUFSIZE = 4096;
private BufferedInputStream stream;
private byte[] yyinput;
private int yycursor;
private int yylimit;
private int token;
private boolean eof;
public Lexer(File file) throws FileNotFoundException {
stream = new BufferedInputStream(new FileInputStream(file));
// Prepare lexer state: all offsets are at the end of buffer.
// This immediately triggers YYFILL, as the YYLESSTHAN condition is true.
yyinput = new byte[BUFSIZE + YYMAXFILL];
yycursor = yylimit = token = BUFSIZE;
eof = false;
}
private int fill(int need) throws IOException {
if (eof) { return -1; } // unexpected EOF
// Error: lexeme too long. In real life can reallocate a larger buffer.
if (token < need) { return -2; }
// Shift buffer contents (discard everything up to the current token).
System.arraycopy(yyinput, token, yyinput, 0, yylimit - token);
yycursor -= token;
yylimit -= token;
token = 0;
// Fill free space at the end of buffer with new data from file.
yylimit += stream.read(yyinput, yylimit, BUFSIZE - yylimit);
yyinput[yylimit] = 0; // append sentinel symbol
// If read less than expected, this is the end of input.
if (yylimit < BUFSIZE) {
eof = true;
Arrays.fill(yyinput, yylimit, yylimit + YYMAXFILL, (byte)0);
yylimit += YYMAXFILL;
}
return 0;
}
// Expects a null-terminated string.
public int lex() throws IOException {
int count = 0;
loop: while (true) {
token = yycursor;
/*!re2c
re2c:YYCTYPE = "int";
re2c:YYPEEK = "Byte.toUnsignedInt(yyinput[yycursor])";
re2c:YYFILL = "if (fill(@@) != 0) { return -2; }";
str = ['] ([^'\\] | [\\][^])* ['];
[\x00] {
// Check that it is the sentinel, not some unexpected null.
return (token == yylimit - YYMAXFILL) ? count : -1;
}
str { count += 1; continue loop; }
[ ]+ { continue loop; }
* { return -1; }
*/
}
}
public static void main(String []args) throws FileNotFoundException, IOException {
String fname = "input";
String content = "'qu\0tes' 'are' 'fine: \\'' ".repeat(Lexer.BUFSIZE);
// Prepare input file: a few times the size of the buffer, containing
// strings with zeroes and escaped quotes.
Files.writeString(Paths.get(fname), content);
int count = 3 * Lexer.BUFSIZE; // number of quoted strings written to file
// Prepare lexer state: all offsets are at the end of buffer.
File file = new File(".", fname);
Lexer lexer = new Lexer(file);
// Run the lexer.
int n = lexer.lex();
assert n == count;
// Cleanup: remove input file.
file.delete();
}
};
بلوکهای چندگانه (Multiple blocks)
گاهی داشتن چندین تحلیلگر لغوی (lexer) مرتبط با یکدیگر ضروری است (برای مثال، اگر یک ماشین حالت سطح بالا وجود داشته باشد که بین حالتهای لکسر جابهجا میشود). این کار میتواند با استفاده از چندین بلوک بههمپیوسته re2java پیادهسازی شود. گزینه دیگر استفاده از شرطهای شروع (start conditions) است.
پیادهسازی ارتباط بین بلوکها به زبان مقصد بستگی دارد. در زبانهایی که دستور goto دارند (مانند C/C++ و Go) میتوان تمامی بلوکها را درون یک تابع قرار داد که پیش از هر یک از آنها یک برچسب (label) قرار گرفته باشد. جابهجایی از یک بلوک به بلوک دیگر یک goto ساده است. در زبانهایی که دستور goto ندارند (مانند Rust) استفاده از یک حلقه همراه با switch روی یک متغیر حالت ضروری است؛ مشابه حلقه/switch مربوط به yystate که توسط re2java تولید میشود، یا اینکه باید هر بلوک را درون یک تابع قرار داد و از فراخوانی توابع بهره برد.
مثال زیر از چندین بلوک برای تجزیه اعداد دودویی (binary)، هشتهشتی (octal)، دهدهی (decimal) و شانزدهشانزدهی (hexadecimal) استفاده میکند. هر مبنا بلوک مخصوص به خود را دارد. بلوک اولیه مبنا را تعیین کرده و کنترل را به بلوکهای دیگر واگذار میکند. پیکربندیهای مشترک در یک بلوک جداگانه در ابتدای برنامه تعریف شدهاند؛ این پیکربندیها توسط بلوکهای دیگر به ارث برده میشوند.
// re2java $INPUT -o $OUTPUT
class Parser {
private String yyinput;
private int yycursor;
private int yymarker;
private int number;
private void add_digit(int base, int offset) throws ArithmeticException {
number = Math.addExact(
Math.multiplyExact(number, base),
yyinput.charAt(yycursor - 1) - offset);
}
public int parse(String str) throws ArithmeticException, IllegalArgumentException {
yyinput = str;
yycursor = 0;
number = 0;
try {
/*!re2c
re2c:YYCTYPE = "char";
re2c:YYPEEK = "yyinput.charAt(yycursor)";
re2c:yyfill:enable = 0;
end = "\x00";
'0b' / [01] { return parse_bin(); }
"0" { return parse_oct(); }
"" / [1-9] { return parse_dec(); }
'0x' / [0-9a-fA-F] { return parse_hex(); }
* { throw new IllegalArgumentException("not a number"); }
*/
} catch (Exception e) {
return -1;
}
}
private int parse_bin() throws ArithmeticException, IllegalArgumentException {
/*!re2c
end { return number; }
[01] { add_digit(2, 48); return parse_bin(); }
* { throw new IllegalArgumentException("ill-formed binary number"); }
*/
}
private int parse_oct() throws ArithmeticException, IllegalArgumentException {
/*!re2c
end { return number; }
[0-7] { add_digit(8, 48); return parse_oct(); }
* { throw new IllegalArgumentException("ill-formed octal number"); }
*/
}
private int parse_dec() throws ArithmeticException, IllegalArgumentException {
/*!re2c
end { return number; }
[0-9] { add_digit(10, 48); return parse_dec(); }
* { throw new IllegalArgumentException("ill-formed decimal number"); }
*/
}
private int parse_hex() throws ArithmeticException, IllegalArgumentException {
/*!re2c
end { return number; }
[0-9] { add_digit(16, 48); return parse_hex(); }
[a-f] { add_digit(16, 87); return parse_hex(); }
[A-F] { add_digit(16, 55); return parse_hex(); }
* { throw new IllegalArgumentException("ill-formed hexadecimal number"); }
*/
}
public static void main(String []args) {
Parser parser = new Parser();
assert parser.parse("1234567890\0") == 1234567890;
assert parser.parse("0b1101\0") == 0b1101;
assert parser.parse("0x007Fe\0") == 0x7fe;
assert parser.parse("0644\0") == 0644;
assert parser.parse("9999999999\0") == -1;
assert parser.parse("123??\0") == -1;
}
};
شرایط شروع با گزینهٔ --start-conditions فعال میشوند. آنها روشی را برای کدگذاری چندین اتوماتای مرتبط با یکدیگر در یک بلوک یکسان re2java فراهم میکنند.
هر شرط متناظر با یک اتوماتای منفرد است و دارای یک نام منحصربهفرد مشخصشده توسط کاربر و یک شمارهٔ داخلی منحصربهفرد تعریفشده توسط re2java است. این شمارهها برای جابهجایی بین شرایط به کار میروند: کد تولیدشده از پایههای 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 پرش میکند.
کدی که re2java برای شرایط تولید میکند بستگی به این دارد که re2java از رویکرد goto/label یا رویکرد loop/switch برای کدگذاری اتوماتا استفاده کند.
در زبانهایی که دارای دستور goto هستند (مانند C/C++ و Go)، شرایط بهطور طبیعی به عنوان بلوکهایی از کد با پیشوند برچسبهایی به فرم yyc_<cond> پیادهسازی میشوند، که در آن cond نام یک شرط است (پیشوند برچسب میتواند با re2c:cond:prefix تغییر یابد). گذارها بین شرایط با استفاده از goto و برچسبهای شرط پیادهسازی میشوند. پیش از همهٔ شرایط، re2java یک switch اولیه بر روی YYGETSTATE تولید میکند که به وضعیت شروعِ شرط جاری میپرد. قواعد میانبر :=> از switch اولیه عبور کرده و مستقیماً به شرط تعیینشده پرش میکنند (برای تغییر رفتار پیشفرض میتوان از re2c:cond:goto استفاده کرد). قواعد دارای کنشهای معنایی بهطور خودکار به شرط بعدی نمیپرند؛ این کار باید توسط کد کنشِ تعریفشده توسط کاربر انجام شود.
در زبانهایی که فاقد goto هستند (مانند Rust)، re2java متغیر yystate را برای ذخیرهٔ شمارههای شرط بازاستفاده میکند. هر شرط یک شناسهٔ عددی برابر با شمارهٔ وضعیت شروع خود دریافت میکند، و switch بین شرایط هیچ تفاوتی با switch بین وضعیتهای DFA یک شرط منفرد ندارد. نیازی به یک switch شرط اولیهٔ مجزا نیست. (از آنجا که رویکرد یکسانی برای پیادهسازی وضعیتهای قابل ذخیره به کار میرود، در صورت استفادهٔ همزمان از وضعیتهای قابل ذخیره و شرایط، YYGETCOND/YYSETCOND زائد خواهند بود).
برنامهٔ زیر از شرایط شروع برای تجزیهٔ اعداد دودویی (باینری)، هشتهشتی (اکتال)، دهدهی (دسیمال) و شانزدهشانزدهی (هگزادسیمال) استفاده میکند. یک بلوک منفرد وجود دارد که در آن هر مبنا شرط مختص به خود را دارد، و شرط اولیه به همهٔ آنها متصل است. متغیر تعریفشده توسط کاربر cond شمارهٔ شرط جاری را ذخیره میکند؛ این متغیر با شمارهٔ شرط اولیه که با بلوک conditions تولید شده است، مقداردهی اولیه میشود.
// re2java $INPUT -o $OUTPUT -c
class Parser {
/*!conditions:re2c*/
private String yyinput;
private int yycursor;
private int yymarker;
private int number;
private void add_digit(int base, int offset) throws ArithmeticException {
number = Math.addExact(
Math.multiplyExact(number, base),
yyinput.charAt(yycursor - 1) - offset);
}
public int parse(String str) throws ArithmeticException, IllegalArgumentException {
yyinput = str;
yycursor = 0;
int yycond = YYC_init;
number = 0;
try {
loop: while (true) {
/*!re2c
re2c:YYCTYPE = "char";
re2c:YYPEEK = "yyinput.charAt(yycursor)";
re2c:yyfill:enable = 0;
<*> * { throw new IllegalArgumentException("ill-formed number"); }
<init> '0b' / [01] :=> bin
<init> "0" :=> oct
<init> "" / [1-9] :=> dec
<init> '0x' / [0-9a-fA-F] :=> hex
<bin, oct, dec, hex> "\x00" { return number; }
<bin> [01] { add_digit(2, 48); continue loop; }
<oct> [0-7] { add_digit(8, 48); continue loop; }
<dec> [0-9] { add_digit(10, 48); continue loop; }
<hex> [0-9] { add_digit(16, 48); continue loop; }
<hex> [a-f] { add_digit(16, 87); continue loop; }
<hex> [A-F] { add_digit(16, 55); continue loop; }
*/
}
} catch (Exception e) {
return -1;
}
}
public static void main(String []args) {
Parser parser = new Parser();
assert parser.parse("1234567890\0") == 1234567890;
assert parser.parse("0b1101\0") == 0b1101;
assert parser.parse("0x007Fe\0") == 0x7fe;
assert parser.parse("0644\0") == 0644;
assert parser.parse("9999999999\0") == -1;
assert parser.parse("123??\0") == -1;
}
};
با گزینهٔ --storable-state، برنامهٔ re2java تحلیلگر لغویای تولید میکند که میتواند وضعیت فعلی خود را ذخیره کند، به فراخواننده بازگردد، و بعداً عملیات را دقیقاً از همانجایی که رها کرده بود از سر بگیرد. حالت پیشفرض عملکرد در re2java یک مدل «کِششی» ("pull") است که در آن تحلیلگر لغوی هر زمان که نیاز داشته باشد، ورودی بیشتری را «میکشد» ("pulls"). این امر ممکن است در مواردی که ورودی بهصورت تکهتکه در دسترس قرار میگیرد غیرقابلقبول باشد (برای مثال، اگر تحلیلگر لغوی توسط تجزیهکننده فراخوانی شود، یا اگر برنامهٔ تحلیلگر لغوی از طریق یک پروتکل سوکت با برنامهٔ دیگری ارتباط برقرار کند که باید پیش از ارسال پیام بعدی، منتظر پاسخی از تحلیلگر لغوی بماند). قابلیت وضعیت قابل ذخیرهسازی دقیقاً برای چنین مواردی در نظر گرفته شده است: این قابلیت امکان تولید تحلیلگرهای لغویای را میدهد که در یک مدل «رانشی» ("push") کار میکنند. هنگامی که تحلیلگر لغوی به ورودی بیشتری نیاز دارد، وضعیت خود را ذخیره کرده و به فراخواننده بازمیگردد. بعداً، زمانی که ورودی بیشتری در دسترس قرار گیرد، فراخواننده تحلیلگر لغوی را دقیقاً از جایی که متوقف شده بود از سر میگیرد. در مقایسه با مدل "pull"، چند تغییر لازم است:
- تعریف اولیههای YYSETSTATE() و YYGETSTATE(state).
- تعریف متغیرهای yych، yyaccept (در صورت استفاده) و state بهعنوان بخشی از وضعیت پایدار تحلیلگر لغوی. متغیر state باید با -1 مقداردهی اولیه شود.
- YYFILL باید به جای تلاش برای تأمین ورودی بیشتر، به برنامهٔ بیرونی بازگردد. کد بازگشتی باید نشان دهد که تحلیلگر لغوی به ورودی بیشتری نیاز دارد.
- برنامهٔ بیرونی باید موقعیتهایی را که تحلیلگر لغوی به ورودی بیشتری نیاز دارد تشخیص داده و بهطور مناسب پاسخ دهد.
- استفادهٔ اختیاری از بلوک getstate برای تولید سوئیچ YYGETSTATE جدا شده از تحلیلگر لغوی اصلی. این ویژگی فقط برای زبانهایی که دارای goto هستند کار میکند (نه در حالت --loop-switch).
- استفاده از re2c:eof و روش sentinel with bounds checks برای مدیریت پایان ورودی. روش مبتنی بر لایهگذاری (padding) ممکن است کار نکند زیرا مشخص نیست چه زمانی باید لایهگذاری را اضافه کرد: پایان فعلی ورودی ممکن است پایان نهایی ورودی نباشد، و اضافه کردن زودهنگام لایهگذاری ممکن است یک واحد لغوی (lexeme) حریصانه را که تا حدی خوانده شده است قطع کند. علاوه بر این، با توجه به منطق سطح بالای برنامه، دریافت ورودی بیشتر ممکن است به پردازش واحد لغوی در انتهای بافر وابسته باشد (که از قبل بهدلیل شرایط پایان ورودی مسدود شده است).
در اینجا مثالی از یک تحلیلگر لغوی با مدل "push" آورده شده است که خواندن بستهها از یک سوکت را شبیهسازی میکند. تحلیلگر لغوی تا زمانی که با پایان ورودی مواجه شود حلقه میزند و به تابع فراخواننده بازمیگردد. تابع فراخواننده با «ارسال» ("sending") بستهٔ بعدی، ورودی بیشتری تأمین میکند و تحلیل لغوی را از سر میگیرد. این فرایند زمانی که تمام بستهها ارسال شوند، یا خطایی رخ دهد، متوقف میشود.
// re2java $INPUT -o $OUTPUT -f
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Pipe;
class Lexer {
enum Status {
END,
READY,
WAITING,
BIG_PACKET,
BAD_PACKET
};
// Use a small buffer to cover the case when a lexeme doesn't fit.
// In real world use a larger buffer.
public static final int BUFSIZE = 10;
public static class State {
Pipe.SourceChannel source;
byte[] yyinput;
int yycursor;
int yymarker;
int yylimit;
int token;
int yystate;
int received;
public State(Pipe pipe) {
source = pipe.source();
// Sentinel at `yylimit` offset is set to zero, which triggers YYFILL.
yyinput = new byte[BUFSIZE + 1];
yycursor = yymarker = yylimit = token = BUFSIZE;
yystate = -1;
received = 0;
}
}
private static void log(String format, Object... args) {
if (false) { System.out.printf(format + "\n", args); }
}
private static Status fill(State st) throws IOException {
// Error: lexeme too long. In real life can reallocate a larger buffer.
if (st.token < 1) { return Status.BIG_PACKET; }
// Shift buffer contents (discard everything up to the current token).
System.arraycopy(st.yyinput, st.token, st.yyinput, 0, st.yylimit - st.token);
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.
ByteBuffer buffer = ByteBuffer.wrap(st.yyinput, st.yylimit, BUFSIZE - st.yylimit);
int have = st.source.read(buffer);
if (have != -1) st.yylimit += have; // -1 means that pipe is closed
st.yyinput[st.yylimit] = 0; // append sentinel symbol
return Status.READY;
}
private static Status lex(State yyrecord) {
int yych;
loop: while (true) {
yyrecord.token = yyrecord.yycursor;
/*!re2c
re2c:api = record;
re2c:YYCTYPE = "int";
re2c:YYPEEK = "Byte.toUnsignedInt(yyrecord.yyinput[yyrecord.yycursor])";
re2c:YYFILL = "return Status.WAITING;";
re2c:eof = 0;
packet = [a-z]+[;];
* { return Status.BAD_PACKET; }
$ { return Status.END; }
packet { yyrecord.received += 1; continue loop; }
*/
}
}
public static void test(String[] packets, Status expect) throws IOException {
// Create a pipe.
Pipe pipe = Pipe.open();
Pipe.SinkChannel sink = pipe.sink();
// Initialize lexer state
Lexer.State st = new Lexer.State(pipe);
// 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.
int send = 0;
Status status;
while (true) {
status = lex(st);
if (status == Status.END) {
log("done: got %d packets", st.received);
break;
} else if (status == Status.WAITING) {
log("waiting...");
if (send < packets.length) {
log("sent packet %d: %s", send, packets[send]);
ByteBuffer buffer = ByteBuffer.wrap(packets[send].getBytes());
sink.write(buffer);
send += 1;
} else {
sink.close();
}
status = fill(st);
if (status == Status.BIG_PACKET) {
log("error: packet too big");
break;
}
assert status == Status.READY;
} else {
assert status == Status.BAD_PACKET;
log("error: ill-formed packet");
break;
}
}
// Check results.
assert status == expect;
if (status == Status.END) {
assert send == st.received;
}
}
public static void main(String []args) throws IOException {
test(new String[]{}, Status.END);
test(new String[]{"zero;", "one;", "two;", "three;", "four;"}, Status.END);
test(new String[]{"zer0;"}, Status.BAD_PACKET);
test(new String[]{"goooooooooogle;"}, Status.BIG_PACKET);
}
};
بلوکهای با قابلیت استفاده مجدد به شکل /*!rules:re2c[:<name>] ... */ یا %{rules[:<name>] ... %} را میتوان به هر تعداد بار مجدداً استفاده کرد و با سایر بلوکهای re2java ترکیب نمود. بخش <name> اختیاری است. یک بلوک rules را میتوان در یک بلوک یا دستورالعمل use به کار برد. کد یک بلوک rules در هر نقطهای که استفاده شود، تولید میگردد.
بلوکهای use با /*!use:re2c[:<name>] ... */ یا %{use[:<name>] ... %} تعریف میشوند. بخش <name> اختیاری است: اگر مشخص نشود، بلوک rules مرتبط جدیدترین بلوک خواهد بود (چه نامگذاری شده باشد، چه بدون نام). یک بلوک use میتواند تعاریف نامدار، پیکربندیها و قوانین مختص به خود را اضافه کند. یک مورد استفاده مهم برای بلوکهای use، تحلیلگر لغویای است که از چندین کدگذاری ورودی پشتیبانی میکند: همان بلوک rules چندین بار با پیکربندیهای خاص هر کدگذاری استفاده مجدد میشود (مثال زیر را ببینید).
دستورالعمل درونبلوکی !use:<name>; را میتوان از داخل یک بلوک re2java استفاده کرد. این دستورالعمل بلوک ارجاعدادهشده <name> را در بلوک فعلی ادغام میکند. اگر برخی از قوانین و پیکربندیهای ادغامشده با موارد تعریفشده پیشین همپوشانی داشته باشند، تداخلها به روش معمول حلوفصل میشوند: اولین قاعده اولویت دارد و آخرین پیکربندی بر موارد پیشین بازنویسی میشود. یک استثنا، قواعد خاص *، $ و (در حالت شرط) <!> هستند که برای آنها یک تعریف محلی در بلوک، بر هر تعریف به ارثبردهشده ارجحیت دارد. دستورالعمل use به شخص امکان میدهد بلوکهای مختلف re2java را در یک بلوک با هم ترکیب کند (مثال زیر را ببینید).
بلوکهای نامدار و دستورالعمل درونبلوکی use در نسخه 2.2 برنامه re2java اضافه شدند. از آن نسخه به بعد، بلوکهای با قابلیت استفاده مجدد به صورت پیشفرض مجاز هستند (نیازی به گزینه خاصی نیست). پیش از نسخه 2.2، حالت استفاده مجدد با گزینه -r --reusable فعال میشد. پیش از نسخه 1.2، بلوکهای با قابلیت استفاده مجدد نمیتوانستند با بلوکهای معمولی ترکیب شوند.
مثالی از دستورالعمل !use
// re2java $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.
/*!rules:re2c:colors
* { throw new IllegalArgumentException("ah"); }
"red" | "salmon" | "magenta" { return Ans.COLOR; }
*/
/*!rules:re2c:fish
* { throw new IllegalArgumentException("oh"); }
"haddock" | "salmon" | "eel" { return Ans.FISH; }
*/
class Main {
enum Ans {COLOR, FISH, DUNNO};
static Ans lex(String yyinput) { // no-throw, as '*' rules are overridden
int yycursor = 0;
int yymarker = 0;
/*!re2c
re2c:yyfill:enable = 0;
re2c:YYCTYPE = "char";
re2c:YYPEEK = "yyinput.charAt(yycursor)";
!use:fish;
!use:colors;
* { return Ans.DUNNO; } // overrides inherited '*' rules
*/
}
public static void main(String []args) {
assert lex("salmon") == Ans.FISH;
assert lex("what?") == Ans.DUNNO;
}
};
// re2java $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;
re2c:YYPEEK = "yyinput[yycursor]";
re2c:indent:top = 1;
"∀x ∃y" { return true; }
* { return false; }
*/
class Main {
static boolean lex_utf8(int[] yyinput) {
int yycursor = 0;
int yymarker = 0;
/*!use:re2c
re2c:YYCTYPE = "int"; // Java lacks unsigned 8-bit integer type
re2c:encoding:utf8 = 1;
*/
}
static boolean lex_utf32(int[] yyinput) {
int yycursor = 0;
int yymarker = 0;
/*!use:re2c
re2c:YYCTYPE = "int";
re2c:encoding:utf32 = 1;
*/
}
public static void main(String []args) {
// we have to use `int`, because `byte`in Java cannot represent values greater than 127
int[] s_utf8 = new int[]{0xe2, 0x88, 0x80, 0x78, 0x20, 0xe2, 0x88, 0x83, 0x79};
assert lex_utf8(s_utf8);
int[] s_utf32 = new int[]{0x2200, 0x78, 0x20, 0x2203, 0x79};
assert lex_utf32(s_utf32);
}
};
استخراج زیرتطابق (Submatch extraction)
ابزار re2java دو گزینه برای استخراج زیرتطابقها دارد.
- 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 longest-match) میباشد. در این حالت تمام زیرعبارتهای درون پرانتز، گروههای گیرانداز در نظر گرفته میشوند، و از علامت تعجب میتوان برای مشخص کردن گروههای غیرگیرانداز استفاده کرد: (! ... ). با گزینهٔ --invert-captures یا پیکربندی re2c:invert-captures = 1، مفهوم علامت تعجب معکوس میشود. تعداد گروهها برای قاعدهٔ منطبقشده در متغیر yynmatch ذخیره میشود (کل عبارت باقاعده گروه شمارهٔ صفر است)، و نتایج زیرتطابق در آرایهٔ yypmatch ذخیره میشوند. هر دو متغیر yynmatch و yypmatch باید توسط کاربر تعریف شوند، و اندازهٔ yypmatch باید حداقل برابر با [yynmatch * 2] باشد. از بلوک maxnmatch برای تعریف YYMAXNMATCH استفاده کنید؛ ثابتی که برابر با بیشترین مقدار yynmatch در میان تمام قواعد است.
- Captvars
- روش دیگر برای استفاده از گروههای گیراندازی، گزینهٔ --captvars یا پیکربندی re2c:captvars = 1 است. تنها تفاوت آن با --captures در نحوهٔ ذخیرهسازی نتایج زیرتطابق توسط کد تولیدشده است: بهجای yynmatch و yypmatch، ابزار re2java متغیرهای yytl<k> و yytr<k> را برای k-امین گروه گیراندازی تولید میکند (کاربر باید اینها را با استفاده از یک بلوک svars اعلان کند). گیراندازی با متغیرها از دو سیاست رفع ابهام پشتیبانی میکند: --leftmost-captvars یا re2c:leftmost-captvars = 1 برای سیاست چپترین حریصانه (حالت پیشفرض) و --posix-captvars یا re2c:posix-captvars برای سیاست طولانیترین تطابق پازیکس.
در پشت صحنه، تمام این گزینهها به برچسبها و خودکارههای متناهی معین برچسبدار با پیشنگری (TDFA) ترجمه میشوند. ایدهٔ اصلی TDFA به حداقل رساندن بار اضافی (overhead) روی استخراج زیرتطابق است. در حالت نهایی، اگر هیچ برچسب یا گیراندازیای در یک عبارت باقاعده نباشد، TDFA صرفاً یک DFA معمولی خواهد بود. اگر تعداد برچسبها متعادل باشد، بار اضافی به سختی محسوس است. TDFA تولیدشده از تعدادی متغیر برچسب استفاده میکند که مستقیماً به برچسبها نگاشت نمیشوند: یک متغیر منفرد ممکن است برای برچسبهای مختلف استفاده شود، و یک برچسب ممکن است برای نگهداری تمام مقادیر ممکن خود به چندین متغیر نیاز داشته باشد. در نهایت ابهام حل میشود و تنها یک متغیر نهایی برای هر برچسب باقی میماند. متغیرهای برچسب باید با استفاده از بلوکهای stags یا mtags تعریف شوند. اگر وضعیت لکسر ذخیره میشود، متغیرهای برچسب باید بخشی از آن باشند. آنها همچنین باید توسط YYFILL بهروزرسانی شوند.
برچسبهای S-tag از عملیاتهای زیر پشتیبانی میکنند:
- ذخیرهٔ موقعیت ورودی در یک s-tag: عبارت t = YYCURSOR با API اشارهگر C یا عملیات تعریفشده توسط کاربر YYSTAGP(t) با API عمومی (generic API)
- ذخیرهٔ مقدار پیشفرض در یک s-tag: عبارت t = NULL با API اشارهگر C یا عملیات تعریفشده توسط کاربر YYSTAGN(t) با API عمومی
- کپی کردن یک s-tag در دیگری: t1 = t2
برچسبهای M-tag از عملیاتهای زیر پشتیبانی میکنند:
- الحاق موقعیت ورودی به یک m-tag: عملیات تعریفشده توسط کاربر YYMTAGP(t) هم با API پیشفرض و هم با API عمومی
- الحاق مقدار پیشفرض به یک m-tag: عملیات تعریفشده توسط کاربر YYMTAGN(t) هم با API پیشفرض و هم با API عمومی
- کپی کردن یک m-tag در دیگری: t1 = t2
برچسبهای S-tag را میتوان بهصورت مقادیر اسکالر (اشارهگرها یا آفستها) پیادهسازی کرد. برچسبهای M-tag به نمایش پیچیدهتری نیاز دارند، زیرا باید دنبالهای از مقادیر برچسب را ذخیره کنند. سادهترین و ناکارآمدترین نمایش برای یک m-tag، یک لیست (آرایه، بردار) از مقادیر برچسب است؛ نمایش کارآمدتر، ذخیرهٔ تمام m-tagها در یک درخت پیشوندی (prefix-tree) است که بهصورت آرایهای از گرههای (v, p) نشان داده میشود، که در آن v مقدار برچسب و p اشارهگری به گره والد است.
در اینجا یک مثال ساده از کاربرد s-tagها برای تجزیهٔ نگارشهای معنایی (semantic versions) متشکل از سه جزء عددی آورده شده است: major ،minor و patch (مورد آخر اختیاری است). برای نمونهای پیچیدهتر که از YYFILL استفاده میکند، به ادامه مراجعه کنید.
// re2java $INPUT -o $OUTPUT
import java.util.Optional;
class Main {
static class SemVer {
int major;
int minor;
int patch;
public SemVer(int m, int n, int k) {
major = m;
minor = n;
patch = k;
}
public boolean equals(SemVer v) {
return major == v.major && minor == v.minor && patch == v.patch;
}
};
static Optional<SemVer> parse(String yyinput) {
int yycursor = 0;
int yymarker = 0;
// Final tag variables available in semantic action.
/*!svars:re2c format = "int @@;"; */
// Intermediate tag variables used by the lexer (must be autogenerated).
/*!stags:re2c format = "int @@ = -1;"; */
/*!re2c
re2c:YYCTYPE = "char";
re2c:YYPEEK = "yyinput.charAt(yycursor)";
re2c:yyfill:enable = 0;
re2c:tags = 1;
num = [0-9]+;
@t1 num @t2 "." @t3 num @t4 ("." @t5 num)? [\x00] {
int major = Integer.valueOf(yyinput.substring(t1, t2));
int minor = Integer.valueOf(yyinput.substring(t3, t4));
int patch = (t5 == -1) ? 0 : Integer.valueOf(yyinput.substring(t5, yycursor - 1));
return Optional.of(new SemVer(major, minor, patch));
}
* { return Optional.empty(); }
*/
}
public static void main(String []args) {
assert parse("23.34\0").get().equals(new SemVer(23, 34, 0));
assert parse("1.2.99999\0").get().equals(new SemVer(1, 2, 99999));
assert !parse("1.a\0").isPresent();
}
};
// re2java $INPUT -o $OUTPUT
import java.io.*;
import java.nio.file.*;
import java.util.*;
class Lexer {
static class SemVer {
int major;
int minor;
int patch;
public SemVer(int m, int n, int k) {
major = m;
minor = n;
patch = k;
}
public boolean equals(SemVer v) {
return major == v.major && minor == v.minor && patch == v.patch;
}
};
public static final int BUFSIZE = 4096;
private BufferedInputStream stream;
private byte[] yyinput;
private int yycursor;
private int yymarker;
private int yylimit;
private int token;
// Intermediate tag variables used by the lexer (must be autogenerated).
/*!stags:re2c format = "private int @@;\n"; */
private boolean eof;
public Lexer(File file) throws FileNotFoundException {
stream = new BufferedInputStream(new FileInputStream(file));
// Sentinel at `yylimit` offset is set to zero, which triggers YYFILL.
yyinput = new byte[BUFSIZE + 1];
yycursor = yymarker = yylimit = token = BUFSIZE;
/*!stags:re2c format = "@@ = -1;\n"; */
eof = false;
}
private int fill() throws IOException {
if (eof) { return -1; } // unexpected EOF
// Error: lexeme too long. In real life can reallocate a larger buffer.
if (token < 1) { return -2; }
// Shift buffer contents (discard everything up to the current token).
System.arraycopy(yyinput, token, yyinput, 0, yylimit - token);
yycursor -= token;
yymarker -= token;
yylimit -= token;
/*!stags:re2c format = "if (@@ != -1) {@@ -= token;}\n"; */
token = 0;
// Fill free space at the end of buffer with new data from file.
yylimit += stream.read(yyinput, yylimit, BUFSIZE - yylimit);
yyinput[yylimit] = 0; // append sentinel symbol
// If read less than expected, this is the end of input.
eof = yylimit < BUFSIZE;
return 0;
}
private int readInt(int tag1, int tag2) {
int n = 0;
for (int i = tag1; i < tag2; ++i) { n = n * 10 + (yyinput[i] - 48); }
return n;
}
public Optional<ArrayList<SemVer>> lex() throws IOException {
ArrayList<SemVer> vers = new ArrayList<SemVer>();
// Final tag variables available in semantic action.
/*!svars:re2c format = "int @@;"; */
loop: while (true) {
token = yycursor;
/*!re2c
re2c:YYCTYPE = "int";
re2c:YYPEEK = "Byte.toUnsignedInt(yyinput[yycursor])";
re2c:YYFILL = "fill() == 0";
re2c:eof = 0;
re2c:tags = 1;
num = [0-9]+;
@t1 num @t2 "." @t3 num @t4 ("." @t5 num)? [\n] {
int major = readInt(t1, t2);
int minor = readInt(t3, t4);
int patch = (t5 == -1) ? 0 : readInt(t5, yycursor - 1);
vers.add(new SemVer(major, minor, patch));
continue loop;
}
$ { return Optional.of(vers); }
* { return Optional.empty(); }
*/
}
}
public static void main(String []args) throws FileNotFoundException, IOException {
String fname = "input";
String content = "1.22.333\n".repeat(Lexer.BUFSIZE);
// Prepare input file: a few times the size of the buffer, containing
// strings with zeroes and escaped quotes.
Files.writeString(Paths.get(fname), content);
// Prepare lexer state: all offsets are at the end of buffer.
File file = new File(".", fname);
Lexer lexer = new Lexer(file);
// Run the lexer.
Optional<ArrayList<SemVer>> vers = lexer.lex();
// Check results.
assert vers.isPresent() && vers.get().size() == BUFSIZE;
SemVer v = new SemVer(1, 22, 333);
for (int i = 0; i < BUFSIZE; ++i) {
assert vers.get().get(i).equals(v);
}
// Cleanup: remove input file.
file.delete();
}
};
در اینجا مثالی از استفاده از گروههای تسخیر (capturing groups) برای تجزیه نسخههای معنایی آمده است.
// re2java $INPUT -o $OUTPUT
import java.util.Optional;
class Main {
static class SemVer {
int major;
int minor;
int patch;
public SemVer(int m, int n, int k) {
major = m;
minor = n;
patch = k;
}
public boolean equals(SemVer v) {
return major == v.major && minor == v.minor && patch == v.patch;
}
};
static Optional<SemVer> parse(String yyinput) {
int yycursor = 0;
int yymarker = 0;
// Final tag variables available in semantic action.
/*!svars:re2c format = "int @@;"; */
// Intermediate tag variables used by the lexer (must be autogenerated).
/*!stags:re2c format = "int @@ = -1;"; */
/*!re2c
re2c:YYCTYPE = "char";
re2c:YYPEEK = "yyinput.charAt(yycursor)";
re2c:yyfill:enable = 0;
re2c:captvars = 1;
num = [0-9]+;
(num) "." (num) ("." num)? [\x00] {
int major = Integer.valueOf(yyinput.substring(yytl1, yytr1));
int minor = Integer.valueOf(yyinput.substring(yytl2, yytr2));
int patch = (yytl3 == -1) ? 0
: Integer.valueOf(yyinput.substring(yytl3 + 1, yytr3));
return Optional.of(new SemVer(major, minor, patch));
}
* { return Optional.empty(); }
*/
}
public static void main(String []args) {
assert parse("23.34\0").get().equals(new SemVer(23, 34, 0));
assert parse("1.2.99999\0").get().equals(new SemVer(1, 2, 99999));
assert !parse("1.a\0").isPresent();
}
};
// re2java $INPUT -o $OUTPUT
import java.util.*;
class Main {
static Optional<int[]> parse(String yyinput) {
int yycursor = 0;
int yymarker = 0;
// Final tag variables available in semantic action.
/*!svars:re2c format = "int @@;"; */
/*!mvars:re2c format = "List<Integer> @@;"; */
// Intermediate tag variables used by the lexer (must be autogenerated).
/*!stags:re2c format = "int @@ = -1;"; */
/*!mtags:re2c format = "List<Integer> @@ = new ArrayList<>();"; */
/*!re2c
re2c:YYCTYPE = "char";
re2c:YYPEEK = "yyinput.charAt(yycursor)";
re2c:YYMTAGP = "@@.add(yycursor);";
re2c:YYMTAGN = ""; // do nothing
re2c:yyfill:enable = 0;
re2c:tags = 1;
num = [0-9]+;
@t1 num @t2 ("." #t3 num #t4)* [\x00] {
int[] vers = new int[t3.size() + 1];
vers[0] = Integer.valueOf(yyinput.substring(t1, t2));
for (int i = 0; i < t3.size(); ++i) {
vers[i + 1] = Integer.valueOf(yyinput.substring(t3.get(i), t4.get(i)));
}
return Optional.of(vers);
}
* { return Optional.empty(); }
*/
}
public static void main(String []args) {
assert Arrays.equals(parse("1\0").get(), new int[]{1});
assert Arrays.equals(parse("1.2.3.4.5.6.7\0").get(), new int[]{1, 2, 3, 4, 5, 6, 7});
assert !parse("1.2.\0").isPresent();
}
};
پشتیبانی از کدگذاری (Encoding support)
درک تفاوت بین نقطهکدها (code points) و واحدکدها (code units) ضروری است. یک نقطهکد، یک شناسه عددی برای یک نماد است. یک واحدکد، کوچکترین واحد ذخیرهسازی در متن کدگذاریشده است. یک نقطهکد منفرد ممکن است با یک یا چند واحدکد نشان داده شود. در یک کدگذاری با طول ثابت، همه نقطهکدها با تعداد یکسانی از واحدکدها نمایش داده میشوند. در یک کدگذاری با طول متغیر، نقطهکدها ممکن است با تعداد متفاوتی از واحدکدها نشان داده شوند. توجه داشته باشید که قاعده «هر چیز» [^] با هر نقطهکدی مطابقت دارد، اما لزوماً با هر واحدکدی منطبق نمیشود (تنها راه برای تطبیق با هر واحدکد بدون در نظر گرفتن نوع کدگذاری، قاعده پیشفرض * است). تحلیلگر لغوی تولیدشده با جریانی از واحدکدها کار میکند: yych یک واحدکد را ذخیره میکند، و YYCTYPE نوع واحدکد است. از سوی دیگر، عبارتهای منظم بر اساس نقطهکدها مشخص میشوند. هنگامی که re2java عبارتهای منظم را به اتوماتا کامپایل میکند، نقطهکدها را به واحدکدها ترجمه مینماید. این کار معمولاً یک نگاشت ساده نیست: در کدگذاریهای با طول متغیر، بازهای از یک نقطهکد منفرد ممکن است به یک گراف پیچیده از واحدکدها تبدیل شود. کدگذاریهای زیر پشتیبانی میشوند:
- 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 تعاریف re2java را برای بلوکها، دستهبندیها و ویژگیهای توصیفشده در استاندارد یونیکد ارائه میدهند.
گزینه --input-encoding کدگذاری فایل مبدأ را مشخص میکند که میتواند برای فعالسازی لیترالهای یونیکد در عبارتهای منظم به کار رود. برای مثال --input-encoding utf8 به re2java اعلام میکند که فایل مبدأ با کدگذاری UTF8 است (این گزینه با --utf8 که کدگذاری متن ورودی را تعیین میکند متفاوت است). گزینه --encoding-policy شیوهای را که re2java جانشینهای یونیکد (نقطهکدهای موجود در بازه [0xD800-0xDFFF]) را مدیریت میکند مشخص میسازد.
در زیر مثالی از یک تحلیلگر لغوی برای شناسههای یونیکد کدگذاریشده با UTF8 آمده است.
// re2java $INPUT -o $OUTPUT --utf8 -s
/*!include:re2c "unicode_categories.re" */
class Main {
static boolean lex(String yyinput) {
int yycursor = 0;
int yymarker = 0;
/*!re2c
re2c:YYCTYPE = "char";
re2c:YYPEEK = "yyinput.charAt(yycursor)";
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+;
// It should be `id_start id_continue*`, but that causes `error: code too large`
identifier { return true; }
* { return false; }
*/
}
public static void main(String []args) {
assert lex("_Ыдентификатор\0");
}
};
ابزار re2java به شما امکان میدهد با استفاده از بلوکی به قالب /*!include:re2c FILE */ یا %{include FILE %}، یا دستور درونبلوکی !include FILE ; فایلهای دیگر را بگنجانید، که در آن FILE مسیر فایلی است که باید گنجانده شود. ابزار re2java فایلهای الحاقی را در دایرکتوریِ فایلی که عمل الحاق را انجام داده و همچنین در مکانهای include که میتوان آنها را با گزینهٔ -I تعیین کرد، جستجو میکند. بلوکها/دستورهای include در re2java همانند #include در C/C++ عمل میکنند: محتوای FILE عیناً و بدون تغییر در جای بلوک/دستور قرار میگیرد. فایلهای الحاقی میتوانند خود شامل موارد include دیگری باشند. از گزینهٔ --depfile برای ردگیری وابستگیهای ساخت فایل خروجی به فایلهای الحاقی استفاده کنید. ابزار re2java تعدادی فایل الحاقی از پیشتعریفشده ارائه میدهد که در زیردایرکتوری include/ پروژه یافت میشوند. این فایلها شامل تعاریفی هستند که ممکن است برای سایر پروژهها نیز مفید باشند (مانند دستهبندیهای یونیکد) و ساختاری شبیه به یک کتابخانهٔ استاندارد برای re2java تشکیل میدهند. در ادامه مثالی از کاربرد فایلهای الحاقی آورده شده است.
فایل الحاقی ۱ (definitions.java)
/*!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 Num.FLOAT; }
فایل ورودی (Input file)
// re2java $INPUT -o $OUTPUT
/*!include:re2c "definitions.java" */
class Main {
enum Num {INT, FLOAT, NAN};
static Num lex(String yyinput) {
int yycursor = 0;
int yymarker = 0;
/*!re2c
re2c:YYCTYPE = "char";
re2c:YYPEEK = "yyinput.charAt(yycursor)";
re2c:yyfill:enable = 0;
* { return Num.NAN; }
number { return Num.INT; }
!include "extra_rules.re.inc";
*/
}
public static void main(String []args) {
assert lex("123\0") == Num.INT;
assert lex("123.4567\0") == Num.FLOAT;
}
};
ابزار re2java امکان تولید فایل هدر از فایل ورودی .re را با استفاده از گزینهٔ --header یا پیکربندی re2c:header و جفتبلوکهایی به شکل /*!header:re2c:on*/ و /*!header:re2c:off*/، یا %{header:on%} و %{header:off%} فراهم میکند. بلوک نخست آغاز فایل هدر و بلوک دوم پایان آن را مشخص میکند. تمام موارد میان این بلوکها توسط re2java پردازش شده و کد تولیدشده در فایلی که با گزینهٔ --header یا پیکربندی re2c:header تعیین شده (یا در صورت عدم استفاده از گزینه یا پیکربندی، در stdout) نوشته میشود. تولید خودکار فایل هدر ممکن است در مواردی نیاز باشد که از re2java برای ایجاد تعاریفی استفاده میشود که باید از سایر واحدهای ترجمه (translation units) قابل دسترسی باشند.
در ادامه مثالی از تولید یک فایل هدر آورده شده است که شامل تعریف وضعیت تحلیلگر لغوی (lexer state) به همراه متغیرهای برچسب (tag variables) است (تعداد متغیرها به گرامر باقاعده بستگی داشته و برای برنامهنویس نامشخص است).
فایل ورودی (Input file)
// re2java $INPUT -o $OUTPUT --header lexer/state.java
package headers;
import headers.lexer.State;
/*!header:re2c:on*/
package headers.lexer;
public class State {
public String yyinput;
public int yycursor;
/*!stags:re2c format = "public int @@;\n"; */
public State(String str) {
yyinput = str;
yycursor = 0;
/*!stags:re2c format = "@@ = 0;\n"; */
}
};
/*!header:re2c:off*/
class Main {
static int lex(String str) {
State yyrecord = new State(str);
int t;
/*!re2c
re2c:api = record;
re2c:tags = 1;
re2c:yyfill:enable = 0;
re2c:YYCTYPE = "char";
re2c:YYPEEK = "yyrecord.yyinput.charAt(yyrecord.yycursor)";
re2c:header = "lexer/state.java";
[a]* @t [b]* { return t; }
*/
}
public static void main(String []args) {
assert lex("ab\0") == 1;
}
};
فایل هدر (Header file)
// Generated by re2c
package headers.lexer;
public class State {
public String yyinput;
public int yycursor;
public int yyt1;
public State(String str) {
yyinput = str;
yycursor = 0;
yyt1 = 0;
}
};
با گزینهٔ -S, --skeleton، ابزار re2java تمام کدهای غیر re2java را نادیده گرفته و یک برنامهٔ C خودکفا و مستقل تولید میکند که میتواند در ادامه کامپایل و اجرا شود. این برنامه از کد تحلیلگر لغوی (lexer) و دادههای ورودی تشکیل شده است. به ازای هر DFA ساختهشده (بلوک یا شرط)، re2java یک تحلیلگر لغوی مستقل و دو فایل تولید میکند: یک فایل .input با رشتههای مشتقشده از DFA و یک فایل .keys با نتایج تطابق مورد انتظار. این برنامه هر تحلیلگر لغوی را روی فایل .input متناظر اجرا کرده و نتایج را با انتظارات مقایسه میکند. برنامههای اسکلتی به دلایل متعددی بسیار مفید هستند:
- آنها میتوانند درستی بهینهسازیهای گوناگون re2java را بررسی کنند (دادهها در اوایل فرآیند و پیش از اعمال هرگونه تغییر و تبدیل در DFA تولید میشوند).
- تولید مجموعهای از دادههای ورودی با پوشش مناسب میتواند هم برای آزمون و هم برای سنجش کارایی (benchmarking) مفید باشد.
- تولید برنامههای اجرایی مستقل امکان دسترسی به نمونههای آزمون کمینهشده را فراهم میکند (کد اصلی ممکن است بزرگ بوده یا وابستگیهای فراوانی داشته باشد).
دشواری تولید دادههای ورودی در این است که به جز در سادهترین حالتها، تعداد رشتههای ورودیِ ممکن بیش از حد زیاد است (حتی اگر طول رشته محدود باشد). re2java این دشواری را با تولید رشتههای کافی برای پوشش دادن تقریباً تمام انتقالهای DFA برطرف میکند. این ابزار از الگوریتم زیر استفاده میکند. نخست، اسکلتی از DFA میسازد. برای رمزگذاریهایی با اندازهٔ واحد کد ۱ بایتی (مانند ASCII، UTF-8 و EBCDIC) اسکلت صرفاً یک رونوشت دقیق از DFA اصلی است. برای رمزگذاریهایی با واحدهای کد چندبایتی، اسکلت رونوشتی از DFA با حذف برخی از انتقالها است: بدین صورت که re2java حداکثر ۲۵۶ واحد کد را برای هر بازهٔ پیوسته و مجزا که با یک انتقال DFA متناظر است، در نظر میگیرد. مقادیر انتخابشده به طور یکنواخت توزیع شده و شامل کرانهای بازه نیز هستند. به جای تلاش برای پوشش تمام مسیرهای ممکن در اسکلت (که نشدنی است)، re2java مسیرهای کافی برای پوشش تمام انتقالهای اسکلت تولید میکند تا بدین ترتیب پرشهای شرطی مربوطه در تحلیلگر لغوی فعال شوند. پیادهسازی این الگوریتم به حدود ۱ گیگابایت انتقال محدود شده و مقدار ثابتی حافظه مصرف میکند (re2java دادهها را به محض تولید در فایل مینویسد).
مصورسازی و اشکالزدایی (Visualization and debug)
با گزینهٔ -D, --emit-dot، ابزار re2java کدی تولید نمیکند؛ در عوض، 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)
برنامهٔ re2java در ابتدا توسط 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.