• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Permify / permify / 7949440764

18 Feb 2024 01:46PM UTC coverage: 78.608% (-1.8%) from 80.385%
7949440764

Pull #1057

github

thegeekywanderer
feat(api): implemented schema updater for memory
Pull Request #1057: feat(api): partial schema insertion

24 of 235 new or added lines in 3 files covered. (10.21%)

4 existing lines in 1 file now uncovered.

7522 of 9569 relevant lines covered (78.61%)

48.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.07
/pkg/dsl/parser/parser.go
1
package parser
2

3
import (
4
        "errors"
5
        "fmt"
6
        "strings"
7

8
        "github.com/Permify/permify/pkg/dsl/ast"
9
        "github.com/Permify/permify/pkg/dsl/lexer"
10
        "github.com/Permify/permify/pkg/dsl/token"
11
        "github.com/Permify/permify/pkg/dsl/utils"
12
)
13

14
const (
15
        // iota is a special identifier that is automatically set to 0 in this case, and increments by 1 for each subsequent constant declaration. By assigning the value to the blank identifier _, it is effectively ignored.
16
        _ int = iota
17

18
        // LOWEST precedence level for lowest precedence
19
        LOWEST
20
        // AND_OR_NOT precedence level for logical operators (AND, OR)
21
        AND_OR_NOT
22
)
23

24
var precedences = map[token.Type]int{ // a map that assigns precedence levels to different token types
25
        token.AND: AND_OR_NOT,
26
        token.OR:  AND_OR_NOT,
27
        token.NOT: AND_OR_NOT,
28
}
29

30
// Parser is a struct that contains information and functions related to parsing
31
type Parser struct {
32
        // a pointer to a Lexer object that will provide tokens for parsing
33
        l *lexer.Lexer
34
        // the current token being processed
35
        currentToken token.Token
36
        // the next token after currentToken
37
        peekToken token.Token
38
        // a slice of error messages that are generated during parsing
39
        errors []string
40
        // a map that associates prefix parsing functions with token types
41
        prefixParseFns map[token.Type]prefixParseFn
42
        // a map that associates infix parsing functions with token types
43
        infixParseFunc map[token.Type]infixParseFn
44
        // references to entities, rules, relations, attributes, and permissions
45
        references *ast.References
46
}
47

48
type (
49
        // a function that parses prefix expressions and returns an ast.Expression and error
50
        prefixParseFn func() (ast.Expression, error)
51

52
        // a function that parses infix expressions and returns an ast.Expression and error
53
        infixParseFn func(ast.Expression) (ast.Expression, error)
54
)
55

56
// NewParser creates a new Parser object with the given input string
57
func NewParser(str string) (p *Parser) {
22✔
58
        // initialize a new Parser object with the given input string and default values for other fields
22✔
59
        p = &Parser{
22✔
60
                l:          lexer.NewLexer(str), // create a new Lexer object with the input string
22✔
61
                errors:     []string{},          // initialize an empty slice of error messages
22✔
62
                references: ast.NewReferences(), // initialize an empty map for relational references
22✔
63
        }
22✔
64

22✔
65
        // register prefix parsing functions for token types IDENT and NOT
22✔
66
        p.prefixParseFns = make(map[token.Type]prefixParseFn) // initialize an empty map for prefix parsing functions
22✔
67
        p.registerPrefix(
22✔
68
                token.IDENT,
22✔
69
                p.parseIdentifierOrCall,
22✔
70
        ) // associate the parseIdentifier function with the IDENT token type
22✔
71

22✔
72
        // register infix parsing functions for token types AND, OR, NOT
22✔
73
        p.infixParseFunc = make(map[token.Type]infixParseFn) // initialize an empty map for infix parsing functions
22✔
74
        p.registerInfix(
22✔
75
                token.AND,
22✔
76
                p.parseInfixExpression,
22✔
77
        ) // associate the parseInfixExpression function with the AND token type
22✔
78
        p.registerInfix(
22✔
79
                token.OR,
22✔
80
                p.parseInfixExpression,
22✔
81
        ) // associate the parseInfixExpression function with the OR token type
22✔
82
        p.registerInfix(
22✔
83
                token.NOT,
22✔
84
                p.parseInfixExpression,
22✔
85
        ) // associate the parseInfixExpression function with the OR token type
22✔
86

22✔
87
        return p // return the newly created Parser object and no error
22✔
88
}
22✔
89

90
// next retrieves the next non-ignored token from the Parser's lexer and updates the Parser's currentToken and peekToken fields
91
func (p *Parser) next() {
769✔
92
        for {
2,356✔
93
                // retrieve the next token from the lexer
1,587✔
94
                peek := p.l.NextToken()
1,587✔
95
                // if the token is not an ignored token (e.g. whitespace or comments), update the currentToken and peekToken fields and exit the loop
1,587✔
96
                if !token.IsIgnores(peek.Type) {
2,356✔
97
                        // set the currentToken field to the previous peekToken value
769✔
98
                        p.currentToken = p.peekToken
769✔
99
                        // set the peekToken field to the new peek value
769✔
100
                        p.peekToken = peek
769✔
101
                        // exit the loop
769✔
102
                        break
769✔
103
                }
104
        }
105
}
106

107
// nextWithIgnores advances the parser's token stream by one position.
108
// It updates the currentToken and peekToken of the Parser.
109
func (p *Parser) nextWithIgnores() {
30✔
110
        // Get the next token in the lexers token stream and store it in the variable peek.
30✔
111
        peek := p.l.NextToken()
30✔
112

30✔
113
        // Update the currentToken with the value of peekToken.
30✔
114
        p.currentToken = p.peekToken
30✔
115

30✔
116
        // Update the peekToken with the value of peek (the new next token in the lexers stream).
30✔
117
        p.peekToken = peek
30✔
118
}
30✔
119

120
// currentTokenIs checks if the Parser's currentToken is any of the given token types
121
func (p *Parser) currentTokenIs(tokens ...token.Type) bool {
974✔
122
        // iterate through the given token types and check if any of them match the currentToken's type
974✔
123
        for _, t := range tokens {
1,948✔
124
                if p.currentToken.Type == t {
1,303✔
125
                        // if a match is found, return true
329✔
126
                        return true
329✔
127
                }
329✔
128
        }
129
        // if no match is found, return false
130
        return false
645✔
131
}
132

133
// peekTokenIs checks if the Parser's peekToken is any of the given token types
134
func (p *Parser) peekTokenIs(tokens ...token.Type) bool {
763✔
135
        // iterate through the given token types and check if any of them match the peekToken's type
763✔
136
        for _, t := range tokens {
1,526✔
137
                if p.peekToken.Type == t {
1,186✔
138
                        // if a match is found, return true
423✔
139
                        return true
423✔
140
                }
423✔
141
        }
142
        // if no match is found, return false
143
        return false
340✔
144
}
145

146
// Error returns an error if there are any errors in the Parser's errors slice
147
func (p *Parser) Error() error {
27✔
148
        // if there are no errors, return nil
27✔
149
        if len(p.errors) == 0 {
27✔
150
                return nil
×
151
        }
×
152
        // if there are errors, return the first error message in the errors slice as an error type
153
        return errors.New(p.errors[0])
27✔
154
}
155

156
// Parse reads and parses the input string and returns an AST representation of the schema, along with any errors encountered during parsing
157
func (p *Parser) Parse() (*ast.Schema, error) {
22✔
158
        // create a new Schema object to store the parsed statements
22✔
159
        schema := ast.NewSchema()
22✔
160
        schema.Statements = []ast.Statement{}
22✔
161

22✔
162
        // loop through the input string until the end is reached
22✔
163
        for !p.currentTokenIs(token.EOF) {
152✔
164
                // parse the next statement in the input string
130✔
165
                stmt, err := p.parseStatement()
130✔
166
                if err != nil {
140✔
167
                        // if there was an error parsing the statement, return the error message
10✔
168
                        return nil, p.Error()
10✔
169
                }
10✔
170
                if stmt != nil {
143✔
171
                        // add the parsed statement to the schema's Statements field if it is not nil
23✔
172
                        schema.Statements = append(schema.Statements, stmt)
23✔
173
                }
23✔
174

175
                // move to the next token in the input string
176
                p.next()
120✔
177
        }
178

179
        schema.SetReferences(p.references)
12✔
180

12✔
181
        // return the parsed schema object and nil to indicate that there were no errors
12✔
182
        return schema, nil
12✔
183
}
184

185
// parseStatement method parses the current statement based on its defined token types
186
func (p *Parser) parseStatement() (ast.Statement, error) {
130✔
187
        // switch on the currentToken's type to determine which type of statement to parse
130✔
188
        switch p.currentToken.Type {
130✔
189
        case token.ENTITY:
31✔
190
                // if the currentToken is ENTITY, parse an EntityStatement
31✔
191
                return p.parseEntityStatement()
31✔
192
        case token.RULE:
2✔
193
                // if the currentToken is RULE, parse a RuleStatement
2✔
194
                return p.parseRuleStatement()
2✔
NEW
195
        case token.RELATION:
×
NEW
196
                // if currentToken is RELATION, parse a RelationStatement (only for partial update scenario)
×
NEW
197
                return p.parseRelationStatement("")
×
NEW
198
        case token.ATTRIBUTE:
×
NEW
199
                // if currentToken is ATTRIBUTE, parse an AttributeStatement (only for partial update scenario)
×
NEW
200
                return p.parseAttributeStatement("")
×
NEW
201
        case token.PERMISSION:
×
NEW
202
                // if currentToken is PERMISSION, parse a PermissionStatement (only for partial update scenario)
×
NEW
203
                return p.parsePermissionStatement("")
×
204
        default:
97✔
205
                return nil, nil
97✔
206
        }
207
}
208

209
// parseEntityStatement method parses an ENTITY statement and returns an EntityStatement AST node
210
func (p *Parser) parseEntityStatement() (*ast.EntityStatement, error) {
31✔
211
        // create a new EntityStatement object and set its Entity field to the currentToken
31✔
212
        stmt := &ast.EntityStatement{Entity: p.currentToken}
31✔
213
        // expect the next token to be an identifier token, and set the EntityStatement's Name field to the identifier's value
31✔
214
        if !p.expectAndNext(token.IDENT) {
32✔
215
                return nil, p.Error()
1✔
216
        }
1✔
217
        stmt.Name = p.currentToken
30✔
218

30✔
219
        // add the entity reference to the Parser's entityReferences map
30✔
220
        err := p.references.SetEntityReference(stmt.Name.Literal)
30✔
221
        if err != nil {
31✔
222
                p.duplicationError(stmt.Name.Literal) // Generate an error message indicating a duplication error
1✔
223
                return nil, p.Error()
1✔
224
        }
1✔
225

226
        // expect the next token to be a left brace token, indicating the start of the entity's body
227
        if !p.expectAndNext(token.LCB) {
30✔
228
                return nil, p.Error()
1✔
229
        }
1✔
230

231
        // loop through the entity's body until a right brace token is encountered
232
        for !p.currentTokenIs(token.RCB) {
245✔
233
                // if the currentToken is EOF, raise an error and return nil for both the statement and error values
217✔
234
                if p.currentTokenIs(token.EOF) {
218✔
235
                        p.currentError(token.RCB)
1✔
236
                        return nil, p.Error()
1✔
237
                }
1✔
238
                // based on the currentToken's type, parse a RelationStatement or PermissionStatement and add it to the EntityStatement's corresponding field
239
                switch p.currentToken.Type {
216✔
240
                case token.RELATION:
34✔
241
                        relation, err := p.parseRelationStatement(stmt.Name.Literal)
34✔
242
                        if err != nil {
37✔
243
                                return nil, p.Error()
3✔
244
                        }
3✔
245
                        stmt.RelationStatements = append(stmt.RelationStatements, relation)
31✔
246
                case token.ATTRIBUTE:
4✔
247
                        attribute, err := p.parseAttributeStatement(stmt.Name.Literal)
4✔
248
                        if err != nil {
5✔
249
                                return nil, p.Error()
1✔
250
                        }
1✔
251
                        stmt.AttributeStatements = append(stmt.AttributeStatements, attribute)
3✔
252
                case token.PERMISSION:
23✔
253
                        action, err := p.parsePermissionStatement(stmt.Name.Literal)
23✔
254
                        if err != nil {
25✔
255
                                return nil, p.Error()
2✔
256
                        }
2✔
257
                        stmt.PermissionStatements = append(stmt.PermissionStatements, action)
21✔
258
                default:
155✔
259
                        // if the currentToken is not recognized, check if it is a newline, left brace, or right brace token, and skip it if it is
155✔
260
                        if !p.currentTokenIs(token.NEWLINE) && !p.currentTokenIs(token.LCB) && !p.currentTokenIs(token.RCB) {
155✔
261
                                // if the currentToken is not recognized and not a newline, left brace, or right brace token, raise an error and return nil for both the statement and error values
×
262
                                p.currentError(token.RELATION, token.PERMISSION, token.ATTRIBUTE)
×
263
                                return nil, p.Error()
×
264
                        }
×
265
                }
266
                // move to the next token in the input string
267
                p.next()
210✔
268
        }
269

270
        // return the parsed EntityStatement and nil for the error value
271
        return stmt, nil
21✔
272
}
273

274
// parseRuleStatement is responsible for parsing a rule statement in the form:
275
//
276
//        rule name(typ1 string, typ2 boolean) {
277
//            EXPRESSION
278
//        }
279
//
280
// This method assumes the current token points to the 'rule' token when it is called.
281
func (p *Parser) parseRuleStatement() (*ast.RuleStatement, error) {
2✔
282
        // Create a new RuleStatement
2✔
283
        stmt := &ast.RuleStatement{Rule: p.currentToken}
2✔
284

2✔
285
        // Expect the next token to be an identifier (the name of the rule).
2✔
286
        // If it's not an identifier, return an error.
2✔
287
        if !p.expectAndNext(token.IDENT) {
2✔
288
                return nil, p.Error()
×
289
        }
×
290
        stmt.Name = p.currentToken
2✔
291

2✔
292
        // Expect the next token to be a left parenthesis '(' starting the argument list.
2✔
293
        if !p.expectAndNext(token.LP) {
2✔
294
                return nil, p.Error()
×
295
        }
×
296

297
        arguments := map[token.Token]ast.AttributeTypeStatement{}
2✔
298
        args := map[string]string{}
2✔
299

2✔
300
        // Loop over the tokens until a right parenthesis ')' is encountered.
2✔
301
        // In each iteration, two tokens are processed: an identifier (arg name) and its type.
2✔
302
        for !p.peekTokenIs(token.RP) {
6✔
303
                // Expect the first token to be the parameter's identifier.
4✔
304
                if !p.expectAndNext(token.IDENT) {
4✔
305
                        return nil, p.Error()
×
306
                }
×
307
                argument := p.currentToken
4✔
308
                arg := p.currentToken.Literal
4✔
309

4✔
310
                // Expect the second token to be the parameter's type.
4✔
311
                if !p.expectAndNext(token.IDENT) {
4✔
312
                        return nil, p.Error()
×
313
                }
×
314

315
                if p.peekTokenIs(token.LSB) { // Check if the next token is '['
5✔
316
                        arguments[argument] = ast.AttributeTypeStatement{
1✔
317
                                Type:    p.currentToken,
1✔
318
                                IsArray: true, // Marking the type as an array
1✔
319
                        }
1✔
320
                        args[arg] = p.currentToken.Literal + "[]" // Store the argument type as string with "[]" suffix
1✔
321
                        p.next()                                  // Move to the '[' token
1✔
322
                        if !p.expectAndNext(token.RSB) {          // Expect and move to the ']' token
1✔
323
                                return nil, p.Error()
×
324
                        }
×
325
                } else {
3✔
326
                        arguments[argument] = ast.AttributeTypeStatement{
3✔
327
                                Type:    p.currentToken,
3✔
328
                                IsArray: false, // Marking the type as not an array
3✔
329
                        }
3✔
330
                        args[arg] = p.currentToken.Literal // Store the regular argument type
3✔
331
                }
3✔
332

333
                // If the next token is a comma, there are more parameters to parse.
334
                // Continue to the next iteration.
335
                if p.peekTokenIs(token.COMMA) {
6✔
336
                        p.next()
2✔
337
                        continue
2✔
338
                } else if !p.peekTokenIs(token.RP) {
2✔
339
                        // If the next token is not a comma, it must be a closing parenthesis.
×
340
                        // If it's not, return an error.
×
341
                        return nil, p.Error()
×
342
                }
×
343
        }
344

345
        // Save parsed arguments to the statement
346
        stmt.Arguments = arguments
2✔
347

2✔
348
        // Consume the right parenthesis.
2✔
349
        p.next()
2✔
350

2✔
351
        // Expect the next token to be a left curly bracket '{' starting the body.
2✔
352
        if !p.expectAndNext(token.LCB) {
2✔
353
                return nil, p.Error()
×
354
        }
×
355

356
        p.next()
2✔
357

2✔
358
        // Collect tokens for the body until a closing curly bracket '}' is encountered.
2✔
359
        var bodyTokens []token.Token
2✔
360
        for !p.peekTokenIs(token.RCB) {
32✔
361
                // If there's no closing bracket, return an error.
30✔
362
                if p.peekTokenIs(token.EOF) {
30✔
363
                        return nil, p.Error()
×
364
                }
×
365

366
                bodyTokens = append(bodyTokens, p.currentToken)
30✔
367
                p.nextWithIgnores()
30✔
368
        }
369

370
        // Combine all the body tokens into a single string
371
        var bodyStr strings.Builder
2✔
372
        for _, t := range bodyTokens {
32✔
373
                bodyStr.WriteString(t.Literal)
30✔
374
        }
30✔
375
        stmt.Expression = bodyStr.String()
2✔
376

2✔
377
        // Expect and consume the closing curly bracket '}'.
2✔
378
        if !p.expectAndNext(token.RCB) {
2✔
379
                return nil, p.Error()
×
380
        }
×
381

382
        // Register the parsed rule in the parser's references.
383
        err := p.references.SetRuleReference(stmt.Name.Literal, args)
2✔
384
        if err != nil {
2✔
385
                // If there's an error (e.g., a duplicate rule), return an error.
×
386
                p.duplicationError(stmt.Name.Literal)
×
387
                return nil, p.Error()
×
388
        }
×
389

390
        // Return the successfully parsed RuleStatement.
391
        return stmt, nil
2✔
392
}
393

394
// parseRelationStatement method parses a RELATION statement and returns a RelationStatement AST node
395
func (p *Parser) parseAttributeStatement(entityName string) (*ast.AttributeStatement, error) {
4✔
396
        // create a new RelationStatement object and set its Relation field to the currentToken
4✔
397
        stmt := &ast.AttributeStatement{Attribute: p.currentToken}
4✔
398

4✔
399
        // expect the next token to be an identifier token, and set the RelationStatement's Name field to the identifier's value
4✔
400
        if !p.expectAndNext(token.IDENT) {
5✔
401
                return nil, p.Error()
1✔
402
        }
1✔
403
        stmt.Name = p.currentToken
3✔
404

3✔
405
        if !p.expectAndNext(token.IDENT) {
3✔
406
                return nil, p.Error()
×
407
        }
×
408

409
        atstmt := ast.AttributeTypeStatement{Type: p.currentToken}
3✔
410
        atstmt.IsArray = false
3✔
411

3✔
412
        if p.peekTokenIs(token.LSB) {
4✔
413
                p.next()
1✔
414
                if !p.expectAndNext(token.RSB) {
1✔
415
                        return nil, p.Error()
×
416
                }
×
417
                atstmt.IsArray = true
1✔
418
        }
419

420
        stmt.AttributeType = atstmt
3✔
421

3✔
422
        key := utils.Key(entityName, stmt.Name.Literal)
3✔
423
        // add the relation reference to the Parser's relationReferences and relationalReferences maps
3✔
424
        err := p.references.SetAttributeReferences(key, atstmt)
3✔
425
        if err != nil {
3✔
426
                p.duplicationError(key) // Generate an error message indicating a duplication error
×
427
                return nil, p.Error()
×
428
        }
×
429

430
        // return the parsed RelationStatement and nil for the error value
431
        return stmt, nil
3✔
432
}
433

434
// parseRelationStatement method parses a RELATION statement and returns a RelationStatement AST node
435
func (p *Parser) parseRelationStatement(entityName string) (*ast.RelationStatement, error) {
34✔
436
        // create a new RelationStatement object and set its Relation field to the currentToken
34✔
437
        stmt := &ast.RelationStatement{Relation: p.currentToken}
34✔
438

34✔
439
        // expect the next token to be an identifier token, and set the RelationStatement's Name field to the identifier's value
34✔
440
        if !p.expectAndNext(token.IDENT) {
34✔
441
                return nil, p.Error()
×
442
        }
×
443
        stmt.Name = p.currentToken
34✔
444
        relationName := stmt.Name.Literal
34✔
445

34✔
446
        // expect the next token to be a SIGN token, indicating the start of the relation type(s)
34✔
447
        if !p.expect(token.SIGN) {
35✔
448
                return nil, p.Error()
1✔
449
        }
1✔
450

451
        // loop through the relation types until no more SIGN tokens are encountered
452
        for p.peekTokenIs(token.SIGN) {
69✔
453
                // parse a RelationTypeStatement and append it to the RelationStatement's RelationTypes field
36✔
454
                relationStatement, err := p.parseRelationTypeStatement()
36✔
455
                if err != nil {
37✔
456
                        return nil, p.Error()
1✔
457
                }
1✔
458
                stmt.RelationTypes = append(stmt.RelationTypes, *relationStatement)
35✔
459
        }
460

461
        key := utils.Key(entityName, relationName)
32✔
462

32✔
463
        // add the relation reference to the Parser's relationReferences and relationalReferences maps
32✔
464
        err := p.references.SetRelationReferences(key, stmt.RelationTypes)
32✔
465
        if err != nil {
33✔
466
                p.duplicationError(key) // Generate an error message indicating a duplication error
1✔
467
                return nil, p.Error()
1✔
468
        }
1✔
469

470
        // return the parsed RelationStatement and nil for the error value
471
        return stmt, nil
31✔
472
}
473

474
// parseRelationTypeStatement method parses a single relation type within a RELATION statement and returns a RelationTypeStatement AST node
475
func (p *Parser) parseRelationTypeStatement() (*ast.RelationTypeStatement, error) {
36✔
476
        // expect the currentToken to be a SIGN token, indicating the start of the relation type
36✔
477
        if !p.expectAndNext(token.SIGN) {
36✔
478
                return nil, p.Error()
×
479
        }
×
480
        // create a new RelationTypeStatement object and set its Sign field to the SIGN token
481
        stmt := &ast.RelationTypeStatement{Sign: p.currentToken}
36✔
482

36✔
483
        // expect the next token to be an identifier token, and set the RelationTypeStatement's Type field to the identifier's value
36✔
484
        if !p.expectAndNext(token.IDENT) {
37✔
485
                return nil, p.Error()
1✔
486
        }
1✔
487
        stmt.Type = p.currentToken
35✔
488

35✔
489
        // if the next token is a HASH token, indicating that a specific relation within the relation type is being referenced, parse it and set the RelationTypeStatement's Relation field to the identifier's value
35✔
490
        if p.peekTokenIs(token.HASH) {
38✔
491
                p.next()
3✔
492
                if !p.expectAndNext(token.IDENT) {
3✔
493
                        return nil, p.Error()
×
494
                }
×
495
                stmt.Relation = p.currentToken
3✔
496
        }
497

498
        // return the parsed RelationTypeStatement and nil for the error value
499
        return stmt, nil
35✔
500
}
501

502
// parsePermissionStatement method parses an PERMISSION statement and returns an PermissionStatement AST node
503
func (p *Parser) parsePermissionStatement(entityName string) (ast.Statement, error) {
23✔
504
        // create a new PermissionStatement object and set its Permission field to the currentToken
23✔
505
        stmt := &ast.PermissionStatement{Permission: p.currentToken}
23✔
506

23✔
507
        // expect the next token to be an identifier token, and set the PermissionStatement's Name field to the identifier's value
23✔
508
        if !p.expectAndNext(token.IDENT) {
24✔
509
                return nil, p.Error()
1✔
510
        }
1✔
511
        stmt.Name = p.currentToken
22✔
512

22✔
513
        key := utils.Key(entityName, stmt.Name.Literal)
22✔
514
        // add the action reference to the Parser's actionReferences and relationalReferences maps
22✔
515
        err := p.references.SetPermissionReference(key)
22✔
516
        if err != nil {
23✔
517
                p.duplicationError(key) // Generate an error message indicating a duplication error
1✔
518
                return nil, p.Error()
1✔
519
        }
1✔
520

521
        // expect the next token to be an ASSIGN token, indicating the start of the expression to be assigned to the action
522
        if !p.expectAndNext(token.ASSIGN) {
21✔
523
                return nil, p.Error()
×
524
        }
×
525

526
        p.next()
21✔
527

21✔
528
        // parse the expression statement and set it as the PermissionStatement's ExpressionStatement field
21✔
529
        ex, err := p.parseExpressionStatement()
21✔
530
        if err != nil {
21✔
531
                return nil, p.Error()
×
532
        }
×
533
        stmt.ExpressionStatement = ex
21✔
534

21✔
535
        // return the parsed PermissionStatement and nil for the error value
21✔
536
        return stmt, nil
21✔
537
}
538

539
// parseExpressionStatement method parses an expression statement and returns an ExpressionStatement AST node
540
func (p *Parser) parseExpressionStatement() (*ast.ExpressionStatement, error) {
21✔
541
        // create a new ExpressionStatement object
21✔
542
        stmt := &ast.ExpressionStatement{}
21✔
543
        var err error
21✔
544
        // parse the expression using the lowest precedence value as the initial precedence level
21✔
545
        stmt.Expression, err = p.parseExpression(LOWEST)
21✔
546
        if err != nil {
21✔
547
                return nil, p.Error()
×
548
        }
×
549

550
        // return the parsed ExpressionStatement and nil for the error value
551
        return stmt, nil
21✔
552
}
553

554
// expectAndNext method checks if the next token is of the expected type and advances the lexer to the next token if it is. It returns true if the next token is of the expected type, and false otherwise.
555
func (p *Parser) expectAndNext(t token.Type) bool {
244✔
556
        // if the next token is of the expected type, advance the lexer to the next token and return true
244✔
557
        if p.peekTokenIs(t) {
483✔
558
                p.next()
239✔
559
                return true
239✔
560
        }
239✔
561
        // otherwise, generate an error message indicating that the expected token type was not found and return false
562
        p.peekError(t)
5✔
563
        return false
5✔
564
}
565

566
// expect method checks if the next token is of the expected type, without advancing the lexer. It returns true if the next token is of the expected type, and false otherwise.
567
func (p *Parser) expect(t token.Type) bool {
62✔
568
        // if the next token is of the expected type, return true
62✔
569
        if p.peekTokenIs(t) {
123✔
570
                return true
61✔
571
        }
61✔
572
        // otherwise, generate an error message indicating that the expected token type was not found and return false
573
        p.peekError(t)
1✔
574
        return false
1✔
575
}
576

577
// parseExpression method parses an expression with a given precedence level and returns the parsed expression as an AST node. It takes an integer value indicating the precedence level.
578
func (p *Parser) parseExpression(precedence int) (ast.Expression, error) {
82✔
579
        var exp ast.Expression
82✔
580
        var err error
82✔
581

82✔
582
        if p.currentTokenIs(token.LP) {
110✔
583
                p.next() // Consume the left parenthesis.
28✔
584
                exp, err = p.parseExpression(LOWEST)
28✔
585
                if err != nil {
28✔
586
                        return nil, err
×
587
                }
×
588

589
                if !p.expect(token.RP) {
28✔
590
                        return nil, p.Error()
×
591
                }
×
592
                p.next() // Consume the right parenthesis.
28✔
593
        } else {
54✔
594
                // get the prefix parsing function for the current token type
54✔
595
                prefix := p.prefixParseFns[p.currentToken.Type]
54✔
596
                if prefix == nil {
54✔
597
                        p.noPrefixParseFnError(p.currentToken.Type)
×
598
                        return nil, p.Error()
×
599
                }
×
600

601
                // parse the prefix expression
602
                exp, err = prefix()
54✔
603
                if err != nil {
54✔
604
                        return nil, p.Error()
×
605
                }
×
606
        }
607

608
        // continue parsing the expression while the next token has a higher precedence level than the current precedence level
609
        for !p.peekTokenIs(token.NEWLINE) && precedence < p.peekPrecedence() {
115✔
610
                // get the infix parsing function for the next token type
33✔
611
                infix := p.infixParseFunc[p.peekToken.Type]
33✔
612
                if infix == nil {
33✔
613
                        return exp, nil
×
614
                }
×
615
                p.next()
33✔
616
                // parse the infix expression with the current expression as its left-hand side
33✔
617
                exp, err = infix(exp)
33✔
618
                if err != nil {
33✔
619
                        return nil, p.Error()
×
620
                }
×
621
        }
622

623
        // return the parsed expression and nil for the error value
624
        return exp, nil
82✔
625
}
626

627
// parseInfixExpression parses an infix expression that has a left operand and an operator followed by
628
// a right operand, such as "a or b" or "x and y".
629
// It takes the left operand as an argument, constructs an InfixExpression with the current operator
630
// and left operand, and parses the right operand with a higher precedence to construct the final
631
// expression tree.
632
// It returns the resulting InfixExpression and any error encountered.
633
func (p *Parser) parseInfixExpression(left ast.Expression) (ast.Expression, error) {
33✔
634
        // Ensure the current token is a valid infix operator before proceeding.
33✔
635
        if !p.isInfixOperator(p.currentToken.Type) {
33✔
636
                p.currentError(token.AND, token.OR, token.NOT) // Replace with your actual valid infix token types
×
637
                return nil, p.Error()
×
638
        }
×
639

640
        // Create a new InfixExpression with the left operand and the current operator.
641
        expression := &ast.InfixExpression{
33✔
642
                Op:       p.currentToken,
33✔
643
                Left:     left,
33✔
644
                Operator: ast.Operator(p.currentToken.Literal),
33✔
645
        }
33✔
646

33✔
647
        // Get the precedence of the current operator and consume the operator token.
33✔
648
        precedence := p.currentPrecedence()
33✔
649
        p.next()
33✔
650

33✔
651
        // Parse the right operand with a higher precedence to construct the final expression tree.
33✔
652
        right, err := p.parseExpression(precedence)
33✔
653
        if err != nil {
33✔
654
                return nil, err
×
655
        }
×
656

657
        // Ensure the right operand is not nil.
658
        if right == nil {
33✔
659
                p.currentError(token.IDENT, token.LP) // Replace with your actual valid right operand token types
×
660
                return nil, p.Error()
×
661
        }
×
662

663
        // Set the right operand of the InfixExpression and return it.
664
        expression.Right = right
33✔
665
        return expression, nil
33✔
666
}
667

668
// parseIntegerLiteral parses an integer literal and returns the resulting IntegerLiteral expression.
669
func (p *Parser) isInfixOperator(tokenType token.Type) bool {
33✔
670
        return tokenType == token.AND || tokenType == token.OR || tokenType == token.NOT
33✔
671
}
33✔
672

673
// peekPrecedence returns the precedence of the next token in the input, if it is a known
674
// operator, or the lowest precedence otherwise.
675
func (p *Parser) peekPrecedence() int {
87✔
676
        if pr, ok := precedences[p.peekToken.Type]; ok {
121✔
677
                return pr
34✔
678
        }
34✔
679
        return LOWEST
53✔
680
}
681

682
// currentPrecedence returns the precedence of the current token in the input, if it is a known
683
// operator, or the lowest precedence otherwise.
684
func (p *Parser) currentPrecedence() int {
33✔
685
        if pr, ok := precedences[p.currentToken.Type]; ok {
66✔
686
                return pr
33✔
687
        }
33✔
688
        return LOWEST
×
689
}
690

691
func (p *Parser) parseIdentifierOrCall() (ast.Expression, error) {
54✔
692
        // Ensure the current token is a valid identifier before proceeding.
54✔
693
        if !p.currentTokenIs(token.IDENT) {
54✔
694
                return nil, fmt.Errorf("unexpected token type for identifier expression: %s", p.currentToken.Type)
×
695
        }
×
696

697
        if p.peekTokenIs(token.LP) {
56✔
698
                return p.parseCallExpression()
2✔
699
        }
2✔
700

701
        return p.parseIdentifierExpression()
52✔
702
}
703

704
// parseIdentifier parses an identifier expression that may consist of one or more dot-separated
705
// identifiers, such as "x", "foo.bar", or "a.b.c.d".
706
// It constructs a new Identifier expression with the first token as the prefix and subsequent
707
// tokens as identifiers, and returns the resulting expression and any error encountered.
708
func (p *Parser) parseIdentifierExpression() (ast.Expression, error) {
56✔
709
        // Ensure the current token is a valid identifier before proceeding.
56✔
710
        if !p.currentTokenIs(token.IDENT) {
56✔
711
                return nil, fmt.Errorf("unexpected token type for identifier expression: %s", p.currentToken.Type)
×
712
        }
×
713

714
        // Create a new Identifier expression with the first token as the prefix.
715
        ident := &ast.Identifier{Idents: []token.Token{p.currentToken}}
56✔
716

56✔
717
        // If the next token is a dot, consume it and continue parsing the next identifier.
56✔
718
        for p.peekTokenIs(token.DOT) {
77✔
719
                p.next() // Consume the dot token
21✔
720

21✔
721
                // Check if the next token after the dot is a valid identifier
21✔
722
                if !p.peekTokenIs(token.IDENT) {
21✔
723
                        return nil, fmt.Errorf("expected identifier after dot, got %s", p.peekToken.Type)
×
724
                }
×
725

726
                p.next() // Consume the identifier token
21✔
727
                ident.Idents = append(ident.Idents, p.currentToken)
21✔
728
        }
729

730
        // Return the resulting Identifier expression.
731
        return ident, nil
56✔
732
}
733

734
// call_func(variable1, variable2)
735
func (p *Parser) parseCallExpression() (ast.Expression, error) {
2✔
736
        // Ensure the current token is a valid identifier before proceeding.
2✔
737
        if !p.currentTokenIs(token.IDENT) {
2✔
738
                return nil, fmt.Errorf("unexpected token type for identifier expression: %s", p.currentToken.Type)
×
739
        }
×
740

741
        // Create a new Identifier expression with the first token as the prefix.
742
        call := &ast.Call{Name: p.currentToken}
2✔
743

2✔
744
        if !p.expectAndNext(token.LP) {
2✔
745
                return nil, p.Error()
×
746
        }
×
747

748
        // Check if there are no arguments
749
        if p.peekTokenIs(token.RP) {
2✔
750
                p.next() // consume the RP token
×
751
                return call, nil
×
752
        }
×
753

754
        p.next()
2✔
755

2✔
756
        // Parse the first argument
2✔
757
        ident, err := p.parseIdentifierExpression()
2✔
758
        if err != nil {
2✔
759
                return nil, err
×
760
        }
×
761

762
        i, ok := ident.(*ast.Identifier)
2✔
763
        if !ok {
2✔
764
                return nil, fmt.Errorf("expected identifier, got %T", ident)
×
765
        }
×
766
        call.Arguments = append(call.Arguments, *i)
2✔
767

2✔
768
        // Parse remaining arguments
2✔
769
        for p.peekTokenIs(token.COMMA) {
4✔
770
                p.next()
2✔
771

2✔
772
                if !p.expectAndNext(token.IDENT) {
2✔
773
                        return nil, p.Error()
×
774
                }
×
775

776
                ident, err = p.parseIdentifierExpression()
2✔
777
                if err != nil {
2✔
778
                        return nil, err
×
779
                }
×
780

781
                i, ok = ident.(*ast.Identifier)
2✔
782
                if !ok {
2✔
783
                        return nil, fmt.Errorf("expected identifier, got %T", ident)
×
784
                }
×
785
                call.Arguments = append(call.Arguments, *i)
2✔
786
        }
787

788
        if !p.expectAndNext(token.RP) {
2✔
789
                return nil, p.Error()
×
790
        }
×
791

792
        // Return the resulting Identifier expression.
793
        return call, nil
2✔
794
}
795

796
// registerPrefix safely registers a parsing function for a prefix token type in the parser's prefixParseFns map.
797
// It takes a token type and a prefix parsing function as arguments, and stores the function in the map
798
// under the given token type key.
799
func (p *Parser) registerPrefix(tokenType token.Type, fn prefixParseFn) {
22✔
800
        if fn == nil {
22✔
801
                p.duplicationError(fmt.Sprintf("registerPrefix: nil function for token type %s", tokenType))
×
802
                return
×
803
        }
×
804

805
        if _, exists := p.prefixParseFns[tokenType]; exists {
22✔
806
                p.duplicationError(fmt.Sprintf("registerPrefix: token type %s already registered", tokenType))
×
807
                return
×
808
        }
×
809

810
        p.prefixParseFns[tokenType] = fn
22✔
811
}
812

813
// registerInfix safely registers a parsing function for an infix token type in the parser's infixParseFunc map.
814
// It takes a token type and an infix parsing function as arguments, and stores the function in the map
815
// under the given token type key.
816
func (p *Parser) registerInfix(tokenType token.Type, fn infixParseFn) {
66✔
817
        if fn == nil {
66✔
818
                p.duplicationError(fmt.Sprintf("registerInfix: nil function for token type %s", tokenType))
×
819
                return
×
820
        }
×
821

822
        if _, exists := p.infixParseFunc[tokenType]; exists {
66✔
823
                p.duplicationError(fmt.Sprintf("registerInfix: token type %s already registered", tokenType))
×
824
                return
×
825
        }
×
826

827
        p.infixParseFunc[tokenType] = fn
66✔
828
}
829

830
// duplicationError adds an error message to the parser's error list indicating that a duplication was found.
831
// It takes a key string as an argument that is used to identify the source of the duplication in the input.
832
func (p *Parser) duplicationError(key string) {
3✔
833
        msg := fmt.Sprintf("%v:%v:duplication found for %s", p.l.GetLinePosition(), p.l.GetColumnPosition(), key)
3✔
834
        p.errors = append(p.errors, msg)
3✔
835
}
3✔
836

837
// noPrefixParseFnError adds an error message to the parser's error list indicating that no prefix parsing
838
// function was found for a given token type.
839
// It takes a token type as an argument that indicates the type of the token for which a parsing function is missing.
840
func (p *Parser) noPrefixParseFnError(t token.Type) {
×
841
        msg := fmt.Sprintf("%v:%v:no prefix parse function for %s found", p.l.GetLinePosition(), p.l.GetColumnPosition(), t)
×
842
        p.errors = append(p.errors, msg)
×
843
}
×
844

845
// peekError adds an error message to the parser's error list indicating that the next token in the input
846
// did not match the expected type(s).
847
// It takes one or more token types as arguments that indicate the expected types.
848
func (p *Parser) peekError(t ...token.Type) {
6✔
849
        expected := strings.Join(tokenTypesToStrings(t), ", ")
6✔
850
        msg := fmt.Sprintf(
6✔
851
                "%v:%v:expected next token to be %s, got %s instead",
6✔
852
                p.l.GetLinePosition(),
6✔
853
                p.l.GetColumnPosition(),
6✔
854
                expected,
6✔
855
                p.peekToken.Type,
6✔
856
        )
6✔
857
        p.errors = append(p.errors, msg)
6✔
858
}
6✔
859

860
// currentError adds an error message to the parser's error list indicating that the current token in the input
861
// did not match the expected type(s).
862
// It takes one or more token types as arguments that indicate the expected types.
863
func (p *Parser) currentError(t ...token.Type) {
1✔
864
        expected := strings.Join(tokenTypesToStrings(t), ", ")
1✔
865
        msg := fmt.Sprintf("%v:%v:expected token to be %s, got %s instead", p.l.GetLinePosition(),
1✔
866
                p.l.GetColumnPosition(), expected, p.currentToken.Type)
1✔
867
        p.errors = append(p.errors, msg)
1✔
868
}
1✔
869

870
// tokenTypesToStrings converts a slice of token types to a slice of their string representations.
871
func tokenTypesToStrings(types []token.Type) []string {
7✔
872
        strs := make([]string, len(types))
7✔
873
        for i, t := range types {
14✔
874
                strs[i] = t.String()
7✔
875
        }
7✔
876
        return strs
7✔
877
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc