Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

parser/printer: Static methods #1848

Merged
merged 3 commits into from
Apr 9, 2024
Merged
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
1 change: 1 addition & 0 deletions ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -1055,6 +1055,7 @@ type (
Operator bool // is operator or not
Shadow bool // is a shadow entry
IsClass bool // recv set by class
Static bool // recv is static (class method)
}
)

Expand Down
10 changes: 10 additions & 0 deletions cl/compile_spx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,16 @@
`)
}

func _TestMatrix1(t *testing.T) {

Check warning on line 308 in cl/compile_spx_test.go

View check run for this annotation

qiniu-x / golangci-lint

cl/compile_spx_test.go#L308

func `_TestMatrix1` is unused (unused)

Check warning on line 308 in cl/compile_spx_test.go

View check run for this annotation

qiniu-x / staticcheck

cl/compile_spx_test.go#L308

func _TestMatrix1 is unused (U1000)
gopSpxTest(t, `
echo [
1, 2, 3
4, 5, 6
]
`, ``, `
`)
}

func TestEnvOp(t *testing.T) {
gopSpxTest(t, `
echo ${PATH}, $id
Expand Down
30 changes: 30 additions & 0 deletions cl/expr.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,34 @@ find:
return
}

/*
func compileMatrixLit(ctx *blockCtx, v *ast.MatrixLit) {
cb := ctx.cb
ncol := -1
for _, elts := range v.Elts {
switch n := len(elts); n {
case 1:
elt := elts[0]
if e, ok := elt.(*ast.Ellipsis); ok {
compileExpr(ctx, e.Elt)
panic("TODO") // TODO(xsw): matrixLit with ellipsis
}
fallthrough
default:
if ncol < 0 {
ncol = n
} else if ncol != n {
ctx.handleErrorf(elts[0].Pos(), "inconsistent matrix column count: got %v, want %v", n, ncol)
}
for _, elt := range elts {
compileExpr(ctx, elt)
}
cb.SliceLitEx(...)
}
}
}
*/

func compileEnvExpr(ctx *blockCtx, v *ast.EnvExpr) {
cb := ctx.cb
if ctx.isClass { // in a Go+ class file
Expand Down Expand Up @@ -363,6 +391,8 @@ func compileExpr(ctx *blockCtx, expr ast.Expr, inFlags ...int) {
ctx.cb.Typ(toFuncType(ctx, v, nil, nil), v)
case *ast.EnvExpr:
compileEnvExpr(ctx, v)
/* case *ast.MatrixLit:
compileMatrixLit(ctx, v) */
default:
panic(ctx.newCodeErrorf(v.Pos(), "compileExpr failed: unknown - %T", v))
}
Expand Down
37 changes: 37 additions & 0 deletions parser/_testdata/staticmthd1/parser.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package main

file static_method.gop
ast.FuncDecl:
Recv:
ast.FieldList:
List:
ast.Field:
Type:
ast.Ident:
Name: T
Name:
ast.Ident:
Name: foo
Type:
ast.FuncType:
Params:
ast.FieldList:
List:
ast.Field:
Names:
ast.Ident:
Name: a
ast.Ident:
Name: b
Type:
ast.Ident:
Name: int
Results:
ast.FieldList:
List:
ast.Field:
Type:
ast.Ident:
Name: string
Body:
ast.BlockStmt:
2 changes: 2 additions & 0 deletions parser/_testdata/staticmthd1/static_method.gop
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
func T.foo(a, b int) string {
}
31 changes: 23 additions & 8 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -3648,7 +3648,7 @@ func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.Gen
}
}

func isOverloadOps(tok token.Token) bool {
func isOverloadOp(tok token.Token) bool {
return int(tok) < len(overloadOps) && overloadOps[tok] != 0
}

Expand Down Expand Up @@ -3705,6 +3705,8 @@ func (p *parser) parseOverloadDecl(decl *ast.OverloadFuncDecl) *ast.OverloadFunc
// `func identOrOp(params) results {...}`
// `func identOrOp = (overloadFuncs)`
//
// `func T.ident(params) results { ... }`
//
// `func (recv) identOrOp(params) results { ... }`
// `func (T).identOrOp = (overloadFuncs)`
// `func (params) results { ... }()`
Expand All @@ -3719,20 +3721,30 @@ func (p *parser) parseFuncDeclOrCall() (ast.Decl, *ast.CallExpr) {

var recv, params, results *ast.FieldList
var ident *ast.Ident
var isOp, isFunLit, ok bool
var isOp, isStatic, isFunLit, ok bool

if p.tok != token.LPAREN {
// func: `func identOrOp(...) results`
// overload: `func identOrOp = (overloadFuncs)`
// static method: `func T.ident(...) results`
ident, isOp = p.parseIdentOrOp()
if p.tok == token.ASSIGN {
switch p.tok {
case token.ASSIGN:
// func identOrOp = (overloadFuncs)
return p.parseOverloadDecl(&ast.OverloadFuncDecl{
Doc: doc,
Func: pos,
Name: ident,
Operator: isOp,
}), nil
case token.PERIOD:
// func T.ident(...) results
if !isOp {
p.next()
recv = &ast.FieldList{List: []*ast.Field{{Type: ident}}}
ident = p.parseIdent()
isStatic = true
}
}
params, results = p.parseSignature(scope)
} else {
Expand All @@ -3754,19 +3766,21 @@ func (p *parser) parseFuncDeclOrCall() (ast.Decl, *ast.CallExpr) {
Name: ident,
Operator: isOp,
}), nil
} else if isOp = isOverloadOps(p.tok); isOp {
oldtok, oldpos := p.tok, p.pos
} else if isOp = isOverloadOp(p.tok); isOp {
oldtok, oldpos, oldlit := p.tok, p.pos, p.lit
p.next()
if p.tok == token.LPAREN {
// func (recv) op(params) results { ... }
recv, ident = params, &ast.Ident{NamePos: oldpos, Name: oldtok.String()}
params, results = p.parseSignature(scope)
} else {
// func (params) typ { ... }()
p.unget(oldpos, oldtok, "")
p.unget(oldpos, oldtok, oldlit)
typ := p.tryType()
if typ == nil {
panic("TODO: invalid result type")
p.errorExpected(oldpos, "type", 1)
p.next()
typ = &ast.BadExpr{From: oldpos, To: p.pos}
}
isFunLit, results = true, &ast.FieldList{List: []*ast.Field{{Type: typ}}}
}
Expand Down Expand Up @@ -3799,7 +3813,7 @@ func (p *parser) parseFuncDeclOrCall() (ast.Decl, *ast.CallExpr) {

if isOp {
if params == nil || len(params.List) != 1 {
log.Panicln("TODO: overload operator can only have one parameter")
p.error(ident.Pos(), "overload operator can only have one parameter")
}
}
var body *ast.BlockStmt
Expand Down Expand Up @@ -3829,6 +3843,7 @@ func (p *parser) parseFuncDeclOrCall() (ast.Decl, *ast.CallExpr) {
},
Body: body,
Operator: isOp,
Static: isStatic,
}
if recv == nil {
// Go spec: The scope of an identifier denoting a constant, type,
Expand Down
14 changes: 14 additions & 0 deletions parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,4 +325,18 @@ func TestCheckExpr(t *testing.T) {
p.checkExpr(&ast.FuncLit{})
}

func TestErrFuncDecl(t *testing.T) {
testErrCode(t, `func test()
{
}
`, `/foo/bar.gop:2:1: unexpected semicolon or newline before {`, ``)
testErrCode(t, `func test() +1
`, `/foo/bar.gop:1:13: expected ';', found '+'`, ``)
testErrCode(t, `
func (a T) +{}
`, `/foo/bar.gop:2:12: expected type, found '+'`, ``)
testErrCode(t, `func +(a T, b T) {}
`, `/foo/bar.gop:1:6: overload operator can only have one parameter`, ``)
}

// -----------------------------------------------------------------------------
9 changes: 7 additions & 2 deletions printer/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -2016,8 +2016,13 @@ func (p *printer) funcDecl(d *ast.FuncDecl) {
// FUNC is emitted).
startCol := p.out.Column - len("func ")
if d.Recv != nil && !d.IsClass {
p.parameters(d.Recv) // method: print receiver
p.print(blank)
if d.Static { // static method
p.expr(d.Recv.List[0].Type)
p.print(token.PERIOD)
} else {
p.parameters(d.Recv) // method: print receiver
p.print(blank)
}
}
p.expr(d.Name)
if d.Operator && d.Recv != nil {
Expand Down
Loading