1 ###########################################################################
2 # Copyright (C) 2008 by Andrew Mahone
3 # <andrew.mahone@gmail.com>
5 # Copyright: See COPYING file that comes with this distribution
7 ###########################################################################
8 from pyparsing
import *
9 from audiomangler
import Config
11 ParserElement
.enablePackrat()
14 def __new__(cls
,*args
,**kw
):
15 ret
= object.__new
__(cls
,*args
,**kw
)
19 def evaluate(item
,cdict
):
20 if isinstance(item
,Value
):
21 return item
.evaluate(cdict
)
25 #this is necessary because Formats need to know if items are substitutions,
26 #even if the items are literals.
27 class SubsValue(Value
):
28 def __new__(cls
,data
):
29 if isinstance(data
,Value
):
32 return Value
.__new
__(cls
,data
)
33 def __init__(self
,data
):
36 def evaluate(self
,cdict
):
39 class LookupValue(Value
):
40 def __init__(self
,key
):
42 def evaluate(self
, cdict
):
43 return cdict
.get(self
.key
,u
'')
45 class FuncValue(Value
):
46 def __init__(self
,args
):
47 self
.funcname
= args
[0]
48 self
.args
= tuple(args
[1:])
50 def evaluate(self
, cdict
):
51 return getattr(self
,self
.funcname
)(cdict
,*self
.args
)
53 def firstof(self
,cdict
,*args
):
55 arg
= evaluate(arg
,cdict
)
60 def format(self
,cdict
,*args
):
61 return evaluate(args
[0],cdict
) % (evaluate(arg
,cdict
) for arg
in args
[1:])
63 def iftrue(self
,cdict
,*args
):
64 cond
= evaluate(args
[0],cdict
)
68 if cond
: return evaluate(args
[1],cdict
)
71 return evaluate(args
[1],cdict
)
73 return evaluate(args
[2],cdict
)
75 locals()['if'] = iftrue
77 def joinpath(self
, cdict
, *args
):
78 return os
.path
.join(evaluate(arg
,cdict
) for arg
in args
)
80 class TupleValue(Value
):
81 def __new__(cls
,items
):
82 if not [item
for item
in items
if isinstance(item
,Value
)]:
85 return Value
.__new
__(cls
,items
)
86 def __init__(self
,items
):
88 def evaluate(self
,cdict
):
89 return tuple(evaluate(item
,cdict
) for item
in self
.items
)
91 class ReductionValue(Value
):
93 '%': lambda x
,y
: x
% y
,
94 '/': lambda x
,y
: x
/ y
,
95 '*': lambda x
,y
: x
* y
,
96 '+': lambda x
,y
: x
+ y
,
97 '-': lambda x
,y
: x
- y
,
98 '<': lambda x
,y
: x
< y
,
99 '>': lambda x
,y
: x
> y
,
100 '<=': lambda x
,y
: x
<= y
,
101 '>=': lambda x
,y
: x
>= y
,
102 '!=': lambda x
,y
: x
!= y
,
103 '==': lambda x
,y
: x
== y
,
105 def __new__(cls
,args
):
109 if [args
[i
] for i
in range(0,len(args
),2) if isinstance(args
[i
],Value
)]:
110 return Value
.__new
__(cls
,args
)
113 rest
= tuple(tuple(args
[n
:n
+2]) for n
in range(1,len(args
)-1,2))
114 return reduce(lambda x
,y
: cls
.opstbl
[y
[0]](x
,y
[1]), rest
, first
)
116 def __init__(self
,args
):
117 #because of single-term reduction bubbling up through parse levels,
118 #we might get init'ed again.
119 if hasattr(self
,'first') and hasattr(self
,'rest'):
122 if len(args
) % 2 != 1:
123 raise ValueError('wrong number of arguments')
124 self
.rest
= tuple((self
.opstbl
[args
[n
]],args
[n
+1]) for n
in range(1,len(args
)-1,2))
126 def evaluate(self
,cdict
):
127 return reduce(lambda x
,y
: y
[0](x
,evaluate(y
[1],cdict
)), self
.rest
, evaluate(self
.first
,cdict
))
129 class BooleanReductionValue(ReductionValue
):
130 #values need to be wrapped in a lambda, then called, to prevent their
131 #evaluation in the short-circuit case
133 'and': lambda x
,y
: x
and y(),
134 'or': lambda x
,y
: x
or y(),
137 def evaluate(self
,cdict
):
138 return reduce(lambda x
,y
: y
[0](x
,lambda:evaluate(y
[1],cdict
)), self
.rest
, evaluate(self
.first
,cdict
))
140 #this means we also need to change the formula for constant reduction
141 def __new__(cls
,args
):
145 if [args
[i
] for i
in range(0,len(args
),2) if isinstance(args
[i
],Value
)]:
146 return Value
.__new
__(cls
,args
)
149 rest
= tuple(tuple(args
[n
:n
+2]) for n
in range(1,len(args
)-1,2))
150 return reduce(lambda x
,y
: cls
.opstbl
[y
[0]](x
,lambda:y
[1]), rest
, first
)
152 class UnaryOperatorValue(Value
):
156 'not': lambda x
: not x
,
158 def __new__(cls
, args
):
162 if isinstance(args
[1],Value
):
163 return Value
.__new
__(value
,args
)
165 return cls
.opstbl
[args
[0]](args
[1])
166 def __init__(self
,args
):
167 self
.op
= self
.opstbl(args
[0])
169 def evaluate(self
,cdict
):
170 return self
.op(evaluate(self
.value
,cdict
))
173 def __init__(self
,items
):
174 self
.subvalue
= items
[0]
176 def evaluate(self
,cdict
):
177 return evaluate(self
.subvalue
,cdict
)
181 def __new__(cls
,items
):
182 if isinstance(items
, basestring
):
183 if items
not in cls
._cache
:
184 ret
= Value
.__new
__(cls
)
185 ret
.parsedformat
= formatexpr
.parseString(items
)
186 cls
._cache
[items
] = ret
187 return cls
._cache
[items
]
188 elif isinstance(items
,cls
):
190 def __init__(self
,items
):
192 def sanitize(self
,instring
):
193 return re
.sub(r
'[]?[/\\=+<>:;",*|]','_',instring
)
194 def evaluate(self
, cdict
):
196 for item
in self
.parsedformat
:
197 if isinstance(item
,Value
):
198 if isinstance(item
,AsIs
):
199 item
= re
.sub(r
'[]?[\\=+<>:;",*|]','_',item
.evaluate(cdict
).encode(Config
['fs_encoding'],Config
['fs_encoding_err'] or 'replace'))
201 item
= re
.sub(r
'[]?[/\\=+<>:;",*|]','_',item
.evaluate(cdict
).encode(Config
['fs_encoding'],Config
['fs_encoding_err'] or 'replace'))
203 return ''.join(reslist
)
207 def __new__(cls
,items
):
208 if isinstance(items
, basestring
):
209 if items
not in cls
._cache
:
210 ret
= Value
.__new
__(cls
)
211 ret
.parsedformat
= expr
.parseString(items
)
212 cls
._cache
[items
] = ret
213 return cls
._cache
[items
]
214 elif isinstance(items
,cls
):
216 def __init__(self
,items
):
218 def evaluate(self
, cdict
):
219 return evaluate(self
.parsedformat
[0],cdict
)
221 def NumericValue(string
):
227 doubquot
= QuotedString('"','\\','\\')
228 singquot
= QuotedString("'",'\\','\\')
229 quot
= doubquot | singquot
230 quot
.setParseAction(lambda s
,loc
,toks
: unicode(u
''.join(toks
)))
231 spaces
= Optional(Word(' \t\n').suppress())
233 number
= Regex('([0-9]+(\.[0-9]*)?|(\.[0-9]+))')
234 number
.setParseAction(lambda s
,loc
,toks
: NumericValue(toks
[0]))
235 truth
= Keyword('True') |
Keyword('False')
236 truth
.setParseAction(lambda s
,loc
,toks
: toks
[0] == 'True')
237 validname
= Word(alphas
,alphanums
+'_')
238 lookup
= validname
.copy()
239 lookup
.setParseAction(lambda s
,loc
,toks
: LookupValue(u
''.join(toks
)))
240 lparen
= Literal('(').suppress()
241 rparen
= Literal(')').suppress()
242 arglist
= delimitedList(expr
)
243 funccall
= validname
+ lparen
+ spaces
+ arglist
+ spaces
+ rparen
244 funccall
.setParseAction(lambda s
,loc
,toks
: FuncValue(toks
))
245 parenexpr
= lparen
+ spaces
+ expr
+ spaces
+ rparen
246 tupleelem
= expr
+ Literal(',').suppress()
247 tupleexpr
= lparen
+ tupleelem
+ ZeroOrMore(tupleelem
) + Optional(expr
) + rparen
248 tupleexpr
.setParseAction(lambda s
,loc
,toks
: TupleValue(toks
))
249 sumop
= Literal('+') |
Literal('-')
250 atom
= Optional(sumop
+ spaces
) + (truth | funccall | quot | lookup | number | parenexpr | tupleexpr
) + spaces
251 atom
.setParseAction(lambda s
,loc
,toks
:UnaryOperatorValue(toks
))
252 productop
= Literal('*') |
Literal('/') |
Literal('%')
253 product
= atom
+ ZeroOrMore(spaces
+ productop
+ spaces
+ atom
)
254 product
.setParseAction(lambda s
,loc
,toks
: ReductionValue(toks
))
255 sumop
= Literal('+') |
Literal('-')
256 sum = product
+ ZeroOrMore(spaces
+ sumop
+ spaces
+ product
)
257 sum.setParseAction(lambda s
,loc
,toks
: ReductionValue(toks
))
258 compareop
= Literal('<=') |
Literal('>=') |
Literal('<') |
Literal('>') |
Literal('==') |
Literal('!=')
259 comparison
= sum + ZeroOrMore(spaces
+ compareop
+ spaces
+ sum)
260 comparison
.setParseAction(lambda s
,loc
,toks
: ReductionValue(toks
))
261 notexpr
= Optional(Keyword('not') + spaces
) + comparison
262 notexpr
.setParseAction(lambda s
,loc
,toks
: UnaryOperatorValue(toks
))
263 andexpr
= notexpr
+ ZeroOrMore(spaces
+ Keyword('and') + spaces
+ notexpr
)
264 andexpr
.setParseAction(lambda s
,loc
,toks
: BooleanReductionValue(toks
))
265 orexpr
= andexpr
+ ZeroOrMore(spaces
+ Keyword('or') + spaces
+ andexpr
)
266 orexpr
.setParseAction(lambda s
,loc
,toks
: BooleanReductionValue(toks
))
268 expr
.leaveWhitespace()
269 subsint
= Literal('$').suppress()
270 funcsubs
= subsint
+ funccall
271 asissubs
= subsint
+ Literal('/').suppress() + lparen
+ spaces
+ expr
+ spaces
+ rparen
272 asissubs
.setParseAction(lambda s
,loc
,toks
: AsIs(toks
))
273 looksubs
= subsint
+ lookup
+ WordEnd(alphanums
+ '_')
274 exprsubs
= subsint
+ parenexpr
275 subs
= exprsubs | asissubs | funcsubs | looksubs
276 subs
.setParseAction(lambda s
,loc
,toks
: SubsValue(toks
[0]))
277 literal
= Combine(OneOrMore(CharsNotIn('$')|
(subsint
+Literal('$'))))
278 formatexpr
= OneOrMore(subs|literal
)
279 formatexpr
.leaveWhitespace()
280 __all__
= ['Format','Expr','evaluate']