1
1
package tygo
2
2
3
3
import (
4
+ "encoding/json"
4
5
"fmt"
5
6
"go/ast"
6
7
"go/token"
7
8
"regexp"
9
+ "strconv"
8
10
"strings"
9
11
10
12
"github.com/fatih/structtag"
11
13
)
12
14
13
15
var validJSNameRegexp = regexp .MustCompile (`(?m)^[\pL_][\pL\pN_]*$` )
14
16
var backquoteEscapeRegexp = regexp .MustCompile (`([$\\])` )
17
+ var octalPrefixRegexp = regexp .MustCompile (`^0[0-7]` )
18
+ var unicode8Regexp = regexp .MustCompile (`\\\\|\\U[\da-fA-F]{8}` )
19
+
20
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_precedence#table
21
+ var jsNumberOperatorPrecedence = map [token.Token ]int {
22
+ token .MUL : 6 ,
23
+ token .QUO : 6 ,
24
+ token .REM : 6 ,
25
+ token .ADD : 5 ,
26
+ token .SUB : 5 ,
27
+ token .SHL : 4 ,
28
+ token .SHR : 4 ,
29
+ token .AND : 3 ,
30
+ token .AND_NOT : 3 ,
31
+ token .OR : 2 ,
32
+ token .XOR : 1 ,
33
+ }
15
34
16
35
func validJSName (n string ) bool {
17
36
return validJSNameRegexp .MatchString (n )
@@ -39,15 +58,17 @@ func (g *PackageGenerator) writeIndent(s *strings.Builder, depth int) {
39
58
func (g * PackageGenerator ) writeType (
40
59
s * strings.Builder ,
41
60
t ast.Expr ,
61
+ p ast.Expr ,
42
62
depth int ,
43
63
optionalParens bool ,
44
64
) {
65
+ // log.Println("writeType:", reflect.TypeOf(t), t)
45
66
switch t := t .(type ) {
46
67
case * ast.StarExpr :
47
68
if optionalParens {
48
69
s .WriteByte ('(' )
49
70
}
50
- g .writeType (s , t .X , depth , false )
71
+ g .writeType (s , t .X , t , depth , false )
51
72
s .WriteString (" | undefined" )
52
73
if optionalParens {
53
74
s .WriteByte (')' )
@@ -57,7 +78,7 @@ func (g *PackageGenerator) writeType(
57
78
s .WriteString ("string" )
58
79
break
59
80
}
60
- g .writeType (s , t .Elt , depth , true )
81
+ g .writeType (s , t .Elt , t , depth , true )
61
82
s .WriteString ("[]" )
62
83
case * ast.StructType :
63
84
s .WriteString ("{\n " )
@@ -84,25 +105,83 @@ func (g *PackageGenerator) writeType(
84
105
}
85
106
case * ast.MapType :
86
107
s .WriteString ("{ [key: " )
87
- g .writeType (s , t .Key , depth , false )
108
+ g .writeType (s , t .Key , t , depth , false )
88
109
s .WriteString ("]: " )
89
- g .writeType (s , t .Value , depth , false )
110
+ g .writeType (s , t .Value , t , depth , false )
90
111
s .WriteByte ('}' )
91
112
case * ast.BasicLit :
92
- if strings .HasPrefix (t .Value , "`" ) {
93
- t .Value = backquoteEscapeRegexp .ReplaceAllString (t .Value , `\$1` )
113
+ switch t .Kind {
114
+ case token .INT :
115
+ if octalPrefixRegexp .MatchString (t .Value ) {
116
+ t .Value = "0o" + t .Value [1 :]
117
+ }
118
+ case token .CHAR :
119
+ var char rune
120
+ if strings .HasPrefix (t .Value , `'\x` ) ||
121
+ strings .HasPrefix (t .Value , `'\u` ) ||
122
+ strings .HasPrefix (t .Value , `'\U` ) {
123
+ i32 , err := strconv .ParseInt (t .Value [3 :len (t .Value )- 1 ], 16 , 32 )
124
+ if err != nil {
125
+ panic (err )
126
+ }
127
+ char = rune (i32 )
128
+ } else {
129
+ var data []byte
130
+ data = append (data , '"' )
131
+ data = append (data , []byte (t .Value [1 :len (t .Value )- 1 ])... )
132
+ data = append (data , '"' )
133
+ var s string
134
+ err := json .Unmarshal (data , & s )
135
+ if err != nil {
136
+ panic (err )
137
+ }
138
+ char = []rune (s )[0 ]
139
+ }
140
+ if char > 0xFFFF {
141
+ t .Value = fmt .Sprintf ("0x%08X /* %s */" , char , t .Value )
142
+ } else {
143
+ t .Value = fmt .Sprintf ("0x%04X /* %s */" , char , t .Value )
144
+ }
145
+ case token .STRING :
146
+ if strings .HasPrefix (t .Value , "`" ) {
147
+ t .Value = backquoteEscapeRegexp .ReplaceAllString (t .Value , `\$1` )
148
+ } else {
149
+ t .Value = unicode8Regexp .ReplaceAllStringFunc (t .Value , func (s string ) string {
150
+ if len (s ) == 10 {
151
+ s = fmt .Sprintf ("\\ u{%s}" , strings .ToUpper (s [2 :]))
152
+ }
153
+ return s
154
+ })
155
+ }
94
156
}
95
157
s .WriteString (t .Value )
96
158
case * ast.ParenExpr :
97
159
s .WriteByte ('(' )
98
- g .writeType (s , t .X , depth , false )
160
+ g .writeType (s , t .X , t , depth , false )
99
161
s .WriteByte (')' )
100
162
case * ast.BinaryExpr :
101
- g .writeType (s , t .X , depth , false )
102
- s .WriteByte (' ' )
103
- s .WriteString (t .Op .String ())
163
+ inParen := false
164
+ switch p := p .(type ) {
165
+ case * ast.BinaryExpr :
166
+ if jsNumberOperatorPrecedence [t .Op ] < jsNumberOperatorPrecedence [p .Op ] {
167
+ inParen = true
168
+ }
169
+ }
170
+ if inParen {
171
+ s .WriteByte ('(' )
172
+ }
173
+ g .writeType (s , t .X , t , depth , false )
104
174
s .WriteByte (' ' )
105
- g .writeType (s , t .Y , depth , false )
175
+ if t .Op == token .AND_NOT {
176
+ s .WriteString ("& ~" )
177
+ } else {
178
+ s .WriteString (t .Op .String ())
179
+ s .WriteByte (' ' )
180
+ }
181
+ g .writeType (s , t .Y , t , depth , false )
182
+ if inParen {
183
+ s .WriteByte (')' )
184
+ }
106
185
case * ast.InterfaceType :
107
186
g .writeInterfaceFields (s , t .Methods .List , depth + 1 )
108
187
case * ast.CallExpr , * ast.FuncType , * ast.ChanType :
@@ -112,32 +191,32 @@ func (g *PackageGenerator) writeType(
112
191
case token .TILDE :
113
192
// We just ignore the tilde token, in Typescript extended types are
114
193
// put into the generic typing itself, which we can't support yet.
115
- g .writeType (s , t .X , depth , false )
194
+ g .writeType (s , t .X , t , depth , false )
116
195
case token .XOR :
117
196
s .WriteString ("~" )
118
- g .writeType (s , t .X , depth , false )
197
+ g .writeType (s , t .X , t , depth , false )
119
198
case token .ADD , token .SUB , token .NOT :
120
199
s .WriteString (t .Op .String ())
121
- g .writeType (s , t .X , depth , false )
200
+ g .writeType (s , t .X , t , depth , false )
122
201
default :
123
202
err := fmt .Errorf ("unhandled unary expr: %v\n %T" , t , t )
124
203
fmt .Println (err )
125
204
panic (err )
126
205
}
127
206
case * ast.IndexListExpr :
128
- g .writeType (s , t .X , depth , false )
207
+ g .writeType (s , t .X , t , depth , false )
129
208
s .WriteByte ('<' )
130
209
for i , index := range t .Indices {
131
- g .writeType (s , index , depth , false )
210
+ g .writeType (s , index , t , depth , false )
132
211
if i != len (t .Indices )- 1 {
133
212
s .WriteString (", " )
134
213
}
135
214
}
136
215
s .WriteByte ('>' )
137
216
case * ast.IndexExpr :
138
- g .writeType (s , t .X , depth , false )
217
+ g .writeType (s , t .X , t , depth , false )
139
218
s .WriteByte ('<' )
140
- g .writeType (s , t .Index , depth , false )
219
+ g .writeType (s , t .Index , t , depth , false )
141
220
s .WriteByte ('>' )
142
221
default :
143
222
err := fmt .Errorf ("unhandled: %s\n %T" , t , t )
@@ -152,7 +231,7 @@ func (g *PackageGenerator) writeTypeParamsFields(s *strings.Builder, fields []*a
152
231
for j , ident := range f .Names {
153
232
s .WriteString (ident .Name )
154
233
s .WriteString (" extends " )
155
- g .writeType (s , f .Type , 0 , true )
234
+ g .writeType (s , f .Type , nil , 0 , true )
156
235
157
236
if i != len (fields )- 1 || j != len (f .Names )- 1 {
158
237
s .WriteString (", " )
@@ -192,7 +271,7 @@ func (g *PackageGenerator) writeInterfaceFields(
192
271
g .writeCommentGroupIfNotNil (s , f .Doc , depth + 1 )
193
272
}
194
273
g .writeIndent (s , depth + 1 )
195
- g .writeType (s , f .Type , depth , false )
274
+ g .writeType (s , f .Type , nil , depth , false )
196
275
197
276
if f .Comment != nil && g .PreserveTypeComments () {
198
277
s .WriteString (" // " )
@@ -301,7 +380,7 @@ func (g *PackageGenerator) writeStructFields(s *strings.Builder, fields []*ast.F
301
380
s .WriteString (": " )
302
381
303
382
if tstype == "" {
304
- g .writeType (s , f .Type , depth , false )
383
+ g .writeType (s , f .Type , nil , depth , false )
305
384
} else {
306
385
s .WriteString (tstype )
307
386
}
0 commit comments