goscript 0.0.11 → 0.0.14
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/LICENSE +2 -1
- package/README.md +13 -9
- package/builtin/builtin.go +11 -0
- package/builtin/builtin.ts +11 -1
- package/cmd/goscript/cmd_compile.go +22 -4
- package/compiler/compile.go +16 -25
- package/compiler/compile_expr.go +80 -10
- package/compiler/compile_field.go +22 -1
- package/compiler/compile_spec.go +38 -8
- package/compiler/compile_stmt.go +36 -29
- 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/index.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 +10 -1
- package/dist/builtin/builtin.js.map +1 -1
- package/dist/compiler/index.d.ts +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/go.mod +1 -1
- package/package.json +18 -3
package/LICENSE
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) 2024-2025 Aperture Robotics, LLC.
|
|
4
|
+
Copyright (c) 2019-2025 Christian Stewart
|
|
4
5
|
|
|
5
6
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
7
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
[![GoDoc Widget]][GoDoc] [![Go Report Card Widget]][Go Report Card]
|
|
4
4
|
|
|
5
|
-
[GoDoc]: https://godoc.org/github.com/
|
|
6
|
-
[GoDoc Widget]: https://godoc.org/github.com/
|
|
7
|
-
[Go Report Card Widget]: https://goreportcard.com/badge/github.com/
|
|
8
|
-
[Go Report Card]: https://goreportcard.com/report/github.com/
|
|
5
|
+
[GoDoc]: https://godoc.org/github.com/aperturerobotics/goscript
|
|
6
|
+
[GoDoc Widget]: https://godoc.org/github.com/aperturerobotics/goscript?status.svg
|
|
7
|
+
[Go Report Card Widget]: https://goreportcard.com/badge/github.com/aperturerobotics/goscript
|
|
8
|
+
[Go Report Card]: https://goreportcard.com/report/github.com/aperturerobotics/goscript
|
|
9
9
|
|
|
10
10
|
## Introduction
|
|
11
11
|
|
|
@@ -15,6 +15,10 @@ It works on translating Go to TypeScript on the AST level.
|
|
|
15
15
|
|
|
16
16
|
For detailed information on the compiler's design, refer to the [design document](./design/DESIGN.md).
|
|
17
17
|
|
|
18
|
+
> Right now goscript looks pretty cool if you problem is "I want this self-sufficient algorithm be available in Go and JS runtimes". gopherjs's ambition, however, has always been "any valid Go program can run in a browser". There is a lot that goes on in gopherjs that is necessary for supporting the standard library, which goes beyond cross-language translation.
|
|
19
|
+
>
|
|
20
|
+
> — [nevkontakte](https://gophers.slack.com/archives/C039C0R2T/p1745870396945719), developer of [GopherJS](https://github.com/gopherjs/gopherjs)
|
|
21
|
+
|
|
18
22
|
GoScript intends to compile a subset of the Go language to TypeScript in a
|
|
19
23
|
non-spec-compliant way. It is intended only for bringing over high-level logic
|
|
20
24
|
from Go to TypeScript, so not all programs will work correctly:
|
|
@@ -63,7 +67,7 @@ import (
|
|
|
63
67
|
"context"
|
|
64
68
|
"log"
|
|
65
69
|
|
|
66
|
-
"github.com/
|
|
70
|
+
"github.com/aperturerobotics/goscript/compiler"
|
|
67
71
|
"github.com/sirupsen/logrus"
|
|
68
72
|
)
|
|
69
73
|
|
|
@@ -306,7 +310,7 @@ func main() {
|
|
|
306
310
|
Generated with `goscript compile .`:
|
|
307
311
|
|
|
308
312
|
```typescript
|
|
309
|
-
import * as goscript from "@
|
|
313
|
+
import * as goscript from "@goscript/builtin"
|
|
310
314
|
|
|
311
315
|
class MyStruct {
|
|
312
316
|
// MyInt is a public integer field, initialized to zero.
|
|
@@ -460,10 +464,10 @@ export async function main(): Promise<void> {
|
|
|
460
464
|
|
|
461
465
|
Code is compiled with `GOARCH=js` and uses a 32-bit environment similar to wasm.
|
|
462
466
|
|
|
463
|
-
All Go import paths are prefixed with `@
|
|
467
|
+
All Go import paths are prefixed with `@goscript/` and can be imported in TypeScript:
|
|
464
468
|
|
|
465
469
|
```typescript
|
|
466
|
-
import { MyAsyncFunction, MyStruct } from '@
|
|
470
|
+
import { MyAsyncFunction, MyStruct } from '@goscript/github.com/myorg/mypackage';
|
|
467
471
|
|
|
468
472
|
// Example of importing and using a compiled Go async function
|
|
469
473
|
async function runGoCode() {
|
|
@@ -480,4 +484,4 @@ async function runGoCode() {
|
|
|
480
484
|
|
|
481
485
|
## License
|
|
482
486
|
|
|
483
|
-
MIT
|
|
487
|
+
MIT
|
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.
|
|
@@ -262,7 +271,7 @@ export function typeAssert<T>(
|
|
|
262
271
|
}
|
|
263
272
|
break
|
|
264
273
|
|
|
265
|
-
case TypeKind.Basic:
|
|
274
|
+
case TypeKind.Basic: {
|
|
266
275
|
// For basic types, check if the value matches the expected JavaScript type
|
|
267
276
|
// This is a simple check for common basic types
|
|
268
277
|
const basicType = typeof value
|
|
@@ -274,6 +283,7 @@ export function typeAssert<T>(
|
|
|
274
283
|
return { value: value as T, ok: true }
|
|
275
284
|
}
|
|
276
285
|
break
|
|
286
|
+
}
|
|
277
287
|
|
|
278
288
|
case TypeKind.Pointer:
|
|
279
289
|
// For pointers, check if value is not null or undefined
|
|
@@ -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
|
-
"github.com/
|
|
8
|
+
"github.com/aperturerobotics/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/aperturerobotics/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,9 +4,9 @@ import (
|
|
|
4
4
|
"fmt"
|
|
5
5
|
"go/ast"
|
|
6
6
|
"go/token"
|
|
7
|
-
"go/types"
|
|
7
|
+
gtypes "go/types"
|
|
8
8
|
|
|
9
|
-
gstypes "github.com/
|
|
9
|
+
gstypes "github.com/aperturerobotics/goscript/compiler/types"
|
|
10
10
|
"golang.org/x/tools/go/packages"
|
|
11
11
|
)
|
|
12
12
|
|
|
@@ -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
|
@@ -9,7 +9,7 @@ import (
|
|
|
9
9
|
"strconv"
|
|
10
10
|
"strings"
|
|
11
11
|
|
|
12
|
-
gstypes "github.com/
|
|
12
|
+
gstypes "github.com/aperturerobotics/goscript/compiler/types"
|
|
13
13
|
)
|
|
14
14
|
|
|
15
15
|
// WriteTypeExpr writes an expression that represents a type.
|
|
@@ -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")
|