KiCad PCB EDA Suite
Loading...
Searching...
No Matches
libeval_compiler.cpp
Go to the documentation of this file.
1/*
2 * This file is part of libeval, a simple math expression evaluator
3 *
4 * Copyright (C) 2017 Michael Geselbracht, [email protected]
5 * Copyright (C) 2019-2023 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#include <memory>
22#include <set>
23#include <vector>
24#include <algorithm>
25
26#include <eda_units.h>
27#include <string_utils.h>
28#include <wx/log.h>
29
30#ifdef DEBUG
31#include <cstdarg>
32#endif
33
35
36/* The (generated) lemon parser is written in C.
37 * In order to keep its symbol from the global namespace include the parser code with
38 * a C++ namespace.
39 */
40namespace LIBEVAL
41{
42
43#ifdef __GNUC__
44#pragma GCC diagnostic push
45#pragma GCC diagnostic ignored "-Wunused-variable"
46#pragma GCC diagnostic ignored "-Wsign-compare"
47#endif
48
49#include <libeval_compiler/grammar.c>
50#include <libeval_compiler/grammar.h>
51
52#ifdef __GNUC__
53#pragma GCC diagnostic pop
54#endif
55
56
57#define libeval_dbg(level, fmt, ...) \
58 wxLogTrace( "libeval_compiler", fmt, __VA_ARGS__ );
59
60
61TREE_NODE* newNode( LIBEVAL::COMPILER* compiler, int op, const T_TOKEN_VALUE& value )
62{
63 auto t2 = new TREE_NODE();
64
65 t2->valid = true;
66 t2->value.str = value.str ? new wxString( *value.str ) : nullptr;
67 t2->value.num = value.num;
68 t2->value.idx = value.idx;
69 t2->op = op;
70 t2->leaf[0] = nullptr;
71 t2->leaf[1] = nullptr;
72 t2->isTerminal = false;
73 t2->srcPos = compiler->GetSourcePos();
74 t2->uop = nullptr;
75
76 libeval_dbg(10, " ostr %p nstr %p nnode %p op %d", value.str, t2->value.str, t2, t2->op );
77
78 if(t2->value.str)
79 compiler->GcItem( t2->value.str );
80
81 compiler->GcItem( t2 );
82
83 return t2;
84}
85
86
87static const wxString formatOpName( int op )
88{
89 static const struct
90 {
91 int op;
92 wxString mnemonic;
93 }
94 simpleOps[] =
95 {
96 { TR_OP_MUL, "MUL" }, { TR_OP_DIV, "DIV" }, { TR_OP_ADD, "ADD" },
97 { TR_OP_SUB, "SUB" }, { TR_OP_LESS, "LESS" }, { TR_OP_GREATER, "GREATER" },
98 { TR_OP_LESS_EQUAL, "LESS_EQUAL" }, { TR_OP_GREATER_EQUAL, "GREATER_EQUAL" },
99 { TR_OP_EQUAL, "EQUAL" }, { TR_OP_NOT_EQUAL, "NEQUAL" }, { TR_OP_BOOL_AND, "AND" },
100 { TR_OP_BOOL_OR, "OR" }, { TR_OP_BOOL_NOT, "NOT" }, { -1, "" }
101 };
102
103 for( int i = 0; simpleOps[i].op >= 0; i++ )
104 {
105 if( simpleOps[i].op == op )
106 return simpleOps[i].mnemonic;
107 }
108
109 return "???";
110}
111
112
113bool VALUE::EqualTo( CONTEXT* aCtx, const VALUE* b ) const
114{
115 if( m_type == VT_UNDEFINED || b->m_type == VT_UNDEFINED )
116 return false;
117
118 if( m_type == VT_NULL && b->m_type == VT_NULL )
119 return true;
120
121 if( m_type == VT_NUMERIC && b->m_type == VT_NUMERIC )
122 {
123 return AsDouble() == b->AsDouble();
124 }
125 else if( m_type == VT_STRING && b->m_type == VT_STRING )
126 {
127 if( b->m_stringIsWildcard )
128 return WildCompareString( b->AsString(), AsString(), false );
129 else
130 return AsString().IsSameAs( b->AsString(), false );
131 }
132
133 return false;
134}
135
136
137bool VALUE::NotEqualTo( CONTEXT* aCtx, const VALUE* b ) const
138{
139 if( m_type == VT_UNDEFINED || b->m_type == VT_UNDEFINED )
140 return false;
141
142 return !EqualTo( aCtx, b );
143}
144
145
146wxString UOP::Format() const
147{
148 wxString str;
149
150 switch( m_op )
151 {
152 case TR_UOP_PUSH_VAR:
153 str = wxString::Format( "PUSH VAR [%p]", m_ref.get() );
154 break;
155
157 {
158 if( !m_value )
159 str = wxString::Format( "PUSH nullptr" );
160 else if( m_value->GetType() == VT_NUMERIC )
161 str = wxString::Format( "PUSH NUM [%.10f]", m_value->AsDouble() );
162 else
163 str = wxString::Format( "PUSH STR [%ls]", m_value->AsString() );
164 }
165 break;
166
168 str = wxString::Format( "MCALL" );
169 break;
170
171 case TR_OP_FUNC_CALL:
172 str = wxString::Format( "FCALL" );
173 break;
174
175 default:
176 str = wxString::Format( "%s %d", formatOpName( m_op ).c_str(), m_op );
177 break;
178 }
179
180 return str;
181}
182
183
185{
186 for ( auto op : m_ucode )
187 {
188 delete op;
189 }
190}
191
192
193wxString UCODE::Dump() const
194{
195 wxString rv;
196
197 for( auto op : m_ucode )
198 {
199 rv += op->Format();
200 rv += "\n";
201 }
202
203 return rv;
204};
205
206
208{
209 wxString rv;
210
211 while( m_pos < m_str.length() && m_str[ m_pos ] != '\'' )
212 {
213 if( m_str[ m_pos ] == '\\' && m_pos + 1 < m_str.length() && m_str[ m_pos + 1 ] == '\'' )
214 m_pos++;
215
216 rv.append( 1, m_str[ m_pos++ ] );
217 }
218
219 m_pos++;
220
221 return rv;
222}
223
224
225wxString TOKENIZER::GetChars( const std::function<bool( wxUniChar )>& cond ) const
226{
227 wxString rv;
228 size_t p = m_pos;
229
230 while( p < m_str.length() && cond( m_str[p] ) )
231 {
232 rv.append( 1, m_str[p] );
233 p++;
234 }
235
236 return rv;
237}
238
239bool TOKENIZER::MatchAhead( const wxString& match,
240 const std::function<bool( wxUniChar )>& stopCond ) const
241{
242 int remaining = (int) m_str.Length() - m_pos;
243
244 if( remaining < (int) match.length() )
245 return false;
246
247 if( m_str.substr( m_pos, match.length() ) == match )
248 return ( remaining == (int) match.length() || stopCond( m_str[m_pos + match.length()] ) );
249
250 return false;
251}
252
253
255 m_lexerState( COMPILER::LS_DEFAULT )
256{
258 m_sourcePos = 0;
259 m_parseFinished = false;
260 m_unitResolver = std::make_unique<UNIT_RESOLVER>();
261 m_parser = LIBEVAL::ParseAlloc( malloc );
262 m_tree = nullptr;
264}
265
266
268{
269 LIBEVAL::ParseFree( m_parser, free );
270
271 if( m_tree )
272 {
273 freeTree( m_tree );
274 m_tree = nullptr;
275 }
276
277 // Allow explicit call to destructor
278 m_parser = nullptr;
279
280 Clear();
281}
282
283
285{
286 //free( current.token );
288
289 if( m_tree )
290 {
291 freeTree( m_tree );
292 m_tree = nullptr;
293 }
294
295 m_tree = nullptr;
296
297 for( auto tok : m_gcItems )
298 delete tok;
299
300 for( auto tok: m_gcStrings )
301 delete tok;
302
303 m_gcItems.clear();
304 m_gcStrings.clear();
305}
306
307
308void COMPILER::parseError( const char* s )
309{
311}
312
313
315{
316 m_parseFinished = true;
317}
318
319
320bool COMPILER::Compile( const wxString& aString, UCODE* aCode, CONTEXT* aPreflightContext )
321{
322 // Feed parser token after token until end of input.
323
324 newString( aString );
325
326 if( m_tree )
327 {
328 freeTree( m_tree );
329 m_tree = nullptr;
330 }
331
332 m_tree = nullptr;
333 m_parseFinished = false;
334 T_TOKEN tok( defaultToken );
335
336 libeval_dbg(0, "str: '%s' empty: %d\n", aString.c_str(), !!aString.empty() );
337
338 if( aString.empty() )
339 {
340 m_parseFinished = true;
341 return generateUCode( aCode, aPreflightContext );
342 }
343
344 do
345 {
347
348 tok = getToken();
349
350 if( tok.value.str )
351 GcItem( tok.value.str );
352
353 libeval_dbg(10, "parse: tok %d valstr %p\n", tok.token, tok.value.str );
354 Parse( m_parser, tok.token, tok, this );
355
357 return false;
358
359 if( m_parseFinished || tok.token == G_ENDS )
360 {
361 // Reset parser by passing zero as token ID, value is ignored.
362 Parse( m_parser, 0, tok, this );
363 break;
364 }
365 } while( tok.token );
366
367 return generateUCode( aCode, aPreflightContext );
368}
369
370
371void COMPILER::newString( const wxString& aString )
372{
373 Clear();
374
376 m_tokenizer.Restart( aString );
377 m_parseFinished = false;
378}
379
380
382{
383 T_TOKEN rv;
385
386 bool done = false;
387
388 do
389 {
390 switch( m_lexerState )
391 {
392 case LS_DEFAULT:
393 done = lexDefault( rv );
394 break;
395 case LS_STRING:
396 done = lexString( rv );
397 break;
398 }
399 } while( !done );
400
401 return rv;
402}
403
404
406{
407 aToken.token = G_STRING;
408 aToken.value.str = new wxString( m_tokenizer.GetString() );
409
411 return true;
412}
413
414
416{
417 int unitId = 0;
418
419 for( const wxString& unitName : m_unitResolver->GetSupportedUnits() )
420 {
421 if( m_tokenizer.MatchAhead( unitName, []( int c ) -> bool { return !isalnum( c ); } ) )
422 {
423 libeval_dbg(10, "Match unit '%s'\n", unitName.c_str() );
424 m_tokenizer.NextChar( unitName.length() );
425 return unitId;
426 }
427
428 unitId++;
429 }
430
431 return -1;
432}
433
434
436{
437 T_TOKEN retval;
438 wxString current;
439 int convertFrom;
440 wxString msg;
441
442 retval.value.str = nullptr;
443 retval.value.num = 0.0;
444 retval.value.idx = -1;
445 retval.token = G_ENDS;
446
447 if( m_tokenizer.Done() )
448 {
449 aToken = retval;
450 return true;
451 }
452
453 auto isDecimalSeparator =
454 [&]( wxUniChar ch ) -> bool
455 {
456 return ( ch == m_localeDecimalSeparator || ch == '.' || ch == ',' );
457 };
458
459 // Lambda: get value as string, store into clToken.token and update current index.
460 auto extractNumber =
461 [&]()
462 {
463 bool haveSeparator = false;
464 wxUniChar ch = m_tokenizer.GetChar();
465
466 do
467 {
468 if( isDecimalSeparator( ch ) && haveSeparator )
469 break;
470
471 current.append( 1, ch );
472
473 if( isDecimalSeparator( ch ) )
474 haveSeparator = true;
475
477 ch = m_tokenizer.GetChar();
478 } while( isdigit( ch ) || isDecimalSeparator( ch ) );
479
480 // Ensure that the systems decimal separator is used
481 for( int i = current.length(); i; i-- )
482 {
483 if( isDecimalSeparator( current[i - 1] ) )
484 current[i - 1] = m_localeDecimalSeparator;
485 }
486 };
487
488
489 int ch;
490
491 // Start processing of first/next token: Remove whitespace
492 for( ;; )
493 {
494 ch = m_tokenizer.GetChar();
495
496 if( ch == ' ' )
498 else
499 break;
500 }
501
502 libeval_dbg(10, "LEX ch '%c' pos %lu\n", ch, (unsigned long)m_tokenizer.GetPos() );
503
504 if( ch == 0 )
505 {
506 /* End of input */
507 }
508 else if( isdigit( ch ) )
509 {
510 // VALUE
511 extractNumber();
512 retval.token = G_VALUE;
513 retval.value.str = new wxString( current );
514 }
515 else if( ( convertFrom = resolveUnits() ) >= 0 )
516 {
517 // UNIT
518 // Units are appended to a VALUE.
519 // Determine factor to default unit if unit for value is given.
520 // Example: Default is mm, unit is inch: factor is 25.4
521 // The factor is assigned to the terminal UNIT. The actual
522 // conversion is done within a parser action.
523 retval.token = G_UNIT;
524 retval.value.idx = convertFrom;
525 }
526 else if( ch == '\'' ) // string literal
527 {
530 return false;
531 }
532 else if( isalpha( ch ) || ch == '_' )
533 {
534 current = m_tokenizer.GetChars( []( int c ) -> bool { return isalnum( c ) || c == '_'; } );
535 retval.token = G_IDENTIFIER;
536 retval.value.str = new wxString( current );
537 m_tokenizer.NextChar( current.length() );
538 }
539 else if( m_tokenizer.MatchAhead( "==", []( int c ) -> bool { return c != '='; } ) )
540 {
541 retval.token = G_EQUAL;
543 }
544 else if( m_tokenizer.MatchAhead( "!=", []( int c ) -> bool { return c != '='; } ) )
545 {
546 retval.token = G_NOT_EQUAL;
548 }
549 else if( m_tokenizer.MatchAhead( "<=", []( int c ) -> bool { return c != '='; } ) )
550 {
551 retval.token = G_LESS_EQUAL_THAN;
553 }
554 else if( m_tokenizer.MatchAhead( ">=", []( int c ) -> bool { return c != '='; } ) )
555 {
556 retval.token = G_GREATER_EQUAL_THAN;
558 }
559 else if( m_tokenizer.MatchAhead( "&&", []( int c ) -> bool { return c != '&'; } ) )
560 {
561 retval.token = G_BOOL_AND;
563 }
564 else if( m_tokenizer.MatchAhead( "||", []( int c ) -> bool { return c != '|'; } ) )
565 {
566 retval.token = G_BOOL_OR;
568 }
569 else
570 {
571 // Single char tokens
572 switch( ch )
573 {
574 case '+': retval.token = G_PLUS; break;
575 case '!': retval.token = G_BOOL_NOT; break;
576 case '-': retval.token = G_MINUS; break;
577 case '*': retval.token = G_MULT; break;
578 case '/': retval.token = G_DIVIDE; break;
579 case '<': retval.token = G_LESS_THAN; break;
580 case '>': retval.token = G_GREATER_THAN; break;
581 case '(': retval.token = G_PARENL; break;
582 case ')': retval.token = G_PARENR; break;
583 case ';': retval.token = G_SEMCOL; break;
584 case '.': retval.token = G_STRUCT_REF; break;
585 case ',': retval.token = G_COMMA; break;
586
587 default:
588 reportError( CST_PARSE, wxString::Format( _( "Unrecognized character '%c'" ),
589 (char) ch ) );
590 break;
591 }
592
594 }
595
596 aToken = retval;
597 return true;
598}
599
600
601const wxString formatNode( TREE_NODE* node )
602{
603 return node->value.str ? *(node->value.str) : wxString( wxEmptyString );
604}
605
606
607void dumpNode( wxString& buf, TREE_NODE* tok, int depth = 0 )
608{
609 wxString str;
610
611 if( !tok )
612 return;
613
614 str.Printf( "\n[%p L0:%-20p L1:%-20p] ", tok, tok->leaf[0], tok->leaf[1] );
615 buf += str;
616
617 for( int i = 0; i < 2 * depth; i++ )
618 buf += " ";
619
620 if( tok->op & TR_OP_BINARY_MASK )
621 {
622 buf += formatOpName( tok->op );
623 dumpNode( buf, tok->leaf[0], depth + 1 );
624 dumpNode( buf, tok->leaf[1], depth + 1 );
625 }
626
627 switch( tok->op )
628 {
629 case TR_NUMBER:
630 buf += "NUMERIC: ";
631 buf += formatNode( tok );
632
633 if( tok->leaf[0] )
634 dumpNode( buf, tok->leaf[0], depth + 1 );
635
636 break;
637
638 case TR_ARG_LIST:
639 buf += "ARG_LIST: ";
640 buf += formatNode( tok );
641
642 if( tok->leaf[0] )
643 dumpNode( buf, tok->leaf[0], depth + 1 );
644 if( tok->leaf[1] )
645 dumpNode( buf, tok->leaf[1], depth + 1 );
646
647 break;
648
649 case TR_STRING:
650 buf += "STRING: ";
651 buf += formatNode( tok );
652 break;
653
654 case TR_IDENTIFIER:
655 buf += "ID: ";
656 buf += formatNode( tok );
657 break;
658
659 case TR_STRUCT_REF:
660 buf += "SREF: ";
661 dumpNode( buf, tok->leaf[0], depth + 1 );
662 dumpNode( buf, tok->leaf[1], depth + 1 );
663 break;
664
665 case TR_OP_FUNC_CALL:
666 buf += "CALL '";
667 buf += formatNode( tok->leaf[0] );
668 buf += "': ";
669 dumpNode( buf, tok->leaf[1], depth + 1 );
670 break;
671
672 case TR_UNIT:
673 str.Printf( "UNIT: %d ", tok->value.idx );
674 buf += str;
675 break;
676 }
677}
678
679
680void CONTEXT::ReportError( const wxString& aErrorMsg )
681{
682 if( m_errorCallback )
683 m_errorCallback( aErrorMsg, -1 );
684}
685
686
687void COMPILER::reportError( COMPILATION_STAGE stage, const wxString& aErrorMsg, int aPos )
688{
689 if( aPos == -1 )
690 aPos = m_sourcePos;
691
693 m_errorStatus.stage = stage;
694 m_errorStatus.message = aErrorMsg;
695 m_errorStatus.srcPos = aPos;
696
697 if( m_errorCallback )
698 m_errorCallback( aErrorMsg, aPos );
699}
700
701
703{
704 m_tree = root;
705}
706
707
709{
710 if ( tree->leaf[0] )
711 freeTree( tree->leaf[0] );
712
713 if ( tree->leaf[1] )
714 freeTree( tree->leaf[1] );
715
716 delete tree->uop;
717 tree->uop = nullptr;
718}
719
720
721void TREE_NODE::SetUop( int aOp, double aValue )
722{
723 delete uop;
724
725 std::unique_ptr<VALUE> val = std::make_unique<VALUE>( aValue );
726 uop = new UOP( aOp, std::move( val ) );
727}
728
729
730void TREE_NODE::SetUop( int aOp, const wxString& aValue, bool aStringIsWildcard )
731{
732 delete uop;
733
734 std::unique_ptr<VALUE> val = std::make_unique<VALUE>( aValue, aStringIsWildcard );
735 uop = new UOP( aOp, std::move( val ) );
736}
737
738
739void TREE_NODE::SetUop( int aOp, std::unique_ptr<VAR_REF> aRef )
740{
741 delete uop;
742
743 uop = new UOP( aOp, std::move( aRef ) );
744}
745
746
747void TREE_NODE::SetUop( int aOp, FUNC_CALL_REF aFunc, std::unique_ptr<VAR_REF> aRef )
748{
749 delete uop;
750
751 uop = new UOP( aOp, std::move( aFunc ), std::move( aRef ) );
752}
753
754
755static void prepareTree( LIBEVAL::TREE_NODE *node )
756{
757 node->isVisited = false;
758
759 // fixme: for reasons I don't understand the lemon parser isn't initializing the
760 // leaf node pointers of function name nodes. -JY
761 if( node->op == TR_OP_FUNC_CALL && node->leaf[0] )
762 {
763 node->leaf[0]->leaf[0] = nullptr;
764 node->leaf[0]->leaf[1] = nullptr;
765 }
766
767 if ( node->leaf[0] )
768 prepareTree( node->leaf[0] );
769
770 if ( node->leaf[1] )
771 prepareTree( node->leaf[1] );
772}
773
774static std::vector<TREE_NODE*> squashParamList( TREE_NODE* root )
775{
776 std::vector<TREE_NODE*> args;
777
778 if( !root )
779 {
780 return args;
781 }
782
783 if( root->op != TR_ARG_LIST && root->op != TR_NULL )
784 {
785 args.push_back( root );
786 }
787 else
788 {
789 TREE_NODE *n = root;
790 do
791 {
792 if( n->leaf[1] )
793 args.push_back(n->leaf[1]);
794
795 n = n->leaf[0];
796 } while ( n && n->op == TR_ARG_LIST );
797
798 if( n )
799 {
800 args.push_back( n );
801 }
802 }
803
804 std::reverse( args.begin(), args.end() );
805
806 for( size_t i = 0; i < args.size(); i++ )
807 libeval_dbg( 10, "squash arg%d: %s\n", int( i ), formatNode( args[i] ) );
808
809 return args;
810}
811
812
813bool COMPILER::generateUCode( UCODE* aCode, CONTEXT* aPreflightContext )
814{
815 std::vector<TREE_NODE*> stack;
816 wxString msg;
817
818 if( !m_tree )
819 {
820 std::unique_ptr<VALUE> val = std::make_unique<VALUE>( 1.0 );
821 // Empty expression returns true
822 aCode->AddOp( new UOP( TR_UOP_PUSH_VALUE, std::move(val) ) );
823 return true;
824 }
825
827
828 stack.push_back( m_tree );
829
830 wxString dump;
831
832 dumpNode( dump, m_tree, 0 );
833 libeval_dbg( 3, "Tree dump:\n%s\n\n", (const char*) dump.c_str() );
834
835 while( !stack.empty() )
836 {
837 TREE_NODE* node = stack.back();
838
839 libeval_dbg( 4, "process node %p [op %d] [stack %lu]\n",
840 node, node->op, (unsigned long)stack.size() );
841
842 // process terminal nodes first
843 switch( node->op )
844 {
845 case TR_OP_FUNC_CALL:
846 // Function call's uop was generated inside TR_STRUCT_REF
847 if( !node->uop )
848 {
849 reportError( CST_CODEGEN, _( "Unknown parent of function parameters" ),
850 node->srcPos );
851 }
852
853 node->isTerminal = true;
854 break;
855
856 case TR_STRUCT_REF:
857 {
858 // leaf[0]: object
859 // leaf[1]: field (TR_IDENTIFIER) or TR_OP_FUNC_CALL
860
861 if( node->leaf[0]->op != TR_IDENTIFIER )
862 {
863 int pos = node->leaf[0]->srcPos;
864
865 if( node->leaf[0]->value.str )
866 pos -= static_cast<int>( formatNode( node->leaf[0] ).length() );
867
868 reportError( CST_CODEGEN, _( "Unknown parent of property" ), pos );
869
870 node->leaf[0]->isVisited = true;
871 node->leaf[1]->isVisited = true;
872
873 node->SetUop( TR_UOP_PUSH_VALUE, 0.0 );
874 node->isTerminal = true;
875 break;
876 }
877
878 switch( node->leaf[1]->op )
879 {
880 case TR_IDENTIFIER:
881 {
882 // leaf[0]: object
883 // leaf[1]: field
884
885 wxString itemName = formatNode( node->leaf[0] );
886 wxString propName = formatNode( node->leaf[1] );
887 std::unique_ptr<VAR_REF> vref = aCode->CreateVarRef( itemName, propName );
888
889 if( !vref )
890 {
891 msg.Printf( _( "Unrecognized item '%s'" ), itemName );
893 node->leaf[0]->srcPos - (int) itemName.length() );
894 }
895 else if( vref->GetType() == VT_PARSE_ERROR )
896 {
897 msg.Printf( _( "Unrecognized property '%s'" ), propName );
899 node->leaf[1]->srcPos - (int) propName.length() );
900 }
901
902 node->leaf[0]->isVisited = true;
903 node->leaf[1]->isVisited = true;
904
905 node->SetUop( TR_UOP_PUSH_VAR, std::move( vref ) );
906 node->isTerminal = true;
907 break;
908 }
909 case TR_OP_FUNC_CALL:
910 {
911 // leaf[0]: object
912 // leaf[1]: TR_OP_FUNC_CALL
913 // leaf[0]: function name
914 // leaf[1]: parameter
915
916 wxString itemName = formatNode( node->leaf[0] );
917 std::unique_ptr<VAR_REF> vref = aCode->CreateVarRef( itemName, "" );
918
919 if( !vref )
920 {
921 msg.Printf( _( "Unrecognized item '%s'" ), itemName );
923 node->leaf[0]->srcPos - (int) itemName.length() );
924 }
925
926 wxString functionName = formatNode( node->leaf[1]->leaf[0] );
927 auto func = aCode->CreateFuncCall( functionName );
928 std::vector<TREE_NODE*> params = squashParamList( node->leaf[1]->leaf[1] );
929
930 libeval_dbg( 10, "emit func call: %s\n", functionName );
931
932 if( !func )
933 {
934 msg.Printf( _( "Unrecognized function '%s'" ), functionName );
935 reportError( CST_CODEGEN, msg, node->leaf[0]->srcPos + 1 );
936 }
937
938 if( func )
939 {
940 // Preflight the function call
941
942 for( TREE_NODE* pnode : params )
943 {
944 VALUE* param = aPreflightContext->AllocValue();
945 param->Set( formatNode( pnode ) );
946 aPreflightContext->Push( param );
947 }
948
949 aPreflightContext->SetErrorCallback(
950 [&]( const wxString& aMessage, int aOffset )
951 {
952 size_t loc = node->leaf[1]->leaf[1]->srcPos;
953 reportError( CST_CODEGEN, aMessage, (int) loc - 1 );
954 } );
955
956 try
957 {
958 func( aPreflightContext, vref.get() );
959 aPreflightContext->Pop(); // return value
960 }
961 catch( ... )
962 {
963 }
964 }
965
966 node->leaf[0]->isVisited = true;
967 node->leaf[1]->isVisited = true;
968 node->leaf[1]->leaf[0]->isVisited = true;
969 node->leaf[1]->leaf[1]->isVisited = true;
970
971 // Our non-terminal-node stacking algorithm can't handle doubly-nested
972 // structures so we need to pop a level by replacing the TR_STRUCT_REF with
973 // a TR_OP_FUNC_CALL and its function parameter
974 stack.pop_back();
975 stack.push_back( node->leaf[1] );
976
977 for( TREE_NODE* pnode : params )
978 stack.push_back( pnode );
979
980 node->leaf[1]->SetUop( TR_OP_METHOD_CALL, func, std::move( vref ) );
981 node->isTerminal = false;
982 break;
983 }
984
985 default:
986 // leaf[0]: object
987 // leaf[1]: malformed syntax
988
989 wxString itemName = formatNode( node->leaf[0] );
990 wxString propName = formatNode( node->leaf[1] );
991 std::unique_ptr<VAR_REF> vref = aCode->CreateVarRef( itemName, propName );
992
993 if( !vref )
994 {
995 msg.Printf( _( "Unrecognized item '%s'" ), itemName );
997 node->leaf[0]->srcPos - (int) itemName.length() );
998 }
999
1000 msg.Printf( _( "Unrecognized property '%s'" ), propName );
1001 reportError( CST_CODEGEN, msg, node->leaf[0]->srcPos + 1 );
1002
1003 node->leaf[0]->isVisited = true;
1004 node->leaf[1]->isVisited = true;
1005
1006 node->SetUop( TR_UOP_PUSH_VALUE, 0.0 );
1007 node->isTerminal = true;
1008 break;
1009 }
1010
1011 break;
1012 }
1013
1014 case TR_NUMBER:
1015 {
1016 TREE_NODE* son = node->leaf[0];
1017 double value;
1018
1019 if( !node->value.str )
1020 {
1021 value = 0.0;
1022 }
1023 else if( son && son->op == TR_UNIT )
1024 {
1025 if( m_unitResolver->GetSupportedUnits().empty() )
1026 {
1027 msg.Printf( _( "Unexpected units for '%s'" ), formatNode( node ) );
1028 reportError( CST_CODEGEN, msg, node->srcPos );
1029 }
1030
1031 int units = son->value.idx;
1032 value = m_unitResolver->Convert( formatNode( node ), units );
1033 son->isVisited = true;
1034 }
1035 else
1036 {
1037 if( !m_unitResolver->GetSupportedUnitsMessage().empty() )
1038 {
1039 msg.Printf( _( "Missing units for '%s'| (%s)" ),
1040 formatNode( node ),
1041 m_unitResolver->GetSupportedUnitsMessage() );
1042 reportError( CST_CODEGEN, msg, node->srcPos );
1043 }
1044
1046 }
1047
1048 node->SetUop( TR_UOP_PUSH_VALUE, value );
1049 node->isTerminal = true;
1050 break;
1051 }
1052
1053 case TR_STRING:
1054 {
1055 wxString str = formatNode( node );
1056 bool isWildcard = str.Contains("?") || str.Contains("*");
1057 node->SetUop( TR_UOP_PUSH_VALUE, str, isWildcard );
1058 node->isTerminal = true;
1059 break;
1060 }
1061
1062 case TR_IDENTIFIER:
1063 {
1064 std::unique_ptr<VAR_REF> vref = aCode->CreateVarRef( formatNode( node ), "" );
1065
1066 if( !vref )
1067 {
1068 msg.Printf( _( "Unrecognized item '%s'" ), formatNode( node ) );
1069 reportError( CST_CODEGEN, msg, node->srcPos - (int) formatNode( node ).length() );
1070 }
1071
1072 node->SetUop( TR_UOP_PUSH_VAR, std::move( vref ) );
1073 node->isTerminal = true;
1074 break;
1075 }
1076
1077 default:
1078 node->SetUop( node->op );
1079 node->isTerminal = ( !node->leaf[0] || node->leaf[0]->isVisited )
1080 && ( !node->leaf[1] || node->leaf[1]->isVisited );
1081 break;
1082 }
1083
1084 if( !node->isTerminal )
1085 {
1086 if( node->leaf[0] && !node->leaf[0]->isVisited )
1087 {
1088 stack.push_back( node->leaf[0] );
1089 node->leaf[0]->isVisited = true;
1090 continue;
1091 }
1092 else if( node->leaf[1] && !node->leaf[1]->isVisited )
1093 {
1094 stack.push_back( node->leaf[1] );
1095 node->leaf[1]->isVisited = true;
1096 }
1097
1098 continue;
1099 }
1100
1101 node->isVisited = true;
1102
1103 if( node->uop )
1104 {
1105 aCode->AddOp( node->uop );
1106 node->uop = nullptr;
1107 }
1108
1109 stack.pop_back();
1110 }
1111
1112 libeval_dbg(2,"dump: \n%s\n", aCode->Dump().c_str() );
1113
1114 return true;
1115}
1116
1117
1118void UOP::Exec( CONTEXT* ctx )
1119{
1120 switch( m_op )
1121 {
1122 case TR_UOP_PUSH_VAR:
1123 {
1124 VALUE* value = nullptr;
1125
1126 if( m_ref )
1127 value = ctx->StoreValue( m_ref->GetValue( ctx ) );
1128 else
1129 value = ctx->AllocValue();
1130
1131 ctx->Push( value );
1132 }
1133 break;
1134
1135 case TR_UOP_PUSH_VALUE:
1136 ctx->Push( m_value.get() );
1137 return;
1138
1139 case TR_OP_METHOD_CALL:
1140 m_func( ctx, m_ref.get() );
1141 return;
1142
1143 default:
1144 break;
1145 }
1146
1147#define AS_DOUBLE( arg ) ( arg ? arg->AsDouble() : 0.0 )
1148
1149 if( m_op & TR_OP_BINARY_MASK )
1150 {
1151 LIBEVAL::VALUE* arg2 = ctx->Pop();
1152 LIBEVAL::VALUE* arg1 = ctx->Pop();
1153 double result;
1154
1155 if( ctx->HasErrorCallback() )
1156 {
1157 if( arg1 && arg1->GetType() == VT_STRING && arg2 && arg2->GetType() == VT_NUMERIC )
1158 {
1159 ctx->ReportError( wxString::Format( _( "Type mismatch between '%s' and %lf" ),
1160 arg1->AsString(),
1161 arg2->AsDouble() ) );
1162 }
1163 else if( arg1 && arg1->GetType() == VT_NUMERIC && arg2 && arg2->GetType() == VT_STRING )
1164 {
1165 ctx->ReportError( wxString::Format( _( "Type mismatch between %lf and '%s'" ),
1166 arg1->AsDouble(),
1167 arg2->AsString() ) );
1168 }
1169 }
1170
1171 switch( m_op )
1172 {
1173 case TR_OP_ADD:
1174 result = AS_DOUBLE( arg1 ) + AS_DOUBLE( arg2 );
1175 break;
1176 case TR_OP_SUB:
1177 result = AS_DOUBLE( arg1 ) - AS_DOUBLE( arg2 );
1178 break;
1179 case TR_OP_MUL:
1180 result = AS_DOUBLE( arg1 ) * AS_DOUBLE( arg2 );
1181 break;
1182 case TR_OP_DIV:
1183 result = AS_DOUBLE( arg1 ) / AS_DOUBLE( arg2 );
1184 break;
1185 case TR_OP_LESS_EQUAL:
1186 result = AS_DOUBLE( arg1 ) <= AS_DOUBLE( arg2 ) ? 1 : 0;
1187 break;
1189 result = AS_DOUBLE( arg1 ) >= AS_DOUBLE( arg2 ) ? 1 : 0;
1190 break;
1191 case TR_OP_LESS:
1192 result = AS_DOUBLE( arg1 ) < AS_DOUBLE( arg2 ) ? 1 : 0;
1193 break;
1194 case TR_OP_GREATER:
1195 result = AS_DOUBLE( arg1 ) > AS_DOUBLE( arg2 ) ? 1 : 0;
1196 break;
1197 case TR_OP_EQUAL:
1198 if( !arg1 || !arg2 )
1199 result = arg1 == arg2 ? 1 : 0;
1200 else if( arg2->GetType() == VT_UNDEFINED )
1201 result = arg2->EqualTo( ctx, arg1 ) ? 1 : 0;
1202 else
1203 result = arg1->EqualTo( ctx, arg2 ) ? 1 : 0;
1204 break;
1205 case TR_OP_NOT_EQUAL:
1206 if( !arg1 || !arg2 )
1207 result = arg1 != arg2 ? 1 : 0;
1208 else if( arg2->GetType() == VT_UNDEFINED )
1209 result = arg2->NotEqualTo( ctx, arg1 ) ? 1 : 0;
1210 else
1211 result = arg1->NotEqualTo( ctx, arg2 ) ? 1 : 0;
1212 break;
1213 case TR_OP_BOOL_AND:
1214 result = AS_DOUBLE( arg1 ) != 0.0 && AS_DOUBLE( arg2 ) != 0.0 ? 1 : 0;
1215 break;
1216 case TR_OP_BOOL_OR:
1217 result = AS_DOUBLE( arg1 ) != 0.0 || AS_DOUBLE( arg2 ) != 0.0 ? 1 : 0;
1218 break;
1219 default:
1220 result = 0.0;
1221 break;
1222 }
1223
1224 auto rp = ctx->AllocValue();
1225 rp->Set( result );
1226 ctx->Push( rp );
1227 return;
1228 }
1229 else if( m_op & TR_OP_UNARY_MASK )
1230 {
1231 LIBEVAL::VALUE* arg1 = ctx->Pop();
1232 double ARG1VALUE = arg1 ? arg1->AsDouble() : 0.0;
1233 double result;
1234
1235 switch( m_op )
1236 {
1237 case TR_OP_BOOL_NOT:
1238 result = ARG1VALUE != 0.0 ? 0 : 1;
1239 break;
1240 default:
1241 result = ARG1VALUE != 0.0 ? 1 : 0;
1242 break;
1243 }
1244
1245 auto rp = ctx->AllocValue();
1246 rp->Set( result );
1247 ctx->Push( rp );
1248 return;
1249 }
1250}
1251
1252
1254{
1255 static VALUE g_false( 0 );
1256
1257 try
1258 {
1259 for( UOP* op : m_ucode )
1260 op->Exec( ctx );
1261 }
1262 catch(...)
1263 {
1264 // rules which fail outright should not be fired
1265 return &g_false;
1266 }
1267
1268 if( ctx->SP() == 1 )
1269 {
1270 return ctx->Pop();
1271 }
1272 else
1273 {
1274 // If stack is corrupted after execution it suggests a problem with the compiler, not
1275 // the rule....
1276
1277 // do not use "assert"; it crashes outright on OSX
1278 wxASSERT( ctx->SP() == 1 );
1279
1280 // non-well-formed rules should not be fired on a release build
1281 return &g_false;
1282 }
1283}
1284
1285
1286} // namespace LIBEVAL
std::unique_ptr< UNIT_RESOLVER > m_unitResolver
void newString(const wxString &aString)
void freeTree(LIBEVAL::TREE_NODE *tree)
bool lexString(T_TOKEN &aToken)
void GcItem(TREE_NODE *aItem)
LEXER_STATE m_lexerState
bool generateUCode(UCODE *aCode, CONTEXT *aPreflightContext)
std::function< void(const wxString &aMessage, int aOffset)> m_errorCallback
bool lexDefault(T_TOKEN &aToken)
int GetSourcePos() const
std::vector< TREE_NODE * > m_gcItems
void reportError(COMPILATION_STAGE stage, const wxString &aErrorMsg, int aPos=-1)
void setRoot(LIBEVAL::TREE_NODE *root)
std::vector< wxString * > m_gcStrings
bool Compile(const wxString &aString, UCODE *aCode, CONTEXT *aPreflightContext)
ERROR_STATUS m_errorStatus
void parseError(const char *s)
VALUE * StoreValue(VALUE *aValue)
void ReportError(const wxString &aErrorMsg)
std::function< void(const wxString &aMessage, int aOffset)> m_errorCallback
void SetErrorCallback(std::function< void(const wxString &aMessage, int aOffset)> aCallback)
void Push(VALUE *v)
void NextChar(int aAdvance=1)
void Restart(const wxString &aStr)
wxString GetChars(const std::function< bool(wxUniChar)> &cond) const
size_t GetPos() const
bool MatchAhead(const wxString &match, const std::function< bool(wxUniChar)> &stopCond) const
TREE_NODE * leaf[2]
void SetUop(int aOp, double aValue)
virtual std::unique_ptr< VAR_REF > CreateVarRef(const wxString &var, const wxString &field)
void AddOp(UOP *uop)
std::vector< UOP * > m_ucode
wxString Dump() const
VALUE * Run(CONTEXT *ctx)
virtual FUNC_CALL_REF CreateFuncCall(const wxString &name)
std::unique_ptr< VAR_REF > m_ref
FUNC_CALL_REF m_func
wxString Format() const
void Exec(CONTEXT *ctx)
std::unique_ptr< VALUE > m_value
void Set(double aValue)
virtual const wxString & AsString() const
virtual bool NotEqualTo(CONTEXT *aCtx, const VALUE *b) const
virtual double AsDouble() const
VAR_TYPE_T GetType() const
virtual bool EqualTo(CONTEXT *aCtx, const VALUE *b) const
#define _(s)
#define libeval_dbg(level, fmt,...)
#define AS_DOUBLE(arg)
#define TR_OP_GREATER_EQUAL
#define TR_OP_BOOL_AND
#define TR_OP_MUL
#define TR_UOP_PUSH_VAR
#define TR_UOP_PUSH_VALUE
#define TR_OP_BOOL_OR
#define TR_OP_GREATER
#define TR_OP_EQUAL
#define TR_OP_ADD
#define TR_OP_FUNC_CALL
#define TR_OP_SUB
#define TR_OP_METHOD_CALL
#define TR_OP_UNARY_MASK
#define TR_OP_LESS_EQUAL
#define TR_OP_BINARY_MASK
#define TR_OP_LESS
#define TR_OP_DIV
#define TR_OP_NOT_EQUAL
#define TR_OP_BOOL_NOT
KICOMMON_API double DoubleValueFromString(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Function DoubleValueFromString converts aTextValue to a double.
Definition: eda_units.cpp:576
TREE_NODE * newNode(LIBEVAL::COMPILER *compiler, int op, const T_TOKEN_VALUE &value)
static std::vector< TREE_NODE * > squashParamList(TREE_NODE *root)
constexpr T_TOKEN defaultToken
void dumpNode(wxString &buf, TREE_NODE *tok, int depth=0)
constexpr T_TOKEN_VALUE defaultTokenValue
std::function< void(CONTEXT *, void *)> FUNC_CALL_REF
static const wxString formatOpName(int op)
const wxString formatNode(TREE_NODE *node)
static void prepareTree(LIBEVAL::TREE_NODE *node)
bool WildCompareString(const wxString &pattern, const wxString &string_to_tst, bool case_sensitive)
Compare a string against wild card (* and ?) pattern using the usual rules.
COMPILATION_STAGE stage
T_TOKEN_VALUE value
wxString dump(const wxArrayString &aArray)
Debug helper for printing wxArrayString contents.