diff --git a/README.md b/README.md index eae8621..f9c2473 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,9 @@ See `just compile`. - [x] random table entries (ADDED in vHydrogen) - [x] expanding macros (ADDED in vHydrogen) -- [ ] table files plugin: syntax highlighting + folding -- [ ] expansion filters +- [x] table files plugin: syntax highlighting + folding (ADDED vHelium) +- [x] expansion filters (ADDED vHelium) +- [ ] table context ## Resources diff --git a/doc/History.md b/doc/History.md new file mode 100644 index 0000000..705c9e1 --- /dev/null +++ b/doc/History.md @@ -0,0 +1,15 @@ +# History + +`tbls` began as the Story module for a board game I was writing. +When I completed all the features for the board game itself, +I wanted to add some kind of a generative story engine +based on the state of the game +and on player moves. + +This ended up complicating the game quite a bit, +and also I couldn't figure out how or where +to actually incorporate the story. +So I scrapped it for that particular project, +but continued to develop the story engine on its own. + +It became `tbls`. diff --git a/doc/Tutorial.md b/doc/Tutorial.md index 6e8399d..e13616f 100644 --- a/doc/Tutorial.md +++ b/doc/Tutorial.md @@ -1,4 +1,6 @@ -## Tutorial +# Tutorial + +## Random Selections At its most basic, `tbls` selects a random element from a table. @@ -17,21 +19,19 @@ Shovels Ace One Two -... -Ten -Eleven -Twelve -Thief +[...] Queen King Beast ``` `tbls` might return "Whistles" from suit. -Or "Twelve" from card. +Or "Two" from card. + +## Expansion But wait there's more. -`tbls` will also expand macros found in table entries. +`tbls` will also expand certain text found in a table entry. Let's add another table: @@ -43,6 +43,39 @@ Let's add another table: When you place the name of a table in `[squarebrackets]`, then `tbls` views that as a placeholder for a random item from that table. -And it will expand the text. -So this might end up being "Thief of Shovels" +It will expand that text. +So 'draw' might end up being "Thief of Shovels" or "Twelve or Cups". + +## Filters + +`tbls` can run arbitary filters on text after expanding it. +Example filters can be found in `src/flib.fnl`. + +To apply filters, +dot-chain them at the end +of a table reference. + +Consider the following tables: + +``` +:: origin +Evelyn eats one [fruit] +Evelyn eats many [fruit.s] + +:: fruit +banana +apple +pear +``` + +`s`, `plural`, and `pluralize` +are all different ways +to call the 'plural' filter function. + +Example output: + +``` +Evelyn eats one pear +Evelyn eats one bananas +``` diff --git a/doc/meta.rec b/doc/meta.rec index 6123ac5..6f782ad 100644 --- a/doc/meta.rec +++ b/doc/meta.rec @@ -5,5 +5,5 @@ name: tbls author: dozens@tilde.team created: 2024-07-31 -updated: 2024-08-01 -version: Hydrogen +updated: 2024-08-03 +version: Helium diff --git a/justfile b/justfile index 0a988e1..95eb57c 100644 --- a/justfile +++ b/justfile @@ -32,3 +32,11 @@ bump: # show full metadata meta: awk 'FNR==1{print ""}{print}' doc/*.rec | recsel -t meta -j version + +# install vim plugin +install-plugin: + cp -r vim-tbls/* ~/.config/nvim + +# install binary +install-binary: compile + cp tbls ~/bin diff --git a/src/filter.fnl b/src/filter.fnl new file mode 100644 index 0000000..29d7c5b --- /dev/null +++ b/src/filter.fnl @@ -0,0 +1,16 @@ +(local {: contains?} (require :src.table)) +(local {:split split-full} (require :src.string)) +(local split (partial split-full "[^%.]*")) + +(fn filter [s] + (let [filters (require :src.flib) + [str & funs] (split s)] + (var res str) + (each [_ f (ipairs funs)] + (each [_ filter (pairs filters)] + (when (contains? filter.call f) + (set res (filter.fun res))))) + res)) + +{: filter + } diff --git a/src/flib.fnl b/src/flib.fnl new file mode 100644 index 0000000..e106554 --- /dev/null +++ b/src/flib.fnl @@ -0,0 +1,30 @@ +(fn capitalize [s] + (s:gsub "." string.upper 1)) + +(fn uppercase [s] + (string.upper s)) + +(fn lowercase [s] + (string.lower s)) + +(fn plural [s] + (.. s :s)) + +{ + :capitalize { + :call [:c :cap :capital :capitalize] + :fun capitalize + } + :uppercase { + :call [:u :upper :uppercase] + :fun uppercase + } + :lowercase { + :call [:l :lower :lowercase] + :fun lowercase + } + :plural { + :call [:s :plural :pluralize] + :fun plural + } +} diff --git a/src/main.fnl b/src/main.fnl index 9e592e1..a509950 100644 --- a/src/main.fnl +++ b/src/main.fnl @@ -40,8 +40,7 @@ (fn main [] (let [opts (parse-args arg) - corpus (create-corpus opts.input) - ] + corpus (create-corpus opts.input)] (if opts.key (print (flatten corpus (. corpus opts.key))) (print (flatten corpus opts.string))))) diff --git a/src/story.fnl b/src/story.fnl index e1871e9..2b5b68c 100644 --- a/src/story.fnl +++ b/src/story.fnl @@ -1,20 +1,8 @@ ;; helper funs -(local tbl { - :contains? (fn contains [t x] - "does sequence t contain element x?" - (accumulate [found false - _ v (ipairs t) - &until found] ; escape early - (or found (= x v)))) - :keys (fn keys [t] - "takes a table returns a sequential list of its keys" - (local out []) - (each [k v (pairs t)] (table.insert out k)) - out) -}) -(fn has-key? [t k] - (tbl.contains? (tbl.keys t) k)) - +(local tbl (require :src.table)) +(local {: filter} (require :src.filter)) +(local {:split _split} (require :src.string)) +(local split (partial _split "[^%.]*")) (fn lines [filename callback] (case (pcall #(with-open [file (io.open filename)] (each [line (file:lines)] (callback line)))) @@ -22,7 +10,7 @@ (fn _create-corpus [lines data] (var current-key nil) - (var corpus {}) + (local corpus {}) (lines data #(let [key (string.match $1 "^::%s+([%a-]+)") blank? (or (= nil $1) (= "" $1)) @@ -50,7 +38,7 @@ seed (math.randomseed random) ;; SIDE EFFECT whatever (handle:close) ;; SIDE EFFECT idx (math.random len) - keys (accumulate [acc [] k v (pairs t)] (do (table.insert acc k) acc)) + keys (accumulate [acc [] k _ (pairs t)] (do (table.insert acc k) acc)) rndkey (. keys idx) ] (. t rndkey))) @@ -60,18 +48,22 @@ "string" origin "table" (one-of origin) _ (error "Origin must be a table or a string")) - template-pattern "%[[%a-]+%]" ; [word] - word-pattern "%[([%a-]+)%]" ; word + template-pattern "%[[%a-%.]+%]" ; [word] + word-pattern "%[([%a-%.]+)%]" ; word (i j) (string.find str template-pattern) ; indices - word (string.match str word-pattern)] ; the actual keyword + raw-word (or (string.match str word-pattern) str) + [word & fs] (split raw-word) + ] (if (not i) str (do - (assert (has-key? corpus word) + (assert (tbl.has-key? corpus word) (string.format "Error trying to expand \"%s\". Corpus does not contain a table called \"%s\"" str word)) (let [next-str (string.format "%s%s%s" (string.sub str 1 (- i 1)) - (one-of (. corpus word)) + (if (length fs) + (filter (.. (one-of (. corpus word)) "." (table.concat fs "."))) + (one-of (. corpus word))) (string.sub str (+ j 1)))] (flatten corpus next-str j)))))) ;; this is a tail call! diff --git a/src/string.fnl b/src/string.fnl new file mode 100644 index 0000000..303a6eb --- /dev/null +++ b/src/string.fnl @@ -0,0 +1,7 @@ +(fn split [pattern str] + (let [res []] + (each [seg (str:gmatch pattern)] + (table.insert res seg)) + res)) + +{: split} diff --git a/src/table.fnl b/src/table.fnl new file mode 100644 index 0000000..d395645 --- /dev/null +++ b/src/table.fnl @@ -0,0 +1,20 @@ +(fn contains? [t x] + "does sequence t contain element x?" + (accumulate [found false + _ v (ipairs t) + &until found] ; escape early + (or found (= x v)))) + +(fn keys [t] + "takes a table returns a sequential list of its keys" + (local out []) + (each [k _ (pairs t)] (table.insert out k)) + out) + +(fn has-key? [t k] + (contains? (keys t) k)) + +{: contains? + : keys + : has-key? + } diff --git a/test/morpheme-word-epithet.txt b/test/morpheme-word-epithet.txt index 1b0ff0a..7d5ef3f 100644 --- a/test/morpheme-word-epithet.txt +++ b/test/morpheme-word-epithet.txt @@ -2,349 +2,350 @@ :: name ## Entry point / origin -[morpheme][word] [epithet] -[prefix] [morpheme][word] -[prefix] [morpheme][word] [epithet] -[morpheme][word]'[morpheme] -[morpheme] [morpheme][word] +[morpheme.c][word] [epithet] +[prefix] [morpheme.c][word] +[prefix] [morpheme.c][word.s] +[prefix] [morpheme.c][word] [epithet] +[morpheme.c][word]'[morpheme.c] +[morpheme.c] [morpheme.c][word] :: morpheme ## A random sound -Brog -Guyn -Driz -Oy -Poin -Broin -Dray -Hoy -Khu -Elbb -Zee -Teg -Thig -Volor -Moror -Zarn -Zark -Zarzl -Zhong -Zok -Krinc -Prunc -Seld -Serd -Quo -Quog -Wast -Writ -Wub -Eem -Eo -Ev -Wuld -Rond -Trop -Yhoo -Urdd -Ing -Ogg -Phef -Aph -Fro -Drov -Frol -Gis -Hirn -Wilk -Rof -Roch -Thip -Throg -Teng -Tirk -Tenk -Twu -Twal -Yuth -Yirg -Ull -Uk -Iv -Irch -Ok -Ort -Ool -Oon -Oos -Oop -Pur -Purth -Pux -Pix -Ang -Ank -Ack -Arr -Arc -Sor -Shri -Sarc -Sib -Spo -Snil -Dral -Dax -Druz -Droon -Tiong -Snid -Phra -Vang -Gawp -Hool -Grul -Dhoop -Jair -Qim -Quisl -Fiaz -Prax -Aal -Righ -Ugh -Yog -Yig -Yair -Var -Vaq -Skog -Drij -Volg -Dem -Ghil -Uvar -Err -Azil -Obil -Zanzil -Zhat -Blug -Blig -Hroo -Waf -Xar -Grar -Lorp -Vuh -Ee -Ay -Oo -Ux -Ix -Whal -Mun -Ilun -Fargl -Xab -Frang -Bao -Bif -Xif -Zij -Quix -Vonch -Van -Klanc -Lug -Nirm -Zirm -Sil -Wev -Vil -Vran -Quiv -Squan -Squank -Squop -Akun -Apar -Epar -Iq -Exy -Eny -Ery -Uth -Ist -Ost -Hrosh -Imb -Omb -Onk -Arem -Urum -Irim -Irik -Eliv -Wep -Wroov -Droov -Seef -Hiqa -Ayy -Jaal -Khee -Ish -Xiliv -Whij -Koj -Krong -Swue -Aur -Eth -Qeth -Neth -Veth -Yeth -Aer -Aen -Ain -Aun -Uon -Xiv -Ung -Ong -Eng -Ehal -Dhar -Dhom -Hom -Kom -Rom -Qom -Eez -Dhezz -Elpz -Upz -Akz -Auz -Goaz -Goz -Mepz -Tepz -Crunz -Grenz -Purb -Ony -Igy -Eart -Ehng -Leng -Jub -Zhoon -Zhewt -Juju -Lhop -Lhow -Ewl -Ewil -Avul -Ap -Ip -Ep -Eb -En -El -Ek -Eh -Ez -Es -Ej -Ol -Om -Ob -Oc -Ot -Oz -Os -Oq -Mork -Morg -Borg -Franken -Pranken -Lankim -Khim -Khem -Vinken -Inken -Swohd -Glup -Blubil -Bulbil -Ziz -Miz -Lilip -Soong -Yolp -Grang -Grunc -Gharn -Bral -Criv -Durn -Flom -Griv -Harn -Jurn -Klom -Larn -Mird -Norl -Plen -Qarn -Rilk -Skiv -Tarm -Ulm -Vorn -Wim -Yorn -Blen -Cril -Dorn -Frel -Glim -Jiln -Kren -Lom -Morn -Nix -Polm -Quen -Ralk -Shil -Trok -Ulk -Vlim -Yiln -Brim -Crev -Dril -Flon -Gril -Jorn -Kliv -Lorn -Mirk -Norn -Plim -Qril -Rik -Skon -Trel -Ulv -Vril +brog +guyn +driz +oy +poin +broin +dray +hoy +khu +elbb +zee +teg +thig +volor +moror +zarn +zark +zarzl +zhong +zok +krinc +prunc +seld +serd +quo +quog +wast +writ +wub +eem +eo +ev +wuld +rond +trop +yhoo +urdd +ing +ogg +phef +aph +fro +drov +frol +gis +hirn +wilk +rof +roch +thip +throg +teng +tirk +tenk +twu +twal +yuth +yirg +ull +uk +iv +irch +ok +ort +ool +oon +oos +oop +pur +purth +pux +pix +ang +ank +ack +arr +arc +sor +shri +sarc +sib +spo +snil +dral +dax +druz +droon +tiong +snid +phra +vang +gawp +hool +grul +dhoop +jair +qim +quisl +fiaz +prax +aal +righ +ugh +yog +yig +yair +var +vaq +skog +drij +volg +dem +ghil +uvar +err +azil +obil +zanzil +zhat +blug +blig +hroo +waf +xar +grar +lorp +vuh +ee +ay +oo +ux +ix +whal +mun +ilun +fargl +xab +frang +bao +bif +xif +zij +quix +vonch +van +klanc +lug +nirm +zirm +sil +wev +vil +vran +quiv +squan +squank +squop +akun +apar +epar +iq +exy +eny +ery +uth +ist +ost +hrosh +imb +omb +onk +arem +urum +irim +irik +eliv +wep +wroov +droov +seef +hiqa +ayy +jaal +khee +ish +xiliv +whij +koj +krong +swue +aur +eth +qeth +neth +veth +yeth +aer +aen +ain +aun +uon +xiv +ung +ong +eng +ehal +dhar +dhom +hom +kom +rom +qom +eez +dhezz +elpz +upz +akz +auz +goaz +goz +mepz +tepz +crunz +grenz +purb +ony +igy +eart +ehng +leng +jub +zhoon +zhewt +juju +lhop +lhow +ewl +ewil +avul +ap +ip +ep +eb +en +el +ek +eh +ez +es +ej +ol +om +ob +oc +ot +oz +os +oq +mork +morg +borg +franken +pranken +lankim +khim +khem +vinken +inken +swohd +glup +blubil +bulbil +ziz +miz +lilip +soong +yolp +grang +grunc +gharn +bral +criv +durn +flom +griv +harn +jurn +klom +larn +mird +norl +plen +qarn +rilk +skiv +tarm +ulm +vorn +wim +yorn +blen +cril +dorn +frel +glim +jiln +kren +lom +morn +nix +polm +quen +ralk +shil +trok +ulk +vlim +yiln +brim +crev +dril +flon +gril +jorn +kliv +lorn +mirk +norn +plim +qril +rik +skon +trel +ulv +vril :: word ## A regular old word @@ -1011,3 +1012,4 @@ Sister Sour Pickled The Machine known as +# vim:set filetype=tbls: diff --git a/test/story.test.fnl b/test/story.test.fnl index f9fe37a..4909d23 100644 --- a/test/story.test.fnl +++ b/test/story.test.fnl @@ -5,11 +5,11 @@ (let [corpus (create-corpus "test/morpheme-word-epithet.txt") origin-key :name - origin-table ["[morpheme][word] [epithet]" - "[prefix] [morpheme][word] [epithet]" - "[prefix] [morpheme][word]" + origin-table ["[morpheme.c][word] [epithet]" + "[prefix] [morpheme.c][word] [epithet]" + "[prefix] [morpheme.c][word]" ] - origin-string "[morpheme][word] [epithet]" + origin-string "[morpheme.c][word] [epithet]" get-story-with-key (partial flatten corpus (. corpus origin-key)) get-story-with-table diff --git a/vim-tbls/README.md b/vim-tbls/README.md new file mode 100644 index 0000000..a725fbd --- /dev/null +++ b/vim-tbls/README.md @@ -0,0 +1,25 @@ +# vim-tbls + +syntax, filtetype detection, folding, and navigation for tbls files + +## installing + +1. find out where your `syntax` and `ftdetect` files are. e.g. `$HOME/.config/nvim/` or `$HOME/.vim/`. + +2. copy `syntax/tbls.vim` and `ftdetect/tbls.vim` to the appropriate folders + +after installing, any `.tbl` file should be detected. + +to use syntax for, e.g. a `.txt` file, in vim do `:set filetype=tbls` + +## using + +the syntax defines a foldable table block as the region between a `:: header` and a blank line. +all folding commands should work. +(e.g. `zj` and `zk` to jump to next fold. see `:help fold`.) +the plugin file sets the foldmethod to syntax, +so your tables should start out folded if folding in enabled. + +the plugin file also maps `]]` and `[[` +(next section and previous section, respectively) +to jump between table headers. diff --git a/vim-tbls/ftdetect/tbls.vim b/vim-tbls/ftdetect/tbls.vim new file mode 100644 index 0000000..da6b737 --- /dev/null +++ b/vim-tbls/ftdetect/tbls.vim @@ -0,0 +1 @@ +au BufRead,BufNewFile *.tbl set filetype=tbls diff --git a/vim-tbls/ftplugin/tbls.vim b/vim-tbls/ftplugin/tbls.vim new file mode 100644 index 0000000..c1fef4a --- /dev/null +++ b/vim-tbls/ftplugin/tbls.vim @@ -0,0 +1,3 @@ +setlocal foldmethod=syntax +nnoremap ]] m':call search('^::', "W") +nnoremap [[ m':call search('^::', "bW") diff --git a/vim-tbls/syntax/tbls.vim b/vim-tbls/syntax/tbls.vim new file mode 100644 index 0000000..9520c56 --- /dev/null +++ b/vim-tbls/syntax/tbls.vim @@ -0,0 +1,15 @@ +if exists("b:current_syntax") + finish +endif + +syntax match tblsComment "^#.*$" +syntax match tblsTitle "^:\+ .*$" +syntax match tblsRef "\[[^\]]*\]" + +syntax region tblsTable start=":: .*" end="^$" fold transparent + +highlight default link tblsComment Comment +highlight default link tblsTitle Statement +highlight default link tblsRef Type + +let b:current_syntax = "tbls"