-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathidentifier.go
More file actions
116 lines (103 loc) · 3.32 KB
/
Copy pathidentifier.go
File metadata and controls
116 lines (103 loc) · 3.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package parser
import (
"slices"
"strings"
"unicode"
"github.com/ProCode-Software/klar/internal/ast"
"github.com/ProCode-Software/klar/internal/klarerrs"
"github.com/ProCode-Software/klar/internal/lexer"
)
// isValidIdentifier reports whether tok's Kind is [lexer.Identifier].
func isValidIdentifier(t lexer.TokenType) bool {
return t == lexer.Identifier
}
func isValidIdentOrDiscard(t lexer.TokenType) bool {
return isValidIdentifier(t) || t == lexer.Underscore
}
// validateIdentifier reports whether tok is a valid identifier. If it is false,
// validateIdentifier reports an error to the parser.
func (p *Parser) validateIdentifier(tok lexer.Token) bool {
if isValidIdentifier(tok.Kind) {
return true
}
switch {
case tok.Kind == lexer.Underscore:
p.Error(klarerrs.Token(klarerrs.ErrUnderscoreValue, tok))
case slices.Contains(lexer.ReservedIdent, tok.Kind):
p.Error(klarerrs.Token(klarerrs.ErrReservedKeyword, tok))
default:
p.Error(klarerrs.ExpectedToken(lexer.Identifier, tok))
}
return false
}
func (p *Parser) newIdentifier(t lexer.Token) ast.Identifier {
ident := ast.Identifier{Name: t.Source, Position: t.Position, Len: t.Len()}
p.ValidateIdentName(ident.Name, ident)
return ident
}
// ValidateIdentName reports an error if i's Name does not contain any letters.
// Examples of invalid identifiers: '_123', '__'
func (p *Parser) ValidateIdentName(s string, node ast.Node) {
var label string
switch {
case s == "_", strings.ContainsFunc(s, unicode.IsLetter):
return // No error
case strings.Trim(s, "_") == "":
// Allow discards. '___' is not allowed.
label = "This name has only underscores"
default:
// '_123', '_1_2', only digits (in any language) and underscores
label = "Identifiers should contain at least 1 letter"
}
err := klarerrs.Node(klarerrs.ErrIdentMustHaveLetter, node)
err.Name = s
err.Label = label
p.Error(err)
}
func (p *Parser) ParseIdentifier() ast.Identifier {
tok := p.AdvanceNonBoundary()
p.validateIdentifier(tok)
return p.newIdentifier(tok)
}
func (p *Parser) ParseStrictIdentifier() ast.Identifier {
tok := p.Expect(lexer.Identifier)
return p.newIdentifier(tok)
}
// [*Parser.ParseIdentifier] but will not validate. Expected use case if for already
// validated identifiers. [lexer.Underscore] is allowed (because any token is allowed)
func (p *Parser) ParseValidIdent() ast.Identifier {
tok := p.AdvanceNonBoundary()
return p.newIdentifier(tok)
}
func (p *Parser) ParseIdentOrDiscard() ast.Identifier {
tok := p.AdvanceNonBoundary()
if tok.Kind != lexer.Underscore {
p.validateIdentifier(tok)
}
return p.newIdentifier(tok)
}
// opt1: includingNumber, opt2: isLabel (for a better error)
func (p *Parser) ParseMapIdentifier(opts parseFlags) ast.Identifier {
tok := p.AdvanceNonBoundary()
kind := tok.Kind
switch {
case kind == lexer.Identifier:
break
case kind == lexer.Numeric && opts&allowNumber == 0:
if opts&isLabel != 0 {
p.Error(klarerrs.Token(klarerrs.ErrNumericLabel, tok))
break
}
fallthrough
case !slices.Contains(ast.Modifiers, tok.Kind) &&
!slices.Contains(lexer.ReservedIdent, tok.Kind):
p.Error(klarerrs.ExpectedToken(lexer.Identifier, tok))
}
return p.newIdentifier(tok)
}
func (p *Parser) ParseMapIdentOrDiscard(opts parseFlags) ast.Identifier {
if p.CurrKind() == lexer.Underscore {
return p.ParseValidIdent()
}
return p.ParseMapIdentifier(opts)
}