99 "reflect"
1010 "sort"
1111 "strings"
12+ "time"
1213
1314 "github.com/glojurelang/glojure/pkg/ast"
1415 "github.com/glojurelang/glojure/pkg/lang"
@@ -468,6 +469,11 @@ func (g *Generator) generateValue(value any) string {
468469 return fmt.Sprintf("int64(%d)", v)
469470 case float64:
470471 return fmt.Sprintf("float64(%g)", v)
472+ case float32:
473+ return fmt.Sprintf("float32(%g)", v)
474+ case time.Duration:
475+ alias := g.addImportWithAlias("time")
476+ return fmt.Sprintf("%s.Duration(%d)", alias, int64(v))
471477 case *lang.BigDecimal:
472478 return g.generateBigDecimalValue(v)
473479 case bool:
@@ -517,10 +523,14 @@ func (g *Generator) generateTypeValue(t reflect.Type) string {
517523// generateZeroValueExpr generates a Go expression that creates a zero value
518524// of the given type, handling package imports as needed
519525func (g *Generator) generateZeroValueExpr(t reflect.Type) string {
520- // TODO: review this LLM slop
526+ // TODO: review this LLM slop. for numeric types, return the type
527+ // cast of 0 with the (possibly aliased) type name
521528 switch {
522529 case t == reflect.TypeOf(lang.NewChar('a')):
523530 return "lang.NewChar(0)"
531+ case t == reflect.TypeOf(time.Duration(0)):
532+ alias := g.addImportWithAlias("time")
533+ return fmt.Sprintf("%s.Duration(0)", alias)
524534 }
525535
526536 switch t.Kind() {
@@ -975,8 +985,7 @@ func (g *Generator) generateASTNode(node *ast.Node) (res string) {
975985 fmt.Println("Def not yet implemented; returning nil")
976986 return "nil"
977987 case ast.OpSetBang:
978- fmt.Println("SetBang not yet implemented; returning nil")
979- return "nil"
988+ return g.generateSetBang(node)
980989 case ast.OpLetFn:
981990 fmt.Println("LetFn not yet implemented; returning nil")
982991 return "nil"
@@ -1469,6 +1478,7 @@ var (
14691478 "java.io.StringReader": true,
14701479 "java.util.concurrent.CountDownLatch": true,
14711480 "java.util.concurrent": true,
1481+ "java.lang": true,
14721482 }
14731483)
14741484
@@ -1489,6 +1499,10 @@ func (g *Generator) generateMaybeClass(node *ast.Node) string {
14891499 }
14901500 }
14911501
1502+ return g.generateGoExportedName(pkg)
1503+ }
1504+
1505+ func (g *Generator) generateGoExportedName(pkg string) string {
14921506 // find last dot in the package name
14931507 dotIndex := strings.LastIndex(pkg, ".")
14941508 if dotIndex == -1 {
@@ -1510,6 +1524,11 @@ func (g *Generator) generateMaybeClass(node *ast.Node) string {
15101524 }
15111525 alias := g.addImportWithAlias(packageName)
15121526
1527+ if strings.HasPrefix(exportedName, "*") {
1528+ // pointers look like package.*Type
1529+ exportedName = exportedName[1:]
1530+ alias = "*" + alias
1531+ }
15131532 return alias + "." + exportedName
15141533}
15151534
@@ -1588,28 +1607,106 @@ func (g *Generator) generateTheVar(node *ast.Node) string {
15881607 return resultId
15891608}
15901609
1610+ // generateSetBang generates code for a set! operation
1611+ func (g *Generator) generateSetBang(node *ast.Node) string {
1612+ setBangNode := node.Sub.(*ast.SetBangNode)
1613+
1614+ // Generate the value expression
1615+ valExpr := g.generateASTNode(setBangNode.Val)
1616+
1617+ // Handle the target
1618+ target := setBangNode.Target
1619+ switch target.Op {
1620+ case ast.OpVar:
1621+ // Setting a Var
1622+ varNode := target.Sub.(*ast.VarNode)
1623+ varNamespace := varNode.Var.Namespace()
1624+ varSymbol := varNode.Var.Symbol()
1625+
1626+ // Look up the var variable
1627+ varId := g.allocVarVar(varNamespace.Name().String(), varSymbol.String())
1628+
1629+ // Call Set on the Var and return the value
1630+ resultId := g.allocateTempVar()
1631+ g.writef("%s := %s.Set(%s)\n", resultId, varId, valExpr)
1632+ return resultId
1633+
1634+ case ast.OpHostInterop:
1635+ // Setting a host field
1636+ interopNode := target.Sub.(*ast.HostInteropNode)
1637+ tgt := interopNode.Target
1638+ targetExpr := g.generateASTNode(tgt)
1639+ field := interopNode.MOrF
1640+
1641+ resultId := g.allocateTempVar()
1642+
1643+ // Generate reflection-based field setting
1644+ g.writef("// set! host field\n")
1645+ g.writef("var %s any\n", resultId)
1646+ g.writef("{\n")
1647+ g.writef(" targetV := reflect.ValueOf(%s)\n", targetExpr)
1648+ g.writef(" if targetV.Kind() == reflect.Ptr {\n")
1649+ g.writef(" targetV = targetV.Elem()\n")
1650+ g.writef(" }\n")
1651+ g.writef(" fieldVal := targetV.FieldByName(%q)\n", field.Name())
1652+ g.writef(" if !fieldVal.IsValid() {\n")
1653+ g.writef(" panic(fmt.Errorf(\"no such field %s\"))\n", field.Name())
1654+ g.writef(" }\n")
1655+ g.writef(" if !fieldVal.CanSet() {\n")
1656+ g.writef(" panic(fmt.Errorf(\"cannot set field %s\"))\n", field.Name())
1657+ g.writef(" }\n")
1658+ g.writef(" valV := reflect.ValueOf(%s)\n", valExpr)
1659+ g.writef(" if !valV.IsValid() {\n")
1660+ g.writef(" switch fieldVal.Kind() {\n")
1661+ g.writef(" case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice, reflect.UnsafePointer:\n")
1662+ g.writef(" fieldVal.Set(reflect.Zero(fieldVal.Type()))\n")
1663+ g.writef(" default:\n")
1664+ g.writef(" panic(fmt.Errorf(\"cannot set field %s to nil\"))\n", field.Name())
1665+ g.writef(" }\n")
1666+ g.writef(" } else {\n")
1667+ g.writef(" fieldVal.Set(valV)\n")
1668+ g.writef(" }\n")
1669+ g.writef(" %s = %s\n", resultId, valExpr)
1670+ g.writef("}\n")
1671+ return resultId
1672+
1673+ default:
1674+ //return fmt.Sprintf("%q", "unimplemented: set! target type")
1675+ return `"unimplemented: set! target type"`
1676+ //panic(fmt.Sprintf("unsupported set! target: %v", target.Op))
1677+ }
1678+ }
1679+
15911680func (g *Generator) generateNew(node *ast.Node) string {
15921681 newNode := node.Sub.(*ast.NewNode)
15931682
15941683 // the interpreter is more lax; it allows for expressions that evaluate to a type
15951684 // here we assume the class is a constant type. clojure's new form is similar
1596- constNode, ok := newNode.Class.Sub.(*ast.ConstNode)
1597- if !ok {
1598- fmt.Println("Warning: glojure codegen only supports new with constant class types.")
1599- return fmt.Sprintf("%q", "unimplemented: new with non-constant class type")
1600- }
1601-
1602- class, ok := constNode.Value.(reflect.Type)
1603- if !ok {
1604- fmt.Println("Warning: glojure codegen only supports new with constant class types.")
1685+ switch sub := newNode.Class.Sub.(type) {
1686+ case *ast.ConstNode:
1687+ class, ok := sub.Value.(reflect.Type)
1688+ if !ok {
1689+ fmt.Printf("Warning: glojure codegen only supports new with constant class types. Got %T\n", sub.Value)
1690+ return fmt.Sprintf("%q", "unimplemented: new with non-constant class type")
1691+ }
1692+ // generate a reflect.Type for the class
1693+ classId := g.generateValue(class)
1694+ resultId := g.allocateTempVar()
1695+ g.writef("%s := reflect.New(%s).Interface()\n", resultId, classId)
1696+ return resultId
1697+ case *ast.MaybeClassNode:
1698+ resultId := g.allocateTempVar()
1699+ className := g.generateGoExportedName(sub.Class.(*lang.Symbol).FullName())
1700+ if className == "nil" {
1701+ fmt.Printf("Failed to resolve class for new, generating nil: %v\n", sub.Class)
1702+ return "nil"
1703+ }
1704+ g.writef("%s := new(%s)\n", resultId, className)
1705+ return resultId
1706+ default:
1707+ fmt.Printf("Warning: glojure codegen only supports new with constant class types. Got %T\n", newNode.Class.Sub)
16051708 return fmt.Sprintf("%q", "unimplemented: new with non-constant class type")
16061709 }
1607-
1608- // generate a reflect.Type for the class
1609- classId := g.generateValue(class)
1610- resultId := g.allocateTempVar()
1611- g.writef("%s := reflect.New(%s).Interface()\n", resultId, classId)
1612- return resultId
16131710}
16141711
16151712////////////////////////////////////////////////////////////////////////////////
@@ -1947,7 +2044,12 @@ func isRuntimeOwnedVar(v *lang.Var) bool {
19472044}
19482045
19492046func getWellKnownFunctionName(fn any) (string, bool) {
1950- ptr := reflect.ValueOf(fn).Pointer()
2047+ val := reflect.ValueOf(fn)
2048+ // ensure it's a function
2049+ if val.Kind() != reflect.Func {
2050+ return "", false
2051+ }
2052+ ptr := val.Pointer()
19512053 name, ok := wellKnownFunctions[ptr]
19522054 return name, ok
19532055}
0 commit comments