Skip to content

Commit a1a3bda

Browse files
committed
Implement set!
Signed-off-by: James Hamlin <jfhamlin@gmail.com>
1 parent 16a0116 commit a1a3bda

File tree

19 files changed

+4082
-3484
lines changed

19 files changed

+4082
-3484
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ pkg/stdlib/glojure/%.glj: scripts/rewrite-core/originals/%.clj scripts/rewrite-c
4343
@mkdir -p $(dir $@)
4444
@scripts/rewrite-core/run.sh $< > $@
4545

46-
bin/%/glj: $(wildcard ./cmd/glj/*.go) $(wildcard ./pkg/**/*.go) $(wildcard ./internal/**/*.go)
46+
bin/%/glj: generate $(wildcard ./cmd/glj/*.go) $(wildcard ./pkg/**/*.go) $(wildcard ./internal/**/*.go)
4747
@echo "Building $@"
4848
@mkdir -p $(dir $@)
4949
@scripts/build-glj.sh $@ $*

pkg/gen/gljimports/gljimports_darwin_amd64.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3521,6 +3521,7 @@ func RegisterImports(_register func(string, interface{})) {
35213521
_register("github.com/glojurelang/glojure/pkg/lang.GT", github_com_glojurelang_glojure_pkg_lang.GT)
35223522
_register("github.com/glojurelang/glojure/pkg/lang.Get", github_com_glojurelang_glojure_pkg_lang.Get)
35233523
_register("github.com/glojurelang/glojure/pkg/lang.GetDefault", github_com_glojurelang_glojure_pkg_lang.GetDefault)
3524+
_register("github.com/glojurelang/glojure/pkg/lang.GetPersistentStructMapAccessor", github_com_glojurelang_glojure_pkg_lang.GetPersistentStructMapAccessor)
35243525
_register("github.com/glojurelang/glojure/pkg/lang.GlobalEnv", github_com_glojurelang_glojure_pkg_lang.GlobalEnv)
35253526
_register("github.com/glojurelang/glojure/pkg/lang.GoAppend", github_com_glojurelang_glojure_pkg_lang.GoAppend)
35263527
_register("github.com/glojurelang/glojure/pkg/lang.GoCap", github_com_glojurelang_glojure_pkg_lang.GoCap)

pkg/gen/gljimports/gljimports_darwin_arm64.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3521,6 +3521,7 @@ func RegisterImports(_register func(string, interface{})) {
35213521
_register("github.com/glojurelang/glojure/pkg/lang.GT", github_com_glojurelang_glojure_pkg_lang.GT)
35223522
_register("github.com/glojurelang/glojure/pkg/lang.Get", github_com_glojurelang_glojure_pkg_lang.Get)
35233523
_register("github.com/glojurelang/glojure/pkg/lang.GetDefault", github_com_glojurelang_glojure_pkg_lang.GetDefault)
3524+
_register("github.com/glojurelang/glojure/pkg/lang.GetPersistentStructMapAccessor", github_com_glojurelang_glojure_pkg_lang.GetPersistentStructMapAccessor)
35243525
_register("github.com/glojurelang/glojure/pkg/lang.GlobalEnv", github_com_glojurelang_glojure_pkg_lang.GlobalEnv)
35253526
_register("github.com/glojurelang/glojure/pkg/lang.GoAppend", github_com_glojurelang_glojure_pkg_lang.GoAppend)
35263527
_register("github.com/glojurelang/glojure/pkg/lang.GoCap", github_com_glojurelang_glojure_pkg_lang.GoCap)

pkg/gen/gljimports/gljimports_js_wasm.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3521,6 +3521,7 @@ func RegisterImports(_register func(string, interface{})) {
35213521
_register("github.com/glojurelang/glojure/pkg/lang.GT", github_com_glojurelang_glojure_pkg_lang.GT)
35223522
_register("github.com/glojurelang/glojure/pkg/lang.Get", github_com_glojurelang_glojure_pkg_lang.Get)
35233523
_register("github.com/glojurelang/glojure/pkg/lang.GetDefault", github_com_glojurelang_glojure_pkg_lang.GetDefault)
3524+
_register("github.com/glojurelang/glojure/pkg/lang.GetPersistentStructMapAccessor", github_com_glojurelang_glojure_pkg_lang.GetPersistentStructMapAccessor)
35243525
_register("github.com/glojurelang/glojure/pkg/lang.GlobalEnv", github_com_glojurelang_glojure_pkg_lang.GlobalEnv)
35253526
_register("github.com/glojurelang/glojure/pkg/lang.GoAppend", github_com_glojurelang_glojure_pkg_lang.GoAppend)
35263527
_register("github.com/glojurelang/glojure/pkg/lang.GoCap", github_com_glojurelang_glojure_pkg_lang.GoCap)

pkg/gen/gljimports/gljimports_linux_amd64.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3521,6 +3521,7 @@ func RegisterImports(_register func(string, interface{})) {
35213521
_register("github.com/glojurelang/glojure/pkg/lang.GT", github_com_glojurelang_glojure_pkg_lang.GT)
35223522
_register("github.com/glojurelang/glojure/pkg/lang.Get", github_com_glojurelang_glojure_pkg_lang.Get)
35233523
_register("github.com/glojurelang/glojure/pkg/lang.GetDefault", github_com_glojurelang_glojure_pkg_lang.GetDefault)
3524+
_register("github.com/glojurelang/glojure/pkg/lang.GetPersistentStructMapAccessor", github_com_glojurelang_glojure_pkg_lang.GetPersistentStructMapAccessor)
35243525
_register("github.com/glojurelang/glojure/pkg/lang.GlobalEnv", github_com_glojurelang_glojure_pkg_lang.GlobalEnv)
35253526
_register("github.com/glojurelang/glojure/pkg/lang.GoAppend", github_com_glojurelang_glojure_pkg_lang.GoAppend)
35263527
_register("github.com/glojurelang/glojure/pkg/lang.GoCap", github_com_glojurelang_glojure_pkg_lang.GoCap)

pkg/gen/gljimports/gljimports_linux_arm64.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3521,6 +3521,7 @@ func RegisterImports(_register func(string, interface{})) {
35213521
_register("github.com/glojurelang/glojure/pkg/lang.GT", github_com_glojurelang_glojure_pkg_lang.GT)
35223522
_register("github.com/glojurelang/glojure/pkg/lang.Get", github_com_glojurelang_glojure_pkg_lang.Get)
35233523
_register("github.com/glojurelang/glojure/pkg/lang.GetDefault", github_com_glojurelang_glojure_pkg_lang.GetDefault)
3524+
_register("github.com/glojurelang/glojure/pkg/lang.GetPersistentStructMapAccessor", github_com_glojurelang_glojure_pkg_lang.GetPersistentStructMapAccessor)
35243525
_register("github.com/glojurelang/glojure/pkg/lang.GlobalEnv", github_com_glojurelang_glojure_pkg_lang.GlobalEnv)
35253526
_register("github.com/glojurelang/glojure/pkg/lang.GoAppend", github_com_glojurelang_glojure_pkg_lang.GoAppend)
35263527
_register("github.com/glojurelang/glojure/pkg/lang.GoCap", github_com_glojurelang_glojure_pkg_lang.GoCap)

pkg/gen/gljimports/gljimports_windows_amd64.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3521,6 +3521,7 @@ func RegisterImports(_register func(string, interface{})) {
35213521
_register("github.com/glojurelang/glojure/pkg/lang.GT", github_com_glojurelang_glojure_pkg_lang.GT)
35223522
_register("github.com/glojurelang/glojure/pkg/lang.Get", github_com_glojurelang_glojure_pkg_lang.Get)
35233523
_register("github.com/glojurelang/glojure/pkg/lang.GetDefault", github_com_glojurelang_glojure_pkg_lang.GetDefault)
3524+
_register("github.com/glojurelang/glojure/pkg/lang.GetPersistentStructMapAccessor", github_com_glojurelang_glojure_pkg_lang.GetPersistentStructMapAccessor)
35243525
_register("github.com/glojurelang/glojure/pkg/lang.GlobalEnv", github_com_glojurelang_glojure_pkg_lang.GlobalEnv)
35253526
_register("github.com/glojurelang/glojure/pkg/lang.GoAppend", github_com_glojurelang_glojure_pkg_lang.GoAppend)
35263527
_register("github.com/glojurelang/glojure/pkg/lang.GoCap", github_com_glojurelang_glojure_pkg_lang.GoCap)

pkg/gen/gljimports/gljimports_windows_arm.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3521,6 +3521,7 @@ func RegisterImports(_register func(string, interface{})) {
35213521
_register("github.com/glojurelang/glojure/pkg/lang.GT", github_com_glojurelang_glojure_pkg_lang.GT)
35223522
_register("github.com/glojurelang/glojure/pkg/lang.Get", github_com_glojurelang_glojure_pkg_lang.Get)
35233523
_register("github.com/glojurelang/glojure/pkg/lang.GetDefault", github_com_glojurelang_glojure_pkg_lang.GetDefault)
3524+
_register("github.com/glojurelang/glojure/pkg/lang.GetPersistentStructMapAccessor", github_com_glojurelang_glojure_pkg_lang.GetPersistentStructMapAccessor)
35243525
_register("github.com/glojurelang/glojure/pkg/lang.GlobalEnv", github_com_glojurelang_glojure_pkg_lang.GlobalEnv)
35253526
_register("github.com/glojurelang/glojure/pkg/lang.GoAppend", github_com_glojurelang_glojure_pkg_lang.GoAppend)
35263527
_register("github.com/glojurelang/glojure/pkg/lang.GoCap", github_com_glojurelang_glojure_pkg_lang.GoCap)

pkg/lang/persistentstructmap.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ func CreatePersistentStructMapSlotMap(keys ISeq) *PersistentStructMapDef {
108108
}
109109
}
110110

111+
func GetPersistentStructMapAccessor() any {
112+
panic("not implemented")
113+
}
114+
111115
func newPersistentStructMap(meta IPersistentMap, def *PersistentStructMapDef, vals []any, ext IPersistentMap) *PersistentStructMap {
112116
return &PersistentStructMap{
113117
meta: meta,

pkg/runtime/codegen.go

Lines changed: 121 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
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
519525
func (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+
15911680
func (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

19492046
func 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

Comments
 (0)