Skip to content

Commit

Permalink
Merge pull request #1848 from xushiwei/q
Browse files Browse the repository at this point in the history
parser/printer: Static methods
  • Loading branch information
xushiwei committed Apr 9, 2024
2 parents fc2a773 + f810c74 commit c8e1889
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 10 deletions.
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 main() {
`)
}

func _TestMatrix1(t *testing.T) {
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

0 comments on commit c8e1889

Please sign in to comment.