goscript 0.0.58 → 0.0.59
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 +40 -33
- package/compiler/analysis.go +115 -19
- package/compiler/assignment.go +163 -217
- package/compiler/compiler.go +35 -31
- package/compiler/composite-lit.go +233 -196
- package/compiler/constraint.go +88 -0
- package/compiler/decl.go +82 -24
- package/compiler/expr-call-async.go +20 -34
- package/compiler/expr-call-builtins.go +19 -0
- package/compiler/expr-call-helpers.go +0 -28
- package/compiler/expr-call-make.go +93 -343
- package/compiler/expr-call-type-conversion.go +221 -249
- package/compiler/expr-call.go +70 -69
- package/compiler/expr-selector.go +21 -24
- package/compiler/expr.go +3 -60
- package/compiler/protobuf.go +180 -36
- package/compiler/spec-value.go +132 -24
- package/compiler/spec.go +14 -55
- package/compiler/stmt-assign.go +338 -356
- package/compiler/stmt-range.go +4 -24
- package/compiler/stmt.go +92 -203
- package/compiler/type-utils.go +185 -0
- package/compiler/type.go +26 -80
- package/dist/gs/builtin/slice.d.ts +1 -1
- package/dist/gs/builtin/slice.js +3 -0
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/builtin/type.js +8 -2
- package/dist/gs/builtin/type.js.map +1 -1
- package/dist/gs/fmt/fmt.js +113 -16
- package/dist/gs/fmt/fmt.js.map +1 -1
- package/dist/gs/runtime/runtime.d.ts +1 -1
- package/dist/gs/runtime/runtime.js +1 -1
- package/dist/gs/slices/slices.d.ts +23 -0
- package/dist/gs/slices/slices.js +61 -0
- package/dist/gs/slices/slices.js.map +1 -1
- package/go.mod +8 -8
- package/go.sum +14 -14
- package/gs/builtin/slice.ts +5 -2
- package/gs/builtin/type.ts +13 -6
- package/gs/fmt/fmt.test.ts +176 -0
- package/gs/fmt/fmt.ts +109 -18
- package/gs/runtime/runtime.ts +1 -1
- package/gs/slices/slices.ts +68 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
## What is GoScript?
|
|
11
11
|
|
|
12
|
-
GoScript is
|
|
12
|
+
GoScript is an experimental **Go to TypeScript compiler** that translates Go code to TypeScript at the AST level. The goal is to enable sharing algorithms and business logic between Go backends and TypeScript frontends.
|
|
13
13
|
|
|
14
14
|
> 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.
|
|
15
15
|
>
|
|
@@ -17,33 +17,46 @@ GoScript is a **Go to TypeScript compiler** that translates Go code to TypeScrip
|
|
|
17
17
|
|
|
18
18
|
### 🎯 Why GoScript?
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
Write once, run everywhere. Share your Go algorithms, business logic, and data structures seamlessly between your backend and frontend without maintaining two codebases.
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
🔬 **Experimental features being developed:**
|
|
23
23
|
- Sharing business logic between Go services and web apps
|
|
24
24
|
- Porting Go algorithms to run in browsers
|
|
25
25
|
- Building TypeScript libraries from existing Go code
|
|
26
|
-
- Full-stack teams that love Go's simplicity
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
GoScript compiles a powerful subset of Go:
|
|
30
|
-
- Structs, interfaces, methods, and functions
|
|
31
|
-
- Channels and goroutines (translated to async/await)
|
|
32
|
-
- Slices, maps, and most built-in types
|
|
33
|
-
- Basic reflection support
|
|
34
|
-
- Standard control flow (if, for, switch, etc.)
|
|
27
|
+
Go has powerful concurrency support and an excellent standard library. GoScript brings these capabilities to TypeScript with as simple and readable of a translation as possible.
|
|
35
28
|
|
|
36
|
-
**Current
|
|
29
|
+
⚠️ **Current development status:**
|
|
30
|
+
GoScript is working on compiling a subset of Go:
|
|
31
|
+
- ✅ Basic structs, interfaces, methods, and functions
|
|
32
|
+
- ✅ Channels and goroutines (translating to async/await)
|
|
33
|
+
- ✅ Slice semantics, maps, and built-in types
|
|
34
|
+
- ✅ Standard control flow (if, for, switch, select, range, etc.)
|
|
35
|
+
- 🚧 Basic reflection support
|
|
36
|
+
- 🚧 Standard library support
|
|
37
|
+
|
|
38
|
+
**Known limitations in this preview:**
|
|
37
39
|
- Uses JavaScript `number` type (64-bit float, not Go's int types)
|
|
38
40
|
- No pointer arithmetic (`uintptr`) or `unsafe` package
|
|
39
41
|
- No complex numbers
|
|
40
|
-
- Limited standard library (
|
|
42
|
+
- Limited standard library (working on it)
|
|
43
|
+
- Performance not yet optimized
|
|
44
|
+
|
|
45
|
+
**This is a prototype!** Expect bugs and missing features. Please contribute!
|
|
46
|
+
|
|
47
|
+
### ⚠️ Development Preview
|
|
48
|
+
|
|
49
|
+
**This project is currently in active development and should be considered experimental.** GoScript is a work-in-progress prototype that may have bugs, incomplete features, and breaking changes. We're actively iterating on the compiler and welcome your feedback!
|
|
50
|
+
|
|
51
|
+
🐛 **Found an issue?** Please [open an issue](https://github.com/aperturerobotics/goscript/issues) and we'll fix it.
|
|
41
52
|
|
|
42
|
-
|
|
53
|
+
🤖 **AI** This prototype is heavily AI-written, but I plan to manually rewrite the codebase by hand once it reaches a working state for advanced use cases. AI Producer, human Reducer.
|
|
43
54
|
|
|
44
55
|
📖 **Learn more:** [Design document](./design/DESIGN.md) | [Compliance tests](./compliance/COMPLIANCE.md)
|
|
45
56
|
|
|
46
|
-
## 🚀
|
|
57
|
+
## 🚀 Try It
|
|
58
|
+
|
|
59
|
+
> **Warning:** This is experimental software. Features may be incomplete or broken. Please report any issues!
|
|
47
60
|
|
|
48
61
|
### Installation
|
|
49
62
|
|
|
@@ -52,18 +65,20 @@ If you're building algorithms, business logic, or data processing code, GoScript
|
|
|
52
65
|
go install github.com/aperturerobotics/goscript/cmd/goscript@latest
|
|
53
66
|
```
|
|
54
67
|
|
|
55
|
-
**Option 2: NPM**
|
|
68
|
+
**Option 2: NPM** (if available)
|
|
56
69
|
```bash
|
|
57
70
|
npm install -g goscript
|
|
58
71
|
```
|
|
59
72
|
|
|
60
|
-
###
|
|
73
|
+
### Compilation
|
|
61
74
|
|
|
62
75
|
```bash
|
|
63
|
-
#
|
|
76
|
+
# Try compiling your Go package to TypeScript
|
|
64
77
|
goscript compile --package . --output ./dist
|
|
65
78
|
```
|
|
66
79
|
|
|
80
|
+
**Note:** Many Go packages may not compile successfully yet. Start with simple code and gradually test more complex features.
|
|
81
|
+
|
|
67
82
|
## 📦 Using Generated Code in Your Project
|
|
68
83
|
|
|
69
84
|
After compiling your Go code to TypeScript, you'll need to set up your project appropriately.
|
|
@@ -168,6 +183,8 @@ const searchUser = (email: string) => {
|
|
|
168
183
|
|
|
169
184
|
## 💡 See It In Action
|
|
170
185
|
|
|
186
|
+
> **Disclaimer:** These examples represent the target functionality. Your mileage may vary with the current development preview.
|
|
187
|
+
|
|
171
188
|
### Example: User Management
|
|
172
189
|
|
|
173
190
|
**Go Code** (`user.go`):
|
|
@@ -203,7 +220,7 @@ func FindUserByEmail(users []*User, email string) *User {
|
|
|
203
220
|
goscript compile --package . --output ./dist
|
|
204
221
|
```
|
|
205
222
|
|
|
206
|
-
**Generated TypeScript** (`user.ts`):
|
|
223
|
+
**Generated TypeScript** (`user.gs.ts`):
|
|
207
224
|
```typescript
|
|
208
225
|
export class User {
|
|
209
226
|
public ID: number = 0
|
|
@@ -298,21 +315,11 @@ async function handleMessages() {
|
|
|
298
315
|
}
|
|
299
316
|
```
|
|
300
317
|
|
|
301
|
-
##
|
|
302
|
-
|
|
303
|
-
**Current Status:**
|
|
304
|
-
- ✅ Core language features (structs, methods, interfaces)
|
|
305
|
-
- ✅ Async/await for goroutines and channels
|
|
306
|
-
- ✅ Basic reflection support
|
|
307
|
-
- ✅ Most control flow and data types
|
|
308
|
-
|
|
309
|
-
**Coming Soon:**
|
|
310
|
-
- 📦 Expanded standard library
|
|
311
|
-
- 🧪 Go test → TypeScript test conversion
|
|
312
|
-
- ⚡ Performance optimizations
|
|
313
|
-
- 🔧 Better tooling integration
|
|
318
|
+
## 🤝 How You Can Help
|
|
314
319
|
|
|
315
|
-
|
|
320
|
+
- Try GoScript on your code and [report issues](https://github.com/aperturerobotics/goscript/issues)
|
|
321
|
+
- Check the [compliance tests](./compliance/COMPLIANCE.md) for current progress
|
|
322
|
+
- Contribute test cases for edge cases you discover
|
|
316
323
|
|
|
317
324
|
## License
|
|
318
325
|
|
package/compiler/analysis.go
CHANGED
|
@@ -641,6 +641,22 @@ func (v *analysisVisitor) visitFuncDecl(n *ast.FuncDecl) ast.Visitor {
|
|
|
641
641
|
funcInfo := v.analysis.ensureFunctionData(obj)
|
|
642
642
|
funcInfo.ReceiverUsed = receiverUsed
|
|
643
643
|
}
|
|
644
|
+
|
|
645
|
+
// If receiver is used, mark all identifiers that refer to the receiver variable
|
|
646
|
+
if receiverUsed && n.Body != nil {
|
|
647
|
+
recvName := ident.Name
|
|
648
|
+
ast.Inspect(n.Body, func(nn ast.Node) bool {
|
|
649
|
+
id, ok := nn.(*ast.Ident)
|
|
650
|
+
if !ok {
|
|
651
|
+
return true
|
|
652
|
+
}
|
|
653
|
+
if obj := v.pkg.TypesInfo.Uses[id]; obj != nil && obj == vr {
|
|
654
|
+
ni := v.analysis.ensureNodeData(id)
|
|
655
|
+
ni.IdentifierMapping = recvName
|
|
656
|
+
}
|
|
657
|
+
return true
|
|
658
|
+
})
|
|
659
|
+
}
|
|
644
660
|
}
|
|
645
661
|
}
|
|
646
662
|
}
|
|
@@ -759,9 +775,92 @@ func (v *analysisVisitor) visitCallExpr(n *ast.CallExpr) ast.Visitor {
|
|
|
759
775
|
// Track interface implementations from function call arguments
|
|
760
776
|
v.trackInterfaceCallArguments(n)
|
|
761
777
|
|
|
778
|
+
// Check for implicit address-taking in method calls with pointer receivers
|
|
779
|
+
v.checkImplicitAddressTaking(n)
|
|
780
|
+
|
|
762
781
|
return v
|
|
763
782
|
}
|
|
764
783
|
|
|
784
|
+
// checkImplicitAddressTaking detects when a method call with a pointer receiver
|
|
785
|
+
// is called on a non-pointer variable, which requires implicit address-taking.
|
|
786
|
+
// Example: var s MySlice; s.Add(10) where Add has receiver *MySlice
|
|
787
|
+
// This is equivalent to (&s).Add(10), so s needs to be marked as NeedsVarRef
|
|
788
|
+
func (v *analysisVisitor) checkImplicitAddressTaking(callExpr *ast.CallExpr) {
|
|
789
|
+
// Check if this is a method call (selector expression)
|
|
790
|
+
selExpr, ok := callExpr.Fun.(*ast.SelectorExpr)
|
|
791
|
+
if !ok {
|
|
792
|
+
return
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// Get the selection information
|
|
796
|
+
selection := v.pkg.TypesInfo.Selections[selExpr]
|
|
797
|
+
if selection == nil || selection.Kind() != types.MethodVal {
|
|
798
|
+
return
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
// Get the method object
|
|
802
|
+
methodObj := selection.Obj()
|
|
803
|
+
if methodObj == nil {
|
|
804
|
+
return
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// Get the method's signature to check the receiver type
|
|
808
|
+
methodFunc, ok := methodObj.(*types.Func)
|
|
809
|
+
if !ok {
|
|
810
|
+
return
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
sig := methodFunc.Type().(*types.Signature)
|
|
814
|
+
recv := sig.Recv()
|
|
815
|
+
if recv == nil {
|
|
816
|
+
return
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// Check if the method has a pointer receiver
|
|
820
|
+
recvType := recv.Type()
|
|
821
|
+
_, hasPointerReceiver := recvType.(*types.Pointer)
|
|
822
|
+
if !hasPointerReceiver {
|
|
823
|
+
return
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// Get the type of the receiver expression (the thing before the dot)
|
|
827
|
+
exprType := v.pkg.TypesInfo.TypeOf(selExpr.X)
|
|
828
|
+
if exprType == nil {
|
|
829
|
+
return
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
// Check if the receiver expression is NOT already a pointer
|
|
833
|
+
_, exprIsPointer := exprType.(*types.Pointer)
|
|
834
|
+
if exprIsPointer {
|
|
835
|
+
// Expression is already a pointer, no implicit address-taking needed
|
|
836
|
+
return
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// At this point, we have:
|
|
840
|
+
// - A method with a pointer receiver
|
|
841
|
+
// - Being called on a non-pointer expression
|
|
842
|
+
// This means Go will implicitly take the address
|
|
843
|
+
|
|
844
|
+
// Check if the receiver expression is an identifier (variable)
|
|
845
|
+
ident, ok := selExpr.X.(*ast.Ident)
|
|
846
|
+
if !ok {
|
|
847
|
+
return
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// Get the variable object
|
|
851
|
+
obj := v.pkg.TypesInfo.ObjectOf(ident)
|
|
852
|
+
if obj == nil {
|
|
853
|
+
return
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
// Mark this variable as needing VarRef (its address is being taken)
|
|
857
|
+
usageInfo := v.getOrCreateUsageInfo(obj)
|
|
858
|
+
usageInfo.Destinations = append(usageInfo.Destinations, AssignmentInfo{
|
|
859
|
+
Object: nil, // No specific destination for method calls
|
|
860
|
+
Type: AddressOfAssignment,
|
|
861
|
+
})
|
|
862
|
+
}
|
|
863
|
+
|
|
765
864
|
// visitSelectorExpr handles selector expression analysis
|
|
766
865
|
func (v *analysisVisitor) visitSelectorExpr(n *ast.SelectorExpr) ast.Visitor {
|
|
767
866
|
// Check if this is a method value (method being used as a value, not called immediately)
|
|
@@ -2234,13 +2333,12 @@ func (v *analysisVisitor) analyzeMethodAsync(funcDecl *ast.FuncDecl, pkg *packag
|
|
|
2234
2333
|
isExternalPackage := pkg.Types != v.pkg.Types && v.analysis.AllPackages[pkg.Types.Path()] == nil
|
|
2235
2334
|
|
|
2236
2335
|
if isExternalPackage {
|
|
2237
|
-
// Truly external package: check metadata first
|
|
2336
|
+
// Truly external package: check metadata first
|
|
2238
2337
|
isAsync = v.checkExternalMethodMetadata(methodKey.PackagePath, methodKey.ReceiverType, methodKey.MethodName)
|
|
2239
|
-
}
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
}
|
|
2338
|
+
}
|
|
2339
|
+
// If not determined async yet and body exists, analyze it
|
|
2340
|
+
if !isAsync && funcDecl.Body != nil {
|
|
2341
|
+
isAsync = v.containsAsyncOperationsComplete(funcDecl.Body, pkg)
|
|
2244
2342
|
}
|
|
2245
2343
|
|
|
2246
2344
|
// Store result in MethodAsyncStatus
|
|
@@ -2459,21 +2557,19 @@ func (v *analysisVisitor) isMethodAsyncFromSelection(selExpr *ast.SelectorExpr,
|
|
|
2459
2557
|
return status
|
|
2460
2558
|
}
|
|
2461
2559
|
|
|
2462
|
-
//
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
if key.PackagePath == methodPkgPath {
|
|
2470
|
-
hasMetadata = true
|
|
2471
|
-
break
|
|
2560
|
+
// Check if this is a method in the same package we're currently analyzing
|
|
2561
|
+
if methodPkgPath == v.pkg.Types.Path() {
|
|
2562
|
+
// This is a method in the same package - we should analyze it if we haven't yet
|
|
2563
|
+
if funcDecl := v.findMethodDecl(receiverType, methodObj.Name(), v.pkg); funcDecl != nil {
|
|
2564
|
+
v.analyzeMethodAsync(funcDecl, v.pkg)
|
|
2565
|
+
if status, exists := v.analysis.MethodAsyncStatus[methodKey]; exists {
|
|
2566
|
+
return status
|
|
2472
2567
|
}
|
|
2473
2568
|
}
|
|
2474
|
-
|
|
2475
|
-
//
|
|
2476
|
-
if
|
|
2569
|
+
} else {
|
|
2570
|
+
// For methods in other packages that we're compiling together
|
|
2571
|
+
if targetPkg := v.analysis.AllPackages[methodPkgPath]; targetPkg != nil {
|
|
2572
|
+
// Try to analyze the method if we haven't already
|
|
2477
2573
|
if funcDecl := v.findMethodDecl(receiverType, methodObj.Name(), targetPkg); funcDecl != nil {
|
|
2478
2574
|
v.analyzeMethodAsync(funcDecl, targetPkg)
|
|
2479
2575
|
if status, exists := v.analysis.MethodAsyncStatus[methodKey]; exists {
|