[#82] Lexer: handle PHP 7.3 heredoc
This commit is contained in:
parent
777873afae
commit
6afa2a089b
@ -41,6 +41,7 @@ type Lexer struct {
|
|||||||
lastToken *Token
|
lastToken *Token
|
||||||
Errors []*errors.Error
|
Errors []*errors.Error
|
||||||
NewLines NewLines
|
NewLines NewLines
|
||||||
|
PHPVersion string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Lexer) ReturnTokenToPool(t *Token) {
|
func (l *Lexer) ReturnTokenToPool(t *Token) {
|
||||||
@ -132,6 +133,18 @@ func (lex *Lexer) isNotStringEnd(s byte) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (lex *Lexer) isHeredocEnd(p int) bool {
|
func (lex *Lexer) isHeredocEnd(p int) bool {
|
||||||
|
if lex.PHPVersion == "" {
|
||||||
|
return lex.isHeredocEndSince73(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
if comparePHPVersion(lex.PHPVersion, "7.3") == -1 {
|
||||||
|
return lex.isHeredocEndBefore73(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return lex.isHeredocEndSince73(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lex *Lexer) isHeredocEndBefore73(p int) bool {
|
||||||
if lex.data[p-1] != '\r' && lex.data[p-1] != '\n' {
|
if lex.data[p-1] != '\r' && lex.data[p-1] != '\n' {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -152,6 +165,37 @@ func (lex *Lexer) isHeredocEnd(p int) bool {
|
|||||||
return bytes.Equal(lex.heredocLabel, lex.data[p:p+l])
|
return bytes.Equal(lex.heredocLabel, lex.data[p:p+l])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (lex *Lexer) isHeredocEndSince73(p int) bool {
|
||||||
|
if lex.data[p-1] != '\r' && lex.data[p-1] != '\n' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for lex.data[p] == ' ' || lex.data[p] == '\t' {
|
||||||
|
p++
|
||||||
|
}
|
||||||
|
|
||||||
|
l := len(lex.heredocLabel)
|
||||||
|
if len(lex.data) < p+l {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(lex.data) > p+l && isValidVarName(lex.data[p+l]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
a := string(lex.heredocLabel)
|
||||||
|
b := string(lex.data[p : p+l])
|
||||||
|
|
||||||
|
_, _ = a, b
|
||||||
|
|
||||||
|
if bytes.Equal(lex.heredocLabel, lex.data[p:p+l]) {
|
||||||
|
lex.p = p
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (lex *Lexer) isNotHeredocEnd(p int) bool {
|
func (lex *Lexer) isNotHeredocEnd(p int) bool {
|
||||||
return !lex.isHeredocEnd(p)
|
return !lex.isHeredocEnd(p)
|
||||||
}
|
}
|
||||||
@ -221,5 +265,32 @@ func (lex *Lexer) Error(msg string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isValidVarNameStart(r byte) bool {
|
func isValidVarNameStart(r byte) bool {
|
||||||
return r >= 'A' && r <= 'Z' || r == '_' || r >= 'a' && r <= 'z' || r >= '\u007f' && r <= 'ÿ'
|
return (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || r == '_' || (r >= 0x80 && r <= 0xff)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidVarName(r byte) bool {
|
||||||
|
return (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || r == '_' || (r >= 0x80 && r <= 0xff)
|
||||||
|
}
|
||||||
|
|
||||||
|
func comparePHPVersion(a string, b string) int {
|
||||||
|
first := strings.Split(a, ".")
|
||||||
|
second := strings.Split(b, ".")
|
||||||
|
|
||||||
|
if first[0] < second[0] {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if first[0] > second[0] {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if first[1] < second[1] {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if first[1] > second[1] {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -17157,7 +17157,7 @@ func (lex *Lexer) Lex(lval Lval) int {
|
|||||||
lex.setTokenPosition(token)
|
lex.setTokenPosition(token)
|
||||||
tok = T_ENCAPSED_AND_WHITESPACE
|
tok = T_ENCAPSED_AND_WHITESPACE
|
||||||
|
|
||||||
if lex.data[lex.p+1] != '$' && lex.data[lex.p+1] != '{' {
|
if len(lex.data) > lex.p+1 && lex.data[lex.p+1] != '$' && lex.data[lex.p+1] != '{' {
|
||||||
lex.cs = 486
|
lex.cs = 486
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@ -17179,7 +17179,7 @@ func (lex *Lexer) Lex(lval Lval) int {
|
|||||||
lex.setTokenPosition(token)
|
lex.setTokenPosition(token)
|
||||||
tok = T_ENCAPSED_AND_WHITESPACE
|
tok = T_ENCAPSED_AND_WHITESPACE
|
||||||
|
|
||||||
if lex.data[lex.p+1] != '$' && lex.data[lex.p+1] != '{' {
|
if len(lex.data) > lex.p+1 && lex.data[lex.p+1] != '$' && lex.data[lex.p+1] != '{' {
|
||||||
lex.cs = 486
|
lex.cs = 486
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@ -17197,7 +17197,7 @@ func (lex *Lexer) Lex(lval Lval) int {
|
|||||||
lex.setTokenPosition(token)
|
lex.setTokenPosition(token)
|
||||||
tok = T_ENCAPSED_AND_WHITESPACE
|
tok = T_ENCAPSED_AND_WHITESPACE
|
||||||
|
|
||||||
if lex.data[lex.p+1] != '$' && lex.data[lex.p+1] != '{' {
|
if len(lex.data) > lex.p+1 && lex.data[lex.p+1] != '$' && lex.data[lex.p+1] != '{' {
|
||||||
lex.cs = 486
|
lex.cs = 486
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
@ -394,7 +394,7 @@ func (lex *Lexer) Lex(lval Lval) int {
|
|||||||
lex.setTokenPosition(token);
|
lex.setTokenPosition(token);
|
||||||
tok = T_ENCAPSED_AND_WHITESPACE;
|
tok = T_ENCAPSED_AND_WHITESPACE;
|
||||||
|
|
||||||
if lex.data[lex.p+1] != '$' && lex.data[lex.p+1] != '{' {
|
if len(lex.data) > lex.p+1 && lex.data[lex.p+1] != '$' && lex.data[lex.p+1] != '{' {
|
||||||
fnext heredoc_end;
|
fnext heredoc_end;
|
||||||
}
|
}
|
||||||
fbreak;
|
fbreak;
|
||||||
|
@ -892,6 +892,70 @@ CAT;
|
|||||||
assert.DeepEqual(t, expected, actual)
|
assert.DeepEqual(t, expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHereDocTokens73(t *testing.T) {
|
||||||
|
src := `<?php
|
||||||
|
<<<"CAT"
|
||||||
|
text
|
||||||
|
CAT, $b`
|
||||||
|
|
||||||
|
expected := []string{
|
||||||
|
|
||||||
|
T_START_HEREDOC.String(),
|
||||||
|
T_ENCAPSED_AND_WHITESPACE.String(),
|
||||||
|
T_END_HEREDOC.String(),
|
||||||
|
TokenID(int(',')).String(),
|
||||||
|
T_VARIABLE.String(),
|
||||||
|
}
|
||||||
|
|
||||||
|
lexer := NewLexer([]byte(src))
|
||||||
|
lexer.WithFreeFloating = true
|
||||||
|
lv := &lval{}
|
||||||
|
actual := []string{}
|
||||||
|
|
||||||
|
for {
|
||||||
|
token := lexer.Lex(lv)
|
||||||
|
if token == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
actual = append(actual, TokenID(token).String())
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.DeepEqual(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHereDocTokensBefore73(t *testing.T) {
|
||||||
|
src := `<?php
|
||||||
|
<<<"CAT"
|
||||||
|
CAT
|
||||||
|
CAT;`
|
||||||
|
|
||||||
|
expected := []string{
|
||||||
|
|
||||||
|
T_START_HEREDOC.String(),
|
||||||
|
T_ENCAPSED_AND_WHITESPACE.String(),
|
||||||
|
T_END_HEREDOC.String(),
|
||||||
|
TokenID(int(';')).String(),
|
||||||
|
}
|
||||||
|
|
||||||
|
lexer := NewLexer([]byte(src))
|
||||||
|
lexer.PHPVersion = "7.2"
|
||||||
|
lexer.WithFreeFloating = true
|
||||||
|
lv := &lval{}
|
||||||
|
actual := []string{}
|
||||||
|
|
||||||
|
for {
|
||||||
|
token := lexer.Lex(lv)
|
||||||
|
if token == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
actual = append(actual, TokenID(token).String())
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.DeepEqual(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
func TestInlineHtmlNopTokens(t *testing.T) {
|
func TestInlineHtmlNopTokens(t *testing.T) {
|
||||||
src := `<?php
|
src := `<?php
|
||||||
$a; ?> test <?php
|
$a; ?> test <?php
|
||||||
|
Loading…
Reference in New Issue
Block a user