How to parse the config file shown to create a lua table that is desired? - lua

I want to parse a config file which has information like:
[MY_WINDOW_0]
Address = 0xA0B0C0D0
Size = 0x100
Type = cpu0
[MY_WINDOW_1]
Address = 0xB0C0D0A0
Size = 0x200
Type = cpu0
[MY_WINDOW_2]
Address = 0xC0D0A0B0
Size = 0x100
Type = cpu1
into a LUA table as follows
CPU_TRACE_WINDOWS =
{
["cpu0"] = {{address = 0xA0B0C0D0, size = 0x100},{address = 0xB0C0D0A0, size = 0x200},}
["cpu1"] = {{address = 0xC0D0A0B0, size = 0x100},...}
}
I tried my best with some basic LUA string manipulation functions but couldn't get the output that I'm looking for due to repetition of strings in each sections like 'Address',' Size', 'Type' etc. Also my actual config file is huge with 20 such sections.
I got so far, this is basically one section of the code, rest would be just repetition of the logic.
OriginalConfigFile = "test.cfg"
os.execute("cls")
CPU_TRACE_WINDOWS = {}
local bus
for line in io.lines(OriginalConfigFile) do
if string.find(line, "Type") ~= nil then
bus = string.gsub(line, "%a=%a", "")
k,v = string.match(bus, "(%w+) = (%w+)")
table.insert(CPU_TRACE_WINDOWS, v)
end
end
Basically I'm having trouble with coming up with the FINAL TABLE STRUCTURE that I need. v here is the different rvalues of the string "Type". I'm having issues with arranging it in the table. I'm currently working to find a solution but I thought I could ask for help meanwhile.

This should work for you. Just change the filename to wherever you have your config file stored.
f, Address, Size, Type = io.input("configfile"), "", "", ""
CPU_TRACE_WINDOWS = {}
for line in f:lines() do
if line:find("MY_WINDOW") then
Type = ""
Address = ""
Size = ""
elseif line:find("=") then
_G[line:match("^%a+")] = line:match("[%d%a]+$")
if line:match("Type") then
if not CPU_TRACE_WINDOWS[Type] then
CPU_TRACE_WINDOWS[Type] = {}
end
table.insert(CPU_TRACE_WINDOWS[Type], {address = Address, size = Size})
end
end
end
end
It searches for the MY_WINDOW phrase and resets the variable. If the table exists within CPU_TRACE_WINDOWS, then it just appends a new table value, otherwise it just creates it. Note that this is dependent upon Type always being the last entry. If it switches up anywhere, then it will not have all the required information. There may be a cleaner way to do it, but this works (tested on my end).
Edit: Whoops, forgot to change the variables in the middle there if MY_WINDOW matched. That needed to be corrected.
Edit 2: Cleaned up the redundancy with table.insert. Only need it once, just need to make sure the table is created first.

Related

How do I use more then one pattern for gmatch

Hello I am trying to get some data from a text file and put it into a table.
Im not sure how to add more then one pattern while also doing what I want, I know this pattern by its self %a+ finds letters and %b{} finds brackets, but I am not sure how to combine them together so that I find the letters as a key and the brackets as a value and have them be put into a table that I could use.
text file :
left = {{0,63},{16,63},{32,63},{48,63}}
right = {{0,21},{16,21},{32,21},{48,21}}
up = {{0,42},{16,42},{32,42},{48,42}}
down = {{0,0},{16,0},{32,0},{48,0}}
code:
local function get_animations(file_path)
local animation_table = {}
local file = io.open(file_path,"r")
local contents = file:read("*a")
for k, v in string.gmatch(contents, ("(%a+)=(%b{})")) do -- A gets words and %b{} finds brackets
animation_table[k] = v
print("key : " .. k.. " Value : ".. v)
end
file:close()
end
get_animations("Sprites/Player/MainPlayer.txt")
This is valid Lua code, why not simply execute it?
left = {{0,63},{16,63},{32,63},{48,63}}
right = {{0,21},{16,21},{32,21},{48,21}}
up = {{0,42},{16,42},{32,42},{48,42}}
down = {{0,0},{16,0},{32,0},{48,0}}
If you don't want the data in globals, use the string library to turn it into
return {
left = {{0,63},{16,63},{32,63},{48,63}},
right = {{0,21},{16,21},{32,21},{48,21}},
up = {{0,42},{16,42},{32,42},{48,42}},
down = {{0,0},{16,0},{32,0},{48,0}},
}
befor you execute it.
If you insist on parsing that file you can use a something like this for each line:
local line = "left = {{0,63},{16,63},{32,63},{48,63}}"
print(line:match("^%w+"))
for num1, num2 in a:gmatch("(%d+),(%d+)") do
print(num1, num2)
end
This should be enough to get you started. Of course you wouldn't print those values but put them into a table.

Biopython Genbank.Record : trying to understand source code

I am writing a csv reader to generate Genbank files to capture annotations with sequence.
First I used a Bio.SeqRecord and got correctly formatted output but the SeqRecord class lacks fields that I need.
Blockquote
FEATURES Location/Qualifiers
HCDR1 27..35
HCDR2 50..66
HCDR3 99..109
I switched to Bio.GenBank.Record and have the needed fields except now the annotation formatting is wrong. It can't have the extra "type:" "location:" and "qualifiers:" text and the information should all be on one line.
Blockquote
FEATURES Location/Qualifiers
type: HCDR1
location: [26:35]
qualifiers:
type: HCDR2
location: [49:66]
qualifiers:
type: HCDR3
location: [98:109]
qualifiers:
The code for pulling annotations is the same for both versions. Only the class changed.
# Read csv entries and create a container with the data
container = Record()
container.locus = row['Sample']
container.size = len(row['Seq'])
container.residue_type="PROTEIN"
container.data_file_division="PRI"
container.date = (datetime.date.today().strftime("%d-%b-%Y")) # today's date
container.definition = row['FullCloneName']
container.accession = [row['Vgene'],row['HCDR3']]
container.version = getpass.getuser()
container.keywords = [row['ProjectName']]
container.source = "test"
container.organism = "Homo Sapiens"
container.sequence = row['Seq']
annotations = []
CDRS = ["HCDR1", "HCDR2", "HCDR3"]
for CDR in CDRS:
start = row['Seq'].find(row[CDR])
end = start + len(row[CDR])
feature = SeqFeature(FeatureLocation(start=start, end=end), type=CDR)
container.features.append(feature)
I have looked at the source code for Bio.Genbank.Record but can't figure out why the SeqFeature class has different formatting output compared to Bio.SeqRecord.
Is there an elegant fix or do I write a separate tool to reformat the annotations in the Genbank file?
After reading the source code again, I discovered Bio.Genbank.Record has its own Features method that takes key and location as strings. These are formatted correctly in the output Genbank file.
CDRS = ["HCDR1", "HCDR2", "HCDR3"]
for CDR in CDRS:
start = row['Seq'].find(row[CDR])
end = start + len(row[CDR])
feature = Feature()
feature.key = "{}".format(CDR)
feature.location = "{}..{}".format(start, end)
container.features.append(feature)

Re-initialize table without losing references

I'd like to re-initialize a table without losing references to it.
What I want to achieve is defining tables in files, and when a file is changed (with a text editor) the file is reloaded, changing the table. Of course this doesn't change the table but creates a new instance, old references will still point to the old table.
Any suggestions?
EDIT: I want to elaborate on what I want to achieve. An example with game characters and weapons. I want to modify the weapons.lua and so affect the characters.
-- weapons.lua
sword = { damage = 3 }
-- characters.lua
character = { weapon = sword }
Adding a level of indirection (putting "sword" inside "weapons") like suggested by JWT doesn't help, unless I split character into { weaponTable = weapons, weaponKey = "sword" } but I don't see this as an option.
Anchor everything that needs to survive in the global environment. Nesting is fine, and this doesn't have to be your primary reference. (You can still local things, but make sure to initialize those local variables from the global environment and update the global if you change the local.)
To initialize the global values, say
foo = foo or value -- if foo is always true-ish
bar = (bar == nil) and value or bar -- if bar may be `false`
To initialize or update tables, you can
foo = foo or { }
foo.bar = foo.bar or 23
foo.baz = foo.baz or 42
-- and so on...
but that's kinda icky, so maybe say
function reinit( new, old ) -- (re)initialize one level, prefer old
if old == nil then return new end
if type( old ) ~= "table" then return old end
for k, v in pairs( new ) do
if old[k] == nil then old[k] = v end
end
return old
end
function reset( new, old ) -- (re)initialize one level, prefer new
if old == nil then return new end
if type( old ) ~= "table" then return new end
for k, v in pairs( new ) do old[k] = v end
return old
end
and then just
foo = reinit( { bar = 23, baz = 42 }, foo ) -- only setting if not defined
-- or
foo = reset( { bar = 23, baz = 42 }, foo ) -- always overriding given fields
or maybe make it even more fancy and say
function traverse( t, path )
local here, last, lastk, created = t
-- follow path of several keys starting from t, creating tables as needed
for k in path:gmatch "[^.]+" do
k = tonumber( k ) or k -- convert all-number keys to integer (for arrays)
local next = here[k]
if not next then
next, created = { }, true
here[k] = next
else
created = false
end
lastk, last, here = k, here, next
end
return here, last, lastk, created
end
function repopulate( path, value, update )
update = update or reinit -- pass 'reset' as 'update' for the other behavior
-- or something entirely different if that's what you need
local here, last, lastk, created = traverse( _G, path )
if type( value ) == "table" then
update( value, here )
else
if created then last[lastk] = nil end -- created one level too much
update( { [lastk] = value }, last )
end
end
and then (with arbitrary nesting)
-- No need to create 'state' first if it doesn't exist yet!
-- (If it exists, it will get updated, otherwise it's created)
repopulate( "state.player.weapon", { kind = "sword", damage = 11 } )
-- Do keep in mind that generally update order is relevant -- you may want to
-- give a newly created player a default inventory, but you may not want to
-- "refill" the player's inventory on every reload. So generally `repopulate`
-- with the parent and all child nodes for from-scratch creation, then
-- `repopulate` the child nodes that need to be checked & potentially updated
-- as well.
-- (So here you'd probably repopulate `state.player` first and then
-- `state.player.weapon` or other fields only if they should be updated anyway.)
-- e.g.:
repopulate( "state.player", {
x = 0, y = 0, hp = 100, mp = 100, level = 0, -- and so on
weapon = { kind = "sword", damage = 11 }, -- etc. etc.
} )
-- on reload always force a sword-kind weapon, leave rest (damage, ...) as-is
repopulate( "state.player.weapon", { kind = "sword" }, reset )
-- or alternatively: only if player has no weapon, give them a sword
repopulate( "state.player.weapon", { kind = "sword", damage = 3 } )
And you can go further, add metamethods to hide some of that shuffling, define different update policies, ... – you've seen some of the possibilities, now go and build your own version that fits your style and your code.
(While you're free to use the above code in any way, please note that it was written ad-hoc in the browser. I did some testing, fixed some glitches, and it seems to work now, but don't be surprised if there's still one or two bugs hiding in there. So play with this, change it, break it (and see how/why it breaks), adapt and extend it, ... – but unless you completely understand what it does and can fix any bugs, I strongly suggest you write your own version, or just stick to the basics. You probably don't need everything that this does, and you're likely to need other things that this doesn't do. As this is a central part of the reloading/live-coding infrastructure and everything has to be adapted to be reload-compatible, any mismatch between your tooling and what you actually need will result in a lot of pain everywhere in your code. So if you need something like this, put in a day or two to make it work the way you need it to, or you will regret it.)
(Free bonus warning: If you do OOP, you'll probably have to store and retrieve your classes instead of creating them every time, otherwise old objects from previous iterations will miss code updates and still run their old methods. I've forgotten about that more than just a couple of times and wasted several hours pondering "why isn't it fixed now?!?" after repeatedly re-loading code... So remember to anchor your metatables, anchor your classes!)
You could nest the tables in another table.
Before:
local a = { 1, 2, 3 }
local b = { 7, 8, 9 }
print(a[2] + b[2]) -- #=> 10
After:
local lookup = {
a = { 1, 2, 3 },
b = { 7, 8, 9 }
}
print(lookup.a[2] + lookup.b[2]) -- #=> 10
Then you can fully replace (or just update) a table in the lookup table and any dependent statements will use that updated value:
lookup.a = { 100, 50, 0 }
print(lookup.a[2] + lookup.b[2]) -- #=> 58
I don't know if it's exactly what you needed (As an ID is necessary) but I hope it will fit your needs.
meta = {
tables = {},
__call = function(arg, t)
for k, v in pairs(t) do
arg[k] = v
end
end,
__bnot = function(arg)
return arg.__key
end,
__newindex = function(arg, key, val)
meta.tables[arg.__key][key] = val
end,
__index = function(arg, key)
return meta.tables[arg.__key][key]
end
}
function RefTable(arg)
local newtable = {}
if arg ~= nil then
newtable.__key = arg
setmetatable(newtable, meta)
if meta.tables[arg] == nil then
meta.tables[arg] = {}
end
else
error("RefTable can't have nil key")
end
return newtable
end
-- Using the RefTable
sword = RefTable("Sword")
sword({damage = 3})
sword.cooldown = 10
character = {sword = sword}
print("base", sword.damage, sword.cooldown)
print("embed", character.sword.damage, character.sword.cooldown)
sword = RefTable("Sword")
sword({damage = 8, cooldown = 50})
print("embed2", character.sword.damage, character.sword.cooldown)
print(sword.__key, sword.cooldown)
ref = RefTable("Sword")
ref.cooldown = 1000
print(sword.cooldown)

Adding new elements to a table that has already been saved

So, I am working on a premium code system where you can input a secret code and if the code is valid, it will reward you based on what code you have used. Pretty simple idea, however, I have some trouble with it.
I have a table with all the codes that looks something like this:
GameState.PremiumCodesTable = GameState.PremiumCodesTable or {{code = "X45", value = 30}, {code = "MM4", value = 45}, {code = "B47", value = 100}}
Basically, if the game runs for the first time, the GameState.PremiumCodesTable will be nil and it will create new element in GameState table which is this table: {{code = "X45", value = 30}, {code = "MM4", value = 45}, {code = "B47", value = 100}}
Now, when the user uses the valid code, it gets deleted from the table. Eventually the user will find all the codes, use them and the GameState.PremiumCodesTable table will be empty.
Now, I want to update the game and bring in some new codes to find. However, since the GameState.PremiumCodesTable is already empty and saved, it will always be empty.
How can I add more codes later on?
I have a solution, but I don't like it, so I am asking here. My solution is to add code by code like so:
GameState.PremiumCodesTable[1] = GameState.PremiumCodesTable[1] or {code = "X45", value = 30}
GameState.PremiumCodesTable[2] = GameState.PremiumCodesTable[2] or {code = "MM4", value = 45}
GameState.PremiumCodesTable[3] = GameState.PremiumCodesTable[3] or {code = "B47", value = 100}
and so on... However, this takes too much space, so I'd like it to have in only ONE big table like the one above.
Use the save file to store an array of code strings that have been used, then initialize PremiumCodesTable to contain full data for all of the codes. That way, the save file keeps the bare minimum amount of information, and you get maximum freedom to process the save file however you want. (You may need to store the code data in a non-array table to make it easier to access.)
GameState.usedCodes = GameState.usedCodes or {}
local PremiumCodesTable = {
{code = "X45", value = 30},
{code = "MM4", value = 45},
{code = "B47", value = 100}
}

lua Hashtables, table index is nil?

What I'm currently trying to do is make a table of email addresses (as keys) that hold person_records (as values). Where the person_record holds 6 or so things in it. The problem I'm getting is that when I try to assign the email address as a key to a table it complains and says table index is nil... This is what I have so far:
random_record = split(line, ",")
person_record = {first_name = random_record[1], last_name = random_record[2], email_address = random_record[3], street_address = random_record[4], city = random_record[5], state = random_record[6]}
email_table[person_record.email_address] = person_record
I wrote my own split function that basically takes a line of input and pulls out the 6 comma seperated values and stores them in a table (random_record)
I get an error when I try to say email_table[person_record.email_address] = person_record.
But when I print out person_record.email_address it's NOT nil, it prints out the string I stored in it.. I'm so confused.
function split(str, pat)
local t = {} -- NOTE: use {n = 0} in Lua-5.0
local fpat = "(.-)" .. pat
local last_end = 1
local s, e, cap = str:find(fpat, 1)
while s do
if s ~= 1 or cap ~= "" then
table.insert(t,cap)
end
last_end = e+1
s, e, cap = str:find(fpat, last_end)
end
if last_end <= #str then
cap = str:sub(last_end)
table.insert(t, cap)
end
return t
end
The following code is copy and pasted from your example and runs just fine:
email_table = {}
random_record = {"first", "second", "third"}
person_record = {first_name = random_record[1], last_name = random_record[1], email_address = random_record[1]}
email_table[person_record.email_address] = person_record
So your problem is in your split function.
BTW, Lua doesn't have "hashtables". It simply has "tables" which store key/value pairs. Whether these happen to use hashes or not is an implementation detail.
It looks like you iterating over some lines that have comma-separated data.
Looking at your split function, it stops as soon as there's no more separator (,) symbols in particular line to find. So feeding it anything with less than 3 ,-separated fields (for very common example: an empty line at end of file) will produce a table that doesn't go up to [3]. Addressing any empty table value will return you a nil, so person_record.email_address will be set to nil as well on the 2nd line of your code. Then, when you attempt to use this nil stored in person_record.email_address as an index to email_table in 3rd line, you will get the exact error you've mentioned.

Resources