Skip to content

Commit 61004ba

Browse files
committed
Add function to match expressions in tokens
1 parent a5d8d04 commit 61004ba

6 files changed

Lines changed: 184 additions & 36 deletions

File tree

expression.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package stringutils
2+
3+
// Expression represents tokens to be matched in a list of tokens
4+
// they might have a specific word or not, in case the word is empty, any token of a given Type will be matched
5+
type Expression struct {
6+
Type string // verb, keyword, separator, etc
7+
Text string // words to be matched with a token (empty for any)
8+
}
9+
10+
// FindExpressionsInTokens checks if a given Expression is present in a list of tokens
11+
func FindExpressionsInTokens(tokens []Token, expressions []Expression) bool {
12+
if len(expressions) > len(tokens) {
13+
return false
14+
}
15+
if len(expressions) > 1 {
16+
return FindExpressionsInTokens(tokens[1:], expressions[1:])
17+
} else if len(expressions) == 1 {
18+
if len(tokens) > 1 {
19+
return FindExpressionInTokens(tokens[1:], expressions[0])
20+
} else {
21+
return FindExpressionInTokens(tokens, expressions[0])
22+
}
23+
}
24+
return false
25+
}
26+
27+
// FindExpressionInTokens checks if a given Expression is present in a list of tokens
28+
func FindExpressionInTokens(tokens []Token, expression Expression) bool {
29+
for _, token := range tokens {
30+
if MatchToken(token, expression) {
31+
return true
32+
}
33+
}
34+
return false
35+
}
36+
37+
// MatchToken checks if a given Expression matches a given Token
38+
func MatchToken(token Token, expression Expression) bool {
39+
if len(expression.Text) == 0 {
40+
return EqualsIgnoreCase(token.Type, expression.Type)
41+
} else {
42+
return EqualsIgnoreCase(token.Type, expression.Type) && EqualsIgnoreCase(token.Text, expression.Text)
43+
}
44+
}

expression_test.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package stringutils
2+
3+
import (
4+
"github.com/stretchr/testify/assert"
5+
"testing"
6+
)
7+
8+
func TestFindExpressionInTokens(t *testing.T) {
9+
assertions := assert.New(t)
10+
11+
token := Token{
12+
Type: "keyword",
13+
Position: 1,
14+
Text: "if",
15+
}
16+
17+
expression := Expression{
18+
Type: "keyword",
19+
Text: "if",
20+
}
21+
assertions.True(FindExpressionInTokens([]Token{token}, expression))
22+
23+
token = Token{
24+
Type: "bracket",
25+
Position: 1,
26+
Text: "(",
27+
}
28+
29+
expression = Expression{
30+
Type: "keyword",
31+
Text: "if",
32+
}
33+
assertions.False(FindExpressionInTokens([]Token{token}, expression))
34+
}
35+
36+
func TestFindExpressionsInTokens(t *testing.T) {
37+
assertions := assert.New(t)
38+
39+
token := Token{
40+
Type: "keyword",
41+
Position: 1,
42+
Text: "if",
43+
}
44+
45+
expression := Expression{
46+
Type: "keyword",
47+
Text: "if",
48+
}
49+
assertions.True(FindExpressionsInTokens([]Token{token}, []Expression{expression}))
50+
51+
token2 := Token{
52+
Type: "bracket",
53+
Position: 2,
54+
Text: "(",
55+
}
56+
57+
expression2 := Expression{
58+
Type: "bracket",
59+
Text: "(",
60+
}
61+
assertions.True(FindExpressionsInTokens([]Token{token, token2}, []Expression{expression, expression2}))
62+
63+
expression2 = Expression{
64+
Type: "keyword",
65+
Text: "for",
66+
}
67+
assertions.False(FindExpressionsInTokens([]Token{token, token2}, []Expression{expression, expression2}))
68+
69+
token = Token{
70+
Type: "bracket",
71+
Position: 1,
72+
Text: "(",
73+
}
74+
75+
expression = Expression{
76+
Type: "keyword",
77+
Text: "if",
78+
}
79+
assertions.False(FindExpressionsInTokens([]Token{token}, []Expression{expression}))
80+
81+
token2 = Token{
82+
Type: "bracket",
83+
Position: 2,
84+
Text: ")",
85+
}
86+
87+
assertions.False(FindExpressionsInTokens([]Token{token, token2}, []Expression{expression}))
88+
89+
assertions.False(FindExpressionsInTokens([]Token{token}, []Expression{expression, expression2}))
90+
91+
assertions.False(FindExpressionsInTokens([]Token{token}, []Expression{}))
92+
}
93+
94+
func TestMatchToken(t *testing.T) {
95+
assertions := assert.New(t)
96+
97+
token := Token{
98+
Type: "keyword",
99+
Position: 1,
100+
Text: "if",
101+
}
102+
103+
expression := Expression{
104+
Type: "keyword",
105+
Text: "if",
106+
}
107+
assertions.True(MatchToken(token, expression))
108+
109+
expression = Expression{
110+
Type: "keyword",
111+
Text: "IF",
112+
}
113+
assertions.True(MatchToken(token, expression))
114+
115+
expression = Expression{
116+
Type: "keyword",
117+
Text: "",
118+
}
119+
assertions.True(MatchToken(token, expression))
120+
}

lexer.go

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,13 @@ func TokenizeWord(word string, position int, tokenTypes []TokenType) Token {
2727
// If no match is found, it returns a token type with "" as type
2828
func LookupType(word string, tokenTypes []TokenType) TokenType {
2929
for _, aToken := range tokenTypes {
30-
if aToken.CaseSensitive {
31-
if EqualsAny(word, aToken.Words) {
32-
return aToken
33-
}
34-
} else {
35-
if EqualsAnyIgnoreCase(word, aToken.Words) {
36-
return aToken
37-
}
30+
if EqualsAnyIgnoreCase(word, aToken.Words) {
31+
return aToken
3832
}
3933
}
34+
4035
return TokenType{
41-
Type: "",
42-
Words: []string{""},
43-
CaseSensitive: false,
36+
Type: "",
37+
Words: []string{""},
4438
}
4539
}

lexer_test.go

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@ func TestTokenizeCaseSensitive(t *testing.T) {
1010

1111
var tokenTypes []TokenType
1212
tokenTypes = append(tokenTypes, TokenType{
13-
Type: "keyword",
14-
Words: []string{"if", "for"},
15-
CaseSensitive: true,
13+
Type: "keyword",
14+
Words: []string{"if", "for"},
1615
})
1716
result := Tokenize("if", tokenTypes)
1817
assertions.Equal("if", result[0].Text)
@@ -25,9 +24,8 @@ func TestTokenizeCaseInsensitive(t *testing.T) {
2524

2625
var tokenTypes []TokenType
2726
tokenTypes = append(tokenTypes, TokenType{
28-
Type: "keyword",
29-
Words: []string{"if", "for"},
30-
CaseSensitive: false,
27+
Type: "keyword",
28+
Words: []string{"if", "for"},
3129
})
3230
result := Tokenize("IF", tokenTypes)
3331
assertions.Equal("IF", result[0].Text)
@@ -40,9 +38,8 @@ func TestTokenizeWord(t *testing.T) {
4038

4139
var tokenTypes []TokenType
4240
tokenTypes = append(tokenTypes, TokenType{
43-
Type: "keyword",
44-
Words: []string{"if", "for"},
45-
CaseSensitive: true,
41+
Type: "keyword",
42+
Words: []string{"if", "for"},
4643
})
4744
result := TokenizeWord("if", 0, tokenTypes)
4845
assertions.Equal("if", result.Text)
@@ -55,29 +52,25 @@ func TestLookupType(t *testing.T) {
5552

5653
var tokenTypes []TokenType
5754
tokenTypes = append(tokenTypes, TokenType{
58-
Type: "keyword",
59-
Words: []string{"if", "for"},
60-
CaseSensitive: false,
55+
Type: "keyword",
56+
Words: []string{"if", "for"},
6157
})
6258

6359
result := LookupType("for", tokenTypes)
6460
assertions.Equal("keyword", result.Type)
6561
assertions.Equal([]string{"if", "for"}, result.Words)
66-
assertions.Equal(false, result.CaseSensitive)
6762
}
6863

6964
func TestLookupTypeNotFound(t *testing.T) {
7065
assertions := assert.New(t)
7166

7267
var tokenTypes []TokenType
7368
tokenTypes = append(tokenTypes, TokenType{
74-
Type: "keyword",
75-
Words: []string{"if", "for"},
76-
CaseSensitive: false,
69+
Type: "keyword",
70+
Words: []string{"if", "for"},
7771
})
7872

7973
result := LookupType("var", tokenTypes)
8074
assertions.Equal("", result.Type)
8175
assertions.Equal([]string{""}, result.Words)
82-
assertions.Equal(false, result.CaseSensitive)
8376
}

token.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@ type Token struct {
77
Text string // text
88
}
99

10-
//TokenType represents a category of words, with a collection of tokens and whether or not they should be matched as
10+
// TokenType represents a category of words, with a collection of tokens and whether or not they should be matched as
1111
// case sensitive
1212
type TokenType struct {
13-
Type string // verb, keyword, separator, etc
14-
Words []string // words to be considered as a given token type
15-
CaseSensitive bool // whether or not this token type is case sensitive
13+
Type string // verb, keyword, separator, etc
14+
Words []string // words to be considered as a given token type
1615
}

token_test.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,9 @@ func TestTokenType(t *testing.T) {
99
assertions := assert.New(t)
1010

1111
result := TokenType{
12-
Type: "keyword",
13-
Words: []string{"for", "if"},
14-
CaseSensitive: true,
12+
Type: "keyword",
13+
Words: []string{"for", "if"},
1514
}
16-
assertions.Equal(true, result.CaseSensitive)
1715
assertions.Equal([]string{"for", "if"}, result.Words)
1816
assertions.Equal("keyword", result.Type)
1917
}

0 commit comments

Comments
 (0)