[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[lmi-commits] [lmi] master bb64784 013/156: Add a helper allowing to int
From: |
Greg Chicares |
Subject: |
[lmi-commits] [lmi] master bb64784 013/156: Add a helper allowing to interpolate variables in strings |
Date: |
Tue, 30 Jan 2018 17:21:51 -0500 (EST) |
branch: master
commit bb647843dd723c611405752b56e03b13d091aa85
Author: Vadim Zeitlin <address@hidden>
Commit: Vadim Zeitlin <address@hidden>
Add a helper allowing to interpolate variables in strings
This is convenient to use when generating illustrations as inserting the
variable references directly in the string is much less verbose than
concatenating many strings together.
---
Makefile.am | 8 ++++
interpolate_string.cpp | 96 +++++++++++++++++++++++++++++++++++++++++++++
interpolate_string.hpp | 45 +++++++++++++++++++++
interpolate_string_test.cpp | 70 +++++++++++++++++++++++++++++++++
ledger_pdf_generator_wx.cpp | 16 ++++----
objects.make | 7 ++++
6 files changed, 234 insertions(+), 8 deletions(-)
diff --git a/Makefile.am b/Makefile.am
index 288754b..a100537 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -110,6 +110,7 @@ TESTS = \
test_ieee754 \
test_input_seq \
test_input \
+ test_interpolate_string \
test_irc7702a \
test_istream_to_string \
test_loads \
@@ -328,6 +329,7 @@ liblmi_common_sources = \
input_sequence_parser.cpp \
input_xml_io.cpp \
interest_rates.cpp \
+ interpolate_string.cpp \
ledger.cpp \
ledger_base.cpp \
ledger_invariant.cpp \
@@ -778,6 +780,11 @@ test_input_LDADD = \
$(BOOST_LIBS) \
$(XMLWRAPP_LIBS)
+test_interpolate_string_SOURCES = \
+ $(common_test_objects) \
+ interpolate_string.cpp \
+ interpolate_string_test.cpp
+
test_irc7702a_SOURCES = \
$(common_test_objects) \
ihs_irc7702a.cpp \
@@ -1170,6 +1177,7 @@ noinst_HEADERS = \
input_sequence_interval.hpp \
input_sequence_parser.hpp \
interest_rates.hpp \
+ interpolate_string.hpp \
istream_to_string.hpp \
ledger.hpp \
ledger_base.hpp \
diff --git a/interpolate_string.cpp b/interpolate_string.cpp
new file mode 100644
index 0000000..dae1f02
--- /dev/null
+++ b/interpolate_string.cpp
@@ -0,0 +1,96 @@
+// Interpolate string containing embedded variable references.
+//
+// Copyright (C) 2017 Gregory W. Chicares.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+//
+// http://savannah.nongnu.org/projects/lmi
+// email: <address@hidden>
+// snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
+
+#include "pchfile.hpp"
+
+#include "interpolate_string.hpp"
+
+#include "alert.hpp"
+
+#include <stdexcept>
+
+std::string interpolate_string
+ (char const* s
+ ,std::function<std::string (std::string const&)> const& lookup
+ )
+{
+ std::string out;
+
+ // This is probably not going to be enough as replacements of the
+ // interpolated variables tend to be longer than the variables names
+ // themselves, but it's difficult to estimate the resulting string length
+ // any better than this.
+ out.reserve(strlen(s));
+
+ for(char const* p = s; *p; ++p)
+ {
+ if(p[0] == '$' && p[1] == '{')
+ {
+ std::string name;
+ for(p += 2;; ++p)
+ {
+ if(*p == '\0')
+ {
+ alarum()
+ << "Unmatched opening brace at position "
+ << (p - s - 1 - name.length())
+ << std::flush
+ ;
+ }
+
+ if(*p == '}')
+ {
+ // We don't check here if name is not empty, as there is no
+ // real reason to do it. Empty variable name may seem
+ // strange, but why not allow using "${}" to insert
+ // something into the interpolated string, after all?
+ out += lookup(name);
+ break;
+ }
+
+ if(p[0] == '$' && p[1] == '{')
+ {
+ // We don't allow nested interpolations, so this can only
+ // be result of an error, e.g. a forgotten '}' before it.
+ alarum()
+ << "Unexpected nested interpolation at position "
+ << (p - s + 1)
+ << " (outer interpolation starts at "
+ << (p - s - 1 - name.length())
+ << ")"
+ << std::flush
+ ;
+ }
+
+ // We don't impose any restrictions on the kind of characters
+ // that can occur in the variable names neither because there
+ // just doesn't seem to be anything to gain from it.
+ name += *p;
+ }
+ }
+ else
+ {
+ out += *p;
+ }
+ }
+
+ return out;
+}
diff --git a/interpolate_string.hpp b/interpolate_string.hpp
new file mode 100644
index 0000000..66c7445
--- /dev/null
+++ b/interpolate_string.hpp
@@ -0,0 +1,45 @@
+// Interpolate string containing embedded variable references.
+//
+// Copyright (C) 2017 Gregory W. Chicares.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+//
+// http://savannah.nongnu.org/projects/lmi
+// email: <address@hidden>
+// snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
+
+#ifndef interpolate_string_hpp
+#define interpolate_string_hpp
+
+#include "config.hpp"
+
+#include <functional>
+#include <string>
+
+/// Interpolate string containing embedded variable references.
+///
+/// Return the input string after replacing all ${variable} references in it
+/// with the value of the variable as returned by the provided function.
+///
+/// To allow embedding literal "${" fragment into the returned string, create a
+/// pseudo-variable returning these characters as its expansion, there is no
+/// built-in way to escape these characters.
+///
+/// Throw if the lookup function throws or if the string uses invalid syntax.
+std::string interpolate_string
+ (char const* s
+ ,std::function<std::string (std::string const&)> const& lookup
+ );
+
+#endif // interpolate_string_hpp
diff --git a/interpolate_string_test.cpp b/interpolate_string_test.cpp
new file mode 100644
index 0000000..24e2b6d
--- /dev/null
+++ b/interpolate_string_test.cpp
@@ -0,0 +1,70 @@
+// Interpolate string containing embedded variable references.
+//
+// Copyright (C) 2017 Gregory W. Chicares.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+//
+// http://savannah.nongnu.org/projects/lmi
+// email: <address@hidden>
+// snail: Chicares, 186 Belle Woods Drive, Glastonbury CT 06033, USA
+
+#include "pchfile.hpp"
+
+#include "interpolate_string.hpp"
+
+#include "test_tools.hpp"
+
+int test_main(int, char*[])
+{
+ auto const test_interpolate = [](char const* s)
+ {
+ return interpolate_string(s, [](std::string const& k) { return k; });
+ };
+
+ // Check that basic interpolation works.
+ BOOST_TEST_EQUAL( test_interpolate(""), "" );
+ BOOST_TEST_EQUAL( test_interpolate("${foo}"), "foo" );
+ BOOST_TEST_EQUAL( test_interpolate("${foo}bar"), "foobar" );
+ BOOST_TEST_EQUAL( test_interpolate("foo${}bar"), "foobar" );
+ BOOST_TEST_EQUAL( test_interpolate("foo${bar}"), "foobar" );
+ BOOST_TEST_EQUAL( test_interpolate("${foo}${bar}"), "foobar" );
+
+ // Should throw if the input syntax is invalid.
+ BOOST_TEST_THROW
+ (test_interpolate("${x")
+ ,std::runtime_error
+ ,lmi_test::what_regex("Unmatched opening brace")
+ );
+ BOOST_TEST_THROW
+ (test_interpolate("${x${y}}")
+ ,std::runtime_error
+ ,lmi_test::what_regex("Unexpected nested interpolation")
+ );
+
+ // Or because the lookup function throws.
+ BOOST_TEST_THROW
+ (interpolate_string
+ ("${x}"
+ ,[](std::string const& s) -> std::string
+ {
+ throw std::runtime_error("no such variable '" + s + "'");
+ }
+ )
+ ,std::runtime_error
+ ,"no such variable 'x'"
+ );
+
+
+ return EXIT_SUCCESS;
+}
diff --git a/ledger_pdf_generator_wx.cpp b/ledger_pdf_generator_wx.cpp
index 432f9c9..a645ef3 100644
--- a/ledger_pdf_generator_wx.cpp
+++ b/ledger_pdf_generator_wx.cpp
@@ -28,6 +28,7 @@
#include "force_linking.hpp"
#include "global_settings.hpp"
#include "html.hpp"
+#include "interpolate_string.hpp"
#include "ledger.hpp"
#include "ledger_invariant.hpp"
#include "pdf_writer_wx.hpp"
@@ -309,14 +310,13 @@ class cover_page : public page
auto const footer_html = tag::p[attr::align("center")]
(tag::font[attr::size("-1")]
(text::from
- (invar.InsCoShortName
- +"Financial Group is a marketing name for "
- +invar.InsCoName
- +"("
- +invar.InsCoShortName
- +") and its affiliated company and sales representatives, "
- +invar.InsCoAddr
- +"."
+ (interpolate_string
+ ("${InsCoShortName} Financial Group is a marketing "
+ "name for ${InsCoName} (${InsCoShortName}) and its "
+ "affiliated company and sales representatives, "
+ "${InsCoAddr}.",
+ [=](std::string const& k) { return
invar.value_str(k); }
+ )
)
)
);
diff --git a/objects.make b/objects.make
index c7eda34..d6ebba3 100644
--- a/objects.make
+++ b/objects.make
@@ -215,6 +215,7 @@ common_common_objects := \
input_sequence_parser.o \
input_xml_io.o \
interest_rates.o \
+ interpolate_string.o \
ledger.o \
ledger_base.o \
ledger_invariant.o \
@@ -424,6 +425,7 @@ unit_test_targets := \
ieee754_test \
input_sequence_test \
input_test \
+ interpolate_string_test \
irc7702a_test \
istream_to_string_test \
loads_test \
@@ -693,6 +695,11 @@ input_test$(EXEEXT): \
xml_lmi.o \
yare_input.o \
+interpolate_string_test$(EXEEXT): \
+ $(common_test_objects) \
+ interpolate_string.o \
+ interpolate_string_test.o \
+
irc7702a_test$(EXEEXT): \
$(boost_filesystem_objects) \
$(common_test_objects) \
- [lmi-commits] [lmi] master updated (70fb246 -> 408ba18), Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master cfdf224 016/156: Use raw multiline string instead of concatenating several strings, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 1d99e76 008/156: Make pdf_writer_wx::output_html() type safe by taking html::text, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master e43a485 005/156: Further improve wxPdfDocument API encapsulation and reuse, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 7d278ef 011/156: Add footer to the cover page, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master d5d8185 057/156: Add tabular_details page, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master bb64784 013/156: Add a helper allowing to interpolate variables in strings,
Greg Chicares <=
- [lmi-commits] [lmi] master b72ced6 014/156: Replace ad hoc illustration properties with HTML interpolator, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master e7d763d 066/156: Add company logo display to the PDF illustration header, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master f575c94 003/156: Refactor more group_quote_pdf_gen_wx code to allow its reuse, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 5e2fc70 007/156: Make HTML generation utilities more type-safe, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 7ed2bbf 027/156: Simplify footer generation code by moving font tag outside, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master d7244c1 043/156: Change the colour used for lines and borders, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 41ae40d 089/156: Fix wrong "<br>" tag in the header template, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master acb7aae 049/156: Add numbered_page::get_extra_pages_needed() hook, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master c0d68e1 122/156: Add rate of return pages of the individual placement illustration, Greg Chicares, 2018/01/30
- [lmi-commits] [lmi] master 875ca7d 039/156: Rename StateIsTX ledger variable to StateIsTexas, Greg Chicares, 2018/01/30