2012-03-24 13 views
7

Basit bir SQL sorgu seçicisi gibi çalışıyorum ve belirli yerlerde gerçek zamanlı olarak oluşabilecek alt sorguları yakalayabilmem gerekiyor. Lexer durumlarının en iyi çözüm olduğunu buldum ve başlangıç ​​ve bitiş işaretlemek için kaşlı ayraçlar kullanarak POC yapabiliyordum. Bununla birlikte, alt sorgular parantez ile sınırlandırılmayacaktır, kıvrımlar değil ve parantez diğer yerlerde de meydana gelebilir, bu yüzden her açık parenle devlet olamıyorum. Bu bilgi ayrıştırıcı ile kolayca kullanılabilir, bu yüzden ayrıştırıcı kurallarında uygun yerlerde başlangıç ​​ve bitiş çağrısı yapmayı umuyordum. Ancak bu, işe yaramadı çünkü lexer akışı bir kerede belirtiyor gibi görünüyor ve bu yüzden jetonlar INITIAL durumunda üretiliyor. Bu sorun için bir çözüm var mı?Python PLY lexer denetleniyor ayrıştırıcıdan ayrılıyor

def p_value_subquery(p): 
    """ 
    value : start_sub end_sub 
    """ 
    p[0] = "(" + p[1] + ")" 

def p_start_sub(p): 
    """ 
    start_sub : OPAR 
    """ 
    start_subquery(p.lexer) 
    p[0] = p[1] 

def p_end_sub(p): 
    """ 
    end_sub : CPAR 
    """ 
    subquery = end_subquery(p.lexer) 
    p[0] = subquery 

start_subquery() ve end_subquery() böyle tanımlanır: Burada yapmaya çalıştığım şeyin bir taslak olduğunu

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

def end_subquery(lexer): 
    value = lexer.lexdata[lexer.code_start:lexer.lexpos-1] 
    lexer.lineno += value.count('\n') 
    lexer.begin('INITIAL') 
    return value 

lexer belirteçleri yakın paren algılamak için sadece orada :

@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 

Herhangi bir yardım için minnettarım.

cevap

2

PLY yazarının yanıtına dayanarak, daha iyi bir çözüm buldum. Alt sorgunun nasıl bir belirteci olarak döndürüleceğini henüz anlayamadım, ancak geri kalanı daha iyi görünüyor ve artık bir hack olarak görülmemesi gerekiyor.

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) 
1

Kimsenin bir cevabı olmadığından, bir geçici çözüm bulmam için beni uyardı ve burada hata kurtarma ve yeniden başlatma() kullanarak çirkin bir hack.

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

def end_subquery(lexer): 
    value = lexer.lexdata[lexer.code_start:lexer.lexpos-1] 
    lexer.lineno += value.count('\n') 
    lexer.begin('INITIAL') 
    return value 

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

@lex.TOKEN(r"\)") 
def t_subquery_SUBQEN(t): 
    lexer.level -= 1 
    if lexer.level == 0: 
     t.type = "SUBQUERY" 
     t.value = end_subquery(lexer) 
     return t 

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

# NOTE: Due to the nature of the ugly workaround, the CPAR gets dropped, which 
# makes it look like there is an imbalance. 
def p_value_subquery(p): 
    """ 
    value : OPAR SUBQUERY 
    """ 
    p[0] = "(" + p[2] + ")" 

subquery_retry_pos = None 

def p_error(p): 
    global subquery_retry_pos 
    if p is None: 
     print >> sys.stderr, "ERROR: unexpected end of query" 
    elif p.type == 'SELECT' and parser.symstack[-1].type == 'OPAR': 
     lexer.input(lexer.lexdata) 
     subquery_retry_pos = parser.symstack[-1].lexpos 
     yacc.restart() 
    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 subquery_retry_pos 
    token = lexer.token() 
    if token and token.lexpos == subquery_retry_pos: 
     start_subquery(lexer, lexer.lexpos) 
     subquery_retry_pos = None 
    return token 

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

Bu cevap sadece kısmen yararlı olabilir, ama aynı zamanda bölümüne KAT belgelerinde (http://www.dabeaz.com/ply/ply.html) arasında "6.11 Gömülü Eylemler" bakarak öneririz. Özetle, eylemlerin kural ortasında gerçekleştiği dilbilgisi kurallarını yazmak mümkündür. Kullanılan lookahead sadece bir belirteç yoktur, lexer davranışı ile ilgili olarak

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') 

: O buna benzer bir şey olmazdı. Yani, belirli bir dilbilgisi kuralında, en fazla bir tane fazladan belirteci zaten okundu. Eğer lexer durumlarını çevirecekseniz, jetonun çözümleyici tarafından tüketilmesinden önce, ancak ayrıştırıcı bir sonraki gelen belirteci okumayı istemeden önce olduğundan emin olmanız gerekir.

Ayrıca, mümkünse, bir çözüm kadar yacc() hata işlem yığınından uzak durmaya çalışacağım. Hata işlemede çok fazla kara büyü var - daha fazla önleyebilirsiniz, daha iyi.

Şu an biraz zamanım var, ama bu PLY'nin bir sonraki sürümü için araştırılabilecek bir şey gibi görünüyor. Bunu benim yapılacaklar listesine koyacağım.

+0

Dmbedded eylemlerinin işaretçisi için teşekkürler, çok umut verici görünüyor. Ancak, örneğinizde, son belirteç yerine lookahead simgesini kontrol etmemiz gerekiyor? Son belirteç “B” olurdu, fakat göze bakacak olursak “LBRACE” doğru mu? – haridsv

İlgili konular