diff options
45 files changed, 728 insertions, 298 deletions
@@ -1,6 +1,4 @@ -=============================================================================== - README FIRST!!! To build this code after doing a Git clone, run: @@ -11,18 +9,9 @@ If you try to configure and build on your own, you are almost certainly going to run into problems. In future, you can run this command again and again, and it will keep you updated to the very latest version. -=============================================================================== - - NOTE FOR MAC OS X USERS - -To build and install Ledger on the Mac requires several dependencies. If you -are a MacPorts user, you can install these dependencies very simply using: - - $ ./acprep dependencies - -Once this is done, I recommend building both debug and optimized versions of -Ledger, in a subdirectory of your source tree named 'build' (which acprep will -manage for you, you simply need to make it): +I further recommend building both debug and optimized versions of Ledger, in a +subdirectory of your source tree named 'build' (which acprep will manage for +you, you simply need to create it): $ mkdir build $ ./acprep opt make @@ -35,6 +24,12 @@ available for testing and more useful bug reports. IF YOU HAVE CONFIGURE OR BUILD PROBLEMS +To build and install Ledger requires several dependencies on various +platforms. You can install these dependencies very simply for most of them +using: + + $ ./acprep dependencies + The first order of business if acprep update doesn't work is to find out where things went wrong. So follow these steps to produce a bug report I can track down easily: @@ -237,7 +237,7 @@ class CommandLineApp(object): force_exit = True # If true, always ends run() with sys.exit() log_handler = None darwin_gcc = False - boost_version = "1_43" + boost_version = "1_44" options = { 'debug': False, @@ -461,6 +461,9 @@ class PrepareBuild(CommandLineApp): op.add_option('', '--force', action="callback", callback=self.option_force, help="Perform every action, without checking") + op.add_option('', '--clang', action='store_true', + dest='use_clang', default=False, + help='Use the Clang C++ compiler') op.add_option('', '--glibcxx-debug', action='store_true', dest='use_glibcxx_debug', default=False, help='Define _GLIBCXX_DEBUG=1 during compilation') @@ -678,7 +681,8 @@ class PrepareBuild(CommandLineApp): self.log.info("Need to run configure => " + str(self.need_to_run_configure())) self.log.info("Use _GLIBCXX_DEBUG => " + - str(self.options.use_glibcxx_debug)) + str(self.options.use_glibcxx_debug + and not self.options.use_clang)) self.log.info("Use pre-compiled headers => " + str('--enable-pch' in conf_args)) @@ -949,31 +953,44 @@ class PrepareBuild(CommandLineApp): self.sys_library_dirs.append(path) def setup_for_johnw(self): - match = re.search('gcc-mp-([0-9]+)\.([0-9]+)', self.envvars['CC']) - if match: - gcc_version = match.group(1) + match.group(2) + if self.options.use_clang: + self.boost_inc_ident = "clang" + self.boost_lib_ident = "29" + + self.log.debug('Using Clang ident: %s/%s' % + (self.boost_inc_ident, self.boost_lib_ident)) else: - gcc_version = "42" + match = re.search('gcc-mp-([0-9]+)\.([0-9]+)', self.envvars['CC']) + if match: + self.boost_inc_ident = "gcc" + match.group(1) + match.group(2) + self.boost_lib_ident = "x" + self.boost_inc_ident + else: + self.boost_inc_ident = "gcc42" + self.boost_lib_ident = "xgcc42" + + self.log.debug('Using Boost ident: %s/%s' % + (self.boost_inc_ident, self.boost_lib_ident)) - if gcc_version != "42": + if self.boost_lib_ident != "xgcc42": self.CPPFLAGS.append('-D_GLIBCXX_FULLY_DYNAMIC_STRING=1') if self.current_flavor == 'debug': - if exists('/usr/local/stow/cppunit-gcc%s/include' % gcc_version): + if exists('/usr/local/stow/cppunit-%s/include' % self.boost_inc_ident): self.sys_include_dirs.insert( - 0, '/usr/local/stow/cppunit-gcc%s/include' % gcc_version) + 0, '/usr/local/stow/cppunit-%s/include' % self.boost_inc_ident) self.sys_library_dirs.insert( - 0, '/usr/local/stow/cppunit-gcc%s/lib' % gcc_version) + 0, '/usr/local/stow/cppunit-%s/lib' % self.boost_inc_ident) - if exists('/usr/local/stow/icu-gcc%s/include' % gcc_version): + if exists('/usr/local/stow/icu-%s/include' % self.boost_inc_ident): self.sys_include_dirs.insert( - 0, '/usr/local/stow/icu-gcc%s/include' % gcc_version) + 0, '/usr/local/stow/icu-%s/include' % self.boost_inc_ident) self.sys_library_dirs.insert( - 0, '/usr/local/stow/icu-gcc%s/lib' % gcc_version) + 0, '/usr/local/stow/icu-%s/lib' % self.boost_inc_ident) self.configure_args.append('--disable-shared') - self.options.use_glibcxx_debug = True + if not self.options.use_clang: + self.options.use_glibcxx_debug = True elif self.current_flavor == 'gcov': self.configure_args.append('--disable-shared') else: @@ -1004,7 +1021,11 @@ class PrepareBuild(CommandLineApp): self.CXXFLAGS.append('-pthread') elif system == 'Darwin': - if (self.current_flavor == 'opt' or \ + if self.options.use_clang: + self.envvars['CC'] = 'clang' + self.envvars['CXX'] = 'clang++' + self.envvars['LD'] = 'llvm-ld' + elif (self.current_flavor == 'opt' or \ self.current_flavor == 'default') and \ not self.options.gcc45 and \ exists('/usr/bin/g++-4.2'): @@ -1014,7 +1035,10 @@ class PrepareBuild(CommandLineApp): self.darwin_gcc = True elif exists('/opt/local/bin/g++-mp-4.5'): self.envvars['CC'] = '/opt/local/bin/gcc-mp-4.5' - self.envvars['CXX'] = '/opt/local/bin/g++-mp-4.5' + if exists('/Users/johnw/bin/gfilt'): + self.envvars['CXX'] = '/Users/johnw/bin/gfilt' + else: + self.envvars['CXX'] = '/opt/local/bin/g++-mp-4.5' self.envvars['LD'] = '/opt/local/bin/g++-mp-4.5' elif exists('/opt/local/bin/g++-mp-4.4'): self.envvars['CC'] = '/opt/local/bin/gcc-mp-4.4' @@ -1106,7 +1130,7 @@ class PrepareBuild(CommandLineApp): self.configure_args.append('--enable-pch') - self.CXXFLAGS.append('-fpch-deps') + self.LDFLAGS.append('-fpch-deps') self.CXXFLAGS.append('-Wconversion') #self.CXXFLAGS.append('-Wold-style-cast') @@ -1152,7 +1176,7 @@ class PrepareBuild(CommandLineApp): self.CXXFLAGS.append('-Woverloaded-virtual') self.CXXFLAGS.append('-Wsign-compare') self.CXXFLAGS.append('-Wsign-promo') - self.CXXFLAGS.append('-Wstrict-null-sentinel') + #self.CXXFLAGS.append('-Wstrict-null-sentinel') self.CXXFLAGS.append('-Wwrite-strings') self.CXXFLAGS.append('-Wno-old-style-cast') self.CXXFLAGS.append('-Wno-deprecated') @@ -1197,32 +1221,24 @@ class PrepareBuild(CommandLineApp): ######################################################################### def locate_darwin_libraries(self): - match = re.search('gcc-mp-([0-9]+)\.([0-9]+)', self.envvars['CC']) - if match: - gcc_version = match.group(1) + match.group(2) - else: - gcc_version = "42" - - self.log.debug('Using gcc version: %s' % gcc_version) - if self.current_flavor == 'debug': - if self.options.use_glibcxx_debug: + if self.options.use_glibcxx_debug and not self.options.use_clang: self.log.debug('We are using GLIBCXX_DEBUG, so setting up flags') self.CPPFLAGS.append('-D_GLIBCXX_DEBUG=1') if self.boost_info.configure( - home_path = '/usr/local/stow/boost_%s_0-gcc%s' % \ - (self.boost_version, gcc_version), - suffix = '-xgcc%s-sd-%s' % \ - (gcc_version, self.boost_version), + home_path = '/usr/local/stow/boost_%s_0-%s' % \ + (self.boost_version, self.boost_inc_ident), + suffix = '-%s-sd-%s' % \ + (self.boost_lib_ident, self.boost_version), file_suffix = '.dylib', include_path = 'include/boost-%s' % self.boost_version): pass elif self.boost_info.configure( - home_path = '/usr/local/stow/boost_%s_0-gcc%s' % \ - (self.boost_version, gcc_version), - suffix = '-xgcc%s-d-%s' % \ - (gcc_version, self.boost_version), + home_path = '/usr/local/stow/boost_%s_0-%s' % \ + (self.boost_version, self.boost_inc_ident), + suffix = '-%s-d-%s' % \ + (self.boost_lib_ident, self.boost_version), file_suffix = '.dylib', include_path = 'include/boost-%s' % self.boost_version): pass @@ -1233,18 +1249,18 @@ class PrepareBuild(CommandLineApp): if self.boost_info.configure(): pass elif self.boost_info.configure( - home_path = '/usr/local/stow/boost_%s_0-gcc%s' % \ - (self.boost_version, gcc_version), - suffix = '-xgcc%s-s-%s' % \ - (gcc_version, self.boost_version), + home_path = '/usr/local/stow/boost_%s_0-%s' % \ + (self.boost_version, self.boost_inc_ident), + suffix = '-%s-s-%s' % \ + (self.boost_lib_ident, self.boost_version), file_suffix = '.dylib', include_path = 'include/boost-%s' % self.boost_version): pass elif self.boost_info.configure( - home_path = '/usr/local/stow/boost_%s_0-gcc%s' % \ - (self.boost_version, gcc_version), - suffix = '-xgcc%s-%s' % \ - (gcc_version, self.boost_version), + home_path = '/usr/local/stow/boost_%s_0-%s' % \ + (self.boost_version, self.boost_inc_ident), + suffix = '-%s-%s' % \ + (self.boost_lib_ident, self.boost_version), file_suffix = '.dylib', include_path = 'include/boost-%s' % self.boost_version): pass diff --git a/doc/ledger3.texi b/doc/ledger3.texi index 3a44803f..1f875764 100644 --- a/doc/ledger3.texi +++ b/doc/ledger3.texi @@ -99,7 +99,7 @@ one hundred dollars from an account within the Income hierarchy, to the specified expense account. The name and meaning of these accounts in arbitrary, with no preferences implied, although you will find it useful to follow standard accounting practice (@pxref{Principles of -accounting}}. +accounting}). Since an amount is missing from the second posting, it is assumed to be the inverse of the first. This guarantee the cardinal rule of @@ -194,7 +194,7 @@ characters are allowed in a commodity name, except for the following: @item Punctuation: @samp{.,;:?!} @item Mathematical and logical operators: @samp{-+*/^&|=} @item Bracketing characters: @samp{<>[](){}} -@item The at symbol: @samp{@} +@item The at symbol: @samp{@@} @end itemize And yet, any of these may appear in a commodity name if it is @@ -249,7 +249,7 @@ implied cost for you. You can also make the cost explicit using a @example 2010/05/31 Farmer's Market - Assets:My Larder 100 apples @ $0.200000 + Assets:My Larder 100 apples @@ $0.200000 Assets:Checking @end example @@ -260,7 +260,7 @@ the final report. You can also specify the @dfn{total cost}: @example 2010/05/31 Farmer's Market - Assets:My Larder 100 apples @@ $20 + Assets:My Larder 100 apples @@@@ $20 Assets:Checking @end example @@ -270,9 +270,9 @@ postings are involved: @example 2010/05/31 Farmer's Market - Assets:My Larder 100 apples @ $0.200000 - Assets:My Larder 100 pineapples @ $0.33 - Assets:My Larder 100 "crab apples" @ $0.04 + Assets:My Larder 100 apples @@ $0.200000 + Assets:My Larder 100 pineapples @@ $0.33 + Assets:My Larder 100 "crab apples" @@ $0.04 Assets:Checking @end example @@ -294,11 +294,11 @@ on the placement of the commodity in the transaction: Assets 50 primary 2010/05/31 Sample Transaction - Expenses 100 secondary @ 0.5 primary + Expenses 100 secondary @@ 0.5 primary Assets 2010/05/31 Sample Transaction - Expenses 100 secondary @@ 50 primary + Expenses 100 secondary @@@@ 50 primary Assets @end example diff --git a/lib/Makefile b/lib/Makefile index e52764bc..f2a8cd14 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -6,26 +6,40 @@ STOW_ROOT = /usr/local/stow PRODUCTS = $(HOME)/Products CC = gcc-mp-4.5 +ifeq ($(CC),clang) +CXX = clang++ +LD = llvm-ld +DIR_SUFFIX = clang +else CXX = g++-mp-4.5 LD = gcc-mp-4.5 -CXX_VERSION = 45 -CPPFLAGS = -D_GLIBCXX_DEBUG=1 -D_GLIBCXX_FULLY_DYNAMIC_STRING=1 +DIR_SUFFIX = gcc45 +endif +CPPFLAGS = -D_GLIBCXX_FULLY_DYNAMIC_STRING=1 +ifneq ($(CC),clang) +CPPFLAGS += -D_GLIBCXX_DEBUG=1 +endif CFLAGS = $(CPPFLAGS) -g LDFLAGS = -g -BOOST_VERSION = 1_43_0 +BOOST_VERSION = 1_44_0 BOOST_SOURCE = boost_$(BOOST_VERSION) +BOOST_DEFINES = define=_GLIBCXX_FULLY_DYNAMIC_STRING=1 +ifeq ($(CC),clang) +BOOST_TOOLSET = clang +else BOOST_TOOLSET = darwin -BOOST_DEFINES = define=_GLIBCXX_DEBUG=1 define=_GLIBCXX_FULLY_DYNAMIC_STRING=1 +BOOST_DEFINES = define=_GLIBCXX_DEBUG=1 +endif #BOOST_FLAGS = --architecture=x86 --address_model=32_64 BOOST_FLAGS = --toolset=$(BOOST_TOOLSET) \ --build-type=complete --layout=versioned \ $(BOOST_DEFINES) -BOOST_DIR = boost_$(BOOST_VERSION)-gcc$(CXX_VERSION) +BOOST_DIR = boost_$(BOOST_VERSION)-$(DIR_SUFFIX) BOOST_STOW = $(STOW_ROOT)/$(BOOST_DIR) BOOST_BUILD = $(PRODUCTS)/$(BOOST_DIR) ICU_FLAGS = -sHAVE_ICU=1 -sICU_PATH=$(STOW_ROOT)/icu -BOOST_ICU_DIR = boost_$(BOOST_VERSION)-icu-gcc$(CXX_VERSION) +BOOST_ICU_DIR = boost_$(BOOST_VERSION)-icu-$(DIR_SUFFIX) BOOST_ICU_STOW = $(STOW_ROOT)/$(BOOST_ICU_DIR) BOOST_ICU_BUILD = $(PRODUCTS)/$(BOOST_ICU_DIR) @@ -43,7 +57,7 @@ cppunit-build: CFLAGS="$(CFLAGS)" \ LDFLAGS="$(LDFLAGS)" \ CC="$(CC)" CXX="$(CXX)" LD="$(LD)" \ - --prefix=$(STOW_ROOT)/cppunit-gcc$(CXX_VERSION) \ + --prefix=$(STOW_ROOT)/cppunit-$(DIR_SUFFIX) \ --disable-doxygen --disable-dot && \ make install) @@ -55,7 +69,7 @@ cppunit-build: # LDFLAGS="$(LDFLAGS)" \ # CC="$(CC)" CXX="$(CXX)" LD="$(LD)" \ # --enable-static --enable-debug \ -# --prefix=$(STOW_ROOT)/icu-gcc$(CXX_VERSION) && \ +# --prefix=$(STOW_ROOT)/icu-$(DIR_SUFFIX) && \ # make install) # #boost-icu-build: @@ -66,8 +80,8 @@ cppunit-build: clean: -rm -fr $(BOOST_STOW) $(BOOST_BUILD) -rm -fr $(BOOST_ICU_STOW) $(BOOST_ICU_BUILD) - -rm -fr $(STOW_ROOT)/cppunit-gcc$(CXX_VERSION) - -rm -fr $(STOW_ROOT)/icu-gcc$(CXX_VERSION) + -rm -fr $(STOW_ROOT)/cppunit-$(DIR_SUFFIX) + -rm -fr $(STOW_ROOT)/icu-$(DIR_SUFFIX) -(cd cppunit; make distclean) -(cd icu/source; make distclean) diff --git a/lib/utfcpp b/lib/utfcpp -Subproject 200cf64535a8a2545414e993349f6c87c8dd64b +Subproject e1100092ff1b6f9d0fd6af9c4f38360989f05e7 diff --git a/src/account.h b/src/account.h index 26ebe261..7a632b35 100644 --- a/src/account.h +++ b/src/account.h @@ -89,6 +89,10 @@ public: } ~account_t(); + virtual string description() { + return string(_("account ")) + fullname(); + } + operator string() const { return fullname(); } diff --git a/src/amount.cc b/src/amount.cc index 1fbc96c8..d5b7f03d 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -998,7 +998,8 @@ bool amount_t::parse(std::istream& in, const parse_flags_t& flags) if (! symbol.empty()) comm_flags |= COMMODITY_STYLE_SUFFIXED; - if (! in.eof() && ((n = static_cast<char>(in.peek())) != '\n')) + if (! flags.has_flags(PARSE_NO_ANNOT) && + ! in.eof() && ((n = static_cast<char>(in.peek())) != '\n')) details.parse(in); } } else { @@ -1010,7 +1011,8 @@ bool amount_t::parse(std::istream& in, const parse_flags_t& flags) parse_quantity(in, quant); - if (! quant.empty() && ! in.eof() && + if (! flags.has_flags(PARSE_NO_ANNOT) && + ! quant.empty() && ! in.eof() && ((n = static_cast<char>(in.peek())) != '\n')) details.parse(in); } diff --git a/src/amount.h b/src/amount.h index 1dfb4234..8a2ebf04 100644 --- a/src/amount.h +++ b/src/amount.h @@ -72,7 +72,7 @@ enum parse_flags_enum_t { PARSE_NO_MIGRATE = 0x04, PARSE_NO_REDUCE = 0x08, PARSE_NO_ASSIGN = 0x10, - PARSE_NO_DATES = 0x20, + PARSE_NO_ANNOT = 0x20, PARSE_OP_CONTEXT = 0x40, PARSE_SOFT_FAIL = 0x80 }; diff --git a/src/commodity.cc b/src/commodity.cc index 179bbc05..81183b25 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -490,16 +490,32 @@ commodity_t::operator bool() const return this != pool().null_commodity; } -bool commodity_t::symbol_needs_quotes(const string& symbol) -{ - foreach (char ch, symbol) - if (std::isspace(ch) || std::isdigit(ch) || ch == '-' || ch == '.') - return true; +namespace { + // Invalid commodity characters: + // SPACE, TAB, NEWLINE, RETURN + // 0-9 . , ; : ? ! - + * / ^ & | = + // < > { } [ ] ( ) @ - return false; -} + static int invalid_chars[256] = { + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + /* 00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, + /* 10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 20 */ 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, + /* 30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + /* 40 */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 50 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, + /* 60 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 70 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, + /* 80 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 90 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* a0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* b0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* c0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* d0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* e0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* f0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; -namespace { bool is_reserved_token(const char * buf) { switch (buf[0]) { @@ -524,33 +540,17 @@ namespace { } } -void commodity_t::parse_symbol(std::istream& in, string& symbol) +bool commodity_t::symbol_needs_quotes(const string& symbol) { - // Invalid commodity characters: - // SPACE, TAB, NEWLINE, RETURN - // 0-9 . , ; : ? ! - + * / ^ & | = - // < > { } [ ] ( ) @ + foreach (char ch, symbol) + if (invalid_chars[static_cast<unsigned char>(ch)]) + return true; - static int invalid_chars[256] = { - /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ - /* 00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, - /* 10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* 20 */ 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, - /* 30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - /* 40 */ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* 50 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, - /* 60 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* 70 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, - /* 80 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* 90 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* a0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* b0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* c0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* d0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* e0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* f0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - }; + return false; +} +void commodity_t::parse_symbol(std::istream& in, string& symbol) +{ istream_pos_type pos = in.tellg(); char buf[256]; diff --git a/src/compare.cc b/src/compare.cc index 99e430a7..12114c7d 100644 --- a/src/compare.cc +++ b/src/compare.cc @@ -42,8 +42,10 @@ void push_sort_value(std::list<sort_value_t>& sort_values, expr_t::ptr_op_t node, scope_t& scope) { if (node->kind == expr_t::op_t::O_CONS) { - push_sort_value(sort_values, node->left(), scope); - push_sort_value(sort_values, node->right(), scope); + while (node && node->kind == expr_t::op_t::O_CONS) { + push_sort_value(sort_values, node->left(), scope); + node = node->right(); + } } else { bool inverted = false; @@ -54,7 +56,7 @@ void push_sort_value(std::list<sort_value_t>& sort_values, sort_values.push_back(sort_value_t()); sort_values.back().inverted = inverted; - sort_values.back().value = expr_t(node).calc(scope).simplified(); + sort_values.back().value = expr_t(node).calc(scope).simplified(); if (sort_values.back().value.is_null()) throw_(calc_error, diff --git a/src/expr.cc b/src/expr.cc index 5bc537d9..b3d4abcd 100644 --- a/src/expr.cc +++ b/src/expr.cc @@ -33,6 +33,7 @@ #include "expr.h" #include "parser.h" +#include "scope.h" namespace ledger { @@ -162,4 +163,45 @@ void expr_t::dump(std::ostream& out) const if (ptr) ptr->dump(out, 0); } +value_t source_command(call_scope_t& args) +{ + std::istream * in = NULL; + scoped_ptr<ifstream> stream; + string pathname; + + if (args.has(0)) { + pathname = args.get<string>(0); + stream.reset(new ifstream(path(pathname))); + in = stream.get(); + } else { + pathname = "<stdin>"; + in = &std::cin; + } + + symbol_scope_t file_locals(args); + std::size_t linenum = 0; + char buf[4096]; + istream_pos_type pos; + + while (in->good() && ! in->eof()) { + pos = in->tellg(); + in->getline(buf, 4095); + linenum++; + + char * p = skip_ws(buf); + if (*p && *p != ';') { + try { + expr_t(p).calc(file_locals); + } + catch (const std::exception&) { + add_error_context(_("While parsing value expression on line %1:") + << linenum); + add_error_context(source_context(pathname, pos, in->tellg(), "> ")); + } + } + } + + return true; +} + } // namespace ledger @@ -160,6 +160,10 @@ inline value_t expr_value(expr_t::ptr_op_t op) { return temp; } +class call_scope_t; + +value_t source_command(call_scope_t& scope); + } // namespace ledger #endif // _EXPR_H diff --git a/src/flags.h b/src/flags.h index 03f47ff9..09b7eec4 100644 --- a/src/flags.h +++ b/src/flags.h @@ -112,11 +112,11 @@ public: } basic_flags_t(const T& bits) { TRACE_CTOR(basic_flags_t, "const T&"); - set_flags(bits); + supports_flags<T, U>::set_flags(bits); } basic_flags_t(const U& bits) { TRACE_CTOR(basic_flags_t, "const U&"); - set_flags(static_cast<T>(bits)); + supports_flags<T, U>::set_flags(static_cast<T>(bits)); } ~basic_flags_t() throw() { TRACE_DTOR(basic_flags_t); diff --git a/src/global.h b/src/global.h index 115183a5..6504230d 100644 --- a/src/global.h +++ b/src/global.h @@ -55,6 +55,10 @@ public: global_scope_t(char ** envp); ~global_scope_t(); + virtual string description() { + return _("global scope"); + } + void read_init(); void read_environment_settings(char * envp[]); strings_list read_command_arguments(scope_t& scope, strings_list args); @@ -108,10 +108,15 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth) scope.define(symbol_t::FUNCTION, left()->as_ident(), right()); break; case O_CALL: - if (left()->left()->is_ident()) - scope.define(symbol_t::FUNCTION, left()->left()->as_ident(), this); - else + if (left()->left()->is_ident()) { + ptr_op_t node(new op_t(op_t::O_LAMBDA)); + node->set_left(left()->right()); + node->set_right(right()); + + scope.define(symbol_t::FUNCTION, left()->left()->as_ident(), node); + } else { throw_(compile_error, _("Invalid function definition")); + } break; default: throw_(compile_error, _("Invalid function definition")); @@ -151,15 +156,21 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) break; case IDENT: { - if (! left()) + ptr_op_t definition = left(); + if (! definition) { + // If no definition was pre-compiled for this identifier, look it + // up in the current scope. + definition = scope.lookup(symbol_t::FUNCTION, as_ident()); + } + if (! definition) throw_(calc_error, _("Unknown identifier '%1'") << as_ident()); // Evaluating an identifier is the same as calling its definition // directly, so we create an empty call_scope_t to reflect the scope for // this implicit call. call_scope_t call_args(scope, locus, depth); - result = left()->compile(call_args, depth + 1) - ->calc(call_args, locus, depth + 1); + result = definition->compile(call_args, depth + 1) + ->calc(call_args, locus, depth + 1); check_type_context(scope, result); break; } @@ -177,74 +188,68 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth) break; } - case O_DEFINE: { + case O_LAMBDA: { call_scope_t& call_args(downcast<call_scope_t>(scope)); - std::size_t args_count = call_args.size(); - std::size_t args_index = 0; - - assert(left()->kind == O_CALL); + std::size_t args_count(call_args.size()); + std::size_t args_index(0); + symbol_scope_t call_scope(call_args); + ptr_op_t sym(left()); - for (ptr_op_t sym = left()->right(); - sym; - sym = sym->has_right() ? sym->right() : NULL) { + for (; sym; sym = sym->has_right() ? sym->right() : NULL) { ptr_op_t varname = sym; if (sym->kind == O_CONS) varname = sym->left(); - if (! varname->is_ident()) + if (! varname->is_ident()) { throw_(calc_error, _("Invalid function definition")); - else if (args_index == args_count) - scope.define(symbol_t::FUNCTION, varname->as_ident(), - wrap_value(false)); - else - scope.define(symbol_t::FUNCTION, varname->as_ident(), - wrap_value(call_args[args_index++])); + } + else if (args_index == args_count) { + call_scope.define(symbol_t::FUNCTION, varname->as_ident(), + wrap_value(NULL_VALUE)); + } + else { + DEBUG("expr.compile", + "Defining function parameter " << varname->as_ident()); + call_scope.define(symbol_t::FUNCTION, varname->as_ident(), + wrap_value(call_args[args_index++])); + } } if (args_index < args_count) throw_(calc_error, - _("Too many arguments in function call (saw %1)") << args_count); + _("Too few arguments in function call (saw %1)") << args_count); - result = right()->calc(scope, locus, depth + 1); + result = right()->calc(call_scope, locus, depth + 1); break; } case O_LOOKUP: { context_scope_t context_scope(scope, value_t::SCOPE); + bool scope_error = true; if (value_t obj = left()->calc(context_scope, locus, depth + 1)) { - if (obj.is_scope()) { - if (obj.as_scope() == NULL) { - throw_(calc_error, _("Left operand of . operator is NULL")); - } else { - scope_t& objscope(*obj.as_scope()); - if (ptr_op_t member = - objscope.lookup(symbol_t::FUNCTION, right()->as_ident())) { - result = member->calc(objscope, NULL, depth + 1); - break; - } - } + if (obj.is_scope() && obj.as_scope() != NULL) { + bind_scope_t bound_scope(scope, *obj.as_scope()); + result = right()->calc(bound_scope, locus, depth + 1); + scope_error = false; } } - if (right()->kind != IDENT) - throw_(calc_error, - _("Right operand of . operator must be an identifier")); - else - throw_(calc_error, - _("Failed to lookup member '%1'") << right()->as_ident()); + if (scope_error) + throw_(calc_error, _("Left operand does not evaluate to an object")); break; } case O_CALL: { call_scope_t call_args(scope, locus, depth); if (has_right()) - call_args.set_args(split_cons_expr(right()->kind == O_SEQ ? - right()->left() : right())); + call_args.set_args(split_cons_expr(right())); ptr_op_t func = left(); const string& name(func->as_ident()); func = func->left(); if (! func) + func = scope.lookup(symbol_t::FUNCTION, name); + if (! func) throw_(calc_error, _("Calling unknown function '%1'") << name); if (func->is_function()) @@ -467,6 +472,9 @@ bool expr_t::op_t::print(std::ostream& out, const context_t& context) const string symbol; + if (kind > TERMINALS && (kind != O_CALL && kind != O_DEFINE)) + out << '('; + switch (kind) { case VALUE: as_value().dump(out, context.relaxed); @@ -481,118 +489,94 @@ bool expr_t::op_t::print(std::ostream& out, const context_t& context) const break; case O_NOT: - out << "!("; + out << "! "; if (left() && left()->print(out, context)) found = true; - out << ")"; break; case O_NEG: - out << "-("; + out << "- "; if (left() && left()->print(out, context)) found = true; - out << ")"; break; case O_ADD: - out << "("; if (left() && left()->print(out, context)) found = true; out << " + "; if (has_right() && right()->print(out, context)) found = true; - out << ")"; break; case O_SUB: - out << "("; if (left() && left()->print(out, context)) found = true; out << " - "; if (has_right() && right()->print(out, context)) found = true; - out << ")"; break; case O_MUL: - out << "("; if (left() && left()->print(out, context)) found = true; out << " * "; if (has_right() && right()->print(out, context)) found = true; - out << ")"; break; case O_DIV: - out << "("; if (left() && left()->print(out, context)) found = true; out << " / "; if (has_right() && right()->print(out, context)) found = true; - out << ")"; break; case O_EQ: - out << "("; if (left() && left()->print(out, context)) found = true; out << " == "; if (has_right() && right()->print(out, context)) found = true; - out << ")"; break; case O_LT: - out << "("; if (left() && left()->print(out, context)) found = true; out << " < "; if (has_right() && right()->print(out, context)) found = true; - out << ")"; break; case O_LTE: - out << "("; if (left() && left()->print(out, context)) found = true; out << " <= "; if (has_right() && right()->print(out, context)) found = true; - out << ")"; break; case O_GT: - out << "("; if (left() && left()->print(out, context)) found = true; out << " > "; if (has_right() && right()->print(out, context)) found = true; - out << ")"; break; case O_GTE: - out << "("; if (left() && left()->print(out, context)) found = true; out << " >= "; if (has_right() && right()->print(out, context)) found = true; - out << ")"; break; case O_AND: - out << "("; if (left() && left()->print(out, context)) found = true; out << " & "; if (has_right() && right()->print(out, context)) found = true; - out << ")"; break; case O_OR: - out << "("; if (left() && left()->print(out, context)) found = true; out << " | "; if (has_right() && right()->print(out, context)) found = true; - out << ")"; break; case O_QUERY: @@ -616,15 +600,13 @@ bool expr_t::op_t::print(std::ostream& out, const context_t& context) const break; case O_SEQ: - out << "("; found = print_seq(out, this, context); - out << ")"; break; case O_DEFINE: if (left() && left()->print(out, context)) found = true; - out << " := "; + out << " = "; if (has_right() && right()->print(out, context)) found = true; break; @@ -637,11 +619,19 @@ bool expr_t::op_t::print(std::ostream& out, const context_t& context) const found = true; break; + case O_LAMBDA: + if (left() && left()->print(out, context)) + found = true; + out << " -> "; + if (has_right() && right()->print(out, context)) + found = true; + break; + case O_CALL: if (left() && left()->print(out, context)) found = true; if (has_right()) { - if (right()->kind == O_SEQ) { + if (right()->kind == O_CONS) { if (right()->print(out, context)) found = true; } else { @@ -669,6 +659,9 @@ bool expr_t::op_t::print(std::ostream& out, const context_t& context) const break; } + if (kind > TERMINALS && (kind != O_CALL && kind != O_DEFINE)) + out << ')'; + if (! symbol.empty()) { if (commodity_pool_t::current_pool->find(symbol)) out << '@'; @@ -708,6 +701,7 @@ void expr_t::op_t::dump(std::ostream& out, const int depth) const case O_DEFINE: out << "O_DEFINE"; break; case O_LOOKUP: out << "O_LOOKUP"; break; + case O_LAMBDA: out << "O_LAMBDA"; break; case O_CALL: out << "O_CALL"; break; case O_MATCH: out << "O_MATCH"; break; @@ -104,6 +104,7 @@ public: O_DEFINE, O_LOOKUP, + O_LAMBDA, O_CALL, O_MATCH, @@ -266,8 +267,10 @@ public: ostream_pos_type * end_pos; bool relaxed; - context_t(const ptr_op_t& _expr_op = NULL, - const ptr_op_t& _op_to_find = NULL, + context_t() : start_pos(NULL), end_pos(NULL), relaxed(false) {} + + context_t(const ptr_op_t& _expr_op, + const ptr_op_t& _op_to_find, ostream_pos_type * const _start_pos = NULL, ostream_pos_type * const _end_pos = NULL, const bool _relaxed = true) @@ -280,7 +283,7 @@ public: void dump(std::ostream& out, const int depth = 0) const; static ptr_op_t wrap_value(const value_t& val); - static ptr_op_t wrap_functor(const expr_t::func_t& fobj); + static ptr_op_t wrap_functor(expr_t::func_t fobj); #if defined(HAVE_BOOST_SERIALIZATION) private: @@ -326,7 +329,7 @@ inline expr_t::ptr_op_t expr_t::op_t::wrap_value(const value_t& val) { } inline expr_t::ptr_op_t -expr_t::op_t::wrap_functor(const expr_t::func_t& fobj) { +expr_t::op_t::wrap_functor(expr_t::func_t fobj) { ptr_op_t temp(new op_t(op_t::FUNCTION)); temp->set_function(fobj); return temp; diff --git a/src/output.cc b/src/output.cc index f53e60c9..9d2e0cb6 100644 --- a/src/output.cc +++ b/src/output.cc @@ -90,14 +90,10 @@ void format_posts::operator()(post_t& post) else out << '\n'; - value_scope_t val_scope(string_value(report_title)); - bind_scope_t inner_scope(bound_scope, val_scope); - - format_t group_title_format; - group_title_format - .parse_format(report.HANDLER(group_title_format_).str()); + value_scope_t val_scope(bound_scope, string_value(report_title)); + format_t group_title_format(report.HANDLER(group_title_format_).str()); - out << group_title_format(inner_scope); + out << group_title_format(val_scope); report_title = ""; } @@ -176,14 +172,10 @@ std::size_t format_accounts::post_account(account_t& account, const bool flat) else out << '\n'; - value_scope_t val_scope(string_value(report_title)); - bind_scope_t inner_scope(bound_scope, val_scope); - - format_t group_title_format; - group_title_format - .parse_format(report.HANDLER(group_title_format_).str()); + value_scope_t val_scope(bound_scope, string_value(report_title)); + format_t group_title_format(report.HANDLER(group_title_format_).str()); - out << group_title_format(inner_scope); + out << group_title_format(val_scope); report_title = ""; } diff --git a/src/parser.cc b/src/parser.cc index 9a1ebf6f..f0085295 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -65,9 +65,6 @@ expr_t::parser_t::parse_value_term(std::istream& in, push_token(tok); // let the parser see it again node->set_right(parse_value_expr(in, tflags.plus_flags(PARSE_SINGLE))); - - if (node->has_right() && node->right()->kind == op_t::O_CONS) - node->set_right(node->right()->left()); } else { push_token(tok); } @@ -78,12 +75,6 @@ expr_t::parser_t::parse_value_term(std::istream& in, node = parse_value_expr(in, tflags.plus_flags(PARSE_PARTIAL) .minus_flags(PARSE_SINGLE)); tok = next_token(in, tflags, ')'); - - if (node && node->kind == op_t::O_CONS) { - ptr_op_t prev(node); - node = new op_t(op_t::O_SEQ); - node->set_left(prev); - } break; default: @@ -241,15 +232,12 @@ expr_t::parser_t::parse_logic_expr(std::istream& in, if (node && ! tflags.has_flags(PARSE_SINGLE)) { while (true) { - op_t::kind_t kind = op_t::LAST; + op_t::kind_t kind = op_t::LAST; parse_flags_t _flags = tflags; - token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); - bool negate = false; + token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); + bool negate = false; switch (tok.kind) { - case token_t::DEFINE: - kind = op_t::O_DEFINE; - break; case token_t::EQUAL: if (tflags.has_flags(PARSE_NO_ASSIGN)) tok.rewind(in); @@ -431,39 +419,120 @@ expr_t::parser_t::parse_querycolon_expr(std::istream& in, } expr_t::ptr_op_t -expr_t::parser_t::parse_value_expr(std::istream& in, +expr_t::parser_t::parse_comma_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_querycolon_expr(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { - token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); + ptr_op_t next; + while (true) { + token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); - if (tok.kind == token_t::COMMA || tok.kind == token_t::SEMI) { - bool comma_op = tok.kind == token_t::COMMA; + if (tok.kind == token_t::COMMA) { + if (! next) { + ptr_op_t prev(node); + node = new op_t(op_t::O_CONS); + node->set_left(prev); + + next = node; + } + + token_t& ntok = next_token(in, tflags); + push_token(ntok); + if (ntok.kind == token_t::RPAREN) + break; + + ptr_op_t chain(new op_t(op_t::O_CONS)); + chain->set_left(parse_querycolon_expr(in, tflags)); + next->set_right(chain); + next = chain; + } else { + push_token(tok); + break; + } + } + } + + return node; +} + +expr_t::ptr_op_t +expr_t::parser_t::parse_lambda_expr(std::istream& in, + const parse_flags_t& tflags) const +{ + ptr_op_t node(parse_comma_expr(in, tflags)); + + if (node && ! tflags.has_flags(PARSE_SINGLE)) { + token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); + + if (tok.kind == token_t::ARROW) { ptr_op_t prev(node); - node = new op_t(comma_op ? op_t::O_CONS : op_t::O_SEQ); + node = new op_t(op_t::O_LAMBDA); node->set_left(prev); - node->set_right(parse_value_expr(in, tflags)); - if (! node->right()) - throw_(parse_error, - _("%1 operator not followed by argument") << tok.symbol); + node->set_right(parse_querycolon_expr(in, tflags)); + } else { + push_token(tok); + } + } - tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); + return node; +} + +expr_t::ptr_op_t +expr_t::parser_t::parse_assign_expr(std::istream& in, + const parse_flags_t& tflags) const +{ + ptr_op_t node(parse_lambda_expr(in, tflags)); + + if (node && ! tflags.has_flags(PARSE_SINGLE)) { + token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); + + if (tok.kind == token_t::ASSIGN) { + ptr_op_t prev(node); + node = new op_t(op_t::O_DEFINE); + node->set_left(prev); + node->set_right(parse_lambda_expr(in, tflags)); + } else { + push_token(tok); } + } + + return node; +} - if (tok.kind != token_t::TOK_EOF) { - if (tflags.has_flags(PARSE_PARTIAL)) +expr_t::ptr_op_t +expr_t::parser_t::parse_value_expr(std::istream& in, + const parse_flags_t& tflags) const +{ + ptr_op_t node(parse_assign_expr(in, tflags)); + + if (node && ! tflags.has_flags(PARSE_SINGLE)) { + ptr_op_t next; + while (true) { + token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); + + if (tok.kind == token_t::SEMI) { + if (! next) { + ptr_op_t prev(node); + node = new op_t(op_t::O_SEQ); + node->set_left(prev); + + next = node; + } + + ptr_op_t chain(new op_t(op_t::O_SEQ)); + chain->set_left(parse_assign_expr(in, tflags)); + + next->set_right(chain); + next = chain; + } else { push_token(tok); - else - tok.unexpected(); + break; + } } } - else if (! tflags.has_flags(PARSE_PARTIAL) && - ! tflags.has_flags(PARSE_SINGLE)) { - throw_(parse_error, _("Failed to parse value expression")); - } return node; } diff --git a/src/parser.h b/src/parser.h index 93ea994f..9a65765d 100644 --- a/src/parser.h +++ b/src/parser.h @@ -93,6 +93,12 @@ class expr_t::parser_t : public noncopyable const parse_flags_t& flags) const; ptr_op_t parse_querycolon_expr(std::istream& in, const parse_flags_t& flags) const; + ptr_op_t parse_comma_expr(std::istream& in, + const parse_flags_t& flags) const; + ptr_op_t parse_lambda_expr(std::istream& in, + const parse_flags_t& flags) const; + ptr_op_t parse_assign_expr(std::istream& in, + const parse_flags_t& flags) const; ptr_op_t parse_value_expr(std::istream& in, const parse_flags_t& flags) const; @@ -99,6 +99,16 @@ public: TRACE_DTOR(post_t); } + virtual string description() { + if (pos) { + std::ostringstream buf; + buf << _("posting at line %1") << pos->beg_line; + return buf.str(); + } else { + return string(_("generated posting")); + } + } + virtual bool has_tag(const string& tag, bool inherit = true) const; virtual bool has_tag(const mask_t& tag_mask, diff --git a/src/py_amount.cc b/src/py_amount.cc index 3cb9f0eb..0b9e0410 100644 --- a/src/py_amount.cc +++ b/src/py_amount.cc @@ -295,7 +295,6 @@ internal precision.")) .value("NoMigrate", PARSE_NO_MIGRATE) .value("NoReduce", PARSE_NO_REDUCE) .value("NoAssign", PARSE_NO_ASSIGN) - .value("NoDates", PARSE_NO_DATES) .value("OpContext", PARSE_OP_CONTEXT) .value("SoftFail", PARSE_SOFT_FAIL) ; diff --git a/src/py_item.cc b/src/py_item.cc index 0e95f24f..e37228fe 100644 --- a/src/py_item.cc +++ b/src/py_item.cc @@ -106,7 +106,7 @@ void export_item() #if 0 class_< item_t, bases<scope_t> > ("JournalItem", init<uint_least8_t>()) #else - class_< item_t > ("JournalItem", init<uint_least8_t>()) + class_< item_t, noncopyable > ("JournalItem", no_init) #endif #if 1 .add_property("flags", &supports_flags<>::flags, diff --git a/src/py_xact.cc b/src/py_xact.cc index 5ab0d90d..b7582854 100644 --- a/src/py_xact.cc +++ b/src/py_xact.cc @@ -82,7 +82,7 @@ using namespace boost::python; void export_xact() { - class_< xact_base_t, bases<item_t> > ("TransactionBase") + class_< xact_base_t, bases<item_t>, noncopyable > ("TransactionBase", no_init) .add_property("journal", make_getter(&xact_base_t::journal, return_internal_reference<>()), diff --git a/src/query.cc b/src/query.cc index bed6afae..5480336c 100644 --- a/src/query.cc +++ b/src/query.cc @@ -186,6 +186,8 @@ test_ident: return token_t(token_t::TOK_META); else if (ident == "show") return token_t(token_t::TOK_SHOW); + else if (ident == "only") + return token_t(token_t::TOK_ONLY); else if (ident == "bold") return token_t(token_t::TOK_BOLD); else if (ident == "for") @@ -249,6 +251,7 @@ query_t::parser_t::parse_query_term(query_t::lexer_t::token_t::kind_t tok_contex lexer_t::token_t tok = lexer.next_token(); switch (tok.kind) { case lexer_t::token_t::TOK_SHOW: + case lexer_t::token_t::TOK_ONLY: case lexer_t::token_t::TOK_BOLD: case lexer_t::token_t::TOK_FOR: case lexer_t::token_t::TOK_SINCE: @@ -452,9 +455,26 @@ query_t::parser_t::parse_query_expr(lexer_t::token_t::kind_t tok_context, lexer_t::token_t tok = lexer.peek_token(); while (tok.kind != lexer_t::token_t::END_REACHED) { switch (tok.kind) { - case lexer_t::token_t::TOK_SHOW: { + case lexer_t::token_t::TOK_SHOW: + case lexer_t::token_t::TOK_ONLY: + case lexer_t::token_t::TOK_BOLD: { lexer.next_token(); + kind_t kind; + switch (tok.kind) { + case lexer_t::token_t::TOK_SHOW: + kind = QUERY_SHOW; + break; + case lexer_t::token_t::TOK_ONLY: + kind = QUERY_ONLY; + break; + case lexer_t::token_t::TOK_BOLD: + kind = QUERY_BOLD; + break; + default: + break; + } + expr_t::ptr_op_t node; while (expr_t::ptr_op_t next = parse_or_expr(tok_context)) { if (! node) { @@ -470,25 +490,7 @@ query_t::parser_t::parse_query_expr(lexer_t::token_t::kind_t tok_context, if (node) query_map.insert (query_map_t::value_type - (QUERY_SHOW, predicate_t(node, what_to_keep).print_to_str())); - break; - } - - case lexer_t::token_t::TOK_BOLD: { - lexer.next_token(); - - expr_t::ptr_op_t node = parse_or_expr(tok_context); - while (expr_t::ptr_op_t next = parse_or_expr(tok_context)) { - expr_t::ptr_op_t prev(node); - node = new expr_t::op_t(expr_t::op_t::O_OR); - node->set_left(prev); - node->set_right(next); - } - - if (node) - query_map.insert - (query_map_t::value_type - (QUERY_BOLD, predicate_t(node, what_to_keep).print_to_str())); + (kind, predicate_t(node, what_to_keep).print_to_str())); break; } diff --git a/src/query.h b/src/query.h index 5a4651a0..b5b3b0fc 100644 --- a/src/query.h +++ b/src/query.h @@ -90,6 +90,7 @@ public: TOK_EXPR, TOK_SHOW, + TOK_ONLY, TOK_BOLD, TOK_FOR, TOK_SINCE, @@ -144,6 +145,7 @@ public: case TOK_META: return "TOK_META"; case TOK_EXPR: return "TOK_EXPR"; case TOK_SHOW: return "TOK_SHOW"; + case TOK_ONLY: return "TOK_ONLY"; case TOK_BOLD: return "TOK_BOLD"; case TOK_FOR: return "TOK_FOR"; case TOK_SINCE: return "TOK_SINCE"; @@ -170,6 +172,7 @@ public: case TOK_META: return "meta"; case TOK_EXPR: return "expr"; case TOK_SHOW: return "show"; + case TOK_ONLY: return "only"; case TOK_BOLD: return "bold"; case TOK_FOR: return "for"; case TOK_SINCE: return "since"; @@ -234,6 +237,7 @@ public: enum kind_t { QUERY_LIMIT, QUERY_SHOW, + QUERY_ONLY, QUERY_BOLD, QUERY_FOR }; diff --git a/src/report.cc b/src/report.cc index 9d733674..5c7bf01d 100644 --- a/src/report.cc +++ b/src/report.cc @@ -267,6 +267,11 @@ void report_t::parse_query_args(const value_t& args, const string& whence) DEBUG("report.predicate", "Limit predicate = " << HANDLER(limit_).str()); } + if (query.has_query(query_t::QUERY_ONLY)) { + HANDLER(only_).on(whence, query.get_query(query_t::QUERY_ONLY)); + DEBUG("report.predicate", "Only predicate = " << HANDLER(only_).str()); + } + if (query.has_query(query_t::QUERY_SHOW)) { HANDLER(display_).on(whence, query.get_query(query_t::QUERY_SHOW)); DEBUG("report.predicate", "Display predicate = " << HANDLER(display_).str()); @@ -544,17 +549,19 @@ value_t report_t::fn_trim(call_scope_t& args) } } +value_t report_t::fn_format(call_scope_t& args) +{ + format_t format(args.get<string>(0)); + std::ostringstream out; + out << format(args); + return string_value(out.str()); +} + value_t report_t::fn_print(call_scope_t& args) { - std::ostream& out(output_stream); - bool first = true; - for (std::size_t i = 0; i < args.size(); i++) { - if (first) - first = false; - else - out << ' '; - args[i].print(out); - } + for (std::size_t i = 0; i < args.size(); i++) + args[i].print(output_stream); + static_cast<std::ostream&>(output_stream) << std::endl; return true; } @@ -1173,6 +1180,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, case 'f': if (is_eq(p, "format_date")) return MAKE_FUNCTOR(report_t::fn_format_date); + else if (is_eq(p, "format")) + return MAKE_FUNCTOR(report_t::fn_format); else if (is_eq(p, "floor")) return MAKE_FUNCTOR(report_t::fn_floor); break; @@ -1444,6 +1453,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, case 's': if (is_eq(p, "stats") || is_eq(p, "stat")) return WRAP_FUNCTOR(report_statistics); + else if (is_eq(p, "source")) + return WRAP_FUNCTOR(source_command); break; case 'x': diff --git a/src/report.h b/src/report.h index 59a632e6..5c172ee9 100644 --- a/src/report.h +++ b/src/report.h @@ -125,6 +125,10 @@ public: output_stream.close(); } + virtual string description() { + return _("current report"); + } + void normalize_options(const string& verb); void normalize_period(); void parse_query_args(const value_t& args, const string& whence); @@ -147,6 +151,7 @@ public: value_t fn_is_seq(call_scope_t& scope); value_t fn_strip(call_scope_t& scope); value_t fn_trim(call_scope_t& scope); + value_t fn_format(call_scope_t& scope); value_t fn_print(call_scope_t& scope); value_t fn_scrub(call_scope_t& scope); value_t fn_quantity(call_scope_t& scope); @@ -389,6 +394,7 @@ public: " ansify_if(partial_account(options.flat), blue if color)," " bold if should_bold))\n%/" "%$1\n%/" + "%(prepend_width ? \" \" * prepend_width : \"\")" "--------------------\n"); }); @@ -446,6 +452,7 @@ public: " %(!options.flat ? depth_spacer : \"\")" "%-(ansify_if(partial_account(options.flat), blue if color))\n" "%/%$1 %$2 %$3 %$4\n%/" + "%(prepend_width ? \" \" * prepend_width : \"\")" "------------ ------------ ------------ -----\n"); }); @@ -464,6 +471,7 @@ public: " %(!options.flat ? depth_spacer : \"\")" "%-(ansify_if(partial_account(options.flat), blue if color))\n%/" "%$1 %$2 %$3\n%/" + "%(prepend_width ? \" \" * prepend_width : \"\")" "---------------- ---------------- ---------\n"); }); diff --git a/src/scope.h b/src/scope.h index dac6eba3..31b10f6b 100644 --- a/src/scope.h +++ b/src/scope.h @@ -109,6 +109,8 @@ public: TRACE_DTOR(scope_t); } + virtual string description() = 0; + virtual void define(const symbol_t::kind_t, const string&, expr_t::ptr_op_t) {} virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, @@ -191,6 +193,10 @@ public: TRACE_DTOR(bind_scope_t); } + virtual string description() { + return grandchild.description(); + } + virtual void define(const symbol_t::kind_t kind, const string& name, expr_t::ptr_op_t def) { parent->define(kind, name, def); @@ -262,6 +268,16 @@ public: TRACE_DTOR(symbol_scope_t); } + virtual string description() { + if (parent) + return parent->description(); +#if !defined(NO_ASSERTS) + else + assert(false); +#endif + return empty_string; + } + virtual void define(const symbol_t::kind_t kind, const string& name, expr_t::ptr_op_t def); @@ -299,6 +315,10 @@ public: TRACE_DTOR(context_scope_t); } + virtual string description() { + return parent->description(); + } + virtual value_t::type_t type_context() const { return value_type_context; } @@ -351,6 +371,10 @@ public: TRACE_DTOR(call_scope_t); } + virtual string description() { + return context_scope_t::description(); + } + void set_args(const value_t& _args) { args = _args; } @@ -605,7 +629,7 @@ call_scope_t::get<expr_t::ptr_op_t>(std::size_t index, bool) { return args[index].as_any<expr_t::ptr_op_t>(); } -class value_scope_t : public scope_t +class value_scope_t : public child_scope_t { value_t value; @@ -614,8 +638,13 @@ class value_scope_t : public scope_t } public: - value_scope_t(const value_t& _value) : value(_value) {} + value_scope_t(scope_t& _parent, const value_t& _value) + : child_scope_t(_parent), value(_value) {} + virtual string description() { + return parent->description(); + } + virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& name) { diff --git a/src/session.h b/src/session.h index 6de4b2dd..b8fd52f2 100644 --- a/src/session.h +++ b/src/session.h @@ -64,6 +64,10 @@ public: TRACE_DTOR(session_t); } + virtual string description() { + return _("current session"); + } + void set_flush_on_next_data_file(const bool truth) { flush_on_next_data_file = truth; } diff --git a/src/stream.cc b/src/stream.cc index 001e7760..5d4cf5e0 100644 --- a/src/stream.cc +++ b/src/stream.cc @@ -102,7 +102,11 @@ namespace { else { // parent close(pfd[0]); typedef iostreams::stream<iostreams::file_descriptor_sink> fdstream; +#if BOOST_VERSION >= 104400 + *os = new fdstream(pfd[1], iostreams::never_close_handle); +#else // BOOST_VERSION >= 104400 *os = new fdstream(pfd[1]); +#endif // BOOST_VERSION >= 104400 } return pfd[1]; #else diff --git a/src/system.hh.in b/src/system.hh.in index d9e664e3..6f709684 100644 --- a/src/system.hh.in +++ b/src/system.hh.in @@ -160,7 +160,6 @@ typedef std::ostream::pos_type ostream_pos_type; #include <boost/operators.hpp> #include <boost/optional.hpp> #include <boost/ptr_container/ptr_list.hpp> -#include <boost/ptr_container/serialize_ptr_deque.hpp> #include <boost/random/mersenne_twister.hpp> #include <boost/random/uniform_int.hpp> #include <boost/random/uniform_real.hpp> @@ -194,6 +193,7 @@ typedef std::ostream::pos_type ostream_pos_type; #include <boost/date_time/posix_time/time_serialize.hpp> #include <boost/date_time/gregorian/greg_serialize.hpp> +#include <boost/ptr_container/serialize_ptr_deque.hpp> namespace boost { namespace serialization { @@ -239,6 +239,10 @@ void serialize(Archive& ar, istream_pos_type& pos, const unsigned int) } // namespace serialization } // namespace boost +#else // HAVE_BOOST_SERIALIZATION + +#include <boost/ptr_container/ptr_deque.hpp> + #endif // HAVE_BOOST_SERIALIZATION #if defined(HAVE_BOOST_PYTHON) diff --git a/src/textual.cc b/src/textual.cc index 7ddb5251..3dbae9a1 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -112,6 +112,10 @@ namespace { ~instance_t(); + virtual string description() { + return _("textual parser"); + } + void parse(); std::streamsize read_line(char *& line); bool peek_whitespace_line() { diff --git a/src/token.cc b/src/token.cc index 67cff65e..199c3b3c 100644 --- a/src/token.cc +++ b/src/token.cc @@ -60,8 +60,7 @@ int expr_t::token_t::parse_reserved_word(std::istream& in) case 'd': if (std::strcmp(buf, "div") == 0) { symbol[0] = '/'; - symbol[1] = '/'; - symbol[2] = '\0'; + symbol[1] = '\0'; kind = KW_DIV; return 1; } @@ -69,9 +68,7 @@ int expr_t::token_t::parse_reserved_word(std::istream& in) case 'e': if (std::strcmp(buf, "else") == 0) { - symbol[0] = 'L'; - symbol[1] = 'S'; - symbol[2] = '\0'; + std::strcpy(symbol, "else"); kind = KW_ELSE; return 1; } @@ -79,6 +76,7 @@ int expr_t::token_t::parse_reserved_word(std::istream& in) case 'f': if (std::strcmp(buf, "false") == 0) { + std::strcpy(symbol, "false"); kind = VALUE; value = false; return 1; @@ -115,6 +113,7 @@ int expr_t::token_t::parse_reserved_word(std::istream& in) case 't': if (std::strcmp(buf, "true") == 0) { + std::strcpy(symbol, "true"); kind = VALUE; value = true; return 1; @@ -231,6 +230,7 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags, break; } +#if 0 case '{': { in.get(c); amount_t temp; @@ -243,6 +243,7 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags, value = temp; break; } +#endif case '!': in.get(c); @@ -268,6 +269,15 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags, case '-': in.get(c); + c = static_cast<char>(in.peek()); + if (c == '>') { + in.get(c); + symbol[1] = c; + symbol[2] = '\0'; + kind = ARROW; + length = 2; + break; + } kind = MINUS; break; case '+': @@ -287,14 +297,6 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags, case ':': in.get(c); c = static_cast<char>(in.peek()); - if (c == '=') { - in.get(c); - symbol[1] = c; - symbol[2] = '\0'; - kind = DEFINE; - length = 2; - break; - } kind = COLON; break; @@ -336,7 +338,7 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags, length = 2; break; } - kind = EQUAL; + kind = ASSIGN; break; case '<': @@ -403,6 +405,7 @@ void expr_t::token_t::next(std::istream& in, const parse_flags_t& pflags, // maximum displayed precision. parse_flags_t parse_flags; + parse_flags.add_flags(PARSE_NO_ANNOT); if (pflags.has_flags(PARSE_NO_MIGRATE)) parse_flags.add_flags(PARSE_NO_MIGRATE); if (pflags.has_flags(PARSE_NO_REDUCE)) diff --git a/src/token.h b/src/token.h index aae73837..13a799cb 100644 --- a/src/token.h +++ b/src/token.h @@ -56,6 +56,8 @@ struct expr_t::token_t : public noncopyable LPAREN, // ( RPAREN, // ) + LBRACE, // { + RBRACE, // } EQUAL, // == NEQUAL, // != @@ -64,7 +66,6 @@ struct expr_t::token_t : public noncopyable GREATER, // > GREATEREQ, // >= - DEFINE, // := ASSIGN, // = MATCH, // =~ NMATCH, // !~ @@ -72,6 +73,7 @@ struct expr_t::token_t : public noncopyable PLUS, // + STAR, // * SLASH, // / + ARROW, // -> KW_DIV, // div EXCLAM, // !, not @@ -94,7 +96,7 @@ struct expr_t::token_t : public noncopyable } kind; - char symbol[3]; + char symbol[6]; value_t value; std::size_t length; @@ -113,13 +115,10 @@ struct expr_t::token_t : public noncopyable } void clear() { - kind = UNKNOWN; - length = 0; - value = NULL_VALUE; - + kind = UNKNOWN; + length = 0; + value = NULL_VALUE; symbol[0] = '\0'; - symbol[1] = '\0'; - symbol[2] = '\0'; } int parse_reserved_word(std::istream& in); @@ -117,6 +117,16 @@ public: TRACE_DTOR(xact_t); } + virtual string description() { + if (pos) { + std::ostringstream buf; + buf << _("transaction at line %1") << pos->beg_line; + return buf.str(); + } else { + return string(_("generated transaction")); + } + } + virtual void add_post(post_t * post); string idstring() const; @@ -194,6 +204,16 @@ public: TRACE_DTOR(auto_xact_t); } + virtual string description() { + if (pos) { + std::ostringstream buf; + buf << _("automated transaction at line %1") << pos->beg_line; + return buf.str(); + } else { + return string(_("generated automated transaction")); + } + } + virtual void parse_tags(const char * p, scope_t&, bool overwrite_existing = true) { @@ -242,6 +262,16 @@ class period_xact_t : public xact_base_t TRACE_DTOR(period_xact_t); } + virtual string description() { + if (pos) { + std::ostringstream buf; + buf << _("periodic transaction at line %1") << pos->beg_line; + return buf.str(); + } else { + return string(_("generated periodic transaction")); + } + } + #if defined(HAVE_BOOST_SERIALIZATION) private: /** Serialization. */ diff --git a/test/baseline/feat-check.test b/test/baseline/feat-check.test new file mode 100644 index 00000000..4029dfcd --- /dev/null +++ b/test/baseline/feat-check.test @@ -0,0 +1,18 @@ +bal +<<< += /Checking/ + check account =~ /Foo/ + +2010-06-24 Sample + Expenses:Food $100 + Assets:Checking + +check account("Assets:Checking").all(account =~ /Expense/) +>>> + $-100 Assets:Checking + $100 Expenses:Food +-------------------- + 0 +>>>2 +Warning: Transaction check failed: account =~ /Foo/ +Warning: Check failed: account("Assets:Checking").all(account =~ /Expense/) diff --git a/test/baseline/opt-meta-width.test b/test/baseline/opt-meta-width.test index e69de29b..51fd09cf 100644 --- a/test/baseline/opt-meta-width.test +++ b/test/baseline/opt-meta-width.test @@ -0,0 +1,14 @@ +reg --meta Sample --meta-width=15 +<<< +2004/05/27 (100) Credit card company + ; This is an xact note! + ; Sample: Value + Liabilities:MasterCard $20.00 + ; This is a posting note! + ; Sample: Another Value + ; :MyTag: + Assets:Bank:Checking + ; :AnotherTag: +>>> +Another Value 04-May-27 Credit card com.. Liab:MasterCard $20.00 $20.00 +Value As:Ban:Checking $-20.00 0 diff --git a/test/baseline/opt-meta.test b/test/baseline/opt-meta.test index e69de29b..85f53003 100644 --- a/test/baseline/opt-meta.test +++ b/test/baseline/opt-meta.test @@ -0,0 +1,14 @@ +reg --meta Sample +<<< +2004/05/27 (100) Credit card company + ; This is an xact note! + ; Sample: Value + Liabilities:MasterCard $20.00 + ; This is a posting note! + ; Sample: Another Value + ; :MyTag: + Assets:Bank:Checking + ; :AnotherTag: +>>> +Another Value04-May-27 Credit card company Liabilities:MasterCard $20.00 $20.00 +Value Assets:Bank:Checking $-20.00 0 diff --git a/test/baseline/opt-now.test b/test/baseline/opt-now.test index e69de29b..c517a24c 100644 --- a/test/baseline/opt-now.test +++ b/test/baseline/opt-now.test @@ -0,0 +1,4 @@ +eval today --now=2009/01/01 +<<< +>>> +2009/01/01 diff --git a/test/baseline/opt-prepend-format.test b/test/baseline/opt-prepend-format.test index e69de29b..35b6e8d0 100644 --- a/test/baseline/opt-prepend-format.test +++ b/test/baseline/opt-prepend-format.test @@ -0,0 +1,17 @@ +bal --prepend-format "%(account_base)" +<<< +2007/02/02 RD VMMXX + Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 + Income:Dividends:Vanguard:VMMXX $-0.35 +>>> +VMMXX 0.350 VMMXX Assets:Investments:Vanguard:VMMXX +VMMXX $-0.35 Income:Dividends:Vanguard:VMMXX +-------------------- + $-0.35 + 0.350 VMMXX +=== 0 +reg --prepend-format "%(account_base)" +>>> +VMMXX07-Feb-02 RD VMMXX As:Inves:Vanguar:VMMXX 0.350 VMMXX 0.350 VMMXX +VMMXX In:Divid:Vanguar:VMMXX $-0.35 $-0.35 + 0.350 VMMXX diff --git a/test/baseline/opt-prepend-width.test b/test/baseline/opt-prepend-width.test index e69de29b..488f737b 100644 --- a/test/baseline/opt-prepend-width.test +++ b/test/baseline/opt-prepend-width.test @@ -0,0 +1,17 @@ +bal --prepend-format "%(account_base) " --prepend-width=10 +<<< +2007/02/02 RD VMMXX + Assets:Investments:Vanguard:VMMXX 0.350 VMMXX @ $1.00 + Income:Dividends:Vanguard:VMMXX $-0.35 +>>> + VMMXX 0.350 VMMXX Assets:Investments:Vanguard:VMMXX + VMMXX $-0.35 Income:Dividends:Vanguard:VMMXX + -------------------- + $-0.35 + 0.350 VMMXX +=== 0 +reg --prepend-format "%(account_base) " --prepend-width=10 +>>> + VMMXX 07-Feb-02 RD VMMXX As:Inves:Vanguar:VMMXX 0.350 VMMXX 0.350 VMMXX + VMMXX In:Divid:Vanguar:VMMXX $-0.35 $-0.35 + 0.350 VMMXX diff --git a/test/regress/7C44010B.test b/test/regress/7C44010B.test new file mode 100644 index 00000000..f0437588 --- /dev/null +++ b/test/regress/7C44010B.test @@ -0,0 +1,30 @@ +reg -X F -J Assets +<<< +D 1000,00 € + +1994/1/1 Company + Assets:Checking 10000 F + Income:Salary + +1998/1/1 Transfer + Assets:US account 200 $ + Assets:Checking -1000 F + +P 1998/12/31 $ 6 F + +1999/1/1 Books + Expenses:Books 200 $ + Assets:US account + +P 2002/1/1 € 6,55957 F + +2002/1/1 Company + Assets:Checking 2000 € + Income:Salary +>>> +1994-01-01 10000 +1998-01-01 11000 +1998-01-01 10000 +1998-12-31 10200 +1999-01-01 9000 +2002-01-01 22119.14 diff --git a/test/regress/9EB10714.test b/test/regress/9EB10714.test new file mode 100644 index 00000000..9fd49b7a --- /dev/null +++ b/test/regress/9EB10714.test @@ -0,0 +1,43 @@ +reg -J -V ^Actif +<<< +1994/01/01 Achat Rialto + Actif:Fixe:Rialto 2 Rialto + Actif:BNP -120000 € + Actif:BNP 120000 € + Revenu:Salaire -120000 € + +P 1995/01/01 Rialto 70000 € +P 1996/01/01 Rialto 90000 € +P 1997/01/01 Rialto 90000 € +P 1998/01/01 Rialto 105000 € +P 1999/01/01 Rialto 110000 € +P 2000/01/01 Rialto 120000 € +P 2001/01/01 Rialto 130000 € +P 2002/01/01 Rialto 140000 € +P 2003/01/01 Rialto 150000 € +P 2004/01/01 Rialto 160000 € +P 2005/01/01 Rialto 170000 € +P 2006/01/01 Rialto 180000 € +P 2007/01/01 Rialto 190000 € +P 2008/01/01 Rialto 200000 € +P 2009/01/01 Rialto 210000 € +P 2010/01/01 Rialto 211000 € +>>> +1994-01-01 120000 +1994-01-01 0 +1994-01-01 120000 +1995-01-01 140000 +1996-01-01 180000 +1998-01-01 210000 +1999-01-01 220000 +2000-01-01 240000 +2001-01-01 260000 +2002-01-01 280000 +2003-01-01 300000 +2004-01-01 320000 +2005-01-01 340000 +2006-01-01 360000 +2007-01-01 380000 +2008-01-01 400000 +2009-01-01 420000 +2010-01-01 422000 diff --git a/test/regress/A28CF697.test b/test/regress/A28CF697.test new file mode 100644 index 00000000..513dbd97 --- /dev/null +++ b/test/regress/A28CF697.test @@ -0,0 +1,9 @@ +print +<<< +2010-02-05 * Flight SN2094 + Assets:Rewards:Airmiles 125 "M&M" + Income:Rewards +>>> +2010/02/05 * Flight SN2094 + Assets:Rewards:Airmiles 125 "M&M" + Income:Rewards diff --git a/tools/configure.ac b/tools/configure.ac index 69874101..92a30431 100644 --- a/tools/configure.ac +++ b/tools/configure.ac @@ -309,10 +309,16 @@ AC_CACHE_CHECK( AC_LINK_IFELSE( [AC_LANG_PROGRAM( [[#include <boost/iostreams/device/file_descriptor.hpp> - #include <boost/iostreams/stream.hpp>]], + #include <boost/iostreams/stream.hpp> + #include <boost/version.hpp>]], [[namespace io = boost::iostreams; typedef io::stream<io::file_descriptor_sink> ofdstream; - ofdstream outstream(1);]])], +#if BOOST_VERSION >= 104400 + ofdstream outstream(1, io::never_close_handle); +#else // BOOST_VERSION >= 104400 + ofdstream outstream(1); +#endif // BOOST_VERSION >= 104400 + ]])], [boost_iostreams_cpplib_avail_cv_=true], [boost_iostreams_cpplib_avail_cv_=false]) AC_LANG_POP |