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