7
votes

Contrôler les états de Python Ply Lexer de l'analyseur

Je travaille sur un simple SQL Sélectionnez comme une requête de requête et je dois pouvoir capturer les sous-couches pouvant survenir à certains endroits littéralement. J'ai trouvé que Lexer States est la meilleure solution et a pu faire un POC en utilisant des accolades bouclées pour marquer le début et la fin. Cependant, les sous-sollicitations seront délimitées par des parenthèses, non des bouclés et que la parenthèse peut également se produire à d'autres endroits, donc je ne peux donc pas être l'état avec chaque paren ouvert. Cette information est facilement disponible avec l'analyseur. J'espérais donc appeler commencer et mettre fin à des emplacements appropriés dans les règles d'analyse. Cela n'a toutefois pas fonctionné car Lexer semble goûter à la fois le flux à la fois, et les jetons sont donc générés dans l'état initial. Y a-t-il une solution de contournement pour ce problème? Voici un aperçu de ce que j'ai essayé de faire:

@lex.TOKEN(r"\(")
def t_subquery_SUBQST(t):
    lexer.level += 1

@lex.TOKEN(r"\)")
def t_subquery_SUBQEN(t):
    lexer.level -= 1

@lex.TOKEN(r".")
def t_subquery_anychar(t):
    pass


0 commentaires

3 Réponses :


1
votes

Puisque personne n'a de réponse, il m'a bugié de trouver une solution de contournement et voici un piratage laids utilisant la récupération d'erreur et le redémarrage (). xxx


0 commentaires

5
votes

Cette réponse ne peut être partiellement utile que partiellement utile, mais je suggérerais également de regarder la section "6.11 actions intégrées" de la documentation de plie (http://www.dabeaz.com/ply/ply.html). En un mot, il est possible d'écrire des règles de grammaire dans lesquelles les actions se produisent à mi-règle. Cela ressemblerait quelque chose de similaire à celui-ci:

def p_somerule(p):
    '''somerule : A B possible_sub_query LBRACE sub_query RBRACE'''

def p_possible_sub_query(p):
    '''possible_sub_query :'''
    ...
    # Check if the last token read was LBRACE.   If so, flip lexer state
    # Sadly, it doesn't seem that the token is easily accessible. Would have to hack it
    if last_token == 'LBRACE':
        p.lexer.begin('SUBQUERY')


1 commentaires

Merci pour le pointeur des actions DMBDED, il a l'air très prometteur. Cependant, dans votre exemple, nous sommes censés vérifier le jeton Lookahead au lieu du dernier jeton? Le dernier jeton serait B , mais le lookahead serait lgramse non?



3
votes

Basé sur la réponse de l'auteur de pli, j'ai proposé cette meilleure solution. Je dois encore savoir comment retourner la sous-requête comme jeton, mais le reste semble beaucoup mieux et ne doit plus être considéré comme un hack.

def start_subquery(lexer):
    lexer.code_start = lexer.lexpos        # Record the starting position
    lexer.level = 1
    lexer.begin("subquery")

def end_subquery(lexer):
    lexer.begin("INITIAL")

def get_subquery(lexer):
    value = lexer.lexdata[lexer.code_start:lexer.code_end-1]
    lexer.lineno += value.count('\n')
    return value

@lex.TOKEN(r"\(")
def t_subquery_OPAR(t):
    lexer.level += 1

@lex.TOKEN(r"\)")
def t_subquery_CPAR(t):
    lexer.level -= 1
    if lexer.level == 0:
        lexer.code_end = lexer.lexpos        # Record the ending position
        return t

@lex.TOKEN(r".")
def t_subquery_anychar(t):
    pass

def p_value_subquery(p):
    """
    value : check_subquery_start OPAR check_subquery_end CPAR
    """
    p[0] = "( " + get_subquery(p.lexer) + " )"

def p_check_subquery_start(p):
    """
    check_subquery_start : 
    """
    # Here last_token would be yacc's lookahead.
    if last_token.type == "OPAR":
        start_subquery(p.lexer)

def p_check_subquery_end(p):
    """
    check_subquery_end : 
    """
    # Here last_token would be yacc's lookahead.
    if last_token.type == "CPAR":
        end_subquery(p.lexer)

last_token = None

def p_error(p):
    global subquery_retry_pos
    if p is None:
        print >> sys.stderr, "ERROR: unexpected end of query"
    else:
        print >> sys.stderr, "ERROR: Skipping unrecognized token", p.type, "("+ \
                p.value+") at line:", p.lineno, "and column:", find_column(p.lexer.lexdata, p)
        # Just discard the token and tell the parser it's okay.
        yacc.errok()

def get_token():
    global last_token
    last_token = lexer.token()
    return last_token

def parse_query(input, debug=0):
    lexer.input(input)
    return parser.parse(input, tokenfunc=get_token, debug=0)


0 commentaires