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