require 'posix' List = require 'pl.List' stringx = require 'pl.stringx' file = require 'pl.file' -- seed random number generator seed = posix.unistd.getpid() * os.time() seed = seed % (2^31) math.randomseed(seed) -- return a random integer in the range 1..value function randomval (value) return (math.ceil(math.random() * value)) end -- split a string on a delimiter (stripping whitespice around delimiters) function split(str, delim) delim = delim and ('%s*'..delim..'%s*') or '%s+' local t = List.new() local fpat = '(.-)'..delim local last_end = 1 local s, e, cap = str:find(fpat, 1) while s do if s ~= 1 or cap ~= "" then t:append(cap) end last_end = e + 1 s, e, cap = str:find(fpat, last_end) end if last_end <= #str then cap = str:sub(last_end) t:append(cap) end return t end -- select a random member of a List function pickone (choices) return (split(choices[randomval(#choices)])) end -- return the index of the first word in wordlist that is a key in dict function find_key (dict, wordlist) for i = 1, #wordlist do if (dict[wordlist[i]]) then return i end end return nil end -- Recursively process a list of words, expanding the dictionary keys into -- randomly-selected replacement phrases" function generate_phrase (words) if #words == 0 then return List.new() end local left, choices, right local divvy = function(i) left = words:slice(1, i-1) choices = repl[words[i]] right = words:slice(i+1, -1) end local i = find_key(repl, words) if i and pcall(divvy, i) then return (left .. generate_phrase(pickone(choices)..right)) else return (words) end end -- load the data file function load_data (filename) filename = filename or "/ext/www/foods.txt" local content = file.read(filename) local data = stringx.splitlines(content) sentence = data:pop(1) repl = List.new() for i,line in pairs(data) do line = split(line, ':') repl[line:pop(1)] = line -- store replacement phrases in dictionary end end -- capitalize the first letter of a string function capitalize (str) return (str:sub(1,1):upper()..str:sub(2)) end -- CGI/HTML helper functions function content_type (typ, fp) typ = typ or 'text/html' fp = fp or io.stdout fp:write("Content-type: "..typ.."\n\n") end function doc_type(fp) fp = fp or io.stdout fp:write('\n') end function headers(title, fp) title = title or "unknown" fp = fp or io.stdout fp:write("\n") fp:write(""..title.."\n") fp:write("\n") fp:write('\n') end function startlist(header, fp) fp = fp or io.stdout if header then fp:write("

" .. header .. ":

\n") end fp:write("") end function footer(fp) fp = fp or io.stdout fp:write("\n") end function howmany() local url = os.getenv("REQUEST_URI") local n if not url then return (5) end _, _, n = url:find('?n=(%d+)') if n then n = tonumber(n) end if not n then n = 5 elseif n > 100 then n = 100 end return (n) end -- -- main -- load_data() -- generate HTML content_type() headers("Potluck menu") startlist("This week's potluck will feature") for i = 1, howmany() do food = generate_phrase(split(sentence)):join(' ') bullet (capitalize(food)) end endlist() footer()