2012-03-24 5 views
7

से पाइथन पीएलवाई लेक्सर राज्यों को नियंत्रित करना मैं क्वेरी पार्सर जैसे सरल एसक्यूएल चयन पर काम कर रहा हूं और मुझे कुछ स्थानों पर होने वाली उप-श्रेणियों को कैप्चर करने में सक्षम होना चाहिए। मैंने पाया कि लेक्सर राज्य सबसे अच्छा समाधान हैं और प्रारंभ और अंत को चिह्नित करने के लिए घुंघराले ब्रेसिज़ का उपयोग करके पीओसी करने में सक्षम थे। हालांकि, subqueries कोष्ठक द्वारा सीमित किया जाएगा, घुंघराले नहीं, और अन्य स्थानों पर भी संश्लेषण हो सकता है, इसलिए मैं हर खुले-पैर के साथ राज्य नहीं हो सकता। यह जानकारी पार्सर के साथ आसानी से उपलब्ध है, इसलिए मैं पार्सर नियमों में उपयुक्त स्थानों पर शुरू और अंत करने की उम्मीद कर रहा था। हालांकि यह काम नहीं करता क्योंकि लेक्सर एक बार में स्ट्रीम को टोकनिज़ करना प्रतीत होता है, और इसलिए टोकन को प्रारंभिक राज्य में उत्पन्न किया जाता है। क्या इस समस्या के लिए कोई समाधान है?पार्सन

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() और end_subquery() इस तरह परिभाषित कर रहे हैं:: यहाँ मैं क्या करने की कोशिश की की एक रूपरेखा है

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 टोकन बंद कोष्ठक पता लगाने के लिए बस देखते हैं :

@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 

मैं किसी भी मदद की सराहना करता हूं।

उत्तर

2

पीएलवाई लेखक की प्रतिक्रिया के आधार पर, मैं इस बेहतर समाधान के साथ आया। मुझे पता नहीं है कि सबकुरी को टोकन के रूप में कैसे वापस किया जाए, लेकिन बाकी बहुत बेहतर दिखते हैं और अब एक हैक नहीं माना जाना चाहिए।

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

चूंकि किसी के पास कोई जवाब नहीं है, इसलिए मुझे एक वर्कअराउंड खोजने के लिए मुझे परेशान किया गया है, और यहां त्रुटि पुनर्प्राप्ति का उपयोग करके एक बदसूरत हैक और पुनरारंभ()।

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

यह उत्तर केवल आंशिक रूप से सहायक हो सकता है, लेकिन मैं यह भी खंड पर देख रहे हैं प्लाई प्रलेखन (http://www.dabeaz.com/ply/ply.html) की "6.11 एंबेडेड कार्रवाई" सुझाव है। संक्षेप में, व्याकरण नियम लिखना संभव है जिसमें क्रियाएं मध्य-नियम होती हैं। यह इस के लिए कुछ इसी तरह दिखेगा:

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

lexer के व्यवहार के बारे में, अग्रदर्शी का केवल एक ही टोकन इस्तेमाल किया जा रहा है। तो, किसी भी विशेष व्याकरण नियम में, केवल एक ही अतिरिक्त टोकन पहले से ही पढ़ा गया है। यदि आप लेक्सर राज्यों को फ़्लिप करने जा रहे हैं, तो आपको यह सुनिश्चित करना होगा कि टोकन द्वारा टोकन का उपभोग करने से पहले ऐसा होता है, लेकिन पार्सर अगले आने वाले टोकन को पढ़ने के लिए कहता है।

इसके अलावा, यदि संभव हो, तो मैं समाधान के रूप में yacc() त्रुटि हैंडलिंग स्टैक से बाहर रहने का प्रयास करूंगा। त्रुटि प्रबंधन में बहुत अधिक काला जादू चल रहा है - जितना अधिक आप इससे बच सकते हैं उतना बेहतर।

मैं इस समय समय के लिए थोड़ा दबाया गया हूं, लेकिन ऐसा लगता है कि पीएलवाई के अगले संस्करण के लिए जांच की जा सकती है। इसे मेरी टू-डू सूची में रखेगा।

+0

सूचक के लिए सूचक के लिए धन्यवाद, यह बहुत ही आशाजनक लग रहा है। हालांकि, आपके उदाहरण में, हमें अंतिम टोकन के बजाय लुकहेड टोकन की जांच करनी है? आखिरी टोकन 'बी' होगा, लेकिन लुकहेड 'एलबीआरएसीई' सही होगा? – haridsv