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:
210 m_valueDbl( 0 ),
211 m_stringIsWildcard( false ),
212 m_isDeferredDbl( false ),
213 m_isDeferredStr( false )
214 {}
215
216 VALUE( const wxString& aStr, bool aIsWildcard = false ) :
217 m_type( VT_STRING ),
219 m_valueDbl( 0 ),
220 m_valueStr( aStr ),
221 m_stringIsWildcard( aIsWildcard ),
222 m_isDeferredDbl( false ),
223 m_isDeferredStr( false )
224 {}
225
226 VALUE( const double aVal ) :
229 m_valueDbl( aVal ),
230 m_stringIsWildcard( false ),
231 m_isDeferredDbl( false ),
232 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:
319 mutable double m_valueDbl; // mutable to support deferred evaluation
320 mutable wxString m_valueStr; // mutable to support deferred evaluation
321
325 mutable bool m_isDeferredDbl;
326 mutable bool m_isDeferredStr;
327
328};
329
331{
332public:
334 {}
335
336 virtual ~VAR_REF() = default;
337
338 virtual VAR_TYPE_T GetType() const = 0;
339 virtual VALUE* GetValue( CONTEXT* aCtx ) = 0;
340};
341
342
344{
345public:
347 m_stack(),
348 m_stackPtr( 0 ),
349 m_inlineUsed( 0 )
350 {
351 m_ownedValues.reserve( 8 );
352 }
353
354 virtual ~CONTEXT()
355 {
357 }
358
359 // We own at least one list of raw pointers. Don't let the compiler fill in copy c'tors that
360 // will only land us in trouble.
361 CONTEXT( const CONTEXT& ) = delete;
362 CONTEXT& operator=( const CONTEXT& ) = delete;
363
370 virtual void Reset()
371 {
372 for( std::size_t i = 0; i < m_inlineUsed; ++i )
373 inlineValue( i )->~VALUE();
374
375 m_inlineUsed = 0;
376
377 for( VALUE* v : m_ownedValues )
378 delete v;
379
380 m_ownedValues.clear();
381 m_stackPtr = 0;
382 m_errorCallback = nullptr;
383 }
384
386 {
387 // Serve the first values from an inline buffer so the hot DRC evaluation path stays off
388 // the heap; only deeper expressions spill to individual allocations.
390 {
391 // Advance the count only after construction so a throwing VALUE() never leaves the
392 // destructor to tear down an uninitialized slot.
393 VALUE* value = new( inlineValue( m_inlineUsed ) ) VALUE();
394 ++m_inlineUsed;
395 return value;
396 }
397
398 m_ownedValues.emplace_back( new VALUE );
399 return m_ownedValues.back();
400 }
401
403 {
404 m_ownedValues.emplace_back( aValue );
405 return m_ownedValues.back();
406 }
407
408 void Push( VALUE* v )
409 {
410 m_stack[ m_stackPtr++ ] = v;
411 }
412
414 {
415 if( m_stackPtr == 0 )
416 {
417 ReportError( _( "Malformed expression" ) );
418 return AllocValue();
419 }
420
421 return m_stack[ --m_stackPtr ];
422 }
423
426 {
427 if( m_stackPtr == 0 )
428 {
429 ReportError( _( "Malformed expression" ) );
430 return AllocValue();
431 }
432
433 return m_stack[ m_stackPtr - 1 ];
434 }
435
436 int SP() const
437 {
438 return m_stackPtr;
439 };
440
441 void SetErrorCallback( std::function<void( const wxString& aMessage, int aOffset )> aCallback )
442 {
443 m_errorCallback = std::move( aCallback );
444 }
445
446 bool HasErrorCallback() { return m_errorCallback != nullptr; }
447
448 void ReportError( const wxString& aErrorMsg );
449
450private:
451 static constexpr std::size_t INLINE_VALUE_COUNT = 32;
452
453 VALUE* inlineValue( std::size_t aIndex )
454 {
455 return reinterpret_cast<VALUE*>( m_inlineStorage ) + aIndex;
456 }
457
458 std::vector<VALUE*> m_ownedValues;
459 VALUE* m_stack[100]; // std::stack not performant enough
461
462 alignas( VALUE ) unsigned char m_inlineStorage[INLINE_VALUE_COUNT * sizeof( VALUE )];
463 std::size_t m_inlineUsed;
464
465 std::function<void( const wxString& aMessage, int aOffset )> m_errorCallback;
466};
467
468
470{
471public:
473 {}
474
475 virtual ~UCODE();
476
477 // We own at least one list of raw pointers. Don't let the compiler fill in copy c'tors that
478 // will only land us in trouble.
479 UCODE( const UCODE& ) = delete;
480 UCODE& operator=( const UCODE& ) = delete;
481
482 void AddOp( UOP* uop )
483 {
484 m_ucode.push_back(uop);
485 }
486
488 void MarkHasJumps() { m_hasJumps = true; }
489
491 int GetSize() const { return static_cast<int>( m_ucode.size() ); }
492
493 VALUE* Run( CONTEXT* ctx );
494 wxString Dump() const;
495
496 virtual std::unique_ptr<VAR_REF> CreateVarRef( const wxString& var, const wxString& field )
497 {
498 return nullptr;
499 }
500
501 virtual FUNC_CALL_REF CreateFuncCall( const wxString& name )
502 {
503 return nullptr;
504 }
505
506protected:
507 std::vector<UOP*> m_ucode;
508 bool m_hasJumps = false; // any short-circuit jumps? lets Run() skip the jump loop
509};
510
511
513{
514public:
515 UOP( int op, std::unique_ptr<VALUE> value ) :
516 m_op( op ),
517 m_ref(nullptr),
518 m_value( std::move( value ) )
519 {}
520
521 UOP( int op, std::unique_ptr<VAR_REF> vref ) :
522 m_op( op ),
523 m_ref( std::move( vref ) ),
524 m_value(nullptr)
525 {}
526
527 UOP( int op, FUNC_CALL_REF func, std::unique_ptr<VAR_REF> vref = nullptr ) :
528 m_op( op ),
529 m_func( std::move( func ) ),
530 m_ref( std::move( vref ) ),
531 m_value(nullptr)
532 {}
533
535 explicit UOP( int op ) :
536 m_op( op ),
537 m_ref( nullptr ),
538 m_value( nullptr )
539 {}
540
541 virtual ~UOP() = default;
542
545 int Exec( CONTEXT* ctx );
546
547 void SetJumpTarget( int aTarget ) { m_jumpTarget = aTarget; }
548
549 wxString Format() const;
550
551private:
552 int m_op;
553 int m_jumpTarget = -1;
554
556 std::unique_ptr<VAR_REF> m_ref;
557 std::unique_ptr<VALUE> m_value;
558};
559
561{
562public:
563 void Restart( const wxString& aStr )
564 {
565 m_str = aStr;
566 m_pos = 0;
567 }
568
569 void Clear()
570 {
571 m_str = "";
572 m_pos = 0;
573 }
574
575 int GetChar() const
576 {
577 if( m_pos >= m_str.length() )
578 return 0;
579
580 return m_str[m_pos];
581 }
582
583 bool Done() const
584 {
585 return m_pos >= m_str.length();
586 }
587
588 void NextChar( int aAdvance = 1 )
589 {
590 m_pos += aAdvance;
591 }
592
593 size_t GetPos() const
594 {
595 return m_pos;
596 }
597
598 wxString GetString();
599
600 wxString GetChars( const std::function<bool( wxUniChar )>& cond ) const;
601
602 bool MatchAhead( const wxString& match,
603 const std::function<bool( wxUniChar )>& stopCond ) const;
604
605private:
606 wxString m_str;
607 size_t m_pos = 0;
608};
609
610
612{
613public:
614 COMPILER();
615 virtual ~COMPILER();
616
617 // We own at least one list of raw pointers. Don't let the compiler fill in copy c'tors that
618 // will only land us in trouble.
619 COMPILER( const COMPILER& ) = delete;
620 COMPILER& operator=( const COMPILER& ) = delete;
621
622 /*
623 * clear() should be invoked by the client if a new input string is to be processed. It
624 * will reset the parser. User defined variables are retained.
625 */
626 void Clear();
627
628 /* Used by the lemon parser */
629 void parseError( const char* s );
630 void parseOk();
631
632 int GetSourcePos() const { return m_sourcePos; }
633
634 void setRoot( LIBEVAL::TREE_NODE *root );
635 void freeTree( LIBEVAL::TREE_NODE *tree );
636
637 bool Compile( const wxString& aString, UCODE* aCode, CONTEXT* aPreflightContext );
638
639 void SetErrorCallback( std::function<void( const wxString& aMessage, int aOffset )> aCallback )
640 {
641 m_errorCallback = std::move( aCallback );
642 }
643
644 bool IsErrorPending() const { return m_errorStatus.pendingError; }
645 const ERROR_STATUS& GetError() const { return m_errorStatus; }
646
647 void GcItem( TREE_NODE* aItem ) { m_gcItems.push_back( aItem ); }
648 void GcItem( wxString* aItem ) { m_gcStrings.push_back( aItem ); }
649
650protected:
652 {
655 };
656
658
659 bool generateUCode( UCODE* aCode, CONTEXT* aPreflightContext );
660
661 void reportError( COMPILATION_STAGE stage, const wxString& aErrorMsg, int aPos = -1 );
662
663 /* Begin processing of a new input string */
664 void newString( const wxString& aString );
665
666 /* Tokenizer: Next token/value taken from input string. */
668 bool lexDefault( T_TOKEN& aToken );
669 bool lexString( T_TOKEN& aToken );
670
671 int resolveUnits();
672
673protected:
674 /* Token state for input string. */
675 void* m_parser; // the current lemon parser state machine
678
679 std::unique_ptr<UNIT_RESOLVER> m_unitResolver;
680
684
685 std::function<void( const wxString& aMessage, int aOffset )> m_errorCallback;
686
688
689 std::vector<TREE_NODE*> m_gcItems;
690 std::vector<wxString*> m_gcStrings;
691};
692
693
694} // namespace LIBEVAL
695
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".