diff --git a/manual/template_lang.rst b/manual/template_lang.rst index 8fabe7b7c6..0f307df566 100644 --- a/manual/template_lang.rst +++ b/manual/template_lang.rst @@ -228,9 +228,12 @@ General Program Mode times_div_expr ::= unary_op_expr [ times_div_op unary_op_expr ]* times_div_op ::= '*' | '/' unary_op_expr ::= [ add_sub_op unary_op_expr ]* | expression - expression ::= identifier | constant | function | assignment | + expression ::= identifier | constant | function | assignment | field_reference | if_expression | for_expression | '(' expression_list ')' - identifier ::= sequence of letters or ``_`` characters + field_reference ::= '$' [ '$' ] [ '#' ] identifier + identifier ::= id_start [ id_rest ]* + id_start ::= letter | underscore + id_rest ::= id_start | digit constant ::= " string " | ' string ' | number function ::= identifier '(' expression_list [ ',' expression_list ]* ')' assignment ::= identifier '=' top_expression @@ -256,7 +259,7 @@ is 3. The operator precedence (order of evaluation) specified by the above grammar, from highest to lowest is: - * Function calls, constants, parenthesized expressions, statement expressions, assignment expressions. + * Function calls, constants, parenthesized expressions, statement expressions, assignment expressions, field references. * Unary plus (``+``) and minus (``-``). These operators evaluate right to left. These and all the other arithmetic operators return integers if the expression results in a fractional part equal to zero. Example: if an expression returns ``3.0`` it is changed to ``3``. * Multiply (``*``) and divide (``/``). These operators are associative and evaluate left to right. Use parentheses if you want to change the order of evaluation. * Add (``+``) and subtract (``-``). These operators are associative and evaluate left to right. @@ -265,6 +268,15 @@ The operator precedence (order of evaluation) specified by the above grammar, fr * Logical and (``&&``). This operator returns '1' if both the left-hand and right-hand expressions are True, or the empty string ``''`` if either is False. It is associative, evaluates left to right, and does `short-circuiting `_. * Logical or (``||``). This operator returns ``'1'`` if either the left-hand or right-hand expression is True, or ``''`` if both are False. It is associative, evaluates left to right, and does short-circuiting. The operator is an inclusive or, returning '1' if both the left- and right-hand expressions are True. +**Field References** + +A ``field_reference`` evaluates to the value of the metadata field named by lookup key that follows the ``$`` or ``$$``. Using ``$`` is equivalent to using the ``field()`` function. Using ``$$`` is equivalent to using the ``raw_field`` function. Examples:: + + * $authors ==> field('authors') + * $#genre ==> field('#genre') + * $$pubdate ==> raw_field('pubdate') + * $$#my_int ==> raw_field('#my_int') + **If Expressions** ``If`` expressions first evaluate the ``condition``, which is True if it evaluates to anything other than the empty string. If the ``condition`` is True then the ``expression_list`` in the ``then`` clause is evaluated. If it is False then the ``expression_list`` in the ``elif`` or ``else`` clause is evaluated if present. The ``elif`` and ``else`` parts are optional. The words ``if``, ``then``, ``elif``, ``else``, and ``fi`` are reserved; you cannot use them as identifier names. You can put newlines and white space wherever they make sense. The ``condition`` is a ``top_expression`` not an ``expression_list``; semicolons are not allowed. The ``expression_lists`` are semicolon-separated sequences of template language top_expressions. An ``if`` expression returns the result of the last ``top_expression`` in the evaluated ``expression_list``, or '' if no expression list was evaluated. diff --git a/src/calibre/utils/formatter.py b/src/calibre/utils/formatter.py index 38de469c8e..65f15cea9e 100644 --- a/src/calibre/utils/formatter.py +++ b/src/calibre/utils/formatter.py @@ -578,8 +578,13 @@ class _Parser(object): if self.token_is_for(): return self.for_expression() if self.token_is_id(): - # We have an identifier. Determine if it is a function id_ = self.token() + # We have an identifier. Check if it is a field reference + if len(id_) > 1 and id_[0] == '$': + if id_[1] == '$': + return RawFieldNode(ConstantNode(id_[2:])) + return FieldNode(ConstantNode(id_[1:])) + # Determine if it is a function if not self.token_op_is_lparen(): if self.token_op_is_equals(): # classic assignment statement @@ -992,6 +997,7 @@ class TemplateFormatter(string.Formatter): (r'(\|\||&&|!)', lambda x,t: (_Parser.LEX_OP, t)), # noqa (r'[(),=;:\+\-*/]', lambda x,t: (_Parser.LEX_OP, t)), # noqa (r'-?[\d\.]+', lambda x,t: (_Parser.LEX_CONST, t)), # noqa + (r'\$\$?#?\w+', lambda x,t: (_Parser.LEX_ID, t)), # noqa (r'\$', lambda x,t: (_Parser.LEX_ID, t)), # noqa (r'\w+', lambda x,t: (_Parser.LEX_ID, t)), # noqa (r'".*?((?