lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


On Fri, Aug 21, 2015 at 05:06:52PM +0200, Dirk Laurie wrote:
> 2015-08-21 16:04 GMT+02:00 Coda Highland <chighland@gmail.com>:
> 
> > But the thing is, if THAT'S what you want, Lua already has it!
> >
> > ymd_"2015/08/01" is, by Lua's standard parsing rules, equivalent to
> > ymd_("2015/08/01").
> 
> I know. But I might not remember if it is ymd or ymd_ or YMD or YMD_
> or date or date_ or whatever.  Nicest would be just 2015/08/01 but Lua
> already treats that as something. <2015/08/01> is a good second.
> The same delimiters apply to all user literals, and if they follow a pattern
> well known to the intended user community, there is nothing extra to be
> memorized.

An LPeg grammar to parse Lua source code is so small that there's really no
excuse for not coming to the table having already tested out the idea. Your
proposal might cause all sorts of headaches with the grammar, especially
because < is already a binary operator.

The following is a quick copy+paste from a file I had lying around, which I
wrote when implementing a declarative language for a filtering engine that
permitted embedding arbitrary Lua code as actions. It might not be from the
working copy; I didn't test. Any missing pattern definition, foo, should
just be V"foo".

If you can make your proposal work your case would be much stronger. But I
really can't imagine how user-defined literals could be made much easier.
E.g. L"2015/08/01", where L is a function that traverses your pattern set.

--
-- L U A  G R A M M A R
--
-- Lua 5.2 Syntax from http://www.lua.org/manual/5.2/manual.html#9
--
-- Right-recursive refactoring from http://leg.luaforge.net/ with some
-- changes to rely on proper PEG lookahead assertions to resolve prefixexp
-- ambiguities instead of gross hacks.
--
-- -------------------------------------------------------------------------

local endstring = P"]" * Cb("eq") * P"]"
local _longstring = P"[" * Cg(P"="^0, "eq") * P"[" * (P(1) - endstring)^0 * endstring
local longstring = _longstring / function() return end --> discard group/back captures
local escape = P"\\" * P(1)
local doublequote = P'"' * (escape + (P(1) - S('"\n\r\f')))^0 * P'"'
local singlequote = P"'" * (escape + (P(1) - S("'\n\r\f")))^0 * P"'"
local shortstring = doublequote + singlequote
local longcomment = P"--" * longstring
local shortcomment = P"--" * (-P"\n" * P(1))^0 * P"\n"
local comment = longcomment + shortcomment
local whitespace = S" \t\n\r\f"^1
local space = (whitespace + comment)^0
local shebang = P"#!" * (P(1) - P"\n")^0 * P"\n"
local nameprefix = R("AZ", "az") + P"_"
local namesuffix = R("AZ", "az", "09") + P"_"

local function K(word) -- future keyword augmentations
	return P(word)
end

local function J(patt, ...) -- join sequences with whitespace breaks
	if patt then
		-- consuming whitespace upfront also makes lists work
		return space * patt * J(...)
	else
		return P(true)
	end
end

local function E(list, terminal) -- generate list of terminal matches
	local patt = P(list[1])

	for i = 2, #list do
		patt = patt + P(list[i])
	end

	-- exclude prefix matches
	return patt * terminal
end

local LuaGrammar = G{ shebang^-1 * V"block",
	block = J(stat^0, retstat^-1) * space,
	stat = P";"
	     + J(varlist, "=", explist)
	     + functioncall
	     + label
	     + K"break"
	     + J(K"goto", Name)
	     + J(K"do", block, K"end")
	     + J(K"while", exp, K"do", block, K"end")
	     + J(K"repeat", block, K"until", exp)
	     + J(K"if", exp, K"then", block, J(K"elseif", exp, K"then", block)^0, J(K"else", block)^-1, K"end")
	     + J(K"for", Name, "=", exp, ",", exp, J(",", exp)^-1, K"do", block, K"end")
	     + J(K"for", namelist, K"in", explist, K"do", block, K"end")
	     + J(K"function", funcname, funcbody)
	     + J(K"local", K"function", Name, funcbody)
	     + J(K"local", namelist, J(P"=", explist)^-1),
	retstat = J(K"return", explist^-1, P";"^-1),
	label = J(P"::", Name, P"::"),
	funcname = J(Name, J(P".", Name)^0, J(":" * Name)^-1),
	varlist = J(var, J(",", var)^0),
	var = prefixexp,
	namelist = J(Name, J(P",", Name)^0),
	explist = J(exp, J(P",", exp)^0),
	simplexp = K"nil"
	     + K"false"
	     + K"true"
	     + Number
	     + String
	     + P"..."
	     + functiondef
	     + prefixexp
	     + tableconstructor
	     + J(unop, simplexp),
	prefixexp = J(Name + parenexp, (bracexp + dotexp + argsexp + colonexp)^0),
	parenexp = J(P"(", exp, P")"),
	bracexp = J(P"[", exp, P"]"),
	dotexp = J(P".", Name),
	argsexp = args,
	colonexp = J(P":", Name, args),
	exp = J(simplexp, J(binop, simplexp)^0),
	functioncall = prefixexp,
	args = J(P"(", explist^-1, P")") + tableconstructor + String,
	functiondef = J(K"function", funcbody),
	funcbody = J(P"(", parlist^-1, P")", block, K"end"),
	parlist = J(namelist, J(P",", P"...")^-1) + P"...",
	tableconstructor = J(P"{", fieldlist^-1, P"}"),
	fieldlist = J(field, J(fieldsep, field)^0, fieldsep^-1),
	field = J(P"[", exp, P"]", P"=", exp)
	      + J(Name, "=", exp)
	      + exp,
	fieldsep = P"," + P";",
	binop = P"and" + P"or" + P"~=" + P"==" + P">=" + P"<=" + P">" + P"<"
	      + P".." + P"%" + P"^" + P"/" + P"*" + P"-" + P"+",
	unop = P"-" + P"not" + P"#",
	keyword = E({ -- NOTE: swapped else/elseif from Lua reference manual
		"and", "break", "do", "elseif", "else", "end", "false",
		"for", "function", "if", "in", "local", "nil", "not",
		"or", "repeat", "return", "then", "true", "until", "while"
	}, -namesuffix),
	Name = (nameprefix * namesuffix^0) - keyword,
	String = shortstring + longstring,
	Number = R"09"^1 --> FIXME
}