-
Notifications
You must be signed in to change notification settings - Fork 1
/
Ink.lua
190 lines (165 loc) · 6.98 KB
/
Ink.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
--[[
Author: Miqueas Martinez (miqueas2020@yahoo.com)
Date: 2020/09/10
Git Repository: https://github.com/Miqueas/Ink
License:
zlib License
Copyright (c) 2020 - 2022 Miqueas Martinez
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
]]
local Ink = {}
local Tokens = {
-- Attributes
"None", "Bold", "Dim", "Italic", "Underline", "DobleU", "Blink", "Reverse", "Hidden", "Strike",
-- Predefined colors
"Black", "Red", "Green", "Yellow", "Blue", "Magenta", "Cyan", "White"
}
local function tokExists(val) -- Check if the given value is a valid token
for i=1, #Tokens do
if val == Tokens[i] then return true end
end
return false
end
local function split(str, sep) -- Like 'split()' in JS
local sep = sep or "%s"
local t = {}
for e in str:gmatch("([^" .. sep .. "]+)") do t[#t+1] = e end
return t
end
Ink.Cache = {}
Ink.ESC = string.char(27)
Ink.Attr = { -- Text attributes
None = "0", Bold = "1",
Dim = "2", Italic = "3",
Underline = "4", Blink = "5",
Reverse = "7", Hidden = "8",
Strike = "9", DobleU = "21" -- "Doble Underline"
}
Ink.FG = { -- Predefined colors for foreground
Black = "30", Red = "31",
Green = "32", Yellow = "33",
Blue = "34", Magenta = "35",
Cyan = "36", White = "37",
FG = "38"
}
Ink.BG = { -- Predefined colors for background
Black = "40", Red = "41",
Green = "42", Yellow = "43",
Blue = "44", Magenta = "45",
Cyan = "46", White = "47",
BG = "48"
}
function Ink:compile(input)
if Ink.Cache[input] then return Ink.Cache[input] end
assert(type(input) == "string", "wrong input to 'compile()', string expected, got '" .. type(input) .. "'.")
local gi = 0 -- "group index": Used to count the number of "style" groups in the string
local tn = 0 -- "token number": Like 'gi', but for count the number of "style properties" in a group
local esc = string.char(27)
local str = input:gsub("(%#%{(.-)%})", function (match) -- Caught "style" groups in the input
gi = gi + 1
match = match:gsub("%s", "")
match = match:gsub("%#%{(.-)%}", function (props)
for _, val in pairs(split(props, ";")) do -- Split down all properties in separated values
tn = tn + 1
if val:match("%w%(.*%)") then -- Check if 'val' is a function
local func, farg = val:match("(%w+)%((.*)%)") -- Gets the function name an their arguments
local rgb = nil
if farg:find("RGB") and farg:match("RGB%((%d+,%d+,%d+)%)") then
farg = farg
:gsub("[%(%)]", "") -- Remove parentheses
:gsub("RGB", "2;") -- Replace the 'RGB' function name by the correct needed value
:gsub(",", ";") -- ...
rgb = true
elseif farg:find("RGB") and not farg:match("RGB%((%d+,%d+,%d+)%)") then
-- Error: bad call to RGB() function
error(
("Bad call to RGB() function.\n\t→ in input: %s\n\t→ in group #%s: %s\n\t→ in token #%s: %s")
:format(input, gi, match, tn, val)
)
end
if func == "FG" or func == "BG" then
if not rgb then
if tokExists(farg) then props = props:gsub(func .. "%(?" .. farg .. "%)?", self[func][farg])
elseif farg:match("^(%d+)$") and not farg:find(",") and not farg:find("%a+") then
if tonumber(farg) >= 0 and tonumber(farg) <= 255 then
props = props:gsub(func .. "%(?" .. farg .. "%)?", self[func][func] .. ";5;" .. farg)
else
-- Error: 8-bit color value out of range
error(
("8-bit color value out of range: %s.\n\t→ in input: %s\n\t→ in group #%s: %s\n\t→ in token #%s: %s")
:format(farg, input, gi, match, tn, val)
)
end
else
-- Error: unknown pre-defined color
error(
("wrong color '%s'.\n\t→ in input: %s\n\t→ in group #%s: %s\n\t→ in token #%s: %s")
:format(farg, input, gi, match, tn, val)
)
end
elseif rgb then
props = props:gsub(func, self[func][func]..";2;")
:gsub("[%(%)]+", "")
:gsub("RGB", "")
:gsub(",", ";")
else
-- Error: idk...
error(
("something's wrong...\n\t→ in input: %s\n\t→ in group #%s: %s\n\t→ in token #%s: %s")
:format(input, gi, match, tn, val)
)
end
elseif func == "RGB" then
-- Error: calling RGB() function outside FG() and BG() functions is not allowed
error(
("calling RGB() outside FG() and BG() is not allowed\n\t→ in input: %s\n\t→ in group #%s: %s\n\t→ in token #%s: %s")
:format(input, gi, match, tn, val)
)
else
-- Error: unknown function name
error(
("unknown function '%s'.\n\t→ in input: %s\n\t→ in group #%s: %s\n\t→ in token #%s: %s")
:format(func, input, gi, match, tn, val)
)
end
elseif tokExists(val) and self.Attr[val] then props = props:gsub(val, self.Attr[val])
elseif tokExists(val) and not self.Attr[val] then
-- Error: trying to use a color value outside FG() and BG() functions is not allowed
error(
("trying to use a color value outside FG() and BG() functions is not allowed.\n\t→ in input: %s\n\t→ in group #%s: %s\n\t→ in token #%s: %s")
:format(input, gi, match, tn, val)
)
else
-- Error: unknown property
error(
("unknown property '%s'.\n\t→ in input: %s\n\t→ in group #%s: %s\n\t→ in token #%s: %s")
:format(val, input, gi, match, tn, val)
)
end
end
return props
end)
match = esc.."["..match.."m"
tn = 0
return match
end)
if not Ink.DisableCache then
Ink.Cache[input] = str
end
return str
end
function Ink:print(str, ...) print(self:compile(tostring(str or "")):format(...)) end;
return setmetatable(Ink, { __call = Ink.print })