2016-10-07 11 views
1

J'essaye de créer un analyseur en utilisant Ply mais je suis confronté à une erreur étrange. Voici un MCVE où l'erreur correspondante se produit:Python parser ply correspond mauvaise regex

Lexer

import ply.lex as lex 

tokens = (
    'IDENTIFIER', 
    'NAME', 
    'EQUALS' 
) 

def t_IDENTIFIER(t): 
    r'\* *[a-zA-Z_]+' 
    print("identifier") 
    return t 

def t_NAME(t): 
    r"[a-zA-Z_]+" 
    print("name") 
    return t 

t_EQUALS = r"=" 
t_ignore = ' \t' 


def t_newline(t): 
    r'\n+' 
    t.lexer.lineno += len(t.value) 

# Error handling rule 
def t_error(t): 
    print("Illegal character '%s' at line' %s'" % (t.value[0] , t.lexer.lineno)) 
    t.lexer.skip(1) 

# Build the lexer 
lexer = lex.lex() 

Parser

import ply.yacc as yacc 
from l import tokens 

def p_main(p): 
    ''' 
    main : NAME EQUALS NAME 
    ''' 

def p_error(p): 
    if p is not None: 
     print ("Line %s, illegal token %s" % (p.lineno, p.value)) 
    else: 
     print("Syntax error in input!") 

parser = yacc.yacc() 

with open('simple2','r') as f: 
    result = parser.parse(f.read()) 

Mon fichier d'entrée contient seulement ceci:

A = B 

Et ce qui se passe est que le premier mot A correspond au jeton IDENTIFIER même si ce n'est pas censé faire puisque l'expression régulière exige un * avant les lettres. Après cela, l'analyseur est incapable de reconnaître l'expression puisque le lexeur ne renvoie pas les bons jetons.

Qu'est-ce qui ne va pas? La regex utilisée pour le jeton IDENTIFIER fonctionne parfaitement en Python.

+0

peut-être '\ *' est traité comme "" many \ or none "'? Avez-vous essayé '\ A = B'? – furas

+0

Non, c'est la bonne façon de faire correspondre '*'. J'ai essayé une seule règle séparée comme 'r" \ * "et celle-ci correspond correctement à l'étoile. – ibi0tux

+0

Je n'arrêtais que – furas

Répondre

1

Je pense avoir trouvé un problème et une solution.

problème est '*' à ' *' car il traite '\* ' comme une chaîne - donc '\* *' signifie '\* ' plusieurs fois ou aucun (comme 'abc*' signifie 'abc' plusieurs fois ou pas).

Vous devez '\*[ ]*' ou '\*\s*'

1

Selon le PLY manual: (italique ajouté)

En interne, lex.py utilise le module re pour faire son pattern matching. Les motifs sont compilés en utilisant le drapeau re.VERBOSE qui peut être utilisé pour faciliter la lecture. Cependant, sachez que les espaces non échappés sont ignorés et que les commentaires sont autorisés dans ce mode. Si votre motif implique des espaces, assurez-vous d'utiliser \s. Si vous devez faire correspondre le caractère #, utilisez [#].

Ainsi, le caractère d'espace dans votre expression régulière \* *[a-zA-Z_]+ est ignorée, ce qui rend l'expression régulière, efficace, \**[a-zA-Z_]+; c'est-à-dire zéro étoile ou plus. Si vous voulez vraiment que ce soit une étoile suivie d'un ou plusieurs espaces, vous voudriez quelque chose comme: \*\ [a-zA-Z_]+.