21#include <fmt/format.h>
34 static constexpr std::array<int, 12>
daysInMonth = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
35 static constexpr std::array<const char*, 12>
monthNames = {
36 "January",
"February",
"March",
"April",
"May",
"June",
37 "July",
"August",
"September",
"October",
"November",
"December"
40 "Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
41 "Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
44 "Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday"
49 return ( aYear % 4 == 0 && aYear % 100 != 0 ) || ( aYear % 400 == 0 );
66 static auto DaysToYmd(
int aDaysSinceEpoch ) -> std::tuple<int, int, int>
69 int remainingDays = aDaysSinceEpoch;
71 if( remainingDays >= 0 )
81 while( remainingDays < 0 )
95 int day = remainingDays + 1;
96 return {year, month, day};
99 static auto YmdToDays(
int aYear,
int aMonth,
int aDay ) ->
int
114 for(
int m = 1; m < aMonth; ++m )
117 totalDays += aDay - 1;
121 static auto ParseDate(
const std::string& aDateStr ) -> std::optional<int>
123 std::istringstream iss( aDateStr );
125 std::vector<int> parts;
128 bool isCjkFormat =
false;
131 bool hasChineseYear = aDateStr.find(
"年" ) != std::string::npos;
132 bool hasChineseMonth = aDateStr.find(
"月" ) != std::string::npos;
133 bool hasChineseDay = aDateStr.find(
"日" ) != std::string::npos;
134 bool hasKoreanYear = aDateStr.find(
"년" ) != std::string::npos;
135 bool hasKoreanMonth = aDateStr.find(
"월" ) != std::string::npos;
136 bool hasKoreanDay = aDateStr.find(
"일" ) != std::string::npos;
139 if( (hasChineseYear || hasKoreanYear) &&
140 (hasChineseMonth || hasKoreanMonth) &&
141 (hasChineseDay || hasKoreanDay) )
146 size_t yearPos, monthPos, dayPos;
150 yearPos = aDateStr.find(
"年" );
152 yearPos = aDateStr.find(
"년" );
155 if( hasChineseMonth )
156 monthPos = aDateStr.find(
"月" );
158 monthPos = aDateStr.find(
"월" );
162 dayPos = aDateStr.find(
"日" );
164 dayPos = aDateStr.find(
"일" );
168 int year = std::stoi( aDateStr.substr( 0, yearPos ) );
169 int month = std::stoi( aDateStr.substr( yearPos + 3, monthPos - yearPos - 3 ) );
170 int day = std::stoi( aDateStr.substr( monthPos + 3, dayPos - monthPos - 3 ) );
172 parts = { year, month, day };
179 else if( aDateStr.find(
'-' ) != std::string::npos )
181 else if( aDateStr.find(
'/' ) != std::string::npos )
183 else if( aDateStr.find(
'.' ) != std::string::npos )
188 while( std::getline( iss, token, separator ) )
192 parts.push_back( std::stoi( token ) );
200 else if( !isCjkFormat && aDateStr.length() == 8 )
204 int dateNum = std::stoi( aDateStr );
205 int year = dateNum / 10000;
206 int month = ( dateNum / 100 ) % 100;
207 int day = dateNum % 100;
215 else if( !isCjkFormat )
220 if( parts.empty() || parts.size() > 3 )
223 int year, month, day;
225 if( parts.size() == 1 )
231 else if( parts.size() == 2 )
246 else if( separator ==
'/' && parts[0] <= 12 && parts[1] <= 31 )
252 else if( separator ==
'/' && parts[1] <= 12 )
266 if( month < 1 || month > 12 )
274 static auto FormatDate(
int aDaysSinceEpoch,
const std::string& aFormat ) -> std::string
276 auto [year, month, day] =
DaysToYmd( aDaysSinceEpoch );
278 if( aFormat ==
"ISO" || aFormat ==
"iso" )
279 return fmt::format(
"{:04d}-{:02d}-{:02d}", year, month, day );
280 else if( aFormat ==
"US" || aFormat ==
"us" )
281 return fmt::format(
"{:02d}/{:02d}/{:04d}", month, day, year );
282 else if( aFormat ==
"EU" || aFormat ==
"european" )
283 return fmt::format(
"{:02d}/{:02d}/{:04d}", day, month, year );
284 else if( aFormat ==
"long" )
285 return fmt::format(
"{} {}, {}",
monthNames[month-1], day, year );
286 else if( aFormat ==
"short" )
287 return fmt::format(
"{} {}, {}",
monthAbbrev[month-1], day, year );
288 else if( aFormat ==
"Chinese" || aFormat ==
"chinese" || aFormat ==
"CN" || aFormat ==
"cn" || aFormat ==
"中文" )
289 return fmt::format(
"{}年{:02d}月{:02d}日", year, month, day );
290 else if( aFormat ==
"Japanese" || aFormat ==
"japanese" || aFormat ==
"JP" || aFormat ==
"jp" || aFormat ==
"日本語" )
291 return fmt::format(
"{}年{:02d}月{:02d}日", year, month, day );
292 else if( aFormat ==
"Korean" || aFormat ==
"korean" || aFormat ==
"KR" || aFormat ==
"kr" || aFormat ==
"한국어" )
293 return fmt::format(
"{}년 {:02d}월 {:02d}일", year, month, day );
295 return fmt::format(
"{:04d}-{:02d}-{:02d}", year, month, day );
300 int weekday = ( ( aDaysSinceEpoch + 3 ) % 7 );
310 auto now = std::chrono::system_clock::now();
311 auto timeT = std::chrono::system_clock::to_time_t( now );
312 return static_cast<int>( timeT / ( 24 * 3600 ) );
317 auto now = std::chrono::system_clock::now();
318 auto timeT = std::chrono::system_clock::to_time_t( now );
319 return static_cast<double>( timeT );
341 const auto& varName = std::get<std::string>( aNode.data );
347 return MakeError<Value>( fmt::format(
"No variable resolver configured for: {}", varName ) );
352 const auto& binop = std::get<BIN_OP_DATA>( aNode.data );
353 auto leftResult = binop.left->Accept( *
this );
357 auto rightResult = binop.right ?
363 if( binop.op ==
'+' )
365 const auto& leftVal = leftResult.GetValue();
366 const auto& rightVal = rightResult.GetValue();
369 if( std::holds_alternative<std::string>( leftVal ) ||
370 std::holds_alternative<std::string>( rightVal ) )
382 const auto& func = std::get<FUNC_DATA>( aNode.data );
393 const auto&
name = aFunc.name;
394 const auto& args = aFunc.args;
399 if(
name ==
"today" )
401 else if(
name ==
"now" )
403 else if(
name ==
"random" )
405 std::uniform_real_distribution<double> dis( 0.0, 1.0 );
411 std::vector<Value> argValues;
412 argValues.reserve( args.size() );
414 for(
const auto& arg : args )
416 auto result = arg->Accept( *
this );
420 argValues.push_back(
result.GetValue() );
423 const auto argc = argValues.size();
426 if(
name ==
"format" && argc >= 1 )
432 const auto value = numResult.GetValue();
438 decimals =
static_cast<int>( decResult.GetValue() );
443 else if(
name ==
"currency" && argc >= 1 )
449 const auto amount = numResult.GetValue();
454 else if(
name ==
"fixed" && argc >= 1 )
460 const auto value = numResult.GetValue();
466 decimals =
static_cast<int>( decResult.GetValue() );
473 else if(
name ==
"dateformat" && argc >= 1 )
479 const auto days =
static_cast<int>( dateResult.GetValue() );
484 else if(
name ==
"datestring" && argc == 1 )
494 else if(
name ==
"weekdayname" && argc == 1 )
500 const auto days =
static_cast<int>( dateResult.GetValue() );
505 else if(
name ==
"upper" && argc == 1 )
508 std::transform( str.begin(), str.end(), str.begin(), ::toupper );
511 else if(
name ==
"lower" && argc == 1 )
514 std::transform( str.begin(), str.end(), str.begin(), ::tolower );
517 else if(
name ==
"concat" && argc >= 2 )
520 for(
const auto& val : argValues )
525 else if(
name ==
"beforefirst" && argc == 2 )
532 else if(
name ==
"beforelast" && argc == 2 )
539 else if(
name ==
"afterfirst" && argc == 2 )
546 else if(
name ==
"afterlast" && argc == 2 )
555 if(
name ==
"if" && argc == 3 )
559 if( !conditionResult )
562 const auto condition = conditionResult.GetValue() != 0.0;
567 std::vector<double> numArgs;
568 for(
const auto& val : argValues )
574 numArgs.push_back( numResult.GetValue() );
578 if(
name ==
"abs" && argc == 1 )
580 else if(
name ==
"sum" && argc >= 1 )
581 return MakeValue<Value>( std::accumulate( numArgs.begin(), numArgs.end(), 0.0 ) );
582 else if(
name ==
"round" && argc >= 1 )
584 const auto value = numArgs[0];
585 const auto precision = argc > 1 ?
static_cast<int>( numArgs[1] ) : 0;
586 const auto multiplier = std::pow( 10.0, precision );
589 else if(
name ==
"sqrt" && argc == 1 )
596 else if(
name ==
"pow" && argc == 2 )
598 else if(
name ==
"floor" && argc == 1 )
600 else if(
name ==
"ceil" && argc == 1 )
602 else if(
name ==
"min" && argc >= 1 )
603 return MakeValue<Value>( *std::min_element( numArgs.begin(), numArgs.end() ) );
604 else if(
name ==
"max" && argc >= 1 )
605 return MakeValue<Value>( *std::max_element( numArgs.begin(), numArgs.end() ) );
606 else if(
name ==
"avg" && argc >= 1 )
608 const auto sum = std::accumulate( numArgs.begin(), numArgs.end(), 0.0 );
616 -> std::pair<std::string, bool>
620 EVAL_VISITOR evaluator{ std::move( aVariableCallback ), localErrors };
621 bool hadErrors = aDoc.HasErrors();
623 for(
const auto& node : aDoc.GetNodes() )
628 result += std::get<std::string>( node->data );
633 const auto& calcData = std::get<BIN_OP_DATA>( node->data );
634 auto evalResult = calcData.left->Accept( evaluator );
648 result +=
"[Unknown node type]";
654 return { std::move(
result ), hadErrors || localErrors.HasErrors() };
658 -> std::tuple<std::string, std::vector<std::string>,
bool>
660 auto [
result, hadErrors] =
Process( aDoc, std::move( aVariableCallback ) );
661 auto allErrors = aDoc.GetErrors();
663 return { std::move(
result ), std::move( allErrors ), hadErrors };
static auto GetCurrentDays() -> int
static constexpr std::array< const char *, 12 > monthNames
static auto GetWeekdayName(int aDaysSinceEpoch) -> std::string
static auto FormatDate(int aDaysSinceEpoch, const std::string &aFormat) -> std::string
static constexpr std::array< const char *, 12 > monthAbbrev
static auto YmdToDays(int aYear, int aMonth, int aDay) -> int
static auto DaysToYmd(int aDaysSinceEpoch) -> std::tuple< int, int, int >
static auto ParseDate(const std::string &aDateStr) -> std::optional< int >
static auto daysInYear(int aYear) -> int
static constexpr int epochYear
static constexpr std::array< int, 12 > daysInMonth
static auto isLeapYear(int aYear) -> bool
static constexpr std::array< const char *, 7 > weekdayNames
static auto daysInMonthForYear(int aMonth, int aYear) -> int
static auto GetCurrentTimestamp() -> double
static auto ProcessWithDetails(const DOC &aDoc, VariableCallback aVariableCallback) -> std::tuple< std::string, std::vector< std::string >, bool >
Process document with detailed error reporting.
EVAL_VISITOR::VariableCallback VariableCallback
static auto Process(const DOC &aDoc, VariableCallback aVariableCallback) -> std::pair< std::string, bool >
Process document using callback for variable resolution.
auto operator()(const NODE &aNode) const -> Result< Value >
VariableCallback m_variableCallback
std::function< Result< Value >(const std::string &aVariableName)> VariableCallback
EVAL_VISITOR(VariableCallback aVariableCallback, ERROR_COLLECTOR &aErrorCollector)
Construct evaluator with variable callback function.
auto evaluateFunction(const FUNC_DATA &aFunc) const -> Result< Value >
ERROR_COLLECTOR & m_errors
static auto ArithmeticOp(const Value &aLeft, const Value &aRight, char aOp) -> Result< Value >
static auto ToString(const Value &aVal) -> std::string
static auto ToDouble(const Value &aVal) -> Result< double >
static auto ConcatStrings(const Value &aLeft, const Value &aRight) -> Value
static auto ToChar(const Value &aVal) -> char
thread_local ERROR_COLLECTOR * g_errorCollector
auto MakeValue(T aVal) -> Result< T >
auto MakeError(std::string aMsg) -> Result< T >
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
wxString result
Test unit parsing edge cases and error handling.