deploy: v0.12 fix 存档key名

This commit is contained in:
2026-06-24 03:09:02 +00:00
parent 7a08d49fe3
commit e0a5281119
58743 changed files with 7297269 additions and 39505 deletions
+1
View File
@@ -0,0 +1 @@
35197
+67
View File
@@ -0,0 +1,67 @@
## 1.0.5 (2025-09-23)
### Bug fixes
Make the package work with new TS resolution styles.
## 1.0.4 (2025-07-22)
### Bug fixes
Properly parse intersection types.
Allow using keywords as member names in `->` syntax.
Properly parse class property hooks.
Parse dynamic scoped member access (`::{...}`).
Properly parse `readonly` modifiers on properties and anonymous classes.
Allow names after namespace separators to be keywords.
## 1.0.3 (2025-07-07)
### Bug fixes
Allow const declarations in enum bodies.
## 1.0.2 (2023-12-28)
### Bug fixes
Tag comments and strings as isolating for the purpose of bidirectional text.
## 1.0.1 (2023-01-18)
### Bug fixes
Fix an issue where the grammar didn't handle less-than characters at the end of the document correctly.
Remove use of `require` as an identifier in the build output, which could break CommonJS builds.
## 1.0.0 (2022-06-06)
### New features
First stable version.
## 0.16.0 (2022-04-20)
### Breaking changes
Move to 0.16 serialized parser format.
### Bug fixes
Allow `class` to be an identifier in constructs like `A::class`. Add highlighting information
### New features
The parser now includes syntax highlighting information in its node types.
## 0.15.0 (2021-09-03)
### New features
First numbered release.
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (C) 2018 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
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.
+11
View File
@@ -0,0 +1,11 @@
# @lezer/php
This is a PHP grammar for the
[Lezer](https://lezer.codemirror.net/) parser system.
The grammar used is based in part on the corresponding [tree-sitter
grammar](https://github.com/tree-sitter/tree-sitter-php) and the [Zend
Yacc
grammar](https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y).
The code is licensed under an MIT license.
+362
View File
File diff suppressed because one or more lines are too long
+3
View File
@@ -0,0 +1,3 @@
import {LRParser} from "@lezer/lr"
export const parser: LRParser
+3
View File
@@ -0,0 +1,3 @@
import {LRParser} from "@lezer/lr"
export const parser: LRParser
+358
View File
File diff suppressed because one or more lines are too long
+36
View File
@@ -0,0 +1,36 @@
{
"name": "@lezer/php",
"version": "1.0.5",
"description": "Lezer-based PHP grammar",
"main": "dist/index.cjs",
"type": "module",
"exports": {
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"module": "dist/index.js",
"types": "dist/index.d.ts",
"author": "Marijn Haverbeke <marijn@haverbeke.berlin>",
"license": "MIT",
"devDependencies": {
"@lezer/generator": "^1.2.2",
"mocha": "^10.2.0",
"rollup": "^2.52.2",
"@rollup/plugin-node-resolve": "^9.0.0"
},
"dependencies": {
"@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.1.0"
},
"repository": {
"type" : "git",
"url" : "https://github.com/lezer-parser/php.git"
},
"scripts": {
"build": "lezer-generator src/php.grammar -o src/parser && rollup -c",
"build-debug": "lezer-generator src/php.grammar --names -o src/parser && rollup -c",
"prepare": "npm run build",
"test": "mocha test/test-*.js"
}
}
+16
View File
@@ -0,0 +1,16 @@
import {nodeResolve} from "@rollup/plugin-node-resolve"
export default {
input: "./src/parser.js",
output: [{
format: "cjs",
file: "./dist/index.cjs"
}, {
format: "es",
file: "./dist/index.js"
}],
external(id) { return !/^[\.\/]/.test(id) },
plugins: [
nodeResolve()
]
}
+49
View File
@@ -0,0 +1,49 @@
import {styleTags, tags as t} from "@lezer/highlight"
export const phpHighlighting = styleTags({
"Visibility abstract final static": t.modifier,
"for foreach while do if else elseif switch try catch finally return throw break continue default case": t.controlKeyword,
"endif endfor endforeach endswitch endwhile declare enddeclare goto match": t.controlKeyword,
"and or xor yield unset clone instanceof insteadof": t.operatorKeyword,
"function fn class trait implements extends const enum global interface use var": t.definitionKeyword,
"include include_once require require_once namespace": t.moduleKeyword,
"new from echo print array list as": t.keyword,
null: t.null,
Boolean: t.bool,
VariableName: t.variableName,
"NamespaceName/...": t.namespace,
"NamedType/...": t.typeName,
Name: t.name,
"CallExpression/Name": t.function(t.variableName),
"LabelStatement/Name": t.labelName,
"MemberExpression/Name": t.propertyName,
"MemberExpression/VariableName": t.special(t.propertyName),
"ScopedExpression/ClassMemberName/Name": t.propertyName,
"ScopedExpression/ClassMemberName/VariableName": t.special(t.propertyName),
"CallExpression/MemberExpression/Name": t.function(t.propertyName),
"CallExpression/ScopedExpression/ClassMemberName/Name": t.function(t.propertyName),
"MethodDeclaration/Name": t.function(t.definition(t.variableName)),
"FunctionDefinition/Name": t.function(t.definition(t.variableName)),
"ClassDeclaration/Name": t.definition(t.className),
UpdateOp: t.updateOperator,
ArithOp: t.arithmeticOperator,
"LogicOp IntersectionType/&": t.logicOperator,
BitOp: t.bitwiseOperator,
CompareOp: t.compareOperator,
ControlOp: t.controlOperator,
AssignOp: t.definitionOperator,
"$ ConcatOp": t.operator,
LineComment: t.lineComment,
BlockComment: t.blockComment,
Integer: t.integer,
Float: t.float,
String: t.string,
ShellExpression: t.special(t.string),
"=> ->": t.punctuation,
"( )": t.paren,
"#[ [ ]": t.squareBracket,
"${ { }": t.brace,
"-> ?->": t.derefOperator,
", ; :: : \\": t.separator,
"PhpOpen PhpClose": t.processingInstruction,
})
+28
View File
File diff suppressed because one or more lines are too long
+123
View File
@@ -0,0 +1,123 @@
// This file was generated by lezer-generator. You probably shouldn't edit it.
export const
castOpen = 1,
HeredocString = 2,
interpolatedStringContent = 275,
EscapeSequence = 3,
afterInterpolation = 276,
automaticSemicolon = 277,
eof = 278,
abstract = 4,
and = 5,
array = 6,
as = 7,
Boolean = 8,
_break = 9,
_case = 10,
_catch = 11,
clone = 12,
_const = 13,
_continue = 14,
_default = 15,
declare = 16,
_do = 17,
echo = 18,
_else = 19,
elseif = 20,
enddeclare = 21,
endfor = 22,
endforeach = 23,
endif = 24,
endswitch = 25,
endwhile = 26,
_enum = 27,
_extends = 28,
final = 29,
_finally = 30,
fn = 31,
_for = 32,
foreach = 33,
from = 34,
_function = 35,
global = 36,
goto = 37,
_if = 38,
_implements = 39,
include = 40,
include_once = 41,
_instanceof = 42,
insteadof = 43,
_interface = 44,
list = 45,
match = 46,
namespace = 47,
_new = 48,
_null = 49,
or = 50,
print = 51,
readonly = 52,
_require = 53,
require_once = 54,
_return = 55,
_switch = 56,
_throw = 57,
trait = 58,
_try = 59,
unset = 60,
use = 61,
_var = 62,
Visibility = 63,
_while = 64,
xor = 65,
_yield = 66,
LineComment = 67,
BlockComment = 68,
TextInterpolation = 69,
PhpClose = 70,
Text = 71,
PhpOpen = 72,
Template = 73,
Block = 79,
Name = 82,
ParenthesizedExpression = 89,
MatchArm = 91,
MemberName = 111,
VariableName = 112,
DynamicVariable = 113,
ArgList = 117,
NamespaceNameInner = 128,
NamespaceName = 130,
ScopedExpression = 132,
ClassMemberName = 134,
UpdateOp = 138,
BinaryExpression = 140,
_static = 162,
ParamList = 163,
Attributes = 166,
Attribute = 167,
PropertyHooks = 170,
PropertyHook = 171,
_class = 175,
BaseClause = 176,
ClassInterfaceClause = 177,
DeclarationList = 178,
ConstDeclarator = 180,
VariableDeclarator = 182,
MethodDeclaration = 183,
UseDeclaration = 184,
UseList = 185,
UseInsteadOfClause = 186,
UseAsClause = 187,
ShellExpression = 190,
Integer = 192,
Float = 193,
InterpolatedString = 194,
Interpolation = 199,
String = 200,
ColonBlock = 202,
CaseStatement = 205,
DefaultStatement = 206,
EnumBody = 232,
EnumCase = 233,
UseGroup = 236,
Program = 241
+558
View File
@@ -0,0 +1,558 @@
@precedence {
pair,
namespace,
member,
scope,
new,
call,
cast,
else @right,
optionalType,
unionType @left,
intersectionType @left,
incdec @left,
instanceof @left,
unary @left,
exponent @right,
mult @left,
plus @left,
shift @left,
concat @left,
compare @left,
equal @left,
binAnd @left,
binXor @left,
binOr @left,
logAnd @left,
logOr @left,
qq @right,
conditional @left,
update @right,
and @left,
xor @left,
or @left,
yield @right
}
@skip {} {
@top Template {
TextInterpolation { Text? PhpOpen } (LineComment | BlockComment | whitespace | TextInterpolation)* topStatements |
Text?
}
}
@top Program {
statement*
}
topStatements { statement* eof }
statement[@isGroup=Statement] {
EmptyStatement { ";" } |
Block |
LabelStatement { Name ":" } |
ExpressionStatement { expression semicolon } |
IfStatement {
if ParenthesizedExpression (
statement (!else elseif ParenthesizedExpression statement)* (!else else statement)? |
ColonBlock (elseif ParenthesizedExpression ColonBlock)* (else ColonBlock)? endif semicolon
)
} |
SwitchStatement {
switch ParenthesizedExpression (
Block { "{" (CaseStatement | DefaultStatement)* "}" } |
ColonBlock { ":" (CaseStatement | DefaultStatement)* } endswitch semicolon
)
} |
WhileStatement {
while ParenthesizedExpression (trailingStatement | ColonBlock endwhile semicolon)
} |
DoStatement {
do statement while ParenthesizedExpression semicolon
} |
ForStatement {
for ForSpec { "(" expressions? ";" expressions? ";" expressions? ")" }
(trailingStatement | ColonBlock endfor semicolon)
} |
ForeachStatement {
foreach ForSpec { "(" expression as (Pair { expression "=>" expression } | expression) ")" }
(trailingStatement | ColonBlock endforeach semicolon)
} |
GotoStatement {
goto Name semicolon
} |
ContinueStatement {
continue expression? semicolon
} |
BreakStatement {
break expression? semicolon
} |
ReturnStatement {
return expression? semicolon
} |
TryStatement {
try Block (catch CatchDeclarator { "(" type VariableName? ")" } Block | finally Block)+
} |
DeclareStatement {
declare "(" Name "=" literal ")" (trailingStatement | ColonBlock enddeclare semicolon)
} |
EchoStatement {
echo expressions semicolon
} |
UnsetStatement {
unset "(" commaSep1<expression> ")" semicolon
} |
ConstDeclaration {
Attributes* Visibility? const commaSep1<ConstDeclarator> semicolon
} |
FunctionDefinition {
Attributes* functionDefinitionHeader Block
} |
ClassDeclaration {
Attributes* (final | abstract)? class Name BaseClause? ClassInterfaceClause? DeclarationList
} |
InterfaceDeclaration {
interface Name BaseClause? DeclarationList
} |
TraitDeclaration {
trait Name DeclarationList
} |
EnumDeclaration {
Attributes* enum Name (":" type)? ClassInterfaceClause? EnumBody
} |
NamespaceDefinition {
namespace (qualifiedName semicolon | qualifiedName? Block)
} |
NamespaceUseDeclaration {
use (function | const)?
(commaSep1<qualifiedName (as Name)?> |
("\\" NamespaceNameInner | NamespaceName) !namespace "\\" (NamespaceNameInner "\\")* UseGroup)
semicolon
} |
GlobalDeclaration {
global commaSep1<expression> semicolon
} |
FunctionStaticDeclaration {
static commaSep1<VariableDeclarator> semicolon
}
}
qualifiedName {
Name |
QualifiedName {
"\\" (NamespaceNameInner !namespace "\\")* Name { rawName } |
NamespaceName !namespace "\\" (NamespaceNameInner "\\")* Name { rawName }
}
}
NamespaceName { name }
NamespaceNameInner[@name=NamespaceName] { rawName }
UseGroup {
"{" commaSep1<UseClause { (function | const)? qualifiedName (as Name)? }> "}"
}
BaseClause {
extends commaSep1<qualifiedName>
}
EnumBody {
"{" (EnumCase | memberDeclaration)* "}"
}
EnumCase {
Attributes* case Name ("=" (String | Integer))? semicolon
}
DeclarationList {
"{" memberDeclaration* "}"
}
ClassInterfaceClause {
implements commaSep1<qualifiedName>
}
memberDeclaration {
ConstDeclaration { Attributes* modifier* const type? commaSep1<ConstDeclarator> semicolon } |
PropertyDeclaration { Attributes* modifier* type? commaSep1<VariableDeclarator> (semicolon | PropertyHooks) } |
MethodDeclaration |
UseDeclaration
}
PropertyHooks { "{" PropertyHook* "}" }
PropertyHook { Name (ParamList? (Block | "=>" expression semicolon) | semicolon) }
modifier {
var |
Visibility |
static |
final |
abstract |
readonly
}
VariableDeclarator { VariableName ("=" expression)? }
ConstDeclarator[@name=VariableDeclarator] { Name "=" expression }
MethodDeclaration {
Attributes* modifier* functionDefinitionHeader (Block | semicolon)
}
UseDeclaration {
use commaSep1<qualifiedName> (UseList | semicolon)
}
UseList {
"{" ((UseInsteadOfClause | UseAsClause) semicolon)* "}"
}
UseInsteadOfClause {
(ScopedExpression | Name) insteadof Name
}
UseAsClause {
(ScopedExpression | Name) as (Visibility? Name | Visibility Name?)
}
functionDefinitionHeader {
function "&"? Name ParamList (":" type)?
}
ParamList {
"(" commaSep<parameter> ")"
}
parameter {
Parameter {
Attributes* type? "&"? VariableName ("=" expression)?
} |
VariadicParameter {
Attributes* type? "&"? "..." VariableName
} |
PropertyParameter {
Visibility type? VariableName ("=" expression | PropertyHooks)?
}
}
type[@isGroup=Type] {
UnionType { type (!unionType LogicOp<"|"> type)+ } |
IntersectionType { type (!intersectionType "&" type)+ } |
OptionalType { !optionalType LogicOp<"?"> type } |
NamedType { qualifiedName | null | array }
}
literal {
Integer |
Float |
string |
Boolean |
null
}
expressions {
expression |
SequenceExpression { expression ("," expression)+ }
}
ColonBlock { ":" statement* }
trailingStatement {
statement |
EmptyStatement { automaticSemicolon }
}
MatchArm { (default | commaSep1<expression>) "=>" expression }
CaseStatement { case expression (":" | ";") statement* }
DefaultStatement { default (":" | ";") statement* }
Block { "{" statement* "}" }
expression[@isGroup=Expression] {
ConditionalExpression { expression !conditional LogicOp<"?"> expression? ":" expression } |
MatchExpression { match ParenthesizedExpression MatchBlock { "{" commaSep1<MatchArm> "}" } } |
AssignmentExpression { variable !update "=" "&"? expression } |
UpdateExpression { variable !update UpdateOp expression } |
YieldExpression { yield (!yield arrayElement | from expression)? } |
BinaryExpression |
IncludeExpression { (include | include_once) expression } |
RequireExpression { (require | require_once) expression } |
CloneExpression { clone expression } |
UnaryExpression {
ControlOp<"@"> expression |
(ArithOp<"+" | "-"> | LogicOp<"~" | "!">) !unary expression
} |
qualifiedName |
PrintIntrinsic { print expression } |
FunctionExpression {
static? function "&"? ParamList (use UseList { "(" commaSep<"&"? VariableName> ")" })? (":" type)? Block
} |
ArrowFunction { static? fn "&"? ParamList (":" type)? "=>" expression } |
NewExpression {
new expression (!new ArgList)? |
new readonly? class ArgList? BaseClause? ClassInterfaceClause? DeclarationList
} |
IncDecExpression[@name=UpdateExpression] {
expression !incdec ArithOp<"++" | "--"> |
ArithOp<"++" | "--"> !incdec expression
} |
ShellExpression |
ParenthesizedExpression |
ThrowExpression { throw expression } |
variable |
literal
}
variable {
ArrayExpression {
array ValueList { "(" ","? commaSep<arrayElement> ")" } |
"[" ","? commaSep<arrayElement> "]"
} |
ListExpression {
list ValueList { "(" ","? commaSep<expression | Pair { expression !pair "=>" expression }> ")" }
} |
SubscriptExpression { expression !member "[" expression? "]" } |
MemberExpression { expression !member ("->" | "?->") memberName } |
DynamicVariable |
VariableName |
CallExpression { expression !call ArgList } |
CastExpression { castOpen type ")" !cast expression } |
ScopedExpression
}
DynamicVariable { "$" (VariableName | DynamicVariable) | "${" expression "}" }
ParenthesizedExpression { "(" expression ")" }
ScopedExpression { expression !scope "::" ClassMemberName }
ClassMemberName {
Name | VariableName | DynamicMemberName { "{" expression "}" }
}
ArgList {
"(" commaSep<argument> ")"
}
argument {
NamedArgument { Name ":" expression } |
SpreadArgument { "..." expression? } |
expression
}
memberName {
MemberName | VariableName | "{" expression "}"
}
arrayElement {
"&"? expression |
VariadicUnpacking { "..." expression } |
Pair { expression !pair "=>" "&"? expression }
}
Attributes {
"#[" commaSep1<Attribute> "]"
}
Attribute {
qualifiedName ArgList?
}
@skip {} {
TextInterpolation { PhpClose Text? (PhpOpen | eof) }
Text[isolate] { textElement+ }
InterpolatedString[@name=String] {
startInterpolatedString (
interpolatedStringContent |
EscapeSequence |
interpolatedExpression |
Interpolation
)* endInterpolatedString
}
// The afterInterpolation token isn't actually real, but used as a
// signal (via canShift) to the tokenizer to indicate it shouldn't
// consume '[' or '->' tokens.
interpolatedExpression {
VariableName |
MemberExpression {
interpolatedExpression afterInterpolation? ("?->" | "->") Name
} |
SubscriptExpression {
interpolatedExpression afterInterpolation?
"[" (Integer | UnaryExpression { ArithOp<"-"> Integer } | Name | VariableName) "]"
}
}
}
Interpolation { ("{" | "${") expression "}" }
string {
InterpolatedString |
HeredocString |
String
}
BinaryExpression {
expression !instanceof instanceof expression |
expression !qq LogicOp<"??"> expression |
expression !and and expression |
expression !or or expression |
expression !xor xor expression |
expression !logOr LogicOp<"||"> expression |
expression !logAnd LogicOp<"&&"> expression |
expression !binOr BitOp<"|"> expression |
expression !binXor BitOp<"^"> expression |
expression !binAnd BitOp { "&" } expression |
expression !equal CompareOp<"==" | "===" | "!=" | "!==" | "<>" | "<=>"> expression |
expression !compare CompareOp<"<" | ">" | "<=" | ">="> expression |
expression !shift BitOp<"<<" | ">>"> expression |
expression !plus ArithOp<"+" | "-"> expression |
expression !concat ConcatOp<"."> expression |
expression !mult ArithOp<"*" | "/" | "%"> expression |
expression !exponent ArithOp<"**"> expression
}
Name { name }
MemberName[@name=Name] { rawName }
semicolon {
automaticSemicolon |
";"
}
@skip { LineComment | BlockComment | whitespace | TextInterpolation }
@external specialize {name} keywords from "./tokens" {
abstract[@name=abstract], and[@name=LogicOp], array[@name=array], as[@name=as], Boolean,
break[@name=break], case[@name=case], catch[@name=catch],
clone[@name=clone], const[@name=const], continue[@name=continue], default[@name=default],
declare[@name=declare], do[@name=do], echo[@name=echo], else[@name=else], elseif[@name=elseif],
enddeclare[@name=enddeclare], endfor[@name=endfor], endforeach[@name=endforeach],
endif[@name=endif], endswitch[@name=endswitch], endwhile[@name=endwhile], enum[@name=enum],
extends[@name=extends], final[@name=final], finally[@name=finally], fn[@name=fn], for[@name=for],
foreach[@name=foreach], from[@name=from], function[@name=function], global[@name=global],
goto[@name=goto], if[@name=if], implements[@name=implements], include[@name=include],
include_once[@name=include_once], instanceof[@name=LogicOp], insteadof[@name=insteadof],
interface[@name=interface], list[@name=list], match[@name=match], namespace[@name=namespace],
new[@name=new], null[@name=null], or[@name=LogicOp], print[@name=print], readonly[@name=readonly],
require[@name=require], require_once[@name=require_once],
return[@name=return], switch[@name=switch], throw[@name=throw],
trait[@name=trait], try[@name=try], unset[@name=unset], use[@name=use], var[@name=var], Visibility,
while[@name=while], xor[@name=LogicOp], yield[@name=yield]
}
commaSep<content> { "" | content ("," content?)* }
commaSep1<content> { content ("," content?)* }
static[@dynamicPrecedence=1] { @extend[@name=static]<name, "static" | "STATIC"> }
class { @extend[@name=class]<name, "class" | "CLASS"> }
@external tokens expression from "./tokens" {
castOpen[@name="("],
HeredocString
}
@external tokens interpolated from "./tokens" {
interpolatedStringContent,
EscapeSequence,
afterInterpolation
}
@external tokens semicolon from "./tokens" { automaticSemicolon }
@tokens {
whitespace { $[ \t\n\r ]+ }
PhpOpen[closedBy=phpClose] { "<?" ($[pP] $[hH] $[pP] | "=")? }
PhpClose[openedBy=phpOpen] { "?>" }
textElement {
"\n" | ![\n<] textElement? | "<" ("\n" | @eof | ![\n\?] textElement?)
}
digit { $[0-9] }
hex { $[0-9A-Fa-f] }
oct { $[0-7] }
separatedDigits { ("_" digit+)* }
exponent { $[eE] $[+-]? digit+ separatedDigits }
Float {
digit+ separatedDigits ("." digit* separatedDigits exponent? | exponent) |
"." digit+ separatedDigits exponent?
}
Integer {
digit+ separatedDigits |
"0" $[oO] oct+ ("_" oct+)* |
"0" $[xX] hex+ ("_" hex+)* |
"0" $[bB] $[01]+ ("_" $[01]+)*
}
@precedence { Float, ConcatOp<".">, Integer }
ShellExpression { "`" ("\\" _ | ![`\\])* "`" }
UpdateOp { ("**" | "*" | "/" | "%" | "+" | "-" | "." | "<<" | ">>" | "&" | "^" | "|" | "??") "=" }
CompareOp<term> { term }
ArithOp<term> { term }
LogicOp<term> { term }
BitOp<term> { term }
ControlOp<term> { term }
ConcatOp<term> { term }
startInterpolatedString { $[bB]? '"' }
endInterpolatedString { '"' }
@precedence { startInterpolatedString, name }
String[isolate] { $[bB]? "'" (![\\'] | "\\" _)* "'"? }
@precedence { String, name }
letter { $[_a-zA-Z\u00A1-\u00ff] }
name { letter (letter | @digit)* }
rawName { name }
VariableName { "$" name }
@precedence { VariableName, "${", "$" }
LineComment[isolate] { ("//" | "#") lineCommentRest? }
lineCommentRest { ![\r\n?] lineCommentRest? | "?" lineCommentQuestion }
lineCommentQuestion { $[\r\n] | ![>\r\n] lineCommentRest? }
@precedence { "#[", LineComment, ArithOp<"*" | "/" | "%"> }
BlockComment[isolate] { "/*" blockCommentRest }
blockCommentRest { ![*] blockCommentRest | "*" blockCommentAfterStar }
blockCommentAfterStar { "/" | "*" blockCommentAfterStar | ![/*] blockCommentRest }
"${" "{" "}" "#[" "[" "]" "(" ")"
";" ":" "::" "," "\\" "?->" "->" "=>" "&" "$" "..."
"="[@name=AssignOp]
}
@external tokens eofToken from "./tokens" { eof }
@external propSource phpHighlighting from "./highlight"
@detectDelim
+221
View File
@@ -0,0 +1,221 @@
import {ExternalTokenizer} from "@lezer/lr"
import {
abstract, and, array, as, Boolean, _break, _case, _catch, clone, _const, _continue,
declare, _default, _do, echo, _else, elseif, enddeclare, endfor, endforeach, endif,
endswitch, endwhile, _enum, _extends, final, _finally, fn, _for, foreach, from,
_function, global, goto, _if, _implements, include, include_once, _instanceof,
insteadof, _interface, list, match, namespace, _new, _null, or, print, readonly, _require, require_once,
_return, _switch, _throw, trait, _try, unset, use, _var, Visibility,
_while, xor, _yield,
castOpen, eof, automaticSemicolon, HeredocString,
interpolatedStringContent, EscapeSequence, afterInterpolation
} from "./parser.terms.js"
const keywordMap = {
abstract,
and,
array,
as,
true: Boolean,
false: Boolean,
break: _break,
case: _case,
catch: _catch,
clone,
const: _const,
continue: _continue,
declare,
default: _default,
do: _do,
echo,
else: _else,
elseif,
enddeclare,
endfor,
endforeach,
endif,
endswitch,
endwhile,
enum: _enum,
extends: _extends,
final,
finally: _finally,
fn,
for: _for,
foreach,
from,
function: _function,
global,
goto,
if: _if,
implements: _implements,
include,
include_once,
instanceof: _instanceof,
insteadof,
interface: _interface,
list,
match,
namespace,
new: _new,
null: _null,
or,
print,
readonly,
require: _require,
require_once,
return: _return,
switch: _switch,
throw: _throw,
trait,
try: _try,
unset,
use,
var: _var,
public: Visibility,
private: Visibility,
protected: Visibility,
while: _while,
xor,
yield: _yield,
__proto__: null,
}
export function keywords(name) {
let found = keywordMap[name.toLowerCase()]
return found == null ? -1 : found
}
function isSpace(ch) {
return ch == 9 || ch == 10 || ch == 13 || ch == 32
}
function isASCIILetter(ch) {
return ch >= 97 && ch <= 122 || ch >= 65 && ch <= 90
}
function isIdentifierStart(ch) {
return ch == 95 || ch >= 0x80 || isASCIILetter(ch)
}
function isHex(ch) {
return ch >= 48 && ch <= 55 || ch >= 97 && ch <= 102 || ch >= 65 && ch <= 70 /* 0-9, a-f, A-F */
}
const castTypes = {
int: true, integer: true, bool: true, boolean: true,
float: true, double: true, real: true, string: true,
array: true, object: true, unset: true,
__proto__: null
}
export const expression = new ExternalTokenizer(input => {
if (input.next == 40 /* '(' */) {
input.advance()
let peek = 0
while (isSpace(input.peek(peek))) peek++
let name = "", next
while (isASCIILetter(next = input.peek(peek))) {
name += String.fromCharCode(next)
peek++
}
while (isSpace(input.peek(peek))) peek++
if (input.peek(peek) == 41 /* ')' */ && castTypes[name.toLowerCase()])
input.acceptToken(castOpen)
} else if (input.next == 60 /* '<' */ && input.peek(1) == 60 && input.peek(2) == 60) {
for (let i = 0; i < 3; i++) input.advance();
while (input.next == 32 /* ' ' */ || input.next == 9 /* '\t' */) input.advance()
let quoted = input.next == 39 /* "'" */
if (quoted) input.advance()
if (!isIdentifierStart(input.next)) return
let tag = String.fromCharCode(input.next)
for (;;) {
input.advance()
if (!isIdentifierStart(input.next) && !(input.next >= 48 && input.next <= 55) /* 0-9 */) break
tag += String.fromCharCode(input.next)
}
if (quoted) {
if (input.next != 39) return
input.advance()
}
if (input.next != 10 /* '\n' */ && input.next != 13 /* '\r' */) return
for (;;) {
let lineStart = input.next == 10 || input.next == 13
input.advance()
if (input.next < 0) return
if (lineStart) {
while (input.next == 32 /* ' ' */ || input.next == 9 /* '\t' */) input.advance()
let match = true
for (let i = 0; i < tag.length; i++) {
if (input.next != tag.charCodeAt(i)) { match = false; break }
input.advance()
}
if (match) return input.acceptToken(HeredocString)
}
}
}
})
export const eofToken = new ExternalTokenizer(input => {
if (input.next < 0) input.acceptToken(eof)
})
export const semicolon = new ExternalTokenizer((input, stack) => {
if (input.next == 63 /* '?' */ && stack.canShift(automaticSemicolon) && input.peek(1) == 62 /* '>' */)
input.acceptToken(automaticSemicolon)
})
function scanEscape(input) {
let after = input.peek(1)
if (after == 110 /* 'n' */ || after == 114 /* 'r' */ || after == 116 /* 't' */ ||
after == 118 /* 'v' */ || after == 101 /* 'e' */ || after == 102 /* 'f' */ ||
after == 92 /* '\\' */ || after == 36 /* '"' */ || after == 34 /* '$' */ ||
after == 123 /* '{' */)
return 2
if (after >= 48 && after <= 55 /* '0'-'7' */) {
let size = 2, next
while (size < 5 && (next = input.peek(size)) >= 48 && next <= 55) size++
return size
}
if (after == 120 /* 'x' */ && isHex(input.peek(2))) {
return isHex(input.peek(3)) ? 4 : 3
}
if (after == 117 /* 'u' */ && input.peek(2) == 123 /* '{' */) {
for (let size = 3;; size++) {
let next = input.peek(size)
if (next == 125 /* '}' */) return size == 2 ? 0 : size + 1
if (!isHex(next)) break
}
}
return 0
}
export const interpolated = new ExternalTokenizer((input, stack) => {
let content = false
for (;; content = true) {
if (input.next == 34 /* '"' */ || input.next < 0 ||
input.next == 36 /* '$' */ && (isIdentifierStart(input.peek(1)) || input.peek(1) == 123 /* '{' */) ||
input.next == 123 /* '{' */ && input.peek(1) == 36 /* '$' */) {
break
} else if (input.next == 92 /* '\\' */) {
let escaped = scanEscape(input)
if (escaped) {
if (content) break
else return input.acceptToken(EscapeSequence, escaped)
}
} else if (!content && (
input.next == 91 /* '[' */ ||
input.next == 45 /* '-' */ && input.peek(1) == 62 /* '>' */ && isIdentifierStart(input.peek(2)) ||
input.next == 63 /* '?' */ && input.peek(1) == 45 && input.peek(2) == 62 && isIdentifierStart(input.peek(3))
) && stack.canShift(afterInterpolation)) {
break
}
input.advance()
}
if (content) input.acceptToken(interpolatedStringContent)
})
+1
View File
@@ -0,0 +1 @@
39573
+365
View File
@@ -0,0 +1,365 @@
# Abstract class
<?php
abstract class A {
public function a() {}
abstract public function b();
}
==>
Template(
TextInterpolation(PhpOpen),
ClassDeclaration(abstract, class,
Name,
DeclarationList(
MethodDeclaration(Visibility, function,
Name,
ParamList,
Block
),
MethodDeclaration(abstract, Visibility, function,
Name,
ParamList
)
)
)
)
# 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';
};
}
}
==>
Template(
TextInterpolation(PhpOpen),
ExpressionStatement(
NewExpression(new, class,
DeclarationList(
MethodDeclaration(Visibility, function,
Name,
ParamList,
Block
)
)
)
),
ExpressionStatement(
NewExpression(new, class,
BaseClause(extends, Name),
ClassInterfaceClause(implements, Name, Name),
DeclarationList
)
),
ExpressionStatement(
NewExpression(new, class,
ArgList,
DeclarationList(
PropertyDeclaration(
Visibility,
VariableDeclarator(
VariableName
)
)
)
)
),
ExpressionStatement(
NewExpression(new, class,
ArgList(VariableName, VariableName),
BaseClause(extends, Name),
DeclarationList(
UseDeclaration(use,
Name
)
)
)
),
ClassDeclaration(class,
Name,
DeclarationList(
MethodDeclaration(Visibility, function,
Name,
ParamList,
Block(
ReturnStatement(return,
NewExpression(new, class,
ArgList(VariableName),
BaseClause(extends, Name),
DeclarationList(
ConstDeclaration(const,
VariableDeclarator(Name, AssignOp, String)
)
)
)
)
)
)
)
)
)
# Conditional class definition
<?php
if (true) {
class A {}
}
==>
Template(
TextInterpolation(PhpOpen),
IfStatement(if,
ParenthesizedExpression(Boolean),
Block(
ClassDeclaration(class,
Name,
DeclarationList
)
)
)
)
# 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;
}
==>
Template(
TextInterpolation(PhpOpen),
ClassDeclaration(class,
Name,
DeclarationList(
ConstDeclaration(const,
VariableDeclarator(Name, AssignOp, Integer)
),
ConstDeclaration(Visibility, const,
VariableDeclarator(Name, AssignOp, Integer)
),
ConstDeclaration(Visibility, const,
VariableDeclarator(Name, AssignOp, Integer)
),
ConstDeclaration(Visibility, const,
VariableDeclarator(Name, AssignOp, Integer)
),
ConstDeclaration(final, const,
VariableDeclarator(Name, AssignOp, Integer)
)
)
)
)
# Final class
<?php
final class A {}
==>
Template(
TextInterpolation(PhpOpen),
ClassDeclaration(final, class,
Name,
DeclarationList
)
)
# 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() {}
}
==>
Template(
TextInterpolation(PhpOpen),
ClassDeclaration(abstract, class,
Name,
DeclarationList(
PropertyDeclaration(
var,
VariableDeclarator(VariableName)
),
PropertyDeclaration(
static,
VariableDeclarator(VariableName)
),
MethodDeclaration(abstract, function,
Name,
ParamList
),
MethodDeclaration(final, function,
Name,
ParamList,
Block
),
MethodDeclaration(static, function,
Name,
ParamList,
Block
),
MethodDeclaration(final, static, function,
Name,
ParamList,
Block
),
MethodDeclaration(function,
Name,
ParamList,
Block
)
)
)
)
# Property Types
<?php
class A {
public string $a;
protected static D $b;
private ?float $c;
private $d;
}
==>
Template(
TextInterpolation(PhpOpen),
ClassDeclaration(class,
Name,
DeclarationList(
PropertyDeclaration(
Visibility,
NamedType(Name),
VariableDeclarator(VariableName)
),
PropertyDeclaration(
Visibility,
static,
NamedType(Name),
VariableDeclarator(VariableName)
),
PropertyDeclaration(
Visibility,
OptionalType(LogicOp, NamedType(Name)),
VariableDeclarator(VariableName)
),
PropertyDeclaration(
Visibility,
VariableDeclarator(VariableName)
)
)
)
)
# Constructor Property Promotion
<?php
class Point {
public function __construct(
public float $x = 0.0,
float $y = 0.0,
private float $z = 0.0
) {}
}
==>
Template(
TextInterpolation(PhpOpen),
ClassDeclaration(class,
Name,
DeclarationList(
MethodDeclaration(Visibility, function,
Name,
ParamList(
PropertyParameter(
Visibility,
NamedType(Name),
VariableName,
AssignOp,
Float
),
Parameter(
NamedType(Name),
VariableName,
AssignOp,
Float
),
PropertyParameter(
Visibility,
NamedType(Name),
VariableName,
AssignOp,
Float
)
),
Block
)
)
)
)
# Class constant
<?php
A::class;
==>
Template(
TextInterpolation(PhpOpen),
ExpressionStatement(
ScopedExpression(
Name,
ClassMemberName(Name)
),
";"
)
)
+535
View File
@@ -0,0 +1,535 @@
# Interface declarations
<?php
interface ThrowableInterface {
public function getMessage();
}
class Exception_foo implements ThrowableInterface {
public $foo = "foo";
public function getMessage() {
return $this->foo;
}
}
==>
Template(
TextInterpolation(PhpOpen),
InterfaceDeclaration(interface,
Name,
DeclarationList(
MethodDeclaration(
Visibility,
function,
Name,
ParamList))),
ClassDeclaration(
class,
Name,
ClassInterfaceClause(implements, Name),
DeclarationList(
PropertyDeclaration(
Visibility,
VariableDeclarator(VariableName, AssignOp, String)),
MethodDeclaration(
Visibility,
function,
Name,
ParamList,
Block(
ReturnStatement(return,MemberExpression(
VariableName,
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;
}
}
==>
Template(
TextInterpolation(PhpOpen),
TraitDeclaration(trait,
Name,
DeclarationList(
UseDeclaration(use,Name),
UseDeclaration(use,Name, Name, UseList),
UseDeclaration(use,Name, Name))),
ClassDeclaration(
class,
Name,
DeclarationList(
UseDeclaration(use,
Name,
UseList(
UseAsClause(Name, as, Visibility),
UseAsClause(ScopedExpression(Name, ClassMemberName(Name)), as, Name),
UseAsClause(ScopedExpression(Name, ClassMemberName(Name)), as, Name))))))
# Use Groups
<?php
use a as b;
use some\namespace\{ClassA, ClassB, ClassC as C};
use function some\namespace\{fn_a, fn_b, fn_c};
use const some\namespace\{ConstA, ConstB, ConstC};
==>
Template(
TextInterpolation(PhpOpen),
NamespaceUseDeclaration(use,Name,as,Name),
NamespaceUseDeclaration(use,NamespaceName,NamespaceName,UseGroup(UseClause(Name),UseClause(Name),UseClause(Name,as,Name))),
NamespaceUseDeclaration(use,function,NamespaceName,NamespaceName,UseGroup(UseClause(Name),UseClause(Name),UseClause(Name))),
NamespaceUseDeclaration(use,const,NamespaceName,NamespaceName,UseGroup(UseClause(Name),UseClause(Name),UseClause(Name))))
# Namespace names in namespaces
<?php
namespace Be \ ta {
class A {}
class B {}
}
==>
Template(
TextInterpolation(PhpOpen),
NamespaceDefinition(namespace,
QualifiedName(NamespaceName, Name),
Block(
ClassDeclaration(class,
Name,
DeclarationList),
ClassDeclaration(class,
Name,
DeclarationList))))
# Class declarations
<?php
class foo {
function __construct($name) {
$GLOBALS['List']= &$this;
$this->Name = $name;
$GLOBALS['List']->echoName();
}
function echoName() {
$GLOBALS['names'][]=$this->Name;
}
}
==>
Template(
TextInterpolation(PhpOpen),
ClassDeclaration(class,
Name,
DeclarationList(
MethodDeclaration(function,
Name,
ParamList(
Parameter(
VariableName)),
Block(
ExpressionStatement(AssignmentExpression(
SubscriptExpression(VariableName, String),
AssignOp,
VariableName)),
ExpressionStatement(AssignmentExpression(
MemberExpression(
VariableName,
Name),
AssignOp,
VariableName)),
ExpressionStatement(CallExpression(
MemberExpression(SubscriptExpression(VariableName, String), Name),
ArgList)))),
MethodDeclaration(function,
Name,
ParamList,
Block(
ExpressionStatement(AssignmentExpression(
SubscriptExpression(SubscriptExpression(VariableName, String)),
AssignOp,
MemberExpression(VariableName,Name))))))))
# Class declarations with base classes
<?php
class A extends B {
}
==>
Template(
TextInterpolation(PhpOpen),
ClassDeclaration(class,
Name,
BaseClause(extends, Name),
DeclarationList))
# Function parameters
<?php
function test(int $a, string ...$b)
{
}
==>
Template(
TextInterpolation(PhpOpen),
FunctionDefinition(function,
Name,
ParamList(
Parameter(
NamedType(Name),
VariableName),
VariadicParameter(
NamedType(Name),
VariableName)),
Block))
# Functions with default parameters
<?php
function a($arg = self::bar) {
echo $arg;
}
==>
Template(
TextInterpolation(PhpOpen),
FunctionDefinition(function,
Name,
ParamList(
Parameter(
VariableName,
AssignOp,
ScopedExpression(Name, ClassMemberName(Name)))),
Block(EchoStatement(echo,VariableName))))
# Static variables in functions
<?php
function blah()
{
static $hey=0, $yo=0;
}
==>
Template(
TextInterpolation(PhpOpen),
FunctionDefinition(function,
Name,
ParamList,
Block(
FunctionStaticDeclaration(static,
VariableDeclarator(VariableName, AssignOp, Integer),
VariableDeclarator(VariableName, AssignOp, 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'
));
==>
Template(
TextInterpolation(PhpOpen),
ExpressionStatement(
CallExpression(
Name,
ArgList(String,String)
)
),
ConstDeclaration(const,
VariableDeclarator(
Name,
AssignOp,
String
)
),
ConstDeclaration(const,
VariableDeclarator(
Name,
AssignOp,
BinaryExpression(
Name,
ConcatOp,
String
)
)
),
ConstDeclaration(const,
VariableDeclarator(
Name,
AssignOp,
ArrayExpression(array, ValueList(String,String,String))
)
),
ExpressionStatement(
CallExpression(
Name,
ArgList(
String,
ArrayExpression(array, ValueList(String,String,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
{
}
==>
Template(
TextInterpolation(PhpOpen),
FunctionDefinition(
Attributes(Attribute(Name)),
function,
Name,
ParamList(
Parameter(
Attributes(Attribute(Name)),
VariableName
)
),
Block(ExpressionStatement(VariableName))
),
ClassDeclaration(class,
Name,
DeclarationList(
ConstDeclaration(
Attributes(Attribute(Name)),
const,
VariableDeclarator(Name, AssignOp, String)
),
PropertyDeclaration(
Attributes(Attribute(Name)),
Visibility,
NamedType(Name),
VariableDeclarator(VariableName, AssignOp, String)
),
MethodDeclaration(
Attributes(
Attribute(
Name,
ArgList(
String,
ArrayExpression(String)
)
)
),
Visibility,
function,
Name,
ParamList(
Parameter(
Attributes(Attribute(Name)),
VariableName
)
),
Block(BlockComment)
)
)
),
ClassDeclaration(
Attributes(Attribute(Name)),
Attributes(Attribute(QualifiedName(NamespaceName,Name))),
Attributes(Attribute(Name,ArgList(Integer))),
Attributes(Attribute(Name,ArgList(ScopedExpression(Name,ClassMemberName(Name))))),
Attributes(Attribute(Name,ArgList(ArrayExpression(array, ValueList(Pair(String, String)))))),
Attributes(Attribute(Name,ArgList(BinaryExpression(Integer, ArithOp, Integer)))),
class,
Name,
DeclarationList
)
)
# 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',
};
}
public const X = self::Clubs;
}
==>
Template(
TextInterpolation(PhpOpen),
EnumDeclaration(enum,
Name,
EnumBody
),
EnumDeclaration(enum,
Name,
ClassInterfaceClause(implements, Name, Name),
EnumBody
),
EnumDeclaration(enum,
Name,
NamedType(Name),
ClassInterfaceClause(implements, Name),
EnumBody
),
EnumDeclaration(enum,
Name,
NamedType(Name),
EnumBody(
EnumCase(case, Name, AssignOp, String),
EnumCase(case, Name),
EnumCase(case, Name, AssignOp, String),
EnumCase(case, Name, AssignOp, String),
LineComment,
MethodDeclaration(
Visibility,
function,
Name,
ParamList,
NamedType(Name),
Block(
ReturnStatement(return,
MatchExpression(match,
ParenthesizedExpression(
VariableName
),
MatchBlock(
MatchArm(
ScopedExpression(Name, ClassMemberName(Name)),
ScopedExpression(Name, ClassMemberName(Name)),
String
),
MatchArm(
ScopedExpression(Name, ClassMemberName(Name)),
ScopedExpression(Name, ClassMemberName(Name)),
String
)
)
)
)
)
),
ConstDeclaration(Visibility,const,VariableDeclarator(Name,AssignOp,ScopedExpression(Name,ClassMemberName(Name))))
)
)
)
# Property Hooks
<?php
class Example
{
private bool $modified = false;
public string $foo = 'default value' {
get => $this->foo . ($this->modified ? ' (modified)' : '');
set(string $value) {
$this->foo = strtolower($value);
$this->modified = true;
}
}
}
==>
Template(TextInterpolation(PhpOpen),ClassDeclaration(
class,Name,DeclarationList(
PropertyDeclaration(Visibility,NamedType(Name),VariableDeclarator(VariableName,AssignOp,Boolean)),
PropertyDeclaration(Visibility,NamedType(Name),VariableDeclarator(VariableName,AssignOp,String),PropertyHooks(
PropertyHook(Name,BinaryExpression(MemberExpression(VariableName,Name),ConcatOp,ParenthesizedExpression(
ConditionalExpression(MemberExpression(VariableName,Name),LogicOp,String,String)))),
PropertyHook(Name,ParamList(Parameter(NamedType(Name),VariableName)),Block(
ExpressionStatement(AssignmentExpression(MemberExpression(VariableName,Name),AssignOp,CallExpression(Name,ArgList( VariableName)))),
ExpressionStatement(AssignmentExpression(MemberExpression(VariableName,Name),AssignOp,Boolean)))))))))
+1024
View File
File diff suppressed because it is too large Load Diff
+166
View File
@@ -0,0 +1,166 @@
# no interpolated text
<?php
echo "hi";
==>
Template(
TextInterpolation(PhpOpen),
EchoStatement(echo,String))
# interpolated text at beginning
<div>
<?php
echo "hi";
==>
Template(
TextInterpolation(Text, PhpOpen),
EchoStatement(echo,String))
# interpolated text at end
<?php
echo "hi";
?>
<div>
==>
Template(
TextInterpolation(PhpOpen),
EchoStatement(echo,String),
TextInterpolation(PhpClose, Text))
# interpolated text in middle
<?php
echo "hi";
?>
<div>
<?php
echo "bye";
?>
==>
Template(
TextInterpolation(PhpOpen),
EchoStatement(echo,String),
TextInterpolation(PhpClose, Text, PhpOpen),
EchoStatement(echo,String),
TextInterpolation(PhpClose))
# short open tag: On
<?
echo "Used a short tag\n";
?>
Finished
==>
Template(
TextInterpolation(PhpOpen),
EchoStatement(echo,String(EscapeSequence)),
TextInterpolation(PhpClose, Text))
# short open tag: Off
<div>one</div>
<?php
$a = 'This gets echoed twice';
?>
<?= $a ?>
<div>two</div>
<? $b=3; ?>
<?php
echo "{$b}";
?>
<?= "{$b}" ?>
==>
Template(
TextInterpolation(Text,PhpOpen),
ExpressionStatement(AssignmentExpression(VariableName, AssignOp, String)),
TextInterpolation(PhpClose,Text,PhpOpen),
ExpressionStatement(VariableName),
TextInterpolation(PhpClose,Text,PhpOpen),
ExpressionStatement(AssignmentExpression(VariableName, AssignOp, Integer)),
TextInterpolation(PhpClose,Text,PhpOpen),
EchoStatement(echo,String(Interpolation(VariableName))),
TextInterpolation(PhpClose,Text,PhpOpen),
ExpressionStatement(String(Interpolation(VariableName))),
TextInterpolation(PhpClose))
# Single line php comment
<ul class="foo"><?php // this is a comment ?></ul>
<?php
// foo?
// foo? bar?
echo "hi";
==>
Template(
TextInterpolation(Text, PhpOpen),
LineComment,
TextInterpolation(PhpClose, Text, PhpOpen),
LineComment,
LineComment,
EchoStatement(echo,String))
# Single line comment without any content
<?php
# Check if PHP xml isn't compiled
#
if ( ! function_exists('xml_parser_create') ) {
echo $test;
}
==>
Template(
TextInterpolation(PhpOpen),
LineComment,
LineComment,
IfStatement(if,
ParenthesizedExpression(
UnaryExpression(LogicOp, CallExpression(Name,ArgList(String)))
),
Block(EchoStatement(echo,VariableName))
)
)
# Closing tags before the first PHP tag
a ?> b <?php c;
==>
Template(
TextInterpolation(Text, PhpOpen),
ExpressionStatement(Name))
# Text ends in a less-than char
foo<
==>
Template(Text)
+195
View File
@@ -0,0 +1,195 @@
# Booleans
<?php
True;
true;
TRUE;
false;
False;
FALSE;
?>
==>
Template(
TextInterpolation(PhpOpen),
ExpressionStatement(Boolean),
ExpressionStatement(Boolean),
ExpressionStatement(Boolean),
ExpressionStatement(Boolean),
ExpressionStatement(Boolean),
ExpressionStatement(Boolean),
TextInterpolation(PhpClose))
# Floats
<?php
1.0;
1E432;
1.0E-3432;
1423.0E3432;
.5;
6.674_083e11;
107_925_284.88;
==>
Template(
TextInterpolation(PhpOpen),
ExpressionStatement(Float),
ExpressionStatement(Float),
ExpressionStatement(Float),
ExpressionStatement(Float),
ExpressionStatement(Float),
ExpressionStatement(Float),
ExpressionStatement(Float)
)
# Integers
<?php
1234;
1_234_456;
0123;
0123_456;
0x1A;
0x1A_2B_3C;
0b111111111;
0b1111_1111_1111;
0o123;
==>
Template(
TextInterpolation(PhpOpen),
ExpressionStatement(Integer),
ExpressionStatement(Integer),
ExpressionStatement(Integer),
ExpressionStatement(Integer),
ExpressionStatement(Integer),
ExpressionStatement(Integer),
ExpressionStatement(Integer),
ExpressionStatement(Integer),
ExpressionStatement(Integer)
)
# Testing string scanner conformance
<?php echo "\"\t\\'" . '\n\\\'a\\\b\\' ?>
==>
Template(
TextInterpolation(PhpOpen),
EchoStatement(echo, BinaryExpression(String(EscapeSequence, EscapeSequence, EscapeSequence), ConcatOp, String)),
TextInterpolation(PhpClose))
# Shell command
<?php
`ls -la`;
`ls`;
==>
Template(
TextInterpolation(PhpOpen),
ExpressionStatement(ShellExpression),
ExpressionStatement(ShellExpression))
# Heredocs
<?php
<<<HERE
foo #{bar}
HERE;
?>
<?php
<<<HERE
foo #{bar}
HERE;
<<< HERE
foo #{bar}
HERE;
// Allow Heredoc as function argument
read(<<< HERE
foo #{bar}
HERE);
read(<<< HERE
foo #{bar}
HERE , true);
==>
Template(
TextInterpolation(PhpOpen),
ExpressionStatement(HeredocString),
TextInterpolation(PhpClose,Text,PhpOpen),
ExpressionStatement(HeredocString),
ExpressionStatement(HeredocString),
LineComment,
ExpressionStatement(
CallExpression(
Name,
ArgList(HeredocString)
)
),
ExpressionStatement(
CallExpression(
Name,
ArgList(
HeredocString,
Boolean
)
)
)
)
# Nowdocs
<?php
<<<'PHP'
<?php echo phpversion().PHP_SAPI;
PHP
?>
==>
Template(
TextInterpolation(PhpOpen),
ExpressionStatement(HeredocString),
TextInterpolation(PhpClose))
# 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
==>
Template(
TextInterpolation(PhpOpen),
ExpressionStatement(String(EscapeSequence)), LineComment,
ExpressionStatement(String(EscapeSequence)), LineComment,
ExpressionStatement(String(EscapeSequence)), LineComment,
ExpressionStatement(String(EscapeSequence)), LineComment,
ExpressionStatement(String(EscapeSequence)), LineComment,
ExpressionStatement(String(EscapeSequence)), LineComment)
+364
View File
@@ -0,0 +1,364 @@
# 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";
}
==>
Template(
TextInterpolation(PhpOpen),
IfStatement(if,
ParenthesizedExpression(BinaryExpression(
VariableName,
CompareOp,
Integer)),
Block(EchoStatement(echo,String))),
IfStatement(if,
ParenthesizedExpression(BinaryExpression(
VariableName,
CompareOp,
Integer)),
Block(EchoStatement(echo,String)),
else, Block(EchoStatement(echo,String))),
IfStatement(if,
ParenthesizedExpression(BinaryExpression(
VariableName,
CompareOp,
Integer)),
Block(EchoStatement(echo,String)),
elseif, ParenthesizedExpression(BinaryExpression(VariableName,CompareOp,Integer)),
Block(EchoStatement(echo,String)),
else, Block(EchoStatement(echo,String))))
# Alternative if statements
<?php
if ($a) echo 1; else echo 0;
if ($a):
echo 1;
echo 2;
else:
echo 0;
endif;
==>
Template(
TextInterpolation(PhpOpen),
IfStatement(if,
ParenthesizedExpression(VariableName),
EchoStatement(echo,Integer),
else, EchoStatement(echo,Integer)),
IfStatement(if,
ParenthesizedExpression(VariableName),
ColonBlock(
EchoStatement(echo,Integer),
EchoStatement(echo,Integer)),
else, ColonBlock(EchoStatement(echo,Integer)),
endif))
# While statements
<?php
while ($a < 10) {
echo $a;
$a++;
}
==>
Template(
TextInterpolation(PhpOpen),
WhileStatement(while,
ParenthesizedExpression(BinaryExpression(VariableName,CompareOp,Integer)),
Block(
EchoStatement(echo,VariableName),
ExpressionStatement(UpdateExpression(VariableName, ArithOp)))))
# Alternative while statements
<?php
while ($a<5) echo $a++;
while ($a<9):
echo ++$a;
echo $b;
endwhile;
==>
Template(
TextInterpolation(PhpOpen),
WhileStatement(while,
ParenthesizedExpression(BinaryExpression(VariableName, CompareOp, Integer)),
EchoStatement(echo,UpdateExpression(VariableName, ArithOp))),
WhileStatement(while,
ParenthesizedExpression(BinaryExpression(VariableName, CompareOp, Integer)),
ColonBlock(
EchoStatement(echo,UpdateExpression(ArithOp, VariableName)),
EchoStatement(echo,VariableName)),
endwhile))
# For statements
<?php
for($a=0;$a<5;$a++) echo $a;
for($a=0;$a<5;$a++):
echo $a;
endfor;
==>
Template(
TextInterpolation(PhpOpen),
ForStatement(for,
ForSpec(
AssignmentExpression(VariableName, AssignOp, Integer),
BinaryExpression(VariableName, CompareOp, Integer),
UpdateExpression(VariableName, ArithOp)),
EchoStatement(echo,VariableName)),
ForStatement(for,
ForSpec(
AssignmentExpression(VariableName, AssignOp, Integer),
BinaryExpression(VariableName, CompareOp, Integer),
UpdateExpression(VariableName, ArithOp)),
ColonBlock(EchoStatement(echo,VariableName)),
endfor))
# Switch statements
<?php
switch ($a) {
case 0:
echo "bad";
break;
case 1:
echo "good";
break;
default:
echo "bad";
break;
}
?>
==>
Template(
TextInterpolation(PhpOpen),
SwitchStatement(switch,
ParenthesizedExpression(VariableName),
Block(
CaseStatement(case,
Integer,
EchoStatement(echo,String), BreakStatement(break)),
CaseStatement(case,
Integer,
EchoStatement(echo,String), BreakStatement(break)),
DefaultStatement(default,
EchoStatement(echo,String), BreakStatement(break)))),
TextInterpolation(PhpClose))
# Alternative switch statements
<?php
switch ($a):
case 0;
echo 0;
break;
case 5:
echo 1;
break;
default;
echo 0;
break;
endswitch;
==>
Template(
TextInterpolation(PhpOpen),
SwitchStatement(switch,
ParenthesizedExpression(VariableName),
ColonBlock(
CaseStatement(case,
Integer,
EchoStatement(echo,Integer),
BreakStatement(break)),
CaseStatement(case,
Integer,
EchoStatement(echo,Integer),
BreakStatement(break)),
DefaultStatement(default,
EchoStatement(echo,Integer),
BreakStatement(break)))
endswitch))
# Include statement
<?php
include "015.inc";
==>
Template(
TextInterpolation(PhpOpen),
ExpressionStatement(IncludeExpression(include, String)))
# Do-while statements
<?php
do {
echo $i;
$i--;
} while($i>0);
==>
Template(
TextInterpolation(PhpOpen),
DoStatement(do,
Block(
EchoStatement(echo,VariableName),
ExpressionStatement(UpdateExpression(VariableName, ArithOp))),
while, ParenthesizedExpression(BinaryExpression(VariableName, CompareOp, 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";
}
==>
Template(
TextInterpolation(PhpOpen),
TryStatement(
try, Block,
catch, CatchDeclarator(NamedType(Name)), Block,
catch, CatchDeclarator(UnionType(NamedType(Name), LogicOp, NamedType(Name)), VariableName), Block,
finally, Block),
TryStatement(
try, Block(ExpressionStatement(CallExpression(Name,ArgList))),
catch, CatchDeclarator(NamedType(Name), VariableName), Block(
ExpressionStatement(PrintIntrinsic(print, BinaryExpression(
String,
ConcatOp,
CallExpression(MemberExpression(VariableName,Name),ArgList)))),
ExpressionStatement(PrintIntrinsic(print, String(EscapeSequence))))))
# 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;
==>
Template(
TextInterpolation(PhpOpen),
ForeachStatement(foreach,
ForSpec(VariableName, as, SubscriptExpression(VariableName, Integer)),
Block(
EchoStatement(echo,BinaryExpression(
SubscriptExpression(VariableName, Integer),
ConcatOp,
String(EscapeSequence))))),
ForeachStatement(foreach,
ForSpec(VariableName, as, Pair(VariableName, VariableName)), EmptyStatement),
ForeachStatement(foreach,
ForSpec(VariableName, as, VariableName),
ColonBlock(
EchoStatement(echo,VariableName),
EchoStatement(echo,VariableName)),
endforeach))
# Case insensitive keywords
<?php
FOREACH ($a AS $b) {
DO {
if ($c) {
d();
} else {
e();
}
} while ($f);
}
==>
Template(
TextInterpolation(PhpOpen),
ForeachStatement(foreach,
ForSpec(VariableName, as, VariableName),
Block(
DoStatement(do,
Block(
IfStatement(if,
ParenthesizedExpression(VariableName),
Block(ExpressionStatement(CallExpression(Name, ArgList))),
else, Block(ExpressionStatement(CallExpression(Name, ArgList))))),
while, ParenthesizedExpression(VariableName)))))
# Accessing Constants
<?php
echo ANOTHER_CONST;
echo ANIMALS[1];
==>
Template(
TextInterpolation(PhpOpen),
EchoStatement(echo,
Name
),
EchoStatement(echo,
SubscriptExpression(
Name,
Integer
)
)
)
+411
View File
@@ -0,0 +1,411 @@
# Complex: Variable access
<?php
"{$test}";
==>
Template(
TextInterpolation(PhpOpen),
ExpressionStatement(
String(Interpolation(VariableName))
)
)
# Complex: Disallow space between { and $
<?php
"{ $test}";
==>
Template(
TextInterpolation(PhpOpen),
ExpressionStatement(
String(VariableName)
)
)
# 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";
==>
Template(
TextInterpolation(PhpOpen),
ExpressionStatement(
String(Interpolation(VariableName))
),
ExpressionStatement(
String(Interpolation(MemberExpression(VariableName, Name)))
),
LineComment,
ExpressionStatement(
String(Interpolation(SubscriptExpression(VariableName, String)))),
ExpressionStatement(
String(Interpolation(SubscriptExpression(SubscriptExpression(VariableName,Integer),Integer)))
),
LineComment,
LineComment,
ExpressionStatement(
String(Interpolation(SubscriptExpression(SubscriptExpression(VariableName,String),Integer)))
),
ExpressionStatement(
BinaryExpression(
String,
ConcatOp,
SubscriptExpression(SubscriptExpression(VariableName,String),Integer))
),
ExpressionStatement(
String(
Interpolation(MemberExpression(
SubscriptExpression(
MemberExpression(
VariableName,
Name
),
Integer
),
Name
))
)
),
ExpressionStatement(
String(
VariableName,
Interpolation(DynamicVariable(VariableName))
)
),
ExpressionStatement(
String(
Interpolation(DynamicVariable(CallExpression(Name,ArgList)))
)
),
ExpressionStatement(
String(
EscapeSequence,
Interpolation(DynamicVariable(
CallExpression(MemberExpression(VariableName, Name), ArgList)
))
)
),
LineComment,
ExpressionStatement(String),
ExpressionStatement(
String(
Interpolation(MemberExpression(VariableName,VariableName)),
EscapeSequence
)
),
ExpressionStatement(
String(
Interpolation(MemberExpression(
VariableName,
SubscriptExpression(
VariableName,
Integer
)
)),
EscapeSequence
)
),
ExpressionStatement(
String(
Interpolation(DynamicVariable(
ScopedExpression(
Name,
ClassMemberName(Name)
)
)),
EscapeSequence
)
),
ExpressionStatement(
String(
Interpolation(DynamicVariable(
ScopedExpression(
Name,
ClassMemberName(VariableName)
)
)),
EscapeSequence
)
)
)
# Simple: Variable access
<?php
"Hello $people, you're awesome!";
"hello ${a} world";
==>
Template(
TextInterpolation(PhpOpen),
ExpressionStatement(
String(VariableName)
),
ExpressionStatement(
String(Interpolation(Name))
)
)
# 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].";
==>
Template(
TextInterpolation(PhpOpen),
ExpressionStatement(
BinaryExpression(
String(
MemberExpression(VariableName,Name),
SubscriptExpression(VariableName,Integer),
),
ConcatOp,
Name
)
),
ExpressionStatement(
BinaryExpression(
String(
MemberExpression(VariableName,Name),
MemberExpression(VariableName,Name),
),
ConcatOp,
Name
)
),
ExpressionStatement(
String(
MemberExpression(VariableName,Name),
MemberExpression(VariableName,Name),
)
),
ExpressionStatement(
String(
SubscriptExpression(VariableName,UnaryExpression(ArithOp,Integer)),
)
)
)
# 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}";
"\{$";
"\u{$a}";
==>
Template(
TextInterpolation(PhpOpen),
ExpressionStatement(
String
),
ExpressionStatement(
String(EscapeSequence)
),
ExpressionStatement(
String
),
ExpressionStatement(
String
),
ExpressionStatement(
String
),
ExpressionStatement(
String
),
ExpressionStatement(
String
),
ExpressionStatement(
String
),
ExpressionStatement(
String
),
ExpressionStatement(
String
),
ExpressionStatement(
String(EscapeSequence)
),
ExpressionStatement(
String(EscapeSequence)
),
ExpressionStatement(
String
),
ExpressionStatement(
String(EscapeSequence)
),
ExpressionStatement(
String(EscapeSequence,EscapeSequence,EscapeSequence)
),
ExpressionStatement(
String(EscapeSequence,EscapeSequence,VariableName)
),
ExpressionStatement(
String(VariableName)
),
ExpressionStatement(
String(VariableName)
),
ExpressionStatement(
String(VariableName)
),
ExpressionStatement(
String(
Interpolation(CallExpression(ScopedExpression(VariableName,ClassMemberName(Name)), ArgList))
)
),
ExpressionStatement(
String(MemberExpression(VariableName,Name))
),
ExpressionStatement(
String(
Interpolation(SubscriptExpression(
MemberExpression(VariableName,Name),
BinaryExpression(
String,
ConcatOp,
String(Interpolation(CallExpression(MemberExpression(VariableName,Name),ArgList(String))))
)
))
)
),
ExpressionStatement(
String(EscapeSequence)
),
ExpressionStatement(
String(Interpolation(BinaryExpression(String,ConcatOp,String)))
),
ExpressionStatement(
String(VariableName)
),
ExpressionStatement(
String(
Interpolation(DynamicVariable(
DynamicVariable(
DynamicVariable(
DynamicVariable(
DynamicVariable(
DynamicVariable(
DynamicVariable(
VariableName
)
)
)
)
)
)
))
)
),
ExpressionStatement(
String(EscapeSequence)
),
ExpressionStatement(
String(Interpolation(VariableName))
)
)
# 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';
==>
Template(
TextInterpolation(PhpOpen),
ExpressionStatement(String),
ExpressionStatement(String),
ExpressionStatement(String),
ExpressionStatement(String),
ExpressionStatement(String),
ExpressionStatement(String),
ExpressionStatement(String)
)
+17
View File
@@ -0,0 +1,17 @@
import {parser} from "../dist/index.js"
import {fileTests} from "@lezer/generator/dist/test"
import * as fs from "fs"
import * as path from "path"
import {fileURLToPath} from "url"
let caseDir = path.dirname(fileURLToPath(import.meta.url))
for (let file of fs.readdirSync(caseDir)) {
if (!/\.txt$/.test(file)) continue
let name = /^[^\.]*/.exec(file)[0]
describe(name, () => {
for (let {name, run} of fileTests(fs.readFileSync(path.join(caseDir, file), "utf8"), file))
it(name, () => run(parser))
})
}
+124
View File
@@ -0,0 +1,124 @@
# Type names
<?php
function a(): A {}
function b(): A\B {}
==>
Template(
TextInterpolation(PhpOpen),
FunctionDefinition(function,
Name, ParamList,
NamedType(Name),
Block),
FunctionDefinition(function,
Name, ParamList,
NamedType(QualifiedName(NamespaceName, Name)),
Block))
# Primitive types
<?php
function a(): int {}
function b(): callable {}
function c(): iterable {}
==>
Template(
TextInterpolation(PhpOpen),
FunctionDefinition(function,
Name, ParamList,
NamedType(Name),
Block),
FunctionDefinition(function,
Name, ParamList,
NamedType(Name),
Block),
FunctionDefinition(function,
Name, ParamList,
NamedType(Name),
Block))
# Optional types
<?php
function a(): ?array {}
function b(): ?Something {}
==>
Template(
TextInterpolation(PhpOpen),
FunctionDefinition(function,
Name, ParamList,
OptionalType(LogicOp, NamedType(array)),
Block),
FunctionDefinition(function,
Name, ParamList,
OptionalType(LogicOp, NamedType(Name)),
Block))
# Union types
<?php
function a(int|string|null $var) : ?int|MyClass {}
==>
Template(
TextInterpolation(PhpOpen),
FunctionDefinition(function,
Name,
ParamList(
Parameter(
UnionType(NamedType(Name),LogicOp,NamedType(Name),LogicOp,NamedType(null)),
VariableName)),
UnionType(OptionalType(LogicOp, NamedType(Name)),LogicOp,NamedType(Name)),
Block))
# Mixed type
<?php
function a(mixed|string $var) : mixed {
}
==>
Template(
TextInterpolation(PhpOpen),
FunctionDefinition(function,
Name,
ParamList(
Parameter(
UnionType(NamedType(Name),LogicOp,NamedType(Name)),
VariableName
)
),
NamedType(Name),
Block
)
)
# Static type
<?php
function a(string $var) : static {
}
==>
Template(
TextInterpolation(PhpOpen),
FunctionDefinition(function,
Name,
ParamList(
Parameter(NamedType(Name),VariableName)),
NamedType(Name),
Block))