Using the gofmt refactoring tool to rename a global variable - go

I'm experimenting the gofmt tool capabilities for refactoring go code based on this blog post, I have this trivial example:
package main
import (
"fmt"
)
var v = 12
func main() {
fmt.Println(v)
}
I'm trying to rename the v variable to mapplying this recipe:
gofmt -r 'v -> m' -w main.go
The code after the refactoring looks (broken) like:
package m
import (
"fmt"
)
var m = m
func m() {
m
}
What am I missing here?

There is a problem with what you're trying, the gofmt manual states:
The rewrite rule specified with the -r flag must be a string of the form:
pattern -> replacement
Both pattern and replacement must be valid Go expressions. In the pattern, single-character lowercase >identifiers serve as wildcards matching arbitrary sub-expressions; those expressions will be substituted for the same identifiers in the replacement.
(highlighting added)
If you had var vee = 12 and used -r vee -> foo everything would be fine. With v -> m however,
v -> m matches every Go expression, identifies it as v and replaces it by m.

Related

Why does the vscode golang plugin remove spaces around operators on save?

If it matters, I've been writing code for many years, but I'm only now exploring golang and vscode at the same time.
On my Linux box, I installed vscode (1.56.2) and the golang plugin (0.25.1).
I've been stepping through some simple golang tutorials, and I noticed a curious thing that happened when I saved a file I had edited.
This is the line I had:
out = append(out, base + v)
When I saved it, it went to this:
out = append(out, base+v)
It removed the spaces around the "+" operator. I went searching through the vscode and golang extension settings, and I found almost nothing about what it will do on save.
In fact, in Settings, I have "Format On Save" UNchecked.
So, what is doing this, and why? Do I have any ability to configure this?
So, what is doing this, and why?
Your editor runs the the gofmt command on save. The command uses space to show operator precedence.
The following snippet is formatted with the gofmt command. Notice how operands are closer to the higher precedence operators.
fmt.Println(a + b + c + d)
fmt.Println(a * b * c * d)
fmt.Println(a + b*c + d)
fmt.Println(a*b + c*d)
The gofmt command removes the spaces around the + in the question because the + has precedence over the ,.
This has nothing to do with VScode. If you have this file:
package main
import "fmt"
func main() {
var a []int
a = append(a, 1 + 2)
fmt.Println(a)
}
Running gofmt file.go gives you:
package main
import "fmt"
func main() {
var a []int
a = append(a, 1+2)
fmt.Println(a)
}

Why is Go's escape analysis behaving differently when simple type is passed to two different functions with same signature

I don't quite get why Go's escape analysis says that a local integer variable escapes to the heap when it is passed to fmt.Fprintln while it is not escaping when it is passed to another self-written function with the same signature as fmt.Fprintln. I hope the two scenarios A and B below should make the problem more clear.
Scenario A: x escapes to heap
Take the following content of main.go:
package main
import (
"fmt"
"os"
)
func main() {
x := 73
fmt.Fprintln(os.Stdout, x)
}
When you now run go build -gcflags "-m" you see an output that indicates that the x escapes to the heap. The output says something along the lines of
...
.\main.go:10:14: x escapes to heap
...
Scenario B: x does not escape to heap
Now take the following content of main.go:
package main
import (
"io"
"os"
)
func main() {
x := 73
myOwnPrintFunction(os.Stdout, x)
}
//go:noinline
func myOwnPrintFunction(w io.Writer, a ...interface{}) {
println(a)
}
Running go build -gcflags "-m" again you see an output that indicates that x no longer escapes to the heap. The output now says something along the lines of
...
.\main.go:10:20: x does not escape
...
I don't get the difference here. I would have thought that scenario A is correct since x is passed to a variadic function that accepts an arbitrary number of interface{} arguments. But apparently that's wrong since the variable no longer escapes when it is passed to the self-written function myOwnPrintFunction which is also a variadic function accepting an arbitrary number of interface{} arguments and where the compiler is instructed to not inline this simple function.

Regular Expression with If Else Condition

I have a problem with If Else Condition in Regex. I have a file which contains the below format. I was looking for return value to be either 0.0.985 or 3.3.5-3811.
I was trying to use if else condition in regex but unable to do so, can anyone explain me while solving the problem please.
random-app-0.0.985.tgz
busy-app-7.3.1.2-3.3.5-3811-a19874elkc-123254376584.zip
Below is the Go code I am trying to use
package main
import (
"fmt"
"io/ioutil"
"regexp"
)
func main(){
content, err:= ioutil.ReadFile("version.txt")
if err != nil{
fmt.Println(err)
}
version:= string(content)
re:= regexp.MustCompile(`(\d+).(\d+).(\d+)|(\d+).(\d+).(\d+).(\d+)`)
result:= re.FindAllStringSubmatch(version,-1)
for i:= range(result){
fmt.Println(result[i][0])
}
}
Output is coming like
0.0.985
7.3.1
2-3.3
5-3811
19874
123254376584
The following regexp can be used: [\d\.]+[\.-][\d]{2,}
package main
import (
"regexp"
"fmt"
)
func main() {
var re = regexp.MustCompile(`(?m)[\d\.]+[\.-][\d]{2,}`)
var str = `random-app-0.0.985.tgz
busy-app-7.3.1.2-3.3.5-3811-a19874elkc-123254376584.zip`
for i, match := range re.FindAllString(str, -1) {
fmt.Println(match, "found at index", i)
}
}
The output
0.0.985 found at index 0
3.3.5-3811 found at index 1
playground
?m multi line modifier. Causes ^ and $ to match the begin/end of each line (not only begin/end of string). In this case it does not make to much difference. It will work without it.
[\d\.]+ matches at least once (quantifier +) a sequence of a digit or a dot
[\.-] matches a dot or a hypen
[\d]{2,} matches at least two digits (quantifier {2,})
One problem with your code is that in a regular expression . matches any character but you're intending it to match a literal dot. Use \. or [.] instead.

Does my gofmt work wrongly or I don't understand something?

I suppose that my gofmt works not how it's supposed to, am I right ?
Original file:
package main
import "fmt"
func main() {
fmt.Printf("hello, world\n")
}
Then I did:
gofmt -r 'h -> H' -w "hello.go"
Content of the file after:
package H
import "fmt"
func H() {
H
}
Presumably gofmt works as its authors intended, which might be different from what you expected.
The documentation says:
Both pattern and replacement must be valid Go expressions. In the pattern, single-character lowercase identifiers serve as wildcards matching arbitrary sub-expressions; those expressions will be substituted for the same identifiers in the replacement.
As you have only a single lowercase letter in the pattern, it matches all sub-expressions. And then replaces them with H. Let's take your example further, consider this:
package main
import "fmt"
func compare(a, b int) {
if a + b < a * b {
fmt.Printf("hello, world\n")
}
}
After the same gofmt command the above code becomes:
package H
import "fmt"
func H(H, H H) {
if H+H < H*H {
H
}
}
If this is not what you want, then you should use a more specific pattern expression.

Evaluate formula in Go

Using Go (golang) I'd like to take a string with a formula and evaluate it with pre-defined values. Here's a way to do it with python's parser module:
x = 8
code = parser.expr("(x + 2) / 10").compile()
print eval(code)
# prints 1
Any idea how to do it with Go?
This package will probably work for your needs: https://github.com/Knetic/govaluate
expression, err := govaluate.NewEvaluableExpression("(x + 2) / 10");
parameters := make(map[string]interface{}, 8)
parameters["x"] = 8;
result, err := expression.Evaluate(parameters);
You will probably need to resort to a library that interprets math statements or have to write your own parser. Python being a dynamic language can parse and execute python code at runtime. Standard Go cannot do that.
If you want to write a parser on your own, the go package will be of help. Example (On play):
import (
"go/ast"
"go/parser"
"go/token"
)
func main() {
fs := token.NewFileSet()
tr, _ := parser.ParseExpr("(3-1) * 5")
ast.Print(fs, tr)
}
The resulting AST (Abstract Syntax Tree) can then be traversed and interpreted as you choose (handling '+' tokens as addition for the now stored values, for example).
I have made my own equation evaluator, using Djikstra's Shunting Yard Algorithm.
It supports all operators, nested parenthesis, functions and even user defined variables.
It is written in pure go
https://github.com/marcmak/calc
go-exprtk package will probably meet all kinds of your needs to evaluate any kind of mathematical expression dynamically.
package main
import (
"fmt"
"github.com/Pramod-Devireddy/go-exprtk"
)
func main() {
exprtkObj := exprtk.NewExprtk()
exprtkObj.SetExpression("(x + 2) / 10")
exprtkObj.AddDoubleVariable("x")
exprtkObj.CompileExpression()
exprtkObj.SetDoubleVariableValue("x", 8)
fmt.Println(exprtkObj.GetEvaluatedValue())
}
This package has many capabilities
There is no such module in Go. You have to build your own. You could use subpackages of the go package, but they might be overkill for your application.
For expression or program evaluation, you can build a lexer and parser using lex and yacc, and specify exactly the syntax and semantics of your mini-language. A calculator has always been a standard yacc example, and the go versions of lex and yacc are no different.
Here's a pointer to the calc example: https://github.com/golang-samples/yacc/tree/master/simple
With this code you can evaluate dynamically any formula and return true or false:
package main
import (
"go/token"
"go/types"
)
func main() {
fs := token.NewFileSet()
tv, err := types.Eval(fs, nil, token.NoPos, "(1 + 4) >= 5")
if err != nil {
panic(err)
}
println(tv.Value.String())
}
There's nothing built in that could do that (remember, Go is not a dynamic language).
However, you can always use bufio.Scanner and build your own parser.
Googling around I found this: https://github.com/sbinet/go-eval
It appears to be an eval loop for Go.
go get github.com/sbinet/go-eval/cmd/go-eval
go install github.com/sbinet/go-eval/cmd/go-eval
go-eval

Resources