تحلیل لغوی

اشنایی با درس کامپایلر و تحلیل لغوی

تحلیل لغوی

اشنایی با درس کامپایلر و تحلیل لغوی

مشکل گردی و راست گردی و رفع آن

 

 

مشکل چپگردی در روش تقدم ساده :

U -> U …

U -> …XU..

تداخل به صورت زیر خواهیم داشت:

 X = U , X < U

راه حل آن است که :

U -> U ..

V -> …XW…

W -> U

که در این صورت اینگونه خواهد شد :

X = W

X < Head(W) ={U}

 

مشکل راست گردی:

U -> …U

V ->…UX..

رفع مشکل:

U -> …U

V -> ..W X…

W -> U

 

در پیوست باید گفت که در این مقاله X,Y,W هم می تواند ناپایانه باشد و هم پایانه .

 

 

 

 

 

 

 

 

 

 

 

 

روش تقدم ساده

روش تقدم ساده Simple precedence  

 

این روش بهبود یافته تقدم عملگر است . در این روش رابطه بین پایانه ها و ناپایانه ها هم بررسی می شود.

در آن روابط تقدم بین همه عناصر گرامر یعنی پایانه ها و ناپایانه ها تعریف می شود . محدودیت های این روش کمتر بوده و طیف وسیعتری از گرامر را شامل می شود .

 

ویژگی ها:

1.     وجود ناپایانه ها و پایانه های مجاور مجاز است .

2.     قاعده ε  نباید در گرامر وجود داشته باشد.

3.     محدودیت : سمت راست دو قاعده ای نباید یکسان باشد .

4.     جدول پارسی نباید حاوی تداخل باشد .

 

جدول پارس:

 

یک جدول مربعی با ابعاد (|T| + | N | + 1)  با توان 2 می باشد .

 

تعریف:

Head(U)={X => X α}

Tail (U)={X => α X}

Head: پایانه یا ناپایانه ای که در ابتدای فرم های جمله ای خاصل از U ظاهر شود .

Tail: پایانه یا ناپایانه هایی که انتهای فرم جمله ای ظاهر شده باشند .

 

روابط موجود در این روش:

 X = Y ó З U ( U -> .. XY ..)

 

X < Y ó З U ( U ->  .. X A ..) Y is member of head(A)

X < Head(A)

 

X > Y ó З U ( U ->  ..  A Y ..) X is member of Tail(A)

TaiL(A) . Y

 

برای مثال برای گرامر زیر:

N -> $S$ این خط را در ابتدا خود اضافه می کنیم                               ///

S ->(SS)

S -> c

 

 

S

(

)

C

$

S

=

< 

=

< 

=

(

=

< 

 

< 

 

)

> 

> 

> 

> 

> 

c

> 

> 

> 

> 

> 

$

=

< 

 

< 

accepted

 

Head(S) = { ( ,c }

Tail(S) = { ),c }

 

 

X := stack top terminal / non-terminal

B := lookahead

 

if p[X,b] =  <    then push (<); push(b);

if p[X,b] =  =    then push (=); push(b);

if p[X,b] =  >    then reduce( );

if p[X,b] =        then  error( );

reduce: ابتدا دستگیره را پیدا می کنیم به این ترتیب که آنقدر در پشته پایین می رویم تا به اولین نماد کوچکتر برسیم رشته میان > و بالای پشته دستگیره است .

TOP: عنصر بالای پشته پس از حذف دستگیره

LHS : سمت چپ قاعده مورد استفاده برای کاهش

برای کاهش با توجه به تعاریف داده شده اگر روابط به صورت زیر باشد این کارها را انجام می دهیم:

If p[TOP,LHS] =  <   then  push(<);push(LHS);

If p[TOP,LHS] =  =   then  push(=);push(LHS);

 

روش تجزیه تقدم عملگر

 

روش تجزیه تقدم عملگر

 operator precedence parsing

 

 

گرامر عمگر :

 

1.       قاعده ε  نداشته باشد

2.       در سمت راست قواعد ، هیچ دو پایانه کنارهم  (..AB..)وجود نداشته باشد

شرط لازم برای استفاده از روش تجزیه تقدم عملگر آن است  که گرامر ، گرامر عملگر باشد.

بین هر دو پایانه گرامر یکی از چهار رابطه زیر برقرار است :

 

a <. b   a قبل از b

a.>b   a بعد از b

a=b    a با b باشد

a   b     a  هیچ رابطه ای با b نداشته باشد.

 

 

** این روابط متقارن نمی باشند .

 

 

 

 

در اینجا با ارائه یک مثال به تفصیل می پردازیم .

 

 S -> $ E $                       این خط را در ابتدا خود اضافه می کنیم  

E -> E + T | T

T -> T*F | F

F -> (E) | id

که این گرامر دارای جدول پارس زیر است .

 

 

$

Id

(

)

*

+

 

> 

< 

> 

< 

> 

< 

+

< 

> 

< 

> 

< 

> 

*

 

> 

=

> 

> 

> 

)

< 

 

< 

 

< 

< 

(

< 

 

< 

 

< 

< 

id

=

> 

 

> 

> 

> 

$

 

 

 

اگر سطر اول b  و ستون اول از چپ a  باشد :

a := stack top terminal

b:= lookahead

 

اول از همه با push($) آغاز می شود .

 if p[a,b] = <  => a

if p[a,b] =  =  => a=b then push(=);push(b);

if p[a,b] = >   => a>b then reduce();

if p[a,b] =      => a   b then error();

 

 

 

a+(a*a)

 

Input

Stack

a+(a*a)$

        +(a*a)$

            a*a)$

            a*a)$

              *a)$

              *a)$

               a)$

                )$

                )$

                )$

                  $

                  $

                  $

$ 

  $

 $ N

    $N<+

       $N<+<(

          $N<+<(

        $N<+<(N

           $N<+<(N<*

                $N<+<(N<*

              $N<+<(N<*N

        $N<+<(N

           $N<+<(N=)

    $N<+N

$N

 

 

 

 

reduce( ): یافتن دستگیره بالای پشته ، حذف آن از بالای پشته و قرار دادن ناپایانه نوعی N در بالای پشته

به عبارتی ساده تر آنقدر از بالای پشته پایین می آییم تا به اولین نماد کوچکتر برسیم. رشته میان نماد کوچکتر و بالای پشته و ناپایانه زیر > (در صورت وجود) دستگیره است .

 

حالا با یک عبارت ، جدول را تست می کنیم .

 

 

 با حذف آنها یک ناپایانه N جایگزین می شود و همراه دستگیره کاهش می یابد .

 

 

روش به دست آوردن جدول پارس :

 

P: (T Ụ {$}) * (T Ụ {$}) ->{<,>,+, }

firstterm(A)={a :A=>a α  or A =>B a α }

Lastterm(A)={ a :A=>α a or A =>α a β}

 

a = b <=> ЗU (U-> …ab…. Or  U->…a W b ….)

 

a < b  <=>  ЗU (U-> a W , b is member of firstterm(W) )

a < first term (W)

 

a > b  <=> ЗU (U-> W b , a is member of lastterm(W) )

lastterm(W) > b

 

a   b   <=>  ~(بالایی ها )

 

 

 

 

 

تحلیل نحوی (Syntax Analysis)

 

تحلیل نحوی (Syntax Analysis)

 که همان تجزیه یا parsing است .

پارسینگ وظیفه اش برعکس است ، یعنی چگونه اشتقاق انجام شده است .

در پارسینگ در اشتقاقهای ایجاد شده برعکس مگاه می کنیم تا به چگونگی اشتقاق پی ببریم .

 

روش تجزیه Parsing

 1.       روشهای عمومی { که این روش ها کارایی بهینه ندارند }

2.       روشهای بالا به پایین

در این روش د درخت تجزیه از ریشه ها به سمت برگها حرکت می کنیم و روند طی شده را از ریشه ها به برگها طی می کنیم و یا در جمله ای بهتر آن که درخت تجزیه را از ریشه به برگها تولید می کنیم .

که به طور کلی به دو روش :

 ·        پایین گرد

·        LL(1)

دسته بندی می شوند .

 3.       روشهای پایین به بالا

در این روش برعکس روش قبل درخت تجزیه از برگها آغاز شده و به سمت ریشه گسترش می یابد .

که این روش نیز به سه گروه کلی تقسیم بندی می شود :

 ·        تقدم عملگر

·        تقدم ساده

·        LR که این روش هم به طور کلی به 3 روش دیگر دسته بندی می شود :

 1.       SLR

2.       LALR

3.       CLR

در اینجا ما به روشهای پایین به بالا می پردازیم .

 

 تجزیه پایین به بالا  Bottom-Up Parsing

 

مقدمه:

 ایده اصلی آن ، ساخت درخت تجزیه با شروع از برگ ها تا رسیدن به ریشه درخت می باشد .

 

در این روش از سمت راست گرامرها شروع کرده و با تشخیص آنها به سمت چپ هر گرامر رسیده و در آخر آنقدر جلو یم رویم تا به Start Symbol  برسیم .

به طور مثال در گرامر زیر :

S -> aABe (1

A -> Abc|b  (2,3

B->d  (4

 و در زیر رشته تولید شده توسط آن :

abbcde

این مراحل را طی می کنیم :

Abbcde

Abcde  (3 گرامر سوم

aAde  (2

aABe  (4

S   (1

 

مشاهده می شود که در گرامر از سمت چپ ها به سمت راست ها می رویم تا به سمبل شروع برسیم .

 رشته ورودی را با جستجو به دنبال زیررشته ای که با سمت راست یک قاعده تولید منطبق شود پویش می کنیم .

مساله اصلی در تجزیه پایین به بالا انتخاب درست دست گیره (handle) و قاعده مربوط به آن است .

هدف :

 ساخت درخت پارس رشته w با شروع از برگها تا رسیدن به ریشه پارسر به طور مکرر سمت راست یک قاعده تولید را در مقابل یک زیر رشته در فرم جمله ای راست تطابق می دهد .

در هر تطابق برای ساخت درخت پارس یک عمل کاهش reduce انجام میشود .

o       هر کاهش زیررشته تطابق یافته ها با ناپایانه واقع در سمت چپ قاعده تولید جایگزین می کند و

o       هر کاهش ، یک گره داخلی به درخت تجزیه ها می افزاید .

o       حاصل هر عمل کاهش یک فرم جمله ای دیگر است.

o       در نهایت حاصل یک اشتقاق راست به صورت معکوس خواهد بود .

 

 دستگیره Handle

دسگیره بر اساس زیررشته ای تعیین می شود که:

- با سمت راست یک قاعده تولید A->α تطابق یابد

- کاهش α به A  یک مرحله از عکس اشتقاق راست می باشد

به شکل رسمی : یک دستگیره از فرم جمله ای راست γ یک زوج مرتب از قاعده تولید βA -> و مکانی است که β در آن دریافت می شود .

Handle(γ) = <A -> β ,k>      

1<= k <= |β|

اگر  <A -> β ,k> دستگیره γ باشد ، آنگاه جایگزین β در مکان k  با A، فرم جمله ای راست قبلی در اشتقاق راست را مشخص می کند .

 

هرس کردن دستگیره ها  Handle pruning

 

برای بنا کردن درخت تجزیه از پایین به بالا ، فرزندان یک گره خذف می شوند و با پدر جایگزین می شوند .

 

 

قضیه:

 اگر گرامر G مبهم نباشد آنگاه هر فرم جکله ای راست یک دستگیره یکتا دارد .

  

پارسر شیفت-کاهش  Shift-Reduce   

 یکی از روشهای پیاده سازی هرس کردن دستگیره ها ، پارسر پایین به بالاست که shift-reduce parser نام دارد . این پارسر مطابق مدل [1]PDA از یک پشته و یک بافر ورودی استفاده می کند .

 1.       ابتدا یک علامت داده درون پشته push  می شود.

2.       مراحل زیر را تکرار می کنیم تاب الای پشته علامت δ و توکن جاری $ گردد :

         i.            دستگیره را می یابیم

اگر دستگیره ای بالای پشته وجود نداشت توکن ورودی را درون پشته شیفت می دهیم .

       ii.            دستگیره یافت شده را هرس می کنیم .

 

اگر دستگیره <A -> β ,k> بر روی پشته قرار داشت عمل reduce را انجام می دهیم : |  β | علامت از بالای پشته pop  می کنیم و به جای آن A را  push می کنیم .

یک پارسر شیفت=کاهش چهار عمل اصلی دارد :

1.       shift : push کردن توکن جاری به پشته

2.       reduce : pop کردن دستگیره از بالای پشته و قرار دادن ناپایانه سمت راست قاعده به جای آن در پشته

3.       accept :  پارس با موفقیت حاتمه پیدا کند .  ( lookahead =$, top=S)

4.       error : در صورت عدم تطابق نحوی

 

یک دستگیره همیشه بالای پشته است چون گرامر و زبان مستقل از متن است .

 

دو نوع تداخل Conflict  داریم :

o       Shift –reduce conflict  عدم تشخیص شیفت یا کاهش

o       Reduce-reduce conflict عدم تشخیص قاعده مورد استفاده برای reduce

 

 

--------------------------------------------------------------------------------

[1] Push Down Automata