使用PLY解析SQL语句
我知道市面上有其他工具可以解析SQL语句,但我为了学习的目的在自己做一个。我现在在语法上遇到了一些问题。如果你能快速发现错误,请告诉我。
SELECT = r'SELECT'
FROM = r'FROM'
COLUMN = TABLE = r'[a-zA-Z]+'
COMMA = r','
STAR = r'\*'
END = r';'
t_ignore = ' ' #ignores spaces
statement : SELECT columns FROM TABLE END
columns : STAR
| rec_columns
rec_columns : COLUMN
| rec_columns COMMA COLUMN
当我尝试解析像'SELECT a FROM b;'这样的语句时,我在FROM这个词那里遇到了语法错误... 非常感谢你的帮助!
(编辑) 代码:
#!/usr/bin/python
import ply.lex as lex
import ply.yacc as yacc
tokens = (
'SELECT',
'FROM',
'WHERE',
'TABLE',
'COLUMN',
'STAR',
'COMMA',
'END',
)
t_SELECT = r'select|SELECT'
t_FROM = r'from|FROM'
t_WHERE = r'where|WHERE'
t_TABLE = r'[a-zA-Z]+'
t_COLUMN = r'[a-zA-Z]+'
t_STAR = r'\*'
t_COMMA = r','
t_END = r';'
t_ignore = ' \t'
def t_error(t):
print 'Illegal character "%s"' % t.value[0]
t.lexer.skip(1)
lex.lex()
NONE, SELECT, INSERT, DELETE, UPDATE = range(5)
states = ['NONE', 'SELECT', 'INSERT', 'DELETE', 'UPDATE']
current_state = NONE
def p_statement_expr(t):
'statement : expression'
print states[current_state], t[1]
def p_expr_select(t):
'expression : SELECT columns FROM TABLE END'
global current_state
current_state = SELECT
print t[3]
def p_recursive_columns(t):
'''recursive_columns : recursive_columns COMMA COLUMN'''
t[0] = ', '.join([t[1], t[3]])
def p_recursive_columns_base(t):
'''recursive_columns : COLUMN'''
t[0] = t[1]
def p_columns(t):
'''columns : STAR
| recursive_columns'''
t[0] = t[1]
def p_error(t):
print 'Syntax error at "%s"' % t.value if t else 'NULL'
global current_state
current_state = NONE
yacc.yacc()
while True:
try:
input = raw_input('sql> ')
except EOFError:
break
yacc.parse(input)
1 个回答
6
我觉得你的问题在于,你为 t_TABLE
和 t_COLUMN
定义的正则表达式也匹配了你的保留字(比如 SELECT 和 FROM)。换句话说,当你输入 SELECT a FROM b;
时,它会被分解成类似 COLUMN COLUMN COLUMN COLUMN END
的东西(或者其他模糊的分解方式),这样就和你的语法规则不匹配了,所以出现了语法错误。
作为一个快速的检查,你可以把那些正则表达式改成严格匹配你输入的内容,像这样:
t_TABLE = r'b'
t_COLUMN = r'a'
你会发现语法 SELECT a FROM b;
可以通过,因为正则表达式 'a' 和 'b' 不会匹配你的保留字。
另外,还有一个问题是,TABLE 和 COLUMN 的正则表达式也有重叠,所以词法分析器在处理这些标记时也会出现模糊性。
在 PLY 文档中有一个微妙但相关的部分,关于这个问题。我不太确定怎么解释最合适,但关键是词法分析会先进行,所以它不能真正利用你的语法规则的上下文来判断它遇到的是 TABLE 标记还是 COLUMN 标记。你需要把这些标记归纳成某种 ID
标记,然后在解析时再进行筛选。
如果我有更多精力,我会尝试更深入地研究你的代码,并提供一个实际的代码解决方案,但我觉得既然你已经表示这是一个学习练习,也许我指明一个正确的方向就足够了。