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