You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

464 lines
15 KiB

2 months ago
2 months ago
2 months ago
2 months ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
  5. *
  6. * This program is free software: you can redistribute it and/or modify it
  7. * under the terms of the GNU General Public License as published by the
  8. * Free Software Foundation, either version 3 of the License, or (at your
  9. * option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful, but
  12. * WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License along
  17. * with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #pragma once
  20. #include <fast_float/fast_float.h>
  21. #include <kicommon.h>
  22. #include <text_eval/text_eval_types.h>
  23. #include <iostream>
  24. #include <string>
  25. #include <memory>
  26. #include <vector>
  27. #include <variant>
  28. #include <concepts>
  29. #include <ranges>
  30. #include <fmt/format.h>
  31. #include <optional>
  32. #include <cassert>
  33. #include <cmath>
  34. #include <chrono>
  35. #include <random>
  36. #include <numeric>
  37. #include <algorithm>
  38. #include <sstream>
  39. #include <iomanip>
  40. #include <unordered_map>
  41. #include <functional>
  42. #include <cstring>
  43. #ifndef M_PI
  44. #define M_PI 3.14159265358979323846
  45. #endif
  46. namespace calc_parser
  47. {
  48. using Value = std::variant<double, std::string>;
  49. // Simple token type for parser compatibility
  50. struct TOKEN_TYPE
  51. {
  52. char text[256]; // Fixed size buffer for strings
  53. double dValue; // Numeric value
  54. bool isString; // Flag to indicate if this is a string token
  55. };
  56. // Helper functions for TOKEN_TYPE
  57. inline TOKEN_TYPE MakeStringToken(const std::string& str)
  58. {
  59. TOKEN_TYPE token;
  60. token.dValue = 0.0;
  61. token.isString = true;
  62. strncpy(token.text, str.c_str(), sizeof(token.text) - 1);
  63. token.text[sizeof(token.text) - 1] = '\0';
  64. return token;
  65. }
  66. inline TOKEN_TYPE MakeNumberToken(double val)
  67. {
  68. TOKEN_TYPE token;
  69. token.dValue = val;
  70. token.isString = false;
  71. token.text[0] = '\0';
  72. return token;
  73. }
  74. inline std::string GetTokenString(const TOKEN_TYPE& token)
  75. {
  76. return std::string(token.text);
  77. }
  78. inline double GetTokenDouble(const TOKEN_TYPE& token)
  79. {
  80. return token.dValue;
  81. }
  82. // Value utilities for type handling
  83. class VALUE_UTILS
  84. {
  85. public:
  86. // Convert Value to double (for arithmetic operations)
  87. static auto ToDouble( const Value& aVal ) -> Result<double>
  88. {
  89. if( std::holds_alternative<double>( aVal ) )
  90. return MakeValue( std::get<double>( aVal ) );
  91. const auto& str = std::get<std::string>( aVal );
  92. try
  93. {
  94. double value;
  95. auto result = fast_float::from_chars( str.data(), str.data() + str.size(), value );
  96. if( result.ec != std::errc() || result.ptr != str.data() + str.size() )
  97. throw std::invalid_argument( "Invalid number format" );
  98. return MakeValue( value );
  99. }
  100. catch( ... )
  101. {
  102. return MakeError<double>( fmt::format( "Cannot convert '{}' to number", str ) );
  103. }
  104. }
  105. // Convert Value to string (for display/concatenation)
  106. static auto ToString( const Value& aVal ) -> std::string
  107. {
  108. if( std::holds_alternative<std::string>( aVal ) )
  109. return std::get<std::string>( aVal );
  110. const auto num = std::get<double>( aVal );
  111. // Smart number formatting with tolerance for floating-point precision
  112. constexpr double tolerance = 1e-10;
  113. double rounded = std::round( num );
  114. // If the number is very close to a whole number, treat it as such
  115. if( std::abs( num - rounded ) < tolerance && std::abs( rounded ) < 1e15 )
  116. return fmt::format( "{:.0f}", rounded );
  117. return fmt::format( "{}", num );
  118. }
  119. static auto ToChar( const Value& aVal ) -> char
  120. {
  121. std::string str = ToString( aVal );
  122. if( str.empty() )
  123. return ' ';
  124. else
  125. return str[0];
  126. }
  127. // Check if Value represents a "truthy" value for conditionals
  128. static auto IsTruthy( const Value& aVal ) -> bool
  129. {
  130. if( std::holds_alternative<double>( aVal ) )
  131. return std::get<double>( aVal ) != 0.0;
  132. return !std::get<std::string>( aVal ).empty();
  133. }
  134. // arithmetic operation with type coercion
  135. static auto ArithmeticOp( const Value& aLeft, const Value& aRight, char aOp ) -> Result<Value>
  136. {
  137. auto leftNum = ToDouble( aLeft );
  138. auto rightNum = ToDouble( aRight );
  139. if( !leftNum ) return MakeError<Value>( leftNum.GetError() );
  140. if( !rightNum ) return MakeError<Value>( rightNum.GetError() );
  141. const auto leftVal = leftNum.GetValue();
  142. const auto rightVal = rightNum.GetValue();
  143. switch( aOp )
  144. {
  145. case '+': return MakeValue<Value>( leftVal + rightVal );
  146. case '-': return MakeValue<Value>( leftVal - rightVal );
  147. case '*': return MakeValue<Value>( leftVal * rightVal );
  148. case '/':
  149. if( rightVal == 0.0 )
  150. return MakeError<Value>( "Division by zero" );
  151. return MakeValue<Value>( leftVal / rightVal );
  152. case '%':
  153. if( rightVal == 0.0 )
  154. return MakeError<Value>( "Modulo by zero" );
  155. return MakeValue<Value>( std::fmod( leftVal, rightVal ) );
  156. case '^': return MakeValue<Value>( std::pow( leftVal, rightVal ) );
  157. case '<': return MakeValue<Value>( leftVal < rightVal ? 1.0 : 0.0 );
  158. case '>': return MakeValue<Value>( leftVal > rightVal ? 1.0 : 0.0 );
  159. case 1: return MakeValue<Value>( leftVal <= rightVal ? 1.0 : 0.0 ); // <=
  160. case 2: return MakeValue<Value>( leftVal >= rightVal ? 1.0 : 0.0 ); // >=
  161. case 3: return MakeValue<Value>( leftVal == rightVal ? 1.0 : 0.0 ); // ==
  162. case 4: return MakeValue<Value>( leftVal != rightVal ? 1.0 : 0.0 ); // !=
  163. default:
  164. return MakeError<Value>( "Unknown operator" );
  165. }
  166. }
  167. // String concatenation (special case of '+' for strings)
  168. static auto ConcatStrings( const Value& aLeft, const Value& aRight ) -> Value
  169. {
  170. return Value{ ToString( aLeft ) + ToString( aRight ) };
  171. }
  172. };
  173. class NODE;
  174. class DOC;
  175. class PARSE_CONTEXT;
  176. // AST Node types - supporting mixed values
  177. enum class NodeType { Text, Calc, Var, Number, String, BinOp, Function };
  178. struct BIN_OP_DATA
  179. {
  180. std::unique_ptr<NODE> left;
  181. std::unique_ptr<NODE> right;
  182. char op;
  183. BIN_OP_DATA( std::unique_ptr<NODE> aLeft, char aOperation, std::unique_ptr<NODE> aRight ) :
  184. left( std::move( aLeft ) ),
  185. right( std::move( aRight ) ),
  186. op( aOperation )
  187. {}
  188. };
  189. struct FUNC_DATA
  190. {
  191. std::string name;
  192. std::vector<std::unique_ptr<NODE>> args;
  193. FUNC_DATA( std::string aName, std::vector<std::unique_ptr<NODE>> aArguments ) :
  194. name( std::move( aName ) ),
  195. args( std::move( aArguments ) )
  196. {}
  197. };
  198. class NODE
  199. {
  200. public:
  201. NodeType type;
  202. std::variant<std::string, double, BIN_OP_DATA, FUNC_DATA> data;
  203. // Factory methods for type safety
  204. static auto CreateText( std::string aText ) -> std::unique_ptr<NODE>
  205. {
  206. auto node = std::make_unique<NODE>();
  207. node->type = NodeType::Text;
  208. node->data = std::move( aText );
  209. return node;
  210. }
  211. static auto CreateCalc( std::unique_ptr<NODE> aExpr ) -> std::unique_ptr<NODE>
  212. {
  213. auto node = std::make_unique<NODE>();
  214. node->type = NodeType::Calc;
  215. node->data = BIN_OP_DATA( std::move( aExpr ), '=', nullptr );
  216. return node;
  217. }
  218. static auto CreateVar( std::string aName ) -> std::unique_ptr<NODE>
  219. {
  220. auto node = std::make_unique<NODE>();
  221. node->type = NodeType::Var;
  222. node->data = std::move( aName );
  223. return node;
  224. }
  225. static auto CreateNumber( double aValue ) -> std::unique_ptr<NODE>
  226. {
  227. auto node = std::make_unique<NODE>();
  228. node->type = NodeType::Number;
  229. node->data = aValue;
  230. return node;
  231. }
  232. static auto CreateString( std::string aValue ) -> std::unique_ptr<NODE>
  233. {
  234. auto node = std::make_unique<NODE>();
  235. node->type = NodeType::String;
  236. node->data = std::move( aValue );
  237. return node;
  238. }
  239. static auto CreateBinOp( std::unique_ptr<NODE> aLeft, char aOp, std::unique_ptr<NODE> aRight ) -> std::unique_ptr<NODE>
  240. {
  241. auto node = std::make_unique<NODE>();
  242. node->type = NodeType::BinOp;
  243. node->data = BIN_OP_DATA( std::move( aLeft ), aOp, std::move( aRight ) );
  244. return node;
  245. }
  246. static auto CreateFunction( std::string aName, std::vector<std::unique_ptr<NODE>> aArgs ) -> std::unique_ptr<NODE>
  247. {
  248. auto node = std::make_unique<NODE>();
  249. node->type = NodeType::Function;
  250. node->data = FUNC_DATA( std::move( aName ), std::move( aArgs ) );
  251. return node;
  252. }
  253. // Raw pointer factory methods for parser use
  254. static auto CreateTextRaw( std::string aText ) -> NODE*
  255. {
  256. auto node = new NODE();
  257. node->type = NodeType::Text;
  258. node->data = std::move( aText );
  259. return node;
  260. }
  261. static auto CreateCalcRaw( NODE* aExpr ) -> NODE*
  262. {
  263. auto node = new NODE();
  264. node->type = NodeType::Calc;
  265. node->data = BIN_OP_DATA( std::unique_ptr<NODE>( aExpr ), '=', nullptr );
  266. return node;
  267. }
  268. static auto CreateVarRaw( std::string aName ) -> NODE*
  269. {
  270. auto node = new NODE();
  271. node->type = NodeType::Var;
  272. node->data = std::move( aName );
  273. return node;
  274. }
  275. static auto CreateNumberRaw( double aValue ) -> NODE*
  276. {
  277. auto node = new NODE();
  278. node->type = NodeType::Number;
  279. node->data = aValue;
  280. return node;
  281. }
  282. static auto CreateStringRaw( std::string aValue ) -> NODE*
  283. {
  284. auto node = new NODE();
  285. node->type = NodeType::String;
  286. node->data = std::move( aValue );
  287. return node;
  288. }
  289. static auto CreateBinOpRaw( NODE* aLeft, char aOp, NODE* aRight ) -> NODE*
  290. {
  291. auto node = new NODE();
  292. node->type = NodeType::BinOp;
  293. node->data = BIN_OP_DATA( std::unique_ptr<NODE>( aLeft ), aOp, std::unique_ptr<NODE>( aRight ) );
  294. return node;
  295. }
  296. static auto CreateFunctionRaw( std::string aName, std::vector<std::unique_ptr<NODE>>* aArgs ) -> NODE*
  297. {
  298. auto node = new NODE();
  299. node->type = NodeType::Function;
  300. node->data = FUNC_DATA( std::move( aName ), std::move( *aArgs ) );
  301. delete aArgs;
  302. return node;
  303. }
  304. // Mixed-type evaluation
  305. template<typename Visitor>
  306. auto Accept( Visitor&& aVisitor ) const -> Result<Value>
  307. {
  308. return std::forward<Visitor>( aVisitor )( *this );
  309. }
  310. };
  311. class DOC
  312. {
  313. public:
  314. std::vector<std::unique_ptr<NODE>> nodes;
  315. mutable ERROR_COLLECTOR errors;
  316. auto AddNode( std::unique_ptr<NODE> aNode ) -> void
  317. {
  318. nodes.emplace_back( std::move( aNode ) );
  319. }
  320. auto AddNodeRaw( NODE* aNode ) -> void
  321. {
  322. nodes.emplace_back( std::unique_ptr<NODE>( aNode ) );
  323. }
  324. auto HasErrors() const -> bool { return errors.HasErrors(); }
  325. auto GetErrors() const -> const std::vector<std::string>& { return errors.GetErrors(); }
  326. auto GetErrorSummary() const -> std::string { return errors.GetAllMessages(); }
  327. auto GetNodes() const -> const auto& { return nodes; }
  328. auto begin() const { return nodes.begin(); }
  329. auto end() const { return nodes.end(); }
  330. };
  331. // Global error collector for parser callbacks
  332. extern thread_local ERROR_COLLECTOR* g_errorCollector;
  333. class PARSE_CONTEXT
  334. {
  335. public:
  336. ERROR_COLLECTOR& errors;
  337. explicit PARSE_CONTEXT( ERROR_COLLECTOR& aErrorCollector ) :
  338. errors( aErrorCollector )
  339. {
  340. g_errorCollector = &aErrorCollector;
  341. }
  342. ~PARSE_CONTEXT()
  343. {
  344. g_errorCollector = nullptr;
  345. }
  346. PARSE_CONTEXT( const PARSE_CONTEXT& ) = delete;
  347. PARSE_CONTEXT& operator=( const PARSE_CONTEXT& ) = delete;
  348. PARSE_CONTEXT( PARSE_CONTEXT&& ) = delete;
  349. PARSE_CONTEXT& operator=( PARSE_CONTEXT&& ) = delete;
  350. };
  351. // Enhanced evaluation visitor supporting callback-based variable resolution
  352. class KICOMMON_API EVAL_VISITOR
  353. {
  354. public:
  355. // Callback function type for variable resolution
  356. using VariableCallback = std::function<Result<Value>(const std::string& aVariableName)>;
  357. private:
  358. VariableCallback m_variableCallback;
  359. [[maybe_unused]] ERROR_COLLECTOR& m_errors;
  360. mutable std::random_device m_rd;
  361. mutable std::mt19937 m_gen;
  362. public:
  363. /**
  364. * @brief Construct evaluator with variable callback function
  365. * @param aVariableCallback Function to call when resolving variables
  366. * @param aErrorCollector Error collector for storing errors
  367. */
  368. explicit EVAL_VISITOR( VariableCallback aVariableCallback, ERROR_COLLECTOR& aErrorCollector );
  369. // Visitor methods for evaluating different node types
  370. auto operator()( const NODE& aNode ) const -> Result<Value>;
  371. private:
  372. auto evaluateFunction( const FUNC_DATA& aFunc ) const -> Result<Value>;
  373. };
  374. // Enhanced document processor supporting callback-based variable resolution
  375. class KICOMMON_API DOC_PROCESSOR
  376. {
  377. public:
  378. using VariableCallback = EVAL_VISITOR::VariableCallback;
  379. /**
  380. * @brief Process document using callback for variable resolution
  381. * @param aDoc Document to process
  382. * @param aVariableCallback Function to resolve variables
  383. * @return Pair of (result_string, had_errors)
  384. */
  385. static auto Process( const DOC& aDoc, VariableCallback aVariableCallback )
  386. -> std::pair<std::string, bool>;
  387. /**
  388. * @brief Process document with detailed error reporting
  389. * @param aDoc Document to process
  390. * @param aVariableCallback Function to resolve variables
  391. * @return Tuple of (result_string, error_messages, had_errors)
  392. */
  393. static auto ProcessWithDetails( const DOC& aDoc, VariableCallback aVariableCallback )
  394. -> std::tuple<std::string, std::vector<std::string>, bool>;
  395. };
  396. } // namespace calc_parser