goscript 0.0.11 → 0.0.13
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/README.md +3 -3
- package/builtin/builtin.go +11 -0
- package/builtin/builtin.ts +9 -0
- package/cmd/goscript/cmd_compile.go +21 -3
- package/compiler/compile.go +15 -24
- package/compiler/compile_expr.go +79 -9
- package/compiler/compile_field.go +22 -1
- package/compiler/compile_spec.go +38 -8
- package/compiler/compile_stmt.go +35 -28
- package/compiler/compiler.go +1 -0
- package/compiler/config.go +2 -0
- package/compiler/file_compiler.go +1 -1
- package/compiler/index.test.ts +1 -1
- package/compiler/output_path.go +1 -1
- package/compiler/types/tokens.go +1 -0
- package/compiler/writer.go +1 -1
- package/dist/builtin/builtin.d.ts +6 -0
- package/dist/builtin/builtin.js +8 -0
- package/dist/builtin/builtin.js.map +1 -1
- package/dist/compliance/tests/async_basic/async_basic.gs.js +1 -1
- package/dist/compliance/tests/async_basic/async_basic.gs.js.map +1 -1
- package/dist/compliance/tests/async_defer_statement/async_defer_statement.gs.js +1 -1
- package/dist/compliance/tests/async_defer_statement/async_defer_statement.gs.js.map +1 -1
- package/dist/compliance/tests/channel_basic/channel_basic.gs.js +1 -1
- package/dist/compliance/tests/channel_basic/channel_basic.gs.js.map +1 -1
- package/dist/compliance/tests/composite_literal_assignment/composite_literal_assignment.gs.js +1 -1
- package/dist/compliance/tests/composite_literal_assignment/composite_literal_assignment.gs.js.map +1 -1
- package/dist/compliance/tests/copy_independence/copy_independence.gs.js +1 -1
- package/dist/compliance/tests/copy_independence/copy_independence.gs.js.map +1 -1
- package/dist/compliance/tests/defer_statement/defer_statement.gs.js +1 -1
- package/dist/compliance/tests/defer_statement/defer_statement.gs.js.map +1 -1
- package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.js +2 -2
- package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.js.map +1 -1
- package/dist/compliance/tests/flag_bitwise_op/flag_bitwise_op.gs.d.ts +1 -0
- package/dist/compliance/tests/flag_bitwise_op/flag_bitwise_op.gs.js +29 -0
- package/dist/compliance/tests/flag_bitwise_op/flag_bitwise_op.gs.js.map +1 -0
- package/dist/compliance/tests/for_range/for_range.gs.js +1 -1
- package/dist/compliance/tests/for_range/for_range.gs.js.map +1 -1
- package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.d.ts +1 -1
- package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.js +1 -1
- package/dist/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.js.map +1 -1
- package/dist/compliance/tests/interface_method_comments/interface_method_comments.gs.d.ts +1 -0
- package/dist/compliance/tests/interface_method_comments/interface_method_comments.gs.js +12 -0
- package/dist/compliance/tests/interface_method_comments/interface_method_comments.gs.js.map +1 -0
- package/dist/compliance/tests/interface_multi_param_return/interface_multi_param_return.gs.js +2 -2
- package/dist/compliance/tests/interface_multi_param_return/interface_multi_param_return.gs.js.map +1 -1
- package/dist/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.js +2 -2
- package/dist/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.js.map +1 -1
- package/dist/compliance/tests/interface_type_assertion/interface_type_assertion.gs.js +2 -2
- package/dist/compliance/tests/interface_type_assertion/interface_type_assertion.gs.js.map +1 -1
- package/dist/compliance/tests/interface_type_assertion_signature_mismatch/interface_type_assertion_signature_mismatch.gs.js +1 -1
- package/dist/compliance/tests/interface_type_assertion_signature_mismatch/interface_type_assertion_signature_mismatch.gs.js.map +1 -1
- package/dist/compliance/tests/map_support/map_support.gs.js +1 -1
- package/dist/compliance/tests/map_support/map_support.gs.js.map +1 -1
- package/dist/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.gs.js +1 -1
- package/dist/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.gs.js.map +1 -1
- package/dist/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.gs.js +1 -1
- package/dist/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.gs.js.map +1 -1
- package/dist/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.gs.js +1 -1
- package/dist/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.gs.js.map +1 -1
- package/dist/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.gs.js +1 -1
- package/dist/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.gs.js.map +1 -1
- package/dist/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.gs.js +1 -1
- package/dist/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.gs.js.map +1 -1
- package/dist/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.gs.js +1 -1
- package/dist/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.gs.js.map +1 -1
- package/dist/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.gs.js +1 -1
- package/dist/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.gs.js.map +1 -1
- package/dist/compliance/tests/pointer_initialization/pointer_initialization.gs.js +1 -1
- package/dist/compliance/tests/pointer_initialization/pointer_initialization.gs.js.map +1 -1
- package/dist/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.gs.js +1 -1
- package/dist/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.gs.js.map +1 -1
- package/dist/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.gs.js +2 -1
- package/dist/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.gs.js.map +1 -1
- package/dist/compliance/tests/select_statement/select_statement.gs.js +2 -4
- package/dist/compliance/tests/select_statement/select_statement.gs.js.map +1 -1
- package/dist/compliance/tests/simple_deref_assignment/simple_deref_assignment.gs.js +1 -1
- package/dist/compliance/tests/simple_deref_assignment/simple_deref_assignment.gs.js.map +1 -1
- package/dist/compliance/tests/slices/slices.gs.js +1 -1
- package/dist/compliance/tests/slices/slices.gs.js.map +1 -1
- package/dist/compliance/tests/string_conversion/string_conversion.gs.d.ts +1 -0
- package/dist/compliance/tests/string_conversion/string_conversion.gs.js +41 -0
- package/dist/compliance/tests/string_conversion/string_conversion.gs.js.map +1 -0
- package/dist/compliance/tests/struct_embedding/struct_embedding.gs.js +1 -1
- package/dist/compliance/tests/struct_embedding/struct_embedding.gs.js.map +1 -1
- package/dist/compliance/tests/struct_field_access/struct_field_access.gs.js +1 -1
- package/dist/compliance/tests/struct_field_access/struct_field_access.gs.js.map +1 -1
- package/dist/compliance/tests/struct_pointer_interface_fields/struct_pointer_interface_fields.gs.d.ts +1 -0
- package/dist/compliance/tests/struct_pointer_interface_fields/struct_pointer_interface_fields.gs.js +26 -0
- package/dist/compliance/tests/struct_pointer_interface_fields/struct_pointer_interface_fields.gs.js.map +1 -0
- package/dist/compliance/tests/struct_value_init_clone/struct_value_init_clone.gs.js +1 -1
- package/dist/compliance/tests/struct_value_init_clone/struct_value_init_clone.gs.js.map +1 -1
- package/dist/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.gs.js +1 -1
- package/dist/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.gs.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -306,7 +306,7 @@ func main() {
|
|
|
306
306
|
Generated with `goscript compile .`:
|
|
307
307
|
|
|
308
308
|
```typescript
|
|
309
|
-
import * as goscript from "@
|
|
309
|
+
import * as goscript from "@goscript/builtin"
|
|
310
310
|
|
|
311
311
|
class MyStruct {
|
|
312
312
|
// MyInt is a public integer field, initialized to zero.
|
|
@@ -460,10 +460,10 @@ export async function main(): Promise<void> {
|
|
|
460
460
|
|
|
461
461
|
Code is compiled with `GOARCH=js` and uses a 32-bit environment similar to wasm.
|
|
462
462
|
|
|
463
|
-
All Go import paths are prefixed with `@
|
|
463
|
+
All Go import paths are prefixed with `@goscript/` and can be imported in TypeScript:
|
|
464
464
|
|
|
465
465
|
```typescript
|
|
466
|
-
import { MyAsyncFunction, MyStruct } from '@
|
|
466
|
+
import { MyAsyncFunction, MyStruct } from '@goscript/github.com/myorg/mypackage';
|
|
467
467
|
|
|
468
468
|
// Example of importing and using a compiled Go async function
|
|
469
469
|
async function runGoCode() {
|
package/builtin/builtin.ts
CHANGED
|
@@ -90,6 +90,15 @@ export const stringToRunes = (str: string): number[] => {
|
|
|
90
90
|
return Array.from(str).map((c) => c.codePointAt(0) || 0)
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Converts an array of Unicode code points (runes) to a string.
|
|
95
|
+
* @param runes The input array of numbers representing Unicode code points.
|
|
96
|
+
* @returns The resulting string.
|
|
97
|
+
*/
|
|
98
|
+
export const runesToString = (runes: number[]): string => {
|
|
99
|
+
return String.fromCharCode(...runes);
|
|
100
|
+
};
|
|
101
|
+
|
|
93
102
|
/**
|
|
94
103
|
* Gets a value from a map, with a default value if the key doesn't exist.
|
|
95
104
|
* @param map The map to get from.
|
|
@@ -2,17 +2,22 @@ package main
|
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
4
|
"context"
|
|
5
|
+
"slices"
|
|
5
6
|
|
|
6
7
|
"github.com/aperturerobotics/cli"
|
|
7
8
|
"github.com/paralin/goscript/compiler"
|
|
8
9
|
"github.com/pkg/errors"
|
|
9
10
|
"github.com/sirupsen/logrus"
|
|
11
|
+
|
|
12
|
+
// _ ensure we include the builtin package
|
|
13
|
+
_ "github.com/paralin/goscript/builtin"
|
|
10
14
|
)
|
|
11
15
|
|
|
12
16
|
var (
|
|
13
|
-
cliCompiler
|
|
14
|
-
cliCompilerConfig
|
|
15
|
-
cliCompilerPkg
|
|
17
|
+
cliCompiler *compiler.Compiler
|
|
18
|
+
cliCompilerConfig compiler.Config
|
|
19
|
+
cliCompilerPkg cli.StringSlice
|
|
20
|
+
cliCompilerBuildFlags cli.StringSlice
|
|
16
21
|
)
|
|
17
22
|
|
|
18
23
|
// CompileCommands are commands related to compiling code.
|
|
@@ -33,6 +38,7 @@ var CompileCommands = []*cli.Command{{
|
|
|
33
38
|
Name: "package",
|
|
34
39
|
Usage: "the package(s) to compile",
|
|
35
40
|
Aliases: []string{"p", "packages"},
|
|
41
|
+
EnvVars: []string{"GOSCRIPT_PACKAGES"},
|
|
36
42
|
Destination: &cliCompilerPkg,
|
|
37
43
|
},
|
|
38
44
|
&cli.StringFlag{
|
|
@@ -40,12 +46,21 @@ var CompileCommands = []*cli.Command{{
|
|
|
40
46
|
Usage: "the output typescript path to use",
|
|
41
47
|
Destination: &cliCompilerConfig.OutputPathRoot,
|
|
42
48
|
Value: "./output",
|
|
49
|
+
EnvVars: []string{"GOSCRIPT_OUTPUT"},
|
|
43
50
|
},
|
|
44
51
|
&cli.StringFlag{
|
|
45
52
|
Name: "dir",
|
|
46
53
|
Usage: "the working directory to use for the compiler (default: current directory)",
|
|
47
54
|
Destination: &cliCompilerConfig.Dir,
|
|
48
55
|
Value: "",
|
|
56
|
+
EnvVars: []string{"GOSCRIPT_DIR"},
|
|
57
|
+
},
|
|
58
|
+
&cli.StringSliceFlag{
|
|
59
|
+
Name: "build-flags",
|
|
60
|
+
Aliases: []string{"b", "buildflags", "build-flag", "buildflag"},
|
|
61
|
+
Usage: "Go build flags (tags) to use during analysis",
|
|
62
|
+
Destination: &cliCompilerBuildFlags,
|
|
63
|
+
EnvVars: []string{"GOSCRIPT_BUILD_FLAGS"},
|
|
49
64
|
},
|
|
50
65
|
},
|
|
51
66
|
}}
|
|
@@ -57,5 +72,8 @@ func compilePackage(c *cli.Context) error {
|
|
|
57
72
|
return errors.New("package(s) must be specified")
|
|
58
73
|
}
|
|
59
74
|
|
|
75
|
+
// build flags
|
|
76
|
+
cliCompilerConfig.BuildFlags = slices.Clone(cliCompilerBuildFlags.Value())
|
|
77
|
+
|
|
60
78
|
return cliCompiler.CompilePackages(context.Background(), pkgs...)
|
|
61
79
|
}
|
package/compiler/compile.go
CHANGED
|
@@ -4,7 +4,7 @@ import (
|
|
|
4
4
|
"fmt"
|
|
5
5
|
"go/ast"
|
|
6
6
|
"go/token"
|
|
7
|
-
"go/types"
|
|
7
|
+
gtypes "go/types"
|
|
8
8
|
|
|
9
9
|
gstypes "github.com/paralin/goscript/compiler/types"
|
|
10
10
|
"golang.org/x/tools/go/packages"
|
|
@@ -19,14 +19,12 @@ type GoToTSCompiler struct {
|
|
|
19
19
|
asyncFuncs map[string]bool // Track which functions are async
|
|
20
20
|
nextBlockNeedsDefer bool // Track if the next block should have a "using" statement
|
|
21
21
|
inAsyncFunction bool // Track if we're inside an async function
|
|
22
|
-
|
|
23
|
-
tempVarCounter int // Counter for generating unique temporary variable names
|
|
24
22
|
}
|
|
25
23
|
|
|
26
24
|
// WriteGoType writes a Go type as a TypeScript type.
|
|
27
|
-
func (c *GoToTSCompiler) WriteGoType(typ
|
|
25
|
+
func (c *GoToTSCompiler) WriteGoType(typ gtypes.Type) {
|
|
28
26
|
switch t := typ.(type) {
|
|
29
|
-
case *
|
|
27
|
+
case *gtypes.Basic:
|
|
30
28
|
// Handle basic types (int, string, etc.)
|
|
31
29
|
name := t.Name()
|
|
32
30
|
if tsType, ok := gstypes.GoBuiltinToTypescript(name); ok {
|
|
@@ -34,31 +32,31 @@ func (c *GoToTSCompiler) WriteGoType(typ types.Type) {
|
|
|
34
32
|
} else {
|
|
35
33
|
c.tsw.WriteLiterally(name)
|
|
36
34
|
}
|
|
37
|
-
case *
|
|
35
|
+
case *gtypes.Named:
|
|
38
36
|
// Handle named types (custom types)
|
|
39
37
|
c.tsw.WriteLiterally(t.Obj().Name())
|
|
40
|
-
case *
|
|
38
|
+
case *gtypes.Pointer:
|
|
41
39
|
// Handle pointer types (*T becomes T | null)
|
|
42
40
|
c.WriteGoType(t.Elem())
|
|
43
41
|
c.tsw.WriteLiterally(" | null")
|
|
44
|
-
case *
|
|
42
|
+
case *gtypes.Slice, *gtypes.Array:
|
|
45
43
|
// Handle array/slice types ([]T or [N]T becomes T[])
|
|
46
|
-
var elemType
|
|
47
|
-
if slice, ok := t.(*
|
|
44
|
+
var elemType gtypes.Type
|
|
45
|
+
if slice, ok := t.(*gtypes.Slice); ok {
|
|
48
46
|
elemType = slice.Elem()
|
|
49
|
-
} else if array, ok := t.(*
|
|
47
|
+
} else if array, ok := t.(*gtypes.Array); ok {
|
|
50
48
|
elemType = array.Elem()
|
|
51
49
|
}
|
|
52
50
|
c.WriteGoType(elemType)
|
|
53
51
|
c.tsw.WriteLiterally("[]")
|
|
54
|
-
case *
|
|
52
|
+
case *gtypes.Map:
|
|
55
53
|
// Handle map types (map[K]V becomes Map<K, V>)
|
|
56
54
|
c.tsw.WriteLiterally("Map<")
|
|
57
55
|
c.WriteGoType(t.Key())
|
|
58
56
|
c.tsw.WriteLiterally(", ")
|
|
59
57
|
c.WriteGoType(t.Elem())
|
|
60
58
|
c.tsw.WriteLiterally(">")
|
|
61
|
-
case *
|
|
59
|
+
case *gtypes.Chan:
|
|
62
60
|
// Handle channel types (chan T becomes goscript.Channel<T>)
|
|
63
61
|
c.tsw.WriteLiterally("goscript.Channel<")
|
|
64
62
|
c.WriteGoType(t.Elem())
|
|
@@ -93,16 +91,9 @@ func NewGoToTSCompiler(tsw *TSCodeWriter, pkg *packages.Package, cmap ast.Commen
|
|
|
93
91
|
asyncFuncs: make(map[string]bool),
|
|
94
92
|
nextBlockNeedsDefer: false,
|
|
95
93
|
inAsyncFunction: false,
|
|
96
|
-
tempVarCounter: 0, // Initialize counter
|
|
97
94
|
}
|
|
98
95
|
}
|
|
99
96
|
|
|
100
|
-
// newTempVar generates a unique temporary variable name.
|
|
101
|
-
func (c *GoToTSCompiler) newTempVar() string {
|
|
102
|
-
c.tempVarCounter++
|
|
103
|
-
return fmt.Sprintf("_tempVar%d", c.tempVarCounter)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
97
|
// isAsyncFunc determines if a function is asynchronous
|
|
107
98
|
// A function is async if it contains channel operations or calls other async functions
|
|
108
99
|
func (c *GoToTSCompiler) isAsyncFunc(name string) bool {
|
|
@@ -145,7 +136,7 @@ func (c *GoToTSCompiler) containsAsyncOperations(node ast.Node) bool {
|
|
|
145
136
|
// Handles array types recursively.
|
|
146
137
|
func (c *GoToTSCompiler) WriteZeroValueForType(typ any) {
|
|
147
138
|
switch t := typ.(type) {
|
|
148
|
-
case *
|
|
139
|
+
case *gtypes.Array:
|
|
149
140
|
c.tsw.WriteLiterally("[")
|
|
150
141
|
for i := 0; i < int(t.Len()); i++ {
|
|
151
142
|
if i > 0 {
|
|
@@ -170,11 +161,11 @@ func (c *GoToTSCompiler) WriteZeroValueForType(typ any) {
|
|
|
170
161
|
c.WriteZeroValueForType(t.Elt)
|
|
171
162
|
}
|
|
172
163
|
c.tsw.WriteLiterally("]")
|
|
173
|
-
case *
|
|
164
|
+
case *gtypes.Basic:
|
|
174
165
|
switch t.Kind() {
|
|
175
|
-
case
|
|
166
|
+
case gtypes.Bool:
|
|
176
167
|
c.tsw.WriteLiterally("false")
|
|
177
|
-
case
|
|
168
|
+
case gtypes.String:
|
|
178
169
|
c.tsw.WriteLiterally(`""`)
|
|
179
170
|
default:
|
|
180
171
|
c.tsw.WriteLiterally("0")
|
package/compiler/compile_expr.go
CHANGED
|
@@ -315,13 +315,14 @@ func (c *GoToTSCompiler) WriteInterfaceType(exp *ast.InterfaceType) {
|
|
|
315
315
|
|
|
316
316
|
// If there are embedded interfaces, write the extends clause
|
|
317
317
|
if len(embeddedInterfaces) > 0 {
|
|
318
|
-
c.tsw.WriteLiterally("
|
|
318
|
+
c.tsw.WriteLiterally("extends ")
|
|
319
319
|
c.tsw.WriteLiterally(strings.Join(embeddedInterfaces, ", "))
|
|
320
|
+
c.tsw.WriteLiterally(" ")
|
|
320
321
|
}
|
|
321
322
|
|
|
322
323
|
// Write the interface body on the same line
|
|
323
|
-
c.tsw.WriteLiterally("{")
|
|
324
|
-
c.tsw.WriteLine("")
|
|
324
|
+
c.tsw.WriteLiterally("{")
|
|
325
|
+
c.tsw.WriteLine("") // Newline after opening brace
|
|
325
326
|
c.tsw.Indent(1)
|
|
326
327
|
|
|
327
328
|
// Write named methods
|
|
@@ -377,6 +378,28 @@ func (c *GoToTSCompiler) WriteFuncType(exp *ast.FuncType, isAsync bool) {
|
|
|
377
378
|
func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
378
379
|
expFun := exp.Fun
|
|
379
380
|
|
|
381
|
+
// Handle array type conversions like []rune(string)
|
|
382
|
+
if arrayType, isArrayType := expFun.(*ast.ArrayType); isArrayType {
|
|
383
|
+
// Check if it's a []rune type
|
|
384
|
+
if ident, isIdent := arrayType.Elt.(*ast.Ident); isIdent && ident.Name == "rune" {
|
|
385
|
+
// Check if the argument is a string
|
|
386
|
+
if len(exp.Args) == 1 {
|
|
387
|
+
arg := exp.Args[0]
|
|
388
|
+
if tv, ok := c.pkg.TypesInfo.Types[arg]; ok && tv.Type != nil {
|
|
389
|
+
if basic, isBasic := tv.Type.Underlying().(*gtypes.Basic); isBasic && basic.Kind() == gtypes.String {
|
|
390
|
+
// Translate []rune(stringValue) to goscript.stringToRunes(stringValue)
|
|
391
|
+
c.tsw.WriteLiterally("goscript.stringToRunes(")
|
|
392
|
+
if err := c.WriteValueExpr(arg); err != nil {
|
|
393
|
+
return fmt.Errorf("failed to write argument for []rune(string) conversion: %w", err)
|
|
394
|
+
}
|
|
395
|
+
c.tsw.WriteLiterally(")")
|
|
396
|
+
return nil // Handled []rune(string)
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
380
403
|
if funIdent, funIsIdent := expFun.(*ast.Ident); funIsIdent {
|
|
381
404
|
switch funIdent.String() {
|
|
382
405
|
case "println":
|
|
@@ -507,10 +530,18 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
507
530
|
// Fallthrough for unhandled make calls (e.g., channels)
|
|
508
531
|
return errors.New("unhandled make call")
|
|
509
532
|
case "string":
|
|
510
|
-
// Handle string() conversion
|
|
533
|
+
// Handle string() conversion
|
|
511
534
|
if len(exp.Args) == 1 {
|
|
512
|
-
// Check if the argument is a rune (int32) or a call to rune()
|
|
513
535
|
arg := exp.Args[0]
|
|
536
|
+
|
|
537
|
+
// Case 1: Argument is a string literal string("...")
|
|
538
|
+
if basicLit, isBasicLit := arg.(*ast.BasicLit); isBasicLit && basicLit.Kind == token.STRING {
|
|
539
|
+
// Translate string("...") to "..." (no-op)
|
|
540
|
+
c.WriteBasicLitValue(basicLit)
|
|
541
|
+
return nil // Handled string literal conversion
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Case 2: Argument is a rune (int32) or a call to rune()
|
|
514
545
|
innerCall, isCallExpr := arg.(*ast.CallExpr)
|
|
515
546
|
|
|
516
547
|
if isCallExpr {
|
|
@@ -540,6 +571,19 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
540
571
|
c.tsw.WriteLiterally(")")
|
|
541
572
|
return nil // Handled string(int32)
|
|
542
573
|
}
|
|
574
|
+
|
|
575
|
+
// Case 3: Argument is a slice of runes string([]rune{...})
|
|
576
|
+
if sliceType, isSlice := tv.Type.Underlying().(*gtypes.Slice); isSlice {
|
|
577
|
+
if basic, isBasic := sliceType.Elem().Underlying().(*gtypes.Basic); isBasic && basic.Kind() == gtypes.Int32 {
|
|
578
|
+
// Translate string([]rune) to goscript.runesToString(...)
|
|
579
|
+
c.tsw.WriteLiterally("goscript.runesToString(")
|
|
580
|
+
if err := c.WriteValueExpr(arg); err != nil {
|
|
581
|
+
return fmt.Errorf("failed to write argument for string([]rune) conversion: %w", err)
|
|
582
|
+
}
|
|
583
|
+
c.tsw.WriteLiterally(")")
|
|
584
|
+
return nil // Handled string([]rune)
|
|
585
|
+
}
|
|
586
|
+
}
|
|
543
587
|
}
|
|
544
588
|
}
|
|
545
589
|
// Return error for other unhandled string conversions
|
|
@@ -667,6 +711,17 @@ func (c *GoToTSCompiler) WriteBinaryExprValue(exp *ast.BinaryExpr) error {
|
|
|
667
711
|
return nil
|
|
668
712
|
}
|
|
669
713
|
|
|
714
|
+
// Check if the operator is a bitwise operator
|
|
715
|
+
isBitwise := false
|
|
716
|
+
switch exp.Op {
|
|
717
|
+
case token.AND, token.OR, token.XOR, token.SHL, token.SHR, token.AND_NOT:
|
|
718
|
+
isBitwise = true
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
if isBitwise {
|
|
722
|
+
c.tsw.WriteLiterally("(") // Add opening parenthesis for bitwise operations
|
|
723
|
+
}
|
|
724
|
+
|
|
670
725
|
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
671
726
|
return fmt.Errorf("failed to write binary expression left operand: %w", err)
|
|
672
727
|
}
|
|
@@ -682,6 +737,11 @@ func (c *GoToTSCompiler) WriteBinaryExprValue(exp *ast.BinaryExpr) error {
|
|
|
682
737
|
if err := c.WriteValueExpr(exp.Y); err != nil {
|
|
683
738
|
return fmt.Errorf("failed to write binary expression right operand: %w", err)
|
|
684
739
|
}
|
|
740
|
+
|
|
741
|
+
if isBitwise {
|
|
742
|
+
c.tsw.WriteLiterally(")") // Add closing parenthesis for bitwise operations
|
|
743
|
+
}
|
|
744
|
+
|
|
685
745
|
return nil
|
|
686
746
|
}
|
|
687
747
|
|
|
@@ -742,6 +802,16 @@ func (c *GoToTSCompiler) WriteCompositeLitValue(exp *ast.CompositeLit) error {
|
|
|
742
802
|
|
|
743
803
|
// Handle array literals
|
|
744
804
|
if arrType, isArrayType := exp.Type.(*ast.ArrayType); isArrayType {
|
|
805
|
+
// Special case: empty slice literal
|
|
806
|
+
if len(exp.Elts) == 0 {
|
|
807
|
+
// Generate: ([] as ElementType[])
|
|
808
|
+
c.tsw.WriteLiterally("([] as ")
|
|
809
|
+
// Write the element type using the existing function
|
|
810
|
+
c.WriteTypeExpr(arrType.Elt)
|
|
811
|
+
c.tsw.WriteLiterally("[])") // Close the type assertion
|
|
812
|
+
return nil // Handled empty slice literal
|
|
813
|
+
}
|
|
814
|
+
|
|
745
815
|
c.tsw.WriteLiterally("[")
|
|
746
816
|
// Use type info to get array length and element type
|
|
747
817
|
var arrayLen int
|
|
@@ -829,7 +899,7 @@ func (c *GoToTSCompiler) WriteCompositeLitValue(exp *ast.CompositeLit) error {
|
|
|
829
899
|
// Typed literal, likely a struct: new Type({...})
|
|
830
900
|
c.tsw.WriteLiterally("new ")
|
|
831
901
|
c.WriteTypeExpr(exp.Type)
|
|
832
|
-
c.tsw.WriteLiterally("({
|
|
902
|
+
c.tsw.WriteLiterally("({")
|
|
833
903
|
for i, elm := range exp.Elts {
|
|
834
904
|
if i != 0 {
|
|
835
905
|
c.tsw.WriteLiterally(", ")
|
|
@@ -838,7 +908,7 @@ func (c *GoToTSCompiler) WriteCompositeLitValue(exp *ast.CompositeLit) error {
|
|
|
838
908
|
return fmt.Errorf("failed to write struct literal field: %w", err)
|
|
839
909
|
}
|
|
840
910
|
}
|
|
841
|
-
c.tsw.WriteLiterally("
|
|
911
|
+
c.tsw.WriteLiterally("})")
|
|
842
912
|
return nil
|
|
843
913
|
}
|
|
844
914
|
}
|
|
@@ -935,7 +1005,7 @@ func (c *GoToTSCompiler) WriteFuncLitValue(exp *ast.FuncLit) error {
|
|
|
935
1005
|
}
|
|
936
1006
|
|
|
937
1007
|
c.tsw.WriteLiterally(" => ")
|
|
938
|
-
|
|
1008
|
+
|
|
939
1009
|
// Save previous async state and set current state based on isAsync
|
|
940
1010
|
previousAsyncState := c.inAsyncFunction
|
|
941
1011
|
c.inAsyncFunction = isAsync
|
|
@@ -945,7 +1015,7 @@ func (c *GoToTSCompiler) WriteFuncLitValue(exp *ast.FuncLit) error {
|
|
|
945
1015
|
c.inAsyncFunction = previousAsyncState // Restore state before returning error
|
|
946
1016
|
return fmt.Errorf("failed to write function literal body: %w", err)
|
|
947
1017
|
}
|
|
948
|
-
|
|
1018
|
+
|
|
949
1019
|
// Restore previous async state
|
|
950
1020
|
c.inAsyncFunction = previousAsyncState
|
|
951
1021
|
return nil
|
|
@@ -3,6 +3,7 @@ package compiler
|
|
|
3
3
|
import (
|
|
4
4
|
"fmt"
|
|
5
5
|
"go/ast"
|
|
6
|
+
gtypes "go/types"
|
|
6
7
|
)
|
|
7
8
|
|
|
8
9
|
// WriteFieldList writes a field list.
|
|
@@ -50,6 +51,7 @@ func (c *GoToTSCompiler) WriteField(field *ast.Field, isArguments bool) {
|
|
|
50
51
|
c.WriteDoc(field.Comment)
|
|
51
52
|
}
|
|
52
53
|
}
|
|
54
|
+
|
|
53
55
|
for _, name := range field.Names {
|
|
54
56
|
isExported := name.IsExported()
|
|
55
57
|
|
|
@@ -68,14 +70,33 @@ func (c *GoToTSCompiler) WriteField(field *ast.Field, isArguments bool) {
|
|
|
68
70
|
c.tsw.WriteLiterally(name.Name)
|
|
69
71
|
}
|
|
70
72
|
|
|
73
|
+
// Check if field type is an interface
|
|
74
|
+
isInterface := false
|
|
75
|
+
if c.pkg != nil && c.pkg.TypesInfo != nil {
|
|
76
|
+
if tv, ok := c.pkg.TypesInfo.Types[field.Type]; ok && tv.Type != nil {
|
|
77
|
+
_, isInterface = tv.Type.Underlying().(*gtypes.Interface)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
71
81
|
// write type for struct fields (not arguments)
|
|
72
82
|
c.tsw.WriteLiterally(": ")
|
|
73
83
|
c.WriteTypeExpr(field.Type) // Use WriteTypeExpr for field type
|
|
74
84
|
|
|
85
|
+
// Append "| null" for interface fields
|
|
86
|
+
if !isArguments && isInterface {
|
|
87
|
+
c.tsw.WriteLiterally(" | null")
|
|
88
|
+
}
|
|
89
|
+
|
|
75
90
|
if !isArguments {
|
|
76
91
|
// write initializer with zero value for struct fields
|
|
77
92
|
c.tsw.WriteLiterally(" = ")
|
|
78
|
-
|
|
93
|
+
|
|
94
|
+
// Special initialization for interface fields
|
|
95
|
+
if isInterface {
|
|
96
|
+
c.tsw.WriteLiterally("null")
|
|
97
|
+
} else {
|
|
98
|
+
c.WriteZeroValueForType(field.Type)
|
|
99
|
+
}
|
|
79
100
|
|
|
80
101
|
// write tag comment if any for struct fields
|
|
81
102
|
if field.Tag != nil {
|
package/compiler/compile_spec.go
CHANGED
|
@@ -4,6 +4,7 @@ import (
|
|
|
4
4
|
"fmt"
|
|
5
5
|
"go/ast"
|
|
6
6
|
"go/types"
|
|
7
|
+
gtypes "go/types"
|
|
7
8
|
"sort"
|
|
8
9
|
"strings"
|
|
9
10
|
)
|
|
@@ -147,15 +148,19 @@ func (c *GoToTSCompiler) WriteTypeSpec(a *ast.TypeSpec) error {
|
|
|
147
148
|
if err := c.WriteValueExpr(a.Name); err != nil { // Class name is a value identifier
|
|
148
149
|
return err
|
|
149
150
|
}
|
|
151
|
+
c.tsw.WriteLiterally(" ")
|
|
152
|
+
|
|
150
153
|
if embeddedExpr != nil {
|
|
151
154
|
// For pointer embedding (*T) unwrap the star so we extend T, not (T|null)
|
|
152
155
|
if star, ok := embeddedExpr.(*ast.StarExpr); ok {
|
|
153
156
|
embeddedExpr = star.X
|
|
154
157
|
}
|
|
155
|
-
c.tsw.WriteLiterally("
|
|
158
|
+
c.tsw.WriteLiterally("extends ")
|
|
156
159
|
c.WriteTypeExpr(embeddedExpr) // Embedded type name
|
|
160
|
+
c.tsw.WriteLiterally(" ")
|
|
157
161
|
}
|
|
158
|
-
|
|
162
|
+
|
|
163
|
+
c.tsw.WriteLine("{")
|
|
159
164
|
c.tsw.Indent(1)
|
|
160
165
|
|
|
161
166
|
// className is the name of the class type
|
|
@@ -282,15 +287,15 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
|
|
|
282
287
|
|
|
283
288
|
// Determine if method is async by checking for async operations in the body
|
|
284
289
|
isAsync := c.containsAsyncOperations(decl.Body)
|
|
285
|
-
|
|
290
|
+
|
|
286
291
|
// Methods are typically public in the TS output
|
|
287
292
|
c.tsw.WriteLiterally("public ")
|
|
288
|
-
|
|
293
|
+
|
|
289
294
|
// Add async modifier if needed
|
|
290
295
|
if isAsync {
|
|
291
296
|
c.tsw.WriteLiterally("async ")
|
|
292
297
|
}
|
|
293
|
-
|
|
298
|
+
|
|
294
299
|
// Keep original Go casing for method names
|
|
295
300
|
if err := c.WriteValueExpr(decl.Name); err != nil { // Method name is a value identifier
|
|
296
301
|
return err
|
|
@@ -342,7 +347,7 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
|
|
|
342
347
|
|
|
343
348
|
// Check if function body has defer statements
|
|
344
349
|
c.nextBlockNeedsDefer = c.scanForDefer(decl.Body)
|
|
345
|
-
|
|
350
|
+
|
|
346
351
|
// Save previous async state and set current state based on isAsync
|
|
347
352
|
previousAsyncState := c.inAsyncFunction
|
|
348
353
|
c.inAsyncFunction = isAsync
|
|
@@ -374,7 +379,7 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
|
|
|
374
379
|
}
|
|
375
380
|
c.tsw.Indent(-1)
|
|
376
381
|
c.tsw.WriteLine("}")
|
|
377
|
-
|
|
382
|
+
|
|
378
383
|
// Restore previous async state
|
|
379
384
|
c.inAsyncFunction = previousAsyncState
|
|
380
385
|
return nil
|
|
@@ -385,7 +390,7 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
|
|
|
385
390
|
c.inAsyncFunction = previousAsyncState // Restore state before returning error
|
|
386
391
|
return fmt.Errorf("failed to write function body: %w", err)
|
|
387
392
|
}
|
|
388
|
-
|
|
393
|
+
|
|
389
394
|
// Restore previous async state
|
|
390
395
|
c.inAsyncFunction = previousAsyncState
|
|
391
396
|
return nil
|
|
@@ -403,13 +408,30 @@ func (c *GoToTSCompiler) WriteValueSpec(a *ast.ValueSpec) error {
|
|
|
403
408
|
if len(a.Names) == 1 {
|
|
404
409
|
name := a.Names[0]
|
|
405
410
|
c.tsw.WriteLiterally(name.Name)
|
|
411
|
+
|
|
412
|
+
// Check if it's an interface type
|
|
413
|
+
isInterface := false
|
|
414
|
+
if c.pkg != nil && c.pkg.TypesInfo != nil && a.Type != nil {
|
|
415
|
+
if tv, ok := c.pkg.TypesInfo.Types[a.Type]; ok && tv.Type != nil {
|
|
416
|
+
_, isInterface = tv.Type.Underlying().(*gtypes.Interface)
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
406
420
|
if a.Type != nil {
|
|
407
421
|
c.tsw.WriteLiterally(": ")
|
|
408
422
|
c.WriteTypeExpr(a.Type) // Variable type annotation
|
|
409
423
|
|
|
424
|
+
// Append "| null" for interface types
|
|
425
|
+
if isInterface {
|
|
426
|
+
c.tsw.WriteLiterally(" | null")
|
|
427
|
+
}
|
|
428
|
+
|
|
410
429
|
// Check if it's an array type declaration without an initial value
|
|
411
430
|
if _, isArrayType := a.Type.(*ast.ArrayType); isArrayType && len(a.Values) == 0 {
|
|
412
431
|
c.tsw.WriteLiterally(" = []")
|
|
432
|
+
} else if isInterface && len(a.Values) == 0 {
|
|
433
|
+
// Interface with no initialization value, initialize to null
|
|
434
|
+
c.tsw.WriteLiterally(" = null")
|
|
413
435
|
}
|
|
414
436
|
}
|
|
415
437
|
if len(a.Values) > 0 {
|
|
@@ -473,6 +495,14 @@ func (c *GoToTSCompiler) WriteImportSpec(a *ast.ImportSpec) {
|
|
|
473
495
|
|
|
474
496
|
// WriteInterfaceMethodSignature writes a TypeScript interface method signature from a Go ast.Field.
|
|
475
497
|
func (c *GoToTSCompiler) WriteInterfaceMethodSignature(field *ast.Field) error {
|
|
498
|
+
// Include comments
|
|
499
|
+
if field.Doc != nil {
|
|
500
|
+
c.WriteDoc(field.Doc)
|
|
501
|
+
}
|
|
502
|
+
if field.Comment != nil {
|
|
503
|
+
c.WriteDoc(field.Comment)
|
|
504
|
+
}
|
|
505
|
+
|
|
476
506
|
if len(field.Names) == 0 {
|
|
477
507
|
// Should not happen for named methods in an interface, but handle defensively
|
|
478
508
|
return fmt.Errorf("interface method field has no name")
|
package/compiler/compile_stmt.go
CHANGED
|
@@ -806,43 +806,50 @@ func (c *GoToTSCompiler) writeChannelReceiveWithOk(lhs []ast.Expr, unaryExpr *as
|
|
|
806
806
|
return fmt.Errorf("unhandled LHS expression type for ok in channel receive: %T", lhs[1])
|
|
807
807
|
}
|
|
808
808
|
|
|
809
|
-
// Generate
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
c.tsw.WriteLiterally(" = await ")
|
|
814
|
-
if err := c.WriteValueExpr(unaryExpr.X); err != nil { // Channel expression
|
|
815
|
-
return fmt.Errorf("failed to write channel expression in receive: %w", err)
|
|
809
|
+
// Generate destructuring assignment/declaration for val, ok := <-channel
|
|
810
|
+
keyword := ""
|
|
811
|
+
if tok == token.DEFINE {
|
|
812
|
+
keyword = "const " // Use const for destructuring declaration
|
|
816
813
|
}
|
|
817
|
-
c.tsw.WriteLiterally(".receiveWithOk()")
|
|
818
|
-
c.tsw.WriteLine("")
|
|
819
814
|
|
|
820
|
-
//
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
815
|
+
// Build the destructuring pattern, handling blank identifiers correctly for TS
|
|
816
|
+
patternParts := []string{}
|
|
817
|
+
if !valueIsBlank {
|
|
818
|
+
// Map the 'value' field to the Go variable name
|
|
819
|
+
patternParts = append(patternParts, fmt.Sprintf("value: %s", valueName))
|
|
820
|
+
} else {
|
|
821
|
+
// If both are blank, just await the call and return
|
|
822
|
+
if okIsBlank {
|
|
823
|
+
c.tsw.WriteLiterally("await ")
|
|
824
|
+
if err := c.WriteValueExpr(unaryExpr.X); err != nil { // Channel expression
|
|
825
|
+
return fmt.Errorf("failed to write channel expression in receive: %w", err)
|
|
829
826
|
}
|
|
830
|
-
c.tsw.WriteLiterally(
|
|
831
|
-
c.tsw.WriteLiterally(" = ")
|
|
832
|
-
c.tsw.WriteLiterally(resultVar)
|
|
833
|
-
c.tsw.WriteLiterally(".")
|
|
834
|
-
c.tsw.WriteLiterally(valueSource)
|
|
827
|
+
c.tsw.WriteLiterally(".receiveWithOk()")
|
|
835
828
|
c.tsw.WriteLine("")
|
|
829
|
+
return nil // Nothing to assign
|
|
836
830
|
}
|
|
837
|
-
|
|
831
|
+
// Only value is blank, need to map 'ok' property
|
|
832
|
+
patternParts = append(patternParts, fmt.Sprintf("ok: %s", okName))
|
|
838
833
|
}
|
|
839
834
|
|
|
840
|
-
if
|
|
841
|
-
|
|
835
|
+
if !okIsBlank && !valueIsBlank { // Both are present
|
|
836
|
+
patternParts = append(patternParts, fmt.Sprintf("ok: %s", okName))
|
|
837
|
+
} else if !okIsBlank && valueIsBlank { // Only ok is present (already handled above)
|
|
838
|
+
// No need to add ok again
|
|
842
839
|
}
|
|
843
|
-
|
|
844
|
-
|
|
840
|
+
// If both are blank, patternParts remains empty, handled earlier.
|
|
841
|
+
|
|
842
|
+
destructuringPattern := fmt.Sprintf("{ %s }", strings.Join(patternParts, ", "))
|
|
843
|
+
|
|
844
|
+
// Write the destructuring assignment/declaration
|
|
845
|
+
c.tsw.WriteLiterally(keyword) // "const " or ""
|
|
846
|
+
c.tsw.WriteLiterally(destructuringPattern)
|
|
847
|
+
c.tsw.WriteLiterally(" = await ")
|
|
848
|
+
if err := c.WriteValueExpr(unaryExpr.X); err != nil { // Channel expression
|
|
849
|
+
return fmt.Errorf("failed to write channel expression in receive: %w", err)
|
|
845
850
|
}
|
|
851
|
+
c.tsw.WriteLiterally(".receiveWithOk()")
|
|
852
|
+
c.tsw.WriteLine("")
|
|
846
853
|
|
|
847
854
|
return nil
|
|
848
855
|
}
|
package/compiler/compiler.go
CHANGED
|
@@ -29,6 +29,7 @@ func NewCompiler(conf *Config, le *logrus.Entry, opts *packages.Config) (*Compil
|
|
|
29
29
|
opts.Tests = false
|
|
30
30
|
opts.Env = append(opts.Env, "GOOS=js", "GOARCH=wasm")
|
|
31
31
|
opts.Dir = conf.Dir
|
|
32
|
+
opts.BuildFlags = conf.BuildFlags
|
|
32
33
|
|
|
33
34
|
// NeedName adds Name and PkgPath.
|
|
34
35
|
// NeedFiles adds GoFiles and OtherFiles.
|
package/compiler/config.go
CHANGED
|
@@ -69,7 +69,7 @@ func (c *FileCompiler) Compile(ctx context.Context) error {
|
|
|
69
69
|
goWriter := NewGoToTSCompiler(c.codeWriter, c.pkg, cmap)
|
|
70
70
|
|
|
71
71
|
// Add import for the goscript runtime using namespace import and alias
|
|
72
|
-
c.codeWriter.WriteLine("import * as goscript from \"@
|
|
72
|
+
c.codeWriter.WriteLine("import * as goscript from \"@goscript/builtin\";")
|
|
73
73
|
c.codeWriter.WriteLine("") // Add a newline after the import
|
|
74
74
|
|
|
75
75
|
if err := goWriter.WriteDecls(f.Decls); err != nil {
|