first

main vHydrogen
Dozens B. McCuzzins 2024-08-02 12:22:14 -06:00
commit 365fabdf67
10 changed files with 1771 additions and 0 deletions

1
.gitignore vendored 100644
View File

@ -0,0 +1 @@
tbls

33
README.md 100644
View File

@ -0,0 +1,33 @@
# tbls
for expanding random table entries
## Requirements
Built with
Fennel 1.3.1 on PUC Lua 5.4
## Usage
You can run the script: `fennel src/main.fnl`.
Or you can compile a binary and use that.
See `just compile`.
## Roadmap
- [x] random table entries (ADDED in vHydrogen)
- [x] expanding macros (ADDED in vHydrogen)
- [ ] table files plugin: syntax highlighting + folding
- [ ] expansion filters
## Resources
inspired heavily by tracery:
https://github.com/galaxykate/tracery
and paper elemental's list-to-html generator:
https://paperelemental.blogspot.com/p/list-to-html-generator.html
and perchance:
https://perchance.org/welcome

48
doc/Tutorial.md 100644
View File

@ -0,0 +1,48 @@
## Tutorial
At its most basic, `tbls` selects a random element from a table.
Suppose you have a few tables:
```
:: suit
Hearts
Bells
Whistles
Cups
Knives
Shovels
:: card
Ace
One
Two
...
Ten
Eleven
Twelve
Thief
Queen
King
Beast
```
`tbls` might return "Whistles" from suit.
Or "Twelve" from card.
But wait there's more.
`tbls` will also expand macros found in table entries.
Let's add another table:
```
:: draw
[card] of [suit]
```
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"
or "Twelve or Cups".

9
doc/meta.rec 100644
View File

@ -0,0 +1,9 @@
%rec: meta
%doc: project metadata
%type: version rec version
name: tbls
author: dozens@tilde.team
created: 2024-07-31
updated: 2024-08-01
version: Hydrogen

481
doc/versions.rec 100644
View File

@ -0,0 +1,481 @@
%rec: version
%doc: a table of version names for "tables" based on the most famous table
%type: Number int
%type: Symbol,Name line
%allowed: Number Symbol Name
%mandatory: Number Symbol Name
%unique: Number Symbol Name
%sort: Number
%key: Name
Number: 1
Symbol: H
Name: Hydrogen
Number: 2
Symbol: He
Name: Helium
Number: 3
Symbol: Li
Name: Lithium
Number: 4
Symbol: Be
Name: Beryllium
Number: 5
Symbol: B
Name: Boron
Number: 6
Symbol: C
Name: Carbon
Number: 7
Symbol: N
Name: Nitrogen
Number: 8
Symbol: O
Name: Oxygen
Number: 9
Symbol: F
Name: Fluorine
Number: 10
Symbol: Ne
Name: Neon
Number: 11
Symbol: Na
Name: Sodium
Number: 12
Symbol: Mg
Name: Magnesium
Number: 13
Symbol: Al
Name: Aluminum
Number: 14
Symbol: Si
Name: Silicon
Number: 15
Symbol: P
Name: Phosphorus
Number: 16
Symbol: S
Name: Sulfur
Number: 17
Symbol: Cl
Name: Chlorine
Number: 18
Symbol: Ar
Name: Argon
Number: 19
Symbol: K
Name: Potassium
Number: 20
Symbol: Ca
Name: Calcium
Number: 21
Symbol: Sc
Name: Scandium
Number: 22
Symbol: Ti
Name: Titanium
Number: 23
Symbol: V
Name: Vanadium
Number: 24
Symbol: Cr
Name: Chromium
Number: 25
Symbol: Mn
Name: Manganese
Number: 26
Symbol: Fe
Name: Iron
Number: 27
Symbol: Co
Name: Cobalt
Number: 28
Symbol: Ni
Name: Nickel
Number: 29
Symbol: Cu
Name: Copper
Number: 30
Symbol: Zn
Name: Zinc
Number: 31
Symbol: Ga
Name: Gallium
Number: 32
Symbol: Ge
Name: Germanium
Number: 33
Symbol: As
Name: Arsenic
Number: 34
Symbol: Se
Name: Selenium
Number: 35
Symbol: Br
Name: Bromine
Number: 36
Symbol: Kr
Name: Krypton
Number: 37
Symbol: Rb
Name: Rubidium
Number: 38
Symbol: Sr
Name: Strontium
Number: 39
Symbol: Y
Name: Yttrium
Number: 40
Symbol: Zr
Name: Zirconium
Number: 41
Symbol: Nb
Name: Niobium
Number: 42
Symbol: Mo
Name: Molybdenum
Number: 43
Symbol: Tc
Name: Technetium
Number: 44
Symbol: Ru
Name: Ruthenium
Number: 45
Symbol: Rh
Name: Rhodium
Number: 46
Symbol: Pd
Name: Palladium
Number: 47
Symbol: Ag
Name: Silver
Number: 48
Symbol: Cd
Name: Cadmium
Number: 49
Symbol: In
Name: Indium
Number: 50
Symbol: Sn
Name: Tin
Number: 51
Symbol: Sb
Name: Antimony
Number: 52
Symbol: Te
Name: Tellurium
Number: 53
Symbol: I
Name: Iodine
Number: 54
Symbol: Xe
Name: Xenon
Number: 55
Symbol: Cs
Name: Cesium
Number: 56
Symbol: Ba
Name: Barium
Number: 57
Symbol: La
Name: Lanthanum
Number: 58
Symbol: Ce
Name: Cerium
Number: 59
Symbol: Pr
Name: Praseodymium
Number: 60
Symbol: Nd
Name: Neodymium
Number: 61
Symbol: Pm
Name: Promethium
Number: 62
Symbol: Sm
Name: Samarium
Number: 63
Symbol: Eu
Name: Europium
Number: 64
Symbol: Gd
Name: Gadolinium
Number: 65
Symbol: Tb
Name: Terbium
Number: 66
Symbol: Dy
Name: Dysprosium
Number: 67
Symbol: Ho
Name: Holmium
Number: 68
Symbol: Er
Name: Erbium
Number: 69
Symbol: Tm
Name: Thulium
Number: 70
Symbol: Yb
Name: Ytterbium
Number: 71
Symbol: Lu
Name: Lutetium
Number: 72
Symbol: Hf
Name: Hafnium
Number: 73
Symbol: Ta
Name: Tantalum
Number: 74
Symbol: W
Name: Tungsten
Number: 75
Symbol: Re
Name: Rhenium
Number: 76
Symbol: Os
Name: Osmium
Number: 77
Symbol: Ir
Name: Iridium
Number: 78
Symbol: Pt
Name: Platinum
Number: 79
Symbol: Au
Name: Gold
Number: 80
Symbol: Hg
Name: Mercury
Number: 81
Symbol: Tl
Name: Thallium
Number: 82
Symbol: Pb
Name: Lead
Number: 83
Symbol: Bi
Name: Bismuth
Number: 84
Symbol: Po
Name: Polonium
Number: 85
Symbol: At
Name: Astatine
Number: 86
Symbol: Rn
Name: Radon
Number: 87
Symbol: Fr
Name: Francium
Number: 88
Symbol: Ra
Name: Radium
Number: 89
Symbol: Ac
Name: Actinium
Number: 90
Symbol: Th
Name: Thorium
Number: 91
Symbol: Pa
Name: Protactinium
Number: 92
Symbol: U
Name: Uranium
Number: 93
Symbol: Np
Name: Neptunium
Number: 94
Symbol: Pu
Name: Plutonium
Number: 95
Symbol: Am
Name: Americium
Number: 96
Symbol: Cm
Name: Curium
Number: 97
Symbol: Bk
Name: Berkelium
Number: 98
Symbol: Cf
Name: Californium
Number: 99
Symbol: Es
Name: Einsteinium
Number: 100
Symbol: Fm
Name: Fermium
Number: 101
Symbol: Md
Name: Mendelevium
Number: 102
Symbol: No
Name: Nobelium
Number: 103
Symbol: Lr
Name: Lawrencium
Number: 104
Symbol: Rf
Name: Rutherfordium
Number: 105
Symbol: Db
Name: Dubnium
Number: 106
Symbol: Sg
Name: Seaborgium
Number: 107
Symbol: Bh
Name: Bohrium
Number: 108
Symbol: Hs
Name: Hassium
Number: 109
Symbol: Mt
Name: Meitnerium
Number: 110
Symbol: Ds
Name: Darmstadtium
Number: 111
Symbol: Rg
Name: Roentgenium
Number: 112
Symbol: Cn
Name: Copernicium
Number: 113
Symbol: Nh
Name: Nihonium
Number: 114
Symbol: Fl
Name: Flerovium
Number: 115
Symbol: Mc
Name: Moscovium
Number: 116
Symbol: Lv
Name: Livermorium
Number: 117
Symbol: Ts
Name: Tennessine
Number: 118
Symbol: Og
Name: Oganesson

34
justfile 100644
View File

@ -0,0 +1,34 @@
# show all recipes
default:
just --list --unsorted
# compile binary
compile:
fennel --compile-binary src/main.fnl tbls /usr/local/lib/liblua.a /usr/local/include/lua5.4
# run test file
_test-story:
fennel test/story.test.fnl
# test main
_test-main:
for i in $(seq 1 10); do fennel src/main.fnl -i test/morpheme-word-epithet.txt -k name; done
# run all tests
test: _test-main _test-story
# bump version
bump:
#!/usr/local/bin/bash
currname=$(recsel doc/meta.rec -P version)
currnum=$(recsel doc/versions.rec -e "Name = '$currname'" -P Number)
nextnum=$((currnum + 1))
nextname=$(recsel doc/versions.rec -e "Number = $nextnum" -P Name)
echo "Bumping version from $currname to $nextname:"
recset doc/meta.rec -f version -s $nextname
recset doc/meta.rec -f updated -S $(gdate +'%Y-%m-%d')
recsel doc/meta.rec
# show full metadata
meta:
awk 'FNR==1{print ""}{print}' doc/*.rec | recsel -t meta -j version

49
src/main.fnl 100644
View File

@ -0,0 +1,49 @@
(local {
: flatten
: create-corpus
} (require :src.story))
(fn show-help []
(print "Usage: tbls [options]")
(print)
(print "Basic Options:")
(print " -h|--help print this message and exit")
(print " -i|--input <file> name of input file")
(print " -k|--origin-table-key <key> name of a table in the input file")
(print " -s|--origin-table-string <string> a string template")
(print)
(print "You must specify an input file")
(print "You must specify either a string or a key to serve as an origin"))
(fn parse-args [t]
(if (= 0 (length t))
(do
(show-help)
(os.exit 0)))
(fn inner [t opts]
(if (= 0 (length t)) opts
(do
(case t
(where [a] (or (= a "-h") (= a "--help")))
(do
(show-help)
(os.exit 0))
(where [a input] (or (= a "-i") (= a "--input")))
(set opts.input input)
(where [a key] (or (= a "-k") (= a "--origin-table-key")))
(set opts.key key)
(where [a str] (or (= a "-s") (= a "--origin-table-string")))
(set opts.string str))
(let [next-t (icollect [i v (ipairs t)] (if (> i 2) v))]
(inner next-t opts)))))
(inner t {}))
(fn main []
(let [opts (parse-args arg)
corpus (create-corpus opts.input)
]
(if opts.key
(print (flatten corpus (. corpus opts.key)))
(print (flatten corpus opts.string)))))
(main)

80
src/story.fnl 100644
View File

@ -0,0 +1,80 @@
;; 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))
(fn lines [filename callback]
(case (pcall #(with-open [file (io.open filename)] (each [line (file:lines)] (callback line))))
(false err) (print (string.format "Error: Could not open file %s\n%s" filename err))))
(fn _create-corpus [lines data]
(var current-key nil)
(var corpus {})
(lines data
#(let [key (string.match $1 "^::%s+([%a-]+)")
blank? (or (= nil $1) (= "" $1))
comment? (string.match $1 "^#")
]
(when (and (not blank?) (not comment?))
(if (not key)
(let [list (. corpus current-key)]
(table.insert list $1)
(tset corpus current-key list))
(do
(set current-key key)
(tset corpus current-key []))))))
corpus)
(local create-corpus (partial _create-corpus lines))
(fn one-of [t]
"returns a random element of a sequential or non-sequential table"
(let [len (accumulate [l 0 _ _ (pairs t)] (+ l 1)) ;; do it the hard way
;; because nonseq tables
;; have no length?
handle (io.popen "echo $RANDOM")
output (handle:read "*a")
random (output:gsub "[\n\r]" "")
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))
rndkey (. keys idx)
]
(. t rndkey)))
(fn flatten [corpus origin]
(let [str (case (type origin)
"string" origin
"table" (one-of origin)
_ (error "Origin must be a table or a string"))
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
(if (not i)
str
(do
(assert (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))
(string.sub str (+ j 1)))]
(flatten corpus next-str j)))))) ;; this is a tail call!
{: create-corpus
: flatten
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,23 @@
(let [{
: flatten
: create-corpus
} (require :src.story)]
(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-string "[morpheme][word] [epithet]"
get-story-with-key
(partial flatten corpus (. corpus origin-key))
get-story-with-table
(partial flatten corpus origin-table)
get-story-with-string
(partial flatten corpus origin-string)
]
(print "\n== Cast of Characters ==")
(for [_ 1 4] (print (get-story-with-key)))
(for [_ 1 4] (print (get-story-with-table)))
(for [_ 1 4] (print (get-story-with-string)))))