예제 #1
0
 def emitFillSlot(self, slotName):
     program = self.popProgram()
     slotName = slotName.strip()
     if slotName in self.slots:
         raise METALError("duplicate fill-slot name: %s" % repr(slotName),
                          self.position)
     if not re.match('%s$' % NAME_RE, slotName):
         raise METALError("invalid slot name: %s" % repr(slotName),
                          self.position)
     self.slots[slotName] = program
     self.inMacroUse = 1
     self.emit("fillSlot", slotName, program)
예제 #2
0
 def emitDefineMacro(self, macroName):
     program = self.popProgram()
     macroName = macroName.strip()
     if macroName in self.macros:
         raise METALError(
             "duplicate macro definition: %s" % repr(macroName),
             self.position)
     if not re.match('%s$' % NAME_RE, macroName):
         raise METALError("invalid macro name: %s" % repr(macroName),
                          self.position)
     self.macros[macroName] = program
     self.inMacroDef = self.inMacroDef - 1
     self.emit("defineMacro", macroName, program)
예제 #3
0
 def pushMacro(self, macroName, slots, definingName, extending):
     if len(self.macroStack) >= self.stackLimit:
         raise METALError("macro nesting limit (%d) exceeded "
                          "by %s" % (self.stackLimit, repr(macroName)))
     self.macroStack.append(
         MacroStackItem((macroName, slots, definingName, extending, True,
                         self.i18nContext)))
예제 #4
0
 def emitDefineSlot(self, slotName):
     program = self.popProgram()
     slotName = slotName.strip()
     if not re.match('%s$' % NAME_RE, slotName):
         raise METALError("invalid slot name: %s" % repr(slotName),
                          self.position)
     self.emit("defineSlot", slotName, program)
예제 #5
0
 def process_ns(self, name, attrs):
     attrlist = []
     taldict = {}
     metaldict = {}
     i18ndict = {}
     name, namebase, namens = self.fixname(name)
     for item in attrs:
         key, value = item
         key, keybase, keyns = self.fixname(key)
         ns = keyns or namens  # default to tag namespace
         if ns and ns != 'unknown':
             item = (key, value, ns)
         if ns == 'tal':
             if keybase in taldict:
                 raise TALError("duplicate TAL attribute " + repr(keybase),
                                self.getpos())
             taldict[keybase] = value
         elif ns == 'metal':
             if keybase in metaldict:
                 raise METALError(
                     "duplicate METAL attribute " + repr(keybase),
                     self.getpos())
             metaldict[keybase] = value
         elif ns == 'i18n':
             if keybase in i18ndict:
                 raise I18NError(
                     "duplicate i18n attribute " + repr(keybase),
                     self.getpos())
             i18ndict[keybase] = value
         attrlist.append(item)
     if namens in ('metal', 'tal', 'i18n'):
         taldict['tal tag'] = namens
     return name, attrlist, taldict, metaldict, i18ndict
예제 #6
0
    def do_useMacro(self, stuff, definingName=None, extending=False):
        (macroName, macroExpr, compiledSlots, block) = stuff
        if not self.metal:
            self.interpret(block)
            return
        macro = self.engine.evaluateMacro(macroExpr)
        if macro is self.Default:
            macro = block
        else:
            if not isCurrentVersion(macro):
                raise METALError(
                    "macro %s has incompatible version %s" %
                    (repr(macroName), repr(getProgramVersion(macro))),
                    self.position)
            mode = getProgramMode(macro)
            if mode != (self.html and "html" or "xml"):
                raise METALError(
                    "macro %s has incompatible mode %s" %
                    (repr(macroName), repr(mode)), self.position)
        self.pushMacro(macroName, compiledSlots, definingName, extending)

        # We want 'macroname' name to be always available as a variable
        outer = self.engine.getValue('macroname')
        self.engine.setLocal('macroname', macroName.rsplit('/', 1)[-1])

        prev_source = self.sourceFile
        wasInUse = self.inUseDirective
        self.inUseDirective = True
        self.interpret(macro)
        self.inUseDirective = wasInUse

        if self.sourceFile != prev_source:
            self.engine.setSourceFile(prev_source)
            self.sourceFile = prev_source
        self.popMacro()
        # Push the outer macroname again.
        self.engine.setLocal('macroname', outer)
예제 #7
0
    def emitStartElement(self,
                         name,
                         attrlist,
                         taldict,
                         metaldict,
                         i18ndict,
                         position=(None, None),
                         isend=0):
        if not taldict and not metaldict and not i18ndict:
            # Handle the simple, common case
            self.emitStartTag(name, attrlist, isend)
            self.todoPush({})
            if isend:
                self.emitEndElement(name, isend)
            return
        self.position = position

        # TODO: Ugly hack to work around tal:replace and i18n:translate issue.
        # I (DV) need to cleanup the code later.
        replaced = False
        if "replace" in taldict:
            if "content" in taldict:
                raise TALError(
                    "tal:content and tal:replace are mutually exclusive",
                    position)
            taldict["omit-tag"] = taldict.get("omit-tag", "")
            taldict["content"] = taldict.pop("replace")
            replaced = True

        for key, value in taldict.items():
            if key not in taldefs.KNOWN_TAL_ATTRIBUTES:
                raise TALError("bad TAL attribute: " + repr(key), position)
            if not (value or key == 'omit-tag'):
                raise TALError("missing value for TAL attribute: " + repr(key),
                               position)
        for key, value in metaldict.items():
            if key not in taldefs.KNOWN_METAL_ATTRIBUTES:
                raise METALError("bad METAL attribute: " + repr(key), position)
            if not value:
                raise TALError(
                    "missing value for METAL attribute: " + repr(key),
                    position)
        for key, value in i18ndict.items():
            if key not in taldefs.KNOWN_I18N_ATTRIBUTES:
                raise I18NError("bad i18n attribute: " + repr(key), position)
            if not value and key in ("attributes", "data", "id"):
                raise I18NError(
                    "missing value for i18n attribute: " + repr(key), position)

        todo = {}
        defineMacro = metaldict.get("define-macro")
        extendMacro = metaldict.get("extend-macro")
        useMacro = metaldict.get("use-macro")
        defineSlot = metaldict.get("define-slot")
        fillSlot = metaldict.get("fill-slot")
        define = taldict.get("define")
        condition = taldict.get("condition")
        repeat = taldict.get("repeat")
        content = taldict.get("content")
        script = taldict.get("script")
        attrsubst = taldict.get("attributes")
        onError = taldict.get("on-error")
        omitTag = taldict.get("omit-tag")
        TALtag = taldict.get("tal tag")
        i18nattrs = i18ndict.get("attributes")
        # Preserve empty string if implicit msgids are used.  We'll generate
        # code with the msgid='' and calculate the right implicit msgid during
        # interpretation phase.
        msgid = i18ndict.get("translate")
        varname = i18ndict.get('name')
        i18ndata = i18ndict.get('data')

        if varname and not self.i18nLevel:
            raise I18NError(
                "i18n:name can only occur inside a translation unit", position)

        if i18ndata and not msgid:
            raise I18NError("i18n:data must be accompanied by i18n:translate",
                            position)

        if extendMacro:
            if useMacro:
                raise METALError("extend-macro cannot be used with use-macro",
                                 position)
            if not defineMacro:
                raise METALError("extend-macro must be used with define-macro",
                                 position)

        if defineMacro or extendMacro or useMacro:
            if fillSlot or defineSlot:
                raise METALError(
                    "define-slot and fill-slot cannot be used with "
                    "define-macro, extend-macro, or use-macro", position)
            if defineMacro and useMacro:
                raise METALError("define-macro may not be used with use-macro",
                                 position)

            useMacro = useMacro or extendMacro

        if content and msgid:
            raise I18NError(
                "explicit message id and tal:content can't be used together",
                position)

        repeatWhitespace = None
        if repeat:
            # Hack to include preceding whitespace in the loop program
            repeatWhitespace = self.unEmitNewlineWhitespace()
        if position != (None, None):
            # TODO: at some point we should insist on a non-trivial position
            self.emit("setPosition", position)
        if self.inMacroUse:
            if fillSlot:
                self.pushProgram()
                # generate a source annotation at the beginning of fill-slot
                if self.source_file is not None:
                    if position != (None, None):
                        self.emit("setPosition", position)
                    self.emit("setSourceFile", self.source_file)
                todo["fillSlot"] = fillSlot
                self.inMacroUse = 0
        else:
            if fillSlot:
                raise METALError("fill-slot must be within a use-macro",
                                 position)
        if not self.inMacroUse:
            if defineMacro:
                self.pushProgram()
                self.emit("version", TAL_VERSION)
                self.emit("mode", self.xml and "xml" or "html")
                # generate a source annotation at the beginning of the macro
                if self.source_file is not None:
                    if position != (None, None):
                        self.emit("setPosition", position)
                    self.emit("setSourceFile", self.source_file)
                todo["defineMacro"] = defineMacro
                self.inMacroDef = self.inMacroDef + 1
            if useMacro:
                self.pushSlots()
                self.pushProgram()
                todo["useMacro"] = useMacro
                self.inMacroUse = 1
            if defineSlot:
                if not self.inMacroDef:
                    raise METALError(
                        "define-slot must be within a define-macro", position)
                self.pushProgram()
                todo["defineSlot"] = defineSlot

        if defineSlot or i18ndict:

            domain = i18ndict.get("domain") or self.i18nContext.domain
            source = i18ndict.get("source") or self.i18nContext.source
            target = i18ndict.get("target") or self.i18nContext.target
            if (domain != DEFAULT_DOMAIN or source is not None
                    or target is not None):
                self.i18nContext = TranslationContext(self.i18nContext,
                                                      domain=domain,
                                                      source=source,
                                                      target=target)
                self.emit("beginI18nContext", {
                    "domain": domain,
                    "source": source,
                    "target": target
                })
                todo["i18ncontext"] = 1
        if taldict or i18ndict:
            dict = {}
            for item in attrlist:
                key, value = item[:2]
                dict[key] = value
            self.emit("beginScope", dict)
            todo["scope"] = 1
        if onError:
            self.pushProgram()  # handler
            if TALtag:
                self.pushProgram()  # start
            self.emitStartTag(name, list(attrlist))  # Must copy attrlist!
            if TALtag:
                self.pushProgram()  # start
            self.pushProgram()  # block
            todo["onError"] = onError
        if define:
            self.emitDefines(define)
            todo["define"] = define
        if condition:
            self.pushProgram()
            todo["condition"] = condition
        if repeat:
            todo["repeat"] = repeat
            self.pushProgram()
            if repeatWhitespace:
                self.emitText(repeatWhitespace)
        if content:
            if varname:
                todo['i18nvar'] = varname
                todo["content"] = content
                self.pushProgram()
            else:
                todo["content"] = content
        # i18n:name w/o tal:replace uses the content as the interpolation
        # dictionary values
        elif varname:
            todo['i18nvar'] = varname
            self.pushProgram()
        if msgid is not None:
            self.i18nLevel += 1
            todo['msgid'] = msgid
        if i18ndata:
            todo['i18ndata'] = i18ndata
        optTag = omitTag is not None or TALtag
        if optTag:
            todo["optional tag"] = omitTag, TALtag
            self.pushProgram()
        if attrsubst or i18nattrs:
            if attrsubst:
                repldict = taldefs.parseAttributeReplacements(
                    attrsubst, self.xml)
            else:
                repldict = {}
            if i18nattrs:
                i18nattrs = _parseI18nAttributes(i18nattrs, self.position,
                                                 self.xml)
            else:
                i18nattrs = {}
            # Convert repldict's name-->expr mapping to a
            # name-->(compiled_expr, translate) mapping
            for key, value in sorted(repldict.items()):
                if i18nattrs.get(key, None):
                    raise I18NError(
                        "attribute [%s] cannot both be part of tal:attributes"
                        " and have a msgid in i18n:attributes" % key, position)
                ce = self.compileExpression(value)
                repldict[key] = ce, key in i18nattrs, i18nattrs.get(key)
            for key in sorted(i18nattrs):
                if key not in repldict:
                    repldict[key] = None, 1, i18nattrs.get(key)
        else:
            repldict = {}
        if replaced:
            todo["repldict"] = repldict
            repldict = {}
        if script:
            todo["script"] = script
        self.emitStartTag(name, self.replaceAttrs(attrlist, repldict), isend)
        if optTag:
            self.pushProgram()
        if content and not varname:
            self.pushProgram()
        if not content and msgid is not None:
            self.pushProgram()
        if content and varname:
            self.pushProgram()
        if script:
            self.pushProgram()
        if todo and position != (None, None):
            todo["position"] = position
        self.todoPush(todo)
        if isend:
            self.emitEndElement(name, isend, position=position)