pull/76/head
vvollo 11 months ago committed by GitHub
parent bd274293aa
commit 4bb6186dc9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 182
      parser/mp-en.lua
  2. 311
      parser/mp-ru.lua
  3. 262
      parser/mp.lua
  4. 267
      parser/mplib.lua

@ -70,7 +70,8 @@ mp.shorten = {
["se"] = "southeast";
["sw"] = "southwest";
["nw"] = "northwest";
["u"] = "up";
["d"] = "down";
}
mp.shorten_expert = {
@ -87,8 +88,13 @@ function mp:skip_filter(w)
end
return true
end
_'@compass'.before_Default = 'Try to verb "go".'
function mp:ignore_filter(w)
if w == 'the' or w == 'a' or w == 'an' then
return true
end
return false
end
_'@compass'.before_Default = function() p('"{#First}" is the direction. You can not ', mp.parsed[1], ' {#firstit}.') end
function mp.msg.SCORE(d)
if d > 0 then
@ -98,19 +104,16 @@ function mp.msg.SCORE(d)
end
end
function mp.msg.MULTIDSC(oo, inv)
if #oo > 0 then
local s = oo[1]
if not s:hint'proper' and not s:hint'surname' then
p "the"
end
mp.door.word = "door"
mp.msg.TITLE_SCORE = function()
if mp.maxscore then
pr ("Score: ", mp.score, "/", mp.maxscore)
end
mp:multidsc(oo, inv)
pr ("Score: ", mp.score)
end
mp.msg.TITLE_TURNS = function()
pr ("Turns: ", game:time() - 1)
end
mp.door.word = "door"
mp.msg.TITLE_SCORE = "Score: "
mp.msg.TITLE_TURNS = "Turns: "
mp.msg.YES = "Yes"
mp.msg.WHEN_DARK = "Darkness."
mp.msg.UNKNOWN_THEDARK = "Probably, it is because there is no light?"
@ -118,6 +121,9 @@ mp.msg.COMPASS_NOWAY = "{#Me} can't go that way."
mp.msg.COMPASS_EXAM_NO = "Nothing interesting in that direction."
mp.msg.ENUM = "items."
mp.msg.CUTSCENE_HELP = "Press <Enter> or enter {$fmt em|next} to continue."
if instead.reinstead then
mp.msg.CUTSCENE_MORE = "^{$fmt em|(more)}"
end
mp.msg.DLG_HELP = "Enter number to select the phrase."
mp.msg.NO_ALL = "This verb can not be used with all."
mp.msg.DROPPING_ALL = function(w)
@ -127,14 +133,14 @@ mp.msg.TAKING_ALL = function(w)
pn (iface:em("(taking "..w:the_noun()..")"))
end
mp.msg.TAKE_BEFORE = function(w)
pn (iface:em("(taking "..w:the_noun().." before)"))
pn (iface:em("(taking "..w:the_noun().." first)"))
end
mp.msg.DISROBE_BEFORE = function(w)
pn (iface:em("(disrobing "..w:the_noun().." before)"))
pn (iface:em("(disrobing "..w:the_noun().." first)"))
end
mp.msg.CLOSE_BEFORE = function(w)
pn (iface:em("(closing "..w:the_noun() .. " before)"))
pn (iface:em("(closing "..w:the_noun() .. " first)"))
end
local function str_split(str, delim)
@ -340,10 +346,23 @@ end
mp.msg.enter = "<Enter>"
mp.msg.EMPTY = 'Excuse me?'
mp.msg.UNKNOWN_VERB = "Unknown verb"
mp.msg.UNKNOWN_VERB_HINT = "Maybe you meant"
mp.msg.UNKNOWN_VERB = function(w)
p ("Unknown verb ", iface:em(w), ".")
end
mp.msg.UNKNOWN_VERB_HINT = function(w)
p ("The most similar word is ", iface:em(w), ".")
end
mp.msg.INCOMPLETE = "The sentence must be supplemented."
mp.msg.INCOMPLETE_NOUN = "What do you want to apply the command to"
mp.msg.INCOMPLETE_NOUN = function(w)
if w then
p ('What do you want to apply the command "'..w..'" to?')
else
p "What do you want to apply the command to?"
end
end
mp.msg.INCOMPLETE_SECOND_NOUN = function(w)
p ('Clarify the command: "', w, '"?')
end
mp.msg.UNKNOWN_OBJ = "Here is no such thing"
mp.msg.UNKNOWN_OBJ = function(w)
if not w then
@ -363,9 +382,8 @@ mp.msg.UNKNOWN_WORD = function(w)
end
mp.msg.NOTHING_OBJ = "Nothing."
mp.msg.HINT_WORDS = "Maybe you meant"
mp.msg.HINT_OR = "or"
mp.msg.HINT_AND = "and"
mp.msg.AND = "and"
mp.msg.OR = "or"
mp.msg.MULTIPLE = "Here is"
mp.msg.LIVE_ACTION = function(w)
p (mp:It(w), " would not like it.")
@ -374,11 +392,17 @@ mp.msg.NO_LIVE_ACTION = "{#Me} can only do that to something animate."
mp.msg.NOTINV = function(t)
p (lang.cap(t:the_noun()) .. " must be taken first.")
end
mp.msg.WORN = function(_)
pr (" (worn)")
mp.msg.HAS_WORN = function(_)
return "worn"
end
mp.msg.OPEN = function(_)
pr (" (opened)")
mp.msg.HAS_OPEN = function(_)
return "opened"
end
mp.msg.HAS_ON = function(_)
return "switched on"
end
mp.msg.HAS_LIGHT = function(_)
return "providing light"
end
mp.msg.EXITBEFORE = "May be, {#me} should to {#if_has/#where,supporter,get off,get out of} {#thenoun/#where}."
@ -403,12 +427,34 @@ mp.msg.NOROOM = function(w)
end
mp.msg.Exam.SWITCHSTATE = "{#Thefirst} {#is/#first} switched {#if_has/#first,on,on,off}."
mp.msg.Exam.NOTHING = "nothing."
mp.msg.Exam.IS = "there is"
mp.msg.Exam.ARE = "there are"
mp.msg.Exam.IN = "In {#thefirst}"
mp.msg.Exam.ON = "On {#thefirst}"
mp.msg.Exam.NOTHING = function(w)
p "There is nothing "
if w:has 'supporter' then
mp:pnoun (w, "on {#thefirst}.")
else
mp:pnoun (w, "in {#thefirst}.")
end
end
mp.msg.Exam.CONTENT = function(w, oo)
local single = not oo[1]:hint 'plural'
if std.me():where() == w or std.here() == w then
p "{#Me} can see"
mp:multidsc(oo)
p " here."
return
end
if single then
p "There is"
else
p "There are"
end
mp:multidsc(oo)
if w:has 'supporter' then
mp:pnoun (w, " on {#thefirst}.")
else
mp:pnoun (w, " in {#thefirst}.")
end
end
mp.msg.Exam.DEFAULT = "{#Me} {#does/#me} not see anything unusual in {#thefirst}.";
mp.msg.Exam.SELF = "{#Me} {#does/#me} not see anything unusual in {#yourself/#me}.";
@ -429,7 +475,8 @@ mp.msg.Walk.WALK = "But {#thefirst} {#is/#first} already here."
mp.msg.Walk.NOWHERE = "Where?"
mp.msg.Walk.INV = "{#Me} {#is/#me} holding this."
mp.msg.Enter.EXITBEFORE = "{#Me} {#present/#me,need} to {#if_has/#where,supporter,get off from,leave} {#thenoun/#where} first."
mp.msg.Enter.EXITBEFORE = "{#Me} {#present/#me,need} to "..
"{#if_has/#where,supporter,get off from,leave} {#thenoun/#where} first."
mp.msg.Exit.NOTHERE = "But {#me} {#is/#me} not {#if_has/#first,supporter,on,in} {#thefirst}."
mp.msg.Exit.NOWHERE = "But {#me} {#have/#me} no way to exit."
@ -619,7 +666,8 @@ mp.msg.Answer.EMPTY = "{#Me} can't find anything to answer."
mp.msg.Answer.SELF = "Good answer."
mp.msg.Yes.YES = "That was a rhetorical question."
mp.msg.Buy.USE = "How exactly?"
mp.msg.Buy.BUY = "Nothing is on sale."
mp.msg.Use.USE = "How exactly?"
mp.keyboard_space = '<space>'
mp.keyboard_backspace = '<backspace>'
@ -628,21 +676,21 @@ mp.msg.GAMEOVER_HELP = [[Use restart to restart game.]];
function mp:myself(ob)
if ob:hint'first' then
return { "myself", "me" }
return { "myself", "self", "me" }
end
if ob:hint'second' then
return { "yourself", "me", "myself" }
return { "yourself", "myself", "self", "me" }
end
if ob:hint'plural' then
return { "themselves", "our" }
return { "themselves", "ourselves", "self" }
end
if ob:hint'female' then
return { "herself", "me" }
return { "herself", "myself", "self", "me" }
end
if ob:hint'male' then
return { "himself", "me" }
return { "himself", "myself", "self", "me" }
end
return { "itself" }
return { "itself", "myself", "self", "me" }
end
function mp:it(w)
@ -695,7 +743,8 @@ function mp:before_Enter(w)
return false
end
mp.msg.HELP = [[{$fmt b|INSTRUCTIONS}^^
mp.msg.HELP = function()
p [[{$fmt b|INSTRUCTIONS}^^
Enter your actions in verb noun form. For example:^
> open door^
@ -709,13 +758,27 @@ To examine whole scene, enter "exam" or press "Enter".^
^
To exam your inventory, enter "inv".^
^
Use compass directions to walk. For example: "go north" or "north" or just "n".
^^
You may use the "TAB" key for autocompletion.
]]
Use compass directions to walk. For example: "go north" or "north" or just "n". There are also up and down directions, outside and inside.]]
if not instead.tiny then
p [[^^You may use the "TAB" key for autocompletion.]]
else
p [[^^Use "save" and "load" to save and load game.]]
if instead.tiny then
p [[For ex. "save 1".]]
end
p [[Restart game: "restart".]]
if instead.reinstead then
p [[^^Also available: !restart, !quit, !info, !save, !load and !font <size>.]]
end
end
end
function mp.token.compass1(_)
return "{noun_obj}/@n_to,compass|{noun_obj}/@ne_to,compass|{noun_obj}/@e_to,compass|{noun_obj}/@se_to,compass|{noun_obj}/@s_to,compass|{noun_obj}/@sw_to,compass|{noun_obj}/@w_to,compass|{noun_obj}/@nw_to,compass"
return "{noun_obj}/@n_to,compass|{noun_obj}/@ne_to,compass|"..
"{noun_obj}/@e_to,compass|{noun_obj}/@se_to,compass|"..
"{noun_obj}/@s_to,compass|{noun_obj}/@sw_to,compass|"..
"{noun_obj}/@w_to,compass|{noun_obj}/@nw_to,compass"
end
function mp.token.compass2(_)
@ -724,12 +787,18 @@ end
std.mod_init(function(_)
Verb { "#Walk",
"go,walk,run,enter",
"go,walk,run,enter,come",
"{compass1} : Walk",
"in|into|inside|on {noun}/scene,enterable : Enter",
"{noun}/scene : Walk",
"{compass2}: Walk",
"outside|out|away: Exit" }
"outside|out|away: Exit"
}
Verb { "#Enter",
"enter",
"{noun}/scene,enterable : Enter"
}
Verb { "#Sit",
"sit,stand",
@ -740,7 +809,7 @@ Verb { "#Lie",
"down in|into|inside|on {noun}/scene,enterable : Enter" }
Verb { "#Exit",
"exit,out",
"exit,out,leave",
"?from {noun}/scene : Exit",
": Exit"}
@ -751,6 +820,7 @@ Verb { "#Exam",
"inventory : Inv",
"~ under {noun} : LookUnder",
"~ in|inside|into|through|on {noun} : Search",
"~ ?at {noun} : Exam",
"~ up * in {noun} : Consult reverse",
}
@ -853,6 +923,7 @@ Verb {
Verb {
"#SwitchOff",
"switch",
"off {noun}: SwitchOff",
"~ {noun} off : SwitchOff",
}
@ -979,7 +1050,7 @@ Verb {
Verb {
"#Listen",
"listen.hear",
"listen,hear",
"Listen",
"?to {noun}: Listen",
}
@ -1090,8 +1161,7 @@ Verb {
Verb {
"#Talk",
"talk",
"with {noun}/live : Talk"
"with|to {noun}/live : Talk"
}
Verb {
@ -1180,6 +1250,8 @@ MetaVerb {
"~parser",
"expert on : MetaExpertOn",
"expert off : MetaExpertOff",
"verbs : MetaVerbs",
"version : MetaVersion",
}
MetaVerb {
@ -1219,6 +1291,12 @@ std.mod_start(function()
"MetaUndo",
}
end
if mp.score then
MetaVerb {
"~ счёт",
"MetaScore",
}
end
end)
-- Dialog
std.phr.default_Event = "Exam"

@ -67,7 +67,40 @@ function mp:skip_filter(w)
return true
end
_'@compass'.before_Default = 'Попробуйте глагол "идти".'
local function endswith(w, t)
return not not w:find(t..'$')
end
function mp:verb_filter(w)
if #w > 1 then
return true
end
local utf = mp.utf
local verb = w[1]
local t = utf.chars(w[1])
if endswith(verb, 'ся') or endswith(verb, 'сь') or endswith(verb, 'те') then
local len = #verb
len = len - utf.bb(verb, len)
len = len - utf.bb(verb, len)
verb = verb:sub(1, len)
end
if endswith(verb, 'и') or endswith(verb, 'ь') then
return true
end
local t = utf.chars(verb)
local a = { ['а'] = true, ['е'] = true, ['и'] = true,
['о'] = true, ['у'] = true, ['ы'] = true,
['ю'] = true, ['я'] = true };
local len = #t
if len >= 2 and a[t[len - 1]] and t[len] == 'й' then -- or a[t[len]] then
return true
end
return false
end
_'@compass'.before_Default = function()
p('"{#First}" это направление. {#Firstit/вн} нельзя ', mp.parsed[1], ".")
end
function mp.msg.SCORE(d)
if d > 0 then
@ -77,8 +110,16 @@ function mp.msg.SCORE(d)
end
end
mp.door.word = -"дверь";
mp.msg.TITLE_SCORE = "Счёт: "
mp.msg.TITLE_TURNS = "Ходы: "
mp.msg.TITLE_SCORE = function()
if mp.maxscore then
pr ("Счёт: ", mp.score, "/", mp.maxscore)
else
pr ("Счёт: ", mp.score)
end
end
mp.msg.TITLE_TURNS = function()
pr ("Ходы: ", game:time() - 1)
end
mp.msg.YES = "Да"
mp.msg.WHEN_DARK = "Кромешная тьма."
mp.msg.UNKNOWN_THEDARK = "Возможно, это потому что в темноте ничего не видно?"
@ -86,6 +127,9 @@ mp.msg.COMPASS_NOWAY = "Этот путь недоступен."
mp.msg.COMPASS_EXAM_NO = "В этом направлении не видно ничего примечательного."
mp.msg.ENUM = "шт."
mp.msg.CUTSCENE_HELP = "Для продолжения нажмите <ввод> или введите {$fmt em|дальше}."
if instead.reinstead then
mp.msg.CUTSCENE_MORE = "^{$fmt em|(дальше)}"
end
mp.msg.DLG_HELP = "Для выбора фразы введите цифру."
mp.msg.NO_ALL = "Это действие нельзя применить на всё."
mp.msg.DROPPING_ALL = function(w)
@ -130,11 +174,25 @@ end
mp.msg.enter = "<ввод>"
mp.msg.EMPTY = 'Простите?'
mp.msg.UNKNOWN_VERB = "Непонятный глагол"
mp.msg.UNKNOWN_VERB_HINT = "Возможно, вы имели в виду"
mp.msg.UNKNOWN_VERB = function(w)
p ("Непонятный глагол ", iface:em(w), ".")
end
mp.msg.UNKNOWN_VERB_HINT = function(w)
p ("Самое похожее слово: ", iface:em(w), ".")
end
mp.msg.INCOMPLETE = "Нужно дополнить предложение."
mp.msg.INCOMPLETE_NOUN = "К чему вы хотите применить команду"
mp.msg.INCOMPLETE_SECOND_NOUN = "Уточните команду:"
mp.msg.INCOMPLETE_NOUN = function(w)
if w then
p('К чему вы хотите применить команду "',w, '"?')
else
p"К чему вы хотите применить команду?"
end
end
mp.msg.INCOMPLETE_SECOND_NOUN = function(w)
p ('Уточните команду: "',w,'"?')
end
mp.msg.UNKNOWN_OBJ = function(w)
if not w then
p "Об этом предмете ничего не известно."
@ -152,10 +210,9 @@ mp.msg.UNKNOWN_WORD = function(w)
p ("(",w,"?).")
end
end
mp.msg.HINT_WORDS = "Может быть"
mp.msg.HINT_OR = "или"
mp.msg.HINT_AND = "и"
mp.msg.HINT_WORDS = "Возможно"
mp.msg.AND = "и"
mp.msg.OR = "или"
mp.msg.MULTIPLE = "Тут есть"
mp.msg.LIVE_ACTION = function(w)
p (w:It'дт'," это не понравится.")
@ -166,16 +223,28 @@ mp.msg.NOTINV = function(t)
p (lang.cap(t:noun'вн') .. " сначала нужно взять.")
end
--"надет"
mp.msg.WORN = function(w)
mp.msg.HAS_WORN = function(w)
local hint = w:gram().hint
pr (" (",mp.mrd:word('надет/' .. hint), ")")
return mp.mrd:word('надет/' .. hint)
end
--"открыт"
mp.msg.OPEN = function(w)
mp.msg.HAS_OPEN = function(w)
local hint = w:gram().hint
pr (" (",mp.mrd:word('открыт/' .. hint), ")")
return mp.mrd:word('открыт/' .. hint)
end
mp.msg.EXITBEFORE = "Возможно, {#me/дт} нужно сначала {#if_has/#where,supporter,слезть с {#where/рд}.,покинуть {#where/вн}.}"
--"включён"
mp.msg.HAS_ON = function(w)
local hint = w:gram().hint
return mp.mrd:word('включён/' .. hint)
end
--"светится"
mp.msg.HAS_LIGHT = function(w)
local hint = w:gram().hint
return mp.mrd:word('светится/' .. hint)
end
mp.msg.EXITBEFORE = "Возможно, {#me/дт} нужно сначала "..
"{#if_has/#where,supporter,слезть с {#where/рд}.,покинуть {#where/вн}.}"
mp.default_Event = "Exam"
mp.default_Verb = "осмотреть"
@ -186,7 +255,6 @@ mp.msg.ACCESS2 = "{#Second} отсюда не{#word/доступен,#second}."
mp.msg.Look.HEREIS = "Здесь находится"
mp.msg.Look.HEREARE = "Здесь находятся"
mp.msg.NOROOM = function(w)
if w == std.me() then
p ("У {#me/рд} слишком много вещей.")
@ -199,11 +267,47 @@ end
--"включён"
--"выключен"
mp.msg.Exam.SWITCHSTATE = "{#First} сейчас {#if_has/#first,on,{#word/включён,#first},{#word/выключен,#first}}."
mp.msg.Exam.NOTHING = "ничего нет."
mp.msg.Exam.IS = "находится"
mp.msg.Exam.ARE = "находятся"
mp.msg.Exam.IN = "В {#first/пр,2}"
mp.msg.Exam.ON = "На {#first/пр,2}"
mp.msg.Exam.NOTHING = function(w)
if w:has 'supporter' then
mp:pnoun (w, "На {#first/пр,2}")
else
mp:pnoun (w, "В {#first/пр,2}")
end
p "ничего нет."
end
mp.msg.Exam.CONTENT = function(w, oo)
local single = #oo == 1 and not oo[1]:hint 'plural'
if std.me():where() == w or std.here() == w then
if false then
if single then
p "Здесь находится"
else
p "Здесь находятся"
end
mp:multidsc(oo)
else
p "{#Me} {#word/видеть,#me,нст} здесь";
mp:multidsc(oo, 'вн')
end
p "."
return
end
if w:has 'supporter' then
mp:pnoun (w, "На {#first/пр,2}")
else
mp:pnoun (w, "В {#first/пр,2}")
end
if single then
p "находится"
else
p "находятся"
end
mp:multidsc(oo)
p "."
end
--"видеть"
mp.msg.Exam.DEFAULT = "{#Me} не {#word/видеть,#me,нст} {#vo/{#first/пр}} ничего необычного.";
mp.msg.Exam.SELF = "{#Me} не {#word/видеть,#me,нст} в себе ничего необычного.";
@ -240,7 +344,8 @@ mp.msg.Exit.CLOSED = "Но {#first} {#word/закрыт,#first}."
--"покидать"
--"слезать"
mp.msg.Exit.EXITED = "{#Me} {#if_has/#first,supporter,{#word/слезать с,#me,нст} {#first/рд},{#word/покидать,#me,нст} {#first/вн}}."
mp.msg.Exit.EXITED = "{#Me} {#if_has/#first,supporter,{#word/слезать,#me,нст} {#so/{#first/рд}},"..
"{#word/покидать,#me,нст} {#first/вн}}."
mp.msg.GetOff.NOWHERE = "Но {#me/дт} не с чего слезать."
@ -295,7 +400,10 @@ mp.msg.Take.WORN = "{#First} {#word/надет,#first} на {#firstwhere/вн}."
mp.msg.Take.PARTOF = "{#First} {#if_hint/#first,plural,являются,является} частью {#firstwhere/рд}."
mp.msg.Remove.WHERE = "{#First} не {#word/находиться,#first,нст} {#if_has/#second,supporter,на,в} {#second/пр,2}."
mp.msg.Remove.REMOVE = "{#First} {#if_has/#second,supporter,{#word/поднят с,#first},{#word/извлечён из,#first}} {#second/рд}."
--"поднят"
--"извлечён"
mp.msg.Remove.REMOVE = "{#First} {#if_has/#second,supporter,{#word/поднят с,#first},"..
"{#word/извлечён из,#first}} {#second/рд}."
mp.msg.Drop.SELF = "У {#me/рд} не хватит ловкости."
mp.msg.Drop.WORN = "{#First/вн} сначала нужно снять."
@ -480,13 +588,13 @@ end
function mp:myself(_, hint)
local ww = dict({
["вн"] = "себя";
["дт"] = "себе";
["тв"] = "собой";
["пр"] = "себе";
["рд"] = "себя";
["вн"] = { "себя" };
["дт"] = { "себе" };
["тв"] = {"собой" };
["пр"] = { "себе" };
["рд"] = { "себя" };
}, hint)
return { ww }
return ww
end
function mp:it(w, hint)
@ -560,27 +668,65 @@ function mp:err_noun(noun)
end
function mp.shortcut.vo(hint)
local w = std.split(mp.mrd.lang.norm(hint))
local utf = mp.utf
local vow = lang.is_vowel
local char = utf.char
local excl = {
["льве"] = true,
["львах"] = true,
["льду"] = true,
["льдах"] = true,
["льне"] = true,
["льнах"] = true,
["лбу"] = true,
["лбах"] = true,
["лжи"] = true,
["лжах"] = true,
["мху"] = true,
["мхах"] = true,
["рву"] = true,
["рвах"] = true,
["ржи"] = true,
["ржах"] = true,
["рту"] = true,
["ртах"] = true,
["мне"] = true,
["что"] = true,
}
w = w[#w]
if mp.utf.len(w) > 2 and
(vow(char(w, 1) == 'в' or vow(char(w, 1) == 'ф') and
not vow(char(w, 2)))) or excl[w] then
return "во ".. hint
end
return "в ".. hint
-- local w = std.split(hint)
-- w = w[#w]
-- if mp.utf.len(w) > 2 and
-- (lang.is_vowel(utf.char(w, 1)) or
-- lang.is_vowel(utf.char(w, 2))) then
-- return "в ".. hint
-- end
-- return "во ".. hint
end
function mp.shortcut.so(hint)
local so = {
["с"] = true,
["з"] = true,
["ш"] = true,
["ж"] = true,
["л"] = true,
["р"] = true,
["м"] = true,
}
local w = std.split(mp.mrd.lang.norm(hint))
local utf = mp.utf
w = w[#w]
if utf.len(w) > 2 and
((so[utf.char(w, 1)] and
not lang.is_vowel(utf.char(w, 2))) or utf.char(w, 1) == 'щ') then
return "со ".. hint
end
if utf.len(w) > 2 and utf.char(w, 1) == 'л' and utf.char(w, 2) == 'ь' and
not lang.is_vowel(utf.char(w, 2)) then
return "со ".. hint
end
return "с ".. hint
-- local w = std.split(hint)
-- w = w[#w]
-- if mp.utf.len(w) > 2 and
-- (lang.is_vowel(utf.char(w, 1)) or
-- lang.is_vowel(utf.char(w, 2))) then
-- return "с ".. hint
-- end
-- return "со ".. hint
end
function mp:before_Enter(w)
@ -591,7 +737,8 @@ function mp:before_Enter(w)
return false
end
mp.msg.HELP = [[{$fmt b|КАК ИГРАТЬ?}^^
mp.msg.HELP = function()
p [[{$fmt b|КАК ИГРАТЬ?}^^
Вводите ваши действия в виде простых предложений вида: глагол -- существительное. Например:^
> открыть дверь^
@ -607,14 +754,27 @@ mp.msg.HELP = [[{$fmt b|КАК ИГРАТЬ?}^^
^
Чтобы узнать какие предметы у вас с собой, наберите "инвентарь" или "инв".^
^
Для перемещений используйте стороны света, например: "идти на север" или "север" или просто "с".^
Кроме сторон света можно перемещаться вверх ("вверх" или "вв") и вниз ("вниз" или "вн").
^^
Вы можете воспользоваться клавишей "TAB" для автодополнения ввода.
]]
Для перемещений используйте стороны света, например: "идти на север" или "север" или просто "с". Кроме сторон света можно перемещаться вверх ("вверх" или "вв") и вниз ("вниз" или "вн"), "внутрь" и "наружу".]]
if not instead.tiny then
p [[^^Вы можете воспользоваться клавишей "TAB" для автодополнения ввода.]]
else
p [[^^Вы можете сокращать названия объектов.]]
p [[^^Чтобы сохранять и загружать игру используйте "сохранить" и "загрузить".]]
if instead.tiny then
p [[Например, "сохранить 1".]]
end
p [[Начать заново: "заново".]]
if instead.reinstead then
p [[^^Также доступны команды: !restart, !quit, !info, !save, !load и !font <размер>.]]
end
end
end
function mp.token.compass1(_)
return "{noun_obj}/@n_to,compass|{noun_obj}/@ne_to,compass|{noun_obj}/@e_to,compass|{noun_obj}/@se_to,compass|{noun_obj}/@s_to,compass|{noun_obj}/@sw_to,compass|{noun_obj}/@w_to,compass|{noun_obj}/@nw_to,compass"
return "{noun_obj}/@n_to,compass|{noun_obj}/@ne_to,compass|"..
"{noun_obj}/@e_to,compass|{noun_obj}/@se_to,compass|"..
"{noun_obj}/@s_to,compass|{noun_obj}/@sw_to,compass|"..
"{noun_obj}/@w_to,compass|{noun_obj}/@nw_to,compass"
end
function mp.token.compass2(_)
@ -659,8 +819,8 @@ Verb { "#Exam",
"~ в|во|на {noun}/пр,2 : Search",
"~ внутри {noun}/рд : Search",
"~ в|во {noun}/вн : Search",
"~ в|во {noun}/пр,2 о|об|обо|про * : Consult",
"~ о|об|обо|про * в|во {noun}/пр,2 : Consult reverse",
"~ в|во {noun}/пр,2 ?о|?об|?обо|?про * : Consult",
"~ ?о|?об|?обо|?про * в|во {noun}/пр,2 : Consult reverse",
}
Verb { "#Search",
@ -668,8 +828,8 @@ Verb { "#Search",
"{noun}/вн : Search",
"в|во|на {noun}/пр,2 : Search",
"под {noun}/тв : LookUnder",
"~ в|во {noun}/пр,2 * : Consult",
"~ * в|во {noun}/пр,2 : Consult reverse",
"~ в|во {noun}/пр,2 ?о|?об|?обо|?про * : Consult",
"~ ?о|?об|?обо|?про * в|во {noun}/пр,2 : Consult reverse",
}
Verb { "#Open",
@ -721,7 +881,7 @@ Verb { "#Take",
}
Verb { "#Insert",
"воткн/уть,втык/ать,вставить,влож/ить",
"воткн/уть,втык/ать,вставить,влож/ить,"..
"[|про|за]сун/уть,вставь/",
"{noun}/вн,held в|во {noun}/вн,inside : Insert",
"~ {noun}/вн,held внутрь {noun}/рд : Insert",
@ -742,7 +902,7 @@ Verb { "#Drop",
Verb {
"#ThrowAt",
"брос/ить,выбро/сить,кин/уть,кида/ть,швыр/нуть,метн/уть,метать",
"брос/ить,выбро/сить,кину/ть,кинь/,кида/ть,швыр/нуть,метн/уть,метать",
"{noun}/вн,held : Drop",
"{noun}/вн,held в|во|на {noun}/вн : ThrowAt",
"~ в|во|на {noun}/вн {noun}/вн : ThrowAt reverse",
@ -1098,6 +1258,26 @@ Verb {
}
if DEBUG then
function mp:MetaForm(w)
if not w then return end
local t, hint
w = w:gsub("_", "/")
if w:find "/" then
hint = true
end
for _, f in ipairs { "им", "рд", "дт", "вн", "тв", "пр", "пр,2" } do
local ww = w
if hint then
ww = ww .. ','.. f
else
ww = ww .. '/' .. f
end
t = self.mrd:word(ww)
pn(t, " (", f, ")")
end
end
MetaVerb {
"#MetaWord",
"~_слово",
@ -1119,6 +1299,11 @@ if DEBUG then
"~_дамп",
"MetaDump"
}
MetaVerb {
"#МетаForm",
"~_форм/ы",
"* :MetaForm"
}
end
MetaVerb {
"#MetaTranscript",
@ -1133,6 +1318,8 @@ MetaVerb {
"~парсер",
"эксперт да : MetaExpertOn",
"эксперт нет : MetaExpertOff",
"глаголы : MetaVerbs",
"версия : MetaVersion",
}
MetaVerb {
@ -1173,10 +1360,16 @@ std.mod_start(function()
mp.msg.MetaUndo.EMPTY = "Отменять нечего."
MetaVerb {
"#MetaUndo",
"~отмен/ить",
"~отмен/а",
"MetaUndo",
}
end
if mp.score then
MetaVerb {
"~ счёт",
"MetaScore",
}
end
end)
-- Dialog
std.phr.default_Event = "Exam"

@ -1,5 +1,5 @@
local curdir = std.getinfo(1).source:gsub("^(.+[\\/])[^\\/]+$", "%1"):gsub("^@", "");
local table = std.table
require "fmt"
require "snapshots"
@ -97,6 +97,38 @@ local function utf_chars(b)
return res
end
local function utf_similar(str1, str2, lev)
local chars1 = utf_chars(str1)
local chars2 = utf_chars(str2)
local len1 = #chars1
local len2 = #chars2
if len1 < lev or len2 < lev then
return false
end
for i = 0, len2 - lev do
local ok = true
for k = 1, lev do
if chars1[k] ~= chars2[i + k] then
ok = false
break
end
end
if ok then return true end
end
for i = 0, len1 - lev do
local ok = true
for k = 1, lev do
if chars2[k] ~= chars1[i + k] then
ok = false
break
end
end
if ok then return true end
end
return false
end
--- Returns the Levenshtein distance between the two given strings.
-- https://gist.github.com/Badgerati/3261142
@ -223,8 +255,11 @@ end
mp = std.obj {
nam = '@metaparser';
started = false;
score = false;
maxscore = false;
expert_mode = true;
strict_mode = false;
autohelp = false;
autohelp_limit = 1000;
autohelp_noverbs = false;
@ -237,7 +272,7 @@ mp = std.obj {
detailed_inv = false;
daemons = std.list {};
{
version = "1.11";
version = "2.2.2";
cache = { tokens = {}, nouns = false };
scope = std.list {};
logfile = false;
@ -254,6 +289,7 @@ mp = std.obj {
ff = std.rawget(_G, 'utf8_next') or utf_ff;
len = std.rawget(_G, 'utf8_len') or utf_len;
char = std.rawget(_G, 'utf8_char') or utf_char;
chars = utf_chars;
};
lev_thresh = 3;
lev_ratio = 0.20;
@ -611,24 +647,29 @@ function mp.token.noun(w)
for _, o in ipairs(oo) do
local d = {}
local r = o:noun(attr, d)
for k, v in ipairs(d) do
local hidden = (k ~= 1) or w.hidden
if o:has 'multi' then
hidden = w.hidden or (v.idx ~= 1)
end
if o == std.me() and mp.myself then
for _, vm in ipairs(mp:myself(o, w.morph)) do
table.insert(ww, { optional = w.optional, word = vm, morph = attr, ob = o, alias = o.alias, hidden = hidden })
end
break
else
table.insert(ww, { optional = w.optional, word = r[k], ob = o, morph = attr, alias = v.alias, hidden = hidden })
if o == std.me() and mp.myself then
for _, vm in ipairs(mp:myself(o, w.morph) or {}) do
table.insert(ww, { optional = w.optional, word = vm, morph = attr, ob = o, alias = o.alias,
hidden = w.hidden or _ ~= 1 })
end
end
if o == mp.first_it then
table.insert(syms, 1, o)
elseif o == mp.second_it then
table.insert(syms, o)
if o ~= std.me() or (not o:hint'first' and not o:hint'second') then
for k, v in ipairs(d) do
local hidden = (k ~= 1) or w.hidden
if o:has 'multi' then
hidden = w.hidden or (v.idx ~= 1)
end
table.insert(ww,
{ optional = w.optional,
word = r[k], ob = o,
morph = attr, alias = v.alias,
hidden = hidden })
end
if o == mp.first_it then
table.insert(syms, 1, o)
elseif o == mp.second_it then
table.insert(syms, o)
end
end
end
@ -689,6 +730,11 @@ function mp:eq(t1, t2, lev)
return self:__startswith(t2, t)
end
if lev then
t1 = self:norm(t1)
t2 = self:norm(t2)
if not utf_similar(t1, t2, 3) then -- 3 is hardcoded
return false
end
local l = utf_lev(t1, t2)
if l < lev and l / (utf_len(t1) + utf_len(t2)) <= self.lev_ratio then
return l
@ -708,7 +754,7 @@ end
function mp:pattern(t, delim)
local words = {}
local pat = str_split(self:norm(t), delim or "|")
local pat = str_split(t, delim or "|")
for _, v in ipairs(pat) do
local w = { }
local ov = v
@ -939,6 +985,14 @@ function mp:skip_filter()
return true
end
function mp:ignore_filter(w)
return false
end
function mp:verb_filter(words)
return true
end
function mp:lookup_verb(words, lev)
local ret = {}
local w = self:verbs()
@ -947,9 +1001,20 @@ function mp:lookup_verb(words, lev)
for _, vv in ipairs(v.verb) do
local verb = vv.word .. (vv.morph or "")
local i, len, rlev
i, len, rlev = word_search(words, verb, lev and self.lev_thresh)
if not i and not lev and verb ~= vv.word then
i, len = self:lookup_short(words, vv.word)
local vwords = mp.strict_mode and { words[1] } or words
i, len, rlev = word_search(vwords, verb, lev and self.lev_thresh)
if not i and not lev and vv.morph then
i, len = self:lookup_short(vwords, vv.word)
if i then
local v = {}
for k = i, i + len - 1 do
table.insert(v, words[k])
end
if verb:find(table.concat(v, ' '), 1, true) ~= 1 and
not self:verb_filter(v) then
i = false
end
end
end
if i and i > 1 and not self:skip_filter({words[i - 1]}) then
i = nil
@ -1194,11 +1259,12 @@ function mp:compl_filter(v)
hidden = not v.ob.hint_noun
end
end
if hidden and self.compl_thresh == 0 then
local _, pre = self:compl_ctx()
local nsym = mp.utf.len(pre)
if hidden and self.compl_thresh == 0 and nsym == 0 then
return false
end
local _, pre = self:compl_ctx()
if mp.utf.len(pre) < self.compl_thresh then
if nsym < self.compl_thresh then
return false
end
if not v.ob or not v.morph then
@ -1304,6 +1370,7 @@ function mp:compl_ctx_poss()
table.insert(res, v)
end
end
res.eol = ctx.eol
return res
end
@ -1318,6 +1385,7 @@ function mp:compl(str)
collectgarbage("stop")
self:compl_ctx_current();
poss = self:compl_ctx_poss()
eol = poss.eol
if (#poss == 0 and e) or #words == 0 then -- no context
if #words == 0 or (#words == 1 and not e) then -- verb?
poss, eol = self:compl_verb(words)
@ -1333,13 +1401,16 @@ function mp:compl(str)
end
else -- matches
self.cache.nouns = self:nouns()
poss, eol, vargs = self:compl_match(words)
poss, eol = self:compl_match(words)
end
poss.eol = eol
self:compl_ctx_push(poss)
end
local _, pre = self:compl_ctx()
for _, v in ipairs(poss) do
if v.word == '*' then vargs = true end
if v.word == '*' and not v.hidden then
vargs = true
end
if self:startswith(v.word, pre) and not v.word:find("%*$") then
if not dups[v.word] then
dups[v.word] = v
@ -1462,7 +1533,7 @@ function mp:match(verb, w, compl)
table.insert(parsed_verb, fixed_verb)
for _, d in ipairs(verb.dsc) do -- verb variants
-- local was_noun = false
local match = { args = {}, vargs = {}, ev = d.ev, wildcards = 0, verb = parsed_verb, defaults = 0 }
local match = { args = {}, vargs = {}, skip = 0, ev = d.ev, wildcards = 0, verb = parsed_verb, defaults = 0 }
local a = {}
found = (#d.pat == 0)
for k, v in ipairs(w) do
@ -1478,7 +1549,7 @@ function mp:match(verb, w, compl)
local vargs
for lev, v in ipairs(d.pat) do -- pattern arguments
if v == '*' or v == '~*' then
vargs = true -- found
vargs = v -- found
v = '*'
end
local noun = not not v:find("^~?{noun}")
@ -1496,19 +1567,20 @@ function mp:match(verb, w, compl)
need_required = true
all_optional = false
end
if pp.default then
default = pp.default
if default then
word = pp.word
default = true
end
local new_wildcard
local k, len = word_search(a, pp.word)
if not k and mp.compare_len > 0 then
if not k and mp.compare_len > 0 and not pp.synonym then
k, len = word_search(a, pp.word, starteq)
new_wildcard = true
else
new_wildcard = false
end
if k and ((k < best or len > best_len) or
if (not required or mp.strict_mode) and k ~= 1 then k = false end -- ?word is only in 1st pos
if k and ((k < best or (k == best and len > best_len)) or
(not new_wildcard and wildcard and k <= best and len >= best_len)) then
wildcard = new_wildcard
best = k
@ -1562,19 +1634,27 @@ function mp:match(verb, w, compl)
break
end
rlev = rlev + 1
end
if (wildcard or match.wildcards > 0) and best > 1 then -- do not skip words if wildcard used
found = false
vargs = false
break
end
-- if false then
-- a = tab_exclude(a, best, best + best_len - 1)
-- else
-- if not was_noun then
if not vargs then
match.skip = match.skip + (best - 1)
for i = 1, best - 1 do
table.insert(skip, a[i])
end
end
-- end
a = tab_sub(a, best + best_len)
-- table.remove(a, 1)
-- end
vargs = false
table.insert(match, word)
table.insert(match.args, found)
if wildcard then
@ -1600,9 +1680,14 @@ function mp:match(verb, w, compl)
else
found = false
if #a > 0 or #match.vargs > 0 then
table.insert(hints, { word = v, lev = rlev })
while #a > 0 do
table.insert(match.vargs, a[1])
table.insert(match, a[1])
table.remove(a, 1)
end
table.insert(hints, { word = v, lev = rlev, match = match })
else
table.insert(hints, { word = '*', lev = rlev })
table.insert(hints, { word = vargs, lev = rlev, match = match })
end
end
if not found then
@ -1618,10 +1703,14 @@ function mp:match(verb, w, compl)
end
end
if not compl and mp.errhints then
local objs = {}
for _, pp in ipairs(pat) do -- single argument
if mp.utf.len(pp.word) >= 3 then
if not pp.synonym and not objs[pp.ob or 0] then
local k, _ = word_search(a, pp.word, self.lev_thresh)
if k then table.insert(hints, { word = pp.word, lev = rlev, fuzzy = true, match = match }) end
if k then
table.insert(hints, { word = pp.word, lev = rlev, fuzzy = true, match = match })
objs[pp.ob or 1] = true
end
end
end
end
@ -1634,7 +1723,11 @@ function mp:match(verb, w, compl)
match.defaults = match.defaults + 1
end
end
table.insert(match.args, { word = false, optional = true } )
if default then
table.insert(match.args, { word = word, default = true } )
else
table.insert(match.args, { word = false, optional = true } )
end
-- table.insert(hints, { word = v, lev = rlev })
found = true
end
@ -1645,29 +1738,39 @@ function mp:match(verb, w, compl)
-- end
if found or all_optional then
match.extra = (#a ~= 0)
table.insert(match, 1, fixed_verb) -- w[verb.verb_nr])
if self:skip_filter(skip) then
table.insert(matches, match)
end
if #match.vargs == 0 and not vargs then
match.vargs = false
if not match.extra or match.wildcards == 0 then
table.insert(match, 1, fixed_verb) -- w[verb.verb_nr])
if self:skip_filter(skip) then
table.insert(matches, match)
end
if #match.vargs == 0 and not vargs then
match.vargs = false
end
end
end
end
for k, v in ipairs(matches) do
v.nr = k
--[[
if false then
print("-----------", k)
for kk, vv in ipairs(v) do
print(vv)
end
end
]]--
end
table.sort(matches,
function(a, b)
local na, nb = #a - a.defaults, #b - b.defaults
if not a.extra and a.skip == 0 then
na = na + 100
end
if not b.extra and b.skip == 0 then
nb = nb + 100
end
if na == nb and a.wildcards == b.wildcards then
return a.nr < b.nr
end
@ -1969,6 +2072,14 @@ function mp:correct(inp)
if rinp ~= '' then rinp = rinp .. ' ' end
rinp = rinp .. v
end