Add the function "f_string()", similar to python's 'f strings', to the template language.

This commit is contained in:
Charles Haley 2025-09-18 17:38:15 +01:00
parent 6e20528004
commit 4887d6990b
2 changed files with 65 additions and 3 deletions

View File

@ -61,6 +61,7 @@ class Node:
NODE_SWITCH_IF = 32
NODE_LIST_COUNT_FIELD = 33
NODE_WITH = 34
NODE_FSTRING = 35
def __init__(self, line_number, name):
self.my_line_number = line_number
@ -77,7 +78,7 @@ class Node:
class WithNode(Node):
def __init__(self, line_number, book_id, block):
Node.__init__(self, line_number, 'if ...')
Node.__init__(self, line_number, 'with ...')
self.node_type = self.NODE_WITH
self.book_id = book_id
self.block = block
@ -348,6 +349,13 @@ class ListCountFieldNode(Node):
self.expression = expression
class FStringNode(Node):
def __init__(self, line_number, string):
Node.__init__(self, line_number, 'f_string')
self.node_type = self.NODE_FSTRING
self.string = string
class _Parser:
LEX_OP = 1
LEX_ID = 2
@ -743,7 +751,9 @@ class _Parser:
'strcat': (lambda _: True,
lambda ln, args: StrcatNode(ln, args)),
'list_count_field': (lambda args: len(args) == 1,
lambda ln, args: ListCountFieldNode(ln, args[0]))
lambda ln, args: ListCountFieldNode(ln, args[0])),
'f_string': (lambda args: len(args) == 1,
lambda ln, args: FStringNode(ln, args[0])),
}
def expr(self):
@ -1392,6 +1402,14 @@ class _Interpreter:
self.break_reporter(prog.node_name, res, prog.line_number)
return res
def do_node_f_string(self, prog):
def repl(mo):
print(mo.group()[1:-1])
p = self.parent.gpm_parser.program(self.parent, self.funcs,
self.parent.lex_scanner.scan(mo.group()[1:-1]))
return self.expr(p)
return str(re.sub(r'\{.*?\}', repl, self.expr(prog.string)))
def do_node_list_count_field(self, prog):
name = field_metadata.search_term_to_field_key(self.expr(prog.expression))
res = getattr(self.parent_book, name, None)
@ -1654,6 +1672,7 @@ class _Interpreter:
Node.NODE_LOCAL_FUNCTION_CALL: do_node_local_function_call,
Node.NODE_LIST_COUNT_FIELD: do_node_list_count_field,
Node.NODE_WITH: do_node_with,
Node.NODE_FSTRING: do_node_f_string,
}
def expr(self, prog):

View File

@ -3761,6 +3761,49 @@ This function can be used only in the GUI.
return '1' if d.exec() == QDialog.DialogCode.Accepted else ''
class BuiltinFString(BuiltinFormatterFunction):
name = 'f_string'
arg_count = 1
category = FORMATTING_VALUES
def __doc__getter__(self): return translate_ffml(
r'''
``f_string(string)`` -- interpret ``string`` similar to how python interprets ``f`` strings.
The indended use is to simplify long sequences of ``str & str`` or strcat(a,b,c) expressions.
Text between braces (``{`` and ``}``) must be General Program Mode template
expressions. The expressions, which can be expression lists, are evaluated in
the current context (current book and local variables). Text not between
braces is passed through unchanged.
Examples:
[LIST]
[*]``f_string('Here is the title: {$title}')`` - returns the string with ``{$title}``
replaced with the title of the current book. For example, if the book's title is
`20,000 Leagues Under the Sea` then the ``f_string()`` returns
`Here is the title: 20,000 Leagues Under the Sea`.
[*]Assuming the current date is 18 Sept 2025, this ``f_string()``
[CODE]
f_string("Today's date: the {d = today(); format_date(d, 'd')} of {format_date(d, 'MMMM')}, {format_date(d, 'yyyy')}")
[/CODE]
returns the string `Today's date: the 18 of September, 2025`.
Note the expression list (an assignment then an ``if`` statement) used in the first ``{ ... }`` group to assign today's date to a local variable.
[*]If the book is book #3 in a series named `Foo` that has 5 books then this template
[CODE]
program:
if $series then
series_count = book_count('series:"""=' & $series & '"""', 0);
return f_string("{$series}, book {$series_index} of {series_count}")
fi;
return 'This book is not in a series'
[/CODE]
returns `Foo, book 3 of 5`
[/LIST]
''')
def evaluate(self, formatter, kwargs, mi, locals, fstring):
raise ValueError(_('This function cannot be called directly. It is built into the formatter'))
_formatter_builtins = [
BuiltinAdd(), BuiltinAnd(), BuiltinApproximateFormats(), BuiltinArguments(),
BuiltinAssign(),
@ -3776,7 +3819,7 @@ _formatter_builtins = [
BuiltinFinishFormatting(), BuiltinFirstMatchingCmp(), BuiltinFloor(),
BuiltinFormatDate(), BuiltinFormatDateField(), BuiltinFormatDuration(), BuiltinFormatNumber(),
BuiltinFormatsModtimes(),BuiltinFormatsPaths(), BuiltinFormatsPathSegments(),
BuiltinFormatsSizes(), BuiltinFractionalPart(),BuiltinGetLink(),
BuiltinFormatsSizes(), BuiltinFractionalPart(),BuiltinFString(), BuiltinGetLink(),
BuiltinGetNote(), BuiltinGlobals(), BuiltinHasCover(), BuiltinHasExtraFiles(),
BuiltinHasNote(), BuiltinHumanReadable(), BuiltinIdentifierInList(),
BuiltinIfempty(), BuiltinIsDarkMode(), BuiltinLanguageCodes(), BuiltinLanguageStrings(),