Add 'vendor/tree-sitter-php/' from commit '0ce134234214427b6aeb2735e93a307881c6cd6f'

git-subtree-dir: vendor/tree-sitter-php
git-subtree-mainline: 020983cd85
git-subtree-split: 0ce1342342
pull/297/head
Wilfred Hughes 2022-03-09 23:36:23 +07:00
commit 51a4da6d7c
38 changed files with 142928 additions and 0 deletions

@ -0,0 +1,3 @@
/src/** linguist-vendored
/examples/* linguist-vendored
/corpus/* linguist-vendored

@ -0,0 +1,8 @@
Checklist:
- [ ] All tests pass in CI
- [ ] There are sufficient tests for the new fix/feature
- [ ] Grammar rules have not been renamed unless absolutely necessary (x rules renamed)
- [ ] The conflicts section hasn't grown too much (x new conflicts)
- [ ] The parser size hasn't grown too much (master: STATE_COUNT, PR: STATE_COUNT) (check the value of STATE_COUNT in src/parser.c)

@ -0,0 +1,22 @@
name: Build/test
on:
pull_request:
branches:
- "**"
push:
branches:
- "master"
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 14
- run: npm install
- run: npm test

@ -0,0 +1,6 @@
package-lock.json
node_modules
build
*.log
.DS_Store
examples

@ -0,0 +1,5 @@
test
build
script
examples
target

@ -0,0 +1,25 @@
[package]
name = "tree-sitter-php"
description = "php grammar for the tree-sitter parsing library"
version = "0.19.0"
keywords = ["incremental", "parsing", "php"]
categories = ["parsing", "text-editors"]
repository = "https://github.com/tree-sitter/tree-sitter-javascript"
edition = "2018"
build = "bindings/rust/build.rs"
include = [
"bindings/rust/*",
"grammar.js",
"queries/*",
"src/*",
]
[lib]
path = "bindings/rust/lib.rs"
[dependencies]
tree-sitter = "0.19"
[build-dependencies]
cc = "1.0"

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2017 Josh Vera, GitHub
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,9 @@
tree-sitter-php
==================
[![Build/test](https://github.com/tree-sitter/tree-sitter-php/actions/workflows/ci.yml/badge.svg)](https://github.com/tree-sitter/tree-sitter-php/actions/workflows/ci.yml)
PHP grammar for [tree-sitter][].
[tree-sitter]: https://github.com/tree-sitter/tree-sitter

@ -0,0 +1,19 @@
{
"targets": [
{
"target_name": "tree_sitter_php_binding",
"include_dirs": [
"<!(node -e \"require('nan')\")",
"src"
],
"sources": [
"src/parser.c",
"src/scanner.cc",
"bindings/node/binding.cc"
],
"cflags_c": [
"-std=c99"
]
}
]
}

@ -0,0 +1,28 @@
#include "tree_sitter/parser.h"
#include <node.h>
#include "nan.h"
using namespace v8;
extern "C" TSLanguage * tree_sitter_php();
namespace {
NAN_METHOD(New) {}
void Init(Local<Object> exports, Local<Object> module) {
Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
tpl->SetClassName(Nan::New("Language").ToLocalChecked());
tpl->InstanceTemplate()->SetInternalFieldCount(1);
Local<Function> constructor = Nan::GetFunction(tpl).ToLocalChecked();
Local<Object> instance = constructor->NewInstance(Nan::GetCurrentContext()).ToLocalChecked();
Nan::SetInternalFieldPointer(instance, 0, tree_sitter_php());
Nan::Set(instance, Nan::New("name").ToLocalChecked(), Nan::New("php").ToLocalChecked());
Nan::Set(module, Nan::New("exports").ToLocalChecked(), instance);
}
NODE_MODULE(tree_sitter_php_binding, Init)
} // namespace

@ -0,0 +1,19 @@
try {
module.exports = require("../../build/Release/tree_sitter_php_binding");
} catch (error1) {
if (error1.code !== 'MODULE_NOT_FOUND') {
throw error1;
}
try {
module.exports = require("../../build/Debug/tree_sitter_php_binding");
} catch (error2) {
if (error2.code !== 'MODULE_NOT_FOUND') {
throw error2;
}
throw error1
}
}
try {
module.exports.nodeTypeInfo = require("../../src/node-types.json");
} catch (_) {}

@ -0,0 +1,48 @@
# tree-sitter-php
This crate provides support of the PHP language for the [tree-sitter][] parsing library. To use this crate, add it to
the `[dependencies]` section of your
`Cargo.toml` file. As this crate is not (yet) published to the central registry, you will have to specify it as a git
dependency, currently we suggest using the `master` branch.
You will also need the [tree-sitter crate][] to do the actual parsing here.
``` toml
[dependencies]
tree-sitter = "0.19"
tree-sitter-php = { git = "https://github.com/tree-sitter/tree-sitter-php.git", branch = "master" }
```
To you the parser, you need to obtain an instance of a [`tree_sitter::Language`][Language] struct for php.
The `language()` function provides this.
Passing this struct to a [`tree_sitter::Parser`][Parser] will enable it to parse PHP.
``` rust
use tree_sitter::Parser;
fn main() {
let code = r#"
function double(int $x) {
return $x * 2;
}
"#;
let mut parser = Parser::new();
parser
.set_language(tree_sitter_php::language())
.expect("Error loading PHP parsing support");
let parsed = parser.parse(code, None);
println!("{:#?}", parsed);
}
```
If you have any questions, please reach out to us in the [tree-sitter discussions] page.
[Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html
[Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html
[tree-sitter]: https://tree-sitter.github.io/
[tree-sitter crate]: https://crates.io/crates/tree-sitter
[tree-sitter discussions]: https://github.com/tree-sitter/tree-sitter/discussions

@ -0,0 +1,29 @@
fn main() {
let src_dir = std::path::Path::new("src");
let mut c_config = cc::Build::new();
c_config.include(&src_dir);
c_config
.flag_if_supported("-Wno-unused-parameter")
.flag_if_supported("-Wno-unused-but-set-variable")
.flag_if_supported("-Wno-trigraphs");
let parser_path = src_dir.join("parser.c");
c_config.file(&parser_path);
println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap());
c_config.compile("parser");
// If your language uses an external scanner written in C++,
// then include this block of code:
let mut cpp_config = cc::Build::new();
cpp_config.cpp(true);
cpp_config.include(&src_dir);
cpp_config
.flag_if_supported("-Wno-unused-parameter")
.flag_if_supported("-Wno-unused-but-set-variable");
let scanner_path = src_dir.join("scanner.cc");
cpp_config.file(&scanner_path);
println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap());
cpp_config.compile("scanner");
}

@ -0,0 +1,52 @@
//! This crate provides php language support for the [tree-sitter][] parsing library.
//!
//! Typically, you will use the [language][language func] function to add this language to a
//! tree-sitter [Parser][], and then use the parser to parse some code:
//!
//! ```
//! let code = "";
//! let mut parser = tree_sitter::Parser::new();
//! parser.set_language(tree_sitter_php::language()).expect("Error loading php grammar");
//! let tree = parser.parse(code, None).unwrap();
//! ```
//!
//! [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html
//! [language func]: fn.language.html
//! [Parser]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Parser.html
//! [tree-sitter]: https://tree-sitter.github.io/
use tree_sitter::Language;
extern "C" {
fn tree_sitter_php() -> Language;
}
/// Get the tree-sitter [Language][] for this grammar.
///
/// [Language]: https://docs.rs/tree-sitter/*/tree_sitter/struct.Language.html
pub fn language() -> Language {
unsafe { tree_sitter_php() }
}
/// The content of the [`node-types.json`][] file for this grammar.
///
/// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers#static-node-types
pub const NODE_TYPES: &'static str = include_str!("../../src/node-types.json");
// Uncomment these to include any queries that this grammar contains
// pub const HIGHLIGHTS_QUERY: &'static str = include_str!("../../queries/highlights.scm");
// pub const INJECTIONS_QUERY: &'static str = include_str!("../../queries/injections.scm");
// pub const LOCALS_QUERY: &'static str = include_str!("../../queries/locals.scm");
// pub const TAGS_QUERY: &'static str = include_str!("../../queries/tags.scm");
#[cfg(test)]
mod tests {
#[test]
fn test_can_load_grammar() {
let mut parser = tree_sitter::Parser::new();
parser
.set_language(super::language())
.expect("Error loading php language");
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,40 @@
{
"name": "tree-sitter-php",
"version": "0.19.0",
"description": "PHP grammar for tree-sitter",
"main": "bindings/node",
"keywords": [
"parser",
"php"
],
"author": "Josh Vera",
"license": "MIT",
"bugs": {
"url": "https://github.com/tree-sitter/tree-sitter-php/issues"
},
"homepage": "https://github.com/tree-sitter/tree-sitter-php#readme",
"dependencies": {
"nan": "^2.14.0"
},
"devDependencies": {
"tree-sitter-cli": "^0.19.1",
"shelljs": "^0.8.4"
},
"scripts": {
"build": "tree-sitter generate && node-gyp build",
"test": "tree-sitter test && node script/parse-examples.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/tree-sitter/tree-sitter-php.git"
},
"tree-sitter": [
{
"scope": "source.php",
"file-types": [
"php"
],
"highlights": "queries/highlights.scm"
}
]
}

@ -0,0 +1,115 @@
(php_tag) @tag
"?>" @tag
; Types
(primitive_type) @type.builtin
(cast_type) @type.builtin
(named_type (name) @type) @type
(named_type (qualified_name) @type) @type
; Functions
(array_creation_expression "array" @function.builtin)
(list_literal "list" @function.builtin)
(method_declaration
name: (name) @function.method)
(function_call_expression
function: (qualified_name (name)) @function)
(scoped_call_expression
name: (name) @function)
(member_call_expression
name: (name) @function.method)
(function_definition
name: (name) @function)
; Member
(property_element
(variable_name) @property)
(member_access_expression
name: (variable_name (name)) @property)
(member_access_expression
name: (name) @property)
; Variables
(relative_scope) @variable.builtin
((name) @constant
(#match? @constant "^_?[A-Z][A-Z\\d_]+$"))
((name) @constructor
(#match? @constructor "^[A-Z]"))
((name) @variable.builtin
(#eq? @variable.builtin "this"))
(variable_name) @variable
; Basic tokens
(string) @string
(heredoc) @string
(boolean) @constant.builtin
(null) @constant.builtin
(integer) @number
(float) @number
(comment) @comment
"$" @operator
; Keywords
"abstract" @keyword
"as" @keyword
"break" @keyword
"case" @keyword
"catch" @keyword
"class" @keyword
"const" @keyword
"continue" @keyword
"declare" @keyword
"default" @keyword
"do" @keyword
"echo" @keyword
"else" @keyword
"elseif" @keyword
"enddeclare" @keyword
"endforeach" @keyword
"endif" @keyword
"endswitch" @keyword
"endwhile" @keyword
"extends" @keyword
"final" @keyword
"finally" @keyword
"foreach" @keyword
"function" @keyword
"global" @keyword
"if" @keyword
"implements" @keyword
"include_once" @keyword
"include" @keyword
"insteadof" @keyword
"interface" @keyword
"namespace" @keyword
"new" @keyword
"private" @keyword
"protected" @keyword
"public" @keyword
"require_once" @keyword
"require" @keyword
"return" @keyword
"static" @keyword
"switch" @keyword
"throw" @keyword
"trait" @keyword
"try" @keyword
"use" @keyword
"while" @keyword

@ -0,0 +1,3 @@
((text) @injection.content
(#set! injection.language "html")
(#set! injection.combined))

@ -0,0 +1,26 @@
(class_declaration
name: (name) @name) @definition.class
(function_definition
name: (name) @name) @definition.function
(method_declaration
name: (name) @name) @definition.function
(object_creation_expression
[
(qualified_name (name) @name)
(variable_name (name) @name)
]) @reference.class
(function_call_expression
function: [
(qualified_name (name) @name)
(variable_name (name)) @name
]) @reference.call
(scoped_call_expression
name: (name) @name) @reference.call
(member_call_expression
name: (name) @name) @reference.call

@ -0,0 +1,39 @@
#!/bin/bash
set -e
cd "$(dirname "$0")/.."
function checkout_at() {
path="examples/$1"
url=$2
sha=$3
if [ ! -d "$path" ]; then
git clone "https://github.com/$url" "$path"
fi
pushd "$path"
git fetch && git reset --hard "$sha"
popd
}
checkout_at "laravel" "laravel/laravel" "9d0862b3340c8243ee072afc181e315ffa35e110"
checkout_at "phabricator" "phacility/phabricator" "d0b01a41f2498fb2a6487c2d6704dc7acfd4675f"
checkout_at "phpunit" "sebastianbergmann/phpunit" "5e523bdc7dd4d90fed9fb29d1df05347b3e7eaba"
checkout_at "WordPress" "WordPress/WordPress" "45286c5bb3f6fe5005567903ec858d87077eae2c"
known_failures="$(cat script/known-failures.txt)"
tree-sitter parse -q \
'examples/**/*.php' \
$(for file in $known_failures; do echo "!${file}"; done)
example_count=$(find examples -name '*.php' | wc -l)
failure_count=$(wc -w <<< "$known_failures")
success_count=$(( $example_count - $failure_count ))
success_percent=$(bc -l <<< "100*${success_count}/${example_count}")
printf \
"Successfully parsed %d of %d example files (%.1f%%)\n" \
$success_count $example_count $success_percent

@ -0,0 +1,102 @@
const fs = require('fs')
const os = require('os')
const util = require('util');
const { exec, execSync } = require('child_process')
const shell = require('shelljs')
function main() {
checkoutExampleProjects([
{ dir: 'laravel', repository: 'https://github.com/laravel/laravel', sha: '9d0862b3340c8243ee072afc181e315ffa35e110' },
{ dir: 'framework', repository: 'https://github.com/laravel/framework', sha: '45d439e98a6b14afde8911f7d22a265948adbf72' },
{ dir: 'phabricator', repository: 'https://github.com/phacility/phabricator', sha: 'd0b01a41f2498fb2a6487c2d6704dc7acfd4675f' },
{ dir: 'phpunit', repository: 'https://github.com/sebastianbergmann/phpunit', sha: '5e523bdc7dd4d90fed9fb29d1df05347b3e7eaba' },
{ dir: 'WordPress', repository: 'https://github.com/WordPress/WordPress', sha: '45286c5bb3f6fe5005567903ec858d87077eae2c' },
{ dir: 'mediawiki', repository: 'https://github.com/wikimedia/mediawiki', sha: 'b6b88cbf98fb0c7891324709a85eabc290ed28b4' },
])
parseExamples()
}
function parseExamples() {
const knownFailures = loadKnownFailures().split(/\r\n|\n/).filter(line => line.length > 0)
let excludeString = knownFailures.join(" !")
if (knownFailures.length > 0) {
excludeString = "!" + excludeString
}
if (os.platform == 'win32') {
excludeString = convertToWindowsPath(excludeString)
}
exec('"./node_modules/.bin/tree-sitter" parse -q examples/**/*.php ' + excludeString, (err, stdout) => {
const failures = extractFailureFilePaths(stdout)
failures.forEach(failure => {
if (!knownFailures.includes(failure)) {
console.error(`Unknown failure occurred: ${failure}`)
}
})
const failureCount = failures.length + knownFailures.length
const exampleCount = countExampleFiles()
const successCount = exampleCount - failureCount;
const successPercent = 100 * successCount / exampleCount
console.log(`Successfully parsed ${successCount} of ${exampleCount} example files (${successPercent.toFixed(2)}%)`)
})
}
function extractFailureFilePaths(stdout) {
return stdout.split(/\r\n|\n/).filter(line => line.length > 0).map(line => convertToUnixPath(line.substring(0, line.indexOf(" "))))
}
function convertToUnixPath(filePathString) {
return filePathString.split("\\").join("/")
}
function convertToWindowsPath(filePathString) {
return filePathString.split("/").join("\\")
}
function countExampleFiles() {
return shell.find('./examples').filter((file) => { return file.match(/\.php$/) }).length
}
function createExamplesDirectoryIfNotExists() {
const dir = './examples'
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir)
}
}
function checkoutExampleProjects(projects) {
createExamplesDirectoryIfNotExists()
checkoutPromise = util.promisify(checkoutExampleProject)
Promise.all(projects.map((project) => checkoutPromise(project)))
}
function checkoutExampleProject(project) {
const projectDir = './examples/' + project.dir
if (!fs.existsSync(projectDir)) {
cloneRepository(project)
}
checkoutCommit(project)
}
function cloneRepository(project) {
execSync('cd examples && git clone --quiet ' + project.repository + ' ' + project.dir + ' && cd ..')
}
function checkoutCommit(project) {
execSync('cd examples/' + project.dir + ' && git fetch --quiet && git reset --quiet --hard "' + project.sha + '" && cd ../..')
}
function loadKnownFailures() {
return fs.readFileSync('./script/known-failures.txt').toString()
}
main()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,381 @@
#include <tree_sitter/parser.h>
#include <vector>
#include <string>
#include <cwctype>
namespace {
using std::vector;
using std::string;
enum TokenType {
AUTOMATIC_SEMICOLON,
HEREDOC,
ENCAPSED_STRING_CHARS,
ENCAPSED_STRING_CHARS_AFTER_VARIABLE,
EOF_TOKEN,
SENTINEL_ERROR, // Unused token used to indicate error recovery mode
};
struct Heredoc {
Heredoc() : end_word_indentation_allowed(false) {}
string word;
bool end_word_indentation_allowed;
};
struct Scanner {
bool has_leading_whitespace;
vector<Heredoc> open_heredocs;
Scanner() : has_leading_whitespace(false) {}
void reset() {
open_heredocs.clear();
}
enum ScanContentResult {
Error,
End
};
unsigned serialize(char *buffer) {
unsigned i = 0;
buffer[i++] = open_heredocs.size();
for (
vector<Heredoc>::iterator iter = open_heredocs.begin(),
end = open_heredocs.end();
iter != end;
++iter
) {
if (i + 2 + iter->word.size() >= TREE_SITTER_SERIALIZATION_BUFFER_SIZE) return 0;
buffer[i++] = iter->end_word_indentation_allowed;
buffer[i++] = iter->word.size();
iter->word.copy(&buffer[i], iter->word.size());
i += iter->word.size();
}
return i;
}
void deserialize(const char *buffer, unsigned length) {
unsigned i = 0;
has_leading_whitespace = false;
open_heredocs.clear();
if (length == 0) return;
uint8_t open_heredoc_count = buffer[i++];
for (unsigned j = 0; j < open_heredoc_count; j++) {
Heredoc heredoc;
heredoc.end_word_indentation_allowed = buffer[i++];
uint8_t word_length = buffer[i++];
heredoc.word.assign(buffer + i, buffer + i + word_length);
i += word_length;
open_heredocs.push_back(heredoc);
}
// assert(i == length);
}
void skip(TSLexer *lexer) {
has_leading_whitespace = true;
lexer->advance(lexer, true);
}
static void advance(TSLexer *lexer) {
lexer->advance(lexer, false);
}
bool scan_whitespace(TSLexer *lexer) {
for (;;) {
while (iswspace(lexer->lookahead)) {
advance(lexer);
}
if (lexer->lookahead == '/') {
advance(lexer);
if (lexer->lookahead == '/') {
advance(lexer);
while (lexer->lookahead != 0 && lexer->lookahead != '\n') {
advance(lexer);
}
} else {
return false;
}
} else {
return true;
}
}
}
static bool is_valid_name_char(TSLexer *lexer) {
return iswalpha(lexer->lookahead) || lexer->lookahead == '_';
}
static bool is_escapable_sequence(TSLexer *lexer) {
// Note: remember to also update the escape_sequence rule in the
// main grammar whenever changing this method
auto letter = lexer->lookahead;
if (letter == 'n' ||
letter == 'r' ||
letter == 't' ||
letter == 'v' ||
letter == 'e' ||
letter == 'f' ||
letter == '\\' ||
letter == '$' ||
letter == '"') {
return true;
}
// Hex
if (letter == 'x') {
advance(lexer);
return isxdigit(lexer->lookahead);
}
// Unicode
if (letter == 'u') {
return true; // We handle the case where this is not really an escape sequence in grammar.js - this is needed to support the edge case "\u{$a}" in which case "\u" is to be interprented as characters and {$a} as a variable
}
// Octal
return iswdigit(lexer->lookahead) && lexer->lookahead >= '0' && lexer->lookahead <= '7';
}
bool scan_encapsed_part_string(TSLexer *lexer, bool is_after_variable) {
lexer->result_symbol = ENCAPSED_STRING_CHARS;
for (bool has_content = false;; has_content = true) {
lexer->mark_end(lexer);
switch (lexer->lookahead) {
case '"':
return has_content;
case '\0':
return false;
case '\\':
advance(lexer);
// \{ should not be interprented as an escape sequence, but both
// should be consumed as normal characters
if (lexer->lookahead == '{') {
advance(lexer);
break;
}
if (is_escapable_sequence(lexer)) {
return has_content;
}
break;
case '$':
advance(lexer);
if (is_valid_name_char(lexer) || lexer->lookahead == '{') {
return has_content;
}
break;
case '-':
if (is_after_variable) {
advance(lexer);
if (lexer->lookahead == '>') {
advance(lexer);
if (is_valid_name_char(lexer)) {
return has_content;
}
break;
}
break;
}
case '[':
if (is_after_variable) {
return has_content;
}
advance(lexer);
break;
case '{':
advance(lexer);
if (lexer->lookahead == '$') {
return has_content;
}
break;
default:
advance(lexer);
}
is_after_variable = false;
}
}
string scan_heredoc_word(TSLexer *lexer) {
string result;
int32_t quote;
switch (lexer->lookahead) {
case '\'':
quote = lexer->lookahead;
advance(lexer);
while (lexer->lookahead != quote && lexer->lookahead != 0) {
result += lexer->lookahead;
advance(lexer);
}
advance(lexer);
break;
default:
if (iswalnum(lexer->lookahead) || lexer->lookahead == '_') {
result += lexer->lookahead;
advance(lexer);
while (iswalnum(lexer->lookahead) || lexer->lookahead == '_') {
result += lexer->lookahead;
advance(lexer);
}
}
break;
}
return result;
}
ScanContentResult scan_heredoc_content(TSLexer *lexer) {
if (open_heredocs.empty()) return Error;
Heredoc heredoc = open_heredocs.front();
size_t position_in_word = 0;
for (;;) {
if (position_in_word == heredoc.word.size()) {
// While PHP requires the heredoc end tag to be the very first on a new line, there may be an
// arbitrary amount of whitespace before the closing token
while (lexer->lookahead == ' ') {
advance(lexer);
}
// , and ) is needed to support heredoc in function arguments
if (lexer->lookahead == ';' || lexer->lookahead == ',' || lexer->lookahead == ')' || lexer->lookahead == '\n' || lexer->lookahead == '\r') {
open_heredocs.erase(open_heredocs.begin());
return End;
}
position_in_word = 0;
}
if (lexer->lookahead == 0) {
open_heredocs.erase(open_heredocs.begin());
return Error;
}
if (lexer->lookahead == heredoc.word[position_in_word]) {
advance(lexer);
position_in_word++;
} else {
position_in_word = 0;
advance(lexer);
}
}
}
bool scan(TSLexer *lexer, const bool *valid_symbols) {
const bool is_error_recovery = valid_symbols[SENTINEL_ERROR];
if (is_error_recovery) {
return false;
}
has_leading_whitespace = false;
lexer->mark_end(lexer);
if (valid_symbols[ENCAPSED_STRING_CHARS_AFTER_VARIABLE]) {
return scan_encapsed_part_string(lexer, true);
}
if (valid_symbols[ENCAPSED_STRING_CHARS]) {
return scan_encapsed_part_string(lexer, false);
}
if (!scan_whitespace(lexer)) return false;
if (valid_symbols[EOF_TOKEN] && lexer->eof(lexer)) {
lexer->result_symbol = EOF_TOKEN;
return true;
}
if (valid_symbols[HEREDOC]) {
if (lexer->lookahead == '<') {
advance(lexer);
if (lexer->lookahead != '<') return false;
advance(lexer);
if (lexer->lookahead != '<') return false;
advance(lexer);
if (!scan_whitespace(lexer)) return false;
// Found a heredoc
Heredoc heredoc;
heredoc.word = scan_heredoc_word(lexer);
if (heredoc.word.empty()) return false;
open_heredocs.push_back(heredoc);
switch (scan_heredoc_content(lexer)) {
case Error:
return false;
case End:
lexer->result_symbol = HEREDOC;
lexer->mark_end(lexer);
return true;
}
}
}
if (valid_symbols[AUTOMATIC_SEMICOLON]) {
lexer->result_symbol = AUTOMATIC_SEMICOLON;
if (lexer->lookahead != '?') return false;
advance(lexer);
return lexer->lookahead == '>';
}
return false;
}
};
}
extern "C" {
void *tree_sitter_php_external_scanner_create() {
return new Scanner();
}
unsigned tree_sitter_php_external_scanner_serialize(void *payload, char *buffer) {
Scanner *scanner = static_cast<Scanner *>(payload);
return scanner->serialize(buffer);
}
void tree_sitter_php_external_scanner_deserialize(void *payload, const char *buffer, unsigned length) {
Scanner *scanner = static_cast<Scanner *>(payload);
scanner->deserialize(buffer, length);
}
void tree_sitter_php_external_scanner_destroy(void *payload) {
Scanner *scanner = static_cast<Scanner *>(payload);
delete scanner;
}
bool tree_sitter_php_external_scanner_scan(void *payload, TSLexer *lexer,
const bool *valid_symbols) {
Scanner *scanner = static_cast<Scanner *>(payload);
return scanner->scan(lexer, valid_symbols);
}
void tree_sitter_php_external_scanner_reset(void *p) {}
}

@ -0,0 +1,223 @@
#ifndef TREE_SITTER_PARSER_H_
#define TREE_SITTER_PARSER_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#define ts_builtin_sym_error ((TSSymbol)-1)
#define ts_builtin_sym_end 0
#define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024
typedef uint16_t TSStateId;
#ifndef TREE_SITTER_API_H_
typedef uint16_t TSSymbol;
typedef uint16_t TSFieldId;
typedef struct TSLanguage TSLanguage;
#endif
typedef struct {
TSFieldId field_id;
uint8_t child_index;
bool inherited;
} TSFieldMapEntry;
typedef struct {
uint16_t index;
uint16_t length;
} TSFieldMapSlice;
typedef struct {
bool visible;
bool named;
bool supertype;
} TSSymbolMetadata;
typedef struct TSLexer TSLexer;
struct TSLexer {
int32_t lookahead;
TSSymbol result_symbol;
void (*advance)(TSLexer *, bool);
void (*mark_end)(TSLexer *);
uint32_t (*get_column)(TSLexer *);
bool (*is_at_included_range_start)(const TSLexer *);
bool (*eof)(const TSLexer *);
};
typedef enum {
TSParseActionTypeShift,
TSParseActionTypeReduce,
TSParseActionTypeAccept,
TSParseActionTypeRecover,
} TSParseActionType;
typedef union {
struct {
uint8_t type;
TSStateId state;
bool extra;
bool repetition;
} shift;
struct {
uint8_t type;
uint8_t child_count;
TSSymbol symbol;
int16_t dynamic_precedence;
uint16_t production_id;
} reduce;
uint8_t type;
} TSParseAction;
typedef struct {
uint16_t lex_state;
uint16_t external_lex_state;
} TSLexMode;
typedef union {
TSParseAction action;
struct {
uint8_t count;
bool reusable;
} entry;
} TSParseActionEntry;
struct TSLanguage {
uint32_t version;
uint32_t symbol_count;
uint32_t alias_count;
uint32_t token_count;
uint32_t external_token_count;
uint32_t state_count;
uint32_t large_state_count;
uint32_t production_id_count;
uint32_t field_count;
uint16_t max_alias_sequence_length;
const uint16_t *parse_table;
const uint16_t *small_parse_table;
const uint32_t *small_parse_table_map;
const TSParseActionEntry *parse_actions;
const char * const *symbol_names;
const char * const *field_names;
const TSFieldMapSlice *field_map_slices;
const TSFieldMapEntry *field_map_entries;
const TSSymbolMetadata *symbol_metadata;
const TSSymbol *public_symbol_map;
const uint16_t *alias_map;
const TSSymbol *alias_sequences;
const TSLexMode *lex_modes;
bool (*lex_fn)(TSLexer *, TSStateId);
bool (*keyword_lex_fn)(TSLexer *, TSStateId);
TSSymbol keyword_capture_token;
struct {
const bool *states;
const TSSymbol *symbol_map;
void *(*create)(void);
void (*destroy)(void *);
bool (*scan)(void *, TSLexer *, const bool *symbol_whitelist);
unsigned (*serialize)(void *, char *);
void (*deserialize)(void *, const char *, unsigned);
} external_scanner;
};
/*
* Lexer Macros
*/
#define START_LEXER() \
bool result = false; \
bool skip = false; \
bool eof = false; \
int32_t lookahead; \
goto start; \
next_state: \
lexer->advance(lexer, skip); \
start: \
skip = false; \
lookahead = lexer->lookahead;
#define ADVANCE(state_value) \
{ \
state = state_value; \
goto next_state; \
}
#define SKIP(state_value) \
{ \
skip = true; \
state = state_value; \
goto next_state; \
}
#define ACCEPT_TOKEN(symbol_value) \
result = true; \
lexer->result_symbol = symbol_value; \
lexer->mark_end(lexer);
#define END_STATE() return result;
/*
* Parse Table Macros
*/
#define SMALL_STATE(id) id - LARGE_STATE_COUNT
#define STATE(id) id
#define ACTIONS(id) id
#define SHIFT(state_value) \
{{ \
.shift = { \
.type = TSParseActionTypeShift, \
.state = state_value \
} \
}}
#define SHIFT_REPEAT(state_value) \
{{ \
.shift = { \
.type = TSParseActionTypeShift, \
.state = state_value, \
.repetition = true \
} \
}}
#define SHIFT_EXTRA() \
{{ \
.shift = { \
.type = TSParseActionTypeShift, \
.extra = true \
} \
}}
#define REDUCE(symbol_val, child_count_val, ...) \
{{ \
.reduce = { \
.type = TSParseActionTypeReduce, \
.symbol = symbol_val, \
.child_count = child_count_val, \
__VA_ARGS__ \
}, \
}}
#define RECOVER() \
{{ \
.type = TSParseActionTypeRecover \
}}
#define ACCEPT_INPUT() \
{{ \
.type = TSParseActionTypeAccept \
}}
#ifdef __cplusplus
}
#endif
#endif // TREE_SITTER_PARSER_H_

@ -0,0 +1,396 @@
=========================================
Abstract class
=========================================
<?php
abstract class A {
public function a() {}
abstract public function b();
}
---
(program
(php_tag)
(class_declaration
(abstract_modifier)
(name)
(declaration_list
(method_declaration
(visibility_modifier)
(name)
(formal_parameters)
(compound_statement)
)
(method_declaration
(abstract_modifier)
(visibility_modifier)
(name)
(formal_parameters)
)
)
)
)
=========================================
Anonymous classes
=========================================
<?php
new class {
public function test() {}
};
new class extends A implements B, C {};
new class() {
public $foo;
};
new class($a, $b) extends A {
use T;
};
class A {
public function test() {
return new class($this) extends A {
const A = 'B';
};
}
}
---
(program
(php_tag)
(expression_statement
(object_creation_expression
(declaration_list
(method_declaration
(visibility_modifier)
(name)
(formal_parameters)
(compound_statement)
)
)
)
)
(expression_statement
(object_creation_expression
(base_clause
(name)
)
(class_interface_clause
(name)
(name)
)
(declaration_list)
)
)
(expression_statement
(object_creation_expression
(arguments)
(declaration_list
(property_declaration
(visibility_modifier)
(property_element
(variable_name (name))
)
)
)
)
)
(expression_statement
(object_creation_expression
(arguments
(argument
(variable_name (name))
)
(argument
(variable_name (name))
)
)
(base_clause
(name)
)
(declaration_list
(use_declaration
(name)
)
)
)
)
(class_declaration
(name)
(declaration_list
(method_declaration
(visibility_modifier)
(name)
(formal_parameters)
(compound_statement
(return_statement
(object_creation_expression
(arguments (argument (variable_name (name))))
(base_clause (name))
(declaration_list
(const_declaration
(const_element (name) (string))
)
)
)
)
)
)
)
)
)
=========================================
Conditional class definition
=========================================
<?php
if (true) {
class A {}
}
---
(program
(php_tag)
(if_statement
(parenthesized_expression (boolean))
(compound_statement
(class_declaration
(name)
(declaration_list)
)
)
)
)
=========================================
Class constant modifiers
=========================================
<?php
class Foo {
const A = 1;
public const B = 2;
protected const C = 3;
private const D = 4;
final const E = 5;
}
---
(program
(php_tag)
(class_declaration
(name)
(declaration_list
(const_declaration
(const_element (name) (integer))
)
(const_declaration
(visibility_modifier)
(const_element (name) (integer))
)
(const_declaration
(visibility_modifier)
(const_element (name) (integer))
)
(const_declaration
(visibility_modifier)
(const_element (name) (integer))
)
(const_declaration
(final_modifier)
(const_element (name) (integer))
)
)
)
)
=========================================
Final class
=========================================
<?php
final class A {}
---
(program
(php_tag)
(class_declaration
(final_modifier)
(name)
(declaration_list)
)
)
=========================================
Implicitly public properties and methods
=========================================
<?php
abstract class A {
var $a;
static $b;
abstract function c();
final function d() {}
static function e() {}
final static function f() {}
function g() {}
}
---
(program
(php_tag)
(class_declaration
(abstract_modifier)
(name)
(declaration_list
(property_declaration
(var_modifier)
(property_element (variable_name (name)))
)
(property_declaration
(static_modifier)
(property_element (variable_name (name)))
)
(method_declaration
(abstract_modifier)
(name)
(formal_parameters)
)
(method_declaration
(final_modifier)
(name)
(formal_parameters)
(compound_statement)
)
(method_declaration
(static_modifier)
(name)
(formal_parameters)
(compound_statement)
)
(method_declaration
(final_modifier)
(static_modifier)
(name)
(formal_parameters)
(compound_statement)
)
(method_declaration
(name)
(formal_parameters)
(compound_statement)
)
)
)
)
=========================================
Property Types
=========================================
<?php
class A {
public string $a;
protected static D $b;
private ?float $c;
private $d;
}
---
(program
(php_tag)
(class_declaration
name: (name)
body: (declaration_list
(property_declaration
(visibility_modifier)
type: (union_type (primitive_type))
(property_element (variable_name (name)))
)
(property_declaration
(visibility_modifier)
(static_modifier)
type: (union_type (named_type (name)))
(property_element (variable_name (name)))
)
(property_declaration
(visibility_modifier)
type: (union_type (optional_type (primitive_type)))
(property_element (variable_name (name)))
)
(property_declaration
(visibility_modifier)
(property_element (variable_name (name)))
)
)
)
)
=========================================
Constructor Property Promotion
=========================================
<?php
class Point {
public function __construct(
public float $x = 0.0,
float $y = 0.0,
private float $z = 0.0
) {}
}
---
(program
(php_tag)
(class_declaration
name: (name)
body: (declaration_list
(method_declaration
(visibility_modifier)
name: (name)
parameters: (formal_parameters
(property_promotion_parameter
visibility: (visibility_modifier)
type: (union_type
(primitive_type)
)
name: (variable_name (name))
default_value: (float)
)
(simple_parameter
type: (union_type (primitive_type))
name: (variable_name (name))
default_value: (float)
)
(property_promotion_parameter
visibility: (visibility_modifier)
type: (union_type (primitive_type))
name: (variable_name (name))
default_value: (float)
)
)
body: (compound_statement)
)
)
)
)

@ -0,0 +1,603 @@
=========================================
Interface declarations
=========================================
<?php
interface ThrowableInterface {
public function getMessage();
}
class Exception_foo implements ThrowableInterface {
public $foo = "foo";
public function getMessage() {
return $this->foo;
}
}
---
(program
(php_tag)
(interface_declaration
name: (name)
body: (declaration_list
(method_declaration
(visibility_modifier)
name: (name)
parameters: (formal_parameters))))
(class_declaration
name: (name)
(class_interface_clause (name))
body: (declaration_list
(property_declaration
(visibility_modifier)
(property_element (variable_name (name)) (property_initializer (encapsed_string (string)))))
(method_declaration
(visibility_modifier)
name: (name)
parameters: (formal_parameters)
body: (compound_statement
(return_statement (member_access_expression
object: (variable_name (name))
name: (name))))))))
==========================
Use declarations
==========================
<?php
trait AbstractTrait
{
use LoggerAwareTrait;
use LoggerAwareTrait, OtherTrait {}
use LoggerAwareTrait, OtherTrait;
}
class AbstractCache
{
use AbstractTrait {
deleteItems as private;
AbstractTrait::deleteItem as delete;
AbstractTrait::hasItem as has;
}
}
---
(program
(php_tag)
(trait_declaration
(name)
(declaration_list
(use_declaration (name))
(use_declaration (name) (name) (use_list))
(use_declaration (name) (name))))
(class_declaration
(name)
(declaration_list
(use_declaration
(name)
(use_list
(use_as_clause (name) (visibility_modifier))
(use_as_clause (class_constant_access_expression (name) (name)) (name))
(use_as_clause (class_constant_access_expression (name) (name)) (name)))))))
==========================
Namespace names in namespaces
==========================
<?php
namespace Be \ ta {
class A {}
class B {}
}
---
(program
(php_tag)
(namespace_definition
name: (namespace_name (name) (name))
body: (compound_statement
(class_declaration
name: (name)
body: (declaration_list))
(class_declaration
name: (name)
body: (declaration_list)))))
==============================
Class declarations
==============================
<?php
class foo {
function __construct($name) {
$GLOBALS['List']= &$this;
$this->Name = $name;
$GLOBALS['List']->echoName();
}
function echoName() {
$GLOBALS['names'][]=$this->Name;
}
}
---
(program
(php_tag)
(class_declaration
name: (name)
body: (declaration_list
(method_declaration
name: (name)
parameters: (formal_parameters
(simple_parameter
name: (variable_name (name))))
body: (compound_statement
(expression_statement (reference_assignment_expression
left: (subscript_expression (variable_name (name)) (string))
right: (variable_name (name))))
(expression_statement (assignment_expression
left: (member_access_expression
object: (variable_name (name))
name: (name))
right: (variable_name (name))))
(expression_statement (member_call_expression
object: (subscript_expression (variable_name (name)) (string))
name: (name)
arguments: (arguments)))))
(method_declaration
name: (name)
parameters: (formal_parameters)
body: (compound_statement
(expression_statement (assignment_expression
left: (subscript_expression (subscript_expression (variable_name (name)) (string)))
right: (member_access_expression
object: (variable_name (name))
name: (name)))))))))
========================================
Class declarations with base classes
========================================
<?php
class A extends B {
}
---
(program
(php_tag)
(class_declaration
name: (name)
(base_clause (name))
body: (declaration_list)))
==========================
Function parameters
==========================
<?php
function test(int $a, string ...$b)
{
}
---
(program
(php_tag)
(function_definition
name: (name)
parameters: (formal_parameters
(simple_parameter
type: (union_type (primitive_type))
name: (variable_name (name)))
(variadic_parameter
type: (union_type (primitive_type))
name: (variable_name (name))))
body: (compound_statement)))
====================================
Functions with default parameters
====================================
<?php
function a($arg = self::bar) {
echo $arg;
}
---
(program
(php_tag)
(function_definition
(name)
(formal_parameters
(simple_parameter
(variable_name (name))
(class_constant_access_expression (relative_scope) (name))))
(compound_statement (echo_statement (variable_name (name))))))
========================================================================
Static variables in functions
========================================================================
<?php
function blah()
{
static $hey=0, $yo=0;
}
---
(program
(php_tag)
(function_definition
(name)
(formal_parameters)
(compound_statement
(function_static_declaration
(static_variable_declaration (variable_name (name)) (integer))
(static_variable_declaration (variable_name (name)) (integer))))))
=========================================
Defining Constants
=========================================
<?php
define("CONSTANT", "Hello world.");
const CONSTANT = 'Hello World';
const ANOTHER_CONST = CONSTANT.'; Goodbye World';
const ANIMALS = array('dog', 'cat', 'bird');
define('ANIMALS', array(
'dog',
'cat',
'bird'
));
---
(program
(php_tag)
(expression_statement
(function_call_expression
(name)
(arguments
(argument (encapsed_string (string)))
(argument (encapsed_string (string)))
)
)
)
(const_declaration
(const_element
(name)
(string)
)
)
(const_declaration
(const_element
(name)
(binary_expression
(name)
(string)
)
)
)
(const_declaration
(const_element
(name)
(array_creation_expression
(array_element_initializer (string))
(array_element_initializer (string))
(array_element_initializer (string))
)
)
)
(expression_statement
(function_call_expression
(name)
(arguments
(argument (string))
(argument
(array_creation_expression
(array_element_initializer (string))
(array_element_initializer (string))
(array_element_initializer (string))
)
)
)
)
)
)
=======================================
Attributes
=======================================
<?php
#[Test]
function a(#[Test] $a) {
$c;
}
class PostsController
{
#[Test]
const CONSTANT = 'constant value';
#[Test]
private string $a = '';
#[Route("/api/posts/{id}", ["GET"])]
public function get(#[Test] $id) { /* ... */ }
}
#[MyAttribute]
#[\MyExample\MyAttribute]
#[MyAttribute(1234)]
#[MyAttribute(MyAttribute::VALUE)]
#[MyAttribute(array("key" => "value"))]
#[MyAttribute(100 + 200)]
class Thing
{
}
new #[ExampleAttribute] class() {};
#[ExampleAttribute] fn($x) => $x;
$baz = #[ExampleAttribute] function($x) {return $x;};
---
(program
(php_tag)
(function_definition
attributes: (attribute_list (attribute (name)))
name: (name)
parameters: (formal_parameters
(simple_parameter
attributes: (attribute_list (attribute (name)))
name: (variable_name (name))
)
)
body: (compound_statement (expression_statement (variable_name (name))))
)
(class_declaration
name: (name)
body: (declaration_list
(const_declaration
attributes: (attribute_list (attribute (name)))
(const_element (name) (string))
)
(property_declaration
attributes: (attribute_list (attribute (name)))
(visibility_modifier)
type: (union_type (primitive_type))
(property_element (variable_name (name))
(property_initializer (string))
)
)
(method_declaration
attributes: (attribute_list
(attribute
(name)
parameters: (arguments
(argument (encapsed_string (string)))
(argument
(array_creation_expression
(array_element_initializer (encapsed_string (string)))
)
)
)
)
)
(visibility_modifier)
name: (name)
parameters: (formal_parameters
(simple_parameter
attributes: (attribute_list (attribute (name)))
name: (variable_name (name))
)
)
body: (compound_statement (comment))
)
)
)
(class_declaration
attributes: (attribute_list
(attribute (name))
(attribute
(qualified_name
(namespace_name_as_prefix
(namespace_name (name))
)
(name)
)
)
(attribute
(name)
parameters: (arguments (argument (integer)))
)
(attribute
(name)
parameters: (arguments
(argument
(class_constant_access_expression
(name)
(name)
)
)
)
)
(attribute
(name)
parameters: (arguments
(argument
(array_creation_expression
(array_element_initializer
(encapsed_string (string))
(encapsed_string (string))
)
)
)
)
)
(attribute
(name)
parameters: (arguments
(argument
(binary_expression
left: (integer)
right: (integer)
)
)
)
)
)
name: (name)
body: (declaration_list)
)
(expression_statement
(object_creation_expression
attributes: (attribute_list (attribute (name)))
(arguments)
(declaration_list)
)
)
(expression_statement
(arrow_function
attributes: (attribute_list (attribute (name)))
parameters: (formal_parameters
(simple_parameter
name: (variable_name (name))
)
)
body: (variable_name (name))
)
)
(expression_statement
(assignment_expression
left: (variable_name (name))
right: (anonymous_function_creation_expression
attributes: (attribute_list (attribute (name)))
parameters: (formal_parameters
(simple_parameter
name: (variable_name (name))
)
)
body: (compound_statement
(return_statement
(variable_name (name))
)
)
)
)
)
)
=======================================
Enums
=======================================
<?php
enum A {}
enum B implements Bar, Baz {
}
enum C: int implements Bar {}
enum Suit: string
{
case Hearts = 'H';
case Diamonds;
case Clubs = 'C';
case Spades = 'S';
// Fulfills the interface contract.
public function color(): string {
return match($this) {
Suit::Hearts, Suit::Diamonds => 'Red',
Suit::Clubs, Suit::Spades => 'Black',
};
}
}
---
(program
(php_tag)
(enum_declaration
(name)
(enum_declaration_list)
)
(enum_declaration
(name)
(class_interface_clause
(name)
(name)
)
(enum_declaration_list)
)
(enum_declaration
(name)
(union_type (primitive_type))
(class_interface_clause (name))
(enum_declaration_list)
)
(enum_declaration
(name)
(union_type (primitive_type))
(enum_declaration_list
(enum_case (name) (string))
(enum_case (name))
(enum_case (name) (string))
(enum_case (name) (string))
(comment)
(method_declaration
(visibility_modifier)
(name)
(formal_parameters)
(union_type (primitive_type))
(compound_statement
(return_statement
(match_expression
(parenthesized_expression
(variable_name (name))
)
(match_block
(match_conditional_expression
(match_condition_list
(class_constant_access_expression (name) (name))
(class_constant_access_expression (name) (name))
)
(string)
)
(match_conditional_expression
(match_condition_list
(class_constant_access_expression (name) (name))
(class_constant_access_expression (name) (name))
)
(string)
)
)
)
)
)
)
)
)
)

File diff suppressed because it is too large Load Diff

@ -0,0 +1,188 @@
====================
no interpolated text
====================
<?php
echo "hi";
---
(program
(php_tag)
(echo_statement (encapsed_string (string))))
===============================
interpolated text at beginning
===============================
<div>
<?php
echo "hi";
---
(program
(text)
(php_tag)
(echo_statement (encapsed_string (string))))
===============================
interpolated text at end
===============================
<?php
echo "hi";
?>
<div>
---
(program
(php_tag)
(echo_statement (encapsed_string (string)))
(text_interpolation (text)))
===============================
interpolated text in middle
===============================
<?php
echo "hi";
?>
<div>
<?php
echo "bye";
?>
---
(program
(php_tag)
(echo_statement (encapsed_string (string)))
(text_interpolation
(text)
(php_tag))
(echo_statement (encapsed_string (string)))
(text_interpolation))
==============================
short open tag: On
==============================
<?
echo "Used a short tag\n";
?>
Finished
---
(program
(php_tag)
(echo_statement (encapsed_string (string) (escape_sequence)))
(text_interpolation (text)))
==============================
short open tag: Off
==============================
<div>one</div>
<?php
$a = 'This gets echoed twice';
?>
<?= $a ?>
<div>two</div>
<? $b=3; ?>
<?php
echo "{$b}";
?>
<?= "{$b}" ?>
---
(program
(text)
(php_tag)
(expression_statement (assignment_expression (variable_name (name)) (string)))
(text_interpolation (php_tag))
(expression_statement (variable_name (name)))
(text_interpolation (text) (php_tag))
(expression_statement (assignment_expression (variable_name (name)) (integer)))
(text_interpolation (php_tag))
(echo_statement (encapsed_string (variable_name (name))))
(text_interpolation (php_tag))
(expression_statement (encapsed_string (variable_name (name))))
(text_interpolation))
======================
Single line php comment
======================
<ul class="foo"><?php // this is a comment ?></ul>
<?php
// foo?
// foo? bar?
echo "hi";
---
(program
(text)
(php_tag)
(comment)
(text_interpolation (text) (php_tag))
(comment)
(comment)
(echo_statement (encapsed_string (string))))
=======================================
Singel line comment without any content
=======================================
<?php
# Check if PHP xml isn't compiled
#
if ( ! function_exists('xml_parser_create') ) {
echo $test;
}
---
(program
(php_tag)
(comment)
(comment)
(if_statement
condition: (parenthesized_expression
(unary_op_expression
(function_call_expression
function: (name)
arguments: (arguments (argument (string)))
)
)
)
body: (compound_statement (echo_statement (variable_name (name))))
)
)
=====================================
Closing tags before the first PHP tag
=====================================
a ?> b <?php c;
---
(program
(text)
(php_tag)
(expression_statement
(name)))

@ -0,0 +1,217 @@
========================
Booleans
========================
<?php
True;
true;
TRUE;
false;
False;
FALSE;
?>
---
(program
(php_tag)
(expression_statement (boolean))
(expression_statement (boolean))
(expression_statement (boolean))
(expression_statement (boolean))
(expression_statement (boolean))
(expression_statement (boolean))
(text_interpolation))
==========================
Floats
==========================
<?php
1.0;
1E432;
1.0E-3432;
1423.0E3432;
.5;
6.674_083e11;
107_925_284.88;
---
(program
(php_tag)
(expression_statement (float))
(expression_statement (float))
(expression_statement (float))
(expression_statement (float))
(expression_statement (float))
(expression_statement (float))
(expression_statement (float))
)
==========================
Integers
==========================
<?php
1234;
1_234_456;
0123;
0123_456;
0x1A;
0x1A_2B_3C;
0b111111111;
0b1111_1111_1111;
0o123;
---
(program
(php_tag)
(expression_statement (integer))
(expression_statement (integer))
(expression_statement (integer))
(expression_statement (integer))
(expression_statement (integer))
(expression_statement (integer))
(expression_statement (integer))
(expression_statement (integer))
(expression_statement (integer))
)
============================
Testing string scanner confirmance
==============================
<?php echo "\"\t\\'" . '\n\\\'a\\\b\\' ?>
---
(program
(php_tag)
(echo_statement (binary_expression (encapsed_string (escape_sequence) (escape_sequence) (escape_sequence) (string)) (string)))
(text_interpolation))
==========================
Shell command
==========================
<?php
`ls -la`;
`ls`;
---
(program
(php_tag)
(expression_statement (shell_command_expression))
(expression_statement (shell_command_expression)))
==========================
Heredocs
==========================
<?php
<<<HERE
foo #{bar} HERE;
<<<HERE
foo #{bar}
HERE;
?>
<?php
<<<HERE
foo #{bar} HERE;
<<<HERE
foo #{bar}
HERE;
<<< HERE
foo #{bar}
HERE;
// Allow Heredoc as function argument
read(<<< HERE
foo #{bar}
HERE);
read(<<< HERE
foo #{bar}
HERE , true);
---
(program
(php_tag)
(expression_statement (heredoc))
(expression_statement (heredoc))
(text_interpolation (php_tag))
(expression_statement (heredoc))
(expression_statement (heredoc))
(expression_statement (heredoc))
(comment)
(expression_statement
(function_call_expression
(name)
(arguments (argument (heredoc)))
)
)
(expression_statement
(function_call_expression
(name)
(arguments
(argument (heredoc))
(argument (boolean))
)
)
)
)
==========================
Nowdocs
==========================
<?php
<<<'PHP'
<?php echo phpversion().PHP_SAPI;
PHP
?>
---
(program
(php_tag)
(expression_statement (heredoc))
(text_interpolation))
==============================
Unicode escape sequences
==============================
<?php
"\u{61}"; // ASCII "a" - characters below U+007F just encode as ASCII, as it's UTF-8
"\u{FF}"; // y with diaeresis
"\u{ff}"; // case-insensitive
"\u{2603}"; // Unicode snowman
"\u{1F602}"; // FACE WITH TEARS OF JOY emoji
"\u{0000001F602}"; // Leading zeroes permitted
---
(program
(php_tag)
(expression_statement (encapsed_string (escape_sequence))) (comment)
(expression_statement (encapsed_string (escape_sequence))) (comment)
(expression_statement (encapsed_string (escape_sequence))) (comment)
(expression_statement (encapsed_string (escape_sequence))) (comment)
(expression_statement (encapsed_string (escape_sequence))) (comment)
(expression_statement (encapsed_string (escape_sequence))) (comment))

@ -0,0 +1,469 @@
==============================
If statements
==============================
<?php
if ($a > 0) {
echo "Yes";
}
if ($a==0) {
echo "bad";
} else {
echo "good";
}
if ($a==0) {
echo "bad";
} elseif ($a==3) {
echo "bad";
} else {
echo "good";
}
---
(program
(php_tag)
(if_statement
condition: (parenthesized_expression (binary_expression
left: (variable_name (name))
right: (integer)))
body: (compound_statement (echo_statement (encapsed_string (string)))))
(if_statement
condition: (parenthesized_expression (binary_expression
left: (variable_name (name))
right: (integer)))
body: (compound_statement (echo_statement (encapsed_string (string))))
alternative: (else_clause
body: (compound_statement (echo_statement (encapsed_string (string))))))
(if_statement
condition: (parenthesized_expression (binary_expression
left: (variable_name (name))
right: (integer)))
body: (compound_statement (echo_statement (encapsed_string (string))))
alternative: (else_if_clause
condition: (parenthesized_expression (binary_expression
left: (variable_name (name))
right: (integer)))
body: (compound_statement (echo_statement (encapsed_string (string)))))
alternative: (else_clause
body: (compound_statement (echo_statement (encapsed_string (string)))))))
==============================
Alternative if statements
==============================
<?php
if ($a) echo 1; else echo 0;
if ($a):
echo 1;
echo 2;
else:
echo 0;
endif;
---
(program
(php_tag)
(if_statement
condition: (parenthesized_expression (variable_name (name)))
body: (echo_statement (integer))
alternative: (else_clause
body: (echo_statement (integer))))
(if_statement
condition: (parenthesized_expression (variable_name (name)))
body: (colon_block
(echo_statement (integer))
(echo_statement (integer)))
alternative: (else_clause
body: (colon_block
(echo_statement (integer))))))
====================================
Wordpress colon blocks
====================================
<?php
if ($post) :
?>
<?php
if ( $open ) {
$attachment_id;
}
?>
<?php
else :
$post;
endif;
?>
---
(program
(php_tag)
(if_statement
condition: (parenthesized_expression (variable_name (name)))
body: (colon_block
(text_interpolation (php_tag))
(if_statement
condition: (parenthesized_expression (variable_name (name)))
body: (compound_statement (expression_statement (variable_name (name))))
)
)
(text_interpolation (php_tag))
alternative: (else_clause
body: (colon_block
(expression_statement (variable_name (name)))
)
)
)
(text_interpolation)
)
==============================
While statements
==============================
<?php
while ($a < 10) {
echo $a;
$a++;
}
---
(program
(php_tag)
(while_statement
condition: (parenthesized_expression (binary_expression
left: (variable_name (name))
right: (integer)))
body: (compound_statement
(echo_statement (variable_name (name)))
(expression_statement (update_expression (variable_name (name)))))))
==============================
Alternative while statements
==============================
<?php
while ($a<5) echo $a++;
while ($a<9):
echo ++$a;
echo $b;
endwhile;
---
(program
(php_tag)
(while_statement
condition: (parenthesized_expression (binary_expression
left: (variable_name (name))
right: (integer)))
body: (echo_statement (update_expression (variable_name (name)))))
(while_statement
condition: (parenthesized_expression (binary_expression
left: (variable_name (name))
right: (integer)))
body: (colon_block
(echo_statement (update_expression (variable_name (name))))
(echo_statement (variable_name (name))))))
==============================
For statements
==============================
<?php
for($a=0;$a<5;$a++) echo $a;
for($a=0;$a<5;$a++):
echo $a;
endfor;
---
(program
(php_tag)
(for_statement
(assignment_expression (variable_name (name)) (integer))
(binary_expression (variable_name (name)) (integer))
(update_expression (variable_name (name)))
(echo_statement (variable_name (name))))
(for_statement
(assignment_expression (variable_name (name)) (integer))
(binary_expression (variable_name (name)) (integer))
(update_expression (variable_name (name)))
(echo_statement (variable_name (name)))))
==============================
Switch statements
==============================
<?php
switch ($a) {
case 0:
echo "bad";
break;
case 1:
echo "good";
break;
default:
echo "bad";
break;
}
?>
---
(program
(php_tag)
(switch_statement
condition: (parenthesized_expression (variable_name (name)))
body: (switch_block
(case_statement
value: (integer)
(echo_statement (encapsed_string (string))) (break_statement))
(case_statement
value: (integer)
(echo_statement (encapsed_string (string))) (break_statement))
(default_statement
(echo_statement (encapsed_string (string))) (break_statement))))
(text_interpolation))
==============================
Alternative switch statements
==============================
<?php
switch ($a):
case 0;
echo 0;
break;
case 5:
echo 1;
break;
default;
echo 0;
break;
endswitch;
---
(program
(php_tag)
(switch_statement
condition: (parenthesized_expression (variable_name (name)))
body: (switch_block
(case_statement
value: (integer)
(echo_statement (integer))
(break_statement))
(case_statement
value: (integer)
(echo_statement (integer))
(break_statement))
(default_statement
(echo_statement (integer))
(break_statement)))))
==============================
Include statement
==============================
<?php
include "015.inc";
---
(program
(php_tag)
(expression_statement (include_expression (encapsed_string (string)))))
==============================
Do-while statements
==============================
<?php
do {
echo $i;
$i--;
} while($i>0);
---
(program
(php_tag)
(do_statement
body: (compound_statement
(echo_statement (variable_name (name)))
(expression_statement (update_expression (variable_name (name)))))
condition: (parenthesized_expression (binary_expression
left: (variable_name (name))
right: (integer)))))
==============================
Try statements
==============================
<?php
try {
} catch (MyException) {
} catch (OtherException|YetAnotherException $e) {
} finally {
}
try {
ThrowException();
} catch (MyException $exception) {
print "There was an exception: " . $exception->getException();
print "\n";
}
---
(program
(php_tag)
(try_statement
body: (compound_statement)
(catch_clause
type: (type_list
(named_type (name))
)
body: (compound_statement))
(catch_clause
type: (type_list
(named_type (name))
(named_type (name))
)
name: (variable_name (name))
body: (compound_statement))
(finally_clause
body: (compound_statement)))
(try_statement
body: (compound_statement
(expression_statement (function_call_expression
function: (name)
arguments: (arguments))))
(catch_clause
type: (type_list
(named_type (name))
)
name: (variable_name (name))
body: (compound_statement
(expression_statement (print_intrinsic (binary_expression
left: (encapsed_string (string))
right: (member_call_expression
object: (variable_name (name))
name: (name)
arguments: (arguments)))))
(expression_statement (print_intrinsic (encapsed_string (escape_sequence))))))))
==============================
Foreach statements
==============================
<?php
foreach ($a as $b[0]) {
echo $b[0]."\n";
}
foreach($arr as $key => $value);
foreach($a as $b):
echo $a;
echo $b;
endforeach;
---
(program
(php_tag)
(foreach_statement
(variable_name (name))
(subscript_expression (variable_name (name)) (integer))
body: (compound_statement
(echo_statement (binary_expression
left: (subscript_expression (variable_name (name)) (integer))
right: (encapsed_string (escape_sequence))))))
(foreach_statement
(variable_name (name))
(pair (variable_name (name)) (variable_name (name))))
(foreach_statement
(variable_name (name))
(variable_name (name))
body: (colon_block
(echo_statement (variable_name (name)))
(echo_statement (variable_name (name))))))
=================================
Case insensitive keywords
=================================
<?php
FOREACH ($a AS $b) {
DO {
if ($c) {
d();
} else {
e();
}
} while ($f);
}
---
(program
(php_tag)
(foreach_statement
(variable_name (name))
(variable_name (name))
(compound_statement
(do_statement
(compound_statement
(if_statement
(parenthesized_expression (variable_name (name)))
(compound_statement
(expression_statement (function_call_expression (name) (arguments))))
(else_clause
(compound_statement
(expression_statement (function_call_expression (name) (arguments)))))))
(parenthesized_expression (variable_name (name)))))))
=========================================
Accessing Constants
=========================================
<?php
echo ANOTHER_CONST;
echo ANIMALS[1];
---
(program
(php_tag)
(echo_statement
(name)
)
(echo_statement
(subscript_expression
(name)
(integer)
)
)
)

@ -0,0 +1,655 @@
=======================================
Complex: Variable access
=======================================
<?php
"{$test}";
---
(program
(php_tag)
(expression_statement
(encapsed_string
(variable_name (name))
)
)
)
=======================================
Complex: Disallow space between { and $
=======================================
<?php
"{ $test}";
---
(program
(php_tag)
(expression_statement
(encapsed_string
(string)
(variable_name (name))
(string)
)
)
)
=========================================
Complex: PHP documentation tests
=========================================
<?php
"This is {$great}";
"This square is {$square->width}00 centimeters broad.";
// Works, quoted keys only work using the curly brace syntax
"This works: {$arr['key']}";
"This works: {$arr[4][3]}";
// Works. When using multi-dimensional arrays, always use braces around arrays
// when inside of strings
"This works: {$arr['foo'][3]}";
"This works: " . $arr['foo'][3];
"This works too: {$obj->values[3]->name}";
"This is the value of the var named $name: {${$name}}";
"This is the value of the var named by the return value of getName(): {${getName()}}";
"This is the value of the var named by the return value of \$object->getName(): {${$object->getName()}}";
// Won't work, outputs: This is the return value of getName(): {getName()}
"This is the return value of getName(): {getName()}";
"{$foo->$bar}\n";
"{$foo->{$baz[1]}}\n";
"I'd like an {${beers::softdrink}}\n";
"I'd like an {${beers::$ale}}\n";
---
(program
(php_tag)
(expression_statement
(encapsed_string
(string)
(variable_name (name))
)
)
(expression_statement
(encapsed_string
(string)
(member_access_expression
(variable_name (name))
(name)
)
(string)
)
)
(comment)
(expression_statement
(encapsed_string
(string)
(subscript_expression
(variable_name (name))
(string)
)
)
)
(expression_statement
(encapsed_string
(string)
(subscript_expression
(subscript_expression
(variable_name (name))
(integer)
)
(integer)
)
)
)
(comment)
(comment)
(expression_statement
(encapsed_string
(string)
(subscript_expression
(subscript_expression
(variable_name (name))
(string)
)
(integer)
)
)
)
(expression_statement
(binary_expression
(encapsed_string
(string)
)
(subscript_expression
(subscript_expression
(variable_name (name))
(string)
)
(integer)
)
)
)
(expression_statement
(encapsed_string
(string)
(member_access_expression
(subscript_expression
(member_access_expression
(variable_name (name))
(name)
)
(integer)
)
(name)
)
)
)
(expression_statement
(encapsed_string
(string)
(variable_name (name))
(string)
(dynamic_variable_name
(variable_name (name))
)
)
)
(expression_statement
(encapsed_string
(string)
(dynamic_variable_name
(function_call_expression
(name)
(arguments)
)
)
)
)
(expression_statement
(encapsed_string
(string)
(escape_sequence)
(string)
(dynamic_variable_name
(member_call_expression
(variable_name (name))
(name)
(arguments)
)
)
)
)
(comment)
(expression_statement
(encapsed_string (string))
)
(expression_statement
(encapsed_string
(member_access_expression
(variable_name (name))
(variable_name (name))
)
(escape_sequence)
)
)
(expression_statement
(encapsed_string
(member_access_expression
(variable_name (name))
(subscript_expression
(variable_name (name))
(integer)
)
)
(escape_sequence)
)
)
(expression_statement
(encapsed_string
(string)
(dynamic_variable_name
(class_constant_access_expression
(name)
(name)
)
)
(escape_sequence)
)
)
(expression_statement
(encapsed_string
(string)
(dynamic_variable_name
(scoped_property_access_expression
(name)
(variable_name (name))
)
)
(escape_sequence)
)
)
)
=======================================
Simple: Variable access
=======================================
<?php
"Hello $people, you're awesome!";
"hello ${a} world";
---
(program
(php_tag)
(expression_statement
(encapsed_string
(string)
(variable_name (name))
(string)
)
)
(expression_statement
(encapsed_string
(string)
(dynamic_variable_name (name))
(string)
)
)
)
=========================================
Simple: Member and array access
=========================================
<?php
"$people->john drank some $juices[0] juice.".PHP_EOL;
"$people->john then said hello to $people->jane.".PHP_EOL;
"$people->john's wife greeted $people->robert.";
"The character at index -2 is $string[-2].";
---
(program
(php_tag)
(expression_statement
(binary_expression
(encapsed_string
(member_access_expression
(variable_name (name))
(name)
)
(string)
(subscript_expression
(variable_name (name))
(integer)
)
(string)
)
(name)
)
)
(expression_statement
(binary_expression
(encapsed_string
(member_access_expression
(variable_name (name))
(name)
)
(string)
(member_access_expression
(variable_name (name))
(name)
)
(string)
)
(name)
)
)
(expression_statement
(encapsed_string
(member_access_expression
(variable_name (name))
(name)
)
(string)
(member_access_expression
(variable_name (name))
(name)
)
(string)
)
)
(expression_statement
(encapsed_string
(string)
(subscript_expression
(variable_name (name))
(unary_op_expression (integer))
)
(string)
)
)
)
=========================================
Corner cases
=========================================
<?php
"{";
"{\$";
"{ $";
"/a";
"#";
"//";
"/*";
"/* text *#//";
"/**/";
"// # /**/";
"\\";
"\{";
"";
"\$notavar";
"\\\\\$notavar";
"\\\{$embedexp}";
"#x$var";
" # x $var#x";
"sometext$var";
"{$var::get()}";
"Test $var->tester- Hello";
" # x {$var->prop["key:"."key: {$var->func("arg")}"]}# x";
"hello \0 world";
"hello ${"a"."b"} world";
"$$$$$$$$$$$$$a";
"{$$$$$$$$b}";
"\{$";
"${a}[";
"\u{$a}";
---
(program
(php_tag)
(expression_statement
(encapsed_string
(string)
)
)
(expression_statement
(encapsed_string
(string)
(escape_sequence)
)
)
(expression_statement
(encapsed_string
(string)
)
)
(expression_statement
(encapsed_string (string))
)
(expression_statement
(encapsed_string (string))
)
(expression_statement
(encapsed_string (string))
)
(expression_statement
(encapsed_string (string))
)
(expression_statement
(encapsed_string (string))
)
(expression_statement
(encapsed_string (string))
)
(expression_statement
(encapsed_string (string))
)
(expression_statement
(encapsed_string (escape_sequence))
)
(expression_statement
(encapsed_string (string))
)
(expression_statement
(encapsed_string)
)
(expression_statement
(encapsed_string
(escape_sequence)
(string)
)
)
(expression_statement
(encapsed_string
(escape_sequence)
(escape_sequence)
(escape_sequence)
(string)
)
)
(expression_statement
(encapsed_string
(escape_sequence)
(string)
(variable_name (name))
(string)
)
)
(expression_statement
(encapsed_string
(string)
(variable_name (name))
)
)
(expression_statement
(encapsed_string
(string)
(variable_name (name))
(string)
)
)
(expression_statement
(encapsed_string
(string)
(variable_name (name))
)
)
(expression_statement
(encapsed_string
(scoped_call_expression
(variable_name (name))
(name)
(arguments)
)
)
)
(expression_statement
(encapsed_string
(string)
(member_access_expression
(variable_name (name))
(name)
)
(string)
)
)
(expression_statement
(encapsed_string
(string)
(subscript_expression
(member_access_expression
(variable_name (name))
(name)
)
(binary_expression
(encapsed_string (string))
(encapsed_string
(string)
(member_call_expression
(variable_name (name))
(name)
(arguments
(argument
(encapsed_string (string))
)
)
)
)
)
)
(string)
)
)
(expression_statement
(encapsed_string
(string)
(escape_sequence)
(string)
)
)
(expression_statement
(encapsed_string
(string)
(dynamic_variable_name
(binary_expression
(encapsed_string (string))
(encapsed_string (string))
)
)
(string)
)
)
(expression_statement
(encapsed_string
(string)
(variable_name (name))
)
)
(expression_statement
(encapsed_string
(dynamic_variable_name
(dynamic_variable_name
(dynamic_variable_name
(dynamic_variable_name
(dynamic_variable_name
(dynamic_variable_name
(dynamic_variable_name
(variable_name (name))
)
)
)
)
)
)
)
)
)
(expression_statement
(encapsed_string
(string)
)
)
(expression_statement
(encapsed_string
(dynamic_variable_name (name))
(string)
)
)
(expression_statement
(encapsed_string
(string)
(variable_name (name))
)
)
)
=========================================
Single quoted
=========================================
<?php
'this is a simple string';
'You can also have embedded newlines in
strings this way as it is
okay to do';
'Arnold once said: "I\'ll be back"';
'You deleted C:\\*.*?';
'You deleted C:\*.*?';
'This will not expand: \n a newline';
'Variables do not $expand $either';
'socket://';
'#valid regexp#';
'hello#world';
'hello//world';
'/*valid regexp*/';
'/*valid regexp';
---
(program
(php_tag)
(expression_statement (string))
(expression_statement (string))
(expression_statement (string))
(expression_statement (string))
(expression_statement (string))
(expression_statement (string))
(expression_statement (string))
(expression_statement (string))
(expression_statement (string))
(expression_statement (string))
(expression_statement (string))
(expression_statement (string))
(expression_statement (string))
)
=========================================
Bug: #113
=========================================
<?php
"$b'";
"'";
---
(program
(php_tag)
(expression_statement
(encapsed_string
(variable_name (name))
(string)
)
)
(expression_statement
(encapsed_string
(string)
)
)
)

@ -0,0 +1,166 @@
=========================
Type names
=========================
<?php
function a(): A {}
function b(): A\B {}
---
(program
(php_tag)
(function_definition
(name) (formal_parameters)
(union_type (named_type (name)))
(compound_statement))
(function_definition
(name) (formal_parameters)
(union_type
(named_type (qualified_name (namespace_name_as_prefix (namespace_name (name))) (name)))
)
(compound_statement)))
=========================
Primitive types
=========================
<?php
function a(): int {}
function b(): callable {}
function c(): iterable {}
---
(program
(php_tag)
(function_definition
(name) (formal_parameters)
(union_type (primitive_type))
(compound_statement))
(function_definition
(name) (formal_parameters)
(union_type (primitive_type))
(compound_statement))
(function_definition
(name) (formal_parameters)
(union_type (primitive_type))
(compound_statement)))
=======================
Optional types
=======================
<?php
function a(): ?array {}
function b(): ?Something {}
---
(program
(php_tag)
(function_definition
(name) (formal_parameters)
(union_type
(optional_type (primitive_type))
)
(compound_statement))
(function_definition
(name) (formal_parameters)
(union_type
(optional_type (named_type (name)))
)
(compound_statement)))
==========================
Union types
==========================
<?php
function a(int|string|null $var) : ?int|MyClass {}
---
(program
(php_tag)
(function_definition
name: (name)
parameters: (formal_parameters
(simple_parameter
type: (union_type
(primitive_type)
(primitive_type)
(primitive_type)
)
name: (variable_name (name))
)
)
return_type: (union_type
(optional_type
(primitive_type)
)
(named_type (name))
)
body: (compound_statement)
)
)
==========================
Mixed type
==========================
<?php
function a(mixed|string $var) : mixed {
}
---
(program
(php_tag)
(function_definition
(name)
(formal_parameters
(simple_parameter
(union_type
(primitive_type)
(primitive_type)
)
(variable_name (name))
)
)
(union_type (primitive_type))
(compound_statement)
)
)
==========================
Static type
==========================
<?php
function a(string $var) : static {
}
---
(program
(php_tag)
(function_definition
(name)
(formal_parameters
(simple_parameter
(union_type
(primitive_type)
)
(variable_name (name))
)
)
(union_type (primitive_type))
(compound_statement)
)
)

@ -0,0 +1,47 @@
<?php
// <- tag
if ($a) {}
// <- keyword
while ($b) {}
// <- keyword
WHILE ($b) {}
// <- keyword
do { } while ($c);
// <- keyword
// ^ keyword
try {} catch (Exception $e) {}
// <- keyword
// ^ keyword
function a() {}
// <- keyword
class A {}
// <- keyword
throw new Exception("oh");
// <- keyword
// ^ keyword
function b(
int $a,
// <- type.builtin
string $b,
// <- type.builtin
Person $e
// ^ type
): Dog {}
// ^ type
interface T {}
// ^ keyword
trait T {}
// ^ keyword

@ -0,0 +1,16 @@
<?php
echo <<<OMG
something
OMG
// <- string
echo true, TRUE, false, FALSE
// ^ constant.builtin
// ^ constant.builtin
// ^ constant.builtin
// ^ constant.builtin
echo PI_314
// ^ constant

@ -0,0 +1,9 @@
<?php
function a(array $b, Something $c) {
// ^ type.builtin
// ^ type
echo (int) $foo;
// ^ type.builtin
}