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