Skip to content

Commit

Permalink
fix: fix nested function calls and mix the array index may cause error
Browse files Browse the repository at this point in the history
  • Loading branch information
fefit committed Nov 18, 2021
1 parent c6068d3 commit 36fef36
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 23 deletions.
1 change: 1 addition & 0 deletions fet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func TestCompile(t *testing.T) {
assertOutputToBe(t, "variable.tpl", nil, helloFet)
assertOutputToBe(t, "strvar.tpl", nil, helloFet+helloFet)
assertOutputToBe(t, "keywordvar.tpl", nil, helloFet)
assertOutputToBe(t, "concat.tpl", nil, helloFet)
// rewrite foreach
assertOutputToBe(t, "foreach.tpl", map[string][]string{
"Result": helloFetChars,
Expand Down
70 changes: 48 additions & 22 deletions lib/expression/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,16 +182,17 @@ func getNoSpaceTokens(tokens []AnyToken, num int) (isPrevSpace bool, result []An

// TokenStat struct
type TokenStat struct {
StartIndex int
Index int
ParseIndex int
RBLevel int
RBSubLevel int
SBLevel int
SBSubLevel int
Logics Flags
Context *Runes
Values Runes
StartIndex int
Index int
ParseIndex int
RBLevel int
RBSubLevel int
SBLevel int
SBSubLevel int
Logics Flags
Context *Runes
Values Runes
LeftBracket *LeftBracketToken
}

// Token struct
Expand Down Expand Up @@ -271,14 +272,20 @@ func (bracket *LeftBracketToken) Validate(tokens []AnyToken) (retryToken AnyToke
if prev == nil {
return
}
switch prev.(type) {
switch prev := prev.(type) {
case *OperatorToken, *LeftBracketToken:
case *IdentifierToken, *RightSquareBracketToken:
if hasSpace {
return nil, fmt.Errorf("wrong space between function name and (")
}
stat := bracket.Stat
stat.Logics = Flags{
bracket.Stat.Logics = Flags{
"IsFunc": true,
}
case *RightBracketToken:
if prev.Stat.LeftBracket == nil || !prev.Stat.LeftBracket.Stat.Logics["IsFunc"] {
return nil, fmt.Errorf("the previous bracket ')' before the left bracket '(' must a function call")
}
bracket.Stat.Logics = Flags{
"IsFunc": true,
}
default:
Expand All @@ -303,7 +310,7 @@ func (bracket *RightBracketToken) Validate(tokens []AnyToken) (retryToken AnyTok
if stat.RBLevel < 1 {
return nil, fmt.Errorf("wrong right round bracket without start:%d", stat.StartIndex)
}
prevs := getPrevTokens(tokens, 1)
_, prevs := getNoSpaceTokens(tokens, 1)
p0 := prevs[0]
if p0, ok := p0.(*LeftBracketToken); ok && !p0.Stat.Logics["IsFunc"] {
return nil, fmt.Errorf("empty expression")
Expand Down Expand Up @@ -439,7 +446,7 @@ func (identifier *IdentifierToken) Validate(tokens []AnyToken) (retryToken AnyTo
switch prev.(type) {
case *OperatorToken, *LeftBracketToken, *LeftSquareBracketToken:
default:
return nil, fmt.Errorf("wrong idenfier token")
return nil, fmt.Errorf("wrong identifier token")
}
return
}
Expand Down Expand Up @@ -868,6 +875,7 @@ func (node *TokenNode) Add(s rune) (ok bool, isComplete bool, retry bool, err er
return
}

// set level by check the map
func setLevel(list map[int]int, level int) (subLevel int) {
if value, exists := list[level]; exists {
list[level] = value + 1
Expand All @@ -877,18 +885,33 @@ func setLevel(list map[int]int, level int) (subLevel int) {
return list[level]
}

func popLeftBracket(leftBrackets *[]*LeftBracketToken) *LeftBracketToken {
len := len(*leftBrackets)
if len > 0 {
last := (*leftBrackets)[len-1]
*leftBrackets = (*leftBrackets)[:len-1]
return last
}
return nil
}

// Parser struct
type Parser struct {
Current AnyToken
Tokens []AnyToken
RBLevel int
RBSubLevel int
SBLevel int
Current AnyToken
Tokens []AnyToken
// round bracket level
RBLevel int
// nested round bracket level
RBSubLevel int
// square bracket level
SBLevel int
// nested square bracket level
SBSubLevel int
BLList map[int]int
SLList map[int]int
CurBrSqLevel [2]int
CurSqBrLevel [2]int
LeftBrackets []*LeftBracketToken
Asserts map[int]AnyToken
TokenIndex int
Context Runes
Expand Down Expand Up @@ -929,6 +952,7 @@ func (parser *Parser) Reuse() {
parser.SLList = map[int]int{}
parser.CurBrSqLevel = [2]int{}
parser.CurSqBrLevel = [2]int{}
parser.LeftBrackets = []*LeftBracketToken{}
parser.TokenIndex = -1
parser.IgnoreIndex = -1
parser.TokenStat = &TokenStat{}
Expand Down Expand Up @@ -1027,7 +1051,7 @@ func (parser *Parser) Add(s rune) error {
}
}
if current == nil {
return fmt.Errorf("can not find any token match ->" + string(s))
return fmt.Errorf("syntax error: no token match the character '%s'", string(s))
}
} else {
ok, isComplete, retry, err = current.Add(s)
Expand Down Expand Up @@ -1058,19 +1082,21 @@ func (parser *Parser) Add(s rune) error {
rbsl := parser.RBSubLevel
sbsl := parser.SBSubLevel
tokens := parser.Tokens
switch current.(type) {
switch current := current.(type) {
case *LeftBracketToken:
parser.RBSubLevel = setLevel(blList, rbl)
currentStat.RBLevel = rbl + 1
currentStat.RBSubLevel = parser.RBSubLevel
parser.CurBrSqLevel = [2]int{sbl, sbsl}
parser.LeftBrackets = append(parser.LeftBrackets, current)
parser.RBLevel++
case *RightBracketToken:
levels := parser.CurBrSqLevel
// e.g [(][)]
if levels[0] != sbl {
return fmt.Errorf("wrong matched right bracket")
}
current.Stat.LeftBracket = popLeftBracket(&parser.LeftBrackets)
currentStat.RBLevel = rbl
parser.RBLevel--
case *LeftSquareBracketToken:
Expand Down
20 changes: 19 additions & 1 deletion lib/expression/expression_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,21 @@ func TestTokenize(t *testing.T) {
assertTokenList(t, "_1", "IdentifierToken")
assertTokenList(t, "ID", "IdentifierToken")
assertTokenList(t, "i_", "IdentifierToken")
// brackets
// round brackets
assertTokenList(t, "(e)", "LeftBracketToken", "IdentifierToken", "RightBracketToken")
assertTokenList(t, "((e)+e)", "LeftBracketToken", "LeftBracketToken", "IdentifierToken", "RightBracketToken", "OperatorToken", "IdentifierToken", "RightBracketToken")
assertTokenList(t, "a+(e)", "IdentifierToken", "OperatorToken", "LeftBracketToken", "IdentifierToken", "RightBracketToken")
assertTokenList(t, "call(e)", "IdentifierToken", "LeftBracketToken", "IdentifierToken", "RightBracketToken")
assertTokenList(t, "call(e)(f)", "IdentifierToken", "LeftBracketToken", "IdentifierToken", "RightBracketToken", "LeftBracketToken", "IdentifierToken", "RightBracketToken")
assertTokenList(t, "call(e)(f)(g)", "IdentifierToken", "LeftBracketToken", "IdentifierToken", "RightBracketToken", "LeftBracketToken", "IdentifierToken", "RightBracketToken", "LeftBracketToken", "IdentifierToken", "RightBracketToken")
// square brackets
assertTokenList(t, "a[0]", "IdentifierToken", "LeftSquareBracketToken", "NumberToken", "RightSquareBracketToken")
assertTokenList(t, "a[0 ]", "IdentifierToken", "LeftSquareBracketToken", "NumberToken", "SpaceToken", "RightSquareBracketToken")
assertTokenList(t, "a[ 0]", "IdentifierToken", "LeftSquareBracketToken", "SpaceToken", "NumberToken", "RightSquareBracketToken")
assertTokenList(t, "a[ 0 ]", "IdentifierToken", "LeftSquareBracketToken", "SpaceToken", "NumberToken", "SpaceToken", "RightSquareBracketToken")
// mix brackets
assertTokenList(t, "a[0](e)", "IdentifierToken", "LeftSquareBracketToken", "NumberToken", "RightSquareBracketToken", "LeftBracketToken", "IdentifierToken", "RightBracketToken")
assertTokenList(t, "a[0](e)[1]", "IdentifierToken", "LeftSquareBracketToken", "NumberToken", "RightSquareBracketToken", "LeftBracketToken", "IdentifierToken", "RightBracketToken", "LeftSquareBracketToken", "NumberToken", "RightSquareBracketToken")
// operators
assertTokenList(t, "1+1", "NumberToken", "OperatorToken", "NumberToken")
assertTokenList(t, "1-1", "NumberToken", "OperatorToken", "NumberToken")
Expand Down Expand Up @@ -90,6 +102,12 @@ func TestTokenize(t *testing.T) {
assertErrorTokenize(t, `1 >= "a"`)
assertErrorTokenize(t, `1bitor 2`)
assertErrorTokenize(t, `1 bitor2`)
// wrong brackets
assertErrorTokenize(t, "()")
assertErrorTokenize(t, "( )")
assertErrorTokenize(t, "a (e)")
assertErrorTokenize(t, "(e)(f)")
assertErrorTokenize(t, "a [0]")
})
// complex tokens
t.Run("Test multiple tokens", func(t *testing.T) {
Expand Down
2 changes: 2 additions & 0 deletions tests/smarty/templates/concat.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{%$hello = "hello"%}
{%concat($hello, " fet","!")%}

0 comments on commit 36fef36

Please sign in to comment.