diff --git a/.gitignore b/.gitignore index bf1a793..1813863 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ build /cmake-build-debug/ .idea ml +.DS_Store diff --git a/CMakeLists.txt b/CMakeLists.txt index e2cb5ed..5e4e813 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,7 @@ set(PROJECT_NAME ml) set(SOURCE ml.cpp + ml_io.cpp clib/csvparser.cpp clib/sslclient.cpp clib/json11.cpp) diff --git a/Readme.md b/Readme.md index 820fa5b..0fd0946 100644 --- a/Readme.md +++ b/Readme.md @@ -1,13 +1,13 @@ ### TODO -- support for strings with " included +- move system_cmd into ml_io - logical operators - documentation - add url of source/inspiration to clib/*.cpp - load std lib when starting - add more command line args -- split into more files -- prejmenovat ivaluize +- add stdtest +- rename ivaluize - add benchmark - add some debug support?? - add instrumentation (time, nr of evals, debug info, debug environment etc) diff --git a/clib/csvparser.cpp b/clib/csvparser.cpp index fcbeb48..0a491d6 100644 --- a/clib/csvparser.cpp +++ b/clib/csvparser.cpp @@ -12,11 +12,13 @@ CsvParser::CsvParser(bool skip_hdr, char field_sep, char quote_ch, char line_sep header_skiped = false; } -void CsvParser::parseCSV(const std::string &csvSource, std::vector< std::vector > &lines) { +MlValue CsvParser::parseCSV(const std::string &csvSource) { bool inQuote(false); bool newLine(false); std::string field; - lines.clear(); + + // PERF optimize it for memory usage and performance + std::vector> parsed_data; // TODO some default size here std::vector line; std::string::const_iterator aChar = csvSource.begin(); @@ -38,7 +40,7 @@ void CsvParser::parseCSV(const std::string &csvSource, std::vector< std::vector< } else { if (newLine == false) { line.push_back(field); - addLine(line, lines); + addLine(line, parsed_data); field.clear(); line.clear(); newLine = true; @@ -55,37 +57,39 @@ void CsvParser::parseCSV(const std::string &csvSource, std::vector< std::vector< if (field.size()) line.push_back(field); - addLine(line, lines); + addLine(line, parsed_data); + + return ivalualize(parsed_data); } -MlValue CsvParser::ivalualize(std::vector< std::vector > &parsed_data) const { - int rows = parsed_data.size(); - int cols = rows > 0 ? parsed_data[0].size() : 0; +MlValue CsvParser::ivalualize(std::vector > &parsed_data) const { + int rows = parsed_data.size(); + int cols = rows > 0 ? parsed_data[0].size() : 0; - std::vector result; + std::vector result; - if (rows > 0 && cols > 0) { - for (int r = 0; r < rows; r++) { - std::vector row; - for (int c = 0; c < cols; c++) { - std::string value = parsed_data[r][c]; - if (is_string_int(value)) { - row.push_back(MlValue(stoi(value))); - } - if (is_string_float(value)) { - row.push_back(MlValue(std::stod(value))); - } else { - row.push_back(MlValue::string(value)); - } - } - result.push_back(row); - } - } + if (rows > 0 && cols > 0) { + for (int r = 0; r < rows; r++) { + std::vector row; + for (int c = 0; c < cols; c++) { + std::string value = parsed_data[r][c]; + if (is_string_int(value)) { + row.push_back(MlValue(stoi(value))); + } + if (is_string_float(value)) { + row.push_back(MlValue(std::stod(value))); + } else { + row.push_back(MlValue::string(value)); + } + } + result.push_back(row); + } + } - return result; + return result; } -void CsvParser::addLine(const std::vector &line, std::vector< std::vector > &lines) { +void CsvParser::addLine(const std::vector &line, std::vector > &lines) { if (skip_header && !header_skiped) { header_skiped = true; } else { @@ -101,10 +105,10 @@ std::regex double_regex("[0-9]+\\.[0-9]+"); // Is string representing int value bool CsvParser::is_string_int(const std::string &str) const { - return std::regex_match(str, int_regex); + return std::regex_match(str, int_regex); } // Is string representing float value bool CsvParser::is_string_float(const std::string &str) const { - return std::regex_match(str, double_regex); + return std::regex_match(str, double_regex); } diff --git a/clib/csvparser.h b/clib/csvparser.h index 2548906..2507ebf 100644 --- a/clib/csvparser.h +++ b/clib/csvparser.h @@ -8,23 +8,26 @@ class CsvParser { private: - char field_separator; - char line_separator; - char line_separator2; - char quote_character; + char field_separator; + char line_separator; + char line_separator2; + char quote_character; - bool skip_header; - bool header_skiped; + bool skip_header; + bool header_skiped; public: - CsvParser(bool skip_hdr = false, char field_sep = ',', char quote_ch = '"', char line_sep = '\r', char line_sep2 = '\n'); + CsvParser(bool skip_hdr = false, char field_sep = ',', char quote_ch = '"', char line_sep = '\r', + char line_sep2 = '\n'); - void parseCSV(const std::string &csvSource, std::vector< std::vector > &lines); + MlValue parseCSV(const std::string &csvSource); - MlValue ivalualize(std::vector< std::vector > &parsed_data) const; private: - void addLine(const std::vector &line, std::vector< std::vector > &lines); + void addLine(const std::vector &line, std::vector > &lines); + + MlValue ivalualize(std::vector > &parsed_data) const; bool is_string_int(const std::string &str) const; + bool is_string_float(const std::string &str) const; }; diff --git a/clib/date.h b/clib/date.h new file mode 100644 index 0000000..b152ca1 --- /dev/null +++ b/clib/date.h @@ -0,0 +1,8319 @@ +#ifndef DATE_H +#define DATE_H + +// The MIT License (MIT) +// +// Copyright (c) 2015, 2016, 2017 Howard Hinnant +// Copyright (c) 2016 Adrian Colomitchi +// Copyright (c) 2017 Florian Dang +// Copyright (c) 2017 Paul Thompson +// Copyright (c) 2018 Tomasz KamiƄski +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Our apologies. When the previous paragraph was written, lowercase had not yet +// been invented (that would involve another several millennia of evolution). +// We did not mean to shout. + +#ifndef HAS_STRING_VIEW +# if __cplusplus >= 201703 +# define HAS_STRING_VIEW 1 +# else +# define HAS_STRING_VIEW 0 +# endif +#endif // HAS_STRING_VIEW + +#include +#include +#include +#include +#include +#if !(__cplusplus >= 201402) +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if HAS_STRING_VIEW +# include +#endif +#include +#include + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wpedantic" +# if __GNUC__ < 5 + // GCC 4.9 Bug 61489 Wrong warning with -Wmissing-field-initializers +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +# endif +#endif + +namespace date +{ + +//---------------+ +// Configuration | +//---------------+ + +#ifndef ONLY_C_LOCALE +# define ONLY_C_LOCALE 0 +#endif + +#if defined(_MSC_VER) && (!defined(__clang__) || (_MSC_VER < 1910)) +// MSVC +# define _SILENCE_CXX17_UNCAUGHT_EXCEPTION_DEPRECATION_WARNING +# if _MSC_VER < 1910 +// before VS2017 +# define CONSTDATA const +# define CONSTCD11 +# define CONSTCD14 +# define NOEXCEPT _NOEXCEPT +# else +// VS2017 and later +# define CONSTDATA constexpr const +# define CONSTCD11 constexpr +# define CONSTCD14 constexpr +# define NOEXCEPT noexcept +# endif + +#elif defined(__SUNPRO_CC) && __SUNPRO_CC <= 0x5150 +// Oracle Developer Studio 12.6 and earlier +# define CONSTDATA constexpr const +# define CONSTCD11 constexpr +# define CONSTCD14 +# define NOEXCEPT noexcept + +#elif __cplusplus >= 201402 +// C++14 +# define CONSTDATA constexpr const +# define CONSTCD11 constexpr +# define CONSTCD14 constexpr +# define NOEXCEPT noexcept +#else +// C++11 +# define CONSTDATA constexpr const +# define CONSTCD11 constexpr +# define CONSTCD14 +# define NOEXCEPT noexcept +#endif + +#ifndef HAS_VOID_T +# if __cplusplus >= 201703 +# define HAS_VOID_T 1 +# else +# define HAS_VOID_T 0 +# endif +#endif // HAS_VOID_T + +// Protect from Oracle sun macro +#ifdef sun +# undef sun +#endif + +//-----------+ +// Interface | +//-----------+ + +// durations + +using days = std::chrono::duration + , std::chrono::hours::period>>; + +using weeks = std::chrono::duration + , days::period>>; + +using years = std::chrono::duration + , days::period>>; + +using months = std::chrono::duration + >>; + +// time_point + +template + using sys_time = std::chrono::time_point; + +using sys_days = sys_time; +using sys_seconds = sys_time; + +struct local_t {}; + +template + using local_time = std::chrono::time_point; + +using local_seconds = local_time; +using local_days = local_time; + +// types + +struct last_spec +{ + explicit last_spec() = default; +}; + +class day; +class month; +class year; + +class weekday; +class weekday_indexed; +class weekday_last; + +class month_day; +class month_day_last; +class month_weekday; +class month_weekday_last; + +class year_month; + +class year_month_day; +class year_month_day_last; +class year_month_weekday; +class year_month_weekday_last; + +// date composition operators + +CONSTCD11 year_month operator/(const year& y, const month& m) NOEXCEPT; +CONSTCD11 year_month operator/(const year& y, int m) NOEXCEPT; + +CONSTCD11 month_day operator/(const day& d, const month& m) NOEXCEPT; +CONSTCD11 month_day operator/(const day& d, int m) NOEXCEPT; +CONSTCD11 month_day operator/(const month& m, const day& d) NOEXCEPT; +CONSTCD11 month_day operator/(const month& m, int d) NOEXCEPT; +CONSTCD11 month_day operator/(int m, const day& d) NOEXCEPT; + +CONSTCD11 month_day_last operator/(const month& m, last_spec) NOEXCEPT; +CONSTCD11 month_day_last operator/(int m, last_spec) NOEXCEPT; +CONSTCD11 month_day_last operator/(last_spec, const month& m) NOEXCEPT; +CONSTCD11 month_day_last operator/(last_spec, int m) NOEXCEPT; + +CONSTCD11 month_weekday operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT; +CONSTCD11 month_weekday operator/(int m, const weekday_indexed& wdi) NOEXCEPT; +CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT; +CONSTCD11 month_weekday operator/(const weekday_indexed& wdi, int m) NOEXCEPT; + +CONSTCD11 month_weekday_last operator/(const month& m, const weekday_last& wdl) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(int m, const weekday_last& wdl) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, const month& m) NOEXCEPT; +CONSTCD11 month_weekday_last operator/(const weekday_last& wdl, int m) NOEXCEPT; + +CONSTCD11 year_month_day operator/(const year_month& ym, const day& d) NOEXCEPT; +CONSTCD11 year_month_day operator/(const year_month& ym, int d) NOEXCEPT; +CONSTCD11 year_month_day operator/(const year& y, const month_day& md) NOEXCEPT; +CONSTCD11 year_month_day operator/(int y, const month_day& md) NOEXCEPT; +CONSTCD11 year_month_day operator/(const month_day& md, const year& y) NOEXCEPT; +CONSTCD11 year_month_day operator/(const month_day& md, int y) NOEXCEPT; + +CONSTCD11 + year_month_day_last operator/(const year_month& ym, last_spec) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const year& y, const month_day_last& mdl) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(int y, const month_day_last& mdl) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const month_day_last& mdl, const year& y) NOEXCEPT; +CONSTCD11 + year_month_day_last operator/(const month_day_last& mdl, int y) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const year& y, const month_weekday& mwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(int y, const month_weekday& mwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const month_weekday& mwd, const year& y) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator/(const month_weekday& mwd, int y) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(int y, const month_weekday_last& mwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator/(const month_weekday_last& mwdl, int y) NOEXCEPT; + +// Detailed interface + +// day + +class day +{ + unsigned char d_; + +public: + day() = default; + explicit CONSTCD11 day(unsigned d) NOEXCEPT; + + CONSTCD14 day& operator++() NOEXCEPT; + CONSTCD14 day operator++(int) NOEXCEPT; + CONSTCD14 day& operator--() NOEXCEPT; + CONSTCD14 day operator--(int) NOEXCEPT; + + CONSTCD14 day& operator+=(const days& d) NOEXCEPT; + CONSTCD14 day& operator-=(const days& d) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator< (const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator> (const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const day& x, const day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const day& x, const day& y) NOEXCEPT; + +CONSTCD11 day operator+(const day& x, const days& y) NOEXCEPT; +CONSTCD11 day operator+(const days& x, const day& y) NOEXCEPT; +CONSTCD11 day operator-(const day& x, const days& y) NOEXCEPT; +CONSTCD11 days operator-(const day& x, const day& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const day& d); + +// month + +class month +{ + unsigned char m_; + +public: + month() = default; + explicit CONSTCD11 month(unsigned m) NOEXCEPT; + + CONSTCD14 month& operator++() NOEXCEPT; + CONSTCD14 month operator++(int) NOEXCEPT; + CONSTCD14 month& operator--() NOEXCEPT; + CONSTCD14 month operator--(int) NOEXCEPT; + + CONSTCD14 month& operator+=(const months& m) NOEXCEPT; + CONSTCD14 month& operator-=(const months& m) NOEXCEPT; + + CONSTCD11 explicit operator unsigned() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator< (const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator> (const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month& x, const month& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month& x, const month& y) NOEXCEPT; + +CONSTCD14 month operator+(const month& x, const months& y) NOEXCEPT; +CONSTCD14 month operator+(const months& x, const month& y) NOEXCEPT; +CONSTCD14 month operator-(const month& x, const months& y) NOEXCEPT; +CONSTCD14 months operator-(const month& x, const month& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month& m); + +// year + +class year +{ + short y_; + +public: + year() = default; + explicit CONSTCD11 year(int y) NOEXCEPT; + + CONSTCD14 year& operator++() NOEXCEPT; + CONSTCD14 year operator++(int) NOEXCEPT; + CONSTCD14 year& operator--() NOEXCEPT; + CONSTCD14 year operator--(int) NOEXCEPT; + + CONSTCD14 year& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 year operator-() const NOEXCEPT; + CONSTCD11 year operator+() const NOEXCEPT; + + CONSTCD11 bool is_leap() const NOEXCEPT; + + CONSTCD11 explicit operator int() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + + static CONSTCD11 year min() NOEXCEPT; + static CONSTCD11 year max() NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator< (const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator> (const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year& x, const year& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year& x, const year& y) NOEXCEPT; + +CONSTCD11 year operator+(const year& x, const years& y) NOEXCEPT; +CONSTCD11 year operator+(const years& x, const year& y) NOEXCEPT; +CONSTCD11 year operator-(const year& x, const years& y) NOEXCEPT; +CONSTCD11 years operator-(const year& x, const year& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year& y); + +// weekday + +class weekday +{ + unsigned char wd_; +public: + weekday() = default; + explicit CONSTCD11 weekday(unsigned wd) NOEXCEPT; + CONSTCD11 weekday(const sys_days& dp) NOEXCEPT; + CONSTCD11 explicit weekday(const local_days& dp) NOEXCEPT; + + CONSTCD14 weekday& operator++() NOEXCEPT; + CONSTCD14 weekday operator++(int) NOEXCEPT; + CONSTCD14 weekday& operator--() NOEXCEPT; + CONSTCD14 weekday operator--(int) NOEXCEPT; + + CONSTCD14 weekday& operator+=(const days& d) NOEXCEPT; + CONSTCD14 weekday& operator-=(const days& d) NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; + + CONSTCD11 weekday_indexed operator[](unsigned index) const NOEXCEPT; + CONSTCD11 weekday_last operator[](last_spec) const NOEXCEPT; + +private: + static CONSTCD11 unsigned char weekday_from_days(int z) NOEXCEPT; + + friend CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; + friend CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; + friend CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; + template + friend std::basic_ostream& + operator<<(std::basic_ostream& os, const weekday& wd); + friend class weekday_indexed; +}; + +CONSTCD11 bool operator==(const weekday& x, const weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday& x, const weekday& y) NOEXCEPT; + +CONSTCD14 weekday operator+(const weekday& x, const days& y) NOEXCEPT; +CONSTCD14 weekday operator+(const days& x, const weekday& y) NOEXCEPT; +CONSTCD14 weekday operator-(const weekday& x, const days& y) NOEXCEPT; +CONSTCD14 days operator-(const weekday& x, const weekday& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday& wd); + +// weekday_indexed + +class weekday_indexed +{ + unsigned char wd_ : 4; + unsigned char index_ : 4; + +public: + weekday_indexed() = default; + CONSTCD11 weekday_indexed(const date::weekday& wd, unsigned index) NOEXCEPT; + + CONSTCD11 date::weekday weekday() const NOEXCEPT; + CONSTCD11 unsigned index() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday_indexed& wdi); + +// weekday_last + +class weekday_last +{ + date::weekday wd_; + +public: + explicit CONSTCD11 weekday_last(const date::weekday& wd) NOEXCEPT; + + CONSTCD11 date::weekday weekday() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT; +CONSTCD11 bool operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday_last& wdl); + +namespace detail +{ + +struct unspecified_month_disambiguator {}; + +} // namespace detail + +// year_month + +class year_month +{ + date::year y_; + date::month m_; + +public: + year_month() = default; + CONSTCD11 year_month(const date::year& y, const date::month& m) NOEXCEPT; + + CONSTCD11 date::year year() const NOEXCEPT; + CONSTCD11 date::month month() const NOEXCEPT; + + template + CONSTCD14 year_month& operator+=(const months& dm) NOEXCEPT; + template + CONSTCD14 year_month& operator-=(const months& dm) NOEXCEPT; + CONSTCD14 year_month& operator+=(const years& dy) NOEXCEPT; + CONSTCD14 year_month& operator-=(const years& dy) NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_month& x, const year_month& y) NOEXCEPT; + +template +CONSTCD14 year_month operator+(const year_month& ym, const months& dm) NOEXCEPT; +template +CONSTCD14 year_month operator+(const months& dm, const year_month& ym) NOEXCEPT; +template +CONSTCD14 year_month operator-(const year_month& ym, const months& dm) NOEXCEPT; + +CONSTCD11 months operator-(const year_month& x, const year_month& y) NOEXCEPT; +CONSTCD11 year_month operator+(const year_month& ym, const years& dy) NOEXCEPT; +CONSTCD11 year_month operator+(const years& dy, const year_month& ym) NOEXCEPT; +CONSTCD11 year_month operator-(const year_month& ym, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month& ym); + +// month_day + +class month_day +{ + date::month m_; + date::day d_; + +public: + month_day() = default; + CONSTCD11 month_day(const date::month& m, const date::day& d) NOEXCEPT; + + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::day day() const NOEXCEPT; + + CONSTCD14 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator< (const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator> (const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month_day& x, const month_day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month_day& x, const month_day& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_day& md); + +// month_day_last + +class month_day_last +{ + date::month m_; + +public: + CONSTCD11 explicit month_day_last(const date::month& m) NOEXCEPT; + + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator< (const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator> (const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT; +CONSTCD11 bool operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_day_last& mdl); + +// month_weekday + +class month_weekday +{ + date::month m_; + date::weekday_indexed wdi_; +public: + CONSTCD11 month_weekday(const date::month& m, + const date::weekday_indexed& wdi) NOEXCEPT; + + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::weekday_indexed weekday_indexed() const NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT; +CONSTCD11 bool operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_weekday& mwd); + +// month_weekday_last + +class month_weekday_last +{ + date::month m_; + date::weekday_last wdl_; + +public: + CONSTCD11 month_weekday_last(const date::month& m, + const date::weekday_last& wd) NOEXCEPT; + + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::weekday_last weekday_last() const NOEXCEPT; + + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_weekday_last& mwdl); + +// class year_month_day + +class year_month_day +{ + date::year y_; + date::month m_; + date::day d_; + +public: + year_month_day() = default; + CONSTCD11 year_month_day(const date::year& y, const date::month& m, + const date::day& d) NOEXCEPT; + CONSTCD14 year_month_day(const year_month_day_last& ymdl) NOEXCEPT; + + CONSTCD14 year_month_day(sys_days dp) NOEXCEPT; + CONSTCD14 explicit year_month_day(local_days dp) NOEXCEPT; + + template + CONSTCD14 year_month_day& operator+=(const months& m) NOEXCEPT; + template + CONSTCD14 year_month_day& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_day& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 date::year year() const NOEXCEPT; + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::day day() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD14 bool ok() const NOEXCEPT; + +private: + static CONSTCD14 year_month_day from_days(days dp) NOEXCEPT; + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 bool operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator< (const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator> (const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT; +CONSTCD11 bool operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT; + +template +CONSTCD14 year_month_day operator+(const year_month_day& ymd, const months& dm) NOEXCEPT; +template +CONSTCD14 year_month_day operator+(const months& dm, const year_month_day& ymd) NOEXCEPT; +template +CONSTCD14 year_month_day operator-(const year_month_day& ymd, const months& dm) NOEXCEPT; +CONSTCD11 year_month_day operator+(const year_month_day& ymd, const years& dy) NOEXCEPT; +CONSTCD11 year_month_day operator+(const years& dy, const year_month_day& ymd) NOEXCEPT; +CONSTCD11 year_month_day operator-(const year_month_day& ymd, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_day& ymd); + +// year_month_day_last + +class year_month_day_last +{ + date::year y_; + date::month_day_last mdl_; + +public: + CONSTCD11 year_month_day_last(const date::year& y, + const date::month_day_last& mdl) NOEXCEPT; + + template + CONSTCD14 year_month_day_last& operator+=(const months& m) NOEXCEPT; + template + CONSTCD14 year_month_day_last& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_day_last& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_day_last& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 date::year year() const NOEXCEPT; + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::month_day_last month_day_last() const NOEXCEPT; + CONSTCD14 date::day day() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator< (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator> (const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; +CONSTCD11 + bool operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT; + +template +CONSTCD14 +year_month_day_last +operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; + +template +CONSTCD14 +year_month_day_last +operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT; + +template +CONSTCD14 +year_month_day_last +operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_day_last +operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_day_last& ymdl); + +// year_month_weekday + +class year_month_weekday +{ + date::year y_; + date::month m_; + date::weekday_indexed wdi_; + +public: + year_month_weekday() = default; + CONSTCD11 year_month_weekday(const date::year& y, const date::month& m, + const date::weekday_indexed& wdi) NOEXCEPT; + CONSTCD14 year_month_weekday(const sys_days& dp) NOEXCEPT; + CONSTCD14 explicit year_month_weekday(const local_days& dp) NOEXCEPT; + + template + CONSTCD14 year_month_weekday& operator+=(const months& m) NOEXCEPT; + template + CONSTCD14 year_month_weekday& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_weekday& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 date::year year() const NOEXCEPT; + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::weekday weekday() const NOEXCEPT; + CONSTCD11 unsigned index() const NOEXCEPT; + CONSTCD11 date::weekday_indexed weekday_indexed() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD14 bool ok() const NOEXCEPT; + +private: + static CONSTCD14 year_month_weekday from_days(days dp) NOEXCEPT; + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 + bool operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; +CONSTCD11 + bool operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT; + +template +CONSTCD14 +year_month_weekday +operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; + +template +CONSTCD14 +year_month_weekday +operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT; + +template +CONSTCD14 +year_month_weekday +operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_weekday +operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi); + +// year_month_weekday_last + +class year_month_weekday_last +{ + date::year y_; + date::month m_; + date::weekday_last wdl_; + +public: + CONSTCD11 year_month_weekday_last(const date::year& y, const date::month& m, + const date::weekday_last& wdl) NOEXCEPT; + + template + CONSTCD14 year_month_weekday_last& operator+=(const months& m) NOEXCEPT; + template + CONSTCD14 year_month_weekday_last& operator-=(const months& m) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator+=(const years& y) NOEXCEPT; + CONSTCD14 year_month_weekday_last& operator-=(const years& y) NOEXCEPT; + + CONSTCD11 date::year year() const NOEXCEPT; + CONSTCD11 date::month month() const NOEXCEPT; + CONSTCD11 date::weekday weekday() const NOEXCEPT; + CONSTCD11 date::weekday_last weekday_last() const NOEXCEPT; + + CONSTCD14 operator sys_days() const NOEXCEPT; + CONSTCD14 explicit operator local_days() const NOEXCEPT; + CONSTCD11 bool ok() const NOEXCEPT; + +private: + CONSTCD14 days to_days() const NOEXCEPT; +}; + +CONSTCD11 +bool +operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; + +CONSTCD11 +bool +operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT; + +template +CONSTCD14 +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; + +template +CONSTCD14 +year_month_weekday_last +operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT; + +template +CONSTCD14 +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT; + +CONSTCD11 +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT; + +template +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl); + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +inline namespace literals +{ + +CONSTCD11 date::day operator "" _d(unsigned long long d) NOEXCEPT; +CONSTCD11 date::year operator "" _y(unsigned long long y) NOEXCEPT; + +} // inline namespace literals +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + +// CONSTDATA date::month January{1}; +// CONSTDATA date::month February{2}; +// CONSTDATA date::month March{3}; +// CONSTDATA date::month April{4}; +// CONSTDATA date::month May{5}; +// CONSTDATA date::month June{6}; +// CONSTDATA date::month July{7}; +// CONSTDATA date::month August{8}; +// CONSTDATA date::month September{9}; +// CONSTDATA date::month October{10}; +// CONSTDATA date::month November{11}; +// CONSTDATA date::month December{12}; +// +// CONSTDATA date::weekday Sunday{0u}; +// CONSTDATA date::weekday Monday{1u}; +// CONSTDATA date::weekday Tuesday{2u}; +// CONSTDATA date::weekday Wednesday{3u}; +// CONSTDATA date::weekday Thursday{4u}; +// CONSTDATA date::weekday Friday{5u}; +// CONSTDATA date::weekday Saturday{6u}; + +#if HAS_VOID_T + +template > +struct is_clock + : std::false_type +{}; + +template +struct is_clock> + : std::true_type +{}; + +#endif // HAS_VOID_T + +//----------------+ +// Implementation | +//----------------+ + +// utilities +namespace detail { + +template> +class save_istream +{ +protected: + std::basic_ios& is_; + CharT fill_; + std::ios::fmtflags flags_; + std::streamsize width_; + std::basic_ostream* tie_; + std::locale loc_; + +public: + ~save_istream() + { + is_.fill(fill_); + is_.flags(flags_); + is_.width(width_); + is_.imbue(loc_); + is_.tie(tie_); + } + + save_istream(const save_istream&) = delete; + save_istream& operator=(const save_istream&) = delete; + + explicit save_istream(std::basic_ios& is) + : is_(is) + , fill_(is.fill()) + , flags_(is.flags()) + , width_(is.width(0)) + , tie_(is.tie(nullptr)) + , loc_(is.getloc()) + { + if (tie_ != nullptr) + tie_->flush(); + } +}; + +template> +class save_ostream + : private save_istream +{ +public: + ~save_ostream() + { + if ((this->flags_ & std::ios::unitbuf) && +#if __cplusplus >= 201703 + std::uncaught_exceptions() == 0 && +#else + !std::uncaught_exception() && +#endif + this->is_.good()) + this->is_.rdbuf()->pubsync(); + } + + save_ostream(const save_ostream&) = delete; + save_ostream& operator=(const save_ostream&) = delete; + + explicit save_ostream(std::basic_ios& os) + : save_istream(os) + { + } +}; + +template +struct choose_trunc_type +{ + static const int digits = std::numeric_limits::digits; + using type = typename std::conditional + < + digits < 32, + std::int32_t, + typename std::conditional + < + digits < 64, + std::int64_t, +#ifdef __SIZEOF_INT128__ + __int128 +#else + std::int64_t +#endif + >::type + >::type; +}; + +template +CONSTCD11 +inline +typename std::enable_if +< + !std::chrono::treat_as_floating_point::value, + T +>::type +trunc(T t) NOEXCEPT +{ + return t; +} + +template +CONSTCD14 +inline +typename std::enable_if +< + std::chrono::treat_as_floating_point::value, + T +>::type +trunc(T t) NOEXCEPT +{ + using namespace std; + using I = typename choose_trunc_type::type; + CONSTDATA auto digits = numeric_limits::digits; + static_assert(digits < numeric_limits::digits, ""); + CONSTDATA auto max = I{1} << (digits-1); + CONSTDATA auto min = -max; + const auto negative = t < T{0}; + if (min <= t && t <= max && t != 0 && t == t) + { + t = static_cast(static_cast(t)); + if (t == 0 && negative) + t = -t; + } + return t; +} + +template +struct static_gcd +{ + static const std::intmax_t value = static_gcd::value; +}; + +template +struct static_gcd +{ + static const std::intmax_t value = Xp; +}; + +template <> +struct static_gcd<0, 0> +{ + static const std::intmax_t value = 1; +}; + +template +struct no_overflow +{ +private: + static const std::intmax_t gcd_n1_n2 = static_gcd::value; + static const std::intmax_t gcd_d1_d2 = static_gcd::value; + static const std::intmax_t n1 = R1::num / gcd_n1_n2; + static const std::intmax_t d1 = R1::den / gcd_d1_d2; + static const std::intmax_t n2 = R2::num / gcd_n1_n2; + static const std::intmax_t d2 = R2::den / gcd_d1_d2; + static const std::intmax_t max = -((std::intmax_t(1) << + (sizeof(std::intmax_t) * CHAR_BIT - 1)) + 1); + + template + struct mul // overflow == false + { + static const std::intmax_t value = Xp * Yp; + }; + + template + struct mul + { + static const std::intmax_t value = 1; + }; + +public: + static const bool value = (n1 <= max / d2) && (n2 <= max / d1); + typedef std::ratio::value, + mul::value> type; +}; + +} // detail + +// trunc towards zero +template +CONSTCD11 +inline +typename std::enable_if +< + detail::no_overflow::value, + To +>::type +trunc(const std::chrono::duration& d) +{ + return To{detail::trunc(std::chrono::duration_cast(d).count())}; +} + +template +CONSTCD11 +inline +typename std::enable_if +< + !detail::no_overflow::value, + To +>::type +trunc(const std::chrono::duration& d) +{ + using namespace std::chrono; + using rep = typename std::common_type::type; + return To{detail::trunc(duration_cast(duration_cast>(d)).count())}; +} + +#ifndef HAS_CHRONO_ROUNDING +# if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 190023918 || (_MSC_FULL_VER >= 190000000 && defined (__clang__))) +# define HAS_CHRONO_ROUNDING 1 +# elif defined(__cpp_lib_chrono) && __cplusplus > 201402 && __cpp_lib_chrono >= 201510 +# define HAS_CHRONO_ROUNDING 1 +# elif defined(_LIBCPP_VERSION) && __cplusplus > 201402 && _LIBCPP_VERSION >= 3800 +# define HAS_CHRONO_ROUNDING 1 +# else +# define HAS_CHRONO_ROUNDING 0 +# endif +#endif // HAS_CHRONO_ROUNDING + +#if HAS_CHRONO_ROUNDING == 0 + +// round down +template +CONSTCD14 +inline +typename std::enable_if +< + detail::no_overflow::value, + To +>::type +floor(const std::chrono::duration& d) +{ + auto t = trunc(d); + if (t > d) + return t - To{1}; + return t; +} + +template +CONSTCD14 +inline +typename std::enable_if +< + !detail::no_overflow::value, + To +>::type +floor(const std::chrono::duration& d) +{ + using namespace std::chrono; + using rep = typename std::common_type::type; + return floor(floor>(d)); +} + +// round to nearest, to even on tie +template +CONSTCD14 +inline +To +round(const std::chrono::duration& d) +{ + auto t0 = floor(d); + auto t1 = t0 + To{1}; + if (t1 == To{0} && t0 < To{0}) + t1 = -t1; + auto diff0 = d - t0; + auto diff1 = t1 - d; + if (diff0 == diff1) + { + if (t0 - trunc(t0/2)*2 == To{0}) + return t0; + return t1; + } + if (diff0 < diff1) + return t0; + return t1; +} + +// round up +template +CONSTCD14 +inline +To +ceil(const std::chrono::duration& d) +{ + auto t = trunc(d); + if (t < d) + return t + To{1}; + return t; +} + +template ::is_signed + >::type> +CONSTCD11 +std::chrono::duration +abs(std::chrono::duration d) +{ + return d >= d.zero() ? d : -d; +} + +// round down +template +CONSTCD11 +inline +std::chrono::time_point +floor(const std::chrono::time_point& tp) +{ + using std::chrono::time_point; + return time_point{date::floor(tp.time_since_epoch())}; +} + +// round to nearest, to even on tie +template +CONSTCD11 +inline +std::chrono::time_point +round(const std::chrono::time_point& tp) +{ + using std::chrono::time_point; + return time_point{round(tp.time_since_epoch())}; +} + +// round up +template +CONSTCD11 +inline +std::chrono::time_point +ceil(const std::chrono::time_point& tp) +{ + using std::chrono::time_point; + return time_point{ceil(tp.time_since_epoch())}; +} + +#else // HAS_CHRONO_ROUNDING == 1 + +using std::chrono::floor; +using std::chrono::ceil; +using std::chrono::round; +using std::chrono::abs; + +#endif // HAS_CHRONO_ROUNDING + +// trunc towards zero +template +CONSTCD11 +inline +std::chrono::time_point +trunc(const std::chrono::time_point& tp) +{ + using std::chrono::time_point; + return time_point{trunc(tp.time_since_epoch())}; +} + +// day + +CONSTCD11 inline day::day(unsigned d) NOEXCEPT : d_(static_cast(d)) {} +CONSTCD14 inline day& day::operator++() NOEXCEPT {++d_; return *this;} +CONSTCD14 inline day day::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline day& day::operator--() NOEXCEPT {--d_; return *this;} +CONSTCD14 inline day day::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} +CONSTCD14 inline day& day::operator+=(const days& d) NOEXCEPT {*this = *this + d; return *this;} +CONSTCD14 inline day& day::operator-=(const days& d) NOEXCEPT {*this = *this - d; return *this;} +CONSTCD11 inline day::operator unsigned() const NOEXCEPT {return d_;} +CONSTCD11 inline bool day::ok() const NOEXCEPT {return 1 <= d_ && d_ <= 31;} + +CONSTCD11 +inline +bool +operator==(const day& x, const day& y) NOEXCEPT +{ + return static_cast(x) == static_cast(y); +} + +CONSTCD11 +inline +bool +operator!=(const day& x, const day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const day& x, const day& y) NOEXCEPT +{ + return static_cast(x) < static_cast(y); +} + +CONSTCD11 +inline +bool +operator>(const day& x, const day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const day& x, const day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const day& x, const day& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +days +operator-(const day& x, const day& y) NOEXCEPT +{ + return days{static_cast(static_cast(x) + - static_cast(y))}; +} + +CONSTCD11 +inline +day +operator+(const day& x, const days& y) NOEXCEPT +{ + return day{static_cast(x) + static_cast(y.count())}; +} + +CONSTCD11 +inline +day +operator+(const days& x, const day& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD11 +inline +day +operator-(const day& x, const days& y) NOEXCEPT +{ + return x + -y; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const day& d) +{ + detail::save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << static_cast(d); + if (!d.ok()) + os << " is not a valid day"; + return os; +} + +// month + +CONSTCD11 inline month::month(unsigned m) NOEXCEPT : m_(static_cast(m)) {} +CONSTCD14 inline month& month::operator++() NOEXCEPT {*this += months{1}; return *this;} +CONSTCD14 inline month month::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline month& month::operator--() NOEXCEPT {*this -= months{1}; return *this;} +CONSTCD14 inline month month::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} + +CONSTCD14 +inline +month& +month::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +CONSTCD14 +inline +month& +month::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD11 inline month::operator unsigned() const NOEXCEPT {return m_;} +CONSTCD11 inline bool month::ok() const NOEXCEPT {return 1 <= m_ && m_ <= 12;} + +CONSTCD11 +inline +bool +operator==(const month& x, const month& y) NOEXCEPT +{ + return static_cast(x) == static_cast(y); +} + +CONSTCD11 +inline +bool +operator!=(const month& x, const month& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month& x, const month& y) NOEXCEPT +{ + return static_cast(x) < static_cast(y); +} + +CONSTCD11 +inline +bool +operator>(const month& x, const month& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month& x, const month& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month& x, const month& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD14 +inline +months +operator-(const month& x, const month& y) NOEXCEPT +{ + auto const d = static_cast(x) - static_cast(y); + return months(d <= 11 ? d : d + 12); +} + +CONSTCD14 +inline +month +operator+(const month& x, const months& y) NOEXCEPT +{ + auto const mu = static_cast(static_cast(x)) + (y.count() - 1); + auto const yr = (mu >= 0 ? mu : mu-11) / 12; + return month{static_cast(mu - yr * 12 + 1)}; +} + +CONSTCD14 +inline +month +operator+(const months& x, const month& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD14 +inline +month +operator-(const month& x, const months& y) NOEXCEPT +{ + return x + -y; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month& m) +{ + if (m.ok()) + { + CharT fmt[] = {'%', 'b', 0}; + os << format(os.getloc(), fmt, m); + } + else + os << static_cast(m) << " is not a valid month"; + return os; +} + +// year + +CONSTCD11 inline year::year(int y) NOEXCEPT : y_(static_cast(y)) {} +CONSTCD14 inline year& year::operator++() NOEXCEPT {++y_; return *this;} +CONSTCD14 inline year year::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline year& year::operator--() NOEXCEPT {--y_; return *this;} +CONSTCD14 inline year year::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} +CONSTCD14 inline year& year::operator+=(const years& y) NOEXCEPT {*this = *this + y; return *this;} +CONSTCD14 inline year& year::operator-=(const years& y) NOEXCEPT {*this = *this - y; return *this;} +CONSTCD11 inline year year::operator-() const NOEXCEPT {return year{-y_};} +CONSTCD11 inline year year::operator+() const NOEXCEPT {return *this;} + +CONSTCD11 +inline +bool +year::is_leap() const NOEXCEPT +{ + return y_ % 4 == 0 && (y_ % 100 != 0 || y_ % 400 == 0); +} + +CONSTCD11 inline year::operator int() const NOEXCEPT {return y_;} + +CONSTCD11 +inline +bool +year::ok() const NOEXCEPT +{ + return y_ != std::numeric_limits::min(); +} + +CONSTCD11 +inline +year +year::min() NOEXCEPT +{ + return year{-32767}; +} + +CONSTCD11 +inline +year +year::max() NOEXCEPT +{ + return year{32767}; +} + +CONSTCD11 +inline +bool +operator==(const year& x, const year& y) NOEXCEPT +{ + return static_cast(x) == static_cast(y); +} + +CONSTCD11 +inline +bool +operator!=(const year& x, const year& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year& x, const year& y) NOEXCEPT +{ + return static_cast(x) < static_cast(y); +} + +CONSTCD11 +inline +bool +operator>(const year& x, const year& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year& x, const year& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year& x, const year& y) NOEXCEPT +{ + return !(x < y); +} + +CONSTCD11 +inline +years +operator-(const year& x, const year& y) NOEXCEPT +{ + return years{static_cast(x) - static_cast(y)}; +} + +CONSTCD11 +inline +year +operator+(const year& x, const years& y) NOEXCEPT +{ + return year{static_cast(x) + y.count()}; +} + +CONSTCD11 +inline +year +operator+(const years& x, const year& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD11 +inline +year +operator-(const year& x, const years& y) NOEXCEPT +{ + return year{static_cast(x) - y.count()}; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year& y) +{ + detail::save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::internal); + os.width(4 + (y < year{0})); + os << static_cast(y); + if (!y.ok()) + os << " is not a valid year"; + return os; +} + +// weekday + +CONSTCD11 +inline +unsigned char +weekday::weekday_from_days(int z) NOEXCEPT +{ + return static_cast(static_cast( + z >= -4 ? (z+4) % 7 : (z+5) % 7 + 6)); +} + +CONSTCD11 +inline +weekday::weekday(unsigned wd) NOEXCEPT + : wd_(static_cast(wd != 7 ? wd : 0)) + {} + +CONSTCD11 +inline +weekday::weekday(const sys_days& dp) NOEXCEPT + : wd_(weekday_from_days(dp.time_since_epoch().count())) + {} + +CONSTCD11 +inline +weekday::weekday(const local_days& dp) NOEXCEPT + : wd_(weekday_from_days(dp.time_since_epoch().count())) + {} + +CONSTCD14 inline weekday& weekday::operator++() NOEXCEPT {*this += days{1}; return *this;} +CONSTCD14 inline weekday weekday::operator++(int) NOEXCEPT {auto tmp(*this); ++(*this); return tmp;} +CONSTCD14 inline weekday& weekday::operator--() NOEXCEPT {*this -= days{1}; return *this;} +CONSTCD14 inline weekday weekday::operator--(int) NOEXCEPT {auto tmp(*this); --(*this); return tmp;} + +CONSTCD14 +inline +weekday& +weekday::operator+=(const days& d) NOEXCEPT +{ + *this = *this + d; + return *this; +} + +CONSTCD14 +inline +weekday& +weekday::operator-=(const days& d) NOEXCEPT +{ + *this = *this - d; + return *this; +} + +CONSTCD11 inline bool weekday::ok() const NOEXCEPT {return wd_ <= 6;} + +CONSTCD11 +inline +bool +operator==(const weekday& x, const weekday& y) NOEXCEPT +{ + return x.wd_ == y.wd_; +} + +CONSTCD11 +inline +bool +operator!=(const weekday& x, const weekday& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD14 +inline +days +operator-(const weekday& x, const weekday& y) NOEXCEPT +{ + auto const wdu = x.wd_ - y.wd_; + auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; + return days{wdu - wk * 7}; +} + +CONSTCD14 +inline +weekday +operator+(const weekday& x, const days& y) NOEXCEPT +{ + auto const wdu = static_cast(static_cast(x.wd_)) + y.count(); + auto const wk = (wdu >= 0 ? wdu : wdu-6) / 7; + return weekday{static_cast(wdu - wk * 7)}; +} + +CONSTCD14 +inline +weekday +operator+(const days& x, const weekday& y) NOEXCEPT +{ + return y + x; +} + +CONSTCD14 +inline +weekday +operator-(const weekday& x, const days& y) NOEXCEPT +{ + return x + -y; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday& wd) +{ + if (wd.ok()) + { + CharT fmt[] = {'%', 'a', 0}; + os << format(fmt, wd); + } + else + os << static_cast(wd.wd_) << " is not a valid weekday"; + return os; +} + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +inline namespace literals +{ + +CONSTCD11 +inline +date::day +operator "" _d(unsigned long long d) NOEXCEPT +{ + return date::day{static_cast(d)}; +} + +CONSTCD11 +inline +date::year +operator "" _y(unsigned long long y) NOEXCEPT +{ + return date::year(static_cast(y)); +} +#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900) + +CONSTDATA date::last_spec last{}; + +CONSTDATA date::month jan{1}; +CONSTDATA date::month feb{2}; +CONSTDATA date::month mar{3}; +CONSTDATA date::month apr{4}; +CONSTDATA date::month may{5}; +CONSTDATA date::month jun{6}; +CONSTDATA date::month jul{7}; +CONSTDATA date::month aug{8}; +CONSTDATA date::month sep{9}; +CONSTDATA date::month oct{10}; +CONSTDATA date::month nov{11}; +CONSTDATA date::month dec{12}; + +CONSTDATA date::weekday sun{0u}; +CONSTDATA date::weekday mon{1u}; +CONSTDATA date::weekday tue{2u}; +CONSTDATA date::weekday wed{3u}; +CONSTDATA date::weekday thu{4u}; +CONSTDATA date::weekday fri{5u}; +CONSTDATA date::weekday sat{6u}; + +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) +} // inline namespace literals +#endif + +CONSTDATA date::month January{1}; +CONSTDATA date::month February{2}; +CONSTDATA date::month March{3}; +CONSTDATA date::month April{4}; +CONSTDATA date::month May{5}; +CONSTDATA date::month June{6}; +CONSTDATA date::month July{7}; +CONSTDATA date::month August{8}; +CONSTDATA date::month September{9}; +CONSTDATA date::month October{10}; +CONSTDATA date::month November{11}; +CONSTDATA date::month December{12}; + +CONSTDATA date::weekday Monday{1}; +CONSTDATA date::weekday Tuesday{2}; +CONSTDATA date::weekday Wednesday{3}; +CONSTDATA date::weekday Thursday{4}; +CONSTDATA date::weekday Friday{5}; +CONSTDATA date::weekday Saturday{6}; +CONSTDATA date::weekday Sunday{7}; + +// weekday_indexed + +CONSTCD11 +inline +weekday +weekday_indexed::weekday() const NOEXCEPT +{ + return date::weekday{static_cast(wd_)}; +} + +CONSTCD11 inline unsigned weekday_indexed::index() const NOEXCEPT {return index_;} + +CONSTCD11 +inline +bool +weekday_indexed::ok() const NOEXCEPT +{ + return weekday().ok() && 1 <= index_ && index_ <= 5; +} + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +#endif // __GNUC__ + +CONSTCD11 +inline +weekday_indexed::weekday_indexed(const date::weekday& wd, unsigned index) NOEXCEPT + : wd_(static_cast(static_cast(wd.wd_))) + , index_(static_cast(index)) + {} + +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif // __GNUC__ + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday_indexed& wdi) +{ + os << wdi.weekday() << '[' << wdi.index(); + if (!(1 <= wdi.index() && wdi.index() <= 5)) + os << " is not a valid index"; + os << ']'; + return os; +} + +CONSTCD11 +inline +weekday_indexed +weekday::operator[](unsigned index) const NOEXCEPT +{ + return {*this, index}; +} + +CONSTCD11 +inline +bool +operator==(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT +{ + return x.weekday() == y.weekday() && x.index() == y.index(); +} + +CONSTCD11 +inline +bool +operator!=(const weekday_indexed& x, const weekday_indexed& y) NOEXCEPT +{ + return !(x == y); +} + +// weekday_last + +CONSTCD11 inline date::weekday weekday_last::weekday() const NOEXCEPT {return wd_;} +CONSTCD11 inline bool weekday_last::ok() const NOEXCEPT {return wd_.ok();} +CONSTCD11 inline weekday_last::weekday_last(const date::weekday& wd) NOEXCEPT : wd_(wd) {} + +CONSTCD11 +inline +bool +operator==(const weekday_last& x, const weekday_last& y) NOEXCEPT +{ + return x.weekday() == y.weekday(); +} + +CONSTCD11 +inline +bool +operator!=(const weekday_last& x, const weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const weekday_last& wdl) +{ + return os << wdl.weekday() << "[last]"; +} + +CONSTCD11 +inline +weekday_last +weekday::operator[](last_spec) const NOEXCEPT +{ + return weekday_last{*this}; +} + +// year_month + +CONSTCD11 +inline +year_month::year_month(const date::year& y, const date::month& m) NOEXCEPT + : y_(y) + , m_(m) + {} + +CONSTCD11 inline year year_month::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month::month() const NOEXCEPT {return m_;} +CONSTCD11 inline bool year_month::ok() const NOEXCEPT {return y_.ok() && m_.ok();} + +template +CONSTCD14 +inline +year_month& +year_month::operator+=(const months& dm) NOEXCEPT +{ + *this = *this + dm; + return *this; +} + +template +CONSTCD14 +inline +year_month& +year_month::operator-=(const months& dm) NOEXCEPT +{ + *this = *this - dm; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator+=(const years& dy) NOEXCEPT +{ + *this = *this + dy; + return *this; +} + +CONSTCD14 +inline +year_month& +year_month::operator-=(const years& dy) NOEXCEPT +{ + *this = *this - dy; + return *this; +} + +CONSTCD11 +inline +bool +operator==(const year_month& x, const year_month& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month& x, const year_month& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month() < y.month())); +} + +CONSTCD11 +inline +bool +operator>(const year_month& x, const year_month& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month& x, const year_month& y) NOEXCEPT +{ + return !(x < y); +} + +template +CONSTCD14 +inline +year_month +operator+(const year_month& ym, const months& dm) NOEXCEPT +{ + auto dmi = static_cast(static_cast(ym.month())) - 1 + dm.count(); + auto dy = (dmi >= 0 ? dmi : dmi-11) / 12; + dmi = dmi - dy * 12 + 1; + return (ym.year() + years(dy)) / month(static_cast(dmi)); +} + +template +CONSTCD14 +inline +year_month +operator+(const months& dm, const year_month& ym) NOEXCEPT +{ + return ym + dm; +} + +template +CONSTCD14 +inline +year_month +operator-(const year_month& ym, const months& dm) NOEXCEPT +{ + return ym + -dm; +} + +CONSTCD11 +inline +months +operator-(const year_month& x, const year_month& y) NOEXCEPT +{ + return (x.year() - y.year()) + + months(static_cast(x.month()) - static_cast(y.month())); +} + +CONSTCD11 +inline +year_month +operator+(const year_month& ym, const years& dy) NOEXCEPT +{ + return (ym.year() + dy) / ym.month(); +} + +CONSTCD11 +inline +year_month +operator+(const years& dy, const year_month& ym) NOEXCEPT +{ + return ym + dy; +} + +CONSTCD11 +inline +year_month +operator-(const year_month& ym, const years& dy) NOEXCEPT +{ + return ym + -dy; +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month& ym) +{ + return os << ym.year() << '/' << ym.month(); +} + +// month_day + +CONSTCD11 +inline +month_day::month_day(const date::month& m, const date::day& d) NOEXCEPT + : m_(m) + , d_(d) + {} + +CONSTCD11 inline date::month month_day::month() const NOEXCEPT {return m_;} +CONSTCD11 inline date::day month_day::day() const NOEXCEPT {return d_;} + +CONSTCD14 +inline +bool +month_day::ok() const NOEXCEPT +{ + CONSTDATA date::day d[] = + { + date::day(31), date::day(29), date::day(31), + date::day(30), date::day(31), date::day(30), + date::day(31), date::day(31), date::day(30), + date::day(31), date::day(30), date::day(31) + }; + return m_.ok() && date::day{1} <= d_ && d_ <= d[static_cast(m_)-1]; +} + +CONSTCD11 +inline +bool +operator==(const month_day& x, const month_day& y) NOEXCEPT +{ + return x.month() == y.month() && x.day() == y.day(); +} + +CONSTCD11 +inline +bool +operator!=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month_day& x, const month_day& y) NOEXCEPT +{ + return x.month() < y.month() ? true + : (x.month() > y.month() ? false + : (x.day() < y.day())); +} + +CONSTCD11 +inline +bool +operator>(const month_day& x, const month_day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month_day& x, const month_day& y) NOEXCEPT +{ + return !(x < y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_day& md) +{ + return os << md.month() << '/' << md.day(); +} + +// month_day_last + +CONSTCD11 inline month month_day_last::month() const NOEXCEPT {return m_;} +CONSTCD11 inline bool month_day_last::ok() const NOEXCEPT {return m_.ok();} +CONSTCD11 inline month_day_last::month_day_last(const date::month& m) NOEXCEPT : m_(m) {} + +CONSTCD11 +inline +bool +operator==(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return x.month() == y.month(); +} + +CONSTCD11 +inline +bool +operator!=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return x.month() < y.month(); +} + +CONSTCD11 +inline +bool +operator>(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const month_day_last& x, const month_day_last& y) NOEXCEPT +{ + return !(x < y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_day_last& mdl) +{ + return os << mdl.month() << "/last"; +} + +// month_weekday + +CONSTCD11 +inline +month_weekday::month_weekday(const date::month& m, + const date::weekday_indexed& wdi) NOEXCEPT + : m_(m) + , wdi_(wdi) + {} + +CONSTCD11 inline month month_weekday::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday_indexed +month_weekday::weekday_indexed() const NOEXCEPT +{ + return wdi_; +} + +CONSTCD11 +inline +bool +month_weekday::ok() const NOEXCEPT +{ + return m_.ok() && wdi_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const month_weekday& x, const month_weekday& y) NOEXCEPT +{ + return x.month() == y.month() && x.weekday_indexed() == y.weekday_indexed(); +} + +CONSTCD11 +inline +bool +operator!=(const month_weekday& x, const month_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_weekday& mwd) +{ + return os << mwd.month() << '/' << mwd.weekday_indexed(); +} + +// month_weekday_last + +CONSTCD11 +inline +month_weekday_last::month_weekday_last(const date::month& m, + const date::weekday_last& wdl) NOEXCEPT + : m_(m) + , wdl_(wdl) + {} + +CONSTCD11 inline month month_weekday_last::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday_last +month_weekday_last::weekday_last() const NOEXCEPT +{ + return wdl_; +} + +CONSTCD11 +inline +bool +month_weekday_last::ok() const NOEXCEPT +{ + return m_.ok() && wdl_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT +{ + return x.month() == y.month() && x.weekday_last() == y.weekday_last(); +} + +CONSTCD11 +inline +bool +operator!=(const month_weekday_last& x, const month_weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const month_weekday_last& mwdl) +{ + return os << mwdl.month() << '/' << mwdl.weekday_last(); +} + +// year_month_day_last + +CONSTCD11 +inline +year_month_day_last::year_month_day_last(const date::year& y, + const date::month_day_last& mdl) NOEXCEPT + : y_(y) + , mdl_(mdl) + {} + +template +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +template +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_day_last& +year_month_day_last::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_day_last::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_day_last::month() const NOEXCEPT {return mdl_.month();} + +CONSTCD11 +inline +month_day_last +year_month_day_last::month_day_last() const NOEXCEPT +{ + return mdl_; +} + +CONSTCD14 +inline +day +year_month_day_last::day() const NOEXCEPT +{ + CONSTDATA date::day d[] = + { + date::day(31), date::day(28), date::day(31), + date::day(30), date::day(31), date::day(30), + date::day(31), date::day(31), date::day(30), + date::day(31), date::day(30), date::day(31) + }; + return month() != February || !y_.is_leap() ? + d[static_cast(month()) - 1] : date::day{29}; +} + +CONSTCD14 +inline +year_month_day_last::operator sys_days() const NOEXCEPT +{ + return sys_days(year()/month()/day()); +} + +CONSTCD14 +inline +year_month_day_last::operator local_days() const NOEXCEPT +{ + return local_days(year()/month()/day()); +} + +CONSTCD11 +inline +bool +year_month_day_last::ok() const NOEXCEPT +{ + return y_.ok() && mdl_.ok(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return x.year() == y.year() && x.month_day_last() == y.month_day_last(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month_day_last() < y.month_day_last())); +} + +CONSTCD11 +inline +bool +operator>(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month_day_last& x, const year_month_day_last& y) NOEXCEPT +{ + return !(x < y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_day_last& ymdl) +{ + return os << ymdl.year() << '/' << ymdl.month_day_last(); +} + +template +CONSTCD14 +inline +year_month_day_last +operator+(const year_month_day_last& ymdl, const months& dm) NOEXCEPT +{ + return (ymdl.year() / ymdl.month() + dm) / last; +} + +template +CONSTCD14 +inline +year_month_day_last +operator+(const months& dm, const year_month_day_last& ymdl) NOEXCEPT +{ + return ymdl + dm; +} + +template +CONSTCD14 +inline +year_month_day_last +operator-(const year_month_day_last& ymdl, const months& dm) NOEXCEPT +{ + return ymdl + (-dm); +} + +CONSTCD11 +inline +year_month_day_last +operator+(const year_month_day_last& ymdl, const years& dy) NOEXCEPT +{ + return {ymdl.year()+dy, ymdl.month_day_last()}; +} + +CONSTCD11 +inline +year_month_day_last +operator+(const years& dy, const year_month_day_last& ymdl) NOEXCEPT +{ + return ymdl + dy; +} + +CONSTCD11 +inline +year_month_day_last +operator-(const year_month_day_last& ymdl, const years& dy) NOEXCEPT +{ + return ymdl + (-dy); +} + +// year_month_day + +CONSTCD11 +inline +year_month_day::year_month_day(const date::year& y, const date::month& m, + const date::day& d) NOEXCEPT + : y_(y) + , m_(m) + , d_(d) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(const year_month_day_last& ymdl) NOEXCEPT + : y_(ymdl.year()) + , m_(ymdl.month()) + , d_(ymdl.day()) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(sys_days dp) NOEXCEPT + : year_month_day(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_day::year_month_day(local_days dp) NOEXCEPT + : year_month_day(from_days(dp.time_since_epoch())) + {} + +CONSTCD11 inline year year_month_day::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_day::month() const NOEXCEPT {return m_;} +CONSTCD11 inline day year_month_day::day() const NOEXCEPT {return d_;} + +template +CONSTCD14 +inline +year_month_day& +year_month_day::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +template +CONSTCD14 +inline +year_month_day& +year_month_day::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_day& +year_month_day::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD14 +inline +days +year_month_day::to_days() const NOEXCEPT +{ + static_assert(std::numeric_limits::digits >= 18, + "This algorithm has not been ported to a 16 bit unsigned integer"); + static_assert(std::numeric_limits::digits >= 20, + "This algorithm has not been ported to a 16 bit signed integer"); + auto const y = static_cast(y_) - (m_ <= February); + auto const m = static_cast(m_); + auto const d = static_cast(d_); + auto const era = (y >= 0 ? y : y-399) / 400; + auto const yoe = static_cast(y - era * 400); // [0, 399] + auto const doy = (153*(m > 2 ? m-3 : m+9) + 2)/5 + d-1; // [0, 365] + auto const doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096] + return days{era * 146097 + static_cast(doe) - 719468}; +} + +CONSTCD14 +inline +year_month_day::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_day::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD14 +inline +bool +year_month_day::ok() const NOEXCEPT +{ + if (!(y_.ok() && m_.ok())) + return false; + return date::day{1} <= d_ && d_ <= (y_ / m_ / last).day(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && x.day() == y.day(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(x == y); +} + +CONSTCD11 +inline +bool +operator<(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return x.year() < y.year() ? true + : (x.year() > y.year() ? false + : (x.month() < y.month() ? true + : (x.month() > y.month() ? false + : (x.day() < y.day())))); +} + +CONSTCD11 +inline +bool +operator>(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return y < x; +} + +CONSTCD11 +inline +bool +operator<=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(y < x); +} + +CONSTCD11 +inline +bool +operator>=(const year_month_day& x, const year_month_day& y) NOEXCEPT +{ + return !(x < y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_day& ymd) +{ + detail::save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os << ymd.year() << '-'; + os.width(2); + os << static_cast(ymd.month()) << '-'; + os << ymd.day(); + if (!ymd.ok()) + os << " is not a valid date"; + return os; +} + +CONSTCD14 +inline +year_month_day +year_month_day::from_days(days dp) NOEXCEPT +{ + static_assert(std::numeric_limits::digits >= 18, + "This algorithm has not been ported to a 16 bit unsigned integer"); + static_assert(std::numeric_limits::digits >= 20, + "This algorithm has not been ported to a 16 bit signed integer"); + auto const z = dp.count() + 719468; + auto const era = (z >= 0 ? z : z - 146096) / 146097; + auto const doe = static_cast(z - era * 146097); // [0, 146096] + auto const yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365; // [0, 399] + auto const y = static_cast(yoe) + era * 400; + auto const doy = doe - (365*yoe + yoe/4 - yoe/100); // [0, 365] + auto const mp = (5*doy + 2)/153; // [0, 11] + auto const d = doy - (153*mp+2)/5 + 1; // [1, 31] + auto const m = mp < 10 ? mp+3 : mp-9; // [1, 12] + return year_month_day{date::year{y + (m <= 2)}, date::month(m), date::day(d)}; +} + +template +CONSTCD14 +inline +year_month_day +operator+(const year_month_day& ymd, const months& dm) NOEXCEPT +{ + return (ymd.year() / ymd.month() + dm) / ymd.day(); +} + +template +CONSTCD14 +inline +year_month_day +operator+(const months& dm, const year_month_day& ymd) NOEXCEPT +{ + return ymd + dm; +} + +template +CONSTCD14 +inline +year_month_day +operator-(const year_month_day& ymd, const months& dm) NOEXCEPT +{ + return ymd + (-dm); +} + +CONSTCD11 +inline +year_month_day +operator+(const year_month_day& ymd, const years& dy) NOEXCEPT +{ + return (ymd.year() + dy) / ymd.month() / ymd.day(); +} + +CONSTCD11 +inline +year_month_day +operator+(const years& dy, const year_month_day& ymd) NOEXCEPT +{ + return ymd + dy; +} + +CONSTCD11 +inline +year_month_day +operator-(const year_month_day& ymd, const years& dy) NOEXCEPT +{ + return ymd + (-dy); +} + +// year_month_weekday + +CONSTCD11 +inline +year_month_weekday::year_month_weekday(const date::year& y, const date::month& m, + const date::weekday_indexed& wdi) + NOEXCEPT + : y_(y) + , m_(m) + , wdi_(wdi) + {} + +CONSTCD14 +inline +year_month_weekday::year_month_weekday(const sys_days& dp) NOEXCEPT + : year_month_weekday(from_days(dp.time_since_epoch())) + {} + +CONSTCD14 +inline +year_month_weekday::year_month_weekday(const local_days& dp) NOEXCEPT + : year_month_weekday(from_days(dp.time_since_epoch())) + {} + +template +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +template +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_weekday& +year_month_weekday::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_weekday::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_weekday::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday +year_month_weekday::weekday() const NOEXCEPT +{ + return wdi_.weekday(); +} + +CONSTCD11 +inline +unsigned +year_month_weekday::index() const NOEXCEPT +{ + return wdi_.index(); +} + +CONSTCD11 +inline +weekday_indexed +year_month_weekday::weekday_indexed() const NOEXCEPT +{ + return wdi_; +} + +CONSTCD14 +inline +year_month_weekday::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_weekday::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD14 +inline +bool +year_month_weekday::ok() const NOEXCEPT +{ + if (!y_.ok() || !m_.ok() || !wdi_.weekday().ok() || wdi_.index() < 1) + return false; + if (wdi_.index() <= 4) + return true; + auto d2 = wdi_.weekday() - date::weekday(static_cast(y_/m_/1)) + + days((wdi_.index()-1)*7 + 1); + return static_cast(d2.count()) <= static_cast((y_/m_/last).day()); +} + +CONSTCD14 +inline +year_month_weekday +year_month_weekday::from_days(days d) NOEXCEPT +{ + sys_days dp{d}; + auto const wd = date::weekday(dp); + auto const ymd = year_month_day(dp); + return {ymd.year(), ymd.month(), wd[(static_cast(ymd.day())-1)/7+1]}; +} + +CONSTCD14 +inline +days +year_month_weekday::to_days() const NOEXCEPT +{ + auto d = sys_days(y_/m_/1); + return (d + (wdi_.weekday() - date::weekday(d) + days{(wdi_.index()-1)*7}) + ).time_since_epoch(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && + x.weekday_indexed() == y.weekday_indexed(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_weekday& x, const year_month_weekday& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_weekday& ymwdi) +{ + return os << ymwdi.year() << '/' << ymwdi.month() + << '/' << ymwdi.weekday_indexed(); +} + +template +CONSTCD14 +inline +year_month_weekday +operator+(const year_month_weekday& ymwd, const months& dm) NOEXCEPT +{ + return (ymwd.year() / ymwd.month() + dm) / ymwd.weekday_indexed(); +} + +template +CONSTCD14 +inline +year_month_weekday +operator+(const months& dm, const year_month_weekday& ymwd) NOEXCEPT +{ + return ymwd + dm; +} + +template +CONSTCD14 +inline +year_month_weekday +operator-(const year_month_weekday& ymwd, const months& dm) NOEXCEPT +{ + return ymwd + (-dm); +} + +CONSTCD11 +inline +year_month_weekday +operator+(const year_month_weekday& ymwd, const years& dy) NOEXCEPT +{ + return {ymwd.year()+dy, ymwd.month(), ymwd.weekday_indexed()}; +} + +CONSTCD11 +inline +year_month_weekday +operator+(const years& dy, const year_month_weekday& ymwd) NOEXCEPT +{ + return ymwd + dy; +} + +CONSTCD11 +inline +year_month_weekday +operator-(const year_month_weekday& ymwd, const years& dy) NOEXCEPT +{ + return ymwd + (-dy); +} + +// year_month_weekday_last + +CONSTCD11 +inline +year_month_weekday_last::year_month_weekday_last(const date::year& y, + const date::month& m, + const date::weekday_last& wdl) NOEXCEPT + : y_(y) + , m_(m) + , wdl_(wdl) + {} + +template +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator+=(const months& m) NOEXCEPT +{ + *this = *this + m; + return *this; +} + +template +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator-=(const months& m) NOEXCEPT +{ + *this = *this - m; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator+=(const years& y) NOEXCEPT +{ + *this = *this + y; + return *this; +} + +CONSTCD14 +inline +year_month_weekday_last& +year_month_weekday_last::operator-=(const years& y) NOEXCEPT +{ + *this = *this - y; + return *this; +} + +CONSTCD11 inline year year_month_weekday_last::year() const NOEXCEPT {return y_;} +CONSTCD11 inline month year_month_weekday_last::month() const NOEXCEPT {return m_;} + +CONSTCD11 +inline +weekday +year_month_weekday_last::weekday() const NOEXCEPT +{ + return wdl_.weekday(); +} + +CONSTCD11 +inline +weekday_last +year_month_weekday_last::weekday_last() const NOEXCEPT +{ + return wdl_; +} + +CONSTCD14 +inline +year_month_weekday_last::operator sys_days() const NOEXCEPT +{ + return sys_days{to_days()}; +} + +CONSTCD14 +inline +year_month_weekday_last::operator local_days() const NOEXCEPT +{ + return local_days{to_days()}; +} + +CONSTCD11 +inline +bool +year_month_weekday_last::ok() const NOEXCEPT +{ + return y_.ok() && m_.ok() && wdl_.ok(); +} + +CONSTCD14 +inline +days +year_month_weekday_last::to_days() const NOEXCEPT +{ + auto const d = sys_days(y_/m_/last); + return (d - (date::weekday{d} - wdl_.weekday())).time_since_epoch(); +} + +CONSTCD11 +inline +bool +operator==(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT +{ + return x.year() == y.year() && x.month() == y.month() && + x.weekday_last() == y.weekday_last(); +} + +CONSTCD11 +inline +bool +operator!=(const year_month_weekday_last& x, const year_month_weekday_last& y) NOEXCEPT +{ + return !(x == y); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const year_month_weekday_last& ymwdl) +{ + return os << ymwdl.year() << '/' << ymwdl.month() << '/' << ymwdl.weekday_last(); +} + +template +CONSTCD14 +inline +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT +{ + return (ymwdl.year() / ymwdl.month() + dm) / ymwdl.weekday_last(); +} + +template +CONSTCD14 +inline +year_month_weekday_last +operator+(const months& dm, const year_month_weekday_last& ymwdl) NOEXCEPT +{ + return ymwdl + dm; +} + +template +CONSTCD14 +inline +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const months& dm) NOEXCEPT +{ + return ymwdl + (-dm); +} + +CONSTCD11 +inline +year_month_weekday_last +operator+(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT +{ + return {ymwdl.year()+dy, ymwdl.month(), ymwdl.weekday_last()}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator+(const years& dy, const year_month_weekday_last& ymwdl) NOEXCEPT +{ + return ymwdl + dy; +} + +CONSTCD11 +inline +year_month_weekday_last +operator-(const year_month_weekday_last& ymwdl, const years& dy) NOEXCEPT +{ + return ymwdl + (-dy); +} + +// year_month from operator/() + +CONSTCD11 +inline +year_month +operator/(const year& y, const month& m) NOEXCEPT +{ + return {y, m}; +} + +CONSTCD11 +inline +year_month +operator/(const year& y, int m) NOEXCEPT +{ + return y / month(static_cast(m)); +} + +// month_day from operator/() + +CONSTCD11 +inline +month_day +operator/(const month& m, const day& d) NOEXCEPT +{ + return {m, d}; +} + +CONSTCD11 +inline +month_day +operator/(const day& d, const month& m) NOEXCEPT +{ + return m / d; +} + +CONSTCD11 +inline +month_day +operator/(const month& m, int d) NOEXCEPT +{ + return m / day(static_cast(d)); +} + +CONSTCD11 +inline +month_day +operator/(int m, const day& d) NOEXCEPT +{ + return month(static_cast(m)) / d; +} + +CONSTCD11 inline month_day operator/(const day& d, int m) NOEXCEPT {return m / d;} + +// month_day_last from operator/() + +CONSTCD11 +inline +month_day_last +operator/(const month& m, last_spec) NOEXCEPT +{ + return month_day_last{m}; +} + +CONSTCD11 +inline +month_day_last +operator/(last_spec, const month& m) NOEXCEPT +{ + return m/last; +} + +CONSTCD11 +inline +month_day_last +operator/(int m, last_spec) NOEXCEPT +{ + return month(static_cast(m))/last; +} + +CONSTCD11 +inline +month_day_last +operator/(last_spec, int m) NOEXCEPT +{ + return m/last; +} + +// month_weekday from operator/() + +CONSTCD11 +inline +month_weekday +operator/(const month& m, const weekday_indexed& wdi) NOEXCEPT +{ + return {m, wdi}; +} + +CONSTCD11 +inline +month_weekday +operator/(const weekday_indexed& wdi, const month& m) NOEXCEPT +{ + return m / wdi; +} + +CONSTCD11 +inline +month_weekday +operator/(int m, const weekday_indexed& wdi) NOEXCEPT +{ + return month(static_cast(m)) / wdi; +} + +CONSTCD11 +inline +month_weekday +operator/(const weekday_indexed& wdi, int m) NOEXCEPT +{ + return m / wdi; +} + +// month_weekday_last from operator/() + +CONSTCD11 +inline +month_weekday_last +operator/(const month& m, const weekday_last& wdl) NOEXCEPT +{ + return {m, wdl}; +} + +CONSTCD11 +inline +month_weekday_last +operator/(const weekday_last& wdl, const month& m) NOEXCEPT +{ + return m / wdl; +} + +CONSTCD11 +inline +month_weekday_last +operator/(int m, const weekday_last& wdl) NOEXCEPT +{ + return month(static_cast(m)) / wdl; +} + +CONSTCD11 +inline +month_weekday_last +operator/(const weekday_last& wdl, int m) NOEXCEPT +{ + return m / wdl; +} + +// year_month_day from operator/() + +CONSTCD11 +inline +year_month_day +operator/(const year_month& ym, const day& d) NOEXCEPT +{ + return {ym.year(), ym.month(), d}; +} + +CONSTCD11 +inline +year_month_day +operator/(const year_month& ym, int d) NOEXCEPT +{ + return ym / day(static_cast(d)); +} + +CONSTCD11 +inline +year_month_day +operator/(const year& y, const month_day& md) NOEXCEPT +{ + return y / md.month() / md.day(); +} + +CONSTCD11 +inline +year_month_day +operator/(int y, const month_day& md) NOEXCEPT +{ + return year(y) / md; +} + +CONSTCD11 +inline +year_month_day +operator/(const month_day& md, const year& y) NOEXCEPT +{ + return y / md; +} + +CONSTCD11 +inline +year_month_day +operator/(const month_day& md, int y) NOEXCEPT +{ + return year(y) / md; +} + +// year_month_day_last from operator/() + +CONSTCD11 +inline +year_month_day_last +operator/(const year_month& ym, last_spec) NOEXCEPT +{ + return {ym.year(), month_day_last{ym.month()}}; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const year& y, const month_day_last& mdl) NOEXCEPT +{ + return {y, mdl}; +} + +CONSTCD11 +inline +year_month_day_last +operator/(int y, const month_day_last& mdl) NOEXCEPT +{ + return year(y) / mdl; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const month_day_last& mdl, const year& y) NOEXCEPT +{ + return y / mdl; +} + +CONSTCD11 +inline +year_month_day_last +operator/(const month_day_last& mdl, int y) NOEXCEPT +{ + return year(y) / mdl; +} + +// year_month_weekday from operator/() + +CONSTCD11 +inline +year_month_weekday +operator/(const year_month& ym, const weekday_indexed& wdi) NOEXCEPT +{ + return {ym.year(), ym.month(), wdi}; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const year& y, const month_weekday& mwd) NOEXCEPT +{ + return {y, mwd.month(), mwd.weekday_indexed()}; +} + +CONSTCD11 +inline +year_month_weekday +operator/(int y, const month_weekday& mwd) NOEXCEPT +{ + return year(y) / mwd; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const month_weekday& mwd, const year& y) NOEXCEPT +{ + return y / mwd; +} + +CONSTCD11 +inline +year_month_weekday +operator/(const month_weekday& mwd, int y) NOEXCEPT +{ + return year(y) / mwd; +} + +// year_month_weekday_last from operator/() + +CONSTCD11 +inline +year_month_weekday_last +operator/(const year_month& ym, const weekday_last& wdl) NOEXCEPT +{ + return {ym.year(), ym.month(), wdl}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const year& y, const month_weekday_last& mwdl) NOEXCEPT +{ + return {y, mwdl.month(), mwdl.weekday_last()}; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(int y, const month_weekday_last& mwdl) NOEXCEPT +{ + return year(y) / mwdl; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const month_weekday_last& mwdl, const year& y) NOEXCEPT +{ + return y / mwdl; +} + +CONSTCD11 +inline +year_month_weekday_last +operator/(const month_weekday_last& mwdl, int y) NOEXCEPT +{ + return year(y) / mwdl; +} + +template +struct fields; + +template +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, + const fields& fds, const std::string* abbrev = nullptr, + const std::chrono::seconds* offset_sec = nullptr); + +template +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, + fields& fds, std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr); + +// time_of_day + +enum {am = 1, pm}; + +namespace detail +{ + +// width::value is the number of fractional decimal digits in 1/n +// width<0>::value and width<1>::value are defined to be 0 +// If 1/n takes more than 18 fractional decimal digits, +// the result is truncated to 19. +// Example: width<2>::value == 1 +// Example: width<3>::value == 19 +// Example: width<4>::value == 2 +// Example: width<10>::value == 1 +// Example: width<1000>::value == 3 +template +struct width +{ + static CONSTDATA unsigned value = 1 + width::value; +}; + +template +struct width +{ + static CONSTDATA unsigned value = 0; +}; + +template +struct static_pow10 +{ +private: + static CONSTDATA std::uint64_t h = static_pow10::value; +public: + static CONSTDATA std::uint64_t value = h * h * (exp % 2 ? 10 : 1); +}; + +template <> +struct static_pow10<0> +{ + static CONSTDATA std::uint64_t value = 1; +}; + +template +struct make_precision +{ + using type = std::chrono::duration::value>>; + static CONSTDATA unsigned width = w; +}; + +template +struct make_precision +{ + using type = std::chrono::duration; + static CONSTDATA unsigned width = 6; +}; + +template ::type::period::den>::value> +class decimal_format_seconds +{ +public: + using rep = typename std::common_type::type::rep; + using precision = typename make_precision::type; + static auto CONSTDATA width = make_precision::width; + +private: + std::chrono::seconds s_; + precision sub_s_; + +public: + CONSTCD11 decimal_format_seconds() + : s_() + , sub_s_() + {} + + CONSTCD11 explicit decimal_format_seconds(const Duration& d) NOEXCEPT + : s_(std::chrono::duration_cast(d)) + , sub_s_(std::chrono::duration_cast(d - s_)) + {} + + CONSTCD14 std::chrono::seconds& seconds() NOEXCEPT {return s_;} + CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_;} + CONSTCD11 precision subseconds() const NOEXCEPT {return sub_s_;} + + CONSTCD14 precision to_duration() const NOEXCEPT + { + return s_ + sub_s_; + } + + CONSTCD11 bool in_conventional_range() const NOEXCEPT + { + using namespace std::chrono; + return sub_s_ < std::chrono::seconds{1} && s_ < minutes{1}; + } + + template + friend + std::basic_ostream& + operator<<(std::basic_ostream& os, const decimal_format_seconds& x) + { + date::detail::save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << x.s_.count() << + std::use_facet>(os.getloc()).decimal_point(); + os.width(width); + os << static_cast(x.sub_s_.count()); + return os; + } +}; + +template +class decimal_format_seconds +{ + static CONSTDATA unsigned w = 0; +public: + using rep = typename std::common_type::type::rep; + using precision = std::chrono::duration; + static auto CONSTDATA width = make_precision::width; +private: + + std::chrono::seconds s_; + +public: + CONSTCD11 decimal_format_seconds() : s_() {} + CONSTCD11 explicit decimal_format_seconds(const precision& s) NOEXCEPT + : s_(s) + {} + + CONSTCD14 std::chrono::seconds& seconds() NOEXCEPT {return s_;} + CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_;} + CONSTCD14 precision to_duration() const NOEXCEPT {return s_;} + + CONSTCD11 bool in_conventional_range() const NOEXCEPT + { + using namespace std::chrono; + return s_ < minutes{1}; + } + + template + friend + std::basic_ostream& + operator<<(std::basic_ostream& os, const decimal_format_seconds& x) + { + date::detail::save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << x.s_.count(); + return os; + } +}; + +enum class classify +{ + not_valid, + hour, + minute, + second, + subsecond +}; + +template +struct classify_duration +{ + static CONSTDATA classify value = + std::is_convertible::value + ? classify::hour : + std::is_convertible::value + ? classify::minute : + std::is_convertible::value + ? classify::second : + std::chrono::treat_as_floating_point::value + ? classify::not_valid : + classify::subsecond; +}; + +template +inline +CONSTCD11 +typename std::enable_if + < + std::numeric_limits::is_signed, + std::chrono::duration + >::type +abs(std::chrono::duration d) +{ + return d >= d.zero() ? +d : -d; +} + +template +inline +CONSTCD11 +typename std::enable_if + < + !std::numeric_limits::is_signed, + std::chrono::duration + >::type +abs(std::chrono::duration d) +{ + return d; +} + +class time_of_day_base +{ +protected: + std::chrono::hours h_; + unsigned char mode_; + bool neg_; + + enum {is24hr}; + + CONSTCD11 time_of_day_base() NOEXCEPT + : h_(0) + , mode_(static_cast(is24hr)) + , neg_(false) + {} + + + CONSTCD11 time_of_day_base(std::chrono::hours h, bool neg, unsigned m) NOEXCEPT + : h_(detail::abs(h)) + , mode_(static_cast(m)) + , neg_(neg) + {} + + CONSTCD14 void make24() NOEXCEPT; + CONSTCD14 void make12() NOEXCEPT; + + CONSTCD14 std::chrono::hours to24hr() const; + + CONSTCD11 bool in_conventional_range() const NOEXCEPT + { + return !neg_ && h_ < days{1}; + } +}; + +CONSTCD14 +inline +std::chrono::hours +time_of_day_base::to24hr() const +{ + auto h = h_; + if (mode_ == am || mode_ == pm) + { + CONSTDATA auto h12 = std::chrono::hours(12); + if (mode_ == pm) + { + if (h != h12) + h = h + h12; + } + else if (h == h12) + h = std::chrono::hours(0); + } + return h; +} + +CONSTCD14 +inline +void +time_of_day_base::make24() NOEXCEPT +{ + h_ = to24hr(); + mode_ = is24hr; +} + +CONSTCD14 +inline +void +time_of_day_base::make12() NOEXCEPT +{ + if (mode_ == is24hr) + { + CONSTDATA auto h12 = std::chrono::hours(12); + if (h_ >= h12) + { + if (h_ > h12) + h_ = h_ - h12; + mode_ = pm; + } + else + { + if (h_ == std::chrono::hours(0)) + h_ = h12; + mode_ = am; + } + } +} + +template ::value> +class time_of_day_storage; + +template +class time_of_day_storage, detail::classify::hour> + : private detail::time_of_day_base +{ + using base = detail::time_of_day_base; + +public: + using precision = std::chrono::hours; + +#if !defined(_MSC_VER) || _MSC_VER >= 1900 + CONSTCD11 time_of_day_storage() NOEXCEPT = default; +#else + CONSTCD11 time_of_day_storage() = default; +#endif /* !defined(_MSC_VER) || _MSC_VER >= 1900 */ + + CONSTCD11 explicit time_of_day_storage(std::chrono::hours since_midnight) NOEXCEPT + : base(since_midnight, since_midnight < std::chrono::hours{0}, is24hr) + {} + + CONSTCD11 explicit time_of_day_storage(std::chrono::hours h, unsigned md) NOEXCEPT + : base(h, h < std::chrono::hours{0}, md) + {} + + CONSTCD11 std::chrono::hours hours() const NOEXCEPT {return h_;} + CONSTCD11 unsigned mode() const NOEXCEPT {return mode_;} + + CONSTCD14 explicit operator precision() const NOEXCEPT + { + auto p = to24hr(); + if (neg_) + p = -p; + return p; + } + + CONSTCD14 precision to_duration() const NOEXCEPT + { + return static_cast(*this); + } + + CONSTCD14 time_of_day_storage& make24() NOEXCEPT {base::make24(); return *this;} + CONSTCD14 time_of_day_storage& make12() NOEXCEPT {base::make12(); return *this;} + + CONSTCD11 bool in_conventional_range() const NOEXCEPT + { + return base::in_conventional_range(); + } + + template + friend + std::basic_ostream& + operator<<(std::basic_ostream& os, const time_of_day_storage& t) + { + using namespace std; + detail::save_ostream _(os); + if (t.neg_) + os << '-'; + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + if (t.mode_ != am && t.mode_ != pm) + os.width(2); + os << t.h_.count(); + switch (t.mode_) + { + case time_of_day_storage::is24hr: + os << "00"; + break; + case am: + os << "am"; + break; + case pm: + os << "pm"; + break; + } + return os; + } +}; + +template +class time_of_day_storage, detail::classify::minute> + : private detail::time_of_day_base +{ + using base = detail::time_of_day_base; + + std::chrono::minutes m_; + +public: + using precision = std::chrono::minutes; + + CONSTCD11 time_of_day_storage() NOEXCEPT + : base() + , m_(0) + {} + + CONSTCD11 explicit time_of_day_storage(std::chrono::minutes since_midnight) NOEXCEPT + : base(std::chrono::duration_cast(since_midnight), + since_midnight < std::chrono::minutes{0}, is24hr) + , m_(detail::abs(since_midnight) - h_) + {} + + CONSTCD11 explicit time_of_day_storage(std::chrono::hours h, std::chrono::minutes m, + unsigned md) NOEXCEPT + : base(h, false, md) + , m_(m) + {} + + CONSTCD11 std::chrono::hours hours() const NOEXCEPT {return h_;} + CONSTCD11 std::chrono::minutes minutes() const NOEXCEPT {return m_;} + CONSTCD11 unsigned mode() const NOEXCEPT {return mode_;} + + CONSTCD14 explicit operator precision() const NOEXCEPT + { + auto p = to24hr() + m_; + if (neg_) + p = -p; + return p; + } + + CONSTCD14 precision to_duration() const NOEXCEPT + { + return static_cast(*this); + } + + CONSTCD14 time_of_day_storage& make24() NOEXCEPT {base::make24(); return *this;} + CONSTCD14 time_of_day_storage& make12() NOEXCEPT {base::make12(); return *this;} + + CONSTCD11 bool in_conventional_range() const NOEXCEPT + { + return base::in_conventional_range() && m_ < std::chrono::hours{1}; + } + + template + friend + std::basic_ostream& + operator<<(std::basic_ostream& os, const time_of_day_storage& t) + { + using namespace std; + detail::save_ostream _(os); + if (t.neg_) + os << '-'; + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + if (t.mode_ != am && t.mode_ != pm) + os.width(2); + os << t.h_.count() << ':'; + os.width(2); + os << t.m_.count(); + switch (t.mode_) + { + case am: + os << "am"; + break; + case pm: + os << "pm"; + break; + } + return os; + } +}; + +template +class time_of_day_storage, detail::classify::second> + : private detail::time_of_day_base +{ + using base = detail::time_of_day_base; + using dfs = decimal_format_seconds; + + std::chrono::minutes m_; + dfs s_; + +public: + using precision = std::chrono::seconds; + + CONSTCD11 time_of_day_storage() NOEXCEPT + : base() + , m_(0) + , s_() + {} + + CONSTCD11 explicit time_of_day_storage(std::chrono::seconds since_midnight) NOEXCEPT + : base(std::chrono::duration_cast(since_midnight), + since_midnight < std::chrono::seconds{0}, is24hr) + , m_(std::chrono::duration_cast(detail::abs(since_midnight) - h_)) + , s_(detail::abs(since_midnight) - h_ - m_) + {} + + CONSTCD11 explicit time_of_day_storage(std::chrono::hours h, std::chrono::minutes m, + std::chrono::seconds s, unsigned md) NOEXCEPT + : base(h, false, md) + , m_(m) + , s_(s) + {} + + CONSTCD11 std::chrono::hours hours() const NOEXCEPT {return h_;} + CONSTCD11 std::chrono::minutes minutes() const NOEXCEPT {return m_;} + CONSTCD14 std::chrono::seconds& seconds() NOEXCEPT {return s_.seconds();} + CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_.seconds();} + CONSTCD11 unsigned mode() const NOEXCEPT {return mode_;} + + CONSTCD14 explicit operator precision() const NOEXCEPT + { + auto p = to24hr() + s_.to_duration() + m_; + if (neg_) + p = -p; + return p; + } + + CONSTCD14 precision to_duration() const NOEXCEPT + { + return static_cast(*this); + } + + CONSTCD14 time_of_day_storage& make24() NOEXCEPT {base::make24(); return *this;} + CONSTCD14 time_of_day_storage& make12() NOEXCEPT {base::make12(); return *this;} + + CONSTCD11 bool in_conventional_range() const NOEXCEPT + { + return base::in_conventional_range() && m_ < std::chrono::hours{1} && + s_.in_conventional_range(); + } + + template + friend + std::basic_ostream& + operator<<(std::basic_ostream& os, const time_of_day_storage& t) + { + using namespace std; + detail::save_ostream _(os); + if (t.neg_) + os << '-'; + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + if (t.mode_ != am && t.mode_ != pm) + os.width(2); + os << t.h_.count() << ':'; + os.width(2); + os << t.m_.count() << ':' << t.s_; + switch (t.mode_) + { + case am: + os << "am"; + break; + case pm: + os << "pm"; + break; + } + return os; + } + + template + friend + std::basic_ostream& + date::to_stream(std::basic_ostream& os, const CharT* fmt, + const fields& fds, const std::string* abbrev, + const std::chrono::seconds* offset_sec); + + template + friend + std::basic_istream& + date::from_stream(std::basic_istream& is, const CharT* fmt, + fields& fds, + std::basic_string* abbrev, std::chrono::minutes* offset); +}; + +template +class time_of_day_storage, detail::classify::subsecond> + : private detail::time_of_day_base +{ +public: + using Duration = std::chrono::duration; + using dfs = decimal_format_seconds::type>; + using precision = typename dfs::precision; + +private: + using base = detail::time_of_day_base; + + std::chrono::minutes m_; + dfs s_; + +public: + CONSTCD11 time_of_day_storage() NOEXCEPT + : base() + , m_(0) + , s_() + {} + + CONSTCD11 explicit time_of_day_storage(Duration since_midnight) NOEXCEPT + : base(date::trunc(since_midnight), + since_midnight < Duration{0}, is24hr) + , m_(date::trunc(detail::abs(since_midnight) - h_)) + , s_(detail::abs(since_midnight) - h_ - m_) + {} + + CONSTCD11 explicit time_of_day_storage(std::chrono::hours h, std::chrono::minutes m, + std::chrono::seconds s, precision sub_s, + unsigned md) NOEXCEPT + : base(h, false, md) + , m_(m) + , s_(s + sub_s) + {} + + CONSTCD11 std::chrono::hours hours() const NOEXCEPT {return h_;} + CONSTCD11 std::chrono::minutes minutes() const NOEXCEPT {return m_;} + CONSTCD14 std::chrono::seconds& seconds() NOEXCEPT {return s_.seconds();} + CONSTCD11 std::chrono::seconds seconds() const NOEXCEPT {return s_.seconds();} + CONSTCD11 precision subseconds() const NOEXCEPT {return s_.subseconds();} + CONSTCD11 unsigned mode() const NOEXCEPT {return mode_;} + + CONSTCD14 explicit operator precision() const NOEXCEPT + { + auto p = to24hr() + s_.to_duration() + m_; + if (neg_) + p = -p; + return p; + } + + CONSTCD14 precision to_duration() const NOEXCEPT + { + return static_cast(*this); + } + + CONSTCD14 time_of_day_storage& make24() NOEXCEPT {base::make24(); return *this;} + CONSTCD14 time_of_day_storage& make12() NOEXCEPT {base::make12(); return *this;} + + CONSTCD11 bool in_conventional_range() const NOEXCEPT + { + return base::in_conventional_range() && m_ < std::chrono::hours{1} && + s_.in_conventional_range(); + } + + template + friend + std::basic_ostream& + operator<<(std::basic_ostream& os, const time_of_day_storage& t) + { + using namespace std; + detail::save_ostream _(os); + if (t.neg_) + os << '-'; + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + if (t.mode_ != am && t.mode_ != pm) + os.width(2); + os << t.h_.count() << ':'; + os.width(2); + os << t.m_.count() << ':' << t.s_; + switch (t.mode_) + { + case am: + os << "am"; + break; + case pm: + os << "pm"; + break; + } + return os; + } + + template + friend + std::basic_ostream& + date::to_stream(std::basic_ostream& os, const CharT* fmt, + const fields& fds, const std::string* abbrev, + const std::chrono::seconds* offset_sec); + + template + friend + std::basic_istream& + date::from_stream(std::basic_istream& is, const CharT* fmt, + fields& fds, + std::basic_string* abbrev, std::chrono::minutes* offset); +}; + +} // namespace detail + +template +class time_of_day + : public detail::time_of_day_storage +{ + using base = detail::time_of_day_storage; +public: +#if !defined(_MSC_VER) || _MSC_VER >= 1900 + CONSTCD11 time_of_day() NOEXCEPT = default; +#else + CONSTCD11 time_of_day() = default; +#endif /* !defined(_MSC_VER) || _MSC_VER >= 1900 */ + + CONSTCD11 explicit time_of_day(Duration since_midnight) NOEXCEPT + : base(since_midnight) + {} + + template + CONSTCD11 + explicit time_of_day(Arg0&& arg0, Arg1&& arg1, Args&& ...args) NOEXCEPT + : base(std::forward(arg0), std::forward(arg1), std::forward(args)...) + {} +}; + +template ::value>::type> +CONSTCD11 +inline +time_of_day> +make_time(const std::chrono::duration& d) +{ + return time_of_day>(d); +} + +CONSTCD11 +inline +time_of_day +make_time(const std::chrono::hours& h, unsigned md) +{ + return time_of_day(h, md); +} + +CONSTCD11 +inline +time_of_day +make_time(const std::chrono::hours& h, const std::chrono::minutes& m, + unsigned md) +{ + return time_of_day(h, m, md); +} + +CONSTCD11 +inline +time_of_day +make_time(const std::chrono::hours& h, const std::chrono::minutes& m, + const std::chrono::seconds& s, unsigned md) +{ + return time_of_day(h, m, s, md); +} + +template >::value>::type> +CONSTCD11 +inline +time_of_day> +make_time(const std::chrono::hours& h, const std::chrono::minutes& m, + const std::chrono::seconds& s, const std::chrono::duration& sub_s, + unsigned md) +{ + return time_of_day>(h, m, s, sub_s, md); +} + +template +inline +typename std::enable_if +< + !std::chrono::treat_as_floating_point::value && + std::ratio_less::value + , std::basic_ostream& +>::type +operator<<(std::basic_ostream& os, const sys_time& tp) +{ + auto const dp = date::floor(tp); + return os << year_month_day(dp) << ' ' << make_time(tp-dp); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const sys_days& dp) +{ + return os << year_month_day(dp); +} + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, const local_time& ut) +{ + return (os << sys_time{ut.time_since_epoch()}); +} + +// to_stream + +CONSTDATA year nanyear{-32768}; + +template +struct fields +{ + year_month_day ymd{nanyear/0/0}; + weekday wd{8u}; + time_of_day tod{}; + bool has_tod = false; + + fields() = default; + + fields(year_month_day ymd_) : ymd(ymd_) {} + fields(weekday wd_) : wd(wd_) {} + fields(time_of_day tod_) : tod(tod_), has_tod(true) {} + + fields(year_month_day ymd_, weekday wd_) : ymd(ymd_), wd(wd_) {} + fields(year_month_day ymd_, time_of_day tod_) : ymd(ymd_), tod(tod_), + has_tod(true) {} + + fields(weekday wd_, time_of_day tod_) : wd(wd_), tod(tod_), has_tod(true) {} + + fields(year_month_day ymd_, weekday wd_, time_of_day tod_) + : ymd(ymd_) + , wd(wd_) + , tod(tod_) + , has_tod(true) + {} +}; + +namespace detail +{ + +template +unsigned +extract_weekday(std::basic_ostream& os, const fields& fds) +{ + if (!fds.ymd.ok() && !fds.wd.ok()) + { + // fds does not contain a valid weekday + os.setstate(std::ios::failbit); + return 8; + } + weekday wd; + if (fds.ymd.ok()) + { + wd = weekday{sys_days(fds.ymd)}; + if (fds.wd.ok() && wd != fds.wd) + { + // fds.ymd and fds.wd are inconsistent + os.setstate(std::ios::failbit); + return 8; + } + } + else + wd = fds.wd; + return static_cast((wd - Sunday).count()); +} + +template +unsigned +extract_month(std::basic_ostream& os, const fields& fds) +{ + if (!fds.ymd.month().ok()) + { + // fds does not contain a valid month + os.setstate(std::ios::failbit); + return 0; + } + return static_cast(fds.ymd.month()); +} + +} // namespace detail + +#if ONLY_C_LOCALE + +namespace detail +{ + +inline +std::pair +weekday_names() +{ + using namespace std; + static const string nm[] = + { + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sun", + "Mon", + "Tue", + "Wed", + "Thu", + "Fri", + "Sat" + }; + return make_pair(nm, nm+sizeof(nm)/sizeof(nm[0])); +} + +inline +std::pair +month_names() +{ + using namespace std; + static const string nm[] = + { + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec" + }; + return make_pair(nm, nm+sizeof(nm)/sizeof(nm[0])); +} + +inline +std::pair +ampm_names() +{ + using namespace std; + static const string nm[] = + { + "AM", + "PM" + }; + return make_pair(nm, nm+sizeof(nm)/sizeof(nm[0])); +} + +template +FwdIter +scan_keyword(std::basic_istream& is, FwdIter kb, FwdIter ke) +{ + using namespace std; + size_t nkw = static_cast(std::distance(kb, ke)); + const unsigned char doesnt_match = '\0'; + const unsigned char might_match = '\1'; + const unsigned char does_match = '\2'; + unsigned char statbuf[100]; + unsigned char* status = statbuf; + unique_ptr stat_hold(0, free); + if (nkw > sizeof(statbuf)) + { + status = (unsigned char*)malloc(nkw); + if (status == nullptr) + throw bad_alloc(); + stat_hold.reset(status); + } + size_t n_might_match = nkw; // At this point, any keyword might match + size_t n_does_match = 0; // but none of them definitely do + // Initialize all statuses to might_match, except for "" keywords are does_match + unsigned char* st = status; + for (auto ky = kb; ky != ke; ++ky, ++st) + { + if (!ky->empty()) + *st = might_match; + else + { + *st = does_match; + --n_might_match; + ++n_does_match; + } + } + // While there might be a match, test keywords against the next CharT + for (size_t indx = 0; is && n_might_match > 0; ++indx) + { + // Peek at the next CharT but don't consume it + auto ic = is.peek(); + if (ic == EOF) + { + is.setstate(ios::eofbit); + break; + } + auto c = static_cast(toupper(ic)); + bool consume = false; + // For each keyword which might match, see if the indx character is c + // If a match if found, consume c + // If a match is found, and that is the last character in the keyword, + // then that keyword matches. + // If the keyword doesn't match this character, then change the keyword + // to doesn't match + st = status; + for (auto ky = kb; ky != ke; ++ky, ++st) + { + if (*st == might_match) + { + if (c == static_cast(toupper((*ky)[indx]))) + { + consume = true; + if (ky->size() == indx+1) + { + *st = does_match; + --n_might_match; + ++n_does_match; + } + } + else + { + *st = doesnt_match; + --n_might_match; + } + } + } + // consume if we matched a character + if (consume) + { + (void)is.get(); + // If we consumed a character and there might be a matched keyword that + // was marked matched on a previous iteration, then such keywords + // are now marked as not matching. + if (n_might_match + n_does_match > 1) + { + st = status; + for (auto ky = kb; ky != ke; ++ky, ++st) + { + if (*st == does_match && ky->size() != indx+1) + { + *st = doesnt_match; + --n_does_match; + } + } + } + } + } + // We've exited the loop because we hit eof and/or we have no more "might matches". + // Return the first matching result + for (st = status; kb != ke; ++kb, ++st) + if (*st == does_match) + break; + if (kb == ke) + is.setstate(ios_base::failbit); + return kb; +} + +} // namespace detail + +#endif // ONLY_C_LOCALE + +template +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, + const fields& fds, const std::string* abbrev, + const std::chrono::seconds* offset_sec) +{ + using namespace std; + using namespace std::chrono; + using namespace detail; + date::detail::save_ostream ss(os); + os.fill(' '); + os.flags(std::ios::skipws | std::ios::dec); + os.width(0); + tm tm{}; + bool insert_negative = fds.has_tod && fds.tod.to_duration() < Duration::zero(); +#if !ONLY_C_LOCALE + auto& facet = use_facet>(os.getloc()); +#endif + const CharT* command = nullptr; + CharT modified = CharT{}; + for (; *fmt; ++fmt) + { + switch (*fmt) + { + case 'a': + case 'A': + if (command) + { + if (modified == CharT{}) + { + tm.tm_wday = static_cast(extract_weekday(os, fds)); + if (os.fail()) + return os; +#if !ONLY_C_LOCALE + const CharT f[] = {'%', *fmt}; + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); +#else // ONLY_C_LOCALE + os << weekday_names().first[tm.tm_wday+7*(*fmt == 'a')]; +#endif // ONLY_C_LOCALE + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'b': + case 'B': + case 'h': + if (command) + { + if (modified == CharT{}) + { + tm.tm_mon = static_cast(extract_month(os, fds)) - 1; +#if !ONLY_C_LOCALE + const CharT f[] = {'%', *fmt}; + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); +#else // ONLY_C_LOCALE + os << month_names().first[tm.tm_mon+12*(*fmt != 'B')]; +#endif // ONLY_C_LOCALE + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'c': + case 'x': + if (command) + { + if (modified == CharT{'O'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.ymd.ok()) + os.setstate(std::ios::failbit); + if (*fmt == 'c' && !fds.has_tod) + os.setstate(std::ios::failbit); +#if !ONLY_C_LOCALE + tm = std::tm{}; + auto const& ymd = fds.ymd; + auto ld = local_days(ymd); + if (*fmt == 'c') + { + tm.tm_sec = static_cast(fds.tod.seconds().count()); + tm.tm_min = static_cast(fds.tod.minutes().count()); + tm.tm_hour = static_cast(fds.tod.hours().count()); + } + tm.tm_mday = static_cast(static_cast(ymd.day())); + tm.tm_mon = static_cast(extract_month(os, fds) - 1); + tm.tm_year = static_cast(ymd.year()) - 1900; + tm.tm_wday = static_cast(extract_weekday(os, fds)); + if (os.fail()) + return os; + tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); + CharT f[3] = {'%'}; + auto fe = begin(f) + 1; + if (modified == CharT{'E'}) + *fe++ = modified; + *fe++ = *fmt; + facet.put(os, os, os.fill(), &tm, begin(f), fe); +#else // ONLY_C_LOCALE + if (*fmt == 'c') + { + auto wd = static_cast(extract_weekday(os, fds)); + os << weekday_names().first[static_cast(wd)+7] + << ' '; + os << month_names().first[extract_month(os, fds)-1+12] << ' '; + auto d = static_cast(static_cast(fds.ymd.day())); + if (d < 10) + os << ' '; + os << d << ' ' + << make_time(duration_cast(fds.tod.to_duration())) + << ' ' << fds.ymd.year(); + + } + else // *fmt == 'x' + { + auto const& ymd = fds.ymd; + save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << static_cast(ymd.month()) << CharT{'/'}; + os.width(2); + os << static_cast(ymd.day()) << CharT{'/'}; + os.width(2); + os << static_cast(ymd.year()) % 100; + } +#endif // ONLY_C_LOCALE + } + command = nullptr; + modified = CharT{}; + } + else + os << *fmt; + break; + case 'C': + if (command) + { + if (modified == CharT{'O'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.ymd.year().ok()) + os.setstate(std::ios::failbit); + auto y = static_cast(fds.ymd.year()); +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + if (y >= 0) + { + os.width(2); + os << y/100; + } + else + { + os << CharT{'-'}; + os.width(2); + os << -(y-99)/100; + } + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'E'}) + { + tm.tm_year = y - 1900; + CharT f[3] = {'%', 'E', 'C'}; + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + } +#endif + } + command = nullptr; + modified = CharT{}; + } + else + os << *fmt; + break; + case 'd': + case 'e': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.ymd.day().ok()) + os.setstate(std::ios::failbit); + auto d = static_cast(static_cast(fds.ymd.day())); +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + save_ostream _(os); + if (*fmt == CharT{'d'}) + os.fill('0'); + else + os.fill(' '); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << d; + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + tm.tm_mday = d; + CharT f[3] = {'%', 'O', *fmt}; + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + } +#endif + } + command = nullptr; + modified = CharT{}; + } + else + os << *fmt; + break; + case 'D': + if (command) + { + if (modified == CharT{}) + { + if (!fds.ymd.ok()) + os.setstate(std::ios::failbit); + auto const& ymd = fds.ymd; + save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << static_cast(ymd.month()) << CharT{'/'}; + os.width(2); + os << static_cast(ymd.day()) << CharT{'/'}; + os.width(2); + os << static_cast(ymd.year()) % 100; + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'F': + if (command) + { + if (modified == CharT{}) + { + if (!fds.ymd.ok()) + os.setstate(std::ios::failbit); + auto const& ymd = fds.ymd; + save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(4); + os << static_cast(ymd.year()) << CharT{'-'}; + os.width(2); + os << static_cast(ymd.month()) << CharT{'-'}; + os.width(2); + os << static_cast(ymd.day()); + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'g': + case 'G': + if (command) + { + if (modified == CharT{}) + { + if (!fds.ymd.ok()) + os.setstate(std::ios::failbit); + auto ld = local_days(fds.ymd); + auto y = year_month_day{ld + days{3}}.year(); + auto start = local_days((y-years{1})/December/Thursday[last]) + + (Monday-Thursday); + if (ld < start) + --y; + if (*fmt == CharT{'G'}) + os << y; + else + { + save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(2); + os << std::abs(static_cast(y)) % 100; + } + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'H': + case 'I': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); + if (insert_negative) + { + os << '-'; + insert_negative = false; + } + auto hms = fds.tod; +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + if (*fmt == CharT{'I'}) + hms.make12(); + if (hms.hours() < hours{10}) + os << CharT{'0'}; + os << hms.hours().count(); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_hour = static_cast(hms.hours().count()); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'j': + if (command) + { + if (modified == CharT{}) + { + if (!fds.ymd.ok()) + os.setstate(std::ios::failbit); + auto ld = local_days(fds.ymd); + auto y = fds.ymd.year(); + auto doy = ld - local_days(y/January/1) + days{1}; + save_ostream _(os); + os.fill('0'); + os.flags(std::ios::dec | std::ios::right); + os.width(3); + os << doy.count(); + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'm': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.ymd.month().ok()) + os.setstate(std::ios::failbit); + auto m = static_cast(fds.ymd.month()); +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + if (m < 10) + os << CharT{'0'}; + os << m; + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_mon = static_cast(m-1); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'M': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); + if (insert_negative) + { + os << '-'; + insert_negative = false; + } +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + if (fds.tod.minutes() < minutes{10}) + os << CharT{'0'}; + os << fds.tod.minutes().count(); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_min = static_cast(fds.tod.minutes().count()); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'n': + if (command) + { + if (modified == CharT{}) + os << CharT{'\n'}; + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'p': + if (command) + { + if (modified == CharT{}) + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); +#if !ONLY_C_LOCALE + const CharT f[] = {'%', *fmt}; + tm.tm_hour = static_cast(fds.tod.hours().count()); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); +#else + if (fds.tod.hours() < hours{12}) + os << ampm_names().first[0]; + else + os << ampm_names().first[1]; +#endif + } + else + { + os << CharT{'%'} << modified << *fmt; + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'r': + if (command) + { + if (modified == CharT{}) + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); +#if !ONLY_C_LOCALE + const CharT f[] = {'%', *fmt}; + tm.tm_hour = static_cast(fds.tod.hours().count()); + tm.tm_min = static_cast(fds.tod.minutes().count()); + tm.tm_sec = static_cast(fds.tod.seconds().count()); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); +#else + time_of_day tod(duration_cast(fds.tod.to_duration())); + tod.make12(); + save_ostream _(os); + os.fill('0'); + os.width(2); + os << tod.hours().count() << CharT{':'}; + os.width(2); + os << tod.minutes().count() << CharT{':'}; + os.width(2); + os << tod.seconds().count() << CharT{' '}; + tod.make24(); + if (tod.hours() < hours{12}) + os << ampm_names().first[0]; + else + os << ampm_names().first[1]; +#endif + } + else + { + os << CharT{'%'} << modified << *fmt; + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'R': + if (command) + { + if (modified == CharT{}) + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); + if (fds.tod.hours() < hours{10}) + os << CharT{'0'}; + os << fds.tod.hours().count() << CharT{':'}; + if (fds.tod.minutes() < minutes{10}) + os << CharT{'0'}; + os << fds.tod.minutes().count(); + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'S': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); + if (insert_negative) + { + os << '-'; + insert_negative = false; + } +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + os << fds.tod.s_; + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_sec = static_cast(fds.tod.s_.seconds().count()); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 't': + if (command) + { + if (modified == CharT{}) + os << CharT{'\t'}; + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'T': + if (command) + { + if (modified == CharT{}) + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); + os << fds.tod; + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'u': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + auto wd = extract_weekday(os, fds); +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + os << (wd != 0 ? wd : 7u); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_wday = static_cast(wd); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'U': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + auto const& ymd = fds.ymd; + if (!ymd.ok()) + os.setstate(std::ios::failbit); + auto ld = local_days(ymd); +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + auto st = local_days(Sunday[1]/January/ymd.year()); + if (ld < st) + os << CharT{'0'} << CharT{'0'}; + else + { + auto wn = duration_cast(ld - st).count() + 1; + if (wn < 10) + os << CharT{'0'}; + os << wn; + } + } + #if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_year = static_cast(ymd.year()) - 1900; + tm.tm_wday = static_cast(extract_weekday(os, fds)); + if (os.fail()) + return os; + tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'V': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.ymd.ok()) + os.setstate(std::ios::failbit); + auto ld = local_days(fds.ymd); +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + auto y = year_month_day{ld + days{3}}.year(); + auto st = local_days((y-years{1})/12/Thursday[last]) + + (Monday-Thursday); + if (ld < st) + { + --y; + st = local_days((y - years{1})/12/Thursday[last]) + + (Monday-Thursday); + } + auto wn = duration_cast(ld - st).count() + 1; + if (wn < 10) + os << CharT{'0'}; + os << wn; + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + auto const& ymd = fds.ymd; + tm.tm_year = static_cast(ymd.year()) - 1900; + tm.tm_wday = static_cast(extract_weekday(os, fds)); + if (os.fail()) + return os; + tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'w': + if (command) + { + auto wd = extract_weekday(os, fds); + if (os.fail()) + return os; +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#else + if (modified != CharT{'E'}) +#endif + { + os << wd; + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_wday = static_cast(wd); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + } +#endif + else + { + os << CharT{'%'} << modified << *fmt; + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'W': + if (command) + { + if (modified == CharT{'E'}) + os << CharT{'%'} << modified << *fmt; + else + { + auto const& ymd = fds.ymd; + if (!ymd.ok()) + os.setstate(std::ios::failbit); + auto ld = local_days(ymd); +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + auto st = local_days(Monday[1]/January/ymd.year()); + if (ld < st) + os << CharT{'0'} << CharT{'0'}; + else + { + auto wn = duration_cast(ld - st).count() + 1; + if (wn < 10) + os << CharT{'0'}; + os << wn; + } + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_year = static_cast(ymd.year()) - 1900; + tm.tm_wday = static_cast(extract_weekday(os, fds)); + if (os.fail()) + return os; + tm.tm_yday = static_cast((ld - local_days(ymd.year()/1/1)).count()); + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'X': + if (command) + { + if (modified == CharT{'O'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.has_tod) + os.setstate(std::ios::failbit); +#if !ONLY_C_LOCALE + tm = std::tm{}; + tm.tm_sec = static_cast(fds.tod.seconds().count()); + tm.tm_min = static_cast(fds.tod.minutes().count()); + tm.tm_hour = static_cast(fds.tod.hours().count()); + CharT f[3] = {'%'}; + auto fe = begin(f) + 1; + if (modified == CharT{'E'}) + *fe++ = modified; + *fe++ = *fmt; + facet.put(os, os, os.fill(), &tm, begin(f), fe); +#else + os << fds.tod; +#endif + } + command = nullptr; + modified = CharT{}; + } + else + os << *fmt; + break; + case 'y': + if (command) + { + if (!fds.ymd.year().ok()) + os.setstate(std::ios::failbit); + auto y = static_cast(fds.ymd.year()); +#if !ONLY_C_LOCALE + if (modified == CharT{}) + { +#endif + y = std::abs(y) % 100; + if (y < 10) + os << CharT{'0'}; + os << y; +#if !ONLY_C_LOCALE + } + else + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_year = y - 1900; + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + } +#endif + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'Y': + if (command) + { + if (modified == CharT{'O'}) + os << CharT{'%'} << modified << *fmt; + else + { + if (!fds.ymd.year().ok()) + os.setstate(std::ios::failbit); + auto y = fds.ymd.year(); +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + os << y; + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'E'}) + { + const CharT f[] = {'%', modified, *fmt}; + tm.tm_year = static_cast(y) - 1900; + facet.put(os, os, os.fill(), &tm, begin(f), end(f)); + } +#endif + } + modified = CharT{}; + command = nullptr; + } + else + os << *fmt; + break; + case 'z': + if (command) + { + if (offset_sec == nullptr) + { + // Can not format %z with unknown offset + os.setstate(ios::failbit); + return os; + } + auto m = duration_cast(*offset_sec); + auto neg = m < minutes{0}; + m = date::abs(m); + auto h = duration_cast(m); + m -= h; + if (neg) + os << CharT{'-'}; + else + os << CharT{'+'}; + if (h < hours{10}) + os << CharT{'0'}; + os << h.count(); + if (modified != CharT{}) + os << CharT{':'}; + if (m < minutes{10}) + os << CharT{'0'}; + os << m.count(); + command = nullptr; + modified = CharT{}; + } + else + os << *fmt; + break; + case 'Z': + if (command) + { + if (modified == CharT{}) + { + if (abbrev == nullptr) + { + // Can not format %Z with unknown time_zone + os.setstate(ios::failbit); + return os; + } + for (auto c : *abbrev) + os << CharT(c); + } + else + { + os << CharT{'%'} << modified << *fmt; + modified = CharT{}; + } + command = nullptr; + } + else + os << *fmt; + break; + case 'E': + case 'O': + if (command) + { + if (modified == CharT{}) + { + modified = *fmt; + } + else + { + os << CharT{'%'} << modified << *fmt; + command = nullptr; + modified = CharT{}; + } + } + else + os << *fmt; + break; + case '%': + if (command) + { + if (modified == CharT{}) + { + os << CharT{'%'}; + command = nullptr; + } + else + { + os << CharT{'%'} << modified << CharT{'%'}; + command = nullptr; + modified = CharT{}; + } + } + else + command = fmt; + break; + default: + if (command) + { + os << CharT{'%'}; + command = nullptr; + } + if (modified != CharT{}) + { + os << modified; + modified = CharT{}; + } + os << *fmt; + break; + } + } + if (command) + os << CharT{'%'}; + if (modified != CharT{}) + os << modified; + return os; +} + +template +inline +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, const year& y) +{ + using CT = std::chrono::seconds; + fields fds{y/0/0}; + return to_stream(os, fmt, fds); +} + +template +inline +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, const month& m) +{ + using CT = std::chrono::seconds; + fields fds{m/0/nanyear}; + return to_stream(os, fmt, fds); +} + +template +inline +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, const day& d) +{ + using CT = std::chrono::seconds; + fields fds{d/0/nanyear}; + return to_stream(os, fmt, fds); +} + +template +inline +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, const weekday& wd) +{ + using CT = std::chrono::seconds; + fields fds{wd}; + return to_stream(os, fmt, fds); +} + +template +inline +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, const year_month& ym) +{ + using CT = std::chrono::seconds; + fields fds{ym/0}; + return to_stream(os, fmt, fds); +} + +template +inline +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, const month_day& md) +{ + using CT = std::chrono::seconds; + fields fds{md/nanyear}; + return to_stream(os, fmt, fds); +} + +template +inline +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, + const year_month_day& ymd) +{ + using CT = std::chrono::seconds; + fields fds{ymd}; + return to_stream(os, fmt, fds); +} + +template +inline +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, + const std::chrono::duration& d) +{ + using Duration = std::chrono::duration; + using CT = typename std::common_type::type; + fields fds{time_of_day{d}}; + return to_stream(os, fmt, fds); +} + +template +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, + const local_time& tp, const std::string* abbrev = nullptr, + const std::chrono::seconds* offset_sec = nullptr) +{ + using CT = typename std::common_type::type; + auto ld = floor(tp); + fields fds{year_month_day{ld}, time_of_day{tp-local_seconds{ld}}}; + return to_stream(os, fmt, fds, abbrev, offset_sec); +} + +template +std::basic_ostream& +to_stream(std::basic_ostream& os, const CharT* fmt, + const sys_time& tp) +{ + using namespace std::chrono; + using CT = typename std::common_type::type; + const std::string abbrev("UTC"); + CONSTDATA seconds offset{0}; + auto sd = floor(tp); + fields fds{year_month_day{sd}, time_of_day{tp-sys_seconds{sd}}}; + return to_stream(os, fmt, fds, &abbrev, &offset); +} + +// format + +template +auto +format(const std::locale& loc, const CharT* fmt, const Streamable& tp) + -> decltype(to_stream(std::declval&>(), fmt, tp), + std::basic_string{}) +{ + std::basic_ostringstream os; + os.exceptions(std::ios::failbit | std::ios::badbit); + os.imbue(loc); + to_stream(os, fmt, tp); + return os.str(); +} + +template +auto +format(const CharT* fmt, const Streamable& tp) + -> decltype(to_stream(std::declval&>(), fmt, tp), + std::basic_string{}) +{ + std::basic_ostringstream os; + os.exceptions(std::ios::failbit | std::ios::badbit); + to_stream(os, fmt, tp); + return os.str(); +} + +template +auto +format(const std::locale& loc, const std::basic_string& fmt, + const Streamable& tp) + -> decltype(to_stream(std::declval&>(), fmt.c_str(), tp), + std::basic_string{}) +{ + std::basic_ostringstream os; + os.exceptions(std::ios::failbit | std::ios::badbit); + os.imbue(loc); + to_stream(os, fmt.c_str(), tp); + return os.str(); +} + +template +auto +format(const std::basic_string& fmt, const Streamable& tp) + -> decltype(to_stream(std::declval&>(), fmt.c_str(), tp), + std::basic_string{}) +{ + std::basic_ostringstream os; + os.exceptions(std::ios::failbit | std::ios::badbit); + to_stream(os, fmt.c_str(), tp); + return os.str(); +} + +// parse + +namespace detail +{ + +template +bool +read_char(std::basic_istream& is, CharT fmt, std::ios::iostate& err) +{ + auto ic = is.get(); + if (Traits::eq_int_type(ic, Traits::eof()) || + !Traits::eq(Traits::to_char_type(ic), fmt)) + { + err |= std::ios::failbit; + is.setstate(std::ios::failbit); + return false; + } + return true; +} + +template +unsigned +read_unsigned(std::basic_istream& is, unsigned m = 1, unsigned M = 10) +{ + unsigned x = 0; + unsigned count = 0; + while (true) + { + auto ic = is.peek(); + if (Traits::eq_int_type(ic, Traits::eof())) + break; + auto c = static_cast(Traits::to_char_type(ic)); + if (!('0' <= c && c <= '9')) + break; + (void)is.get(); + ++count; + x = 10*x + static_cast(c - '0'); + if (count == M) + break; + } + if (count < m) + is.setstate(std::ios::failbit); + return x; +} + +template +int +read_signed(std::basic_istream& is, unsigned m = 1, unsigned M = 10) +{ + auto ic = is.peek(); + if (!Traits::eq_int_type(ic, Traits::eof())) + { + auto c = static_cast(Traits::to_char_type(ic)); + if (('0' <= c && c <= '9') || c == '-' || c == '+') + { + if (c == '-' || c == '+') + (void)is.get(); + auto x = static_cast(read_unsigned(is, std::max(m, 1u), M)); + if (!is.fail()) + { + if (c == '-') + x = -x; + return x; + } + } + } + if (m > 0) + is.setstate(std::ios::failbit); + return 0; +} + +template +long double +read_long_double(std::basic_istream& is, unsigned m = 1, unsigned M = 10) +{ + using namespace std; + unsigned count = 0; + auto decimal_point = Traits::to_int_type( + use_facet>(is.getloc()).decimal_point()); + std::string buf; + while (true) + { + auto ic = is.peek(); + if (Traits::eq_int_type(ic, Traits::eof())) + break; + if (Traits::eq_int_type(ic, decimal_point)) + { + buf += '.'; + decimal_point = Traits::eof(); + is.get(); + } + else + { + auto c = static_cast(Traits::to_char_type(ic)); + if (!('0' <= c && c <= '9')) + break; + buf += c; + (void)is.get(); + } + if (++count == M) + break; + } + if (count < m) + { + is.setstate(std::ios::failbit); + return 0; + } + return std::stold(buf); +} + +struct rs +{ + int& i; + unsigned m; + unsigned M; +}; + +struct ru +{ + int& i; + unsigned m; + unsigned M; +}; + +struct rld +{ + long double& i; + unsigned m; + unsigned M; +}; + +template +void +read(std::basic_istream&) +{ +} + +template +void +read(std::basic_istream& is, CharT a0, Args&& ...args); + +template +void +read(std::basic_istream& is, rs a0, Args&& ...args); + +template +void +read(std::basic_istream& is, ru a0, Args&& ...args); + +template +void +read(std::basic_istream& is, int a0, Args&& ...args); + +template +void +read(std::basic_istream& is, rld a0, Args&& ...args); + +template +void +read(std::basic_istream& is, CharT a0, Args&& ...args) +{ + // No-op if a0 == CharT{} + if (a0 != CharT{}) + { + auto ic = is.peek(); + if (Traits::eq_int_type(ic, Traits::eof())) + { + is.setstate(std::ios::failbit | std::ios::eofbit); + return; + } + if (!Traits::eq(Traits::to_char_type(ic), a0)) + { + is.setstate(std::ios::failbit); + return; + } + (void)is.get(); + } + read(is, std::forward(args)...); +} + +template +void +read(std::basic_istream& is, rs a0, Args&& ...args) +{ + auto x = read_signed(is, a0.m, a0.M); + if (is.fail()) + return; + a0.i = x; + read(is, std::forward(args)...); +} + +template +void +read(std::basic_istream& is, ru a0, Args&& ...args) +{ + auto x = read_unsigned(is, a0.m, a0.M); + if (is.fail()) + return; + a0.i = static_cast(x); + read(is, std::forward(args)...); +} + +template +void +read(std::basic_istream& is, int a0, Args&& ...args) +{ + if (a0 != -1) + { + auto u = static_cast(a0); + CharT buf[std::numeric_limits::digits10+2] = {}; + auto e = buf; + do + { + *e++ = CharT(u % 10) + CharT{'0'}; + u /= 10; + } while (u > 0); + std::reverse(buf, e); + for (auto p = buf; p != e && is.rdstate() == std::ios::goodbit; ++p) + read(is, *p); + } + if (is.rdstate() == std::ios::goodbit) + read(is, std::forward(args)...); +} + +template +void +read(std::basic_istream& is, rld a0, Args&& ...args) +{ + auto x = read_long_double(is, a0.m, a0.M); + if (is.fail()) + return; + a0.i = x; + read(is, std::forward(args)...); +} + +template +inline +void +checked_set(T& value, T from, T not_a_value, std::basic_ios& is) +{ + if (!is.fail()) + { + if (value == not_a_value) + value = std::move(from); + else if (value != from) + is.setstate(std::ios::failbit); + } +} + +} // namespace detail; + +template > +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, + fields& fds, std::basic_string* abbrev, + std::chrono::minutes* offset) +{ + using namespace std; + using namespace std::chrono; + typename basic_istream::sentry ok{is, true}; + if (ok) + { + date::detail::save_istream ss(is); + is.fill(' '); + is.flags(std::ios::skipws | std::ios::dec); + is.width(0); +#if !ONLY_C_LOCALE + auto& f = use_facet>(is.getloc()); + std::tm tm{}; +#endif + const CharT* command = nullptr; + auto modified = CharT{}; + auto width = -1; + + CONSTDATA int not_a_year = numeric_limits::min(); + CONSTDATA int not_a_2digit_year = 100; + CONSTDATA int not_a_century = not_a_year / 100; + CONSTDATA int not_a_month = 0; + CONSTDATA int not_a_day = 0; + CONSTDATA int not_a_hour = numeric_limits::min(); + CONSTDATA int not_a_hour_12_value = 0; + CONSTDATA int not_a_minute = not_a_hour; + CONSTDATA Duration not_a_second = Duration::min(); + CONSTDATA int not_a_doy = 0; + CONSTDATA int not_a_weekday = 8; + CONSTDATA int not_a_week_num = 100; + CONSTDATA int not_a_ampm = -1; + CONSTDATA minutes not_a_offset = minutes::min(); + + int Y = not_a_year; // c, F, Y * + int y = not_a_2digit_year; // D, x, y * + int g = not_a_2digit_year; // g * + int G = not_a_year; // G * + int C = not_a_century; // C * + int m = not_a_month; // b, B, h, m, c, D, F, x * + int d = not_a_day; // c, d, D, e, F, x * + int j = not_a_doy; // j * + int wd = not_a_weekday; // a, A, u, w * + int H = not_a_hour; // c, H, R, T, X * + int I = not_a_hour_12_value; // I, r * + int p = not_a_ampm; // p, r * + int M = not_a_minute; // c, M, r, R, T, X * + Duration s = not_a_second; // c, r, S, T, X * + int U = not_a_week_num; // U * + int V = not_a_week_num; // V * + int W = not_a_week_num; // W * + std::basic_string temp_abbrev; // Z * + minutes temp_offset = not_a_offset; // z * + + using detail::read; + using detail::rs; + using detail::ru; + using detail::rld; + using detail::checked_set; + for (; *fmt && is.rdstate() == std::ios::goodbit; ++fmt) + { + switch (*fmt) + { + case 'a': + case 'A': + case 'u': + case 'w': // wd: a, A, u, w + if (command) + { + int trial_wd = not_a_weekday; + if (*fmt == 'a' || *fmt == 'A') + { + if (modified == CharT{}) + { +#if !ONLY_C_LOCALE + ios_base::iostate err = ios_base::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + is.setstate(err); + if (!is.fail()) + trial_wd = tm.tm_wday; +#else + auto nm = detail::weekday_names(); + auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; + if (!is.fail()) + trial_wd = i % 7; +#endif + } + else + read(is, CharT{'%'}, width, modified, *fmt); + } + else // *fmt == 'u' || *fmt == 'w' + { +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#else + if (modified != CharT{'E'}) +#endif + { + read(is, ru{trial_wd, 1, width == -1 ? + 1u : static_cast(width)}); + if (!is.fail()) + { + if (*fmt == 'u') + { + if (!(1 <= trial_wd && trial_wd <= 7)) + { + trial_wd = not_a_weekday; + is.setstate(ios_base::failbit); + } + else if (trial_wd == 7) + trial_wd = 0; + } + else // *fmt == 'w' + { + if (!(0 <= trial_wd && trial_wd <= 6)) + { + trial_wd = not_a_weekday; + is.setstate(ios_base::failbit); + } + } + } + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + is.setstate(err); + if (!is.fail()) + trial_wd = tm.tm_wday; + } +#endif + else + read(is, CharT{'%'}, width, modified, *fmt); + } + if (trial_wd != not_a_weekday) + checked_set(wd, trial_wd, not_a_weekday, is); + } + else // !command + read(is, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + break; + case 'b': + case 'B': + case 'h': + if (command) + { + if (modified == CharT{}) + { + int ttm = not_a_month; +#if !ONLY_C_LOCALE + ios_base::iostate err = ios_base::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + ttm = tm.tm_mon + 1; + is.setstate(err); +#else + auto nm = detail::month_names(); + auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; + if (!is.fail()) + ttm = i % 12 + 1; +#endif + checked_set(m, ttm, not_a_month, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'c': + if (command) + { + if (modified != CharT{'O'}) + { +#if !ONLY_C_LOCALE + ios_base::iostate err = ios_base::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + { + checked_set(Y, tm.tm_year + 1900, not_a_year, is); + checked_set(m, tm.tm_mon + 1, not_a_month, is); + checked_set(d, tm.tm_mday, not_a_day, is); + checked_set(H, tm.tm_hour, not_a_hour, is); + checked_set(M, tm.tm_min, not_a_minute, is); + checked_set(s, duration_cast(seconds{tm.tm_sec}), + not_a_second, is); + } + is.setstate(err); +#else + // "%a %b %e %T %Y" + auto nm = detail::weekday_names(); + auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; + checked_set(wd, static_cast(i % 7), not_a_weekday, is); + ws(is); + nm = detail::month_names(); + i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; + checked_set(m, static_cast(i % 12 + 1), not_a_month, is); + ws(is); + int td = not_a_day; + read(is, rs{td, 1, 2}); + checked_set(d, td, not_a_day, is); + ws(is); + using dfs = detail::decimal_format_seconds; + CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; + int tH; + int tM; + long double S; + read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2}, + CharT{':'}, rld{S, 1, w}); + checked_set(H, tH, not_a_hour, is); + checked_set(M, tM, not_a_minute, is); + checked_set(s, round(duration{S}), + not_a_second, is); + ws(is); + int tY = not_a_year; + read(is, rs{tY, 1, 4u}); + checked_set(Y, tY, not_a_year, is); +#endif + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'x': + if (command) + { + if (modified != CharT{'O'}) + { +#if !ONLY_C_LOCALE + ios_base::iostate err = ios_base::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + { + checked_set(Y, tm.tm_year + 1900, not_a_year, is); + checked_set(m, tm.tm_mon + 1, not_a_month, is); + checked_set(d, tm.tm_mday, not_a_day, is); + } + is.setstate(err); +#else + // "%m/%d/%y" + int ty = not_a_2digit_year; + int tm = not_a_month; + int td = not_a_day; + read(is, ru{tm, 1, 2}, CharT{'/'}, ru{td, 1, 2}, CharT{'/'}, + rs{ty, 1, 2}); + checked_set(y, ty, not_a_2digit_year, is); + checked_set(m, tm, not_a_month, is); + checked_set(d, td, not_a_day, is); +#endif + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'X': + if (command) + { + if (modified != CharT{'O'}) + { +#if !ONLY_C_LOCALE + ios_base::iostate err = ios_base::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + { + checked_set(H, tm.tm_hour, not_a_hour, is); + checked_set(M, tm.tm_min, not_a_minute, is); + checked_set(s, duration_cast(seconds{tm.tm_sec}), + not_a_second, is); + } + is.setstate(err); +#else + // "%T" + using dfs = detail::decimal_format_seconds; + CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; + int tH = not_a_hour; + int tM = not_a_minute; + long double S; + read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2}, + CharT{':'}, rld{S, 1, w}); + checked_set(H, tH, not_a_hour, is); + checked_set(M, tM, not_a_minute, is); + checked_set(s, round(duration{S}), + not_a_second, is); +#endif + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'C': + if (command) + { + int tC = not_a_century; +#if !ONLY_C_LOCALE + if (modified == CharT{}) + { +#endif + read(is, rs{tC, 1, width == -1 ? 2u : static_cast(width)}); +#if !ONLY_C_LOCALE + } + else + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + { + auto tY = tm.tm_year + 1900; + tC = (tY >= 0 ? tY : tY-99) / 100; + } + is.setstate(err); + } +#endif + checked_set(C, tC, not_a_century, is); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'D': + if (command) + { + if (modified == CharT{}) + { + int tn = not_a_month; + int td = not_a_day; + int ty = not_a_2digit_year; + read(is, ru{tn, 1, 2}, CharT{'\0'}, CharT{'/'}, CharT{'\0'}, + ru{td, 1, 2}, CharT{'\0'}, CharT{'/'}, CharT{'\0'}, + rs{ty, 1, 2}); + checked_set(y, ty, not_a_2digit_year, is); + checked_set(m, tn, not_a_month, is); + checked_set(d, td, not_a_day, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'F': + if (command) + { + if (modified == CharT{}) + { + int tY = not_a_year; + int tn = not_a_month; + int td = not_a_day; + read(is, rs{tY, 1, width == -1 ? 4u : static_cast(width)}, + CharT{'-'}, ru{tn, 1, 2}, CharT{'-'}, ru{td, 1, 2}); + checked_set(Y, tY, not_a_year, is); + checked_set(m, tn, not_a_month, is); + checked_set(d, td, not_a_day, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'd': + case 'e': + if (command) + { +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#else + if (modified != CharT{'E'}) +#endif + { + int td = not_a_day; + read(is, rs{td, 1, width == -1 ? 2u : static_cast(width)}); + checked_set(d, td, not_a_day, is); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + command = nullptr; + width = -1; + modified = CharT{}; + if ((err & ios::failbit) == 0) + checked_set(d, tm.tm_mday, not_a_day, is); + is.setstate(err); + } +#endif + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'H': + if (command) + { +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#else + if (modified != CharT{'E'}) +#endif + { + int tH = not_a_hour; + read(is, ru{tH, 1, width == -1 ? 2u : static_cast(width)}); + checked_set(H, tH, not_a_hour, is); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + checked_set(H, tm.tm_hour, not_a_hour, is); + is.setstate(err); + } +#endif + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'I': + if (command) + { + if (modified == CharT{}) + { + int tI = not_a_hour_12_value; + // reads in an hour into I, but most be in [1, 12] + read(is, rs{tI, 1, width == -1 ? 2u : static_cast(width)}); + if (!(1 <= tI && tI <= 12)) + is.setstate(ios::failbit); + checked_set(I, tI, not_a_hour_12_value, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'j': + if (command) + { + if (modified == CharT{}) + { + int tj = not_a_doy; + read(is, ru{tj, 1, width == -1 ? 3u : static_cast(width)}); + checked_set(j, tj, not_a_doy, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'M': + if (command) + { +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#else + if (modified != CharT{'E'}) +#endif + { + int tM = not_a_minute; + read(is, ru{tM, 1, width == -1 ? 2u : static_cast(width)}); + checked_set(M, tM, not_a_minute, is); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + checked_set(M, tm.tm_min, not_a_minute, is); + is.setstate(err); + } +#endif + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'm': + if (command) + { +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#else + if (modified != CharT{'E'}) +#endif + { + int tn = not_a_month; + read(is, rs{tn, 1, width == -1 ? 2u : static_cast(width)}); + checked_set(m, tn, not_a_month, is); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + checked_set(m, tm.tm_mon + 1, not_a_month, is); + is.setstate(err); + } +#endif + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'n': + case 't': + if (command) + { + if (modified == CharT{}) + { + // %n matches a single white space character + // %t matches 0 or 1 white space characters + auto ic = is.peek(); + if (Traits::eq_int_type(ic, Traits::eof())) + { + ios_base::iostate err = ios_base::eofbit; + if (*fmt == 'n') + err |= ios_base::failbit; + is.setstate(err); + break; + } + if (isspace(ic)) + { + (void)is.get(); + } + else if (*fmt == 'n') + is.setstate(ios_base::failbit); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'p': + if (command) + { + if (modified == CharT{}) + { + int tp = not_a_ampm; +#if !ONLY_C_LOCALE + tm = std::tm{}; + tm.tm_hour = 1; + ios_base::iostate err = ios_base::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + is.setstate(err); + if (tm.tm_hour == 1) + tp = 0; + else if (tm.tm_hour == 13) + tp = 1; + else + is.setstate(err); +#else + auto nm = detail::ampm_names(); + auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; + tp = i; +#endif + checked_set(p, tp, not_a_ampm, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + + break; + case 'r': + if (command) + { + if (modified == CharT{}) + { +#if !ONLY_C_LOCALE + ios_base::iostate err = ios_base::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + { + checked_set(H, tm.tm_hour, not_a_hour, is); + checked_set(M, tm.tm_min, not_a_hour, is); + checked_set(s, duration_cast(seconds{tm.tm_sec}), + not_a_second, is); + } + is.setstate(err); +#else + // "%I:%M:%S %p" + using dfs = detail::decimal_format_seconds; + CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; + long double S; + int tI = not_a_hour_12_value; + int tM = not_a_minute; + read(is, ru{tI, 1, 2}, CharT{':'}, ru{tM, 1, 2}, + CharT{':'}, rld{S, 1, w}); + checked_set(I, tI, not_a_hour_12_value, is); + checked_set(M, tM, not_a_minute, is); + checked_set(s, round(duration{S}), + not_a_second, is); + ws(is); + auto nm = detail::ampm_names(); + auto i = detail::scan_keyword(is, nm.first, nm.second) - nm.first; + checked_set(p, static_cast(i), not_a_ampm, is); +#endif + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'R': + if (command) + { + if (modified == CharT{}) + { + int tH = not_a_hour; + int tM = not_a_minute; + read(is, ru{tH, 1, 2}, CharT{'\0'}, CharT{':'}, CharT{'\0'}, + ru{tM, 1, 2}, CharT{'\0'}); + checked_set(H, tH, not_a_hour, is); + checked_set(M, tM, not_a_minute, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'S': + if (command) + { + #if !ONLY_C_LOCALE + if (modified == CharT{}) +#else + if (modified != CharT{'E'}) +#endif + { + using dfs = detail::decimal_format_seconds; + CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; + long double S; + read(is, rld{S, 1, width == -1 ? w : static_cast(width)}); + checked_set(s, round(duration{S}), + not_a_second, is); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'O'}) + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + checked_set(s, duration_cast(seconds{tm.tm_sec}), + not_a_second, is); + is.setstate(err); + } +#endif + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'T': + if (command) + { + if (modified == CharT{}) + { + using dfs = detail::decimal_format_seconds; + CONSTDATA auto w = Duration::period::den == 1 ? 2 : 3 + dfs::width; + int tH = not_a_hour; + int tM = not_a_minute; + long double S; + read(is, ru{tH, 1, 2}, CharT{':'}, ru{tM, 1, 2}, + CharT{':'}, rld{S, 1, w}); + checked_set(H, tH, not_a_hour, is); + checked_set(M, tM, not_a_minute, is); + checked_set(s, round(duration{S}), + not_a_second, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'Y': + if (command) + { +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#else + if (modified != CharT{'O'}) +#endif + { + int tY = not_a_year; + read(is, rs{tY, 1, width == -1 ? 4u : static_cast(width)}); + checked_set(Y, tY, not_a_year, is); + } +#if !ONLY_C_LOCALE + else if (modified == CharT{'E'}) + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + checked_set(Y, tm.tm_year + 1900, not_a_year, is); + is.setstate(err); + } +#endif + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'y': + if (command) + { +#if !ONLY_C_LOCALE + if (modified == CharT{}) +#endif + { + int ty = not_a_2digit_year; + read(is, ru{ty, 1, width == -1 ? 2u : static_cast(width)}); + checked_set(y, ty, not_a_2digit_year, is); + } +#if !ONLY_C_LOCALE + else + { + ios_base::iostate err = ios_base::goodbit; + f.get(is, nullptr, is, err, &tm, command, fmt+1); + if ((err & ios::failbit) == 0) + checked_set(Y, tm.tm_year + 1900, not_a_year, is); + is.setstate(err); + } +#endif + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'g': + if (command) + { + if (modified == CharT{}) + { + int tg = not_a_2digit_year; + read(is, ru{tg, 1, width == -1 ? 2u : static_cast(width)}); + checked_set(g, tg, not_a_2digit_year, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'G': + if (command) + { + if (modified == CharT{}) + { + int tG = not_a_year; + read(is, rs{tG, 1, width == -1 ? 4u : static_cast(width)}); + checked_set(G, tG, not_a_year, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'U': + if (command) + { + if (modified == CharT{}) + { + int tU = not_a_week_num; + read(is, ru{tU, 1, width == -1 ? 2u : static_cast(width)}); + checked_set(U, tU, not_a_week_num, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'V': + if (command) + { + if (modified == CharT{}) + { + int tV = not_a_week_num; + read(is, ru{tV, 1, width == -1 ? 2u : static_cast(width)}); + checked_set(V, tV, not_a_week_num, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'W': + if (command) + { + if (modified == CharT{}) + { + int tW = not_a_week_num; + read(is, ru{tW, 1, width == -1 ? 2u : static_cast(width)}); + checked_set(W, tW, not_a_week_num, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'E': + case 'O': + if (command) + { + if (modified == CharT{}) + { + modified = *fmt; + } + else + { + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + } + else + read(is, *fmt); + break; + case '%': + if (command) + { + if (modified == CharT{}) + read(is, *fmt); + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + command = fmt; + break; + case 'z': + if (command) + { + int tH, tM; + minutes toff = not_a_offset; + bool neg = false; + auto ic = is.peek(); + if (!Traits::eq_int_type(ic, Traits::eof())) + { + auto c = static_cast(Traits::to_char_type(ic)); + if (c == '-') + neg = true; + } + if (modified == CharT{}) + { + read(is, rs{tH, 2, 2}); + if (!is.fail()) + toff = hours{std::abs(tH)}; + if (is.good()) + { + ic = is.peek(); + if (!Traits::eq_int_type(ic, Traits::eof())) + { + auto c = static_cast(Traits::to_char_type(ic)); + if ('0' <= c && c <= '9') + { + read(is, ru{tM, 2, 2}); + if (!is.fail()) + toff += minutes{tM}; + } + } + } + } + else + { + read(is, rs{tH, 1, 2}); + if (!is.fail()) + toff = hours{std::abs(tH)}; + if (is.good()) + { + ic = is.peek(); + if (!Traits::eq_int_type(ic, Traits::eof())) + { + auto c = static_cast(Traits::to_char_type(ic)); + if (c == ':') + { + (void)is.get(); + read(is, ru{tM, 2, 2}); + if (!is.fail()) + toff += minutes{tM}; + } + } + } + } + if (neg) + toff = -toff; + checked_set(temp_offset, toff, not_a_offset, is); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + case 'Z': + if (command) + { + if (modified == CharT{}) + { + std::basic_string buf; + while (is.rdstate() == std::ios::goodbit) + { + auto i = is.rdbuf()->sgetc(); + if (Traits::eq_int_type(i, Traits::eof())) + { + is.setstate(ios::eofbit); + break; + } + auto wc = Traits::to_char_type(i); + auto c = static_cast(wc); + // is c a valid time zone name or abbreviation character? + if (!(CharT{1} < wc && wc < CharT{127}) || !(isalnum(c) || + c == '_' || c == '/' || c == '-' || c == '+')) + break; + buf.push_back(c); + is.rdbuf()->sbumpc(); + } + if (buf.empty()) + is.setstate(ios::failbit); + checked_set(temp_abbrev, buf, {}, is); + } + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + else + read(is, *fmt); + break; + default: + if (command) + { + if (width == -1 && modified == CharT{} && '0' <= *fmt && *fmt <= '9') + { + width = static_cast(*fmt) - '0'; + while ('0' <= fmt[1] && fmt[1] <= '9') + width = 10*width + static_cast(*++fmt) - '0'; + } + else + { + if (modified == CharT{}) + read(is, CharT{'%'}, width, *fmt); + else + read(is, CharT{'%'}, width, modified, *fmt); + command = nullptr; + width = -1; + modified = CharT{}; + } + } + else // !command + { + if (isspace(static_cast(*fmt))) + ws(is); // space matches 0 or more white space characters + else + read(is, *fmt); + } + break; + } + } + // is.rdstate() != ios::goodbit || *fmt == CharT{} + if (is.rdstate() == ios::goodbit && command) + { + if (modified == CharT{}) + read(is, CharT{'%'}, width); + else + read(is, CharT{'%'}, width, modified); + } + if (is.rdstate() != ios::goodbit && *fmt != CharT{} && !is.fail()) + is.setstate(ios::failbit); + if (!is.fail()) + { + if (y != not_a_2digit_year) + { + // Convert y and an optional C to Y + if (!(0 <= y && y <= 99)) + goto broken; + if (C == not_a_century) + { + if (Y == not_a_year) + { + if (y >= 69) + C = 19; + else + C = 20; + } + else + { + C = (Y >= 0 ? Y : Y-100) / 100; + } + } + int tY; + if (C >= 0) + tY = 100*C + y; + else + tY = 100*(C+1) - (y == 0 ? 100 : y); + if (Y != not_a_year && Y != tY) + goto broken; + Y = tY; + } + if (g != not_a_2digit_year) + { + // Convert g and an optional C to G + if (!(0 <= g && g <= 99)) + goto broken; + if (C == not_a_century) + { + if (G == not_a_year) + { + if (g >= 69) + C = 19; + else + C = 20; + } + else + { + C = (G >= 0 ? G : G-100) / 100; + } + } + int tG; + if (C >= 0) + tG = 100*C + g; + else + tG = 100*(C+1) - (g == 0 ? 100 : g); + if (G != not_a_year && G != tG) + goto broken; + G = tG; + } + if (Y < static_cast(year::min()) || Y > static_cast(year::max())) + Y = not_a_year; + bool computed = false; + if (G != not_a_year && V != not_a_week_num && wd != not_a_weekday) + { + year_month_day ymd_trial = sys_days(year{G-1}/December/Thursday[last]) + + (Monday-Thursday) + weeks{V-1} + + (weekday{static_cast(wd)}-Monday); + if (Y == not_a_year) + Y = static_cast(ymd_trial.year()); + else if (year{Y} != ymd_trial.year()) + goto broken; + if (m == not_a_month) + m = static_cast(static_cast(ymd_trial.month())); + else if (month(static_cast(m)) != ymd_trial.month()) + goto broken; + if (d == not_a_day) + d = static_cast(static_cast(ymd_trial.day())); + else if (day(static_cast(d)) != ymd_trial.day()) + goto broken; + computed = true; + } + if (Y != not_a_year && U != not_a_week_num && wd != not_a_weekday) + { + year_month_day ymd_trial = sys_days(year{Y}/January/Sunday[1]) + + weeks{U-1} + + (weekday{static_cast(wd)} - Sunday); + if (Y == not_a_year) + Y = static_cast(ymd_trial.year()); + else if (year{Y} != ymd_trial.year()) + goto broken; + if (m == not_a_month) + m = static_cast(static_cast(ymd_trial.month())); + else if (month(static_cast(m)) != ymd_trial.month()) + goto broken; + if (d == not_a_day) + d = static_cast(static_cast(ymd_trial.day())); + else if (day(static_cast(d)) != ymd_trial.day()) + goto broken; + computed = true; + } + if (Y != not_a_year && W != not_a_week_num && wd != not_a_weekday) + { + year_month_day ymd_trial = sys_days(year{Y}/January/Monday[1]) + + weeks{W-1} + + (weekday{static_cast(wd)} - Monday); + if (Y == not_a_year) + Y = static_cast(ymd_trial.year()); + else if (year{Y} != ymd_trial.year()) + goto broken; + if (m == not_a_month) + m = static_cast(static_cast(ymd_trial.month())); + else if (month(static_cast(m)) != ymd_trial.month()) + goto broken; + if (d == not_a_day) + d = static_cast(static_cast(ymd_trial.day())); + else if (day(static_cast(d)) != ymd_trial.day()) + goto broken; + computed = true; + } + if (j != 0 && Y != not_a_year) + { + auto ymd_trial = year_month_day{local_days(year{Y}/1/1) + days{j-1}}; + if (m == 0) + m = static_cast(static_cast(ymd_trial.month())); + else if (month(static_cast(m)) != ymd_trial.month()) + goto broken; + if (d == 0) + d = static_cast(static_cast(ymd_trial.day())); + else if (day(static_cast(d)) != ymd_trial.day()) + goto broken; + } + auto ymd = year{Y}/m/d; + if (ymd.ok()) + { + if (wd == not_a_weekday) + wd = static_cast((weekday(sys_days(ymd)) - Sunday).count()); + else if (wd != static_cast((weekday(sys_days(ymd)) - Sunday).count())) + goto broken; + if (!computed) + { + if (G != not_a_year || V != not_a_week_num) + { + sys_days sd = ymd; + auto G_trial = year_month_day{sd + days{3}}.year(); + auto start = sys_days((G_trial - years{1})/December/Thursday[last]) + + (Monday - Thursday); + if (sd < start) + { + --G_trial; + if (V != not_a_week_num) + start = sys_days((G_trial - years{1})/December/Thursday[last]) + + (Monday - Thursday); + } + if (G != not_a_year && G != static_cast(G_trial)) + goto broken; + if (V != not_a_week_num) + { + auto V_trial = duration_cast(sd - start).count() + 1; + if (V != V_trial) + goto broken; + } + } + if (U != not_a_week_num) + { + auto start = sys_days(Sunday[1]/January/ymd.year()); + auto U_trial = floor(sys_days(ymd) - start).count() + 1; + if (U != U_trial) + goto broken; + } + if (W != not_a_week_num) + { + auto start = sys_days(Monday[1]/January/ymd.year()); + auto W_trial = floor(sys_days(ymd) - start).count() + 1; + if (W != W_trial) + goto broken; + } + } + } + fds.ymd = ymd; + if (I != not_a_hour_12_value) + { + if (!(1 <= I && I <= 12)) + goto broken; + if (p != not_a_ampm) + { + // p is in [0, 1] == [AM, PM] + // Store trial H in I + if (I == 12) + --p; + I += p*12; + // Either set H from I or make sure H and I are consistent + if (H == not_a_hour) + H = I; + else if (I != H) + goto broken; + } + else // p == not_a_ampm + { + // if H, make sure H and I could be consistent + if (H != not_a_hour) + { + if (I == 12) + { + if (H != 0 && H != 12) + goto broken; + } + else if (!(I == H || I == H+12)) + { + goto broken; + } + } + } + } + if (H != not_a_hour) + { + fds.has_tod = true; + fds.tod = time_of_day{hours{H}}; + } + if (M != not_a_minute) + { + fds.has_tod = true; + fds.tod.m_ = minutes{M}; + } + if (s != not_a_second) + { + fds.has_tod = true; + fds.tod.s_ = detail::decimal_format_seconds{s}; + } + if (wd != not_a_weekday) + fds.wd = weekday{static_cast(wd)}; + if (abbrev != nullptr) + *abbrev = std::move(temp_abbrev); + if (offset != nullptr && temp_offset != not_a_offset) + *offset = temp_offset; + } + return is; + } +broken: + is.setstate(ios_base::failbit); + return is; +} + +template > +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, year& y, + std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using namespace std; + using namespace std::chrono; + using CT = seconds; + fields fds{}; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.ymd.year().ok()) + is.setstate(ios::failbit); + if (!is.fail()) + y = fds.ymd.year(); + return is; +} + +template > +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, month& m, + std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using namespace std; + using namespace std::chrono; + using CT = seconds; + fields fds{}; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.ymd.month().ok()) + is.setstate(ios::failbit); + if (!is.fail()) + m = fds.ymd.month(); + return is; +} + +template > +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, day& d, + std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using namespace std; + using namespace std::chrono; + using CT = seconds; + fields fds{}; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.ymd.day().ok()) + is.setstate(ios::failbit); + if (!is.fail()) + d = fds.ymd.day(); + return is; +} + +template > +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, weekday& wd, + std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using namespace std; + using namespace std::chrono; + using CT = seconds; + fields fds{}; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.wd.ok()) + is.setstate(ios::failbit); + if (!is.fail()) + wd = fds.wd; + return is; +} + +template > +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, year_month& ym, + std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using namespace std; + using namespace std::chrono; + using CT = seconds; + fields fds{}; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.ymd.month().ok()) + is.setstate(ios::failbit); + if (!is.fail()) + ym = fds.ymd.year()/fds.ymd.month(); + return is; +} + +template > +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, month_day& md, + std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using namespace std; + using namespace std::chrono; + using CT = seconds; + fields fds{}; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.ymd.month().ok() || !fds.ymd.day().ok()) + is.setstate(ios::failbit); + if (!is.fail()) + md = fds.ymd.month()/fds.ymd.day(); + return is; +} + +template > +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, + year_month_day& ymd, std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using namespace std; + using namespace std::chrono; + using CT = seconds; + fields fds{}; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.ymd.ok()) + is.setstate(ios::failbit); + if (!is.fail()) + ymd = fds.ymd; + return is; +} + +template > +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, + sys_time& tp, std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using namespace std; + using namespace std::chrono; + using CT = typename common_type::type; + minutes offset_local{}; + auto offptr = offset ? offset : &offset_local; + fields fds{}; + fds.has_tod = true; + from_stream(is, fmt, fds, abbrev, offptr); + if (!fds.ymd.ok() || !fds.tod.in_conventional_range()) + is.setstate(ios::failbit); + if (!is.fail()) + tp = round(sys_days(fds.ymd) - *offptr + fds.tod.to_duration()); + return is; +} + +template > +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, + local_time& tp, std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using namespace std; + using namespace std::chrono; + using CT = typename common_type::type; + fields fds{}; + fds.has_tod = true; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.ymd.ok() || !fds.tod.in_conventional_range()) + is.setstate(ios::failbit); + if (!is.fail()) + tp = round(local_seconds{local_days(fds.ymd)} + fds.tod.to_duration()); + return is; +} + +template > +std::basic_istream& +from_stream(std::basic_istream& is, const CharT* fmt, + std::chrono::duration& d, + std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) +{ + using namespace std; + using namespace std::chrono; + using Duration = std::chrono::duration; + using CT = typename common_type::type; + fields fds{}; + from_stream(is, fmt, fds, abbrev, offset); + if (!fds.has_tod) + is.setstate(ios::failbit); + if (!is.fail()) + d = duration_cast(fds.tod.to_duration()); + return is; +} + +template , + class Alloc = std::allocator> +struct parse_manip +{ + const std::basic_string format_; + Parsable& tp_; + std::basic_string* abbrev_; + std::chrono::minutes* offset_; + +public: + parse_manip(std::basic_string format, Parsable& tp, + std::basic_string* abbrev = nullptr, + std::chrono::minutes* offset = nullptr) + : format_(std::move(format)) + , tp_(tp) + , abbrev_(abbrev) + , offset_(offset) + {} + +}; + +template +std::basic_istream& +operator>>(std::basic_istream& is, + const parse_manip& x) +{ + return from_stream(is, x.format_.c_str(), x.tp_, x.abbrev_, x.offset_); +} + +template +inline +auto +parse(const std::basic_string& format, Parsable& tp) + -> decltype(from_stream(std::declval&>(), + format.c_str(), tp), + parse_manip{format, tp}) +{ + return {format, tp}; +} + +template +inline +auto +parse(const std::basic_string& format, Parsable& tp, + std::basic_string& abbrev) + -> decltype(from_stream(std::declval&>(), + format.c_str(), tp, &abbrev), + parse_manip{format, tp, &abbrev}) +{ + return {format, tp, &abbrev}; +} + +template +inline +auto +parse(const std::basic_string& format, Parsable& tp, + std::chrono::minutes& offset) + -> decltype(from_stream(std::declval&>(), + format.c_str(), tp, + std::declval*>(), + &offset), + parse_manip{format, tp, nullptr, &offset}) +{ + return {format, tp, nullptr, &offset}; +} + +template +inline +auto +parse(const std::basic_string& format, Parsable& tp, + std::basic_string& abbrev, std::chrono::minutes& offset) + -> decltype(from_stream(std::declval&>(), + format.c_str(), tp, &abbrev, &offset), + parse_manip{format, tp, &abbrev, &offset}) +{ + return {format, tp, &abbrev, &offset}; +} + +// const CharT* formats + +template +inline +auto +parse(const CharT* format, Parsable& tp) + -> decltype(from_stream(std::declval&>(), format, tp), + parse_manip{format, tp}) +{ + return {format, tp}; +} + +template +inline +auto +parse(const CharT* format, Parsable& tp, std::basic_string& abbrev) + -> decltype(from_stream(std::declval&>(), format, + tp, &abbrev), + parse_manip{format, tp, &abbrev}) +{ + return {format, tp, &abbrev}; +} + +template +inline +auto +parse(const CharT* format, Parsable& tp, std::chrono::minutes& offset) + -> decltype(from_stream(std::declval&>(), format, + tp, std::declval*>(), &offset), + parse_manip{format, tp, nullptr, &offset}) +{ + return {format, tp, nullptr, &offset}; +} + +template +inline +auto +parse(const CharT* format, Parsable& tp, + std::basic_string& abbrev, std::chrono::minutes& offset) + -> decltype(from_stream(std::declval&>(), format, + tp, &abbrev, &offset), + parse_manip{format, tp, &abbrev, &offset}) +{ + return {format, tp, &abbrev, &offset}; +} + +// duration streaming + +namespace detail +{ + +template +class string_literal; + +template +inline +CONSTCD14 +string_literal::type, + N1 + N2 - 1> +operator+(const string_literal& x, const string_literal& y) NOEXCEPT; + +template +class string_literal +{ + CharT p_[N]; + + CONSTCD11 string_literal() NOEXCEPT + : p_{} + {} + +public: + using const_iterator = const CharT*; + + string_literal(string_literal const&) = default; + string_literal& operator=(string_literal const&) = delete; + + template ::type> + CONSTCD11 string_literal(CharT c) NOEXCEPT + : p_{c} + { + } + + template ::type> + CONSTCD11 string_literal(CharT c1, CharT c2) NOEXCEPT + : p_{c1, c2} + { + } + + template ::type> + CONSTCD11 string_literal(CharT c1, CharT c2, CharT c3) NOEXCEPT + : p_{c1, c2, c3} + { + } + + CONSTCD14 string_literal(const CharT(&a)[N]) NOEXCEPT + : p_{} + { + for (std::size_t i = 0; i < N; ++i) + p_[i] = a[i]; + } + + template ::type> + CONSTCD14 string_literal(const char(&a)[N]) NOEXCEPT + : p_{} + { + for (std::size_t i = 0; i < N; ++i) + p_[i] = a[i]; + } + + template ::value>::type> + CONSTCD14 string_literal(string_literal const& a) NOEXCEPT + : p_{} + { + for (std::size_t i = 0; i < N; ++i) + p_[i] = a[i]; + } + + CONSTCD11 const CharT* data() const NOEXCEPT {return p_;} + CONSTCD11 std::size_t size() const NOEXCEPT {return N-1;} + + CONSTCD11 const_iterator begin() const NOEXCEPT {return p_;} + CONSTCD11 const_iterator end() const NOEXCEPT {return p_ + N-1;} + + CONSTCD11 CharT const& operator[](std::size_t n) const NOEXCEPT + { + return p_[n]; + } + + template + friend + std::basic_ostream& + operator<<(std::basic_ostream& os, const string_literal& s) + { + return os << s.p_; + } + + template + friend + CONSTCD14 + string_literal::type, + N1 + N2 - 1> + operator+(const string_literal& x, const string_literal& y) NOEXCEPT; +}; + +template +CONSTCD11 +inline +string_literal +operator+(const string_literal& x, const string_literal& y) NOEXCEPT +{ + return string_literal(x[0], y[0]); +} + +template +CONSTCD11 +inline +string_literal +operator+(const string_literal& x, const string_literal& y) NOEXCEPT +{ + return string_literal(x[0], x[1], y[0]); +} + +template +CONSTCD14 +inline +string_literal::type, + N1 + N2 - 1> +operator+(const string_literal& x, const string_literal& y) NOEXCEPT +{ + using CT = typename std::conditional::type; + + string_literal r; + std::size_t i = 0; + for (; i < N1-1; ++i) + r.p_[i] = CT(x.p_[i]); + for (std::size_t j = 0; j < N2; ++j, ++i) + r.p_[i] = CT(y.p_[j]); + + return r; +} + + +template +inline +std::basic_string +operator+(std::basic_string x, const string_literal& y) +{ + x.append(y.data(), y.size()); + return x; +} + +#if __cplusplus >= 201402 && (!defined(__EDG_VERSION__) || __EDG_VERSION__ > 411) \ + && (!defined(__SUNPRO_CC) || __SUNPRO_CC > 0x5150) + +template {} || + std::is_same{} || + std::is_same{} || + std::is_same{}>> +CONSTCD14 +inline +string_literal +msl(CharT c) NOEXCEPT +{ + return string_literal{c}; +} + +CONSTCD14 +inline +std::size_t +to_string_len(std::intmax_t i) +{ + std::size_t r = 0; + do + { + i /= 10; + ++r; + } while (i > 0); + return r; +} + +template +CONSTCD14 +inline +std::enable_if_t +< + N < 10, + string_literal +> +msl() NOEXCEPT +{ + return msl(char(N % 10 + '0')); +} + +template +CONSTCD14 +inline +std::enable_if_t +< + 10 <= N, + string_literal +> +msl() NOEXCEPT +{ + return msl() + msl(char(N % 10 + '0')); +} + +template +CONSTCD14 +inline +std::enable_if_t +< + std::ratio::type::den != 1, + string_literal::type::num) + + to_string_len(std::ratio::type::den) + 4> +> +msl(std::ratio) NOEXCEPT +{ + using R = typename std::ratio::type; + return msl(CharT{'['}) + msl() + msl(CharT{'/'}) + + msl() + msl(CharT{']'}); +} + +template +CONSTCD14 +inline +std::enable_if_t +< + std::ratio::type::den == 1, + string_literal::type::num) + 3> +> +msl(std::ratio) NOEXCEPT +{ + using R = typename std::ratio::type; + return msl(CharT{'['}) + msl() + msl(CharT{']'}); +} + + +#else // __cplusplus < 201402 || (defined(__EDG_VERSION__) && __EDG_VERSION__ <= 411) + +inline +std::string +to_string(std::uint64_t x) +{ + return std::to_string(x); +} + +template +inline +std::basic_string +to_string(std::uint64_t x) +{ + auto y = std::to_string(x); + return std::basic_string(y.begin(), y.end()); +} + +template +inline +typename std::enable_if +< + std::ratio::type::den != 1, + std::basic_string +>::type +msl(std::ratio) +{ + using R = typename std::ratio::type; + return std::basic_string(1, '[') + to_string(R::num) + CharT{'/'} + + to_string(R::den) + CharT{']'}; +} + +template +inline +typename std::enable_if +< + std::ratio::type::den == 1, + std::basic_string +>::type +msl(std::ratio) +{ + using R = typename std::ratio::type; + return std::basic_string(1, '[') + to_string(R::num) + CharT{']'}; +} + +#endif // __cplusplus < 201402 || (defined(__EDG_VERSION__) && __EDG_VERSION__ <= 411) + +template +CONSTCD11 +inline +string_literal +msl(std::atto) NOEXCEPT +{ + return string_literal{'a'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::femto) NOEXCEPT +{ + return string_literal{'f'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::pico) NOEXCEPT +{ + return string_literal{'p'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::nano) NOEXCEPT +{ + return string_literal{'n'}; +} + +template +CONSTCD11 +inline +typename std::enable_if +< + std::is_same::value, + string_literal +>::type +msl(std::micro) NOEXCEPT +{ + return string_literal{'\xC2', '\xB5'}; +} + +template +CONSTCD11 +inline +typename std::enable_if +< + !std::is_same::value, + string_literal +>::type +msl(std::micro) NOEXCEPT +{ + return string_literal{CharT{static_cast('\xB5')}}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::milli) NOEXCEPT +{ + return string_literal{'m'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::centi) NOEXCEPT +{ + return string_literal{'c'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::deca) NOEXCEPT +{ + return string_literal{'d', 'a'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::deci) NOEXCEPT +{ + return string_literal{'d'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::hecto) NOEXCEPT +{ + return string_literal{'h'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::kilo) NOEXCEPT +{ + return string_literal{'k'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::mega) NOEXCEPT +{ + return string_literal{'M'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::giga) NOEXCEPT +{ + return string_literal{'G'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::tera) NOEXCEPT +{ + return string_literal{'T'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::peta) NOEXCEPT +{ + return string_literal{'P'}; +} + +template +CONSTCD11 +inline +string_literal +msl(std::exa) NOEXCEPT +{ + return string_literal{'E'}; +} + +template +CONSTCD11 +inline +auto +get_units(Period p) + -> decltype(msl(p) + string_literal{'s'}) +{ + return msl(p) + string_literal{'s'}; +} + +template +CONSTCD11 +inline +string_literal +get_units(std::ratio<1>) +{ + return string_literal{'s'}; +} + +template +CONSTCD11 +inline +string_literal +get_units(std::ratio<3600>) +{ + return string_literal{'h'}; +} + +template +CONSTCD11 +inline +string_literal +get_units(std::ratio<60>) +{ + return string_literal{'m', 'i', 'n'}; +} + +template +CONSTCD11 +inline +string_literal +get_units(std::ratio<86400>) +{ + return string_literal{'d'}; +} + +template > +struct make_string; + +template <> +struct make_string +{ + template + static + std::string + from(Rep n) + { + return std::to_string(n); + } +}; + +template +struct make_string +{ + template + static + std::basic_string + from(Rep n) + { + auto s = std::to_string(n); + return std::basic_string(s.begin(), s.end()); + } +}; + +template <> +struct make_string +{ + template + static + std::wstring + from(Rep n) + { + return std::to_wstring(n); + } +}; + +template +struct make_string +{ + template + static + std::basic_string + from(Rep n) + { + auto s = std::to_wstring(n); + return std::basic_string(s.begin(), s.end()); + } +}; + +} // namespace detail + +template +inline +std::basic_ostream& +operator<<(std::basic_ostream& os, + const std::chrono::duration& d) +{ + using namespace detail; + return os << make_string::from(d.count()) + + get_units(typename Period::type{}); +} + +} // namespace date + +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + +#endif // DATE_H diff --git a/clib/json11.cpp b/clib/json11.cpp index 534b5c7..0ed523e 100644 --- a/clib/json11.cpp +++ b/clib/json11.cpp @@ -22,8 +22,9 @@ namespace json11 { * it may not be orderable. */ struct NullStruct { - bool operator==(NullStruct) const { return true; } - bool operator<(NullStruct) const { return false; } + bool operator==(NullStruct) const { return true; } + + bool operator<(NullStruct) const { return false; } }; /* * * * * * * * * * * * * * * * * * * * @@ -31,356 +32,376 @@ namespace json11 { */ static void dump(NullStruct, string &out) { - out += "null"; + out += "null"; } static void dump(double value, string &out) { - if (std::isfinite(value)) { - char buf[32]; - snprintf(buf, sizeof buf, "%.17g", value); - out += buf; - } else { - out += "null"; - } + if (std::isfinite(value)) { + char buf[32]; + snprintf(buf, sizeof buf, "%.17g", value); + out += buf; + } else { + out += "null"; + } } static void dump(int value, string &out) { - char buf[32]; - snprintf(buf, sizeof buf, "%d", value); - out += buf; + char buf[32]; + snprintf(buf, sizeof buf, "%d", value); + out += buf; } static void dump(bool value, string &out) { - out += value ? "true" : "false"; + out += value ? "true" : "false"; } static void dump(const string &value, string &out) { - out += '"'; - for (size_t i = 0; i < value.length(); i++) { - const char ch = value[i]; - if (ch == '\\') { - out += "\\\\"; - } else if (ch == '"') { - out += "\\\""; - } else if (ch == '\b') { - out += "\\b"; - } else if (ch == '\f') { - out += "\\f"; - } else if (ch == '\n') { - out += "\\n"; - } else if (ch == '\r') { - out += "\\r"; - } else if (ch == '\t') { - out += "\\t"; - } else if (static_cast(ch) <= 0x1f) { - char buf[8]; - snprintf(buf, sizeof buf, "\\u%04x", ch); - out += buf; - } else if (static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 - && static_cast(value[i+2]) == 0xa8) { - out += "\\u2028"; - i += 2; - } else if (static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 - && static_cast(value[i+2]) == 0xa9) { - out += "\\u2029"; - i += 2; - } else { - out += ch; - } - } - out += '"'; + out += '"'; + for (size_t i = 0; i < value.length(); i++) { + const char ch = value[i]; + if (ch == '\\') { + out += "\\\\"; + } else if (ch == '"') { + out += "\\\""; + } else if (ch == '\b') { + out += "\\b"; + } else if (ch == '\f') { + out += "\\f"; + } else if (ch == '\n') { + out += "\\n"; + } else if (ch == '\r') { + out += "\\r"; + } else if (ch == '\t') { + out += "\\t"; + } else if (static_cast(ch) <= 0x1f) { + char buf[8]; + snprintf(buf, sizeof buf, "\\u%04x", ch); + out += buf; + } else if (static_cast(ch) == 0xe2 && static_cast(value[i + 1]) == 0x80 + && static_cast(value[i + 2]) == 0xa8) { + out += "\\u2028"; + i += 2; + } else if (static_cast(ch) == 0xe2 && static_cast(value[i + 1]) == 0x80 + && static_cast(value[i + 2]) == 0xa9) { + out += "\\u2029"; + i += 2; + } else { + out += ch; + } + } + out += '"'; } static void dump(const Json::array &values, string &out) { - bool first = true; - out += "["; - for (const auto &value : values) { - if (!first) - out += ", "; - value.dump(out); - first = false; - } - out += "]"; + bool first = true; + out += "["; + for (const auto &value : values) { + if (!first) + out += ", "; + value.dump(out); + first = false; + } + out += "]"; } static void dump(const Json::object &values, string &out) { - bool first = true; - out += "{"; - for (const auto &kv : values) { - if (!first) - out += ", "; - dump(kv.first, out); - out += ": "; - kv.second.dump(out); - first = false; - } - out += "}"; + bool first = true; + out += "{"; + for (const auto &kv : values) { + if (!first) + out += ", "; + dump(kv.first, out); + out += ": "; + kv.second.dump(out); + first = false; + } + out += "}"; } void Json::dump(string &out) const { - m_ptr->dump(out); + m_ptr->dump(out); } //////////////////////// static MlValue ivalualize(NullStruct) { - MlValue null; - return null; + MlValue null; + return null; } static MlValue ivalualize(double value) { - MlValue d; - if (std::isfinite(value)) { - d = value; - } - return d; + MlValue d; + if (std::isfinite(value)) { + d = value; + } + return d; } static MlValue ivalualize(int value) { - return MlValue(value); + return MlValue(value); } static MlValue ivalualize(bool value) { - return MlValue(value); + return MlValue(value); } - static MlValue ivalualize(const string& value) { - return MlValue::string(value); + static MlValue ivalualize(const string &value) { + return MlValue::string(value); } static MlValue ivalualize(const Json::array &values) { - vector arry; - for (const auto &value : values) { - arry.push_back(value.ivalualize()); - } - return arry; + vector arry; + for (const auto &value : values) { + arry.push_back(value.ivalualize()); + } + return arry; } static MlValue ivalualize(const Json::object &values) { - vector map; + vector map; - for (const auto &kv : values) { - std::string first = kv.first; - Json second = kv.second; + for (const auto &kv : values) { + std::string first = kv.first; + Json second = kv.second; - vector pair; - pair.push_back(MlValue::string(first)); - pair.push_back(second.ivalualize()); + vector pair; + pair.push_back(MlValue::string(first)); + pair.push_back(second.ivalualize()); - map.push_back(pair); - } - return map; + map.push_back(pair); + } + return map; } MlValue Json::ivalualize() const { - return m_ptr->ivalualize(); + return m_ptr->ivalualize(); } - - - - - - - - - - - - - - - - - - - - - - - - - - - - /* * * * * * * * * * * * * * * * * * * * * Value wrappers */ - template + template class Value : public JsonValue { protected: - // Constructors - explicit Value(const T &value) : m_value(value) {} - explicit Value(T &&value) : m_value(move(value)) {} + // Constructors + explicit Value(const T &value) : m_value(value) {} - // Get type tag - Json::Type type() const override { - return tag; - } + explicit Value(T &&value) : m_value(move(value)) {} - // Comparisons - bool equals(const JsonValue * other) const override { - return m_value == static_cast *>(other)->m_value; - } - bool less(const JsonValue * other) const override { - return m_value < static_cast *>(other)->m_value; - } + // Get type tag + Json::Type type() const override { + return tag; + } - const T m_value; - void dump(string &out) const override { json11::dump(m_value, out); } + // Comparisons + bool equals(const JsonValue *other) const override { + return m_value == static_cast *>(other)->m_value; + } - MlValue ivalualize() const override { return json11::ivalualize(m_value); } + bool less(const JsonValue *other) const override { + return m_value < static_cast *>(other)->m_value; + } + + const T m_value; + + void dump(string &out) const override { json11::dump(m_value, out); } + + MlValue ivalualize() const override { return json11::ivalualize(m_value); } }; class JsonDouble final : public Value { - double number_value() const override { return m_value; } - int int_value() const override { return static_cast(m_value); } - bool equals(const JsonValue * other) const override { return m_value == other->number_value(); } - bool less(const JsonValue * other) const override { return m_value < other->number_value(); } + double number_value() const override { return m_value; } + + int int_value() const override { return static_cast(m_value); } + + bool equals(const JsonValue *other) const override { return m_value == other->number_value(); } + + bool less(const JsonValue *other) const override { return m_value < other->number_value(); } + public: - explicit JsonDouble(double value) : Value(value) {} + explicit JsonDouble(double value) : Value(value) {} }; class JsonInt final : public Value { - double number_value() const override { return m_value; } - int int_value() const override { return m_value; } - bool equals(const JsonValue * other) const override { return m_value == other->number_value(); } - bool less(const JsonValue * other) const override { return m_value < other->number_value(); } + double number_value() const override { return m_value; } + + int int_value() const override { return m_value; } + + bool equals(const JsonValue *other) const override { return m_value == other->number_value(); } + + bool less(const JsonValue *other) const override { return m_value < other->number_value(); } + public: - explicit JsonInt(int value) : Value(value) {} + explicit JsonInt(int value) : Value(value) {} }; class JsonBoolean final : public Value { - bool bool_value() const override { return m_value; } + bool bool_value() const override { return m_value; } + public: - explicit JsonBoolean(bool value) : Value(value) {} + explicit JsonBoolean(bool value) : Value(value) {} }; class JsonString final : public Value { - const string &string_value() const override { return m_value; } + const string &string_value() const override { return m_value; } + public: - explicit JsonString(const string &value) : Value(value) {} - explicit JsonString(string &&value) : Value(move(value)) {} + explicit JsonString(const string &value) : Value(value) {} + + explicit JsonString(string &&value) : Value(move(value)) {} }; class JsonArray final : public Value { - const Json::array &array_items() const override { return m_value; } - const Json & operator[](size_t i) const override; + const Json::array &array_items() const override { return m_value; } + + const Json &operator[](size_t i) const override; + public: - explicit JsonArray(const Json::array &value) : Value(value) {} - explicit JsonArray(Json::array &&value) : Value(move(value)) {} + explicit JsonArray(const Json::array &value) : Value(value) {} + + explicit JsonArray(Json::array &&value) : Value(move(value)) {} }; class JsonObject final : public Value { - const Json::object &object_items() const override { return m_value; } - const Json & operator[](const string &key) const override; + const Json::object &object_items() const override { return m_value; } + + const Json &operator[](const string &key) const override; + public: - explicit JsonObject(const Json::object &value) : Value(value) {} - explicit JsonObject(Json::object &&value) : Value(move(value)) {} + explicit JsonObject(const Json::object &value) : Value(value) {} + + explicit JsonObject(Json::object &&value) : Value(move(value)) {} }; class JsonNull final : public Value { public: - JsonNull() : Value({}) {} + JsonNull() : Value({}) {} }; /* * * * * * * * * * * * * * * * * * * * * Static globals - static-init-safe */ struct Statics { - const std::shared_ptr null = make_shared(); - const std::shared_ptr t = make_shared(true); - const std::shared_ptr f = make_shared(false); - const string empty_string; - const vector empty_vector; - const map empty_map; - Statics() {} + const std::shared_ptr null = make_shared(); + const std::shared_ptr t = make_shared(true); + const std::shared_ptr f = make_shared(false); + const string empty_string; + const vector empty_vector; + const map empty_map; + + Statics() {} }; - static const Statics & statics() { - static const Statics s {}; - return s; + static const Statics &statics() { + static const Statics s{}; + return s; } - static const Json & static_null() { - // This has to be separate, not in Statics, because Json() accesses statics().null. - static const Json json_null; - return json_null; + static const Json &static_null() { + // This has to be separate, not in Statics, because Json() accesses statics().null. + static const Json json_null; + return json_null; } /* * * * * * * * * * * * * * * * * * * * * Constructors */ - Json::Json() noexcept : m_ptr(statics().null) {} - Json::Json(std::nullptr_t) noexcept : m_ptr(statics().null) {} - Json::Json(double value) : m_ptr(make_shared(value)) {} - Json::Json(int value) : m_ptr(make_shared(value)) {} - Json::Json(bool value) : m_ptr(value ? statics().t : statics().f) {} - Json::Json(const string &value) : m_ptr(make_shared(value)) {} - Json::Json(string &&value) : m_ptr(make_shared(move(value))) {} - Json::Json(const char * value) : m_ptr(make_shared(value)) {} - Json::Json(const Json::array &values) : m_ptr(make_shared(values)) {} - Json::Json(Json::array &&values) : m_ptr(make_shared(move(values))) {} + Json::Json() noexcept: m_ptr(statics().null) {} + + Json::Json(std::nullptr_t) noexcept: m_ptr(statics().null) {} + + Json::Json(double value) : m_ptr(make_shared(value)) {} + + Json::Json(int value) : m_ptr(make_shared(value)) {} + + Json::Json(bool value) : m_ptr(value ? statics().t : statics().f) {} + + Json::Json(const string &value) : m_ptr(make_shared(value)) {} + + Json::Json(string &&value) : m_ptr(make_shared(move(value))) {} + + Json::Json(const char *value) : m_ptr(make_shared(value)) {} + + Json::Json(const Json::array &values) : m_ptr(make_shared(values)) {} + + Json::Json(Json::array &&values) : m_ptr(make_shared(move(values))) {} + Json::Json(const Json::object &values) : m_ptr(make_shared(values)) {} - Json::Json(Json::object &&values) : m_ptr(make_shared(move(values))) {} + + Json::Json(Json::object &&values) : m_ptr(make_shared(move(values))) {} /* * * * * * * * * * * * * * * * * * * * * Accessors */ - Json::Type Json::type() const { return m_ptr->type(); } - double Json::number_value() const { return m_ptr->number_value(); } - int Json::int_value() const { return m_ptr->int_value(); } - bool Json::bool_value() const { return m_ptr->bool_value(); } - const string & Json::string_value() const { return m_ptr->string_value(); } - const vector & Json::array_items() const { return m_ptr->array_items(); } - const map & Json::object_items() const { return m_ptr->object_items(); } - const Json & Json::operator[] (size_t i) const { return (*m_ptr)[i]; } - const Json & Json::operator[] (const string &key) const { return (*m_ptr)[key]; } + Json::Type Json::type() const { return m_ptr->type(); } - double JsonValue::number_value() const { return 0; } - int JsonValue::int_value() const { return 0; } - bool JsonValue::bool_value() const { return false; } - const string & JsonValue::string_value() const { return statics().empty_string; } - const vector & JsonValue::array_items() const { return statics().empty_vector; } - const map & JsonValue::object_items() const { return statics().empty_map; } - const Json & JsonValue::operator[] (size_t) const { return static_null(); } - const Json & JsonValue::operator[] (const string &) const { return static_null(); } + double Json::number_value() const { return m_ptr->number_value(); } - const Json & JsonObject::operator[] (const string &key) const { - auto iter = m_value.find(key); - return (iter == m_value.end()) ? static_null() : iter->second; + int Json::int_value() const { return m_ptr->int_value(); } + + bool Json::bool_value() const { return m_ptr->bool_value(); } + + const string &Json::string_value() const { return m_ptr->string_value(); } + + const vector &Json::array_items() const { return m_ptr->array_items(); } + + const map &Json::object_items() const { return m_ptr->object_items(); } + + const Json &Json::operator[](size_t i) const { return (*m_ptr)[i]; } + + const Json &Json::operator[](const string &key) const { return (*m_ptr)[key]; } + + double JsonValue::number_value() const { return 0; } + + int JsonValue::int_value() const { return 0; } + + bool JsonValue::bool_value() const { return false; } + + const string &JsonValue::string_value() const { return statics().empty_string; } + + const vector &JsonValue::array_items() const { return statics().empty_vector; } + + const map &JsonValue::object_items() const { return statics().empty_map; } + + const Json &JsonValue::operator[](size_t) const { return static_null(); } + + const Json &JsonValue::operator[](const string &) const { return static_null(); } + + const Json &JsonObject::operator[](const string &key) const { + auto iter = m_value.find(key); + return (iter == m_value.end()) ? static_null() : iter->second; } - const Json & JsonArray::operator[] (size_t i) const { - if (i >= m_value.size()) return static_null(); - else return m_value[i]; + + const Json &JsonArray::operator[](size_t i) const { + if (i >= m_value.size()) return static_null(); + else return m_value[i]; } /* * * * * * * * * * * * * * * * * * * * * Comparison */ - bool Json::operator== (const Json &other) const { - if (m_ptr == other.m_ptr) - return true; - if (m_ptr->type() != other.m_ptr->type()) - return false; + bool Json::operator==(const Json &other) const { + if (m_ptr == other.m_ptr) + return true; + if (m_ptr->type() != other.m_ptr->type()) + return false; - return m_ptr->equals(other.m_ptr.get()); + return m_ptr->equals(other.m_ptr.get()); } - bool Json::operator< (const Json &other) const { - if (m_ptr == other.m_ptr) - return false; - if (m_ptr->type() != other.m_ptr->type()) - return m_ptr->type() < other.m_ptr->type(); + bool Json::operator<(const Json &other) const { + if (m_ptr == other.m_ptr) + return false; + if (m_ptr->type() != other.m_ptr->type()) + return m_ptr->type() < other.m_ptr->type(); - return m_ptr->less(other.m_ptr.get()); + return m_ptr->less(other.m_ptr.get()); } /* * * * * * * * * * * * * * * * * * * * @@ -392,17 +413,17 @@ namespace json11 { * Format char c suitable for printing in an error message. */ static inline string esc(char c) { - char buf[12]; - if (static_cast(c) >= 0x20 && static_cast(c) <= 0x7f) { - snprintf(buf, sizeof buf, "'%c' (%d)", c, c); - } else { - snprintf(buf, sizeof buf, "(%d)", c); - } - return string(buf); + char buf[12]; + if (static_cast(c) >= 0x20 && static_cast(c) <= 0x7f) { + snprintf(buf, sizeof buf, "'%c' (%d)", c, c); + } else { + snprintf(buf, sizeof buf, "(%d)", c); + } + return string(buf); } static inline bool in_range(long x, long lower, long upper) { - return (x >= lower && x <= upper); + return (x >= lower && x <= upper); } namespace { @@ -410,445 +431,443 @@ namespace json11 { * * Object that tracks all state of an in-progress parse. */ - struct JsonParser final { + struct JsonParser final { - /* State - */ - const string &str; - size_t i; - string &err; - bool failed; - const JsonParse strategy; + /* State + */ + const string &str; + size_t i; + string &err; + bool failed; + const JsonParse strategy; - /* fail(msg, err_ret = Json()) - * - * Mark this parse as failed. - */ - Json fail(string &&msg) { - return fail(move(msg), Json()); - } + /* fail(msg, err_ret = Json()) + * + * Mark this parse as failed. + */ + Json fail(string &&msg) { + return fail(move(msg), Json()); + } - template - T fail(string &&msg, const T err_ret) { - if (!failed) - err = std::move(msg); - failed = true; - return err_ret; - } + template + T fail(string &&msg, const T err_ret) { + if (!failed) + err = std::move(msg); + failed = true; + return err_ret; + } - /* consume_whitespace() - * - * Advance until the current character is non-whitespace. - */ - void consume_whitespace() { - while (str[i] == ' ' || str[i] == '\r' || str[i] == '\n' || str[i] == '\t') - i++; - } + /* consume_whitespace() + * + * Advance until the current character is non-whitespace. + */ + void consume_whitespace() { + while (str[i] == ' ' || str[i] == '\r' || str[i] == '\n' || str[i] == '\t') + i++; + } - /* consume_comment() - * - * Advance comments (c-style inline and multiline). - */ - bool consume_comment() { - bool comment_found = false; - if (str[i] == '/') { - i++; - if (i == str.size()) - return fail("unexpected end of input after start of comment", false); - if (str[i] == '/') { // inline comment - i++; - // advance until next line, or end of input - while (i < str.size() && str[i] != '\n') { - i++; - } - comment_found = true; - } - else if (str[i] == '*') { // multiline comment - i++; - if (i > str.size()-2) - return fail("unexpected end of input inside multi-line comment", false); - // advance until closing tokens - while (!(str[i] == '*' && str[i+1] == '/')) { - i++; - if (i > str.size()-2) - return fail( - "unexpected end of input inside multi-line comment", false); - } - i += 2; - comment_found = true; - } - else - return fail("malformed comment", false); - } - return comment_found; - } + /* consume_comment() + * + * Advance comments (c-style inline and multiline). + */ + bool consume_comment() { + bool comment_found = false; + if (str[i] == '/') { + i++; + if (i == str.size()) + return fail("unexpected end of input after start of comment", false); + if (str[i] == '/') { // inline comment + i++; + // advance until next line, or end of input + while (i < str.size() && str[i] != '\n') { + i++; + } + comment_found = true; + } else if (str[i] == '*') { // multiline comment + i++; + if (i > str.size() - 2) + return fail("unexpected end of input inside multi-line comment", false); + // advance until closing tokens + while (!(str[i] == '*' && str[i + 1] == '/')) { + i++; + if (i > str.size() - 2) + return fail( + "unexpected end of input inside multi-line comment", false); + } + i += 2; + comment_found = true; + } else + return fail("malformed comment", false); + } + return comment_found; + } - /* consume_garbage() - * - * Advance until the current character is non-whitespace and non-comment. - */ - void consume_garbage() { - consume_whitespace(); - if(strategy == JsonParse::COMMENTS) { - bool comment_found = false; - do { - comment_found = consume_comment(); - if (failed) return; - consume_whitespace(); - } - while(comment_found); - } - } + /* consume_garbage() + * + * Advance until the current character is non-whitespace and non-comment. + */ + void consume_garbage() { + consume_whitespace(); + if (strategy == JsonParse::COMMENTS) { + bool comment_found = false; + do { + comment_found = consume_comment(); + if (failed) return; + consume_whitespace(); + } while (comment_found); + } + } - /* get_next_token() - * - * Return the next non-whitespace character. If the end of the input is reached, - * flag an error and return 0. - */ - char get_next_token() { - consume_garbage(); - if (failed) return (char)0; - if (i == str.size()) - return fail("unexpected end of input", (char)0); + /* get_next_token() + * + * Return the next non-whitespace character. If the end of the input is reached, + * flag an error and return 0. + */ + char get_next_token() { + consume_garbage(); + if (failed) return (char) 0; + if (i == str.size()) + return fail("unexpected end of input", (char) 0); - return str[i++]; - } + return str[i++]; + } - /* encode_utf8(pt, out) - * - * Encode pt as UTF-8 and add it to out. - */ - void encode_utf8(long pt, string & out) { - if (pt < 0) - return; + /* encode_utf8(pt, out) + * + * Encode pt as UTF-8 and add it to out. + */ + void encode_utf8(long pt, string &out) { + if (pt < 0) + return; - if (pt < 0x80) { - out += static_cast(pt); - } else if (pt < 0x800) { - out += static_cast((pt >> 6) | 0xC0); - out += static_cast((pt & 0x3F) | 0x80); - } else if (pt < 0x10000) { - out += static_cast((pt >> 12) | 0xE0); - out += static_cast(((pt >> 6) & 0x3F) | 0x80); - out += static_cast((pt & 0x3F) | 0x80); - } else { - out += static_cast((pt >> 18) | 0xF0); - out += static_cast(((pt >> 12) & 0x3F) | 0x80); - out += static_cast(((pt >> 6) & 0x3F) | 0x80); - out += static_cast((pt & 0x3F) | 0x80); - } - } + if (pt < 0x80) { + out += static_cast(pt); + } else if (pt < 0x800) { + out += static_cast((pt >> 6) | 0xC0); + out += static_cast((pt & 0x3F) | 0x80); + } else if (pt < 0x10000) { + out += static_cast((pt >> 12) | 0xE0); + out += static_cast(((pt >> 6) & 0x3F) | 0x80); + out += static_cast((pt & 0x3F) | 0x80); + } else { + out += static_cast((pt >> 18) | 0xF0); + out += static_cast(((pt >> 12) & 0x3F) | 0x80); + out += static_cast(((pt >> 6) & 0x3F) | 0x80); + out += static_cast((pt & 0x3F) | 0x80); + } + } - /* parse_string() - * - * Parse a string, starting at the current position. - */ - string parse_string() { - string out; - long last_escaped_codepoint = -1; - while (true) { - if (i == str.size()) - return fail("unexpected end of input in string", ""); + /* parse_string() + * + * Parse a string, starting at the current position. + */ + string parse_string() { + string out; + long last_escaped_codepoint = -1; + while (true) { + if (i == str.size()) + return fail("unexpected end of input in string", ""); - char ch = str[i++]; + char ch = str[i++]; - if (ch == '"') { - encode_utf8(last_escaped_codepoint, out); - return out; - } + if (ch == '"') { + encode_utf8(last_escaped_codepoint, out); + return out; + } - if (in_range(ch, 0, 0x1f)) - return fail("unescaped " + esc(ch) + " in string", ""); + if (in_range(ch, 0, 0x1f)) + return fail("unescaped " + esc(ch) + " in string", ""); - // The usual case: non-escaped characters - if (ch != '\\') { - encode_utf8(last_escaped_codepoint, out); - last_escaped_codepoint = -1; - out += ch; - continue; - } + // The usual case: non-escaped characters + if (ch != '\\') { + encode_utf8(last_escaped_codepoint, out); + last_escaped_codepoint = -1; + out += ch; + continue; + } - // Handle escapes - if (i == str.size()) - return fail("unexpected end of input in string", ""); + // Handle escapes + if (i == str.size()) + return fail("unexpected end of input in string", ""); - ch = str[i++]; + ch = str[i++]; - if (ch == 'u') { - // Extract 4-byte escape sequence - string esc = str.substr(i, 4); - // Explicitly check length of the substring. The following loop - // relies on std::string returning the terminating NUL when - // accessing str[length]. Checking here reduces brittleness. - if (esc.length() < 4) { - return fail("bad \\u escape: " + esc, ""); - } - for (size_t j = 0; j < 4; j++) { - if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F') - && !in_range(esc[j], '0', '9')) - return fail("bad \\u escape: " + esc, ""); - } + if (ch == 'u') { + // Extract 4-byte escape sequence + string esc = str.substr(i, 4); + // Explicitly check length of the substring. The following loop + // relies on std::string returning the terminating NUL when + // accessing str[length]. Checking here reduces brittleness. + if (esc.length() < 4) { + return fail("bad \\u escape: " + esc, ""); + } + for (size_t j = 0; j < 4; j++) { + if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F') + && !in_range(esc[j], '0', '9')) + return fail("bad \\u escape: " + esc, ""); + } - long codepoint = strtol(esc.data(), nullptr, 16); + long codepoint = strtol(esc.data(), nullptr, 16); - // JSON specifies that characters outside the BMP shall be encoded as a pair - // of 4-hex-digit \u escapes encoding their surrogate pair components. Check - // whether we're in the middle of such a beast: the previous codepoint was an - // escaped lead (high) surrogate, and this is a trail (low) surrogate. - if (in_range(last_escaped_codepoint, 0xD800, 0xDBFF) - && in_range(codepoint, 0xDC00, 0xDFFF)) { - // Reassemble the two surrogate pairs into one astral-plane character, per - // the UTF-16 algorithm. - encode_utf8((((last_escaped_codepoint - 0xD800) << 10) - | (codepoint - 0xDC00)) + 0x10000, out); - last_escaped_codepoint = -1; - } else { - encode_utf8(last_escaped_codepoint, out); - last_escaped_codepoint = codepoint; - } + // JSON specifies that characters outside the BMP shall be encoded as a pair + // of 4-hex-digit \u escapes encoding their surrogate pair components. Check + // whether we're in the middle of such a beast: the previous codepoint was an + // escaped lead (high) surrogate, and this is a trail (low) surrogate. + if (in_range(last_escaped_codepoint, 0xD800, 0xDBFF) + && in_range(codepoint, 0xDC00, 0xDFFF)) { + // Reassemble the two surrogate pairs into one astral-plane character, per + // the UTF-16 algorithm. + encode_utf8((((last_escaped_codepoint - 0xD800) << 10) + | (codepoint - 0xDC00)) + 0x10000, out); + last_escaped_codepoint = -1; + } else { + encode_utf8(last_escaped_codepoint, out); + last_escaped_codepoint = codepoint; + } - i += 4; - continue; - } + i += 4; + continue; + } - encode_utf8(last_escaped_codepoint, out); - last_escaped_codepoint = -1; + encode_utf8(last_escaped_codepoint, out); + last_escaped_codepoint = -1; - if (ch == 'b') { - out += '\b'; - } else if (ch == 'f') { - out += '\f'; - } else if (ch == 'n') { - out += '\n'; - } else if (ch == 'r') { - out += '\r'; - } else if (ch == 't') { - out += '\t'; - } else if (ch == '"' || ch == '\\' || ch == '/') { - out += ch; - } else { - return fail("invalid escape character " + esc(ch), ""); - } - } - } + if (ch == 'b') { + out += '\b'; + } else if (ch == 'f') { + out += '\f'; + } else if (ch == 'n') { + out += '\n'; + } else if (ch == 'r') { + out += '\r'; + } else if (ch == 't') { + out += '\t'; + } else if (ch == '"' || ch == '\\' || ch == '/') { + out += ch; + } else { + return fail("invalid escape character " + esc(ch), ""); + } + } + } - /* parse_number() - * - * Parse a double. - */ - Json parse_number() { - size_t start_pos = i; + /* parse_number() + * + * Parse a double. + */ + Json parse_number() { + size_t start_pos = i; - if (str[i] == '-') - i++; + if (str[i] == '-') + i++; - // Integer part - if (str[i] == '0') { - i++; - if (in_range(str[i], '0', '9')) - return fail("leading 0s not permitted in numbers"); - } else if (in_range(str[i], '1', '9')) { - i++; - while (in_range(str[i], '0', '9')) - i++; - } else { - return fail("invalid " + esc(str[i]) + " in number"); - } + // Integer part + if (str[i] == '0') { + i++; + if (in_range(str[i], '0', '9')) + return fail("leading 0s not permitted in numbers"); + } else if (in_range(str[i], '1', '9')) { + i++; + while (in_range(str[i], '0', '9')) + i++; + } else { + return fail("invalid " + esc(str[i]) + " in number"); + } - if (str[i] != '.' && str[i] != 'e' && str[i] != 'E' - && (i - start_pos) <= static_cast(std::numeric_limits::digits10)) { - return std::atoi(str.c_str() + start_pos); - } + if (str[i] != '.' && str[i] != 'e' && str[i] != 'E' + && (i - start_pos) <= static_cast(std::numeric_limits::digits10)) { + return std::atoi(str.c_str() + start_pos); + } - // Decimal part - if (str[i] == '.') { - i++; - if (!in_range(str[i], '0', '9')) - return fail("at least one digit required in fractional part"); + // Decimal part + if (str[i] == '.') { + i++; + if (!in_range(str[i], '0', '9')) + return fail("at least one digit required in fractional part"); - while (in_range(str[i], '0', '9')) - i++; - } + while (in_range(str[i], '0', '9')) + i++; + } - // Exponent part - if (str[i] == 'e' || str[i] == 'E') { - i++; + // Exponent part + if (str[i] == 'e' || str[i] == 'E') { + i++; - if (str[i] == '+' || str[i] == '-') - i++; + if (str[i] == '+' || str[i] == '-') + i++; - if (!in_range(str[i], '0', '9')) - return fail("at least one digit required in exponent"); + if (!in_range(str[i], '0', '9')) + return fail("at least one digit required in exponent"); - while (in_range(str[i], '0', '9')) - i++; - } + while (in_range(str[i], '0', '9')) + i++; + } - return std::strtod(str.c_str() + start_pos, nullptr); - } + return std::strtod(str.c_str() + start_pos, nullptr); + } - /* expect(str, res) - * - * Expect that 'str' starts at the character that was just read. If it does, advance - * the input and return res. If not, flag an error. - */ - Json expect(const string &expected, Json res) { - assert(i != 0); - i--; - if (str.compare(i, expected.length(), expected) == 0) { - i += expected.length(); - return res; - } else { - return fail("parse error: expected " + expected + ", got " + str.substr(i, expected.length())); - } - } + /* expect(str, res) + * + * Expect that 'str' starts at the character that was just read. If it does, advance + * the input and return res. If not, flag an error. + */ + Json expect(const string &expected, Json res) { + assert(i != 0); + i--; + if (str.compare(i, expected.length(), expected) == 0) { + i += expected.length(); + return res; + } else { + return fail( + "parse error: expected " + expected + ", got " + str.substr(i, expected.length())); + } + } - /* parse_json() - * - * Parse a JSON object. - */ - Json parse_json(int depth) { - if (depth > max_depth) { - return fail("exceeded maximum nesting depth"); - } + /* parse_json() + * + * Parse a JSON object. + */ + Json parse_json(int depth) { + if (depth > max_depth) { + return fail("exceeded maximum nesting depth"); + } - char ch = get_next_token(); - if (failed) - return Json(); + char ch = get_next_token(); + if (failed) + return Json(); - if (ch == '-' || (ch >= '0' && ch <= '9')) { - i--; - return parse_number(); - } + if (ch == '-' || (ch >= '0' && ch <= '9')) { + i--; + return parse_number(); + } - if (ch == 't') - return expect("true", true); + if (ch == 't') + return expect("true", true); - if (ch == 'f') - return expect("false", false); + if (ch == 'f') + return expect("false", false); - if (ch == 'n') - return expect("null", Json()); + if (ch == 'n') + return expect("null", Json()); - if (ch == '"') - return parse_string(); + if (ch == '"') + return parse_string(); - if (ch == '{') { - map data; - ch = get_next_token(); - if (ch == '}') - return data; + if (ch == '{') { + map data; + ch = get_next_token(); + if (ch == '}') + return data; - while (1) { - if (ch != '"') - return fail("expected '\"' in object, got " + esc(ch)); + while (1) { + if (ch != '"') + return fail("expected '\"' in object, got " + esc(ch)); - string key = parse_string(); - if (failed) - return Json(); + string key = parse_string(); + if (failed) + return Json(); - ch = get_next_token(); - if (ch != ':') - return fail("expected ':' in object, got " + esc(ch)); + ch = get_next_token(); + if (ch != ':') + return fail("expected ':' in object, got " + esc(ch)); - data[std::move(key)] = parse_json(depth + 1); - if (failed) - return Json(); + data[std::move(key)] = parse_json(depth + 1); + if (failed) + return Json(); - ch = get_next_token(); - if (ch == '}') - break; - if (ch != ',') - return fail("expected ',' in object, got " + esc(ch)); + ch = get_next_token(); + if (ch == '}') + break; + if (ch != ',') + return fail("expected ',' in object, got " + esc(ch)); - ch = get_next_token(); - } - return data; - } + ch = get_next_token(); + } + return data; + } - if (ch == '[') { - vector data; - ch = get_next_token(); - if (ch == ']') - return data; + if (ch == '[') { + vector data; + ch = get_next_token(); + if (ch == ']') + return data; - while (1) { - i--; - data.push_back(parse_json(depth + 1)); - if (failed) - return Json(); + while (1) { + i--; + data.push_back(parse_json(depth + 1)); + if (failed) + return Json(); - ch = get_next_token(); - if (ch == ']') - break; - if (ch != ',') - return fail("expected ',' in list, got " + esc(ch)); + ch = get_next_token(); + if (ch == ']') + break; + if (ch != ',') + return fail("expected ',' in list, got " + esc(ch)); - ch = get_next_token(); - (void)ch; - } - return data; - } + ch = get_next_token(); + (void) ch; + } + return data; + } - return fail("expected value, got " + esc(ch)); - } - }; + return fail("expected value, got " + esc(ch)); + } + }; }//namespace { Json Json::parse(const string &in, string &err, JsonParse strategy) { - JsonParser parser { in, 0, err, false, strategy }; - Json result = parser.parse_json(0); + JsonParser parser{in, 0, err, false, strategy}; + Json result = parser.parse_json(0); - // Check for any trailing garbage - parser.consume_garbage(); - if (parser.failed) - return Json(); - if (parser.i != in.size()) - return parser.fail("unexpected trailing " + esc(in[parser.i])); + // Check for any trailing garbage + parser.consume_garbage(); + if (parser.failed) + return Json(); + if (parser.i != in.size()) + return parser.fail("unexpected trailing " + esc(in[parser.i])); - return result; + return result; } // Documented in json11.hpp vector Json::parse_multi(const string &in, - std::string::size_type &parser_stop_pos, - string &err, - JsonParse strategy) { - JsonParser parser { in, 0, err, false, strategy }; - parser_stop_pos = 0; - vector json_vec; - while (parser.i != in.size() && !parser.failed) { - json_vec.push_back(parser.parse_json(0)); - if (parser.failed) - break; + std::string::size_type &parser_stop_pos, + string &err, + JsonParse strategy) { + JsonParser parser{in, 0, err, false, strategy}; + parser_stop_pos = 0; + vector json_vec; + while (parser.i != in.size() && !parser.failed) { + json_vec.push_back(parser.parse_json(0)); + if (parser.failed) + break; - // Check for another object - parser.consume_garbage(); - if (parser.failed) - break; - parser_stop_pos = parser.i; - } - return json_vec; + // Check for another object + parser.consume_garbage(); + if (parser.failed) + break; + parser_stop_pos = parser.i; + } + return json_vec; } /* * * * * * * * * * * * * * * * * * * * * Shape-checking */ - bool Json::has_shape(const shape & types, string & err) const { - if (!is_object()) { - err = "expected JSON object, got " + dump(); - return false; - } + bool Json::has_shape(const shape &types, string &err) const { + if (!is_object()) { + err = "expected JSON object, got " + dump(); + return false; + } - for (auto & item : types) { - if ((*this)[item.first].type() != item.second) { - err = "bad type for " + item.first + " in " + dump(); - return false; - } - } + for (auto &item : types) { + if ((*this)[item.first].type() != item.second) { + err = "bad type for " + item.first + " in " + dump(); + return false; + } + } - return true; + return true; } } // namespace json11 diff --git a/clib/json11.h b/clib/json11.h index ebe4a6b..8f4621e 100644 --- a/clib/json11.h +++ b/clib/json11.h @@ -11,165 +11,200 @@ namespace json11 { enum JsonParse { - STANDARD, COMMENTS + STANDARD, COMMENTS }; class JsonValue; class Json final { public: - // Types - enum Type { - NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT - }; + // Types + enum Type { + NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT + }; - // Array and object typedefs - typedef std::vector array; - typedef std::map object; + // Array and object typedefs + typedef std::vector array; + typedef std::map object; - // Constructors for the various types of JSON value. - Json() noexcept; // NUL - Json(std::nullptr_t) noexcept; // NUL - Json(double value); // NUMBER - Json(int value); // NUMBER - Json(bool value); // BOOL - Json(const std::string &value); // STRING - Json(std::string &&value); // STRING - Json(const char * value); // STRING - Json(const array &values); // ARRAY - Json(array &&values); // ARRAY - Json(const object &values); // OBJECT - Json(object &&values); // OBJECT + // Constructors for the various types of JSON value. + Json() noexcept; // NUL + Json(std::nullptr_t) noexcept; // NUL + Json(double value); // NUMBER + Json(int value); // NUMBER + Json(bool value); // BOOL + Json(const std::string &value); // STRING + Json(std::string &&value); // STRING + Json(const char *value); // STRING + Json(const array &values); // ARRAY + Json(array &&values); // ARRAY + Json(const object &values); // OBJECT + Json(object &&values); // OBJECT - // Implicit constructor: anything with a to_json() function. - template - Json(const T & t) : Json(t.to_json()) {} + // Implicit constructor: anything with a to_json() function. + template + Json(const T &t) : Json(t.to_json()) {} - // Implicit constructor: map-like objects (std::map, std::unordered_map, etc) - template ().begin()->first)>::value - && std::is_constructible().begin()->second)>::value, - int>::type = 0> - Json(const M & m) : Json(object(m.begin(), m.end())) {} + // Implicit constructor: map-like objects (std::map, std::unordered_map, etc) + template().begin()->first)>::value + && std::is_constructible().begin()->second)>::value, + int>::type = 0> + Json(const M &m) : Json(object(m.begin(), m.end())) {} - // Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc) - template ().begin())>::value, - int>::type = 0> - Json(const V & v) : Json(array(v.begin(), v.end())) {} + // Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc) + template().begin())>::value, + int>::type = 0> + Json(const V &v) : Json(array(v.begin(), v.end())) {} - // This prevents Json(some_pointer) from accidentally producing a bool. Use - // Json(bool(some_pointer)) if that behavior is desired. - Json(void *) = delete; + // This prevents Json(some_pointer) from accidentally producing a bool. Use + // Json(bool(some_pointer)) if that behavior is desired. + Json(void *) = delete; - // Accessors - Type type() const; + // Accessors + Type type() const; - bool is_null() const { return type() == NUL; } - bool is_number() const { return type() == NUMBER; } - bool is_bool() const { return type() == BOOL; } - bool is_string() const { return type() == STRING; } - bool is_array() const { return type() == ARRAY; } - bool is_object() const { return type() == OBJECT; } + bool is_null() const { return type() == NUL; } - // Return the enclosed value if this is a number, 0 otherwise. Note that json11 does not - // distinguish between integer and non-integer numbers - number_value() and int_value() - // can both be applied to a NUMBER-typed object. - double number_value() const; - int int_value() const; + bool is_number() const { return type() == NUMBER; } - // Return the enclosed value if this is a boolean, false otherwise. - bool bool_value() const; - // Return the enclosed string if this is a string, "" otherwise. - const std::string &string_value() const; - // Return the enclosed std::vector if this is an array, or an empty vector otherwise. - const array &array_items() const; - // Return the enclosed std::map if this is an object, or an empty map otherwise. - const object &object_items() const; + bool is_bool() const { return type() == BOOL; } - // Return a reference to arr[i] if this is an array, Json() otherwise. - const Json & operator[](size_t i) const; - // Return a reference to obj[key] if this is an object, Json() otherwise. - const Json & operator[](const std::string &key) const; + bool is_string() const { return type() == STRING; } - // Serialize. - void dump(std::string &out) const; - std::string dump() const { - std::string out; - dump(out); - return out; - } + bool is_array() const { return type() == ARRAY; } - MlValue ivalualize() const; + bool is_object() const { return type() == OBJECT; } - // Parse. If parse fails, return Json() and assign an error message to err. - static Json parse(const std::string & in, - std::string & err, - JsonParse strategy = JsonParse::STANDARD); - static Json parse(const char * in, - std::string & err, - JsonParse strategy = JsonParse::STANDARD) { - if (in) { - return parse(std::string(in), err, strategy); - } else { - err = "null input"; - return nullptr; - } - } - // Parse multiple objects, concatenated or separated by whitespace - static std::vector parse_multi( - const std::string & in, - std::string::size_type & parser_stop_pos, - std::string & err, - JsonParse strategy = JsonParse::STANDARD); + // Return the enclosed value if this is a number, 0 otherwise. Note that json11 does not + // distinguish between integer and non-integer numbers - number_value() and int_value() + // can both be applied to a NUMBER-typed object. + double number_value() const; - static inline std::vector parse_multi( - const std::string & in, - std::string & err, - JsonParse strategy = JsonParse::STANDARD) { - std::string::size_type parser_stop_pos; - return parse_multi(in, parser_stop_pos, err, strategy); - } + int int_value() const; - bool operator== (const Json &rhs) const; - bool operator< (const Json &rhs) const; - bool operator!= (const Json &rhs) const { return !(*this == rhs); } - bool operator<= (const Json &rhs) const { return !(rhs < *this); } - bool operator> (const Json &rhs) const { return (rhs < *this); } - bool operator>= (const Json &rhs) const { return !(*this < rhs); } + // Return the enclosed value if this is a boolean, false otherwise. + bool bool_value() const; - /* has_shape(types, err) - * - * Return true if this is a JSON object and, for each item in types, has a field of - * the given type. If not, return false and set err to a descriptive message. - */ - typedef std::initializer_list> shape; - bool has_shape(const shape & types, std::string & err) const; + // Return the enclosed string if this is a string, "" otherwise. + const std::string &string_value() const; + + // Return the enclosed std::vector if this is an array, or an empty vector otherwise. + const array &array_items() const; + + // Return the enclosed std::map if this is an object, or an empty map otherwise. + const object &object_items() const; + + // Return a reference to arr[i] if this is an array, Json() otherwise. + const Json &operator[](size_t i) const; + + // Return a reference to obj[key] if this is an object, Json() otherwise. + const Json &operator[](const std::string &key) const; + + // Serialize. + void dump(std::string &out) const; + + std::string dump() const { + std::string out; + dump(out); + return out; + } + + MlValue ivalualize() const; + + // Parse. If parse fails, return Json() and assign an error message to err. + static Json parse(const std::string &in, + std::string &err, + JsonParse strategy = JsonParse::STANDARD); + + static Json parse(const char *in, + std::string &err, + JsonParse strategy = JsonParse::STANDARD) { + if (in) { + return parse(std::string(in), err, strategy); + } else { + err = "null input"; + return nullptr; + } + } + + // Parse multiple objects, concatenated or separated by whitespace + static std::vector parse_multi( + const std::string &in, + std::string::size_type &parser_stop_pos, + std::string &err, + JsonParse strategy = JsonParse::STANDARD); + + static inline std::vector parse_multi( + const std::string &in, + std::string &err, + JsonParse strategy = JsonParse::STANDARD) { + std::string::size_type parser_stop_pos; + return parse_multi(in, parser_stop_pos, err, strategy); + } + + bool operator==(const Json &rhs) const; + + bool operator<(const Json &rhs) const; + + bool operator!=(const Json &rhs) const { return !(*this == rhs); } + + bool operator<=(const Json &rhs) const { return !(rhs < *this); } + + bool operator>(const Json &rhs) const { return (rhs < *this); } + + bool operator>=(const Json &rhs) const { return !(*this < rhs); } + + /* has_shape(types, err) + * + * Return true if this is a JSON object and, for each item in types, has a field of + * the given type. If not, return false and set err to a descriptive message. + */ + typedef std::initializer_list> shape; + + bool has_shape(const shape &types, std::string &err) const; private: - std::shared_ptr m_ptr; + std::shared_ptr m_ptr; }; // Internal class hierarchy - JsonValue objects are not exposed to users of this API. class JsonValue { protected: - friend class Json; - friend class JsonInt; - friend class JsonDouble; - virtual Json::Type type() const = 0; - virtual bool equals(const JsonValue * other) const = 0; - virtual bool less(const JsonValue * other) const = 0; - virtual void dump(std::string &out) const = 0; - virtual MlValue ivalualize() const = 0; - virtual double number_value() const; - virtual int int_value() const; - virtual bool bool_value() const; - virtual const std::string &string_value() const; - virtual const Json::array &array_items() const; - virtual const Json &operator[](size_t i) const; - virtual const Json::object &object_items() const; - virtual const Json &operator[](const std::string &key) const; - virtual ~JsonValue() {} + friend class Json; + + friend class JsonInt; + + friend class JsonDouble; + + virtual Json::Type type() const = 0; + + virtual bool equals(const JsonValue *other) const = 0; + + virtual bool less(const JsonValue *other) const = 0; + + virtual void dump(std::string &out) const = 0; + + virtual MlValue ivalualize() const = 0; + + virtual double number_value() const; + + virtual int int_value() const; + + virtual bool bool_value() const; + + virtual const std::string &string_value() const; + + virtual const Json::array &array_items() const; + + virtual const Json &operator[](size_t i) const; + + virtual const Json::object &object_items() const; + + virtual const Json &operator[](const std::string &key) const; + + virtual ~JsonValue() {} }; } // namespace json11 diff --git a/clib/sslclient.cpp b/clib/sslclient.cpp index 0d3c621..43bbd69 100644 --- a/clib/sslclient.cpp +++ b/clib/sslclient.cpp @@ -17,59 +17,60 @@ #include -HttpClient::HttpClient(){}; +HttpClient::HttpClient() {}; -std::pair HttpClient::doGetRequest(const std::string &url, const std::unordered_map &headers) { +std::pair +HttpClient::doGetRequest(const std::string &url, const std::unordered_map &headers) { // https://stackoverflow.com/questions/25896916/parse-http-headers-in-c - + std::regex rgx{R"(^(?:((?:https?|s?ftp):)//)([^:/\s]+)(?::(\d*))?(?:/([^\s?#]+)?([?][^?#]*)?(#.*)?)?)"}; std::smatch matches; if (std::regex_search(url, matches, rgx)) { for (size_t i = 0; i < matches.size(); ++i) { switch (i) { - case 0: - full_url = matches[i].str(); - break; - case 1: - proto = matches[i].str(); - break; - case 2: - server = matches[i].str(); - break; - case 3: - port = matches[i].str(); - break; - case 4: - uri = matches[i].str(); - break; - case 5: - params = matches[i].str(); - break; - case 6: - href = matches[i].str(); - break; + case 0: + full_url = matches[i].str(); + break; + case 1: + proto = matches[i].str(); + break; + case 2: + server = matches[i].str(); + break; + case 3: + port = matches[i].str(); + break; + case 4: + uri = matches[i].str(); + break; + case 5: + params = matches[i].str(); + break; + case 6: + href = matches[i].str(); + break; } // std::cout << i << ": '" << matches[i].str() << "'\n"; } } else { - std::cerr << "Match not found" << std::endl; // TODO better message + std::cerr << "Match not found" << std::endl; // TODO better message } std::string headers_string = ""; for (auto it = headers.begin(); it != headers.end(); ++it) { headers_string.append("\r\n" + it->first + ": " + it->second); // std::cerr << "KEY: `" << it->first << "`, VALUE: `" << it->second << '`' << std::endl; - } + } - std::string request = "GET " + full_url + " HTTP/1.0\r\nHost: " + server + headers_string + "\r\n\r\n"; + std::string request = "GET " + full_url + " HTTP/1.0\r\nHost: " + server + headers_string + "\r\n\r\n"; // TODO memory leaks ??? int bytes_read = sslRequest(server, request); if (bytes_read <= 0) { - std::cerr << "no data read" << std::endl; - return std::make_pair(403, ""); + std::cerr << "no data read" << std::endl; + return std::make_pair(403, ""); } std::string::size_type position = ssl_read_packet.find("\r\n\r\n"); @@ -88,8 +89,8 @@ std::pair HttpClient::doGetRequest(const std::string &url, con std::smatch status_matches; if (std::regex_search(status_str, status_matches, status_rgx)) { if (status_matches.size() > 1) { - auto sta = status_matches[1].str(); // string "200" - // std::cout << "status: " << sta << std::endl; + auto sta = status_matches[1].str(); // string "200" + // std::cout << "status: " << sta << std::endl; } } @@ -116,7 +117,7 @@ std::string HttpClient::inetAddress(std::string hostname) { std::cerr << hostname << " is unavailable" << std::endl; exit(1); } - in_addr *address = (in_addr *)record->h_addr; + in_addr *address = (in_addr *) record->h_addr; std::string ip_address = inet_ntoa(*address); return ip_address; @@ -131,8 +132,8 @@ int HttpClient::sslRecvPacket() { do { len = SSL_read(ssl, buf, len); if (len >= 0) { - buf[len] = 0; - ssl_read_packet.append((const char *)buf); + buf[len] = 0; + ssl_read_packet.append((const char *) buf); } } while (len > 0); @@ -154,15 +155,15 @@ int HttpClient::sslSendPacket(std::string buf) { if (len < 0) { int err = SSL_get_error(ssl, len); switch (err) { - case SSL_ERROR_WANT_WRITE: - return 0; - case SSL_ERROR_WANT_READ: - return 0; - case SSL_ERROR_ZERO_RETURN: - case SSL_ERROR_SYSCALL: - case SSL_ERROR_SSL: - default: - return -1; + case SSL_ERROR_WANT_WRITE: + return 0; + case SSL_ERROR_WANT_READ: + return 0; + case SSL_ERROR_ZERO_RETURN: + case SSL_ERROR_SYSCALL: + case SSL_ERROR_SSL: + default: + return -1; } } @@ -188,7 +189,7 @@ int HttpClient::sslRequest(const std::string &server_name, const std::string &re socklen_t socklen = sizeof(sa); // connect to server - if (connect(s, (struct sockaddr *)&sa, socklen)) { + if (connect(s, (struct sockaddr *) &sa, socklen)) { printf("MlError connecting to server.\n"); return -1; } @@ -206,8 +207,8 @@ int HttpClient::sslRequest(const std::string &server_name, const std::string &re } sock = SSL_get_fd(ssl); SSL_set_fd(ssl, s); - - SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, (void*)server.c_str()); + + SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, (void *) server.c_str()); int err = SSL_connect(ssl); if (err <= 0) { @@ -221,7 +222,7 @@ int HttpClient::sslRequest(const std::string &server_name, const std::string &re // printf ("SSL connection using %s\n", SSL_get_cipher (ssl)); // send request - //std::err << request << std::endl; + //std::err << request << std::endl; sslSendPacket(request); // read response and return its length diff --git a/clib/sslclient.h b/clib/sslclient.h index 8294284..36a7d48 100644 --- a/clib/sslclient.h +++ b/clib/sslclient.h @@ -7,26 +7,30 @@ class HttpClient { - // TODO at this moment only https is implemented + // TODO at this moment only https is implemented private: - SSL *ssl; - int sock; + SSL *ssl; + int sock; - std::string full_url, proto, server, port, uri, params, href; - std::basic_string ssl_read_packet; - std::unordered_map headers_map; + std::string full_url, proto, server, port, uri, params, href; + std::basic_string ssl_read_packet; + std::unordered_map headers_map; public: - HttpClient(); + HttpClient(); - std::pair doGetRequest(const std::string &url, const std::unordered_map &headers); + std::pair + doGetRequest(const std::string &url, const std::unordered_map &headers); private: - std::string inetAddress(std::string hostname); + std::string inetAddress(std::string hostname); - int sslRecvPacket(); - int sslSendPacket(std::string buf); - int sslRequest(const std::string &server_name, const std::string &request); - void log_ssl(); + int sslRecvPacket(); + + int sslSendPacket(std::string buf); + + int sslRequest(const std::string &server_name, const std::string &request); + + void log_ssl(); }; diff --git a/debug.lisp b/debug.lisp index 01ae424..bfe8cc8 100644 --- a/debug.lisp +++ b/debug.lisp @@ -61,8 +61,15 @@ (define pdate (second (first (filter (lambda (x) (= (first x) "payment_Date")) e)))) (define rdate (second (first (filter (lambda (x) (= (first x) "record_Date")) e)))) - (print symbol edate pdate divrate) + ;(print symbol edate pdate divrate) + '(symbol edate pdate divrate) ) + +(print (ls-dir "/tmp")) +(print (is-file? "/tmp/file")) +(print (is-file? "/tmp/hovinko")) +(print (is-dir? "/tmp/powerlog")) + (print "Debug ends") diff --git a/ml.cpp b/ml.cpp index 2adc85a..9cb06f5 100644 --- a/ml.cpp +++ b/ml.cpp @@ -1,39 +1,23 @@ -// Comment this define out to drop support for standard library functions. -// This allows the program to run without a runtime. - - -#ifdef HAS_LIBM -#include -#else -#define NO_LIBM_SUPPORT "no libm support" -#endif - #include "ml.h" - - -#include -#include -#include -#include -#include - - -#ifdef USE_STD - -#include -#include -#include -#include - -#endif - +#include "ml_io.h" #include "csvparser.h" #include "sslclient.h" #include "json11.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + + #define TOO_FEW_ARGS "too few arguments to function" #define TOO_MANY_ARGS "too many arguments to function" #define INVALID_ARGUMENT "invalid argument" @@ -62,49 +46,24 @@ -#ifdef USE_STD - -std::string read_file_contents(const std::string &filename) { - std::ifstream f; - f.open(filename.c_str()); - if (!f) - throw std::runtime_error("could not open file"); - - f.seekg(0, std::ios::end); - std::string contents; - contents.reserve(f.tellg()); - f.seekg(0, std::ios::beg); - contents.assign(std::istreambuf_iterator(f), - std::istreambuf_iterator()); - f.close(); - - return contents; -} - -#else -#define NO_STD "no standard library support" -#endif - // Convert an object to a string using a stringstream conveniently #define to_string(x) static_cast((std::ostringstream() << std::dec << x )).str() // Replace a substring with a replacement string in a source string -void replace_substring(std::string &src, const std::string& substr, const std::string &replacement) { - size_t i = 0; - for (i = src.find(substr, i); i != std::string::npos; i = src.find(substr, i)) { - src.replace(i, substr.size(), replacement); - i += replacement.size(); - } +void replace_substring(std::string &src, const std::string &substr, const std::string &replacement) { + size_t i = 0; + for (i = src.find(substr, i); i != std::string::npos; i = src.find(substr, i)) { + src.replace(i, substr.size(), replacement); + i += replacement.size(); + } } // Is this character a valid lisp symbol character bool is_symbol(char ch) { - return (isalpha(ch) || ispunct(ch)) && ch != '(' && ch != ')' && ch != '"' && ch != '\''; + return (isalpha(ch) || ispunct(ch)) && ch != '(' && ch != ')' && ch != '"' && ch != '\''; } - - MlValue::MlValue() : type(UNIT) {} MlValue::MlValue(int i) : type(INT) { stack_data.i = i; } @@ -114,808 +73,799 @@ MlValue::MlValue(double f) : type(FLOAT) { stack_data.f = f; } MlValue::MlValue(const std::vector &list) : type(LIST), list(list) {} MlValue MlValue::quote(const MlValue "ed) { - MlValue result; - result.type = QUOTE; + MlValue result; + result.type = QUOTE; - // The first position in the list is - // used to store the quoted expression. - result.list.push_back(quoted); - return result; + // The first position in the list is + // used to store the quoted expression. + result.list.push_back(quoted); + return result; } MlValue MlValue::atom(const std::string &s) { - MlValue result; - result.type = ATOM; + MlValue result; + result.type = ATOM; - // We use the `str` member to store the atom. - result.str = s; - return result; + // We use the `str` member to store the atom. + result.str = s; + return result; } MlValue MlValue::string(const std::string &s) { - MlValue result; - result.type = STRING; + MlValue result; + result.type = STRING; - // We use the `str` member to store the string. - result.str = s; - return result; + // We use the `str` member to store the string. + result.str = s; + return result; } // Construct a lambda function MlValue::MlValue(const std::vector ¶ms, MlValue ret, MlEnvironment const &env) : type(LAMBDA) { - // We store the params and the result in the list member - // instead of having dedicated members. This is to save memory. - list.push_back(MlValue(params)); - list.push_back(ret); + // We store the params and the result in the list member + // instead of having dedicated members. This is to save memory. + list.push_back(MlValue(params)); + list.push_back(ret); - // Lambdas capture only variables that they know they will use. - std::vector used_atoms = ret.get_used_atoms(); - for (size_t i = 0; i < used_atoms.size(); i++) { - // If the environment has a symbol that this lambda uses, capture it. - if (env.has(used_atoms[i])) - lambda_scope.set(used_atoms[i], env.get(used_atoms[i])); - } + // Lambdas capture only variables that they know they will use. + std::vector used_atoms = ret.get_used_atoms(); + for (size_t i = 0; i < used_atoms.size(); i++) { + // If the environment has a symbol that this lambda uses, capture it. + if (env.has(used_atoms[i])) + lambda_scope.set(used_atoms[i], env.get(used_atoms[i])); + } } // Construct a builtin function MlValue::MlValue(const std::string &name, Builtin b) : type(BUILTIN) { - // Store the name of the builtin function in the str member - // to save memory, and use the builtin function slot in the union - // to store the function pointer. - str = name; - stack_data.b = b; + // Store the name of the builtin function in the str member + // to save memory, and use the builtin function slot in the union + // to store the function pointer. + str = name; + stack_data.b = b; } // Get all of the atoms used in a given MlValue std::vector MlValue::get_used_atoms() { - std::vector result, tmp; - switch (type) { - case QUOTE: - // The data for a quote is stored in the - // first slot of the list member. - return list[0].get_used_atoms(); - case ATOM: - // If this is an atom, add it to the list - // of used atoms in this expression. - result.push_back(as_atom()); - return result; - case LAMBDA: - // If this is a lambda, get the list of used atoms in the body - // of the expression. - return list[1].get_used_atoms(); - case LIST: - // If this is a list, add each of the atoms used in all - // of the elements in the list. - for (size_t i = 0; i < list.size(); i++) { - // Get the atoms used in the element - tmp = list[i].get_used_atoms(); - // Add the used atoms to the current list of used atoms - result.insert(result.end(), tmp.begin(), tmp.end()); - } - return result; - default: - return result; - } + std::vector result, tmp; + switch (type) { + case QUOTE: + // The data for a quote is stored in the + // first slot of the list member. + return list[0].get_used_atoms(); + case ATOM: + // If this is an atom, add it to the list + // of used atoms in this expression. + result.push_back(as_atom()); + return result; + case LAMBDA: + // If this is a lambda, get the list of used atoms in the body + // of the expression. + return list[1].get_used_atoms(); + case LIST: + // If this is a list, add each of the atoms used in all + // of the elements in the list. + for (size_t i = 0; i < list.size(); i++) { + // Get the atoms used in the element + tmp = list[i].get_used_atoms(); + // Add the used atoms to the current list of used atoms + result.insert(result.end(), tmp.begin(), tmp.end()); + } + return result; + default: + return result; + } } // Is this a builtin function? bool MlValue::is_builtin() { - return type == BUILTIN; + return type == BUILTIN; } bool MlValue::is_number() const { - return type == INT || type == FLOAT; + return type == INT || type == FLOAT; } // Get the "truthy" boolean value of this value. bool MlValue::as_bool() const { - return *this != MlValue(0); + return *this != MlValue(0); } // Get this item's integer value int MlValue::as_int() const { - return cast_to_int().stack_data.i; + return cast_to_int().stack_data.i; } // Get this item's floating point value double MlValue::as_float() const { - return cast_to_int().stack_data.f; + return cast_to_int().stack_data.f; } // Get this item's string value std::string MlValue::as_string() const { - // If this item is not a string, throw a cast error. - if (type != STRING) - throw MlError(*this, MlEnvironment(), BAD_CAST); - return str; + // If this item is not a string, throw a cast error. + if (type != STRING) + throw MlError(*this, MlEnvironment(), BAD_CAST); + return str; } // Get this item's atom value std::string MlValue::as_atom() const { - // If this item is not an atom, throw a cast error. - if (type != ATOM) - throw MlError(*this, MlEnvironment(), BAD_CAST); - return str; + // If this item is not an atom, throw a cast error. + if (type != ATOM) + throw MlError(*this, MlEnvironment(), BAD_CAST); + return str; } // Get this item's list value std::vector MlValue::as_list() const { - // If this item is not a list, throw a cast error. - if (type != LIST) - throw MlError(*this, MlEnvironment(), BAD_CAST); - return list; + // If this item is not a list, throw a cast error. + if (type != LIST) + throw MlError(*this, MlEnvironment(), BAD_CAST); + return list; } // Push an item to the end of this list void MlValue::push(MlValue val) { - // If this item is not a list, you cannot push to it. - // Throw an error. - if (type != LIST) - throw MlError(*this, MlEnvironment(), MISMATCHED_TYPES); + // If this item is not a list, you cannot push to it. + // Throw an error. + if (type != LIST) + throw MlError(*this, MlEnvironment(), MISMATCHED_TYPES); - list.push_back(val); + list.push_back(val); } // Push an item from the end of this list MlValue MlValue::pop() { - // If this item is not a list, you cannot pop from it. - // Throw an error. - if (type != LIST) - throw MlError(*this, MlEnvironment(), MISMATCHED_TYPES); + // If this item is not a list, you cannot pop from it. + // Throw an error. + if (type != LIST) + throw MlError(*this, MlEnvironment(), MISMATCHED_TYPES); - // Remember the last item in the list - MlValue result = list[list.size() - 1]; - // Remove it from this instance - list.pop_back(); - // Return the remembered value - return result; + // Remember the last item in the list + MlValue result = list[list.size() - 1]; + // Remove it from this instance + list.pop_back(); + // Return the remembered value + return result; } // Cast this to an integer value MlValue MlValue::cast_to_int() const { - switch (type) { - case INT: - return *this; - case FLOAT: - return MlValue(int(stack_data.f)); - // Only ints and floats can be cast to an int - default: - throw MlError(*this, MlEnvironment(), BAD_CAST); - } + switch (type) { + case INT: + return *this; + case FLOAT: + return MlValue(int(stack_data.f)); + // Only ints and floats can be cast to an int + default: + throw MlError(*this, MlEnvironment(), BAD_CAST); + } } // Cast this to a floating point value MlValue MlValue::cast_to_float() const { - switch (type) { - case FLOAT: - return *this; - case INT: - return MlValue(float(stack_data.i)); - // Only ints and floats can be cast to a float - default: - throw MlError(*this, MlEnvironment(), BAD_CAST); - } + switch (type) { + case FLOAT: + return *this; + case INT: + return MlValue(float(stack_data.i)); + // Only ints and floats can be cast to a float + default: + throw MlError(*this, MlEnvironment(), BAD_CAST); + } } bool MlValue::operator==(MlValue other) const { - // If either of these values are floats, promote the - // other to a float, and then compare for equality. - if (type == FLOAT && other.type == INT) return *this == other.cast_to_float(); - else if (type == INT && other.type == FLOAT) return this->cast_to_float() == other; - // If the values types aren't equal, then they cannot be equal. - else if (type != other.type) return false; + // If either of these values are floats, promote the + // other to a float, and then compare for equality. + if (type == FLOAT && other.type == INT) return *this == other.cast_to_float(); + else if (type == INT && other.type == FLOAT) return this->cast_to_float() == other; + // If the values types aren't equal, then they cannot be equal. + else if (type != other.type) return false; - switch (type) { - case FLOAT: - return stack_data.f == other.stack_data.f; - case INT: - return stack_data.i == other.stack_data.i; - case BUILTIN: - return stack_data.b == other.stack_data.b; - case STRING: - case ATOM: - // Both atoms and strings store their - // data in the str member. - return str == other.str; - case LAMBDA: - case LIST: - // Both lambdas and lists store their - // data in the list member. - return list == other.list; - case QUOTE: - // The values for quotes are stored in the - // first slot of the list member. - return list[0] == other.list[0]; - default: - return true; - } + switch (type) { + case FLOAT: + return stack_data.f == other.stack_data.f; + case INT: + return stack_data.i == other.stack_data.i; + case BUILTIN: + return stack_data.b == other.stack_data.b; + case STRING: + case ATOM: + // Both atoms and strings store their + // data in the str member. + return str == other.str; + case LAMBDA: + case LIST: + // Both lambdas and lists store their + // data in the list member. + return list == other.list; + case QUOTE: + // The values for quotes are stored in the + // first slot of the list member. + return list[0] == other.list[0]; + default: + return true; + } } bool MlValue::operator!=(const MlValue &other) const { - return !(*this == other); + return !(*this == other); } bool MlValue::operator>=(const MlValue &other) const { - return !(*this < other); + return !(*this < other); } bool MlValue::operator<=(const MlValue &other) const { - return (*this == other) || (*this < other); + return (*this == other) || (*this < other); } bool MlValue::operator>(const MlValue &other) const { - return !(*this <= other); + return !(*this <= other); } bool MlValue::operator<(const MlValue &other) const { - // Other type must be a float or an int - if (other.type != FLOAT && other.type != INT) - throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); + // Other type must be a float or an int + if (other.type != FLOAT && other.type != INT) + throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); - switch (type) { - case FLOAT: - // If this is a float, promote the other value to a float and compare. - return stack_data.f < other.cast_to_float().stack_data.f; - case INT: - // If the other value is a float, promote this value to a float and compare. - if (other.type == FLOAT) - return cast_to_float().stack_data.f < other.stack_data.f; - // Otherwise compare the integer values - else return stack_data.i < other.stack_data.i; - default: - // Only allow comparisons between integers and floats - throw MlError(*this, MlEnvironment(), INVALID_ORDER); - } + switch (type) { + case FLOAT: + // If this is a float, promote the other value to a float and compare. + return stack_data.f < other.cast_to_float().stack_data.f; + case INT: + // If the other value is a float, promote this value to a float and compare. + if (other.type == FLOAT) + return cast_to_float().stack_data.f < other.stack_data.f; + // Otherwise compare the integer values + else return stack_data.i < other.stack_data.i; + default: + // Only allow comparisons between integers and floats + throw MlError(*this, MlEnvironment(), INVALID_ORDER); + } } // This function adds two lisp values, and returns the lisp value result. MlValue MlValue::operator+(const MlValue &other) const { - // If the other value's type is the unit type, - // don't even bother continuing. - // Unit types consume all arithmetic operations. - if (other.type == UNIT) return other; + // If the other value's type is the unit type, + // don't even bother continuing. + // Unit types consume all arithmetic operations. + if (other.type == UNIT) return other; - // Other type must be a float or an int - if ((is_number() || other.is_number()) && - !(is_number() && other.is_number())) - throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); + // Other type must be a float or an int + if ((is_number() || other.is_number()) && + !(is_number() && other.is_number())) + throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); - switch (type) { - case FLOAT: - // If one is a float, promote the other by default and do - // float addition. - return MlValue(stack_data.f + other.cast_to_float().stack_data.f); - case INT: - // If the other type is a float, go ahead and promote this expression - // before continuing with the addition. - if (other.type == FLOAT) - return MlValue(cast_to_float() + other.stack_data.f); - // Otherwise, do integer addition. - else return MlValue(stack_data.i + other.stack_data.i); - case STRING: - // If the other value is also a string, do the concat - if (other.type == STRING) - return MlValue::string(str + other.str); - // We throw an error if we try to concat anything of non-string type - else throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); - case LIST: - // If the other value is also a list, do the concat - if (other.type == LIST) { - // Maintain the value that will be returned - MlValue result = *this; - // Add each item in the other list to the end of this list - for (size_t i = 0; i < other.list.size(); i++) - result.push(other.list[i]); - return result; + switch (type) { + case FLOAT: + // If one is a float, promote the other by default and do + // float addition. + return MlValue(stack_data.f + other.cast_to_float().stack_data.f); + case INT: + // If the other type is a float, go ahead and promote this expression + // before continuing with the addition. + if (other.type == FLOAT) + return MlValue(cast_to_float() + other.stack_data.f); + // Otherwise, do integer addition. + else return MlValue(stack_data.i + other.stack_data.i); + case STRING: + // If the other value is also a string, do the concat + if (other.type == STRING) + return MlValue::string(str + other.str); + // We throw an error if we try to concat anything of non-string type + else throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); + case LIST: + // If the other value is also a list, do the concat + if (other.type == LIST) { + // Maintain the value that will be returned + MlValue result = *this; + // Add each item in the other list to the end of this list + for (size_t i = 0; i < other.list.size(); i++) + result.push(other.list[i]); + return result; - } else throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); - case UNIT: - return *this; - default: - throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); - } + } else throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); + case UNIT: + return *this; + default: + throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); + } } // This function subtracts two lisp values, and returns the lisp value result. MlValue MlValue::operator-(const MlValue &other) const { - // If the other value's type is the unit type, - // don't even bother continuing. - // Unit types consume all arithmetic operations. - if (other.type == UNIT) return other; + // If the other value's type is the unit type, + // don't even bother continuing. + // Unit types consume all arithmetic operations. + if (other.type == UNIT) return other; - // Other type must be a float or an int - if (other.type != FLOAT && other.type != INT) - throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); + // Other type must be a float or an int + if (other.type != FLOAT && other.type != INT) + throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); - switch (type) { - case FLOAT: - // If one is a float, promote the other by default and do - // float subtraction. - return MlValue(stack_data.f - other.cast_to_float().stack_data.f); - case INT: - // If the other type is a float, go ahead and promote this expression - // before continuing with the subtraction - if (other.type == FLOAT) - return MlValue(cast_to_float().stack_data.f - other.stack_data.f); - // Otherwise, do integer subtraction. - else return MlValue(stack_data.i - other.stack_data.i); - case UNIT: - // Unit types consume all arithmetic operations. - return *this; - default: - // This operation was done on an unsupported type - throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); - } + switch (type) { + case FLOAT: + // If one is a float, promote the other by default and do + // float subtraction. + return MlValue(stack_data.f - other.cast_to_float().stack_data.f); + case INT: + // If the other type is a float, go ahead and promote this expression + // before continuing with the subtraction + if (other.type == FLOAT) + return MlValue(cast_to_float().stack_data.f - other.stack_data.f); + // Otherwise, do integer subtraction. + else return MlValue(stack_data.i - other.stack_data.i); + case UNIT: + // Unit types consume all arithmetic operations. + return *this; + default: + // This operation was done on an unsupported type + throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); + } } // This function multiplies two lisp values, and returns the lisp value result. MlValue MlValue::operator*(const MlValue &other) const { - // If the other value's type is the unit type, - // don't even bother continuing. - // Unit types consume all arithmetic operations. - if (other.type == UNIT) return other; + // If the other value's type is the unit type, + // don't even bother continuing. + // Unit types consume all arithmetic operations. + if (other.type == UNIT) return other; - // Other type must be a float or an int - if (other.type != FLOAT && other.type != INT) - throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); + // Other type must be a float or an int + if (other.type != FLOAT && other.type != INT) + throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); - switch (type) { - case FLOAT: - return MlValue(stack_data.f * other.cast_to_float().stack_data.f); - case INT: - // If the other type is a float, go ahead and promote this expression - // before continuing with the product - if (other.type == FLOAT) - return MlValue(cast_to_float().stack_data.f * other.stack_data.f); - // Otherwise, do integer multiplication. - else return MlValue(stack_data.i * other.stack_data.i); - case UNIT: - // Unit types consume all arithmetic operations. - return *this; - default: - // This operation was done on an unsupported type - throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); - } + switch (type) { + case FLOAT: + return MlValue(stack_data.f * other.cast_to_float().stack_data.f); + case INT: + // If the other type is a float, go ahead and promote this expression + // before continuing with the product + if (other.type == FLOAT) + return MlValue(cast_to_float().stack_data.f * other.stack_data.f); + // Otherwise, do integer multiplication. + else return MlValue(stack_data.i * other.stack_data.i); + case UNIT: + // Unit types consume all arithmetic operations. + return *this; + default: + // This operation was done on an unsupported type + throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); + } } // This function divides two lisp values, and returns the lisp value result. MlValue MlValue::operator/(const MlValue &other) const { - // If the other value's type is the unit type, - // don't even bother continuing. - // Unit types consume all arithmetic operations. - if (other.type == UNIT) return other; + // If the other value's type is the unit type, + // don't even bother continuing. + // Unit types consume all arithmetic operations. + if (other.type == UNIT) return other; - // Other type must be a float or an int - if (other.type != FLOAT && other.type != INT) - throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); + // Other type must be a float or an int + if (other.type != FLOAT && other.type != INT) + throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); - switch (type) { - case FLOAT: - return MlValue(stack_data.f / other.cast_to_float().stack_data.f); - case INT: - // If the other type is a float, go ahead and promote this expression - // before continuing with the product - if (other.type == FLOAT) - return MlValue(cast_to_float().stack_data.f / other.stack_data.f); - // Otherwise, do integer multiplication. - else return MlValue(stack_data.i / other.stack_data.i); - case UNIT: - // Unit types consume all arithmetic operations. - return *this; - default: - // This operation was done on an unsupported type - throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); - } + switch (type) { + case FLOAT: + return MlValue(stack_data.f / other.cast_to_float().stack_data.f); + case INT: + // If the other type is a float, go ahead and promote this expression + // before continuing with the product + if (other.type == FLOAT) + return MlValue(cast_to_float().stack_data.f / other.stack_data.f); + // Otherwise, do integer multiplication. + else return MlValue(stack_data.i / other.stack_data.i); + case UNIT: + // Unit types consume all arithmetic operations. + return *this; + default: + // This operation was done on an unsupported type + throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); + } } // This function finds the remainder of two lisp values, and returns the lisp value result. MlValue MlValue::operator%(const MlValue &other) const { - // If the other value's type is the unit type, - // don't even bother continuing. - // Unit types consume all arithmetic operations. - if (other.type == UNIT) return other; + // If the other value's type is the unit type, + // don't even bother continuing. + // Unit types consume all arithmetic operations. + if (other.type == UNIT) return other; - // Other type must be a float or an int - if (other.type != FLOAT && other.type != INT) - throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); + // Other type must be a float or an int + if (other.type != FLOAT && other.type != INT) + throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); - switch (type) { - // If we support libm, we can find the remainder of floating point values. -#ifdef HAS_LIBM - case FLOAT: - return MlValue(fmod(stack_data.f, other.cast_to_float().stack_data.f)); - case INT: - if (other.type == FLOAT) - return MlValue(fmod(cast_to_float().stack_data.f, other.stack_data.f)); - else return MlValue(stack_data.i % other.stack_data.i); -#else - case INT: - // If we do not support libm, we have to throw errors for floating point values. - if (other.type != INT) - throw MlError(other, MlEnvironment(), NO_LIBM_SUPPORT); - return MlValue(stack_data.i % other.stack_data.i); -#endif - - case UNIT: - // Unit types consume all arithmetic operations. - return *this; - default: - // This operation was done on an unsupported type - throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); - } + switch (type) { + // If we support libm, we can find the remainder of floating point values. + case FLOAT: + return MlValue(fmod(stack_data.f, other.cast_to_float().stack_data.f)); + case INT: + if (other.type == FLOAT) + return MlValue(fmod(cast_to_float().stack_data.f, other.stack_data.f)); + else return MlValue(stack_data.i % other.stack_data.i); + case UNIT: + // Unit types consume all arithmetic operations. + return *this; + default: + // This operation was done on an unsupported type + throw MlError(*this, MlEnvironment(), INVALID_BIN_OP); + } } // Get the name of the type of this value std::string MlValue::get_type_name() { - switch (type) { - case QUOTE: - return QUOTE_TYPE; - case ATOM: - return ATOM_TYPE; - case INT: - return INT_TYPE; - case FLOAT: - return FLOAT_TYPE; - case LIST: - return LIST_TYPE; - case STRING: - return STRING_TYPE; - case BUILTIN: - case LAMBDA: - // Instead of differentiating between - // lambda and builtin types, we group them together. - // This is because they are both callable. - return FUNCTION_TYPE; - case UNIT: - return UNIT_TYPE; - default: - // We don't know the name of this type. - // This isn't the users fault, this is just unhandled. - // This should never be reached. - throw MlError(*this, MlEnvironment(), INTERNAL_ERROR); - } + switch (type) { + case QUOTE: + return QUOTE_TYPE; + case ATOM: + return ATOM_TYPE; + case INT: + return INT_TYPE; + case FLOAT: + return FLOAT_TYPE; + case LIST: + return LIST_TYPE; + case STRING: + return STRING_TYPE; + case BUILTIN: + case LAMBDA: + // Instead of differentiating between + // lambda and builtin types, we group them together. + // This is because they are both callable. + return FUNCTION_TYPE; + case UNIT: + return UNIT_TYPE; + default: + // We don't know the name of this type. + // This isn't the users fault, this is just unhandled. + // This should never be reached. + throw MlError(*this, MlEnvironment(), INTERNAL_ERROR); + } } std::string MlValue::display() const { - std::string result; - switch (type) { - case QUOTE: - return "'" + list[0].debug(); - case ATOM: - return str; - case INT: - return to_string(stack_data.i); - case FLOAT: - return to_string(stack_data.f); - case STRING: - return str; - case LAMBDA: - for (size_t i = 0; i < list.size(); i++) { - result += list[i].debug(); - if (i < list.size() - 1) result += " "; - } - return "(lambda " + result + ")"; - case LIST: - for (size_t i = 0; i < list.size(); i++) { - result += list[i].debug(); - if (i < list.size() - 1) result += " "; - } - return "(" + result + ")"; - case BUILTIN: - return "<" + str + " at " + to_string(long(stack_data.b)) + ">"; - case UNIT: - return "@"; - default: - // We don't know how to display whatever type this is. - // This isn't the users fault, this is just unhandled. - // This should never be reached. - throw MlError(*this, MlEnvironment(), INTERNAL_ERROR); - } + std::string result; + switch (type) { + case QUOTE: + return "'" + list[0].debug(); + case ATOM: + return str; + case INT: + return to_string(stack_data.i); + case FLOAT: + return to_string(stack_data.f); + case STRING: + return str; + case LAMBDA: + for (size_t i = 0; i < list.size(); i++) { + result += list[i].debug(); + if (i < list.size() - 1) result += " "; + } + return "(lambda " + result + ")"; + case LIST: + for (size_t i = 0; i < list.size(); i++) { + result += list[i].debug(); + if (i < list.size() - 1) result += " "; + } + return "(" + result + ")"; + case BUILTIN: + return "<" + str + " at " + to_string(long(stack_data.b)) + ">"; + case UNIT: + return "@"; + default: + // We don't know how to display whatever type this is. + // This isn't the users fault, this is just unhandled. + // This should never be reached. + throw MlError(*this, MlEnvironment(), INTERNAL_ERROR); + } } std::string MlValue::debug() const { - std::string result; - switch (type) { - case QUOTE: - return "'" + list[0].debug(); - case ATOM: - return str; - case INT: - return to_string(stack_data.i); - case FLOAT: - return to_string(stack_data.f); - case STRING: - for (size_t i = 0; i < str.length(); i++) { - if (str[i] == '"') result += "\\\""; - else result.push_back(str[i]); - } - return "\"" + result + "\""; - case LAMBDA: - for (size_t i = 0; i < list.size(); i++) { - result += list[i].debug(); - if (i < list.size() - 1) result += " "; - } - return "(lambda " + result + ")"; - case LIST: - for (size_t i = 0; i < list.size(); i++) { - result += list[i].debug(); - if (i < list.size() - 1) result += " "; - } - return "(" + result + ")"; - case BUILTIN: - return "<" + str + " at " + to_string(long(stack_data.b)) + ">"; - case UNIT: - return "@"; - default: - // We don't know how to debug whatever type this is. - // This isn't the users fault, this is just unhandled. - // This should never be reached. - throw MlError(*this, MlEnvironment(), INTERNAL_ERROR); - } + std::string result; + switch (type) { + case QUOTE: + return "'" + list[0].debug(); + case ATOM: + return str; + case INT: + return to_string(stack_data.i); + case FLOAT: + return to_string(stack_data.f); + case STRING: + for (size_t i = 0; i < str.length(); i++) { + if (str[i] == '"') result += "\\\""; + else result.push_back(str[i]); + } + return "\"" + result + "\""; + case LAMBDA: + for (size_t i = 0; i < list.size(); i++) { + result += list[i].debug(); + if (i < list.size() - 1) result += " "; + } + return "(lambda " + result + ")"; + case LIST: + for (size_t i = 0; i < list.size(); i++) { + result += list[i].debug(); + if (i < list.size() - 1) result += " "; + } + return "(" + result + ")"; + case BUILTIN: + return "<" + str + " at " + to_string(long(stack_data.b)) + ">"; + case UNIT: + return "@"; + default: + // We don't know how to debug whatever type this is. + // This isn't the users fault, this is just unhandled. + // This should never be reached. + throw MlError(*this, MlEnvironment(), INTERNAL_ERROR); + } } std::ostream &operator<<(std::ostream &os, MlValue const &v) { - return os << v.display(); + return os << v.display(); } MlError::MlError(const MlValue &v, MlEnvironment const &env, const char *msg) : env(env), msg(msg) { - cause = new MlValue; - *cause = v; + cause = new MlValue; + *cause = v; } MlError::MlError(MlError const &other) : env(other.env), msg(other.msg) { - cause = new MlValue(*other.cause); + cause = new MlValue(*other.cause); } MlError::~MlError() { - delete cause; + delete cause; } std::string MlError::description() { - return "error: the expression `" + cause->debug() + "` failed in scope " + to_string(env) + " with message \"" + - msg + "\""; + return "error: the expression `" + cause->debug() + "` failed in scope " + to_string(env) + " with message \"" + + msg + "\""; } void MlEnvironment::combine(MlEnvironment const &other) { - // Normally, I would use the `insert` method of the `map` class, - // but it doesn't overwrite previously declared values for keys. - std::map::const_iterator itr = other.defs.begin(); - for (; itr != other.defs.end(); itr++) { - // Iterate through the keys and assign each value. - defs[itr->first] = itr->second; - } + // Normally, I would use the `insert` method of the `map` class, + // but it doesn't overwrite previously declared values for keys. + std::map::const_iterator itr = other.defs.begin(); + for (; itr != other.defs.end(); itr++) { + // Iterate through the keys and assign each value. + defs[itr->first] = itr->second; + } } std::ostream &operator<<(std::ostream &os, MlEnvironment const &e) { - std::map::const_iterator itr = e.defs.begin(); - os << "{ "; - for (; itr != e.defs.end(); itr++) { - os << '\'' << itr->first << "' : " << itr->second.debug() << ", "; - } - return os << "}"; + std::map::const_iterator itr = e.defs.begin(); + os << "{ "; + for (; itr != e.defs.end(); itr++) { + os << '\'' << itr->first << "' : " << itr->second.debug() << ", "; + } + return os << "}"; } void MlEnvironment::set(std::string name, MlValue value) { - defs[name] = value; + defs[name] = value; } MlValue MlValue::apply(std::vector args, MlEnvironment &env) { - MlEnvironment e; - std::vector params; - switch (type) { - case LAMBDA: - // Get the list of parameter atoms - params = list[0].list; - if (params.size() != args.size()) - throw MlError(MlValue(args), env, args.size() > params.size() ? - TOO_MANY_ARGS : TOO_FEW_ARGS - ); + MlEnvironment e; + std::vector params; + switch (type) { + case LAMBDA: + // Get the list of parameter atoms + params = list[0].list; + if (params.size() != args.size()) + throw MlError(MlValue(args), env, args.size() > params.size() ? + TOO_MANY_ARGS : TOO_FEW_ARGS + ); - // Get the captured scope from the lambda - e = lambda_scope; - // And make this scope the parent scope - e.set_parent_scope(&env); + // Get the captured scope from the lambda + e = lambda_scope; + // And make this scope the parent scope + e.set_parent_scope(&env); - // Iterate through the list of parameters and - // insert the arguments into the scope. - for (size_t i = 0; i < params.size(); i++) { - if (params[i].type != ATOM) - throw MlError(*this, env, INVALID_LAMBDA); - // Set the parameter name into the scope. - e.set(params[i].str, args[i]); - } + // Iterate through the list of parameters and + // insert the arguments into the scope. + for (size_t i = 0; i < params.size(); i++) { + if (params[i].type != ATOM) + throw MlError(*this, env, INVALID_LAMBDA); + // Set the parameter name into the scope. + e.set(params[i].str, args[i]); + } - // Evaluate the function body with the function scope - return list[1].eval(e); - case BUILTIN: - // Here, we call the builtin function with the current scope. - // This allows us to write special forms without syntactic sugar. - // For functions that are not special forms, we just evaluate - // the arguments before we run the function. - return (stack_data.b)(args, env); - default: - // We can only call lambdas and builtins - throw MlError(*this, env, CALL_NON_FUNCTION); - } + // Evaluate the function body with the function scope + return list[1].eval(e); + case BUILTIN: + // Here, we call the builtin function with the current scope. + // This allows us to write special forms without syntactic sugar. + // For functions that are not special forms, we just evaluate + // the arguments before we run the function. + return (stack_data.b)(args, env); + default: + // We can only call lambdas and builtins + throw MlError(*this, env, CALL_NON_FUNCTION); + } } MlValue MlValue::eval(MlEnvironment &env) { - std::vector args; - MlValue function; - MlEnvironment e; - switch (type) { - case QUOTE: - return list[0]; - case ATOM: - return env.get(str); - case LIST: - if (list.size() < 1) - throw MlError(*this, env, EVAL_EMPTY_LIST); + std::vector args; + MlValue function; + MlEnvironment e; + switch (type) { + case QUOTE: + return list[0]; + case ATOM: + return env.get(str); + case LIST: + if (list.size() < 1) + throw MlError(*this, env, EVAL_EMPTY_LIST); - args = std::vector(list.begin() + 1, list.end()); + args = std::vector(list.begin() + 1, list.end()); - // Only evaluate our arguments if it's not builtin! - // Builtin functions can be special forms, so we - // leave them to evaluate their arguments. - function = list[0].eval(env); + // Only evaluate our arguments if it's not builtin! + // Builtin functions can be special forms, so we + // leave them to evaluate their arguments. + function = list[0].eval(env); - if (!function.is_builtin()) - for (size_t i = 0; i < args.size(); i++) - args[i] = args[i].eval(env); + if (!function.is_builtin()) + for (size_t i = 0; i < args.size(); i++) + args[i] = args[i].eval(env); - return function.apply( - args, - env - ); + return function.apply( + args, + env + ); - default: - return *this; - } + default: + return *this; + } } void skip_whitespace(const std::string &s, int &ptr) { - while (isspace(s[ptr])) { ptr++; } + while (isspace(s[ptr])) { ptr++; } } // Parse a single value and increment the pointer // to the beginning of the next value to parse. MlValue parse(std::string &s, int &ptr) { - skip_whitespace(s, ptr); + skip_whitespace(s, ptr); - while (s[ptr] == ';') { - // If this is a comment - int save_ptr = ptr; - while (s[save_ptr] != '\n' && save_ptr < int(s.length())) { save_ptr++; } - s.erase(ptr, save_ptr - ptr); - skip_whitespace(s, ptr); + while (s[ptr] == ';') { + // If this is a comment + int save_ptr = ptr; + while (s[save_ptr] != '\n' && save_ptr < int(s.length())) { save_ptr++; } + s.erase(ptr, save_ptr - ptr); + skip_whitespace(s, ptr); - if (s.substr(ptr, s.length() - ptr - 1) == "") - return MlValue(); - } + if (s.substr(ptr, s.length() - ptr - 1) == "") + return MlValue(); + } - if (s == "") { - return MlValue(); - } else if (s[ptr] == '\'') { - // If this is a quote - ptr++; - return MlValue::quote(parse(s, ptr)); + if (s == "") { + return MlValue(); + } else if (s[ptr] == '\'') { + // If this is a quote + ptr++; + return MlValue::quote(parse(s, ptr)); - } else if (s[ptr] == '(') { - // If this is a list - skip_whitespace(s, ++ptr); + } else if (s[ptr] == '(') { + // If this is a list + skip_whitespace(s, ++ptr); - MlValue result = MlValue(std::vector()); + MlValue result = MlValue(std::vector()); - while (s[ptr] != ')') - result.push(parse(s, ptr)); + while (s[ptr] != ')') + result.push(parse(s, ptr)); - skip_whitespace(s, ++ptr); - return result; + skip_whitespace(s, ++ptr); + return result; - } else if (isdigit(s[ptr]) || (s[ptr] == '-' && isdigit(s[ptr + 1]))) { - // If this is a number - bool negate = s[ptr] == '-'; - if (negate) ptr++; + } else if (isdigit(s[ptr]) || (s[ptr] == '-' && isdigit(s[ptr + 1]))) { + // If this is a number + bool negate = s[ptr] == '-'; + if (negate) ptr++; - int save_ptr = ptr; - while (isdigit(s[ptr]) || s[ptr] == '.') ptr++; - std::string n = s.substr(save_ptr, ptr); - skip_whitespace(s, ptr); + int save_ptr = ptr; + while (isdigit(s[ptr]) || s[ptr] == '.') ptr++; + std::string n = s.substr(save_ptr, ptr); + skip_whitespace(s, ptr); - if (n.find('.') != std::string::npos) - return MlValue((negate ? -1 : 1) * atof(n.c_str())); - else return MlValue((negate ? -1 : 1) * atoi(n.c_str())); + if (n.find('.') != std::string::npos) + return MlValue((negate ? -1 : 1) * atof(n.c_str())); + else return MlValue((negate ? -1 : 1) * atoi(n.c_str())); - } else if (s[ptr] == '\"') { - // If this is a string - int n = 1; - while (s[ptr + n] != '\"') { - if (ptr + n >= int(s.length())) - throw std::runtime_error(MALFORMED_PROGRAM); + } else if (s[ptr] == '\"') { + // If this is a string + int n = 1; + while (s[ptr + n] != '\"') { + if (ptr + n >= int(s.length())) + throw std::runtime_error(MALFORMED_PROGRAM); - if (s[ptr + n] == '\\') n++; - n++; - } + if (s[ptr + n] == '\\') n++; + n++; + } - std::string x = s.substr(ptr + 1, n - 1); - ptr += n + 1; - skip_whitespace(s, ptr); + std::string x = s.substr(ptr + 1, n - 1); + ptr += n + 1; + skip_whitespace(s, ptr); - // Iterate over the characters in the string, and - // replace escaped characters with their intended values. - for (size_t i = 0; i < x.size(); i++) { - if (x[i] == '\\' && x[i + 1] == '\\') - x.replace(i, 2, "\\"); - else if (x[i] == '\\' && x[i + 1] == '"') - x.replace(i, 2, "\""); - else if (x[i] == '\\' && x[i + 1] == 'n') - x.replace(i, 2, "\n"); - else if (x[i] == '\\' && x[i + 1] == 't') - x.replace(i, 2, "\t"); - } + // Iterate over the characters in the string, and + // replace escaped characters with their intended values. + for (size_t i = 0; i < x.size(); i++) { + if (x[i] == '\\' && x[i + 1] == '\\') + x.replace(i, 2, "\\"); + else if (x[i] == '\\' && x[i + 1] == '"') + x.replace(i, 2, "\""); + else if (x[i] == '\\' && x[i + 1] == 'n') + x.replace(i, 2, "\n"); + else if (x[i] == '\\' && x[i + 1] == 't') + x.replace(i, 2, "\t"); + } - return MlValue::string(x); - } else if (s[ptr] == '@') { - ptr++; - skip_whitespace(s, ptr); - return MlValue(); + return MlValue::string(x); + } else if (s[ptr] == '@') { + ptr++; + skip_whitespace(s, ptr); + return MlValue(); - } else if (is_symbol(s[ptr])) { - // If this is a string - int n = 0; - while (is_symbol(s[ptr + n])) { - n++; - } + } else if (is_symbol(s[ptr])) { + // If this is a string + int n = 0; + while (is_symbol(s[ptr + n])) { + n++; + } - std::string x = s.substr(ptr, n); - ptr += n; - skip_whitespace(s, ptr); - return MlValue::atom(x); - } else { - throw std::runtime_error(MALFORMED_PROGRAM); - } + std::string x = s.substr(ptr, n); + ptr += n; + skip_whitespace(s, ptr); + return MlValue::atom(x); + } else { + throw std::runtime_error(MALFORMED_PROGRAM); + } } // Parse an entire program and get its list of expressions. std::vector parse(std::string s) { - int i = 0, last_i = -1; - std::vector result; - // While the parser is making progress (while the pointer is moving right) - // and the pointer hasn't reached the end of the string, - while (last_i != i && i <= int(s.length() - 1)) { - // Parse another expression and add it to the list. - last_i = i; - result.push_back(parse(s, i)); - } + int i = 0, last_i = -1; + std::vector result; + // While the parser is making progress (while the pointer is moving right) + // and the pointer hasn't reached the end of the string, + while (last_i != i && i <= int(s.length() - 1)) { + // Parse another expression and add it to the list. + last_i = i; + result.push_back(parse(s, i)); + } - // If the whole string wasn't parsed, the program must be bad. - if (i < int(s.length())) - throw std::runtime_error(MALFORMED_PROGRAM); + // If the whole string wasn't parsed, the program must be bad. + if (i < int(s.length())) + throw std::runtime_error(MALFORMED_PROGRAM); - // Return the list of values parsed. - return result; + // Return the list of values parsed. + return result; } // Execute code in an environment MlValue run(const std::string &code, MlEnvironment &env) { - // Parse the code - std::vector parsed = parse(code); - // Iterate over the expressions and evaluate them - // in this environment. - for (size_t i = 0; i < parsed.size() - 1; i++) - parsed[i].eval(env); + // Parse the code + std::vector parsed = parse(code); + // Iterate over the expressions and evaluate them + // in this environment. + for (size_t i = 0; i < parsed.size() - 1; i++) + parsed[i].eval(env); - // Return the result of the last expression. - return parsed[parsed.size() - 1].eval(env); + // Return the result of the last expression. + return parsed[parsed.size() - 1].eval(env); } // This namespace contains all the definitions of builtin functions @@ -927,851 +877,873 @@ namespace builtin { // their arguments. To make a regular builtin that evaluates its // arguments, we just call this function in our builtin definition. void eval_args(std::vector &args, MlEnvironment &env) { - for (size_t i = 0; i < args.size(); i++) - args[i] = args[i].eval(env); + for (size_t i = 0; i < args.size(); i++) + args[i] = args[i].eval(env); } // Create a lambda function (SPECIAL FORM) MlValue lambda(std::vector args, MlEnvironment &env) { - if (args.size() < 2) - throw MlError(MlValue("lambda", lambda), env, TOO_FEW_ARGS); + if (args.size() < 2) + throw MlError(MlValue("lambda", lambda), env, TOO_FEW_ARGS); - if (args[0].get_type_name() != LIST_TYPE) - throw MlError(MlValue("lambda", lambda), env, INVALID_LAMBDA); + if (args[0].get_type_name() != LIST_TYPE) + throw MlError(MlValue("lambda", lambda), env, INVALID_LAMBDA); - return MlValue(args[0].as_list(), args[1], env); + return MlValue(args[0].as_list(), args[1], env); } // if-else (SPECIAL FORM) MlValue if_then_else(std::vector args, MlEnvironment &env) { - if (args.size() != 3) - throw MlError(MlValue("if", if_then_else), env, args.size() > 3 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - if (args[0].eval(env).as_bool()) - return args[1].eval(env); - else return args[2].eval(env); + if (args.size() != 3) + throw MlError(MlValue("if", if_then_else), env, args.size() > 3 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + if (args[0].eval(env).as_bool()) + return args[1].eval(env); + else return args[2].eval(env); } // Define a variable with a value (SPECIAL FORM) MlValue define(std::vector args, MlEnvironment &env) { - if (args.size() != 2) - throw MlError(MlValue("define", define), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + if (args.size() != 2) + throw MlError(MlValue("define", define), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - MlValue result = args[1].eval(env); - env.set(args[0].display(), result); - return result; + MlValue result = args[1].eval(env); + env.set(args[0].display(), result); + return result; } // Define a function with parameters and a result expression (SPECIAL FORM) MlValue defun(std::vector args, MlEnvironment &env) { - if (args.size() != 3) - throw MlError(MlValue("defun", defun), env, args.size() > 3 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + if (args.size() != 3) + throw MlError(MlValue("defun", defun), env, args.size() > 3 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - if (args[1].get_type_name() != LIST_TYPE) - throw MlError(MlValue("defun", defun), env, INVALID_LAMBDA); + if (args[1].get_type_name() != LIST_TYPE) + throw MlError(MlValue("defun", defun), env, INVALID_LAMBDA); - MlValue f = MlValue(args[1].as_list(), args[2], env); - env.set(args[0].display(), f); - return f; + MlValue f = MlValue(args[1].as_list(), args[2], env); + env.set(args[0].display(), f); + return f; } // Loop over a list of expressions with a condition (SPECIAL FORM) MlValue while_loop(std::vector args, MlEnvironment &env) { - MlValue acc; - while (args[0].eval(env).as_bool()) { - for (size_t i = 1; i < args.size() - 1; i++) - args[i].eval(env); - acc = args[args.size() - 1].eval(env); - } - return acc; + MlValue acc; + while (args[0].eval(env).as_bool()) { + for (size_t i = 1; i < args.size() - 1; i++) + args[i].eval(env); + acc = args[args.size() - 1].eval(env); + } + return acc; } // Iterate through a list of values in a list (SPECIAL FORM) MlValue for_loop(std::vector args, MlEnvironment &env) { - MlValue acc; - std::vector list = args[1].eval(env).as_list(); + MlValue acc; + std::vector list = args[1].eval(env).as_list(); - for (size_t i = 0; i < list.size(); i++) { - env.set(args[0].as_atom(), list[i]); + for (size_t i = 0; i < list.size(); i++) { + env.set(args[0].as_atom(), list[i]); - for (size_t j = 1; j < args.size() - 1; j++) - args[j].eval(env); - acc = args[args.size() - 1].eval(env); - } + for (size_t j = 1; j < args.size() - 1; j++) + args[j].eval(env); + acc = args[args.size() - 1].eval(env); + } - return acc; + return acc; } // Evaluate a block of expressions in the current environment (SPECIAL FORM) MlValue do_block(std::vector args, MlEnvironment &env) { - MlValue acc; - for (auto & arg : args) - acc = arg.eval(env); - return acc; + MlValue acc; + for (auto &arg : args) + acc = arg.eval(env); + return acc; } // Evaluate a block of expressions in a new environment (SPECIAL FORM) MlValue scope(std::vector args, MlEnvironment &env) { - MlEnvironment e = env; - MlValue acc; - for (auto & arg : args) - acc = arg.eval(e); - return acc; + MlEnvironment e = env; + MlValue acc; + for (auto &arg : args) + acc = arg.eval(e); + return acc; } // Quote an expression (SPECIAL FORM) MlValue quote(std::vector args, MlEnvironment &env) { - std::vector v; - for (const auto & arg : args) - v.push_back(arg); - return MlValue(v); + std::vector v; + for (const auto &arg : args) + v.push_back(arg); + return MlValue(v); } -#ifdef USE_STD // Exit the program with an integer code MlValue exit(std::vector args, MlEnvironment &env) { - // Is not a special form, so we can evaluate our args. - eval_args(args, env); + // Is not a special form, so we can evaluate our args. + eval_args(args, env); - std::exit(args.size() < 1 ? 0 : args[0].cast_to_int().as_int()); - return MlValue(); + std::exit(args.size() < 1 ? 0 : args[0].cast_to_int().as_int()); + return MlValue(); } // Print several values and return the last one MlValue print(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() < 1) - throw MlError(MlValue("print", print), env, TOO_FEW_ARGS); + if (args.size() < 1) + throw MlError(MlValue("print", print), env, TOO_FEW_ARGS); - MlValue acc; - for (size_t i = 0; i < args.size(); i++) { - acc = args[i]; - std::cout << acc.display(); - if (i < args.size() - 1) - std::cout << " "; - } - std::cout << std::endl; - return acc; + MlValue acc; + for (size_t i = 0; i < args.size(); i++) { + acc = args[i]; + std::cout << acc.display(); + if (i < args.size() - 1) + std::cout << " "; + } + std::cout << std::endl; + return acc; } // Get user input with an optional prompt MlValue input(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() > 1) - throw MlError(MlValue("input", input), env, TOO_MANY_ARGS); + if (args.size() > 1) + throw MlError(MlValue("input", input), env, TOO_MANY_ARGS); - if (!args.empty()) - std::cout << args[0]; + if (!args.empty()) + std::cout << args[0]; - std::string s; - std::getline(std::cin, s); - return MlValue::string(s); + std::string s; + std::getline(std::cin, s); + return MlValue::string(s); } // Get a random number between two numbers inclusively MlValue random(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 2) - throw MlError(MlValue("random", random), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + if (args.size() != 2) + throw MlError(MlValue("random", random), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - int low = args[0].as_int(), high = args[1].as_int(); - return MlValue(rand() % (high - low + 1) + low); + int low = args[0].as_int(), high = args[1].as_int(); + return MlValue(rand() % (high - low + 1) + low); } // Parse CSV string MlValue parse_csv(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - // TODO add support for more params specifying options - if (args.size() != 1) - throw MlError(MlValue("parse-csv", parse_csv), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + // TODO add support for more params specifying options + if (args.size() != 1) + throw MlError(MlValue("parse-csv", parse_csv), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - // PERF optimize it for memory usage and performance - CsvParser csv(true); - std::vector > parsed_data; // TODO some default size here - csv.parseCSV(args[0].as_string(), parsed_data); - - return csv.ivalualize(parsed_data); + CsvParser csv(true); + return csv.parseCSV(args[0].as_string()); } // Get the contents of a file MlValue read_file(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 1) - throw MlError(MlValue("read-file", read_file), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + if (args.size() != 1) + throw MlError(MlValue("read-file", read_file), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - return MlValue::string(read_file_contents(args[0].as_string())); + return MlValue::string(read_file_contents(args[0].as_string())); } // Write a string to a file MlValue write_file(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 2) - throw MlError(MlValue("write-file", write_file), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + if (args.size() != 2) + throw MlError(MlValue("write-file", write_file), env, + args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - std::ofstream f; - // The first argument is the file name - f.open(args[0].as_string().c_str()); - // The second argument is the contents of the file to write - MlValue result = MlValue((f << args[1].as_string()) ? 1 : 0); - f.close(); - return result; + std::ofstream f; + // The first argument is the file name + f.open(args[0].as_string().c_str()); + // The second argument is the contents of the file to write + MlValue result = MlValue((f << args[1].as_string()) ? 1 : 0); + f.close(); + return result; } // Read URL to (code content) MlValue read_url(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - // PERF optimize it for memory usage and performance - if (args.size() < 1 || args.size() > 2) - throw MlError(MlValue("read_url", read_url), env, args.size() < 1 ? TOO_FEW_ARGS : TOO_MANY_ARGS); + // PERF optimize it for memory usage and performance + if (args.size() < 1 || args.size() > 2) + throw MlError(MlValue("read_url", read_url), env, args.size() < 1 ? TOO_FEW_ARGS : TOO_MANY_ARGS); - std::unordered_map headers = {}; - HttpClient client; + std::unordered_map headers = {}; + HttpClient client; - if (args.size() == 2) { - for(const auto& pair_list: args[1].as_list()[0].as_list()) { - // TODO check its 2 string elements list - const auto& pair = pair_list.as_list(); - headers[pair[0].as_string()] = pair[1].as_string(); - } - } + if (args.size() == 2) { + for (const auto &pair_list: args[1].as_list()[0].as_list()) { + // TODO check its 2 string elements list + const auto &pair = pair_list.as_list(); + headers[pair[0].as_string()] = pair[1].as_string(); + } + } - std::pair result = client.doGetRequest(args[0].as_string(), headers); - // TODO add helper function for this - std::vector lst; - lst.push_back(MlValue(result.first)); - lst.push_back(MlValue::string(result.second)); - return lst; + std::pair result = client.doGetRequest(args[0].as_string(), headers); + // TODO add helper function for this + std::vector lst; + lst.push_back(MlValue(result.first)); + lst.push_back(MlValue::string(result.second)); + return lst; } // Parse JSON string MlValue read_json(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - // TODO add support for more params specifying options - if (args.size() != 1) - throw MlError(MlValue("read-json", read_json), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + // TODO add support for more params specifying options + if (args.size() != 1) + throw MlError(MlValue("read-json", read_json), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - std::string str = args[0].as_string(); - std::string err; - auto json = json11::Json::parse(str, err); + std::string str = args[0].as_string(); + std::string err; + auto json = json11::Json::parse(str, err); - if (!err.empty()) { - // TODO handle error - return MlValue::string("ERROR json parsing: " + err); - } + if (!err.empty()) { + // TODO handle error + return MlValue::string("ERROR json parsing: " + err); + } - return json.ivalualize(); + return json.ivalualize(); } // Execute system command MlValue system_cmd(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - // TODO add support for more params constructing options as one string - // TODO add support for stderr - if (args.size() != 1) - throw MlError(MlValue("system-cmd", system_cmd), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + // TODO add support for more params constructing options as one string + // TODO add support for stderr + if (args.size() != 1) + throw MlError(MlValue("system-cmd", system_cmd), env, + args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - std::string cmd = args[0].as_string(); + std::string cmd = args[0].as_string(); - std::string cmd_output; - int stat; + std::string cmd_output; + int stat; - // TODO better parameter here - // TODO handle reading of stderr - // https://stackoverflow.com/questions/478898/how-do-i-execute-a-command-and-get-the-output-of-the-command-within-c-using-po - // https://jineshkj.wordpress.com/2006/12/22/how-to-capture-stdin-stdout-and-stderr-of-child-program/ - char buffer[128]; - FILE *pipe = popen(cmd.c_str(), "r"); - if (!pipe) - throw std::runtime_error("popen() failed!"); - try { - while (!std::feof(pipe)) { - if (std::fgets(buffer, 128, pipe) != nullptr) - cmd_output += buffer; - } - } catch (...) { - stat = pclose(pipe); - throw; - } - stat = pclose(pipe); - int cmd_retval = WEXITSTATUS(stat); + // TODO better parameter here + // TODO handle reading of stderr + // https://stackoverflow.com/questions/478898/how-do-i-execute-a-command-and-get-the-output-of-the-command-within-c-using-po + // https://jineshkj.wordpress.com/2006/12/22/how-to-capture-stdin-stdout-and-stderr-of-child-program/ + char buffer[128]; + FILE *pipe = popen(cmd.c_str(), "r"); + if (!pipe) + throw std::runtime_error("popen() failed!"); + try { + while (!std::feof(pipe)) { + if (std::fgets(buffer, 128, pipe) != nullptr) + cmd_output += buffer; + } + } catch (...) { + stat = pclose(pipe); + throw; + } + stat = pclose(pipe); + int cmd_retval = WEXITSTATUS(stat); - // TODO add helper function for this - std::vector lst; - lst.push_back(MlValue(cmd_retval)); - lst.push_back(MlValue::string(cmd_output)); - return lst; + // TODO add helper function for this + std::vector lst; + lst.push_back(MlValue(cmd_retval)); + lst.push_back(MlValue::string(cmd_output)); + return lst; } + // list directory + MlValue ls_dir(std::vector args, MlEnvironment &env) { + eval_args(args, env); + + if (args.size() != 1) + throw MlError(MlValue("ls-dir", ls_dir), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + + return list_dir(args[0].as_string()); + } + + // is_path file + MlValue is_file(std::vector args, MlEnvironment &env) { + eval_args(args, env); + + // TODO add suppor for more params and list params + if (args.size() != 1) + throw MlError(MlValue("is-file?", is_file), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + + return MlValue(is_path_file(args[0].as_string())); + } + + // is_path directory + MlValue is_dir(std::vector args, MlEnvironment &env) { + eval_args(args, env); + + // TODO add suppor for more params and list params + if (args.size() != 1) + throw MlError(MlValue("is-dir?", is_dir), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + + return MlValue(is_path_dir(args[0].as_string())); + } // Read a file and execute its code MlValue include(std::vector args, MlEnvironment &env) { - // Import is technically not a special form, it's more of a macro. - // We can evaluate our arguments. - eval_args(args, env); + // Import is technically not a special form, it's more of a macro. + // We can evaluate our arguments. + eval_args(args, env); - if (args.size() != 1) - throw MlError(MlValue("include", include), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + if (args.size() != 1) + throw MlError(MlValue("include", include), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - MlEnvironment e; - MlValue result = run(read_file_contents(args[0].as_string()), e); - env.combine(e); - return result; + MlEnvironment e; + MlValue result = run(read_file_contents(args[0].as_string()), e); + env.combine(e); + return result; } -#endif // Evaluate a value as code MlValue eval(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 1) - throw MlError(MlValue("eval", eval), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - else return args[0].eval(env); + if (args.size() != 1) + throw MlError(MlValue("eval", eval), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + else return args[0].eval(env); } // Create a list of values MlValue list(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - return MlValue(args); + return MlValue(args); } // Sum multiple values MlValue sum(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() < 2) - throw MlError(MlValue("+", sum), env, TOO_FEW_ARGS); + if (args.size() < 2) + throw MlError(MlValue("+", sum), env, TOO_FEW_ARGS); - MlValue acc = args[0]; - for (size_t i = 1; i < args.size(); i++) - acc = acc + args[i]; - return acc; + MlValue acc = args[0]; + for (size_t i = 1; i < args.size(); i++) + acc = acc + args[i]; + return acc; } // Subtract two values MlValue subtract(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 2) - throw MlError(MlValue("-", subtract), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - return args[0] - args[1]; + if (args.size() != 2) + throw MlError(MlValue("-", subtract), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + return args[0] - args[1]; } // Multiply several values MlValue product(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() < 2) - throw MlError(MlValue("*", product), env, TOO_FEW_ARGS); + if (args.size() < 2) + throw MlError(MlValue("*", product), env, TOO_FEW_ARGS); - MlValue acc = args[0]; - for (size_t i = 1; i < args.size(); i++) - acc = acc * args[i]; - return acc; + MlValue acc = args[0]; + for (size_t i = 1; i < args.size(); i++) + acc = acc * args[i]; + return acc; } // Divide two values MlValue divide(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 2) - throw MlError(MlValue("/", divide), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - return args[0] / args[1]; + if (args.size() != 2) + throw MlError(MlValue("/", divide), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + return args[0] / args[1]; } // Get the remainder of values MlValue remainder(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 2) - throw MlError(MlValue("%", remainder), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - return args[0] % args[1]; + if (args.size() != 2) + throw MlError(MlValue("%", remainder), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + return args[0] % args[1]; } // Are two values equal? MlValue eq(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 2) - throw MlError(MlValue("=", eq), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - return MlValue(int(args[0] == args[1])); + if (args.size() != 2) + throw MlError(MlValue("=", eq), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + return MlValue(int(args[0] == args[1])); } // Are two values not equal? MlValue neq(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 2) - throw MlError(MlValue("!=", neq), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - return MlValue(int(args[0] != args[1])); + if (args.size() != 2) + throw MlError(MlValue("!=", neq), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + return MlValue(int(args[0] != args[1])); } // Is one number greater than another? MlValue greater(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 2) - throw MlError(MlValue(">", greater), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - return MlValue(int(args[0] > args[1])); + if (args.size() != 2) + throw MlError(MlValue(">", greater), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + return MlValue(int(args[0] > args[1])); } // Is one number less than another? MlValue less(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 2) - throw MlError(MlValue("<", less), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - return MlValue(int(args[0] < args[1])); + if (args.size() != 2) + throw MlError(MlValue("<", less), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + return MlValue(int(args[0] < args[1])); } // Is one number greater than or equal to another? MlValue greater_eq(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 2) - throw MlError(MlValue(">=", greater_eq), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - return MlValue(int(args[0] >= args[1])); + if (args.size() != 2) + throw MlError(MlValue(">=", greater_eq), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + return MlValue(int(args[0] >= args[1])); } // Is one number less than or equal to another? MlValue less_eq(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 2) - throw MlError(MlValue("<=", less_eq), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - return MlValue(int(args[0] <= args[1])); + if (args.size() != 2) + throw MlError(MlValue("<=", less_eq), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + return MlValue(int(args[0] <= args[1])); } // Get the type name of a value MlValue get_type_name(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 1) - throw MlError(MlValue("type", get_type_name), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + if (args.size() != 1) + throw MlError(MlValue("type", get_type_name), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - return MlValue::string(args[0].get_type_name()); + return MlValue::string(args[0].get_type_name()); } // Cast an item to a float MlValue cast_to_float(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 1) - throw MlError(MlValue(FLOAT_TYPE, cast_to_float), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - return args[0].cast_to_float(); + if (args.size() != 1) + throw MlError(MlValue(FLOAT_TYPE, cast_to_float), env, + args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + return args[0].cast_to_float(); } // Cast an item to an int MlValue cast_to_int(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 1) - throw MlError(MlValue(INT_TYPE, cast_to_int), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - return args[0].cast_to_int(); + if (args.size() != 1) + throw MlError(MlValue(INT_TYPE, cast_to_int), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + return args[0].cast_to_int(); } // Index a list MlValue index(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 2) - throw MlError(MlValue("index", index), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + if (args.size() != 2) + throw MlError(MlValue("index", index), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - std::vector list = args[0].as_list(); - int i = args[1].as_int(); - if (list.empty() || i >= list.size()) - throw MlError(list, env, INDEX_OUT_OF_RANGE); + std::vector list = args[0].as_list(); + int i = args[1].as_int(); + if (list.empty() || i >= list.size()) + throw MlError(list, env, INDEX_OUT_OF_RANGE); - return list[i]; + return list[i]; } // Insert a value into a list MlValue insert(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 3) - throw MlError(MlValue("insert", insert), env, args.size() > 3 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + if (args.size() != 3) + throw MlError(MlValue("insert", insert), env, args.size() > 3 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - std::vector list = args[0].as_list(); - int i = args[1].as_int(); - if (i > list.size()) - throw MlError(list, env, INDEX_OUT_OF_RANGE); + std::vector list = args[0].as_list(); + int i = args[1].as_int(); + if (i > list.size()) + throw MlError(list, env, INDEX_OUT_OF_RANGE); - list.insert(list.begin() + args[1].as_int(), args[2]); - return MlValue(list); + list.insert(list.begin() + args[1].as_int(), args[2]); + return MlValue(list); } // Remove a value at an index from a list MlValue remove(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 2) - throw MlError(MlValue("remove", remove), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + if (args.size() != 2) + throw MlError(MlValue("remove", remove), env, args.size() > 2 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - std::vector list = args[0].as_list(); - int i = args[1].as_int(); - if (list.empty() || i >= list.size()) - throw MlError(list, env, INDEX_OUT_OF_RANGE); + std::vector list = args[0].as_list(); + int i = args[1].as_int(); + if (list.empty() || i >= list.size()) + throw MlError(list, env, INDEX_OUT_OF_RANGE); - list.erase(list.begin() + i); - return MlValue(list); + list.erase(list.begin() + i); + return MlValue(list); } // Get the length of a list MlValue len(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 1) - throw MlError(MlValue("len", len), env, args.size() > 1 ? - TOO_MANY_ARGS : TOO_FEW_ARGS - ); + if (args.size() != 1) + throw MlError(MlValue("len", len), env, args.size() > 1 ? + TOO_MANY_ARGS : TOO_FEW_ARGS + ); - return MlValue(int(args[0].as_list().size())); + return MlValue(int(args[0].as_list().size())); } // Add an item to the end of a list MlValue push(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() == 0) - throw MlError(MlValue("push", push), env, TOO_FEW_ARGS); - for (size_t i = 1; i < args.size(); i++) - args[0].push(args[i]); - return args[0]; + if (args.size() == 0) + throw MlError(MlValue("push", push), env, TOO_FEW_ARGS); + for (size_t i = 1; i < args.size(); i++) + args[0].push(args[i]); + return args[0]; } MlValue pop(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 1) - throw MlError(MlValue("pop", pop), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - return args[0].pop(); + if (args.size() != 1) + throw MlError(MlValue("pop", pop), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + return args[0].pop(); } MlValue head(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 1) - throw MlError(MlValue("head", head), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - std::vector list = args[0].as_list(); - if (list.empty()) - throw MlError(MlValue("head", head), env, INDEX_OUT_OF_RANGE); + if (args.size() != 1) + throw MlError(MlValue("head", head), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + std::vector list = args[0].as_list(); + if (list.empty()) + throw MlError(MlValue("head", head), env, INDEX_OUT_OF_RANGE); - return list[0]; + return list[0]; } MlValue tail(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 1) - throw MlError(MlValue("tail", tail), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + if (args.size() != 1) + throw MlError(MlValue("tail", tail), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - std::vector result, list = args[0].as_list(); + std::vector result, list = args[0].as_list(); - for (size_t i = 1; i < list.size(); i++) - result.push_back(list[i]); + for (size_t i = 1; i < list.size(); i++) + result.push_back(list[i]); - return MlValue(result); + return MlValue(result); } MlValue parse(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 1) - throw MlError(MlValue("parse", parse), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - if (args[0].get_type_name() != STRING_TYPE) - throw MlError(args[0], env, INVALID_ARGUMENT); - std::vector parsed = ::parse(args[0].as_string()); + if (args.size() != 1) + throw MlError(MlValue("parse", parse), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + if (args[0].get_type_name() != STRING_TYPE) + throw MlError(args[0], env, INVALID_ARGUMENT); + std::vector parsed = ::parse(args[0].as_string()); - // if (parsed.size() == 1) - // return parsed[0]; - // else return MlValue(parsed); - return MlValue(parsed); + // if (parsed.size() == 1) + // return parsed[0]; + // else return MlValue(parsed); + return MlValue(parsed); } MlValue replace(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 3) - throw MlError(MlValue("replace", replace), env, args.size() > 3 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + if (args.size() != 3) + throw MlError(MlValue("replace", replace), env, args.size() > 3 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - std::string src = args[0].as_string(); - replace_substring(src, args[1].as_string(), args[2].as_string()); - return MlValue::string(src); + std::string src = args[0].as_string(); + replace_substring(src, args[1].as_string(), args[2].as_string()); + return MlValue::string(src); } MlValue display(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 1) - throw MlError(MlValue("display", display), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + if (args.size() != 1) + throw MlError(MlValue("display", display), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - return MlValue::string(args[0].display()); + return MlValue::string(args[0].display()); } MlValue debug(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - if (args.size() != 1) - throw MlError(MlValue("debug", debug), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); + if (args.size() != 1) + throw MlError(MlValue("debug", debug), env, args.size() > 1 ? TOO_MANY_ARGS : TOO_FEW_ARGS); - return MlValue::string(args[0].debug()); + return MlValue::string(args[0].debug()); } MlValue map_list(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - std::vector result, l = args[1].as_list(), tmp; - for (size_t i = 0; i < l.size(); i++) { - tmp.push_back(l[i]); - result.push_back(args[0].apply(tmp, env)); - tmp.clear(); - } - return MlValue(result); + std::vector result, l = args[1].as_list(), tmp; + for (size_t i = 0; i < l.size(); i++) { + tmp.push_back(l[i]); + result.push_back(args[0].apply(tmp, env)); + tmp.clear(); + } + return MlValue(result); } //>>> (filter (lambda (x) (> x 2)) '(1 2 3 4 5)) // => (3 4 5) MlValue filter_list(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - std::vector result, l = args[1].as_list(), tmp; - for (size_t i = 0; i < l.size(); i++) { - tmp.push_back(l[i]); - if (args[0].apply(tmp, env).as_bool()) - result.push_back(l[i]); - tmp.clear(); - } - return MlValue(result); + std::vector result, l = args[1].as_list(), tmp; + for (size_t i = 0; i < l.size(); i++) { + tmp.push_back(l[i]); + if (args[0].apply(tmp, env).as_bool()) + result.push_back(l[i]); + tmp.clear(); + } + return MlValue(result); } MlValue reduce_list(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - std::vector l = args[2].as_list(), tmp; - MlValue acc = args[1]; - for (size_t i = 0; i < l.size(); i++) { - tmp.push_back(acc); - tmp.push_back(l[i]); - acc = args[0].apply(tmp, env); - tmp.clear(); - } - return acc; + std::vector l = args[2].as_list(), tmp; + MlValue acc = args[1]; + for (size_t i = 0; i < l.size(); i++) { + tmp.push_back(acc); + tmp.push_back(l[i]); + acc = args[0].apply(tmp, env); + tmp.clear(); + } + return acc; } MlValue range(std::vector args, MlEnvironment &env) { - eval_args(args, env); + eval_args(args, env); - std::vector result; - MlValue low = args[0], high = args[1]; - if (low.get_type_name() != INT_TYPE && low.get_type_name() != FLOAT_TYPE) - throw MlError(low, env, MISMATCHED_TYPES); - if (high.get_type_name() != INT_TYPE && high.get_type_name() != FLOAT_TYPE) - throw MlError(high, env, MISMATCHED_TYPES); + std::vector result; + MlValue low = args[0], high = args[1]; + if (low.get_type_name() != INT_TYPE && low.get_type_name() != FLOAT_TYPE) + throw MlError(low, env, MISMATCHED_TYPES); + if (high.get_type_name() != INT_TYPE && high.get_type_name() != FLOAT_TYPE) + throw MlError(high, env, MISMATCHED_TYPES); - if (low >= high) return MlValue(result); + if (low >= high) return MlValue(result); - while (low < high) { - result.push_back(low); - low = low + MlValue(1); - } - return MlValue(result); + while (low < high) { + result.push_back(low); + low = low + MlValue(1); + } + return MlValue(result); } } void repl(MlEnvironment &env) { -#ifdef USE_STD - std::string code; - std::string input; - MlValue tmp; - std::vector parsed; - while (true) { - std::cout << ">>> "; - std::getline(std::cin, input); - if (input == "!quit" || input == "!q") - break; - else if (input == "!env" || input == "!e") - std::cout << env << std::endl; - else if (input == "!export" || input == "!x") { - std::cout << "File to export to: "; - std::getline(std::cin, input); + std::string code; + std::string input; + MlValue tmp; + std::vector parsed; + while (true) { + std::cout << ">>> "; + std::getline(std::cin, input); + if (input == "!quit" || input == "!q") + break; + else if (input == "!env" || input == "!e") + std::cout << env << std::endl; + else if (input == "!export" || input == "!x") { + std::cout << "File to export to: "; + std::getline(std::cin, input); - std::ofstream f; - f.open(input.c_str(), std::ofstream::out); - f << code; - f.close(); - } else if (input != "") { - try { - tmp = run(input, env); - std::cout << " => " << tmp.debug() << std::endl; - code += input + "\n"; - } catch (MlError &e) { - std::cerr << e.description() << std::endl; - } catch (std::runtime_error &e) { - std::cerr << e.what() << std::endl; - } - } - } -#endif + std::ofstream f; + f.open(input.c_str(), std::ofstream::out); + f << code; + f.close(); + } else if (input != "") { + try { + tmp = run(input, env); + std::cout << " => " << tmp.debug() << std::endl; + code += input + "\n"; + } catch (MlError &e) { + std::cerr << e.description() << std::endl; + } catch (std::runtime_error &e) { + std::cerr << e.what() << std::endl; + } + } + } } // Does this environment, or its parent environment, have a variable? bool MlEnvironment::has(std::string name) const { - // Find the value in the map - std::map::const_iterator itr = defs.find(name); - if (itr != defs.end()) - // If it was found - return true; - else if (parent_scope != nullptr) - // If it was not found in the current environment, - // try to find it in the parent environment - return parent_scope->has(name); - else return false; + // Find the value in the map + std::map::const_iterator itr = defs.find(name); + if (itr != defs.end()) + // If it was found + return true; + else if (parent_scope != nullptr) + // If it was not found in the current environment, + // try to find it in the parent environment + return parent_scope->has(name); + else return false; } // Get the value associated with this name in this scope -MlValue MlEnvironment::get(const std::string& name) const { - // Meta operations - if (name == "eval") return MlValue("eval", builtin::eval); - if (name == "type") return MlValue("type", builtin::get_type_name); - if (name == "parse") return MlValue("parse", builtin::parse); +MlValue MlEnvironment::get(const std::string &name) const { + // Meta operations + if (name == "eval") return MlValue("eval", builtin::eval); + if (name == "type") return MlValue("type", builtin::get_type_name); + if (name == "parse") return MlValue("parse", builtin::parse); - // Special forms - if (name == "do") return MlValue("do", builtin::do_block); - if (name == "if") return MlValue("if", builtin::if_then_else); - if (name == "for") return MlValue("for", builtin::for_loop); - if (name == "while") return MlValue("while", builtin::while_loop); - if (name == "scope") return MlValue("scope", builtin::scope); - if (name == "quote") return MlValue("quote", builtin::quote); - if (name == "defun") return MlValue("defun", builtin::defun); - if (name == "define") return MlValue("define", builtin::define); - if (name == "lambda") return MlValue("lambda", builtin::lambda); + // Special forms + if (name == "do") return MlValue("do", builtin::do_block); + if (name == "if") return MlValue("if", builtin::if_then_else); + if (name == "for") return MlValue("for", builtin::for_loop); + if (name == "while") return MlValue("while", builtin::while_loop); + if (name == "scope") return MlValue("scope", builtin::scope); + if (name == "quote") return MlValue("quote", builtin::quote); + if (name == "defun") return MlValue("defun", builtin::defun); + if (name == "define") return MlValue("define", builtin::define); + if (name == "lambda") return MlValue("lambda", builtin::lambda); - // Comparison operations - if (name == "=") return MlValue("=", builtin::eq); - if (name == "!=") return MlValue("!=", builtin::neq); - if (name == ">") return MlValue(">", builtin::greater); - if (name == "<") return MlValue("<", builtin::less); - if (name == ">=") return MlValue(">=", builtin::greater_eq); - if (name == "<=") return MlValue("<=", builtin::less_eq); + // Comparison operations + if (name == "=") return MlValue("=", builtin::eq); + if (name == "!=") return MlValue("!=", builtin::neq); + if (name == ">") return MlValue(">", builtin::greater); + if (name == "<") return MlValue("<", builtin::less); + if (name == ">=") return MlValue(">=", builtin::greater_eq); + if (name == "<=") return MlValue("<=", builtin::less_eq); - // Arithmetic operations - if (name == "+") return MlValue("+", builtin::sum); - if (name == "-") return MlValue("-", builtin::subtract); - if (name == "*") return MlValue("*", builtin::product); - if (name == "/") return MlValue("/", builtin::divide); - if (name == "%") return MlValue("%", builtin::remainder); + // Arithmetic operations + if (name == "+") return MlValue("+", builtin::sum); + if (name == "-") return MlValue("-", builtin::subtract); + if (name == "*") return MlValue("*", builtin::product); + if (name == "/") return MlValue("/", builtin::divide); + if (name == "%") return MlValue("%", builtin::remainder); - // List operations - if (name == "list") return MlValue("list", builtin::list); - if (name == "insert") return MlValue("insert", builtin::insert); - if (name == "index") return MlValue("index", builtin::index); - if (name == "remove") return MlValue("remove", builtin::remove); + // List operations + if (name == "list") return MlValue("list", builtin::list); + if (name == "insert") return MlValue("insert", builtin::insert); + if (name == "index") return MlValue("index", builtin::index); + if (name == "remove") return MlValue("remove", builtin::remove); - if (name == "len") return MlValue("len", builtin::len); + if (name == "len") return MlValue("len", builtin::len); - if (name == "push") return MlValue("push", builtin::push); - if (name == "pop") return MlValue("pop", builtin::pop); - if (name == "head") return MlValue("head", builtin::head); - if (name == "tail") return MlValue("tail", builtin::tail); - if (name == "first") return MlValue("first", builtin::head); - if (name == "last") return MlValue("last", builtin::pop); - if (name == "range") return MlValue("range", builtin::range); + if (name == "push") return MlValue("push", builtin::push); + if (name == "pop") return MlValue("pop", builtin::pop); + if (name == "head") return MlValue("head", builtin::head); + if (name == "tail") return MlValue("tail", builtin::tail); + if (name == "first") return MlValue("first", builtin::head); + if (name == "last") return MlValue("last", builtin::pop); + if (name == "range") return MlValue("range", builtin::range); - // Functional operations - if (name == "map") return MlValue("map", builtin::map_list); - if (name == "filter") return MlValue("filter", builtin::filter_list); - if (name == "reduce") return MlValue("reduce", builtin::reduce_list); + // Functional operations + if (name == "map") return MlValue("map", builtin::map_list); + if (name == "filter") return MlValue("filter", builtin::filter_list); + if (name == "reduce") return MlValue("reduce", builtin::reduce_list); - // IO operations -#ifdef USE_STD - if (name == "exit") return MlValue("exit", builtin::exit); - if (name == "quit") return MlValue("quit", builtin::exit); - if (name == "print") return MlValue("print", builtin::print); - if (name == "input") return MlValue("input", builtin::input); - if (name == "random") return MlValue("random", builtin::random); - if (name == "include") return MlValue("include", builtin::include); - if (name == "parse-csv") return MlValue("parse-csv", builtin::parse_csv); - if (name == "read-file") return MlValue("read-file", builtin::read_file); - if (name == "write-file") return MlValue("write-file", builtin::write_file); - if (name == "read-url") return MlValue("read-url", builtin::read_url); - if (name == "read-json") return MlValue("read-json", builtin::read_json); - if (name == "system-cmd") return MlValue("system-cmd", builtin::system_cmd); -#endif + // IO operations + if (name == "exit") return MlValue("exit", builtin::exit); + if (name == "quit") return MlValue("quit", builtin::exit); + if (name == "print") return MlValue("print", builtin::print); + if (name == "input") return MlValue("input", builtin::input); + if (name == "random") return MlValue("random", builtin::random); + if (name == "include") return MlValue("include", builtin::include); + if (name == "parse-csv") return MlValue("parse-csv", builtin::parse_csv); + if (name == "read-file") return MlValue("read-file", builtin::read_file); + if (name == "write-file") return MlValue("write-file", builtin::write_file); + if (name == "read-url") return MlValue("read-url", builtin::read_url); + if (name == "read-json") return MlValue("read-json", builtin::read_json); + if (name == "system-cmd") return MlValue("system-cmd", builtin::system_cmd); + if (name == "ls-dir") return MlValue("ls-dir", builtin::ls_dir); + if (name == "is-file?") return MlValue("is-file?", builtin::is_file); + if (name == "is-dir?") return MlValue("is-der?", builtin::is_dir); - // String operations - if (name == "debug") return MlValue("debug", builtin::debug); - if (name == "replace") return MlValue("replace", builtin::replace); - if (name == "display") return MlValue("display", builtin::display); + // String operations + if (name == "debug") return MlValue("debug", builtin::debug); + if (name == "replace") return MlValue("replace", builtin::replace); + if (name == "display") return MlValue("display", builtin::display); - // Casting operations - if (name == "int") return MlValue("int", builtin::cast_to_int); - if (name == "float") return MlValue("float", builtin::cast_to_float); + // Casting operations + if (name == "int") return MlValue("int", builtin::cast_to_int); + if (name == "float") return MlValue("float", builtin::cast_to_float); - // Constants - if (name == "endl") return MlValue::string("\n"); + // Constants + if (name == "endl") return MlValue::string("\n"); - std::map::const_iterator itr = defs.find(name); - if (itr != defs.end()) return itr->second; - else if (parent_scope != nullptr) { - itr = parent_scope->defs.find(name); - if (itr != parent_scope->defs.end()) return itr->second; - else return parent_scope->get(name); - } + std::map::const_iterator itr = defs.find(name); + if (itr != defs.end()) return itr->second; + else if (parent_scope != nullptr) { + itr = parent_scope->defs.find(name); + if (itr != parent_scope->defs.end()) return itr->second; + else return parent_scope->get(name); + } - throw MlError(MlValue::atom(name), *this, ATOM_NOT_DEFINED); + throw MlError(MlValue::atom(name), *this, ATOM_NOT_DEFINED); } // load lisp std library void load_std_lib(MlEnvironment &env) { - // TODO iterate through dir with files - std::string filename = "stdlib/stdlib.lisp"; + // TODO iterate through dir with files + std::string filename = "stdlib/stdlib.lisp"; - MlEnvironment e; - MlValue result = run(read_file_contents(filename), e); - env.combine(e); + MlEnvironment e; + MlValue result = run(read_file_contents(filename), e); + env.combine(e); } int main(int argc, const char **argv) { - MlEnvironment env; - std::vector args; - for (int i = 0; i < argc; i++) - args.push_back(MlValue::string(argv[i])); - env.set("cmd-args", MlValue(args)); + MlEnvironment env; + std::vector args; + for (int i = 0; i < argc; i++) + args.push_back(MlValue::string(argv[i])); + env.set("cmd-args", MlValue(args)); -#ifdef USE_STD - srand(time(NULL)); - try { - // Load std library - load_std_lib(env); + srand(time(NULL)); + try { + // Load std library + load_std_lib(env); - if (argc == 1 || (argc == 2 && std::string(argv[1]) == "-i")) - repl(env); - else if (argc == 3 && std::string(argv[1]) == "-c") - run(argv[2], env); - else if (argc == 3 && std::string(argv[1]) == "-f") - run(read_file_contents(argv[2]), env); - else std::cerr << "invalid arguments" << std::endl; - } catch (MlError &e) { - std::cerr << e.description() << std::endl; - } catch (std::runtime_error &e) { - std::cerr << e.what() << std::endl; - } -#else - if (argc == 3 && std::string(argv[1]) == "-c") - run(argv[2], env); -#endif + if (argc == 1 || (argc == 2 && std::string(argv[1]) == "-i")) + repl(env); + else if (argc == 3 && std::string(argv[1]) == "-c") + run(argv[2], env); + else if (argc == 3 && std::string(argv[1]) == "-f") + run(read_file_contents(argv[2]), env); + else std::cerr << "invalid arguments" << std::endl; + } catch (MlError &e) { + std::cerr << e.description() << std::endl; + } catch (std::runtime_error &e) { + std::cerr << e.what() << std::endl; + } - return 0; + return 0; } diff --git a/ml.h b/ml.h index 433c39b..cbf64cf 100644 --- a/ml.h +++ b/ml.h @@ -1,11 +1,5 @@ #pragma once -// Comment this define out to drop support for standard library functions. -// This allows the program to run without a runtime. -#define USE_STD - -// Comment this define out to drop support for libm functions -#define HAS_LIBM #include #include @@ -14,7 +8,7 @@ #include -// // Forward declaration for MlEnvironment class definition +// Forward declaration for MlEnvironment class definition class MlValue; @@ -29,19 +23,22 @@ public: // This is only used to determine which atoms to capture when // creating a lambda function. bool has(std::string name) const; + // Get the value associated with this name in this scope - MlValue get(const std::string& name) const; + MlValue get(const std::string &name) const; + // Set the value associated with this name in this scope void set(std::string name, MlValue value); void combine(MlEnvironment const &other); void set_parent_scope(MlEnvironment *parent) { - parent_scope = parent; + parent_scope = parent; } // Output this scope in readable form to a stream. friend std::ostream &operator<<(std::ostream &os, MlEnvironment const &v); + private: // The definitions in the scope. @@ -50,20 +47,21 @@ private: }; - - // An exception thrown by the lisp class MlError { public: // Create an error with the value that caused the error, // the scope where the error was found, and the message. MlError(const MlValue &v, MlEnvironment const &env, const char *msg); + // Copy constructor is needed to prevent double frees MlError(MlError const &other); + ~MlError(); // Get the printable error description. std::string description(); + private: MlValue *cause; MlEnvironment env; @@ -71,142 +69,144 @@ private: }; - // The type for a builtin function, which takes a list of values, // and the environment to run the function in. typedef MlValue (*Builtin)(std::vector, MlEnvironment &); - class MlValue { - public: +class MlValue { +public: - // Constructs a unit value - MlValue(); + // Constructs a unit value + MlValue(); - // Constructs an integer - MlValue(int i); - // Constructs a floating point value - MlValue(double f); - // Constructs a list - MlValue(const std::vector &list); + // Constructs an integer + MlValue(int i); - // Construct a quoted value - static MlValue quote(const MlValue& quoted); + // Constructs a floating point value + MlValue(double f); - // Construct an atom - static MlValue atom(const std::string &s); + // Constructs a list + MlValue(const std::vector &list); - // Construct a string - static MlValue string(const std::string &s); + // Construct a quoted value + static MlValue quote(const MlValue "ed); - // Construct a lambda function - MlValue(const std::vector ¶ms, MlValue ret, MlEnvironment const &env); + // Construct an atom + static MlValue atom(const std::string &s); - // Construct a builtin function - MlValue(const std::string &name, Builtin b); + // Construct a string + static MlValue string(const std::string &s); - std::vector get_used_atoms(); + // Construct a lambda function + MlValue(const std::vector ¶ms, MlValue ret, MlEnvironment const &env); - // Is this a builtin function? - bool is_builtin(); + // Construct a builtin function + MlValue(const std::string &name, Builtin b); - // Apply this as a function to a list of arguments in a given environment. - MlValue apply(std::vector args, MlEnvironment &env); - // Evaluate this value as lisp code. - MlValue eval(MlEnvironment &env); + std::vector get_used_atoms(); - bool is_number() const; + // Is this a builtin function? + bool is_builtin(); - // Get the "truthy" boolean value of this value. - bool as_bool() const; + // Apply this as a function to a list of arguments in a given environment. + MlValue apply(std::vector args, MlEnvironment &env); - // Get this item's integer value - int as_int() const; + // Evaluate this value as lisp code. + MlValue eval(MlEnvironment &env); - // Get this item's floating point value - double as_float() const; + bool is_number() const; - // Get this item's string value - std::string as_string() const; + // Get the "truthy" boolean value of this value. + bool as_bool() const; - // Get this item's atom value - std::string as_atom() const; + // Get this item's integer value + int as_int() const; - // Get this item's list value - std::vector as_list() const; + // Get this item's floating point value + double as_float() const; - // Push an item to the end of this list - void push(MlValue val); + // Get this item's string value + std::string as_string() const; - // Push an item from the end of this list - MlValue pop(); + // Get this item's atom value + std::string as_atom() const; + + // Get this item's list value + std::vector as_list() const; + + // Push an item to the end of this list + void push(MlValue val); + + // Push an item from the end of this list + MlValue pop(); - // Cast this to an integer value - MlValue cast_to_int() const; + // Cast this to an integer value + MlValue cast_to_int() const; - // Cast this to a floating point value - MlValue cast_to_float() const; + // Cast this to a floating point value + MlValue cast_to_float() const; - bool operator==(MlValue other) const; + bool operator==(MlValue other) const; - bool operator!=(const MlValue &other) const; + bool operator!=(const MlValue &other) const; - bool operator>=(const MlValue &other) const; + bool operator>=(const MlValue &other) const; - bool operator<=(const MlValue &other) const; + bool operator<=(const MlValue &other) const; - bool operator>(const MlValue &other) const; + bool operator>(const MlValue &other) const; - bool operator<(const MlValue &other) const; + bool operator<(const MlValue &other) const; - // This function adds two lisp values, and returns the lisp value result. - MlValue operator+(const MlValue &other) const; + // This function adds two lisp values, and returns the lisp value result. + MlValue operator+(const MlValue &other) const; - // This function subtracts two lisp values, and returns the lisp value result. - MlValue operator-(const MlValue &other) const; + // This function subtracts two lisp values, and returns the lisp value result. + MlValue operator-(const MlValue &other) const; - // This function multiplies two lisp values, and returns the lisp value result. - MlValue operator*(const MlValue &other) const; + // This function multiplies two lisp values, and returns the lisp value result. + MlValue operator*(const MlValue &other) const; - // This function divides two lisp values, and returns the lisp value result. - MlValue operator/(const MlValue &other) const; + // This function divides two lisp values, and returns the lisp value result. + MlValue operator/(const MlValue &other) const; - // This function finds the remainder of two lisp values, and returns the lisp value result. - MlValue operator%(const MlValue &other) const; + // This function finds the remainder of two lisp values, and returns the lisp value result. + MlValue operator%(const MlValue &other) const; - // Get the name of the type of this value - std::string get_type_name(); + // Get the name of the type of this value + std::string get_type_name(); - std::string display() const; + std::string display() const; - std::string debug() const; + std::string debug() const; - friend std::ostream &operator<<(std::ostream &os, MlValue const &v); + friend std::ostream &operator<<(std::ostream &os, MlValue const &v); - private: - enum { - QUOTE, - ATOM, - INT, - FLOAT, - LIST, - STRING, - LAMBDA, - BUILTIN, - UNIT - } type; +private: + enum { + QUOTE, + ATOM, + INT, + FLOAT, + LIST, + STRING, + LAMBDA, + BUILTIN, + UNIT + } type; - union { - int i; - double f; - Builtin b; - } stack_data; + union { + int i; + double f; + Builtin b; + } stack_data; - std::string str; - std::vector list; - MlEnvironment lambda_scope; - }; \ No newline at end of file + std::string str; + std::vector list; + MlEnvironment lambda_scope; +}; \ No newline at end of file diff --git a/ml_io.cpp b/ml_io.cpp new file mode 100644 index 0000000..8af76cb --- /dev/null +++ b/ml_io.cpp @@ -0,0 +1,62 @@ + +#include "ml_io.h" + +#include +#include +#include + + +std::string read_file_contents(const std::string &filename) { + std::ifstream f; + f.open(filename.c_str()); + if (!f) + throw std::runtime_error("could not open file"); + + f.seekg(0, std::ios::end); + std::string contents; + contents.reserve(f.tellg()); + f.seekg(0, std::ios::beg); + contents.assign(std::istreambuf_iterator(f), + std::istreambuf_iterator()); + f.close(); + + return contents; +} + +MlValue list_dir(const std::string &path) { + std::vector entries; + + DIR *dirp = opendir(path.c_str()); + if (dirp == NULL) { + // handle error - probably not a dir or non existing path + } + struct dirent *dp; + while ((dp = readdir(dirp)) != NULL) { + entries.push_back(MlValue::string(dp->d_name)); + } + closedir(dirp); + + return MlValue(entries); +} + +bool is_path_file(const std::string &path) { + std::ifstream ifile(path.c_str()); + return (bool) ifile; +} + +bool is_path_dir(const std::string &path) { + struct stat buf; + stat(path.c_str(), &buf); + return (bool) S_ISDIR(buf.st_mode); +} + +int mk_dir(const std::string &path) { + int r = ::mkdir(path.c_str(), 0755); + return r; +} + +int rm_dir(const std::string &path) { + int r = ::rmdir(path.c_str()); + return r; +} + diff --git a/ml_io.h b/ml_io.h new file mode 100644 index 0000000..e4e1324 --- /dev/null +++ b/ml_io.h @@ -0,0 +1,21 @@ +#pragma once + +#include "../ml.h" + +#include +#include +#include +#include + +std::string read_file_contents(const std::string &filename); + +MlValue list_dir(const std::string &path); + +bool is_path_file(const std::string &path); + +bool is_path_dir(const std::string &path); + +int mk_dir(const std::string &path); + +int rm_dir(const std::string &path); +