2018-01-24 16:42:23 +00:00
|
|
|
package scanner
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2019-03-10 21:37:01 +00:00
|
|
|
"strings"
|
2018-01-24 16:42:23 +00:00
|
|
|
|
2020-05-12 21:16:36 +00:00
|
|
|
"github.com/z7zmey/php-parser/internal/version"
|
|
|
|
"github.com/z7zmey/php-parser/pkg/errors"
|
|
|
|
"github.com/z7zmey/php-parser/pkg/position"
|
|
|
|
"github.com/z7zmey/php-parser/pkg/token"
|
2018-01-24 16:42:23 +00:00
|
|
|
)
|
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
type Scanner interface {
|
2020-05-17 22:01:35 +00:00
|
|
|
Lex() *Token
|
2019-03-10 21:37:01 +00:00
|
|
|
ReturnTokenToPool(t *Token)
|
|
|
|
GetErrors() []*errors.Error
|
2020-05-17 21:04:04 +00:00
|
|
|
GetWithHiddenTokens() bool
|
|
|
|
SetWithHiddenTokens(bool)
|
2019-03-10 21:37:01 +00:00
|
|
|
AddError(e *errors.Error)
|
|
|
|
SetErrors(e []*errors.Error)
|
|
|
|
}
|
2018-01-24 16:42:23 +00:00
|
|
|
|
|
|
|
type Lexer struct {
|
2019-03-10 21:37:01 +00:00
|
|
|
data []byte
|
|
|
|
p, pe, cs int
|
|
|
|
ts, te, act int
|
|
|
|
stack []int
|
|
|
|
top int
|
|
|
|
heredocLabel []byte
|
|
|
|
|
2020-05-17 21:04:04 +00:00
|
|
|
TokenPool *TokenPool
|
|
|
|
HiddenTokens []token.Token
|
|
|
|
WithHiddenTokens bool
|
|
|
|
Errors []*errors.Error
|
|
|
|
NewLines NewLines
|
|
|
|
PHPVersion string
|
2018-01-24 16:42:23 +00:00
|
|
|
}
|
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
func (l *Lexer) ReturnTokenToPool(t *Token) {
|
|
|
|
l.TokenPool.Put(t)
|
2018-01-24 16:42:23 +00:00
|
|
|
}
|
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
func (l *Lexer) GetErrors() []*errors.Error {
|
|
|
|
return l.Errors
|
|
|
|
}
|
|
|
|
|
2020-05-17 21:04:04 +00:00
|
|
|
func (l *Lexer) GetWithHiddenTokens() bool {
|
|
|
|
return l.WithHiddenTokens
|
2019-03-10 21:37:01 +00:00
|
|
|
}
|
|
|
|
|
2020-05-17 21:04:04 +00:00
|
|
|
func (l *Lexer) SetWithHiddenTokens(b bool) {
|
|
|
|
l.WithHiddenTokens = b
|
2019-03-10 21:37:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (l *Lexer) AddError(e *errors.Error) {
|
|
|
|
l.Errors = append(l.Errors, e)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *Lexer) SetErrors(e []*errors.Error) {
|
|
|
|
l.Errors = e
|
|
|
|
}
|
|
|
|
|
2019-12-21 12:44:25 +00:00
|
|
|
func (lex *Lexer) setTokenPosition(token *Token) {
|
2020-05-17 20:50:23 +00:00
|
|
|
token.Position.StartLine = lex.NewLines.GetLine(lex.ts)
|
|
|
|
token.Position.EndLine = lex.NewLines.GetLine(lex.te - 1)
|
|
|
|
token.Position.StartPos = lex.ts
|
|
|
|
token.Position.EndPos = lex.te
|
2019-03-10 21:37:01 +00:00
|
|
|
}
|
|
|
|
|
2020-05-12 21:16:36 +00:00
|
|
|
func (lex *Lexer) addToken(id TokenID, ps, pe int) {
|
2020-05-17 21:04:04 +00:00
|
|
|
if !lex.WithHiddenTokens {
|
2019-03-10 21:37:01 +00:00
|
|
|
return
|
|
|
|
}
|
2018-11-05 14:56:27 +00:00
|
|
|
|
2020-05-17 21:04:04 +00:00
|
|
|
lex.HiddenTokens = append(lex.HiddenTokens, token.Token{
|
2020-05-12 21:16:36 +00:00
|
|
|
ID: token.ID(id),
|
|
|
|
Value: lex.data[ps:pe],
|
2019-03-10 21:37:01 +00:00
|
|
|
})
|
2018-11-05 14:56:27 +00:00
|
|
|
}
|
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
func (lex *Lexer) isNotStringVar() bool {
|
|
|
|
p := lex.p
|
|
|
|
if lex.data[p-1] == '\\' && lex.data[p-2] != '\\' {
|
|
|
|
return true
|
|
|
|
}
|
2018-01-24 16:42:23 +00:00
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
if len(lex.data) < p+1 {
|
|
|
|
return true
|
|
|
|
}
|
2018-01-24 16:42:23 +00:00
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
if lex.data[p] == '$' && (lex.data[p+1] == '{' || isValidVarNameStart(lex.data[p+1])) {
|
|
|
|
return false
|
2018-01-24 16:42:23 +00:00
|
|
|
}
|
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
if lex.data[p] == '{' && lex.data[p+1] == '$' {
|
|
|
|
return false
|
|
|
|
}
|
2018-01-24 16:42:23 +00:00
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
return true
|
2018-01-24 16:42:23 +00:00
|
|
|
}
|
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
func (lex *Lexer) isNotStringEnd(s byte) bool {
|
|
|
|
p := lex.p
|
|
|
|
if lex.data[p-1] == '\\' && lex.data[p-2] != '\\' {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
return !(lex.data[p] == s)
|
2018-01-24 16:42:23 +00:00
|
|
|
}
|
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
func (lex *Lexer) isHeredocEnd(p int) bool {
|
2019-12-26 15:57:56 +00:00
|
|
|
r, err := version.Compare(lex.PHPVersion, "7.3")
|
|
|
|
if err != nil {
|
2019-12-26 13:41:06 +00:00
|
|
|
return lex.isHeredocEndSince73(p)
|
|
|
|
}
|
|
|
|
|
2019-12-26 15:57:56 +00:00
|
|
|
if r == -1 {
|
2019-12-26 13:41:06 +00:00
|
|
|
return lex.isHeredocEndBefore73(p)
|
|
|
|
}
|
|
|
|
|
|
|
|
return lex.isHeredocEndSince73(p)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (lex *Lexer) isHeredocEndBefore73(p int) bool {
|
2019-03-10 21:37:01 +00:00
|
|
|
if lex.data[p-1] != '\r' && lex.data[p-1] != '\n' {
|
|
|
|
return false
|
2018-01-24 16:42:23 +00:00
|
|
|
}
|
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
l := len(lex.heredocLabel)
|
|
|
|
if len(lex.data) < p+l {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(lex.data) > p+l && lex.data[p+l] != ';' && lex.data[p+l] != '\r' && lex.data[p+l] != '\n' {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(lex.data) > p+l+1 && lex.data[p+l] == ';' && lex.data[p+l+1] != '\r' && lex.data[p+l+1] != '\n' {
|
|
|
|
return false
|
|
|
|
}
|
2018-01-24 16:42:23 +00:00
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
return bytes.Equal(lex.heredocLabel, lex.data[p:p+l])
|
2018-01-24 16:42:23 +00:00
|
|
|
}
|
|
|
|
|
2019-12-26 13:41:06 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
func (lex *Lexer) isNotHeredocEnd(p int) bool {
|
|
|
|
return !lex.isHeredocEnd(p)
|
2018-01-24 16:42:23 +00:00
|
|
|
}
|
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
func (lex *Lexer) growCallStack() {
|
|
|
|
if lex.top == len(lex.stack) {
|
|
|
|
lex.stack = append(lex.stack, 0)
|
|
|
|
}
|
|
|
|
}
|
2018-01-24 16:42:23 +00:00
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
func (lex *Lexer) isNotPhpCloseToken() bool {
|
|
|
|
if lex.p+1 == len(lex.data) {
|
|
|
|
return true
|
|
|
|
}
|
2018-06-10 23:41:12 +00:00
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
return lex.data[lex.p] != '?' || lex.data[lex.p+1] != '>'
|
|
|
|
}
|
2018-07-14 15:00:48 +00:00
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
func (lex *Lexer) isNotNewLine() bool {
|
|
|
|
if lex.data[lex.p] == '\n' && lex.data[lex.p-1] == '\r' {
|
|
|
|
return true
|
|
|
|
}
|
2018-06-24 07:19:44 +00:00
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
return lex.data[lex.p-1] != '\n' && lex.data[lex.p-1] != '\r'
|
2018-01-24 16:42:23 +00:00
|
|
|
}
|
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
func (lex *Lexer) call(state int, fnext int) {
|
|
|
|
lex.growCallStack()
|
2018-06-29 21:51:11 +00:00
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
lex.stack[lex.top] = state
|
|
|
|
lex.top++
|
2018-06-29 21:51:11 +00:00
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
lex.p++
|
|
|
|
lex.cs = fnext
|
2018-06-29 21:51:11 +00:00
|
|
|
}
|
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
func (lex *Lexer) ret(n int) {
|
|
|
|
lex.top = lex.top - n
|
|
|
|
if lex.top < 0 {
|
|
|
|
lex.top = 0
|
|
|
|
}
|
|
|
|
lex.cs = lex.stack[lex.top]
|
|
|
|
lex.p++
|
|
|
|
}
|
2018-07-29 08:44:38 +00:00
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
func (lex *Lexer) ungetStr(s string) {
|
|
|
|
tokenStr := string(lex.data[lex.ts:lex.te])
|
|
|
|
if strings.HasSuffix(tokenStr, s) {
|
|
|
|
lex.ungetCnt(len(s))
|
2018-06-29 21:51:11 +00:00
|
|
|
}
|
2019-03-10 21:37:01 +00:00
|
|
|
}
|
2018-06-29 21:51:11 +00:00
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
func (lex *Lexer) ungetCnt(n int) {
|
|
|
|
lex.p = lex.p - n
|
|
|
|
lex.te = lex.te - n
|
|
|
|
}
|
2018-04-15 18:39:26 +00:00
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
func (lex *Lexer) Error(msg string) {
|
2018-04-15 18:39:26 +00:00
|
|
|
pos := position.NewPosition(
|
2019-03-10 21:37:01 +00:00
|
|
|
lex.NewLines.GetLine(lex.ts),
|
|
|
|
lex.NewLines.GetLine(lex.te-1),
|
|
|
|
lex.ts,
|
|
|
|
lex.te,
|
2018-04-15 18:39:26 +00:00
|
|
|
)
|
|
|
|
|
2019-03-10 21:37:01 +00:00
|
|
|
lex.Errors = append(lex.Errors, errors.NewError(msg, pos))
|
|
|
|
}
|
|
|
|
|
|
|
|
func isValidVarNameStart(r byte) bool {
|
2019-12-26 13:41:06 +00:00
|
|
|
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)
|
|
|
|
}
|