7

クエリパーサーのような単純な SQL 選択に取り組んでおり、特定の場所で発生する可能性のあるサブクエリを文字通りキャプチャできるようにする必要があります。lexer 状態が最適なソリューションであることがわかり、中かっこを使用して POC を実行して開始と終了をマークすることができました。ただし、サブクエリは中括弧ではなく括弧で区切られ、括弧は他の場所でも発生する可能性があるため、すべてのオープン括弧で状態になることはできません. この情報はパーサーですぐに利用できるので、パーサー ルールの適切な場所で begin と end を呼び出したいと考えていました。ただし、レクサーはストリームを一度にトークン化するように見えるため、これは機能しませんでした。したがって、トークンは INITIAL 状態で生成されます。この問題の回避策はありますか? これが私がやろうとしたことの概要です:

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

助けていただければ幸いです。

4

3 に答える 3

5

この回答は部分的にしか役に立たないかもしれませんが、PLYドキュメント(http://www.dabeaz.com/ply/ply.html)のセクション「6.11EmbeddedActions」も参照することをお勧めします。一言で言えば、アクションがルールの途中で発生する文法ルールを書くことが可能です。これは次のようになります。

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つだけです。したがって、特定の文法規則では、最大で1つの追加トークンのみがすでに読み取られています。レクサーの状態を反転する場合は、トークンがパーサーによって消費される前に、パーサーが次の着信トークンの読み取りを要求する前に、それが発生することを確認する必要があります。

また、可能であれば、解決策として、yacc()エラー処理スタックを避けようとします。エラー処理では黒魔術が多すぎる方法があります。回避できるほど、より良い結果が得られます。

現時点では少し時間に追われていますが、これはPLYの次のバージョンで調査できるもののようです。それを私のやることリストに載せます。

于 2012-03-28T11:37:21.947 に答える
1

誰も答えを持っていないので、回避策を見つけるのが面倒でした。ここに、エラー回復と restart() を使用した醜いハックがあります。

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)
于 2012-03-28T06:08:17.103 に答える