goscript 0.0.10 → 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 +170 -65
- package/cmd/goscript/cmd_compile.go +28 -8
- package/compiler/compile.go +42 -34
- package/compiler/compile_decls.go +12 -0
- package/compiler/compile_expr.go +135 -8
- package/compiler/compile_field.go +22 -1
- package/compiler/compile_spec.go +141 -4
- package/compiler/compile_stmt.go +124 -43
- 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 +63 -1
- package/dist/builtin/builtin.js +81 -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.d.ts +1 -0
- package/dist/compliance/tests/async_defer_statement/async_defer_statement.gs.js +82 -0
- package/dist/compliance/tests/async_defer_statement/async_defer_statement.gs.js.map +1 -0
- 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.d.ts +1 -0
- package/dist/compliance/tests/defer_statement/defer_statement.gs.js +75 -0
- package/dist/compliance/tests/defer_statement/defer_statement.gs.js.map +1 -0
- package/dist/compliance/tests/embedded_interface_assertion/embedded_interface_assertion.gs.js +3 -3
- 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_loop_infinite/for_loop_infinite.gs.d.ts +1 -0
- package/dist/compliance/tests/for_loop_infinite/for_loop_infinite.gs.js +14 -0
- package/dist/compliance/tests/for_loop_infinite/for_loop_infinite.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 +3 -3
- 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 +13 -3
- 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.d.ts +1 -0
- package/dist/compliance/tests/interface_type_assertion_signature_mismatch/interface_type_assertion_signature_mismatch.gs.js +51 -0
- package/dist/compliance/tests/interface_type_assertion_signature_mismatch/interface_type_assertion_signature_mismatch.gs.js.map +1 -0
- 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 +281 -8
- 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.d.ts +1 -0
- package/dist/compliance/tests/struct_embedding/struct_embedding.gs.js +48 -0
- package/dist/compliance/tests/struct_embedding/struct_embedding.gs.js.map +1 -0
- 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 +4 -3
package/compiler/compile_expr.go
CHANGED
|
@@ -108,6 +108,43 @@ func (c *GoToTSCompiler) WriteValueExpr(a ast.Expr) error {
|
|
|
108
108
|
}
|
|
109
109
|
c.tsw.WriteLiterally("]")
|
|
110
110
|
return nil
|
|
111
|
+
case *ast.SliceExpr:
|
|
112
|
+
// Translate Go slice expression to goscript.slice(x, low, high, max)
|
|
113
|
+
c.tsw.WriteLiterally("goscript.slice(")
|
|
114
|
+
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
115
|
+
return err
|
|
116
|
+
}
|
|
117
|
+
// low argument
|
|
118
|
+
c.tsw.WriteLiterally(", ")
|
|
119
|
+
if exp.Low != nil {
|
|
120
|
+
if err := c.WriteValueExpr(exp.Low); err != nil {
|
|
121
|
+
return err
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
c.tsw.WriteLiterally("undefined")
|
|
125
|
+
}
|
|
126
|
+
// high argument
|
|
127
|
+
c.tsw.WriteLiterally(", ")
|
|
128
|
+
if exp.High != nil {
|
|
129
|
+
if err := c.WriteValueExpr(exp.High); err != nil {
|
|
130
|
+
return err
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
c.tsw.WriteLiterally("undefined")
|
|
134
|
+
}
|
|
135
|
+
// max argument (only for full slice expressions)
|
|
136
|
+
if exp.Slice3 {
|
|
137
|
+
c.tsw.WriteLiterally(", ")
|
|
138
|
+
if exp.Max != nil {
|
|
139
|
+
if err := c.WriteValueExpr(exp.Max); err != nil {
|
|
140
|
+
return err
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
c.tsw.WriteLiterally("undefined")
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
c.tsw.WriteLiterally(")")
|
|
147
|
+
return nil
|
|
111
148
|
case *ast.ParenExpr:
|
|
112
149
|
// Translate (X) to (X)
|
|
113
150
|
c.tsw.WriteLiterally("(")
|
|
@@ -278,13 +315,14 @@ func (c *GoToTSCompiler) WriteInterfaceType(exp *ast.InterfaceType) {
|
|
|
278
315
|
|
|
279
316
|
// If there are embedded interfaces, write the extends clause
|
|
280
317
|
if len(embeddedInterfaces) > 0 {
|
|
281
|
-
c.tsw.WriteLiterally("
|
|
318
|
+
c.tsw.WriteLiterally("extends ")
|
|
282
319
|
c.tsw.WriteLiterally(strings.Join(embeddedInterfaces, ", "))
|
|
320
|
+
c.tsw.WriteLiterally(" ")
|
|
283
321
|
}
|
|
284
322
|
|
|
285
323
|
// Write the interface body on the same line
|
|
286
|
-
c.tsw.WriteLiterally("{")
|
|
287
|
-
c.tsw.WriteLine("")
|
|
324
|
+
c.tsw.WriteLiterally("{")
|
|
325
|
+
c.tsw.WriteLine("") // Newline after opening brace
|
|
288
326
|
c.tsw.Indent(1)
|
|
289
327
|
|
|
290
328
|
// Write named methods
|
|
@@ -340,6 +378,28 @@ func (c *GoToTSCompiler) WriteFuncType(exp *ast.FuncType, isAsync bool) {
|
|
|
340
378
|
func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
341
379
|
expFun := exp.Fun
|
|
342
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
|
+
|
|
343
403
|
if funIdent, funIsIdent := expFun.(*ast.Ident); funIsIdent {
|
|
344
404
|
switch funIdent.String() {
|
|
345
405
|
case "println":
|
|
@@ -433,7 +493,20 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
433
493
|
|
|
434
494
|
// Handle slice creation
|
|
435
495
|
if _, ok := exp.Args[0].(*ast.ArrayType); ok {
|
|
436
|
-
|
|
496
|
+
// Get the slice type information
|
|
497
|
+
sliceType := c.pkg.TypesInfo.TypeOf(exp.Args[0])
|
|
498
|
+
if sliceType == nil {
|
|
499
|
+
return errors.New("could not get type information for slice in make call")
|
|
500
|
+
}
|
|
501
|
+
goElemType, ok := sliceType.Underlying().(*gtypes.Slice)
|
|
502
|
+
if !ok {
|
|
503
|
+
return errors.New("expected slice type for make call")
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
c.tsw.WriteLiterally("goscript.makeSlice<")
|
|
507
|
+
c.WriteGoType(goElemType.Elem()) // Write the element type
|
|
508
|
+
c.tsw.WriteLiterally(">(")
|
|
509
|
+
|
|
437
510
|
if len(exp.Args) >= 2 {
|
|
438
511
|
if err := c.WriteValueExpr(exp.Args[1]); err != nil { // Length
|
|
439
512
|
return err
|
|
@@ -457,10 +530,18 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
457
530
|
// Fallthrough for unhandled make calls (e.g., channels)
|
|
458
531
|
return errors.New("unhandled make call")
|
|
459
532
|
case "string":
|
|
460
|
-
// Handle string() conversion
|
|
533
|
+
// Handle string() conversion
|
|
461
534
|
if len(exp.Args) == 1 {
|
|
462
|
-
// Check if the argument is a rune (int32) or a call to rune()
|
|
463
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()
|
|
464
545
|
innerCall, isCallExpr := arg.(*ast.CallExpr)
|
|
465
546
|
|
|
466
547
|
if isCallExpr {
|
|
@@ -490,6 +571,19 @@ func (c *GoToTSCompiler) WriteCallExpr(exp *ast.CallExpr) error {
|
|
|
490
571
|
c.tsw.WriteLiterally(")")
|
|
491
572
|
return nil // Handled string(int32)
|
|
492
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
|
+
}
|
|
493
587
|
}
|
|
494
588
|
}
|
|
495
589
|
// Return error for other unhandled string conversions
|
|
@@ -617,6 +711,17 @@ func (c *GoToTSCompiler) WriteBinaryExprValue(exp *ast.BinaryExpr) error {
|
|
|
617
711
|
return nil
|
|
618
712
|
}
|
|
619
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
|
+
|
|
620
725
|
if err := c.WriteValueExpr(exp.X); err != nil {
|
|
621
726
|
return fmt.Errorf("failed to write binary expression left operand: %w", err)
|
|
622
727
|
}
|
|
@@ -632,6 +737,11 @@ func (c *GoToTSCompiler) WriteBinaryExprValue(exp *ast.BinaryExpr) error {
|
|
|
632
737
|
if err := c.WriteValueExpr(exp.Y); err != nil {
|
|
633
738
|
return fmt.Errorf("failed to write binary expression right operand: %w", err)
|
|
634
739
|
}
|
|
740
|
+
|
|
741
|
+
if isBitwise {
|
|
742
|
+
c.tsw.WriteLiterally(")") // Add closing parenthesis for bitwise operations
|
|
743
|
+
}
|
|
744
|
+
|
|
635
745
|
return nil
|
|
636
746
|
}
|
|
637
747
|
|
|
@@ -692,6 +802,16 @@ func (c *GoToTSCompiler) WriteCompositeLitValue(exp *ast.CompositeLit) error {
|
|
|
692
802
|
|
|
693
803
|
// Handle array literals
|
|
694
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
|
+
|
|
695
815
|
c.tsw.WriteLiterally("[")
|
|
696
816
|
// Use type info to get array length and element type
|
|
697
817
|
var arrayLen int
|
|
@@ -779,7 +899,7 @@ func (c *GoToTSCompiler) WriteCompositeLitValue(exp *ast.CompositeLit) error {
|
|
|
779
899
|
// Typed literal, likely a struct: new Type({...})
|
|
780
900
|
c.tsw.WriteLiterally("new ")
|
|
781
901
|
c.WriteTypeExpr(exp.Type)
|
|
782
|
-
c.tsw.WriteLiterally("({
|
|
902
|
+
c.tsw.WriteLiterally("({")
|
|
783
903
|
for i, elm := range exp.Elts {
|
|
784
904
|
if i != 0 {
|
|
785
905
|
c.tsw.WriteLiterally(", ")
|
|
@@ -788,7 +908,7 @@ func (c *GoToTSCompiler) WriteCompositeLitValue(exp *ast.CompositeLit) error {
|
|
|
788
908
|
return fmt.Errorf("failed to write struct literal field: %w", err)
|
|
789
909
|
}
|
|
790
910
|
}
|
|
791
|
-
c.tsw.WriteLiterally("
|
|
911
|
+
c.tsw.WriteLiterally("})")
|
|
792
912
|
return nil
|
|
793
913
|
}
|
|
794
914
|
}
|
|
@@ -886,10 +1006,17 @@ func (c *GoToTSCompiler) WriteFuncLitValue(exp *ast.FuncLit) error {
|
|
|
886
1006
|
|
|
887
1007
|
c.tsw.WriteLiterally(" => ")
|
|
888
1008
|
|
|
1009
|
+
// Save previous async state and set current state based on isAsync
|
|
1010
|
+
previousAsyncState := c.inAsyncFunction
|
|
1011
|
+
c.inAsyncFunction = isAsync
|
|
1012
|
+
|
|
889
1013
|
// Write function body
|
|
890
1014
|
if err := c.WriteStmt(exp.Body); err != nil {
|
|
1015
|
+
c.inAsyncFunction = previousAsyncState // Restore state before returning error
|
|
891
1016
|
return fmt.Errorf("failed to write function literal body: %w", err)
|
|
892
1017
|
}
|
|
893
1018
|
|
|
1019
|
+
// Restore previous async state
|
|
1020
|
+
c.inAsyncFunction = previousAsyncState
|
|
894
1021
|
return nil
|
|
895
1022
|
}
|
|
@@ -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
|
)
|
|
@@ -57,6 +58,20 @@ func (c *GoToTSCompiler) collectMethodNames(structName string) string {
|
|
|
57
58
|
return strings.Join(methodNames, ", ")
|
|
58
59
|
}
|
|
59
60
|
|
|
61
|
+
// getTypeExprName returns a string representation of a type expression.
|
|
62
|
+
func (c *GoToTSCompiler) getTypeExprName(expr ast.Expr) string {
|
|
63
|
+
switch t := expr.(type) {
|
|
64
|
+
case *ast.Ident:
|
|
65
|
+
return t.Name
|
|
66
|
+
case *ast.SelectorExpr:
|
|
67
|
+
return fmt.Sprintf("%s.%s", c.getTypeExprName(t.X), t.Sel.Name)
|
|
68
|
+
case *ast.StarExpr:
|
|
69
|
+
return c.getTypeExprName(t.X) // Unwrap pointer type
|
|
70
|
+
default:
|
|
71
|
+
return "embedded" // Fallback for complex type expressions
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
60
75
|
// collectInterfaceMethods returns a comma-separated string of method names for an interface
|
|
61
76
|
func (c *GoToTSCompiler) collectInterfaceMethods(interfaceType *ast.InterfaceType) string {
|
|
62
77
|
// Use a map to ensure uniqueness of method names
|
|
@@ -120,12 +135,32 @@ func (c *GoToTSCompiler) WriteTypeSpec(a *ast.TypeSpec) error {
|
|
|
120
135
|
|
|
121
136
|
switch t := a.Type.(type) {
|
|
122
137
|
case *ast.StructType:
|
|
123
|
-
//
|
|
138
|
+
// Detect embedded struct (anonymous field) to support Go struct embedding.
|
|
139
|
+
var embeddedExpr ast.Expr
|
|
140
|
+
for _, f := range t.Fields.List {
|
|
141
|
+
if len(f.Names) == 0 { // anonymous field ⇒ embedded type
|
|
142
|
+
embeddedExpr = f.Type
|
|
143
|
+
break // only the first embedded struct is supported for now
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// Write the class header, adding "extends Embedded" when an embedded struct is present.
|
|
124
147
|
c.tsw.WriteLiterally("class ")
|
|
125
148
|
if err := c.WriteValueExpr(a.Name); err != nil { // Class name is a value identifier
|
|
126
149
|
return err
|
|
127
150
|
}
|
|
128
|
-
c.tsw.
|
|
151
|
+
c.tsw.WriteLiterally(" ")
|
|
152
|
+
|
|
153
|
+
if embeddedExpr != nil {
|
|
154
|
+
// For pointer embedding (*T) unwrap the star so we extend T, not (T|null)
|
|
155
|
+
if star, ok := embeddedExpr.(*ast.StarExpr); ok {
|
|
156
|
+
embeddedExpr = star.X
|
|
157
|
+
}
|
|
158
|
+
c.tsw.WriteLiterally("extends ")
|
|
159
|
+
c.WriteTypeExpr(embeddedExpr) // Embedded type name
|
|
160
|
+
c.tsw.WriteLiterally(" ")
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
c.tsw.WriteLine("{")
|
|
129
164
|
c.tsw.Indent(1)
|
|
130
165
|
|
|
131
166
|
// className is the name of the class type
|
|
@@ -164,7 +199,38 @@ func (c *GoToTSCompiler) WriteTypeSpec(a *ast.TypeSpec) error {
|
|
|
164
199
|
|
|
165
200
|
// constructor and clone using Object.assign for compactness
|
|
166
201
|
c.tsw.WriteLine("")
|
|
167
|
-
|
|
202
|
+
if embeddedExpr != nil {
|
|
203
|
+
// Determine the embedded type's identifier (strip any package prefix)
|
|
204
|
+
embeddedTypeNameFull := c.getTypeExprName(embeddedExpr)
|
|
205
|
+
propName := embeddedTypeNameFull
|
|
206
|
+
if idx := strings.LastIndex(propName, "."); idx != -1 {
|
|
207
|
+
propName = propName[idx+1:] // keep only the last segment for the property key
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// The init parameter allows either flattened fields or a nested {Embedded: {...}} object
|
|
211
|
+
c.tsw.WriteLinef(
|
|
212
|
+
"constructor(init?: Partial<%s> & { %s?: Partial<%s> }) {",
|
|
213
|
+
className, propName, propName,
|
|
214
|
+
)
|
|
215
|
+
c.tsw.Indent(1)
|
|
216
|
+
|
|
217
|
+
// Pass either the nested embedded object or the full init directly to super()
|
|
218
|
+
c.tsw.WriteLinef("super(init?.%s || init);", propName)
|
|
219
|
+
|
|
220
|
+
// Copy fields that belong only to this derived class
|
|
221
|
+
c.tsw.WriteLine("if (init) {")
|
|
222
|
+
c.tsw.Indent(1)
|
|
223
|
+
c.tsw.WriteLinef("const { %s, ...rest } = init as any;", propName)
|
|
224
|
+
c.tsw.WriteLine("Object.assign(this, rest);")
|
|
225
|
+
c.tsw.Indent(-1)
|
|
226
|
+
c.tsw.WriteLine("}")
|
|
227
|
+
|
|
228
|
+
c.tsw.Indent(-1)
|
|
229
|
+
c.tsw.WriteLine("}")
|
|
230
|
+
} else {
|
|
231
|
+
// For a base class without embedding, use simple constructor
|
|
232
|
+
c.tsw.WriteLinef("constructor(init?: Partial<%s>) { if (init) Object.assign(this, init as any); }", className)
|
|
233
|
+
}
|
|
168
234
|
c.tsw.WriteLinef("public clone(): %s { return Object.assign(Object.create(%s.prototype) as %s, this); }", className, className, className)
|
|
169
235
|
|
|
170
236
|
// Add code to register the type with the runtime system
|
|
@@ -219,8 +285,17 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
|
|
|
219
285
|
c.WriteDoc(decl.Doc)
|
|
220
286
|
}
|
|
221
287
|
|
|
288
|
+
// Determine if method is async by checking for async operations in the body
|
|
289
|
+
isAsync := c.containsAsyncOperations(decl.Body)
|
|
290
|
+
|
|
222
291
|
// Methods are typically public in the TS output
|
|
223
292
|
c.tsw.WriteLiterally("public ")
|
|
293
|
+
|
|
294
|
+
// Add async modifier if needed
|
|
295
|
+
if isAsync {
|
|
296
|
+
c.tsw.WriteLiterally("async ")
|
|
297
|
+
}
|
|
298
|
+
|
|
224
299
|
// Keep original Go casing for method names
|
|
225
300
|
if err := c.WriteValueExpr(decl.Name); err != nil { // Method name is a value identifier
|
|
226
301
|
return err
|
|
@@ -238,6 +313,9 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
|
|
|
238
313
|
// Handle return type
|
|
239
314
|
if funcType.Results != nil && len(funcType.Results.List) > 0 {
|
|
240
315
|
c.tsw.WriteLiterally(": ")
|
|
316
|
+
if isAsync {
|
|
317
|
+
c.tsw.WriteLiterally("Promise<")
|
|
318
|
+
}
|
|
241
319
|
if len(funcType.Results.List) == 1 {
|
|
242
320
|
// Single return value
|
|
243
321
|
resultType := funcType.Results.List[0].Type
|
|
@@ -253,12 +331,27 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
|
|
|
253
331
|
}
|
|
254
332
|
c.tsw.WriteLiterally("]")
|
|
255
333
|
}
|
|
334
|
+
if isAsync {
|
|
335
|
+
c.tsw.WriteLiterally(">")
|
|
336
|
+
}
|
|
256
337
|
} else {
|
|
257
338
|
// No return value -> void
|
|
258
|
-
|
|
339
|
+
if isAsync {
|
|
340
|
+
c.tsw.WriteLiterally(": Promise<void>")
|
|
341
|
+
} else {
|
|
342
|
+
c.tsw.WriteLiterally(": void")
|
|
343
|
+
}
|
|
259
344
|
}
|
|
260
345
|
|
|
261
346
|
c.tsw.WriteLiterally(" ")
|
|
347
|
+
|
|
348
|
+
// Check if function body has defer statements
|
|
349
|
+
c.nextBlockNeedsDefer = c.scanForDefer(decl.Body)
|
|
350
|
+
|
|
351
|
+
// Save previous async state and set current state based on isAsync
|
|
352
|
+
previousAsyncState := c.inAsyncFunction
|
|
353
|
+
c.inAsyncFunction = isAsync
|
|
354
|
+
|
|
262
355
|
// Bind receiver name to this
|
|
263
356
|
if recvField := decl.Recv.List[0]; len(recvField.Names) > 0 {
|
|
264
357
|
recvName := recvField.Names[0].Name
|
|
@@ -266,21 +359,40 @@ func (c *GoToTSCompiler) WriteFuncDeclAsMethod(decl *ast.FuncDecl) error {
|
|
|
266
359
|
c.tsw.WriteLine("{")
|
|
267
360
|
c.tsw.Indent(1)
|
|
268
361
|
c.tsw.WriteLinef("const %s = this", recvName)
|
|
362
|
+
|
|
363
|
+
// Add using statement if needed
|
|
364
|
+
if c.nextBlockNeedsDefer {
|
|
365
|
+
if c.inAsyncFunction {
|
|
366
|
+
c.tsw.WriteLine("await using __defer = new goscript.AsyncDisposableStack();")
|
|
367
|
+
} else {
|
|
368
|
+
c.tsw.WriteLine("using cleanup = new goscript.DisposableStack();")
|
|
369
|
+
}
|
|
370
|
+
c.nextBlockNeedsDefer = false
|
|
371
|
+
}
|
|
372
|
+
|
|
269
373
|
// write method body without outer braces
|
|
270
374
|
for _, stmt := range decl.Body.List {
|
|
271
375
|
if err := c.WriteStmt(stmt); err != nil {
|
|
376
|
+
c.inAsyncFunction = previousAsyncState // Restore state before returning error
|
|
272
377
|
return fmt.Errorf("failed to write statement in function body: %w", err)
|
|
273
378
|
}
|
|
274
379
|
}
|
|
275
380
|
c.tsw.Indent(-1)
|
|
276
381
|
c.tsw.WriteLine("}")
|
|
382
|
+
|
|
383
|
+
// Restore previous async state
|
|
384
|
+
c.inAsyncFunction = previousAsyncState
|
|
277
385
|
return nil
|
|
278
386
|
}
|
|
279
387
|
}
|
|
280
388
|
// no named receiver, write whole body
|
|
281
389
|
if err := c.WriteStmt(decl.Body); err != nil {
|
|
390
|
+
c.inAsyncFunction = previousAsyncState // Restore state before returning error
|
|
282
391
|
return fmt.Errorf("failed to write function body: %w", err)
|
|
283
392
|
}
|
|
393
|
+
|
|
394
|
+
// Restore previous async state
|
|
395
|
+
c.inAsyncFunction = previousAsyncState
|
|
284
396
|
return nil
|
|
285
397
|
}
|
|
286
398
|
|
|
@@ -296,13 +408,30 @@ func (c *GoToTSCompiler) WriteValueSpec(a *ast.ValueSpec) error {
|
|
|
296
408
|
if len(a.Names) == 1 {
|
|
297
409
|
name := a.Names[0]
|
|
298
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
|
+
|
|
299
420
|
if a.Type != nil {
|
|
300
421
|
c.tsw.WriteLiterally(": ")
|
|
301
422
|
c.WriteTypeExpr(a.Type) // Variable type annotation
|
|
302
423
|
|
|
424
|
+
// Append "| null" for interface types
|
|
425
|
+
if isInterface {
|
|
426
|
+
c.tsw.WriteLiterally(" | null")
|
|
427
|
+
}
|
|
428
|
+
|
|
303
429
|
// Check if it's an array type declaration without an initial value
|
|
304
430
|
if _, isArrayType := a.Type.(*ast.ArrayType); isArrayType && len(a.Values) == 0 {
|
|
305
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")
|
|
306
435
|
}
|
|
307
436
|
}
|
|
308
437
|
if len(a.Values) > 0 {
|
|
@@ -366,6 +495,14 @@ func (c *GoToTSCompiler) WriteImportSpec(a *ast.ImportSpec) {
|
|
|
366
495
|
|
|
367
496
|
// WriteInterfaceMethodSignature writes a TypeScript interface method signature from a Go ast.Field.
|
|
368
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
|
+
|
|
369
506
|
if len(field.Names) == 0 {
|
|
370
507
|
// Should not happen for named methods in an interface, but handle defensively
|
|
371
508
|
return fmt.Errorf("interface method field has no name")
|