Professional Documents
Culture Documents
ﭼﻜﻴﺪه
اﻳﻦ ﻧﻮﺷﺘﻪ راﺟﻊﺑﻪ وﻳﮋﮔﻲﻫﺎ و ﻧﺤﻮهي اﺳﺘﻔﺎده از Constructor of Useful Parsersﻳﺎ ﻫﻤـﺎن CUPﻛـﻪ ﻣﺒﺘﻨـﻲ ﺑـﺮ
زﺑﺎن ﺟﺎوا اﺳﺖ ،ﻣﻲﺑﺎﺷﺪ CUP.اﺑﺰاري ﻗﺪرﺗﻤﻨﺪ ﺑﺮاي ﺗﻮﻟﻴﺪ ﺗﺠﺰﻳﻪﮔﺮ LALRاز روي ﺗﻌﺮﻳﻒ ﮔﺮاﻣﺮ زﺑﺎن آن اﺳﺖ.
ﻛﻠﻴﺪواژهﻫﺎ ،CUP :ﻣﻮﻟﺪ ﺗﺠﺰﻳﻪﮔﺮ LALR ،و ﺟﺎوا
.1ﻣﻘﺪﻣﻪ
اﻳﻦ ﻧﻮﺷﺘﻪ راﺟﻊﺑﻪ وﻳﮋﮔﻲﻫﺎ و ﻧﺤﻮهي اﺳﺘﻔﺎده از Constructor of Useful Parsersﻳﺎ ﻫﻤﺎن CUPﻛﻪ ﻣﺒﺘﻨﻲ ﺑـﺮ زﺑـﺎن
ﺟﺎوا اﺳﺖ ،ﻣﻲﺑﺎﺷﺪ.ﻛﺎپ اﺑﺰاري ﺑﺮاي ﺗﻮﻟﻴﺪ ﺗﺠﺰﻳﻪﮔﺮ LALRاز روي ﮔﺮاﻣﺮ زﺑﺎن آن اﺳﺖ و ﺑﻴﺸﺘﺮ ﻛﺎرﻫـﺎي ﻧـﺮم اﻓـﺰار
ﻣﺸﻬﻮر YACCرا اﻧﺠﺎم ﻣﻲدﻫﺪ .وﻟﻲ ﻛﺎپ ﺑﻪ زﺑﺎن ﺟﺎوا اﺳﺖ و ﺗﺠﺰﻳﻪﮔﺮﻫﺎﻳﻲ ﺑﻪ زﺑﺎن ﺟﺎوا ﺗﻮﻟﻴﺪ ﻣﻲﻛﻨﺪ .ﺑﺮاي اﺳـﺘﻔﺎده
از ﻛﺎپ ﺑﻪ ﺑﺮﻧﺎﻣﻪاي ﻧﻴﺰ ﺑﻪ ﻣﻨﻈﻮر اﻧﺠﺎم ﺗﺤﻠﻴﻞ ﻟﻐﻮي ﻧﻴﺎز اﺳﺖ ﻛﻪ اﺳﺘﻔﺎده از JLexﺗﻮﺻﻴﻪ ﻣﻲﺷﻮد .اﻳﻦ ﻣﻘﺎﻟﻪ در 5ﺑﺨـﺶ
ﺗﻨﻈﻴﻢ ﺷﺪه اﺳﺖ .در ﺑﺨﺶ اول ﺑﻪ آﺷﻨﺎﻳﻲ ﺑﺎ ﻛﺎپ و ﻣﺰاﻳﺎي آن ﻣﻲﭘﺮدازﻳﻢ .در ﺑﺨـﺶ دوم ﺑـﻪ ﻧﺤـﻮهي اﺳـﺘﻔﺎده از ﻛـﺎپ
ﭘﺮداﺧﺘﻪ ﻣﻲﺷﻮد .در ﺑﺨﺶ ﺳﻮم ﺳﺎﺧﺘﺎر ﻳﻚ ﺑﺮﻧﺎﻣﻪي ﻛﺎپ ﺷﺮح داده ﻣﻲﺷﻮد .در ﺑﺨﺶ ﭼﻬﺎرم ﻧﺤﻮهي ﺑﺮﺧﻮرد ﺑـﺎ ﺧﻄـﺎ
ﺑﻴﺎن ﻣﻲﺷﻮد و در ﻧﻬﺎﻳﺖ در ﺑﺨﺶ آﺧﺮ ﺧﻼﺻﻪ ،ﻧﺘﻴﺠﻪﮔﻴﺮي و ...اﻧﺠﺎم ﻣﻲﺷﻮد.
ﻧﺤﻮهي اﺳﺘﻔﺎده.2
ﺑﻪ ﻋﻨﻮان ﻣﺜﺎﻟﻲ ﺑﺮاي آﺷﻨﺎﻳﻲ ﺑﺎ زﺑﺎن ﻛﺎپ ﺑﻪ ﻣﺜﺎل ﻛﻪ زﻳﺮ ﻛﻪ ﺑﺮاي ﻣﺤﺎﺳﺒﻪي ﻣﻘﺪار ﻋﺪدي ﻳﻚ راﺑﻄـﻪي رﻳﺎﺿـﻲ ﺑـﺮ روي
:اﻋﺪاد ﺻﺤﻴﺢ ﺑﻪ ﻛﺎر ﻣﻲرود ﺗﻮﺟﻪ ﻛﻨﻴﺪ
ﺳـﭙﺲ ﺗﻌﺮﻳـﻒ ﻛـﺮدن ﭘﺎﻳﺎﻧـﻪﻫـﺎ و.ﻫﻤﺎﻧﻄﻮر ﻛﻪ ﻣﻼﺣﻈﻪ ﻣﻲﺷﻮد اوﻟﻴﻦ ﮔﺎم ﻓﺮاﻫﻢ ﻛﺮدن ﻣﻘﺪﻣﺎت ﻻزم ﺑﺮاي ﺑﺮﻧﺎﻣﻪ اﺳـﺖ
ﻧﺴﺨﻪﻫﺎي ﻗـﺪﻳﻤﻲ. اﮔﺮ ﺗﻮﺟﻪ ﻛﺮده ﺑﺎﺷﻴﺪ ﮔﺮاﻣﺮ ﺑﺎﻻ ﮔﺮاﻣﺮي ﻣﺒﻬﻢ اﺳﺖ.ﻧﺎﭘﺎﻳﺎﻧﻪﻫﺎ و ﺳﭙﺲ ﺗﻌﺮﻳﻒ ﻛﺮدن ﮔﺮاﻣﺮ ﻣﻮرد ﻧﻈﺮ
ﻣﻮﻟﺪ ﺗﺠﺰﻳﻪ ﮔﺮ ﻛﺎپ
ﻛﺎپ ﺑﺮﻧﺎﻣﻪ ﻧﻮﻳﺲ را ﻣﺠﺒﻮر ﻣﻲﻛﺮدﻧﺪ ﻛﻪ از ﮔﺮاﻣﺮﻫﺎي ﻏﻴﺮﻣﺒﻬﻢ اﺳﺘﻔﺎده ﻛﻨﺪ اﻣﺎ در ﻧﺴﺨﻪﻫﺎي ﺟﺪﻳـﺪ اﻣﻜـﺎن ﭘﺸـﺘﻴﺒﺎﻧﻲ از
اﻳﻦ ﮔﺮاﻣﺮﻫﺎ ﻫﻢ وﺟﻮد دارد و از ﻃﺮﻳﻖ ﺗﻌﺮﻳﻒ ﻛﺮدن اوﻟﻮﻳﺘﻬﺎ و ﺷﺮﻛﺖ ﭘﺬﻳﺮيﻫﺎ ،اﺑﻬﺎمﻫﺎ رﻓﻊ ﻣﻲﺷﻮﻧﺪ.
ﺑﺮاي ﺳﺎﺧﺘﻦ ﺗﺠﺰﻳﻪ ﮔﺮ از ﻃﺮﻳﻖ ﻛﺎپ راﺣﺖﺗﺮﻳﻦ راه اﺳﺘﻔﺎده از ﻛﺘﺎﺑﺨﺎﻧﻪي ﺟﺎواي آن اﺳﺖ ﻛﻪ ﺑﻪ ﺻـﻮرت زﻳـﺮ
اﺳﺘﻔﺎده ﻣﻲﺷﻮد:
java -jar java-cup-xxx.jar file.cup
ﺗﻌﺮﻳﻒ ﮔﺮاﻣﺮ در ﻓﺎﻳﻞ file.cupﺑﻪ ﻛﺎپ داده ﻣﻲﺷـﻮد و دو ﻓﺎﻳـﻞ Parser.javaو sym.javaﺳـﺎﺧﺘﻪ ﻣـﻲﺷـﻮد .ﺑـﺮاي
اﺳﺘﻔﺎده ﺑﺎﻳﺪ آﻧﻬﺎ را ﻫﻤﺮاه ﺑﺎ java-cup-xxx-runtime.jarﺑﻪ javaداد ﺗﺎ ﺑﺮﻧﺎﻣﻪ ﻣﻮرد ﻧﻈﺮ را اﺟﺮا ﻛﻨﺪ و ﺳﭙﺲ از ﻃﺮﻳﻖ
ورودي اﺳــﺘﺎﻧﺪارد ورودي را ﺑــﻪ آن داد .دو ﻓﺎﻳــﻞ sym.javaو parser.javaﺗﻌﺮﻳــﻒ دو ﻛــﻼس symو parserرا
درﺑﺮدارﻧﺪ .ﻛﻼس symﺣﺎوي ﺗﻌﺮﻳﻒ ﻳﻚ ﺳﺮي ﺛﺎﺑﺖﻫﺎﺳﺖ ﻛﻪ ﺑﻪ ازاي ﻫﺮ ﭘﺎﻳﺎﻧﻪ ﻳـﻚ ﻣﻘـﺪار درون آن ﻗـﺮار دارد .اﻳـﻦ
ﺗﻌﺎرﻳﻒ ﺑﻪ ﻃﻮر ﻋﻤﺪه ﺗﻮﺳﻂ scannerاﺳﺘﻔﺎده ﻣﻲﺷﻮﻧﺪ و اﺟﺮاي دﺳﺘﻮراﺗﻲ ﻣﺜﻞ:
;)return new Symbol(sym.SEMI
را ﻣﻤﻜﻦ ﻣﻲﺳﺎزﻧﺪ .ﻛﻼس scannerﻧﻴﺰ ﺗﻌﺮﻳﻒ ﺧﻮد ﺗﺠﺰﻳﻪﮔﺮ را در ﺑﺮ دارد.
ﺑﺮﻧﺎﻣﻪاي ﺑﻪ ﺷﻜﻞ ﻓﻮق ﺑﻪ ﻋﻨﻮان ﺧﺮوﺟﻲ ﺗﻨﻬﺎ ﻣﻮﻓﻘﻴﺖآﻣﻴﺰ ﺑﻮدن ﻳﺎ ﻧﺒﻮدن ﻧﺘﻴﺠﻪي ﺗﺠﺰﻳﻪ را ﺑﺮﻣـﻲﮔﺮداﻧـﺪ .ﺑـﺮاي
اﻳﻨﻜﻪ ﻣﺤﺎﺳﺒﻪ ﻣﻘﺎدﻳﺮ اﻧﺠﺎم ﺷﻮد ﺑﺎﻳﺪ ﻗﻮاﻋﺪ ﻣﻌﻨﺎﻳﻲ را از ﻃﺮﻳﻖ ﻛﺪ ﺟﺎوا ﺑﻪ درون ﺗﻌﺮﻳﻒ ﮔﺮاﻣﺮ وارد ﻛﺮد .اﻳﻦ ﻛﺪﻫﺎ ﻛﻪ در
ﻛﺎپ از آﻧﻬﺎ ﺗﺤﺖ ﻋﻨﻮان actionsﻳﺎد ﻣﻲﺷﻮد ﺑﻴﻦ ﺟﺪاﻛﻨﻨﺪهﻫﺎﻳﻲ ﺑﻪ ﺷﻜﻞ } :و {:آورده ﻣﻲﺷﻮﻧﺪ .اﻳﻦ ﻛﺪﻫﺎ ﻣﺴﺘﻘﻴﻤﺎ ﺑـﻪ
ﻛﺪ ﺗﺠﺰﻳﻪﮔﺮ ﻣﻨﺘﻘﻞ ﻣﻲﺷﻮﻧﺪ و ﺑﺮرﺳﻲ اﻳﻨﻜﻪ آﻳﺎ ﻛﺪ ﻣﻌﺘﺒﺮي ﻫﺴﺘﻨﺪ ﻧﻴﺰ اﻧﺠﺎم ﻧﻤﻲﺷﻮد .ﻧﻤﻮﻧﻪي ﻛﺎﻣﻞﺗﺮي از ﻣﺜﺎل ﻓﻮق ﻛـﻪ
ﻗﻮاﻋﺪ ﻧﺤﻮي ﻧﻴﺰ در آن ﻧﻮﺷﺘﻪ ﺷﺪه اﺳﺖ در زﻳﺮ آﻣﺪه اﺳﺖ:
ﻫﻤﺎﻧﻄﻮر ﻛﻪ در ﺑﺎﻻ دﻳﺪه ﻣﻲﺷﻮد اﻣﻜﺎن ﻧﺎﻣﮕﺬاري ﻳﻚ not-terminalﺑﻪ ﻣﻨﻈـﻮر ﻧـﺎم ﺑـﺮدن از آن در actionﻫـﺎ وﺟـﻮد
دارد .ﻣﺜﻼ در
expr:e1 PLUS expr:e2
}{: RESULT = new Integer(e1.intValue() + e2.intValue()); :
اوﻟﻴﻦ e1 ،exprو دوﻣﻴﻦ e2 ،exprﻧﺎﻣﻴﺪه ﺷﺪه اﺳﺖ .در ﺿﻤﻦ ﻫﻤﺎﻧﻄﻮر ﻛﻪ ﻣﺸﺎﻫﺪه ﻣﻲﺷﻮد ﺳﻤﺖ ﭼﭗ ﻫـﺮ ﻗﺎﻋـﺪه ﺑـﺎ
RESULTﻧﺸﺎن داده ﻣﻲﺷﻮد.
ﻫﺮ ﻧﻤﺎدي ﻛﻪ در ﻳﻚ ﻗﺎﻋﺪه ﻇﺎﻫﺮ ﻣﻲﺷﻮد در زﻣﺎن اﺟﺮا ﺗﻮﺳﻂ objectاي از ﻧـﻮع Symbolدر ﭘﺸـﺘﻪي ﺗﺠﺰﻳـﻪ
ﻧﺸﺎن داده ﻣﻲﺷﻮد .ﺑﺮﭼﺴﺐﻫﺎ ﺑﻪ ﻣﺘﻐﻴﺮ valueدر داﺧﻞ آن objectﻫﺎ اﺷﺎره ﻣﻲﻛﻨﻨﺪ .در ﻣﺜﺎل ﺑﺎﻻ e1و e2ﺑـﻪ اﺷـﻴﺎﻳﻲ از
ﺟﻨﺲ Integerاﺷﺎره ﻣﻲﻛﻨﻨﺪ .اﻳﻦ اﺷﻴﺎ درون ﻓﻴﻠﺪ valueي اﺷﻴﺎﻳﻲ از ﺟﻨﺲ Symbolﻛـﻪ آن non-terminalﻫـﺎ را در
ﭘﺸﺘﻪ ﺗﺠﺰﻳﻪ ﻧﺸﺎن ﻣﻲدﻫﻨﺪ ﻗﺮار دارﻧﺪ .ﻫﻤﭽﻨـﻴﻦ RESULTﻫـﻢ از ﻧـﻮع Integerاﺳـﺖ ﭼـﻮن در اﻳﻨﺠـﺎ exprاز ﻧـﻮع
Integerﺗﻌﺮﻳﻒ ﺷﺪه اﺳﺖ .اﻳﻦ Integerدر ﻓﻴﻠﺪ valueي objectﺟﺪﻳﺪي از ﻧﻮع Symbolﻗﺮار ﻣﻲﮔﻴﺮد.
ﺑﺮاي ﻫﺮ ﺑﺮﭼﺴﺐ دو ﻣﺘﻐﻴﺮ دﻳﮕﺮ ﻧﻴﺰ ﺑﺮاي ﻛﺎرﺑﺮ ﻗﺎﺑﻞ دﺳﺘﺮﺳﻲ ﻫﺴﺘﻨﺪ .دو ﺑﺮﭼﺴﺐ ﭼﭗ و راﺳﺖ وﺟـﻮد دارﻧـﺪ
ﻛﻪ ﺑﻪ ﻛﺎرﺑﺮ اﻳﻦ اﻣﻜﺎن را ﻣﻲدﻫﻨﺪ ﻛﻪ ﻛﺎرﺑﺮ ﺑﺘﻮاﻧﺪ ﺑﻔﻬﻤﺪ ﺳﻤﺖ ﭼﭗ و راﺳﺖ آن ﻧﻤﺎد در رﺷﺘﻪي ورودي ﻛﺠﺎ ﻗـﺮار دارد.
ﻣﻮﻟﺪ ﺗﺠﺰﻳﻪ ﮔﺮ ﻛﺎپ
ﻧﺎم اﻳﻦ ﻣﺘﻐﻴﺮﻫﺎ از اﺿﺎﻓﻪ ﻛﺮدن ﻛﻠﻤﺎت rightو leftﺑﻪ اﺳﻢ ﺑﺮﭼﺴﺐ ﻣﻮردﻧﻈﺮ ﺑﻪ دﺳﺖ ﻣﻲآﻳﺪ .ﻣﺜﻼ در ﻣﺜﺎل ﻓﻮق ﻋـﻼوه
ﺑﺮ e1دو ﻣﺘﻐﻴﺮ e1leftو e1rightﻧﻴﺰ ﺗﻌﺮﻳﻒ ﻣﻲﺷﻮﻧﺪ ﻛﻪ ﻣﻘﺪار آﻧﻬﺎ از ﻧﻮع intاﺳﺖ.
ﮔﺎم آﺧﺮ در ﺳﺎﺧﺖ ﻳﻚ ﺗﺠﺰﻳﻪﮔﺮ ﺳﺎﺧﺖ ﻳﻚ ) scannerﻳﺎ ﻫﻤﺎن (lexerاﺳـﺖ .اﻳـﻦ روال وﻇﻴﻔـﻪي ﺧﻮاﻧـﺪن
ﺣﺮﻓﻬﺎي ﺟﺪاﮔﺎﻧﻪ و ﺗﺒﺪﻳﻞ آن ﺑﻪ اﺷﻴﺎﻳﻲ از ﺟﻨﺲ Symbolﻛﻪ اﻃﻼﻋﺎت آن ﻧﻤﺎدﻫﺎ را ﺑﻪ ﺗﺠﺰﻳﻪﮔﺮ ﻣﻲدﻫﺪ ﺑﺮ ﻋﻬـﺪه دارد.
ﭘﺎﻳﺎﻧﻪﻫﺎ از ﻃﺮﻳﻖ ﻓﺮاﺧﻮاﻧﻲ روال scannerﺑﻪ دﺳﺖ ﻣﻲآﻳﻨﺪ .در اﻳﻦ ﻣﺜـﺎل ﺗﺠﺰﻳـﻪﮔـﺮ )( scanner.next_tokenرا ﺻـﺪا
ﻣﻲزﻧﺪ scanner .ﺑﺎﻳﺪ ﺷﻲءاي از ﻧﻮع java_cup.runtime.symbolﺑﺮﮔﺮداﻧﺪ .اﻳﻦ ﺷﻲء ﺣﺎوي ﻣﺘﻐﻴﺮي ﺑـﻪ ﻧـﺎم value
اﺳﺖ ﻛﻪ ﻣﻘﺪار آن ﻧﻤﺎد را در ﺧﻮد ذﺧﻴﺮه ﻣﻲﻛﻨﺪ و ﻧﻮع آن از ﺟﻨﺲ ﺗﻌﺮﻳﻒ ﺷﺪه در ﺑﺨﺶ ﺗﻌﺮﻳﻒ اﻧـﻮاع اﺳـﺖ .در اﻳـﻦ
ﻣﺜﺎل اﮔﺮ lexerﺑﺨﻮاﻫﺪ ﻳﻚ NUMBERﺑﻪ ﺗﺠﺰﻳﻪﮔﺮ ﺑﻔﺮﺳﺘﺪ ﺑﺎﻳﺪ ﻳﻚ ﺷﻲء از ﻧﻮع Symbolﺑﺴﺎزد و ﻣﻘﺪار valueآن
را ﺑﺎ ﺷﻲءاي از ﻧﻮع Integerﭘﺮ ﻛﻨﺪ .در ﺻﻮرﺗﻲ ﻛﻪ ﻧﻤﺎد ﻣﻮرد ﻧﻈﺮ terminalﻳﺎ non-terminalاي ﺑـﺪون ﻣﻘـﺪار ﺑﺎﺷـﺪ
null ،valueﺧﻮاﻫﺪ ﺑﻮد.
ﺑﺮﻧﺎﻣﻪاي ﻛﻪ داﺧﻞ ﺑﺨﺶ init withﻧﻮﺷﺘﻪ ﻣﻲﺷﻮد ﻗﺒﻞ از اﻳﻨﻜﻪ ﻫﻴﭻ tokenاي ﺗﻘﺎﺿﺎ ﺷﻮد ﺷﻮد اﺟﺮا ﻣﻲﺷﻮد و
درﺧﻮاﺳﺖ ﻫﺮ tokenﺗﻮﺳﻂ ﻛﺪي ﻛﻪ در ﺑﺨﺶ scan withﻧﻮﺷﺘﻪ ﺷﺪه اﻧﺠﺎم ﻣﻲﺷـﻮد .اﻳﻨﻜـﻪ scannerدﻗﻴﻘـﺎ ﭼﮕﻮﻧـﻪ
ﻋﻤﻞ ﻣﻲﻛﻨﺪ ﺑﺮاي ﻛﺎپ ﻣﻬﻢ ﻧﻴﺴﺖ ،ﺗﻨﻬﺎ ﺑﺎﻳﺪ ﺷﻲءاي از ﻧﻮع Symbolﺑﺎ ﻛﻪ ﺑﻪ ﺻﻮرت درﺳﺖ ﻣﻘﺪاردﻫﻲ ﺷﺪه ﺑﺮﮔﺮداﻧﺪ.
در ﺿﻤﻦ اﺳﻜﻨﺮ ﻧﺒﺎﻳﺪ از اﺷﻴﺎي ﺗﻜﺮاري ﺑﺮﮔﺮداﻧﺪ.
.3ﺳﺎﺧﺘﺎر ﺑﺮﻧﺎﻣﻪ
ﺣﺎل ﺑﻪ ﺑﺮرﺳﻲ ﺳﺎﺧﺘﺎرﺑﻨﺪي ﺑﺮﻧﺎﻣﻪ در ﻛﺎپ ﻣﻲ ﭘﺮدازﻳﻢ .ﻳﻚ ﺑﺮﻧﺎﻣﻪ در ﻛﺎپ ﺷﺎﻣﻞ 5ﺑﺨﺶ ﻣﻲﺑﺎﺷﺪ:
ﻣﺸﺨﺺ ﻛﺮدن packageﻫﺎ و importﻫﺎ.
ﻛﺪﻫﺎﻳﻲ ﻛﺎرﺑﺮ
ﺗﻌﺮﻳﻒ ﻧﻤﺎدﻫﺎي ﮔﺮاﻣﺮ
ﺑﻴﺎن اوﻟﻮﻳﺖﻫﺎ
ﺷﺮح ﮔﺮاﻣﺮ
;*import package_name.
importﻫﺎ ﺑﻪ ﻫﻤﺎن ﺷﻜﻠﻲ ﻛﻪ ﻧﻮﺷﺘﻪ ﻣﻲﺷﻮﻧﺪ ﺑﻪ ﻛﺪ ﺗﺠﺰﻳﻪﮔﺮ ﻣﻨﺘﻘﻞ ﻣﻲﺷﻮﻧﺪ و اﺟﺎزه ﻣﻲدﻫﻨﺪ ﻛﻪ در actionﻫﺎ از آن
ﻛﻼﺳﻬﺎ اﺳﺘﻔﺎده ﺷﻮد.
ﻛﺪﻫﺎي ﻛﺎرﺑﺮ
ﭘﺲ از دو ﺑﺨﺶ اﺧﺘﻴﺎري ﻓﻮق ﻳﻚ ﺑﺨﺶ اﺧﺘﻴﺎري دﻳﮕﺮ ﻗﺮار دارد ﻛﻪ ﺑﻪ ﻛﺎرﺑﺮ اﺟﺎزه ﻣﻲدﻫﺪ ﻛﺪﻫﺎي ﻣـﻮرد ﻧﻴـﺎزش را در
ﺗﺠﺰﻳﻪﮔﺮ ﻧﻬﺎﻳﻲ ﺟﺎﺳﺎزي ﻛﻨﺪ .ﺑﻪ ﻋﻨﻮان ﺑﺨﺸﻲ از ﻛﺪ ﺗﺠﺰﻳﻪﮔﺮ ﻳﻚ ﻛﻼس ﻏﻴﺮ ﻋﻤﻮﻣﻲ 1ﻛﻪ ﺗﻤﺎم actionﻫﺎي ﺗﻌﺒﻴﻪﺷﺪه را
در ﺑﺮ دارد ﺗﻮﻟﻴﺪ ﻣﻲﺷﻮد .در اﺑﺘﺪاي ﺑﺮﻧﺎﻣﻪ ﻳﻚ ﺑﺨﺶ ﺑﺎ ﻧﺎم action codeﺑﻪ ﻛﺎرﺑﺮ اﺟﺎزه ﻣﻲدﻫﺪ ﻛﻪ ﻛﺪﻫﺎﻳﻲ را ﻣﺴـﺘﻘﻴﻤﺎ
در اﻳﻦ ﻛﻼس وارد ﻛﻨﺪ .ﻛﺪﻫﺎ و ﻣﺘﻐﻴﺮﻫﺎﻳﻲ ﻛﻪ ﺗﻮﺳﻂ ﺑﻴﺸﺘﺮ actionﻫﺎ اﺳﺘﻔﺎده ﻣﻲﺷﻮﻧﺪ ﻣﻌﻤﻮﻻ در اﻳﻦ ﺑﺨـﺶ ﻗـﺮار ﻣـﻲ-
ﮔﻴﺮﻧﺪ )ﺑﻪ ﻋﻨﻮان ﻣﺜﺎل رواﻟﻬﺎي ﻛﺎر ﺑﺎ . (symbol tableاﻳﻦ ﺑﺨﺶ ﺑﻪ ﺷﻜﻞ زﻳﺮ ﺗﻌﺮﻳﻒ ﻣﻲﺷﻮد:
;}action code {: ... :
ﻛﻪ در آن داﺧﻞ } {: ... :ﻛﺪي ﻗﺮار دارد ﻛﻪ ﻣﺴﺘﻘﻴﻤﺎ داﺧﻞ ﺗﻌﺮﻳﻒ ﻛﻼس action classﻗﺮار ﻣﻲﮔﻴﺮد.
ﺑﻌﺪ از اﻳﻦ ﺑﺨﺶ ﻳﻚ ﺑﺨﺶ اﺧﺘﻴﺎري دﻳﮕﺮ ﺑﻪ ﻧﺎم parser codeﻗﺮار ﻣﻲﮔﻴﺮد .اﻳﻦ ﺑﺨﺶ ﺑﻪ ﻛﺎرﺑﺮ اﺟﺎزه ﻣﻲدﻫﺪ
ﻛﺪي را ﺑﻪ ﻃﻮر ﻣﺴﺘﻘﻴﻢ در ﻛﻼس ﺧﻮد ﺗﺠﺰﻳﻪﮔﺮ ﻗﺮار دﻫﺪ .از ﺟﻤﻠﻪ ﻛﺎرﺑﺮدﻫﺎي آن ﻣﻲﺗﻮان ﺑﻪ ﻗﺮار دادن روالﻫﺎي
scanningدر داﺧﻞ parserو ﻳﺎ overrideﻛﺮدن رواﻟﻬﺎي ﭘﻴﺶﻓﺮض ﮔﺰارش ﺧﻄﺎ اﺷﺎره ﻛﺮد .ﺗﻌﺮﻳﻒ اﻳﻦ ﺑﺨﺶ ﻧﻴﺰ
درﺳﺖ ﻣﺜﻞ action codeو ﺑﻪ ﺻﻮرت زﻳﺮ اﺳﺖ:
ﺑﻌﺪ از parser codeﻳﻚ ﺑﺨﺶ اﺧﺘﻴﺎري دﻳﮕﺮ ﺗﺤﺖ ﻋﻨﻮان init withﻗﺮار ﻣﻲﮔﻴﺮد ﻛﻪ ﺑﻪ ﺷﻜﻞ زﻳﺮ ﺗﻌﺮﻳﻒ
ﻣﻲﺷﻮد:
اﻳﻦ ﺑﺨﺶ ﻛﺪي را ﻣﺸﺨﺺ ﻣﻲﻛﻨﺪ ﻛﻪ ﻗﺒﻞ از اﻳﻨﻜﻪ ﺗﺠﺰﻳﻪﮔﺮ درﺧﻮاﺳﺖ اوﻟﻴﻦ ﺗﻮﻛﻦ را ﺑﺪﻫﺪ اﺟﺮا ﻣﻲﺷﻮد .اﻳﻦ ﺑﺨﺶ
ﻣﻌﻤﻮﻻ ﺑﺮاي ﻣﻘﺪاردﻫﻲ اوﻟﻴﻪ ﺑﻪ scannerو ﺟﺪاول و دﻳﮕﺮ ﺳﺎﺧﺘﺎرﻫﺎي دادهي ﻣﻮرد ﻧﻴﺎز اﺳﺘﻔﺎده ﻣﻲﺷﻮد .اﻳﻦ ﺑﺨﺶ
ﺑﺪﻧﻪي ﻳﻚ روال voidرا داﺧﻞ ﻛﻼس parserﺗﺸﻜﻴﻞ ﻣﻲدﻫﺪ.
آﺧﺮﻳﻦ ﺑﺨﺸﻲ ﻛﻪ ﻣﻲﺗﻮاﻧﺪ ﺗﻌﻴﻴﻦ ﺷﻮد)اﺧﺘﻴﺎري( ﺗﻌﻴﻴﻦ اﻳﻦ اﺳﺖ ﻛﻪ ﺗﺠﺰﻳﻪﮔﺮ ﭼﮕﻮﻧﻪ ﺑﺎﻳﺪ درﺧﻮاﺳﺖ ﮔﺮﻓﺘﻦ
ﺗﻮﻛﻦ ﺑﻌﺪي را ﺑﺪﻫﺪ .ﺗﻌﺮﻳﻒ آن ﺑﻪ ﺷﻜﻞ زﻳﺮ اﺳﺖ:
ﻫﻤﺎﻧﻨﺪ ﺣﺎﻟﺖ initاﻳﻦ ﺑﺨﺶ ﺑﺪﻧﻪي ﻳﻚ روال از ﻛﻼس ﺗﺠﺰﻳﻪﮔﺮ را ﺗﺸﻜﻴﻞ ﻣﻲدﻫﺪ .وﻟﻲ ﺑﺮ ﺧﻼف آن اﻳﻦ روال ﺷﻲءاي
از ﻧﻮع java_cup.runtime.Symbolﺑﺮ ﻣﻲﮔﺮداﻧﺪ .ﺑﻨﺎﻳﺒﺮاﻳﻦ ﻛﺪ ﻧﻮﺷﺘﻪ ﺷﺪه در اﻳﻦ ﺑﺨﺶ ﺑﺎﻳﺪ ﻣﻘﺪاري از آن ﻧﻮع
ﺑﺮﮔﺮداﻧﺪ.
1
Non-public
ﻣﻮﻟﺪ ﺗﺠﺰﻳﻪ ﮔﺮ ﻛﺎپ
اﻳﻦ ﺑﺨﺶ ﻛﻪ ﻧﻮﺷﺘﻦ آن ﺿﺮوري اﺳﺖ ﺑﺮاي ﺗﻌﺮﻳﻒ ﻛﺮدن ﭘﺎﻳﺎﻧﻪﻫﺎي و ﻧﺎﭘﺎﻳﺎﻧﻪﻫﺎي زﺑﺎن و ﺑﻴﺎن ﻧﻮع آﻧﻬﺎ ﺑﻪ ﻛﺎر ﻣﻲرود.
ﻫﻤﺎﻧﻄﻮر ﻛﻪ در ﺑﺎﻻ ﮔﻔﺘﻪ ﺷﺪ ﻫﺮ ﭘﺎﻳﺎﻧﻪ ﻳﺎ ﻧﺎﭘﺎﻳﺎﻧﻪ در زﻣﺎن اﺟﺮا ﺗﻮﺳﻂ ﻳﻚ ﺷﻲء از ﻛﻼس Symbolﻧﺸﺎن داده ﻣﻲﺷﻮد .در
ﻣﻮرد ﭘﺎﻳﺎﻧﻪﻫﺎ آﻧﻬﺎ ﺗﻮﺳﻂ ﺗﺠﺰﻳﻪﮔﺮ ﺑﺮﮔﺮداﻧﺪه ﻣﻲﺷﻮﻧﺪ و در ﭘﺸﺘﻪي ﺗﺠﺰﻳﻪ ﻗﺮار ﻣﻲﮔﻴﺮﻧﺪ lexer .ﺑﺎﻳﺪ ﻣﻘﺪار آن ﭘﺎﻳﺎﻧﻪ در
در ﻓﻴﻠﺪ valueآن ﻗﺮار دﻫﺪ .در ﻣﻮرد ﻧﺎﭘﺎﻳﺎﻧﻪﻫﺎ ﻧﻴﺰ زﻣﺎﻧﻲ ﻳﻚ ﻧﺎﭘﺎﻳﺎﻧﻪ در ﭘﺸﺘﻪ ﻇﺎﻫﺮ ﻣﻲﺷﻮد ﻛﻪ ﻳﻚ ﺳﺮي ﻧﻤﺎد ﻛﻪ ﻣﻌﺎدل
ﺳﻤﺖ راﺳﺖ ﻳﻚ ﻗﺎﻋﺪه ﻫﺴﺘﻨﺪ reduceﺷﻮﻧﺪ و ﻧﺎﭘﺎﻳﺎﻧﻪي ﻣﻌﺎدل ﺟﺎي آﻧﻬﺎ ﻗﺮار ﮔﻴﺮد .ﺑﺮاي ﺗﻌﻴﻴﻦ اﻳﻨﻜﻪ ﻳﻚ ﻧﻤﺎد ﭘﺎﻳﺎﻧﻪ ﻳﺎ
ﻧﺎﭘﺎﻳﺎﻧﻪ اﺳﺖ ﺑﺎﻳﺪ از دو ﻧﻤﺎد terminalو non terminalاﺳﺘﻔﺎده ﻛﺮد .ﺑﻪ ﺷﻜﻞﻫﺎي زﻳﺮ ﻣﻲﺗﻮان ﻧﻤﺎدﻫﺎي ﮔﺮاﻣﺮ را
ﺗﻌﺮﻳﻒ ﻛﺮد:
ﻛﻪ در آن classnameﻧﻮع ﻣﻘﺪار آن ﭘﺎﻳﺎﻧﻪ ﻳﺎ ﻧﺎﭘﺎﻳﺎﻧﻪ را ﻣﺸﺨﺺ ﻣﻲﻛﻨﺪ .اﻳﻦ ﻧﻮع زﻣﺎﻧﻲ ﻛﻪ ﻛﺎرﺑﺮ از ﻃﺮﻳﻖ ﻳﻚ ﺑﺮﭼﺴﺐ
ﺑﻪ آن ﻧﻤﺎد دﺳﺘﺮﺳﻲ ﭘﻴﺪا ﻣﻲﻛﻨﺪ اﺳﺘﻔﺎده ﻣﻲﺷﻮد .اﮔﺮ ﻧﻮﻋﻲ ﻣﺸﺨﺺ ﻧﺸﻮد آن ﻧﻤﺎد ﻣﻘﺪاري ﻧﺨﻮاﻫﺪ داﺷﺖ .اﺳﻢ ﻧﻤﺎدﻫﺎ
ﻧﺒﺎﻳﺪ ﺟﺰو ﻛﻠﻤﺎت رزروﺷﺪهي ﻛﺎپ ﻳﻌﻨﻲ،"nonterminal" ،"non" ،"terminal" ،"parser" ،"action" ،"code" :
" "import" ،"nonassoc" ،"right" ،"left" ،"precedence" ،"start" ،"with" ،"scan" ،"initو ""package
ﺑﺎﺷﺪ.
ﺑﻴﺎن اوﻟﻮﻳﺖﻫﺎ
اﻳﻦ ﺑﺨﺶ ﻛﻪ ﻧﻮﺷﺘﻦ آن اﺧﺘﻴﺎري ﻣﻲﺑﺎﺷﺪ ،اوﻟﻮﻳﺖ ﻋﻤﻠﮕﺮﻫﺎ و ﻧﺤﻮهي ﺷﺮﻛﺖﭘﺬﻳﺮي آﻧﻬﺎ را ﻣﺸﺨﺺ ﻣﻲﻛﻨﺪ .اﻳﻦ ﺑﺨﺶ
زﻣﺎﻧﻲ ﻣﻔﻴﺪ اﺳﺖ ﻛﻪ ﻣﻲﺧﻮاﻫﻴﻢ از ﮔﺮاﻣﺮﻫﺎي ﻣﺒﻬﻢ اﺳﺘﻔﺎده ﻛﻨﻴﻢ ،ﻫﻤﺎﻧﻄﻮر ﻛﻪ در ﺑﺮﻧﺎﻣﻪي 2اﺳﺘﻔﺎده ﻛﺮدﻳﻢ .ﺳﻪ ﻧﻮع
ﺗﻌﺮﻳﻒ اوﻟﻮﻳﺖ و ﺷﺮﻛﺖﭘﺬﻳﺮي وﺟﻮد دارد:
ﻛﺎپ ﺑﺮ اﺳﺎس ﺗﻌﺮﻳﻒ اوﻟﻮﻳﺘﻬﺎ ﺑﻪ ﻫﺮ ﭘﺎﻳﺎﻧﻪ ﻳﻚ اوﻟﻮﻳﺖ ﻧﺴﺒﺖ ﻣﻲدﻫﺪ .اﮔﺮ ﻳﻚ ﭘﺎﻳﺎﻧﻪ در اﻳﻦ ﻟﻴﺴﺖ ﻧﻴﺎﻳﺪ
ﻛﻤﺘﺮﻳﻦ اوﻟﻮﻳﺖ را ﺧﻮاﻫﺪ داﺷﺖ .ﻛﺎپ ﻫﻤﭽﻨﻴﻦ ﺑﻪ ﻫﺮ ﻗﺎﻋﺪه ﻳﻚ اوﻟﻮﻳﺖ ﻧﺴﺒﺖ ﻣﻲدﻫﺪ .آن اوﻟﻮﻳﺖ ﺑﺮاﺑﺮ اﺳﺖ ﺑﺎ
اوﻟﻮﻳﺖ آﺧﺮﻳﻦ ﭘﺎﻳﺎﻧﻪي آن ﻗﺎﻋﺪه .اﮔﺮ آن ﻗﺎﻋﺪه ﺷﺎﻣﻞ ﻫﻴﭻ ﭘﺎﻳﺎﻧﻪاي ﻧﺒﻮد ﻛﻤﺘﺮﻳﻦ اوﻟﻮﻳﺖ را ﺧﻮاﻫﺪ داﺷﺖ .ﺑﻪ ﻋﻨﻮان ﻣﺜﺎل:
ﻣﻮﻟﺪ ﺗﺠﺰﻳﻪ ﮔﺮ ﻛﺎپ
expr ::= expr TIMES exprاوﻟﻮﻳﺘﻲ ﺑﺮاﺑﺮ TIMESﺧﻮاﻫﺪ داﺷﺖ .زﻣﺎﻧﻲ ﻛﻪ ﺗﺪاﺧﻞ ﺷﻴﻔﺖ-ﻛﺎﻫﺶ وﺟﻮد دارد ﻛﺎپ
ﺗﺸﺨﻴﺺ ﻣﻲدﻫﺪ ﻛﻪ ﭘﺎﻳﺎﻧﻪاي ﻛﻪ ﻣﻲﺧﻮاﻫﺪ ﺷﻴﻔﺖ ﭘﻴﺪا ﻛﻨﺪ اوﻟﻮﻳﺖ ﺑﻴﺸﺘﺮي دارد و ﻳﺎ ﻗﺎﻋﺪهاي ﻛﻪ ﻣﻲﺧﻮاﻫﺪ ﻛﺎﻫﺶ ﭘﻴﺪا
ﻛﻨﺪ .ﺑﺮ اﺳﺎس آن ﺗﺼﻤﻴﻢ ﻣﻲﮔﻴﺮد ﻛﻪ ﺷﻴﻔﺖ را اﻧﺠﺎم دﻫﺪ ﻳﺎ ﻛﺎﻫﺶ را .در ﺻﻮرﺗﻲ ﻛﻪ اوﻟﻮﻳﺖ آﻧﻬﺎ ﻳﻜﺴﺎن ﺑﻮد ﺷﺮﻛﺖ-
ﭘﺬﻳﺮي آن ﭘﺎﻳﺎﻧﻪ ﺗﻌﻴﻴﻦ ﻣﻲﻛﻨﺪ ﻛﻪ ﭼﻪ اﺗﻔﺎﻗﻲ ﺑﻴﺎﻓﺘﺪ.
ﺑﻪ ﻫﺮ ﭘﺎﻳﺎﻧﻪاي ﻛﻪ در ﻟﻴﺴﺖ ﺑﺎﻻ ﻇﺎﻫﺮ ﺷﻮد ﻳﻚ ﻧﺤﻮهي ﺷﺮﻛﺖﭘﺬﻳﺮي ﻧﺴﺒﺖ داده ﻣﻲﺷﻮد .ﺳﻪ ﻧﻮع ﺷﺮﻛﺖ-
ﭘﺬﻳﺮي دارﻳﻢ right ،left :و . nonassocدر ﺷﺮاﻳﻄﻲ ﻛﻪ اوﻟﻮﻳﺖﻫﺎي ﺑﺮاﺑﺮي دارﻳﻢ از ﺷﺮﻛﺖﭘﺬﻳﺮي ﺑﺮاي ﺗﻌﻴﻴﻦ ﻛﺎري
ﻛﻪ ﺑﺎﻳﺪ اﻧﺠﺎم ﺷﻮد اﺳﺘﻔﺎده ﻣﻲﺷﻮد .اﮔﺮ آن ﭘﺎﻳﺎﻧﻪ از ﭼﭗ ﺷﺮﻛﺖﭘﺬﻳﺮ ﺑﺎﺷﺪ ﻛﺎﻫﺶ اﻧﺠﺎم ﻣﻲﺷﻮد .ﻳﻌﻨﻲ اﮔﺮ رﺷﺘﻪي
ورودي ﺑﻪ ﺷﻜﻞ 3 + 4 + 5ﺑﺎﺷﺪ ﺗﺠﺰﻳﻪﮔﺮ ﻫﻤﻴﺸﻪ ﻛﺎﻫﺶ را از ﭼﭗ ﺑﻪ راﺳﺖ اﻧﺠﺎم ﻣﻲدﻫﺪ .ﻳﻌﻨﻲ در اﻳﻦ ﻣﺜﺎل اول + 4
3اﻧﺠﺎم ﻣﻲﺷﻮد .اﮔﺮ ﭘﺎﻳﺎﻧﻪ از راﺳﺖ ﺷﺮﻛﺖﭘﺬﻳﺮ ﺑﺎﺷﺪ ﺑﻪ درون ﭘﺸﺘﻪ ﺷﻴﻔﺖ ﭘﻴﺪا ﻣﻲﻛﻨﺪ .ﺑﻨﺎﺑﺮاﻳﻦ ﻛﺎﻫﺶ از راﺳﺖ ﺑﻪ ﭼﭗ
اﻧﺠﺎم ﻣﻲﺷﻮد ،ﺑﻨﺎﺑﺮاﻳﻦ اﮔﺮ PLUSاز راﺳﺖ ﺷﺮﻛﺖﭘﺬﻳﺮ ﺗﻌﺮﻳﻒ ﺷﺪه ﺑﻮد اﺑﺘﺪا 4 + 5اﻧﺠﺎم ﻣﻲﺷﺪ .اﮔﺮ ﭘﺎﻳﺎﻧﻪاي از ﻧﻮع
nonassocﺗﻌﺮﻳﻒ ﺷﻮد وﻗﻮع ﻣﺘﻮاﻟﻲ دوﺗﺎ از آن ﻛﻪ اوﻟﻮﻳﺖ ﻣﺴﺎوياي دارﻧﺪ ﺑﺎﻋﺚ اﻳﺠﺎد ﺧﻄﺎ ﺧﻮاﻫﺪ ﺷﺪ .اﻳﻦ ﻣﻮﺿﻮع
ﺑﺮاي ﻋﻤﻠﮕﺮ "==" ﻣﻨﺎﺳﺐ اﺳﺖ .ﺑﺮاي ﻣﺜﺎل اﮔﺮ "==" از ﻧﻮع nonassocﺗﻌﺮﻳﻒ ﺷﺪه ﺑﺎﺷﺪ ﻋﺒﺎرت 3 == 4 == 5ﺑﺎﻋﺚ
وﻗﻮع ﺧﻄﺎ ﺧﻮاﻫﺪ ﺷﺪ .اﮔﺮ ﺑﺎ ﺗﻮﺟﻪ ﺑﻪ ﻗﻮاﻋﺪ ﻓﻮق ﺗﺪاﺧﻠﻲ رﻓﻊ ﻧﺸﺪ ﺑﻪ ﻛﺎرﺑﺮ ﮔﺰارش ﻣﻲﺷﻮد.
ﺷﺮح ﮔﺮاﻣﺮ
آﺧﺮﻳﻦ ﮔﺎم در ﺑﻴﺎن وﻳﮋﮔﻲﻫﺎي ﺗﺠﺰﻳﻪﮔﺮ ﺷﺮح ﮔﺮاﻣﺮ آن اﺳﺖ .اﻳﻦ ﺑﺨﺶ ﺑﻪ ﻃﻮر اﺧﺘﻴﺎري ﺑﺎ ﻋﺒﺎرﺗﻲ ﺑﻪ ﺷﻜﻞ زﻳﺮ آﻏﺎز
ﻣﻲﺷﻮد:
اﻳﻦ ﻋﺒﺎرت ﺗﻌﻴﻴﻦ ﻣﻲﻛﻨﺪ ﻛﻪ ﻧﺎﭘﺎﻳﺎﻧﻪي آﻏﺎزﻳﻦ ﻛﺪام اﺳﺖ .اﮔﺮ آن را ﻣﺸﺨﺺ ﻧﻜﻨﻴﻢ ﺑﻪ ﻃﻮر ﭘﻴﺶﻓﺮض ﻧﺎﭘﺎﻳﺎﻧﻪي ﺳﻤﺖ
ﭼﭗ اوﻟﻴﻦ ﻗﺎﻋﺪه ﺑﻪ ﻋﻨﻮان ﺷﺮوع در ﻧﻈﺮ ﮔﺮﻓﺘﻪ ﺧﻮاﻫﺪ ﺷﺪ .در ﺿﻤﻦ ﭘﺲ از ﻳﻚ ﺗﺠﺰﻳﻪي ﻣﻮﻓﻖ ،ﻛﺎپ ﺷﻲءاي از ﻧﻮع
Symbolﺑﺮ ﻣﻲﮔﺮداﻧﺪ ﻛﻪ valueآن ﻣﻘﺪار ﻧﻬﺎﻳﻲ آﺧﺮﻳﻦ ﻛﺎﻫﺶ را در ﺧﻮد دارد.
ﭘﺲ از ﻋﺒﺎرت ﻓﻮق ﺗﻌﺮﻳﻒ ﺧﻮد ﮔﺮاﻣﺮ ﻗﺮار ﻣﻲﮔﻴﺮد .ﺗﻤﺎم ﻗﻮاﻋﺪ ﺑﻪ ﺻﻮرت ﻳﻚ ﻧﺎﭘﺎﻳﺎﻧﻪ ﻛﻪ ﺑﻌﺪ از آن "=“::
ﻗﺮار ﮔﺮﻓﺘﻪ اﺳﺖ و ﺑﻌﺪ از آن ﺗﻌﺪادي ،actionﭘﺎﻳﺎﻧﻪ و ﻧﺎﭘﺎﻳﺎﻧﻪ ﻗﺮار دارد و ﺑﻌﺪ از آن ﻋﺒﺎرت اﺧﺘﻴﺎرياي ﺑﺮاي ﺑﻴﺎن اوﻟﻮﻳﺖ
ﻗﺮار ﮔﺮﻓﺘﻪ و ﺳﭙﺲ ﺑﻪ ";" ﻣﻨﺘﻬﻲ ﺷﺪه اﺳﺖ ،ﻣﻲﺑﺎﺷﻨﺪ .ﻫﺮ ﻧﻤﺎدي در ﺳﻤﺖ راﺳﺖ ﺑﻪ ﻃﻮر اﺧﺘﻴﺎري ﻣﻲﺗﻮاﻧﺪ داراي ﻳﻚ
ﺑﺮﭼﺴﺐ ﺑﺎﺷﺪ و ﻧﺎم آن ﺑﻌﺪ از ﻧﻤﺎد ﻣﻮرد ﻧﻈﺮ ﻧﻮﺷﺘﻪ ﻣﻲﺷﻮد و ﺑﻴﻦ آن دو ﻳﻚ " ":ﻗﺮار ﻣﻲﮔﻴﺮد .ﺑﺮﭼﺴﺐﻫﺎ ﺑﺎﻳﺪ داﺧﻞ آن
ﻗﺎﻋﺪه ﻳﻜﺘﺎ ﺑﺎﺷﻨﺪ و ﺑﺮاي اﺷﺎره ﻛﺮدن ﺑﻪ ﻣﻘﺪار آن ﻧﻤﺎدﻫﺎ در actionﻫﺎ اﺳﺘﻔﺎده ﻣﻲﺷﻮﻧﺪ .ﻫﻤﺎﻧﻄﻮر ﻛﻪ ﭘﻴﺸﺘﺮ ﻧﻴﺰ ﺗﻮﺿﻴﺢ
داده ﺷﺪ دو ﻣﺘﻐﻴﺮ دﻳﮕﺮ ﻧﻴﺰ ﺳﺎﺧﺘﻪ ﻣﻲﺷﻮﻧﺪ ﻛﻪ ﻧﺎم اﻳﻦ ﻣﺘﻐﻴﺮﻫﺎ از اﺿﺎﻓﻪ ﻛﺮدن ﻛﻠﻤﺎت rightو leftﺑﻪ اﺳﻢ ﺑﺮﭼﺴﺐ
ﻣﻮردﻧﻈﺮ ﺑﻪ دﺳﺖ ﻣﻲآﻳﺪ .اﻳﻦ ﻣﺘﻐﻴﺮﻫﺎ ﺣﺎوي ﻣﻘﺪارﻫﺎي intاي ﻫﺴﺘﻨﺪ ﻛﻪ ﻣﺤﻞ راﺳﺖﺗﺮﻳﻦ و ﭼﭗﺗﺮﻳﻦ ﻣﺤﻠّﻲ را ﻣﺸﺨﺺ
ﻣﻲﻛﻨﻨﺪ ﻛﻪ ﻣﺘﻌﻠﻖ ﺑﻪ ﻧﻤﺎد ﻓﻌﻠﻲ در ﻓﺎﻳﻞ ورودي اﺳﺖ .اﻳﻦ دو ﻣﺘﻐﻴﺮ ﺑﺎﻳﺪ ﺑﻪ ﻃﻮر ﺻﺤﻴﺢ ﺗﻮﺳﻂ lexerﻣﻘﺪاردﻫﻲ ﺷﻮﻧﺪ.
اﻳﻦ ﻣﻘﺎدﻳﺮ ﺳﭙﺲ در ﻧﺎﭘﺎﻳﺎﻧﻪﻫﺎﻳﻲ ﻛﻪ ﻛﺎﻫﺶ ﺑﻪ آﻧﻬﺎ اﻧﺠﺎم ﻣﻲﺷﻮد اﻧﺘﺸﺎر ﻣﻲﻳﺎﺑﻨﺪ.
اﮔﺮ ﺑﺨﻮاﻫﻴﻢ ﭼﻨﺪ ﻗﺎﻋﺪه داﺷﺘﻪ ﺑﺎﺷﻴﻢ ﻛﻪ ﺳﻤﺖ ﭼﭗ ﻣﺸﺘﺮﻛﻲ دارﻧﺪ ﻣﻲﺗﻮاﻧﻴﻢ ﺳﻤﺖ راﺳﺖ آﻧﻬﺎ را ﭘﺸﺖ ﺳﺮ ﻫﻢ
ﺑﻨﻮﻳﺴﻴﻢ و ﺑﺎ "|" ﺑﻴﻦ آﻧﻬﺎ ﻓﺎﺻﻠﻪ ﺑﺪﻫﻴﻢ.
ﻣﻮﻟﺪ ﺗﺠﺰﻳﻪ ﮔﺮ ﻛﺎپ
actionﻫﺎﻳﻲ ﻛﻪ ﺳﻤﺖ راﺳﺖ ﻗﻮاﻋﺪ ﻧﻮﺷﺘﻪ ﻣﻲﺷﻮﻧﺪ )ﺑﺮاي ﻣﺜﺎل ﻣﻴﺎن } ({: ... :زﻣﺎﻧﻲ اﺟﺮا ﻣﻲﺷﻮﻧﺪ ﻛﻪ ﺗﺠﺰﻳﻪ-
ﮔﺮ ﺑﺨﺸﻲ از ﻗﺎﻋﺪه ﻛﻪ در ﺳﻤﺖ ﭼﭗ آن actionﻗﺮار دارد را ﺷﻨﺎﺳﺎﻳﻲ ﻛﻨﺪ )ﺗﻮﺟﻪ ﺷﻮد ﻛﻪ scannerدر اﻳﻦ ﻣﺮﺣﻠﻪ ﺑﺎﻳﺪ
ﺗﻮﻛﻦ ﺑﻌﺪ از actionرا ﻧﻴﺰ ﺑﺮﮔﺮداﻧﺪه ﺑﺎﺷﺪ ﭼﻮن ﺗﺠﺰﻳﻪﮔﺮ ﺑﻪ ﻳﻚ ﺗﻮﻛﻦ lookaheadاﺿﺎﻓﻲ ﻧﻴﺰ ﺑﺮاي ﺗﺸﺨﻴﺺ ﻧﻴﺎز
دارد(.
ﺑﺮاي ﺗﻌﻴﻴﻦ اوﻟﻮﻳﺖ ﻳﻚ ﻗﺎﻋﺪه ﺑﺪون رﻋﺎﻳﺖ ﻗﺎﻋﺪهي آﺧﺮﻳﻦ ﭘﺎﻳﺎﻧﻪ ،ﺑﺎﻳﺪ ﻋﺒﺎرت ﺗﻌﻴﻴﻦ اوﻟﻮﻳﺖ را در اﻧﺘﻬﺎي
ﻗﺎﻋﺪهي ﺗﻮﻟﻴﺪ ﺑﻴﺎورﻳﻢ .ﻣﺜﺎل ﺧﻮﺑﻲ ﺑﺮاي اﻳﻦ ﻛﺎر ﻣﺜﺎل زﻳﺮ اﺳﺖ:
در اﻳﻨﺠﺎ اﻳﻦ ﻗﺎﻋﺪه ﻃﻮري ﺗﻌﺮﻳﻒ ﺷﺪه ﻛﻪ اوﻟﻮﻳﺖ آن ﺑﻪ اﻧﺪازهي اوﻟﻮﻳﺖ UMINUSاﺳﺖ .ﺑﺎ اﺳﺘﻔﺎده از اﻳﻦ ﻗﺎﺑﻠﻴﺖ
ﺗﺠﺰﻳﻪﮔﺮ ﻣﻲﺗﻮاﻧﺪ ﺑﻪ ﭘﺎﻳﺎﻧﻪي MINUSدو ﻧﻮع اوﻟﻮﻳﺖ ﻣﺨﺘﻠﻒ ﺑﺪﻫﺪ ﺑﻨﺎﺑﺮ اﻳﻨﻜﻪ ﻋﻤﻠﮕﺮ ﺗﻚ ﻋﻤﻠﻮﻧﺪي ﻣﻨﻔﻲ اﺳﺖ و ﻳﺎ
ﻋﻤﻠﮕﺮ ﺗﻔﺮﻳﻖ اﺳﺖ.
.4ﺑﺮﺧﻮرد ﺑﺎ ﺧﻄﺎ
ﻳﻚ وﻳﮋﮔﻲ ﺑﺴﻴﺎر ﺧﻮب ﻛﺎپ ﭘﺸﺘﻴﺒﺎﻧﻲ آن ﺑﺮاي رﻓﻊ ﺧﻄﺎﻫﺎي ﻧﺤﻮي اﺳﺖ .ﻛﺎپ ﻫﻤﺎن روش ﺑﺮﺧﻮرد ﺑﺎ ﺧﻄﺎي YACC
را ﺑﻪ ﻛﺎر ﻣﻲﺑﺮد .ﺑﻪ ﻃﻮر ﺧﺎص ﻛﺎپ ﻳﻚ ﻧﻤﺎد ﺧﺎص ﺑﻪ ﻧﺎم errorرا ﭘﺸﺘﻴﺒﺎﻧﻲ ﻣﻲﻛﻨﺪ ﻛﻪ اﻳﻦ ﻧﻤﺎد ﻧﻘـﺶ ﻳـﻚ ﻧﺎﭘﺎﻳﺎﻧـﻪ را
اﻳﻔﺎ ﻣﻲﻛﻨﺪ ﻛﻪ ﺑﻪ ﺟﺎي اﻳﻨﻜﻪ ﺑﺎ ﻳﻚ ﺳﺮي ﻗﻮاﻋﺪ ﻣﻌﻨﺎ ﭘﻴﺪا ﻛﻨﺪ ،ﺑﺎ ﻳﻚ ﻋﺒﺎرت داراي ﺧﻄﺎ ﺗﻄﺒﻴﻖ ﭘﻴﺪا ﺧﻮاﻫﺪ ﻛﺮد.
اﻳﻦ ﻧﻤﺎد ﺗﻨﻬﺎ زﻣﺎﻧﻲ ﻧﻘﺶ اﻳﻔﺎ ﻣﻲﻛﻨﺪ ﻛﻪ ﻳﻚ ﺧﻄﺎي ﻧﺤﻮي رخ دﻫﺪ .اﮔﺮ ﺧﻄﺎﻳﻲ ﺷﻨﺎﺳﺎﻳﻲ ﺷﻮد ﺗﺠﺰﻳﻪﮔـﺮ ﺳـﻌﻲ
ﻣﻲﻛﻨﺪ ﺑﺨﺸﻲ از رﺷﺘﻪي ورودي را ﺑﻪ errorﻛﺎﻫﺶ دﻫﺪ و ﺳﭙﺲ ﺑﻪ ﻛﺎر ﺧﻮد اداﻣﻪ دﻫﺪ .ﺑﺮاي ﻣﺜﺎل ﻣﻤﻜﻦ اﺳﺖ ﻗﻮاﻋﺪي
ﺑﻪ ﺷﻜﻞ زﻳﺮ داﺷﺘﻪ ﺑﺎﺷﻴﻢ:
اﻳﻦ ﻣﺸﺨﺺ ﻣﻲﻛﻨﺪ ﻛﻪ اﮔﺮ ﻫﻴﭽﻜﺪام از ﻓﺮﻣﻬﺎي ﻋﺎدي stmtﺑﺎ ورودي ﺗﻄﺎﺑﻖ ﭘﻴﺪا ﻧﻜﺮد ﻳﻚ ﺧﻄﺎي ﻧﺤﻮي ﺷﻨﺎﺳﺎﻳﻲ ﺷﻮد
و ﺗﻮﻛﻦﻫﺎي داراي ﺧﻄﺎ رد ﺷﻮﻧﺪ ﺗﺎ ﺗﺠﺰﻳﻪ ﺑﺘﻮاﻧﺪ ﺑﺎ ﻳﻚ ";" ﺗﻄﺎﺑﻖ ﭘﻴﺪا ﻛﺮده و اداﻣﻪ داده ﺷﻮد .ﻳﻚ ﺧﻄﺎ ﺗﻨﻬﺎ در ﺻـﻮرﺗﻲ
رﻓﻊ ﻣﻲﺷﻮد ﻛﻪ ﺑﻪ ﺗﻌﺪاد ﻛﺎﻓﻲ از ﺗﻮﻛﻦﻫﺎي ﺑﻌﺪ از آن ﺧﻄﺎ ﺑﻪ ﻃﻮر ﺻﺤﻴﺢ و ﺑﺪون ﺧﻄﺎ ﺗﺠﺰﻳﻪ ﺷﻮد )اﻳـﻦ ﺗﻌـﺪاد ﺗﻮﺳـﻂ
روال )( error_sync_sizeاز ﺗﺠﺰﻳﻪﮔﺮ ﻣﺸﺨﺺ ﻣﻲﺷﻮد و ﻣﻘﺪار ﭘﻴﺶﻓﺮض آن 3اﺳﺖ(.
ﻣﻮﻟﺪ ﺗﺠﺰﻳﻪ ﮔﺮ ﻛﺎپ
ﺑﻪ ﻃﻮر ﺧﺎص ﺗﺠﺰﻳﻪﮔﺮ اول ﺑﻪ ﻧﺰدﻳﻚﺗﺮﻳﻦ وﺿﻌﻴﺘﻲ در ﺑﺎﻻي ﭘﺸﺘﻪي ﺗﺠﺰﻳﻪ ﻧﮕﺎه ﻣﻲﻛﻨﺪ ﻛﻪ ﺗﺤـﺖ آن ﻳـﺎﻟﻲ ﺑـﺎ
ﺑﺮﭼﺴﺐ errorوﺟﻮد دارد .ﭘﺲ از آن ﺗﺠﺰﻳﻪﮔﺮ از روي ﭘﺸﺘﻪ آﻧﻘﺪر ﺑﺮﻣﻲدارد ﺗﺎ ﺑﻪ آن وﺿـﻌﻴﺖ ﺑﺮﺳـﺪ .ﺳـﭙﺲ آﻧﻘـﺪر از
ﺗﻮﻛﻦﻫﺎي ورودي ﺣﺬف ﻣﻲﻛﻨﺪ ﺗﺎ ﺑﺘﻮاﻧﺪ ﺑﻪ ﻛﺎر ﺧﻮد اداﻣﻪ دﻫﺪ .ﭘﺲ از ﺣﺬف ﻫﺮ ﺗﻮﻛﻦ ﺗﺠﺰﻳﻪﮔﺮ ﺗﻼش ﻣﻲﻛﻨﺪ ﺗﺎ ﺑـﺪون
اﺟﺮاي ﻫﻴﭻ actionاي ورودي را ﺗﺠﺰﻳﻪ ﻛﻨﺪ اﮔﺮ ﺗﻮاﻧﺴﺖ ﺑﻪ ﺗﻌﺪاد ﻛﺎﻓﻲ ﺗﻮﻛﻦ را ﺗﺠﺰﻳـﻪ ﻛﻨـﺪ ﺑـﻪ ﻋﻘـﺐ ﺑﺮﻣـﻲﮔـﺮدد و
ﺗﺠﺰﻳﻪي ﻋﺎدي )ﺑﺎ اﺟﺮاي actionﻫﺎ( اﻧﺠﺎم ﻣﻲﺷﻮد .اﮔﺮ ﻧﺘﻮاﻧﺴﺖ ﺑﻪ ﺗﻌﺪاد ﻛـﺎﻓﻲ ﺗـﻮﻛﻦ را ﺗﺠﺰﻳـﻪ ﻛﻨـﺪ ،ﺗـﻮﻛﻦ دﻳﮕـﺮي
ﺣﺬف ﻣﻲﺷﻮد و ﺗﺠﺰﻳﻪﮔﺮ دوﺑﺎره ﻫﻤﺎن ﻛﺎرﻫﺎ را اﻧﺠﺎم ﻣﻲدﻫﺪ .اﮔﺮ اﻳﻦ ﻛﺎر ﺗﺎ ﭘﺎﻳﺎن ﻓﺎﻳﻞ اداﻣﻪ ﻳﺎﺑﺪ و ﻧﺘﻮاﻧﺪ ﻣﺸﻜﻞ را رﻓﻊ
ﻛﻨﺪ )و ﻳﺎ وﺿﻌﻴﺖ ﻣﻨﺎﺳﺒﻲ در ﭘﺸﺘﻪ ﻳﺎﻓﺖ ﻧﺸﻮد( رﻓﻊ ﺧﻄﺎ ﺷﻜﺴﺖ ﺧﻮرده اﺳﺖ.
.5ﺧﻼﺻﻪ و ﻧﺘﻴﺠﻪﮔﻴﺮي
اﻳﻦ ﻣﺘﻦ ﺑﻪ ﻃﻮر ﺧﻼﺻﻪ ﻣﻮﻟﺪ ﺗﺠﺰﻳﻪﮔﺮ CUPرا ﺗﻮﺿﻴﺢ داد .ﻛﺎپ ﻃﺮاﺣﻲ ﺷﺪه اﺳﺖ ﺗﺎ ﺟﺎي ﺑﺮﻧﺎﻣـﻪي ﺷـﻨﺎﺧﺘﻪﺷـﺪهي
YACCﻛﻪ ﺑﻪ زﺑﺎن C++اﺳﺖ را ،ﺑﺮاي ﻛﺎرﺑﺮان ﺟﺎوا ﭘﺮ ﻛﻨﺪ .اﻃﻼﻋﺎت ﺑﻴﺸﺘﺮ راﺟﻊ ﺑﻪ ﻋﻤﻠﻜﺮد اﻳﻦ ﺑﺮﻧﺎﻣﻪ در ﻛﺪ ﺑﺮﻧﺎﻣﻪ-
2ي ﻛﺎپ ﻳﺎﻓﺖ ﻣﻲﺷﻮد.
در ﻣﺠﻤﻮع اﻳﻦ اﺑﺰار ﺑﺮاي ﻛﺴﺎﻧﻲ ﻛﻪ ﻣﻲﺧﻮاﻫﻨﺪ راﺣﺖ و ﺑﻪ زﺑﺎن ﺟﺎوا compilerاي ﻃﺮاﺣﻲ ﻛﻨﻨﺪ ﺗﻮﺻﻴﻪ ﻣـﻲ-
ﺷﻮد.
ﺳﭙﺎﺳﮕﺰاري
در ﭘﺎﻳﺎن ﻻزم ﻣﻲداﻧﻢ از اﺳﺘﺎد ﺧﻮﺑﻢ ﺟﻨﺎب آﻗﺎي دﻛﺘﺮ ﻛﺎﻇﻢ ﻓﻮﻻدي ﻛﻪ ﻣﻦ را در ﻳـﺎﻓﺘﻦ ﻣﺴـﻴﺮي ﺑـﺮاي اﻧﺠـﺎم ﻣﻄﺎﻟﻌـﺎت
راﻫﻨﻤﺎﻳﻲ ﻓﺮﻣﻮدﻧﺪ ﺗﺸﻜﺮ ﻓﺮاوان ﺑﻨﻤﺎﻳﻢ.
ﻣﺮاﺟﻊ
[1] S.E. Hudson. CUP User’s Manual, Georgia Institute of Technology, 2006.
[2] Looking for a parser generator recommendation: Compilers, URL: http://objectmix.com/compilers/36342-
looking-parser-generator-recommendation.html#post149459, visited: 6/15/2009.
2
Source code
ﻣﻮﻟﺪ ﺗﺠﺰﻳﻪ ﮔﺮ ﻛﺎپ
ﺗﻌﺮﻳﻒ ﻛﺎﻣﭙﺎﻳﻠﺮ
ﺧﺮوﺟﻲ