| RE2OCAML(1) | RE2OCAML(1) |
نام (NAME)
re2ocaml - تولید تحلیلگرهای واژگانی سریع برای OCaml
خلاصه دستور (SYNOPSIS)
re2ocaml [ OPTIONS ] [ WARNINGS ] INPUT
ورودی میتواند یک فایل یا - برای stdin باشد.
مقدمه (INTRODUCTION)
ابزار re2ocaml به عنوان یک پیشپردازنده کار میکند. فایل ورودی را (که معمولاً برنامهای در OCaml است، اما میتواند هر چیز دیگری باشد) خوانده و به دنبال بلوکهایی از کد میگردد که درون نشانگرهای شروع/پایان با قالب خاص قرار گرفتهاند. متن خارج از این بلوکها عیناً به فایل خروجی کپی میشود. محتوای این بلوکها توسط re2ocaml پردازش میشود. این ابزار آنها را به کد OCaml ترجمه کرده و کد تولیدشده را به جای بلوک مربوطه در خروجی قرار میدهد.
در اینجا نمونهای از یک برنامه کوچک آورده شده است که بررسی میکند آیا رشته دادهشده شامل یک عدد دهدهی است یا خیر:
(* re2ocaml -o -i *)
open String
type state = {
yyinput: string;
mutable yycursor: int;
}
%{
re2c:YYFN = ["lex;bool", "yyrecord;state"];
re2c:yyfill:enable = 0;
[1-9][0-9]* { true }
* { false }
%}
let main () =
let st = {yyinput = "1234"; yycursor = 0}
in if not (lex st) then raise (Failure "error")
let _ = main ()
در خروجی، re2ocaml بلوک میانی را با کد تولیدشده جایگزین کرده است:
(* Generated by re2ocaml *)
(* re2ocaml -o -i *)
open String
type state = {
yyinput: string;
mutable yycursor: int;
}
let rec yy0 (yyrecord : state) : bool =
let yych = unsafe_get yyrecord.yyinput yyrecord.yycursor in
yyrecord.yycursor <- yyrecord.yycursor + 1;
match yych with
| '1'..'9' -> (yy2 [@tailcall]) yyrecord
| _ -> (yy1 [@tailcall]) yyrecord
and yy1 (yyrecord : state) : bool =
false
and yy2 (yyrecord : state) : bool =
let yych = unsafe_get yyrecord.yyinput yyrecord.yycursor in
match yych with
| '0'..'9' ->
yyrecord.yycursor <- yyrecord.yycursor + 1;
(yy2 [@tailcall]) yyrecord
| _ -> (yy3 [@tailcall]) yyrecord
and yy3 (yyrecord : state) : bool =
true
and lex (yyrecord : state) : bool =
(yy0 [@tailcall]) yyrecord
let main () =
let st = {yyinput = "1234"; yycursor = 0}
in if not (lex st) then raise (Failure "error")
let _ = main ()
مفاهیم پایه (BASICS)
یک برنامه re2ocaml شامل توالیای از بلوکها ترکیبشده با کد به زبان مقصد است. یک بلوک ممکن است شامل تعاریف، پیکربندیها، قواعد، کنشها و رهنمودها به هر ترتیبی باشد:
- name = regular-expression ;
- یک تعریف، name را به regular-expression پیوند میدهد. نامها میتوانند شامل نویسههای الفبایی-عددی و خط زیر باشند. بخش %عبارتهای منظم مروری بر نحو re2ocaml برای عبارتهای منظم ارائه میدهد. پس از تعریف، میتوان از name در سایر عبارتهای منظم و قواعد استفاده کرد. بازگشت در تعاریف نامگذاریشده مجاز نیست و هر نام باید پیش از استفاده تعریف شود. یک بلوک، تعاریف نامگذاریشده را از حوزه سراسری به ارث میبرد. تعریف مجدد نامی که در حوزه جاری وجود دارد یک خطا است.
- configuration = value ;
- یک پیکربندی امکان تغییر رفتار re2ocaml و سفارشیسازی کد تولیدشده را فراهم میکند. برای فهرست کامل پیکربندیهای پشتیبانیشده توسط re2ocaml به بخش %پیکربندیها مراجعه کنید. بسته به یک پیکربندی خاص، value میتواند یک کلمه کلیدی، یک عدد صحیح غیرمنفی یا یک رشته تکخطی باشد که باید در علامت نقلقول تکی یا دوتایی قرار گیرد، مگر اینکه از نویسههای الفبایی-عددی تشکیل شده باشد. یک بلوک، پیکربندیها را از حوزه سراسری به ارث میبرد و میتواند آنها را دوباره تعریف کند یا موارد جدیدی اضافه نماید. پیکربندیهای تعریفشده درون یک بلوک بر کل بلوک تأثیر میگذارند، حتی اگر در انتهای آن ظاهر شوند.
- regular-expression code
- یک قاعده، regular-expression را به کنش معنایی آن متصل میکند (یک بلوک کد درون آکولاد، یا یک بلوک کد که با := شروع شده و به یک خط جدید ختم میشود که پس از آن هر نویسه غیرفاصلهای قرار میگیرد). اگر regular-expression مطابقت پیدا کند، code مرتبط اجرا میشود. اگر چندین قاعده مطابقت داشته باشند، طولانیترین تطابق اولویت دارد. اگر چندین قاعده با همان رشته تطابق داشته باشند، اولین قاعده اولویت دارد. دو قاعده ویژه وجود دارد: قاعده پیشفرض * و قاعده پایان ورودی $. قاعده پیشفرض همیشه باید تعریف شود؛ این قاعده صرفنظر از جایگاهش در بلوک، کمترین اولویت را دارد و با هر واحد کد مطابقت پیدا میکند (لزوماً یک نویسه معتبر نیست، به بخش %پشتیبانی از رمزگذاری مراجعه کنید). قاعده پایان ورودی در صورتی باید تعریف شود که روش متناظر برای %مدیریت پایان ورودی استفاده شده باشد. با %شرایط شروع، قواعد نحو پیچیدهتری خواهند داشت.
- !action code
- یک کنش یک بلوک تعریفشده توسط کاربر از code را به یک مکان خاص در ماشین حالت متناهی تولیدشده پیوند میدهد (به همان روشی که کنشهای معنایی کد را به حالتهای نهایی پیوند میدهند). برای فهرست کامل کنشهای از پیش تعریفشده به بخش %کنشها مراجعه کنید.
- !directive ;
- یک رهنمود یکی از دستورات ویژه از پیش تعریفشده است. هر رهنمود هدف منحصربهفردی دارد. برای جزئیات به بخش %رهنمودها مراجعه کنید.
بلوکها (Blocks)
نشانگرهای شروع و پایان بلوک یا /*!re2c و */، یا %{ و %} هستند (از هر دو سبک پشتیبانی میشود). از نسخه 2.2، بلوکها میتوانند نامهای اختیاری داشته باشند که امکان ارجاع به آنها را در سایر بلوکها فراهم میکند. انواع مختلفی از بلوکها وجود دارد:
- /*!re2c[:<name>] ... */ یا %{[:<name>] ... %}
- یک بلوک سراسری شامل تعاریف، پیکربندیها، قواعد و رهنمودها است. re2ocaml عبارتهای منظم مرتبط با هر قاعده را به یک اتوماتون متناهی قطعی کامپایل میکند، آن را به شکل پرشهای شرطی در زبان مقصد کدگذاری کرده و بلوک را با کد تولیدشده جایگزین مینماید. نامها و پیکربندیهای تعریفشده در یک بلوک سراسری به حوزه سراسری اضافه میشوند و برای بلوکهای پس از آن قابل مشاهده خواهند بود. در آغاز برنامه، حوزه سراسری با %گزینههای خط فرمان مقداردهی اولیه میشود.
- /*!local:re2c[:<name>] ... */ یا %{local[:<name>] ... %}
- یک بلوک محلی مانند یک بلوک سراسری است، اما نامها و پیکربندیهای درون آن حوزه محلی دارند (بر سایر بلوکها تأثیری نمیگذارند).
- /*!rules:re2c[:<name>] ... */ یا %{rules[:<name>] ... %}
- یک بلوک قواعد شبیه یک بلوک محلی است، اما خودش کدی تولید نمیکند و تعریفی را هم به حوزه سراسری اضافه نمینماید - - این بلوک برای استفاده مجدد در سایر بلوکها در نظر گرفته شده است. این روشی برای اشتراکگذاری کد است (جزئیات بیشتر در بخش %بلوکهای قابل استفاده مجدد). پیش از نسخه 2.2 re2ocaml، بلوکهای قواعد به گزینه -r --reusable نیاز داشتند.
- /*!use:re2c[:<name>] ... */ یا %{use[:<name>] ... %}
- یک بلوک استفاده که به یک بلوک قواعد از پیش تعریفشده ارجاع میدهد. اگر نام مشخص شده باشد، re2ocaml به دنبال بلوک قواعدی با این نام میگردد. در غیر این صورت، از جدیدترین بلوک قواعد استفاده میشود (خواه نامگذاریشده باشد یا بدون نام). یک بلوک use میتواند تعاریف، پیکربندیها و قواعد خاص خود را بیفزاید که به موارد بلوک قواعد ارجاعشده اضافه میشوند. پیش از نسخه 2.2 re2ocaml، بلوکهای use به گزینه -r --reusable نیاز داشتند.
- /*!max:re2c[:<name1>[:<name2>...]] ... */ یا %{max[:<name1>[:<name2>...]] ... %}
- بلوکی که تعریف YYMAXFILL را تولید میکند. یک فهرست اختیاری از نام بلوکها مشخص میکند که هنگام محاسبه مقدار YYMAXFILL کدام بلوکها باید لحاظ شوند (اگر فهرست خالی باشد، همه بلوکها لحاظ میشوند). به طور پیشفرض، کد تولیدشده یک تعریف ماکرو برای C (#define YYMAXFILL <n>) یا یک متغیر سراسری برای Go (var YYMAXFILL int = <n>) است. میتوان آن را با پیکربندی اختیاری format سفارشی کرد که یک رشته الگو را مشخص میکند که در آن @@{max} (یا به اختصار @@) با مقدار عددی YYMAXFILL جایگزین میشود.
- /*!maxnmatch:re2c[:<name1>[:<name2>...]] ... */ یا %{maxnmatch[:<name1>[:<name2>...]] ... %}
- بلوکی که تعریف YYMAXNMATCH را تولید میکند (به گزینه -P --posix-captures نیاز دارد). یک فهرست اختیاری از نام بلوکها مشخص میکند که هنگام محاسبه مقدار YYMAXNMATCH کدام بلوکها باید لحاظ شوند (اگر فهرست خالی باشد، همه بلوکها لحاظ میشوند). به طور پیشفرض، کد تولیدشده یک تعریف ماکرو برای C (#define YYMAXNMATCH <n>) یا یک متغیر سراسری برای Go (var YYMAXNMATCH int = <n>) است. میتوان آن را با پیکربندی اختیاری format سفارشی کرد که یک رشته الگو را مشخص میکند که در آن @@{max} (یا به اختصار @@) با مقدار عددی YYMAXNMATCH جایگزین میشود.
- /*!stags:re2c[:<name1>[:<name2>...]] ... */، /*!mtags:re2c[:<name1>[:<name2>...]] ... */ یا %{stags[:<name1>[:<name2>...]] ... %}، %{mtags[:<name1>[:<name2>...]] ... %{
- بلوکهایی که قطعهکد الگویی را مشخص میکنند که برای هر متغیر s-tag/m-tag تولیدشده توسط re2ocaml گسترش مییابد. یک فهرست اختیاری از نام بلوکها مشخص میکند که هنگام محاسبه مجموعه متغیرهای برچسب کدام بلوکها باید لحاظ شوند (اگر فهرست خالی باشد، همه بلوکها لحاظ میشوند). دو پیکربندی اختیاری وجود دارد: format و separator. پیکربندی format یک رشته الگو را مشخص میکند که در آن @@{tag} (یا به اختصار @@) با نام هر متغیر برچسب جایگزین میشود. پیکربندی separator قطعهکدی را مشخص میکند که برای پیوستن قطعات format تولیدشده برای متغیرهای مختلف برچسب استفاده میشود.
- /*!svars:re2c[:<name1>[:<name2>...]] ... */، /*!mvars:re2c[:<name1>[:<name2>...]] ... */ یا %{svars[:<name1>[:<name2>...]] ... %}، %{mvars[:<name1>[:<name2>...]] ... %{
- بلوکهایی که قطعهکد الگویی را مشخص میکنند که برای هر s-tag/m-tag گسترش مییابد که یا صریحاً در قواعد ذکر شده است (با گزینه - -tags) یا به طور ضمنی توسط re2ocaml تولید شده است (با گزینههای - -captvars یا - -posix-captvars). یک فهرست اختیاری از نام بلوکها مشخص میکند که هنگام محاسبه مجموعه برچسبها کدام بلوکها باید لحاظ شوند (اگر فهرست خالی باشد، همه بلوکها لحاظ میشوند). دو پیکربندی اختیاری وجود دارد: format و separator. پیکربندی format یک رشته الگو را مشخص میکند که در آن @@{tag} (یا به اختصار @@) با نام هر برچسب جایگزین میشود. پیکربندی separator قطعهکدی را مشخص میکند که برای پیوستن قطعات format تولیدشده برای برچسبهای مختلف استفاده میشود.
- /*!getstate:re2c[:<name1>[:<name2>...]] ... */ یا %{getstate[:<name1>[:<name2>...]] ... %}
- بلوکی که توزیع شرطی را بر اساس وضعیت تحلیلگر لغوی تولید میکند (به گزینه - -storable-state نیاز دارد). یک فهرست اختیاری از نام بلوکها مشخص میکند که کدام بلوکها باید در توزیع وضعیت گنجانده شوند. انتقال پیشفرض به برچسب شروع اولین بلوک در فهرست میرود. اگر این فهرست خالی باشد، تمام بلوکها لحاظ میشوند و انتقال پیشفرض به اولین بلوک در فایل که دارای برچسب شروع است میرود. این نوع بلوک با گزینه - -loop-switch ناسازگار است، زیرا به انتقالهای بینبلوکی نیاز دارد که بدون goto یا فراخوانیهای تابع پشتیبانی نمیشوند.
- /*!conditions:re2c[:<name1>[:<name2>...]] ... */، /*!types:re2c... */ یا %{conditions[:<name1>[:<name2>...]] ... %}، %{types... %}
- بلوکی که شمارش شرایط را تولید میکند (به گزینه - -conditions نیاز دارد). یک فهرست اختیاری از نام بلوکها مشخص میکند که هنگام محاسبه مجموعه شرایط کدام بلوکها باید لحاظ شوند (اگر فهرست خالی باشد، همه بلوکها لحاظ میشوند). به طور پیشفرض، کد تولیدشده یک شمارش YYCONDTYPE است. میتوان آن را با پیکربندیهای اختیاری format و separator سفارشیسازی کرد. پیکربندی format یک رشته الگو را مشخص میکند که در آن @@{cond} (یا به اختصار @@) با نام هر شرط و @@{num} با نمایه عددی آن شرط جایگزین میشود. پیکربندی separator قطعهکدی را مشخص میکند که برای پیوستن قطعات format تولیدشده برای شرایط مختلف استفاده میشود.
- /*!include:re2c <file> */ یا %{include <file> %}
- این بلوک اجازه میدهد <file> را ضمیمه کنید که باید یک مسیر فایل درون علامت نقلقول دوتایی باشد. محتوای فایل عیناً به جای این بلوک جایگزین میشود، درست به همان شیوهای که #include در C/C++ کار میکند. این بلوک میتواند همراه با گزینه - -depfile برای تولید وابستگیهای سیستم ساخت بر روی فایلهای ضمیمهشده استفاده شود.
- /*!header:re2c:on*/ یا %{header:on %}
- این بلوک شروع فایل سرآیند را مشخص میکند. همه چیز پس از آن و تا بلوک header:off بعدی توسط re2ocaml پردازش شده و در فایل سرآیند مشخصشده با گزینه -t - -type-header نوشته میشود.
- /*!header:re2c:off*/ یا %{header:off %}
- این بلوک پایان فایل سرآیند شروعشده با بلوک header:on*/ را مشخص میکند.
- /*!ignore:re2c ... */ یا %{ignore ... %}
- بلوکی که محتوای آن نادیده گرفته شده و از فایل خروجی حذف میشود.
پیکربندیها (Configurations)
در اینجا فهرست کاملی از پیکربندیهای پشتیبانیشده توسط re2ocaml آورده شده است:
- re2c:api, re2c:input
- مشابه گزینهٔ --api.
- re2c:api:sigil
- نشانگری ("sigil") را مشخص میکند که برای جاینگهدارهای آرگومان در اولیههای API استفاده میشود. پیشفرض @@ است. یک جاینگهدار با نشانه شروع شده و به دنبال آن نام آرگومان درون آکولاد قرار میگیرد. برای مثال، اگر نشانه روی $ تنظیم شود، جاینگهدارها به صورت ${name} خواهند بود. APIهای تکآرگومانی میتوانند از نشانهگذاری خلاصهشده بدون قرار دادن نام در آکولاد استفاده کنند. این گزینه میتواند توسط گزینههای مربوط به اولیههای تکی API بازنویسی شود، مانند re2c:YYFILL@len برای YYFILL.
- re2c:api:style
- سبک API را تعیین میکند. مقادیر ممکن عبارتند از functions (پیشفرض برای C) و free-form (پیشفرض برای Go و Rust). در سبک functions، اولیههای API همراه با فهرستی از آرگومانها درون پرانتز به دنبال نام اولیه تولید میشوند. آرگومانها تنها برای پارامترهای خودکار تولیدشده (مانند تعداد نویسههای ارسالشده به YYFILL) ارائه میشوند، اما نه برای بافت کلی تحلیلگر واژگانی، بنابراین اولیهها رفتاری شبیه به ماکروها در C/C++ یا بستارها (closures) در Go و Rust دارند. در سبک free-form، اولیههای API شکل ثابتی ندارند: آنها باید به صورت رشتههایی شامل قطعهکدهای آزاد با متغیرهای جایگذاریشده به شکل @@{var} یا @@ تعریف شوند (که با آرگومانها در سبک تابعگونه مطابقت دارند). این پیکربندی میتواند برای اولیههای تکی API بازنویسی شود؛ برای مثال پیکربندی re2c:YYFILL:naked را برای YYFILL ببینید.
- re2c:bit-vectors, re2c:flags:bit-vectors, re2c:flags:b
- مشابه گزینهٔ --bit-vectors، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- re2c:captures, re2c:leftmost-captures
- مشابه گزینهٔ --leftmost-captures، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- re2c:captvars, re2c:leftmost-captvars
- مشابه گزینهٔ --leftmost-captvars، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- re2c:case-insensitive, re2c:flags:case-insensitive
- مشابه گزینهٔ --case-insensitive، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- re2c:case-inverted, re2c:flags:case-inverted
- مشابه گزینهٔ --case-inverted، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- re2c:case-ranges, re2c:flags:case-ranges
- مشابه گزینهٔ --case-ranges، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- re2c:computed-gotos, re2c:flags:computed-gotos, re2c:flags:g
- مشابه گزینهٔ --computed-gotos، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- re2c:computed-gotos:relative, re2c:cgoto:relative
- مشابه گزینهٔ --computed-gotos-relative، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- re2c:computed-gotos:threshold, re2c:cgoto:threshold
- اگر gotoی محاسبهشده استفاده شود، این پیکربندی آستانهٔ پیچیدگی را تعیین میکند که باعث تولید جدولهای پرش به جای دستورهای if تودرتو و نقشههای بیتی میشود. مقدار پیشفرض 9 است.
- re2c:cond:abort
- اگر روی یک مقدار عدد صحیح مثبت تنظیم شود، حالت پیشفرض در توزیع شرط تولیدشده، اجرای برنامه را متوقف (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 است).
- 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، اما میتواند بر پایهٔ هر بلوک پیکربندی شود.
- عبارت مورد استفاده برای متغیرهای برچسب را مشخص میکند. بهطور پیشفرض re2ocaml عبارتهایی به فرم yyt<N> را تولید میکند. این موضوع ممکن است ناخوشایند باشد، برای مثال اگر متغیرهای برچسب به عنوان فیلدهایی درون یک struct تعریف شده باشند. تمام رخدادهای @@{tag} یا @@ با نام واقعی برچسب جایگزین میشوند. برای نمونه، re2c:tags:expression = "s.@@"; منجر به ایجاد عبارتهایی به شکل s.yyt<N> در کد تولیدشده خواهد شد. همچنین پیکربندی re2c:api:sigil را ببینید..TP re2c:tags:negative عبارت ثابتی را مشخص میکند که برای مقدار منفی برچسب استفاده میشود (بهطور معمول در صورتی که برچسبها آفستهای عددی صحیح در رشته ورودی باشند، این مقدار -1، و در صورتی که اشارهگر باشند، یک اشارهگر تهی (null pointer) خواهد بود).
- پیشوند نام متغیرهای برچسب را مشخص میکند. مقدار پیشفرض yyt است.
- re2c:sentinel
- نماد سنتینل مورد استفاده برای بررسیهای پایان ورودی را مشخص میکند (هنگامی که بررسیهای مرز با re2c:yyfill:enable = 0; غیرفعال شده باشند و re2c:eof تنظیم نشده باشد). این پیکربندی بر تولید کد تأثیری ندارد: هدف آن اطمینان از این است که نماد سنتینل در میانه یک قاعده مجاز نباشد، و تضمین اینکه تحلیلگر لغوی فراتر از پایان بافر را نخواند. مقدار پیشفرض -1` است (در این حالت re2ocaml فرض میکند که سنتینل صفر است، که معمولترین حالت است). تنها اعداد دهدهی شناسایی میشوند.
- re2c:state:abort
- اگر روی یک مقدار عدد صحیح مثبت تنظیم شود، حالت پیشفرض در توزیع وضعیت تولیدشده اجرای برنامه را لغو (abort) میکند، و یک حالت صریح -1 حاوی انتقال به ابتدای بلوک خواهد بود.
- re2c:state:nextlabel
- کنترل میکند که آیا به دنبال عبارت switch مربوط به YYGETSTATE برچسب yyNext بیاید یا خیر (مقدار پیشفرض صفر است که نشاندهنده نبود برچسب است). بهطور جایگزین میتوان از re2c:label:start برای تولید یک برچسب شروع مشخص، یا از یک بلوک صریح getstate برای تولید عبارت switch مربوط به YYGETSTATE بهطور جداگانه از بلوک تحلیلگر لغوی استفاده کرد.
- re2c:unsafe, re2c:flags:unsafe
- مشابه گزینه --no-unsafe است، اما میتواند بر مبنای هر بلوک پیکربندی شود. اگر روی صفر تنظیم شود، از تولید پوششهای unsafe پیرامون YYPEEK جلوگیری میکند. پیشفرض غیرصفر است (پوششها تولید میشوند). این پیکربندی مختص به Rust است.
- re2c:YYBACKUP, re2c:define:YYBACKUP
- عنصر پایه عمومی API با نام YYBACKUP را تعریف میکند.
- re2c:YYBACKUPCTX, re2c:define:YYBACKUPCTX
- عنصر پایه عمومی API با نام YYBACKUPCTX را تعریف میکند.
- re2c:YYCONDTYPE, re2c:define:YYCONDTYPE
- عنصر پایه API با نام YYCONDTYPE را تعریف میکند.
- re2c:YYCTYPE, re2c:define:YYCTYPE
- عنصر پایه API با نام YYCTYPE را تعریف میکند.
- re2c:YYCTXMARKER, re2c:define:YYCTXMARKER
- عنصر پایه API با نام YYCTXMARKER را تعریف میکند.
- re2c:YYCURSOR, re2c:define:YYCURSOR
- عنصر پایه API با نام YYCURSOR را تعریف میکند.
- re2c:YYDEBUG, re2c:define:YYDEBUG
- عنصر پایه API با نام YYDEBUG را تعریف میکند.
- re2c:YYFILL, re2c:define:YYFILL
- عنصر پایه API با نام YYFILL را تعریف میکند.
- re2c:YYFILL@len, re2c:define:YYFILL@len
- نماد (sigil) مورد استفاده برای جایگزینی آرگومان در تعریف YYFILL را مشخص میکند. پیشفرض @@ است. جایگزین پیکربندی عمومیتر re2c:api:sigil میشود.
- re2c:YYFILL:naked, re2c:define:YYFILL:naked
- جایگزین پیکربندی عمومیتر re2c:api:style برای YYFILL میشود. مقدار صفر متناظر با سبک آزاد 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 است.
- re2c:YYGETSTATE, re2c:define:YYGETSTATE
- عنصر پایه API با نام YYGETSTATE را تعریف میکند.
- re2c:YYGETSTATE:naked, re2c:define:YYGETSTATE:naked
- جایگزین پیکربندی عمومیتر re2c:api:style برای YYGETSTATE میشود. مقدار صفر متناظر با سبک آزاد API است.
- re2c:YYGETACCEPT, re2c:define:YYGETACCEPT
- عنصر پایه API با نام YYGETACCEPT را تعریف میکند.
- re2c:YYLESSTHAN, re2c:define:YYLESSTHAN
- عنصر پایه عمومی API با نام YYLESSTHAN را تعریف میکند.
- re2c:YYLIMIT, re2c:define:YYLIMIT
- عنصر پایه API با نام YYLIMIT را تعریف میکند.
- re2c:YYMARKER, re2c:define:YYMARKER
- عنصر پایه API با نام YYMARKER را تعریف میکند.
- re2c:YYMTAGN, re2c:define:YYMTAGN
- عنصر پایه عمومی API با نام YYMTAGN را تعریف میکند.
- re2c:YYMTAGP, re2c:define:YYMTAGP
- عنصر پایه عمومی API با نام YYMTAGP را تعریف میکند.
- re2c:YYPEEK, re2c:define:YYPEEK
- عنصر پایه عمومی API با نام YYPEEK را تعریف میکند.
- re2c:YYRESTORE, re2c:define:YYRESTORE
- عنصر پایه عمومی API با نام YYRESTORE را تعریف میکند.
- re2c:YYRESTORECTX, re2c:define:YYRESTORECTX
- عنصر پایه عمومی API با نام YYRESTORECTX را تعریف میکند.
- re2c:YYRESTORETAG, re2c:define:YYRESTORETAG
- عنصر پایه عمومی API با نام YYRESTORETAG را تعریف میکند.
- re2c:YYSETCOND, re2c:define:YYSETCONDITION
- عنصر پایه API با نام YYSETCOND را تعریف میکند.
- re2c:YYSETCOND@cond, re2c:define:YYSETCONDITION@cond
- نماد (sigil) مورد استفاده برای جایگزینی آرگومان در تعریف YYSETCOND را مشخص میکند. مقدار پیشفرض @@ است. جایگزین پیکربندی عمومیتر re2c:api:sigil میشود.
- re2c:YYSETCOND:naked, re2c:define:YYSETCONDITION:naked
- جایگزین پیکربندی عمومیتر re2c:api:style برای YYSETCOND میشود. مقدار صفر متناظر با سبک آزاد API است.
- re2c:YYSETSTATE, re2c:define:YYSETSTATE
- عنصر پایه API با نام YYSETSTATE را تعریف میکند.
- re2c:YYSETSTATE@state, re2c:define:YYSETSTATE@state
- نماد (sigil) مورد استفاده برای جایگزینی آرگومان در تعریف YYSETSTATE را مشخص میکند. مقدار پیشفرض @@ است. جایگزین پیکربندی عمومیتر re2c:api:sigil میشود.
- re2c:YYSETSTATE:naked, re2c:define:YYSETSTATE:naked
- جایگزین پیکربندی عمومیتر re2c:api:style برای YYSETSTATE میشود. مقدار صفر متناظر با سبک آزاد API است.
- re2c:YYSETACCEPT, re2c:define:YYSETACCEPT
- عنصر پایه API با نام YYSETACCEPT را تعریف میکند.
- re2c:YYSKIP, re2c:define:YYSKIP
- عنصر پایه عمومی API با نام YYSKIP را تعریف میکند.
- re2c:YYSHIFT, re2c:define:YYSHIFT
- عنصر پایه عمومی API با نام YYSHIFT را تعریف میکند.
- re2c:YYCOPYMTAG, re2c:define:YYCOPYMTAG
- عنصر پایه عمومی API با نام YYCOPYMTAG را تعریف میکند.
- re2c:YYCOPYSTAG, re2c:define:YYCOPYSTAG
- عنصر پایه عمومی API با نام YYCOPYSTAG را تعریف میکند.
- re2c:YYSHIFTMTAG, re2c:define:YYSHIFTMTAG
- عنصر پایه عمومی API با نام YYSHIFTMTAG را تعریف میکند.
- re2c:YYSHIFTSTAG, re2c:define:YYSHIFTSTAG
- عنصر پایه عمومی API با نام YYSHIFTSTAG را تعریف میکند.
- re2c:YYSTAGN, re2c:define:YYSTAGN
- عنصر پایه عمومی API با نام YYSTAGN را تعریف میکند.
- re2c:YYSTAGP, re2c:define:YYSTAGP
- عنصر پایه عمومی API با نام YYSTAGP را تعریف میکند.
- re2c:yyaccept, re2c:variable:yyaccept
- عنصر پایه API با نام yyaccept را تعریف میکند.
- re2c:yybm, re2c:variable:yybm
- عنصر پایه API با نام yybm را تعریف میکند.
- re2c:yybm:hex, re2c:variable:yybm:hex
- اگر روی غیرصفر تنظیم شود، نقشههای بیتی (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
- اگر روی غیرصفر تنظیم شود، re2ocaml در هر بار خوانده شدن yych، بهطور خودکار یک تبدیل به YYCTYPE تولید میکند. مقدار پیشفرض صفر است (بدون تبدیل).
- re2c:yych:literals, re2c:variable:yych:literals
- شکل لیترالهایی که yych در برابر آنها تطبیق داده میشود را مشخص میکند. مقادیر ممکن عبارتند از: char (لیترالهای نویسهای درون گیومه تکی؛ نویسههای غیرقابلچاپ از توالیهای گریز که با بکاسلش آغاز میشوند استفاده میکنند)، hex (اعداد صحیح هگزادسیمال) و char_or_hex (ترکیبی از هر دو، لیترالهای نویسهای برای نویسههای قابلچاپ و اعداد صحیح هگزادسیمال برای بقیه).
- re2c:yyctable, re2c:variable:yyctable
- عنصر پایه API با نام yyctable را تعریف میکند..TP re2c:yynmatch, re2c:variable:yynmatch پریمیتیو API با نام yynmatch را تعریف میکند.
- re2c:yypmatch, re2c:variable:yypmatch
- پریمیتیو API با نام yypmatch را تعریف میکند.
- re2c:yytarget, re2c:variable:yytarget
- پریمیتیو API با نام yytarget را تعریف میکند.
- re2c:yystable, re2c:variable:yystable
- منسوخ شده است.
- re2c:yystate, re2c:variable:yystate
- پریمیتیو API با نام yystate را تعریف میکند.
- re2c:yyfill, re2c:variable:yyfill
- پریمیتیو API با نام yyfill را تعریف میکند.
- re2c:yyfill:check
- در صورت تنظیم روی صفر، از تولید بررسی پیش از YYFILL برای تعداد نویسههای ورودی (تعریف YYLESSTHAN در API عمومی و مقایسه مبتنی بر YYLIMIT در API اشارهگر C) جلوگیری میکند. مقدار پیشفرض غیرصفر است (بررسی تولید میشود).
- re2c:yyfill:enable
- در صورت تنظیم روی صفر، از تولید YYFILL (همراه با بررسی آن) جلوگیری میکند. این مورد باید زمانی استفاده شود که کل ورودی در یک قطعه از حافظه قرار میگیرد (نیازی به بافرسازی نیست) و بررسیهای پایان ورودی به بررسیهای YYFILL وابسته نیستند (برای مثال در صورتی که از یک نویسه نگهبان (sentinel) استفاده شده باشد). از هشدارها (گزینه -W) و پیکربندی re2c:sentinel استفاده کنید تا مطمئن شوید تحلیلگر واژگانی (lexer) تولیدشده نمیتواند فراتر از پایان ورودی را بخواند. مقدار پیشفرض غیرصفر است (YYFILL فعال است).
- re2c:yyfill:parameter
- در صورت تنظیم روی صفر، از تولید پارامتر ارسالی به YYFILL جلوگیری میکند. این پارامتر حداقل تعداد نویسههایی است که باید فراهم شوند. پیشفرض مقدار غیرصفر است (پارامتر تولید میشود). این پیکربندی میتواند توسط re2c:YYFILL:naked یا re2c:api:style بازنویسی شود.
- re2c:yyfn:sep
- جداکننده مورد استفاده در عناصر YYFN را تعیین میکند (پیشفرض نقطه-ویرگول است).
- re2c:yyfn:throw
- استثناهای پرتابشده توسط تابع YYFN را مشخص میکند (پیشفرض خالی است، که به معنای عدم وجود هیچ استثنایی است).
عبارات باقاعده (Regular expressions)
re2ocaml از نحو زیر برای عبارات باقاعده استفاده میکند:
- "foo"
- لیترال رشتهای حساس به حروف بزرگ و کوچک.
- 'foo'
- لیترال رشتهای غیرحساس به حروف بزرگ و کوچک.
- [a-xyz], [^a-xyz]
- کلاس نویسه (احتمالاً نفیشده).
- .
- هر نویسهای به جز خط جدید (newline).
- R \ S
- تفاضل کلاسهای نویسه R و S.
- R*
- صفر یا چند بار تکرار R.
- R+
- یک یا چند بار تکرار R.
- R?
- R اختیاری.
- R{n}
- تکرار R دقیقاً n بار.
- R{n,}
- تکرار R حداقل n بار.
- R{n,m}
- تکرار R از n تا m بار.
- (R)
- تنها R؛ از پرانتز برای تغییر اولویت تقدم استفاده میشود. اگر استخراج زیرتطابق (submatch) فعال باشد، بسته به گزینه --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..SS "کنشها (Actions)"
در اینجا فهرستی از کنشهای از پیش تعریفشده که توسط re2ocaml پشتیبانی میشوند آمده است:
- !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) جهت جلوگیری از جریان کنترل ناخواسته استفاده شود.
دستورالعملها (Directives)
در اینجا فهرست کاملی از دستورالعملهای پشتیبانیشده توسط re2ocaml آورده شده است:
- !use:name ;
- یک دستورالعمل use درونبلوکی که یک بلوک قواعد از پیش تعریفشده با name مشخصشده را در بلوک فعلی ادغام میکند. تعاریف نامدار، پیکربندیها و قواعد بلوک ارجاعشده به موارد فعلی افزوده میشوند. تداخلها میان قواعد و پیکربندیهای همپوشان به روش معمول برطرف میشوند: نخستین قاعده اولویت دارد و آخرین پیکربندی بر پیکربندیهای قبلی بازنویسی میشود. یک استثنا، قواعد ویژه *، $ و <!> هستند که برای آنها تعریف محلی بلوک همواره اولویت دارد. یک دستورالعمل use میتواند در هر کجای درون یک بلوک قرار گیرد و استفاده از چندین دستورالعمل use مجاز است.
- !include file ;
- این دستورالعمل همانند بلوک include است: محتویات file را دقیقاً در محل دستورالعمل درج میکند.
رابط برنامه (Program interface)
کد تولیدشده به کمک بنیادینهها (primitives)، که در مجموع با نام API شناخته میشوند، با برنامه بیرونی ارتباط برقرار میکند. اینکه چه بنیادینههایی باید برای یک برنامه خاص تعریف شوند به عوامل متعددی از جمله پیچیدگی عبارتهای منظم، شیوه نمایش ورودی، بافرینگ و استفاده از ویژگیهای گوناگون بستگی دارد. تمام بنیادینههای لازم باید توسط کاربر در قالب ماکروها، توابع، متغیرها یا هر شکل مناسب دیگری که کد تولیدشده را از نظر نحوی و معنایی صحیح سازد، تعریف شوند. ابزار re2ocaml تعاریف را بررسی نمیکند (و نمیتواند بررسی کند)، بنابراین اگر چیزی جا افتاده باشد یا به نادرستی تعریف شده باشد، برنامه تولیدشده ممکن است در زمان کامپایل یا زمان اجرا دچار خطا شود. این راهنما مثالهایی از تعاریف API را در رایجترین موارد ارائه میدهد.
ابزار re2ocaml دارای دو سبک API است که مجموعه اصلی بنیادینههای مورد استفاده یک برنامه را مشخص میکنند:
- Record API
- رابط Record API پیشفرض برای بکاند OCaml است. این رابط شامل یک متغیر yyrecord است (نام آن میتواند با re2c:yyrecord بازنویسی شود) که باید به عنوان یک record با فیلدهای _yyinput، _yycursor، _yymarker، _yyctxmarker و _yylimit تعریف شود. تنها فیلدهایی که توسط کد تولیدشده استفاده میشوند نیاز به تعریف دارند، و نامهای آنها قابل پیکربندی است.
- Generic API
- این منعطفترین API است. این رابط با گزینه --api generic یا پیکربندی re2c:api = generic فعال میشود. این رابط شامل بنیادینههایی برای عملیات عمومی است: YYPEEK، YYSKIP، YYBACKUP، YYBACKUPCTX، YYSTAGP، YYSTAGN، YYMTAGP، YYMTAGN، YYRESTORE، YYRESTORECTX، YYRESTORETAG، YYSHIFT، YYSHIFTSTAG، YYSHIFTMTAG، YYLESSTHAN، YYEND.
در اینجا فهرست کاملی از بنیادینههای API آورده شده است که ممکن است توسط کد تولیدشده به منظور ارتباط با برنامه بیرونی استفاده شوند.
- YYCTYPE
- نوع نویسههای ورودی (واحدهای کد). برای رمزگذاریهای ASCII، EBCDIC و UTF-8 باید یک عدد صحیح بدون علامت ۱ بایتی باشد. برای UTF-16 یا UCS-2 باید یک عدد صحیح بدون علامت ۲ بایتی باشد. برای UTF-32 باید یک عدد صحیح بدون علامت ۴ بایتی باشد.
- YYCURSOR
- یک l-value که موقعیت ورودی جاری را ذخیره میکند (یک اشارهگر یا یک آفست عددی صحیح در YYINPUT). در ابتدا YYCURSOR باید به اولین نویسه ورودی اشاره کند و بعداً توسط کد تولیدشده پیش برده میشود. هنگامی که یک قاعده مطابقت پیدا میکند، موقعیت YYCURSOR بعد از آخرین نویسه منطبقشده قرار دارد.
- YYLIMIT
- یک r-value که موقعیت پایان ورودی را ذخیره میکند (یک اشارهگر یا یک آفست عددی صحیح در YYINPUT). در ابتدا YYLIMIT باید به موقعیت پس از آخرین نویسه ورودی موجود اشاره کند. این مقدار توسط کد تولیدشده تغییر نمیکند. تحلیلگر لغوی YYCURSOR را با YYLIMIT مقایسه میکند تا تشخیص دهد آیا نویسههای ورودی کافی باقی ماندهاند یا خیر.
- YYMARKER
- یک l-value که موقعیت آخرین قاعده منطبقشده را ذخیره میکند (یک اشارهگر یا یک آفست عددی صحیح در YYINPUT). این متغیر برای بازیابی موقعیت YYCURSOR در صورتی که تطابق طولانیتر با شکست مواجه شود و تحلیلگر لغوی نیاز به عقبگرد داشته باشد، استفاده میشود. مقداردهی اولیه لازم نیست.
- YYCTXMARKER
- یک l-value که موقعیت بافت پسین (trailing context) را ذخیره میکند (یک اشارهگر یا یک آفست عددی صحیح در YYINPUT). مقداردهی اولیه لازم نیست. YYCTXMARKER تنها در صورتی مورد نیاز است که از عملگر پیشنگری / استفاده شود.
- YYFILL
- یک اولیه API عمومی با یک متغیر len. YYFILL باید حداقل len نویسه ورودی دیگر را فراهم کند یا با شکست مواجه شود. اگر re2c:eof استفاده شود، len همیشه 1 است و YYFILL همیشه باید به تابع فراخواننده بازگردد؛ مقدار بازگشتی صفر نشاندهنده موفقیت است. اگر re2c:eof استفاده نشود، مقدار بازگشتی YYFILL نادیده گرفته میشود و در صورت بروز خطا نباید بازگردد. حداکثر مقدار len برابر با YYMAXFILL است.
- YYFN
- یک اولیه که پیشنمونه تابع را در مدل کد --recursive-functions تعریف میکند. مقدار آن باید آرایهای از یک یا چند رشته باشد، که در آن هر رشته شامل دو یا سه مؤلفه است که با رشته مشخصشده در پیکربندی re2c:fn:sep (معمولاً یک نقطهویرگول) از هم جدا میشوند. اولین عنصر آرایه، نام تابع و نوع بازگشتی را تعریف میکند (برای یک تابع بدون مقدار بازگشتی، خالی است). عناصر بعدی، آرگومانهای تابع را تعریف میکنند: نخست، عبارتی برای آرگومان مورد استفاده در بدنه تابع (معمولاً فقط یک نام)؛ دوم، نوع آرگومان؛ سوم، پارامتر رسمی اختیاری (به طور پیشفرض به مؤلفه نخست در نظر گرفته میشود - معمولاً هر دو آرگومان و پارامتر شناسه یکسانی هستند).
- YYINPUT
- یک r-value که دنباله نویسههای ورودی جاری را ذخیره میکند (رشته، بافر و غیره).
- YYMAXFILL
- یک ثبات عددی صحیح برابر با حداکثر مقدار آرگومان ورودی به YYFILL. این مقدار میتواند با یک بلوک max تولید شود.
- YYLESSTHAN
- یک اولیه API عمومی با یک متغیر len. این اولیه باید به عنوان یک r-value از نوع بولی تعریف شود که اگر و تنها اگر کمتر از 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
- نوع شمارش شرایط. میتواند با بلوک 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-ام درست است اگر و تنها اگر واحد کد n-ام در محدوده گذار k-ام باشد. ایده این نقشه بیتی جایگزینی بسیاری از شاخههای if یا موارد switch با یک بررسی از یک بیت منفرد در جدول است.
- yych
- یک l-value از نوع YYCTYPE که نویسه ورودی جاری را ذخیره میکند. تعریف کاربر تنها با گزینه -f --storable-state لازم است.
- yyctable
- جدول پرش تولیدشده برای توزیع شرط اولیه (فعالشده با ترکیب گزینههای --conditions و --computed-gotos).
- yyfill
- یک l-value که نتیجه فراخوانی YYFILL را ذخیره میکند (این ممکن است برای زبانهای تابعی خالص لازم باشد، جایی که YYFILL یک تابع مونادیک با مقدار بازگشتی پیچیده است).
- yynmatch
- یک l-value از نوع عدد صحیح بدون علامت که تعداد گروههای گیرانداز POSIX را در قاعده منطبقشده ذخیره میکند. تنها با گزینه -P --posix-captures استفاده میشود.
- yypmatch
- آرایهای از l-valueها که برای نگهداری مقادیر برچسب متناظر با پرانتزهای گیرانداز در قاعده منطبقشده استفاده میشوند. طول آرایه باید حداقل yynmatch * 2 باشد (معمولاً YYMAXNMATCH * 2 انتخاب مناسبی است). تنها با گزینه -P --posix-captures استفاده میشود.
- yystable
- منسوخ شده است.
- yystate
- یک l-value استفادهشده با گزینه --loop-switch برای ذخیره وضعیت کنونی DFA.
- yytarget
- جدول پرشی که شامل مقاصد پرش (آدرسهای برچسب) برای تمامی گذارها از یک وضعیت است. این جدول مختص به هر وضعیت است. تولید جداول yytarget با گزینه --computed-gotos فعال میشود.
گزینهها (OPTIONS)
برخی از گزینهها دارای پیکربندیهای متناظر هستند، برخی دیگر سراسری بوده و پس از شروع خواندن فایل ورودی توسط re2ocaml قابل تغییر نیستند. گزینههای اشکالزدایی معمولاً نیازمند ساختن re2ocaml در پیکربندی اشکالزدایی هستند. گزینههای داخلی برای آزمایش الگوریتمهای بهکاررفته در re2ocaml مفید میباشند.
- -? --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
- ادغام حالتهای متوالی در یک دستور switch در قالب یک بازه به شکل low ... high. این نحو، یک افزونه زبان C/C++ است که توسط کامپایلرهایی مانند GCC، Clang و Tcc پشتیبانی میشود. مزیت اصلی نسبت به استفاده از حالتهای تکی، تولید کد کوچکتر و زمان تولید سریعتر است، هرچند برای برخی از کامپایلرها مانند Tcc به اندازه باینری کوچکتر نیز منجر میشود.
- --computed-gotos -g
- بهینهسازی پرشهای شرطی با استفاده از افزونه غیراستاندارد "computed goto" (که باید توسط کامپایلر پشتیبانی شود). re2ocaml تنها در موارد پیچیده با انشعابهای شرطی فراوان جدولهای پرش را تولید میکند. آستانه پیچیدگی را میتوان با پیکربندی cgoto:threshold تنظیم کرد. آفستهای نسبی را میتوان با پیکربندی cgoto:relative فعال نمود. این گزینه مستلزم --bit-vectors است.
- --computed-gotos-relative
- مشابه --computed-gotos اما در عوض آفستهای نسبی را برای جدولهای پرش تولید میکند (که باید توسط کامپایلر پشتیبانی شود). این گزینه مستلزم --computed-gotos است.
- --conditions --start-conditions -c
- فعالسازی پشتیبانی از "conditions" مشابه Flex: چندین تحلیلگر واژگانی بههمپیوسته در یک بلوک. این یک جایگزین برای مشخص کردن دستی بلوکهای مختلف re2ocaml متصلشده با goto یا فراخوانیهای تابع است.
- --depfile FILE
- نوشتن اطلاعات وابستگی در FILE در قالب یک قاعده Makefile به شکل <output-file> : <input-file> [include-file ...]. این امکان رهگیری وابستگیهای ساخت را در حضور بلوکها/دستورالعملهای include فراهم میکند، بهطوری که بهروزرسانی فایلهای include باعث بازتولید فایل خروجی میشود. این گزینه به گزینه --output وابسته است.
- --ebcdic --ecb -e
- تولید یک تحلیلگر واژگانی که ورودی را در کدگذاری EBCDIC میخواند. re2ocaml فرض میکند که محدوده نویسهها 0 -- 0xFF و اندازه هر نویسه ۱ بایت است.
- --empty-class <match-empty | match-none | error>
- تعریف شیوه برخورد re2ocaml با کلاسهای نویسه خالی. با match-empty (پیشفرض)، کلاس خالی با ورودی خالی تطبیق مییابد (که غیرمنطقی است اما سازگار با گذشته است). با match-none کلاس خالی همیشه در تطبیق شکست میخورد. با error کلاس خالی یک خطای کامپایل ایجاد میکند.
- --encoding-policy <fail | substitute | ignore>
- تعریف شیوه برخورد re2ocaml با جانشینهای یونیکد. با fail برنامه re2ocaml در صورت مواجهه با یک جانشین با خطا متوقف میشود. با substitute برنامه re2ocaml بیصدا جانشینها را با نقطه کد خطای 0xFFFD جایگزین میکند. با ignore (پیشفرض) re2ocaml با جانشینها مانند نقاط کد عادی رفتار میکند. استاندارد یونیکد میگوید که جانشینهای تنها نامعتبرند، اما کتابخانهها و برنامههای دنیای واقعی رفتارهای متفاوتی دارند.
- --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 مفید است. re2ocaml به دنبال FILE در دایرکتوری فایل والد و در مکانهای include مشخصشده با گزینه -I میگردد.
- --input <default | custom>
- نام مستعار منسوخشده برای --api. گزینه default متناظر با simple است (در واقع برای بیشتر پیشرانهها پیشفرض است، اما نه برای همه). گزینه custom متناظر با generic است.
- --input-encoding <ascii | utf8>
- تعیین شیوه تجزیه عبارتهای منظم توسط re2ocaml. با ascii (پیشفرض) re2ocaml ورودی را با کدگذاری ASCII مدیریت میکند: هر دنبالهای از واحدهای کد، دنبالهای از نویسههای مستقل ۱ بایتی است. با utf8 re2ocaml ورودی را با کدگذاری 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، جایی که وضعیتهای منفرد همان حالتهای دستور switch هستند. وضعیت در یک متغیر yystate ذخیره میشود. گذارها میان وضعیتها yystate را به برچسب case وضعیت مقصد بهروزرسانی کرده و اجرا را به ابتدای حلقه ادامه میدهند..TP --nested-ifs -s استفاده از دستورهای if تودرتو به جای دستورهای switch در پرشهای شرطی. این کار معمولاً در کامپایلرهای فاقد بهینهسازی منجر به ایجاد کد کارآمدتری میشود.
- --no-debug-info -i
- دستورالعملهای خط (line directives) را در خروجی قرار نده. این گزینه ممکن است هنگامی که کد تولیدشده در یک سیستم کنترل نسخه نگهداری میشود مفید باشد (برای جلوگیری از تفاوتها یا diffهای عظیمِ تولیدشده بهصورت خودکار بر اثر تغییرات کوچک).
- --no-generation-date
- جلوگیری از درج تاریخ در فایل تولیدشده.
- --no-version
- جلوگیری از درج نسخه در فایل تولیدشده.
- --no-unsafe
- پوشش unsafe را روی YYPEEK تولید نکن (این گزینه مخصوص Rust است). به دلایل عملکردی، YYPEEK باید از بررسی مرزها (bounds-checking) خودداری کند، زیرا تحلیلگر لغوی (lexer) از قبل بررسیهای انتهای ورودی را به شیوهای کارآمدتر انجام میدهد. کاربر میتواند تعریف امنی از YYPEEK ارائه دهد، یا تعریفی که تنها در بیلدهای release ناامن است؛ در چنین حالتی، گزینه --no-unsafe به جلوگیری از هشدارهای مربوط به بلوکهای unsafe زائد کمک میکند.
- --output -o OUTPUT
- فایل OUTPUT را مشخص میکند.
- --posix-captures, -P
- استخراج زیرتطابق (submatch) را با گروههای ضبط به سبک 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 مشخصشده بارگذاری کرده و آنها را بر روی فایل نحو (syntax) پیشفرض اعمال میکند. توجه داشته باشید که FILE میتواند تنها چند پیکربندی را تعریف کند (اگر برای اصلاح فایل نحو پیشفرض استفاده شود)، یا میتواند یک بکاند زبانی کاملاً جدید را تعریف نماید (در مورد اخیر توصیه میشود از گزینه --lang none استفاده کنید).
- استخراج زیرتطابق را با برچسبها (tags) فعال میکند.
- --ucs2 --wide-chars -w
- تولید لکسری که ورودی کدگذاریشده با UCS2 را میخواند. re2ocaml فرض میکند که محدوده نویسهها 0 -- 0xFFFF و اندازه نویسه ۲ بایت است. این گزینه مستلزم --nested-ifs است.
- --utf8 --utf-8 -8
- تولید لکسری که ورودی با کدگذاری UTF-8 را میخواند. re2ocaml فرض میکند که محدوده نویسهها 0 -- 0x10FFFF و اندازه نویسه ۱ بایت است.
- --utf16 --utf-16 -x
- تولید لکسری که ورودی کدگذاریشده با UTF16 را میخواند. re2ocaml فرض میکند که محدوده نویسهها 0 -- 0x10FFFF و اندازه نویسه ۲ بایت است. این گزینه مستلزم --nested-ifs است.
- --utf32 --unicode -u
- تولید لکسری که ورودی کدگذاریشده با UTF32 را میخواند. re2ocaml فرض میکند که محدوده نویسهها 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 پس از کمینهسازی (minimization) (در قالب .dot).
- --dump-dfa-tagopt
- گزینه اشکالزدایی: خروجی دادن DFA پس از بهینهسازیهای برچسب (در قالب .dot).
- --dump-dfa-tree
- گزینه اشکالزدایی: خروجی دادن DFA در حال ساخت با حالتهایی که به عنوان درختهای تاریخچه برچسب نمایش داده میشوند (در قالب .dot).
- --dump-dfa-raw
- گزینه اشکالزدایی: خروجی دادن DFA در حال ساخت با مجموعهحالتهای بسطیافته (در قالب .dot).
- --dump-interf
- گزینه اشکالزدایی: خروجی دادن جدول تداخل حاصل از تحلیل زنده بودن (liveness analysis) متغیرهای برچسب.
- --dump-nfa
- گزینه اشکالزدایی: خروجی دادن NFA (در قالب .dot).
- --emit-dot -D
- به جای خروجی عادی، گراف لکسر را در قالب .dot تولید میکند. خروجی میتواند به کمک Graphviz به یک تصویر تبدیل شود (مثلاً دستورهایی مانند dot -Tpng -odfa.png dfa.dot).
- --dfa-minimization <moore | table>
- گزینه داخلی: الگوریتم کمینهسازی DFA مورد استفاده توسط re2ocaml. گزینه moore نشاندهنده الگوریتم مور (Moore) است (که پیشفرض میباشد). گزینه table نشاندهنده الگوریتم "پر کردن جدول" (table filling) است. هر دو الگوریتم باید صرفنظر از برچسبگذاری مجدد حالتها، DFA یکسانی تولید کنند؛ الگوریتم پر کردن جدول سادهتر و بسیار کندتر است و به عنوان پیادهسازی مرجع عمل میکند.
- --eager-skip
- گزینه داخلی: باعث میشود لکسر تولیدشده موقعیت ورودی را بهصورت مشتاقانه (eagerly) پیش ببرد -- بلافاصله پس از خواندن نماد ورودی. این گزینه رفتار پیشفرض را که در آن موقعیت ورودی بهصورت تنبل (lazily) -- پس از گذار به حالت بعدی -- پیش میرود، تغییر میدهد.
- --no-lookahead
- گزینه داخلی، منسوخشده. قبلاً برای فعال کردن الگوریتم TDFA(0) استفاده میشد. بر خلاف TDFA(1)، الگوریتم TDFA(0) از پیشنگری یکنمادی (one-symbol lookahead) استفاده نمیکند. این الگوریتم عملیاتهای ثبات را به جای گذارهای خروجی، روی گذارهای ورودی اعمال میکند. ارزیابیها نشان دادند که الگوریتم TDFA(0) نسبت به TDFA(1) کارایی کمتری دارد.
- --no-optimize-tags
- گزینه داخلی: جلوگیری از بهینهسازی متغیرهای برچسب (مفید برای اشکالزدایی).
- --posix-closure <gor1 | gtop>
- گزینه داخلی: تعیین الگوریتم کوتاهترین مسیر مورد استفاده برای ساخت بستار اپسیلون (epsilon-closure) با معناشناسی رفع ابهام POSIX: گزینه gor1 (پیشفرض) نشاندهنده الگوریتم گلدبرگ-رادزیک (Goldberg-Radzik) است، و gtop نشاندهنده الگوریتم "ترتیب توپولوژیکی سراسری" (global topological order) است.
- --posix-prectable <complex | naive>
- گزینه داخلی: مشخص کردن الگوریتم مورد استفاده برای محاسبه جدول اولویت POSIX. الگوریتم complex جدول اولویت را در یک پیمایش از درخت تاریخچه برچسب محاسبه میکند و دارای پیچیدگی مرتبه دوم در تعداد حالتهای TNFA است؛ این گزینه پیشفرض است. الگوریتم naive در بدترین حالت دارای پیچیدگی مرتبه سوم در تعداد حالتهای TNFA است، اما بسیار سادهتر از complex بوده و در موارد عادی ممکن است کمی سریعتر باشد..TP --stadfa گزینهٔ داخلی، منسوخشده. در گذشته برای فعالسازی الگوریتم staDFA به کار میرفت، که تفاوت آن با TDFA در این است که عملیات ثباتها به جای انتقالها درون وضعیتها قرار میگیرد. بنچمارکها نشان دادند که الگوریتم staDFA کارایی کمتری نسبت به TDFA دارد.
- --fixed-tags <none | toplevel | all>
- گزینهٔ داخلی: مشخص میکند که آیا بهینهسازی برچسب ثابت (fixed-tag) باید روی همهٔ برچسبها اعمال شود (all)، هیچیک از آنها (none)، یا فقط روی برچسبهای موجود در الحاق بالاترین سطح (toplevel). مقدار پیشفرض all است. برچسبهای "ثابت" برچسبهایی هستند که در فاصلهٔ ثابتی از برچسبی دیگر (موسوم به "پایه") قرار دارند. در چنین مواردی تنها نیاز به ردیابی برچسب پایه است، و مقدار برچسب ثابت میتواند از مقدار برچسب پایه به علاوهٔ یک آفست ایستا محاسبه شود. برای برچسبهایی که تحت جایگزینی یا تکرار هستند، بررسی اینکه آیا برچسب پایه مقدار عدمتطابق (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
- هشدار در صورتی که یک عبارت منظم حاوی یک کلاس نویسهٔ خالی باشد. تلاش برای تطبیق یک کلاس نویسهٔ خالی بیمعنی است: همیشه باید ناموفق باشد. با این حال، به دلایل سازگاری عقبرو، re2ocaml کلاسهای نویسهٔ خالی را مجاز دانسته و با آنها مانند رشتههای خالی رفتار میکند. از گزینهٔ --empty-class برای تغییر رفتار پیشفرض استفاده کنید.
- -Wmatch-empty-string
- هشدار در صورتی که یک قاعده پوچپذیر باشد (با یک رشتهٔ خالی تطبیق یابد). اگر تحلیلگر لغوی درون یک حلقه اجرا شود و تطبیق رشتهٔ خالی غیرعمدی باشد، تحلیلگر لغوی ممکن است بهطور غیرمنتظره در یک حلقهٔ بیپایان متوقف شود.
- -Wswapped-range
- هشدار در صورتی که کران پایین یک بازه بزرگتر از کران بالای آن باشد. رفتار پیشفرض این است که بدون هشدار، جای کرانهای بازه جابهجا میشود.
- -Wundefined-control-flow
- هشدار در صورتی که برخی از رشتههای ورودی باعث جریان کنترل تعریفنشده در تحلیلگر لغوی شوند (الگوهای دارای اشکال گزارش میشوند). این یک اشتباه خطرناک و رایج است. این مشکل را میتوان به سادگی با افزودن قاعدهٔ پیشفرض * که کمترین اولویت را دارد، با هر واحد کد مطابقت دارد و همیشه یک واحد کد را مصرف میکند، برطرف ساخت.
- -Wunreachable-rules
- هشدار دربارهٔ قواعدی که توسط قواعد دیگر پوشانده شدهاند (shadowed) و هرگز تطبیق نخواهند یافت.
- -Wdeprecated-eof_rule
- هشدار دربارهٔ قواعد مستقل پایان ورودی $ که بر اثر تغییرات آینده از کار خواهند افتاد و نیاز به اصلاح دارند. در حال حاضر این قواعد هنگام تداخل با قواعد دیگر دارای اولویت هستند، اما پس از معرفی نماد عمومی پایان ورودی $، ترتیب اولویت تغییر خواهد کرد و این قواعد توسط قواعد دیگر پوشانده خواهند شد.
- -Wuseless-escape
- هشدار در صورتی که یک نماد به اشتباه گریزدهی شده باشد (دارای escape باشد). بهطور پیشفرض، re2ocaml چنین گریزهایی را بیصدا نادیده میگیرد، اما این حالت میتواند نشاندهندهٔ یک اشتباه تایپی یا خطا در توالی گریز (escape sequence) باشد.
- -Wnondeterministic-tags
- هشدار در صورتی که یک برچسب دارای درجهٔ nام از غیرقطعی بودن باشد، که در آن n بزرگتر از ۱ است.
- -Wsentinel-in-midrule
- هشدار در صورتی که نماد نگهبان در میانهٔ یک قاعده رخ دهد --- این حالت ممکن است باعث خواندن دادهها فراتر از انتهای بافر، کرش کردن یا آسیب دیدن حافظه در تحلیلگر لغوی تولیدشده شود. این هشدار تنها زمانی اعمال میشود که روش نگهبان برای بررسی پایان ورودی استفاده شده باشد. در صورتی که از پیکربندی re2ocaml:sentinel استفاده شود، این مورد به یک خطا تبدیل میگردد.
- -Wundefined-syntax-config
- هشدار در
صورتی که
فایل نحو
مشخصشده
با گزینهٔ
--syntax فاقد
تعاریف
برخی از
پیکربندیها
باشد. این
ویژگی به
نگهداری
فایلهای
نحو
تعریفشده
توسط کاربر
کمک میکند:
اگر انتشار
جدیدی
پیکربندیهای
جدیدی
اضافه کند،
فایل نحو
قدیمی یک
هشدار صادر
خواهد کرد و
کاربر مطلع
میشود. اگر
برخی
پیکربندیها
استفاده
نمیشوند و
نیازی به
تعریف
ندارند،
باید به
صراحت روی
<undefined> تنظیم
شوند.
پشتیبانی از زبانهای گوناگون در re2ocaml بر پایه ایده syntax files (فایلهای نحو) استوار است. یک فایل نحو، یک فایل پیکربندی است که نحو زبان مقصد را تعریف میکند -- نه تمام زبان، بلکه بخش کوچکی از آن را که توسط کد تولیدشده استفاده میشود. فایلهای نحو re2ocaml را بسیار انعطافپذیر میسازند، اما نباید به عنوان جایگزینی برای
فایلهای نحو (Syntax files)
پشتیبانی از زبانهای مختلف در re2c بر پایهٔ ایدهٔ فایلهای نحو استوار است. یک فایل نحو، فایلی پیکربندی است که نحو زبان مقصد را تعریف میکند -- نه کل زبان، بلکه بخش کوچکی از آن که توسط کد تولیدشده استفاده میشود. فایلهای نحو انعطافپذیری بالایی به re2c میبخشند، اما نباید به عنوان جایگزینی برای پیکربندیهای re2c: استفاده شوند: هدف آنها تعریف نحو زبان مقصد است، نه سفارشیسازی یک تحلیلگر لغوی خاص. تمام زبانهای پشتیبانیشده دارای فایلهای نحو پیشفرضی هستند که بخشی از توزیع به شمار میروند (زیردایرکتوری include/syntax را ببینید)؛ آنها همچنین در فایل باینری re2ocaml تعبیه شدهاند. کاربران میتوانند یک فایل نحو سفارشی ارائه دهند که چند پیکربندی را برای یکی از زبانهای پشتیبانیشده بازنویسی میکند، یا میتوانند همهٔ پیکربندیها را مجدداً تعریف کنند (در این حالت باید از گزینهٔ --lang none استفاده شود). فایلهای نحو شامل پیکربندیهایی در چهار نوع مختلف هستند: فهرست ویژگیها، پیکربندیهای زبان، پیکربندیهای درونجایی و الگوهای کد.
فهرستهای ویژگی (Feature lists)
- supported_apis
- فهرستی از APIهای پشتیبانیشده با مقادیر ممکنِ simple، record و generic.
- supported_api_styles
- فهرستی از شیوههای API پشتیبانیشده با مقادیر ممکنِ functions و free-form.
- supported_code_models
- فهرستی از مدلهای کد پشتیبانیشده با مقادیر ممکنِ goto-label، loop-switch و recursive-functions.
- supported_targets
- فهرستی از هدفهای تولید کد با مقادیر ممکنِ code، dot و skeleton.
- supported_features
- فهرستی از ویژگیهای پشتیبانیشده با مقادیر ممکنِ nested-ifs، bitmaps، computed-gotos، case-ranges، monadic، unsafe، tags، captures و captvars.
پیکربندیهای زبان (Language configurations)
- semicolons
- غیرصفر در صورتی که زبان پس از دستورها از نقطهویرگول (semicolon) استفاده کند.
- backtick_quoted_strings
- غیرصفر در صورتی که زبان دارای رشتههای محصور در بکتیک (backtick) باشد.
- single_quoted_strings
- غیرصفر در صورتی که زبان دارای رشتههای محصور در نقلقول تکی (single-quote) باشد.
- indentation_sensitive
- غیرصفر در صورتی که زبان به تورفتگی حساس باشد.
- wrap_blocks_in_braces
- غیرصفر در صورتی که دستورهای مرکب باید درون آکولاد محصور شوند.
پیکربندیهای درونجایی (Inplace configurations)
الگوهای کد (Code templates)
code-template ::
name '=' code-exprs ';'
| CODE_TEMPLATE ';'
| '<undefined>' ';'
code-exprs ::
<EMPTY>
| code-exprs code-expr
code-expr ::
STRING
| VARIABLE
| optional
| list
optional ::
'(' CONDITIONAL '?' code-exprs ')'
| '(' CONDITIONAL '?' code-exprs ':' code-exprs ')'
list ::
'[' VARIABLE ':' code-exprs ']'
| '[' VARIABLE '{' NUMBER '}' ':' code-exprs ']'
| '[' VARIABLE '{' NUMBER ',' NUMBER '}' ':' code-exprs ']'
یک الگوی کد دنبالهای از لیترالهای رشتهای، متغیرها، مؤلفههای اختیاری و فهرستها، یا ارجاعی به یک الگوی کد دیگر، یا مقدار ویژهٔ <undefined> است. متغیرها جانگهدارندههایی هستند که در مرحلهٔ تولید کد جایگزین میشوند. متغیرهای فهرست خاص هستند: هنگام بسط الگوهای فهرست، re2ocaml عبارتهای سمت راست دونقطه را چند بار تکرار میکند، و هر بار رخدادهای متغیر فهرست را با مقداری مخصوص به همان تکرار جایگزین میسازد. فهرستها دارای کرانهای اختیاری هستند (مقادیر منفی از انتها شمرده میشوند، برای نمونه -1 به معنای آخرین مؤلفه است). نامهای شرطی با یک نقطه شروع میشوند. هم شرطها و هم متغیرها میتوانند محلی (مخصوص همان الگوی کد) یا سراسری (مجاز در تمام الگوهای کد) باشند. هنگامی که re2ocaml فایل نحو را میخواند، بررسی میکند که هر الگوی کد تنها از متغیرها و شرطهایی استفاده کند که در آن مجاز شناخته شدهاند.
برای نمونه، الگوی کد زیر ساختار 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
}
در ادامه فهرستی از تمام الگوهای کد پشتیبانیشده در re2ocaml به همراه متغیرها و شرطهای محلی آنها آورده شده است. توجه داشته باشید که یک تعریف مشخص میتواند از متغیرها و شرطهای محلی استفاده کند، اما الزامی به این کار ندارد. تمام الگوهای کد بدون استفاده باید روی <undefined> تنظیم شوند.
- code:var_global
- مشابه code:var_local، با این تفاوت که در top-level استفاده میشود.
- code:const_local
- تعریف یک مقدار ثابت محلی. متغیرهای پشتیبانیشده: type (نوع ثابت)، name (نام آن) و init (مقدار اولیه).
- code:const_global
- مشابه code:const_local، با این تفاوت که در top-level استفاده میشود.
- code:array_local
- تعریف یک آرایه (جدول) محلی. متغیرهای پشتیبانیشده: type (نوع عناصر آرایه)، name (نام آرایه)، size (اندازهٔ آن)، row (متغیری از نوع فهرست که خودش هیچ کدی تولید نمیکند، اما عبارت فهرست را به تعداد ردیفهای موجود در جدول بسط میدهد) و elem (متغیری از نوع فهرست که به تمام عناصر جدول در ردیف جاری بسط مییابد -- در نظر گرفته شده که در فهرست row تو در تو باشد). شرط پشتیبانیشده: .const (درست، اگر آرایه تغییرناپذیر باشد).
- code:array_global
- مشابه code:array_local، با این تفاوت که در top-level استفاده میشود.
- code:array_elem
- ارجاع به عنصری از یک آرایه (جدول). متغیرهای پشتیبانیشده: array (نام آرایه) و index (اندیس عنصر).
- code:enum
- تعریف یک شمارشگر یا enum (ممکن است با ساختار زبانی ویژهای برای شمارشها تعریف شود، یا صرفاً به صورت چند مقدار ثابت مستقل). متغیرهای پشتیبانیشده عبارتند از: type (نوع شمارشی تعریفشده توسط کاربر یا نوع ثابتها)، elem (متغیر فهرستی که به نام هر عضو بسط مییابد) و init (مقداردهندهٔ اولیه برای هر عضو). شرطها: .init (درست، اگر مقداردهی اولیه وجود داشته باشد).
- code:enum_elem
- عنصر شمارشی (عضوی از نوع شمارشی تعریفشده توسط کاربر یا نام یک مقدار ثابت، بسته به نحوهٔ تعریف code:enum). متغیرهای پشتیبانیشده عبارتند از: name (نام عنصر) و type (نوع آن).
- code:assign
- دستور انتساب. متغیرهای پشتیبانیشده عبارتند از: lhs (سمت چپ) و rhs (سمت راست).
- code:type_int
- نوع عدد صحیح علامتدار (signed).
- code:type_uint
- نوع عدد صحیح بدون علامت (unsigned).
- code:type_yybm
- نوع عناصر در جدول yybm.
- code:type_yytarget
- نوع عناصر در جدول yytarget.
- code:type_yyctable
- نوع عناصر در جدول yyctable.
- code:cmp_eq
- عملگر "برابر با" (equals).
- code:cmp_ne
- عملگر "نابرابر با" (not equals).
- code:cmp_lt
- عملگر "کوچکتر از" (less than).
- code:cmp_gt
- عملگر "بزرگتر از" (greater than).
- code:cmp_le
- عملگر "کوچکتر یا مساوی" (less or equal).
- code:cmp_ge
- عملگر "بزرگتر یا مساوی" (greater or equal).
- code:if_then_else
- دستور if-then-else با یک یا چند شاخه. متغیرهای پشتیبانیشده: branch (متغیری از نوع فهرست که خودش هیچ کدی تولید نمیکند، اما عبارت فهرست را به تعداد شاخهها بسط میدهد)، cond (شرط شاخهٔ جاری) و stmt (متغیری از نوع فهرست که به تمام دستورات شاخهٔ جاری بسط مییابد). شرطها: .cond (درست، اگر شاخهٔ جاری دارای شرط باشد)، .many (درست، اگر بیش از یک شاخه وجود داشته باشد).
- code:if_then_else_oneline
- حالت خاصی از code:if_then_else برای حالتی که تمام شاخهها دستورات تکخطی دارند. اگر مقدار آن <undefined> باشد، به جای آن از code:if_then_else استفاده میشود.
- code:switch
- یک دستور switch با یک یا چند حالت (case). متغیرهای پشتیبانیشده: expr (عبارت مورد بررسی در switch) و case (متغیری از نوع فهرست که به تمامی گروههای case به همراه بلوکهای کد مربوطهشان بسط مییابد).
- code:switch_cases
- گروهی از حالتهای switch که به یک بلوک کد واحد نگاشت میشوند. متغیرهای پشتیبانیشده عبارتند از: case (متغیری از نوع فهرست که به تمام حالتها در این گروه بسط مییابد) و stmt (متغیری از نوع فهرست که به تمام دستورات موجود در بلوک کد بسط مییابد).
- code:switch_cases_oneline
- حالت خاصی از code:switch_cases برای حالتی که بلوک کد شامل یک دستور تکخطی واحد باشد. اگر مقدار آن <undefined> باشد، به جای آن از code:switch_cases استفاده میشود.
- code:switch_case_range
- یک حالت 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 (تعریف تابع جاری).
- code:fingerprint
- اثر انگشت (fingerprint) در بالای فایل خروجی تولیدشده. متغیرهای پشتیبانیشده: ver (نسخهٔ re2ocaml مورد استفاده برای تولید این مورد) و date (تاریخ تولید).
- code:line_info
- قالب دستورات خط (line directives) (اگر روی <undefined> تنظیم شده باشد، هیچ دستوری تولید نمیشود). متغیرهای پشتیبانیشده: line (شمارهٔ خط) و file (نام فایل).
- code:abort
- دستوری که اجرای برنامه را متوقف (abort) میکند.
- code:yydebug
- دستور YYDEBUG، احتمالاً سفارشیشده برای رابطهای برنامهنویسی کاربردی (API) مختلف. متغیرهای پشتیبانیشده: YYDEBUG، yyrecord، yych (نگاشت به پیکربندیهای متناظر در re2c:)، state (شمارهٔ وضعیت DFA)..TP code:yypeek دستور YYPEEK، احتمالاً برای APIهای گوناگون سفارشیشده است. متغیرهای پشتیبانیشده: YYPEEK، YYCTYPE، YYINPUT، YYCURSOR، yyrecord، yych (به پیکربندیهای متناظر re2c: نگاشت میشوند). شرطها: .cast (اگر re2c:yych:conversion روی مقداری غیر از صفر تنظیم شده باشد، درست است).
- code:yyskip
- دستور YYSKIP، احتمالاً برای APIهای گوناگون سفارشیشده است. متغیرهای پشتیبانیشده: YYSKIP، YYCURSOR، yyrecord (به پیکربندیهای متناظر re2c: نگاشت میشوند).
- code:yybackup
- دستور YYBACKUP، احتمالاً برای APIهای گوناگون سفارشیشده است. متغیرهای پشتیبانیشده: YYBACKUP، YYCURSOR، YYMARKER، yyrecord (به پیکربندیهای متناظر re2c: نگاشت میشوند).
- code:yybackupctx
- دستور YYBACKUPCTX، احتمالاً برای APIهای گوناگون سفارشیشده است. متغیرهای پشتیبانیشده: YYBACKUPCTX، YYCURSOR، YYCTXMARKER، yyrecord (به پیکربندیهای متناظر re2c: نگاشت میشوند).
- code:yyskip_yypeek
- دستور ترکیبی code:yyskip و code:yypeek (پیشفرض code:yyskip و به دنبال آن code:yypeek است).
- code:yypeek_yyskip
- دستور ترکیبی code:yypeek و code:yyskip (پیشفرض code:yypeek و به دنبال آن code:yyskip است).
- code:yyskip_yybackup
- دستور ترکیبی code:yyskip و code:yybackup (پیشفرض code:yyskip و به دنبال آن code:yybackup است).
- code:yybackup_yyskip
- دستور ترکیبی code:yybackup و code:yyskip (پیشفرض code:yybackup و به دنبال آن code:yyskip است).
- code:yybackup_yypeek
- دستور ترکیبی code:yybackup و code:yypeek (پیشفرض code:yybackup و به دنبال آن code:yypeek است).
- code:yyskip_yybackup_yypeek
- دستور ترکیبی code:yyskip، code:yybackup و code:yypeek (پیشفرض ``code:yyskip`` و به دنبال آن code:yybackup و سپس code:yypeek است).
- code:yybackup_yypeek_yyskip
- دستور ترکیبی code:yybackup، code:yypeek و code:yyskip (پیشفرض ``code:yybackup`` و به دنبال آن code:yypeek و سپس code:yyskip است).
- code:yyrestore
- دستور YYRESTORE، احتمالاً برای APIهای گوناگون سفارشیشده است. متغیرهای پشتیبانیشده: YYRESTORE، YYCURSOR، YYMARKER، yyrecord (به پیکربندیهای متناظر re2c: نگاشت میشوند).
- code:yyrestorectx
- دستور YYRESTORECTX، احتمالاً برای APIهای گوناگون سفارشیشده است. متغیرهای پشتیبانیشده: YYRESTORECTX، YYCURSOR، YYCTXMARKER، yyrecord (به پیکربندیهای متناظر re2c: نگاشت میشوند).
- code:yyrestoretag
- دستور YYRESTORETAG، احتمالاً برای APIهای گوناگون سفارشیشده است. متغیرهای پشتیبانیشده: YYRESTORETAG، YYCURSOR، yyrecord (به پیکربندیهای متناظر re2c: نگاشت میشوند)، tag (نام متغیر برچسب استفادهشده برای بازیابی موقعیت).
- code:yyshift
- دستور YYSHIFT، احتمالاً برای APIهای گوناگون سفارشیشده است. متغیرهای پشتیبانیشده: YYSHIFT، YYCURSOR، yyrecord (به پیکربندیهای متناظر re2c: نگاشت میشوند)، offset (تعداد واحدهای کد برای جابهجایی موقعیت فعلی).
- code:yyshiftstag
- دستور YYSHIFTSTAG، احتمالاً برای APIهای گوناگون سفارشیشده است. متغیرهای پشتیبانیشده: YYSHIFTSTAG، yyrecord، negative (به پیکربندیهای متناظر re2c: نگاشت میشوند)، tag (متغیر برچسبی که باید جابهجا شود)، offset (تعداد واحدهای کد برای جابهجایی). شرطها: .nested (اگر این یک برچسب تو در تو باشد درست است -- در این حالت ممکن است مقدار آن برابر با re2c:tags:negative باشد که نباید جابهجا شود).
- code:yyshiftmtag
- دستور YYSHIFTMTAG، احتمالاً برای APIهای گوناگون سفارشیشده است. متغیرهای پشتیبانیشده: YYSHIFTMTAG (به پیکربندی متناظر re2c: نگاشت میشود)، tag (متغیر برچسبی که باید جابهجا شود)، offset (تعداد واحدهای کد برای جابهجایی).
- code:yystagp
- دستور YYSTAGP، احتمالاً برای APIهای گوناگون سفارشیشده است. متغیرهای پشتیبانیشده: YYSTAGP، YYCURSOR، yyrecord (به پیکربندیهای متناظر re2c: نگاشت میشوند)، tag (متغیر برچسبی که باید بهروزرسانی شود).
- code:yymtagp
- دستور YYMTAGP، احتمالاً برای APIهای گوناگون سفارشیشده است. متغیرهای پشتیبانیشده: YYMTAGP (به پیکربندی متناظر re2c: نگاشت میشود)، tag (متغیر برچسبی که باید بهروزرسانی شود).
- code:yystagn
- دستور YYSTAGN، احتمالاً برای APIهای گوناگون سفارشیشده است. متغیرهای پشتیبانیشده: YYSTAGN، negative، yyrecord (به پیکربندیهای متناظر re2c: نگاشت میشوند)، tag (متغیر برچسبی که باید بهروزرسانی شود).
- code:yymtagn
- دستور YYMTAGN، احتمالاً برای APIهای گوناگون سفارشیشده است. متغیرهای پشتیبانیشده: YYMTAGN (به پیکربندی متناظر re2c: نگاشت میشود)، tag (متغیر برچسبی که باید بهروزرسانی شود).
- code:yycopystag
- دستور YYCOPYSTAG، احتمالاً برای APIهای گوناگون سفارشیشده است. متغیرهای پشتیبانیشده: YYCOPYSTAG، yyrecord (به پیکربندیهای متناظر re2c: نگاشت میشوند)، lhs، rhs (متغیرهای برچسب سمت چپ و راست در عملیات کپی).
- code:yycopymtag
- دستور YYCOPYMTAG، احتمالاً برای APIهای گوناگون سفارشیشده است. متغیرهای پشتیبانیشده: YYCOPYMTAG، yyrecord (به پیکربندیهای متناظر re2c: نگاشت میشوند)، lhs، rhs (متغیرهای برچسب سمت چپ و راست در عملیات کپی).
- code:yygetaccept
- دستور YYGETACCEPT، احتمالاً برای APIهای گوناگون سفارشیشده است. متغیرهای پشتیبانیشده: YYGETACCEPT، yyrecord (به پیکربندیهای متناظر re2c: نگاشت میشوند)، var (به پیکربندی re2c:yyaccept نگاشت میشود).
- code:yysetaccept
- دستور YYSETACCEPT، احتمالاً برای APIهای گوناگون سفارشیشده است. متغیرهای پشتیبانیشده: YYSETACCEPT، yyrecord (به پیکربندیهای متناظر re2c: نگاشت میشوند)، var (به پیکربندی re2c:yyaccept نگاشت میشود) و val (مقدار عددی قاعده پذیرفتهشده).
- code:yygetcond
- دستور YYGETCOND، احتمالاً برای APIهای گوناگون سفارشیشده است. متغیرهای پشتیبانیشده: YYGETCOND، yyrecord (به پیکربندیهای متناظر re2c: نگاشت میشوند)، var (به پیکربندی re2c:yycond نگاشت میشود).
- code:yysetcond
- دستور YYSETCOND، احتمالاً برای APIهای گوناگون سفارشیشده است. متغیرهای پشتیبانیشده: YYSETCOND، yyrecord (به پیکربندیهای متناظر re2c: نگاشت میشوند)، var (به پیکربندی re2c:yycond نگاشت میشود) و val (شناسه عددی شرط).
- code:yygetstate
- دستور YYGETSTATE، احتمالاً برای APIهای گوناگون سفارشیشده است. متغیرهای پشتیبانیشده: YYGETSTATE، yyrecord (به پیکربندیهای متناظر re2c: نگاشت میشوند)، var (به پیکربندی re2c:yystate نگاشت میشود).
- code:yysetstate
- دستور YYSETSTATE، احتمالاً برای APIهای گوناگون سفارشیشده است. متغیرهای پشتیبانیشده: YYSETSTATE، yyrecord (به پیکربندیهای متناظر re2c: نگاشت میشوند)، var (به پیکربندی re2c:yystate نگاشت میشود) و val (شماره وضعیت).
- code:yylessthan
- دستور YYLESSTHAN، احتمالاً برای APIهای گوناگون سفارشیشده است. متغیرهای پشتیبانیشده: YYLESSTHAN، YYCURSOR، YYLIMIT، yyrecord (به پیکربندیهای متناظر re2c: نگاشت میشوند)، need (تعداد واحدهای کد برای بررسی در برابر آن). شرط: .many (اگر need بیش از یک باشد درست است).
- code:yyend
- عبارت YYEND، احتمالاً برای APIهای گوناگون سفارشیشده است. متغیرهای پشتیبانیشده: YYEND، YYCURSOR، YYLIMIT.
- code:yybm_filter
- شرطی که برای فیلتر کردن مقادیر yych که توسط جدول yybm پوشش داده نشدهاند استفاده میشود (همراه با گزینه --bitmaps به کار میرود). متغیر پشتیبانیشده: yych (به پیکربندی re2c:yych نگاشت میشود).
- code:yybm_match
- قالب بررسی جدول yybm (تولیدشده با گزینه --bitmaps). متغیرهای پشتیبانیشده: yybm، yych (به پیکربندیهای متناظر re2c: نگاشت میشوند)، offset (آفست در جدول yybm که باید به yych اضافه شود) و mask (ماسک بیتی که باید روی مدخل جدول اعمال شود تا مقدار بولی مورد نیاز برای بررسی به دست آید).
- code:yytarget_filter
- شرطی که برای فیلتر کردن مقادیر yych که توسط جدول yytarget پوشش داده نشدهاند استفاده میشود (همراه با گزینه --computed-gotos به کار میرود). متغیر پشتیبانیشده: yych (به پیکربندی re2c:yych نگاشت میشود).
در اینجا فهرستی از تمام متغیرهای سراسری مجاز در فایلهای نحو (syntax files) آمده است:
- nl
- یک خط جدید (newline).
- indent
- متغیری که هیچ کدی تولید نمیکند، اما اثر جانبی افزایش سطح تورفتگی را دارد..TP dedent متغیری که هیچ کدی تولید نمیکند، اما دارای اثر جانبیِ کاهش سطح دندانهگذاری است.
- topindent
- رشته دندانهگذاری برای دستور فعلی. سطح دندانهگذاری توسط تولیدکننده کد ردیابی شده و بهطور خودکار بهروزرسانی میشود.
در اینجا فهرستی از تمام شرطهای سراسریِ مجاز در فایلهای نحو (syntax) آورده شده است:
- .api.simple
- درست است اگر از API ساده استفاده شود (--api simple یا re2c:api = simple).
- .api.generic
- درست است اگر از API عمومی استفاده شود (--api generic یا re2c:api = generic).
- .api.record
- درست است اگر از API رکوردی استفاده شود (--api record یا re2c:api = record).
- .api_style.functions
- درست است اگر سبک API تابعگونه استفاده شود (re2c:api-style = functions).
- .api_style.freeform
- درست است اگر سبک API آزاد (free-form) استفاده شود (re2c:api-style = free-form).
- .case_ranges
- درست است اگر ویژگی بازههای case فعال باشد (--case-ranges یا re2c:case-ranges = 1).
- .cgoto.relative
- درست است اگر حالت نسبی دستور goto محاسباتی استفاده شود (--computed-gotos-relative یا re2c:cgoto:relative = 1).
- .code_model.goto_label
- درست است اگر مدل کد مبتنی بر goto/label استفاده شود (--goto-label).
- .code_model.loop_switch
- درست است اگر مدل کد مبتنی بر loop/switch استفاده شود (--loop-switch).
- .code_model.recursive_functions
- درست است اگر مدل کد مبتنی بر توابع بازگشتی استفاده شود (--recursive-function).
- .date
- درست است اگر اثرانگشت (fingerprint) تولیدشده باید حاوی تاریخ تولید باشد.
- .loop_label
- درست است اگر حلقههای تولیدشده توسط re2ocaml باید دارای برچسب باشند (re2c:label:yyloop روی یک رشته غیرخالی تنظیم شده باشد).
- .monadic
- درست است اگر کد تولیدشده باید بهصورت مونادیک باشد (re2c:monadic = 1). این مورد تنها برای زبانهای تابعی محض مرتبط است.
- .start_conditions
- درست است اگر شرایط شروع (start conditions) فعال باشند (--start-conditions).
- .storable_state
- درست است اگر وضعیت قابل ذخیرهسازی فعال باشد (--storable-state).
- .unsafe
- درست است اگر re2ocaml برای تولید کد سریعتر باید از بلوکهای "unsafe" استفاده کند (--unsafe، re2c:unsafe = 1). این مورد تنها برای زبانهایی کاربرد دارد که دارای ویژگی "unsafe" هستند.
- .version
- درست است اگر اثرانگشت (fingerprint) تولیدشده باید حاوی نسخه re2ocaml باشد.
- .yyfill.enable
- درست است اگر YYFILL فعال باشد (re2c:yyfill:enable = 1).
- .yyfn.throw
- درست است اگر پیکربندی re2c:yyfn:throw روی یک رشته غیرخالی تعریف شده باشد.
مدیریت پایان ورودی (HANDLING THE END OF INPUT)
یکی از چالشهای اصلی برای تحلیلگر لغوی، دانستن زمان توقف است. چندین شرط برای پایان کار وجود دارد:
- تحلیلگر لغوی ممکن است با یک قاعده (شامل قاعده پیشفرض *) تطبیق یابد و به یک وضعیت نهایی برسد
- تحلیلگر لغوی ممکن است در تطبیق با هر قاعدهای شکست بخورد و به وضعیت پیشفرض برسد
- تحلیلگر لغوی ممکن است به پایان ورودی برسد
دو شرط اول کار تحلیلگر لغوی را بهطور طبیعی خاتمه میدهند: تحلیلگر به وضعیتی بدون گذارهای خروجی میرسد و تطبیق بهطور خودکار متوقف میشود. اما شرط سوم، یعنی پایان ورودی، متفاوت است: این وضعیت ممکن است در هر حالتی رخ دهد و تحلیلگر باید بتواند آن را مدیریت کند. بررسی پایان ورودی، جریان عادی کار تحلیلگر لغوی را قطع کرده و شاخههای شرطی به برنامه تولیدشده اضافه میکند؛ از این رو لازم است تعداد این بررسیها به حداقل برسد. re2ocaml از چند روش مختلف برای مدیریت پایان ورودی پشتیبانی میکند. انتخاب روش به پیچیدگی عبارتهای منظم، نیاز به بافرسازی، ملاحظات کارایی و عوامل دیگر بستگی دارد. در ادامه فهرستی از این روشها آورده شده است:
- نگهبان (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 پوشش داده شود. این کار تعداد بررسیها را کاهش میدهد، اما مشکلی در مورد لکسمهای کوتاه در پایان ورودی به وجود میآید، زیرا این بررسی به نویسههای کافی برای پوشش طولانیترین لکسم نیاز دارد. این مشکل را میتوان با لایهگذاری (padding) ورودی با چند نویسه ساختگی که پسوند لکسم معتبری تشکیل نمیدهند (به طوری که تحلیلگر نتواند با آنها تطبیق یابد) برطرف کرد. طول این پدینگ باید YYMAXFILL باشد که با یک بلوک max تولید میشود. اگر ورودی کافی نباشد، تحلیلگر تابع YYFILL را فراخوانی میکند که باید حداقل تعداد نویسه مورد نیاز را تأمین کند یا بازنگردد. این روش در صورتی استفاده میشود که YYFILL فعال باشد و re2c:eof برابر -1 باشد (این پیکربندی پیشفرض است).
- بررسیهای سفارشی (Custom checks). رابط عمومی (Generic API) اجازه میدهد تا عملیات پایهای مانند خواندن یک نویسه بازنویسی شوند، که این امر امکان گنجاندن بررسیهای پایان ورودی را به عنوان بخشی از آنها فراهم میسازد. این رویکرد مستعد خطا است و باید با احتیاط استفاده شود. برای استفاده از یک روش سفارشی، API عمومی را با --api custom یا re2c:api = custom; فعال کرده و بررسیهای مرز پیشفرض را با re2c:yyfill:enable = 0; یا re2c:yyfill:check = 0; غیرفعال نمایید.
زیربخشهای زیر شامل مثالی از هر روش هستند.
نگهبان (Sentinel)
این مثال از یک نویسه نگهبان برای مدیریت پایان ورودی استفاده میکند. برنامه کلمات جداشده با فاصله را در یک رشته خاتمهیافته با null شمارش میکند. نگهبان null است: این آخرین نویسه هر رشته ورودی است و در میانه یک لکسم توسط هیچیک از قواعد مجاز نیست (بهویژه، در بازههای نویسهای که ممکن است نادیده گرفته شوند، گنجانده نشده است). اگر یک null در میانه رشته رخ دهد، این یک خطای نحوی است و تحلیلگر لغوی با قاعده پیشفرض * مطابقت پیدا میکند، اما فراتر از پایان ورودی نمیخواند یا دچار کرش نمیشود (از هشدار %-Wsentinel-in-midrule و پیکربندی re2c:sentinel برای اعتبارسنجی آن استفاده کنید). پیکربندی re2c:yyfill:enable = 0; از تولید بررسیهای مرزی و فراخوانیهای YYFILL جلوگیری میکند.
(* re2ocaml -o *)
open String
type state = {
yyinput: string;
mutable yycursor: int;
}
(* expect a null-terminated string *)
%{
re2c:YYFN = ["lex;int", "yyrecord;state", "count;int"];
re2c:yyfill:enable = 0;
* { -1 }
[\x00] { count }
[a-z]+ { lex yyrecord (count + 1) }
[ ]+ { lex yyrecord count }
%}
let test(yyinput, count) =
let st = {yyinput = yyinput; yycursor = 0}
in if not (lex st 0 = count) then raise (Failure "error")
let main () =
test("", 0);
test("one two three", 3);
test("f0ur", -1)
let _ = main ()
نگهبان با بررسیهای مرزی (Sentinel with bounds checks)
این مثال از نگهبان همراه با بررسی مرزها برای مدیریت پایان ورودی استفاده میکند (این روش در نسخه 1.2 افزوده شد). برنامه رشتههای درون گیومه تکی جداشده با فاصله را شمارش میکند. نویسه نگهبان null است که با پیکربندی re2c:eof = 0; مشخص میشود. همانند روش %نگهبان، null آخرین نویسه هر رشته ورودی است، اما در میانه یک قاعده مجاز است (برای مثال، 'aaa\0aa'\0 ورودی معتبر است، اما 'aaa\0 یک خطای نحوی است). بررسیهای مرزی در هر وضعیتی که با یک نویسه ورودی مطابقت دارد تولید میشوند، اما به شاخهای که null را پردازش میکند محدود هستند. بررسیهای مرزی در API عمومی به شکل YYLIMIT <= YYCURSOR یا YYLESSTHAN(1) هستند. اگر شرط بررسی درست باشد، تحلیلگر به پایان ورودی رسیده است و باید متوقف شود (YYFILL با re2c:yyfill:enable = 0; غیرفعال شده است چرا که ورودی در یک بافر جا میشود، برای مثالی که از YYFILL استفاده میکند بخش %YYFILL با نگهبان را ببینید). رسیدن به پایان ورودی سه احتمال را ایجاد میکند: اگر تحلیلگر در وضعیت اولیه باشد، با قاعده پایان ورودی $ مطابقت پیدا میکند؛ در غیر این صورت ممکن است به یک قاعده منطبقشده قبلی (از جمله قاعده پیشفرض *) عقبگرد کند یا به یک وضعیت پیشفرض برود، که موجب هشدار %-Wundefined-control-flow میشود.
(* re2ocaml -o *)
open String
type state = {
yyinput: string;
mutable yycursor: int;
mutable yymarker: int;
yylimit: int;
}
(* expect a null-terminated string *)
%{
re2c:YYFN = ["lex;int", "yyrecord;state", "count;int"];
re2c:yyfill:enable = 0;
re2c:eof = 0;
str = ['] ([^'\\] | [\\][^])* ['];
* { -1 }
$ { count }
str { lex yyrecord (count + 1) }
[ ]+ { lex yyrecord count }
%}
let test(str, count) =
let st = {
yyinput = str;
yycursor = 0;
yymarker = 0;
yylimit = length str - 1; (* terminating null not included *)
}
in if not (lex st 0 = count) then raise (Failure "error")
let main () =
test("", 0);
test("'qu\x00tes' 'are' 'fine: \\'' ", 3);
test("'unterminated\\'", -1)
let _ = main ()
بررسیهای مرزی با پدینگ (Bounds checks with padding)
این مثال از بررسی مرزها با پدینگ (لایهگذاری) برای مدیریت پایان ورودی استفاده میکند (این روش به طور پیشفرض فعال است). برنامه رشتههای درون گیومه تکی جداشده با فاصله را شمارش میکند. یک پدینگ از نویسههای null به تعداد YYMAXFILL در انتهای ورودی الحاق میشود، که در آن مقدار YYMAXFILL به طور خودکار با یک بلوک max تولید میشود. استفاده از null برای پدینگ الزامی نیست -- هر نویسهای تا زمانی که پسوند لکسم معتبری را تشکیل ندهد میتواند استفاده شود (در این مثال پدینگ نباید شامل گیومه تکی باشد، زیرا ممکن است با پسوند یک رشته درون گیومه تکی اشتباه گرفته شود). یک قاعده «توقف» وجود دارد که با اولین نویسه پدینگ (null) تطابق مییابد و تحلیلگر لغوی را متوقف میکند (توجه داشته باشید که بررسی میکند آیا null در آغاز پدینگ است یا خیر، در غیر این صورت یک خطای نحوی است). بررسیهای مرزی تنها در وضعیتهای خاصی که توسط مؤلفههای قویاً همبند اتوماتون مشخص میشوند تولید میگردند. بررسیها در API عمومی به شکل (YYLIMIT - YYCURSOR) < n یا YYLESSTHAN(n) هستند، که در آن n حداقل تعداد نویسههای مورد نیاز برای پیشروی تحلیلگر است (همچنین به این معناست که بررسی مرز بعدی حداکثر در n نویسه آینده رخ خواهد داد). اگر شرط بررسی درست باشد، تحلیلگر به پایان ورودی رسیده است و YYFILL(n) را فراخوانی میکند که باید یا حداقل n نویسه ورودی فراهم کند یا بازنگردد. در این مثال YYFILL همیشه با شکست مواجه میشود و تحلیلگر را با خطا خاتمه میدهد (که اشکالی ندارد چرا که ورودی در یک بافر جا میشود). برای مثالی که بافر ورودی را با YYFILL مجدداً پر میکند بخش %YYFILL با پدینگ را ببینید.
(* re2ocaml -o *)
open String
exception Fill
type state = {
yyinput: string;
mutable yycursor: int;
yylimit: int;
}
%{max %}
%{
re2c:YYFN = ["lex;int", "yyrecord;state", "count;int"];
re2c:YYFILL = "raise Fill;";
str = ['] ([^'\\] | [\\][^])* ['];
[\x00] {
(* check that it is the sentinel, not some unexpected null *)
if yyrecord.yycursor = length yyrecord.yyinput - yymaxfill + 1 then count else -1
}
str { lex yyrecord (count + 1) }
[ ]+ { lex yyrecord count }
* { -1 }
%}
let test(str, count) =
let buf = cat str (make yymaxfill '\x00') in
let st = {yyinput = buf; yycursor = 0; yylimit = length buf} in
let result = try lex st 0 with Fill -> -1 in
if not (result = count) then raise (Failure "error")
let main () =
test("", 0);
test("'unterminated\\'", -1);
test("'qu\x00tes' 'are' 'fine: \\'' ", 3);
test("'unexpected \x00 null", -1)
let _ = main ()
بررسیهای سفارشی (Custom checks)
این مثال از یک روش سفارشی مدیریت پایان ورودی مبتنی بر API عمومی استفاده میکند. برنامه رشتههای درون گیومه تکی جداشده با فاصله را شمارش میکند. این مثال همانند مثال %نگهبان است، با این تفاوت که ورودی با null خاتمه نیافته است. برای جبران عدم وجود نویسه نگهبان در انتهای ورودی، YYPEEK بازتعریف شده است تا پیش از خواندن نویسه ورودی بعدی یک بررسی مرز انجام دهد. این روش به دلیل انجام بررسیهای مکرر ناکارآمد است. اگر شرط بررسی ناموفق باشد، YYPEEK نویسه واقعی را برمیگرداند؛ در غیر این صورت یک نویسه نگهبان ساختگی بازمیگرداند.
(* re2ocaml -o *)
type state = {
str: string;
mutable cur: int;
lim: int;
}
(* expect a string without terminating null *)
%{
re2c:api = generic;
re2c:YYFN = ["lex;int", "st;state", "count;int"];
re2c:YYPEEK = "if st.cur < st.lim then st.str.[st.cur] else '\\x00'";
re2c:YYSKIP = "st.cur <- st.cur + 1;";
re2c:yyfill:enable = 0;
* { -1 }
[\x00] { count }
[a-z]+ { lex st (count + 1) }
[ ]+ { lex st count }
%}
let test(str, count) =
let st = {str = str; cur = 0; lim = String.length str}
in if not (lex st 0 = count) then raise (Failure "error")
let main () =
test("", 0);
test("one two three", 3);
test("f0ur", -1)
let _ = main ()
پر کردن مجدد بافر (BUFFER REFILLING)
نیاز به بافرسازی زمانی پیش میآید که ورودی نتواند بهیکباره در حافظه نگاشت شود: یا بیش از حد بزرگ است، یا به صورت جریانی (مانند خواندن از یک سوکت) دریافت میشود. روش معمول در این موارد، تخصیص یک بافر حافظه با اندازه ثابت و پردازش ورودی در تکههایی است که در این بافر جا میشوند. هنگامی که تکه فعلی پردازش شد، به بیرون منتقل شده و دادههای جدید وارد میشوند. در عمل این موضوع تا حدی پیچیدهتر است، زیرا وضعیت تحلیلگر لغوی تنها از یک موقعیت ورودی تشکیل نشده، بلکه مجموعهای از موقعیتهای مرتبط با یکدیگر است:
- مکاننما (cursor): نویسه بعدی ورودی که باید خوانده شود (YYCURSOR در API اشارهگر C یا YYSKIP/YYPEEK در API عمومی)
- حد (limit): موقعیت بعد از آخرین نویسه موجود ورودی (YYLIMIT در API اشارهگر C، که بهطور ضمنی توسط YYLESSTHAN در API عمومی مدیریت میشود)
- نشانگر (marker): موقعیت جدیدترین تطابق، در صورت وجود (YYMARKER در API پیشفرض یا YYBACKUP/YYRESTORE در API عمومی)
- توکن (token): آغاز لکسم جاری (در API مربوط به re2ocaml ضمنی است، زیرا برای عملکرد عادی تحلیلگر نیازی به آن نیست و میتواند توسط کاربر تعریف و بهروزرسانی شود)
- نشانگر بافتار (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 تا حدودی سادهتر از حالت بررسی مرز همراه با پدینگ است. همچنین توجه داشته باشید که اگر گزینه -f --storable-state استفاده شود، YYFILL معنای متفاوتی خواهد داشت (که در بخش مربوط به وضعیت قابل ذخیرهسازی شرح داده شده است).
YYFILL با نگهبان (YYFILL with sentinel)
اگر از قاعده EOF استفاده شود، YYFILL یک اولیه شبهتابع است که هیچ آرگومانی نمیپذیرد و مقداری را بازمیگرداند که با صفر مقایسه میشود. فراخوانی YYFILL با شرط YYLIMIT <= YYCURSOR در API اشارهگر C و YYLESSTHAN() در API عمومی برانگیخته میشود. یک مقدار بازگشتی غیرصفر به این معنی است که YYFILL ناموفق بوده است. یک فراخوانی موفق YYFILL باید حداقل یک نویسه را فراهم کرده و موقعیتهای ورودی را بر این اساس تنظیم کند. Limit همیشه باید روی یک موقعیت بعد از آخرین موقعیت ورودی در بافر تنظیم شود، و نویسه موجود در موقعیت limit باید نماد نگهبان مشخصشده توسط پیکربندی re2c:eof باشد. تصاویر زیر مکانهای نسبی موقعیتهای ورودی را در بافر قبل و بعد از فراخوانی YYFILL نشان میدهند (نماد نگهبان با # مشخص شده است، و تصویر دوم حالتی را نشان میدهد که ورودی کافی برای پر کردن کل بافر وجود ندارد).
<-- shift -->
>-A------------B---------C-------------D#-----------E->
buffer token marker limit,
cursor
>-A------------B---------C-------------D------------E#->
buffer, marker cursor limit
token
<-- shift -->
>-A------------B---------C-------------D#--E (EOF)
buffer token marker limit,
cursor
>-A------------B---------C-------------D---E#........
buffer, marker cursor limit
token
Here is an example of a program that reads input file input.txt in chunks of 4096 bytes and uses EOF rule.
(* re2ocaml $INPUT -o $OUTPUT *)
open Bytes
let bufsize = 4096
type state = {
file: in_channel;
yyinput: bytes;
mutable yycursor: int;
mutable yymarker: int;
mutable yylimit: int;
mutable token: int;
mutable eof: bool;
}
type status = Ok | Eof | LongLexeme
let fill(st: state) : status =
if st.eof then Eof else
(* Error: lexeme too long. In real life could reallocate a larger buffer. *)
if st.token < 1 then LongLexeme else (
(* Shift buffer contents (discard everything up to the current token). *)
blit st.yyinput st.token st.yyinput 0 (st.yylimit - st.token);
st.yycursor <- st.yycursor - st.token;
st.yymarker <- st.yymarker - st.token;
st.yylimit <- st.yylimit - st.token;
st.token <- 0;
(* Fill free space at the end of buffer with new data from file. *)
let n = input st.file st.yyinput st.yylimit (bufsize - st.yylimit - 1) in (* -1 for sentinel *)
st.yylimit <- st.yylimit + n;
if n = 0 then
st.eof <- true; (* end of file *)
set st.yyinput st.yylimit '\x00'; (* append sentinel *)
Ok)
%{
re2c:YYFN = ["lex;int", "yyrecord;state", "count;int"];
re2c:YYFILL = "fill yyrecord = Ok";
re2c:eof = 0;
str = ['] ([^'\\] | [\\][^])* ['];
* { -1 }
$ { count }
str { lex_loop yyrecord (count + 1) }
[ ]+ { lex_loop yyrecord count }
%}
and lex_loop st count =
st.token <- st.yycursor;
lex st count
let main () =
let fname = "input" in
(* Prepare input file. *)
Out_channel.with_open_bin fname
(fun oc -> for i = 1 to bufsize do
output_string oc "'qu\x00tes' 'are' 'fine: \\'' "
done);
(* Run lexer on the prepared file. *)
In_channel.with_open_bin fname
(fun ic ->
let yylimit = bufsize - 1 in
let st = {
file = ic;
yyinput = create bufsize;
yycursor = yylimit;
yymarker = yylimit;
yylimit = yylimit;
token = yylimit;
eof = false;
} in if not (lex_loop st 0 = 3 * bufsize) then
raise (Failure "error"));
(* Cleanup. *)
Sys.remove fname
let _ = main ()
YYFILL with padding
In the default case (when EOF rule is not used) YYFILL is a function-like primitive that accepts a single argument and does not return any value. YYFILL invocation is triggered by condition (YYLIMIT - YYCURSOR) < n in C pointer API and YYLESSTHAN(n) in generic API. The argument passed to YYFILL is the minimal number of characters that must be supplied. If it fails to do so, YYFILL must not return to the lexer (for that reason it is best implemented as a macro that returns from the calling function on failure). In case of a successful YYFILL invocation the limit position must be set either to one after the last input position in buffer, or to the end of YYMAXFILL padding (in case YYFILL has successfully read at least n characters, but not enough to fill the entire buffer). The pictures below show the relative locations of input positions in buffer before and after YYFILL invocation (YYMAXFILL padding on the second picture is marked with # symbols).
<-- 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 ->
Here is an example of a program that reads input file input.txt in chunks of 4096 bytes and uses bounds-checking with padding.
(* re2ocaml $INPUT -o $OUTPUT *)
open Bytes
%{max %}
let bufsize = 4096
exception Fill
type state = {
file: in_channel;
yyinput: bytes;
mutable yycursor: int;
mutable yymarker: int;
mutable yylimit: int;
mutable token: int;
mutable eof: bool;
}
type status = Ok | Eof | LongLexeme
let fill (st: state) (need: int) : status =
if st.eof then Eof else
(* Error: lexeme too long. In real life could reallocate a larger buffer. *)
if st.token < need then LongLexeme else (
(* Shift buffer contents (discard everything up to the current token). *)
blit st.yyinput st.token st.yyinput 0 (st.yylimit - st.token);
st.yycursor <- st.yycursor - st.token;
st.yymarker <- st.yymarker - st.token;
st.yylimit <- st.yylimit - st.token;
st.token <- 0;
(* Fill free space at the end of buffer with new data from file. *)
let n = input st.file st.yyinput st.yylimit (bufsize - st.yylimit - 1) in (* -1 for sentinel *)
st.yylimit <- st.yylimit + n;
(* If read zero characters, this is end of input => add zero padding
so that the lexer can access characters at the end of buffer. *)
if n = 0 then
st.eof <- true; (* end of file *)
for i = 0 to (yymaxfill - 1) do
set st.yyinput (st.yylimit + i) '\x00';
st.yylimit <- st.yylimit + yymaxfill
done;
Ok)
%{
re2c:YYFN = ["lex;int", "yyrecord;state", "count;int"];
re2c:YYFILL = "if not (fill yyrecord @@ = Ok) then raise Fill;";
str = ['] ([^'\\] | [\\][^])* ['];
[\x00] {
(* check that it is the sentinel, not some unexpected null *)
if yyrecord.token = yyrecord.yylimit - yymaxfill then count else -1
}
str { lex_loop yyrecord (count + 1) }
[ ]+ { lex_loop yyrecord count }
* { -1 }
%}
and lex_loop st count =
st.token <- st.yycursor;
try lex st count with Fill -> -1
let main () =
let fname = "input" in
(* Prepare input file. *)
Out_channel.with_open_bin fname
(fun oc -> for i = 1 to bufsize do
output_string oc "'qu\x00tes' 'are' 'fine: \\'' "
done);
(* Run lexer on the prepared file. *)
In_channel.with_open_bin fname
(fun ic ->
let yylimit = bufsize - yymaxfill in
let st = {
file = ic;
yyinput = create bufsize;
yycursor = yylimit;
yymarker = yylimit;
yylimit = yylimit;
token = yylimit;
eof = false;
} in if not (lex_loop st 0 = 3 * bufsize) then
raise (Failure "error"));
(* Cleanup. *)
Sys.remove fname
let _ = main ()
در ادامه مثالی از برنامهای آمده است که فایل ورودی input.txt را در تکههای ۴۰۹۶ بایتی میخواند و از قاعده EOF استفاده میکند.
YYFILL با پدینگ (YYFILL with padding)
در حالت
پیشفرض
(زمانی که از
قاعده EOF
استفاده
نمیشود)،
YYFILL یک اولیه
شبهتابع
است که یک
آرگومان
منفرد
میپذیرد و
هیچ مقداری
بازنمیگرداند.
فراخوانی
YYFILL با شرط (YYLIMIT -
YYCURSOR) < n در API
اشارهگر C و
YYLESSTHAN(n) در API
عمومی
برانگیخته
میشود.
آرگومان
ارسالی به
YYFILL حداقل
تعداد
نویسههایی
است که باید
تأمین شوند.
اگر در
انجام این
کار ناموفق
باشد، YYFILL
نباید به
تحلیلگر
لغوی
بازگردد (به
همین دلیل
بهتر است با
بالا
انداختن یک
استثنا
پیادهسازی
شود). در صورت
فراخوانی
موفق YYFILL،
موقعیت limit
باید یا روی
یک موقعیت
پس از آخرین
موقعیت
ورودی در
بافر تنظیم
شود، یا در
انتهای
پدینگ YYMAXFILL
قرار گیرد
(در صورتی که
YYFILL حداقل n
نویسه را با
موفقیت
خوانده
باشد، اما
برای پر
کردن کل
بافر کافی
نباشد).
تصاویر زیر
مکانهای
نسبی
موقعیتهای
ورودی در
بافر را قبل
و بعد از
فراخوانی
YYFILL نشان
میدهند
(پدینگ YYMAXFILL در
تصویر دوم
با نمادهای
# مشخص شده
است).
در ادامه مثالی از برنامهای آمده است که فایل ورودی input.txt را در تکههای ۴۰۹۶ بایتی میخواند و از بررسی مرز همراه با پدینگ استفاده میکند.
ویژگیها (FEATURES)
بلوکهای چندگانه (Multiple blocks)
گاهی لازم است چندین تحلیلگر لغوی مرتبط با یکدیگر داشته باشیم (برای مثال، اگر یک ماشین حالت سطحبالا وجود داشته باشد که بین حالتهای تحلیلگر لغوی جابهجا شود). این ساختار را میتوان با استفاده از چندین بلوک متصل re2ocaml پیادهسازی کرد. گزینه دیگر استفاده از %شرایط شروع (start conditions) است.
پیادهسازی اتصالات بین بلوکها به زبان مقصد بستگی دارد. در زبانهایی که دارای دستور goto هستند (مانند C/C++ و Go) میتوان تمام بلوکها را درون یک تابع قرار داد، بهطوری که پیش از هر یک از آنها یک برچسب (label) قرار گیرد. انتقال از یک بلوک به بلوک دیگر یک goto ساده است. در زبانهایی که فاقد دستور goto هستند، استفاده از یک حلقه همراه با switch روی یک متغیر وضعیت ضروری است، یا اینکه میتوان هر بلوک را در یک تابع کپسولهسازی کرد و از فراخوانی توابع بهره برد.
مثال زیر از چندین بلوک برای تجزیه اعداد باینری (مبنای ۲)، اکتال (مبنای ۸)، دهدهی (مبنای ۱۰) و هگزادسیمال (مبنای ۱۶) استفاده میکند. هر مبنا بلوک مخصوص به خود را دارد. بلوک اولیه، مبنا را مشخص کرده و کار را به بلوکهای دیگر ارجاع میدهد. پیکربندیهای مشترک در یک بلوک مجزا در ابتدای برنامه تعریف شدهاند؛ این پیکربندیها توسط سایر بلوکها به ارث برده میشوند.
(* re2ocaml $INPUT -o $OUTPUT -i *)
open Int64
open Option
open String
type state = {
yyinput: string;
mutable yycursor: int;
mutable yymarker: int;
}
let add (num: int option) (dgt: int) (base: int) : int option =
match num with
| None -> None
| Some n ->
let n' = add (mul (of_int n) (of_int base)) (of_int dgt)
in if n' > (of_int32 Int32.max_int) then None else Some (to_int n')
%{
re2c:yyrecord = "st";
re2c:yyfill:enable = 0;
%}
%{local
re2c:YYFN = ["parse_bin;int option", "st;state", "num;int option"];
[01] { parse_bin st (add num (Char.code st.yyinput.[st.yycursor - 1] - 48) 2) }
* { num }
%}
%{local
re2c:YYFN = ["parse_oct;int option", "st;state", "num;int option"];
[0-7] { parse_oct st (add num (Char.code st.yyinput.[st.yycursor - 1] - 48) 8) }
* { num }
%}
%{local
re2c:YYFN = ["parse_dec;int option", "st;state", "num;int option"];
[0-9] { parse_dec st (add num (Char.code st.yyinput.[st.yycursor - 1] - 48) 10) }
* { num }
%}
%{local
re2c:YYFN = ["parse_hex;int option", "st;state", "num;int option"];
[0-9] { parse_hex st (add num (Char.code st.yyinput.[st.yycursor - 1] - 48) 16) }
[a-f] { parse_hex st (add num (Char.code st.yyinput.[st.yycursor - 1] - 87) 16) }
[A-F] { parse_hex st (add num (Char.code st.yyinput.[st.yycursor - 1] - 55) 16) }
* { num }
%}
%{local
re2c:YYFN = ["parse;int option", "st;state"];
'0b' / [01] { parse_bin st (Some 0) }
"0" { parse_oct st (Some 0) }
"" / [1-9] { parse_dec st (Some 0) }
'0x' / [0-9a-fA-F] { parse_hex st (Some 0) }
* { None }
%}
let test (yyinput: string) (result: int option) =
let st = {yyinput = yyinput; yycursor = 0; yymarker = 0} in
if not (parse st = result) then raise (Failure "error")
let main () =
test "\x00" None;
test "1234567890\x00" (Some 1234567890);
test "0b1101\x00" (Some 13);
test "0x7Fe\x00" (Some 2046);
test "0644\x00" (Some 420);
test "9999999999\x00" None
let _ = main ()
شرایط شروع (Start conditions)
شرایط شروع با گزینه --start-conditions فعال میشوند. آنها روشی برای کدگذاری چندین اتوماتای مرتبط با یکدیگر در یک بلوک re2ocaml یکسان ارائه میدهند.
هر شرط متناظر با یک اتوماتای منفرد است و یک نام یکتا دارد که توسط کاربر مشخص میشود و یک شماره داخلی یکتا که توسط re2ocaml تعریف میگردد. این شمارهها برای جابهجایی میان شرایط استفاده میشوند: کد تولیدشده از سازوکارهای اولیه 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 پرش میکند.
کدی که re2ocaml برای شرایط تولید میکند بستگی به این دارد که re2ocaml از رویکرد goto/label استفاده میکند یا رویکرد loop/switch برای کدگذاری اتوماتا.
در زبانهایی که دارای دستور goto هستند (مانند C/C++ و Go)، شرایط بهطور طبیعی به صورت بلوکهایی از کد با پیشوند برچسبهایی به شکل yyc_<cond> پیادهسازی میشوند، که در آن cond نام شرط است (پیشوند برچسب را میتوان با re2c:cond:prefix تغییر داد). جابهجاییها میان شرایط با استفاده از goto و برچسبهای شرط پیادهسازی میشوند. پیش از تمام شرایط، re2ocaml یک switch اولیه روی YYGETSTATE تولید میکند که به وضعیت شروع شرط کنونی پرش میکند. قواعد میانبر :=> از switch اولیه عبور کرده و مستقیماً به شرط مشخصشده پرش میکنند (میتوان از re2c:cond:goto برای تغییر رفتار پیشفرض استفاده کرد). قواعد دارای کنشهای معنایی بهطور خودکار به شرط بعدی پرش نمیکنند؛ این کار باید توسط کد کنش تعریفشده توسط کاربر انجام شود.
در زبانهایی که دستور goto را ندارند (مانند Rust)، ابزار re2ocaml از متغیر yystate برای ذخیره شمارههای شرایط استفاده مجدد میکند. هر شرط یک شناسه عددی برابر با شماره وضعیت شروع خود دریافت میکند، و جابهجایی بین شرایط هیچ تفاوتی با switch میان وضعیتهای DFA در یک شرط واحد ندارد. نیازی به یک switch اولیه جداگانه برای شرایط وجود ندارد. (از آنجا که رویکرد یکسانی برای پیادهسازی وضعیتهای قابل ذخیره استفاده میشود، در صورتی که هر دو قابلیت وضعیتهای قابل ذخیره و شرایط استفاده شوند، YYGETCOND/YYSETCOND زائد خواهند بود).
برنامه زیر از شرایط شروع برای تجزیه اعداد دودویی، هشتهشتی، دهدهی و شانزدهشانزدهی استفاده میکند. یک بلوک منفرد وجود دارد که در آن هر مبنا شرط مخصوص به خود را دارد و شرط اولیه به همه آنها متصل است. متغیر تعریفشده توسط کاربر با نام cond شماره شرط کنونی را ذخیره میکند؛ این متغیر با شماره شرط اولیه که توسط بلوک conditions تولید شده است، مقداردهی اولیه میشود.
(* re2ocaml $INPUT -o $OUTPUT -ci *)
open Int64
open Option
open String
%{conditions %}
type state = {
yyinput: string;
mutable yycursor: int;
mutable yymarker: int;
mutable yycond: yycondtype;
}
let add (num: int option) (dgt: int) (base: int) : int option =
match num with
| None -> None
| Some n ->
let n' = add (mul (of_int n) (of_int base)) (of_int dgt)
in if n' > (of_int32 Int32.max_int) then None else Some (to_int n')
%{
re2c:YYFN = ["parse;int option", "st;state", "num;int option"];
re2c:yyrecord = "st";
re2c:yyfill:enable = 0;
<init> '0b' / [01] :=> bin
<init> "0" :=> oct
<init> "" / [1-9] :=> dec
<init> '0x' / [0-9a-fA-F] :=> hex
<init> * { None }
<bin> [01] { yyfnbin st (add num (Char.code st.yyinput.[st.yycursor - 1] - 48) 2) }
<oct> [0-7] { yyfnoct st (add num (Char.code st.yyinput.[st.yycursor - 1] - 48) 8) }
<dec> [0-9] { yyfndec st (add num (Char.code st.yyinput.[st.yycursor - 1] - 48) 10) }
<hex> [0-9] { yyfnhex st (add num (Char.code st.yyinput.[st.yycursor - 1] - 48) 16) }
<hex> [a-f] { yyfnhex st (add num (Char.code st.yyinput.[st.yycursor - 1] - 87) 16) }
<hex> [A-F] { yyfnhex st (add num (Char.code st.yyinput.[st.yycursor - 1] - 55) 16) }
<bin, oct, dec, hex> * { num }
%}
let test (yyinput: string) (result: int option) =
let st = {yyinput = yyinput; yycursor = 0; yymarker = 0; yycond = YYC_init} in
if not (parse st (Some 0) = result) then raise (Failure "error")
let main () =
test "\x00" None;
test "1234567890\x00" (Some 1234567890);
test "0b1101\x00" (Some 13);
test "0x7Fe\x00" (Some 2046);
test "0644\x00" (Some 420);
test "9999999999\x00" None
let _ = main ()
وضعیت قابل ذخیرهسازی (Storable state)
با گزینه --storable-state، ابزار re2ocaml تحلیلگر لغویای تولید میکند که میتواند وضعیت فعلی خود را ذخیره کند، به فراخواننده بازگردد، و بعداً عملیات را دقیقاً از همانجایی که متوقف شده بود از سر بگیرد. حالت پیشفرض عملکرد در re2ocaml یک مدل «کششی» ("pull") است که در آن تحلیلگر لغوی هر زمان که نیاز داشته باشد، ورودی بیشتری را «میکشد» (دریافت میکند). این امر ممکن است در مواردی که ورودی بهصورت تکهتکه در دسترس قرار میگیرد غیرقابل قبول باشد (برای مثال، اگر تحلیلگر لغوی توسط تجزیهکننده (parser) فراخوانی شود، یا اگر برنامه تحلیلگر لغوی از طریق پروتکل سوکت با برنامه دیگری ارتباط برقرار کند که باید پیش از ارسال پیام بعدی، منتظر پاسخ از سوی تحلیلگر لغوی بماند). ویژگی وضعیت ذخیرهپذیر دقیقاً برای چنین مواردی در نظر گرفته شده است: این ویژگی امکان تولید تحلیلگرهای لغوی را فراهم میکند که در یک مدل «رانشی» ("push") کار میکنند. هنگامی که تحلیلگر لغوی به ورودی بیشتری نیاز دارد، وضعیت خود را ذخیره کرده و به فراخواننده بازمیگردد. بعداً، زمانی که ورودی بیشتری در دسترس قرار گرفت، فراخواننده تحلیلگر لغوی را دقیقاً از جایی که متوقف شده بود از سر میگیرد. در مقایسه با مدل «کششی»، چند تغییر ضروری است:
- اولیههای YYSETSTATE() و YYGETSTATE(state) را تعریف کنید.
- متغیرهای yych، yyaccept (در صورت استفاده) و state را بهعنوان بخشی از وضعیت ماندگار (persistent) تحلیلگر لغوی تعریف کنید. متغیر state باید با -1 مقداردهی اولیه شود.
- ماکروی YYFILL باید به جای تلاش برای تأمین ورودی بیشتر، به برنامه بیرونی بازگردد. کد بازگشتی باید نشان دهد که تحلیلگر لغوی به ورودی بیشتری نیاز دارد.
- برنامه بیرونی باید موقعیتهایی را که تحلیلگر لغوی به ورودی بیشتری نیاز دارد تشخیص داده و واکنش مناسب نشان دهد.
- بهصورت اختیاری از بلوک getstate برای تولید سوییچ YYGETSTATE جدا از تحلیلگر لغوی اصلی استفاده کنید. این کار فقط برای زبانهایی که دارای goto هستند کار میکند (نه در حالت --loop-switch).
- از re2c:eof و روش %sentinel with bounds checks (نگهبان به همراه بررسی کرانها) برای مدیریت پایان ورودی استفاده کنید. روش مبتنی بر فاصلهگذاری (padding) ممکن است کار نکند زیرا مشخص نیست چه زمانی باید فاصلهگذاری را اضافه کرد: انتهای فعلی ورودی ممکن است انتهای نهایی ورودی نباشد، و اضافه کردن زودهنگام فاصلهگذاری ممکن است یک لکسم (lexeme) حریصانه نیمهخواندهشده را قطع کند. علاوه بر این، به دلیل منطق سطح بالای برنامه، دریافت ورودی بیشتر ممکن است به پردازش لکسم انتهای بافر وابسته باشد (که از پیش به دلیل شرط پایان ورودی مسدود شده است).
در اینجا مثالی از یک تحلیلگر لغوی مدل «رانشی» آورده شده است که خواندن بستهها از یک سوکت را شبیهسازی میکند. تحلیلگر لغوی تا زمانی که با پایان ورودی مواجه شود حلقه میزند و به تابع فراخواننده بازمیگردد. تابع فراخواننده با «ارسال» بسته بعدی، ورودی بیشتری فراهم کرده و تحلیل لغوی را از سر میگیرد. این فرایند زمانی که تمام بستهها ارسال شوند، یا خطایی رخ دهد، متوقف میشود.
(* re2ocaml $INPUT -o $OUTPUT -fi *)
open Bytes
(* Use a small buffer to cover the case when a lexeme doesn't fit.
In real world use a larger buffer. *)
let bufsize = 10
let debug = false
let log format = (if debug then Printf.eprintf else Printf.ifprintf stderr) format
type state = {
file: in_channel;
yyinput: bytes;
mutable yycursor: int;
mutable yymarker: int;
mutable yylimit: int;
mutable token: int;
mutable yystate: int;
mutable recv: int;
}
type status = End | Ready | Waiting | BadPacket | BigPacket
let fill(st: state) : status =
(* Error: lexeme too long. In real life could reallocate a larger buffer. *)
if st.token < 1 then BigPacket else (
(* Shift buffer contents (discard everything up to the current token). *)
blit st.yyinput st.token st.yyinput 0 (st.yylimit - st.token);
st.yycursor <- st.yycursor - st.token;
st.yymarker <- st.yymarker - st.token;
st.yylimit <- st.yylimit - st.token;
st.token <- 0;
(* Fill free space at the end of buffer with new data from file. *)
let n = In_channel.input st.file st.yyinput st.yylimit (bufsize - st.yylimit - 1) in
st.yylimit <- st.yylimit + n;
set st.yyinput st.yylimit '\x00'; (* append sentinel *)
Ready)
%{
re2c:YYFN = ["lex;status", "yyrecord;state"];
re2c:YYFILL = "Waiting";
re2c:eof = 0;
packet = [a-z]+[;];
* { BadPacket }
$ { End }
packet { yyrecord.recv <- yyrecord.recv + 1; lex_loop yyrecord }
%}
and lex_loop st =
st.token <- st.yycursor;
lex st
let test (packets: string list) (sts: status) =
let fname = "pipe" in
let oc = Out_channel.open_bin fname in
let ic = In_channel.open_bin fname in
let yylimit = bufsize - 1 in
let st = {
file = ic;
(* Sentinel (at `yylimit` offset) is set to null, which triggers YYFILL. *)
yyinput = create bufsize;
yycursor = yylimit;
yymarker = yylimit;
yylimit = yylimit;
token = yylimit;
yystate = -1;
recv = 0;
} in
let rec loop packets = match lex_loop st with
| End ->
log "done: got %d packets\n" st.recv;
End
| Waiting ->
log "waiting...\n";
let packets' = match packets with
| [] -> []
| p :: ps ->
log "sent packet '%s'\n" p;
Out_channel.output_string oc p;
Out_channel.flush oc; (* without `flush` write happens too late *)
ps
in (match fill st with
| BigPacket ->
log "error: packet too big\n";
BigPacket
| Ready -> loop packets'
| _ -> raise (Failure "unexpected status after fill"))
| BadPacket ->
log "error: ill-formed packet\n";
BadPacket
| _ -> raise (Failure "unexpected status")
in if not (loop packets = sts) then
raise (Failure "error");
In_channel.close ic;
Out_channel.close oc;
Sys.remove fname
let main () =
test [] End;
test ["zero;"; "one;"; "two;"; "three;"; "four;"] End;
test ["zer0;"] BadPacket;
test ["goooooooooogle;"] BigPacket
let _ = main ()
بلوکهای قابل استفاده مجدد (Reusable blocks)
بلوکهای قابل استفاده مجدد با ساختار /*!rules:re2c[:<name>] ... */ یا %{rules[:<name>] ... %} میتوانند به هر تعداد بار مجدداً استفاده شوند و با سایر بلوکهای re2ocaml ترکیب گردند. بخش <name> اختیاری است. یک بلوک قوانین (rules block) میتواند در یک بلوک یا دستور use به کار گرفته شود. کد مربوط به یک بلوک قوانین، در هر نقطه استفاده تولید میشود.
بلوکهای use با /*!use:re2c[:<name>] ... */ یا %{use[:<name>] ... %} تعریف میشوند. شناسه <name> اختیاری است: اگر مشخص نشده باشد، بلوک قوانین مرتبط، نزدیکترین بلوک قبلی خواهد بود (خواه نامگذاریشده یا بدون نام). یک بلوک use میتواند تعاریف نامگذاریشده، پیکربندیها و قوانین اختصاصی خود را اضافه کند. یک کاربرد مهم برای بلوکهای use، تحلیلگر لغوی (lexer) با پشتیبانی از چندین کدگذاری ورودی است: همان بلوک قوانین چندین بار با پیکربندیهای مختص هر کدگذاری مجدداً استفاده میشود (مثال زیر را ببینید).
دستور درونبلوکی !use:<name>; میتواند از داخل یک بلوک re2ocaml استفاده شود. این دستور، بلوک ارجاعدادهشده <name> را با بلوک فعلی ادغام میکند. اگر برخی از قوانین و پیکربندیهای ادغامشده با موارد تعریفشده قبلی همپوشانی داشته باشند، تداخلها به روش معمول حل میشوند: نخستین قاعده اولویت دارد، و آخرین پیکربندی بر موارد پیشین ارجحیت مییابد (آنها را رونویسی میکند). یک استثنا قوانین ویژه *، $ و (در حالت شرطی) <!> هستند که برای آنها تعریف محلی بلوک بر هرگونه تعریف به ارثرسیده برتری دارد. دستور use امکان ترکیب بلوکهای مختلف re2ocaml را با یکدیگر در یک بلوک واحد فراهم میسازد (مثال زیر را ببینید).
بلوکهای نامگذاریشده و دستور درونبلوکی use در نگارش 2.2 از re2ocaml اضافه شدند. از آن نگارش به بعد، استفاده از بلوکهای قابل استفاده مجدد بهطور پیشفرض مجاز است (نیازی به گزینه خاصی نیست). پیش از نگارش 2.2، حالت استفاده مجدد با گزینه -r --reusable فعال میشد. پیش از نگارش 1.2، بلوکهای قابل استفاده مجدد نمیتوانستند با بلوکهای معمولی ترکیب شوند.
مثالی از دستورالعمل !use
(* re2ocaml $INPUT -o $OUTPUT *)
(* This example shows how to combine reusable re2c blocks: two blocks
('colors' and 'fish') are merged into one. The 'salmon' rule occurs
in both blocks; the 'fish' block takes priority because it is used
earlier. Default rule * occurs in all three blocks; the local (not
inherited) definition takes priority. *)
open String
type answer = Color | Fish | Dunno
type state = {
yyinput: string;
mutable yycursor: int;
mutable yymarker: int;
}
%{rules:colors
* { raise (Failure "ah"); }
"red" | "salmon" | "magenta" { Color }
%}
%{rules:fish
* { raise (Failure "oh"); }
"haddock" | "salmon" | "eel" { Fish }
%}
%{
re2c:YYFN = ["lex;answer", "yyrecord;state"];
re2c:yyfill:enable = 0;
!use:fish;
!use:colors;
* { Dunno } // overrides inherited '*' rules
%}
let test(str, ans) =
let st = {yyinput = str; yycursor = 0; yymarker = 0}
in if not (lex st = ans) then raise (Failure "error")
let main () =
test("salmon", Fish);
test("what?", Dunno)
let _ = main ()
مثالی از یک بلوک /*!use:re2c ... */
(* re2ocaml $INPUT -o $OUTPUT --input-encoding utf8 *)
(* This example supports multiple input encodings: UTF-8 and UTF-32.
Both lexers are generated from the same rules block, and the use
blocks add only encoding-specific configurations. *)
open Array
type 'a state = {
yyinput: 'a array;
mutable yycursor: int;
mutable yymarker: int;
}
%{rules
re2c:yyfill:enable = 0;
"∀x ∃y" { Some yyrecord.yycursor }
* { None }
%}
%{use
re2c:YYFN = ["lex8;int option", "yyrecord;char state"];
re2c:encoding:utf8 = 1;
%}
%{use
re2c:YYFN = ["lex32;int option", "yyrecord;int state"];
re2c:encoding:utf32 = 1;
%}
let main() =
let st8 = {
yyinput = [|'\xe2'; '\x08'; '\x80'; '\x78'; '\x20'; '\xe2'; '\x88'; '\x83'; '\x79'|];
yycursor = 0;
yymarker = 0;
} in if not (lex8 st8 = Some (Array.length st8.yyinput)) then raise (Failure "error");
let st32 = {
yycursor = 0;
yymarker = 0;
yyinput = [|0x2200; 0x78; 0x20; 0x2203; 0x79|];
} in if not (lex32 st32 = Some (Array.length st32.yyinput)) then raise (Failure "error");
استخراج زیرتطابق (Submatch extraction)
ابزار re2ocaml دو گزینه برای استخراج زیرتطابق دارد.
- Tags
- نخستین گزینه، استفاده از tags (برچسبهای) مستقل به شکل @stag یا #mtag است، که در آن stag و mtag نامهای دلخواه تعریفشده توسط کاربر هستند. برچسبها با گزینهٔ -T --tags یا پیکربندی re2c:tags = 1 فعال میشوند. از نظر معنایی، برچسبها نشانگرهای موقعیت هستند: آنها میتوانند در هر کجای یک عبارت منظم درج شوند و به موقعیت متناظر (یا چندین موقعیت) در رشتهٔ ورودی پیوند میخورند. S-tags به آخرین موقعیت تطبیقیافته متصل میشوند و m-tags به فهرستی از موقعیتها پیوند میخورند (آنها ممکن است در زیرعبارتهای تکرار استفاده شوند، جایی که یک موقعیت واحد در یک عبارت منظم با چندین موقعیت در رشتهٔ ورودی متناظر است). تمام برچسبها باید توسط کاربر تعریف شوند؛ خواه به صورت دستی یا با کمک بلوکهای svars و mvars. اگر بیش از یک روش برای تطبیق برچسبها با ورودی وجود داشته باشد، ابهام با استفاده از راهبرد رفع ابهام حریصانهٔ چپترین (leftmost greedy) برطرف میشود.
- Captures
- گزینهٔ دوم استفاده از capturing groups (گروههای ضبط) است. آنها با گزینهٔ --captures یا پیکربندی re2c:captures = 1 فعال میشوند. دو گونه برای سیاستهای مختلف رفع ابهام وجود دارد؛ --leftmost-captures (پیشفرض) برای سیاست حریصانهٔ چپترین، و --posix-captures برای سیاست طولانیترین تطابق POSIX است. در این حالت تمام زیرعبارتهای درون پرانتز گروههای ضبط در نظر گرفته میشوند و میتوان از علامت تعجب (bang) برای علامتگذاری گروههای غیرقابلضبط استفاده کرد: (! ... ). با گزینهٔ --invert-captures یا پیکربندی re2c:invert-captures = 1 معنای علامت تعجب معکوس میشود. تعداد گروهها برای قاعدهٔ تطبیقیافته در متغیری به نام yynmatch ذخیره میشود (کل عبارت منظم گروه شمارهٔ صفر است)، و نتایج زیرتطابق در آرایهٔ yypmatch ذخیره میشوند. هر دو متغیر yynmatch و yypmatch باید توسط کاربر تعریف شوند، و اندازهٔ yypmatch باید حداقل [yynmatch * 2] باشد. از بلوک maxnmatch برای تعریف YYMAXNMATCH استفاده کنید، ثابتی که برابر با بیشترین مقدار yynmatch در میان تمام قواعد است.
- Captvars
- روش دیگر برای استفاده از گروههای ضبط، گزینهٔ --captvars یا پیکربندی re2c:captvars = 1 است. تنها تفاوت با --captures در نحوهٔ ذخیرهسازی نتایج زیرتطابق در کد تولیدشده است: بهجای yynmatch و yypmatch، ابزار re2ocaml متغیرهای yytl<k> و yytr<k> را برای k-امین گروه ضبط تولید میکند (کاربر باید این متغیرها را با استفاده از یک بلوک svars اعلان کند). گروههای ضبط با متغیر از دو سیاست رفع ابهام پشتیبانی میکنند: --leftmost-captvars یا re2c:leftmost-captvars = 1 برای سیاست حریصانهٔ چپترین (حالت پیشفرض) و --posix-captvars یا re2c:posix-captvars برای سیاست طولانیترین تطابق POSIX.
در پشت صحنه، تمام این گزینهها به برچسبها و Tagged Deterministic Finite Automata with Lookahead ترجمه میشوند. ایدهٔ اصلی TDFA به حداقل رساندن بار اضافی (overhead) در استخراج زیرتطابق است. در حالت فرین (حد نهایی)، اگر هیچ برچسب یا گروه ضبطی در عبارت منظم نباشد، TDFA صرفاً یک DFA معمولی است. اگر تعداد برچسبها در حد معتدل باشد، بار اضافی به سختی محسوس خواهد بود. اتوماتای TDFA تولیدشده از تعدادی tag variables (متغیرهای برچسب) استفاده میکند که مستقیماً به برچسبها نگاشت نمیشوند: یک متغیر واحد ممکن است برای برچسبهای متفاوتی استفاده شود، و یک برچسب ممکن است برای نگهداشتن تمام مقادیر ممکن خود به چندین متغیر نیاز داشته باشد. در نهایت، ابهام برطرف میشود و برای هر برچسب تنها یک متغیر نهایی باقی میماند. متغیرهای برچسب باید با استفاده از بلوکهای stags یا mtags تعریف شوند. اگر وضعیت تحلیلگر لغوی ذخیره شود، متغیرهای برچسب نیز باید بخشی از آن باشند. آنها همچنین باید توسط YYFILL بهروزرسانی شوند.
برچسبهای S-tags از عملیات زیر پشتیبانی میکنند:
- ذخیرهٔ موقعیت ورودی در یک s-tag: دستور t = YYCURSOR با API اشارهگر C یا یک عملیات تعریفشده توسط کاربر YYSTAGP(t) با API عمومی (generic)
- ذخیرهٔ مقدار پیشفرض در یک s-tag: دستور t = NULL با API اشارهگر C یا یک عملیات تعریفشده توسط کاربر YYSTAGN(t) با API عمومی
- رونوشت از یک s-tag به دیگری: t1 = t2
برچسبهای M-tags از عملیات زیر پشتیبانی میکنند:
- الحاق موقعیت ورودی به یک m-tag: عملیات تعریفشده توسط کاربر YYMTAGP(t) با هر دو API پیشفرض و عمومی
- الحاق مقدار پیشفرض به یک m-tag: عملیات تعریفشده توسط کاربر YYMTAGN(t) با هر دو API پیشفرض و عمومی
- رونوشت از یک m-tag به دیگری: t1 = t2
برچسبهای S-tag را میتوان به صورت مقادیر اسکالر (اشارهگرها یا آفستها) پیادهسازی کرد. برچسبهای M-tag به نمایش پیچیدهتری نیاز دارند، زیرا باید دنبالهای از مقادیر برچسب را ذخیره کنند. ابتداییترین و ناکارآمدترین نمایش برای یک m-tag، یک لیست (آرایه، بردار) از مقادیر برچسب است؛ نمایشی کارآمدتر، ذخیرهٔ تمام m-tagها در یک درخت پیشوندی (prefix-tree) است که به صورت آرایهای از گرههای (v, p) نشان داده میشود، که در آن v مقدار برچسب و p اشارهگری به گره والد است.
در اینجا یک مثال ساده از بهکارگیری s-tagها برای تجزیهٔ نگارشهای معنایی (semantic versions) متشکل از سه جزء عددی آورده شده است: major، minor، patch (مورد آخر اختیاری است). برای یک مثال پیچیدهتر که از YYFILL استفاده میکند، بخش زیر را ببینید.
(* re2ocaml $INPUT -o $OUTPUT *)
open String
type state = {
yyinput: string;
mutable yycursor: int;
mutable yymarker: int;
(* Final tag variables available in semantic action. *)
%{svars format = "\n\tmutable @@{tag}: int;"; %}
(* Intermediate tag variables used by the lexer (must be autogenerated). *)
%{stags format = "\n\tmutable @@{tag}: int;"; %}
}
type semver = {
major: int;
minor: int;
patch: int;
}
let s2n (str: string) (i1: int) (i2: int) : int =
let rec f s i j n =
if i >= j then n else f s (i + 1) j (n * 10 + Char.code s.[i] - 48)
in f str i1 i2 0
%{local
re2c:YYFN = ["parse;semver option", "st;state"];
re2c:yyrecord = "st";
re2c:tags = 1;
re2c:yyfill:enable = 0;
num = [0-9]+;
@t1 num @t2 "." @t3 num @t4 ("." @t5 num)? [\x00] {
Some {
major = s2n st.yyinput st.t1 st.t2;
minor = s2n st.yyinput st.t3 st.t4;
patch = if st.t5 = -1 then 0 else s2n st.yyinput st.t5 (st.yycursor - 1)
}
}
* { None }
%}
let test (str: string) (result: semver option) =
let st = {
yyinput = str;
yycursor = 0;
yymarker = 0;
%{svars format = "\n\t\t@@{tag} = -1;"; %}
%{stags format = "\n\t\t@@{tag} = -1;"; %}
}
in if not (parse st = result) then raise (Failure "error")
let main () =
test "23.34" (Some {major = 23; minor = 34; patch = 0});
test "1.2.99999" (Some {major = 1; minor = 2; patch = 99999});
test "1.a" None
let _ = main ()
در اینجا مثالی پیچیدهتر از استفاده از s-tagها با YYFILL برای تجزیهٔ فایلی با نگارشهای معنایی جداشده با خط جدید آمده است. متغیرهای برچسب بخشی از وضعیت تحلیلگر لغوی هستند، و همانند سایر موقعیتهای ورودی در YYFILL تنظیم میشوند. توجه داشته باشید که این امر برای s-tagها ضروری است زیرا مقادیر آنها پس از جابهجایی محتویات بافر باطل میشوند. این کار ممکن است در پیادهسازیهای سفارشی که در آنها متغیرهای برچسب آفستها را بهجای بافر، نسبت به ابتدای رشتهٔ ورودی ذخیره میکنند (که ممکن است در مورد m-tagها چنین باشد)، ضروری نباشد.
(* re2ocaml $INPUT -o $OUTPUT *)
open Bytes
let bufsize = 4096
type state = {
file: in_channel;
yyinput: bytes;
mutable yycursor: int;
mutable yymarker: int;
mutable yylimit: int;
mutable token: int;
mutable eof: bool;
(* Final tag variables available in semantic action. *)
%{svars format = "\n\tmutable @@{tag}: int;"; %}
(* Intermediate tag variables used by the lexer (must be autogenerated). *)
%{stags format = "\n\tmutable @@{tag}: int;"; %}
}
type status = Ok | Eof | LongLexeme
type semver = {
major: int;
minor: int;
patch: int;
}
let s2n (str: bytes) (i1: int) (i2: int) : int =
let rec f s i j n =
if i >= j then n else f s (i + 1) j (n * 10 + Char.code (get s i) - 48)
in f str i1 i2 0
let fill(st: state) : status =
if st.eof then Eof else
(* Error: lexeme too long. In real life could reallocate a larger buffer. *)
if st.token < 1 then LongLexeme else (
(* Shift buffer contents (discard everything up to the current token). *)
blit st.yyinput st.token st.yyinput 0 (st.yylimit - st.token);
st.yycursor <- st.yycursor - st.token;
st.yymarker <- st.yymarker - st.token;
st.yylimit <- st.yylimit - st.token;
%{stags format = "\n\tst.@@ <- if st.@@ = -1 then -1 else st.@@ - st.token;"; %}
st.token <- 0;
(* Fill free space at the end of buffer with new data from file. *)
let n = input st.file st.yyinput st.yylimit (bufsize - st.yylimit - 1) in (* -1 for sentinel *)
st.yylimit <- st.yylimit + n;
if n = 0 then
st.eof <- true; (* end of file *)
set st.yyinput st.yylimit '\x00'; (* append sentinel *)
Ok)
%{
re2c:YYFN = ["lex;(semver list) option", "st;state", "vers;semver list"];
re2c:YYFILL = "fill st = Ok";
re2c:yyrecord = "st";
re2c:tags = 1;
re2c:eof = 0;
num = [0-9]+;
@t1 num @t2 "." @t3 num @t4 ("." @t5 num)? [\n] {
let ver = {
major = s2n st.yyinput st.t1 st.t2;
minor = s2n st.yyinput st.t3 st.t4;
patch = if st.t5 = -1 then 0 else s2n st.yyinput st.t5 (st.yycursor - 1)
} in lex_loop st (ver :: vers)
}
$ { Some (List.rev vers) }
* { None }
%}
and lex_loop st vers =
st.token <- st.yycursor;
lex st vers
let main () =
let fname = "input" in
(* Prepare input file. *)
Out_channel.with_open_bin fname
(fun oc -> for i = 1 to bufsize do
output_string oc "1.22.333\n"
done);
(* Construct the expected result to compare against. *)
let expect = Some (List.init bufsize
(fun _ -> {major = 1; minor = 22; patch = 333;})) in
(* Run lexer on the prepared file. *)
In_channel.with_open_bin fname
(fun ic ->
let yylimit = bufsize - 1 in
let st = {
file = ic;
yyinput = create bufsize;
yycursor = yylimit;
yymarker = yylimit;
yylimit = yylimit;
token = yylimit;
eof = false;
%{svars format = "\n\t\t@@{tag} = -1;"; %}
%{stags format = "\n\t\t@@{tag} = -1;"; %}
} in if (lex_loop st [] <> expect) then
raise (Failure "error"));
(* Cleanup. *)
Sys.remove fname
let _ = main ()
در اینجا مثالی از بهکارگیری گروههای تسخیر (capturing groups) برای تجزیهٔ نگارشهای معنایی (semantic versions) آورده شده است.
(* re2ocaml $INPUT -o $OUTPUT *)
open String
type state = {
yyinput: string;
mutable yycursor: int;
mutable yymarker: int;
(* Final tag variables available in semantic action. *)
%{svars format = "\n\tmutable @@{tag}: int;"; %}
(* Intermediate tag variables used by the lexer (must be autogenerated). *)
%{stags format = "\n\tmutable @@{tag}: int;"; %}
}
type semver = {
major: int;
minor: int;
patch: int;
}
let s2n (str: string) (i1: int) (i2: int) : int =
let rec f s i j n =
if i >= j then n else f s (i + 1) j (n * 10 + Char.code s.[i] - 48)
in f str i1 i2 0
%{local
re2c:YYFN = ["parse;semver option", "st;state"];
re2c:yyrecord = "st";
re2c:captvars = 1;
re2c:yyfill:enable = 0;
num = [0-9]+;
(num) "." (num) ("." num)? [\x00] {
Some {
major = s2n st.yyinput st.yytl1 st.yytr1;
minor = s2n st.yyinput st.yytl2 st.yytr2;
patch = if st.yytl3 = -1 then 0 else s2n st.yyinput (st.yytl3 + 1) st.yytr3
}
}
* { None }
%}
let test (str: string) (result: semver option) =
let st = {
yyinput = str;
yycursor = 0;
yymarker = 0;
%{svars format = "\n\t\t@@{tag} = -1;"; %}
%{stags format = "\n\t\t@@{tag} = -1;"; %}
}
in if not (parse st = result) then raise (Failure "error")
let main () =
test "23.34" (Some {major = 23; minor = 34; patch = 0});
test "1.2.99999" (Some {major = 1; minor = 2; patch = 99999});
test "1.a" None
let _ = main ()
در اینجا مثالی از بهکارگیری m-tags برای تجزیهٔ نگارشی با تعداد متغیر مؤلفهها آورده شده است. متغیرهای برچسب در یک trie ذخیره میشوند.
(* re2ocaml $INPUT -o $OUTPUT *)
open String
type state = {
yyinput: string;
mutable yycursor: int;
mutable yymarker: int;
(* Final tag variables available in semantic action. *)
%{svars format = "\n\tmutable @@{tag}: int;"; %}
%{mvars format = "\n\tmutable @@{tag}: int list;"; %}
(* Intermediate tag variables used by the lexer (must be autogenerated). *)
%{stags format = "\n\tmutable @@{tag}: int;"; %}
%{mtags format = "\n\tmutable @@{tag}: int list;"; %}
}
let s2n (str: string) (i1: int) (i2: int) : int =
let rec f s i j n =
if i >= j then n else f s (i + 1) j (n * 10 + Char.code s.[i] - 48)
in f str i1 i2 0
%{local
re2c:YYFN = ["parse;(int list) option", "st;state"];
re2c:YYMTAGP = "@@ <- st.yycursor :: @@;";
re2c:YYMTAGN = ""; // alternatively could add `-1` to the list
re2c:yyrecord = "st";
re2c:tags = 1;
re2c:yyfill:enable = 0;
num = [0-9]+;
@t1 num @t2 ("." #t3 num #t4)* [\x00] {
let x = s2n st.yyinput st.t1 st.t2 in
let xs = List.rev (List.map2 (fun x y -> s2n st.yyinput x y) st.t3 st.t4) in
Some (x :: xs)
}
* { None }
%}
let test (str: string) (result: (int list) option) =
let st = {
yyinput = str;
yycursor = 0;
yymarker = 0;
%{svars format = "\n\t\t@@{tag} = -1;"; %}
%{mvars format = "\n\t\t@@{tag} = [];"; %}
%{stags format = "\n\t\t@@{tag} = -1;"; %}
%{mtags format = "\n\t\t@@{tag} = [];"; %}
}
in if not (parse st = result) then raise (Failure "error")
let main () =
test "1" (Some [1]);
test "1.2.3.4.5.6.7" (Some [1; 2; 3; 4; 5; 6; 7;]);
test "1.2." None
let _ = main ()
پشتیبانی از کدگذاری (Encoding support)
درک تفاوت بین نقطههای کد (code points) و واحدهای کد (code units) ضروری است. یک نقطه کد، شناسه عددی یک نماد است. یک واحد کد، کوچکترین واحد ذخیرهسازی در متن کدگذاریشده است. یک نقطه کد تکی ممکن است با یک یا چند واحد کد نمایش داده شود. در یک کدگذاری با طول ثابت، تمام نقطههای کد با تعداد یکسانی از واحدهای کد نشان داده میشوند. در یک کدگذاری با طول متغیر، نقطههای کد ممکن است با تعداد متفاوتی از واحدهای کد نمایش داده شوند. توجه داشته باشید که قاعده "any" یعنی [^] با هر نقطه کدی تطابق دارد، اما لزوماً با هر واحد کدی تطابق ندارد (تنها راه تطبیق با هر واحد کد صرفنظر از نوع کدگذاری، قاعده پیشفرض * است). تحلیلگر لغوی تولیدشده با جریانی از واحدهای کد کار میکند: yych یک واحد کد را ذخیره میکند و YYCTYPE نوع واحد کد است. از سوی دیگر، عبارتهای منظم برحسب نقطههای کد مشخص میشوند. هنگامی که re2ocaml عبارتهای منظم را به اتوماتا کامپایل میکند، نقطههای کد را به واحدهای کد ترجمه مینماید. این کار معمولاً یک نگاشت ساده نیست: در کدگذاریهای با طول متغیر، ممکن است یک بازه از یک نقطه کد منفرد به یک گراف پیچیده از واحدهای کد ترجمه شود. کدگذاریهای زیر پشتیبانی میشوند:
- ASCII (بهطور پیشفرض فعال است). یک کدگذاری با طول ثابت با فضای کد [0-255] و نقطههای کد و واحدهای کد ۱-بایتی است.
- EBCDIC (فعالسازی با --ebcdic یا re2c:encoding:ebcdic). یک کدگذاری با طول ثابت با فضای کد [0-255] و نقطههای کد و واحدهای کد ۱-بایتی است.
- UCS2 (فعالسازی با --ucs2 یا re2c:encoding:ucs2). یک کدگذاری با طول ثابت با فضای کد [0-0xFFFF] و نقطههای کد و واحدهای کد ۲-بایتی است.
- UTF8 (فعالسازی با --utf8 یا re2c:encoding:utf8). یک کدگذاری Unicode با طول متغیر است. اندازه واحد کد ۱ بایت است. نقطههای کد با 1 -- 4 واحد کد نمایش داده میشوند.
- UTF16 (فعالسازی با --utf16 یا re2c:encoding:utf16). یک کدگذاری Unicode با طول متغیر است. اندازه واحد کد ۲ بایت است. نقطههای کد با 1 -- 2 واحد کد نمایش داده میشوند.
- UTF32 (فعالسازی با --utf32 یا re2c:encoding:utf32). یک کدگذاری Unicode با طول ثابت با فضای کد [0-0x10FFFF] و نقطههای کد و واحدهای کد ۴-بایتی است.
فایلهای ضمیمه include/unicode_blocks.re، include/unicode_categories.re و include/unicode_properties.re تعاریف re2ocaml را برای بلوکها، دستهها و ویژگیهای شرحدادهشده در استاندارد Unicode ارائه میدهند.
گزینه --input-encoding کدگذاری فایل منبع را مشخص میکند، که میتواند برای فعالسازی لیترالهای Unicode در عبارتهای منظم استفاده شود. برای مثال، --input-encoding utf8 به re2ocaml اعلام میکند که فایل منبع به فرمت UTF8 است (این با --utf8 که کدگذاری متن ورودی را تنظیم میکند متفاوت است). گزینه --encoding-policy نحوه مدیریت جانشینهای یونیکد (Unicode surrogates؛ نقطههای کد در محدوده [0xD800-0xDFFF]) را توسط re2ocaml تعیین میکند.
در زیر نمونهای از یک تحلیلگر لغوی برای شناسههای Unicode با کدگذاری UTF8 آورده شده است.
(* re2ocaml $INPUT -o $OUTPUT --utf8 -i *)
open String
%{include "unicode_categories.re" %}
type state = {
yyinput: string;
mutable yycursor: int;
mutable yymarker: int;
mutable yyaccept: int;
}
%{
re2c:YYFN = ["lex;bool", "yyrecord;state"];
re2c:yyfill:enable = 0;
// Simplified "Unicode Identifier and Pattern Syntax"
// (see https://unicode.org/reports/tr31)
id_start = L | Nl | [$_];
id_continue = id_start | Mn | Mc | Nd | Pc | [\u200D\u05F3];
identifier = id_start id_continue*;
identifier { true }
* { false }
%}
let main () =
let st = {
yyinput = "_Ыдентификатор";
yycursor = 0;
yymarker = 0;
yyaccept = 0;
}
in if not (lex st) then raise (Failure "error")
let _ = main ()
فایلهای ضمیمه (Include files)
ابزار re2ocaml به شما اجازه میدهد فایلهای دیگر را با استفاده از بلوکی به شکل /*!include:re2c FILE */ یا %{include FILE %}، یا دستور درونبلوکی !include FILE ; ضمیمه کنید، که در آن FILE مسیر فایلی است که باید ضمیمه شود. re2ocaml فایلهای ضمیمه را در دایرکتوریِ فایلِ دربرگیرنده و در مکانهای include (که میتوان با گزینه -I مشخص کرد) جستجو میکند. بلوکها/دستورهای ضمیمهسازی در re2ocaml به همان شیوه #include در C/C++ عمل میکنند: محتویات FILE عیناً به جای بلوک/دستور کپی و جایگذاری میشوند. فایلهای ضمیمه میتوانند خود دارای includeهای بیشتری باشند. از گزینه --depfile برای ردیابی وابستگیهای ساخت فایل خروجی نسبت به فایلهای ضمیمه استفاده کنید. ابزار re2ocaml تعدادی فایل ضمیمه از پیش تعریفشده ارائه میدهد که در زیردایرکتوری include/ از پروژه قرار دارند. این فایلها شامل تعاریفی هستند که ممکن است برای پروژههای دیگر نیز مفید باشند (مانند دستهبندیهای Unicode) و چیزی شبیه به یک کتابخانه استاندارد را برای re2ocaml تشکیل میدهند. در ادامه مثالی از بهکارگیری فایلهای ضمیمه آورده شده است.
فایل ضمیمه ۱ (definitions.ml)
type number = Int | Float | NaN
%{
number = [1-9][0-9]*;
%}
فایل ضمیمه ۲ (extra_rules.re.inc)
// floating-point numbers
frac = [0-9]* "." [0-9]+ | [0-9]+ ".";
exp = 'e' [+-]? [0-9]+;
float = frac exp? | [0-9]+ exp;
float { Float }
فایل ورودی (Input file)
(* re2ocaml $INPUT -o $OUTPUT -i *)
open String
%{include "definitions.ml" %}
type state = {
yyinput: string;
mutable yycursor: int;
mutable yymarker: int;
mutable yyaccept: int;
}
%{
re2c:YYFN = ["lex;number", "yyrecord;state"];
re2c:yyfill:enable = 0;
* { NaN }
number { Int }
!include "extra_rules.re.inc";
%}
let test(str, num) =
let st = {yyinput = str; yycursor = 0; yymarker = 0; yyaccept = 0}
in if not (lex st = num) then raise (Failure "error")
let main () =
test("123", Int);
test("123.4567", Float)
let _ = main ()
فایلهای سرآیند (Header files)
ابزار re2ocaml به کاربر امکان میدهد تا با استفاده از گزینه --header یا پیکربندی re2c:header و جفتبلوکهایی به شکل /*!header:re2c:on*/ و /*!header:re2c:off*/، یا %{header:on%} و %{header:off%}، از فایل ورودی یک فایل سرآیند تولید کند. بلوک نخست آغاز فایل سرآیند و بلوک دوم پایان آن را مشخص میسازد. هر آنچه میان این دو بلوک قرار گیرد توسط re2ocaml پردازش شده و کد تولیدشده در فایلی که با گزینه --header یا پیکربندی re2c:header مشخص شده نوشته میشود (یا در صورت عدم استفاده از گزینه یا پیکربندی، در stdout نوشته خواهد شد). فایل سرآیند تولیدشده خودکار ممکن است در مواردی نیاز باشد که از re2ocaml برای ایجاد تعاریفی استفاده میشود که باید از سایر واحدهای ترجمه قابل مشاهده باشند.
در ادامه مثالی از تولید یک فایل سرآیند آورده شده است که شامل تعریف وضعیت تحلیلگر لغوی به همراه متغیرهای برچسب (tag variables) است (تعداد متغیرها به دستور زبان باقاعده بستگی داشته و برای برنامهنویس نامشخص است).
فایل ورودی (Input file)
(* re2ocaml $INPUT -o $OUTPUT --header lexer/state.ml -i *)
open State
open String
%{header:on %}
type state = {
yyinput: string;
mutable yycursor: int;
mutable tag: int;
%{stags format = "mutable @@: int;"; %}
}
%{header:off %}
%{
re2c:YYFN = ["lex;int", "yyrecord;State.state"];
re2c:tags = 1;
re2c:yyfill:enable = 0;
re2c:header = "lexer/state.ml";
[a]* @tag [b]* { yyrecord.tag }
%}
let main () =
let st = {
yyinput = "ab";
yycursor = 0;
tag = 0;
%{stags format = "\n\t@@ = 0;"; %}
}
in if not (lex st = 1) then raise (Failure "error")
let _ = main ()
فایل سرآیند (Header file)
(* Generated by re2c *)
type state = {
yyinput: string;
mutable yycursor: int;
mutable tag: int;
mutable yyt1: int;
}
برنامههای اسکلت (Skeleton programs)
با استفاده از گزینه -S, --skeleton، ابزار re2ocaml تمام کدهای غیر re2ocaml را نادیده گرفته و یک برنامه خودکفای C تولید میکند که میتواند در ادامه کامپایل و اجرا شود. این برنامه از کد تحلیلگر لغوی و دادههای ورودی تشکیل شده است. به ازای هر DFA (بلوک یا شرط) ساختهشده، re2ocaml یک تحلیلگر لغوی مستقل و دو فایل تولید میکند: یک فایل &.input شامل رشتههای مشتقشده از DFA و یک فایل &.keys با نتایج تطابق مورد انتظار. برنامه هر تحلیلگر را روی فایل &.input متناظر اجرا کرده و نتایج را با موارد مورد انتظار مقایسه میکند. برنامههای اسکلت به دلایل متعددی بسیار سودمند هستند:
- آنها میتوانند صحت بهینهسازیهای گوناگون re2ocaml را بررسی کنند (دادهها در مراحل اولیه پردازش و پیش از اعمال هرگونه دگرگونی بر روی DFA تولید میشوند).
- تولید مجموعهای از دادههای ورودی با پوششدهی مناسب میتواند هم برای آزمون و هم برای سنجش کارایی (benchmarking) مفید باشد.
- تولید برنامههای اجرایی خودکفا امکان بهدست آوردن نمونههای آزمون کمینهشده را فراهم میسازد (چرا که کد اصلی ممکن است بسیار بزرگ بوده یا وابستگیهای فراوانی داشته باشد).
دشواری تولید دادههای ورودی در این است که برای تمامی موارد به جز سادهترین حالتها، تعداد رشتههای ورودی ممکن بیش از حد زیاد است (حتی در حالتی که طول رشته محدود باشد). ابزار re2ocaml این مشکل را با تولید رشتههای کافی به منظور پوشش تقریباً تمام گذارهای DFA حل میکند. این ابزار از الگوریتم زیر بهره میبرد: نخست، اسکلتی از DFA میسازد. برای کدگذاریهایی با اندازه واحد کد ۱ بایتی (مانند ASCII، UTF-8 و EBCDIC) اسکلت تنها یک رونوشت دقیق از DFA اولیه است. برای کدگذاریهایی با واحدهای کد چندبایتی، اسکلت رونوشتی از DFA با حذف برخی گذارهاست: به این صورت که re2ocaml برای هر بازه پیوسته و مجزا که با یک گذار DFA مطابقت دارد، حداکثر ۲۵۶ واحد کد برمیگزیند. مقادیر انتخابشده بهطور یکنواخت توزیع شدهاند و شامل کرانهای بازه نیز میشوند. re2ocaml به جای تلاش برای پوشش تمام مسیرهای ممکن در اسکلت (که امری ناشدنی است)، مسیرهای کافی را برای پوشش تمامی گذارهای اسکلت ایجاد میکند و بدین ترتیب پرشهای شرطی متناظر را در تحلیلگر فعال میسازد. پیادهسازی این الگوریتم به حدود ۱ گیگابایت گذار محدود شده است و مقدار ثابتی از حافظه را مصرف میکند (re2ocaml دادهها را به محض تولید، در فایل مینویسد).
مصورسازی و اشکالزدایی (Visualization and debug)
با گزینه -D, --emit-dot، برنامه re2ocaml کدی تولید نمیکند. در عوض، 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)
برنامه re2ocaml در ابتدا توسط 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.