Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions parser/_testdata/staticvalues/Rect.gox
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const global = 1
const .kind, Rect.name = "shape", "rect"

var (
.count int = 1
Rect.total, .extra int = 2, 3
width int
)
107 changes: 107 additions & 0 deletions parser/_testdata/staticvalues/parser.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package main

file Rect.gox
ast.GenDecl:
Tok: const
Specs:
ast.ValueSpec:
Names:
ast.Ident:
Name: global
Values:
ast.BasicLit:
Kind: INT
Value: 1
ast.GenDecl:
Tok: const
Specs:
ast.ValueSpec:
Names:
ast.Ident:
Name: .kind
ast.Ident:
Name: Rect.name
Values:
ast.BasicLit:
Kind: STRING
Value: "shape"
ast.BasicLit:
Kind: STRING
Value: "rect"
ast.GenDecl:
Tok: var
Specs:
ast.ValueSpec:
Names:
ast.Ident:
Name: .count
Type:
ast.Ident:
Name: int
Values:
ast.BasicLit:
Kind: INT
Value: 1
ast.ValueSpec:
Names:
ast.Ident:
Name: Rect.total
ast.Ident:
Name: .extra
Type:
ast.Ident:
Name: int
Values:
ast.BasicLit:
Kind: INT
Value: 2
ast.BasicLit:
Kind: INT
Value: 3
ast.ValueSpec:
Names:
ast.Ident:
Name: width
Type:
ast.Ident:
Name: int

file values.xgo
ast.GenDecl:
Tok: type
Specs:
ast.TypeSpec:
Name:
ast.Ident:
Name: Rect
Type:
ast.Ident:
Name: int
ast.GenDecl:
Tok: const
Specs:
ast.ValueSpec:
Names:
ast.Ident:
Name: Rect.kind
ast.Ident:
Name: global
Values:
ast.BasicLit:
Kind: STRING
Value: "shape"
ast.BasicLit:
Kind: STRING
Value: "global"
ast.GenDecl:
Tok: var
Specs:
ast.ValueSpec:
Names:
ast.Ident:
Name: Rect.count
ast.Ident:
Name: other
Type:
ast.Ident:
Name: int
5 changes: 5 additions & 0 deletions parser/_testdata/staticvalues/values.xgo
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type Rect int

const Rect.kind, global = "shape", "global"

var Rect.count, other int
196 changes: 101 additions & 95 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,6 @@ type parser struct {
syncPos token.Pos // last synchronization position
syncCnt int // number of parser.advance calls without progress

classVarDecl token.Pos // first top-level var declaration in a classfile

// Non-syntactic parser control
exprLev int // < 0: in control clause, >= 0: in expression
inRHS bool // if set, the parser is parsing a rhs expression
Expand Down Expand Up @@ -709,55 +707,47 @@ func (p *parser) parseIdent() *ast.Ident {
return &ast.Ident{NamePos: pos, Name: name}
}

func (p *parser) parseIdentEx() (*ast.Ident, bool) {
var posErr token.Pos
var ret string
if p.inClassFile() && p.tok == token.PERIOD {
posDot := p.pos
func (p *parser) parseValueName(allowDot bool) (*ast.Ident, bool) {
if allowDot && p.tok == token.PERIOD {
dot := p.pos
p.next()
name := p.parseIdent()
ret = "." + name.Name
if posDot+1 == name.NamePos {
return &ast.Ident{NamePos: posDot, Name: ret}, true
}
posErr = name.NamePos
} else {
id := p.parseIdent()
if p.tok != token.PERIOD {
return id, false
}
posDot := p.pos
p.next()
name := p.parseIdent()
ret = id.Name + "." + name.Name
if id.End() == posDot {
if posDot+1 == name.NamePos {
return &ast.Ident{NamePos: id.NamePos, Name: ret}, true
}
posErr = name.NamePos
} else {
posErr = posDot
}
p.checkStaticValueNameSpacing(token.NoPos, dot, name.Pos())
name.NamePos = dot
name.Name = "." + name.Name
return name, true
}
const msg = "whitespace is not allowed in static member name"
p.error(posErr, msg)
return &ast.Ident{NamePos: posErr, Name: ret}, true
name := p.parseIdent()
if p.tok != token.PERIOD {
return name, false
}
dot := p.pos
p.next()
sel := p.parseIdent()
p.checkStaticValueNameSpacing(name.End(), dot, sel.Pos())
name.Name += "." + sel.Name
return name, true
}

func (p *parser) parseIdentList() (list []*ast.Ident, hasStatic bool) {
if p.trace {
defer un(trace(p, "IdentList"))
func (p *parser) checkStaticValueNameSpacing(leftEnd, dot, right token.Pos) {
const msg = "whitespace is not allowed in static value name"
if leftEnd.IsValid() && leftEnd != dot {
p.error(dot, msg)
}
if right != dot+1 {
p.error(right, msg)
}
}

idFirst, hasStatic := p.parseIdentEx()
list = append(list, idFirst)
func (p *parser) parseValueNameList(allowDot bool) (list []*ast.Ident, hasStatic bool) {
name, static := p.parseValueName(allowDot)
list = append(list, name)
hasStatic = static
for p.tok == token.COMMA {
p.next()
idNext, isStatic := p.parseIdentEx()
if isStatic {
hasStatic = true
}
list = append(list, idNext)
name, static = p.parseValueName(allowDot)
list = append(list, name)
hasStatic = hasStatic || static
}
return
}
Expand Down Expand Up @@ -3991,69 +3981,92 @@ func (p *parser) inClassFile() bool {
return p.mode&ParseXGoClass != 0
}

func typeFromIdentEx(id *ast.Ident, isStatic bool) ast.Expr {
if isStatic {
pos := strings.IndexByte(id.Name, '.')
return &ast.SelectorExpr{
X: &ast.Ident{NamePos: id.NamePos, Name: id.Name[:pos]},
Sel: &ast.Ident{NamePos: id.NamePos + token.Pos(pos+1), Name: id.Name[pos+1:]},
}
}
return id
}

func (p *parser) parseValueSpec(doc *ast.CommentGroup, keyword token.Token, iota int) ast.Spec {
if p.trace {
defer un(trace(p, keyword.String()+"Spec"))
func (p *parser) parseClassValueSpec() (idents []*ast.Ident, typ ast.Expr, tag *ast.BasicLit, values []ast.Expr, hasStatic bool) {
var starPos token.Pos
if p.tok == token.MUL {
starPos = p.pos
p.next()
}

pos := p.pos
var idents []*ast.Ident
var hasStatic bool
var typ ast.Expr
var tag *ast.BasicLit
var values []ast.Expr
if p.inClassFile() && p.topScope == p.pkgScope && keyword == token.VAR {
var idFirst *ast.Ident
var starPos token.Pos
if p.tok == token.MUL {
starPos = p.pos
if p.tok == token.PERIOD && starPos == token.NoPos {
idents, hasStatic = p.parseValueNameList(true)
typ = p.tryType()
if p.tok == token.ASSIGN {
p.next()
values = p.parseRHSList()
}
idFirst, hasStatic = p.parseIdentEx()
if starPos != token.NoPos {
typ = &ast.StarExpr{
Star: starPos,
X: typeFromIdentEx(idFirst, hasStatic),
} else {
ident := p.parseIdent()
if p.tok == token.PERIOD {
dot := p.pos
p.next()
selOK := p.tok == token.IDENT
sel := p.parseIdent()
if selOK && starPos == token.NoPos &&
p.tok != token.SEMICOLON && p.tok != token.STRING && p.tok != token.RPAREN {
p.checkStaticValueNameSpacing(ident.End(), dot, sel.Pos())
ident.Name += "." + sel.Name
idents = append(idents, ident)
hasStatic = true
for p.tok == token.COMMA {
p.next()
name, static := p.parseValueName(true)
idents = append(idents, name)
hasStatic = hasStatic || static
}
typ = p.tryType()
if p.tok == token.ASSIGN {
p.next()
values = p.parseRHSList()
}
} else {
typ = &ast.SelectorExpr{X: ident, Sel: sel}
if starPos != token.NoPos {
typ = &ast.StarExpr{Star: starPos, X: typ}
}
}
hasStatic = false
} else if starPos != token.NoPos {
typ = &ast.StarExpr{Star: starPos, X: ident}
} else {
idents = append(idents, idFirst)
idents = append(idents, ident)
for p.tok == token.COMMA {
p.next()
idNext, isStatic := p.parseIdentEx()
if isStatic {
hasStatic = true
}
idents = append(idents, idNext)
name, static := p.parseValueName(true)
idents = append(idents, name)
hasStatic = hasStatic || static
}
typ = p.tryType()
if p.tok == token.ASSIGN {
p.next()
values = p.parseRHSList()
} else if len(idents) == 1 && typ == nil {
typ = typeFromIdentEx(idFirst, hasStatic)
hasStatic = false
typ = ident
idents = nil
}
}
if p.tok == token.STRING {
tag = &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit}
p.next()
}
p.expect(token.SEMICOLON)
}
if p.tok == token.STRING {
tag = &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit}
p.next()
}
p.expect(token.SEMICOLON)
return
}

func (p *parser) parseValueSpec(doc *ast.CommentGroup, keyword token.Token, iota int) ast.Spec {
if p.trace {
defer un(trace(p, keyword.String()+"Spec"))
}

pos := p.pos
var idents []*ast.Ident
var typ ast.Expr
var tag *ast.BasicLit
var values []ast.Expr
var hasStatic bool
if p.inClassFile() && p.topScope == p.pkgScope && keyword == token.VAR {
idents, typ, tag, values, hasStatic = p.parseClassValueSpec()
} else {
idents, hasStatic = p.parseIdentList()
idents, hasStatic = p.parseValueNameList(p.inClassFile())
typ = p.tryType()
// always permit optional initialization for more tolerant parsing
if p.tok == token.ASSIGN {
Expand Down Expand Up @@ -4160,13 +4173,6 @@ func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.Gen
}
doc := p.leadComment
pos := p.expect(keyword)
if keyword == token.VAR && p.inClassFile() && p.topScope == p.pkgScope {
if p.classVarDecl.IsValid() {
p.error(pos, "multiple top-level var declarations in classfile")
} else {
p.classVarDecl = pos
}
}
var lparen, rparen token.Pos
var list []ast.Spec
if p.tok == token.LPAREN {
Expand Down
Loading
Loading