Started work on gql server for dictionary access

This commit is contained in:
Louis Burke 2025-02-05 18:04:16 -05:00
parent 51e77feee8
commit e11a72be16
20 changed files with 7989 additions and 1 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*.ttf
shenikan_server

View file

@ -13,6 +13,11 @@ fonts: $(FONT_TTFS)
%.ttf: fonts/%.sfdir
fontforge -lang=ff -c 'Open($$1); Generate($$2)' $^ $@
tools: shenikan_server
shenikan_server:
go -C tools/server build -o ../../shenikan_server
dump-dictionary:
@cue export --out $(EXPORT_FORMAT) -p shenikan
@ -21,3 +26,4 @@ print-%:
clean:
-rm $(FONT_TTFS)
-rm shenikan_server

View file

@ -64,6 +64,7 @@ _dictionary: {
}
dialects: {
// TODO: make these per-consonant-cluster
"shenkian": {
"l": "pl"
"r": "pr"

38
tools/server/go.mod Normal file
View file

@ -0,0 +1,38 @@
module labprogramming.net/shenikan_server
go 1.23.1
replace labprogramming.net/shenikan => ../shenikan
replace labprogramming.net/shenikandata => ../..
require (
github.com/99designs/gqlgen v0.17.64
github.com/vektah/gqlparser/v2 v2.5.22
labprogramming.net/shenikan v0.0.0-00010101000000-000000000000
)
require (
cuelabs.dev/go/oci/ociregistry v0.0.0-20240807094312-a32ad29eed79 // indirect
cuelang.org/go v0.10.0 // indirect
github.com/agnivade/levenshtein v1.2.0 // indirect
github.com/cockroachdb/apd/v3 v3.2.1 // indirect
github.com/emicklei/proto v1.13.2 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/protocolbuffers/txtpbfmt v0.0.0-20230328191034-3462fbc510c0 // indirect
github.com/rogpeppe/go-internal v1.12.1-0.20240709150035-ccf4b4329d21 // indirect
github.com/sosodev/duration v1.3.1 // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/oauth2 v0.22.0 // indirect
golang.org/x/text v0.21.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
labprogramming.net/shenikandata v0.0.0-00010101000000-000000000000 // indirect
)

95
tools/server/go.sum Normal file
View file

@ -0,0 +1,95 @@
cuelabs.dev/go/oci/ociregistry v0.0.0-20240807094312-a32ad29eed79 h1:EceZITBGET3qHneD5xowSTY/YHbNybvMWGh62K2fG/M=
cuelabs.dev/go/oci/ociregistry v0.0.0-20240807094312-a32ad29eed79/go.mod h1:5A4xfTzHTXfeVJBU6RAUf+QrlfTCW+017q/QiW+sMLg=
cuelang.org/go v0.10.0 h1:Y1Pu4wwga5HkXfLFK1sWAYaSWIBdcsr5Cb5AWj2pOuE=
cuelang.org/go v0.10.0/go.mod h1:HzlaqqqInHNiqE6slTP6+UtxT9hN6DAzgJgdbNxXvX8=
github.com/99designs/gqlgen v0.17.64 h1:BzpqO5ofQXyy2XOa93Q6fP1BHLRjTOeU35ovTEsbYlw=
github.com/99designs/gqlgen v0.17.64/go.mod h1:kaxLetFxPGeBBwiuKk75NxuI1fe9HRvob17In74v/Zc=
github.com/PuerkitoBio/goquery v1.9.3 h1:mpJr/ikUA9/GNJB/DBZcGeFDXUtosHRyRrwh7KGdTG0=
github.com/PuerkitoBio/goquery v1.9.3/go.mod h1:1ndLHPdTz+DyQPICCWYlYQMPl0oXZj0G6D4LCYA6u4U=
github.com/agnivade/levenshtein v1.2.0 h1:U9L4IOT0Y3i0TIlUIDJ7rVUziKi/zPbrJGaFrtYH3SY=
github.com/agnivade/levenshtein v1.2.0/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg=
github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo=
github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/emicklei/proto v1.13.2 h1:z/etSFO3uyXeuEsVPzfl56WNgzcvIr42aQazXaQmFZY=
github.com/emicklei/proto v1.13.2/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/protocolbuffers/txtpbfmt v0.0.0-20230328191034-3462fbc510c0 h1:sadMIsgmHpEOGbUs6VtHBXRR1OHevnj7hLx9ZcdNGW4=
github.com/protocolbuffers/txtpbfmt v0.0.0-20230328191034-3462fbc510c0/go.mod h1:jgxiZysxFPM+iWKwQwPR+y+Jvo54ARd4EisXxKYpB5c=
github.com/rogpeppe/go-internal v1.12.1-0.20240709150035-ccf4b4329d21 h1:igWZJluD8KtEtAgRyF4x6lqcxDry1ULztksMJh2mnQE=
github.com/rogpeppe/go-internal v1.12.1-0.20240709150035-ccf4b4329d21/go.mod h1:RMRJLmBOqWacUkmJHRMiPKh1S1m3PA7Zh4W80/kWPpg=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4=
github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/vektah/gqlparser/v2 v2.5.22 h1:yaaeJ0fu+nv1vUMW0Hl+aS1eiv1vMfapBNjpffAda1I=
github.com/vektah/gqlparser/v2 v2.5.22/go.mod h1:xMl+ta8a5M1Yo1A1Iwt/k7gSpscwSnHZdw7tfhEGfTM=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

155
tools/server/gqlgen.yml Normal file
View file

@ -0,0 +1,155 @@
# Where are all the schema files located? globs are supported eg src/**/*.graphqls
schema:
- graph/*.graphqls
# Where should the generated server code go?
exec:
package: graph
layout: single-file # Only other option is "follow-schema," ie multi-file.
# Only for single-file layout:
filename: graph/generated.go
# Only for follow-schema layout:
# dir: graph
# filename_template: "{name}.generated.go"
# Optional: Maximum number of goroutines in concurrency to use per child resolvers(default: unlimited)
# worker_limit: 1000
# Uncomment to enable federation
# federation:
# filename: graph/federation.go
# package: graph
# version: 2
# options:
# computed_requires: true
# Where should any generated models go?
model:
filename: graph/model/models_gen.go
package: model
# Optional: Pass in a path to a new gotpl template to use for generating the models
# model_template: [your/path/model.gotpl]
# Where should the resolver implementations go?
resolver:
package: graph
layout: follow-schema # Only other option is "single-file."
# Only for single-file layout:
# filename: graph/resolver.go
# Only for follow-schema layout:
dir: graph
filename_template: "{name}.resolvers.go"
# Optional: turn on to not generate template comments above resolvers
# omit_template_comment: false
# Optional: Pass in a path to a new gotpl template to use for generating resolvers
# resolver_template: [your/path/resolver.gotpl]
# Optional: turn on to avoid rewriting existing resolver(s) when generating
# preserve_resolver: false
# Optional: turn on use ` + "`" + `gqlgen:"fieldName"` + "`" + ` tags in your models
# struct_tag: json
# Optional: turn on to use []Thing instead of []*Thing
omit_slice_element_pointers: true
# Optional: turn on to omit Is<Name>() methods to interface and unions
# omit_interface_checks: true
# Optional: turn on to skip generation of ComplexityRoot struct content and Complexity function
# omit_complexity: false
# Optional: turn on to not generate any file notice comments in generated files
# omit_gqlgen_file_notice: false
# Optional: turn on to exclude the gqlgen version in the generated file notice. No effect if `omit_gqlgen_file_notice` is true.
# omit_gqlgen_version_in_file_notice: false
# Optional: turn on to exclude root models such as Query and Mutation from the generated models file.
# omit_root_models: false
# Optional: turn on to exclude resolver fields from the generated models file.
# omit_resolver_fields: false
# Optional: turn off to make struct-type struct fields not use pointers
# e.g. type Thing struct { FieldA OtherThing } instead of { FieldA *OtherThing }
# struct_fields_always_pointers: true
# Optional: turn off to make resolvers return values instead of pointers for structs
# resolvers_always_return_pointers: true
# Optional: turn on to return pointers instead of values in unmarshalInput
# return_pointers_in_unmarshalinput: false
# Optional: wrap nullable input fields with Omittable
# nullable_input_omittable: true
# Optional: set to speed up generation time by not performing a final validation pass.
# skip_validation: true
# Optional: set to skip running `go mod tidy` when generating server code
# skip_mod_tidy: true
# Optional: if this is set to true, argument directives that
# decorate a field with a null value will still be called.
#
# This enables argumment directives to not just mutate
# argument values but to set them even if they're null.
call_argument_directives_with_null: true
# Optional: set build tags that will be used to load packages
# go_build_tags:
# - private
# - enterprise
# Optional: set to modify the initialisms regarded for Go names
# go_initialisms:
# replace_defaults: false # if true, the default initialisms will get dropped in favor of the new ones instead of being added
# initialisms: # List of initialisms to for Go names
# - 'CC'
# - 'BCC'
# gqlgen will search for any type names in the schema in these go packages
# if they match it will use them, otherwise it will generate them.
autobind:
# - "labprogramming.net/shenikan_server/graph/model"
# This section declares type mapping between the GraphQL and go type systems
#
# The first line in each type will be used as defaults for resolver arguments and
# modelgen, the others will be allowed when binding to fields. Configure them to
# your liking
models:
ID:
model:
- github.com/99designs/gqlgen/graphql.ID
- github.com/99designs/gqlgen/graphql.Int
- github.com/99designs/gqlgen/graphql.Int64
- github.com/99designs/gqlgen/graphql.Int32
# gqlgen provides a default GraphQL UUID convenience wrapper for github.com/google/uuid
# but you can override this to provide your own GraphQL UUID implementation
UUID:
model:
- github.com/99designs/gqlgen/graphql.UUID
# The GraphQL spec explicitly states that the Int type is a signed 32-bit
# integer. Using Go int or int64 to represent it can lead to unexpected
# behavior, and some GraphQL tools like Apollo Router will fail when
# communicating numbers that overflow 32-bits.
#
# You may choose to use the custom, built-in Int64 scalar to represent 64-bit
# integers, or ignore the spec and bind Int to graphql.Int / graphql.Int64
# (the default behavior of gqlgen). This is fine in simple use cases when you
# do not need to worry about interoperability and only expect small numbers.
Int:
model:
- github.com/99designs/gqlgen/graphql.Int32
Int64:
model:
- github.com/99designs/gqlgen/graphql.Int
- github.com/99designs/gqlgen/graphql.Int64

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,299 @@
// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.
package model
import (
"fmt"
"io"
"strconv"
)
// A pentascale of derivational morphologies
type DMPenta struct {
Spelling string `json:"spelling"`
Name *string `json:"name,omitempty"`
Extremes *Extremes `json:"extremes,omitempty"`
}
// A shenikan-english definition
type Definition struct {
Pos PartOfSpeech `json:"pos"`
Short string `json:"short"`
Long *string `json:"long,omitempty"`
}
// A shenikan derivation
type Derivation struct {
Root string `json:"root"`
Via []string `json:"via"`
}
// A shenikan derivational morphology
type DerivationalMorphology struct {
Spelling string `json:"spelling"`
Definitions []Definition `json:"definitions"`
}
// A shenikan dialect
type Dialect struct {
Name string `json:"name"`
Replacements []Replacement `json:"replacements"`
}
// A whole shenikan dictionary.
type Dictionary struct {
Glyphs []Glyph `json:"glyphs"`
Dialects []Dialect `json:"dialects"`
Dms []DerivationalMorphology `json:"dms"`
DmPentas []DMPenta `json:"dmPentas"`
Words []Word `json:"words"`
Pentas []Penta `json:"pentas"`
Icosas []Icosa `json:"icosas"`
}
// The extremes of an icosapentascale
type DoubleExtremes struct {
Ix *string `json:"ix,omitempty"`
Ux *string `json:"ux,omitempty"`
Xi *string `json:"xi,omitempty"`
Xu *string `json:"xu,omitempty"`
}
// The extremes of a pentascale
type Extremes struct {
I *string `json:"i,omitempty"`
U *string `json:"u,omitempty"`
}
// A glyph in a shenikan dictionary.
type Glyph struct {
Kind GlyphKind `json:"kind"`
Ortho string `json:"ortho"`
Attrs []GlyphAttribute `json:"attrs"`
}
// An icosapentascale of words
type Icosa struct {
Spelling string `json:"spelling"`
Name *string `json:"name,omitempty"`
Extremes *DoubleExtremes `json:"extremes,omitempty"`
}
// A pentascale of words
type Penta struct {
Spelling string `json:"spelling"`
Name *string `json:"name,omitempty"`
Extremes *Extremes `json:"extremes,omitempty"`
}
type Query struct {
}
// A consonant cluster replacement for a dialect
type Replacement struct {
Old string `json:"old"`
New string `json:"new"`
}
// A shenikan word
type Word struct {
Spelling string `json:"spelling"`
Definitions []Definition `json:"definitions"`
Derivations []Derivation `json:"derivations"`
}
// The determining attributes of a glyph.
type GlyphAttribute string
const (
// Marks an outer vowel. (valid for VOWEL/SYLLABLE glyphs)
GlyphAttributeOuter GlyphAttribute = "OUTER"
// Marks a slashed vowel. (valid for VOWEL/SYLLABLE glyphs)
GlyphAttributeSLAShed GlyphAttribute = "SLASHED"
// Marks an inner vowel. (valid for VOWEL/SYLLABLE glyphs)
GlyphAttributeInner GlyphAttribute = "INNER"
// Marks a left consonant cluster or punctuation. (valid for CLUSTER/VOWEL/PUNCTUATION glyphs)
GlyphAttributeLeft GlyphAttribute = "LEFT"
// Marks a center consonant cluster. (valid for CLUSTER/VOWEL glyphs)
GlyphAttributeCenter GlyphAttribute = "CENTER"
// Marks a right consonant cluster or punctuation. (valid for CLUSTER/VOWEL/PUNCTUATION glyphs)
GlyphAttributeRight GlyphAttribute = "RIGHT"
// Marks a top consonant cluster. (valid for CLUSTER/VOWEL glyphs)
GlyphAttributeTop GlyphAttribute = "TOP"
// Marks a middle consonant cluster. (valid for CLUSTER/VOWEL glyphs)
GlyphAttributeMiddle GlyphAttribute = "MIDDLE"
// Marks a bottom consonant cluster. (valid for CLUSTER/VOWEL glyphs)
GlyphAttributeBottom GlyphAttribute = "BOTTOM"
// Marks a tall consonant cluster. (valid for CLUSTER/VOWEL glyphs)
GlyphAttributeTall GlyphAttribute = "TALL"
// Marks a wide consonant cluster. (valid for CLUSTER/VOWEL glyphs)
GlyphAttributeWide GlyphAttribute = "WIDE"
// Marks a tall and wide consonant cluster. (valid for CLUSTER/VOWEL glyphs)
GlyphAttributeBoth GlyphAttribute = "BOTH"
// Marks a circular number. (valid for NUMERIC glyphs)
GlyphAttributeCircle GlyphAttribute = "CIRCLE"
// Marks a dash number. (valid for NUMERIC glyphs)
GlyphAttributeDash GlyphAttribute = "DASH"
// Marks a vee number. (valid for NUMERIC glyphs)
GlyphAttributeVee GlyphAttribute = "VEE"
// Marks a hump number. (valid for NUMERIC glyphs)
GlyphAttributeHump GlyphAttribute = "HUMP"
// Marks a dot number. (valid for NUMERIC glyphs)
GlyphAttributeDot GlyphAttribute = "DOT"
)
var AllGlyphAttribute = []GlyphAttribute{
GlyphAttributeOuter,
GlyphAttributeSLAShed,
GlyphAttributeInner,
GlyphAttributeLeft,
GlyphAttributeCenter,
GlyphAttributeRight,
GlyphAttributeTop,
GlyphAttributeMiddle,
GlyphAttributeBottom,
GlyphAttributeTall,
GlyphAttributeWide,
GlyphAttributeBoth,
GlyphAttributeCircle,
GlyphAttributeDash,
GlyphAttributeVee,
GlyphAttributeHump,
GlyphAttributeDot,
}
func (e GlyphAttribute) IsValid() bool {
switch e {
case GlyphAttributeOuter, GlyphAttributeSLAShed, GlyphAttributeInner, GlyphAttributeLeft, GlyphAttributeCenter, GlyphAttributeRight, GlyphAttributeTop, GlyphAttributeMiddle, GlyphAttributeBottom, GlyphAttributeTall, GlyphAttributeWide, GlyphAttributeBoth, GlyphAttributeCircle, GlyphAttributeDash, GlyphAttributeVee, GlyphAttributeHump, GlyphAttributeDot:
return true
}
return false
}
func (e GlyphAttribute) String() string {
return string(e)
}
func (e *GlyphAttribute) UnmarshalGQL(v any) error {
str, ok := v.(string)
if !ok {
return fmt.Errorf("enums must be strings")
}
*e = GlyphAttribute(str)
if !e.IsValid() {
return fmt.Errorf("%s is not a valid GlyphAttribute", str)
}
return nil
}
func (e GlyphAttribute) MarshalGQL(w io.Writer) {
fmt.Fprint(w, strconv.Quote(e.String()))
}
// The kind of a glyph.
type GlyphKind string
const (
// A bare vowel cluster.
GlyphKindVowel GlyphKind = "VOWEL"
// A consonant cluster.
GlyphKindCluster GlyphKind = "CLUSTER"
// A punctuation mark.
GlyphKindPunctuation GlyphKind = "PUNCTUATION"
// A numeric or symbolic glyph.
GlyphKindNumeric GlyphKind = "NUMERIC"
// A complete syllable cluster.
GlyphKindSyllable GlyphKind = "SYLLABLE"
)
var AllGlyphKind = []GlyphKind{
GlyphKindVowel,
GlyphKindCluster,
GlyphKindPunctuation,
GlyphKindNumeric,
GlyphKindSyllable,
}
func (e GlyphKind) IsValid() bool {
switch e {
case GlyphKindVowel, GlyphKindCluster, GlyphKindPunctuation, GlyphKindNumeric, GlyphKindSyllable:
return true
}
return false
}
func (e GlyphKind) String() string {
return string(e)
}
func (e *GlyphKind) UnmarshalGQL(v any) error {
str, ok := v.(string)
if !ok {
return fmt.Errorf("enums must be strings")
}
*e = GlyphKind(str)
if !e.IsValid() {
return fmt.Errorf("%s is not a valid GlyphKind", str)
}
return nil
}
func (e GlyphKind) MarshalGQL(w io.Writer) {
fmt.Fprint(w, strconv.Quote(e.String()))
}
// A part of speech
type PartOfSpeech string
const (
PartOfSpeechNoun PartOfSpeech = "NOUN"
PartOfSpeechPronoun PartOfSpeech = "PRONOUN"
PartOfSpeechVerb PartOfSpeech = "VERB"
PartOfSpeechAdjective PartOfSpeech = "ADJECTIVE"
PartOfSpeechAdverb PartOfSpeech = "ADVERB"
PartOfSpeechAdposition PartOfSpeech = "ADPOSITION"
PartOfSpeechConjunction PartOfSpeech = "CONJUNCTION"
PartOfSpeechSyntax PartOfSpeech = "SYNTAX"
)
var AllPartOfSpeech = []PartOfSpeech{
PartOfSpeechNoun,
PartOfSpeechPronoun,
PartOfSpeechVerb,
PartOfSpeechAdjective,
PartOfSpeechAdverb,
PartOfSpeechAdposition,
PartOfSpeechConjunction,
PartOfSpeechSyntax,
}
func (e PartOfSpeech) IsValid() bool {
switch e {
case PartOfSpeechNoun, PartOfSpeechPronoun, PartOfSpeechVerb, PartOfSpeechAdjective, PartOfSpeechAdverb, PartOfSpeechAdposition, PartOfSpeechConjunction, PartOfSpeechSyntax:
return true
}
return false
}
func (e PartOfSpeech) String() string {
return string(e)
}
func (e *PartOfSpeech) UnmarshalGQL(v any) error {
str, ok := v.(string)
if !ok {
return fmt.Errorf("enums must be strings")
}
*e = PartOfSpeech(str)
if !e.IsValid() {
return fmt.Errorf("%s is not a valid PartOfSpeech", str)
}
return nil
}
func (e PartOfSpeech) MarshalGQL(w io.Writer) {
fmt.Fprint(w, strconv.Quote(e.String()))
}

View file

@ -0,0 +1,7 @@
package graph
// This file will not be regenerated automatically.
//
// It serves as dependency injection for your app, add any dependencies you require here.
type Resolver struct{}

View file

@ -0,0 +1,142 @@
type Query {
dictionary: Dictionary!
}
"A whole shenikan dictionary."
type Dictionary {
glyphs: [Glyph!]!
dialects: [Dialect!]!
dms: [DerivationalMorphology!]!
dmPentas: [DMPenta!]!
words: [Word!]!
pentas: [Penta!]!
icosas: [Icosa!]!
}
"A glyph in a shenikan dictionary."
type Glyph {
kind: GlyphKind!
ortho: String!
attrs: [GlyphAttribute!]!
}
"The kind of a glyph."
enum GlyphKind {
"A bare vowel cluster." VOWEL
"A consonant cluster." CLUSTER
"A punctuation mark." PUNCTUATION
"A numeric or symbolic glyph." NUMERIC
"A complete syllable cluster." SYLLABLE
}
# TODO: group these into, e.g. consonant height, to enforce mutex appropriately?
"The determining attributes of a glyph."
enum GlyphAttribute {
# Vowel Attributes
"Marks an outer vowel. (valid for VOWEL/SYLLABLE glyphs)" OUTER
"Marks a slashed vowel. (valid for VOWEL/SYLLABLE glyphs)" SLASHED
"Marks an inner vowel. (valid for VOWEL/SYLLABLE glyphs)" INNER
# Consonant Cluster Attributes (and Punctuation Attributes???)
"Marks a left consonant cluster or punctuation. (valid for CLUSTER/VOWEL/PUNCTUATION glyphs)" LEFT
"Marks a center consonant cluster. (valid for CLUSTER/VOWEL glyphs)" CENTER
"Marks a right consonant cluster or punctuation. (valid for CLUSTER/VOWEL/PUNCTUATION glyphs)" RIGHT
"Marks a top consonant cluster. (valid for CLUSTER/VOWEL glyphs)" TOP
"Marks a middle consonant cluster. (valid for CLUSTER/VOWEL glyphs)" MIDDLE
"Marks a bottom consonant cluster. (valid for CLUSTER/VOWEL glyphs)" BOTTOM
"Marks a tall consonant cluster. (valid for CLUSTER/VOWEL glyphs)" TALL
"Marks a wide consonant cluster. (valid for CLUSTER/VOWEL glyphs)" WIDE
"Marks a tall and wide consonant cluster. (valid for CLUSTER/VOWEL glyphs)" BOTH
# Numeric Attributes
"Marks a circular number. (valid for NUMERIC glyphs)" CIRCLE
"Marks a dash number. (valid for NUMERIC glyphs)" DASH
"Marks a vee number. (valid for NUMERIC glyphs)" VEE
"Marks a hump number. (valid for NUMERIC glyphs)" HUMP
"Marks a dot number. (valid for NUMERIC glyphs)" DOT
}
"A shenikan dialect"
type Dialect {
name: String!
replacements: [Replacement!]!
}
"A consonant cluster replacement for a dialect"
type Replacement {
old: String!
new: String!
}
"A shenikan derivational morphology"
type DerivationalMorphology {
spelling: String!
definitions: [Definition!]!
}
"A shenikan-english definition"
type Definition {
pos: PartOfSpeech!
short: String!
long: String
}
"A part of speech"
enum PartOfSpeech {
NOUN
PRONOUN
VERB
ADJECTIVE
ADVERB
ADPOSITION
CONJUNCTION
SYNTAX
}
"A pentascale of derivational morphologies"
type DMPenta {
spelling: String!
name: String
extremes: Extremes
}
"The extremes of a pentascale"
type Extremes {
i: String
u: String
}
"A shenikan word"
type Word {
spelling: String!
definitions: [Definition!]!
derivations: [Derivation!]!
}
"A shenikan derivation"
type Derivation {
root: String!
via: [String!]!
}
"A pentascale of words"
type Penta {
spelling: String!
name: String
extremes: Extremes
}
"An icosapentascale of words"
type Icosa {
spelling: String!
name: String
extremes: DoubleExtremes
}
"The extremes of an icosapentascale"
type DoubleExtremes {
ix: String
ux: String
xi: String
xu: String
}

View file

@ -0,0 +1,39 @@
package graph
// This file will be automatically regenerated based on the schema, any resolver implementations
// will be copied through when generating and any unknown code will be moved to the end.
// Code generated by github.com/99designs/gqlgen version v0.17.64
import (
"context"
"labprogramming.net/shenikan"
"labprogramming.net/shenikan_server/graph/model"
)
// Dictionary is the resolver for the dictionary field.
func (r *queryResolver) Dictionary(ctx context.Context) (m *model.Dictionary, err error) {
if shenikan.Dict == nil {
return nil, shenikan.DictError
}
m = &model.Dictionary{}
m.Glyphs = make([]model.Glyph, len(shenikan.Dict.Glyphs))
for i, g := range shenikan.Dict.Glyphs {
m.Glyphs[i] = model.Glyph{
Kind: model.GlyphKind(g.Kind),
Ortho: g.Ortho,
Attrs: make([]model.GlyphAttribute, len(g.Attrs)),
}
for j, a := range g.Attrs {
m.Glyphs[i].Attrs[j] = model.GlyphAttribute(a)
}
}
return
}
// Query returns QueryResolver implementation.
func (r *Resolver) Query() QueryResolver { return &queryResolver{r} }
type queryResolver struct{ *Resolver }

View file

@ -0,0 +1,3 @@
#!/bin/bash
go run github.com/99designs/gqlgen generate

View file

@ -0,0 +1,142 @@
type Query {
dictionary: Dictionary!
}
"A whole shenikan dictionary."
type Dictionary {
glyphs: [Glyph!]!
dialects: [Dialect!]!
dms: [DerivationalMorphology!]!
dmPentas: [DMPenta!]!
words: [Word!]!
pentas: [Penta!]!
icosas: [Icosa!]!
}
"A glyph in a shenikan dictionary."
type Glyph {
kind: GlyphKind!
ortho: String!
attrs: [GlyphAttribute]!
}
"The kind of a glyph."
enum GlyphKind {
"A bare vowel cluster." VOWEL
"A consonant cluster." CLUSTER
"A punctuation mark." PUNCTUATION
"A numeric or symbolic glyph." NUMERIC
"A complete syllable cluster." SYLLABLE
}
# TODO: group these into, e.g. consonant height, to enforce mutex appropriately?
"The determining attributes of a glyph."
enum GlyphAttribute {
# Vowel Attributes
"Marks an outer vowel. (valid for VOWEL/SYLLABLE glyphs)" OUTER
"Marks a slashed vowel. (valid for VOWEL/SYLLABLE glyphs)" SLASHED
"Marks an inner vowel. (valid for VOWEL/SYLLABLE glyphs)" INNER
# Consonant Cluster Attributes (and Punctuation Attributes???)
"Marks a left consonant cluster or punctuation. (valid for CLUSTER/VOWEL/PUNCTUATION glyphs)" LEFT
"Marks a center consonant cluster. (valid for CLUSTER/VOWEL glyphs)" CENTER
"Marks a right consonant cluster or punctuation. (valid for CLUSTER/VOWEL/PUNCTUATION glyphs)" RIGHT
"Marks a top consonant cluster. (valid for CLUSTER/VOWEL glyphs)" TOP
"Marks a middle consonant cluster. (valid for CLUSTER/VOWEL glyphs)" MIDDLE
"Marks a bottom consonant cluster. (valid for CLUSTER/VOWEL glyphs)" BOTTOM
"Marks a tall consonant cluster. (valid for CLUSTER/VOWEL glyphs)" TALL
"Marks a wide consonant cluster. (valid for CLUSTER/VOWEL glyphs)" WIDE
"Marks a tall and wide consonant cluster. (valid for CLUSTER/VOWEL glyphs)" BOTH
# Numeric Attributes
"Marks a circular number. (valid for NUMERIC glyphs)" CIRCLE
"Marks a dash number. (valid for NUMERIC glyphs)" DASH
"Marks a vee number. (valid for NUMERIC glyphs)" VEE
"Marks a hump number. (valid for NUMERIC glyphs)" HUMP
"Marks a dot number. (valid for NUMERIC glyphs)" DOT
}
"A shenikan dialect"
type Dialect {
name: String!
replacements: [Replacement!]!
}
"A consonant cluster replacement for a dialect"
type Replacement {
old: String!
new: String!
}
"A shenikan derivational morphology"
type DerivationalMorphology {
spelling: String!
definitions: [Definition!]!
}
"A shenikan-english definition"
type Definition {
pos: PartOfSpeech!
short: String!
long: String
}
"A part of speech"
enum PartOfSpeech {
NOUN
PRONOUN
VERB
ADJECTIVE
ADVERB
ADPOSITION
CONJUNCTION
SYNTAX
}
"A pentascale of derivational morphologies"
type DMPenta {
spelling: String!
name: String
extremes: Extremes
}
"The extremes of a pentascale"
type Extremes {
i: String
u: String
}
"A shenikan word"
type Word {
spelling: String!
definitions: [Definition!]!
derivations: [Derivation!]!
}
"A shenikan derivation"
type Derivation {
root: String!
via: [String!]!
}
"A pentascale of words"
type Penta {
spelling: String!
name: String
extremes: Extremes
}
"An icosapentascale of words"
type Icosa {
spelling: String!
name: String
extremes: DoubleExtremes
}
"The extremes of an icosapentascale"
type DoubleExtremes {
ix: String
ux: String
xi: String
xu: String
}

43
tools/server/server.go Normal file
View file

@ -0,0 +1,43 @@
package main
import (
"log"
"net/http"
"os"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/99designs/gqlgen/graphql/handler/extension"
"github.com/99designs/gqlgen/graphql/handler/lru"
"github.com/99designs/gqlgen/graphql/handler/transport"
"github.com/99designs/gqlgen/graphql/playground"
"github.com/vektah/gqlparser/v2/ast"
"labprogramming.net/shenikan_server/graph"
)
const defaultPort = "8080"
func main() {
port := os.Getenv("PORT")
if port == "" {
port = defaultPort
}
srv := handler.New(graph.NewExecutableSchema(graph.Config{Resolvers: &graph.Resolver{}}))
srv.AddTransport(transport.Options{})
srv.AddTransport(transport.GET{})
srv.AddTransport(transport.POST{})
srv.SetQueryCache(lru.New[*ast.QueryDocument](1000))
srv.Use(extension.Introspection{})
srv.Use(extension.AutomaticPersistedQuery{
Cache: lru.New[string](100),
})
http.Handle("/", playground.Handler("GraphQL playground", "/query"))
http.Handle("/query", srv)
log.Printf("connect to http://localhost:%s/ for GraphQL playground", port)
log.Fatal(http.ListenAndServe(":"+port, nil))
}

22
tools/shenikan/go.mod Normal file
View file

@ -0,0 +1,22 @@
module labprogramming.net/shenikan
go 1.23.1
require (
cuelabs.dev/go/oci/ociregistry v0.0.0-20240807094312-a32ad29eed79 // indirect
cuelang.org/go v0.10.0 // indirect
github.com/cockroachdb/apd/v3 v3.2.1 // indirect
github.com/emicklei/proto v1.13.2 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/protocolbuffers/txtpbfmt v0.0.0-20230328191034-3462fbc510c0 // indirect
github.com/rogpeppe/go-internal v1.12.1-0.20240709150035-ccf4b4329d21 // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/net v0.28.0 // indirect
golang.org/x/oauth2 v0.22.0 // indirect
golang.org/x/text v0.17.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

45
tools/shenikan/go.sum Normal file
View file

@ -0,0 +1,45 @@
cuelabs.dev/go/oci/ociregistry v0.0.0-20240807094312-a32ad29eed79 h1:EceZITBGET3qHneD5xowSTY/YHbNybvMWGh62K2fG/M=
cuelabs.dev/go/oci/ociregistry v0.0.0-20240807094312-a32ad29eed79/go.mod h1:5A4xfTzHTXfeVJBU6RAUf+QrlfTCW+017q/QiW+sMLg=
cuelang.org/go v0.10.0 h1:Y1Pu4wwga5HkXfLFK1sWAYaSWIBdcsr5Cb5AWj2pOuE=
cuelang.org/go v0.10.0/go.mod h1:HzlaqqqInHNiqE6slTP6+UtxT9hN6DAzgJgdbNxXvX8=
github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg=
github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/proto v1.13.2 h1:z/etSFO3uyXeuEsVPzfl56WNgzcvIr42aQazXaQmFZY=
github.com/emicklei/proto v1.13.2/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/protocolbuffers/txtpbfmt v0.0.0-20230328191034-3462fbc510c0 h1:sadMIsgmHpEOGbUs6VtHBXRR1OHevnj7hLx9ZcdNGW4=
github.com/protocolbuffers/txtpbfmt v0.0.0-20230328191034-3462fbc510c0/go.mod h1:jgxiZysxFPM+iWKwQwPR+y+Jvo54ARd4EisXxKYpB5c=
github.com/rogpeppe/go-internal v1.12.1-0.20240709150035-ccf4b4329d21 h1:igWZJluD8KtEtAgRyF4x6lqcxDry1ULztksMJh2mnQE=
github.com/rogpeppe/go-internal v1.12.1-0.20240709150035-ccf4b4329d21/go.mod h1:RMRJLmBOqWacUkmJHRMiPKh1S1m3PA7Zh4W80/kWPpg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

170
tools/shenikan/shenikan.go Normal file
View file

@ -0,0 +1,170 @@
package shenikan
import (
"strings"
"io/fs"
// "cuelang.org/go/cue"
"cuelang.org/go/cue/cuecontext"
"cuelang.org/go/cue/load"
"labprogramming.net/shenikandata"
)
type Glyph struct {
Kind string `json:"kind"`
Ortho string `json:"ortho"`
Attrs []string `json:"attrs"`
}
type Replacement struct {
Old string `json:"old"`
New string `json:"new"`
}
type Dialect struct {
Name string `json:"name"`
Replacements []Replacement `json:"replacements"`
}
type Definition struct {
PartOfSpeech string `json:"pos"`
Short string `json:"short"`
Long *string `json:"long"`
}
type DerivationalMorphology struct {
Spelling string `json:"spelling"`
Definitions []Definition
}
type PentaExtremes struct {
I *string `json:"i"`
U *string `json:"u"`
}
type DMPenta struct {
Spelling string `json:"spelling"`
Name *string `json:"name"`
Extremes *PentaExtremes `json:"extremes"`
}
type Derivation struct {
Root string `json:"root"`
Via []string `json:"via"`
}
type Word struct {
Spelling string `json:"spelling"`
Definitions []Definition `json:"definitions"`
Derivations []Derivation `json:"derivations"`
}
type Penta struct {
Spelling string `json:"spelling"`
Name *string `json:"name"`
Extremes *PentaExtremes `json:"extremes"`
}
type IcosaExtremes struct {
IX *string `json:"ix"`
UX *string `json:"ux"`
XI *string `json:"xi"`
XU *string `json:"xu"`
}
type Icosa struct {
Spelling string `json:"spelling"`
Name *string `json:"name"`
Extremes *IcosaExtremes `json:"extremes"`
}
type Dictionary struct {
Glyphs []Glyph `json:"glyphs"`
Dialects []Dialect `json:"dialects"`
DMs []DerivationalMorphology `json:"dms"`
DMPentas []DMPenta `json:"dmPentas"`
Words []Word `json:"words"`
Pentas []Penta `json:"pentas"`
Icosas []Icosa `json:"icosas"`
}
type dictionaryData struct {
Dict *Dictionary `json:"dictionary"`
}
var DictError error
var Dict *Dictionary = func() *Dictionary {
var overlay map[string]load.Source = make(map[string]load.Source)
sources, DictError := fs.Glob(shenikandata.Cues, "*")
if DictError != nil {
return nil
}
for source := range sources {
content, DictError := shenikandata.Cues.ReadFile(sources[source])
if DictError != nil {
return nil
}
overlay["/.secretprefix/" + sources[source]] = load.FromBytes(content)
}
insts := load.Instances([]string{"."}, &load.Config{
Dir: "/.secretprefix",
Env: []string{},
Overlay: overlay,
})
ctx := cuecontext.New()
vals, DictError := ctx.BuildInstances(insts)
if DictError != nil {
return nil
}
var dd dictionaryData
DictError = vals[0].Decode(&dd)
return dd.Dict
}()
// Canonicalizes a string to unicode shenikan without pr or pl
func Canonicalize(shenikan string) (result string) {
result = shenikan
result = strings.ReplaceAll(result, "sh", "∫")
result = strings.ReplaceAll(result, "th", "θ")
result = strings.ReplaceAll(result, "`", "«")
result = strings.ReplaceAll(result, "'", "»")
result = strings.ReplaceAll(result, "c", "x")
result = strings.ReplaceAll(result, "ç", "x")
result = strings.ReplaceAll(result, "ç", "x")
result = strings.ReplaceAll(result, "pr", "r")
result = strings.ReplaceAll(result, "pl", "l")
result = strings.ReplaceAll(result, "j", "p")
result = strings.ReplaceAll(result, "g", "k")
result = strings.ReplaceAll(result, "z", "s")
result = strings.ReplaceAll(result, "d", "t")
return
}
func RenderCSUR(shenikan string, offset int) (result string) {
result = Canonicalize(shenikan)
base := 0xF3A00 + 0x100 * offset
var longest int = 0
for _, glyph := range Dict.Glyphs {
if len(glyph.Ortho) > longest {
longest = len(glyph.Ortho)
}
}
for length := longest; length > 0; length-- {
for i, glyph := range Dict.Glyphs {
if len(glyph.Ortho) == length {
result = strings.ReplaceAll(result, glyph.Ortho, string(rune(base + i)))
}
}
}
return
}

View file

@ -0,0 +1,20 @@
package shenikan
import (
"testing"
)
func TestLoadUp2(t *testing.T) {
dict, err := LoadDictionary("../../")
if err != nil {
t.Fatal("Failed to load ../../:", err)
return
}
if len(dict.Glyphs) == 0 {
t.Fatal("No glyphs in ../../:", dict)
return
}
t.Log("Loaded", dict)
}

1
tools/up Symbolic link
View file

@ -0,0 +1 @@
..

View file

@ -2,7 +2,7 @@
package shenikan
_vowels: ["i", "e", "a", "o", "u"]
#VowelAttribute: "outer" | "slashed" | "inner" | "both"
#VowelAttribute: "outer" | "slashed" | "inner"
#ClusterAttribute: "left" | "center" | "right" | "top" | "middle" | "bottom" |
"tall" | "wide" | "both"
#PunctuationAttribute: "left" | "right"