├── .github
└── workflows
│ └── php.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── LICENSE.md
├── README.md
├── bin
└── phpyacc
├── composer.json
├── composer.lock
├── examples
├── 00-basic-usage
│ ├── grammar.y
│ ├── parser.diff
│ ├── parser.kmyacc.php
│ ├── parser.phpyacc.php
│ ├── parser.template.php
│ ├── y.diff
│ ├── y.kmyacc.output
│ └── y.phpyacc.output
├── 01-expression-support
│ ├── grammar.y
│ ├── parser.diff
│ ├── parser.kmyacc.php
│ ├── parser.phpyacc.php
│ ├── parser.template.php
│ ├── y.diff
│ ├── y.kmyacc.output
│ └── y.phpyacc.output
├── 02-complex-expression-support
│ ├── grammar.y
│ ├── parser.diff
│ ├── parser.kmyacc.php
│ ├── parser.phpyacc.php
│ ├── parser.template.php
│ ├── y.diff
│ ├── y.kmyacc.output
│ └── y.phpyacc.output
├── 10-php7
│ ├── grammar.y
│ ├── parser.diff
│ ├── parser.kmyacc.php
│ ├── parser.phpyacc.php
│ ├── parser.template.php
│ ├── y.diff
│ ├── y.kmyacc.output
│ └── y.phpyacc.output
├── 20-custom-parser
│ ├── grammar.y
│ ├── parser.diff
│ ├── parser.kmyacc.php
│ ├── parser.phpyacc.php
│ ├── parser.template.php
│ ├── y.diff
│ ├── y.kmyacc.output
│ └── y.phpyacc.output
└── rebuild.php
└── lib
├── CodeGen
├── Language.php
├── Language
│ └── PHP.php
└── Template.php
├── Compress
├── Auxiliary.php
├── Compress.php
├── CompressResult.php
├── Preimage.php
├── TRow.php
└── functions.php
├── Exception
├── LexingException.php
├── LogicException.php
├── ParseException.php
├── PhpYaccException.php
└── TemplateException.php
├── Generator.php
├── Grammar
├── Context.php
├── State.php
└── Symbol.php
├── Lalr
├── ArrayBitset.php
├── Bitset.php
├── Conflict.php
├── Conflict
│ ├── ReduceReduce.php
│ └── ShiftReduce.php
├── Generator.php
├── Item.php
├── LalrResult.php
├── Lr1.php
├── Reduce.php
├── StringBitset.php
└── functions.php
├── Macro.php
├── Yacc
├── Lexer.php
├── LexerTest.php
├── Macro
│ └── DollarExpansion.php
├── MacroAbstract.php
├── MacroSet.php
├── Parser.php
├── ParserTest.php
├── Production.php
├── ProductionTest.php
├── Token.php
└── TokenTest.php
└── functions.php
/.github/workflows/php.yml:
--------------------------------------------------------------------------------
1 | name: PHP Composer
2 | on: [push, pull_request]
3 | jobs:
4 | build:
5 | name: Run tests on ${{ matrix.php }}
6 | runs-on: ubuntu-latest
7 |
8 | strategy:
9 | matrix:
10 | php: [ '8.1', '8.2', '8.3', '8.4' ]
11 |
12 | steps:
13 | - name: Checkout
14 | uses: actions/checkout@v4
15 |
16 | # Docs: https://github.com/shivammathur/setup-php
17 | - name: Setup PHP
18 | uses: shivammathur/setup-php@v2
19 | with:
20 | php-version: ${{ matrix.php }}
21 |
22 | - name: Setup problem matchers for PHPUnit
23 | run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
24 |
25 | - name: Install Composer dependencies
26 | run: composer install --no-progress
27 |
28 | - name: Run PHPUnit
29 | run: composer test
30 |
31 | - name: Check Formatting
32 | run: vendor/bin/php-cs-fixer fix -v --dry-run --stop-on-violation --using-cache=no ./lib
33 | if: ${{ matrix.php == '8.3' }}
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/
2 | .idea/
3 | .phpunit.result.cache
4 | .php-cs-fixer.cache
5 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at [INSERT EMAIL ADDRESS]. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Apache License
2 | ==============
3 |
4 | _Version 2.0, January 2004_
5 | _<>_
6 |
7 | ### Terms and Conditions for use, reproduction, and distribution
8 |
9 | #### 1. Definitions
10 |
11 | “License” shall mean the terms and conditions for use, reproduction, and
12 | distribution as defined by Sections 1 through 9 of this document.
13 |
14 | “Licensor” shall mean the copyright owner or entity authorized by the copyright
15 | owner that is granting the License.
16 |
17 | “Legal Entity” shall mean the union of the acting entity and all other entities
18 | that control, are controlled by, or are under common control with that entity.
19 | For the purposes of this definition, “control” means **(i)** the power, direct or
20 | indirect, to cause the direction or management of such entity, whether by
21 | contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the
22 | outstanding shares, or **(iii)** beneficial ownership of such entity.
23 |
24 | “You” (or “Your”) shall mean an individual or Legal Entity exercising
25 | permissions granted by this License.
26 |
27 | “Source” form shall mean the preferred form for making modifications, including
28 | but not limited to software source code, documentation source, and configuration
29 | files.
30 |
31 | “Object” form shall mean any form resulting from mechanical transformation or
32 | translation of a Source form, including but not limited to compiled object code,
33 | generated documentation, and conversions to other media types.
34 |
35 | “Work” shall mean the work of authorship, whether in Source or Object form, made
36 | available under the License, as indicated by a copyright notice that is included
37 | in or attached to the work (an example is provided in the Appendix below).
38 |
39 | “Derivative Works” shall mean any work, whether in Source or Object form, that
40 | is based on (or derived from) the Work and for which the editorial revisions,
41 | annotations, elaborations, or other modifications represent, as a whole, an
42 | original work of authorship. For the purposes of this License, Derivative Works
43 | shall not include works that remain separable from, or merely link (or bind by
44 | name) to the interfaces of, the Work and Derivative Works thereof.
45 |
46 | “Contribution” shall mean any work of authorship, including the original version
47 | of the Work and any modifications or additions to that Work or Derivative Works
48 | thereof, that is intentionally submitted to Licensor for inclusion in the Work
49 | by the copyright owner or by an individual or Legal Entity authorized to submit
50 | on behalf of the copyright owner. For the purposes of this definition,
51 | “submitted” means any form of electronic, verbal, or written communication sent
52 | to the Licensor or its representatives, including but not limited to
53 | communication on electronic mailing lists, source code control systems, and
54 | issue tracking systems that are managed by, or on behalf of, the Licensor for
55 | the purpose of discussing and improving the Work, but excluding communication
56 | that is conspicuously marked or otherwise designated in writing by the copyright
57 | owner as “Not a Contribution.”
58 |
59 | “Contributor” shall mean Licensor and any individual or Legal Entity on behalf
60 | of whom a Contribution has been received by Licensor and subsequently
61 | incorporated within the Work.
62 |
63 | #### 2. Grant of Copyright License
64 |
65 | Subject to the terms and conditions of this License, each Contributor hereby
66 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
67 | irrevocable copyright license to reproduce, prepare Derivative Works of,
68 | publicly display, publicly perform, sublicense, and distribute the Work and such
69 | Derivative Works in Source or Object form.
70 |
71 | #### 3. Grant of Patent License
72 |
73 | Subject to the terms and conditions of this License, each Contributor hereby
74 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
75 | irrevocable (except as stated in this section) patent license to make, have
76 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where
77 | such license applies only to those patent claims licensable by such Contributor
78 | that are necessarily infringed by their Contribution(s) alone or by combination
79 | of their Contribution(s) with the Work to which such Contribution(s) was
80 | submitted. If You institute patent litigation against any entity (including a
81 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a
82 | Contribution incorporated within the Work constitutes direct or contributory
83 | patent infringement, then any patent licenses granted to You under this License
84 | for that Work shall terminate as of the date such litigation is filed.
85 |
86 | #### 4. Redistribution
87 |
88 | You may reproduce and distribute copies of the Work or Derivative Works thereof
89 | in any medium, with or without modifications, and in Source or Object form,
90 | provided that You meet the following conditions:
91 |
92 | * **(a)** You must give any other recipients of the Work or Derivative Works a copy of
93 | this License; and
94 | * **(b)** You must cause any modified files to carry prominent notices stating that You
95 | changed the files; and
96 | * **(c)** You must retain, in the Source form of any Derivative Works that You distribute,
97 | all copyright, patent, trademark, and attribution notices from the Source form
98 | of the Work, excluding those notices that do not pertain to any part of the
99 | Derivative Works; and
100 | * **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any
101 | Derivative Works that You distribute must include a readable copy of the
102 | attribution notices contained within such NOTICE file, excluding those notices
103 | that do not pertain to any part of the Derivative Works, in at least one of the
104 | following places: within a NOTICE text file distributed as part of the
105 | Derivative Works; within the Source form or documentation, if provided along
106 | with the Derivative Works; or, within a display generated by the Derivative
107 | Works, if and wherever such third-party notices normally appear. The contents of
108 | the NOTICE file are for informational purposes only and do not modify the
109 | License. You may add Your own attribution notices within Derivative Works that
110 | You distribute, alongside or as an addendum to the NOTICE text from the Work,
111 | provided that such additional attribution notices cannot be construed as
112 | modifying the License.
113 |
114 | You may add Your own copyright statement to Your modifications and may provide
115 | additional or different license terms and conditions for use, reproduction, or
116 | distribution of Your modifications, or for any such Derivative Works as a whole,
117 | provided Your use, reproduction, and distribution of the Work otherwise complies
118 | with the conditions stated in this License.
119 |
120 | #### 5. Submission of Contributions
121 |
122 | Unless You explicitly state otherwise, any Contribution intentionally submitted
123 | for inclusion in the Work by You to the Licensor shall be under the terms and
124 | conditions of this License, without any additional terms or conditions.
125 | Notwithstanding the above, nothing herein shall supersede or modify the terms of
126 | any separate license agreement you may have executed with Licensor regarding
127 | such Contributions.
128 |
129 | #### 6. Trademarks
130 |
131 | This License does not grant permission to use the trade names, trademarks,
132 | service marks, or product names of the Licensor, except as required for
133 | reasonable and customary use in describing the origin of the Work and
134 | reproducing the content of the NOTICE file.
135 |
136 | #### 7. Disclaimer of Warranty
137 |
138 | Unless required by applicable law or agreed to in writing, Licensor provides the
139 | Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,
140 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
141 | including, without limitation, any warranties or conditions of TITLE,
142 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
143 | solely responsible for determining the appropriateness of using or
144 | redistributing the Work and assume any risks associated with Your exercise of
145 | permissions under this License.
146 |
147 | #### 8. Limitation of Liability
148 |
149 | In no event and under no legal theory, whether in tort (including negligence),
150 | contract, or otherwise, unless required by applicable law (such as deliberate
151 | and grossly negligent acts) or agreed to in writing, shall any Contributor be
152 | liable to You for damages, including any direct, indirect, special, incidental,
153 | or consequential damages of any character arising as a result of this License or
154 | out of the use or inability to use the Work (including but not limited to
155 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or
156 | any and all other commercial damages or losses), even if such Contributor has
157 | been advised of the possibility of such damages.
158 |
159 | #### 9. Accepting Warranty or Additional Liability
160 |
161 | While redistributing the Work or Derivative Works thereof, You may choose to
162 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or
163 | other liability obligations and/or rights consistent with this License. However,
164 | in accepting such obligations, You may act only on Your own behalf and on Your
165 | sole responsibility, not on behalf of any other Contributor, and only if You
166 | agree to indemnify, defend, and hold each Contributor harmless for any liability
167 | incurred by, or claims asserted against, such Contributor by reason of your
168 | accepting any such warranty or additional liability.
169 |
170 | _END OF TERMS AND CONDITIONS_
171 |
172 | ### APPENDIX: How to apply the Apache License to your work
173 |
174 | To apply the Apache License to your work, attach the following boilerplate
175 | notice, with the fields enclosed by brackets `[]` replaced with your own
176 | identifying information. (Don't include the brackets!) The text should be
177 | enclosed in the appropriate comment syntax for the file format. We also
178 | recommend that a file or class name and description of purpose be included on
179 | the same “printed page” as the copyright notice for easier identification within
180 | third-party archives.
181 |
182 | Copyright [2017] [Anthony Ferrara]
183 |
184 | Licensed under the Apache License, Version 2.0 (the "License");
185 | you may not use this file except in compliance with the License.
186 | You may obtain a copy of the License at
187 |
188 | http://www.apache.org/licenses/LICENSE-2.0
189 |
190 | Unless required by applicable law or agreed to in writing, software
191 | distributed under the License is distributed on an "AS IS" BASIS,
192 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
193 | See the License for the specific language governing permissions and
194 | limitations under the License.
195 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PHP-Yacc
2 |
3 | This is a port of [`kmyacc`](https://github.com/moriyoshi/kmyacc-forked) into PHP. It is a parser-generator, meaning it takes a YACC grammar file and generates a parser file.
4 |
5 | ## A Direct Port (For Now)
6 |
7 | Right now, this is a direct port. Meaning that it works exactly like `kmyacc`. Looking in the examples, you can see that this means that you must supply a "parser template" in addition to the grammar.
8 |
9 | Longer term, we want to add simplifying functionality. We will always support providing a template, but we will offer a series of default templates for common use-cases.
10 |
11 | ## What can I do with this?
12 |
13 | You can parse most structured and unstructured grammars. There are some gotchas to [LALR(1) parsers](https://en.wikipedia.org/wiki/LALR_parser) that you need to be aware of (for example, Shift/Shift conflicts and Shift/Reduce conflicts). But those are beyond this simple intro.
14 |
15 | ## How does it work?
16 |
17 | I don't know. I just ported the code until it worked correctly.
18 |
19 | ## YACC Grammar
20 |
21 | That's way beyond the scope of this documentation, but checkout [The YACC page here](http://dinosaur.compilertools.net/yacc/) for some info.
22 |
23 | Over time we will document the grammar more...
24 |
25 | ## How do I use it?
26 |
27 | For now, check out the examples folder. The current state of the CLI tool will change, so any usage today should please provide feedback and use-cases so that we can better design the tooling support.
28 |
29 | ## Why did you do this?
30 |
31 | Many projects have the need for parsers (and therefore parser-generators). Nikita's [PHP-Parser](https://github.com/nikic/PHP-Parser) is one tool that uses kmyacc to generate its parser. There are many other projects out there that either use hand-written parsers, or use kmyacc or another parser-generator.
32 |
33 | Unfortunately, not many parser-generators exist for PHP. And those that do exist I have found to be rigid or not powerful enough to parse PHP itself.
34 |
35 | This project is an aim to resolve that.
36 |
37 | ## Performance
38 |
39 | There's a TON of performance optimizations possible here. The original code was a direct port, so some structures are definitely sub-optimal. Over time we will improve the performance.
40 |
41 | However, this will always be at least a slightly-slow process. Generating a parser requires a lot of resources, so should never happen inside of a web request.
42 |
43 | Using the generated parser however should be quite fast (the generated parser is fairly well optimized already).
44 |
45 | ## What's left to do?
46 |
47 | A bunch of things. Here's the wishlist:
48 |
49 | * Refactor to make conventions consistent (some parts currently use camelCase, some parts use snake_case, etc).
50 | * Performance tuning
51 | * Unit test as much as possible
52 | * Document as much as possible (It's a complicated series of algorithms with no source documentation in either project).
53 | * Redesign the CLI binary and how it operates
54 | * Decide whether multi-language support is worth while, or if we should just move to only PHP codegen support.
55 | * Add default templates and parser implementations
56 | * At least one of which generates an "AST" by default, similar to Ruby's [Treetop library](https://github.com/nathansobo/treetop)
57 | * Build a reasonably performant lexer-generator (very likely as a separate project)
58 | * A lot of debugging (though we don't know of any bugs, they are there)
59 | * Building out of features we didn't need for the initial go (for example, support for `%union`, etc).
60 |
61 | And a lot more.
62 |
63 | ## Contributing
64 |
65 |
--------------------------------------------------------------------------------
/bin/phpyacc:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | pspref = $argv[++$i];
57 | break;
58 | case '-x':
59 | $context->verboseDebug = true;
60 | case '-v':
61 | $context->debugFile = fopen(getcwd() . "/y.output", 'w' );
62 | break;
63 | case '-t':
64 | $context->tflag = true;
65 | break;
66 | case '-a':
67 | $context->aflag = true;
68 | break;
69 | case '-n':
70 | $context->allowSemanticValueReferenceByName = true;
71 | break;
72 | case '-m':
73 | if ($i === $argc - 2) {
74 | error("Skeleton file required for option -m");
75 | }
76 | $skeleton = file_get_contents($argv[++$i]);
77 | break;
78 | default:
79 | error("Unexpected argument/flag {$argv[$i]}");
80 |
81 | }
82 | }
83 |
84 | (new Generator)->generate($context, file_get_contents($grammarFile), $skeleton, $resultFile);
85 |
86 |
87 | function help()
88 | {
89 | echo << The name of the class to generate
96 | -x Enable extended debug mode
97 | -v Generate y.output file
98 | -t Set the T flag for templates (inclusion of debug information)
99 | -a Set the A flag for templates (unused)
100 | -m Path to the skeleton file to use
101 |
102 |
103 | EOH;
104 | }
105 |
106 | function error(string $message)
107 | {
108 | echo $message . "\n";
109 | help();
110 | exit(2);
111 | }
112 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ircmaxell/php-yacc",
3 | "description": "A PHP YACC Parser-Generator library",
4 | "license": "Apache-2.0",
5 | "authors": [
6 | {
7 | "name": "Anthony Ferrara",
8 | "email": "ircmaxell@php.net"
9 | }
10 | ],
11 | "autoload": {
12 | "psr-4": {
13 | "PhpYacc\\": "lib/"
14 | },
15 | "files": [
16 | "lib/functions.php"
17 | ]
18 | },
19 | "bin": ["bin/phpyacc"],
20 | "require": {
21 | "php": ">=8.1"
22 | },
23 | "require-dev": {
24 | "friendsofphp/php-cs-fixer": "^3.65",
25 | "phpstan/phpstan": "^2.0",
26 | "phpunit/phpunit": "^10.5"
27 | },
28 | "scripts": {
29 | "analyze": "phpstan analyze lib",
30 | "build": [
31 | "@cs-fix",
32 | "@build-examples"
33 | ],
34 | "build-examples": "php examples/rebuild.php",
35 | "cs-fix": "php-cs-fixer fix -v ./lib",
36 | "test": "phpunit lib"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/examples/00-basic-usage/grammar.y:
--------------------------------------------------------------------------------
1 |
2 | %%
3 |
4 | expr:
5 | '1' { $$ = 1; }
6 | ;
7 |
8 | %%
9 |
--------------------------------------------------------------------------------
/examples/00-basic-usage/parser.diff:
--------------------------------------------------------------------------------
1 | 103c103
2 | < "start : expr",
3 | ---
4 | > "\$start : expr",
5 |
--------------------------------------------------------------------------------
/examples/00-basic-usage/parser.kmyacc.php:
--------------------------------------------------------------------------------
1 | reduceCallbacks = [
109 | 0 => function ($stackPos) {
110 | $this->semValue = $this->semStack[$stackPos];
111 | },
112 | 1 => function ($stackPos) {
113 | $this->semValue = 1;
114 | },
115 | ];
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/examples/00-basic-usage/parser.phpyacc.php:
--------------------------------------------------------------------------------
1 | reduceCallbacks = [
109 | 0 => function ($stackPos) {
110 | $this->semValue = $this->semStack[$stackPos];
111 | },
112 | 1 => function ($stackPos) {
113 | $this->semValue = 1;
114 | },
115 | ];
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/examples/00-basic-usage/parser.template.php:
--------------------------------------------------------------------------------
1 | semValue
4 | #semval($,%t) $this->semValue
5 | #semval(%n) $stackPos-(%l-%n)
6 | #semval(%n,%t) $stackPos-(%l-%n)
7 |
8 | namespace PhpParser\Parser;
9 |
10 | use PhpParser\Error;
11 | use PhpParser\Node;
12 | use PhpParser\Node\Expr;
13 | use PhpParser\Node\Name;
14 | use PhpParser\Node\Scalar;
15 | use PhpParser\Node\Stmt;
16 | #include;
17 |
18 | /**
19 | * This is an automatically GENERATED file, which should not be manually edited.
20 | */
21 | class Parser extends \PhpParser\ParserAbstract
22 | {
23 | protected $tokenToSymbolMapSize = #(YYMAXLEX);
24 | protected $actionTableSize = #(YYLAST);
25 | protected $gotoTableSize = #(YYGLAST);
26 |
27 | protected $invalidSymbol = #(YYBADCH);
28 | protected $errorSymbol = #(YYINTERRTOK);
29 | protected $defaultAction = #(YYDEFAULT);
30 | protected $unexpectedTokenRule = #(YYUNEXPECTED);
31 |
32 | protected $YY2TBLSTATE = #(YY2TBLSTATE);
33 | protected $YYNLSTATES = #(YYNLSTATES);
34 |
35 | protected $symbolToName = array(
36 | #listvar terminals
37 | );
38 |
39 | protected $tokenToSymbol = array(
40 | #listvar yytranslate
41 | );
42 |
43 | protected $action = array(
44 | #listvar yyaction
45 | );
46 |
47 | protected $actionCheck = array(
48 | #listvar yycheck
49 | );
50 |
51 | protected $actionBase = array(
52 | #listvar yybase
53 | );
54 |
55 | protected $actionDefault = array(
56 | #listvar yydefault
57 | );
58 |
59 | protected $goto = array(
60 | #listvar yygoto
61 | );
62 |
63 | protected $gotoCheck = array(
64 | #listvar yygcheck
65 | );
66 |
67 | protected $gotoBase = array(
68 | #listvar yygbase
69 | );
70 |
71 | protected $gotoDefault = array(
72 | #listvar yygdefault
73 | );
74 |
75 | protected $ruleToNonTerminal = array(
76 | #listvar yylhs
77 | );
78 |
79 | protected $ruleToLength = array(
80 | #listvar yylen
81 | );
82 | #if -t
83 |
84 | protected $productions = array(
85 | #production-strings;
86 | );
87 | #endif
88 |
89 | protected function initReduceCallbacks() {
90 | $this->reduceCallbacks = [
91 | #reduce
92 | %n => function ($stackPos) {
93 | %b
94 | },
95 | #noact
96 | %n => function ($stackPos) {
97 | $this->semValue = $this->semStack[$stackPos];
98 | },
99 | #endreduce
100 | ];
101 | }
102 | }
103 | #tailcode;
--------------------------------------------------------------------------------
/examples/00-basic-usage/y.diff:
--------------------------------------------------------------------------------
1 | 3c3
2 | < start [ '1' ]
3 | ---
4 | > $start [ '1' ]
5 | 6c6
6 | < (0) start : . expr
7 | ---
8 | > (0) $start : . expr
9 | 12c12
10 | < (0) start : expr .
11 | ---
12 | > (0) $start : expr .
13 | 15c15
14 | < (0) start : . expr
15 | ---
16 | > (0) $start : . expr
17 | 22c22
18 | < (0) start : expr .
19 | ---
20 | > (0) $start : expr .
21 | 39d38
22 | < 3 items
23 | 42,43d40
24 | < 1304 bytes used
25 | <
26 |
--------------------------------------------------------------------------------
/examples/00-basic-usage/y.kmyacc.output:
--------------------------------------------------------------------------------
1 | EMPTY nonterminals:
2 | First:
3 | start [ '1' ]
4 | expr [ '1' ]
5 | state unknown:
6 | (0) start : . expr
7 | [ EOF ]
8 | state unknown:
9 | (1) expr : '1' .
10 | [ EOF ]
11 | state unknown:
12 | (0) start : expr .
13 | [ EOF ]
14 | state 0
15 | (0) start : . expr
16 |
17 | '1' shift 2 and reduce (1)
18 | expr goto 1
19 | . error
20 |
21 | state 1
22 | (0) start : expr .
23 |
24 | EOF accept
25 | . error
26 |
27 | state 2
28 | (1) expr : '1' .
29 |
30 | . reduce (1)
31 |
32 |
33 | Statistics for grammar.y:
34 | 3 terminal symbols
35 | 2 nonterminal symbols
36 | 2 productions
37 | 3 states
38 | 0 shift/reduce, 0 reduce/reduce conflicts
39 | 3 items
40 | 3 lookahead sets used
41 | 2+4=6 action entries
42 | 1304 bytes used
43 |
44 | State=>class:
45 |
46 | 0=>0 1=>1
47 |
48 | Terminal action:
49 | T\S 0 1
50 | EOF . 0
51 | '1' 2 .
52 |
53 | Nonterminal GOTO table:
54 | T\S 0 1
55 | expr 1 .
56 |
57 | Nonterminal GOTO table:
58 | T\S default 0 1
59 | expr 1 = .
60 |
61 | Candidates of aux table:
62 | Used aux table:
63 | state 0 (class 0)
64 | state 1 (class 1)
65 | Order:
66 | 1,0,
67 | Order:
68 | 0,1,
69 |
--------------------------------------------------------------------------------
/examples/00-basic-usage/y.phpyacc.output:
--------------------------------------------------------------------------------
1 | EMPTY nonterminals:
2 | First:
3 | $start [ '1' ]
4 | expr [ '1' ]
5 | state unknown:
6 | (0) $start : . expr
7 | [ EOF ]
8 | state unknown:
9 | (1) expr : '1' .
10 | [ EOF ]
11 | state unknown:
12 | (0) $start : expr .
13 | [ EOF ]
14 | state 0
15 | (0) $start : . expr
16 |
17 | '1' shift 2 and reduce (1)
18 | expr goto 1
19 | . error
20 |
21 | state 1
22 | (0) $start : expr .
23 |
24 | EOF accept
25 | . error
26 |
27 | state 2
28 | (1) expr : '1' .
29 |
30 | . reduce (1)
31 |
32 |
33 | Statistics for grammar.y:
34 | 3 terminal symbols
35 | 2 nonterminal symbols
36 | 2 productions
37 | 3 states
38 | 0 shift/reduce, 0 reduce/reduce conflicts
39 | 3 lookahead sets used
40 | 2+4=6 action entries
41 | State=>class:
42 |
43 | 0=>0 1=>1
44 |
45 | Terminal action:
46 | T\S 0 1
47 | EOF . 0
48 | '1' 2 .
49 |
50 | Nonterminal GOTO table:
51 | T\S 0 1
52 | expr 1 .
53 |
54 | Nonterminal GOTO table:
55 | T\S default 0 1
56 | expr 1 = .
57 |
58 | Candidates of aux table:
59 | Used aux table:
60 | state 0 (class 0)
61 | state 1 (class 1)
62 | Order:
63 | 1,0,
64 | Order:
65 | 0,1,
66 |
--------------------------------------------------------------------------------
/examples/01-expression-support/grammar.y:
--------------------------------------------------------------------------------
1 |
2 | %left '+'
3 |
4 | %%
5 |
6 | expr:
7 | expr '+' expr { $$ = $1 + $3; }
8 | | '1' { $$ = 1; }
9 | ;
10 |
11 | %%
12 |
--------------------------------------------------------------------------------
/examples/01-expression-support/parser.diff:
--------------------------------------------------------------------------------
1 | 105c105
2 | < "start : expr",
3 | ---
4 | > "\$start : expr",
5 |
--------------------------------------------------------------------------------
/examples/01-expression-support/parser.kmyacc.php:
--------------------------------------------------------------------------------
1 | reduceCallbacks = [
112 | 0 => function ($stackPos) {
113 | $this->semValue = $this->semStack[$stackPos];
114 | },
115 | 1 => function ($stackPos) {
116 | $this->semValue = $stackPos-(3-1) + $stackPos-(3-3);
117 | },
118 | 2 => function ($stackPos) {
119 | $this->semValue = 1;
120 | },
121 | ];
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/examples/01-expression-support/parser.phpyacc.php:
--------------------------------------------------------------------------------
1 | reduceCallbacks = [
112 | 0 => function ($stackPos) {
113 | $this->semValue = $this->semStack[$stackPos];
114 | },
115 | 1 => function ($stackPos) {
116 | $this->semValue = $stackPos-(3-1) + $stackPos-(3-3);
117 | },
118 | 2 => function ($stackPos) {
119 | $this->semValue = 1;
120 | },
121 | ];
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/examples/01-expression-support/parser.template.php:
--------------------------------------------------------------------------------
1 | semValue
4 | #semval($,%t) $this->semValue
5 | #semval(%n) $stackPos-(%l-%n)
6 | #semval(%n,%t) $stackPos-(%l-%n)
7 |
8 | namespace PhpParser\Parser;
9 |
10 | use PhpParser\Error;
11 | use PhpParser\Node;
12 | use PhpParser\Node\Expr;
13 | use PhpParser\Node\Name;
14 | use PhpParser\Node\Scalar;
15 | use PhpParser\Node\Stmt;
16 | #include;
17 |
18 | /* This is an automatically GENERATED file, which should not be manually edited.
19 | */
20 | class Parser extends \PhpParser\ParserAbstract
21 | {
22 | protected $tokenToSymbolMapSize = #(YYMAXLEX);
23 | protected $actionTableSize = #(YYLAST);
24 | protected $gotoTableSize = #(YYGLAST);
25 |
26 | protected $invalidSymbol = #(YYBADCH);
27 | protected $errorSymbol = #(YYINTERRTOK);
28 | protected $defaultAction = #(YYDEFAULT);
29 | protected $unexpectedTokenRule = #(YYUNEXPECTED);
30 |
31 | protected $YY2TBLSTATE = #(YY2TBLSTATE);
32 | protected $YYNLSTATES = #(YYNLSTATES);
33 |
34 | protected $symbolToName = array(
35 | #listvar terminals
36 | );
37 |
38 | protected $tokenToSymbol = array(
39 | #listvar yytranslate
40 | );
41 |
42 | protected $action = array(
43 | #listvar yyaction
44 | );
45 |
46 | protected $actionCheck = array(
47 | #listvar yycheck
48 | );
49 |
50 | protected $actionBase = array(
51 | #listvar yybase
52 | );
53 |
54 | protected $actionDefault = array(
55 | #listvar yydefault
56 | );
57 |
58 | protected $goto = array(
59 | #listvar yygoto
60 | );
61 |
62 | protected $gotoCheck = array(
63 | #listvar yygcheck
64 | );
65 |
66 | protected $gotoBase = array(
67 | #listvar yygbase
68 | );
69 |
70 | protected $gotoDefault = array(
71 | #listvar yygdefault
72 | );
73 |
74 | protected $ruleToNonTerminal = array(
75 | #listvar yylhs
76 | );
77 |
78 | protected $ruleToLength = array(
79 | #listvar yylen
80 | );
81 | #if -t
82 |
83 | protected $productions = array(
84 | #production-strings;
85 | );
86 | #endif
87 |
88 | protected function initReduceCallbacks() {
89 | $this->reduceCallbacks = [
90 | #reduce
91 | %n => function ($stackPos) {
92 | %b
93 | },
94 | #noact
95 | %n => function ($stackPos) {
96 | $this->semValue = $this->semStack[$stackPos];
97 | },
98 | #endreduce
99 | ];
100 | }
101 | }
102 | #tailcode;
--------------------------------------------------------------------------------
/examples/01-expression-support/y.diff:
--------------------------------------------------------------------------------
1 | 3c3
2 | < start [ '1' ]
3 | ---
4 | > $start [ '1' ]
5 | 6c6
6 | < (0) start : . expr
7 | ---
8 | > (0) $start : . expr
9 | 12c12
10 | < (0) start : expr .
11 | ---
12 | > (0) $start : expr .
13 | 25c25
14 | < (0) start : . expr
15 | ---
16 | > (0) $start : . expr
17 | 32c32
18 | < (0) start : expr .
19 | ---
20 | > (0) $start : expr .
21 | 64d63
22 | < 8 items
23 | 67,68d65
24 | < 1920 bytes used
25 | <
26 |
--------------------------------------------------------------------------------
/examples/01-expression-support/y.kmyacc.output:
--------------------------------------------------------------------------------
1 | EMPTY nonterminals:
2 | First:
3 | start [ '1' ]
4 | expr [ '1' ]
5 | state unknown:
6 | (0) start : . expr
7 | [ EOF ]
8 | state unknown:
9 | (2) expr : '1' .
10 | [ EOF '+' ]
11 | state unknown:
12 | (0) start : expr .
13 | [ EOF ]
14 | (1) expr : expr . '+' expr
15 | [ EOF '+' ]
16 | state unknown:
17 | (1) expr : expr '+' . expr
18 | [ EOF '+' ]
19 | state unknown:
20 | (1) expr : expr . '+' expr
21 | [ EOF '+' ]
22 | (1) expr : expr '+' expr .
23 | [ EOF '+' ]
24 | state 0
25 | (0) start : . expr
26 |
27 | '1' shift 3 and reduce (2)
28 | expr goto 1
29 | . error
30 |
31 | state 1
32 | (0) start : expr .
33 | (1) expr : expr . '+' expr
34 |
35 | EOF accept
36 | '+' shift 2
37 | . error
38 |
39 | state 2
40 | (1) expr : expr '+' . expr
41 |
42 | '1' shift 3 and reduce (2)
43 | expr goto 4 and reduce (1)
44 | . error
45 |
46 | state 3
47 | (2) expr : '1' .
48 |
49 | . reduce (2)
50 |
51 | state 4
52 | (1) expr : expr . '+' expr
53 | (1) expr : expr '+' expr .
54 |
55 | . reduce (1)
56 |
57 |
58 | Statistics for grammar.y:
59 | 4 terminal symbols
60 | 2 nonterminal symbols
61 | 3 productions
62 | 5 states
63 | 0 shift/reduce, 0 reduce/reduce conflicts
64 | 8 items
65 | 7 lookahead sets used
66 | 6+6=12 action entries
67 | 1920 bytes used
68 |
69 | State=>class:
70 |
71 | 0=>0 1=>1 2=>0
72 |
73 | Terminal action:
74 | T\S 0 1
75 | EOF . 0
76 | '+' . 2
77 | '1' 3 .
78 |
79 | Nonterminal GOTO table:
80 | T\S 0 1 2
81 | expr 1 . 4
82 |
83 | Nonterminal GOTO table:
84 | T\S default 0 1 2
85 | expr 1 = . 4
86 |
87 | Candidates of aux table:
88 | Used aux table:
89 | state 0 (class 0)
90 | state 1 (class 1)
91 | state 2 (class 0)
92 | Order:
93 | 1,0,
94 | Order:
95 | 1,0,
96 |
--------------------------------------------------------------------------------
/examples/01-expression-support/y.phpyacc.output:
--------------------------------------------------------------------------------
1 | EMPTY nonterminals:
2 | First:
3 | $start [ '1' ]
4 | expr [ '1' ]
5 | state unknown:
6 | (0) $start : . expr
7 | [ EOF ]
8 | state unknown:
9 | (2) expr : '1' .
10 | [ EOF '+' ]
11 | state unknown:
12 | (0) $start : expr .
13 | [ EOF ]
14 | (1) expr : expr . '+' expr
15 | [ EOF '+' ]
16 | state unknown:
17 | (1) expr : expr '+' . expr
18 | [ EOF '+' ]
19 | state unknown:
20 | (1) expr : expr . '+' expr
21 | [ EOF '+' ]
22 | (1) expr : expr '+' expr .
23 | [ EOF '+' ]
24 | state 0
25 | (0) $start : . expr
26 |
27 | '1' shift 3 and reduce (2)
28 | expr goto 1
29 | . error
30 |
31 | state 1
32 | (0) $start : expr .
33 | (1) expr : expr . '+' expr
34 |
35 | EOF accept
36 | '+' shift 2
37 | . error
38 |
39 | state 2
40 | (1) expr : expr '+' . expr
41 |
42 | '1' shift 3 and reduce (2)
43 | expr goto 4 and reduce (1)
44 | . error
45 |
46 | state 3
47 | (2) expr : '1' .
48 |
49 | . reduce (2)
50 |
51 | state 4
52 | (1) expr : expr . '+' expr
53 | (1) expr : expr '+' expr .
54 |
55 | . reduce (1)
56 |
57 |
58 | Statistics for grammar.y:
59 | 4 terminal symbols
60 | 2 nonterminal symbols
61 | 3 productions
62 | 5 states
63 | 0 shift/reduce, 0 reduce/reduce conflicts
64 | 7 lookahead sets used
65 | 6+6=12 action entries
66 | State=>class:
67 |
68 | 0=>0 1=>1 2=>0
69 |
70 | Terminal action:
71 | T\S 0 1
72 | EOF . 0
73 | '+' . 2
74 | '1' 3 .
75 |
76 | Nonterminal GOTO table:
77 | T\S 0 1 2
78 | expr 1 . 4
79 |
80 | Nonterminal GOTO table:
81 | T\S default 0 1 2
82 | expr 1 = . 4
83 |
84 | Candidates of aux table:
85 | Used aux table:
86 | state 0 (class 0)
87 | state 1 (class 1)
88 | state 2 (class 0)
89 | Order:
90 | 1,0,
91 | Order:
92 | 1,0,
93 |
--------------------------------------------------------------------------------
/examples/02-complex-expression-support/grammar.y:
--------------------------------------------------------------------------------
1 |
2 | %left '+' T_FOO
3 |
4 | %%
5 |
6 | expr:
7 | expr '+' expr { $$ = $1 + $3; }
8 | | '1' { $$ = 1; }
9 | | T_FOO expr { $$ = $2; }
10 | ;
11 |
12 | %%
13 |
--------------------------------------------------------------------------------
/examples/02-complex-expression-support/parser.diff:
--------------------------------------------------------------------------------
1 | 106c106
2 | < "start : expr",
3 | ---
4 | > "\$start : expr",
5 |
--------------------------------------------------------------------------------
/examples/02-complex-expression-support/parser.kmyacc.php:
--------------------------------------------------------------------------------
1 | reduceCallbacks = [
114 | 0 => function ($stackPos) {
115 | $this->semValue = $this->semStack[$stackPos];
116 | },
117 | 1 => function ($stackPos) {
118 | $this->semValue = $stackPos-(3-1) + $stackPos-(3-3);
119 | },
120 | 2 => function ($stackPos) {
121 | $this->semValue = 1;
122 | },
123 | 3 => function ($stackPos) {
124 | $this->semValue = $stackPos-(2-2);
125 | },
126 | ];
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/examples/02-complex-expression-support/parser.phpyacc.php:
--------------------------------------------------------------------------------
1 | reduceCallbacks = [
114 | 0 => function ($stackPos) {
115 | $this->semValue = $this->semStack[$stackPos];
116 | },
117 | 1 => function ($stackPos) {
118 | $this->semValue = $stackPos-(3-1) + $stackPos-(3-3);
119 | },
120 | 2 => function ($stackPos) {
121 | $this->semValue = 1;
122 | },
123 | 3 => function ($stackPos) {
124 | $this->semValue = $stackPos-(2-2);
125 | },
126 | ];
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/examples/02-complex-expression-support/parser.template.php:
--------------------------------------------------------------------------------
1 | semValue
4 | #semval($,%t) $this->semValue
5 | #semval(%n) $stackPos-(%l-%n)
6 | #semval(%n,%t) $stackPos-(%l-%n)
7 |
8 | namespace PhpParser\Parser;
9 |
10 | use PhpParser\Error;
11 | use PhpParser\Node;
12 | use PhpParser\Node\Expr;
13 | use PhpParser\Node\Name;
14 | use PhpParser\Node\Scalar;
15 | use PhpParser\Node\Stmt;
16 | #include;
17 |
18 | /* This is an automatically GENERATED file, which should not be manually edited.
19 | */
20 | class Parser extends \PhpParser\ParserAbstract
21 | {
22 | protected $tokenToSymbolMapSize = #(YYMAXLEX);
23 | protected $actionTableSize = #(YYLAST);
24 | protected $gotoTableSize = #(YYGLAST);
25 |
26 | protected $invalidSymbol = #(YYBADCH);
27 | protected $errorSymbol = #(YYINTERRTOK);
28 | protected $defaultAction = #(YYDEFAULT);
29 | protected $unexpectedTokenRule = #(YYUNEXPECTED);
30 |
31 | protected $YY2TBLSTATE = #(YY2TBLSTATE);
32 | protected $YYNLSTATES = #(YYNLSTATES);
33 |
34 | protected $symbolToName = array(
35 | #listvar terminals
36 | );
37 |
38 | protected $tokenToSymbol = array(
39 | #listvar yytranslate
40 | );
41 |
42 | protected $action = array(
43 | #listvar yyaction
44 | );
45 |
46 | protected $actionCheck = array(
47 | #listvar yycheck
48 | );
49 |
50 | protected $actionBase = array(
51 | #listvar yybase
52 | );
53 |
54 | protected $actionDefault = array(
55 | #listvar yydefault
56 | );
57 |
58 | protected $goto = array(
59 | #listvar yygoto
60 | );
61 |
62 | protected $gotoCheck = array(
63 | #listvar yygcheck
64 | );
65 |
66 | protected $gotoBase = array(
67 | #listvar yygbase
68 | );
69 |
70 | protected $gotoDefault = array(
71 | #listvar yygdefault
72 | );
73 |
74 | protected $ruleToNonTerminal = array(
75 | #listvar yylhs
76 | );
77 |
78 | protected $ruleToLength = array(
79 | #listvar yylen
80 | );
81 | #if -t
82 |
83 | protected $productions = array(
84 | #production-strings;
85 | );
86 | #endif
87 |
88 | protected function initReduceCallbacks() {
89 | $this->reduceCallbacks = [
90 | #reduce
91 | %n => function ($stackPos) {
92 | %b
93 | },
94 | #noact
95 | %n => function ($stackPos) {
96 | $this->semValue = $this->semStack[$stackPos];
97 | },
98 | #endreduce
99 | ];
100 | }
101 | }
102 | #tailcode;
--------------------------------------------------------------------------------
/examples/02-complex-expression-support/y.diff:
--------------------------------------------------------------------------------
1 | 3c3
2 | < start [ T_FOO '1' ]
3 | ---
4 | > $start [ T_FOO '1' ]
5 | 6c6
6 | < (0) start : . expr
7 | ---
8 | > (0) $start : . expr
9 | 15c15
10 | < (0) start : expr .
11 | ---
12 | > (0) $start : expr .
13 | 33c33
14 | < (0) start : . expr
15 | ---
16 | > (0) $start : . expr
17 | 57c57
18 | < (0) start : expr .
19 | ---
20 | > (0) $start : expr .
21 | 88d87
22 | < 12 items
23 | 91,92d89
24 | < 2432 bytes used
25 | <
26 |
--------------------------------------------------------------------------------
/examples/02-complex-expression-support/y.kmyacc.output:
--------------------------------------------------------------------------------
1 | EMPTY nonterminals:
2 | First:
3 | start [ T_FOO '1' ]
4 | expr [ T_FOO '1' ]
5 | state unknown:
6 | (0) start : . expr
7 | [ EOF ]
8 | state unknown:
9 | (3) expr : T_FOO . expr
10 | [ EOF '+' ]
11 | state unknown:
12 | (2) expr : '1' .
13 | [ EOF '+' ]
14 | state unknown:
15 | (0) start : expr .
16 | [ EOF ]
17 | (1) expr : expr . '+' expr
18 | [ EOF '+' ]
19 | state unknown:
20 | (1) expr : expr . '+' expr
21 | [ EOF '+' ]
22 | (3) expr : T_FOO expr .
23 | [ EOF '+' ]
24 | state unknown:
25 | (1) expr : expr '+' . expr
26 | [ EOF '+' ]
27 | state unknown:
28 | (1) expr : expr . '+' expr
29 | [ EOF '+' ]
30 | (1) expr : expr '+' expr .
31 | [ EOF '+' ]
32 | state 0
33 | (0) start : . expr
34 |
35 | T_FOO shift 1
36 | '1' shift 4 and reduce (2)
37 | expr goto 3
38 | . error
39 |
40 | state 1
41 | (3) expr : T_FOO . expr
42 |
43 | T_FOO shift 1
44 | '1' shift 4 and reduce (2)
45 | expr goto 5 and reduce (3)
46 | . error
47 |
48 | state 2
49 | (1) expr : expr '+' . expr
50 |
51 | T_FOO shift 1
52 | '1' shift 4 and reduce (2)
53 | expr goto 6 and reduce (1)
54 | . error
55 |
56 | state 3
57 | (0) start : expr .
58 | (1) expr : expr . '+' expr
59 |
60 | EOF accept
61 | '+' shift 2
62 | . error
63 |
64 | state 4
65 | (2) expr : '1' .
66 |
67 | . reduce (2)
68 |
69 | state 5
70 | (1) expr : expr . '+' expr
71 | (3) expr : T_FOO expr .
72 |
73 | . reduce (3)
74 |
75 | state 6
76 | (1) expr : expr . '+' expr
77 | (1) expr : expr '+' expr .
78 |
79 | . reduce (1)
80 |
81 |
82 | Statistics for grammar.y:
83 | 5 terminal symbols
84 | 2 nonterminal symbols
85 | 4 productions
86 | 7 states
87 | 0 shift/reduce, 0 reduce/reduce conflicts
88 | 12 items
89 | 10 lookahead sets used
90 | 12+8=20 action entries
91 | 2432 bytes used
92 |
93 | State=>class:
94 |
95 | 0=>0 1=>0 2=>0 3=>1
96 |
97 | Terminal action:
98 | T\S 0 1
99 | EOF . 0
100 | '+' . 2
101 | T_FOO 1 .
102 | '1' 4 .
103 |
104 | Nonterminal GOTO table:
105 | T\S 0 1 2 3
106 | expr 3 5 6 .
107 |
108 | Nonterminal GOTO table:
109 | T\S default 0 1 2 3
110 | expr 3 = 5 6 .
111 |
112 | Candidates of aux table:
113 | Used aux table:
114 | state 0 (class 0)
115 | state 1 (class 0)
116 | state 2 (class 0)
117 | state 3 (class 1)
118 | Order:
119 | 1,0,
120 | Order:
121 | 1,0,
122 |
--------------------------------------------------------------------------------
/examples/02-complex-expression-support/y.phpyacc.output:
--------------------------------------------------------------------------------
1 | EMPTY nonterminals:
2 | First:
3 | $start [ T_FOO '1' ]
4 | expr [ T_FOO '1' ]
5 | state unknown:
6 | (0) $start : . expr
7 | [ EOF ]
8 | state unknown:
9 | (3) expr : T_FOO . expr
10 | [ EOF '+' ]
11 | state unknown:
12 | (2) expr : '1' .
13 | [ EOF '+' ]
14 | state unknown:
15 | (0) $start : expr .
16 | [ EOF ]
17 | (1) expr : expr . '+' expr
18 | [ EOF '+' ]
19 | state unknown:
20 | (1) expr : expr . '+' expr
21 | [ EOF '+' ]
22 | (3) expr : T_FOO expr .
23 | [ EOF '+' ]
24 | state unknown:
25 | (1) expr : expr '+' . expr
26 | [ EOF '+' ]
27 | state unknown:
28 | (1) expr : expr . '+' expr
29 | [ EOF '+' ]
30 | (1) expr : expr '+' expr .
31 | [ EOF '+' ]
32 | state 0
33 | (0) $start : . expr
34 |
35 | T_FOO shift 1
36 | '1' shift 4 and reduce (2)
37 | expr goto 3
38 | . error
39 |
40 | state 1
41 | (3) expr : T_FOO . expr
42 |
43 | T_FOO shift 1
44 | '1' shift 4 and reduce (2)
45 | expr goto 5 and reduce (3)
46 | . error
47 |
48 | state 2
49 | (1) expr : expr '+' . expr
50 |
51 | T_FOO shift 1
52 | '1' shift 4 and reduce (2)
53 | expr goto 6 and reduce (1)
54 | . error
55 |
56 | state 3
57 | (0) $start : expr .
58 | (1) expr : expr . '+' expr
59 |
60 | EOF accept
61 | '+' shift 2
62 | . error
63 |
64 | state 4
65 | (2) expr : '1' .
66 |
67 | . reduce (2)
68 |
69 | state 5
70 | (1) expr : expr . '+' expr
71 | (3) expr : T_FOO expr .
72 |
73 | . reduce (3)
74 |
75 | state 6
76 | (1) expr : expr . '+' expr
77 | (1) expr : expr '+' expr .
78 |
79 | . reduce (1)
80 |
81 |
82 | Statistics for grammar.y:
83 | 5 terminal symbols
84 | 2 nonterminal symbols
85 | 4 productions
86 | 7 states
87 | 0 shift/reduce, 0 reduce/reduce conflicts
88 | 10 lookahead sets used
89 | 12+8=20 action entries
90 | State=>class:
91 |
92 | 0=>0 1=>0 2=>0 3=>1
93 |
94 | Terminal action:
95 | T\S 0 1
96 | EOF . 0
97 | '+' . 2
98 | T_FOO 1 .
99 | '1' 4 .
100 |
101 | Nonterminal GOTO table:
102 | T\S 0 1 2 3
103 | expr 3 5 6 .
104 |
105 | Nonterminal GOTO table:
106 | T\S default 0 1 2 3
107 | expr 3 = 5 6 .
108 |
109 | Candidates of aux table:
110 | Used aux table:
111 | state 0 (class 0)
112 | state 1 (class 0)
113 | state 2 (class 0)
114 | state 3 (class 1)
115 | Order:
116 | 1,0,
117 | Order:
118 | 1,0,
119 |
--------------------------------------------------------------------------------
/examples/10-php7/parser.diff:
--------------------------------------------------------------------------------
1 | 809c809
2 | < "start : start",
3 | ---
4 | > "\$start : start",
5 |
--------------------------------------------------------------------------------
/examples/10-php7/parser.template.php:
--------------------------------------------------------------------------------
1 | semValue
4 | #semval($,%t) $this->semValue
5 | #semval(%n) $stackPos-(%l-%n)
6 | #semval(%n,%t) $stackPos-(%l-%n)
7 |
8 | namespace PhpParser\Parser;
9 |
10 | use PhpParser\Error;
11 | use PhpParser\Node;
12 | use PhpParser\Node\Expr;
13 | use PhpParser\Node\Name;
14 | use PhpParser\Node\Scalar;
15 | use PhpParser\Node\Stmt;
16 | #include;
17 |
18 | /* This is an automatically GENERATED file, which should not be manually edited.
19 | */
20 | class Parser extends \PhpParser\ParserAbstract
21 | {
22 | protected $tokenToSymbolMapSize = #(YYMAXLEX);
23 | protected $actionTableSize = #(YYLAST);
24 | protected $gotoTableSize = #(YYGLAST);
25 |
26 | protected $invalidSymbol = #(YYBADCH);
27 | protected $errorSymbol = #(YYINTERRTOK);
28 | protected $defaultAction = #(YYDEFAULT);
29 | protected $unexpectedTokenRule = #(YYUNEXPECTED);
30 |
31 | protected $YY2TBLSTATE = #(YY2TBLSTATE);
32 | protected $YYNLSTATES = #(YYNLSTATES);
33 |
34 | protected $symbolToName = array(
35 | #listvar terminals
36 | );
37 |
38 | protected $tokenToSymbol = array(
39 | #listvar yytranslate
40 | );
41 |
42 | protected $action = array(
43 | #listvar yyaction
44 | );
45 |
46 | protected $actionCheck = array(
47 | #listvar yycheck
48 | );
49 |
50 | protected $actionBase = array(
51 | #listvar yybase
52 | );
53 |
54 | protected $actionDefault = array(
55 | #listvar yydefault
56 | );
57 |
58 | protected $goto = array(
59 | #listvar yygoto
60 | );
61 |
62 | protected $gotoCheck = array(
63 | #listvar yygcheck
64 | );
65 |
66 | protected $gotoBase = array(
67 | #listvar yygbase
68 | );
69 |
70 | protected $gotoDefault = array(
71 | #listvar yygdefault
72 | );
73 |
74 | protected $ruleToNonTerminal = array(
75 | #listvar yylhs
76 | );
77 |
78 | protected $ruleToLength = array(
79 | #listvar yylen
80 | );
81 | #if -t
82 |
83 | protected $productions = array(
84 | #production-strings;
85 | );
86 | #endif
87 |
88 | protected function initReduceCallbacks() {
89 | $this->reduceCallbacks = [
90 | #reduce
91 | %n => function ($stackPos) {
92 | %b
93 | },
94 | #noact
95 | %n => function ($stackPos) {
96 | $this->semValue = $this->semStack[$stackPos];
97 | },
98 | #endreduce
99 | ];
100 | }
101 | }
102 | #tailcode;
--------------------------------------------------------------------------------
/examples/10-php7/y.diff:
--------------------------------------------------------------------------------
1 | 1c1
2 | < EMPTY nonterminals: start start top_statement_list top_statement_list_ex no_comma optional_comma inner_statement_list_ex inner_statement_list elseif_list else_single new_elseif_list new_else_single for_expr optional_expr catches optional_finally optional_ref optional_ellipsis parameter_list optional_return_type extends_from implements_list class_statement_list interface_extends_list case_list non_empty_parameter_list parameter optional_param_type class_statement method_modifiers trait_adaptation_list exit_expr backticks_expr lexical_vars ctor_arguments lexical_var_list non_empty_lexical_var_list lexical_var array_pair_list list_expr_elements list_expr_element inner_array_pair_list array_pair
3 | ---
4 | > EMPTY nonterminals: $start start top_statement_list top_statement_list_ex no_comma optional_comma inner_statement_list_ex inner_statement_list elseif_list else_single new_elseif_list new_else_single for_expr optional_expr catches optional_finally optional_ref optional_ellipsis parameter_list optional_return_type extends_from implements_list class_statement_list interface_extends_list case_list non_empty_parameter_list parameter optional_param_type class_statement method_modifiers trait_adaptation_list exit_expr backticks_expr lexical_vars ctor_arguments lexical_var_list non_empty_lexical_var_list lexical_var array_pair_list list_expr_elements list_expr_element inner_array_pair_list array_pair
5 | 3c3
6 | < start [ error T_INCLUDE T_INCLUDE_ONCE T_EVAL T_REQUIRE T_REQUIRE_ONCE T_PRINT T_YIELD T_YIELD_FROM '+' '-' '!' '~' T_INC T_DEC T_INT_CAST T_DOUBLE_CAST T_STRING_CAST T_ARRAY_CAST T_OBJECT_CAST T_BOOL_CAST T_UNSET_CAST '@' '[' T_NEW T_CLONE T_EXIT T_IF T_LNUMBER T_DNUMBER T_STRING T_VARIABLE T_INLINE_HTML T_CONSTANT_ENCAPSED_STRING T_ECHO T_DO T_WHILE T_FOR T_FOREACH T_DECLARE T_SWITCH T_BREAK T_CONTINUE T_GOTO T_FUNCTION T_CONST T_RETURN T_TRY T_THROW T_USE T_GLOBAL T_STATIC T_ABSTRACT T_FINAL T_UNSET T_ISSET T_EMPTY T_HALT_COMPILER T_CLASS T_TRAIT T_INTERFACE T_LIST T_ARRAY T_CLASS_C T_TRAIT_C T_METHOD_C T_FUNC_C T_LINE T_FILE T_START_HEREDOC T_NAMESPACE T_NS_C T_DIR T_NS_SEPARATOR ';' '{' '(' '`' '"' '$' @ ]
7 | ---
8 | > $start [ error T_INCLUDE T_INCLUDE_ONCE T_EVAL T_REQUIRE T_REQUIRE_ONCE T_PRINT T_YIELD T_YIELD_FROM '+' '-' '!' '~' T_INC T_DEC T_INT_CAST T_DOUBLE_CAST T_STRING_CAST T_ARRAY_CAST T_OBJECT_CAST T_BOOL_CAST T_UNSET_CAST '@' '[' T_NEW T_CLONE T_EXIT T_IF T_LNUMBER T_DNUMBER T_STRING T_VARIABLE T_INLINE_HTML T_CONSTANT_ENCAPSED_STRING T_ECHO T_DO T_WHILE T_FOR T_FOREACH T_DECLARE T_SWITCH T_BREAK T_CONTINUE T_GOTO T_FUNCTION T_CONST T_RETURN T_TRY T_THROW T_USE T_GLOBAL T_STATIC T_ABSTRACT T_FINAL T_UNSET T_ISSET T_EMPTY T_HALT_COMPILER T_CLASS T_TRAIT T_INTERFACE T_LIST T_ARRAY T_CLASS_C T_TRAIT_C T_METHOD_C T_FUNC_C T_LINE T_FILE T_START_HEREDOC T_NAMESPACE T_NS_C T_DIR T_NS_SEPARATOR ';' '{' '(' '`' '"' '$' @ ]
9 | 147c147
10 | < (0) start : . start
11 | ---
12 | > (0) $start : . start
13 | 152c152
14 | < (0) start : start .
15 | ---
16 | > (0) $start : start .
17 | 9628c9628
18 | < (0) start : . start
19 | ---
20 | > (0) $start : . start
21 | 27346c27346
22 | < (0) start : start .
23 | ---
24 | > (0) $start : start .
25 | 30675a30676
26 | > grammar.y: there are 2 shift/reduce conflicts
27 | 30683d30683
28 | < 4416 items
29 | 30686,30687d30685
30 | < 440128 bytes used
31 | <
32 |
--------------------------------------------------------------------------------
/examples/20-custom-parser/grammar.y:
--------------------------------------------------------------------------------
1 |
2 |
3 | %token NUMBER
4 | %left '+' '-'
5 | %left '*' '/'
6 | %right '^'
7 | %left NEG
8 |
9 | %%
10 |
11 | statement
12 | : /* empty */ { exit(0); }
13 | | expression { printf("= %f\n", $1); }
14 | ;
15 |
16 | expression
17 | : factor { $$ = $1; }
18 | | expression '*' expression { $$ = $1 * $3; }
19 | | expression '/' expression { $$ = $1 / $3; }
20 | | expression '+' expression { $$ = $1 + $3; }
21 | | expression '-' expression { $$ = $1 - $3; }
22 | | expression '^' expression { $$ = pow($1, $3); }
23 | | '-' expression %prec NEG { $$ = -$2; }
24 | ;
25 |
26 | factor
27 | : NUMBER { $$ = $1; }
28 | | '(' expression ')' { $$ = $2; }
29 | ;
30 |
31 | %%
32 |
--------------------------------------------------------------------------------
/examples/20-custom-parser/parser.phpyacc.php:
--------------------------------------------------------------------------------
1 | = 0
269 | && $yyn < YYLAST && $yycheck[$yyn] == $yychar
270 | || ($yystate < YY2TBLSTATE
271 | && ($yyn = $yybase[$yystate + YYNLSTATES] + $yychar) >= 0
272 | && $yyn < YYLAST && $yycheck[$yyn] == $yychar))
273 | && ($yyn = $yyaction[$yyn]) != YYDEFAULT) {
274 | /*
275 | * >= YYNLSTATE: shift and reduce
276 | * > 0: shift
277 | * = 0: accept
278 | * < 0: reduce
279 | * = -YYUNEXPECTED: error
280 | */
281 | if ($yyn > 0) {
282 | /* shift */
283 | YYTRACE_SHIFT($yychar);
284 | $yysp++;
285 |
286 | $yysstk[$yysp] = $yystate = $yyn;
287 | $yyastk[$yysp] = $yylval;
288 | $yychar = -1;
289 |
290 | if ($yyerrflag > 0)
291 | $yyerrflag--;
292 | if ($yyn < YYNLSTATES)
293 | continue;
294 |
295 | /* $yyn >= YYNLSTATES means shift-and-reduce */
296 | $yyn -= YYNLSTATES;
297 | } else
298 | $yyn = -$yyn;
299 | } else
300 | $yyn = $yydefault[$yystate];
301 | }
302 |
303 | while (true) {
304 | /* reduce/error */
305 | if ($yyn == 0) {
306 | /* accept */
307 | YYTRACE_ACCEPT();
308 | yyflush();
309 | return 0;
310 | }
311 | else if ($yyn != YYUNEXPECTED) {
312 | /* reduce */
313 | $yyl = $yylen[$yyn];
314 | $n = $yysp-$yyl+1;
315 | $yyval = isset($yyastk[$n]) ? $yyastk[$n] : null;
316 | YYTRACE_REDUCE($yyn);
317 | /* Following line will be replaced by reduce actions */
318 | switch($yyn) {
319 | case 1:
320 | { exit(0); } break;
321 | case 2:
322 | { printf("= %f\n", $yyastk[$yysp-(1-1)]); } break;
323 | case 3:
324 | { $yyval = $yyastk[$yysp-(1-1)]; } break;
325 | case 4:
326 | { $yyval = $yyastk[$yysp-(3-1)] * $yyastk[$yysp-(3-3)]; } break;
327 | case 5:
328 | { $yyval = $yyastk[$yysp-(3-1)] $yyastk[$yysp-(3-3)]; } break;
329 | case 6:
330 | { $yyval = $yyastk[$yysp-(3-1)] + $yyastk[$yysp-(3-3)]; } break;
331 | case 7:
332 | { $yyval = $yyastk[$yysp-(3-1)] - $yyastk[$yysp-(3-3)]; } break;
333 | case 8:
334 | { $yyval = pow($yyastk[$yysp-(3-1)], $yyastk[$yysp-(3-3)]); } break;
335 | case 9:
336 | { $yyval = -$yyastk[$yysp-(2-2)]; } break;
337 | case 10:
338 | { $yyval = $yyastk[$yysp-(1-1)]; } break;
339 | case 11:
340 | { $yyval = $yyastk[$yysp-(3-2)]; } break;
341 | }
342 | /* Goto - shift nonterminal */
343 | $yysp -= $yyl;
344 | $yyn = $yylhs[$yyn];
345 | if (($yyp = $yygbase[$yyn] + $yysstk[$yysp]) >= 0 && $yyp < YYGLAST
346 | && $yygcheck[$yyp] == $yyn)
347 | $yystate = $yygoto[$yyp];
348 | else
349 | $yystate = $yygdefault[$yyn];
350 |
351 | $yysp++;
352 |
353 | $yysstk[$yysp] = $yystate;
354 | $yyastk[$yysp] = $yyval;
355 | }
356 | else {
357 | /* error */
358 | switch ($yyerrflag) {
359 | case 0:
360 | yyerror("syntax error");
361 | case 1:
362 | case 2:
363 | $yyerrflag = 3;
364 | /* Pop until error-expecting state uncovered */
365 |
366 | while (!(($yyn = $yybase[$yystate] + YYINTERRTOK) >= 0
367 | && $yyn < YYLAST && $yycheck[$yyn] == YYINTERRTOK
368 | || ($yystate < YY2TBLSTATE
369 | && ($yyn = $yybase[$yystate + YYNLSTATES] + YYINTERRTOK) >= 0
370 | && $yyn < YYLAST && $yycheck[$yyn] == YYINTERRTOK))) {
371 | if ($yysp <= 0) {
372 | yyflush();
373 | return 1;
374 | }
375 | $yystate = $yysstk[--$yysp];
376 | YYTRACE_POP($yystate);
377 | }
378 | $yyn = $yyaction[$yyn];
379 | YYTRACE_SHIFT(YYINTERRTOK);
380 | $yysstk[++$yysp] = $yystate = $yyn;
381 | break;
382 |
383 | case 3:
384 | YYTRACE_DISCARD($yychar);
385 | if ($yychar == 0) {
386 | yyflush();
387 | return 1;
388 | }
389 | $yychar = -1;
390 | break;
391 | }
392 | }
393 |
394 | if ($yystate < YYNLSTATES)
395 | break;
396 | /* >= YYNLSTATES means shift-and-reduce */
397 | $yyn = $yystate - YYNLSTATES;
398 | }
399 | }
400 | }
401 |
402 |
403 |
--------------------------------------------------------------------------------
/examples/20-custom-parser/parser.template.php:
--------------------------------------------------------------------------------
1 | = 0
221 | && $yyn < YYLAST && $yycheck[$yyn] == $yychar
222 | || ($yystate < YY2TBLSTATE
223 | && ($yyn = $yybase[$yystate + YYNLSTATES] + $yychar) >= 0
224 | && $yyn < YYLAST && $yycheck[$yyn] == $yychar))
225 | && ($yyn = $yyaction[$yyn]) != YYDEFAULT) {
226 | /*
227 | * >= YYNLSTATE: shift and reduce
228 | * > 0: shift
229 | * = 0: accept
230 | * < 0: reduce
231 | * = -YYUNEXPECTED: error
232 | */
233 | if ($yyn > 0) {
234 | /* shift */
235 | @if -t
236 | YYTRACE_SHIFT($yychar);
237 | @endif
238 | $yysp++;
239 |
240 | $yysstk[$yysp] = $yystate = $yyn;
241 | $yyastk[$yysp] = $yylval;
242 | $yychar = -1;
243 |
244 | if ($yyerrflag > 0)
245 | $yyerrflag--;
246 | if ($yyn < YYNLSTATES)
247 | continue;
248 |
249 | /* $yyn >= YYNLSTATES means shift-and-reduce */
250 | $yyn -= YYNLSTATES;
251 | } else
252 | $yyn = -$yyn;
253 | } else
254 | $yyn = $yydefault[$yystate];
255 | }
256 |
257 | while (true) {
258 | /* reduce/error */
259 | if ($yyn == 0) {
260 | /* accept */
261 | @if -t
262 | YYTRACE_ACCEPT();
263 | @endif
264 | yyflush();
265 | return 0;
266 | }
267 | else if ($yyn != YYUNEXPECTED) {
268 | /* reduce */
269 | $yyl = $yylen[$yyn];
270 | $n = $yysp-$yyl+1;
271 | $yyval = isset($yyastk[$n]) ? $yyastk[$n] : null;
272 | @if -t
273 | YYTRACE_REDUCE($yyn);
274 | @endif
275 | /* Following line will be replaced by reduce actions */
276 | switch($yyn) {
277 | @reduce
278 | case %n:
279 | {%b} break;
280 | @endreduce
281 | }
282 | /* Goto - shift nonterminal */
283 | $yysp -= $yyl;
284 | $yyn = $yylhs[$yyn];
285 | if (($yyp = $yygbase[$yyn] + $yysstk[$yysp]) >= 0 && $yyp < YYGLAST
286 | && $yygcheck[$yyp] == $yyn)
287 | $yystate = $yygoto[$yyp];
288 | else
289 | $yystate = $yygdefault[$yyn];
290 |
291 | $yysp++;
292 |
293 | $yysstk[$yysp] = $yystate;
294 | $yyastk[$yysp] = $yyval;
295 | }
296 | else {
297 | /* error */
298 | switch ($yyerrflag) {
299 | case 0:
300 | yyerror("syntax error");
301 | case 1:
302 | case 2:
303 | $yyerrflag = 3;
304 | /* Pop until error-expecting state uncovered */
305 |
306 | while (!(($yyn = $yybase[$yystate] + YYINTERRTOK) >= 0
307 | && $yyn < YYLAST && $yycheck[$yyn] == YYINTERRTOK
308 | || ($yystate < YY2TBLSTATE
309 | && ($yyn = $yybase[$yystate + YYNLSTATES] + YYINTERRTOK) >= 0
310 | && $yyn < YYLAST && $yycheck[$yyn] == YYINTERRTOK))) {
311 | if ($yysp <= 0) {
312 | yyflush();
313 | return 1;
314 | }
315 | $yystate = $yysstk[--$yysp];
316 | @if -t
317 | YYTRACE_POP($yystate);
318 | @endif
319 | }
320 | $yyn = $yyaction[$yyn];
321 | @if -t
322 | YYTRACE_SHIFT(YYINTERRTOK);
323 | @endif
324 | $yysstk[++$yysp] = $yystate = $yyn;
325 | break;
326 |
327 | case 3:
328 | @if -t
329 | YYTRACE_DISCARD($yychar);
330 | @endif
331 | if ($yychar == 0) {
332 | yyflush();
333 | return 1;
334 | }
335 | $yychar = -1;
336 | break;
337 | }
338 | }
339 |
340 | if ($yystate < YYNLSTATES)
341 | break;
342 | /* >= YYNLSTATES means shift-and-reduce */
343 | $yyn = $yystate - YYNLSTATES;
344 | }
345 | }
346 | }
347 |
348 | @tailcode;
--------------------------------------------------------------------------------
/examples/20-custom-parser/y.diff:
--------------------------------------------------------------------------------
1 | 1c1
2 | < EMPTY nonterminals: start statement
3 | ---
4 | > EMPTY nonterminals: $start statement
5 | 3c3
6 | < start [ NUMBER '-' '(' @ ]
7 | ---
8 | > $start [ NUMBER '-' '(' @ ]
9 | 8c8
10 | < (0) start : . statement
11 | ---
12 | > (0) $start : . statement
13 | 22c22
14 | < (0) start : statement .
15 | ---
16 | > (0) $start : statement .
17 | 150c150
18 | < (0) start : . statement
19 | ---
20 | > (0) $start : . statement
21 | 289c289
22 | < (0) start : statement .
23 | ---
24 | > (0) $start : statement .
25 | 359d358
26 | < 66 items
27 | 362,363d360
28 | < 7544 bytes used
29 | <
30 |
--------------------------------------------------------------------------------
/examples/20-custom-parser/y.kmyacc.output:
--------------------------------------------------------------------------------
1 | EMPTY nonterminals: start statement
2 | First:
3 | start [ NUMBER '-' '(' @ ]
4 | statement [ NUMBER '-' '(' @ ]
5 | expression [ NUMBER '-' '(' ]
6 | factor [ NUMBER '(' ]
7 | state unknown:
8 | (0) start : . statement
9 | [ EOF ]
10 | (1) statement : .
11 | [ EOF ]
12 | state unknown:
13 | (10) factor : NUMBER .
14 | [ EOF '+' '-' '*' '/' '^' ')' ]
15 | state unknown:
16 | (9) expression : '-' . expression
17 | [ EOF '+' '-' '*' '/' '^' ')' ]
18 | state unknown:
19 | (11) factor : '(' . expression ')'
20 | [ EOF '+' '-' '*' '/' '^' ')' ]
21 | state unknown:
22 | (0) start : statement .
23 | [ EOF ]
24 | state unknown:
25 | (2) statement : expression .
26 | [ EOF ]
27 | (4) expression : expression . '*' expression
28 | [ EOF '+' '-' '*' '/' '^' ]
29 | (5) expression : expression . '/' expression
30 | [ EOF '+' '-' '*' '/' '^' ]
31 | (6) expression : expression . '+' expression
32 | [ EOF '+' '-' '*' '/' '^' ]
33 | (7) expression : expression . '-' expression
34 | [ EOF '+' '-' '*' '/' '^' ]
35 | (8) expression : expression . '^' expression
36 | [ EOF '+' '-' '*' '/' '^' ]
37 | state unknown:
38 | (3) expression : factor .
39 | [ EOF '+' '-' '*' '/' '^' ')' ]
40 | state unknown:
41 | (4) expression : expression . '*' expression
42 | [ EOF '+' '-' '*' '/' '^' ')' ]
43 | (5) expression : expression . '/' expression
44 | [ EOF '+' '-' '*' '/' '^' ')' ]
45 | (6) expression : expression . '+' expression
46 | [ EOF '+' '-' '*' '/' '^' ')' ]
47 | (7) expression : expression . '-' expression
48 | [ EOF '+' '-' '*' '/' '^' ')' ]
49 | (8) expression : expression . '^' expression
50 | [ EOF '+' '-' '*' '/' '^' ')' ]
51 | (9) expression : '-' expression .
52 | [ EOF '+' '-' '*' '/' '^' ')' ]
53 | state unknown:
54 | (4) expression : expression . '*' expression
55 | [ '+' '-' '*' '/' '^' ')' ]
56 | (5) expression : expression . '/' expression
57 | [ '+' '-' '*' '/' '^' ')' ]
58 | (6) expression : expression . '+' expression
59 | [ '+' '-' '*' '/' '^' ')' ]
60 | (7) expression : expression . '-' expression
61 | [ '+' '-' '*' '/' '^' ')' ]
62 | (8) expression : expression . '^' expression
63 | [ '+' '-' '*' '/' '^' ')' ]
64 | (11) factor : '(' expression . ')'
65 | [ EOF '+' '-' '*' '/' '^' ')' ]
66 | state unknown:
67 | (6) expression : expression '+' . expression
68 | [ EOF '+' '-' '*' '/' '^' ')' ]
69 | state unknown:
70 | (7) expression : expression '-' . expression
71 | [ EOF '+' '-' '*' '/' '^' ')' ]
72 | state unknown:
73 | (4) expression : expression '*' . expression
74 | [ EOF '+' '-' '*' '/' '^' ')' ]
75 | state unknown:
76 | (5) expression : expression '/' . expression
77 | [ EOF '+' '-' '*' '/' '^' ')' ]
78 | state unknown:
79 | (8) expression : expression '^' . expression
80 | [ EOF '+' '-' '*' '/' '^' ')' ]
81 | state unknown:
82 | (11) factor : '(' expression ')' .
83 | [ EOF '+' '-' '*' '/' '^' ')' ]
84 | state unknown:
85 | (4) expression : expression . '*' expression
86 | [ EOF '+' '-' '*' '/' '^' ')' ]
87 | (5) expression : expression . '/' expression
88 | [ EOF '+' '-' '*' '/' '^' ')' ]
89 | (6) expression : expression . '+' expression
90 | [ EOF '+' '-' '*' '/' '^' ')' ]
91 | (6) expression : expression '+' expression .
92 | [ EOF '+' '-' '*' '/' '^' ')' ]
93 | (7) expression : expression . '-' expression
94 | [ EOF '+' '-' '*' '/' '^' ')' ]
95 | (8) expression : expression . '^' expression
96 | [ EOF '+' '-' '*' '/' '^' ')' ]
97 | state unknown:
98 | (4) expression : expression . '*' expression
99 | [ EOF '+' '-' '*' '/' '^' ')' ]
100 | (5) expression : expression . '/' expression
101 | [ EOF '+' '-' '*' '/' '^' ')' ]
102 | (6) expression : expression . '+' expression
103 | [ EOF '+' '-' '*' '/' '^' ')' ]
104 | (7) expression : expression . '-' expression
105 | [ EOF '+' '-' '*' '/' '^' ')' ]
106 | (7) expression : expression '-' expression .
107 | [ EOF '+' '-' '*' '/' '^' ')' ]
108 | (8) expression : expression . '^' expression
109 | [ EOF '+' '-' '*' '/' '^' ')' ]
110 | state unknown:
111 | (4) expression : expression . '*' expression
112 | [ EOF '+' '-' '*' '/' '^' ')' ]
113 | (4) expression : expression '*' expression .
114 | [ EOF '+' '-' '*' '/' '^' ')' ]
115 | (5) expression : expression . '/' expression
116 | [ EOF '+' '-' '*' '/' '^' ')' ]
117 | (6) expression : expression . '+' expression
118 | [ EOF '+' '-' '*' '/' '^' ')' ]
119 | (7) expression : expression . '-' expression
120 | [ EOF '+' '-' '*' '/' '^' ')' ]
121 | (8) expression : expression . '^' expression
122 | [ EOF '+' '-' '*' '/' '^' ')' ]
123 | state unknown:
124 | (4) expression : expression . '*' expression
125 | [ EOF '+' '-' '*' '/' '^' ')' ]
126 | (5) expression : expression . '/' expression
127 | [ EOF '+' '-' '*' '/' '^' ')' ]
128 | (5) expression : expression '/' expression .
129 | [ EOF '+' '-' '*' '/' '^' ')' ]
130 | (6) expression : expression . '+' expression
131 | [ EOF '+' '-' '*' '/' '^' ')' ]
132 | (7) expression : expression . '-' expression
133 | [ EOF '+' '-' '*' '/' '^' ')' ]
134 | (8) expression : expression . '^' expression
135 | [ EOF '+' '-' '*' '/' '^' ')' ]
136 | state unknown:
137 | (4) expression : expression . '*' expression
138 | [ EOF '+' '-' '*' '/' '^' ')' ]
139 | (5) expression : expression . '/' expression
140 | [ EOF '+' '-' '*' '/' '^' ')' ]
141 | (6) expression : expression . '+' expression
142 | [ EOF '+' '-' '*' '/' '^' ')' ]
143 | (7) expression : expression . '-' expression
144 | [ EOF '+' '-' '*' '/' '^' ')' ]
145 | (8) expression : expression . '^' expression
146 | [ EOF '+' '-' '*' '/' '^' ')' ]
147 | (8) expression : expression '^' expression .
148 | [ EOF '+' '-' '*' '/' '^' ')' ]
149 | state 0
150 | (0) start : . statement
151 | (1) statement : .
152 |
153 | NUMBER shift 16 and reduce (10)
154 | '-' shift 3
155 | '(' shift 4
156 | statement goto 12
157 | expression goto 2
158 | factor goto 17 and reduce (3)
159 | . reduce (1)
160 |
161 | state 1
162 | (4) expression : expression . '*' expression
163 | (5) expression : expression . '/' expression
164 | (6) expression : expression . '+' expression
165 | (7) expression : expression . '-' expression
166 | (8) expression : expression . '^' expression
167 | (11) factor : '(' expression . ')'
168 |
169 | '+' shift 5
170 | '-' shift 6
171 | '*' shift 7
172 | '/' shift 8
173 | '^' shift 9
174 | ')' shift 19 and reduce (11)
175 | . error
176 |
177 | state 2
178 | (2) statement : expression .
179 | (4) expression : expression . '*' expression
180 | (5) expression : expression . '/' expression
181 | (6) expression : expression . '+' expression
182 | (7) expression : expression . '-' expression
183 | (8) expression : expression . '^' expression
184 |
185 | '+' shift 5
186 | '-' shift 6
187 | '*' shift 7
188 | '/' shift 8
189 | '^' shift 9
190 | . reduce (2)
191 |
192 | state 3
193 | (9) expression : '-' . expression
194 |
195 | NUMBER shift 16 and reduce (10)
196 | '-' shift 3
197 | '(' shift 4
198 | expression goto 18 and reduce (9)
199 | factor goto 17 and reduce (3)
200 | . error
201 |
202 | state 4
203 | (11) factor : '(' . expression ')'
204 |
205 | NUMBER shift 16 and reduce (10)
206 | '-' shift 3
207 | '(' shift 4
208 | expression goto 1
209 | factor goto 17 and reduce (3)
210 | . error
211 |
212 | state 5
213 | (6) expression : expression '+' . expression
214 |
215 | NUMBER shift 16 and reduce (10)
216 | '-' shift 3
217 | '(' shift 4
218 | expression goto 10
219 | factor goto 17 and reduce (3)
220 | . error
221 |
222 | state 6
223 | (7) expression : expression '-' . expression
224 |
225 | NUMBER shift 16 and reduce (10)
226 | '-' shift 3
227 | '(' shift 4
228 | expression goto 11
229 | factor goto 17 and reduce (3)
230 | . error
231 |
232 | state 7
233 | (4) expression : expression '*' . expression
234 |
235 | NUMBER shift 16 and reduce (10)
236 | '-' shift 3
237 | '(' shift 4
238 | expression goto 13
239 | factor goto 17 and reduce (3)
240 | . error
241 |
242 | state 8
243 | (5) expression : expression '/' . expression
244 |
245 | NUMBER shift 16 and reduce (10)
246 | '-' shift 3
247 | '(' shift 4
248 | expression goto 14
249 | factor goto 17 and reduce (3)
250 | . error
251 |
252 | state 9
253 | (8) expression : expression '^' . expression
254 |
255 | NUMBER shift 16 and reduce (10)
256 | '-' shift 3
257 | '(' shift 4
258 | expression goto 15
259 | factor goto 17 and reduce (3)
260 | . error
261 |
262 | state 10
263 | (4) expression : expression . '*' expression
264 | (5) expression : expression . '/' expression
265 | (6) expression : expression . '+' expression
266 | (6) expression : expression '+' expression .
267 | (7) expression : expression . '-' expression
268 | (8) expression : expression . '^' expression
269 |
270 | '*' shift 7
271 | '/' shift 8
272 | '^' shift 9
273 | . reduce (6)
274 |
275 | state 11
276 | (4) expression : expression . '*' expression
277 | (5) expression : expression . '/' expression
278 | (6) expression : expression . '+' expression
279 | (7) expression : expression . '-' expression
280 | (7) expression : expression '-' expression .
281 | (8) expression : expression . '^' expression
282 |
283 | '*' shift 7
284 | '/' shift 8
285 | '^' shift 9
286 | . reduce (7)
287 |
288 | state 12
289 | (0) start : statement .
290 |
291 | EOF accept
292 | . error
293 |
294 | state 13
295 | (4) expression : expression . '*' expression
296 | (4) expression : expression '*' expression .
297 | (5) expression : expression . '/' expression
298 | (6) expression : expression . '+' expression
299 | (7) expression : expression . '-' expression
300 | (8) expression : expression . '^' expression
301 |
302 | '^' shift 9
303 | . reduce (4)
304 |
305 | state 14
306 | (4) expression : expression . '*' expression
307 | (5) expression : expression . '/' expression
308 | (5) expression : expression '/' expression .
309 | (6) expression : expression . '+' expression
310 | (7) expression : expression . '-' expression
311 | (8) expression : expression . '^' expression
312 |
313 | '^' shift 9
314 | . reduce (5)
315 |
316 | state 15
317 | (4) expression : expression . '*' expression
318 | (5) expression : expression . '/' expression
319 | (6) expression : expression . '+' expression
320 | (7) expression : expression . '-' expression
321 | (8) expression : expression . '^' expression
322 | (8) expression : expression '^' expression .
323 |
324 | '^' shift 9
325 | . reduce (8)
326 |
327 | state 16
328 | (10) factor : NUMBER .
329 |
330 | . reduce (10)
331 |
332 | state 17
333 | (3) expression : factor .
334 |
335 | . reduce (3)
336 |
337 | state 18
338 | (4) expression : expression . '*' expression
339 | (5) expression : expression . '/' expression
340 | (6) expression : expression . '+' expression
341 | (7) expression : expression . '-' expression
342 | (8) expression : expression . '^' expression
343 | (9) expression : '-' expression .
344 |
345 | . reduce (9)
346 |
347 | state 19
348 | (11) factor : '(' expression ')' .
349 |
350 | . reduce (11)
351 |
352 |
353 | Statistics for grammar.y:
354 | 11 terminal symbols
355 | 4 nonterminal symbols
356 | 12 productions
357 | 20 states
358 | 0 shift/reduce, 0 reduce/reduce conflicts
359 | 66 items
360 | 29 lookahead sets used
361 | 82+21=103 action entries
362 | 7544 bytes used
363 |
364 | State=>class:
365 |
366 | 0=>4 1=>3 2=>2 3=>4 4=>4 5=>4 6=>4 7=>4 8=>4 9=>4
367 | 10=>1 11=>1 12=>5 13=>0 14=>0 15=>0
368 |
369 | Terminal action:
370 | T\S 0 1 2 3 4 5
371 | EOF . . . . . 0
372 | NUMBER . . . . 16 .
373 | '+' . . 5 5 . .
374 | '-' . . 6 6 3 .
375 | '*' . 7 7 7 . .
376 | '/' . 8 8 8 . .
377 | '^' 9 9 9 9 . .
378 | '(' . . . . 4 .
379 | ')' . . . 19 . .
380 |
381 | Nonterminal GOTO table:
382 | T\S 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
383 | statemen 12 . . . . . . . . . . . . . . .
384 | expressi 2 . . 18 1 10 11 13 14 15 . . . . . .
385 | factor 17 . . 17 17 17 17 17 17 17 . . . . . .
386 |
387 | Nonterminal GOTO table:
388 | T\S default 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
389 | statemen 12 = . . . . . . . . . . . . . . .
390 | expressi 2 = . . 18 1 10 11 13 14 15 . . . . . .
391 | factor 17 = . . = = = = = = = . . . . . .
392 |
393 | Candidates of aux table:
394 | Aux = (5) 7,8,9 * 0,1,2,3
395 | Aux = (6) 7,8,9 * 1,2,3
396 | Aux = (5) 5,6,7,8,9 * 2,3
397 | Used aux table:
398 | Selected aux[1]: (6) 7,8,9 * 1,2,3
399 | state 0 (class 4)
400 | state 1 (class 3): aux[1]
401 | state 2 (class 2): aux[1]
402 | state 3 (class 4)
403 | state 4 (class 4)
404 | state 5 (class 4)
405 | state 6 (class 4)
406 | state 7 (class 4)
407 | state 8 (class 4)
408 | state 9 (class 4)
409 | state 10 (class 1)
410 | state 11 (class 1)
411 | state 12 (class 5)
412 | state 13 (class 0)
413 | state 14 (class 0)
414 | state 15 (class 0)
415 | Order:
416 | 4,3,1,2,5,0,
417 | Order:
418 | 2,0,1,3,
419 |
--------------------------------------------------------------------------------
/examples/20-custom-parser/y.phpyacc.output:
--------------------------------------------------------------------------------
1 | EMPTY nonterminals: $start statement
2 | First:
3 | $start [ NUMBER '-' '(' @ ]
4 | statement [ NUMBER '-' '(' @ ]
5 | expression [ NUMBER '-' '(' ]
6 | factor [ NUMBER '(' ]
7 | state unknown:
8 | (0) $start : . statement
9 | [ EOF ]
10 | (1) statement : .
11 | [ EOF ]
12 | state unknown:
13 | (10) factor : NUMBER .
14 | [ EOF '+' '-' '*' '/' '^' ')' ]
15 | state unknown:
16 | (9) expression : '-' . expression
17 | [ EOF '+' '-' '*' '/' '^' ')' ]
18 | state unknown:
19 | (11) factor : '(' . expression ')'
20 | [ EOF '+' '-' '*' '/' '^' ')' ]
21 | state unknown:
22 | (0) $start : statement .
23 | [ EOF ]
24 | state unknown:
25 | (2) statement : expression .
26 | [ EOF ]
27 | (4) expression : expression . '*' expression
28 | [ EOF '+' '-' '*' '/' '^' ]
29 | (5) expression : expression . '/' expression
30 | [ EOF '+' '-' '*' '/' '^' ]
31 | (6) expression : expression . '+' expression
32 | [ EOF '+' '-' '*' '/' '^' ]
33 | (7) expression : expression . '-' expression
34 | [ EOF '+' '-' '*' '/' '^' ]
35 | (8) expression : expression . '^' expression
36 | [ EOF '+' '-' '*' '/' '^' ]
37 | state unknown:
38 | (3) expression : factor .
39 | [ EOF '+' '-' '*' '/' '^' ')' ]
40 | state unknown:
41 | (4) expression : expression . '*' expression
42 | [ EOF '+' '-' '*' '/' '^' ')' ]
43 | (5) expression : expression . '/' expression
44 | [ EOF '+' '-' '*' '/' '^' ')' ]
45 | (6) expression : expression . '+' expression
46 | [ EOF '+' '-' '*' '/' '^' ')' ]
47 | (7) expression : expression . '-' expression
48 | [ EOF '+' '-' '*' '/' '^' ')' ]
49 | (8) expression : expression . '^' expression
50 | [ EOF '+' '-' '*' '/' '^' ')' ]
51 | (9) expression : '-' expression .
52 | [ EOF '+' '-' '*' '/' '^' ')' ]
53 | state unknown:
54 | (4) expression : expression . '*' expression
55 | [ '+' '-' '*' '/' '^' ')' ]
56 | (5) expression : expression . '/' expression
57 | [ '+' '-' '*' '/' '^' ')' ]
58 | (6) expression : expression . '+' expression
59 | [ '+' '-' '*' '/' '^' ')' ]
60 | (7) expression : expression . '-' expression
61 | [ '+' '-' '*' '/' '^' ')' ]
62 | (8) expression : expression . '^' expression
63 | [ '+' '-' '*' '/' '^' ')' ]
64 | (11) factor : '(' expression . ')'
65 | [ EOF '+' '-' '*' '/' '^' ')' ]
66 | state unknown:
67 | (6) expression : expression '+' . expression
68 | [ EOF '+' '-' '*' '/' '^' ')' ]
69 | state unknown:
70 | (7) expression : expression '-' . expression
71 | [ EOF '+' '-' '*' '/' '^' ')' ]
72 | state unknown:
73 | (4) expression : expression '*' . expression
74 | [ EOF '+' '-' '*' '/' '^' ')' ]
75 | state unknown:
76 | (5) expression : expression '/' . expression
77 | [ EOF '+' '-' '*' '/' '^' ')' ]
78 | state unknown:
79 | (8) expression : expression '^' . expression
80 | [ EOF '+' '-' '*' '/' '^' ')' ]
81 | state unknown:
82 | (11) factor : '(' expression ')' .
83 | [ EOF '+' '-' '*' '/' '^' ')' ]
84 | state unknown:
85 | (4) expression : expression . '*' expression
86 | [ EOF '+' '-' '*' '/' '^' ')' ]
87 | (5) expression : expression . '/' expression
88 | [ EOF '+' '-' '*' '/' '^' ')' ]
89 | (6) expression : expression . '+' expression
90 | [ EOF '+' '-' '*' '/' '^' ')' ]
91 | (6) expression : expression '+' expression .
92 | [ EOF '+' '-' '*' '/' '^' ')' ]
93 | (7) expression : expression . '-' expression
94 | [ EOF '+' '-' '*' '/' '^' ')' ]
95 | (8) expression : expression . '^' expression
96 | [ EOF '+' '-' '*' '/' '^' ')' ]
97 | state unknown:
98 | (4) expression : expression . '*' expression
99 | [ EOF '+' '-' '*' '/' '^' ')' ]
100 | (5) expression : expression . '/' expression
101 | [ EOF '+' '-' '*' '/' '^' ')' ]
102 | (6) expression : expression . '+' expression
103 | [ EOF '+' '-' '*' '/' '^' ')' ]
104 | (7) expression : expression . '-' expression
105 | [ EOF '+' '-' '*' '/' '^' ')' ]
106 | (7) expression : expression '-' expression .
107 | [ EOF '+' '-' '*' '/' '^' ')' ]
108 | (8) expression : expression . '^' expression
109 | [ EOF '+' '-' '*' '/' '^' ')' ]
110 | state unknown:
111 | (4) expression : expression . '*' expression
112 | [ EOF '+' '-' '*' '/' '^' ')' ]
113 | (4) expression : expression '*' expression .
114 | [ EOF '+' '-' '*' '/' '^' ')' ]
115 | (5) expression : expression . '/' expression
116 | [ EOF '+' '-' '*' '/' '^' ')' ]
117 | (6) expression : expression . '+' expression
118 | [ EOF '+' '-' '*' '/' '^' ')' ]
119 | (7) expression : expression . '-' expression
120 | [ EOF '+' '-' '*' '/' '^' ')' ]
121 | (8) expression : expression . '^' expression
122 | [ EOF '+' '-' '*' '/' '^' ')' ]
123 | state unknown:
124 | (4) expression : expression . '*' expression
125 | [ EOF '+' '-' '*' '/' '^' ')' ]
126 | (5) expression : expression . '/' expression
127 | [ EOF '+' '-' '*' '/' '^' ')' ]
128 | (5) expression : expression '/' expression .
129 | [ EOF '+' '-' '*' '/' '^' ')' ]
130 | (6) expression : expression . '+' expression
131 | [ EOF '+' '-' '*' '/' '^' ')' ]
132 | (7) expression : expression . '-' expression
133 | [ EOF '+' '-' '*' '/' '^' ')' ]
134 | (8) expression : expression . '^' expression
135 | [ EOF '+' '-' '*' '/' '^' ')' ]
136 | state unknown:
137 | (4) expression : expression . '*' expression
138 | [ EOF '+' '-' '*' '/' '^' ')' ]
139 | (5) expression : expression . '/' expression
140 | [ EOF '+' '-' '*' '/' '^' ')' ]
141 | (6) expression : expression . '+' expression
142 | [ EOF '+' '-' '*' '/' '^' ')' ]
143 | (7) expression : expression . '-' expression
144 | [ EOF '+' '-' '*' '/' '^' ')' ]
145 | (8) expression : expression . '^' expression
146 | [ EOF '+' '-' '*' '/' '^' ')' ]
147 | (8) expression : expression '^' expression .
148 | [ EOF '+' '-' '*' '/' '^' ')' ]
149 | state 0
150 | (0) $start : . statement
151 | (1) statement : .
152 |
153 | NUMBER shift 16 and reduce (10)
154 | '-' shift 3
155 | '(' shift 4
156 | statement goto 12
157 | expression goto 2
158 | factor goto 17 and reduce (3)
159 | . reduce (1)
160 |
161 | state 1
162 | (4) expression : expression . '*' expression
163 | (5) expression : expression . '/' expression
164 | (6) expression : expression . '+' expression
165 | (7) expression : expression . '-' expression
166 | (8) expression : expression . '^' expression
167 | (11) factor : '(' expression . ')'
168 |
169 | '+' shift 5
170 | '-' shift 6
171 | '*' shift 7
172 | '/' shift 8
173 | '^' shift 9
174 | ')' shift 19 and reduce (11)
175 | . error
176 |
177 | state 2
178 | (2) statement : expression .
179 | (4) expression : expression . '*' expression
180 | (5) expression : expression . '/' expression
181 | (6) expression : expression . '+' expression
182 | (7) expression : expression . '-' expression
183 | (8) expression : expression . '^' expression
184 |
185 | '+' shift 5
186 | '-' shift 6
187 | '*' shift 7
188 | '/' shift 8
189 | '^' shift 9
190 | . reduce (2)
191 |
192 | state 3
193 | (9) expression : '-' . expression
194 |
195 | NUMBER shift 16 and reduce (10)
196 | '-' shift 3
197 | '(' shift 4
198 | expression goto 18 and reduce (9)
199 | factor goto 17 and reduce (3)
200 | . error
201 |
202 | state 4
203 | (11) factor : '(' . expression ')'
204 |
205 | NUMBER shift 16 and reduce (10)
206 | '-' shift 3
207 | '(' shift 4
208 | expression goto 1
209 | factor goto 17 and reduce (3)
210 | . error
211 |
212 | state 5
213 | (6) expression : expression '+' . expression
214 |
215 | NUMBER shift 16 and reduce (10)
216 | '-' shift 3
217 | '(' shift 4
218 | expression goto 10
219 | factor goto 17 and reduce (3)
220 | . error
221 |
222 | state 6
223 | (7) expression : expression '-' . expression
224 |
225 | NUMBER shift 16 and reduce (10)
226 | '-' shift 3
227 | '(' shift 4
228 | expression goto 11
229 | factor goto 17 and reduce (3)
230 | . error
231 |
232 | state 7
233 | (4) expression : expression '*' . expression
234 |
235 | NUMBER shift 16 and reduce (10)
236 | '-' shift 3
237 | '(' shift 4
238 | expression goto 13
239 | factor goto 17 and reduce (3)
240 | . error
241 |
242 | state 8
243 | (5) expression : expression '/' . expression
244 |
245 | NUMBER shift 16 and reduce (10)
246 | '-' shift 3
247 | '(' shift 4
248 | expression goto 14
249 | factor goto 17 and reduce (3)
250 | . error
251 |
252 | state 9
253 | (8) expression : expression '^' . expression
254 |
255 | NUMBER shift 16 and reduce (10)
256 | '-' shift 3
257 | '(' shift 4
258 | expression goto 15
259 | factor goto 17 and reduce (3)
260 | . error
261 |
262 | state 10
263 | (4) expression : expression . '*' expression
264 | (5) expression : expression . '/' expression
265 | (6) expression : expression . '+' expression
266 | (6) expression : expression '+' expression .
267 | (7) expression : expression . '-' expression
268 | (8) expression : expression . '^' expression
269 |
270 | '*' shift 7
271 | '/' shift 8
272 | '^' shift 9
273 | . reduce (6)
274 |
275 | state 11
276 | (4) expression : expression . '*' expression
277 | (5) expression : expression . '/' expression
278 | (6) expression : expression . '+' expression
279 | (7) expression : expression . '-' expression
280 | (7) expression : expression '-' expression .
281 | (8) expression : expression . '^' expression
282 |
283 | '*' shift 7
284 | '/' shift 8
285 | '^' shift 9
286 | . reduce (7)
287 |
288 | state 12
289 | (0) $start : statement .
290 |
291 | EOF accept
292 | . error
293 |
294 | state 13
295 | (4) expression : expression . '*' expression
296 | (4) expression : expression '*' expression .
297 | (5) expression : expression . '/' expression
298 | (6) expression : expression . '+' expression
299 | (7) expression : expression . '-' expression
300 | (8) expression : expression . '^' expression
301 |
302 | '^' shift 9
303 | . reduce (4)
304 |
305 | state 14
306 | (4) expression : expression . '*' expression
307 | (5) expression : expression . '/' expression
308 | (5) expression : expression '/' expression .
309 | (6) expression : expression . '+' expression
310 | (7) expression : expression . '-' expression
311 | (8) expression : expression . '^' expression
312 |
313 | '^' shift 9
314 | . reduce (5)
315 |
316 | state 15
317 | (4) expression : expression . '*' expression
318 | (5) expression : expression . '/' expression
319 | (6) expression : expression . '+' expression
320 | (7) expression : expression . '-' expression
321 | (8) expression : expression . '^' expression
322 | (8) expression : expression '^' expression .
323 |
324 | '^' shift 9
325 | . reduce (8)
326 |
327 | state 16
328 | (10) factor : NUMBER .
329 |
330 | . reduce (10)
331 |
332 | state 17
333 | (3) expression : factor .
334 |
335 | . reduce (3)
336 |
337 | state 18
338 | (4) expression : expression . '*' expression
339 | (5) expression : expression . '/' expression
340 | (6) expression : expression . '+' expression
341 | (7) expression : expression . '-' expression
342 | (8) expression : expression . '^' expression
343 | (9) expression : '-' expression .
344 |
345 | . reduce (9)
346 |
347 | state 19
348 | (11) factor : '(' expression ')' .
349 |
350 | . reduce (11)
351 |
352 |
353 | Statistics for grammar.y:
354 | 11 terminal symbols
355 | 4 nonterminal symbols
356 | 12 productions
357 | 20 states
358 | 0 shift/reduce, 0 reduce/reduce conflicts
359 | 29 lookahead sets used
360 | 82+21=103 action entries
361 | State=>class:
362 |
363 | 0=>4 1=>3 2=>2 3=>4 4=>4 5=>4 6=>4 7=>4 8=>4 9=>4
364 | 10=>1 11=>1 12=>5 13=>0 14=>0 15=>0
365 |
366 | Terminal action:
367 | T\S 0 1 2 3 4 5
368 | EOF . . . . . 0
369 | NUMBER . . . . 16 .
370 | '+' . . 5 5 . .
371 | '-' . . 6 6 3 .
372 | '*' . 7 7 7 . .
373 | '/' . 8 8 8 . .
374 | '^' 9 9 9 9 . .
375 | '(' . . . . 4 .
376 | ')' . . . 19 . .
377 |
378 | Nonterminal GOTO table:
379 | T\S 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
380 | statemen 12 . . . . . . . . . . . . . . .
381 | expressi 2 . . 18 1 10 11 13 14 15 . . . . . .
382 | factor 17 . . 17 17 17 17 17 17 17 . . . . . .
383 |
384 | Nonterminal GOTO table:
385 | T\S default 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
386 | statemen 12 = . . . . . . . . . . . . . . .
387 | expressi 2 = . . 18 1 10 11 13 14 15 . . . . . .
388 | factor 17 = . . = = = = = = = . . . . . .
389 |
390 | Candidates of aux table:
391 | Aux = (5) 7,8,9 * 0,1,2,3
392 | Aux = (6) 7,8,9 * 1,2,3
393 | Aux = (5) 5,6,7,8,9 * 2,3
394 | Used aux table:
395 | Selected aux[1]: (6) 7,8,9 * 1,2,3
396 | state 0 (class 4)
397 | state 1 (class 3): aux[1]
398 | state 2 (class 2): aux[1]
399 | state 3 (class 4)
400 | state 4 (class 4)
401 | state 5 (class 4)
402 | state 6 (class 4)
403 | state 7 (class 4)
404 | state 8 (class 4)
405 | state 9 (class 4)
406 | state 10 (class 1)
407 | state 11 (class 1)
408 | state 12 (class 5)
409 | state 13 (class 0)
410 | state 14 (class 0)
411 | state 15 (class 0)
412 | Order:
413 | 4,3,1,2,5,0,
414 | Order:
415 | 2,0,1,3,
416 |
--------------------------------------------------------------------------------
/examples/rebuild.php:
--------------------------------------------------------------------------------
1 | args[0])) {
15 | buildFolder($options, $generator, realpath($options->args[0]));
16 | } else {
17 | buildAll($options, $generator, __DIR__);
18 | }
19 |
20 |
21 | function buildAll(CliOptions $options, Generator $generator, string $dir)
22 | {
23 | $it = new DirectoryIterator($dir);
24 | foreach ($it as $file) {
25 | if (!$file->isDir() || $file->isDot()) {
26 | continue;
27 | }
28 | $dir = $file->getPathname();
29 | buildFolder($options, $generator, $dir);
30 | }
31 | }
32 |
33 |
34 | function buildFolder(CliOptions $options, Generator $generator, string $dir) {
35 | chdir($dir);
36 | echo "Building $dir\n";
37 |
38 | $grammar = "grammar.y";
39 | $skeleton = "parser.template.php";
40 |
41 | if ($options->runKmyacc) {
42 | shell_exec("cd $dir && kmyacc -x -t -v -L php -m $skeleton -p Parser $grammar 2>&1");
43 | rename("$dir/y.output", "$dir/y.kmyacc.output");
44 | rename("$dir/grammar.php", "$dir/parser.kmyacc.php");
45 | }
46 |
47 | $errorFile = fopen("php://stderr", "w");
48 | $debugFile = DEBUG ? fopen("$dir/y.phpyacc.output", 'w') : null;
49 | $context = new Context($grammar, $errorFile, $debugFile, VERBOSE_DEBUG);
50 | $context->tflag = true;
51 | $generator->generate(
52 | $context,
53 | file_get_contents($grammar),
54 | file_get_contents($skeleton),
55 | "$dir/parser.phpyacc.php"
56 | );
57 |
58 | shell_exec("cd $dir && diff -w parser.kmyacc.php parser.phpyacc.php > parser.diff");
59 |
60 | shell_exec("cd $dir && diff -w y.kmyacc.output y.phpyacc.output > y.diff");
61 |
62 | }
63 |
64 | class CliOptions {
65 | public $runKmyacc = false;
66 | public $args = [];
67 |
68 | public static function fromArgv(array $argv) {
69 | $options = new self;
70 | foreach (array_slice($argv, 1) as $arg) {
71 | if ($arg === '-k') {
72 | $options->runKmyacc = true;
73 | } else {
74 | $options->args[] = $arg;
75 | }
76 | }
77 | return $options;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/lib/CodeGen/Language.php:
--------------------------------------------------------------------------------
1 | fp = $file;
25 | $this->hp = $headerFile;
26 | $this->fileBuffer = '';
27 | $this->headerBuffer = '';
28 | }
29 |
30 | public function commit()
31 | {
32 | // Make sure there is exactly one trailing newline.
33 | $this->fileBuffer = rtrim($this->fileBuffer, "\n") . "\n";
34 | $this->headerBuffer = rtrim($this->headerBuffer, "\n") . "\n";
35 |
36 | fwrite($this->fp, $this->fileBuffer);
37 | fwrite($this->hp, $this->headerBuffer);
38 | $this->fp = $this->hp = null;
39 | $this->fileBuffer = '';
40 | $this->headerBuffer = '';
41 | }
42 |
43 | public function inline_comment(string $text)
44 | {
45 | $this->fileBuffer .= '/* ' . $text . " */";
46 | }
47 |
48 | public function comment(string $text)
49 | {
50 | $this->fileBuffer .= '//' . $text . "\n";
51 | }
52 |
53 | public function case_block(string $indent, int $num, string $value)
54 | {
55 | $this->fileBuffer .= sprintf("%scase %d: return %s;\n", $indent, $num, var_export($value, true));
56 | }
57 |
58 | public function write(string $text, bool $includeHeader = false)
59 | {
60 | $this->fileBuffer .= $text;
61 | if ($includeHeader) {
62 | $this->headerBuffer .= $text;
63 | }
64 | }
65 |
66 | public function writeQuoted(string $text)
67 | {
68 | $regex = '(\\$(?=[a-zA-Z_])|")';
69 | $text = preg_replace($regex, "\\\\$0", $text);
70 | $this->fileBuffer .= $text;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/lib/Compress/Auxiliary.php:
--------------------------------------------------------------------------------
1 | index = $index;
16 | }
17 |
18 | public static function compare(Preimage $x, Preimage $y): int
19 | {
20 | if ($x->length !== $y->length) {
21 | return $x->length - $y->length;
22 | }
23 | foreach ($x->classes as $key => $value) {
24 | if ($value !== $y->classes[$key]) {
25 | return $value - $y->classes[$key];
26 | }
27 | }
28 | return 0;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib/Compress/TRow.php:
--------------------------------------------------------------------------------
1 | index = $index;
17 | $this->mini = -1;
18 | $this->maxi = 0;
19 | $this->nent = 0;
20 | }
21 |
22 | public function span(): int
23 | {
24 | return $this->maxi - $this->mini;
25 | }
26 |
27 | public function nhole(): int
28 | {
29 | return $this->span() - $this->nent;
30 | }
31 |
32 | public static function compare(TRow $a, TRow $b): int
33 | {
34 | if ($a->nent !== $b->nent) {
35 | return $b->nent - $a->nent;
36 | }
37 | if ($a->span() !== $b->span()) {
38 | return $b->span() - $a->span();
39 | }
40 | return $a->mini - $b->mini;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/lib/Compress/functions.php:
--------------------------------------------------------------------------------
1 | t), Token::decode($expecting), $token->fn, $token->ln));
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/lib/Exception/PhpYaccException.php:
--------------------------------------------------------------------------------
1 | parser = $parser ?: new Parser(new Lexer(), new MacroSet());
23 | $this->lalr = $lalr ?: new Lalr();
24 | $this->compressor = $compressor ?: new Compress();
25 | }
26 |
27 | public function generate(Context $context, string $grammar, string $template, string $resultFile)
28 | {
29 | $template = new Template(new PHP(), $template, $context);
30 |
31 | $this->parser->parse($grammar, $context);
32 |
33 | $this->lalr->compute($context);
34 |
35 | $result = $this->compressor->compress($context);
36 |
37 | $template->render($result, fopen($resultFile, 'w'));
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/lib/Grammar/Context.php:
--------------------------------------------------------------------------------
1 | '',
29 | DollarExpansion::SEMVAL_LHS_UNTYPED => '',
30 | DollarExpansion::SEMVAL_RHS_TYPED => '',
31 | DollarExpansion::SEMVAL_RHS_UNTYPED => '',
32 | ];
33 |
34 | public $nsymbols = 0;
35 | public $nterminals = 0;
36 | public $nnonterminals = 0;
37 |
38 | protected $symbolHash = [];
39 | protected $_symbols = [];
40 | protected $_nilsymbol = null;
41 | protected $finished = false;
42 |
43 | protected $_states;
44 | public $nstates = 0;
45 | public $nnonleafstates = 0;
46 |
47 | public $aflag = false;
48 | public $tflag = false;
49 | public $allowSemanticValueReferenceByName = false;
50 | public $pspref = '';
51 | public $verboseDebug = false;
52 |
53 | public $filename = 'YY';
54 | public $pureFlag = false;
55 | public $startSymbol = null;
56 | public $expected = 0;
57 | public $unioned = false;
58 | public $eofToken = null;
59 | public $errorToken = null;
60 | public $startPrime = null;
61 | protected $_grams = [];
62 | public $ngrams = 0;
63 |
64 | public $default_act = [];
65 | public $default_goto = [];
66 | public $term_action = [];
67 | public $class_action = [];
68 | public $nonterm_goto = [];
69 | public $class_of = [];
70 | public $ctermindex = [];
71 | public $otermindex = [];
72 | public $frequency = [];
73 | public $state_imagesorted = [];
74 | public $nprims = 0;
75 | public $prims = [];
76 | public $primof = [];
77 | public $class2nd = [];
78 | public $nclasses = 0;
79 | public $naux = 0;
80 |
81 | public $debugFile;
82 | public $errorFile;
83 |
84 | public function __construct(
85 | string $filename = 'YY',
86 | $errorFile = null,
87 | $debugFile = null,
88 | bool $verboseDebug = false
89 | ) {
90 | $this->filename = $filename;
91 | $this->errorFile = $errorFile;
92 | $this->debugFile = $debugFile;
93 | $this->verboseDebug = $verboseDebug;
94 | }
95 |
96 | public function error(string $data)
97 | {
98 | if ($this->errorFile) {
99 | fwrite($this->errorFile, $data);
100 | }
101 | }
102 |
103 | public function debug(string $data)
104 | {
105 | if ($this->debugFile) {
106 | fwrite($this->debugFile, $data);
107 | }
108 | }
109 |
110 | public function __get($name)
111 | {
112 | switch ($name) {
113 | case 'terminals': return $this->terminals();
114 | case 'nonterminals': return $this->nonTerminals();
115 | }
116 | if (!isset($this->{'_' . $name})) {
117 | throw new LogicException("Should never happen: unknown property $name");
118 | }
119 | return $this->{'_' . $name};
120 | }
121 |
122 | public function __set($name, $value)
123 | {
124 | $this->{'set' . $name}($value);
125 | }
126 |
127 | public function finish()
128 | {
129 | if ($this->finished) {
130 | return;
131 | }
132 | $this->finished = true;
133 | $code = 0;
134 | foreach ($this->terminals() as $term) {
135 | $term->code = $code++;
136 | }
137 | foreach ($this->nonTerminals() as $nonterm) {
138 | $nonterm->code = $code++;
139 | }
140 | foreach ($this->nilSymbols() as $nil) {
141 | $nil->code = $code++;
142 | }
143 |
144 | usort($this->_symbols, function ($a, $b) {
145 | return $a->code <=> $b->code;
146 | });
147 | }
148 |
149 | public function nilSymbol(): Symbol
150 | {
151 | if ($this->_nilsymbol === null) {
152 | $this->_nilsymbol = $this->intern("@nil");
153 | }
154 | return $this->_nilsymbol;
155 | }
156 |
157 | public function terminals(): Generator
158 | {
159 | foreach ($this->_symbols as $symbol) {
160 | if ($symbol->isterminal) {
161 | yield $symbol;
162 | }
163 | }
164 | }
165 |
166 | public function nilSymbols(): Generator
167 | {
168 | foreach ($this->_symbols as $symbol) {
169 | if ($symbol->isNilSymbol()) {
170 | yield $symbol;
171 | }
172 | }
173 | }
174 |
175 | public function nonTerminals(): Generator
176 | {
177 | foreach ($this->_symbols as $symbol) {
178 | if ($symbol->isnonterminal) {
179 | yield $symbol;
180 | }
181 | }
182 | }
183 |
184 | public function genNonTerminal(): Symbol
185 | {
186 | $buffer = sprintf("@%d", $this->nnonterminals);
187 | return $this->internSymbol($buffer, false);
188 | }
189 |
190 | public function internSymbol(string $s, bool $isTerm): Symbol
191 | {
192 | $p = $this->intern($s);
193 |
194 | if (!$p->isNilSymbol()) {
195 | return $p;
196 | }
197 | if ($isTerm || $s[0] === "'") {
198 | if ($s[0] === "'") {
199 | $p->value = character_value(substr($s, 1, -1));
200 | } else {
201 | $p->value = -1;
202 | }
203 | $p->terminal = Symbol::TERMINAL;
204 | } else {
205 | $p->value = null;
206 | $p->terminal = Symbol::NONTERMINAL;
207 | }
208 |
209 | $p->associativity = Symbol::UNDEF;
210 | $p->precedence = Symbol::UNDEF;
211 | return $p;
212 | }
213 |
214 | public function intern(string $s): Symbol
215 | {
216 | if (isset($this->symbolHash[$s])) {
217 | return $this->symbolHash[$s];
218 | }
219 | $p = new Symbol($this->nsymbols++, $s);
220 | return $this->addSymbol($p);
221 | }
222 |
223 | public function addSymbol(Symbol $symbol): Symbol
224 | {
225 | $this->finished = false;
226 | $this->_symbols[] = $symbol;
227 | $this->symbolHash[$symbol->name] = $symbol;
228 | $this->nterminals = 0;
229 | $this->nnonterminals = 0;
230 | foreach ($this->_symbols as $symbol) {
231 | if ($symbol->isterminal) {
232 | $this->nterminals++;
233 | } elseif ($symbol->isnonterminal) {
234 | $this->nnonterminals++;
235 | }
236 | }
237 | return $symbol;
238 | }
239 |
240 | public function symbols(): array
241 | {
242 | return $this->_symbols;
243 | }
244 |
245 | public function symbol(int $code): Symbol
246 | {
247 | foreach ($this->_symbols as $symbol) {
248 | if ($symbol->code === $code) {
249 | return $symbol;
250 | }
251 | }
252 | throw new LogicException("Should never happen: unknown symbol $code");
253 | }
254 |
255 | public function addGram(Production $p)
256 | {
257 | $p->num = $this->ngrams++;
258 | $this->_grams[] = $p;
259 | return $p;
260 | }
261 |
262 | public function gram(int $i): Production
263 | {
264 | assert($i < $this->ngrams);
265 | return $this->_grams[$i];
266 | }
267 |
268 | public function setStates(array $states)
269 | {
270 | foreach ($states as $state) {
271 | assert($state instanceof State);
272 | }
273 | $this->_states = $states;
274 | $this->nstates = count($states);
275 | }
276 |
277 | public function setNNonLeafStates(int $n)
278 | {
279 | $this->nnonleafstates = $n;
280 | }
281 | }
282 |
--------------------------------------------------------------------------------
/lib/Grammar/State.php:
--------------------------------------------------------------------------------
1 | through = $through;
29 | $this->items = $items;
30 | }
31 |
32 | public function isReduceOnly(): bool
33 | {
34 | return empty($this->shifts)
35 | && $this->reduce[0]->symbol->isNilSymbol();
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/lib/Grammar/Symbol.php:
--------------------------------------------------------------------------------
1 | code = $code;
46 | $this->_name = $name;
47 | $this->_value = $value;
48 | $this->setTerminal($terminal);
49 | $this->_precedence = $precedence;
50 | $this->_associativity = $associativity;
51 | $this->_type = $type;
52 | }
53 |
54 | public function isNilSymbol(): bool
55 | {
56 | return $this->_terminal === self::UNDEF;
57 | }
58 |
59 | public function __get($name)
60 | {
61 | return $this->{'_'.$name};
62 | }
63 |
64 | public function __set($name, $value)
65 | {
66 | $this->{'set' . $name}($value);
67 | }
68 |
69 | public function setTerminal(int $terminal)
70 | {
71 | $this->_terminal = $terminal;
72 | if ($terminal === self::TERMINAL) {
73 | $this->isterminal = true;
74 | $this->isnonterminal = false;
75 | } elseif ($terminal === self::NONTERMINAL) {
76 | $this->isterminal = false;
77 | $this->isnonterminal = true;
78 | } else {
79 | $this->isterminal = false;
80 | $this->isnonterminal = false;
81 | }
82 | $this->setValue($this->_value); // force check to prevent issues
83 | }
84 |
85 | public function setAssociativity(int $associativity)
86 | {
87 | $this->_associativity = $associativity;
88 | }
89 |
90 | public function setPrecedence(int $precedence)
91 | {
92 | $this->_precedence = $precedence;
93 | }
94 |
95 | public function setValue($value)
96 | {
97 | if ($this->isterminal && !is_int($value)) {
98 | throw new LogicException("Terminals value must be an integer, " . gettype($value) . " provided");
99 | } elseif ($this->isnonterminal && !($value instanceof Production || $value === null)) {
100 | throw new LogicException("NonTerminals value must be a production, " . gettype($value) . " provided");
101 | }
102 | $this->_value = $value;
103 | }
104 |
105 | public function setType(?Symbol $type): void
106 | {
107 | $this->_type = $type;
108 | }
109 |
110 | public function setAssociativityFlag(int $flag)
111 | {
112 | $this->_associativity |= $flag;
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/lib/Lalr/ArrayBitset.php:
--------------------------------------------------------------------------------
1 | numBits = $numBits;
17 | $this->array = array_fill(0, intdiv($numBits + self::NBITS - 1, self::NBITS), 0);
18 | }
19 |
20 | public function __clone()
21 | {
22 | $this->array = array_values($this->array);
23 | }
24 |
25 | public function testBit(int $i): bool
26 | {
27 | $offset = intdiv($i, self::NBITS);
28 | return ($this->array[$offset] & (1 << ($i % self::NBITS))) !== 0;
29 | }
30 |
31 | public function setBit(int $i)
32 | {
33 | $offset = intdiv($i, self::NBITS);
34 | $this->array[$offset] |= (1 << ($i % self::NBITS));
35 | }
36 |
37 | public function clearBit(int $i)
38 | {
39 | $offset = intdiv($i, self::NBITS);
40 | $this->array[$offset] &= ~(1 << ($i % self::NBITS));
41 | }
42 |
43 | public function or(Bitset $other): bool
44 | {
45 | assert($this->numBits === $other->numBits);
46 |
47 | $changed = false;
48 | foreach ($this->array as $key => $value) {
49 | $this->array[$key] = $value | $other->array[$key];
50 | $changed = $changed || $value !== $this->array[$key];
51 | }
52 | return $changed;
53 | }
54 |
55 | public function getIterator(): \Traversable
56 | {
57 | $numElems = count($this->array);
58 | for ($n = 0; $n < $numElems; $n++) {
59 | $elem = $this->array[$n];
60 | if ($elem !== 0) {
61 | for ($i = 0; $i < self::NBITS; $i++) {
62 | if ($elem & (1 << $i)) {
63 | yield $n * self::NBITS + $i;
64 | }
65 | }
66 | }
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/lib/Lalr/Bitset.php:
--------------------------------------------------------------------------------
1 | next = $next;
17 | $this->symbol = $symbol;
18 | }
19 |
20 | public function isShiftReduce(): bool
21 | {
22 | return false;
23 | }
24 |
25 | public function isReduceReduce(): bool
26 | {
27 | return false;
28 | }
29 |
30 | public function symbol(): Symbol
31 | {
32 | return $this->symbol;
33 | }
34 |
35 | public function next()
36 | {
37 | return $this->next;
38 | }
39 |
40 | public function setNext(Conflict $next = null)
41 | {
42 | $this->next = $next;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/lib/Lalr/Conflict/ReduceReduce.php:
--------------------------------------------------------------------------------
1 | reduce1 = $reduce1;
20 | $this->reduce2 = $reduce2;
21 | parent::__construct($symbol, $next);
22 | }
23 |
24 | public function isReduceReduce(): bool
25 | {
26 | return true;
27 | }
28 |
29 | public function reduce1(): int
30 | {
31 | return $this->reduce1;
32 | }
33 |
34 | public function reduce2(): int
35 | {
36 | return $this->reduce2;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/lib/Lalr/Conflict/ShiftReduce.php:
--------------------------------------------------------------------------------
1 | state = $state;
19 | $this->reduce = $reduce;
20 | parent::__construct($symbol, $next);
21 | }
22 |
23 | public function isShiftReduce(): bool
24 | {
25 | return true;
26 | }
27 |
28 | public function state(): State
29 | {
30 | return $this->state;
31 | }
32 |
33 | public function reduce(): int
34 | {
35 | return $this->reduce;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/lib/Lalr/Item.php:
--------------------------------------------------------------------------------
1 | = 1);
20 | assert($offset <= count($production->body));
21 | $this->production = $production;
22 | $this->pos = $offset;
23 | }
24 |
25 | public function getIterator(): \Traversable
26 | {
27 | for ($i = $this->pos; $i < \count($this->production->body); $i++) {
28 | yield $this->production->body[$i];
29 | }
30 | }
31 |
32 | public function slice(int $n): Item
33 | {
34 | return new Item($this->production, $this->pos + $n);
35 | }
36 |
37 | public function offsetExists($offset): bool
38 | {
39 | return isset($this->production->body[$offset + $this->pos]);
40 | }
41 |
42 | public function offsetGet($offset): mixed
43 | {
44 | if (!$this->offsetExists($offset)) {
45 | throw new LogicException("Offset $offset does not exist");
46 | }
47 | return $this->production->body[$offset + $this->pos];
48 | }
49 |
50 | public function offsetSet($offset, $value): void
51 | {
52 | throw new LogicException("Not supported");
53 | }
54 |
55 | public function offsetUnset($offset): void
56 | {
57 | throw new LogicException("Not supported");
58 | }
59 |
60 | public function isHeadItem()
61 | {
62 | return $this->pos === 1;
63 | }
64 |
65 | public function isTailItem()
66 | {
67 | return $this->pos === count($this->production->body);
68 | }
69 |
70 | public function getProduction(): Production
71 | {
72 | return $this->production;
73 | }
74 |
75 | public function getPos(): int
76 | {
77 | return $this->pos;
78 | }
79 |
80 | public function __toString()
81 | {
82 | $result = "(" . $this->production->num . ")";
83 | for ($i = 0; $i < count($this->production->body); $i++) {
84 | if ($i === 1) {
85 | $result .= " :";
86 | }
87 | if ($i === $this->pos) {
88 | $result .= " .";
89 | }
90 | $result .= " " . $this->production->body[$i]->name;
91 | }
92 | if ($i === 1) {
93 | $result .= " :";
94 | }
95 | if ($i === $this->pos) {
96 | $result .= " .";
97 | }
98 | return $result;
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/lib/Lalr/LalrResult.php:
--------------------------------------------------------------------------------
1 | grams = $grams;
18 | $this->states = $states;
19 | $this->nstates = count($states);
20 | $this->output = $output;
21 | $this->nnonleafstates = $nnonleafstates;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/lib/Lalr/Lr1.php:
--------------------------------------------------------------------------------
1 | left = $left;
23 | $this->look = $look;
24 | $this->item = $item;
25 | }
26 |
27 | public function isTailItem(): bool
28 | {
29 | return $this->item->isTailItem();
30 | }
31 |
32 | public function isHeadItem(): bool
33 | {
34 | return $this->item->isHeadItem();
35 | }
36 |
37 | public function dump(): string
38 | {
39 | $result = '';
40 | $lr1 = $this;
41 | while ($lr1 !== null) {
42 | $result .= $lr1->item . "\n";
43 | $lr1 = $lr1->next;
44 | }
45 | return $result . "\n";
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/lib/Lalr/Reduce.php:
--------------------------------------------------------------------------------
1 | symbol = $symbol;
19 | $this->number = $number;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/lib/Lalr/StringBitset.php:
--------------------------------------------------------------------------------
1 | numBits = $numBits;
28 | $this->str = str_repeat("\0", intdiv($numBits + self::NBITS - 1, self::NBITS));
29 | }
30 |
31 | public function testBit(int $i): bool
32 | {
33 | $offset = intdiv($i, self::NBITS);
34 | return ((ord($this->str[$offset]) >> ($i % self::NBITS)) & 1) !== 0;
35 | }
36 |
37 | public function setBit(int $i)
38 | {
39 | $offset = intdiv($i, self::NBITS);
40 | $char = $this->str[$offset];
41 | $char |= self::MASKS[$i % self::NBITS];
42 | $this->str[$offset] = $char;
43 | }
44 |
45 | public function clearBit(int $i)
46 | {
47 | $offset = intdiv($i, self::NBITS);
48 | $char = $this->str[$offset];
49 | $char &= ~self::MASKS[$i % self::NBITS];
50 | $this->str[$offset] = $char;
51 | }
52 |
53 | public function or(Bitset $other): bool
54 | {
55 | assert($this->numBits === $other->numBits);
56 |
57 | $changed = false;
58 | for ($i = 0; $i < $this->numBits; $i += self::NBITS) {
59 | $offset = $i / self::NBITS;
60 | if ("\0" !== ($other->str[$offset] & ~$this->str[$offset])) {
61 | $changed = true;
62 | $this->str[$offset] = $this->str[$offset] | $other->str[$offset];
63 | }
64 | }
65 | return $changed;
66 | }
67 |
68 | public function getIterator(): \Traversable
69 | {
70 | for ($i = 0; $i < $this->numBits; $i++) {
71 | if ($this->testBit($i)) {
72 | yield $i;
73 | }
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/lib/Lalr/functions.php:
--------------------------------------------------------------------------------
1 | item != $t->item) {
16 | return false;
17 | }
18 | $p = $p->next;
19 | $t = $t->next;
20 | }
21 | return $p === null || $p->isHeadItem();
22 | }
23 |
24 | function dumpSet(Context $ctx, Bitset $set): string
25 | {
26 | $result = '';
27 | foreach ($set as $code) {
28 | $symbol = $ctx->symbols[$code];
29 | $result .= "{$symbol->name} ";
30 | }
31 | return $result;
32 | }
33 |
--------------------------------------------------------------------------------
/lib/Macro.php:
--------------------------------------------------------------------------------
1 | Token::MARK,
26 | "%{" => Token::BEGININC,
27 | "%}" => Token::ENDINC,
28 | "%token" => Token::TOKEN,
29 | "%term" => Token::TOKEN,
30 | "%left" => Token::LEFT,
31 | "%right" => Token::RIGHT,
32 | "%nonassoc" => Token::NONASSOC,
33 | "%prec" => Token::PRECTOK,
34 | "%type" => Token::TYPE,
35 | "%union" => Token::UNION,
36 | "%start" => Token::START,
37 | "%expect" => Token::EXPECT,
38 | "%pure_parser" => Token::PURE_PARSER,
39 | ];
40 |
41 |
42 | protected $backToken = null;
43 | protected $token = null;
44 | protected $prevIsDollar = false;
45 |
46 | protected $filename;
47 | protected $lineNumber = 0;
48 |
49 | public function getLineNumber(): int
50 | {
51 | return $this->lineNumber;
52 | }
53 |
54 | public function peek(): Token
55 | {
56 | $result = $this->get();
57 | $this->unget();
58 | return $result;
59 | }
60 |
61 | public function get(): Token
62 | {
63 | $this->token = $this->rawGet();
64 | while (in_array($this->token->t, self::SPACE_TOKENS)) {
65 | $this->token = $this->rawGet();
66 | }
67 | return $this->token;
68 | }
69 |
70 | public function unget()
71 | {
72 | if ($this->backToken) {
73 | throw new LexingException("Too many ungetToken calls");
74 | }
75 | $this->backToken = $this->token;
76 | }
77 |
78 | public function rawGet(): Token
79 | {
80 | if ($this->backToken) {
81 | $this->token = $this->backToken;
82 | $this->backToken = null;
83 | return $this->token;
84 | }
85 | $c = $this->getc();
86 | $p = '';
87 | if (is_white($c)) {
88 | while (is_white($c)) {
89 | $p .= $c;
90 | $c = $this->getc();
91 | }
92 | $this->ungetc($c);
93 | return $this->token(Token::SPACE, $p);
94 | }
95 | if ($c === "\n") {
96 | $this->lineNumber++;
97 | return $this->token(Token::NEWLINE, $c);
98 | }
99 | if ($c === "/") {
100 | if (($c = $this->getc()) === '*') {
101 | // skip comments
102 | $p = "/*";
103 | while (true) {
104 | if (($c = $this->getc()) === '*') {
105 | if (($c = $this->getc()) === '/') {
106 | break;
107 | }
108 | $this->ungetc($c);
109 | }
110 | if ($c === EOF) {
111 | throw ParseException::unexpected($this->token(EOF, ''), "*/");
112 | }
113 | $p .= $c;
114 | }
115 | $p .= "*/";
116 | return $this->token(Token::COMMENT, $p);
117 | } elseif ($c === '/') {
118 | // skip // comment
119 | $p = '//';
120 | do {
121 | $c = $this->getc();
122 | if ($c !== EOF) {
123 | $p .= $c;
124 | }
125 | } while ($c !== "\n" && $c !== EOF);
126 | return $this->token(Token::COMMENT, $p);
127 | }
128 | }
129 | if ($c === EOF) {
130 | return $this->token(EOF, '');
131 | }
132 |
133 | $tag = $c;
134 | if ($c === '%') {
135 | $c = $this->getc();
136 | if ($c === '%' || $c === '{' | $c === '}' || is_sym_character($c)) {
137 | $p .= "%";
138 | } else {
139 | $this->ungetc($c);
140 | $c = '%';
141 | }
142 | }
143 |
144 | if ($c === '$') {
145 | if (!$this->prevIsDollar) {
146 | $p .= '$';
147 | $c = $this->getc();
148 | if ($c === '$') {
149 | $this->ungetc($c);
150 | $this->prevIsDollar = true;
151 | } elseif (!ctype_digit($c) && is_sym_character($c)) {
152 | do {
153 | $p .= $c;
154 | $c = $this->getc();
155 | } while (is_sym_character($c));
156 | $this->ungetc($c);
157 | $tag = Token::NAME;
158 | } else {
159 | $this->ungetc($c);
160 | }
161 | } else {
162 | $p .= '$';
163 | $this->prevIsDollar = false;
164 | }
165 | } elseif (is_sym_character($c)) {
166 | do {
167 | $p .= $c;
168 | $c = $this->getc();
169 | } while ($c !== EOF && is_sym_character($c));
170 | $this->ungetc($c);
171 | $tag = ctype_digit($p) ? Token::NUMBER : Token::NAME;
172 | } elseif ($c === '\'' || $c === '"') {
173 | $p .= $c;
174 | while (($c = $this->getc()) !== $tag) {
175 | if ($c === EOF) {
176 | throw ParseException::unexpected($this->token("EOF", ''), $tag);
177 | }
178 | if ($c === "\n") {
179 | throw ParseException::unexpected($this->token(Token::NEWLINE, "\n"), $tag);
180 | }
181 | $p .= $c;
182 | if ($c === '\\') {
183 | $c = $this->getc();
184 | if ($c === EOF) {
185 | break;
186 | }
187 | if ($c === "\n") {
188 | continue;
189 | }
190 | $p .= $c;
191 | }
192 | }
193 | $p .= $c;
194 | } else {
195 | $p .= $c;
196 | }
197 |
198 | if (isset(self::TAG_MAP[$p])) {
199 | $tag = self::TAG_MAP[$p];
200 | }
201 | return $this->token($tag, $p);
202 | }
203 |
204 | protected function token($id, $value): Token
205 | {
206 | return new Token($id, $value, $this->lineNumber, $this->filename);
207 | }
208 |
209 |
210 |
211 | protected $buffer = '';
212 | protected $bufferOffset = 0;
213 | protected $backChar = null;
214 |
215 | public function startLexing(string $code, string $filename)
216 | {
217 | $this->filename = $filename;
218 | $this->buffer = $code;
219 | $this->bufferOffset = 0;
220 | $this->backChar = null;
221 | $this->backToken = null;
222 | $this->token = null;
223 | $this->prevIsDollar = false;
224 | }
225 |
226 | protected function getc(): string
227 | {
228 | if (null !== $this->backChar) {
229 | $result = $this->backChar;
230 | $this->backChar = null;
231 | return $result;
232 | }
233 | if ($this->bufferOffset >= strlen($this->buffer)) {
234 | return EOF;
235 | }
236 | return $this->buffer[$this->bufferOffset++];
237 | }
238 |
239 | protected function ungetc(string $c)
240 | {
241 | if ($c === EOF) {
242 | return;
243 | }
244 | if ($this->backChar !== null) {
245 | throw new LexingException("To many unget calls");
246 | }
247 | $this->backChar = $c;
248 | }
249 | }
250 |
--------------------------------------------------------------------------------
/lib/Yacc/LexerTest.php:
--------------------------------------------------------------------------------
1 | boot($source);
29 | $token = $lexer->rawGet();
30 | $this->assertEquals($expected, $token->t);
31 | $this->assertEquals($source, $token->v);
32 | }
33 |
34 | protected function boot(string $source): Lexer
35 | {
36 | $lexer = new Lexer();
37 | $lexer->startLexing($source, "xxx");
38 | return $lexer;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/lib/Yacc/Macro/DollarExpansion.php:
--------------------------------------------------------------------------------
1 | rewind(); $tokens->valid(); $tokens->next()) {
25 | $t = $tokens->current();
26 | switch ($t->t) {
27 | case Token::NAME:
28 | if (!$ctx->allowSemanticValueReferenceByName) {
29 | break;
30 | }
31 | $type = null;
32 | $v = -1;
33 | for ($i = 0; $i <= $n; $i++) {
34 | if ($symbols[$i]->name === $t->v) {
35 | if ($v < 0) {
36 | $v = $i;
37 | } else {
38 | throw new ParseException("Ambiguous semantic value reference for $t");
39 | }
40 | }
41 | }
42 | if ($v < 0) {
43 | for ($i = 0; $i <= $n; $i++) {
44 | if ($attribute[$i] === $t->v) {
45 | $v = $i;
46 | break;
47 | }
48 | }
49 | if ($t->v === $attribute[$n + 1]) {
50 | $v = 0;
51 | }
52 | }
53 | if ($v >= 0) {
54 | $t = clone $t;
55 | $t->t = $v === 0 ? '$' : 0;
56 | goto semval;
57 | }
58 | break;
59 | case '$':
60 | $type = null;
61 | $t = self::next($tokens);
62 | if ($t->t === '<') {
63 | $t = self::next($tokens);
64 | if ($t->t !== Token::NAME) {
65 | throw ParseException::unexpected($t, Token::NAME);
66 | }
67 | $type = $ctx->intern($t->v);
68 | $dump = self::next($tokens);
69 | if ($dump->t !== '>') {
70 | throw ParseException::unexpected($dump, '>');
71 | }
72 | $t = self::next($tokens);
73 | }
74 | $v = 1;
75 | if ($t->t === '$') {
76 | $v = 0;
77 | } elseif ($t->t === '-') {
78 | $t = self::next($tokens);
79 | if ($t->t !== Token::NUMBER) {
80 | throw ParseException::unexpected($t, Token::NUMBER);
81 | }
82 | $v = -1 * ((int) $t->v);
83 | } else {
84 | if ($t->t !== Token::NUMBER) {
85 | throw new RuntimeException("Number expected");
86 | }
87 | $v = (int) $t->v;
88 | if ($v > $n) {
89 | throw new RuntimeException("N is too big");
90 | }
91 | }
92 | semval:
93 | if ($type === null) {
94 | $type = $symbols[$v]->type;
95 | }
96 | if ($type === null /** && $ctx->unioned */ && false) {
97 | throw new ParseException("Type not defined for " . $symbols[$v]->name);
98 | }
99 | foreach ($this->parseDollar($ctx, $t, $v, $n, $type?->name) as $t) {
100 | yield $t;
101 | }
102 |
103 | continue 2;
104 | }
105 | yield $t;
106 | }
107 | }
108 |
109 | protected function parseDollar(Context $ctx, Token $t, int $nth, int $len, ?string $type): array
110 | {
111 | if ($t->t === '$') {
112 | if ($type) {
113 | $mp = $ctx->macros[self::SEMVAL_LHS_TYPED];
114 | } else {
115 | $mp = $ctx->macros[self::SEMVAL_LHS_UNTYPED];
116 | }
117 | } else {
118 | if ($type) {
119 | $mp = $ctx->macros[self::SEMVAL_RHS_TYPED];
120 | } else {
121 | $mp = $ctx->macros[self::SEMVAL_RHS_UNTYPED];
122 | }
123 | }
124 |
125 | $result = '';
126 | for ($i = 0; $i < strlen($mp); $i++) {
127 | if ($mp[$i] === '%') {
128 | $i++;
129 | switch ($mp[$i]) {
130 | case 'n':
131 | $result .= sprintf('%d', $nth);
132 | break;
133 | case 'l':
134 | $result .= sprintf('%d', $len);
135 | break;
136 | case 't':
137 | $result .= $type;
138 | break;
139 | default:
140 | $result .= $mp[$i];
141 | }
142 | } else {
143 | $result .= $mp[$i];
144 | }
145 | }
146 | return $this->parse($result, $t->ln, $t->fn);
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/lib/Yacc/MacroAbstract.php:
--------------------------------------------------------------------------------
1 | next();
39 | if (!$it->valid()) {
40 | throw new LogicException("Unexpected end of action stream: this should never happen");
41 | }
42 | return $it->current();
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/lib/Yacc/MacroSet.php:
--------------------------------------------------------------------------------
1 | addMacro(new Macro\DollarExpansion());
18 | $this->addMacro(...$macros);
19 | }
20 |
21 | public function addMacro(MacroAbstract ...$macros)
22 | {
23 | foreach ($macros as $macro) {
24 | $this->macros[] = $macro;
25 | }
26 | }
27 |
28 | public function apply(Context $ctx, array $symbols, array $tokens, int $n, array $attribute): array
29 | {
30 | $tokens = new ArrayIterator($tokens);
31 | $macroCount = count($this->macros);
32 | if ($macroCount === 1) {
33 | // special case
34 | return iterator_to_array($this->macros[0]->apply($ctx, $symbols, $tokens, $n, $attribute));
35 | }
36 | foreach ($this->macros as $macro) {
37 | $tokens = $macro->apply($ctx, $symbols, $tokens, $n, $attribute);
38 | }
39 | $tokens = self::cache($tokens);
40 |
41 | return iterator_to_array($tokens);
42 | }
43 |
44 | public static function cache(Traversable $t): Traversable
45 | {
46 | return new ArrayIterator(iterator_to_array($t));
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/lib/Yacc/Parser.php:
--------------------------------------------------------------------------------
1 | lexer = $lexer;
30 | $this->macros = $macros;
31 | }
32 |
33 | public function parse(string $code, Context $context)
34 | {
35 | $this->context = $context;
36 | $this->lexer->startLexing($code, $this->context->filename);
37 | $this->doDeclaration();
38 | $this->doGrammar();
39 | $this->context->eofToken = $this->eofToken;
40 | $this->context->errorToken = $this->errorToken;
41 | $this->context->startPrime = $this->startPrime;
42 | $this->context->finish();
43 | return $this->context;
44 | }
45 |
46 | protected function copyAction(array $symbols, int $n, $delm, array $attribute): string
47 | {
48 | $tokens = [];
49 | $ct = 0;
50 | while (($t = $this->lexer->rawGet())->t !== $delm || $ct > 0) {
51 | switch ($t->t) {
52 | case EOF:
53 | throw ParseException::unexpected($t, Token::decode($delm));
54 | case '{':
55 | $ct++;
56 | break;
57 | case '}':
58 | $ct--;
59 | break;
60 | }
61 | $tokens[] = $t;
62 | }
63 | $expanded = $this->macros->apply($this->context, $symbols, $tokens, $n, $attribute);
64 | return implode('', array_map(function (Token $t) {
65 | return $t->v;
66 | }, $expanded));
67 | }
68 |
69 | protected function doType()
70 | {
71 | $type = $this->getType();
72 | while (true) {
73 | if (($t = $this->lexer->get())->v === ',') {
74 | continue;
75 | }
76 | if ($t->t !== Token::NAME && $t->t !== "'") {
77 | break;
78 | }
79 | $p = $this->context->internSymbol($t->v, false);
80 | if ($type !== null) {
81 | $p->type = $type;
82 | }
83 | }
84 | $this->lexer->unget();
85 | }
86 |
87 | protected function doGrammar()
88 | {
89 | $attribute = [];
90 | $gbuffer = [null];
91 | $r = new Production('', 0);
92 | $r->body = [$this->startPrime];
93 | $this->context->addGram($r);
94 |
95 | $t = $this->lexer->get();
96 |
97 | while ($t->t !== Token::MARK && $t->t !== EOF) {
98 | if ($t->t === Token::NAME) {
99 | if ($this->lexer->peek()->t === '@') {
100 | $attribute[0] = $t->v;
101 | $this->lexer->get();
102 | $t = $this->lexer->get();
103 | } else {
104 | $attribute[0] = null;
105 | }
106 | $gbuffer[0] = $this->context->internSymbol($t->v, false);
107 | $attribute[1] = null;
108 | if ($gbuffer[0]->isterminal) {
109 | throw new RuntimeException("Nonterminal symbol expected: $t");
110 | } elseif (($tmp = $this->lexer->get())->t !== ':') {
111 | throw new RuntimeException("':' expected, $tmp found");
112 | }
113 | if ($this->context->startSymbol === null) {
114 | $this->context->startSymbol = $gbuffer[0];
115 | }
116 | } elseif ($t->t === '|') {
117 | if (!$gbuffer[0]) {
118 | throw new RuntimeException("Syntax Error, unexpected $t");
119 | }
120 | $attribute[1] = null;
121 | } elseif ($t->t === Token::BEGININC) {
122 | $this->doCopy();
123 | $t = $this->lexer->get();
124 | continue;
125 | } else {
126 | throw new RuntimeException("Syntax Error Unexpected $t");
127 | }
128 |
129 | $lastTerm = $this->startPrime;
130 | $action = '';
131 | $pos = 0;
132 | $i = 1;
133 | while (true) {
134 | $t = $this->lexer->get();
135 | if ($t->t === '=') {
136 | $pos = $t->ln;
137 | if (($t = $this->lexer->get())->t === '{') {
138 | $pos = $t->ln;
139 | $action = $this->copyAction($gbuffer, $i - 1, '}', $attribute);
140 | } else {
141 | $this->lexer->unget();
142 | $action = $this->copyAction($gbuffer, $i - 1, ';', $attribute);
143 | }
144 | } elseif ($t->t === '{') {
145 | $pos = $t->ln;
146 | $action = $this->copyAction($gbuffer, $i - 1, '}', $attribute);
147 | } elseif ($t->t === Token::PRECTOK) {
148 | $lastTerm = $this->context->internSymbol($this->lexer->get()->v, false);
149 | } elseif ($t->t === Token::NAME && $this->lexer->peek()->t === ':') {
150 | break;
151 | } elseif ($t->t === Token::NAME && $this->lexer->peek()->t === '@') {
152 | $attribute[$i] = $t->v;
153 | $this->lexer->get();
154 | } elseif ($t->t === Token::NAME || $t->t === "'") {
155 | if ($action) {
156 | $g = $this->context->genNonTerminal();
157 | $r = new Production($action, $pos);
158 | $r->body = [$g];
159 | $gbuffer[$i++] = $g;
160 | $attribute[$i] = null;
161 | $r->link = $r->body[0]->value;
162 | $g->value = $this->context->addGram($r);
163 | }
164 | $gbuffer[$i++] = $w = $this->context->internSymbol($t->v, false);
165 | $attribute[$i] = null;
166 | if ($w->isterminal) {
167 | $lastTerm = $w;
168 | }
169 | $action = '';
170 | } else {
171 | break;
172 | }
173 | }
174 | if (!$action) {
175 | if ($i > 1 && $gbuffer[0]->type !== null && $gbuffer[0]->type !== $gbuffer[1]->type) {
176 | throw new ParseException("Stack types are different");
177 | }
178 | }
179 | $r = new Production($action, $pos);
180 | $r->body = array_slice($gbuffer, 0, $i);
181 | $r->precedence = $lastTerm->precedence;
182 | $r->associativity = $lastTerm->associativity & Symbol::MASK;
183 | $r->link = $r->body[0]->value;
184 | $gbuffer[0]->value = $this->context->addGram($r);
185 |
186 | if ($t->t === ';') {
187 | $t = $this->lexer->get();
188 | }
189 | }
190 | $this->context->gram(0)->body[] = $this->context->startSymbol;
191 | $this->startPrime->value = null;
192 | foreach ($this->context->nonterminals as $key => $symbol) {
193 | if ($symbol === $this->startPrime) {
194 | continue;
195 | }
196 | if (($j = $symbol->value) === null) {
197 | throw new ParseException("Nonterminal {$symbol->name} used but not defined");
198 | }
199 | $k = null;
200 | while ($j) {
201 | $w = $j->link;
202 | $j->link = $k;
203 | $k = $j;
204 | $j = $w;
205 | }
206 | $symbol->value = $k;
207 | }
208 | }
209 |
210 | protected function doDeclaration()
211 | {
212 | $this->eofToken = $this->context->internSymbol("EOF", true);
213 | $this->eofToken->value = 0;
214 | $this->errorToken = $this->context->internSymbol("error", true);
215 | $this->startPrime = $this->context->internSymbol("\$start", false);
216 |
217 | while (($t = $this->lexer->get())->t !== Token::MARK) {
218 | switch ($t->t) {
219 | case Token::TOKEN:
220 | case Token::RIGHT:
221 | case Token::LEFT:
222 | case Token::NONASSOC:
223 | $this->doToken($t);
224 | break;
225 | case Token::BEGININC:
226 | $this->doCopy();
227 | break;
228 | case Token::UNION:
229 | $this->doUnion();
230 | $this->context->unioned = true;
231 | break;
232 | case Token::TYPE:
233 | $this->doType();
234 | break;
235 | case Token::EXPECT:
236 | $t = $this->lexer->get();
237 | if ($t->t === Token::NUMBER) {
238 | $this->context->expected = (int) $t->v;
239 | } else {
240 | throw ParseException::unexpected($t, Token::NUMBER);
241 | }
242 | break;
243 | case Token::START:
244 | $t = $this->lexer->get();
245 | $this->context->startSymbol = $this->context->internSymbol($t->v, false);
246 | break;
247 | case Token::PURE_PARSER:
248 | $this->context->pureFlag = true;
249 | break;
250 | case EOF:
251 | throw new ParseException("No grammar given");
252 | default:
253 | throw new ParseException("Syntax error, unexpected {$t->v}");
254 | }
255 | }
256 | $base = 256;
257 | foreach ($this->context->terminals as $terminal) {
258 | if ($terminal === $this->context->eofToken) {
259 | continue;
260 | }
261 | if ($terminal->value < 0) {
262 | $terminal->value = $base++;
263 | }
264 | }
265 | }
266 |
267 | protected $currentPrecedence = 0;
268 |
269 | protected function doToken(Token $tag)
270 | {
271 | $preIncr = 0;
272 | $type = $this->getType();
273 | $t = $this->lexer->get();
274 |
275 | while ($t->t === Token::NAME || $t->t === "'") {
276 | $p = $this->context->internSymbol($t->v, true);
277 | if ($p->name[0] === "'") {
278 | $p->value = character_value(substr($p->name, 1, -1));
279 | }
280 |
281 | if ($type) {
282 | $p->type = $type;
283 | }
284 | switch ($tag->t) {
285 | case Token::LEFT:
286 | $p->associativity |= Symbol::LEFT;
287 | break;
288 | case Token::RIGHT:
289 | $p->associativity |= Symbol::RIGHT;
290 | break;
291 | case Token::NONASSOC:
292 | $p->associativity |= Symbol::NON;
293 | break;
294 | }
295 | if ($tag->t !== Token::TOKEN) {
296 | $p->precedence = $this->currentPrecedence;
297 | $preIncr = 1;
298 | }
299 | $t = $this->lexer->get();
300 | if ($t->t === Token::NUMBER) {
301 | if ($p->value === null) {
302 | $p->value = (int) $t->v;
303 | } else {
304 | throw new ParseException("Unexpected Token::NUMBER as {$p->name} already has a value");
305 | }
306 | $t = $this->lexer->get();
307 | }
308 | if ($t->t === ',') {
309 | $t = $this->lexer->get();
310 | }
311 | }
312 | $this->lexer->unget();
313 | $this->currentPrecedence += $preIncr;
314 | }
315 |
316 | protected function getType()
317 | {
318 | $t = $this->lexer->get();
319 | if ($t->t !== '<') {
320 | $this->lexer->unget();
321 | return null;
322 | }
323 | $ct = 1;
324 | $p = '';
325 | $t = $this->lexer->get();
326 | while (true) {
327 | switch ($t->t) {
328 | case "\n":
329 | case EOF:
330 | throw ParseException::unexpected($t, ">");
331 |
332 | case '<':
333 | $ct++;
334 | break;
335 | case '>':
336 | $ct--;
337 | break;
338 | }
339 | if ($ct === 0) {
340 | break;
341 | }
342 | $p .= $t->v;
343 | $t = $this->lexer->rawGet();
344 | }
345 | $this->context->unioned = true;
346 | return $this->context->intern($p);
347 | }
348 | }
349 |
--------------------------------------------------------------------------------
/lib/Yacc/ParserTest.php:
--------------------------------------------------------------------------------
1 | false,
26 | "nsymbols" => 5,
27 | "nterminals" => 2,
28 | "nnonterminals" => 2,
29 | "ngrams" => 2,
30 | ], [
31 | "terminals" => [
32 | 49 => "'1'",
33 | ],
34 | "nonterminals" => [
35 | "expr"
36 | ],
37 | "grams" => [
38 | [
39 | "action" => "",
40 | "empty" => false,
41 | "body" => [3, 4]
42 | ],
43 | [
44 | "action" => " m2(0,1) = 1; ",
45 | "empty" => false,
46 | "body" => [4, 2],
47 | ]
48 | ]
49 | ]
50 | ],
51 |
52 | [
53 | << true,
64 | "nsymbols" => 6,
65 | "nterminals" => 3,
66 | "nnonterminals" => 2,
67 | "ngrams" => 3,
68 | ], [
69 | "terminals" => [
70 | 49 => "'1'",
71 | 50 => "'2'",
72 | ],
73 | "nonterminals" => [
74 | "expr"
75 | ],
76 | "grams" => [
77 | [
78 | "action" => "",
79 | "empty" => false,
80 | "body" => [4, 5]
81 | ],
82 | [
83 | "action" => " m2(0,1) = 1; ",
84 | "empty" => false,
85 | "body" => [5, 2],
86 | ],
87 | [
88 | "action" => " m2(0,1) = 2; ",
89 | "empty" => false,
90 | "body" => [5, 3],
91 | ]
92 | ]
93 | ]
94 | ],
95 |
96 | [
97 | << false,
113 | "nsymbols" => 9,
114 | "nterminals" => 7,
115 | "nnonterminals" => 1,
116 | "ngrams" => 5,
117 | ], [
118 | "terminals" => [
119 | 43 => "'+'",
120 | 45 => "'-'",
121 | 40 => "'('",
122 | 41 => "')'",
123 | 257 => "T_NUMBER",
124 | ],
125 | "nonterminals" => [
126 | "expr"
127 | ],
128 | "grams" => [
129 | [
130 | "action" => "",
131 | "empty" => false,
132 | "body" => [7, 8]
133 | ],
134 | [
135 | "action" => " m2(0,1) = m4(1,1); ",
136 | "empty" => false,
137 | "body" => [8, 6],
138 | ],
139 | [
140 | "action" => " m2(0,3) = m4(1,3) + m4(3,3); ",
141 | "empty" => false,
142 | "body" => [8, 8, 2, 8],
143 | ],
144 | [
145 | "action" => " m2(0,3) = m4(1,3) - m4(3,3); ",
146 | "empty" => false,
147 | "body" => [8, 8, 3, 8],
148 | ],
149 | [
150 | "action" => " { m2(0,3) = (m4(2,3)); } ",
151 | "empty" => false,
152 | "body" => [8, 4, 8, 5],
153 | ]
154 | ]
155 | ]
156 | ]
157 |
158 | ];
159 | }
160 |
161 | #[DataProvider("provideParserDebugCases")]
162 | public function testParserWithDebug(string $grammar, array $directProps, array $info)
163 | {
164 | $parser = new Parser(new Lexer(), new MacroSet());
165 | $context = new Context('YY');
166 | $context->macros = [
167 | 1 => "m1(%n,%l,%t)",
168 | 2 => "m2(%n,%l)",
169 | 3 => "m3(%n,%l,%t)",
170 | 4 => "m4(%n,%l)",
171 | ];
172 | $parser->parse($grammar, $context);
173 | foreach ($directProps as $prop => $expected) {
174 | $this->assertEquals($expected, $context->$prop, "context->$prop");
175 | }
176 | $this->assertEquals($context->eofToken, $context->symbols[0], "eofToken: symbol[0]");
177 | $this->assertEquals($context->errorToken, $context->symbols[1], "errorToken: symbol[1]");
178 | $i = 2;
179 | foreach ($info['terminals'] as $value => $token) {
180 | $symbol = $context->symbols[$i];
181 | $this->assertEquals($token, $symbol->name, "terminal: symbol[$i]->name");
182 | $this->assertEquals($value, $symbol->value, "terminal: symbol[$i]->value");
183 | $i++;
184 | }
185 | $this->assertEquals($context->startPrime, $context->symbols[$i], "startPrime: symbol[$i]");
186 | $i++;
187 | foreach ($info['nonterminals'] as $token) {
188 | $symbol = $context->symbols[$i];
189 | $this->assertEquals($token, $symbol->name, "nonterminal: symbol[$i]->name");
190 | $i++;
191 | }
192 | foreach ($info['grams'] as $key => $expect) {
193 | $gram = $context->grams[$key];
194 | $this->assertEquals($expect['action'], $gram->action, "gram[$key]->action");
195 | $this->assertEquals($expect['empty'], $gram->isEmpty(), "gram[$key]->isEmpty()");
196 | $this->assertEquals(count($expect['body']), count($gram->body), "count(gram[$key]->body)");
197 | foreach ($expect['body'] as $k => $v) {
198 | $this->assertEquals($v, $gram->body[$k]->code, "gram[$key]->body[$k]");
199 | }
200 | }
201 | }
202 | }
203 |
--------------------------------------------------------------------------------
/lib/Yacc/Production.php:
--------------------------------------------------------------------------------
1 | action = $action;
31 | $this->position = $position;
32 | $this->body = [];
33 | }
34 |
35 | public function setAssociativityFlag(int $flag)
36 | {
37 | $this->associativity |= $flag;
38 | }
39 |
40 | public function isEmpty(): bool
41 | {
42 | return count($this->body) <= 1;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/lib/Yacc/ProductionTest.php:
--------------------------------------------------------------------------------
1 | assertEquals(0, $prod->associativity);
15 | $prod->setAssociativityFlag(1);
16 | $this->assertEquals(1, $prod->associativity);
17 | $prod->setAssociativityFlag(2);
18 | $this->assertEquals(3, $prod->associativity);
19 | }
20 |
21 | public function testIsEmpty()
22 | {
23 | $prod = new Production('', 1);
24 | $this->assertTrue($prod->isEmpty());
25 | $prod->body[] = 1;
26 | $this->assertTrue($prod->isEmpty());
27 | $prod->body[] = 1;
28 | $this->assertFalse($prod->isEmpty());
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib/Yacc/Token.php:
--------------------------------------------------------------------------------
1 | "NAME",
34 | self::NUMBER => "NUMBER",
35 | self::COLON => 'COLON',
36 | self::SPACE => 'SPACE',
37 | self::NEWLINE => 'NEWLINE',
38 | self::MARK => 'MARK',
39 | self::BEGININC => 'BEGININC',
40 | self::ENDINC => 'ENDINC',
41 | self::TOKEN => 'TOKEN',
42 | self::LEFT => 'LEFT',
43 | self::RIGHT => 'RIGHT',
44 | self::NONASSOC => 'NONASSOC',
45 | self::PRECTOK => 'PRECTOK',
46 | self::TYPE => 'TYPE',
47 | self::UNION => 'UNION',
48 | self::START => 'START',
49 | self::COMMENT => 'COMMENT',
50 | self::EXPECT => 'EXPECT',
51 | self::PURE_PARSER => 'PURE_PARSER',
52 | ];
53 |
54 | public $t;
55 | public $v;
56 | public $ln;
57 | public $fn;
58 | public function __construct($token, string $value, int $lineNumber, string $filename)
59 | {
60 | if (!isset(self::TOKEN_MAP[$token]) && !is_string($token)) {
61 | throw new LexingException("Unknown token found: $token");
62 | }
63 | $this->t = $token;
64 | $this->v = $value;
65 | $this->ln = $lineNumber;
66 | $this->fn = $filename;
67 | }
68 |
69 | public static function decode($tag): string
70 | {
71 | if (!isset(self::TOKEN_MAP[$tag])) {
72 | return "$tag";
73 | }
74 | return "Token::" . self::TOKEN_MAP[$tag];
75 | }
76 |
77 | public function __toString(): string
78 | {
79 | return "[{$this->fn}:{$this->ln}] " . self::decode($this->t) . " ({$this->v})";
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/lib/Yacc/TokenTest.php:
--------------------------------------------------------------------------------
1 | assertEquals("[foo.php:42] Token::TOKEN (%token)", "$token");
16 | }
17 |
18 | public function testToStringWithLiteralToken()
19 | {
20 | $token = new Token("'f'", "f", 42, "foo.php");
21 | $this->assertEquals("[foo.php:42] 'f' (f)", "$token");
22 | }
23 |
24 | public function testUnknownToken()
25 | {
26 | $this->expectException(LexingException::class);
27 | $this->expectExceptionMessage('Unknown token found: -2');
28 | new Token(-2, '', 42, 'foo.php');
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib/functions.php:
--------------------------------------------------------------------------------
1 | = 48 && $n <= 55;
45 | }
46 |
47 | function is_gsym(Token $t): bool
48 | {
49 | return $t->t === Token::NAME || $t->t === "'";
50 | }
51 |
52 | function character_value(string $string): int
53 | {
54 | $n = 0;
55 | $length = strlen($string);
56 | if ($length === 0) {
57 | return 0;
58 | }
59 | $c = $string[$n++];
60 | if ($c !== '\\') {
61 | return ord($c);
62 | }
63 | $c = $string[$n++];
64 | if (is_octal($c)) {
65 | $value = (int) $c;
66 | for ($i = 0; $n < $length && is_octal($string[$n]) && $i < 3; $i++) {
67 | $value = $value * 8 + $string[$n++];
68 | }
69 | return $value;
70 | }
71 | switch ($c) {
72 | case 'n': return ord("\n");
73 | case 't': return ord("\t");
74 | case 'b': return ord("\x08");
75 | case 'r': return ord("\r");
76 | case 'f': return ord("\x0C");
77 | case 'v': return ord("\x0B");
78 | case 'a': return ord("\x07");
79 | default:
80 | return ord($c);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------