正式的文法指定什麼是在 Go 語言(或者其他的語言)中依據文法構成有效程式。```Block = "{" StatementList "}" .StatementList = { Statement ";" } .```以上定義取自 Go 規範。它使用[擴充的 Backus-Naur](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form) 形式(EBNF)。這意味著代碼塊是用分號分隔的一個或多個語句。函數調用是一個表達的例子。我們可以建立一個簡單的代碼塊:```go{ fmt.Println(1); fmt.Println(2);}```經驗豐富的 Gophers 們應該注意到通常代碼的每行末尾沒有使用分號。它可以簡化為:```{ fmt.Println(1) fmt.Println(2)}```這樣的代碼和上一個工作方式相同。既然文法需要分號,又是什麼使它可啟動並執行?## 根源![](https://raw.githubusercontent.com/studygolang/gctt-images/master/automatic-semicolon-insertion-in-go/1.jpeg)語言設計者們開始擺脫分號之類的標記的原因是什麼呢?這個答案相當簡單。都是關於可讀性。人造代碼越少,它就越容易使用。這是很重要的,因為一旦寫下一段代碼很可能會被不同的人閱讀。文法使用分號作為產品終結者。由於目標是讓程式員不必鍵入這些分號,所以必須有一個自動注入它們的方法。這就是 Go 的 lexer [正在做的事情](https://github.com/golang/go/blob/1106512db54fc2736c7a9a67dd553fc9e1fca742/src/go/scanner/scanner.go#L641)。分號被添加當行的最後一個標記是以下標記之一時:+ 一個[標識符](https://golang.org/ref/spec#Identifiers)+ 一個[整數](https://golang.org/ref/spec#Integer_literals),[浮點數](https://golang.org/ref/spec#Floating-point_literals),[虛數](https://golang.org/ref/spec#Imaginary_literals),[符號](https://golang.org/ref/spec#Rune_literals)或[字串](https://golang.org/ref/spec#String_literals)。+ [關鍵字](https://golang.org/ref/spec#Keywords) 之一 break, continure, fallthrough 或 return+ [運算子和分隔字元](https://golang.org/ref/spec#Operators_and_Delimiters)之一 ++,--,),] 或 }讓我們看個例子:```gofunc g() int { return 1}func f() func(int) { return func(n int) { fmt.Println("Inner func called") }}```有這樣的定義,我們可以分析倆種情況:```gof()(g())```和:```gof()(g())```第一個片段沒有列印任何東西,但是第二個給出內建函式調用。這是因為前面提到的第4條規則:因為最後的記號都是圓括弧,所以兩行後面都加了分號。```gof();(g());```## 在底層![](https://raw.githubusercontent.com/studygolang/gctt-images/master/automatic-semicolon-insertion-in-go/2.jpeg)Golang 在文法分析(掃描)時加入分號。在處理 `.go` 檔案開始的時,字元被轉換為標識符,數字,關鍵字等。掃碼器時用 Go 實現的,所以我們可以很容易使用它:```gopackage mainimport ( "fmt" "go/scanner" "go/token")func main() { scanner := scanner.Scanner{} source := []byte("n := 1\nfmt.Println(n)") errorHandler := func(_ token.Position, msg string) { fmt.Printf("error handler called: %s\n", msg) } fset := token.NewFileSet() file := fset.AddFile("", fset.Base(), len(source)) scanner.Init(file, source, errorHandler, 0) for { position, tok, literal := scanner.Scan() fmt.Printf("%d: %s", position, tok) if literal != ""{ fmt.Printf(" %q", literal) } fmt.Println() if tok == token.EOF { break } }}```輸出:```1: IDENT "n"3: :=6: INT "1"7: ; "\n"8: IDENT "fmt"11: .12: IDENT "Println"19: (20: IDENT "n"21: )22: ; "\n"22: EOF```行列印 ; "\n" 是掃描器(lexer)為丞相添加分號的地方:```gon := 1fmt.Println(n)```golangspec 已有 300 多粉絲。這並不是它的目標,但有越來越多的人認為它是一個有用的出版物時是非常有動力的。喜歡加關注,雙擊 666。
via: https://medium.com/golangspec/automatic-semicolon-insertion-in-go-1990338f2649
作者:Michał Łowicki 譯者:themoonbear 校對:polaris1119
本文由 GCTT 原創編譯,Go語言中文網 榮譽推出
本文由 GCTT 原創翻譯,Go語言中文網 首發。也想加入譯者行列,為開源做一些自己的貢獻嗎?歡迎加入 GCTT!
翻譯工作和譯文發表僅用於學習和交流目的,翻譯工作遵照 CC-BY-NC-SA 協議規定,如果我們的工作有侵犯到您的權益,請及時聯絡我們。
歡迎遵照 CC-BY-NC-SA 協議規定 轉載,敬請在本文中標註並保留原文/譯文連結和作者/譯者等資訊。
文章僅代表作者的知識和看法,如有不同觀點,請樓下排隊吐槽
124 次點擊