| RE2GO(1) | RE2GO(1) |
نام (NAME)
re2go - تولید تحلیلگرهای واژگانی سریع برای Go
خلاصه دستور (SYNOPSIS)
re2go [ OPTIONS ] [ WARNINGS ] INPUT
ورودی میتواند یک فایل یا - برای stdin باشد.
مقدمه (INTRODUCTION)
ابزار re2go به عنوان یک پیشپردازنده عمل میکند. این ابزار فایل ورودی را (که معمولاً برنامهای به زبان Go است، اما میتواند هر چیز دیگری نیز باشد) خوانده و به دنبال بلوکهای کدی میگردد که در نشانگرهای آغاز/پایان با ساختار ویژه محصور شدهاند. متنهای خارج از این بلوکها عیناً در فایل خروجی کپی میشوند. محتویات این بلوکها توسط re2go پردازش میشوند. این ابزار آنها را به کدهایی به زبان Go ترجمه کرده و کد تولیدشده را به جای بلوک اصلی در خروجی قرار میدهد.
در اینجا مثالی از یک برنامه کوچک آورده شده است که بررسی میکند آیا رشته دادهشده شامل یک عدد دهدهی است یا خیر:
//go:generate re2go $INPUT -o $OUTPUT -i --api simple
package main
func lex(yyinput string) {
var yycursor int
/*!re2c
re2c:YYCTYPE = byte;
re2c:yyfill:enable = 0;
[1-9][0-9]* { return }
* { panic("error!") }
*/
}
func main() {
lex("1234\x00")
}
در خروجی، re2go بلوک میانی را با کد تولیدشده جایگزین کرده است:
// Code generated by re2go, DO NOT EDIT.
//go:generate re2go $INPUT -o $OUTPUT -i --api simple
package main
func lex(yyinput string) {
var yycursor int
{
var yych byte
yych = yyinput[yycursor]
switch (yych) {
case '1','2','3','4','5','6','7','8','9':
goto yy2
default:
goto yy1
}
yy1:
yycursor += 1
{ panic("error!") }
yy2:
yycursor += 1
yych = yyinput[yycursor]
switch (yych) {
case '0','1','2','3','4','5','6','7','8','9':
goto yy2
default:
goto yy3
}
yy3:
{ return }
}
}
func main() {
lex("1234\x00")
}
یک برنامه re2go از دنبالهای از بلوکها آمیخته با کد در زبان مقصد تشکیل میشود. یک بلوک میتواند شامل تعاریف، پیکربندیها، قواعد، کنشها و دستورالعملها به هر ترتیبی باشد:
- name = regular-expression ;
- یک تعریف، name را به regular-expression متصل میکند. نامها میتوانند شامل نویسههای الفبایی-عددی و زیرخط باشند. بخش regular expressions مروری بر نحو re2go برای عبارتهای باقاعده ارائه میدهد. پس از تعریف، name میتواند در سایر عبارتهای باقاعده و قواعد استفاده شود. بازگشت در تعاریف نامگذاریشده مجاز نیست و هر نام باید پیش از استفاده تعریف شده باشد. یک بلوک تعاریف نامگذاریشده را از دامنه سراسری به ارث میبرد. تعریف مجدد نامی که در دامنه فعلی وجود دارد خطا است.
- configuration = value ;
- یک پیکربندی امکان تغییر رفتار re2go و سفارشیسازی کد تولیدشده را فراهم میکند. برای فهرست کاملی از پیکربندیهای پشتیبانیشده توسط re2go بخش configurations را ببینید. بسته به یک پیکربندی خاص، value میتواند یک کلمه کلیدی، یک عدد صحیح غیرمنفی یا یک رشته تکخطی باشد که باید در علامتهای نقلقول تکی یا دوگانه قرار گیرد مگر اینکه از نویسههای الفبایی-عددی تشکیل شده باشد. یک بلوک پیکربندیها را از دامنه سراسری به ارث میبرد و میتواند آنها را دوباره تعریف کند یا موارد جدیدی بیفزاید. پیکربندیهای تعریفشده درون یک بلوک بر کل بلوک تأثیر میگذارند، حتی اگر در انتهای آن ظاهر شوند.
- regular-expression code
- یک قاعده، regular-expression را به کنش معنایی آن پیوند میدهد (یک بلوک کد در آکوالد، یا یک بلوک کد که با := شروع شده و با یک خط جدید و پس از آن هر نویسه غیرفضایخالی پایان مییابد). اگر regular-expression مطابقت یابد، code مربوطه اجرا میشود. اگر چندین قاعده تطابق یابند، طولانیترین تطابق اولویت دارد. اگر چندین قاعده با یک رشته تطابق یابند، نخستین مورد اولویت دارد. دو قاعده ویژه وجود دارد: قاعده پیشفرض * و قاعده پایان ورودی $. قاعده پیشفرض همواره باید تعریف شود، بدون توجه به جایگاهش در بلوک دارای کمترین اولویت است و با هر واحد کد مطابقت مییابد (نه لزوماً یک نویسه معتبر، به بخش encoding support مراجعه کنید). اگر روش متناظر برای handling the end of input استفاده شود، قاعده پایان ورودی باید تعریف شود. با start conditions قواعد نحو پیچیدهتری دارند.
- !action code
- یک کنش یک بلوک تعریفشده توسط کاربر از code را به مکان خاصی در ماشین حالت متناهی تولیدشده متصل میکند (به همان شیوهای که کنشهای معنایی کد را به وضعیتهای نهایی پیوند میدهند). برای فهرست کاملی از کنشهای از پیش تعریفشده به بخش actions مراجعه کنید.
- !directive ;
- یک دستورالعمل یکی از عبارتهای ویژه از پیش تعریفشده است. هر دستورالعمل هدف منحصربهفردی دارد. برای جزئیات به بخش directives مراجعه کنید.
بلوکها (Blocks)
نشانگرهای شروع و پایان بلوک یا /*!re2c و */ هستند، یا %{ و %} (از هر دو سبک پشتیبانی میشود). از نسخه 2.2 بلوکها میتوانند نامهای اختیاری داشته باشند که امکان ارجاع به آنها را در سایر بلوکها فراهم میسازد. انواع مختلفی از بلوکها وجود دارد:
- /*!re2c[:<name>] ... */ or %{[:<name>] ... %}
- یک بلوک سراسری شامل تعاریف، پیکربندیها، قواعد و دستورالعملها است. re2go عبارتهای باقاعده مرتبط با هر قاعده را به یک اتوماتون متناهی قطعی کامپایل میکند، آن را به شکل پرشهای شرطی در زبان مقصد کدگذاری کرده و بلوک را با کد تولیدشده جایگزین میکند. نامها و پیکربندیهای تعریفشده در یک بلوک سراسری به دامنه سراسری اضافه شده و برای بلوکهای بعدی قابل مشاهده میشوند. در آغاز برنامه دامنه سراسری با options خط فرمان مقداردهی اولیه میشود.
- /*!local:re2c[:<name>] ... */ or %{local[:<name>] ... %}
- یک بلوک محلی مانند یک بلوک سراسری است، اما نامها و پیکربندیهای درون آن دارای دامنه محلی هستند (بر سایر بلوکها تأثیری نمیگذارند).
- /*!rules:re2c[:<name>] ... */ or %{rules[:<name>] ... %}
- یک بلوک قواعد مانند یک بلوک محلی است، اما خودش کدی تولید نمیکند و هیچ تعریفی نیز به دامنه سراسری اضافه نمیکند -- هدف آن استفاده مجدد در سایر بلوکها است. این روشی برای به اشتراکگذاری کد است (جزئیات بیشتر در بخش reusable blocks). پیش از نسخه 2.2 re2go بلوکهای قواعد نیازمند گزینه -r --reusable بودند.
- /*!use:re2c[:<name>] ... */ or %{use[:<name>] ... %}
- یک بلوک استفاده که به یک بلوک قواعد از پیش تعریفشده ارجاع میدهد. اگر نام مشخص شده باشد، re2go به دنبال بلوک قواعدی با این نام میگردد. در غیر این صورت از جدیدترین بلوک قواعد استفاده میشود (خواه نامگذاریشده یا بدون نام). یک بلوک use میتواند تعاریف، پیکربندیها و قواعد خاص خود را اضافه کند که به موارد بلوک قواعد ارجاعشده افزوده میشوند. پیش از نسخه 2.2 re2go بلوکهای use نیازمند گزینه -r --reusable بودند.
- /*!max:re2c[:<name1>[:<name2>...]] ... */ or %{max[:<name1>[:<name2>...]] ... %}
- بلوکی که تعریف YYMAXFILL را تولید میکند. یک فهرست اختیاری از نامهای بلوک مشخص میکند که هنگام محاسبه مقدار YYMAXFILL کدام بلوکها باید گنجانده شوند (اگر فهرست خالی باشد، همه بلوکها گنجانده میشوند). بهطور پیشفرض کد تولیدشده یک تعریف ماکرو برای C (#define YYMAXFILL <n>)، یا یک متغیر سراسری برای Go (var YYMAXFILL int = <n>) است. این مورد را میتوان با پیکربندی اختیاری format سفارشی کرد که یک رشته الگو را مشخص میکند که در آن @@{max} (یا بهطور خلاصه @@) با مقدار عددی YYMAXFILL جایگزین میشود.
- /*!maxnmatch:re2c[:<name1>[:<name2>...]] ... */ or %{maxnmatch[:<name1>[:<name2>...]] ... %}
- بلوکی که تعریف YYMAXNMATCH را تولید میکند (نیازمند گزینه -P --posix-captures است). یک فهرست اختیاری از نامهای بلوک مشخص میکند که هنگام محاسبه مقدار YYMAXNMATCH کدام بلوکها باید گنجانده شوند (اگر فهرست خالی باشد، همه بلوکها گنجانده میشوند). بهطور پیشفرض کد تولیدشده یک تعریف ماکرو برای C (#define YYMAXNMATCH <n>)، یا یک متغیر سراسری برای Go (var YYMAXNMATCH int = <n>) است. این مورد را میتوان با پیکربندی اختیاری format سفارشی کرد که یک رشته الگو را مشخص میکند که در آن @@{max} (یا بهطور خلاصه @@) با مقدار عددی YYMAXNMATCH جایگزین میشود.
- /*!stags:re2c[:<name1>[:<name2>...]] ... */, /*!mtags:re2c[:<name1>[:<name2>...]] ... */ or %{stags[:<name1>[:<name2>...]] ... %}, %{mtags[:<name1>[:<name2>...]] ... %{
- بلوکهایی که یک قطعه کد الگو را مشخص میکنند که برای هر متغیر s-tag/m-tag تولیدشده توسط re2go باز میشود. یک فهرست اختیاری از نامهای بلوک مشخص میکند که هنگام محاسبه مجموعه متغیرهای برچسب کدام بلوکها باید گنجانده شوند (اگر فهرست خالی باشد، همه بلوکها گنجانده میشوند). دو پیکربندی اختیاری وجود دارد: format و separator. پیکربندی format یک رشته الگو را مشخص میکند که در آن @@{tag} (یا بهطور خلاصه @@) با نام هر متغیر برچسب جایگزین میشود. پیکربندی separator قطعه کدی را مشخص میکند که برای متصل کردن بخشهای تولیدشده format برای متغیرهای برچسب مختلف استفاده میشود.
- /*!svars:re2c[:<name1>[:<name2>...]] ... */, /*!mvars:re2c[:<name1>[:<name2>...]] ... */ or %{svars[:<name1>[:<name2>...]] ... %}, %{mvars[:<name1>[:<name2>...]] ... %{
- بلوکهایی که یک قطعه کد الگو را مشخص میکنند که برای هر s-tag/m-tag که صراحتاً توسط قواعد ذکر شده باشد (با گزینه --tags) یا بهطور ضمنی توسط re2go تولید شده باشد (با گزینههای --captvars یا --posix-captvars)، باز میشود. یک فهرست اختیاری از نامهای بلوک مشخص میکند که هنگام محاسبه مجموعه برچسبها کدام بلوکها باید گنجانده شوند (اگر فهرست خالی باشد، همه بلوکها گنجانده میشوند). دو پیکربندی اختیاری وجود دارد: format و separator. پیکربندی format یک رشته الگو را مشخص میکند که در آن @@{tag} (یا بهطور خلاصه @@) با نام هر برچسب جایگزین میشود. پیکربندی separator قطعه کدی را مشخص میکند که برای متصل کردن بخشهای تولیدشده format برای برچسبهای مختلف استفاده میشود.
- /*!getstate:re2c[:<name1>[:<name2>...]] ... */ or %{getstate[:<name1>[:<name2>...]] ... %}
- بلوکی که ارسال شرطی را روی وضعیت تحلیلگر واژگانی تولید میکند (نیازمند گزینه --storable-state است). یک فهرست اختیاری از نامهای بلوک مشخص میکند که کدام بلوکها باید در ارسال وضعیت گنجانده شوند. انتقال پیشفرض به برچسب آغازین اولین بلوک در فهرست میرود. اگر فهرست خالی باشد، همه بلوکها گنجانده میشوند و انتقال پیشفرض به اولین بلوک موجود در فایل که دارای برچسب آغازین باشد میرود. این نوع بلوک با گزینه --loop-switch ناسازگار است، چرا که نیازمند انتقالهای بینبلوکی است که بدون goto یا فراخوانیهای تابع پشتیبانی نمیشوند.
- /*!conditions:re2c[:<name1>[:<name2>...]] ... */, /*!types:re2c... */ or %{conditions[:<name1>[:<name2>...]] ... %}, %{types... %}
- بلوکی که شمارش شرطها را تولید میکند (نیازمند گزینه --conditions است). یک فهرست اختیاری از نامهای بلوک مشخص میکند که هنگام محاسبه مجموعه شرطها کدام بلوکها باید گنجانده شوند (اگر فهرست خالی باشد، همه بلوکها گنجانده میشوند). بهطور پیشفرض کد تولیدشده یک شمارش YYCONDTYPE است. این مورد را میتوان با پیکربندیهای اختیاری format و separator سفارشی کرد. پیکربندی format یک رشته الگو را مشخص میکند که در آن @@{cond} (یا بهطور خلاصه @@) با نام هر شرط، و @@{num} با اندیس عددی آن شرط جایگزین میشود. پیکربندی separator قطعه کدی را مشخص میکند که برای متصل کردن بخشهای تولیدشده format برای شرطهای مختلف استفاده میشود.
- /*!include:re2c <file> */ or %{include <file> %}
- این بلوک اجازه میدهد <file> را که باید یک مسیر فایل درون علامت نقلقول دوگانه باشد ضمیمه کنید. محتویات فایل بهطور عینی بهجای بلوک جایگزین میشود، درست همانگونه که #include در C/C++ کار میکند. این بلوک میتواند همراه با گزینه --depfile برای تولید وابستگیهای سامانه ساخت روی فایلهای ضمیمهشده استفاده شود.
- /*!header:re2c:on*/ or %{header:on %}
- این بلوک آغاز فایل سرآیند را مشخص میکند. همه موارد پس از آن و تا بلوک header:off بعدی توسط re2go پردازش شده و در فایل سرآیند مشخصشده با گزینه -t --type-header نوشته میشود.
- /*!header:re2c:off*/ or %{header:off %}
- این بلوک پایان فایل سرآیند آغازشده با بلوک header:on*/ را مشخص میکند.
- /*!ignore:re2c ... */ or %{ignore ... %}
- بلوکی که
محتویات آن
نادیده
گرفته شده و
از فایل
خروجی حذف
میشود.
در اینجا فهرست کاملی از پیکربندیهای پشتیبانیشده توسط re2go آمده است:
- re2c:api, re2c:input
- مشابه گزینهٔ --api.
- re2c:api:sigil
- نشانگر ("sigil") مورد استفاده برای جاینگهدارهای آرگومان در عملگرهای اولیهٔ API را مشخص میکند. مقدار پیشفرض @@ است. یک جاینگهدار با نشانگر آغاز شده و به دنبال آن نام آرگومان در آکولاد میآید. برای مثال، اگر نشانگر روی $ تنظیم شود، جاینگهدارها به شکل ${name} خواهند بود. APIهای تکآرگومانی میتوانند از نمادگذاری کوتاه بدون نام در آکولاد استفاده کنند. این گزینه میتواند با گزینههای مربوط به عملگرهای اولیهٔ منفرد API باطل و بازنویسی شود، مانند re2c:YYFILL@len برای YYFILL.
- re2c:api:style
- سبک API را مشخص میکند. مقادیر ممکن عبارتند از functions (پیشفرض برای C) و free-form (پیشفرض برای Go و Rust). در سبک functions، عملگرهای اولیهٔ API به همراه فهرستی از آرگومانها درون پرانتز پس از نام عملگر تولید میشوند. آرگومانها تنها برای پارامترهای خودکار تولیدشده (مانند تعداد نویسههای ارسالشده به YYFILL) ارائه میشوند، نه برای زمینهٔ عمومی تحلیلگر لغوی، بنابراین این عملگرها بیشتر شبیه ماکروها در C/C++ یا کلوژرها در Go و Rust عمل میکنند. در سبک free-form، عملگرهای اولیهٔ API قالب ثابتی ندارند: آنها باید به عنوان رشتههایی حاوی قطعهکدهای آزاد با متغیرهای درونیابیشده به شکل @@{var} یا @@ تعریف شوند (اینها متناظر با آرگومانها در سبک تابعگونه هستند). این پیکربندی میتواند برای عملگرهای اولیهٔ API به صورت مجزا باطل و بازنویسی شود؛ برای نمونه، پیکربندی re2c:YYFILL:naked را برای YYFILL ببینید.
- re2c:bit-vectors, re2c:flags:bit-vectors, re2c:flags:b
- مشابه گزینهٔ --bit-vectors، اما میتواند بر مبنای هر بلوک پیکربندی شود.
- re2c:captures, re2c:leftmost-captures
- مشابه گزینهٔ --leftmost-captures، اما میتواند بر مبنای هر بلوک پیکربندی شود.
- re2c:captvars, re2c:leftmost-captvars
- مشابه گزینهٔ --leftmost-captvars، اما میتواند بر مبنای هر بلوک پیکربندی شود.
- re2c:case-insensitive, re2c:flags:case-insensitive
- مشابه گزینهٔ --case-insensitive، اما میتواند بر مبنای هر بلوک پیکربندی شود.
- re2c:case-inverted, re2c:flags:case-inverted
- مشابه گزینهٔ --case-inverted، اما میتواند بر مبنای هر بلوک پیکربندی شود.
- re2c:case-ranges, re2c:flags:case-ranges
- مشابه گزینهٔ --case-ranges، اما میتواند بر مبنای هر بلوک پیکربندی شود.
- re2c:computed-gotos, re2c:flags:computed-gotos, re2c:flags:g
- مشابه گزینهٔ --computed-gotos، اما میتواند بر مبنای هر بلوک پیکربندی شود.
- re2c:computed-gotos:relative, re2c:cgoto:relative
- مشابه گزینهٔ --computed-gotos-relative، اما میتواند بر مبنای هر بلوک پیکربندی شود.
- re2c:computed-gotos:threshold, re2c:cgoto:threshold
- در صورت استفاده از gotoی محاسبهشده، این پیکربندی آستانهٔ پیچیدگیای را مشخص میکند که منجر به تولید جدولهای پرش به جای دستورهای تودرتوی if و نقشههای بیتی میشود. مقدار پیشفرض 9 است.
- re2c:cond:abort
- اگر روی یک مقدار عددی صحیح و مثبت تنظیم شود، حالت پیشفرض در توزیع وضعیت تولیدشده، اجرای برنامه را متوقف میکند.
- re2c:cond:goto
- قطعهکد مورد استفاده برای قواعد میانبر خودکار تولیدشدهٔ :=> در شرطها را مشخص میکند. مقدار پیشفرض goto @@; است. جاینگهدار @@ با نام شرط جایگزین میشود (پیکربندیهای re2c:api:sigil و re2c:cond:goto@cond را ببینید).
- re2c:cond:goto@cond
- نشانگر مورد استفاده برای جایگزینی آرگومان در تعریف re2c:cond:goto را مشخص میکند. مقدار پیشفرض @@ است. این گزینه پیکربندی عمومیتر re2c:api:sigil را باطل و بازنویسی میکند.
- re2c:cond:divider
- جداکنندهٔ بلوکهای شرطی را تعریف میکند. مقدار پیشفرض /* *********************************** */ است. جاینگهدارها با نام شرط جایگزین میشوند (به re2c:api;sigil و re2c:cond:divider@cond مراجعه کنید).
- re2c:cond:divider@cond
- نشانگر مورد استفاده برای جایگزینی آرگومان در تعریف re2c:cond:divider را مشخص میکند. مقدار پیشفرض @@ است. این گزینه پیکربندی عمومیتر re2c:api:sigil را باطل و بازنویسی میکند.
- re2c:cond:prefix, re2c:condprefix
- پیشوند مورد استفاده برای برچسبهای شرط را مشخص میکند. مقدار پیشفرض yyc_ است.
- re2c:cond:enumprefix, re2c:condenumprefix
- پیشوند مورد استفاده برای شناسههای شرط را مشخص میکند. مقدار پیشفرض yyc است.
- re2c:debug-output, re2c:flags:debug-output, re2c:flags:d
- مشابه گزینهٔ --debug-output، اما میتواند بر مبنای هر بلوک پیکربندی شود.
- re2c:empty-class, re2c:flags:empty-class
- مشابه گزینهٔ --empty-class، اما میتواند بر مبنای هر بلوک پیکربندی شود.
- re2c:encoding:ebcdic, re2c:flags:ecb, re2c:flags:e
- مشابه گزینهٔ --ebcdic، اما میتواند بر مبنای هر بلوک پیکربندی شود.
- re2c:encoding:ucs2, re2c:flags:wide-chars, re2c:flags:w
- مشابه گزینهٔ --ucs2، اما میتواند بر مبنای هر بلوک پیکربندی شود.
- re2c:encoding:utf8, re2c:flags:utf-8, re2c:flags:8
- مشابه گزینهٔ --utf8، اما میتواند بر مبنای هر بلوک پیکربندی شود.
- re2c:encoding:utf16, re2c:flags:utf-16, re2c:flags:x
- مشابه گزینهٔ --utf16، اما میتواند بر مبنای هر بلوک پیکربندی شود.
- re2c:encoding:utf32, re2c:flags:unicode, re2c:flags:u
- مشابه گزینهٔ --utf32، اما میتواند بر مبنای هر بلوک پیکربندی شود.
- re2c:encoding-policy, re2c:flags:encoding-policy
- مشابه گزینهٔ --encoding-policy، اما میتواند بر مبنای هر بلوک پیکربندی شود.
- re2c:eof
- نماد نگهبان مورد استفاده همراه با قاعدهٔ پایان ورودی $ را مشخص میکند. مقدار پیشفرض -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، با این تفاوت که میتواند بهازای هر بلوک پیکربندی شود.
- عبارت مورد استفاده برای متغیرهای برچسب را مشخص میکند. بهطور پیشفرض re2go عبارتهایی به فرم 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` است (در این حالت re2go فرض میکند که نماد نگهبان صفر است، که معمولترین حالت محسوب میشود). تنها اعداد دهدهی شناسایی میشوند.
- re2c:state:abort
- اگر روی یک مقدار عددی صحیح مثبت تنظیم شود، مورد پیشفرض (default case) در توزیع وضعیتِ تولیدشده اجرای برنامه را متوقف (abort) میکند، و یک مورد صریح -1 شامل انتقال به ابتدای بلوک خواهد بود.
- re2c:state:nextlabel
- کنترل میکند که آیا بهدنبال سوییچ YYGETSTATE یک برچسب yyNext بیاید یا خیر (مقدار پیشفرض صفر است که متناظر با عدم وجود برچسب میباشد). بهعنوان روشی دیگر، میتوان از re2c:label:start برای تولید یک برچسب شروع خاص، یا از یک بلوک صریح getstate برای تولید سوییچ YYGETSTATE بهصورت جداگانه از بلوک تحلیلگر لغوی استفاده کرد.
- re2c:unsafe, re2c:flags:unsafe
- همانند گزینهٔ --no-unsafe، با این تفاوت که میتواند بهازای هر بلوک پیکربندی شود. اگر روی صفر تنظیم شود، از تولید پوششهای unsafe در اطراف YYPEEK جلوگیری میکند. مقدار پیشفرض غیرصفر است (پوششها تولید میشوند). این پیکربندی مختص به Rust است.
- re2c:YYBACKUP, re2c:define:YYBACKUP
- پریمیتیو عمومی API با نام YYBACKUP را تعریف میکند.
- re2c:YYBACKUPCTX, re2c:define:YYBACKUPCTX
- پریمیتیو عمومی API با نام YYBACKUPCTX را تعریف میکند.
- re2c:YYCONDTYPE, re2c:define:YYCONDTYPE
- پریمیتیو API با نام YYCONDTYPE را تعریف میکند.
- re2c:YYCTYPE, re2c:define:YYCTYPE
- پریمیتیو API با نام YYCTYPE را تعریف میکند.
- re2c:YYCTXMARKER, re2c:define:YYCTXMARKER
- پریمیتیو API با نام YYCTXMARKER را تعریف میکند.
- re2c:YYCURSOR, re2c:define:YYCURSOR
- پریمیتیو API با نام YYCURSOR را تعریف میکند.
- re2c:YYDEBUG, re2c:define:YYDEBUG
- پریمیتیو API با نام YYDEBUG را تعریف میکند.
- re2c:YYFILL, re2c:define:YYFILL
- پریمیتیو API با نام YYFILL را تعریف میکند.
- re2c:YYFILL@len, re2c:define:YYFILL@len
- نشانهٔ (sigil) مورد استفاده برای جایگزینی آرگومان را در تعریف YYFILL مشخص میکند. پیشفرض @@ است. پیکربندی عمومیتر re2c:api:sigil را بازنویسی میکند.
- re2c:YYFILL:naked, re2c:define:YYFILL:naked
- پیکربندی عمومیتر re2c:api:style را برای YYFILL بازنویسی میکند. مقدار صفر متناظر با سبک API قالبآزاد (free-form) است.
- re2c:YYFN
- پریمیتیو API با نام YYFN را تعریف میکند.
- re2c:YYINPUT
- پریمیتیو API با نام YYINPUT را تعریف میکند.
- re2c:YYGETCOND, re2c:define:YYGETCONDITION
- پریمیتیو API با نام YYGETCOND را تعریف میکند.
- re2c:YYGETCOND:naked, re2c:define:YYGETCONDITION:naked
- پیکربندی عمومیتر re2c:api:style را برای YYGETCOND بازنویسی میکند. مقدار صفر متناظر با سبک API قالبآزاد (free-form) است.
- re2c:YYGETSTATE, re2c:define:YYGETSTATE
- پریمیتیو API با نام YYGETSTATE را تعریف میکند.
- re2c:YYGETSTATE:naked, re2c:define:YYGETSTATE:naked
- پیکربندی عمومیتر re2c:api:style را برای YYGETSTATE بازنویسی میکند. مقدار صفر متناظر با سبک API قالبآزاد (free-form) است.
- re2c:YYGETACCEPT, re2c:define:YYGETACCEPT
- پریمیتیو API با نام YYGETACCEPT را تعریف میکند.
- re2c:YYLESSTHAN, re2c:define:YYLESSTHAN
- پریمیتیو عمومی API با نام YYLESSTHAN را تعریف میکند.
- re2c:YYLIMIT, re2c:define:YYLIMIT
- پریمیتیو API با نام YYLIMIT را تعریف میکند.
- re2c:YYMARKER, re2c:define:YYMARKER
- پریمیتیو API با نام YYMARKER را تعریف میکند.
- re2c:YYMTAGN, re2c:define:YYMTAGN
- پریمیتیو عمومی API با نام YYMTAGN را تعریف میکند.
- re2c:YYMTAGP, re2c:define:YYMTAGP
- پریمیتیو عمومی API با نام YYMTAGP را تعریف میکند.
- re2c:YYPEEK, re2c:define:YYPEEK
- پریمیتیو عمومی API با نام YYPEEK را تعریف میکند.
- re2c:YYRESTORE, re2c:define:YYRESTORE
- پریمیتیو عمومی API با نام YYRESTORE را تعریف میکند.
- re2c:YYRESTORECTX, re2c:define:YYRESTORECTX
- پریمیتیو عمومی API با نام YYRESTORECTX را تعریف میکند.
- re2c:YYRESTORETAG, re2c:define:YYRESTORETAG
- پریمیتیو عمومی API با نام YYRESTORETAG را تعریف میکند.
- re2c:YYSETCOND, re2c:define:YYSETCONDITION
- پریمیتیو API با نام YYSETCOND را تعریف میکند.
- re2c:YYSETCOND@cond, re2c:define:YYSETCONDITION@cond
- نشانهٔ (sigil) مورد استفاده برای جایگزینی آرگومان را در تعریف YYSETCOND مشخص میکند. مقدار پیشفرض @@ است. پیکربندی عمومیتر re2c:api:sigil را بازنویسی میکند.
- re2c:YYSETCOND:naked, re2c:define:YYSETCONDITION:naked
- پیکربندی عمومیتر re2c:api:style را برای YYSETCOND بازنویسی میکند. مقدار صفر متناظر با سبک API قالبآزاد (free-form) است.
- re2c:YYSETSTATE, re2c:define:YYSETSTATE
- پریمیتیو API با نام YYSETSTATE را تعریف میکند.
- re2c:YYSETSTATE@state, re2c:define:YYSETSTATE@state
- نشانهٔ (sigil) مورد استفاده برای جایگزینی آرگومان را در تعریف YYSETSTATE مشخص میکند. مقدار پیشفرض @@ است. پیکربندی عمومیتر re2c:api:sigil را بازنویسی میکند.
- re2c:YYSETSTATE:naked, re2c:define:YYSETSTATE:naked
- پیکربندی عمومیتر re2c:api:style را برای YYSETSTATE بازنویسی میکند. مقدار صفر متناظر با سبک API قالبآزاد (free-form) است.
- re2c:YYSETACCEPT, re2c:define:YYSETACCEPT
- پریمیتیو API با نام YYSETACCEPT را تعریف میکند.
- re2c:YYSKIP, re2c:define:YYSKIP
- پریمیتیو عمومی API با نام YYSKIP را تعریف میکند.
- re2c:YYSHIFT, re2c:define:YYSHIFT
- پریمیتیو عمومی API با نام YYSHIFT را تعریف میکند.
- re2c:YYCOPYMTAG, re2c:define:YYCOPYMTAG
- پریمیتیو عمومی API با نام YYCOPYMTAG را تعریف میکند.
- re2c:YYCOPYSTAG, re2c:define:YYCOPYSTAG
- پریمیتیو عمومی API با نام YYCOPYSTAG را تعریف میکند..TP re2c:YYSHIFTMTAG, re2c:define:YYSHIFTMTAG دستور پایه 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
- اگر روی مقداری غیر از صفر تنظیم شود، نگاشتهای بیتی (bitmaps) برای گزینه --bit-vectors در قالب شانزدهشانزدهی (هگزادسیمال) تولید میشوند. پیشفرض صفر است (نگاشتهای بیتی در قالب دهدهی هستند).
- re2c:yych, re2c:variable:yych
- دستور پایه API با نام yych را تعریف میکند.
- re2c:yych:emit, re2c:variable:yych:emit
- اگر روی صفر تنظیم شود، تعریف yych تولید نمیشود. پیشفرض مقداری غیرصفر است.
- re2c:yych:conversion, re2c:variable:yych:conversion
- اگر روی غیرصفر تنظیم شود، re2go هر بار که yych خوانده میشود، بهطور خودکار یک تبدیل به YYCTYPE تولید میکند. پیشفرض صفر است (بدون تبدیل).
- re2c:yych:literals, re2c:variable:yych:literals
- قالب مقادیر ثابتی (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 را مشخص
میکند
(پیشفرض
خالی است،
که به معنی
عدم وجود
استثنا
میباشد).
re2go از نحو زیر برای عبارتهای منظم استفاده میکند:
- "foo"
- رشتهٔ تحتاللفظی حساس به بزرگ و کوچکی حروف.
- 'foo'
- رشتهٔ تحتاللفظی غیرحساس به بزرگ و کوچکی حروف.
- [a-xyz], [^a-xyz]
- کلاس نویسه (احتمالاً نقیضشده).
- .
- هر نویسهای به جز خط جدید.
- R \ S
- تفاضل کلاسهای نویسهٔ R و S.
- R*
- صفر یا چند بار تکرار R.
- R+
- یک یا چند بار تکرار R.
- R?
- مورد اختیاری R.
- R{n}
- تکرار R دقیقاً به تعداد n بار.
- R{n,}
- تکرار R حداقل n بار.
- R{n,m}
- تکرار R از n تا m بار.
- (R)
- صرفاً همان R؛ از پرانتزها برای تغییر اولویت استفاده میشود. اگر استخراج تطابقهای فرعی فعال باشد، بسته به گزینهٔ --invert-captures، (R) یک گروه ضبطکننده یا غیرضبطکننده خواهد بود.
- (!R)
- اگر استخراج تطابقهای فرعی فعال باشد، بسته به گزینهٔ --invert-captures، (!R) یک گروه غیرضبطکننده یا ضبطکننده خواهد بود.
- R S
- الحاق: R و به دنبال آن S.
- R | S
- جایگزین: R یا S.
- R / S
- پیشنگری (Lookahead): R و به دنبال آن S، اما S مصرف نمیشود.
- name
- عبارت منظم تعریفشده بهصورت name (یا رشتهٔ تحتاللفظی "name" در حالت سازگاری با Flex).
- {name}
- عبارت منظم تعریفشده بهصورت name در حالت سازگاری با Flex.
- @stag
- یک s-tag: آخرین موقعیت ورودی که در آن @stag تطابق مییابد را در متغیری به نام stag ذخیره میکند.
- #mtag
- یک m-tag: تمام موقعیتهای ورودی که در آن #mtag تطابق مییابد را در متغیری به نام mtag ذخیره میکند.
- $
- پایان ورودی.
کلاسهای نویسه و رشتههای تحتاللفظی میتوانند شامل توالیهای گریز زیر باشند: \a، \b، \f، \n، \r، \t، \v، \\، گریزهای هشتهشتی \ooo و گریزهای شانزدهشانزدهی \xhh، \uhhhh و \Uhhhhhhhh.
کنشها (Actions)
فهرست کنشهای از پیش تعریفشده که توسط re2go پشتیبانی میشوند در زیر آمده است:
- !entry code
- کنش ورود (Entry action) یک بلوک کاربری از code را به وضعیت آغازین ماشین حالت متناهی فعلی مقید میکند. در صورت استفاده از start conditions، کنش ورود میتواند بهطور مجزا برای هر شرط تنظیم شود. از این کنش میتوان برای انجام مقداردهی اولیه استفاده کرد، مانند ذخیرهٔ موقعیت شروع یک نشانه (lexeme).
- !pre_rule code
- کنش پیشقاعده (Pre-rule action) یک بلوک کاربری از code را به ابتدای کنشهای معنایی تمامی قوانین در بلوک فعلی (یا شرط، در صورت استفاده از start conditions) اضافه میکند. از این کنش میتوان برای جداسازی بخش مشترک تمام کنشهای معنایی (مانند ذخیرهٔ موقعیت پایان یک نشانه) استفاده کرد.
- !post_rule code
- کنش
پسقاعده
(Post-rule action) یک بلوک
کاربری از
code را به
انتهای
کنشهای
معنایی
تمامی
قوانین در
بلوک فعلی
(یا شرط، در
صورت
استفاده از
start conditions) الحاق
میکند. از
این کنش
میتوان
برای صدور
دستورهای
تله (trap statements)
استفاده
کرد تا از
جریان
کنترل
ناخواسته
جلوگیری
شود.
در اینجا فهرست کاملی از دستورالعملهای پشتیبانیشده توسط re2go آورده شده است:
- !use:name ;
- یک دستورالعمل use درونبلوکی که یک بلوک قواعد از پیش تعریفشده با name مشخص را در بلوک جاری ادغام میکند. تعاریف نامگذاریشده، پیکربندیها و قواعد بلوک ارجاعدادهشده به موارد جاری افزوده میشوند. تداخل میان قواعد و پیکربندیهای همپوشان به روش معمول حلوفصل میشود: قاعدهٔ نخست اولویت دارد، و آخرین پیکربندی بر موارد پیشین بازنویسی میشود. یک استثنا قواعد خاص *، $ و <!> هستند که برای آنها تعریف محلی در بلوک همواره اولویت دارد. یک دستورالعمل use میتواند در هر کجای درون یک بلوک قرار گیرد، و استفاده از چند دستورالعمل use مجاز است.
- !include file ;
- این دستورالعمل همانند بلوک include است: محتویات file را عیناً به جای دستورالعمل درج میکند.
رابط برنامه (Program interface)
کد تولیدشده به کمک عناصر اولیه (primitives) با برنامهٔ بیرونی ارتباط برقرار میکند، که در مجموع به عنوان API شناخته میشوند. اینکه کدام عناصر اولیه باید برای یک برنامهٔ خاص تعریف شوند به عوامل متعددی بستگی دارد، از جمله پیچیدگی عبارات منظم، نحوهٔ نمایش ورودی، بافرینگ و استفاده از قابلیتهای گوناگون. تمام عناصر اولیهٔ ضروری باید توسط کاربر در قالب ماکروها، توابع، متغیرها یا هر شکل مناسب دیگری که کد تولیدشده را از نظر نحوی و معنایی درست میسازد، تعریف شوند. re2go تعاریف را بررسی نمیکند (و نمیتواند بررسی کند)، بنابراین اگر موردی جا افتاده باشد یا به نادرستی تعریف شده باشد، برنامهٔ تولیدشده ممکن است خطاهای زمان کامپایل یا زمان اجرا داشته باشد. این راهنما مثالهایی از تعاریف API را برای رایجترین حالتها ارائه میدهد.
برنامهٔ re2go دارای سه شیوهٔ API است که مجموعهٔ اصلی عناصر اولیهٔ مورد استفاده توسط یک برنامه را مشخص میکنند:
- Simple API
- (در نسخهٔ 4.0 افزوده شد) این یک API پایهای است که میتواند با گزینهٔ --api simple یا پیکربندی re2c:api = simple فعال شود. این رابط شامل عناصر اولیهٔ زیر است: YYINPUT (که باید بهصورت دنبالهای از واحدهای کد، مانند یک رشته تعریف شود) و YYCURSOR، YYMARKER، YYCTXMARKER، YYLIMIT (که باید بهصورت اندیسهایی در YYINPUT تعریف شوند).
- Record API
- (در نسخهٔ 4.0 افزوده شد) رابط Record API در مواردی کاربرد دارد که وضعیت تحلیلگر لغوی (lexer) باید در یک struct ذخیره شود. این رابط با گزینهٔ --api record یا پیکربندی re2c:api = record فعال میگردد. این API شامل یک متغیر yyrecord است (این نام را میتوان با re2c:yyrecord بازنویسی کرد) که باید بهصورت یک struct با فیلدهای yyinput، yycursor، yymarker، yyctxmarker، yylimit تعریف شود (تنها فیلدهایی که توسط کد تولیدشده استفاده میشوند باید تعریف شوند، و نامهای آنها نیز قابل پیکربندی است).
- Generic API
- این انعطافپذیرترین API و رابط پیشفرض برای backend زبان Go است. این رابط شامل عناصر اولیه برای عملیات عمومی است: YYPEEK، YYSKIP، YYBACKUP، YYBACKUPCTX، YYSTAGP، YYSTAGN، YYMTAGP، YYMTAGN، YYRESTORE، YYRESTORECTX، YYRESTORETAG، YYSHIFT، YYSHIFTSTAG، YYSHIFTMTAG، YYLESSTHAN، YYEND. رابط Generic API از دو سبک پشتیبانی میکند که نحوهٔ تعریف عناصر اولیه را تعیین میکنند:
- Free-form
- سبک آزاد (Free-form) سبک پیشفرض است. این سبک همچنین میتواند با پیکربندی re2c:api:style = free-form فعال شود. در این سبک، عناصر اولیهٔ رابط باید بهصورت قطعهکدهای آزاد با متغیرهای الحاقی به فرم @@{var} یا در صورت وجود تنها یک متغیر، به شکل اختیاری @@ تعریف شوند. مجموعهٔ متغیرها مختص به هر عنصر اولیه است. در اینجا نحوهٔ تعریف Generic API به سبک free-form بر اساس متغیرهای صحیح cursor، limit، marker، ctxmarker و یک رشته (یا byte slice) data آورده شده است:
/*!re2c
re2c:YYPEEK = "data[cursor]";
re2c:YYSKIP = "cursor++";
re2c:YYBACKUP = "marker = cursor";
re2c:YYRESTORE = "cursor = marker";
re2c:YYBACKUPCTX = "ctxmarker = cursor";
re2c:YYRESTORECTX = "cursor = ctxmarker";
re2c:YYRESTORETAG = "cursor = ${tag}";
re2c:YYLESSTHAN = "limit - cursor < @@{len}";
re2c:YYEND = "limit == cursor";
re2c:YYSTAGP = "@@{tag} = cursor";
re2c:YYSTAGN = "@@{tag} = -1";
re2c:YYSHIFT = "cursor += @@{shift}";
re2c:YYSHIFTSTAG = "@@{tag} += @@{shift}";
*/
- Function-like
- سبک شبهتابع (Function-like) با پیکربندی re2c:api:style = functions فعال میشود. در این سبک، عناصر اولیه باید بهصورت closureهایی تعریف شوند که آرگومانهای لازم را میپذیرند. در اینجا نحوهٔ تعریف Generic API به سبک function-like بر اساس متغیرهای صحیح cursor، limit، marker، ctxmarker و یک رشته (یا byte slice) data آمده است:
YYPEEK := func() byte { return data[cursor] }
YYSKIP := func() { cursor++ }
YYBACKUP := func() { marker = cursor }
YYRESTORE := func() { cursor = marker }
YYBACKUPCTX := func() { ctxmarker = cursor }
YYRESTORECTX := func() { cursor = ctxmarker }
YYLESSTHAN := func(n int) bool { return limit-cursor < n }
YYEND := func(n int) bool { return limit == cursor }
YYSHIFT := func(n int) { cursor += n }
در اینجا فهرست کاملی از عناصر اولیهٔ API که ممکن است توسط کد تولیدشده برای برقراری ارتباط با برنامهٔ بیرونی استفاده شوند آورده شده است.
- YYCTYPE
- نوع نویسههای ورودی (واحدهای کد یا code units). برای کدگذاریهای 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) داشته باشد. نیازی به مقداردهی اولیه نیست..TP YYCTXMARKER یک l-value که موقعیت بافت پسآیند (trailing context) را ذخیره میکند (یک اشارهگر یا یک افست عدد صحیح در YYINPUT). نیازی به مقداردهی اولیه نیست. YYCTXMARKER تنها در صورتی نیاز است که عملگر پیشنگری / استفاده شده باشد.
- YYFILL
- یک عنصر اولیه عمومی API با یک متغیر len. YYFILL باید حداقل به اندازه len نویسه ورودی بیشتر فراهم کند یا با شکست مواجه شود. اگر re2c:eof استفاده شود، آنگاه len همیشه 1 است و YYFILL باید همیشه به تابع فراخوان بازگردد؛ مقدار بازگشتی صفر نشاندهنده موفقیت است. اگر re2c:eof استفاده نشود، مقدار بازگشتی YYFILL نادیده گرفته میشود و در صورت بروز خطا نباید بازگردد. مقدار بیشینه len برابر با YYMAXFILL است.
- YYFN
- یک عنصر اولیه که پیشنمونه تابع را در مدل کد --recursive-functions تعریف میکند. مقدار آن باید آرایهای از یک یا چند رشته باشد، که در آن هر رشته شامل دو یا سه مؤلفه است که توسط رشته مشخصشده در پیکربندی re2c:fn:sep (معمولاً یک نقطهویرگول) از هم جدا شدهاند. نخستین عنصر آرایه نام تابع و نوع بازگشتی آن را مشخص میکند (برای یک تابع void خالی است). عناصر بعدی آرگومانهای تابع را تعریف میکنند: نخست، عبارت مربوط به آرگومان مورد استفاده در بدنه تابع (معمولاً فقط یک نام)؛ دوم، نوع آرگومان؛ سوم، یک پارامتر صوری اختیاری (بهطور پیشفرض مؤلفه اول در نظر گرفته میشود - معمولاً هر دو آرگومان و پارامتر شناسه یکسانی دارند).
- YYINPUT
- یک r-value که دنباله نویسههای ورودی جاری را ذخیره میکند (رشته، بافر و غیره).
- YYMAXFILL
- یک ثابت از نوع عدد صحیح برابر با بیشینه مقدار آرگومان دادهشده به YYFILL. این مقدار میتواند با یک بلوک max تولید شود.
- YYLESSTHAN
- یک عنصر اولیه عمومی API با یک متغیر len. این مقدار باید به عنوان یک r-value از نوع دودویی (boolean) تعریف شود که اگر و تنها اگر کمتر از len نویسه ورودی باقی مانده باشد، برابر با true خواهد بود.
- YYEND
- یک عنصر اولیه عمومی API بدون هیچ متغیری. این مقدار باید به عنوان یک r-value از نوع بولی تعریف شود که اگر و تنها اگر به پایان منطقی ورودی رسیده باشیم (بهاستثنای هرگونه پدینگ یا نمادهای نگهبان)، برابر با true خواهد بود. YYEND برای پیادهسازی نماد $ در عبارات باقاعده به کار میرود. این عنصر با YYLESSTHAN تفاوت دارد، که برای اطمینان از نخواندن فراتر از انتهای بافر توسط تحلیلگر لغوی استفاده میشود.
- YYPEEK
- یک عنصر اولیه عمومی API بدون هیچ متغیری. این عنصر باید به عنوان یک r-value از نوع YYCTYPE تعریف شود که برابر با نویسه موجود در موقعیت ورودی جاری است.
- YYSKIP
- یک عنصر اولیه عمومی API که باید موقعیت ورودی جاری را به اندازه یک واحد کد جلو ببرد.
- YYBACKUP
- یک عنصر اولیه عمومی API که باید موقعیت ورودی جاری را ذخیره کند (تا بعداً با YYRESTORE بازیابی شود).
- YYRESTORE
- یک عنصر اولیه عمومی API که باید موقعیت ورودی جاری را به مقدار ذخیرهشده توسط YYBACKUP بازگرداند.
- YYBACKUPCTX
- یک عنصر اولیه عمومی API که باید موقعیت ورودی جاری را به عنوان موقعیت بافت پسآیند ذخیره کند (تا بعداً با YYRESTORECTX بازیابی شود).
- YYRESTORECTX
- یک عنصر اولیه عمومی API که باید موقعیت بافت پسآیند ذخیرهشده با YYBACKUPCTX را بازیابی کند.
- YYRESTORETAG
- یک عنصر اولیه عمومی API با یک متغیر tag که باید موقعیت بافت پسآیند را به مقدار tag بازگرداند.
- YYSTAGP
- یک عنصر اولیه عمومی API با یک متغیر tag، که در آن tag میتواند یک اشارهگر یا یک افست در YYINPUT باشد (برای جزئیات به بخش استخراج تطابقهای فرعی مراجعه کنید). YYSTAGP باید tag را روی موقعیت ورودی جاری تنظیم کند.
- YYSTAGN
- یک عنصر اولیه عمومی API با یک متغیر tag، که در آن tag میتواند یک اشارهگر یا یک افست در YYINPUT باشد (برای جزئیات به بخش استخراج تطابقهای فرعی مراجعه کنید). YYSTAGN باید tag را روی مقداری تنظیم کند که نشاندهنده موقعیت ورودی ناموجود باشد.
- YYMTAGP
- یک عنصر اولیه عمومی API با یک متغیر tag. YYMTAGP باید موقعیت جاری را به تاریخچه تطابق فرعی tag ضمیمه کند (برای جزئیات به بخش استخراج تطابقهای فرعی مراجعه کنید).
- YYMTAGN
- یک عنصر اولیه عمومی API با یک متغیر tag. YYMTAGN باید مقداری را که نشاندهنده موقعیت ورودی ناموجود است به تاریخچه تطابق فرعی tag ضمیمه کند (برای جزئیات به بخش استخراج تطابقهای فرعی مراجعه کنید).
- YYSHIFT
- یک عنصر اولیه عمومی API با یک متغیر shift که باید موقعیت ورودی جاری را به اندازه shift نویسه جابهجا کند (مقدار جابهجایی ممکن است منفی باشد).
- YYCOPYSTAG
- یک عنصر اولیه عمومی API با دو متغیر lhs و rhs که باید متغیر s-tag سمت راست rhs را در متغیر s-tag سمت چپ lhs کپی کند. برای بیشتر زبانها این عنصر اولیه تعریفی پیشفرض دارد که lhs را به rhs انتساب میدهد.
- YYCOPYMTAG
- یک عنصر اولیه عمومی API با دو متغیر lhs و rhs که باید متغیر m-tag سمت راست rhs را در متغیر m-tag سمت چپ lhs کپی کند. برای بیشتر زبانها این عنصر اولیه تعریفی پیشفرض دارد که lhs را به rhs انتساب میدهد.
- YYSHIFTSTAG
- یک عنصر اولیه عمومی API با دو متغیر tag و shift که باید tag را به اندازه shift واحد کد جابهجا کند (مقدار جابهجایی ممکن است منفی باشد).
- YYSHIFTMTAG
- یک عنصر اولیه عمومی API با دو متغیر tag و shift که باید آخرین مقدار در تاریخچه tag را به اندازه shift واحد کد جابهجا کند (مقدار جابهجایی ممکن است منفی باشد).
- YYMAXNMATCH
- یک ثابت از نوع عدد صحیح برابر با حداکثر تعداد گروههای تسخیرکننده POSIX در یک قاعده. این مقدار با یک بلوک maxnmatch تولید میشود.
- YYCONDTYPE
- نوع شمارش شرط (condition enum). میتواند با بلوک conditions یا گزینه --header تولید شود.
- YYGETACCEPT
- یک عنصر اولیه با یک متغیر var که گزینشگر عددی قاعده پذیرفتهشده را ذخیره میکند. برای بیشتر زبانها این عنصر اولیه دارای تعریف پیشفرضی است که از var میخواند.
- YYSETACCEPT
- یک عنصر اولیه با دو متغیر: var (یک l-value که گزینشگر عددی قاعده پذیرفتهشده را ذخیره میکند)، و val (مقدار گزینشگر). برای بیشتر زبانها این عنصر اولیه دارای تعریفی پیشفرض است که var را به val انتساب میدهد.
- YYGETCOND
- یک r-value از نوع YYCONDTYPE که برابر با شناسه شرط جاری است.
- YYSETCOND
- یک عنصر اولیه با یک متغیر cond که باید شناسه شرط جاری را روی cond تنظیم کند.
- YYGETSTATE
- یک r-value از نوع عدد صحیح که برابر با وضعیت جاری تحلیلگر لغوی است. مقدار اولیه آن باید برابر با -1 باشد.
- YYSETSTATE
- یک عنصر اولیه با یک متغیر state که باید وضعیت جاری تحلیلگر لغوی را روی state تنظیم کند.
- YYDEBUG
- این عنصر اولیه تنها با گزینه -d، --debug-output تولید میشود. هدف آن افزودن ثبت وقایع به کد تولیدشده است (تعریف رایج YYDEBUG یک دستور چاپ است). دستورات YYDEBUG در هر وضعیت تولید میشوند و دو متغیر دارند: state (نمایه وضعیت DFA یا -1) و symbol (نماد ورودی جاری).
- yyaccept
- یک l-value از نوع عدد صحیح بدون علامت که شماره آخرین قاعده تطبیقیافته را ذخیره میکند. تعریف توسط کاربر تنها با گزینه --storable-state ضروری است.
- yybm
- جدولی شامل نقشههای بیتی فشرده برای حداکثر ۸ گذار (مورد استفاده با گزینه --bitmaps). این جدول شامل ۲۵۶ عنصر است و توسط واحدهای کد ۱ بایتی اندیسگذاری میشود. هر عنصر ۸ بیتی مقادیر بولی را برای حداکثر ۸ گذار ترکیب میکند. بیت k-ام از عنصر n-ام درست (true) است اگر و تنها اگر n-امین واحد کد در محدوده k-امین گذار باشد. ایده این نقشه بیتی جایگزینی چندین شاخه if یا حالت switch با بررسی یک تکبیت در جدول است.
- yych
- یک l-value از نوع YYCTYPE که نویسه ورودی جاری را ذخیره میکند. تعریف توسط کاربر تنها با گزینه -f --storable-state ضروری است..TP yyctable جدول پرش تولیدشده برای توزیع وضعیت اولیه (که با ترکیب گزینههای --conditions و --computed-gotos فعال میشود).
- yyfill
- یک l-value که نتیجهٔ فراخوانی YYFILL را ذخیره میکند (این ممکن است برای زبانهای تابعی محض، که در آنها YYFILL یک تابع مونادیک با مقدار بازگشتی پیچیده است، ضروری باشد).
- yynmatch
- یک l-value از نوع عددی صحیح بدون علامت که تعداد گروههای گیرانداز POSIX را در قاعدهٔ تطبیقیافته ذخیره میکند. تنها با گزینهٔ -P --posix-captures استفاده میشود.
- yypmatch
- آرایهای از l-valueها که برای نگهداری مقادیر برچسب متناظر با پرانتزهای گیرانداز در قاعدهٔ منطبق استفاده میشود. طول آرایه باید حداقل yynmatch * 2 باشد (معمولاً YYMAXNMATCH * 2 انتخاب مناسبی است). تنها با گزینهٔ -P --posix-captures استفاده میشود.
- yystable
- منسوخشده.
- yystate
- یک l-value که با گزینهٔ --loop-switch برای ذخیرهٔ وضعیت فعلی DFA استفاده میشود.
- yytarget
- جدول پرشی که شامل اهداف پرش (آدرسهای برچسب) برای همهٔ گذارها از یک وضعیت است. این جدول برای هر وضعیت محلی است. تولید جداول yytarget با گزینهٔ --computed-gotos فعال میشود.
گزینهها (OPTIONS)
برخی از گزینهها دارای پیکربندیهای متناظر هستند، برخی دیگر سراسری بوده و پس از شروع خواندن فایل ورودی توسط re2c قابل تغییر نیستند. گزینههای اشکالزدایی عموماً نیازمند ساخت re2c در پیکربندی اشکالزدایی هستند. گزینههای داخلی برای آزمایش الگوریتمهای بهکاررفته در re2c مفید هستند.
- -? --help -h
- نمایش پیام راهنما.
- --api <simple | record | generic>
- مشخص کردن API استفادهشده توسط کد تولیدشده برای برقراری ارتباط با کد تعریفشده توسط کاربر. گزینهٔ simple باید در موارد ساده استفاده شود، هنگامی که نیازی به پر کردن مجدد بافر و ذخیرهٔ وضعیت لکسر وجود ندارد. گزینهٔ record باید هنگامی استفاده شود که وضعیت لکسر نیاز به ذخیرهسازی در یک رکورد (struct، class و غیره) دارد. گزینهٔ generic باید در موارد پیچیدهای استفاده شود که دو API دیگر به اندازهٔ کافی انعطافپذیر نیستند.
- --bit-vectors -b
- بهینهسازی پرشهای شرطی با استفاده از ماسکهای بیتی. این گزینه متضمن --nested-ifs است.
- --captures, --leftmost-captures
- فعالسازی استخراج زیرتطبیق با گروههای گیرانداز حریصانهٔ چپترین. نتیجه در آرایهٔ yybmatch با ظرفیت 2 * YYMAXNMATCH جمعآوری شده و yynmatch به تعداد گروهها برای قاعدهٔ منطبق تنظیم میشود.
- --captvars, --leftmost-captvars
- فعالسازی استخراج زیرتطبیق با گروههای گیرانداز حریصانهٔ چپترین. نتیجه در متغیرهای yytl<k> و yytr<k> برای k-امین گروه گیرانداز ذخیره میشود.
- --case-insensitive
- در نظر گرفتن رشتههای درون گیومهٔ تکی و دوتایی به صورت غیرحساس به حروف بزرگ و کوچک.
- --case-inverted
- معکوس کردن معنای رشتههای درون گیومهٔ تکی و دوتایی: در نظر گرفتن رشتههای درون گیومهٔ تکی به صورت حساس به حروف بزرگ و کوچک و رشتههای درون گیومهٔ دوتایی به صورت غیرحساس به حروف بزرگ و کوچک.
- --case-ranges
- ادغام caseهای متوالی در دستور switch به یک بازه با فرم low ... high. این نحو یک افزونهٔ زبان C/C++ است که توسط کامپایلرهایی مانند GCC، Clang و Tcc پشتیبانی میشود. مزیت اصلی نسبت به استفاده از caseهای منفرد، کد تولیدشدهٔ کوچکتر و زمان تولید سریعتر است، هرچند برای برخی کامپایلرها مانند Tcc به اندازهٔ باینری کوچکتر نیز منجر میشود.
- --computed-gotos -g
- بهینهسازی پرشهای شرطی با استفاده از افزونهٔ غیر استاندارد "computed goto" (که باید توسط کامپایلر پشتیبانی شود). re2go جداول پرش را تنها در موارد پیچیده با شاخههای شرطی فراوان تولید میکند. آستانهٔ پیچیدگی با پیکربندی cgoto:threshold قابل تنظیم است. آفستهای نسبی را میتوان با پیکربندی cgoto:relative فعال کرد. این گزینه متضمن --bit-vectors است.
- --computed-gotos-relative
- مشابه --computed-gotos اما در عوض آفستهای نسبی را برای جداول پرش تولید میکند (که باید توسط کامپایلر پشتیبانی شود). این گزینه متضمن --computed-gotos است.
- --conditions --start-conditions -c
- فعالسازی پشتیبانی از "conditions" مشابه Flex: چندین لکسر مرتبط به هم درون یک بلوک. این گزینه جایگزینی برای مشخص کردن دستی بلوکهای مختلف re2go متصل به هم با goto یا فراخوانیهای توابع است.
- --depfile FILE
- نوشتن اطلاعات وابستگی در FILE به صورت یک قاعدهٔ Makefile با قالب <output-file> : <input-file> [include-file ...]. این امکان را فراهم میکند که وابستگیهای ساخت در صورت وجود بلوکها/دستورالعملهای include دنبال شوند، به گونهای که بهروزرسانی فایلهای include باعث تولید مجدد فایل خروجی شود. این گزینه به گزینهٔ --output وابسته است.
- --ebcdic --ecb -e
- تولید لکسری که ورودی را با کدگذاری EBCDIC میخواند. re2go فرض میکند که محدودهٔ نویسه 0 -- 0xFF و اندازهٔ نویسه ۱ بایت است.
- --empty-class <match-empty | match-none | error>
- تعریف نحوهٔ برخورد re2go با کلاسهای نویسهٔ خالی. با match-empty (پیشفرض) کلاس خالی با ورودی خالی تطبیق مییابد (که غیرمنطقی است، اما با نسخههای پیشین سازگار است). با match-none کلاس خالی همواره در تطبیق ناموفق میشود. با error کلاس خالی خطای کامپایل ایجاد میکند.
- --encoding-policy <fail | substitute | ignore>
- تعریف نحوهٔ برخورد re2go با جانشینهای یونیکد (Unicode surrogates). با fail در صورت مواجهه با یک جانشین، re2go با خطا متوقف میشود. با substitute، re2go بدون هشدار جانشینها را با نقطه کد خطای 0xFFFD جایگزین میکند. با ignore (پیشفرض) re2go با جانشینها مانند نقاط کد عادی برخورد میکند. استاندارد یونیکد میگوید که جانشینهای مستقل نامعتبر هستند، اما کتابخانهها و برنامههای دنیای واقعی به روشهای مختلفی رفتار میکنند.
- --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 کاربرد دارد. re2go به دنبال FILE در دایرکتوری فایل والد و در مکانهای include مشخصشده با گزینهٔ -I میگردد.
- --input <default | custom>
- نام مستعار منسوخشده برای --api. گزینهٔ default با simple مطابقت دارد (که البته برای بیشتر بکاندها پیشفرض است، اما نه برای همه). گزینهٔ custom با generic مطابقت دارد.
- --input-encoding <ascii | utf8>
- مشخص کردن نحوهٔ تجزیه عبارتهای منظم توسط re2go. با ascii (پیشفرض) re2go ورودی را با کدگذاری ASCII پردازش میکند: هر دنبالهای از واحدهای کد، دنبالهای از نویسههای مستقل ۱ بایتی است. با utf8، re2go ورودی را با کدگذاری 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 وضعیت مقصد بهروزرسانی کرده و اجرای برنامه را به ابتدای حلقه ادامه میدهند..TP --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 builds) ناامن است؛ در این حالت گزینهٔ --no-unsafe به جلوگیری از هشدارهای مربوط به بلوکهای اضافی unsafe کمک میکند.
- --output -o OUTPUT
- مشخص کردن فایل OUTPUT.
- --posix-captures, -P
- فعالسازی استخراج زیرتطابق با گروههای ضبط به سبک POSIX. نتیجه در آرایهٔ yybmatch با ظرفیت 2 * YYMAXNMATCH جمعآوری میشود و مقدار yynmatch برابر با تعداد گروههای قاعدهٔ تطبیقیافته تنظیم میشود.
- --posix-captvars
- فعالسازی استخراج زیرتطابق با گروههای ضبط به سبک POSIX. نتیجه در متغیرهای yytl<k> و yytr<k> برای kامین گروه ضبط جمعآوری میشود.
- --recursive-functions
- استفاده از مدل کُد مبتنی بر توابع بازگشتی متقابل (co-recursive)، که در آن هر وضعیت DFA یک تابع مجزاست که میتواند توابع وضعیتهای دیگر یا خودش را فراخوانی کند.
- --reusable -r
- از نسخهٔ 2.2 منسوخ شده است (بلوکهای قابل استفادهٔ مجدد اکنون بهطور پیشفرض مجاز هستند).
- --skeleton -S
- نادیده گرفتن کُد رابط تعریفشده توسط کاربر و تولید یک برنامهٔ اسکلتی ("skeleton") خودکفا. علاوه بر این، تولید فایلهای ورودی با رشتههای مشتقشده از گرامر باقاعده و نتایج تطابق فشردهشده که برای اعتبارسنجی رفتار "skeleton" روی تمام ورودیها به کار میروند. این گزینه برای یافتن باگها در بهینهسازیها و تولید کُد مفید است. این گزینه تنها برای C پشتیبانی میشود.
- --storable-state -f
- تولید یک تحلیلگر لغوی که میتواند وضعیت درونی خود را ذخیره کند. این مورد در تحلیلگرهای لغوی مدل push که در صورت نبود ورودی کافی توسط برنامهای بیرونی متوقف میشوند و با فراهم شدن ورودی بیشتر ادامه مییابند، کاربرد دارد. در این حالت کاربران باید علاوه بر این توابع اولیهٔ YYGETSTATE و YYSETSTATE را تعریف کنند، و متغیرهای yych، yyaccept و state باید بخشی از وضعیت ذخیرهشدهٔ تحلیلگر لغوی باشند.
- --syntax FILE
- بارگذاری پیکربندیها از FILE مشخصشده و اعمال آنها روی فایل نحو پیشفرض. توجه داشته باشید که FILE میتواند تنها چند پیکربندی را تعریف کند (در صورتی که برای اصلاح فایل نحو پیشفرض به کار رود)، یا میتواند یک بکاند زبان کاملاً جدید تعریف نماید (در مورد دوم توصیه میشود از گزینهٔ --lang none استفاده شود).
- فعالسازی استخراج زیرتطابق با برچسبها.
- --ucs2 --wide-chars -w
- تولید تحلیلگر لغوی که ورودی با کدگذاری UCS2 را میخواند. re2go فرض میکند که محدودهٔ نویسه 0 -- 0xFFFF و اندازهٔ نویسه ۲ بایت است. این گزینه مستلزم --nested-ifs است.
- --utf8 --utf-8 -8
- تولید تحلیلگر لغوی که ورودی با کدگذاری UTF-8 را میخواند. re2go فرض میکند که محدودهٔ نویسه 0 -- 0x10FFFF و اندازهٔ نویسه ۱ بایت است.
- --utf16 --utf-16 -x
- تولید تحلیلگر لغوی که ورودی با کدگذاری UTF16 را میخواند. re2go فرض میکند که محدودهٔ نویسه 0 -- 0x10FFFF و اندازهٔ نویسه ۲ بایت است. این گزینه مستلزم --nested-ifs است.
- --utf32 --unicode -u
- تولید تحلیلگر لغوی که ورودی با کدگذاری UTF32 را میخواند. re2go فرض میکند که محدودهٔ نویسه 0 -- 0x10FFFF و اندازهٔ نویسه ۴ بایت است. این گزینه مستلزم --nested-ifs است.
- --verbose
- چاپ پیامی کوتاه در صورت موفقیت.
- --vernum -V
- نمایش اطلاعات نسخه در قالب MMmmpp (اصلی، فرعی، وصله).
- --version -v
- نمایش اطلاعات نسخه.
- --single-pass -1
- منسوخ شده است. کاری انجام نمیدهد (اکنون تکگذره بودن پیشفرض است).
- --debug-output -d
- درج فراخوانیهای YYDEBUG در کُد تولیدشده. این گزینه برای ردیابی اجرای تحلیلگر لغوی مفید است.
- --dump-adfa
- گزینهٔ اشکالزدایی: خروجی دادن DFA پس از تونلزنی (tunneling) (در قالب .dot).
- --dump-cfg
- گزینهٔ اشکالزدایی: خروجی دادن گراف جریان کنترل متغیرهای برچسب (در قالب .dot).
- --dump-closure-stats
- گزینهٔ اشکالزدایی: خروجی دادن آمار تعداد وضعیتها در بستار (closure).
- --dump-dfa-det
- گزینهٔ اشکالزدایی: خروجی دادن DFA بلافاصله پس از قطعیسازی (determinization) (در قالب .dot).
- --dump-dfa-min
- گزینهٔ اشکالزدایی: خروجی دادن DFA پس از کمینهسازی (در قالب .dot).
- --dump-dfa-tagopt
- گزینهٔ اشکالزدایی: خروجی دادن DFA پس از بهینهسازیهای برچسب (در قالب .dot).
- --dump-dfa-tree
- گزینهٔ اشکالزدایی: خروجی دادن DFA در حال ساخت با وضعیتهایی که به عنوان درختهای تاریخچهٔ برچسب نمایش داده شدهاند (در قالب .dot).
- --dump-dfa-raw
- گزینهٔ اشکالزدایی: خروجی دادن DFA در حال ساخت با مجموعهوضعیتهای بسطیافته (در قالب .dot).
- --dump-interf
- گزینهٔ اشکالزدایی: خروجی دادن جدول تداخل حاصل از تحلیل زنده بودن متغیرهای برچسب.
- --dump-nfa
- گزینهٔ اشکالزدایی: خروجی دادن NFA (در قالب .dot).
- --emit-dot -D
- تولید گراف تحلیلگر لغوی در قالب .dot به جای خروجی استاندارد. خروجی میتواند با کمک Graphviz به یک تصویر تبدیل شود (به عنوان مثال دستوری مانند dot -Tpng -odfa.png dfa.dot).
- --dfa-minimization <moore | table>
- گزینهٔ داخلی: الگوریتم کمینهسازی DFA مورداستفاده توسط re2go. گزینهٔ moore الگوریتم مور (Moore) است (که پیشفرض است). گزینهٔ table الگوریتم "پر کردن جدول" است. هر دو الگوریتم باید به جز برچسبگذاری مجدد وضعیتها، DFA یکسانی تولید کنند؛ پر کردن جدول سادهتر و بسیار کندتر بوده و به عنوان پیادهسازی مرجع عمل میکند.
- --eager-skip
- گزینهٔ داخلی: پیش بردن موقعیت ورودی در تحلیلگر لغوی تولیدشده به صورت مشتاقانه (eager) -- بلافاصله پس از خواندن نماد ورودی. این گزینه رفتار پیشفرض را که در آن موقعیت ورودی به صورت تنبلانه (lazy) و پس از انتقال به وضعیت بعدی پیش برده میشود تغییر میدهد.
- --no-lookahead
- گزینهٔ داخلی، منسوخ شده. قبلاً برای فعالسازی الگوریتم TDFA(0) به کار میرفت. بر خلاف TDFA(1)، الگوریتم TDFA(0) از پیشنگری یکنمادی استفاده نمیکند. این الگوریتم عملیات ثبات را به جای انتقالهای خروجی روی انتقالهای ورودی اعمال میکند. ارزیابیها نشان دادند که الگوریتم TDFA(0) کارایی کمتری نسبت به TDFA(1) دارد.
- --no-optimize-tags
- گزینهٔ داخلی: جلوگیری از بهینهسازی متغیرهای برچسب (مفید برای اشکالزدایی).
- --posix-closure <gor1 | gtop>
- گزینهٔ داخلی: مشخص کردن الگوریتم کوتاهترین مسیر مورداستفاده برای ساخت بستار اپسیلون با معناشناسی رفع ابهام POSIX: گزینهٔ gor1 (پیشفرض) بیانگر الگوریتم گلدبرگ-رادزیک (Goldberg-Radzik) است، و gtop بیانگر الگوریتم "ترتیب توپولوژیک سراسری" است.
- --posix-prectable <complex | naive>
- گزینهٔ داخلی: مشخص کردن الگوریتم مورداستفاده برای محاسبهٔ جدول اولویت POSIX. الگوریتم complex جدول اولویت را در یک پیمایش از درخت تاریخچهٔ برچسب محاسبه میکند و دارای پیچیدگی درجه دو بر حسب تعداد وضعیتهای TNFA است؛ این حالت پیشفرض است. الگوریتم naive در بدترین حالت پیچیدگی مکعبی بر حسب تعداد وضعیتهای TNFA دارد، اما بسیار سادهتر از complex است و ممکن است در موارد غیرپاتولوژیک کمی سریعتر باشد..TP --stadfa گزینه داخلی، منسوخشده. در گذشته برای فعالسازی الگوریتم staDFA استفاده میشد، که تفاوت آن با TDFA در این است که عملیاتهای ثبات به جای گذارها، در وضعیتها قرار میگیرند. ارزیابیها نشان دادند که الگوریتم staDFA کارایی کمتری نسبت به TDFA دارد.
- --fixed-tags <none | toplevel | all>
- گزینه داخلی: مشخص میکند که آیا بهینهسازی برچسب ثابت (fixed-tag) باید روی همهٔ برچسبها (all)، هیچکدام از آنها (none)، یا فقط برچسبهای موجود در الحاق سطح بالا (toplevel) اعمال شود یا خیر. مقدار پیشفرض all است. برچسبهای "ثابت" برچسبهایی هستند که در فاصلهٔ ثابتی از برچسب دیگری (به نام "base" یا پایه) قرار دارند. در چنین مواردی فقط برچسب پایه نیاز به رهگیری دارد و مقدار برچسب ثابت میتواند از طریق مقدار برچسب پایه بهعلاوهٔ یک آفست ایستا محاسبه شود. برای برچسبهایی که تحت انشعاب یا تکرار هستند، بررسی اینکه آیا برچسب پایه مقدار عدم تطابق (no-match) دارد نیز ضروری است (در این حالت برچسب ثابت نیز باید بدون در نظر گرفتن آفست، روی no-match تنظیم شود). برای برچسبها در الحاق سطح بالا این بررسی لازم نیست، زیرا آنها همیشه مطابقت دارند.
هشدارها (Warnings)
هشدارها را میتوان بهطور جداگانه فعال، غیرفعال یا به یک خطا تبدیل کرد.
- -W
- روشن کردن تمام هشدارها.
- -Werror
- تبدیل هشدارها به خطا. توجه داشته باشید که این گزینه به تنهایی هیچ هشداری را روشن نمیکند؛ بلکه تنها بر هشدارهایی تأثیر میگذارد که تاکنون روشن شدهاند یا بعداً روشن خواهند شد.
- -W<warning>
- روشن کردن warning.
- -Wno-<warning>
- خاموش کردن warning.
- -Werror-<warning>
- روشن کردن warning و تلقی کردن آن به عنوان خطا (این متضمن -W<warning> است).
- -Wno-error-<warning>
- با این warning خاص به عنوان خطا رفتار نکن. این کار خود هشدار را خاموش نمیکند.
- -Wcondition-order
- هشدار در صورتی که برنامهٔ تولیدشده فرضیات ضمنی دربارهٔ شمارهگذاری شرایط داشته باشد. باید از گزینهٔ --header یا بلوک conditions برای تولید نگاشتی از نام شرطها به اعداد استفاده کرده و سپس از نامهای خودکار تولیدشدهٔ شرایط استفاده شود.
- -Wempty-character-class
- هشدار در صورتی که یک عبارت منظم حاوی یک رده نویسهای خالی باشد. تلاش برای تطبیق با یک رده نویسهای خالی بیمعنی است: این کار همیشه باید ناموفق باشد. با این حال، به دلایل سازگاری عقبرو، re2go ردههای نویسهای خالی را مجاز دانسته و با آنها مانند رشتههای خالی رفتار میکند. از گزینهٔ --empty-class برای تغییر رفتار پیشفرض استفاده کنید.
- -Wmatch-empty-string
- هشدار در صورتی که یک قاعده تهیپذیر باشد (با یک رشتهٔ خالی تطبیق یابد). اگر تحلیلگر لغوی در یک حلقه اجرا شود و تطبیق تهی ناخواسته باشد، ممکن است تحلیلگر لغوی بهطور غیرمنتظره در یک حلقهٔ بیپایان متوقف شود (hang کند).
- -Wswapped-range
- هشدار در صورتی که کران پایین یک بازه از کران بالای آن بزرگتر باشد. رفتار پیشفرض این است که بدون هشدار جای کرانهای بازه با یکدیگر عوض شود.
- -Wundefined-control-flow
- هشدار در صورتی که برخی از رشتههای ورودی باعث جریان کنترل تعریفنشده در تحلیلگر لغوی شوند (الگوهای دارای اشکال گزارش میشوند). این یک خطای خطرناک و رایج است. میتوان آن را با افزودن قاعدهٔ پیشفرض * که کمترین اولویت را دارد، با هر واحد کد مطابقت دارد و همیشه یک واحد کد منفرد را مصرف میکند، به آسانی اصلاح کرد.
- -Wunreachable-rules
- هشدار دربارهٔ قواعدی که توسط قواعد دیگر تحتالشعاع قرار گرفتهاند (shadowed) و هرگز مطابقت نخواهند یافت.
- -Wdeprecated-eof_rule
- هشدار دربارهٔ قواعد مستقل پایان ورودی $ که با تغییرات آینده دچار شکست خواهند شد و نیاز به اصلاح دارند. در حال حاضر، این قواعد در زمان تداخل با قواعد دیگر اولویت دارند، اما پس از معرفی نماد تعمیمیافتهٔ پایان ورودی $، ترتیب اولویت تغییر خواهد کرد و این قواعد توسط قواعد دیگر تحتالشعاع قرار خواهند گرفت.
- -Wuseless-escape
- هشدار در صورتی که یک نماد در جایی که نباید، گریز (escape) داده شود. بهطور پیشفرض، re2go بدون هشدار چنین گریزهایی را نادیده میگیرد، اما این حالت میتواند نشاندهندهٔ یک غلط املایی یا خطا در توالی گریز باشد.
- -Wnondeterministic-tags
- هشدار در صورتی که یک برچسب دارای درجهٔ n-ام از عدم قطعیت باشد، جایی که n بزرگتر از ۱ است.
- -Wsentinel-in-midrule
- هشدار در صورتی که نماد نگهبان در وسط یک قاعده رخ دهد --- این امر ممکن است باعث خواندن داده فراتر از انتهای بافر، از کار افتادن (crash) یا تخریب حافظه در تحلیلگر لغوی تولیدشده شود. این هشدار تنها در صورتی کاربرد دارد که از روش نماد نگهبان برای بررسی پایان ورودی استفاده شود. در صورت استفاده از پیکربندی re2c:sentinel، این وضعیت به یک خطا تبدیل خواهد شد.
- -Wundefined-syntax-config
- هشدار در
صورتی که
فایل نحو
مشخصشده
با گزینهٔ
--syntax فاقد
تعاریف
برخی از
پیکربندیها
باشد. این
ویژگی به
نگهداری
فایلهای
نحو
تعریفشده
توسط کاربر
کمک میکند:
اگر نسخهٔ
جدیدی
پیکربندیهایی
را اضافه
کند، فایل
نحو قدیمی
هشداری را
صادر کرده و
به کاربر
اطلاع داده
خواهد شد.
اگر برخی از
پیکربندیها
استفاده
نمیشوند و
نیازی به
تعریف
ندارند،
باید
بهطور
صریح روی
<undefined> تنظیم
شوند.
پشتیبانی از زبانهای گوناگون در re2c بر پایه ایده پروندههای نحو (syntax files) بنا شده است. یک پرونده نحو، یک پرونده پیکربندی است که نحو زبان مقصد را تعریف میکند -- نه کل زبان، بلکه بخش کوچکی از آن را که توسط کد تولیدشده استفاده میشود. پروندههای نحو re2c را بسیار منعطف میسازند، اما نباید به عنوان جایگزینی برای پیکربندیهای re2c: استفاده شوند: هدف آنها تعریف نحو زبان مقصد است، نه سفارشیسازی یک تحلیلگر لغوی خاص. تمام زبانهای پشتیبانیشده دارای پروندههای نحو پیشفرضی هستند که بخشی از بسته توزیع به شمار میروند (زیرپوشه include/syntax را ببینید)؛ آنها همچنین در باینری re2go نیز گنجانده شدهاند. کاربران میتوانند یک پرونده نحو سفارشی ارائه دهند که چند پیکربندی را برای یکی از زبانهای پشتیبانیشده بازنویسی میکند، یا میتوانند همه پیکربندیها را مجدداً تعریف نمایند (در این حالت گزینه --lang none باید استفاده شود). پروندههای نحو شامل چهار گونه پیکربندی مختلف هستند: فهرست ویژگیها، پیکربندیهای زبان، پیکربندیهای درجا (inplace) و قالبهای کد.
فهرست ویژگیها (Feature lists)
- supported_apis
- فهرستی از APIهای پشتیبانیشده با عنصرهای ممکن simple، record، generic.
- supported_api_styles
- فهرستی از سبکهای API پشتیبانیشده با عنصرهای ممکن functions، free-form.
- supported_code_models
- فهرستی از مدلهای کد پشتیبانیشده با عنصرهای ممکن goto-label، loop-switch، recursive-functions.
- supported_targets
- فهرستی از مقصدهای تولید کد (codegen) پشتیبانیشده با عنصرهای ممکن code، dot، skeleton.
- supported_features
- فهرستی از ویژگیهای پشتیبانیشده با عنصرهای ممکن nested-ifs، bitmaps، computed-gotos، case-ranges، monadic، unsafe، tags، captures، captvars.
پیکربندیهای زبان (Language configurations)
- semicolons
- غیر صفر اگر زبان پس از گزارهها از نقطهویرگول (semicolon) استفاده میکند.
- backtick_quoted_strings
- غیر صفر اگر زبان دارای رشتههای محصور در بکتیک (backtick) است.
- single_quoted_strings
- غیر صفر اگر زبان دارای رشتههای محصور در تکنقلقول (single quote) است.
- indentation_sensitive
- غیر صفر اگر زبان به دندانهگذاری (indentation) حساس است.
- wrap_blocks_in_braces
- غیر صفر اگر گزارههای مرکب باید در آکولاد محصور شوند.
پیکربندیهای درجا (Inplace configurations)
قالبهای کد (Code templates)
code-template ::
name '=' code-exprs ';'
| CODE_TEMPLATE ';'
| '<undefined>' ';'
code-exprs ::
<EMPTY>
| code-exprs code-expr
code-expr ::
STRING
| VARIABLE
| optional
| list
optional ::
'(' CONDITIONAL '?' code-exprs ')'
| '(' CONDITIONAL '?' code-exprs ':' code-exprs ')'
list ::
'[' VARIABLE ':' code-exprs ']'
| '[' VARIABLE '{' NUMBER '}' ':' code-exprs ']'
| '[' VARIABLE '{' NUMBER ',' NUMBER '}' ':' code-exprs ']'
یک قالب کد دنبالهای از رشتههای لفظی، متغیرها، عنصرهای اختیاری و فهرستها، یا ارجاعی به یک قالب کد دیگر، یا مقدار ویژه <undefined> است. متغیرها جاینگهدارندههایی هستند که در طول مرحله تولید کد جایگزین میشوند. متغیرهای فهرست خاص هستند: هنگام بسط دادن قالبهای فهرست، re2go عبارتهای سمت راست دونقطه را چند بار تکرار میکند و هر بار رخدادهای متغیر فهرست را با مقداری مختص این تکرار جایگزین میسازد. فهرستها دارای کرانهای اختیاری هستند (مقدارهای منفی از انتها شمرده میشوند، مثلاً -1 به معنای آخرین عنصر است). نام شرطیها با یک نقطه آغاز میشود. هم شرطیها و هم متغیرها میتوانند محلی (مخصوص قالب کد دادهشده) یا سراسری (مجاز در تمام قالبهای کد) باشند. هنگامی که re2go یک پرونده نحو را میخواند، بررسی میکند که هر قالب کد تنها از متغیرها و شرطیهایی استفاده کند که در آن مجاز هستند.
برای نمونه، قالب کد زیر ساختار 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
}
در ادامه فهرستی از تمام قالبهای کد پشتیبانیشده توسط re2go به همراه متغیرها و شرطیهای محلی آنها آورده شده است. توجه داشته باشید که یک تعریف خاص میتواند از متغیرها و شرطیهای محلی استفاده کند، اما الزامی به آن ندارد. هر قالب کد استفادهنشده باید برابر با <undefined> تنظیم شود.
- code:var_global
- مشابه code:var_local، با این تفاوت که در بالاترین سطح (top-level) استفاده میشود.
- code:const_local
- تعریف یک ثابت محلی. متغیرهای پشتیبانیشده: type (نوع ثابت)، name (نام آن) و init (مقدار اولیه).
- code:const_global
- مشابه code:const_local، با این تفاوت که در بالاترین سطح استفاده میشود.
- code:array_local
- تعریف یک آرایه (جدول) محلی. متغیرهای پشتیبانیشده: type (نوع عناصر آرایه)، name (نام آرایه)، size (اندازه آن)، row (متغیر فهرستی که به خودی خود کدی تولید نمیکند، اما عبارت فهرست را به تعداد سطرهای جدول گسترش میدهد) و elem (متغیر فهرستی که به تمام عناصر جدول در سطر جاری بسط مییابد -- برای قرارگیری درون فهرست row در نظر گرفته شده است). شرط پشتیبانیشده: .const (در صورتی که آرایه تغییرناپذیر باشد، درست است).
- code:array_global
- مشابه code:array_local، با این تفاوت که در بالاترین سطح استفاده میشود.
- code:array_elem
- ارجاع به عنصری از یک آرایه (جدول). متغیرهای پشتیبانیشده: array (نام آرایه) و index (نمایه عنصر).
- code:enum
- تعریف یک شمارش یا enumeration (ممکن است با استفاده از یک سازهٔ زبانی ویژه برای شمارشها تعریف شود، یا صرفاً به عنوان چند مقدار ثابت مستقل). متغیرهای پشتیبانیشده عبارتند از type (نوع شمارش تعریفشده توسط کاربر یا نوع مقادیر ثابت)، elem (متغیر فهرستی که به نام هر عضو بسط مییابد) و init (مقداردهنده اولیه برای هر عضو). شرطها: .init (در صورت وجود مقداردهنده اولیه، درست است).
- code:enum_elem
- عنصر شمارش (عضوی از نوع شمارش تعریفشده توسط کاربر یا نام یک ثابت، بسته به نحوه تعریف code:enum). متغیرهای پشتیبانیشده عبارتند از name (نام عنصر) و type (نوع آن).
- code:assign
- دستور انتساب. متغیرهای پشتیبانیشده عبارتند از lhs (سمت چپ) و rhs (سمت راست).
- code:type_int
- نوع عدد صحیح علامتدار (signed integer).
- code:type_uint
- نوع عدد صحیح بدون علامت (unsigned integer).
- code:type_yybm
- نوع عناصر در جدول yybm.
- code:type_yytarget
- نوع عناصر در جدول yytarget.
- code:type_yyctable
- نوع عناصر در جدول yyctable.
- code:cmp_eq
- عملگر «برابر» ("equals").
- code:cmp_ne
- عملگر «نامساوی» ("not equals").
- code:cmp_lt
- عملگر «کوچکتر از» ("less than").
- code:cmp_gt
- عملگر «بزرگتر از» ("greater than").
- code:cmp_le
- عملگر «کوچکتر یا مساوی» ("less or equal").
- code:cmp_ge
- عملگر «بزرگتر یا مساوی» ("greater or equal").
- code:if_then_else
- دستور if-then-else با یک یا چند شاخه. متغیرهای پشتیبانیشده: branch (متغیر فهرستی که به خودی خود کدی تولید نمیکند، اما عبارت فهرست را به تعداد شاخهها بسط میدهد)، cond (شرط شاخه جاری) و stmt (متغیر فهرستی که به تمام دستورات در شاخه جاری بسط مییابد). شرطها: .cond (اگر شاخه جاری دارای شرط باشد، درست است)، .many (اگر بیش از یک شاخه وجود داشته باشد، درست است).
- code:if_then_else_oneline
- یک حالت خاص از code:if_then_else برای حالتی که همه شاخهها دستورات تکخطی دارند. اگر این مقدار <undefined> باشد، به جای آن از code:if_then_else استفاده میشود.
- code:switch
- یک دستور switch با یک یا چند case. متغیرهای پشتیبانیشده: expr (عبارت مورد بررسی در switch) و case (متغیر فهرستی که به تمام گروههای case همراه با بلوکهای کدشان بسط مییابد).
- code:switch_cases
- گروهی از caseهای switch که به یک بلوک کد واحد نگاشت میشوند. متغیرهای پشتیبانیشده عبارتند از case (متغیر فهرستی که به تمام caseهای موجود در این گروه بسط مییابد) و stmt (متغیر فهرستی که به تمام دستورات موجود در بلوک کد بسط مییابد).
- code:switch_cases_oneline
- یک حالت خاص از code:switch_cases برای حالتی که بلوک کد شامل یک دستور تکخطی است. اگر این مقدار <undefined> باشد، به جای آن از code:switch_cases استفاده میشود.
- code:switch_case_range
- یک case منفرد switch که بازهای از مقادیر را پوشش میدهد (احتمالاً شامل یک مقدار منفرد). متغیر پشتیبانیشده: val (متغیر فهرستی که به تمام مقادیر بازه بسط مییابد). شرطهای پشتیبانیشده: .many (اگر بیش از یک مقدار در بازه وجود داشته باشد، درست است) و .char_literals (اگر این switch بر روی نویسههای ثابت یا کاراکتر لیترال باشد، درست است -- برخی زبانها نحو ویژهای برای این حالت ارائه میدهند).
- code:switch_case_default
- حالت پیشفرض (default) در switch.
- code:loop
- حلقهای که تا ابد اجرا میشود (مگر اینکه از درون بدنه حلقه متوقف شود). متغیرهای پشتیبانیشده: label (برچسب حلقه)، stmt (متغیر فهرستی که به تمام دستورات موجود در بدنه حلقه بسط مییابد).
- code:continue
- دستور continue. متغیرهای پشتیبانیشده: label (برچسبی که اجرای برنامه از آن ادامه مییابد).
- code:goto
- دستور goto. متغیرهای پشتیبانیشده: label (برچسب مقصد پرش).
- code:cgoto
- دستور goto محاسباتی (computed 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 (نسخهٔ re2go که برای تولید این فایل استفاده شده است) و date (تاریخ تولید).
- code:line_info
- قالب دستورالعملهای خط (اگر روی <undefined> تنظیم شود، هیچ دستورالعملی تولید نمیشود). متغیرهای پشتیبانیشده: line (شماره خط) و file (نام فایل).
- code:abort
- عبارتی که اجرای برنامه را متوقف (abort) میکند.
- code:yydebug
- عبارت YYDEBUG، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYDEBUG، yyrecord، yych (نگاشت به پیکربندیهای متناظر re2c:)، state (شماره وضعیت DFA).
- code:yypeek
- عبارت YYPEEK، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYPEEK، YYCTYPE، YYINPUT، YYCURSOR، yyrecord، yych (نگاشت به پیکربندیهای متناظر re2c:). شروط: .cast (درست اگر re2c:yych:conversion روی مقداری غیر از صفر تنظیم شده باشد).
- code:yyskip
- عبارت YYSKIP، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYSKIP، YYCURSOR، yyrecord (نگاشت به پیکربندیهای متناظر re2c:).
- code:yybackup
- عبارت YYBACKUP، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYBACKUP، YYCURSOR، YYMARKER، yyrecord (نگاشت به پیکربندیهای متناظر re2c:).
- code:yybackupctx
- عبارت YYBACKUPCTX، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYBACKUPCTX، YYCURSOR، YYCTXMARKER، yyrecord (نگاشت به پیکربندیهای متناظر re2c:).
- code:yyskip_yypeek
- عبارت ترکیبی code:yyskip و code:yypeek (بهطور پیشفرض code:yyskip و سپس code:yypeek).
- code:yypeek_yyskip
- عبارت ترکیبی code:yypeek و code:yyskip (بهطور پیشفرض code:yypeek و سپس code:yyskip).
- code:yyskip_yybackup
- عبارت ترکیبی code:yyskip و code:yybackup (بهطور پیشفرض code:yyskip و سپس code:yybackup).
- code:yybackup_yyskip
- عبارت ترکیبی code:yybackup و code:yyskip (بهطور پیشفرض code:yybackup و سپس code:yyskip).
- code:yybackup_yypeek
- عبارت ترکیبی code:yybackup و code:yypeek (بهطور پیشفرض code:yybackup و سپس code:yypeek).
- code:yyskip_yybackup_yypeek
- عبارت ترکیبی code:yyskip، code:yybackup و code:yypeek (بهطور پیشفرض ``code:yyskip`` و سپس code:yybackup و به دنبال آن code:yypeek).
- code:yybackup_yypeek_yyskip
- عبارت ترکیبی code:yybackup، code:yypeek و code:yyskip (بهطور پیشفرض ``code:yybackup`` و سپس code:yypeek و به دنبال آن code:yyskip).
- code:yyrestore
- عبارت YYRESTORE، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYRESTORE، YYCURSOR، YYMARKER، yyrecord (نگاشت به پیکربندیهای متناظر re2c:).
- code:yyrestorectx
- عبارت YYRESTORECTX، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYRESTORECTX، YYCURSOR، YYCTXMARKER، yyrecord (نگاشت به پیکربندیهای متناظر re2c:).
- code:yyrestoretag
- عبارت YYRESTORETAG، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYRESTORETAG، YYCURSOR، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، tag (نام متغیر برچسب که برای بازیابی موقعیت استفاده میشود).
- code:yyshift
- عبارت YYSHIFT، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYSHIFT، YYCURSOR، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، offset (تعداد واحدهای کد برای جابهجا کردن موقعیت فعلی).
- code:yyshiftstag
- عبارت YYSHIFTSTAG، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYSHIFTSTAG، yyrecord، negative (نگاشت به پیکربندیهای متناظر re2c:)، tag (متغیر برچسبی که باید جابهجا شود)، offset (تعداد واحدهای کد برای جابهجایی). شروط: .nested (درست اگر این یک برچسب تودرتو باشد -- در این حالت مقدار آن ممکن است برابر با re2c:tags:negative باشد، که نباید جابهجا شود).
- code:yyshiftmtag
- عبارت YYSHIFTMTAG، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYSHIFTMTAG (نگاشت به پیکربندی متناظر re2c:)، tag (متغیر برچسبی که باید جابهجا شود)، offset (تعداد واحدهای کد برای جابهجایی).
- code:yystagp
- عبارت YYSTAGP، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYSTAGP، YYCURSOR، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، tag (متغیر برچسبی که باید بهروزرسانی شود).
- code:yymtagp
- عبارت YYMTAGP، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYMTAGP (نگاشت به پیکربندی متناظر re2c:)، tag (متغیر برچسبی که باید بهروزرسانی شود).
- code:yystagn
- عبارت YYSTAGN، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYSTAGN، negative، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، tag (متغیر برچسبی که باید بهروزرسانی شود).
- code:yymtagn
- عبارت YYMTAGN، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYMTAGN (نگاشت به پیکربندی متناظر re2c:)، tag (متغیر برچسبی که باید بهروزرسانی شود).
- code:yycopystag
- عبارت YYCOPYSTAG، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYCOPYSTAG، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، lhs، rhs (متغیرهای برچسب سمت چپ و راست در عملیات رونوشت).
- code:yycopymtag
- عبارت YYCOPYMTAG، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYCOPYMTAG، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، lhs، rhs (متغیرهای برچسب سمت چپ و راست در عملیات رونوشت).
- code:yygetaccept
- عبارت YYGETACCEPT، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYGETACCEPT، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، var (نگاشت به پیکربندی re2c:yyaccept).
- code:yysetaccept
- عبارت YYSETACCEPT، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYSETACCEPT، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، var (نگاشت به پیکربندی re2c:yyaccept) و val (مقدار عددی قاعدهٔ پذیرفتهشده).
- code:yygetcond
- عبارت YYGETCOND، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYGETCOND، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، var (نگاشت به پیکربندی re2c:yycond).
- code:yysetcond
- عبارت YYSETCOND، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYSETCOND، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، var (نگاشت به پیکربندی re2c:yycond) و val (شناسهٔ عددی شرط).
- code:yygetstate
- عبارت YYGETSTATE، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYGETSTATE، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، var (نگاشت به پیکربندی re2c:yystate).
- code:yysetstate
- عبارت YYSETSTATE، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYSETSTATE، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، var (نگاشت به پیکربندی re2c:yystate) و val (شماره وضعیت).
- code:yylessthan
- عبارت YYLESSTHAN، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYLESSTHAN، YYCURSOR، YYLIMIT، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، need (تعداد واحدهای کد برای بررسی در برابر آن). شرط: .many (درست اگر need بیش از یک باشد)..TP code:yyend عبارت YYEND، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYEND، YYCURSOR، YYLIMIT.
- code:yybm_filter
- شرطی که برای فیلتر کردن مقادیر yych که تحت پوشش جدول yybm نیستند استفاده میشود (همراه با گزینهٔ --bitmaps به کار میرود). متغیر پشتیبانیشده: yych (به پیکربندی re2c:yych نگاشت میشود).
- code:yybm_match
- قالب بررسی جدول yybm (تولیدشده با گزینهٔ --bitmaps). متغیرهای پشتیبانیشده: yybm، yych (به پیکربندیهای متناظر re2c: نگاشت میشوند)، offset (آفست در جدول yybm که باید به yych اضافه شود) و mask (ماسک بیتی که باید روی مدخل جدول اعمال شود تا مقدار بولی که باید بررسی شود بازیابی گردد).
- code:yytarget_filter
- شرطی که برای فیلتر کردن مقادیر yych که تحت پوشش جدول yytarget نیستند استفاده میشود (همراه با گزینهٔ --computed-gotos به کار میرود). متغیر پشتیبانیشده: yych (به پیکربندی re2c:yych نگاشت میشود).
در اینجا فهرستی از تمام متغیرهای سراسری که در فایلهای نحو مجاز هستند آمده است:
در اینجا فهرستی از تمام شرطهای سراسری که در فایلهای نحو مجاز هستند آمده است:
- .api.simple
- درست (True) اگر از API ساده استفاده شود (--api simple یا re2c:api = simple).
- .api.generic
- درست (True) اگر از API عمومی استفاده شود (--api generic یا re2c:api = generic).
- .api.record
- درست (True) اگر از API ثبتی استفاده شود (--api record یا re2c:api = record).
- .api_style.functions
- درست (True) اگر از سبک API تابعگونه استفاده شود (re2c:api-style = functions).
- .api_style.freeform
- درست (True) اگر از سبک API آزاد استفاده شود (re2c:api-style = free-form).
- .case_ranges
- درست (True) اگر قابلیت محدودههای مورد فعال باشد (--case-ranges یا re2c:case-ranges = 1).
- .cgoto.relative
- درست (True) اگر شکل نسبی goto محاسبهشده استفاده شود (--computed-gotos-relative یا re2c:cgoto:relative = 1).
- .code_model.goto_label
- درست (True) اگر مدل کد مبتنی بر goto/label استفاده شود (--goto-label).
- .code_model.loop_switch
- درست (True) اگر مدل کد مبتنی بر loop/switch استفاده شود (--loop-switch).
- .code_model.recursive_functions
- درست (True) اگر مدل کد مبتنی بر توابع بازگشتی استفاده شود (--recursive-function).
- .date
- درست (True) اگر اثر انگشت تولیدشده باید شامل تاریخ تولید باشد.
- .loop_label
- درست (True) اگر حلقههای تولیدشده توسط re2go باید دارای برچسب باشند (re2c:label:yyloop روی یک رشتهٔ غیرخالی تنظیم شده باشد).
- .monadic
- درست (True) اگر کد تولیدشده باید مونادیک باشد (re2c:monadic = 1). این مورد فقط برای زبانهای کاملاً تابعی مرتبط است.
- .start_conditions
- درست (True) اگر شرایط شروع فعال باشند (--start-conditions).
- .storable_state
- درست (True) اگر وضعیت قابل ذخیرهسازی فعال باشد (--storable-state).
- .unsafe
- درست (True) اگر re2go باید از بلوکهای "unsafe" بهمنظور تولید کد سریعتر استفاده کند (--unsafe، re2c:unsafe = 1). این تنها برای زبانهایی که ویژگی "unsafe" دارند مرتبط است.
- .version
- درست (True) اگر اثر انگشت تولیدشده باید شامل نسخهٔ re2go باشد.
- .yyfill.enable
- درست (True) اگر YYFILL فعال باشد (re2c:yyfill:enable = 1).
- .yyfn.throw
- درست (True) اگر پیکربندی re2c:yyfn:throw به یک رشتهٔ غیرخالی تعریف شده باشد.
یکی از مسائل اصلی تحلیلگر واژگانی (lexer) این است که بداند چه زمانی باید متوقف شود. چندین شرط پایانی وجود دارد:
- تحلیلگر ممکن است با قاعدهای (از جمله قاعدهٔ پیشفرض *) مطابقت پیدا کند و به یک وضعیت پایانی برسد
- تحلیلگر ممکن است در تطبیق با هر قاعدهای شکست بخورد و به یک وضعیت پیشفرض برسد
- تحلیلگر ممکن است به پایان ورودی برسد
دو شرط نخست، تحلیلگر را به روشی «طبیعی» پایان میدهند: تحلیلگر به وضعیتی بدون هیچ انتقال خروجی میرسد و تطبیق بهطور خودکار متوقف میشود. شرط سوم، یعنی پایان ورودی، متفاوت است: این حالت ممکن است در هر وضعیتی رخ دهد و تحلیلگر باید بتواند آن را مدیریت کند. بررسی پایان ورودی، گردش کار عادی تحلیلگر را مختل کرده و شاخههای شرطی را به برنامهٔ تولیدشده اضافه میکند؛ بنابراین ضروری است که تعداد چنین بررسیهایی به حداقل برسد. re2go از چند روش گوناگون برای مدیریت پایان ورودی پشتیبانی میکند. اینکه از کدام روش استفاده شود به پیچیدگی عبارات باقاعده، نیاز به بافرسازی، ملاحظات کارایی و سایر عوامل بستگی دارد. در ادامه فهرستی از این روشها آورده شده است:
- دیدهبان (Sentinel). این روش نیاز به بررسیهای پایان ورودی را بهطور کامل از بین میبرد. این روش ساده و کارآمد است، اما محدود به مواردی است که یک نویسهٔ «دیدهبان» (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 را پوشش دهد. این کار تعداد بررسیها را کاهش میدهد، اما مشکلی در رابطه با واژکهای کوتاه در انتهای ورودی ایجاد میکند، زیرا بررسی نیازمند نویسههای کافی برای پوشش دادن طولانیترین واژک است. این مشکل میتواند با پدگذاری ورودی با چند نویسهٔ ساختگی که پسوند واژک معتبری را تشکیل نمیدهند (بهطوری که تحلیلگر نتواند آنها را مطابقت دهد) برطرف شود. طول پدگذاری باید YYMAXFILL باشد که با یک بلوک max تولید میشود. اگر ورودی کافی نباشد، تحلیلگر YYFILL را فراخوانی میکند که باید حداقل تعداد نویسهٔ مورد نیاز را تأمین کند یا بازگشتی نداشته باشد. این روش در صورتی استفاده میشود که YYFILL فعال باشد و re2c:eof مقدار -1 داشته باشد (این پیکربندی پیشفرض است).
- بررسیهای سفارشی (Custom checks). رابط Generic API امکان بازتعریف (override) عملیات پایهای مانند خواندن یک نویسه را فراهم میکند، که این امر گنجاندن بررسیهای پایان ورودی را بهعنوان بخشی از آنها ممکن میسازد. این رویکرد مستعد خطا است و باید با احتیاط استفاده شود. برای استفاده از روش سفارشی، Generic API را با --api custom یا re2c:api = custom; فعال کرده و بررسیهای پیشفرض مرز را با re2c:yyfill:enable = 0; یا re2c:yyfill:check = 0; غیرفعال کنید.
زیربخشهای زیر شامل مثالی از هر روش هستند..SS دیدهبان (Sentinel)
این مثال از یک نویسهٔ دیدهبان (sentinel) برای مدیریت پایان ورودی استفاده میکند. این برنامه تعداد کلمات جداشده با فاصله را در یک رشتهٔ پایانیافته با null میشمارد. دیدهبان همان null است: آخرین نویسه از هر رشتهٔ ورودی است و توسط هیچیک از قواعد در میانهٔ یک واحد واژگانی (lexeme) مجاز دانسته نشده است (بهویژه، در بازههای نویسهای که ممکن است بهسادگی نادیده گرفته شوند گنجانده نشده است). اگر یک null در میانهٔ یک رشته رخ دهد، یک خطای نحوی است و تحلیلگر لغوی قاعدهٔ پیشفرض * را مطابقت میدهد، اما دادهای فراتر از پایان ورودی نخواهد خواند و دچار فروپاشی (crash) نخواهد شد (از هشدار -Wsentinel-in-midrule و پیکربندی re2c:sentinel برای راستیآزمایی این مورد استفاده کنید). پیکربندی re2c:yyfill:enable = 0; مانع از تولید بررسیهای مرز و فراخوانیهای YYFILL میشود.
//go:generate re2go $INPUT -o $OUTPUT --api simple
package main
// Expect a null-terminated string.
func lex(yyinput string) int {
yycursor := 0
count := 0
for { /*!re2c
re2c:yyfill:enable = 0;
re2c:YYCTYPE = byte;
* { return -1 }
[\x00] { return count }
[a-z]+ { count += 1; continue }
[ ]+ { continue }
*/
}
}
func main() {
assert_eq := func(x, y int) { if x != y { panic("error") } }
assert_eq(lex("\000"), 0)
assert_eq(lex("one two three\000"), 3)
assert_eq(lex("f0ur\000"), -1)
}
دیدهبان همراه با بررسیهای مرز (Sentinel with bounds checks)
این مثال برای مدیریت پایان ورودی، از دیدهبان همراه با بررسیهای مرز استفاده میکند (این روش در نگارش 1.2 افزوده شد). این برنامه رشتههای نقلقولشدهٔ تکیِ (single-quoted) جداشده با فاصله را میشمارد. نویسهٔ دیدهبان null است که با پیکربندی re2c:eof = 0; مشخص میشود. همانند روش sentinel، نویسهٔ null آخرین نویسهٔ هر رشتهٔ ورودی است، اما در میانهٔ یک قاعده مجاز است (برای مثال، 'aaa\0aa'\0 ورودی معتبر است، اما 'aaa\0 خطای نحوی بهشمار میرود). بررسیهای مرز در هر وضعیتی که با یک نویسهٔ ورودی مطابقت دارد تولید میشوند، اما دامنهٔ آنها به شاخهای که null را مدیریت میکند محدود است. بررسیهای مرز در generic API به شکل YYLIMIT <= YYCURSOR یا YYLESSTHAN(1) هستند. اگر شرط بررسی برقرار باشد، تحلیلگر به پایان ورودی رسیده است و باید متوقف شود (YYFILL با re2c:yyfill:enable = 0; غیرفعال شده است چرا که کل ورودی در یک بافر جای میگیرد؛ برای مشاهدهٔ مثالی که از YYFILL استفاده میکند، به بخش YYFILL with sentinel مراجعه کنید). رسیدن به پایان ورودی سه احتمال به وجود میآورد: اگر تحلیلگر در وضعیت اولیه باشد، با قاعدهٔ پایان ورودی $ تطبیق مییابد، در غیر این صورت ممکن است به قاعدهای که پیشتر مطابقت یافته بازگردد (شامل قاعدهٔ پیشفرض *) یا به یک وضعیت پیشفرض برود که منجر به -Wundefined-control-flow میشود.
//go:generate re2go $INPUT -o $OUTPUT --api simple
package main
// Expects a null-terminated string.
func lex(yyinput string) int {
var yycursor, yymarker int
yylimit := len(yyinput) - 1 // lim points at the terminating null
count := 0
for { /*!re2c
re2c:YYCTYPE = byte;
re2c:yyfill:enable = 0;
re2c:eof = 0;
str = ['] ([^'\\] | [\\][^])* ['];
* { return -1 }
$ { return count }
str { count += 1; continue }
[ ]+ { continue }
*/
}
}
func main() {
assert_eq := func(x, y int) { if x != y { panic("error") } }
assert_eq(lex("\000"), 0)
assert_eq(lex("'qu\000tes' 'are' 'fine: \\'' \000"), 3)
assert_eq(lex("'unterminated\\'\000"), -1)
}
این مثال از بررسیهای محدوده همراه با لایهگذاری برای مدیریت پایان ورودی استفاده میکند (این روش بهطور پیشفرض فعال است). برنامه رشتههای محصور در نقلقول تکی را که با فاصله از هم جدا شدهاند شمارش میکند. یک لایهگذاری شامل YYMAXFILL نویسهٔ null به انتهای ورودی ضمیمه شده است، که مقدار YYMAXFILL بهطور خودکار توسط یک بلوک max تولید میشود. استفاده از null برای لایهگذاری الزامی نیست --- تا زمانی که نویسهها پسوند یک نشانهٔ معتبر (lexeme) را نسازند، میتوان از هر نویسهای استفاده کرد (در این مثال، لایهگذاری نباید شامل نقلقول تکی باشد، زیرا ممکن است به عنوان پسوند یک رشتهٔ نقلقولشده تکی اشتباه گرفته شود). یک قاعدهٔ "stop" وجود دارد که با نخستین نویسهٔ لایهگذاری (null) تطبیق یافته و تحلیلگر واژگانی (lexer) را خاتمه میدهد (توجه داشته باشید که بررسی میکند آیا null در ابتدای لایهگذاری است یا خیر، در غیر این صورت یک خطای نحوی است). بررسیهای محدوده تنها در وضعیتهایی تولید میشوند که توسط مؤلفههای قویاً همبندِ اتوماتون زیربنایی تعیین شده باشند. بررسیها با رابط عمومی (generic API) به صورت (YYLIMIT - YYCURSOR) < n یا YYLESSTHAN(n) هستند، که در آن n حداقل تعداد نویسههای مورد نیاز برای پیشروی تحلیلگر واژگانی است (این همچنین بدان معناست که بررسی محدودهٔ بعدی حداکثر تا n نویسهٔ دیگر رخ خواهد داد). اگر شرط بررسی برقرار باشد، تحلیلگر واژگانی به پایان ورودی رسیده است و YYFILL(n) را فراخوانی میکند که باید یا حداقل n نویسه ورودی را فراهم کند یا بازگشتی نداشته باشد. در این مثال YYFILL همواره با شکست مواجه میشود و تحلیلگر واژگانی را با یک خطا پایان میدهد (که مشکلی نیست زیرا کل ورودی در یک بافر جا میشود). برای مثالی که بافر ورودی را با YYFILL دوباره پر میکند، بخش YYFILL with padding را ببینید.
//go:generate re2go $INPUT -o $OUTPUT --api simple
package main
import "strings"
/*!max:re2c*/
// Expects YYMAXFILL-padded string.
func lex(str string) int {
// Pad string with YYMAXFILL zeroes at the end.
yyinput := str + strings.Repeat("\000", int(YYMAXFILL))
yycursor := 0
yylimit := len(yyinput)
count := 0
for { /*!re2c
re2c:YYCTYPE = byte;
re2c:YYFILL = "return -1";
str = ['] ([^'\\] | [\\][^])* ['];
[\x00] {
// Check that it is the sentinel, not some unexpected null.
if yycursor - 1 == len(str) { return count } else { return -1 }
}
str { count += 1; continue }
[ ]+ { continue }
* { return -1 }
*/
}
}
func main() {
assert_eq := func(x, y int) { if x != y { panic("error") } }
assert_eq(lex(""), 0)
assert_eq(lex("'qu\000tes' 'are' 'fine: \\'' "), 3)
assert_eq(lex("'unterminated\\'"), -1)
assert_eq(lex("'unexpected \000 null\\'"), -1)
}
بررسیهای سفارشی (Custom checks)
این مثال از یک روش سفارشی برای مدیریت پایان ورودی بر پایهٔ API عمومی استفاده میکند. برنامه رشتههای محصور در نقلقول تکی که با فاصله از هم جدا شدهاند را میشمارد. این برنامه مشابه مثال sentinel است، با این تفاوت که ورودی با null خاتمه نیافته است. برای جبران نبودِ نویسهٔ نگهبان (sentinel) در انتهای ورودی، YYPEEK مجدداً تعریف شده است تا پیش از خواندن نویسهٔ بعدی ورودی، یک بررسی محدوده انجام دهد. این روش ناکارآمد است زیرا بررسیها به دفعات بسیار زیادی انجام میشوند. اگر شرط بررسی برقرار نباشد، YYPEEK نویسهٔ واقعی را برمیگرداند، در غیر این صورت یک نویسهٔ نگهبان ساختگی را بازمیگرداند.
//go:generate re2go $INPUT -o $OUTPUT
package main
// Returns "fake" terminating null if cursor has reached limit.
func peek(str string, cur int) byte {
if cur >= len(str) {
return 0 // fake null
} else {
return str[cur]
}
}
// Expects a string without terminating null.
func lex(str string) int {
var cur int
count := 0
for { /*!re2c
re2c:yyfill:enable = 0;
re2c:YYCTYPE = byte;
re2c:YYPEEK = "peek(str, cur)";
re2c:YYSKIP = "cur += 1";
* { return -1 }
[\x00] { return count }
[a-z]+ { count += 1; continue }
[ ]+ { continue }
*/
}
}
func main() {
assert_eq := func(x, y int) { if x != y { panic("error") } }
assert_eq(lex(""), 0)
assert_eq(lex("one two three"), 3)
assert_eq(lex("f0ur"), -1)
}
نیاز به بافرسازی زمانی رخ میدهد که ورودی را نتوان به یکباره در حافظه نگاشت کرد: یا ورودی بیش از حد بزرگ است، یا به شکل جریانی (مانند خواندن از یک سوکت) وارد میشود. روش معمول در چنین مواردی، تخصیص یک بافر حافظه با اندازهٔ ثابت و پردازش ورودی در قطعاتی است که در این بافر جا میگیرند. هنگامی که قطعهٔ جاری پردازش شد، به بیرون منتقل شده و دادههای جدید وارد میشوند. در عمل این موضوع تا حدی پیچیدهتر است، زیرا وضعیت تحلیلگر لغوی (lexer) تنها از یک موقعیت ورودی تشکیل نشده، بلکه مجموعهای از موقعیتهای مرتبط به یکدیگر است:
- مکاننما (cursor): نویسهٔ ورودی بعدی که باید خوانده شود (YYCURSOR در API اشارهگر C یا YYSKIP/YYPEEK در API عمومی)
- حد (limit): موقعیت پس از آخرین نویسهٔ ورودیِ موجود (YYLIMIT در API اشارهگر C، که به صورت ضمنی توسط YYLESSTHAN در API عمومی مدیریت میشود)
- نشانگر (marker): موقعیت آخرین تطابق، در صورت وجود (YYMARKER در API پیشفرض یا YYBACKUP/YYRESTORE در API عمومی)
- توکن (token): ابتدای نشانهٔ لغوی (lexeme) جاری (در API مربوط به re2go ضمنی است، چرا که برای عملکرد عادی لکسر نیازی به آن نیست و میتواند توسط کاربر تعریف و بهروزرسانی شود)
- نشانگر بافتار (context marker): موقعیت بافتار پایانی (YYCTXMARKER در API اشارهگر C یا YYBACKUPCTX/YYRESTORECTX در API عمومی)
- متغیرهای برچسب (tag variables): موقعیتهای زیرتطابق (تعریفشده با بلوکهای stags و mtags و عملگرهای پایهٔ API عمومی شامل YYSTAGP/YYSTAGN/YYMTAGP/YYMTAGN)
همهٔ این موارد در هر شرایطی استفاده نمیشوند، اما در صورت استفاده، باید توسط YYFILL بهروزرسانی شوند. تمام موقعیتهای فعال در بازهٔ میان token و cursor قرار دارند، بنابراین هر آنچه میان ابتدای بافر و token است میتواند دور ریخته شود، بخش میان token تا limit باید به ابتدای بافر منتقل شود، و فضای خالی انتهای بافر باید با دادههای جدید پر گردد. برای جلوگیری از فراخوانیهای مکرر YYFILL، بهتر است تا حد امکان نویسههای ورودی بیشتری پر شوند (حتی اگر نویسههای کمتری برای از سرگیری کار لکسر کافی باشد). جزئیات پیادهسازی YYFILL بسته به روش مدیریت پایان فایل (EOF) مورد استفاده، کمی متفاوت است: حالت قاعدهٔ EOF تا حدی سادهتر از حالت بررسی محدوده (bounds-checking) با فاصلهگذاری (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 استفاده میکند.
//go:generate re2go $INPUT -o $OUTPUT
package main
import (
"os"
"strings"
)
const BUFSIZE uint = 4096
type Input struct {
file *os.File
yyinput []byte
yycursor uint
yymarker uint
yylimit uint
token uint
eof bool
}
func fill(in *Input) int {
if in.eof { return -1 } // unexpected EOF
// Error: lexeme too long. In real life can reallocate a larger buffer.
if in.token < 1 { return -2 }
// Shift buffer contents (discard everything up to the current token).
copy(in.yyinput[0:], in.yyinput[in.token:in.yylimit])
in.yycursor -= in.token
in.yymarker -= in.token
in.yylimit -= in.token
in.token = 0
// Fill free space at the end of buffer with new data from file.
n, _ := in.file.Read(in.yyinput[in.yylimit:BUFSIZE])
in.yylimit += uint(n)
in.yyinput[in.yylimit] = 0
// If read less than expected, this is the end of input.
in.eof = in.yylimit < BUFSIZE
return 0
}
func lex(yyrecord *Input) int {
count := 0
for {
yyrecord.token = yyrecord.yycursor
/*!re2c
re2c:api = record;
re2c:eof = 0;
re2c:YYCTYPE = byte;
re2c:YYFILL = "fill(yyrecord) == 0";
str = ['] ([^'\\] | [\\][^])* ['];
* { return -1 }
$ { return count }
str { count += 1; continue }
[ ]+ { continue }
*/
}
}
func main() () {
fname := "input"
content := "'qu\000tes' 'are' 'fine: \\'' ";
// Prepare input file: a few times the size of the buffer, containing
// strings with zeroes and escaped quotes.
f, _ := os.Create(fname)
f.WriteString(strings.Repeat(content, int(BUFSIZE)))
f.Seek(0, 0)
count := 3 * int(BUFSIZE) // number of quoted strings written to file
// Prepare lexer state: all offsets are at the end of buffer.
in := &Input{
file: f,
// Sentinel at `yylimit` offset is set to zero, which triggers YYFILL.
yyinput: make([]byte, BUFSIZE+1),
yycursor: BUFSIZE,
yymarker: BUFSIZE,
yylimit: BUFSIZE,
token: BUFSIZE,
eof: false,
}
// Run the lexer.
if lex(in) != count { panic("error"); }
// Cleanup: remove input file.
f.Close();
os.Remove(fname);
}
در حالت پیشفرض (زمانی که از قاعده EOF استفاده نمیشود)، YYFILL یک دستور اولیه شبهتابع است که یک آرگومان میپذیرد و هیچ مقداری برنمیگرداند. فراخوانی YYFILL با شرط (YYLIMIT - YYCURSOR) < n در API اشارهگر C و YYLESSTHAN(n) در API عمومی (generic API) فعال میشود. آرگومان ارسالشده به YYFILL حداقل تعداد نویسههایی است که باید تأمین شوند. اگر در انجام این کار ناموفق باشد، YYFILL نباید کنترل را به lexer بازگرداند (به همین دلیل بهتر است به صورت ماکرویی پیادهسازی شود که در صورت شکست، از تابع فراخواننده خارج شود). در صورت فراخوانی موفقیتآمیز YYFILL، موقعیت حد (limit) باید روی یک موقعیت پس از آخرین موقعیت ورودی در بافر، یا روی انتهای پدینگ YYMAXFILL تنظیم شود (در حالتی که YYFILL با موفقیت حداقل n نویسه را خوانده باشد، اما این مقدار برای پر کردن کل بافر کافی نباشد). تصاویر زیر موقعیتهای نسبی ورودی را در بافر قبل و بعد از فراخوانی YYFILL نشان میدهند (پدینگ YYMAXFILL در تصویر دوم با نمادهای # مشخص شده است).
<-- shift --> <-- need -->
>-A------------B---------C-----D-------E---F--------G->
buffer token marker cursor limit
>-A------------B---------C-----D-------E---F--------G->
buffer, marker cursor limit
token
<-- shift --> <-- need -->
>-A------------B---------C-----D-------E-F (EOF)
buffer token marker cursor limit
>-A------------B---------C-----D-------E-F###############
buffer, marker cursor limit
token <- YYMAXFILL ->
در اینجا نمونهای از برنامهای آورده شده است که فایل ورودی input.txt را در قطعات ۴۰۹۶ بایتی خوانده و از بررسی مرزها (bounds-checking) همراه با پدینگ استفاده میکند.
//go:generate re2go $INPUT -o $OUTPUT
package main
import (
"os"
"strings"
)
/*!max:re2c*/
const BUFSIZE uint = 4096
type Input struct {
file *os.File
yyinput []byte
yycursor uint
yylimit uint
token uint
eof bool
}
func fill(in *Input, need uint) int {
if in.eof { return -1 } // unexpected EOF
// Error: lexeme too long. In real life can reallocate a larger buffer.
if in.token < need { return -2 }
// Shift buffer contents (discard everything up to the current token).
copy(in.yyinput[0:], in.yyinput[in.token:in.yylimit])
in.yycursor -= in.token
in.yylimit -= in.token
in.token = 0
// Fill free space at the end of buffer with new data from file.
n, _ := in.file.Read(in.yyinput[in.yylimit:BUFSIZE])
in.yylimit += uint(n)
// If read less than expected, this is end of input => add zero padding
// so that the lexer can access characters at the end of buffer.
if in.yylimit < BUFSIZE {
in.eof = true
for i := uint(0); i < YYMAXFILL; i += 1 { in.yyinput[in.yylimit+i] = 0 }
in.yylimit += YYMAXFILL
}
return 0
}
func lex(yyrecord *Input) int {
count := 0
for {
yyrecord.token = yyrecord.yycursor
/*!re2c
re2c:api = record;
re2c:YYCTYPE = byte;
re2c:YYFILL = "if r := fill(yyrecord, @@); r != 0 { return r }";
str = ['] ([^'\\] | [\\][^])* ['];
[\x00] {
// Check that it is the sentinel, not some unexpected null.
if yyrecord.token == yyrecord.yylimit - YYMAXFILL { return count } else { return -1 }
}
str { count += 1; continue }
[ ]+ { continue }
* { return -1 }
*/
}
}
func main() () {
fname := "input"
content := "'qu\000tes' 'are' 'fine: \\'' ";
// Prepare input file: a few times the size of the buffer, containing
// strings with zeroes and escaped quotes.
f, _ := os.Create(fname)
f.WriteString(strings.Repeat(content, int(BUFSIZE)))
f.Seek(0, 0)
count := 3 * int(BUFSIZE) // number of quoted strings written to file
// Prepare lexer state: all offsets are at the end of buffer.
// This immediately triggers YYFILL, as the YYLESSTHAN condition is true.
in := &Input{
file: f,
yyinput: make([]byte, BUFSIZE+YYMAXFILL),
yycursor: BUFSIZE,
yylimit: BUFSIZE,
token: BUFSIZE,
eof: false,
}
// Run the lexer.
if lex(in) != count { panic("error"); }
// Cleanup: remove input file.
f.Close();
os.Remove(fname);
}
بلوکهای چندگانه (Multiple blocks)
گاهی اوقات لازم است چندین تحلیلگر لغوی (lexer) مرتبط با یکدیگر داشته باشیم (برای مثال، زمانی که یک ماشین حالت سطحبالا وجود دارد که بین حالتهای تحلیلگر لغوی جابهجا میشود). این ساختار را میتوان با استفاده از چندین بلوک متصل re2go پیادهسازی کرد. گزینه دیگر، استفاده از start conditions است.
پیادهسازی ارتباط بین بلوکها به زبان مقصد بستگی دارد. در زبانهایی که دستور goto دارند (مانند C/C++ و Go)، میتوان تمام بلوکها را در یک تابع قرار داد، بهگونهای که پیش از هر کدام یک برچسب بیاید. انتقال از یک بلوک به بلوک دیگر، یک goto ساده است. در زبانهایی که فاقد goto هستند (مانند Rust)، استفاده از یک حلقه همراه با switch روی متغیر حالت ضروری است؛ مشابه حلقه/switch مربوط به yystate که توسط re2go تولید میشود، یا در غیر این صورت، قرار دادن هر بلوک درون یک تابع و استفاده از فراخوانی توابع.
مثال زیر از چندین بلوک برای تجزیه اعداد دودویی (binary)، هشتهشتی (octal)، دهدهی (decimal) و شانزدهشانزدهی (hexadecimal) استفاده میکند. هر مبنا بلوک اختصاصی خود را دارد. بلوک اولیه مبنا را تشخیص داده و پردازش را به بلوکهای دیگر هدایت میکند. پیکربندیهای مشترک در یک بلوک جداگانه در ابتدای برنامه تعریف شدهاند؛ این پیکربندیها توسط سایر بلوکها به ارث برده میشوند.
//go:generate re2go $INPUT -o $OUTPUT -i --api simple
package main
import "errors"
const u32Limit uint64 = 1<<32
var (
eSyntax = errors.New("syntax error")
eOverflow = errors.New("overflow error")
)
func parse_u32(yyinput string) (uint32, error) {
var yycursor, yymarker int
result := uint64(0)
add := func(base uint64, offset byte) {
result = result * base + uint64(yyinput[yycursor-1] - offset)
if result >= u32Limit {
result = u32Limit
}
}
/*!re2c
re2c:yyfill:enable = 0;
re2c:YYCTYPE = byte;
end = "\x00";
'0b' / [01] { goto bin }
"0" { goto oct }
"" / [1-9] { goto dec }
'0x' / [0-9a-fA-F] { goto hex }
* { goto err }
*/
bin:
/*!re2c
end { goto end }
[01] { add(2, '0'); goto bin }
* { goto err }
*/
oct:
/*!re2c
end { goto end }
[0-7] { add(8, '0'); goto oct }
* { goto err }
*/
dec:
/*!re2c
end { goto end }
[0-9] { add(10, '0'); goto dec }
* { goto err }
*/
hex:
/*!re2c
end { goto end }
[0-9] { add(16, '0'); goto hex }
[a-f] { add(16, 'a'-10); goto hex }
[A-F] { add(16, 'A'-10); goto hex }
* { goto err }
*/
end:
if result < u32Limit {
return uint32(result), nil
} else {
return 0, eOverflow
}
err:
return 0, eSyntax
}
func main() {
test := func(num uint32, str string, err error) {
if n, e := parse_u32(str); !(n == num && e == err) {
panic("error")
}
}
test(1234567890, "1234567890\000", nil)
test(13, "0b1101\000", nil)
test(0x7fe, "0x007Fe\000", nil)
test(0644, "0644\000", nil)
test(0, "9999999999\000", eOverflow)
test(0, "123??\000", eSyntax)
}
شرایط شروع با گزینهٔ --start-conditions فعال میشوند. این سازوکار راهکاری برای کدگذاری چندین خودکارهٔ (automata) مرتبط با یکدیگر درون یک بلوک re2go ارائه میدهد.
هر شرط متناظر با یک خودکارهٔ واحد است و دارای یک نام منحصربهفردِ تعیینشده توسط کاربر و یک شمارهٔ داخلی یکتا تعریفشده توسط re2go میباشد. این شمارهها برای جابهجایی میان شرایط استفاده میشوند: کد تولیدشده از توابع اولیهٔ 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 میپرد.
کدی که re2go برای شرایط تولید میکند بستگی به این دارد که re2go از رویکرد goto/label یا رویکرد loop/switch برای کدگذاری خودکارهها استفاده کند.
در زبانهایی که دارای دستور goto هستند (مانند C/C++ و Go)، شرایط بهطور طبیعی بهصورت بلوکهایی از کد با پیشوند برچسبهایی به شکل yyc_<cond> پیادهسازی میشوند، که در آن cond نام یک شرط است (پیشوند برچسب را میتوان با re2c:cond:prefix تغییر داد). گذارها میان شرایط با استفاده از goto و برچسبهای شرایط پیادهسازی میشوند. پیش از همهٔ شرایط، re2go یک switch اولیه روی YYGETSTATE تولید میکند که به وضعیت شروعِ شرطِ کنونی میپرد. قواعد میانبُر :=> این switch اولیه را دور میزنند و مستقیماً به شرط مشخصشده میپرند (میتوان از re2c:cond:goto برای تغییر رفتار پیشفرض استفاده کرد). قواعد دارای کنشهای معنایی بهطور خودکار به شرط بعدی نمیپرند؛ این کار باید توسط کد کنشِ تعریفشده توسط کاربر انجام گیرد.
در زبانهایی که فاقد goto هستند (مانند Rust)، re2go از متغیر yystate برای ذخیرهٔ شمارهٔ شرایط استفادهٔ مجدد میکند. هر شرط یک شناسهٔ عددی برابر با شمارهٔ وضعیت آغازین خود دریافت میکند، و یک switch میان شرایط، هیچ تفاوتی با یک switch میان وضعیتهای DFA درون یک شرط منفرد ندارد. نیازی به یک switch اولیهٔ مجزا برای شرایط نیست. (از آنجا که همین رویکرد برای پیادهسازی وضعیتهای ذخیرهپذیر استفاده میشود، در صورتی که هم از وضعیتهای ذخیرهپذیر و هم از شرایط استفاده شود، YYGETCOND/YYSETCOND زائد خواهند بود).
برنامهٔ زیر از شرایط شروع برای تجزیهٔ اعداد دودویی، هشتهشتی، دهدهی و شانزدهشانزدهی استفاده میکند. یک بلوک واحد وجود دارد که در آن هر مبنا شرط مخصوص به خود را دارد، و شرط آغازین به همهٔ آنها متصل است. متغیر تعریفشده توسط کاربر cond شمارهٔ شرط کنونی را ذخیره میکند؛ این متغیر با شمارهٔ شرط اولیهای که با بلوک conditions تولید شده است مقداردهی اولیه میشود.
//go:generate re2go -c $INPUT -o $OUTPUT -i --api simple
package main
import "errors"
var (
eSyntax = errors.New("syntax error")
eOverflow = errors.New("overflow error")
)
/*!conditions:re2c*/
const u32Limit uint64 = 1<<32
func parse_u32(yyinput string) (uint32, error) {
var yycursor, yymarker int
result := uint64(0)
yycond := yycinit
add := func(base uint64, offset byte) {
result = result * base + uint64(yyinput[yycursor-1] - offset)
if result >= u32Limit {
result = u32Limit
}
}
/*!re2c
re2c:yyfill:enable = 0;
re2c:YYCTYPE = byte;
re2c:YYGETCOND = "cond";
re2c:YYSETCOND = "cond = @@";
<*> * { return 0, eSyntax }
<init> '0b' / [01] :=> bin
<init> "0" :=> oct
<init> "" / [1-9] :=> dec
<init> '0x' / [0-9a-fA-F] :=> hex
<bin, oct, dec, hex> "\x00" {
if result < u32Limit {
return uint32(result), nil
} else {
return 0, eOverflow
}
}
<bin> [01] { add(2, '0'); goto yyc_bin }
<oct> [0-7] { add(8, '0'); goto yyc_oct }
<dec> [0-9] { add(10, '0'); goto yyc_dec }
<hex> [0-9] { add(16, '0'); goto yyc_hex }
<hex> [a-f] { add(16, 'a'-10); goto yyc_hex }
<hex> [A-F] { add(16, 'A'-10); goto yyc_hex }
*/
}
func main() {
test := func(num uint32, str string, err error) {
if n, e := parse_u32(str); !(n == num && e == err) {
panic("error")
}
}
test(1234567890, "1234567890\000", nil)
test(13, "0b1101\000", nil)
test(0x7fe, "0x007Fe\000", nil)
test(0644, "0644\000", nil)
test(0, "9999999999\000", eOverflow)
test(0, "123??\000", eSyntax)
}
با گزینهٔ --storable-state، ابزار re2go یک تحلیلگر لغوی تولید میکند که میتواند وضعیت فعلی خود را ذخیره کرده، به فراخواننده بازگردد، و بعداً عملیات را دقیقاً از همانجایی که رها کرده بود از سر بگیرد. حالت پیشفرض عملکرد در re2go مدل «کشش» ("pull") است که در آن تحلیلگر لغوی هر زمان که نیاز داشته باشد، ورودی بیشتری را «میکشد» ("pulls"). این امر ممکن است در مواردی که ورودی بهصورت تکهتکه در دسترس قرار میگیرد غیرقابل قبول باشد (برای مثال، اگر تحلیلگر لغوی توسط تجزیهکننده (parser) فراخوانی شود، یا اگر برنامهٔ تحلیلگر لغوی از طریق یک پروتکل سوکت با برنامهٔ دیگری ارتباط برقرار کند که باید پیش از ارسال پیام بعدی، منتظر پاسخی از تحلیلگر لغوی بماند). قابلیت وضعیت قابل ذخیرهسازی دقیقاً برای چنین مواردی در نظر گرفته شده است: این ویژگی اجازه میدهد تحلیلگرهای لغوی تولید شوند که در یک مدل «رانش» ("push") کار میکنند. هنگامی که تحلیلگر لغوی به ورودی بیشتری نیاز دارد، وضعیت خود را ذخیره کرده و به فراخواننده بازمیگردد. بعداً، زمانی که ورودی بیشتری در دسترس قرار گرفت، فراخواننده تحلیلگر لغوی را دقیقاً از جایی که متوقف شده بود از سر میگیرد. در مقایسه با مدل «کشش» ("pull")، چند تغییر ضروری است:
- تعریف پیشدستورهای پایهای YYSETSTATE() و YYGETSTATE(state).
- تعریف متغیرهای yych، yyaccept (در صورت استفاده) و state بهعنوان بخشی از وضعیت ماندگار تحلیلگر لغوی. متغیر state باید با مقدار -1 مقداردهی اولیه شود.
- دستور YYFILL به جای تلاش برای تأمین ورودی بیشتر، باید به برنامهٔ بیرونی بازگردد. کد بازگشتی باید نشان دهد که تحلیلگر لغوی به ورودی بیشتری نیاز دارد.
- برنامهٔ بیرونی باید موقعیتهایی را که تحلیلگر لغوی به ورودی بیشتری نیاز دارد تشخیص داده و واکنش مناسب نشان دهد.
- استفادهٔ اختیاری از بلوک getstate برای تولید ساختار سوییچ YYGETSTATE بهصورت جدا از تحلیلگر لغوی اصلی. این ویژگی تنها برای زبانهایی کار میکند که دارای goto هستند (نه در حالت --loop-switch).
- استفاده از re2c:eof و روش sentinel with bounds checks برای مدیریت انتهای ورودی. روش مبتنی بر لایهگذاری (padding) ممکن است کار نکند زیرا مشخص نیست چه زمانی باید لایهگذاری را اضافه کرد: انتهای فعلی ورودی ممکن است انتهای نهایی ورودی نباشد، و اضافه کردن زودهنگام لایهگذاری ممکن است یک لکسم طمعکارانه (greedy lexeme) را که بخشی از آن خوانده شده قطع کند. علاوه بر این، به دلیل منطق سطحبالای برنامه، دریافت ورودی بیشتر ممکن است به پردازش لکسم در انتهای بافر وابسته باشد (که از قبل به دلیل شرط پایان ورودی مسدود شده است).
در اینجا مثالی از یک تحلیلگر لغوی با مدل «رانش» ("push") آورده شده است که خواندن بستهها از یک سوکت را شبیهسازی میکند. تحلیلگر لغوی تا زمانی که با انتهای ورودی مواجه شود حلقه میزند و به تابع فراخواننده بازمیگردد. تابع فراخواننده با «ارسال» ("sending") بستهٔ بعدی، ورودی بیشتری فراهم کرده و تحلیل لغوی را از سر میگیرد. این فرآیند زمانی که تمام بستهها ارسال شده باشند، یا زمانی که خطایی رخ دهد، متوقف میشود.
//go:generate re2go -f $INPUT -o $OUTPUT
package main
import (
"fmt"
"os"
)
// Use a small buffer to cover the case when a lexeme doesn't fit.
// In real world use a larger buffer.
const BUFSIZE int = 10
type State struct {
file *os.File
yyinput []byte
yycursor int
yymarker int
yylimit int
token int
yystate int
}
const (
lexEnd = iota
lexReady
lexWaitingForInput
lexPacketBroken
lexPacketTooBig
)
func fill(st *State) int {
shift := st.token
used := st.yylimit - st.token
free := BUFSIZE - used
// Error: no space. In real life can reallocate a larger buffer.
if free < 1 { return lexPacketTooBig }
// Shift buffer contents (discard already processed data).
copy(st.yyinput[0:], st.yyinput[shift:shift+used])
st.yycursor -= shift
st.yymarker -= shift
st.yylimit -= shift
st.token -= shift
// Fill free space at the end of buffer with new data.
n, _ := st.file.Read(st.yyinput[st.yylimit:BUFSIZE])
st.yylimit += n
st.yyinput[st.yylimit] = 0 // append sentinel symbol
return lexReady
}
func lex(yyrecord *State, recv *int) int {
var yych byte
/*!getstate:re2c*/
loop:
yyrecord.token = yyrecord.yycursor
/*!re2c
re2c:api = record;
re2c:eof = 0;
re2c:YYFILL = "return lexWaitingForInput";
packet = [a-z]+[;];
* { return lexPacketBroken }
$ { return lexEnd }
packet { *recv = *recv + 1; goto loop }
*/
}
func test(expect int, packets []string) {
// Create a pipe (open the same file for reading and writing).
fname := "pipe"
fw, _ := os.Create(fname)
fr, _ := os.Open(fname)
// Initialize lexer state: `state` value is -1, all offsets are at the end
// of buffer.
st := &State{
file: fr,
// Sentinel at `yylimit` offset is set to zero, which triggers YYFILL.
yyinput: make([]byte, BUFSIZE+1),
yycursor: BUFSIZE,
yymarker: BUFSIZE,
yylimit: BUFSIZE,
token: BUFSIZE,
yystate: -1,
}
// Main loop. The buffer contains incomplete data which appears packet by
// packet. When the lexer needs more input it saves its internal state and
// returns to the caller which should provide more input and resume lexing.
var status int
send := 0
recv := 0
for {
status = lex(st, &recv)
if status == lexEnd {
break
} else if status == lexWaitingForInput {
if send < len(packets) {
fw.WriteString(packets[send])
send += 1
}
status = fill(st)
if status != lexReady {
break
}
} else if status == lexPacketBroken {
break
}
}
// Check results.
if status != expect || (status == lexEnd && recv != send) {
panic(fmt.Sprintf("got %d, want %d", status, expect))
}
// Cleanup: remove input file.
fr.Close()
fw.Close()
os.Remove(fname)
}
func main() {
test(lexEnd, []string{})
test(lexEnd, []string{"zero;", "one;", "two;", "three;", "four;"})
test(lexPacketBroken, []string{"??;"})
test(lexPacketTooBig, []string{"looooooooooooong;"})
}
بلوکهای قابل استفاده مجدد در قالب /*!rules:re2c[:<name>] ... */ یا %{rules[:<name>] ... %} میتوانند به هر تعداد بار دوباره استفاده شده و با دیگر بلوکهای re2go ترکیب شوند. مقدار <name> اختیاری است. یک بلوک rules میتواند در یک بلوک یا رهنمود use به کار رود. کد مربوط به یک بلوک rules در هر نقطهای از استفاده تولید میشود.
بلوکهای use با /*!use:re2c[:<name>] ... */ یا %{use[:<name>] ... %} تعریف میشوند. شناسهٔ <name> اختیاری است: در صورتی که مشخص نشود، بلوک rules مرتبط، جدیدترین بلوک موجود خواهد بود (چه نامدار باشد و چه بینام). یک بلوک use میتواند تعاریف نامدار، پیکربندیها و قواعد مختص به خود را اضافه کند. یک کاربرد مهم برای بلوکهای use، تحلیلگر لغوی (lexer) با پشتیبانی از چندین کدگذاری ورودی است: همان بلوک rules چندین بار با پیکربندیهای خاص هر کدگذاری بازاستفاده میشود (مثال زیر را ببینید).
رهنمود use درونبلوکی !use:<name>; میتواند از داخل یک بلوک re2go استفاده شود. این رهنمود بلوک ارجاعیافتهٔ <name> را با بلوک فعلی ادغام میکند. اگر برخی از قواعد و پیکربندیهای ادغامشده با موارد تعریفشدهٔ پیشین همپوشانی داشته باشند، تداخلها به شیوهٔ معمول حلوفصل میشوند: نخستین قاعده اولویت دارد، و آخرین پیکربندی بر موارد پیش از خود جایگزین میشود. یک استثنا، قواعد ویژهٔ *، $ و (در حالت شرط/condition) <!> هستند که برای آنها تعریف محلیِ بلوک بر هر تعریف به ارثرسیدهای ارجحیت دارد. رهنمود use به کاربر اجازه میدهد بلوکهای گوناگون re2go را با یکدیگر در یک بلوک ترکیب کند (مثال زیر را ببینید).
بلوکهای نامدار و رهنمود use درونبلوکی در نسخهٔ 2.2 از re2go افزوده شدند. از آن نسخه به بعد، بلوکهای قابل استفاده مجدد بهطور پیشفرض مجاز هستند (نیازی به گزینهٔ خاصی نیست). پیش از نسخهٔ 2.2، حالت استفادهٔ مجدد با گزینهٔ -r --reusable فعال میشد. پیش از نسخهٔ 1.2، بلوکهای قابل استفاده مجدد نمیتوانستند با بلوکهای معمولی ترکیب شوند.
مثالی از رهنمود !use
//go:generate re2go $INPUT -o $OUTPUT --api simple
package main
// This example shows how to combine reusable re2c blocks: two blocks
// ('colors' and 'fish') are merged into one. The 'salmon' rule occurs
// in both blocks; the 'fish' block takes priority because it is used
// earlier. Default rule * occurs in all three blocks; the local (not
// inherited) definition takes priority.
const (
Color = iota
Fish
Dunno
)
/*!rules:re2c:colors
* { panic("eh!") }
"red" | "salmon" | "magenta" { return Color }
*/
/*!rules:re2c:fish
* { panic("oh!") }
"haddock" | "salmon" | "eel" { return Fish }
*/
func lex(yyinput string) int {
var yycursor, yymarker int
/*!re2c
re2c:yyfill:enable = 0;
re2c:YYCTYPE = byte;
!use:fish;
!use:colors;
* { return Dunno } // overrides inherited '*' rules
*/
}
func main() {
assert_eq := func(x, y int) { if x != y { panic("error") } }
assert_eq(lex("salmon"), Fish);
assert_eq(lex("what?"), Dunno);
}
//go:generate re2go $INPUT -o $OUTPUT --input-encoding utf8 --api simple
package main
// This example supports multiple input encodings: UTF-8 and UTF-32.
// Both lexers are generated from the same rules block, and the use
// blocks add only encoding-specific configurations.
/*!rules:re2c
re2c:yyfill:enable = 0;
"∀x ∃y" { return 0; }
* { return 1; }
*/
func lexUTF8(yyinput []uint8) int {
var yycursor, yymarker int
/*!use:re2c
re2c:encoding:utf8 = 1;
re2c:YYCTYPE = uint8;
*/
}
func lexUTF32(yyinput []uint32) int {
var yycursor, yymarker int
/*!use:re2c
re2c:encoding:utf32 = 1;
re2c:YYCTYPE = uint32;
*/
}
func main() {
assert_eq := func(x, y int) { if x != y { panic("error") } }
assert_eq(lexUTF8([]uint8{0xe2, 0x88, 0x80, 0x78, 0x20, 0xe2, 0x88, 0x83, 0x79}), 0)
assert_eq(lexUTF32([]uint32{0x2200, 0x78, 0x20, 0x2203, 0x79}), 0)
}
استخراج زیرتطبیق (Submatch extraction)
ابزار re2go دو گزینه برای استخراج زیرتطبیق دارد.
- Tags
- گزینهٔ نخست استفاده از tagهای مستقل به فرم @stag یا #mtag است، که در آن stag و mtag نامهای اختیاری تعریفشده توسط کاربر هستند. برچسبها با گزینهٔ -T --tags یا پیکربندی re2c:tags = 1 فعال میشوند. از نظر معنایی برچسبها نشانگرهای موقعیت هستند: آنها میتوانند در هر کجای یک عبارت منظم درج شوند و به موقعیت متناظر (یا موقعیتهای چندگانه) در رشتهٔ ورودی متصل گردند. S-tagها به آخرین موقعیت تطبیقیافته متصل میشوند، و m-tagها به فهرستی از موقعیتها متصل میگردند (آنها ممکن است در زیرعبارتهای تکرارشونده استفاده شوند، جایی که یک موقعیت واحد در عبارت منظم متناظر با چندین موقعیت در رشتهٔ ورودی است). تمام برچسبها باید توسط کاربر تعریف شوند، چه به صورت دستی یا به کمک بلوکهای svars و mvars. اگر بیش از یک راه برای تطبیق برچسبها در برابر ورودی وجود داشته باشد، ابهام با استفاده از راهبرد ابهامزدایی چپترینِ حریصانه (leftmost greedy) حل میشود.
- Captures
- گزینهٔ دوم استفاده از گروههای گیرانداز (capturing groups) است. آنها با گزینهٔ --captures یا پیکربندی re2c:captures = 1 فعال میشوند. دو نوع برای سیاستهای ابهامزدایی متفاوت وجود دارد: --leftmost-captures (پیشفرض) برای سیاست چپترینِ حریصانه، و --posix-captures برای سیاست طولانیترین تطبیق POSIX. در این حالت تمام زیرعبارتهای درون پرانتز به عنوان گروههای گیرانداز در نظر گرفته میشوند، و میتوان از یک علامت تعجب برای مشخص کردن گروههای غیرگیرانداز استفاده کرد: (! ... ). با گزینهٔ --invert-captures یا پیکربندی re2c:invert-captures = 1 معنای علامت تعجب وارونه میشود. تعداد گروهها برای قاعدهٔ منطبقشده در یک متغیر به نام yynmatch ذخیره میشود (کل عبارت منظم گروه شمارهٔ صفر است)، و نتایج زیرتطبیق در آرایهٔ yypmatch ذخیره میگردند. هر دو متغیر yynmatch و yypmatch باید توسط کاربر تعریف شوند، و اندازهٔ yypmatch باید حداقل [yynmatch * 2] باشد. از بلوک maxnmatch برای تعریف YYMAXNMATCH استفاده کنید؛ ثابتی که برابر با بیشترین مقدار yynmatch در میان تمام قواعد است.
- Captvars
- راه دیگر برای استفاده از گروههای گیرانداز، گزینهٔ --captvars یا پیکربندی re2c:captvars = 1 است. تنها تفاوت با --captures در روشی است که کد تولیدشده نتایج زیرتطبیق را ذخیره میکند: به جای yynmatch و yypmatch، ابزار re2go متغیرهای yytl<k> و yytr<k> را برای k-امین گروه گیرانداز تولید میکند (کاربر باید اینها را با استفاده از یک بلوک svars اعلان کند). گیراندازی با متغیرها از دو سیاست ابهامزدایی پشتیبانی میکند: --leftmost-captvars یا re2c:leftmost-captvars = 1 برای سیاست چپترینِ حریصانه (حالت پیشفرض) و --posix-captvars یا re2c:posix-captvars برای سیاست طولانیترین تطبیق POSIX.
در پشت صحنه، تمام این گزینهها به برچسبها و اتوماتون متناهی قطعی برچسبدار با پیشنگری (Tagged Deterministic Finite Automata with Lookahead) یا TDFA ترجمه میشوند. ایدهٔ اصلی TDFA به حداقل رساندن بار اضافی (overhead) در استخراج زیرتطبیق است. در حالت حدی، اگر هیچ برچسب یا گیراندازیای در یک عبارت منظم وجود نداشته باشد، TDFA تنها یک DFA معمولی است. اگر تعداد برچسبها معتدل باشد، بار اضافی به سختی قابل توجه خواهد بود. ماشین TDFA تولیدشده از تعدادی متغیر برچسب (tag variables) استفاده میکند که مستقیماً به برچسبها نگاشت نمیشوند: یک متغیر منفرد ممکن است برای برچسبهای متفاوتی استفاده شود، و یک برچسب ممکن است برای نگهداری تمام مقادیر احتمالی خود به چندین متغیر نیاز داشته باشد. سرانجام ابهام حل میشود، و تنها یک متغیر نهایی به ازای هر برچسب باقی میماند. متغیرهای برچسب باید با استفاده از بلوکهای stags یا mtags تعریف شوند. اگر وضعیت لکسر ذخیره میشود، متغیرهای برچسب باید بخشی از آن باشند. آنها همچنین باید توسط YYFILL بهروزرسانی شوند.
S-tagها از عملیات زیر پشتیبانی میکنند:
- ذخیرهٔ موقعیت ورودی در یک s-tag: عبارت t = YYCURSOR با API اشارهگر C یا یک عملیات تعریفشده توسط کاربر YYSTAGP(t) با API جنریک
- ذخیرهٔ مقدار پیشفرض در یک s-tag: عبارت t = NULL با API اشارهگر C یا یک عملیات تعریفشده توسط کاربر YYSTAGN(t) با API جنریک
- کپی کردن یک s-tag به دیگری: t1 = t2
M-tagها از عملیات زیر پشتیبانی میکنند:
- افزودن موقعیت ورودی به یک m-tag: یک عملیات تعریفشده توسط کاربر YYMTAGP(t) با هر دو API پیشفرض و جنریک
- افزودن مقدار پیشفرض به یک m-tag: یک عملیات تعریفشده توسط کاربر YYMTAGN(t) با هر دو API پیشفرض و جنریک
- کپی کردن یک m-tag به دیگری: t1 = t2
S-tagها میتوانند به عنوان مقادیر اسکالر (اشارهگرها یا آفستها) پیادهسازی شوند. M-tagها به نمایش پیچیدهتری نیاز دارند، چرا که باید دنبالهای از مقادیر برچسب را ذخیره کنند. سادهانگارانهترین و ناکارآمدترین نمایش از یک m-tag، یک فهرست (آرایه، بردار) از مقادیر برچسب است؛ یک نمایش کارآمدتر، ذخیرهٔ تمام m-tagها در یک درخت پیشوندی (prefix-tree) است که به صورت آرایهای از گرههای (v, p) نشان داده میشود، جایی که v مقدار برچسب و p اشارهگری به گره والد است.
در اینجا مثالی ساده از استفاده از s-tagها برای تجزیهٔ نگارشهای معنایی (semantic versions) متشکل از سه مؤلفهٔ عددی آمده است: major، minor، patch (مورد آخر اختیاری است). برای یک مثال پیچیدهتر که از YYFILL استفاده میکند به بخشهای زیر مراجعه کنید.
//go:generate re2go $INPUT -o $OUTPUT --api simple
package main
import "reflect"
type SemVer struct { major, minor, patch int }
func s2n(s string) int { // convert pre-parsed string to a number
n := 0
for _, c := range s { n = n*10 + int(c-'0') }
return n
}
func parse(yyinput string) *SemVer {
var yycursor, yymarker int
// Final tag variables available in semantic action.
/*!svars:re2c format = 'var @@ int;'; */
// Intermediate tag variables used by the lexer (must be autogenerated).
/*!stags:re2c format = 'var @@ int;'; */
/*!re2c
re2c:yyfill:enable = 0;
re2c:YYCTYPE = byte;
re2c:tags = 1;
num = [0-9]+;
@t1 num @t2 "." @t3 num @t4 ("." @t5 num)? [\x00] {
major := s2n(yyinput[t1:t2])
minor := s2n(yyinput[t3:t4])
patch := 0
if t5 != -1 { patch = s2n(yyinput[t5:yycursor-1]) }
return &SemVer{major, minor, patch}
}
* { return nil }
*/
}
func main() {
assert_eq := func(x, y *SemVer) {
if !reflect.DeepEqual(x, y) { panic("error") }
}
assert_eq(parse("23.34\000"), &SemVer{23, 34, 0})
assert_eq(parse("1.2.9999\000"), &SemVer{1, 2, 9999})
assert_eq(parse("1.a\000"), nil)
}
//go:generate re2go $INPUT -o $OUTPUT --tags
package main
import (
"os"
"reflect"
"strings"
)
const BUFSIZE int = 4095
type Input struct {
file *os.File
yyinput []byte
yycursor int
yymarker int
yylimit int
token int
// Intermediate tag variables must be part of the lexer state passed to YYFILL.
// They don't correspond to tags and should be autogenerated by re2c.
/*!stags:re2c format = "\t@@ int\n"; */
eof bool
}
type SemVer struct { major, minor, patch int }
func s2n(s []byte) int { // convert pre-parsed string to a number
n := 0
for _, c := range s { n = n*10 + int(c-'0') }
return n
}
func fill(in *Input) int {
if in.eof { return -1 } // unexpected EOF
// Error: lexeme too long. In real life can reallocate a larger buffer.
if in.token < 1 { return -2 }
// Shift buffer contents (discard everything up to the current token).
copy(in.yyinput[0:], in.yyinput[in.token:in.yylimit])
in.yycursor -= in.token
in.yymarker -= in.token
in.yylimit -= in.token
// Tag variables need to be shifted like other input positions. The check
// for -1 is only needed if some tags are nested inside of alternative or
// repetition, so that they can have -1 value.
/*!stags:re2c format = "\tif in.@@ != -1 { in.@@ -= in.token }\n"; */
in.token = 0
// Fill free space at the end of buffer with new data from file.
n, _ := in.file.Read(in.yyinput[in.yylimit:BUFSIZE])
in.yylimit += n
in.yyinput[in.yylimit] = 0
// If read less than expected, this is the end of input.
in.eof = in.yylimit < BUFSIZE
return 0
}
func parse(in *Input) []SemVer {
// Final tag variables available in semantic action.
/*!svars:re2c format = "var @@ int;"; */
vers := make([]SemVer, 0)
for {
in.token = in.yycursor
/*!re2c
re2c:api = record;
re2c:eof = 0;
re2c:yyrecord = in;
re2c:YYCTYPE = byte;
re2c:YYFILL = "fill(in) == 0";
num = [0-9]+;
num @t1 "." @t2 num @t3 ("." @t4 num)? [\n] {
major := s2n(in.yyinput[in.token:t1])
minor := s2n(in.yyinput[t2:t3])
patch := 0
if t4 != -1 { patch = s2n(in.yyinput[t4:in.yycursor-1]) }
vers = append(vers, SemVer{major, minor, patch})
continue
}
$ { return vers }
* { return nil }
*/
}
}
func main() () {
fname := "input"
content := "1.22.333\n";
expect := make([]SemVer, 0, BUFSIZE)
for i := 0; i < BUFSIZE; i += 1 { expect = append(expect, SemVer{1, 22, 333}) }
// Prepare input file (make sure it exceeds buffer size).
f, _ := os.Create(fname)
f.WriteString(strings.Repeat(content, BUFSIZE))
f.Seek(0, 0)
// Initialize lexer state: all offsets are at the end of buffer.
in := &Input{
file: f,
// Sentinel at `yylimit` offset is set to zero, which triggers YYFILL.
yyinput: make([]byte, BUFSIZE+1),
yycursor: BUFSIZE,
yymarker: BUFSIZE,
yylimit: BUFSIZE,
token: BUFSIZE,
eof: false,
}
// Run the lexer and check results.
if !reflect.DeepEqual(parse(in), expect) { panic("error"); }
// Cleanup: remove input file.
f.Close();
os.Remove(fname);
}
در اینجا نمونهای از استفاده از گروههای ضبط (capturing groups) برای تجزیه نسخهبندیهای معنایی آورده شده است.
//go:generate re2go $INPUT -o $OUTPUT --api simple
package main
import "reflect"
type SemVer struct { major, minor, patch int }
func s2n(s string) int { // convert pre-parsed string to a number
n := 0
for _, c := range s { n = n*10 + int(c-'0') }
return n
}
func parse(yyinput string) *SemVer {
var yycursor, yymarker int
// Final tag variables used in semantic action.
/*!svars:re2c format = 'var @@ int;'; */
// Intermediate tag variables used by the lexer (must be autogenerated).
/*!stags:re2c format = 'var @@ int;'; */
/*!re2c
re2c:yyfill:enable = 0;
re2c:YYCTYPE = byte;
re2c:captvars = 1;
num = [0-9]+;
(num) "." (num) ("." num)? [\x00] {
_ = yytl0; _ = yytr0; // some variables are unused
major := s2n(yyinput[yytl1:yytr1])
minor := s2n(yyinput[yytl2:yytr2])
patch := 0
if yytl3 != -1 { patch = s2n(yyinput[yytl3+1:yytr3]) }
return &SemVer{major, minor, patch}
}
* { return nil }
*/
}
func main() {
assert_eq := func(x, y *SemVer) {
if !reflect.DeepEqual(x, y) { panic("error") }
}
assert_eq(parse("23.34\000"), &SemVer{23, 34, 0})
assert_eq(parse("1.2.9999\000"), &SemVer{1, 2, 9999})
assert_eq(parse("1.a\000"), nil)
}
//go:generate re2go $INPUT -o $OUTPUT --api simple
package main
import "reflect"
const (
mtagRoot int = -1
tagNone int = -1
)
// An m-tag tree is a way to store histories with an O(1) copy operation.
// Histories naturally form a tree, as they have common start and fork at some
// point. The tree is stored as an array of pairs (tag value, link to parent).
// An m-tag is represented with a single link in the tree (array index).
type mtagElem struct {
elem int
pred int
}
type mtagTrie = []mtagElem
type Ver = []int // unbounded number of version components
func s2n(s string) int { // convert pre-parsed string to a number
n := 0
for _, c := range s { n = n*10 + int(c-'0') }
return n
}
// Append a single value to an m-tag history.
func add_mtag(trie *mtagTrie, mtag int, value int) int {
*trie = append(*trie, mtagElem{value, mtag})
return len(*trie) - 1
}
// Recursively unwind tag histories and collect version components.
func unwind(trie mtagTrie, x int, y int, str string) Ver {
// Reached the root of the m-tag tree, stop recursion.
if x == mtagRoot && y == mtagRoot {
return []int{}
}
// Unwind history further.
ver := unwind(trie, trie[x].pred, trie[y].pred, str)
// Get tag values. Tag histories must have equal length.
if x == mtagRoot || y == mtagRoot {
panic("tag histories have different length")
}
ex := trie[x].elem
ey := trie[y].elem
if ex != tagNone && ey != tagNone {
// Both tags are valid string indices, extract component.
ver = append(ver, s2n(str[ex:ey]))
} else if !(ex == tagNone && ey == tagNone) {
panic("both tags should be tagNone")
}
return ver
}
func parse(yyinput string) []int {
var yycursor, yymarker int
trie := make([]mtagElem, 0)
// Final tag variables available in semantic action.
/*!svars:re2c format = 'var @@ int;'; */
/*!mvars:re2c format = "var @@ int;"; */
// Intermediate tag variables used by the lexer (must be autogenerated).
/*!stags:re2c format = 'var @@ int'; separator = "\n\t"; */
/*!mtags:re2c format = "\t@@ := mtagRoot\n"; */
/*!re2c
re2c:tags = 1;
re2c:yyfill:enable = 0;
re2c:YYCTYPE = byte;
re2c:YYMTAGP = "@@ = add_mtag(&trie, @@, yycursor)";
re2c:YYMTAGN = "@@ = add_mtag(&trie, @@, tagNone)";
num = [0-9]+;
@t1 num @t2 ("." #t3 num #t4)* [\x00] {
ver := make([]int, 0)
ver = append(ver, s2n(yyinput[t1:t2]))
ver = append(ver, unwind(trie, t3, t4, yyinput)...)
return ver
}
* { return nil }
*/
}
func main() {
assert_eq := func(x, y []int) {
if !reflect.DeepEqual(x, y) { panic("error") }
}
assert_eq(parse("1\000"), []int{1})
assert_eq(parse("1.2.3.4.5.6.7\000"), []int{1, 2, 3, 4, 5, 6, 7})
assert_eq(parse("1.\000"), nil)
}
درک تفاوت میان نقطهکدها (code points) و واحدهای کد (code units) ضروری است. نقطهکد یک شناسهٔ عددی برای یک نماد است. واحد کد کوچکترین واحد ذخیرهسازی در متن کدگذاریشده است. یک نقطهکد واحد ممکن است با یک یا چند واحد کد نمایش داده شود. در یک کدگذاری با طول ثابت (fixed-length)، همهٔ نقطهکدها با تعداد یکسانی از واحدهای کد نمایش داده میشوند. در یک کدگذاری با طول متغیر (variable-length)، نقطهکدها ممکن است با تعداد متفاوتی از واحدهای کد نمایش داده شوند. توجه داشته باشید که قاعدهٔ «هر چیزی» "any" [^] با هر نقطهکدی مطابقت مییابد، اما لزوماً با هر واحد کدی مطابقت ندارد (تنها روش برای تطبیق با هر واحد کد بدون توجه به نوع کدگذاری، قاعدهٔ پیشفرض * است). تحلیلگر لغوی تولیدشده با جریانی از واحدهای کد کار میکند: yych یک واحد کد را ذخیره میکند و YYCTYPE نوع واحد کد است. از سوی دیگر، عبارات منظم بر حسب نقطهکدها مشخص میشوند. هنگامی که re2go عبارات منظم را به اتوماتا کامپایل میکند، نقطهکدها را به واحدهای کد ترجمه مینماید. این عموماً یک نگاشت ساده نیست: در کدگذاریهای با طول متغیر، یک بازه از نقطهکد ممکن است به گراف پیچیدهای از واحدهای کد ترجمه شود. کدگذاریهای زیر پشتیبانی میشوند:
- ASCII (بهطور پیشفرض فعال است). این یک کدگذاری با طول ثابت با فضای کد [0-255] و نقطهکدها و واحدهای کد ۱ بایتی است.
- EBCDIC (فعالشده با --ebcdic یا re2c:encoding:ebcdic). این یک کدگذاری با طول ثابت با فضای کد [0-255] و نقطهکدها و واحدهای کد ۱ بایتی است.
- UCS2 (فعالشده با --ucs2 یا re2c:encoding:ucs2). این یک کدگذاری با طول ثابت با فضای کد [0-0xFFFF] و نقطهکدها و واحدهای کد ۲ بایتی است.
- UTF8 (فعالشده با --utf8 یا re2c:encoding:utf8). این یک کدگذاری یونیکد با طول متغیر است. اندازه واحد کد ۱ بایت است. نقطهکدها با ۱ تا ۴ واحد کد نمایش داده میشوند.
- UTF16 (فعالشده با --utf16 یا re2c:encoding:utf16). این یک کدگذاری یونیکد با طول متغیر است. اندازه واحد کد ۲ بایت است. نقطهکدها با ۱ تا ۲ واحد کد نمایش داده میشوند.
- UTF32 (فعالشده با --utf32 یا re2c:encoding:utf32). این یک کدگذاری یونیکد با طول ثابت با فضای کد [0-0x10FFFF] و نقطهکدها و واحدهای کد ۴ بایتی است.
فایلهای ضمیمهٔ include/unicode_blocks.re، include/unicode_categories.re و include/unicode_properties.re تعاریف re2go را برای بلوکها، دستهها و ویژگیهای توصیفشده در استاندارد یونیکد (Unicode) فراهم میکنند.
گزینهٔ --input-encoding کدگذاری فایل مبدا را مشخص میکند، که میتواند برای فعال کردن لیترالهای یونیکد در عبارات منظم استفاده شود. برای نمونه --input-encoding utf8 به re2go اعلام میکند که فایل مبدا در قالب UTF8 است (با --utf8 که کدگذاری متن ورودی را مشخص میکند متفاوت است). گزینهٔ --encoding-policy نحوهٔ مدیریت جانشینهای یونیکد (نقطهکدهای موجود در بازهٔ [0xD800-0xDFFF]) را توسط re2go مشخص میکند.
در زیر یک مثال از تحلیلگر لغوی برای شناسههای یونیکد کدگذاریشده با UTF8 آمده است.
//go:generate re2go $INPUT -o $OUTPUT -8si --api simple
package main
/*!include:re2c "unicode_categories.re" */
func lex(yyinput string) int {
var yycursor, yymarker int
/*!re2c
re2c:yyfill:enable = 0;
re2c:YYCTYPE = byte;
// Simplified "Unicode Identifier and Pattern Syntax"
// (see https://unicode.org/reports/tr31)
id_start = L | Nl | [$_];
id_continue = id_start | Mn | Mc | Nd | Pc | [\u200D\u05F3];
identifier = id_start id_continue*;
identifier { return 0 }
* { return 1 }
*/
}
func main() {
if lex("_Ыдентификатор\000") != 0 {
panic("error")
}
}
re2go به شما امکان میدهد با استفاده از بلوکی به شکل /*!include:re2c FILE */ یا %{include FILE %}، یا یک دستورالعمل درونبلوکیِ !include FILE ;، فایلهای دیگر را ضمیمه کنید که در آن FILE مسیر فایلی است که باید ضمیمه شود. re2go به دنبال فایلهای include در دایرکتوریِ فایلِ فراخواننده و مکانهای include میگردد که میتوان آنها را با گزینهٔ -I مشخص کرد. بلوکها/دستورالعملهای include در re2go همانند #include در C/C++ عمل میکنند: محتویات FILE عیناً در محل بلوک/دستورالعمل کپی میشوند. فایلهای include ممکن است خود دارای includeهای بیشتری باشند. از گزینهٔ --depfile برای ردیابی وابستگیهای ساخت فایل خروجی به فایلهای include استفاده کنید. re2go برخی فایلهای include از پیش تعریفشده را فراهم میکند که در زیردایرکتوری include/ پروژه یافت میشوند. این فایلها حاوی تعاریفی هستند که ممکن است برای پروژههای دیگر مفید باشند (مانند دستهبندیهای Unicode) و چیزی شبیه به یک کتابخانه استاندارد برای re2go تشکیل میدهند. در ادامه مثالی از استفاده از فایلهای include آمده است.
فایل Include شماره ۱ (definitions.go)
const (
ResultOk = iota
ResultFail
)
/*!re2c
number = [1-9][0-9]*;
*/
فایل Include شماره ۲ (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 ResultOk }
فایل ورودی (Input file)
//go:generate re2go $INPUT -o $OUTPUT -i --api simple
package main
/*!include:re2c "definitions.go" */
func lex(yyinput string) int {
var yycursor, yymarker int
/*!re2c
re2c:YYCTYPE = byte;
re2c:yyfill:enable = 0;
* { return ResultFail }
number { return ResultOk }
!include "extra_rules.re.inc";
*/
}
func main() {
assert_eq := func(x, y int) { if x != y { panic("error") } }
assert_eq(lex("123\000"), ResultOk)
assert_eq(lex("123.4567\000"), ResultOk)
}
ابزار re2go به شما امکان میدهد تا با استفاده از گزینهٔ --header یا پیکربندی re2c:header و جفتبلوکهایی به شکل /*!header:re2c:on*/ و /*!header:re2c:off*/، یا %{header:on%} و %{header:off%}، از فایل ورودی .re یک فایل سرآیند تولید کنید. بلوک اول آغاز فایل سرآیند و بلوک دوم پایان آن را مشخص میکند. تمام محتوای بین این بلوکها توسط re2go پردازش میشود و کد تولیدشده در فایلی که با گزینهٔ --header یا پیکربندی re2c:header مشخص شده نوشته خواهد شد (یا در stdout اگر نه از گزینه و نه از پیکربندی استفاده نشده باشد). فایل سرآیند تولیدشدهٔ خودکار ممکن است در مواردی نیاز باشد که از re2go برای تولید تعاریفی استفاده میشود که باید از سایر واحدهای ترجمه (translation units) قابل مشاهده باشند.
در اینجا مثالی از تولید یک فایل سرآیند آورده شده است که شامل تعریف وضعیت تحلیلگر لغوی (lexer state) با متغیرهای برچسب است (تعداد متغیرها به گرامر باقاعده بستگی دارد و برای برنامهنویس مشخص نیست).
فایل ورودی (Input file)
//go:generate re2go $INPUT -o $OUTPUT -i --header lexer/state.go
package main
import "./lexer" // the package is generated by re2c
/*!header:re2c:on*/
package lexer
type State struct {
Data string
Cur /*!stags:re2c format=", @@"; */ int
}
/*!header:re2c:off*/
func lex(yyrecord *lexer.State) int {
var t int
/*!re2c
re2c:header = "lexer/state.go";
re2c:api = record;
re2c:YYCTYPE = byte;
re2c:YYINPUT = "yyrecord.Data";
re2c:YYCURSOR = "yyrecord.Cur";
re2c:yyfill:enable = 0;
re2c:tags = 1;
re2c:tags:prefix = "Tag";
[a]* @t [b]* { return t }
*/
}
func main() {
st := &lexer.State{Data:"ab\x00",}
if lex(st) != 1 {
panic("error")
}
}
فایل سرآیند (Header file)
// Code generated by re2c, DO NOT EDIT.
package lexer
type State struct {
Data string
Cur, Mar, Tag1 int
}
با گزینهٔ -S, --skeleton، برنامهٔ re2go تمام کدهای غیر re2go را نادیده گرفته و یک برنامهٔ C خودکفا تولید میکند که میتواند در ادامه کامپایل و اجرا شود. این برنامه شامل کد تحلیلگر لغوی و دادههای ورودی است. برای هر DFA ساختهشده (بلاک یا شرط)، re2go یک تحلیلگر لغوی مستقل و دو فایل تولید میکند: یک فایل .input شامل رشتههای مشتقشده از DFA و یک فایل .keys با نتایج تطابق مورد انتظار. برنامه هر تحلیلگر لغوی را روی فایل .input متناظر اجرا کرده و نتایج را با موارد مورد انتظار مقایسه میکند. برنامههای اسکلت به دلایل متعددی بسیار مفید هستند:
- آنها میتوانند صحت بهینهسازیهای گوناگون re2go را بررسی کنند (دادهها در مراحل اولیهٔ فرآیند و پیش از اعمال هرگونه تبدیل روی DFA تولید میشوند).
- تولید مجموعهای از دادههای ورودی با پوشش مناسب میتواند برای هر دو مقصود آزمون و محکزنی (benchmarking) مفید باشد.
- تولید برنامههای اجرایی خودکفا امکان دسترسی به نمونههای آزمون کمینهشده (minimized) را فراهم میآورد (ممکن است کد اصلی بزرگ باشد یا وابستگیهای زیادی داشته باشد).
دشواری تولید دادههای ورودی در این است که بهجز در سادهترین حالتها، تعداد رشتههای ورودی ممکن بسیار زیاد است (حتی اگر طول رشته محدود باشد). re2go این مشکل را با تولید رشتههای کافی برای پوشش تقریباً تمام گذارهای DFA حل میکند. این ابزار از الگوریتم زیر استفاده میکند: ابتدا یک اسکلت از DFA میسازد. برای کدگذاریهایی با اندازه واحد کد ۱ بایتی (مانند ASCII، UTF-8 و EBCDIC)، اسکلت دقیقاً یک رونوشت از DFA اصلی است. برای کدگذاریهایی با واحدهای کد چندبایتی، اسکلت رونوشتی از DFA با حذف برخی گذارها است: بدین صورت که re2go حداکثر ۲۵۶ واحد کد را برای هر بازهٔ پیوسته و مجزا که متناظر با یک گذار DFA است در نظر میگیرد. مقادیر انتخابشده بهصورت یکنواخت توزیع شده و شامل کرانهای بازه نیز هستند. re2go به جای تلاش برای پوشش تمام مسیرهای ممکن در اسکلت (که نشدنی است)، مسیرهای کافی برای پوشش همهٔ گذارهای اسکلت را تولید میکند و بدین ترتیب پرشهای شرطی متناظر را در تحلیلگر لغوی فعال میسازد. پیادهسازی این الگوریتم به حدود ۱ گیگابایت گذار محدود شده و مقدار ثابتی حافظه مصرف میکند (re2go دادهها را به محض تولید در فایل مینویسد).
مصورسازی و اشکالزدایی (Visualization and debug)
با گزینهٔ -D, --emit-dot، برنامهٔ re2go کدی تولید نمیکند؛ در عوض، 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)
برنامهٔ re2go در ابتدا توسط 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.