smr/src/lua/parser_search.lua

86 lines
2.5 KiB
Lua

local lpeg = require('lpeg')
local etlua = require('etlua')
local config = require("config")
local args = {...}
lpeg.locale(lpeg)
local V,P,C,S,B,Cs = lpeg.V,lpeg.P,lpeg.C,lpeg.S,lpeg.B,lpeg.Cs
--Identity function
local ident = function(a) return a end
--Lowercase and capitalize first letter
local function capitalize(str)
return string.lower(str):gsub("^(.)",string.upper)
end
--Trim whitespace
local function trim(str)
return str:match("^%s*(.-)%s*$")
end
--SQL like match anywhere
local function like(str)
return "%" .. str .. "%"
end
--Tags are always trimed of whitepsace, lowercase with first letter capitalized
local function tag_fmt(str)
return capitalize(trim(str))
end
--Title match
local function title_fmt(str)
return like(trim(str))
end
--Author names are all lowercase alphanumeric, max 30 characters
local function author_fmt(str)
return like(string.lower(trim(str)))
end
local fieldnames = {
title = {name="title",type="string",fmt=title_fmt},
author = {name="author",type="string",fmt=author_fmt},
date = {name="date",type="number",fmt=tonumber},
hits = {name="hits",type="number",fmt=tonumber},
tags = {name="tag",type="string",fmt=tag_fmt},
}
local field_default = "tag"
local fields
local grammar = P{
"chunk";
whitespace = S" \t\n"^0,
itm = C((P(1 - (P" " * S"+-")))^0), --go until the next '+' or '-'
likefield = C(P"title" + P"author") * V"whitespace" * C(P"=") * V"whitespace" * V"itm",
rangeop = P"<=" + P">=" + P">" + P"<" + P"=",
rangefield = C(P"date" + P"hits") * V"whitespace" * C(V"rangeop") * V"whitespace" * C(V"itm"),
field = C(S"+-") * (V"likefield" + V"rangefield" + V"itm") / function(pn,field,expr,value)
if expr and value then
fields[field] = fields[field] or {}
table.insert(fields[field],{pn,expr,value})
else
fields.tags = fields.tags or {}
table.insert(fields.tags,{pn,"=",field})
end
end,
chunk = V"field" * (P" " * V"field")^0
}
--Grammar
--Transpile a sting with + and - into an sql query that searches tags
local fname = config.approot .. "pages/search_sql.etlua"
local sqltmpl = assert(io.open(fname))
local c = etlua.compile(sqltmpl:read("*a"),fname)
sqltmpl:close()
local function transpile(str)
str = string.lower(str)
fields = {tags={}}
table.concat({grammar:match(str)}," ")
--Sanity perform formatting on data
for field,values in pairs(fields) do
for _,value in pairs(values) do
local pn, expr, val = unpack(value)
local nval = fieldnames[field].fmt(val)
value[3] = nval
end
end
local ressql = c{
result = fields
}
return ressql, fields
end
return transpile