Phffff=blog===
A Short Guide to Adding a Keyword to Go
13 Jan 2018
Go is a programming language.
Sometimes code looks cleaner when variable declarations are listed after code that uses them. Imagine writing Go in this style.
$ cat test.go
package main
import (
"fmt"
"strconv"
)
func main() {
fmt.Println("hi")
fmt.Println(f(a))
andwhere a = 1
andwhere f = func(a int) string { return strconv.Itoa(a+1) }
}
$ ./bin/go run test.go
hi
2
Below are the modifications I made to the Go compiler.
- The string “andwhere” is associated with a new token
_Andwhere
:
--- a/src/cmd/compile/internal/syntax/tokens.go
+++ b/src/cmd/compile/internal/syntax/tokens.go
@@ -46,6 +46,7 @@ const (
_Continue
_Default
_Defer
+ _Andwhere
_Else
_Fallthrough
_For
@@ -118,6 +119,7 @@ var tokstrings = [...]string{
_Continue: "continue",
_Default: "default",
_Defer: "defer",
+ _Andwhere: "andwhere",
_Else: "else",
_Fallthrough: "fallthrough",
_For: "for",
- The new AST node
Andwhere
holds a declaration statement:
--- a/src/cmd/compile/internal/syntax/nodes.go
+++ b/src/cmd/compile/internal/syntax/nodes.go
@@ -371,6 +371,11 @@ type (
stmt
}
+ Andwhere struct {
+ S *DeclStmt
+ stmt
+ }
+
ReturnStmt struct {
Results Expr // nil means no explicit return values
stmt[
Andwhere
statements are parsed in the statement parsing functionstmtOrNil
:
--- a/src/cmd/compile/internal/syntax/parser.go
+++ b/src/cmd/compile/internal/syntax/parser.go
@@ -2034,6 +2053,11 @@ func (p *parser) stmtOrNil() Stmt {
case _Go, _Defer:
return p.callStmt()
+ case _Andwhere:
+ s := new(Andwhere)
+ s.S = p.declStmt(p.varDecl)
+ return s
+
case _Goto:
s := new(BranchStmt)
s.pos = p.pos()
- Every
Andwhere
is moved to the beginning of its surrounding function body at end of the function declaration parsing functionfuncDeclOrNil
:
@@ -542,9 +543,27 @@ func (p *parser) funcDeclOrNil() *FuncDecl {
}
f.Pragma = p.pragma
+ // Lift all Andwhere to the top
+ if f.Body != nil {
+ f.Body.List = lift_andwheres(f.Body.List)
+ }
+
return f
}
+func lift_andwheres(list []Stmt) []Stmt {
+ for i, x := range list {
+ switch x.(type) {
+ case *Andwhere:
+ removed := append(list[:i], list[i+1:]...)
+ modified := x.(*Andwhere).S
+ prepended := append([]Stmt{modified}, removed...)
+ return lift_andwheres(prepended)
+ }
+ }
+ return list
+}
+
func (p *parser) funcBody() *BlockStmt {
p.fnest++
errcnt := p.errcnt
View on GitHub