
Автоматическая вставка точки с запятой в Go
Формальная грамматика определяет, что составляет синтаксически допустимую программу на Go (или другом языке программирования):
Block = "{" StatementList "}" .
StatementList = { Statement ";" } .
Приведенные выше определения взяты из спецификации Go. Они используют расширенную форму Бэкуса-Наура (EBNF). Все это означает, что блок кода - это одно или несколько операторов, разделенных точкой с запятой. Вызов функции является примером оператора. Зная, что мы можем создать простой блок:
{
fmt.Println(1);
fmt.Println(2);
}
Опытные суслики должны заметить точки с запятой, которые не используются в конце каждой строки в идиоматическом коде. Его можно упростить до:
{
fmt.Println(1)
fmt.Println(2)
}
Такой код работает так же, как и первый. Но что делает это возможным, если грамматика требует точки с запятой?
Корни

Почему разработчики языков даже начали работать над избавлением от токенов, таких как точки с запятой? Ответ довольно прост. Все дело в удобочитаемости. Чем меньше в коде артефактов, тем проще с ним работать. Это важно, поскольку однажды написанный фрагмент кода, вероятно, будет много раз прочитан разными людьми.
Грамматика использует точки с запятой как терминаторы продукции. Поскольку цель состоит в том, чтобы освободить программиста от ввода этих точек с запятой, должен быть способ их автоматического ввода. Это то, что делает лексер Go. Точка с запятой добавляется, когда последний токен строки является одним из:
- идентификатор
- целочисленный, с плавающей точкой, воображаемый, рунический или строковый литералы
- одно из ключевых слов
break,continue,fallthroughилиreturn - один из операторов и разделителей
++,--,),]или}
Приведем пример:
func g() int {
return 1
}
func f() func(int) {
return func(n int) {
fmt.Println("Inner func called")
}
}
Имея такие определения, мы можем проанализировать два сценария:
f() (g())
а также:
f()(g())
Первый фрагмент ничего не печатает, а второй даетInner func called. Это из-за 4-го вышеупомянутого правила - точки с запятой были добавлены после обеих строк, поскольку последние токены закрывают круглые скобки:
f(); (g());
Под капотом

Добавление точек с запятой в Golang происходит при лексическом анализе (сканировании). Это в самом начале обработки файла .go, когда символы преобразуются в токены, такие как идентификаторы, числа, ключевые слова и т. Д. Сканер реализован в самом Go, поэтому мы можем легко его использовать:
package main
import (
"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" - это места, где сканер (лексер) добавляет точки с запятой для программы:
n := 1 fmt.Println(n)
Golangspec насчитывает более 300 последователей. Это не было самоцелью, но все же очень воодушевляет знать, что все больше и больше людей видят эту публикацию полезной.
Нажмите ❤ ниже, чтобы помочь другим узнать эту историю. Если вы хотите получать обновления о новых сообщениях, подписывайтесь на меня.