php-parser/scanner/lexer.go

297 lines
5.6 KiB
Go
Raw Normal View History

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
"github.com/z7zmey/php-parser/errors"
"github.com/z7zmey/php-parser/freefloating"
2018-04-15 18:39:26 +00:00
"github.com/z7zmey/php-parser/position"
2018-01-24 16:42:23 +00:00
)
2019-03-10 21:37:01 +00:00
type Scanner interface {
Lex(lval Lval) int
ReturnTokenToPool(t *Token)
GetPhpDocComment() string
SetPhpDocComment(string)
GetErrors() []*errors.Error
GetWithFreeFloating() bool
SetWithFreeFloating(bool)
AddError(e *errors.Error)
SetErrors(e []*errors.Error)
}
2018-01-24 16:42:23 +00:00
// Lval parsers yySymType must implement this interface
2018-01-24 16:42:23 +00:00
type Lval interface {
Token(tkn *Token)
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
2019-02-25 14:52:47 +00:00
TokenPool *TokenPool
2019-03-10 21:37:01 +00:00
FreeFloating []freefloating.String
2019-02-25 14:52:47 +00:00
WithFreeFloating bool
2019-03-10 21:37:01 +00:00
PhpDocComment string
2019-02-25 14:52:47 +00:00
lastToken *Token
Errors []*errors.Error
2019-03-10 21:37:01 +00:00
NewLines NewLines
2019-12-26 13:41:06 +00:00
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) GetPhpDocComment() string {
return l.PhpDocComment
}
2019-03-10 21:37:01 +00:00
func (l *Lexer) SetPhpDocComment(s string) {
l.PhpDocComment = s
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
}
func (l *Lexer) GetWithFreeFloating() bool {
return l.WithFreeFloating
}
func (l *Lexer) SetWithFreeFloating(b bool) {
l.WithFreeFloating = b
}
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) {
2019-03-10 21:37:01 +00:00
token.StartLine = lex.NewLines.GetLine(lex.ts)
token.EndLine = lex.NewLines.GetLine(lex.te - 1)
token.StartPos = lex.ts
token.EndPos = lex.te
}
func (lex *Lexer) addFreeFloating(t freefloating.StringType, ps, pe int) {
if !lex.WithFreeFloating {
return
}
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,
)
2019-03-10 21:37:01 +00:00
lex.FreeFloating = append(lex.FreeFloating, freefloating.String{
StringType: t,
Value: string(lex.data[ps:pe]),
Position: pos,
})
}
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 13:41:06 +00:00
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 {
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
}
2019-03-10 21:37:01 +00:00
return lex.data[lex.p] != '?' || lex.data[lex.p+1] != '>'
}
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()
2019-03-10 21:37:01 +00:00
lex.stack[lex.top] = state
lex.top++
2019-03-10 21:37:01 +00:00
lex.p++
lex.cs = fnext
}
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++
}
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))
}
2019-03-10 21:37:01 +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)
}
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
2018-01-24 16:42:23 +00:00
}