Ki.KD-Go is the Go implementation of Ki Data / Ki Declarative Language.
It parses KD documents into Go types, supports snip resolution, and exposes a small helper API for working with tags, calls, maps, grids, coordinates, quantities, temporal values, and snips.
Reference docs for the language live at:
The repository is in a good place for practical use:
- parser/value behavior is driven by the Kotlin
Ki.KD-JVMimplementation - the main JVM parser and snip suites are covered in Go tests
- the original JVM fixtures in
examples/parse in Go - helper/read APIs exist for the common Kotlin-style workflows
See jvm_test_parity_checklist.md for the current JVM-to-Go mapping.
go get github.com/kixi-io/Ki.KD-GoThe module path is:
import kikdgo "github.com/kixi-io/Ki.KD-Go"package main
import (
"fmt"
"log"
kikdgo "github.com/kixi-io/Ki.KD-Go"
)
func main() {
doc, err := kikdgo.Parse(`
person "Alice" age=30 {
email alice@example.com
}
`)
if err != nil {
log.Fatal(err)
}
fmt.Println(doc.Name()) // person
fmt.Println(doc.Value()) // Alice
fmt.Println(doc.GetAttributeOrNil("age")) // 30
email := doc.GetChild("email")
fmt.Println(email.Value()) // alice@example.com
}Use Parse / Read for text, ParseFile / ReadFile for files, and ReadReader when you already have an io.Reader.
tag, err := kikdgo.Read(`server host="localhost" port=8080`)tag, err := kikdgo.ReadFile("config.kd")If the input contains a single top-level tag, that tag is returned directly. If it contains multiple top-level tags, they are wrapped in a root tag named root.
Plain Parse does not resolve snips. Use the snip-aware entrypoints when you want .snip(...) references expanded.
resolver := kikdgo.NewSnipResolver(kikdgo.DefaultSnipOptions())
tag, err := kikdgo.ReadFileWithSnips("app.kd", resolver)Or parse text relative to a base directory:
tag, err := kikdgo.ReadWithSnips(`
app {
.snip(shared/header)
}
`, "/path/to/project", nil)Use ReadLiteral when you want the first parsed value instead of the enclosing tag.
value, err := kikdgo.ReadLiteral(`2024/3/15`)
// value is kikdgo.LocalDateExamples:
price, _ := kikdgo.ReadLiteral(`$29.99`)
when, _ := kikdgo.ReadLiteral(`2024/3/15@14:30:00-US/PST`)
rangeValue, _ := kikdgo.ReadLiteral(`1..<10`)Use Format for canonical KD literal output, Tag.String() for rendered tags/documents, and UnresolvedSnipString() when you want quoted snip literals shown in their original .snip(...) form.
value := kikdgo.NewQuantity(5, kikdgo.UnitCM)
fmt.Println(kikdgo.Format(value)) // 5cm:itag, _ := kikdgo.Parse(`person "Alice" age=30`)
fmt.Println(tag.String()) // person "Alice" age=30doc, _ := kikdgo.Parse(`layout { ".snip(shared/header)" }`)
fmt.Println(doc.UnresolvedSnipString())Tag includes helper methods for the common config/document traversal cases:
GetChild,GetChildrenFindChild,FindChildrenDescendantsGetAttribute,HasAttribute,SetAttributeGetProperty,HasProperty,GetPropertiesChildrenValues
Example:
config, _ := kikdgo.Parse(`
config env="prod" {
host = "localhost"
port = 8080
}
`)
host, _ := config.GetProperty("host")
env, _ := config.GetProperty("env")
fmt.Println(host) // localhost
fmt.Println(env) // prodGetProperty checks the tag's own attributes first, then immediate child attributes, which is useful for config-style KD.
The parser produces native Go values where that preserves meaning, and explicit types where Kotlin distinguishes values more precisely.
Common outputs include:
string,rune,bool,int64,float32,float64,nilkikdgo.Decimalkikdgo.Emailkikdgo.Versiontime.Durationkikdgo.LocalDatekikdgo.LocalDateTimekikdgo.OffsetDateTimekikdgo.KiTZDateTimekikdgo.Quantitykikdgo.Range[]anykikdgo.KDMapkikdgo.Callkikdgo.GeoPointkikdgo.Coordinatekikdgo.Gridkikdgo.Snip
Most parsed value types now also have KD string renderers, so fmt.Println(value) is useful for Tag, Call, Range, Quantity, Coordinate, GeoPoint, Grid, and Snip.
The repository includes JVM parity fixtures in examples/:
KDTest.kdKDTestWorld.kdKDTestScience.kd
They are used by the Go test suite and are a good source of real parser examples.
Run the full test suite with:
go test ./...If you are changing parser behavior, read CONTRIBUTING.md first. The short version is:
- treat
Ki.KD-JVMas the contract - port or extend tests with the change
- keep file names descriptive
- preserve the existing small-file Go style