aboutsummaryrefslogtreecommitdiff

– Expand the capabilities of the built-in strings {:List, :Dict} = require “containers” {:reverse, :upper, :lower, :find, :byte, :match, :gmatch, :gsub, :sub, :format, :rep, :char} = string

isplit = (sep=’%s+’)=> step = (i)=> start = @pos return unless start i += 1 nl = find(@str, @sep, start) @pos = nl and (nl+1) or nil line = sub(@str, start, nl and (nl-1) or #@str) return i, line, start, (nl and (nl-1) or #@str) return step, {str:@, pos:1, :sep}, 0

luakeywords = { [“and”]:true, [“break”]:true, [“do”]:true, [“else”]:true, [“elseif”]:true, [“end”]:true, [“false”]:true, [“for”]:true, [“function”]:true, [“goto”]:true, [“if”]:true, [“in”]:true, [“local”]:true, [“nil”]:true, [“not”]:true, [“or”]:true, [“repeat”]:true, [“return”]:true, [“then”]:true, [“true”]:true, [“until”]:true, [“while”]:true } isluaid = (str)-> match(str, “^[a-zA-Z][a-zA-Z0-9]*$”) and not luakeywords[str]

– Convert an arbitrary text into a valid Lua identifier. This function is injective, – but not idempotent. In logic terms: (x != y) => (asluaid(x) != asluaid(y)), – but not (asluaid(a) == b) => (asluaid(b) == b). asluaid = (str)-> – Escape ‘x’ (\x78) when it precedes something that looks like an uppercase hex sequence. – This way, all Lua IDs can be unambiguously reverse-engineered, but normal usage – of ‘x’ won’t produce ugly Lua IDs. – i.e. “x” -> “x”, “oxen” -> “oxen”, but “Hex2Dec” -> “Hex782Dec” and “He-ec” -> “Hex2Dec” str = gsub str, “x([0-9A-F][0-9A-F])”, “x78%1” – Map spaces to underscores, and everything else non-alphanumeric to hex escape sequences str = gsub str, “%W”, ©-> if c == ‘ ‘ then ‘_’ else format(“x%02X”, byte©)

unless is_lua_id(match(str, "^_*(.*)$"))
    str = "_"..str
return str

– fromluaid(asluaid(str)) == str, but behavior is unspecified for inputs that – did not come from asluaid() fromluaid = (str)-> unless isluaid(match(str, “^(.)$”)) str = sub(str,2,-1) str = gsub(str, ““, “ “) str = gsub(str, “x([0-9A-F][0-9A-F])”, (hex)-> char(tonumber(hex, 16))) return str

Text = { :isplit, uppercase:upper, lowercase:lower, reversed:reverse, :isluaid capitalized: => (gsub(@, ‘%l’, upper, 1)) byte: byte, asanumber: tonumber, asabase1number: tonumber, bytes: (i, j)=> List{byte(@, i or 1, j or -1)} split: (sep)=> List[chunk for i,chunk in isplit(@, sep)] startswith: (s)=> sub(@, 1, #s) == s endswith: (s)=> #@ >= #s and sub(@, #@-#s, -1) == s lines: => List[line for i,line in isplit(@, ‘\n’)] line: (linenum)=> for i, line, start in isplit(@, ‘\n’) return line if i == linenum

line_info_at: (pos)=>
    assert(type(pos) == 'number', "Invalid string position")
    for i, line, start, stop in isplit(@, '\n')
        if stop+1 >= pos
            return line, i, (pos-start+1)

indented: (indent="    ")=>
    indent..(gsub(@, "\n", "\n"..indent))

as_lua: =>
    escaped = gsub(@, "\\", "\\\\")
    escaped = gsub(escaped, "\n", "\\n")
    escaped = gsub(escaped, '"', '\\"')
    escaped = gsub(escaped, "[^ %g]", (c)-> format("\\%03d", byte(c, 1)))
    return '"'..escaped..'"'

as_nomsu: => @as_lua!

formatted_with:format, byte:byte,
position_of:((...)->(find(...))), position_of_1_after:((...)->(find(...))),
as_a_lua_identifier: as_lua_id, is_a_lua_identifier: is_lua_id,
as_lua_id: as_lua_id, as_a_lua_id: as_lua_id, is_a_lua_id: is_lua_id,
is_lua_id: is_lua_id, from_lua_id: from_lua_id,
bytes_1_to: (start, stop)=> List{byte(@, start, stop)}
[as_lua_id "with 1 ->"]: (...)-> (gsub(...))
bytes: => List{byte(@, 1, -1)},
wrapped_to: (maxlen=80, margin=8)=>
    lines = {}
    for line in *@lines!
        while #line > maxlen
            chunk = sub(line, 1, maxlen)
            split = find(chunk, ' ', maxlen-margin, true) or maxlen
            chunk = sub(line, 1, split)
            line = sub(line, split+1, -1)
            lines[#lines+1] = chunk
        lines[#lines+1] = line
    return table.concat(lines, "\n")

line_at: (i)=> (@line_info_at(i))
line_number_at: (i)=> select(2, @line_info_at(i))
line_position_at: (i)=> select(3, @line_info_at(i))
matches: (patt)=> match(@, patt) and true or false
matching: (patt)=> (match(@, patt))
matching_groups: (patt)=> List{match(@, patt)}
[as_lua_id "* 1"]: (n)=> rep(@, n)
all_matches_of: (patt)=>
    result = {}
    stepper,x,i = gmatch(@, patt)
    while true
        tmp = List{stepper(x,i)}
        break if #tmp == 0
        i = tmp[1]
        result[#result+1] = (#tmp == 1) and tmp[1] or tmp
    return List(result)
from_1_to: sub, from: sub,
character: (i)=> sub(@, i, i)

} for k,v in pairs(string) do Text[k] or= v

1as_text = (x)-> if x == true then return “yes” if x == false then return “no” return tostring(x)

setmetatable Text, __call: (…)=> ret = {…} for i=1,select(“#”, …) ret[i] = 1as_text(ret[i]) return table.concat(ret)

debug.setmetatable ““, __type: “Text” __index: (k)=> Text[k] or (type(k) == ‘number’ and sub(@, k, k) or nil) __add: (x)=> 1astext(@)..1astext(x)

return Text