diff options
| author | Christine Dodrill <me@christine.website> | 2019-04-01 10:05:03 -0700 |
|---|---|---|
| committer | Christine Dodrill <me@christine.website> | 2019-04-01 10:05:28 -0700 |
| commit | f06f021f402270951f849dde7bee3f3340b8a1d5 (patch) | |
| tree | baee337aab524f162b349d254d21c2d8f2716d44 /cmd | |
| parent | ba91a17859267201b1d1f0e71da465b1464d940f (diff) | |
| download | x-f06f021f402270951f849dde7bee3f3340b8a1d5.tar.xz x-f06f021f402270951f849dde7bee3f3340b8a1d5.zip | |
reorg
Diffstat (limited to 'cmd')
31 files changed, 2656 insertions, 0 deletions
diff --git a/cmd/appsluggr/main.go b/cmd/appsluggr/main.go new file mode 100644 index 0000000..b4701a1 --- /dev/null +++ b/cmd/appsluggr/main.go @@ -0,0 +1,153 @@ +package main + +import ( + "archive/tar" + "compress/gzip" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "path/filepath" + "strings" + + "github.com/otiai10/copy" + "github.com/Xe/x/internal" +) + +var ( + web = flag.String("web", "", "path to binary for web process") + webScale = flag.Int("web-scale", 1, "default scale for web process if defined") + worker = flag.String("worker", "", "path to binary for worker process") + workerScale = flag.Int("worker-scale", 1, "default scale for worker process if defined") + fname = flag.String("fname", "slug.tar.gz", "slug name") +) + +func main() { + internal.HandleStartup() + + fout, err := os.Create(*fname) + if err != nil { + log.Fatal(err) + } + defer fout.Close() + + gzw := gzip.NewWriter(fout) + defer gzw.Close() + + tw := tar.NewWriter(gzw) + defer tw.Close() + + dir, err := ioutil.TempDir("", "appsluggr") + if err != nil { + log.Fatal(err) + } + + defer os.RemoveAll(dir) // clean up + + os.MkdirAll(filepath.Join(dir, "bin"), 0777) + var procfile, scalefile string + + copy.Copy("translations", filepath.Join(dir, "translations")) + if *web != "" { + procfile += "web: /app/bin/web\n" + scalefile += fmt.Sprintf("web=%d", *webScale) + Copy(*web, filepath.Join(dir, "bin", "web")) + + } + if *worker != "" { + procfile += "worker: /app/bin/worker\n" + scalefile += fmt.Sprintf("worker=%d", *workerScale) + Copy(*worker, filepath.Join(dir, "bin", "worker")) + } + + os.MkdirAll(filepath.Join(dir, ".config"), 0777) + + err = ioutil.WriteFile(filepath.Join(dir, ".buildpacks"), []byte("https://github.com/ryandotsmith/null-buildpack"), 0666) + if err != nil { + log.Fatal(err) + } + err = ioutil.WriteFile(filepath.Join(dir, "DOKKU_SCALE"), []byte(scalefile), 0666) + if err != nil { + log.Fatal(err) + } + err = ioutil.WriteFile(filepath.Join(dir, "Procfile"), []byte(procfile), 0666) + if err != nil { + log.Fatal(err) + } + + filepath.Walk(dir, func(file string, fi os.FileInfo, err error) error { + // return on any error + if err != nil { + log.Printf("got error on %s: %v", file, err) + return err + } + + if fi.IsDir() { + return nil // not a file. ignore. + } + + // create a new dir/file header + header, err := tar.FileInfoHeader(fi, fi.Name()) + if err != nil { + return err + } + + // update the name to correctly reflect the desired destination when untaring + header.Name = strings.TrimPrefix(strings.Replace(file, dir, "", -1), string(filepath.Separator)) + + // write the header + if err := tw.WriteHeader(header); err != nil { + return err + } + + // return on non-regular files (thanks to [kumo](https://medium.com/@komuw/just-like-you-did-fbdd7df829d3) for this suggested update) + if !fi.Mode().IsRegular() { + return nil + } + + // open files for taring + f, err := os.Open(file) + if err != nil { + return err + } + + // copy file data into tar writer + if _, err := io.Copy(tw, f); err != nil { + return err + } + + // manually close here after each file operation; defering would cause each file close + // to wait until all operations have completed. + f.Close() + + return nil + }) +} + +// Copy the src file to dst. Any existing file will be overwritten and will not +// copy file attributes. +func Copy(src, dst string) error { + in, err := os.Open(src) + if err != nil { + return err + } + defer in.Close() + st, err := in.Stat() + if err != nil { + return err + } + + out, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY, st.Mode()) + if err != nil { + return err + } + defer out.Close() + + _, err = io.Copy(out, in) + if err != nil { + return err + } + return out.Close() +} diff --git a/cmd/dnsd/dnsd.conf b/cmd/dnsd/dnsd.conf new file mode 100644 index 0000000..e490d13 --- /dev/null +++ b/cmd/dnsd/dnsd.conf @@ -0,0 +1,7 @@ +port 5900 +forward-server 1.1.1.1:53 + +zone-url ( + https://xena.greedo.xeserv.us/files/akua.zone + https://xena.greedo.xeserv.us/files/adblock.zone +)
\ No newline at end of file diff --git a/cmd/dnsd/main.go b/cmd/dnsd/main.go new file mode 100644 index 0000000..314c86d --- /dev/null +++ b/cmd/dnsd/main.go @@ -0,0 +1,128 @@ +package main + +import ( + "bufio" + "flag" + "log" + "net/http" + + "os" + "os/signal" + "syscall" + + "github.com/Xe/x/internal" + "github.com/miekg/dns" + "github.com/mmikulicic/stringlist" +) + +var ( + port = flag.String("port", "53", "UDP port to listen on for DNS") + server = flag.String("forward-server", "1.1.1.1:53", "forward DNS server") + + zoneURLs = stringlist.Flag("zone-url", "DNS zonefiles to load") +) + +var ( + defaultZoneURLS = []string{ + "https://xena.greedo.xeserv.us/files/akua.zone", + "https://xena.greedo.xeserv.us/files/adblock.zone", + } +) + +func main() { + internal.HandleStartup() + + if len(*zoneURLs) == 0 { + *zoneURLs = defaultZoneURLS + } + + for _, zurl := range *zoneURLs { + log.Printf("conf: -zone-url=%s", zurl) + } + log.Printf("conf: -port=%s", *port) + log.Printf("conf: -forward-server=%s", *server) + + rrs := []dns.RR{} + + for _, zurl := range *zoneURLs { + resp, err := http.Get(zurl) + if err != nil { + panic(err) + } + + reader := bufio.NewReaderSize(resp.Body, 2048) + + var i int + zp := dns.NewZoneParser(reader, "", zurl) + for rr, ok := zp.Next(); ok; rr, ok = zp.Next() { + rrs = append(rrs, rr) + i++ + } + + if zp.Err() != nil { + panic(zp.Err()) + } + + resp.Body.Close() + + log.Printf("%s: %d records", zurl, i) + } + + dns.HandleFunc(".", func(w dns.ResponseWriter, r *dns.Msg) { + m := new(dns.Msg) + m.SetReply(r) + m.Authoritative = true + + for _, q := range r.Question { + answers := []dns.RR{} + for _, rr := range rrs { + rh := rr.Header() + + if rh.Rrtype == dns.TypeCNAME && q.Name == rh.Name { + answers = append(answers, rr) + + for _, a := range resolver("127.0.0.1:"+*port, rr.(*dns.CNAME).Target, q.Qtype) { + answers = append(answers, a) + } + } + + if q.Name == rh.Name && q.Qtype == rh.Rrtype && q.Qclass == rh.Class { + answers = append(answers, rr) + } + } + if len(answers) == 0 && *server != "" { + for _, a := range resolver(*server, q.Name, q.Qtype) { + answers = append(answers, a) + } + } + for _, a := range answers { + m.Answer = append(m.Answer, a) + } + } + w.WriteMsg(m) + }) + + go func() { + srv := &dns.Server{Addr: ":" + *port, Net: "udp"} + if err := srv.ListenAndServe(); err != nil { + log.Fatalf("Failed to set udp listener %s\n", err.Error()) + } + }() + + sig := make(chan os.Signal) + signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) + s := <-sig + log.Fatalf("Signal (%v) received, stopping\n", s) +} + +func resolver(server, fqdn string, r_type uint16) []dns.RR { + m1 := new(dns.Msg) + m1.Id = dns.Id() + m1.SetQuestion(fqdn, r_type) + + in, err := dns.Exchange(m1, server) + if err == nil { + return in.Answer + } + return []dns.RR{} +} diff --git a/cmd/ff-primitive/main.go b/cmd/ff-primitive/main.go new file mode 100644 index 0000000..3ee0882 --- /dev/null +++ b/cmd/ff-primitive/main.go @@ -0,0 +1,55 @@ +package main + +import ( + "flag" + "image" + "log" + "os" + "runtime" + "runtime/pprof" + + "github.com/Xe/x/internal" + "github.com/fogleman/primitive/primitive" + farbfeld "github.com/hullerob/go.farbfeld" +) + +var ( + shapeCount = flag.Int("count", 150, "number of shapes used") + repeatShapeCount = flag.Int("repeat-count", 0, "number of extra shapes drawn in each step") + alpha = flag.Int("alpha", 128, "alpha of all shapes") + cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") +) + +func stepImg(img image.Image, count int) image.Image { + bg := primitive.MakeColor(primitive.AverageImageColor(img)) + model := primitive.NewModel(img, bg, 512, runtime.NumCPU()) + + for range make([]struct{}, count) { + model.Step(primitive.ShapeTypeTriangle, *alpha, *repeatShapeCount) + } + + return model.Context.Image() +} + +func main() { + internal.HandleStartup() + + if *cpuprofile != "" { + f, err := os.Create(*cpuprofile) + if err != nil { + log.Fatal(err) + } + pprof.StartCPUProfile(f) + defer pprof.StopCPUProfile() + } + + img, err := farbfeld.Decode(os.Stdin) + if err != nil { + log.Fatal(err) + } + + err = farbfeld.Encode(os.Stdout, stepImg(img, *shapeCount)) + if err != nil { + log.Fatal(err) + } +} diff --git a/cmd/johaus/build.go b/cmd/johaus/build.go new file mode 100644 index 0000000..07e8489 --- /dev/null +++ b/cmd/johaus/build.go @@ -0,0 +1,39 @@ +//+build ignore + +// Builds and deploys the application to minipaas. +package main + +import ( + "context" + "log" + "os" + + "github.com/Xe/x/internal/greedo" + "github.com/Xe/x/internal/minipaas" + "github.com/Xe/x/internal/yeet" +) + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + env := append(os.Environ(), []string{"CGO_ENABLED=0", "GOOS=linux"}...) + yeet.ShouldWork(ctx, env, yeet.WD, "vgo", "build", "-o=web") + yeet.ShouldWork(ctx, env, yeet.WD, "appsluggr", "-web=web") + fin, err := os.Open("slug.tar.gz") + if err != nil { + log.Fatal(err) + } + defer fin.Close() + + fname := "johaus-" + yeet.DateTag + ".tar.gz" + pubURL, err := greedo.CopyFile(fname, fin) + if err != nil { + log.Fatal(err) + } + + err = minipaas.Exec("tar:from johaus " + pubURL) + if err != nil { + log.Fatal(err) + } +} diff --git a/cmd/johaus/main.go b/cmd/johaus/main.go new file mode 100644 index 0000000..5c9c59e --- /dev/null +++ b/cmd/johaus/main.go @@ -0,0 +1,72 @@ +package main + +import ( + "encoding/json" + "flag" + "io/ioutil" + "log" + "net/http" + + "github.com/Xe/x/internal" + "within.website/johaus/parser" + _ "within.website/johaus/parser/camxes" + "within.website/johaus/pretty" +) + +const dialect = "camxes" + +var ( + port = flag.String("port", "9001", "TCP port to bind on for HTTP") +) + +func main() { + internal.HandleStartup() + + log.Printf("Listening on http://0.0.0.0:%s", *port) + + http.DefaultServeMux.HandleFunc("/tree", tree) + http.DefaultServeMux.HandleFunc("/braces", braces) + + http.ListenAndServe(":"+*port, nil) +} + +func braces(w http.ResponseWriter, r *http.Request) { + data, err := ioutil.ReadAll(r.Body) + if err != nil { + http.Error(w, "can't parse: "+err.Error(), http.StatusBadRequest) + return + } + + tree, err := parser.Parse(dialect, string(data)) + if err != nil { + http.Error(w, "can't parse: "+err.Error(), http.StatusBadRequest) + return + } + + parser.RemoveMorphology(tree) + parser.AddElidedTerminators(tree) + parser.RemoveSpace(tree) + parser.CollapseLists(tree) + + pretty.Braces(w, tree) +} +func tree(w http.ResponseWriter, r *http.Request) { + data, err := ioutil.ReadAll(r.Body) + if err != nil { + http.Error(w, "can't parse: "+err.Error(), http.StatusBadRequest) + return + } + + tree, err := parser.Parse(dialect, string(data)) + if err != nil { + http.Error(w, "can't parse: "+err.Error(), http.StatusBadRequest) + return + } + + parser.RemoveMorphology(tree) + parser.AddElidedTerminators(tree) + parser.RemoveSpace(tree) + parser.CollapseLists(tree) + + json.NewEncoder(w).Encode(tree) +} diff --git a/cmd/la-baujmi/.gitignore b/cmd/la-baujmi/.gitignore new file mode 100644 index 0000000..07b6fbf --- /dev/null +++ b/cmd/la-baujmi/.gitignore @@ -0,0 +1,2 @@ +.env +la-baujmi
\ No newline at end of file diff --git a/cmd/la-baujmi/README.md b/cmd/la-baujmi/README.md new file mode 100644 index 0000000..10aa48d --- /dev/null +++ b/cmd/la-baujmi/README.md @@ -0,0 +1,66 @@ +# la baujmi + +Combination of: + +> bangu +> +> x1 is a/the language/dialect used by x2 to express/communicate x3 (si'o/du'u, not quote). + +> jimpe +> +> x1 understands/comprehends fact/truth x2 (du'u) about subject x3; x1 understands (fi) x3. + +This is an attempt to create a tool that can understand language. + +At first, [Toki Pona](http://tokipona.net) will be used. At a high level a toki pona sentence consists of four main parts: + +- context phrase +- subject + descriptors +- verb + descriptors +- object + descriptors + +You can describe a sentence as a form of predicate relation between those four parts. If you are told "Stacy purchased a tool for strange-plant", you can later then ask the program who purchased a tool for strange-plant. + +Because a Toki Pona sentence always matches the following form: + +``` +[<name> o,] [context la] <subject> [li <verb> [e <object>]] +``` + +And the particle `seme` fills in the part of a question that you don't know. So from this we can fill in the blanks with prolog. + +Consider the following: + +``` +jan Kesi li toki. +Cadey is speaking +toki(jan_Kesi). + +jan Kesi en jan Pola li toki. +Cadey and Pola are speaking. +toki(jan_Kesi). +toki(jan_Pola). + +jan Kesi li toki e jan Pola. +Cadey is talking about Pola +toki(jan_Kesi, jan_Pola). + +jan Kesi li toki e toki pona. +Cadey is talking about toki pona. +toki(jan_Kesi, toki_pona). + +ilo Kesi o, toki e jan Kesi. +Robo-Cadey: talk about Cadey. +command(ilo_Kesi, toki(ziho, jan_Kesi)). % ziho -> nothing in lojban (zi'o) +``` + +And then we can ask prolog questions about this sentence: + +``` +seme li toki? +> toki(X). +toki(jan_Kesi). +jan Kesi li toki. +toki(jan_Pola). +jan Pola li toki. +``` diff --git a/cmd/la-baujmi/const.go b/cmd/la-baujmi/const.go new file mode 100644 index 0000000..2b70d19 --- /dev/null +++ b/cmd/la-baujmi/const.go @@ -0,0 +1,5 @@ +package main + +const ( + tokiPonaAPIURL = "https://us-central1-golden-cove-408.cloudfunctions.net/toki-pona-verb-marker" +) diff --git a/cmd/la-baujmi/corpus.txt b/cmd/la-baujmi/corpus.txt new file mode 100644 index 0000000..5313317 --- /dev/null +++ b/cmd/la-baujmi/corpus.txt @@ -0,0 +1,23 @@ +sina li lukin e ni la sina pilin pona. sina li lukin e ni la pilin pona ni li suli suwi. sina li lukin e ni la sina li pona ale. sina li lukin e ni la sina li pali e ijo mute mute. + +sina li pona ale la sina pali ijo mute mute. + +sina li jan pi pali ijo mute mute. sina ken pali ijo mute mute. + +sina li wile pali e ilo suli la sina wile jo lukin wawa e tawa ala pi tenpo ni. lukin wawa e tawa ala pi tenpo ni li ilo sina kama e pali ijo pi tenpo pini. nasin ni li pilin sina ala. sina li kama pi toki lawa insa ala e pali ijo pi tenpo pini. + +tenpo ni li ni tenpo. + +tenpo pini li tenpo ni ala. tenpo ni la tenpo pini li suli ala. + +tenpo kama li tenpo ni ala. tenpo ni la tenpo kama li suli ala. + +tenpo ni li tawa ale. sina ken tawa ale e tawa ala pi tenpo ni. + +sina wile jo tawa ala pi tenpo ni la sina wile tawa ni: + +tenpo mute anu sina pilin ni la sijelo sina suli. +tenpo mute anu sina pilin ni la sijelo sina lili. +sina lukin e ijo mute la sina lukin wawa e nena insa. + +sina ken tawa ijo mute la sina kepeken tawa ala pi tenpo ni e sina. sina jo e ni la sina jo lukin wawa pona. sina jo lukin wawa mute en tawa ala pi tenpo ni ale li pali pona e ilo suli. diff --git a/cmd/la-baujmi/fact.go b/cmd/la-baujmi/fact.go new file mode 100644 index 0000000..a421d1e --- /dev/null +++ b/cmd/la-baujmi/fact.go @@ -0,0 +1,210 @@ +package main + +import ( + "errors" + "strings" + + "github.com/Xe/x/web/tokiponatokens" +) + +// Selbri is a predicate relationship between its arguments. The name comes from +// the lojban term selbri: http://jbovlaste.lojban.org/dict/selbri. +type Selbri struct { + Predicate string + Arguments []string +} + +// Fact converts a selbri into a prolog fact. +func (s Selbri) Fact() string { + var sb strings.Builder + + var varCount byte + + sb.WriteString("selbri(verb(") + + if s.Predicate == "seme" { + sb.WriteByte(byte('A') + varCount) + varCount++ + } else { + sb.WriteString(s.Predicate) + } + sb.WriteString("), ") + + for i, arg := range s.Arguments { + if i != 0 { + sb.WriteString(", ") + } + + switch arg { + case "subject(seme)", "object(seme)": + if strings.HasPrefix(arg, "subject") { + sb.WriteString("subject(") + } else { + sb.WriteString("object(") + } + sb.WriteByte(byte('A') + varCount) + varCount++ + sb.WriteByte(')') + continue + } + sb.WriteString(arg) + } + + sb.WriteString(").") + return sb.String() +} + +var ( + // ErrNoPredicate is raised when the given sentence does not have a verb. + // This is a side effect of the way we are potentially misusing prolog + // here. + ErrNoPredicate = errors.New("la-baujmi: sentence must have a verb to function as the logical predicate") +) + +// SentenceToSelbris creates logical facts derived from toki pona sentences. +// This is intended to be the first step in loading them into prolog. +func SentenceToSelbris(s tokiponatokens.Sentence) ([]Selbri, error) { + var ( + subjects []string + verbs []string + objects []string + context string + ) + + for _, pt := range s { + switch pt.Type { + case tokiponatokens.PartSubject: + if strings.Contains(pt.Braces(), " en ") { + temp := strings.Split(strings.Join(pt.Tokens, " "), " en ") + + for _, t := range temp { + subjects = append(subjects, "subject("+t+")") + } + + continue + } + + if len(pt.Parts) != 0 { + var sb strings.Builder + sb.WriteString("subject(") + + for i, sp := range pt.Parts { + if i != 0 { + sb.WriteString(", ") + } + if sp.Sep != nil && *sp.Sep == "pi" { + sb.WriteString("pi(") + for j, tk := range sp.Tokens { + if j != 0 { + sb.WriteString(", ") + } + + sb.WriteString(tk) + } + sb.WriteString(")") + } else { + sb.WriteString(strings.Join(sp.Tokens, "_")) + } + } + + sb.WriteString(")") + + subjects = append(objects, sb.String()) + continue + } + + subjects = append(subjects, "subject("+strings.Join(pt.Tokens, "_")+")") + + case tokiponatokens.PartVerbMarker: + verbs = append(verbs, strings.Join(pt.Tokens, "_")) + + case tokiponatokens.PartObjectMarker: + if len(pt.Parts) != 0 { + var sb strings.Builder + sb.WriteString("object(") + + for i, sp := range pt.Parts { + if i != 0 { + sb.WriteString(", ") + } + if sp.Sep != nil && *sp.Sep == "pi" { + sb.WriteString("pi(") + for j, tk := range sp.Tokens { + if j != 0 { + sb.WriteString(", ") + } + + sb.WriteString(tk) + } + sb.WriteString(")") + } else { + sb.WriteString(strings.Join(sp.Tokens, "_")) + } + } + + sb.WriteString(")") + + objects = append(objects, sb.String()) + continue + } + objects = append(objects, "object("+strings.Join(pt.Tokens, "_")+")") + + case tokiponatokens.PartPunctuation: + if len(pt.Tokens) == 1 { + switch pt.Tokens[0] { + case "la": + context = "context(" + subjects[len(subjects)-1] + ")" + subjects = subjects[:len(subjects)-1] + + case tokiponatokens.PunctComma: + return nil, errors.New("please avoid commas in this function") + } + } + } + } + + if len(verbs) == 0 { + return nil, ErrNoPredicate + } + + var result []Selbri + + for _, v := range verbs { + for _, s := range subjects { + if len(objects) == 0 { + // sumti: x1 is a/the argument of predicate function x2 filling place x3 (kind/number) + var sumti []string + if context != "" { + sumti = append([]string{}, context) + } + sumti = append(sumti, s) + + r := Selbri{ + Predicate: v, + Arguments: sumti, + } + + result = append(result, r) + } + + for _, o := range objects { + // sumti: x1 is a/the argument of predicate function x2 filling place x3 (kind/number) + var sumti []string + if context != "" { + sumti = append([]string{}, context) + } + sumti = append(sumti, s) + sumti = append(sumti, o) + + r := Selbri{ + Predicate: v, + Arguments: sumti, + } + + result = append(result, r) + } + } + } + + return result, nil +} diff --git a/cmd/la-baujmi/fact_test.go b/cmd/la-baujmi/fact_test.go new file mode 100644 index 0000000..43f7a4b --- /dev/null +++ b/cmd/la-baujmi/fact_test.go @@ -0,0 +1,278 @@ +package main + +import ( + "encoding/json" + "log" + "testing" + + "github.com/Xe/x/web/tokiponatokens" + "github.com/kr/pretty" +) + +// equal tells whether a and b contain the same elements. +// A nil argument is equivalent to an empty slice. +func equal(a, b []string) bool { + if len(a) != len(b) { + return false + } + for _, v := range a { + var has bool + for _, vv := range b { + if v == vv { + has = true + } + } + if !has { + return false + } + } + return true +} + +func selbrisEqual(a, b []Selbri) bool { + if len(a) != len(b) { + return false + } + for _, v := range a { + var has bool + for _, vv := range b { + if v.Eq(vv) { + has = true + } + } + + if !has { + return false + } + } + return true +} + +// Eq checks two Selbri instances for equality. +func (lhs Selbri) Eq(rhs Selbri) bool { + switch { + case lhs.Predicate != rhs.Predicate: + return false + case len(lhs.Arguments) != len(rhs.Arguments): + return false + case len(lhs.Arguments) == len(rhs.Arguments): + return equal(lhs.Arguments, rhs.Arguments) + } + + return true +} + +func TestSentenceToSelbris(t *testing.T) { + cases := []struct { + name string + json []byte + want []Selbri + wantFacts []string + }{ + { + name: "basic", + json: []byte(`[{"part":"subject","tokens":["ona"]},{"part":"verbMarker","sep":"li","tokens":["sona"]},{"part":"objectMarker","sep":"e","tokens":["mute"]},{"part":"punctuation","tokens":["period"]}]`), + want: []Selbri{ + { + Predicate: "sona", + Arguments: []string{"subject(ona)", "object(mute)"}, + }, + }, + wantFacts: []string{"selbri(verb(sona), subject(ona), object(mute))."}, + }, + { + name: "zen", + json: []byte(`[{"part":"subject","tokens":["tenpo","ni"]},{"part":"punctuation","tokens":["la"]},{"part":"subject","tokens":["seme"]},{"part":"verbMarker","sep":"li","tokens":["ala"]}]`), + want: []Selbri{ + { + Predicate: "ala", + Arguments: []string{"context(subject(tenpo_ni))", "subject(seme)"}, + }, + }, + wantFacts: []string{"selbri(verb(ala), context(subject(tenpo_ni)), subject(A))."}, + }, + { + name: "pi_subject", + json: []byte(`[{"part":"subject","parts":[{"part":"subject","tokens":["ilo","mi"]},{"part":"subject","sep":"pi","tokens":["kasi","nasa"]}]},{"part":"verbMarker","sep":"li","tokens":["pona","ale"]}]`), + want: []Selbri{ + { + Predicate: "pona_ale", + Arguments: []string{"subject(ilo_mi, pi(kasi, nasa))"}, + }, + }, + wantFacts: []string{"selbri(verb(pona_ale), subject(ilo_mi, pi(kasi, nasa)))."}, + }, + { + name: "pi_object", + json: []byte(`[{"part":"subject","tokens":["mi"]},{"part":"verbMarker","sep":"li","tokens":["esun"]},{"part":"objectMarker","sep":"e","parts":[{"part":"objectMarker","tokens":["ilo"]},{"part":"objectMarker","sep":"pi","tokens":["kalama","musi"]}]},{"part":"punctuation","tokens":["period"]}]`), + want: []Selbri{ + { + Predicate: "esun", + Arguments: []string{"subject(mi)", "object(ilo, pi(kalama, musi))"}, + }, + }, + wantFacts: []string{"selbri(verb(esun), subject(mi), object(ilo, pi(kalama, musi)))."}, + }, + { + name: "multiple verbs", + json: []byte(`[{"part":"subject","tokens":["ona"]},{"part":"verbMarker","sep":"li","tokens":["sona"]},{"part":"verbMarker","sep":"li","tokens":["pona"]},{"part":"objectMarker","sep":"e","tokens":["mute"]},{"part":"punctuation","tokens":["period"]}]`), + want: []Selbri{ + { + Predicate: "sona", + Arguments: []string{"subject(ona)", "object(mute)"}, + }, + { + Predicate: "pona", + Arguments: []string{"subject(ona)", "object(mute)"}, + }, + }, + wantFacts: []string{ + "selbri(verb(sona), subject(ona), object(mute)).", + "selbri(verb(pona), subject(ona), object(mute)).", + }, + }, + { + name: "multiple subjects and verbs", + json: []byte(`[{"part":"subject","tokens":["ona","en","sina","en","mi"]},{"part":"verbMarker","sep":"li","tokens":["sona"]},{"part":"verbMarker","sep":"li","tokens":["pona"]},{"part":"objectMarker","sep":"e","tokens":["mute"]},{"part":"punctuation","tokens":["period"]}]`), + want: []Selbri{ + { + Predicate: "sona", + Arguments: []string{"subject(ona)", "object(mute)"}, + }, + { + Predicate: "pona", + Arguments: []string{"subject(ona)", "object(mute)"}, + }, + { + Predicate: "sona", + Arguments: []string{"subject(sina)", "object(mute)"}, + }, + { + Predicate: "pona", + Arguments: []string{"subject(sina)", "object(mute)"}, + }, + { + Predicate: "sona", + Arguments: []string{"subject(mi)", "object(mute)"}, + }, + { + Predicate: "pona", + Arguments: []string{"subject(mi)", "object(mute)"}, + }, + }, + wantFacts: []string{ + "selbri(verb(sona), subject(ona), object(mute)).", + "selbri(verb(sona), subject(sina), object(mute)).", + "selbri(verb(sona), subject(mi), object(mute)).", + "selbri(verb(pona), subject(ona), object(mute)).", + "selbri(verb(pona), subject(sina), object(mute)).", + "selbri( |
