KiCad PCB EDA Suite
Loading...
Searching...
No Matches
libeval_compiler.h
Go to the documentation of this file.
1/*
2 This file is part of libeval, a simple math expression evaluator
3
4 Copyright (C) 2007 Michael Geselbracht, [email protected]
5 Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <https://www.gnu.org/licenses/>.
19*/
20
21#pragma once
22
23#include <cstddef>
24#include <functional>
25#include <map>
26#include <optional>
27#include <string>
28#include <stack>
29
31
32#include <kicommon.h>
33#include <base_units.h>
34#include <eda_units.h>
35#include <wx/intl.h>
36
37#if defined(WIN32)
38// This gets leaked by python headers on MSVC only and will cause chaos
39#undef COMPILER
40#endif
41
42#define TR_OP_BINARY_MASK 0x200
43#define TR_OP_UNARY_MASK 0x100
44
45#define TR_OP_MUL 0x201
46#define TR_OP_DIV 0x202
47#define TR_OP_ADD 0x203
48#define TR_OP_SUB 0x204
49#define TR_OP_LESS 0x205
50#define TR_OP_GREATER 0x206
51#define TR_OP_LESS_EQUAL 0x207
52#define TR_OP_GREATER_EQUAL 0x208
53#define TR_OP_EQUAL 0x209
54#define TR_OP_NOT_EQUAL 0x20a
55#define TR_OP_BOOL_AND 0x20b
56#define TR_OP_BOOL_OR 0x20c
57#define TR_OP_BOOL_NOT 0x100
58#define TR_OP_FUNC_CALL 24
59#define TR_OP_METHOD_CALL 25
60#define TR_UOP_PUSH_VAR 1
61#define TR_UOP_PUSH_VALUE 2
62
63// Short-circuit jumps for && / ||. They peek (do not pop) the left-hand result already on the
64// stack and, when it already decides the boolean, jump past the right-hand operand's microcode so
65// it is never executed. This is the compile-time pruning the SSA-style evaluator performs.
66// The opcodes deliberately sit outside both TR_OP_UNARY_MASK and TR_OP_BINARY_MASK so they are
67// never misclassified as arithmetic operators by a mask test.
68#define TR_OP_JZ 0x401 // jump if top == 0 (left side of &&)
69#define TR_OP_JNZ 0x402 // jump if top != 0 (left side of ||)
70
71// This namespace is used for the lemon parser
72namespace LIBEVAL
73{
74
75class COMPILER;
76
83
92
93
103
116
117class UOP;
118class UCODE;
119class CONTEXT;
120class VAR_REF;
121
122typedef std::function<void( CONTEXT*, void* )> FUNC_CALL_REF;
123
125{
126 wxString* str;
127 double num;
128 int idx;
129};
130
131// Lemon can't handle c'tors and d'tors, so we provide a poor-man's version.
132constexpr T_TOKEN_VALUE defaultTokenValue = { nullptr, 0.0, 0 };
133
134
140
141// Lemon can't handle c'tors and d'tors, so we provide a poor-man's version.
143
144
146{
147public:
149
150 int op;
153 bool valid;
157
158 void SetUop( int aOp, double aValue, EDA_UNITS aUnits );
159 void SetUop( int aOp, const wxString& aValue, bool aStringIsWildcard );
160 void SetUop( int aOp, std::unique_ptr<VAR_REF> aRef = nullptr );
161 void SetUop( int aOp, FUNC_CALL_REF aFunc, std::unique_ptr<VAR_REF> aRef = nullptr );
162};
163
164
165TREE_NODE* newNode( LIBEVAL::COMPILER* compiler, int op,
166 const T_TOKEN_VALUE& value = defaultTokenValue );
167
169{
170public:
172 {
173 }
174
176 {
177 }
178
179 virtual const std::vector<wxString>& GetSupportedUnits() const
180 {
181 static const std::vector<wxString> nullUnits;
182
183 return nullUnits;
184 }
185
186
187 virtual const std::vector<EDA_UNITS>& GetSupportedUnitsTypes() const
188 {
189 static const std::vector<EDA_UNITS> nullUnits;
190
191 return nullUnits;
192 }
193
194 virtual wxString GetSupportedUnitsMessage() const
195 {
196 return wxEmptyString;
197 }
198
199 virtual double Convert( const wxString& aString, int unitType ) const
200 {
201 return 0.0;
202 }
203};
204
205
207{
208public:
212 m_valueDbl( 0 ),
213 m_stringIsWildcard( false ),
214 m_isDeferredDbl( false ),
215 m_isDeferredStr( false )
216 {}
217
218 VALUE( const wxString& aStr, bool aIsWildcard = false ) :
219 m_type( VT_STRING ),
221 m_valueDbl( 0 ),
222 m_valueStr( aStr ),
223 m_stringIsWildcard( aIsWildcard ),
224 m_isDeferredDbl( false ),
225 m_isDeferredStr( false )
226 {}
227
228 VALUE( const double aVal ) :
231 m_valueDbl( aVal ),
232 m_stringIsWildcard( false ),
233 m_isDeferredDbl( false ),
234 m_isDeferredStr( false )
235 {}
236
238 {
239 VALUE* v = new VALUE();
240 v->m_type = VT_NULL;
241 return v;
242 }
243
244 virtual ~VALUE() = default;
245
246 virtual double AsDouble() const
247 {
248 if( m_isDeferredDbl )
249 {
251 m_isDeferredDbl = false;
252 }
253
254 return m_valueDbl;
255 }
256
257 virtual const wxString& AsString() const
258 {
259 if( m_isDeferredStr )
260 {
262 m_isDeferredStr = false;
263 }
264
265 return m_valueStr;
266 }
267
268 virtual bool EqualTo( CONTEXT* aCtx, const VALUE* b ) const;
269
270 // NB: this is not an inverse of EqualTo as they both return false for undefined values.
271 virtual bool NotEqualTo( CONTEXT* aCtx, const VALUE* b ) const;
272
273 VAR_TYPE_T GetType() const { return m_type; };
274
275 void Set( double aValue )
276 {
278 m_valueDbl = aValue;
279 }
280
281 void SetDeferredEval( INPLACE_FUNCTION<double()> aLambda )
282 {
284 m_lambdaDbl = std::move( aLambda );
285 m_isDeferredDbl = true;
286 }
287
288 void SetDeferredEval( INPLACE_FUNCTION<wxString()> aLambda )
289 {
291 m_lambdaStr = std::move( aLambda );
292 m_isDeferredStr = true;
293 }
294
295 void Set( const wxString& aValue )
296 {
298 m_valueStr = aValue;
299 }
300
301 void Set( const VALUE &val )
302 {
303 m_type = val.m_type;
306 m_units = val.m_units;
307
308 if( m_type == VT_STRING )
310 }
311
312 void SetUnits( const EDA_UNITS aUnits ) { m_units = aUnits; }
313
314 EDA_UNITS GetUnits() const { return m_units; }
315
316 bool StringIsWildcard() const { return m_stringIsWildcard; }
317
318private:
321 mutable double m_valueDbl; // mutable to support deferred evaluation
322 mutable wxString m_valueStr; // mutable to support deferred evaluation
323
327 mutable bool m_isDeferredDbl;
328 mutable bool m_isDeferredStr;
329
330};
331
333{
334public:
336 {}
337
338 virtual ~VAR_REF() = default;
339
340 virtual VAR_TYPE_T GetType() const = 0;
341 virtual VALUE* GetValue( CONTEXT* aCtx ) = 0;
342};
343
344
346{
347public:
349 m_stack(),
350 m_stackPtr( 0 ),
351 m_inlineUsed( 0 )
352 {
353 m_ownedValues.reserve( 8 );
354 }
355
356 virtual ~CONTEXT()
357 {
359 }
360
361 // We own at least one list of raw pointers. Don't let the compiler fill in copy c'tors that
362 // will only land us in trouble.
363 CONTEXT( const CONTEXT& ) = delete;
364 CONTEXT& operator=( const CONTEXT& ) = delete;
365
372 virtual void Reset()
373 {
374 for( std::size_t i = 0; i < m_inlineUsed; ++i )
375 inlineValue( i )->~VALUE();
376
377 m_inlineUsed = 0;
378
379 for( VALUE* v : m_ownedValues )
380 delete v;
381
382 m_ownedValues.clear();
383 m_stackPtr = 0;
384 m_errorCallback = nullptr;
385 }
386
388 {
389 // Serve the first values from an inline buffer so the hot DRC evaluation path stays off
390 // the heap; only deeper expressions spill to individual allocations.
392 {
393 // Advance the count only after construction so a throwing VALUE() never leaves the
394 // destructor to tear down an uninitialized slot.
395 VALUE* value = new( inlineValue( m_inlineUsed ) ) VALUE();
396 ++m_inlineUsed;
397 return value;
398 }
399
400 m_ownedValues.emplace_back( new VALUE );
401 return m_ownedValues.back();
402 }
403
405 {
406 m_ownedValues.emplace_back( aValue );
407 return m_ownedValues.back();
408 }
409
410 void Push( VALUE* v )
411 {
412 m_stack[ m_stackPtr++ ] = v;
413 }
414
416 {
417 if( m_stackPtr == 0 )
418 {
419 ReportError( _( "Malformed expression" ) );
420 return AllocValue();
421 }
422
423 return m_stack[ --m_stackPtr ];
424 }
425
428 {
429 if( m_stackPtr == 0 )
430 {
431 ReportError( _( "Malformed expression" ) );
432 return AllocValue();
433 }
434
435 return m_stack[ m_stackPtr - 1 ];
436 }
437
438 int SP() const
439 {
440 return m_stackPtr;
441 };
442
443 void SetErrorCallback( std::function<void( const wxString& aMessage, int aOffset )> aCallback )
444 {
445 m_errorCallback = std::move( aCallback );
446 }
447
448 bool HasErrorCallback() { return m_errorCallback != nullptr; }
449
450 void ReportError( const wxString& aErrorMsg );
451
452private:
453 static constexpr std::size_t INLINE_VALUE_COUNT = 32;
454
455 VALUE* inlineValue( std::size_t aIndex )
456 {
457 return reinterpret_cast<VALUE*>( m_inlineStorage ) + aIndex;
458 }
459
460 std::vector<VALUE*> m_ownedValues;
461 VALUE* m_stack[100]; // std::stack not performant enough
463
464 alignas( VALUE ) unsigned char m_inlineStorage[INLINE_VALUE_COUNT * sizeof( VALUE )];
465 std::size_t m_inlineUsed;
466
467 std::function<void( const wxString& aMessage, int aOffset )> m_errorCallback;
468};
469
470
472{
473public:
475 {}
476
477 virtual ~UCODE();
478
479 // We own at least one list of raw pointers. Don't let the compiler fill in copy c'tors that
480 // will only land us in trouble.
481 UCODE( const UCODE& ) = delete;
482 UCODE& operator=( const UCODE& ) = delete;
483
484 void AddOp( UOP* uop )
485 {
486 m_ucode.push_back(uop);
487 }
488
490 void MarkHasJumps() { m_hasJumps = true; }
491
493 int GetSize() const { return static_cast<int>( m_ucode.size() ); }
494
495 VALUE* Run( CONTEXT* ctx );
496 wxString Dump() const;
497
498 virtual std::unique_ptr<VAR_REF> CreateVarRef( const wxString& var, const wxString& field )
499 {
500 return nullptr;
501 }
502
503 virtual FUNC_CALL_REF CreateFuncCall( const wxString& name )
504 {
505 return nullptr;
506 }
507
508protected:
509 std::vector<UOP*> m_ucode;
510 bool m_hasJumps = false; // any short-circuit jumps? lets Run() skip the jump loop
511};
512
513
515{
516public:
517 UOP( int op, std::unique_ptr<VALUE> value ) :
518 m_op( op ),
519 m_ref(nullptr),
520 m_value( std::move( value ) )
521 {}
522
523 UOP( int op, std::unique_ptr<VAR_REF> vref ) :
524 m_op( op ),
525 m_ref( std::move( vref ) ),
526 m_value(nullptr)
527 {}
528
529 UOP( int op, FUNC_CALL_REF func, std::unique_ptr<VAR_REF> vref = nullptr ) :
530 m_op( op ),
531 m_func( std::move( func ) ),
532 m_ref( std::move( vref ) ),
533 m_value(nullptr)
534 {}
535
537 explicit UOP( int op ) :
538 m_op( op ),
539 m_ref( nullptr ),
540 m_value( nullptr )
541 {}
542
543 virtual ~UOP() = default;
544
547 int Exec( CONTEXT* ctx );
548
549 void SetJumpTarget( int aTarget ) { m_jumpTarget = aTarget; }
550
551 wxString Format() const;
552
553private:
554 int m_op;
555 int m_jumpTarget = -1;
556
558 std::unique_ptr<VAR_REF> m_ref;
559 std::unique_ptr<VALUE> m_value;
560};
561
563{
564public:
565 void Restart( const wxString& aStr )
566 {
567 m_str = aStr;
568 m_pos = 0;
569 }
570
571 void Clear()
572 {
573 m_str = "";
574 m_pos = 0;
575 }
576
577 int GetChar() const
578 {
579 if( m_pos >= m_str.length() )
580 return 0;
581
582 return m_str[m_pos];
583 }
584
585 bool Done() const
586 {
587 return m_pos >= m_str.length();
588 }
589
590 void NextChar( int aAdvance = 1 )
591 {
592 m_pos += aAdvance;
593 }
594
595 size_t GetPos() const
596 {
597 return m_pos;
598 }
599
600 wxString GetString();
601
602 wxString GetChars( const std::function<bool( wxUniChar )>& cond ) const;
603
604 bool MatchAhead( const wxString& match,
605 const std::function<bool( wxUniChar )>& stopCond ) const;
606
607private:
608 wxString m_str;
609 size_t m_pos = 0;
610};
611
612
614{
615public:
616 COMPILER();
617 virtual ~COMPILER();
618
619 // We own at least one list of raw pointers. Don't let the compiler fill in copy c'tors that
620 // will only land us in trouble.
621 COMPILER( const COMPILER& ) = delete;
622 COMPILER& operator=( const COMPILER& ) = delete;
623
624 /*
625 * clear() should be invoked by the client if a new input string is to be processed. It
626 * will reset the parser. User defined variables are retained.
627 */
628 void Clear();
629
630 /* Used by the lemon parser */
631 void parseError( const char* s );
632 void parseOk();
633
634 int GetSourcePos() const { return m_sourcePos; }
635
636 void setRoot( LIBEVAL::TREE_NODE *root );
637 void freeTree( LIBEVAL::TREE_NODE *tree );
638
639 bool Compile( const wxString& aString, UCODE* aCode, CONTEXT* aPreflightContext );
640
641 void SetErrorCallback( std::function<void( const wxString& aMessage, int aOffset )> aCallback )
642 {
643 m_errorCallback = std::move( aCallback );
644 }
645
646 bool IsErrorPending() const { return m_errorStatus.pendingError; }
647 const ERROR_STATUS& GetError() const { return m_errorStatus; }
648
649 void GcItem( TREE_NODE* aItem ) { m_gcItems.push_back( aItem ); }
650 void GcItem( wxString* aItem ) { m_gcStrings.push_back( aItem ); }
651
652protected:
654 {
657 };
658
660
661 bool generateUCode( UCODE* aCode, CONTEXT* aPreflightContext );
662
663 void reportError( COMPILATION_STAGE stage, const wxString& aErrorMsg, int aPos = -1 );
664
665 /* Begin processing of a new input string */
666 void newString( const wxString& aString );
667
668 /* Tokenizer: Next token/value taken from input string. */
670 bool lexDefault( T_TOKEN& aToken );
671 bool lexString( T_TOKEN& aToken );
672
673 int resolveUnits();
674
675protected:
676 /* Token state for input string. */
677 void* m_parser; // the current lemon parser state machine
680
681 std::unique_ptr<UNIT_RESOLVER> m_unitResolver;
682
686
687 std::function<void( const wxString& aMessage, int aOffset )> m_errorCallback;
688
690
691 std::vector<TREE_NODE*> m_gcItems;
692 std::vector<wxString*> m_gcStrings;
693};
694
695
703KICOMMON_API std::optional<wxString> EscapeStringLiteral( const wxString& aText );
704
705
706} // namespace LIBEVAL
707
const char * name
A std::function-style callable wrapper that stores the target in a fixed inline buffer and never allo...
void SetErrorCallback(std::function< void(const wxString &aMessage, int aOffset)> aCallback)
bool IsErrorPending() const
std::unique_ptr< UNIT_RESOLVER > m_unitResolver
void newString(const wxString &aString)
void GcItem(wxString *aItem)
bool lexString(T_TOKEN &aToken)
void GcItem(TREE_NODE *aItem)
COMPILER(const COMPILER &)=delete
bool generateUCode(UCODE *aCode, CONTEXT *aPreflightContext)
std::function< void(const wxString &aMessage, int aOffset)> m_errorCallback
bool lexDefault(T_TOKEN &aToken)
std::vector< TREE_NODE * > m_gcItems
void reportError(COMPILATION_STAGE stage, const wxString &aErrorMsg, int aPos=-1)
const ERROR_STATUS & GetError() const
COMPILER & operator=(const COMPILER &)=delete
std::vector< wxString * > m_gcStrings
ERROR_STATUS m_errorStatus
void parseError(const char *s)
std::vector< VALUE * > m_ownedValues
std::size_t m_inlineUsed
VALUE * StoreValue(VALUE *aValue)
void ReportError(const wxString &aErrorMsg)
std::function< void(const wxString &aMessage, int aOffset)> m_errorCallback
CONTEXT(const CONTEXT &)=delete
void SetErrorCallback(std::function< void(const wxString &aMessage, int aOffset)> aCallback)
unsigned char m_inlineStorage[INLINE_VALUE_COUNT *sizeof(VALUE)]
VALUE * inlineValue(std::size_t aIndex)
static constexpr std::size_t INLINE_VALUE_COUNT
virtual void Reset()
Release every value allocated by the previous evaluation and rewind the stack so the same CONTEXT can...
CONTEXT & operator=(const CONTEXT &)=delete
VALUE * Top()
Peek the top of the stack without popping (used by the short-circuit jumps).
void Push(VALUE *v)
void NextChar(int aAdvance=1)
void Restart(const wxString &aStr)
size_t GetPos() const
void SetUop(int aOp, double aValue, EDA_UNITS aUnits)
virtual std::unique_ptr< VAR_REF > CreateVarRef(const wxString &var, const wxString &field)
void AddOp(UOP *uop)
UCODE(const UCODE &)=delete
std::vector< UOP * > m_ucode
void MarkHasJumps()
Flag that this ucode contains short-circuit jumps, so Run() uses the jump-aware loop.
int GetSize() const
Index of the next op to be added (used to backpatch short-circuit jump targets).
virtual FUNC_CALL_REF CreateFuncCall(const wxString &name)
UCODE & operator=(const UCODE &)=delete
virtual wxString GetSupportedUnitsMessage() const
virtual const std::vector< EDA_UNITS > & GetSupportedUnitsTypes() const
virtual double Convert(const wxString &aString, int unitType) const
virtual const std::vector< wxString > & GetSupportedUnits() const
virtual ~UOP()=default
UOP(int op, FUNC_CALL_REF func, std::unique_ptr< VAR_REF > vref=nullptr)
std::unique_ptr< VAR_REF > m_ref
FUNC_CALL_REF m_func
UOP(int op, std::unique_ptr< VALUE > value)
int Exec(CONTEXT *ctx)
Execute the op.
UOP(int op)
Bare op (used for the short-circuit jumps, whose only payload is a jump target).
void SetJumpTarget(int aTarget)
std::unique_ptr< VALUE > m_value
UOP(int op, std::unique_ptr< VAR_REF > vref)
void Set(double aValue)
virtual const wxString & AsString() const
void SetUnits(const EDA_UNITS aUnits)
bool StringIsWildcard() const
virtual ~VALUE()=default
void Set(const wxString &aValue)
INPLACE_FUNCTION< double()> m_lambdaDbl
static VALUE * MakeNullValue()
INPLACE_FUNCTION< wxString()> m_lambdaStr
void SetDeferredEval(INPLACE_FUNCTION< wxString()> aLambda)
virtual double AsDouble() const
EDA_UNITS GetUnits() const
VAR_TYPE_T GetType() const
void Set(const VALUE &val)
VALUE(const double aVal)
void SetDeferredEval(INPLACE_FUNCTION< double()> aLambda)
VALUE(const wxString &aStr, bool aIsWildcard=false)
virtual VALUE * GetValue(CONTEXT *aCtx)=0
virtual VAR_TYPE_T GetType() const =0
virtual ~VAR_REF()=default
#define _(s)
EDA_UNITS
Definition eda_units.h:44
#define KICOMMON_API
Definition kicommon.h:27
TREE_NODE * newNode(LIBEVAL::COMPILER *compiler, int op, const T_TOKEN_VALUE &value)
constexpr T_TOKEN defaultToken
constexpr T_TOKEN_VALUE defaultTokenValue
std::function< void(CONTEXT *, void *)> FUNC_CALL_REF
std::optional< wxString > EscapeStringLiteral(const wxString &aText)
Escape text for use between the quotes of an expression string.
STL namespace.
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition ptree.cpp:194
COMPILATION_STAGE stage
T_TOKEN_VALUE value
@ VALUE
Field Value of part, i.e. "3.3K".