ນີ້ແມ່ນຄໍາສັ່ງ peg ທີ່ສາມາດດໍາເນີນການໄດ້ໃນ OnWorks ຜູ້ໃຫ້ບໍລິການໂຮດຕິ້ງຟຣີໂດຍໃຊ້ຫນຶ່ງໃນຫຼາຍໆບ່ອນເຮັດວຽກອອນໄລນ໌ຂອງພວກເຮົາເຊັ່ນ Ubuntu Online, Fedora Online, Windows online emulator ຫຼື MAC OS online emulator
ໂຄງການ:
NAME
peg, ຂາ - parser generator
ສະຫຼຸບສັງລວມ
peg [-hvV - ຜົນຜະລິດ] [ຊື່ເອກະສານ ... ]
ຂາ [-hvV - ຜົນຜະລິດ] [ຊື່ເອກະສານ ... ]
ລາຍລະອຽດ
peg ແລະ ຂາ ແມ່ນເຄື່ອງມືສໍາລັບການສ້າງ parsers recursive-descent: ໂຄງການທີ່ປະຕິບັດ
ຮູບແບບທີ່ກົງກັນກັບຂໍ້ຄວາມ. ພວກເຂົາເຈົ້າປະມວນຜົນ Grammar ການສະແດງອອກ (PEG) [Ford 2004] ກັບ
ຜະລິດໂຄງການທີ່ຮັບຮູ້ປະໂຫຍກທາງກົດໝາຍຂອງໄວຍາກອນນັ້ນ. peg ປະມວນຜົນ PEGs
ຂຽນໂດຍໃຊ້ syntax ຕົ້ນສະບັບທີ່ອະທິບາຍໂດຍ Ford; ຂາ ປະມວນຜົນ PEGs ທີ່ຂຽນໂດຍໃຊ້
syntax ແລະສົນທິສັນຍາທີ່ແຕກຕ່າງກັນເລັກນ້ອຍທີ່ມີຈຸດປະສົງເພື່ອເຮັດໃຫ້ມັນເປັນທີ່ຫນ້າສົນໃຈ
ການທົດແທນສໍາລັບຕົວວິເຄາະທີ່ສ້າງຂຶ້ນດ້ວຍ lex(1) ແລະ yacc(1). ບໍ່ມັກ lex ແລະ yacc, peg ແລະ ຂາ
ສະຫນັບສະຫນູນ backtracking ບໍ່ຈໍາກັດ, ສະຫນອງທາງເລືອກຕາມຄໍາສັ່ງເປັນວິທີການສໍາລັບການ disambiguation, ແລະ
ສາມາດສົມທົບການສະແກນ (ການວິເຄາະ lexical) ແລະ parsing (ການວິເຄາະ syntactic) ເປັນອັນດຽວ
ກິດຈະກໍາ.
peg ອ່ານທີ່ລະບຸໄວ້ ຊື່ເອກະສານs, ຫຼືມາດຕະຖານ input ຖ້າບໍ່ມີ ຊື່ເອກະສານs ແມ່ນໃຫ້, ສໍາລັບ a
ໄວຍະກອນອະທິບາຍຕົວວິເຄາະເພື່ອສ້າງ. peg ຫຼັງຈາກນັ້ນສ້າງໄຟລ໌ແຫຼ່ງ C ທີ່
ກໍານົດຫນ້າທີ່ yyparse(). ໄຟລ໌ແຫຼ່ງ C ນີ້ສາມາດຖືກລວມເຂົ້າໃນ, ຫຼືລວບລວມແລະຫຼັງຈາກນັ້ນ
ເຊື່ອມໂຍງກັບ, ໂຄງການລູກຄ້າ. ແຕ່ລະຄັ້ງທີ່ໂຄງການລູກຄ້າໂທຫາ yyparse() parser
ບໍລິໂພກຂໍ້ຄວາມທີ່ປ້ອນເຂົ້າຕາມກົດລະບຽບການວິເຄາະ, ເລີ່ມຕົ້ນຈາກກົດລະບຽບທໍາອິດໃນ
ໄວຍາກອນ. yyparse() ສົ່ງຄືນຄ່າທີ່ບໍ່ແມ່ນສູນ ຖ້າການປ້ອນຂໍ້ມູນສາມາດຖືກວິເຄາະຕາມ
ໄວຍາກອນ; ມັນຈະສົ່ງຄ່າສູນຖ້າການປ້ອນຂໍ້ມູນບໍ່ສາມາດວິເຄາະໄດ້.
ຄຳນຳໜ້າ 'yy' ຫຼື 'YY' ແມ່ນນຳໜ້າກັບສັນຍາລັກທີ່ເຫັນໄດ້ຈາກພາຍນອກທັງໝົດໃນເຄື່ອງທີ່ສ້າງຂຶ້ນ.
parser. ນີ້ມີຈຸດປະສົງເພື່ອຫຼຸດຜ່ອນຄວາມສ່ຽງຂອງມົນລະພິດ namespace ໃນໂຄງການລູກຄ້າ.
(ທາງເລືອກຂອງ 'yy' ແມ່ນປະຫວັດສາດ; ເບິ່ງ lex(1) ແລະ yacc(1), ສໍາລັບການຍົກຕົວຢ່າງ.
OPTIONS
peg ແລະ ຂາ ໃຫ້ທາງເລືອກດັ່ງຕໍ່ໄປນີ້:
-h ພິມບົດສະຫຼຸບຂອງທາງເລືອກທີ່ມີຢູ່ແລະຫຼັງຈາກນັ້ນອອກ.
- ຜົນຜະລິດ
ຂຽນ parser ທີ່ສ້າງຂຶ້ນໃສ່ໄຟລ໌ output ແທນທີ່ຈະເປັນຜົນຜະລິດມາດຕະຖານ.
-v ຂຽນຂໍ້ມູນ verbose ກັບຄວາມຜິດພາດມາດຕະຖານໃນຂະນະທີ່ເຮັດວຽກ.
-V ຂຽນຂໍ້ມູນສະບັບເປັນຄວາມຜິດພາດມາດຕະຖານຫຼັງຈາກນັ້ນອອກ.
A SIMPLE EXAMPLE
ຕໍ່ໄປນີ້ peg input ກໍານົດໄວຍະກອນທີ່ມີກົດລະບຽບດຽວ (ເອີ້ນວ່າ 'ເລີ່ມຕົ້ນ') ນັ້ນແມ່ນ
ພໍໃຈເມື່ອການປ້ອນຂໍ້ມູນມີສະຕຣິງ "ຊື່ຜູ້ໃຊ້".
ເລີ່ມ <- "ຊື່ຜູ້ໃຊ້"
(ເຄື່ອງໝາຍວົງຢືມແມ່ນ ບໍ່ ສ່ວນຫນຶ່ງຂອງຂໍ້ຄວາມທີ່ກົງກັນ; ພວກເຂົາຮັບໃຊ້ເພື່ອຊີ້ບອກຕົວຫນັງສື
string to be matched.) ໃນຄໍາສັບຕ່າງໆອື່ນໆ, yyparse() ໃນແຫຼ່ງ C ທີ່ຜະລິດຈະກັບຄືນມາ
ບໍ່ແມ່ນສູນເທົ່ານັ້ນຖ້າແປດຕົວອັກສອນຕໍ່ໄປອ່ານຈາກການປ້ອນຂໍ້ມູນສະກົດຄຳວ່າ "ຊື່ຜູ້ໃຊ້".
ຖ້າການປ້ອນຂໍ້ມູນມີອັນອື່ນ, yyparse() ສົ່ງຄ່າສູນ ແລະບໍ່ມີການປ້ອນຂໍ້ມູນໃດໆ
ບໍລິໂພກ. (ໂທຕໍ່ມາ yyparse() ຍັງຈະສົ່ງຄ່າສູນ, ເນື່ອງຈາກຕົວວິເຄາະແມ່ນ
ສະກັດຢ່າງມີປະສິດທິພາບຊອກຫາສາຍ "ຊື່ຜູ້ໃຊ້".) ເພື່ອຮັບປະກັນຄວາມຄືບຫນ້າພວກເຮົາສາມາດເພີ່ມ
ປະໂຫຍກທາງເລືອກກັບກົດລະບຽບ 'ເລີ່ມຕົ້ນ' ທີ່ຈະກົງກັບຕົວອັກສອນດຽວຖ້າ "ຊື່ຜູ້ໃຊ້"
ບໍ່ພົບ.
ເລີ່ມ <- "ຊື່ຜູ້ໃຊ້"
/.
yyparse() ຕອນນີ້ສົ່ງຄ່າທີ່ບໍ່ແມ່ນສູນສະເໝີ (ຍົກເວັ້ນໃນຕອນທ້າຍຂອງການປ້ອນຂໍ້ມູນ). ເຮັດ
ບາງສິ່ງບາງຢ່າງທີ່ເປັນປະໂຫຍດທີ່ພວກເຮົາສາມາດເພີ່ມການປະຕິບັດກັບກົດລະບຽບ. ການປະຕິບັດເຫຼົ່ານີ້ແມ່ນປະຕິບັດຫຼັງຈາກ a
ການແຂ່ງຂັນທີ່ສົມບູນແມ່ນໄດ້ພົບເຫັນ (ເລີ່ມຈາກກົດລະບຽບທໍາອິດ) ແລະໄດ້ຮັບການຄັດເລືອກຕາມ
'ເສັ້ນທາງ' ປະຕິບັດຜ່ານໄວຍະກອນເພື່ອໃຫ້ກົງກັບວັດສະດຸປ້ອນ. (ນັກພາສາສາດຈະເອີ້ນເສັ້ນທາງນີ້ວ່າ a
'ເຄື່ອງໝາຍວະລີ'.)
ເລີ່ມ <- "ຊື່ຜູ້ໃຊ້" { printf("%s\n", getlogin()); }
/ < . > { putchar(yytext[0]); }
ແຖວທຳອິດແນະນຳໃຫ້ນັກວິເຄາະພິມຊື່ເຂົ້າສູ່ລະບົບຂອງຜູ້ໃຊ້ທຸກຄັ້ງທີ່ມັນເຫັນ
"ຊື່ຜູ້ໃຊ້" ໃນການປ້ອນຂໍ້ມູນ. ຖ້າການຈັບຄູ່ນັ້ນລົ້ມເຫລວ, ແຖວທີສອງຈະບອກຕົວວິເຄາະໃຫ້ສະທ້ອນ
ຕົວອັກສອນຕໍ່ໄປກ່ຽວກັບການປ້ອນຂໍ້ມູນມາດຕະຖານຜົນຜະລິດ. ຕອນນີ້ຕົວວິເຄາະຂອງພວກເຮົາກຳລັງປະຕິບັດໄດ້ທີ່ເປັນປະໂຫຍດ
ເຮັດວຽກ: ມັນຈະສໍາເນົາເອົາການປ້ອນຂໍ້ມູນກັບຜົນຜະລິດ, ທົດແທນການເກີດທັງຫມົດຂອງ "ຊື່ຜູ້ໃຊ້" ກັບ.
ຊື່ບັນຊີຂອງຜູ້ໃຊ້.
ໃຫ້ສັງເກດວົງເລັບມຸມ ('<' ແລະ '>') ທີ່ຖືກເພີ່ມໃສ່ທາງເລືອກທີສອງ. ເຫຼົ່ານີ້
ບໍ່ມີຜົນຕໍ່ຄວາມຫມາຍຂອງກົດລະບຽບ, ແຕ່ໃຫ້ບໍລິການເພື່ອຈໍາກັດຂໍ້ຄວາມທີ່ມີໃຫ້
ການປະຕິບັດຕໍ່ໄປນີ້ຢູ່ໃນຕົວແປ yytext.
ຖ້າໄວຍະກອນຂ້າງເທິງແມ່ນຖືກຈັດໃສ່ໃນໄຟລ໌ username.peg, ແລ່ນຄໍາສັ່ງ
peg -o username.c username.peg
ຈະບັນທຶກ parser ທີ່ສອດຄ້ອງກັນຢູ່ໃນໄຟລ໌ ຊື່ຜູ້ໃຊ້.c. ເພື່ອສ້າງໂຄງການທີ່ສົມບູນ
parser ນີ້ສາມາດໄດ້ຮັບການລວມເຂົ້າໂດຍໂຄງການ C ດັ່ງຕໍ່ໄປນີ້.
#ລວມທັງ /* printf(), putchar() */
#ລວມທັງ /* getlogin() */
#include "username.c" /* yyparse() */
int ຕົ້ນຕໍ ()
{
ໃນຂະນະທີ່ (yyparse()) /* ເຮັດເລື້ມຄືນຈົນກ່ວາ EOF */
;
return 0
}
PEG ໄວຍະກອນ
ໄວຍາກອນປະກອບດ້ວຍຊຸດຂອງກົດລະບຽບທີ່ມີຊື່.
ຊື່ <- ຮູບແບບ
ໄດ້ ຮູບແບບ ມີໜຶ່ງ ຫຼືຫຼາຍອົງປະກອບຕໍ່ໄປນີ້.
ຊື່ ອົງປະກອບຢືນສໍາລັບຮູບແບບທັງຫມົດໃນກົດລະບຽບທີ່ມີໃຫ້ ຊື່.
"ລັກສະນະ"
ຕົວອັກສອນ ຫຼືສະຕຣິງທີ່ຕິດຢູ່ໃນວົງຢືມຄູ່ແມ່ນກົງກັນຕາມຕົວອັກສອນ. ANSI C
ລໍາດັບ escape ແມ່ນຮັບຮູ້ພາຍໃນ ລັກສະນະ.
'ລັກສະນະ'
ຕົວອັກສອນ ຫຼືສະຕຣິງທີ່ຕິດຢູ່ໃນວົງຢືມດຽວແມ່ນກົງກັນຕາມຕົວອັກສອນ, ດັ່ງຂ້າງເທິງ.
[ລັກສະນະ]
ຊຸດຕົວລະຄອນທີ່ປິດຢູ່ໃນວົງເລັບສີ່ຫຼ່ຽມກົງກັບຕົວອັກສອນດຽວຈາກ
ທີ່ກໍານົດໄວ້, ມີຕົວອັກສອນ escape ຮັບຮູ້ເປັນຂ້າງເທິງ. ຖ້າຊຸດເລີ່ມຕົ້ນດ້ວຍ
uparrow (^) ຫຼັງຈາກນັ້ນຊຸດຈະຖືກລົບອອກ (ອົງປະກອບທີ່ກົງກັບຕົວອັກສອນໃດນຶ່ງ ບໍ່ ໃນ
ຕັ້ງ). ຄູ່ຂອງຕົວອັກສອນທີ່ແຍກອອກດ້ວຍ dash (-) ເປັນຕົວແທນຂອງຂອບເຂດຂອງ
ຕົວອັກສອນຈາກຕົວທຳອິດຫາຕົວທີສອງ, ລວມທັງຕົວລະຄອນ. ຕົວອັກສອນຕົວໜັງສືອັນດຽວ
ຫຼືຂີດກ້ອງແມ່ນຖືກຈັບຄູ່ໂດຍຊຸດຕໍ່ໄປນີ້.
[a-zA-Z_]
ເຊັ່ນດຽວກັນ, ຕໍ່ໄປນີ້ກົງກັບຕົວອັກສອນທີ່ບໍ່ແມ່ນຕົວເລກດຽວ.
[^0-9]
. ຈຸດກົງກັບຕົວອັກສອນໃດນຶ່ງ. ໃຫ້ສັງເກດວ່າເວລາດຽວທີ່ລົ້ມເຫລວນີ້ແມ່ນໃນຕອນທ້າຍຂອງ
ໄຟລ໌, ບ່ອນທີ່ບໍ່ມີຕົວອັກສອນທີ່ຈະກົງກັນ.
( ຮູບແບບ )
ວົງເລັບຖືກໃຊ້ສໍາລັບການຈັດກຸ່ມ (ການດັດແກ້ກ່ອນຫນ້າຂອງຕົວປະຕິບັດການ
ອະທິບາຍຂ້າງລຸ່ມນີ້).
{ ການປະຕິບັດ }
ວົງເລັບ curly ອ້ອມຮອບການກະທໍາ. ການປະຕິບັດແມ່ນລະຫັດແຫຼ່ງ C ທີ່ບໍ່ຄາດຄິດທີ່ຈະເປັນ
ປະຕິບັດໃນຕອນທ້າຍຂອງການຈັບຄູ່. ວົງເລັບໃດໆພາຍໃນການປະຕິບັດຕ້ອງຖືກຕ້ອງ
ຮັງ. ຂໍ້ຄວາມທີ່ປ້ອນຂໍ້ມູນໃດຫນຶ່ງທີ່ຖືກຈັບຄູ່ກ່ອນການດໍາເນີນການແລະ delimited ໂດຍມຸມ
ວົງເລັບ (ເບິ່ງຂ້າງລຸ່ມນີ້) ແມ່ນມີຢູ່ໃນການປະຕິບັດເປັນເນື້ອໃນຂອງ
array ຕົວອັກສອນ yytext. ຄວາມຍາວຂອງ (ຈໍານວນຕົວອັກສອນໃນ) yytext is
ມີຢູ່ໃນຕົວແປ yyeng. (ຊື່ຕົວແປເຫຼົ່ານີ້ແມ່ນປະຫວັດສາດ; ເບິ່ງ
lex(1).
< ວົງເລັບມຸມເປີດກົງກັນສະເໝີ (ບໍ່ໃຊ້ການປ້ອນຂໍ້ມູນ) ແລະເຮັດໃຫ້ເກີດການແຍກວິເຄາະ
ເພື່ອເລີ່ມຕົ້ນການສະສົມຂໍ້ຄວາມທີ່ກົງກັນ. ຂໍ້ຄວາມນີ້ຈະຖືກເຮັດໃຫ້ມີການປະຕິບັດໃນ
ຕົວແປ yytext.
> ວົງເລັບມຸມປິດກົງກັນສະເໝີ (ບໍ່ໃຊ້ການປ້ອນຂໍ້ມູນ) ແລະເຮັດໃຫ້ຕົວວິເຄາະ
ເພື່ອຢຸດການສະສົມຂໍ້ຄວາມສໍາລັບ yytext.
ຂ້າງເທິງ elements ສາມາດຖືກສ້າງເປັນທາງເລືອກ ແລະ/ຫຼືເຮັດຊ້ຳໄດ້ດ້ວຍຄຳຕໍ່ທ້າຍຕໍ່ໄປນີ້:
element ?
ອົງປະກອບແມ່ນທາງເລືອກ. ຖ້າມີຢູ່ໃນວັດສະດຸປ້ອນ, ມັນຖືກບໍລິໂພກແລະກົງກັນ
ສໍາເລັດ. ຖ້າບໍ່ມີຢູ່ໃນການປ້ອນຂໍ້ມູນ, ບໍ່ມີຂໍ້ຄວາມຖືກບໍລິໂພກ ແລະການແຂ່ງຂັນສຳເລັດ
ຢ່າງໃດກໍ່ຕາມ.
element +
ອົງປະກອບແມ່ນເຮັດຊ້ໍາໄດ້. ຖ້າຫາກວ່າມີຢູ່ໃນການປ້ອນຂໍ້ມູນ, ຫນຶ່ງຫຼືຫຼາຍການເກີດຂຶ້ນຂອງ
element ຖືກບໍລິໂພກແລະການແຂ່ງຂັນປະສົບຜົນສໍາເລັດ. ຖ້າບໍ່ມີການປະກົດຕົວຂອງ element ມີ
ປະກົດຢູ່ໃນວັດສະດຸປ້ອນ, ການແຂ່ງຂັນລົ້ມເຫລວ.
element *
ອົງປະກອບແມ່ນທາງເລືອກແລະເຮັດຊ້ໍາໄດ້. ຖ້າມີຢູ່ໃນວັດສະດຸປ້ອນ, ໜຶ່ງ ຫຼືຫຼາຍກວ່ານັ້ນ
ການປະກົດຕົວຂອງ element ຖືກບໍລິໂພກແລະການແຂ່ງຂັນປະສົບຜົນສໍາເລັດ. ຖ້າບໍ່ມີການປະກົດຕົວຂອງ
element ມີຢູ່ໃນວັດສະດຸປ້ອນ, ການແຂ່ງຂັນປະສົບຜົນສໍາເລັດຢ່າງໃດກໍ່ຕາມ.
ອົງປະກອບຂ້າງເທິງນີ້ແລະຄໍາຕໍ່ທ້າຍສາມາດຖືກປ່ຽນເປັນ predicates (ທີ່ກົງກັນເອງ
ປ້ອນຂໍ້ຄວາມແລະຕໍ່ມາສໍາເລັດຫຼືລົ້ມເຫຼວ ໂດຍບໍ່ມີການ ການບໍລິໂພກວັດສະດຸປ້ອນນັ້ນ) ກັບ
ຄຳນຳໜ້າຕໍ່ໄປນີ້:
& element
predicate ສົບຜົນສໍາເລັດພຽງແຕ່ຖ້າຫາກວ່າ element ສາມາດຈັບຄູ່ໄດ້. ພິມຂໍ້ຄວາມຖືກສະແກນໃນຂະນະທີ່
ການຈັບຄູ່ element ບໍ່ໄດ້ບໍລິໂພກຈາກການປ້ອນຂໍ້ມູນ ແລະຍັງມີຢູ່
ການຈັບຄູ່ຕໍ່ມາ.
! element
predicate ສົບຜົນສໍາເລັດພຽງແຕ່ຖ້າຫາກວ່າ element ບໍ່ສາມາດຈັບຄູ່ກັນໄດ້. ພິມຂໍ້ຄວາມຖືກສະແກນໃນຂະນະທີ່
ການຈັບຄູ່ element ບໍ່ໄດ້ບໍລິໂພກຈາກການປ້ອນຂໍ້ມູນ ແລະຍັງມີຢູ່
ການຈັບຄູ່ຕໍ່ມາ. ເປັນ idiom ທີ່ນິຍົມແມ່ນ
!.
ເຊິ່ງກົງກັບຈຸດສິ້ນສຸດຂອງໄຟລ໌, ຫຼັງຈາກຕົວອັກສອນສຸດທ້າຍຂອງການປ້ອນຂໍ້ມູນແລ້ວ
ຖືກບໍລິໂພກ.
ແບບຟອມພິເສດຂອງ '&' ແມ່ນໃຫ້:
&{ ການສະແດງອອກ }
ໃນນີ້ predicate ງ່າຍດາຍ C ການສະແດງອອກ (ບໍ່ ຖະແຫຼງການ) ຖືກປະເມີນທັນທີ
ເມື່ອ parser ມາຮອດ predicate. ຖ້າ ການສະແດງອອກ ໃຫ້ຜົນທີ່ບໍ່ແມ່ນສູນ (ຄວາມຈິງ)
'ການຈັບຄູ່' ປະສົບຜົນສໍາເລັດແລະຕົວວິເຄາະສືບຕໍ່ກັບອົງປະກອບຕໍ່ໄປໃນຮູບແບບ.
ຖ້າ ການສະແດງອອກ yields zero (false) the 'match' fails and the parser back up to
ຊອກຫາຕົວແຍກທາງເລືອກຂອງການປ້ອນຂໍ້ມູນ.
ອົງປະກອບຈໍານວນຫນຶ່ງ (ມີຫຼືບໍ່ມີຄໍານໍາຫນ້າແລະ suffixes) ສາມາດຖືກລວມເຂົ້າກັນເປັນ a ລໍາດັບ
ໂດຍການຂຽນໃຫ້ເຂົາເຈົ້າຫນຶ່ງຫຼັງຈາກນັ້ນ. ລໍາດັບທັງຫມົດກົງກັນພຽງແຕ່ຖ້າແຕ່ລະຄົນ
ອົງປະກອບພາຍໃນມັນກົງກັນ, ຈາກຊ້າຍຫາຂວາ.
ລໍາດັບສາມາດຖືກແຍກອອກເປັນທາງເລືອກທີ່ບໍ່ເຂົ້າກັນໂດຍຕົວປະຕິບັດການສະຫຼັບ '/'.
ລຳດັບ-1 / ລຳດັບ-2 / ... / ລຳດັບ-N
ແຕ່ລະລໍາດັບແມ່ນພະຍາຍາມເຮັດຈົນກ່ວາຫນຶ່ງຂອງພວກເຂົາກົງກັນ, ໃນເວລານັ້ນກົງກັນ
ສໍາລັບຮູບແບບໂດຍລວມສໍາເລັດ. ຖ້າບໍ່ມີ ລຳ ດັບໃດກົງກັນ, ກົງກັນ
ຂອງຮູບແບບໂດຍລວມລົ້ມເຫລວ.
ສຸດທ້າຍ, ສັນຍາລັກປອນ (#) ແນະນໍາຄໍາຄິດຄໍາເຫັນ (ຍົກເລີກ) ທີ່ສືບຕໍ່ໄປຈົນເຖິງທີ່ສຸດ
ຂອງສາຍ.
ເພື່ອສະຫຼຸບຂ້າງເທິງ, parser ພະຍາຍາມຈັບຄູ່ຂໍ້ຄວາມທີ່ປ້ອນເຂົ້າກັບຮູບແບບໃດນຶ່ງ
ປະກອບມີຕົວຫນັງສື, ຊື່ (ເປັນຕົວແທນຂອງກົດລະບຽບອື່ນໆ), ແລະຕົວປະຕິບັດການຕ່າງໆ (ຂຽນເປັນ
prefixes, suffixes, juxtaposition for sequencing and and infix alternation operator) ທີ່
ປັບປຸງແກ້ໄຂວິທີການຈັບຄູ່ອົງປະກອບພາຍໃນຮູບແບບ. ການແຂ່ງຂັນແມ່ນເຮັດຈາກຊ້າຍຫາ
ສິດ, 'descending' ເຂົ້າໄປໃນກົດລະບຽບຍ່ອຍທີ່ມີຊື່ຍ້ອນວ່າເຂົາເຈົ້າໄດ້ພົບ. ຖ້າຂະບວນການຈັບຄູ່
ລົ້ມເຫລວ, parser 'back track' ('rewinding' input ຢ່າງເຫມາະສົມໃນຂະບວນການ) ເພື່ອ
ຊອກຫາທາງເລືອກທີ່ໃກ້ທີ່ສຸດ 'ເສັ້ນທາງ' ຜ່ານໄວຍາກອນ. ໃນຄໍາສັບຕ່າງໆອື່ນໆ, parser
ດໍາເນີນການຄົ້ນຫາຄວາມເລິກຄັ້ງທໍາອິດ, ຊ້າຍຫາຂວາສໍາລັບເສັ້ນທາງທີ່ປະສົບຜົນສໍາເລັດທໍາອິດທີ່ກົງກັນ
ໂດຍຜ່ານກົດລະບຽບ. ຖ້າພົບເຫັນ, ການປະຕິບັດຕາມເສັ້ນທາງທີ່ປະສົບຜົນສໍາເລັດຈະຖືກປະຕິບັດ (ໃນ
ຄໍາສັ່ງທີ່ເຂົາເຈົ້າໄດ້ພົບ).
ໃຫ້ສັງເກດວ່າ predicates ໄດ້ຖືກປະເມີນ ທັນທີ ໃນລະຫວ່າງການຄົ້ນຫາການແຂ່ງຂັນທີ່ປະສົບຜົນສໍາເລັດ,
ນັບຕັ້ງແຕ່ພວກເຂົາປະກອບສ່ວນກັບຄວາມສໍາເລັດຫຼືຄວາມລົ້ມເຫລວຂອງການຄົ້ນຫາ. ການກະທໍາ, ຢ່າງໃດກໍຕາມ, ແມ່ນ
ການປະເມີນພຽງແຕ່ຫຼັງຈາກການແຂ່ງຂັນທີ່ປະສົບຜົນສໍາເລັດໄດ້ຖືກພົບເຫັນ.
PEG ໄວຍະກອນ FOR PEG ໄວຍະກອນ
ໄວຍາກອນສໍາລັບ peg ໄວຍະກອນແມ່ນສະແດງໃຫ້ເຫັນຂ້າງລຸ່ມນີ້. ນີ້ຈະສະແດງໃຫ້ເຫັນທັງສອງຢ່າງແລະເປັນທາງການ
ຄຳອະທິບາຍຂ້າງເທິງ.
ໄວຍະກອນ <- Spacing Definition+ EndOfFile
ຄໍານິຍາມ <- Identifier LEFTARROW Expression
ການສະແດງອອກ <- ລໍາດັບ ( SLASH Sequence )*
ລຳດັບ <- ຄຳນຳໜ້າ*
ຄຳນຳໜ້າ <- ແລະ ຄຳສັ່ງ
/ ( ແລະ | ບໍ່ແມ່ນ )? ຕໍ່ທ້າຍ
Suffix <- ຫຼັກ ( QUERY / STAR / PLUS )?
ຫຼັກ <- ຕົວລະບຸ !LEFTARROW
/ ເປີດການສະແດງອອກ CLOSE
/ ຕົວໜັງສື
/ ຫ້ອງຮຽນ
/ DOT
/ ປະຕິບັດ
/ ເລີ່ມຕົ້ນ
/ ສິ້ນສຸດ
ຕົວລະບຸ <- < IdentStart IdentCont* > Spacing
IdentStart <- [a-zA-Z_]
IdentCont <- IdentStart / [0-9]
ຕົວໜັງສື <-[']< ( !['] Char )* > ['] ຍະຫວ່າງ
/ ["]< ( !["] Char )* > ["] ໄລຍະຫ່າງ
ຫ້ອງຮຽນ <- '[' < ( !']' Range )* > ']' ໄລຍະຫ່າງ
ຊ່ວງ <- Char '-' Char / Char
Char <- '\\' [abefnrtv'"\[\]\\]
/ '\\' [0-3][0-7][0-7]
/ '\\' [0-7][0-7]?
/ '\\' '-'
/ !'\\'.
ໄລຍະຫ່າງທາງຊ້າຍ <- '<-'
SLASH <- '/' ໄລຍະຫ່າງ
ແລະ <- '&' ໄລຍະຫ່າງ
ບໍ່ແມ່ນ <- '!' ໄລຍະຫ່າງ
ຄໍາຖາມ <- '?' ໄລຍະຫ່າງ
STAR <- '*' ໄລຍະຫ່າງ
ບວກ <- '+' ໄລຍະຫ່າງ
ເປີດ <- '(' ໄລຍະຫ່າງ
ປິດ <- ')' ໄລຍະຫ່າງ
DOT <- '.' ໄລຍະຫ່າງ
ຍະຫວ່າງ <- (ຍະຫວ່າງ / ຄຳເຫັນ)*
ຄຳເຫັນ <- '#' ( !EndOfLine . )* EndOfLine
Space <- ' ' / '\t' / EndOfLine
EndOfLine <- '\r\n' / '\n' / '\r'
EndOfFile <- !.
ການປະຕິບັດ <- '{' < [^}]* > '}' ໄລຍະຫ່າງ
BEGIN <- '<' ໄລຍະຫ່າງ
END <- '>' ໄລຍະຫ່າງ
LEG ໄວຍະກອນ
ຂາ ແມ່ນການປ່ຽນແປງຂອງ peg ທີ່ເພີ່ມຄຸນສົມບັດບາງຢ່າງຂອງ lex(1) ແລະ yacc(1). ມັນແຕກຕ່າງຈາກ
peg ໃນວິທີການດັ່ງຕໍ່ໄປນີ້.
%{ ຂໍ້ຄວາມ... %}
ພາກສ່ວນການປະກາດສາມາດປາກົດຢູ່ທຸກບ່ອນທີ່ຄາດການກໍານົດກົດລະບຽບ. ໄດ້
ຂໍ້ຄວາມ ລະຫວ່າງຕົວຂັ້ນ '%{' ແລະ '%}' ແມ່ນສຳເນົາຄຳສັບໃສ່ C ທີ່ສ້າງຂຶ້ນ.
ລະຫັດວິເຄາະ ກ່ອນທີ່ຈະ ລະຫັດທີ່ປະຕິບັດຕົວວິເຄາະຕົວມັນເອງ.
ຊື່ = ຮູບແບບ
ໂຕປະຕິບັດການ 'ມອບໝາຍ' ແທນຕົວປະຕິບັດການລູກສອນຊ້າຍ '<-'.
ຊື່ກົດລະບຽບ
Hyphens ສາມາດປາກົດເປັນຕົວອັກສອນໃນຊື່ຂອງກົດລະບຽບ. ແຕ່ລະຂີດຖືກປ່ຽນເປັນ
ຂີດກ້ອງໃນລະຫັດແຫຼ່ງ C ທີ່ສ້າງຂຶ້ນ. ຂີດດຽວ '-' ແມ່ນ ກ
ຊື່ກົດຫມາຍ.
- = [ \t\n\r]*
ເລກ = [0-9]+ -
ຊື່ = [a-zA-Z_][a-zA_Z_0-9]* -
l-paren = '(' -
r-paren = ')' -
ຕົວຢ່າງນີ້ສະແດງໃຫ້ເຫັນວ່າຊ່ອງຫວ່າງທີ່ຖືກລະເລີຍສາມາດເຫັນໄດ້ຊັດເຈນໃນເວລາອ່ານໄວຍາກອນ
ແລະຍັງ unobtrusive ໃນເວລາທີ່ວາງໄວ້ຢ່າງເສລີໃນຕອນທ້າຍຂອງທຸກກົດລະບຽບທີ່ກ່ຽວຂ້ອງກັບ
ອົງປະກອບ lexical.
seq-1 | seq-2
ຕົວປະຕິບັດການສະຫຼັບແມ່ນແຖບຕັ້ງ '|' ແທນທີ່ຈະເປັນຕົວຫຍໍ້ໜ້າ '/'. ໄດ້
peg ກົດລະບຽບ
ຊື່ <- sequence-1
/ sequence-2
/ sequence-3
ດັ່ງນັ້ນຈຶ່ງຂຽນ
name = sequence-1
| ລຳດັບ-2
| ລຳດັບ-3
;
in ຂາ (ມີເຄື່ອງໝາຍຈຸດສຸດທ້າຍເປັນທາງເລືອກ, ດັ່ງທີ່ອະທິບາຍຕໍ່ໄປ).
exp ~ { ການປະຕິບັດ }
ຕົວປະຕິບັດການ postfix ~{ ການປະຕິບັດ } ສາມາດຖືກວາງໄວ້ຫຼັງຈາກການສະແດງອອກໃດໆແລະຈະປະຕິບັດຕົວ
ເຊັ່ນດຽວກັນກັບການປະຕິບັດຕາມປົກກະຕິ (ລະຫັດ C ທີ່ຕົນເອງ) ເວັ້ນເສຍແຕ່ວ່າມັນຖືກຮຽກຮ້ອງພຽງແຕ່ໃນເວລາທີ່ exp
ລົ້ມເຫລວ. ມັນຜູກມັດຫນ້ອຍກ່ວາຕົວປະຕິບັດການອື່ນໆຍົກເວັ້ນສະຫຼັບແລະ
sequencing, ແລະມີຈຸດປະສົງເພື່ອເຮັດໃຫ້ການຈັດການຄວາມຜິດພາດແລະລະຫັດການຟື້ນຕົວງ່າຍຂຶ້ນ
ຂຽນ. ໃຫ້ສັງເກດວ່າ yytext ແລະ yyeng ບໍ່ມີຢູ່ໃນການປະຕິບັດເຫຼົ່ານີ້, ແຕ່ວ່າ
ຕົວປ່ຽນຕົວຊີ້ yy ສາມາດໃຊ້ໄດ້ເພື່ອໃຫ້ລະຫັດເຂົ້າເຖິງໃດໆທີ່ຜູ້ໃຊ້ກໍານົດ
ສະມາຊິກຂອງສະຖານະຕົວວິເຄາະ (ເບິ່ງ "ປັບແຕ່ງຕົວວິເຄາະ" ຂ້າງລຸ່ມນີ້). ໃຫ້ສັງເກດວ່າ
exp ສະເຫມີເປັນການສະແດງອອກດຽວ; ເພື່ອຮຽກຮ້ອງການກະ ທຳ ຂໍ້ຜິດພາດ ສຳ ລັບຄວາມລົ້ມເຫຼວພາຍໃນ
ລໍາດັບ, ວົງເລັບຕ້ອງຖືກນໍາໃຊ້ເພື່ອຈັດກຸ່ມລໍາດັບເປັນອັນດຽວ
ການສະແດງອອກ.
rule = e1 e2 e3 ~{ error("e[12] ok; e3 has failed"); }
| ...
rule = (e1 e2 e3) ~{ error("ຫນຶ່ງໃນ e[123] ລົ້ມເຫລວ"); }
| ...
ຮູບແບບ ;
ເຄື່ອງໝາຍວັກຕອນເຄິ່ງຈໍ້າສອງເມັດສາມາດປິດເປັນທາງເລືອກໄດ້ ຮູບແບບ.
%% ຂໍ້ຄວາມ...
ອັດຕາສ່ວນສອງເທົ່າ '%%' ສິ້ນສຸດກົດລະບຽບ (ແລະການປະກາດ) ພາກສ່ວນຂອງ
ໄວຍາກອນ. ທັງໝົດ ຂໍ້ຄວາມ ຕໍ່ໄປນີ້ '%%' ຖືກຄັດລອກຄໍາເວົ້າທີ່ຈະສ້າງລະຫັດ parser C
ຫຼັງຈາກ ລະຫັດການປະຕິບັດຕົວວິເຄາະ.
$$ = ມູນຄ່າ
ກົດລະບຽບຍ່ອຍສາມາດສົ່ງຄືນຄວາມຫມາຍໄດ້ ມູນຄ່າ ຈາກການປະຕິບັດໂດຍການມອບຫມາຍໃຫ້
pseudo-variable '$$'. ຄ່າ semantic ທັງໝົດຕ້ອງມີປະເພດດຽວກັນ (ເຊິ່ງເປັນຄ່າເລີ່ມຕົ້ນ
ກັບ 'int'). ປະເພດນີ້ສາມາດປ່ຽນແປງໄດ້ໂດຍການກໍານົດ YYSTYPE ໃນສ່ວນການປະກາດ.
ຕົວລະບຸ:ຊື່
ຄ່າ semantic ກັບຄືນມາ (ໂດຍການມອບຫມາຍໃຫ້ '$$') ຈາກກົດລະບຽບຍ່ອຍ ຊື່ is
ທີ່ກ່ຽວຂ້ອງກັບ ຕົວລະບຸ ແລະສາມາດຖືກກ່າວເຖິງໃນການປະຕິບັດຕໍ່ໄປ.
ຕົວຢ່າງຂອງເຄື່ອງຄິດເລກຢູ່ຂ້າງລຸ່ມນີ້ສະແດງໃຫ້ເຫັນການນໍາໃຊ້ '$$' ແລະ ':'.
LEG ຕົວຢ່າງ: A ໂຕະ ເຄື່ອງຄິດໄລ່
ສ່ວນຂະຫຍາຍໃນ ຂາ ອະທິບາຍຂ້າງເທິງນີ້ອະນຸຍາດໃຫ້ຕົວວິເຄາະທີ່ເປັນປະໂຫຍດ ແລະຜູ້ປະເມີນ (ລວມທັງ
ປະກາດ, ກົດລະບຽບໄວຍາກອນ, ແລະສະຫນັບສະຫນູນຫນ້າທີ່ C ເຊັ່ນ 'ຕົ້ນຕໍ') ທີ່ຈະຮັກສາໄວ້ພາຍໃນ.
ໄຟລ໌ແຫຼ່ງດຽວ. ເພື່ອສະແດງໃຫ້ເຫັນນີ້ພວກເຮົາສະແດງໃຫ້ເຫັນການຄິດໄລ່ desk ງ່າຍດາຍສະຫນັບສະຫນູນ
ສີ່ຕົວປະຕິບັດການເລກຄະນິດສາດທົ່ວໄປ ແລະຕົວແປທີ່ມີຊື່. ຜົນໄດ້ຮັບລະດັບປານກາງຂອງ
ການປະເມີນເລກຄະນິດຈະຖືກສະສົມຢູ່ໃນ stack implicit ໂດຍການສົ່ງຄືນໃຫ້ເຂົາເຈົ້າເປັນ
ຄ່າ semantic ຈາກກົດລະບຽບຍ່ອຍ.
%{
#ລວມທັງ /* printf() */
#ລວມທັງ /* atoi() */
int vars[26];
%}
Stmt = - e:Expr EOL { printf("%d\n", e); }
| ( !EOL . )* EOL { printf("error\n"); }
Expr = i:ID ASSIGN s:Sum { $$ = vars[i] = s; }
| s:ລວມ { $$ = s; }
ລວມ = l:ຜະລິດຕະພັນ
(ບວກ r:ຜະລິດຕະພັນ { l += r; }
| MINUS r:ຜະລິດຕະພັນ { l -= r; }
)* { $$ = l; }
ຜະລິດຕະພັນ = l:ມູນຄ່າ
(TIMES r:ຄ່າ { l *= r; }
| DIVIDE r:ຄ່າ { l /= r; }
)* { $$ = l; }
ຄ່າ = i:NUMBER { $$ = atoi(yytext); }
| i:ID !ASSIGN { $$ = vars[i]; }
| ເປີດ i:Expr ປິດ { $$ = i; }
NUMBER = < [0-9]+ > - { $$ = atoi(yytext); }
ID = < [az] > - { $$ = yytext[0] - 'a'; }
ມອບໝາຍ = '=' -
ບວກ = '+' -
MINUS = '-' -
TIMES = '*' -
DIVIDE = '/' -
ເປີດ = '(' -
CLOSE = ')' -
- = [ \t ]*
EOL = '\n' | '\r\n' | '\r' | ';'
%%
int ຕົ້ນຕໍ ()
{
ໃນຂະນະທີ່ (yyparse())
;
return 0
}
LEG ໄວຍະກອນ FOR LEG ໄວຍະກອນ
ໄວຍາກອນສໍາລັບ ຂາ ໄວຍະກອນແມ່ນສະແດງໃຫ້ເຫັນຂ້າງລຸ່ມນີ້. ນີ້ຈະສະແດງໃຫ້ເຫັນທັງສອງຢ່າງແລະເປັນທາງການ
ຄຳອະທິບາຍຂ້າງເທິງ.
ໄວຍະກອນ = -
(ປະກາດ | ຄໍານິຍາມ)+
trailer? ທ້າຍຂອງໄຟລ໌
ປະກາດ = '%{' < ( !'%}' . )* > RPERCENT
trailer = '%%' < .* >
ຄໍານິຍາມ = ຕົວລະບຸ EQUAL expression SEMICOLON?
expression = ລຳດັບ (ລຳດັບ BAR)*
ລໍາດັບ = ຄວາມຜິດພາດ+
error = ຄໍານໍາຫນ້າ ( TILDE action )?
ຄໍານໍາຫນ້າ = ແລະການປະຕິບັດ
| (ແລະ | ບໍ່ແມ່ນ)? ຕໍ່ທ້າຍ
suffix = ປະຖົມ ( QUERY | STAR | PLUS )?
primary = ຕົວລະບຸຕົວລະບຸ COLON !EQUAL
| ຕົວລະບຸ !EQUAL
| ເປີດສະແດງອອກ CLOSE
| ຕົວໜັງສື
| ຫ້ອງຮຽນ
| DOT
| ການກະທຳ
| ເລີ່ມຕົ້ນ
| ສິ້ນສຸດ
ຕົວລະບຸ = < [-a-zA-Z_][-a-zA-Z_0-9]* > -
ລິດ = [']< ( ![']char )* > [']-
| ["]< ( !["] char )* > ["] -
class = '[' < ( !']' range )* > ']' -
range = char '-' char | char
char = '\\' [abefnrtv'"\[\]\\]
| '\\' [0-3][0-7][0-7]
| '\\' [0-7][0-7]?
| !'\\' .
action = '{' < braces* > '}' -
braces = '{' braces* '}'
| !'}' .
EQUAL = '=' -
COLON = ':' -
SEMICOLON = ';' -
BAR = '|' -
ແລະ = '&' -
ບໍ່ແມ່ນ = '!' -
QUERY = '?' -
ດາວ = '*' -
ບວກ = '+' -
ເປີດ = '(' -
CLOSE = ')' -
DOT = '.' -
BEGIN = '<' -
END = '>' -
TILDE = '~' -
RPERCENT = '%}' -
- = (ຊ່ອງ | ຄໍາເຫັນ)*
ຊ່ອງ = ' ' | '\t' | ປາຍສາຍ
comment = '#' ( !end-of-line . )* end-of-line
end-of-line = '\r\n' | '\n' | '\r'
end-of-file = !.
ປັບແຕ່ງ ການ PARSER
ສັນຍາລັກຕໍ່ໄປນີ້ສາມາດຖືກກໍານົດຄືນໃຫມ່ໃນພາກສ່ວນການປະກາດເພື່ອດັດແກ້ທີ່ສ້າງຂຶ້ນ
ລະຫັດວິເຄາະ.
YYSTYPE
ປະເພດມູນຄ່າ semantic. ຕົວແປ pseudo-variable '$$' ແລະຕົວລະບຸ 'ຜູກມັດ' ກັບ
ຜົນໄດ້ຮັບກົດລະບຽບກັບຕົວປະຕິບັດການຈໍ້າສອງເມັດ ':' ທັງຫມົດຄວນຈະຖືກພິຈາລະນາວ່າເປັນການປະກາດ
ທີ່ຈະມີປະເພດນີ້. ຄ່າເລີ່ມຕົ້ນແມ່ນ 'int'.
YYPARSE
ຊື່ຂອງຈຸດເຂົ້າຫຼັກໄປຫາຕົວວິເຄາະ. ຄ່າເລີ່ມຕົ້ນແມ່ນ 'yyparse'.
YYPARSEFROM
ຊື່ຂອງທາງເລືອກທີ່ປ້ອນໄປຫາຕົວວິເຄາະ. ຟັງຊັນນີ້ຄາດຫວັງວ່າຫນຶ່ງ
argument: ຟັງຊັນທີ່ສອດຄ້ອງກັບກົດລະບຽບທີ່ຊອກຫາການຈັບຄູ່
ຄວນເລີ່ມຕົ້ນ. ຄ່າເລີ່ມຕົ້ນແມ່ນ 'yyparsefrom'. ໃຫ້ສັງເກດວ່າ yyparse() ຖືກກໍານົດເປັນ
int yyparse() { return yyparsefrom(yy_foo); }
ບ່ອນທີ່ 'foo' ແມ່ນຊື່ຂອງກົດລະບຽບທໍາອິດໃນໄວຍາກອນ.
YY_INPUT(buff, ຜົນ, ຂະໜາດສູງສຸດ)
ມະຫາພາກນີ້ຖືກເອີ້ນໂດຍຕົວແຍກວິເຄາະເພື່ອໃຫ້ໄດ້ຂໍ້ຄວາມທີ່ປ້ອນເຂົ້າຫຼາຍຂຶ້ນ. buff ຊີ້ໃຫ້ເຫັນເຖິງ
ພື້ນທີ່ຂອງຄວາມຊົງຈໍາທີ່ສາມາດຖືໄດ້ຫຼາຍທີ່ສຸດ ຂະໜາດສູງສຸດ ຕົວລະຄອນ. ມະຫາພາກຄວນສຳເນົາ
ປ້ອນຂໍ້ຄວາມໃສ່ buff ແລະຫຼັງຈາກນັ້ນກໍານົດຕົວແປຈໍານວນເຕັມ ຜົນ ເພື່ອຊີ້ບອກ
ສຳເນົາຕົວອັກສອນ. ຖ້າບໍ່ມີການປ້ອນຂໍ້ມູນເພີ່ມເຕີມ, macro ຄວນ
ມອບໝາຍ 0 ໃຫ້ ຜົນ. ໂດຍຄ່າເລີ່ມຕົ້ນ, ມະຫາພາກ YY_INPUT ຖືກກຳນົດໄວ້ດັ່ງນີ້.
#ກຳນົດ YY_INPUT(buf, ຜົນໄດ້ຮັບ, max_size)
{
int yyc= getchar();
ຜົນໄດ້ຮັບ= (EOF == yyc) ? 0 : (*(buf)= yyc, 1);
}
ໃຫ້ສັງເກດວ່າຖ້າ YY_CTX_LOCAL ຖືກກໍານົດ (ເບິ່ງຂ້າງລຸ່ມນີ້) ຫຼັງຈາກນັ້ນ, ການໂຕ້ຖຽງທໍາອິດເພີ່ມເຕີມ,
ປະກອບດ້ວຍບໍລິບົດການວິເຄາະ, ຖືກສົ່ງກັບ YY_INPUT.
YY_DEBUG
ຖ້າສັນຍາລັກນີ້ຖືກກໍານົດ, ລະຫັດເພີ່ມເຕີມຈະຖືກລວມຢູ່ໃນຕົວວິເຄາະ
ພິມຂໍ້ມູນ arcane ຈໍານວນຫຼວງຫຼາຍໃຫ້ກັບຄວາມຜິດພາດມາດຕະຖານໃນຂະນະທີ່ເຄື່ອງວິເຄາະ
ກຳ ລັງແລ່ນ.
YY_BEGIN
ມະຫາພາກນີ້ຖືກຮຽກຮ້ອງເພື່ອໝາຍຈຸດເລີ່ມຕົ້ນຂອງຂໍ້ຄວາມທີ່ປ້ອນເຂົ້າທີ່ຈະສາມາດໃຊ້ໄດ້
ໃນການປະຕິບັດເປັນ 'yytext'. ນີ້ເທົ່າກັບການປະກົດຕົວຂອງ '<' ໃນໄວຍະກອນ.
ສິ່ງເຫຼົ່ານີ້ຖືກປ່ຽນເປັນ predicates ທີ່ຄາດວ່າຈະປະສົບຜົນສໍາເລັດ. ຄ່າເລີ່ມຕົ້ນ
ຄໍານິຍາມ
#define YY_BEGIN (yybegin= yypos, 1)
ດັ່ງນັ້ນຈຶ່ງຊ່ວຍປະຢັດຕໍາແຫນ່ງ input ໃນປັດຈຸບັນແລະກັບຄືນ 1 ('true') ເປັນຜົນມາຈາກ
ຄາດ.
YY_END ມະຫາພາກນີ້ກົງກັບ '>' ໃນໄວຍະກອນ. ອີກເທື່ອຫນຶ່ງ, ມັນເປັນ predicate ດັ່ງນັ້ນ
ຄໍານິຍາມເລີ່ມຕົ້ນຈະບັນທຶກຕໍາແຫນ່ງປ້ອນຂໍ້ມູນກ່ອນທີ່ຈະ 'ສໍາເລັດ'.
#define YY_END (yyend= yypos, 1)
YY_PARSE(T)
ມະຫາພາກນີ້ປະກາດຈຸດເຂົ້າຕົວວິເຄາະ (yyparse ແລະ yyparsefrom) ເປັນປະເພດ.
T. ຄໍານິຍາມເລີ່ມຕົ້ນ
#ກຳນົດ YY_PARSE(T) T
ໃບ yyparse() ແລະ yyparsefrom() ດ້ວຍການເບິ່ງເຫັນທົ່ວໂລກ. ຖ້າພວກເຂົາບໍ່ຄວນເປັນ
ເຫັນໄດ້ຈາກພາຍນອກໃນໄຟລ໌ແຫຼ່ງອື່ນໆ, ມະຫາພາກນີ້ສາມາດຖືກກຳນົດຄືນໃໝ່ເພື່ອປະກາດ
ເຂົາເຈົ້າ 'static'.
#define YY_PARSE(T) static T
YY_CTX_LOCAL
ຖ້າສັນຍາລັກນີ້ຖືກກໍານົດໃນລະຫວ່າງການລວບລວມຕົວແຍກທີ່ສ້າງຂຶ້ນຫຼັງຈາກນັ້ນທົ່ວໂລກ
ສະຖານະຕົວວິເຄາະຈະຖືກເກັບໄວ້ໃນໂຄງສ້າງຂອງປະເພດ 'yycontext' ເຊິ່ງສາມາດປະກາດໄດ້
ເປັນຕົວແປທ້ອງຖິ່ນ. ນີ້ອະນຸຍາດໃຫ້ຫຼາຍໆຕົວຢ່າງຂອງ parsers ຢູ່ຮ່ວມກັນແລະ
ປອດໄພກະດ້າງ. ຟັງຊັນການແຍກ yyparse() ຈະຖືກປະກາດວ່າຄາດວ່າຈະມີຄັ້ງທໍາອິດ
ການໂຕ້ຖຽງຂອງປະເພດ 'yycontext *', ຕົວຢ່າງຂອງໂຄງສ້າງທີ່ຖືທົ່ວໂລກ
ລັດສໍາລັບຕົວວິເຄາະ. ຕົວຢ່າງນີ້ຕ້ອງໄດ້ຮັບການຈັດສັນແລະເລີ່ມຕົ້ນເປັນສູນໂດຍ
ລູກຄ້າ. ຕົວຢ່າງເລັກນ້ອຍແຕ່ຄົບຖ້ວນສົມບູນມີດັ່ງນີ້.
#ລວມ
#ກຳນົດ YY_CTX_LOCAL
#include "the-generated-parser.peg.c"
int ຕົ້ນຕໍ ()
{
yycontext ctx;
memset(&ctx, 0, sizeof(yycontext));
ໃນຂະນະທີ່ (yyparse(&ctx));
return 0
}
ໃຫ້ສັງເກດວ່າຖ້າສັນຍາລັກນີ້ບໍ່ໄດ້ຖືກ ກຳ ນົດຫຼັງຈາກນັ້ນຕົວວິເຄາະທີ່ລວບລວມຈະຄົງທີ່
ຈັດສັນລັດທົ່ວໂລກຂອງຕົນ ແລະຈະບໍ່ເປັນຄືນມາ ຫຼືປອດໄພ. ຫມາຍເຫດຍັງ
ວ່າໂຄງສ້າງ yycontext parser ຖືກເລີ່ມຕົ້ນໂດຍອັດຕະໂນມັດໃນຄັ້ງທໍາອິດ
yyparse() ເອີ້ນວ່າ; ໂຄງປະກອບການນີ້ ຕ້ອງ ດັ່ງນັ້ນຈຶ່ງຖືກເລີ່ມຕົ້ນຢ່າງຖືກຕ້ອງເປັນສູນ
ກ່ອນທີ່ຈະໂທຫາຄັ້ງທໍາອິດ yyparse,
YY_CTX_MEMBERS
ຖ້າ YY_CTX_LOCAL ຖືກກໍານົດ (ເບິ່ງຂ້າງເທິງ) ຫຼັງຈາກນັ້ນ macro YY_CTX_MEMBERS ສາມາດຖືກກໍານົດ.
ເພື່ອຂະຫຍາຍໄປຫາການປະກາດພາກສະຫນາມສະມາຊິກເພີ່ມເຕີມທີ່ລູກຄ້າຕ້ອງການ
ລວມຢູ່ໃນການປະກາດປະເພດໂຄງສ້າງ 'yycontext'. ເຫຼົ່ານີ້ເພີ່ມເຕີມ
ສະມາຊິກຈະຖືກລະເລີຍໂດຍຕົວແຍກວິເຄາະທີ່ສ້າງຂຶ້ນ. ຕົວຢ່າງຂອງ 'yycontext'
ທີ່ກ່ຽວຂ້ອງກັບ parser ທີ່ໃຊ້ໃນປັດຈຸບັນແມ່ນມີຢູ່ພາຍໃນການປະຕິບັດເປັນ
ຕົວປ່ຽນຕົວຊີ້ yy.
YY_BUFFER_SIZE
ຂະຫນາດເບື້ອງຕົ້ນຂອງຂໍ້ຄວາມ buffer, ໃນ bytes. ຄ່າເລີ່ມຕົ້ນແມ່ນ 1024 ແລະ buffer
ຂະຫນາດແມ່ນເພີ່ມຂຶ້ນສອງເທົ່າທຸກຄັ້ງທີ່ຕ້ອງການເພື່ອຕອບສະຫນອງຄວາມຕ້ອງການໃນລະຫວ່າງການວິເຄາະ. ຄໍາຮ້ອງສະຫມັກ
ທີ່ໂດຍທົ່ວໄປແລ້ວ parses ສາຍຍາວຫຼາຍສາມາດເພີ່ມອັນນີ້ເພື່ອຫຼີກເວັ້ນການທີ່ບໍ່ຈໍາເປັນ
ການຈັດສັນພູມຕ້ານທານ.
YY_STACK_SIZE
ຂະໜາດເບື້ອງຕົ້ນຂອງຕົວແປ ແລະ stacks ການປະຕິບັດ. ຄ່າເລີ່ມຕົ້ນແມ່ນ 128, ເຊິ່ງແມ່ນ
ເພີ່ມຂຶ້ນສອງເທົ່າທຸກຄັ້ງທີ່ຕ້ອງການເພື່ອຕອບສະໜອງຄວາມຕ້ອງການໃນລະຫວ່າງການວິເຄາະ. ຄໍາຮ້ອງສະຫມັກທີ່ມີ
ການໂທເລິກຊ້ອນກັບຕົວແປທ້ອງຖິ່ນຫຼາຍອັນ, ຫຼືວ່າປະຕິບັດຫຼາຍຢ່າງຫຼັງຈາກ a
ການແຂ່ງຂັນທີ່ປະສົບຜົນສໍາເລັດອັນດຽວ, ສາມາດເພີ່ມທະວີການນີ້ເພື່ອຫຼີກເວັ້ນການ buffer ທີ່ບໍ່ຈໍາເປັນ
ການຈັດສັນ.
YY_MALLOC(YY, ຫລືຂະຫຍາຍໂຕ)
ໜ່ວຍຄວາມຈຳສຳລັບບ່ອນເກັບຂໍ້ມູນທີ່ກ່ຽວຂ້ອງກັບຕົວແຍກວິເຄາະທັງໝົດ. ຕົວກໍານົດການແມ່ນ
ໂຄງສ້າງ yy ປະຈຸບັນແລະຈໍານວນຂອງ bytes ທີ່ຈະຈັດສັນ. ຄ່າເລີ່ມຕົ້ນ
ຄໍານິຍາມແມ່ນ: malloc(ຫລືຂະຫຍາຍໂຕ)
YY_REALLOC(YY, PTR, ຫລືຂະຫຍາຍໂຕ)
ຕົວຈັດແບ່ງໜ່ວຍຄວາມຈຳສຳລັບການເກັບຮັກສາແບບໄດນາມິກ (ເຊັ່ນ: ຂໍ້ຄວາມ buffers ແລະ
stacks ຕົວປ່ຽນແປງ). ພາລາມິເຕີແມ່ນໂຄງສ້າງ yycontext ໃນປັດຈຸບັນ, the
ການຈັດສັນທີ່ຈັດສັນໃນເມື່ອກ່ອນ, ແລະຈໍານວນຂອງໄບຕ໌ທີ່ຈະເກັບຮັກສາໄວ້
ເຕີບໃຫຍ່. ຄໍານິຍາມເລີ່ມຕົ້ນແມ່ນ: realloc(PTR, ຫລືຂະຫຍາຍໂຕ)
YY_FREE(YY, PTR)
ຕົວແທນຈໍາຫນ່າຍຫນ່ວຍຄວາມຈໍາ. ພາລາມິເຕີແມ່ນໂຄງສ້າງ yycontext ໃນປັດຈຸບັນແລະ
ການເກັບຮັກສາທີ່ຈະຈັດສັນ. ຄໍານິຍາມໃນຕອນຕົ້ນແມ່ນ: free(PTR)
YYRELEASE
ຊື່ຂອງຟັງຊັນທີ່ປ່ອຍຊັບພະຍາກອນທັງຫມົດທີ່ຖືໂດຍໂຄງສ້າງ yycontext.
ຄ່າເລີ່ມຕົ້ນແມ່ນ 'yyrelease'.
ຕົວແປຕໍ່ໄປນີ້ສາມາດຖືກອ້າງເຖິງພາຍໃນການປະຕິບັດ.
char *yybuf
ຕົວແປນີ້ຊີ້ໃຫ້ເຫັນເຖິງການປ້ອນຂໍ້ມູນຂອງ parser buffer ທີ່ໃຊ້ເພື່ອເກັບຮັກສາຂໍ້ຄວາມປ້ອນຂໍ້ມູນທີ່ມີ
ຍັງບໍ່ທັນຖືກຈັບຄູ່.
int yypos
ນີ້ແມ່ນການຊົດເຊີຍ (ໃນ yybuf) ຂອງຕົວອັກສອນຕໍ່ໄປທີ່ຈະຈັບຄູ່ແລະບໍລິໂພກ.
char * yytext
ຂໍ້ຄວາມທີ່ກົງກັນຫຼ້າສຸດທີ່ຂັ້ນດ້ວຍ '<' ແລະ '>' ຖືກເກັບໄວ້ໃນຕົວແປນີ້.
int yyeng
ຕົວແປນີ້ຊີ້ບອກຈໍານວນຕົວອັກສອນໃນ 'yytext'.
ເນື້ອໃນ yy *ປີ
ຕົວແປນີ້ຊີ້ໃຫ້ເຫັນເຖິງຕົວຢ່າງຂອງ 'yycontext' ທີ່ກ່ຽວຂ້ອງກັບ
parser ທີ່ໃຊ້ໃນປັດຈຸບັນ.
ໂປຣແກຣມທີ່ຕ້ອງການປ່ອຍຊັບພະຍາກອນທັງໝົດທີ່ກ່ຽວຂ້ອງກັບຕົວວິເຄາະສາມາດນຳໃຊ້ໄດ້
ຫນ້າທີ່ດັ່ງຕໍ່ໄປນີ້.
yyrelease(yyrelease*yy)
ສົ່ງຄືນພື້ນທີ່ເກັບຂໍ້ມູນທີ່ຈັດສັນໂດຍຕົວວິເຄາະທັງໝົດທີ່ກ່ຽວຂ້ອງກັບ yy ກັບລະບົບ. ການເກັບຮັກສາ
ຈະຖືກຈັດສັນຄືນໃໝ່ໃນການໂທຄັ້ງຕໍ່ໄປ yyparse,
ໃຫ້ສັງເກດວ່າບ່ອນເກັບມ້ຽນສໍາລັບໂຄງສ້າງ yycontext ຕົວມັນເອງບໍ່ເຄີຍຖືກຈັດສັນຫຼືຍຶດຄືນ
ໂດຍທາງອ້ອມ. ຄໍາຮ້ອງສະຫມັກຕ້ອງຈັດສັນໂຄງສ້າງເຫຼົ່ານີ້ໃນການເກັບຮັກສາອັດຕະໂນມັດ, ຫຼືການນໍາໃຊ້
ໂທກ() ແລະ ຟຣີ() ເພື່ອຄຸ້ມຄອງພວກມັນຢ່າງຈະແຈ້ງ. ຕົວຢ່າງໃນພາກຕໍ່ໄປນີ້
ສະແດງໃຫ້ເຫັນວິທີການຫນຶ່ງໃນການຄຸ້ມຄອງຊັບພະຍາກອນ.
LEG ຕົວຢ່າງ: ຂະຫຍາຍ ການ PARSER'S CONTEXT
ໄດ້ yy ຕົວແປທີ່ສົ່ງຕໍ່ກັບການກະທຳປະກອບມີສະຖານະຂອງຕົວວິເຄາະບວກກັບສິ່ງເພີ່ມເຕີມ
ຊ່ອງຂໍ້ມູນທີ່ກຳນົດໂດຍ YY_CTX_MEMBERS. ຊ່ອງຂໍ້ມູນເຫຼົ່ານີ້ສາມາດຖືກນໍາໃຊ້ເພື່ອເກັບຮັກສາຄໍາຮ້ອງສະຫມັກສະເພາະ
ຂໍ້ມູນທີ່ເປັນທົ່ວໂລກກັບການໂທສະເພາະຂອງ yyparse(). ເປັນເລື່ອງເລັກໆນ້ອຍໆແຕ່ສົມບູນ ຂາ
ຕົວຢ່າງຕໍ່ໄປນີ້ທີ່ໂຄງສ້າງ yycontext ຖືກຂະຫຍາຍດ້ວຍ a ນັບ ຂອງຈໍານວນຂອງ
ຕົວອັກສອນແຖວໃໝ່ທີ່ເຫັນໃນການປ້ອນຂໍ້ມູນມາເຖິງຕອນນັ້ນ (ໄວຍະກອນນັ້ນຈະບໍລິໂພກ ແລະ ບໍ່ສົນໃຈ
ການປ້ອນຂໍ້ມູນທັງໝົດ). ຜູ້ໂທຂອງ yyparse() ໃຊ້ ນັບ ເພື່ອພິມຈໍານວນຂອງສາຍຂອງ
ວັດສະດຸປ້ອນທີ່ຖືກອ່ານ.
%{
#ກຳນົດ YY_CTX_LOCAL 1
#ກຳນົດ YY_CTX_MEMBERS
int ນັບ;
%}
Char = ('\n' | '\r\n' | '\r') { yy->count++ }
| .
%%
#ລວມ
#ລວມທັງ
int ຕົ້ນຕໍ ()
{
/* ສ້າງບໍລິບົດ parser ທ້ອງຖິ່ນໃນການເກັບຮັກສາອັດຕະໂນມັດ */
yy ສະພາບການ yy;
/* ບໍລິບົດ *ຕ້ອງ* ເລີ່ມຕົ້ນເປັນສູນກ່ອນນຳໃຊ້ຄັ້ງທຳອິດ*/
memset(&yy, 0, sizeof(yy));
ໃນຂະນະທີ່ (yyparse(&yy))
;
printf("%d ແຖວໃໝ່\n", yy.count);
/* ປ່ອຍຊັບພະຍາກອນທັງໝົດທີ່ກ່ຽວຂ້ອງກັບບໍລິບົດ */
yyrelease(&yy);
return 0
}
ທິດສະດີວິທະຍາ
peg ແລະ ຂາ ເຕືອນກ່ຽວກັບເງື່ອນໄຂຕໍ່ໄປນີ້ໃນຂະນະທີ່ປ່ຽນໄວຍາກອນເປັນ parser.
syntax ຄວາມຜິດພາດ
ໄວຍະກອນການປ້ອນຂໍ້ມູນບໍ່ຖືກຕ້ອງໃນບາງທາງ. ຂໍ້ຄວາມຜິດພາດຈະປະກອບມີ
ຂໍ້ຄວາມກ່ຽວກັບການທີ່ຈະຖືກຈັບຄູ່ (ມັກຈະສໍາຮອງຂໍ້ມູນຈໍານວນຫຼາຍຈາກສະຖານທີ່ຕົວຈິງຂອງ
ຂໍ້ຜິດພາດ) ແລະຕົວເລກແຖວຂອງຕົວອັກສອນທີ່ພິຈາລະນາຫຼ້າສຸດ (ເຊິ່ງແມ່ນ
ເລື້ອຍໆສະຖານທີ່ທີ່ແທ້ຈິງຂອງບັນຫາ).
ກົດລະບຽບ 'ຟູ' ໃຫມ່ ແຕ່ ບໍ່ ກໍານົດໄວ້
ໄວຍາກອນຫມາຍເຖິງກົດລະບຽບທີ່ມີຊື່ວ່າ 'foo' ແຕ່ບໍ່ມີຄໍານິຍາມສໍາລັບມັນ.
ການພະຍາຍາມໃຊ້ຕົວແຍກວິເຄາະທີ່ສ້າງຂຶ້ນນັ້ນອາດຈະເຮັດໃຫ້ເກີດຄວາມຜິດພາດຈາກຕົວເຊື່ອມຕໍ່
ເນື່ອງຈາກສັນຍາລັກທີ່ບໍ່ໄດ້ກໍານົດທີ່ກ່ຽວຂ້ອງກັບກົດລະບຽບທີ່ຂາດຫາຍໄປ.
ກົດລະບຽບ 'ຟູ' ກໍານົດໄວ້ ແຕ່ ບໍ່ ໃຫມ່
ໄວຍາກອນໄດ້ກໍານົດກົດລະບຽບທີ່ມີຊື່ວ່າ 'foo' ແລະຫຼັງຈາກນັ້ນບໍ່ສົນໃຈມັນ. ລະຫັດທີ່ກ່ຽວຂ້ອງ
ກັບກົດລະບຽບແມ່ນລວມຢູ່ໃນຕົວແຍກວິເຄາະທີ່ສ້າງຂຶ້ນເຊິ່ງຈະຢູ່ໃນທຸກໆດ້ານ
ມີສຸຂະພາບດີ.
ເປັນໄປໄດ້ infinite ໄວ້ ການເອີ້ນຄືນ in ກົດລະບຽບ 'ຟູ'
ມີຢ່າງຫນ້ອຍຫນຶ່ງເສັ້ນທາງຜ່ານໄວຍາກອນທີ່ນໍາພາຈາກກົດລະບຽບ 'foo'.
ກັບຄືນໄປບ່ອນ (ການຮຽກຮ້ອງ recursive ຂອງ) ກົດລະບຽບດຽວກັນໂດຍບໍ່ມີການບໍລິໂພກການປ້ອນຂໍ້ມູນໃດໆ.
recursion ຊ້າຍ, ໂດຍສະເພາະທີ່ພົບເຫັນຢູ່ໃນເອກະສານມາດຕະຖານ, ມັກຈະ 'ໂດຍກົງ' ແລະ
ຫມາຍເຖິງການຊໍ້າຊາກເລັກນ້ອຍ.
# (6.7.6)
direct-abstract-declarator =
LPAREN abstract-declarator RPAREN
| direct-abstract-declarator? LBRACKET assign-expr? RBRACKET
| direct-abstract-declarator? RBRACKET ດາວ LBRACKET
| direct-abstract-declarator? LPAREN param-type-list? RPAREN
recursion ສາມາດໄດ້ຮັບການກໍາຈັດໄດ້ຢ່າງງ່າຍດາຍໂດຍການແປງພາກສ່ວນຂອງຮູບແບບດັ່ງຕໍ່ໄປນີ້
recursion ເຂົ້າໄປໃນ suffix ຊ້ໍາ.
# (6.7.6)
direct-abstract-declarator =
direct-abstract-declarator-head?
direct-abstract-declarator-tail*
direct-abstract-declarator-head =
LPAREN abstract-declarator RPAREN
direct-abstract-declarator-tail =
LBRACKET assign-expr? RBRACKET
| RBRACKET ດາວ LBRACKET
| LPAREN param-type-list? RPAREN
ຂໍ້ຄວນລະວັງ
ຕົວແຍກວິເຄາະທີ່ຍອມຮັບການປ້ອນຂໍ້ມູນຫວ່າງເປົ່າຈະ ສະເຫມີໄປ ສໍາເລັດ. ພິຈາລະນາຕົວຢ່າງຕໍ່ໄປນີ້,
ບໍ່ແມ່ນຄວາມຜິດປົກກະຕິຂອງຄວາມພະຍາຍາມທໍາອິດທີ່ຈະຂຽນຕົວວິເຄາະທີ່ອີງໃສ່ PEG:
ໂຄງການ = ການສະແດງອອກ *
ການສະແດງອອກ = "ອັນໃດກໍໄດ້"
%%
int ຕົ້ນຕໍ () {
ໃນຂະນະທີ່ (yyparse())
puts("ຄວາມສໍາເລັດ!");
return 0
}
ໂປຣແກມນີ້ loops ຕະຫຼອດໄປ, ບໍ່ວ່າມີ input ໃດ (ຖ້າມີ) ຢູ່ໃນ stdin. ຫຼາຍ
ການແກ້ໄຂແມ່ນເປັນໄປໄດ້, ທີ່ງ່າຍທີ່ສຸດທີ່ຈະຮຽກຮ້ອງໃຫ້ມີການ parser ສະເຫມີກິນບາງສ່ວນ
ການປ້ອນຂໍ້ມູນທີ່ບໍ່ຫວ່າງເປົ່າ. ການປ່ຽນແປງແຖວທໍາອິດເປັນ
ໂປຣແກມ = Expression+
ສໍາເລັດນີ້. ຖ້າ parser ຄາດວ່າຈະໃຊ້ການປ້ອນຂໍ້ມູນທັງຫມົດ, ຫຼັງຈາກນັ້ນຢ່າງຈະແຈ້ງ
ການຮຽກຮ້ອງໃຫ້ມີການສິ້ນສຸດຂອງໄຟລ໌ແມ່ນຍັງແນະນໍາໃຫ້ສູງ:
ໂປຣແກມ = Expression+ !.
ອັນນີ້ໃຊ້ໄດ້ເພາະວ່າຕົວວິເຄາະພຽງແຕ່ບໍ່ສາມາດຈັບຄູ່ ("!" predicate) ຕົວອັກສອນໃດໆທັງໝົດ
("." expression) ເມື່ອມັນພະຍາຍາມອ່ານເກີນຈຸດສິ້ນສຸດຂອງການປ້ອນຂໍ້ມູນ.
ໃຊ້ peg ອອນໄລນ໌ໂດຍໃຊ້ບໍລິການ onworks.net