Repository: goby-lang/goby
Pull request #802 (2019-11-16 01:37:44)

Tadpole operator to `String#match?`
This is just my idea that the tadpole operator `=~` looks cryptic and is a legacy from Perl, so changing this to `match?` is better for clarification. 

Rubocop style guides also suggests that `=~` is not (partially) recommended: https://github.com/rubocop-hq/ruby-style-guide#no-perl-regexp-last-matchers

If unnecessary, just discard the PR.
Thank you, 


Patch:
diff --git a/compiler/lexer/lexer.go b/compiler/lexer/lexer.go
index 4070659..c7e1ab0 100644
--- a/compiler/lexer/lexer.go
+++ b/compiler/lexer/lexer.go
@@ -54,9 +54,6 @@ func (l *Lexer) NextToken() token.Token {
 		if l.peekChar() == '=' {
 			l.readChar()
 			tok = token.CreateOperator("==", l.line)
-		} else if l.peekChar() == '~' {
-			l.readChar()
-			tok = token.CreateOperator("=~", l.line)
 		} else {
 			tok = token.CreateOperator("=", l.line)
 		}
diff --git a/compiler/parser/parser.go b/compiler/parser/parser.go
index 6ed4441..29e513e 100644
--- a/compiler/parser/parser.go
+++ b/compiler/parser/parser.go
@@ -93,7 +93,6 @@ func New(l *lexer.Lexer) *Parser {
 	p.registerInfix(token.Pow, p.parseInfixExpression)
 	p.registerInfix(token.Eq, p.parseInfixExpression)
 	p.registerInfix(token.NotEq, p.parseInfixExpression)
-	p.registerInfix(token.Match, p.parseInfixExpression)
 	p.registerInfix(token.LT, p.parseInfixExpression)
 	p.registerInfix(token.LTE, p.parseInfixExpression)
 	p.registerInfix(token.GT, p.parseInfixExpression)
diff --git a/compiler/parser/precedence/precedence.go b/compiler/parser/precedence/precedence.go
index 5928d02..ccc8da0 100644
--- a/compiler/parser/precedence/precedence.go
+++ b/compiler/parser/precedence/precedence.go
@@ -24,7 +24,6 @@ const (
 var LookupTable = map[token.Type]int{
 	token.Eq:                 Equals,
 	token.NotEq:              Equals,
-	token.Match:              Compare,
 	token.LT:                 Compare,
 	token.LTE:                Compare,
 	token.GT:                 Compare,
diff --git a/compiler/token/token.go b/compiler/token/token.go
index af869fe..fa8897b 100644
--- a/compiler/token/token.go
+++ b/compiler/token/token.go
@@ -38,12 +38,11 @@ const (
 	OrEq     = "||="
 	Modulo   = "%"
 
-	Match = "=~"
-	LT    = "<"
-	LTE   = "<="
-	GT    = ">"
-	GTE   = ">="
-	COMP  = "<=>"
+	LT   = "<"
+	LTE  = "<="
+	GT   = ">"
+	GTE  = ">="
+	COMP = "<=>"
 
 	Comma     = ","
 	Semicolon = ";"
@@ -124,7 +123,6 @@ var operators = map[string]Type{
 	"||=": OrEq,
 	"%":   Modulo,
 
-	"=~":  Match,
 	"<":   LT,
 	"<=":  LTE,
 	">":   GT,
diff --git a/native/ripper/ripper.go b/native/ripper/ripper.go
index 71395cc..488d7e7 100644
--- a/native/ripper/ripper.go
+++ b/native/ripper/ripper.go
@@ -312,8 +312,6 @@ func convertLex(t token.Type) string {
 		s = "lt"
 	case token.LTE:
 		s = "lte"
-	case token.Match:
-		s = "match"
 	case token.Minus:
 		s = "minus"
 	case token.MinusEq:
diff --git a/vm/string.go b/vm/string.go
index e8a0ae7..1b1a1a8 100644
--- a/vm/string.go
+++ b/vm/string.go
@@ -197,40 +197,6 @@ var builtinStringInstanceMethods = []*BuiltinMethodObject{
 
 		},
 	},
-	{
-		// Matches the receiver with a Regexp, and returns the number of matched strings.
-		//
-		// ```ruby
-		// "pizza" =~ Regex.new("zz")  # => 2
-		// "pizza" =~ Regex.new("OH!") # => nil
-		// ```
-		//
-		// @param regexp [Regexp]
-		// @return [Integer]
-		Name: "=~",
-		Fn: func(receiver Object, sourceLine int, t *Thread, args []Object, blockFrame *normalCallFrame) Object {
-			if len(args) != 1 {
-				return t.vm.InitErrorObject(errors.ArgumentError, sourceLine, errors.WrongNumberOfArgument, 1, len(args))
-			}
-
-			re, ok := args[0].(*RegexpObject)
-			if !ok {
-				return t.vm.InitErrorObject(errors.TypeError, sourceLine, errors.WrongArgumentTypeFormat, classes.RegexpClass, args[0].Class().Name)
-			}
-
-			text := receiver.(*StringObject).value
-
-			match, _ := re.regexp.FindStringMatch(text)
-			if match == nil {
-				return NULL
-			}
-
-			position := match.Groups()[0].Captures[0].Index
-
-			return t.vm.InitIntegerObject(position)
-
-		},
-	},
 	{
 		// Returns a Integer.
 		// Returns -1 if the first string is less than the second string returns -1, returns 0 if equal to, or returns 1 if greater than.
@@ -1008,6 +974,40 @@ var builtinStringInstanceMethods = []*BuiltinMethodObject{
 
 		},
 	},
+	{
+		// Matches the receiver with a Regexp, and returns the number of matched strings.
+		//
+		// ```ruby
+		// "pizza".match? Regex.new("zz")  # => 2
+		// "pizza".match? Regex.new("OH!") # => nil
+		// ```
+		//
+		// @param regexp [Regexp]
+		// @return [Integer]
+		Name: "match?",
+		Fn: func(receiver Object, sourceLine int, t *Thread, args []Object, blockFrame *normalCallFrame) Object {
+			if len(args) != 1 {
+				return t.vm.InitErrorObject(errors.ArgumentError, sourceLine, errors.WrongNumberOfArgument, 1, len(args))
+			}
+
+			re, ok := args[0].(*RegexpObject)
+			if !ok {
+				return t.vm.InitErrorObject(errors.TypeError, sourceLine, errors.WrongArgumentTypeFormat, classes.RegexpClass, args[0].Class().Name)
+			}
+
+			text := receiver.(*StringObject).value
+
+			match, _ := re.regexp.FindStringMatch(text)
+			if match == nil {
+				return NULL
+			}
+
+			position := match.Groups()[0].Captures[0].Index
+
+			return t.vm.InitIntegerObject(position)
+
+		},
+	},
 	{
 		// Returns a copy of str with the all occurrences of pattern substituted for the second argument.
 		// The pattern is typically a String or Regexp; if given as a String, any


Test patch:
diff --git a/compiler/lexer/lexer_test.go b/compiler/lexer/lexer_test.go
index 4d2e411..776f359 100644
--- a/compiler/lexer/lexer_test.go
+++ b/compiler/lexer/lexer_test.go
@@ -1,8 +1,9 @@
 package lexer
 
 import (
-	"github.com/goby-lang/goby/compiler/token"
 	"testing"
+
+	"github.com/goby-lang/goby/compiler/token"
 )
 
 func TestNextToken(t *testing.T) {
@@ -283,7 +284,6 @@ func TestNextToken(t *testing.T) {
 			},
 		}, {
 			`
-	'a' =~ 'a';
 	10 <= 10;
 	10 >= 10;
 	a = 1 <=> 2;
@@ -293,27 +293,22 @@ func TestNextToken(t *testing.T) {
 				expectedLiteral string
 				expectedLine    int
 			}{
-				{token.String, "a", 1},
-				{token.Match, "=~", 1},
-				{token.String, "a", 1},
+				{token.Int, "10", 1},
+				{token.LTE, "<=", 1},
+				{token.Int, "10", 1},
 				{token.Semicolon, ";", 1},
 
 				{token.Int, "10", 2},
-				{token.LTE, "<=", 2},
+				{token.GTE, ">=", 2},
 				{token.Int, "10", 2},
 				{token.Semicolon, ";", 2},
 
-				{token.Int, "10", 3},
-				{token.GTE, ">=", 3},
-				{token.Int, "10", 3},
+				{token.Ident, "a", 3},
+				{token.Assign, "=", 3},
+				{token.Int, "1", 3},
+				{token.COMP, "<=>", 3},
+				{token.Int, "2", 3},
 				{token.Semicolon, ";", 3},
-
-				{token.Ident, "a", 4},
-				{token.Assign, "=", 4},
-				{token.Int, "1", 4},
-				{token.COMP, "<=>", 4},
-				{token.Int, "2", 4},
-				{token.Semicolon, ";", 4},
 			},
 		}, {
 			`
diff --git a/compiler/token/token_test.go b/compiler/token/token_test.go
index 2e733a6..0bff183 100644
--- a/compiler/token/token_test.go
+++ b/compiler/token/token_test.go
@@ -66,7 +66,6 @@ func TestCreateOperatorIdentTrue(t *testing.T) {
 		"||=": OrEq,
 		"%":   Modulo,
 
-		"=~":  Match,
 		"<":   LT,
 		"<=":  LTE,
 		">":   GT,
diff --git a/vm/string_test.go b/vm/string_test.go
index b2a92ca..fdc8a4b 100644
--- a/vm/string_test.go
+++ b/vm/string_test.go
@@ -152,13 +152,13 @@ func TestStringComparisonFail(t *testing.T) {
 	}
 }
 
-func TestStringMatchOperator(t *testing.T) {
+func TestMatchMethod(t *testing.T) {
 	tests := []struct {
 		input    string
 		expected interface{}
 	}{
-		{`"abc" =~ Regexp.new("bc")`, 1},
-		{`"abc" =~ Regexp.new("d")`, nil},
+		{`"abc".match? Regexp.new("bc")`, 1},
+		{`"abc".match? Regexp.new("d")`, nil},
 	}
 
 	for i, tt := range tests {
@@ -170,10 +170,10 @@ func TestStringMatchOperator(t *testing.T) {
 	}
 }
 
-func TestStringMatchOperatorFail(t *testing.T) {
+func TestMatchMethodFail(t *testing.T) {
 	testsFail := []errorTestCase{
-		{`"abc" =~ *[1, 2]`, "ArgumentError: Expect 1 argument(s). got: 2", 1},
-		{`"abc" =~ 'a'`, "TypeError: Expect argument to be Regexp. got: String", 1},
+		{`"abc".match?(*[1, 2])`, "ArgumentError: Expect 1 argument(s). got: 2", 1},
+		{`"abc".match?('a')`, "TypeError: Expect argument to be Regexp. got: String", 1},
 	}
 	for i, tt := range testsFail {
 		v := initTestVM()