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