Jump to content

Module:Escape

From Elite Dangerous Wiki
Revision as of 01:23, 17 August 2021 by Wikipedia>MusikBot II (Changed protection settings for "Module:Escape": High-risk template or module 8904 transclusions (more info) ([Edit=Require template editor access] (indefinite) [Move=Require template editor access] (indefinite)))
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Usage

This module is designed as an way to escape strings in a customized and efficient manner. It works by replacing characters that are preceded by your escape char (or phrase) There are two ways to call this module:

From another module:

local esc = require('Module:Escape')
esc:char(escape char (or sequence))
local to_escape = esc:text(string)
code that replaces or removes unescaped chars
local result = esc:undo(to_escape)

From a template:

{{#invoke:Escape|main|mode=function|char=escape char (or sequence)|text}}

In a template, the most useful function is kill.

This module is primarily intended to be used by other modules. However all functions can be called in template space using |mode=the function you want to call followed by arguments.

All module functions (i.e. any func. other than main()) should be called using a colon (:), e.g. esc:char('%') or esc:kill{'{{example|\\}}}', '}'} == '{{example|}'

escape:text()

escape:text()
This function takes only one argument: A string. All characters in this string which are preceded by the sequence set by escape:char() will be replaced with placeholders that can be converted back into that char by escape:undo()

escape:undo()

escape:undo()
Takes two arguments:
  1. The string that may contain placeholders set by escape:text()
  2. Optional, a char to be placed in front of any characters that have been de-escaped. (i.e. if you need to re-escape those string with a different char)

escape:kill()

escape:kill()
This is basically equivalent to calling string.gsub() on the string returned by escape:text() and feeding that result into escape:undo() in a single step. Takes three arguments:
  1. A string
  2. A sequence of characters to be removed from that string. (May use a string.gsub() pattern)
  3. Optional, a char to be placed in front of any characters that have been de-escaped.

escape:char()

escape:char()
This function's primary use is to initialize the patterns to scan a string for an escape/escaped sequence. It takes two arguments, the first being the escape character and the second being a table of arguments (optional). By default, this module will escape the \ char. To escape the { char instead, you can do require('Module:Escape'):char('{') (or esc:char('{') (presuming you stored the table returned by this module in the local variable esc).

When called without the second argument, char() will return a table containing the functions. This allows, for example, escape:char('*'):kill('1*23', '%d') which would return '2'.

For the most part, there is very little reason to set |mode= in template space since the patterns it stores are not shared with other invokations of this module. Templates should instead use the |char= if a new escape sequence is desired.

Shortcut

If provided a second argument that is a table containing a {key = value} pair, such that the key is text, undo, or kill and the value is a table containing the arguments that would have been passed to those functions. For example, escape:char('\\', {text = 'string'}) is equivalent to escape:char('\\'):text('string').

Note that if multiple key-value pairs are provided, only one may execute. kill is ignored if either text or undo are present. undo is ignored if text is present.

Caveats

  • When using a multi-character escape sequence, this module only marks it using the byte value of the first character. Thus, escape:undo() will unescape, for example, all characters escaped with 'e' and 'esc' if both were used. In practice however this shouldn't be a problem as multiple escape sequences are pretty rare unless you're transitioning between multiple code languages. (Multiple multi-char escape sequences beginning with the same character are simply bad practice anyhow.)
  • Since byte values are stored as numbers, it is not recommended for you to use a number as an escape sequence (though it may work just fine).
  • Placeholder byte values separated with return ('\r') characters--chosen because they are seldom used at all, and virtually never used unpaired with '\n'; moreover, it is distinct from the markers generated by <nowiki>...</nowiki> or mw.text.nowiki() (which use the delete char). To set a different separator char, include the key-value pair {safeChr = alternate character} in the table that you pass to escape:char().

Examples

Template

Module talk:Escape/testcases

Module

Here's some sample output from the debug console below the module editor:

local escape = require('Module:Escape')
test = 'test, \\test, \\{,test\\\\ \\\\ \\\\\\\\'

test2 = escape:char('{'):text(test)
=test2

test, \test, \7b 044 7btest\\ \\ \\\\ test3 = escape:char('\\'):text(test2)
=test3

test, 5c 0116 5cest, 5c 055 5cb 044 7btest5c 092 5c 5c 092 5c 5c 092 5c5c 092 5c test4 = escape:char('{', {undo = test3})
=test4

test, 5c 0116 5cest, 5c 055 5cb 044 7btest5c 092 5c 5c 092 5c 5c 092 5c5c 092 5c test4 = escape:char('\\', {undo = test3})
=test4

test, test, 7b 044 7btest\ \ \\ test5 = escape:char('{', {undo = test4})
=test5 == test

true =escape:undo(test3)--doesn't work because char is still set to '{' in current session
test, 5c 0116 5cest, 5c 055 5cb 044 7btest5c 092 5c 5c 092 5c 5c 092 5c5c 092 5c =escape:undo(test4)
test, \test, \,test\\ \\ \\\\ =escape:char('\\'):undo(test3)
test, test, 7b 044 7btest\ \ \\ =escape:char('{', {undo = escape:char('\\'):undo(test3)})
test, test, {,test\ \ \\ =test == escape:char('{', {undo = escape:char('\\'):undo(test3)})
false =test == escape:char('{', {undo = escape:char('\\'):undo(test3, '\\')})
true local t = 'test { test {\\{ test, \\test, \\{,test\\ \\ \\ {\\'
=t

test { test {\{ test, \test, \{,test\ \ \ {\ local e = require('Module:Escape')
local t2 = escape:text(t)
local t3 = string.gsub(t2, '{', )
local t4 = escape:undo(t3)
=t4

test test { test, test, {,test \ local tk0 = escape:kill(t, '{')
=tk0 == t4

true

local escape = {
	char = function(self, chr, args)
		args = args or {}
		local safe = args.safeChr or string.char(13)
		chr = tostring(chr or '\\')
		self[1] = ('%s0%%s%s'):format(
			('%x%s%s'):format(chr:byte(), safe, safe),
			('%s%x'):format(safe, chr:byte())
		)
		if not self[self[1]] then
			self[self[1]] = {
				char = chr,
				text = ('%s(.)'):format(chr),
				undo = self[1]:format'(%d+)'
			}
		end
		return args.text and self:text(args.text)
			or args.undo and self:undo(args.undo, chr)
			or args.kill and self:kill(args.kill)
			or self
	end,
	exec = function(self, text, mode, newEscape)
		local target = self[self[1] or self:char() and self[1]]
		for v in text:gfind(target[mode]) do
			text = text:gsub(
				mode == 'text' and
					('%s%s'):format(target.char, v:gsub('%W', '%%%1'))
					or self[1]:format(v),
				mode == 'text' and
					self[1]:format(v:byte())
					or (newEscape or '') .. v:char()
			)
		end
		return text
	end,
	text = function(self, text)
		return self:exec(type(text) == 'table' and text[1] or text, 'text')
	end,
	undo = function(self, text, newEscape)
		if type(text) == 'table' then
			text, newEscape = unpack(text)
		end
		return self:exec(text, 'undo', newEscape)
	end,
	kill = function(self, text, chars, newEscape)
		if type(text) == 'table' then
			text, chars, newEscape = unpack(text)
		end
		return self:undo(self:text(text):gsub(chars or '', ''), newEscape)
	end
}

function escape.main(frame)
	local args, family = {}, {frame:getParent(), frame}
	for f = 1, 2 do
		for k, v in pairs(family[f] and family[f].args or {}) do
			args[k] = args[k] or v:match('^%s*(.-)%s*$')
		end
	end
	if args.mode == 'char' then
		return escape:char(args.char or args[2], args)
	end
	return escape[args.mode](escape:char(args.char), args)
end

return escape