diff options
| author | Bruce Hill <bitbucket@bruce-hill.com> | 2018-09-10 15:55:34 -0700 |
|---|---|---|
| committer | Bruce Hill <bitbucket@bruce-hill.com> | 2018-09-10 15:56:00 -0700 |
| commit | 43e6523fd42f736acfcd0c32d82246c262e07a1d (patch) | |
| tree | 5a2d6b5250528dc42ce7433374cac6c0c65859a9 /containers.moon | |
| parent | 603c5b12451d6c68b1e41906e10117da4d99e362 (diff) | |
Shifting towards more text methods instead of text global functions.
Also fixed a bug with method call parenthesizing.
Diffstat (limited to 'containers.moon')
| -rw-r--r-- | containers.moon | 101 |
1 files changed, 100 insertions, 1 deletions
diff --git a/containers.moon b/containers.moon index 68c618b..a9f6857 100644 --- a/containers.moon +++ b/containers.moon @@ -2,6 +2,8 @@ {:insert,:remove,:concat} = table {:repr, :stringify, :equivalent, :nth_to_last, :size} = require 'utils' +lpeg = require 'lpeg' +re = require 're' local List, Dict @@ -41,6 +43,9 @@ _list_mt = last: (=> @[#@]), first: (=> @[1]) _1_st_to_last:nth_to_last, _1_nd_to_last:nth_to_last _1_rd_to_last:nth_to_last, _1_th_to_last:nth_to_last + -- TODO: use stringify() to allow joining misc. objects? + joined: => table.concat([tostring(x) for x in *@]), + joined_with_1: (glue)=> table.concat([tostring(x) for x in *@], glue), has_1: (item)=> for x in *@ if x == item @@ -101,4 +106,98 @@ Dict = (t)-> setmetatable(t, _dict_mt) for i,entry in ipairs(Dict({x:99})) assert(i == 1 and entry.key == "x" and entry.value == 99, "ipairs compatibility issue") -return {:List, :Dict} +local Text +do + {:reverse, :upper, :lower, :find, :byte, :match, :gmatch, :gsub, :sub, :format, :rep} = string + + -- Convert an arbitrary text into a valid Lua identifier. This function is injective, + -- but not idempotent, i.e. if (x != y) then (as_lua_id(x) != as_lua_id(y)), + -- but as_lua_id(x) is not necessarily equal to as_lua_id(as_lua_id(x)) + as_lua_id = (str)-> + -- Empty strings are not valid lua identifiers, so treat them like "\3", + -- and treat "\3" as "\3\3", etc. to preserve injectivity. + str = gsub str, "^\3*$", "%1\3" + -- Escape 'x' 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", (c)-> + if c == ' ' then '_' + else format("x%02X", byte(c)) + -- Lua IDs can't start with numbers, so map "1" -> "_1", "_1" -> "__1", etc. + str = gsub str, "^_*%d", "_%1" + return str + + line_matcher = re.compile([[ + lines <- {| line (%nl line)* |} + line <- {(!%nl .)*} + ]], nl:lpeg.P("\r")^-1 * lpeg.P("\n")) + + text_methods = + reversed:=>reverse(tostring @) + uppercase:=>upper(tostring @) + lowercase:=>lower(tostring @) + as_lua_id:=>as_lua_id(tostring @) + formatted_with_1:(args)=>format(tostring(@), unpack(args)) + byte_1:(i)=>byte(tostring(@), i) + position_of_1:=>find(tostring @), + position_of_1_after_2:(i)=> find(tostring(@), i) + bytes_1_to_2: (start, stop)=> List{byte(tostring(@), start, stop)} + bytes: => List{byte(tostring(@), 1, #@)}, + capitalized: => gsub(tostring(@), '%l', upper, 1) + lines: => List(line_matcher\match(@)) + matches_1: (patt)=> match(tostring(@), patt) and true or false + [as_lua_id "* 1"]: (n)=> rep(@, n) + matching_1: (patt)=> + result = {} + stepper,x,i = gmatch(tostring(@), patt) + while true + tmp = List{stepper(x,i)} + break if #tmp == 0 + i = tmp[1] + result[#result+1] = tmp + return List(result) + [as_lua_id "with 1 -> 2"]: (patt, sub)=> gsub(tostring(@), patt, sub) + _coalesce: => + if rawlen(@) > 1 + s = table.concat(@) + for i=rawlen(@), 2, -1 do @[i] = nil + @[1] = s + return @ + + setmetatable(text_methods, {__index:string}) + + getmetatable("").__index = (i)=> + -- Use [] for accessing text characters, or s[{3,4}] for s:sub(3,4) + if type(i) == 'number' then return sub(@, i, i) + elseif type(i) == 'table' then return sub(@, i[1], i[2]) + else return text_methods[i] + + assert(("abc")\matches_1("ab")) + + [==[ + text_metatable = + __mul: (other)=> + assert(type(other) == 'number', "Invalid type for multiplication") + return rep(@, other) + __index: (i)=> + -- Use [] for accessing text characters, or s[{3,4}] for s:sub(3,4) + if type(i) == 'number' then return sub(@, i, i) + elseif type(i) == 'table' then return sub(@, i[1], i[2]) + else return text_methods[i] + __tostring: => @_coalesce![1] + __len: => #tostring(@) + __concat: (other)=> tostring(@), tostring(other) + __len: => #tostring(@) + __eq: (other)=> + type(@) == type(other) and getmetatable(@) == getmetatable(other) and tostring(@) == tostring(other) + __lt: (other)=> tostring(@) < tostring(other) + __le: (other)=> tostring(@) <= tostring(other) + __newindex: => error("Cannot modify Text") + + Text = (s)-> setmetatable(s, text_metatable) + ]==] + +return {:List, :Dict, :Text} |
