Author Topic: tbl parser  (Read 2480 times)

0 Members and 1 Guest are viewing this topic.

Offline Nuke

  • Ka-Boom!
  • 212
  • Mutants Worship Me
heres the table file parser ive been working on, i designed it to be more user friendly than the previous unreleased version. this version
rather than create a large parse function, you create 2 lua tables (not tbl files). the first is a datatypes table where you can define the time of data that a tag will be followed by. right now its got a pretty long list of things you can convert strings to. the second table (more like a table of tables), the hierarchy table, defines where the data goes. it consists of lists of a few magic strings and user strings, which define a "path" within the table. all data is stored to a large nested lua table, data can be stored in named keys, as indexed keys or both. its a good way to interface modders with scripts, you can write scripts that modders can use without any scriptng knowledge, and in some cases helps speed up mod development.

Code: [Select]
--[[=========================================]]--
--[[======= virtual table file parser =======]]--
--[[=========================================]]--
--[[======== do not edit (see below) ========]]--
--[[=========================================]]--
--load a file from path, returns a table of lines
load_file = function(fname, fpath) 
--dont read a file thats not there
if cf.fileExists(fname, fpath, false) then
--open the file
local file = cf.openFile(fname, "r", fpath)
--read the first line
local lines = {}
local idx = 1
line = file:read("*l")
while line ~= nil do
lines[idx] = line
idx = idx + 1
line = file:read("*l")
end
file:close()
ba.print("File '" .. fname .."' loaded, "..#lines.." lines found\n")
return lines
else
ba.warning("File '" .. fname .."' not found")
return nil
end
end
--cleans up a line of text for parsing, removes comments and whitespace
clean_line = function(line)
--strip out comments
line = line:gsub(";.*" ,"")
--remove leading white space
line = line:gsub("%s*(.+)","%1")
--remove trailing white space
line = line:reverse()
line = line:gsub("%s*(.+)","%1")
line = line:reverse()
return line
end
--takes a keyword and removes all special markers
get_key = function(keyword)
local key = keyword:gsub("%p*(.+)" ,"%1")
key = key:reverse()
key = key:gsub("%p*(.+)" ,"%1")
key = key:reverse()
return key
end
--read a line of text, takes out white space and comments, returns the keyword string and data string
--if there is no data string following the keyword, then the text portion of the keyword is returned
read_line = function(pline)
--bail if nothing read
if pline == nil or pline:len() == 0 then return nil end
pline = clean_line(pline)
--find out where the start and end of the $keyword: are
local si,ei = pline:find("%$.+%:")
--see if it found it
if si == nil or ei == nil then
--nope, try other +keyword: varients
si,ei = pline:find("%+.+%:")
--now did it work
if si == nil or ei == nil then
--nope, try other @keyword: varients
si,ei = pline:find("%@.+%:")
--this is getting old
if si == nil or ei == nil then
--nope, is it a section #keyword
si,ei = pline:find("%#.+")
--did it ****ing work yet
if si == nil or ei == nil then
--its nothing, eject and stop wasting time on it
return nil
end
end
end
end
--get the keyword from the line and convert it to lower case
local kword = pline:sub(si,ei):lower()
--get the rest of the line
local dline = pline:sub(ei+1)
--remove leading white space, again
dline = dline:gsub("%s*(.+)","%1")
--anything left
if dline:len() > 0 then
return kword, dline
else
--no text after keyword, no second retur
return kword
end
end
--convert the string data into whatever format it needs to go in, assumes leading and trailing whirespace is removed
--returns type converted data,
parse_line = function(dline, dtype)
--check if our line exists and is long enough to do anything with
if not dline or dline:len() < 1 then return nil end
--make our dtype lowercase
if dtype and type(dtype) == "string" then dtype = dtype:lower() end
--default return value, returns unmodified data string, useful for names
if dtype == "name" then
return dline
elseif dtype == "word" then --like name but converts to lower case
return dline:lower()
elseif dtype == "number" then --number values
return tonumber(dline)
elseif dtype == "angle" then --angle values in degrees (converts to radians)
return math.rad(tonumber(dline))
elseif dtype == "string" then --returns any text placed between quotes "", can include most special characters
return dline:gsub("%s*\"%s*" , "")
elseif dtype == "boolean" then --accepts true or false, on or off, yes or no, anything else is false
--strip spaces
dline = dline:gsub("%s", "")
--make it lowercase
dline = dline:lower()
if dline == "true" or dline == "on" or dline == "yes" then return true else return false end
elseif dtype == "vector" then --returns a vector (seporated by spaces or commas)
local X,Y,Z
--strip parentheses and commas (theyre optional)
dline = dline:gsub("%(*%)*%,+"," ")
--iterate through and store
for c in dline:gmatch("%S+") do
if X == nil then
X = tonumber(c)
elseif Y == nil then
Y = tonumber(c)
elseif Z == nil then
Z = tonumber(c)
break
end
end
return ba.createVector(X,Y,Z)
elseif dtype == "euler" then --pitch bank and heading, in degrees (converts to radians), returns as vector
local P, B, H
--strip commas and parentheses (theyre optional)
dline = dline:gsub("%(*%)*%,+"," ")
--iterate through and store
for c in dline:gmatch("%S+") do
if P == nil then
P = tonumber(c)
elseif B == nil then
B = tonumber(c)
elseif H == nil then
H = tonumber(c)
break
end
end
P,B,H = math.rad(P),math.rad(B),math.rad(H)
return ba.createVector(P,B,H)
elseif dtype == "pbh" then --pitch bank and heading, in degrees (converts to radians), returns as orientation
local P, B, H
--strip commas and parentheses (theyre optional)
dline = dline:gsub("%(*%)*%,+"," ")
--iterate through and store
for c in dline:gmatch("%S+") do
if P == nil then
P = tonumber(c)
elseif B == nil then
B = tonumber(c)
elseif H == nil then
H = tonumber(c)
break
end
end
P,B,H = math.rad(P),math.rad(B),math.rad(H)
return ba.createOrinetation(P,B,H)
elseif dtype == "matrix" then --parse an entire 3x3 matrix
local m = ba.createOrinetation()
local i = 1
--strip commas and parentheses (theyre optional)
dline = dline:gsub("%(*%)*%,+"," ")
for c in dline:gmatch("%S+") do
m[i] = tonumber(c)
if i == 9 then break end
i=i+1
end
return m
elseif dtype == "number_list" then
local t = {}
local i = 1
--strip commas and parentheses (theyre optional)
dline = dline:gsub("%(*%)*%,+"," ")
for c in string.gmatch(dline, "([%d-+e.]+)") do
t[i] = tonumber(c)
i=i+1
end
return t
elseif dtype == "string_list" then
local t = {}
local i = 1
--strip commas and parentheses (theyre optional)
dline = dline:gsub("%(*%)*%,+"," ")
for c in string.gmatch(dline, '%"([%w_ ]+)%"') do
t[i] = c
i=i+1
end
return t
elseif dtype == "mixed_list" then
local t = {}
local i = 1
--strip commas and parentheses (theyre optional)
dline = dline:gsub("%(*%)*%,+"," ")
for c in string.gmatch(dline, '%"([%w_ .+-]+)%"') do
t[i] = maybe_tonumber(c)
i=i+1
end
return t
else --oh ****, nothing i thought of happened
return dline --default return
end
end
--replace magic strings in a branch with string generated by the parser, returns a new branch of the same length as branch, a table containing
--all used magic strings, and an error string if something fails. will not return nil.
resolve_branch = function(branch, nstrings, keyword, value, index, vindex)
--copy table
local newbranch = {}
--some vars
local branchlen = #branch
local mstr = 1
local estr = ""
local newbstrings = {} --lulz
--swap out stuff
for i = 1, branchlen do
if string.find(branch[i]:lower(), "*bstrings*") then
if nstrings[mstr] then
newbranch[i] = nstrings[mstr]
else
newbranch[i] = branch[i]
estr = estr.."value in branchstrings is nil, keeping string from branch    "
end
mstr=mstr+1
table.insert(newbstrings, newbranch[i])
elseif string.find(branch[i]:lower(), "*keyword*") then
newbranch[i] = keyword
table.insert(newbstrings, newbranch[i])
elseif string.find(branch[i]:lower(), "*value*") then
newbranch[i] = value
table.insert(newbstrings, newbranch[i])
elseif string.find(branch[i]:lower(), "*index*") then
newbranch[i] = index
table.insert(newbstrings, newbranch[i])
elseif string.find(branch[i]:lower(), "*validx*") then
newbranch[i] = "*"..tostring(vindex).."*"..value
table.insert(newbstrings, newbranch[i])
else
newbranch[i] = branch[i]
end
end
--clear blank error
if estr:len() == 0 then estr = nil end
return newbranch, newbstrings, estr
end
--branch printing func
print_branch = function (branch)
local nb = #branch
ba.print("Branch: ")
for i=1,nb do ba.print("'"..tostring(branch[i]).."', ") end
ba.print("\n")
end
--some branch strings may have a magic index as a prefix, a neumeric index encased between 2 "*" characters. this function looks for the prefix
--and removes it from the string. the function then returns the string without the prefix and the prefix converted to a number. if no prefix exists,
--returns keyword, its also safed in the event that the keyword is a number, allowing it to pass through the function unmodified
get_magic_idx = function(str)
--maybe see if the string contains a magic index
local s,e
if type(str) == "string" then
s,e = string.find(str, "*%w+*")
end
--store the value
if s and e then
--both index and name theese
return string.sub(str,e+1), tonumber(string.sub(str,s+1,e-1))
else
return str
end
end
--try to convert a string to a number, if it can it return the number, otherwise return the original string
maybe_tonumber = function(mabstr)
local mabnum = tonumber(mabstr)
if mabnum then
return mabnum
else
return mabstr
end
end
--traverse an infotable along branch, creating tables along the way, and append a keyword and value at the end
infotable_insert = function(branch, infotable, keyword, value)
--make sure branch is a table, if not return infotable
if type(branch) ~= "table" then return infotable, "branch is not valid" end
--bail if theres no keys in the list, if not return the table
if blen == 0 then return infotable, "branch is empty" end
--traverse tables
local n = infotable
local blen = #branch
for i = 1, blen do
local tab = {}
--check to see if this branch string has a magic index
local bst,idx = get_magic_idx(branch[i])
--if it exists, index it
if idx and n[idx] == nil then
n[idx] = tab
n[idx]["index_name"] = bst
n[idx]["index_number"] = idx
end
--initialize tables along the path if need be
if n[bst] == nil then n[bst] = tab end
--dont try to traverse over anyhing thats not a table
if type(n[bst]) ~= "table" then return infotable, "cannot traverse over value" end
--scope to the new level
n = n[bst]
end
n[keyword] = value
--return the infotable nil error and the key or table at the end of the branch
return infotable
end
--recursively prints an infotable
print_infotable = function(t, p)
if not t then return end
if p == nil then p = 0 end
for k,v in pairs(t) do
if type(v) == "table" then
ba.print(string.rep(" ", p)..tostring(k)..": \n")
print_infotable(v, p+1)
else
ba.print(string.rep(" ", p)..tostring(k)..": "..tostring(v).."\n")
end
end
end
--takes a tablefile, its path, and a table containing datatypes, heirarchy, and defaults
parse_table = function(name, path, datatypes, hierarchy)
--load a file
local flines = load_file(name, path)
--actual info will go here
local infotable = {}
--check to see if file loaded
if flines then
ba.print("Parsing file: "..path..name.."\n")
--bool indicating parsing within a section
local section = nil
--cache that stores replacements for placeholders in the herarchy
local branchstrings = {}
--last index used at each level of heirarchy
local lastidx = {}
--this one is for the validx index at each level of heirarchy
local lastvidx = {}
--keep a copy of last keyword to passed at each level of heirarchy
local lastkeyword = {}
--iterate through the lines
for i=1, #flines do
--read the line
local key_word, tag_data = read_line(flines[i])
--was a keyword found on this line
if key_word and key_word ~= "#end" then
--print some ****
--[[ba.print("Parsing: "..key_word)
if tag_data then
ba.print(" "..tag_data.."\n")
else
ba.print("\n")
end]]--
--get info about this keyword
local key = get_key(key_word)
local datatype = datatypes[key_word]
local data = parse_line(tag_data, datatype)
local branch = hierarchy[key_word]
if branch == nil or #branch == 0 then
ba.print("Parsing error, line "..i..": Heirarchy branch for keyword: "..key_word.." cannot be read. Storing keyword-value pair to root\n")
branch = {}
end
local blen = #branch
--errors message maybe goes here
local err
--resolved branch goes here
local rbranch
if datatype == "node" then
--autoindex if neccisary
if lastidx[blen] == nil then lastidx[blen] = 1 end
if lastvidx[blen] == nil then lastvidx[blen] = 1 end
if branch[blen] == "*index*" then
lastidx[blen] = lastidx[blen] + 1
elseif branch[blen] == "*validx*" then
lastvidx[blen] = lastvidx[blen] + 1
end
if lastkeyword[blen] ~= key_word then
lastidx[blen] = 1
lastvidx[blen] = 1
end
rbranch, branchstrings, err = resolve_branch(branch, branchstrings, key, data, lastidx[blen], lastvidx[blen])
if err then ba.print("Parsing error, line "..i..": "..err..".\n") end
lastkeyword[blen] = key_word
--data is pretty much self explanitory. these are the actual leaves on our branches. the data has already
--been parsed so all this does is make sure its filed under their respective nodes, sections, and sometimes root
else
--print_branch(branch)
rbranch, branchstrings, err = resolve_branch(branch, branchstrings, key, data, lastidx[blen], lastvidx[blen])
--print_branch(rbranch)
infotable,err = infotable_insert(rbranch,infotable,key,data)
if err then ba.print("Parsing error, line "..i..": "..err..".\n") end
end
elseif key_word and key_word == "#end" then
branchstrings = {} --reset branchstrings after section
lastidx = {}
lastvidx = {}
lastkeyword = {}
end
end
else
ba.print("Parsing Failed.\n")
return nil
end
ba.print("Parsing complete!\n")
return infotable
end
--[[================================================]]--
--[[======= end of virtual table file parser =======]]--
--[[================================================]]--


--[[    ^           ^           ^
       ^|^         ^|^         ^|^
      ^ | ^       ^ | ^       ^ | ^
     ^  |  ^     ^  |  ^     ^  |  ^
        |           |           |
do not **** with that ****. it hurts, alot, if you want to parse your own tables you need to create 2 lua tables, one containing the datatype
of every keyword. and one which defines the desired heirarchy. then call parse_table(filename, filepath, datatypestable, heirarchytable) it returns
an infotable that contains all the parsed data. the system is pretty versitile once you figure out how it ****ing works. but it lets you set up
a table parser faster with fewer lines of code than the version from the atmospheric flight script and i think its a little faster too.

everything in the heirarchy and datatype tables need to be in lower case, however keyword capitaization in tbl files is not case sensitive.
keywords need to ether start with "$" or "+" or "@" and end with ":", or start with "#" with no terminating character. this allows you to simulate
sections. you should place the string "#end" after sections. its not enforced and you can probly not use them but its not tested nor recommended.
"#end" is a special keyword and shouldnt be place in either the datatype or heirarchy tables. it doesnt actually get parsed, all it does is reset
the heirarchy which can cause problems if not used properly. sections are also not manditory. you use any other characters in the keyword body,
but mainly stick to characters, numbers, underscores, and spaces. keywords may not contain the strings "index_name" or "index_number" (see below)
or the "*" character. keywords must also use one of the folowing datatypes. if the type you need is not there, you may add it by editing the
parse_line() function.

datatypes define how the data following the keyword is interpreted by the parser. they are:
 "node" - acts as a container and also changes heirarchy (see below)
 "name" - parses the data as a string without modifiying it
 "word" - parses data as a lower case string
 "string" - parses quote encased string
 "number" - parses data as a number
 "angle" - parses data as an angle, in degrees, and stores it inernally in radians
 "boolean" - parses data as a boolean, true,yes,or on parse as true, anyting else as false
 "vector" - parses 3 numbers as a freespace vector, which can be comma or space seporated and optionaly encased in parentheses
 "euler" - parses 3 numbers as a freespace vector, but assumes them to be euler angles (in degrees), stores internally in radians
 "pbh" - parses 3 numbers (in degrees) as a freespace orientation.
 "matrix" - parses 9 numbers as a freespace orientation matrix, in row-columb order from top left to bottom right
 "number_list" - parses any number of numbers as a lua table, can be seporated by commas, spaces or parentheses
 "string_list" - parse any number of strings as a lua table. strings must be placed in "" and seprated by spaces commas or parentheses
 "mixed_list - parse any combination of strings and numbers as a list, same rules apply as the previous two lists
]]--
cockpit_datatypes = {}
cockpit_datatypes["#shipinfo"] = "node"
cockpit_datatypes["$name:"] = "node"
cockpit_datatypes["$view:"] = "node"
cockpit_datatypes["+position:"] = "vector"
cockpit_datatypes["+direction:"] = "vector"
cockpit_datatypes["+fov:"] = "angle"
cockpit_datatypes["+adjustable:"] = "boolean"
cockpit_datatypes["+turret:"] = "name"
cockpit_datatypes["$turret:"] = "node"
cockpit_datatypes["+mount:"] = "word"
cockpit_datatypes["+yaw_speed"] = "number"
cockpit_datatypes["+pitch_speed"] = "number"
cockpit_datatypes["+view:"] = "name"
cockpit_datatypes["+lookaim:"] = "boolean"
cockpit_datatypes["+gatling_subsystem:"] = "name"
cockpit_datatypes["+num_barrels:"] = "number"
cockpit_datatypes["+max_speed:"] = "number"
cockpit_datatypes["+acceleration:"] = "number"
cockpit_datatypes["$panel:"] = "node"
cockpit_datatypes["+texture_name:"] = "name"
cockpit_datatypes["+texture_index:"] = "number"
cockpit_datatypes["$animation:"] = "node"
cockpit_datatypes["+subobject:"] = "name"
cockpit_datatypes["+linked_to:"] = "string_list"
cockpit_datatypes["+range:"] = "euler"
cockpit_datatypes["+offset:"] = "euler"
cockpit_datatypes["+smoothing:"] = "number"
--[[
each keyword in the heirarchy table is associated with a branch, in essence a path through the infotable where the data for that keyword is stored.
each strng in the branch represents a level of heirarchy in the infotable. data is stored in nested lua tables. so a branch with 3 strings, read
right to left, is a table within a table within the infotable. so a keyword with the branch of {"secondaries", "missiles", "nukes"} would store
its data under 'infotable.secondaries.missiles.nukes'. the strings in a branch may either be explicit, in that they are user defined and do not
get modified by the parser, or they may be magic. magic strings are swapped out by data generated at parse time. what data depends on the magic
string used.
 
some magic words are replaced by data read from the tbl file at parse time. theese magic words are only compatable with node types. they should
also only be used as the last string in a branch. they are:
  "*value*" - the parsed value in current line
  "*keyword*" - the keyword from the crrent line (with special characters removed), use it when you dont have a value attached
  "*index*" - indicates to the parser to auto-index the value, starting at 1 and going up each time the same branch is used, you can not
  nest them. nor can any node types pop up during indexing.
  "*validx*" - this tells the parser to copy the table reference to both a named location and an auto-indexed location if used two new keys,
  called "index_name", and "index_number" will be added to that table containing the name of the non-indexed table
 
this one may be used on any datatype:
  "*bstrings*" - keep reading

when a keyword has the type node, the parser uses its branch to generate an array of strings and/or indexes which replace "*bstrings*". each
time a new instance of "*bstrings*" is encountered in a branch it is replaced by the string at the next index in the array. when a node is
eveluated the array is replaced with a new one, using the data associated with the magic strings in the nodes branch, including "*bstrings*"
which get recycled back into the new array. this allows the parser to create heirarchy in pretty much any way the user wants.

some other considerations about this system:
 -order of tags are less important than standard fs2 tables, so long as those tags are siblings, if they are at different levels of
  heirarchy or are related to different nodes, **** it. better yet keep them n order so you dont bug me when you **** up
 -most data types dont need seporators (commas, patentheses, double quotes) things like vectors for example can be seporated by
  commas spaces and parentheses, the exceptions are *_list, which require quotes
 -theres not a whole lot of idiot proofing right now. while this table parser is less pickey than the games, it still can be borked
  either by poor heirarchy/datatype definitions, or by modder error.
 -this system ues no defaults in order to save memory (memory usage is literally huge with large lua table structures), so assume
  everything is nil when esigning code to use this. its a good idea to ifthen any peice of info before using it (use an else
  statement to substitude data)
 -the parse line function contains all the datatype conversions, its pretty straight forward to add new datatypes
 
]]--
cockpit_heirarchy = {}
cockpit_heirarchy["#shipinfo"] = {"*keyword*"}
cockpit_heirarchy["$name:"] = {"*bstrings*","*validx*"}
cockpit_heirarchy["$view:"] = {"*bstrings*","*bstrings*","views","*validx*"}
cockpit_heirarchy["+position:"] = {"*bstrings*","*bstrings*","views","*bstrings*"}
cockpit_heirarchy["+direction:"] = {"*bstrings*","*bstrings*","views","*bstrings*"}
cockpit_heirarchy["+fov:"] = {"*bstrings*","*bstrings*","views","*bstrings*"}
cockpit_heirarchy["+adjustable:"] = {"*bstrings*","*bstrings*","views","*bstrings*"}
cockpit_heirarchy["+turret:"] = {"*bstrings*","*bstrings*","views","*bstrings*"}
cockpit_heirarchy["$turret:"] = {"*bstrings*","*bstrings*","turrets","*validx*"}
cockpit_heirarchy["+mount:"] = {"*bstrings*","*bstrings*","turrets","*bstrings*"}
cockpit_heirarchy["+yaw_speed:"] = {"*bstrings*","*bstrings*","turrets","*bstrings*"}
cockpit_heirarchy["+pitch_speed:"] = {"*bstrings*","*bstrings*","turrets","*bstrings*"}
cockpit_heirarchy["+view:"] = {"*bstrings*","*bstrings*","turrets","*bstrings*"}
cockpit_heirarchy["+lookaim:"] = {"*bstrings*","*bstrings*","turrets","*bstrings*"}
cockpit_heirarchy["+gatling_subsystem:"] = {"*bstrings*","*bstrings*","turrets","*bstrings*"}
cockpit_heirarchy["+num_barrels:"] = {"*bstrings*","*bstrings*","turrets","*bstrings*"}
cockpit_heirarchy["+max_speed:"] = {"*bstrings*","*bstrings*","turrets","*bstrings*"}
cockpit_heirarchy["+acceleration:"] = {"*bstrings*","*bstrings*","turrets","*bstrings*"}
cockpit_heirarchy["$panel:"] = {"*bstrings*","*bstrings*","panels","*index*"}
cockpit_heirarchy["+texture_name:"] = {"*bstrings*","*bstrings*","panels","*bstrings*"}
cockpit_heirarchy["+texture_index:"] = {"*bstrings*","*bstrings*","panels","*bstrings*"}
cockpit_heirarchy["$animation:"] = {"*bstrings*","*bstrings*","animations","*index*"}
cockpit_heirarchy["+subobject:"] = {"*bstrings*","*bstrings*","animations","*bstrings*"}
cockpit_heirarchy["+linked_to:"] = {"*bstrings*","*bstrings*","animations","*bstrings*"}
cockpit_heirarchy["+range:"] = {"*bstrings*","*bstrings*","animations","*bstrings*"}
cockpit_heirarchy["+offset:"] = {"*bstrings*","*bstrings*","animations","*bstrings*"}
cockpit_heirarchy["+smoothing:"] = {"*bstrings*","*bstrings*","animations","*bstrings*"}

--parse it with this function!
--takes a string containing the file name, a string containing the file path, the datatypes table, and the heirarchy table                 
cockpits_tbl = parse_table("cockpits.tbl", "data/tables/", cockpit_datatypes, cockpit_heirarchy)
--use this function with a debug build (prints to debug console/fslog) to check that your data is parsed correctly
--print_infotable(cockpits_tbl)

^note the last two tables and the comments at the end, which explain how the whole system works, i advise running it in the global gameinit hook

heres a sample table (actually its from the cockpit demo) that i know works.

Code: [Select]
#Shipinfo

$Name: PB Sepulture
$View: Pilot ;display name of camera
+Position: -4.498, 0.45, 10.35 ;position of camera (modelspace)
+Direction: 0,0,1 ;direction it points (modelspace)
+Fov: 42.972 ;field of view (degrees)
+Adjustable: YES ;use adjustable view view info (track ir, etc)
$View: Co-Pilot
+Position: -3.52,0.45,10.35
+Direction: 0,0,1
+Fov: 42.972
+Adjustable: YES
$View: Rear View Cam
+Position: 0.000000,-0.003325,-2.491289
+Direction: 0,0,-1
+Fov: 40
+Adjustable: NO
$View: Gun Scope 1
+Position: 4.000000,1.114944,8.799005
+Direction: -0.006000,0.000000,0.999982
+Fov: 10
+Adjustable: NO
$View: Gun Scope 2
+Position: -16.642000,-4.128261,0.130365
+Direction: 0.011999,0.000000,0.999928
+Fov: 10
+Adjustable: NO
$View: Gun Scope 3
+Position: 16.642000,-4.128261,0.130365
+Direction: -0.011999,0.000000,0.999928
+Fov: 10
+Adjustable: NO
$View: Turret Scope
+Position: -0.000000,-1.272048,-1.190684
+Direction: 0,-1,0 ;in turret context the vector of the turrets starting direction
+Fov: 30
+Adjustable: NO
+Turret: gunturret-0 ;name of turret that view is bound to
$Turret: gunturret-0 ;subsystem name, must match values with ships.tbl
+Mount: V ;mounting: (Dorsal, (Ventral, (Forward, (Aft (Port aka Left, or (Starbord aka right
+Yaw_Speed: 2 ;speed of turret
+Pitch_Speed: 2
+View: Turret Scope ;name of view that is bound to this turret
+Lookaim: NO ;aim with view controls (freelancer style turret control
$Panel: ;panel def, list of properties for each rtt panel in a ship, supports up to 4
+Texture_Name: panel2 ;name of texture to be replaced by a rtt textures
+Texture_Index: 4 ;index of texture to be replaced by rtt texture
$Panel:
+Texture_Name: panel1
+Texture_Index: 6
$Panel:
+Texture_Name: panel3
+Texture_Index: 4
$Animation: ;animation definition, you may have lots of these
+Subobject: stick_pitch ;name of the suobject which is animated
+Linked_To: "js_pitch" "none" "none" ;3 strings which defne the linked variables, valid: "none" "js_pitch" "js_heading" "js_throttle" "js_bank" "js_lateral" "js_vertical"
+Range: 1.7, 0, 0 ;how many degrees does it rotate about center (pitch bank heading)
+Offset: 0, 0, 0 ;angular offset in degrees (pitch bank heading)
+Smoothing: 2 ;the amount of smoothing that is used
$Animation:
+Subobject: pilot_stick
+Linked_To: "none" "js_bank" "none"
+Range: 0, -80, 0
+Offset: 0, 0, 0
+Smoothing: 2
$Animation:
+Subobject: copilot_stick
+Linked_To: "none" "js_bank" "none"
+Range: 0, -80, 0
+Offset: 0, 0, 0
+Smoothing: 2
$Animation:
+Subobject: throttle
+Linked_To: "js_throttle" "none" "none"
+Range: 68.75, 0, 0
+Offset: -65, 0, 0
+Smoothing: 2

$Name: PF Satyr
$View: Pilot
+Position: 0,0.9,9.9
+Direction: 0,0,1
+Fov: 42.972
+Adjustable: YES
$View: Tail Gunner
+Position: 0,2.7,0.68
+Direction: 0,0,-1
+Fov: 42.972
+Adjustable: YES
$View: Rear View Cam
+Position: 0.0,2.39607,-5.445775
+Direction: 0,0,-1
+Fov: 40
+Adjustable: NO
$View: Gun Scope
+Position: -0.000335,-1.560670,11.904683
+Direction: 0,0,1
+Fov: 10
+Adjustable: NO
$View: Turret Scope
+Position: -0.000334,0.879042,0.356610
+Direction: 0,1,0
+Fov: 30
+Adjustable: NO
+Turret: gunturret-0
$Turret: gunturret-0
+Mount: D
+Yaw_Speed: 1
+Pitch_Speed: 1
+View: Turret Scope
+Lookaim: NO
$Panel:
+Texture_Name: panel3
+Texture_Index: 4
$Panel:
+Texture_Name: panel1
+Texture_Index: 2
$Panel:
+Texture_Name: panel2
+Texture_Index: 3
$Animation:
+Subobject: pilot_stick
+Linked_To: "js_pitch" "js_heading" "none"
+Range: 10, 10, 0
+Offset: 0, 0, 0
+Smoothing: 1
$Animation:
+Subobject: uv_stick
+Linked_To: "js_lateral" "js_vertical" "none"
+Range: 20, 20, 0
+Offset: 0, 0, 0
+Smoothing: 1
$Animation:
+Subobject: turret_stick
+Linked_To: "ms_x" "ms_y" "none"
+Range: 20, 20, 0
+Offset: 0, 0, 0
+Smoothing: 1
$Animation:
+Subobject: throttle
+Linked_To: "js_throttle" "none" "none"
+Range: 74, 0, 0
+Offset: -13, 0, 0
+Smoothing: 1

$Name: PF Satyr#gt
$View: Pilot
+Position: 0,0.9,9.9
+Direction: 0,0,1
+Fov: 42.972
+Adjustable: YES
$View: Tail Gunner
+Position: 0,2.7,0.68
+Direction: 0,0,-1
+Fov: 42.972
+Adjustable: YES
$View: Rear View Cam
+Position: 0.0,2.39607,-5.445775
+Direction: 0,0,-1
+Fov: 40
+Adjustable: NO
$View: Gun Scope
+Position: -0.000335,-1.560670,11.904683
+Direction: 0,0,1
+Fov: 10
+Adjustable: NO
$View: Turret Scope
+Position: 0.000000,3.676328,-2.215622
+Direction: 0,1,0
+Fov: 30
+Adjustable: NO
+Turret: gunturret-0
$Turret: gunturret-0
+Mount: D
+Yaw_Speed: 0.8
+Pitch_Speed: 0.8
+View: Turret Scope
+Lookaim: NO
+Gatling_Subsystem: gatling
+Num_Barrels: 6 ;number of barrels
+Max_Speed: 5 ;max speed of gun rotation (rotatons per second)
+Acceleration: 1 ;time to accelerate to max speed (seconds)
$Panel:
+Texture_Name: panel3
+Texture_Index: 4
$Panel:
+Texture_Name: panel1
+Texture_Index: 2
$Panel:
+Texture_Name: panel2
+Texture_Index: 3
$Animation:
+Subobject: pilot_stick
+Linked_To: "js_pitch" "js_heading" "none"
+Range: 10, 10, 0
+Offset: 0, 0, 0
+Smoothing: 1
$Animation:
+Subobject: uv_stick
+Linked_To: "js_vertical" "js_lateral" "none"
+Range: 20, 20, 0
+Offset: 0, 0, 0
+Smoothing: 1
$Animation:
+Subobject: turret_stick
+Linked_To: "msy" "msx" "none"
+Range: 20, 20, 0
+Offset: 0, 0, 0
+Smoothing: 1
$Animation:
+Subobject: throttle
+Linked_To: "js_throttle" "none" "none"
+Range: 74, 0, 0
+Offset: -13, 0, 0
+Smoothing: 1

#end

I can no longer sit back and allow communist infiltration, communist indoctrination, communist subversion, and the international communist conspiracy to sap and impurify all of our precious bodily fluids.

Nuke's Scripting SVN

 

Offline Wanderer

  • Wiki Warrior
  • 211
  • Mostly harmless
Very nice piece of work
Do not meddle in the affairs of coders for they are soggy and hard to light

 

Offline Nuke

  • Ka-Boom!
  • 212
  • Mutants Worship Me
while attempting to update the file parser in the atmospheric flight script, i found some serious bugs and dirty hacks in the lua table parser that were preventing the tables from being read correctly. anyway the bugfixes will allow better use of index nodes, which were problematic before and seldom worked correctly. there was also a scope alignment issue where if you had parsed two indexed sets at the same level of scope, the indexes from one set would start counting up where the previous one left off. nesting them should also work now. of course fixing that introduced other bugs and led to a partial rewrite of some major functions, such as an issue with value+index nodes not asssigning numbers to datasets if they omitted subsections. i think i also added new datatypes too.

maybe a rewrite of terminology and nomenclature as well as improved documentation is in order, because i didnt know what to name things and just assigned them names that seemed ok and ended up being confusing as hell (which is why debugging the damn thing has taken 3 days). maybe later

anyway heres the updated parser, it should still parse the table supplied above, however i commented out the two tables that describe the structure, so that i could run it from dofile and call the function externally, making this file more portable between scripts.

Code: [Select]
--[[=========================================]]--
--[[======= virtual table file parser =======]]--
--[[=========================================]]--
--[[======== do not edit (see below) ========]]--
--[[=========================================]]--
--load a file from path, returns a table of lines
function load_file(fname, fpath)
--dont read a file thats not there
if cf.fileExists(fname, fpath, false) then
--open the file
local file = cf.openFile(fname, "r", fpath)
--read the first line
local lines = {}
local idx = 1
line = file:read("*l")
while line ~= nil do
lines[idx] = line
idx = idx + 1
line = file:read("*l")
end
file:close()
ba.print("File '" .. fname .."' loaded, "..#lines.." lines found\n")
return lines
else
ba.warning("File '" .. fname .."' not found")
return nil
end
end
--cleans up a line of text for parsing, removes comments and whitespace
function clean_line(line)
--strip out comments
line = line:gsub(";.*" ,"")
--remove leading white space
line = line:gsub("%s*(.+)","%1")
--remove trailing white space
line = line:reverse()
line = line:gsub("%s*(.+)","%1")
line = line:reverse()
return line
end
--takes a keyword and removes all special markers
function get_key(keyword)
local key = keyword:gsub("%p*(.+)" ,"%1")
key = key:reverse()
key = key:gsub("%p*(.+)" ,"%1")
key = key:reverse()
return key
end
--read a line of text, takes out white space and comments, returns the keyword string and data string
--if there is no data string following the keyword, then the text portion of the keyword is returned
function read_line(pline)
--bail if nothing read
if pline == nil or pline:len() == 0 then return nil end
pline = clean_line(pline)
--find out where the start and end of the $keyword: are
local si,ei = pline:find("%$.+%:")
--see if it found it
if si == nil or ei == nil then
--nope, try other +keyword: varients
si,ei = pline:find("%+.+%:")
--now did it work
if si == nil or ei == nil then
--nope, try other @keyword: varients
si,ei = pline:find("%@.+%:")
--this is getting old
if si == nil or ei == nil then
--nope, is it a section #keyword
si,ei = pline:find("%#.+")
--did it ****ing work yet
if si == nil or ei == nil then
--its nothing, eject and stop wasting time on it
return nil
end
end
end
end
--get the keyword from the line and convert it to lower case
local kword = pline:sub(si,ei):lower()
--get the rest of the line
local dline = pline:sub(ei+1)
--remove leading white space, again
dline = dline:gsub("%s*(.+)","%1")
--anything left
if dline:len() > 0 then
return kword, dline
else
--no text after keyword, no second retur
return kword
end
end
--convert the string data into whatever format it needs to go in, assumes leading and trailing whirespace is removed
--returns type converted data,
function parse_line(dline, dtype)
--check if our line exists and is long enough to do anything with
if not dline or dline:len() < 1 then return nil end
--make our dtype lowercase
if dtype and type(dtype) == "string" then dtype = dtype:lower() end
--default return value, returns unmodified data string, useful for names
if dtype == "name" then
return dline
elseif dtype == "word" then --like name but converts to lower case
return dline:lower()
elseif dtype == "number" then --number values
return tonumber(dline)
elseif dtype == "angle" then --angle values in degrees (converts to radians)
return math.rad(tonumber(dline))
elseif dtype == "string" then --returns any text placed between quotes "", can include most special characters
return dline:gsub("%s*\"%s*" , "")
elseif dtype == "boolean" then --accepts true or false, on or off, yes or no, anything else is false
--strip spaces
dline = dline:gsub("%s", "")
--make it lowercase
dline = dline:lower()
if dline == "true" or dline == "on" or dline == "yes" then return true else return false end
elseif dtype == "vector" then --returns a vector (seporated by spaces or commas)
local X,Y,Z
--strip parentheses and commas (theyre optional)
dline = dline:gsub("%(*%)*%,+"," ")
--iterate through and store
for c in dline:gmatch("%S+") do
if X == nil then
X = tonumber(c)
elseif Y == nil then
Y = tonumber(c)
elseif Z == nil then
Z = tonumber(c)
break
end
end
return ba.createVector(X,Y,Z)
elseif dtype == "euler" then --pitch bank and heading, in degrees (converts to radians), returns as vector
local P, B, H
--strip commas and parentheses (theyre optional)
dline = dline:gsub("%(*%)*%,+"," ")
--iterate through and store
for c in dline:gmatch("%S+") do
if P == nil then
P = tonumber(c)
elseif B == nil then
B = tonumber(c)
elseif H == nil then
H = tonumber(c)
break
end
end
P,B,H = math.rad(P),math.rad(B),math.rad(H)
return ba.createVector(P,B,H)
elseif dtype == "pbh" then --pitch bank and heading, in degrees (converts to radians), returns as orientation
local P, B, H
--strip commas and parentheses (theyre optional)
dline = dline:gsub("%(*%)*%,+"," ")
--iterate through and store
for c in dline:gmatch("%S+") do
if P == nil then
P = tonumber(c)
elseif B == nil then
B = tonumber(c)
elseif H == nil then
H = tonumber(c)
break
end
end
P,B,H = math.rad(P),math.rad(B),math.rad(H)
return ba.createOrinetation(P,B,H)
elseif dtype == "matrix" then --parse an entire 3x3 matrix
local m = ba.createOrinetation()
local i = 1
--strip commas and parentheses (theyre optional)
dline = dline:gsub("%(*%)*%,+"," ")
for c in dline:gmatch("%S+") do
m[i] = tonumber(c)
if i == 9 then break end
i=i+1
end
return m
elseif dtype == "number_list" then
local t = {}
local i = 1
--strip commas and parentheses (theyre optional)
dline = dline:gsub("%(*%)*%,+"," ")
for c in string.gmatch(dline, "([%d-+e.]+)") do
t[i] = tonumber(c)
i=i+1
end
return t
elseif dtype == "string_list" then
local t = {}
local i = 1
--strip commas and parentheses (theyre optional)
dline = dline:gsub("%(*%)*%,+"," ")
for c in string.gmatch(dline, '%"([%w_ ]+)%"') do
t[i] = c
i=i+1
end
return t
elseif dtype == "mixed_list" then
local t = {}
local i = 1
--strip commas and parentheses (theyre optional)
dline = dline:gsub("%(*%)*%,+"," ")
for c in string.gmatch(dline, '%"([%w_ .+-]+)%"') do
t[i] = maybe_tonumber(c)
i=i+1
end
return t
else --oh ****, nothing i thought of happened
return dline --default return
end
end
--replace magic strings in a branch with string generated by the parser, returns a new branch of the same length as branch, a table containing
--all used magic strings, and an error string if something fails. will not return nil.
function resolve_branch(branch, nstrings, keyword, value, index)
--copy table
local newbranch = {}
--some vars
local branchlen = #branch
local mstr = 1
local estr = ""
local newbstrings = {} --lulz
--swap out stuff
for i = 1, branchlen do
if string.find(branch[i]:lower(), "*bstrings*") then
if nstrings[mstr] then
newbranch[i] = nstrings[mstr]
else
newbranch[i] = branch[i]
estr = estr.."value in branchstrings is nil, keeping string from branch    "
end
mstr=mstr+1
table.insert(newbstrings, newbranch[i])
elseif string.find(branch[i]:lower(), "*keyword*") then
newbranch[i] = keyword
table.insert(newbstrings, newbranch[i])
elseif string.find(branch[i]:lower(), "*value*") then
newbranch[i] = value
table.insert(newbstrings, newbranch[i])
elseif string.find(branch[i]:lower(), "*index*") then
newbranch[i] = index
table.insert(newbstrings, newbranch[i])
elseif string.find(branch[i]:lower(), "*validx*") then
newbranch[i] = "*"..tostring(index).."*"..value
table.insert(newbstrings, newbranch[i])
else
newbranch[i] = branch[i]
end
end
--clear blank error
if estr:len() == 0 then estr = nil end
return newbranch, newbstrings, estr
end
--branch printing func
function print_branch(branch)
local nb = #branch
ba.print("Branch: ")
for i=1,nb do ba.print("'"..tostring(branch[i]).."', ") end
ba.print("\n")
end
--some branch strings may have a magic index as a prefix, a neumeric index encased between 2 "*" characters. this function looks for the prefix
--and removes it from the string. the function then returns the string without the prefix and the prefix converted to a number. if no prefix exists,
--returns keyword, its also safed in the event that the keyword is a number, allowing it to pass through the function unmodified
function get_magic_idx(str)
--maybe see if the string contains a magic index
local s,e
if type(str) == "string" then
s,e = string.find(str, "*%w+*")
end
--store the value
if s and e then
--both index and name theese
return string.sub(str,e+1), tonumber(string.sub(str,s+1,e-1))
else
return str
end
end
--try to convert a string to a number, if it can it return the number, otherwise return the original string
function maybe_tonumber(mabstr)
local mabnum = tonumber(mabstr)
if mabnum then
return mabnum
else
return mabstr
end
end
--traverse an infotable along branch, creating tables along the way, and append a keyword and value at the end
function infotable_insert(branch, infotable, keyword, value)
--make sure branch is a table, if not return infotable
if type(branch) ~= "table" then return infotable, "branch is not valid" end
--bail if theres no keys in the list, if not return the table
if blen == 0 then return infotable, "branch is empty" end
--traverse tables
local n = infotable
local blen = #branch
for i = 1, blen do
local tab = {}
--check to see if this branch string has a magic index
local bst,idx = get_magic_idx(branch[i])
--if it exists, index it
if idx and n[idx] == nil then
n[idx] = tab
n[idx]["index_name"] = bst
n[idx]["index_number"] = idx
end
--initialize tables along the path if need be
if n[bst] == nil then n[bst] = tab end
--dont try to traverse over anyhing thats not a table
if type(n[bst]) ~= "table" then return infotable, "cannot traverse over value" end
--scope to the new level
n = n[bst]
end
n[keyword] = value
--return the infotable nil error and the key or table at the end of the branch
return infotable
end
--recursively prints an infotable
function print_infotable(t, p)
if not t then return end
if p == nil then p = 0 end
for k,v in pairs(t) do
if type(v) == "table" then
ba.print(string.rep(" ", p)..tostring(k)..": \n")
print_infotable(v, p+1)
else
ba.print(string.rep(" ", p)..tostring(k)..": "..tostring(v).."\n")
end
end
end
--takes a tablefile, its path, and a table containing datatypes, heirarchy, and defaults
function parse_table(name, path, datatypes, hierarchy)
--load a file
local flines = load_file(name, path)
--actual info will go here
local infotable = {}
--check to see if file loaded
if flines then
ba.print("Parsing file: "..path..name.."\n")
--cache that stores replacements for placeholders in the herarchy
local branchstrings = {}
--last index used at each level of heirarchy
local lastidx = {}
--scope variable to keep track of lessening changes int the branch size
local lastbranchlen = 0
--the name of the last node to be parsed, used to reset indexes between subsets
local lastnode = ""
--iterate through the lines
for i=1, #flines do
--read the line
local key_word, tag_data = read_line(flines[i])
--was a keyword found on this line
if key_word and key_word ~= "#end" then
--print some ****
--[[ba.print("Parsing: "..key_word)
if tag_data then
ba.print(" "..tag_data.."\n")
else
ba.print("\n")
end]]--
--get info about this keyword
local key = get_key(key_word)
local datatype = datatypes[key_word]
local data = parse_line(tag_data, datatype)
local branch = hierarchy[key_word]
if branch == nil or #branch == 0 then
ba.print("Parsing error, line "..i..": Heirarchy branch for keyword: "..key_word.." cannot be read. Storing keyword-value pair to root\n")
branch = {}
end
local blen = #branch
--errors message maybe goes here
local err
--resolved branch goes here
local rbranch
if datatype == "node" then
--autoindex if neccisary
--clear out of scope indexes
if lastbranchlen > blen then
for j = blen + 1, lastbranchlen do
lastidx[j] = nil
end
end
--clear indexes if the node name has changed since last pass yet remains at the same scope
if lastnode ~= key and lastbranchlen == blen then
lastidx[blen] = nil
end
lastnode = key
--initialize or increment current index (even if its not used)
if branch[blen] == "*index*" or branch[blen] == "*validx*" then
if lastidx[blen] == nil then
lastidx[blen] = 1
else
lastidx[blen] = lastidx[blen] + 1
end
else
lastidx[blen] = 1
end
--resolve it
rbranch, branchstrings, err = resolve_branch(branch, branchstrings, key, data, lastidx[blen])
if err then ba.print("Parsing error, line "..i..": "..err..".\n") end
lastbranchlen = blen
--data is pretty much self explanitory. these are the actual leaves on our branches. the data has already
--been parsed so all this does is make sure its filed under their respective nodes, sections, and sometimes root
else
--print_branch(branch)
rbranch, branchstrings, err = resolve_branch(branch, branchstrings, key, data, lastidx[blen])
if err then ba.print("Parsing error, line "..i..": "..err..".\n") end
--print_branch(rbranch)
infotable,err = infotable_insert(rbranch,infotable,key,data)
if err then ba.print("Parsing error, line "..i..": "..err..".\n") end
end
elseif key_word and key_word == "#end" then
branchstrings = {} --reset branchstrings after section
lastidx = {}
end
end
else
ba.print("Parsing Failed.\n")
return nil
end
ba.print("Parsing complete!\n")
return infotable
end
--[[================================================]]--
--[[======= end of virtual table file parser =======]]--
--[[================================================]]--


--[[    ^           ^           ^
       ^|^         ^|^         ^|^
      ^ | ^       ^ | ^       ^ | ^
     ^  |  ^     ^  |  ^     ^  |  ^
        |           |           |
do not **** with that ****. it hurts, alot, if you want to parse your own tables you need to create 2 lua tables, one containing the datatype
of every keyword. and one which defines the desired heirarchy. then call parse_table(filename, filepath, datatypestable, heirarchytable) it returns
an infotable that contains all the parsed data. the system is pretty versitile once you figure out how it ****ing works. but it lets you set up
a table parser faster with fewer lines of code than the version from the atmospheric flight script and i think its a little faster too.

everything in the heirarchy and datatype tables need to be in lower case, however keyword capitaization in tbl files is not case sensitive.
keywords need to ether start with "$" or "+" or "@" and end with ":", or start with "#" with no terminating character. this allows you to simulate
sections. you should place the string "#end" after sections. its not enforced and you can probly not use them but its not tested nor recommended.
"#end" is a special keyword and shouldnt be place in either the datatype or heirarchy tables. it doesnt actually get parsed, all it does is reset
the heirarchy which can cause problems if not used properly. sections are also not manditory. you use any other characters in the keyword body,
but mainly stick to characters, numbers, underscores, and spaces. keywords may not contain the strings "index_name" or "index_number" (see below)
or the "*" character. keywords must also use one of the folowing datatypes. if the type you need is not there, you may add it by editing the
parse_line() function.

datatypes define how the data following the keyword is interpreted by the parser. they are:
 "node" - acts as a container and also changes heirarchy (see below)
 "name" - parses the data as a string without modifiying it
 "word" - parses data as a lower case string
 "string" - parses quote encased string
 "number" - parses data as a number
 "angle" - parses data as an angle, in degrees, and stores it inernally in radians
 "boolean" - parses data as a boolean, true,yes,or on parse as true, anyting else as false
 "vector" - parses 3 numbers as a freespace vector, which can be comma or space seporated and optionaly encased in parentheses
 "euler" - parses 3 numbers as a freespace vector, but assumes them to be euler angles (in degrees), stores internally in radians
 "pbh" - parses 3 numbers (in degrees) as a freespace orientation.
 "matrix" - parses 9 numbers as a freespace orientation matrix, in row-columb order from top left to bottom right
 "number_list" - parses any number of numbers as a lua table, can be seporated by commas, spaces or parentheses
 "string_list" - parse any number of strings as a lua table. strings must be placed in "" and seprated by spaces commas or parentheses
 "mixed_list - parse any combination of strings and numbers as a list, same rules apply as the previous two lists
]]--
--[[
cockpit_datatypes = {}
cockpit_datatypes["#shipinfo"] = "node"
cockpit_datatypes["$name:"] = "node"
cockpit_datatypes["$view:"] = "node"
cockpit_datatypes["+position:"] = "vector"
cockpit_datatypes["+direction:"] = "vector"
cockpit_datatypes["+fov:"] = "angle"
cockpit_datatypes["+adjustable:"] = "boolean"
cockpit_datatypes["+renders_to_screen:"] = "boolean"
cockpit_datatypes["+renders_to_texture:"] = "boolean"
cockpit_datatypes["+missile_cam:"] = "boolean"
cockpit_datatypes["+turret:"] = "name"
cockpit_datatypes["$turret:"] = "node"
cockpit_datatypes["+mount:"] = "word"
cockpit_datatypes["+yaw_speed"] = "number"
cockpit_datatypes["+pitch_speed"] = "number"
cockpit_datatypes["+view:"] = "name"
cockpit_datatypes["+lookaim:"] = "boolean"
cockpit_datatypes["+gatling_subsystem:"] = "name"
cockpit_datatypes["+gatling_subsystem2:"] = "name"
cockpit_datatypes["+num_barrels:"] = "number"
cockpit_datatypes["+max_speed:"] = "number"
cockpit_datatypes["+acceleration:"] = "number"
cockpit_datatypes["+ai_burst_time:"] = "number"
cockpit_datatypes["$panel:"] = "node"
cockpit_datatypes["+texture_name:"] = "name"
cockpit_datatypes["+texture_index:"] = "number"
cockpit_datatypes["$animation:"] = "node"
cockpit_datatypes["+subobject:"] = "name"
cockpit_datatypes["+linked_to:"] = "string_list"
cockpit_datatypes["+range:"] = "euler"
cockpit_datatypes["+offset:"] = "euler"
cockpit_datatypes["+smoothing:"] = "number"

cockpit_datatypes["#drone_info"] = "node"
cockpit_datatypes["$name:"] = "node"
cockpit_datatypes["$type:"] = "word"
cockpit_datatypes["$cam_pos:"] = "vector"
cockpit_datatypes["$cam_fov:"] = "angle"
cockpit_datatypes["$type:"] = "word"
cockpit_datatypes["$velocity_factor:"] = "number"
cockpit_datatypes["$acceleration:"] = "number"
cockpit_datatypes["$turn_rate:"] = "angle"
cockpit_datatypes["$soft_turn_cone:"] = "angle"
cockpit_datatypes["$weapon:"] = "name"
cockpit_datatypes["$ammo:"] = "number"
cockpit_datatypes["$fuel_factor:"] = "number"
cockpit_datatypes["$firing_point:"] = "node"
cockpit_datatypes["+pos:"] = "vector"

cockpit_datatypes["#input_bindings"] = "node"
cockpit_datatypes["$bind:"] = "node"
cockpit_datatypes["+key:"] = "string"
cockpit_datatypes["+command:"] = "string"
]]--
--[[
each keyword in the heirarchy table is associated with a branch, in essence a path through the infotable where the data for that keyword is stored.
each strng in the branch represents a level of heirarchy in the infotable. data is stored in nested lua tables. so a branch with 3 strings, read
right to left, is a table within a table within the infotable. so a keyword with the branch of {"secondaries", "missiles", "nukes"} would store
its data under 'infotable.secondaries.missiles.nukes'. the strings in a branch may either be explicit, in that they are user defined and do not
get modified by the parser, or they may be magic. magic strings are swapped out by data generated at parse time. what data depends on the magic
string used.
 
some magic words are replaced by data read from the tbl file at parse time. theese magic words are only compatable with node types. they should
also only be used as the last string in a branch. they are:
  "*value*" - the parsed value in current line
  "*keyword*" - the keyword from the crrent line (with special characters removed), use it when you dont have a value attached
  "*index*" - indicates to the parser to auto-index the value, starting at 1 and going up each time the same branch is used, you can not
 nest them. nor can any node types pop up during indexing.
  "*validx*" - this tells the parser to copy the table reference to both a named location and an auto-indexed location if used two new keys,
 called "index_name", and "index_number" will be added to that table containing the name of the non-indexed table
  
this one may be used on any datatype:
  "*bstrings*" - keep reading

when a keyword has the type node, the parser uses its branch to generate an array of strings and/or indexes which replace "*bstrings*". each
time a new instance of "*bstrings*" is encountered in a branch it is replaced by the string at the next index in the array. when a node is
eveluated the array is replaced with a new one, using the data associated with the magic strings in the nodes branch, including "*bstrings*"
which get recycled back into the new array. this allows the parser to create heirarchy in pretty much any way the user wants.

some other considerations about this system:
 -order of tags are less important than standard fs2 tables, so long as those tags are siblings, if they are at different levels of
  heirarchy or are related to different nodes, **** it. better yet keep them n order so you dont bug me when you **** up
 -most data types dont need seporators (commas, patentheses, double quotes) things like vectors for example can be seporated by
  commas spaces and parentheses, the exceptions are *_list, which require quotes
 -theres not a whole lot of idiot proofing right now. while this table parser is less pickey than the games, it still can be borked
  either by poor heirarchy/datatype definitions, or by modder error.
 -this system ues no defaults in order to save memory (memory usage is literally huge with large lua table structures), so assume
  everything is nil when esigning code to use this. its a good idea to ifthen any peice of info before using it (use an else
  statement to substitude data)
 -the parse line function contains all the datatype conversions, its pretty straight forward to add new datatypes
  
]]--
--[[
cockpit_heirarchy = {}
cockpit_heirarchy["#shipinfo"] = {"*keyword*"}
cockpit_heirarchy["$name:"] = {"*bstrings*","*validx*"}
cockpit_heirarchy["$view:"] = {"*bstrings*","*bstrings*","views","*validx*"}
cockpit_heirarchy["+position:"] = {"*bstrings*","*bstrings*","views","*bstrings*"}
cockpit_heirarchy["+direction:"] = {"*bstrings*","*bstrings*","views","*bstrings*"}
cockpit_heirarchy["+fov:"] = {"*bstrings*","*bstrings*","views","*bstrings*"}
cockpit_heirarchy["+adjustable:"] = {"*bstrings*","*bstrings*","views","*bstrings*"}
cockpit_heirarchy["+renders_to_screen:"] = {"*bstrings*","*bstrings*","views","*bstrings*"}
cockpit_heirarchy["+renders_to_texture:"] = {"*bstrings*","*bstrings*","views","*bstrings*"}
cockpit_heirarchy["+missile_cam:"] = {"*bstrings*","*bstrings*","views","*bstrings*"}
cockpit_heirarchy["+turret:"] = {"*bstrings*","*bstrings*","views","*bstrings*"}
cockpit_heirarchy["$turret:"] = {"*bstrings*","*bstrings*","turrets","*validx*"}
cockpit_heirarchy["+mount:"] = {"*bstrings*","*bstrings*","turrets","*bstrings*"}
cockpit_heirarchy["+yaw_speed:"] = {"*bstrings*","*bstrings*","turrets","*bstrings*"}
cockpit_heirarchy["+pitch_speed:"] = {"*bstrings*","*bstrings*","turrets","*bstrings*"}
cockpit_heirarchy["+view:"] = {"*bstrings*","*bstrings*","turrets","*bstrings*"}
cockpit_heirarchy["+lookaim:"] = {"*bstrings*","*bstrings*","turrets","*bstrings*"}
cockpit_heirarchy["+gatling_subsystem:"] = {"*bstrings*","*bstrings*","turrets","*bstrings*"}
cockpit_heirarchy["+gatling_subsystem2:"] = {"*bstrings*","*bstrings*","turrets","*bstrings*"}
cockpit_heirarchy["+num_barrels:"] = {"*bstrings*","*bstrings*","turrets","*bstrings*"}
cockpit_heirarchy["+max_speed:"] = {"*bstrings*","*bstrings*","turrets","*bstrings*"}
cockpit_heirarchy["+acceleration:"] = {"*bstrings*","*bstrings*","turrets","*bstrings*"}
cockpit_heirarchy["+ai_burst_time:"] = {"*bstrings*","*bstrings*","turrets","*bstrings*"}
cockpit_heirarchy["$panel:"] = {"*bstrings*","*bstrings*","panels","*index*"}
cockpit_heirarchy["+texture_name:"] = {"*bstrings*","*bstrings*","panels","*bstrings*"}
cockpit_heirarchy["+texture_index:"] = {"*bstrings*","*bstrings*","panels","*bstrings*"}
cockpit_heirarchy["$animation:"] = {"*bstrings*","*bstrings*","animations","*index*"}
cockpit_heirarchy["+subobject:"] = {"*bstrings*","*bstrings*","animations","*bstrings*"}
cockpit_heirarchy["+linked_to:"] = {"*bstrings*","*bstrings*","animations","*bstrings*"}
cockpit_heirarchy["+range:"] = {"*bstrings*","*bstrings*","animations","*bstrings*"}
cockpit_heirarchy["+offset:"] = {"*bstrings*","*bstrings*","animations","*bstrings*"}
cockpit_heirarchy["+smoothing:"] = {"*bstrings*","*bstrings*","animations","*bstrings*"}

cockpit_heirarchy["#drone_info"] = {"*keyword*"}
cockpit_heirarchy["$name:"] = {"*bstrings*","*validx*"}
cockpit_heirarchy["$type:"] = {"*bstrings*","*bstrings*"}
cockpit_heirarchy["$cam_pos:"] = {"*bstrings*","*bstrings*"}
cockpit_heirarchy["$cam_fov:"] = {"*bstrings*","*bstrings*"}
cockpit_heirarchy["$velocity_factor:"] = {"*bstrings*","*bstrings*"}
cockpit_heirarchy["$acceleration:"] = {"*bstrings*","*bstrings*"}
cockpit_heirarchy["$turn_rate:"] = {"*bstrings*","*bstrings*"}
cockpit_heirarchy["$soft_turn_cone:"] = {"*bstrings*","*bstrings*"}
cockpit_heirarchy["$weapon:"] = {"*bstrings*","*bstrings*"}
cockpit_heirarchy["$ammo:"] = {"*bstrings*","*bstrings*"}
cockpit_heirarchy["$fuel_factor:"] = {"*bstrings*","*bstrings*"}
cockpit_heirarchy["$firing_point:"] = {"*bstrings*","*bstrings*","points","*index*"}
cockpit_heirarchy["+pos:"] = {"*bstrings*","*bstrings*","points","*bstrings*"}

cockpit_heirarchy["#input_bindings"] = {"*keyword*"}
cockpit_heirarchy["$bind:"] = {"*bstrings*","*index*"}
cockpit_heirarchy["+key:"] = {"*bstrings*","*bstrings*"}
cockpit_heirarchy["+command:"] = {"*bstrings*","*bstrings*"}
--parse it!
cockpits_tbl = parse_table("cockpits.tbl", "data/tables/", cockpit_datatypes, cockpit_heirarchy)
--print_infotable(cockpits_tbl) --use this function with a debug build (prints to debug console/fslog) to check that your data is parsed correctly
]]--
« Last Edit: December 22, 2009, 03:33:02 am by Nuke »
I can no longer sit back and allow communist infiltration, communist indoctrination, communist subversion, and the international communist conspiracy to sap and impurify all of our precious bodily fluids.

Nuke's Scripting SVN