| RE2PY(1) | RE2PY(1) |
نام (NAME)
re2py - تولید تحلیلگرهای واژگانی سریع برای Python
خلاصه دستور (SYNOPSIS)
re2py [ OPTIONS ] [ WARNINGS ] INPUT
ورودی میتواند یک فایل یا - برای stdin باشد.
مقدمه (INTRODUCTION)
ابزار re2py به عنوان یک پیشپردازنده کار میکند. فایل ورودی را (که معمولاً برنامهای در Python است، اما میتواند هر چیز دیگری باشد) خوانده و به دنبال بلوکهایی از کد میگردد که درون نشانگرهای شروع/پایان با قالب خاص قرار گرفتهاند. متن خارج از این بلوکها عیناً به فایل خروجی کپی میشود. محتوای این بلوکها توسط re2py پردازش میشود. این ابزار آنها را به کد Python ترجمه کرده و کد تولیدشده را به جای بلوک مربوطه در خروجی قرار میدهد.
در اینجا نمونهای از یک برنامه کوچک آورده شده است که بررسی میکند آیا رشته دادهشده شامل یک عدد دهدهی است یا خیر:
# re2py $INPUT -o $OUTPUT
def lex(yyinput):
yycursor = 0
%{
re2c:yyfill:enable = 0;
re2c:indent:top = 1;
[1-9][0-9]* { return True }
* { return False }
%}
assert lex(b"1234\0")
در خروجی، re2py بلوک میانی را با کد تولیدشده جایگزین کرده است:
# Generated by re2py
# re2py $INPUT -o $OUTPUT
def lex(yyinput):
yycursor = 0
yystate = 0
while True:
match yystate:
case 0:
yych = yyinput[yycursor]
yycursor += 1
if yych <= 0x30:
yystate = 1
continue
if yych <= 0x39:
yystate = 2
continue
yystate = 1
continue
case 1:
return False
case 2:
yych = yyinput[yycursor]
if yych <= 0x2F:
yystate = 3
continue
if yych <= 0x39:
yycursor += 1
yystate = 2
continue
yystate = 3
continue
case 3:
return True
case _:
raise "internal lexer error"
assert lex(b"1234\0")
یک برنامه re2py شامل توالیای از بلوکها ترکیبشده با کد به زبان مقصد است. یک بلوک ممکن است شامل تعاریف، پیکربندیها، قواعد، کنشها و رهنمودها به هر ترتیبی باشد:
- name = regular-expression ;
- یک تعریف، name را به regular-expression پیوند میدهد. نامها میتوانند شامل نویسههای الفبایی-عددی و خط زیر باشند. بخش عبارتهای منظم مروری بر نحو re2py برای عبارتهای منظم ارائه میدهد. پس از تعریف، میتوان از name در سایر عبارتهای منظم و قواعد استفاده کرد. بازگشت در تعاریف نامگذاریشده مجاز نیست و هر نام باید پیش از استفاده تعریف شود. یک بلوک، تعاریف نامگذاریشده را از حوزه سراسری به ارث میبرد. تعریف مجدد نامی که در حوزه جاری وجود دارد یک خطا است.
- configuration = value ;
- یک پیکربندی امکان تغییر رفتار re2py و سفارشیسازی کد تولیدشده را فراهم میکند. برای فهرست کامل پیکربندیهای پشتیبانیشده توسط re2py به بخش پیکربندیها مراجعه کنید. بسته به یک پیکربندی خاص، value میتواند یک کلمه کلیدی، یک عدد صحیح غیرمنفی یا یک رشته تکخطی باشد که باید در علامت نقلقول تکی یا دوتایی قرار گیرد، مگر اینکه از نویسههای الفبایی-عددی تشکیل شده باشد. یک بلوک، پیکربندیها را از حوزه سراسری به ارث میبرد و میتواند آنها را دوباره تعریف کند یا موارد جدیدی اضافه نماید. پیکربندیهای تعریفشده درون یک بلوک بر کل بلوک تأثیر میگذارند، حتی اگر در انتهای آن ظاهر شوند.
- regular-expression code
- یک قاعده، regular-expression را به کنش معنایی آن متصل میکند (یک بلوک کد درون آکولاد، یا یک بلوک کد که با := شروع شده و به یک خط جدید ختم میشود که پس از آن هر نویسه غیرفاصلهای قرار میگیرد). اگر regular-expression مطابقت پیدا کند، code مرتبط اجرا میشود. اگر چندین قاعده مطابقت داشته باشند، طولانیترین تطابق اولویت دارد. اگر چندین قاعده با همان رشته تطابق داشته باشند، اولین قاعده اولویت دارد. دو قاعده ویژه وجود دارد: قاعده پیشفرض * و قاعده پایان ورودی $. قاعده پیشفرض همیشه باید تعریف شود؛ این قاعده صرفنظر از جایگاهش در بلوک، کمترین اولویت را دارد و با هر واحد کد مطابقت پیدا میکند (لزوماً یک نویسه معتبر نیست، به بخش پشتیبانی از رمزگذاری مراجعه کنید). قاعده پایان ورودی در صورتی باید تعریف شود که روش متناظر برای مدیریت پایان ورودی استفاده شده باشد. با شرایط شروع، قواعد نحو پیچیدهتری خواهند داشت.
- !action code
- یک کنش یک بلوک تعریفشده توسط کاربر از code را به یک مکان خاص در ماشین حالت متناهی تولیدشده پیوند میدهد (به همان روشی که کنشهای معنایی کد را به حالتهای نهایی پیوند میدهند). برای فهرست کامل کنشهای از پیش تعریفشده به بخش کنشها مراجعه کنید.
- !directive ;
- یک رهنمود یکی از دستورات ویژه از پیش تعریفشده است. هر رهنمود هدف منحصربهفردی دارد. برای جزئیات به بخش رهنمودها مراجعه کنید.
بلوکها (Blocks)
نشانگرهای شروع و پایان بلوک یا /*!re2c و */، یا %{ و %} هستند (از هر دو سبک پشتیبانی میشود). از نسخه 2.2، بلوکها میتوانند نامهای اختیاری داشته باشند که امکان ارجاع به آنها را در سایر بلوکها فراهم میکند. انواع مختلفی از بلوکها وجود دارد:
- /*!re2c[:<name>] ... */ یا %{[:<name>] ... %}
- یک بلوک سراسری شامل تعاریف، پیکربندیها، قواعد و رهنمودها است. re2py عبارتهای منظم مرتبط با هر قاعده را به یک اتوماتون متناهی قطعی کامپایل میکند، آن را به شکل پرشهای شرطی در زبان مقصد کدگذاری کرده و بلوک را با کد تولیدشده جایگزین مینماید. نامها و پیکربندیهای تعریفشده در یک بلوک سراسری به حوزه سراسری اضافه میشوند و برای بلوکهای پس از آن قابل مشاهده خواهند بود. در آغاز برنامه، حوزه سراسری با گزینههای خط فرمان مقداردهی اولیه میشود.
- /*!local:re2c[:<name>] ... */ یا %{local[:<name>] ... %}
- یک بلوک محلی مانند یک بلوک سراسری است، اما نامها و پیکربندیهای درون آن حوزه محلی دارند (بر سایر بلوکها تأثیری نمیگذارند).
- /*!rules:re2c[:<name>] ... */ یا %{rules[:<name>] ... %}
- یک بلوک قواعد شبیه یک بلوک محلی است، اما خودش کدی تولید نمیکند و تعریفی را هم به حوزه سراسری اضافه نمینماید -- این بلوک برای استفاده مجدد در سایر بلوکها در نظر گرفته شده است. این روشی برای اشتراکگذاری کد است (جزئیات بیشتر در بخش بلوکهای قابل استفاده مجدد). پیش از نسخه 2.2 re2py، بلوکهای قواعد به گزینه -r --reusable نیاز داشتند.
- /*!use:re2c[:<name>] ... */ یا %{use[:<name>] ... %}
- یک بلوک استفاده که به یک بلوک قواعد از پیش تعریفشده ارجاع میدهد. اگر نام مشخص شده باشد، re2py به دنبال بلوک قواعدی با این نام میگردد. در غیر این صورت، از جدیدترین بلوک قواعد استفاده میشود (خواه نامگذاریشده باشد یا بدون نام). یک بلوک use میتواند تعاریف، پیکربندیها و قواعد خاص خود را بیفزاید که به موارد بلوک قواعد ارجاعشده اضافه میشوند. پیش از نسخه 2.2 re2py، بلوکهای use به گزینه -r --reusable نیاز داشتند.
- /*!max:re2c[:<name1>[:<name2>...]] ... */ یا %{max[:<name1>[:<name2>...]] ... %}
- بلوکی که تعریف YYMAXFILL را تولید میکند. یک فهرست اختیاری از نام بلوکها مشخص میکند که هنگام محاسبه مقدار YYMAXFILL کدام بلوکها باید لحاظ شوند (اگر فهرست خالی باشد، همه بلوکها لحاظ میشوند). بهطور پیشفرض، کد تولیدشده یک تعریف ماکرو برای C (#define YYMAXFILL <n>) یا یک متغیر سراسری برای Go (var YYMAXFILL int = <n>) است. این مقدار را میتوان با پیکربندی اختیاری format سفارشی کرد که یک رشته الگو را مشخص میکند که در آن @@{max} (یا به اختصار @@) با مقدار عددی YYMAXFILL جایگزین میشود.
- /*!maxnmatch:re2c[:<name1>[:<name2>...]] ... */ یا %{maxnmatch[:<name1>[:<name2>...]] ... %}
- بلوکی که تعریف YYMAXNMATCH را تولید میکند (به گزینه -P --posix-captures نیاز دارد). یک فهرست اختیاری از نام بلوکها مشخص میکند که هنگام محاسبه مقدار YYMAXNMATCH کدام بلوکها باید لحاظ شوند (اگر فهرست خالی باشد، همه بلوکها لحاظ میشوند). بهطور پیشفرض، کد تولیدشده یک تعریف ماکرو برای C (#define YYMAXNMATCH <n>) یا یک متغیر سراسری برای Go (var YYMAXNMATCH int = <n>) است. این مقدار را میتوان با پیکربندی اختیاری format سفارشی کرد که یک رشته الگو را مشخص میکند که در آن @@{max} (یا به اختصار @@) با مقدار عددی YYMAXNMATCH جایگزین میشود.
- /*!stags:re2c[:<name1>[:<name2>...]] ... */، /*!mtags:re2c[:<name1>[:<name2>...]] ... */ یا %{stags[:<name1>[:<name2>...]] ... %}، %{mtags[:<name1>[:<name2>...]] ... %{
- بلوکهایی که یک قطعه کد الگو را مشخص میکنند که برای هر متغیر s-tag/m-tag تولیدشده توسط re2py گسترش مییابد. یک فهرست اختیاری از نام بلوکها مشخص میکند که هنگام محاسبه مجموعه متغیرهای برچسب کدام بلوکها باید لحاظ شوند (اگر فهرست خالی باشد، همه بلوکها لحاظ میشوند). دو پیکربندی اختیاری وجود دارد: format و separator. پیکربندی format یک رشته الگو را مشخص میکند که در آن @@{tag} (یا به اختصار @@) با نام هر متغیر برچسب جایگزین میشود. پیکربندی separator یک قطعه کد را برای اتصال قطعات تولیدشده format برای متغیرهای برچسب مختلف مشخص میکند.
- /*!svars:re2c[:<name1>[:<name2>...]] ... */، /*!mvars:re2c[:<name1>[:<name2>...]] ... */ یا %{svars[:<name1>[:<name2>...]] ... %}، %{mvars[:<name1>[:<name2>...]] ... %{
- بلوکهایی که یک قطعه کد الگو را مشخص میکنند که برای هر s-tag/m-tag گسترش مییابد، خواه صراحتاً توسط قواعد ذکر شده باشد (با گزینه --tags) یا بهطور ضمنی توسط re2py تولید شده باشد (با گزینههای --captvars یا --posix-captvars). یک فهرست اختیاری از نام بلوکها مشخص میکند که هنگام محاسبه مجموعه برچسبها کدام بلوکها باید لحاظ شوند (اگر فهرست خالی باشد، همه بلوکها لحاظ میشوند). دو پیکربندی اختیاری وجود دارد: format و separator. پیکربندی format یک رشته الگو را مشخص میکند که در آن @@{tag} (یا به اختصار @@) با نام هر برچسب جایگزین میشود. پیکربندی separator یک قطعه کد را برای پیوند دادن قطعات تولیدشده format برای برچسبهای مختلف مشخص میکند.
- /*!getstate:re2c[:<name1>[:<name2>...]] ... */ یا %{getstate[:<name1>[:<name2>...]] ... %}
- بلوکی که توزیع شرطی را روی وضعیت تحلیلگر لغوی ایجاد میکند (به گزینه --storable-state نیاز دارد). یک فهرست اختیاری از نام بلوکها مشخص میکند که کدام بلوکها باید در توزیع وضعیت گنجانده شوند. گذار پیشفرض به برچسب شروع اولین بلوک در فهرست میرود. اگر فهرست خالی باشد، همه بلوکها لحاظ میشوند و گذار پیشفرض به اولین بلوک فایل که دارای برچسب شروع است میرود. این نوع بلوک با گزینه --loop-switch ناسازگار است، زیرا نیازمند گذارهای میانبلوکی است که بدون goto یا فراخوانی تابع پشتیبانی نمیشوند.
- /*!conditions:re2c[:<name1>[:<name2>...]] ... */، /*!types:re2c... */ یا %{conditions[:<name1>[:<name2>...]] ... %}، %{types... %}
- بلوکی که شمارش شرط را تولید میکند (به گزینه --conditions نیاز دارد). یک فهرست اختیاری از نام بلوکها مشخص میکند که هنگام محاسبه مجموعه شرطها کدام بلوکها باید لحاظ شوند (اگر فهرست خالی باشد، همه بلوکها لحاظ میشوند). بهطور پیشفرض، کد تولیدشده یک شمارش YYCONDTYPE است. این کد را میتوان با پیکربندیهای اختیاری format و separator سفارشی کرد. پیکربندی format یک رشته الگو را مشخص میکند که در آن @@{cond} (یا به اختصار @@) با نام هر شرط، و @@{num} با یک نمایه عددی از آن شرط جایگزین میشود. پیکربندی separator یک قطعه کد را برای اتصال قطعات تولیدشده format برای شرایط مختلف تعیین میکند.
- /*!include:re2c <file> */ یا %{include <file> %}
- این بلوک امکان درج <file> را فراهم میکند که باید یک مسیر فایل درون علامت نقلقول دوتایی باشد. محتویات فایل دقیقاً به جای بلوک جایگزین میشوند، همانطور که #include در C/C++ عمل میکند. از این بلوک میتوان همراه با گزینه --depfile برای ایجاد وابستگیهای سامانه ساخت روی فایلهای اضافهشده استفاده کرد.
- /*!header:re2c:on*/ یا %{header:on %}
- این بلوک شروع فایل سرایند را مشخص میکند. تمام محتویات پس از آن تا بلوک header:off بعدی توسط re2py پردازش شده و در فایل سرایند مشخصشده با گزینه -t --type-header نوشته میشود.
- /*!header:re2c:off*/ یا %{header:off %}
- این بلوک پایان فایل سرایند شروعشده با بلوک header:on*/ را مشخص میکند.
- /*!ignore:re2c ... */ یا %{ignore ... %}
- بلوکی که
محتویات آن
نادیده
گرفته شده و
از فایل
خروجی حذف
میشود.
در اینجا فهرست کاملی از پیکربندیهای پشتیبانیشده توسط re2py آورده شده است:
- re2c:api, re2c:input
- مشابه گزینه --api است.
- re2c:api:sigil
- نشانگر ("sigil") مورد استفاده برای جاینگهدارهای آرگومان در عناصر اولیه API را مشخص میکند. مقدار پیشفرض @@ است. یک جاینگهدار با sigil آغاز شده و به دنبال آن نام آرگومان درون آکولاد میآید. برای نمونه، اگر sigil روی $ تنظیم شود، جاینگهدارها به شکل ${name} خواهند بود. APIهای تکآرگومانی میتوانند از نمادگذاری خلاصه بدون نام درون آکولاد استفاده کنند. این گزینه میتواند توسط گزینههای مربوط به هر یک از عناصر اولیه API بازنویسی شود، مانند re2c:YYFILL@len برای YYFILL.
- re2c:api:style
- سبک API را مشخص میکند. مقادیر ممکن عبارتند از functions (پیشفرض برای C) و free-form (پیشفرض برای Go و Rust). در سبک functions، عناصر اولیه API با یک فهرست آرگومان درون پرانتز به دنبال نام عنصر اولیه تولید میشوند. آرگومانها تنها برای پارامترهای خودکار تولیدشده فراهم میشوند (مانند تعداد نویسههای ارسالی به YYFILL)، اما برای بافت کلی تحلیلگر واژگانی (lexer) ارائه نمیشوند؛ بنابراین عناصر اولیه رفتاری شبیه به ماکروها در C/C++ یا بستارها (closures) در Go و Rust دارند. در سبک فرم آزاد (free-form)، عناصر اولیه API شکل ثابتی ندارند: آنها باید بهصورت رشتههایی حاوی قطعهکدهای آزاد با متغیرهای درونیابیشده به شکل @@{var} یا @@ تعریف شوند (که متناظر با آرگومانها در سبک تابعمانند هستند). این پیکربندی ممکن است برای هر یک از عناصر اولیه API بهطور جداگانه بازنویسی شود؛ برای نمونه پیکربندی re2c:YYFILL:naked را برای YYFILL ببینید.
- re2c:bit-vectors, re2c:flags:bit-vectors, re2c:flags:b
- مشابه گزینه --bit-vectors است، اما میتواند برای هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:captures, re2c:leftmost-captures
- مشابه گزینه --leftmost-captures است، اما میتواند برای هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:captvars, re2c:leftmost-captvars
- مشابه گزینه --leftmost-captvars است، اما میتواند برای هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:case-insensitive, re2c:flags:case-insensitive
- مشابه گزینه --case-insensitive است، اما میتواند برای هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:case-inverted, re2c:flags:case-inverted
- مشابه گزینه --case-inverted است، اما میتواند برای هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:case-ranges, re2c:flags:case-ranges
- مشابه گزینه --case-ranges است، اما میتواند برای هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:computed-gotos, re2c:flags:computed-gotos, re2c:flags:g
- مشابه گزینه --computed-gotos است، اما میتواند برای هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:computed-gotos:relative, re2c:cgoto:relative
- مشابه گزینه --computed-gotos-relative است، اما میتواند برای هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:computed-gotos:threshold, re2c:cgoto:threshold
- در صورت استفاده از goto محاسباتی، این پیکربندی آستانه پیچیدگی را مشخص میکند که باعث تولید جداول پرش بهجای دستورات تودرتوی if و نقشههای بیتی (bitmaps) میشود. مقدار پیشفرض 9 است.
- re2c:cond:abort
- اگر روی یک مقدار عددی صحیح مثبت تنظیم شود، حالت پیشفرض (default case) در هدایت وضعیت تولیدشده، اجرای برنامه را لغو (abort) میکند.
- re2c:cond:goto
- قطعهکدی را مشخص میکند که برای قواعد میانبر خودکار تولیدشده :=> در شرطها استفاده میشود. پیشفرض goto @@; است. جاینگهدار @@ با نام شرط جایگزین میشود (پیکربندیهای re2c:api:sigil و re2c:cond:goto@cond را ببینید).
- re2c:cond:goto@cond
- نشانگر مورد استفاده برای جایگزینی آرگومان در تعریف re2c:cond:goto را مشخص میکند. مقدار پیشفرض @@ است. پیکربندی عمومیتر re2c:api:sigil را بازنویسی میکند.
- re2c:cond:divider
- جداکننده بلوکهای شرط را تعریف میکند. مقدار پیشفرض /* *********************************** */ است. جاینگهدارها با نام شرط جایگزین میشوند (به re2c:api;sigil و re2c:cond:divider@cond مراجعه کنید).
- re2c:cond:divider@cond
- نشانگر مورد استفاده برای جایگزینی آرگومان در تعریف re2c:cond:divider را مشخص میکند. پیشفرض @@ است. پیکربندی عمومیتر re2c:api:sigil را بازنویسی میکند.
- re2c:cond:prefix, re2c:condprefix
- پیشوند مورد استفاده برای برچسبهای شرط را مشخص میکند. پیشفرض yyc_ است.
- re2c:cond:enumprefix, re2c:condenumprefix
- پیشوند مورد استفاده برای شناسههای شرط را مشخص میکند. پیشفرض yyc است.
- re2c:debug-output, re2c:flags:debug-output, re2c:flags:d
- مشابه گزینه --debug-output است، اما میتواند برای هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:empty-class, re2c:flags:empty-class
- مشابه گزینه --empty-class است، اما میتواند برای هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:encoding:ebcdic, re2c:flags:ecb, re2c:flags:e
- مشابه گزینه --ebcdic است، اما میتواند برای هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:encoding:ucs2, re2c:flags:wide-chars, re2c:flags:w
- مشابه گزینه --ucs2 است، اما میتواند برای هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:encoding:utf8, re2c:flags:utf-8, re2c:flags:8
- مشابه گزینه --utf8 است، اما میتواند برای هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:encoding:utf16, re2c:flags:utf-16, re2c:flags:x
- مشابه گزینه --utf16 است، اما میتواند برای هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:encoding:utf32, re2c:flags:unicode, re2c:flags:u
- مشابه گزینه --utf32 است، اما میتواند برای هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:encoding-policy, re2c:flags:encoding-policy
- مشابه گزینه --encoding-policy است، اما میتواند برای هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:eof
- نماد نگهبان مورد استفاده همراه با قاعده پایان ورودی $ را مشخص میکند. مقدار پیشفرض -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 است)..TP re2c:nested-ifs, re2c:flags:nested-ifs, re2c:flags:s همانند گزینهٔ --nested-ifs است، اما میتواند به ازای هر بلوک پیکربندی شود.
- re2c:posix-captures, re2c:flags:posix-captures, re2c:flags:P
- همانند گزینهٔ --posix-captures است، اما میتواند به ازای هر بلوک پیکربندی شود.
- re2c:posix-captvars
- همانند گزینهٔ --posix-captvars است، اما میتواند به ازای هر بلوک پیکربندی شود.
- همانند گزینهٔ --tags است، اما میتواند به ازای هر بلوک پیکربندی شود.
- عبارت مورد استفاده برای متغیرهای برچسب را مشخص میکند. بهطور پیشفرض re2py عبارتهایی به شکل yyt<N> تولید میکند. این حالت ممکن است نامناسب باشد، برای نمونه اگر متغیرهای برچسب بهصورت فیلدهایی در یک struct تعریف شده باشند. تمام تکرارهای @@{tag} یا @@ با نام واقعی برچسب جایگزین میشوند. برای مثال، re2c:tags:expression = "s.@@"; منجر به ایجاد عبارتهایی به شکل s.yyt<N> در کد تولیدشده خواهد شد. همچنین پیکربندی re2c:api:sigil را ببینید.
- عبارت ثابتی را مشخص میکند که برای مقدار منفی برچسب استفاده میشود (بهطور معمول این مقدار در صورتی که برچسبها آفستهای صحیح در رشتهٔ ورودی باشند برابر با -1، و در صورت اشارهگر بودن یک اشارهگر تهی (null pointer) خواهد بود).
- پیشوند نام متغیرهای برچسب را مشخص میکند. پیشفرض yyt است.
- re2c:sentinel
- نماد نگهبان (sentinel) مورد استفاده برای بررسیهای پایان ورودی را مشخص میکند (زمانی که بررسی محدودهها با re2c:yyfill:enable = 0; غیرفعال شده باشد و re2c:eof تنظیم نشده باشد). این پیکربندی تأثیری بر تولید کد ندارد: هدف آن اطمینان از این است که نماد نگهبان در میانهٔ یک قاعده مجاز نباشد، و تضمین کند که تحلیلگر واژگانی فراتر از انتهای بافر را نخواند. مقدار پیشفرض -1` است (در این حالت re2py فرض میکند که نگهبان صفر است، که رایجترین حالت ممکن میباشد). تنها اعداد دهدهی شناسایی میشوند.
- re2c:state:abort
- اگر روی یک مقدار عدد صحیح مثبت تنظیم شود، حالت پیشفرض (default case) در توزیع وضعیت (state dispatch) تولیدشده، اجرای برنامه را متوقف میکند، و یک حالت -1 صریح حاوی انتقال به آغاز بلوک خواهد بود.
- re2c:state:nextlabel
- تعیین میکند که آیا بعد از سوییچ YYGETSTATE یک برچسب yyNext قرار گیرد یا خیر (مقدار پیشفرض صفر است که متناظر با نبود برچسب است). همچنین میتوان از re2c:label:start برای تولید یک برچسب آغازین خاص، یا یک بلوک صریح getstate برای تولید سوییچ YYGETSTATE بهطور جداگانه از بلوک تحلیلگر واژگانی استفاده کرد.
- re2c:unsafe, re2c:flags:unsafe
- همانند گزینهٔ --no-unsafe است، اما میتواند به ازای هر بلوک پیکربندی شود. اگر روی صفر تنظیم شود، از تولید دربرگیرندههای unsafe پیرامون YYPEEK جلوگیری میکند. پیشفرض مقداری غیرصفر است (دربرگیرندهها تولید میشوند). این پیکربندی مختص به Rust است.
- re2c:YYBACKUP, re2c:define:YYBACKUP
- دستور پایهٔ عمومی API یعنی YYBACKUP را تعریف میکند.
- re2c:YYBACKUPCTX, re2c:define:YYBACKUPCTX
- دستور پایهٔ عمومی API یعنی YYBACKUPCTX را تعریف میکند.
- re2c:YYCONDTYPE, re2c:define:YYCONDTYPE
- دستور پایهٔ API یعنی YYCONDTYPE را تعریف میکند.
- re2c:YYCTYPE, re2c:define:YYCTYPE
- دستور پایهٔ API یعنی YYCTYPE را تعریف میکند.
- re2c:YYCTXMARKER, re2c:define:YYCTXMARKER
- دستور پایهٔ API یعنی YYCTXMARKER را تعریف میکند.
- re2c:YYCURSOR, re2c:define:YYCURSOR
- دستور پایهٔ API یعنی YYCURSOR را تعریف میکند.
- re2c:YYDEBUG, re2c:define:YYDEBUG
- دستور پایهٔ API یعنی YYDEBUG را تعریف میکند.
- re2c:YYFILL, re2c:define:YYFILL
- دستور پایهٔ API یعنی YYFILL را تعریف میکند.
- re2c:YYFILL@len, re2c:define:YYFILL@len
- علامت (sigil) مورد استفاده برای جایگزینی آرگومان را در تعریف YYFILL مشخص میکند. پیشفرض @@ است. بر پیکربندی عمومیتر re2c:api:sigil اولویت دارد.
- re2c:YYFILL:naked, re2c:define:YYFILL:naked
- بر پیکربندی عمومیتر re2c:api:style برای YYFILL اولویت دارد. مقدار صفر با سبک آزاد API (free-form API style) مطابقت دارد.
- re2c:YYFN
- دستور پایهٔ API یعنی YYFN را تعریف میکند.
- re2c:YYINPUT
- دستور پایهٔ API یعنی YYINPUT را تعریف میکند.
- re2c:YYGETCOND, re2c:define:YYGETCONDITION
- دستور پایهٔ API یعنی YYGETCOND را تعریف میکند.
- re2c:YYGETCOND:naked, re2c:define:YYGETCONDITION:naked
- بر پیکربندی عمومیتر re2c:api:style برای YYGETCOND اولویت دارد. مقدار صفر با سبک آزاد API (free-form API style) مطابقت دارد.
- re2c:YYGETSTATE, re2c:define:YYGETSTATE
- دستور پایهٔ API یعنی YYGETSTATE را تعریف میکند.
- re2c:YYGETSTATE:naked, re2c:define:YYGETSTATE:naked
- بر پیکربندی عمومیتر re2c:api:style برای YYGETSTATE اولویت دارد. مقدار صفر با سبک آزاد API (free-form API style) مطابقت دارد.
- re2c:YYGETACCEPT, re2c:define:YYGETACCEPT
- دستور پایهٔ API یعنی YYGETACCEPT را تعریف میکند.
- re2c:YYLESSTHAN, re2c:define:YYLESSTHAN
- دستور پایهٔ عمومی API یعنی YYLESSTHAN را تعریف میکند.
- re2c:YYLIMIT, re2c:define:YYLIMIT
- دستور پایهٔ API یعنی YYLIMIT را تعریف میکند.
- re2c:YYMARKER, re2c:define:YYMARKER
- دستور پایهٔ API یعنی YYMARKER را تعریف میکند.
- re2c:YYMTAGN, re2c:define:YYMTAGN
- دستور پایهٔ عمومی API یعنی YYMTAGN را تعریف میکند.
- re2c:YYMTAGP, re2c:define:YYMTAGP
- دستور پایهٔ عمومی API یعنی YYMTAGP را تعریف میکند.
- re2c:YYPEEK, re2c:define:YYPEEK
- دستور پایهٔ عمومی API یعنی YYPEEK را تعریف میکند.
- re2c:YYRESTORE, re2c:define:YYRESTORE
- دستور پایهٔ عمومی API یعنی YYRESTORE را تعریف میکند.
- re2c:YYRESTORECTX, re2c:define:YYRESTORECTX
- دستور پایهٔ عمومی API یعنی YYRESTORECTX را تعریف میکند.
- re2c:YYRESTORETAG, re2c:define:YYRESTORETAG
- دستور پایهٔ عمومی API یعنی YYRESTORETAG را تعریف میکند.
- re2c:YYSETCOND, re2c:define:YYSETCONDITION
- دستور پایهٔ API یعنی YYSETCOND را تعریف میکند.
- re2c:YYSETCOND@cond, re2c:define:YYSETCONDITION@cond
- علامت (sigil) مورد استفاده برای جایگزینی آرگومان را در تعریف YYSETCOND مشخص میکند. مقدار پیشفرض @@ است. بر پیکربندی عمومیتر re2c:api:sigil اولویت دارد.
- re2c:YYSETCOND:naked, re2c:define:YYSETCONDITION:naked
- بر پیکربندی عمومیتر re2c:api:style برای YYSETCOND اولویت دارد. مقدار صفر با سبک آزاد API (free-form API style) مطابقت دارد.
- re2c:YYSETSTATE, re2c:define:YYSETSTATE
- دستور پایهٔ API یعنی YYSETSTATE را تعریف میکند.
- re2c:YYSETSTATE@state, re2c:define:YYSETSTATE@state
- علامت (sigil) مورد استفاده برای جایگزینی آرگومان را در تعریف YYSETSTATE مشخص میکند. مقدار پیشفرض @@ است. بر پیکربندی عمومیتر re2c:api:sigil اولویت دارد.
- re2c:YYSETSTATE:naked, re2c:define:YYSETSTATE:naked
- بر پیکربندی عمومیتر re2c:api:style برای YYSETSTATE اولویت دارد. مقدار صفر با سبک آزاد API (free-form API style) مطابقت دارد.
- re2c:YYSETACCEPT, re2c:define:YYSETACCEPT
- دستور پایهٔ API یعنی YYSETACCEPT را تعریف میکند.
- re2c:YYSKIP, re2c:define:YYSKIP
- دستور پایهٔ عمومی API یعنی YYSKIP را تعریف میکند.
- re2c:YYSHIFT, re2c:define:YYSHIFT
- دستور پایهٔ عمومی API یعنی YYSHIFT را تعریف میکند.
- re2c:YYCOPYMTAG, re2c:define:YYCOPYMTAG
- دستور پایهٔ عمومی API یعنی YYCOPYMTAG را تعریف میکند.
- re2c:YYCOPYSTAG, re2c:define:YYCOPYSTAG
- دستور پایهٔ عمومی API یعنی YYCOPYSTAG را تعریف میکند..TP re2c:YYSHIFTMTAG, re2c:define:YYSHIFTMTAG عنصر اولیهٔ API عمومیِ YYSHIFTMTAG را تعریف میکند.
- re2c:YYSHIFTSTAG, re2c:define:YYSHIFTSTAG
- عنصر اولیهٔ API عمومیِ YYSHIFTSTAG را تعریف میکند.
- re2c:YYSTAGN, re2c:define:YYSTAGN
- عنصر اولیهٔ API عمومیِ YYSTAGN را تعریف میکند.
- re2c:YYSTAGP, re2c:define:YYSTAGP
- عنصر اولیهٔ API عمومیِ YYSTAGP را تعریف میکند.
- re2c:yyaccept, re2c:variable:yyaccept
- عنصر اولیهٔ API با نام yyaccept را تعریف میکند.
- re2c:yybm, re2c:variable:yybm
- عنصر اولیهٔ API با نام yybm را تعریف میکند.
- re2c:yybm:hex, re2c:variable:yybm:hex
- در صورت تنظیم روی مقداری غیرصفر، نگاشتهای بیتی برای گزینهٔ --bit-vectors در قالب هگزادسیمال تولید میشوند. مقدار پیشفرض صفر است (نگاشتهای بیتی در قالب دهدهی هستند).
- re2c:yych, re2c:variable:yych
- عنصر اولیهٔ API با نام yych را تعریف میکند.
- re2c:yych:emit, re2c:variable:yych:emit
- در صورت تنظیم روی صفر، تعریف yych تولید نمیشود. مقدار پیشفرض غیرصفر است.
- re2c:yych:conversion, re2c:variable:yych:conversion
- در صورت تنظیم روی مقداری غیرصفر، re2py هر بار که yych خوانده میشود، بهطور خودکار یک تبدیل به YYCTYPE تولید میکند. مقدار پیشفرض صفر است (بدون تبدیل).
- re2c:yych:literals, re2c:variable:yych:literals
- قالب لیترالهایی را که yych با آنها مطابقت داده میشود مشخص میکند. مقادیر ممکن عبارتند از: char (لیترالهای نویسهای درون علامت نقلقول تکی؛ موارد غیرقابلچاپ از دنبالههای گریز که با بکاسلش شروع میشوند استفاده میکنند)، hex (اعداد صحیح هگزادسیمال)، و char_or_hex (ترکیبی از هر دو؛ لیترالهای نویسهای برای نویسههای قابلچاپ و اعداد صحیح هگزادسیمال برای سایر موارد).
- re2c:yyctable, re2c:variable:yyctable
- عنصر اولیهٔ API با نام yyctable را تعریف میکند.
- re2c:yynmatch, re2c:variable:yynmatch
- عنصر اولیهٔ API با نام yynmatch را تعریف میکند.
- re2c:yypmatch, re2c:variable:yypmatch
- عنصر اولیهٔ API با نام yypmatch را تعریف میکند.
- re2c:yytarget, re2c:variable:yytarget
- عنصر اولیهٔ API با نام yytarget را تعریف میکند.
- re2c:yystable, re2c:variable:yystable
- منسوخ شده است.
- re2c:yystate, re2c:variable:yystate
- عنصر اولیهٔ API با نام yystate را تعریف میکند.
- re2c:yyfill, re2c:variable:yyfill
- عنصر اولیهٔ API با نام yyfill را تعریف میکند.
- re2c:yyfill:check
- در صورت تنظیم روی صفر، از تولید بررسی پیش از YYFILL برای تعداد نویسههای ورودی (تعریف YYLESSTHAN در API عمومی و مقایسهٔ مبتنی بر YYLIMIT در API اشارهگر C) جلوگیری میکند. مقدار پیشفرض غیرصفر است (بررسی تولید میشود).
- re2c:yyfill:enable
- در صورت تنظیم روی صفر، از تولید YYFILL (بههمراه بررسی آن) جلوگیری میکند. این مورد باید زمانی استفاده شود که کل ورودی در یک تکه حافظه جا میگیرد (نیازی به بافرسازی نیست) و بررسیهای پایان ورودی به بررسیهای YYFILL متکی نیستند (برای مثال اگر از یک نویسهٔ نگهبان [sentinel] استفاده شود). از هشدارها (گزینهٔ -W) و پیکربندی re2c:sentinel استفاده کنید تا تأیید شود تحلیلگر لغوی تولیدشده نمیتواند فراتر از پایان ورودی را بخواند. مقدار پیشفرض غیرصفر است (YYFILL فعال است).
- re2c:yyfill:parameter
- در صورت تنظیم روی صفر، از تولید پارامتر ارسالی به YYFILL جلوگیری میکند. این پارامتر حداقل تعداد نویسههایی است که باید تأمین شوند. مقدار پیشفرض غیرصفر است (پارامتر تولید میشود). این پیکربندی میتواند توسط re2c:YYFILL:naked یا re2c:api:style بازنویسی شود.
- re2c:yyfn:sep
- جداکنندهٔ مورداستفاده در عناصر YYFN را مشخص میکند (پیشفرض نقطهویرگول است).
- re2c:yyfn:throw
- استثناهای
پرتابشده
توسط تابع
YYFN را مشخص
میکند
(پیشفرض
خالی است که
به معنای
نبود هیچ
استثنایی
میباشد).
ابزار re2py از نحو زیر برای عبارات باقاعده استفاده میکند:
- "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؛ از پرانتز برای تغییر تقدم استفاده میشود. اگر استخراج تطابقهای فرعی (submatch extraction) فعال باشد، بسته به گزینه --invert-captures، (R) یک گروه گیرنده (capturing) یا غیرگیرنده (non-capturing) خواهد بود.
- (!R)
- در صورت فعال بودن استخراج تطابقهای فرعی، بسته به گزینه --invert-captures، (!R) یک گروه غیرگیرنده یا گیرنده خواهد بود.
- R S
- الحاق: R و به دنبال آن S.
- R | S
- جایگزین: R یا S.
- R / S
- پیشنگری (Lookahead): R و به دنبال آن S، اما S مصرف نمیشود.
- name
- عبارت باقاعده تعریفشده به نام name (یا رشته لفظی "name" در حالت سازگاری با Flex).
- {name}
- عبارت باقاعده تعریفشده به نام name در حالت سازگاری با Flex.
- @stag
- یک s-tag: آخرین موقعیت ورودی را که @stag در آن تطابق دارد، در متغیری با نام stag ذخیره میکند.
- #mtag
- یک m-tag: تمام موقعیتهای ورودی را که #mtag در آنها تطابق دارد، در متغیری با نام mtag ذخیره میکند.
- $
- پایان ورودی.
کلاسهای نویسه و رشتههای لفظی میتوانند شامل توالیهای گریز زیر باشند: \a، \b، \f، \n، \r، \t، \v، \\، گریزهای هشتهشتی \ooo و گریزهای شانزدهشانزدهی \xhh، \uhhhh و \Uhhhhhhhh.
کنشها (Actions)
در اینجا فهرستی از کنشهای از پیش تعریفشده و مورد پشتیبانی re2py آمده است:
- !entry code
- کنش ورود، یک بلوک از code تعریفشده توسط کاربر را به حالت آغازین ماشین حالت متناهی فعلی پیوند میدهد. اگر از start conditions استفاده شود، کنش ورود را میتوان بهصورت جداگانه برای هر شرط تنظیم کرد. از این کنش میتوان برای انجام مقداردهی اولیه، مثلاً ذخیره مکان شروع یک لکسم (lexeme)، استفاده کرد.
- !pre_rule code
- کنش پیشقاعده، یک بلوک از code تعریفشده توسط کاربر را به ابتدای کنشهای معناییِ تمام قواعد در بلوک فعلی (یا شرط، در صورت استفاده از start conditions) اضافه میکند. از این کنش میتوان برای فاکتور گرفتن بخش مشترک تمام کنشهای معنایی (مانند ذخیره موقعیت پایان یک لکسم) استفاده کرد.
- !post_rule code
- کنش
پسقاعده،
یک بلوک از
code
تعریفشده
توسط کاربر
را به
انتهای
کنشهای
معناییِ
تمام قواعد
در بلوک
فعلی (یا
شرط، در
صورت
استفاده از
start conditions) ضمیمه
میکند. از
این کنش
میتوان
برای درج
دستورهای
تله (trap statements) جهت
محافظت در
برابر
جریان
کنترل
ناخواسته
استفاده
کرد.
در اینجا فهرست کاملی از دستورالعملهای پشتیبانیشده توسط re2py آورده شده است:
- !use:name ;
- یک دستورالعمل use درونبلوکی که یک بلوک قواعد از پیش تعریفشده با name مشخصشده را در بلوک فعلی ادغام میکند. تعاریف نامگذاریشده، پیکربندیها و قواعد بلوک مرجع به بلوک فعلی افزوده میشوند. تداخلها میان قواعد و پیکربندیهای همپوشان به روش معمول حل میشوند: نخستین قاعده اولویت دارد و آخرین پیکربندی بر موارد قبلی جایگزین میشود. یک استثنا، قواعد ویژه *، $ و <!> هستند که تعریف محلی درون بلوک همیشه برای آنها اولویت دارد. دستورالعمل use میتواند در هر جای یک بلوک قرار گیرد و استفاده از چند دستورالعمل use مجاز است.
- !include file ;
- این دستورالعمل همانند بلوک include عمل میکند: محتویات file را عیناً در محل دستورالعمل درج میکند.
رابط برنامه (Program interface)
کد تولیدشده به کمک پریمیتیوها (primitives)، که در مجموع به عنوان API شناخته میشوند، با برنامه بیرونی ارتباط برقرار میکند. اینکه کدام پریمیتیوها باید برای یک برنامه خاص تعریف شوند به عوامل متعددی بستگی دارد، از جمله پیچیدگی عبارات باقاعده، نحوه نمایش ورودی، بافرسازی و استفاده از ویژگیهای گوناگون. همه پریمیتیوهای ضروری باید توسط کاربر در قالب ماکروها، توابع، متغیرها یا هر شکل مناسب دیگری که کد تولیدشده را از نظر نحوی و معنایی صحیح میسازد، تعریف شوند. re2py تعاریف را بررسی نمیکند (و نمیتواند بررسی کند)، بنابراین اگر موردی جا افتاده باشد یا به نادرستی تعریف شده باشد، ممکن است برنامه تولیدشده در زمان کامپایل یا زمان اجرا با خطا مواجه شود. این راهنما مثالهایی از تعاریف API در رایجترین موارد ارائه میدهد.
ابزار re2py دارای سه سبک API است که مجموعه اصلی پریمیتیوهای مورداستفاده در یک برنامه را تعریف میکنند:
- Simple API
- این API پیشفرض برای بکاند پایتون است. از پریمیتیوهای زیر تشکیل شده است: YYINPUT (که باید به عنوان دنبالهای از واحدهای کد، مانند یک رشته تعریف شود) و YYCURSOR، YYMARKER، YYCTXMARKER، YYLIMIT (که باید به عنوان اندیسهایی در YYINPUT تعریف شوند).
- Record API
- رابط Record API در مواردی مفید است که وضعیت لکسر باید در یک کلاس ذخیره شود. این قابلیت با گزینه --api record یا پیکربندی re2c:api = record فعال میشود. این API شامل متغیری به نام yyrecord است (نام آن را میتوان با re2c:yyrecord بازنویسی کرد) که باید به عنوان کلاسی با ویژگیهای yyinput، yycursor، yymarker، yyctxmarker، yylimit تعریف شود (تنها فیلدهایی که در کد تولیدشده استفاده میشوند نیاز به تعریف دارند و نام آنها قابل پیکربندی است).
- Generic API
- این منعطفترین API است. این حالت با گزینه --api generic یا پیکربندی re2c:api = generic فعال میشود. این API شامل پریمیتیوهایی برای عملیات عمومی است: 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 از نوع بولی تعریف شود که اگر و تنها اگر پایان منطقی ورودی فرا رسیده باشد (به استثنای هرگونه پدینگ یا نمادهای نگهبان)، برابر با true است. YYEND برای پیادهسازی نماد $ در عبارات باقاعده استفاده میشود. این با YYLESSTHAN، که برای اطمینان از عدم خواندن فراتر از انتهای بافر توسط لکسر به کار میرود، متفاوت است.
- YYPEEK
- یک پریمیتیو Generic API بدون متغیر. باید به عنوان یک r-value از نوع YYCTYPE تعریف شود که برابر با نویسه موجود در موقعیت فعلی ورودی است.
- YYSKIP
- یک پریمیتیو Generic API که باید موقعیت فعلی ورودی را به اندازه یک واحد کد به جلو ببرد.
- YYBACKUP
- یک پریمیتیو Generic API که باید موقعیت فعلی ورودی را ذخیره کند (تا بعداً با YYRESTORE بازیابی شود).
- YYRESTORE
- یک پریمیتیو Generic API که باید موقعیت فعلی ورودی را به مقداری که توسط YYBACKUP ذخیره شده بازگرداند.
- YYBACKUPCTX
- یک پریمیتیو Generic API که باید موقعیت فعلی ورودی را به عنوان موقعیت بافت پایانی ذخیره کند (تا بعداً با YYRESTORECTX بازیابی شود).
- YYRESTORECTX
- یک پریمیتیو Generic API که باید موقعیت بافت پایانی ذخیرهشده توسط YYBACKUPCTX را بازگرداند. YYRESTORETAG یک دستور اولیه عمومی API با یک متغیر tag که باید موقعیت بافت پسآیند (trailing context) را به مقدار tag بازگرداند.
- YYSTAGP
- یک دستور اولیه عمومی API با یک متغیر tag، که در آن tag میتواند یک اشارهگر یا یک آفست در YYINPUT باشد (برای جزئیات به بخش استخراج تطابقهای فرعی مراجعه کنید). YYSTAGP باید tag را روی موقعیت فعلی ورودی تنظیم کند.
- YYSTAGN
- یک دستور اولیه عمومی API با یک متغیر tag، که در آن tag میتواند یک اشارهگر یا یک آفست در YYINPUT باشد (برای جزئیات به بخش استخراج تطابقهای فرعی مراجعه کنید). YYSTAGN باید tag را روی مقداری تنظیم کند که نشاندهندهٔ موقعیت ورودی ناموجود است.
- YYMTAGP
- یک دستور اولیه عمومی API با یک متغیر tag. YYMTAGP باید موقعیت فعلی را به تاریخچهٔ تطابقهای فرعی tag ضمیمه کند (برای جزئیات به بخش استخراج تطابقهای فرعی مراجعه کنید).
- YYMTAGN
- یک دستور اولیه عمومی API با یک متغیر tag. YYMTAGN باید مقداری را که نشاندهندهٔ موقعیت ورودی ناموجود است، به تاریخچهٔ تطابقهای فرعی tag ضمیمه کند (برای جزئیات به بخش استخراج تطابقهای فرعی مراجعه کنید).
- YYSHIFT
- یک دستور اولیه عمومی API با یک متغیر shift که باید موقعیت فعلی ورودی را به اندازهٔ shift نویسه جابهجا کند (مقدار جابهجایی میتواند منفی باشد).
- YYCOPYSTAG
- یک دستور اولیه عمومی API با دو متغیر، lhs و rhs، که باید متغیر s-tag سمت راست (rhs) را در متغیر s-tag سمت چپ (lhs) رونوشت کند. برای بیشتر زبانها این دستور اولیه تعریفی پیشفرض دارد که lhs را به rhs انتساب میدهد.
- YYCOPYMTAG
- یک دستور اولیه عمومی API با دو متغیر، lhs و rhs، که باید متغیر m-tag سمت راست (rhs) را در متغیر m-tag سمت چپ (lhs) رونوشت کند. برای بیشتر زبانها این دستور اولیه تعریفی پیشفرض دارد که lhs را به rhs انتساب میدهد.
- YYSHIFTSTAG
- یک دستور اولیه عمومی API با دو متغیر، tag و shift، که باید tag را به اندازهٔ shift واحد کد جابهجا کند (مقدار جابهجایی میتواند منفی باشد).
- 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 قابل تغییر نیستند. گزینههای اشکالزدایی (debug) عموماً نیاز به ساخت re2c در پیکربندی اشکالزدایی دارند. گزینههای داخلی برای آزمایش الگوریتمهای استفادهشده در re2c مفید هستند.
- -? --help -h
- نمایش پیام راهنما.
- --api <simple | record | generic>
- مشخص کردن API استفادهشده توسط کد تولیدشده جهت ارتباط با کد تعریفشده توسط کاربر. گزینه simple باید در موارد سادهای استفاده شود که نیازی به پر کردن مجدد بافر و ذخیره وضعیت تحلیلگر لغوی (lexer) وجود ندارد. گزینه record باید زمانی استفاده شود که وضعیت lexer باید در یک رکورد (struct، class و غیره) ذخیره شود. گزینه generic باید در موارد پیچیدهای استفاده شود که دو API دیگر به اندازه کافی انعطافپذیر نیستند.
- --bit-vectors -b
- بهینهسازی پرشهای شرطی با استفاده از ماسکهای بیتی (bit masks). این گزینه مستلزم --nested-ifs است.
- --captures, --leftmost-captures
- فعالسازی استخراج تطابق فرعی (submatch) با گروههای گیرنده حریصانه چپترین (leftmost greedy). نتیجه در آرایه yybmatch با ظرفیت 2 * YYMAXNMATCH ذخیره شده، و yynmatch روی تعداد گروههای قاعده تطابق تنظیم میشود.
- --captvars, --leftmost-captvars
- فعالسازی استخراج تطابق فرعی با گروههای گیرنده حریصانه چپترین. نتیجه در متغیرهای yytl<k> و yytr<k> برای k-امین گروه گیرنده جمعآوری میشود.
- --case-insensitive
- رفتار با رشتههای درون نقلقول تکی و جفتی بهصورت غیرحساس به بزرگی و کوچکی حروف.
- --case-inverted
- معکوس کردن معنای رشتههای با علامت نقلقول تکی و جفتی: رشتههای درون نقلقول تکی حساس به بزرگی و کوچکی حروف و رشتههای درون نقلقول جفتی غیرحساس به حروف در نظر گرفته میشوند.
- --case-ranges
- فشردهسازی caseهای متوالی در دستورات switch به بازهای به فرم low ... high. این نحو یک افزونه زبان C/C++ است که توسط کامپایلرهایی مانند GCC، Clang و Tcc پشتیبانی میشود. مزیت اصلی نسبت به استفاده از caseهای تکی، تولید کد کوچکتر و سرعت تولید بیشتر است، اگرچه برای برخی کامپایلرها مانند Tcc منجر به حجم دودویی (binary) کوچکتر نیز میشود.
- --computed-gotos -g
- بهینهسازی پرشهای شرطی با استفاده از افزونه غیراستاندارد "computed goto" (که باید توسط کامپایلر پشتیبانی شود). re2py جدولهای پرش را فقط در موارد پیچیده با انشعابهای شرطی فراوان تولید میکند. آستانه پیچیدگی را میتوان با پیکربندی cgoto:threshold تنظیم کرد. آفستهای نسبی را میتوان با پیکربندی cgoto:relative فعال کرد. این گزینه مستلزم --bit-vectors است.
- --computed-gotos-relative
- مشابه --computed-gotos اما در عوض آفستهای نسبی را برای جدولهای پرش تولید میکند (که باید توسط کامپایلر پشتیبانی شود). این گزینه مستلزم --computed-gotos است.
- --conditions --start-conditions -c
- فعالسازی پشتیبانی از "شرطها" شبیه به Flex: چندین تحلیلگر لغوی مرتبط با یکدیگر درون یک بلوک. این یک جایگزین برای مشخص کردن دستی بلوکهای مختلف re2py متصلشده با goto یا فراخوانی توابع است.
- --depfile FILE
- نوشتن اطلاعات وابستگی در FILE در قالب یک قاعده Makefile به صورت <output-file> : <input-file> [include-file ...]. این امکان ردیابی وابستگیهای ساخت را در حضور بلوکها/دستورالعملهای include فراهم میکند، به طوری که بهروزرسانی فایلهای include باعث بازتولید فایل خروجی میشود. این گزینه به گزینه --output وابسته است.
- --ebcdic --ecb -e
- تولید تحلیلگر لغوی که ورودی را با کدگذاری EBCDIC میخواند. re2py فرض میکند که محدوده نویسهها 0 -- 0xFF و اندازه نویسه ۱ بایت است.
- --empty-class <match-empty | match-none | error>
- تعریف نحوه رفتار re2py با کلاسهای نویسه خالی. با match-empty (پیشفرض) کلاس خالی با ورودی خالی تطابق مییابد (که غیرمنطقی است، اما سازگار با نسخههای پیشین است). با match-none کلاس خالی همیشه در تطابق شکست میخورد. با error کلاس خالی خطای کامپایل ایجاد میکند.
- --encoding-policy <fail | substitute | ignore>
- تعریف نحوه رفتار re2py با جانشینهای یونیکد (Unicode surrogates). با fail، برنامه re2py هنگام برخورد با یک جانشین با خطا متوقف میشود. با substitute، برنامه re2py بیصدا جانشینها را با نقطه کد خطای 0xFFFD جایگزین میکند. با ignore (پیشفرض)، برنامه re2py با جانشینها مانند نقاط کد معمولی رفتار میکند. استاندارد یونیکد بیان میکند که جانشینهای تنها نامعتبر هستند، اما کتابخانهها و برنامهها در دنیای واقعی رفتارهای متفاوتی نشان میدهند.
- --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 مفید است. برنامه re2py به دنبال FILE در دایرکتوری فایل والد و مکانهای include مشخصشده با گزینه -I میگردد.
- --input <default | custom>
- نام مستعار منسوخشده برای --api. گزینه default متناظر با simple است (در واقع برای بیشتر backendها پیشفرض است، اما نه برای همه). گزینه custom متناظر با generic است.
- --input-encoding <ascii | utf8>
- مشخص کردن نحوه تجزیه عبارات باقاعده توسط re2py. با ascii (پیشفرض) re2py با ورودی با کدگذاری ASCII رفتار میکند: هر دنبالهای از واحدهای کد یک دنباله از نویسههای تکبایتی مستقل است. با utf8 برنامه re2py با ورودی با کدگذاری 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 روی تعداد گروههای قاعده تطابق تنظیم میشود..TP --posix-captvars فعالسازی استخراج زیرتطابق با گروههای گیرنده به سبک POSIX. نتیجه برای k-امین گروه گیرنده در متغیرهای yytl<k> و yytr<k> جمعآوری میشود.
- --recursive-functions
- استفاده از مدل کد مبتنی بر توابع همبازگشتی (co-recursive)، که در آن هر وضعیت DFA یک تابع مجزا است که میتواند توابع وضعیتهای دیگر یا خودش را فراخوانی کند.
- --reusable -r
- از نسخه 2.2 منسوخ شده است (بلوکهای با قابلیت استفاده مجدد اکنون به طور پیشفرض مجاز هستند).
- --skeleton -S
- نادیده گرفتن کد واسط تعریفشده توسط کاربر و تولید یک برنامه خودکفای "skeleton" (اسکلتی). علاوه بر این، فایلهای ورودی با رشتههای مشتقشده از گرامر باقاعده و نتایج تطابق فشردهشده تولید میشوند که برای اعتبارسنجی رفتار "skeleton" روی تمام ورودیها به کار میروند. این گزینه برای یافتن باگها در بهینهسازیها و تولید کد مفید است. این گزینه فقط برای C پشتیبانی میشود.
- --storable-state -f
- تولید یک تحلیلگر لغوی (lexer) که میتواند وضعیت درونی خود را ذخیره کند. این قابلیت در لکسرهای مبتنی بر مدل push که هنگام نبود ورودی کافی توسط یک برنامه بیرونی متوقف شده و پس از در دسترس قرار گرفتن ورودی بیشتر ادامه مییابند، مفید است. در این حالت، کاربران باید علاوه بر این توابع اولیهی YYGETSTATE و YYSETSTATE را تعریف کنند و متغیرهای yych، yyaccept و state باید بخشی از وضعیت ذخیرهشدهی لکسر باشند.
- --syntax FILE
- بارگذاری پیکربندیها از FILE مشخصشده و اعمال آنها بر روی فایل نحو پیشفرض. توجه داشته باشید که FILE میتواند تنها چند پیکربندی را تعریف کند (اگر برای اصلاح فایل نحو پیشفرض استفاده شود)، یا یک بکاند زبان کاملاً جدید را تعریف نماید (در حالت دوم توصیه میشود از گزینه --lang none استفاده شود).
- فعالسازی استخراج زیرتطابق با تگها.
- --ucs2 --wide-chars -w
- تولید لکسری که ورودی کدگذاریشده با UCS2 را میخواند. برنامه re2py فرض میکند که محدوده نویسهها 0 -- 0xFFFF و اندازه نویسه ۲ بایت است. این گزینه مستلزم --nested-ifs است.
- --utf8 --utf-8 -8
- تولید لکسری که ورودی را در کدگذاری UTF-8 میخواند. برنامه re2py فرض میکند که محدوده نویسهها 0 -- 0x10FFFF و اندازه نویسه ۱ بایت است.
- --utf16 --utf-16 -x
- تولید لکسری که ورودی کدگذاریشده با UTF16 را میخواند. برنامه re2py فرض میکند که محدوده نویسهها 0 -- 0x10FFFF و اندازه نویسه ۲ بایت است. این گزینه مستلزم --nested-ifs است.
- --utf32 --unicode -u
- تولید لکسری که ورودی کدگذاریشده با UTF32 را میخواند. برنامه re2py فرض میکند که محدوده نویسهها 0 -- 0x10FFFF و اندازه نویسه ۴ بایت است. این گزینه مستلزم --nested-ifs است.
- --verbose
- چاپ یک پیام کوتاه در صورت موفقیت.
- --vernum -V
- نمایش اطلاعات نسخه در قالب MMmmpp (اصلی، فرعی، وصله).
- --version -v
- نمایش اطلاعات نسخه.
- --single-pass -1
- منسوخ شده است. کاری انجام نمیدهد (حالت تکگذره اکنون پیشفرض است).
- --debug-output -d
- قرار دادن فراخوانیهای YYDEBUG در کد تولیدشده. این گزینه برای ردگیری اجرای لکسر مفید است.
- --dump-adfa
- گزینه اشکالزدایی: خروجی دادن DFA پس از تونلزنی (در قالب .dot).
- --dump-cfg
- گزینه اشکالزدایی: خروجی دادن گراف جریان کنترل (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 مورداستفاده توسط re2py. گزینه moore همان الگوریتم مور (Moore) است (که پیشفرض میباشد). گزینه table الگوریتم "table filling" (پر کردن جدول) است. هر دو الگوریتم باید DFA یکسانی، صرفنظر از برچسبگذاری مجدد وضعیتها تولید کنند؛ الگوریتم پر کردن جدول سادهتر و بسیار کندتر بوده و به عنوان پیادهسازی مرجع عمل میکند.
- --eager-skip
- گزینه درونی: وادار کردن لکسر تولیدشده به جلو بردن موقعیت ورودی به صورت مشتاقانه (eagerly) -- بلافاصله پس از خواندن نماد ورودی. این گزینه رفتار پیشفرض را که در آن موقعیت ورودی به صورت تنبلانه (lazily) -- پس از انتقال به وضعیت بعدی جلو برده میشود، تغییر میدهد.
- --no-lookahead
- گزینه درونی، منسوخشده. این گزینه برای فعالسازی الگوریتم TDFA(0) به کار میرفت. بر خلاف TDFA(1)، الگوریتم TDFA(0) از پیشنگری تکنمادی استفاده نمیکند. این الگوریتم عملیات ثباتها را به جای انتقالهای خروجی، روی انتقالهای ورودی اعمال میکند. ارزیابیهای عملکرد نشان داد که الگوریتم TDFA(0) کارایی کمتری نسبت به TDFA(1) دارد.
- --no-optimize-tags
- گزینه درونی: جلوگیری از بهینهسازی متغیرهای تگ (مفید برای اشکالزدایی).
- --posix-closure <gor1 | gtop>
- گزینه درونی: تعیین الگوریتم کوتاهترین مسیر مورداستفاده برای ساخت بستار اپسیلون (epsilon-closure) با معناشناسی رفع ابهام POSIX: مقدار gor1 (پیشفرض) نمایانگر الگوریتم گلدبرگ-رادزیک (Goldberg-Radzik) است و gtop نمایانگر الگوریتم "global topological order" (ترتیب توپولوژیکی سراسری) میباشد.
- --posix-prectable <complex | naive>
- گزینه درونی: تعیین الگوریتم مورداستفاده برای محاسبه جدول اولویت POSIX. الگوریتم complex جدول اولویت را در یک پیمایش از درخت تاریخچه تگ محاسبه میکند و دارای پیچیدگی مرتبه دوم بر حسب تعداد وضعیتهای TNFA است؛ این گزینه پیشفرض است. الگوریتم naive در بدترین حالت دارای پیچیدگی مکعبی (مرتبه سه) بر حسب تعداد وضعیتهای TNFA است، اما بسیار سادهتر از complex بوده و ممکن است در موارد غیرپاتولوژیک اندکی سریعتر باشد.
- --stadfa
- گزینه درونی، منسوخشده. این گزینه برای فعالسازی الگوریتم staDFA به کار میرفت، که تفاوت آن با TDFA در این است که عملیات ثبات به جای انتقالها در وضعیتها قرار میگیرند. ارزیابیهای عملکرد نشان داد که الگوریتم staDFA کارایی کمتری نسبت به TDFA دارد.
- --fixed-tags <none | toplevel | all>
- گزینه
درونی:
تعیین
اینکه آیا
بهینهسازی
تگ ثابت (fixed-tag)
باید روی
همه تگها
(all)،
هیچکدام
از آنها
(none)، یا فقط
تگهای
موجود در
الحاق سطح
بالا (toplevel)
اعمال شود
یا خیر.
مقدار
پیشفرض all
است. تگهای
«ثابت» ("Fixed")
تگهایی
هستند که در
فاصله
ثابتی از یک
تگ دیگر
(موسوم به
«پایه» یا
"base") قرار
دارند. در
چنین
مواردی فقط
تگ پایه
باید
ردیابی شود
و مقدار تگ
ثابت
میتواند
برابر با
مقدار تگ
پایه به
علاوه یک
آفست ایستا
محاسبه
گردد. برای
تگهایی که
تحت انتخاب
جایگزین یا
تکرار قرار
دارند،
بررسی
اینکه آیا
تگ پایه
مقدار عدم
تطابق (no-match)
دارد یا خیر
نیز الزامی
است (در این
حالت تگ
ثابت نیز
باید بدون
در نظر
گرفتن آفست
روی no-match تنظیم
شود). برای
تگهای
موجود در
الحاق سطح
بالا نیازی
به این
بررسی
نیست، زیرا
آنها
همیشه
مطابقت
دارند.
هشدارها را میتوان بهصورت جداگانه فعال یا غیرفعال کرد و یا به یک خطا تبدیل نمود.
- -W
- فعالسازی تمام هشدارها.
- -Werror
- تبدیل هشدارها به خطا. توجه داشته باشید که این گزینه بهتنهایی هیچ هشداری را فعال نمیکند؛ بلکه تنها بر هشدارهایی تأثیر میگذارد که تاکنون فعال شدهاند یا بعداً فعال خواهند شد.
- -W<warning>
- فعالسازی warning.
- -Wno-<warning>
- غیرفعالسازی warning.
- -Werror-<warning>
- فعالسازی warning و تلقی آن بهعنوان خطا (این گزینه متضمن -W<warning> است).
- -Wno-error-<warning>
- عدم تلقی این warning خاص بهعنوان خطا. این گزینه خودِ هشدار را غیرفعال نمیکند.
- -Wcondition-order
- هشدار در صورتی که برنامهٔ تولیدشده فرضیات ضمنی دربارهٔ شمارهگذاری شرطها داشته باشد. باید از گزینهٔ --header یا بلوک conditions برای تولید نگاشت نامهای شرط به شمارهها استفاده شود و سپس از نامهای شرط تولیدشدهٔ خودکار استفاده گردد.
- -Wempty-character-class
- هشدار در صورتی که یک عبارت باقاعده حاوی یک ردهٔ نویسهای خالی باشد. تلاش برای تطبیق یک ردهٔ نویسهای خالی بیمعنی است: این عمل همیشه باید ناموفق باشد. با این حال، به دلایل سازگاری با نسخههای پیشین، re2py ردههای نویسهای خالی را مجاز دانسته و با آنها مانند رشتههای خالی رفتار میکند. برای تغییر رفتار پیشفرض از گزینهٔ --empty-class استفاده کنید.
- -Wmatch-empty-string
- هشدار در صورتی که یک قاعده تهیپذیر باشد (با یک رشتهٔ خالی تطبیق یابد). اگر تحلیلگر لغوی در یک حلقه اجرا شود و تطبیق خالی ناخواسته باشد، ممکن است تحلیلگر لغوی بهطور غیرمنتظره در یک حلقهٔ بیپایان معلق شود.
- -Wswapped-range
- هشدار در صورتی که کران پایین یک بازه بزرگتر از کران بالای آن باشد. رفتار پیشفرض جابهجایی خاموش کرانهای بازه است.
- -Wundefined-control-flow
- هشدار در صورتی که برخی از رشتههای ورودی باعث ایجاد جریان کنترل تعریفنشده در تحلیلگر لغوی شوند (الگوهای ناقص گزارش میشوند). این یک خطای خطرناک و رایج است. این مشکل را میتوان بهسادگی با افزودن قاعدهٔ پیشفرض * که کمترین اولویت را دارد، با هر واحد کد مطابقت دارد و همیشه یک واحد کد را مصرف میکند، برطرف کرد.
- -Wunreachable-rules
- هشدار دربارهٔ قواعدی که توسط قواعد دیگر پوشانده شدهاند (shadowed) و هرگز مطابقت نخواهند یافت.
- -Wdeprecated-eof_rule
- هشدار دربارهٔ قواعد مستقل پایان ورودی $ که با تغییرات آینده از کار خواهند افتاد و نیازمند اصلاح هستند. در حال حاضر، این قواعد هنگام تداخل با سایر قواعد اولویت دارند، اما پس از معرفی نماد تعمیمیافتهٔ پایان ورودی $، ترتیب اولویت تغییر خواهد کرد و این قواعد توسط قواعد دیگر پوشانده خواهند شد.
- -Wuseless-escape
- هشدار در صورتی که یک نماد در جایی که نباید، گریز داده شود (escaped). بهطور پیشفرض، re2py بهصورت خاموش چنین گریزهایی را نادیده میگیرد، اما این مورد میتواند نشاندهندهٔ یک غلط تایپی یا خطایی در توالی گریز نیز باشد.
- -Wnondeterministic-tags
- هشدار در صورتی که یک برچسب دارای درجهٔ n-ام از عدمقطعیت باشد، که در آن n بزرگتر از ۱ است.
- -Wsentinel-in-midrule
- هشدار در صورتی که نماد نگهبان (sentinel) در میانهٔ یک قاعده رخ دهد --- این امر ممکن است باعث خواندن فراتر از انتهای بافر، از کار افتادگی یا خرابی حافظه در تحلیلگر لغوی تولیدشده شود. این هشدار تنها در صورتی اعمال میشود که از روش نگهبان برای بررسی پایان ورودی استفاده شده باشد. در صورت استفاده از پیکربندی re2c:sentinel، این وضعیت تبدیل به خطا میشود.
- -Wundefined-syntax-config
- هشدار در
صورتی که
فایل نحو
مشخصشده
با گزینهٔ
--syntax فاقد
تعاریف
برخی از
پیکربندیها
باشد. این
به نگهداری
فایلهای
نحو
تعریفشده
توسط کاربر
کمک میکند:
اگر نسخهٔ
جدیدی
پیکربندیهایی
را اضافه
کند، فایل
نحو قدیمی
یک هشدار
ایجاد کرده
و کاربر
مطلع خواهد
شد. اگر
برخی از
پیکربندیها
استفاده
نمیشوند و
نیازی به
تعریف
ندارند،
باید
صراحتاً
روی <undefined>
تنظیم شوند.
پشتیبانی از زبانهای گوناگون در re2c بر پایه ایده فایلهای نحو (syntax files) استوار است. یک فایل نحو، فایلی پیکربندی است که نحو زبان مقصد را تعریف میکند -- نه کل زبان، بلکه بخش کوچکی از آن را که توسط کد تولیدشده استفاده میشود. فایلهای نحو، re2c را بسیار انعطافپذیر میکنند، اما نباید به عنوان جایگزینی برای پیکربندیهای re2c: استفاده شوند: هدف آنها تعریف نحو زبان مقصد است، نه سفارشیسازی یک تحلیلگر لغوی خاص. تمام زبانهای پشتیبانیشده دارای فایلهای نحو پیشفرضی هستند که بخشی از توزیع به شمار میروند (زیرپوشه include/syntax را ببینید)؛ آنها همچنین در باینری re2py تعبیه شدهاند. کاربران میتوانند یک فایل نحو سفارشی ارائه دهند که چند پیکربندی را برای یکی از زبانهای پشتیبانیشده بازنویسی میکند، یا میتوانند همه پیکربندیها را مجدداً تعریف کنند (در این حالت باید از گزینه --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
- غیرصفر اگر زبان از رشتههای درون بکتیک پشتیبانی کند.
- single_quoted_strings
- غیرصفر اگر زبان از رشتههای درون نقلقول تکی پشتیبانی کند.
- indentation_sensitive
- غیرصفر اگر زبان به تورفتگی حساس باشد.
- wrap_blocks_in_braces
- غیرصفر اگر گزارههای مرکب باید درون آکولاد محصور شوند.
پیکربندیهای درجا (Inplace configurations)
الگوهای کد (Code templates)
code-template ::
name '=' code-exprs ';'
| CODE_TEMPLATE ';'
| '<undefined>' ';'
code-exprs ::
<EMPTY>
| code-exprs code-expr
code-expr ::
STRING
| VARIABLE
| optional
| list
optional ::
'(' CONDITIONAL '?' code-exprs ')'
| '(' CONDITIONAL '?' code-exprs ':' code-exprs ')'
list ::
'[' VARIABLE ':' code-exprs ']'
| '[' VARIABLE '{' NUMBER '}' ':' code-exprs ']'
| '[' VARIABLE '{' NUMBER ',' NUMBER '}' ':' code-exprs ']'
یک الگوی کد، دنبالهای از رشتههای الفاظ، متغیرها، عناصر اختیاری و فهرستها، یا ارجاعی به یک الگوی کد دیگر، یا مقدار ویژه <undefined> است. متغیرها جاینماهایی (placeholders) هستند که در مرحله تولید کد جایگزین میشوند. متغیرهای فهرستی خاص هستند: هنگام گسترش الگوهای فهرستی، re2py عبارتهای سمت راست دونقطه را چند بار تکرار میکند و هر بار رخدادهای متغیر فهرستی را با مقداری مختص به این تکرار جایگزین مینماید. فهرستها کرانهای اختیاری دارند (مقادیر منفی از انتها شمرده میشوند، برای نمونه -1 به معنای آخرین عنصر است). نامهای شرطی با یک نقطه آغاز میشوند. هر دوی شرطها و متغیرها میتوانند محلی (مختص به الگوی کد دادهشده) یا سراسری (مجاز در تمام الگوهای کد) باشند. هنگامی که re2py یک فایل نحو را میخواند، بررسی میکند که هر الگوی کد تنها از متغیرها و شرطهایی استفاده کند که در آن مجاز هستند.
برای نمونه، الگوی کد زیر ساختار 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
}
در ادامه فهرستی از تمام الگوهای کد پشتیبانیشده توسط re2py به همراه متغیرها و شرطهای محلی آنها آمده است. توجه داشته باشید که یک تعریف خاص میتواند، اما الزامی ندارد که از متغیرها و شرطهای محلی استفاده کند. هر الگوی کد استفادهنشده باید روی <undefined> تنظیم شود.
- code:var_global
- مشابه code:var_local، با این تفاوت که در سطح بالا (top-level) استفاده میشود.
- code:const_local
- تعریف یک ثابت محلی. متغیرهای پشتیبانیشده: type (نوع ثابت)، name (نام آن) و init (مقدار اولیه).
- code:const_global
- مشابه code:const_local، با این تفاوت که در سطح بالا (top-level) استفاده میشود.
- code:array_local
- تعریف یک آرایه (جدول) محلی. متغیرهای پشتیبانیشده: type (نوع عناصر آرایه)، name (نام آرایه)، size (اندازه آن)، row (یک متغیر فهرستی که به خودی خود کدی تولید نمیکند، اما عبارت فهرست را به تعداد سطرها در جدول گسترش میدهد) و elem (یک متغیر فهرستی که به تمام عناصر جدول در سطر فعلی گسترش مییابد -- در نظر گرفته شده تا در فهرست row تودرتو شود). شرط پشتیبانیشده: .const (اگر آرایه غیرقابل تغییر باشد درست است).
- code:array_global
- مشابه code:array_local، با این تفاوت که در سطح بالا (top-level) استفاده میشود.
- code:array_elem
- ارجاع به عنصری از یک آرایه (جدول). متغیرهای پشتیبانیشده: array (نام آرایه) و index (شاخص عنصر).
- code:enum
- تعریف یک شمارش (میتواند با استفاده از یک ساختار زبانی ویژه برای شمارشها تعریف شود، یا صرفاً بهصورت چند ثابت مستقل). متغیرهای پشتیبانیشده عبارتند از type (نوع شمارش تعریفشده توسط کاربر یا نوع ثابتها)، elem (متغیر فهرستی که به نام هر عضو گسترش مییابد) و init (مقداردهنده اولیه برای هر عضو). شرطها: .init (اگر مقداردهنده اولیه وجود داشته باشد درست است).
- code:enum_elem
- عنصر شمارش (عضوی از نوع شمارش تعریفشده توسط کاربر یا نام یک ثابت، بسته به نحوه تعریف code:enum). متغیرهای پشتیبانیشده عبارتند از name (نام عنصر) و type (نوع آن).
- code:assign
- دستور انتساب. متغیرهای پشتیبانیشده عبارتند از lhs (سمت چپ) و rhs (سمت راست).
- code:type_int
- نوع عدد صحیح علامتدار.
- code:type_uint
- نوع عدد صحیح بدون علامت.
- code:type_yybm
- نوع عناصر در جدول yybm.
- code:type_yytarget
- نوع عناصر در جدول yytarget.
- code:type_yyctable
- نوع عناصر در جدول yyctable.
- code:cmp_eq
- عملگر "برابر با".
- code:cmp_ne
- عملگر "نابرابر با".
- code:cmp_lt
- عملگر "کوچکتر از".
- code:cmp_gt
- عملگر "بزرگتر از".
- code:cmp_le
- عملگر "کوچکتر یا مساوی".
- code:cmp_ge
- عملگر "بزرگتر یا مساوی".
- code:if_then_else
- دستور If-then-else با یک یا چند شاخه. متغیرهای پشتیبانیشده: branch (یک متغیر فهرستی که به خودی خود کدی تولید نمیکند، اما عبارت فهرست را به تعداد شاخهها گسترش میدهد)، cond (شرط شاخه فعلی) و stmt (یک متغیر فهرستی که به تمام دستورها در شاخه فعلی گسترش مییابد). شرطها: .cond (اگر شاخه فعلی دارای شرط باشد درست است)، .many (اگر بیش از یک شاخه وجود داشته باشد درست است).
- code:if_then_else_oneline
- یک حالت خاص از code:if_then_else برای زمانی که تمام شاخهها دستورهای یکخطی دارند. اگر این مقدار <undefined> باشد، به جای آن از code:if_then_else استفاده میشود.
- code:switch
- یک دستور switch با یک یا چند case. متغیرهای پشتیبانیشده: expr (عبارت ارزیابیشده در switch) و case (یک متغیر فهرستی که به تمام گروههای case به همراه بلوک کد مربوط به آنها گسترش مییابد).
- code:switch_cases
- گروهی از 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 محاسباتی. متغیرهای پشتیبانیشده: array (جدول حاوی اطلاعات goto محاسباتی)، index (شاخص عنصر در جدول) و base (برچسب پایه، تنها زمانی استفاده میشود که .cgoto.relative درست باشد).
- code:cgoto:data
- عبارت مقداردهنده اولیه برای یک عنصر منفرد در جدول goto محاسباتی. متغیرهای پشتیبانیشده: label (برچسبی که برای مقداردهی اولیه به عنصر فعلی استفاده میشود)، type (نوع زیرین عناصر در جدول) و base (برچسب پایه - تنها زمانی استفاده میشود که .cgoto.relative درست باشد).
- code:fndecl
- اعلان تابع. متغیرهای پشتیبانیشده: name (نام تابع)، type (نوع بازگشتی)، throw (استثناهای پرتابشده توسط این تابع، به پیکربندی re2c:yyfn:throw نگاشت میشود)، arg (یک متغیر فهرستی که به خودی خود کدی تولید نمیکند، اما عبارت فهرست را به تعداد آرگومانهای تابع گسترش میدهد)، argname (نام آرگومان فعلی)، argtype (نوع آرگومان فعلی). شرط: .type (اگر تابع غیر از نوع void باشد درست است).
- code:fndef
- مشابه code:fndecl، اما برای تعریف تابع استفاده میشود، بنابراین دارای یک متغیر فهرستی اضافی stmt است که به تمام دستورها در بدنه تابع گسترش مییابد.
- code:fncall
- دستور فراخوانی تابع. متغیرهای پشتیبانیشده: name (نام تابع)، retval (مقدار l-value که مقدار بازگشتی در آن ذخیره میشود، در صورت وجود) و arg (یک متغیر فهرستی که به تمام آرگومانهای تابع گسترش مییابد). شرطها: .args (اگر تابع دارای آرگومان باشد درست است) و .retval (اگر نیاز به ذخیره مقدار بازگشتی باشد درست است).
- code:tailcall
- دستور فراخوانی دم (tail call). متغیرهای پشتیبانیشده: name (نام تابع)، و arg (یک متغیر فهرستی که به تمام آرگومانهای تابع گسترش مییابد). شرطها: .args (اگر تابع دارای آرگومان باشد درست است) و .retval (اگر تابع غیر از نوع void باشد درست است).
- code:recursive_functions
- بدنه برنامه با مدل کد --recursive-functions. متغیرهای پشتیبانیشده: fn (یک متغیر فهرستی که به خودی خود کدی تولید نمیکند، اما عبارت فهرست را به تعداد توابع گسترش میدهد)، fndecl (اعلان تابع فعلی) و fndef (تعریف تابع فعلی)..TP code:fingerprint اثرانگشت (fingerprint) در بالای فایل خروجی تولیدشده. متغیرهای پشتیبانیشده: ver (نسخهٔ re2py مورداستفاده برای تولید آن) و date (تاریخ تولید).
- code:line_info
- قالب دستورالعملهای خط (اگر این مقدار روی <undefined> تنظیم شود، هیچ دستورالعملی تولید نمیشود). متغیرهای پشتیبانیشده: line (شمارهٔ خط) و file (نام فایل).
- code:abort
- دستوری که اجرای برنامه را ساقط (abort) میکند.
- code:yydebug
- دستور YYDEBUG، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYDEBUG، yyrecord، yych (نگاشت به پیکربندیهای متناظر re2c:)، state (شماره وضعیت DFA).
- code:yypeek
- دستور YYPEEK، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYPEEK، YYCTYPE، YYINPUT، YYCURSOR، yyrecord، yych (نگاشت به پیکربندیهای متناظر re2c:). شرطها: .cast (درست اگر re2c:yych:conversion روی مقداری غیر از صفر تنظیم شده باشد).
- code:yyskip
- دستور YYSKIP، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSKIP، YYCURSOR، yyrecord (نگاشت به پیکربندیهای متناظر re2c:).
- code:yybackup
- دستور YYBACKUP، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYBACKUP، YYCURSOR، YYMARKER، yyrecord (نگاشت به پیکربندیهای متناظر re2c:).
- code:yybackupctx
- دستور YYBACKUPCTX، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYBACKUPCTX، YYCURSOR، YYCTXMARKER، yyrecord (نگاشت به پیکربندیهای متناظر re2c:).
- code:yyskip_yypeek
- دستور ترکیبی code:yyskip و code:yypeek (پیشفرض code:yyskip و به دنبال آن code:yypeek است).
- code:yypeek_yyskip
- دستور ترکیبی code:yypeek و code:yyskip (پیشفرض code:yypeek و به دنبال آن code:yyskip است).
- code:yyskip_yybackup
- دستور ترکیبی code:yyskip و code:yybackup (پیشفرض code:yyskip و به دنبال آن code:yybackup است).
- code:yybackup_yyskip
- دستور ترکیبی code:yybackup و code:yyskip (پیشفرض code:yybackup و به دنبال آن code:yyskip است).
- code:yybackup_yypeek
- دستور ترکیبی code:yybackup و code:yypeek (پیشفرض code:yybackup و به دنبال آن code:yypeek است).
- code:yyskip_yybackup_yypeek
- دستور ترکیبی code:yyskip، code:yybackup و code:yypeek (پیشفرض ``code:yyskip`` و به دنبال آن code:yybackup و به دنبال آن code:yypeek است).
- code:yybackup_yypeek_yyskip
- دستور ترکیبی code:yybackup، code:yypeek و code:yyskip (پیشفرض ``code:yybackup`` و به دنبال آن code:yypeek و به دنبال آن code:yyskip است).
- code:yyrestore
- دستور YYRESTORE، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYRESTORE، YYCURSOR، YYMARKER، yyrecord (نگاشت به پیکربندیهای متناظر re2c:).
- code:yyrestorectx
- دستور YYRESTORECTX، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYRESTORECTX، YYCURSOR، YYCTXMARKER، yyrecord (نگاشت به پیکربندیهای متناظر re2c:).
- code:yyrestoretag
- دستور YYRESTORETAG، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYRESTORETAG، YYCURSOR، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، tag (نام متغیر برچسب مورداستفاده برای بازیابی موقعیت).
- code:yyshift
- دستور YYSHIFT، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSHIFT، YYCURSOR، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، offset (تعداد واحدهای کد برای جابهجایی موقعیت فعلی).
- code:yyshiftstag
- دستور YYSHIFTSTAG، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSHIFTSTAG، yyrecord، negative (نگاشت به پیکربندیهای متناظر re2c:)، tag (متغیر برچسبی که باید جابهجا شود)، offset (تعداد واحدهای کد برای جابهجایی). شرطها: .nested (درست درصورتیکه این یک برچسب تودرتو باشد -- در این حالت ممکن است مقدار آن برابر با re2c:tags:negative باشد، که نباید جابهجا شود).
- code:yyshiftmtag
- دستور YYSHIFTMTAG، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSHIFTMTAG (نگاشت به پیکربندی متناظر re2c:)، tag (متغیر برچسبی که باید جابهجا شود)، offset (تعداد واحدهای کد برای جابهجایی).
- code:yystagp
- دستور YYSTAGP، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSTAGP، YYCURSOR، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، tag (متغیر برچسبی که باید بهروزرسانی شود).
- code:yymtagp
- دستور YYMTAGP، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYMTAGP (نگاشت به پیکربندی متناظر re2c:)، tag (متغیر برچسبی که باید بهروزرسانی شود).
- code:yystagn
- دستور YYSTAGN، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSTAGN، negative، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، tag (متغیر برچسبی که باید بهروزرسانی شود).
- code:yymtagn
- دستور YYMTAGN، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYMTAGN (نگاشت به پیکربندی متناظر re2c:)، tag (متغیر برچسبی که باید بهروزرسانی شود).
- code:yycopystag
- دستور YYCOPYSTAG، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYCOPYSTAG، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، lhs، rhs (متغیرهای برچسب سمت چپ و راست در عملیات رونوشت).
- code:yycopymtag
- دستور YYCOPYMTAG، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYCOPYMTAG، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، lhs، rhs (متغیرهای برچسب سمت چپ و راست در عملیات رونوشت).
- code:yygetaccept
- دستور YYGETACCEPT، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYGETACCEPT، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، var (نگاشت به پیکربندی re2c:yyaccept).
- code:yysetaccept
- دستور YYSETACCEPT، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSETACCEPT، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، var (نگاشت به پیکربندی re2c:yyaccept) و val (مقدار عددی قاعدهٔ پذیرفتهشده).
- code:yygetcond
- دستور YYGETCOND، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYGETCOND، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، var (نگاشت به پیکربندی re2c:yycond).
- code:yysetcond
- دستور YYSETCOND، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSETCOND، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، var (نگاشت به پیکربندی re2c:yycond) و val (شناسهٔ عددی شرط).
- code:yygetstate
- دستور YYGETSTATE، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYGETSTATE، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، var (نگاشت به پیکربندی re2c:yystate).
- code:yysetstate
- دستور YYSETSTATE، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSETSTATE، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، var (نگاشت به پیکربندی re2c:yystate) و val (شماره وضعیت).
- code:yylessthan
- دستور YYLESSTHAN، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYLESSTHAN، YYCURSOR، YYLIMIT، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، need (تعداد واحدهای کد که باید بررسی شوند). شرط: .many (درست درصورتیکه need بیشتر از یک باشد)..TP code:yyend عبارت YYEND، احتمالاً اختصاصیشده برای APIهای مختلف. متغیرهای پشتیبانیشده: YYEND، YYCURSOR، YYLIMIT.
- code:yybm_filter
- شرطی که برای فیلتر کردن مقادیر yych که توسط جدول yybm پوشش داده نشدهاند به کار میرود (همراه با گزینهٔ --bitmaps استفاده میشود). متغیر پشتیبانیشده: yych (به پیکربندی re2c:yych نگاشت میشود).
- code:yybm_match
- قالب بررسی جدول yybm (تولیدشده با گزینهٔ --bitmaps). متغیرهای پشتیبانیشده: yybm، yych (به پیکربندیهای متناظر re2c: نگاشت میشوند)، offset (آفست در جدول yybm که باید به yych اضافه شود) و mask (ماسک بیتی که باید روی مدخل جدول اعمال شود تا مقدار بولی مورد بررسی بازیابی گردد)
- code:yytarget_filter
- شرطی که برای فیلتر کردن مقادیر yych که توسط جدول yytarget پوشش داده نشدهاند به کار میرود (همراه با گزینهٔ --computed-gotos استفاده میشود). متغیر پشتیبانیشده: yych (به پیکربندی re2c:yych نگاشت میشود).
در اینجا فهرستی از تمام متغیرهای سراسریِ مجاز در فایلهای نحو آمده است:
- nl
- یک خط جدید (newline).
- indent
- متغیری که هیچ کدی تولید نمیکند، اما اثر جانبیِ افزایش سطح تورفتگی را دارد.
- dedent
- متغیری که هیچ کدی تولید نمیکند، اما اثر جانبیِ کاهش سطح تورفتگی را دارد.
- topindent
- رشتهٔ تورفتگی برای گزارهٔ کنونی. سطح تورفتگی توسط مولد کد ردیابی شده و بهطور خودکار بهروزرسانی میشود.
در اینجا فهرستی از تمام شرطهای سراسریِ مجاز در فایلهای نحو آمده است:
- .api.simple
- درست است اگر از 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
- درست است اگر حلقههای تولیدشده توسط re2py باید دارای برچسب باشند (re2c:label:yyloop روی یک رشتهٔ غیرخالی تنظیم شده باشد).
- .monadic
- درست است اگر کد تولیدشده باید مونادیک باشد (re2c:monadic = 1). این مورد تنها برای زبانهای تابعی محض کاربرد دارد.
- .start_conditions
- درست است اگر شرایط شروع فعال باشند (--start-conditions).
- .storable_state
- درست است اگر وضعیت قابل ذخیرهسازی فعال باشد (--storable-state).
- .unsafe
- درست است اگر re2py برای تولید کد سریعتر باید از بلوکهای "unsafe" استفاده کند (--unsafe، re2c:unsafe = 1). این مورد تنها برای زبانهایی کاربرد دارد که دارای ویژگی "unsafe" هستند.
- .version
- درست است اگر اثرانگشت تولیدشده باید شامل نسخهٔ re2py باشد.
- .yyfill.enable
- درست است اگر YYFILL فعال باشد (re2c:yyfill:enable = 1).
- .yyfn.throw
- درست است اگر پیکربندی re2c:yyfn:throw روی یک رشتهٔ غیرخالی تعریف شده باشد.
یکی از چالشهای اصلی برای تحلیلگر لغوی (lexer)، دانستن زمان توقف است. چندین شرط پایاندهنده وجود دارد:
- تحلیلگر لغوی ممکن است با قاعدهای (شامل قاعده پیشفرض *) تطابق یابد و به یک وضعیت نهایی برسد
- تحلیلگر لغوی ممکن است در تطبیق با هر قاعدهای شکست بخورد و به یک وضعیت پیشفرض برسد
- تحلیلگر لغوی ممکن است به پایان ورودی برسد
دو شرط نخست، تحلیلگر لغوی را به روشی "طبیعی" خاتمه میدهند: تحلیلگر به وضعیتی بدون انتقالهای خروجی میرسد و تطبیق بهطور خودکار متوقف میشود. اما شرط سوم، یعنی پایان ورودی، متفاوت است: این حالت میتواند در هر وضعیتی رخ دهد و تحلیلگر باید بتواند آن را مدیریت کند. بررسی پایان ورودی، روند کار عادی تحلیلگر لغوی را مختل کرده و شاخههای شرطی را به برنامه تولیدشده میافزاید؛ از این رو بهحداقلرساندن تعداد این بررسیها ضروری است. re2py از چند روش مختلف برای مدیریت پایان ورودی پشتیبانی میکند. انتخاب روش مناسب به پیچیدگی عبارات باقاعده، نیاز به بافرسازی، ملاحظات کارایی و عوامل دیگر بستگی دارد. در ادامه فهرستی از این روشها آمده است:
- نگهبان (Sentinel). این روش نیاز به بررسیهای پایان ورودی را بهطور کامل برطرف میکند. ساده و کارآمد است، اما محدود به حالتی است که یک نویسه "نگهبان" طبیعی وجود داشته باشد که هرگز در ورودی معتبر رخ ندهد. این نویسه ممکن است همچنان در ورودی نامعتبر ظاهر شود، اما نباید در عبارات باقاعده مجاز باشد، مگر شاید به عنوان آخرین نویسه یک قاعده. نویسه نگهبان به انتهای ورودی الحاق میشود و به عنوان سیگنال توقف عمل میکند: زمانی که تحلیلگر لغوی این نویسه را بخواند، یا یک خطای نحوی رخ داده یا پایان ورودی است. در هر دو حالت، تحلیلگر لغوی باید متوقف شود. این روش در صورتی استفاده میشود که YYFILL با re2c:yyfill:enable = 0; غیرفعال شده باشد و re2c:eof دارای مقدار پیشفرض -1 باشد.
- نگهبان همراه با بررسی مرزها (Sentinel with bounds checks). این روش جامع است: اجازه میدهد هر ورودی بدون اعمال محدودیت بر عبارات باقاعده مدیریت شود. ایده اصلی، کاهش تعداد بررسیهای پایان ورودی از طریق انجام آنها صرفاً روی نویسههایی خاص است. مشابه روش "نگهبان"، یکی از نویسهها به عنوان "نگهبان" انتخاب شده و به انتهای ورودی الحاق میشود. با این حال، هیچ محدودیتی در محل وقوع نویسه نگهبان وجود ندارد (در واقع هر نویسهای میتواند به عنوان نگهبان انتخاب شود). هنگامی که تحلیلگر لغوی این نویسه را میخواند، علاوه بر آن، یک بررسی مرزی نیز انجام میدهد. اگر موقعیت فعلی در محدوده مجاز باشد، تحلیلگر تطبیق را از سر میگیرد و با نویسه نگهبان مانند یک نویسه عادی رفتار میکند. در غیر این صورت، YYFILL را فراخوانی میکند (مگر اینکه غیرفعال شده باشد). اگر ورودی بیشتری ارائه شود، تحلیلگر لغوی آخرین نویسه را مجدداً تطبیق داده و طوری ادامه میدهد که گویی نویسه نگهبان وجود نداشته است. در غیر این صورت، ورودی واقعاً به پایان رسیده و تحلیلگر متوقف میشود. این روش زمانی استفاده میشود که re2c:eof مقداری غیرمنفی داشته باشد (باید روی مقدار عددی نویسه نگهبان تنظیم شود). استفاده از YYFILL اختیاری است.
- بررسی مرزها همراه با پدینگ (Bounds checks with padding). این روش جامع است و ممکن است از روش "نگهبان همراه با بررسی مرزها" سریعتر باشد، اما پیچیدهتر نیز هست. ایده اصلی، افرازبندی وضعیتهای DFA به مؤلفههای قویاً همبند (SCCها) و ایجاد یک بررسی منفرد به ازای هر SCC است، به میزانی نویسه که برای پوشش طولانیترین مسیر بدون حلقه در آن SCC کافی باشد. این کار تعداد بررسیها را کاهش میدهد، اما مشکلی در رابطه با لِکسِمهای (lexemes) کوتاه در انتهای ورودی ایجاد میکند، زیرا این بررسی نیازمند نویسههای کافی برای پوشش طولانیترین لِکسِم است. این مشکل را میتوان با پدینگ (پر کردن) ورودی با چند نویسه ساختگی حل کرد که پسوند لِکسِم معتبری را تشکیل ندهند (به طوری که تحلیلگر نتواند آنها را مطابقت دهد). طول پدینگ باید YYMAXFILL باشد که با یک بلوک max تولید میشود. اگر ورودی کافی نباشد، تحلیلگر YYFILL را فراخوانی میکند که باید دستکم تعداد نویسههای مورد نیاز را فراهم کند یا بازنگردد. این روش در صورتی استفاده میشود که YYFILL فعال بوده و re2c:eof برابر -1 باشد (این پیکربندی پیشفرض است).
- بررسیهای سفارشی (Custom checks). رابط عمومی (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 جلوگیری میکند.
# re2py $INPUT -o $OUTPUT
# expect a null-terminated string
def lex(yyinput):
yycursor = 0
count = 0
while True: %{
re2c:yyfill:enable = 0;
re2c:indent:top = 2;
* { return -1 }
[\x00] { return count }
[ ]+ { break }
[a-z]+ {
count += 1
break
}
%}
assert lex(b"\0") == 0
assert lex(b"one two three\0") == 3
assert lex(b"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 میشود.
# re2py $INPUT -o $OUTPUT
# expect a null-terminated string
def lex(yyinput):
yycursor = 0
yylimit = len(yyinput) - 1 # terminating null not included
count = 0
while True: %{
re2c:yyfill:enable = 0;
re2c:eof = 0;
re2c:indent:top = 2;
str = ['] ([^'\\] | [\\][^])* ['];
* { return -1 }
$ { return count }
[ ]+ { break }
str {
count += 1
break
}
%}
def test(str, count):
# termunating null not included in `lim`
assert count == lex(str)
test(b"\0", 0);
test(b"'qu\0tes' 'are' 'fine: \\'' \0", 3);
test(b"'unterminated\\'\0", -1)
این مثال از بررسیهای محدوده همراه با پدینگ (padding) برای مدیریت انتهای ورودی استفاده میکند (این روش بهطور پیشفرض فعال است). این برنامه رشتههای محصور در علامت نقلقول تکی که با فاصله از هم جدا شدهاند را میشمارد. پدینگی به اندازهٔ YYMAXFILL نویسهٔ null به انتهای ورودی الحاق شده است، که مقدار YYMAXFILL با یک بلوک max بهطور خودکار تولید میشود. نیازی نیست که حتماً از null برای پدینگ استفاده شود --- هر نویسهای تا زمانی که پسوند معتبری برای یک لکسیم (lexeme) نسازد، قابل استفاده است (در این مثال، پدینگ نباید شامل علامت نقلقول تکی باشد، چرا که ممکن است با پسوند یک رشتهٔ محصور در نقلقول تکی اشتباه گرفته شود). یک قاعدهٔ "stop" وجود دارد که با نخستین نویسهٔ پدینگ (null) تطابق مییابد و لکسر را متوقف میکند (توجه داشته باشید که این قاعده بررسی میکند آیا null در ابتدای پدینگ قرار دارد یا خیر، در غیر این صورت یک خطای نحوی است). بررسیهای محدوده تنها در برخی وضعیتها تولید میشوند که بر اساس مؤلفههای قویاً همبند در اتوماتای زیرین تعیین میگردند. بررسیها در API عمومی به شکل (YYLIMIT - YYCURSOR) < n یا YYLESSTHAN(n) هستند، که در آن n حداقل تعداد نویسههای لازم برای ادامهٔ کار لکسر است (این بدان معنا نیز هست که بررسی محدودهٔ بعدی حداکثر در n نویسهٔ آینده رخ خواهد داد). اگر شرط بررسی درست باشد، لکسر به انتهای ورودی رسیده است و YYFILL(n) را فراخوانی میکند که باید یا حداقل n نویسهٔ ورودی را فراهم کند یا بازنگردد. در این مثال YYFILL همیشه با شکست مواجه میشود و لکسر را با یک خطا خاتمه میدهد (که مشکلی ایجاد نمیکند زیرا کل ورودی در یک بافر جا میگیرد). برای مشاهدهٔ مثالی که بافر ورودی را مجدداً با YYFILL پر میکند، بخش YYFILL with padding را ببینید.
# re2py $INPUT -o $OUTPUT
%{max %}
def lex(yyinput):
yycursor = 0
yylimit = len(yyinput)
count = 0
while True: %{
re2c:YYFILL = "return -1";
re2c:indent:top = 2;
str = ['] ([^'\\] | [\\][^])* ['];
[\x00] {
# check that it is the sentinel, not some unexpected null
return count if yycursor == yylimit - YYMAXFILL + 1 else -1
}
str {
count += 1
break
}
[ ]+ { break }
* { return -1 }
%}
def test(str, count):
padded_str = str + (b"\0" * YYMAXFILL)
assert lex(padded_str) == count
test(b"", 0)
test(b"'unterminated\\'", -1)
test(b"'qu\x00tes' 'are' 'fine: \\'' ", 3)
test(b"'unexpected \x00 null", -1)
بررسیهای سفارشی (Custom checks)
این مثال از یک روش سفارشی برای مدیریت پایان ورودی بر پایهٔ generic API استفاده میکند. این برنامه رشتههای محصور در علامت نقلقول تکی که با فاصله از هم جدا شدهاند را میشمارد. این مثال مشابه با مثال sentinel است، با این تفاوت که ورودی به null ختم نمیشود. برای جبران نبود نویسهٔ نگهبان (sentinel) در پایان ورودی، دستور YYPEEK بازتعریف شده است تا پیش از خواندن نویسهٔ ورودی بعدی، یک بررسی محدوده انجام دهد. این روش ناکارآمد است زیرا بررسیها به دفعات بسیار زیاد انجام میشوند. اگر شرط بررسی برقرار نباشد، YYPEEK نویسهٔ واقعی را برمیگرداند، در غیر این صورت یک نویسهٔ نگهبان جعلی را برمیگرداند.
# re2py $INPUT -o $OUTPUT
# expect a string without terminating null
def lex(str):
cur = 0
lim = len(str)
count = 0
while True: %{
re2c:api = generic;
re2c:YYPEEK = "str[cur] if cur < lim else 0";
re2c:YYSKIP = "cur += 1";
re2c:yyfill:enable = 0;
re2c:indent:top = 2;
* { return -1 }
[\x00] { return count }
[ ]+ { break }
[a-z]+ {
count += 1
break
}
%}
assert lex(b"") == 0
assert lex(b"one two three") == 3
assert lex(b"f0ur") == -1
نیاز به بافرسازی (buffering) زمانی پیش میآید که ورودی نتواند بهیکباره در حافظه نگاشت شود: یا بیش از حد بزرگ است، یا به شیوهٔ جریانی دریافت میشود (مانند خواندن از یک سوکت). روش معمول در چنین سناریوهایی، تخصیص یک بافر حافظه با اندازهٔ ثابت و پردازش ورودی در تکههایی (chunks) است که در این بافر میگنجند. هنگامی که تکهٔ فعلی پردازش شد، به بیرون منتقل شده و دادههای جدید جایگزین آن میشوند. در عمل این روند تا حدی پیچیدهتر است، زیرا وضعیت تحلیلگر واژگانی (lexer) از یک موقعیت ورودی منفرد تشکیل نشده، بلکه متشکل از مجموعهای از موقعیتهای مرتبط با یکدیگر است:
- مکاننما (cursor): نویسهٔ ورودی بعدی که باید خوانده شود (YYCURSOR در API اشارهگر C یا YYSKIP/YYPEEK در API عمومی).
- حد (limit): موقعیتِ پس از آخرین نویسهٔ ورودی در دسترس (YYLIMIT در API اشارهگر C، مدیریت بهصورت ضمنی توسط YYLESSTHAN در API عمومی).
- نشانگر (marker): موقعیت تازهترین تطابق، در صورت وجود (YYMARKER در API پیشفرض یا YYBACKUP/YYRESTORE در API عمومی).
- توکن (token): آغاز لکسم (lexeme) فعلی (در API مربوط به re2py ضمنی است، زیرا برای عملکرد عادی lexer نیاز نیست و میتواند توسط کاربر تعریف و بهروزرسانی شود).
- نشانگر زمینه (context marker): موقعیت زمینهٔ انتهایی (trailing context) (YYCTXMARKER در API اشارهگر C یا YYBACKUPCTX/YYRESTORECTX در API عمومی).
- متغیرهای برچسب (tag variables): موقعیتهای زیرتطابق (تعریفشده با بلوکهای stags و mtags و توابع پایهٔ API عمومی YYSTAGP/YYSTAGN/YYMTAGP/YYMTAGN).
همهٔ این موقعیتها در تمام سناریوها استفاده نمیشوند، اما در صورت استفاده، باید توسط YYFILL بهروزرسانی شوند. تمام موقعیتهای فعال در بازهٔ بین token و cursor قرار دارند؛ بنابراین هر چیزی میان ابتدای بافر و token را میتوان دور انداخت، بازهٔ از token تا limit باید به ابتدای بافر منتقل شود، و فضای خالی ایجادشده در انتهای بافر با دادههای تازه پر گردد. به منظور جلوگیری از فراخوانیهای مکرر YYFILL، بهتر است تا جای ممکن نویسههای ورودی بیشتری پر شوند (حتی اگر نویسههای کمتری برای از سرگیری lexer کافی باشد). جزئیات پیادهسازی YYFILL بسته به روش مدیریت EOF مورد استفاده اندکی متفاوت است: حالت قاعدهٔ EOF تا حدی سادهتر از حالت بررسی مرزها همراه با فاصلهگذاری (padding) است. همچنین توجه داشته باشید که اگر گزینهٔ -f --storable-state استفاده شود، YYFILL معنای کمی متفاوتی خواهد داشت (که در بخش مربوط به وضعیت قابل ذخیرهسازی شرح داده شده است).
YYFILL با نگهبان (YYFILL with sentinel)
اگر از قاعدهٔ EOF استفاده شود، YYFILL یک ساختار پایهٔ شبهتابع است که هیچ آرگومانی نمیپذیرد و مقداری را برمیگرداند که با صفر ارزیابی میشود. فراخوانی YYFILL با شرط YYLIMIT <= YYCURSOR در API اشارهگر C و YYLESSTHAN() در API عمومی فعال میشود. مقدار بازگشتی غیرصفر نشاندهندهٔ شکست YYFILL است. یک فراخوانی موفق YYFILL باید حداقل یک نویسه را فراهم کند و موقعیتهای ورودی را بر همین اساس تنظیم نماید. حد (Limit) همیشه باید روی یک واحد پس از آخرین موقعیت ورودی در بافر تنظیم شود، و نویسهٔ موجود در موقعیت limit باید نماد نگهبان (sentinel) مشخصشده توسط پیکربندی re2c:eof باشد. شکلهای زیر مکانهای نسبی موقعیتهای ورودی در بافر را پیش و پس از فراخوانی YYFILL نشان میدهند (نماد نگهبان با # نشانهگذاری شده و شکل دوم حالتی را نشان میدهد که ورودی کافی برای پر کردن تمام بافر وجود ندارد).
<-- shift -->
>-A------------B---------C------------D#-----------E->
buffer token marker limit,
cursor
>-A------------B---------C------------D------------E#->
buffer, marker cursor limit
token
<-- shift -->
>-A------------B---------C------------D#--E (EOF)
buffer token marker limit,
cursor
>-A------------B---------C------------D---E#........
buffer, marker cursor limit
token
در ادامه مثالی از برنامهای آمده است که فایل ورودی input.txt را در تکههای ۴۰۹۶ بایتی میخواند و از قاعدهٔ EOF استفاده میکند.
# re2py $INPUT -o $OUTPUT
from enum import Enum
import os
BUFSIZE = 4096
class State:
def __init__(self, fname):
self.file = open(fname, "rb")
self.yyinput = bytearray(BUFSIZE)
self.yylimit = BUFSIZE - 1 # exclude terminating null
self.yycursor = self.yylimit
self.yymarker = self.yylimit
self.token = self.yylimit
self.eof = False
def __del__(self):
self.file.close()
class Status(Enum):
OK = 0
EOF = 1
LONG_LEXEME = 2
def fill(st):
if st.eof:
return Status.EOF
# Error: lexeme too long. In real life could reallocate a larger buffer.
if st.token < 1:
return Status.LONG_LEXEME
# Shift buffer contents (discard everything up to the current token).
st.yyinput = st.yyinput[st.token:st.yylimit]
st.yycursor -= st.token;
st.yymarker -= st.token;
st.yylimit -= st.token;
st.token = 0;
# Fill free space at the end of buffer with new data from file.
bytes = st.file.read(BUFSIZE - st.yylimit - 1) # -1 for sentinel
if not bytes:
st.eof = True # end of file
else:
st.yylimit += len(bytes);
st.yyinput += bytes
st.yyinput += b'\0' # append sentinel
return Status.OK
def lex(yyrecord, count):
while True:
yyrecord.token = yyrecord.yycursor
%{
re2c:api = record;
re2c:define:YYFILL = "fill(yyrecord) == Status.OK";
re2c:eof = 0;
re2c:indent:top = 2;
str = ['] ([^'\\] | [\\][^])* ['];
* { return -1 }
$ { return count }
[ ]+ { break }
str {
count += 1
break
}
%}
def main():
fname = "input"
# Prepare input file.
f = open(fname, "w")
for i in range(BUFSIZE):
f.write("'qu\0tes' 'are' 'fine: \\'' ")
f.close()
# Run lexer on the prepared file.
st = State(fname)
assert lex(st, 0) == 3 * BUFSIZE
# Cleanup.
os.remove(fname)
if __name__ == '__main__':
main()
در حالت پیشفرض (زمانی که از قاعدهٔ EOF استفاده نمیشود) YYFILL یک سازهٔ اولیهٔ شبهتابع است که یک آرگومان دریافت میکند و هیچ مقداری را بازنمیگرداند. فراخوانی YYFILL با شرط (YYLIMIT - YYCURSOR) < n در API اشارهگر C و YYLESSTHAN(n) در API عمومی آغاز میشود. آرگومان ارسالشده به YYFILL حداقل تعداد نویسههایی است که باید فراهم شوند. اگر در فراهم کردن آن ناموفق باشد، YYFILL نباید به لکسر بازگردد (به همین دلیل بهتر است بهصورت ماکرویی پیادهسازی شود که در صورت شکست، از تابع فراخواننده بازمیگردد). در صورت فراخوانی موفقیتآمیز 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 را در تکههای ۴۰۹۶ بایتی میخواند و از بررسی محدودهها همراه با لایهگذاری (padding) استفاده میکند.
# re2py $INPUT -o $OUTPUT
from enum import Enum
import os
BUFSIZE = 4096
%{max %}
class State:
def __init__(self, fname):
self.file = open(fname, "rb")
self.yyinput = bytearray(BUFSIZE)
self.yylimit = BUFSIZE - YYMAXFILL
self.yycursor = self.yylimit
self.yymarker = self.yylimit
self.token = self.yylimit
self.eof = False
def __del__(self):
self.file.close()
class Status(Enum):
OK = 0
EOF = 1
LONG_LEXEME = 2
def fill(st, need):
if st.eof:
return Status.EOF
# Error: lexeme too long. In real life could reallocate a larger buffer.
if st.token < need:
return Status.LONG_LEXEME
# Shift buffer contents (discard everything up to the current token).
st.yyinput = st.yyinput[st.token:st.yylimit]
st.yycursor -= st.token;
st.yymarker -= st.token;
st.yylimit -= st.token;
st.token = 0;
# Fill free space at the end of buffer with new data from file.
bytes = st.file.read(BUFSIZE - st.yylimit - 1) # -1 for sentinel
if not bytes:
st.eof = True # end of file
st.yylimit += YYMAXFILL
st.yyinput += b"\0" * YYMAXFILL
else:
st.yylimit += len(bytes);
st.yyinput += bytes
return Status.OK
def lex(yyrecord):
count = 0
while True:
yyrecord.token = yyrecord.yycursor
%{
re2c:api = record;
re2c:YYFILL = "if fill(yyrecord, @@) != Status.OK: return -1";
re2c:indent:top = 2;
str = ['] ([^'\\] | [\\][^])* ['];
[\x00] {
# Check that it is the sentinel, not some unexpected null.
return count if yyrecord.token == yyrecord.yylimit - YYMAXFILL else -1
}
str {
count += 1
break
}
[ ]+ { break }
* { return -1 }
%}
def main():
fname = "input"
# Prepare input file.
f = open(fname, "w")
for i in range(BUFSIZE):
f.write("'qu\0tes' 'are' 'fine: \\'' ")
f.close()
# Run lexer on the prepared file.
st = State(fname)
assert lex(st) == 3 * BUFSIZE
# Cleanup.
os.remove(fname)
if __name__ == '__main__':
main()
بلوکهای چندگانه (Multiple blocks)
گاهی لازم است چندین تحلیلگر لغوی (lexer) مرتبط با یکدیگر داشته باشیم (برای مثال، اگر یک ماشین حالت سطحبالا وجود داشته باشد که بین حالتهای تحلیلگر لغوی جابهجا شود). این ساختار را میتوان با استفاده از چندین بلوک متصل re2py پیادهسازی کرد. گزینه دیگر استفاده از شرایط شروع (start conditions) است.
پیادهسازی اتصالات بین بلوکها به زبان مقصد بستگی دارد. در زبانهایی که دارای دستور goto هستند (مانند C/C++ و Go) میتوان تمام بلوکها را درون یک تابع قرار داد، بهطوری که پیش از هر یک از آنها یک برچسب (label) قرار گیرد. انتقال از یک بلوک به بلوک دیگر یک goto ساده است. در زبانهایی که فاقد دستور goto هستند (مانند Rust)، استفاده از یک حلقه همراه با switch روی یک متغیر وضعیت (state variable) ضروری است، مشابه حلقه/switch مربوط به yystate که توسط re2py تولید میشود؛ یا اینکه میتوان هر بلوک را در یک تابع کپسولهسازی کرد و از فراخوانی توابع بهره برد.
مثال زیر از چندین بلوک برای تجزیه اعداد باینری (مبنای ۲)، اکتال (مبنای ۸)، دهدهی (مبنای ۱۰) و هگزادسیمال (مبنای ۱۶) استفاده میکند. هر مبنا بلوک مخصوص به خود را دارد. بلوک اولیه، مبنا را مشخص کرده و کار را به بلوکهای دیگر ارجاع میدهد. پیکربندیهای مشترک در یک بلوک مجزا در ابتدای برنامه تعریف شدهاند؛ این پیکربندیها توسط سایر بلوکها به ارث برده میشوند.
# re2py $INPUT -o $OUTPUT
class State:
def __init__(self, str):
self.yyinput = str
self.yycursor = 0
self.yymarker = 0
# Common re2c definitions shared between all functions.
%{
re2c:api = record;
re2c:yyrecord = st;
re2c:yyfill:enable = 0;
re2c:indent:top = 2;
%}
def parse_u32(str):
st = State(str)
%{local
re2c:indent:top = 1;
'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 None }
%}
def parse_bin(st):
n = 0
while True: %{
[01] {
n = n * 2 + (st.yyinput[st.yycursor - 1] - 48)
break
}
* { return n }
%}
def parse_oct(st):
n = 0
while True: %{
[0-7] {
n = n * 8 + (st.yyinput[st.yycursor - 1] - 48)
break
}
* { return n }
%}
def parse_dec(st):
n = 0
while True: %{
[0-9] {
n = n * 10 + (st.yyinput[st.yycursor - 1] - 48)
break
}
* { return n }
%}
def parse_hex(st):
n = 0
while True: %{
[0-9] {
n = n * 16 + (st.yyinput[st.yycursor - 1] - 48)
break
}
[a-f] {
n = n * 16 + (st.yyinput[st.yycursor - 1] - 87)
break
}
[A-F] {
n = n * 16 + (st.yyinput[st.yycursor - 1] - 55)
break
}
* { return n }
%}
assert parse_u32(b"\0") == None
assert parse_u32(b"1234567890\0") == 1234567890
assert parse_u32(b"0b1101\0") == 13
assert parse_u32(b"0x7Fe\0") == 2046
assert parse_u32(b"0644\0") == 420
assert parse_u32(b"9999999999\0") == 9999999999
شرایط شروع با گزینهٔ --start-conditions فعال میشوند. آنها روشی برای کدگذاری چندین خودکارهٔ به هم مرتبط در یک بلوک re2py مشترک ارائه میدهند.
هر شرط متناظر با یک خودکارهٔ منفرد است و دارای یک نام یکتاست که توسط کاربر مشخص میشود، و یک شمارهٔ داخلی یکتا دارد که توسط re2py تعریف میگردد. از این شمارهها برای جابهجایی میان شرایط استفاده میشود: کد تولیدشده از توابع اولیهٔ 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 پرش میکند.
کدی که re2py برای شرایط تولید میکند بستگی به این دارد که آیا re2py برای کدگذاری خودکارهها از رویکرد goto/label استفاده میکند یا رویکرد loop/switch.
در زبانهایی که دارای دستور goto هستند (مانند C/C++ و Go)، شرایط بهطور طبیعی بهصورت بلوکهایی از کد با پیشوند برچسبهایی به شکل yyc_<cond> پیادهسازی میشوند، که در آن cond نام یک شرط است (پیشوند برچسب را میتوان با re2c:cond:prefix تغییر داد). گذارها میان شرایط با استفاده از goto و برچسبهای شرط پیادهسازی میشوند. پیش از همهٔ شرایط، re2py یک switch اولیه بر روی YYGETSTATE تولید میکند که به وضعیت آغازین شرط کنونی پرش میکند. قواعد میانبر :=> این switch اولیه را دور میزنند و مستقیماً به شرط مشخصشده پرش میکنند (میتوان از re2c:cond:goto برای تغییر رفتار پیشفرض استفاده کرد). قواعد دارای کنشهای معنایی بهطور خودکار به شرط بعدی پرش نمیکنند؛ این کار باید توسط کد کنشِ تعریفشده توسط کاربر انجام شود.
در زبانهایی که goto ندارند (مانند Rust)، re2py از متغیر yystate برای ذخیرهٔ شمارههای شرط دوباره استفاده میکند. هر شرط یک شناسهٔ عددی برابر با شمارهٔ وضعیت آغازین خود دریافت میکند، و یک switch میان شرایط، هیچ تفاوتی با یک switch میان وضعیتهای DFA در یک شرط واحد ندارد. نیازی به یک switch اولیهٔ جداگانه برای شرط نیست. (از آنجا که رویکرد مشابهی برای پیادهسازی وضعیتهای قابل ذخیره بهکار میرود، در صورت استفادهٔ همزمان از وضعیتهای قابل ذخیره و شرایط، YYGETCOND/YYSETCOND زاید هستند).
برنامهٔ زیر از شرایط شروع برای تجزیهٔ اعداد دودویی (باینری)، هشتهشتی (اکتال)، دهدهی (دسیمال) و شانزدهشانزدهی (هگزادسیمال) استفاده میکند. یک بلوک منفرد وجود دارد که در آن هر مبنا شرط اختصاصی خود را دارد، و شرط اولیه به همهٔ آنها متصل است. متغیر تعریفشده توسط کاربر cond شمارهٔ شرط کنونی را ذخیره میکند؛ این متغیر با شمارهٔ شرط اولیه که با بلوک conditions تولید شده مقداردهی اولیه میشود.
# re2py $INPUT -o $OUTPUT -c
%{conditions %}
def parse_u32(yyinput):
yycursor = 0
yycond = YYC_INIT
num = 0
while True: %{
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 None }
<BIN> [01] {
num = num * 2 + (yyinput[yycursor - 1] - 48)
break
}
<OCT> [0-7] {
num = num * 8 + (yyinput[yycursor - 1] - 48)
break
}
<DEC> [0-9] {
num = num * 10 + (yyinput[yycursor - 1] - 48)
break
}
<HEX> [0-9] {
num = num * 16 + (yyinput[yycursor - 1] - 48)
break
}
<HEX> [a-f] {
num = num * 16 + (yyinput[yycursor - 1] - 87)
break
}
<HEX> [A-F] {
num = num * 16 + (yyinput[yycursor - 1] - 55)
break
}
<BIN, OCT, DEC, HEX> * { return num }
%}
assert parse_u32(b"\0") == None
assert parse_u32(b"1234567890\0") == 1234567890
assert parse_u32(b"0b1101\0") == 13
assert parse_u32(b"0x7Fe\0") == 2046
assert parse_u32(b"0644\0") == 420
assert parse_u32(b"9999999999\0") == 9999999999
با گزینهٔ --storable-state، ابزار re2py یک تحلیلگر لغوی (lexer) تولید میکند که میتواند وضعیت فعلی خود را ذخیره کرده، کنترل را به فراخواننده بازگرداند، و بعداً دقیقاً از همان نقطهای که متوقف شده بود ادامه دهد. حالت پیشفرض عملکرد در re2py یک مدل «کششی» ("pull") است که در آن لکسر هر زمان به داده نیاز داشته باشد، ورودی بیشتری دریافت میکند. این رفتار ممکن است در مواردی که ورودی به صورت تکهتکه در دسترس قرار میگیرد غیرقابلقبول باشد (برای نمونه، هنگامی که لکسر توسط یک تجزیهکننده (parser) فراخوانی میشود، یا هنگامی که برنامهٔ لکسر از طریق یک پروتکل سوکت با برنامهٔ دیگری ارتباط دارد که باید پیش از ارسال پیام بعدی، منتظر پاسخ از سوی لکسر بماند). قابلیت وضعیت قابل ذخیرهسازی دقیقاً برای چنین سناریوهایی طراحی شده است: این ویژگی امکان تولید لکسرهایی را فراهم میسازد که در یک مدل «رانشی» ("push") کار میکنند. زمانی که لکسر به ورودی بیشتری نیاز داشته باشد، وضعیت خود را ذخیره کرده و به فراخواننده بازمیگردد. سپس، با مهیا شدن ورودی بیشتر، فراخواننده لکسر را دقیقاً از همان نقطهای که متوقف شده بود از سر میگیرد. در مقایسه با مدل «کششی»، چند تغییر ضروری لازم است:
- دستورهای پایهٔ YYSETSTATE() و YYGETSTATE(state) را تعریف کنید.
- متغیرهای yych، yyaccept (در صورت استفاده) و state را بهعنوان بخشی از وضعیت پایدار لکسر تعریف کنید. متغیر state باید با مقدار -1 مقداردهی اولیه شود.
- دستور YYFILL باید بهجای تلاش برای تأمین ورودی بیشتر، به برنامهٔ بیرونی بازگردد. کد بازگشتی باید نشان دهد که لکسر به ورودی بیشتری نیاز دارد.
- برنامهٔ بیرونی باید شرایطی را که در آن لکسر به ورودی بیشتری نیاز دارد تشخیص داده و واکنش مناسب نشان دهد.
- بهصورت اختیاری، از بلوک getstate برای تولید سوئیچِ YYGETSTATE جدا از لکسر اصلی استفاده کنید. این قابلیت تنها برای زبانهایی کار میکند که دارای دستور goto هستند (نه در حالت --loop-switch).
- از re2c:eof و روش sentinel with bounds checks (نگهبان با بررسی مرزها) برای مدیریت پایان ورودی استفاده کنید. روش مبتنی بر لایهگذاری (padding) ممکن است کارایی نداشته باشد؛ زیرا مشخص نیست چه زمانی باید لایهگذاری اضافه شود: پایان فعلی بافر ورودی لزوماً پایان نهایی دادهها نیست و الصاق زودهنگام لایهگذاری ممکن است یک لکسمِ طماع (greedy lexeme) را که نیمهکاره خوانده شده قطع کند. علاوه بر این، به دلیل منطق سطح بالای برنامه، دریافت ورودی بیشتر ممکن است به پردازش لکسمِ موجود در انتهای بافر بستگی داشته باشد (که از قبل به دلیل شرایط انتهای ورودی مسدود شده است).
در اینجا مثالی از یک لکسر با مدل «رانشی» آورده شده است که خواندن بستهها از یک سوکت را شبیهسازی میکند. لکسر تا زمانی که به پایان ورودی برسد در یک حلقه اجرا شده و به تابع فراخواننده بازمیگردد. تابع فراخواننده با «ارسال» بستهٔ بعدی، ورودی بیشتری را فراهم کرده و تحلیل لغوی را از سر میگیرد. این فرایند زمانی که تمام بستهها ارسال شوند یا خطایی رخ دهد پایان مییابد.
# re2py $INPUT -o $OUTPUT -f
from enum import Enum
import os
# Use a small buffer to cover the case when a lexeme doesn't fit.
# In real world use a larger buffer.
BUFSIZE = 10
DEBUG = False
class State:
def __init__(self, file):
self.file = file
self.yyinput = bytearray(BUFSIZE)
self.yylimit = BUFSIZE - 1 # exclude terminating null
self.yycursor = self.yylimit
self.yymarker = self.yylimit
self.token = self.yylimit
self.yystate = -1
class Status(Enum):
END = 0
READY = 1
WAITING = 2
BIG_PACKET = 3
BAD_PACKET = 4
def fill(st):
# Error: lexeme too long. In real life could reallocate a larger buffer.
if st.token < 1:
return Status.BIG_PACKET
# Shift buffer contents (discard everything up to the current token).
st.yyinput = st.yyinput[st.token:st.yylimit]
st.yycursor -= st.token;
st.yymarker -= st.token;
st.yylimit -= st.token;
st.token = 0;
# Fill free space at the end of buffer with new data from file.
bytes = st.file.read(BUFSIZE - st.yylimit - 1) # -1 for sentinel
if bytes:
st.yylimit += len(bytes);
st.yyinput += bytes
st.yyinput += b'\0' # append sentinel
return Status.READY
def lex(yyrecord, recv):
while True:
yyrecord.token = yyrecord.yycursor
%{
re2c:api = record;
re2c:YYFILL = "return Status.WAITING, recv";
re2c:eof = 0;
re2c:indent:top = 2;
packet = [a-z]+[;];
* { return Status.BAD_PACKET, recv }
$ { return Status.END, recv }
packet {
recv += 1
break
}
%}
def test(packets, expect):
# Create a pipe (open the same file for reading and writing).
fname = "pipe"
fw = open(fname, "wb")
fr = open(fname, "rb")
# Initialize lexer state
st = State(fr)
# 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.
send = 0
recv = 0
while True:
status, recv = lex(st, recv)
if status == Status.END:
if DEBUG: print("done: got {} packets".format(recv))
break
elif status == Status.WAITING:
if DEBUG: print("waiting...");
if send < len(packets):
if DEBUG: print("sent packet {}: {}".format(send, packets[send]))
fw.write(packets[send])
fw.flush()
send += 1
status = fill(st)
if DEBUG: print("queue: '{}', status: {}".format(st.yyinput, status))
if status == Status.BIG_PACKET:
if DEBUG: print("error: packet too big")
break
assert status == Status.READY
else:
assert status == Status.BAD_PACKET
if DEBUG: print("error: ill-formed packet")
break
# Check results.
assert status == expect
if status == Status.END:
assert recv == send
# Cleanup: remove input file.
fr.close()
fw.close()
os.remove(fname)
def main():
test([], Status.END)
test([b"zero;", b"one;", b"two;", b"three;", b"four;"], Status.END)
test([b"zer0;"], Status.BAD_PACKET)
test([b"goooooooooogle;"], Status.BIG_PACKET)
if __name__ == '__main__':
main()
بلوکهای چندبارمصرف به شکل /*!rules:re2c[:<name>] ... */ یا %{rules[:<name>] ... %} میتوانند به هر تعداد دفعات دوباره استفاده شده و با سایر بلوکهای re2py ترکیب شوند. مقدار <name> اختیاری است. یک بلوک قوانین میتواند در یک بلوک یا دستورالعمل use استفاده شود. کد مربوط به یک بلوک قوانین در هر نقطه از استفاده تولید میشود.
بلوکهای use با /*!use:re2c[:<name>] ... */ یا %{use[:<name>] ... %} تعریف میشوند. مقدار <name> اختیاری است: اگر مشخص نشده باشد، بلوک قوانین مرتبط، جدیدترین بلوک قبلی خواهد بود (چه نامگذاری شده باشد چه بدون نام). یک بلوک use میتواند تعاریف با نام، پیکربندیها و قوانین مختص به خود را اضافه کند. یک کاربرد مهم برای بلوکهای use، تحلیلگر لغوی (lexer) با پشتیبانی از چندین کدگذاری ورودی است: همان بلوک قوانین چندین بار با پیکربندیهای مختص به هر کدگذاری مجدداً استفاده میشود (به مثال زیر مراجعه کنید).
دستورالعمل درونبلوکی !use:<name>; میتواند از داخل یک بلوک re2py استفاده شود. این دستور، بلوک ارجاعدادهشده <name> را با بلوک فعلی ادغام میکند. اگر برخی از قوانین و پیکربندیهای ادغامشده با موارد تعریفشده قبلی همپوشانی داشته باشند، تداخلها به شیوه معمول حل میشوند: نخستین قانون اولویت دارد و آخرین پیکربندی جایگزین موارد پیشین میشود. یک استثنا قوانین ویژه *، $ و (در حالت شرط) <!> هستند که برای آنها، تعریف محلیِ بلوک بر هر تعریف به ارثرسیدهای ارجحیت دارد. دستورالعمل use امکان ترکیب بلوکهای مختلف re2py را درون یک بلوک واحد فراهم میکند (به مثال زیر مراجعه کنید).
بلوکهای نامگذاریشده و دستورالعمل درونبلوکی use در نگارش 2.2 از re2py افزوده شدند. از آن نگارش به بعد، بلوکهای چندبارمصرف بهطور پیشفرض مجاز هستند (به هیچ گزینه خاصی نیاز نیست). پیش از نگارش 2.2، حالت استفاده مجدد با گزینه -r --reusable فعال میشد. پیش از نگارش 1.2، امکان ترکیب بلوکهای چندبارمصرف با بلوکهای معمولی وجود نداشت.
مثالی از دستورالعمل !use
# re2py $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.
from enum import Enum
class Ans(Enum):
COLOR = 1
FISH = 2
DUNNO = 3
%{rules:colors
* { raise "ah" }
"red" | "salmon" | "magenta" { return Ans.COLOR }
%}
%{rules:fish
* { raise "oh" }
"haddock" | "salmon" | "eel" { return Ans.FISH }
%}
def lex(yyinput):
yycursor = 0
%{
re2c:yyfill:enable = 0;
re2c:indent:top = 1;
!use:fish;
!use:colors;
* { return Ans.DUNNO } // overrides inherited '*' rules
%}
assert lex(b"salmon") == Ans.FISH
assert lex(b"what?") == Ans.DUNNO
مثالی از یک بلوک /*!use:re2c ... */
# re2py $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:yyfill:enable = 0;
re2c:indent:top = 1;
"∀x ∃y" { return yycursor }
* { return None }
%}
def lex_utf8(yyinput):
yycursor = 0
%{use
re2c:encoding:utf8 = 1;
%}
def lex_utf32(yyinput):
yycursor = 0
%{use
re2c:encoding:utf32 = 1;
%}
s8 = [0xe2, 0x88, 0x80, 0x78, 0x20, 0xe2, 0x88, 0x83, 0x79]
assert lex_utf8(s8) == len(s8)
s32 = [0x2200, 0x78, 0x20, 0x2203, 0x79]
assert lex_utf32(s32) == len(s32)
ابزار re2py دو گزینه برای استخراج زیرتطابق دارد.
- Tags
- نخستین گزینه، استفاده از tags (برچسبهای) مستقل به شکل @stag یا #mtag است، که در آن stag و mtag نامهای دلخواه تعریفشده توسط کاربر هستند. برچسبها با گزینهٔ -T --tags یا پیکربندی re2c:tags = 1 فعال میشوند. از نظر معنایی، برچسبها نشانگرهای موقعیت هستند: آنها میتوانند در هر کجای یک عبارت منظم درج شوند و به موقعیت متناظر (یا چندین موقعیت) در رشتهٔ ورودی پیوند میخورند. S-tags به آخرین موقعیت تطبیقیافته متصل میشوند و m-tags به فهرستی از موقعیتها پیوند میخورند (آنها ممکن است در زیرعبارتهای تکرار استفاده شوند، جایی که یک موقعیت واحد در یک عبارت منظم با چندین موقعیت در رشتهٔ ورودی متناظر است). تمام برچسبها باید توسط کاربر تعریف شوند؛ خواه به صورت دستی یا با کمک بلوکهای svars و mvars. اگر بیش از یک روش برای تطبیق برچسبها با ورودی وجود داشته باشد، ابهام با استفاده از راهبرد رفع ابهام حریصانهٔ چپترین (leftmost greedy) برطرف میشود.
- Captures
- گزینهٔ دوم استفاده از capturing groups (گروههای ضبط) است. آنها با گزینهٔ --captures یا پیکربندی re2c:captures = 1 فعال میشوند. دو گونه برای سیاستهای مختلف رفع ابهام وجود دارد؛ --leftmost-captures (پیشفرض) برای سیاست حریصانهٔ چپترین، و --posix-captures برای سیاست طولانیترین تطابق POSIX است. در این حالت تمام زیرعبارتهای درون پرانتز گروههای ضبط در نظر گرفته میشوند و میتوان از علامت تعجب (bang) برای علامتگذاری گروههای غیرقابلضبط استفاده کرد: (! ... ). با گزینهٔ --invert-captures یا پیکربندی re2c:invert-captures = 1 معنای علامت تعجب معکوس میشود. تعداد گروهها برای قاعدهٔ تطبیقیافته در متغیری به نام yynmatch ذخیره میشود (کل عبارت منظم گروه شمارهٔ صفر است)، و نتایج زیرتطابق در آرایهٔ yypmatch ذخیره میشوند. هر دو متغیر yynmatch و yypmatch باید توسط کاربر تعریف شوند، و اندازهٔ yypmatch باید حداقل [yynmatch * 2] باشد. از بلوک maxnmatch برای تعریف YYMAXNMATCH استفاده کنید، ثابتی که برابر با بیشترین مقدار yynmatch در میان تمام قواعد است.
- Captvars
- روش دیگر برای استفاده از گروههای ضبط، گزینهٔ --captvars یا پیکربندی re2c:captvars = 1 است. تنها تفاوت با --captures در نحوهٔ ذخیرهسازی نتایج زیرتطابق در کد تولیدشده است: بهجای yynmatch و yypmatch، ابزار re2py متغیرهای yytl<k> و yytr<k> را برای k-امین گروه ضبط تولید میکند (کاربر باید این متغیرها را با استفاده از یک بلوک svars اعلان کند). گروههای ضبط با متغیر از دو سیاست رفع ابهام پشتیبانی میکنند: --leftmost-captvars یا re2c:leftmost-captvars = 1 برای سیاست حریصانهٔ چپترین (حالت پیشفرض) و --posix-captvars یا re2c:posix-captvars برای سیاست طولانیترین تطابق POSIX.
در پشت صحنه، تمام این گزینهها به برچسبها و Tagged Deterministic Finite Automata with Lookahead ترجمه میشوند. ایدهٔ اصلی TDFA به حداقل رساندن بار اضافی (overhead) در استخراج زیرتطابق است. در حالت فرین (حد نهایی)، اگر هیچ برچسب یا گروه ضبطی در عبارت منظم نباشد، TDFA صرفاً یک DFA معمولی است. اگر تعداد برچسبها در حد معتدل باشد، بار اضافی به سختی محسوس خواهد بود. اتوماتای TDFA تولیدشده از تعدادی tag variables (متغیرهای برچسب) استفاده میکند که مستقیماً به برچسبها نگاشت نمیشوند: یک متغیر واحد ممکن است برای برچسبهای متفاوتی استفاده شود، و یک برچسب ممکن است برای نگهداشتن تمام مقادیر ممکن خود به چندین متغیر نیاز داشته باشد. در نهایت، ابهام برطرف میشود و برای هر برچسب تنها یک متغیر نهایی باقی میماند. متغیرهای برچسب باید با استفاده از بلوکهای stags یا mtags تعریف شوند. اگر وضعیت تحلیلگر لغوی ذخیره شود، متغیرهای برچسب نیز باید بخشی از آن باشند. آنها همچنین باید توسط YYFILL بهروزرسانی شوند.
برچسبهای S-tags از عملیات زیر پشتیبانی میکنند:
- ذخیرهٔ موقعیت ورودی در یک s-tag: دستور t = YYCURSOR با API اشارهگر C یا یک عملیات تعریفشده توسط کاربر YYSTAGP(t) با API عمومی (generic)
- ذخیرهٔ مقدار پیشفرض در یک s-tag: دستور t = NULL با API اشارهگر C یا یک عملیات تعریفشده توسط کاربر YYSTAGN(t) با API عمومی
- رونوشت از یک s-tag به دیگری: t1 = t2
برچسبهای M-tags از عملیات زیر پشتیبانی میکنند:
- الحاق موقعیت ورودی به یک 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 استفاده میکند، بخش زیر را ببینید.
# re2py $INPUT -o $OUTPUT
from collections import namedtuple
SemVer = namedtuple('SemVer', 'major minor patch')
NONE = -1
def parse(yyinput):
yycursor = 0
%{
re2c:yyfill:enable = 0;
re2c:tags = 1;
re2c:indent:top = 1;
num = [0-9]+;
@t1 num @t2 "." @t3 num @t4 ("." @t5 num)? [\x00] {
major = int(yyinput[t1:t2])
minor = int(yyinput[t3:t4])
patch = int(yyinput[t5:yycursor - 1]) if t5 != NONE else 0
return SemVer(major, minor, patch)
}
* { return None }
%}
assert parse(b"23.34\0") == SemVer(23, 34, 0)
assert parse(b"1.2.99999\0") == SemVer(1, 2, 99999)
assert parse(b"1.a\0") == None
در اینجا مثالی پیچیدهتر از استفاده از s-tagها با YYFILL برای تجزیهٔ فایلی با نگارشهای معنایی جداشده با خط جدید آمده است. متغیرهای برچسب بخشی از وضعیت تحلیلگر لغوی هستند، و همانند سایر موقعیتهای ورودی در YYFILL تنظیم میشوند. توجه داشته باشید که این امر برای s-tagها ضروری است زیرا مقادیر آنها پس از جابهجایی محتویات بافر باطل میشوند. این کار ممکن است در پیادهسازیهای سفارشی که در آنها متغیرهای برچسب آفستها را بهجای بافر، نسبت به ابتدای رشتهٔ ورودی ذخیره میکنند (که ممکن است در مورد m-tagها چنین باشد)، ضروری نباشد.
# re2py $INPUT -o $OUTPUT
from collections import namedtuple
from enum import Enum
import os
BUFSIZE = 4096
SemVer = namedtuple('SemVer', 'major minor patch')
class State:
def __init__(self, fname):
self.file = open(fname, "rb")
self.yyinput = bytearray(BUFSIZE)
self.yylimit = BUFSIZE - 1 # exclude terminating null
self.yycursor = self.yylimit
self.yymarker = self.yylimit
self.token = self.yylimit
self.eof = False
%{stags format = "\n self.@@ = -1"; %}
def __del__(self):
self.file.close()
class Status(Enum):
OK = 0
EOF = 1
LONG_LEXEME = 2
def fill(st):
if st.eof:
return Status.EOF
# Error: lexeme too long. In real life could reallocate a larger buffer.
if st.token < 1:
return Status.LONG_LEXEME
# Shift buffer contents (discard everything up to the current token).
st.yyinput = st.yyinput[st.token:st.yylimit]
st.yycursor -= st.token;
st.yymarker -= st.token;
st.yylimit -= st.token;
%{stags format = "\n if st.@@ != -1: st.@@ -= st.token"; %}
st.token = 0;
# Fill free space at the end of buffer with new data from file.
bytes = st.file.read(BUFSIZE - st.yylimit - 1) # -1 for sentinel
if not bytes:
st.eof = True # end of file
else:
st.yylimit += len(bytes);
st.yyinput += bytes
st.yyinput += b'\0' # append sentinel
return Status.OK
def lex(st, count):
vers = []
while True:
st.token = st.yycursor
%{
re2c:api = record;
re2c:yyrecord = st;
re2c:YYFILL = "fill(st) == Status.OK";
re2c:eof = 0;
re2c:indent:top = 2;
re2c:tags = 1;
num = [0-9]+;
num @t1 "." @t2 num @t3 ("." @t4 num)? [\n] {
major = int(st.yyinput[st.token:t1])
minor = int(st.yyinput[t2:t3])
patch = int(st.yyinput[t4:st.yycursor - 1]) if t4 != -1 else 0
vers.append(SemVer(major, minor, patch))
break
}
$ { return vers }
* { return None }
%}
def main():
fname = "input"
verstr = b"1.22.333\n"
expect = [SemVer(1, 22, 333)] * BUFSIZE
# Prepare input file.
f = open(fname, "wb")
for i in range(BUFSIZE):
f.write(verstr)
f.close()
# Run lexer on the prepared file.
st = State(fname)
assert lex(st, 0) == expect
# Cleanup.
os.remove(fname)
if __name__ == '__main__':
main()
در اینجا مثالی از بهکارگیری گروههای تسخیر (capturing groups) برای تجزیهٔ نگارشهای معنایی (semantic versions) آورده شده است.
# re2py $INPUT -o $OUTPUT
from collections import namedtuple
SemVer = namedtuple('SemVer', 'major minor patch')
NONE = -1
def parse(yyinput):
yycursor = 0
%{
re2c:yyfill:enable = 0;
re2c:captvars = 1;
re2c:indent:top = 1;
num = [0-9]+;
(num) "." (num) ("." num)? [\x00] {
major = int(yyinput[yytl1:yytr1])
minor = int(yyinput[yytl2:yytr2])
patch = 0 if yytl3 == NONE else int(yyinput[yytl3 + 1:yytr3])
return SemVer(major, minor, patch)
}
* { return None }
%}
assert parse(b"23.34\0") == SemVer(23, 34, 0)
assert parse(b"1.2.99999\0") == SemVer(1, 2, 99999)
assert parse(b"1.a\0") == None
در اینجا مثالی از بهکارگیری m-tags برای تجزیهٔ نگارشی با تعداد متغیر مؤلفهها آورده شده است. متغیرهای برچسب در یک trie ذخیره میشوند.
# re2py $INPUT -o $OUTPUT
NONE = -1
def parse(yyinput):
yycursor = 0
%{mtags format = '\n @@ = []'; %} # autogenerated tag variables
%{
re2c:YYMTAGP = "@@.append(yycursor)";
re2c:YYMTAGN = ""; // do nothing
re2c:yyfill:enable = 0;
re2c:tags = 1;
re2c:indent:top = 1;
num = [0-9]+;
@t1 num @t2 ("." #t3 num #t4)* [\x00] {
vers = [int(yyinput[t1:t2])]
for i in range(len(t3)):
vers.append(int(yyinput[t3[i]:t4[i]]))
return vers
}
* { return None }
%}
assert parse(b"1\0") == [1]
assert parse(b"1.2.3.4.5.6.7\0") == [1, 2, 3, 4, 5, 6, 7]
assert parse(b"1.2.\0") == None
درک تفاوت بین نقطههای کد (code points) و واحدهای کد (code units) ضروری است. یک نقطه کد، شناسه عددی یک نماد است. یک واحد کد، کوچکترین واحد ذخیرهسازی در متن کدگذاریشده است. یک نقطه کد تکی ممکن است با یک یا چند واحد کد نمایش داده شود. در یک کدگذاری با طول ثابت، تمام نقطههای کد با تعداد یکسانی از واحدهای کد نشان داده میشوند. در یک کدگذاری با طول متغیر، نقطههای کد ممکن است با تعداد متفاوتی از واحدهای کد نمایش داده شوند. توجه داشته باشید که قاعده "any" یعنی [^] با هر نقطه کدی تطابق دارد، اما لزوماً با هر واحد کدی تطابق ندارد (تنها راه تطبیق با هر واحد کد صرفنظر از نوع کدگذاری، قاعده پیشفرض * است). تحلیلگر لغوی تولیدشده با جریانی از واحدهای کد کار میکند: yych یک واحد کد را ذخیره میکند و YYCTYPE نوع واحد کد است. از سوی دیگر، عبارتهای منظم برحسب نقطههای کد مشخص میشوند. هنگامی که re2py عبارتهای منظم را به اتوماتا کامپایل میکند، نقطههای کد را به واحدهای کد ترجمه مینماید. این کار معمولاً یک نگاشت ساده نیست: در کدگذاریهای با طول متغیر، ممکن است یک بازه از یک نقطه کد منفرد به یک گراف پیچیده از واحدهای کد ترجمه شود. کدگذاریهای زیر پشتیبانی میشوند:
- ASCII (بهطور پیشفرض فعال است). یک کدگذاری با طول ثابت با فضای کد [0-255] و نقطههای کد و واحدهای کد ۱-بایتی است.
- EBCDIC (فعالسازی با --ebcdic یا re2c:encoding:ebcdic). یک کدگذاری با طول ثابت با فضای کد [0-255] و نقطههای کد و واحدهای کد ۱-بایتی است.
- UCS2 (فعالسازی با --ucs2 یا re2c:encoding:ucs2). یک کدگذاری با طول ثابت با فضای کد [0-0xFFFF] و نقطههای کد و واحدهای کد ۲-بایتی است.
- UTF8 (فعالسازی با --utf8 یا re2c:encoding:utf8). یک کدگذاری Unicode با طول متغیر است. اندازه واحد کد ۱ بایت است. نقطههای کد با 1 -- 4 واحد کد نمایش داده میشوند.
- UTF16 (فعالسازی با --utf16 یا re2c:encoding:utf16). یک کدگذاری Unicode با طول متغیر است. اندازه واحد کد ۲ بایت است. نقطههای کد با 1 -- 2 واحد کد نمایش داده میشوند.
- UTF32 (فعالسازی با --utf32 یا re2c:encoding:utf32). یک کدگذاری Unicode با طول ثابت با فضای کد [0-0x10FFFF] و نقطههای کد و واحدهای کد ۴-بایتی است.
فایلهای ضمیمه include/unicode_blocks.re، include/unicode_categories.re و include/unicode_properties.re تعاریف re2py را برای بلوکها، دستهها و ویژگیهای شرحدادهشده در استاندارد Unicode ارائه میدهند.
گزینه --input-encoding کدگذاری فایل منبع را مشخص میکند، که میتواند برای فعالسازی لیترالهای Unicode در عبارتهای منظم استفاده شود. برای مثال، --input-encoding utf8 به re2py اعلام میکند که فایل منبع به فرمت UTF8 است (این با --utf8 که کدگذاری متن ورودی را تنظیم میکند متفاوت است). گزینه --encoding-policy نحوه مدیریت جانشینهای یونیکد (Unicode surrogates؛ نقطههای کد در محدوده [0xD800-0xDFFF]) را توسط re2py تعیین میکند.
در زیر نمونهای از یک تحلیلگر لغوی برای شناسههای Unicode با کدگذاری UTF8 آورده شده است.
# re2py $INPUT -o $OUTPUT --utf8
%{include "unicode_categories.re" %}
def lex(yyinput):
yycursor = 0
%{
re2c:yyfill:enable = 0;
re2c:indent:top = 1;
// 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 }
%}
assert lex(bytes("_Ыдентификатор\0", "utf-8"))
ابزار re2py به شما اجازه میدهد فایلهای دیگر را با استفاده از بلوکی به شکل /*!include:re2c FILE */ یا %{include FILE %}، یا دستور درونبلوکی !include FILE ; ضمیمه کنید، که در آن FILE مسیر فایلی است که باید ضمیمه شود. re2py فایلهای ضمیمه را در دایرکتوریِ فایلِ دربرگیرنده و در مکانهای include (که میتوان با گزینهٔ -I مشخص کرد) جستجو میکند. بلوکها/دستورهای ضمیمهسازی در re2py به همان شیوهٔ #include در C/C++ عمل میکنند: محتویات FILE عیناً به جای بلوک/دستور کپی و جایگذاری میشوند. فایلهای ضمیمه میتوانند خود دارای includeهای بیشتری باشند. از گزینهٔ --depfile برای ردیابی وابستگیهای ساخت فایل خروجی نسبت به فایلهای ضمیمه استفاده کنید. ابزار re2py تعدادی فایل ضمیمه از پیش تعریفشده ارائه میدهد که در زیردایرکتوری include/ از پروژه قرار دارند. این فایلها شامل تعاریفی هستند که ممکن است برای پروژههای دیگر نیز مفید باشند (مانند دستهبندیهای Unicode) و چیزی شبیه به یک کتابخانه استاندارد را برای re2py تشکیل میدهند. در ادامه مثالی از بهکارگیری فایلهای ضمیمه آورده شده است.
فایل ضمیمه ۱ (definitions.py)
from enum import Enum
class Num(Enum):
INT = 1
FLOAT = 2
NAN = 3
%{
number = [1-9][0-9]*;
%}
فایل ضمیمه ۲ (extra_rules.re.inc)
// floating-point numbers
frac = [0-9]* "." [0-9]+ | [0-9]+ ".";
exp = 'e' [+-]? [0-9]+;
float = frac exp? | [0-9]+ exp;
float { return Num.FLOAT }
فایل ورودی (Input file)
# re2py $INPUT -o $OUTPUT
%{include "definitions.py" %}
def lex(yyinput):
yycursor = 0
%{
re2c:yyfill:enable = 0;
re2c:indent:top = 1;
* { return Num.NAN }
number { return Num.INT }
!include "extra_rules.re.inc";
%}
assert lex(b"123\0") == Num.INT
assert lex(b"123.4567\0") == Num.FLOAT
ابزار re2py به کاربر امکان میدهد تا با استفاده از گزینهٔ --header یا پیکربندی re2c:header و جفتبلوکهایی به شکل /*!header:re2c:on*/ و /*!header:re2c:off*/، یا %{header:on%} و %{header:off%}، از فایل ورودی .re یک فایل سرآیند تولید کند. بلوک نخست آغاز فایل سرآیند و بلوک دوم پایان آن را مشخص میسازد. هر آنچه میان این دو بلوک قرار گیرد توسط re2py پردازش شده و کد تولیدشده در فایلی که با گزینهٔ --header یا پیکربندی re2c:header مشخص شده نوشته میشود (یا در صورت عدم استفاده از گزینه یا پیکربندی، در stdout نوشته خواهد شد). فایل سرآیند تولیدشدهٔ خودکار ممکن است در مواردی نیاز باشد که از re2py برای ایجاد تعاریفی استفاده میشود که باید از سایر واحدهای ترجمه (translation units) قابل مشاهده باشند.
در ادامه مثالی از تولید یک فایل سرآیند آورده شده است که شامل تعریف وضعیت لکسر (lexer state) به همراه متغیرهای برچسب (tag variables) است (تعداد متغیرها به دستور زبان باقاعده بستگی داشته و برای برنامهنویس نامشخص است).
فایل ورودی (Input file)
# re2py $INPUT -o $OUTPUT --header lexer/state.py
from lexer.state import State
%{header:on %}
class State:
def __init__(self, str):
self.yyinput = str
self.yycursor = 0
%{stags format = "\n self.@@ = 0"; %}
%{header:off %}
def lex(yyrecord):
%{
re2c:api = record;
re2c:tags = 1;
re2c:yyfill:enable = 0;
re2c:indent:top = 1;
re2c:header = "lexer/state.py";
[a]* @t [b]* { return t }
%}
assert lex(State(b"ab\0")) == 1
فایل سرآیند (Header file)
# Generated by re2c
class State:
def __init__(self, str):
self.yyinput = str
self.yycursor = 0
self.yyt1 = 0
برنامههای اسکلت (Skeleton programs)
با استفاده از گزینهٔ -S, --skeleton، ابزار re2py تمام کدهای غیر re2py را نادیده گرفته و یک برنامهٔ خودکفای C تولید میکند که میتواند در ادامه کامپایل و اجرا شود. این برنامه از کد لکسر و دادههای ورودی تشکیل شده است. به ازای هر DFA (بلوک یا شرط) ساختهشده، re2py یک لکسر مستقل و دو فایل تولید میکند: یک فایل .input شامل رشتههای مشتقشده از DFA و یک فایل .keys با نتایج تطابق مورد انتظار. برنامه هر لکسر را روی فایل .input متناظر اجرا کرده و نتایج را با موارد مورد انتظار مقایسه میکند. برنامههای اسکلت به دلایل متعددی بسیار سودمند هستند:
- آنها میتوانند صحت بهینهسازیهای گوناگون re2py را بررسی کنند (دادهها در مراحل اولیهٔ پردازش و پیش از اعمال هرگونه دگرگونی بر روی DFA تولید میشوند).
- تولید مجموعهای از دادههای ورودی با پوششدهی مناسب میتواند هم برای آزمون و هم برای سنجش کارایی (benchmarking) مفید باشد.
- تولید برنامههای اجرایی خودکفا امکان بهدست آوردن نمونههای آزمون کمینهشده (minimized test cases) را فراهم میسازد (چرا که کد اصلی ممکن است بسیار بزرگ بوده یا وابستگیهای فراوانی داشته باشد).
دشواری تولید دادههای ورودی در این است که برای تمامی موارد به جز سادهترین حالتها، تعداد رشتههای ورودیِ ممکن بیش از حد زیاد است (حتی در حالتی که طول رشته محدود باشد). ابزار re2py این مشکل را با تولید رشتههای کافی به منظور پوشش تقریباً تمام گذارهای DFA حل میکند. این ابزار از الگوریتم زیر بهره میبرد: نخست، اسکلتی از DFA میسازد. برای کدگذاریهایی با اندازهٔ واحد کد ۱ بایتی (مانند ASCII، UTF-8 و EBCDIC) اسکلت تنها یک رونوشت دقیق از DFA اولیه است. برای کدگذاریهایی با واحدهای کد چندبایتی، اسکلت رونوشتی از DFA با حذف برخی گذارهاست: به این صورت که re2py برای هر بازهٔ پیوسته و مجزا که با یک گذار DFA مطابقت دارد، حداکثر ۲۵۶ واحد کد برمیگزیند. مقادیر انتخابشده بهطور یکنواخت توزیع شدهاند و شامل کرانهای بازه نیز میشوند. re2py به جای تلاش برای پوشش تمام مسیرهای ممکن در اسکلت (که امری ناشدنی است)، مسیرهای کافی را برای پوشش تمامی گذارهای اسکلت ایجاد میکند و بدین ترتیب پرشهای شرطی متناظر را در لکسر فعال میسازد. پیادهسازی این الگوریتم به حدود ۱ گیگابایت گذار محدود شده است و مقدار ثابتی از حافظه را مصرف میکند (re2py دادهها را به محض تولید، در فایل مینویسد)..SS "بصریسازی و اشکالزدایی"
با گزینهٔ -D, --emit-dot، برنامهٔ re2py کدی تولید نمیکند. در عوض، DFA تولیدشده را در قالب DOT خروجی میدهد. میتوان این خروجی را با استفاده از Graphviz یا کتابخانهای دیگر به تصویری از DFA تبدیل کرد. توجه داشته باشید که این گزینه، DFA نهایی را پس از اعمال چندین مرحله بهینهسازی و تبدیل نمایش میدهد. مراحل پیشین را میتوان با گزینههای گوناگون اشکالزدایی، مانند --dump-nfa، --dump-dfa-raw و غیره خروجی گرفت (فهرست کامل گزینهها را ببینید).
همچنین ببینید (SEE ALSO)
میتوانید اطلاعات بیشتری دربارهٔ re2c در وبگاه رسمی بیابید: http://re2c.org. برنامههای مشابه عبارتند از flex(1)، lex(1)، quex(http://quex.sourceforge.net).
نویسندگان (AUTHORS)
برنامهٔ re2py در ابتدا توسط 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.