PHP Target

This commit is contained in:
Marcos Passos 2019-09-14 09:36:37 -03:00
parent cd81586d3d
commit 789d746636
48 changed files with 2854 additions and 23 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "runtime/PHP"]
path = runtime/PHP
url = https://github.com/antlr/antlr-php-runtime.git

View File

@ -152,6 +152,13 @@ matrix:
jdk: openjdk8
env: TARGET=csharp
stage: main-test
- os: linux
language: php
php:
- 7.2
jdk: openjdk8
env: TARGET=php
stage: main-test
- os: linux
jdk: openjdk8
dist: trusty

View File

@ -0,0 +1,10 @@
#!/bin/bash
set -euo pipefail
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
sudo apt-get update -qq
php -v
mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V

9
.travis/run-tests-php.sh Executable file
View File

@ -0,0 +1,9 @@
#!/bin/bash
set -euo pipefail
php_path=$(which php)
composer install -d ../runtime/PHP
mvn -q -DPHP_PATH="${php_path}" -Dparallel=methods -DthreadCount=4 -Dtest=php.* test

View File

@ -22,6 +22,7 @@ ANTLR project lead and supreme dictator for life
* [Janyou](https://github.com/janyou) (Swift target)
* [Ewan Mellor](https://github.com/ewanmellor), [Hanzhou Shi](https://github.com/hanjoes) (Swift target merging)
* [Ben Hamilton](https://github.com/bhamiltoncx) (Full Unicode support in serialized ATN and all languages' runtimes for code points > U+FFFF)
* [Marcos Passos](https://github.com/marcospassos) (PHP target)
## Useful information

View File

@ -4,6 +4,9 @@ cache:
- '%USERPROFILE%\.nuget\packages -> **\project.json'
image: Visual Studio 2017
build: off
install:
- git submodule update --init --recursive
- cinst -y php composer
build_script:
- mvn -DskipTests install --batch-mode
- msbuild /target:restore /target:rebuild /property:Configuration=Release /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" /verbosity:detailed runtime/CSharp/runtime/CSharp/Antlr4.dotnet.sln
@ -11,7 +14,7 @@ build_script:
after_build:
- msbuild /target:pack /property:Configuration=Release /verbosity:detailed runtime/CSharp/runtime/CSharp/Antlr4.dotnet.sln
test_script:
- mvn install -Dantlr-python2-python="C:\Python27\python.exe" -Dantlr-python3-python="C:\Python35\python.exe" -Dantlr-javascript-nodejs="C:\Program Files (x86)\nodejs\node.exe" --batch-mode
- mvn install -Dantlr-php-php="C:\tools\php73\php.exe" -Dantlr-python2-python="C:\Python27\python.exe" -Dantlr-python3-python="C:\Python35\python.exe" -Dantlr-javascript-nodejs="C:\Program Files (x86)\nodejs\node.exe" --batch-mode
artifacts:
- path: 'runtime\**\*.nupkg'
name: NuGet

View File

@ -222,4 +222,7 @@ YYYY/MM/DD, github id, Full name, email
2019/07/16, abhijithneilabraham, Abhijith Neil Abraham, abhijithneilabrahampk@gmail.com
2019/07/26, Braavos96, Eric Hettiaratchi, erichettiaratchi@gmail.com
2019/09/10, ImanHosseini, Iman Hosseini, hosseini.iman@yahoo.com
2019/09/03, João Henrique, johnnyonflame@hotmail.com
2019/09/03, João Henrique, johnnyonflame@hotmail.com
2019/09/10, yar3333, Yaroslav Sivakov, yar3333@gmail.com
2019/09/10, marcospassos, Marcos Passos, marcospassos.com@gmail.com
2019/09/10, amorimjuliana, Juliana Amorim, juu.amorim@gmail.com

111
doc/php-target.md Normal file
View File

@ -0,0 +1,111 @@
# ANTLR4 Runtime for PHP
### First steps
#### 1. Install ANTLR4
[The getting started guide](https://github.com/antlr/antlr4/blob/master/doc/getting-started.md)
should get you started.
#### 2. Install the PHP ANTLR runtime
Each target language for ANTLR has a runtime package for running parser
generated by ANTLR4. The runtime provides a common set of tools for using your parser.
Install the runtime with Composer:
```bash
composer install antlr/antlr4
```
#### 3. Generate your parser
You use the ANTLR4 "tool" to generate a parser. These will reference the ANTLR
runtime, installed above.
Suppose you're using a UNIX system and have set up an alias for the ANTLR4 tool
as described in [the getting started guide](https://github.com/antlr/antlr4/blob/master/doc/getting-started.md).
To generate your PHP parser, run the following command:
```bash
antlr4 -Dlanguage=PHP MyGrammar.g4
```
For a full list of antlr4 tool options, please visit the
[tool documentation page](https://github.com/antlr/antlr4/blob/master/doc/tool-options.md).
### Complete example
Suppose you're using the JSON grammar from https://github.com/antlr/grammars-v4/tree/master/json.
Then, invoke `antlr4 -Dlanguage=PHP JSON.g4`. The result of this is a
collection of `.php` files in the `parser` directory including:
```
JsonParser.php
JsonBaseListener.php
JsonLexer.php
JsonListener.php
```
Another common option to the ANTLR tool is `-visitor`, which generates a parse
tree visitor, but we won't be doing that here. For a full list of antlr4 tool
options, please visit the [tool documentation page](tool-options.md).
We'll write a small main func to call the generated parser/lexer
(assuming they are separate). This one writes out the encountered
`ParseTreeContext`'s:
```php
<?php
namespace JsonParser;
use Antlr\Antlr4\Runtime\CommonTokenStream;
use Antlr\Antlr4\Runtime\Error\Listeners\DiagnosticErrorListener;
use Antlr\Antlr4\Runtime\InputStream;
use Antlr\Antlr4\Runtime\ParserRuleContext;
use Antlr\Antlr4\Runtime\Tree\ErrorNode;
use Antlr\Antlr4\Runtime\Tree\ParseTreeListener;
use Antlr\Antlr4\Runtime\Tree\ParseTreeWalker;
use Antlr\Antlr4\Runtime\Tree\TerminalNode;
final class TreeShapeListener implements ParseTreeListener {
public function visitTerminal(TerminalNode $node) : void {}
public function visitErrorNode(ErrorNode $node) : void {}
public function exitEveryRule(ParserRuleContext $ctx) : void {}
public function enterEveryRule(ParserRuleContext $ctx) : void {
echo $ctx->getText();
}
}
$input = InputStream::fromPath($argv[1]);
$lexer = new JSONLexer($input);
$tokens = new CommonTokenStream($lexer);
$parser = new JSONParser($tokens);
$parser->addErrorListener(new DiagnosticErrorListener());
$parser->setBuildParseTree(true);
$tree = $parser->json();
ParseTreeWalker::default()->walk(new TreeShapeListener(), $tree);
```
Create a `example.json` file:
```json
{"a":1}
```
Parse the input file:
```
php json.php example.json
```
The expected output is:
```
{"a":1}
{"a":1}
"a":1
1
```

View File

@ -9,12 +9,13 @@ This page lists the available and upcoming ANTLR runtimes. Please note that you
* [Go](go-target.md)
* [C++](cpp-target.md)
* [Swift](swift-target.md)
* [PHP](php-target.md)
## Target feature parity
New features generally appear in the Java target and then migrate to the other targets, but these other targets don't always get updated in the same overall tool release. This section tries to identify features added to Java that have not been added to the other targets.
|Feature|Java|C&sharp;|Python2|Python3|JavaScript|Go|C++|Swift|
|---|---|---|---|---|---|---|---|---|
|Ambiguous tree construction|4.5.1|-|-|-|-|-|-|-|
|Feature|Java|C&sharp;|Python2|Python3|JavaScript|Go|C++|Swift|PHP
|---|---|---|---|---|---|---|---|---|---|
|Ambiguous tree construction|4.5.1|-|-|-|-|-|-|-|-|

View File

@ -109,14 +109,15 @@
<!-- SUREFIRE-951: file.encoding cannot be set via systemPropertyVariables -->
<argLine>-Dfile.encoding=UTF-8</argLine>
<includes>
<include>**/csharp/Test*.java</include>
<include>**/java/Test*.java</include>
<include>**/go/Test*.java</include>
<include>**/javascript/node/Test*.java</include>
<include>**/python2/Test*.java</include>
<include>**/python3/Test*.java</include>
<include>${antlr.tests.swift}</include>
</includes>
<include>**/csharp/Test*.java</include>
<include>**/java/Test*.java</include>
<include>**/go/Test*.java</include>
<include>**/javascript/node/Test*.java</include>
<include>**/python2/Test*.java</include>
<include>**/python3/Test*.java</include>
<include>**/php/Test*.java</include>
<include>${antlr.tests.swift}</include>
</includes>
</configuration>
</plugin>
<plugin>

View File

@ -14,6 +14,8 @@ Cast(t,v) ::= "((<t>)<v>)"
Append(a,b) ::= "<a> + <b>"
AppendStr(a,b) ::= <%<Append(a,b)>%>
Concat(a,b) ::= "<a><b>"
DeclareLocal(s,v) ::= "Object <s> = <v>;"
@ -26,6 +28,12 @@ InitIntMember(n,v) ::= <%int <n> = <v>;%>
InitBooleanMember(n,v) ::= <%bool <n> = <v>;%>
InitIntVar(n,v) ::= <%<InitIntMember(n,v)>%>
IntArg(n) ::= "int <n>"
VarRef(n) ::= "<n>"
GetMember(n) ::= <%this.<n>%>
SetMember(n,v) ::= <%this.<n> = <v>;%>
@ -94,6 +102,8 @@ bool Property() {
ParserPropertyCall(p, call) ::= "<p>.<call>"
PositionAdjustingLexerDef() ::= ""
PositionAdjustingLexer() ::= <<
public override IToken NextToken() {

View File

@ -14,6 +14,8 @@ Cast(t,v) ::= "<v>"
Append(a,b) ::= "<a> + <b>"
AppendStr(a,b) ::= <%<Append(a,b)>%>
Concat(a,b) ::= "<a><b>"
DeclareLocal(s,v) ::= "var <s> = <v>;"
@ -26,6 +28,12 @@ InitIntMember(n,v) ::= <%this.<n> = <v>;%>
InitBooleanMember(n,v) ::= <%this.<n> = <v>;%>
InitIntVar(n,v) ::= <%<InitIntMember(n,v)>%>
IntArg(n) ::= "<n>"
VarRef(n) ::= "<n>"
GetMember(n) ::= <%this.<n>%>
SetMember(n,v) ::= <%this.<n> = <v>;%>
@ -92,6 +100,8 @@ this.Property = function() {
}
>>
PositionAdjustingLexerDef() ::= ""
PositionAdjustingLexer() ::= <<
PositionAdjustingLexer.prototype.resetAcceptPosition = function(index, line, column) {

View File

@ -9,6 +9,7 @@ Assert(s) ::= ""
Cast(t,v) ::= "dynamic_cast\<<t> *>(<v>)" // Should actually use a more specific name. We may have to use other casts as well.
Append(a,b) ::= "<a> + <b>->toString()"
Concat(a,b) ::= "<a><b>"
AppendStr(a,b) ::= "<a> + <b>"
DeclareLocal(s,v) ::= "<s> = <v>"
@ -17,6 +18,9 @@ AssignLocal(s,v) ::= "<s> = <v>;"
InitIntMember(n,v) ::= "int <n> = <v>;"
InitBooleanMember(n,v) ::= "bool <n> = <v>;"
InitIntVar(n,v) ::= <%<InitIntMember(n,v)>%>
IntArg(n) ::= "int <n>"
VarRef(n) ::= "<n>"
GetMember(n) ::= "<n>"
SetMember(n,v) ::= "<n> = <v>;"
@ -68,6 +72,8 @@ bool Property() {
ParserPropertyCall(p, call) ::= "<call>"
PositionAdjustingLexerDef() ::= ""
PositionAdjustingLexer() ::= <<
protected:
class PositionAdjustingLexerATNSimulator : public antlr4::atn::LexerATNSimulator {

View File

@ -14,6 +14,8 @@ Cast(t,v) ::= "<v>"
Append(a,b) ::= "<a> + <b>"
AppendStr(a,b) ::= <%<Append(a,b)>%>
Concat(a,b) ::= "<a><b>"
DeclareLocal(s,v) ::= "var <s> = <v>;"
@ -26,6 +28,12 @@ InitIntMember(n,v) ::= <%this.<n> = <v>;%>
InitBooleanMember(n,v) ::= <%this.<n> = <v>;%>
InitIntVar(n,v) ::= <%<InitIntMember(n,v)>%>
IntArg(n) ::= "<n>"
VarRef(n) ::= "<n>"
GetMember(n) ::= <%this.<n>%>
SetMember(n,v) ::= <%this.<n> = <v>;%>
@ -98,6 +106,8 @@ this.Property = function() {
ParserPropertyCall(p, call) ::= "<p>.<call>"
PositionAdjustingLexerDef() ::= ""
PositionAdjustingLexer() ::= <<
PositionAdjustingLexer.prototype.resetAcceptPosition = function(index, line, column) {

View File

@ -14,6 +14,8 @@ Cast(t,v) ::= "<v>"
Append(a,b) ::= "<a> + <b>"
AppendStr(a,b) ::= <%<Append(a,b)>%>
Concat(a,b) ::= "<a><b>"
DeclareLocal(s,v) ::= "var <s> = <v>;"
@ -26,6 +28,12 @@ InitIntMember(n,v) ::= <%this.<n> = <v>;%>
InitBooleanMember(n,v) ::= <%this.<n> = <v>;%>
InitIntVar(n,v) ::= <%<InitIntMember(n,v)>%>
IntArg(n) ::= "<n>"
VarRef(n) ::= "<n>"
GetMember(n) ::= <%this.<n>%>
SetMember(n,v) ::= <%this.<n> = <v>;%>
@ -100,6 +108,8 @@ this.Property = function() {
ParserPropertyCall(p, call) ::= "<p>.<call>"
PositionAdjustingLexerDef() ::= ""
PositionAdjustingLexer() ::= <<
PositionAdjustingLexer.prototype.resetAcceptPosition = function(index, line, column) {

View File

@ -14,6 +14,8 @@ Cast(t,v) ::= "(<v>)"
Append(a,b) ::= "<a> + fmt.Sprint(<b>)"
AppendStr(a,b) ::= "<a> + <b>"
Concat(a,b) ::= "<a><b>"
DeclareLocal(s, v) ::= "var <s> = <v>"
@ -26,6 +28,12 @@ InitIntMember(n, v) ::= <%var <n> int = <v>; var _ int = <n>; %>
InitBooleanMember(n, v) ::= <%var <n> bool = <v>; var _ bool = <n>; %>
InitIntVar(n,v) ::= <%<InitIntMember(n,v)>%>
IntArg(n) ::= "int <n>"
VarRef(n) ::= "<n>"
GetMember(n) ::= <%<n>%>
SetMember(n, v) ::= <%<n> = <v>;%>
@ -94,6 +102,8 @@ func (p *TParser) Property() bool {
ParserPropertyCall(p, call) ::= "<p>.<call>"
PositionAdjustingLexerDef() ::= ""
PositionAdjustingLexer() ::= <<
func (p *PositionAdjustingLexer) NextToken() antlr.Token {
if _, ok := p.Interpreter.(*PositionAdjustingLexerATNSimulator); !ok {

View File

@ -14,6 +14,8 @@ Cast(t,v) ::= "((<t>)<v>)"
Append(a,b) ::= "<a> + <b>"
AppendStr(a,b) ::= <%<Append(a,b)>%>
Concat(a,b) ::= "<a><b>"
DeclareLocal(s,v) ::= "Object <s> = <v>;"
@ -26,6 +28,12 @@ InitIntMember(n,v) ::= <%int <n> = <v>;%>
InitBooleanMember(n,v) ::= <%boolean <n> = <v>;%>
InitIntVar(n,v) ::= <%<InitIntMember(n,v)>%>
IntArg(n) ::= "int <n>"
VarRef(n) ::= "<n>"
GetMember(n) ::= <%this.<n>%>
SetMember(n,v) ::= <%this.<n> = <v>;%>
@ -94,6 +102,8 @@ boolean Property() {
ParserPropertyCall(p, call) ::= "<p>.<call>"
PositionAdjustingLexerDef() ::= ""
PositionAdjustingLexer() ::= <<
@Override

View File

@ -14,6 +14,8 @@ Cast(t,v) ::= "<v>"
Append(a,b) ::= "<a> + <b>"
AppendStr(a,b) ::= <%<Append(a,b)>%>
Concat(a,b) ::= "<a><b>"
DeclareLocal(s,v) ::= "var <s> = <v>;"
@ -26,6 +28,12 @@ InitIntMember(n,v) ::= <%this.<n> = <v>;%>
InitBooleanMember(n,v) ::= <%this.<n> = <v>;%>
InitIntVar(n,v) ::= <%<InitIntMember(n,v)>%>
IntArg(n) ::= "<n>"
VarRef(n) ::= "<n>"
GetMember(n) ::= <%this.<n>%>
SetMember(n,v) ::= <%this.<n> = <v>;%>
@ -98,6 +106,8 @@ this.Property = function() {
ParserPropertyCall(p, call) ::= "<p>.<call>"
PositionAdjustingLexerDef() ::= ""
PositionAdjustingLexer() ::= <<
PositionAdjustingLexer.prototype.resetAcceptPosition = function(index, line, column) {

View File

@ -0,0 +1,272 @@
writeln(s) ::= <<echo <s> . \PHP_EOL;>>
write(s) ::= <<echo <s>;>>
writeList(s) ::= <<echo <s; separator=".">;>>
False() ::= "false"
True() ::= "true"
Not(v) ::= "!<v>"
Assert(s) ::= <<assert(<s>);>>
Cast(t,v) ::= "<v>"
Append(a,b) ::= "<a> . <b>"
AppendStr(a,b) ::= <%<Append(a,b)>%>
Concat(a,b) ::= "<a><b>"
DeclareLocal(s,v) ::= "<s> = <v>;"
AssertIsList(v) ::= "assert(\is_array(<v>));" // just use static type system
AssignLocal(s,v) ::= "<s> = <v>;"
InitIntMember(n,v) ::= <%public \$<n> = <v>;%>
InitBooleanMember(n,v) ::= <%public \$<n> = <v>;%>
InitIntVar(n,v) ::= <%\$<n> = <v>;%>
IntArg(n) ::= "int <n>"
VarRef(n) ::= "$<n>"
GetMember(n) ::= <%\$this-><n>%>
SetMember(n,v) ::= <%\$this-><n> = <v>;%>
AddMember(n,v) ::= <%\$this-><n> += <v>;%>
PlusMember(v,n) ::= <%<v> + \$this-><n>%>
MemberEquals(n,v) ::= <%\$this-><n> == <v>%>
ModMemberEquals(n,m,v) ::= <%\$this-><n> % <m> == <v>%>
ModMemberNotEquals(n,m,v) ::= <%\$this-><n> % <m> !== <v>%>
DumpDFA() ::= "\$this->dumpDFA();"
Pass() ::= ""
StringList() ::= ""
BuildParseTrees() ::= "\$this->setBuildParseTree(true);"
BailErrorStrategy() ::= <%\$this->setErrorHandler(new Antlr\\Antlr4\\Runtime\\Error\\BailErrorStrategy());%>
ToStringTree(s) ::= <%<s>->toStringTree(\$this->getRuleNames())%>
Column() ::= "\$this->getCharPositionInLine()"
Text() ::= "\$this->getText()"
ValEquals(a,b) ::= <%<a>===<b>%>
TextEquals(a) ::= <%\$this->getText() === "<a>"%>
PlusText(a) ::= <%"<a>" . \$this->getText()%>
InputText() ::= "\$this->input->getText()"
LTEquals(i, v) ::= <%\$this->input->LT(<i>)->getText() === <v>%>
LANotEquals(i, v) ::= <%\$this->input->LA(<i>) !== <v>%>
TokenStartColumnEquals(i) ::= <%\$this->tokenStartCharPositionInLine === <i>%>
ImportListener(X) ::= ""
GetExpectedTokenNames() ::= "\$this->getExpectedTokens()->toStringVocabulary(\$this->getVocabulary())"
RuleInvocationStack() ::= "'[' . \implode(', ', \$this->getRuleInvocationStack()) . ']'"
LL_EXACT_AMBIG_DETECTION() ::= <<\$this->interp->setPredictionMode(Antlr\\Antlr4\\Runtime\\Atn\\PredictionMode::LL_EXACT_AMBIG_DETECTION);>>
ParserToken(parser, token) ::= <%<parser>::<token>%>
Production(p) ::= <%<p>%>
Result(r) ::= <%<r>%>
ParserPropertyMember() ::= <<
@members {
public function Property() : bool
{
return true;
}
}
>>
ParserPropertyCall(p, call) ::= "<p>-><call>"
PositionAdjustingLexerDef() ::= <<
class PositionAdjustingLexerATNSimulator extends LexerATNSimulator {
public function resetAcceptPosition(CharStream \$input, int \$index, int \$line, int \$charPositionInLine) : void {
\$input->seek(\$index);
\$this->line = \$line;
\$this->charPositionInLine = \$charPositionInLine;
\$this->consume(\$input);
}
}
>>
PositionAdjustingLexer() ::= <<
public function nextToken() : Antlr\\Antlr4\\Runtime\\Token
{
if (!\$this->interp instanceof PositionAdjustingLexerATNSimulator) {
\$this->interp = new PositionAdjustingLexerATNSimulator(\$this, self::\$atn, self::\$decisionToDFA, self::\$sharedContextCache);
}
return parent::nextToken();
}
public function emit() : Antlr\\Antlr4\\Runtime\\Token
{
switch (\$this->type) {
case self::TOKENS:
\$this->handleAcceptPositionForKeyword('tokens');
break;
case self::LABEL:
\$this->handleAcceptPositionForIdentifier();
break;
}
return parent::emit();
}
private function handleAcceptPositionForIdentifier() : bool
{
\$tokenText = \$this->getText();
\$identifierLength = 0;
while (\$identifierLength \< \strlen(\$tokenText) && self::isIdentifierChar(\$tokenText[\$identifierLength])) {
\$identifierLength++;
}
if (\$this->getInputStream()->getIndex() > \$this->tokenStartCharIndex + \$identifierLength) {
\$offset = \$identifierLength - 1;
\$this->getInterpreter()->resetAcceptPosition(\$this->getInputStream(), \$this->tokenStartCharIndex + \$offset, \$this->tokenStartLine, \$this->tokenStartCharPositionInLine + \$offset);
return true;
}
return false;
}
private function handleAcceptPositionForKeyword(string \$keyword) : bool
{
if (\$this->getInputStream()->getIndex() > \$this->tokenStartCharIndex + \strlen(\$keyword)) {
\$offset = \strlen(\$keyword) - 1;
\$this->getInterpreter()->resetAcceptPosition(\$this->getInputStream(), \$this->tokenStartCharIndex + \$offset, \$this->tokenStartLine, \$this->tokenStartCharPositionInLine + \$offset);
return true;
}
return false;
}
private static function isIdentifierChar(string \$c) : bool
{
return \ctype_alnum(\$c) || \$c === '_';
}
>>
BasicListener(X) ::= <<
@parser::definitions {
class LeafListener extends TBaseListener
{
public function visitTerminal(Antlr\\Antlr4\\Runtime\\Tree\\TerminalNode \$node) : void
{
echo \$node->getSymbol()->getText() . \PHP_EOL;
}
}
}
>>
WalkListener(s) ::= <<
\$walker = new Antlr\\Antlr4\\Runtime\\Tree\\ParseTreeWalker();
\$walker->walk(new LeafListener(), <s>);
>>
TreeNodeWithAltNumField(X) ::= <<
@parser::contexts {
class MyRuleNode extends ParserRuleContext
{
public \$altNum;
public function getAltNumber() : int
{
return \$this->altNum;
}
public function setAltNumber(int \$altNum) : void
{
\$this->altNum = \$altNum;
}
}
}
>>
TokenGetterListener(X) ::= <<
@parser::definitions {
class LeafListener extends TBaseListener {
public function exitA(Context\\AContext \$ctx) : void {
if (\$ctx->getChildCount() === 2) {
echo \sprintf('%s %s [%s]',\$ctx->INT(0)->getSymbol()->getText(), \$ctx->INT(1)->getSymbol()->getText(), \implode(', ', \$ctx->INT())) . \PHP_EOL;
} else {
echo \$ctx->ID()->getSymbol() . \PHP_EOL;
}
}
}
}
>>
RuleGetterListener(X) ::= <<
@parser::definitions {
class LeafListener extends TBaseListener {
public function exitA(Context\\AContext \$ctx) : void
{
if (\$ctx->getChildCount() === 2) {
echo \sprintf('%s %s %s', \$ctx->b(0)->start->getText(), \$ctx->b(1)->start->getText(),\$ctx->b()[0]->start->getText()) . \PHP_EOL;
} else {
echo \$ctx->b(0)->start->getText() . \PHP_EOL;
}
}
}
}
>>
LRListener(X) ::= <<
@parser::definitions {
class LeafListener extends TBaseListener {
public function exitE(Context\\EContext \$ctx) : void
{
if (\$ctx->getChildCount() === 3) {
echo \sprintf('%s %s %s', \$ctx->e(0)->start->getText(), \$ctx->e(1)->start->getText(), \$ctx->e()[0]->start->getText()) . \PHP_EOL;
} else {
echo \$ctx->INT()->getSymbol()->getText() . \PHP_EOL;
}
}
}
}
>>
LRWithLabelsListener(X) ::= <<
@parser::definitions {
class LeafListener extends TBaseListener
{
public function exitCall(Context\\CallContext \$ctx) : void {
echo \sprintf('%s %s',\$ctx->e()->start->getText(),\$ctx->eList()) . \PHP_EOL;
}
public function exitInt(Context\\IntContext \$ctx) : void {
echo \$ctx->INT()->getSymbol()->getText() . \PHP_EOL;
}
}
}
>>
DeclareContextListGettersFunction() ::= <<
public function foo() : void {
\$s = null;
\$a = \$s->a();
\$b = \$s->b();
}
>>
Declare_foo() ::= <<
public function foo() : void {
echo 'foo' . \PHP_EOL;
}
>>
Invoke_foo() ::= "\$this->foo();"
Declare_pred() ::= <<public function pred(bool \$v) : bool {
echo "eval=".(\$v ? 'true' : 'false') . \PHP_EOL;
return \$v;
}
>>
Invoke_pred(v) ::= "\$this->pred(<v>)"
ParserTokenType(t) ::= "Parser::<t>"
ContextRuleFunction(ctx, rule) ::= "<ctx>-><rule>"
StringType() ::= ""
ContextMember(ctx, subctx, member) ::= "<ctx>-><subctx>-><member>"

View File

@ -14,6 +14,8 @@ Cast(t,v) ::= "<v>"
Append(a,b) ::= "<a> + str(<b>)"
AppendStr(a,b) ::= "<a> + <b>"
Concat(a,b) ::= "<a><b>"
DeclareLocal(s,v) ::= "<s> = <v>"
@ -26,6 +28,12 @@ InitIntMember(n,v) ::= <%<n> = <v>%>
InitBooleanMember(n,v) ::= <%<n> = <v>%>
InitIntVar(n,v) ::= <%<InitIntMember(n,v)>%>
IntArg(n) ::= "<n>"
VarRef(n) ::= "<n>"
GetMember(n) ::= <%self.<n>%>
SetMember(n,v) ::= <%self.<n> = <v>%>
@ -94,6 +102,8 @@ def Property(self):
ParserPropertyCall(p, call) ::= "<p>.<call>"
PositionAdjustingLexerDef() ::= ""
PositionAdjustingLexer() ::= <<
def resetAcceptPosition(self, index, line, column):

View File

@ -14,6 +14,8 @@ Cast(t,v) ::= "<v>"
Append(a,b) ::= "<a> + str(<b>)"
AppendStr(a,b) ::= "<a> + <b>"
Concat(a,b) ::= "<a><b>"
DeclareLocal(s,v) ::= "<s> = <v>"
@ -26,6 +28,12 @@ InitIntMember(n,v) ::= <%<n> = <v>%>
InitBooleanMember(n,v) ::= <%<n> = <v>%>
InitIntVar(n,v) ::= <%<InitIntMember(n,v)>%>
IntArg(n) ::= "<n>"
VarRef(n) ::= "<n>"
GetMember(n) ::= <%self.<n>%>
SetMember(n,v) ::= <%self.<n> = <v>%>
@ -99,6 +107,8 @@ def Property(self):
ParserPropertyCall(p, call) ::= "<p>.<call>"
PositionAdjustingLexerDef() ::= ""
PositionAdjustingLexer() ::= <<
def resetAcceptPosition(self, index, line, column):

View File

@ -14,6 +14,8 @@ Cast(t,v) ::= "<v>"
Append(a,b) ::= "<a> + <b>"
AppendStr(a,b) ::= <%<Append(a,b)>%>
Concat(a,b) ::= "<a><b>"
DeclareLocal(s,v) ::= "var <s> = <v>;"
@ -26,6 +28,12 @@ InitIntMember(n,v) ::= <%this.<n> = <v>;%>
InitBooleanMember(n,v) ::= <%this.<n> = <v>;%>
InitIntVar(n,v) ::= <%<InitIntMember(n,v)>%>
IntArg(n) ::= "<n>"
VarRef(n) ::= "<n>"
GetMember(n) ::= <%this.<n>%>
SetMember(n,v) ::= <%this.<n> = <v>;%>
@ -98,6 +106,8 @@ this.Property = function() {
ParserPropertyCall(p, call) ::= "<p>.<call>"
PositionAdjustingLexerDef() ::= ""
PositionAdjustingLexer() ::= <<
PositionAdjustingLexer.prototype.resetAcceptPosition = function(index, line, column) {

View File

@ -14,6 +14,8 @@ Cast(t,v) ::= "((<v> as! <t>))"
Append(a,b) ::= "<a> + <b>"
AppendStr(a,b) ::= <%<Append(a,b)>%>
Concat(a,b) ::= "<a><b>"
DeclareLocal(s,v) ::= "var <s> = <v>"
@ -26,6 +28,12 @@ InitIntMember(n,v) ::= <%var <n> = <v>%>
InitBooleanMember(n,v) ::= <%var <n> = <v>%>
InitIntVar(n,v) ::= <%<InitIntMember(n,v)>%>
IntArg(n) ::= "int <n>"
VarRef(n) ::= "<n>"
GetMember(n) ::= <%self.<n>%>
SetMember(n,v) ::= <%self.<n> = <v>%>
@ -96,6 +104,8 @@ ParserPropertyCall(parser, property) ::= <<
<parser>.<property>
>>
PositionAdjustingLexerDef() ::= ""
PositionAdjustingLexer() ::= <<
override

View File

@ -50,6 +50,7 @@ public abstract class BaseRuntimeTest {
"Go",
"CSharp",
"Python2", "Python3",
"PHP",
"Node", "Safari", "Firefox", "Explorer", "Chrome"
};
public final static String[] JavaScriptTargets = {
@ -116,6 +117,7 @@ public abstract class BaseRuntimeTest {
System.out.printf("Ignore "+descriptor);
return;
}
if ( descriptor.getTestType().contains("Parser") ) {
testParser(descriptor);
}

View File

@ -418,7 +418,7 @@ public class CompositeParsersDescriptors {
parser grammar S;
type_ : 'int' ;
decl : type_ ID ';'
| type_ ID init_ ';' {<write("\"JavaDecl: \" + $text")>};
| type_ ID init_ ';' {<AppendStr("\"JavaDecl: \"","$text"):writeln()>};
init_ : '=' INT;
*/
@CommentHasStringValue
@ -532,7 +532,7 @@ public class CompositeParsersDescriptors {
/**
parser grammar S;
a @after {<InitIntMember("x","0")>} : B;
a @after {<InitIntVar("x","0")>} : B;
*/
@CommentHasStringValue
public String slaveGrammarS;

View File

@ -372,7 +372,7 @@ public class FullContextParsingDescriptors {
: expr_or_assign*;
expr_or_assign
: expr '++' {<writeln("\"fail.\"")>}
| expr {<writeln("\"pass: \"+$expr.text")>}
| expr {<AppendStr("\"pass: \"","$expr.text"):writeln()>}
;
expr: expr_primary ('\<-' ID)?;
expr_primary

View File

@ -529,8 +529,8 @@ public class LeftRecursionDescriptors {
| e '+' e {$v = <Cast("BinaryContext","$ctx"):ContextMember({<Production("e")>(0)}, {<Result("v")>})> + <Cast("BinaryContext","$ctx"):ContextMember({<Production("e")>(1)}, {<Result("v")>})>;} # binary
| INT {$v = $INT.int;} # anInt
| '(' e ')' {$v = $e.v;} # parens
| left=e INC {<Cast("UnaryContext","$ctx"):Concat(".INC() != null"):Assert()>$v = $left.v + 1;} # unary
| left=e DEC {<Cast("UnaryContext","$ctx"):Concat(".DEC() != null"):Assert()>$v = $left.v - 1;} # unary
| left=e INC {<ContextRuleFunction(Cast("UnaryContext","$ctx"), "INC()"):Concat(" != null"):Assert()>$v = $left.v + 1;} # unary
| left=e DEC {<ContextRuleFunction(Cast("UnaryContext","$ctx"), "DEC()"):Concat(" != null"):Assert()>$v = $left.v - 1;} # unary
| ID {<AssignLocal("$v","3")>} # anID
;
ID : 'a'..'z'+ ;
@ -636,9 +636,9 @@ public class LeftRecursionDescriptors {
grammar T;
s : e {<writeln("$e.result")>} ;
e returns [<StringType()> result]
: ID '=' e1=e {$result = "(" + $ID.text + "=" + $e1.result + ")";}
: ID '=' e1=e {$result = <AppendStr("\"(\"", AppendStr("$ID.text", AppendStr("\"=\"", AppendStr("$e1.result", "\")\""))))>;}
| ID {$result = $ID.text;}
| e1=e '+' e2=e {$result = "(" + $e1.result + "+" + $e2.result + ")";}
| e1=e '+' e2=e {$result = <AppendStr("\"(\"", AppendStr("$e1.result", AppendStr("\"+\"", AppendStr("$e2.result", "\")\""))))>;}
;
ID : 'a'..'z'+ ;
INT : '0'..'9'+ ;

View File

@ -766,6 +766,10 @@ public class LexerExecDescriptors {
/**
lexer grammar PositionAdjustingLexer;
@definitions {
<PositionAdjustingLexerDef()>
}
@members {
<PositionAdjustingLexer()>
}

View File

@ -144,8 +144,8 @@ public class SemPredEvalParserDescriptors {
/**
grammar T;
s : b[2] ';' | b[2] '.' ; // decision in s drills down to ctx-dependent pred in a;
b[int i] : a[i] ;
a[int i]
b[<IntArg("i")>] : a[<VarRef("i")>] ;
a[<IntArg("i")>]
: {<ValEquals("$i","1")>}? ID {<writeln("\"alt 1\"")>}
| {<ValEquals("$i","2")>}? ID {<writeln("\"alt 2\"")>}
;
@ -310,7 +310,7 @@ public class SemPredEvalParserDescriptors {
grammar T;
@parser::members {<InitBooleanMember("enumKeyword",True())>}
primary
: ID {<writeln("\"ID \"+$ID.text")>}
: ID {<AppendStr("\"ID \"", "$ID.text"):writeln()>}
| {<GetMember("enumKeyword"):Not()>}? 'enum' {<writeln("\"enum\"")>}
;
ID : [a-z]+ ;

View File

@ -0,0 +1,599 @@
/*
* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/
package org.antlr.v4.test.runtime.php;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.antlr.v4.Tool;
import org.antlr.v4.automata.LexerATNFactory;
import org.antlr.v4.automata.ParserATNFactory;
import org.antlr.v4.runtime.atn.ATN;
import org.antlr.v4.runtime.atn.ATNDeserializer;
import org.antlr.v4.runtime.atn.ATNSerializer;
import org.antlr.v4.semantics.SemanticPipeline;
import org.antlr.v4.test.runtime.ErrorQueue;
import org.antlr.v4.test.runtime.RuntimeTestSupport;
import org.antlr.v4.test.runtime.StreamVacuum;
import org.antlr.v4.tool.Grammar;
import org.antlr.v4.tool.LexerGrammar;
import org.stringtemplate.v4.ST;
import static org.antlr.v4.test.runtime.BaseRuntimeTest.antlrOnString;
import static org.antlr.v4.test.runtime.BaseRuntimeTest.writeFile;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class BasePHPTest implements RuntimeTestSupport {
public static final String newline = System.getProperty("line.separator");
public String tmpdir = null;
/**
* If error during parser execution, store stderr here; can't return
* stdout and stderr. This doesn't trap errors from running antlr.
*/
protected String stderrDuringParse;
/**
* Errors found while running antlr
*/
protected StringBuilder antlrToolErrors;
private String getPropertyPrefix() {
return "antlr-php";
}
@Override
public void testSetUp() throws Exception {
// new output dir for each test
String propName = getPropertyPrefix() + "-test-dir";
String prop = System.getProperty(propName);
if (prop != null && prop.length() > 0) {
tmpdir = prop;
} else {
String classSimpleName = getClass().getSimpleName();
String threadName = Thread.currentThread().getName();
String childPath = String.format("%s-%s-%s", classSimpleName, threadName, System.currentTimeMillis());
tmpdir = new File(System.getProperty("java.io.tmpdir"), childPath).getAbsolutePath();
}
antlrToolErrors = new StringBuilder();
}
@Override
public void testTearDown() throws Exception {
}
@Override
public String getTmpDir() {
return tmpdir;
}
@Override
public String getStdout() {
return null;
}
@Override
public String getParseErrors() {
return stderrDuringParse;
}
@Override
public String getANTLRToolErrors() {
if (antlrToolErrors.length() == 0) {
return null;
}
return antlrToolErrors.toString();
}
protected ATN createATN(Grammar g, boolean useSerializer) {
if (g.atn == null) {
semanticProcess(g);
assertEquals(0, g.tool.getNumErrors());
ParserATNFactory f;
if (g.isLexer()) {
f = new LexerATNFactory((LexerGrammar) g);
} else {
f = new ParserATNFactory(g);
}
g.atn = f.createATN();
assertEquals(0, g.tool.getNumErrors());
}
ATN atn = g.atn;
if (useSerializer) {
char[] serialized = ATNSerializer.getSerializedAsChars(atn);
return new ATNDeserializer().deserialize(serialized);
}
return atn;
}
protected void semanticProcess(Grammar g) {
if (g.ast != null && !g.ast.hasErrors) {
Tool antlr = new Tool();
SemanticPipeline sem = new SemanticPipeline(g);
sem.process();
if (g.getImportedGrammars() != null) {
for (Grammar imp: g.getImportedGrammars()) {
antlr.processNonCombinedGrammar(imp, false);
}
}
}
}
protected String execLexer(
String grammarFileName,
String grammarStr,
String lexerName,
String input
) {
return execLexer(grammarFileName, grammarStr, lexerName, input, false);
}
@Override
public String execLexer(
String grammarFileName,
String grammarStr,
String lexerName,
String input,
boolean showDFA
) {
boolean success = rawGenerateAndBuildRecognizer(
grammarFileName,
grammarStr,
null,
lexerName,
"-no-listener"
);
assertTrue(success);
writeFile(tmpdir, "input", input);
writeLexerTestFile(lexerName, showDFA);
String output = execModule("Test.php");
return output;
}
public String execParser(
String grammarFileName,
String grammarStr,
String parserName,
String lexerName,
String listenerName,
String visitorName,
String startRuleName,
String input,
boolean showDiagnosticErrors
) {
return execParser_(
grammarFileName,
grammarStr,
parserName,
lexerName,
listenerName,
visitorName,
startRuleName,
input,
showDiagnosticErrors,
false
);
}
public String execParser_(
String grammarFileName,
String grammarStr,
String parserName,
String lexerName,
String listenerName,
String visitorName,
String startRuleName,
String input,
boolean debug,
boolean trace
) {
boolean success = rawGenerateAndBuildRecognizer(
grammarFileName,
grammarStr,
parserName,
lexerName,
"-visitor"
);
assertTrue(success);
writeFile(tmpdir, "input", input);
rawBuildRecognizerTestFile(
parserName,
lexerName,
listenerName,
visitorName,
startRuleName,
debug,
trace
);
return execRecognizer();
}
/**
* Return true if all is well
*/
protected boolean rawGenerateAndBuildRecognizer(
String grammarFileName,
String grammarStr,
String parserName,
String lexerName,
String... extraOptions
) {
return rawGenerateAndBuildRecognizer(
grammarFileName,
grammarStr,
parserName,
lexerName,
false,
extraOptions
);
}
/**
* Return true if all is well
*/
protected boolean rawGenerateAndBuildRecognizer(
String grammarFileName,
String grammarStr,
String parserName,
String lexerName,
boolean defaultListener,
String... extraOptions
) {
ErrorQueue equeue = antlrOnString(getTmpDir(), "PHP", grammarFileName, grammarStr, defaultListener, extraOptions);
if (!equeue.errors.isEmpty()) {
return false;
}
List<String> files = new ArrayList<String>();
if (lexerName != null) {
files.add(lexerName + ".php");
}
if (parserName != null) {
files.add(parserName + ".php");
Set<String> optionsSet = new HashSet<String>(Arrays.asList(extraOptions));
if (!optionsSet.contains("-no-listener")) {
files.add(grammarFileName.substring(0, grammarFileName.lastIndexOf('.')) + "Listener.php");
}
if (optionsSet.contains("-visitor")) {
files.add(grammarFileName.substring(0, grammarFileName.lastIndexOf('.')) + "Visitor.php");
}
}
return true;
}
protected void rawBuildRecognizerTestFile(
String parserName,
String lexerName,
String listenerName,
String visitorName,
String parserStartRuleName,
boolean debug,
boolean trace
) {
this.stderrDuringParse = null;
if (parserName == null) {
writeLexerTestFile(lexerName, false);
} else {
writeParserTestFile(
parserName,
lexerName,
listenerName,
visitorName,
parserStartRuleName,
debug,
trace
);
}
}
public String execRecognizer() {
return execModule("Test.php");
}
public String execModule(String fileName) {
String phpPath = locatePhp();
String runtimePath = locateRuntime();
File tmpdirFile = new File(tmpdir);
String modulePath = new File(tmpdirFile, fileName).getAbsolutePath();
String inputPath = new File(tmpdirFile, "input").getAbsolutePath();
Path outputPath = tmpdirFile.toPath().resolve("output").toAbsolutePath();
try {
ProcessBuilder builder = new ProcessBuilder(phpPath, modulePath, inputPath, outputPath.toString());
builder.environment().put("RUNTIME", runtimePath);
builder.directory(tmpdirFile);
Process process = builder.start();
StreamVacuum stdoutVacuum = new StreamVacuum(process.getInputStream());
StreamVacuum stderrVacuum = new StreamVacuum(process.getErrorStream());
stdoutVacuum.start();
stderrVacuum.start();
process.waitFor();
stdoutVacuum.join();
stderrVacuum.join();
String output = stdoutVacuum.toString();
if (output.length() == 0) {
output = null;
}
if (stderrVacuum.toString().length() > 0) {
this.stderrDuringParse = stderrVacuum.toString();
}
return output;
} catch (Exception e) {
System.err.println("can't exec recognizer");
e.printStackTrace(System.err);
}
return null;
}
private String locateTool(String tool) {
final String phpPath = System.getProperty("PHP_PATH");
if (phpPath != null && new File(phpPath).exists()) {
return phpPath;
}
String[] roots = {"/usr/local/bin/", "/opt/local/bin", "/usr/bin/"};
for (String root: roots) {
if (new File(root + tool).exists()) {
return root + tool;
}
}
throw new RuntimeException("Could not locate " + tool);
}
protected String locatePhp() {
String propName = getPropertyPrefix() + "-php";
String prop = System.getProperty(propName);
if (prop == null || prop.length() == 0) {
prop = locateTool("php");
}
File file = new File(prop);
if (!file.exists()) {
throw new RuntimeException("Missing system property:" + propName);
}
return file.getAbsolutePath();
}
protected String locateRuntime() {
String propName = "antlr-php-runtime";
String prop = System.getProperty(propName);
if (prop == null || prop.length() == 0) {
prop = "../runtime/PHP";
}
File file = new File(prop);
if (!file.exists()) {
throw new RuntimeException("Missing system property:" + propName);
}
try {
return file.getCanonicalPath();
} catch (IOException e) {
return file.getAbsolutePath();
}
}
protected void mkdir(String dir) {
File f = new File(dir);
f.mkdirs();
}
protected void writeLexerTestFile(String lexerName, boolean showDFA) {
ST outputFileST = new ST(
"\\<?php\n"
+ "\n"
+ "declare(strict_types=1);\n"
+ "\n"
+ "use Antlr\\Antlr4\\Runtime\\CommonTokenStream;\n"
+ "use Antlr\\Antlr4\\Runtime\\Error\\Listeners\\ConsoleErrorListener;\n"
+ "use Antlr\\Antlr4\\Runtime\\InputStream;\n"
+ "use Antlr\\Antlr4\\Runtime\\Lexer;\n"
+ "\n"
+ "$runtime = \\getenv('RUNTIME');\n"
+ "\n"
+ "\\spl_autoload_register(function (string $class) use ($runtime) : void {\n"
+ " $file = \\str_replace('\\\\\\', \\DIRECTORY_SEPARATOR, \\str_replace('Antlr\\Antlr4\\Runtime\\\\\\', $runtime . '\\\\\\src\\\\\\', $class)) . '.php';\n"
+ "\n"
+ " if (\\file_exists($file)) {\n"
+ " require_once $file; \n"
+ " }\n"
+ "});"
+ "\n"
+ "$input = InputStream::fromPath($argv[1]);\n"
+ "$lexer = new <lexerName>($input);\n"
+ "$lexer->addErrorListener(new ConsoleErrorListener());"
+ "$tokens = new CommonTokenStream($lexer);\n"
+ "$tokens->fill();\n"
+ "\n"
+ "foreach ($tokens->getAllTokens() as $token) {\n"
+ " echo $token . \\PHP_EOL;\n"
+ "}"
+ (showDFA
? "echo $lexer->getInterpreter()->getDFA(Lexer::DEFAULT_MODE)->toLexerString();\n"
: "")
);
outputFileST.add("lexerName", lexerName);
writeFile(tmpdir, "Test.php", outputFileST.render());
}
protected void writeParserTestFile(
String parserName, String lexerName,
String listenerName, String visitorName,
String parserStartRuleName, boolean debug, boolean trace
) {
if (!parserStartRuleName.endsWith(")")) {
parserStartRuleName += "()";
}
ST outputFileST = new ST(
"\\<?php\n"
+ "\n"
+ "declare(strict_types=1);\n"
+ "\n"
+ "use Antlr\\Antlr4\\Runtime\\CommonTokenStream;\n"
+ "use Antlr\\Antlr4\\Runtime\\Error\\Listeners\\DiagnosticErrorListener;\n"
+ "use Antlr\\Antlr4\\Runtime\\Error\\Listeners\\ConsoleErrorListener;\n"
+ "use Antlr\\Antlr4\\Runtime\\InputStream;\n"
+ "use Antlr\\Antlr4\\Runtime\\ParserRuleContext;\n"
+ "use Antlr\\Antlr4\\Runtime\\Tree\\ErrorNode;\n"
+ "use Antlr\\Antlr4\\Runtime\\Tree\\ParseTreeListener;\n"
+ "use Antlr\\Antlr4\\Runtime\\Tree\\ParseTreeWalker;\n"
+ "use Antlr\\Antlr4\\Runtime\\Tree\\RuleNode;\n"
+ "use Antlr\\Antlr4\\Runtime\\Tree\\TerminalNode;\n"
+ "\n"
+ "$runtime = \\getenv('RUNTIME');\n"
+ "\n"
+ "\\spl_autoload_register(function (string $class) use ($runtime) : void {\n"
+ " $file = \\str_replace('\\\\\\', \\DIRECTORY_SEPARATOR, \\str_replace('Antlr\\Antlr4\\Runtime\\\\\\', $runtime . '\\\\\\src\\\\\\', $class)) . '.php';\n"
+ "\n"
+ " if (\\file_exists($file)) {\n"
+ " require_once $file; \n"
+ " }\n"
+ "});\n"
+ "\n"
+ "final class TreeShapeListener implements ParseTreeListener {\n"
+ " public function visitTerminal(TerminalNode $node) : void {}\n"
+ " public function visitErrorNode(ErrorNode $node) : void {}\n"
+ " public function exitEveryRule(ParserRuleContext $ctx) : void {}\n"
+ "\n"
+ " public function enterEveryRule(ParserRuleContext $ctx) : void {\n"
+ " for ($i = 0, $count = $ctx->getChildCount(); $i \\< $count; $i++) {\n"
+ " $parent = $ctx->getChild($i)->getParent();\n"
+ "\n"
+ " if (!($parent instanceof RuleNode) || $parent->getRuleContext() !== $ctx) {\n"
+ " throw new RuntimeException('Invalid parse tree shape detected.');\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}"
+ "\n"
+ "$input = InputStream::fromPath($argv[1]);\n"
+ "$lexer = new <lexerName>($input);\n"
+ "$lexer->addErrorListener(new ConsoleErrorListener());"
+ "$tokens = new CommonTokenStream($lexer);\n"
+ "<createParser>"
+ "$parser->addErrorListener(new ConsoleErrorListener());"
+ "$parser->setBuildParseTree(true);\n"
+ "$tree = $parser-><parserStartRuleName>;\n\n"
+ "ParseTreeWalker::default()->walk(new TreeShapeListener(), $tree);\n"
);
String stSource = "$parser = new <parserName>($tokens);\n";
if (debug) {
stSource += "$parser->addErrorListener(new DiagnosticErrorListener());\n";
}
if (trace) {
stSource += "$parser->setTrace(true);\n";
}
ST createParserST = new ST(stSource);
outputFileST.add("createParser", createParserST);
outputFileST.add("parserName", parserName);
outputFileST.add("lexerName", lexerName);
outputFileST.add("listenerName", listenerName);
outputFileST.add("visitorName", visitorName);
outputFileST.add("parserStartRuleName", parserStartRuleName);
writeFile(tmpdir, "Test.php", outputFileST.render());
}
protected void eraseFiles(File dir) {
String[] files = dir.list();
for (int i = 0; files != null && i < files.length; i++) {
new File(dir, files[i]).delete();
}
}
@Override
public void eraseTempDir() {
boolean doErase = true;
String propName = getPropertyPrefix() + "-erase-test-dir";
String prop = System.getProperty(propName);
if (prop != null && prop.length() > 0) {
doErase = Boolean.getBoolean(prop);
}
if (doErase) {
File tmpdirF = new File(tmpdir);
if (tmpdirF.exists()) {
eraseFiles(tmpdirF);
tmpdirF.delete();
}
}
}
/**
* Sort a list
*/
public <T extends Comparable<? super T>> List<T> sort(List<T> data) {
List<T> dup = new ArrayList<T>();
dup.addAll(data);
Collections.sort(dup);
return dup;
}
/**
* Return map sorted by key
*/
public <K extends Comparable<? super K>, V> LinkedHashMap<K, V> sort(Map<K, V> data) {
LinkedHashMap<K, V> dup = new LinkedHashMap<K, V>();
List<K> keys = new ArrayList<K>();
keys.addAll(data.keySet());
Collections.sort(keys);
for (K k: keys) {
dup.put(k, data.get(k));
}
return dup;
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/
package org.antlr.v4.test.runtime.php;
import org.antlr.v4.test.runtime.BaseRuntimeTest;
import org.antlr.v4.test.runtime.RuntimeTestDescriptor;
import org.antlr.v4.test.runtime.descriptors.CompositeLexersDescriptors;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class TestCompositeLexers extends BaseRuntimeTest {
public TestCompositeLexers(RuntimeTestDescriptor descriptor) {
super(descriptor,new BasePHPTest());
}
@Parameterized.Parameters(name="{0}")
public static RuntimeTestDescriptor[] getAllTestDescriptors() {
return BaseRuntimeTest.getRuntimeTestDescriptors(CompositeLexersDescriptors.class, "PHP");
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/
package org.antlr.v4.test.runtime.php;
import org.antlr.v4.test.runtime.BaseRuntimeTest;
import org.antlr.v4.test.runtime.RuntimeTestDescriptor;
import org.antlr.v4.test.runtime.descriptors.CompositeParsersDescriptors;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class TestCompositeParsers extends BaseRuntimeTest {
public TestCompositeParsers(RuntimeTestDescriptor descriptor) {
super(descriptor,new BasePHPTest());
}
@Parameterized.Parameters(name="{0}")
public static RuntimeTestDescriptor[] getAllTestDescriptors() {
return BaseRuntimeTest.getRuntimeTestDescriptors(CompositeParsersDescriptors.class, "PHP");
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/
package org.antlr.v4.test.runtime.php;
import org.antlr.v4.test.runtime.BaseRuntimeTest;
import org.antlr.v4.test.runtime.RuntimeTestDescriptor;
import org.antlr.v4.test.runtime.descriptors.FullContextParsingDescriptors;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class TestFullContextParsing extends BaseRuntimeTest {
public TestFullContextParsing(RuntimeTestDescriptor descriptor) {
super(descriptor,new BasePHPTest());
}
@Parameterized.Parameters(name="{0}")
public static RuntimeTestDescriptor[] getAllTestDescriptors() {
return BaseRuntimeTest.getRuntimeTestDescriptors(FullContextParsingDescriptors.class, "PHP");
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/
package org.antlr.v4.test.runtime.php;
import org.antlr.v4.test.runtime.BaseRuntimeTest;
import org.antlr.v4.test.runtime.RuntimeTestDescriptor;
import org.antlr.v4.test.runtime.descriptors.LeftRecursionDescriptors;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class TestLeftRecursion extends BaseRuntimeTest {
public TestLeftRecursion(RuntimeTestDescriptor descriptor) {
super(descriptor,new BasePHPTest());
}
@Parameterized.Parameters(name="{0}")
public static RuntimeTestDescriptor[] getAllTestDescriptors() {
return BaseRuntimeTest.getRuntimeTestDescriptors(LeftRecursionDescriptors.class, "PHP");
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/
package org.antlr.v4.test.runtime.php;
import org.antlr.v4.test.runtime.BaseRuntimeTest;
import org.antlr.v4.test.runtime.RuntimeTestDescriptor;
import org.antlr.v4.test.runtime.descriptors.LexerErrorsDescriptors;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class TestLexerErrors extends BaseRuntimeTest {
public TestLexerErrors(RuntimeTestDescriptor descriptor) {
super(descriptor,new BasePHPTest());
}
@Parameterized.Parameters(name="{0}")
public static RuntimeTestDescriptor[] getAllTestDescriptors() {
return BaseRuntimeTest.getRuntimeTestDescriptors(LexerErrorsDescriptors.class, "PHP");
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/
package org.antlr.v4.test.runtime.php;
import org.antlr.v4.test.runtime.BaseRuntimeTest;
import org.antlr.v4.test.runtime.RuntimeTestDescriptor;
import org.antlr.v4.test.runtime.descriptors.LexerExecDescriptors;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class TestLexerExec extends BaseRuntimeTest {
public TestLexerExec(RuntimeTestDescriptor descriptor) {
super(descriptor,new BasePHPTest());
}
@Parameterized.Parameters(name="{0}")
public static RuntimeTestDescriptor[] getAllTestDescriptors() {
return BaseRuntimeTest.getRuntimeTestDescriptors(LexerExecDescriptors.class, "PHP");
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/
package org.antlr.v4.test.runtime.php;
import org.antlr.v4.test.runtime.BaseRuntimeTest;
import org.antlr.v4.test.runtime.RuntimeTestDescriptor;
import org.antlr.v4.test.runtime.descriptors.ListenersDescriptors;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class TestListeners extends BaseRuntimeTest {
public TestListeners(RuntimeTestDescriptor descriptor) {
super(descriptor,new BasePHPTest());
}
@Parameterized.Parameters(name="{0}")
public static RuntimeTestDescriptor[] getAllTestDescriptors() {
return BaseRuntimeTest.getRuntimeTestDescriptors(ListenersDescriptors.class, "PHP");
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/
package org.antlr.v4.test.runtime.php;
import org.antlr.v4.test.runtime.BaseRuntimeTest;
import org.antlr.v4.test.runtime.RuntimeTestDescriptor;
import org.antlr.v4.test.runtime.descriptors.ParseTreesDescriptors;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class TestParseTrees extends BaseRuntimeTest {
public TestParseTrees(RuntimeTestDescriptor descriptor) {
super(descriptor,new BasePHPTest());
}
@Parameterized.Parameters(name="{0}")
public static RuntimeTestDescriptor[] getAllTestDescriptors() {
return BaseRuntimeTest.getRuntimeTestDescriptors(ParseTreesDescriptors.class, "PHP");
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/
package org.antlr.v4.test.runtime.php;
import org.antlr.v4.test.runtime.BaseRuntimeTest;
import org.antlr.v4.test.runtime.RuntimeTestDescriptor;
import org.antlr.v4.test.runtime.descriptors.ParserErrorsDescriptors;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class TestParserErrors extends BaseRuntimeTest {
public TestParserErrors(RuntimeTestDescriptor descriptor) {
super(descriptor,new BasePHPTest());
}
@Parameterized.Parameters(name="{0}")
public static RuntimeTestDescriptor[] getAllTestDescriptors() {
return BaseRuntimeTest.getRuntimeTestDescriptors(ParserErrorsDescriptors.class, "PHP");
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/
package org.antlr.v4.test.runtime.php;
import org.antlr.v4.test.runtime.BaseRuntimeTest;
import org.antlr.v4.test.runtime.RuntimeTestDescriptor;
import org.antlr.v4.test.runtime.descriptors.ParserExecDescriptors;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class TestParserExec extends BaseRuntimeTest {
public TestParserExec(RuntimeTestDescriptor descriptor) {
super(descriptor,new BasePHPTest());
}
@Parameterized.Parameters(name="{0}")
public static RuntimeTestDescriptor[] getAllTestDescriptors() {
return BaseRuntimeTest.getRuntimeTestDescriptors(ParserExecDescriptors.class, "PHP");
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/
package org.antlr.v4.test.runtime.php;
import org.antlr.v4.test.runtime.BaseRuntimeTest;
import org.antlr.v4.test.runtime.RuntimeTestDescriptor;
import org.antlr.v4.test.runtime.descriptors.PerformanceDescriptors;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class TestPerformance extends BaseRuntimeTest {
public TestPerformance(RuntimeTestDescriptor descriptor) {
super(descriptor,new BasePHPTest());
}
@Parameterized.Parameters(name="{0}")
public static RuntimeTestDescriptor[] getAllTestDescriptors() {
return BaseRuntimeTest.getRuntimeTestDescriptors(PerformanceDescriptors.class, "PHP");
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/
package org.antlr.v4.test.runtime.php;
import org.antlr.v4.test.runtime.BaseRuntimeTest;
import org.antlr.v4.test.runtime.RuntimeTestDescriptor;
import org.antlr.v4.test.runtime.descriptors.SemPredEvalLexerDescriptors;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class TestSemPredEvalLexer extends BaseRuntimeTest {
public TestSemPredEvalLexer(RuntimeTestDescriptor descriptor) {
super(descriptor,new BasePHPTest());
}
@Parameterized.Parameters(name="{0}")
public static RuntimeTestDescriptor[] getAllTestDescriptors() {
return BaseRuntimeTest.getRuntimeTestDescriptors(SemPredEvalLexerDescriptors.class, "PHP");
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/
package org.antlr.v4.test.runtime.php;
import org.antlr.v4.test.runtime.BaseRuntimeTest;
import org.antlr.v4.test.runtime.RuntimeTestDescriptor;
import org.antlr.v4.test.runtime.descriptors.SemPredEvalParserDescriptors;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class TestSemPredEvalParser extends BaseRuntimeTest {
public TestSemPredEvalParser(RuntimeTestDescriptor descriptor) {
super(descriptor,new BasePHPTest());
}
@Parameterized.Parameters(name="{0}")
public static RuntimeTestDescriptor[] getAllTestDescriptors() {
return BaseRuntimeTest.getRuntimeTestDescriptors(SemPredEvalParserDescriptors.class, "PHP");
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/
package org.antlr.v4.test.runtime.php;
import org.antlr.v4.test.runtime.BaseRuntimeTest;
import org.antlr.v4.test.runtime.RuntimeTestDescriptor;
import org.antlr.v4.test.runtime.descriptors.SetsDescriptors;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class TestSets extends BaseRuntimeTest {
public TestSets(RuntimeTestDescriptor descriptor) {
super(descriptor,new BasePHPTest());
}
@Parameterized.Parameters(name="{0}")
public static RuntimeTestDescriptor[] getAllTestDescriptors() {
return BaseRuntimeTest.getRuntimeTestDescriptors(SetsDescriptors.class, "PHP");
}
}

1
runtime/PHP Submodule

@ -0,0 +1 @@
Subproject commit 9e1b759d02220eedf477771169bdfb6a186a2984

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,7 @@ import java.util.ArrayList;
/** A StructDecl to handle a -&gt; label on alt */
public class AltLabelStructDecl extends StructDecl {
public int altNum;
public String parentRule;
public AltLabelStructDecl(OutputModelFactory factory, Rule r,
int altNum, String label)
{
@ -24,6 +25,7 @@ public class AltLabelStructDecl extends StructDecl {
this.altNum = altNum;
this.name = // override name set in super to the label ctx
factory.getGenerator().getTarget().getAltLabelContextStructName(label);
this.parentRule = r.name;
derivedFromName = label;
}

View File

@ -0,0 +1,105 @@
/*
* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/
package org.antlr.v4.codegen.target;
import org.antlr.v4.codegen.CodeGenerator;
import org.antlr.v4.codegen.Target;
import org.antlr.v4.codegen.UnicodeEscapes;
import org.antlr.v4.tool.ast.GrammarAST;
import org.stringtemplate.v4.STGroup;
import org.stringtemplate.v4.StringRenderer;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class PHPTarget extends Target {
private static final String[] phpKeywords = {
"abstract", "and", "array", "as",
"break",
"callable", "case", "catch", "class", "clone", "const", "continue",
"declare", "default", "die", "do",
"echo", "else", "elseif", "empty", "enddeclare", "endfor", "endforeach",
"endif", "endswitch", "endwhile", "eval", "exit", "extends",
"final", "finally", "for", "foreach", "function",
"global", "goto",
"if", "implements", "include", "include_once", "instanceof", "insteadof", "interface", "isset",
"list",
"namespace", "new",
"or",
"print", "private", "protected", "public",
"require", "require_once", "return",
"static", "switch",
"throw", "trait", "try",
"unset", "use",
"var",
"while",
"xor",
"yield",
"__halt_compiler", "__CLASS__", "__DIR__", "__FILE__", "__FUNCTION__",
"__LINE__", "__METHOD__", "__NAMESPACE__", "__TRAIT__"
};
private final Set<String> badWords = new HashSet<String>();
public PHPTarget(CodeGenerator gen) {
super(gen, "PHP");
targetCharValueEscape['$'] = "\\$";
}
@Override
public String getVersion() {
return "4.7.2";
}
@Override
public String encodeIntAsCharEscape(int v) {
if (v < Character.MIN_VALUE || v > Character.MAX_VALUE) {
throw new IllegalArgumentException(String.format("Cannot encode the specified value: %d", v));
}
return String.format("\\u{%X}", v & 0xFFFF);
}
public Set<String> getBadWords() {
if (badWords.isEmpty()) {
addBadWords();
}
return badWords;
}
protected void addBadWords() {
badWords.addAll(Arrays.asList(phpKeywords));
badWords.add("rule");
badWords.add("parserRule");
}
@Override
protected boolean visibleGrammarSymbolCausesIssueInGeneratedCode(GrammarAST idNode) {
return getBadWords().contains(idNode.getText());
}
@Override
protected STGroup loadTemplates() {
STGroup result = super.loadTemplates();
result.registerRenderer(String.class, new StringRenderer(), true);
return result;
}
@Override
public boolean supportsOverloadedMethods() {
return false;
}
@Override
protected void appendUnicodeEscapedCodePoint(int codePoint, StringBuilder sb) {
UnicodeEscapes.appendPythonStyleEscapedCodePoint(codePoint, sb);
}
}