goscript 0.0.2
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/.aider-prompt +11 -0
- package/LICENSE +21 -0
- package/README.md +427 -0
- package/builtin/builtin.ts +507 -0
- package/cmd/goscript/cmd_compile.go +59 -0
- package/cmd/goscript/main.go +23 -0
- package/compiler/compile.go +183 -0
- package/compiler/compile_comment.go +41 -0
- package/compiler/compile_decls.go +72 -0
- package/compiler/compile_expr.go +831 -0
- package/compiler/compile_field.go +89 -0
- package/compiler/compile_spec.go +256 -0
- package/compiler/compile_stmt.go +1509 -0
- package/compiler/compiler.go +81 -0
- package/compiler/config.go +32 -0
- package/compiler/context.go +9 -0
- package/compiler/file_compiler.go +80 -0
- package/compiler/output_path.go +31 -0
- package/compiler/pkg_compiler.go +73 -0
- package/compiler/writer.go +90 -0
- package/compliance/COMPLIANCE.md +133 -0
- package/compliance/compliance.go +313 -0
- package/compliance/compliance_test.go +57 -0
- package/compliance/tests/array_literal/array_literal.go +15 -0
- package/compliance/tests/array_literal/array_literal.gs.ts +19 -0
- package/compliance/tests/array_literal/expected.log +3 -0
- package/compliance/tests/async_basic/async_basic.go +26 -0
- package/compliance/tests/async_basic/async_basic.gs.ts +30 -0
- package/compliance/tests/async_basic/expected.log +1 -0
- package/compliance/tests/basic_arithmetic/basic_arithmetic.go +15 -0
- package/compliance/tests/basic_arithmetic/basic_arithmetic.gs.ts +19 -0
- package/compliance/tests/basic_arithmetic/expected.log +5 -0
- package/compliance/tests/boolean_logic/boolean_logic.go +13 -0
- package/compliance/tests/boolean_logic/boolean_logic.gs.ts +17 -0
- package/compliance/tests/boolean_logic/expected.log +3 -0
- package/compliance/tests/channel_basic/channel_basic.go +12 -0
- package/compliance/tests/channel_basic/channel_basic.gs.ts +18 -0
- package/compliance/tests/channel_basic/expected.log +1 -0
- package/compliance/tests/composite_literal_assignment/composite_literal_assignment.go +20 -0
- package/compliance/tests/composite_literal_assignment/composite_literal_assignment.gs.ts +27 -0
- package/compliance/tests/composite_literal_assignment/expected.log +2 -0
- package/compliance/tests/constants/constants.go +18 -0
- package/compliance/tests/constants/constants.gs.ts +22 -0
- package/compliance/tests/constants/expected.log +3 -0
- package/compliance/tests/copy_independence/copy_independence.go +29 -0
- package/compliance/tests/copy_independence/copy_independence.gs.ts +36 -0
- package/compliance/tests/copy_independence/expected.log +4 -0
- package/compliance/tests/float64/expected.log +6 -0
- package/compliance/tests/float64/float64.go +28 -0
- package/compliance/tests/float64/float64.gs.ts +32 -0
- package/compliance/tests/for_loop_basic/expected.log +5 -0
- package/compliance/tests/for_loop_basic/for_loop_basic.go +9 -0
- package/compliance/tests/for_loop_basic/for_loop_basic.gs.ts +13 -0
- package/compliance/tests/for_loop_condition_only/expected.log +5 -0
- package/compliance/tests/for_loop_condition_only/main.go +9 -0
- package/compliance/tests/for_loop_condition_only/main.gs.ts +13 -0
- package/compliance/tests/for_range/expected.log +9 -0
- package/compliance/tests/for_range/for_range.go +26 -0
- package/compliance/tests/for_range/for_range.gs.ts +45 -0
- package/compliance/tests/for_range_index_use/expected.log +6 -0
- package/compliance/tests/for_range_index_use/for_range_index_use.go +11 -0
- package/compliance/tests/for_range_index_use/for_range_index_use.gs.ts +18 -0
- package/compliance/tests/func_literal/expected.log +1 -0
- package/compliance/tests/func_literal/func_literal.go +10 -0
- package/compliance/tests/func_literal/func_literal.gs.ts +15 -0
- package/compliance/tests/function_call_result_assignment/expected.log +2 -0
- package/compliance/tests/function_call_result_assignment/function_call_result_assignment.go +24 -0
- package/compliance/tests/function_call_result_assignment/function_call_result_assignment.gs.ts +31 -0
- package/compliance/tests/if_statement/expected.log +1 -0
- package/compliance/tests/if_statement/if_statement.go +11 -0
- package/compliance/tests/if_statement/if_statement.gs.ts +15 -0
- package/compliance/tests/interface_to_interface_type_assertion/expected.log +1 -0
- package/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.go +30 -0
- package/compliance/tests/interface_to_interface_type_assertion/interface_to_interface_type_assertion.gs.ts +41 -0
- package/compliance/tests/interface_type_assertion/expected.log +1 -0
- package/compliance/tests/interface_type_assertion/interface_type_assertion.go +26 -0
- package/compliance/tests/interface_type_assertion/interface_type_assertion.gs.ts +36 -0
- package/compliance/tests/map_support/expected.log +13 -0
- package/compliance/tests/map_support/map_support.go +89 -0
- package/compliance/tests/map_support/map_support.gs.ts +102 -0
- package/compliance/tests/method_call_on_pointer_receiver/expected.log +1 -0
- package/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.go +19 -0
- package/compliance/tests/method_call_on_pointer_receiver/method_call_on_pointer_receiver.gs.ts +27 -0
- package/compliance/tests/method_call_on_pointer_via_value/expected.log +1 -0
- package/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.go +29 -0
- package/compliance/tests/method_call_on_pointer_via_value/method_call_on_pointer_via_value.gs.ts +38 -0
- package/compliance/tests/method_call_on_value_receiver/expected.log +1 -0
- package/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.go +16 -0
- package/compliance/tests/method_call_on_value_receiver/method_call_on_value_receiver.gs.ts +24 -0
- package/compliance/tests/method_call_on_value_via_pointer/expected.log +2 -0
- package/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.go +30 -0
- package/compliance/tests/method_call_on_value_via_pointer/method_call_on_value_via_pointer.gs.ts +38 -0
- package/compliance/tests/multiple_return_values/expected.log +6 -0
- package/compliance/tests/multiple_return_values/multiple_return_values.go +19 -0
- package/compliance/tests/multiple_return_values/multiple_return_values.gs.ts +23 -0
- package/compliance/tests/pointer_assignment_no_copy/expected.log +2 -0
- package/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.go +28 -0
- package/compliance/tests/pointer_assignment_no_copy/pointer_assignment_no_copy.gs.ts +35 -0
- package/compliance/tests/pointer_composite_literal_assignment/expected.log +3 -0
- package/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.go +23 -0
- package/compliance/tests/pointer_composite_literal_assignment/pointer_composite_literal_assignment.gs.ts +30 -0
- package/compliance/tests/pointer_deref_multiassign/expected.log +0 -0
- package/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.go +17 -0
- package/compliance/tests/pointer_deref_multiassign/pointer_deref_multiassign.gs.ts +27 -0
- package/compliance/tests/pointer_initialization/expected.log +1 -0
- package/compliance/tests/pointer_initialization/pointer_initialization.go +16 -0
- package/compliance/tests/pointer_initialization/pointer_initialization.gs.ts +22 -0
- package/compliance/tests/select_receive_on_closed_channel_no_default/expected.log +1 -0
- package/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.go +15 -0
- package/compliance/tests/select_receive_on_closed_channel_no_default/select_receive_on_closed_channel_no_default.gs.ts +31 -0
- package/compliance/tests/select_send_on_full_buffered_channel_with_default/expected.log +1 -0
- package/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.go +13 -0
- package/compliance/tests/select_send_on_full_buffered_channel_with_default/select_send_on_full_buffered_channel_with_default.gs.ts +35 -0
- package/compliance/tests/select_statement/expected.log +9 -0
- package/compliance/tests/select_statement/select_statement.go +109 -0
- package/compliance/tests/select_statement/select_statement.gs.ts +239 -0
- package/compliance/tests/simple/expected.log +1 -0
- package/compliance/tests/simple/simple.go +5 -0
- package/compliance/tests/simple/simple.gs.ts +9 -0
- package/compliance/tests/simple_deref_assignment/expected.log +2 -0
- package/compliance/tests/simple_deref_assignment/simple_deref_assignment.go +19 -0
- package/compliance/tests/simple_deref_assignment/simple_deref_assignment.gs.ts +26 -0
- package/compliance/tests/slices/expected.log +7 -0
- package/compliance/tests/slices/slices.go +22 -0
- package/compliance/tests/slices/slices.gs.ts +26 -0
- package/compliance/tests/string_rune_conversion/expected.log +3 -0
- package/compliance/tests/string_rune_conversion/string_rune_conversion.go +16 -0
- package/compliance/tests/string_rune_conversion/string_rune_conversion.gs.ts +22 -0
- package/compliance/tests/struct_field_access/expected.log +2 -0
- package/compliance/tests/struct_field_access/struct_field_access.go +13 -0
- package/compliance/tests/struct_field_access/struct_field_access.gs.ts +20 -0
- package/compliance/tests/struct_value_init_clone/expected.log +5 -0
- package/compliance/tests/struct_value_init_clone/struct_value_init_clone.go +28 -0
- package/compliance/tests/struct_value_init_clone/struct_value_init_clone.gs.ts +35 -0
- package/compliance/tests/switch_statement/expected.log +14 -0
- package/compliance/tests/switch_statement/switch_statement.go +59 -0
- package/compliance/tests/switch_statement/switch_statement.gs.ts +85 -0
- package/compliance/tests/value_type_copy_behavior/expected.log +3 -0
- package/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.go +25 -0
- package/compliance/tests/value_type_copy_behavior/value_type_copy_behavior.gs.ts +34 -0
- package/design/DESIGN.md +599 -0
- package/example/simple/build.bash +10 -0
- package/example/simple/go.mod +23 -0
- package/example/simple/go.sum +39 -0
- package/example/simple/main.go +138 -0
- package/example/simple/main.gs.ts +133 -0
- package/example/simple/main.ts +3 -0
- package/example/simple/main_test.go +59 -0
- package/example/simple/main_tools.go +5 -0
- package/example/simple/package.json +7 -0
- package/example/simple/run.bash +6 -0
- package/example/simple/tsconfig.json +28 -0
- package/example/simple/yarn.lock +8 -0
- package/go.mod +22 -0
- package/go.sum +39 -0
- package/output/output.go +10 -0
- package/package.json +14 -0
- package/tsconfig.json +10 -0
- package/types/tokens.go +65 -0
- package/types/types.go +46 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
package compliance
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bytes"
|
|
5
|
+
"context"
|
|
6
|
+
"fmt" // Added for Sprintf
|
|
7
|
+
"io"
|
|
8
|
+
"os"
|
|
9
|
+
"os/exec"
|
|
10
|
+
"path/filepath"
|
|
11
|
+
"strings"
|
|
12
|
+
"testing"
|
|
13
|
+
|
|
14
|
+
"github.com/paralin/goscript/compiler"
|
|
15
|
+
"github.com/sirupsen/logrus"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
// TestCase defines a single Go-to-TypeScript compliance test.
|
|
19
|
+
type TestCase struct {
|
|
20
|
+
Name string
|
|
21
|
+
GoSource string
|
|
22
|
+
ExpectedOutput string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// PrepareTempTestDir creates a temp dir, copies .go files, and writes go.mod. Returns tempDir path.
|
|
26
|
+
func PrepareTempTestDir(t *testing.T, testDir string) string {
|
|
27
|
+
t.Helper()
|
|
28
|
+
tempDir, err := os.MkdirTemp("", "goscript-test-")
|
|
29
|
+
if err != nil {
|
|
30
|
+
t.Fatalf("failed to create temp dir: %v", err)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
goModPath := filepath.Join(tempDir, "go.mod")
|
|
34
|
+
goModContent := []byte("module tempmod\n\ngo 1.24\n")
|
|
35
|
+
if err := os.WriteFile(goModPath, goModContent, 0o644); err != nil {
|
|
36
|
+
os.RemoveAll(tempDir) //nolint:errcheck
|
|
37
|
+
t.Fatalf("failed to write go.mod: %v", err)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
goFiles, err := filepath.Glob(filepath.Join(testDir, "*.go"))
|
|
41
|
+
if err != nil || len(goFiles) == 0 {
|
|
42
|
+
os.RemoveAll(tempDir) //nolint:errcheck
|
|
43
|
+
t.Fatalf("no .go files found in %s", testDir)
|
|
44
|
+
}
|
|
45
|
+
for _, src := range goFiles {
|
|
46
|
+
base := filepath.Base(src)
|
|
47
|
+
dst := filepath.Join(tempDir, base)
|
|
48
|
+
data, err := os.ReadFile(src)
|
|
49
|
+
if err != nil {
|
|
50
|
+
os.RemoveAll(tempDir) //nolint:errcheck
|
|
51
|
+
t.Fatalf("failed to read %s: %v", src, err)
|
|
52
|
+
}
|
|
53
|
+
if err := os.WriteFile(dst, data, 0o644); err != nil {
|
|
54
|
+
os.RemoveAll(tempDir) //nolint:errcheck
|
|
55
|
+
t.Fatalf("failed to write %s: %v", dst, err)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return tempDir
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ReadExpectedLog reads expected.log from testDir.
|
|
62
|
+
func ReadExpectedLog(t *testing.T, testDir string) string {
|
|
63
|
+
t.Helper()
|
|
64
|
+
expectedLogPath := filepath.Join(testDir, "expected.log")
|
|
65
|
+
expected, err := os.ReadFile(expectedLogPath)
|
|
66
|
+
if err != nil {
|
|
67
|
+
t.Fatalf("failed to read expected.log in %s: %v", testDir, err)
|
|
68
|
+
}
|
|
69
|
+
return string(expected)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
func CompileGoToTypeScript(t *testing.T, testDir, tempDir, outputDir string, le *logrus.Entry) {
|
|
73
|
+
t.Helper()
|
|
74
|
+
conf := &compiler.Config{
|
|
75
|
+
Dir: tempDir,
|
|
76
|
+
OutputPathRoot: outputDir,
|
|
77
|
+
}
|
|
78
|
+
if err := conf.Validate(); err != nil {
|
|
79
|
+
t.Fatalf("invalid compiler config: %v", err)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Log each .go file and its mapped .gs.ts output file and contents
|
|
83
|
+
goFiles, err := filepath.Glob(filepath.Join(tempDir, "*.go"))
|
|
84
|
+
if err != nil || len(goFiles) == 0 {
|
|
85
|
+
t.Fatalf("no .go files found in %s: %v", tempDir, err)
|
|
86
|
+
}
|
|
87
|
+
for _, src := range goFiles {
|
|
88
|
+
base := filepath.Base(src)
|
|
89
|
+
out := filepath.Join(outputDir, compiler.TranslateGoFilePathToTypescriptFilePath("tempmod", base))
|
|
90
|
+
t.Logf("Compiling Go file: %s => %s", src, out)
|
|
91
|
+
if data, err := os.ReadFile(src); err == nil {
|
|
92
|
+
t.Logf("Source %s:\n%s", src, string(data))
|
|
93
|
+
} else {
|
|
94
|
+
t.Logf("could not read source %s: %v", src, err)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
comp, err := compiler.NewCompiler(conf, le, nil)
|
|
99
|
+
if err != nil {
|
|
100
|
+
t.Fatalf("failed to create compiler: %v", err)
|
|
101
|
+
}
|
|
102
|
+
cmpErr := comp.CompilePackages(context.Background(), ".")
|
|
103
|
+
if cmpErr != nil {
|
|
104
|
+
t.Errorf("compilation failed: %v", err)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Log generated TypeScript files and copy them back to testDir
|
|
108
|
+
if err := filepath.WalkDir(outputDir, func(path string, d os.DirEntry, err error) error {
|
|
109
|
+
if err != nil {
|
|
110
|
+
t.Logf("error walking path %s: %v", path, err)
|
|
111
|
+
return nil
|
|
112
|
+
}
|
|
113
|
+
if strings.HasSuffix(path, ".gs.ts") { // Look specifically for .gs.ts files
|
|
114
|
+
// Determine the destination path in the original testDir
|
|
115
|
+
relPath, err := filepath.Rel(outputDir, path)
|
|
116
|
+
if err != nil {
|
|
117
|
+
t.Logf("failed to get relative path for %s: %v", path, err)
|
|
118
|
+
return nil // Continue walking
|
|
119
|
+
}
|
|
120
|
+
// relPath is like "@go/tempmod/file.gs.ts", so extract the base file name
|
|
121
|
+
parts := strings.Split(relPath, string(filepath.Separator))
|
|
122
|
+
if len(parts) < 3 {
|
|
123
|
+
t.Logf("unexpected path structure for %s", path)
|
|
124
|
+
return nil // Continue walking
|
|
125
|
+
}
|
|
126
|
+
fileName := parts[len(parts)-1]
|
|
127
|
+
destPath := filepath.Join(testDir, fileName)
|
|
128
|
+
|
|
129
|
+
// Read the generated content
|
|
130
|
+
generatedContent, err := os.ReadFile(path)
|
|
131
|
+
if err != nil {
|
|
132
|
+
t.Logf("could not read generated file %s: %v", path, err)
|
|
133
|
+
return nil // Continue walking
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Determine the original Go file name
|
|
137
|
+
goFileName := strings.TrimSuffix(fileName, ".gs.ts") + ".go"
|
|
138
|
+
|
|
139
|
+
// Construct the comment
|
|
140
|
+
comment := fmt.Sprintf("// Generated file based on %s\n// Updated when compliance tests are re-run, DO NOT EDIT!\n\n", goFileName)
|
|
141
|
+
|
|
142
|
+
// Prepend the comment to the generated content
|
|
143
|
+
finalContent := append([]byte(comment), generatedContent...)
|
|
144
|
+
|
|
145
|
+
if err := os.WriteFile(destPath, finalContent, 0o644); err != nil {
|
|
146
|
+
t.Logf("failed to write file %s: %v", destPath, err)
|
|
147
|
+
return err
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
t.Logf("generated content written to %s:\n%s", destPath, string(finalContent))
|
|
151
|
+
}
|
|
152
|
+
return nil
|
|
153
|
+
}); err != nil {
|
|
154
|
+
t.Fatalf("error while walking: %v", err.Error())
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if cmpErr != nil {
|
|
158
|
+
t.Fatalf("compilation failed: %v", cmpErr)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// WriteTypeScriptRunner writes a runner.ts file to tempDir.
|
|
163
|
+
func WriteTypeScriptRunner(t *testing.T, tempDir string) string {
|
|
164
|
+
t.Helper()
|
|
165
|
+
|
|
166
|
+
// Find the Go source file in the temp directory
|
|
167
|
+
goFiles, err := filepath.Glob(filepath.Join(tempDir, "*.go"))
|
|
168
|
+
if err != nil || len(goFiles) == 0 {
|
|
169
|
+
t.Fatalf("could not find Go source file in temp dir %s: %v", tempDir, err)
|
|
170
|
+
}
|
|
171
|
+
if len(goFiles) > 1 {
|
|
172
|
+
// For simplicity, assume only one relevant Go file per test case for now.
|
|
173
|
+
t.Logf("warning: found multiple Go files in %s, using the first one: %s", tempDir, goFiles[0])
|
|
174
|
+
}
|
|
175
|
+
goSourceBase := filepath.Base(goFiles[0])
|
|
176
|
+
tsFileName := strings.TrimSuffix(goSourceBase, ".go") + ".gs.ts"
|
|
177
|
+
tsImportPath := fmt.Sprintf("./output/@go/tempmod/%s", tsFileName)
|
|
178
|
+
|
|
179
|
+
tsRunner := filepath.Join(tempDir, "runner.ts")
|
|
180
|
+
// Import the goscript runtime and the main function from the compiled code
|
|
181
|
+
runnerContent := fmt.Sprintf("import { goscript } from \"./goscript\";\nimport { main } from %q;\nmain();\n", tsImportPath) // Use dynamic path
|
|
182
|
+
if err := os.WriteFile(tsRunner, []byte(runnerContent), 0o644); err != nil {
|
|
183
|
+
t.Fatalf("failed to write runner: %v", err)
|
|
184
|
+
}
|
|
185
|
+
return tsRunner
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// RunTypeScriptRunner runs the runner.ts file using tsx and returns its stdout.
|
|
189
|
+
func RunTypeScriptRunner(t *testing.T, workspaceDir, tempDir, tsRunner string) string {
|
|
190
|
+
t.Helper()
|
|
191
|
+
cmd := exec.Command("tsx", tsRunner)
|
|
192
|
+
cmd.Dir = tempDir
|
|
193
|
+
|
|
194
|
+
// Prepend node_modules/.bin to PATH
|
|
195
|
+
nodeBinDir := filepath.Join(workspaceDir, "node_modules", ".bin")
|
|
196
|
+
currentPath := os.Getenv("PATH")
|
|
197
|
+
newPath := fmt.Sprintf("%s%c%s", nodeBinDir, os.PathListSeparator, currentPath)
|
|
198
|
+
cmd.Env = append(os.Environ(), "PATH="+newPath) // Set the modified PATH
|
|
199
|
+
|
|
200
|
+
var outBuf, errBuf bytes.Buffer
|
|
201
|
+
cmd.Stdout = io.MultiWriter(&outBuf, os.Stderr)
|
|
202
|
+
cmd.Stderr = os.Stderr // Keep stderr going to the test output for debugging
|
|
203
|
+
if err := cmd.Run(); err != nil {
|
|
204
|
+
t.Fatalf("run failed: %v\nstderr: %s", err, errBuf.String())
|
|
205
|
+
}
|
|
206
|
+
return outBuf.String()
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// copyFile copies a file from src to dst.
|
|
210
|
+
func copyFile(src, dst string) error {
|
|
211
|
+
sourceFile, err := os.Open(src)
|
|
212
|
+
if err != nil {
|
|
213
|
+
return fmt.Errorf("failed to open source file %s: %w", src, err)
|
|
214
|
+
}
|
|
215
|
+
defer sourceFile.Close() //nolint:errcheck
|
|
216
|
+
|
|
217
|
+
// Ensure destination directory exists
|
|
218
|
+
destDir := filepath.Dir(dst)
|
|
219
|
+
if err := os.MkdirAll(destDir, 0o755); err != nil {
|
|
220
|
+
return fmt.Errorf("failed to create destination directory %s: %w", destDir, err)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
destFile, err := os.Create(dst)
|
|
224
|
+
if err != nil {
|
|
225
|
+
return fmt.Errorf("failed to create destination file %s: %w", dst, err)
|
|
226
|
+
}
|
|
227
|
+
defer destFile.Close() //nolint:errcheck
|
|
228
|
+
|
|
229
|
+
if _, err := io.Copy(destFile, sourceFile); err != nil {
|
|
230
|
+
return fmt.Errorf("failed to copy file content from %s to %s: %w", src, dst, err)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Ensure data is synced to disk
|
|
234
|
+
if err := destFile.Sync(); err != nil {
|
|
235
|
+
return fmt.Errorf("failed to sync destination file %s: %w", dst, err)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return nil
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// RunGoScriptTestDir compiles all .go files in testDir, runs the generated TypeScript, and compares output to expected.log.
|
|
242
|
+
func RunGoScriptTestDir(t *testing.T, workspaceDir, testDir string) {
|
|
243
|
+
t.Helper()
|
|
244
|
+
|
|
245
|
+
// Check for expect-fail file
|
|
246
|
+
expectFailPath := filepath.Join(testDir, "expect-fail")
|
|
247
|
+
if _, err := os.Stat(expectFailPath); err == nil {
|
|
248
|
+
t.Skipf("Skipping test %s: expect-fail file found", filepath.Base(testDir))
|
|
249
|
+
return // Skip the test
|
|
250
|
+
} else if !os.IsNotExist(err) {
|
|
251
|
+
// If there was an error other than "not exists", fail the test
|
|
252
|
+
t.Fatalf("failed to check for expect-fail file in %s: %v", testDir, err)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
log := logrus.New()
|
|
256
|
+
log.SetLevel(logrus.DebugLevel)
|
|
257
|
+
le := logrus.NewEntry(log)
|
|
258
|
+
|
|
259
|
+
tempDir := PrepareTempTestDir(t, testDir)
|
|
260
|
+
defer os.RemoveAll(tempDir)
|
|
261
|
+
|
|
262
|
+
// Create tsconfig.json in the temporary directory for path aliases
|
|
263
|
+
builtinTsPath := filepath.Join(workspaceDir, "builtin", "builtin.ts") // Use passed workspaceDir
|
|
264
|
+
// Ensure the path uses forward slashes for JSON compatibility, even on Windows
|
|
265
|
+
builtinTsPathForJSON := filepath.ToSlash(builtinTsPath)
|
|
266
|
+
tsconfigContent := fmt.Sprintf(`{
|
|
267
|
+
"compilerOptions": {
|
|
268
|
+
"baseUrl": ".",
|
|
269
|
+
"paths": {
|
|
270
|
+
"@go/builtin": ["%s"]
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}`, builtinTsPathForJSON) // Use dynamic path
|
|
274
|
+
tsconfigPath := filepath.Join(tempDir, "tsconfig.json")
|
|
275
|
+
if err := os.WriteFile(tsconfigPath, []byte(tsconfigContent), 0o644); err != nil {
|
|
276
|
+
t.Fatalf("failed to write tsconfig.json to temp dir: %v", err)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
outputDir := filepath.Join(tempDir, "output")
|
|
280
|
+
CompileGoToTypeScript(t, testDir, tempDir, outputDir, le) // Pass testDir to enable copying output files back to the test directory
|
|
281
|
+
|
|
282
|
+
// Copy the goscript runtime file to the temp directory
|
|
283
|
+
// Use absolute path to avoid issues with changing working directories
|
|
284
|
+
runtimeSrc := filepath.Join(workspaceDir, "builtin", "builtin.ts") // Use passed workspaceDir
|
|
285
|
+
runtimeDst := filepath.Join(tempDir, "builtin.ts") // Rename to builtin.ts in temp dir
|
|
286
|
+
if err := copyFile(runtimeSrc, runtimeDst); err != nil {
|
|
287
|
+
t.Fatalf("failed to copy goscript runtime file: %v", err)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
tsRunner := WriteTypeScriptRunner(t, tempDir)
|
|
291
|
+
actual := strings.TrimSpace(RunTypeScriptRunner(t, workspaceDir, tempDir, tsRunner)) // Pass workspaceDir
|
|
292
|
+
|
|
293
|
+
expectedLogPath := filepath.Join(testDir, "expected.log")
|
|
294
|
+
expected, err := os.ReadFile(expectedLogPath)
|
|
295
|
+
if os.IsNotExist(err) {
|
|
296
|
+
// If expected.log doesn't exist, write the actual output to it
|
|
297
|
+
t.Logf("expected.log not found, writing actual output to %s", expectedLogPath)
|
|
298
|
+
if writeErr := os.WriteFile(expectedLogPath, []byte(actual), 0o644); writeErr != nil {
|
|
299
|
+
t.Fatalf("failed to write expected.log: %v", writeErr)
|
|
300
|
+
}
|
|
301
|
+
// Test passes on the first run if expected.log is generated
|
|
302
|
+
t.Logf("Generated expected.log for %s", filepath.Base(testDir))
|
|
303
|
+
} else if err != nil {
|
|
304
|
+
// If there was another error reading expected.log, fail the test
|
|
305
|
+
t.Fatalf("failed to read expected.log in %s: %v", testDir, err)
|
|
306
|
+
} else {
|
|
307
|
+
// If expected.log exists, compare actual output to expected
|
|
308
|
+
exp := strings.TrimSpace(string(expected))
|
|
309
|
+
if actual != exp {
|
|
310
|
+
t.Fatalf("output mismatch\nExpected:\n%s\nActual:\n%s", exp, actual)
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
package compliance
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"os"
|
|
5
|
+
"path/filepath"
|
|
6
|
+
"sync"
|
|
7
|
+
"testing"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
func TestCompliance(t *testing.T) {
|
|
11
|
+
testsDir := "./tests"
|
|
12
|
+
dirs, err := os.ReadDir(testsDir)
|
|
13
|
+
if err != nil {
|
|
14
|
+
t.Fatalf("failed to read tests dir: %v", err)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Get workspace directory (project root)
|
|
18
|
+
workspaceDir, err := os.Getwd()
|
|
19
|
+
if err != nil {
|
|
20
|
+
t.Fatalf("failed to get working directory: %v", err)
|
|
21
|
+
}
|
|
22
|
+
workspaceDir = filepath.Join(workspaceDir, "..")
|
|
23
|
+
|
|
24
|
+
// First collect all test paths
|
|
25
|
+
var testPaths []string
|
|
26
|
+
for _, dir := range dirs {
|
|
27
|
+
if !dir.IsDir() {
|
|
28
|
+
continue
|
|
29
|
+
}
|
|
30
|
+
testPath := filepath.Join(testsDir, dir.Name())
|
|
31
|
+
// expectedLogPath := filepath.Join(testPath, "expected.log")
|
|
32
|
+
// if _, err := os.Stat(expectedLogPath); err != nil {
|
|
33
|
+
// continue // skip if no expected.log
|
|
34
|
+
//}
|
|
35
|
+
goFiles, err := filepath.Glob(filepath.Join(testPath, "*.go"))
|
|
36
|
+
if err != nil || len(goFiles) == 0 {
|
|
37
|
+
t.Errorf("no .go files found in %s", testPath)
|
|
38
|
+
continue
|
|
39
|
+
}
|
|
40
|
+
testPaths = append(testPaths, testPath)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Now run tests in parallel with goroutines
|
|
44
|
+
var wg sync.WaitGroup
|
|
45
|
+
for _, testPath := range testPaths {
|
|
46
|
+
wg.Add(1)
|
|
47
|
+
go func(path string) {
|
|
48
|
+
defer wg.Done()
|
|
49
|
+
t.Run(filepath.Base(path), func(t *testing.T) {
|
|
50
|
+
RunGoScriptTestDir(t, workspaceDir, path) // Pass workspaceDir
|
|
51
|
+
})
|
|
52
|
+
}(testPath)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Wait for all tests to complete
|
|
56
|
+
wg.Wait()
|
|
57
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
func main() {
|
|
4
|
+
// Test basic array literal
|
|
5
|
+
var a [3]int = [3]int{1, 2, 3}
|
|
6
|
+
println(a[0], a[1], a[2])
|
|
7
|
+
|
|
8
|
+
// Test array literal with inferred length
|
|
9
|
+
b := [...]string{"hello", "world"}
|
|
10
|
+
println(b[0], b[1])
|
|
11
|
+
|
|
12
|
+
// Test array literal with specific element initialization
|
|
13
|
+
c := [5]int{1: 10, 3: 30}
|
|
14
|
+
println(c[0], c[1], c[2], c[3], c[4])
|
|
15
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Generated file based on array_literal.go
|
|
2
|
+
// Updated when compliance tests are re-run, DO NOT EDIT!
|
|
3
|
+
|
|
4
|
+
import * as goscript from "@go/builtin";
|
|
5
|
+
|
|
6
|
+
export async function main(): Promise<void> {
|
|
7
|
+
// Test basic array literal
|
|
8
|
+
let a: number[] = [1, 2, 3];
|
|
9
|
+
console.log(a[0], a[1], a[2])
|
|
10
|
+
|
|
11
|
+
// Test array literal with inferred length
|
|
12
|
+
let b = ["hello", "world"]
|
|
13
|
+
console.log(b[0], b[1])
|
|
14
|
+
|
|
15
|
+
// Test array literal with specific element initialization
|
|
16
|
+
let c = [0, 10, 0, 30, 0]
|
|
17
|
+
console.log(c[0], c[1], c[2], c[3], c[4])
|
|
18
|
+
}
|
|
19
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
// This function receives from a channel, making it async.
|
|
4
|
+
func receiveFromChan(ch chan int) int {
|
|
5
|
+
val := <-ch // This operation makes the function async
|
|
6
|
+
return val
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// This function calls an async function, making it async too.
|
|
10
|
+
func caller(ch chan int) int {
|
|
11
|
+
// We expect this call to be awaited in TypeScript
|
|
12
|
+
result := receiveFromChan(ch)
|
|
13
|
+
return result + 1
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
func main() {
|
|
17
|
+
// Create a buffered channel
|
|
18
|
+
myChan := make(chan int, 1)
|
|
19
|
+
myChan <- 10 // Send a value
|
|
20
|
+
|
|
21
|
+
// Call the async caller function
|
|
22
|
+
finalResult := caller(myChan)
|
|
23
|
+
println(finalResult) // Expected output: 11
|
|
24
|
+
|
|
25
|
+
close(myChan)
|
|
26
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Generated file based on async_basic.go
|
|
2
|
+
// Updated when compliance tests are re-run, DO NOT EDIT!
|
|
3
|
+
|
|
4
|
+
import * as goscript from "@go/builtin";
|
|
5
|
+
|
|
6
|
+
// This function receives from a channel, making it async.
|
|
7
|
+
async function receiveFromChan(ch: goscript.Channel<number>): Promise<number> {
|
|
8
|
+
let val = await ch.receive() // This operation makes the function async
|
|
9
|
+
return val
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// This function calls an async function, making it async too.
|
|
13
|
+
async function caller(ch: goscript.Channel<number>): Promise<number> {
|
|
14
|
+
// We expect this call to be awaited in TypeScript
|
|
15
|
+
let result = await receiveFromChan(ch)
|
|
16
|
+
return result + 1
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function main(): Promise<void> {
|
|
20
|
+
// Create a buffered channel
|
|
21
|
+
let myChan = goscript.makeChannel<number>(1)
|
|
22
|
+
await myChan.send(10)
|
|
23
|
+
|
|
24
|
+
// Call the async caller function
|
|
25
|
+
let finalResult = await caller(myChan)
|
|
26
|
+
console.log(finalResult) // Expected output: 11
|
|
27
|
+
|
|
28
|
+
myChan.close()
|
|
29
|
+
}
|
|
30
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
11
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
func main() {
|
|
4
|
+
// === Basic Arithmetic ===
|
|
5
|
+
add := 2 + 3
|
|
6
|
+
sub := 10 - 4
|
|
7
|
+
mul := 6 * 7
|
|
8
|
+
div := 20 / 5
|
|
9
|
+
mod := 17 % 3
|
|
10
|
+
println("Addition: Expected: 5, Actual:", add)
|
|
11
|
+
println("Subtraction: Expected: 6, Actual:", sub)
|
|
12
|
+
println("Multiplication: Expected: 42, Actual:", mul)
|
|
13
|
+
println("Division: Expected: 4, Actual:", div)
|
|
14
|
+
println("Modulus: Expected: 2, Actual:", mod)
|
|
15
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Generated file based on basic_arithmetic.go
|
|
2
|
+
// Updated when compliance tests are re-run, DO NOT EDIT!
|
|
3
|
+
|
|
4
|
+
import * as goscript from "@go/builtin";
|
|
5
|
+
|
|
6
|
+
export async function main(): Promise<void> {
|
|
7
|
+
// === Basic Arithmetic ===
|
|
8
|
+
let add = 2 + 3
|
|
9
|
+
let sub = 10 - 4
|
|
10
|
+
let mul = 6 * 7
|
|
11
|
+
let div = 20 / 5
|
|
12
|
+
let mod = 17 % 3
|
|
13
|
+
console.log("Addition: Expected: 5, Actual:", add)
|
|
14
|
+
console.log("Subtraction: Expected: 6, Actual:", sub)
|
|
15
|
+
console.log("Multiplication: Expected: 42, Actual:", mul)
|
|
16
|
+
console.log("Division: Expected: 4, Actual:", div)
|
|
17
|
+
console.log("Modulus: Expected: 2, Actual:", mod)
|
|
18
|
+
}
|
|
19
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
func main() {
|
|
4
|
+
// === Boolean Logic ===
|
|
5
|
+
a := true
|
|
6
|
+
b := false
|
|
7
|
+
and := a && b
|
|
8
|
+
or := a || b
|
|
9
|
+
notA := !a
|
|
10
|
+
println("AND: Expected: false, Actual:", and)
|
|
11
|
+
println("OR: Expected: true, Actual:", or)
|
|
12
|
+
println("NOT: Expected: false, Actual:", notA)
|
|
13
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Generated file based on boolean_logic.go
|
|
2
|
+
// Updated when compliance tests are re-run, DO NOT EDIT!
|
|
3
|
+
|
|
4
|
+
import * as goscript from "@go/builtin";
|
|
5
|
+
|
|
6
|
+
export async function main(): Promise<void> {
|
|
7
|
+
// === Boolean Logic ===
|
|
8
|
+
let a = true
|
|
9
|
+
let b = false
|
|
10
|
+
let and = a && b
|
|
11
|
+
let or = a || b
|
|
12
|
+
let notA = !a
|
|
13
|
+
console.log("AND: Expected: false, Actual:", and)
|
|
14
|
+
console.log("OR: Expected: true, Actual:", or)
|
|
15
|
+
console.log("NOT: Expected: false, Actual:", notA)
|
|
16
|
+
}
|
|
17
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Generated file based on channel_basic.go
|
|
2
|
+
// Updated when compliance tests are re-run, DO NOT EDIT!
|
|
3
|
+
|
|
4
|
+
import * as goscript from "@go/builtin";
|
|
5
|
+
|
|
6
|
+
export async function main(): Promise<void> {
|
|
7
|
+
let messages = goscript.makeChannel<string>(0)
|
|
8
|
+
|
|
9
|
+
queueMicrotask(async () => {
|
|
10
|
+
{
|
|
11
|
+
await messages.send("ping")
|
|
12
|
+
}
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
let msg = await messages.receive()
|
|
16
|
+
console.log(msg)
|
|
17
|
+
}
|
|
18
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ping
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
type MyStruct struct {
|
|
4
|
+
MyInt int
|
|
5
|
+
MyString string
|
|
6
|
+
myBool bool
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
func main() {
|
|
10
|
+
// === Composite Literal Assignment (Value Copy) ===
|
|
11
|
+
// Creating a struct directly using a composite literal.
|
|
12
|
+
structLiteral := MyStruct{MyString: "composite literal"}
|
|
13
|
+
// Assigning it creates another independent copy.
|
|
14
|
+
structLiteralCopy := structLiteral
|
|
15
|
+
structLiteralCopy.MyString = "modified composite literal copy"
|
|
16
|
+
// Expected: "composite literal"
|
|
17
|
+
println("Original struct literal: Expected: composite literal, Actual: " + structLiteral.MyString)
|
|
18
|
+
// Expected: "modified composite literal copy"
|
|
19
|
+
println("Modified struct literal copy: Expected: modified composite literal copy, Actual: " + structLiteralCopy.MyString)
|
|
20
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Generated file based on composite_literal_assignment.go
|
|
2
|
+
// Updated when compliance tests are re-run, DO NOT EDIT!
|
|
3
|
+
|
|
4
|
+
import * as goscript from "@go/builtin";
|
|
5
|
+
|
|
6
|
+
class MyStruct {
|
|
7
|
+
public MyInt: number = 0;
|
|
8
|
+
public MyString: string = "";
|
|
9
|
+
private myBool: boolean = false;
|
|
10
|
+
|
|
11
|
+
constructor(init?: Partial<MyStruct>) { if (init) Object.assign(this, init as any); }
|
|
12
|
+
public clone(): MyStruct { return Object.assign(Object.create(MyStruct.prototype) as MyStruct, this); }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function main(): Promise<void> {
|
|
16
|
+
// === Composite Literal Assignment (Value Copy) ===
|
|
17
|
+
// Creating a struct directly using a composite literal.
|
|
18
|
+
let structLiteral = new MyStruct({ MyString: "composite literal" })
|
|
19
|
+
// Assigning it creates another independent copy.
|
|
20
|
+
let structLiteralCopy = structLiteral.clone()
|
|
21
|
+
structLiteralCopy.MyString = "modified composite literal copy"
|
|
22
|
+
// Expected: "composite literal"
|
|
23
|
+
console.log("Original struct literal: Expected: composite literal, Actual: " + structLiteral.MyString)
|
|
24
|
+
// Expected: "modified composite literal copy"
|
|
25
|
+
console.log("Modified struct literal copy: Expected: modified composite literal copy, Actual: " + structLiteralCopy.MyString)
|
|
26
|
+
}
|
|
27
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
const (
|
|
4
|
+
Pi = 3.14
|
|
5
|
+
Truth = false
|
|
6
|
+
// TODO: Handle large integer constants and bit shifts exceeding JS number limits.
|
|
7
|
+
// Big = 1 << 60
|
|
8
|
+
// Small = Big >> 59 // Commented out as it depends on Big
|
|
9
|
+
Greeting = "Hello, Constants!"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
func main() {
|
|
13
|
+
println(Pi)
|
|
14
|
+
println(Truth)
|
|
15
|
+
// println(Big) // Commented out until large integer handling is implemented
|
|
16
|
+
// println(Small) // Commented out as it depends on Big
|
|
17
|
+
println(Greeting)
|
|
18
|
+
}
|