Parse GenCAD file to python lists
Question: I am new to python. In my first little project I want to **parse** `GenCAD`
output file and assign the `$PARTS$` content to a python list of lists data
structure for further procesing.

The file to import:

    
    
    $HEADER$
    BOARD_TYPE PCB_DESIGN
    UNITS MM
    $END HEADER$
    
    $PARTS$
    CONN1                            CONN003081                       25.00        22.70        TOP      3
          1      25.00        20.70        SIGNALA
          2      25.00        21.70        SIGNALB
          3      25.00        22.70        SIGNALC
    CONN2                            CONN003081                       31.50        45.00        TOP      3
          1      31.50        43.00        F-
          2      31.50        44.00        S-
          3      31.50        45.00        (Net0)
    R1                               RESI100161                       29.89        46.50        TOP      2
          2      29.89        47.00        F+
          1      29.89        46.00        S+
    $END PARTS$
    ...
    

I want something like that:  
` print(parts[0] ['CONN1', 'CONN003081', '25.00', '22.70', 'TOP', '3', ['1',
'25.00', '20.70', 'SIGNALA'], ['2', '25.00', '21.70', 'SIGNALB'], ['3',
'25.00', '22.70', 'SIGNALC']] `

`  
print(parts[1]) ['CONN2', 'CONN003081', '31.50', '45.00', 'TOP', '3', ['1',
'31.50', '43.00', 'F-'], ['2', '31.50', '44.00', 'S-'], ['3', '31.50',
'45.00', '(Net0)']] `


Answer: pyparsing is just right for this kind of task. Here is a pyparsing parser for
your `$PARTS$` section - see the embedded comments:

    
    
    from pyparsing import *
    
    # define little expressions for the pieces in your input text
    real = pyparsing_common.real
    integer = pyparsing_common.integer
    word = Word(alphas, alphanums+'_+-') | QuotedString('(', endQuoteChar=')', unquoteResults=False)
    
    # combine the small expressions into more complex ones - use Group to retain structure
    part_head   = Group(word + word + real + real + word + integer)
    part_detail = Group(integer + real + real + word)
    
    # define an overall part defn expression, and add results names to access the substructures
    part_defn   = Group(part_head('head') + OneOrMore(part_detail)('details'))
    
    # finally, define an expression for the input data section
    parts_list = '$PARTS$' + Group(OneOrMore(part_defn)) + '$END PARTS'
    

And here is how you put the parser to use, and access the returned results:

    
    
    # use searchString to find the $PARTS$ section, and parse its contents
    # (searchString returns a list of matches, we just want the first one)
    parts = parts_list.searchString(sample)[0]
    
    # output using pretty-printing
    parts.pprint()
    
    # output, including results names
    print(parts.dump())
    
    # iterate over parts and access substructures
    for part in parts[1]:
        print(part.head)
    

Gives:

    
    
    ['$PARTS$',
     [[['CONN1', 'CONN003081', 25.0, 22.7, 'TOP', 3],
       [1, 25.0, 20.7, 'SIGNALA'],
       [2, 25.0, 21.7, 'SIGNALB'],
       [3, 25.0, 22.7, 'SIGNALC']],
      [['CONN2', 'CONN003081', 31.5, 45.0, 'TOP', 3],
       [1, 31.5, 43.0, 'F-'],
       [2, 31.5, 44.0, 'S-'],
       [3, 31.5, 45.0, '(Net0)']],
      [['R1', 'RESI100161', 29.89, 46.5, 'TOP', 2],
       [2, 29.89, 47.0, 'F+'],
       [1, 29.89, 46.0, 'S+']]],
     '$END PARTS']
    
    ['$PARTS$', [[['CONN1', 'CONN003081', 25.0, 22.7, 'TOP', 3], [1, 25.0, ...
    [0]:
      $PARTS$
    [1]:
      [[['CONN1', 'CONN003081', 25.0, 22.7, 'TOP', 3], [1, 25.0, 20.7, ...
      [0]:
        [['CONN1', 'CONN003081', 25.0, 22.7, 'TOP', 3], [1, 25.0, 20.7, ...
        - details: [[1, 25.0, 20.7, 'SIGNALA'], [2, 25.0, 21.7, 'SIGNALB'],...
          [0]:
            [1, 25.0, 20.7, 'SIGNALA']
          [1]:
            [2, 25.0, 21.7, 'SIGNALB']
          [2]:
            [3, 25.0, 22.7, 'SIGNALC']
        - head: ['CONN1', 'CONN003081', 25.0, 22.7, 'TOP', 3]
      [1]:
        [['CONN2', 'CONN003081', 31.5, 45.0, 'TOP', 3], [1, 31.5, 43.0, 'F-'], ...
        - details: [[1, 31.5, 43.0, 'F-'], [2, 31.5, 44.0, 'S-'], [3, 31.5, ...
          [0]:
            [1, 31.5, 43.0, 'F-']
          [1]:
            [2, 31.5, 44.0, 'S-']
          [2]:
            [3, 31.5, 45.0, '(Net0)']
        - head: ['CONN2', 'CONN003081', 31.5, 45.0, 'TOP', 3]
      [2]:
        [['R1', 'RESI100161', 29.89, 46.5, 'TOP', 2], [2, 29.89, 47.0, 'F+'], ...
        - details: [[2, 29.89, 47.0, 'F+'], [1, 29.89, 46.0, 'S+']]
          [0]:
            [2, 29.89, 47.0, 'F+']
          [1]:
            [1, 29.89, 46.0, 'S+']
        - head: ['R1', 'RESI100161', 29.89, 46.5, 'TOP', 2]
    [2]:
      $END PARTS
    
    ['CONN1', 'CONN003081', 25.0, 22.7, 'TOP', 3]
    ['CONN2', 'CONN003081', 31.5, 45.0, 'TOP', 3]
    ['R1', 'RESI100161', 29.89, 46.5, 'TOP', 2]
    

