1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """
23 Basic syntactic elements used to format a citation.
24
25 This module defines the base syntax elements providing the formatting
26 domain specific language.
27
28 """
29
30
31 from Pyblio.Format import S2
32 from Pyblio.Format.S3 import Tag
33 from Pyblio.Format.Base import Missing
34
35 from Pyblio.Attribute import Txo
36
37 from gettext import gettext as _
38
39 -def _deferredText(text):
40 """Ensure the parameter is a stage 1 object."""
41 if isinstance(text, (str, unicode)):
42 return _S1T(text)
43 return text
44
45
47 """ A base class that known how to join together multiple
48 fragments of DSL code."""
49
52
55
58
61
62
64
66 self.a = a
67 self.b = b
68 return
69
71 a = self.a(db, props)
72 b = self.b(db, props)
73 return S2.Sum(a, b)
74
76 return '_Sum(%s, %s)' % (repr(self.a),
77 repr(self.b))
78
80
82 self.a = a
83 self.b = b
84 return
85
87 a = self.a(db, props)
88 b = self.b(db, props)
89 return S2.Or(a, b)
90
92 return '_Or(%s, %s)' % (repr(self.a),
93 repr(self.b))
94
96 """ This is a stage 1 text, ie a text that returns a stage 2 text
97 when called."""
98
100 self.t = t
101 return
102
105
107 return '_S1T(%s)' % repr(self.t)
108
109
110 -def join(middle, last=None):
111 return _Join(middle, last)
112
114 """ The join operator is used to join together multiple fragments
115 of records::
116
117 citation = join(middle, last)[part1, part2, ...]
118
119 part1, part2, ... are joined together by inserting 'middle'
120 between them. If a part is missing, it is simply skipped.
121 If no part is available at all, the join fails.
122
123 It is possible to specify a different separator between the last
124 two parts.
125 """
126
128
129 self.middle = _deferredText(middle)
130
131 if last: self.last = _deferredText(last)
132 else: self.last = self.middle
133
134 self.children = []
135 return
136
138 if not isinstance (children, (list, tuple)):
139 children = [children]
140
141 self.children.extend([_deferredText(t) for t in children])
142 return self
143
145
146 return S2.Join(self.middle(db, props),
147 self.last(db, props),
148 [child(db, props) for child in self.children])
149
150
152 """ The switch operator helps in bringing together multiple
153 citation parts, according to the value of a Txo.
154
155 >>> citation = switch('doctype')
156 >>> citation.case(ARTICLE=article, BOOK=book)
157 >>> citation.default(default)
158
159 """
160
161 - def __init__(self, switch, _cases={}, _default=None):
162 self._switch = switch
163
164
165
166
167 self._cases = {}
168 self._cases.update(_cases)
169
170 self._default = _default
171 return
172
173 - def case(self, **kargs):
174 new = switch(self._switch, self._cases, self._default)
175
176 for k, v in kargs.items():
177 new._cases[k] = _deferredText(v)
178 return new
179
184
186 return 'switch(%s)' % repr(self._switch)
187
189
190
191 parts = self._switch.split('.')
192
193 if len(parts) == 1:
194 a = self._switch
195 try:
196 s = db.schema[a]
197 except KeyError:
198 raise KeyError(_('%s: unknown attribute') % repr(self))
199
200 def _fetch(record):
201 return record[a][0]
202
203 elif len(parts) == 2:
204 a, q = parts
205 try:
206 s = db.schema[parts[0]].q[parts[1]]
207 except KeyError:
208 raise KeyError(_('%s: unknown attribute') % repr(self))
209
210 def _fetch(record):
211 return record[a][0].q[q][0]
212
213
214 if s.type is not Txo:
215 raise TypeError(_('%s: attribute is not a txo') % repr(self))
216
217 group = db.schema.txo[s.group]
218
219 sw = {}
220
221 if self._default:
222 default = self._default(db, props)
223 else:
224 default = None
225
226
227 for name, child in self._cases.items():
228 try:
229 txo = group.byname(name)
230 except KeyError:
231 raise KeyError(_('%s: unknown txo %s in group %s') % (
232 repr(self), repr(name), repr(s.group)))
233
234 sw[Txo(txo)] = child(db, props)
235
236 return S2.Switch(_fetch, sw, default)
237
238
240 """ Translatable content.
241
242 To create translatable content, do:
243
244 >>> citation = i18n(fr=u'En français',
245 en=u'In english',
246 default=u'Zloktagrok')
247
248 >>> compiled = citation(db, props={'ln': 'fr'})
249 """
250
252
253 self._langs = {}
254
255 for k, v in langs.iteritems():
256 if k == 'default': k = ''
257 self._langs[k] = _deferredText(v)
258 return
259
261
262 ln = props.get('ln', '')
263
264 try:
265 c = self._langs[ln]
266 except KeyError:
267 c = self._langs['']
268
269 return c(db, props)
270
271
272
273
274
275
277 """ Base class for attribute accessors, providing some checks for
278 stage 2."""
279
281 self._f = field
282 return
283
284
286 """ Return a compiled version of the attribute accessor."""
287
288 parts = self._f.split('.')
289 if len(parts) == 1:
290 try:
291 s = db.schema[self._f]
292 except KeyError:
293 raise KeyError(_('%s: unknown attribute') % (
294 repr(self),))
295
296 return self._fetch_a(self._f)
297
298 elif len(parts) == 2:
299 an, qn = parts
300 try:
301 s = db.schema[an]
302 except KeyError:
303 raise KeyError(_('%s: unknown attribute') % (
304 repr(self),))
305 try:
306 q = s.q[parts[1]]
307 except KeyError:
308 raise KeyError(_('%s: unknown qualifier') % (
309 repr(self),))
310
311 return self._fetch_q(an, qn)
312
313
314 else:
315 raise SyntaxError(_('%s: illegal attribute syntax') % (
316 repr(self),))
317
318
319 -class all(_Validated):
320
322 return 'all(%s)' % repr(self._f)
323
324
326 def _fetch(record):
327 try:
328 return record[f]
329 except (KeyError, IndexError), msg:
330 raise Missing (_('%s: no such attribute in record') % repr(self))
331
332 return _fetch
333
335 def _fetch(record):
336 try:
337 return record[an][0].q[qn]
338 except (KeyError, IndexError), msg:
339 raise Missing (_('%s: no such attribute in record') % repr(self))
340
341 return _fetch
342
343 -class one(_Validated):
344
346 return 'one(%s)' % repr(self._f)
347
348
350 def _fetch(record):
351 try:
352 return record[f][0]
353 except (KeyError, IndexError), msg:
354 raise Missing (_('%s: no such attribute in record') % repr(self))
355
356 return _fetch
357
359 def _fetch(record):
360 try:
361 return record[an][0].q[qn][0]
362 except (KeyError, IndexError), msg:
363 raise Missing (_('%s: no such attribute in record') % repr(self))
364
365 return _fetch
366
367
370 def _fetch(record):
371 return str(record.key)
372 return _fetch
373
374 record_key = _record_key()
375
376
377
378
379
381 """ This is a layout tag before its [] marker. """
382
384 self.tag = tag.lower ()
385 self.attributes = {}
386
388 """Change attributes of this tag. This is implemented using
389 __call__ because it then allows the natural syntax::
390
391 A (href="http://...")
392
393 """
394 if not kw:
395 return self
396
397 for k, v in kw.iteritems():
398 if k[-1] == '_':
399 k = k[:-1]
400 elif k[0] == '_':
401 k = k[1:]
402 self.attributes[k] = _deferredText(v)
403 return self
404
407
410
412 if not isinstance(children, (list, tuple)):
413 children = [children]
414
415 children = [_deferredText(child) for child in children]
416
417 return _Tag(self.tag, children, self.attributes)
418
420
421 """ This is a layout tag after its [] marker, but before the
422 compilation."""
423
424 - def __init__ (self, tag, children, attributes):
425
426 self.tag = tag
427 self.children = children
428 self.attributes = attributes
429
431 rstr = ''
432 if self.attributes:
433 rstr += ', attributes=%r' % self.attributes
434 if self.children:
435 rstr += ', children=%s' % repr(self.children)
436
437 return "DSL.Tag(%r%s)" % (self.tag, rstr)
438
440 children = [child(db, props) for child in self.children]
441 kwargs = {}
442 for k, v in self.attributes.items():
443 try:
444 kwargs[k] = v(db, props)
445 except TypeError:
446 print repr(v)
447
448 return Tag(self.tag, children, kwargs)
449
452
455
456
457
459 """Proto is a string subclass. Instances of Proto, which are constructed
460 with a string, will construct Tag instances in response to __call__
461 and __getitem__, delegating responsibility to the tag.
462 """
463 __slots__ = []
464
467
470
471
472 glob = globals ()
473
474 for t in ('A', 'B', 'I', 'Small', 'Span'):
475 glob[t] = _Proto(t)
476
477 BR = _Proto('BR')[_S1T('')]
478
479
480
481
482
483
484
486
487 """ Transform a simple function into a lazy function lifted in the
488 formatting system.
489
490 This is only sugar : the initial function must be aware that every
491 argument must be made strict by calling them before use.
492 """
493
494 class _caller(Glue):
495 def __init__ (self, * args, ** kargs):
496 self.__args = [_deferredText(arg) for arg in args]
497
498 for k, v in kargs.items():
499 kargs[k] = _deferredText(v)
500
501 self.__kargs = kargs
502
503 def __call__(self, db, props={}):
504 args = [arg(db, props) for arg in self.__args]
505 kargs = {}
506 for k, v in self.__kargs.items():
507 kargs[k] = v(db, props)
508
509 def _late(record):
510 return fn(record, *args, **kargs)
511
512 return _late
513
514 return _caller
515