上下文无关语法通常具有树状结构,功能程序也具有树状结构。我并不声称以下内容可以解决您的所有问题,但如果您确定不想使用类似SQLite3
.
from functools import partial
def select_keys(keys, from_):
return ({k : fun(v, row) for k, (v, fun) in keys.items()}
for row in from_)
def select_where(from_, where):
return (row for row in from_
if where(row))
def default_keys_transform(keys, transform=lambda v, row: row[v]):
return {k : (k, transform) for k in keys}
def select(keys=None, from_=None, where=None):
"""
SELECT v1 AS k1, 2*v2 AS k2 FROM table WHERE v1 = a AND v2 >= b OR v3 = c
translates to
select(dict(k1=(v1, lambda v1, r: r[v1]), k2=(v2, lambda v2, r: 2*r[v2])
, from_=table
, where= lambda r : r[v1] = a and r[v2] >= b or r[v3] = c)
"""
assert from_ is not None
idfunc = lambda k, t : t
select_k = idfunc if keys is None else select_keys
if isinstance(keys, list):
keys = default_keys_transform(keys)
idfunc = lambda t, w : t
select_w = idfunc if where is None else select_where
return select_k(keys, select_w(from_, where))
你如何确保你没有给用户执行任意代码的能力。这个框架承认所有可能的功能。好吧,为了安全起见,您可以在其上设置一个包装器,以公开可接受的函数对象的固定列表。
ALLOWED_FUNCS = [ operator.mul, operator.add, ...] # List of allowed funcs
def select_secure(keys=None, from_=None, where=None):
if keys is not None and isinstance(keys, dict):
for v, fun keys.values:
assert fun in ALLOWED_FUNCS
if where is not None:
assert_composition_of_allowed_funcs(where, ALLOWED_FUNCS)
return select(keys=keys, from_=from_, where=where)
怎么写assert_composition_of_allowed_funcs
。在 python 中很难做到这一点,但在 lisp 中很容易。让我们假设 where 是一个要以嘴唇形式评估的函数列表,例如格式 iewhere=(operator.add, (operator.getitem, row, v1), 2)
或where=(operator.mul, (operator.add, (opreator.getitem, row, v2), 2), 3)
。
这样就可以编写一个apply_lisp
函数,确保 where 函数仅由 ALLOWED_FUNCS 或诸如 float、int、str 之类的常量组成。
def apply_lisp(where, rowsym, rowval, ALLOWED_FUNCS):
assert where[0] in ALLOWED_FUNCS
return apply(where[0],
[ (apply_lisp(w, rowsym, rowval, ALLOWED_FUNCS)
if isinstance(w, tuple)
else rowval if w is rowsym
else w if isinstance(w, (float, int, str))
else None ) for w in where[1:] ])
除此之外,您还需要检查确切的类型,因为您不希望您的类型被覆盖。所以不要用isinstance
,用type in (float, int, str)
。哦,男孩,我们遇到了:
Greenspun 的第十条编程规则:任何足够复杂的 C 或 Fortran 程序都包含一个临时的、非正式指定的、充满 bug 的慢速实现,它是 Common Lisp 的一半。