| RE2HS(1) | RE2HS(1) |
نام (NAME)
re2hs - تولید تحلیلگرهای لغوی سریع برای Haskell
خلاصه دستور (SYNOPSIS)
re2hs [ OPTIONS ] [ WARNINGS ] INPUT
ورودی میتواند یک فایل یا - برای stdin باشد.
مقدمه (INTRODUCTION)
ابزار re2hs به عنوان یک پیشپردازنده عمل میکند. این ابزار فایل ورودی را (که معمولاً یک برنامه به زبان Haskell است، اما میتواند هر چیز دیگری نیز باشد) خوانده و به دنبال بلوکهای کدی میگردد که در نشانگرهای آغاز/پایان با ساختار ویژه محصور شدهاند. متنهای خارج از این بلوکها عیناً در فایل خروجی کپی میشوند. محتویات درون بلوکها توسط re2hs پردازش میشوند. این ابزار آنها را به کد Haskell ترجمه کرده و کد تولیدشده را به جای بلوک اصلی در خروجی قرار میدهد.
در ادامه مثالی از یک برنامه کوچک آورده شده است که بررسی میکند آیا یک رشته دادهشده حاوی یک عدد دهدهی است یا خیر:
-- re2hs $INPUT -o $OUTPUT -i
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -Wno-unused-record-wildcards #-}
import Data.ByteString (ByteString, index)
data State = State {
_yyinput :: ByteString,
_yycursor :: Int
}
%{
re2c:YYFN = ["lexer;Bool", "State{..};State"];
re2c:yyfill:enable = 0;
[1-9][0-9]* { True }
* { False }
%}
main :: IO ()
main = case lexer State{_yyinput = "1234\0", _yycursor = 0} of
True -> return ()
False -> error "lexer failed!"
در خروجی، re2hs بلوک میانی را با کد تولیدشده جایگزین کرده است:
-- Generated by re2hs
{-# LANGUAGE RecordWildCards #-}
-- re2hs $INPUT -o $OUTPUT -i
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -Wno-unused-record-wildcards #-}
import Data.ByteString (ByteString, index)
data State = State {
_yyinput :: ByteString,
_yycursor :: Int
}
yy0 :: State -> Bool
yy0 State{..} =
let yych = index _yyinput _yycursor in
let __ = _yycursor + 1 in let _yycursor = __ in
case yych of
_c | 0x31 <= _c && _c <= 0x39 ->
yy2 State{..}
_c | True ->
yy1 State{..}
yy1 :: State -> Bool
yy1 State{..} =
False
yy2 :: State -> Bool
yy2 State{..} =
let yych = index _yyinput _yycursor in
case yych of
_c | 0x30 <= _c && _c <= 0x39 ->
let __ = _yycursor + 1 in let _yycursor = __ in
yy2 State{..}
_c | True ->
yy3 State{..}
yy3 :: State -> Bool
yy3 State{..} =
True
lexer :: State -> Bool
lexer State{..} =
yy0 State{..}
main :: IO ()
main = case lexer State{_yyinput = "1234\0", _yycursor = 0} of
True -> return ()
False -> error "lexer failed!"
یک برنامه re2hs از توالیای از بلوکها آمیخته با کد زبان مقصد تشکیل شده است. یک بلوک میتواند شامل تعاریف، پیکربندیها، قواعد، کنشها و دستورالعملها با هر ترتیبی باشد:
- name = regular-expression ;
- یک تعریف، name را به regular-expression مقید میکند. نامها میتوانند شامل نویسههای الفبایی-عددی و خط زیرین باشند. بخش regular expressions مروری بر نحو re2hs برای عبارتهای منظم ارائه میدهد. پس از تعریف، name میتواند در سایر عبارتهای منظم و قواعد استفاده شود. بازگشت (توابع بازگشتی) در تعاریف نامگذاریشده مجاز نیست و هر نام باید پیش از استفاده تعریف شود. یک بلوک تعاریف نامگذاریشده را از حوزه سراسری (global scope) به ارث میبرد. بازتعریف نامی که در حوزه فعلی وجود دارد یک خطا است.
- configuration = value ;
- یک پیکربندی امکان تغییر رفتار re2hs و شخصیسازی کد تولیدشده را فراهم میسازد. برای دیدن فهرست کاملی از پیکربندیهای پشتیبانیشده توسط re2hs به بخش configurations مراجعه کنید. بسته به نوع پیکربندی، value میتواند یک کلیدواژه، یک عدد صحیح غیرمنفی یا یک رشته تکخطی باشد که باید درون گیومه تکی یا جفتی قرار گیرد مگر اینکه فقط از نویسههای الفبایی-عددی تشکیل شده باشد. یک بلوک پیکربندیها را از حوزه سراسری به ارث میبرد و میتواند آنها را بازتعریف کرده یا موارد جدیدی را بیفزاید. پیکربندیهای تعریفشده درون یک بلوک بر کل بلوک تأثیر میگذارند، حتی اگر در انتهای آن قرار داشته باشند.
- regular-expression code
- یک قاعده، regular-expression را به کنش معنایی آن (یک بلوک کد درون آکولاد، یا یک بلوک کد که با := آغاز شده و در یک خط جدید و به دنبال آن هر نویسه غیر فاصله خاتمه مییابد) مقید میکند. اگر regular-expression تطبیق یابد، code مرتبط اجرا میشود. اگر چند قاعده تطبیق یابند، طولانیترین تطبیق اولویت دارد. اگر چند قاعده با یک رشته یکسان تطبیق یابند، اولین قاعده اولویت دارد. دو قاعده ویژه وجود دارد: قاعده پیشفرض * و قاعده پایان ورودی $. قاعده پیشفرض باید همیشه تعریف شود، صرفنظر از موقعیتش در بلوک کمترین اولویت را دارد و با هر واحد کد مطابقت مییابد (نه لزوماً یک نویسه معتبر، به بخش encoding support مراجعه کنید). قاعده پایان ورودی باید در صورتی تعریف شود که متد متناظر برای handling the end of input به کار رفته باشد. با فعال بودن start conditions، قواعد نحو پیچیدهتری خواهند داشت.
- !action code
- یک کنش یک بلوک تعریفشده توسط کاربر از code را به یک مکان خاص در ماشین حالت متناهی تولیدشده متصل میکند (به همان شیوهای که کنشهای معنایی کد را به حالتهای پایانی پیوند میدهند). برای فهرست کامل کنشهای از پیش تعریفشده به بخش actions مراجعه کنید.
- !directive ;
- یک دستورالعمل یکی از گزارههای خاص از پیش تعریفشده است. هر دستورالعمل یک هدف منحصربهفرد دارد. برای جزئیات به بخش directives مراجعه کنید.
بلوکها (Blocks)
نشانگرهای شروع و پایان بلوک یا /*!re2c و */ هستند، یا %{ و %} (از هر دو سبک پشتیبانی میشود). از نسخه 2.2 بلوکها میتوانند نامهای اختیاری داشته باشند که امکان ارجاع به آنها در بلوکهای دیگر را فراهم میکند. انواع گوناگونی از بلوکها وجود دارد:
- /*!re2c[:<name>] ... */ or %{[:<name>] ... %}
- یک بلوک سراسری شامل تعاریف، پیکربندیها، قواعد و دستورالعملها است. re2hs عبارتهای منظم مرتبط با هر قاعده را به یک اتوماتون متناهی معین (DFA) کامپایل میکند، آن را به شکل پرشهای شرطی در زبان مقصد کدگذاری کرده و بلوک را با کد تولیدشده جایگزین میکند. نامها و پیکربندیهای تعریفشده در یک بلوک سراسری به حوزه سراسری اضافه میشوند و برای بلوکهای بعدی قابل مشاهده خواهند بود. در آغاز برنامه حوزه سراسری با options خط فرمان مقداردهی اولیه میشود.
- /*!local:re2c[:<name>] ... */ or %{local[:<name>] ... %}
- یک بلوک محلی مانند یک بلوک سراسری است، اما نامها و پیکربندیهای درون آن دارای حوزه محلی هستند (بر سایر بلوکها تأثیری نمیگذارند).
- /*!rules:re2c[:<name>] ... */ or %{rules[:<name>] ... %}
- یک بلوک قواعد شبیه یک بلوک محلی است، اما خودش به تنهایی کدی تولید نمیکند و هیچ تعریفی را به حوزه سراسری اضافه نمینماید -- هدف آن استفاده مجدد در سایر بلوکها است. این یک روش برای به اشتراکگذاری کد است (جزئیات بیشتر در بخش reusable blocks). پیش از نسخه 2.2 re2hs، بلوکهای قواعد نیازمند گزینه -r --reusable بودند.
- /*!use:re2c[:<name>] ... */ or %{use[:<name>] ... %}
- یک بلوک use که به یک بلوک قواعد از پیش تعریفشده ارجاع میدهد. اگر نام مشخص شده باشد، re2hs به دنبال یک بلوک قواعد با این نام میگردد. در غیر این صورت آخرین بلوک قواعد استفاده میشود (خواه نامگذاریشده باشد یا بدون نام). یک بلوک use میتواند تعاریف، پیکربندیها و قواعد خاص خود را بیفزاید که به موارد بلوک قواعد مرجع اضافه میشوند. پیش از نسخه 2.2 re2hs، بلوکهای use به گزینه -r --reusable نیاز داشتند.
- /*!max:re2c[:<name1>[:<name2>...]] ... */ or %{max[:<name1>[:<name2>...]] ... %}
- بلوکی که تعریف YYMAXFILL را تولید میکند. یک فهرست اختیاری از نام بلوکها مشخص میکند که هنگام محاسبه مقدار YYMAXFILL کدام بلوکها باید گنجانده شوند (اگر فهرست خالی باشد، تمام بلوکها لحاظ میشوند). بهطور پیشفرض کد تولیدشده یک تعریف ماکرو برای C (#define YYMAXFILL <n>) یا یک متغیر سراسری برای Go (var YYMAXFILL int = <n>) است. این بخش میتواند با پیکربندی اختیاری format سفارشیسازی شود که یک رشته الگو را مشخص میکند و در آن @@{max} (یا بهطور خلاصه @@) با مقدار عددی YYMAXFILL جایگزین میشود.
- /*!maxnmatch:re2c[:<name1>[:<name2>...]] ... */ or %{maxnmatch[:<name1>[:<name2>...]] ... %}
- بلوکی که تعریف YYMAXNMATCH را تولید میکند (نیازمند گزینه -P --posix-captures است). یک فهرست اختیاری از نام بلوکها مشخص میکند هنگام محاسبه مقدار YYMAXNMATCH کدام بلوکها باید گنجانده شوند (اگر فهرست خالی باشد، همه بلوکها گنجانده میشوند). بهطور پیشفرض کد تولیدشده یک تعریف ماکرو برای C (#define YYMAXNMATCH <n>) یا یک متغیر سراسری برای Go (var YYMAXNMATCH int = <n>) است. این مورد میتواند با پیکربندی اختیاری format سفارشیسازی شود که یک رشته الگو را مشخص میکند و در آن @@{max} (یا بهطور خلاصه @@) با مقدار عددی YYMAXNMATCH جایگزین میشود.
- /*!stags:re2c[:<name1>[:<name2>...]] ... */, /*!mtags:re2c[:<name1>[:<name2>...]] ... */ or %{stags[:<name1>[:<name2>...]] ... %}, %{mtags[:<name1>[:<name2>...]] ... %{
- بلوکهایی که یک قطعه کد الگو را مشخص میکنند که برای هر متغیر s-tag/m-tag تولیدشده توسط re2hs بسط داده میشود. یک فهرست اختیاری از نام بلوکها مشخص میکند که هنگام محاسبه مجموعه متغیرهای تگ کدام بلوکها باید گنجانده شوند (اگر فهرست خالی باشد، تمام بلوکها لحاظ میشوند). دو پیکربندی اختیاری وجود دارد: format و separator. پیکربندی format یک رشته الگو را مشخص میکند که در آن @@{tag} (یا بهطور خلاصه @@) با نام هر متغیر تگ جایگزین میشود. پیکربندی separator قطعه کدی را مشخص میکند که برای اتصال قطعات format تولیدشده برای متغیرهای مختلف تگ به کار میرود.
- /*!svars:re2c[:<name1>[:<name2>...]] ... */, /*!mvars:re2c[:<name1>[:<name2>...]] ... */ or %{svars[:<name1>[:<name2>...]] ... %}, %{mvars[:<name1>[:<name2>...]] ... %{
- بلوکهایی که یک قطعه کد الگو را مشخص میکنند که برای هر s-tag/m-tag که صریحاً توسط قواعد ذکر شده (با گزینه --tags) یا بهطور ضمنی توسط re2hs تولید شده است (با گزینههای --captvars یا --posix-captvars) بسط مییابد. یک فهرست اختیاری از نام بلوکها مشخص میکند هنگام محاسبه مجموعه تگها کدام بلوکها باید گنجانده شوند (اگر فهرست خالی باشد، تمام بلوکها لحاظ میشوند). دو پیکربندی اختیاری وجود دارد: format و separator. پیکربندی format یک رشته الگو را مشخص میکند که در آن @@{tag} (یا بهطور خلاصه @@) با نام هر تگ جایگزین میشود. پیکربندی separator قطعه کدی را مشخص میکند که برای متصل ساختن قطعات format تولیدشده برای تگهای مختلف استفاده میشود.
- /*!getstate:re2c[:<name1>[:<name2>...]] ... */ or %{getstate[:<name1>[:<name2>...]] ... %}
- بلوکی که ارسال شرطی (conditional dispatch) را بر اساس وضعیت لکسر تولید میکند (نیازمند گزینه --storable-state است). یک فهرست اختیاری از نام بلوکها مشخص میکند که کدام بلوکها باید در ارسال وضعیت گنجانده شوند. انتقال پیشفرض به برچسب آغازین اولین بلوک موجود در فهرست میرود. اگر فهرست خالی باشد، تمام بلوکها گنجانده میشوند و انتقال پیشفرض به اولین بلوک در فایل که برچسب شروع دارد میرود. این نوع بلوک با گزینه --loop-switch ناسازگار است، چرا که نیازمند انتقالهای میانبلوکی (cross-block) است که بدون goto یا فراخوانیهای تابع پشتیبانی نمیشوند.
- /*!conditions:re2c[:<name1>[:<name2>...]] ... */, /*!types:re2c... */ or %{conditions[:<name1>[:<name2>...]] ... %}, %{types... %}
- بلوکی که نوع شمارشی شرط (condition enumeration) را تولید میکند (نیازمند گزینه --conditions است). یک فهرست اختیاری از نام بلوکها مشخص میکند که هنگام محاسبه مجموعه شرطها کدام بلوکها باید لحاظ شوند (اگر فهرست خالی باشد، تمام بلوکها لحاظ میشوند). بهطور پیشفرض کد تولیدشده یک شمارش YYCONDTYPE است. این مورد میتواند با پیکربندیهای اختیاری format و separator سفارشیسازی شود. پیکربندی format یک رشته الگو را تعیین میکند که در آن @@{cond} (یا بهطور خلاصه @@) با نام هر شرط و @@{num} با اندیس عددی آن شرط جایگزین میشود. پیکربندی separator قطعه کدی را مشخص میکند که برای اتصال قطعات format تولیدشده برای شرطهای گوناگون استفاده میشود.
- /*!include:re2c <file> */ or %{include <file> %}
- این بلوک اجازه میدهد <file> اضافه شود، که باید یک مسیر فایل درون گیومه دوتایی باشد. محتویات فایل بهطور مستقیم در جایگاه این بلوک قرار میگیرد، درست همانگونه که #include در C/C++ کار میکند. این بلوک میتواند همراه با گزینه --depfile برای تولید وابستگیهای سیستم ساخت (build system) به فایلهای پیوستشده به کار رود.
- /*!header:re2c:on*/ or %{header:on %}
- این بلوک شروع فایل هدر را مشخص میکند. همه چیز پس از آن و تا بلوک بعدی header:off توسط re2hs پردازش شده و در فایل هدر مشخصشده با گزینه -t --type-header نوشته میشود.
- /*!header:re2c:off*/ or %{header:off %}
- این بلوک پایان فایل هدر را که با بلوک header:on*/ آغاز شده بود مشخص میکند.
- /*!ignore:re2c ... */ or %{ignore ... %}
- بلوکی که
محتویات آن
نادیده
گرفته شده و
از فایل
خروجی حذف
میشود.
در ادامه فهرست کاملی از پیکربندیهای پشتیبانیشده توسط re2hs آمده است:
- 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) ارائه میشوند، نه برای زمینهٔ کلی تحلیلگر لغوی (lexer)؛ بنابراین اولیهها رفتاری شبیه به ماکروها در C/C++ یا بستارها (closures) در Go و Rust دارند. در سبک free-form، اولیههای API شکل ثابتی ندارند: آنها باید به عنوان رشتههایی حاوی قطعهکدهای آزاد با متغیرهای الحاقی (interpolated) به شکل @@{var} یا @@ تعریف شوند (اینها متناظر با آرگومانها در سبک تابعگونه هستند). این پیکربندی ممکن است برای اولیههای جداگانهٔ API بازنویسی شود؛ برای نمونه به پیکربندی re2c:YYFILL:naked برای YYFILL مراجعه کنید.
- re2c:bit-vectors, re2c:flags:bit-vectors, re2c:flags:b
- مشابه گزینهٔ --bit-vectors، اما میتواند بر پایهٔ هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:captures, re2c:leftmost-captures
- مشابه گزینهٔ --leftmost-captures، اما میتواند بر پایهٔ هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:captvars, re2c:leftmost-captvars
- مشابه گزینهٔ --leftmost-captvars، اما میتواند بر پایهٔ هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:case-insensitive, re2c:flags:case-insensitive
- مشابه گزینهٔ --case-insensitive، اما میتواند بر پایهٔ هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:case-inverted, re2c:flags:case-inverted
- مشابه گزینهٔ --case-inverted، اما میتواند بر پایهٔ هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:case-ranges, re2c:flags:case-ranges
- مشابه گزینهٔ --case-ranges، اما میتواند بر پایهٔ هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:computed-gotos, re2c:flags:computed-gotos, re2c:flags:g
- مشابه گزینهٔ --computed-gotos، اما میتواند بر پایهٔ هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:computed-gotos:relative, re2c:cgoto:relative
- مشابه گزینهٔ --computed-gotos-relative، اما میتواند بر پایهٔ هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:computed-gotos:threshold, re2c:cgoto:threshold
- اگر از gotoی محاسبهشده استفاده شود، این پیکربندی آستانهٔ پیچیدگی را مشخص میکند که باعث تولید جدولهای پرش به جای دستورهای تودرتوی if و نگاشتهای بیتی (bitmaps) میشود. مقدار پیشفرض 9 است.
- re2c:cond:abort
- اگر روی یک مقدار عدد صحیح مثبت تنظیم شود، حالت پیشفرض (default case) در توزیع شرط تولیدشده، اجرای برنامه را متوقف (abort) میکند.
- re2c:cond:goto
- قطعهکد مورد استفاده برای قواعد میانبر خودکار تولیدشدهٔ :=> در شرطها را مشخص میکند. مقدار پیشفرض goto @@; است. جاینگهدار @@ با نام شرط جایگزین میشود (پیکربندیهای re2c:api:sigil و re2c:cond:goto@cond را ببینید).
- re2c:cond:goto@cond
- نشانگر مورد استفاده برای جایگزینی آرگومان در تعریف re2c:cond:goto را مشخص میکند. مقدار پیشفرض @@ است. پیکربندی عمومیتر re2c:api:sigil را لغو و بازنویسی میکند.
- re2c:cond:divider
- جداکنندهٔ بلوکهای شرط را تعیین میکند. مقدار پیشفرض /* *********************************** */ است. جاینگهدارها با نام شرط جایگزین میشوند (re2c:api;sigil و re2c:cond:divider@cond را ببینید).
- re2c:cond:divider@cond
- نشانگر مورد استفاده برای جایگزینی آرگومان در تعریف re2c:cond:divider را مشخص میکند. مقدار پیشفرض @@ است. پیکربندی عمومیتر re2c:api:sigil را لغو و بازنویسی میکند.
- re2c:cond:prefix, re2c:condprefix
- پیشوند مورد استفاده برای برچسبهای شرط را مشخص میکند. مقدار پیشفرض yyc_ است.
- re2c:cond:enumprefix, re2c:condenumprefix
- پیشوند مورد استفاده برای شناسههای شرط را مشخص میکند. مقدار پیشفرض yyc است.
- re2c:debug-output, re2c:flags:debug-output, re2c:flags:d
- مشابه گزینهٔ --debug-output، اما میتواند بر پایهٔ هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:empty-class, re2c:flags:empty-class
- مشابه گزینهٔ --empty-class، اما میتواند بر پایهٔ هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:encoding:ebcdic, re2c:flags:ecb, re2c:flags:e
- مشابه گزینهٔ --ebcdic، اما میتواند بر پایهٔ هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:encoding:ucs2, re2c:flags:wide-chars, re2c:flags:w
- مشابه گزینهٔ --ucs2، اما میتواند بر پایهٔ هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:encoding:utf8, re2c:flags:utf-8, re2c:flags:8
- مشابه گزینهٔ --utf8، اما میتواند بر پایهٔ هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:encoding:utf16, re2c:flags:utf-16, re2c:flags:x
- مشابه گزینهٔ --utf16، اما میتواند بر پایهٔ هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:encoding:utf32, re2c:flags:unicode, re2c:flags:u
- مشابه گزینهٔ --utf32، اما میتواند بر پایهٔ هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:encoding-policy, re2c:flags:encoding-policy
- مشابه گزینهٔ --encoding-policy، اما میتواند بر پایهٔ هر بلوک بهطور جداگانه پیکربندی شود.
- re2c:eof
- نماد نگهبان (sentinel symbol) مورد استفاده با قاعدهٔ پایان ورودی $ را مشخص میکند. مقدار پیشفرض -1 است (قاعدهٔ $ استفاده نمیشود). سایر مقادیر ممکن شامل تمام واحدهای کد معتبر است. تنها اعداد دهدهی شناسایی میشوند.
- re2c:header, re2c:flags:type-header, re2c:flags:t
- نام فایل سرآیند تولیدشده را به صورت نسبی نسبت به دایرکتوری فایل خروجی مشخص میکند. مشابه گزینهٔ --header با این تفاوت که مسیر فایل نسبی است.
- re2c:indent:string
- رشتهٔ مورد استفاده برای تورفتگی را مشخص میکند. مقدار پیشفرض یک نویسهٔ تب منفرد "\t" است. رشتهٔ تورفتگی باید تنها شامل نویسههای فاصله خالی (whitespace) باشد. برای غیرفعال کردن کامل تورفتگی، این پیکربندی را روی یک رشتهٔ خالی تنظیم کنید.
- 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 و در حالت وضعیت قابلذخیره (storable state) را مشخص میکند.
- re2c:label:yyloop
- نام برچسب نشاندهندهٔ شروع حلقهٔ تحلیلگر لغوی با گزینهٔ --loop-switch را مشخص میکند. مقدار پیشفرض yyloop است.
- re2c:label:yyNext
- نام برچسب اختیاری را مشخص میکند که پس از سوییچ YYGETSTATE در حالت وضعیت قابلذخیره میآید (فعالشده با re2c:state:nextlabel). مقدار پیشفرض yyNext است.
- re2c:lookahead, re2c:flags:lookahead
- منسوخ شده است (گزینهٔ منسوخشدهٔ --no-lookahead را ببینید).
- re2c:monadic
- اگر روی مقداری غیر از صفر تنظیم شود، تحلیلگر لغوی تولیدشده از نشانهگذاری مونادیک استفاده خواهد کرد (این پیکربندی مخصوص Haskell است)..TP re2c:nested-ifs, re2c:flags:nested-ifs, re2c:flags:s همانند گزینهٔ --nested-ifs است، اما میتواند بر مبنای هر بلوک پیکربندی شود.
- re2c:posix-captures, re2c:flags:posix-captures, re2c:flags:P
- همانند گزینهٔ --posix-captures است، اما میتواند بر مبنای هر بلوک پیکربندی شود.
- re2c:posix-captvars
- همانند گزینهٔ --posix-captvars است، اما میتواند بر مبنای هر بلوک پیکربندی شود.
- همانند گزینهٔ --tags است، اما میتواند بر مبنای هر بلوک پیکربندی شود.
- عبارت مورد استفاده برای متغیرهای برچسب را مشخص میکند. بهطور پیشفرض re2hs عبارتهایی به شکل yyt<N> تولید میکند. این امر ممکن است ناخوشایند باشد، برای مثال اگر متغیرهای برچسب به عنوان فیلدهایی درون یک struct تعریف شده باشند. تمام رخدادهای @@{tag} یا @@ با نام واقعی برچسب جایگزین میشوند. برای نمونه، re2c:tags:expression = "s.@@"; منجر به عباراتی به شکل s.yyt<N> در کد تولیدشده میگردد. همچنین پیکربندی re2c:api:sigil را ببینید.
- عبارت ثابتی را مشخص میکند که برای مقدار برچسب منفی استفاده میشود (معمولاً در صورتی که برچسبها آفستهای صحیح در رشتهٔ ورودی باشند این مقدار -1، یا اگر اشارهگر باشند اشارهگر null خواهد بود).
- پیشوند نام متغیرهای برچسب را مشخص میکند. مقدار پیشفرض yyt است.
- re2c:sentinel
- نماد نگهبان (sentinel) مورد استفاده برای بررسیهای پایان ورودی (زمانی که بررسیهای مرزی با re2c:yyfill:enable = 0; غیرفعال شده و re2c:eof تنظیم نشده باشد) را مشخص میکند. این پیکربندی تأثیری بر تولید کد ندارد: هدف آن حصول اطمینان از مجاز نبودن نماد نگهبان در میانهٔ یک قاعده، و تضمین این است که تحلیلگر لغوی فراتر از پایان بافر را نخواند. مقدار پیشفرض -1` است (در این حالت re2hs فرض میکند که نماد نگهبان صفر است، که رایجترین حالت میباشد). فقط اعداد دهدهی شناسایی میشوند.
- re2c:state:abort
- اگر روی یک مقدار عدد صحیح مثبت تنظیم شود، حالت پیشفرض (default case) در هدایت وضعیت تولیدشده، اجرای برنامه را لغو (abort) میکند و یک حالت صریح -1 شامل انتقال به ابتدای بلوک خواهد بود.
- re2c:state:nextlabel
- کنترل میکند که آیا سوییچ YYGETSTATE با برچسب yyNext دنبال شود یا خیر (مقدار پیشفرض صفر است که متناظر با نبود برچسب است). بهعنوان روشی جایگزین میتوان از re2c:label:start برای تولید یک برچسب شروع مشخص، یا یک بلوک صریح getstate برای تولید سوییچ YYGETSTATE بهطور جداگانه از بلوک لکسر استفاده کرد.
- re2c:unsafe, re2c:flags:unsafe
- همانند گزینهٔ --no-unsafe است، اما میتواند بر مبنای هر بلوک پیکربندی شود. اگر روی صفر تنظیم شود، از تولید پوششهای unsafe در اطراف YYPEEK جلوگیری میکند. مقدار پیشفرض غیرصفر است (پوششها تولید میشوند). این پیکربندی مختص به Rust است.
- re2c:YYBACKUP, re2c:define:YYBACKUP
- پریمیتیو عمومی API به نام YYBACKUP را تعریف میکند.
- re2c:YYBACKUPCTX, re2c:define:YYBACKUPCTX
- پریمیتیو عمومی API به نام YYBACKUPCTX را تعریف میکند.
- re2c:YYCONDTYPE, re2c:define:YYCONDTYPE
- پریمیتیو API به نام YYCONDTYPE را تعریف میکند.
- re2c:YYCTYPE, re2c:define:YYCTYPE
- پریمیتیو API به نام YYCTYPE را تعریف میکند.
- re2c:YYCTXMARKER, re2c:define:YYCTXMARKER
- پریمیتیو API به نام YYCTXMARKER را تعریف میکند.
- re2c:YYCURSOR, re2c:define:YYCURSOR
- پریمیتیو API به نام YYCURSOR را تعریف میکند.
- re2c:YYDEBUG, re2c:define:YYDEBUG
- پریمیتیو API به نام YYDEBUG را تعریف میکند.
- re2c:YYFILL, re2c:define:YYFILL
- پریمیتیو API به نام YYFILL را تعریف میکند.
- re2c:YYFILL@len, re2c:define:YYFILL@len
- علامت (sigil) مورد استفاده برای جایگزینی آرگومان در تعریف YYFILL را مشخص میکند. پیشفرض @@ است. پیکربندی عمومیتر re2c:api:sigil را بازنویسی میکند.
- re2c:YYFILL:naked, re2c:define:YYFILL:naked
- پیکربندی عمومیتر re2c:api:style را برای YYFILL بازنویسی میکند. مقدار صفر متناظر با سبک آزاد API (free-form) است.
- re2c:YYFN
- پریمیتیو API به نام YYFN را تعریف میکند.
- re2c:YYINPUT
- پریمیتیو API به نام YYINPUT را تعریف میکند.
- re2c:YYGETCOND, re2c:define:YYGETCONDITION
- پریمیتیو API به نام YYGETCOND را تعریف میکند.
- re2c:YYGETCOND:naked, re2c:define:YYGETCONDITION:naked
- پیکربندی عمومیتر re2c:api:style را برای YYGETCOND بازنویسی میکند. مقدار صفر متناظر با سبک آزاد API است.
- 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 را تعریف میکند..TP re2c:YYSHIFTMTAG, re2c:define:YYSHIFTMTAG عنصر پایهٔ API عمومی YYSHIFTMTAG را تعریف میکند.
- re2c:YYSHIFTSTAG, re2c:define:YYSHIFTSTAG
- عنصر پایهٔ API عمومی YYSHIFTSTAG را تعریف میکند.
- re2c:YYSTAGN, re2c:define:YYSTAGN
- عنصر پایهٔ API عمومی YYSTAGN را تعریف میکند.
- re2c:YYSTAGP, re2c:define:YYSTAGP
- عنصر پایهٔ API عمومی YYSTAGP را تعریف میکند.
- re2c:yyaccept, re2c:variable:yyaccept
- عنصر پایهٔ API با نام yyaccept را تعریف میکند.
- re2c:yybm, re2c:variable:yybm
- عنصر پایهٔ API با نام yybm را تعریف میکند.
- re2c:yybm:hex, re2c:variable:yybm:hex
- اگر روی مقداری غیر از صفر تنظیم شود، نگاشتهای بیتی برای گزینهٔ --bit-vectors در قالب هگزادسیمال تولید میشوند. پیشفرض صفر است (نگاشتهای بیتی در قالب دهدهی هستند).
- re2c:yych, re2c:variable:yych
- عنصر پایهٔ API با نام yych را تعریف میکند.
- re2c:yych:emit, re2c:variable:yych:emit
- اگر روی صفر تنظیم شود، تعریف yych تولید نمیشود. پیشفرض عددی غیر صفر است.
- re2c:yych:conversion, re2c:variable:yych:conversion
- اگر روی مقداری غیر از صفر تنظیم شود، re2hs با هر بار خواندن yych، بهطور خودکار تبدیل به YYCTYPE را تولید میکند. پیشفرض صفر است (بدون تبدیل).
- re2c:yych:literals, re2c:variable:yych:literals
- شکل مقادیر لفظی را که yych در برابر آنها تطبیق داده میشود مشخص میکند. مقادیر ممکن عبارتاند از: char (نویسههای لفظی در گیومه تکی؛ نویسههای غیرقابلچاپ از توالیهای گریز که با بکاسلش آغاز میشوند استفاده میکنند)، hex (اعداد صحیح هگزادسیمال) و char_or_hex (ترکیبی از هر دو؛ نویسههای لفظی برای نویسههای قابلچاپ و اعداد صحیح هگزادسیمال برای سایر موارد).
- re2c:yyctable, re2c:variable:yyctable
- عنصر پایهٔ API با نام yyctable را تعریف میکند.
- re2c:yynmatch, re2c:variable:yynmatch
- عنصر پایهٔ API با نام yynmatch را تعریف میکند.
- re2c:yypmatch, re2c:variable:yypmatch
- عنصر پایهٔ API با نام yypmatch را تعریف میکند.
- re2c:yytarget, re2c:variable:yytarget
- عنصر پایهٔ API با نام yytarget را تعریف میکند.
- re2c:yystable, re2c:variable:yystable
- منسوخ شده است.
- re2c:yystate, re2c:variable:yystate
- عنصر پایهٔ API با نام yystate را تعریف میکند.
- re2c:yyfill, re2c:variable:yyfill
- عنصر پایهٔ API با نام yyfill را تعریف میکند.
- re2c:yyfill:check
- اگر روی صفر تنظیم شود، از تولید بررسی پیش از YYFILL برای تعداد نویسههای ورودی (تعریف YYLESSTHAN در API عمومی و مقایسهٔ مبتنی بر YYLIMIT در API اشارهگر C) جلوگیری میکند. پیشفرض مقداری غیر از صفر است (بررسی تولید میشود).
- re2c:yyfill:enable
- اگر روی صفر تنظیم شود، از تولید YYFILL (بههمراه بررسی مربوط به آن) جلوگیری میکند. این تنظیم زمانی باید استفاده شود که تمام ورودی در یک بخش پیوسته از حافظه جا میگیرد (نیازی به میانگیرسازی نیست) و بررسیهای پایان ورودی به بررسیهای YYFILL متکی نیستند (برای نمونه هنگامی که از نویسهٔ نگهبان استفاده میشود). از هشدارها (گزینهٔ -W) و پیکربندی re2c:sentinel استفاده کنید تا مطمئن شوید تحلیلگر واژگانی تولیدشده نمیتواند فراتر از پایان ورودی را بخواند. پیشفرض مقداری غیر از صفر است (YYFILL فعال است).
- re2c:yyfill:parameter
- اگر روی صفر تنظیم شود، از تولید پارامتر ارسالی به YYFILL جلوگیری میکند. این پارامتر نشاندهندهٔ حداقل تعداد نویسههایی است که باید فراهم شوند. پیشفرض مقداری غیر از صفر است (پارامتر تولید میشود). این پیکربندی میتواند توسط re2c:YYFILL:naked یا re2c:api:style بازنویسی شود.
- re2c:yyfn:sep
- جداکنندهٔ استفادهشده در عناصر YYFN را مشخص میکند (پیشفرض نقطهویرگول است).
- re2c:yyfn:throw
- استثناهای
پرتابشده
توسط تابع
YYFN را مشخص
میکند
(پیشفرض
خالی است،
که به معنای
عدم وجود
هرگونه
استثناست).
ابزار re2hs از ساختار دستوری زیر برای عبارات باقاعده استفاده میکند:
- "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؛ از پرانتزها برای لغو و تغییر اولویت استفاده میشود. اگر استخراج زیرتطابق فعال باشد، بسته به گزینهٔ --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، \\، گریزهای هشتهشتی (octal) \ooo و گریزهای شانزدهشانزدهی (hexadecimal) \xhh، \uhhhh و \Uhhhhhhhh.
کنشها (Actions)
در اینجا فهرستی از کنشهای از پیش تعریفشده که توسط re2hs پشتیبانی میشوند آمده است:
- !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) به
منظور
محافظت در
برابر
جریان
کنترل
ناخواسته
استفاده
نمود.
در اینجا فهرست کاملی از دستورالعملهای پشتیبانیشده توسط re2hs آمده است:
- !use:name ;
- یک دستورالعمل use درونبلوکی که یک بلوک قواعد از قبل تعریفشده با name مشخص را در بلوک فعلی ادغام میکند. تعاریف نامگذاریشده، پیکربندیها و قواعد بلوک ارجاعدادهشده به موارد موجود در بلوک فعلی افزوده میشوند. تداخل میان قواعد و پیکربندیهای همپوشان به روش معمول برطرف میشود: اولین قاعده اولویت دارد، و آخرین پیکربندی بر موارد قبلی اعمال میشود (آنها را بازنویسی میکند). یک استثنا قواعد ویژهٔ *، $ و <!> هستند که برای آنها تعریف محلیِ بلوک همواره اولویت دارد. یک دستورالعمل use میتواند در هر کجای داخل یک بلوک قرار گیرد، و استفاده از چندین دستورالعمل use نیز مجاز است.
- !include file ;
- این دستورالعمل همانند بلوک include است: محتویات file را عیناً و بدون تغییر در محل دستورالعمل درج میکند.
رابط برنامه (Program interface)
کد تولیدشده به کمک primitives (عناصر پایه) که در مجموع با نام API شناخته میشوند، با برنامهٔ بیرونی ارتباط برقرار میکند. اینکه کدام عناصر پایه باید برای یک برنامهٔ خاص تعریف شوند به عوامل متعددی بستگی دارد، از جمله پیچیدگی عبارات باقاعده، نحوهٔ نمایش ورودی، بافرینگ و استفاده از قابلیتهای گوناگون. تمام عناصر پایهٔ لازم باید توسط کاربر در قالب ماکروها، توابع، متغیرها یا هر شکل مناسب دیگری که کد تولیدشده را از نظر نحوی و معنایی درست کند، تعریف شوند. re2hs تعاریف را بررسی نمیکند (و نمیتواند بکند)، بنابراین در صورت نبود یا نادرست تعریف شدن هر مورد، برنامهٔ تولیدشده ممکن است با خطاهای زمان کامپایل یا زمان اجرا مواجه شود. این راهنما مثالهایی از تعاریف API در رایجترین حالتها ارائه میدهد.
ابزار re2hs دارای دو نوع رابط (API flavor) است که مجموعهٔ اصلی عناصر پایهٔ مورد استفادهٔ برنامه را مشخص میکنند:
- Record API
- رابط Record API رابط پیشفرض برای backend زبان Haskell است. این API شامل یک مقیدسازی (binding) به نام yyrecord است (نام آن با re2c:yyrecord قابل بازنویسی است) که باید به صورت یک رکورد با فیلدهای _yyinput، _yycursor، _yymarker، _yyctxmarker و _yylimit تعریف شود. تنها فیلدهایی که توسط کد تولیدشده استفاده میشوند نیاز به تعریف دارند، و نامهای آنها قابل پیکربندی است.
- Generic API
- این منعطفترین API است. این حالت با گزینهٔ --api generic یا پیکربندی re2c:api = generic فعال میشود. این رابط شامل عناصر پایه برای عملیات عمومی است: YYPEEK، YYSKIP، YYBACKUP، YYBACKUPCTX، YYSTAGP، YYSTAGN، YYMTAGP، YYMTAGN، YYRESTORE، YYRESTORECTX، YYRESTORETAG، YYCOPYSTAG، YYCOPYMTAG، YYSHIFT، YYSHIFTSTAG، YYSHIFTMTAG، YYLESSTHAN، YYEND.
در اینجا فهرست کاملی از عناصر پایهٔ API آمده است که ممکن است توسط کد تولیدشده جهت ارتباط با برنامهٔ بیرونی استفاده شوند.
- YYCTYPE
- نوع نویسههای ورودی (واحدهای کد یا code units). برای کدگذاریهای ASCII، EBCDIC و UTF-8 باید یک عدد صحیح بدون علامت ۱ بایتی باشد. برای UTF-16 یا UCS-2 باید یک عدد صحیح بدون علامت ۲ بایتی باشد. برای UTF-32 باید یک عدد صحیح بدون علامت ۴ بایتی باشد.
- YYCURSOR
- یک مقدار چپ (l-value) که موقعیت ورودی فعلی را ذخیره میکند (یک اشارهگر یا آفست عدد صحیح در YYINPUT). در ابتدا YYCURSOR باید به نخستین نویسهٔ ورودی اشاره کند، و در ادامه توسط کد تولیدشده به جلو برده میشود. هنگامی که یک قاعده مطابقت مییابد، موقعیت YYCURSOR در مکان پس از آخرین نویسهٔ منطبقشده قرار میگیرد.
- YYLIMIT
- یک مقدار راست (r-value) که موقعیت پایان ورودی را ذخیره میکند (یک اشارهگر یا آفست عدد صحیح در YYINPUT). در ابتدا YYLIMIT باید به موقعیت پس از آخرین نویسهٔ ورودی موجود اشاره کند. این مقدار توسط کد تولیدشده تغییر نمییابد. تحلیلگر لغوی YYCURSOR را با YYLIMIT مقایسه میکند تا تشخیص دهد آیا نویسههای ورودی به اندازهٔ کافی باقی ماندهاند یا خیر.
- YYMARKER
- یک مقدار چپ (l-value) که موقعیت آخرین قاعدهٔ منطبقشده را ذخیره میکند (یک اشارهگر یا آفست عدد صحیح در YYINPUT). از آن برای بازیابی موقعیت YYCURSOR در صورتی استفاده میشود که تطابق طولانیتر شکست بخورد و تحلیلگر لغوی نیاز به بازگشت به عقب (rollback) داشته باشد. نیازی به مقداردهی اولیه نیست.
- YYCTXMARKER
- یک مقدار چپ (l-value) که موقعیت بافت پسآیند (trailing context) را ذخیره میکند (یک اشارهگر یا آفست عدد صحیح در YYINPUT). نیازی به مقداردهی اولیه نیست. YYCTXMARKER تنها در صورتی مورد نیاز است که عملگر پیشنگری / به کار رفته باشد.
- YYFILL
- یک عنصر پایه در API عمومی با یک متغیر len. YYFILL باید دستکم len نویسهٔ ورودی دیگر را تأمین کند یا با شکست مواجه شود. اگر از re2c:eof استفاده شده باشد، آنگاه len همیشه 1 است و YYFILL باید همواره به تابع فراخواننده بازگردد؛ مقدار بازگشتی صفر نشاندهندهٔ موفقیت است. اگر از re2c:eof استفاده نشود، مقدار بازگشتی YYFILL نادیده گرفته میشود و در صورت شکست نباید بازگردد. بیشینهٔ مقدار len برابر با YYMAXFILL است.
- YYFN
- عنصری پایه که پیشنمونهٔ تابع را در مدل کد --recursive-functions تعریف میکند. مقدار آن باید آرایهای از یک یا چند رشته باشد، که در آن هر رشته شامل دو یا سه مؤلفه است که توسط رشتهٔ مشخصشده در پیکربندی re2c:fn:sep (معمولاً یک نقطهویرگول) جدا شدهاند. نخستین عنصر آرایه نام تابع و نوع بازگشتی را مشخص میکند (برای یک تابع تهی یا void، خالی است). عناصر بعدی آرگومانهای تابع را تعریف میکنند: نخست، عبارت مربوط به آرگومان مورد استفاده در بدنهٔ تابع (معمولاً فقط یک نام)؛ دوم، نوع آرگومان؛ سوم، یک پارامتر رسمی اختیاری (بهطور پیشفرض همان مؤلفهٔ اول است - معمولاً هر دو آرگومان و پارامتر یک شناسه هستند).
- YYINPUT
- یک مقدار راست (r-value) که دنبالهٔ نویسههای ورودی فعلی (رشته، بافر، و غیره) را ذخیره میکند.
- YYMAXFILL
- یک ثابت عدد صحیح برابر با بیشینهٔ مقدار آرگومان ورودی به YYFILL. این مقدار میتواند با یک بلوک max تولید شود.
- YYLESSTHAN
- یک عنصر پایه در API عمومی با یک متغیر len. باید به عنوان یک مقدار راست (r-value) از نوع بولی تعریف شود که اگر و تنها اگر کمتر از 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 را روی موقعیت ورودی فعلی تنظیم کند..TP YYSTAGN یک دستور پایه عمومی API با یک متغیر tag، که در آن tag میتواند یک اشارهگر یا آفست در YYINPUT باشد (برای جزئیات به بخش استخراج تطابقهای فرعی مراجعه کنید). YYSTAGN باید tag را روی مقداری تنظیم کند که بیانگر یک موقعیت ورودی ناموجود باشد.
- YYMTAGP
- یک دستور پایه عمومی API با یک متغیر tag. YYMTAGP باید موقعیت فعلی را به تاریخچه تطابقهای فرعیِ tag اضافه کند (برای جزئیات به بخش استخراج تطابقهای فرعی مراجعه کنید).
- YYMTAGN
- یک دستور پایه عمومی API با یک متغیر tag. YYMTAGN باید مقداری را که بیانگر یک موقعیت ورودی ناموجود است به تاریخچه تطابقهای فرعیِ tag اضافه کند (برای جزئیات به بخش استخراج تطابقهای فرعی مراجعه کنید).
- YYSHIFT
- یک دستور پایه عمومی API با یک متغیر shift که باید موقعیت ورودی فعلی را به اندازه shift نویسه تغییر دهد (مقدار تغییر میتواند منفی باشد).
- YYCOPYSTAG
- یک دستور پایه عمومی API با دو متغیر، lhs و rhs، که باید متغیر سمت راستِ s-tag یعنی rhs را در متغیر سمت چپِ s-tag یعنی lhs کپی کند. برای بیشتر زبانها این دستور پایه تعریفی پیشفرض دارد که lhs را برابر با rhs قرار میدهد.
- YYCOPYMTAG
- یک دستور پایه عمومی API با دو متغیر، lhs و rhs، که باید متغیر سمت راستِ m-tag یعنی rhs را در متغیر سمت چپِ m-tag یعنی lhs کپی کند. برای بیشتر زبانها این دستور پایه تعریفی پیشفرض دارد که lhs را برابر با rhs قرار میدهد.
- YYSHIFTSTAG
- یک دستور پایه عمومی API با دو متغیر، tag و shift، که باید tag را به اندازه shift واحد کد (code unit) انتقال دهد (مقدار انتقال میتواند منفی باشد).
- YYSHIFTMTAG
- یک دستور پایه عمومی API با دو متغیر، tag و shift، که باید آخرین مقدار در تاریخچه tag را به اندازه shift واحد کد انتقال دهد (مقدار انتقال میتواند منفی باشد).
- YYMAXNMATCH
- یک ثابت عددی صحیح برابر با حداکثر تعداد گروههای تسخیر (capturing groups) استاندارد POSIX در یک قاعده. این مقدار با یک بلوک maxnmatch تولید میشود.
- YYCONDTYPE
- نوع شمارش (enum) شرط. این مقدار میتواند با بلوک conditions یا گزینه --header تولید شود.
- YYGETACCEPT
- یک دستور پایه با یک متغیر var که انتخابگر عددی قاعده پذیرفتهشده را ذخیره میکند. برای بیشتر زبانها این دستور پایه تعریفی پیشفرض دارد که از var میخواند.
- YYSETACCEPT
- یک دستور پایه با دو متغیر: var (یک l-value که انتخابگر عددی قاعده پذیرفتهشده را ذخیره میکند)، و val (مقدار انتخابگر). برای بیشتر زبانها این دستور پایه تعریفی پیشفرض دارد که val را به var انتساب میدهد.
- YYGETCOND
- یک r-value از نوع YYCONDTYPE که برابر با شناسه شرط فعلی است.
- YYSETCOND
- یک دستور پایه با یک متغیر cond که باید شناسه شرط فعلی را به cond تنظیم کند.
- YYGETSTATE
- یک r-value از نوع عدد صحیح که برابر با وضعیت فعلی تحلیلگر لغوی (lexer) است. این متغیر باید با -1 مقداردهی اولیه شود.
- YYSETSTATE
- یک دستور پایه با یک متغیر state که باید وضعیت فعلی تحلیلگر لغوی را به state تنظیم کند.
- YYDEBUG
- این دستور پایه تنها با گزینه -d، --debug-output تولید میشود. هدف آن افزودن ثبت وقایع (لاگین) به کد تولید شده است (تعریف مرسوم YYDEBUG یک دستور چاپ است). عبارتهای YYDEBUG در هر وضعیت تولید میشوند و دارای دو متغیر هستند: state (نمایه وضعیت DFA یا -1) و symbol (نماد ورودی فعلی).
- yyaccept
- یک l-value از نوع عددی صحیح بدون علامت که شماره آخرین قاعده تطبیقیافته را ذخیره میکند. تعریف توسط کاربر تنها با گزینه --storable-state ضروری است.
- yybm
- جدولی شامل نگاشتهای بیتی فشردهشده برای حداکثر ۸ گذار (همراه با گزینه --bitmaps استفاده میشود). این جدول شامل ۲۵۶ عنصر است و با واحدهای کد ۱ بایتی اندیسگذاری میشود. هر عنصر ۸ بیتی مقادیر بولی را برای حداکثر ۸ گذار ترکیب میکند. بیت k-ام از عنصر n-ام درست (true) است اگر و تنها اگر n-امین واحد کد در بازه k-امین گذار قرار داشته باشد. ایده این نگاشت بیتی جایگزینی چندین شاخه if یا گزینههای switch با بررسی یک تکبیت در جدول است.
- yych
- یک l-value از نوع YYCTYPE که نویسه ورودی فعلی را ذخیره میکند. تعریف توسط کاربر تنها با گزینه -f --storable-state الزامی است.
- yyctable
- جدول پرش تولیدشده برای ارجاع شرط اولیه (فعالشده با ترکیب گزینههای --conditions و --computed-gotos).
- yyfill
- یک l-value که نتیجه فراخوانی YYFILL را ذخیره میکند (این مورد ممکن است برای زبانهای کاملاً تابعی، که در آنها YYFILL تابعی مونادیک با مقدار بازگشتی پیچیده است، ضروری باشد).
- yynmatch
- یک l-value از نوع عددی صحیح بدون علامت که تعداد گروههای تسخیر POSIX را در قاعده تطبیقیافته ذخیره میکند. تنها با گزینه -P --posix-captures استفاده میشود.
- yypmatch
- آرایهای از l-valueها که برای نگهداری مقادیر برچسب متناظر با پرانتزهای تسخیر در قاعده تطبیقیافته استفاده میشوند. طول آرایه باید حداقل yynmatch * 2 باشد (معمولاً YYMAXNMATCH * 2 گزینه مناسبی است). تنها با گزینه -P --posix-captures استفاده میشود.
- yystable
- منسوخشده.
- yystate
- یک l-value که همراه با گزینه --loop-switch برای ذخیره وضعیت فعلی DFA استفاده میشود.
- yytarget
- جدول پرش
شامل مقاصد
پرش
(نشانیهای
برچسب) برای
تمامی
گذارها از
یک وضعیت.
این جدول
برای هر
وضعیت محلی
است. تولید
جدولهای
yytarget با گزینه
--computed-gotos فعال
میشود.
برخی از گزینهها دارای پیکربندیهای متناظر هستند، برخی دیگر سراسری بوده و پس از شروع خواندن فایل ورودی توسط re2c قابل تغییر نیستند. گزینههای اشکالزدایی معمولاً نیازمند ساخت re2c در پیکربندی اشکالزدایی (debug) هستند. گزینههای داخلی برای آزمایش الگوریتمهای بهکاررفته در re2c مفید میباشند.
- -? --help -h
- نمایش پیام راهنما.
- --api <simple | record | generic>
- تعیین API مورد استفاده توسط کد تولیدشده برای ارتباط با کد تعریفشده توسط کاربر. گزینهٔ simple باید در موارد سادهای استفاده شود که نیازی به پر کردن مجدد بافر و ذخیره وضعیت تحلیلگر لغوی نیست. گزینهٔ record باید زمانی استفاده شود که وضعیت تحلیلگر لغوی نیاز به ذخیره در یک رکورد (struct، class و غیره) دارد. گزینهٔ generic باید در موارد پیچیدهای استفاده شود که دو API دیگر به اندازه کافی انعطافپذیر نیستند.
- --bit-vectors -b
- بهینهسازی پرشهای شرطی با استفاده از ماسکهای بیتی. این گزینه مستلزم --nested-ifs است.
- --captures, --leftmost-captures
- فعالسازی استخراج زیرتطبیق با گروههای ضبط حریصانهٔ چپترین (leftmost greedy). نتیجه در آرایهٔ yybmatch با ظرفیت 2 * YYMAXNMATCH جمعآوری شده، و yynmatch روی تعداد گروههای قاعدهٔ منطبق تنظیم میشود.
- --captvars, --leftmost-captvars
- فعالسازی استخراج زیرتطبیق با گروههای ضبط حریصانهٔ چپترین. نتیجه در متغیرهای yytl<k> و yytr<k> برای k-امین گروه ضبط جمعآوری میشود.
- --case-insensitive
- غیرحساس به بزرگی و کوچکی حروف در نظر گرفتن رشتههای درون نقلقول تکی و دوتایی.
- --case-inverted
- معکوس کردن معنای رشتههای درون نقلقول تکی و دوتایی: حساس به بزرگی و کوچکی حروف در نظر گرفتن رشتههای نقلقول تکی و غیرحساس در نظر گرفتن رشتههای نقلقول دوتایی.
- --case-ranges
- ادغام caseهای متوالی در دستورهای switch به یک بازه به فرم low ... high. این نحو یک افزونهٔ زبان C/C++ است که توسط کامپایلرهایی مانند GCC، Clang و Tcc پشتیبانی میشود. مزیت اصلی نسبت به استفاده از caseهای تکی، تولید کد کوچکتر و سرعت تولید بیشتر است، هرچند برای برخی کامپایلرها مانند Tcc منجر به اندازه باینری کوچکتر نیز میشود.
- --computed-gotos -g
- بهینهسازی پرشهای شرطی با استفاده از افزونهٔ غیراستاندارد "computed goto" (که باید توسط کامپایلر پشتیبانی شود). برنامهٔ re2hs جداول پرش را تنها در موارد پیچیده با شاخههای شرطی فراوان تولید میکند. آستانهٔ پیچیدگی را میتوان با پیکربندی cgoto:threshold تعیین کرد. آفستهای نسبی را میتوان با پیکربندی cgoto:relative فعال کرد. این گزینه مستلزم --bit-vectors است.
- --computed-gotos-relative
- مشابه --computed-gotos اما در عوض، آفستهای نسبی را برای جداول پرش تولید میکند (که باید توسط کامپایلر پشتیبانی شود). این گزینه مستلزم --computed-gotos است.
- --conditions --start-conditions -c
- فعالسازی پشتیبانی از "conditions" مشابه Flex: چندین تحلیلگر لغوی بههمپیوسته درون یک بلوک. این یک جایگزین برای مشخص کردن دستی بلوکهای مختلف re2hs متصلشده با goto یا فراخوانی توابع است.
- --depfile FILE
- نوشتن اطلاعات وابستگی در FILE در قالب یک قاعدهٔ Makefile به صورت <output-file> : <input-file> [include-file ...]. این امکان را میدهد که وابستگیهای ساخت در حضور بلوکها/دستورهای include ردیابی شوند، بهطوری که بهروزرسانی فایلهای include باعث تولید مجدد فایل خروجی شود. این گزینه به گزینهٔ --output وابسته است.
- --ebcdic --ecb -e
- تولید تحلیلگر لغوی که ورودی را در کدگذاری EBCDIC میخواند. re2hs فرض میکند که بازهٔ نویسهها 0 -- 0xFF و اندازهٔ نویسه ۱ بایت است.
- --empty-class <match-empty | match-none | error>
- تعریف نحوهٔ برخورد re2hs با کلاسهای نویسهٔ خالی. با match-empty (پیشفرض) کلاس خالی با ورودی خالی تطبیق مییابد (که غیرمنطقی است، اما سازگار با گذشته است). با match-none کلاس خالی همواره در تطبیق ناموفق است. با error کلاس خالی خطای کامپایل ایجاد میکند.
- --encoding-policy <fail | substitute | ignore>
- تعریف نحوهٔ برخورد re2hs با جانشینهای یونیکد (Unicode surrogates). با fail در هنگام مواجهه با جانشین، re2hs با یک خطا متوقف میشود. با substitute برنامهٔ re2hs بدون هشدار جانشینها را با نقطهٔ کد خطای 0xFFFD جایگزین میکند. با ignore (پیشفرض) re2hs با جانشینها مانند نقاط کد معمولی رفتار میکند. استاندارد یونیکد بیان میکند که جانشینهای مستقل نامعتبر هستند، اما کتابخانهها و برنامههای دنیای واقعی رفتارهای متفاوتی دارند.
- --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 مفید است. re2hs به دنبال FILE در دایرکتوری فایل والد و در مکانهای include مشخصشده با گزینهٔ -I میگردد.
- --input <default | custom>
- نام مستعار منسوخشده برای --api. گزینهٔ default معادل simple است (در واقع برای اکثر بکاندها پیشفرض است، اما نه برای همه). گزینهٔ custom معادل generic است.
- --input-encoding <ascii | utf8>
- تعیین نحوهٔ تجزیه عبارات منظم توسط re2hs. با ascii (پیشفرض) re2hs ورودی را به صورت کدگذاریشده با ASCII پردازش میکند: هر دنبالهای از واحدهای کد، دنبالهای از نویسههای ۱ بایتی مستقل است. با utf8 برنامهٔ re2hs ورودی را به صورت کدگذاریشده با UTF8 پردازش کرده و نویسههای چندبایتی را شناسایی میکند.
- --invert-captures
- معکوس کردن معنای گروههای ضبطکننده و غیرضبطکننده. به طور پیشفرض (...) ضبطکننده و (! ...) غیرضبطکننده است. با این گزینه (! ...) ضبطکننده و (...) غیرضبطکننده خواهد بود.
- --lang <none | c | d | go | haskell | java | js | ocaml | python | rust | swift | v | zig>
- تعیین زبان مقصد. زبانهای پشتیبانیشده عبارتند از C، D، Go، Haskell، Java، JS، OCaml، Python، Rust، Swift، V، Zig (زبانهای بیشتری را میتوان از طریق فایلهای نحو تعریفشده توسط کاربر اضافه کرد، گزینهٔ --syntax را ببینید). گزینهٔ none پیکربندیهای نحوی پیشفرض را غیرفعال میکند تا زبان مقصد تعریفنشده باقی بماند.
- --location-format <gnu | msvc>
- تعیین قالب مکان در پیامها. با gnu مکانها به صورت 'filename:line:column: ...' چاپ میشوند. با msvc مکانها به صورت 'filename(line,column) ...' چاپ میشوند. پیشفرض gnu است.
- --loop-switch
- استفاده از مدل کد "loop/switch": کدگذاری DFA در قالب یک حلقه روی دستور switch، که در آن هر وضعیت یک case از switch است. وضعیت در یک متغیر yystate ذخیره میشود. گذارها بین وضعیتها، yystate را به برچسب case وضعیت مقصد بهروزرسانی کرده و اجرا را به ابتدای حلقه ادامه میدهند.
- --nested-ifs -s
- استفاده از دستورهای شرطی if تودرتو به جای دستورهای switch در پرشهای شرطی. این معمولاً در کامپایلرهای غیربهینهساز منجر به کد کارآمدتری میشود.
- --no-debug-info -i
- عدم تولید دستورهای خط (line directives). این گزینه زمانی مفید است که کد تولیدشده در یک سامانه کنترل نسخه ذخیره میشود (برای جلوگیری از تغییرات بزرگ (diff) خودکار ناشی از تغییرات کوچک).
- --no-generation-date
- جلوگیری از درج تاریخ در فایل تولیدشده.
- --no-version
- جلوگیری از درج نسخه در فایل تولیدشده.
- --no-unsafe
- عدم تولید پوشش unsafe روی YYPEEK (این گزینه مختص Rust است). به دلایل عملکردی، YYPEEK باید از بررسی حدود (bounds-checking) خودداری کند، زیرا تحلیلگر لغوی بررسیهای پایان ورودی را به روش کارآمدتری انجام میدهد. کاربر ممکن است بخواهد یک تعریف امن برای YYPEEK ارائه دهد، یا تعریفی که فقط در ساختهای انتشار (release) به صورت unsafe باشد؛ در چنین مواردی گزینهٔ --no-unsafe به جلوگیری از هشدارهای مربوط به بلوکهای unsafe زائد کمک میکند.
- --output -o OUTPUT
- تعیین فایل OUTPUT.
- --posix-captures, -P
- فعالسازی استخراج زیرتطبیق با گروههای ضبط به سبک POSIX. نتیجه در آرایهای از yybmatch با ظرفیت 2 * YYMAXNMATCH جمعآوری شده، و yynmatch روی تعداد گروههای قاعدهٔ تطبیقیافته تنظیم میشود..TP --posix-captvars فعالسازی استخراج زیرتطابق با گروههای ضبط به سبک POSIX. نتیجه برای k-اُمین گروه ضبط در متغیرهای yytl<k> و yytr<k> جمعآوری میشود.
- --recursive-functions
- استفاده از مدل کدی مبتنی بر توابع همبازگشتی (co-recursive)، که در آن هر حالت DFA یک تابع مجزا است که میتواند سایر توابع-حالت یا خودش را فراخوانی کند.
- --reusable -r
- از نسخه 2.2 منسوخ شده است (بلوکهای با قابلیت استفاده مجدد اکنون به طور پیشفرض مجاز هستند).
- --skeleton -S
- نادیده گرفتن کد واسط تعریفشده توسط کاربر و تولید یک برنامه خودکفای «اسکلت» ("skeleton"). علاوه بر این، تولید فایلهای ورودی با رشتههای مشتقشده از گرامر باقاعده و نتایج تطابق فشردهشده که برای اعتبارسنجی رفتار «اسکلت» روی تمام ورودیها به کار میروند. این گزینه برای یافتن باگها در بهینهسازیها و تولید کد مفید است. این گزینه فقط برای C پشتیبانی میشود.
- --storable-state -f
- تولید یک تحلیلگر لغوی (lexer) که میتواند وضعیت درونی خود را ذخیره کند. این قابلیت در لکسرهای مدل فشاری (push-model) مفید است که در هنگام ناکافی بودن ورودی توسط یک برنامه بیرونی متوقف شده و سپس با در دسترس قرار گرفتن ورودی بیشتر کار خود را از سر میگیرند. در این حالت، کاربران باید علاوه بر این، سازههای پایه YYGETSTATE و YYSETSTATE را تعریف کنند، و متغیرهای yych، yyaccept و state باید بخشی از وضعیت ذخیرهشده لکسر باشند.
- --syntax FILE
- بارگذاری پیکربندیها از FILE مشخصشده و اعمال آنها بر روی فایل نحو (syntax) پیشفرض. توجه داشته باشید که FILE میتواند تنها چند پیکربندی را تعریف کند (اگر برای اصلاح فایل نحو پیشفرض استفاده شود)، یا میتواند یک بکاند زبان کاملاً جدید را تعریف کند (در حالت دوم توصیه میشود از گزینه --lang none استفاده شود).
- فعالسازی استخراج زیرتطابق با برچسبها (tags).
- --ucs2 --wide-chars -w
- تولید لکسری که ورودی کدگذاریشده با UCS2 را میخواند. re2hs فرض میکند که محدوده نویسهها بین 0 -- 0xFFFF و اندازه نویسه 2 بایت است. این گزینه مستلزم --nested-ifs است.
- --utf8 --utf-8 -8
- تولید لکسری که ورودی را با کدگذاری UTF-8 میخواند. re2hs فرض میکند که محدوده نویسهها بین 0 -- 0x10FFFF و اندازه نویسه 1 بایت است.
- --utf16 --utf-16 -x
- تولید لکسری که ورودی کدگذاریشده با UTF16 را میخواند. re2hs فرض میکند که محدوده نویسهها بین 0 -- 0x10FFFF و اندازه نویسه 2 بایت است. این گزینه مستلزم --nested-ifs است.
- --utf32 --unicode -u
- تولید لکسری که ورودی کدگذاریشده با UTF32 را میخواند. re2hs فرض میکند که محدوده نویسهها بین 0 -- 0x10FFFF و اندازه نویسه 4 بایت است. این گزینه مستلزم --nested-ifs است.
- --verbose
- نمایش یک پیام کوتاه در صورت موفقیت.
- --vernum -V
- نمایش اطلاعات نسخه در قالب MMmmpp (اصلی، فرعی، وصله).
- --version -v
- نمایش اطلاعات نسخه.
- --single-pass -1
- منسوخ شده است. هیچ کاری انجام نمیدهد (تکگذره بودن اکنون پیشفرض است).
- --debug-output -d
- درج فراخوانیهای YYDEBUG در کد تولیدشده. این گزینه برای ردیابی اجرای لکسر مفید است.
- --dump-adfa
- گزینه اشکالزدایی: خروجی DFA پس از تونلزنی (tunneling) (در قالب .dot).
- --dump-cfg
- گزینه اشکالزدایی: خروجی گراف جریان کنترل (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 مورداستفاده توسط re2hs. گزینه moore همان الگوریتم مور (Moore) است (که پیشفرض میباشد). گزینه table الگوریتم «پر کردن جدول» ("table filling") است. هر دو الگوریتم باید به جز برچسبگذاری مجدد حالتها، DFA یکسانی تولید کنند؛ پر کردن جدول سادهتر و بسیار کندتر است و به عنوان یک پیادهسازی مرجع عمل میکند.
- --eager-skip
- گزینه داخلی: وادار کردن لکسر تولیدشده به جلو بردن مشتاقانه (eagerly) موقعیت ورودی -- بلافاصله پس از خواندن نماد ورودی. این گزینه رفتار پیشفرض را که در آن موقعیت ورودی به صورت تنبلانه (lazily) -- پس از گذار به حالت بعدی -- جلو برده میشود، تغییر میدهد.
- --no-lookahead
- گزینه داخلی، منسوخ شده است. در گذشته برای فعالسازی الگوریتم TDFA(0) استفاده میشد. بر خلاف TDFA(1)، الگوریتم TDFA(0) از پیشنگری یکنمادی استفاده نمیکند. این الگوریتم عملیات ثباتها را به جای گذارهای خروجی روی گذارهای ورودی اعمال میکند. ارزیابیها نشان دادند که الگوریتم TDFA(0) کارایی کمتری نسبت به TDFA(1) دارد.
- --no-optimize-tags
- گزینه داخلی: جلوگیری از بهینهسازی متغیرهای برچسب (مفید برای اشکالزدایی).
- --posix-closure <gor1 | gtop>
- گزینه داخلی: مشخص کردن الگوریتم کوتاهترین مسیر برای ساخت بستار اپسیلون با معناشناسی ابهامزدایی POSIX: مقدار gor1 (پیشفرض) نشاندهنده الگوریتم گلدبرگ-رادزیک (Goldberg-Radzik) است و gtop نشاندهنده الگوریتم «ترتیب توپولوژیکی سراسری» ("global topological order").
- --posix-prectable <complex | naive>
- گزینه داخلی: مشخص کردن الگوریتم مورداستفاده برای محاسبه جدول اولویت POSIX. الگوریتم complex جدول اولویت را در یک پیمایش از درخت تاریخچه برچسب محاسبه میکند و دارای پیچیدگی درجه دو بر حسب تعداد حالتهای TNFA است؛ این گزینه پیشفرض است. الگوریتم naive در بدترین حالت دارای پیچیدگی درجه سه بر حسب تعداد حالتهای TNFA است، اما بسیار سادهتر از complex بوده و در حالتهای غیرنامتعارف ممکن است کمی سریعتر باشد.
- --stadfa
- گزینه داخلی، منسوخ شده است. در گذشته برای فعالسازی الگوریتم staDFA استفاده میشد، که تفاوت آن با TDFA در این است که عملیات ثباتها به جای گذارها در حالتها قرار میگیرند. ارزیابیها نشان دادند که الگوریتم staDFA کارایی کمتری نسبت به TDFA دارد.
- --fixed-tags <none | toplevel | all>
- گزینه
داخلی: مشخص
کردن اینکه
آیا
بهینهسازی
برچسبهای
ثابت باید
روی همه
برچسبها
اعمال شود
(all)، روی
هیچکدام
اعمال نشود
(none)، یا فقط
روی
آنهایی که
در الحاق
بالاترین
سطح هستند
اعمال گردد
(toplevel). مقدار
پیشفرض all
است.
برچسبهای
«ثابت»
برچسبهایی
هستند که در
فاصله
ثابتی نسبت
به یک برچسب
دیگر (موسوم
به برچسب
«پایه» یا
"base") قرار
دارند. در
چنین
مواردی
تنها نیاز
است برچسب
پایه
ردیابی
شود، و
مقدار
برچسب ثابت
میتواند
به صورت
مقدار
برچسب پایه
به علاوه یک
آفست ایستا
محاسبه
گردد. برای
برچسبهایی
که تحت
تناوب یا
تکرار
هستند،
بررسی
اینکه آیا
برچسب پایه
مقدار
عدمتطابق
دارد یا خیر
نیز ضروری
است (در آن
صورت برچسب
ثابت نیز
بدون در نظر
گرفتن آفست
باید روی
عدمتطابق
تنظیم شود).
برای
برچسبها
در الحاق
بالاترین
سطح، نیازی
به این
بررسی نیست
زیرا آنها
همیشه
مطابقت
دارند.
هشدارها را میتوان بهصورت جداگانه فعال یا غیرفعال کرد و یا به یک خطا تبدیل نمود.
- -W
- فعال کردن همهٔ هشدارها.
- -Werror
- تبدیل هشدارها به خطا. توجه داشته باشید که این گزینه بهتنهایی هیچ هشداری را فعال نمیکند؛ بلکه تنها بر هشدارهایی اثر میگذارد که تاکنون فعال شدهاند یا بعداً فعال خواهند شد.
- -W<warning>
- فعال کردن warning.
- -Wno-<warning>
- غیرفعال کردن warning.
- -Werror-<warning>
- فعال کردن warning و رفتار با آن بهعنوان یک خطا (این گزینه متضمن -W<warning> است).
- -Wno-error-<warning>
- عدم برخورد با این warning خاص بهعنوان خطا. این گزینه خود هشدار را غیرفعال نمیکند.
- -Wcondition-order
- هشدار در صورتی که برنامهٔ تولیدشده فرضیات ضمنی دربارهٔ شمارهگذاری شرطها داشته باشد. باید از گزینهٔ --header یا بلوک conditions برای تولید نگاشت نامهای شرط به اعداد استفاده کرد و سپس نامهای شرطِ بهطور خودکار تولیدشده را به کار برد.
- -Wempty-character-class
- هشدار در صورتی که یک عبارت باقاعده شامل یک کلاس نویسهٔ خالی باشد. تلاش برای تطبیق یک کلاس نویسهٔ خالی بیمعنی است: این کار باید همیشه با شکست مواجه شود. با این حال، به دلایل سازگاری با نسخههای پیشین، re2hs کلاسهای نویسهٔ خالی را مجاز دانسته و با آنها مانند رشتههای خالی رفتار میکند. برای تغییر رفتار پیشفرض از گزینهٔ --empty-class استفاده کنید.
- -Wmatch-empty-string
- هشدار در صورتی که یک قاعده تهیپذیر باشد (با یک رشتهٔ خالی تطبیق یابد). اگر تحلیلگر لغوی در یک حلقه اجرا شود و تطبیق تهی غیرعمدی باشد، تحلیلگر لغوی ممکن است بهطور غیرمنتظرهای در یک حلقهٔ بیپایان متوقف شود.
- -Wswapped-range
- هشدار در صورتی که کران پایین یک بازه بزرگتر از کران بالای آن باشد. رفتار پیشفرض این است که بدون هشدار جای کرانهای بازه جابهجا شود.
- -Wundefined-control-flow
- هشدار در صورتی که برخی از رشتههای ورودی باعث جریان کنترل تعریفنشده در تحلیلگر لغوی شوند (الگوهای معیوب گزارش میشوند). این یک اشتباه رایج و خطرناک است. این مشکل را میتوان بهسادگی با افزودن قاعدهٔ پیشفرض * برطرف کرد که کمترین اولویت را دارد، با هر واحد کد (code unit) تطبیق مییابد، و همیشه یک واحد کد را مصرف میکند.
- -Wunreachable-rules
- هشدار دربارهٔ قواعدی که توسط قواعد دیگر پوشانده شدهاند (shadowed) و هرگز تطبیق نخواهند یافت.
- -Wdeprecated-eof_rule
- هشدار دربارهٔ قواعد مستقل پایان ورودی $ که با تغییرات آینده از کار خواهند افتاد و نیاز به اصلاح دارند. در حال حاضر این قواعد هنگام تداخل با سایر قواعد اولویت دارند، اما پس از معرفی نماد تعمیمیافتهٔ پایان ورودی $ ترتیب اولویت تغییر خواهد کرد و این قواعد توسط سایر قواعد پوشانده خواهند شد.
- -Wuseless-escape
- هشدار در صورتی که یک نماد گریز داده شده باشد (escaped) در حالی که نیازی به آن نبوده است. بهطور پیشفرض، re2hs چنین نویسههای گریزی را نادیده میگیرد، اما این موضوع ممکن است نشاندهندهٔ یک غلط املایی یا خطا در توالی گریز (escape sequence) باشد.
- -Wnondeterministic-tags
- هشدار در صورتی که یک برچسب دارای درجهٔ n-ام از عدم قطعیت باشد، بهطوری که n بزرگتر از ۱ است.
- -Wsentinel-in-midrule
- هشدار در صورتی که نماد نگهبان (sentinel) در میانهٔ یک قاعده رخ دهد --- این امر ممکن است باعث خواندن فراتر از انتهای بافر، خرابی برنامه (crash) یا فساد حافظه در تحلیلگر لغوی تولیدشده شود. این هشدار تنها در صورتی کاربرد دارد که از روش نگهبان برای بررسی پایان ورودی استفاده شود. در صورت استفاده از پیکربندی re2c:sentinel، این هشدار به خطا تبدیل میشود.
- -Wundefined-syntax-config
- هشدار در
صورتی که
فایل نحو
مشخصشده
با گزینهٔ
--syntax فاقد
تعاریف
برخی از
پیکربندیها
باشد. این
ویژگی به
نگهداری
فایلهای
نحو
تعریفشده
توسط کاربر
کمک میکند:
اگر نسخهٔ
جدیدی
پیکربندیهایی
را اضافه
کند، فایل
نحو قدیمی
هشداری
صادر کرده و
کاربر مطلع
خواهد شد.
اگر برخی از
پیکربندیها
بیاستفاده
هستند و
نیازی به
تعریف
ندارند،
باید
بهطور
صریح روی
<undefined> تنظیم
شوند.
پشتیبانی از زبانهای مختلف در re2c بر پایه ایده فایلهای نحو (syntax files) استوار است. یک فایل نحو، یک فایل پیکربندی است که نحو زبان مقصد را تعریف میکند -- نه کل زبان، بلکه بخش کوچکی از آن را که توسط کد تولیدشده استفاده میشود. فایلهای نحو re2c را بسیار منعطف میسازند، اما نباید به عنوان جایگزینی برای پیکربندیهای re2c: به کار روند: هدف آنها تعریف نحو زبان مقصد است، نه سفارشیسازی یک تحلیلگر لغوی خاص. همه زبانهای پشتیبانیشده دارای فایلهای نحو پیشفرضی هستند که بخشی از توزیع برنامه میباشند (زیرپوشه include/syntax را ببینید)؛ آنها همچنین در باینری re2hs تعبیه شدهاند. کاربران میتوانند یک فایل نحو سفارشی فراهم کنند که چند پیکربندی را برای یکی از زبانهای پشتیبانیشده بازنویسی کند، یا میتوانند همه پیکربندیها را از نو تعریف نمایند (در این صورت باید گزینه --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
- فهرستی از هدفهای تولید کد (codegen) پشتیبانیشده با مقادیر ممکن code، dot، skeleton.
- supported_features
- فهرستی از ویژگیهای پشتیبانیشده با مقادیر ممکن nested-ifs، bitmaps، computed-gotos، case-ranges، monadic، unsafe، tags، captures، captvars.
پیکربندیهای زبان (Language configurations)
- semicolons
- غیر صفر، اگر زبان از سمیکالن بعد از عبارات دستوری استفاده میکند.
- backtick_quoted_strings
- غیر صفر، اگر زبان دارای رشتههای محصور در بکتیک است.
- single_quoted_strings
- غیر صفر، اگر زبان دارای رشتههای محصور در نقلقول تکی است.
- indentation_sensitive
- غیر صفر، اگر زبان به تورفتگی حساس است.
- wrap_blocks_in_braces
- غیر صفر، اگر عبارات دستوری مرکب باید درون آکوالاد قرار گیرند.
پیکربندیهای درجا (Inplace configurations)
الگوهای کد (Code templates)
code-template ::
name '=' code-exprs ';'
| CODE_TEMPLATE ';'
| '<undefined>' ';'
code-exprs ::
<EMPTY>
| code-exprs code-expr
code-expr ::
STRING
| VARIABLE
| optional
| list
optional ::
'(' CONDITIONAL '?' code-exprs ')'
| '(' CONDITIONAL '?' code-exprs ':' code-exprs ')'
list ::
'[' VARIABLE ':' code-exprs ']'
| '[' VARIABLE '{' NUMBER '}' ':' code-exprs ']'
| '[' VARIABLE '{' NUMBER ',' NUMBER '}' ':' code-exprs ']'
یک الگوی کد دنبالهای از رشتههای متنی دقیق، متغیرها، عناصر اختیاری و فهرستها، یا ارجاعی به یک الگوی کد دیگر، یا مقدار ویژه <undefined> است. متغیرها جانگهدارندههایی هستند که در مرحله تولید کد جایگزین میشوند. متغیرهای فهرستی ویژه هستند: هنگام بسط الگوهای فهرست، re2hs عبارات سمت راست دونقطه را چند بار تکرار میکند و هر بار رخدادهای متغیر فهرست را با مقداری مخصوص به این تکرار جایگزین میسازد. فهرستها دارای کرانهای اختیاری هستند (مقادیر منفی از انتها شمرده میشوند، برای نمونه -1 به معنای آخرین عنصر است). نامهای شرطی با یک نقطه آغاز میشوند. هر دو مورد شرطها و متغیرها میتوانند محلی (مخصوص همان الگوی کد) یا عمومی (مجاز در تمام الگوهای کد) باشند. هنگامی که re2hs یک فایل نحو را میخواند، بررسی میکند که هر الگوی کد تنها از متغیرها و شرطهایی استفاده کند که در آن مجاز هستند.
برای نمونه، الگوی کد زیر ساختار 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
}
در اینجا فهرستی از تمام الگوهای کد پشتیبانیشده توسط re2hs به همراه متغیرها و شرطهای محلی آنها آورده شده است. توجه داشته باشید که یک تعریف خاص میتواند از متغیرها و شرطهای محلی استفاده کند، اما الزامی به آن ندارد. هر الگوی کد استفادهنشده باید روی <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
- عملگر "مساوی".
- code:cmp_ne
- عملگر "نامساوی".
- code:cmp_lt
- عملگر "کوچکتر از".
- code:cmp_gt
- عملگر "بزرگتر از".
- code:cmp_le
- عملگر "کوچکتر یا مساوی".
- code:cmp_ge
- عملگر "بزرگتر یا مساوی".
- code:if_then_else
- دستور If-then-else با یک یا چند شاخه. متغیرهای پشتیبانیشده: branch (متغیر فهرستی که به خودی خود کدی تولید نمیکند، اما عبارت فهرستی را به تعداد شاخهها بسط میدهد)، cond (شرط شاخه جاری) و stmt (متغیر فهرستی که به تمام دستورهای شاخه جاری بسط مییابد). شرطها: .cond (اگر شاخه جاری دارای شرط باشد درست است)، .many (اگر بیش از یک شاخه وجود داشته باشد درست است).
- code:if_then_else_oneline
- حالت خاصی از code:if_then_else برای حالتی که تمام شاخهها دستورهای تکخطی دارند. اگر این مقدار <undefined> باشد، در عوض code:if_then_else استفاده میشود.
- code:switch
- یک دستور switch با یک یا چند مورد (case). متغیرهای پشتیبانیشده: expr (عبارتی که روی آن switch میشود) و case (متغیر فهرستی که به تمام گروههای case همراه با بلوکهای کدشان بسط مییابد).
- code:switch_cases
- گروهی از موارد switch که به یک بلوک کد واحد نگاشت میشوند. متغیرهای پشتیبانیشده عبارتند از case (متغیر فهرستی که به تمام 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). متغیرهای پشتیبانیشده: array (جدول حاوی اطلاعات goto محاسباتی)، index (اندیس عنصر در جدول) و base (برچسب پایه، تنها در صورتی استفاده میشود که .cgoto.relative درست باشد).
- code:cgoto:data
- عبارت مقداردهی اولیه برای یک عنصر منفرد در جدول goto محاسباتی. متغیرهای پشتیبانیشده: label (برچسبی که برای مقداردهی اولیه عنصر جاری استفاده میشود)، type (نوع زیربنایی عناصر در جدول) و base (برچسب پایه - تنها در صورتی استفاده میشود که .cgoto.relative درست باشد).
- code:fndecl
- اعلان تابع. متغیرهای پشتیبانیشده: name (نام تابع)، type (نوع بازگشتی)، throw (استثناهای پرتابشده توسط این تابع، که به پیکربندی re2c:yyfn:throw نگاشت میشود)، arg (متغیر فهرستی که خود کدی تولید نمیکند، اما عبارت فهرستی را به تعداد آرگومانهای تابع بسط میدهد)، argname (نام آرگومان جاری)، argtype (نوع آرگومان جاری). شرط: .type (اگر تابع غیر void باشد، درست است).
- code:fndef
- مشابه code:fndecl، اما برای تعریف تابع به کار میرود؛ بنابراین یک متغیر فهرستی اضافی به نام stmt دارد که به تمام دستورهای درون بدنه تابع بسط مییابد.
- code:fncall
- دستور فراخوانی تابع. متغیرهای پشتیبانیشده: name (نام تابع)، retval (یک l-value که مقدار بازگشتی، در صورت وجود، در آن ذخیره میشود) و arg (متغیر فهرستی که به تمام آرگومانهای تابع بسط مییابد). شرطها: .args (اگر تابع دارای آرگومان باشد، درست است) و .retval (اگر نیاز به ذخیره مقدار بازگشتی باشد، درست است).
- code:tailcall
- دستور فراخوانی انتهایی (tail call). متغیرهای پشتیبانیشده: name (نام تابع)، و arg (متغیر فهرستی که به تمام آرگومانهای تابع بسط مییابد). شرطها: .args (اگر تابع دارای آرگومان باشد، درست است) و .retval (اگر تابع غیر void باشد، درست است).
- code:recursive_functions
- بدنه برنامه با مدل کد --recursive-functions. متغیرهای پشتیبانیشده: fn (متغیر فهرستی که به خودی خود کدی تولید نمیکند، اما عبارت فهرستی را به تعداد توابع بسط میدهد)، fndecl (اعلان تابع جاری) و fndef (تعریف تابع جاری)..TP code:fingerprint اثر انگشت (fingerprint) در بالای فایل خروجی تولیدشده. متغیرهای پشتیبانیشده: ver (نسخهٔ re2hs که برای تولید این فایل به کار رفته است) و date (تاریخ تولید).
- code:line_info
- قالب دستورالعملهای خط (اگر روی <undefined> تنظیم شود، هیچ دستورالعملی تولید نمیشود). متغیرهای پشتیبانیشده: line (شماره خط) و file (نام فایل).
- code:abort
- دستوری که اجرای برنامه را متوقف (abort) میکند.
- code:yydebug
- دستور YYDEBUG، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYDEBUG، yyrecord، yych (نگاشت به پیکربندیهای متناظر re2c:)، state (شماره وضعیت DFA).
- code:yypeek
- دستور YYPEEK، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYPEEK، YYCTYPE، YYINPUT، YYCURSOR، yyrecord، yych (نگاشت به پیکربندیهای متناظر re2c:). گزارههای شرطی: .cast (درست است اگر re2c:yych:conversion روی مقداری غیر از صفر تنظیم شده باشد).
- code:yyskip
- دستور YYSKIP، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSKIP، YYCURSOR، yyrecord (نگاشت به پیکربندیهای متناظر re2c:).
- code:yybackup
- دستور YYBACKUP، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYBACKUP، YYCURSOR، YYMARKER، yyrecord (نگاشت به پیکربندیهای متناظر re2c:).
- code:yybackupctx
- دستور YYBACKUPCTX، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYBACKUPCTX، YYCURSOR، YYCTXMARKER، yyrecord (نگاشت به پیکربندیهای متناظر re2c:).
- code:yyskip_yypeek
- دستور ترکیبی code:yyskip و code:yypeek (پیشفرض آن code:yyskip و به دنبال آن code:yypeek است).
- code:yypeek_yyskip
- دستور ترکیبی code:yypeek و code:yyskip (پیشفرض آن code:yypeek و به دنبال آن code:yyskip است).
- code:yyskip_yybackup
- دستور ترکیبی code:yyskip و code:yybackup (پیشفرض آن code:yyskip و به دنبال آن code:yybackup است).
- code:yybackup_yyskip
- دستور ترکیبی code:yybackup و code:yyskip (پیشفرض آن code:yybackup و به دنبال آن code:yyskip است).
- code:yybackup_yypeek
- دستور ترکیبی code:yybackup و code:yypeek (پیشفرض آن code:yybackup و به دنبال آن code:yypeek است).
- code:yyskip_yybackup_yypeek
- دستور ترکیبی code:yyskip، code:yybackup و code:yypeek (پیشفرض آن ``code:yyskip`` و به دنبال آن code:yybackup و سپس code:yypeek است).
- code:yybackup_yypeek_yyskip
- دستور ترکیبی code:yybackup، code:yypeek و code:yyskip (پیشفرض آن ``code:yybackup`` و به دنبال آن code:yypeek و سپس code:yyskip است).
- code:yyrestore
- دستور YYRESTORE، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYRESTORE، YYCURSOR، YYMARKER، yyrecord (نگاشت به پیکربندیهای متناظر re2c:).
- code:yyrestorectx
- دستور YYRESTORECTX، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYRESTORECTX، YYCURSOR، YYCTXMARKER، yyrecord (نگاشت به پیکربندیهای متناظر re2c:).
- code:yyrestoretag
- دستور YYRESTORETAG، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYRESTORETAG، YYCURSOR، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، tag (نام متغیر برچسب که برای بازیابی موقعیت استفاده میشود).
- code:yyshift
- دستور YYSHIFT، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSHIFT، YYCURSOR، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، offset (تعداد واحدهای کد برای جابهجا کردن موقعیت فعلی).
- code:yyshiftstag
- دستور YYSHIFTSTAG، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSHIFTSTAG، yyrecord، negative (نگاشت به پیکربندیهای متناظر re2c:)، tag (متغیر برچسبی که باید جابهجا شود)، offset (تعداد واحدهای کد برای جابهجایی). گزارههای شرطی: .nested (درست است اگر این یک برچسب تو در تو باشد -- در این حالت ممکن است مقدار آن برابر با re2c:tags:negative باشد که نباید جابهجا شود).
- code:yyshiftmtag
- دستور YYSHIFTMTAG، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSHIFTMTAG (نگاشت به پیکربندی متناظر re2c:)، tag (متغیر برچسبی که باید جابهجا شود)، offset (تعداد واحدهای کد برای جابهجایی).
- code:yystagp
- دستور YYSTAGP، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSTAGP، YYCURSOR، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، tag (متغیر برچسبی که باید بهروزرسانی شود).
- code:yymtagp
- دستور YYMTAGP، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYMTAGP (نگاشت به پیکربندی متناظر re2c:)، tag (متغیر برچسبی که باید بهروزرسانی شود).
- code:yystagn
- دستور YYSTAGN، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSTAGN، negative، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، tag (متغیر برچسبی که باید بهروزرسانی شود).
- code:yymtagn
- دستور YYMTAGN، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYMTAGN (نگاشت به پیکربندی متناظر re2c:)، tag (متغیر برچسبی که باید بهروزرسانی شود).
- code:yycopystag
- دستور YYCOPYSTAG، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYCOPYSTAG، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، lhs، rhs (متغیرهای برچسب سمت چپ و راست در عملیات کپی).
- code:yycopymtag
- دستور YYCOPYMTAG، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYCOPYMTAG، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، lhs، rhs (متغیرهای برچسب سمت چپ و راست در عملیات کپی).
- code:yygetaccept
- دستور YYGETACCEPT، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYGETACCEPT، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، var (نگاشت به پیکربندی re2c:yyaccept).
- code:yysetaccept
- دستور YYSETACCEPT، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSETACCEPT، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، var (نگاشت به پیکربندی re2c:yyaccept) و val (مقدار عددی قاعدهٔ پذیرفتهشده).
- code:yygetcond
- دستور YYGETCOND، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYGETCOND، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، var (نگاشت به پیکربندی re2c:yycond).
- code:yysetcond
- دستور YYSETCOND، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSETCOND، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، var (نگاشت به پیکربندی re2c:yycond) و val (شناسهٔ عددی وضعیت شرطی).
- code:yygetstate
- دستور YYGETSTATE، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYGETSTATE، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، var (نگاشت به پیکربندی re2c:yystate).
- code:yysetstate
- دستور YYSETSTATE، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYSETSTATE، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، var (نگاشت به پیکربندی re2c:yystate) و val (شماره وضعیت).
- code:yylessthan
- دستور YYLESSTHAN، احتمالاً تخصیصیافته برای APIهای مختلف. متغیرهای پشتیبانیشده: YYLESSTHAN، YYCURSOR، YYLIMIT، yyrecord (نگاشت به پیکربندیهای متناظر re2c:)، need (تعداد واحدهای کد برای بررسی در برابر آن). گزاره شرطی: .many (درست است اگر need بیش از یک باشد)..TP code:yyend عبارت YYEND، احتمالاً تخصیصیافته برای APIهای گوناگون. متغیرهای پشتیبانیشده: YYEND، YYCURSOR، YYLIMIT.
- code:yybm_filter
- شرطی که برای فیلتر کردن مقادیر yych که توسط جدول yybm پوشش داده نشدهاند استفاده میشود (همراه با گزینهٔ --bitmaps استفاده میشود). متغیر پشتیبانیشده: yych (به پیکربندی re2c:yych نگاشت میشود).
- code:yybm_match
- قالب بررسی جدول yybm (تولیدشده با گزینهٔ --bitmaps). متغیرهای پشتیبانیشده: yybm، yych (به پیکربندیهای متناظر re2c: نگاشت میشوند)، offset (آفست در جدول yybm که باید به yych افزوده شود) و mask (ماسک بیتی که باید روی مدخل جدول اعمال شود تا مقدار بولی مورد بررسی بازیابی شود).
- code:yytarget_filter
- شرطی که برای فیلتر کردن مقادیر yych که توسط جدول yytarget پوشش داده نشدهاند استفاده میشود (همراه با گزینهٔ --computed-gotos استفاده میشود). متغیر پشتیبانیشده: yych (به پیکربندی re2c:yych نگاشت میشود).
در اینجا فهرستی از تمام متغیرهای سراسری که در فایلهای نحو (syntax) مجاز هستند، آمده است:
- nl
- یک خط جدید (newline).
- indent
- متغیری که هیچ کدی تولید نمیکند، اما دارای اثر جانبی افزایش سطح تورفتگی است.
- dedent
- متغیری که هیچ کدی تولید نمیکند، اما دارای اثر جانبی کاهش سطح تورفتگی است.
- topindent
- رشتهٔ تورفتگی برای دستور جاری. سطح تورفتگی ردیابی شده و بهطور خودکار توسط تولیدکنندهٔ کد بهروزرسانی میشود.
در اینجا فهرستی از تمام شرطهای سراسری که در فایلهای نحو مجاز هستند، آمده است:
- .api.simple
- درست (True) است اگر API ساده استفاده شده باشد (--api simple یا re2c:api = simple).
- .api.generic
- درست است اگر API عمومی (generic) استفاده شده باشد (--api generic یا re2c:api = generic).
- .api.record
- درست است اگر API رکوردی استفاده شده باشد (--api record یا re2c:api = record).
- .api_style.functions
- درست است اگر سبک API تابعگونه استفاده شده باشد (re2c:api-style = functions).
- .api_style.freeform
- درست است اگر سبک API آزادشکل استفاده شده باشد (re2c:api-style = free-form).
- .case_ranges
- درست است اگر قابلیت بازههای case فعال باشد (--case-ranges یا re2c:case-ranges = 1).
- .cgoto.relative
- درست است اگر شکل نسبی دستور goto محاسبهشده استفاده شود (--computed-gotos-relative یا re2c:cgoto:relative = 1).
- .code_model.goto_label
- درست است اگر مدل کد مبتنی بر goto/label استفاده شده باشد (--goto-label).
- .code_model.loop_switch
- درست است اگر مدل کد مبتنی بر loop/switch استفاده شده باشد (--loop-switch).
- .code_model.recursive_functions
- درست است اگر مدل کد مبتنی بر توابع بازگشتی استفاده شده باشد (--recursive-function).
- .date
- درست است اگر اثرانگشت (fingerprint) تولیدشده باید حاوی تاریخ تولید باشد.
- .loop_label
- درست است اگر حلقههای تولیدشده توسط re2hs باید دارای برچسب باشند (re2c:label:yyloop روی یک رشتهٔ غیرخالی تنظیم شده باشد).
- .monadic
- درست است اگر کد تولیدشده باید مونادیک (monadic) باشد (re2c:monadic = 1). این گزینه فقط برای زبانهای تابعی محض کاربرد دارد.
- .start_conditions
- درست است اگر شرایط شروع فعال باشند (--start-conditions).
- .storable_state
- درست است اگر وضعیت قابل ذخیرهسازی فعال باشد (--storable-state).
- .unsafe
- درست است اگر re2hs باید بهمنظور تولید کد سریعتر از بلوکهای "unsafe" استفاده کند (--unsafe، re2c:unsafe = 1). این گزینه فقط برای زبانهایی که دارای قابلیت "unsafe" هستند کاربرد دارد.
- .version
- درست است اگر اثرانگشت تولیدشده باید شامل نسخهٔ re2hs باشد.
- .yyfill.enable
- درست است اگر YYFILL فعال باشد (re2c:yyfill:enable = 1).
- .yyfn.throw
- درست است اگر پیکربندی re2c:yyfn:throw روی یک رشتهٔ غیرخالی تعریف شده باشد.
یکی از مسائل اصلی برای تحلیلگر لغوی (lexer)، تشخیص زمان توقف است. چند شرط برای پایان کار وجود دارد:
- تحلیلگر لغوی ممکن است با قاعدهای تطابق یابد (از جمله قاعدهٔ پیشفرض *) و به یک حالت نهایی (final state) برسد
- تحلیلگر لغوی ممکن است در تطبیق با هر قاعدهای ناکام بماند و به یک حالت پیشفرض (default state) برسد
- تحلیلگر لغوی ممکن است به پایان ورودی برسد
دو شرط نخست، تحلیلگر لغوی را به روشی «طبیعی» خاتمه میدهند: تحلیلگر به حالتی بدون انتقالهای خروجی میرسد و فرآیند تطبیق بهطور خودکار متوقف میشود. شرط سوم، یعنی پایان ورودی، متفاوت است: این حالت میتواند در هر وضعیتی رخ دهد و تحلیلگر لغوی باید بتواند آن را مدیریت کند. بررسی پایان ورودی، جریان کار عادی تحلیلگر را قطع کرده و انشعابهای شرطی را به برنامهٔ تولیدشده اضافه میکند؛ بنابراین به حداقل رساندن تعداد این بررسیها ضروری است. ابزار re2hs از چندین روش مختلف برای مدیریت پایان ورودی پشتیبانی میکند. انتخاب روش مناسب به پیچیدگی عبارات باقاعده، نیاز به بافرسازی، ملاحظات کارایی و عوامل دیگر بستگی دارد. در ادامه فهرستی از این روشها آمده است:
- سنتینل (Sentinel). این روش نیاز به بررسیهای پایان ورودی را بهکلی برطرف میکند. این روش ساده و کارآمد است، اما محدود به مواردی است که یک نویسهٔ «سنتینل» طبیعی وجود داشته باشد که هرگز نتواند در ورودی معتبر رخ دهد. این نویسه ممکن است همچنان در ورودیهای نامعتبر ظاهر شود، اما نباید توسط عبارات باقاعده مجاز دانسته شود، مگر شاید به عنوان آخرین نویسهٔ یک قاعده. نویسهٔ سنتینل به انتهای ورودی ضمیمه میشود و به عنوان سیگنال توقف عمل میکند: هنگامی که تحلیلگر لغوی این نویسه را میخواند، یا خطای نحوی رخ داده و یا پایان ورودی است. در هر دو حالت، تحلیلگر لغوی باید متوقف شود. این روش در صورتی استفاده میشود که YYFILL با دستور re2c:yyfill:enable = 0; غیرفعال شده باشد و re2c:eof مقدار پیشفرض -1 را داشته باشد.
- سنتینل با بررسیهای مرزی (Sentinel with bounds checks). این روش عمومی است: اجازه میدهد هر ورودی بدون محدودیت روی عبارات باقاعده مدیریت شود. ایدهٔ کار، کاهش تعداد بررسیهای پایان ورودی از طریق انجام آنها فقط بر روی نویسههای خاصی است. مشابه روش «سنتینل»، یکی از نویسهها به عنوان «سنتینل» انتخاب شده و به انتهای ورودی ضمیمه میشود. با این حال، هیچ محدودیتی در محل وقوع سنتینل وجود ندارد (در واقع هر نویسهای میتواند به عنوان سنتینل انتخاب شود). هنگامی که تحلیلگر لغوی این نویسه را میخواند، علاوه بر آن یک بررسی مرزها (bounds check) را نیز انجام میدهد. اگر موقعیت فعلی در محدودهٔ مجاز باشد، تحلیلگر تطبیق را از سر گرفته و با سنتینل مانند یک نویسهٔ عادی رفتار میکند. در غیر این صورت، 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; غیرفعال کنید.
زیربخشهای بعدی شامل مثالی از هر روش هستند..SS نگهبان (Sentinel)
این مثال از یک کاراکتر نگهبان برای مدیریت پایان ورودی استفاده میکند. این برنامه کلمات جداشده با فاصله را در یک رشتهٔ خاتمهیافته با null شمارش میکند. نگهبان همان null است: آخرین کاراکتر هر رشتهٔ ورودی است و طبق هیچیک از قواعد در میانهٔ یک لکسم (lexeme) مجاز نیست (بهویژه، در بازههای کاراکتری که نادیدهگرفتن آن آسان است گنجانده نشده است). اگر یک null در میانهٔ یک رشته رخ دهد، یک خطای نحوی است و تحلیلگر لغوی (lexer) با قاعدهٔ پیشفرض * تطبیق مییابد، اما فراتر از انتهای ورودی را نخواهد خواند و از کار نخواهد افتاد (برای تأیید این موضوع، از هشدار -Wsentinel-in-midrule و پیکربندی re2c:sentinel استفاده کنید). پیکربندی re2c:yyfill:enable = 0; از تولید بررسیهای مرز و فراخوانیهای YYFILL جلوگیری میکند.
-- re2hs $INPUT -o $OUTPUT
{-# OPTIONS_GHC -Wno-unused-record-wildcards #-}
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad (when)
import Data.ByteString (ByteString, index)
data State = State {
_yyinput :: ByteString,
_yycursor :: Int,
_count :: Int
}
-- expect a null-terminated string
%{
re2c:YYFN = ["lexer;Int", "State{..};State"];
re2c:yyfill:enable = 0;
* { (-1) }
[\x00] { _count }
[a-z]+ { lexer State{_count = _count + 1, ..} }
[ ]+ { lexer State{..} }
%}
main :: IO ()
main = do
let test s n = when (lexer st /= n) $ error "failed"
where st = State{_yyinput = s, _yycursor = 0, _count = 0}
test "\0" 0
test "one two three\0" 3
test "f0ur\0" (-1)
نگهبان همراه با بررسیهای مرز (Sentinel with bounds checks)
این مثال از نگهبان به همراه بررسیهای مرز برای مدیریت پایان ورودی استفاده میکند (این روش در نسخهٔ 1.2 اضافه شد). این برنامه رشتههای محصور در علامت نقلقول تکی که با فاصله جدا شدهاند را شمارش میکند. کاراکتر نگهبان null است که با پیکربندی re2c:eof = 0; تعیین میشود. همانند روش sentinel، کاراکتر null آخرین کاراکتر هر رشتهٔ ورودی است، اما در میانهٔ یک قاعده مجاز است (برای نمونه، 'aaa\0aa'\0 یک ورودی معتبر است، اما 'aaa\0 یک خطای نحوی محسوب میشود). بررسیهای مرز در هر حالتی که با یک کاراکتر ورودی مطابقت داشته باشد تولید میشوند، اما دامنهٔ آنها به شاخهای که null را مدیریت میکند محدود است. بررسیهای مرز در API عمومی بهصورت YYLIMIT <= YYCURSOR یا YYLESSTHAN(1) هستند. اگر شرط بررسی برقرار باشد، تحلیلگر لغوی به انتهای ورودی رسیده است و باید متوقف شود (YYFILL با re2c:yyfill:enable = 0; غیرفعال شده است زیرا ورودی در یک بافر جای میگیرد؛ برای مثالی که از YYFILL استفاده میکند به بخش YYFILL with sentinel مراجعه کنید). رسیدن به انتهای ورودی سه احتمال را مطرح میکند: اگر تحلیلگر لغوی در وضعیت اولیه باشد، با قاعدهٔ پایان ورودی $ تطابق مییابد، در غیر این صورت ممکن است به یک قاعده با تطابق قبلی بازگردد (شامل قاعدهٔ پیشفرض *) یا به یک وضعیت پیشفرض برود که باعث هشدار -Wundefined-control-flow خواهد شد.
-- re2hs $INPUT -o $OUTPUT
{-# OPTIONS_GHC -Wno-unused-record-wildcards #-}
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad (when)
import qualified Data.ByteString as BS
import Data.Word
data State = State {
_yyinput :: BS.ByteString,
_yycursor :: Int,
_yymarker :: Int,
_yylimit :: Int,
_count :: Int
}
-- expect a null-terminated string
%{
re2c:YYFN = ["lexer;Int", "State{..};State"];
re2c:YYCTYPE = "Word8";
re2c:YYPEEK = "BS.index";
re2c:eof = 0;
re2c:yyfill:enable = 0;
str = ['] ([^'\\] | [\\][^])* ['];
* { (-1) }
$ { _count }
str { lexer State{_count = _count + 1, ..} }
[ ]+ { lexer State{..} }
%}
main :: IO ()
main = do
let test s n = do
let st = State {
_yyinput = s,
_yycursor = 0,
_yymarker = 0,
_yylimit = BS.length s - 1, -- terminating null not included
_count = 0}
when (lexer st /= n) $ error "failed"
test "\0" 0
test "'qu\0tes' 'are' 'fine: \\'' \0" 3
test "'unterminated\\'\0" (-1)
این مثال برای مدیریت پایان ورودی از بررسیهای محدوده با فاصلهگذاری (padding) استفاده میکند (این روش بهطور پیشفرض فعال است). این برنامه رشتههای محصور در نقلقول تکی را که با فاصله از یکدیگر جدا شدهاند شمارش میکند. فاصلهگذاریای شامل YYMAXFILL نویسهٔ null به انتهای ورودی پیوست شده است، که مقدار YYMAXFILL در آن بهطور خودکار توسط یک بلوک max تولید میشود. الزامی به استفاده از null برای فاصلهگذاری وجود ندارد --- تا زمانی که نویسهها پسوند معتبری برای یک نشانه (lexeme) تشکیل ندهند، میتوان از هر نویسهای استفاده کرد (در این مثال فاصلهگذاری نباید حاوی نقلقول تکی باشد، زیرا ممکن است با پسوند یک رشتهٔ محصور در نقلقول تکی اشتباه گرفته شود). یک قاعدهٔ "stop" وجود دارد که با نخستین نویسهٔ فاصلهگذاری (null) تطابق مییابد و تحلیلگر لغوی را متوقف میکند (توجه داشته باشید که این قاعده بررسی میکند آیا null در ابتدای فاصلهگذاری قرار دارد یا خیر، در غیر این صورت یک خطای نحوی است). بررسیهای محدوده تنها در برخی از حالتها که توسط مؤلفههای قویاً همبند (strongly connected components) در اتوماتون زیربنایی تعیین میشوند، تولید میگردند. بررسیها به صورت (YYLIMIT - YYCURSOR) < n یا در API عمومی به صورت YYLESSTHAN(n) هستند، که در آنها n حداقل تعداد نویسههای مورد نیاز برای ادامهٔ کار تحلیلگر لغوی است (همچنین بدین معناست که بررسی محدودهٔ بعدی حداکثر تا n نویسهٔ دیگر رخ خواهد داد). اگر شرط بررسی درست باشد، تحلیلگر لغوی به انتهای ورودی رسیده است و YYFILL(n) را فراخوانی میکند که باید یا حداقل n نویسهٔ ورودی را تأمین کند یا بازگشتی نداشته باشد. در این مثال YYFILL همواره با شکست مواجه شده و تحلیلگر لغوی را با یک خطا متوقف میکند (که مشکلی ایجاد نمیکند زیرا تمام ورودی درون یک بافر جا میشود). برای مثالی که بافر ورودی را با استفاده از YYFILL دوباره پر میکند، بخش YYFILL with padding را ببینید.
-- re2hs $INPUT -o $OUTPUT
{-# OPTIONS_GHC -Wno-unused-record-wildcards #-}
{-# LANGUAGE OverloadedStrings #-}
import Control.Exception
import Control.Monad (when)
import qualified Data.ByteString as BS
data State = State {
_yyinput :: BS.ByteString,
_yycursor :: Int,
_yylimit :: Int,
_count :: Int
}
data FillException = UnexpectedFill deriving (Show)
instance Exception FillException
yymaxfill :: Int
%{max %}
%{
re2c:YYFN = ["lexer;IO Int", "State{..};State"];
re2c:YYPEEK = "BS.index";
re2c:YYFILL = "throw UnexpectedFill";
re2c:monadic = 1; // YYFILL requires monadic do-notation for `when` conditions
str = ['] ([^'\\] | [\\][^])* ['];
[\x00] {
-- check that it is the sentinel, not some unexpected null
return $ if _yycursor == BS.length _yyinput - yymaxfill + 1 then _count else (-1)
}
str { lexer State{_count = _count + 1, ..} }
[ ]+ { lexer State{..} }
* { return (-1) }
%}
main :: IO ()
main = do
let test s n = do
let buf = BS.concat [s, BS.replicate yymaxfill 0]
let st = State {
_yyinput = buf,
_yycursor = 0,
_yylimit = BS.length buf,
_count = 0}
m <- catch (lexer st) (\(_ :: FillException) -> return (-2))
when (m /= n) $ error "failed"
test "" 0
test "'unterminated\\'" (-2)
test "'qu\0tes' 'are' 'fine: \\'' " 3
test "'unexpected' \0 'null'" (-1)
این مثال از یک روش سفارشی مدیریت پایان ورودی بر پایهی generic API استفاده میکند. این برنامه رشتههای تککوتیشندار جداشده با فاصله را شمارش میکند. این مثال همانند مثال sentinel است، با این تفاوت که ورودی با null خاتمه نمییابد. برای جبران نبود نویسه نگهبان در انتهای ورودی، YYPEEK بازتعریف شده است تا پیش از خواندن نویسه بعدی ورودی، یک بررسی محدوده انجام دهد. این کار ناکارآمد است زیرا بررسیها بسیار مکرر انجام میشوند. اگر شرط بررسی رد شود، YYPEEK نویسه واقعی را بازمیگرداند، در غیر این صورت یک نویسه نگهبان ساختگی بازمیگرداند.
-- re2hs $INPUT -o $OUTPUT
{-# OPTIONS_GHC -Wno-unused-record-wildcards #-}
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad (when)
import qualified Data.ByteString as BS
data State = State {
_str :: BS.ByteString,
_cur :: Int,
_lim :: Int,
_cnt :: Int
}
-- Expect a string without terminating null.
%{
re2c:api = generic;
re2c:YYFN = ["lexer;Int", "State{..};State"];
re2c:YYPEEK = "if _cur < _lim then BS.index _str _cur else 0";
re2c:YYSKIP = "let cur = _cur + 1 in let _cur = cur in";
re2c:yyfill:enable = 0;
* { (-1) }
[\x00] { _cnt }
[a-z]+ { lexer State{_cnt = _cnt + 1, ..} }
[ ]+ { lexer State{..} }
%}
main :: IO ()
main = do
let test s n =
let st = State {_str = s, _cur = 0, _lim = BS.length s, _cnt = 0}
in when (lexer st /= n) $ error "failed"
test "" 0
test "one two three " 3
test "f0ur" (-1)
پر کردن مجدد بافر (BUFFER REFILLING)
نیاز به بافرسازی زمانی پیش میآید که ورودی نتواند بهیکباره در حافظه نگاشت شود: یا به دلیل اینکه بسیار حجیم است، یا اینکه بهصورت جریانی (مانند خواندن از یک سوکت) وارد میشود. تکنیک معمول در چنین مواردی، تخصیص یک بافر حافظه با اندازه ثابت و پردازش ورودی در تکههایی است که در بافر جا میشوند. هنگامی که تکه فعلی پردازش شد، به بیرون منتقل شده و دادههای جدید وارد میشوند. در عمل این کار تا حدودی پیچیدهتر است، زیرا وضعیت لکسر تنها از یک موقعیت ورودی تشکیل نشده، بلکه شامل مجموعهای از موقعیتهای مرتبط با یکدیگر است:
- مکاننما (cursor): نویسه ورودی بعدی که باید خوانده شود (YYCURSOR در C pointer API یا YYSKIP/YYPEEK در generic API)
- حد (limit): موقعیت پس از آخرین نویسه در دسترس ورودی (YYLIMIT در C pointer API، که بهطور ضمنی توسط YYLESSTHAN در generic API مدیریت میشود)
- نشانهگذار (marker): موقعیت تازهترین تطابق، در صورت وجود (YYMARKER در default API یا YYBACKUP/YYRESTORE در generic API)
- توکن (token): آغاز نشانه (lexeme) فعلی (در re2hs API ضمنی است، زیرا برای عملکرد عادی لکسر نیازی به آن نیست و میتواند توسط کاربر تعریف و بهروزرسانی شود)
- نشانهگذار بافتار (context marker): موقعیت بافتار پسرو (YYCTXMARKER در C pointer API یا YYBACKUPCTX/YYRESTORECTX در generic API)
- متغیرهای تگ (tag variables): موقعیتهای زیرتطابق (تعریفشده با بلوکهای stags و mtags و توابع پایهای generic API شامل YYSTAGP/YYSTAGN/YYMTAGP/YYMTAGN)
همهی این موارد در هر حالتی استفاده نمیشوند، اما در صورت استفاده، باید توسط YYFILL بهروزرسانی شوند. تمام موقعیتهای فعال در بخشی بین token و cursor قرار دارند، بنابراین هر چیزی بین آغاز بافر و token را میتوان دور انداخت، بخش بین token تا limit باید به ابتدای بافر منتقل شود، و فضای خالی در انتهای بافر باید با دادههای جدید پر گردد. برای جلوگیری از فراخوانیهای مکرر YYFILL، بهتر است تا حد امکان نویسههای ورودی بیشتری پر شوند (حتی اگر نویسههای کمتری برای ازسرگیری لکسر کافی باشد). جزئیات پیادهسازی YYFILL بسته به روش مدیریت EOF استفادهشده کمی متفاوت است: حالت قاعده EOF تا حدودی سادهتر از حالت بررسی محدوده با لایهگذاری (padding) است. همچنین توجه داشته باشید که اگر گزینه -f --storable-state استفاده شود، YYFILL معناشناسی اندکی متفاوت خواهد داشت (که در بخش مربوط به وضعیت ذخیرهپذیر توضیح داده شده است)..SS "YYFILL همراه با نگهبان (sentinel)"
اگر از قاعده EOF استفاده شود، YYFILL یک سازوکار پایهای تابعمانند است که هیچ آرگومانی نمیپذیرد و مقداری بازمیگرداند که در برابر صفر بررسی میشود. فراخوانی YYFILL با شرط YYLIMIT <= YYCURSOR در API اشارهگر C و YYLESSTHAN() در API عمومی برانگیخته میشود. یک مقدار بازگشتی غیرصفر بدین معنی است که YYFILL با شکست مواجه شده است. یک فراخوانی موفق YYFILL باید حداقل یک نویسه را فراهم کرده و موقعیتهای ورودی را متناسب با آن تنظیم کند. Limit باید همیشه روی یکی پس از آخرین موقعیت ورودی در بافر تنظیم شود، و نویسهٔ موجود در موقعیت limit باید نماد نگهبان مشخصشده توسط پیکربندی re2c:eof باشد. تصاویر زیر موقعیتهای نسبی ورودی در بافر را پیش و پس از فراخوانی YYFILL نشان میدهند (نماد نگهبان با # نشانهگذاری شده است، و تصویر دوم حالتی را نشان میدهد که ورودی کافی برای پر کردن کل بافر وجود ندارد).
<-- shift -->
>-A------------B---------C-------------D#-----------E->
buffer token marker limit,
cursor
>-A------------B---------C-------------D------------E#->
buffer, marker cursor limit
token
<-- shift -->
>-A------------B---------C-------------D#--E (EOF)
buffer token marker limit,
cursor
>-A------------B---------C-------------D---E#........
buffer, marker cursor limit
token
در اینجا مثالی از یک برنامه آورده شده است که فایل ورودی input.txt را در تکههای ۴۰۹۶ بایتی میخواند و از قاعده EOF استفاده میکند.
-- re2hs $INPUT -o $OUTPUT
{-# OPTIONS_GHC -Wno-unused-record-wildcards #-}
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad
import Data.ByteString as BS
import Data.Word
import GHC.IO.Handle
import System.Directory
import System.IO
chunk_size :: Int
chunk_size = 4096
data State = State {
_file :: !Handle,
_yyinput :: !BS.ByteString,
_yycursor :: !Int,
_yymarker :: !Int,
_yylimit :: !Int,
_token :: !Int,
_eof :: !Bool,
_count :: !Int
}
%{
re2c:YYFN = ["lexer;IO Int", "State{..};State;!State{..}"];
re2c:YYCTYPE = "Word8";
re2c:YYPEEK = "BS.index";
re2c:YYFILL = "(State{..}, yyfill) <- fill State{..}";
re2c:eof = 0;
re2c:monadic = 1;
str = ['] ([^'\\] | [\\][^])* ['];
* { return (-1) }
$ { return _count }
str { lexer State{_token = _yycursor, _count = _count + 1, ..} }
[ ]+ { lexer State{_token = _yycursor, ..} }
%}
fill :: State -> IO (State, Bool)
fill State{..} = do
case _eof of
True -> return (State{..}, False)
False -> do
-- Discard everything up to the current token, cut off terminating null,
-- read new chunk from file and reappend terminating null at the end.
chunk <- BS.hGet _file chunk_size
return (State {
_yyinput = BS.concat [(BS.init . BS.drop _token) _yyinput, chunk, "\0"],
_yycursor = _yycursor - _token,
_yymarker = _yymarker - _token,
_yylimit = _yylimit - _token + BS.length chunk, -- exclude terminating null
_token = 0,
_eof = BS.null chunk, -- end of file?
..}, True)
main :: IO ()
main = do
let fname = "input"
-- Prepare input file.
BS.writeFile fname $ BS.concat ["'qu\0tes' 'are' 'fine: \\'' " | _ <- [1..chunk_size]]
let expect = 3 * chunk_size -- the total number of strings in file
-- Run lexer on the prepared file.
fh <- openFile fname ReadMode
let st = State {
_file = fh,
_yyinput = BS.singleton 0,
_yycursor = 0,
_yymarker = 0,
_token = 0,
_yylimit = 0,
_eof = False,
_count = 0
}
result <- lexer st
hClose fh
-- Cleanup.
removeFile fname
-- Check result.
when (result /= expect) $ error $ "expected " ++ show expect ++ ", got " ++ show result
return ()
در حالت پیشفرض (زمانی که قاعدهٔ EOF به کار نمیرود)، YYFILL یک سازوکار پایهایِ شبهتابع است که یک تکآرگومان را میپذیرد و هیچ مقداری را برنمیگرداند. فراخوانی YYFILL با شرط (YYLIMIT - YYCURSOR) < n در API اشارهگر C و با YYLESSTHAN(n) در API عمومی فعال میشود. آرگومان فرستادهشده به YYFILL حداقل تعداد نویسههایی است که باید تأمین شوند. اگر در تأمین این مقدار شکست بخورد، YYFILL نباید کنترل را به lexer بازگرداند (به همین دلیل بهتر است بهعنوان ماکرویی پیادهسازی شود که در صورت شکست، از تابع فراخواننده خارج میشود). در صورت فراخوانی موفقیتآمیز YYFILL، موقعیت مرز (limit) باید یا روی یک موقعیت پس از آخرین موقعیت ورودی در بافر تنظیم شود، یا روی انتهای پدینگ YYMAXFILL (در حالتی که YYFILL حداقل n نویسه را با موفقیت خوانده باشد، اما این مقدار برای پر کردن کل بافر کافی نباشد). تصاویر زیر موقعیتهای نسبی جایگاههای ورودی در بافر را پیش و پس از فراخوانی YYFILL نشان میدهند (پدینگ YYMAXFILL در تصویر دوم با نمادهای # مشخص شده است).
<-- shift --> <-- need -->
>-A------------B---------C-----D-------E---F--------G->
buffer token marker cursor limit
>-A------------B---------C-----D-------E---F--------G->
buffer, marker cursor limit
token
<-- shift --> <-- need -->
>-A------------B---------C-----D-------E-F (EOF)
buffer token marker cursor limit
>-A------------B---------C-----D-------E-F###############
buffer, marker cursor limit
token <- YYMAXFILL ->
در اینجا مثالی از برنامهای آورده شده است که فایل ورودی input.txt را در قطعات ۴۰۹۶ بایتی میخواند و از بررسی محدودهها (bounds-checking) به همراه پدینگ استفاده میکند.
-- re2hs $INPUT -o $OUTPUT
{-# OPTIONS_GHC -Wno-unused-record-wildcards #-}
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad
import qualified Data.ByteString as BS
import GHC.IO.Handle
import System.Directory
import System.IO
chunk_size :: Int
chunk_size = 4096
data State = State {
_file :: !Handle,
_yyinput :: !BS.ByteString,
_yycursor :: !Int,
_yylimit :: !Int,
_token :: !Int,
_eof :: !Bool,
_count :: !Int
}
%{
re2c:YYFN = ["lexer;IO Int", "State{..};State;!State{..}"];
re2c:YYPEEK = "BS.index";
// We have to turn off autogenerated YFILL check and write it manually as part of YYFILL
// implementation, so that we can propagate the updated state out of it.
re2c:yyfill:check = 0;
re2c:YYFILL = "State{..} <- fill State{..} @@";
re2c:monadic = 1;
str = ['] ([^'\\] | [\\][^])* ['];
* { return (-1) }
[\x00] { return $ if _yycursor == _yylimit - yymaxfill + 1 then _count else (-1) }
str { lexer State{_token = _yycursor, _count = _count + 1, ..} }
[ ]+ { lexer State{_token = _yycursor, ..} }
%}
yymaxfill :: Int
%{max %}
fill :: State -> Int -> IO State
fill !st@State{..} !need =
if _yylimit - _yycursor >= need then
return st
else case _eof of
True -> error "fill failed"
False -> do
-- Discard everything up to the current token, cut off terminating null,
-- read new chunk from file and reappend terminating null at the end.
chunk <- BS.hGet _file chunk_size
let !eof = BS.length chunk < need -- end of file ?
let !buf = BS.concat [
BS.drop _token _yyinput,
chunk,
if eof then (BS.replicate yymaxfill 0) else BS.empty]
return State {
_yyinput = buf,
_yycursor = _yycursor - _token,
_yylimit = BS.length buf,
_token = 0,
_eof = eof,
..}
main :: IO ()
main = do
let fname = "input"
-- Prepare input file.
BS.writeFile fname $ BS.concat ["'qu\0tes' 'are' 'fine: \\'' " | _ <- [1..chunk_size]]
let expect = 3 * chunk_size -- the total number of strings in file
-- Run lexer on the prepared file.
fh <- openFile fname ReadMode
let st = State {
_file = fh,
_yyinput = BS.empty,
_yycursor = 0,
_token = 0,
_yylimit = 0,
_eof = False,
_count = 0
}
result <- lexer st
hClose fh
-- Cleanup.
removeFile fname
-- Check result.
when (result /= expect) $ error $ "expected " ++ show expect ++ ", got " ++ show result
return ()
چندین بلوک (Multiple blocks)
گاهی اوقات لازم است چندین تحلیلگر لغوی (lexer) مرتبط با یکدیگر داشته باشیم (برای مثال، اگر یک ماشین وضعیت سطح بالا وجود داشته باشد که بین حالتهای مختلف تحلیلگر جابجا میشود). این قابلیت میتواند با استفاده از چندین بلوک متصل re2hs پیادهسازی شود. گزینه دیگر، استفاده از start conditions است.
پیادهسازی اتصالات میان بلوکها به زبان مقصد بستگی دارد. در زبانهایی که دستور goto دارند (مانند C/C++ و Go) میتوان همه بلوکها را در یک تابع قرار داد، به طوری که ابتدای هر کدام یک برچسب (label) قرار گیرد. گذار از یک بلوک به بلوک دیگر یک goto ساده است. در زبانهایی که دستور goto ندارند (مانند Rust) استفاده از یک حلقه به همراه switch روی یک متغیر وضعیت ضروری است، مشابه با حلقه/switch تولید شده برای yystate توسط re2hs، یا اینکه هر بلوک درون یک تابع قرار گرفته و از فراخوانی توابع استفاده شود.
مثال زیر از چندین بلوک برای تجزیه اعداد دودویی (binary)، هشتهشتی (octal)، دهدهی (decimal) و شانزدهشانزدهی (hexadecimal) استفاده میکند. هر مبنا بلوک مختص به خود را دارد. بلوک اولیه مبنا را تعیین کرده و به بلوکهای دیگر هدایت میکند. پیکربندیهای مشترک در یک بلوک مجزا در ابتدای برنامه تعریف شدهاند؛ این تنظیمات توسط سایر بلوکها به ارث برده میشوند.
-- re2hs $INPUT -o $OUTPUT -i
{-# OPTIONS_GHC -Wno-unused-record-wildcards #-}
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad (when)
import Data.ByteString (ByteString, index)
data State = State {
_yyinput :: !ByteString,
_yycursor :: !Int,
_yymarker :: !Int
}
peek_digit :: ByteString -> Int -> Int -> Int
peek_digit str idx offs = fromIntegral (index str (idx - 1)) - offs
%{
re2c:yyfill:enable = 0;
%}
%{local
re2c:YYFN = ["parse_bin;Int", "State{..};State", "num;Int"];
[01] { parse_bin State{..} $ num * 2 + (peek_digit _yyinput _yycursor 48) }
* { num }
%}
%{local
re2c:YYFN = ["parse_oct;Int", "State{..};State", "num;Int"];
[0-7] { parse_oct State{..} $ num * 8 + (peek_digit _yyinput _yycursor 48) }
* { num }
%}
%{local
re2c:YYFN = ["parse_dec;Int", "State{..};State", "num;Int"];
[0-9] { parse_dec State{..} $ num * 10 + (peek_digit _yyinput _yycursor 48) }
* { num }
%}
%{local
re2c:YYFN = ["parse_hex;Int", "State{..};State", "num;Int"];
[0-9] { parse_hex State{..} $ num * 16 + (peek_digit _yyinput _yycursor 48) }
[a-f] { parse_hex State{..} $ num * 16 + (peek_digit _yyinput _yycursor 87) }
[A-F] { parse_hex State{..} $ num * 16 + (peek_digit _yyinput _yycursor 55) }
* { num }
%}
%{local
re2c:YYFN = ["parse;Maybe Int", "State{..};State"];
'0b' / [01] { Just $ parse_bin State{..} 0 }
"0" { Just $ parse_oct State{..} 0 }
"" / [1-9] { Just $ parse_dec State{..} 0 }
'0x' / [0-9a-fA-F] { Just $ parse_hex State{..} 0 }
* { Nothing }
%}
test :: ByteString -> Maybe Int -> IO ()
test str expect = do
let s = State {_yyinput = str, _yycursor = 0, _yymarker = 0}
when (parse s /= expect) $ error "failed!"
main :: IO ()
main = do
test "\0" Nothing
test "1234567890\0" (Just 1234567890)
test "0b1101\0" (Just 13)
test "0x7Fe\0" (Just 2046)
test "0644\0" (Just 420)
test "9223372036854775807\0" (Just 9223372036854775807)
شرایط شروع با گزینهٔ --start-conditions فعال میشوند. آنها روشی را برای کدگذاری چندین اتوماتون مرتبط با یکدیگر درون همان بلوک re2hs فراهم میکنند.
هر شرط متناظر با یک اتوماتون منفرد است و یک نام یکتای مشخصشده توسط کاربر و یک شمارهٔ داخلی یکتای تعریفشده توسط re2hs دارد. این شمارهها برای جابهجایی میان شرطها استفاده میشوند: کد تولیدشده از پیشساختههای 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 پرش میکند.
کدی که re2hs برای شرطها تولید میکند به این بستگی دارد که آیا re2hs از رویکرد goto/label استفاده میکند یا از رویکرد loop/switch برای کدگذاری اتوماتا.
در زبانهایی که دارای دستور goto هستند (مانند C/C++ و Go)، شرطها بهطور طبیعی به صورت بلوکهایی از کد با برچسبهایی به شکل yyc_<cond> پیادهسازی میشوند، که در آن cond نام شرط است (پیشوند برچسب را میتوان با re2c:cond:prefix تغییر داد). گذارها میان شرطها با استفاده از goto و برچسبهای شرط پیادهسازی میشوند. پیش از تمام شرطها، re2hs یک switch اولیه بر روی YYGETSTATE تولید میکند که به وضعیت شروع شرط فعلی پرش میکند. قواعد میانبر :=> این switch اولیه را دور میزنند و مستقیماً به شرط مشخصشده پرش میکنند (از re2c:cond:goto میتوان برای تغییر رفتار پیشفرض استفاده کرد). قواعد دارای اعمال معنایی بهطور خودکار به شرط بعدی پرش نمیکنند؛ این کار باید توسط کد عمل تعریفشده توسط کاربر انجام گیرد.
در زبانهایی که فاقد goto هستند (مانند Rust)، re2hs از متغیر yystate برای ذخیره شمارههای شرط استفادهٔ مجدد میکند. هر شرط یک شناسهٔ عددی برابر با شماره وضعیت شروع خود دریافت میکند، و switch میان شرطها تفاوتی با switch میان وضعیتهای DFA یک شرط منفرد ندارد. نیازی به یک switch اولیهٔ جداگانه برای شرط نیست. (از آنجا که رویکرد یکسانی برای پیادهسازی وضعیتهای قابل ذخیرهسازی استفاده میشود، در صورت استفادهٔ همزمان از وضعیتهای قابل ذخیرهسازی و شرطها، YYGETCOND/YYSETCOND زائد خواهند بود).
برنامهٔ زیر از شرایط شروع برای تجزیه اعداد دودویی، هشتهشتی، دهدهی و شانزدهشانزدهی استفاده میکند. یک بلوک منفرد وجود دارد که در آن هر مبنا شرط مخصوص به خود را دارد، و شرط اولیه به همهٔ آنها متصل است. متغیر تعریفشده توسط کاربر cond شمارهٔ شرط فعلی را ذخیره میکند؛ این متغیر با شمارهٔ شرط اولیه که با بلوک conditions تولید شده است مقداردهی اولیه میشود.
-- re2hs $INPUT -o $OUTPUT -ci
{-# OPTIONS_GHC -Wno-unused-record-wildcards #-}
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad (when)
import Data.ByteString (ByteString, index)
%{conditions %}
data State = State {
_yyinput :: !ByteString,
_yycursor :: !Int,
_yymarker :: !Int,
_yycond :: !YYCONDTYPE
}
peek_digit :: ByteString -> Int -> Int -> Int
peek_digit str idx offs = fromIntegral (index str (idx - 1)) - offs
%{
re2c:YYFN = ["parse;Maybe Int", "State{..};State", "_num;Int"];
re2c:yyfill:enable = 0;
<init> '0b' / [01] :=> bin
<init> "0" :=> oct
<init> "" / [1-9] :=> dec
<init> '0x' / [0-9a-fA-F] :=> hex
<init> * { Nothing }
<bin> [01] { yyfnbin State{..} $ _num * 2 + (peek_digit _yyinput _yycursor 48) }
<oct> [0-7] { yyfnoct State{..} $ _num * 8 + (peek_digit _yyinput _yycursor 48) }
<dec> [0-9] { yyfndec State{..} $ _num * 10 + (peek_digit _yyinput _yycursor 48) }
<hex> [0-9] { yyfnhex State{..} $ _num * 16 + (peek_digit _yyinput _yycursor 48) }
<hex> [a-f] { yyfnhex State{..} $ _num * 16 + (peek_digit _yyinput _yycursor 87) }
<hex> [A-F] { yyfnhex State{..} $ _num * 16 + (peek_digit _yyinput _yycursor 55) }
<bin, oct, dec, hex> * { Just _num }
%}
test :: ByteString -> Maybe Int -> IO ()
test str expect = do
let s = State {
_yyinput = str,
_yycursor = 0,
_yymarker = 0,
_yycond = YYC_init}
when (parse s 0 /= expect) $ error "failed!"
main :: IO ()
main = do
test "\0" Nothing
test "1234567890\0" (Just 1234567890)
test "0b1101\0" (Just 13)
test "0x7Fe\0" (Just 2046)
test "0644\0" (Just 420)
test "9223372036854775807\0" (Just 9223372036854775807)
با گزینهٔ --storable-state، برنامهٔ re2hs یک تحلیلگر لغوی تولید میکند که میتواند وضعیت کنونی خود را ذخیره کند، به فراخواننده بازگردد، و بعداً عملیات را دقیقاً از همانجایی که رها کرده بود از سر بگیرد. حالت پیشفرض عملکرد در re2hs یک مدل «کِششی» ("pull") است که در آن تحلیلگر لغوی هر زمان که به ورودی بیشتری نیاز داشته باشد آن را «میکشد». این حالت ممکن است در مواردی که ورودی به صورت تکهتکه در دسترس قرار میگیرد غیرقابل قبول باشد (برای مثال، اگر تحلیلگر لغوی توسط تجزیهکننده فراخوانی شود، یا اگر برنامهٔ تحلیلگر لغوی از طریق پروتکل سوکت با برنامهٔ دیگری ارتباط برقرار کند که باید پیش از ارسال پیام بعدی، منتظر پاسخ از تحلیلگر لغوی بماند). قابلیت وضعیت قابل ذخیرهسازی دقیقاً برای چنین مواردی در نظر گرفته شده است: این ویژگی امکان میدهد تحلیلگرهای لغوی تولید شوند که در یک مدل «رانشی» ("push") کار میکنند. هنگامی که تحلیلگر لغوی به ورودی بیشتری نیاز داشته باشد، وضعیت خود را ذخیره کرده و به فراخواننده بازمیگردد. سپس، هنگامی که ورودی بیشتری در دسترس قرار گیرد، فراخواننده تحلیلگر لغوی را دقیقاً از جایی که متوقف شده بود از سر میگیرد. در مقایسه با مدل «کِششی»، چند تغییر ضروری است:
- تعریف دستورهای اولیهٔ YYSETSTATE() و YYGETSTATE(state).
- تعریف متغیرهای yych، yyaccept (در صورت استفاده) و state به عنوان بخشی از وضعیت ماندگار تحلیلگر لغوی. متغیر state باید با مقدار -1 مقداردهی اولیه شود.
- دستور YYFILL به جای تلاش برای تأمین ورودی بیشتر، باید به برنامهٔ بیرونی بازگردد. کد بازگشتی باید نشان دهد که تحلیلگر لغوی به ورودی بیشتری نیاز دارد.
- برنامهٔ بیرونی باید موقعیتهایی را که در آن تحلیلگر لغوی به ورودی بیشتری نیاز دارد تشخیص داده و واکنش مناسب نشان دهد.
- به صورت اختیاری، استفاده از بلوک getstate برای تولید سوییچ YYGETSTATE جدا از تحلیلگر لغوی اصلی. این قابلیت تنها برای زبانهایی کار میکند که دارای goto هستند (نه در حالت --loop-switch).
- استفاده از re2c:eof و روش sentinel with bounds checks برای مدیریت پایان ورودی. روش مبتنی بر پدینگ (padding) ممکن است کار نکند زیرا مشخص نیست چه زمانی باید پدینگ اضافه شود: پایان فعلی ورودی ممکن است پایان نهایی ورودی نباشد، و افزودن زودهنگام پدینگ ممکن است یک لکسم طماع را که به طور جزئی خوانده شده است ناقص قطع کند. علاوه بر این، به دلیل منطق سطح بالای برنامه، دریافت ورودی بیشتر ممکن است به پردازش لکسم در انتهای بافر وابسته باشد (که خود به دلیل شرط پایان ورودی مسدود شده است).
در اینجا مثالی از یک تحلیلگر لغوی با مدل «رانشی» ("push") آورده شده است که خواندن بستهها از یک سوکت را شبیهسازی میکند. تحلیلگر لغوی تا زمانی که با پایان ورودی مواجه شود در حلقه اجرا میشود و به تابع فراخواننده بازمیگردد. تابع فراخواننده با «ارسال» بستهٔ بعدی ورودی بیشتری را فراهم میکند و تحلیل لغوی را از سر میگیرد. این فرآیند زمانی متوقف میشود که همهٔ بستهها ارسال شده باشند، یا خطایی رخ دهد.
-- re2hs $INPUT -o $OUTPUT -fi
{-# OPTIONS_GHC -Wno-unused-record-wildcards #-}
{-# LANGUAGE OverloadedStrings #-}
import Control.Concurrent.Chan
import Control.Monad
import Data.ByteString as BS
import Text.Printf
debug :: IO () -> IO ()
debug = when False
data State = State {
_pipe :: !(Chan BS.ByteString),
_yyinput :: !BS.ByteString,
_yycursor :: !Int,
_yymarker :: !Int,
_yylimit :: !Int,
_token :: !Int,
_eof :: !Bool,
_yystate :: !Int,
_recv :: !Int
}
data Status = End | Ready | Waiting | BadPacket deriving (Eq)
%{
re2c:YYFN = ["lexer;IO (State, Status)", "State{..};State;!State{..}"];
re2c:YYPEEK = "BS.index";
re2c:YYFILL = "return (State{..}, Waiting)";
re2c:eof = 0;
re2c:monadic = 1;
packet = [a-z]+[;];
* { return (State{..}, BadPacket) }
$ { return (State{..}, End) }
packet { lexer State{_token = _yycursor, _recv = _recv + 1, ..} }
%}
fill :: State -> IO (State, Status)
fill st@State{..} = do
case _eof of
True -> return (st, End)
False -> do
-- Discard everything up to the current token, cut off terminating null,
-- read new chunk from file and reappend terminating null at the end.
chunk <- readChan _pipe
return (State {
_yyinput = BS.concat [(BS.init . BS.drop _token) _yyinput, chunk, "\0"],
_yycursor = _yycursor - _token,
_yymarker = _yymarker - _token,
_yylimit = _yylimit - _token + BS.length chunk, -- exclude terminating null
_token = 0,
_eof = BS.null chunk, -- end of file?
..}, Ready)
loop :: State -> [BS.ByteString] -> IO Status
loop State{..} packets = do
(State{..}, status) <- lexer State{..}
case status of
End -> do
debug $ printf "done: got %d packets\n" _recv
return End
Waiting -> do
debug $ printf "waiting...\n"
packets' <- case packets of
[] -> do
writeChan _pipe BS.empty
return []
p:ps -> do
debug $ printf "sent packet '%s'\n" (show p)
writeChan _pipe p
return ps
(State{..}, status') <- fill State{..}
case status' of
Ready -> loop State{..} packets'
_ -> error "unexpected status after fill"
BadPacket -> do
debug $ printf "error: ill-formed packet\n"
return BadPacket
_ -> error "unexpected status"
test :: [BS.ByteString] -> Status -> IO ()
test packets expect = do
pipe <- newChan -- emulate pipe using a chan of bytestrings
let st = State {
_pipe = pipe,
_yyinput = BS.singleton 0, -- null sentinel triggers YYFILL
_yycursor = 0,
_yymarker = 0,
_token = 0,
_yylimit = 0,
_eof = False,
_yystate = -1,
_recv = 0
}
status <- loop st packets
when (status /= expect) $ error "failed"
return ()
main :: IO ()
main = do
test [] End
test ["ze", "ro;o", "ne", ";t", "wo;thr", "e", "e", ";", "four;"] End
test ["zer0;"] BadPacket
بلوکهای قابل استفاده مجدد به شکل /*!rules:re2c[:<name>] ... */ یا %{rules[:<name>] ... %} میتوانند به هر تعداد دفعات دوباره استفاده شوند و با دیگر بلوکهای re2hs ترکیب گردند. مقدار <name> اختیاری است. یک بلوک rules میتواند در یک بلوک یا دستورالعمل use به کار رود. کد مربوط به یک بلوک rules در هر نقطه از استفاده تولید میشود.
بلوکهای use با /*!use:re2c[:<name>] ... */ یا %{use[:<name>] ... %} تعریف میشوند. مقدار <name> اختیاری است: اگر مشخص نشده باشد، بلوک rules مرتبط، نزدیکترین (جدیدترین) بلوک خواهد بود (خواه نامگذاریشده باشد یا بدون نام). یک بلوک use میتواند تعاریف نامگذاریشده، پیکربندیها و قوانین مختص به خود را اضافه کند. یک مورد کاربرد مهم برای بلوکهای use، تحلیلگر واژگانی (lexer) با پشتیبانی از چندین کدگذاری ورودی است: همان بلوک rules چندین بار با پیکربندیهای مختص هر کدگذاری مجدداً استفاده میشود (مثال زیر را ببینید).
دستورالعمل درونبلوکی !use:<name>; میتواند از درون یک بلوک re2hs استفاده شود. این دستور، بلوک ارجاعدادهشدهی <name> را در بلوک جاری ادغام میکند. اگر برخی از قوانین و پیکربندیهای ادغامشده با موارد تعریفشده پیشین همپوشانی داشته باشند، تداخلها به شیوه معمول حل میشوند: نخستین قانون اولویت دارد، و آخرین پیکربندی جایگزین موارد پیشین میشود. یک استثنا، قوانین ویژه *، $ و (در حالت شرطی) <!> هستند که در آنها تعریف محلیِ بلوک بر تعاریف به ارث رسیده برتری دارد. دستورالعمل use امکان ترکیب بلوکهای مختلف re2hs را در یک بلوک واحد فراهم میکند (مثال زیر را ببینید).
بلوکهای نامگذاریشده و دستورالعمل درونبلوکی use در نگارش 2.2 از re2hs اضافه شدند. از آن نگارش به بعد، بلوکهای قابل استفاده مجدد بهطور پیشفرض مجاز هستند (نیازی به گزینه خاصی نیست). پیش از نگارش 2.2، حالت استفاده مجدد با گزینه -r --reusable فعال میشد. پیش از نگارش 1.2، امکان ترکیب بلوکهای قابل استفاده مجدد با بلوکهای معمولی وجود نداشت.
مثالی از دستورالعمل !use
-- re2hs $INPUT -o $OUTPUT
{-# OPTIONS_GHC -Wno-unused-record-wildcards #-}
{-# LANGUAGE OverloadedStrings #-}
-- 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.
import Control.Monad (when)
import Data.ByteString (ByteString, index)
data Answer = Color | Fish | Dunno deriving (Eq)
data State = State {
_yyinput :: ByteString,
_yycursor :: Int,
_yymarker :: Int
}
%{rules:colors
* { error "ah" }
"red" | "salmon" | "magenta" { Color }
%}
%{rules:fish
* { error "oh" }
"haddock" | "salmon" | "eel" { Fish }
%}
%{
re2c:YYFN = ["lexer;Answer", "State{..};State"];
re2c:yyfill:enable = 0;
!use:fish;
!use:colors;
* { Dunno } // overrides inherited '*' rules
%}
main :: IO ()
main = do
let test str ans = do
let st = State {_yyinput = str, _yycursor = 0, _yymarker = 0}
when (lexer st /= ans) $ error "failed"
test "salmon" Fish
test "what?" Dunno
-- re2hs $INPUT -o $OUTPUT --input-encoding utf8
{-# OPTIONS_GHC -Wno-unused-record-wildcards #-}
{-# LANGUAGE OverloadedStrings #-}
-- 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.
import Control.Monad (when)
import Data.Array
import Data.Word
data State a = State {
_yyinput :: a,
_yycursor :: Int,
_yymarker :: Int
}
%{rules
re2c:yyfill:enable = 0;
re2c:YYPEEK = "(!)";
"∀x ∃y" { Just _yycursor }
* { Nothing }
%}
%{use
re2c:YYFN = ["lex8;Maybe Int", "State{..};State (Array Int Word8)"];
re2c:encoding:utf8 = 1;
re2c:YYCTYPE = Word8;
%}
%{use
re2c:YYFN = ["lex32;Maybe Int", "State{..};State (Array Int Word)"];
re2c:encoding:utf32 = 1;
re2c:YYCTYPE = Word;
%}
main :: IO ()
main = do
let make_st l = State {
_yyinput = listArray (0, length l - 1) l,
_yycursor = 0,
_yymarker = 0}
let s8 = [0xe2, 0x88, 0x80, 0x78, 0x20, 0xe2, 0x88, 0x83, 0x79]
when (lex8 (make_st s8) /= Just (length s8)) $ error "lex8 failed"
let s32 = [0x2200, 0x78, 0x20, 0x2203, 0x79]
when (lex32 (make_st s32) /= Just (length s32)) $ error "lex32 failed"
استخراج تطابق فرعی (Submatch extraction)
ابزار re2hs دو گزینه برای استخراج تطابق فرعی دارد.
- Tags
- گزینهٔ نخست استفاده از برچسبهای (tags) مستقل به فرم @stag یا #mtag است، که در آن stag و mtag نامهای دلخواه تعریفشده توسط کاربر هستند. برچسبها با گزینهٔ -T --tags یا پیکربندی re2c:tags = 1 فعال میشوند. از نظر معنایی، برچسبها نشانگرهای موقعیت هستند: میتوان آنها را در هر کجای یک عبارت منظم درج کرد و آنها به موقعیت متناظر (یا چندین موقعیت) در رشتهٔ ورودی متصل میشوند. S-tagها به آخرین موقعیت تطابق متصل میشوند و m-tagها به فهرستی از موقعیتها متصل میشوند (ممکن است در زیرعبارتهای تکرار استفاده شوند، جایی که یک موقعیت واحد در یک عبارت منظم با چندین موقعیت در رشتهٔ ورودی مطابقت دارد). تمام برچسبها باید توسط کاربر تعریف شوند، خواه به صورت دستی یا با کمک بلوکهای svars و mvars. اگر بیش از یک روش برای تطابق برچسبها با ورودی وجود داشته باشد، ابهام با استفاده از راهبرد رفع ابهام حریصانهٔ چپترین (leftmost greedy) برطرف میشود.
- Captures
- گزینهٔ دوم استفاده از گروههای گیرانداز (capturing groups) است. آنها با گزینهٔ --captures یا پیکربندی re2c:captures = 1 فعال میشوند. دو حالت برای سیاستهای رفع ابهام مختلف وجود دارد؛ --leftmost-captures (پیشفرض) برای سیاست حریصانهٔ چپترین، و --posix-captures برای سیاست طولانیترین تطابق POSIX است. در این حالت تمام زیرعبارتهای داخل پرانتز به عنوان گروههای گیرانداز در نظر گرفته میشوند و از علامت تعجب میتوان برای مشخص کردن گروههای غیرگیرانداز استفاده کرد: (! ... ). با گزینهٔ --invert-captures یا پیکربندی re2c:invert-captures = 1 معنای علامت تعجب معکوس میشود. تعداد گروهها برای قاعدهٔ تطابقیافته در متغیر yynmatch ذخیره میشود (کل عبارت منظم گروه شماره صفر است)، و نتایج تطابق فرعی در آرایهٔ yypmatch ذخیره میشوند. هر دوی yynmatch و yypmatch باید توسط کاربر تعریف شوند، و اندازهٔ yypmatch باید حداقل [yynmatch * 2] باشد. از بلوک maxnmatch برای تعریف YYMAXNMATCH استفاده کنید؛ ثابتی که برابر با بیشینه مقدار yynmatch در میان تمام قواعد است.
- Captvars
- روش دیگر برای استفاده از گروههای گیرانداز، گزینهٔ --captvars یا پیکربندی re2c:captvars = 1 است. تنها تفاوت آن با --captures در شیوهٔ ذخیرهسازی نتایج تطابق فرعی توسط کد تولیدشده است: به جای yynmatch و yypmatch، ابزار re2hs متغیرهای 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-tag از عملیاتهای زیر پشتیبانی میکنند:
- ذخیرهٔ موقعیت ورودی در یک s-tag: به صورت t = YYCURSOR با API اشارهگر C یا یک عملیات تعریفشده توسط کاربر YYSTAGP(t) با API عمومی (generic)
- ذخیرهٔ مقدار پیشفرض در یک s-tag: به صورت t = NULL با API اشارهگر C یا یک عملیات تعریفشده توسط کاربر YYSTAGN(t) با API عمومی
- رونوشت از یک s-tag به دیگری: t1 = t2
برچسبهای M-tag از عملیاتهای زیر پشتیبانی میکنند:
- الحاق موقعیت ورودی به یک m-tag: عملیات تعریفشده توسط کاربر YYMTAGP(t) با هر دو API پیشفرض و عمومی
- الحاق مقدار پیشفرض به یک m-tag: عملیات تعریفشده توسط کاربر YYMTAGN(t) با هر دو API پیشفرض و عمومی
- رونوشت از یک m-tag به دیگری: t1 = t2
برچسبهای S-tag را میتوان به عنوان مقادیر اسکالر (اشارهگرها یا آفستها) پیادهسازی کرد. برچسبهای M-tag به نمایش پیچیدهتری نیاز دارند، چرا که باید توالیای از مقادیر برچسب را ذخیره کنند. سادهترین و ناکارآمدترین نمایش از یک m-tag، یک لیست (آرایه، بردار) از مقادیر برچسب است؛ نمایشی کارآمدتر، ذخیرهٔ تمام m-tagها در یک درخت پیشوندی (prefix-tree) است که به صورت آرایهای از گرههای (v, p) نمایش داده میشود، که در آن v مقدار برچسب و p اشارهگری به گره والد است.
در اینجا مثال سادهای از استفاده از s-tagها برای تجزیهٔ نسخهبندی معنایی متشکل از سه جزء عددی آمده است: major، minor، patch (مورد آخر اختیاری است). برای یک مثال پیچیدهتر که از YYFILL استفاده میکند، بخشهای زیر را ببینید.
-- re2hs $INPUT -o $OUTPUT
{-# OPTIONS_GHC -Wno-unused-record-wildcards #-}
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad (when)
import Data.ByteString (ByteString, index)
data State = State {
-- Final tag bindings available in semantic action.
%{svars format = "\n@@ :: !Int,"; %}
-- Intermediate tag bindings used by the lexer (must be autogenerated).
%{stags format = "\n@@ :: !Int,"; %}
_yyinput :: !ByteString,
_yycursor :: !Int,
_yymarker :: !Int
}
data SemVer = SemVer {
major :: !Int,
minor :: !Int,
patch :: !Int
} deriving (Eq)
s2n :: ByteString -> Int -> Int -> Int
s2n s i j = f i 0 where
f k n = if k >= j then n else f (k + 1) (n * 10 + (fromIntegral (index s k) - 48))
%{
re2c:YYFN = ["parse;Maybe SemVer", "State{..};State"];
re2c:tags = 1;
re2c:yyfill:enable = 0;
num = [0-9]+;
@_1 num @_2 "." @_3 num @_4 ("." @_5 num)? [\x00] {
Just SemVer {
major = s2n _yyinput _1 _2,
minor = s2n _yyinput _3 _4,
patch = if _5 == (-1) then 0 else s2n _yyinput _5 (_yycursor - 1)
}
}
* { Nothing }
%}
test :: ByteString -> Maybe SemVer -> IO ()
test str expect = do
let s = State {
%{svars format = "\n@@ = (-1),"; %}
%{stags format = "\n@@ = (-1),"; %}
_yyinput = str,
_yycursor = 0,
_yymarker = 0
}
when (parse s /= expect) $ error "failed!"
main :: IO ()
main = do
test "23.34\0" (Just SemVer {major = 23, minor = 34, patch = 0})
test "1.2.99999\0" (Just SemVer {major = 1, minor = 2, patch = 99999})
test "1.a\0" Nothing
-- re2hs $INPUT -o $OUTPUT
{-# OPTIONS_GHC -Wno-unused-record-wildcards #-}
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad
import Data.ByteString as BS
import GHC.IO.Handle
import System.Directory
import System.IO
chunk_size :: Int
chunk_size = 4096
data State = State {
_file :: !Handle,
_yyinput :: !BS.ByteString,
_yycursor :: !Int,
_yymarker :: !Int,
_yylimit :: !Int,
_token :: !Int,
-- Final tag bindings available in semantic action.
%{svars format = "\n@@ :: !Int,"; %}
-- Intermediate tag bindings used by the lexer (must be autogenerated).
%{stags format = "\n@@ :: !Int,"; %}
_eof :: !Bool
}
data SemVer = SemVer {
major :: !Int,
minor :: !Int,
patch :: !Int
} deriving (Eq, Show)
s2n :: BS.ByteString -> Int -> Int -> Int
s2n s i j = f i 0 where
f k n = if k >= j then n else f (k + 1) (n * 10 + (fromIntegral (BS.index s k) - 48))
%{
re2c:YYFN = ["lexer;IO [SemVer]", "State{..};State", "_vers;[SemVer]"];
re2c:YYPEEK = "BS.index";
re2c:YYFILL = "(State{..}, yyfill) <- fill State{..}";
re2c:eof = 0;
re2c:monadic = 1;
re2c:tags = 1;
num = [0-9]+;
@_1 num @_2 "." @_3 num @_4 ("." @_5 num)? [\n] {
let ver = SemVer {
major = s2n _yyinput _1 _2,
minor = s2n _yyinput _3 _4,
patch = if _5 == (-1) then 0 else s2n _yyinput _5 (_yycursor - 1)
}
lexer State{..} (ver: _vers)
}
$ { return _vers }
* { error "lexer failed" }
%}
fill :: State -> IO (State, Bool)
fill State{..} = do
case _eof of
True -> return (State{..}, False)
False -> do
-- Discard everything up to the current token, cut off terminating null,
-- read new chunk from file and reappend terminating null at the end.
chunk <- BS.hGet _file chunk_size
return (State{
_yyinput = BS.concat [(BS.init . BS.drop _token) _yyinput, chunk, "\0"],
_yycursor = _yycursor - _token,
_yymarker = _yymarker - _token,
_yylimit = _yylimit - _token + BS.length chunk, -- exclude terminating null
_token = 0,
_eof = BS.null chunk, -- end of file?
..}, True)
main :: IO ()
main = do
let fname = "input"
-- Prepare input file.
BS.writeFile fname $ BS.concat ["1.22.333\n" | _ <- [1..chunk_size]]
let expect = [SemVer {major = 1, minor = 22, patch = 333} | _ <- [1..chunk_size]]
-- Run lexer on the prepared file.
fh <- openFile fname ReadMode
let st = State {
_file = fh,
_yyinput = BS.singleton 0,
_yycursor = 0,
_yymarker = 0,
_yylimit = 0,
_token = 0,
%{svars format = "\n@@ = (-1),"; %}
%{stags format = "\n@@ = (-1),"; %}
_eof = False
}
result <- lexer st []
hClose fh
-- Cleanup.
removeFile fname
-- Check result.
when (result /= expect) $ error $ "expected " ++ show expect ++ ", got " ++ show result
return ()
در اینجا مثالی از بهکارگیری گروههای گیرنده (capturing groups) برای تجزیهٔ نسخهبندی معنایی آورده شده است.
-- re2hs $INPUT -o $OUTPUT
{-# OPTIONS_GHC -Wno-unused-record-wildcards #-}
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad (when)
import Data.ByteString (ByteString, index)
import Data.Word (Word8)
none :: Int
none = -1
data State = State {
-- Final tag bindings available in semantic action.
%{svars format = "\n@@ :: !Int,"; %}
-- Intermediate tag bindings used by the lexer (must be autogenerated).
%{stags format = "\n@@ :: !Int,"; %}
_yyinput :: !ByteString,
_yycursor :: !Int,
_yymarker :: !Int
}
data SemVer = SemVer {
major :: !Int,
minor :: !Int,
patch :: !Int
} deriving (Eq)
s2n :: ByteString -> Int -> Int -> Int
s2n s i j = f i 0 where
f k n = if k >= j then n else f (k + 1) (n * 10 + (fromIntegral (index s k) - 48))
%{
re2c:YYFN = ["parse;Maybe SemVer", "State{..};State"];
re2c:YYCTYPE = "Word8";
re2c:captvars = 1;
re2c:yypmatch = _;
re2c:yyfill:enable = 0;
num = [0-9]+;
(num) "." (num) ("." num)? [\x00] {
Just SemVer {
major = s2n _yyinput _yytl1 _yytr1,
minor = s2n _yyinput _yytl2 _yytr2,
patch = if _yytl3 == none then 0 else s2n _yyinput (_yytl3 + 1) _yytr3
}
}
* { Nothing }
%}
test :: ByteString -> Maybe SemVer -> IO ()
test str expect = do
let s = State {
%{svars format = "\n@@ = none,"; %}
%{stags format = "\n@@ = none,"; %}
_yyinput = str,
_yycursor = 0,
_yymarker = 0
}
when (parse s /= expect) $ error "failed!"
main :: IO ()
main = do
test "23.34\0" (Just SemVer {major = 23, minor = 34, patch = 0})
test "1.2.99999\0" (Just SemVer {major = 1, minor = 2, patch = 99999})
test "1.a\0" Nothing
-- re2hs $INPUT -o $OUTPUT
{-# OPTIONS_GHC -Wno-unused-record-wildcards #-}
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad (when)
import Data.ByteString (ByteString, index)
data State = State {
-- Final tag bindings available in semantic action.
%{svars format = "\n@@ :: !Int,"; %}
%{mvars format = "\n@@ :: ![Int],"; %}
-- Intermediate tag bindings used by the lexer (must be autogenerated).
%{stags format = "\n@@ :: !Int,"; %}
%{mtags format = "\n@@ :: ![Int],"; %}
_yyinput :: !ByteString,
_yycursor :: !Int,
_yymarker :: !Int
}
s2n :: ByteString -> Int -> Int -> Int
s2n s i j = f i 0 where
f k n = if k >= j then n else f (k + 1) (n * 10 + (fromIntegral (index s k) - 48))
%{
re2c:YYFN = ["parse;Maybe [Int]", "State{..};State"];
re2c:YYMTAGP = "let tag = _yycursor : @@{tag} in let @@{tag} = tag in";
re2c:YYMTAGN = ""; // alternatively could add -1 to the list
re2c:tags = 1;
re2c:yyfill:enable = 0;
num = [0-9]+;
@_1 num @_2 ("." #_3 num #_4)* [\x00] {
Just $ (s2n _yyinput _1 _2) : (reverse $ zipWith (\i j -> s2n _yyinput i j) _3 _4)
}
* { Nothing }
%}
test :: ByteString -> Maybe [Int] -> IO ()
test str expect = do
let st = State {
%{svars format = "\n@@ = (-1),"; %}
%{stags format = "\n@@ = (-1),"; %}
%{mvars format = "\n@@ = [],"; %}
%{mtags format = "\n@@ = [],"; %}
_yyinput = str,
_yycursor = 0,
_yymarker = 0
}
when (parse st /= expect) $ error "failed!"
main :: IO ()
main = do
test "1\0" (Just [1])
test "1.2.3.4.5.6.7\0" (Just [1, 2, 3, 4, 5, 6, 7])
test "1.2.\0" Nothing
پشتیبانی از کدگذاری (Encoding support)
درک تفاوت بین code points (نقطهکدها) و code units (واحدهای کد) ضروری است. یک نقطهکد، یک شناسهٔ عددی برای یک نماد است. یک واحد کد، کوچکترین واحد ذخیرهسازی در متن کدگذاریشده است. یک نقطهکد منفرد ممکن است با یک یا چند واحد کد نمایش داده شود. در یک کدگذاری با طول ثابت، همهٔ نقطهکدها با تعداد یکسانی از واحدهای کد نمایش مییابند. در یک کدگذاری با طول متغیر، نقطهکدها ممکن است با تعداد متفاوتی از واحدهای کد نمایش داده شوند. توجه داشته باشید که قاعدهٔ "any" یعنی [^] با هر نقطهکدی مطابقت دارد، اما لزوماً با هر واحد کد مطابقت ندارد (تنها روش برای تطبیق با هر واحد کد فارغ از نوع کدگذاری، قاعدهٔ پیشفرض * است). تحلیلگر لغوی تولیدشده با جریانی از واحدهای کد کار میکند: yych یک واحد کد را ذخیره میکند و YYCTYPE نوع واحد کد است. از سوی دیگر، عبارتهای منظم برحسب نقطهکدها مشخص میشوند. هنگامی که re2hs عبارتهای منظم را به اتوماتا کامپایل میکند، نقطهکدها را به واحدهای کد ترجمه مینماید. این تبدیل عموماً یک نگاشت ساده نیست: در کدگذاریهای با طول متغیر، یک محدودهٔ نقطهکد منفرد ممکن است به یک گراف پیچیده از واحدهای کد ترجمه شود. کدگذاریهای زیر پشتیبانی میشوند:
- ASCII (بهطور پیشفرض فعال است). یک کدگذاری با طول ثابت با فضای کد [0-255] و نقطهکدها و واحدهای کد ۱ بایتی است.
- EBCDIC (فعالسازی با --ebcdic یا re2c:encoding:ebcdic). یک کدگذاری با طول ثابت با فضای کد [0-255] و نقطهکدها و واحدهای کد ۱ بایتی است.
- UCS2 (فعالسازی با --ucs2 یا re2c:encoding:ucs2). یک کدگذاری با طول ثابت با فضای کد [0-0xFFFF] و نقطهکدها و واحدهای کد ۲ بایتی است.
- UTF8 (فعالسازی با --utf8 یا re2c:encoding:utf8). یک کدگذاری یونیکد با طول متغیر است. اندازهٔ واحد کد ۱ بایت است. نقطهکدها با ۱ -- ۴ واحد کد نمایش داده میشوند.
- UTF16 (فعالسازی با --utf16 یا re2c:encoding:utf16). یک کدگذاری یونیکد با طول متغیر است. اندازهٔ واحد کد ۲ بایت است. نقطهکدها با ۱ -- ۲ واحد کد نمایش داده میشوند.
- UTF32 (فعالسازی با --utf32 یا re2c:encoding:utf32). یک کدگذاری یونیکد با طول ثابت با فضای کد [0-0x10FFFF] و نقطهکدها و واحدهای کد ۴ بایتی است.
فایلهای گنجاندنی include/unicode_blocks.re، include/unicode_categories.re و include/unicode_properties.re تعاریف re2hs را برای بلوکها، دستهها و ویژگیهای توصیفشده در استاندارد یونیکد فراهم میکنند.
گزینهٔ --input-encoding کدگذاری فایل مبدأ را مشخص میکند که میتواند برای فعالسازی لیترالهای یونیکد در عبارتهای منظم استفاده شود. برای مثال، --input-encoding utf8 به re2hs اعلام میکند که فایل مبدأ با UTF8 کدگذاری شده است (این گزینه با --utf8 که کدگذاری متن ورودی را تعیین میکند، متفاوت است). گزینهٔ --encoding-policy نحوهٔ رفتار re2hs با جانشینهای یونیکد (Unicode surrogates، نقطهکدها در محدودهٔ [0xD800-0xDFFF]) را تعیین میکند.
در ادامه مثالی از یک تحلیلگر لغوی برای شناسههای یونیکد با کدگذاری UTF8 آورده شده است.
-- re2hs $INPUT -o $OUTPUT --utf8 -i
{-# OPTIONS_GHC -Wno-unused-record-wildcards #-}
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad (when)
import Data.ByteString (ByteString, index)
%{include "unicode_categories.re" %}
data State = State {
_yyinput :: ByteString,
_yycursor :: Int,
_yymarker :: Int,
_yyaccept :: Int
}
%{
re2c:YYFN = ["lexer;Bool", "State{..};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 }
%}
main :: IO ()
main = do
let st = State {
_yyinput = "_Ыдентификатор\x00",
_yycursor = 0,
_yymarker = 0,
_yyaccept = 0}
when (not $ lexer st) $ error "failed"
ابزار re2hs امکان ضمیمه کردن فایلهای دیگر را با استفاده از بلوکی به قالب /*!include:re2c FILE */ یا %{include FILE %}، یا دستور درونبلوکیِ !include FILE ; فراهم میکند، که در آن FILE مسیر فایل مورد نظر برای ضمیمه شدن است. re2hs فایلهای include را در دایرکتوریِ فایل فراخواننده و در مسیرهای include که با گزینهٔ -I مشخص میشوند، جستجو میکند. بلوکها/دستورهای include در re2hs همانند #include در C/C++ عمل میکنند: محتوای FILE عیناً در محل بلوک/دستور جایگذاری میشود. فایلهای include میتوانند خود حاوی includeهای دیگری باشند. از گزینهٔ --depfile برای ردیابی وابستگیهای ساختِ فایل خروجی به فایلهای include استفاده کنید. ابزار re2hs تعدادی فایل include از پیش تعریفشده ارائه میدهد که در زیردایرکتوری include/ پروژه یافت میشوند. این فایلها حاوی تعاریفی هستند که ممکن است برای پروژههای دیگر نیز مفید باشند (مانند دستهبندیهای Unicode) و چیزی شبیه به یک کتابخانهٔ استاندارد برای re2hs تشکیل میدهند. در ادامه مثالی از نحوهٔ استفاده از فایلهای include آورده شده است.
فایل ضمیمه ۱ (definitions.hs)
data Number = INum | FNum | NNaN deriving (Eq)
%{
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 { FNum }
فایل ورودی (Input file)
-- re2hs $INPUT -o $OUTPUT -i
{-# OPTIONS_GHC -Wno-unused-record-wildcards #-}
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad (when)
import Data.ByteString (ByteString, index)
%{include "definitions.hs" %}
data State = State {
_yyinput :: ByteString,
_yycursor :: Int,
_yymarker :: Int,
_yyaccept :: Int
}
%{
re2c:YYFN = ["lexer;Number", "State{..};State"];
re2c:yyfill:enable = 0;
* { NNaN }
number { INum }
!include "extra_rules.re.inc";
%}
main :: IO ()
main = do
let test s n = do
let st = State {
_yyinput = s,
_yycursor = 0,
_yymarker = 0,
_yyaccept = 0}
when (lexer st /= n) $ error "failed"
test "123\0" INum
test "123.4567\0" FNum
ابزار re2hs امکان تولید فایل سرآیند از فایل ورودی .re را با استفاده از گزینهٔ --header یا پیکربندی re2c:header و جفتبلوکهایی به شکل /*!header:re2c:on*/ و /*!header:re2c:off*/، یا %{header:on%} و %{header:off%} فراهم میکند. بلوک نخست آغاز فایل سرآیند و بلوک دوم پایان آن را مشخص میکند. تمام موارد میان این بلوکها توسط re2hs پردازش شده و کد تولیدشده در فایلی که با گزینهٔ --header یا پیکربندی re2c:header مشخص شده (یا در stdout اگر هیچکدام مشخص نشده باشند) نوشته میشود. تولید خودکار فایل سرآیند ممکن است در مواردی نیاز باشد که از re2hs برای تولید تعاریفی استفاده میشود که باید از سایر واحدهای ترجمه (translation units) قابل دسترسی و مشاهده باشند.
در ادامه مثالی از تولید یک فایل سرآیند آورده شده است که شامل تعریف وضعیت تحلیلگر لغوی (lexer state) همراه با متغیرهای برچسب است (تعداد متغیرها به گرامر منظم بستگی دارد و برای برنامهنویس نامشخص است).
فایل ورودی (Input file)
-- re2hs $INPUT -o $OUTPUT --header lexer/state.hs -i
{-# OPTIONS_GHC -Wno-unused-record-wildcards #-}
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad (when)
import Data.ByteString (index)
import State
%{header:on %}
module State where
import Data.ByteString (ByteString)
data State = State {
_yyinput :: !ByteString,
_yycursor :: !Int,
%{stags format = "\n@@{tag} :: !Int,"; %}
_tag :: !Int
}
%{header:off %}
%{
re2c:YYFN = ["lexer;Int", "State{..};State"];
re2c:tags = 1;
re2c:yyfill:enable = 0;
re2c:header = "lexer/state.hs";
[a]* @_tag [b]* { _tag }
%}
main :: IO ()
main = do
let s = State {
_yyinput = "ab\0",
_yycursor = 0,
%{stags format = "\n@@{tag} = -1,"; %}
_tag = 0}
when (lexer s /= 1) $ error "failed!"
فایل سرآیند (Header file)
-- Generated by re2c
{-# LANGUAGE RecordWildCards #-}
module State where
import Data.ByteString (ByteString)
data State = State {
_yyinput :: !ByteString,
_yycursor :: !Int,
_yyt1 :: !Int,
_tag :: !Int
}
با گزینهٔ -S, --skeleton، دستور re2hs تمام کدهای غیر re2hs را نادیده میگیرد و یک برنامهٔ C مستقل تولید میکند که میتواند در ادامه کامپایل و اجرا شود. این برنامه از کد تحلیلگر لغوی و دادههای ورودی تشکیل شده است. برای هر DFA ساختهشده (بلوک یا شرط)، re2hs یک تحلیلگر لغوی مستقل و دو فایل تولید میکند: یک فایل .input با رشتههای مشتقشده از DFA، و یک فایل .keys با نتایج تطابق مورد انتظار. برنامه هر تحلیلگر لغوی را روی فایل .input متناظر اجرا کرده و نتایج را با موارد مورد انتظار مقایسه میکند. برنامههای اسکلتی به دلایل متعددی بسیار مفید هستند:
- آنها میتوانند صحت بهینهسازیهای مختلف re2hs را بررسی کنند (دادهها در مراحل اولیهٔ فرایند، پیش از اعمال هرگونه تبدیل روی DFA، تولید میشوند).
- تولید مجموعهای از دادههای ورودی با پوشش مناسب میتواند هم برای آزمودن و هم برای محکزدن (benchmarking) مفید باشد.
- تولید برنامههای اجرایی مستقل امکان دسترسی به موارد آزمون کمینهشده را فراهم میکند (کد اصلی ممکن است بزرگ باشد یا وابستگیهای فراوانی داشته باشد).
دشواری تولید دادههای ورودی در این است که به جز در سادهترین موارد، تعداد رشتههای ورودی ممکن بیش از حد زیاد است (حتی اگر طول رشته محدود باشد). re2hs این مشکل را با تولید رشتههای کافی جهت پوشش تقریباً تمام انتقالهای DFA برطرف میکند. این ابزار از الگوریتم زیر استفاده میکند: ابتدا اسکلتی از DFA را میسازد. برای کدگذاریهایی با اندازهٔ واحد کد ۱ بایتی (مانند ASCII، UTF-8 و EBCDIC)، اسکلت صرفاً یک رونوشت دقیق از DFA اصلی است. برای کدگذاریهایی با واحدهای کد چندبایتی، اسکلت رونوشتی از DFA با حذف برخی از انتقالها است: به طور مشخص، re2hs برای هر بازهٔ پیوسته و مجزا که متناظر با یک انتقال DFA باشد، حداکثر ۲۵۶ واحد کد در نظر میگیرد. مقادیر انتخابشده به طور یکنواخت توزیع میشوند و شامل کرانهای بازه نیز هستند. re2hs به جای تلاش برای پوشش تمام مسیرهای ممکن در اسکلت (که کاری نشدنی است)، مسیرهای کافی برای پوشش تمامی انتقالهای اسکلت تولید میکند و از این رو پرشهای شرطی متناظر را در تحلیلگر لغوی فعال میسازد. پیادهسازی این الگوریتم به حدود ~1Gb از انتقالها محدود شده و میزان ثابتی از حافظه را مصرف میکند (re2hs دادهها را بلافاصله پس از تولید در فایل مینویسد).
تجسم و اشکالزدایی (Visualization and debug)
با گزینهٔ -D, --emit-dot، دستور re2hs کدی تولید نمیکند. در عوض، 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)
برنامهٔ re2hs در ابتدا توسط 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.