substitution can be too greedy

Issue #12 resolved
nicm
created an issue

Hi

I want to substitute @Header like this:

#ifndef _@header_H_

Into:

#ifndef _FOO_H_

Or whatever @Header is.

But wheezy.template looks for @header_H_ instead of @Header and fails.

I can get around this by using a \ and a newline but that's kind of ugly.

Is there a better way to do this? I tried @{header} and some other obvious stuff but no luck.

Comments (19)

  1. nicm reporter

    Woot, this works! Thanks!

    Another quick question if you don't mind:

    I want to insert the result of a method, for example something like:

    #define FOOBAR @foo.size()
    

    But the only way I've come up with is to write a new global_vars function:

    engine.global_vars.update({'z: lambda o: o.size ()})

    And then do @m!z. This works fine, but means I need to write a add a new function for each method.

    I tried using CodeExtension and @() but I can't get it to insert into the rendered document.

    Is there a better way?

    Thanks

  2. nicm reporter

    Oh wait... it almost works. When I do this with my noop function:

    #include "@s(name).h"
    

    I get an error again:

    Traceback (most recent call last):
    File "tcxMessageGenerator.py", line 66, in <module>
    data = engine.get_template (template).render (context)
    File "/home/nick/tcx/common/thirdparty/wheezy.template/lib/wheezy/template/engine.py", line 134, in render
    return self.render_template(ctx, {}, {})
    File "%s_c.t", line 4, in render
    AttributeError: 'str' object has no attribute 'h'
    make: *** [generate] Error 1
    

    Any ideas?

  3. nicm reporter
    Ok that problem told me how to fix the other one, I think I was being dumb, just @foo.size() (or @foo.size()!s.bar if I want eg 123.bar) seem to work. Not sure what I was doing before.
    
    You can close this issue. Thanks!
    
  4. Andriy Kornatskyy repo owner

    The core extension attempts to extend expression as far as possible, thus syntax like @header.h is considered as h property of object header. The reason for this is an absence of token of expression end/termination. As a workaround it is possible to have a function like: @c_h(header) which is lambda s: s + '.h'.

  5. nicm reporter

    As you can imagine it gets a little messy that I have to use a number of different forms depending what follows my substitution, in C code I am often following substitutions with '.' or '[' or [A-Za-z_] so I have a mixture of @Faisal Mohammed, @Faisal Mohammed!s, @s(foo) depending what follows:

    "foo": "string"
    
    @require(foo)
    @foo
    @foo!s[]        (to give me string[])
    @foo!s.abc      (to give me string.abc)
    @s(foo)_abc     (to give me string_abc)
    

    Also if I have a context item that is a number:

    "bar": 123
    

    I can't use it the same as a string:

     @bar
    

    I need to use my noop function again:

    @bar!s
    

    Any possibility of adding eg @{} or @[] or something which resolves only what is inside @{}. Then I could just use @{} throughout, no matter what I want to substitute and what text follows it?

    Thanks

  6. Andriy Kornatskyy repo owner

    It may be reasonable to extend template engine with support for explicit expression bounds, which can be used to eliminate such confusions, e.g. @{header}.h or {{ header }}.h, etc.

  7. Andriy Kornatskyy repo owner
    """
    """
    
    import re
    
    from wheezy.template.engine import Engine
    from wheezy.template.loader import DictLoader
    from wheezy.template.ext.core import CoreExtension
    
    
    class CurlyExtension(object):
    
        lexer_rules = {
            201: (re.compile(r'@\{ (.+?) \}'),
                  lambda m: (m.end(), 'var', str(m.group(1))))
        }
    
    
    # region: template
    
    templates = {
        'x': """\
    @require(header)
    #ifndef @{ header }.h\
    """
    }
    
    engine = Engine(
        loader=DictLoader(templates),
        extensions=[
            CoreExtension(),
            CurlyExtension()
        ]
    )
    
    t = engine.get_template('x')
    print(t.render({'header': 'FOO'}))
    
  8. nicm reporter

    I think r'@{ ?(.+?) ?}' would be better so the spaces aren't required.

    This seems to work for simple things like @{foo} but not if a function is used such as @{foo!x}.

    Also trying with a number still gives me:

    TypeError: sequence item 9: expected string or Unicode, int found
    

    Thanks

  9. nicm reporter

    Eg this shows what I mean:

    import re
    
    from wheezy.template.engine import Engine
    from wheezy.template.loader import DictLoader
    from wheezy.template.ext.core import CoreExtension
    
    class CurlyExtension(object):
        lexer_rules = {
            201: (re.compile(r'@\{ ?(.+?) ?\}'),
                  lambda m: (m.end(), 'var', str(m.group(1))))
        }
    
    # region: template
    
    templates = {
        'x': """\
    @require(header, foobar)
    #ifndef @{ header }.h
    @{header!s}
    @{foobar}
    """
    }
    
    engine = Engine(
        loader=DictLoader(templates),
        extensions=[
            CoreExtension(),
            CurlyExtension()
        ]
    )
    engine.global_vars.update ({'s': lambda s: s})
    
    t = engine.get_template('x')
    print(t.render({'header': 'FOO', 'foobar': 123}))
    
  10. Andriy Kornatskyy repo owner

    The spaces in expression are for readability.

    What you have inside @{ } must be a valid python expression, so if foo is a function than it should be @{ foo() } the result of the foo() must be string, filtering should be replaced by @{ x(foo()) }, if result is not str you can apply s filter as function @{ s(foo()) }.

  11. Andriy Kornatskyy repo owner

    If you need expression to make spaces optional here is regex '@\{(.+?)\}'. The template that uses filters:

    @require(header, foobar)
    #ifndef @{ header }.h
    @{ header }
    @{ s(foobar) }
    

    Note, filter is nothing more than just a function.

  12. Andriy Kornatskyy repo owner

    This has been added into core extension, the filters separated by !!. Syntax (spaces optional but recommended for better readability):

    @{ variable_name !! filter1!filter2 }
    

    Examples:

    @{ user.name !! e }
    @{ accepted and 'YES' or 'NO' }
    @{ (age > 20 and age < 120) and 'OK' or '?' }
    @{ n > 0 and 1 or -1 !! s }
    
  13. Log in to comment