;; helper funs (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)))) (false err) (print (string.format "Error: Could not open file %s\n%s" filename err)))) (fn _create-corpus [lines data] (var current-key nil) (local 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 _ (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 raw-word (or (string.match str word-pattern) str) [word & fs] (split raw-word) ] (if (not i) str (do (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)) (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! {: create-corpus : flatten }