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