├── LICENSE.md ├── README.md ├── composer.json └── src ├── c-like.m4 ├── lalr1.php ├── php-skel.m4 └── php.m4 /LICENSE.md: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP skeleton for Bison 2 | 3 | ![](https://github.com/mrsuh/php-bison-skeleton/actions/workflows/tests.yml/badge.svg) 4 | ![](https://img.shields.io/github/license/mrsuh/php-bison-skeleton.svg) 5 | ![](https://img.shields.io/github/v/release/mrsuh/php-bison-skeleton) 6 | 7 | A set of Bison skeleton files that can be used to generate a Bison parser written in PHP. 8 | 9 | ## Requirements: 10 | * PHP >= 7.4 11 | * Bison >= 3.8 12 | 13 | ## Installation 14 | ```bash 15 | composer require --dev mrsuh/php-bison-skeleton 16 | ``` 17 | 18 | ## Usage 19 | ```bash 20 | bison -S vendor/mrsuh/php-bison-skeleton/src/php-skel.m4 -o parser.php grammar.y 21 | ``` 22 | 23 | ## Posts 24 | * [PHP Skeleton for Bison](https://mrsuh.com/articles/2023/php-skeleton-for-bison/) 25 | * [AST parser with PHP and Bison](https://mrsuh.com/articles/2023/ast-parser-with-php-and-bison/) 26 | * [Nginx parser with PHP and Bison](https://mrsuh.com/articles/2023/nginx-parser-with-php-and-bison/) 27 | * [JSON parser with PHP and Bison](https://mrsuh.com/articles/2023/json-parser-with-php-and-bison/) 28 | 29 | ## Docker 30 | * [Bison docker image](https://github.com/mrsuh/docker-bison) 31 | 32 | ## Example 33 | 34 | `grammar.y` 35 | ```php 36 | %define api.parser.class {Parser} 37 | %token T_NUMBER 38 | %left '-' '+' 39 | 40 | %% 41 | start: 42 | expression { printf("%d\n", $1); } 43 | ; 44 | 45 | expression: 46 | T_NUMBER { $$ = $1; } 47 | | expression '+' expression { $$ = $1 + $3; } 48 | | expression '-' expression { $$ = $1 - $3; } 49 | ; 50 | 51 | %% 52 | class Lexer implements LexerInterface { 53 | private array $words; 54 | private int $index = 0; 55 | private int $value = 0; 56 | 57 | public function __construct($resource) 58 | { 59 | $this->words = explode(' ', trim(fgets($resource))); 60 | } 61 | 62 | public function yyerror(string $message): void 63 | { 64 | printf("%s\n", $message); 65 | } 66 | 67 | public function getLVal() 68 | { 69 | return $this->value; 70 | } 71 | 72 | public function yylex(): int 73 | { 74 | if ($this->index >= count($this->words)) { 75 | return LexerInterface::YYEOF; 76 | } 77 | 78 | $word = $this->words[$this->index++]; 79 | if (is_numeric($word)) { 80 | $this->value = (int)$word; 81 | 82 | return LexerInterface::T_NUMBER; 83 | } 84 | 85 | return ord($word); 86 | } 87 | } 88 | 89 | $lexer = new Lexer(STDIN); 90 | $parser = new Parser($lexer); 91 | if (!$parser->parse()) { 92 | exit(1); 93 | } 94 | ``` 95 | 96 | ```bash 97 | bison -S vendor/mrsuh/php-bison-skeleton/src/php-skel.m4 -o parser.php grammar.y 98 | ``` 99 | 100 | ```bash 101 | php parser.php <<< "1 + 2" 102 | 3 103 | ``` 104 | 105 | See more examples in the [folder](./examples) 106 | 107 | ### Tests 108 | ```bash 109 | composer test 110 | ``` 111 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mrsuh/php-bison-skeleton", 3 | "type": "library", 4 | "description": "PHP skeleton for Bison", 5 | "license": "GPL-3.0-only", 6 | "keywords": [ 7 | "bison", 8 | "skeleton", 9 | "php", 10 | "parser-generator" 11 | ], 12 | "require": { 13 | "php": ">=7.4" 14 | }, 15 | "autoload-dev": { 16 | "psr-4": { 17 | "Tests\\": "tests/" 18 | } 19 | }, 20 | "authors": [ 21 | { 22 | "name": "Anton Sukhachev", 23 | "email": "mrsuh6@gmail.com" 24 | } 25 | ], 26 | "require-dev": { 27 | "symfony/process": "^5.4", 28 | "phpunit/phpunit": "^9.6" 29 | }, 30 | "scripts": { 31 | "test": "php vendor/bin/phpunit" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/c-like.m4: -------------------------------------------------------------------------------- 1 | -*- Autoconf -*- 2 | 3 | # Common code for C-like languages (C, C++, Java, etc.) 4 | 5 | # Copyright (C) 2012-2015, 2018-2021 Free Software Foundation, Inc. 6 | 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | 20 | 21 | # _b4_comment(TEXT, OPEN, CONTINUE, END) 22 | # -------------------------------------- 23 | # Put TEXT in comment. Avoid trailing spaces: don't indent empty lines. 24 | # Avoid adding indentation to the first line, as the indentation comes 25 | # from OPEN. That's why we don't patsubst([$1], [^\(.\)], [ \1]). 26 | # Turn "*/" in TEXT into "* /" so that we don't unexpectedly close 27 | # the comments before its end. 28 | # 29 | # Prefix all the output lines with PREFIX. 30 | m4_define([_b4_comment], 31 | [$2[]b4_gsub(m4_expand([$1]), 32 | [[*]/], [*\\/], 33 | [/[*]], [/\\*], 34 | [ 35 | \(.\)], [ 36 | $3\1])$4]) 37 | 38 | 39 | # b4_comment(TEXT, [PREFIX]) 40 | # -------------------------- 41 | # Put TEXT in comment. Prefix all the output lines with PREFIX. 42 | m4_define([b4_comment], 43 | [_b4_comment([$1], [$2/* ], [$2 ], [ */])]) 44 | 45 | 46 | 47 | 48 | # _b4_dollar_dollar(VALUE, SYMBOL-NUM, FIELD, DEFAULT-FIELD) 49 | # ---------------------------------------------------------- 50 | # If FIELD (or DEFAULT-FIELD) is non-null, return "VALUE.FIELD", 51 | # otherwise just VALUE. Be sure to pass "(VALUE)" if VALUE is a 52 | # pointer. 53 | m4_define([_b4_dollar_dollar], 54 | [b4_symbol_value([$1], 55 | [$2], 56 | m4_if([$3], [[]], 57 | [[$4]], [[$3]]))]) 58 | 59 | # b4_dollar_pushdef(VALUE-POINTER, SYMBOL-NUM, [TYPE_TAG], LOCATION) 60 | # b4_dollar_popdef 61 | # ------------------------------------------------------------------ 62 | # Define b4_dollar_dollar for VALUE-POINTER and DEFAULT-FIELD, 63 | # and b4_at_dollar for LOCATION. 64 | m4_define([b4_dollar_pushdef], 65 | [m4_pushdef([b4_dollar_dollar], 66 | [_b4_dollar_dollar([$1], [$2], m4_dquote($][1), [$3])])dnl 67 | m4_pushdef([b4_at_dollar], [$4])dnl 68 | ]) 69 | m4_define([b4_dollar_popdef], 70 | [m4_popdef([b4_at_dollar])dnl 71 | m4_popdef([b4_dollar_dollar])dnl 72 | ]) 73 | -------------------------------------------------------------------------------- /src/lalr1.php: -------------------------------------------------------------------------------- 1 | # PHP skeleton for Bison -*- autoconf -*- 2 | 3 | # Copyright (C) 2007-2015, 2018-2023 Free Software Foundation, Inc. 4 | 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with this program. If not, see . 17 | 18 | m4_include(b4_skeletonsdir/[php.m4]) 19 | 20 | b4_header_if([b4_complain([%header/%defines does not make sense in PHP])]) 21 | 22 | m4_define([b4_symbol_no_destructor_assert], 23 | [b4_symbol_if([$1], [has_destructor], 24 | [b4_complain_at(m4_unquote(b4_symbol([$1], [destructor_loc])), 25 | [%destructor does not make sense in PHP])])]) 26 | b4_symbol_foreach([b4_symbol_no_destructor_assert]) 27 | 28 | ## --------------- ## 29 | ## api.push-pull. ## 30 | ## --------------- ## 31 | 32 | b4_percent_define_default([[api.push-pull]], [[pull]]) 33 | b4_percent_define_check_values([[[[api.push-pull]], 34 | [[pull]], [[push]], [[both]]]]) 35 | 36 | # Define m4 conditional macros that encode the value 37 | # of the api.push-pull flag. 38 | b4_define_flag_if([pull]) m4_define([b4_pull_flag], [[1]]) 39 | b4_define_flag_if([push]) m4_define([b4_push_flag], [[1]]) 40 | m4_case(b4_percent_define_get([[api.push-pull]]), 41 | [pull], [m4_define([b4_push_flag], [[0]])], 42 | [push], [m4_define([b4_pull_flag], [[0]])]) 43 | 44 | # Define a macro to be true when api.push-pull has the value "both". 45 | m4_define([b4_both_if],[b4_push_if([b4_pull_if([$1],[$2])],[$2])]) 46 | 47 | # Handle BISON_USE_PUSH_FOR_PULL for the test suite. So that push parsing 48 | # tests function as written, do not let BISON_USE_PUSH_FOR_PULL modify the 49 | # behavior of Bison at all when push parsing is already requested. 50 | b4_define_flag_if([use_push_for_pull]) 51 | b4_use_push_for_pull_if([ 52 | b4_push_if([m4_define([b4_use_push_for_pull_flag], [[0]])], 53 | [m4_define([b4_push_flag], [[1]])])]) 54 | 55 | # parse.lac 56 | b4_percent_define_default([[parse.lac]], [[none]]) 57 | b4_percent_define_check_values([[[[parse.lac]], [[full]], [[none]]]]) 58 | b4_define_flag_if([lac]) 59 | m4_define([b4_lac_flag], 60 | [m4_if(b4_percent_define_get([[parse.lac]]), 61 | [none], [[0]], [[1]])]) 62 | 63 | 64 | ## ------------- ## 65 | ## Parser File. ## 66 | ## ------------- ## 67 | 68 | b4_output_begin([b4_parser_file_name])[ 69 | ]m4_bpatsubst(b4_file_name, [^"\(.*\)"$], [\1])[. 82 | * 83 | * @@author LALR (1) parser skeleton written by Paolo Bonzini. 84 | * Port to PHP language was done by Anton Sukhachev . 85 | */ 86 | 87 | /** 88 | * Communication interface between the scanner and the Bison-generated 89 | * parser ]b4_parser_class[. 90 | */ 91 | interface LexerInterface { 92 | ]b4_token_enums[ 93 | 94 | ]b4_pull_if([b4_locations_if([[ 95 | /** 96 | * Method to retrieve the beginning position of the last scanned token. 97 | * @@return ]b4_position_type[ the position at which the last scanned token starts. 98 | */ 99 | public function getStartPos(): ]b4_position_type[; 100 | 101 | /** 102 | * Method to retrieve the ending position of the last scanned token. 103 | * @@return ]b4_position_type[ the first position beyond the last scanned token. 104 | */ 105 | public function getEndPos(): ]b4_position_type[;]])[ 106 | 107 | /** 108 | * Method to retrieve the semantic value of the last scanned token. 109 | * @@return ]b4_yystype[ the semantic value of the last scanned token. 110 | */ 111 | public function getLVal(); 112 | 113 | /** 114 | * Entry point for the scanner. Returns the token identifier corresponding 115 | * to the next token and prepares to return the semantic value 116 | * ]b4_locations_if([and beginning/ending positions ])[of the token. 117 | * @@return int the token identifier corresponding to the next token. 118 | */ 119 | public function yylex(): int; 120 | ]])[ 121 | /** 122 | * Emit an error]b4_locations_if([ referring to the given location])[in a user-defined way. 123 | * 124 | *]b4_locations_if([[ @@param ]b4_location_type[ $location The location of the element to which the 125 | * error message is related.]])[ 126 | * @@param string $message The string for the error message. 127 | */ 128 | public function yyerror(]b4_locations_if([?b4_location_type[ $location, ]])[string $message): void; 129 | 130 | ]b4_parse_error_bmatch( 131 | [custom], [[ 132 | /** 133 | * Build and emit a "syntax error" message in a user-defined way. 134 | * 135 | * @@param Context $context The context of the error. 136 | */ 137 | public function reportSyntaxError(Context $context): void; 138 | ]])[ 139 | } 140 | 141 | ]b4_lexer_if([[ 142 | class YYLexer implements LexerInterface { 143 | ]b4_percent_code_get([[lexer]])[ 144 | } 145 | 146 | ]])[ 147 | 148 | 149 | /** 150 | * Information needed to get the list of expected tokens and to forge 151 | * a syntax error diagnostic. 152 | */ 153 | class Context { 154 | public function __construct(]b4_parser_class[ $parser, YYStack $stack, SymbolKind $token]b4_locations_if([[, ]b4_location_type[ $loc]])[) { 155 | $this->yyparser = $parser; 156 | $this->yystack = $stack; 157 | $this->yytoken = $token;]b4_locations_if([[ 158 | $this->yylocation = $loc;]])[ 159 | } 160 | 161 | private ]b4_parser_class[ $yyparser; 162 | private YYStack $yystack; 163 | 164 | 165 | /** 166 | * The symbol kind of the lookahead token. 167 | */ 168 | public function getToken(): SymbolKind { 169 | return $this->yytoken; 170 | } 171 | 172 | private SymbolKind $yytoken;]b4_locations_if([[ 173 | 174 | /** 175 | * The location of the lookahead. 176 | */ 177 | public function getLocation(): ]b4_location_type[ { 178 | return $this->yylocation; 179 | } 180 | 181 | private ]b4_location_type[ $yylocation;]])[ 182 | public const NTOKENS = ]b4_parser_class[::YYNTOKENS; 183 | 184 | /** 185 | * Put in YYARG at most YYARGN of the expected tokens given the 186 | * current YYCTX, and return the number of tokens stored in YYARG. If 187 | * YYARG is null, return the number of expected tokens (guaranteed to 188 | * be less than YYNTOKENS). 189 | * @@param SymbolKind[] $yyarg 190 | */ 191 | public function getExpectedTokens(array &$yyarg, int $yyoffset, int $yyargn): int { 192 | $yycount = $yyoffset;]b4_lac_if([b4_parse_trace_if([[ 193 | // Execute LAC once. We don't care if it is successful, we 194 | // only do it for the sake of debugging output. 195 | if (!$this->yyparser->yylacEstablished) { 196 | $this->yyparser->yylacCheck($this->yystack, $this->yytoken); 197 | } 198 | ]])[ 199 | for ($yyx = 0; $yyx < ]b4_parser_class[::YYNTOKENS; ++$yyx) 200 | { 201 | /** @@var SymbolKind $yysym */ 202 | $yysym = new SymbolKind($yyx); 203 | if ($yysym->getCode() !== ]b4_symbol(error, kind)[ 204 | && $yysym->getCode() !== ]b4_symbol(undef, kind)[ 205 | && $this->yyparser->yylacCheck($this->yystack, $yysym)) 206 | { 207 | if ($yyarg === null) { 208 | $yycount += 1; 209 | } else if ($yycount === $yyargn) { 210 | return 0; 211 | } else { 212 | $yyarg[$yycount++] = $yysym; 213 | } 214 | } 215 | }]], [[ 216 | $yyn = $this->yyparser->yypact[$this->yystack->stateAt(0)]; 217 | if (!$this->yyparser->yyPactValueIsDefault($yyn)) 218 | { 219 | /* Start YYX at -YYN if negative to avoid negative 220 | indexes in YYCHECK. In other words, skip the first 221 | -YYN actions for this state because they are default 222 | actions. */ 223 | $yyxbegin = $yyn < 0 ? -$yyn : 0; 224 | /* Stay within bounds of both yycheck and yytname. */ 225 | $yychecklim = ]b4_parser_class[::YYLAST - $yyn + 1; 226 | $yyxend = $yychecklim < self::NTOKENS ? $yychecklim : self::NTOKENS; 227 | for ($yyx = $yyxbegin; $yyx < $yyxend; ++$yyx) 228 | if ($this->yyparser->yycheck[$yyx + $yyn] === $yyx && $yyx !== ]b4_symbol(error, kind)[ 229 | && !$this->yyparser->yyTableValueIsError($this->yyparser->yytable[$yyx + $yyn])) 230 | { 231 | if ($yyarg === null) 232 | $yycount += 1; 233 | else if ($yycount === $yyargn) 234 | return 0; // FIXME: this is incorrect. 235 | else 236 | $yyarg[$yycount++] = new SymbolKind($yyx); 237 | } 238 | }]])[ 239 | if ($yyarg !== null && $yycount === $yyoffset && $yyoffset < $yyargn) 240 | $yyarg[$yycount] = null; 241 | return $yycount - $yyoffset; 242 | } 243 | } 244 | 245 | class YYStack { 246 | private array $stateStack = [];]b4_locations_if([[ 247 | /** @@var ]b4_location_type[[] */ 248 | private array $locStack = [];]])[ 249 | private array $valueStack = []; 250 | 251 | public int $height = -1; 252 | 253 | /** 254 | * @@param ]b4_yystype[ $value 255 | */ 256 | public function push(int $state, $value]b4_locations_if([, ]b4_location_type[ $loc])[): void { 257 | $this->height++; 258 | 259 | $this->stateStack[$this->height] = $state;]b4_locations_if([[ 260 | $this->locStack[$this->height] = $loc;]])[ 261 | $this->valueStack[$this->height] = $value; 262 | } 263 | 264 | public function pop(int $num = 1): void { 265 | $this->height -= $num; 266 | } 267 | 268 | public function &stateAt(int $i): int { 269 | return $this->stateStack[$this->height - $i]; 270 | } 271 | ]b4_locations_if([[ 272 | 273 | public function &locationAt(int $i): ]b4_location_type[ { 274 | return $this->locStack[$this->height - $i]; 275 | } 276 | ]])[ 277 | /** 278 | * @@return ]b4_yystype[ 279 | */ 280 | public function &valueAt(int $i) { 281 | return $this->valueStack[$this->height - $i]; 282 | } 283 | 284 | // Print the state stack on the debug stream. 285 | public function print($resource): void { 286 | fputs($resource,"Stack now"); 287 | for ($i = 0; $i <= $this->height; $i++) { 288 | fputs($resource, ' ' . $this->stateStack[$i]); 289 | } 290 | fputs($resource, PHP_EOL); 291 | } 292 | } 293 | 294 | 295 | ]b4_declare_symbol_enum[ 296 | 297 | ]b4_locations_if([[ 298 | /** 299 | * A class defining a pair of positions. Positions, defined by the 300 | * ]b4_position_type[ class, denote a point in the input. 301 | * Locations represent a part of the input through the beginning 302 | * and ending positions. 303 | */ 304 | class ]b4_location_type[ { 305 | /** 306 | * The first, inclusive, position in the range. 307 | */ 308 | public ?]b4_position_type[ $begin = null; 309 | 310 | /** 311 | * The first position beyond the range. 312 | */ 313 | public ?]b4_position_type[ $end = null; 314 | 315 | /** 316 | * Create a ]b4_location_type[ from the endpoints of the range. 317 | * @@param ]b4_position_type[ $begin The first position included in the range. 318 | * @@param ]b4_position_type[ $end The first position beyond the range. 319 | */ 320 | public function __construct(?]b4_position_type[ $begin = null, ?]b4_position_type[ $end = null) { 321 | $this->begin = $begin; 322 | $this->end = $end; 323 | } 324 | 325 | /** 326 | * Print a representation of the location. For this to be correct, 327 | * ]b4_position_type[ should override the equals 328 | * method. 329 | */ 330 | public function __toString(): string { 331 | return sprintf('%s-%s', $this->begin, $this->end); 332 | } 333 | } 334 | ]])[ 335 | 336 | ]b4_parser_class_declaration[ 337 | { 338 | ]b4_identification[ 339 | ]b4_percent_code_get([[parser]])[ 340 | ][ 341 | ]b4_parse_error_bmatch( 342 | [detailed\|verbose], [[ 343 | /** 344 | * True if verbose error messages are enabled. 345 | */ 346 | private bool $yyErrorVerbose = true; 347 | 348 | /** 349 | * Whether verbose error messages are enabled. 350 | */ 351 | public function getErrorVerbose(): bool 352 | { 353 | return $this->yyErrorVerbose; 354 | } 355 | 356 | /** 357 | * Set the verbosity of error messages. 358 | * @@param verbose True to request verbose error messages. 359 | */ 360 | public function setErrorVerbose(bool $verbose): void 361 | { 362 | $this->yyErrorVerbose = $verbose; 363 | } 364 | ]])[ 365 | 366 | ]b4_locations_if([[ 367 | private function yylloc(YYStack $rhs, int $n): ]b4_location_type[ 368 | { 369 | if (0 < $n) 370 | return new ]b4_location_type[($rhs->locationAt($n - 1)->begin, $rhs->locationAt(0)->end); 371 | else 372 | return new ]b4_location_type[($rhs->locationAt(0)->end, $rhs->locationAt(0)->end); 373 | }]])[ 374 | 375 | /** 376 | * The object doing lexical analysis for us. 377 | */ 378 | private LexerInterface $yylexer; 379 | 380 | ]b4_parse_param_vars[ 381 | 382 | ]b4_lexer_if([[ 383 | /** 384 | * Instantiates the Bison-generated parser. 385 | */ 386 | public function __construct(]b4_parse_param_decl([b4_lex_param_decl])[) 387 | { 388 | ]b4_percent_code_get([[init]])[]b4_lac_if([[ 389 | /** @@var int[] */ 390 | $this->yylacStack = []; 391 | $this->yylacEstablished = false;]])[ 392 | $this->yylexer = new YYLexer(]b4_lex_param_call[); 393 | $this->yystack = new YYStack(); 394 | ]b4_locations_if([[ 395 | $this->yylloc = new Location();]])[ 396 | ]b4_parse_param_cons[ 397 | } 398 | ]], [[ 399 | /** 400 | * Instantiates the Bison-generated parser. 401 | * @@param LexerInterface $lexer The scanner that will supply tokens to the parser. 402 | */ 403 | public function __construct(]b4_parse_param_decl([[LexerInterface $lexer]])[) 404 | { 405 | ]b4_percent_code_get([[init]])[]b4_lac_if([[ 406 | /** @@var int[] */ 407 | $this->yylacStack = []; 408 | $this->yylacEstablished = false;]])[ 409 | $this->yylexer = $lexer; 410 | $this->yystack = new YYStack(); 411 | ]b4_locations_if([[ 412 | $this->yylloc = new Location();]])[ 413 | ]b4_parse_param_cons[ 414 | } 415 | ]])[ 416 | 417 | ]b4_parse_trace_if([[ 418 | private $yyDebugStream = STDERR; 419 | 420 | /** 421 | * The PrintStream on which the debugging output is printed. 422 | */ 423 | public function getDebugStream() { return $this->yyDebugStream; } 424 | 425 | /** 426 | * Set the PrintStream on which the debug output is printed. 427 | * @@param s The stream that is used for debugging output. 428 | */ 429 | public function setDebugStream($resource): void { $this->yyDebugStream = $resource; } 430 | 431 | private int $yydebug = 0; 432 | 433 | /** 434 | * Answer the verbosity of the debugging output; 0 means that all kinds of 435 | * output from the parser are suppressed. 436 | */ 437 | public function getDebugLevel(): int { return $this->yydebug; } 438 | 439 | /** 440 | * Set the verbosity of the debugging output; 0 means that all kinds of 441 | * output from the parser are suppressed. 442 | * @@param level The verbosity level for debugging output. 443 | */ 444 | public function setDebugLevel(int $level): void { $this->yydebug = $level; } 445 | ]])[ 446 | 447 | private int $yynerrs = 0; 448 | 449 | /** 450 | * The number of syntax errors so far. 451 | */ 452 | public function getNumberOfErrors(): int { return $this->yynerrs; } 453 | 454 | /** 455 | * Print an error message via the lexer. 456 | *]b4_locations_if([[ Use a null location.]])[ 457 | * @@param msg The error message. 458 | */ 459 | public function yyerror(]b4_locations_if([?b4_location_type[ $location, ]])[string $msg): void { 460 | $this->yylexer->yyerror(]b4_locations_if([[$location, ]])[$msg); 461 | } 462 | ]b4_parse_trace_if([[ 463 | protected function yycdebugNnl(string $message): void { 464 | if (0 < $this->yydebug) { 465 | fputs($this->yyDebugStream, $message); 466 | } 467 | } 468 | 469 | protected function yycdebug(string $message): void { 470 | if (0 < $this->yydebug) { 471 | fputs($this->yyDebugStream, $message . PHP_EOL); 472 | } 473 | }]])[ 474 | 475 | /** 476 | * Returned by a Bison action in order to stop the parsing process and 477 | * return success (true). 478 | */ 479 | public const YYACCEPT = 0; 480 | 481 | /** 482 | * Returned by a Bison action in order to stop the parsing process and 483 | * return failure (false). 484 | */ 485 | public const YYABORT = 1; 486 | 487 | ]b4_push_if([ 488 | /** 489 | * Returned by a Bison action in order to request a new token. 490 | */ 491 | public const YYPUSH_MORE = 4;])[ 492 | 493 | /** 494 | * Returned by a Bison action in order to start error recovery without 495 | * printing an error message. 496 | */ 497 | public const YYERROR = 2; 498 | 499 | /** 500 | * Internal return codes that are not supported for user semantic 501 | * actions. 502 | */ 503 | private const YYERRLAB = 3; 504 | private const YYNEWSTATE = 4; 505 | private const YYDEFAULT = 5; 506 | private const YYREDUCE = 6; 507 | private const YYERRLAB1 = 7; 508 | private const YYRETURN = 8; 509 | ]b4_push_if([[ private const YYGETTOKEN = 9; /* Signify that a new token is expected when doing push-parsing. */]])[ 510 | 511 | private int $yyerrstatus = 0; 512 | 513 | /** 514 | * Lookahead token kind. 515 | */ 516 | private int $yychar = ]b4_parser_class[::YYEMPTY; 517 | /** 518 | * Lookahead symbol kind. 519 | */ 520 | private ?SymbolKind $yytoken = null; 521 | 522 | /* State. */ 523 | private int $yyn = 0; 524 | private int $yylen = 0; 525 | private int $yystate = 0; 526 | private YYStack $yystack; 527 | private int $label = ]b4_parser_class[::YYNEWSTATE; 528 | 529 | ]b4_locations_if([[ 530 | /* The location where the error started. */ 531 | private ?]b4_location_type[ $yyerrloc = null; 532 | 533 | /* Location. */ 534 | private ]b4_location_type[ $yylloc;]])[ 535 | 536 | /** 537 | * Semantic value of the lookahead. 538 | * @@var ]b4_yystype[ 539 | */ 540 | private $yylval = null; 541 | 542 | /** 543 | * Whether error recovery is being done. In this state, the parser 544 | * reads token until it reaches a known state, and then restarts normal 545 | * operation. 546 | */ 547 | public function recovering(): bool 548 | { 549 | return $this->yyerrstatus === 0; 550 | } 551 | 552 | /** Compute post-reduction state. 553 | * @@param yystate the current state 554 | * @@param yysym the nonterminal to push on the stack 555 | */ 556 | private function yyLRGotoState(int $yystate, int $yysym): int { 557 | 558 | $yyr = $this->yypgoto]b4_quote_value($yysym - b4_parser_class::YYNTOKENS)[ + $yystate; 559 | if (0 <= $yyr && $yyr <= ]b4_parser_class[::YYLAST && $this->yycheck[$yyr] === $yystate) 560 | return $this->yytable[$yyr]; 561 | else 562 | return $this->yydefgoto]b4_quote_value($yysym - b4_parser_class::YYNTOKENS)[; 563 | } 564 | 565 | private function yyaction(int $yyn, YYStack $yystack, int $yylen): int 566 | { 567 | /* If YYLEN is nonzero, implement the default value of the action: 568 | '$$ = $1'. Otherwise, use the top of the stack. 569 | 570 | Otherwise, the following line sets YYVAL to garbage. 571 | This behavior is undocumented and Bison 572 | users should not rely upon it. */ 573 | /** @@var ]b4_yystype[ $yyval */ 574 | $yyval = (0 < $yylen) ? $yystack->valueAt($yylen - 1) : $yystack->valueAt(0);]b4_locations_if([[ 575 | /** @@var ]b4_location_type[ */ 576 | $yyloc = $this->yylloc($yystack, $yylen);]])[]b4_parse_trace_if([[ 577 | 578 | $this->yyReducePrint($yyn, $yystack);]])[ 579 | 580 | switch ($yyn) 581 | { 582 | ]b4_user_actions[ 583 | default: break; 584 | }]b4_parse_trace_if([[ 585 | 586 | $this->yySymbolPrint("-> $$ =", new SymbolKind($this->yyr1[$yyn]), $yyval]b4_locations_if([, $yyloc])[);]])[ 587 | 588 | $yystack->pop($yylen); 589 | $yylen = 0; 590 | /* Shift the result of the reduction. */ 591 | $yystate = $this->yyLRGotoState($yystack->stateAt(0), $this->yyr1[$yyn]); 592 | $yystack->push($yystate, $yyval]b4_locations_if([, $yyloc])[); 593 | return ]b4_parser_class[::YYNEWSTATE; 594 | } 595 | 596 | ]b4_parse_trace_if([[ 597 | /*--------------------------------. 598 | | Print this symbol on YYOUTPUT. | 599 | `--------------------------------*/ 600 | /** 601 | * @@param ]b4_yystype[ $yyvalue 602 | */ 603 | private function yySymbolPrint(string $s, SymbolKind $yykind, $yyvalue]b4_locations_if([, ]b4_location_type[ $yylocation])[): void { 604 | if (0 < $this->yydebug) { 605 | switch(true) { 606 | case is_array($yyvalue): 607 | $value = sprintf('array(%d)', count($yyvalue)); 608 | break; 609 | case is_null($yyvalue): 610 | $value = 'null'; 611 | break; 612 | case is_object($yyvalue): 613 | if(method_exists($yyvalue, '__toString')) { 614 | $value = (string)$yyvalue; 615 | } else { 616 | $value = get_class($yyvalue); 617 | } 618 | break; 619 | default: 620 | $value = (string)$yyvalue; 621 | } 622 | $this->yycdebug($s 623 | . ($yykind->getCode() < ]b4_parser_class[::YYNTOKENS ? " token " : " nterm ") 624 | . $yykind->getName() . " ("]b4_locations_if([ 625 | . $yylocation . ": "])[ 626 | . $value . ")"); 627 | } 628 | }]])[ 629 | 630 | ]b4_push_if([],[[ 631 | /** 632 | * Parse input from the scanner that was specified at object construction 633 | * time. Return whether the end of the input was reached successfully. 634 | * 635 | * @@return true if the parsing succeeds. Note that this does not 636 | * imply that there were no syntax errors. 637 | */ 638 | public function parse(): bool ]])[ 639 | ]b4_push_if([ 640 | /** 641 | * Push Parse input from external lexer 642 | * 643 | * @@param $yylextoken int current token 644 | * @@param $yylexval ]b4_yystype[ current lval]b4_locations_if([[ 645 | * @@param $yylexloc ]b4_location_type[ current position]])[ 646 | * 647 | * @@return int YYACCEPT, YYABORT, YYPUSH_MORE 648 | */ 649 | public function push_parse(int $yylextoken, $yylexval[]b4_locations_if([, b4_location_type $yylexloc])): int])[ 650 | { 651 | ]b4_push_if([],[[ 652 | 653 | ]b4_lac_if([[ 654 | // Discard the LAC context in case there still is one left from a 655 | // previous invocation. 656 | $this->yylacDiscard("init");]])[ 657 | ]b4_parse_trace_if([[ 658 | $this->yycdebug("Starting parse");]])[ 659 | $this->yyerrstatus = 0; 660 | $this->yynerrs = 0; 661 | 662 | /* Initialize the stack. */ 663 | $this->yystack->push($this->yystate, $this->yylval]b4_locations_if([, $this->yylloc])[); 664 | ]m4_ifdef([b4_initial_action], [ 665 | b4_dollar_pushdef([yylval], [], [], [yylloc])dnl 666 | b4_user_initial_action 667 | b4_dollar_popdef[]dnl 668 | ])[ 669 | ]])[ 670 | ]b4_push_if([[ 671 | if (!$this->push_parse_initialized) 672 | { 673 | $this->push_parse_initialize(); 674 | ]m4_ifdef([b4_initial_action], [ 675 | b4_dollar_pushdef([yylval], [], [], [yylloc])dnl 676 | b4_user_initial_action 677 | b4_dollar_popdef[]dnl 678 | ])[]b4_parse_trace_if([[ 679 | $this->yycdebug ("Starting parse");]])[ 680 | $this->yyerrstatus = 0; 681 | } else 682 | $this->label = ]b4_parser_class[::YYGETTOKEN; 683 | 684 | $push_token_consumed = true; 685 | ]])[ 686 | for (;;) 687 | switch ($this->label) 688 | { 689 | /* New state. Unlike in the C/C++ skeletons, the state is already 690 | pushed when we come here. */ 691 | case ]b4_parser_class[::YYNEWSTATE:]b4_parse_trace_if([[ 692 | $this->yycdebug("Entering state " . $this->yystate); 693 | if (0 < $this->yydebug) { 694 | $this->yystack->print($this->getDebugStream()); 695 | }]])[ 696 | 697 | /* Accept? */ 698 | if ($this->yystate === ]b4_parser_class[::YYFINAL) { 699 | ]b4_push_if([$this->label = ]b4_parser_class[::YYACCEPT; break;], 700 | [return true;])[ 701 | } 702 | 703 | /* Take a decision. First try without lookahead. */ 704 | $this->yyn = $this->yypact[$this->yystate]; 705 | if ($this->yyPactValueIsDefault($this->yyn)) 706 | { 707 | $this->label = ]b4_parser_class[::YYDEFAULT; 708 | break; 709 | } 710 | ]b4_push_if([ /* Fall Through */ 711 | 712 | case ]b4_parser_class[::YYGETTOKEN:])[ 713 | /* Read a lookahead token. */ 714 | if ($this->yychar === ]b4_parser_class[::YYEMPTY) 715 | { 716 | ]b4_push_if([[ 717 | if (!$push_token_consumed) { 718 | return ]b4_parser_class[::YYPUSH_MORE;}]b4_parse_trace_if([[ 719 | $this->yycdebug("Reading a token");]])[ 720 | $this->yychar = $yylextoken; 721 | $this->yylval = $yylexval;]b4_locations_if([ 722 | $this->yylloc = $yylexloc;])[ 723 | $push_token_consumed = false;]], [b4_parse_trace_if([[ 724 | $this->yycdebug ("Reading a token");]])[ 725 | $this->yychar = $this->yylexer->yylex(); 726 | $this->yylval = $this->yylexer->getLVal();]b4_locations_if([[ 727 | $this->yylloc = new ]b4_location_type[($this->yylexer->getStartPos(),$this->yylexer->getEndPos());]])[ 728 | ]])[ 729 | } 730 | 731 | /* Convert token to internal form. */ 732 | $this->yytoken = $this->yytranslate($this->yychar);]b4_parse_trace_if([[ 733 | $this->yySymbolPrint("Next token is", $this->yytoken, 734 | $this->yylval]b4_locations_if([, $this->yylloc])[);]])[ 735 | 736 | if ($this->yytoken->getCode() === ]b4_symbol(error, kind)[) 737 | { 738 | // The scanner already issued an error message, process directly 739 | // to error recovery. But do not keep the error token as 740 | // lookahead, it is too special and may lead us to an endless 741 | // loop in error recovery. */ 742 | $this->yychar = LexerInterface::]b4_symbol(undef, id)[; 743 | $this->yytoken = new SymbolKind(]b4_symbol(undef, kind)[);]b4_locations_if([[ 744 | $this->yyerrloc = $this->yylloc;]])[ 745 | $this->label = ]b4_parser_class[::YYERRLAB1; 746 | } 747 | else 748 | { 749 | /* If the proper action on seeing token YYTOKEN is to reduce or to 750 | detect an error, take that action. */ 751 | $this->yyn += $this->yytoken->getCode(); 752 | if ($this->yyn < 0 || ]b4_parser_class[::YYLAST < $this->yyn || $this->yycheck[$this->yyn] !== $this->yytoken->getCode()) {]b4_lac_if([[ 753 | if (!$this->yylacEstablish($this->yystack, $this->yytoken)) { 754 | $this->label = ]b4_parser_class[::YYERRLAB; 755 | } else]])[ 756 | $this->label = ]b4_parser_class[::YYDEFAULT; 757 | } 758 | 759 | /* <= 0 means reduce or error. */ 760 | else if (($this->yyn = $this->yytable[$this->yyn]) <= 0) 761 | { 762 | if ($this->yyTableValueIsError($this->yyn)) { 763 | $this->label = ]b4_parser_class[::YYERRLAB; 764 | }]b4_lac_if([[ else if (!$this->yylacEstablish($this->yystack, $this->yytoken)) { 765 | $this->label = ]b4_parser_class[::YYERRLAB; 766 | }]])[ else { 767 | $this->yyn = -$this->yyn; 768 | $this->label = ]b4_parser_class[::YYREDUCE; 769 | } 770 | } 771 | 772 | else 773 | { 774 | /* Shift the lookahead token. */]b4_parse_trace_if([[ 775 | $this->yySymbolPrint("Shifting", $this->yytoken, $this->yylval]b4_locations_if([, $this->yylloc])[); 776 | ]])[ 777 | /* Discard the token being shifted. */ 778 | $this->yychar = ]b4_parser_class[::YYEMPTY; 779 | 780 | /* Count tokens shifted since error; after three, turn off error 781 | status. */ 782 | if ($this->yyerrstatus > 0) 783 | --$this->yyerrstatus; 784 | 785 | $this->yystate = $this->yyn; 786 | $this->yystack->push($this->yystate, $this->yylval]b4_locations_if([, $this->yylloc])[);]b4_lac_if([[ 787 | $this->yylacDiscard("shift");]])[ 788 | $this->label = ]b4_parser_class[::YYNEWSTATE; 789 | } 790 | } 791 | break; 792 | 793 | /*-----------------------------------------------------------. 794 | | yydefault -- do the default action for the current state. | 795 | `-----------------------------------------------------------*/ 796 | case ]b4_parser_class[::YYDEFAULT: 797 | $this->yyn = $this->yydefact[$this->yystate]; 798 | if ($this->yyn === 0) 799 | $this->label = ]b4_parser_class[::YYERRLAB; 800 | else 801 | $this->label = ]b4_parser_class[::YYREDUCE; 802 | break; 803 | 804 | /*-----------------------------. 805 | | yyreduce -- Do a reduction. | 806 | `-----------------------------*/ 807 | case ]b4_parser_class[::YYREDUCE: 808 | $this->yylen = $this->yyr2[$this->yyn]; 809 | $this->label = $this->yyaction($this->yyn, $this->yystack, $this->yylen); 810 | $this->yystate = $this->yystack->stateAt(0); 811 | break; 812 | 813 | /*------------------------------------. 814 | | yyerrlab -- here on detecting error | 815 | `------------------------------------*/ 816 | case ]b4_parser_class[::YYERRLAB: 817 | /* If not already recovering from an error, report this error. */ 818 | if ($this->yyerrstatus === 0) 819 | { 820 | ++$this->yynerrs; 821 | if ($this->yychar === ]b4_parser_class[::YYEMPTY) { 822 | $this->yytoken = null; 823 | } 824 | $this->yyreportSyntaxError(new Context($this, $this->yystack, $this->yytoken]b4_locations_if([[, $this->yylloc]])[)); 825 | } 826 | ]b4_locations_if([[ 827 | $this->yyerrloc = $this->yylloc;]])[ 828 | if ($this->yyerrstatus === 3) 829 | { 830 | /* If just tried and failed to reuse lookahead token after an 831 | error, discard it. */ 832 | 833 | if ($this->yychar <= LexerInterface::]b4_symbol(eof, id)[) 834 | { 835 | /* Return failure if at end of input. */ 836 | if ($this->yychar === LexerInterface::]b4_symbol(eof, id)[) { 837 | ]b4_push_if([$this->label = ]b4_parser_class[::YYABORT; break;], [return false;])[ 838 | } 839 | } 840 | else 841 | $this->yychar = ]b4_parser_class[::YYEMPTY; 842 | } 843 | 844 | /* Else will try to reuse lookahead token after shifting the error 845 | token. */ 846 | $this->label = ]b4_parser_class[::YYERRLAB1; 847 | break; 848 | 849 | /*-------------------------------------------------. 850 | | errorlab -- error raised explicitly by YYERROR. | 851 | `-------------------------------------------------*/ 852 | case ]b4_parser_class[::YYERROR:]b4_locations_if([[ 853 | $this->yyerrloc = $this->yystack->locationAt ($this->yylen - 1);]])[ 854 | /* Do not reclaim the symbols of the rule which action triggered 855 | this YYERROR. */ 856 | $this->yystack->pop($this->yylen); 857 | $this->yylen = 0; 858 | $this->yystate = $this->yystack->stateAt(0); 859 | $this->label = ]b4_parser_class[::YYERRLAB1; 860 | break; 861 | 862 | /*-------------------------------------------------------------. 863 | | yyerrlab1 -- common code for both syntax error and YYERROR. | 864 | `-------------------------------------------------------------*/ 865 | case ]b4_parser_class[::YYERRLAB1: 866 | $this->yyerrstatus = 3; /* Each real token shifted decrements this. */ 867 | 868 | // Pop stack until we find a state that shifts the error token. 869 | for (;;) 870 | { 871 | $this->yyn = $this->yypact[$this->yystate]; 872 | if (!$this->yyPactValueIsDefault($this->yyn)) 873 | { 874 | $this->yyn += ]b4_symbol(error, kind)[; 875 | if (0 <= $this->yyn && $this->yyn <= ]b4_parser_class[::YYLAST 876 | && $this->yycheck[$this->yyn] === ]b4_symbol(error, kind)[) 877 | { 878 | $this->yyn = $this->yytable[$this->yyn]; 879 | if (0 < $this->yyn) 880 | break; 881 | } 882 | } 883 | 884 | /* Pop the current state because it cannot handle the 885 | * error token. */ 886 | if ($this->yystack->height === 0) { 887 | ]b4_push_if([$this->label = ]b4_parser_class[::YYABORT; break;],[return false;])[ 888 | } 889 | 890 | ]b4_locations_if([[ 891 | $this->yyerrloc = $this->yystack->locationAt(0);]])[ 892 | $this->yystack->pop(); 893 | $this->yystate = $this->yystack->stateAt(0);]b4_parse_trace_if([[ 894 | if (0 < $this->yydebug) 895 | $this->yystack->print($this->getDebugStream());]])[ 896 | } 897 | 898 | if ($this->label === ]b4_parser_class[::YYABORT) 899 | /* Leave the switch. */ 900 | break; 901 | 902 | ]b4_locations_if([[ 903 | /* Muck with the stack to setup for yylloc. */ 904 | $this->yystack->push (0, null, $this->yylloc); 905 | $this->yystack->push (0, null, $this->yyerrloc); 906 | $this->yyloc = $this->yylloc ($this->yystack, 2); 907 | $this->yystack->pop(2);]])[ 908 | 909 | /* Shift the error token. */]b4_lac_if([[ 910 | $this->yylacDiscard("error recovery");]])[]b4_parse_trace_if([[ 911 | $this->yySymbolPrint("Shifting", new SymbolKind($this->yystos[$this->yyn]), 912 | $this->yylval]b4_locations_if([, $this->yyloc])[);]])[ 913 | 914 | $this->yystate = $this->yyn; 915 | $this->yystack->push($this->yyn, $this->yylval]b4_locations_if([, $this->yyloc])[); 916 | $this->label = ]b4_parser_class[::YYNEWSTATE; 917 | break; 918 | 919 | /* Accept. */ 920 | case ]b4_parser_class[::YYACCEPT: 921 | ]b4_push_if([$this->push_parse_initialized = false; return ]b4_parser_class[::YYACCEPT;], 922 | [return true;])[ 923 | 924 | /* Abort. */ 925 | case ]b4_parser_class[::YYABORT: 926 | ]b4_push_if([$this->push_parse_initialized = false; return ]b4_parser_class[::YYABORT;], 927 | [return false;])[ 928 | } 929 | } 930 | ]b4_push_if([[ 931 | private $push_parse_initialized = false; 932 | 933 | /** 934 | * (Re-)Initialize the state of the push parser. 935 | */ 936 | public function push_parse_initialize(): void 937 | { 938 | /* Lookahead and lookahead in internal form. */ 939 | $this->yychar = ]b4_parser_class[::YYEMPTY; 940 | $this->yytoken = null; 941 | 942 | /* State. */ 943 | $this->yyn = 0; 944 | $this->yylen = 0; 945 | $this->yystate = 0; 946 | $this->yystack = new YYStack();]b4_lac_if([[ 947 | $this->yylacStack = []; 948 | $this->yylacEstablished = false;]])[ 949 | $this->label = ]b4_parser_class[::YYNEWSTATE; 950 | 951 | /* Error handling. */ 952 | $this->yynerrs = 0;]b4_locations_if([[ 953 | /* The location where the error started. */ 954 | $this->yyerrloc = null; 955 | $this->yylloc = new ]b4_location_type[(null, null);]])[ 956 | 957 | /* Semantic value of the lookahead. */ 958 | $this->yylval = null; 959 | 960 | $this->yystack->push($this->yystate, $this->yylval]b4_locations_if([, $this->yylloc])[); 961 | 962 | $this->push_parse_initialized = true; 963 | 964 | } 965 | ]])[ 966 | 967 | ]b4_both_if([[ 968 | /** 969 | * Parse input from the scanner that was specified at object construction 970 | * time. Return whether the end of the input was reached successfully. 971 | * This version of parse() is defined only when api.push-push=both. 972 | * 973 | * @@return true if the parsing succeeds. Note that this does not 974 | * imply that there were no syntax errors. 975 | */ 976 | public function parse(): bool { 977 | if ($this->yylexer === null) { 978 | throw new \RuntimeException("Null Lexer"); 979 | } 980 | 981 | do { 982 | $token = $this->yylexer->yylex(); 983 | /** @@var ]b4_yystype[ */ 984 | $lval = $this->yylexer->getLVal();]b4_locations_if([[ 985 | /** @@var ]b4_location_type[ */ 986 | $yyloc = new ]b4_location_type[($this->yylexer->getStartPos(), $this->yylexer->getEndPos()); 987 | $status = $this->push_parse($token, $lval, $yyloc);]], [[ 988 | $status = $this->push_parse($token, $lval);]])[ 989 | } while ($status === ]b4_parser_class[::YYPUSH_MORE); 990 | return $status === ]b4_parser_class[::YYACCEPT; 991 | } 992 | ]])[ 993 | 994 | ]b4_lac_if([[ 995 | /** Check the lookahead yytoken. 996 | * \returns true iff the token will be eventually shifted. 997 | */ 998 | public function yylacCheck(YYStack $yystack, SymbolKind $yytoken): bool 999 | { 1000 | // Logically, the yylacStack's lifetime is confined to this function. 1001 | // Clear it, to get rid of potential left-overs from previous call. 1002 | $this->yylacStack = []; 1003 | // Reduce until we encounter a shift and thereby accept the token. 1004 | $this->yycdebugNnl("LAC: checking lookahead " . $yytoken->getName() . ":"); 1005 | $lacTop = 0; 1006 | while (true) 1007 | { 1008 | $topState = (empty($this->yylacStack) 1009 | ? $yystack->stateAt($lacTop) 1010 | : $this->yylacStack[count($this->yylacStack) - 1]); 1011 | $yyrule = $this->yypact[$topState]; 1012 | if ($this->yyPactValueIsDefault($yyrule) 1013 | || ($yyrule += $this->yytoken->getCode()) < 0 || ]b4_parser_class[::YYLAST < $yyrule 1014 | || $this->yycheck[$yyrule] !== $this->yytoken->getCode()) 1015 | { 1016 | // Use the default action. 1017 | $yyrule = $this->yydefact[+$topState]; 1018 | if ($yyrule === 0) { 1019 | $this->yycdebug(" Err"); 1020 | return false; 1021 | } 1022 | } 1023 | else 1024 | { 1025 | // Use the action from yytable. 1026 | $yyrule = $this->yytable[$yyrule]; 1027 | if ($this->yyTableValueIsError($yyrule)) { 1028 | $this->yycdebug(" Err"); 1029 | return false; 1030 | } 1031 | if (0 < $yyrule) { 1032 | $this->yycdebug(" S" . $yyrule); 1033 | return true; 1034 | } 1035 | $yyrule = -$yyrule; 1036 | } 1037 | // By now we know we have to simulate a reduce. 1038 | $this->yycdebugNnl(" R" . ($yyrule - 1)); 1039 | // Pop the corresponding number of values from the stack. 1040 | { 1041 | $yylen = $this->yyr2[$yyrule]; 1042 | // First pop from the LAC stack as many tokens as possible. 1043 | $lacSize = count($this->yylacStack); 1044 | if ($yylen < $lacSize) { 1045 | for (/* Nothing */; 0 < $yylen; $yylen -= 1) { 1046 | array_pop($this->yylacStack); 1047 | } 1048 | $yylen = 0; 1049 | } else if ($lacSize !== 0) { 1050 | $this->yylacStack = []; 1051 | $yylen -= $lacSize; 1052 | } 1053 | // Only afterwards look at the main stack. 1054 | // We simulate popping elements by incrementing lacTop. 1055 | $lacTop += $yylen; 1056 | } 1057 | // Keep topState in sync with the updated stack. 1058 | $topState = (empty($this->yylacStack) 1059 | ? $yystack->stateAt($lacTop) 1060 | : $this->yylacStack[count($this->yylacStack) - 1]); 1061 | // Push the resulting state of the reduction. 1062 | $state = $this->yyLRGotoState($topState, $this->yyr1[$yyrule]); 1063 | $this->yycdebugNnl(" G" . $state); 1064 | $this->yylacStack[] = $state; 1065 | } 1066 | } 1067 | 1068 | /** Establish the initial context if no initial context currently exists. 1069 | * \returns true iff the token will be eventually shifted. 1070 | */ 1071 | public function yylacEstablish(YYStack $yystack, SymbolKind $yytoken): bool { 1072 | /* Establish the initial context for the current lookahead if no initial 1073 | context is currently established. 1074 | 1075 | We define a context as a snapshot of the parser stacks. We define 1076 | the initial context for a lookahead as the context in which the 1077 | parser initially examines that lookahead in order to select a 1078 | syntactic action. Thus, if the lookahead eventually proves 1079 | syntactically unacceptable (possibly in a later context reached via a 1080 | series of reductions), the initial context can be used to determine 1081 | the exact set of tokens that would be syntactically acceptable in the 1082 | lookahead's place. Moreover, it is the context after which any 1083 | further semantic actions would be erroneous because they would be 1084 | determined by a syntactically unacceptable token. 1085 | 1086 | yylacEstablish should be invoked when a reduction is about to be 1087 | performed in an inconsistent state (which, for the purposes of LAC, 1088 | includes consistent states that don't know they're consistent because 1089 | their default reductions have been disabled). 1090 | 1091 | For parse.lac=full, the implementation of yylacEstablish is as 1092 | follows. If no initial context is currently established for the 1093 | current lookahead, then check if that lookahead can eventually be 1094 | shifted if syntactic actions continue from the current context. */ 1095 | if ($this->yylacEstablished) { 1096 | return true; 1097 | } else { 1098 | $this->yycdebug("LAC: initial context established for " . $yytoken->getName()); 1099 | $this->yylacEstablished = true; 1100 | return $this->yylacCheck($yystack, $yytoken); 1101 | } 1102 | } 1103 | 1104 | /** Discard any previous initial lookahead context because of event. 1105 | * \param event the event which caused the lookahead to be discarded. 1106 | * Only used for debbuging output. */ 1107 | public function yylacDiscard(string $event): void { 1108 | /* Discard any previous initial lookahead context because of Event, 1109 | which may be a lookahead change or an invalidation of the currently 1110 | established initial context for the current lookahead. 1111 | 1112 | The most common example of a lookahead change is a shift. An example 1113 | of both cases is syntax error recovery. That is, a syntax error 1114 | occurs when the lookahead is syntactically erroneous for the 1115 | currently established initial context, so error recovery manipulates 1116 | the parser stacks to try to find a new initial context in which the 1117 | current lookahead is syntactically acceptable. If it fails to find 1118 | such a context, it discards the lookahead. */ 1119 | if ($this->yylacEstablished) { 1120 | $this->yycdebug("LAC: initial context discarded due to " . $event); 1121 | $this->yylacEstablished = false; 1122 | } 1123 | } 1124 | 1125 | /** The stack for LAC. 1126 | * Logically, the yylacStack's lifetime is confined to the function 1127 | * yylacCheck. We just store it as a member of this class to hold 1128 | * on to the memory and to avoid frequent reallocations. 1129 | * @@var int[] 1130 | */ 1131 | private array $yylacStack = []; 1132 | /** Whether an initial LAC context was established. */ 1133 | public bool $yylacEstablished = false; 1134 | ]])[ 1135 | 1136 | ]b4_parse_error_bmatch( 1137 | [detailed\|verbose], [[ 1138 | /** 1139 | * @@param SymbolKind[] $yyarg 1140 | */ 1141 | private function yysyntaxErrorArguments(Context $yyctx, array &$yyarg, int $yyargn): int { 1142 | /* There are many possibilities here to consider: 1143 | - If this state is a consistent state with a default action, 1144 | then the only way this function was invoked is if the 1145 | default action is an error action. In that case, don't 1146 | check for expected tokens because there are none. 1147 | - The only way there can be no lookahead present (in tok) is 1148 | if this state is a consistent state with a default action. 1149 | Thus, detecting the absence of a lookahead is sufficient to 1150 | determine that there is no unexpected or expected token to 1151 | report. In that case, just report a simple "syntax error". 1152 | - Don't assume there isn't a lookahead just because this 1153 | state is a consistent state with a default action. There 1154 | might have been a previous inconsistent state, consistent 1155 | state with a non-default action, or user semantic action 1156 | that manipulated yychar. (However, yychar is currently out 1157 | of scope during semantic actions.) 1158 | - Of course, the expected token list depends on states to 1159 | have correct lookahead information, and it depends on the 1160 | parser not to perform extra reductions after fetching a 1161 | lookahead from the scanner and before detecting a syntax 1162 | error. Thus, state merging (from LALR or IELR) and default 1163 | reductions corrupt the expected token list. However, the 1164 | list is correct for canonical LR with one exception: it 1165 | will still contain any token that will not be accepted due 1166 | to an error action in a later state. 1167 | */ 1168 | $yycount = 0; 1169 | if ($yyctx->getToken() !== null) { 1170 | if ($yyarg !== null) { 1171 | $yyarg[$yycount] = $yyctx->getToken(); 1172 | } 1173 | 1174 | $yycount += 1; 1175 | $yycount += $yyctx->getExpectedTokens($yyarg, 1, $yyargn); 1176 | } 1177 | 1178 | return $yycount; 1179 | } 1180 | ]])[ 1181 | 1182 | /** 1183 | * Build and emit a "syntax error" message in a user-defined way. 1184 | * 1185 | * @@param ctx The context of the error. 1186 | */ 1187 | private function yyreportSyntaxError(Context $yyctx): void {]b4_parse_error_bmatch( 1188 | [custom], [[ 1189 | $this->yylexer->reportSyntaxError($yyctx);]], 1190 | [detailed\|verbose], [[ 1191 | $message = "syntax error"; 1192 | if ($this->yyErrorVerbose) { 1193 | /** @@var SymbolKind[] $yyarg */ 1194 | $yyarg = []; 1195 | $yycount = $this->yysyntaxErrorArguments($yyctx, $yyarg, 5); 1196 | $yystr = []; 1197 | for ($yyi = 0; $yyi < $yycount; ++$yyi) { 1198 | $yystr[$yyi] = $yyarg[$yyi]->getName(); 1199 | } 1200 | if ($yycount > 1) { 1201 | $unexpected = array_shift($yystr); 1202 | $message .= sprintf(", got %s, but expecting %s", $unexpected, implode(' or ', $yystr)); 1203 | } else if ($yycount > 0) { 1204 | $message = sprintf("syntax error, unexpected '%s'", $yystr[0]); 1205 | } 1206 | } 1207 | $this->yyerror(]b4_locations_if([[$yyctx->getLocation(), ]])[$message); 1208 | ]], 1209 | [simple], [[ 1210 | $this->yyerror(]b4_locations_if([[$yyctx->getLocation(), ]])["syntax error");]])[ 1211 | } 1212 | 1213 | /** 1214 | * Whether the given yypact_ value indicates a defaulted state. 1215 | * @@param yyvalue the value to check 1216 | */ 1217 | public function yyPactValueIsDefault(int $yyvalue): bool { 1218 | return $yyvalue === $this->yypact_ninf; 1219 | } 1220 | 1221 | /** 1222 | * Whether the given yytable_ 1223 | * value indicates a syntax error. 1224 | * @@param yyvalue the value to check 1225 | */ 1226 | public function yyTableValueIsError(int $yyvalue): bool { 1227 | return $yyvalue === $this->yytable_ninf; 1228 | } 1229 | 1230 | public int $yypact_ninf = ]b4_pact_ninf[; 1231 | public int $yytable_ninf = ]b4_table_ninf[; 1232 | 1233 | ]b4_parser_tables_define[ 1234 | 1235 | ]b4_parse_trace_if([[ 1236 | ]b4_integral_parser_table_define([rline], [b4_rline], 1237 | [[YYRLINE[YYN] -- Source line where rule number YYN was defined.]])[ 1238 | 1239 | 1240 | // Report on the debug stream that the rule yyrule is going to be reduced. 1241 | private function yyReducePrint(int $yyrule, YYStack $yystack): void 1242 | { 1243 | if ($this->yydebug === 0) 1244 | return; 1245 | 1246 | $yylno = $this->yyrline[$yyrule]; 1247 | $yynrhs = $this->yyr2[$yyrule]; 1248 | /* Print the symbols being reduced, and their result. */ 1249 | $this->yycdebug("Reducing stack by rule " . ($yyrule - 1) 1250 | . " (line " . $yylno . "):"); 1251 | 1252 | /* The symbols being reduced. */ 1253 | for ($yyi = 0; $yyi < $yynrhs; $yyi++) 1254 | $this->yySymbolPrint(" $" . ($yyi + 1) . " =", 1255 | new SymbolKind($this->yystos[$yystack->stateAt($yynrhs - ($yyi + 1))]), 1256 | ]b4_rhs_data($yynrhs, $yyi + 1)b4_locations_if([, 1257 | b4_rhs_location($yynrhs, $yyi + 1)])[); 1258 | }]])[ 1259 | 1260 | /* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM 1261 | as returned by yylex, with out-of-bounds checking. */ 1262 | private function yytranslate(int $t): SymbolKind 1263 | ]b4_api_token_raw_if(dnl 1264 | [[ { 1265 | return new SymbolKind($t); 1266 | } 1267 | ]], 1268 | [[ { 1269 | // Last valid token kind. 1270 | $code_max = ]b4_code_max[; 1271 | if ($t <= 0) 1272 | return new SymbolKind(]b4_symbol(eof, kind)[); 1273 | else if ($t <= $code_max) 1274 | return new SymbolKind($this->yytranslate_table[$t]); 1275 | else 1276 | return new SymbolKind(]b4_symbol(undef, kind)[); 1277 | } 1278 | ]b4_integral_parser_table_define([translate_table], [b4_translate])[ 1279 | ]])[ 1280 | 1281 | public const YYLAST = ]b4_last[; 1282 | public const YYEMPTY = -2; 1283 | public const YYFINAL = ]b4_final_state_number[; 1284 | public const YYNTOKENS = ]b4_tokens_number[; 1285 | 1286 | ]b4_percent_code_get[ 1287 | } 1288 | ]b4_percent_code_get([[epilogue]])[]dnl 1289 | b4_epilogue[]dnl 1290 | b4_output_end 1291 | -------------------------------------------------------------------------------- /src/php-skel.m4: -------------------------------------------------------------------------------- 1 | -*- Autoconf -*- 2 | 3 | # PHP skeleton dispatching for Bison. 4 | 5 | # Copyright (C) 2007, 2009-2015, 2018-2023 Free Software Foundation, 6 | # Inc. 7 | 8 | # This program is free software: you can redistribute it and/or modify 9 | # it under the terms of the GNU General Public License as published by 10 | # the Free Software Foundation, either version 3 of the License, or 11 | # (at your option) any later version. 12 | # 13 | # This program is distributed in the hope that it will be useful, 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | # GNU General Public License for more details. 17 | # 18 | # You should have received a copy of the GNU General Public License 19 | # along with this program. If not, see . 20 | 21 | b4_glr_if( [b4_complain([%%glr-parser not supported for PHP])]) 22 | b4_nondeterministic_if([b4_complain([%%nondeterministic-parser not supported for PHP])]) 23 | 24 | m4_define([b4_skeletonsdir], b4_gsub(__file__, b4_basename(__file__), [])) 25 | 26 | m4_define_default([b4_used_skeleton], [b4_skeletonsdir/[lalr1.php]]) 27 | m4_define_default([b4_skeleton], ["b4_basename(b4_used_skeleton)"]) 28 | 29 | m4_include(b4_used_skeleton) 30 | -------------------------------------------------------------------------------- /src/php.m4: -------------------------------------------------------------------------------- 1 | -*- Autoconf -*- 2 | 3 | # PHP language support for Bison 4 | 5 | # Copyright (C) 2007-2015, 2018-2023 Free Software Foundation, Inc. 6 | 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program. If not, see . 19 | 20 | m4_include(b4_skeletonsdir/[c-like.m4]) 21 | 22 | 23 | # b4_list2(LIST1, LIST2) 24 | # ---------------------- 25 | # Join two lists with a comma if necessary. 26 | m4_define([b4_list2], 27 | [$1[]m4_ifval(m4_quote($1), [m4_ifval(m4_quote($2), [[, ]])])[]$2]) 28 | 29 | 30 | # b4_percent_define_get3(DEF, PRE, POST, NOT) 31 | # ------------------------------------------- 32 | # Expand to the value of DEF surrounded by PRE and POST if it's %define'ed, 33 | # otherwise NOT. 34 | m4_define([b4_percent_define_get3], 35 | [m4_ifval(m4_quote(b4_percent_define_get([$1])), 36 | [$2[]b4_percent_define_get([$1])[]$3], [$4])]) 37 | 38 | 39 | 40 | # b4_flag_value(BOOLEAN-FLAG) 41 | # --------------------------- 42 | m4_define([b4_flag_value], [b4_flag_if([$1], [true], [false])]) 43 | 44 | 45 | # b4_parser_class_declaration 46 | # --------------------------- 47 | # The declaration of the parser class ("class YYParser"), with all its 48 | # qualifiers/annotations. 49 | b4_percent_define_default([[api.parser.abstract]], [[false]]) 50 | b4_percent_define_default([[api.parser.final]], [[false]]) 51 | 52 | m4_define([b4_parser_class_declaration], 53 | [b4_percent_define_get3([api.parser.annotations], [], [ ])dnl 54 | b4_percent_define_flag_if([api.parser.abstract], [abstract ])dnl 55 | b4_percent_define_flag_if([api.parser.final], [final ])dnl 56 | [class ]b4_parser_class[]dnl 57 | b4_percent_define_get3([api.parser.extends], [ extends ])dnl 58 | b4_percent_define_get3([api.parser.implements], [ implements ])]) 59 | 60 | 61 | # b4_lexer_if(TRUE, FALSE) 62 | # ------------------------ 63 | m4_define([b4_lexer_if], 64 | [b4_percent_code_ifdef([[lexer]], [$1], [$2])]) 65 | 66 | 67 | # b4_identification 68 | # ----------------- 69 | m4_define([b4_identification], 70 | [[ /** Version number for the Bison executable that generated this parser. */ 71 | public const BISON_VERSION = "]b4_version_string["; 72 | 73 | /** Name of the skeleton that generated this parser. */ 74 | public const BISON_SKELETON = ]b4_skeleton[; 75 | ]]) 76 | 77 | 78 | ## ------------ ## 79 | ## Data types. ## 80 | ## ------------ ## 81 | 82 | # b4_null 83 | # ------- 84 | m4_define([b4_null], [null]) 85 | m4_define([m4_php_array],[array(m4_bpatsubst([$1], [\$], [\\\$]))]) 86 | 87 | 88 | # b4_typed_parser_table_define(TYPE, NAME, DATA, COMMENT) 89 | # ------------------------------------------------------- 90 | m4_define([b4_typed_parser_table_define], 91 | [m4_ifval([$4], [b4_comment([$4]) 92 | ])dnl 93 | [ 94 | /** @@var ]$1[[] */ 95 | public array $yy$2 = array(]$3[); 96 | ]]) 97 | 98 | 99 | # b4_integral_parser_table_define(NAME, DATA, COMMENT) 100 | #----------------------------------------------------- 101 | m4_define([b4_integral_parser_table_define], 102 | [b4_typed_parser_table_define([int], [$1], [$2], [$3])]) 103 | 104 | 105 | ## ------------- ## 106 | ## Token kinds. ## 107 | ## ------------- ## 108 | 109 | 110 | # b4_token_enum(TOKEN-NUM) 111 | # ------------------------ 112 | # Output the definition of this token as an enum. 113 | m4_define([b4_token_enum], 114 | [b4_token_visible_if([$1], 115 | [m4_format([[ /** Token %s, to be returned by the scanner. */ 116 | public const %s = %s%s; 117 | ]], 118 | b4_symbol([$1], [tag]), 119 | b4_symbol([$1], [id]), 120 | b4_symbol([$1], b4_api_token_raw_if([[number]], [[code]])))])]) 121 | 122 | 123 | # b4_token_enums 124 | # -------------- 125 | # Output the definition of the tokens (if there are) as enums. 126 | m4_define([b4_token_enums], 127 | [b4_any_token_visible_if([ /* Token kinds. */ 128 | b4_symbol_foreach([b4_token_enum])])]) 129 | 130 | 131 | 132 | ## -------------- ## 133 | ## Symbol kinds. ## 134 | ## -------------- ## 135 | 136 | 137 | # b4_symbol_kind(NUM) 138 | # ------------------- 139 | m4_define([b4_symbol_kind], 140 | [SymbolKind::b4_symbol_kind_base($@)]) 141 | 142 | 143 | # b4_symbol_enum(SYMBOL-NUM) 144 | # -------------------------- 145 | # Output the definition of this symbol as an enum. 146 | m4_define([b4_symbol_enum], 147 | [m4_format([ %-30s %s], 148 | m4_format([[public const %s = %s%s;]], 149 | b4_symbol([$1], [kind_base]), 150 | [$1]), 151 | [b4_symbol_tag_comment([$1])])]) 152 | 153 | 154 | # b4_declare_symbol_enum 155 | # ---------------------- 156 | # The definition of the symbol internal numbers as an enum. 157 | m4_define([b4_declare_symbol_enum], 158 | [[ class SymbolKind 159 | { 160 | ]b4_symbol_foreach([b4_symbol_enum])[ 161 | 162 | private int $yycode; 163 | 164 | public function __construct(int $yycode) { 165 | $this->yycode = $yycode; 166 | } 167 | 168 | public function getCode(): int { 169 | return $this->yycode; 170 | } 171 | 172 | ]b4_parse_error_bmatch( 173 | [simple\|verbose], 174 | [[ 175 | private const NAMES = ]m4_php_array(b4_tname)[; 176 | 177 | public function getName(): string { 178 | return trim(self::NAMES[$this->yycode], '"\''); 179 | } 180 | ]], 181 | [custom\|detailed], 182 | [[ 183 | private const NAMES = ]m4_php_array(b4_symbol_names)[; 184 | 185 | public function getName(): string { 186 | return self::NAMES[$this->yycode]; 187 | }]])[ 188 | } 189 | ]])]) 190 | 191 | 192 | 193 | # b4_case(ID, CODE, [COMMENTS]) 194 | # ----------------------------- 195 | m4_define([b4_case], 196 | [ case $1:m4_ifval([$3], [ b4_comment([$3])]) 197 | $2; 198 | break; 199 | ]) 200 | 201 | 202 | # b4_predicate_case(LABEL, CONDITIONS) 203 | # ------------------------------------ 204 | m4_define([b4_predicate_case], 205 | [ case $1: 206 | if (! ($2)) YYERROR; 207 | break; 208 | ]) 209 | 210 | 211 | ## -------- ## 212 | ## Checks. ## 213 | ## -------- ## 214 | 215 | b4_percent_define_check_kind([[api.value.type]], [code], [deprecated]) 216 | 217 | b4_percent_define_check_kind([[annotations]], [code], [deprecated]) 218 | b4_percent_define_check_kind([[extends]], [code], [deprecated]) 219 | b4_percent_define_check_kind([[implements]], [code], [deprecated]) 220 | b4_percent_define_check_kind([[api.parser.class]], [code], [deprecated]) 221 | 222 | 223 | 224 | ## ---------------- ## 225 | ## Default values. ## 226 | ## ---------------- ## 227 | 228 | m4_define([b4_yystype], [b4_percent_define_get([[api.value.type]])]) 229 | b4_percent_define_default([[api.value.type]], [[mixed]]) 230 | b4_percent_define_default([[api.symbol.prefix]], [[S_]]) 231 | 232 | # b4_api_prefix, b4_api_PREFIX 233 | # ---------------------------- 234 | # Corresponds to %define api.prefix 235 | b4_percent_define_default([[api.prefix]], [[YY]]) 236 | m4_define([b4_api_prefix], 237 | [b4_percent_define_get([[api.prefix]])]) 238 | m4_define([b4_api_PREFIX], 239 | [m4_toupper(b4_api_prefix)]) 240 | 241 | # b4_prefix 242 | # --------- 243 | # If the %name-prefix is not given, it is api.prefix. 244 | m4_define_default([b4_prefix], [b4_api_prefix]) 245 | 246 | b4_percent_define_default([[api.parser.class]], [b4_prefix[]Parser]) 247 | m4_define([b4_parser_class], [b4_percent_define_get([[api.parser.class]])]) 248 | 249 | b4_percent_define_default([[api.location.type]], [Location]) 250 | m4_define([b4_location_type], [b4_percent_define_get([[api.location.type]])]) 251 | 252 | b4_percent_define_default([[api.position.type]], [Position]) 253 | m4_define([b4_position_type], [b4_percent_define_get([[api.position.type]])]) 254 | 255 | 256 | ## ----------------- ## 257 | ## Semantic Values. ## 258 | ## ----------------- ## 259 | 260 | 261 | # b4_symbol_translate(STRING) 262 | # --------------------------- 263 | # Used by "bison" in the array of symbol names to mark those that 264 | # require translation. 265 | m4_define([b4_symbol_translate], 266 | [[$1]]) 267 | 268 | 269 | # b4_symbol_value(VAL, [SYMBOL-NUM], [TYPE-TAG]) 270 | # ---------------------------------------------- 271 | # See README. 272 | m4_define([b4_symbol_value], 273 | [m4_ifval([$3], 274 | [(($3)($1))], 275 | [m4_ifval([$2], 276 | [b4_symbol_if([$2], [has_type], 277 | [(($1))], 278 | [$1])], 279 | [$1])])]) 280 | 281 | 282 | # b4_lhs_value([SYMBOL-NUM], [TYPE]) 283 | # ---------------------------------- 284 | # See README. 285 | m4_define([b4_lhs_value], [$yyval]) 286 | 287 | m4_define([b4_quote_value], [[[$1]]]) 288 | 289 | 290 | # b4_rhs_data(RULE-LENGTH, POS) 291 | # ----------------------------- 292 | # See README. 293 | m4_define([b4_rhs_data], 294 | [$yystack->valueAt(b4_subtract($@))]) 295 | 296 | # b4_rhs_value(RULE-LENGTH, POS, SYMBOL-NUM, [TYPE]) 297 | # -------------------------------------------------- 298 | # See README. 299 | # 300 | # In this simple implementation, %token and %type have class names 301 | # between the angle brackets. 302 | m4_define([b4_rhs_value], 303 | [b4_symbol_value([b4_rhs_data([$1], [$2])], [$3], [$4])]) 304 | 305 | 306 | # b4_lhs_location() 307 | # ----------------- 308 | # Expansion of @$. 309 | m4_define([b4_lhs_location], 310 | [($yyloc)]) 311 | 312 | 313 | # b4_rhs_location(RULE-LENGTH, POS) 314 | # --------------------------------- 315 | # Expansion of @POS, where the current rule has RULE-LENGTH symbols 316 | # on RHS. 317 | m4_define([b4_rhs_location], 318 | [$yystack->locationAt(b4_subtract($@))]) 319 | 320 | 321 | # b4_lex_param 322 | # b4_parse_param 323 | # -------------- 324 | # If defined, b4_lex_param arrives double quoted, but below we prefer 325 | # it to be single quoted. Same for b4_parse_param. 326 | 327 | # TODO: should be in bison.m4 328 | m4_define_default([b4_lex_param], [[]]) 329 | m4_define([b4_lex_param], b4_lex_param) 330 | m4_define([b4_parse_param], b4_parse_param) 331 | 332 | # b4_lex_param_decl 333 | # ----------------- 334 | # Extra formal arguments of the constructor. 335 | m4_define([b4_lex_param_decl], 336 | [m4_ifset([b4_lex_param], 337 | [b4_remove_comma([$1], 338 | b4_param_decls(b4_lex_param))], 339 | [$1])]) 340 | 341 | m4_define([b4_param_decls], 342 | [m4_map([b4_param_decl], [$@])]) 343 | m4_define([b4_param_decl], [, $1]) 344 | 345 | m4_define([b4_remove_comma], [m4_ifval(m4_quote($1), [$1, ], [])m4_shift2($@)]) 346 | 347 | 348 | 349 | # b4_parse_param_decl 350 | # ------------------- 351 | # Extra formal arguments of the constructor. 352 | m4_define([b4_parse_param_decl], 353 | [m4_ifset([b4_parse_param], 354 | [b4_remove_comma([$1], 355 | b4_param_decls(b4_parse_param))], 356 | [$1])]) 357 | 358 | 359 | 360 | # b4_lex_param_call 361 | # ----------------- 362 | # Delegating the lexer parameters to the lexer constructor. 363 | m4_define([b4_lex_param_call], 364 | [m4_ifset([b4_lex_param], 365 | [b4_remove_comma([$1], 366 | b4_param_calls(b4_lex_param))], 367 | [$1])]) 368 | m4_define([b4_param_calls], 369 | [m4_map([b4_param_call], [$@])]) 370 | m4_define([b4_param_call], [, $$2]) 371 | 372 | 373 | 374 | # b4_parse_param_cons 375 | # ------------------- 376 | # Extra initialisations of the constructor. 377 | m4_define([b4_parse_param_cons], 378 | [m4_ifset([b4_parse_param], 379 | [b4_constructor_calls(b4_parse_param)])]) 380 | 381 | m4_define([b4_constructor_calls], 382 | [m4_map([b4_constructor_call], [$@])]) 383 | m4_define([b4_constructor_call], 384 | [$this->$2 = $$2; 385 | ]) 386 | 387 | 388 | 389 | # b4_parse_param_vars 390 | # ------------------- 391 | # Extra instance variables. 392 | m4_define([b4_parse_param_vars], 393 | [m4_ifset([b4_parse_param], 394 | [ 395 | /* User arguments. */ 396 | b4_var_decls(b4_parse_param)])]) 397 | 398 | m4_define([b4_var_decls], 399 | [m4_map_sep([b4_var_decl], [ 400 | ], [$@])]) 401 | m4_define([b4_var_decl], 402 | [ protected $1;]) 403 | --------------------------------------------------------------------------------