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 <string>
27#include <stack>
28
30
31#include <kicommon.h>
32#include <base_units.h>
33#include <wx/intl.h>
34
35#if defined(WIN32)
36// This gets leaked by python headers on MSVC only and will cause chaos
37#undef COMPILER
38#endif
39
40#define TR_OP_BINARY_MASK 0x200
41#define TR_OP_UNARY_MASK 0x100
42
43#define TR_OP_MUL 0x201
44#define TR_OP_DIV 0x202
45#define TR_OP_ADD 0x203
46#define TR_OP_SUB 0x204
47#define TR_OP_LESS 0x205
48#define TR_OP_GREATER 0x206
49#define TR_OP_LESS_EQUAL 0x207
50#define TR_OP_GREATER_EQUAL 0x208
51#define TR_OP_EQUAL 0x209
52#define TR_OP_NOT_EQUAL 0x20a
53#define TR_OP_BOOL_AND 0x20b
54#define TR_OP_BOOL_OR 0x20c
55#define TR_OP_BOOL_NOT 0x100
56#define TR_OP_FUNC_CALL 24
57#define TR_OP_METHOD_CALL 25
58#define TR_UOP_PUSH_VAR 1
59#define TR_UOP_PUSH_VALUE 2
60
61// Short-circuit jumps for && / ||. They peek (do not pop) the left-hand result already on the
62// stack and, when it already decides the boolean, jump past the right-hand operand's microcode so
63// it is never executed. This is the compile-time pruning the SSA-style evaluator performs.
64// The opcodes deliberately sit outside both TR_OP_UNARY_MASK and TR_OP_BINARY_MASK so they are
65// never misclassified as arithmetic operators by a mask test.
66#define TR_OP_JZ 0x401 // jump if top == 0 (left side of &&)
67#define TR_OP_JNZ 0x402 // jump if top != 0 (left side of ||)
68
69// This namespace is used for the lemon parser
70namespace LIBEVAL
71{
72
73class COMPILER;
74
81
90
91
101
114
115class UOP;
116class UCODE;
117class CONTEXT;
118class VAR_REF;
119
120typedef std::function<void( CONTEXT*, void* )> FUNC_CALL_REF;
121
123{
124 wxString* str;
125 double num;
126 int idx;
127};
128
129// Lemon can't handle c'tors and d'tors, so we provide a poor-man's version.
130constexpr T_TOKEN_VALUE defaultTokenValue = { nullptr, 0.0, 0 };
131
132
138
139// Lemon can't handle c'tors and d'tors, so we provide a poor-man's version.
141
142
144{
145public:
147
148 int op;
151 bool valid;
155
156 void SetUop( int aOp, double aValue, EDA_UNITS aUnits );
157 void SetUop( int aOp, const wxString& aValue, bool aStringIsWildcard );
158 void SetUop( int aOp, std::unique_ptr<VAR_REF> aRef = nullptr );
159 void SetUop( int aOp, FUNC_CALL_REF aFunc, std::unique_ptr<VAR_REF> aRef = nullptr );
160};
161
162
163TREE_NODE* newNode( LIBEVAL::COMPILER* compiler, int op,
164 const T_TOKEN_VALUE& value = defaultTokenValue );
165
167{
168public:
170 {
171 }
172
174 {
175 }
176
177 virtual const std::vector<wxString>& GetSupportedUnits() const
178 {
179 static const std::vector<wxString> nullUnits;
180
181 return nullUnits;
182 }
183
184
185 virtual const std::vector<EDA_UNITS>& GetSupportedUnitsTypes() const
186 {
187 static const std::vector<EDA_UNITS> nullUnits;
188
189 return nullUnits;
190 }
191
192 virtual wxString GetSupportedUnitsMessage() const
193 {
194 return wxEmptyString;
195 }
196
197 virtual double Convert( const wxString& aString, int unitType ) const
198 {
199 return 0.0;
200 }
201};
202
203
205{
206public:
209 m_valueDbl( 0 ),
210 m_stringIsWildcard( false ),
211 m_isDeferredDbl( false ),
212 m_isDeferredStr( false ),
214 {}
215
216 VALUE( const wxString& aStr, bool aIsWildcard = false ) :
217 m_type( VT_STRING ),
218 m_valueDbl( 0 ),
219 m_valueStr( aStr ),
220 m_stringIsWildcard( aIsWildcard ),
221 m_isDeferredDbl( false ),
222 m_isDeferredStr( false ),
224 {}
225
226 VALUE( const double aVal ) :
228 m_valueDbl( aVal ),
229 m_stringIsWildcard( false ),
230 m_isDeferredDbl( false ),
231 m_isDeferredStr( false ),
233 {}
234
236 {
237 VALUE* v = new VALUE();
238 v->m_type = VT_NULL;
239 return v;
240 }
241
242 virtual ~VALUE() = default;
243
244 virtual double AsDouble() const
245 {
246 if( m_isDeferredDbl )
247 {
249 m_isDeferredDbl = false;
250 }
251
252 return m_valueDbl;
253 }
254
255 virtual const wxString& AsString() const
256 {
257 if( m_isDeferredStr )
258 {
260 m_isDeferredStr = false;
261 }
262
263 return m_valueStr;
264 }
265
266 virtual bool EqualTo( CONTEXT* aCtx, const VALUE* b ) const;
267
268 // NB: this is not an inverse of EqualTo as they both return false for undefined values.
269 virtual bool NotEqualTo( CONTEXT* aCtx, const VALUE* b ) const;
270
271 VAR_TYPE_T GetType() const { return m_type; };
272
273 void Set( double aValue )
274 {
276 m_valueDbl = aValue;
277 }
278
279 void SetDeferredEval( INPLACE_FUNCTION<double()> aLambda )
280 {
282 m_lambdaDbl = std::move( aLambda );
283 m_isDeferredDbl = true;
284 }
285
286 void SetDeferredEval( INPLACE_FUNCTION<wxString()> aLambda )
287 {
289 m_lambdaStr = std::move( aLambda );
290 m_isDeferredStr = true;
291 }
292
293 void Set( const wxString& aValue )
294 {
296 m_valueStr = aValue;
297 }
298
299 void Set( const VALUE &val )
300 {
301 m_type = val.m_type;
304 m_units = val.m_units;
305
306 if( m_type == VT_STRING )
308 }
309
310 void SetUnits( const EDA_UNITS aUnits ) { m_units = aUnits; }
311
312 EDA_UNITS GetUnits() const { return m_units; }
313
314 bool StringIsWildcard() const { return m_stringIsWildcard; }
315
316private:
318 mutable double m_valueDbl; // mutable to support deferred evaluation
319 mutable wxString m_valueStr; // mutable to support deferred evaluation
321
322 mutable bool m_isDeferredDbl;
324
325 mutable bool m_isDeferredStr;
327
329};
330
332{
333public:
335 {}
336
337 virtual ~VAR_REF() = default;
338
339 virtual VAR_TYPE_T GetType() const = 0;
340 virtual VALUE* GetValue( CONTEXT* aCtx ) = 0;
341};
342
343
345{
346public:
348 m_stack(),
349 m_stackPtr( 0 ),
350 m_inlineUsed( 0 )
351 {
352 m_ownedValues.reserve( 8 );
353 }
354
355 virtual ~CONTEXT()
356 {
358 }
359
360 // We own at least one list of raw pointers. Don't let the compiler fill in copy c'tors that
361 // will only land us in trouble.
362 CONTEXT( const CONTEXT& ) = delete;
363 CONTEXT& operator=( const CONTEXT& ) = delete;
364
371 virtual void Reset()
372 {
373 for( std::size_t i = 0; i < m_inlineUsed; ++i )
374 inlineValue( i )->~VALUE();
375
376 m_inlineUsed = 0;
377
378 for( VALUE* v : m_ownedValues )
379 delete v;
380
381 m_ownedValues.clear();
382 m_stackPtr = 0;
383 m_errorCallback = nullptr;
384 }
385
387 {
388 // Serve the first values from an inline buffer so the hot DRC evaluation path stays off
389 // the heap; only deeper expressions spill to individual allocations.
391 {
392 // Advance the count only after construction so a throwing VALUE() never leaves the
393 // destructor to tear down an uninitialized slot.
394 VALUE* value = new( inlineValue( m_inlineUsed ) ) VALUE();
395 ++m_inlineUsed;
396 return value;
397 }
398
399 m_ownedValues.emplace_back( new VALUE );
400 return m_ownedValues.back();
401 }
402
404 {
405 m_ownedValues.emplace_back( aValue );
406 return m_ownedValues.back();
407 }
408
409 void Push( VALUE* v )
410 {
411 m_stack[ m_stackPtr++ ] = v;
412 }
413
415 {
416 if( m_stackPtr == 0 )
417 {
418 ReportError( _( "Malformed expression" ) );
419 return AllocValue();
420 }
421
422 return m_stack[ --m_stackPtr ];
423 }
424
427 {
428 if( m_stackPtr == 0 )
429 {
430 ReportError( _( "Malformed expression" ) );
431 return AllocValue();
432 }
433
434 return m_stack[ m_stackPtr - 1 ];
435 }
436
437 int SP() const
438 {
439 return m_stackPtr;
440 };
441
442 void SetErrorCallback( std::function<void( const wxString& aMessage, int aOffset )> aCallback )
443 {
444 m_errorCallback = std::move( aCallback );
445 }
446
447 bool HasErrorCallback() { return m_errorCallback != nullptr; }
448
449 void ReportError( const wxString& aErrorMsg );
450
451private:
452 static constexpr std::size_t INLINE_VALUE_COUNT = 32;
453
454 VALUE* inlineValue( std::size_t aIndex )
455 {
456 return reinterpret_cast<VALUE*>( m_inlineStorage ) + aIndex;
457 }
458
459 std::vector<VALUE*> m_ownedValues;
460 VALUE* m_stack[100]; // std::stack not performant enough
462
463 alignas( VALUE ) unsigned char m_inlineStorage[INLINE_VALUE_COUNT * sizeof( VALUE )];
464 std::size_t m_inlineUsed;
465
466 std::function<void( const wxString& aMessage, int aOffset )> m_errorCallback;
467};
468
469
471{
472public:
474 {}
475
476 virtual ~UCODE();
477
478 // We own at least one list of raw pointers. Don't let the compiler fill in copy c'tors that
479 // will only land us in trouble.
480 UCODE( const UCODE& ) = delete;
481 UCODE& operator=( const UCODE& ) = delete;
482
483 void AddOp( UOP* uop )
484 {
485 m_ucode.push_back(uop);
486 }
487
489 void MarkHasJumps() { m_hasJumps = true; }
490
492 int GetSize() const { return static_cast<int>( m_ucode.size() ); }
493
494 VALUE* Run( CONTEXT* ctx );
495 wxString Dump() const;
496
497 virtual std::unique_ptr<VAR_REF> CreateVarRef( const wxString& var, const wxString& field )
498 {
499 return nullptr;
500 }
501
502 virtual FUNC_CALL_REF CreateFuncCall( const wxString& name )
503 {
504 return nullptr;
505 }
506
507protected:
508 std::vector<UOP*> m_ucode;
509 bool m_hasJumps = false; // any short-circuit jumps? lets Run() skip the jump loop
510};
511
512
514{
515public:
516 UOP( int op, std::unique_ptr<VALUE> value ) :
517 m_op( op ),
518 m_ref(nullptr),
519 m_value( std::move( value ) )
520 {}
521
522 UOP( int op, std::unique_ptr<VAR_REF> vref ) :
523 m_op( op ),
524 m_ref( std::move( vref ) ),
525 m_value(nullptr)
526 {}
527
528 UOP( int op, FUNC_CALL_REF func, std::unique_ptr<VAR_REF> vref = nullptr ) :
529 m_op( op ),
530 m_func( std::move( func ) ),
531 m_ref( std::move( vref ) ),
532 m_value(nullptr)
533 {}
534
536 explicit UOP( int op ) :
537 m_op( op ),
538 m_ref( nullptr ),
539 m_value( nullptr )
540 {}
541
542 virtual ~UOP() = default;
543
546 int Exec( CONTEXT* ctx );
547
548 void SetJumpTarget( int aTarget ) { m_jumpTarget = aTarget; }
549
550 wxString Format() const;
551
552private:
553 int m_op;
554 int m_jumpTarget = -1;
555
557 std::unique_ptr<VAR_REF> m_ref;
558 std::unique_ptr<VALUE> m_value;
559};
560
562{
563public:
564 void Restart( const wxString& aStr )
565 {
566 m_str = aStr;
567 m_pos = 0;
568 }
569
570 void Clear()
571 {
572 m_str = "";
573 m_pos = 0;
574 }
575
576 int GetChar() const
577 {
578 if( m_pos >= m_str.length() )
579 return 0;
580
581 return m_str[m_pos];
582 }
583
584 bool Done() const
585 {
586 return m_pos >= m_str.length();
587 }
588
589 void NextChar( int aAdvance = 1 )
590 {
591 m_pos += aAdvance;
592 }
593
594 size_t GetPos() const
595 {
596 return m_pos;
597 }
598
599 wxString GetString();
600
601 wxString GetChars( const std::function<bool( wxUniChar )>& cond ) const;
602
603 bool MatchAhead( const wxString& match,
604 const std::function<bool( wxUniChar )>& stopCond ) const;
605
606private:
607 wxString m_str;
608 size_t m_pos = 0;
609};
610
611
613{
614public:
615 COMPILER();
616 virtual ~COMPILER();
617
618 // We own at least one list of raw pointers. Don't let the compiler fill in copy c'tors that
619 // will only land us in trouble.
620 COMPILER( const COMPILER& ) = delete;
621 COMPILER& operator=( const COMPILER& ) = delete;
622
623 /*
624 * clear() should be invoked by the client if a new input string is to be processed. It
625 * will reset the parser. User defined variables are retained.
626 */
627 void Clear();
628
629 /* Used by the lemon parser */
630 void parseError( const char* s );
631 void parseOk();
632
633 int GetSourcePos() const { return m_sourcePos; }
634
635 void setRoot( LIBEVAL::TREE_NODE *root );
636 void freeTree( LIBEVAL::TREE_NODE *tree );
637
638 bool Compile( const wxString& aString, UCODE* aCode, CONTEXT* aPreflightContext );
639
640 void SetErrorCallback( std::function<void( const wxString& aMessage, int aOffset )> aCallback )
641 {
642 m_errorCallback = std::move( aCallback );
643 }
644
645 bool IsErrorPending() const { return m_errorStatus.pendingError; }
646 const ERROR_STATUS& GetError() const { return m_errorStatus; }
647
648 void GcItem( TREE_NODE* aItem ) { m_gcItems.push_back( aItem ); }
649 void GcItem( wxString* aItem ) { m_gcStrings.push_back( aItem ); }
650
651protected:
653 {
656 };
657
659
660 bool generateUCode( UCODE* aCode, CONTEXT* aPreflightContext );
661
662 void reportError( COMPILATION_STAGE stage, const wxString& aErrorMsg, int aPos = -1 );
663
664 /* Begin processing of a new input string */
665 void newString( const wxString& aString );
666
667 /* Tokenizer: Next token/value taken from input string. */
669 bool lexDefault( T_TOKEN& aToken );
670 bool lexString( T_TOKEN& aToken );
671
672 int resolveUnits();
673
674protected:
675 /* Token state for input string. */
676 void* m_parser; // the current lemon parser state machine
679
680 std::unique_ptr<UNIT_RESOLVER> m_unitResolver;
681
685
686 std::function<void( const wxString& aMessage, int aOffset )> m_errorCallback;
687
689
690 std::vector<TREE_NODE*> m_gcItems;
691 std::vector<wxString*> m_gcStrings;
692};
693
694
695} // namespace LIBEVAL
696
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
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".