2009-10-10 16 views
9

Aquí es un subconjunto de la gramática Python:¿Cómo analizo las sangrías y los degradados con pyparsing?

single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE 

stmt: simple_stmt | compound_stmt 
simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE 

small_stmt: pass_stmt 
pass_stmt: 'pass' 

compound_stmt: if_stmt 
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] 

suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT 

(se puede leer la gramática completa en el repositorio de Python SVN: http://svn.python.org/.../Grammar)

Estoy tratando de utilizar esta gramática para generar un analizador para Python , en Python. Lo que estoy teniendo problemas es cómo expresar los tokens INDENT y DEDENT como objetos de pyparsing.

Aquí es cómo he implementado los otros terminales:

import pyparsing as p 

string_start = (p.Literal('"""') | "'''" | '"' | "'") 
string_token = ('\\' + p.CharsNotIn("",exact=1) | p.CharsNotIn('\\',exact=1)) 
string_end = p.matchPreviousExpr(string_start) 

terminals = { 
    'NEWLINE': p.Literal('\n').setWhitespaceChars(' \t') 
     .setName('NEWLINE').setParseAction(terminal_action('NEWLINE')), 
    'ENDMARKER': p.stringEnd.copy().setWhitespaceChars(' \t') 
     .setName('ENDMARKER').setParseAction(terminal_action('ENDMARKER')), 
    'NAME': (p.Word(p.alphas + "_", p.alphanums + "_", asKeyword=True)) 
     .setName('NAME').setParseAction(terminal_action('NAME')), 
    'NUMBER': p.Combine(
      p.Word(p.nums) + p.CaselessLiteral("l") | 
      (p.Word(p.nums) + p.Optional("." + p.Optional(p.Word(p.nums))) | "." + p.Word(p.nums)) + 
       p.Optional(p.CaselessLiteral("e") + p.Optional(p.Literal("+") | "-") + p.Word(p.nums)) + 
       p.Optional(p.CaselessLiteral("j")) 
     ).setName('NUMBER').setParseAction(terminal_action('NUMBER')), 
    'STRING': p.Combine(
      p.Optional(p.CaselessLiteral('u')) + 
      p.Optional(p.CaselessLiteral('r')) + 
      string_start + p.ZeroOrMore(~string_end + string_token) + string_end 
     ).setName('STRING').setParseAction(terminal_action('STRING')), 

    # I can't find a good way of parsing indents/dedents. 
    # The Grammar just has the tokens NEWLINE, INDENT and DEDENT scattered accross the rules. 
    # A single NEWLINE would be translated to NEWLINE + PEER (from pyparsing.indentedBlock()), unless followed by INDENT or DEDENT 
    # That NEWLINE and IN/DEDENT could be spit across rule boundaries. (see the 'suite' rule) 
    'INDENT': (p.LineStart() + p.Optional(p.Word(' '))).setName('INDENT'), 
    'DEDENT': (p.LineStart() + p.Optional(p.Word(' '))).setName('DEDENT') 
} 

terminal_action es una función que devuelve la acción de análisis correspondiente, en función de sus argumentos.

Soy consciente de la función auxiliar pyparsing.indentedBlock, pero no sé cómo adoptar eso en una gramática sin el token PEER.

(Mira el pyparsing souce code para ver lo que estoy hablando)

se puede ver el código fuente completo aquí: http://pastebin.ca/1609860

Respuesta

10

Hay un par de ejemplos en la pyparsing wiki Examples page que podría darle algunas ideas:

Para utilizar pyparsing de indentedBlock, creo que se definiría como suite:

indentstack = [1] 
suite = indentedBlock(stmt, indentstack, True) 

Tenga en cuenta que indentedGrammarExample.py es anterior a la inclusión de indentedBlock en pyparsing, también lo hace su propio análisis IMPLEMENTACIÓN DE guión.

Cuestiones relacionadas