There really isn't a lot of unpredictable nesting going on here, so you could do this with regex's. But pyparsing is my tool of choice, so here is my solution:
from pyparsing import *
LBRACK,RBRACK,COLON = map(Suppress,"[]:")
ident = Word(alphas, alphanums+"_")
datatype = oneOf("Double Long String Boolean")
# define expressions for pieces of attribute definitions
data = LBRACK + "Data" + COLON + SkipTo(RBRACK)("contents") + RBRACK
sec = LBRACK + "Sec" + COLON + SkipTo(RBRACK)("contents") + RBRACK
type = LBRACK + "Type" + COLON + datatype("datatype") + RBRACK
# define entire attribute definition, giving each piece its own results name
attrDef = Group(ident("key") + data("data") + sec("sec") + type("type"))
# now a row is just a "Row[" and one or more attrDef's and "]"
rowDef = Group("Row" + LBRACK + Group(OneOrMore(attrDef))("attrs") + RBRACK)
# this method will process each row, and convert the key and data fields
# to addressable results names
def assignAttrs(tokens):
ret = ParseResults(tokens.asList())
for attr in tokens[0].attrs:
# use datatype mapped to function to convert data at parse time
value = {
'Double' : float,
'Long' : int,
'String' : str,
'Boolean' : bool,
}[attr.type.datatype](attr.data.contents)
ret[attr.key] = value
# replace parse results created by pyparsing with our own named results
tokens[0] = ret
rowDef.setParseAction(assignAttrs)
# a TABLE is just "Table[", one or more rows and "]"
tableDef = "Table" + LBRACK + OneOrMore(rowDef)("rows") + RBRACK
test = """
Table[
Row[
C_ID[Data:12345.0][Sec:12345.0][Type:Double]
F_ID[Data:17660][Sec:17660][Type:Long]
NAME[Data:Mike Jones][Sec:Mike Jones][Type:String]
]
Row[
C_ID[Data:2560.0][Sec:2560.0][Type:Double]
NAME[Data:Casey Jones][Sec:Mike Jones][Type:String]
]
]"""
# now parse table, and access each row and its defined attributes
results = tableDef.parseString(test)
for row in results.rows:
print row.dump()
print row.NAME, row.C_ID
print
prints:
[[[['C_ID', 'Data', '12345.0', 'Sec', '12345.0', 'Type', 'Double'],...
- C_ID: 12345.0
- F_ID: 17660
- NAME: Mike Jones
Mike Jones 12345.0
[[[['C_ID', 'Data', '2560.0', 'Sec', '2560.0', 'Type', 'Double'], ...
- C_ID: 2560.0
- NAME: Casey Jones
Casey Jones 2560.0
The results names assigned in assignAttrs give you access to each of your attributes by name. To see if a name has been omitted, just test "if not row.F_ID:".