Pattern matching with minus sign between spaces - lua

I have a variable message which I get from User input. For example:
!word number
word-word---word
or
!word
wordword-word
Currently I create a table and fill it with every single word/number (without digits like -)
--input table
it = {}
--put input in table
for _input in string.gmatch((message), '%w+') do
it[#it+1] = { input=_input }
end
First of all I cant get words with minus between them to my table.
Also I cant check if it[2].input is not empty. This is an example how I check the table:
--TEST START
if it[1].input == 'test' then
--do something
end
--TEST END
I've tried this without any working result.

-- %s = space character
-- %- = escaped magic character
message = "!word number word-word---word"
-- might not be the most ideal method to fil an array up...
it = {(function() local t = {}; for _input in string.gmatch(message,"[^%s%-]+") do t[#t+1] = {input = _input} end return unpack(t) end)()}
print(t[2].input) --> number
--
--
it = {}
for _input in string.gmatch(message,"[^%s%-]+") do
it[#it+1] = {input = _input}
end
-- now checking value should work fine
if (it[2] and it[2].input == "number") then -- checking that it[2] is set as something and then comparing input
print("t[2].input = \"number\"");
end

Related

Pandoc filter in Lua to insert span into existing string

I am writing a Lua filter for pandoc that adds a glossary function to HTML output of a markdown file. The goal is to add mouseover text to each occurrence of an acronym or key definition in the document.
I would like to be able to include acronyms when they occur in a list (surrounded by punctuation), but not by letters (e.g. so CO isn't highlighted in a word such as cobalt).
My MWE fails on this count because strings in the Pandoc AST include adjacent punctuation (e.g. Str "CO/DBP/SBP," or Str "CO,",Space,Str "SBP,").
-- # MWE
-- Parse glossary file (summarised here for brevity)
local glossary = {CO = "Cardiac Output", DBP = "Diastolic Blood Pressure", SBP = "Systolic Blood Pressure"}
-- Substitute glossary term for span with a mouseover link
function Str(elem)
for key, value in next, glossary do
if elem.text == key then
return pandoc.Span (key, {title = value, class = "glossary"})
end
end
end
I have had a play with string.sub and string.find but wasn`t able to get anything workable, chiefly because I wasn't sure how to go about returning both the new Span and the Str (minus its new Span). Any help would be appreciated!
My test markdown contains:
# Acronyms: SBP, DBP & CO
Spaced acronyms: CO and SBP and DBP.
In a comma-separated list: CO, SBP, DBP; with backslashes; CO/DBP/SBP, and in bullet points:
* CO
* SBP
* DBP
You can just return a table with multiple elements. My idea was to look for the first separator and then replace the glossary entries with spans:
-- Parse glossary file (summarised here for brevity)
local glossary = {CO = "Cardiac Output", DBP = "Diastolic Blood Pressure", SBP = "Systolic Blood Pressure"}
local Set = function(list)
local set = {}
for i,v in ipairs(list) do
set[v] = true
end
return set
end
local findSeparator = function(text)
local separator = Set{",", "/", " "}
for i = 1, #text do
local s = string.sub(text,i,i)
if separator[s] then
return s
end
end
end
local separatedList = function(text)
local found
local t = {}
local separator = findSeparator(text)
if not separator then return end
for abb in string.gmatch(text, "%P+") do
if glossary[abb] then
found = true
t[#t+1] = pandoc.Span(abb, {title = abb, class = "glossary"})
t[#t+1] = pandoc.Str(separator)
end
end
if found then
-- remove last separator if there are more then one elements in the list
-- because otherwise the seperator is part of the element and needs to stay
if #t > 2 then t[#t] = nil end
return t
end
end
local glossarize = {
Str = function(el)
if glossary[el.text] then
return pandoc.Span(el.text, {title = glossary[el.text], class = "glossary"})
else
return separatedList(el.text)
end
end
}
function Pandoc(doc)
local div = pandoc.Div(doc.blocks)
local blocks = pandoc.walk_block(div, glossarize).content
return pandoc.Pandoc(blocks, doc.meta)
end

Lua How to remove apostrophe(s) from string

So I am having a issue with Lua gsub removing apostrophes from strings if there is one apostrophe on its own or loads of them i can't seem to get it to remove any of them.
local uri_without_args = "'" --one on its own
local uri_without_args = "''''''''lol''''" --in text
local uri_without_args = "''''''''''''" --loads
--etc--etc all occurrences must go
local list = {
"%'", --apostrophe
}
for k,v in ipairs(list) do
local uri_without_args_remove_duplicates, occurrences = uri_without_args:gsub(""..v.."","")
if occurrences > 0 then
occurrences = occurrences + 1
for i=1, occurrences do
if uri_without_args_remove_duplicates=="" then
--do nothing
else
uri_without_args = uri_without_args:gsub(""..v.."","")
end
end
end
end
print(uri_without_args)
The only time you assign a new value to uri_without_args is when uri_without_args_remove_duplicates is not empty. If you either remove the if statement from around the assignment to uri_without_args, or if uri_without_args starts off as "''''''''lol''''", then it works fine.
As Egor said in the comment, you also could simply use uri_without_args_remove_duplicates as the result value.

Constructing Key/Value Table in Lua

I'm trying to build a script for a MUD I play that will create a table to keep track of average xp for each mob. I'm having trouble with the syntax of checking whether an element in a table exists and if not creating it. I tried something like this but keep getting: attempt to index field '?' (a nil value)
mobz_buried = {
{mob = "troll", quantity = 2}
{mob = "warrior", quantity = 1}
{mob = "wizard", quantity = 1}} -- sample data
number_of_mobz_buried = 4
xp_from_bury = 2000 -- another script generates these values, these are all just examples
xp_per_corpse = xp_from_bury / number_of_mobz_buried
for _, v in ipairs(mobz_buried) do
if type(mobz[v].kc) == "variable" then -- kc for 'kill count', number of times killed
mobz[v].kc = mobz[v].kc + 1 -- if it exists increment kc
else
mobz[v].kc = 1 -- if it doesn't exist create a key value that matches the mobs name and make the kc 1
end
if type(mobz[v].xp) == "variable" then -- xp for average experience points
mobz[v].xp = (((mobz[v].kc - 1) * mobz[v].xp + xp_per_corpse)/mobz[v].kc) -- just my formula to find the average xp over a range of differant buries
else
mobz[v].xp = xp_per_corpse -- if it doesn't exist create the table just like before
end
end
I'm trying to end up with mobz.troll = {kc, xp}, mobz.warrior = {kc, xp}, mobz.wizard = {kc, xp} and the ability to add more key values based off of the names mobz_buried gives me.
Based on extra info from your comments, it sounds like you didn't construct a table for mobz. Try this:
local mobz = {}
for _, v in ipairs(mobz_buried) do
mobz[v.mob] = mobz[v.mob] or {}
mobz[v.mob].kc = (mobz[v.mob].kc or 0) + 1
-- etc...
end

lua - checking for duplicate data in a string

I have the following string data that I receive as input:
"route1,1234,1,no~,,route2,1234,1,no~,"
It represents two "records" of data... where each record has 4 fields.
I've built code to parse this string into it's individual columns / fields.
But the part that isn't working is when I test to see if I have any duplicates in field 2. Field 2 is the one that currently has "1234" as the value.
Here's the code:
function string:split(delimiter)
local result = { }
local from = 1
local delim_from, delim_to = string.find( self, delimiter, from )
while delim_from do
table.insert( result, string.sub( self, from , delim_from-1 ) )
from = delim_to + 1
delim_from, delim_to = string.find( self, delimiter, from )
end
table.insert( result, string.sub( self, from ) )
return result
end
local check_for_duplicate_entries = function(route_data)
local route
local route_detail = {}
local result =true
local errtxt
local duplicate = false
print("received :" ..route_data)
route = string.gsub(route_data, "~,,", "~")
route = route:sub(1,string.len(route)-2)
print("route :" ..route)
-- break up in to an array
route = string.split(route,"~")
for key, value in pairs(route) do
route_detail[key] = string.split(value,",")
end
local list_of_second_column_only = {}
for key,value in pairs(route_detail) do
local temp = value[2]
print(temp .. " - is the value I'm checking for")
if list_of_second_column_only[temp] == nil then
print("i dont think it exists")
list_of_second_column_only[key] = value[2]
print(list_of_second_column_only[key])
else
--found a duplicate.
return true
end
end
return false
end
print(check_for_duplicate_entries("route1,1234,1,no~,,route2,1234,1,no~,"))
I think where I'm going wrong is the test:
if list_of_second_column_only[temp] == nil then
I think I'm checking for key with the value temp instead of a value with the value that temp contains. But I don't know how to fix the syntax.
Also, I'm wondering if there's a more efficient way to do this.
The number of "records" i receive as input is dynamic / unknown, as is the value of the second column in each record.
Thanks.
EDIT 1
The post I was trying to use as a reference is: Search for an item in a Lua list
In the answer, they show how to test for a record in the table by value, instead of looping through the entire table...
if items["orange"] then
-- do something
end
I was playing around to try and do something similar...
This should be a bit more efficient with only one table creation and less regex matching.
The match does require that you're only interested in dups in the second field.
local function check_for_duplicate_entries(route_data)
assert(type(route_data)=="string")
local field_set = {}
for route in route_data:gmatch"([^~]*)~,?,?" do
local field = route:match",([^,]*)"
if field_set[field] then
return true
else
field_set[field] = true
end
end
return false
end
Try this. It's doing the check on the value of the second field.
I haven't looked at the efficiency.
if list_of_second_column_only[value[2]] == nil then
print("i dont think it exists")
list_of_second_column_only[value[2]] = true
print(list_of_second_column_only[value[2]])
else
--found a duplicate.
return true
end

How to get xth key of a table in Lua

I have 2 functions in Lua which create a dictionary table and allow to check if a word exists:
local dictTable = {}
local dictTableSize = 0
function buildDictionary()
local path = system.pathForFile("wordlist.txt")
local file = io.open( path, "r")
if file then
for line in file:lines() do
dictTable[line] = true
dictTableSize = dictTableSize + 1
end
io.close(file)
end
end
function checkWord(word)
if dictTable[word] then
return(true)
else
return(false)
end
end
Now I want to be able to generate a couple of random words. But since the words are the keys, how can I pick some, given the dictTableSize.
Thanks
Just add a numerical index for each word to the dictionary while loading it:
function buildDictionary()
local path = system.pathForFile("wordlist.txt")
local file = io.open( path, "r")
if file then
local index = 1
for line in file:lines() do
dictTable[line] = true
dictTable[index] = line
index = index + 1
end
io.close(file)
end
end
Now you can get a random word like this:
function randomWord()
return dictTable[math.random(1,#dictTable)]
end
Side note: nil evaluates to false in Lua conditionals, so you could write checkWord like this:
function checkWord(word)
return dictTable[word]
end
Another side note, you'll get less polution of the global namespace if you wrap the dictionary functionality into an object:
local dictionary = { words = {} }
function dictionary:load()
local path = system.pathForFile('wordlist.txt')
local file = io.open( path, 'r')
if file then
local index = 1
for line in file:lines() do
self.words[line] = true
self.words[index] = line
index = index + 1
end
io.close(file)
end
end
function dictionary:checkWord(word)
return self.words[word]
end
function dictionary:randomWord()
return self.words[math.random(1,#self.words)]
end
Then you can say:
dictionary:load()
dictionary:checkWord('foobar')
dictionary:randomWord()
Probably two ways: you can keep the array with words and just do words[math.random(#words)] when you need to pick a random word (just make sure that the second one is different from the first).
The other way is to use next the number of times you need:
function findNth(t, n)
local val = next(t)
for i = 2, n do val = next(t, val) end
return val
end
This will return b for findNth({a = true, b = true, c = true}, 3) (the order is undefined).
You can avoid repetitive scanning by memoizing the results (at this point you will be better off using the first way).
this is a trade off that you have for using the word table the way you are. i would invert the word table once you load it, so that you can get references to words by index as well if you have to. something like this:
-- mimic your dictionary structure
local t = {
["asdf"] = true, ["wer"] = true, ["iweir"] = true, ["erer"] = true
}
-- function to invert your word table
function invert(tbl)
local t = {}
for k,_ in pairs(tbl) do
table.insert(t, k)
end
return t
end
-- now the code to grab random words
local idx1, idx2 = math.random(dictTableSize), math.random(dictTableSize)
local new_t = invert(t)
local word1, word2 = new_t[idx1], new_t[idx2]
-- word1 and word2 now have random words from your 'dictTable'

Resources