2010-11-10 17 views
9

Estoy usando pyparsing para analizar los archivos vcd (value change dump). Básicamente, quiero leer los archivos, analizarlos en un diccionario interno y manipular los valores.pyparsing, forward y recursión

Sin entrar en detalles sobre la estructura, mi problema ocurre con la identificación de categorías anidadas.

En los archivos vcd, tiene 'alcances' que incluyen cables y posiblemente algunos ámbitos más profundos (anidados). Piense en ellos como niveles.

Así que en mi archivo, tengo:

$scope module toplevel $end 
$scope module midlevel $end 
$var wire a $end 
$var wire b $end 
$upscope $end 
$var wire c $end 
$var wire d $end 
$var wire e $end 
$scope module extralevel $end 
$var wire f $end 
$var wire g $end 
$upscope $end 
$var wire h $end 
$var wire i $end 
$upscope $end 

Así 'nivel superior' abarca todo (a - i), 'nivel medio' tiene (a - b), 'extralevel' tiene (f - g) , etc.

Aquí está mi código (fragmento) para analizar esta sección:

scope_header = Group(Literal('$scope') + Word(alphas) + Word(alphas) + \ 
        Literal('$end')) 

wire_map = Group(Literal('$var') + Literal('wire') + Word(alphas) + \ 
       Literal('$end')) 

scope_footer = Group(Literal('$upscope') + Literal('$end')) 

scope = Forward() 
scope << (scope_header + ZeroOrMore(wire_map) + ZeroOrMore(scope) + \ 
      ZeroOrMore(wire_map) + scope_footer) 

Ahora, lo que yo pensaba sucede es que, ya que golpea cada ámbito, sería realizar un seguimiento de cada 'nivel ' y terminaría con una estructura que contiene ámbitos anidados. Sin embargo, se produce un error en

$scope module extralevel $end 

diciendo que espera '$ upscope'.

Así que sé que no estoy utilizando la recursión correctamente. ¿Alguien me puede ayudar? Avíseme si necesito brindar más información.

Gracias !!!!

Respuesta

9

Según su definición, un ámbito no puede contener otro ámbito, seguido de algunos mapas, seguido de otro ámbito.

Si el analizador tiene un modo de depuración donde imprime su árbol de análisis sintáctico, podrá ver esto inmediatamente. Pero, en resumen, está diciendo que hay cero o más mapas, seguidos de cero o más ámbitos, seguidos de cero o más mapas, de modo que si hay un ámbito, seguido de un mapa, ya ha pasado el campo de alcance, por lo cualquier otro alcance no es válido Si el idioma utilizado por los soportes pyparsing "o" que puede usar:

scope << (scope_header + ZeroOrMore((wire_map | scope)) + scope_footer) 
+1

Buena intuición! pyparsing sí admite "o", y con la sintaxis exacta que ha adivinado. – PaulMcG

+0

¡Guau, tan simple! ¡¡¡¡¡Funciona a las mil maravillas!!!!! ¡Muchas gracias! – RaytheonLiszt

+0

Siempre es genial cuando puedes adivinar la sintaxis sin tener que buscarla. La sobrecarga puede morderte en el a $$, sin embargo, intenta depurar un programa usando Boost Spirit (el equivalente de pyparse para C++). Es toda la diversión de depurar C++, pero cada llamada al método tiene veinte operadores sobrecargados. –

6

Por favor, seleccione la respuesta de @ ZackBloom como la correcta, intuyó que de inmediato, sin siquiera saber la sintaxis de pyparsing.

A pocos comentarios/sugerencias sobre su gramática:

Con la respuesta fue anunciado anteriormente, se puede visualizar el anidamiento usando pprint y de pyparsing método asList() el ParseResults:

res = scope.parseString(vcd) 

from pprint import pprint 
pprint(res.asList()) 

Dar:

[[['$scope', 'module', 'toplevel', '$end'], 
    [['$scope', 'module', 'midlevel', '$end'], 
    ['$var', 'wire', 'a', '$end'], 
    ['$var', 'wire', 'b', '$end'], 
    ['$upscope', '$end']], 
    ['$var', 'wire', 'c', '$end'], 
    ['$var', 'wire', 'd', '$end'], 
    ['$var', 'wire', 'e', '$end'], 
    [['$scope', 'module', 'extralevel', '$end'], 
    ['$var', 'wire', 'f', '$end'], 
    ['$var', 'wire', 'g', '$end'], 
    ['$upscope', '$end']], 
    ['$var', 'wire', 'h', '$end'], 
    ['$var', 'wire', 'i', '$end'], 
    ['$upscope', '$end']]] 

Ahora tiene resultados bien estructurados. Pero puedes limpiar las cosas un poco. Por un lado, ahora que tiene estructura, realmente no necesita todos esos tokens $scope, $end, etc. Ciertamente puedes pasar sobre ellos mientras navegas por los resultados analizados, pero también puedes tener pyparsing simplemente soltándolos de la salida analizada (dado que los resultados ahora están estructurados, realmente no estás perdiendo nada).Cambio que las definiciones del analizador a:

SCOPE, VAR, UPSCOPE, END = map(Suppress, 
           "$scope $var $upscope $end".split()) 
MODULE, WIRE = map(Literal, "module wire".split()) 

scope_header = Group(SCOPE + MODULE + Word(alphas) + END) 
wire_map = Group(VAR + WIRE + Word(alphas) + END) 
scope_footer = (UPSCOPE + END) 

(No hay necesidad de grupo scope_footer - todo en esa expresión se suprime para Group solo le daría una lista vacía.)

Y ahora se puede ver más claramente la pedacitos muy importantes:

[[['module', 'toplevel'], 
    [['module', 'midlevel'], ['wire', 'a'], ['wire', 'b']], 
    ['wire', 'c'], 
    ['wire', 'd'], 
    ['wire', 'e'], 
    [['module', 'extralevel'], ['wire', 'f'], ['wire', 'g']], 
    ['wire', 'h'], 
    ['wire', 'i']]] 

a riesgo de exceso de agrupación, sugeriría también Group ing el contenido de su scope expresión, así:

scope << Group(scope_header + 
       Group(ZeroOrMore((wire_map | scope))) + 
       scope_footer) 

que da estos resultados:

[[['module', 'toplevel'], 
    [[['module', 'midlevel'], [['wire', 'a'], ['wire', 'b']]], 
    ['wire', 'c'], 
    ['wire', 'd'], 
    ['wire', 'e'], 
    [['module', 'extralevel'], [['wire', 'f'], ['wire', 'g']]], 
    ['wire', 'h'], 
    ['wire', 'i']]]] 

Ahora cada resultado alcance tiene 2 elementos predecibles: la cabecera de módulo, y una lista de alambres o subscopes. Esta previsibilidad hará que sea mucho más fácil de escribir el código recursivo que navegar por los resultados:

res = scope.parseString(vcd) 
def dumpScope(parsedTokens, indent=''): 
    module,contents = parsedTokens 
    print indent + '- ' + module[1] 
    for item in contents: 
     if item[0]=='wire': 
      print indent + ' wire: ' + item[1] 
     else: 
      dumpScope(item, indent+' ') 
dumpScope(res[0]) 

que viene a buscar como:

- toplevel 
    - midlevel 
    wire: a 
    wire: b 
    wire: c 
    wire: d 
    wire: e 
    - extralevel 
    wire: f 
    wire: g 
    wire: h 
    wire: i 

buena primera pregunta, recepción para SO y pyparsing!

+0

¡Muchas gracias por este Paul! Esto realmente me ayudará a limpiar mi gramática y cómo uso los resultados, y ahora entiendo más de lo que está sucediendo. ¡Y solo tengo que añadir que el pyparsing es increíble! :) – RaytheonLiszt

1

Sé que esta es una vieja pregunta que ya ha sido respondida, pero pensé que agregaría un enlace útil a un paquete de análisis Python VCD que puede poner su VCD en una estructura de datos básica de Python.

Verilog_VCD 1.0

+0

Gracias @Scott Chin! Parece útil. – RaytheonLiszt