goscript 0.0.21 → 0.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/builtin/builtin.ts +298 -33
- package/compiler/assignment.go +407 -0
- package/compiler/compiler.go +160 -5883
- package/compiler/compiler_test.go +4 -0
- package/compiler/composite-lit.go +537 -0
- package/compiler/config.go +3 -0
- package/compiler/decl.go +223 -0
- package/compiler/expr-call.go +423 -0
- package/compiler/expr-selector.go +105 -0
- package/compiler/expr-star.go +90 -0
- package/compiler/expr-type.go +182 -0
- package/compiler/expr-value.go +119 -0
- package/compiler/expr.go +356 -0
- package/compiler/field.go +169 -0
- package/compiler/lit.go +99 -0
- package/compiler/primitive.go +142 -0
- package/compiler/{write-type-spec.go → spec-struct.go} +79 -203
- package/compiler/spec-value.go +194 -0
- package/compiler/spec.go +263 -0
- package/compiler/stmt-assign.go +411 -0
- package/compiler/stmt-for.go +178 -0
- package/compiler/stmt-range.go +237 -0
- package/compiler/stmt.go +907 -0
- package/compiler/type-assert.go +463 -0
- package/compiler/type-info.go +141 -0
- package/compiler/type.go +556 -0
- package/dist/builtin/builtin.d.ts +24 -6
- package/dist/builtin/builtin.js +215 -19
- package/dist/builtin/builtin.js.map +1 -1
- package/go.mod +2 -1
- package/go.sum +4 -2
- package/package.json +2 -2
- /package/compiler/{writer.go → code-writer.go} +0 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"go/ast"
|
|
6
|
+
"go/token"
|
|
7
|
+
|
|
8
|
+
"github.com/pkg/errors"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// WriteStmtFor translates a Go `for` statement (`ast.ForStmt`) into a
|
|
12
|
+
// TypeScript `for` loop.
|
|
13
|
+
// The structure is `for (init_ts; cond_ts; post_ts) { body_ts }`.
|
|
14
|
+
// - The initialization part (`exp.Init`) is translated using `WriteStmtForInit`.
|
|
15
|
+
// - The condition part (`exp.Cond`) is translated using `WriteValueExpr`. If nil,
|
|
16
|
+
// the condition part in TypeScript is empty (resulting in an infinite loop
|
|
17
|
+
// unless broken out of).
|
|
18
|
+
// - The post-iteration part (`exp.Post`) is translated using `WriteStmtForPost`.
|
|
19
|
+
// - The loop body (`exp.Body`) is translated as a block statement using `WriteStmtBlock`.
|
|
20
|
+
//
|
|
21
|
+
// This function covers standard Go `for` loops (three-part loops, condition-only
|
|
22
|
+
// loops, and infinite loops). `for...range` loops are handled by `WriteStmtRange`.
|
|
23
|
+
func (c *GoToTSCompiler) WriteStmtFor(exp *ast.ForStmt) error {
|
|
24
|
+
c.tsw.WriteLiterally("for (")
|
|
25
|
+
if exp.Init != nil {
|
|
26
|
+
if err := c.WriteStmtForInit(exp.Init); err != nil { // Use WriteStmtForInit
|
|
27
|
+
return fmt.Errorf("failed to write for loop initialization: %w", err)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
c.tsw.WriteLiterally("; ")
|
|
31
|
+
if exp.Cond != nil {
|
|
32
|
+
if err := c.WriteValueExpr(exp.Cond); err != nil { // Condition is a value
|
|
33
|
+
return fmt.Errorf("failed to write for loop condition: %w", err)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
c.tsw.WriteLiterally("; ")
|
|
37
|
+
if exp.Post != nil {
|
|
38
|
+
if err := c.WriteStmtForPost(exp.Post); err != nil { // Use WriteStmtForPost
|
|
39
|
+
return fmt.Errorf("failed to write for loop post statement: %w", err)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
c.tsw.WriteLiterally(") ")
|
|
43
|
+
if err := c.WriteStmtBlock(exp.Body, false); err != nil {
|
|
44
|
+
return fmt.Errorf("failed to write for loop body: %w", err)
|
|
45
|
+
}
|
|
46
|
+
return nil
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// WriteStmtForInit translates the initialization part of a Go `for` loop header
|
|
50
|
+
// (e.g., `i := 0` or `i = 0` in `for i := 0; ...`) into its TypeScript equivalent.
|
|
51
|
+
// - If `stmt` is an `ast.AssignStmt`:
|
|
52
|
+
// - For short variable declarations (`:=`) with multiple variables (e.g., `i, j := 0, 10`),
|
|
53
|
+
// it generates `let i = 0, j = 10`. Each LHS variable is paired with its
|
|
54
|
+
// corresponding RHS value; if RHS values are insufficient, remaining LHS
|
|
55
|
+
// variables are initialized with their zero value using `WriteZeroValue`.
|
|
56
|
+
// - For other assignments (single variable `:=`, or regular `=`), it uses
|
|
57
|
+
// `writeAssignmentCore`. If it's `:=`, `let` is prepended.
|
|
58
|
+
// - If `stmt` is an `ast.ExprStmt` (less common in `for` inits), it translates
|
|
59
|
+
// the expression using `WriteValueExpr`.
|
|
60
|
+
//
|
|
61
|
+
// Unhandled statement types in the init part result in a comment.
|
|
62
|
+
func (c *GoToTSCompiler) WriteStmtForInit(stmt ast.Stmt) error {
|
|
63
|
+
switch s := stmt.(type) {
|
|
64
|
+
case *ast.AssignStmt:
|
|
65
|
+
// Handle assignment in init (e.g., i := 0 or i = 0)
|
|
66
|
+
// For TypeScript for-loop init, we need to handle multi-variable declarations differently
|
|
67
|
+
if s.Tok == token.DEFINE && len(s.Lhs) > 1 && len(s.Rhs) > 0 {
|
|
68
|
+
// For loop initialization with multiple variables (e.g., let i = 0, j = 10)
|
|
69
|
+
c.tsw.WriteLiterally("let ")
|
|
70
|
+
|
|
71
|
+
// Handle each LHS variable with its corresponding RHS value
|
|
72
|
+
for i, lhs := range s.Lhs {
|
|
73
|
+
if i > 0 {
|
|
74
|
+
c.tsw.WriteLiterally(", ")
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Write the LHS variable name
|
|
78
|
+
if err := c.WriteValueExpr(lhs); err != nil {
|
|
79
|
+
return err
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Write the corresponding RHS, or a default if not enough RHS values
|
|
83
|
+
c.tsw.WriteLiterally(" = ")
|
|
84
|
+
if i < len(s.Rhs) {
|
|
85
|
+
// If there's a corresponding RHS value
|
|
86
|
+
if err := c.WriteValueExpr(s.Rhs[i]); err != nil {
|
|
87
|
+
return err
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
// No corresponding RHS
|
|
91
|
+
return errors.Errorf("no corresponding rhs to lhs: %v", s)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
// Regular single variable or assignment (not declaration)
|
|
96
|
+
if s.Tok == token.DEFINE {
|
|
97
|
+
c.tsw.WriteLiterally("let ")
|
|
98
|
+
}
|
|
99
|
+
// Use existing assignment core logic
|
|
100
|
+
if err := c.writeAssignmentCore(s.Lhs, s.Rhs, s.Tok, false); err != nil {
|
|
101
|
+
return err
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return nil
|
|
105
|
+
case *ast.ExprStmt:
|
|
106
|
+
// Handle expression statement in init
|
|
107
|
+
return c.WriteValueExpr(s.X)
|
|
108
|
+
default:
|
|
109
|
+
return errors.Errorf("unhandled for loop init statement: %T", stmt)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// WriteStmtForPost translates the post-iteration part of a Go `for` loop header
|
|
114
|
+
// (e.g., `i++` or `i, j = i+1, j-1` in `for ...; i++`) into its TypeScript
|
|
115
|
+
// equivalent.
|
|
116
|
+
// - If `stmt` is an `ast.IncDecStmt` (e.g., `i++`), it writes `i_ts++`.
|
|
117
|
+
// - If `stmt` is an `ast.AssignStmt`:
|
|
118
|
+
// - For multiple variable assignments (e.g., `i, j = i+1, j-1`), it generates
|
|
119
|
+
// TypeScript array destructuring: `[i_ts, j_ts] = [i_ts+1, j_ts-1]`.
|
|
120
|
+
// - For single variable assignments, it uses `writeAssignmentCore`.
|
|
121
|
+
// - If `stmt` is an `ast.ExprStmt` (less common), it translates the expression
|
|
122
|
+
// using `WriteValueExpr`.
|
|
123
|
+
//
|
|
124
|
+
// Unhandled statement types in the post part result in a comment.
|
|
125
|
+
func (c *GoToTSCompiler) WriteStmtForPost(stmt ast.Stmt) error {
|
|
126
|
+
switch s := stmt.(type) {
|
|
127
|
+
case *ast.IncDecStmt:
|
|
128
|
+
// Handle increment/decrement (e.g., i++)
|
|
129
|
+
if err := c.WriteValueExpr(s.X); err != nil { // The expression (e.g., i)
|
|
130
|
+
return err
|
|
131
|
+
}
|
|
132
|
+
tokStr, ok := TokenToTs(s.Tok)
|
|
133
|
+
if !ok {
|
|
134
|
+
return errors.Errorf("unknown incdec token: %v", s.Tok)
|
|
135
|
+
}
|
|
136
|
+
c.tsw.WriteLiterally(tokStr) // The token (e.g., ++)
|
|
137
|
+
return nil
|
|
138
|
+
case *ast.AssignStmt:
|
|
139
|
+
// For multiple variable assignment in post like i, j = i+1, j-1
|
|
140
|
+
// we need to use destructuring in TypeScript like [i, j] = [i+1, j-1]
|
|
141
|
+
if len(s.Lhs) > 1 && len(s.Rhs) > 0 {
|
|
142
|
+
// Write LHS as array destructuring
|
|
143
|
+
c.tsw.WriteLiterally("[")
|
|
144
|
+
for i, lhs := range s.Lhs {
|
|
145
|
+
if i > 0 {
|
|
146
|
+
c.tsw.WriteLiterally(", ")
|
|
147
|
+
}
|
|
148
|
+
if err := c.WriteValueExpr(lhs); err != nil {
|
|
149
|
+
return err
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
c.tsw.WriteLiterally("] = [")
|
|
153
|
+
|
|
154
|
+
// Write RHS as array
|
|
155
|
+
for i, rhs := range s.Rhs {
|
|
156
|
+
if i > 0 {
|
|
157
|
+
c.tsw.WriteLiterally(", ")
|
|
158
|
+
}
|
|
159
|
+
if err := c.WriteValueExpr(rhs); err != nil {
|
|
160
|
+
return err
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
c.tsw.WriteLiterally("]")
|
|
164
|
+
} else {
|
|
165
|
+
// Regular single variable assignment
|
|
166
|
+
// No declaration handling needed in for loop post statements
|
|
167
|
+
if err := c.writeAssignmentCore(s.Lhs, s.Rhs, s.Tok, false); err != nil {
|
|
168
|
+
return err
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return nil
|
|
172
|
+
case *ast.ExprStmt:
|
|
173
|
+
// Handle expression statement in post
|
|
174
|
+
return c.WriteValueExpr(s.X)
|
|
175
|
+
default:
|
|
176
|
+
return errors.Errorf("unhandled for loop post statement: %T", stmt)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
package compiler
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"go/ast"
|
|
6
|
+
"go/types"
|
|
7
|
+
|
|
8
|
+
"github.com/pkg/errors"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// WriteStmtRange translates a Go `for...range` statement (`ast.RangeStmt`)
|
|
12
|
+
// into an equivalent TypeScript loop. The translation depends on the type of
|
|
13
|
+
// the expression being ranged over (`exp.X`), determined using `go/types` info.
|
|
14
|
+
//
|
|
15
|
+
// - **Maps (`*types.Map`):**
|
|
16
|
+
// `for k, v := range myMap` becomes `for (const [k_ts, v_ts] of myMap_ts.entries()) { const k = k_ts; const v = v_ts; ...body... }`.
|
|
17
|
+
// If only `k` or `v` (or neither) is used, the corresponding TypeScript const declaration is adjusted.
|
|
18
|
+
//
|
|
19
|
+
// - **Strings (`*types.Basic` with `IsString` info):**
|
|
20
|
+
// `for i, r := range myString` becomes:
|
|
21
|
+
// `const _runes = $.stringToRunes(myString_ts);`
|
|
22
|
+
// `for (let i_ts = 0; i_ts < _runes.length; i_ts++) { const r_ts = _runes[i_ts]; ...body... }`.
|
|
23
|
+
// The index variable `i_ts` uses the Go key variable name if provided (and not `_`).
|
|
24
|
+
// The rune variable `r_ts` uses the Go value variable name.
|
|
25
|
+
//
|
|
26
|
+
// - **Integers (`*types.Basic` with `IsInteger` info, Go 1.22+):**
|
|
27
|
+
// `for i := range N` becomes `for (let i_ts = 0; i_ts < N_ts; i_ts++) { ...body... }`.
|
|
28
|
+
// `for i, v := range N` becomes `for (let i_ts = 0; i_ts < N_ts; i_ts++) { const v_ts = i_ts; ...body... }`.
|
|
29
|
+
//
|
|
30
|
+
// - **Arrays (`*types.Array`) and Slices (`*types.Slice`):**
|
|
31
|
+
// - If both key (index) and value are used (`for i, val := range arr`):
|
|
32
|
+
// `for (let i_ts = 0; i_ts < arr_ts.length; i_ts++) { const val_ts = arr_ts[i_ts]; ...body... }`.
|
|
33
|
+
// - If only the key (index) is used (`for i := range arr`):
|
|
34
|
+
// `for (let i_ts = 0; i_ts < arr_ts.length; i_ts++) { ...body... }`.
|
|
35
|
+
// - If only the value is used (`for _, val := range arr`):
|
|
36
|
+
// `for (const v_ts of arr_ts) { const val_ts = v_ts; ...body... }`.
|
|
37
|
+
// - If neither is used (e.g., `for range arr`), a simple index loop `for (let _i = 0; ...)` is generated.
|
|
38
|
+
// The index variable `i_ts` uses the Go key variable name if provided.
|
|
39
|
+
//
|
|
40
|
+
// Loop variables (`exp.Key`, `exp.Value`) are declared as `const` inside the loop
|
|
41
|
+
// body if they are not blank identifiers (`_`). The loop body (`exp.Body`) is
|
|
42
|
+
// translated using `WriteStmtBlock` (or `WriteStmt` for array/slice with key and value).
|
|
43
|
+
// If the ranged type is not supported, a comment is written, and an error is returned.
|
|
44
|
+
func (c *GoToTSCompiler) WriteStmtRange(exp *ast.RangeStmt) error {
|
|
45
|
+
// Get the type of the iterable expression
|
|
46
|
+
iterType := c.pkg.TypesInfo.TypeOf(exp.X)
|
|
47
|
+
underlying := iterType.Underlying()
|
|
48
|
+
|
|
49
|
+
// Handle map types
|
|
50
|
+
if _, ok := underlying.(*types.Map); ok {
|
|
51
|
+
// Use for-of with entries() for proper Map iteration
|
|
52
|
+
c.tsw.WriteLiterally("for (const [k, v] of ")
|
|
53
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
54
|
+
return fmt.Errorf("failed to write range loop map expression: %w", err)
|
|
55
|
+
}
|
|
56
|
+
c.tsw.WriteLiterally(".entries()) {")
|
|
57
|
+
c.tsw.Indent(1)
|
|
58
|
+
c.tsw.WriteLine("")
|
|
59
|
+
// If a key variable is provided and is not blank, declare it as a constant
|
|
60
|
+
if exp.Key != nil {
|
|
61
|
+
if ident, ok := exp.Key.(*ast.Ident); ok && ident.Name != "_" {
|
|
62
|
+
c.tsw.WriteLiterally("const ")
|
|
63
|
+
c.WriteIdent(ident, false)
|
|
64
|
+
c.tsw.WriteLiterally(" = k")
|
|
65
|
+
c.tsw.WriteLine("")
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// If a value variable is provided and is not blank, use the value from entries()
|
|
69
|
+
if exp.Value != nil {
|
|
70
|
+
if ident, ok := exp.Value.(*ast.Ident); ok && ident.Name != "_" {
|
|
71
|
+
c.tsw.WriteLiterally("const ")
|
|
72
|
+
c.WriteIdent(ident, false)
|
|
73
|
+
c.tsw.WriteLiterally(" = v")
|
|
74
|
+
c.tsw.WriteLine("")
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Write the loop body
|
|
78
|
+
if err := c.WriteStmtBlock(exp.Body, false); err != nil {
|
|
79
|
+
return fmt.Errorf("failed to write range loop map body: %w", err)
|
|
80
|
+
}
|
|
81
|
+
c.tsw.Indent(-1)
|
|
82
|
+
c.tsw.WriteLine("}")
|
|
83
|
+
return nil
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Handle basic types (string, integer)
|
|
87
|
+
if basic, ok := underlying.(*types.Basic); ok {
|
|
88
|
+
if basic.Info()&types.IsString != 0 {
|
|
89
|
+
// Add a scope to avoid collision of _runes variable
|
|
90
|
+
c.tsw.WriteLine("{")
|
|
91
|
+
c.tsw.Indent(1)
|
|
92
|
+
|
|
93
|
+
// Convert the string to runes using $.stringToRunes
|
|
94
|
+
c.tsw.WriteLiterally("const _runes = $.stringToRunes(")
|
|
95
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
96
|
+
return fmt.Errorf("failed to write range loop string conversion expression: %w", err)
|
|
97
|
+
}
|
|
98
|
+
c.tsw.WriteLiterally(")")
|
|
99
|
+
c.tsw.WriteLine("")
|
|
100
|
+
|
|
101
|
+
// Determine the index variable name for the generated loop
|
|
102
|
+
indexVarName := "i" // Default name
|
|
103
|
+
if exp.Key != nil {
|
|
104
|
+
if keyIdent, ok := exp.Key.(*ast.Ident); ok && keyIdent.Name != "_" {
|
|
105
|
+
indexVarName = keyIdent.Name
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
c.tsw.WriteLiterallyf("for (let %s = 0; %s < _runes.length; %s++) {", indexVarName, indexVarName, indexVarName)
|
|
109
|
+
c.tsw.Indent(1)
|
|
110
|
+
c.tsw.WriteLine("")
|
|
111
|
+
// Declare value if provided and not blank
|
|
112
|
+
if exp.Value != nil {
|
|
113
|
+
if ident, ok := exp.Value.(*ast.Ident); ok && ident.Name != "_" {
|
|
114
|
+
c.tsw.WriteLiterally("const ")
|
|
115
|
+
c.WriteIdent(ident, false)
|
|
116
|
+
c.tsw.WriteLiterally(" = _runes[i]") // TODO: should be indexVarName?
|
|
117
|
+
c.tsw.WriteLine("")
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if err := c.WriteStmtBlock(exp.Body, false); err != nil {
|
|
121
|
+
return fmt.Errorf("failed to write range loop string body: %w", err)
|
|
122
|
+
}
|
|
123
|
+
c.tsw.Indent(-1)
|
|
124
|
+
c.tsw.WriteLine("}")
|
|
125
|
+
|
|
126
|
+
// outer }
|
|
127
|
+
c.tsw.Indent(-1)
|
|
128
|
+
c.tsw.WriteLine("}")
|
|
129
|
+
return nil
|
|
130
|
+
} else if basic.Info()&types.IsInteger != 0 {
|
|
131
|
+
// Handle ranging over an integer (Go 1.22+)
|
|
132
|
+
// Determine the index variable name for the generated loop
|
|
133
|
+
indexVarName := "_i" // Default name
|
|
134
|
+
if exp.Key != nil {
|
|
135
|
+
if keyIdent, ok := exp.Key.(*ast.Ident); ok && keyIdent.Name != "_" {
|
|
136
|
+
indexVarName = keyIdent.Name
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
c.tsw.WriteLiterallyf("for (let %s = 0; %s < ", indexVarName, indexVarName)
|
|
141
|
+
if err := c.WriteValueExpr(exp.X); err != nil { // This is N
|
|
142
|
+
return fmt.Errorf("failed to write range loop integer expression: %w", err)
|
|
143
|
+
}
|
|
144
|
+
c.tsw.WriteLiterallyf("; %s++) {", indexVarName)
|
|
145
|
+
c.tsw.Indent(1)
|
|
146
|
+
c.tsw.WriteLine("")
|
|
147
|
+
|
|
148
|
+
// The value variable is not allowed ranging over an integer.
|
|
149
|
+
if exp.Value != nil {
|
|
150
|
+
return errors.Errorf("ranging over an integer supports key variable only (not value variable): %v", exp)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if err := c.WriteStmtBlock(exp.Body, false); err != nil {
|
|
154
|
+
return fmt.Errorf("failed to write range loop integer body: %w", err)
|
|
155
|
+
}
|
|
156
|
+
c.tsw.Indent(-1)
|
|
157
|
+
c.tsw.WriteLine("}")
|
|
158
|
+
return nil
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Handle array and slice types
|
|
163
|
+
_, isSlice := underlying.(*types.Slice)
|
|
164
|
+
_, isArray := underlying.(*types.Array)
|
|
165
|
+
if isArray || isSlice {
|
|
166
|
+
// Determine the index variable name for the generated loop
|
|
167
|
+
indexVarName := "i" // Default name
|
|
168
|
+
if exp.Key != nil {
|
|
169
|
+
if keyIdent, ok := exp.Key.(*ast.Ident); ok && keyIdent.Name != "_" {
|
|
170
|
+
indexVarName = keyIdent.Name
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// If both key and value are provided, use an index loop and assign both
|
|
174
|
+
if exp.Key != nil && exp.Value != nil {
|
|
175
|
+
c.tsw.WriteLiterallyf("for (let %s = 0; %s < $.len(", indexVarName, indexVarName)
|
|
176
|
+
if err := c.WriteValueExpr(exp.X); err != nil { // Write the expression for the iterable
|
|
177
|
+
return fmt.Errorf("failed to write range loop array/slice expression (key and value): %w", err)
|
|
178
|
+
}
|
|
179
|
+
c.tsw.WriteLiterallyf("); %s++) {", indexVarName)
|
|
180
|
+
c.tsw.Indent(1)
|
|
181
|
+
c.tsw.WriteLine("")
|
|
182
|
+
// Declare value if not blank
|
|
183
|
+
if ident, ok := exp.Value.(*ast.Ident); ok && ident.Name != "_" {
|
|
184
|
+
c.tsw.WriteLiterally("const ")
|
|
185
|
+
c.WriteIdent(ident, false)
|
|
186
|
+
c.tsw.WriteLiterally(" = ")
|
|
187
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
188
|
+
return fmt.Errorf("failed to write range loop array/slice value expression: %w", err)
|
|
189
|
+
}
|
|
190
|
+
c.tsw.WriteLiterallyf("![%s]", indexVarName) // Use indexVarName with not-null assert
|
|
191
|
+
c.tsw.WriteLine("")
|
|
192
|
+
}
|
|
193
|
+
if err := c.WriteStmt(exp.Body); err != nil {
|
|
194
|
+
return fmt.Errorf("failed to write range loop array/slice body (key and value): %w", err)
|
|
195
|
+
}
|
|
196
|
+
c.tsw.Indent(-1)
|
|
197
|
+
c.tsw.WriteLine("}")
|
|
198
|
+
return nil
|
|
199
|
+
} else if exp.Key != nil && exp.Value == nil { // Only key provided
|
|
200
|
+
c.tsw.WriteLiterallyf("for (let %s = 0; %s < $.len(", indexVarName, indexVarName)
|
|
201
|
+
// Write the expression for the iterable
|
|
202
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
203
|
+
return fmt.Errorf("failed to write expression for the iterable: %w", err)
|
|
204
|
+
}
|
|
205
|
+
c.tsw.WriteLiterallyf("); %s++) {", indexVarName)
|
|
206
|
+
c.tsw.Indent(1)
|
|
207
|
+
c.tsw.WriteLine("")
|
|
208
|
+
if err := c.WriteStmtBlock(exp.Body, false); err != nil {
|
|
209
|
+
return fmt.Errorf("failed to write range loop array/slice body (only key): %w", err)
|
|
210
|
+
}
|
|
211
|
+
c.tsw.Indent(-1)
|
|
212
|
+
c.tsw.WriteLine("}")
|
|
213
|
+
return nil
|
|
214
|
+
} else if exp.Key == nil && exp.Value != nil { // Only value provided
|
|
215
|
+
// I think this is impossible. See for_range_value_only test.
|
|
216
|
+
return errors.Errorf("unexpected value without key in for range expression: %v", exp)
|
|
217
|
+
} else {
|
|
218
|
+
// Fallback: simple index loop without declaring range variables, use _i
|
|
219
|
+
indexVarName := "_i"
|
|
220
|
+
c.tsw.WriteLiterallyf("for (let %s = 0; %s < $.len(", indexVarName, indexVarName)
|
|
221
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
222
|
+
return fmt.Errorf("failed to write range loop array/slice length expression (fallback): %w", err)
|
|
223
|
+
}
|
|
224
|
+
c.tsw.WriteLiterallyf("); %s++) {", indexVarName)
|
|
225
|
+
c.tsw.Indent(1)
|
|
226
|
+
c.tsw.WriteLine("")
|
|
227
|
+
if err := c.WriteStmtBlock(exp.Body, false); err != nil {
|
|
228
|
+
return fmt.Errorf("failed to write range loop array/slice body (fallback): %w", err)
|
|
229
|
+
}
|
|
230
|
+
c.tsw.Indent(-1)
|
|
231
|
+
c.tsw.WriteLine("}")
|
|
232
|
+
return nil
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return errors.Errorf("unsupported range loop type: %T for expression %v", underlying, exp)
|
|
237
|
+
}
|