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