(마작패 모듈 테스트(리브레에서 퍼옴)) |
(펑 -> 퐁으로 용어 변경) |
||
22번째 줄: | 22번째 줄: | ||
-- 펑, 치, 깡 -- | -- 펑, 치, 깡 -- | ||
− | pon = ' | + | pon = '퐁', chi = '치', kan = '깡', |
left = '상가', face = '대가', right = '하가', | left = '상가', face = '대가', right = '하가', | ||
2022년 9월 19일 (월) 23:55 기준 최신판
이 모듈에 대한 설명문서는 모듈:마작패/설명문서에서 만들 수 있습니다
--[[
-- token 변수는 실제 사용자로부터 입력받는 글자, 또는 화면에 출력되는 글자를
-- 정의합니다. 만약 이 모듈을 포크해서 다른 위키로 가져가는 경우 이 부분을 필
-- 요에 따라 다르게 수정하면 됩니다.
--]]
local token = {
-- mw 파일 표기 prefix--
file_header = '파일:마작-',
img_side = '-누움',
ext = '.png',
-- 수패 --
man = '만', pin = '통', sou = '삭',
-- 자패 --
E = '동', S = '남', W = '서', N = '북',
white = '백', green = '발', red = '중',
-- 꽃패 --
f1 = '춘', f2 = '하', f3 = '추', f4 = '동',
f5 = '매', f6 = '난', f7 = '국', f8 = '죽',
-- 펑, 치, 깡 --
pon = '퐁', chi = '치', kan = '깡',
left = '상가', face = '대가', right = '하가',
-- 뒷면 --
back = '뒤',
-- 특수표기 --
dora = '적',
side = '-',
double = '/',
-- 쯔모, 론, 도라, 게임 등의 옵션들 --
tsumo_ = '쯔모',
ron_ = '론',
dora_ = '도라',
wind_ = '자리',
side_ = '가',
game_ = '게임',
-- 에러메시지 --
error = {
default = "에러가 발생했습니다. 구문을 확인해주세요",
nofunct = "알 수 없는 함수가 호출되었습니다",
body = "패가 잘못되었습니다. 구문을 확인해주세요",
dir = "알 수 없는 방향입니다. 구문을 확인해주세요",
braket = "괄호가 없거나 잘못되었습니다. 구문을 확인해주세요"
},
-- 공백 --
space = ' '
}
-- 이미지 기본값 --
local image_default_size = 33
local image_real_width = 66
local image_real_height = 90
--[[ 헬퍼 함수들 ]]--
local ustring = mw.ustring
local image_ratio = function (x) return math.floor(x * image_real_height / image_real_width) end
-- switch-case 문을 위한 함수 --
-- from http://lua-users.org/wiki/SwitchStatement --
local function switch (t)
t.case = function (self, x)
local f = self[x] or self.default
if f then
if type(f) == "function" then
f(x, self)
else
error(tostring(x) .. ":" .. token.error.nofunct)
end
end
end
return t
end
-- mw용 이미지 문법을 생성해주는 함수 --
local function toImg ( data, width )
width = '|' .. width .. 'px'
local result = '[[' .. token.file_header .. data .. token.ext .. width ..'|link=|bottom]]'
return result
end
-- 에러 출력용 함수 --
local function err ( message, pos, func )
func = (func ~= nil) and '['..func..'] ' or ''
pos = (pos ~= nil) and 'p'..pos..': ' or ''
error(func..pos..message)
end
-- 펑/깡을 위한 방향 체크 --
local pon_dir = {
[token.left] = 1,
[token.face] = 2,
[token.right] = 3
}
local kan_dir = {
[token.left] = 1,
[token.face] = 2,
[token.right] = 4
}
-- forward declaration --
local pre_option
local post_option
--[[ 입력받은 값을 분석하는 메인 함수 ]]--
local function single_parse ( data, options )
if (data == nil) then err(token.error.default) end
local result = ""
-- parser variable --
local index = 0
local stack = {}
local pre = ""
local side = ""
local wrapper = false
clean_stack = function () index = 0 ; stack = {} end
clean_state = function () pre = "" ; side = "" ; wrapper = false end
local size = options.size or image_default_size
local token_parse = {
default = function (x) return true end
}
-- 특수표기 --
token_parse[token.dora] = function (x) pre = x end
token_parse[token.side] = function (x) side = token.img_side end
token_parse[token.double] = function (x) wrapper = true ; side = token.img_side end
-- 숫자 --
for i = 1, 9 do
token_parse[tostring(i)] = function (x)
index = index + 1
stack[index] = {}
-- 필요한 값들을 스택에 넣는다
stack[index].main = x
stack[index].pre = pre
stack[index].side = side
stack[index].wrapper = wrapper
clean_state()
end
end
-- 수패 --
local function numbers (x)
for i = 1, index do
local width = (stack[i].side ~= '') and image_ratio(size) or size
local caption = (stack[i].pre or '') .. stack[i].main .. x .. (stack[i].side or '')
if (stack[i].wrapper) then -- 이중 처리
result = result .. '<div style="display: inline-block;">'
.. '<div>' .. toImg( caption, width ) .. '</div>'
.. toImg( caption, width )
.. '</div>'
else
result = result .. toImg( caption, width )
end
end
clean_stack() ; clean_state()
end
token_parse[token.man], token_parse[token.pin], token_parse[token.sou] = numbers, numbers, numbers
-- 자패, 뒷면 --
local function strings (x)
local width = (side ~= '') and image_ratio(size) or size
if (wrapper) then -- 이중 처리
result = result .. '<div style="display: inline-block;">'
.. '<div>' .. toImg( x..side, width ) .. '</div>'
.. toImg( x..side, width )
.. '</div>'
else
result = result .. toImg( x..side, width )
end
clean_stack() ; clean_state()
end
token_parse[token.E] = strings
token_parse[token.S] = strings
token_parse[token.W] = strings
token_parse[token.N] = strings
token_parse[token.white] = strings
token_parse[token.green] = strings
token_parse[token.red] = strings
token_parse[token.back] = strings
-- 꽃패 --
token_parse[token.f1] = strings
token_parse[token.f2] = strings
token_parse[token.f3] = strings
token_parse[token.f4] = strings
token_parse[token.f5] = strings
token_parse[token.f6] = strings
token_parse[token.f7] = strings
token_parse[token.f8] = strings
-- switch 문 구성 --
token_parse = switch(token_parse)
-- utf8 문자별로 루프 진행 --
local pos = 0
while #data > 0 do
local c = ustring.match(data, ".")
data = data:sub(#c+1) ; pos = pos + 1
token_parse:case(c) -- 위 select-case 문에 따라 처리
end
return '<div class="majong" style="display:inline-block;">'..result..'</div>'
end
local function parse ( frame ) -- '파싱' 함수 entry point
return pre_option(frame.args) .. single_parse( frame.args[1]:match("^%s*(.-)%s*$"), frame.args ) .. post_option(frame.args) -- trim
end
local function single_print ( data, options )
if (data == nil) then err(token.error.default) end
local pos = 1
local op = ustring.match(data, ".") or ''
pos = pos + #op
local caption = ''
-- 치 --
if (op == token.chi) then
data = data:sub(#op+1)
local body = ustring.match(data, "%s*(%d?.)") or ''
if (#body == 0) then err(token.error.body, pos, token.chi) end
pos = pos + #body
local tail = ustring.match(data, ".*%((.-)%)") or '' -- 괄호 부분 체크
if (#tail == 0) then err(token.error.braket, pos, token.chi) end
pos = pos + #tail
caption = token.side .. body .. tail -- 치는 반드시 상가에게만 받게됨
-- 펑 --
elseif (op == token.pon) then
data = data:sub(#op+1)
local body = ustring.match(data, "%s*(/?%d?.)") or ''
if (#body == 0) then err(token.error.body, pos, token.pon) end
pos = pos + #body
local tail = ustring.match(data, ".*%((.-)%)") or '' -- 괄호 부분 체크
if (#tail == 0) then err(token.error.braket, pos, token.pon) end
pos = pos + #tail
-- 가깡 처리 --
local selector = token.side
if (body:sub(1, 1) == '/') then
body = body:sub(2)
selector = token.double
end
-- 방향에 맞게 캡션 처리 --
local dir = pon_dir[tail] or -1
if (dir == -1) then err(token.error.dir, pos, token.pon) end
for i = 1, 3 do
if (i == dir) then caption = caption .. selector end
caption = caption .. body
end
-- 깡 --
elseif (op == token.kan) then
data = data:sub(#op+1)
local body = ustring.match(data, "%s*(/?%d?.)") or ''
if (#body == 0) then err(token.error.body, pos, token.kan) end
pos = pos + #body
local tail = ustring.match(data, "%s*%((.-)%)") or '' -- 괄호 부분 체크
pos = pos + #tail
-- 안깡 --
if (#tail == 0) then
caption = token.back .. body .. body .. token.back
-- 가깡 --
elseif (body:sub(1, 1) == '/') then
local dir = pon_dir[tail] or -1
if (dir == -1) then err(token.error.dir, pos, token.kan) end
body = body:sub(2)
for i = 1, 3 do
if (i == dir) then caption = caption .. token.double end
caption = caption .. body
end
-- 대명깡 --
else
local dir = kan_dir[tail] or -1
if (dir == -1) then err(token.error.dir, pos, token.kan) end
for i = 1, 4 do
if (i == dir) then caption = caption .. token.side end
caption = caption .. body
end
end
-- 기타 --
else
caption = data
end
return single_parse(caption, options)
end
local function print ( frame ) -- '출력' 함수 entry point
local result = ''
local data = {}
for k, line in pairs(frame.args) do -- 옵션값을 제외한 나머지 입력값들을 data에 모음
if (type(k) == "number") then data[k] = line end
end
for i = 1, #data do
result = result .. single_print( data[i]:match("^%s*(.-)%s*$"), frame.args ) -- trim
if (i ~= #data) then result = result .. token.space end
end
return pre_option(frame.args) .. result .. post_option(frame.args)
end
--[[ 옵션 출력을 위한 보조 함수 ]]--
pre_option = function (options)
local result = ''
-- 게임 옵션 출력 --
if (options[token.game_] ~= nil) then
result = options[token.game_]
end
-- 자리 출력 --
if (options[token.wind_] ~= nil) then
if (result ~= '') then result = result .. token.space end
result = result .. options[token.wind_] .. token.side_
end
if (result ~= '') then result = '(' .. result .. ')' .. token.space end
return '<div class="majong-group" style="white-space:nowrap;overflow-x:auto;">' .. result
end
post_option = function (options)
local result = ''
-- 화료패 출력 --
if (options[token.tsumo_] ~= nil) then
result = token.space .. token.tsumo_ .. single_parse(options[token.tsumo_], options)
elseif (options[token.ron_] ~= nil) then
result = token.space .. token.ron_ .. single_parse(options[token.ron_], options)
end
-- 도라패 출력 --
if (options[token.dora_] ~= nil) then
if (result ~= '') then result = result .. token.space end
result = result .. token.dora_ .. single_parse(options[token.dora_], options)
end
if (result ~= '') then result = token.space .. result end
return result .. '</div>'
end
return {
['파싱'] = parse,
['출력'] = print,
-- 자주 쓰이는 함수들 --
['만수패'] = function (f) return single_parse( "123456789만", f.args ) end,
['통수패'] = function (f) return single_parse( "123456789통", f.args ) end,
['삭수패'] = function (f) return single_parse( "123456789삭", f.args ) end,
['자패'] = function (f) return single_parse( "동남서북백발중", f.args ) end,
['바람패'] = function (f) return single_parse( "동남서북", f.args ) end,
-- ['꽃패'] = function (f) return single_parse( "춘하추동매난국죽", f.args ) end,
['삼원패'] = function (f) return single_parse( "백발중", f.args ) end
}