Module:Th-rm: Difference between revisions
From The Petit Planet Wiki
More actions
ReisuDesign (talk | contribs) Created page with "local p = {} local U = mw.ustring local gsub = U.gsub local find = U.find local sub = U.sub local match = U.match local len = U.len local INITIAL = { ['ก'] = 'k', ['ข'] = 'kh', ['ฃ'] = 'kh', ['ค'] = 'kh', ['ฅ'] = 'kh', ['ฆ'] = 'kh', ['ง'] = 'ng', ['จ'] = 'ch', ['ฉ'] = 'ch', ['ช'] = 'ch', ['ซ'] = 's', ['ฌ'] = 'ch', ['ญ'] = 'y', ['ฎ'] = 'd', ['ฏ'] = 't', ['ฐ'] = 'th', ['ฑ'] = 'th', ['ฒ'] = 'th', ['ณ'] = 'n', ['ด'] = 'd'..." |
ReisuDesign (talk | contribs) No edit summary |
||
| (2 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
local p = {} | local p = {} | ||
local | local arguments = require('Module:Arguments') | ||
local | local tt = require('Module:Tt') | ||
local | local AUTO_TOOLTIP = 'This romanization was generated automatically. Please add a user-curated romanization as soon as possible.' | ||
local THAI = '[ก-๛]' | |||
-- Initial consonants. | |||
local consonants = { | |||
['ก'] = 'k', | ['ก'] = 'k', | ||
['ข'] = 'kh', ['ฃ'] = 'kh', ['ค'] = 'kh', ['ฅ'] = 'kh', ['ฆ'] = 'kh', | ['ข'] = 'kh', | ||
['ฃ'] = 'kh', | |||
['ค'] = 'kh', | |||
['ฅ'] = 'kh', | |||
['ฆ'] = 'kh', | |||
['ง'] = 'ng', | ['ง'] = 'ng', | ||
['จ'] = 'ch', ['ฉ'] = 'ch', ['ช'] = 'ch', | ['จ'] = 'ch', | ||
['ฉ'] = 'ch', | |||
['ช'] = 'ch', | |||
['ซ'] = 's', | ['ซ'] = 's', | ||
['ฌ'] = 'ch', | ['ฌ'] = 'ch', | ||
| Line 18: | Line 25: | ||
['ฎ'] = 'd', | ['ฎ'] = 'd', | ||
['ฏ'] = 't', | ['ฏ'] = 't', | ||
['ฐ'] = 'th', ['ฑ'] = 'th', ['ฒ'] = 'th', | ['ฐ'] = 'th', | ||
['ฑ'] = 'th', | |||
['ฒ'] = 'th', | |||
['ณ'] = 'n', | ['ณ'] = 'n', | ||
['ด'] = 'd', | ['ด'] = 'd', | ||
['ต'] = 't', | ['ต'] = 't', | ||
['ถ'] = 'th', ['ท'] = 'th', ['ธ'] = 'th', | ['ถ'] = 'th', | ||
['ท'] = 'th', | |||
['ธ'] = 'th', | |||
['น'] = 'n', | ['น'] = 'n', | ||
['บ'] = 'b', | ['บ'] = 'b', | ||
| Line 36: | Line 47: | ||
['ล'] = 'l', | ['ล'] = 'l', | ||
['ว'] = 'w', | ['ว'] = 'w', | ||
['ศ'] = 's', ['ษ'] = 's', ['ส'] = 's', | ['ศ'] = 's', | ||
['ษ'] = 's', | |||
['ส'] = 's', | |||
['ห'] = 'h', | ['ห'] = 'h', | ||
['ฬ'] = 'l', | ['ฬ'] = 'l', | ||
['ฮ'] = 'h' | ['ฮ'] = 'h', | ||
['อ'] = '' | |||
} | } | ||
local | -- Final consonants. | ||
local finals = { | |||
['ก'] = 'k', | ['ก'] = 'k', | ||
['ข'] = 'k', ['ฃ'] = 'k', ['ค'] = 'k', ['ฅ'] = 'k', ['ฆ'] = 'k', | ['ข'] = 'k', | ||
['ฃ'] = 'k', | |||
['จ'] = 't', ['ฉ'] = 't', ['ช'] = 't | ['ค'] = 'k', | ||
['ฅ'] = 'k', | |||
['ฆ'] = 'k', | |||
['จ'] = 't', | |||
['ฉ'] = 't', | |||
['ช'] = 't', | |||
['ซ'] = 't', | ['ซ'] = 't', | ||
[' | ['ฌ'] = 't', | ||
['ฎ'] = 't', ['ฏ'] = 't', | ['ฎ'] = 't', | ||
['ฐ'] = 't', ['ฑ'] = 't', ['ฒ'] = 't | ['ฏ'] = 't', | ||
['ฐ'] = 't', | |||
['ฑ'] = 't', | |||
['ฒ'] = 't', | |||
['ด'] = 't', | ['ด'] = 't', | ||
['ต'] = 't', | ['ต'] = 't', | ||
['ถ'] = 't', ['ท'] = 't', ['ธ'] = 't', | ['ถ'] = 't', | ||
[' | ['ท'] = 't', | ||
['ธ'] = 't', | |||
['ศ'] = 't', | |||
['ษ'] = 't', | |||
['ส'] = 't', | |||
['บ'] = 'p', | ['บ'] = 'p', | ||
['ป'] = 'p', | ['ป'] = 'p', | ||
['ผ'] = '', ['ฝ'] = '', | ['ผ'] = 'p', | ||
['พ'] = 'p', ['ฟ'] = 'p', ['ภ'] = 'p', | ['ฝ'] = 'p', | ||
[' | ['พ'] = 'p', | ||
[' | ['ฟ'] = 'p', | ||
['ภ'] = 'p', | |||
['ง'] = 'ng', | |||
['ญ'] = 'n', | |||
['ณ'] = 'n', | |||
['น'] = 'n', | |||
['ร'] = 'n', | ['ร'] = 'n', | ||
['ล'] = 'n', | ['ล'] = 'n', | ||
['ฬ'] = 'n', | ['ฬ'] = 'n', | ||
[' | |||
['ม'] = 'm' | |||
} | } | ||
-- | -- Vowels. | ||
local | local vowels = { | ||
['เียะ'] = 'ia', | |||
['เีย'] = 'ia', | |||
['เือะ'] = 'uea', | |||
['เือ'] = 'uea', | |||
['เือย'] = 'ueai', | |||
['เียว'] = 'iao', | |||
['ัวะ'] = 'ua', | |||
['ัว'] = 'ua', | |||
['เิ็ย'] = 'oei', | |||
['เิย'] = 'oei', | |||
['เอย'] = 'oei', | |||
['็อย'] = 'oi', | |||
['อย'] = 'oi', | |||
['ุย'] = 'ui', | |||
['วย'] = 'uai', | |||
['ัย'] = 'ai', | |||
['ไย'] = 'ai', | |||
['าย'] = 'ai', | |||
['ใ'] = 'ai', | |||
['ไ'] = 'ai', | |||
['เ็ว'] = 'eo', | |||
['แ็ว'] = 'aeo', | |||
['เา'] = 'ao', | |||
['าว'] = 'ao', | |||
['แว'] = 'aeo', | |||
['เว'] = 'eo', | |||
['ิว'] = 'io', | |||
['ีว'] = 'io', | |||
['ำ'] = 'am', | |||
['ือ'] = 'ue', | |||
['ื'] = 'ue', | |||
['ึ'] = 'ue', | |||
['ิ'] = 'i', | |||
['ี'] = 'i', | |||
['ุ'] = 'u', | |||
['ู'] = 'u', | |||
['เ็'] = 'e', | |||
['เ'] = 'e', | |||
['แ็'] = 'ae', | |||
['แ'] = 'ae', | |||
['โะ'] = 'o', | |||
['โ'] = 'o', | |||
['เาะ'] = 'o', | |||
['อ'] = 'o', | |||
['เอะ'] = 'oe', | |||
['เอ'] = 'oe', | |||
['เะ'] = 'e', | |||
['ะ'] = 'a', | |||
['ั'] = 'a', | |||
['า'] = 'a' | |||
} | |||
-- Longest vowel forms must be checked first. | |||
local vowel_order = { | |||
'เือย', | |||
'เียว', | |||
'เือะ', | |||
'เียะ', | |||
'เีย', | |||
'เือ', | |||
'ัวะ', | |||
'ัว', | |||
'เิ็ย', | |||
'เิย', | |||
'็อย', | |||
'เ็ว', | |||
'แ็ว', | |||
'เอย', | |||
'เา', | |||
'าว', | |||
'เว', | |||
'แว', | |||
'าย', | |||
'ไย', | |||
'ัย', | |||
'ุย', | |||
'วย', | |||
'อย', | |||
'ำ', | |||
'ิว', | |||
'ีว', | |||
'ิ', | |||
'ี', | |||
'ึ', | |||
'ื', | |||
'ุ', | |||
'ู', | |||
'เ็', | |||
'แ็', | |||
'เาะ', | |||
'เอะ', | |||
'เอ', | |||
'เะ', | |||
'ะ', | |||
'ั', | |||
'า', | |||
'เ', | |||
'แ', | |||
'โ', | |||
'อ', | |||
'ใ', | |||
'ไ' | |||
} | |||
local consonant_set = {} | |||
{ '' , ' | for c in pairs(consonants) do | ||
consonant_set[c] = true | |||
end | |||
-- Thai consonant clusters. | |||
local valid_clusters = { | |||
['กร'] = true, | |||
['กล'] = true, | |||
['กว'] = true, | |||
['ขร'] = true, | |||
['ขล'] = true, | |||
['ขว'] = true, | |||
['คร'] = true, | |||
['คล'] = true, | |||
['คว'] = true, | |||
['ฆร'] = true, | |||
['ฆล'] = true, | |||
['จร'] = true, | |||
['ตร'] = true, | |||
['ปร'] = true, | |||
['ปล'] = true, | |||
['พร'] = true, | |||
['พล'] = true, | |||
['ผล'] = true, | |||
['ทร'] = true, | |||
['ธร'] = true, | |||
['ศร'] = true, | |||
['สร'] = true, | |||
['สล'] = true, | |||
['สว'] = true, | |||
['ซร'] = true, | |||
['ฝร'] = true, | |||
['ฟร'] = true | |||
} | } | ||
-- ห is silent in these clusters when followed by a vowel. | |||
local h_silent_clusters = { | |||
['หง'] = true, | |||
['หน'] = true, | |||
['หม'] = true, | |||
['หย'] = true, | |||
['หร'] = true, | |||
['หล'] = true, | |||
['หว'] = true, | |||
['หญ'] = true | |||
} | |||
-- ร is silent in these clusters when followed by a vowel. | |||
local r_silent_clusters = { | |||
['จร'] = true, | |||
['ซร'] = true, | |||
['ศร'] = true, | |||
['สร'] = true | |||
} | |||
local function chars(text) | |||
local result = {} | |||
for c in mw.text.gsplit(text, '') do | |||
table.insert(result, c) | |||
end | |||
return result | |||
end | |||
local function isConsonant(c) | local function isConsonant(c) | ||
return c and | return c and consonant_set[c] == true | ||
end | end | ||
local function isThai(c) | local function isThai(c) | ||
return c and | return c and mw.ustring.find(c, THAI) ~= nil | ||
end | end | ||
local function | local function isFrontVowel(c) | ||
return c | return c == 'เ' or c == 'แ' or c == 'โ' or c == 'ใ' or c == 'ไ' | ||
end | end | ||
local function | local function skipMarks(c, pos) | ||
while c[pos] and ( | |||
c[pos] == '็' or | |||
c[pos] == '่' or | |||
c[pos] == '้' or | |||
c[pos] == '๊' or | |||
c[pos] == '๋' or | |||
c[pos] == 'ํ' or | |||
c[pos] == '๎' | |||
) do | |||
pos = pos + 1 | |||
end | |||
return pos | |||
end | end | ||
local function | local function removeSilent(chars_) | ||
local result = {} | |||
local i = 1 | |||
return | while chars_[i] do | ||
if chars_[i] == '์' then | |||
-- ์ makes the following consonant(s) silent. | |||
i = i + 1 | |||
while chars_[i] and ( | |||
isConsonant(chars_[i]) or | |||
chars_[i] == '็' or | |||
chars_[i] == '่' or | |||
chars_[i] == '้' or | |||
chars_[i] == '๊' or | |||
chars_[i] == '๋' | |||
) do | |||
i = i + 1 | |||
end | |||
else | |||
table.insert(result, chars_[i]) | |||
i = i + 1 | |||
end | |||
end | |||
return result | |||
end | end | ||
local function | local function getVowel(c, pos) | ||
local remaining = table.concat(c, '', pos) | |||
for _, form in ipairs(vowel_order) do | |||
local length = mw.ustring.len(form) | |||
if mw.ustring.sub(remaining, 1, length) == form then | |||
return form, vowels[form] | |||
end | |||
end | |||
return nil, nil | |||
end | end | ||
local function | local function getInitial(c, pos) | ||
local | local c1 = c[pos] | ||
if not isConsonant(c1) then | |||
return '', pos | |||
end | |||
local c2 = c[pos + 1] | |||
-- ห is not pronounced in หง หน หม หย หร หล หว หญ. | |||
if c1 == 'ห' and c2 and h_silent_clusters[c1 .. c2] then | |||
return consonants[c2], pos + 2, true | |||
end | |||
-- ทร is pronounced s in the relevant Sanskrit-derived words. | |||
-- This is the broad automatic fallback; exceptions can be manually | |||
-- supplied with th_rm. | |||
if c1 == 'ท' and c2 == 'ร' then | |||
return 's', pos + 2, false | |||
end | |||
if c2 and valid_clusters[c1 .. c2] then | |||
-- ร is silent in these clusters. | |||
if r_silent_clusters[c1 .. c2] then | |||
return consonants[c1], pos + 2, false | |||
end | |||
return consonants[c1] .. consonants[c2], pos + 2, false | |||
end | |||
return consonants[c1], pos + 1, false | |||
end | |||
local function getCoda(c, pos) | |||
local current = c[pos] | |||
if not current or not isConsonant(current) then | |||
return '', pos | |||
end | |||
-- These do not act as ordinary codas here. | |||
if current == 'อ' or current == 'ห' or current == 'ฮ' then | |||
return '', pos | |||
end | |||
-- ย and ว are generally part of the vowel/diphthong. | |||
if current == 'ย' or current == 'ว' then | |||
return '', pos | |||
end | |||
if finals[current] then | |||
return finals[current], pos + 1 | |||
end | end | ||
return '', pos + 1 | |||
return ' | |||
end | end | ||
local function | local function startsNextSyllable(c, pos) | ||
if not c[pos] or not isConsonant(c[pos]) then | |||
return false | |||
end | |||
if isFrontVowel(c[pos + 1]) then | |||
return true | |||
end | |||
local _, vowel = getVowel(c, pos + 1) | |||
return vowel ~= nil | |||
end | |||
local function parseSyllable(c, start) | |||
local pos = start | |||
if not c[pos] then | |||
return nil, pos | |||
end | end | ||
local initial = '' | |||
local cluster = false | |||
-- Preposed vowels. | |||
local front = '' | |||
local | |||
if isFrontVowel(c[pos]) then | |||
front = c[pos] | |||
pos = pos + 1 | pos = pos + 1 | ||
end | end | ||
local initialPos = pos | |||
local | |||
if not isConsonant( | initial, pos, _, cluster = | ||
getInitial(c, pos) | |||
return | |||
if initial == '' and not isConsonant(c[initialPos]) then | |||
return nil, start + 1 | |||
end | end | ||
pos = pos + 1 | local vowel | ||
local vowelForm | |||
if front ~= '' then | |||
-- Handle the vowel forms which contain a preposed vowel. | |||
if front == 'เ' and c[pos] == 'ี' and c[pos + 1] == 'ย' then | |||
vowel = 'ia' | |||
vowelForm = 'เีย' | |||
pos = pos + 2 | |||
elseif front == 'เ' and c[pos] == 'ื' and c[pos + 1] == 'อ' then | |||
vowel = 'uea' | |||
vowelForm = 'เือ' | |||
pos = pos + 2 | |||
elseif front == 'เ' and c[pos] == 'อ' then | |||
vowel = 'oe' | |||
vowelForm = 'เอ' | |||
pos = pos + 1 | |||
elseif front == 'เ' and c[pos] == 'า' then | |||
vowel = 'ao' | |||
vowelForm = 'เา' | |||
pos = pos + 1 | pos = pos + 1 | ||
elseif front == 'เ' and c[pos] == '็' then | |||
vowel = 'e' | |||
vowelForm = 'เ็' | |||
pos = pos + 1 | |||
elseif front == 'แ' and c[pos] == '็' then | |||
vowel = 'ae' | |||
vowelForm = 'แ็' | |||
pos = pos + 1 | |||
elseif front == 'โ' and c[pos] == 'ะ' then | |||
vowel = 'o' | |||
vowelForm = 'โะ' | |||
pos = pos + 1 | |||
elseif front == 'ใ' or front == 'ไ' then | |||
vowel = 'ai' | |||
vowelForm = front | |||
else | |||
vowel = vowels[front] | |||
vowelForm = front | |||
end | |||
else | |||
vowelForm, vowel = getVowel(c, pos) | |||
if vowelForm then | |||
pos = pos + mw.ustring.len(vowelForm) | |||
end | end | ||
end | end | ||
-- | -- Implied vowels. | ||
if | if not vowel then | ||
and | if c[pos] and isConsonant(c[pos]) then | ||
if startsNextSyllable(c, pos) then | |||
vowel = 'a' | |||
else | |||
vowel = 'o' | |||
end | |||
else | |||
vowel = 'a' | |||
end | |||
end | end | ||
pos = skipMarks(c, pos) | |||
-- Final consonant. | |||
local coda = '' | |||
local codaPos = pos | |||
-- If the | if c[pos] and isConsonant(c[pos]) then | ||
-- If another consonant begins the next syllable, don't consume | |||
if | -- it as a coda. | ||
if startsNextSyllable(c, pos) then | |||
coda = '' | |||
else | else | ||
coda, codaPos = getCoda(c, pos) | |||
end | end | ||
end | end | ||
pos = codaPos | |||
pos = skipMarks(c, pos) | |||
return { | |||
initial = initial, | |||
vowel = vowel, | |||
coda = coda, | |||
next = pos | |||
}, pos | |||
end | |||
local function shouldHyphen(previous, current) | |||
if not previous or not current then | |||
return false | |||
end | |||
-- Before a succeeding syllable beginning with a vowel. | |||
if current.initial == '' and current.vowel ~= '' then | |||
return true | |||
end | end | ||
-- | -- Before ng when the preceding syllable ends in a vowel. | ||
if | if previous.coda == '' and current.initial == 'ng' then | ||
return true | |||
end | end | ||
return | return false | ||
end | end | ||
local function romanizeWord(word) | local function romanizeWord(word) | ||
local c = chars(word) | |||
c = removeSilent(c) | |||
local syllables = {} | |||
local pos = 1 | |||
local safety = 0 | |||
while pos <= #c and safety < 1000 do | |||
safety = safety + 1 | |||
if isConsonant(c[pos]) or isFrontVowel(c[pos]) then | |||
local syllable, nextPos = parseSyllable(c, pos) | |||
if syllable then | |||
table.insert(syllables, syllable) | |||
if nextPos <= pos then | |||
pos = pos + 1 | |||
else | |||
pos = nextPos | |||
end | |||
else | else | ||
-- Fail safely rather than looping forever. | |||
pos = pos + 1 | pos = pos + 1 | ||
end | end | ||
else | |||
-- Thai punctuation/marks that aren't part of a syllable. | |||
pos = pos + 1 | pos = pos + 1 | ||
end | end | ||
end | |||
local output = {} | |||
for i, syllable in ipairs(syllables) do | |||
if shouldHyphen(syllables[i - 1], syllable) then | |||
table.insert(output, '-') | |||
end | |||
table.insert( | |||
output, | |||
(syllable.initial or '') .. | |||
(syllable.vowel or '') .. | |||
(syllable.coda or '') | |||
) | |||
end | end | ||
| Line 424: | Line 626: | ||
end | end | ||
local function | -- Parse the text while treating <> and <-> as hard boundaries. | ||
local | -- | ||
-- <> = literal space | |||
-- <-> = literal hyphen | |||
-- | |||
-- Because each Thai section is romanized independently, automatic | |||
-- hyphenation can never cross one of these markers. | |||
local function romanizeThaiText(text) | |||
local result = {} | |||
local pos = 1 | |||
local length = mw.ustring.len(text) | |||
while pos <= length do | |||
-- Manual hyphen. | |||
if | if mw.ustring.sub(text, pos, pos + 2) == '<->' then | ||
table.insert(result, '-') | |||
pos = pos + 3 | |||
-- Manual space. | |||
elseif mw.ustring.sub(text, pos, pos + 1) == '<>' then | |||
table.insert(result, ' ') | |||
pos = pos + 2 | |||
-- Thai run. | |||
elseif isThai(mw.ustring.sub(text, pos, pos)) then | |||
local start = pos | |||
while pos <= length do | |||
local character = mw.ustring.sub(text, pos, pos) | |||
if not isThai(character) then | |||
break | |||
end | |||
-- Stop before a manual control marker. | |||
if mw.ustring.sub(text, pos, pos + 2) == '<->' then | |||
break | |||
end | |||
if mw.ustring.sub(text, pos, pos + 1) == '<>' then | |||
break | |||
end | |||
pos = pos + 1 | |||
end | end | ||
local word = mw.ustring.sub(text, start, pos - 1) | |||
table.insert(result, romanizeWord(word)) | |||
-- Everything else is preserved. | |||
else | |||
table.insert( | |||
result, | |||
mw.ustring.sub(text, pos, pos) | |||
) | |||
pos = pos + 1 | |||
end | |||
end | |||
return table.concat(result) | |||
end | |||
local function convertThaiDigits(text) | |||
local digits = { | |||
['๐'] = '0', | |||
['๑'] = '1', | |||
['๒'] = '2', | |||
['๓'] = '3', | |||
['๔'] = '4', | |||
['๕'] = '5', | |||
['๖'] = '6', | |||
['๗'] = '7', | |||
['๘'] = '8', | |||
['๙'] = '9' | |||
} | |||
for thai, latin in pairs(digits) do | |||
text = mw.ustring.gsub(text, thai, latin) | |||
end | |||
return text | |||
end | |||
local function hasThai(text) | |||
return mw.ustring.find(text, THAI) ~= nil | |||
end | |||
local function validateRomanization(text) | |||
-- Never allow Thai characters to leak into the final result. | |||
if hasThai(text) then | |||
return false | |||
end | |||
return true | |||
end | |||
local function capitalizeWords(text) | |||
return mw.ustring.gsub( | |||
text, | |||
'(%a)([%a]*)', | |||
function(first, rest) | |||
return mw.ustring.upper(first) .. rest | |||
end | end | ||
) | |||
end | |||
local function capitalizeSentence(text) | |||
local prefix, first = mw.ustring.match( | |||
text, | |||
'^([%s%p]*)(%a)' | |||
) | |||
if not first then | |||
return text | |||
end | end | ||
return | return prefix .. | ||
mw.ustring.upper(first) .. | |||
mw.ustring.sub(text, mw.ustring.len(prefix) + 2) | |||
end | |||
function p.romanize(text) | |||
if not text or mw.text.trim(text) == '' then | |||
return '' | |||
end | |||
-- Do not automatically romanize Latin-only text. | |||
if not hasThai(text) then | |||
return '' | |||
end | |||
text = convertThaiDigits(text) | |||
local result = romanizeThaiText(text) | |||
result = mw.ustring.gsub(result, '%s+', ' ') | |||
result = mw.text.trim(result) | |||
if not validateRomanization(result) then | |||
return '' | |||
end | |||
return result | |||
end | end | ||
function p.main(frame) | function p.main(frame) | ||
local args = | local args = arguments.getArgs(frame, { | ||
parentFirst = true, | parentFirst = true, | ||
wrappers = {'Template:Th-rm'} | wrappers = {'Template:Th-rm'} | ||
}) | }) | ||
local text = args[1] | |||
if not text or mw.text.trim(text) == '' then | |||
return '' | return '' | ||
end | end | ||
if not hasThai(text) then | |||
if not | |||
return '' | return '' | ||
end | end | ||
local result = p.romanize(text) | |||
if result == '' then | |||
return '' | |||
end | |||
if args.capi then | if args.capi then | ||
result = | result = capitalizeWords(result) | ||
end | end | ||
return result | if args.sent then | ||
result = capitalizeSentence(result) | |||
end | |||
-- Automatically generated romanizations get a Tt warning. | |||
return tt._main({ | |||
[1] = result, | |||
[2] = AUTO_TOOLTIP, | |||
['class'] = args.class | |||
}) | |||
end | end | ||
return p | return p | ||