| RE2JS(1) | RE2JS(1) |
نام (NAME)
re2js - تولید تحلیلگرهای واژگانی سریع برای JavaScript
خلاصه دستور (SYNOPSIS)
re2js [ OPTIONS ] [ WARNINGS ] INPUT
ورودی میتواند یک فایل یا - برای stdin باشد.
مقدمه (INTRODUCTION)
ابزار re2js مانند یک پیشپردازنده عمل میکند. این ابزار فایل ورودی (که معمولاً برنامهای در JavaScript است، اما میتواند هر چیز دیگری باشد) را میخواند و به دنبال بلوکهای کدی میگردد که درون نشانگرهای آغاز/پایان با فرم خاص محصور شدهاند. متن خارج از این بلوکها عیناً به فایل خروجی کپی میشود. محتوای درون بلوکها توسط re2js پردازش میشود. این ابزار آنها را به کد JavaScript ترجمه کرده و کد تولیدشده را به جای آن بلوک قرار میدهد.
در اینجا مثالی از یک برنامه کوچک آورده شده است که بررسی میکند آیا رشته دادهشده شامل یک عدد دهدهی است یا خیر:
// re2js $INPUT -o $OUTPUT
function lex(yyinput) {
let yycursor = 0;
/*!re2c
re2c:yyfill:enable = 0;
[1-9][0-9]* { return true; }
* { return false; }
*/
}
if (!lex("1234\0")) {
throw "error!"
}
در خروجی، re2js بلوک میانی را با کد تولیدشده جایگزین کرده است:
// Generated by re2js
// re2js $INPUT -o $OUTPUT
function lex(yyinput) {
let yycursor = 0;
{
let yych = 0
let yystate = 0
yyl: while (true) {
switch (yystate) {
case 0:
yych = yyinput.charCodeAt(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.charCodeAt(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 "internal lexer error"
}
}
}
}
if (!lex("1234\0")) {
throw "error!"
}
یک برنامهٔ re2js شامل دنبالهای از بلوکها درآمیخته با کدهایی به زبان مقصد است. یک بلوک میتواند شامل تعاریف، پیکربندیها، قواعد، کنشها و دستورالعملها به هر ترتیبی باشد:
- name = regular-expression ;
- یک تعریف، name را به regular-expression مقید میکند. نامها میتوانند شامل نویسههای الفبایی-عددی و زیرخط باشند. بخش عبارتهای منظم نمایی کلی از نحو عبارتهای منظم در re2js ارائه میدهد. پس از تعریف، name میتواند در دیگر عبارتهای منظم و قواعد استفاده شود. بازگشت در تعاریف نامگذاریشده مجاز نیست و هر نام باید پیش از استفاده تعریف شده باشد. یک بلوک، تعاریف نامگذاریشده را از حوزهٔ سراسری به ارث میبرد. تعریف مجدد نامی که در حوزهٔ فعلی وجود دارد یک خطا است.
- configuration = value ;
- یک پیکربندی امکان تغییر رفتار re2js و سفارشیسازی کد تولیدشده را فراهم میکند. برای مشاهدهٔ فهرست کاملی از پیکربندیهای پشتیبانیشده در re2js بخش پیکربندیها را ببینید. بسته به هر پیکربندی، value میتواند یک کلمهٔ کلیدی، یک عدد صحیح غیرمنفی یا یک رشتهٔ تکخطی باشد که در صورت شامل نبودن نویسههای صرفاً الفبایی-عددی باید در نقلقول تکی یا دوتایی قرار گیرد. یک بلوک پیکربندیها را از حوزهٔ سراسری به ارث برده و میتواند آنها را بازتعریف کند یا موارد جدیدی بیفزاید. پیکربندیهای تعریفشده درون یک بلوک بر کل بلوک تأثیر میگذارند، حتی اگر در انتهای آن قرار گرفته باشند.
- regular-expression code
- یک قاعده، regular-expression را به کنش معنایی آن پیوند میدهد (یک بلوک کد درون آکولد، یا یک بلوک کد که با := آغاز شده و به یک خط جدید به همراه یک نویسهٔ غیرفاصلهای پس از آن ختم میشود). اگر regular-expression مطابقت داشته باشد، code مربوطه اجرا میشود. اگر چندین قاعده مطابقت داشته باشند، طولانیترین تطبیق اولویت دارد. اگر چندین قاعده با رشتهٔ یکسانی مطابقت یابند، اولین آنها اولویت دارد. دو قاعدهٔ ویژه وجود دارد: قاعدهٔ پیشفرض * و قاعدهٔ پایان ورودی $. قاعدهٔ پیشفرض همواره باید تعریف شود، بدون توجه به جایگاهش در بلوک دارای کمترین اولویت است و با هر واحد کد مطابقت دارد (نه لزوماً یک نویسهٔ معتبر، بخش پشتیبانی از کدگذاری را ببینید). قاعدهٔ پایان ورودی در صورتی که روش مربوط به مدیریت پایان ورودی استفاده شده باشد، باید تعریف گردد. با بهکارگیری شرایط شروع، قواعد نحو پیچیدهتری خواهند داشت.
- !action code
- یک کنش، یک بلوک code تعریفشده توسط کاربر را به نقطهٔ خاصی در ماشین حالت متناهی تولیدشده متصل میکند (مشابه پیوند دادن کد به حالتهای پایانی توسط کنشهای معنایی). برای فهرست کامل کنشهای از پیش تعریفشده، بخش کنشها را ببینید.
- !directive ;
- یک دستورالعمل یکی از گزارههای از پیش تعریفشدهٔ ویژه است. هر دستورالعمل هدفی منحصربهفرد دارد. برای جزئیات به بخش دستورالعملها مراجعه کنید.
بلوکها (Blocks)
نشانگرهای آغاز و پایان بلوک یا /*!re2c و */، یا %{ و %} هستند (از هر دو شیوه پشتیبانی میشود). از نسخهٔ 2.2 بلوکها میتوانند نامهای اختیاری داشته باشند که امکان ارجاع به آنها را در دیگر بلوکها فراهم میکند. انواع مختلفی از بلوکها وجود دارند:
- /*!re2c[:<name>] ... */ or %{[:<name>] ... %}
- یک بلوک سراسری شامل تعاریف، پیکربندیها، قواعد و دستورالعملها است. ابزار re2js عبارتهای منظم مربوط به هر قاعده را در یک اتوماتون متناهی معین کامپایل کرده، آن را در قالب پرشهای شرطی در زبان مقصد کدگذاری میکند و بلوک را با کد تولیدشده جایگزین مینماید. نامها و پیکربندیهای تعریفشده در یک بلوک سراسری به حوزهٔ سراسری اضافه شده و برای بلوکهای بعدی قابل مشاهده خواهند بود. در ابتدای برنامه، حوزهٔ سراسری با گزینههای خط فرمان مقداردهی اولیه میشود.
- /*!local:re2c[:<name>] ... */ or %{local[:<name>] ... %}
- یک بلوک محلی شبیه یک بلوک سراسری است، اما نامها و پیکربندیهای موجود در آن حوزهٔ محلی دارند (بر سایر بلوکها تأثیری نمیگذارند).
- /*!rules:re2c[:<name>] ... */ or %{rules[:<name>] ... %}
- یک بلوک قواعد شبیه یک بلوک محلی است، با این تفاوت که به خودی خود کدی تولید نمیکند و تعریفی را هم به حوزهٔ سراسری اضافه نمینماید -- بلکه هدف آن، استفادهٔ مجدد در سایر بلوکها است. این روشی برای اشتراکگذاری کد است (جزئیات بیشتر در بخش بلوکهای با قابلیت استفاده مجدد). پیش از نسخهٔ 2.2 در re2js، بلوکهای قواعد نیازمند گزینهٔ -r --reusable بودند.
- /*!use:re2c[:<name>] ... */ or %{use[:<name>] ... %}
- یک بلوک استفاده که به یک بلوک قواعد از پیش تعریفشده ارجاع میدهد. اگر نامی مشخص شده باشد، re2js به دنبال یک بلوک قواعد با این نام میگردد. در غیر این صورت، نزدیکترین بلوک قواعد قبلی استفاده میشود (چه نامگذاریشده باشد و چه بینام). یک بلوک استفاده میتواند تعاریف، پیکربندیها و قواعد مخصوص به خود را اضافه کند که به موارد موجود در بلوک قواعد ارجاعشده افزوده میشوند. پیش از نسخهٔ 2.2 در re2js، بلوکهای استفاده به گزینهٔ -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 تولیدشده توسط re2js بسط داده میشود. یک فهرست اختیاری از نامهای بلوک مشخص میکند که هنگام محاسبهٔ مجموعه متغیرهای برچسب کدام بلوکها باید لحاظ گردند (اگر فهرست خالی باشد، تمام بلوکها لحاظ میشوند). دو پیکربندی اختیاری وجود دارد: 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) یا به صورت ضمنی تولیدشده توسط re2js (با گزینههای --captvars یا --posix-captvars)، بسط داده میشود. یک فهرست اختیاری از نامهای بلوک تعیین میکند که هنگام محاسبهٔ مجموعه برچسبها کدام بلوکها باید لحاظ شوند (اگر فهرست خالی باشد، همهٔ بلوکها لحاظ میشوند). دو پیکربندی اختیاری وجود دارد: format و separator. پیکربندی format یک رشتهٔ الگو را تعیین میکند که در آن @@{tag} (یا به اختصار @@) با نام هر برچسب جایگزین میشود. پیکربندی separator قطعهکدی را مشخص میکند که برای اتصال بخشهای تولیدشدهٔ format برای برچسبهای مختلف استفاده میشود.
- /*!getstate:re2c[:<name1>[:<name2>...]] ... */ or %{getstate[:<name1>[:<name2>...]] ... %}
- بلوکی که یک ارسال شرطی روی وضعیت تحلیلگر لغوی تولید میکند (به گزینهٔ --storable-state نیاز دارد). یک فهرست اختیاری از نامهای بلوک مشخص میکند که کدام بلوکها باید در ارسال وضعیت گنجانده شوند. گذار پیشفرض به برچسب شروع اولین بلوک موجود در فهرست میرود. اگر فهرست خالی باشد، همهٔ بلوکها گنجانده شده و گذار پیشفرض به نخستین بلوک دارای برچسب شروع در فایل منتقل میشود. این نوع بلوک با گزینهٔ --loop-switch ناسازگار است، چرا که به گذارهای بینبلوکی نیاز دارد که بدون goto یا فراخوانیهای توابع پشتیبانی نمیشوند.
- /*!conditions:re2c[:<name1>[:<name2>...]] ... */, /*!types:re2c... */ or %{conditions[:<name1>[:<name2>...]] ... %}, %{types... %}
- بلوکی که شمارش شرایط را تولید میکند (نیازمند گزینهٔ --conditions است). یک فهرست اختیاری از نامهای بلوک مشخص میکند که هنگام محاسبهٔ مجموعهٔ شرایط کدام بلوکها باید لحاظ شوند (اگر فهرست خالی باشد، همهٔ بلوکها لحاظ میشوند). به طور پیشفرض، کد تولیدشده یک شمارش YYCONDTYPE است. این مورد میتواند با پیکربندیهای اختیاری format و separator سفارشیسازی شود. پیکربندی format یک رشتهٔ الگو تعیین میکند که در آن @@{cond} (یا به اختصار @@) با نام هر شرط و @@{num} با نمایهٔ عددی آن شرط جایگزین میشود. پیکربندی separator قطعهکدی را تعیین میکند که برای اتصال بخشهای تولیدشدهٔ format برای شرایط مختلف به کار میرود.
- /*!include:re2c <file> */ or %{include <file> %}
- این بلوک امکان درج <file> را فراهم میکند، که باید یک مسیر فایل درون علامت نقلقول دوتایی باشد. محتویات فایل عیناً به جای بلوک جایگزین میشود، دقیقاً به همان شیوهای که #include در C/C++ عمل میکند. این بلوک میتواند همراه با گزینهٔ --depfile برای تولید وابستگیهای سامانهٔ ساخت به فایلهای درجشده به کار رود.
- /*!header:re2c:on*/ or %{header:on %}
- این بلوک نشاندهندهٔ آغاز فایل سرآیند است. هر چیزی پس از آن و تا بلوک بعدی header:off توسط re2js پردازش شده و در فایل سرآیند تعیینشده با گزینهٔ -t --type-header نوشته میشود.
- /*!header:re2c:off*/ or %{header:off %}
- این بلوک نشاندهندهٔ پایان فایل سرآیندی است که با بلوک header:on*/ آغاز شده بود.
- /*!ignore:re2c ... */ or %{ignore ... %}
- بلوکی که
محتویات آن
نادیده
گرفته شده و
از فایل
خروجی حذف
میشود.
در اینجا فهرست کاملی از پیکربندیهای پشتیبانیشده توسط re2js آورده شده است:
- re2c:api, re2c:input
- مشابه گزینهٔ --api.
- re2c:api:sigil
- نشانگر ("sigil") مورد استفاده برای جاینگهدارهای آرگومان در بنیادینههای API را مشخص میکند. مقدار پیشفرض @@ است. یک جاینگهدار با نشانگر آغاز شده و به دنبال آن نام آرگومان درون دو آکولاد میآید. برای مثال، اگر نشانگر روی $ تنظیم شود، جاینگهدارها به شکل ${name} خواهند بود. رابطهای تکآرگومانی ممکن است از نشانهگذاری کوتاه بدون نام درون آکولاد استفاده کنند. این گزینه میتواند با گزینههای مربوط به هر بنیادینهٔ API بهصورت مجزا بازنویسی شود، مانند re2c:YYFILL@len برای YYFILL.
- re2c:api:style
- سبک API را مشخص میکند. مقادیر ممکن عبارتند از functions (پیشفرض برای C) و free-form (پیشفرض برای Go و Rust). در سبک functions، بنیادینههای API به همراه فهرستی از آرگومانها درون پرانتز به دنبال نام بنیادینه تولید میشوند. آرگومانها تنها برای پارامترهای خودکار تولیدشده ارائه میشوند (مانند تعداد نویسههای ارسالی به YYFILL)، اما برای بافتار کلی لکسر ارائه نمیشوند؛ بنابراین این بنیادینهها رفتاری شبیهتر به ماکروها در C/C++ یا بستارها (closures) در Go و Rust دارند. در سبک free-form، بنیادینههای API شکل ثابتی ندارند: آنها باید بهصورت رشتههایی حاوی قطعهکدهای آزاد به همراه متغیرهای درونیابیشده به شکل @@{var} یا @@ تعریف شوند (اینها متناظر با آرگومانها در سبک تابعگونه هستند). این پیکربندی میتواند برای بنیادینههای مجزای API بازنویسی شود؛ برای نمونه، پیکربندی re2c:YYFILL:naked برای YYFILL را ببینید.
- re2c:bit-vectors, re2c:flags:bit-vectors, re2c:flags:b
- مشابه گزینهٔ --bit-vectors، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- re2c:captures, re2c:leftmost-captures
- مشابه گزینهٔ --leftmost-captures، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- re2c:captvars, re2c:leftmost-captvars
- مشابه گزینهٔ --leftmost-captvars، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- re2c:case-insensitive, re2c:flags:case-insensitive
- مشابه گزینهٔ --case-insensitive، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- re2c:case-inverted, re2c:flags:case-inverted
- مشابه گزینهٔ --case-inverted، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- re2c:case-ranges, re2c:flags:case-ranges
- مشابه گزینهٔ --case-ranges، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- re2c:computed-gotos, re2c:flags:computed-gotos, re2c:flags:g
- مشابه گزینهٔ --computed-gotos، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- re2c:computed-gotos:relative, re2c:cgoto:relative
- مشابه گزینهٔ --computed-gotos-relative، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- re2c:computed-gotos:threshold, re2c:cgoto:threshold
- اگر goto محاسباتی استفاده شود، این پیکربندی آستانهٔ پیچیدگیای را مشخص میکند که باعث تولید جداول پرش به جای دستورهای if تودرتو و نقشههای بیتی میشود. مقدار پیشفرض 9 است.
- re2c:cond: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 symbol) مورد استفاده همراه با قاعدهٔ پایان ورودی $ را مشخص میکند. مقدار پیشفرض -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
- نام برچسب اختیاری پس از گزینش YYGETSTATE در حالت وضعیت ذخیرهپذیر (فعالشده با re2c:state:nextlabel) را مشخص میکند. پیشفرض yyNext است.
- re2c:lookahead, re2c:flags:lookahead
- منسوخ شده است (گزینهٔ منسوخشدهٔ --no-lookahead را ببینید).
- re2c:monadic
- اگر مقداری غیر صفر تنظیم شود، لکسر تولیدشده از نشانهگذاری مونادیک استفاده خواهد کرد (این پیکربندی مختص Haskell است).
- 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، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- عبارت مورد استفاده برای متغیرهای برچسب را مشخص میکند. بهطور پیشفرض re2js عبارتهایی به شکل yyt<N> تولید میکند. این امر ممکن است ناخوشایند باشد، برای مثال اگر متغیرهای برچسب به عنوان فیلدهایی درون یک struct تعریف شده باشند. تمام موارد رخداد @@{tag} یا @@ با نام واقعی برچسب جایگزین میشوند. برای نمونه، re2c:tags:expression = "s.@@"; منجر به تولید عبارتهایی به شکل s.yyt<N> در کد خروجی خواهد شد. همچنین پیکربندی re2c:api:sigil را ببینید..TP re2c:tags:negative عبارت ثابتی را مشخص میکند که برای مقدار تگ منفی استفاده میشود (معمولاً اگر تگها آفستهای صحیح در رشتهٔ ورودی باشند، این مقدار -1 خواهد بود، یا اشارهگر تهی (null pointer) در صورتی که اشارهگر باشند).
- پیشوند نام متغیرهای تگ را مشخص میکند. مقدار پیشفرض yyt است.
- re2c:sentinel
- نماد نگهبان (sentinel) مورد استفاده برای بررسیهای پایان ورودی را مشخص میکند (زمانی که بررسی مرزها با re2c:yyfill:enable = 0; غیرفعال شده و re2c:eof تنظیم نشده باشد). این پیکربندی تأثیری بر تولید کد ندارد: هدف آن تأیید این است که نماد نگهبان در میانهٔ یک قاعده مجاز نباشد و اطمینان از اینکه تحلیلگر لغوی فراتر از پایان بافر را نخواهد خواند. مقدار پیشفرض -1` است (در این حالت re2js فرض میکند که نماد نگهبان صفر است که معمولترین حالت میباشد). تنها اعداد دهدهی شناسایی میشوند.
- re2c:state:abort
- اگر روی یک مقدار صحیح مثبت تنظیم شود، حالت پیشفرض (default case) در توزیع وضعیت (state dispatch) تولیدشده، اجرای برنامه را لغو (abort) میکند، و یک حالت صریح -1 شامل انتقال به ابتدای بلوک خواهد بود.
- re2c:state:nextlabel
- مشخص میکند که آیا پس از سوییچ YYGETSTATE یک برچسب yyNext بیاید یا خیر (مقدار پیشفرض صفر است که متناظر با نبود برچسب است). به عنوان روشی دیگر میتوان از re2c:label:start برای تولید یک برچسب شروع مشخص استفاده کرد، یا از یک بلوک صریح getstate برای تولید سوییچ YYGETSTATE به صورت جداگانه از بلوک تحلیلگر لغوی بهره برد.
- re2c:unsafe, re2c:flags:unsafe
- مشابه گزینهٔ --no-unsafe است، اما میتواند بر پایهٔ هر بلوک پیکربندی شود. اگر روی صفر تنظیم شود، از تولید بستهبندیهای (wrappers) 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 را باطل (override) میکند.
- re2c:YYFILL:naked, re2c:define:YYFILL:naked
- پیکربندی عمومیتر re2c:api:style را برای YYFILL باطل (override) میکند. مقدار صفر متناظر با شیوهٔ API با قالب آزاد (free-form) است.
- re2c:YYFN
- پریمیتیو API با نام YYFN را تعریف میکند.
- re2c:YYINPUT
- پریمیتیو API با نام YYINPUT را تعریف میکند.
- re2c:YYGETCOND, re2c:define:YYGETCONDITION
- پریمیتیو API با نام YYGETCOND را تعریف میکند.
- re2c:YYGETCOND:naked, re2c:define:YYGETCONDITION:naked
- پیکربندی عمومیتر re2c:api:style را برای YYGETCOND باطل (override) میکند. مقدار صفر متناظر با شیوهٔ API با قالب آزاد است.
- re2c:YYGETSTATE, re2c:define:YYGETSTATE
- پریمیتیو API با نام YYGETSTATE را تعریف میکند.
- re2c:YYGETSTATE:naked, re2c:define:YYGETSTATE:naked
- پیکربندی عمومیتر re2c:api:style را برای YYGETSTATE باطل (override) میکند. مقدار صفر متناظر با شیوهٔ 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 را باطل (override) میکند.
- re2c:YYSETCOND:naked, re2c:define:YYSETCONDITION:naked
- پیکربندی عمومیتر re2c:api:style را برای YYSETCOND باطل (override) میکند. مقدار صفر متناظر با شیوهٔ API با قالب آزاد است.
- re2c:YYSETSTATE, re2c:define:YYSETSTATE
- پریمیتیو API با نام YYSETSTATE را تعریف میکند.
- re2c:YYSETSTATE@state, re2c:define:YYSETSTATE@state
- نماد (sigil) مورد استفاده برای جایگزینی آرگومان در تعریف YYSETSTATE را مشخص میکند. مقدار پیشفرض @@ است. پیکربندی عمومیتر re2c:api:sigil را باطل (override) میکند.
- re2c:YYSETSTATE:naked, re2c:define:YYSETSTATE:naked
- پیکربندی عمومیتر re2c:api:style را برای YYSETSTATE باطل (override) میکند. مقدار صفر متناظر با شیوهٔ 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 را تعریف میکند.
- 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
- اگر روی غیرصفر تنظیم شود، re2js به طور خودکار هر بار که yych خوانده میشود، یک تبدیل به YYCTYPE تولید میکند. پیشفرض صفر است (بدون تبدیل).
- re2c:yych:literals, re2c:variable:yych:literals
- شکل لیترالهایی که yych با آنها مطابقت داده میشود را مشخص میکند. مقادیر ممکن عبارتند از: char (لیترالهای نویسهای در نقلقول تکی، نویسههای غیرقابلچاپ از توالیهای گریز که با بکاسلش آغاز میشوند استفاده میکنند)، hex (اعداد صحیح هگزادسیمال) و char_or_hex (ترکیبی از هر دو، لیترالهای نویسهای برای نویسههای قابلچاپ و اعداد صحیح هگزادسیمال برای بقیه).
- re2c:yyctable, re2c:variable:yyctable
- پریمیتیو API با نام yyctable را تعریف میکند..TP 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 را مشخص میکند (پیشفرض خالی است که به معنای عدم وجود استثناست).
عبارات باقاعده (Regular expressions)
ابزار re2js از نحو زیر برای عبارات باقاعده استفاده میکند:
- "foo"
- رشتهٔ لفظی حساس به بزرگ و کوچکی حروف.
- 'foo'
- رشتهٔ لفظی ناوابسته به بزرگ و کوچکی حروف.
- [a-xyz], [^a-xyz]
- کلاس نویسهای (احتمالاً نقیضشده).
- .
- هر نویسهای به جز خط جدید (newline).
- R \ S
- تفاضل کلاسهای نویسهای R و S.
- R*
- صفر یا چند بار تکرار R.
- R+
- یک یا چند بار تکرار R.
- R?
- R اختیاری.
- R{n}
- تکرار R دقیقاً به تعداد n بار.
- R{n,}
- تکرار R حداقل به تعداد n بار.
- R{n,m}
- تکرار R از n تا m بار.
- (R)
- تنها R؛ پرانتزها برای تغییر اولویت استفاده میشوند. اگر استخراج زیرتطبیق فعال باشد، (R) بسته به گزینهٔ --invert-captures یک گروه گیرنده یا غیرگیرنده خواهد بود.
- (!R)
- اگر استخراج زیرتطبیق فعال باشد، (!R) بسته به گزینهٔ --invert-captures یک گروه غیرگیرنده یا گیرنده خواهد بود.
- R S
- الحاق: R و به دنبال آن S.
- R | S
- انتخاب: R یا S.
- R / S
- پیشنگری (Lookahead): R و به دنبال آن S، اما S مصرف نمیشود.
- name
- عبارت باقاعدهٔ تعریفشده به عنوان name (یا رشتهٔ لفظی "name" در حالت سازگاری با Flex).
- {name}
- عبارت باقاعدهٔ تعریفشده به عنوان name در حالت سازگاری با Flex.
- @stag
- یک s-tag: آخرین موقعیت ورودی را که در آن @stag تطبیق مییابد، در متغیری به نام stag ذخیره میکند.
- #mtag
- یک m-tag: تمام موقعیتهای ورودی را که در آنها #mtag تطبیق مییابد، در متغیری به نام mtag ذخیره میکند.
- $
- پایان ورودی.
کلاسهای نویسهای و رشتههای متنی لفظی میتوانند شامل دنبالههای گریز زیر باشند: \a، \b، \f، \n، \r، \t، \v، \\، دنبالههای گریز هشتهشتی \ooo و دنبالههای گریز شانزدهشانزدهی \xhh، \uhhhh و \Uhhhhhhhh..SS "کنشها (Actions)"
در اینجا فهرستی از کنشهای از پیش تعریفشده که توسط re2js پشتیبانی میشوند آمده است:
- !entry code
- کنش ورود، یک بلوک تعریفشده توسط کاربر از code را به وضعیت آغازین ماشین حالت متناهی فعلی مقید میکند. اگر از شرایط آغازین استفاده شود، کنش ورود را میتوان به صورت جداگانه برای هر شرط تنظیم کرد. از این کنش میتوان برای انجام مقداردهی اولیه، مانند ذخیره مکان آغاز نشانه (lexeme)، استفاده کرد.
- !pre_rule code
- کنش پیشقاعده، یک بلوک تعریفشده توسط کاربر از code را به ابتدای کنشهای معنایی تمام قواعد در بلوک فعلی (یا شرط، در صورت استفاده از شرایط آغازین) میافزاید. از این کنش میتوان برای فاکتورگیری بخش مشترک تمام کنشهای معنایی (مانند ذخیره مکان پایان نشانه) استفاده کرد.
- !post_rule code
- کنش پسقاعده، یک بلوک تعریفشده توسط کاربر از code را به انتهای کنشهای معنایی تمام قواعد در بلوک فعلی (یا شرط، در صورت استفاده از شرایط آغازین) پیوست میکند. از این کنش میتوان برای صدور دستورهای تله (trap statements) که در برابر جریان کنترل ناخواسته محافظت میکنند استفاده کرد.
دستورالعملها (Directives)
در اینجا فهرست کاملی از دستورالعملهای پشتیبانیشده توسط re2js آمده است:
- !use:name ;
- یک دستورالعمل درونبلوکی use که بلوک قواعد از پیش تعریفشده با name مشخص را در بلوک فعلی ادغام میکند. تعاریف نامگذاریشده، پیکربندیها و قواعد بلوک ارجاعشده به موارد فعلی اضافه میشوند. تداخلهای بین قواعد و پیکربندیهای همپوشان به روش معمول حلوفصل میشوند: قاعده نخست اولویت دارد، و جدیدترین پیکربندی موارد پیشین را بازنویسی میکند. یک استثنا قواعد ویژه *، $ و <!> هستند که برای آنها تعریف محلی بلوک همواره اولویت دارد. دستورالعمل use میتواند در هر کجای درون یک بلوک قرار گیرد، و استفاده از چندین دستورالعمل use مجاز است.
- !include file ;
- این دستورالعمل همانند بلوک include است: محتویات file را عیناً به جای دستورالعمل درج میکند.
رابط برنامه (Program interface)
کد تولیدشده با کمک بنیادینها که مجموعاً به عنوان API شناخته میشوند، با برنامه بیرونی ارتباط برقرار میکند. اینکه کدام بنیادینها باید برای یک برنامه خاص تعریف شوند به عوامل متعددی بستگی دارد، از جمله پیچیدگی عبارات باقاعده، نحوه نمایش ورودی، بافرینگ و استفاده از ویژگیهای گوناگون. تمام بنیادینهای ضروری باید توسط کاربر در قالب ماکروها، توابع، متغیرها یا هر شکل مناسب دیگری که کد تولیدشده را از نظر نحوی و معنایی درست میسازد تعریف شوند. re2js تعاریف را بررسی نمیکند (و نمیتواند بکند)، بنابراین اگر چیزی ناقص باشد یا نادرست تعریف شده باشد، برنامه تولیدشده ممکن است خطاهای زمان کامپایل یا زمان اجرا داشته باشد. این راهنما مثالهایی از تعاریف API را در رایجترین موارد ارائه میدهد.
ابزار re2js دارای سه شیوه برای API است که مجموعه اصلی بنیادینهای مورد استفاده توسط یک برنامه را مشخص میکنند:
- Simple API
- این API پیشفرض برای بخش پسین (backend) جاوااسکریپت است. این رابط از بنیادینهای زیر تشکیل شده است: 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 در صورتی استفاده میشود که تطبیق طولانیتر شکست بخورد و تحلیلگر واژگانی نیاز به بازگشت به عقب (rollback) داشته باشد. مقداردهی اولیه نیاز نیست.
- YYCTXMARKER
- یک l-value که موقعیت بافت پسین (trailing context) را ذخیره میکند (یک اشارهگر یا آفست عدد صحیح در YYINPUT). هیچ مقداردهی اولیهای لازم نیست. YYCTXMARKER تنها در صورتی نیاز است که عملگر پیشنگری / استفاده شده باشد.
- YYFILL
- یک بنیادین Generic API با یک متغیر len. YYFILL باید حداقل len نویسه ورودی بیشتر فراهم کند یا با شکست مواجه شود. اگر از re2c:eof استفاده شود، آنگاه len همواره 1 است و YYFILL باید همیشه به تابع فراخواننده بازگردد؛ مقدار بازگشتی صفر نشاندهنده موفقیت است. اگر از re2c:eof استفاده نشود، مقدار بازگشتی YYFILL نادیده گرفته میشود و در صورت بروز شکست نباید بازگردد. بیشینه مقدار len برابر با YYMAXFILL است.
- YYFN
- بنیادینی که پیشنمونه تابع را در مدل کد --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 از نوع بولی تعریف شود که اگر و فقط اگر به پایان منطقی ورودی رسیده باشیم (به استثنای هرگونه نویسه پرکننده یا نمادهای نگهبان [sentinel])، برابر با true شود. YYEND برای پیادهسازی نماد $ در عبارات باقاعده استفاده میشود. این بنیادین با YYLESSTHAN متفاوت است که برای اطمینان از اینکه تحلیلگر واژگانی فراتر از انتهای بافر نخواند استفاده میشود.
- YYPEEK
- یک بنیادین Generic API بدون متغیر. باید به عنوان یک r-value از نوع YYCTYPE تعریف شود که برابر با نویسه در موقعیت فعلی ورودی است.
- YYSKIP
- یک بنیادین Generic API که باید موقعیت فعلی ورودی را به اندازه یک واحد کد به جلو ببرد.
- YYBACKUP
- یک بنیادین Generic API که باید موقعیت فعلی ورودی را ذخیره کند (تا بعداً با YYRESTORE بازیابی شود).
- YYRESTORE
- یک بنیادین Generic API که باید موقعیت فعلی ورودی را به مقدار ذخیرهشده توسط YYBACKUP بازیابی کند.
- YYBACKUPCTX
- یک بنیادین Generic API که باید موقعیت فعلی ورودی را به عنوان موقعیت بافت پسین ذخیره کند (تا بعداً با YYRESTORECTX بازیابی شود)..TP YYRESTORECTX یک دستور پایه عمومی API که باید موقعیت زمینه پایانی (trailing context) ذخیرهشده با YYBACKUPCTX را بازیابی کند.
- YYRESTORETAG
- یک دستور پایه عمومی API با یک متغیر tag که باید موقعیت زمینه پایانی را به مقدار tag بازگرداند.
- YYSTAGP
- یک دستور پایه عمومی API با یک متغیر tag، که در آن tag میتواند یک اشارهگر یا آفست در YYINPUT باشد (برای جزئیات به بخش استخراج تطابقهای فرعی مراجعه کنید). YYSTAGP باید tag را روی موقعیت ورودی فعلی تنظیم کند.
- YYSTAGN
- یک دستور پایه عمومی API با یک متغیر tag، که در آن tag میتواند یک اشارهگر یا آفست در YYINPUT باشد (برای جزئیات به بخش استخراج تطابقهای فرعی مراجعه کنید). YYSTAGN باید tag را روی مقداری تنظیم کند که بیانگر یک موقعیت ورودی ناموجود باشد.
- YYMTAGP
- یک دستور پایه عمومی API با یک متغیر tag. YYMTAGP باید موقعیت فعلی را به تاریخچه تطابقهای فرعی tag اضافه کند (برای جزئیات به بخش استخراج تطابقهای فرعی مراجعه کنید).
- YYMTAGN
- یک دستور پایه عمومی API با یک متغیر tag. YYMTAGN باید مقداری را که بیانگر موقعیت ورودی ناموجود است به تاریخچه تطابقهای فرعی tag اضافه کند (برای جزئیات به بخش استخراج تطابقهای فرعی مراجعه کنید).
- YYSHIFT
- یک دستور پایه عمومی API با یک متغیر shift که باید موقعیت ورودی فعلی را به اندازه shift نویسه تغییر دهد (مقدار جابهجایی ممکن است منفی باشد).
- YYCOPYSTAG
- یک دستور پایه عمومی API با دو متغیر lhs و rhs که باید متغیر s-tag سمت راست rhs را در متغیر s-tag سمت چپ lhs کپی کند. برای بیشتر زبانها این دستور پایه تعریفی پیشفرض دارد که lhs را برابر با rhs قرار میدهد.
- YYCOPYMTAG
- یک دستور پایه عمومی API با دو متغیر lhs و rhs که باید متغیر m-tag سمت راست rhs را در متغیر m-tag سمت چپ lhs کپی کند. برای بیشتر زبانها این دستور پایه تعریفی پیشفرض دارد که lhs را برابر با rhs قرار میدهد.
- YYSHIFTSTAG
- یک دستور پایه عمومی API با دو متغیر tag و shift که باید tag را به اندازه shift واحد کد (code unit) جابهجا کند (مقدار جابهجایی ممکن است منفی باشد).
- YYSHIFTMTAG
- یک دستور پایه عمومی API با دو متغیر tag و shift که باید آخرین مقدار در تاریخچه tag را به اندازه shift واحد کد جابهجا کند (مقدار جابهجایی ممکن است منفی باشد).
- YYMAXNMATCH
- یک ثابت عدد صحیح برابر با حداکثر تعداد گروههای گیرانداز POSIX در یک قاعده. این مقدار با یک بلوک maxnmatch تولید میشود.
- YYCONDTYPE
- نوع شمارشی (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 باید در موارد سادهای که نیازی به پر کردن مجدد بافر و ذخیرهٔ وضعیت تحلیلگر واژگانی (lexer) نیست به کار رود. گزینهٔ record باید زمانی استفاده شود که وضعیت تحلیلگر واژگانی باید در یک رکورد (struct، class و غیره) ذخیره شود. گزینهٔ generic باید در موارد پیچیدهای استفاده شود که دو API دیگر به قدر کافی انعطافپذیر نیستند.
- --bit-vectors -b
- بهینهسازی پرشهای شرطی با استفاده از ماسکهای بیتی. این گزینه مستلزم --nested-ifs است.
- --captures, --leftmost-captures
- فعالسازی استخراج زیرتطابق با گروههای گیرندهٔ حریص چپترین (leftmost greedy). نتیجه در یک آرایهٔ yybmatch با گنجایش 2 * YYMAXNMATCH جمعآوری شده و yynmatch به تعداد گروهها برای قاعدهٔ تطابقیافته تنظیم میشود.
- --captvars, --leftmost-captvars
- فعالسازی استخراج زیرتطابق با گروههای گیرندهٔ حریص چپترین (leftmost greedy). نتیجه در متغیرهای yytl<k> و yytr<k> برای k-امین گروه گیرنده جمعآوری میشود.
- --case-insensitive
- رفتار با رشتههای محصور در نقلقول تکی و جفتی بهصورت غیرحساس به بزرگی و کوچکی حروف (case-insensitive).
- --case-inverted
- معکوس کردن مفهوم رشتههای درون نقلقول تکی و جفتی: رفتار با رشتههای درون نقلقول تکی بهصورت حساس به بزرگی و کوچکی حروف و رشتههای درون نقلقول جفتی بهصورت غیرحساس به حروف.
- --case-ranges
- ادغام caseهای متوالی در دستور switch به یک بازه به فرم low ... high. این نحو یک افزونهٔ زبان C/C++ است که توسط کامپایلرهایی چون GCC، Clang و Tcc پشتیبانی میشود. مزیت اصلی نسبت به استفاده از caseهای منفرد، کد تولیدشدهٔ کوچکتر و زمان تولید سریعتر است، اگرچه برای برخی کامپایلرها نظیر Tcc به حجم باینری کوچکتر نیز میانجامد.
- --computed-gotos -g
- بهینهسازی پرشهای شرطی با استفاده از افزونهٔ غیراستاندارد "computed goto" (که باید توسط کامپایلر پشتیبانی شود). برنامه re2js جدولهای پرش را تنها در موارد پیچیده با انشعابهای شرطی فراوان تولید میکند. آستانهٔ پیچیدگی را میتوان با پیکربندی cgoto:threshold تنظیم کرد. آفستهای نسبی را میتوان با پیکربندی cgoto:relative فعال کرد. این گزینه مستلزم --bit-vectors است.
- --computed-gotos-relative
- مشابه --computed-gotos اما در عوض، آفستهای نسبی را برای جدولهای پرش تولید میکند (که باید توسط کامپایلر پشتیبانی شود). این گزینه مستلزم --computed-gotos است.
- --conditions --start-conditions -c
- فعال کردن پشتیبانی از "شرطها" (conditions) مشابه Flex: چند تحلیلگر واژگانی مرتبط درون یک بلوک واحد. این یک راهکار جایگزین برای تعیین دستی بلوکهای مختلف re2js است که با goto یا فراخوانیهای تابع به هم متصل شدهاند.
- --depfile FILE
- نوشتن اطلاعات وابستگی در FILE در قالب یک قاعدهٔ Makefile به شکل <output-file> : <input-file> [include-file ...]. این قابلیت امکان ردیابی وابستگیهای ساخت را در صورت وجود بلوکها/دستورالعملهای include فراهم میکند، بهطوری که بهروزرسانی فایلهای include باعث بازتولید فایل خروجی میشود. این گزینه به گزینهٔ --output وابسته است.
- --ebcdic --ecb -e
- تولید تحلیلگر واژگانی که ورودی را با کدگذاری EBCDIC میخواند. re2js فرض میکند بازهٔ نویسهها 0 -- 0xFF و اندازهٔ نویسه ۱ بایت است.
- --empty-class <match-empty | match-none | error>
- تعریف شیوهٔ برخورد re2js با ردههای نویسهای خالی. با match-empty (پیشفرض) ردهٔ خالی با ورودی خالی تطابق مییابد (که غیرمنطقی است، اما برای سازگاری با گذشته حفظ شده است). با match-none ردهٔ خالی همیشه در تطابق شکست میخورد. با error ردهٔ خالی خطای کامپایل ایجاد میکند.
- --encoding-policy <fail | substitute | ignore>
- تعریف شیوهٔ برخورد re2js با جانشینهای یونیکد (Unicode surrogates). با fail برنامه re2js هنگام مواجهه با یک جانشین با خطا متوقف میشود. با substitute برنامه re2js بدون هشدار، جانشینها را با نقطه کد خطای 0xFFFD جایگزین میکند. با ignore (پیشفرض) re2js با جانشینها مانند نقاط کد عادی رفتار میکند. استاندارد یونیکد بیان میکند که جانشینهای مستقل نامعتبر هستند، اما برنامهها و کتابخانههای دنیای واقعی رفتارهای متفاوتی دارند.
- --flex-syntax -F
- پشتیبانی جزئی از نحو Flex: در این حالت تعاریف نامگذاریشده نیازی به علامت مساوی و سمیکالن پایانی ندارند، و هنگام استفاده باید درون آکولاد محصور شوند. نامهای بدون آکولاد به عنوان رشتههای محصور در نقلقول جفتی در نظر گرفته میشوند.
- --goto-label
- استفاده از مدل کد "goto/label": کدگذاری DFA در قالب بلوکهای کد برچسبدار متصلشده با انتقالهای goto میان بلوکها. این گزینه تنها برای زبانهایی که دستور goto دارند پشتیبانی میشود.
- --header --type-header -t HEADER
- تولید یک فایل HEADER. محتویات فایل را میتوان با استفاده از بلوکهای ویژهٔ header:on و header:off مشخص کرد. اگر از شرطها استفاده شود، یک enum شرط بهطور خودکار به انتهای هدر تولیدشده افزوده میشود (مگر اینکه یک بلوک صریح conditions وجود داشته باشد).
- -I PATH
- افزودن PATH به فهرست مکانهایی که هنگام جستجوی فایلهای include استفاده میشوند. این گزینه در ترکیب با بلوک یا دستورالعمل include کاربرد دارد. برنامه re2js به دنبال FILE در دایرکتوری فایل والد و در مسیرهای include مشخصشده با گزینهٔ -I میگردد.
- --input <default | custom>
- نام مستعار منسوخشده برای --api. گزینهٔ default متناظر با simple است (در واقع برای اکثر بکاندها پیشفرض است، اما نه همه). گزینهٔ custom متناظر با generic است.
- --input-encoding <ascii | utf8>
- تعیین شیوهٔ تجزیهٔ عبارتهای باقاعده توسط re2js. با ascii (پیشفرض) re2js با ورودی به صورت کدگذاریشده با ASCII رفتار میکند: هر دنبالهای از واحدهای کد، دنبالهای از نویسههای تکبایتی مستقل است. با utf8 برنامه re2js ورودی را با کدگذاری UTF8 مدیریت کرده و نویسههای چندبایتی را تشخیص میدهد.
- --invert-captures
- معکوس کردن مفهوم گروههای گیرنده و غیرگیرنده. بهطور پیشفرض (...) گیرنده و (! ...) غیرگیرنده است. با این گزینه (! ...) گیرنده و (...) غیرگیرنده خواهد بود.
- --lang <none | c | d | go | haskell | java | js | ocaml | python | rust | swift | v | zig>
- تعیین زبان مقصد. زبانهای پشتیبانیشده عبارتند از C، D، Go، Haskell، Java، JS، OCaml، Python، Rust، Swift، V، Zig (زبانهای بیشتری را میتوان از طریق فایلهای نحو تعریفشده توسط کاربر افزود، گزینهٔ --syntax را ببینید). گزینهٔ none پیکربندیهای نحو پیشفرض را غیرفعال میکند، بهطوری که زبان مقصد تعریفنشده خواهد ماند.
- --location-format <gnu | msvc>
- تعیین قالب مکان در پیامها. با gnu مکانها بهصورت 'filename:line:column: ...' چاپ میشوند. با msvc مکانها بهصورت 'filename(line,column) ...' چاپ میشوند. پیشفرض gnu است.
- --loop-switch
- استفاده از مدل کد "loop/switch": کدگذاری DFA در قالب یک حلقه بر روی یک دستور switch، که در آن هر وضعیت مجزا یک case از switch است. وضعیت در متغیر yystate ذخیره میشود. انتقالها میان وضعیتها، yystate را به برچسب case وضعیت مقصد بهروزرسانی کرده و اجرای کد را به ابتدای حلقه ادامه میدهند.
- --nested-ifs -s
- استفاده از دستورهای تو در توی if بهجای دستورهای switch در پرشهای شرطی. این کار معمولاً در کامپایلرهای غیربهینهساز منجر به کد کارآمدتری میشود.
- --no-debug-info -i
- عدم درج دستورالعملهای خط در خروجی. این قابلیت زمانی مفید است که کد تولیدشده در یک سامانهٔ کنترل نسخه ذخیره میشود (جهت جلوگیری از diffهای عظیم ناشی از تغییرات کوچک).
- --no-generation-date
- جلوگیری از چاپ تاریخ در فایل تولیدشده.
- --no-version
- جلوگیری از درج نسخه در فایل تولیدشده.
- --no-unsafe
- عدم تولید پوشش unsafe روی YYPEEK (این گزینه مختص زبان Rust است). به دلایل کارایی، YYPEEK باید از بررسی مرزها (bounds-checking) خودداری کند، چرا که تحلیلگر واژگانی از قبل بررسیهای پایان ورودی را به روشی کارآمدتر انجام میدهد. کاربر میتواند یک تعریف امن برای YYPEEK ارائه دهد، یا تعریفی که تنها در ساختهای انتشار (release) ناامن باشد، که در این حالت گزینهٔ --no-unsafe به جلوگیری از هشدارهای مربوط به بلوکهای زاید unsafe کمک میکند.
- --output -o OUTPUT
- تعیین فایل OUTPUT.
- --posix-captures, -P
- فعالسازی استخراج زیرتطابق با گروههای گیرنده به سبک POSIX. نتیجه در یک آرایهٔ yybmatch با گنجایش 2 * YYMAXNMATCH جمعآوری شده، و yynmatch به تعداد گروهها برای قاعدهٔ تطابقیافته تنظیم میشود.
- --posix-captvars
- فعالسازی استخراج زیرتطابق با گروههای گیرنده به سبک POSIX. نتیجه در متغیرهای yytl<k> و yytr<k> برای k-امین گروه گیرنده جمعآوری میشود.
- --recursive-functions
- استفاده از مدل کد بر پایهٔ توابع بازگشتی متقابل (co-recursive)، که در آن هر وضعیت DFA یک تابع مستقل است که میتواند سایر توابع وضعیت یا خودش را فراخوانی کند.
- --reusable -r
- از نسخهٔ 2.2 منسوخ شده است (بلوکهای قابل استفادهٔ مجدد اکنون بهطور پیشفرض مجاز هستند).
- --skeleton -S
- نادیده گرفتن کد رابط کاربری تعریفشده توسط کاربر و تولید یک برنامهٔ اسکلتی ("skeleton") مستقل. علاوه بر این، تولید فایلهای ورودی با رشتههای برگرفته از گرامر منظم و نتایج تطابق فشردهشده که برای بررسی رفتار "skeleton" روی تمام ورودیها به کار میروند. این گزینه برای یافتن اشکالات در بهینهسازیها و تولید کد مفید است. این گزینه تنها برای C پشتیبانی میشود..TP --storable-state -f تولید یک تحلیلگر لغوی که میتواند وضعیت درونی خود را ذخیره کند. این ویژگی در تحلیلگرهای لغوی مدل push که در صورت نبود ورودی کافی توسط یک برنامهٔ بیرونی متوقف شده و سپس با فراهم شدن ورودی بیشتر از سر گرفته میشوند، مفید است. در این حالت کاربران باید علاوه بر این موارد اولیهٔ YYGETSTATE و YYSETSTATE را تعریف کنند، و متغیرهای yych، yyaccept و state باید بخشی از وضعیت ذخیرهشدهٔ تحلیلگر لغوی باشند.
- --syntax FILE
- بارگذاری پیکربندیها از FILE مشخصشده و اعمال آنها روی فایل نحو پیشفرض. توجه داشته باشید که FILE میتواند تنها چند پیکربندی را تعریف کند (اگر برای اصلاح فایل نحو پیشفرض استفاده شود)، یا میتواند یک backend زبان کاملاً جدید را تعریف کند (در حالت دوم توصیه میشود از گزینهٔ --lang none استفاده شود).
- فعالسازی استخراج تطابقهای فرعی (submatch) با تگها.
- --ucs2 --wide-chars -w
- تولید یک تحلیلگر لغوی که ورودی با کدگذاری UCS2 را میخواند. re2js فرض میکند که بازهٔ نویسهها 0 -- 0xFFFF و اندازهٔ نویسه ۲ بایت است. این گزینه مستلزم --nested-ifs است.
- --utf8 --utf-8 -8
- تولید یک تحلیلگر لغوی که ورودی را با کدگذاری UTF-8 میخواند. re2js فرض میکند که بازهٔ نویسهها 0 -- 0x10FFFF و اندازهٔ نویسه ۱ بایت است.
- --utf16 --utf-16 -x
- تولید یک تحلیلگر لغوی که ورودی با کدگذاری UTF16 را میخواند. re2js فرض میکند که بازهٔ نویسهها 0 -- 0x10FFFF و اندازهٔ نویسه ۲ بایت است. این گزینه مستلزم --nested-ifs است.
- --utf32 --unicode -u
- تولید یک تحلیلگر لغوی که ورودی با کدگذاری UTF32 را میخواند. re2js فرض میکند که بازهٔ نویسهها 0 -- 0x10FFFF و اندازهٔ نویسه ۴ بایت است. این گزینه مستلزم --nested-ifs است.
- --verbose
- نمایش یک پیام کوتاه در صورت موفقیت.
- --vernum -V
- نمایش اطلاعات نسخه در قالب MMmmpp (اصلی، فرعی، وصله).
- --version -v
- نمایش اطلاعات نسخه.
- --single-pass -1
- منسوخشده. هیچ کاری انجام نمیدهد (اکنون تکگذره یا single pass حالت پیشفرض است).
- --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
- گزینهٔ اشکالزدایی: خروجی دادن جدول تداخل حاصل از تحلیل زندهبودن (liveness analysis) متغیرهای تگ.
- --dump-nfa
- گزینهٔ اشکالزدایی: خروجی دادن NFA (به قالب dot.).
- --emit-dot -D
- تولید گراف تحلیلگر لغوی به قالب dot. بهجای خروجی عادی. خروجی را میتوان با کمک Graphviz به یک تصویر تبدیل کرد (مثلاً دستوری مانند dot -Tpng -odfa.png dfa.dot).
- --dfa-minimization <moore | table>
- گزینهٔ داخلی: الگوریتم کمینهسازی DFA مورد استفاده توسط re2js. گزینهٔ moore همان الگوریتم مور است (که پیشفرض میباشد). گزینهٔ table الگوریتم "پر کردن جدول" است. هر دو الگوریتم باید صرفنظر از برچسبگذاری مجدد وضعیتها، DFA یکسانی تولید کنند؛ روش پر کردن جدول سادهتر و بسیار کندتر است و به عنوان پیادهسازی مرجع بهکار میرود.
- --eager-skip
- گزینهٔ داخلی: وادار کردن تحلیلگر لغوی تولیدشده به پیشروی مشتاقانه در موقعیت ورودی -- بلافاصله پس از خواندن نماد ورودی. این گزینه رفتار پیشفرض را که در آن موقعیت ورودی با تأخیر -- پس از گذار به وضعیت بعدی پیش میرود، تغییر میدهد.
- --no-lookahead
- گزینهٔ داخلی، منسوخشده. این گزینه در گذشته الگوریتم TDFA(0) را فعال میکرد. برخلاف TDFA(1)، الگوریتم TDFA(0) از پیشنگری یکنمادی استفاده نمیکند. این الگوریتم عملیاتهای ثبات را به جای گذارهای خروجی، روی گذارهای ورودی اعمال میکند. ارزیابیها نشان دادند که الگوریتم TDFA(0) کارایی کمتری نسبت به TDFA(1) دارد.
- --no-optimize-tags
- گزینهٔ داخلی: غیرفعال کردن بهینهسازی متغیرهای تگ (مفید برای اشکالزدایی).
- --posix-closure <gor1 | gtop>
- گزینهٔ داخلی: مشخصکردن الگوریتم کوتاهترین مسیر مورد استفاده برای ساخت بستار اپسیلون با معناشناسی رفع ابهام POSIX: گزینهٔ gor1 (پیشفرض) بیانگر الگوریتم Goldberg-Radzik است، و gtop بیانگر الگوریتم "ترتیب توپولوژیکی سراسری" است.
- --posix-prectable <complex | naive>
- گزینهٔ داخلی: مشخصکردن الگوریتم مورد استفاده برای محاسبهٔ جدول تقدم POSIX. الگوریتم complex جدول تقدم را در یک پیمایش از درخت تاریخچهٔ تگ محاسبه میکند و دارای پیچیدگی درجه دو بر حسب تعداد وضعیتهای TNFA است؛ این حالت پیشفرض است. الگوریتم naive در بدترین حالت دارای پیچیدگی درجه سه بر حسب تعداد وضعیتهای TNFA است، اما بسیار سادهتر از complex بوده و ممکن است در موارد غیرپاتولوژیک کمی سریعتر باشد.
- --stadfa
- گزینهٔ داخلی، منسوخشده. این گزینه قبلاً الگوریتم staDFA را فعال میکرد، که تفاوت آن با TDFA در قرار گرفتن عملیاتهای ثبات در وضعیتها به جای گذارها است. ارزیابیها نشان دادند که الگوریتم staDFA کارایی کمتری نسبت به TDFA دارد.
- --fixed-tags <none | toplevel | all>
- گزینهٔ
داخلی: مشخص
میکند که
آیا
بهینهسازی
تگ ثابت
باید برای
همهٔ تگها
اعمال شود
(all)، برای
هیچکدام
اعمال نشود
(none)، یا فقط
برای
تگهای
موجود در
الحاق
بالاترین
سطح اعمال
گردد (toplevel).
پیشفرض all
است. تگهای
"ثابت"
آنهایی
هستند که در
یک فاصلهٔ
مشخص از تگی
دیگر (موسوم
به "پایه")
قرار دارند.
در چنین
مواردی فقط
تگ پایه
باید
ردگیری
شود، و
مقدار تگ
ثابت
میتواند
بهعنوان
مقدار تگ
پایه
بهعلاوهٔ
یک آفست
ایستا
محاسبه شود.
برای
تگهایی که
تحت انتخاب
یا تکرار
هستند،
بررسی این
که آیا تگ
پایه مقدار
عدمتطابق
دارد یا خیر
نیز الزامی
است (در این
حالت تگ
ثابت نیز
باید با
نادیده
گرفتن
آفست، روی
عدمتطابق
تنظیم شود).
برای
تگهای
موجود در
الحاق
بالاترین
سطح نیازی
به این
بررسی
نیست، زیرا
آنها
همیشه
مطابقت
دارند.
هشدارها را میتوان بهصورت جداگانه فعال یا غیرفعال کرد و یا به خطا تبدیل نمود.
- -W
- فعالسازی همهٔ هشدارها.
- -Werror
- تبدیل هشدارها به خطا. توجه داشته باشید که این گزینه بهتنهایی هیچ هشداری را فعال نمیکند؛ بلکه تنها بر هشدارهایی تأثیر میگذارد که تاکنون فعال شدهاند یا بعداً فعال خواهند شد.
- -W<warning>
- فعالسازی warning.
- -Wno-<warning>
- غیرفعالسازی warning.
- -Werror-<warning>
- فعالسازی warning و رفتار با آن بهعنوان خطا (این گزینه مستلزم -W<warning> است).
- -Wno-error-<warning>
- عدم برخورد با این warning خاص بهعنوان خطا. این گزینه خودِ هشدار را غیرفعال نمیکند.
- -Wcondition-order
- اگر برنامهٔ تولیدشده فرضیات ضمنی دربارهٔ شمارهگذاری شرطها داشته باشد، هشدار میدهد. باید از گزینهٔ --header یا بلوک conditions برای تولید نگاشتی از نام شرطها به شمارهها استفاده کرده و سپس از نامهای شرط تولیدشدهٔ خودکار استفاده نمود.
- -Wempty-character-class
- اگر یک عبارت باقاعده حاوی یک کلاس نویسهٔ خالی باشد، هشدار میدهد. تلاش برای تطبیق یک کلاس نویسهٔ خالی بیمعنی است: همیشه باید ناموفق باشد. با این حال، به دلایل سازگاری با گذشته، re2js کلاسهای نویسهٔ خالی را مجاز دانسته و با آنها مانند رشتههای خالی رفتار میکند. از گزینهٔ --empty-class برای تغییر رفتار پیشفرض استفاده کنید.
- -Wmatch-empty-string
- اگر قاعدهای تهیپذیر باشد (با یک رشتهٔ خالی تطبیق یابد)، هشدار میدهد. اگر تحلیلگر لغوی در یک حلقه اجرا شود و تطبیق تهی ناخواسته باشد، تحلیلگر لغوی ممکن است بهطور غیرمنتظره در یک حلقهٔ بینهایت متوقف شود.
- -Wswapped-range
- اگر کران پایین یک بازه بزرگتر از کران بالای آن باشد، هشدار میدهد. رفتار پیشفرض جابهجا کردن بیسروصدای کرانهای بازه است.
- -Wundefined-control-flow
- اگر برخی رشتههای ورودی باعث جریان کنترل تعریفنشده در تحلیلگر لغوی شوند، هشدار میدهد (الگوهای معیوب گزارش میشوند). این یک خطای خطرناک و رایج است. میتوان آن را بهراحتی با افزودن قاعدهٔ پیشفرض * که کمترین اولویت را دارد، با هر واحد کد تطبیق مییابد و همیشه یک واحد کد را مصرف میکند، اصلاح کرد.
- -Wunreachable-rules
- دربارهٔ قواعدی که توسط سایر قواعد پوشانده شدهاند و هرگز تطبیق نخواهند یافت، هشدار میدهد.
- -Wdeprecated-eof_rule
- دربارهٔ قواعد مستقل پایان ورودی $ که با تغییرات آینده از کار خواهند افتاد و نیاز به اصلاح دارند، هشدار میدهد. در حال حاضر این قواعد هنگام تداخل با سایر قواعد اولویت دارند، اما پس از معرفی نماد تعمیمیافتهٔ پایان ورودی $، ترتیب اولویت تغییر خواهد کرد و این قواعد توسط سایر قواعد پوشانده خواهند شد.
- -Wuseless-escape
- اگر نمادی گریز داده شده باشد (escaped) در حالی که نباید میبود، هشدار میدهد. بهطور پیشفرض، re2js چنین گریزهایی را نادیده میگیرد، اما این ممکن است نشاندهندهٔ یک اشتباه تایپی یا خطا در توالی گریز باشد.
- -Wnondeterministic-tags
- اگر یک برچسب دارای درجهٔ n-ام از عدمقطعیت باشد، در جایی که n بزرگتر از ۱ است، هشدار میدهد.
- -Wsentinel-in-midrule
- اگر نماد نگهبان در میانهٔ یک قاعده رخ دهد هشدار میدهد --- این امر ممکن است باعث خواندن فراتر از انتهای بافر، فروپاشی یا خرابی حافظه در تحلیلگر لغوی تولیدشده شود. این هشدار تنها در صورتی کاربرد دارد که از روش نگهبان برای بررسی پایان ورودی استفاده شود. اگر از پیکربندی re2c:sentinel استفاده شود، این هشدار به خطا تبدیل میشود.
- -Wundefined-syntax-config
- اگر فایل نحو مشخصشده با گزینهٔ --syntax فاقد تعاریف برخی از پیکربندیها باشد، هشدار میدهد. این امر به نگهداری فایلهای نحو تعریفشده توسط کاربر کمک میکند: اگر انتشار جدیدی پیکربندیهایی را اضافه کند، فایل نحو قدیمی هشداری صادر کرده و کاربر مطلع خواهد شد. اگر برخی از پیکربندیها استفاده نمیشوند و نیازی به تعریف ندارند، باید صراحتاً روی <undefined> تنظیم شوند.
فایلهای نحو (Syntax files)
پشتیبانی از زبانهای مختلف در re2c بر پایهٔ ایدهٔ فایلهای نحو استوار است. یک فایل نحو، فایلی پیکربندی است که نحو زبان مقصد را تعریف میکند -- نه کل زبان، بلکه بخش کوچکی از آن را که توسط کد تولیدشده استفاده میشود. فایلهای نحو re2c را بسیار انعطافپذیر میکنند، اما نباید بهعنوان جایگزینی برای پیکربندیهای re2c: استفاده شوند: هدف آنها تعریف نحو زبان مقصد است، نه سفارشیسازی یک تحلیلگر لغوی خاص. تمام زبانهای پشتیبانیشده دارای فایلهای نحو پیشفرضی هستند که بخشی از توزیع به شمار میروند (زیرشاخهٔ include/syntax را ببینید)؛ آنها همچنین در فایل باینری re2js تعبیه شدهاند. کاربران میتوانند یک فایل نحو سفارشی ارائه دهند که چند پیکربندی را برای یکی از زبانهای پشتیبانیشده بازنویسی میکند، یا میتوانند همهٔ پیکربندیها را مجدداً تعریف نمایند (در آن صورت باید از گزینهٔ --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
- غیرصفر در صورتی که زبان پس از دستورها از نقطهویرگول (semicolon) استفاده کند.
- backtick_quoted_strings
- غیرصفر در صورتی که زبان دارای رشتههای درون بکتیک (backtick) باشد.
- 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> است. متغیرها جاینگهدارهایی هستند که در طول مرحلهٔ تولید کد جایگزین میشوند. متغیرهای فهرستی خاص هستند: هنگام بسط الگوهای فهرستی، re2js عبارتهای سمت راست دونقطه را چند بار تکرار میکند و هر بار رخدادهای متغیر فهرستی را با مقداری مختص به آن تکرار جایگزین مینماید. فهرستها دارای کرانهای اختیاری هستند (مقادیر منفی از انتها شمرده میشوند، برای نمونه -1 به معنای آخرین عنصر است). نامهای شرطی با یک نقطه شروع میشوند. هم شرطها و هم متغیرها میتوانند محلی (مختص به الگوی کد مفروض) یا سراسری (مجاز در تمام الگوهای کد) باشند. هنگامی که re2js فایل نحو را میخواند، بررسی میکند که هر الگوی کد فقط از متغیرها و شرطهایی استفاده کند که در آن مجاز هستند.
برای نمونه، الگوی کد زیر ساختار 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
}
در ادامه فهرستی از تمام الگوهای کد پشتیبانیشده توسط re2js به همراه متغیرها و شرطهای محلی آنها آمده است. توجه داشته باشید که یک تعریف خاص ممکن است از متغیرها و شرطهای محلی استفاده کند، اما الزامی به آن ندارد. هر الگوی کد استفادهنشده باید روی <undefined> تنظیم شود.
- code:var_global
- مشابه code:var_local، با این تفاوت که در سطح بالایی (top-level) استفاده میشود.
- code:const_local
- تعریف یک ثابت محلی. متغیرهای پشتیبانیشده: type (نوع ثابت)، name (نام آن) و init (مقدار اولیه).
- code:const_global
- مشابه code:const_local، با این تفاوت که در سطح بالایی (top-level) استفاده میشود.
- code:array_local
- تعریف یک آرایه محلی (جدول). متغیرهای پشتیبانیشده: type (نوع عناصر آرایه)، name (نام آرایه)، size (اندازه آن)، row (یک متغیر فهرستی که خودش هیچ کدی تولید نمیکند، اما عبارت فهرست را به تعداد سطرهای جدول بسط میدهد) و elem (یک متغیر فهرستی که به تمام عناصر جدول در سطر جاری بسط مییابد -- قرار است درون فهرست row جای گیرد). شرط پشتیبانیشده: .const (اگر آرایه تغییرناپذیر باشد، درست است).
- code:array_global
- مشابه code:array_local، با این تفاوت که در سطح بالایی (top-level) استفاده میشود.
- code:array_elem
- ارجاع به عنصری از یک آرایه (جدول). متغیرهای پشتیبانیشده: array (نام آرایه) و index (اندیس عنصر).
- code:enum
- تعریف یک شمارش (ممکن است با استفاده از یک ساختار زبانی ویژه برای شمارشها تعریف شود، یا صرفاً بهصورت چند ثابت مستقل). متغیرهای پشتیبانیشده عبارتند از type (نوع شمارشی تعریفشده توسط کاربر یا نوع ثابتها)، elem (متغیر فهرستی که به نام هر عضو بسط مییابد) و init (مقداردهی اولیه برای هر عضو). شرطها: .init (در صورت وجود مقداردهی اولیه، درست است).
- code:enum_elem
- عنصر شمارش (عضوی از یک نوع شمارشی تعریفشده توسط کاربر یا نام یک ثابت، بسته به نحوه تعریف code:enum). متغیرهای پشتیبانیشده عبارتند از name (نام عنصر) و type (نوع آن).
- code:assign
- دستور انتساب. متغیرهای پشتیبانیشده عبارتند از lhs (سمت چپ) و rhs (سمت راست).
- code:type_int
- نوع عدد صحیح علامتدار (signed).
- code:type_uint
- نوع عدد صحیح بدون علامت (unsigned).
- code:type_yybm
- نوع عناصر در جدول yybm.
- code:type_yytarget
- نوع عناصر در جدول yytarget.
- code:type_yyctable
- نوع عناصر در جدول yyctable.
- code:cmp_eq
- عملگر «مساوی» ("equals").
- code:cmp_ne
- عملگر «نامساوی» ("not equals").
- code:cmp_lt
- عملگر «کوچکتر از» ("less than").
- code:cmp_gt
- عملگر «بزرگتر از» ("greater than").
- code:cmp_le
- عملگر «کوچکتر یا مساوی» ("less or equal").
- code:cmp_ge
- عملگر «بزرگتر یا مساوی» ("greater or equal").
- code:if_then_else
- دستور If-then-else با یک یا چند شاخه. متغیرهای پشتیبانیشده: branch (یک متغیر فهرستی که خودش هیچ کدی تولید نمیکند، اما عبارت فهرست را به تعداد شاخهها بسط میدهد)، cond (شرط شاخه جاری) و stmt (یک متغیر فهرستی که به تمام دستورات در شاخه جاری بسط مییابد). شرطها: .cond (در صورت وجود شرط در شاخه جاری، درست است)، .many (اگر بیش از یک شاخه وجود داشته باشد، درست است).
- code:if_then_else_oneline
- گونهای خاص از code:if_then_else برای حالتی که تمام شاخهها دستورات یکخطی دارند. اگر این مقدار <undefined> باشد، در عوض از code:if_then_else استفاده میشود.
- code:switch
- یک دستور switch با یک یا چند case. متغیرهای پشتیبانیشده: expr (عبارت مورد بررسی در switch) و case (یک متغیر فهرستی که به تمام گروههای case همراه با بلوکهای کدشان بسط مییابد).
- code:switch_cases
- گروهی از 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). متغیرهای پشتیبانیشده: array (جدول حاوی اطلاعات goto محاسباتی)، index (اندیس عنصر در جدول) و base (برچسب پایه، تنها در صورتی استفاده میشود که .cgoto.relative درست باشد).
- code:cgoto:data
- عبارت مقداردهی اولیه برای یک عنصر واحد در جدول goto محاسباتی. متغیرهای پشتیبانیشده: label (برچسبی که برای مقداردهی اولیه عنصر جاری استفاده میشود)، type (نوع پایهای عناصر جدول) و base (برچسب پایه - تنها در صورتی استفاده میشود که .cgoto.relative درست باشد).
- code:fndecl
- اعلان تابع. متغیرهای پشتیبانیشده: name (نام تابع)، type (نوع بازگشتی)، throw (استثناهای پرتابشده توسط این تابع، به پیکربندی re2c:yyfn:throw نگاشت میشود)، arg (یک متغیر فهرستی که خودش کدی تولید نمیکند، اما عبارت فهرست را به تعداد آرگومانهای تابع بسط میدهد)، argname (نام آرگومان جاری)، argtype (نوع آرگومان جاری). شرط: .type (درست است اگر این یک تابع غیر void باشد).
- code:fndef
- مشابه code:fndecl، اما برای تعریف تابع استفاده میشود، بنابراین یک متغیر فهرستی اضافی به نام stmt دارد که به تمام دستورات موجود در بدنه تابع بسط مییابد.
- code:fncall
- دستور فراخوانی تابع. متغیرهای پشتیبانیشده: name (نام تابع)، retval (مقدار چپ یا l-value که مقدار بازگشتی در آن ذخیره میشود، در صورت وجود) و arg (یک متغیر فهرستی که به تمام آرگومانهای تابع بسط مییابد). شرطها: .args (اگر تابع دارای آرگومان باشد، درست است) و .retval (اگر نیاز به ذخیره مقدار بازگشتی باشد، درست است).
- code:tailcall
- دستور فراخوانی دم (tail call). متغیرهای پشتیبانیشده: name (نام تابع)، و arg (یک متغیر فهرستی که به تمام آرگومانهای تابع بسط مییابد). شرطها: .args (اگر تابع دارای آرگومان باشد، درست است) و .retval (درست است اگر این یک تابع غیر void باشد).
- code:recursive_functions
- بدنه برنامه با مدل کد --recursive-functions. متغیرهای پشتیبانیشده: fn (یک متغیر فهرستی که خودش کدی تولید نمیکند، اما عبارت فهرست را به تعداد توابع بسط میدهد)، fndecl (اعلان تابع جاری) و fndef (تعریف تابع جاری).
- code:fingerprint
- اثرانگشت (fingerprint) در بالای فایل خروجی تولیدشده. متغیرهای پشتیبانیشده: ver (نسخه re2js که برای تولید این فایل استفاده شده است) و date (تاریخ تولید).
- code:line_info
- قالب دستورات خط (line directives) (اگر این مقدار روی <undefined> تنظیم شود، هیچ دستوری تولید نمیشود). متغیرهای پشتیبانیشده: line (شماره خط) و file (نام فایل).
- code:abort
- دستوری که اجرای برنامه را متوقف (abort) میکند.
- code:yydebug
- دستور YYDEBUG، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYDEBUG، yyrecord، yych (به پیکربندیهای متناظر re2c: نگاشت میشوند)، state (شماره وضعیت DFA)..TP 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 بیش از یک باشد).
- 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).
در اینجا فهرستی از تمام متغیرهای سراسری مجاز در فایلهای نحو (syntax files) آمده است:
- nl
- یک خط جدید.
- indent
- متغیری که هیچ کدی تولید نمیکند، اما اثر جانبی افزایش سطح تورفتگی را به همراه دارد..TP dedent متغیری که هیچ کدی تولید نمیکند، اما دارای اثر جانبیِ کاهش سطح تورفتگی است.
- topindent
- رشتهٔ تورفتگی برای گزارهٔ فعلی. سطح تورفتگی توسط مولد کد ردیابی شده و بهطور خودکار بهروزرسانی میشود.
در اینجا فهرستی از تمام شرطهای سراسری مجاز در فایلهای نحو آمده است:
- .api.simple
- درست (True) است اگر از API ساده استفاده شود (--api simple یا re2c:api = simple).
- .api.generic
- درست است اگر از API عمومی استفاده شود (--api generic یا re2c:api = generic).
- .api.record
- درست است اگر از API رکورد استفاده شود (--api record یا re2c:api = record).
- .api_style.functions
- درست است اگر سبک API تابعگونه استفاده شود (re2c:api-style = functions).
- .api_style.freeform
- درست است اگر سبک API آزاد استفاده شود (re2c:api-style = free-form).
- .case_ranges
- درست است اگر قابلیت دامنههای case فعال باشد (--case-ranges یا re2c:case-ranges = 1).
- .cgoto.relative
- درست است اگر شکل نسبی goto محاسباتی استفاده شود (--computed-gotos-relative یا re2c:cgoto:relative = 1).
- .code_model.goto_label
- درست است اگر مدل کدِ مبتنی بر goto/label استفاده شود (--goto-label).
- .code_model.loop_switch
- درست است اگر مدل کدِ مبتنی بر loop/switch استفاده شود (--loop-switch).
- .code_model.recursive_functions
- درست است اگر مدل کدِ مبتنی بر توابع بازگشتی استفاده شود (--recursive-function).
- .date
- درست است اگر اثرانگشت (fingerprint) تولیدشده باید حاوی تاریخ تولید باشد.
- .loop_label
- درست است اگر حلقههای تولیدشده توسط re2js باید دارای برچسب باشند (re2c:label:yyloop روی یک رشتهٔ غیرخالی تنظیم شده باشد).
- .monadic
- درست است اگر کد تولیدشده باید مونادیک (monadic) باشد (re2c:monadic = 1). این گزینه فقط برای زبانهای تابعی محض مرتبط است.
- .start_conditions
- درست است اگر شرایط شروع فعال باشند (--start-conditions).
- .storable_state
- درست است اگر وضعیت قابل ذخیرهسازی فعال باشد (--storable-state).
- .unsafe
- درست است اگر re2js برای تولید کد سریعتر باید از بلوکهای "unsafe" استفاده کند (--unsafe، re2c:unsafe = 1). این مورد فقط برای زبانهایی که دارای قابلیت "unsafe" هستند مرتبط است.
- .version
- درست است اگر اثرانگشت تولیدشده باید حاوی نسخهٔ re2js باشد.
- .yyfill.enable
- درست است اگر YYFILL فعال باشد (re2c:yyfill:enable = 1).
- .yyfn.throw
- درست است اگر پیکربندی re2c:yyfn:throw روی یک رشتهٔ غیرخالی تعریف شده باشد.
مدیریت پایان ورودی (HANDLING THE END OF INPUT)
یکی از مسائل اصلی برای تحلیلگر واژگانی (lexer) این است که بداند چه زمانی متوقف شود. چندین شرط پایانی وجود دارد:
- تحلیلگر ممکن است با قاعدهای (از جمله قاعدهٔ پیشفرض *) تطبیق یافته و به یک وضعیت نهایی برسد
- تحلیلگر ممکن است در تطبیق با هر قاعدهای شکست بخورد و به یک وضعیت پیشفرض برسد
- تحلیلگر ممکن است به پایان ورودی برسد
دو شرط اول تحلیلگر را به شکلی "طبیعی" متوقف میکنند: به وضعیتی بدون هیچ انتقال خروجی میرسد، و فرایند تطبیق بهطور خودکار متوقف میشود. شرط سوم، یعنی پایان ورودی، متفاوت است: ممکن است در هر وضعیتی رخ دهد و تحلیلگر باید بتواند آن را مدیریت کند. بررسی پایان ورودی، گردش کار عادی تحلیلگر را متوقف کرده و شاخههای شرطی را به برنامهٔ تولیدشده اضافه میکند، بنابراین به حداقل رساندن تعداد چنین بررسیهایی ضروری است. re2js از چندین روش مختلف برای مدیریت پایان ورودی پشتیبانی میکند. استفاده از هر یک از آنها به پیچیدگی عبارات باقاعده، نیاز به بافرسازی، ملاحظات کارایی و سایر عوامل بستگی دارد. در ادامه فهرستی از این روشها آورده شده است:
- Sentinel (نویسهٔ نگهبان). این روش نیاز به بررسیهای پایان ورودی را بهکلی برطرف میکند. این روش ساده و کارآمد است، اما محدود به مواردی است که یک نویسهٔ "نگهبان" طبیعی وجود داشته باشد که هرگز نتواند در ورودی معتبر رخ دهد. این نویسه ممکن است همچنان در ورودی نامعتبر رخ دهد، اما نباید توسط عبارات باقاعده مجاز دانسته شود، مگر شاید به عنوان آخرین نویسهٔ یک قاعده. نویسهٔ نگهبان به انتهای ورودی ضمیمه میشود و به عنوان سیگنال توقف عمل میکند: هنگامی که تحلیلگر این نویسه را بخواند، یا خطای نحوی رخ داده یا پایان ورودی است. در هر دو حالت تحلیلگر باید متوقف شود. این روش در صورتی استفاده میشود که YYFILL با re2c:yyfill:enable = 0; غیرفعال شده باشد و re2c:eof مقدار پیشفرض -1 را داشته باشد.
- Sentinel همراه با بررسیهای مرز (Sentinel with bounds checks). این روش عمومی (generic) است: امکان مدیریت هر ورودی را بدون محدودیت روی عبارات باقاعده فراهم میکند. ایده این است که تعداد بررسیهای پایان ورودی با انجام آنها تنها روی نویسههایی خاص کاهش یابد. مشابه روش "نگهبان"، یکی از نویسهها به عنوان "نگهبان" انتخاب شده و به انتهای ورودی اضافه میشود. با این حال، هیچ محدودیتی در مورد محل وقوع نگهبان وجود ندارد (در واقع هر نویسهای میتواند به عنوان نگهبان انتخاب شود). هنگامی که تحلیلگر این نویسه را بخواند، علاوه بر این یک بررسی مرز (bounds check) نیز انجام میدهد. اگر موقعیت فعلی در محدوده باشد، تحلیلگر تطبیق را از سر میگیرد و با نگهبان مانند یک نویسهٔ معمولی رفتار میکند. در غیر این صورت YYFILL را فراخوانی میکند (مگر اینکه غیرفعال شده باشد). اگر ورودی بیشتری ارائه شود، تحلیلگر نویسهٔ آخر را دوباره مطابقت میدهد و طوری ادامه میدهد که انگار نگهبان در آنجا نبوده است. در غیر این صورت، ورودی واقعاً به پایان رسیده است و تحلیلگر متوقف میشود. این روش زمانی استفاده میشود که re2c:eof مقدار غیرمنفی داشته باشد (باید روی مقدار عددی نگهبان تنظیم شود). YYFILL اختیاری است.
- بررسیهای مرز با لایهگذاری (Bounds checks with padding). این روش عمومی است و ممکن است سریعتر از روش "Sentinel همراه با بررسیهای مرز" باشد، اما پیچیدهتر نیز هست. ایده این است که وضعیتهای DFA به مؤلفههای قویاً همبند (SCCها) افراز شوند و برای هر SCC یک بررسی منفرد برای تعداد نویسهٔ کافی جهت پوشش دادن طولانیترین مسیر بدون حلقه در آن SCC تولید شود. این کار تعداد بررسیها را کاهش میدهد، اما برای نشانههای (lexemes) کوتاه در انتهای ورودی مشکلی ایجاد میکند، زیرا بررسی به تعداد نویسهٔ کافی برای پوشش طولانیترین نشانه نیاز دارد. این مشکل را میتوان با لایهگذاری (padding) ورودی با چند نویسهٔ ساختگی که پسوند معتبری برای هیچ نشانهای تشکیل نمیدهند (به طوری که تحلیلگر نتواند با آنها تطبیق یابد) برطرف کرد. طول لایهگذاری باید 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 در میانهٔ یک رشته رخ دهد، یک خطای نحوی است و تحلیلگر لغوی قاعدهٔ پیشفرض * را تطبیق خواهد داد، اما فراتر از انتهای ورودی را نخواهد خواند و دچار فروپاشی نخواهد شد (برای راستیآزمایی این موضوع از هشدار -Wsentinel-in-midrule و پیکربندی re2c:sentinel استفاده کنید). پیکربندی re2c:yyfill:enable = 0; از تولید بررسیهای مرز و فراخوانیهای YYFILL جلوگیری میکند.
// re2js $INPUT -o $OUTPUT
// expects a null-terminated string
function lex(yyinput) {
let yycursor = 0;
let count = 0
loop: while (true) {
/*!re2c
re2c:yyfill:enable = 0;
* { return -1 }
[\x00] { return count }
[ ]+ { continue loop }
[a-z]+ { count += 1; continue loop }
*/
}
}
function test(s, n) { if (lex(s) != n) throw "error!"; }
test("\0", 0)
test("one two three\0", 3)
test("f0ur\0", -1)
نگهبان با بررسیهای مرز (Sentinel with bounds checks)
این مثال از نگهبان به همراه بررسیهای مرز برای مدیریت پایان ورودی استفاده میکند (این روش در نسخهٔ 1.2 اضافه شد). برنامه رشتههای محصور در تکنقلقول و جداشده با فاصله را شمارش میکند. نویسهٔ نگهبان null است که با پیکربندی re2c:eof = 0; مشخص میشود. همانند روش sentinel، نویسهٔ null آخرین نویسهٔ هر رشتهٔ ورودی است، اما در میانهٔ یک قاعده مجاز است (برای مثال، 'aaa\0aa'\0 ورودی معتبری است، اما 'aaa\0 یک خطای نحوی است). بررسیهای مرز در هر وضعیتی که با یک نویسهٔ ورودی مطابقت داشته باشد تولید میشوند، اما دامنهٔ آنها به شاخهای که null را مدیریت میکند محدود است. بررسیهای مرز به شکل YYLIMIT <= YYCURSOR یا با API عمومی به صورت YYLESSTHAN(1) هستند. اگر شرط بررسی برقرار باشد، تحلیلگر لغوی به انتهای ورودی رسیده است و باید متوقف شود (YYFILL با re2c:yyfill:enable = 0; غیرفعال شده است زیرا ورودی در یک بافر جای میگیرد؛ برای مثالی که از YYFILL استفاده میکند به بخش YYFILL with sentinel مراجعه کنید). رسیدن به انتهای ورودی سه احتمال را مطرح میکند: اگر تحلیلگر لغوی در وضعیت اولیه باشد، با قاعدهٔ انتهای ورودی $ تطبیق مییابد، در غیر این صورت ممکن است به یک قاعدهٔ تطبیقیافتهٔ قبلی بازگردد (شامل قاعدهٔ پیشفرض *) یا به یک وضعیت پیشفرض برود، که موجب -Wundefined-control-flow میشود.
// re2js $INPUT -o $OUTPUT
// expects a null-terminated string
function lex(yyinput) {
let yycursor = 0;
let yylimit = yyinput.length - 1 // terminating null not included
let count = 0
loop: while (true) {
/*!re2c
re2c:yyfill:enable = 0;
re2c:eof = 0;
str = ['] ([^'\\] | [\\][^])* ['];
* { return -1 }
$ { return count }
[ ]+ { continue loop }
str { count += 1; continue loop }
*/
}
}
function test(s, n) { if (lex(s) != n) throw "error!"; }
test("\0", 0)
test("'qu\0tes' 'are' 'fine: \\'' \0", 3)
test("'unterminated\\'\0", -1)
بررسیهای مرز با لایهگذاری (Bounds checks with padding)
این مثال از بررسیهای مرز همراه با لایهگذاری (padding) برای مدیریت پایان ورودی استفاده میکند (این روش بهطور پیشفرض فعال است). برنامه رشتههای محصور در تکنقلقول و جداشده با فاصله را شمارش میکند. یک لایهگذاری شامل YYMAXFILL نویسهٔ null به انتهای ورودی ضمیمه شده است، که در آن مقدار YYMAXFILL بهطور خودکار با یک بلوک max تولید میشود. استفاده از null برای لایهگذاری الزامی نیست --- هر نویسهای تا زمانی که پسوندی برای یک واحد واژگانی معتبر نسازد میتواند استفاده شود (در این مثال لایهگذاری نباید شامل تکنقلقول باشد، زیرا ممکن است با پسوند یک رشتهٔ محصور در تکنقلقول اشتباه گرفته شود). یک قاعدهٔ "stop" وجود دارد که با نخستین نویسهٔ لایهگذاری (null) تطبیق مییابد و تحلیلگر لغوی را خاتمه میدهد (توجه داشته باشید که این قاعده بررسی میکند آیا null در ابتدای لایهگذاری است یا خیر، در غیر این صورت یک خطای نحوی است). بررسیهای مرز تنها در برخی وضعیتها تولید میشوند که توسط مؤلفههای قویاً همبند در اتوماتون زیربنایی تعیین میگردند. بررسیها به صورت (YYLIMIT - YYCURSOR) < n یا با API عمومی به شکل YYLESSTHAN(n) هستند، که در آن n حداقل تعداد نویسههایی است که برای پیشروی تحلیلگر لغوی مورد نیاز است (همچنین به این معناست که بررسی مرز بعدی حداکثر در n نویسه رخ خواهد داد). اگر شرط بررسی درست باشد، تحلیلگر لغوی به انتهای ورودی رسیده است و YYFILL(n) را فراخوانی میکند که باید حداقل n نویسهٔ ورودی را تأمین کند یا بازگشت نداشته باشد. در این مثال YYFILL همیشه ناموفق است و تحلیلگر لغوی را با یک خطا خاتمه میدهد (که مشکلی ایجاد نمیکند زیرا ورودی درون یک بافر جای میگیرد). برای مثالی که بافر ورودی را با YYFILL بازپر میکند، به بخش YYFILL with padding مراجعه کنید.
// re2js $INPUT -o $OUTPUT
/*!max:re2c*/
function lex(yyinput) {
let yycursor = 0;
let yylimit = yyinput.length
let count = 0
loop: while (true) {
/*!re2c
re2c:YYFILL = "return -1";
str = ['] ([^'\\] | [\\][^])* ['];
[\x00] {
// check that it is the sentinel, not some unexpected null
return (yycursor == yylimit - YYMAXFILL + 1) ? count : -1
}
str { count += 1; continue loop }
[ ]+ { continue loop }
* { return -1 }
*/
}
}
function test(s, n) {
let padded_s = s + "\0".repeat(YYMAXFILL)
if (lex(padded_s) != n) throw "error!"
}
test("", 0)
test("'unterminated\\'", -1)
test("'qu\0tes' 'are' 'fine: \\'' ", 3)
test("'unexpected \0 null", -1)
این مثال از یک روش سفارشی مدیریت پایان ورودی بر پایه generic API استفاده میکند. این برنامه رشتههای محصور در نقلقول تکی را که با فاصله جدا شدهاند، میشمارد. این برنامه مشابه با مثال sentinel است، با این تفاوت که ورودی به نویسه تهی ختم نمیشود (null-terminated نیست). برای جبران نبود نویسه sentinel در پایان ورودی، YYPEEK بازتعریف شده است تا پیش از خواندن نویسه بعدی ورودی، یک بررسی محدوده انجام دهد. این روش ناکارآمد است زیرا بررسیها با بسامد بسیار بالایی انجام میشوند. اگر شرط بررسی برقرار نباشد، YYPEEK نویسه واقعی را برمیگرداند؛ در غیر این صورت، یک نویسه sentinel جعلی بازمیگرداند.
// re2js $INPUT -o $OUTPUT
// expects a string without terminating null
function lex(str) {
let cur = 0;
let lim = str.length
let count = 0
loop: while (true) {
/*!re2c
re2c:api = generic;
re2c:YYPEEK = "cur < lim ? str.charCodeAt(cur) : 0";
re2c:YYSKIP = "cur += 1";
re2c:yyfill:enable = 0;
* { return -1 }
[\x00] { return count }
[ ]+ { continue loop }
[a-z]+ { count += 1; continue loop }
*/
}
}
function test(s, n) { if (lex(s) != n) throw "error!"; }
test("", 0)
test("one two three", 3)
test("f0ur", -1)
پر کردن مجدد بافر (BUFFER REFILLING)
نیاز به بافرینگ زمانی رخ میدهد که نتوان کل ورودی را بهطور همزمان در حافظه نگاشت کرد: یا ورودی بیش از حد بزرگ است، یا به صورت جریانی (مانند خواندن از یک سوکت) دریافت میشود. روش معمول در چنین مواردی، تخصیص یک بافر حافظه با اندازه ثابت و پردازش ورودی در تکههایی (chunks) است که در بافر جا میشوند. هنگامی که تکه فعلی پردازش شد، به بیرون منتقل شده و دادههای جدید به بافر وارد میشوند. در عمل این فرآیند تا حدی پیچیدهتر است، زیرا وضعیت تحلیلگر لغوی (lexer) تنها از یک موقعیت ورودی تشکیل نشده، بلکه مجموعهای از موقعیتهای مرتبط با یکدیگر است:
- cursor: نویسه بعدی ورودی برای خواندن (YYCURSOR در C pointer API یا YYSKIP/YYPEEK در generic API)
- limit: موقعیت بعد از آخرین نویسه ورودی موجود (YYLIMIT در C pointer API، که بهطور ضمنی توسط YYLESSTHAN در generic API مدیریت میشود)
- marker: موقعیت تازهترین تطابق، در صورت وجود (YYMARKER در default API یا YYBACKUP/YYRESTORE در generic API)
- token: ابتدای جزء واژگانی (lexeme) فعلی (بهطور ضمنی در re2js API، زیرا برای عملکرد عادی تحلیلگر لغوی نیازی به آن نیست و میتواند توسط کاربر تعریف و بهروزرسانی شود)
- context marker: موقعیت زمینه دنبالهدار (YYCTXMARKER در C pointer API یا YYBACKUPCTX/YYRESTORECTX در generic API)
- متغیرهای برچسب (tag variables): موقعیتهای زیرتطابق (تعریفشده با بلوکهای stags و mtags و سازوکارهای اولیه generic API شامل YYSTAGP/YYSTAGN/YYMTAGP/YYMTAGN)
تمام این موارد در هر حالتی استفاده نمیشوند، اما در صورت استفاده، باید توسط YYFILL بهروزرسانی شوند. تمام موقعیتهای فعال در بازه میان token و cursor قرار دارند، بنابراین هر آنچه میان آغاز بافر و token قرار دارد را میتوان دور ریخت، بخش میان token تا limit باید به ابتدای بافر منتقل شود، و فضای خالی انتهای بافر باید با دادههای جدید پر گردد. به منظور جلوگیری از فراخوانیهای مکرر YYFILL، بهتر است تا حد امکان نویسههای ورودی بیشتری پر شوند (حتی اگر نویسههای کمتری برای از سرگیری کار تحلیلگر لغوی کافی باشد). جزئیات پیادهسازی YYFILL بسته به روش مورد استفاده برای مدیریت EOF کمی متفاوت است: حالت قاعده EOF تا حدودی سادهتر از حالت بررسی محدوده همراه با فاصلهگذاری (padding) است. همچنین توجه داشته باشید که اگر گزینه -f --storable-state استفاده شود، YYFILL دارای معناشناسی کمی متفاوتی خواهد بود (که در بخش مربوط به وضعیت قابل ذخیرهسازی توضیح داده شده است).
YYFILL همراه با نشانگر (YYFILL with sentinel)
اگر قاعده EOF استفاده شود، YYFILL یک سازوکار پایهایِ تابعمانند است که هیچ آرگومانی نمیپذیرد و مقداری را برمیگرداند که با صفر مقایسه میشود. فراخوانی YYFILL با شرط YYLIMIT <= YYCURSOR در C pointer API و YYLESSTHAN() در generic API فعال میشود. مقدار بازگشتی غیرصفر به این معنی است که YYFILL با شکست مواجه شده است. یک فراخوانی موفق YYFILL باید حداقل یک نویسه را فراهم کند و موقعیتهای ورودی را بر همین اساس تنظیم نماید. limit همیشه باید بر روی یک موقعیت پس از آخرین موقعیت ورودی در بافر تنظیم شود، و نویسه موجود در موقعیت limit باید نماد نشانگر (sentinel) مشخصشده توسط پیکربندی re2c:eof باشد. تصاویر زیر مکانهای نسبی موقعیتهای ورودی در بافر را قبل و بعد از فراخوانی YYFILL نشان میدهند (نماد sentinel با # مشخص شده است، و تصویر دوم حالتی را نشان میدهد که ورودی کافی برای پر کردن کل بافر وجود ندارد).
<-- 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 استفاده میکند.
// re2js $INPUT -o $OUTPUT
const fs = require('fs')
const BUFSIZE = 4096
const OK = 0
const EOF = 1
const LONG_LEXEME = 2
function fill(st) {
if (st.eof) return EOF
// Error: lexeme too long. In real life could reallocate a larger buffer.
if (st.token < 1) return LONG_LEXEME
// Shift buffer contents (discard everything up to the current token).
st.yyinput.copy(st.yyinput, 0, st.token, st.yylimit)
st.yycursor -= st.token;
st.yymarker -= st.token;
st.yylimit -= st.token;
st.token = 0;
// Read a new chunk of data from file and append it to `yyinput`.
let want = BUFSIZE - st.yylimit - 1 // -1 for sentinel
let nread = fs.readSync(st.file, st.yyinput, st.yylimit, want)
st.eof = nread < want // end of file?
st.yylimit += nread
st.yyinput.writeUInt8(0, st.yylimit) // sentinel
return OK
}
function lex(yyrecord, count) {
loop: while (true) {
yyrecord.token = yyrecord.yycursor
/*!re2c
re2c:api = record;
re2c:YYPEEK = "readUInt8";
re2c:YYFILL = "fill(yyrecord) == OK";
re2c:eof = 0;
str = ['] ([^'\\] | [\\][^])* ['];
* { return -1 }
$ { return count }
[ ]+ { continue loop }
str { count += 1; continue loop }
*/
}
}
function main() {
let fname = "input"
// Create input file.
let content = "'qu\0tes' 'are' 'fine: \\'' ".repeat(BUFSIZE)
fs.writeFileSync(fname, content, function(err) { if (err) throw err; })
// Init lexer state.
let limit = BUFSIZE - 1 // exclude terminating null
let st = {
file: fs.openSync(fname, 'r'),
yyinput: Buffer.alloc(BUFSIZE),
yylimit: limit,
yycursor: limit,
yymarker: limit,
token: limit,
eof: false
}
// Run lexer on the prepared file.
if (lex(st, 0) != 3 * BUFSIZE) { throw "error :[" }
// Cleanup.
fs.unlink(fname, function(err){ if (err) throw err; })
}
main()
در حالت پیشفرض (زمانی که از قاعده EOF استفاده نمیشود)، YYFILL یک سازوکار پایهایِ تابعمانند است که یک تکآرگومان میپذیرد و هیچ مقداری را بازنمیگرداند. فراخوانی YYFILL با شرط (YYLIMIT - YYCURSOR) < n در C pointer API و با YYLESSTHAN(n) در generic API فعال میشود. آرگومان ارسالی به YYFILL، کمینه تعداد نویسههایی است که باید تأمین شوند. اگر در انجام این کار ناموفق باشد، YYFILL نباید کنترل را به لکسر (lexer) بازگرداند (به همین دلیل، بهتر است به صورت ماکرویی پیادهسازی شود که هنگام شکست، از تابع فراخواننده بازگردد). در صورت فراخوانی موفقیتآمیز YYFILL، موقعیت limit باید یا روی یک واحد پس از آخرین موقعیت ورودی در بافر تنظیم شود، یا به انتهای لایهگذاری YYMAXFILL منتقل گردد (در صورتی که YYFILL دستکم n نویسه را با موفقیت خوانده باشد، اما نه به اندازهای که کل بافر پر شود). تصاویر زیر موقعیتهای نسبی ورودی در بافر را قبل و بعد از فراخوانی YYFILL نشان میدهند (لایهگذاری YYMAXFILL در تصویر دوم با نمادهای # مشخص شده است).
<-- shift --> <-- need -->
>-A------------B---------C-----D-------E---F--------G->
buffer token marker cursor limit
>-A------------B---------C-----D-------E---F--------G->
buffer, marker cursor limit
token
<-- shift --> <-- need -->
>-A------------B---------C-----D-------E-F (EOF)
buffer token marker cursor limit
>-A------------B---------C-----D-------E-F###############
buffer, marker cursor limit
token <- YYMAXFILL ->
در اینجا مثالی از برنامهای آورده شده است که فایل ورودی input.txt را در قطعههای 4096 بایتی میخواند و از بررسی محدودهها (bounds-checking) همراه با لایهگذاری استفاده میکند.
// re2js $INPUT -o $OUTPUT
const fs = require('fs')
const BUFSIZE = 4096
const OK = 0
const EOF = 1
const LONG_LEXEME = 2
/*!max:re2c*/
function fill(st, need) {
if (st.eof) return EOF
// Error: lexeme too long. In real life could reallocate a larger buffer.
if (st.token < need) return LONG_LEXEME
// Shift buffer contents (discard everything up to the current token).
st.yyinput.copy(st.yyinput, 0, st.token, st.yylimit)
st.yycursor -= st.token;
st.yylimit -= st.token;
st.token = 0;
// Read a new chunk of data from file and append it to `yyinput`.
let want = BUFSIZE - st.yylimit - 1 // -1 for sentinel
let nread = fs.readSync(st.file, st.yyinput, st.yylimit, want)
st.yylimit += nread
if (nread < want) {
st.eof = true // end of file
st.yyinput.write("\0".repeat(YYMAXFILL), st.yylimit)
st.yylimit += YYMAXFILL
}
return OK
}
function lex(yyrecord, count) {
loop: while (true) {
yyrecord.token = yyrecord.yycursor
/*!re2c
re2c:api = record;
re2c:YYPEEK = "readUInt8";
re2c:YYFILL = "if (fill(yyrecord, @@) != OK) return -1;";
str = ['] ([^'\\] | [\\][^])* ['];
[\x00] {
// Check that it is the sentinel, not some unexpected null.
return yyrecord.token == yyrecord.yylimit - YYMAXFILL ? count : -1
}
str { count += 1; continue loop }
[ ]+ { continue loop }
* { return -1 }
*/
}
}
function main() {
let fname = "input"
// Create input file.
let content = "'qu\0tes' 'are' 'fine: \\'' ".repeat(BUFSIZE)
fs.writeFileSync(fname, content, function(err) { if (err) throw err; })
// Init lexer state.
let limit = BUFSIZE - 1 // exclude terminating null
let st = {
file: fs.openSync(fname, 'r'),
yyinput: Buffer.alloc(BUFSIZE),
yylimit: limit,
yycursor: limit,
token: limit,
eof: false
}
// Run lexer on the prepared file.
if (lex(st, 0) != 3 * BUFSIZE) { throw "error :[" }
// Cleanup.
fs.unlink(fname, function(err){ if (err) throw err; })
}
main()
بلوکهای چندگانه (Multiple blocks)
گاهی اوقات داشتن چندین تحلیلگر لغوی (lexer) مرتبط با یکدیگر ضروری است (برای نمونه، هنگامی که یک ماشین حالت سطح بالا وجود دارد که بین حالتهای مختلف تحلیلگر جابهجا میشود). این قابلیت را میتوان با استفاده از چندین بلوک متصل re2js پیادهسازی کرد. گزینه دیگر استفاده از start conditions است.
پیادهسازی اتصالات بین بلوکها به زبان مقصد بستگی دارد. در زبانهایی که دارای دستور goto هستند (مانند C/C++ و Go) میتوان همه بلوکها را در یک تابع قرار داد، بهطوری که پیشوند هر یک یک برچسب (label) باشد. گذار از یک بلوک به بلوک دیگر یک goto ساده است. در زبانهایی که فاقد دستور goto هستند (مانند Rust)، لازم است از یک حلقه به همراه switch روی یک متغیر حالت استفاده شود، مشابه حلقه/switch مربوط به yystate که توسط re2js تولید میشود؛ در غیر این صورت باید هر بلوک درون یک تابع محصور شده و از فراخوانی توابع استفاده گردد.
مثال زیر از چندین بلوک برای تجزیه اعداد دودویی (binary)، هشتهشتی (octal)، دهدهی (decimal) و شانزدهشانزدهی (hexadecimal) استفاده میکند. هر مبنا بلوک مخصوص به خود را دارد. بلوک اولیه، مبنا را مشخص کرده و کنترل را به سایر بلوکها منتقل میکند. پیکربندیهای مشترک در بلوکی مجزا در ابتدای برنامه تعریف شدهاند؛ این پیکربندیها توسط سایر بلوکها به ارث برده میشوند.
// re2js $INPUT -o $OUTPUT
/*!re2c // Common re2c definitions shared between all functions.
re2c:api = record;
re2c:yyrecord = st;
re2c:yyfill:enable = 0;
*/
function parse_u32(str) {
let st = {
yyinput: str,
yycursor: 0,
yymarker: 0
}
/*!re2c
'0b' / [01] { return parse_bin(st) }
"0" { return parse_oct(st) }
"" / [1-9] { return parse_dec(st) }
'0x' / [0-9a-fA-F] { return parse_hex(st) }
* { return null }
*/
}
function parse_bin(st) {
n = 0
loop: while (true) {
/*!re2c
[01] { n = n * 2 + (st.yyinput.charCodeAt(st.yycursor - 1) - 48); continue loop }
* { return n }
*/
}
}
function parse_oct(st) {
n = 0
loop: while (true) {
/*!re2c
[0-7] { n = n * 8 + (st.yyinput.charCodeAt(st.yycursor - 1) - 48); continue loop }
* { return n }
*/
}
}
function parse_dec(st) {
n = 0
loop: while (true) {
/*!re2c
[0-9] { n = n * 10 + (st.yyinput.charCodeAt(st.yycursor - 1) - 48); continue loop }
* { return n }
*/
}
}
function parse_hex(st) {
n = 0
loop: while (true) {
/*!re2c
[0-9] { n = n * 16 + (st.yyinput.charCodeAt(st.yycursor - 1) - 48); continue loop }
[a-f] { n = n * 16 + (st.yyinput.charCodeAt(st.yycursor - 1) - 87); continue loop }
[A-F] { n = n * 16 + (st.yyinput.charCodeAt(st.yycursor - 1) - 55); continue loop }
* { return n }
*/
}
}
function test(s, n) {
if (parse_u32(s) != n) throw "error!"
}
test("\0", null)
test("1234567890\0", 1234567890)
test("0b1101\0", 13)
test("0x7Fe\0", 2046)
test("0644\0", 420)
test("9999999999\0", 9999999999)
شرایط شروع (Start conditions)
شرایط شروع با گزینه --start-conditions فعال میشوند. آنها روشی برای کدگذاری چندین خودکاره (automata) مرتبط با یکدیگر در یک بلوک یکسان re2js فراهم میکنند.
هر شرط متناظر با یک خودکاره منفرد است و دارای نامی یکتا است که توسط کاربر مشخص میشود، و همچنین شمارهای داخلی و یکتا که توسط re2js تعریف میگردد. این شمارهها برای جابهجایی میان شرایط استفاده میشوند: کد تولیدشده از توابع اولیه 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 پرش میکند.
کدی که re2js برای شرایط تولید میکند، بستگی به این دارد که آیا re2js از رویکرد goto/label استفاده میکند یا رویکرد loop/switch را برای کدگذاری خودکاره به کار میبرد.
در زبانهایی که دارای دستور goto هستند (مانند C/C++ و Go) شرایط بهطور طبیعی بهصورت بلوکهایی از کد با پیشوند برچسبهایی به شکل yyc_<cond> پیادهسازی میشوند، که در آن cond نام شرط است (پیشوند برچسب را میتوان با re2c:cond:prefix تغییر داد). گذار بین شرایط با استفاده از goto و برچسبهای شرایط پیادهسازی میشود. پیش از همه شرایط، re2js یک switch اولیه روی YYGETSTATE تولید میکند که به وضعیت شروع شرط فعلی پرش میکند. قواعد میانبر :=> از switch اولیه صرفنظر کرده و مستقیماً به شرط مشخصشده پرش میکنند (میتوان از re2c:cond:goto برای تغییر رفتار پیشفرض استفاده کرد). قواعد دارای اعمال معنایی بهطور خودکار به شرط بعدی پرش نمیکنند؛ این کار باید توسط کد عملیاتی تعریفشده توسط کاربر انجام شود.
در زبانهایی که فاقد goto هستند (مانند Rust)، re2js از متغیر yystate برای ذخیره شماره شرایط استفاده مجدد میکند. هر شرط یک شناسه عددی برابر با شماره حالت شروع خود دریافت میکند، و switch میان شرایط هیچ تفاوتی با switch میان حالتهای DFA یک شرط منفرد ندارد. نیازی به یک switch اولیه مجزا برای شرایط نیست. (از آنجا که رویکرد مشابهی برای پیادهسازی حالتهای قابل ذخیره به کار میرود، در صورت استفاده همزمان از حالتهای قابل ذخیره و شرایط، توابع YYGETCOND/YYSETCOND زائد خواهند بود).
برنامه زیر از شرایط شروع برای تجزیه اعداد دودویی، هشتهشتی، دهدهی و شانزدهشانزدهی استفاده میکند. یک بلوک منفرد وجود دارد که در آن هر مبنا شرط مخصوص به خود را دارد و شرط اولیه به همه آنها متصل است. متغیر تعریفشده توسط کاربر cond شماره شرط فعلی را ذخیره میکند؛ این متغیر با شماره شرط اولیهای که توسط بلوک conditions تولید شده است مقداردهی اولیه میشود.
// re2js $INPUT -o $OUTPUT -c
/*!conditions:re2c*/
function parse_u32(yyinput) {
let yycursor = 0
let yycond = YYC_INIT
let n = 0
loop: while (true) {
/*!re2c
re2c:yyfill:enable = 0;
re2c:indent:top = 2;
<INIT> '0b' / [01] :=> BIN
<INIT> "0" :=> OCT
<INIT> "" / [1-9] :=> DEC
<INIT> '0x' / [0-9a-fA-F] :=> HEX
<INIT> * { return null }
<BIN> [01] { n = n * 2 + (yyinput.charCodeAt(yycursor - 1) - 48); continue loop }
<OCT> [0-7] { n = n * 8 + (yyinput.charCodeAt(yycursor - 1) - 48); continue loop }
<DEC> [0-9] { n = n * 10 + (yyinput.charCodeAt(yycursor - 1) - 48); continue loop }
<HEX> [0-9] { n = n * 16 + (yyinput.charCodeAt(yycursor - 1) - 48); continue loop }
<HEX> [a-f] { n = n * 16 + (yyinput.charCodeAt(yycursor - 1) - 87); continue loop }
<HEX> [A-F] { n = n * 16 + (yyinput.charCodeAt(yycursor - 1) - 55); continue loop }
<BIN, OCT, DEC, HEX> * { return n }
*/
}
}
function test(s, n) {
if (parse_u32(s) != n) throw "error!"
}
test("\0", null)
test("1234567890\0", 1234567890)
test("0b1101\0", 13)
test("0x7Fe\0", 2046)
test("0644\0", 420)
test("9999999999\0", 9999999999)
با گزینهٔ --storable-state، ابزار re2js یک لکسر تولید میکند که میتواند وضعیت فعلی خود را ذخیره کرده، به فراخواننده بازگردد، و بعداً عملیات را دقیقاً از همان جایی که متوقف شده بود از سر بگیرد. حالت پیشفرض عملیات در re2js یک مدل "pull" است که در آن لکسر هر زمان نیاز داشته باشد، ورودی بیشتری را «میکشد» (دریافت میکند). این امر ممکن است در مواردی که ورودی به صورت تکهتکه در دسترس قرار میگیرد غیرقابلقبول باشد (برای مثال، اگر لکسر توسط یک تجزیهکننده (parser) فراخوانی شود، یا اگر برنامهٔ لکسر از طریق یک پروتکل سوکت با برنامهٔ دیگری ارتباط برقرار کند که باید پیش از ارسال پیام بعدی، منتظر پاسخ از سوی لکسر بماند). قابلیت وضعیت قابل ذخیرهسازی دقیقاً برای چنین مواردی در نظر گرفته شده است: این ویژگی امکان تولید لکسرهایی را فراهم میکند که در یک مدل "push" کار میکنند. هنگامی که لکسر به ورودی بیشتری نیاز دارد، وضعیت خود را ذخیره کرده و به فراخواننده بازمیگردد. بعداً، زمانی که ورودی بیشتری در دسترس قرار گیرد، فراخواننده لکسر را دقیقاً از همانجایی که متوقف شده بود از سر میگیرد. در مقایسه با مدل "pull"، چند تغییر لازم است:
- دستورات پایهای YYSETSTATE() و YYGETSTATE(state) را تعریف کنید.
- متغیرهای yych، yyaccept (در صورت استفاده) و state را به عنوان بخشی از وضعیت ماندگار لکسر تعریف کنید. متغیر state باید با -1 مقداردهی اولیه شود.
- دستور YYFILL باید به جای تلاش برای تأمین ورودی بیشتر، به برنامهٔ بیرونی بازگردد. کد بازگشتی باید نشان دهد که لکسر به ورودی بیشتری نیاز دارد.
- برنامهٔ بیرونی باید موقعیتهایی را که لکسر به ورودی بیشتری نیاز دارد تشخیص داده و واکنش مناسب نشان دهد.
- به صورت اختیاری از بلوک getstate برای تولید سوئیچ YYGETSTATE جدا از لکسر اصلی استفاده کنید. این قابلیت تنها برای زبانهایی کار میکند که دارای goto هستند (نه در حالت --loop-switch).
- از re2c:eof و روش sentinel with bounds checks برای مدیریت پایان ورودی استفاده کنید. روش مبتنی بر پدینگ (padding) ممکن است کار نکند زیرا مشخص نیست چه زمانی باید پدینگ اضافه شود: پایان فعلی ورودی ممکن است پایان نهایی ورودی نباشد، و اضافه کردن زودهنگام پدینگ ممکن است یک لکسم طماع (greedy lexeme) را که بخشی از آن خوانده شده قطع کند. علاوه بر این، به دلیل منطق سطح بالای برنامه، دریافت ورودی بیشتر ممکن است به پردازش لکسم موجود در انتهای بافر وابسته باشد (که از قبل به دلیل شرط پایان ورودی مسدود شده است).
در اینجا نمونهای از یک لکسر با مدل "push" آورده شده است که خواندن بستهها از یک سوکت را شبیهسازی میکند. لکسر تا رسیدن به پایان ورودی در یک حلقه اجرا میشود و به تابع فراخواننده بازمیگردد. تابع فراخواننده با «ارسال» بستهٔ بعدی، ورودی بیشتری فراهم کرده و تحلیل لغوی را از سر میگیرد. این فرایند زمانی که تمام بستهها ارسال شوند یا خطایی رخ دهد، متوقف میشود.
// re2js $INPUT -o $OUTPUT -f
const fs = require('fs')
// Use a small buffer to cover the case when a lexeme doesn't fit.
// In real world use a larger buffer.
const BUFSIZE = 10
const DEBUG = false
const END = 0
const READY = 1
const WAITING = 2
const BIG_PACKET = 3
const BAD_PACKET = 4
function log() {
if (DEBUG) console.log.apply(console, arguments)
}
function fill(st) {
// Error: lexeme too long. In real life could reallocate a larger buffer.
if (st.token < 1) return BIG_PACKET
// Shift buffer contents (discard everything up to the current token).
st.yyinput.copy(st.yyinput, 0, st.token, st.yylimit)
st.yycursor -= st.token;
st.yymarker -= st.token;
st.yylimit -= st.token;
st.token = 0;
// Read a new chunk of data from file and append it to `yyinput`.
let want = BUFSIZE - st.yylimit - 1 // -1 for sentinel
let nread = fs.readSync(st.file, st.yyinput, st.yylimit, want)
st.yylimit += nread
st.yyinput.writeUInt8(0, st.yylimit) // sentinel
return READY
}
function lex(yyrecord) {
loop: while (true) {
yyrecord.token = yyrecord.yycursor
/*!re2c
re2c:api = record;
re2c:YYPEEK = "readUInt8";
re2c:YYFILL = "return WAITING";
re2c:eof = 0;
packet = [a-z]+[;];
* { return BAD_PACKET }
$ { return END }
packet { yyrecord.received += 1; continue loop }
*/
}
}
function test(packets, expect) {
// Emulate a "pipe" by opening the same file for reading and writing.
let fname = "input"
let fw = fs.openSync(fname, 'w');
let fr = fs.openSync(fname, 'r');
// Init lexer state.
let limit = BUFSIZE - 1 // exclude terminating null
let st = {
file: fr,
yyinput: Buffer.alloc(BUFSIZE),
yylimit: limit,
yycursor: limit,
yymarker: limit,
token: limit,
yystate: -1,
received: 0
}
// 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.
let send = 0
let status
loop: while (true) {
status = lex(st)
if (status == END) {
log("done: got", st.received, "packets")
break loop
} else if (status == WAITING) {
log("waiting...");
if (send < packets.length) {
log("sent packet", send, packets[send])
fs.writeFileSync(fw, packets[send])
send += 1
}
status = fill(st)
log("queue:", st.yyinput.toString())
if (status == BIG_PACKET) {
log("error: packet too big")
break loop
}
if (status != READY) throw "expected READY"
} else {
if (status != BAD_PACKET) throw "expected BAD_PACKET"
log("error: ill-formed packet")
break loop
}
}
// Check results.
if (status != expect) throw "unexpected status"
if (status == END && st.received != send) "unexpected packet count"
// Cleanup.
fs.unlinkSync(fname, function(err){ if (err) throw err; })
}
function main() {
test([], END)
test(["zero;", "one;", "two;", "three;", "four;"], END)
test(["zer0;"], BAD_PACKET)
test(["goooooooooogle;"], BIG_PACKET)
}
main()
بلوکهای قابل استفادهٔ مجدد با قالب /*!rules:re2c[:<name>] ... */ یا %{rules[:<name>] ... %} میتوانند به هر تعداد بار دوباره استفاده شده و با دیگر بلوکهای re2js ترکیب شوند. مقدار <name> اختیاری است. یک بلوک rules میتواند در یک بلوک یا دستورالعمل use بهکار رود. کد مربوط به یک بلوک rules در هر نقطه از استفاده تولید میشود.
بلوکهای use با /*!use:re2c[:<name>] ... */ یا %{use[:<name>] ... %} تعریف میشوند. مقدار <name> اختیاری است: اگر مشخص نشده باشد، بلوک rules مرتبط جدیدترین مورد خواهد بود (خواه نامدار باشد یا بینام). یک بلوک use میتواند تعاریف نامدار، پیکربندیها و قواعد ویژهٔ خودش را اضافه کند. یک مورد کاربرد مهم برای بلوکهای use، تحلیلگر لغویای است که از چندین کدگذاری ورودی پشتیبانی میکند: یک بلوک rules یکسان چندین بار با پیکربندیهای مختص هر کدگذاری استفاده میشود (مثال زیر را ببینید).
دستورالعمل درونبلوکیِ !use:<name>; میتواند از داخل یک بلوک re2js استفاده شود. این دستور، بلوک ارجاعشدهٔ <name> را در بلوک فعلی ادغام میکند. اگر برخی از قوانین و پیکربندیهای ادغامشده با موارد تعریفشدهٔ پیشین تداخل داشته باشند، تعارضها به روش معمول حل میشوند: نخستین قاعده اولویت دارد و آخرین پیکربندی بر موارد پیشین بازنویسی میشود. یک استثنا، قواعد ویژهٔ *، $ و (در حالت شرطی) <!> هستند که در آنها تعریف محلی بلوک بر هر تعریف موروثی اولویت دارد. دستورالعمل use امکان ترکیب بلوکهای مختلف re2js را با یکدیگر در یک بلوک واحد فراهم میسازد (مثال زیر را ببینید).
بلوکهای نامدار و دستورالعمل use درونبلوکی در نگارش 2.2 از re2js اضافه شدند. از آن نسخه به بعد، بلوکهای قابل استفادهٔ مجدد بهطور پیشفرض مجاز هستند (نیازی به گزینهٔ خاصی نیست). پیش از نسخه 2.2، حالت استفادهٔ مجدد با گزینهٔ -r --reusable فعال میشد. پیش از نسخه 1.2، بلوکهای قابل استفادهٔ مجدد نمیتوانستند با بلوکهای عادی ترکیب شوند.
مثالی از دستورالعمل !use
// re2js $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.
const COLOR = 1
const FISH = 2
const DUNNO = 3
/*!rules:re2c:colors
* { throw "ah" }
"red" | "salmon" | "magenta" { return COLOR }
*/
/*!rules:re2c:fish
* { throw "oh" }
"haddock" | "salmon" | "eel" { return FISH }
*/
function lex(yyinput) {
let yycursor = 0
/*!re2c
re2c:yyfill:enable = 0;
!use:fish;
!use:colors;
* { return DUNNO } // overrides inherited '*' rules
*/
}
function test(s, n) { if (lex(s) != n) throw "error!"; }
test("salmon", FISH)
test("what?", DUNNO)
مثالی از یک بلوک /*!use:re2c ... */
// re2js $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 = "at";
"∀x ∃y" { return yycursor }
* { return null }
*/
function lex_utf8(yyinput) {
let yycursor = 0
/*!use:re2c
re2c:encoding:utf8 = 1;
*/
}
function lex_utf32(yyinput) {
let yycursor = 0
/*!use:re2c
re2c:encoding:utf32 = 1;
*/
}
function test(f, s) {
if (f(s) != s.length) throw "error!"
}
test(lex_utf8, [0xe2, 0x88, 0x80, 0x78, 0x20, 0xe2, 0x88, 0x83, 0x79])
test(lex_utf32, [0x2200, 0x78, 0x20, 0x2203, 0x79])
ابزار re2js دارای دو گزینه برای استخراج زیرتطبیق است.
- Tags
- گزینهٔ نخست، استفاده از tagهای مستقل به فرم @stag یا #mtag است، که در آن stag و mtag نامهای دلخواه تعریفشده توسط کاربر هستند. برچسبها با گزینهٔ -T --tags یا پیکربندی re2c:tags = 1 فعال میشوند. از نظر معنایی، برچسبها نشانگرهای موقعیت هستند: آنها میتوانند در هر کجای یک عبارت باقاعده درج شوند و به موقعیت مربوطه (یا چندین موقعیت) در رشتهٔ ورودی متصل گردند. S-tagها به آخرین موقعیت منطبق متصل میشوند، و m-tagها به فهرستی از موقعیتها متصل میشوند (ممکن است در زیرعبارتهای تکرار استفاده شوند، جایی که یک موقعیت منفرد در عبارت باقاعده متناظر با چندین موقعیت در رشتهٔ ورودی است). همهٔ برچسبها باید توسط کاربر، یا بهصورت دستی و یا به کمک بلوکهای svars و mvars تعریف شوند. اگر بیش از یک روش برای تطبیق برچسبها با ورودی وجود داشته باشد، ابهام با استفاده از راهبرد ابهامزدایی چپترین حریصانه (leftmost greedy) حل میشود.
- Captures
- گزینهٔ دوم استفاده از گروههای تصاحب (capturing groups) است. آنها با گزینهٔ --captures یا پیکربندی re2c:captures = 1 فعال میشوند. دو نوع برای سیاستهای مختلف ابهامزدایی وجود دارد؛ --leftmost-captures (پیشفرض) برای سیاست چپترین حریصانه است، و --posix-captures برای سیاست طولانیترین تطابق POSIX است. در این حالت تمام زیرعبارتهای درون پرانتز به عنوان گروههای تصاحب در نظر گرفته میشوند، و میتوان از علامت تعجب برای مشخص کردن گروههای غیرتصاحبی استفاده کرد: (! ... ). با گزینهٔ --invert-captures یا پیکربندی re2c:invert-captures = 1 معنای علامت تعجب معکوس میشود. تعداد گروهها برای قاعدهٔ منطبقشده در متغیر yynmatch ذخیره میشود (کل عبارت باقاعده گروه شماره صفر است)، و نتایج زیرتطبیق در آرایهٔ yypmatch ذخیره میگردند. هر دو متغیر yynmatch و yypmatch باید توسط کاربر تعریف شوند، و اندازهٔ yypmatch باید حداقل [yynmatch * 2] باشد. از بلوک maxnmatch برای تعریف YYMAXNMATCH استفاده کنید؛ ثابتی که برابر با بیشترین مقدار yynmatch در بین تمام قواعد است.
- Captvars
- روش دیگر برای استفاده از گروههای تصاحب، گزینهٔ --captvars یا پیکربندی re2c:captvars = 1 است. تنها تفاوت با --captures در نحوهٔ ذخیره نتایج زیرتطبیق توسط کد تولیدشده است: به جای yynmatch و yypmatch، ابزار re2js متغیرهای yytl<k> و yytr<k> را برای k-امین گروه تصاحب تولید میکند (کاربر باید اینها را با استفاده از یک بلوک svars اعلان کند). تصاحبها با متغیرها از دو سیاست ابهامزدایی پشتیبانی میکنند: --leftmost-captvars یا re2c:leftmost-captvars = 1 برای سیاست چپترین حریصانه (پیشفرض) و --posix-captvars یا re2c:posix-captvars برای سیاست طولانیترین تطابق POSIX.
در پشت صحنه، همهٔ این گزینهها به برچسبها و Tagged Deterministic Finite Automata with Lookahead (اتوماتای متناهی معین برچسبدار با نگاه به جلو یا TDFA) ترجمه میشوند. ایدهٔ اصلی TDFA به حداقل رساندن بار اضافی (overhead) در استخراج زیرتطبیق است. در حالت نهایی، اگر هیچ برچسب یا تصاحبی در یک عبارت باقاعده وجود نداشته باشد، TDFA صرفاً یک DFA معمولی است. اگر تعداد برچسبها معقول باشد، بار اضافی به سختی قابل توجه است. TDFA تولیدشده از تعدادی متغیر برچسب استفاده میکند که مستقیماً به برچسبها نگاشت نمیشوند: یک متغیر منفرد ممکن است برای برچسبهای مختلف استفاده شود، و یک برچسب ممکن است برای نگهداری همهٔ مقادیر ممکن خود به چندین متغیر نیاز داشته باشد. در نهایت ابهام برطرف میشود و تنها یک متغیر نهایی به ازای هر برچسب باقی میماند. متغیرهای برچسب باید با استفاده از بلوکهای stags یا mtags تعریف شوند. اگر وضعیت تحلیلگر لغوی (lexer state) ذخیره میشود، متغیرهای برچسب باید بخشی از آن باشند. آنها همچنین باید توسط YYFILL بهروزرسانی شوند.
S-tagها از عملیاتهای زیر پشتیبانی میکنند:
- ذخیره موقعیت ورودی در یک s-tag: دستور t = YYCURSOR با API اشارهگر C یا عملیات تعریفشده توسط کاربر YYSTAGP(t) با API عمومی
- ذخیره مقدار پیشفرض در یک s-tag: دستور t = NULL با API اشارهگر C یا عملیات تعریفشده توسط کاربر YYSTAGN(t) با API عمومی
- کپی کردن یک s-tag در دیگری: t1 = t2
M-tagها از عملیاتهای زیر پشتیبانی میکنند:
- الحاق موقعیت ورودی به یک m-tag: عملیات تعریفشده توسط کاربر YYMTAGP(t) با هر دو API پیشفرض و عمومی
- الحاق مقدار پیشفرض به یک m-tag: عملیات تعریفشده توسط کاربر YYMTAGN(t) با هر دو API پیشفرض و عمومی
- کپی کردن یک m-tag در دیگری: t1 = t2
S-tagها میتوانند به عنوان مقادیر اسکالر (اشارهگرها یا آفستها) پیادهسازی شوند. M-tagها به نمایش پیچیدهتری نیاز دارند، زیرا باید دنبالهای از مقادیر برچسب را ذخیره کنند. سادهترین و ناکارآمدترین نمایش یک m-tag، یک لیست (آرایه، وکتور) از مقادیر برچسب است؛ نمایشی کارآمدتر، ذخیرهٔ تمام m-tagها در یک درخت پیشوندی (prefix-tree) است که به صورت آرایهای از گرههای (v, p) نمایش داده میشود، که در آن v مقدار برچسب و p اشارهگری به گره والد است.
در اینجا یک مثال ساده از استفاده از s-tagها برای تجزیهٔ نگارشهای معنایی (semantic versions) شامل سه جزء عددی آورده شده است: major، minor، patch (مورد آخر اختیاری است). برای یک مثال پیچیدهتر که از YYFILL استفاده میکند، به بخش زیر مراجعه کنید.
// re2js $INPUT -o $OUTPUT
const assert = require('assert');
function parse(yyinput) {
let yycursor = 0
// Final tag variables available in semantic action.
/*!svars:re2c format = "let @@\n"; */
// Intermediate tag variables used by the lexer (must be autogenerated).
/*!stags:re2c format = "let @@\n"; */
/*!re2c
re2c:yyfill:enable = 0;
re2c:tags = 1;
num = [0-9]+;
@t1 num @t2 "." @t3 num @t4 ("." @t5 num)? [\x00] {
return {
major: Number(yyinput.substring(t1, t2)),
minor: Number(yyinput.substring(t3, t4)),
patch: t5 == -1 ? 0 : Number(yyinput.substring(t5, yycursor - 1))
}
}
* { return null }
*/
}
assert.deepEqual(parse("23.34\0"), {major: 23, minor: 34, patch: 0})
assert.deepEqual(parse("1.2.99999\0"), {major: 1, minor: 2, patch: 99999})
assert.deepEqual(parse("1.a\0"), null)
در اینجا مثال پیچیدهتری از استفاده از s-tagها به همراه YYFILL برای تجزیهٔ فایلی با نگارشهای معنایی جداشده با خط جدید آمده است. متغیرهای برچسب بخشی از وضعیت تحلیلگر لغوی هستند، و درست مانند سایر موقعیتهای ورودی در YYFILL تنظیم میشوند. توجه داشته باشید که این کار برای s-tagها ضروری است زیرا مقادیر آنها پس از جابهجایی محتویات بافر باطل میشوند. ممکن است در یک پیادهسازی سفارشی که در آن متغیرهای برچسب به جای بافر، آفستهای نسبت به ابتدای رشتهٔ ورودی را ذخیره میکنند (که ممکن است برای m-tagها اینگونه باشد) ضروری نباشد.
// re2js $INPUT -o $OUTPUT
const assert = require('assert');
const fs = require('fs')
const BUFSIZE = 4096
const OK = 0
const EOF = 1
const LONG_LEXEME = 2
function fill(st) {
if (st.eof) return EOF
// Error: lexeme too long. In real life could reallocate a larger buffer.
if (st.token < 1) return LONG_LEXEME
// Shift buffer contents (discard everything up to the current token).
st.yyinput.copy(st.yyinput, 0, st.token, st.yylimit)
st.yycursor -= st.token;
st.yymarker -= st.token;
st.yylimit -= st.token;
/*!stags:re2c format = "if (st.@@ != -1) st.@@ -= st.token\n"; */
st.token = 0;
// Read a new chunk of data from file and append it to `yyinput`.
let want = BUFSIZE - st.yylimit - 1 // -1 for sentinel
let nread = fs.readSync(st.file, st.yyinput, st.yylimit, want)
st.eof = nread < want // end of file?
st.yylimit += nread
st.yyinput.writeUInt8(0, st.yylimit) // sentinel
return OK
}
function lex(st) {
let vers = []
loop: while (true) {
st.token = st.yycursor
// Final tag variables available in semantic action.
/*!svars:re2c format = "let @@\n"; */
/*!re2c
re2c:api = record;
re2c:yyrecord = st;
re2c:YYPEEK = "readUInt8";
re2c:YYFILL = "fill(st) == OK";
re2c:eof = 0;
re2c:tags = 1;
num = [0-9]+;
num @t1 "." @t2 num @t3 ("." @t4 num)? [\n] {
vers.push({
major: Number(st.yyinput.subarray(st.token, t1)),
minor: Number(st.yyinput.subarray(t2, t3)),
patch: t4 == -1 ? 0 : Number(st.yyinput.subarray(t4, st.yycursor - 1))
})
continue loop
}
$ { return vers }
* { return null }
*/
}
}
function main() {
let fname = "input"
// Create input file.
let content = "1.22.333\n".repeat(BUFSIZE)
fs.writeFileSync(fname, content, function(err) { if (err) throw err; })
// Init lexer state.
let limit = BUFSIZE - 1 // exclude terminating null
let st = {
file: fs.openSync(fname, 'r'),
yyinput: Buffer.alloc(BUFSIZE),
yylimit: limit,
yycursor: limit,
yymarker: limit,
token: limit,
// Intermediate tag variables used by the lexer (must be autogenerated).
/*!stags:re2c format = "@@: -1,\n"; */
eof: false
}
// Run lexer on the prepared file.
assert.deepEqual(lex(st), Array(BUFSIZE).fill({major: 1, minor: 22, patch: 333}))
// Cleanup.
fs.unlink(fname, function(err){ if (err) throw err; })
}
main()
در اینجا مثالی از استفاده از گروههای تصاحب برای تجزیهٔ نگارشهای معنایی آورده شده است.
// re2js $INPUT -o $OUTPUT
const assert = require('assert');
function parse(yyinput) {
let yycursor = 0
// Final tag variables available in semantic action.
/*!svars:re2c format = "let @@\n"; */
// Intermediate tag variables used by the lexer (must be autogenerated).
/*!stags:re2c format = "let @@\n"; */
/*!re2c
re2c:yyfill:enable = 0;
re2c:captvars = 1;
num = [0-9]+;
(num) "." (num) ("." num)? [\x00] {
return {
major: Number(yyinput.substring(yytl1, yytr1)),
minor: Number(yyinput.substring(yytl2, yytr2)),
patch: yytl3 == -1 ? 0 : Number(yyinput.substring(yytl3 + 1, yytr3))
}
}
* { return null }
*/
}
assert.deepEqual(parse("23.34\0"), {major: 23, minor: 34, patch: 0})
assert.deepEqual(parse("1.2.99999\0"), {major: 1, minor: 2, patch: 99999})
assert.deepEqual(parse("1.a\0"), null)
در اینجا مثالی از استفاده از m-tagها برای تجزیهٔ یک نگارش با تعداد متغیری از اجزاء آمده است. متغیرهای برچسب در یک ترای (trie) ذخیره میشوند.
// re2js $INPUT -o $OUTPUT
const assert = require('assert')
function parse(yyinput) {
let yycursor = 0
// Final tag variables available in semantic action.
/*!svars:re2c format = "let @@\n"; */
/*!mvars:re2c format = "let @@\n"; */
// Intermediate tag variables used by the lexer (must be autogenerated).
/*!stags:re2c format = "let @@\n"; */
/*!mtags:re2c format = "let @@ = []\n"; */
/*!re2c
re2c:YYMTAGP = "@@.push(yycursor)";
re2c:YYMTAGN = ""; // do nothing
re2c:yyfill:enable = 0;
re2c:tags = 1;
num = [0-9]+;
@t1 num @t2 ("." #t3 num #t4)* [\x00] {
let vers = [Number(yyinput.substring(t1, t2))]
for (let i = 0; i < t3.length; ++i) {
vers.push(Number(yyinput.substring(t3[i], t4[i])))
}
return vers
}
* { return null }
*/
}
assert.deepEqual(parse("1\0"), [1])
assert.deepEqual(parse("1.2.3.4.5.6.7\0"), [1, 2, 3, 4, 5, 6, 7])
assert.deepEqual(parse("1.2.\0"), null)
درک تفاوت بین code pointها و code unitها ضروری است. یک code point شناسهٔ عددی یک نماد است. یک code unit کوچکترین واحد ذخیرهسازی در متن کدگذاریشده است. یک code point منفرد ممکن است با یک یا چند code unit نمایش داده شود. در یک کدگذاری با طول ثابت، همهٔ code pointها با تعداد یکسانی از code unitها نمایش مییابند. در یک کدگذاری با طول متغیر، code pointها ممکن است با تعداد متفاوتی از code unitها نمایش داده شوند. توجه داشته باشید که قاعدهٔ «هر چیزی» ("any") یعنی [^] با هر code point مطابقت دارد، اما لزوماً با هر code unit مطابقت ندارد (تنها راه برای تطبیق با هر code unit بدون توجه به نوع کدگذاری، قاعدهٔ پیشفرض * است). تحلیلگر لغوی تولیدشده با جریانی از code unitها کار میکند: yych یک code unit را ذخیره میکند و YYCTYPE نوع code unit است. از سوی دیگر، عبارات باقاعده بر حسب code pointها مشخص میشوند. هنگامی که re2js عبارات باقاعده را به اتوماتا کامپایل میکند، code pointها را به code unitها ترجمه میکند. این عموماً یک نگاشت ساده نیست: در کدگذاریهای با طول متغیر، یک بازهٔ واحد از code point ممکن است به گراف پیچیدهای از code unitها ترجمه شود. کدگذاریهای زیر پشتیبانی میشوند:
- ASCII (بهطور پیشفرض فعال است). یک کدگذاری با طول ثابت با فضای کد [0-255] و code pointها و code unitهای ۱ بایتی است.
- EBCDIC (فعالسازی با --ebcdic یا re2c:encoding:ebcdic). یک کدگذاری با طول ثابت با فضای کد [0-255] و code pointها و code unitهای ۱ بایتی است.
- UCS2 (فعالسازی با --ucs2 یا re2c:encoding:ucs2). یک کدگذاری با طول ثابت با فضای کد [0-0xFFFF] و code pointها و code unitهای ۲ بایتی است.
- UTF8 (فعالسازی با --utf8 یا re2c:encoding:utf8). یک کدگذاری یونیکد با طول متغیر است. اندازهٔ هر code unit برابر ۱ بایت است. code pointها با ۱ تا ۴ code unit نمایش داده میشوند.
- UTF16 (فعالسازی با --utf16 یا re2c:encoding:utf16). یک کدگذاری یونیکد با طول متغیر است. اندازهٔ هر code unit برابر ۲ بایت است. code pointها با ۱ تا ۲ code unit نمایش داده میشوند.
- UTF32 (فعالسازی با --utf32 یا re2c:encoding:utf32). یک کدگذاری یونیکد با طول ثابت با فضای کد [0-0x10FFFF] و code pointها و code unitهای ۴ بایتی است.
فایلهای ضمیمهٔ include/unicode_blocks.re، include/unicode_categories.re و include/unicode_properties.re تعاریف re2js را برای بلوکها، دستهبندیها و ویژگیهای توصیفشده در استاندارد یونیکد ارائه میدهند.
گزینهٔ --input-encoding کدگذاری فایل مبدأ را مشخص میکند که میتواند برای فعالسازی مقادیر لفظی (literal) یونیکد در عبارات باقاعده استفاده شود. برای مثال، --input-encoding utf8 به re2js اعلام میکند که فایل مبدأ با UTF8 کدگذاری شده است (این گزینه با --utf8 که کدگذاری متن ورودی را تعیین میکند، تفاوت دارد). گزینهٔ --encoding-policy نحوهٔ مدیریت جانشینهای یونیکد (code pointها در بازهٔ [0xD800-0xDFFF]) را توسط re2js تعیین میکند.
در زیر مثالی از یک تحلیلگر لغوی برای شناسههای یونیکد کدگذاریشده با UTF8 آمده است.
// re2js $INPUT -o $OUTPUT --utf8 -s
/*!include:re2c "unicode_categories.re" */
function lex(yyinput) {
let yycursor = 0
/*!re2c
re2c:yyfill:enable = 0;
// Simplified "Unicode Identifier and Pattern Syntax"
// (see https://unicode.org/reports/tr31)
id_start = L | Nl | [$_];
id_continue = id_start | Mn | Mc | Nd | Pc | [\u200D\u05F3];
identifier = id_start id_continue*;
identifier { return true }
* { return false }
*/
}
if (!lex("_Ыдентификатор\0")) throw "error!"
فایلهای ضمیمه (Include files)
re2js امکان ضمیمهکردن فایلهای دیگر را با استفاده از بلوکی با ساختار /*!include:re2c FILE */ یا %{include FILE %}، یا یک دستور درونبلوکی !include FILE ; فراهم میکند که در آن FILE مسیری به فایل مورد نظر برای ضمیمهشدن است. re2js فایلهای ضمیمه را در دایرکتوری فایلی که شامل دستور است و همچنین در مکانهای ضمیمهای که میتوان با گزینهٔ -I تعیین کرد، جستجو میکند. بلوکها/دستورهای ضمیمهسازی در re2js به همان شیوهٔ #include در C/C++ عمل میکنند: محتویات FILE عیناً در جای بلوک/دستور جایگذاری میشوند. فایلهای ضمیمه میتوانند خود شامل فایلهای ضمیمهٔ دیگری باشند. برای ردیابی وابستگیهای ساخت فایل خروجی به فایلهای ضمیمه، از گزینهٔ --depfile استفاده کنید. re2js چند فایل ضمیمهٔ از پیشتعریفشده را فراهم میکند که در زیردایرکتوری include/ از پروژه یافت میشوند. این فایلها شامل تعاریفی هستند که ممکن است برای پروژههای دیگر مفید باشند (مانند دستهبندیهای یونیکد) و ساختاری شبیه به یک کتابخانهٔ استاندارد برای re2js تشکیل میدهند. در ادامه مثالی از استفاده از فایلهای ضمیمه آورده شده است.
فایل ضمیمهٔ ۱ (definitions.js)
const INT = 1
const FLOAT = 2
const NAN = 3
/*!re2c
number = [1-9][0-9]*;
*/
// floating-point numbers
frac = [0-9]* "." [0-9]+ | [0-9]+ ".";
exp = 'e' [+-]? [0-9]+;
float = frac exp? | [0-9]+ exp;
float { return FLOAT }
فایل ورودی
// re2js $INPUT -o $OUTPUT
/*!include:re2c "definitions.js" */
function lex(yyinput) {
let yycursor = 0
/*!re2c
re2c:yyfill:enable = 0;
* { return NAN }
number { return INT }
!include "extra_rules.re.inc";
*/
}
function test(s, n) {
if (lex(s) != n) throw "error!"
}
test("123\0", INT)
test("123.4567\0", FLOAT)
فایلهای هدر
ابزار re2js این امکان را فراهم میکند تا با استفاده از گزینهٔ --header یا پیکربندی re2c:header و جفتبلوکهایی به فرم /*!header:re2c:on*/ و /*!header:re2c:off*/، یا %{header:on%} و %{header:off%}، بتوان از فایل ورودی .re یک فایل هدر تولید کرد. نخستین بلوک آغاز فایل هدر و دومین بلوک پایان آن را مشخص میکند. تمام محتوای میان این بلوکها توسط re2js پردازش شده و کد تولیدشده در فایلی که با گزینهٔ --header یا پیکربندی re2c:header تعیین شده نوشته میشود (یا در صورت عدم استفاده از گزینه یا پیکربندی، در stdout نوشته خواهد شد). فایل هدر خودکار تولیدشده ممکن است در مواردی نیاز باشد که re2js برای تولید تعاریفی به کار میرود که باید از سایر واحدهای ترجمه (translation units) قابل مشاهده باشند.
در اینجا مثالی از تولید یک فایل هدر آورده شده است که شامل تعریف وضعیت لکسر به همراه متغیرهای برچسب (tag variables) است (تعداد متغیرها به گرامر منظم وابسته بوده و برای برنامهنویس نامشخص است).
فایل ورودی
// re2js $INPUT -o $OUTPUT --header lexer/state.js
let state = require('./lexer/state.js');
/*!header:re2c:on*/
exports.mk_state = function(str) {
return {
yyinput: str,
/*!stags:re2c format = "@@: 0,\n"; */
yycursor: 0
}
}
/*!header:re2c:off*/
function lex(yyrecord) {
let t
/*!re2c
re2c:api = record;
re2c:tags = 1;
re2c:yyfill:enable = 0;
re2c:header = "lexer/state.js";
[a]* @t [b]* { return t }
*/
}
if (lex(state.mk_state("ab\0")) != 1) {
throw "error!"
}
فایل هدر
// Generated by re2c
exports.mk_state = function(str) {
return {
yyinput: str,
yyt1: 0,
yycursor: 0
}
}
با گزینه -S, --skeleton، ابزار re2js تمام کدهای غیر re2js را نادیده میگیرد و یک برنامه خودکفای C تولید میکند که میتواند در ادامه کامپایل و اجرا شود. این برنامه از کد تحلیلگر لغوی (lexer) و دادههای ورودی تشکیل شده است. به ازای هر DFA ساختهشده (بلوک یا شرط)، re2js یک تحلیلگر لغوی مستقل و دو فایل تولید میکند: یک فایل .input حاوی رشتههای مشتقشده از DFA و یک فایل .keys حاوی نتایج تطبیق مورد انتظار. برنامه هر تحلیلگر لغوی را روی فایل .input متناظر اجرا کرده و نتایج را با مقادیر مورد انتظار مقایسه میکند. برنامههای اسکلتی به چند دلیل بسیار مفید هستند:
- آنها میتوانند درستی بهینهسازیهای مختلف re2js را بررسی کنند (دادهها در مراحل اولیه فرایند، پیش از اعمال هرگونه تبدیل روی DFA تولید میشوند).
- تولید مجموعهای از دادههای ورودی با پوشش مناسب میتواند هم برای آزمون و هم برای سنجش کارایی (benchmarking) مفید باشد.
- تولید برنامههای اجرایی خودکفا امکان دستیابی به موارد آزمون کمینهشده را فراهم میسازد (چرا که کد اصلی ممکن است بسیار بزرگ بوده یا وابستگیهای زیادی داشته باشد).
دشواریِ تولید دادههای ورودی در این است که به جز در سادهترین حالتها، تعداد رشتههای ورودیِ ممکن بسیار زیاد است (حتی اگر طول رشته محدود باشد). ابزار re2js این مشکل را با تولید تعداد کافی از رشتهها برای پوشش دادن تقریباً تمام گذارهای DFA برطرف میکند. این ابزار از الگوریتم زیر استفاده میکند: ابتدا اسکلتی از DFA میسازد. برای کدگذاریهایی با اندازه واحد کد ۱ بایتی (مانند ASCII، UTF-8 و EBCDIC)، این اسکلت صرفاً یک کپی دقیق از DFA اصلی است. برای کدگذاریهایی با واحدهای کد چندبایتی، اسکلت کپیای از DFA است که برخی از گذارهای آن حذف شدهاند: به این صورت که re2js حداکثر ۲۵۶ واحد کد را برای هر بازه پیوسته مجزا که متناظر با یک گذار DFA است انتخاب میکند. مقادیر انتخابشده به طور یکنواخت توزیع شده و شامل کرانهای بازه نیز هستند. ابزار re2js به جای تلاش برای پوشش تمام مسیرهای ممکن در اسکلت (که کاری ناممکن است)، مسیرهای کافی برای پوشش تمام گذارهای اسکلت را تولید میکند تا بدین ترتیب پرشهای شرطی متناظر در تحلیلگر لغوی فراخوانی شوند. پیادهسازی این الگوریتم به حدود ۱ گیگابایت از گذارها محدود شده و مقدار ثابتی از حافظه را مصرف میکند (re2js به محض تولید دادهها، آنها را در فایل مینویسد).
مصورسازی و اشکالزدایی (Visualization and debug)
با گزینه -D, --emit-dot، ابزار re2js هیچ کدی تولید نمیکند؛ در عوض، 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)
ابزار re2js در ابتدا توسط 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.