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, EDA_UNITS aUnits )
723{
724 delete uop;
725
726 std::unique_ptr<VALUE> val = std::make_unique<VALUE>( aValue );
727 val->SetUnits( aUnits );
728 uop = new UOP( aOp, std::move( val ) );
729}
730
731
732void TREE_NODE::SetUop( int aOp, const wxString& aValue, bool aStringIsWildcard )
733{
734 delete uop;
735
736 std::unique_ptr<VALUE> val = std::make_unique<VALUE>( aValue, aStringIsWildcard );
737 uop = new UOP( aOp, std::move( val ) );
738}
739
740
741void TREE_NODE::SetUop( int aOp, std::unique_ptr<VAR_REF> aRef )
742{
743 delete uop;
744
745 uop = new UOP( aOp, std::move( aRef ) );
746}
747
748
749void TREE_NODE::SetUop( int aOp, FUNC_CALL_REF aFunc, std::unique_ptr<VAR_REF> aRef )
750{
751 delete uop;
752
753 uop = new UOP( aOp, std::move( aFunc ), std::move( aRef ) );
754}
755
756
757static void prepareTree( LIBEVAL::TREE_NODE *node )
758{
759 node->isVisited = false;
760
761 // fixme: for reasons I don't understand the lemon parser isn't initializing the
762 // leaf node pointers of function name nodes. -JY
763 if( node->op == TR_OP_FUNC_CALL && node->leaf[0] )
764 {
765 node->leaf[0]->leaf[0] = nullptr;
766 node->leaf[0]->leaf[1] = nullptr;
767 }
768
769 if ( node->leaf[0] )
770 prepareTree( node->leaf[0] );
771
772 if ( node->leaf[1] )
773 prepareTree( node->leaf[1] );
774}
775
776
777static std::vector<TREE_NODE*> squashParamList( TREE_NODE* root )
778{
779 std::vector<TREE_NODE*> args;
780
781 if( !root )
782 {
783 return args;
784 }
785
786 if( root->op != TR_ARG_LIST && root->op != TR_NULL )
787 {
788 args.push_back( root );
789 }
790 else
791 {
792 TREE_NODE *n = root;
793 do
794 {
795 if( n->leaf[1] )
796 args.push_back(n->leaf[1]);
797
798 n = n->leaf[0];
799 } while ( n && n->op == TR_ARG_LIST );
800
801 if( n )
802 {
803 args.push_back( n );
804 }
805 }
806
807 std::reverse( args.begin(), args.end() );
808
809 for( size_t i = 0; i < args.size(); i++ )
810 libeval_dbg( 10, "squash arg%d: %s\n", int( i ), formatNode( args[i] ) );
811
812 return args;
813}
814
815
816bool COMPILER::generateUCode( UCODE* aCode, CONTEXT* aPreflightContext )
817{
818 std::vector<TREE_NODE*> stack;
819 wxString msg;
820
821 if( !m_tree )
822 {
823 std::unique_ptr<VALUE> val = std::make_unique<VALUE>( 1.0 );
824 // Empty expression returns true
825 aCode->AddOp( new UOP( TR_UOP_PUSH_VALUE, std::move(val) ) );
826 return true;
827 }
828
830
831 stack.push_back( m_tree );
832
833 wxString dump;
834
835 dumpNode( dump, m_tree, 0 );
836 libeval_dbg( 3, "Tree dump:\n%s\n\n", (const char*) dump.c_str() );
837
838 while( !stack.empty() )
839 {
840 TREE_NODE* node = stack.back();
841
842 libeval_dbg( 4, "process node %p [op %d] [stack %lu]\n",
843 node, node->op, (unsigned long)stack.size() );
844
845 // process terminal nodes first
846 switch( node->op )
847 {
848 case TR_OP_FUNC_CALL:
849 // Function call's uop was generated inside TR_STRUCT_REF
850 if( !node->uop )
851 {
852 reportError( CST_CODEGEN, _( "Unknown parent of function parameters" ),
853 node->srcPos );
854 }
855
856 node->isTerminal = true;
857 break;
858
859 case TR_STRUCT_REF:
860 {
861 // leaf[0]: object
862 // leaf[1]: field (TR_IDENTIFIER) or TR_OP_FUNC_CALL
863
864 if( node->leaf[0]->op != TR_IDENTIFIER )
865 {
866 int pos = node->leaf[0]->srcPos;
867
868 if( node->leaf[0]->value.str )
869 pos -= static_cast<int>( formatNode( node->leaf[0] ).length() );
870
871 reportError( CST_CODEGEN, _( "Unknown parent of property" ), pos );
872
873 node->leaf[0]->isVisited = true;
874 node->leaf[1]->isVisited = true;
875
876 node->SetUop( TR_UOP_PUSH_VALUE, 0.0, EDA_UNITS::UNSCALED );
877 node->isTerminal = true;
878 break;
879 }
880
881 switch( node->leaf[1]->op )
882 {
883 case TR_IDENTIFIER:
884 {
885 // leaf[0]: object
886 // leaf[1]: field
887
888 wxString itemName = formatNode( node->leaf[0] );
889 wxString propName = formatNode( node->leaf[1] );
890 std::unique_ptr<VAR_REF> vref = aCode->CreateVarRef( itemName, propName );
891
892 if( !vref )
893 {
894 msg.Printf( _( "Unrecognized item '%s'" ), itemName );
896 node->leaf[0]->srcPos - (int) itemName.length() );
897 }
898 else if( vref->GetType() == VT_PARSE_ERROR )
899 {
900 msg.Printf( _( "Unrecognized property '%s'" ), propName );
902 node->leaf[1]->srcPos - (int) propName.length() );
903 }
904
905 node->leaf[0]->isVisited = true;
906 node->leaf[1]->isVisited = true;
907
908 node->SetUop( TR_UOP_PUSH_VAR, std::move( vref ) );
909 node->isTerminal = true;
910 break;
911 }
912 case TR_OP_FUNC_CALL:
913 {
914 // leaf[0]: object
915 // leaf[1]: TR_OP_FUNC_CALL
916 // leaf[0]: function name
917 // leaf[1]: parameter
918
919 wxString itemName = formatNode( node->leaf[0] );
920 std::unique_ptr<VAR_REF> vref = aCode->CreateVarRef( itemName, "" );
921
922 if( !vref )
923 {
924 msg.Printf( _( "Unrecognized item '%s'" ), itemName );
926 node->leaf[0]->srcPos - (int) itemName.length() );
927 }
928
929 wxString functionName = formatNode( node->leaf[1]->leaf[0] );
930 auto func = aCode->CreateFuncCall( functionName );
931 std::vector<TREE_NODE*> params = squashParamList( node->leaf[1]->leaf[1] );
932
933 libeval_dbg( 10, "emit func call: %s\n", functionName );
934
935 if( !func )
936 {
937 msg.Printf( _( "Unrecognized function '%s'" ), functionName );
938 reportError( CST_CODEGEN, msg, node->leaf[0]->srcPos + 1 );
939 }
940
941 if( func )
942 {
943 // Preflight the function call
944
945 for( TREE_NODE* pnode : params )
946 {
947 VALUE* param = aPreflightContext->AllocValue();
948 param->Set( formatNode( pnode ) );
949 aPreflightContext->Push( param );
950 }
951
952 aPreflightContext->SetErrorCallback(
953 [&]( const wxString& aMessage, int aOffset )
954 {
955 size_t loc = node->leaf[1]->leaf[1]->srcPos;
956 reportError( CST_CODEGEN, aMessage, (int) loc - 1 );
957 } );
958
959 try
960 {
961 func( aPreflightContext, vref.get() );
962 aPreflightContext->Pop(); // return value
963 }
964 catch( ... )
965 {
966 }
967 }
968
969 node->leaf[0]->isVisited = true;
970 node->leaf[1]->isVisited = true;
971 node->leaf[1]->leaf[0]->isVisited = true;
972 node->leaf[1]->leaf[1]->isVisited = true;
973
974 // Our non-terminal-node stacking algorithm can't handle doubly-nested
975 // structures so we need to pop a level by replacing the TR_STRUCT_REF with
976 // a TR_OP_FUNC_CALL and its function parameter
977 stack.pop_back();
978 stack.push_back( node->leaf[1] );
979
980 for( TREE_NODE* pnode : params )
981 stack.push_back( pnode );
982
983 node->leaf[1]->SetUop( TR_OP_METHOD_CALL, func, std::move( vref ) );
984 node->isTerminal = false;
985 break;
986 }
987
988 default:
989 // leaf[0]: object
990 // leaf[1]: malformed syntax
991
992 wxString itemName = formatNode( node->leaf[0] );
993 wxString propName = formatNode( node->leaf[1] );
994 std::unique_ptr<VAR_REF> vref = aCode->CreateVarRef( itemName, propName );
995
996 if( !vref )
997 {
998 msg.Printf( _( "Unrecognized item '%s'" ), itemName );
1000 node->leaf[0]->srcPos - (int) itemName.length() );
1001 }
1002
1003 msg.Printf( _( "Unrecognized property '%s'" ), propName );
1004 reportError( CST_CODEGEN, msg, node->leaf[0]->srcPos + 1 );
1005
1006 node->leaf[0]->isVisited = true;
1007 node->leaf[1]->isVisited = true;
1008
1009 node->SetUop( TR_UOP_PUSH_VALUE, 0.0, EDA_UNITS::UNSCALED );
1010 node->isTerminal = true;
1011 break;
1012 }
1013
1014 break;
1015 }
1016
1017 case TR_NUMBER:
1018 {
1019 TREE_NODE* son = node->leaf[0];
1020 double value;
1021 EDA_UNITS unitsType = EDA_UNITS::UNSCALED;
1022
1023 if( !node->value.str )
1024 {
1025 value = 0.0;
1026 }
1027 else if( son && son->op == TR_UNIT )
1028 {
1029 if( m_unitResolver->GetSupportedUnits().empty() )
1030 {
1031 msg.Printf( _( "Unexpected units for '%s'" ), formatNode( node ) );
1032 reportError( CST_CODEGEN, msg, node->srcPos );
1033 }
1034
1035 int units = son->value.idx;
1036 unitsType = m_unitResolver->GetSupportedUnitsTypes().at( units );
1037 value = m_unitResolver->Convert( formatNode( node ), units );
1038 son->isVisited = true;
1039 }
1040 else
1041 {
1042 if( !m_unitResolver->GetSupportedUnitsMessage().empty() )
1043 {
1044 msg.Printf( _( "Missing units for '%s'| (%s)" ),
1045 formatNode( node ),
1046 m_unitResolver->GetSupportedUnitsMessage() );
1047 reportError( CST_CODEGEN, msg, node->srcPos );
1048 }
1049
1051 }
1052
1053 node->SetUop( TR_UOP_PUSH_VALUE, value, unitsType );
1054 node->isTerminal = true;
1055 break;
1056 }
1057
1058 case TR_STRING:
1059 {
1060 wxString str = formatNode( node );
1061 bool isWildcard = str.Contains("?") || str.Contains("*");
1062 node->SetUop( TR_UOP_PUSH_VALUE, str, isWildcard );
1063 node->isTerminal = true;
1064 break;
1065 }
1066
1067 case TR_IDENTIFIER:
1068 {
1069 std::unique_ptr<VAR_REF> vref = aCode->CreateVarRef( formatNode( node ), "" );
1070
1071 if( !vref )
1072 {
1073 msg.Printf( _( "Unrecognized item '%s'" ), formatNode( node ) );
1074 reportError( CST_CODEGEN, msg, node->srcPos - (int) formatNode( node ).length() );
1075 }
1076
1077 node->SetUop( TR_UOP_PUSH_VAR, std::move( vref ) );
1078 node->isTerminal = true;
1079 break;
1080 }
1081
1082 default:
1083 node->SetUop( node->op );
1084 node->isTerminal = ( !node->leaf[0] || node->leaf[0]->isVisited )
1085 && ( !node->leaf[1] || node->leaf[1]->isVisited );
1086 break;
1087 }
1088
1089 if( !node->isTerminal )
1090 {
1091 if( node->leaf[0] && !node->leaf[0]->isVisited )
1092 {
1093 stack.push_back( node->leaf[0] );
1094 node->leaf[0]->isVisited = true;
1095 continue;
1096 }
1097 else if( node->leaf[1] && !node->leaf[1]->isVisited )
1098 {
1099 stack.push_back( node->leaf[1] );
1100 node->leaf[1]->isVisited = true;
1101 }
1102
1103 continue;
1104 }
1105
1106 node->isVisited = true;
1107
1108 if( node->uop )
1109 {
1110 aCode->AddOp( node->uop );
1111 node->uop = nullptr;
1112 }
1113
1114 stack.pop_back();
1115 }
1116
1117 libeval_dbg(2,"dump: \n%s\n", aCode->Dump().c_str() );
1118
1119 return true;
1120}
1121
1122
1123void UOP::Exec( CONTEXT* ctx )
1124{
1125 switch( m_op )
1126 {
1127 case TR_UOP_PUSH_VAR:
1128 {
1129 VALUE* value = nullptr;
1130
1131 if( m_ref )
1132 value = ctx->StoreValue( m_ref->GetValue( ctx ) );
1133 else
1134 value = ctx->AllocValue();
1135
1136 ctx->Push( value );
1137 }
1138 break;
1139
1140 case TR_UOP_PUSH_VALUE:
1141 ctx->Push( m_value.get() );
1142 return;
1143
1144 case TR_OP_METHOD_CALL:
1145 m_func( ctx, m_ref.get() );
1146 return;
1147
1148 default:
1149 break;
1150 }
1151
1152#define AS_DOUBLE( arg ) ( arg ? arg->AsDouble() : 0.0 )
1153
1154 if( m_op & TR_OP_BINARY_MASK )
1155 {
1156 LIBEVAL::VALUE* arg2 = ctx->Pop();
1157 LIBEVAL::VALUE* arg1 = ctx->Pop();
1158 double result;
1159
1160 if( ctx->HasErrorCallback() )
1161 {
1162 if( arg1 && arg1->GetType() == VT_STRING && arg2 && arg2->GetType() == VT_NUMERIC )
1163 {
1164 ctx->ReportError( wxString::Format( _( "Type mismatch between '%s' and %lf" ),
1165 arg1->AsString(),
1166 arg2->AsDouble() ) );
1167 }
1168 else if( arg1 && arg1->GetType() == VT_NUMERIC && arg2 && arg2->GetType() == VT_STRING )
1169 {
1170 ctx->ReportError( wxString::Format( _( "Type mismatch between %lf and '%s'" ),
1171 arg1->AsDouble(),
1172 arg2->AsString() ) );
1173 }
1174 }
1175
1176 // TODO: This doesn't fully calculate units correctly yet. We really need to work out the dimensional analysis
1177 // TODO: (and do this independently of unit specifics - e.g. MM + INCH needs to return one of the dimension
1178 // TODO: types, but our units framework doesn't currently allow this. Therefore, use some heuristics to
1179 // TODO: determine the resulting operation unit type for now
1180 auto getOpResultUnits = []( const VALUE* aVal1, const VALUE* aVal2 )
1181 {
1182 // This condition can occur in, e.g., a unary negation operation
1183 if( aVal1->GetUnits() == EDA_UNITS::UNSCALED && aVal2->GetUnits() != EDA_UNITS::UNSCALED )
1184 {
1185 return aVal2->GetUnits();
1186 }
1187
1188 if( aVal1->GetUnits() != EDA_UNITS::UNSCALED && aVal2->GetUnits() == EDA_UNITS::UNSCALED )
1189 {
1190 return aVal1->GetUnits();
1191 }
1192
1193 return aVal2->GetUnits();
1194 };
1195
1196 EDA_UNITS resultUnits = EDA_UNITS::UNSCALED;
1197
1198 switch( m_op )
1199 {
1200 case TR_OP_ADD:
1201 result = AS_DOUBLE( arg1 ) + AS_DOUBLE( arg2 );
1202 resultUnits = getOpResultUnits( arg1, arg2 );
1203 break;
1204 case TR_OP_SUB:
1205 result = AS_DOUBLE( arg1 ) - AS_DOUBLE( arg2 );
1206 resultUnits = getOpResultUnits( arg1, arg2 );
1207 break;
1208 case TR_OP_MUL:
1209 result = AS_DOUBLE( arg1 ) * AS_DOUBLE( arg2 );
1210 resultUnits = getOpResultUnits( arg1, arg2 );
1211 break;
1212 case TR_OP_DIV:
1213 result = AS_DOUBLE( arg1 ) / AS_DOUBLE( arg2 );
1214 resultUnits = getOpResultUnits( arg1, arg2 );
1215 break;
1216 case TR_OP_LESS_EQUAL:
1217 result = AS_DOUBLE( arg1 ) <= AS_DOUBLE( arg2 ) ? 1 : 0;
1218 break;
1220 result = AS_DOUBLE( arg1 ) >= AS_DOUBLE( arg2 ) ? 1 : 0;
1221 break;
1222 case TR_OP_LESS:
1223 result = AS_DOUBLE( arg1 ) < AS_DOUBLE( arg2 ) ? 1 : 0;
1224 break;
1225 case TR_OP_GREATER:
1226 result = AS_DOUBLE( arg1 ) > AS_DOUBLE( arg2 ) ? 1 : 0;
1227 break;
1228 case TR_OP_EQUAL:
1229 if( !arg1 || !arg2 )
1230 result = arg1 == arg2 ? 1 : 0;
1231 else if( arg2->GetType() == VT_UNDEFINED )
1232 result = arg2->EqualTo( ctx, arg1 ) ? 1 : 0;
1233 else
1234 result = arg1->EqualTo( ctx, arg2 ) ? 1 : 0;
1235 break;
1236 case TR_OP_NOT_EQUAL:
1237 if( !arg1 || !arg2 )
1238 result = arg1 != arg2 ? 1 : 0;
1239 else if( arg2->GetType() == VT_UNDEFINED )
1240 result = arg2->NotEqualTo( ctx, arg1 ) ? 1 : 0;
1241 else
1242 result = arg1->NotEqualTo( ctx, arg2 ) ? 1 : 0;
1243 break;
1244 case TR_OP_BOOL_AND:
1245 result = AS_DOUBLE( arg1 ) != 0.0 && AS_DOUBLE( arg2 ) != 0.0 ? 1 : 0;
1246 break;
1247 case TR_OP_BOOL_OR:
1248 result = AS_DOUBLE( arg1 ) != 0.0 || AS_DOUBLE( arg2 ) != 0.0 ? 1 : 0;
1249 break;
1250 default:
1251 result = 0.0;
1252 break;
1253 }
1254
1255 auto rp = ctx->AllocValue();
1256 rp->Set( result );
1257 rp->SetUnits( resultUnits );
1258 ctx->Push( rp );
1259 return;
1260 }
1261 else if( m_op & TR_OP_UNARY_MASK )
1262 {
1263 LIBEVAL::VALUE* arg1 = ctx->Pop();
1264 double ARG1VALUE = arg1 ? arg1->AsDouble() : 0.0;
1265 double result;
1266
1267 switch( m_op )
1268 {
1269 case TR_OP_BOOL_NOT:
1270 result = ARG1VALUE != 0.0 ? 0 : 1;
1271 break;
1272 default:
1273 result = ARG1VALUE != 0.0 ? 1 : 0;
1274 break;
1275 }
1276
1277 auto rp = ctx->AllocValue();
1278 rp->Set( result );
1279 rp->SetUnits( arg1->GetUnits() );
1280 ctx->Push( rp );
1281 return;
1282 }
1283}
1284
1285
1287{
1288 static VALUE g_false( 0 );
1289
1290 try
1291 {
1292 for( UOP* op : m_ucode )
1293 op->Exec( ctx );
1294 }
1295 catch(...)
1296 {
1297 // rules which fail outright should not be fired
1298 return &g_false;
1299 }
1300
1301 if( ctx->SP() == 1 )
1302 {
1303 return ctx->Pop();
1304 }
1305 else
1306 {
1307 // If stack is corrupted after execution it suggests a problem with the compiler, not
1308 // the rule....
1309
1310 // do not use "assert"; it crashes outright on OSX
1311 wxASSERT( ctx->SP() == 1 );
1312
1313 // non-well-formed rules should not be fired on a release build
1314 return &g_false;
1315 }
1316}
1317
1318
1319} // 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
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:559
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.