├── LICENSE ├── README.md ├── c2gofmt ├── decls.go ├── fmt_test.go ├── go.mod ├── go.sum ├── internal │ └── cc │ │ ├── cc.y │ │ ├── doc.go │ │ ├── expr.go │ │ ├── exprop_string.go │ │ ├── lex.go │ │ ├── parse.go │ │ ├── print.go │ │ ├── print_test.go │ │ ├── stmt.go │ │ ├── stmtop_string.go │ │ ├── type.go │ │ ├── typecheck.go │ │ ├── y.go │ │ └── y.output ├── main.go ├── printer.go ├── rename.go ├── rewrite.go ├── syntax.go └── testdata │ ├── addr.txt │ ├── basic.txt │ ├── basic2.txt │ ├── basic3.txt │ ├── bool.txt │ ├── comment.txt │ ├── comment2.txt │ ├── comment3.txt │ ├── comment4.txt │ ├── decl.txt │ ├── decl2.txt │ ├── decl3.txt │ ├── enum.txt │ ├── extern.txt │ ├── func.txt │ ├── func2.txt │ ├── if.txt │ ├── init.txt │ ├── init2.txt │ ├── ptr.txt │ ├── rewrite.txt │ ├── rewrite2.txt │ ├── side.txt │ ├── side2.txt │ ├── slice.txt │ ├── static.txt │ ├── stmt.txt │ ├── struct.txt │ ├── struct2.txt │ ├── switch.txt │ ├── switch2.txt │ └── switch3.txt ├── git-foreach ├── go.mod ├── go.sum └── main.go ├── gofixerr ├── edit.go ├── go.mod ├── go.sum └── main.go ├── mdweb ├── go.mod ├── go.sum └── main.go ├── rdate ├── go.mod └── rdate.go └── sleep ├── go.mod └── sleep.go /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Commands. 2 | -------------------------------------------------------------------------------- /c2gofmt/decls.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import "rsc.io/cmd/c2gofmt/internal/cc" 8 | 9 | func moveDecls(progDecls []*cc.Decl) { 10 | for _, d := range progDecls { 11 | if d.Type != nil && d.Type.Kind == cc.Func && d.Body != nil { 12 | moveFuncDecls(d) 13 | } 14 | } 15 | } 16 | 17 | func inlineBlockNoBrace(x *cc.Stmt) { 18 | if x.Op == cc.Block { 19 | var list []*cc.Stmt 20 | for _, stmt := range x.Block { 21 | // keep stmt always, in case of labels, comments etc 22 | list = append(list, stmt) 23 | if stmt.Op == BlockNoBrace { 24 | list = append(list, stmt.Block...) 25 | stmt.Op = cc.Empty 26 | stmt.Block = nil 27 | } 28 | } 29 | x.Block = list 30 | } 31 | } 32 | 33 | func moveFuncDecls(fndecl *cc.Decl) { 34 | // Inline the BlockNoBraces into the Blocks, so that we can understand 35 | // the flow of the variables properly. 36 | cc.Postorder(fndecl.Body, func(x cc.Syntax) { 37 | switch x := x.(type) { 38 | case *cc.Stmt: 39 | inlineBlockNoBrace(x) 40 | } 41 | }) 42 | 43 | // Push var declarations forward until we hit their uses. 44 | type usesVar struct { 45 | x cc.Syntax 46 | v *cc.Decl 47 | } 48 | uses := make(map[usesVar]bool) 49 | var decls []*cc.Decl 50 | cc.Preorder(fndecl.Body, func(x cc.Syntax) { 51 | if d, ok := x.(*cc.Decl); ok { 52 | decls = append(decls, d) 53 | } 54 | }) 55 | copyUses := func(x, y cc.Syntax) { 56 | for _, d := range decls { 57 | if uses[usesVar{y, d}] { 58 | uses[usesVar{x, d}] = true 59 | } 60 | } 61 | } 62 | cc.Postorder(fndecl.Body, func(x cc.Syntax) { 63 | switch x := x.(type) { 64 | case *cc.Stmt: 65 | copyUses(x, x.Pre) 66 | copyUses(x, x.Expr) 67 | copyUses(x, x.Post) 68 | copyUses(x, x.Body) 69 | copyUses(x, x.Else) 70 | for _, y := range x.Block { 71 | copyUses(x, y) 72 | } 73 | copyUses(x, x.Decl) 74 | case *cc.Expr: 75 | if x.Op == cc.Name && x.XDecl != nil { 76 | uses[usesVar{x, x.XDecl}] = true 77 | } 78 | copyUses(x, x.Left) 79 | copyUses(x, x.Right) 80 | for _, y := range x.List { 81 | copyUses(x, y) 82 | } 83 | for _, y := range x.Block { 84 | copyUses(x, y) 85 | } 86 | for _, y := range x.After { 87 | copyUses(x, y) 88 | } 89 | case *cc.Decl: 90 | copyUses(x, x.Init) 91 | case *cc.Init: 92 | copyUses(x, x.Expr) 93 | for _, y := range x.Braced { 94 | copyUses(x, y) 95 | } 96 | } 97 | }) 98 | 99 | anyUses := func(list []*cc.Stmt, d *cc.Decl) bool { 100 | for _, x := range list { 101 | if uses[usesVar{x, d}] { 102 | return true 103 | } 104 | } 105 | return false 106 | } 107 | 108 | addToBlock := func(x, decl *cc.Stmt) *cc.Stmt { 109 | if x.Op == cc.Block || x.Op == BlockNoBrace { 110 | x.Block = append([]*cc.Stmt{decl}, x.Block...) 111 | return x 112 | } 113 | return &cc.Stmt{ 114 | Op: cc.Block, 115 | Block: []*cc.Stmt{decl, x}, 116 | } 117 | } 118 | 119 | var addToIf func(x, decl *cc.Stmt) bool 120 | addToIf = func(x, d *cc.Stmt) bool { 121 | if uses[usesVar{x.Pre, d.Decl}] || uses[usesVar{x.Expr, d.Decl}] { 122 | return false 123 | } 124 | if uses[usesVar{x.Body, d.Decl}] { 125 | x.Body = addToBlock(x.Body, d) 126 | } 127 | if uses[usesVar{x.Else, d.Decl}] { 128 | if x.Else.Op != cc.If || !addToIf(x.Else, d) { 129 | x.Else = addToBlock(x.Else, d) 130 | } 131 | } 132 | return true 133 | } 134 | 135 | cc.Preorder(fndecl.Body, func(x cc.Syntax) { 136 | switch x := x.(type) { 137 | case *cc.Stmt: 138 | if x.Op == cc.Block || x.Op == BlockNoBrace { 139 | out := x.Block[:0] 140 | var pending []*cc.Stmt // all StmtDecls 141 | for i, stmt := range x.Block { 142 | // Emit any required declarations. 143 | pendout := pending[:0] 144 | for _, d := range pending { 145 | if !uses[usesVar{stmt, d.Decl}] { 146 | pendout = append(pendout, d) 147 | continue 148 | } 149 | if stmt.Op == cc.StmtExpr && stmt.Expr.Op == cc.Eq && stmt.Expr.Left.Op == cc.Name && stmt.Expr.Left.XDecl == d.Decl { 150 | stmt.Expr.Op = ColonEq 151 | continue 152 | } 153 | if !anyUses(x.Block[i+1:], d.Decl) { 154 | switch stmt.Op { 155 | case cc.If: 156 | if addToIf(stmt, d) { 157 | continue 158 | } 159 | case cc.Block: 160 | addToBlock(stmt, d) 161 | continue 162 | case cc.For: 163 | if !uses[usesVar{stmt.Pre, d.Decl}] && !uses[usesVar{stmt.Expr, d.Decl}] && !uses[usesVar{stmt.Post, d.Decl}] { 164 | // Only used in body, and it is uninitialized on entry, 165 | // so it must be OK to use a fresh copy every time. 166 | stmt.Body = addToBlock(stmt.Body, d) 167 | continue 168 | } 169 | if stmt.Pre != nil && stmt.Pre.Op == cc.Eq && stmt.Pre.Left.Op == cc.Name && stmt.Pre.Left.XDecl == d.Decl { 170 | // Loop variable. 171 | stmt.Pre.Op = ColonEq 172 | continue 173 | } 174 | } 175 | } 176 | out = append(out, d) 177 | } 178 | pending = pendout 179 | 180 | // Pick up any uninitialized declarations for emitting later. 181 | if stmt.Op == cc.StmtDecl && stmt.Decl.Init == nil { 182 | pending = append(pending, stmt) 183 | // If the declaration is followed by a blank line, 184 | // as is the custom in C, remove it, to match the 185 | // custom in Go. Also, the var declaration is likely moving 186 | // so the blank line will not follow anything. 187 | if i+1 < len(x.Block) { 188 | if com := &x.Block[i+1].Comments; len(com.Before) > 0 && com.Before[0].Text == "" { 189 | com.Before = com.Before[1:] 190 | } 191 | } 192 | continue 193 | } 194 | out = append(out, stmt) 195 | } 196 | x.Block = out 197 | } 198 | } 199 | }) 200 | } 201 | -------------------------------------------------------------------------------- /c2gofmt/fmt_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "bytes" 9 | "io/ioutil" 10 | "path/filepath" 11 | "testing" 12 | 13 | "rsc.io/rf/diff" 14 | ) 15 | 16 | func Test(t *testing.T) { 17 | parseRules("builtin rules", ` 18 | XMethod(x, y) -> x.Method(y) 19 | r.min -> r.Min 20 | r.max -> r.Max 21 | p.x -> p.X 22 | p.y -> p.Y 23 | `) 24 | 25 | files, _ := filepath.Glob("testdata/*.txt") 26 | if len(files) == 0 { 27 | t.Fatalf("no testdata") 28 | } 29 | for _, file := range files { 30 | t.Run(filepath.Base(file), func(t *testing.T) { 31 | data, err := ioutil.ReadFile(file) 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | i := bytes.Index(data, []byte("\n---\n")) 36 | if i < 0 { 37 | t.Fatalf("cannot find --- marker") 38 | } 39 | cdata, want := data[:i+1], data[i+5:] 40 | have := do(file, cdata) 41 | if !bytes.Equal(have, want) { 42 | t.Errorf("%s:\n%s", file, have) 43 | t.Errorf("want:\n%s", want) 44 | d, err := diff.Diff("want", want, "have", have) 45 | if err == nil { 46 | t.Errorf("diff:\n%s", d) 47 | } 48 | } 49 | }) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /c2gofmt/go.mod: -------------------------------------------------------------------------------- 1 | module rsc.io/cmd/c2gofmt 2 | 3 | go 1.16 4 | 5 | require rsc.io/rf v0.0.0-20201229043002-4f177a6cd303 6 | -------------------------------------------------------------------------------- /c2gofmt/go.sum: -------------------------------------------------------------------------------- 1 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 2 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 3 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 4 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 5 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 6 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 7 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 8 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 9 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 10 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 11 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 12 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 13 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 14 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 15 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 16 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 17 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 18 | golang.org/x/tools v0.0.0-20201111224557-41a3a589386c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 19 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 20 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 21 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 22 | rsc.io/rf v0.0.0-20201229043002-4f177a6cd303 h1:h69TGIgcmTBl5Q8hAUR+G5P9GSCPX3Y7gp8cwiUI0So= 23 | rsc.io/rf v0.0.0-20201229043002-4f177a6cd303/go.mod h1:STN8PzM7ZPqz/xfNAVChFQHn4fG+RwhjiQCPOOqFNY4= 24 | -------------------------------------------------------------------------------- /c2gofmt/internal/cc/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package cc implements parsing, type checking, and printing of C programs. 6 | package cc 7 | -------------------------------------------------------------------------------- /c2gofmt/internal/cc/expr.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package cc 6 | 7 | import "fmt" 8 | 9 | // An Expr is a parsed C expression. 10 | type Expr struct { 11 | SyntaxInfo 12 | Op ExprOp // operator 13 | Left *Expr // left (or only) operand 14 | Right *Expr // right operand 15 | List []*Expr // operand list, for Comma, Cond, Call 16 | Text string // name or literal, for Name, Number, Goto, Arrow, Dot 17 | Texts []string // list of literals, for String 18 | Type *Type // type operand, for SizeofType, Offsetof, Cast, CastInit, VaArg 19 | Init *Init // initializer, for CastInit 20 | Block []*Stmt // for c2go 21 | After []*Stmt 22 | 23 | // derived information 24 | XDecl *Decl 25 | XType *Type // expression type, derived 26 | } 27 | 28 | func (x *Expr) String() string { 29 | var p Printer 30 | p.hideComments = true 31 | p.printExpr(x, precLow) 32 | return p.String() 33 | } 34 | 35 | //go:generate stringer -type ExprOp 36 | type ExprOp int 37 | 38 | const ( 39 | _ ExprOp = iota 40 | Add // Left + Right 41 | AddEq // Left += Right 42 | Addr // &Left 43 | And // Left & Right 44 | AndAnd // Left && Right 45 | AndEq // Left &= Right 46 | Arrow // Left->Text 47 | Call // Left(List) 48 | Cast // (Type)Left 49 | CastInit // (Type){Init} 50 | Comma // x, y, z; List = {x, y, z} 51 | Cond // x ? y : z; List = {x, y, z} 52 | Div // Left / Right 53 | DivEq // Left /= Right 54 | Dot // Left.Name 55 | Eq // Left = Right 56 | EqEq // Left == Right 57 | Gt // Left > Right 58 | GtEq // Left >= Right 59 | Index // Left[Right] 60 | Indir // *Left 61 | Lsh // Left << Right 62 | LshEq // Left <<= Right 63 | Lt // Left < Right 64 | LtEq // Left <= Right 65 | Minus // -Left 66 | Mod // Left % Right 67 | ModEq // Left %= Right 68 | Mul // Left * Right 69 | MulEq // Left *= Right 70 | Name // Text (function, variable, or enum name) 71 | Not // !Left 72 | NotEq // Left != Right 73 | Number // Text (numeric or chraracter constant) 74 | Offsetof // offsetof(Type, Left) 75 | Or // Left | Right 76 | OrEq // Left |= Right 77 | OrOr // Left || Right 78 | Paren // (Left) 79 | Plus // +Left 80 | PostDec // Left-- 81 | PostInc // Left++ 82 | PreDec // --Left 83 | PreInc // ++Left 84 | Rsh // Left >> Right 85 | RshEq // Left >>= Right 86 | SizeofExpr // sizeof(Left) 87 | SizeofType // sizeof(Type) 88 | String // Text (quoted string literal) 89 | Sub // Left - Right 90 | SubEq // Left -= Right 91 | Twid // ~Left 92 | VaArg // va_arg(Left, Type) 93 | Xor // Left ^ Right 94 | XorEq // Left ^= Right 95 | ) 96 | 97 | // Prefix is an initializer prefix. 98 | type Prefix struct { 99 | Span Span 100 | Dot string // .Dot = 101 | XDecl *Decl // for .Dot 102 | Index *Expr // [Index] = 103 | } 104 | 105 | // Init is an initializer expression. 106 | type Init struct { 107 | SyntaxInfo 108 | Prefix []*Prefix // list of prefixes 109 | Expr *Expr // Expr 110 | Braced []*Init // {Braced} 111 | 112 | XType *Type // derived type 113 | } 114 | 115 | // Walk traverses the syntax x, calling before and after on entry to and exit from 116 | // each Syntax encountered during the traversal. In case of cross-linked input, 117 | // the traversal never visits a given Syntax more than once. 118 | func Walk(x Syntax, before, after func(Syntax)) { 119 | seen := map[Syntax]bool{ 120 | nil: true, 121 | (*Decl)(nil): true, 122 | (*Init)(nil): true, 123 | (*Type)(nil): true, 124 | (*Expr)(nil): true, 125 | (*Stmt)(nil): true, 126 | (*Label)(nil): true, 127 | } 128 | walk(x, before, after, seen) 129 | } 130 | 131 | func walk(x Syntax, before, after func(Syntax), seen map[Syntax]bool) { 132 | if seen[x] { 133 | return 134 | } 135 | seen[x] = true 136 | before(x) 137 | switch x := x.(type) { 138 | default: 139 | panic(fmt.Sprintf("walk: unexpected type %T", x)) 140 | 141 | case *Prog: 142 | for _, d := range x.Decls { 143 | walk(d, before, after, seen) 144 | } 145 | 146 | case *Decl: 147 | walk(x.Type, before, after, seen) 148 | walk(x.Init, before, after, seen) 149 | walk(x.Body, before, after, seen) 150 | 151 | case *Init: 152 | for _, b := range x.Braced { 153 | walk(b, before, after, seen) 154 | } 155 | walk(x.Expr, before, after, seen) 156 | 157 | case *Type: 158 | walk(x.Base, before, after, seen) 159 | for _, d := range x.Decls { 160 | walk(d, before, after, seen) 161 | } 162 | walk(x.Width, before, after, seen) 163 | 164 | case *Expr: 165 | walk(x.Left, before, after, seen) 166 | walk(x.Right, before, after, seen) 167 | for _, y := range x.List { 168 | walk(y, before, after, seen) 169 | } 170 | walk(x.Type, before, after, seen) 171 | walk(x.Init, before, after, seen) 172 | for _, y := range x.Block { 173 | walk(y, before, after, seen) 174 | } 175 | 176 | case *Stmt: 177 | walk(x.Pre, before, after, seen) 178 | walk(x.Expr, before, after, seen) 179 | walk(x.Post, before, after, seen) 180 | walk(x.Decl, before, after, seen) 181 | walk(x.Body, before, after, seen) 182 | walk(x.Else, before, after, seen) 183 | for _, y := range x.Block { 184 | walk(y, before, after, seen) 185 | } 186 | for _, y := range x.Labels { 187 | walk(y, before, after, seen) 188 | } 189 | 190 | case *Label: 191 | walk(x.Expr, before, after, seen) 192 | } 193 | after(x) 194 | } 195 | 196 | // Preorder calls f for each piece of syntax of x in a preorder traversal. 197 | func Preorder(x Syntax, f func(Syntax)) { 198 | Walk(x, f, func(Syntax) {}) 199 | } 200 | 201 | // Preorder calls f for each piece of syntax of x in a postorder traversal. 202 | func Postorder(x Syntax, f func(Syntax)) { 203 | Walk(x, func(Syntax) {}, f) 204 | } 205 | -------------------------------------------------------------------------------- /c2gofmt/internal/cc/exprop_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type ExprOp"; DO NOT EDIT. 2 | 3 | package cc 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[Add-1] 12 | _ = x[AddEq-2] 13 | _ = x[Addr-3] 14 | _ = x[And-4] 15 | _ = x[AndAnd-5] 16 | _ = x[AndEq-6] 17 | _ = x[Arrow-7] 18 | _ = x[Call-8] 19 | _ = x[Cast-9] 20 | _ = x[CastInit-10] 21 | _ = x[Comma-11] 22 | _ = x[Cond-12] 23 | _ = x[Div-13] 24 | _ = x[DivEq-14] 25 | _ = x[Dot-15] 26 | _ = x[Eq-16] 27 | _ = x[EqEq-17] 28 | _ = x[Gt-18] 29 | _ = x[GtEq-19] 30 | _ = x[Index-20] 31 | _ = x[Indir-21] 32 | _ = x[Lsh-22] 33 | _ = x[LshEq-23] 34 | _ = x[Lt-24] 35 | _ = x[LtEq-25] 36 | _ = x[Minus-26] 37 | _ = x[Mod-27] 38 | _ = x[ModEq-28] 39 | _ = x[Mul-29] 40 | _ = x[MulEq-30] 41 | _ = x[Name-31] 42 | _ = x[Not-32] 43 | _ = x[NotEq-33] 44 | _ = x[Number-34] 45 | _ = x[Offsetof-35] 46 | _ = x[Or-36] 47 | _ = x[OrEq-37] 48 | _ = x[OrOr-38] 49 | _ = x[Paren-39] 50 | _ = x[Plus-40] 51 | _ = x[PostDec-41] 52 | _ = x[PostInc-42] 53 | _ = x[PreDec-43] 54 | _ = x[PreInc-44] 55 | _ = x[Rsh-45] 56 | _ = x[RshEq-46] 57 | _ = x[SizeofExpr-47] 58 | _ = x[SizeofType-48] 59 | _ = x[String-49] 60 | _ = x[Sub-50] 61 | _ = x[SubEq-51] 62 | _ = x[Twid-52] 63 | _ = x[VaArg-53] 64 | _ = x[Xor-54] 65 | _ = x[XorEq-55] 66 | } 67 | 68 | const _ExprOp_name = "AddAddEqAddrAndAndAndAndEqArrowCallCastCastInitCommaCondDivDivEqDotEqEqEqGtGtEqIndexIndirLshLshEqLtLtEqMinusModModEqMulMulEqNameNotNotEqNumberOffsetofOrOrEqOrOrParenPlusPostDecPostIncPreDecPreIncRshRshEqSizeofExprSizeofTypeStringSubSubEqTwidVaArgXorXorEq" 69 | 70 | var _ExprOp_index = [...]uint8{0, 3, 8, 12, 15, 21, 26, 31, 35, 39, 47, 52, 56, 59, 64, 67, 69, 73, 75, 79, 84, 89, 92, 97, 99, 103, 108, 111, 116, 119, 124, 128, 131, 136, 142, 150, 152, 156, 160, 165, 169, 176, 183, 189, 195, 198, 203, 213, 223, 229, 232, 237, 241, 246, 249, 254} 71 | 72 | func (i ExprOp) String() string { 73 | i -= 1 74 | if i < 0 || i >= ExprOp(len(_ExprOp_index)-1) { 75 | return "ExprOp(" + strconv.FormatInt(int64(i+1), 10) + ")" 76 | } 77 | return _ExprOp_name[_ExprOp_index[i]:_ExprOp_index[i+1]] 78 | } 79 | -------------------------------------------------------------------------------- /c2gofmt/internal/cc/lex.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:generate goyacc cc.y 6 | 7 | package cc 8 | 9 | import ( 10 | "fmt" 11 | "io/ioutil" 12 | "os" 13 | "path/filepath" 14 | "sort" 15 | "strings" 16 | ) 17 | 18 | // A Syntax represents any syntax element. 19 | type Syntax interface { 20 | // GetSpan returns the start and end position of the syntax, 21 | // excluding leading or trailing comments. 22 | // The use of a Get prefix is non-standard but avoids a conflict 23 | // with the field named Span in most implementations. 24 | GetSpan() Span 25 | 26 | // GetComments returns the comments attached to the syntax. 27 | // This method would normally be named 'Comments' but that 28 | // would interfere with embedding a type of the same name. 29 | // The use of a Get prefix is non-standard but avoids a conflict 30 | // with the field named Comments in most implementations. 31 | GetComments() *Comments 32 | } 33 | 34 | // SyntaxInfo contains metadata about a piece of syntax. 35 | type SyntaxInfo struct { 36 | Span Span // location of syntax in input 37 | Comments Comments 38 | } 39 | 40 | func (s *SyntaxInfo) GetSpan() Span { 41 | return s.Span 42 | } 43 | 44 | func (s *SyntaxInfo) GetComments() *Comments { 45 | return &s.Comments 46 | } 47 | 48 | // Comments collects the comments associated with a syntax element. 49 | type Comments struct { 50 | Before []Comment // whole-line comments before this syntax 51 | Suffix []Comment // end-of-line comments after this syntax 52 | 53 | // For top-level syntax elements only, After lists whole-line 54 | // comments following the syntax. 55 | After []Comment 56 | } 57 | 58 | type lexer struct { 59 | // input 60 | start int 61 | byte int 62 | lexInput 63 | pushed []lexInput 64 | forcePos Pos 65 | comments []Comment 66 | 67 | // comment assignment 68 | pre []Syntax 69 | post []Syntax 70 | enumSeen map[interface{}]bool 71 | 72 | // type checking state 73 | scope *Scope 74 | includeSeen map[string]*Header 75 | 76 | typeNames map[string]bool 77 | 78 | // output 79 | errors []string 80 | prog *Prog 81 | expr *Expr 82 | } 83 | 84 | type Header struct { 85 | decls []*Decl 86 | types []*Type 87 | } 88 | 89 | func (lx *lexer) parse() { 90 | if lx.includeSeen == nil { 91 | lx.includeSeen = make(map[string]*Header) 92 | } 93 | if lx.wholeInput == "" { 94 | lx.wholeInput = lx.input 95 | } 96 | lx.scope = &Scope{} 97 | yyParse(lx) 98 | } 99 | 100 | type lexInput struct { 101 | wholeInput string 102 | input string 103 | tok string 104 | lastsym string 105 | lastsym2 string 106 | wassym bool 107 | file string 108 | lineno int 109 | declSave *Header 110 | } 111 | 112 | func (lx *lexer) pushInclude(includeLine string) { 113 | s := strings.TrimSpace(strings.TrimPrefix(includeLine, "#include")) 114 | if !strings.HasPrefix(s, "<") && !strings.HasPrefix(s, "\"") { 115 | lx.Errorf("malformed #include") 116 | return 117 | } 118 | sep := ">" 119 | if s[0] == '"' { 120 | sep = "\"" 121 | } 122 | i := strings.Index(s[1:], sep) 123 | if i < 0 { 124 | lx.Errorf("malformed #include") 125 | return 126 | } 127 | i++ 128 | 129 | file := s[1:i] 130 | origFile := file 131 | 132 | file, data, err := lx.findInclude(file, s[0] == '<') 133 | if err != nil { 134 | lx.Errorf("#include %s: %v", s[:i+1], err) 135 | return 136 | } 137 | 138 | if hdr := lx.includeSeen[file]; hdr != nil { 139 | for _, decl := range hdr.decls { 140 | // fmt.Printf("%s: replay %s\n", file, decl.Name) 141 | lx.pushDecl(decl) 142 | } 143 | for _, typ := range hdr.types { 144 | lx.pushType(typ) 145 | } 146 | return 147 | } 148 | 149 | hdr := lx.declSave 150 | if hdr == nil { 151 | hdr = new(Header) 152 | lx.includeSeen[file] = hdr 153 | } else { 154 | fmt.Printf("%s: warning nested %s\n", lx.span(), includeLine) 155 | } 156 | 157 | if extraMap[origFile] != "" { 158 | str := extraMap[origFile] + "\n" 159 | lx.pushed = append(lx.pushed, lx.lexInput) 160 | lx.lexInput = lexInput{ 161 | input: str, 162 | wholeInput: str, 163 | file: "internal/" + origFile, 164 | lineno: 1, 165 | declSave: hdr, 166 | } 167 | } 168 | 169 | if data == nil { 170 | return 171 | } 172 | 173 | lx.pushed = append(lx.pushed, lx.lexInput) 174 | str := string(append(data, '\n')) 175 | lx.lexInput = lexInput{ 176 | input: str, 177 | wholeInput: str, 178 | file: file, 179 | lineno: 1, 180 | declSave: hdr, 181 | } 182 | } 183 | 184 | var stdMap = map[string]string{} 185 | 186 | var extraMap = map[string]string{} 187 | 188 | var includes []string 189 | 190 | func AddInclude(dir string) { 191 | includes = append(includes, dir) 192 | } 193 | 194 | func (lx *lexer) findInclude(name string, std bool) (string, []byte, error) { 195 | if true { 196 | return "", nil, nil 197 | } 198 | 199 | if std { 200 | if redir, ok := stdMap[name]; ok { 201 | if redir == "" { 202 | return "", nil, nil 203 | } 204 | return "internal/" + name, []byte(redir), nil 205 | } 206 | } 207 | if !filepath.IsAbs(name) { 208 | name1 := filepath.Join(filepath.Dir(lx.file), name) 209 | if _, err := os.Stat(name1); err != nil { 210 | for _, dir := range includes { 211 | name2 := filepath.Join(dir, name) 212 | if _, err := os.Stat(name2); err == nil { 213 | name1 = name2 214 | break 215 | } 216 | } 217 | } 218 | name = name1 219 | } 220 | data, err := ioutil.ReadFile(name) 221 | if err != nil { 222 | return "", nil, err 223 | } 224 | return name, data, nil 225 | } 226 | 227 | func (lx *lexer) pop() bool { 228 | if len(lx.pushed) == 0 { 229 | return false 230 | } 231 | lx.lexInput = lx.pushed[len(lx.pushed)-1] 232 | lx.pushed = lx.pushed[:len(lx.pushed)-1] 233 | return true 234 | } 235 | 236 | func (lx *lexer) pos() Pos { 237 | if lx.forcePos.Line != 0 { 238 | return lx.forcePos 239 | } 240 | return Pos{lx.file, lx.lineno, lx.byte} 241 | } 242 | func (lx *lexer) span() Span { 243 | p := lx.pos() 244 | return Span{p, p} 245 | } 246 | 247 | func (lx *lexer) setSpan(s Span) { 248 | lx.forcePos = s.Start 249 | } 250 | 251 | func span(l1, l2 Span) Span { 252 | if l1.Start.Line == 0 { 253 | return l2 254 | } 255 | if l2.Start.Line == 0 { 256 | return l1 257 | } 258 | return Span{l1.Start, l2.End} 259 | } 260 | 261 | func (lx *lexer) skip(i int) { 262 | lx.lineno += strings.Count(lx.input[:i], "\n") 263 | lx.input = lx.input[i:] 264 | lx.byte += i 265 | } 266 | 267 | func (lx *lexer) token(i int) { 268 | lx.tok = lx.input[:i] 269 | lx.skip(i) 270 | if lx.tok != ")" { 271 | lx.wassym = false 272 | } 273 | } 274 | 275 | func (lx *lexer) sym(i int) { 276 | lx.tok = lx.input[:i] 277 | lx.skip(i) 278 | if lx.wassym { 279 | lx.lastsym2 = lx.lastsym 280 | } else { 281 | lx.lastsym2 = "" 282 | } 283 | lx.lastsym = lx.tok 284 | lx.wassym = true 285 | } 286 | 287 | func (lx *lexer) comment(i int) { 288 | var c Comment 289 | c.Span.Start = lx.pos() 290 | c.Text = lx.input[:i] 291 | j := len(lx.wholeInput) - len(lx.input) 292 | for j > 0 && (lx.wholeInput[j-1] == ' ' || lx.wholeInput[j-1] == '\t') { 293 | j-- 294 | } 295 | if j > 0 && lx.wholeInput[j-1] != '\n' { 296 | c.Suffix = true 297 | } 298 | prefix := lx.wholeInput[j : len(lx.wholeInput)-len(lx.input)] 299 | lines := strings.Split(c.Text, "\n") 300 | for i, line := range lines { 301 | if strings.HasPrefix(line, prefix) { 302 | lines[i] = line[len(prefix):] 303 | } 304 | } 305 | c.Text = strings.Join(lines, "\n") 306 | 307 | lx.skip(i) 308 | c.Span.End = lx.pos() 309 | lx.comments = append(lx.comments, c) 310 | } 311 | 312 | func isalpha(c byte) bool { 313 | return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || c == '_' || c >= 0x80 || '0' <= c && c <= '9' 314 | } 315 | 316 | func isspace(c byte) bool { 317 | return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f' 318 | } 319 | 320 | func (lx *lexer) setEnd(yy *yySymType) { 321 | yy.span.End = lx.pos() 322 | } 323 | 324 | func (lx *lexer) Lex(yy *yySymType) (yyt int) { 325 | //defer func() { println("tok", yy.str, yyt) }() 326 | if lx.start != 0 { 327 | tok := lx.start 328 | lx.start = 0 329 | return tok 330 | } 331 | *yy = yySymType{} 332 | defer lx.setEnd(yy) 333 | Restart: 334 | yy.span.Start = lx.pos() 335 | in := lx.input 336 | if len(in) == 0 { 337 | if lx.pop() { 338 | goto Restart 339 | } 340 | return tokEOF 341 | } 342 | c := in[0] 343 | if isspace(c) { 344 | i := 1 345 | for i < len(in) && isspace(in[i]) && in[i] != '\n' { 346 | i++ 347 | } 348 | if in[0] == '\n' && i < len(in) && in[i] == '\n' { 349 | lx.skip(1) 350 | i-- 351 | lx.comment(0) 352 | } 353 | lx.skip(i) 354 | goto Restart 355 | } 356 | 357 | i := 0 358 | switch c { 359 | case '#': 360 | i++ 361 | for i < len(in) && in[i] != '\n' { 362 | if in[i] == '\\' && in[i+1] == '\n' && i+2 < len(in) { 363 | i++ 364 | } 365 | i++ 366 | } 367 | str := in[:i] 368 | 369 | var c Comment 370 | c.Directive = true 371 | c.Span.Start = lx.pos() 372 | lx.skip(i) 373 | c.Span.End = lx.pos() 374 | if strings.Contains(str, "\n") { 375 | c.Text = "/*\n" + str + "\n*/" 376 | } else { 377 | c.Text = "// " + str 378 | } 379 | lx.comments = append(lx.comments, c) 380 | goto Restart 381 | 382 | case 'L': 383 | if in[1] != '\'' && in[1] != '"' { 384 | break // goes to alpha case after switch 385 | } 386 | i = 1 387 | fallthrough 388 | 389 | case '"', '\'': 390 | q := in[i] 391 | i++ // for the quote 392 | for ; in[i] != q; i++ { 393 | if in[i] == '\n' { 394 | what := "string" 395 | if q == '\'' { 396 | what = "character" 397 | } 398 | lx.Errorf("unterminated %s constant", what) 399 | } 400 | if in[i] == '\\' { 401 | i++ 402 | } 403 | } 404 | i++ // for the quote 405 | lx.sym(i) 406 | yy.str = lx.tok 407 | if q == '"' { 408 | return tokString 409 | } else { 410 | return tokLitChar 411 | } 412 | 413 | case '.': 414 | if in[1] < '0' || '9' < in[1] { 415 | if in[1] == '.' && in[2] == '.' { 416 | lx.token(3) 417 | return tokDotDotDot 418 | } 419 | lx.token(1) 420 | return int(c) 421 | } 422 | fallthrough 423 | 424 | case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 425 | for '0' <= in[i] && in[i] <= '9' || in[i] == '.' || 'A' <= in[i] && in[i] <= 'Z' || 'a' <= in[i] && in[i] <= 'z' { 426 | i++ 427 | } 428 | lx.sym(i) 429 | yy.str = lx.tok 430 | return tokNumber 431 | 432 | case '/': 433 | switch in[1] { 434 | case '*': 435 | i := 2 436 | for ; ; i++ { 437 | if i+2 <= len(in) && in[i] == '*' && in[i+1] == '/' { 438 | i += 2 439 | break 440 | } 441 | if i >= len(in) { 442 | lx.Errorf("unterminated /* comment") 443 | return tokError 444 | } 445 | } 446 | lx.comment(i) 447 | goto Restart 448 | 449 | case '/': 450 | for in[i] != '\n' { 451 | i++ 452 | } 453 | lx.comment(i) 454 | goto Restart 455 | } 456 | fallthrough 457 | 458 | case '~', '*', '(', ')', '[', ']', '{', '}', '?', ':', ';', ',', '%', '^', '!', '=', '<', '>', '+', '-', '&', '|': 459 | if c == '-' && in[1] == '>' { 460 | lx.token(2) 461 | return tokArrow 462 | } 463 | if in[1] == '=' && tokEq[c] != 0 { 464 | lx.token(2) 465 | return int(tokEq[c]) 466 | } 467 | if in[1] == c && tokTok[c] != 0 { 468 | if in[2] == '=' && tokTokEq[c] != 0 { 469 | lx.token(3) 470 | return int(tokTokEq[c]) 471 | } 472 | lx.token(2) 473 | return int(tokTok[c]) 474 | } 475 | lx.token(1) 476 | return int(c) 477 | } 478 | 479 | if isalpha(c) { 480 | for isalpha(in[i]) { 481 | i++ 482 | } 483 | lx.sym(i) 484 | switch lx.tok { 485 | case "Adr": 486 | lx.tok = "Addr" 487 | case "union": 488 | lx.tok = "struct" 489 | } 490 | yy.str = lx.tok 491 | if t := tokId[lx.tok]; t != 0 { 492 | return int(t) 493 | } 494 | yy.decl = lx.lookupDecl(lx.tok) 495 | if yy.decl != nil && yy.decl.Storage&Typedef != 0 { 496 | t := yy.decl.Type 497 | for t.Kind == TypedefType && t.Base != nil { 498 | t = t.Base 499 | } 500 | yy.typ = &Type{Kind: TypedefType, Name: yy.str, Base: t, TypeDecl: yy.decl} 501 | return tokTypeName 502 | } 503 | if yy.decl == nil && lx.typeNames[lx.tok] { 504 | yy.typ = &Type{Kind: TypedefType, Name: yy.str, Base: IntType} 505 | return tokTypeName 506 | } 507 | if lx.tok == "EXTERN" { 508 | goto Restart 509 | } 510 | return tokName 511 | } 512 | 513 | lx.Errorf("unexpected input byte %#02x (%c)", c, c) 514 | return tokError 515 | } 516 | 517 | func (lx *lexer) Error(s string) { 518 | if strings.Contains(s, " near ") { 519 | lx.Errorf("%s", s) 520 | return 521 | } 522 | sym := lx.lastsym 523 | if lx.lastsym2 != "" { 524 | sym = lx.lastsym2 525 | } 526 | lx.Errorf("%s near %s", s, sym) 527 | } 528 | 529 | func (lx *lexer) Errorf(format string, args ...interface{}) { 530 | lx.errors = append(lx.errors, fmt.Sprintf("%s: %s", lx.span(), fmt.Sprintf(format, args...))) 531 | } 532 | 533 | type Pos struct { 534 | File string 535 | Line int 536 | Byte int 537 | } 538 | 539 | type Span struct { 540 | Start Pos 541 | End Pos 542 | } 543 | 544 | func (l Span) String() string { 545 | return fmt.Sprintf("%s:%d", l.Start.File, l.Start.Line) 546 | } 547 | 548 | type Comment struct { 549 | Span 550 | Text string 551 | Suffix bool 552 | After bool 553 | Directive bool 554 | } 555 | 556 | func (c Comment) GetSpan() Span { 557 | return c.Span 558 | } 559 | 560 | var tokEq = [256]int32{ 561 | '*': tokMulEq, 562 | '/': tokDivEq, 563 | '+': tokAddEq, 564 | '-': tokSubEq, 565 | '%': tokModEq, 566 | '^': tokXorEq, 567 | '!': tokNotEq, 568 | '=': tokEqEq, 569 | '<': tokLtEq, 570 | '>': tokGtEq, 571 | '&': tokAndEq, 572 | '|': tokOrEq, 573 | } 574 | 575 | var tokTok = [256]int32{ 576 | '<': tokLsh, 577 | '>': tokRsh, 578 | '=': tokEqEq, 579 | '+': tokInc, 580 | '-': tokDec, 581 | '&': tokAndAnd, 582 | '|': tokOrOr, 583 | } 584 | 585 | var tokTokEq = [256]int32{ 586 | '<': tokLshEq, 587 | '>': tokRshEq, 588 | } 589 | 590 | var tokId = map[string]int32{ 591 | "auto": tokAuto, 592 | "break": tokBreak, 593 | "case": tokCase, 594 | "char": tokChar, 595 | "const": tokConst, 596 | "continue": tokContinue, 597 | "default": tokDefault, 598 | "do": tokDo, 599 | "double": tokDouble, 600 | "else": tokElse, 601 | "enum": tokEnum, 602 | "extern": tokExtern, 603 | "float": tokFloat, 604 | "for": tokFor, 605 | "goto": tokGoto, 606 | "if": tokIf, 607 | "inline": tokInline, 608 | "int": tokInt, 609 | "long": tokLong, 610 | "offsetof": tokOffsetof, 611 | "register": tokRegister, 612 | "return": tokReturn, 613 | "short": tokShort, 614 | "signed": tokSigned, 615 | "sizeof": tokSizeof, 616 | "static": tokStatic, 617 | "struct": tokStruct, 618 | "switch": tokSwitch, 619 | "typedef": tokTypedef, 620 | "union": tokUnion, 621 | "unsigned": tokUnsigned, 622 | "va_arg": tokVaArg, 623 | "void": tokVoid, 624 | "volatile": tokVolatile, 625 | "while": tokWhile, 626 | 627 | "ARGBEGIN": tokARGBEGIN, 628 | "ARGEND": tokARGEND, 629 | "AUTOLIB": tokAUTOLIB, 630 | "USED": tokUSED, 631 | "SET": tokSET, 632 | } 633 | 634 | // Comment assignment. 635 | // We build two lists of all subexpressions, preorder and postorder. 636 | // The preorder list is ordered by start location, with outer expressions first. 637 | // The postorder list is ordered by end location, with outer expressions last. 638 | // We use the preorder list to assign each whole-line comment to the syntax 639 | // immediately following it, and we use the postorder list to assign each 640 | // end-of-line comment to the syntax immediately preceding it. 641 | 642 | // enum walks the expression adding it and its subexpressions to the pre list. 643 | // The order may not reflect the order in the input. 644 | func (lx *lexer) enum(x Syntax) { 645 | switch x := x.(type) { 646 | default: 647 | panic(fmt.Errorf("order: unexpected type %T", x)) 648 | case nil: 649 | return 650 | case *Expr: 651 | if x == nil { 652 | return 653 | } 654 | lx.enum(x.Left) 655 | lx.enum(x.Right) 656 | for _, y := range x.List { 657 | lx.enum(y) 658 | } 659 | case *Init: 660 | if x == nil { 661 | return 662 | } 663 | lx.enum(x.Expr) 664 | for _, y := range x.Braced { 665 | lx.enum(y) 666 | } 667 | case *Prog: 668 | if x == nil { 669 | return 670 | } 671 | for _, y := range x.Decls { 672 | lx.enum(y) 673 | } 674 | case *Stmt: 675 | if x == nil { 676 | return 677 | } 678 | for _, y := range x.Labels { 679 | lx.enum(y) 680 | } 681 | lx.enum(x.Pre) 682 | lx.enum(x.Expr) 683 | lx.enum(x.Post) 684 | lx.enum(x.Body) 685 | lx.enum(x.Else) 686 | for _, y := range x.Block { 687 | lx.enum(y) 688 | } 689 | case *Label: 690 | // ok 691 | case *Decl: 692 | if x == nil { 693 | return 694 | } 695 | if lx.enumSeen[x] { 696 | return 697 | } 698 | lx.enumSeen[x] = true 699 | lx.enum(x.Type) 700 | lx.enum(x.Init) 701 | lx.enum(x.Body) 702 | case *Type: 703 | if x == nil { 704 | return 705 | } 706 | lx.enum(x.Base) 707 | for _, y := range x.Decls { 708 | lx.enum(y) 709 | } 710 | return // do not record type itself, just inner decls 711 | } 712 | lx.pre = append(lx.pre, x) 713 | } 714 | 715 | func (lx *lexer) order(prog *Prog) { 716 | lx.enumSeen = make(map[interface{}]bool) 717 | lx.enum(prog) 718 | sort.Sort(byStart(lx.pre)) 719 | lx.post = make([]Syntax, len(lx.pre)) 720 | copy(lx.post, lx.pre) 721 | sort.Sort(byEnd(lx.post)) 722 | } 723 | 724 | type byStart []Syntax 725 | 726 | func (x byStart) Len() int { return len(x) } 727 | func (x byStart) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 728 | func (x byStart) Less(i, j int) bool { 729 | pi := x[i].GetSpan() 730 | pj := x[j].GetSpan() 731 | // Order by start byte, leftmost first, 732 | // and break ties by choosing outer before inner. 733 | if pi.Start.Byte != pj.Start.Byte { 734 | return pi.Start.Byte < pj.Start.Byte 735 | } 736 | return pi.End.Byte > pj.End.Byte 737 | } 738 | 739 | type byEnd []Syntax 740 | 741 | func (x byEnd) Len() int { return len(x) } 742 | func (x byEnd) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 743 | func (x byEnd) Less(i, j int) bool { 744 | pi := x[i].GetSpan() 745 | pj := x[j].GetSpan() 746 | // Order by end byte, leftmost first, 747 | // and break ties by choosing inner before outer. 748 | if pi.End.Byte != pj.End.Byte { 749 | return pi.End.Byte < pj.End.Byte 750 | } 751 | return pi.Start.Byte > pj.Start.Byte 752 | } 753 | 754 | // assignComments attaches comments to nearby syntax. 755 | func (lx *lexer) assignComments() { 756 | // Generate preorder and postorder lists. 757 | lx.order(lx.prog) 758 | 759 | // Split into whole-line comments and suffix comments. 760 | var line, suffix []Comment 761 | for _, com := range lx.comments { 762 | if com.Suffix { 763 | suffix = append(suffix, com) 764 | } else { 765 | line = append(line, com) 766 | } 767 | } 768 | 769 | // Assign line comments to syntax immediately following. 770 | for _, x := range lx.pre { 771 | start := x.GetSpan().Start 772 | xcom := x.GetComments() 773 | for len(line) > 0 && line[0].Start.Byte <= start.Byte { 774 | xcom.Before = append(xcom.Before, line[0]) 775 | line = line[1:] 776 | } 777 | } 778 | 779 | // Remaining line comments go at end of file. 780 | lx.prog.Comments.After = append(lx.prog.Comments.After, line...) 781 | 782 | // Assign suffix comments to syntax immediately before. 783 | for i := len(lx.post) - 1; i >= 0; i-- { 784 | x := lx.post[i] 785 | 786 | // Do not assign suffix comments to call, list, end-of-list, or whole file. 787 | // Instead assign them to the last argument, element, or rule. 788 | /* 789 | switch x.(type) { 790 | case *CallExpr, *ListExpr, *End, *File: 791 | continue 792 | } 793 | */ 794 | 795 | // Do not assign suffix comments to something that starts 796 | // on an earlier line, so that in 797 | // 798 | // tags = [ "a", 799 | // "b" ], # comment 800 | // 801 | // we assign the comment to "b" and not to tags = [ ... ]. 802 | span := x.GetSpan() 803 | start, end := span.Start, span.End 804 | if start.Line != end.Line { 805 | continue 806 | } 807 | xcom := x.GetComments() 808 | for len(suffix) > 0 && end.Byte <= suffix[len(suffix)-1].Start.Byte { 809 | xcom.Suffix = append(xcom.Suffix, suffix[len(suffix)-1]) 810 | suffix = suffix[:len(suffix)-1] 811 | } 812 | } 813 | 814 | // We assigned suffix comments in reverse. 815 | // If multiple suffix comments were appended to the same 816 | // expression node, they are now in reverse. Fix that. 817 | for _, x := range lx.post { 818 | reverseComments(x.GetComments().Suffix) 819 | } 820 | 821 | // Remaining suffix comments go at beginning of file. 822 | lx.prog.Comments.Before = append(lx.prog.Comments.Before, suffix...) 823 | } 824 | 825 | // reverseComments reverses the []Comment list. 826 | func reverseComments(list []Comment) { 827 | for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 { 828 | list[i], list[j] = list[j], list[i] 829 | } 830 | } 831 | -------------------------------------------------------------------------------- /c2gofmt/internal/cc/parse.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package cc 6 | 7 | import ( 8 | "fmt" 9 | "io" 10 | "io/ioutil" 11 | "strings" 12 | ) 13 | 14 | func Read(name string, r io.Reader, types []string) (*Prog, error) { 15 | return ReadMany([]string{name}, []io.Reader{r}, types) 16 | } 17 | 18 | func ReadMany(names []string, readers []io.Reader, types []string) (*Prog, error) { 19 | lx := &lexer{byte: 1e6} 20 | lx.typeNames = make(map[string]bool) 21 | for _, typ := range types { 22 | lx.typeNames[typ] = true 23 | } 24 | var prog *Prog 25 | for i, name := range names { 26 | if lx.includeSeen[name] != nil { 27 | continue 28 | } 29 | r := readers[i] 30 | data, err := ioutil.ReadAll(r) 31 | if err != nil { 32 | return nil, err 33 | } 34 | data = append(data, '\n') 35 | lx.start = startProg 36 | lx.lexInput = lexInput{ 37 | input: string(data), 38 | file: name, 39 | lineno: 1, 40 | } 41 | lx.parse() 42 | if lx.errors != nil { 43 | return nil, fmt.Errorf("%v", lx.errors[0]) 44 | } 45 | if prog == nil { 46 | prog = lx.prog 47 | } else { 48 | prog.Span.End = lx.prog.Span.End 49 | prog.Decls = append(prog.Decls, lx.prog.Decls...) 50 | } 51 | lx.prog = nil 52 | for sc := lx.scope; sc != nil; sc = sc.Next { 53 | for name, decl := range sc.Decl { 54 | if decl.Storage&Static != 0 || (decl.Storage&Typedef != 0 && strings.HasSuffix(decl.Span.Start.File, ".c")) { 55 | delete(sc.Decl, name) 56 | } 57 | } 58 | for name, typ := range sc.Tag { 59 | if strings.HasSuffix(typ.Span.Start.File, ".c") { 60 | delete(sc.Tag, name) 61 | } 62 | } 63 | } 64 | } 65 | lx.prog = prog 66 | lx.assignComments() 67 | if lx.errors != nil { 68 | return nil, fmt.Errorf("%v", strings.Join(lx.errors, "\n")) 69 | } 70 | 71 | removeDuplicates(lx.prog) 72 | 73 | return lx.prog, nil 74 | } 75 | 76 | func ParseExpr(str string) (*Expr, error) { 77 | lx := &lexer{ 78 | start: startExpr, 79 | lexInput: lexInput{ 80 | input: str + "\n", 81 | file: "", 82 | lineno: 1, 83 | }, 84 | } 85 | lx.parse() 86 | if lx.errors != nil { 87 | return nil, fmt.Errorf("parsing expression %#q: %v", str, lx.errors[0]) 88 | } 89 | return lx.expr, nil 90 | } 91 | 92 | type Prog struct { 93 | SyntaxInfo 94 | Decls []*Decl 95 | } 96 | 97 | // removeDuplicates drops the duplicated declarations 98 | // caused by forward decls from prog. 99 | // It keeps the _last_ of each given declaration, 100 | // assuming that's the complete one. 101 | // This heuristic tends to preserve something like 102 | // source order. 103 | // It would be defeated by someone writing a "forward" 104 | // declaration following the real definition. 105 | func removeDuplicates(prog *Prog) { 106 | count := map[*Decl]int{} 107 | for _, d := range prog.Decls { 108 | count[d]++ 109 | } 110 | var out []*Decl 111 | for _, d := range prog.Decls { 112 | count[d]-- 113 | if count[d] == 0 { 114 | out = append(out, d) 115 | } 116 | } 117 | prog.Decls = out 118 | } 119 | -------------------------------------------------------------------------------- /c2gofmt/internal/cc/print.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package cc 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "strings" 11 | ) 12 | 13 | type special int 14 | 15 | const ( 16 | indent special = iota 17 | unindent 18 | untab 19 | newline 20 | ) 21 | 22 | type Printer struct { 23 | buf bytes.Buffer 24 | indent int 25 | html bool 26 | suffix []Comment // suffix comments to print at next newline 27 | hideComments bool 28 | } 29 | 30 | func (p *Printer) StartHTML() { 31 | p.buf.WriteString("
")
 32 | 	p.html = true
 33 | }
 34 | 
 35 | func (p *Printer) EndHTML() {
 36 | 	p.buf.WriteString("
") 37 | } 38 | 39 | func (p *Printer) Bytes() []byte { 40 | return p.buf.Bytes() 41 | } 42 | 43 | func (p *Printer) String() string { 44 | return p.buf.String() 45 | } 46 | 47 | type exprPrec struct { 48 | expr *Expr 49 | prec int 50 | } 51 | 52 | type nestBlock struct { 53 | stmt *Stmt 54 | more bool 55 | } 56 | 57 | type TypedName struct { 58 | Type *Type 59 | Name string 60 | } 61 | 62 | var htmlEscaper = strings.NewReplacer("<", "<", ">", ">", "&", "&") 63 | 64 | func (p *Printer) atBOL() bool { 65 | buf := p.buf.Bytes() 66 | i := len(buf) 67 | for i > 0 && (buf[i-1] == ' ' || buf[i-1] == '\t') { 68 | i-- 69 | } 70 | return i == 0 || buf[i-1] == '\n' 71 | } 72 | 73 | func (p *Printer) Print(args ...interface{}) { 74 | for _, arg := range args { 75 | switch arg := arg.(type) { 76 | default: 77 | fmt.Fprintf(&p.buf, "(?%T)", arg) 78 | case string: 79 | if p.html { 80 | htmlEscaper.WriteString(&p.buf, arg) 81 | } else { 82 | p.buf.WriteString(arg) 83 | } 84 | case exprPrec: 85 | p.printExpr(arg.expr, arg.prec) 86 | case *Expr: 87 | p.printExpr(arg, precLow) 88 | case *Prefix: 89 | p.printPrefix(arg) 90 | case *Init: 91 | p.printInit(arg) 92 | case *Prog: 93 | p.printProg(arg) 94 | case *Stmt: 95 | p.printStmt(arg) 96 | case *Type: 97 | p.printType(arg, "") 98 | case *Decl: 99 | p.printDecl(arg) 100 | case TypedName: 101 | p.printType(arg.Type, arg.Name) 102 | case Storage: 103 | p.Print(arg.String()) 104 | case []Comment: 105 | for _, com := range arg { 106 | p.Print(com) 107 | } 108 | case Comment: 109 | if p.hideComments { 110 | break 111 | } 112 | com := arg 113 | if com.Suffix { 114 | p.suffix = append(p.suffix, com) 115 | } else { 116 | if !p.atBOL() { 117 | p.Print(newline) 118 | } 119 | for _, line := range strings.Split(com.Text, "\n") { 120 | p.Print("/*before*/ ", line, newline) 121 | } 122 | } 123 | case nestBlock: 124 | if arg.stmt.Op == Block { 125 | p.Print(" ", arg.stmt) 126 | } else { 127 | p.Print(indent, newline, arg.stmt, unindent) 128 | if arg.more { 129 | p.Print(newline) 130 | } 131 | } 132 | case special: 133 | switch arg { 134 | default: 135 | fmt.Fprintf(&p.buf, "(?special:%d)", arg) 136 | case indent: 137 | p.indent++ 138 | case unindent: 139 | p.indent-- 140 | case untab: 141 | b := p.buf.Bytes() 142 | if len(b) > 0 && b[len(b)-1] == '\t' { 143 | p.buf.Truncate(len(b) - 1) 144 | } 145 | case newline: 146 | if len(p.suffix) > 0 { 147 | p.Print(" /*suffix*/") 148 | for _, com := range p.suffix { 149 | p.Print(" ", com.Text) 150 | } 151 | } 152 | p.suffix = p.suffix[:0] 153 | p.buf.WriteString("\n") 154 | for i := 0; i < p.indent; i++ { 155 | p.buf.WriteByte('\t') 156 | } 157 | } 158 | } 159 | } 160 | } 161 | 162 | const ( 163 | precNone = iota 164 | precArrow 165 | precAddr 166 | precMul 167 | precAdd 168 | precLsh 169 | precLt 170 | precEqEq 171 | precAnd 172 | precXor 173 | precOr 174 | precAndAnd 175 | precOrOr 176 | precCond 177 | precEq 178 | precComma 179 | precLow 180 | ) 181 | 182 | var opPrec = []int{ 183 | Add: precAdd, 184 | AddEq: precEq, 185 | Addr: precAddr, 186 | And: precAnd, 187 | AndAnd: precAndAnd, 188 | AndEq: precEq, 189 | Arrow: precArrow, 190 | Call: precArrow, 191 | Cast: precAddr, 192 | CastInit: precAddr, 193 | Comma: precComma, 194 | Cond: precCond, 195 | Div: precMul, 196 | DivEq: precEq, 197 | Dot: precArrow, 198 | Eq: precEq, 199 | EqEq: precEqEq, 200 | Gt: precLt, 201 | GtEq: precLt, 202 | Index: precArrow, 203 | Indir: precAddr, 204 | Lsh: precLsh, 205 | LshEq: precEq, 206 | Lt: precLt, 207 | LtEq: precLt, 208 | Minus: precAddr, 209 | Mod: precMul, 210 | ModEq: precEq, 211 | Mul: precMul, 212 | MulEq: precEq, 213 | Name: precNone, 214 | Not: precAddr, 215 | NotEq: precEqEq, 216 | Number: precNone, 217 | Offsetof: precAddr, 218 | Or: precOr, 219 | OrEq: precEq, 220 | OrOr: precOrOr, 221 | Paren: precLow, 222 | Plus: precAddr, 223 | PostDec: precAddr, 224 | PostInc: precAddr, 225 | PreDec: precAddr, 226 | PreInc: precAddr, 227 | Rsh: precLsh, 228 | RshEq: precEq, 229 | SizeofExpr: precAddr, 230 | SizeofType: precAddr, 231 | String: precNone, 232 | Sub: precAdd, 233 | SubEq: precEq, 234 | Twid: precAddr, 235 | VaArg: precAddr, 236 | Xor: precXor, 237 | XorEq: precEq, 238 | } 239 | 240 | var opStr = []string{ 241 | Add: "+", 242 | AddEq: "+=", 243 | Addr: "&", 244 | And: "&", 245 | AndAnd: "&&", 246 | AndEq: "&=", 247 | Div: "/", 248 | DivEq: "/=", 249 | Eq: "=", 250 | EqEq: "==", 251 | Gt: ">", 252 | GtEq: ">=", 253 | Indir: "*", 254 | Lsh: "<<", 255 | LshEq: "<<=", 256 | Lt: "<", 257 | LtEq: "<=", 258 | Minus: "-", 259 | Mod: "%", 260 | ModEq: "%=", 261 | Mul: "*", 262 | MulEq: "*=", 263 | Not: "!", 264 | NotEq: "!=", 265 | Or: "|", 266 | OrEq: "|=", 267 | OrOr: "||", 268 | Plus: "+", 269 | PreDec: "--", 270 | PreInc: "++", 271 | Rsh: ">>", 272 | RshEq: ">>=", 273 | Sub: "-", 274 | SubEq: "-=", 275 | Twid: "~", 276 | Xor: "^", 277 | XorEq: "^=", 278 | SizeofExpr: "sizeof ", 279 | } 280 | 281 | func (p *Printer) printExpr(x *Expr, prec int) { 282 | if x == nil { 283 | return 284 | } 285 | if p.html { 286 | fmt.Fprintf(&p.buf, "", x.Op, x.XType) 287 | defer fmt.Fprintf(&p.buf, "") 288 | } 289 | 290 | p.Print(x.Comments.Before) 291 | defer p.Print(x.Comments.Suffix, x.Comments.After) 292 | 293 | var newPrec int 294 | if 0 <= int(x.Op) && int(x.Op) < len(opPrec) { 295 | newPrec = opPrec[x.Op] 296 | } 297 | if prec < newPrec { 298 | p.Print("(") 299 | defer p.Print(")") 300 | } 301 | prec = newPrec 302 | 303 | var str string 304 | if 0 <= int(x.Op) && int(x.Op) < len(opStr) { 305 | str = opStr[x.Op] 306 | } 307 | if str != "" { 308 | if x.Right != nil { 309 | // binary operator 310 | if prec == precEq { 311 | // right associative 312 | p.Print(exprPrec{x.Left, prec - 1}, " ", str, " ", exprPrec{x.Right, prec}) 313 | } else { 314 | // left associative 315 | p.Print(exprPrec{x.Left, prec}, " ", str, " ", exprPrec{x.Right, prec - 1}) 316 | } 317 | } else { 318 | // unary operator 319 | if (x.Op == Plus || x.Op == Minus || x.Op == Addr) && x.Left.Op == x.Op || 320 | x.Op == Plus && x.Left.Op == PreInc || 321 | x.Op == Minus && x.Left.Op == PreDec { 322 | prec-- // force parenthesization +(+x) not ++x 323 | } 324 | p.Print(str, exprPrec{x.Left, prec}) 325 | } 326 | return 327 | } 328 | 329 | // special cases 330 | switch x.Op { 331 | default: 332 | p.Print(fmt.Sprintf("Expr(Op=%d)", x.Op)) 333 | 334 | case Arrow: 335 | p.Print(exprPrec{x.Left, prec}, "->", x.Text) 336 | 337 | case Call: 338 | p.Print(exprPrec{x.Left, precAddr}, "(") 339 | for i, y := range x.List { 340 | if i > 0 { 341 | p.Print(", ") 342 | } 343 | p.printExpr(y, precComma) 344 | } 345 | p.Print(")") 346 | 347 | case Cast: 348 | p.Print("(", x.Type, ")", exprPrec{x.Left, prec}) 349 | 350 | case CastInit: 351 | p.Print("(", x.Type, ")", x.Init) 352 | 353 | case Comma: 354 | for i, y := range x.List { 355 | if i > 0 { 356 | p.Print(", ") 357 | } 358 | p.printExpr(y, prec-1) 359 | } 360 | 361 | case Cond: 362 | p.Print(exprPrec{x.List[0], prec - 1}, " ? ", exprPrec{x.List[1], prec}, " : ", exprPrec{x.List[2], prec}) 363 | 364 | case Dot: 365 | p.Print(exprPrec{x.Left, prec}, ".", x.Text) 366 | 367 | case Index: 368 | p.Print(exprPrec{x.Left, prec}, "[", exprPrec{x.Right, precLow}, "]") 369 | 370 | case Name, Number: 371 | p.Print(x.Text) 372 | 373 | case String: 374 | for i, str := range x.Texts { 375 | if i > 0 { 376 | p.Print(" ") 377 | } 378 | p.Print(str) 379 | } 380 | 381 | case Offsetof: 382 | p.Print("offsetof(", x.Type, ", ", exprPrec{x.Left, precComma}, ")") 383 | 384 | case Paren: 385 | p.Print("(", exprPrec{x.Left, prec}, ")") 386 | 387 | case PostDec: 388 | p.Print(exprPrec{x.Left, prec}, "--") 389 | 390 | case PostInc: 391 | p.Print(exprPrec{x.Left, prec}, "++") 392 | 393 | case SizeofType: 394 | p.Print("sizeof(", x.Type, ")") 395 | 396 | case VaArg: 397 | p.Print("va_arg(", exprPrec{x.Left, precComma}, ", ", x.Type, ")") 398 | } 399 | } 400 | 401 | func (p *Printer) printPrefix(x *Prefix) { 402 | if x.Dot != "" { 403 | p.Print(".", x.Dot) 404 | } else { 405 | p.Print("[", x.Index, "]") 406 | } 407 | } 408 | 409 | func (p *Printer) printInit(x *Init) { 410 | p.Print(x.Comments.Before) 411 | defer p.Print(x.Comments.Suffix, x.Comments.After) 412 | 413 | if len(x.Prefix) > 0 { 414 | for _, pre := range x.Prefix { 415 | p.Print(pre) 416 | } 417 | p.Print(" = ") 418 | } 419 | if x.Expr != nil { 420 | p.printExpr(x.Expr, precComma) 421 | } else { 422 | nl := len(x.Braced) > 0 && x.Braced[0].Span.Start.Line != x.Braced[len(x.Braced)-1].Span.End.Line 423 | p.Print("{") 424 | if nl { 425 | p.Print(indent) 426 | } 427 | for i, y := range x.Braced { 428 | if i > 0 { 429 | p.Print(",") 430 | } 431 | if nl { 432 | p.Print(newline) 433 | } else if i > 0 { 434 | p.Print(" ") 435 | } 436 | p.Print(y) 437 | } 438 | if nl { 439 | p.Print(unindent, newline) 440 | } 441 | p.Print("}") 442 | } 443 | 444 | for _, com := range x.Comments.After { 445 | p.Print(com) 446 | } 447 | } 448 | 449 | func (p *Printer) printProg(x *Prog) { 450 | p.Print(x.Comments.Before) 451 | defer p.Print(x.Comments.Suffix, x.Comments.After) 452 | 453 | for _, decl := range x.Decls { 454 | p.Print(decl, newline) 455 | } 456 | } 457 | 458 | func (p *Printer) printStmt(x *Stmt) { 459 | if len(x.Labels) > 0 { 460 | p.Print(untab, unindent, x.Comments.Before, indent, "\t") 461 | for _, lab := range x.Labels { 462 | p.Print(untab, unindent, lab.Comments.Before, indent, "\t") 463 | p.Print(untab) 464 | switch { 465 | case lab.Name != "": 466 | p.Print(lab.Name) 467 | case lab.Expr != nil: 468 | p.Print("case ", lab.Expr) 469 | default: 470 | p.Print("default") 471 | } 472 | p.Print(":", lab.Comments.Suffix, newline) 473 | } 474 | } else { 475 | p.Print(x.Comments.Before) 476 | } 477 | defer p.Print(x.Comments.Suffix, x.Comments.After) 478 | 479 | switch x.Op { 480 | case ARGBEGIN: 481 | p.Print("ARGBEGIN{", indent, newline, x.Body, unindent, newline, "}ARGEND") 482 | 483 | case Block: 484 | p.Print("{", indent) 485 | for _, b := range x.Block { 486 | p.Print(newline, b) 487 | } 488 | p.Print(unindent, newline, "}") 489 | 490 | case Break: 491 | p.Print("break;") 492 | 493 | case Continue: 494 | p.Print("continue;") 495 | 496 | case Do: 497 | p.Print("do", nestBlock{x.Body, true}, " while(", x.Expr, ");") 498 | 499 | case Empty: 500 | p.Print(";") 501 | 502 | case For: 503 | p.Print("for(", x.Pre, ";") 504 | if x.Expr != nil { 505 | p.Print(" ") 506 | } 507 | p.Print(x.Expr, ";") 508 | if x.Post != nil { 509 | p.Print(" ") 510 | } 511 | p.Print(x.Post, ")", nestBlock{x.Body, false}) 512 | 513 | case If: 514 | p.Print("if(", x.Expr, ")", nestBlock{x.Body, x.Else != nil}) 515 | if x.Else != nil { 516 | if x.Body.Op == Block { 517 | p.Print(" ") 518 | } 519 | p.Print("else", nestBlock{x.Else, false}) 520 | } 521 | 522 | case Goto: 523 | p.Print("goto ", x.Text, ";") 524 | 525 | case Return: 526 | if x.Expr == nil { 527 | p.Print("return;") 528 | } else { 529 | p.Print("return ", x.Expr, ";") 530 | } 531 | 532 | case StmtDecl: 533 | p.Print(x.Decl, ";") 534 | 535 | case StmtExpr: 536 | p.Print(x.Expr, ";") 537 | 538 | case Switch: 539 | p.Print("switch(", x.Expr, ")", nestBlock{x.Body, false}) 540 | 541 | case While: 542 | p.Print("while(", x.Expr, ")", nestBlock{x.Body, false}) 543 | } 544 | } 545 | 546 | func (p *Printer) printType(x *Type, name string) { 547 | // Shouldn't happen but handle in case it does. 548 | p.Print(x.Comments.Before) 549 | defer p.Print(x.Comments.Suffix, x.Comments.After) 550 | 551 | switch x.Kind { 552 | case Ptr: 553 | p.printType(x.Base, "*"+name) 554 | case Array: 555 | if strings.HasPrefix(name, "*") { 556 | name = "(" + name + ")" 557 | } 558 | if x.Width == nil { 559 | p.printType(x.Base, name+"[]") 560 | } else { 561 | p.printType(x.Base, name+"["+x.Width.String()+"]") 562 | } 563 | case Func: 564 | var pp Printer 565 | if strings.HasPrefix(name, "*") { 566 | name = "(" + name + ")" 567 | } 568 | pp.Print(name, "(") 569 | for i, decl := range x.Decls { 570 | if i > 0 { 571 | pp.Print(", ") 572 | } 573 | pp.Print(decl) 574 | } 575 | pp.Print(")") 576 | p.printType(x.Base, pp.String()) 577 | 578 | default: 579 | p.Print(x.String()) 580 | i := 0 581 | for i < len(name) && name[i] == '*' { 582 | i++ 583 | } 584 | if i < len(name) && name[i] != '\n' { 585 | p.Print(" ") 586 | } 587 | p.Print(name) 588 | } 589 | } 590 | 591 | func (p *Printer) printDecl(x *Decl) { 592 | p.Print(x.Comments.Before) 593 | defer p.Print(x.Comments.Suffix, x.Comments.After) 594 | 595 | if x.Storage != 0 { 596 | p.Print(x.Storage, " ") 597 | } 598 | if x.Type == nil { 599 | p.Print(x.Name) 600 | } else { 601 | name := x.Name 602 | if x.Type.Kind == Func && x.Body != nil { 603 | name = "\n" + name 604 | } 605 | p.Print(TypedName{x.Type, name}) 606 | if x.Name == "" { 607 | switch x.Type.Kind { 608 | case Struct, Union, Enum: 609 | p.Print(" {", indent) 610 | for _, decl := range x.Type.Decls { 611 | p.Print(newline, decl) 612 | } 613 | p.Print(unindent, newline, "}") 614 | } 615 | } 616 | } 617 | if x.Init != nil { 618 | p.Print(" = ", x.Init) 619 | } 620 | if x.Body != nil { 621 | p.Print(newline, x.Body) 622 | } 623 | } 624 | -------------------------------------------------------------------------------- /c2gofmt/internal/cc/print_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package cc 6 | 7 | import "testing" 8 | 9 | var exprTests = []string{ 10 | "x", 11 | "123", 12 | "1.4", 13 | "'z'", 14 | `"abc" "def"`, 15 | "x + y", 16 | "x * y", 17 | "x / y", 18 | "x % y", 19 | "x << y", 20 | "x >> y", 21 | "x < y", 22 | "x > y", 23 | "x <= y", 24 | "x >= y", 25 | "x == y", 26 | "x != y", 27 | "x & y", 28 | "x ^ y", 29 | "x | y", 30 | "x && y", 31 | "x || y", 32 | "x ? y : z", 33 | "x = y", 34 | "x += y", 35 | "x -= y", 36 | "x *= y", 37 | "x /= y", 38 | "x %= y", 39 | "x <<= y", 40 | "x >>= y", 41 | "x &= y", 42 | "x ^= y", 43 | "x |= y", 44 | "*x", 45 | "&x", 46 | "+x", 47 | "-x", 48 | "!x", 49 | "~x", 50 | "++x", 51 | "--x", 52 | "sizeof x", 53 | "sizeof(int)", 54 | "offsetof(int, x)", 55 | "(int)x", 56 | "(int){}", 57 | "(int){x}", 58 | "(x, y, z)", 59 | "x, y, z", 60 | "f(x, y, z)", 61 | "x[y]", 62 | "x++", 63 | "x--", 64 | "va_arg(x, int)", 65 | } 66 | 67 | func TestPrintExpr(t *testing.T) { 68 | for _, str := range exprTests { 69 | x, err := ParseExpr(str) 70 | if err != nil { 71 | t.Errorf("%v", err) 72 | continue 73 | } 74 | out := x.String() 75 | if out != str { 76 | t.Errorf("ParseExpr(%#q).String() = %#q, want original input", str, out) 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /c2gofmt/internal/cc/stmt.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package cc 6 | 7 | type Stmt struct { 8 | SyntaxInfo 9 | Op StmtOp 10 | Pre *Expr 11 | Expr *Expr 12 | Post *Expr 13 | Decl *Decl 14 | Body *Stmt 15 | Else *Stmt 16 | Block []*Stmt 17 | Labels []*Label 18 | Text string 19 | Type *Type 20 | } 21 | 22 | //go:generate stringer -type StmtOp 23 | type StmtOp int 24 | 25 | const ( 26 | _ StmtOp = iota 27 | StmtDecl 28 | StmtExpr 29 | Empty 30 | Block 31 | ARGBEGIN 32 | Break 33 | Continue 34 | Do 35 | For 36 | If 37 | Goto 38 | Return 39 | Switch 40 | While 41 | ) 42 | 43 | type Label struct { 44 | SyntaxInfo 45 | Op LabelOp 46 | Expr *Expr 47 | Name string 48 | } 49 | 50 | type LabelOp int 51 | 52 | const ( 53 | _ LabelOp = iota 54 | Case 55 | Default 56 | LabelName 57 | ) 58 | -------------------------------------------------------------------------------- /c2gofmt/internal/cc/stmtop_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type StmtOp"; DO NOT EDIT. 2 | 3 | package cc 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[StmtDecl-1] 12 | _ = x[StmtExpr-2] 13 | _ = x[Empty-3] 14 | _ = x[Block-4] 15 | _ = x[ARGBEGIN-5] 16 | _ = x[Break-6] 17 | _ = x[Continue-7] 18 | _ = x[Do-8] 19 | _ = x[For-9] 20 | _ = x[If-10] 21 | _ = x[Goto-11] 22 | _ = x[Return-12] 23 | _ = x[Switch-13] 24 | _ = x[While-14] 25 | } 26 | 27 | const _StmtOp_name = "StmtDeclStmtExprEmptyBlockARGBEGINBreakContinueDoForIfGotoReturnSwitchWhile" 28 | 29 | var _StmtOp_index = [...]uint8{0, 8, 16, 21, 26, 34, 39, 47, 49, 52, 54, 58, 64, 70, 75} 30 | 31 | func (i StmtOp) String() string { 32 | i -= 1 33 | if i < 0 || i >= StmtOp(len(_StmtOp_index)-1) { 34 | return "StmtOp(" + strconv.FormatInt(int64(i+1), 10) + ")" 35 | } 36 | return _StmtOp_name[_StmtOp_index[i]:_StmtOp_index[i+1]] 37 | } 38 | -------------------------------------------------------------------------------- /c2gofmt/internal/cc/type.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package cc 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | ) 11 | 12 | var printf = fmt.Printf 13 | 14 | type Type struct { 15 | SyntaxInfo 16 | Kind TypeKind 17 | Qual TypeQual 18 | Base *Type 19 | Tag string 20 | Decls []*Decl 21 | Width *Expr 22 | Name string 23 | Slice bool // pointer should format as slice 24 | TypeDecl *Decl 25 | } 26 | 27 | type TypeKind int 28 | 29 | const ( 30 | _ TypeKind = iota 31 | Void 32 | Char 33 | Uchar 34 | Short 35 | Ushort 36 | Int 37 | Uint 38 | Long 39 | Ulong 40 | Longlong 41 | Ulonglong 42 | Float 43 | Double 44 | Enum 45 | Ptr 46 | Struct 47 | Union 48 | Array 49 | Func 50 | TypedefType 51 | ) 52 | 53 | var typeKindString = []string{ 54 | Void: "void", 55 | Char: "char", 56 | Uchar: "uchar", 57 | Short: "short", 58 | Ushort: "ushort", 59 | Int: "int", 60 | Uint: "uint", 61 | Long: "long", 62 | Ulong: "ulong", 63 | Longlong: "longlong", 64 | Ulonglong: "ulonglong", 65 | Float: "float", 66 | Double: "double", 67 | Ptr: "pointer", 68 | Struct: "struct", 69 | Union: "union", 70 | Enum: "enum", 71 | Array: "array", 72 | Func: "func", 73 | TypedefType: "", 74 | } 75 | 76 | func (k TypeKind) String() string { 77 | if 0 <= int(k) && int(k) <= len(typeKindString) && typeKindString[k] != "" { 78 | return typeKindString[k] 79 | } 80 | return fmt.Sprintf("TypeKind(%d)", k) 81 | } 82 | 83 | type TypeQual int 84 | 85 | const ( 86 | Const TypeQual = 1 << iota 87 | Volatile 88 | ) 89 | 90 | func (q TypeQual) String() string { 91 | s := "" 92 | if q&Const != 0 { 93 | s += "const " 94 | } 95 | if q&Volatile != 0 { 96 | s += "volatile " 97 | } 98 | if s == "" { 99 | return "" 100 | } 101 | return s[:len(s)-1] 102 | } 103 | 104 | type Storage int 105 | 106 | const ( 107 | Auto Storage = 1 << iota 108 | Static 109 | Extern 110 | Typedef 111 | Register 112 | Inline 113 | ) 114 | 115 | func (c Storage) String() string { 116 | s := "" 117 | if c&Auto != 0 { 118 | s += "auto " 119 | } 120 | if c&Static != 0 { 121 | s += "static " 122 | } 123 | if c&Extern != 0 { 124 | s += "extern " 125 | } 126 | if c&Typedef != 0 { 127 | s += "typedef " 128 | } 129 | if c&Register != 0 { 130 | s += "register " 131 | } 132 | if c&Inline != 0 { 133 | s += "inline " 134 | } 135 | if s == "" { 136 | return "" 137 | } 138 | return s[:len(s)-1] 139 | } 140 | 141 | var ( 142 | CharType = newType(Char) 143 | UcharType = newType(Uchar) 144 | ShortType = newType(Short) 145 | UshortType = newType(Ushort) 146 | IntType = newType(Int) 147 | UintType = newType(Uint) 148 | LongType = newType(Long) 149 | UlongType = newType(Ulong) 150 | LonglongType = newType(Longlong) 151 | UlonglongType = newType(Ulonglong) 152 | FloatType = newType(Float) 153 | DoubleType = newType(Double) 154 | VoidType = newType(Void) 155 | BoolType = &Type{Kind: TypedefType, Name: "bool", Base: IntType} 156 | ) 157 | 158 | type typeOp int 159 | 160 | const ( 161 | tChar typeOp = 1 << iota 162 | tShort 163 | tInt 164 | tLong 165 | tSigned 166 | tUnsigned 167 | tFloat 168 | tDouble 169 | tVoid 170 | tLonglong 171 | ) 172 | 173 | var builtinTypes = map[typeOp]*Type{ 174 | tChar: CharType, 175 | tChar | tSigned: CharType, 176 | tChar | tUnsigned: UcharType, 177 | tShort: ShortType, 178 | tShort | tSigned: ShortType, 179 | tShort | tUnsigned: UshortType, 180 | tShort | tInt: ShortType, 181 | tShort | tSigned | tInt: ShortType, 182 | tShort | tUnsigned | tInt: UshortType, 183 | tInt: IntType, 184 | tInt | tSigned: IntType, 185 | tInt | tUnsigned: UintType, 186 | tLong: LongType, 187 | tLong | tSigned: LongType, 188 | tLong | tUnsigned: UlongType, 189 | tLong | tInt: LongType, 190 | tLong | tSigned | tInt: LongType, 191 | tLong | tUnsigned | tInt: UlongType, 192 | tLonglong: LonglongType, 193 | tLonglong | tSigned: LonglongType, 194 | tLonglong | tUnsigned: UlonglongType, 195 | tLonglong | tInt: LonglongType, 196 | tLonglong | tSigned | tInt: LonglongType, 197 | tLonglong | tUnsigned | tInt: UlonglongType, 198 | tFloat: FloatType, 199 | tDouble: DoubleType, 200 | tVoid: VoidType, 201 | } 202 | 203 | func splitTypeWords(ws []string) (c Storage, q TypeQual, ty *Type) { 204 | // Could check for doubled words in general, 205 | // like const const, but no one cares. 206 | var t typeOp 207 | var ts []string 208 | for _, w := range ws { 209 | switch w { 210 | case "const": 211 | q |= Const 212 | case "volatile": 213 | q |= Volatile 214 | case "auto": 215 | c |= Auto 216 | case "static": 217 | c |= Static 218 | case "extern": 219 | c |= Extern 220 | case "typedef": 221 | c |= Typedef 222 | case "register": 223 | c |= Register 224 | case "inline": 225 | c |= Inline 226 | case "char": 227 | t |= tChar 228 | ts = append(ts, w) 229 | case "short": 230 | t |= tShort 231 | ts = append(ts, w) 232 | case "int": 233 | t |= tInt 234 | ts = append(ts, w) 235 | case "long": 236 | if t&tLong != 0 { 237 | t ^= tLonglong | tLong 238 | } else { 239 | t |= tLong 240 | } 241 | ts = append(ts, w) 242 | case "signed": 243 | t |= tSigned 244 | ts = append(ts, w) 245 | case "unsigned": 246 | t |= tUnsigned 247 | ts = append(ts, w) 248 | case "float": 249 | t |= tFloat 250 | ts = append(ts, w) 251 | case "double": 252 | t |= tDouble 253 | ts = append(ts, w) 254 | case "void": 255 | t |= tVoid 256 | ts = append(ts, w) 257 | } 258 | } 259 | 260 | if t == 0 || t == tUnsigned || t == tLong { 261 | t |= tInt 262 | } 263 | 264 | ty = builtinTypes[t] 265 | if ty == nil { 266 | fmt.Printf("unsupported type %q\n", strings.Join(ts, " ")) 267 | } 268 | 269 | return c, q, builtinTypes[t] 270 | } 271 | 272 | func newType(k TypeKind) *Type { 273 | return &Type{Kind: k} 274 | } 275 | 276 | func (t *Type) String() string { 277 | if t == nil { 278 | return "" 279 | } 280 | switch t.Kind { 281 | default: 282 | return t.Kind.String() 283 | case TypedefType: 284 | if t.Name == "" { 285 | return "missing_typedef_name" 286 | } 287 | return t.Name 288 | case Ptr: 289 | return t.Base.String() + "*" 290 | case Struct, Union, Enum: 291 | if t.Tag == "" { 292 | return t.Kind.String() 293 | } 294 | return t.Kind.String() + " " + t.Tag 295 | case Array: 296 | return t.Base.String() + "[]" 297 | case Func: 298 | s := "func(" 299 | for i, d := range t.Decls { 300 | if i > 0 { 301 | s += ", " 302 | } 303 | s += d.Name + " " + d.Type.String() 304 | } 305 | if t.Base == t { 306 | s += ") SELF" 307 | } else { 308 | s += ") " + t.Base.String() 309 | } 310 | return s 311 | } 312 | } 313 | 314 | type Decl struct { 315 | SyntaxInfo 316 | Blank bool 317 | Name string 318 | Type *Type 319 | Storage Storage 320 | Init *Init 321 | Body *Stmt 322 | 323 | XOuter *Decl 324 | CurFn *Decl 325 | OuterType *Type 326 | GoPackage string 327 | } 328 | 329 | func (d *Decl) String() string { 330 | if d == nil { 331 | return "nil Decl" 332 | } 333 | return fmt.Sprintf("Decl{%s, %s}", d.Name, d.Type) 334 | } 335 | -------------------------------------------------------------------------------- /c2gofmt/internal/cc/typecheck.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Scoping and type checking. 6 | // C99 standard: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf 7 | 8 | package cc 9 | 10 | import ( 11 | "fmt" 12 | "strconv" 13 | "strings" 14 | ) 15 | 16 | type Scope struct { 17 | Decl map[string]*Decl 18 | Tag map[string]*Type 19 | Next *Scope 20 | } 21 | 22 | func (lx *lexer) pushDecl(decl *Decl) { 23 | sc := lx.scope 24 | if sc == nil { 25 | panic("no scope") 26 | } 27 | if decl.Name == "" { 28 | return 29 | } 30 | if sc.Decl == nil { 31 | sc.Decl = make(map[string]*Decl) 32 | } 33 | sc.Decl[decl.Name] = decl 34 | if hdr := lx.declSave; hdr != nil && sc.Next == nil { 35 | hdr.decls = append(hdr.decls, decl) 36 | } 37 | } 38 | 39 | func (lx *lexer) lookupDecl(name string) *Decl { 40 | for sc := lx.scope; sc != nil; sc = sc.Next { 41 | decl := sc.Decl[name] 42 | if decl != nil { 43 | return decl 44 | } 45 | } 46 | return nil 47 | } 48 | 49 | func (lx *lexer) pushType(typ *Type) *Type { 50 | sc := lx.scope 51 | if sc == nil { 52 | panic("no scope") 53 | } 54 | 55 | if typ.Kind == Enum && typ.Decls != nil { 56 | for _, decl := range typ.Decls { 57 | lx.pushDecl(decl) 58 | } 59 | } 60 | 61 | if typ.Tag == "" { 62 | return typ 63 | } 64 | 65 | old := lx.lookupTag(typ.Tag) 66 | if old == nil { 67 | if sc.Tag == nil { 68 | sc.Tag = make(map[string]*Type) 69 | } 70 | sc.Tag[typ.Tag] = typ 71 | if hdr := lx.declSave; hdr != nil && sc.Next == nil { 72 | hdr.types = append(hdr.types, typ) 73 | } 74 | return typ 75 | } 76 | 77 | // merge typ into old 78 | if old.Kind != typ.Kind { 79 | lx.Errorf("conflicting tags: %s %s and %s %s", old.Kind, old.Tag, typ.Kind, typ.Tag) 80 | return typ 81 | } 82 | if typ.Decls != nil { 83 | if old.Decls != nil { 84 | lx.Errorf("multiple definitions for %s %s", old.Kind, old.Tag) 85 | } 86 | old.SyntaxInfo = typ.SyntaxInfo 87 | old.Decls = typ.Decls 88 | } 89 | return old 90 | } 91 | 92 | func (lx *lexer) lookupTag(name string) *Type { 93 | for sc := lx.scope; sc != nil; sc = sc.Next { 94 | typ := sc.Tag[name] 95 | if typ != nil { 96 | return typ 97 | } 98 | } 99 | return nil 100 | } 101 | 102 | func (lx *lexer) pushScope() { 103 | sc := &Scope{Next: lx.scope} 104 | lx.scope = sc 105 | } 106 | 107 | func (lx *lexer) popScope() { 108 | lx.scope = lx.scope.Next 109 | } 110 | 111 | func (lx *lexer) typecheck(prog *Prog) { 112 | for _, decl := range prog.Decls { 113 | lx.typecheckDecl(decl) 114 | } 115 | } 116 | 117 | func (lx *lexer) typecheckDecl(decl *Decl) { 118 | lx.typecheckType(decl.Type) 119 | if decl.Init != nil { 120 | lx.typecheckInit(decl.Type, decl.Init) 121 | } 122 | lx.typecheckStmt(decl.Body) 123 | } 124 | 125 | func (lx *lexer) typecheckStmt(stmt *Stmt) { 126 | if stmt == nil { 127 | return 128 | } 129 | 130 | lx.setSpan(stmt.Span) 131 | switch stmt.Op { 132 | case StmtDecl: 133 | lx.typecheckDecl(stmt.Decl) 134 | case StmtExpr: 135 | lx.typecheckExpr(stmt.Expr) 136 | case Empty: 137 | // ok 138 | case Block: 139 | for _, s := range stmt.Block { 140 | lx.typecheckStmt(s) 141 | } 142 | case ARGBEGIN: 143 | lx.Errorf("ARGBEGIN not supported") 144 | case Break: 145 | // check break context 146 | case Continue: 147 | // check continue context 148 | case Do: 149 | // push break/continue context 150 | lx.typecheckStmt(stmt.Body) 151 | lx.typecheckExpr(stmt.Expr) 152 | case For: 153 | // push break/continue context 154 | lx.typecheckExpr(stmt.Pre) 155 | lx.typecheckExpr(stmt.Expr) 156 | lx.typecheckExpr(stmt.Post) 157 | lx.typecheckStmt(stmt.Body) 158 | case If: 159 | lx.typecheckExpr(stmt.Expr) 160 | // check bool 161 | lx.typecheckStmt(stmt.Body) 162 | lx.typecheckStmt(stmt.Else) 163 | case Goto: 164 | // check that label exists 165 | case Return: 166 | lx.typecheckExpr(stmt.Expr) 167 | // check return 168 | case Switch: 169 | lx.typecheckExpr(stmt.Expr) 170 | lx.typecheckStmt(stmt.Body) 171 | // push break context 172 | // push switch type 173 | case While: 174 | lx.typecheckExpr(stmt.Expr) 175 | lx.typecheckStmt(stmt.Body) 176 | // push break/continue context 177 | } 178 | 179 | for _, lab := range stmt.Labels { 180 | lx.typecheckExpr(lab.Expr) 181 | } 182 | } 183 | 184 | func (lx *lexer) typecheckType(typ *Type) { 185 | if typ == nil { 186 | return 187 | } 188 | 189 | // TODO: Is there any other work to do for type checking a type? 190 | switch typ.Kind { 191 | case Enum: 192 | // Give enum type to the declared names. 193 | // Perhaps should be done during parsing. 194 | for _, decl := range typ.Decls { 195 | if decl.Init != nil { 196 | lx.typecheckInit(typ, decl.Init) 197 | } 198 | decl.Type = typ 199 | } 200 | } 201 | } 202 | 203 | func (lx *lexer) typecheckInit(typ *Type, x *Init) { 204 | // TODO: Type check initializers (ugh). 205 | 206 | x.XType = typ 207 | typ = stripTypedef(typ) 208 | lx.setSpan(x.Span) 209 | if x.Braced == nil { 210 | lx.typecheckExpr(x.Expr) 211 | if x.Expr.XType == nil { 212 | return 213 | } 214 | if typ.Kind == Array && typ.Base.Is(Char) && x.Expr.Op == String { 215 | // ok to initialize char array with string 216 | if typ.Width == nil { 217 | typ.Width = x.Expr.XType.Width 218 | } 219 | return 220 | } 221 | if !canAssign(typ, x.Expr.XType, x.Expr) { 222 | lx.Errorf("cannot initialize %v with %v (type %v)", typ, x.Expr.XType, x.Expr) 223 | return 224 | } 225 | return 226 | } 227 | 228 | switch typ.Kind { 229 | case Array, Struct: 230 | // ok 231 | case Union: 232 | // C allows this but we do not. 233 | fallthrough 234 | default: 235 | lx.Errorf("cannot initialize type %v with braced initializer", typ) 236 | return 237 | } 238 | 239 | // Keep our sanity: require that either all elements have prefixes or none do. 240 | // This is not required by the C standard; it just makes this code more tractable. 241 | n := 0 242 | for _, elem := range x.Braced { 243 | if len(elem.Prefix) > 0 { 244 | if len(elem.Prefix) != 1 { 245 | lx.setSpan(elem.Span) 246 | lx.Errorf("unsupported compound initializer prefix") 247 | return 248 | } 249 | n++ 250 | } 251 | } 252 | if n != 0 && n != len(x.Braced) { 253 | lx.Errorf("initializer elements must have no prefixes or all be prefixed") 254 | return 255 | } 256 | 257 | if n == 0 { 258 | // Assign elements in order. 259 | if typ.Kind == Array { 260 | // TODO: Check against typ.Width and record new typ.Width if missing 261 | for _, elem := range x.Braced { 262 | lx.typecheckInit(typ.Base, elem) 263 | } 264 | return 265 | } 266 | 267 | // Struct 268 | if len(x.Braced) > len(typ.Decls) { 269 | lx.Errorf("more initializer elements than struct fields in %v (%d > %d)", typ, len(x.Braced), len(typ.Decls)) 270 | return 271 | } 272 | for i, elem := range x.Braced { 273 | decl := typ.Decls[i] 274 | lx.typecheckInit(decl.Type, elem) 275 | } 276 | return 277 | } 278 | 279 | // All elements have initializing prefixes. 280 | 281 | if typ.Kind == Array { 282 | for _, elem := range x.Braced { 283 | lx.setSpan(elem.Span) 284 | pre := elem.Prefix[0] 285 | if pre.Index == nil { 286 | lx.Errorf("field initializer prefix in array") 287 | continue 288 | } 289 | lx.typecheckExpr(pre.Index) 290 | // TODO: check that pre.Index is integer constant 291 | // TODO: record width if needed 292 | lx.typecheckInit(typ.Base, elem) 293 | } 294 | return 295 | } 296 | 297 | // Struct 298 | for _, elem := range x.Braced { 299 | lx.setSpan(elem.Span) 300 | pre := elem.Prefix[0] 301 | if pre.Dot == "" { 302 | lx.Errorf("array initializer prefix in struct") 303 | continue 304 | } 305 | decl := structDot(typ, pre.Dot) 306 | if decl == nil { 307 | lx.Errorf("type %v has no field .%v", typ, pre.Dot) 308 | continue 309 | } 310 | pre.XDecl = decl 311 | lx.typecheckInit(decl.Type, elem) 312 | } 313 | } 314 | 315 | func stripTypedef(t *Type) *Type { 316 | if t != nil && t.Kind == TypedefType && t.Base != nil { 317 | t = t.Base 318 | } 319 | return t 320 | } 321 | 322 | func isInt(t *Type) bool { 323 | t = stripTypedef(t) 324 | return Char <= t.Kind && t.Kind <= Ulonglong || t.Kind == Enum 325 | } 326 | 327 | func isPtr(t *Type) bool { 328 | t = stripTypedef(t) 329 | return t.Kind == Ptr || t.Kind == Array 330 | } 331 | 332 | func ptrBase(t *Type) *Type { 333 | t = stripTypedef(t) 334 | if t == nil || (t.Kind != Ptr && t.Kind != Array) { 335 | return nil 336 | } 337 | return t.Base 338 | } 339 | 340 | func toPtr(t *Type) *Type { 341 | t1 := stripTypedef(t) 342 | if t1.Kind == Ptr { 343 | return t 344 | } 345 | if t1.Kind == Array { 346 | return &Type{Kind: Ptr, Base: t1.Base} 347 | } 348 | return nil 349 | } 350 | 351 | func isArith(t *Type) bool { 352 | t = stripTypedef(t) 353 | return Char <= t.Kind && t.Kind <= Enum 354 | } 355 | 356 | func isScalar(t *Type) bool { 357 | t = stripTypedef(t) 358 | return Char <= t.Kind && t.Kind <= Ptr 359 | } 360 | 361 | func (t *Type) Is(k TypeKind) bool { 362 | t = stripTypedef(t) 363 | return t != nil && t.Kind == k 364 | } 365 | 366 | func (t *Type) Def() *Type { 367 | return stripTypedef(t) 368 | } 369 | 370 | func isNull(x *Expr) bool { 371 | for x != nil && x.Op == Paren { 372 | x = x.Left 373 | } 374 | return x != nil && x.Op == Number && x.Text == "0" 375 | } 376 | 377 | func isVoidPtr(t *Type) bool { 378 | return ptrBase(t).Is(Void) 379 | } 380 | 381 | func (t *Type) IsPtrVoid() bool { 382 | return isVoidPtr(t) 383 | } 384 | 385 | func isCompatPtr(t1, t2 *Type) bool { 386 | return isCompat(ptrBase(t1), ptrBase(t2)) 387 | } 388 | 389 | // This is not correct; see C99 §6.2.7. 390 | func isCompat(t1, t2 *Type) bool { 391 | t1 = stripTypedef(t1) 392 | t2 = stripTypedef(t2) 393 | if t1 == nil || t2 == nil || t1.Kind != t2.Kind { 394 | return false 395 | } 396 | if t1 == t2 { 397 | return true 398 | } 399 | switch t1.Kind { 400 | default: 401 | // arithmetic 402 | return true 403 | case Ptr, Array: 404 | return isCompat(ptrBase(t1), ptrBase(t2)) 405 | case Struct, Union, Enum: 406 | return t1.Tag != "" && t1.Tag == t2.Tag 407 | case Func: 408 | if len(t1.Decls) != len(t2.Decls) || !isCompat(t1.Base, t2.Base) { 409 | return false 410 | } 411 | for i, d1 := range t1.Decls { 412 | d2 := t2.Decls[i] 413 | if d1.Type == nil && d1.Name == "..." { 414 | if d2.Type == nil && d2.Name == "..." { 415 | continue 416 | } 417 | return false 418 | } 419 | if !isCompat(d1.Type, d2.Type) { 420 | return false 421 | } 422 | } 423 | return true 424 | } 425 | } 426 | 427 | // TODO 428 | func compositePtr(t1, t2 *Type) *Type { 429 | return toPtr(t1) 430 | } 431 | 432 | func canAssign(l, r *Type, rx *Expr) bool { 433 | switch { 434 | case isArith(l) && isArith(r): 435 | // ok 436 | case isCompat(l, r): 437 | // ok 438 | case isCompatPtr(l, r): 439 | // ok 440 | case isPtr(l) && isPtr(r) && (isVoidPtr(l) || isVoidPtr(r)): 441 | // ok 442 | case isPtr(l) && isNull(rx): 443 | // ok 444 | rx.XType = toPtr(l) 445 | case isPtr(l) && isCompat(ptrBase(l), r): 446 | // ok 447 | case isVoidPtr(l) && r.Is(Func), isVoidPtr(r) && l.Is(Func): 448 | // ok 449 | case isPtr(l) && ptrBase(l).Is(Func) && r.Is(Func): // && isCompat(ptrBase(l), r): 450 | if !isCompat(ptrBase(l), r) { 451 | fmt.Printf("not compat: %v and %v (%v)\n", ptrBase(l), r, rx) 452 | } 453 | // ok 454 | default: 455 | return false 456 | } 457 | return true 458 | } 459 | 460 | func (lx *lexer) toBool(x *Expr) *Type { 461 | if x.XType == nil { 462 | return nil 463 | } 464 | t := stripTypedef(x.XType) 465 | if Char <= t.Kind && t.Kind <= Ptr || t.Kind == Enum { 466 | return BoolType 467 | } 468 | lx.Errorf("cannot use %v (type %v) in boolean context", x, x.XType) 469 | return nil 470 | } 471 | 472 | // The "usual arithmetic conversions". 473 | func promote2(l, r *Type) *Type { 474 | l = promote1(l) 475 | r = promote1(r) 476 | 477 | // if mixed signedness, make l signed and r unsigned. 478 | // specifically, if l is unsigned, swap with r. 479 | if (l.Kind-Char)&1 == 1 { 480 | l, r = r, l 481 | } 482 | 483 | switch { 484 | case l.Kind == Void || r.Kind == Void || l.Kind > Double || r.Kind > Double: 485 | return nil 486 | case l.Kind == r.Kind: 487 | return l 488 | // double wins. 489 | case l.Kind == Double: 490 | return l 491 | case r.Kind == Double: 492 | return r 493 | // float wins. 494 | case l.Kind == Float: 495 | return l 496 | case r.Kind == Float: 497 | return r 498 | // if both signed or both unsigned, higher kind wins. 499 | case (l.Kind-Char)&1 == (r.Kind-Char)&1: 500 | if l.Kind < r.Kind { 501 | return r 502 | } 503 | return l 504 | // mixed signedness: l is signed, r is unsigned (see above). 505 | // if unsigned higher kind than signed, unsigned wins. 506 | case r.Kind >= l.Kind: 507 | return r 508 | // signed is higher kind than unsigned (l.Kind > r.Kind). 509 | // if signed bigger than unsigned, signed wins. 510 | // only possible way this isn't true 511 | case (l.Kind-Char)/2 > (r.Kind-Char)/2 && (l.Kind != Long || r.Kind != Uint): 512 | return l 513 | // otherwise, use unsigned type corresponding to the signed type. 514 | default: 515 | return &Type{Kind: l.Kind + 1} 516 | } 517 | panic(fmt.Sprintf("missing case in promote2(%v, %v)", l, r)) 518 | } 519 | 520 | func promote1(l *Type) *Type { 521 | l = stripTypedef(l) 522 | if Char <= l.Kind && l.Kind <= Ushort || l.Kind == Enum { 523 | l = IntType 524 | } 525 | return l 526 | } 527 | 528 | func structDot(t *Type, name string) *Decl { 529 | if t == nil || (t.Kind != Struct && t.Kind != Union) { 530 | return nil 531 | } 532 | for _, decl := range t.Decls { 533 | if decl.Name == name { 534 | return decl 535 | } 536 | if decl.Name == "" { 537 | d := structDot(decl.Type, name) 538 | if d != nil { 539 | return d 540 | } 541 | } 542 | } 543 | return nil 544 | } 545 | 546 | func (lx *lexer) parseChar1(text string) (val byte, wid int, ok bool) { 547 | if text[0] != '\\' { 548 | return text[0], 1, true 549 | } 550 | if len(text) == 1 { 551 | lx.Errorf("truncated escape sequence in character or string constant") 552 | return 553 | } 554 | switch text[1] { 555 | case 'a': 556 | return 7, 2, true 557 | case 'b': 558 | return 8, 2, true 559 | case 'f': 560 | return 12, 2, true 561 | case 'n': 562 | return 10, 2, true 563 | case 'r': 564 | return 13, 2, true 565 | case 't': 566 | return 9, 2, true 567 | case 'v': 568 | return 11, 2, true 569 | case '\'', '"', '?', '\\': 570 | return text[1], 2, true 571 | case '0', '1', '2', '3', '4', '5', '6', '7': 572 | i := 2 573 | v := int(text[1] - '0') 574 | for i < 4 && i < len(text) && '0' <= text[i] && text[i] <= '7' { 575 | v = v*8 + int(text[i]-'0') 576 | i++ 577 | } 578 | if v >= 256 { 579 | lx.Errorf("octal escape %s out of range", text[:i]) 580 | return 581 | } 582 | return byte(v), i, true 583 | case 'x': 584 | i := 2 585 | v := 0 586 | for i < len(text) && ishex(text[i]) { 587 | v = v*16 + unhex(text[i]) 588 | i++ 589 | } 590 | if i-2 > 2 { 591 | lx.Errorf("hexadecimal escape %s out of range", text[:i]) 592 | return 593 | } 594 | if i == 0 { 595 | lx.Errorf("hexadecimal escape %s missing digits", text[:i]) 596 | return 597 | } 598 | return byte(v), i, true 599 | 600 | default: 601 | lx.Errorf("invalid escape sequence %s", text[:2]) 602 | } 603 | return 604 | } 605 | 606 | func ishex(c byte) bool { 607 | return '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' 608 | } 609 | 610 | func unhex(c byte) int { 611 | if '0' <= c && c <= '9' { 612 | return int(c) - '0' 613 | } 614 | if 'a' <= c && c <= 'f' { 615 | return int(c) - 'a' + 10 616 | } 617 | if 'A' <= c && c <= 'F' { 618 | return int(c) - 'A' + 10 619 | } 620 | return -1 621 | } 622 | 623 | func (lx *lexer) parseChar(text string) (val byte, ok bool) { 624 | if len(text) < 3 || text[0] != '\'' || text[len(text)-1] != '\'' { 625 | lx.Errorf("invalid character constant %v", text) 626 | return 0, false 627 | } 628 | val, wid, ok := lx.parseChar1(text[1 : len(text)-1]) 629 | if !ok { 630 | return 0, false 631 | } 632 | if wid != len(text)-2 { 633 | lx.Errorf("invalid character constant %v - multiple characters", text) 634 | return 0, false 635 | } 636 | return val, true 637 | } 638 | 639 | func (lx *lexer) parseString(text string) (val string, ok bool) { 640 | if len(text) < 2 || text[0] != '"' || text[len(text)-1] != '"' { 641 | lx.Errorf("invalid string constant %v", text) 642 | return "", false 643 | } 644 | tval := text[1 : len(text)-1] 645 | var bval []byte 646 | for len(tval) > 0 { 647 | ch, wid, ok := lx.parseChar1(tval) 648 | if !ok { 649 | return "", false 650 | } 651 | bval = append(bval, ch) 652 | tval = tval[wid:] 653 | } 654 | return string(bval), true 655 | } 656 | 657 | func (lx *lexer) typecheckExpr(x *Expr) { 658 | if x == nil { 659 | return 660 | } 661 | panic("typecheck") 662 | 663 | if x.Op != Offsetof { 664 | lx.typecheckExpr(x.Left) 665 | } 666 | lx.typecheckExpr(x.Right) 667 | for _, y := range x.List { 668 | lx.typecheckExpr(y) 669 | } 670 | lx.typecheckType(x.Type) 671 | 672 | lx.setSpan(x.Span) 673 | switch x.Op { 674 | default: 675 | panic("missing typecheck " + x.Op.String()) 676 | 677 | case Add: 678 | l, r := x.Left.XType, x.Right.XType 679 | if l == nil || r == nil { 680 | break 681 | } 682 | switch { 683 | case isPtr(l) && isInt(r): 684 | x.XType = toPtr(l) 685 | case isInt(l) && isPtr(r): 686 | x.XType = toPtr(r) 687 | default: 688 | lx.typecheckArith(x) 689 | } 690 | 691 | case Addr: 692 | t := x.Left.XType 693 | if t == nil { 694 | break 695 | } 696 | if isPtr(t) { 697 | t = toPtr(t) 698 | } 699 | x.XType = &Type{Kind: Ptr, Base: t} 700 | 701 | case AddEq, SubEq: 702 | l, r := x.Left.XType, x.Right.XType 703 | if l == nil || r == nil { 704 | break 705 | } 706 | if isPtr(l) && isInt(r) { 707 | x.XType = toPtr(l) 708 | break 709 | } 710 | lx.typecheckArithEq(x) 711 | 712 | case And, Mod, Or, Xor: 713 | // int & int 714 | l, r := x.Left.XType, x.Right.XType 715 | if l == nil || r == nil { 716 | break 717 | } 718 | if !isInt(l) || !isInt(r) { 719 | lx.Errorf("invalid bitwise op of %v (type %v) and %v (type %v)", x.Left, l, x.Right, r) 720 | break 721 | } 722 | x.XType = promote2(l, r) 723 | 724 | case AndAnd, OrOr: 725 | // bool && bool 726 | l := lx.toBool(x.Left) 727 | r := lx.toBool(x.Right) 728 | if l == nil || r == nil { 729 | break 730 | } 731 | x.XType = BoolType 732 | 733 | case AndEq, ModEq, OrEq, XorEq: 734 | // int &= int 735 | l, r := x.Left.XType, x.Right.XType 736 | if l == nil || r == nil { 737 | break 738 | } 739 | if !isInt(l) || !isInt(r) { 740 | lx.Errorf("invalid bitwise op of %v (type %v) and %v (type %v)", x.Left, l, x.Right, r) 741 | break 742 | } 743 | x.XType = l 744 | 745 | case Arrow: 746 | t := x.Left.XType 747 | if t == nil { 748 | lx.Errorf("arrow missing type %s", x.Left) 749 | break 750 | } 751 | if !isPtr(t) { 752 | lx.Errorf("invalid -> of non-pointer %v (type %v)", x.Left, t) 753 | break 754 | } 755 | t = stripTypedef(ptrBase(t)) 756 | if t.Kind != Struct && t.Kind != Union { 757 | lx.Errorf("invalid -> of pointer to non-struct/union %v (type %v)", x.Left, t) 758 | break 759 | } 760 | d := structDot(t, x.Text) 761 | if d == nil { 762 | lx.Errorf("unknown field %v->%v", t, x.Text) 763 | break 764 | } 765 | x.XDecl = d 766 | x.XType = d.Type 767 | 768 | case Call: 769 | t := x.Left.XType 770 | if t == nil { 771 | lx.Errorf("no info for call of %v", x.Left) 772 | break 773 | } 774 | if isPtr(t) { 775 | t = ptrBase(t) 776 | } 777 | t = stripTypedef(t) 778 | if t.Kind != Func { 779 | lx.Errorf("invalid call of %v (type %v)", x.Left, x.Left.XType) 780 | break 781 | } 782 | x.XType = t.Base 783 | for i := 0; i < len(x.List) || i < len(t.Decls); i++ { 784 | if i >= len(t.Decls) { 785 | lx.Errorf("too many arguments to call") 786 | break 787 | } 788 | d := t.Decls[i] 789 | if d.Name == "..." { 790 | break 791 | } 792 | if i >= len(x.List) { 793 | if len(x.List) == 0 && len(t.Decls) == 1 && t.Decls[0].Type.Is(Void) { 794 | break 795 | } 796 | lx.Errorf("not enough arguments to call") 797 | break 798 | } 799 | if x.List[i].XType != nil && !canAssign(d.Type, x.List[i].XType, x.List[i]) { 800 | lx.Errorf("cannot assign %v (type %v) to %v in call", x.List[i], x.List[i].XType, d.Type) 801 | } 802 | } 803 | 804 | case Cast: 805 | // NOTE: Assuming cast is valid. 806 | x.XType = x.Type 807 | 808 | case CastInit: 809 | lx.typecheckInit(x.Type, x.Init) 810 | x.XType = x.Type 811 | 812 | case Comma: 813 | x.XType = x.List[len(x.List)-1].XType 814 | 815 | case Cond: 816 | c, l, r := lx.toBool(x.List[0]), x.List[1].XType, x.List[2].XType 817 | if c == nil || l == nil || r == nil { 818 | break 819 | } 820 | switch { 821 | default: 822 | lx.Errorf("incompatible branches %v (type %v) and %v (type %v) in conditional", x.List[1], l, x.List[2], r) 823 | case isArith(l) && isArith(r): 824 | x.XType = promote2(l, r) 825 | case l == r: 826 | x.XType = l 827 | case isCompatPtr(l, r): 828 | x.XType = compositePtr(l, r) 829 | case isPtr(l) && isNull(x.List[1]): 830 | x.XType = toPtr(l) 831 | x.List[1].XType = x.XType 832 | case isPtr(r) && isNull(x.List[0]): 833 | x.XType = toPtr(r) 834 | x.List[0].XType = x.XType 835 | case isPtr(l) && isVoidPtr(r): 836 | x.XType = r 837 | case isPtr(r) && isVoidPtr(l): 838 | x.XType = l 839 | } 840 | 841 | case Div, Mul: 842 | lx.typecheckArith(x) 843 | 844 | case DivEq, MulEq: 845 | lx.typecheckArithEq(x) 846 | 847 | case Dot: 848 | t := x.Left.XType 849 | if t == nil { 850 | break 851 | } 852 | t = stripTypedef(t) 853 | if t.Kind != Struct && t.Kind != Union { 854 | lx.Errorf("invalid . of non-struct/union %v (type %v)", x.Left, t) 855 | break 856 | } 857 | d := structDot(t, x.Text) 858 | if d == nil { 859 | lx.Errorf("unknown field %v.%v", t, x.Text) 860 | break 861 | } 862 | x.XDecl = d 863 | x.XType = d.Type 864 | 865 | case Eq: 866 | l, r := x.Left.XType, x.Right.XType 867 | if l == nil || r == nil { 868 | break 869 | } 870 | x.XType = l 871 | if !canAssign(l, r, x.Right) { 872 | lx.Errorf("invalid assignment %v (type %v) = %v (typ %v)", x.Left, l, x.Right, r) 873 | break 874 | } 875 | 876 | case EqEq, NotEq, Gt, GtEq, Lt, LtEq: 877 | x.XType = BoolType 878 | l, r := x.Left.XType, x.Right.XType 879 | if l == nil || r == nil { 880 | break 881 | } 882 | if isArith(l) && isArith(r) { 883 | if x.Left.Op != Number && x.Right.Op == Number { 884 | x.Right.XType = x.Left.XType 885 | } 886 | if x.Right.Op != Number && x.Left.Op == Number { 887 | x.Left.XType = x.Right.XType 888 | } 889 | break 890 | } 891 | if isCompatPtr(l, r) { 892 | break 893 | } 894 | if x.Op == EqEq || x.Op == NotEq { 895 | if isPtr(l) { 896 | if isNull(x.Right) || isVoidPtr(r) { 897 | x.Right.XType = toPtr(l) 898 | break 899 | } 900 | } 901 | if isPtr(r) { 902 | if isNull(x.Left) || isVoidPtr(l) { 903 | x.Left.XType = toPtr(r) 904 | break 905 | } 906 | } 907 | } 908 | lx.Errorf("invalid comparison of %v (type %v) and %v (type %v)", x.Left, l, x.Right, r) 909 | 910 | case Index: 911 | // ptr[int] 912 | // int[ptr] 913 | l, r := x.Left.XType, x.Right.XType 914 | if l == nil || r == nil { 915 | break 916 | } 917 | switch { 918 | case isPtr(l) && isInt(r): 919 | x.XType = ptrBase(l) 920 | case isInt(l) && isPtr(r): 921 | x.XType = ptrBase(r) 922 | default: 923 | lx.Errorf("invalid index %v (types %v, %v)", x, l, r) 924 | } 925 | 926 | case Indir: 927 | // *ptr 928 | t := x.Left.XType 929 | if t == nil { 930 | break 931 | } 932 | if !isPtr(t) { 933 | lx.Errorf("invalid indirect of non-pointer %v (type %v)", x.Left, t) 934 | break 935 | } 936 | x.XType = ptrBase(t) 937 | 938 | case Lsh, Rsh: 939 | // int << int 940 | l, r := x.Left.XType, x.Right.XType 941 | if l == nil || r == nil { 942 | break 943 | } 944 | if !isInt(l) || !isInt(r) { 945 | lx.Errorf("invalid shift of %v (type %v) and %v (type %v)", x.Left, l, x.Right, r) 946 | break 947 | } 948 | x.XType = promote1(l) 949 | 950 | case LshEq, RshEq: 951 | // int <<= int 952 | l, r := x.Left.XType, x.Right.XType 953 | if l == nil || r == nil { 954 | break 955 | } 956 | if !isInt(l) || !isInt(r) { 957 | lx.Errorf("invalid shift of %v (type %v) and %v (type %v)", x.Left, l, x.Right, r) 958 | break 959 | } 960 | x.XType = l 961 | 962 | case Minus, Plus: 963 | // -int 964 | // -float 965 | t := x.Left.XType 966 | if t == nil { 967 | break 968 | } 969 | if !isArith(t) { 970 | lx.Errorf("invalid ± of %v (type %v)", x, t) 971 | break 972 | } 973 | x.XType = promote1(t) 974 | 975 | case Name: 976 | if x.XDecl == nil { 977 | lx.Errorf("undefined: %s", x.Text) 978 | break 979 | } 980 | // XXX this happens for enums 981 | // if x.XDecl.Type == nil { 982 | // lx.Errorf("missing type for defined variable: %s", x.Text) 983 | // } 984 | x.XType = x.XDecl.Type 985 | 986 | case Not: 987 | // !bool 988 | lx.toBool(x.Left) 989 | x.XType = BoolType 990 | 991 | case Number: 992 | num := x.Text 993 | if num[0] == '\'' { 994 | // character constant 995 | _, _ = lx.parseChar(num) 996 | x.XType = IntType 997 | break 998 | } 999 | 1000 | if strings.Contains(num, ".") || !strings.HasPrefix(num, "0x") && strings.ContainsAny(num, "eE") { 1001 | // floating point 1002 | num = strings.TrimRight(num, "fFlL") 1003 | suf := x.Text[len(num):] 1004 | f, err := strconv.ParseFloat(num, 64) 1005 | if err != nil { 1006 | lx.Errorf("invalid floating point constant %v", x.Text) 1007 | break 1008 | } 1009 | _ = f // TODO use this 1010 | x.XType = DoubleType 1011 | switch suf { 1012 | case "": 1013 | case "f", "F": 1014 | x.XType = FloatType 1015 | default: 1016 | lx.Errorf("unsupported floating point constant suffix %v", x.Text) 1017 | } 1018 | break 1019 | } 1020 | 1021 | // integer 1022 | num = strings.TrimRight(num, "uUlL") 1023 | suf := x.Text[len(num):] 1024 | i, err := strconv.ParseUint(num, 0, 64) 1025 | if err != nil { 1026 | lx.Errorf("invalid integer constant %v", x.Text) 1027 | break 1028 | } 1029 | _ = i // TODO use this 1030 | has := strings.Contains 1031 | suf = strings.ToUpper(suf) 1032 | switch { 1033 | case has(suf, "U") && has(suf, "LL"): 1034 | x.XType = UlonglongType 1035 | case has(suf, "U") && has(suf, "L"): 1036 | if uint64(uint32(i)) == i { 1037 | x.XType = UlongType 1038 | } else { 1039 | x.XType = UlonglongType 1040 | } 1041 | case has(suf, "U"): 1042 | if uint64(uint32(i)) == i { 1043 | x.XType = UintType 1044 | } else { 1045 | x.XType = UlonglongType 1046 | } 1047 | case has(suf, "LL"): 1048 | if int64(i) >= 0 { 1049 | x.XType = LonglongType 1050 | } else { 1051 | lx.Errorf("integer constant %v overflows signed long long", x.Text) 1052 | } 1053 | case has(suf, "L"): 1054 | if int32(i) >= 0 && uint64(int32(i)) == i { 1055 | x.XType = LongType 1056 | } else if int64(i) >= 0 { 1057 | x.XType = LonglongType 1058 | } else { 1059 | lx.Errorf("integer constant %v overflows signed long long", x.Text) 1060 | } 1061 | default: 1062 | if int32(i) >= 0 && uint64(int32(i)) == i { 1063 | x.XType = IntType 1064 | } else if int64(i) >= 0 { 1065 | x.XType = LonglongType 1066 | } else { 1067 | lx.Errorf("integer constant %v overflows signed long long", x.Text) 1068 | } 1069 | } 1070 | 1071 | case Offsetof: 1072 | x.XType = LongType 1073 | if x.Left.Op != Name { 1074 | lx.Errorf("offsetof field too complicated") 1075 | } 1076 | d := structDot(stripTypedef(x.Type), x.Left.Text) 1077 | if d == nil { 1078 | lx.Errorf("unknown field %v.%v", x.Type, x.Left.Text) 1079 | } 1080 | 1081 | case Paren: 1082 | // (non-void) 1083 | t := x.Left.XType 1084 | if t == nil { 1085 | break 1086 | } 1087 | if t.Kind == Void { 1088 | lx.Errorf("cannot parenthesize void expression") 1089 | break 1090 | } 1091 | x.XType = t 1092 | 1093 | case PostDec, PostInc, PreDec, PreInc: 1094 | // int-- 1095 | // float-- 1096 | // ptr-- 1097 | t := x.Left.XType 1098 | if t == nil { 1099 | break 1100 | } 1101 | if !isArith(t) && !isPtr(t) { 1102 | lx.Errorf("cannot increment/decrement %v (type %v)", x.Left, t) 1103 | break 1104 | } 1105 | x.XType = t 1106 | 1107 | case SizeofExpr: 1108 | x.XType = LongType 1109 | 1110 | case SizeofType: 1111 | x.XType = LongType 1112 | 1113 | case String: 1114 | // string list 1115 | var str []string 1116 | ok := true 1117 | for _, text := range x.Texts { 1118 | s, sok := lx.parseString(text) 1119 | if !sok { 1120 | ok = false 1121 | } 1122 | str = append(str, s) 1123 | } 1124 | if !ok { 1125 | break 1126 | } 1127 | s := strings.Join(str, "") 1128 | _ = s // TODO use this 1129 | x.XType = &Type{Kind: Array, Width: &Expr{Op: Number, Text: fmt.Sprint(len(s) + 1)}, Base: CharType} 1130 | 1131 | case Sub: 1132 | l, r := x.Left.XType, x.Right.XType 1133 | if l == nil || r == nil { 1134 | break 1135 | } 1136 | switch { 1137 | case isPtr(l) && isInt(r): 1138 | x.XType = toPtr(l) 1139 | case isCompatPtr(l, r): 1140 | x.XType = LongType 1141 | default: 1142 | lx.typecheckArith(x) 1143 | } 1144 | 1145 | case Twid: 1146 | // ~int 1147 | t := x.Left.XType 1148 | if t == nil { 1149 | break 1150 | } 1151 | if !isInt(t) { 1152 | lx.Errorf("invalid ~ of %v (type %v)", x, t) 1153 | break 1154 | } 1155 | x.XType = promote1(t) 1156 | 1157 | case VaArg: 1158 | // va_arg(arg, int) 1159 | t := x.Left.XType 1160 | if t == nil { 1161 | break 1162 | } 1163 | if t.Name != "va_list" { 1164 | lx.Errorf("va_arg takes va_list, have %v (type %v)", x.Left, t) 1165 | } 1166 | x.XType = x.Type 1167 | } 1168 | } 1169 | 1170 | func (lx *lexer) typecheckArith(x *Expr) { 1171 | // int + int 1172 | // float + float 1173 | l, r := x.Left.XType, x.Right.XType 1174 | if l == nil || r == nil { 1175 | return 1176 | } 1177 | if !isArith(l) || !isArith(r) { 1178 | lx.Errorf("invalid arithmetic op of %v (type %v) and %v (type %v)", x.Left, l, x.Right, r) 1179 | return 1180 | } 1181 | x.XType = promote2(l, r) 1182 | } 1183 | 1184 | func (lx *lexer) typecheckArithEq(x *Expr) { 1185 | // int + int 1186 | // float + float 1187 | l, r := x.Left.XType, x.Right.XType 1188 | if l == nil || r == nil { 1189 | return 1190 | } 1191 | if !isArith(l) || !isArith(r) { 1192 | lx.Errorf("invalid arithmetic op of %v (type %v) and %v (type %v)", x.Left, l, x.Right, r) 1193 | return 1194 | } 1195 | x.XType = l 1196 | } 1197 | -------------------------------------------------------------------------------- /c2gofmt/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // C2gofmt translates C syntax source files into Go syntax. 6 | // 7 | // Usage: 8 | // 9 | // c2gofmt [-v] [-w] [-r file] [file.c file.h ...] 10 | // 11 | // C2gofmt translates the named C source files to Go syntax. 12 | // It only operates syntactically: it does not type-check the C code 13 | // nor the generated Go code. As a result, the generated Go code 14 | // will almost certainly not compile. But it can serve well as the 15 | // starting point for a manual translation, with c2gofmt having 16 | // done much of the most tedious work. 17 | // 18 | // The -v flag causes c2gofmt to print verbose output. 19 | // 20 | // By default, c2gofmt writes the Go translation on standard output. 21 | // The -w flag causes c2gofmt to write a Go file for each C input file, 22 | // named by removing the .c suffix (if any) and adding .go. 23 | // 24 | // The -r flag causes c2gofmt to read rewrite rules from the named file. 25 | // In the file, blank lines or lines beginning with # are ignored. 26 | // Other lines take the form “old -> new” and are interpreted the 27 | // same as the patterns passed to “gofmt -r”. 28 | package main 29 | 30 | import ( 31 | "bytes" 32 | "flag" 33 | "fmt" 34 | "go/format" 35 | "go/parser" 36 | "go/token" 37 | "io/ioutil" 38 | "log" 39 | "os" 40 | "path" 41 | "path/filepath" 42 | "strings" 43 | 44 | "rsc.io/cmd/c2gofmt/internal/cc" 45 | ) 46 | 47 | var ( 48 | rulefile = flag.String("r", "", "load rewrite rules from `file`") 49 | flagW = flag.Bool("w", false, "write files") 50 | verbose = flag.Bool("v", false, "print verbose output") 51 | ) 52 | 53 | func usage() { 54 | fmt.Fprintf(os.Stderr, "usage: c2gofmt [-w] [-r rulefile] [file.c ...]\n") 55 | os.Exit(2) 56 | } 57 | 58 | func main() { 59 | log.SetPrefix("c2gofmt: ") 60 | log.SetFlags(0) 61 | flag.Usage = usage 62 | flag.Parse() 63 | 64 | if *rulefile != "" { 65 | data, err := ioutil.ReadFile(*rulefile) 66 | if err != nil { 67 | log.Fatal(err) 68 | } 69 | parseRules(*rulefile, string(data)) 70 | } 71 | 72 | args := flag.Args() 73 | if len(args) == 0 { 74 | data, err := ioutil.ReadAll(os.Stdin) 75 | if err != nil { 76 | log.Fatal(err) 77 | } 78 | os.Stdout.Write(do("stdin", data)) 79 | return 80 | } 81 | 82 | for _, name := range args { 83 | data, err := ioutil.ReadFile(name) 84 | if err != nil { 85 | log.Fatal(err) 86 | } 87 | goProg := do(name, data) 88 | 89 | if *flagW { 90 | out := strings.TrimSuffix(filepath.Base(name), ".c") + ".go" 91 | err := ioutil.WriteFile(out, goProg, 0666) 92 | if err != nil { 93 | log.Fatal(err) 94 | } 95 | continue 96 | } 97 | os.Stdout.Write(goProg) 98 | } 99 | } 100 | 101 | func do(name string, data []byte) []byte { 102 | var types []string 103 | haveType := make(map[string]bool) 104 | var prog *cc.Prog 105 | for { 106 | p, err := cc.Read(name, bytes.NewReader(data), types) 107 | if err == nil { 108 | prog = p 109 | break 110 | } 111 | 112 | // Can we find some new inferred type names? 113 | n := len(haveType) 114 | if *verbose { 115 | log.Printf("parse errors:\n%s", err) 116 | } 117 | for _, line := range strings.Split(err.Error(), "\n") { 118 | prompts := []string{ 119 | "syntax error near ", 120 | "invalid function definition for ", 121 | "likely type near ", 122 | } 123 | for _, p := range prompts { 124 | if i := strings.Index(line, p); i >= 0 { 125 | word := line[i+len(p):] 126 | if !haveType[word] { 127 | haveType[word] = true 128 | if *verbose { 129 | log.Printf("assume %s is type", word) 130 | } 131 | types = append(types, word) 132 | } 133 | break 134 | } 135 | } 136 | } 137 | if len(haveType) == n { 138 | log.Fatal(err) 139 | } 140 | } 141 | 142 | rewriteSyntax(prog) 143 | simplifyBool(prog) 144 | decls := renameDecls(prog) 145 | moveDecls(decls) 146 | return writeGo(prog, decls) 147 | } 148 | 149 | func writeGo(prog *cc.Prog, decls []*cc.Decl) []byte { 150 | p := new(Printer) 151 | p.Package = "my/pkg" 152 | 153 | if len(decls) > 0 { 154 | steal := 0 155 | for i, com := range decls[0].Comments.Before { 156 | if com.Text == "" { 157 | steal = i + 1 158 | } 159 | } 160 | prog.Comments.Before = append(prog.Comments.Before, decls[0].Comments.Before[:steal]...) 161 | decls[0].Comments.Before = decls[0].Comments.Before[steal:] 162 | } else { 163 | steal := 0 164 | for i, com := range prog.Comments.After { 165 | if com.Text == "" { 166 | steal = i + 1 167 | } 168 | if com.Directive { 169 | break 170 | } 171 | } 172 | prog.Comments.Before = append(prog.Comments.Before, prog.Comments.After[:steal]...) 173 | prog.Comments.After = prog.Comments.After[steal:] 174 | } 175 | 176 | p.Print(prog.Comments.Before) 177 | 178 | for len(decls) > 0 && decls[0].Blank { 179 | p.Print(decls[0], Newline) 180 | decls = decls[1:] 181 | } 182 | 183 | pkg := path.Base(p.Package) 184 | p.Print("package ", pkg, "\n\n") 185 | 186 | for _, d := range decls { 187 | p.printDecl(d, true) 188 | p.Print(Newline) 189 | } 190 | p.Print(prog.Comments.After) 191 | 192 | buf := p.Bytes() 193 | 194 | if len(rules) > 0 { 195 | fset := token.NewFileSet() 196 | f, err := parser.ParseFile(fset, "output", buf, parser.ParseComments) 197 | if err != nil { 198 | log.Printf("parsing Go for %s before rewrites: %v", prog.Span.Start.File, err) 199 | return buf 200 | } 201 | f = rewriteFile(fset, f, rules) 202 | var out bytes.Buffer 203 | if err := format.Node(&out, fset, f); err != nil { 204 | log.Fatalf("reformatting %s after rewrites: %v", prog.Span.Start.File, err) 205 | } 206 | buf = out.Bytes() 207 | } 208 | 209 | if buf1, err := format.Source(buf); err == nil { 210 | buf = buf1 211 | } 212 | return buf 213 | } 214 | -------------------------------------------------------------------------------- /c2gofmt/printer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "os" 11 | "path" 12 | "strings" 13 | 14 | "rsc.io/cmd/c2gofmt/internal/cc" 15 | ) 16 | 17 | type PrintSpecial int 18 | 19 | const ( 20 | Indent PrintSpecial = iota 21 | Unindent 22 | Untab 23 | Newline 24 | Unnewline 25 | DisableNL 26 | EnableNL 27 | ) 28 | 29 | // print an error; fprintf is a bad name but helps go vet. 30 | func fprintf(span cc.Span, format string, args ...interface{}) { 31 | msg := fmt.Sprintf(format, args...) 32 | fmt.Fprintf(os.Stderr, "%s:%d: %s\n", span.Start.File, span.Start.Line, msg) 33 | } 34 | 35 | type Printer struct { 36 | Package string 37 | buf bytes.Buffer 38 | indent int 39 | html bool 40 | lastline int 41 | 42 | printed map[interface{}]bool 43 | suffix []cc.Comment // suffix comments to print at next newline 44 | } 45 | 46 | func (p *Printer) atBOL() bool { 47 | buf := p.buf.Bytes() 48 | i := len(buf) 49 | for i > 0 && (buf[i-1] == ' ' || buf[i-1] == '\t') { 50 | i-- 51 | } 52 | return i == 0 || buf[i-1] == '\n' 53 | } 54 | 55 | func (p *Printer) dup(x interface{}) bool { 56 | if p.printed[x] { 57 | return true 58 | } 59 | if p.printed == nil { 60 | p.printed = make(map[interface{}]bool) 61 | } 62 | p.printed[x] = true 63 | return false 64 | } 65 | 66 | func (p *Printer) StartHTML() { 67 | p.buf.WriteString("
")
  68 | 	p.html = true
  69 | }
  70 | 
  71 | func (p *Printer) EndHTML() {
  72 | 	p.buf.WriteString("
") 73 | } 74 | 75 | func (p *Printer) Bytes() []byte { 76 | return p.buf.Bytes() 77 | } 78 | 79 | func (p *Printer) String() string { 80 | return p.buf.String() 81 | } 82 | 83 | type exprPrec struct { 84 | expr *cc.Expr 85 | prec int 86 | } 87 | 88 | type nestBlock struct { 89 | stmt *cc.Stmt 90 | more bool 91 | } 92 | 93 | type unnestBlock struct { 94 | stmt *cc.Stmt 95 | } 96 | 97 | type typedInit struct { 98 | typ *cc.Type 99 | init *cc.Init 100 | } 101 | 102 | var htmlEscaper = strings.NewReplacer("<", "<", ">", ">", "&", "&") 103 | 104 | func GoString(args ...interface{}) string { 105 | var p Printer 106 | p.Print(args...) 107 | return p.buf.String() 108 | } 109 | 110 | type spanner interface { 111 | GetSpan() cc.Span 112 | } 113 | 114 | func (p *Printer) Print(args ...interface{}) { 115 | for _, arg := range args { 116 | switch arg := arg.(type) { 117 | default: 118 | fmt.Fprintf(&p.buf, "(?%T)", arg) 119 | case string: 120 | if p.html { 121 | htmlEscaper.WriteString(&p.buf, arg) 122 | } else { 123 | p.buf.WriteString(arg) 124 | } 125 | case exprPrec: 126 | p.printExpr(arg.expr, arg.prec) 127 | case *cc.Expr: 128 | p.printExpr(arg, precLow) 129 | case *cc.Prefix: 130 | p.printPrefix(arg) 131 | case *cc.Init: 132 | p.printInit(nil, arg) 133 | case typedInit: 134 | if arg.typ.Kind == cc.Array && arg.typ.Width == nil && len(arg.init.Braced) > 0 { 135 | typ := *arg.typ 136 | arg.typ = &typ 137 | arg.typ.Width = &cc.Expr{Op: cc.Name, Text: fmt.Sprint(len(arg.init.Braced))} 138 | } 139 | p.printInit(arg.typ, arg.init) 140 | case *cc.Prog: 141 | p.printProg(arg) 142 | case *cc.Stmt: 143 | p.printStmt(arg, true) 144 | case unnestBlock: 145 | p.printStmt(arg.stmt, false) 146 | case []*cc.Stmt: 147 | for _, s := range arg { 148 | p.Print(s) 149 | } 150 | case *cc.Type: 151 | p.printType(arg) 152 | case *cc.Decl: 153 | p.printDecl(arg, false) 154 | case cc.Storage: 155 | p.Print(arg.String()) 156 | case []cc.Comment: 157 | for _, com := range arg { 158 | p.Print(com) 159 | } 160 | case cc.Comment: 161 | com := arg 162 | 163 | // p.Print(fmt.Sprintf("/*cstart:%d.%d*/", com.Span.Start.Line, com.Span.Start.Byte)) 164 | // defer p.Print(fmt.Sprintf("/*cend:%d.%d*/", com.Span.End.Line, com.Span.End.Byte)) 165 | if com.Suffix { 166 | p.suffix = append(p.suffix, com) 167 | } else { 168 | if !p.atBOL() { 169 | p.Print(Newline) 170 | } 171 | for _, line := range strings.Split(com.Text, "\n") { 172 | p.Print(line, Newline) 173 | } 174 | } 175 | case nestBlock: 176 | if arg.stmt.Op == cc.Block { 177 | p.Print(" ", arg.stmt) 178 | } else { 179 | p.Print(" {", Indent, Newline, arg.stmt, Unindent, Newline, "}") 180 | } 181 | case PrintSpecial: 182 | switch arg { 183 | default: 184 | fmt.Fprintf(&p.buf, "(?special:%d)", arg) 185 | case Indent: 186 | p.indent++ 187 | case Unindent: 188 | p.indent-- 189 | case Untab: 190 | b := p.buf.Bytes() 191 | if len(b) > 0 && b[len(b)-1] == '\t' { 192 | p.buf.Truncate(len(b) - 1) 193 | } 194 | case Newline: 195 | for _, com := range p.suffix { 196 | p.Print(" ", com.Text) 197 | } 198 | p.suffix = p.suffix[:0] 199 | p.buf.WriteString("\n") 200 | for i := 0; i < p.indent; i++ { 201 | p.buf.WriteByte('\t') 202 | } 203 | case Unnewline: 204 | buf := p.buf.Bytes() 205 | if len(buf) < 1+p.indent || buf[len(buf)-1-p.indent] != '\n' { 206 | goto KeepNewline 207 | } 208 | for i := 0; i < p.indent; i++ { 209 | if buf[len(buf)-1-i] != '\t' { 210 | goto KeepNewline 211 | } 212 | } 213 | p.buf.Truncate(len(buf) - (1 + p.indent)) 214 | KeepNewline: 215 | } 216 | } 217 | } 218 | } 219 | 220 | const ( 221 | precNone = iota 222 | precArrow 223 | precAddr 224 | precMul 225 | precAdd 226 | precCmp 227 | precAndAnd 228 | precOrOr 229 | precComma 230 | precLow 231 | ) 232 | 233 | var opPrec = map[cc.ExprOp]int{ 234 | cc.Add: precAdd, 235 | cc.AddEq: precLow, 236 | cc.Addr: precAddr, 237 | cc.And: precMul, 238 | cc.AndAnd: precAndAnd, 239 | cc.AndEq: precLow, 240 | cc.Arrow: precArrow, 241 | cc.Call: precArrow, 242 | cc.Cast: precAddr, 243 | cc.CastInit: precAddr, 244 | cc.Comma: precComma, 245 | cc.Cond: precComma, 246 | cc.Div: precMul, 247 | cc.DivEq: precLow, 248 | cc.Dot: precArrow, 249 | cc.Eq: precLow, 250 | cc.EqEq: precCmp, 251 | cc.Gt: precCmp, 252 | cc.GtEq: precCmp, 253 | cc.Index: precArrow, 254 | cc.Indir: precAddr, 255 | cc.Lsh: precMul, 256 | cc.LshEq: precLow, 257 | cc.Lt: precCmp, 258 | cc.LtEq: precCmp, 259 | cc.Minus: precAddr, 260 | cc.Mod: precMul, 261 | cc.ModEq: precLow, 262 | cc.Mul: precMul, 263 | cc.MulEq: precLow, 264 | cc.Name: precNone, 265 | cc.Not: precAddr, 266 | cc.NotEq: precCmp, 267 | cc.Number: precNone, 268 | cc.Offsetof: precAddr, 269 | cc.Or: precAdd, 270 | cc.OrEq: precLow, 271 | cc.OrOr: precOrOr, 272 | cc.Paren: precLow, 273 | cc.Plus: precAddr, 274 | cc.PostDec: precAddr, 275 | cc.PostInc: precAddr, 276 | cc.PreDec: precAddr, 277 | cc.PreInc: precAddr, 278 | cc.Rsh: precMul, 279 | cc.RshEq: precLow, 280 | cc.SizeofExpr: precAddr, 281 | cc.SizeofType: precAddr, 282 | cc.String: precNone, 283 | cc.Sub: precAdd, 284 | cc.SubEq: precLow, 285 | cc.Twid: precAddr, 286 | cc.VaArg: precAddr, 287 | cc.Xor: precAdd, 288 | cc.XorEq: precLow, 289 | 290 | AndNot: precMul, 291 | AndNotEq: precLow, 292 | ColonEq: precLow, 293 | TypeAssert: precArrow, 294 | ExprType: precNone, 295 | } 296 | 297 | var opStr = map[cc.ExprOp]string{ 298 | cc.Add: "+", 299 | cc.AddEq: "+=", 300 | cc.Addr: "&", 301 | cc.And: "&", 302 | cc.AndAnd: "&&", 303 | cc.AndEq: "&=", 304 | cc.Div: "/", 305 | cc.DivEq: "/=", 306 | cc.Eq: "=", 307 | cc.EqEq: "==", 308 | cc.Gt: ">", 309 | cc.GtEq: ">=", 310 | cc.Indir: "*", 311 | cc.Lsh: "<<", 312 | cc.LshEq: "<<=", 313 | cc.Lt: "<", 314 | cc.LtEq: "<=", 315 | cc.Minus: "-", 316 | cc.Mod: "%", 317 | cc.ModEq: "%=", 318 | cc.Mul: "*", 319 | cc.MulEq: "*=", 320 | cc.Not: "!", 321 | cc.NotEq: "!=", 322 | cc.Or: "|", 323 | cc.OrEq: "|=", 324 | cc.OrOr: "||", 325 | cc.Plus: "+", 326 | cc.PreDec: "--", 327 | cc.PreInc: "++", 328 | cc.Rsh: ">>", 329 | cc.RshEq: ">>=", 330 | cc.Sub: "-", 331 | cc.SubEq: "-=", 332 | cc.Twid: "^", 333 | cc.Xor: "^", 334 | cc.XorEq: "^=", 335 | 336 | AndNot: "&^", 337 | AndNotEq: "&^=", 338 | ColonEq: ":=", 339 | } 340 | 341 | const ( 342 | ExprBlock cc.ExprOp = 100000 + iota 343 | ExprSlice 344 | AndNot 345 | AndNotEq 346 | ColonEq 347 | TypeAssert 348 | ExprType 349 | SideEffectFunc 350 | ) 351 | 352 | func (p *Printer) printExpr(x *cc.Expr, prec int) { 353 | if x == nil { 354 | return 355 | } 356 | if p.html { 357 | fmt.Fprintf(&p.buf, "", x.Op, x.XType) 358 | defer fmt.Fprintf(&p.buf, "") 359 | } 360 | 361 | if len(x.Comments.Before) > 0 { 362 | p.Print(x.Comments.Before) 363 | } 364 | defer p.Print(x.Comments.Suffix, x.Comments.After) 365 | 366 | newPrec := opPrec[x.Op] 367 | if prec < newPrec { 368 | p.Print("(") 369 | defer p.Print(")") 370 | } 371 | prec = newPrec 372 | 373 | str := opStr[x.Op] 374 | if str != "" { 375 | if x.Right != nil { 376 | // binary operator 377 | // left associative 378 | p.Print(exprPrec{x.Left, prec}, " ", str, " ", exprPrec{x.Right, prec - 1}) 379 | } else { 380 | // unary operator 381 | if (x.Op == cc.Plus || x.Op == cc.Minus || x.Op == cc.Addr) && x.Left.Op == x.Op || 382 | x.Op == cc.Plus && x.Left.Op == cc.PreInc || 383 | x.Op == cc.Minus && x.Left.Op == cc.PreDec { 384 | prec-- // force parenthesization +(+x) not ++x 385 | } 386 | p.Print(str, exprPrec{x.Left, prec}) 387 | } 388 | return 389 | } 390 | 391 | // special cases 392 | switch x.Op { 393 | default: 394 | panic(fmt.Sprintf("printExpr missing case for %v", x.Op)) 395 | 396 | case ExprBlock: 397 | if len(x.Block) == 0 { 398 | break 399 | } 400 | p.Print("func(){") 401 | for i, stmt := range x.Block { 402 | if i > 0 { 403 | p.Print("; ") 404 | } 405 | p.Print(stmt) 406 | } 407 | p.Print("}()") 408 | 409 | case SideEffectFunc: 410 | p.Print("func() ", x.Text, " {") 411 | sep := "" 412 | for _, stmt := range x.Block { 413 | p.Print(sep, stmt) 414 | sep = "; " 415 | } 416 | if len(x.After) == 0 { 417 | p.Print(sep, "return ", x.Left) 418 | } else { 419 | p.Print(sep, "_r := ", x.Left) 420 | for _, stmt := range x.After { 421 | p.Print("; ", stmt) 422 | } 423 | p.Print("; return _r") 424 | } 425 | p.Print(" }()") 426 | 427 | case ExprSlice: 428 | p.Print(x.List[0], "[") 429 | if x.List[1] != nil { 430 | p.Print(x.List[1]) 431 | } 432 | p.Print(":") 433 | if x.List[2] != nil { 434 | p.Print(x.List[2]) 435 | } 436 | p.Print("]") 437 | 438 | case cc.Arrow: 439 | name := x.Text 440 | if x.XDecl != nil { 441 | name = x.XDecl.Name 442 | } 443 | p.Print(exprPrec{x.Left, prec}, ".", name) 444 | 445 | case TypeAssert: 446 | p.Print(exprPrec{x.Left, prec}, ".(", x.Type, ")") 447 | 448 | case ExprType: 449 | p.Print(x.Type) 450 | 451 | case cc.Call: 452 | left := x.Left 453 | if left.Op == cc.Paren && left.Left.Op == cc.Indir { 454 | // print (*f)() as f() 455 | left = left.Left.Left 456 | } 457 | p.Print(exprPrec{left, precAddr}, "(") 458 | for i, y := range x.List { 459 | if i > 0 { 460 | p.Print(", ") 461 | } 462 | p.printExpr(y, precComma) 463 | } 464 | p.Print(")") 465 | 466 | case cc.Cast: 467 | if x.Type.Kind == cc.Ptr || x.Type.Kind == cc.Func { 468 | p.Print("(", x.Type, ")(", exprPrec{x.Left, precLow}, ")") 469 | } else { 470 | p.Print(x.Type, "(", exprPrec{x.Left, precLow}, ")") 471 | } 472 | 473 | case cc.CastInit: 474 | if x.Type.Kind == cc.Ptr || x.Type.Kind == cc.Func { 475 | p.Print("(", x.Type, ")", x.Init) 476 | } else { 477 | p.Print(x.Type, x.Init) 478 | } 479 | 480 | case cc.Comma: 481 | if len(x.List) == 0 { 482 | break 483 | } 484 | p.Print("(func() {") 485 | for i, y := range x.List { 486 | if i > 0 { 487 | p.Print("; ") 488 | } 489 | p.printExpr(y, precLow) 490 | } 491 | p.Print("}())") 492 | 493 | case cc.Cond: 494 | p.Print("TERNARY(", exprPrec{x.List[0], prec - 1}, ", ", exprPrec{x.List[1], prec}, ", ", exprPrec{x.List[2], prec}, ")") 495 | 496 | case cc.Dot: 497 | name := x.Text 498 | p.Print(exprPrec{x.Left, prec}, ".", name) 499 | 500 | case cc.Index: 501 | p.Print(exprPrec{x.Left, prec}, "[", exprPrec{x.Right, precLow}, "]") 502 | 503 | case cc.Name: 504 | name := x.Text 505 | if x.XDecl != nil { 506 | name = x.XDecl.Name 507 | if x.XDecl.GoPackage != "" && p.Package != "" && x.XDecl.GoPackage != p.Package { 508 | name = path.Base(x.XDecl.GoPackage) + "." + name 509 | } 510 | } 511 | p.Print(name) 512 | 513 | case cc.Number: 514 | p.Print(strings.TrimPrefix(strings.TrimRight(x.Text, "LlUu"), "L")) 515 | 516 | case cc.SizeofExpr: 517 | p.Print("sizeof(", x.Left, ")") 518 | 519 | case cc.String: 520 | for i, str := range x.Texts { 521 | if i > 0 { 522 | p.Print(" + ") 523 | } 524 | p.Print(strings.TrimPrefix(str, "L")) 525 | } 526 | 527 | case cc.Offsetof: 528 | p.Print("offsetof(", x.Type, ", ", exprPrec{x.Left, precComma}, ")") 529 | 530 | case cc.Paren: 531 | p.Print(exprPrec{x.Left, prec}) 532 | 533 | case cc.PostDec: 534 | p.Print(exprPrec{x.Left, prec}, "--") 535 | 536 | case cc.PostInc: 537 | p.Print(exprPrec{x.Left, prec}, "++") 538 | 539 | case cc.SizeofType: 540 | p.Print("sizeof(", x.Type, ")") 541 | 542 | case cc.VaArg: 543 | p.Print("va_arg(", exprPrec{x.Left, precComma}, ", ", x.Type, ")") 544 | } 545 | } 546 | 547 | func (p *Printer) printPrefix(x *cc.Prefix) { 548 | if x.Dot != "" { 549 | p.Print(x.XDecl.Name, ": ") 550 | } else { 551 | p.Print(x.Index, ": ") 552 | } 553 | } 554 | 555 | func (p *Printer) printInit(typ *cc.Type, x *cc.Init) { 556 | p.Print(x.Comments.Before) 557 | defer p.Print(x.Comments.Suffix, x.Comments.After) 558 | 559 | if len(x.Prefix) > 0 { 560 | for _, pre := range x.Prefix { 561 | p.Print(pre) 562 | } 563 | } 564 | if x.Expr != nil { 565 | if x.Expr.Op == cc.Number && (typ.Is(cc.Ptr) || typ.Is(Slice)) { 566 | p.Print("nil") 567 | return 568 | } 569 | p.printExpr(x.Expr, precComma) 570 | return 571 | } 572 | 573 | nl := len(x.Braced) > 0 && x.Braced[0].Span.Start.Line != x.Braced[len(x.Braced)-1].Span.End.Line 574 | if typ != nil { 575 | p.printType(typ) 576 | } 577 | p.Print("{") 578 | startLine := true 579 | if nl { 580 | p.Print(Indent, Newline) 581 | } 582 | for i, y := range x.Braced { 583 | if !startLine { 584 | p.Print(" ") 585 | } 586 | startLine = false 587 | var subtyp *cc.Type 588 | if typ != nil { 589 | if typ.Is(cc.Struct) && i < len(typ.Def().Decls) && len(y.Prefix) == 0 { 590 | subtyp = typ.Def().Decls[i].Type 591 | } else if typ.Is(cc.Struct) && len(y.Prefix) == 1 && y.Prefix[0].XDecl != nil { 592 | subtyp = y.Prefix[0].XDecl.Type 593 | } else if typ.Is(cc.Array) || typ.Is(Slice) { 594 | subtyp = typ.Def().Base 595 | } 596 | } 597 | p.printInit(subtyp, y) 598 | p.Print(",") 599 | if i+1 < len(x.Braced) && y.Span.End.Line != x.Braced[i+1].Span.Start.Line { 600 | p.Print(Newline) 601 | startLine = true 602 | } 603 | } 604 | if nl { 605 | p.Print(Unindent, Newline) 606 | } 607 | p.Print("}") 608 | } 609 | 610 | func (p *Printer) printProg(x *cc.Prog) { 611 | p.Print(x.Comments.Before) 612 | defer p.Print(x.Comments.Suffix, x.Comments.After, Newline) 613 | 614 | for _, decl := range x.Decls { 615 | p.printDecl(decl, true) 616 | p.Print(Newline) 617 | } 618 | } 619 | 620 | const ( 621 | BlockNoBrace cc.StmtOp = 100000 + iota 622 | ForRange 623 | Fallthrough 624 | ) 625 | 626 | func (p *Printer) printStmt(x *cc.Stmt, canNest bool) { 627 | printedComments := false 628 | if x.Op == BlockNoBrace { 629 | canNest = false 630 | } 631 | if len(x.Labels) > 0 { 632 | p.Print(Untab, Unindent, x.Comments.Before, Indent, "\t") 633 | printedComments = true 634 | for i := 0; i < len(x.Labels); i++ { 635 | lab := x.Labels[i] 636 | p.Print(Untab, Unindent, lab.Comments.Before, Indent, "\t") 637 | p.Print(Untab) 638 | switch lab.Op { 639 | case cc.LabelName: 640 | p.Print(lab.Name) 641 | case cc.Case: 642 | p.Print("case ", lab.Expr) 643 | for i+1 < len(x.Labels) && x.Labels[i+1].Op == cc.Case { 644 | p.Print(", ", lab.Comments.Suffix, Newline) 645 | i++ 646 | lab = x.Labels[i] 647 | p.Print(lab.Comments.Before, lab.Expr) 648 | } 649 | if i+1 < len(x.Labels) && x.Labels[i+1].Op == cc.Default { 650 | p.Print(":", lab.Comments.Suffix, Newline) 651 | i++ 652 | lab = x.Labels[i] 653 | p.Print("fallthrough", Newline, "default") 654 | } 655 | case cc.Default: 656 | p.Print("default") 657 | } 658 | p.Print(":", lab.Comments.Suffix, Newline) 659 | } 660 | } 661 | if x.Op == cc.Block { 662 | if canNest { 663 | p.Print("{", Indent) 664 | } 665 | if !printedComments { 666 | p.Print(x.Comments.Before) 667 | printedComments = true 668 | } 669 | } 670 | if !printedComments { 671 | p.Print(x.Comments.Before) 672 | } 673 | defer p.Print(x.Comments.Suffix, x.Comments.After) 674 | 675 | switch x.Op { 676 | case cc.Block, BlockNoBrace: 677 | // { printed above 678 | var sep interface{} = Newline 679 | if !canNest { 680 | sep = "" 681 | } 682 | for _, b := range x.Block { 683 | if b.Op == cc.StmtDecl && p.printed[b.Decl] { 684 | continue 685 | } 686 | p.Print(sep, b) 687 | sep = Newline 688 | } 689 | if canNest { 690 | p.Print(Unindent, Newline, "}") 691 | } 692 | 693 | case Fallthrough: 694 | p.Print("fallthrough") 695 | 696 | case cc.Break: 697 | p.Print("break") 698 | 699 | case cc.Continue: 700 | p.Print("continue") 701 | 702 | case cc.Do: 703 | p.Print("for ") 704 | if x.Pre != nil { 705 | p.Print(x.Pre, "; ; ", x.Pre, " ") 706 | } 707 | p.Print("{", Indent, Newline, unnestBlock{x.Body}, Newline, "if ", &cc.Expr{Op: cc.Not, Left: x.Expr}, " {", Indent, Newline, "break", Unindent, Newline, "}", Unindent, Newline, "}") 708 | 709 | case cc.Empty: 710 | p.Print(Unnewline) 711 | 712 | case cc.For: 713 | p.Print("for ", x.Pre, "; ", x.Expr, "; ", x.Post, nestBlock{x.Body, false}) 714 | 715 | case ForRange: 716 | p.Print("for ", x.Pre, " = range ", x.Post, nestBlock{x.Body, false}) 717 | 718 | case cc.If: 719 | p.Print("if ") 720 | if x.Pre != nil { 721 | p.Print(x.Pre, "; ") 722 | } 723 | p.Print(x.Expr, nestBlock{x.Body, x.Else != nil}) 724 | if x.Else != nil { 725 | if x.Else.Op == cc.If { 726 | p.Print(" else ", x.Else) 727 | } else { 728 | p.Print(" else", nestBlock{x.Else, false}) 729 | } 730 | } 731 | 732 | case cc.Goto: 733 | p.Print("goto ", x.Text) 734 | 735 | case cc.Return: 736 | if x.Expr == nil { 737 | p.Print("return") 738 | } else { 739 | p.Print("return ", x.Expr) 740 | } 741 | 742 | case cc.StmtDecl: 743 | p.Print(x.Decl) 744 | 745 | case cc.StmtExpr: 746 | p.Print(x.Expr) 747 | 748 | case cc.Switch: 749 | p.Print("switch ", x.Expr, nestBlock{x.Body, false}) 750 | 751 | case cc.While: 752 | p.Print("for ") 753 | if x.Pre != nil { 754 | p.Print(x.Pre, "; ") 755 | } 756 | p.Print(x.Expr) 757 | if x.Pre != nil { 758 | p.Print("; ", x.Pre, " ") 759 | } 760 | p.Print(nestBlock{x.Body, false}) 761 | } 762 | } 763 | 764 | const ( 765 | Bool cc.TypeKind = 100000 + iota 766 | Int8 767 | Uint8 768 | Byte 769 | Int16 770 | Uint16 771 | Int 772 | Uint 773 | Int32 774 | Rune 775 | Uint32 776 | Uintptr 777 | Int64 778 | Uint64 779 | Float32 780 | Float64 781 | Ideal 782 | String 783 | Slice 784 | ) 785 | 786 | func (p *Printer) printType(t *cc.Type) { 787 | if t == nil { 788 | p.Print("nil_type") 789 | return 790 | } 791 | 792 | // Shouldn't happen but handle in case it does. 793 | p.Print(t.Comments.Before) 794 | defer p.Print(t.Comments.Suffix, t.Comments.After) 795 | 796 | if t == cc.BoolType { 797 | p.Print("bool") 798 | return 799 | } 800 | if typemap[t.Kind] != "" { 801 | p.Print(typemap[t.Kind]) 802 | return 803 | } 804 | 805 | switch t.Kind { 806 | default: 807 | if t.String() == "" { 808 | p.Print("C.unknown") 809 | break 810 | } 811 | p.Print("C.", t.String()) // hope for the best 812 | 813 | case Slice: 814 | p.Print("[]", t.Base) 815 | 816 | case String: 817 | p.Print("string") 818 | 819 | case cc.Struct: 820 | if len(t.Decls) == 0 { 821 | p.Print("struct{}") 822 | break 823 | } 824 | if t.Tag != "" { 825 | p.Print(t.Tag) 826 | break 827 | } 828 | p.Print("struct {", Indent) 829 | p.printStructBody(t) 830 | p.Print(Unindent, Newline, "}") 831 | 832 | case cc.Enum: 833 | if t.Tag != "" { 834 | p.Print(t.Tag) 835 | } else { 836 | panic("enum") 837 | p.Print("int") 838 | } 839 | 840 | case cc.TypedefType: 841 | if t.Base != nil && typemap[t.Base.Kind] != "" && strings.ToLower(t.Name) == t.Name { 842 | p.Print(typemap[t.Base.Kind]) 843 | return 844 | } 845 | if t.TypeDecl != nil && t.TypeDecl.GoPackage != "" && p.Package != "" && t.TypeDecl.GoPackage != p.Package { 846 | p.Print(path.Base(t.TypeDecl.GoPackage) + "." + t.Name) 847 | break 848 | } 849 | p.Print(t.Name) 850 | 851 | case cc.Ptr: 852 | if t.Base.Is(cc.Func) { 853 | p.Print(t.Base) 854 | return 855 | } 856 | if t.Base.Is(cc.Void) { 857 | p.Print("*[0]byte") 858 | return 859 | } 860 | if t.Slice { 861 | p.Print("[]") 862 | } else { 863 | p.Print("*") 864 | } 865 | p.Print(t.Base) 866 | 867 | case cc.Func: 868 | p.Print("func(") 869 | for i, arg := range t.Decls { 870 | if i > 0 { 871 | p.Print(", ") 872 | } 873 | if arg.Name == "..." { 874 | p.Print("...interface{}") 875 | continue 876 | } 877 | if arg.Name == "" && arg.Type.Is(cc.Void) { 878 | continue 879 | } 880 | p.Print(arg.Type) 881 | } 882 | p.Print(")") 883 | if !t.Base.Is(cc.Void) { 884 | p.Print(" ", t.Base) 885 | } 886 | 887 | case cc.Array: 888 | if t.Width == nil { 889 | p.Print("[unknown]", t.Base) 890 | return 891 | } 892 | p.Print("[", t.Width, "]", t.Base) 893 | 894 | case cc.Int, cc.Long: 895 | p.Print("int") 896 | case cc.Uint, cc.Ulong: 897 | p.Print("uint") 898 | } 899 | } 900 | 901 | var typemap = map[cc.TypeKind]string{ 902 | Bool: "bool", 903 | Int8: "int8", 904 | Uint8: "uint8", 905 | Int16: "int16", 906 | Uint16: "uint16", 907 | Rune: "rune", 908 | Byte: "byte", 909 | Int: "int", 910 | Uint: "uint", 911 | Uintptr: "uintptr", 912 | Int32: "int32", 913 | Uint32: "uint32", 914 | Int64: "int64", 915 | Uint64: "uint64", 916 | Float32: "float32", 917 | Float64: "float64", 918 | } 919 | 920 | func (p *Printer) printDecl(decl *cc.Decl, top bool) { 921 | // p.Print(fmt.Sprintf("/*before:%d.%d*/", decl.Span.Start.Line, decl.Span.Start.Byte)) 922 | // defer p.Print(fmt.Sprintf("/*after:%d.%d*/", decl.Span.End.Line, decl.Span.End.Byte)) 923 | 924 | p.Print(decl.Comments.Before) 925 | defer p.Print(decl.Comments.Suffix, decl.Comments.After) 926 | 927 | if decl.Blank { 928 | // p.Print(fmt.Sprintf("/*blank:%d.%d*/", decl.Span.Start.Line, decl.Span.Start.Byte)) 929 | return 930 | } 931 | 932 | if decl.Storage&cc.Extern != 0 { 933 | p.Print("/* extern ") 934 | defer p.Print(" */") 935 | } 936 | 937 | t := decl.Type 938 | if decl.Storage&cc.Typedef != 0 { 939 | if t.Kind == cc.Struct || t.Kind == cc.Union || t.Kind == cc.Enum { 940 | if t.Tag == "" { 941 | t.Tag = decl.Name 942 | } else if decl.Name != t.Tag { 943 | fprintf(decl.Span, "typedef %s and tag %s do not match", decl.Name, t.Tag) 944 | } 945 | if t.Kind == cc.Enum { 946 | p.printEnumDecl(t) 947 | } else { 948 | p.printStructDecl(t) 949 | } 950 | return 951 | } 952 | p.Print("type ", decl.Name, " ", decl.Type) 953 | return 954 | } 955 | 956 | if decl.Name == "" { 957 | switch t.Kind { 958 | case cc.Struct, cc.Union: 959 | p.printStructDecl(t) 960 | return 961 | case cc.Enum: 962 | p.printEnumDecl(t) 963 | return 964 | } 965 | if t.Kind == cc.Int && t.Decls != nil { 966 | // was an enum 967 | p.printEnumDecl(t) 968 | return 969 | } 970 | fprintf(decl.Span, "empty declaration of type %s", GoString(t)) 971 | return 972 | } 973 | 974 | if t != nil && t.Kind == cc.Enum { 975 | if !p.dup(t) { 976 | p.printEnumDecl(t) 977 | } 978 | return 979 | } 980 | 981 | if t != nil && t.Kind == cc.Func { 982 | p.printFuncDecl(decl) 983 | return 984 | } 985 | 986 | if t != nil && decl.Init != nil && len(decl.Init.Braced) > 0 { 987 | if top { 988 | p.Print("var ", decl.Name, " = ", typedInit{decl.Type, decl.Init}) 989 | } else { 990 | p.Print(decl.Name, " := ", typedInit{decl.Type, decl.Init}) 991 | } 992 | return 993 | } 994 | 995 | if decl.Init != nil && !top { 996 | p.Print(decl.Name, " := ", decl.Init) 997 | return 998 | } 999 | 1000 | if t != nil { 1001 | p.Print("var ", decl.Name, " ", decl.Type) 1002 | } else { 1003 | p.Print("var ", decl.Name, " unknown") 1004 | } 1005 | if decl.Init != nil { 1006 | p.Print(" = ", decl.Init) 1007 | } 1008 | } 1009 | 1010 | func (p *Printer) printStructDecl(t *cc.Type) { 1011 | if p.dup(t) { 1012 | return 1013 | } 1014 | if t.Kind == cc.Union { 1015 | fprintf(t.Span, "cannot convert union") 1016 | return 1017 | } 1018 | p.Print("type ", t.Tag, " struct {", Indent) 1019 | p.printStructBody(t) 1020 | p.Print(Unindent, Newline, "}") 1021 | } 1022 | 1023 | func (p *Printer) printStructBody(t *cc.Type) { 1024 | for _, decl := range t.Decls { 1025 | if decl.Name == "" { 1026 | // Hope this is a struct definition. 1027 | if decl.Type.Kind != cc.Struct { 1028 | fprintf(decl.Span, "unnamed non-struct field of type %v", decl.Type) 1029 | continue 1030 | } 1031 | p.printStructBody(decl.Type) 1032 | continue 1033 | } 1034 | p.Print(Newline, decl.Name, " ", decl.Type) 1035 | } 1036 | } 1037 | 1038 | func (p *Printer) printEnumDecl(t *cc.Type) { 1039 | typeSuffix := "" 1040 | if t.Tag != "" { 1041 | typeSuffix = " " + t.Tag 1042 | p.Print("type ", t.Tag, " int", Newline) 1043 | // fprintf(t.Span, "cannot handle enum tags") 1044 | // return 1045 | } 1046 | p.Print("const (", Indent) 1047 | for i, decl := range t.Decls { 1048 | p.Print(Newline, decl.Name) 1049 | if decl.Init == nil && i == 0 { 1050 | if len(t.Decls) >= 2 && t.Decls[1].Init == nil { 1051 | p.Print(typeSuffix, " = iota") 1052 | } else { 1053 | p.Print(typeSuffix, " = 0") 1054 | } 1055 | } else if decl.Init != nil { 1056 | p.Print(typeSuffix, " = ", decl.Init.Expr) 1057 | if i+1 < len(t.Decls) && t.Decls[i+1].Init == nil { 1058 | p.Print(" + iota") 1059 | if i > 0 { 1060 | p.Print(fmt.Sprintf("-%d", i)) 1061 | } 1062 | } 1063 | } 1064 | } 1065 | p.Print(Unindent, Newline, ")") 1066 | } 1067 | 1068 | func (p *Printer) printFuncDecl(decl *cc.Decl) { 1069 | if decl.Body == nil { 1070 | // wait for definition 1071 | return 1072 | } 1073 | save := decl.Body.Block[:0] 1074 | for _, s := range decl.Body.Block { 1075 | if s.Op == cc.StmtDecl && s.Decl.Storage&cc.Static != 0 { 1076 | p.printDecl(s.Decl, true) 1077 | p.Print(Newline) 1078 | continue 1079 | } 1080 | save = append(save, s) 1081 | } 1082 | decl.Body.Block = save 1083 | 1084 | p.Print("func ", decl.Name, "(") 1085 | for i, arg := range decl.Type.Decls { 1086 | if arg.Type.Is(cc.Void) { 1087 | continue 1088 | } 1089 | if i > 0 { 1090 | p.Print(", ") 1091 | } 1092 | if arg.Name == "..." { 1093 | p.Print("args ...interface{}") 1094 | continue 1095 | } 1096 | p.Print(arg.Name, " ", arg.Type) 1097 | } 1098 | p.Print(")") 1099 | if !decl.Type.Base.Is(cc.Void) { 1100 | p.Print(" ", decl.Type.Base) 1101 | } 1102 | p.Print(" {", Indent, unnestBlock{decl.Body}, Unindent, Newline, "}", Newline) 1103 | } 1104 | -------------------------------------------------------------------------------- /c2gofmt/rename.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "path/filepath" 10 | "strings" 11 | 12 | "rsc.io/cmd/c2gofmt/internal/cc" 13 | ) 14 | 15 | var goKeyword = map[string]bool{ 16 | "chan": true, 17 | "defer": true, 18 | "fallthrough": true, 19 | "func": true, 20 | "go": true, 21 | "import": true, 22 | "interface": true, 23 | "iota": true, 24 | "map": true, 25 | "package": true, 26 | "range": true, 27 | "select": true, 28 | "type": true, 29 | "var": true, 30 | 31 | // not keywords but still need renaming 32 | "fmt": true, 33 | "path": true, 34 | "rune": true, 35 | "true": true, 36 | "false": true, 37 | } 38 | 39 | // renameDecls renames file-local declarations to make them 40 | // unique across the whole set of files being compiled. 41 | // For now, it appends the file base name to the declared name. 42 | // Eventually it could be smarter and not do that when not necessary. 43 | // It also renames names like 'type' and 'func' to avoid Go keywords. 44 | // 45 | // renameDecls returns the new list of top-level declarations. 46 | func renameDecls(prog *cc.Prog) []*cc.Decl { 47 | // Rewrite C identifiers to avoid important Go words (keywords, iota, etc). 48 | cc.Preorder(prog, func(x cc.Syntax) { 49 | switch x := x.(type) { 50 | case *cc.Decl: 51 | if goKeyword[x.Name] { 52 | // NOTE: Must put _ last so that name can be upper-cased for export. 53 | x.Name += "_" 54 | } 55 | 56 | case *cc.Stmt: 57 | for _, lab := range x.Labels { 58 | if goKeyword[lab.Name] { 59 | lab.Name += "_" 60 | } 61 | } 62 | switch x.Op { 63 | case cc.Goto: 64 | if goKeyword[x.Text] { 65 | x.Text += "_" 66 | } 67 | } 68 | 69 | case *cc.Expr: 70 | switch x.Op { 71 | case cc.Dot, cc.Arrow, cc.Name: 72 | if goKeyword[x.Text] { 73 | x.Text += "_" 74 | } 75 | } 76 | } 77 | }) 78 | 79 | // Build list of declared top-level names. 80 | // Not just prog.Decls because of enums and struct definitions. 81 | typedefs := map[*cc.Type]*cc.Decl{} 82 | for _, d := range prog.Decls { 83 | if d.Storage&cc.Typedef != 0 { 84 | typedefs[d.Type] = d 85 | } 86 | } 87 | 88 | var decls []*cc.Decl 89 | for _, d := range prog.Decls { 90 | if d.Blank { 91 | decls = append(decls, d) 92 | continue 93 | } 94 | if d.Name == "" { 95 | if td := typedefs[d.Type]; td != nil { 96 | // Print the definition here and not at the location of the typedef. 97 | td.Blank = true 98 | d.Name = td.Name 99 | d.Storage |= cc.Typedef 100 | decls = append(decls, d) 101 | continue 102 | } 103 | switch d.Type.Kind { 104 | case cc.Struct: 105 | if d.Type.Tag != "" { 106 | decls = append(decls, d) 107 | d.Name = d.Type.Tag 108 | d.Storage = cc.Typedef 109 | } else { 110 | d.Blank = true 111 | decls = append(decls, d) 112 | } 113 | if d.Type.TypeDecl == nil { 114 | d.Type.TypeDecl = d 115 | } 116 | case cc.Enum: 117 | d.Blank = true 118 | decls = append(decls, d) 119 | for _, dd := range d.Type.Decls { 120 | decls = append(decls, dd) 121 | } 122 | } 123 | continue 124 | } 125 | decls = append(decls, d) 126 | if d.Storage&cc.Typedef != 0 && d.Type != nil && d.Type.TypeDecl == nil { 127 | d.Type.TypeDecl = d 128 | } 129 | } 130 | 131 | // Identify declaration conflicts. 132 | count := make(map[string]int) 133 | src := make(map[string]string) 134 | for _, d := range decls { 135 | if d.Blank { 136 | continue 137 | } 138 | if count[d.Name]++; count[d.Name] > 1 { 139 | fprintf(d.Span, "conflicting name %s (last at %s)", d.Name, src[d.Name]) 140 | continue 141 | } 142 | src[d.Name] = fmt.Sprintf("%s:%d", d.Span.Start.File, d.Span.Start.Line) 143 | } 144 | 145 | // Rename static, conflicting names. 146 | for _, d := range decls { 147 | if count[d.Name] > 1 { 148 | file := filepath.Base(d.Span.Start.File) 149 | if i := strings.Index(file, "."); i >= 0 { 150 | file = file[:i] 151 | } 152 | d.Name += "_" + file 153 | } 154 | 155 | if d.Type != nil && d.Type.Kind == cc.Func { 156 | if d.Body != nil { 157 | for _, s := range d.Body.Block { 158 | if s.Op == cc.StmtDecl && s.Decl.Storage&cc.Static != 0 { 159 | // Add function name as prefix. 160 | // Will print at top level. 161 | dd := s.Decl 162 | dd.Name = d.Name + "_" + dd.Name 163 | } 164 | } 165 | } 166 | } 167 | } 168 | 169 | return decls 170 | } 171 | -------------------------------------------------------------------------------- /c2gofmt/rewrite.go: -------------------------------------------------------------------------------- 1 | // Copyright 2009 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Adapted from go/src/cmd/gofmt/rewrite.go. 6 | 7 | package main 8 | 9 | import ( 10 | "bytes" 11 | "go/ast" 12 | "go/parser" 13 | "go/printer" 14 | "go/token" 15 | "log" 16 | "reflect" 17 | "strings" 18 | "unicode" 19 | "unicode/utf8" 20 | ) 21 | 22 | type rule struct { 23 | old, new ast.Expr 24 | } 25 | 26 | var rules []rule 27 | 28 | func parseRules(rulefile string, data string) { 29 | for i, line := range strings.Split(data, "\n") { 30 | line = strings.TrimSpace(line) 31 | if line == "" || line[0] == '#' { 32 | continue 33 | } 34 | f := strings.Split(line, "->") 35 | if len(f) != 2 { 36 | log.Fatalf("%s:%d: rewrite rule must be 'old -> new'", rulefile, i+1) 37 | } 38 | old, err := parser.ParseExpr(f[0]) 39 | if err != nil { 40 | log.Fatalf("%s:%d: %v", rulefile, i+1, err) 41 | } 42 | new, err := parser.ParseExpr(f[1]) 43 | if err != nil { 44 | log.Fatalf("%s:%d: %v", rulefile, i+1, err) 45 | } 46 | rules = append(rules, rule{old, new}) 47 | } 48 | } 49 | 50 | func rewriteFile(fset *token.FileSet, f *ast.File, rules []rule) *ast.File { 51 | cmap := ast.NewCommentMap(fset, f, f.Comments) 52 | m := make(map[string]reflect.Value) 53 | 54 | var rewriteVal func(val reflect.Value) reflect.Value 55 | rewriteVal = func(val reflect.Value) reflect.Value { 56 | // don't bother if val is invalid to start with 57 | if !val.IsValid() { 58 | return reflect.Value{} 59 | } 60 | 61 | val = apply(rewriteVal, val) 62 | 63 | for _, r := range rules { 64 | pat := reflect.ValueOf(r.old) 65 | repl := reflect.ValueOf(r.new) 66 | for k := range m { 67 | delete(m, k) 68 | } 69 | if match(m, pat, val) { 70 | val = subst(m, repl, reflect.ValueOf(val.Interface().(ast.Node).Pos())) 71 | } 72 | } 73 | 74 | return val 75 | } 76 | r := apply(rewriteVal, reflect.ValueOf(f)).Interface().(*ast.File) 77 | 78 | rewriteVal = func(val reflect.Value) reflect.Value { 79 | // rewrite (&x).f and (*x).f to x.f. 80 | if !val.IsValid() { 81 | return reflect.Value{} 82 | } 83 | val = apply(rewriteVal, val) 84 | if val.Type() == selectorType { 85 | sel := val.Interface().(*ast.SelectorExpr) 86 | x := sel.X 87 | if p, ok := x.(*ast.ParenExpr); ok { 88 | x = p.X 89 | } 90 | if addr, ok := x.(*ast.UnaryExpr); ok && addr.Op == token.AND { 91 | sel.X = addr.X 92 | } else if star, ok := x.(*ast.StarExpr); ok { 93 | sel.X = star.X 94 | } 95 | } 96 | return val 97 | } 98 | r = apply(rewriteVal, reflect.ValueOf(r)).Interface().(*ast.File) 99 | 100 | r.Comments = cmap.Filter(r).Comments() // recreate comments list 101 | return r 102 | } 103 | 104 | func astString(fset *token.FileSet, n ast.Node) string { 105 | var buf bytes.Buffer 106 | printer.Fprint(&buf, fset, n) 107 | return buf.String() 108 | } 109 | 110 | // set is a wrapper for x.Set(y); it protects the caller from panics if x cannot be changed to y. 111 | func set(x, y reflect.Value) { 112 | // don't bother if x cannot be set or y is invalid 113 | if !x.CanSet() || !y.IsValid() { 114 | return 115 | } 116 | defer func() { 117 | if x := recover(); x != nil { 118 | if s, ok := x.(string); ok && 119 | (strings.Contains(s, "type mismatch") || strings.Contains(s, "not assignable")) { 120 | // x cannot be set to y - ignore this rewrite 121 | return 122 | } 123 | panic(x) 124 | } 125 | }() 126 | x.Set(y) 127 | } 128 | 129 | // Values/types for special cases. 130 | var ( 131 | objectPtrNil = reflect.ValueOf((*ast.Object)(nil)) 132 | scopePtrNil = reflect.ValueOf((*ast.Scope)(nil)) 133 | 134 | identType = reflect.TypeOf((*ast.Ident)(nil)) 135 | objectPtrType = reflect.TypeOf((*ast.Object)(nil)) 136 | positionType = reflect.TypeOf(token.NoPos) 137 | callExprType = reflect.TypeOf((*ast.CallExpr)(nil)) 138 | scopePtrType = reflect.TypeOf((*ast.Scope)(nil)) 139 | selectorType = reflect.TypeOf((*ast.SelectorExpr)(nil)) 140 | ) 141 | 142 | // apply replaces each AST field x in val with f(x), returning val. 143 | // To avoid extra conversions, f operates on the reflect.Value form. 144 | func apply(f func(reflect.Value) reflect.Value, val reflect.Value) reflect.Value { 145 | if !val.IsValid() { 146 | return reflect.Value{} 147 | } 148 | 149 | // *ast.Objects introduce cycles and are likely incorrect after 150 | // rewrite; don't follow them but replace with nil instead 151 | if val.Type() == objectPtrType { 152 | return objectPtrNil 153 | } 154 | 155 | // similarly for scopes: they are likely incorrect after a rewrite; 156 | // replace them with nil 157 | if val.Type() == scopePtrType { 158 | return scopePtrNil 159 | } 160 | 161 | switch v := reflect.Indirect(val); v.Kind() { 162 | case reflect.Slice: 163 | for i := 0; i < v.Len(); i++ { 164 | e := v.Index(i) 165 | set(e, f(e)) 166 | } 167 | case reflect.Struct: 168 | for i := 0; i < v.NumField(); i++ { 169 | if val.Type() == selectorType && selectorType.Elem().Field(i).Name == "Sel" { 170 | continue 171 | } 172 | e := v.Field(i) 173 | set(e, f(e)) 174 | } 175 | case reflect.Interface: 176 | e := v.Elem() 177 | set(v, f(e)) 178 | } 179 | return val 180 | } 181 | 182 | func isWildcard(s string) bool { 183 | rune, size := utf8.DecodeRuneInString(s) 184 | return size == len(s) && unicode.IsLower(rune) 185 | } 186 | 187 | // match reports whether pattern matches val, 188 | // recording wildcard submatches in m. 189 | // If m == nil, match checks whether pattern == val. 190 | func match(m map[string]reflect.Value, pattern, val reflect.Value) bool { 191 | // Wildcard matches any expression. If it appears multiple 192 | // times in the pattern, it must match the same expression 193 | // each time. 194 | if m != nil && pattern.IsValid() && pattern.Type() == identType { 195 | name := pattern.Interface().(*ast.Ident).Name 196 | if isWildcard(name) && val.IsValid() { 197 | // wildcards only match valid (non-nil) expressions. 198 | if _, ok := val.Interface().(ast.Expr); ok && !val.IsNil() { 199 | if old, ok := m[name]; ok { 200 | return match(nil, old, val) 201 | } 202 | m[name] = val 203 | return true 204 | } 205 | } 206 | } 207 | 208 | // Otherwise, pattern and val must match recursively. 209 | if !pattern.IsValid() || !val.IsValid() { 210 | return !pattern.IsValid() && !val.IsValid() 211 | } 212 | if pattern.Type() != val.Type() { 213 | return false 214 | } 215 | 216 | // Special cases. 217 | switch pattern.Type() { 218 | case identType: 219 | // For identifiers, only the names need to match 220 | // (and none of the other *ast.Object information). 221 | // This is a common case, handle it all here instead 222 | // of recursing down any further via reflection. 223 | p := pattern.Interface().(*ast.Ident) 224 | v := val.Interface().(*ast.Ident) 225 | return p == nil && v == nil || p != nil && v != nil && p.Name == v.Name 226 | case objectPtrType, positionType: 227 | // object pointers and token positions always match 228 | return true 229 | case callExprType: 230 | // For calls, the Ellipsis fields (token.Position) must 231 | // match since that is how f(x) and f(x...) are different. 232 | // Check them here but fall through for the remaining fields. 233 | p := pattern.Interface().(*ast.CallExpr) 234 | v := val.Interface().(*ast.CallExpr) 235 | if p.Ellipsis.IsValid() != v.Ellipsis.IsValid() { 236 | return false 237 | } 238 | } 239 | 240 | p := reflect.Indirect(pattern) 241 | v := reflect.Indirect(val) 242 | if !p.IsValid() || !v.IsValid() { 243 | return !p.IsValid() && !v.IsValid() 244 | } 245 | 246 | switch p.Kind() { 247 | case reflect.Slice: 248 | if p.Len() != v.Len() { 249 | return false 250 | } 251 | for i := 0; i < p.Len(); i++ { 252 | if !match(m, p.Index(i), v.Index(i)) { 253 | return false 254 | } 255 | } 256 | return true 257 | 258 | case reflect.Struct: 259 | for i := 0; i < p.NumField(); i++ { 260 | m := m 261 | if pattern.Type() == selectorType && selectorType.Elem().Field(i).Name == "Sel" { 262 | m = nil 263 | } 264 | if !match(m, p.Field(i), v.Field(i)) { 265 | return false 266 | } 267 | } 268 | return true 269 | 270 | case reflect.Interface: 271 | return match(m, p.Elem(), v.Elem()) 272 | } 273 | 274 | // Handle token integers, etc. 275 | return p.Interface() == v.Interface() 276 | } 277 | 278 | // subst returns a copy of pattern with values from m substituted in place 279 | // of wildcards and pos used as the position of tokens from the pattern. 280 | // if m == nil, subst returns a copy of pattern and doesn't change the line 281 | // number information. 282 | func subst(m map[string]reflect.Value, pattern reflect.Value, pos reflect.Value) reflect.Value { 283 | if !pattern.IsValid() { 284 | return reflect.Value{} 285 | } 286 | 287 | // Wildcard gets replaced with map value. 288 | if m != nil && pattern.Type() == identType { 289 | name := pattern.Interface().(*ast.Ident).Name 290 | if isWildcard(name) { 291 | if old, ok := m[name]; ok { 292 | return subst(nil, old, reflect.Value{}) 293 | } 294 | } 295 | } 296 | 297 | if pos.IsValid() && pattern.Type() == positionType { 298 | // use new position only if old position was valid in the first place 299 | if old := pattern.Interface().(token.Pos); !old.IsValid() { 300 | return pattern 301 | } 302 | return pos 303 | } 304 | 305 | // Otherwise copy. 306 | switch p := pattern; p.Kind() { 307 | case reflect.Slice: 308 | if p.IsNil() { 309 | // Do not turn nil slices into empty slices. go/ast 310 | // guarantees that certain lists will be nil if not 311 | // populated. 312 | return reflect.Zero(p.Type()) 313 | } 314 | v := reflect.MakeSlice(p.Type(), p.Len(), p.Len()) 315 | for i := 0; i < p.Len(); i++ { 316 | v.Index(i).Set(subst(m, p.Index(i), pos)) 317 | } 318 | return v 319 | 320 | case reflect.Struct: 321 | v := reflect.New(p.Type()).Elem() 322 | for i := 0; i < p.NumField(); i++ { 323 | m := m 324 | if p.Type() == selectorType.Elem() && selectorType.Elem().Field(i).Name == "Sel" { 325 | m = nil 326 | } 327 | v.Field(i).Set(subst(m, p.Field(i), pos)) 328 | } 329 | return v 330 | 331 | case reflect.Ptr: 332 | v := reflect.New(p.Type()).Elem() 333 | if elem := p.Elem(); elem.IsValid() { 334 | v.Set(subst(m, elem, pos).Addr()) 335 | } 336 | return v 337 | 338 | case reflect.Interface: 339 | v := reflect.New(p.Type()).Elem() 340 | if elem := p.Elem(); elem.IsValid() { 341 | v.Set(subst(m, elem, pos)) 342 | } 343 | return v 344 | } 345 | 346 | return pattern 347 | } 348 | -------------------------------------------------------------------------------- /c2gofmt/syntax.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | 10 | "rsc.io/cmd/c2gofmt/internal/cc" 11 | ) 12 | 13 | // Rewrite from C constructs to Go constructs. 14 | func rewriteSyntax(prog *cc.Prog) { 15 | cc.Preorder(prog, func(x cc.Syntax) { 16 | switch x := x.(type) { 17 | case *cc.Stmt: 18 | rewriteStmt(x) 19 | 20 | case *cc.Expr: 21 | switch x.Op { 22 | case cc.Name: 23 | switch x.Text { 24 | case "nil": 25 | x.XDecl = nil // just nil, not main.Nil 26 | case "nelem": 27 | x.Text = "len" 28 | x.XDecl = nil 29 | } 30 | case cc.Number: 31 | // Rewrite char literal. 32 | // In general we'd need to rewrite all string and char literals 33 | // but these are the only forms that comes up. 34 | switch x.Text { 35 | case `'\0'`: 36 | x.Text = `'\x00'` 37 | case `'\"'`: 38 | x.Text = `'"'` 39 | } 40 | 41 | case cc.Paren: 42 | switch x.Left.Op { 43 | case cc.Number, cc.Name: 44 | fixMerge(x, x.Left) 45 | } 46 | 47 | case cc.Eq, cc.EqEq, cc.NotEq: 48 | // rewrite p = 0 and p == 0 to p = nil for pointers. 49 | if x.Right.Op == cc.Number && x.Right.Text == "0" && 50 | x.Left.Op == cc.Name && x.Left.XDecl != nil && x.Left.XDecl.Type != nil && x.Left.XDecl.Type.Kind == cc.Ptr { 51 | x.Right.Op = cc.Name 52 | x.Right.Text = "nil" 53 | } 54 | 55 | case cc.Index: 56 | // If we see x[i], record that x should be a slice instead of a pointer. 57 | x := x 58 | depth := 1 59 | for x.Left.Op == cc.Index { 60 | x = x.Left 61 | depth++ 62 | } 63 | if x.Left.Op == cc.Name && x.Left.XDecl != nil { 64 | t := x.Left.XDecl.Type 65 | if t != nil { 66 | for ; depth > 0; depth-- { 67 | if t.Kind != cc.Ptr { 68 | break 69 | } 70 | t.Slice = true 71 | t = t.Base 72 | } 73 | } 74 | } 75 | } 76 | 77 | case *cc.Type: 78 | // Rewrite int f(void) to int f(). 79 | if x.Kind == cc.Func && len(x.Decls) == 1 && x.Decls[0].Name == "" && x.Decls[0].Type.Is(cc.Void) { 80 | x.Decls = nil 81 | } 82 | } 83 | }) 84 | 85 | // Apply changed struct tags to typedefs. 86 | // Excise dead pieces. 87 | cc.Postorder(prog, func(x cc.Syntax) { 88 | switch x := x.(type) { 89 | case *cc.Type: 90 | if x.Kind == cc.TypedefType && x.Base != nil && x.Base.Tag != "" { 91 | x.Name = x.Base.Tag 92 | } 93 | 94 | case *cc.Stmt: 95 | if x.Op == cc.StmtExpr && x.Expr.Op == cc.Comma && len(x.Expr.List) == 0 { 96 | x.Op = cc.Empty 97 | } 98 | 99 | case *cc.Expr: 100 | switch x.Op { 101 | case cc.Add, cc.Sub: 102 | // Turn p + y - z, which is really (p + y) - z, into p + (y - z), 103 | // so that there is only one pointer addition (which will turn into 104 | // a slice operation using y-z as the index). 105 | if x.XType != nil && x.XType.Kind == cc.Ptr { 106 | switch x.Left.Op { 107 | case cc.Add, cc.Sub: 108 | if x.Left.XType != nil && x.Left.XType.Kind == cc.Ptr { 109 | p, op1, y, op2, z := x.Left.Left, x.Left.Op, x.Left.Right, x.Op, x.Right 110 | if op1 == cc.Sub { 111 | y = &cc.Expr{Op: cc.Minus, Left: y, XType: y.XType} 112 | } 113 | x.Op = cc.Add 114 | x.Left = p 115 | x.Right = &cc.Expr{Op: op2, Left: y, Right: z, XType: x.XType} 116 | } 117 | } 118 | } 119 | } 120 | 121 | // Turn c + p - q, which is really (c + p) - q, into c + (p - q), 122 | // so that there is no int + ptr addition, only a ptr - ptr subtraction. 123 | if x.Op == cc.Sub && x.Left.Op == cc.Add && !isPtrOrArray(x.XType) && isPtrOrArray(x.Left.XType) && !isPtrOrArray(x.Left.Left.XType) { 124 | c, p, q := x.Left.Left, x.Left.Right, x.Right 125 | expr := x.Left 126 | expr.Left = p 127 | expr.Right = q 128 | expr.Op = cc.Sub 129 | x.Op = cc.Add 130 | x.Left = c 131 | x.Right = expr 132 | expr.XType = x.XType 133 | } 134 | } 135 | }) 136 | 137 | cc.Preorder(prog, func(x cc.Syntax) { 138 | switch x := x.(type) { 139 | case *cc.Expr: 140 | switch x.Op { 141 | case cc.AndAnd, cc.OrOr: 142 | fixBool(x.Left) 143 | fixBool(x.Right) 144 | 145 | case cc.Not: 146 | fixBool(x.Left) 147 | } 148 | 149 | case *cc.Stmt: 150 | switch x.Op { 151 | case cc.If, cc.For: 152 | if x.Expr != nil { 153 | fixBool(x.Expr) 154 | } 155 | } 156 | } 157 | }) 158 | 159 | cc.Postorder(prog, func(x cc.Syntax) { 160 | switch x := x.(type) { 161 | case *cc.Expr: 162 | switch x.Op { 163 | case cc.OrEq, cc.AndEq, cc.Or, cc.Eq, cc.EqEq, cc.NotEq, cc.LtEq, cc.GtEq, cc.Lt, cc.Gt: 164 | cutParen(x, cc.Or, cc.And, cc.Lsh, cc.Rsh) 165 | case cc.Paren: 166 | switch x.Left.Op { 167 | case cc.Dot, cc.Arrow: 168 | fixMerge(x, x.Left) 169 | } 170 | } 171 | } 172 | }) 173 | 174 | } 175 | 176 | func cutParen(x *cc.Expr, ops ...cc.ExprOp) { 177 | if x.Left != nil && x.Left.Op == cc.Paren { 178 | for _, op := range ops { 179 | if x.Left.Left.Op == op { 180 | fixMerge(x.Left, x.Left.Left) 181 | break 182 | } 183 | } 184 | } 185 | if x.Right != nil && x.Right.Op == cc.Paren { 186 | for _, op := range ops { 187 | if x.Right.Left.Op == op { 188 | fixMerge(x.Right, x.Right.Left) 189 | break 190 | } 191 | } 192 | } 193 | } 194 | 195 | func isPtrOrArray(t *cc.Type) bool { 196 | return t != nil && (t.Kind == cc.Ptr || t.Kind == cc.Array) 197 | } 198 | 199 | func rewriteStmt(stmt *cc.Stmt) { 200 | // TODO: Double-check stmt.Labels 201 | 202 | switch stmt.Op { 203 | case cc.Do: 204 | // Rewrite do { ... } while(x) 205 | // to for(;;) { ... if(!x) break } 206 | // Since rewriteStmt is called in a preorder traversal, 207 | // the recursion into the children will clean up x 208 | // in the if condition as needed. 209 | stmt.Op = cc.For 210 | x := stmt.Expr 211 | stmt.Expr = nil 212 | stmt.Body = forceBlock(stmt.Body) 213 | stmt.Body.Block = append(stmt.Body.Block, &cc.Stmt{ 214 | Op: cc.If, 215 | Expr: &cc.Expr{Op: cc.Not, Left: x}, 216 | Body: &cc.Stmt{Op: cc.Break}, 217 | }) 218 | 219 | case cc.While: 220 | stmt.Op = cc.For 221 | fallthrough 222 | 223 | case cc.For: 224 | fixForAndAnd(stmt) 225 | before1, _ := extractSideEffects(stmt.Pre, sideStmt|sideNoAfter) 226 | before2, _ := extractSideEffects(stmt.Expr, sideNoAfter) 227 | if len(before2) > 0 { 228 | x := stmt.Expr 229 | stmt.Expr = nil 230 | stmt.Body = forceBlock(stmt.Body) 231 | top := &cc.Stmt{ 232 | Op: cc.If, 233 | Expr: &cc.Expr{Op: cc.Not, Left: x}, 234 | Body: &cc.Stmt{Op: cc.Break}, 235 | } 236 | stmt.Body.Block = append(append(before2, top), stmt.Body.Block...) 237 | } 238 | if len(before1) > 0 { 239 | old := copyStmt(stmt) 240 | stmt.Pre = nil 241 | stmt.Expr = nil 242 | stmt.Post = nil 243 | stmt.Body = nil 244 | stmt.Op = BlockNoBrace 245 | stmt.Block = append(before1, old) 246 | } 247 | before, after := extractSideEffects(stmt.Post, sideStmt) 248 | if len(before)+len(after) > 0 { 249 | all := append(append(before, &cc.Stmt{Op: cc.StmtExpr, Expr: stmt.Post}), after...) 250 | stmt.Post = &cc.Expr{Op: ExprBlock, Block: all} 251 | } 252 | 253 | case cc.If: 254 | if stmt.Op == cc.If && stmt.Else == nil { 255 | fixIfAndAnd(stmt) 256 | } 257 | fallthrough 258 | case cc.Return: 259 | before, _ := extractSideEffects(stmt.Expr, sideNoAfter) 260 | if len(before) > 0 { 261 | old := copyStmt(stmt) 262 | stmt.Expr = nil 263 | stmt.Body = nil 264 | stmt.Else = nil 265 | stmt.Op = BlockNoBrace 266 | stmt.Block = append(before, old) 267 | } 268 | 269 | case cc.StmtExpr: 270 | before, after := extractSideEffects(stmt.Expr, sideStmt) 271 | if len(before)+len(after) > 0 { 272 | old := copyStmt(stmt) 273 | stmt.Expr = nil 274 | stmt.Op = BlockNoBrace 275 | stmt.Block = append(append(before, old), after...) 276 | } 277 | 278 | case cc.StmtDecl: 279 | if stmt.Decl.Init != nil && stmt.Decl.Init.Expr != nil { 280 | before, after := extractSideEffects(stmt.Decl.Init.Expr, sideStmt) 281 | if len(before)+len(after) > 0 { 282 | old := copyStmt(stmt) 283 | stmt.Expr = nil 284 | stmt.Op = BlockNoBrace 285 | stmt.Block = append(append(before, old), after...) 286 | } 287 | } 288 | 289 | case cc.Goto: 290 | // TODO: Figure out where the goto goes and maybe rewrite 291 | // to labeled break/continue. 292 | // Otherwise move code or something. 293 | 294 | case cc.ARGBEGIN: 295 | stmt.Op = cc.Switch 296 | stmt.Body = stmt.Block[0] 297 | stmt.Expr = &cc.Expr{Op: cc.Name, Text: "ARGBEGIN"} 298 | fallthrough 299 | case cc.Switch: 300 | before, _ := extractSideEffects(stmt.Expr, sideNoAfter) 301 | if len(before) > 0 { 302 | old := copyStmt(stmt) 303 | stmt.Expr = nil 304 | stmt.Body = nil 305 | stmt.Else = nil 306 | stmt.Op = BlockNoBrace 307 | stmt.Block = append(before, old) 308 | break // recursion will rewrite new inner switch 309 | } 310 | rewriteSwitch(stmt) 311 | } 312 | } 313 | 314 | func needFixBool(x *cc.Expr) bool { 315 | switch x.Op { 316 | case SideEffectFunc: 317 | if x.Text == "bool" { 318 | return false 319 | } 320 | case cc.EqEq, cc.Not, cc.NotEq, cc.Lt, cc.LtEq, cc.Gt, cc.GtEq, cc.AndAnd, cc.OrOr: 321 | // ok 322 | return false 323 | case cc.Paren: 324 | return needFixBool(x.Left) 325 | } 326 | // assume not ok 327 | return true 328 | } 329 | 330 | func fixBool(x *cc.Expr) { 331 | if !needFixBool(x) { 332 | return 333 | } 334 | old := copyExpr(x) 335 | if old.Op == cc.Paren { 336 | old = old.Left 337 | } 338 | x.Op = cc.NotEq 339 | x.Left = old 340 | cmp := "0" 341 | if old.Op == cc.Name && old.XDecl != nil && old.XDecl.Type != nil && old.XDecl.Type.Kind == cc.Ptr { 342 | cmp = "nil" 343 | } 344 | x.Right = &cc.Expr{Op: cc.Name, Text: cmp} 345 | } 346 | 347 | // fixForAndAnd rewrites for(; x && (y = z) && (v = w) && ...: ) { ... }to 348 | // for (; x; ) { 349 | // if(!(y = z)) 350 | // break 351 | // if(!(v = w)) 352 | // break 353 | // ... 354 | // } 355 | // 356 | // 357 | // The recursion in rewriteStmt will take care of the if. 358 | func fixForAndAnd(stmt *cc.Stmt) { 359 | changed := false 360 | clauses := splitExpr(stmt.Expr, cc.AndAnd) 361 | for i := len(clauses) - 1; i > 0; i-- { 362 | before, _ := extractSideEffects(clauses[i], sideNoAfter) 363 | if len(before) == 0 { 364 | continue 365 | } 366 | changed = true 367 | stmt.Body.Block = append( 368 | []*cc.Stmt{{ 369 | Op: BlockNoBrace, 370 | Block: append(before, &cc.Stmt{ 371 | Op: cc.If, 372 | Expr: &cc.Expr{Op: cc.Not, Left: joinExpr(clauses[i:], cc.AndAnd)}, 373 | Body: &cc.Stmt{Op: cc.Break}, 374 | }), 375 | }}, 376 | stmt.Body.Block...) 377 | clauses = clauses[:i] 378 | } 379 | if changed { 380 | stmt.Expr = joinExpr(clauses, cc.AndAnd) 381 | } 382 | } 383 | 384 | // fixIfAndAnd rewrites if(x && (y = z) ...) ... to if(x) { y = z; if(...) ... } 385 | func fixIfAndAnd(stmt *cc.Stmt) { 386 | changed := false 387 | clauses := splitExpr(stmt.Expr, cc.AndAnd) 388 | for i := len(clauses) - 1; i > 0; i-- { 389 | before, _ := extractSideEffects(clauses[i], sideNoAfter) 390 | if len(before) == 0 { 391 | continue 392 | } 393 | changed = true 394 | stmt.Body = &cc.Stmt{ 395 | Op: BlockNoBrace, 396 | Block: append(before, &cc.Stmt{ 397 | Op: cc.If, 398 | Expr: joinExpr(clauses[i:], cc.AndAnd), 399 | Body: stmt.Body, 400 | }), 401 | } 402 | clauses = clauses[:i] 403 | } 404 | if changed { 405 | stmt.Expr = joinExpr(clauses, cc.AndAnd) 406 | } 407 | } 408 | 409 | func splitExpr(x *cc.Expr, op cc.ExprOp) []*cc.Expr { 410 | if x == nil { 411 | return nil 412 | } 413 | var list []*cc.Expr 414 | for x.Op == op { 415 | list = append(list, x.Right) 416 | x = x.Left 417 | } 418 | list = append(list, x) 419 | for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 { 420 | list[i], list[j] = list[j], list[i] 421 | } 422 | return list 423 | } 424 | 425 | func joinExpr(list []*cc.Expr, op cc.ExprOp) *cc.Expr { 426 | if len(list) == 0 { 427 | return nil 428 | } 429 | x := list[0] 430 | for _, y := range list[1:] { 431 | x = &cc.Expr{Op: op, Left: x, Right: y} 432 | } 433 | return x 434 | } 435 | 436 | func rewriteSwitch(swt *cc.Stmt) { 437 | inlineBlockNoBrace(swt.Body) 438 | var out []*cc.Stmt 439 | haveCase := false 440 | for _, stmt := range swt.Body.Block { 441 | // Put labels after cases, so that they go to the same place. 442 | var names, cases []*cc.Label 443 | var def *cc.Label 444 | for _, lab := range stmt.Labels { 445 | if lab.Op == cc.LabelName { 446 | names = append(names, lab) 447 | } else if lab.Op == cc.Default { 448 | def = lab 449 | } else { 450 | cases = append(cases, lab) 451 | } 452 | } 453 | if def != nil { 454 | cases = append(cases, def) // put default last for printing 455 | } 456 | if len(cases) > 0 && len(names) > 0 { 457 | stmt.Labels = append(cases, names...) 458 | } 459 | if len(cases) > 0 { 460 | // Remove break or add fallthrough if needed. 461 | if haveCase { 462 | i := len(out) - 1 463 | for i >= 0 && out[i].Op == cc.Empty { 464 | i-- 465 | } 466 | if i >= 0 && out[i].Op == cc.Break && len(out[i].Labels) == 0 { 467 | out[i].Op = cc.Empty 468 | } else if i >= 0 && fallsThrough(out[i]) { 469 | out = append(out, &cc.Stmt{Op: Fallthrough}) 470 | } 471 | } 472 | haveCase = true 473 | } 474 | out = append(out, stmt) 475 | } 476 | // Remove final break. 477 | i := len(out) - 1 478 | for i >= 0 && out[i].Op == cc.Empty { 479 | i-- 480 | } 481 | if i >= 0 && out[i].Op == cc.Break && len(out[i].Labels) == 0 { 482 | out[i].Op = cc.Empty 483 | } 484 | 485 | swt.Body.Block = out 486 | } 487 | 488 | func fallsThrough(x *cc.Stmt) bool { 489 | switch x.Op { 490 | case cc.Break, cc.Continue, cc.Return, cc.Goto: 491 | return false 492 | case cc.StmtExpr: 493 | if x.Expr.Op == cc.Call && x.Expr.Left.Op == cc.Name && (x.Expr.Left.Text == "sysfatal" || x.Expr.Left.Text == "fatal") { 494 | return false 495 | } 496 | if x.Expr.Op == cc.Name && x.Expr.Text == "fallthrough" { 497 | return false 498 | } 499 | } 500 | return true 501 | } 502 | 503 | func forceBlock(x *cc.Stmt) *cc.Stmt { 504 | if x.Op != cc.Block { 505 | x = &cc.Stmt{Op: cc.Block, Block: []*cc.Stmt{x}} 506 | } 507 | return x 508 | } 509 | 510 | const ( 511 | sideStmt = 1 << iota 512 | sideNoAfter 513 | ) 514 | 515 | func extractSideEffects(x *cc.Expr, mode int) (before, after []*cc.Stmt) { 516 | doSideEffects(x, &before, &after, mode) 517 | return 518 | } 519 | 520 | var tmpGen = make(chan int) 521 | 522 | func init() { 523 | go func() { 524 | for i := 1; ; i++ { 525 | tmpGen <- i 526 | } 527 | }() 528 | } 529 | 530 | func doSideEffects(x *cc.Expr, before, after *[]*cc.Stmt, mode int) { 531 | if x == nil { 532 | return 533 | } 534 | 535 | // Cannot hoist side effects from conditionally evaluated expressions 536 | // into unconditionally evaluated statement lists. 537 | // For now, detect but do not handle. 538 | switch x.Op { 539 | case cc.Cond: 540 | doSideEffects(x.List[0], before, after, mode&^sideStmt|sideNoAfter) 541 | checkNoSideEffects(x.List[1], 0, "unknown") 542 | checkNoSideEffects(x.List[2], 0, "unknown") 543 | 544 | case cc.AndAnd, cc.OrOr: 545 | doSideEffects(x.Left, before, after, mode&^sideStmt|sideNoAfter) 546 | checkNoSideEffects(x.Right, 0, "bool") 547 | 548 | case cc.Comma: 549 | var leftover []*cc.Expr 550 | for i, y := range x.List { 551 | m := mode | sideNoAfter 552 | if i+1 < len(x.List) { 553 | m |= sideStmt 554 | } 555 | doSideEffects(y, before, after, m) 556 | switch y.Op { 557 | case cc.PostInc, cc.PostDec, cc.Eq, cc.AddEq, cc.SubEq, cc.MulEq, cc.DivEq, cc.ModEq, cc.XorEq, cc.OrEq, cc.AndEq, cc.LshEq, cc.RshEq: 558 | *before = append(*before, &cc.Stmt{Op: cc.StmtExpr, Expr: y}) 559 | default: 560 | leftover = append(leftover, y) 561 | } 562 | } 563 | x.List = leftover 564 | 565 | default: 566 | doSideEffects(x.Left, before, after, mode&^sideStmt) 567 | doSideEffects(x.Right, before, after, mode&^sideStmt) 568 | for _, y := range x.List { 569 | doSideEffects(y, before, after, mode&^sideStmt) 570 | } 571 | } 572 | 573 | if mode&sideStmt != 0 { 574 | // Expression as statement. 575 | // Can leave x++ alone, can rewrite ++x to x++, can leave x [op]= y alone. 576 | switch x.Op { 577 | case cc.PreInc: 578 | x.Op = cc.PostInc 579 | return 580 | case cc.PreDec: 581 | x.Op = cc.PostDec 582 | return 583 | case cc.PostInc, cc.PostDec: 584 | return 585 | case cc.Eq, cc.AddEq, cc.SubEq, cc.MulEq, cc.DivEq, cc.ModEq, cc.XorEq, cc.OrEq, cc.AndEq, cc.LshEq, cc.RshEq: 586 | return 587 | case cc.Call: 588 | return 589 | } 590 | } 591 | 592 | switch x.Op { 593 | case cc.Eq, cc.AddEq, cc.SubEq, cc.MulEq, cc.DivEq, cc.ModEq, cc.XorEq, cc.OrEq, cc.AndEq, cc.LshEq, cc.RshEq: 594 | x.Left = forceCheap(before, x.Left) 595 | old := copyExpr(x) 596 | *before = append(*before, &cc.Stmt{Op: cc.StmtExpr, Expr: old}) 597 | fixMerge(x, x.Left) 598 | 599 | case cc.PreInc, cc.PreDec: 600 | x.Left = forceCheap(before, x.Left) 601 | old := copyExpr(x) 602 | old.SyntaxInfo = cc.SyntaxInfo{} 603 | if old.Op == cc.PreInc { 604 | old.Op = cc.PostInc 605 | } else { 606 | old.Op = cc.PostDec 607 | } 608 | *before = append(*before, &cc.Stmt{Op: cc.StmtExpr, Expr: old}) 609 | fixMerge(x, x.Left) 610 | 611 | case cc.PostInc, cc.PostDec: 612 | x.Left = forceCheap(before, x.Left) 613 | if mode&sideNoAfter != 0 { 614 | // Not allowed to generate fixups afterward. 615 | d := &cc.Decl{ 616 | Name: fmt.Sprintf("tmp%d", <-tmpGen), 617 | Type: x.Left.XType, 618 | } 619 | eq := &cc.Expr{ 620 | Op: ColonEq, 621 | Left: &cc.Expr{Op: cc.Name, Text: d.Name, XDecl: d}, 622 | Right: x.Left, 623 | } 624 | old := copyExpr(x.Left) 625 | old.SyntaxInfo = cc.SyntaxInfo{} 626 | *before = append(*before, 627 | &cc.Stmt{Op: cc.StmtExpr, Expr: eq}, 628 | &cc.Stmt{Op: cc.StmtExpr, Expr: &cc.Expr{Op: x.Op, Left: old}}, 629 | ) 630 | x.Op = cc.Name 631 | x.Text = d.Name 632 | x.XDecl = d 633 | x.Left = nil 634 | break 635 | } 636 | old := copyExpr(x) 637 | old.SyntaxInfo = cc.SyntaxInfo{} 638 | *after = append(*after, &cc.Stmt{Op: cc.StmtExpr, Expr: old}) 639 | fixMerge(x, x.Left) 640 | 641 | case cc.Cond: 642 | // Rewrite c ? y : z into tmp with initialization: 643 | // var tmp typeof(c?y:z) 644 | // if c { 645 | // tmp = y 646 | // } else { 647 | // tmp = z 648 | // } 649 | d := &cc.Decl{ 650 | Name: fmt.Sprintf("tmp%d", <-tmpGen), 651 | Type: x.XType, 652 | } 653 | *before = append(*before, 654 | &cc.Stmt{Op: cc.StmtDecl, Decl: d}, 655 | &cc.Stmt{Op: cc.If, Expr: x.List[0], 656 | Body: &cc.Stmt{ 657 | Op: cc.StmtExpr, 658 | Expr: &cc.Expr{ 659 | Op: cc.Eq, 660 | Left: &cc.Expr{Op: cc.Name, Text: d.Name, XDecl: d}, 661 | Right: x.List[1], 662 | }, 663 | }, 664 | Else: &cc.Stmt{ 665 | Op: cc.StmtExpr, 666 | Expr: &cc.Expr{ 667 | Op: cc.Eq, 668 | Left: &cc.Expr{Op: cc.Name, Text: d.Name, XDecl: d}, 669 | Right: x.List[2], 670 | }, 671 | }, 672 | }, 673 | ) 674 | x.Op = cc.Name 675 | x.Text = d.Name 676 | x.XDecl = d 677 | x.List = nil 678 | } 679 | } 680 | 681 | func copyExpr(x *cc.Expr) *cc.Expr { 682 | old := *x 683 | old.SyntaxInfo = cc.SyntaxInfo{} 684 | return &old 685 | } 686 | 687 | func copyStmt(x *cc.Stmt) *cc.Stmt { 688 | old := *x 689 | old.SyntaxInfo = cc.SyntaxInfo{} 690 | old.Labels = nil 691 | return &old 692 | } 693 | 694 | func forceCheap(before *[]*cc.Stmt, x *cc.Expr) *cc.Expr { 695 | // TODO 696 | return x 697 | } 698 | 699 | func fixMerge(dst, src *cc.Expr) { 700 | syn := dst.SyntaxInfo 701 | syn.Comments.Before = append(syn.Comments.Before, src.Comments.Before...) 702 | syn.Comments.After = append(syn.Comments.After, src.Comments.After...) 703 | syn.Comments.Suffix = append(syn.Comments.Suffix, src.Comments.Suffix...) 704 | *dst = *src 705 | dst.SyntaxInfo = syn 706 | } 707 | 708 | func checkNoSideEffects(x *cc.Expr, mode int, typ string) { 709 | var before, after []*cc.Stmt 710 | doSideEffects(x, &before, &after, mode) 711 | if len(before)+len(after) > 0 { 712 | old := copyExpr(x) 713 | x.Op = SideEffectFunc 714 | x.Left = old 715 | x.Block = before 716 | x.After = after 717 | x.Text = typ 718 | } 719 | } 720 | 721 | // Apply DeMorgan's law and invert comparisons 722 | // to simplify negation of boolean expressions. 723 | func simplifyBool(prog *cc.Prog) { 724 | cc.Postorder(prog, func(x cc.Syntax) { 725 | switch x := x.(type) { 726 | case *cc.Expr: 727 | switch x.Op { 728 | case cc.Not: 729 | y := x.Left 730 | for y.Op == cc.Paren { 731 | y = y.Left 732 | } 733 | switch y.Op { 734 | case cc.AndAnd: 735 | *x = *y 736 | x.Left = &cc.Expr{Op: cc.Not, Left: x.Left} 737 | x.Right = &cc.Expr{Op: cc.Not, Left: x.Right} 738 | x.Op = cc.OrOr 739 | 740 | case cc.OrOr: 741 | *x = *y 742 | x.Left = &cc.Expr{Op: cc.Not, Left: x.Left} 743 | x.Right = &cc.Expr{Op: cc.Not, Left: x.Right} 744 | x.Op = cc.AndAnd 745 | 746 | case cc.EqEq: 747 | if isfloat(x.Left.XType) { 748 | break 749 | } 750 | *x = *y 751 | x.Op = cc.NotEq 752 | 753 | case cc.NotEq: 754 | if isfloat(x.Left.XType) { 755 | break 756 | } 757 | *x = *y 758 | x.Op = cc.EqEq 759 | 760 | case cc.Lt: 761 | if isfloat(x.Left.XType) { 762 | break 763 | } 764 | *x = *y 765 | x.Op = cc.GtEq 766 | 767 | case cc.LtEq: 768 | if isfloat(x.Left.XType) { 769 | break 770 | } 771 | *x = *y 772 | x.Op = cc.Gt 773 | 774 | case cc.Gt: 775 | if isfloat(x.Left.XType) { 776 | break 777 | } 778 | *x = *y 779 | x.Op = cc.LtEq 780 | 781 | case cc.GtEq: 782 | if isfloat(x.Left.XType) { 783 | break 784 | } 785 | *x = *y 786 | x.Op = cc.Lt 787 | } 788 | } 789 | } 790 | }) 791 | } 792 | 793 | func isfloat(t *cc.Type) bool { 794 | return t != nil && (t.Kind == Float32 || t.Kind == Float64) 795 | } 796 | -------------------------------------------------------------------------------- /c2gofmt/testdata/addr.txt: -------------------------------------------------------------------------------- 1 | void f(void) { 2 | XMethod(&x, y); 3 | XMethod(*x, y); 4 | } 5 | --- 6 | package pkg 7 | 8 | func f() { 9 | x.Method(y) 10 | x.Method(y) 11 | } 12 | -------------------------------------------------------------------------------- /c2gofmt/testdata/basic.txt: -------------------------------------------------------------------------------- 1 | int f; 2 | --- 3 | package pkg 4 | 5 | var f int 6 | -------------------------------------------------------------------------------- /c2gofmt/testdata/basic2.txt: -------------------------------------------------------------------------------- 1 | int f; /* comment */ 2 | int g; 3 | 4 | int h; 5 | --- 6 | package pkg 7 | 8 | var f int /* comment */ 9 | var g int 10 | 11 | var h int 12 | -------------------------------------------------------------------------------- /c2gofmt/testdata/basic3.txt: -------------------------------------------------------------------------------- 1 | #pragma 1 2 | #pragma 2 3 | 4 | int f; /* comment */ 5 | --- 6 | // #pragma 1 7 | // #pragma 2 8 | 9 | package pkg 10 | 11 | var f int /* comment */ 12 | -------------------------------------------------------------------------------- /c2gofmt/testdata/bool.txt: -------------------------------------------------------------------------------- 1 | int f(int *p) { 2 | if(x); 3 | while(p); 4 | return !p && x || !!y; 5 | } 6 | --- 7 | package pkg 8 | 9 | func f(p *int) int { 10 | if x != 0 { 11 | } 12 | for p != nil { 13 | } 14 | return p == nil && x != 0 || y != 0 15 | } 16 | -------------------------------------------------------------------------------- /c2gofmt/testdata/comment.txt: -------------------------------------------------------------------------------- 1 | void 2 | f(void) 3 | { /* comment */ 4 | g(); 5 | } 6 | --- 7 | package pkg 8 | 9 | func f() { /* comment */ 10 | g() 11 | } 12 | -------------------------------------------------------------------------------- /c2gofmt/testdata/comment2.txt: -------------------------------------------------------------------------------- 1 | void 2 | f(void) 3 | { 4 | if(x) 5 | g(); 6 | else /* comment */ 7 | h(); 8 | } 9 | --- 10 | package pkg 11 | 12 | func f() { 13 | if x != 0 { 14 | g() 15 | } else { /* comment */ 16 | h() 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /c2gofmt/testdata/comment3.txt: -------------------------------------------------------------------------------- 1 | // comment 2 | --- 3 | // comment 4 | 5 | package pkg 6 | -------------------------------------------------------------------------------- /c2gofmt/testdata/comment4.txt: -------------------------------------------------------------------------------- 1 | #include 2 | --- 3 | package pkg 4 | 5 | // #include 6 | -------------------------------------------------------------------------------- /c2gofmt/testdata/decl.txt: -------------------------------------------------------------------------------- 1 | int f(void) { 2 | int x, y; 3 | 4 | x = 1; 5 | y = 2; 6 | return x + y; 7 | } 8 | 9 | int g(void) { 10 | int x, y; 11 | int z = 3; 12 | 13 | y = 1; 14 | if(y) { 15 | x = f(); 16 | return x; 17 | } else { 18 | x = f() + f(); 19 | return x; 20 | } 21 | return 2 + z; 22 | } 23 | --- 24 | package pkg 25 | 26 | func f() int { 27 | x := 1 28 | y := 2 29 | return x + y 30 | } 31 | 32 | func g() int { 33 | z := 3 34 | 35 | y := 1 36 | if y != 0 { 37 | x := f() 38 | return x 39 | } else { 40 | x := f() + f() 41 | return x 42 | } 43 | return 2 + z 44 | } 45 | -------------------------------------------------------------------------------- /c2gofmt/testdata/decl2.txt: -------------------------------------------------------------------------------- 1 | Rune* 2 | rload(Rasp *r, ulong p0, ulong p1, ulong *nrp) 3 | { 4 | Section *s; 5 | long p; 6 | int n, nb; 7 | 8 | nb = 0; 9 | for(p=0,s=r->sect; s && p+s->nrunes<=p0; s=s->next) 10 | p += s->nrunes; 11 | return 0; 12 | } 13 | --- 14 | package pkg 15 | 16 | func rload(r *Rasp, p0 ulong, p1 ulong, nrp *ulong) *Rune { 17 | nb := 0 18 | p := 0 19 | s := r.sect 20 | for ; s != nil && p+s.nrunes <= p0; s = s.next { 21 | p += s.nrunes 22 | } 23 | return 0 24 | } 25 | -------------------------------------------------------------------------------- /c2gofmt/testdata/decl3.txt: -------------------------------------------------------------------------------- 1 | int f(void) { 2 | int x, y; 3 | 4 | y = 0; 5 | for(x=0; x<10; x++) { 6 | y += x; 7 | } 8 | return y; 9 | } 10 | --- 11 | package pkg 12 | 13 | func f() int { 14 | y := 0 15 | for x := 0; x < 10; x++ { 16 | y += x 17 | } 18 | return y 19 | } 20 | -------------------------------------------------------------------------------- /c2gofmt/testdata/enum.txt: -------------------------------------------------------------------------------- 1 | enum { 2 | X = 1, 3 | Y, 4 | Z, 5 | }; 6 | enum E { 7 | E0, 8 | E1, 9 | E2, 10 | }; 11 | --- 12 | package pkg 13 | 14 | const ( 15 | X = 1 + iota 16 | Y 17 | Z 18 | ) 19 | 20 | type E int 21 | 22 | const ( 23 | E0 E = iota 24 | E1 25 | E2 26 | ) 27 | -------------------------------------------------------------------------------- /c2gofmt/testdata/extern.txt: -------------------------------------------------------------------------------- 1 | extern int a; 2 | 3 | // comment 4 | #define X Y 5 | 6 | // more comment 7 | extern int b; 8 | --- 9 | package pkg 10 | 11 | /* extern var a int */ 12 | 13 | // comment 14 | // #define X Y 15 | 16 | // more comment 17 | /* extern var b int */ 18 | -------------------------------------------------------------------------------- /c2gofmt/testdata/func.txt: -------------------------------------------------------------------------------- 1 | int f(void) { return 1; } 2 | int g(void) { return 2; } 3 | --- 4 | package pkg 5 | 6 | func f() int { 7 | return 1 8 | } 9 | 10 | func g() int { 11 | return 2 12 | } 13 | -------------------------------------------------------------------------------- /c2gofmt/testdata/func2.txt: -------------------------------------------------------------------------------- 1 | void f(void) { 2 | (*g)(); 3 | } 4 | --- 5 | package pkg 6 | 7 | func f() { 8 | g() 9 | } 10 | -------------------------------------------------------------------------------- /c2gofmt/testdata/if.txt: -------------------------------------------------------------------------------- 1 | void f(void) { 2 | int z; 3 | if(x == 0) 4 | g(); 5 | else if(x == 1) 6 | h(); 7 | else if(x == 2) 8 | i(); 9 | else if(x == 3) 10 | j(); 11 | else 12 | z = k(); 13 | } 14 | --- 15 | package pkg 16 | 17 | func f() { 18 | if x == 0 { 19 | g() 20 | } else if x == 1 { 21 | h() 22 | } else if x == 2 { 23 | i() 24 | } else if x == 3 { 25 | j() 26 | } else { 27 | z := k() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /c2gofmt/testdata/init.txt: -------------------------------------------------------------------------------- 1 | int x[] = {1, 2, 3, 4, 5}; 2 | --- 3 | package pkg 4 | 5 | var x = [5]int{1, 2, 3, 4, 5} 6 | -------------------------------------------------------------------------------- /c2gofmt/testdata/init2.txt: -------------------------------------------------------------------------------- 1 | Cursor bullseye={ 2 | {-7, -7}, 3 | {0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF, 4 | 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 5 | 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF, 6 | 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8,}, 7 | {0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84, 8 | 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE, 9 | 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 10 | 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00,} 11 | }; 12 | --- 13 | package pkg 14 | 15 | var bullseye = Cursor{ 16 | {-7, -7}, 17 | { 18 | 0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF, 19 | 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 20 | 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF, 21 | 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8, 22 | }, 23 | { 24 | 0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84, 25 | 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE, 26 | 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 27 | 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00, 28 | }, 29 | } 30 | -------------------------------------------------------------------------------- /c2gofmt/testdata/ptr.txt: -------------------------------------------------------------------------------- 1 | int *f(int *p) { 2 | p = 0; 3 | return p; 4 | } 5 | --- 6 | package pkg 7 | 8 | func f(p *int) *int { 9 | p = nil 10 | return p 11 | } 12 | -------------------------------------------------------------------------------- /c2gofmt/testdata/rewrite.txt: -------------------------------------------------------------------------------- 1 | void f(void) { 2 | int x; 3 | 4 | XMethod(x, 2); 5 | } 6 | --- 7 | package pkg 8 | 9 | func f() { 10 | var x int 11 | x.Method(2) 12 | } 13 | -------------------------------------------------------------------------------- /c2gofmt/testdata/rewrite2.txt: -------------------------------------------------------------------------------- 1 | int f(Rectangle r) { 2 | return r.min.x + r.min.y + r.something; 3 | } 4 | --- 5 | package pkg 6 | 7 | func f(r Rectangle) int { 8 | return r.Min.X + r.Min.Y + r.something 9 | } 10 | -------------------------------------------------------------------------------- /c2gofmt/testdata/side.txt: -------------------------------------------------------------------------------- 1 | void f(void) { 2 | if((c = g()) != 0) { 3 | z(); 4 | } 5 | 6 | if(x && (c = g()) != 0) { 7 | z(); 8 | } 9 | 10 | if(x && (c = g()) != 0) { 11 | z(); 12 | } else { 13 | y(); 14 | } 15 | 16 | while((c = g()) != 0) { 17 | z(); 18 | } 19 | 20 | for(x = 1; x && (c = g()) != 0 && (d = h()) > 0; x++) { 21 | z(); 22 | } 23 | } 24 | --- 25 | package pkg 26 | 27 | func f() { 28 | c = g() 29 | if c != 0 { 30 | z() 31 | } 32 | 33 | if x != 0 { 34 | c = g() 35 | if c != 0 { 36 | z() 37 | } 38 | } 39 | 40 | if x != 0 && func() bool { c = g(); return c != 0 }() { 41 | z() 42 | } else { 43 | y() 44 | } 45 | 46 | for { 47 | c = g() 48 | if c == 0 { 49 | break 50 | } 51 | z() 52 | } 53 | 54 | for x = 1; x != 0; x++ { 55 | c = g() 56 | if c == 0 { 57 | break 58 | } 59 | d = h() 60 | if d <= 0 { 61 | break 62 | } 63 | z() 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /c2gofmt/testdata/side2.txt: -------------------------------------------------------------------------------- 1 | void f(void) { 2 | c = *p++; 3 | int d = *p++; 4 | } 5 | --- 6 | package pkg 7 | 8 | func f() { 9 | c = *p 10 | p++ 11 | d := *p 12 | p++ 13 | } 14 | -------------------------------------------------------------------------------- /c2gofmt/testdata/slice.txt: -------------------------------------------------------------------------------- 1 | int f(char **p) { 2 | return p[1][2]; 3 | } 4 | --- 5 | package pkg 6 | 7 | func f(p [][]C.char) int { 8 | return p[1][2] 9 | } 10 | -------------------------------------------------------------------------------- /c2gofmt/testdata/static.txt: -------------------------------------------------------------------------------- 1 | int 2 | f(void) 3 | { 4 | static int x; 5 | x++; 6 | return x; 7 | } 8 | --- 9 | package pkg 10 | 11 | var f_x int 12 | 13 | func f() int { 14 | f_x++ 15 | return f_x 16 | } 17 | -------------------------------------------------------------------------------- /c2gofmt/testdata/stmt.txt: -------------------------------------------------------------------------------- 1 | void 2 | f(void) 3 | { 4 | /* c1 */ 5 | 6 | /* c2 */ 7 | int i; /* c3 */ 8 | /* c4 */ 9 | 10 | /* c5 */ 11 | g(i); /* c6 */ 12 | /* c7 */ 13 | 14 | /* c8 */ 15 | g(i); /* c9 */ 16 | /* c10 */ 17 | 18 | /* c11 */ 19 | } 20 | 21 | void 22 | ff(void) 23 | { 24 | g(1); 25 | } 26 | --- 27 | package pkg 28 | 29 | func f() { 30 | /* c1 */ 31 | 32 | /* c2 */ 33 | var i int /* c3 */ 34 | /* c4 */ 35 | 36 | /* c5 */ 37 | g(i) /* c6 */ 38 | /* c7 */ 39 | 40 | /* c8 */ 41 | g(i) /* c9 */ 42 | /* c10 */ 43 | 44 | /* c11 */ 45 | } 46 | 47 | func ff() { 48 | g(1) 49 | } 50 | -------------------------------------------------------------------------------- /c2gofmt/testdata/struct.txt: -------------------------------------------------------------------------------- 1 | typedef struct S S; 2 | typedef struct T T; 3 | 4 | struct S { 5 | int x; 6 | }; 7 | 8 | int z; 9 | 10 | struct T { 11 | int y; 12 | }; 13 | --- 14 | package pkg 15 | 16 | type S struct { 17 | x int 18 | } 19 | 20 | var z int 21 | 22 | type T struct { 23 | y int 24 | } 25 | -------------------------------------------------------------------------------- /c2gofmt/testdata/struct2.txt: -------------------------------------------------------------------------------- 1 | typedef struct S S; 2 | typedef struct T T; 3 | 4 | struct S { 5 | int x; 6 | }; 7 | 8 | #define Z 1 9 | 10 | struct T { 11 | int y; 12 | }; 13 | --- 14 | package pkg 15 | 16 | type S struct { 17 | x int 18 | } 19 | 20 | // #define Z 1 21 | 22 | type T struct { 23 | y int 24 | } 25 | -------------------------------------------------------------------------------- /c2gofmt/testdata/switch.txt: -------------------------------------------------------------------------------- 1 | void 2 | f(void) 3 | { 4 | switch(1){ 5 | case 1: 6 | f(); 7 | break; 8 | 9 | case 2: 10 | g(); 11 | 12 | case 3: 13 | h(); 14 | } 15 | } 16 | --- 17 | package pkg 18 | 19 | func f() { 20 | switch 1 { 21 | case 1: 22 | f() 23 | 24 | case 2: 25 | g() 26 | fallthrough 27 | 28 | case 3: 29 | h() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /c2gofmt/testdata/switch2.txt: -------------------------------------------------------------------------------- 1 | void 2 | f(void) 3 | { 4 | switch(1){ 5 | case 1: 6 | f(); 7 | break; 8 | 9 | case 2: 10 | g(); 11 | 12 | case 3: 13 | h(); 14 | break; 15 | } 16 | } 17 | --- 18 | package pkg 19 | 20 | func f() { 21 | switch 1 { 22 | case 1: 23 | f() 24 | 25 | case 2: 26 | g() 27 | fallthrough 28 | 29 | case 3: 30 | h() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /c2gofmt/testdata/switch3.txt: -------------------------------------------------------------------------------- 1 | void 2 | f(void) 3 | { 4 | switch(1){ 5 | case 1: 6 | if((c = f()) != 0) 7 | g(); 8 | break; 9 | 10 | case 2: 11 | if((c = g()) != 0) 12 | h(); 13 | 14 | case 3: 15 | if((c = h()) != 0) 16 | i(); 17 | break; 18 | } 19 | } 20 | --- 21 | package pkg 22 | 23 | func f() { 24 | switch 1 { 25 | case 1: 26 | c = f() 27 | if c != 0 { 28 | g() 29 | } 30 | 31 | case 2: 32 | c = g() 33 | if c != 0 { 34 | h() 35 | } 36 | fallthrough 37 | 38 | case 3: 39 | c = h() 40 | if c != 0 { 41 | i() 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /git-foreach/go.mod: -------------------------------------------------------------------------------- 1 | module rsc.io/cmd/git-foreach 2 | 3 | go 1.16 4 | 5 | require golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 6 | -------------------------------------------------------------------------------- /git-foreach/go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= 2 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 3 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM= 4 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 5 | -------------------------------------------------------------------------------- /git-foreach/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Git-foreach runs a command at every Git commit in a sequence. 6 | // 7 | // This command display a line for each commit being tested, along 8 | // with a timer to show progress. When the command finishes, it shows 9 | // whether or not it exited successfully. If the command failed, it 10 | // leaves the command's output in a file named "log.". 11 | package main 12 | 13 | import ( 14 | "bytes" 15 | "flag" 16 | "fmt" 17 | "os" 18 | "os/exec" 19 | "os/signal" 20 | "strings" 21 | "sync" 22 | "syscall" 23 | "time" 24 | 25 | "golang.org/x/term" 26 | ) 27 | 28 | // stop is used to coordinate cleaning stopping on a signal. 29 | var stop struct { 30 | sync.Mutex 31 | sig os.Signal 32 | proc *os.Process 33 | } 34 | 35 | // origHEAD is the original value of the HEAD ref. 36 | var origHEAD string 37 | 38 | // pretty indicates the terminal supports vt100 control codes. 39 | var pretty bool 40 | 41 | func main() { 42 | flag.Usage = func() { 43 | fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s rev-list cmd...\n", os.Args[0]) 44 | flag.PrintDefaults() 45 | } 46 | flag.Parse() 47 | if flag.NArg() < 2 { 48 | flag.Usage() 49 | os.Exit(2) 50 | } 51 | revList := flag.Arg(0) 52 | cmd := flag.Args()[1:] 53 | 54 | // Verify clean working tree. 55 | if out, ok := tryGit("diff-index", "--quiet", "HEAD", "--"); !ok { 56 | if len(out) > 0 { 57 | die("%s", out) 58 | } 59 | die("working tree is not clean") 60 | } 61 | 62 | // Save current HEAD so we can restore it. 63 | if sym, ok := tryGit("symbolic-ref", "-q", "--short", "HEAD"); ok { 64 | origHEAD = sym 65 | } else if sha, ok := tryGit("rev-parse", "--verify", "HEAD"); ok { 66 | origHEAD = sha 67 | } else { 68 | die("bad HEAD") 69 | } 70 | 71 | // Catch signals to exit loop. 72 | sigChan := make(chan os.Signal, 1) 73 | signal.Notify(sigChan, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM) 74 | go func() { 75 | sig := <-sigChan 76 | stop.Lock() 77 | stop.sig = sig 78 | if stop.proc != nil { 79 | //stop.proc.Kill() 80 | // Kill the process group. 81 | syscall.Kill(-stop.proc.Pid, sig.(syscall.Signal)) 82 | } 83 | signal.Stop(sigChan) 84 | stop.Unlock() 85 | }() 86 | 87 | pretty = !(os.Getenv("TERM") == "" || os.Getenv("TERM") == "dumb") && term.IsTerminal(syscall.Stdout) 88 | 89 | // Iterate over revisions. 90 | exitStatus := 0 91 | for _, rev := range strings.Fields(git("rev-list", "--reverse", revList)) { 92 | msg := git("rev-list", "-n", "1", "--oneline", rev) 93 | msg = strings.TrimSpace(msg) 94 | fmt.Print(msg) 95 | 96 | // Ensure mtimes are updated by checkout. 97 | for start := time.Now().Unix(); start == time.Now().Unix(); { 98 | time.Sleep(100 * time.Millisecond) 99 | } 100 | 101 | // Check out rev. 102 | git("checkout", "-q", rev, "--") 103 | 104 | stopTimer := startTimer() 105 | stopped, err := run1(cmd) 106 | stopTimer() 107 | if !pretty { 108 | fmt.Println() 109 | } 110 | if stopped { 111 | exitStatus = 1 112 | break 113 | } 114 | if err == nil { 115 | if pretty { 116 | // Bold green 117 | printEOL("PASS", "1;32") 118 | } 119 | } else { 120 | // Bold red 121 | printEOL("FAIL", "1;31") 122 | exitStatus = 1 123 | } 124 | fmt.Println() 125 | } 126 | 127 | // Clean up 128 | cleanup() 129 | 130 | os.Exit(exitStatus) 131 | } 132 | 133 | func run1(cmd []string) (stopped bool, err error) { 134 | // Check if we should stop. 135 | stop.Lock() 136 | if stop.sig != nil { 137 | stop.Unlock() 138 | return true, nil 139 | } 140 | 141 | c := exec.Command(cmd[0], cmd[1:]...) 142 | 143 | // Open log file for this revision. 144 | logName := "log." + git("rev-parse", "--short", "HEAD") 145 | logFile, err := os.Create(logName) 146 | if err != nil { 147 | stop.Unlock() 148 | die("%s", err) 149 | } 150 | c.Stdout = logFile 151 | c.Stderr = logFile 152 | // Don't leak FDs from git into the subprocess (notably, the 153 | // git trace FD) 154 | c.ExtraFiles = make([]*os.File, 100) 155 | 156 | // Start a new process group so we can signal the whole group. 157 | c.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} 158 | 159 | // Start command. 160 | err = c.Start() 161 | logFile.Close() 162 | if err == nil { 163 | stop.proc = c.Process 164 | } 165 | stop.Unlock() 166 | if err != nil { 167 | die("failed to start command: %s", err) 168 | } 169 | 170 | // Wait 171 | err = c.Wait() 172 | 173 | if err == nil { 174 | os.Remove(logName) 175 | } 176 | 177 | // Check again for stop and clear process. 178 | stop.Lock() 179 | stop.proc = nil 180 | if stop.sig != nil { 181 | stop.Unlock() 182 | return true, nil 183 | } 184 | stop.Unlock() 185 | return false, err 186 | } 187 | 188 | func cleanup() { 189 | git("checkout", "-q", origHEAD) 190 | } 191 | 192 | var dying bool 193 | 194 | func die(f string, args ...interface{}) { 195 | if dying { 196 | os.Exit(1) 197 | } 198 | dying = true 199 | msg := fmt.Sprintf(f, args...) 200 | if !strings.HasSuffix(msg, "\n") { 201 | msg += "\n" 202 | } 203 | os.Stderr.WriteString(msg) 204 | cleanup() 205 | os.Exit(1) 206 | } 207 | 208 | func tryGit(args ...string) (string, bool) { 209 | cmd := exec.Command("git", args...) 210 | out, err := cmd.CombinedOutput() 211 | if bytes.HasSuffix(out, []byte("\n")) { 212 | out = out[:len(out)-1] 213 | } 214 | return string(out), err == nil 215 | } 216 | 217 | func git(args ...string) string { 218 | out, ok := tryGit(args...) 219 | if !ok { 220 | die("git %s failed\n%s", strings.Join(args, " "), out) 221 | } 222 | return out 223 | } 224 | 225 | func startTimer() func() { 226 | if !pretty { 227 | return func() {} 228 | } 229 | stopTimerC := make(chan struct{}) 230 | var wg sync.WaitGroup 231 | wg.Add(1) 232 | go func() { 233 | var now string 234 | start := time.Now() 235 | for delta := time.Duration(0); ; delta += time.Second { 236 | // Delay until delta. 237 | select { 238 | case <-time.After(start.Add(delta).Sub(time.Now())): 239 | case <-stopTimerC: 240 | // Clear the timer text. 241 | printEOL(strings.Repeat(" ", len(now)), "") 242 | wg.Done() 243 | return 244 | } 245 | // Print the time. 246 | now = fmt.Sprintf("%d:%02d", delta/time.Minute, (delta/time.Second)%60) 247 | printEOL(now, "") 248 | } 249 | }() 250 | return func() { 251 | close(stopTimerC) 252 | wg.Wait() 253 | } 254 | } 255 | 256 | func printEOL(text string, attrs string) { 257 | if !pretty { 258 | fmt.Print(text) 259 | return 260 | } 261 | 262 | var buf bytes.Buffer 263 | if attrs != "" { 264 | fmt.Fprintf(&buf, "\x1b[%sm", attrs) 265 | } 266 | // Move to the end of the line, then back up 267 | // and print text. 268 | fmt.Fprintf(&buf, "\x1b[999C\x1b[%dD%s", len(text), text) 269 | if attrs != "" { 270 | fmt.Fprintf(&buf, "\x1b[0m") 271 | } 272 | 273 | os.Stdout.Write(buf.Bytes()) 274 | } -------------------------------------------------------------------------------- /gofixerr/edit.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "sort" 10 | ) 11 | 12 | // A Buffer is a queue of edits to apply to a given byte slice. 13 | type Buffer struct { 14 | old []byte 15 | q edits 16 | } 17 | 18 | // An edit records a single text modification: change the bytes in [start,end) to new. 19 | type edit struct { 20 | start int 21 | end int 22 | new string 23 | } 24 | 25 | // An edits is a list of edits that is sortable by start offset, breaking ties by end offset. 26 | type edits []edit 27 | 28 | func (x edits) Len() int { return len(x) } 29 | func (x edits) Swap(i, j int) { x[i], x[j] = x[j], x[i] } 30 | func (x edits) Less(i, j int) bool { 31 | if x[i].start != x[j].start { 32 | return x[i].start < x[j].start 33 | } 34 | return x[i].end < x[j].end 35 | } 36 | 37 | // NewBuffer returns a new buffer to accumulate changes to an initial data slice. 38 | // The returned buffer maintains a reference to the data, so the caller must ensure 39 | // the data is not modified until after the Buffer is done being used. 40 | func NewBuffer(data []byte) *Buffer { 41 | return &Buffer{old: data} 42 | } 43 | 44 | func (b *Buffer) Insert(pos int, new string) { 45 | if pos < 0 || pos > len(b.old) { 46 | panic("invalid edit position") 47 | } 48 | b.q = append(b.q, edit{pos, pos, new}) 49 | } 50 | 51 | func (b *Buffer) Delete(start, end int) { 52 | if end < start || start < 0 || end > len(b.old) { 53 | panic("invalid edit position") 54 | } 55 | b.q = append(b.q, edit{start, end, ""}) 56 | } 57 | 58 | func (b *Buffer) Replace(start, end int, new string) { 59 | if end < start || start < 0 || end > len(b.old) { 60 | panic("invalid edit position") 61 | } 62 | b.q = append(b.q, edit{start, end, new}) 63 | } 64 | 65 | // Bytes returns a new byte slice containing the original data 66 | // with the queued edits applied. 67 | func (b *Buffer) Bytes() []byte { 68 | // Sort edits by starting position and then by ending position. 69 | // Breaking ties by ending position allows insertions at point x 70 | // to be applied before a replacement of the text at [x, y). 71 | sort.Stable(b.q) 72 | 73 | var new []byte 74 | offset := 0 75 | for i, e := range b.q { 76 | if e.start < offset { 77 | e0 := b.q[i-1] 78 | panic(fmt.Sprintf("overlapping edits: [%d,%d)->%q, [%d,%d)->%q", e0.start, e0.end, e0.new, e.start, e.end, e.new)) 79 | } 80 | new = append(new, b.old[offset:e.start]...) 81 | offset = e.end 82 | new = append(new, e.new...) 83 | } 84 | new = append(new, b.old[offset:]...) 85 | return new 86 | } 87 | 88 | // String returns a string containing the original data 89 | // with the queued edits applied. 90 | func (b *Buffer) String() string { 91 | return string(b.Bytes()) 92 | } 93 | -------------------------------------------------------------------------------- /gofixerr/go.mod: -------------------------------------------------------------------------------- 1 | module rsc.io/cmd/gofixerr 2 | 3 | go 1.16 4 | 5 | require rsc.io/rf v0.0.0-20210102024717-be19e66fb52d // indirect 6 | -------------------------------------------------------------------------------- /gofixerr/go.sum: -------------------------------------------------------------------------------- 1 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 2 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 3 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 4 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 5 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 6 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 7 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 8 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 9 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 10 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 11 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 12 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 13 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 14 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 15 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 16 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 17 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 18 | golang.org/x/tools v0.0.0-20201111224557-41a3a589386c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 19 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 20 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 21 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 22 | rsc.io/rf v0.0.0-20201229043002-4f177a6cd303 h1:h69TGIgcmTBl5Q8hAUR+G5P9GSCPX3Y7gp8cwiUI0So= 23 | rsc.io/rf v0.0.0-20201229043002-4f177a6cd303/go.mod h1:STN8PzM7ZPqz/xfNAVChFQHn4fG+RwhjiQCPOOqFNY4= 24 | rsc.io/rf v0.0.0-20210102024717-be19e66fb52d h1:VYQKqn7C7bOYhzosGSAR/2IdpRHkRgBh0VT/rxpqXN4= 25 | rsc.io/rf v0.0.0-20210102024717-be19e66fb52d/go.mod h1:4rdFt/SlKutY8W9onF7XZvD3H0hD+Xpz/uodEcQLuM4= 26 | -------------------------------------------------------------------------------- /gofixerr/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Gofixerr fixes errors in Go programs. 6 | // 7 | // Usage: 8 | // 9 | // gofixerr [-v] [-w] [file.go ... | package ...] 10 | // 11 | // Gofixerr attempts to build the package or packages named on the 12 | // command line and then prints suggested changes to fix any recognized 13 | // compiler errors. 14 | // 15 | // The -v flag prints extra output. 16 | // 17 | // The -w flag causes gofixerr to write the changes to the files. 18 | // 19 | // This is an experiment and subject to change. 20 | package main 21 | 22 | import ( 23 | "bytes" 24 | "flag" 25 | "fmt" 26 | "io/ioutil" 27 | "log" 28 | "os" 29 | "os/exec" 30 | "regexp" 31 | "sort" 32 | "strconv" 33 | "strings" 34 | 35 | "rsc.io/rf/diff" 36 | ) 37 | 38 | var ( 39 | writeFiles = flag.Bool("w", false, "write changes instead of printing diffs") 40 | verbose = flag.Bool("v", false, "print verbose output") 41 | ) 42 | 43 | func usage() { 44 | fmt.Fprintf(os.Stderr, "usage: gofixerr [-v] [-w] [file.go ...]\n") 45 | os.Exit(2) 46 | } 47 | 48 | var ( 49 | fieldRE = regexp.MustCompile(`has no field or method ([^ ]+), but does have ([^() ]+)\)`) 50 | boolCmpRE = regexp.MustCompile(`cannot use [01] \(type (untyped )?int\) as type bool|[!=]= 0 \(mismatched types untyped bool and untyped int\)`) 51 | cmpZeroToNilRE = regexp.MustCompile(`invalid operation: .*[!=] 0 \(mismatched types (\*|func|\[\]).* and int\)`) 52 | useZeroToNilRE = regexp.MustCompile(`cannot use 0 \(type int\) as type (\*|func|\[\]).*`) 53 | ) 54 | 55 | func main() { 56 | log.SetPrefix("gofixerr: ") 57 | log.SetFlags(0) 58 | flag.Usage = usage 59 | flag.Parse() 60 | 61 | cmd := exec.Command("go", append([]string{"build", "-gcflags=-e"}, flag.Args()...)...) 62 | out, err := cmd.CombinedOutput() 63 | if err == nil { 64 | log.Fatal("compile succeeded") 65 | } 66 | 67 | for _, line := range strings.Split(string(out), "\n") { 68 | i := strings.Index(line, ": ") 69 | if i < 0 || strings.HasPrefix(line, "#") { 70 | continue 71 | } 72 | file, msg := line[:i], line[i+2:] 73 | if m := fieldRE.FindStringSubmatch(msg); m != nil { 74 | b, pos := getbuf(file) 75 | if pos >= len(b.old) && b.old[pos] != '.' || !bytes.HasPrefix(b.old[pos+1:], []byte(m[1])) { 76 | log.Printf("%s: out of sync: expected %s", file, m[1]) 77 | continue 78 | } 79 | b.Replace(pos+1, pos+1+len(m[1]), m[2]) 80 | continue 81 | } 82 | if boolCmpRE.MatchString(msg) { 83 | b, pos := getbuf(file) 84 | switch { 85 | default: 86 | log.Printf("%s: out of sync: expected '!= 0'", file) 87 | 88 | case bytes.HasPrefix(b.old[pos:], []byte("= 0")): 89 | b.Replace(pos+2, pos+3, "false") 90 | case bytes.HasPrefix(b.old[pos:], []byte("= 1")): 91 | b.Replace(pos+2, pos+3, "true") 92 | case bytes.HasPrefix(b.old[pos:], []byte("0")): 93 | b.Replace(pos, pos+1, "false") 94 | case bytes.HasPrefix(b.old[pos:], []byte("1")): 95 | b.Replace(pos, pos+1, "true") 96 | case bytes.HasPrefix(b.old[pos:], []byte("!= 0")): 97 | b.Delete(pos, pos+len("!= 0")) 98 | 99 | case bytes.HasPrefix(b.old[pos:], []byte("== 0")): 100 | b.Replace(pos+3, pos+4, "false") 101 | } 102 | continue 103 | } 104 | if cmpZeroToNilRE.MatchString(msg) { 105 | b, pos := getbuf(file) 106 | switch { 107 | default: 108 | log.Printf("%s: out of sync: expected '!= 0'", file) 109 | 110 | case bytes.HasPrefix(b.old[pos:], []byte("!= 0")): 111 | b.Replace(pos+3, pos+4, "nil") 112 | 113 | case bytes.HasPrefix(b.old[pos:], []byte("== 0")): 114 | b.Replace(pos+3, pos+4, "nil") 115 | } 116 | continue 117 | } 118 | if useZeroToNilRE.MatchString(msg) { 119 | b, pos := getbuf(file) 120 | switch { 121 | default: 122 | if i := bytes.Index(b.old[pos:], []byte(" = 0")); 0 <= i && i < 100 && !bytes.Contains(b.old[pos:pos+i], []byte("\n")) { 123 | // Sometimes positioned at start of declaration. 124 | b.Replace(pos+i+3, pos+i+4, "nil") 125 | break 126 | } 127 | log.Printf("%s: out of sync: expected 0", file) 128 | 129 | case bytes.HasPrefix(b.old[pos:], []byte("= 0")): 130 | b.Replace(pos+2, pos+3, "nil") 131 | 132 | case bytes.HasPrefix(b.old[pos:], []byte("0")): 133 | b.Replace(pos, pos+1, "nil") 134 | } 135 | } 136 | } 137 | 138 | if len(bufs) == 0 { 139 | log.Fatalf("no fixes") 140 | } 141 | 142 | var files []string 143 | for file := range bufs { 144 | files = append(files, file) 145 | } 146 | sort.Strings(files) 147 | 148 | if !*writeFiles { 149 | for _, file := range files { 150 | b := bufs[file] 151 | out := b.Bytes() 152 | d, err := diff.Diff(file, b.old, file, out) 153 | if err != nil { 154 | log.Printf("diff %s: %v", file, err) 155 | continue 156 | } 157 | os.Stdout.Write(d) 158 | } 159 | return 160 | } 161 | 162 | for _, file := range files { 163 | b := bufs[file] 164 | out := b.Bytes() 165 | if err := ioutil.WriteFile(file, out, 0666); err != nil { 166 | log.Print(err) 167 | } 168 | } 169 | } 170 | 171 | var bufs = make(map[string]*Buffer) 172 | 173 | func getbuf(addr string) (*Buffer, int) { 174 | i := strings.Index(addr, ":") 175 | if i < 0 { 176 | log.Fatalf("bad file address: %s", addr) 177 | } 178 | file, lineCol := addr[:i], addr[i+1:] 179 | b := bufs[file] 180 | if b == nil { 181 | data, err := ioutil.ReadFile(file) 182 | if err != nil { 183 | log.Fatal(err) 184 | } 185 | b = NewBuffer(data) 186 | bufs[file] = b 187 | } 188 | 189 | i = strings.Index(lineCol, ":") 190 | if i < 0 { 191 | log.Fatalf("bad file address: %s", addr) 192 | } 193 | lineStr, colStr := lineCol[:i], lineCol[i+1:] 194 | line, err := strconv.Atoi(lineStr) 195 | if err != nil { 196 | log.Fatalf("bad file address: %s", addr) 197 | } 198 | col, err := strconv.Atoi(colStr) 199 | if err != nil { 200 | log.Fatalf("bad file address: %s", addr) 201 | } 202 | 203 | pos := 0 204 | for ; pos < len(b.old) && line > 1; pos++ { 205 | if b.old[pos] == '\n' { 206 | line-- 207 | } 208 | } 209 | pos += col - 1 210 | if pos > len(b.old) { 211 | pos = len(b.old) 212 | } 213 | return b, pos 214 | } 215 | -------------------------------------------------------------------------------- /mdweb/go.mod: -------------------------------------------------------------------------------- 1 | module rsc.io/cmd/mdweb 2 | 3 | go 1.21.9 4 | 5 | require rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef 6 | 7 | require golang.org/x/text v0.3.7 // indirect 8 | -------------------------------------------------------------------------------- /mdweb/go.sum: -------------------------------------------------------------------------------- 1 | github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68= 2 | github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 3 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 4 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 5 | golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA= 6 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 7 | rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef h1:mqLYrXCXYEZOop9/Dbo6RPX11539nwiCNBb1icVPmw8= 8 | rsc.io/markdown v0.0.0-20240306144322-0bf8f97ee8ef/go.mod h1:8xcPgWmwlZONN1D9bjxtHEjrUtSEa3fakVF8iaewYKQ= 9 | -------------------------------------------------------------------------------- /mdweb/main.go: -------------------------------------------------------------------------------- 1 | // Mdweb serves rendered Markdown from the current directory on localhost:8780. 2 | // 3 | // Usage: 4 | // 5 | // mdweb [-a addr] [-r root] 6 | // 7 | // The -a flag sets a different service address (default localhost:8780). 8 | // 9 | // The -r flag sets a different root directory to serve (default current directory). 10 | package main 11 | 12 | import ( 13 | "flag" 14 | "fmt" 15 | "io" 16 | "log" 17 | "net/http" 18 | "os" 19 | "strings" 20 | "time" 21 | 22 | "rsc.io/markdown" 23 | ) 24 | 25 | var ( 26 | addr = flag.String("a", "localhost:8780", "serve HTTP requests on `addr`") 27 | root = flag.String("r", ".", "set `root` directory for serving content") 28 | 29 | dir http.FileSystem 30 | fs http.Handler 31 | ) 32 | 33 | func usage() { 34 | fmt.Fprintf(os.Stderr, "usage: mdweb [-a addr] [-r root]\n") 35 | flag.PrintDefaults() 36 | os.Exit(2) 37 | } 38 | 39 | func main() { 40 | log.SetPrefix("mdweb: ") 41 | log.SetFlags(0) 42 | 43 | flag.Usage = usage 44 | flag.Parse() 45 | if flag.NArg() != 0 { 46 | usage() 47 | } 48 | 49 | dir = http.Dir(*root) 50 | fs = http.FileServer(dir) 51 | http.HandleFunc("/", md) 52 | fmt.Fprintf(os.Stderr, "mdweb: serving %s on http://%s\n", *root, *addr) 53 | log.Fatal(http.ListenAndServe(*addr, nil)) 54 | } 55 | 56 | func md(w http.ResponseWriter, req *http.Request) { 57 | if req.Method != "GET" { 58 | http.Error(w, "bad method", http.StatusMethodNotAllowed) 59 | return 60 | } 61 | 62 | if !strings.HasSuffix(req.URL.Path, ".md") { 63 | fs.ServeHTTP(w, req) 64 | return 65 | } 66 | 67 | f, err := dir.Open(req.URL.Path) 68 | if err != nil { 69 | http.Error(w, "file not found", http.StatusNotFound) 70 | return 71 | } 72 | info, err := f.Stat() 73 | if err != nil { 74 | f.Close() 75 | http.Error(w, "file not found", http.StatusNotFound) 76 | return 77 | } 78 | if checkLastModified(w, req, info.ModTime()) { 79 | f.Close() 80 | return 81 | } 82 | 83 | data, err := io.ReadAll(f) 84 | f.Close() 85 | if err != nil { 86 | http.Error(w, "error reading data", http.StatusInternalServerError) 87 | return 88 | } 89 | 90 | 91 | p := &markdown.Parser{ 92 | HeadingIDs: true, 93 | Strikethrough: true, 94 | TaskListItems: true, 95 | AutoLinkText: true, 96 | Table: true, 97 | Emoji: true, 98 | SmartDot: true, 99 | SmartDash: true, 100 | SmartQuote: true, 101 | } 102 | doc := p.Parse(string(data)) 103 | html := markdown.ToHTML(doc) 104 | w.Write([]byte(``)) 105 | w.Write([]byte(html)) 106 | } 107 | 108 | // copied from net/http 109 | 110 | var unixEpochTime = time.Unix(0, 0) 111 | 112 | // modtime is the modification time of the resource to be served, or IsZero(). 113 | // return value is whether this request is now complete. 114 | func checkLastModified(w http.ResponseWriter, r *http.Request, modtime time.Time) bool { 115 | if modtime.IsZero() || modtime.Equal(unixEpochTime) { 116 | // If the file doesn't have a modtime (IsZero), or the modtime 117 | // is obviously garbage (Unix time == 0), then ignore modtimes 118 | // and don't process the If-Modified-Since header. 119 | return false 120 | } 121 | 122 | // The Date-Modified header truncates sub-second precision, so 123 | // use mtime < t+1s instead of mtime <= t to check for unmodified. 124 | if t, err := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && modtime.Before(t.Add(1*time.Second)) { 125 | h := w.Header() 126 | delete(h, "Content-Type") 127 | delete(h, "Content-Length") 128 | w.WriteHeader(http.StatusNotModified) 129 | return true 130 | } 131 | w.Header().Set("Last-Modified", modtime.UTC().Format(http.TimeFormat)) 132 | return false 133 | } 134 | -------------------------------------------------------------------------------- /rdate/go.mod: -------------------------------------------------------------------------------- 1 | module rsc.io/cmd/rdate 2 | 3 | go 1.21.0 4 | 5 | -------------------------------------------------------------------------------- /rdate/rdate.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Rdate prints the current time in RFC3339 format. 6 | package main 7 | 8 | import ( 9 | "fmt" 10 | "time" 11 | ) 12 | 13 | func main() { 14 | fmt.Printf("%s\n", time.Now().Format(time.RFC3339)) 15 | } 16 | -------------------------------------------------------------------------------- /sleep/go.mod: -------------------------------------------------------------------------------- 1 | module rsc.io/cmd/sleep 2 | 3 | go 1.21.0 4 | -------------------------------------------------------------------------------- /sleep/sleep.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Sleep sleeps for a specified duration and then wakes up and exits. 6 | // It is backwards-compatible with the standard Unix sleep(1) command 7 | // but accepts additional duration syntaxes. 8 | // 9 | // Usage: 10 | // 11 | // sleep 12 | // sleep