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

Permify / permify / 9660012130

25 Jun 2024 09:32AM UTC coverage: 80.037% (+0.01%) from 80.027%
9660012130

push

github

tolgaOzen
build: version info update

7702 of 9623 relevant lines covered (80.04%)

49.28 hits per line

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

80.0
/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) {
26✔
58
        // initialize a new Parser object with the given input string and default values for other fields
26✔
59
        p = &Parser{
26✔
60
                l:          lexer.NewLexer(str), // create a new Lexer object with the input string
26✔
61
                errors:     []string{},          // initialize an empty slice of error messages
26✔
62
                references: ast.NewReferences(), // initialize an empty map for relational references
26✔
63
        }
26✔
64

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

26✔
69
        // register infix parsing functions for token types AND, OR, NOT
26✔
70
        p.infixParseFunc = make(map[token.Type]infixParseFn) // initialize an empty map for infix parsing functions
26✔
71
        p.registerInfix(token.AND, p.parseInfixExpression)   // associate the parseInfixExpression function with the AND token type
26✔
72
        p.registerInfix(token.OR, p.parseInfixExpression)    // associate the parseInfixExpression function with the OR token type
26✔
73
        p.registerInfix(token.NOT, p.parseInfixExpression)   // associate the parseInfixExpression function with the OR token type
26✔
74

26✔
75
        return p // return the newly created Parser object and no error
26✔
76
}
26✔
77

78
// next retrieves the next non-ignored token from the Parser's lexer and updates the Parser's currentToken and peekToken fields
79
func (p *Parser) next() {
825✔
80
        for {
2,537✔
81
                // retrieve the next token from the lexer
1,712✔
82
                peek := p.l.NextToken()
1,712✔
83
                // if the token is not an ignored token (e.g. whitespace or comments), update the currentToken and peekToken fields and exit the loop
1,712✔
84
                if !token.IsIgnores(peek.Type) {
2,537✔
85
                        // set the currentToken field to the previous peekToken value
825✔
86
                        p.currentToken = p.peekToken
825✔
87
                        // set the peekToken field to the new peek value
825✔
88
                        p.peekToken = peek
825✔
89
                        // exit the loop
825✔
90
                        break
825✔
91
                }
92
        }
93
}
94

95
// nextWithIgnores advances the parser's token stream by one position.
96
// It updates the currentToken and peekToken of the Parser.
97
func (p *Parser) nextWithIgnores() {
30✔
98
        // Get the next token in the lexers token stream and store it in the variable peek.
30✔
99
        peek := p.l.NextToken()
30✔
100

30✔
101
        // Update the currentToken with the value of peekToken.
30✔
102
        p.currentToken = p.peekToken
30✔
103

30✔
104
        // Update the peekToken with the value of peek (the new next token in the lexers stream).
30✔
105
        p.peekToken = peek
30✔
106
}
30✔
107

108
// currentTokenIs checks if the Parser's currentToken is any of the given token types
109
func (p *Parser) currentTokenIs(tokens ...token.Type) bool {
1,037✔
110
        // iterate through the given token types and check if any of them match the currentToken's type
1,037✔
111
        for _, t := range tokens {
2,074✔
112
                if p.currentToken.Type == t {
1,386✔
113
                        // if a match is found, return true
349✔
114
                        return true
349✔
115
                }
349✔
116
        }
117
        // if no match is found, return false
118
        return false
688✔
119
}
120

121
// peekTokenIs checks if the Parser's peekToken is any of the given token types
122
func (p *Parser) peekTokenIs(tokens ...token.Type) bool {
821✔
123
        // iterate through the given token types and check if any of them match the peekToken's type
821✔
124
        for _, t := range tokens {
1,642✔
125
                if p.peekToken.Type == t {
1,279✔
126
                        // if a match is found, return true
458✔
127
                        return true
458✔
128
                }
458✔
129
        }
130
        // if no match is found, return false
131
        return false
363✔
132
}
133

134
// Error returns an error if there are any errors in the Parser's errors slice
135
func (p *Parser) Error() error {
27✔
136
        // if there are no errors, return nil
27✔
137
        if len(p.errors) == 0 {
27✔
138
                return nil
×
139
        }
×
140
        // if there are errors, return the first error message in the errors slice as an error type
141
        return errors.New(p.errors[0])
27✔
142
}
143

144
// Parse reads and parses the input string and returns an AST representation of the schema, along with any errors encountered during parsing
145
func (p *Parser) Parse() (*ast.Schema, error) {
23✔
146
        // create a new Schema object to store the parsed statements
23✔
147
        schema := ast.NewSchema()
23✔
148
        schema.Statements = []ast.Statement{}
23✔
149

23✔
150
        // loop through the input string until the end is reached
23✔
151
        for !p.currentTokenIs(token.EOF) {
158✔
152
                // parse the next statement in the input string
135✔
153
                stmt, err := p.parseStatement()
135✔
154
                if err != nil {
145✔
155
                        // if there was an error parsing the statement, return the error message
10✔
156
                        return nil, p.Error()
10✔
157
                }
10✔
158
                if stmt != nil {
149✔
159
                        // add the parsed statement to the schema's Statements field if it is not nil
24✔
160
                        schema.Statements = append(schema.Statements, stmt)
24✔
161
                }
24✔
162

163
                // move to the next token in the input string
164
                p.next()
125✔
165
        }
166

167
        schema.SetReferences(p.references)
13✔
168

13✔
169
        // return the parsed schema object and nil to indicate that there were no errors
13✔
170
        return schema, nil
13✔
171
}
172

173
func (p *Parser) ParsePartial(entityName string) (ast.Statement, error) {
3✔
174
        for !p.currentTokenIs(token.EOF) {
15✔
175
                // parse the next statement in the input string
12✔
176
                stmt, err := p.parsePartialStatement(entityName)
12✔
177
                if err != nil {
12✔
178
                        return nil, p.Error()
×
179
                }
×
180
                if stmt != nil {
15✔
181
                        return stmt, nil
3✔
182
                }
3✔
183
                p.next()
9✔
184
        }
185
        return nil, errors.New("no valid statement found")
×
186
}
187

188
func (p *Parser) parsePartialStatement(entityName string) (ast.Statement, error) {
12✔
189
        switch p.currentToken.Type {
12✔
190
        case token.ATTRIBUTE:
×
191
                return p.parseAttributeStatement(entityName)
×
192
        case token.RELATION:
2✔
193
                return p.parseRelationStatement(entityName)
2✔
194
        case token.PERMISSION:
1✔
195
                return p.parsePermissionStatement(entityName)
1✔
196
        default:
9✔
197
                return nil, nil
9✔
198
        }
199
}
200

201
// parseStatement method parses the current statement based on its defined token types
202
func (p *Parser) parseStatement() (ast.Statement, error) {
135✔
203
        // switch on the currentToken's type to determine which type of statement to parse
135✔
204
        switch p.currentToken.Type {
135✔
205
        case token.ENTITY:
32✔
206
                // if the currentToken is ENTITY, parse an EntityStatement
32✔
207
                return p.parseEntityStatement()
32✔
208
        case token.RULE:
2✔
209
                // if the currentToken is RULE, parse a RuleStatement
2✔
210
                return p.parseRuleStatement()
2✔
211
        default:
101✔
212
                return nil, nil
101✔
213
        }
214
}
215

216
// parseEntityStatement method parses an ENTITY statement and returns an EntityStatement AST node
217
func (p *Parser) parseEntityStatement() (*ast.EntityStatement, error) {
32✔
218
        // create a new EntityStatement object and set its Entity field to the currentToken
32✔
219
        stmt := &ast.EntityStatement{Entity: p.currentToken}
32✔
220
        // expect the next token to be an identifier token, and set the EntityStatement's Name field to the identifier's value
32✔
221
        if !p.expectAndNext(token.IDENT) {
33✔
222
                return nil, p.Error()
1✔
223
        }
1✔
224
        stmt.Name = p.currentToken
31✔
225

31✔
226
        // add the entity reference to the Parser's entityReferences map
31✔
227
        err := p.references.AddEntityReference(stmt.Name.Literal)
31✔
228
        if err != nil {
32✔
229
                p.duplicationError(stmt.Name.Literal) // Generate an error message indicating a duplication error
1✔
230
                return nil, p.Error()
1✔
231
        }
1✔
232

233
        // expect the next token to be a left brace token, indicating the start of the entity's body
234
        if !p.expectAndNext(token.LCB) {
31✔
235
                return nil, p.Error()
1✔
236
        }
1✔
237

238
        // loop through the entity's body until a right brace token is encountered
239
        for !p.currentTokenIs(token.RCB) {
256✔
240
                // if the currentToken is EOF, raise an error and return nil for both the statement and error values
227✔
241
                if p.currentTokenIs(token.EOF) {
228✔
242
                        p.currentError(token.RCB)
1✔
243
                        return nil, p.Error()
1✔
244
                }
1✔
245
                // based on the currentToken's type, parse a RelationStatement or PermissionStatement and add it to the EntityStatement's corresponding field
246
                switch p.currentToken.Type {
226✔
247
                case token.RELATION:
36✔
248
                        relation, err := p.parseRelationStatement(stmt.Name.Literal)
36✔
249
                        if err != nil {
39✔
250
                                return nil, p.Error()
3✔
251
                        }
3✔
252
                        stmt.RelationStatements = append(stmt.RelationStatements, relation)
33✔
253
                case token.ATTRIBUTE:
4✔
254
                        attribute, err := p.parseAttributeStatement(stmt.Name.Literal)
4✔
255
                        if err != nil {
5✔
256
                                return nil, p.Error()
1✔
257
                        }
1✔
258
                        stmt.AttributeStatements = append(stmt.AttributeStatements, attribute)
3✔
259
                case token.PERMISSION:
24✔
260
                        action, err := p.parsePermissionStatement(stmt.Name.Literal)
24✔
261
                        if err != nil {
26✔
262
                                return nil, p.Error()
2✔
263
                        }
2✔
264
                        stmt.PermissionStatements = append(stmt.PermissionStatements, action)
22✔
265
                default:
162✔
266
                        // if the currentToken is not recognized, check if it is a newline, left brace, or right brace token, and skip it if it is
162✔
267
                        if !p.currentTokenIs(token.NEWLINE) && !p.currentTokenIs(token.LCB) && !p.currentTokenIs(token.RCB) {
162✔
268
                                // 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
×
269
                                p.currentError(token.RELATION, token.PERMISSION, token.ATTRIBUTE)
×
270
                                return nil, p.Error()
×
271
                        }
×
272
                }
273
                // move to the next token in the input string
274
                p.next()
220✔
275
        }
276

277
        // return the parsed EntityStatement and nil for the error value
278
        return stmt, nil
22✔
279
}
280

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

2✔
292
        // Expect the next token to be an identifier (the name of the rule).
2✔
293
        // If it's not an identifier, return an error.
2✔
294
        if !p.expectAndNext(token.IDENT) {
2✔
295
                return nil, p.Error()
×
296
        }
×
297
        stmt.Name = p.currentToken
2✔
298

2✔
299
        // Expect the next token to be a left parenthesis '(' starting the argument list.
2✔
300
        if !p.expectAndNext(token.LP) {
2✔
301
                return nil, p.Error()
×
302
        }
×
303

304
        arguments := map[token.Token]ast.AttributeTypeStatement{}
2✔
305
        args := map[string]string{}
2✔
306

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

4✔
317
                // Expect the second token to be the parameter's type.
4✔
318
                if !p.expectAndNext(token.IDENT) {
4✔
319
                        return nil, p.Error()
×
320
                }
×
321

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

340
                // If the next token is a comma, there are more parameters to parse.
341
                // Continue to the next iteration.
342
                if p.peekTokenIs(token.COMMA) {
6✔
343
                        p.next()
2✔
344
                        continue
2✔
345
                } else if !p.peekTokenIs(token.RP) {
2✔
346
                        // If the next token is not a comma, it must be a closing parenthesis.
×
347
                        // If it's not, return an error.
×
348
                        p.peekError(token.RP)
×
349
                        return nil, p.Error()
×
350
                }
×
351
        }
352

353
        // Save parsed arguments to the statement
354
        stmt.Arguments = arguments
2✔
355

2✔
356
        // Consume the right parenthesis.
2✔
357
        p.next()
2✔
358

2✔
359
        // Expect the next token to be a left curly bracket '{' starting the body.
2✔
360
        if !p.expectAndNext(token.LCB) {
2✔
361
                return nil, p.Error()
×
362
        }
×
363

364
        p.next()
2✔
365

2✔
366
        // Collect tokens for the body until a closing curly bracket '}' is encountered.
2✔
367
        var bodyTokens []token.Token
2✔
368
        for !p.peekTokenIs(token.RCB) {
32✔
369
                // If there's no closing bracket, return an error.
30✔
370
                if p.peekTokenIs(token.EOF) {
30✔
371
                        p.peekError(token.RCB)
×
372
                        return nil, p.Error()
×
373
                }
×
374

375
                bodyTokens = append(bodyTokens, p.currentToken)
30✔
376
                p.nextWithIgnores()
30✔
377
        }
378

379
        // Combine all the body tokens into a single string
380
        var bodyStr strings.Builder
2✔
381
        for _, t := range bodyTokens {
32✔
382
                bodyStr.WriteString(t.Literal)
30✔
383
        }
30✔
384
        stmt.Expression = bodyStr.String()
2✔
385

2✔
386
        // Expect and consume the closing curly bracket '}'.
2✔
387
        if !p.expectAndNext(token.RCB) {
2✔
388
                return nil, p.Error()
×
389
        }
×
390

391
        // Register the parsed rule in the parser's references.
392
        err := p.references.AddRuleReference(stmt.Name.Literal, args)
2✔
393
        if err != nil {
2✔
394
                // If there's an error (e.g., a duplicate rule), return an error.
×
395
                p.duplicationError(stmt.Name.Literal)
×
396
                return nil, p.Error()
×
397
        }
×
398

399
        // Return the successfully parsed RuleStatement.
400
        return stmt, nil
2✔
401
}
402

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

4✔
408
        // expect the next token to be an identifier token, and set the RelationStatement's Name field to the identifier's value
4✔
409
        if !p.expectAndNext(token.IDENT) {
5✔
410
                return nil, p.Error()
1✔
411
        }
1✔
412
        stmt.Name = p.currentToken
3✔
413

3✔
414
        if !p.expectAndNext(token.IDENT) {
3✔
415
                return nil, p.Error()
×
416
        }
×
417

418
        atstmt := ast.AttributeTypeStatement{Type: p.currentToken}
3✔
419
        atstmt.IsArray = false
3✔
420

3✔
421
        if p.peekTokenIs(token.LSB) {
4✔
422
                p.next()
1✔
423
                if !p.expectAndNext(token.RSB) {
1✔
424
                        return nil, p.Error()
×
425
                }
×
426
                atstmt.IsArray = true
1✔
427
        }
428

429
        stmt.AttributeType = atstmt
3✔
430

3✔
431
        key := utils.Key(entityName, stmt.Name.Literal)
3✔
432
        // add the relation reference to the Parser's relationReferences and relationalReferences maps
3✔
433
        err := p.references.AddAttributeReferences(key, atstmt)
3✔
434
        if err != nil {
3✔
435
                p.duplicationError(key) // Generate an error message indicating a duplication error
×
436
                return nil, p.Error()
×
437
        }
×
438

439
        // return the parsed RelationStatement and nil for the error value
440
        return stmt, nil
3✔
441
}
442

443
// parseRelationStatement method parses a RELATION statement and returns a RelationStatement AST node
444
func (p *Parser) parseRelationStatement(entityName string) (*ast.RelationStatement, error) {
38✔
445
        // create a new RelationStatement object and set its Relation field to the currentToken
38✔
446
        stmt := &ast.RelationStatement{Relation: p.currentToken}
38✔
447

38✔
448
        // expect the next token to be an identifier token, and set the RelationStatement's Name field to the identifier's value
38✔
449
        if !p.expectAndNext(token.IDENT) {
38✔
450
                return nil, p.Error()
×
451
        }
×
452
        stmt.Name = p.currentToken
38✔
453
        relationName := stmt.Name.Literal
38✔
454

38✔
455
        // expect the next token to be a SIGN token, indicating the start of the relation type(s)
38✔
456
        if !p.expect(token.SIGN) {
39✔
457
                return nil, p.Error()
1✔
458
        }
1✔
459

460
        // loop through the relation types until no more SIGN tokens are encountered
461
        for p.peekTokenIs(token.SIGN) {
77✔
462
                // parse a RelationTypeStatement and append it to the RelationStatement's RelationTypes field
40✔
463
                relationStatement, err := p.parseRelationTypeStatement()
40✔
464
                if err != nil {
41✔
465
                        return nil, p.Error()
1✔
466
                }
1✔
467
                stmt.RelationTypes = append(stmt.RelationTypes, *relationStatement)
39✔
468
        }
469

470
        key := utils.Key(entityName, relationName)
36✔
471

36✔
472
        // add the relation reference to the Parser's relationReferences and relationalReferences maps
36✔
473
        err := p.references.AddRelationReferences(key, stmt.RelationTypes)
36✔
474
        if err != nil {
37✔
475
                p.duplicationError(key) // Generate an error message indicating a duplication error
1✔
476
                return nil, p.Error()
1✔
477
        }
1✔
478

479
        // return the parsed RelationStatement and nil for the error value
480
        return stmt, nil
35✔
481
}
482

483
// parseRelationTypeStatement method parses a single relation type within a RELATION statement and returns a RelationTypeStatement AST node
484
func (p *Parser) parseRelationTypeStatement() (*ast.RelationTypeStatement, error) {
40✔
485
        // expect the currentToken to be a SIGN token, indicating the start of the relation type
40✔
486
        if !p.expectAndNext(token.SIGN) {
40✔
487
                return nil, p.Error()
×
488
        }
×
489
        // create a new RelationTypeStatement object and set its Sign field to the SIGN token
490
        stmt := &ast.RelationTypeStatement{Sign: p.currentToken}
40✔
491

40✔
492
        // expect the next token to be an identifier token, and set the RelationTypeStatement's Type field to the identifier's value
40✔
493
        if !p.expectAndNext(token.IDENT) {
41✔
494
                return nil, p.Error()
1✔
495
        }
1✔
496
        stmt.Type = p.currentToken
39✔
497

39✔
498
        // 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
39✔
499
        if p.peekTokenIs(token.HASH) {
42✔
500
                p.next()
3✔
501
                if !p.expectAndNext(token.IDENT) {
3✔
502
                        return nil, p.Error()
×
503
                }
×
504
                stmt.Relation = p.currentToken
3✔
505
        }
506

507
        // return the parsed RelationTypeStatement and nil for the error value
508
        return stmt, nil
39✔
509
}
510

511
// parsePermissionStatement method parses an PERMISSION statement and returns an PermissionStatement AST node
512
func (p *Parser) parsePermissionStatement(entityName string) (ast.Statement, error) {
25✔
513
        // create a new PermissionStatement object and set its Permission field to the currentToken
25✔
514
        stmt := &ast.PermissionStatement{Permission: p.currentToken}
25✔
515

25✔
516
        // expect the next token to be an identifier token, and set the PermissionStatement's Name field to the identifier's value
25✔
517
        if !p.expectAndNext(token.IDENT) {
26✔
518
                return nil, p.Error()
1✔
519
        }
1✔
520
        stmt.Name = p.currentToken
24✔
521

24✔
522
        key := utils.Key(entityName, stmt.Name.Literal)
24✔
523
        // add the action reference to the Parser's actionReferences and relationalReferences maps
24✔
524
        err := p.references.AddPermissionReference(key)
24✔
525
        if err != nil {
25✔
526
                p.duplicationError(key) // Generate an error message indicating a duplication error
1✔
527
                return nil, p.Error()
1✔
528
        }
1✔
529

530
        // expect the next token to be an ASSIGN token, indicating the start of the expression to be assigned to the action
531
        if !p.expectAndNext(token.ASSIGN) {
23✔
532
                return nil, p.Error()
×
533
        }
×
534

535
        p.next()
23✔
536

23✔
537
        // parse the expression statement and set it as the PermissionStatement's ExpressionStatement field
23✔
538
        ex, err := p.parseExpressionStatement()
23✔
539
        if err != nil {
23✔
540
                return nil, p.Error()
×
541
        }
×
542
        stmt.ExpressionStatement = ex
23✔
543

23✔
544
        // return the parsed PermissionStatement and nil for the error value
23✔
545
        return stmt, nil
23✔
546
}
547

548
// parseExpressionStatement method parses an expression statement and returns an ExpressionStatement AST node
549
func (p *Parser) parseExpressionStatement() (*ast.ExpressionStatement, error) {
23✔
550
        // create a new ExpressionStatement object
23✔
551
        stmt := &ast.ExpressionStatement{}
23✔
552
        var err error
23✔
553
        // parse the expression using the lowest precedence value as the initial precedence level
23✔
554
        stmt.Expression, err = p.parseExpression(LOWEST)
23✔
555
        if err != nil {
23✔
556
                return nil, p.Error()
×
557
        }
×
558

559
        // return the parsed ExpressionStatement and nil for the error value
560
        return stmt, nil
23✔
561
}
562

563
// 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.
564
func (p *Parser) expectAndNext(t token.Type) bool {
285✔
565
        // if the next token is of the expected type, advance the lexer to the next token and return true
285✔
566
        if p.peekTokenIs(t) {
565✔
567
                p.next()
280✔
568
                return true
280✔
569
        }
280✔
570
        // otherwise, generate an error message indicating that the expected token type was not found and return false
571
        p.peekError(t)
5✔
572
        return false
5✔
573
}
574

575
// 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.
576
func (p *Parser) expect(t token.Type) bool {
67✔
577
        // if the next token is of the expected type, return true
67✔
578
        if p.peekTokenIs(t) {
133✔
579
                return true
66✔
580
        }
66✔
581
        // otherwise, generate an error message indicating that the expected token type was not found and return false
582
        p.peekError(t)
1✔
583
        return false
1✔
584
}
585

586
// 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.
587
func (p *Parser) parseExpression(precedence int) (ast.Expression, error) {
88✔
588
        var exp ast.Expression
88✔
589
        var err error
88✔
590

88✔
591
        if p.currentTokenIs(token.LP) {
117✔
592
                p.next() // Consume the left parenthesis.
29✔
593
                exp, err = p.parseExpression(LOWEST)
29✔
594
                if err != nil {
29✔
595
                        return nil, err
×
596
                }
×
597

598
                if !p.expect(token.RP) {
29✔
599
                        return nil, p.Error()
×
600
                }
×
601
                p.next() // Consume the right parenthesis.
29✔
602
        } else {
59✔
603
                // get the prefix parsing function for the current token type
59✔
604
                prefix := p.prefixParseFns[p.currentToken.Type]
59✔
605
                if prefix == nil {
59✔
606
                        p.noPrefixParseFnError(p.currentToken.Type)
×
607
                        return nil, p.Error()
×
608
                }
×
609

610
                // parse the prefix expression
611
                exp, err = prefix()
59✔
612
                if err != nil {
59✔
613
                        return nil, p.Error()
×
614
                }
×
615
        }
616

617
        // continue parsing the expression while the next token has a higher precedence level than the current precedence level
618
        for !p.peekTokenIs(token.NEWLINE) && precedence < p.peekPrecedence() {
124✔
619
                // get the infix parsing function for the next token type
36✔
620
                infix := p.infixParseFunc[p.peekToken.Type]
36✔
621
                if infix == nil {
36✔
622
                        return exp, nil
×
623
                }
×
624
                p.next()
36✔
625
                // parse the infix expression with the current expression as its left-hand side
36✔
626
                exp, err = infix(exp)
36✔
627
                if err != nil {
36✔
628
                        return nil, p.Error()
×
629
                }
×
630
        }
631

632
        // return the parsed expression and nil for the error value
633
        return exp, nil
88✔
634
}
635

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

649
        // Create a new InfixExpression with the left operand and the current operator.
650
        expression := &ast.InfixExpression{
36✔
651
                Op:       p.currentToken,
36✔
652
                Left:     left,
36✔
653
                Operator: ast.Operator(p.currentToken.Literal),
36✔
654
        }
36✔
655

36✔
656
        // Get the precedence of the current operator and consume the operator token.
36✔
657
        precedence := p.currentPrecedence()
36✔
658
        p.next()
36✔
659

36✔
660
        // Parse the right operand with a higher precedence to construct the final expression tree.
36✔
661
        right, err := p.parseExpression(precedence)
36✔
662
        if err != nil {
36✔
663
                return nil, err
×
664
        }
×
665

666
        // Ensure the right operand is not nil.
667
        if right == nil {
36✔
668
                p.currentError(token.IDENT, token.LP) // Replace with your actual valid right operand token types
×
669
                return nil, p.Error()
×
670
        }
×
671

672
        // Set the right operand of the InfixExpression and return it.
673
        expression.Right = right
36✔
674
        return expression, nil
36✔
675
}
676

677
// parseIntegerLiteral parses an integer literal and returns the resulting IntegerLiteral expression.
678
func (p *Parser) isInfixOperator(tokenType token.Type) bool {
36✔
679
        return tokenType == token.AND || tokenType == token.OR || tokenType == token.NOT
36✔
680
}
36✔
681

682
// peekPrecedence returns the precedence of the next token in the input, if it is a known
683
// operator, or the lowest precedence otherwise.
684
func (p *Parser) peekPrecedence() int {
92✔
685
        if pr, ok := precedences[p.peekToken.Type]; ok {
129✔
686
                return pr
37✔
687
        }
37✔
688
        return LOWEST
55✔
689
}
690

691
// currentPrecedence returns the precedence of the current token in the input, if it is a known
692
// operator, or the lowest precedence otherwise.
693
func (p *Parser) currentPrecedence() int {
36✔
694
        if pr, ok := precedences[p.currentToken.Type]; ok {
72✔
695
                return pr
36✔
696
        }
36✔
697
        return LOWEST
×
698
}
699

700
func (p *Parser) parseIdentifierOrCall() (ast.Expression, error) {
59✔
701
        // Ensure the current token is a valid identifier before proceeding.
59✔
702
        if !p.currentTokenIs(token.IDENT) {
59✔
703
                return nil, fmt.Errorf("unexpected token type for identifier expression: %s", p.currentToken.Type)
×
704
        }
×
705

706
        if p.peekTokenIs(token.LP) {
61✔
707
                return p.parseCallExpression()
2✔
708
        }
2✔
709

710
        return p.parseIdentifierExpression()
57✔
711
}
712

713
// parseIdentifier parses an identifier expression that may consist of one or more dot-separated
714
// identifiers, such as "x", "foo.bar", or "a.b.c.d".
715
// It constructs a new Identifier expression with the first token as the prefix and subsequent
716
// tokens as identifiers, and returns the resulting expression and any error encountered.
717
func (p *Parser) parseIdentifierExpression() (ast.Expression, error) {
61✔
718
        // Ensure the current token is a valid identifier before proceeding.
61✔
719
        if !p.currentTokenIs(token.IDENT) {
61✔
720
                p.currentError(token.IDENT)
×
721
                return nil, p.Error()
×
722
        }
×
723

724
        // Create a new Identifier expression with the first token as the prefix.
725
        ident := &ast.Identifier{Idents: []token.Token{p.currentToken}}
61✔
726

61✔
727
        // If the next token is a dot, consume it and continue parsing the next identifier.
61✔
728
        for p.peekTokenIs(token.DOT) {
84✔
729
                p.next() // Consume the dot token
23✔
730

23✔
731
                // Check if the next token after the dot is a valid identifier
23✔
732
                if !p.expectAndNext(token.IDENT) {
23✔
733
                        return nil, p.Error()
×
734
                }
×
735

736
                ident.Idents = append(ident.Idents, p.currentToken)
23✔
737
        }
738

739
        // Return the resulting Identifier expression.
740
        return ident, nil
61✔
741
}
742

743
// call_func(variable1, variable2)
744
func (p *Parser) parseCallExpression() (ast.Expression, error) {
2✔
745
        // Ensure the current token is a valid identifier before proceeding.
2✔
746
        if !p.currentTokenIs(token.IDENT) {
2✔
747
                p.currentError(token.IDENT)
×
748
                return nil, p.Error()
×
749
        }
×
750

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

2✔
754
        if !p.expectAndNext(token.LP) {
2✔
755
                return nil, p.Error()
×
756
        }
×
757

758
        // Check if there are no arguments
759
        if p.peekTokenIs(token.RP) {
2✔
760
                p.next() // consume the RP token
×
761
                return call, nil
×
762
        }
×
763

764
        p.next()
2✔
765

2✔
766
        // Parse the first argument
2✔
767
        ident, err := p.parseIdentifierExpression()
2✔
768
        if err != nil {
2✔
769
                return nil, err
×
770
        }
×
771

772
        i, ok := ident.(*ast.Identifier)
2✔
773
        if !ok {
2✔
774
                return nil, fmt.Errorf("expected identifier, got %T", ident)
×
775
        }
×
776
        call.Arguments = append(call.Arguments, *i)
2✔
777

2✔
778
        // Parse remaining arguments
2✔
779
        for p.peekTokenIs(token.COMMA) {
4✔
780
                p.next()
2✔
781

2✔
782
                if !p.expectAndNext(token.IDENT) {
2✔
783
                        return nil, p.Error()
×
784
                }
×
785

786
                ident, err = p.parseIdentifierExpression()
2✔
787
                if err != nil {
2✔
788
                        return nil, err
×
789
                }
×
790

791
                i, ok = ident.(*ast.Identifier)
2✔
792
                if !ok {
2✔
793
                        return nil, fmt.Errorf("expected identifier, got %T", ident)
×
794
                }
×
795
                call.Arguments = append(call.Arguments, *i)
2✔
796
        }
797

798
        if !p.expectAndNext(token.RP) {
2✔
799
                return nil, p.Error()
×
800
        }
×
801

802
        // Return the resulting Identifier expression.
803
        return call, nil
2✔
804
}
805

806
// registerPrefix safely registers a parsing function for a prefix token type in the parser's prefixParseFns map.
807
// It takes a token type and a prefix parsing function as arguments, and stores the function in the map
808
// under the given token type key.
809
func (p *Parser) registerPrefix(tokenType token.Type, fn prefixParseFn) {
26✔
810
        if fn == nil {
26✔
811
                p.duplicationError(fmt.Sprintf("registerPrefix: nil function for token type %s", tokenType))
×
812
                return
×
813
        }
×
814

815
        if _, exists := p.prefixParseFns[tokenType]; exists {
26✔
816
                p.duplicationError(fmt.Sprintf("registerPrefix: token type %s already registered", tokenType))
×
817
                return
×
818
        }
×
819

820
        p.prefixParseFns[tokenType] = fn
26✔
821
}
822

823
// registerInfix safely registers a parsing function for an infix token type in the parser's infixParseFunc map.
824
// It takes a token type and an infix parsing function as arguments, and stores the function in the map
825
// under the given token type key.
826
func (p *Parser) registerInfix(tokenType token.Type, fn infixParseFn) {
78✔
827
        if fn == nil {
78✔
828
                p.duplicationError(fmt.Sprintf("registerInfix: nil function for token type %s", tokenType))
×
829
                return
×
830
        }
×
831

832
        if _, exists := p.infixParseFunc[tokenType]; exists {
78✔
833
                p.duplicationError(fmt.Sprintf("registerInfix: token type %s already registered", tokenType))
×
834
                return
×
835
        }
×
836

837
        p.infixParseFunc[tokenType] = fn
78✔
838
}
839

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

847
// noPrefixParseFnError adds an error message to the parser's error list indicating that no prefix parsing
848
// function was found for a given token type.
849
// It takes a token type as an argument that indicates the type of the token for which a parsing function is missing.
850
func (p *Parser) noPrefixParseFnError(t token.Type) {
×
851
        msg := fmt.Sprintf("%v:%v:no prefix parse function for %s found", p.l.GetLinePosition(), p.l.GetColumnPosition(), t)
×
852
        p.errors = append(p.errors, msg)
×
853
}
×
854

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

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

874
// tokenTypesToStrings converts a slice of token types to a slice of their string representations.
875
func tokenTypesToStrings(types []token.Type) []string {
7✔
876
        strs := make([]string, len(types))
7✔
877
        for i, t := range types {
14✔
878
                strs[i] = t.String()
7✔
879
        }
7✔
880
        return strs
7✔
881
}
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