Repository: goplus/gop
Pull request #1631 (2024-01-15 03:56:13)

Overload


Patch:
diff --git a/ast/ast_gop.go b/ast/ast_gop.go
index b0c398b6..42d5fda8 100644
--- a/ast/ast_gop.go
+++ b/ast/ast_gop.go
@@ -22,6 +22,43 @@ import (
 
 // -----------------------------------------------------------------------------
 
+// OverloadFuncDecl node represents an overload function declaration:
+//
+// `func name = (overloadFuncs)`
+// `func (T).nameOrOp = (overloadFuncs)`
+//
+// here overloadFunc represents
+//
+// `func(params) {...}`
+// `funcName`
+// `(*T).methodName`
+type OverloadFuncDecl struct {
+	Doc      *CommentGroup // associated documentation; or nil
+	Func     token.Pos     // position of "func" keyword
+	Recv     *FieldList    // receiver (methods); or nil (functions)
+	Name     *Ident        // function/method name
+	Assign   token.Pos     // position of token "="
+	Lparen   token.Pos     // position of "("
+	Funcs    []Expr        // overload functions. here `Expr` can be *FuncLit, *Ident or *SelectorExpr
+	Rparen   token.Pos     // position of ")"
+	Operator bool          // is operator or not
+	IsClass  bool          // recv set by class
+}
+
+// Pos - position of first character belonging to the node.
+func (p *OverloadFuncDecl) Pos() token.Pos {
+	return p.Func
+}
+
+// End - position of first character immediately after the node.
+func (p *OverloadFuncDecl) End() token.Pos {
+	return p.Rparen + 1
+}
+
+func (*OverloadFuncDecl) declNode() {}
+
+// -----------------------------------------------------------------------------
+
 // A SliceLit node represents a slice literal.
 type SliceLit struct {
 	Lbrack     token.Pos // position of "["
@@ -30,12 +67,12 @@ type SliceLit struct {
 	Incomplete bool      // true if (source) expressions are missing in the Elts list
 }
 
-// Pos - position of first character belonging to the node
+// Pos - position of first character belonging to the node.
 func (p *SliceLit) Pos() token.Pos {
 	return p.Lbrack
 }
 
-// End - position of first character immediately after the node
+// End - position of first character immediately after the node.
 func (p *SliceLit) End() token.Pos {
 	return p.Rbrack + 1
 }
@@ -44,7 +81,7 @@ func (*SliceLit) exprNode() {}
 
 // -----------------------------------------------------------------------------
 
-// ErrWrapExpr represents `expr!`, `expr?` or `expr?: defaultValue`
+// ErrWrapExpr represents `expr!`, `expr?` or `expr?: defaultValue`.
 type ErrWrapExpr struct {
 	X       Expr
 	Tok     token.Token // ! or ?
@@ -52,12 +89,12 @@ type ErrWrapExpr struct {
 	Default Expr // can be nil
 }
 
-// Pos - position of first character belonging to the node
+// Pos - position of first character belonging to the node.
 func (p *ErrWrapExpr) Pos() token.Pos {
 	return p.X.Pos()
 }
 
-// End - position of first character immediately after the node
+// End - position of first character immediately after the node.
 func (p *ErrWrapExpr) End() token.Pos {
 	if p.Default != nil {
 		return p.Default.End()
@@ -69,13 +106,16 @@ func (*ErrWrapExpr) exprNode() {}
 
 // -----------------------------------------------------------------------------
 
-// LambdaExpr represents
-//    `(x, y, ...) => exprOrExprTuple`
-//    `x => exprOrExprTuple`
-//    `=> exprOrExprTuple`
+// LambdaExpr represents one of the following expressions:
+//
+//	`(x, y, ...) => exprOrExprTuple`
+//	`x => exprOrExprTuple`
+//	`=> exprOrExprTuple`
+//
 // here exprOrExprTuple represents
-//    `expr`
-//    `(expr1, expr2, ...)`
+//
+//	`expr`
+//	`(expr1, expr2, ...)`
 type LambdaExpr struct {
 	First       token.Pos
 	Lhs         []*Ident
@@ -86,10 +126,11 @@ type LambdaExpr struct {
 	RhsHasParen bool
 }
 
-// LambdaExpr2 represents
-//    `(x, y, ...) => { ... }`
-//    `x => { ... }`
-//    `=> { ... }`
+// LambdaExpr2 represents one of the following expressions:
+//
+//	`(x, y, ...) => { ... }`
+//	`x => { ... }`
+//	`=> { ... }`
 type LambdaExpr2 struct {
 	First       token.Pos
 	Lhs         []*Ident
@@ -98,18 +139,22 @@ type LambdaExpr2 struct {
 	LhsHasParen bool
 }
 
+// Pos - position of first character belonging to the node.
 func (p *LambdaExpr) Pos() token.Pos {
 	return p.First
 }
 
+// End - position of first character immediately after the node.
 func (p *LambdaExpr) End() token.Pos {
 	return p.Last
 }
 
+// Pos - position of first character belonging to the node.
 func (p *LambdaExpr2) Pos() token.Pos {
 	return p.First
 }
 
+// End - position of first character immediately after the node.
 func (p *LambdaExpr2) End() token.Pos {
 	return p.Body.End()
 }
@@ -119,7 +164,7 @@ func (*LambdaExpr2) exprNode() {}
 
 // -----------------------------------------------------------------------------
 
-// ForPhrase represents `for k, v <- container, cond`
+// ForPhrase represents `for k, v <- container, cond` phrase.
 type ForPhrase struct {
 	For        token.Pos // position of "for" keyword
 	Key, Value *Ident    // Key may be nil
@@ -138,11 +183,12 @@ func (p *ForPhrase) End() token.Pos { return p.X.End() }
 
 func (p *ForPhrase) exprNode() {}
 
-// ComprehensionExpr represents
-//    `[vexpr for k1, v1 <- container1, cond1 ...]` or
-//    `{vexpr for k1, v1 <- container1, cond1 ...}` or
-//    `{kexpr: vexpr for k1, v1 <- container1, cond1 ...}` or
-//    `{for k1, v1 <- container1, cond1 ...}` or
+// ComprehensionExpr represents one of the following expressions:
+//
+//	`[vexpr for k1, v1 <- container1, cond1 ...]` or
+//	`{vexpr for k1, v1 <- container1, cond1 ...}` or
+//	`{kexpr: vexpr for k1, v1 <- container1, cond1 ...}` or
+//	`{for k1, v1 <- container1, cond1 ...}` or
 type ComprehensionExpr struct {
 	Lpos token.Pos   // position of "[" or "{"
 	Tok  token.Token // token.LBRACK '[' or token.LBRACE '{'
@@ -151,12 +197,12 @@ type ComprehensionExpr struct {
 	Rpos token.Pos // position of "]" or "}"
 }
 
-// Pos - position of first character belonging to the node
+// Pos - position of first character belonging to the node.
 func (p *ComprehensionExpr) Pos() token.Pos {
 	return p.Lpos
 }
 
-// End - position of first character immediately after the node
+// End - position of first character immediately after the node.
 func (p *ComprehensionExpr) End() token.Pos {
 	return p.Rpos + 1
 }
@@ -171,12 +217,12 @@ type ForPhraseStmt struct {
 	Body *BlockStmt
 }
 
-// Pos - position of first character belonging to the node
+// Pos - position of first character belonging to the node.
 func (p *ForPhraseStmt) Pos() token.Pos {
 	return p.For
 }
 
-// End - position of first character immediately after the node
+// End - position of first character immediately after the node.
 func (p *ForPhraseStmt) End() token.Pos {
 	return p.Body.End()
 }
@@ -194,7 +240,7 @@ type RangeExpr struct {
 	Expr3  Expr      // step (or max) of composite elements; or nil
 }
 
-// Pos - position of first character belonging to the node
+// Pos - position of first character belonging to the node.
 func (p *RangeExpr) Pos() token.Pos {
 	if p.First != nil {
 		return p.First.Pos()
@@ -202,7 +248,7 @@ func (p *RangeExpr) Pos() token.Pos {
 	return p.To
 }
 
-// End - position of first character immediately after the node
+// End - position of first character immediately after the node.
 func (p *RangeExpr) End() token.Pos {
 	if p.Expr3 != nil {
 		return p.Expr3.End()
diff --git a/parser/parser.go b/parser/parser.go
index ef1d8a64..a3faf9a7 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -3486,9 +3486,65 @@ func isOverloadOps(tok token.Token) bool {
 	return int(tok) < len(overloadOps) && overloadOps[tok] != 0
 }
 
-func (p *parser) parseFuncDeclOrCall() (*ast.FuncDecl, *ast.CallExpr) {
+// `funcName`
+// `(*T).methodName`
+// `func(params) results {...}`
+func (p *parser) parseOverloadFunc() (ast.Expr, bool) {
+	switch p.tok {
+	case token.IDENT:
+		return p.parseIdent(), true
+	case token.FUNC:
+		return p.parseFuncTypeOrLit(), true
+	case token.LPAREN:
+		x, _ := p.parsePrimaryExpr(false, false, false)
+		return x, true
+	}
+	return nil, false
+}
+
+// `= (overloadFuncs)`
+//
+// here overloadFunc represents
+//
+// `funcName`
+// `(*T).methodName`
+// `func(params) results {...}`
+func (p *parser) parseOverloadDecl(decl *ast.OverloadFuncDecl) *ast.OverloadFuncDecl {
+	decl.Assign = p.expect(token.ASSIGN)
+	decl.Lparen = p.expect(token.LPAREN)
+	funcs := make([]ast.Expr, 0, 4)
+	for {
+		f, ok := p.parseOverloadFunc()
+		if !ok {
+			break
+		}
+		funcs = append(funcs, f)
+		if p.tok == token.SEMICOLON {
+			p.next()
+		}
+	}
+	decl.Funcs = funcs
+	decl.Rparen = p.expect(token.RPAREN)
+	p.expectSemi()
+	if debugParseOutput {
+		var recvt ast.Expr
+		if recv := decl.Recv; recv != nil {
+			recvt = recv.List[0].Type
+		}
+		log.Printf("ast.OverloadFuncDecl{Recv: %v, Name: %v, ...}\n", recvt, decl.Name)
+	}
+	return decl
+}
+
+// `func identOrOp(params) results {...}`
+// `func identOrOp = (overloadFuncs)`
+//
+// `func (recv) identOrOp(params) results { ... }`
+// `func (T).identOrOp = (overloadFuncs)`
+// `func (params) results { ... }()`
+func (p *parser) parseFuncDeclOrCall() (ast.Decl, *ast.CallExpr) {
 	if p.trace {
-		defer un(trace(p, "FunctionDecl"))
+		defer un(trace(p, "FunctionDeclOrCall"))
 	}
 
 	doc := p.leadComment
@@ -3499,16 +3555,39 @@ func (p *parser) parseFuncDeclOrCall() (*ast.FuncDecl, *ast.CallExpr) {
 	var ident *ast.Ident
 	var isOp, isFunLit, ok bool
 
-	if p.tok != token.LPAREN { // func identOrOp(...)
+	if p.tok != token.LPAREN {
+		// func: `func identOrOp(...) results`
+		// overload: `func identOrOp = (overloadFuncs)`
 		ident, isOp = p.parseIdentOrOp()
+		if p.tok == token.ASSIGN {
+			// func identOrOp = (overloadFuncs)
+			return p.parseOverloadDecl(&ast.OverloadFuncDecl{
+				Doc:      doc,
+				Func:     pos,
+				Name:     ident,
+				Operator: isOp,
+			}), nil
+		}
 		params, results = p.parseSignature(scope)
 	} else {
-		// method: func (recv) XXX(params) results { ... }
-		// funlit: func (params) results { ... }()
+		// method: `func (recv) identOrOp(params) results { ... }`
+		// overload: `func (T).identOrOp = (overloadFuncs)`
+		// funlit: `func (params) results { ... }()`
 		params = p.parseParameters(scope, true)
 		if p.tok == token.LPAREN {
 			// func (params) (results) { ... }()
 			isFunLit, results = true, p.parseParameters(scope, false)
+		} else if p.tok == token.PERIOD {
+			p.next()
+			// func (T).identOrOp = (overloadFuncs)
+			ident, isOp = p.parseIdentOrOp()
+			return p.parseOverloadDecl(&ast.OverloadFuncDecl{
+				Doc:      doc,
+				Func:     pos,
+				Recv:     params,
+				Name:     ident,
+				Operator: isOp,
+			}), nil
 		} else if isOp = isOverloadOps(p.tok); isOp {
 			oldtok, oldpos := p.tok, p.pos
 			p.next()
diff --git a/printer/nodes.go b/printer/nodes.go
index 0d732502..b4b0851b 100644
--- a/printer/nodes.go
+++ b/printer/nodes.go
@@ -1992,6 +1992,28 @@ func (p *printer) funcDecl(d *ast.FuncDecl) {
 	p.funcBody(p.distanceFrom(d.Pos(), startCol), vtab, d.Body)
 }
 
+func (p *printer) overloadFuncDecl(d *ast.OverloadFuncDecl) {
+	if debugFormat {
+		log.Println("==> Format OverloadFunc", d.Name.Name)
+	}
+	p.setComment(d.Doc)
+
+	pos := d.Pos()
+	p.print(pos, token.FUNC, blank)
+	if d.Recv != nil && !d.IsClass {
+		p.parameters(d.Recv) // method: print receiver
+		p.print(token.PERIOD)
+	}
+	p.expr(d.Name)
+	p.print(blank, token.ASSIGN, blank, token.LPAREN, newline)
+	for _, fn := range d.Funcs {
+		p.print(indent)
+		p.expr1(fn, token.LowestPrec, 1)
+		p.print(unindent, newline)
+	}
+	p.print(token.RPAREN)
+}
+
 func (p *printer) decl(decl ast.Decl) {
 	switch d := decl.(type) {
 	case *ast.BadDecl:
@@ -2000,6 +2022,8 @@ func (p *printer) decl(decl ast.Decl) {
 		p.genDecl(d)
 	case *ast.FuncDecl:
 		p.funcDecl(d)
+	case *ast.OverloadFuncDecl:
+		p.overloadFuncDecl(d)
 	default:
 		panic("unreachable")
 	}


Test patch:
diff --git a/parser/_testdata/overload1/overload.gop b/parser/_testdata/overload1/overload.gop
new file mode 100644
index 00000000..d5b458bf
--- /dev/null
+++ b/parser/_testdata/overload1/overload.gop
@@ -0,0 +1,13 @@
+func foo = (
+	func(a, b float64) float64 {
+		return a + b
+	}
+	func(a, b string) string {
+		return a + b
+	}
+)
+
+func bar = (
+	addComplex
+	(T).add
+)
diff --git a/parser/_testdata/overload1/parser.expect b/parser/_testdata/overload1/parser.expect
new file mode 100644
index 00000000..aa686f6c
--- /dev/null
+++ b/parser/_testdata/overload1/parser.expect
@@ -0,0 +1,94 @@
+package main
+
+file overload.gop
+ast.OverloadFuncDecl:
+  Name:
+    ast.Ident:
+      Name: foo
+  Funcs:
+    ast.FuncLit:
+      Type:
+        ast.FuncType:
+          Params:
+            ast.FieldList:
+              List:
+                ast.Field:
+                  Names:
+                    ast.Ident:
+                      Name: a
+                    ast.Ident:
+                      Name: b
+                  Type:
+                    ast.Ident:
+                      Name: float64
+          Results:
+            ast.FieldList:
+              List:
+                ast.Field:
+                  Type:
+                    ast.Ident:
+                      Name: float64
+      Body:
+        ast.BlockStmt:
+          List:
+            ast.ReturnStmt:
+              Results:
+                ast.BinaryExpr:
+                  X:
+                    ast.Ident:
+                      Name: a
+                  Op: +
+                  Y:
+                    ast.Ident:
+                      Name: b
+    ast.FuncLit:
+      Type:
+        ast.FuncType:
+          Params:
+            ast.FieldList:
+              List:
+                ast.Field:
+                  Names:
+                    ast.Ident:
+                      Name: a
+                    ast.Ident:
+                      Name: b
+                  Type:
+                    ast.Ident:
+                      Name: string
+          Results:
+            ast.FieldList:
+              List:
+                ast.Field:
+                  Type:
+                    ast.Ident:
+                      Name: string
+      Body:
+        ast.BlockStmt:
+          List:
+            ast.ReturnStmt:
+              Results:
+                ast.BinaryExpr:
+                  X:
+                    ast.Ident:
+                      Name: a
+                  Op: +
+                  Y:
+                    ast.Ident:
+                      Name: b
+ast.OverloadFuncDecl:
+  Name:
+    ast.Ident:
+      Name: bar
+  Funcs:
+    ast.Ident:
+      Name: addComplex
+    ast.SelectorExpr:
+      X:
+        ast.ParenExpr:
+          X:
+            ast.Ident:
+              Name: T
+      Sel:
+        ast.Ident:
+          Name: add
diff --git a/parser/_testdata/overload2/overload2.gop b/parser/_testdata/overload2/overload2.gop
new file mode 100644
index 00000000..6d0eeeea
--- /dev/null
+++ b/parser/_testdata/overload2/overload2.gop
@@ -0,0 +1,11 @@
+func (T).* = (
+	mul1
+	mul2
+)
+
+func (T).add = (
+	add1
+	func(a, b T) T {
+		return a + b
+	}
+)
diff --git a/parser/_testdata/overload2/parser.expect b/parser/_testdata/overload2/parser.expect
new file mode 100644
index 00000000..0a6f83f9
--- /dev/null
+++ b/parser/_testdata/overload2/parser.expect
@@ -0,0 +1,68 @@
+package main
+
+file overload2.gop
+ast.OverloadFuncDecl:
+  Recv:
+    ast.FieldList:
+      List:
+        ast.Field:
+          Type:
+            ast.Ident:
+              Name: T
+  Name:
+    ast.Ident:
+      Name: *
+  Funcs:
+    ast.Ident:
+      Name: mul1
+    ast.Ident:
+      Name: mul2
+ast.OverloadFuncDecl:
+  Recv:
+    ast.FieldList:
+      List:
+        ast.Field:
+          Type:
+            ast.Ident:
+              Name: T
+  Name:
+    ast.Ident:
+      Name: add
+  Funcs:
+    ast.Ident:
+      Name: add1
+    ast.FuncLit:
+      Type:
+        ast.FuncType:
+          Params:
+            ast.FieldList:
+              List:
+                ast.Field:
+                  Names:
+                    ast.Ident:
+                      Name: a
+                    ast.Ident:
+                      Name: b
+                  Type:
+                    ast.Ident:
+                      Name: T
+          Results:
+            ast.FieldList:
+              List:
+                ast.Field:
+                  Type:
+                    ast.Ident:
+                      Name: T
+      Body:
+        ast.BlockStmt:
+          List:
+            ast.ReturnStmt:
+              Results:
+                ast.BinaryExpr:
+                  X:
+                    ast.Ident:
+                      Name: a
+                  Op: +
+                  Y:
+                    ast.Ident:
+                      Name: b
diff --git a/parser/parser_test.go b/parser/parser_test.go
index 9a90a7de..549fb416 100644
--- a/parser/parser_test.go
+++ b/parser/parser_test.go
@@ -63,6 +63,7 @@ func testErrCode(t *testing.T, code string, errExp, panicExp string) {
 			}
 		}
 	}()
+	t.Helper()
 	fset := token.NewFileSet()
 	_, err := Parse(fset, "/foo/bar.gop", code, 0)
 	if err == nil || err.Error() != errExp {
@@ -78,6 +79,7 @@ func testErrCodeParseExpr(t *testing.T, code string, errExp, panicExp string) {
 			}
 		}
 	}()
+	t.Helper()
 	_, err := ParseExpr(code)
 	if err == nil || err.Error() != errExp {
 		t.Fatal("testErrCodeParseExpr error:", err)
@@ -92,6 +94,7 @@ func testClassErrCode(t *testing.T, code string, errExp, panicExp string) {
 			}
 		}
 	}()
+	t.Helper()
 	fset := token.NewFileSet()
 	_, err := Parse(fset, "/foo/bar.gox", code, ParseGoPlusClass)
 	if err == nil || err.Error() != errExp {
@@ -504,4 +507,11 @@ func TestErrCompositeLiteral(t *testing.T) {
 `, `/foo/bar.gop:1:10: cannot parenthesize type in composite literal`, ``)
 }
 
+func TestErrSelectorExpr(t *testing.T) {
+	testErrCode(t, `
+x.
+*p
+`, `/foo/bar.gop:3:1: expected selector or type assertion, found '*'`, ``)
+}
+
 // -----------------------------------------------------------------------------
diff --git a/parser/parserdir_test.go b/parser/parserdir_test.go
index ca6686dc..d43bfcae 100644
--- a/parser/parserdir_test.go
+++ b/parser/parserdir_test.go
@@ -100,6 +100,7 @@ func testFrom(t *testing.T, pkgDir, sel string, exclude Mode) {
 	if sel != "" && !strings.Contains(pkgDir, sel) {
 		return
 	}
+	t.Helper()
 	log.Println("Parsing", pkgDir)
 	fset := token.NewFileSet()
 	pkgs, err := ParseDir(fset, pkgDir, nil, (Trace|ParseComments|ParseGoAsGoPlus)&^exclude)