Commit 5b20092e authored by Jim Fulton's avatar Jim Fulton

updated expr machinery to use parse-tree manipulation

parent 749f2575
'''$Id: DT_Util.py,v 1.13 1997/10/29 22:06:06 jim Exp $'''
'''$Id: DT_Util.py,v 1.14 1997/11/11 18:13:48 jim Exp $'''
############################################################################
# Copyright
......@@ -52,7 +52,7 @@
# (540) 371-6909
#
############################################################################
__version__='$Revision: 1.13 $'[11:-2]
__version__='$Revision: 1.14 $'[11:-2]
import sys, regex, string, types, math, os
from string import rfind, strip, joinfields, atoi,lower,upper,capitalize
......@@ -77,26 +77,30 @@ def int_param(params,md,name,default=0):
def _tm(m, tag):
return m + tag and (' in %s' % tag)
def careful_getattr(inst, name, md):
def careful_getattr(md, inst, name):
if name[:1]!='_':
validate=md.validate
if validate is None: return getattr(inst, name)
if hasattr(inst,'aq_acquire'): return inst.acquire(name, validate, md)
if hasattr(inst,'aq_acquire'):
return inst.aq_acquire(name, validate, md)
v=getattr(inst, name)
if validate(inst,inst,name,v,md): return v
raise AttributeError, name
def careful_getitem(mapping, key, md):
def careful_getitem(md, mapping, key):
v=mapping[key]
if type(v) is type(''): return v # Short-circuit common case
validate=md.validate
if validate is None or validate(mapping,mapping,key,v,md): return v
raise KeyError, key
def careful_getslice(seq, indexes, md):
def careful_getslice(md, seq, *indexes):
v=len(indexes)
if v==2:
v=seq[indexes[0]:indexes[1]]
......@@ -129,6 +133,15 @@ def test(cond, t, f):
d['test']=test
expr_globals={
'__builtins__':{},
'_': expr_globals,
'__guarded_mul__': VSEval.careful_mul,
'__guarded_getattr__': careful_getattr,
'__guarded_getitem__': careful_getitem,
'__guarded_getslice__': careful_getslice,
}
def name_param(params,tag='',expr=0):
used=params.has_key
if used(''):
......@@ -145,13 +158,7 @@ def name_param(params,tag='',expr=0):
return params['name']
elif expr and used('expr'):
name=params['expr']
expr=VSEval.Eval(name,
globals={'__builtins__':{}, '_': expr_globals},
__mul__=VSEval.careful_mul,
__getattr__=careful_getattr,
__getitem__=careful_getitem,
__getslice__=careful_getslice,
)
expr=VSEval.Eval(name, expr_globals)
return name, expr
raise ParseError, ('No name given', tag)
......@@ -278,6 +285,9 @@ TemplateDict.pop.__roles__=TemplateDict.push.__roles__=()
############################################################################
# $Log: DT_Util.py,v $
# Revision 1.14 1997/11/11 18:13:48 jim
# updated expr machinery to use parse-tree manipulation
#
# Revision 1.13 1997/10/29 22:06:06 jim
# Moved func_code from DT_Util to DT_String.
#
......
"""Very Safe Python Expressions
"""
__rcs_id__='$Id: VSEval.py,v 1.7 1997/11/05 22:42:31 jim Exp $'
__rcs_id__='$Id: VSEval.py,v 1.8 1997/11/11 18:13:49 jim Exp $'
############################################################################
# Copyright
#
# Copyright 1996 Digital Creations, L.C., 910 Princess Anne
# Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All
# rights reserved. Copyright in this software is owned by DCLC,
# unless otherwise indicated. Permission to use, copy and
# distribute this software is hereby granted, provided that the
# above copyright notice appear in all copies and that both that
# copyright notice and this permission notice appear. Note that
# any product, process or technology described in this software
# may be the subject of other Intellectual Property rights
# reserved by Digital Creations, L.C. and are not licensed
# hereunder.
#
# Trademarks
#
# Digital Creations & DCLC, are trademarks of Digital Creations, L.C..
# All other trademarks are owned by their respective companies.
#
# No Warranty
#
# The software is provided "as is" without warranty of any kind,
# either express or implied, including, but not limited to, the
# implied warranties of merchantability, fitness for a particular
# purpose, or non-infringement. This software could include
# technical inaccuracies or typographical errors. Changes are
# periodically made to the software; these changes will be
# incorporated in new editions of the software. DCLC may make
# improvements and/or changes in this software at any time
# without notice.
#
# Limitation Of Liability
#
# In no event will DCLC be liable for direct, indirect, special,
# incidental, economic, cover, or consequential damages arising
# out of the use of or inability to use this software even if
# advised of the possibility of such damages. Some states do not
# allow the exclusion or limitation of implied warranties or
# limitation of liability for incidental or consequential
# damages, so the above limitation or exclusion may not apply to
# you.
#
#
# If you have questions regarding this software,
# contact:
#
# Jim Fulton, jim@digicool.com
#
# (540) 371-6909
# rights reserved.
#
############################################################################
__version__='$Revision: 1.7 $'[11:-2]
__version__='$Revision: 1.8 $'[11:-2]
from string import join
import new, sys
import new, sys, gparse
def default_slicer(env, s, *ind):
l=len(ind)
if l==2: return s[ind[0]:ind[1]]
elif l==1: return s[ind[0]:]
return s[:]
def careful_mul(env, *factors):
s=None
r=1
for factor in factors:
try:
l=len(factor)
s=1
except: l=factor
if s and (l*r) > 1000: raise TypeError, 'Illegal sequence repeat'
r=r*factor
return r
default_globals={
'__builtins__':{},
'__guarded_mul__': careful_mul,
'__guarded_getattr__': lambda env, inst, name: getattr(inst, name),
'__guarded_getitem__': lambda env, coll, key: coll[key],
'__guarded_getslice__': default_slicer,
}
class Eval:
"""Provide a very-safe environment for evaluating expressions
......@@ -76,36 +59,21 @@ class Eval:
"""
def __init__(self, expr, custom=None, globals={'__builtins__':{}}, **kw):
def __init__(self, expr, globals=default_globals):
"""Create a 'safe' expression
where:
expr -- a string containing the expression to be evaluated.
custom -- a mapping from operation names to:
- None, meaning that the operation is not allowed, or
- a callable object to be called in place of the operation.
globals -- A global namespace.
"""
if custom:
if kw:
d={}
for k, v in custom.items(): d[k]=v
for k, v in kw.items(): d[k]=v
custom=d
else: custom=kw
self.expr=expr
self.globals=globals
co=compile(expr,'<string>','eval')
out=[]
names=list(co.co_names)
# Check for valid names, disallowing names that begin with '_' or
......@@ -114,129 +82,27 @@ class Eval:
for name in names:
if name[:1]=='_' and name != '_' and name != '_vars':
raise TypeError, 'illegal name used in expression'
consts=list(co.co_consts)
used={}
i=0
code=co.co_code
l=len(code)
envpos=-1
LOAD_NAME=101
HAVE_ARGUMENT=90
def HAS_ARG(op): ((op) >= HAVE_ARGUMENT)
while(i < l):
c=ord(code[i])
if binops.has_key(c) and custom.has_key(binops[c]):
name=binops[c]
custop=custom[name]
self._checkop(custop, expr, name)
# custop(op1,op2,env)
self._const(out, custop, consts)
out.append(ROT_THREE)
envpos=self._push_env(out, envpos, names)
out[len(out):]=[CALL_FUNCTION, 3, 0]
i=i+1
elif unops.has_key(c) and custom.has_key(unops[c]):
name=unops[c]
custop=custom[name]
self._checkop(custop, expr, name)
# custop(op,env)
self._const(out, custop, consts)
out.append(ROT_TWO)
envpos=self._push_env(out, envpos, names)
out[len(out):]=[CALL_FUNCTION, 2, 0]
i=i+1
elif c==LOAD_ATTR and custom.has_key('__getattr__'):
name='__getattr__'
custop=custom[name]
self._checkop(custop, expr, "attribute access")
# custop(inst,name,env)
self._const(out, custop, consts)
out.append(ROT_TWO)
self._const(out, names[ord(code[i+1])+256*ord(code[i+2])],
consts)
envpos=self._push_env(out, envpos, names)
out[len(out):]=[CALL_FUNCTION, 3, 0]
i=i+3
elif (c >= SLICE and c <= SLICE_b_e
and custom.has_key('__getslice__')):
name='__getslice__'
custop=custom[name]
self._checkop(custop, expr, "slice")
# custop(seq,(indexes),env)
if c==SLICE: nargs=0
elif c==SLICE_b_: nargs=1
else:
nargs=2
if c==SLICE__e:
self._const(out,0,consts)
out.append(ROT_TWO)
out[len(out):]=[BUILD_TUPLE, nargs, 0]
self._const(out, custop, consts)
out.append(ROT_THREE)
envpos=self._push_env(out, envpos, names)
out[len(out):]=[CALL_FUNCTION, 3, 0]
i=i+1
elif c==LOAD_NAME:
out.append(c)
out.append(ord(code[i+1]))
out.append(ord(code[i+2]))
name=names[256*out[-1]+out[-2]]
if c==LOAD_NAME:
name=names[ord(code[i+1])+256*ord(code[i+2])]
used[name]=1
i=i+3
else:
out.append(c)
i=i+1
if HAS_ARG(c):
out.append(ord(code[i]))
out.append(ord(code[i+1]))
i=i+2
code=join(map(chr,out),'')
code=new.code(
co.co_argcount, co.co_nlocals, co.co_flags, code, tuple(consts),
tuple(names), co.co_varnames, co.co_filename, co.co_name)
self.code=code
i=i+3
elif c >= HAVE_ARGUMENT: i=i+3
else: i=i+1
self.code=gparse.compile(expr,'<string>','eval')
self.used=tuple(used.keys())
def _checkop(self, custop, expr, name):
if custop is None:
raise TypeError, (
'Attempt tp perform %s in "%s", but %s is not allowed.'
% (name,expr,name))
def _const(self, out, v, consts):
# Load object as a constant
try: i=consts.index(v)
except:
i=len(consts)
consts.append(v)
out.append(LOAD_CONST)
out.append(i%256)
out.append(i/256)
def _push_env(self, out, envpos, names):
# push name of special environment variable, '_env':
if envpos < 0:
envpos=len(names)
names.append('_vars')
out.append(LOAD_NAME)
out.append(envpos%256)
out.append(envpos/256)
return envpos
def eval(self, mapping):
d={'_vars': mapping}
code=self.code
......@@ -249,193 +115,12 @@ class Eval:
def __call__(self, **kw):
return eval(self.code, self.globals, kw)
def co(e,t='eval'):
"""Utility to show code object meta data.
Just call co('some expression')
The byte codes are shown as a list of integers.
"""
c=compile(e,'<string>', t)
print '__members__', c.__members__
print '\targcount', c.co_argcount
print '\tcode', map(ord,c.co_code)
print '\tconsts', c.co_consts
print '\tvarnames', c.co_varnames
print '\tflags', c.co_flags
print '\tnames', c.co_names
print '\tnlocals', c.co_nlocals
############################################################################
# Operator/Bytecode data. This has to change when the VM changes.
POP_TOP= 1
ROT_TWO= 2
ROT_THREE=3
DUP_TOP= 4
UNARY_POSITIVE=10
UNARY_NEGATIVE=11
UNARY_NOT=12
UNARY_CONVERT=13
UNARY_INVERT=15
BINARY_POWER=19
BINARY_MULTIPLY=20
BINARY_DIVIDE=21
BINARY_MODULO=22
BINARY_ADD=23
BINARY_SUBTRACT=24
BINARY_SUBSCR=25
SLICE= 30
SLICE_b_= 31
SLICE__e= 32
SLICE_b_e= 33
STORE_SLICE=40
DELETE_SLICE=50
STORE_SUBSCR=60
DELETE_SUBSCR=61
BINARY_LSHIFT=62
BINARY_RSHIFT=63
BINARY_AND=64
BINARY_XOR=65
BINARY_OR=66
PRINT_EXPR=70
PRINT_ITEM=71
PRINT_NEWLINE=72
BREAK_LOOP=80
LOAD_LOCALS=82
RETURN_VALUE=83
EXEC_STMT=85
POP_BLOCK=87
END_FINALLY=88
BUILD_CLASS=89
STORE_NAME=90
DELETE_NAME=91
UNPACK_TUPLE=92
UNPACK_LIST=93
UNPACK_ARG=94
STORE_ATTR=95
DELETE_ATTR=96
STORE_GLOBAL=97
DELETE_GLOBAL=98
UNPACK_VARARG=99
LOAD_CONST=100
LOAD_NAME=101
BUILD_TUPLE=102
BUILD_LIST=103
BUILD_MAP=104
LOAD_ATTR=105
COMPARE_OP=106
IMPORT_NAME=107
IMPORT_FROM=108
ACCESS_MODE=109
JUMP_FORWARD=110
JUMP_IF_FALSE=111
JUMP_IF_TRUE=112
JUMP_ABSOLUTE=113
FOR_LOOP=114
LOAD_LOCAL=115
LOAD_GLOBAL=116
SET_FUNC_ARGS=117
SETUP_LOOP=120
SETUP_EXCEPT=121
SETUP_FINALLY=122
LOAD_FAST=124
STORE_FAST=125
DELETE_FAST=126
SET_LINENO=127
RAISE_VARARGS=130
CALL_FUNCTION=131
MAKE_FUNCTION=132
BUILD_SLICE =133
STOP_CODE=0
HAVE_ARGUMENT=90
def HAS_ARG(op): ((op) >= HAVE_ARGUMENT)
unops={
UNARY_POSITIVE: '__pos__',
UNARY_NEGATIVE: '__neg__',
UNARY_NOT: '__not__',
UNARY_CONVERT: '__repr__',
UNARY_INVERT: '__invert__',
}
binops={
BINARY_POWER: '__power__',
BINARY_MULTIPLY: '__mul__',
BINARY_DIVIDE: '__div__',
BINARY_MODULO: '__mod__',
BINARY_ADD: '__add__',
BINARY_SUBTRACT: '__sub__',
BINARY_SUBSCR: '__getitem__',
BINARY_LSHIFT: '__lshift__',
BINARY_RSHIFT: '__rshift__',
BINARY_AND: '__and__',
BINARY_XOR: '__xor__',
BINARY_OR: '__or__',
}
############################################################################
#
# Testing code:
def mul(x,y):
print "mul(%s,%s)" % (x,y)
return x*y
def attr(x,y):
print "attr(%s,%s)" % (x,y)
return getattr(x,y)
class A: b="A.b"
def t1():
e=Eval('a*b', __mul__=mul, __getattr__=attr)
print e(a=2, b=3)
def t2():
e=Eval('a.b', __mul__=mul, __getattr__=attr)
print e(a=A)
def t3():
e=Eval('a.b', __mul__=mul, __getattr__=None)
print e(a=A)
def careful_mul(a,b,env):
try: l1=len(a)
except: l1=a
try: l2=len(b)
except: l2=b
if l1*l2 > 1000: raise TypeError, 'Illegal sequence repeat'
return a*b
"""Notes
What can mess us up?
- Creating very large sequences through mult
- Creating very large sequences through map:
map(lambda i:
map(lambda i:
map(lambda i: 'spam',
range(1000)),
range(1000)),
range(1000))
- range(1000000000)
Maybe provide limited range? Or no range at all?
Maybe no map? Remember that map is a kind of for loop.
"""
if __name__=='__main__': globals()[sys.argv[1]]()
############################################################################
#
# $Log: VSEval.py,v $
# Revision 1.8 1997/11/11 18:13:49 jim
# updated expr machinery to use parse-tree manipulation
#
# Revision 1.7 1997/11/05 22:42:31 jim
# Changed careful_mul to be compatible with recent changes.
#
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment