Hook libeval compiler up to rule parser

- convert expression string tokens to single-quote-delimited
- fix bug where netclass assignments weren't getting updated after
  board setup dialog
- move property manager rebuild to lazy evaluation
- improve performance with wider use of const&
- retire DRC_SELECTOR stuff
- use wxString for GUI stuff (particularly translated stuff)
- fix EqualTo() to return false instead of asserting when op types
  don't match
- fix buffer overruns with fixed-size string buffers
- make expression function calls case-insensitive
- integrate expression errors into rule parser
- produce more and better error messages
- keep BOARD_ITEM ptrs const as long as possible
- fix a couple of uninitialized variables
This commit is contained in:
Jeff Young 2020-07-19 22:22:49 +01:00
parent 6d8fb94d86
commit 095937563b
33 changed files with 708 additions and 742 deletions

View File

@ -971,114 +971,114 @@ static YYACTIONTYPE yy_reduce(
break;
case 1: /* expr ::= G_VALUE */
#line 58 "grammar.lemon"
{ yylhsminor.yy0.op = TR_NUMBER; yylhsminor.yy0.value = yymsp[0].minor.yy0.value; yylhsminor.yy0.leaf[0] = yylhsminor.yy0.leaf[1] = NULL; yylhsminor.yy0.valid = true; }
{ yylhsminor.yy0.op = TR_NUMBER; yylhsminor.yy0.value = yymsp[0].minor.yy0.value; yylhsminor.yy0.leaf[0] = yylhsminor.yy0.leaf[1] = NULL; yylhsminor.yy0.valid = true; yylhsminor.yy0.srcPos = pEval->GetSourcePos(); }
#line 976 "grammar.c"
yymsp[0].minor.yy0 = yylhsminor.yy0;
break;
case 2: /* expr ::= G_VALUE G_UNIT */
#line 59 "grammar.lemon"
{ yylhsminor.yy0.op = TR_NUMBER; yylhsminor.yy0.value = yymsp[-1].minor.yy0.value; yylhsminor.yy0.leaf[0] = newNode( TR_UNIT, yymsp[0].minor.yy0.value.type, ""); yylhsminor.yy0.leaf[1] = NULL; yylhsminor.yy0.valid = true; }
{ yylhsminor.yy0.op = TR_NUMBER; yylhsminor.yy0.value = yymsp[-1].minor.yy0.value; yylhsminor.yy0.leaf[0] = newNode( TR_UNIT, yymsp[0].minor.yy0.value.type, ""); yylhsminor.yy0.leaf[1] = NULL; yylhsminor.yy0.valid = true; yylhsminor.yy0.srcPos = pEval->GetSourcePos(); }
#line 982 "grammar.c"
yymsp[-1].minor.yy0 = yylhsminor.yy0;
break;
case 3: /* expr ::= G_STRING */
#line 60 "grammar.lemon"
{ yylhsminor.yy0.op = TR_STRING; yylhsminor.yy0.value = yymsp[0].minor.yy0.value; yylhsminor.yy0.leaf[0] = yylhsminor.yy0.leaf[1] = NULL; yylhsminor.yy0.valid = true; }
{ yylhsminor.yy0.op = TR_STRING; yylhsminor.yy0.value = yymsp[0].minor.yy0.value; yylhsminor.yy0.leaf[0] = yylhsminor.yy0.leaf[1] = NULL; yylhsminor.yy0.valid = true; yylhsminor.yy0.srcPos = pEval->GetSourcePos(); }
#line 988 "grammar.c"
yymsp[0].minor.yy0 = yylhsminor.yy0;
break;
case 4: /* expr ::= G_IDENTIFIER */
#line 61 "grammar.lemon"
{ yylhsminor.yy0.op = TR_IDENTIFIER; yylhsminor.yy0.value = yymsp[0].minor.yy0.value; yylhsminor.yy0.leaf[0] = yylhsminor.yy0.leaf[1] = NULL; yylhsminor.yy0.valid = true; }
{ yylhsminor.yy0.op = TR_IDENTIFIER; yylhsminor.yy0.value = yymsp[0].minor.yy0.value; yylhsminor.yy0.leaf[0] = yylhsminor.yy0.leaf[1] = NULL; yylhsminor.yy0.valid = true; yylhsminor.yy0.srcPos = pEval->GetSourcePos(); }
#line 994 "grammar.c"
yymsp[0].minor.yy0 = yylhsminor.yy0;
break;
case 5: /* expr ::= expr G_LESS_THAN expr */
#line 62 "grammar.lemon"
{ yylhsminor.yy0.op = TR_OP_LESS; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; }
{ yylhsminor.yy0.op = TR_OP_LESS; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; yylhsminor.yy0.srcPos = pEval->GetSourcePos(); }
#line 1000 "grammar.c"
yymsp[-2].minor.yy0 = yylhsminor.yy0;
break;
case 6: /* expr ::= expr G_GREATER_THAN expr */
#line 63 "grammar.lemon"
{ yylhsminor.yy0.op = TR_OP_GREATER; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; }
{ yylhsminor.yy0.op = TR_OP_GREATER; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; yylhsminor.yy0.srcPos = pEval->GetSourcePos(); }
#line 1006 "grammar.c"
yymsp[-2].minor.yy0 = yylhsminor.yy0;
break;
case 7: /* expr ::= expr G_LESS_EQUAL_THAN expr */
#line 64 "grammar.lemon"
{ yylhsminor.yy0.op = TR_OP_LESS_EQUAL; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; }
{ yylhsminor.yy0.op = TR_OP_LESS_EQUAL; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; yylhsminor.yy0.srcPos = pEval->GetSourcePos(); }
#line 1012 "grammar.c"
yymsp[-2].minor.yy0 = yylhsminor.yy0;
break;
case 8: /* expr ::= expr G_GREATER_EQUAL_THAN expr */
#line 65 "grammar.lemon"
{ yylhsminor.yy0.op = TR_OP_GREATER_EQUAL; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; }
{ yylhsminor.yy0.op = TR_OP_GREATER_EQUAL; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; yylhsminor.yy0.srcPos = pEval->GetSourcePos(); }
#line 1018 "grammar.c"
yymsp[-2].minor.yy0 = yylhsminor.yy0;
break;
case 9: /* expr ::= expr G_NOT_EQUAL expr */
#line 66 "grammar.lemon"
{ yylhsminor.yy0.op = TR_OP_NOT_EQUAL; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; }
{ yylhsminor.yy0.op = TR_OP_NOT_EQUAL; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; yylhsminor.yy0.srcPos = pEval->GetSourcePos(); }
#line 1024 "grammar.c"
yymsp[-2].minor.yy0 = yylhsminor.yy0;
break;
case 10: /* expr ::= expr G_BOOL_AND expr */
#line 67 "grammar.lemon"
{ yylhsminor.yy0.op = TR_OP_BOOL_AND; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; }
{ yylhsminor.yy0.op = TR_OP_BOOL_AND; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; yylhsminor.yy0.srcPos = pEval->GetSourcePos(); }
#line 1030 "grammar.c"
yymsp[-2].minor.yy0 = yylhsminor.yy0;
break;
case 11: /* expr ::= expr G_BOOL_OR expr */
#line 68 "grammar.lemon"
{ yylhsminor.yy0.op = TR_OP_BOOL_OR; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; }
{ yylhsminor.yy0.op = TR_OP_BOOL_OR; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; yylhsminor.yy0.srcPos = pEval->GetSourcePos(); }
#line 1036 "grammar.c"
yymsp[-2].minor.yy0 = yylhsminor.yy0;
break;
case 12: /* expr ::= expr G_PLUS expr */
#line 69 "grammar.lemon"
{ yylhsminor.yy0.op = TR_OP_ADD; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; }
{ yylhsminor.yy0.op = TR_OP_ADD; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; yylhsminor.yy0.srcPos = pEval->GetSourcePos(); }
#line 1042 "grammar.c"
yymsp[-2].minor.yy0 = yylhsminor.yy0;
break;
case 13: /* expr ::= expr G_MINUS expr */
#line 70 "grammar.lemon"
{ yylhsminor.yy0.op = TR_OP_SUB; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; }
{ yylhsminor.yy0.op = TR_OP_SUB; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; yylhsminor.yy0.srcPos = pEval->GetSourcePos(); }
#line 1048 "grammar.c"
yymsp[-2].minor.yy0 = yylhsminor.yy0;
break;
case 14: /* expr ::= expr G_MULT expr */
#line 71 "grammar.lemon"
{ yylhsminor.yy0.op = TR_OP_MUL; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; }
{ yylhsminor.yy0.op = TR_OP_MUL; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; yylhsminor.yy0.srcPos = pEval->GetSourcePos(); }
#line 1054 "grammar.c"
yymsp[-2].minor.yy0 = yylhsminor.yy0;
break;
case 15: /* expr ::= expr G_DIVIDE expr */
#line 72 "grammar.lemon"
{ yylhsminor.yy0.op = TR_OP_DIV; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; }
{ yylhsminor.yy0.op = TR_OP_DIV; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; yylhsminor.yy0.srcPos = pEval->GetSourcePos(); }
#line 1060 "grammar.c"
yymsp[-2].minor.yy0 = yylhsminor.yy0;
break;
case 16: /* expr ::= expr G_EQUAL expr */
#line 73 "grammar.lemon"
{ yylhsminor.yy0.op = TR_OP_EQUAL; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; }
{ yylhsminor.yy0.op = TR_OP_EQUAL; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; yylhsminor.yy0.srcPos = pEval->GetSourcePos(); }
#line 1066 "grammar.c"
yymsp[-2].minor.yy0 = yylhsminor.yy0;
break;
case 17: /* expr ::= expr G_STRUCT_REF expr */
#line 75 "grammar.lemon"
{ yylhsminor.yy0.op = TR_STRUCT_REF; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; }
{ yylhsminor.yy0.op = TR_STRUCT_REF; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-2].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[0].minor.yy0); yylhsminor.yy0.valid=yymsp[-2].minor.yy0.valid && yymsp[0].minor.yy0.valid; yylhsminor.yy0.srcPos = pEval->GetSourcePos(); }
#line 1072 "grammar.c"
yymsp[-2].minor.yy0 = yylhsminor.yy0;
break;
case 18: /* expr ::= G_PARENL expr G_PARENR */
#line 77 "grammar.lemon"
{ yymsp[-2].minor.yy0.op = yymsp[-1].minor.yy0.op; yymsp[-2].minor.yy0.value = yymsp[-1].minor.yy0.value; yymsp[-2].minor.yy0.valid=yymsp[-1].minor.yy0.valid; yymsp[-2].minor.yy0.leaf[0] = yymsp[-1].minor.yy0.leaf[0]; yymsp[-2].minor.yy0.leaf[1] = yymsp[-1].minor.yy0.leaf[1]; }
{ yymsp[-2].minor.yy0.op = yymsp[-1].minor.yy0.op; yymsp[-2].minor.yy0.value = yymsp[-1].minor.yy0.value; yymsp[-2].minor.yy0.valid=yymsp[-1].minor.yy0.valid; yymsp[-2].minor.yy0.leaf[0] = yymsp[-1].minor.yy0.leaf[0]; yymsp[-2].minor.yy0.leaf[1] = yymsp[-1].minor.yy0.leaf[1]; yymsp[-2].minor.yy0.srcPos = pEval->GetSourcePos(); }
#line 1078 "grammar.c"
break;
case 19: /* expr ::= G_IDENTIFIER G_PARENL expr G_PARENR */
#line 78 "grammar.lemon"
{ yylhsminor.yy0.op = TR_OP_FUNC_CALL; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-3].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[-1].minor.yy0); yylhsminor.yy0.valid=1; }
{ yylhsminor.yy0.op = TR_OP_FUNC_CALL; yylhsminor.yy0.leaf[0] = copyNode(yymsp[-3].minor.yy0); yylhsminor.yy0.leaf[1] = copyNode(yymsp[-1].minor.yy0); yylhsminor.yy0.valid=1; yylhsminor.yy0.srcPos = pEval->GetSourcePos(); }
#line 1083 "grammar.c"
yymsp[-3].minor.yy0 = yylhsminor.yy0;
break;

View File

@ -54,26 +54,26 @@ in ::= in stmt.
/* A statement can be empty, an expr or an expr followed by ';' */
stmt ::= G_ENDS.
stmt ::= expr(A) G_ENDS. { pEval->setRoot(A); }
//stmt ::= expr G_SEMCOL. { pEval->setRoot(NULL) }
//stmt ::= expr G_SEMCOL. { pEval->setRoot(NULL); }
expr(A) ::= G_VALUE(B). { A.op = TR_NUMBER; A.value = B.value; A.leaf[0] = A.leaf[1] = NULL; A.valid = true; }
expr(A) ::= G_VALUE(B) G_UNIT(C). { A.op = TR_NUMBER; A.value = B.value; A.leaf[0] = newNode( TR_UNIT, C.value.type, ""); A.leaf[1] = NULL; A.valid = true; }
expr(A) ::= G_STRING(B). { A.op = TR_STRING; A.value = B.value; A.leaf[0] = A.leaf[1] = NULL; A.valid = true; }
expr(A) ::= G_IDENTIFIER(B). { A.op = TR_IDENTIFIER; A.value = B.value; A.leaf[0] = A.leaf[1] = NULL; A.valid = true; }
expr(A) ::= expr(B) G_LESS_THAN expr(C). { A.op = TR_OP_LESS; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
expr(A) ::= expr(B) G_GREATER_THAN expr(C). { A.op = TR_OP_GREATER; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
expr(A) ::= expr(B) G_LESS_EQUAL_THAN expr(C). { A.op = TR_OP_LESS_EQUAL; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
expr(A) ::= expr(B) G_GREATER_EQUAL_THAN expr(C). { A.op = TR_OP_GREATER_EQUAL; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
expr(A) ::= expr(B) G_NOT_EQUAL expr(C). { A.op = TR_OP_NOT_EQUAL; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
expr(A) ::= expr(B) G_BOOL_AND expr(C). { A.op = TR_OP_BOOL_AND; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
expr(A) ::= expr(B) G_BOOL_OR expr(C). { A.op = TR_OP_BOOL_OR; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
expr(A) ::= expr(B) G_PLUS expr(C). { A.op = TR_OP_ADD; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
expr(A) ::= expr(B) G_MINUS expr(C). { A.op = TR_OP_SUB; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
expr(A) ::= expr(B) G_MULT expr(C). { A.op = TR_OP_MUL; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
expr(A) ::= expr(B) G_DIVIDE expr(C). { A.op = TR_OP_DIV; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
expr(A) ::= expr(B) G_EQUAL expr(C). { A.op = TR_OP_EQUAL; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
expr(A) ::= G_VALUE(B). { A.op = TR_NUMBER; A.value = B.value; A.leaf[0] = A.leaf[1] = NULL; A.valid = true; A.srcPos = pEval->GetSourcePos(); }
expr(A) ::= G_VALUE(B) G_UNIT(C). { A.op = TR_NUMBER; A.value = B.value; A.leaf[0] = newNode(TR_UNIT, C.value.type, ""); A.leaf[1] = NULL; A.valid = true; A.srcPos = pEval->GetSourcePos(); }
expr(A) ::= G_STRING(B). { A.op = TR_STRING; A.value = B.value; A.leaf[0] = A.leaf[1] = NULL; A.valid = true; A.srcPos = pEval->GetSourcePos(); }
expr(A) ::= G_IDENTIFIER(B). { A.op = TR_IDENTIFIER; A.value = B.value; A.leaf[0] = A.leaf[1] = NULL; A.valid = true; A.srcPos = pEval->GetSourcePos(); }
expr(A) ::= expr(B) G_LESS_THAN expr(C). { A.op = TR_OP_LESS; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid = B.valid && C.valid; A.srcPos = pEval->GetSourcePos(); }
expr(A) ::= expr(B) G_GREATER_THAN expr(C). { A.op = TR_OP_GREATER; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid = B.valid && C.valid; A.srcPos = pEval->GetSourcePos(); }
expr(A) ::= expr(B) G_LESS_EQUAL_THAN expr(C). { A.op = TR_OP_LESS_EQUAL; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid = B.valid && C.valid; A.srcPos = pEval->GetSourcePos(); }
expr(A) ::= expr(B) G_GREATER_EQUAL_THAN expr(C). { A.op = TR_OP_GREATER_EQUAL; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid = B.valid && C.valid; A.srcPos = pEval->GetSourcePos(); }
expr(A) ::= expr(B) G_NOT_EQUAL expr(C). { A.op = TR_OP_NOT_EQUAL; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid = B.valid && C.valid; A.srcPos = pEval->GetSourcePos(); }
expr(A) ::= expr(B) G_BOOL_AND expr(C). { A.op = TR_OP_BOOL_AND; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid = B.valid && C.valid; A.srcPos = pEval->GetSourcePos(); }
expr(A) ::= expr(B) G_BOOL_OR expr(C). { A.op = TR_OP_BOOL_OR; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid = B.valid && C.valid; A.srcPos = pEval->GetSourcePos(); }
expr(A) ::= expr(B) G_PLUS expr(C). { A.op = TR_OP_ADD; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid = B.valid && C.valid; A.srcPos = pEval->GetSourcePos(); }
expr(A) ::= expr(B) G_MINUS expr(C). { A.op = TR_OP_SUB; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid = B.valid && C.valid; A.srcPos = pEval->GetSourcePos(); }
expr(A) ::= expr(B) G_MULT expr(C). { A.op = TR_OP_MUL; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid = B.valid && C.valid; A.srcPos = pEval->GetSourcePos(); }
expr(A) ::= expr(B) G_DIVIDE expr(C). { A.op = TR_OP_DIV; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid = B.valid && C.valid; A.srcPos = pEval->GetSourcePos(); }
expr(A) ::= expr(B) G_EQUAL expr(C). { A.op = TR_OP_EQUAL; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid = B.valid && C.valid; A.srcPos = pEval->GetSourcePos(); }
expr(A) ::= expr(B) G_STRUCT_REF expr(C). { A.op = TR_STRUCT_REF; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; }
expr(A) ::= expr(B) G_STRUCT_REF expr(C). { A.op = TR_STRUCT_REF; A.leaf[0] = copyNode(B); A.leaf[1] = copyNode(C); A.valid=B.valid && C.valid; A.srcPos = pEval->GetSourcePos(); }
expr(A) ::= G_PARENL expr(B) G_PARENR. { A.op = B.op; A.value = B.value; A.valid=B.valid; A.leaf[0] = B.leaf[0]; A.leaf[1] = B.leaf[1]; }
expr(A) ::= G_IDENTIFIER(func_name) G_PARENL expr(B) G_PARENR. { A.op = TR_OP_FUNC_CALL; A.leaf[0] = copyNode(func_name); A.leaf[1] = copyNode(B); A.valid=1; }
expr(A) ::= G_PARENL expr(B) G_PARENR. { A.op = B.op; A.value = B.value; A.valid=B.valid; A.leaf[0] = B.leaf[0]; A.leaf[1] = B.leaf[1]; A.srcPos = pEval->GetSourcePos(); }
expr(A) ::= G_IDENTIFIER(F) G_PARENL expr(B) G_PARENR. { A.op = TR_OP_FUNC_CALL; A.leaf[0] = copyNode(F); A.leaf[1] = copyNode(B); A.valid = true; A.srcPos = pEval->GetSourcePos(); }

View File

@ -18,6 +18,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <memory>
#include <set>
#include <vector>
@ -67,19 +68,21 @@ static const std::string formatOpName( int op )
{
int op;
std::string mnemonic;
} simpleOps[] = { { TR_OP_MUL, "MUL" }, { TR_OP_DIV, "DIV" }, { TR_OP_ADD, "ADD" },
}
simpleOps[] =
{
{ TR_OP_MUL, "MUL" }, { TR_OP_DIV, "DIV" }, { TR_OP_ADD, "ADD" },
{ TR_OP_SUB, "SUB" }, { TR_OP_LESS, "LESS" }, { TR_OP_GREATER, "GREATER" },
{ TR_OP_LESS_EQUAL, "LESS_EQUAL" }, { TR_OP_GREATER_EQUAL, "GREATER_EQUAL" },
{ TR_OP_EQUAL, "EQUAL" }, { TR_OP_NOT_EQUAL, "NEQUAL" }, { TR_OP_BOOL_AND, "AND" },
{ TR_OP_BOOL_OR, "OR" }, { TR_OP_BOOL_NOT, "NOT" }, { -1, "" } };
{ TR_OP_BOOL_OR, "OR" }, { TR_OP_BOOL_NOT, "NOT" }, { -1, "" }
};
for( int i = 0; simpleOps[i].op >= 0; i++ )
{
if( simpleOps[i].op == op )
{
return simpleOps[i].mnemonic;
}
}
return "???";
}
@ -87,32 +90,40 @@ static const std::string formatOpName( int op )
std::string UOP::Format() const
{
char str[1024];
char str[LIBEVAL_MAX_LITERAL_LENGTH];
switch( m_op )
{
case TR_UOP_PUSH_VAR:
sprintf( str, "PUSH VAR [%p]", m_arg );
snprintf( str, LIBEVAL_MAX_LITERAL_LENGTH, "PUSH VAR [%p]", m_arg );
break;
case TR_UOP_PUSH_VALUE:
{
auto val = reinterpret_cast<VALUE*>( m_arg );
if( val->GetType() == VT_NUMERIC )
sprintf( str, "PUSH NUM [%.10f]", val->AsDouble() );
VALUE* val = reinterpret_cast<VALUE*>( m_arg );
if( !val )
snprintf( str, LIBEVAL_MAX_LITERAL_LENGTH, "PUSH nullptr" );
else if( val->GetType() == VT_NUMERIC )
snprintf( str, LIBEVAL_MAX_LITERAL_LENGTH, "PUSH NUM [%.10f]", val->AsDouble() );
else
sprintf( str, "PUSH STR [%s]", val->AsString().c_str() );
break;
snprintf( str, LIBEVAL_MAX_LITERAL_LENGTH, "PUSH STR [%s]", val->AsString().c_str() );
}
break;
case TR_OP_METHOD_CALL:
sprintf(str, "MCALL" );
snprintf( str, LIBEVAL_MAX_LITERAL_LENGTH, "MCALL" );
break;
case TR_OP_FUNC_CALL:
sprintf(str, "FCALL" );
snprintf( str, LIBEVAL_MAX_LITERAL_LENGTH, "FCALL" );
break;
default:
sprintf( str, "%s %d", formatOpName( m_op ).c_str(), m_op );
snprintf( str, LIBEVAL_MAX_LITERAL_LENGTH, "%s %d", formatOpName( m_op ).c_str(), m_op );
break;
}
return str;
}
@ -140,7 +151,7 @@ std::string TOKENIZER::GetChars( std::function<bool( int )> cond ) const
return rv;
}
bool TOKENIZER::MatchAhead( std::string match, std::function<bool( int )> stopCond ) const
bool TOKENIZER::MatchAhead( const std::string& match, std::function<bool( int )> stopCond ) const
{
int remaining = m_str.length() - m_pos;
if( remaining < (int) match.length() )
@ -158,11 +169,10 @@ COMPILER::COMPILER()
{
m_errorStatus.pendingError = false;
m_localeDecimalSeparator = '.';
m_sourcePos = 0;
m_parseFinished = false;
m_unitResolver.reset( new UNIT_RESOLVER );
m_unitResolver = std::make_unique<UNIT_RESOLVER>();
m_parser = LIBEVAL::ParseAlloc( malloc );
m_parseError = false;
m_parseErrorPos = 0;
m_tree = nullptr;
}
@ -221,6 +231,8 @@ bool COMPILER::Compile( const std::string& aString, UCODE* aCode )
do
{
m_sourcePos = m_tokenizer.GetPos();
tok = getToken();
libeval_dbg(10, "parse: tok %d\n", tok.token );
Parse( m_parser, tok.token, tok.value, this );
@ -228,9 +240,8 @@ bool COMPILER::Compile( const std::string& aString, UCODE* aCode )
if( m_errorStatus.pendingError )
{
m_errorStatus.stage = ERROR_STATUS::CST_PARSE;
m_errorStatus.failingPosition = m_tokenizer.GetPos();
m_errorStatus.failingObject = tok.value.value.str;
m_errorStatus.message = "Parse error";
m_errorStatus.message.Printf( _( "Unrecognized token '%s'" ), tok.value.value.str );
m_errorStatus.srcPos = m_tokenizer.GetPos();
return false;
}
@ -261,7 +272,6 @@ COMPILER::T_TOKEN COMPILER::getToken()
bool done = false;
do
{
switch( m_lexerState )
{
case LS_DEFAULT:
@ -280,7 +290,7 @@ COMPILER::T_TOKEN COMPILER::getToken()
bool COMPILER::lexString( COMPILER::T_TOKEN& aToken )
{
auto str = m_tokenizer.GetChars( []( int c ) -> bool { return c != '"'; } );
auto str = m_tokenizer.GetChars( []( int c ) -> bool { return c != '\''; } );
//printf("STR LIT '%s'\n", (const char *)str.c_str() );
aToken.token = G_STRING;
@ -310,6 +320,7 @@ int COMPILER::resolveUnits()
return -1;
}
bool COMPILER::lexDefault( COMPILER::T_TOKEN& aToken )
{
T_TOKEN retval;
@ -326,15 +337,19 @@ bool COMPILER::lexDefault( COMPILER::T_TOKEN& aToken )
return true;
}
auto isDecimalSeparator = [&]( char ch ) -> bool {
auto isDecimalSeparator =
[&]( char ch ) -> bool
{
return ( ch == m_localeDecimalSeparator || ch == '.' || ch == ',' );
};
// Lambda: get value as string, store into clToken.token and update current index.
auto extractNumber = [&]() {
auto extractNumber =
[&]()
{
bool haveSeparator = false;
idx = 0;
auto ch = m_tokenizer.GetChar();
int ch = m_tokenizer.GetChar();
do
{
@ -352,8 +367,10 @@ bool COMPILER::lexDefault( COMPILER::T_TOKEN& aToken )
// Ensure that the systems decimal separator is used
for( int i = current.length(); i; i-- )
{
if( isDecimalSeparator( current[i - 1] ) )
current[i - 1] = m_localeDecimalSeparator;
}
//printf("-> NUM: '%s'\n", (const char *) current.c_str() );
@ -398,7 +415,7 @@ bool COMPILER::lexDefault( COMPILER::T_TOKEN& aToken )
retval.token = G_UNIT;
retval.value.value.type = convertFrom;
}
else if( ch == '\"' ) // string literal
else if( ch == '\'' ) // string literal
{
//printf( "MATCH STRING LITERAL\n" );
m_lexerState = LS_STRING;
@ -488,9 +505,8 @@ bool COMPILER::lexDefault( COMPILER::T_TOKEN& aToken )
break;
default:
m_errorStatus.stage = ERROR_STATUS::CST_PARSE;
m_errorStatus.failingPosition = m_tokenizer.GetPos();
m_errorStatus.failingObject = ch;
m_errorStatus.message = "Syntax error";
m_errorStatus.message.Printf( _( "Unrecognized character '%c'" ), (char) ch );
m_errorStatus.srcPos = m_tokenizer.GetPos();
break; /* invalid character */
}
@ -564,11 +580,10 @@ void dumpNode( std::string& buf, TREE_NODE* tok, int depth = 0 )
ERROR_STATUS COMPILER::GetErrorStatus()
{
ERROR_STATUS dummy;
return dummy;
return m_errorStatus;
}
void COMPILER::ReportError( const std::string aErrorMsg )
void COMPILER::ReportError( const std::string& aErrorMsg )
{
m_errorStatus.pendingError = true;
m_errorStatus.message = aErrorMsg;
@ -585,7 +600,8 @@ bool COMPILER::generateUCode( UCODE* aCode )
std::vector<TREE_NODE*> stack;
std::set<TREE_NODE*> visitedNodes;
auto visited = [&]( TREE_NODE* node ) -> bool {
auto visited = [&]( TREE_NODE* node ) -> bool
{
return visitedNodes.find( node ) != visitedNodes.end();
};
@ -603,7 +619,7 @@ bool COMPILER::generateUCode( UCODE* aCode )
while( !stack.empty() )
{
auto node = stack.back();
TREE_NODE* node = stack.back();
libeval_dbg( 4, "process node %p [op %d] [stack %lu]\n",
node, node->op, (unsigned long)stack.size() );
@ -623,47 +639,71 @@ bool COMPILER::generateUCode( UCODE* aCode )
{
case TR_IDENTIFIER:
{
auto vref = aCode->createVarRef( this, node->leaf[0]->value.str, node->leaf[1]->value.str );
char* itemName = node->leaf[0]->value.str;
char* propName = node->leaf[1]->value.str;
VAR_REF* vref = aCode->createVarRef( this, itemName, propName );
if( m_errorStatus.pendingError )
{
libeval_dbg(4, "varref fail\n");
m_errorStatus.pendingError = true;
m_errorStatus.stage = ERROR_STATUS::CST_CODEGEN;
if( m_errorStatus.message == "var" )
{
m_errorStatus.message.Printf( _( "Unrecognized item '%s'" ),
itemName );
m_errorStatus.srcPos = node->leaf[0]->srcPos - strlen( itemName );
}
else
{
m_errorStatus.message.Printf( _( "Unrecognized property '%s'" ),
propName );
m_errorStatus.srcPos = node->leaf[1]->srcPos - strlen( propName );
}
return false;
}
node->uop = makeUop( TR_UOP_PUSH_VAR, vref );
node->isTerminal = true;
break;
}
case TR_OP_FUNC_CALL:
{
char* itemName = node->leaf[0]->value.str;
VAR_REF* vref = aCode->createVarRef( this, itemName, "" );
libeval_dbg(4, "got a method call... [%s], this = %s\n", node->leaf[1]->leaf[0]->value.str, node->leaf[0]->value.str);
auto vref = aCode->createVarRef( this, node->leaf[0]->value.str, "");
auto func = aCode->createFuncCall( this, node->leaf[1]->leaf[0]->value.str );
if( m_errorStatus.pendingError )
{
m_errorStatus.stage = ERROR_STATUS::CST_CODEGEN;
m_errorStatus.message.Printf( _( "Unrecognized item '%s'" ), itemName );
m_errorStatus.srcPos = node->leaf[0]->srcPos - strlen( itemName );
return false;
}
char* functionName = node->leaf[1]->leaf[0]->value.str;
auto func = aCode->createFuncCall( this, functionName );
if( !func )
{
m_errorStatus.pendingError = true;
m_errorStatus.stage = ERROR_STATUS::CST_CODEGEN;
libeval_dbg(0, "unable to resolve func %s\n", node->leaf[1]->leaf[0]->value.str );
m_errorStatus.message.Printf( _( "Unrecognized function '%s'" ),
functionName );
m_errorStatus.srcPos = node->leaf[1]->leaf[0]->srcPos + 1;
return false;
// fixme: generate a message
}
// printf("cfc4 test\n");
// func(nullptr, nullptr, nullptr);
visitedNodes.insert( node->leaf[0] );
visitedNodes.insert( node->leaf[1]->leaf[0] );
node->uop = makeUop( TR_OP_METHOD_CALL, func, vref );
node->isTerminal = false;
}
break;
}
}
break;
}
case TR_NUMBER:
{
@ -711,15 +751,15 @@ bool COMPILER::generateUCode( UCODE* aCode )
}
visitedNodes.insert( node );
if(node->uop)
aCode->AddOp(node->uop);
stack.pop_back();
stack.pop_back();
}
libeval_dbg(2,"DUMp: \n%s\n", aCode->Dump().c_str() );
return true;
}
@ -734,8 +774,8 @@ void UOP::Exec( UCODE::CONTEXT* ctx, UCODE* ucode )
auto value = ctx->AllocValue();
value->Set( reinterpret_cast<VAR_REF*>( m_arg )->GetValue( ucode ) );
ctx->Push( value );
break;
}
break;
case TR_UOP_PUSH_VALUE:
ctx->Push( reinterpret_cast<VALUE*>( m_arg ) );
@ -745,14 +785,15 @@ void UOP::Exec( UCODE::CONTEXT* ctx, UCODE* ucode )
//printf("CALL METHOD %s\n" );
m_func( ucode, ctx, m_arg );
return;
default:
break;
}
if( m_op & TR_OP_BINARY_MASK )
{
auto arg2 = ctx->Pop();
auto arg1 = ctx->Pop();
LIBEVAL::VALUE* arg2 = ctx->Pop();
LIBEVAL::VALUE* arg1 = ctx->Pop();
double result;
switch( m_op )
@ -815,30 +856,22 @@ void UOP::Exec( UCODE::CONTEXT* ctx, UCODE* ucode )
}
}
VALUE* UCODE::Run()
{
CONTEXT ctx;
for( const auto op : m_ucode )
for( UOP* op : m_ucode )
op->Exec( &ctx, this );
assert( ctx.SP() == 1 );
return ctx.Pop();
}
void UCODE::RuntimeError( const std::string aErrorMsg )
void UCODE::RuntimeError( const std::string& aErrorMsg )
{
}
std::string ERROR_STATUS::Format() const
{
if( !pendingError )
return "";
char str[1024];
sprintf(str,"%s (pos: %d, near: '%s')", message.c_str(), failingPosition, failingObject.c_str() );
return str;
}
} // namespace LIBEVAL

View File

@ -418,13 +418,26 @@ bool NET_SETTINGS::ParseBusGroup( wxString aGroup, wxString* aName,
}
void NET_SETTINGS::ResolveNetClassAssignments()
void NET_SETTINGS::ResolveNetClassAssignments( bool aRebuildFromScratch )
{
std::map<wxString, wxString> existing = m_NetClassAssignments;
std::map<wxString, wxString> baseList = m_NetClassAssignments;
if( aRebuildFromScratch )
{
for( const std::pair<const wxString, NETCLASSPTR>& netclass : m_NetClasses )
{
for( const wxString& net : *netclass.second )
baseList[ net ] = netclass.second->GetName();
}
}
else
{
baseList = m_NetClassAssignments;
}
m_NetClassAssignments.clear();
for( const auto& ii : existing )
for( const auto& ii : baseList )
{
m_NetClassAssignments[ ii.first ] = ii.second;

View File

@ -44,8 +44,8 @@ const wxString& PROPERTY_MANAGER::ResolveType( TYPE_ID aType ) const
PROPERTY_BASE* PROPERTY_MANAGER::GetProperty( TYPE_ID aType, const wxString& aProperty ) const
{
wxASSERT_MSG( !m_dirty, "Have not called PROPERTY_MANAGER::Rebuild(), "
"property list not up-to-date" );
if( m_dirty )
const_cast<PROPERTY_MANAGER*>( this )->Rebuild();
auto it = m_classes.find( aType );
@ -54,7 +54,7 @@ PROPERTY_BASE* PROPERTY_MANAGER::GetProperty( TYPE_ID aType, const wxString& aPr
const CLASS_DESC& classDesc = it->second;
for( const auto& property : classDesc.m_allProperties )
for( PROPERTY_BASE* property : classDesc.m_allProperties )
{
if( !aProperty.CmpNoCase( property->Name() ) )
return property;
@ -66,8 +66,8 @@ PROPERTY_BASE* PROPERTY_MANAGER::GetProperty( TYPE_ID aType, const wxString& aPr
const PROPERTY_LIST& PROPERTY_MANAGER::GetProperties( TYPE_ID aType ) const
{
wxASSERT_MSG( !m_dirty, "Have not called PROPERTY_MANAGER::Rebuild(), "
"property list not up-to-date" );
if( m_dirty )
const_cast<PROPERTY_MANAGER*>( this )->Rebuild();
static const PROPERTY_LIST empty;
auto it = m_classes.find( aType );
@ -163,12 +163,8 @@ bool PROPERTY_MANAGER::IsOfType( TYPE_ID aDerived, TYPE_ID aBase ) const
void PROPERTY_MANAGER::Rebuild()
{
wxASSERT_MSG( m_dirty, "Excessive Rebuild() call" );
for( auto& classEntry : m_classes )
{
for( std::pair<const TYPE_ID, CLASS_DESC>& classEntry : m_classes )
classEntry.second.rebuild();
}
m_dirty = false;
}
@ -195,28 +191,31 @@ void PROPERTY_MANAGER::CLASS_DESC::rebuild()
}
void PROPERTY_MANAGER::CLASS_DESC::collectPropsRecur( PROPERTY_LIST& aResult, PROPERTY_SET& aReplaced ) const
void PROPERTY_MANAGER::CLASS_DESC::collectPropsRecur( PROPERTY_LIST& aResult,
PROPERTY_SET& aReplaced ) const
{
for( const auto& replacedEntry : m_replaced )
for( const std::pair<size_t, wxString>& replacedEntry : m_replaced )
aReplaced.emplace( replacedEntry );
for( auto& propertyData : m_ownProperties )
for( const std::pair<const wxString, std::unique_ptr<PROPERTY_BASE>>& prop : m_ownProperties )
{
PROPERTY_BASE* property = propertyData.second.get();
PROPERTY_BASE* property = prop.second.get();
// Do not store replaced properties
if( aReplaced.count( std::make_pair( property->OwnerHash(), property->Name() ) ) == 0 )
aResult.push_back( property );
}
for( const auto& base : m_bases )
for( const std::reference_wrapper<CLASS_DESC>& base : m_bases )
base.get().collectPropsRecur( aResult, aReplaced );
}
std::vector<TYPE_ID> PROPERTY_MANAGER::GetMatchingClasses( PROPERTY_BASE* aProperty )
{
std::vector<TYPE_ID> ids;
/*
for( auto& cls : m_classes )
{
CLASS_INFO info;
@ -226,21 +225,24 @@ std::vector<TYPE_ID> PROPERTY_MANAGER::GetMatchingClasses( PROPERTY_BASE* aPrope
}
*/
return ids;
}
PROPERTY_MANAGER::CLASSES_INFO PROPERTY_MANAGER::GetAllClasses()
{
CLASSES_INFO rv;
for( auto& cls : m_classes )
for( std::pair<const TYPE_ID, CLASS_DESC>& classEntry : m_classes )
{
CLASS_INFO info;
info.type = cls.first;
info.name = m_classNames[cls.first];
info.type = classEntry.first;
info.name = m_classNames[classEntry.first];
for( auto prop : cls.second.m_allProperties )
for( PROPERTY_BASE* prop : classEntry.second.m_allProperties )
info.properties.push_back( prop );
rv.push_back( info );

View File

@ -268,7 +268,7 @@ void SCH_EDIT_FRAME::ShowSchematicSetupDialog( const wxString& aInitialPage )
if( dlg.ShowQuasiModal() == wxID_OK )
{
Prj().GetProjectFile().NetSettings().ResolveNetClassAssignments();
Prj().GetProjectFile().NetSettings().ResolveNetClassAssignments( true );
SaveProjectSettings();
Kiway().CommonSettingsChanged( false, true );

View File

@ -219,14 +219,6 @@ public:
std::vector<VIA_DIMENSION> m_ViasDimensionsList;
std::vector<DIFF_PAIR_DIMENSION> m_DiffPairDimensionsList;
// List of netclasses. There is always the default netclass.
//NETCLASSES m_NetClasses;
std::vector<DRC_SELECTOR*> m_DRCRuleSelectors;
std::vector<DRC_RULE*> m_DRCRules;
// Temporary storage for rule matching.
std::vector<DRC_SELECTOR*> m_matched;
bool m_MicroViasAllowed; ///< true to allow micro vias
bool m_BlindBuriedViaAllowed; ///< true to allow blind/buried vias
VIATYPE m_CurrentViaType; ///< (VIA_BLIND_BURIED, VIA_THROUGH, VIA_MICROVIA)
@ -243,9 +235,8 @@ public:
int m_CopperEdgeClearance;
int m_HoleToHoleMin; // Min width of peninsula between two drilled holes
std::vector<DRC_RULE*> m_DRCRules;
std::map< int, int > m_DRCSeverities; // Map from DRCErrorCode to SEVERITY
/// Excluded DRC items
std::set<wxString> m_DrcExclusions;
/** Option to handle filled polygons in zones:

View File

@ -354,4 +354,49 @@ protected:
DECLARE_ENUM_TO_WXANY( PCB_LAYER_ID );
#endif
/**
* A singleton item of this class is returned for a weak reference that no longer exists.
* Its sole purpose is to flag the item as having been deleted.
*/
class DELETED_BOARD_ITEM : public BOARD_ITEM
{
public:
DELETED_BOARD_ITEM() :
BOARD_ITEM( nullptr, NOT_USED )
{}
wxString GetSelectMenuText( EDA_UNITS aUnits ) const override
{
return _( "(Deleted Item)" );
}
wxString GetClass() const override
{
return wxT( "DELETED_BOARD_ITEM" );
}
// pure virtuals:
void SetPosition( const wxPoint& ) override {}
wxPoint GetPosition() const override {
return wxPoint(0, 0);
}
static DELETED_BOARD_ITEM* GetInstance()
{
static DELETED_BOARD_ITEM* item = nullptr;
if( !item )
item = new DELETED_BOARD_ITEM();
return item;
}
#if defined(DEBUG)
void Show( int , std::ostream& ) const override {}
#endif
};
#endif /* BOARD_ITEM_STRUCT_H */

View File

@ -59,18 +59,16 @@ struct ERROR_STATUS
{
bool pendingError;
enum STAGE {
enum STAGE
{
CST_PARSE = 0,
CST_CODEGEN,
CST_RUNTIME
};
STAGE stage;
std::string message;
std::string failingObject;
int failingPosition;
std::string Format() const;
wxString message; // Note: use wxString for GUI-related strings
int srcPos;
};
@ -108,6 +106,7 @@ struct TREE_NODE
UOP* uop;
bool valid;
bool isTerminal;
int srcPos;
};
@ -121,6 +120,7 @@ static inline TREE_NODE* copyNode( TREE_NODE& t )
t2->leaf[0] = t.leaf[0];
t2->leaf[1] = t.leaf[1];
t2->isTerminal = false;
t2->srcPos = t.srcPos;
t2->uop = nullptr;
return t2;
}
@ -136,12 +136,14 @@ static inline TREE_NODE* newNode( int op, int type, const std::string& value )
t2->leaf[0] = nullptr;
t2->leaf[1] = nullptr;
t2->isTerminal = false;
t2->srcPos = -1;
t2->uop = nullptr;
return t2;
}
class UNIT_RESOLVER {
class UNIT_RESOLVER
{
public:
UNIT_RESOLVER()
{
@ -158,23 +160,25 @@ class UNIT_RESOLVER {
return nullUnits;
}
virtual double Convert( const std::string aString, int unitType ) const
virtual double Convert( const std::string& aString, int unitType ) const
{
return 0.0;
};
};
class VALUE {
class VALUE
{
public:
VALUE():
m_type(VT_UNDEFINED),
m_valueDbl( 0 )
{};
VALUE( const std::string& aStr ) :
VALUE( std::string aStr ) :
m_type( VT_STRING ),
m_valueDbl( 0 ),
m_valueStr( aStr )
m_valueStr( std::move( aStr ) )
{};
VALUE( const double aVal ) :
@ -194,18 +198,14 @@ public:
bool operator==( const VALUE& b ) const
{
if( m_type != b.m_type )
return false;
if( m_type == VT_NUMERIC && m_valueDbl != b.m_valueDbl )
return false;
if( m_type == VT_STRING && m_valueStr != b.m_valueStr )
return false;
if( m_type == VT_NUMERIC && b.m_type == VT_NUMERIC )
return m_valueDbl == b.m_valueDbl;
else if( m_type == VT_STRING && b.m_type == VT_STRING )
return m_valueStr == b.m_valueStr;
return true;
return false;
}
VAR_TYPE_T GetType() const { return m_type; };
void Set( double aValue )
@ -214,7 +214,7 @@ public:
m_valueDbl = aValue;
}
void Set( std::string aValue )
void Set( const std::string& aValue )
{
m_type = VT_STRING;
m_valueStr = aValue;
@ -224,19 +224,14 @@ public:
{
m_type = val.m_type;
m_valueDbl = val.m_valueDbl;
if( m_type == VT_STRING )
m_valueStr = val.m_valueStr;
}
bool EqualTo( const VALUE* v2 ) const
{
assert ( m_type == v2->m_type );
if(m_type == VT_STRING)
return m_valueStr == v2->m_valueStr;
else
return m_valueDbl == v2->m_valueDbl;
return operator==( *v2 );
}
private:
@ -245,12 +240,13 @@ private:
std::string m_valueStr;
};
class UCODE;
class VAR_REF
{
public:
virtual VAR_TYPE_T GetType( UCODE* aUcode ) = 0;
virtual VAR_TYPE_T GetType() = 0;
virtual VALUE GetValue( UCODE* aUcode ) = 0;
};
@ -259,7 +255,6 @@ class UCODE
{
public:
class CONTEXT
{
public:
@ -268,6 +263,7 @@ public:
CONTEXT()
{
m_sp = 0;
for( int i = 0; i < c_memSize; i++ )
m_memory.push_back( VALUE() );
}
@ -294,6 +290,7 @@ public:
{
return m_sp;
}
private:
std::vector<VALUE> m_memory;
VALUE* m_stack[128];
@ -310,7 +307,7 @@ public:
VALUE* Run();
std::string Dump() const;
void RuntimeError( const std::string aErrorMsg );
void RuntimeError( const std::string& aErrorMsg );
virtual VAR_REF* createVarRef( COMPILER* aCompiler, const std::string& var, const std::string& field )
{
@ -330,12 +327,19 @@ private:
class UOP
{
public:
UOP( int op, void* arg ) : m_op( op ), m_arg( arg )
UOP( int op, void* arg ) :
m_op( op ),
m_arg( arg )
{};
UOP( int op, UCODE::FUNC_PTR func, void *arg ) : m_op( op ), m_arg(arg), m_func( func )
UOP( int op, UCODE::FUNC_PTR func, void *arg ) :
m_op( op ),
m_arg(arg),
m_func( std::move( func ) )
{};
void Exec( UCODE::CONTEXT* ctx, UCODE *ucode );
std::string Format() const;
private:
@ -363,6 +367,7 @@ public:
{
if( m_pos >= m_str.length() )
return 0;
return m_str[m_pos];
}
@ -381,13 +386,13 @@ public:
return m_pos;
}
std::string GetChars( std::function<bool( int )> cond ) const;
bool MatchAhead( std::string match, std::function<bool( int )> stopCond ) const;
bool MatchAhead( const std::string& match, std::function<bool( int )> stopCond ) const;
private:
std::string m_str;
size_t m_pos;
};
@ -397,7 +402,8 @@ public:
COMPILER();
virtual ~COMPILER();
/* clear() should be invoked by the client if a new input string is to be processed. It
/*
* clear() should be invoked by the client if a new input string is to be processed. It
* will reset the parser. User defined variables are retained.
*/
void Clear();
@ -406,6 +412,8 @@ public:
void parseError( const char* s );
void parseOk();
int GetSourcePos() const { return m_sourcePos; }
/* Check if previous invokation of process() was successful */
inline bool IsValid() const
{
@ -415,8 +423,7 @@ public:
void setRoot( LIBEVAL::TREE_NODE root );
bool Compile( const std::string& aString, UCODE* aCode );
std::string DumpTree();
void ReportError( const std::string aErrorMsg );
void ReportError( const std::string& aErrorMsg );
ERROR_STATUS GetErrorStatus();
protected:
@ -446,9 +453,6 @@ protected:
bool lexDefault( T_TOKEN& aToken );
bool lexString( T_TOKEN& aToken );
/* Used by processing loop */
void parse( int token, TREE_NODE value );
int resolveUnits();
UOP* makeUop( int op, double value )
@ -459,38 +463,33 @@ protected:
UOP* makeUop( int op, std::string value )
{
auto uop = new UOP( op, new VALUE( value ) );
UOP* uop = new UOP( op, new VALUE( std::move( value ) ) );
return uop;
}
UOP* makeUop( int op, VAR_REF* aRef = nullptr )
{
auto uop = new UOP( op, aRef );
UOP* uop = new UOP( op, aRef );
return uop;
}
UOP* makeUop( int op, UCODE::FUNC_PTR aFunc, void *arg = nullptr )
{
auto uop = new UOP( op, aFunc, arg );
UOP* uop = new UOP( op, std::move( aFunc ), arg );
return uop;
}
/* Token state for input string. */
void* m_parser; // the current lemon parser state machine
TOKENIZER m_tokenizer;
char m_localeDecimalSeparator;
/* Parse progress. Set by parser actions. */
bool m_parseError;
bool m_parseFinished;
std::string m_parseErrorMessage;
std::string m_parseErrorToken;
int m_parseErrorPos;
std::unique_ptr<UNIT_RESOLVER> m_unitResolver;
int m_sourcePos;
ERROR_STATUS m_errorStatus;
bool m_parseFinished;
TREE_NODE* m_tree;
};

View File

@ -77,8 +77,11 @@ public:
/**
* Explodes the list of netclass assignments to include atomic members of composite labels
* (buses).
*
* @param aRebuildFromScratch indicates the assignments should be rebuilt from the netclass
* membership lists before resolving.
*/
void ResolveNetClassAssignments();
void ResolveNetClassAssignments( bool aRebuildFromScratch = false );
private:
// TODO: Add diff pairs, bus information, etc here.

View File

@ -526,6 +526,11 @@ public:
return *this;
}
void SetDefault( T aValue )
{
m_default = aValue;
}
const wxString& ToString( T value ) const
{
return m_choices.GetLabel( static_cast<int>( value ) );
@ -533,7 +538,10 @@ public:
const T ToEnum( const wxString value )
{
if( m_reverseMap.count( value ) )
return m_reverseMap[ value ];
else
return m_default;
}
const wxPGChoices& Choices() const
@ -544,6 +552,7 @@ public:
private:
wxPGChoices m_choices;
std::unordered_map<wxString, T> m_reverseMap;
T m_default; // Returned if the string is not recognized
ENUM_MAP<T>()
{

View File

@ -204,15 +204,15 @@ static struct BOARD_CONNECTED_ITEM_DESC
{
BOARD_CONNECTED_ITEM_DESC()
{
auto& layerEnum = ENUM_MAP<PCB_LAYER_ID>::Instance();
ENUM_MAP<PCB_LAYER_ID>& layerEnum = ENUM_MAP<PCB_LAYER_ID>::Instance();
if( layerEnum.Choices().GetCount() == 0 )
{
layerEnum.SetDefault( UNDEFINED_LAYER );
for( LSEQ seq = LSET::AllLayersMask().Seq(); seq; ++seq )
{
layerEnum.Map( *seq, LSET::Name( *seq ) );
}
}
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
REGISTER_TYPE( BOARD_CONNECTED_ITEM );

View File

@ -590,9 +590,7 @@ void BOARD_DESIGN_SETTINGS::initFromOther( const BOARD_DESIGN_SETTINGS& aOther )
m_TrackWidthList = aOther.m_TrackWidthList;
m_ViasDimensionsList = aOther.m_ViasDimensionsList;
m_DiffPairDimensionsList = aOther.m_DiffPairDimensionsList;
m_DRCRuleSelectors = aOther.m_DRCRuleSelectors;
m_DRCRules = aOther.m_DRCRules;
m_matched = aOther.m_matched;
m_MicroViasAllowed = aOther.m_MicroViasAllowed;
m_BlindBuriedViaAllowed = aOther.m_BlindBuriedViaAllowed;
m_CurrentViaType = aOther.m_CurrentViaType;

View File

@ -48,52 +48,6 @@
#include <ratsnest/ratsnest_data.h>
#include <ratsnest/ratsnest_viewitem.h>
/**
* A singleton item of this class is returned for a weak reference that no longer exists.
* Its sole purpose is to flag the item as having been deleted.
*/
class DELETED_BOARD_ITEM : public BOARD_ITEM
{
public:
DELETED_BOARD_ITEM() :
BOARD_ITEM( nullptr, NOT_USED )
{}
wxString GetSelectMenuText( EDA_UNITS aUnits ) const override
{
return _( "(Deleted Item)" );
}
wxString GetClass() const override
{
return wxT( "DELETED_BOARD_ITEM" );
}
// pure virtuals:
void SetPosition( const wxPoint& ) override {}
wxPoint GetPosition() const override {
return wxPoint(0, 0);
}
static DELETED_BOARD_ITEM* GetInstance()
{
static DELETED_BOARD_ITEM* item = nullptr;
if( !item )
item = new DELETED_BOARD_ITEM();
return item;
}
#if defined(DEBUG)
void Show( int , std::ostream& ) const override {}
#endif
};
DELETED_BOARD_ITEM* g_DeletedItem = nullptr;
/* This is an odd place for this, but CvPcb won't link if it is
* in class_board_item.cpp like I first tried it.
*/

View File

@ -1,8 +1,3 @@
/**
* @file class_board_item.cpp
* @brief Class BOARD_ITEM definition and some basic functions.
*/
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
@ -29,7 +24,6 @@
*/
#include <fctsys.h>
#include <common.h>
#include <pcbnew.h>
#include <wx/debug.h>
@ -137,8 +131,9 @@ void BOARD_ITEM::SwapData( BOARD_ITEM* aImage )
}
void BOARD_ITEM::TransformShapeWithClearanceToPolygon(
SHAPE_POLY_SET& aCornerBuffer, int aClearanceValue, int aError, bool ignoreLineWidth ) const
void BOARD_ITEM::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
int aClearanceValue, int aError,
bool ignoreLineWidth ) const
{
wxASSERT_MSG( false, "Called TransformShapeWithClearanceToPolygon() on unsupported BOARD_ITEM." );
};
@ -148,15 +143,15 @@ static struct BOARD_ITEM_DESC
{
BOARD_ITEM_DESC()
{
auto& layerEnum = ENUM_MAP<PCB_LAYER_ID>::Instance();
ENUM_MAP<PCB_LAYER_ID>& layerEnum = ENUM_MAP<PCB_LAYER_ID>::Instance();
if( layerEnum.Choices().GetCount() == 0 )
{
layerEnum.SetDefault( UNDEFINED_LAYER );
for( LSEQ seq = LSET::AllLayersMask().Seq(); seq; ++seq )
{
layerEnum.Map( *seq, LSET::Name( *seq ) );
}
}
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
REGISTER_TYPE( BOARD_ITEM );

View File

@ -1647,8 +1647,8 @@ static struct MODULE_DESC
&MODULE::SetValue, &MODULE::GetValue ) );
propMgr.AddProperty( new PROPERTY<MODULE, double>( _( "Orientation" ),
&MODULE::SetOrientationDegrees, &MODULE::GetOrientationDegrees, PROPERTY_DISPLAY::DEGREE ) );
//propMgr.AddProperty( new PROPERTY<MODULE, int>( _( "Local Clearance" ),
// &MODULE::SetLocalClearance, &MODULE::GetLocalClearance, PROPERTY_DISPLAY::DISTANCE ) );
propMgr.AddProperty( new PROPERTY<MODULE, int>( _( "Local Clearance" ),
&MODULE::SetLocalClearance, &MODULE::GetLocalClearance, PROPERTY_DISPLAY::DISTANCE ) );
propMgr.AddProperty( new PROPERTY<MODULE, int>( _( "Local Solderpaste Margin" ),
&MODULE::SetLocalSolderPasteMargin, &MODULE::GetLocalSolderPasteMargin, PROPERTY_DISPLAY::DISTANCE ) );
propMgr.AddProperty( new PROPERTY<MODULE, double>( _( "Local Solderpaste Margin Ratio" ),

View File

@ -237,7 +237,10 @@ public:
int GetLocalSolderMaskMargin() const { return m_LocalSolderMaskMargin; }
void SetLocalSolderMaskMargin( int aMargin ) { m_LocalSolderMaskMargin = aMargin; }
int GetLocalClearance( wxString* aSource = nullptr ) const
int GetLocalClearance() const { return m_LocalClearance; }
void SetLocalClearance( int aClearance ) { m_LocalClearance = aClearance; }
int GetLocalClearance( wxString* aSource ) const
{
if( aSource )
*aSource = wxString::Format( _( "footprint %s" ), GetReference() );
@ -245,8 +248,6 @@ public:
return m_LocalClearance;
}
void SetLocalClearance( int aClearance ) { m_LocalClearance = aClearance; }
int GetLocalSolderPasteMargin() const { return m_LocalSolderPasteMargin; }
void SetLocalSolderPasteMargin( int aMargin ) { m_LocalSolderPasteMargin = aMargin; }

View File

@ -36,8 +36,7 @@ PANEL_PCBNEW_COLOR_SETTINGS::PANEL_PCBNEW_COLOR_SETTINGS( PCB_EDIT_FRAME* aFrame
: PANEL_COLOR_SETTINGS( aParent ),
m_frame( aFrame ),
m_page( nullptr ),
m_titleBlock( nullptr ),
m_ws( nullptr )
m_titleBlock( nullptr )
{
// Currently this only applies to eeschema
m_optOverrideColors->Hide();

View File

@ -61,8 +61,6 @@ private:
TITLE_BLOCK* m_titleBlock;
KIGFX::WS_PROXY_VIEW_ITEM* m_ws;
void createSwatches();
};

View File

@ -189,12 +189,11 @@ bool PANEL_SETUP_RULES::TransferDataFromWindow()
try
{
std::vector<DRC_SELECTOR*> dummySelectors;
std::vector<DRC_RULE*> dummyRules;
DRC_RULES_PARSER parser( m_frame->GetBoard(), m_textEditor->GetText(), _( "DRC rules" ) );
parser.Parse( dummySelectors, dummyRules );
parser.Parse( dummyRules );
}
catch( PARSE_ERROR& pe )
{
@ -249,7 +248,7 @@ void PANEL_SETUP_RULES::OnSyntaxHelp( wxHyperlinkEvent& aEvent )
"<pre>"
"(rule \"copper keepout\"\r"
" (disallow track) (disallow via) (disallow zone)\r"
" (condition \"A.name == no_copper\"))\r"
" (condition \"A.name == 'no_copper'\"))\r"
"\r"
"(rule \"BGA neckdown\"\r"
" (constraint track_width (min 0.2mm) (opt 0.25mm))\r"
@ -258,11 +257,11 @@ void PANEL_SETUP_RULES::OnSyntaxHelp( wxHyperlinkEvent& aEvent )
"\r"
"(rule HV\r"
" (constraint clearance (min 1.5mm))\r"
" (condition \"A.netclass == HV\"))\r"
" (condition \"A.netclass == 'HV'\"))\r"
"\r"
"(rule HV_HV\r"
" (constraint clearance (min 2.0mm))\r"
" (condition \"A.netclass == HV && B.netclass == HV\"))\r"
" (condition \"A.netclass == 'HV' && B.netclass == 'HV'\"))\r"
"</pre>";
HTML_MESSAGE_BOX dlg( m_parent, _( "Syntax Help" ) );

View File

@ -187,7 +187,6 @@ bool DRC::LoadRules()
if( rulesFile.FileExists() )
{
m_ruleSelectors.clear();
m_rules.clear();
FILE* fp = wxFopen( rulesFilepath, wxT( "rt" ) );
@ -197,12 +196,11 @@ bool DRC::LoadRules()
try
{
DRC_RULES_PARSER parser( m_pcb, fp, rulesFilepath );
parser.Parse( m_ruleSelectors, m_rules );
parser.Parse( m_rules );
}
catch( PARSE_ERROR& pe )
{
// Don't leave possibly malformed stuff around for us to trip over
m_ruleSelectors.clear();
m_rules.clear();
wxSafeYield( m_editFrame );
@ -214,10 +212,9 @@ bool DRC::LoadRules()
}
}
std::reverse( std::begin( m_ruleSelectors ), std::end( m_ruleSelectors ) );
std::reverse( std::begin( m_rules ), std::end( m_rules ) );
BOARD_DESIGN_SETTINGS& bds = m_pcb->GetDesignSettings();
bds.m_DRCRuleSelectors = m_ruleSelectors;
bds.m_DRCRules = m_rules;
return true;

View File

@ -132,7 +132,6 @@ private:
bool m_drcRun; // indicates DRC has been run at least once
bool m_footprintsTested; // indicates footprints were tested in last run
std::vector<DRC_SELECTOR*> m_ruleSelectors;
std::vector<DRC_RULE*> m_rules;
// Temp variables for performance during a single DRC run

View File

@ -27,6 +27,8 @@
#include <board_design_settings.h>
#include <class_board.h>
#include <class_board_item.h>
#include <pcb_expr_evaluator.h>
/*
* Rule tokens:
@ -52,8 +54,17 @@
* hole
*
*
* (rule "HV" (constraint clearance (min 200)))
* (rule "HV_external" (constraint clearance (min 400)))
* (rule "HV"
* (constraint clearance (min 200))
* (condition "A.Netclass == 'HV' || B.Netclass == 'HV'")
* )
*
* (rule "HV_external"
* (constraint clearance (min 400))
* (condition "(A.Netclass == 'HV' && (A.onLayer('F.Cu') || A.onLayer('B.Cu'))
* || (B.Netclass == 'HV' && (B.onLayer('F.Cu') || B.onLayer('B.Cu'))")
* )
*
* (rule "HV2HV" (constraint clearance (min 200)))
* (rule "HV2HV_external" (constraint clearance (min 500)))
* (rule "pad2padHV" (constraint clearance (min 500)))
@ -62,99 +73,83 @@
* (rule "neckdown" (constraint clearance (min 15)))
*
* (rule "disallowMicrovias" (disallow micro_via))
*
*
testEvalExpr( "A.type == \"Pad\" && B.type == \"Pad\" && (A.onLayer(\"F.Cu\"))",VAL(0.0), false, &trackA, &trackB );
return 0;
testEvalExpr( "A.Width > B.Width", VAL(0.0), false, &trackA, &trackB );
testEvalExpr( "A.Width + B.Width", VAL(Mils2iu(10) + Mils2iu(20)), false, &trackA, &trackB );
testEvalExpr( "A.Netclass", VAL( (const char*) trackA.GetNetClassName().c_str() ), false, &trackA, &trackB );
testEvalExpr( "(A.Netclass == \"HV\") && (B.netclass == \"otherClass\") && (B.netclass != \"F.Cu\")", VAL( 1.0 ), false, &trackA, &trackB );
testEvalExpr( "A.Netclass + 1.0", VAL( 1.0 ), false, &trackA, &trackB );
testEvalExpr( "A.type == \"Track\" && B.type == \"Track\" && A.layer == \"F.Cu\"", VAL(0.0), false, &trackA, &trackB );
testEvalExpr( "(A.type == \"Track\") && (B.type == \"Track\") && (A.layer == \"F.Cu\")", VAL(0.0), false, &trackA, &trackB );
*/
DRC_RULE* GetRule( const BOARD_ITEM* aItem, const BOARD_ITEM* bItem, int aConstraint )
{
// JEY TODO: the bulk of this will be replaced by Tom's expression evaluator
BOARD* board = aItem->GetBoard();
if( !board )
return nullptr;
NETCLASS* aNetclass = nullptr;
NETCLASS* bNetclass = nullptr;
if( aItem->IsConnected() )
aNetclass = static_cast<const BOARD_CONNECTED_ITEM*>( aItem )->GetEffectiveNetclass();
if( bItem && bItem->IsConnected() )
bNetclass = static_cast<const BOARD_CONNECTED_ITEM*>( bItem )->GetEffectiveNetclass();
for( DRC_SELECTOR* candidate : board->GetDesignSettings().m_DRCRuleSelectors )
for( DRC_RULE* rule : board->GetDesignSettings().m_DRCRules )
{
if( candidate->m_MatchNetclasses.size() == 2 )
if( ( rule->m_ConstraintFlags & aConstraint ) > 0 )
{
if( !bItem )
continue;
NETCLASS* firstNetclass = candidate->m_MatchNetclasses[0].get();
NETCLASS* secondNetclass = candidate->m_MatchNetclasses[1].get();
if( !( aNetclass == firstNetclass && bNetclass == secondNetclass )
&& !( aNetclass == secondNetclass && bNetclass == firstNetclass ) )
{
continue;
if( rule->m_Condition.EvaluateFor( aItem, bItem ) )
return rule;
}
}
else if( candidate->m_MatchNetclasses.size() == 1 )
{
NETCLASS* matchNetclass = candidate->m_MatchNetclasses[0].get();
if( matchNetclass != aNetclass && !( bItem && matchNetclass == bNetclass ) )
continue;
}
if( candidate->m_MatchTypes.size() == 2 )
{
if( !bItem )
continue;
KICAD_T firstType[2] = { candidate->m_MatchTypes[0], EOT };
KICAD_T secondType[2] = { candidate->m_MatchTypes[1], EOT };
if( !( aItem->IsType( firstType ) && bItem->IsType( secondType ) )
&& !( aItem->IsType( secondType ) && bItem->IsType( firstType ) ) )
{
continue;
}
}
else if( candidate->m_MatchTypes.size() == 1 )
{
KICAD_T matchType[2] = { candidate->m_MatchTypes[0], EOT };
if( !aItem->IsType( matchType ) && !( bItem && bItem->IsType( matchType ) ) )
continue;
}
if( candidate->m_MatchLayers.size() )
{
PCB_LAYER_ID matchLayer = candidate->m_MatchLayers[0];
if( !aItem->GetLayerSet().test( matchLayer ) )
continue;
}
if( candidate->m_MatchAreas.size() )
{
if( candidate->m_MatchAreas[0] == "$board" )
{
// matches everything
}
else
{
// TODO: area/room matches...
}
}
// All tests done; if we're still here then it matches
if( ( candidate->m_Rule->m_ConstraintFlags & aConstraint ) > 0 )
return candidate->m_Rule;
}
return nullptr;
}
DRC_RULE_CONDITION::DRC_RULE_CONDITION()
{
m_ucode = nullptr;
}
DRC_RULE_CONDITION::~DRC_RULE_CONDITION()
{
delete m_ucode;
}
bool DRC_RULE_CONDITION::EvaluateFor( const BOARD_ITEM* aItemA, const BOARD_ITEM* aItemB )
{
BOARD_ITEM* a = const_cast<BOARD_ITEM*>( aItemA );
BOARD_ITEM* b = aItemB ? const_cast<BOARD_ITEM*>( aItemB ) : DELETED_BOARD_ITEM::GetInstance();
m_ucode->SetItems( a, b );
// fixme: handle error conditions
return m_ucode->Run()->AsDouble() != 0.0;
}
bool DRC_RULE_CONDITION::Compile()
{
PCB_EXPR_COMPILER compiler;
if (!m_ucode)
m_ucode = new PCB_EXPR_UCODE;
bool ok = compiler.Compile( (const char*) m_Expression.c_str(), m_ucode );
if( ok )
return true;
m_compileError = compiler.GetErrorStatus();
return false;
}
LIBEVAL::ERROR_STATUS DRC_RULE_CONDITION::GetCompilationError()
{
return m_compileError;
}

View File

@ -27,9 +27,11 @@
#include <core/typeinfo.h>
#include <netclass.h>
#include <layers_id_colors_and_visibility.h>
#include <libeval_compiler/libeval_compiler.h>
class BOARD_ITEM;
class PCB_EXPR_UCODE;
#define CLEARANCE_CONSTRAINT (1 << 0)
@ -50,6 +52,52 @@ class BOARD_ITEM;
#define DISALLOW_FOOTPRINTS (1 << 9)
template<class T=int>
class MINOPTMAX
{
public:
T Min() const { assert( m_hasMin ); return m_min; };
T Max() const { assert( m_hasMax ); return m_max; };
T Opt() const { assert( m_hasOpt ); return m_opt; };
bool HasMin() const { return m_hasMin; }
bool HasMax() const { return m_hasMax; }
bool HasOpt() const { return m_hasOpt; }
void SetMin( T v ) { m_min = v; m_hasMin = true; }
void SetMax( T v ) { m_max = v; m_hasMax = true; }
void SetOpt( T v ) { m_opt = v; m_hasOpt = true; }
private:
T m_min;
T m_opt;
T m_max;
bool m_hasMin = false;
bool m_hasOpt = false;
bool m_hasMax = false;
};
class DRC_RULE_CONDITION
{
public:
DRC_RULE_CONDITION();
~DRC_RULE_CONDITION();
bool EvaluateFor( const BOARD_ITEM* aItemA, const BOARD_ITEM* aItemB );
bool Compile();
LIBEVAL::ERROR_STATUS GetCompilationError();
public:
wxString m_Expression;
wxString m_TargetRuleName;
private:
LIBEVAL::ERROR_STATUS m_compileError;
PCB_EXPR_UCODE* m_ucode;
};
class DRC_RULE
{
public:
@ -81,31 +129,11 @@ public:
MINOPTMAX m_TrackConstraint;
int m_MinHole;
wxString m_Condition;
};
class DRC_SELECTOR
{
public:
DRC_SELECTOR() :
m_Priority( 1 ),
m_Rule( nullptr )
{ }
public:
std::vector<NETCLASSPTR> m_MatchNetclasses;
std::vector<KICAD_T> m_MatchTypes;
std::vector<PCB_LAYER_ID> m_MatchLayers;
std::vector<wxString> m_MatchAreas;
int m_Priority; // 0 indicates automatic priority generation
DRC_RULE* m_Rule;
DRC_RULE_CONDITION m_Condition;
};
DRC_RULE* GetRule( const BOARD_ITEM* aItem, const BOARD_ITEM* bItem, int aConstraint );
#endif // DRC_RULE_H

View File

@ -65,10 +65,8 @@ void DRC_RULES_PARSER::initLayerMap()
}
void DRC_RULES_PARSER::Parse( std::vector<DRC_SELECTOR*>& aSelectors,
std::vector<DRC_RULE*>& aRules )
void DRC_RULES_PARSER::Parse( std::vector<DRC_RULE*>& aRules )
{
std::vector< std::pair<DRC_SELECTOR*, wxString> > selectorRules;
bool haveVersion = false;
for( T token = NextTok(); token != T_EOF; token = NextTok() )
@ -91,15 +89,6 @@ void DRC_RULES_PARSER::Parse( std::vector<DRC_SELECTOR*>& aSelectors,
NeedRIGHT();
break;
case T_selector:
{
wxString ruleName;
aSelectors.push_back( parseDRC_SELECTOR( &ruleName ) );
selectorRules.emplace_back( aSelectors.back(), ruleName );
}
break;
case T_rule:
aRules.push_back( parseDRC_RULE() );
break;
@ -108,118 +97,6 @@ void DRC_RULES_PARSER::Parse( std::vector<DRC_SELECTOR*>& aSelectors,
Expecting( "selector or rule" );
}
}
// Hook up the selectors to their rules
std::map<wxString, DRC_RULE*> ruleMap;
for( DRC_RULE* rule : aRules )
ruleMap[ rule->m_Name ] = rule;
for( const std::pair<DRC_SELECTOR*, wxString>& entry : selectorRules )
{
if( ruleMap.count( entry.second ) )
{
entry.first->m_Rule = ruleMap[ entry.second ];
}
else
{
wxString errText = wxString::Format( _( "Rule \"%s\" not found." ), entry.second );
THROW_PARSE_ERROR( errText, CurSource(), "", 0, 0 );
}
}
}
DRC_SELECTOR* DRC_RULES_PARSER::parseDRC_SELECTOR( wxString* aRuleName )
{
NETCLASSES& netclasses = m_board->GetDesignSettings().GetNetClasses();
DRC_SELECTOR* selector = new DRC_SELECTOR();
T token;
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_match_netclass:
{
NeedSYMBOL();
NETCLASSPTR netclass = netclasses.Find( FromUTF8() );
if( netclass )
{
selector->m_MatchNetclasses.push_back( std::move( netclass ) );
}
else
{
// Interesting situation here: if we don't inform the user they may have a typo
// and can't figure out why their rules don't work.
// If we do tell them then it gets really noisy if they're using a single rule
// file for a class of board.
}
NeedRIGHT();
}
break;
case T_match_type:
switch( NextTok() )
{
case T_track: selector->m_MatchTypes.push_back( PCB_TRACE_T ); break;
case T_via: selector->m_MatchTypes.push_back( PCB_LOCATE_STDVIA_T ); break;
case T_micro_via: selector->m_MatchTypes.push_back( PCB_LOCATE_UVIA_T ); break;
case T_buried_via: selector->m_MatchTypes.push_back( PCB_LOCATE_BBVIA_T ); break;
case T_pad: selector->m_MatchTypes.push_back( PCB_PAD_T ); break;
case T_zone: selector->m_MatchTypes.push_back( PCB_ZONE_AREA_T ); break;
case T_text: selector->m_MatchTypes.push_back( PCB_LOCATE_TEXT_T ); break;
case T_graphic: selector->m_MatchTypes.push_back( PCB_LOCATE_GRAPHIC_T ); break;
case T_hole: selector->m_MatchTypes.push_back( PCB_LOCATE_HOLE_T ); break;
case T_npth: selector->m_MatchTypes.push_back( PCB_LOCATE_NPTH_T ); break;
case T_pth: selector->m_MatchTypes.push_back( PCB_LOCATE_PTH_T ); break;
case T_board_edge: selector->m_MatchTypes.push_back( PCB_LOCATE_BOARD_EDGE_T ); break;
default: Expecting( "track, via, micro_via, buried_via, pad, zone, text, "
"graphic, hole, npth, pth, or board_edge" );
}
NeedRIGHT();
break;
case T_match_layer:
NeedSYMBOL();
if( m_layerMap.count( curText ) )
{
selector->m_MatchLayers.push_back( m_layerMap[ curText ] );
}
else
{
wxString errText = wxString::Format( _( "Layer \"%s\" not found." ),
wxString( curText ) );
THROW_PARSE_ERROR( errText, CurSource(), CurLine(), CurLineNumber(), CurOffset() );
}
NeedRIGHT();
break;
case T_match_area:
// TODO
break;
case T_rule:
NeedSYMBOL();
*aRuleName = FromUTF8();
NeedRIGHT();
break;
default:
Expecting( "match_netclass, match_type, match_layer, match_area, or rule" );
}
}
return selector;
}
@ -269,7 +146,15 @@ DRC_RULE* DRC_RULES_PARSER::parseDRC_RULE()
case T_condition:
NeedSYMBOL();
rule->m_Condition = FromUTF8();
rule->m_Condition.m_Expression = FromUTF8();
if( !rule->m_Condition.Compile() )
{
LIBEVAL::ERROR_STATUS error = rule->m_Condition.GetCompilationError();
THROW_PARSE_ERROR( error.message, CurSource(), CurLine(), CurLineNumber(),
CurOffset() + error.srcPos );
}
NeedRIGHT();
break;

View File

@ -43,13 +43,11 @@ public:
DRC_RULES_PARSER( BOARD* aBoard, const wxString& aSource, const wxString& aSourceDescr );
DRC_RULES_PARSER( BOARD* aBoard, FILE* aFile, const wxString& aFilename );
void Parse( std::vector<DRC_SELECTOR*>& aSelectors, std::vector<DRC_RULE*>& aRules );
void Parse( std::vector<DRC_RULE*>& aRules );
private:
void initLayerMap();
DRC_SELECTOR* parseDRC_SELECTOR( wxString* aRuleName );
DRC_RULE* parseDRC_RULE();
void parseConstraint( DRC_RULE* aRule );

View File

@ -653,7 +653,7 @@ void PCB_EDIT_FRAME::ShowBoardSetupDialog( const wxString& aInitialPage, const w
if( dlg.ShowQuasiModal() == wxID_OK )
{
Prj().GetProjectFile().NetSettings().ResolveNetClassAssignments();
Prj().GetProjectFile().NetSettings().ResolveNetClassAssignments( true );
GetBoard()->SynchronizeNetsAndNetClasses();
GetBoard()->GetDesignSettings().SetCurrentNetClass( NETCLASS::Default );

View File

@ -23,7 +23,7 @@
#include <cstdio>
#include <boost/algorithm/string/case_conv.hpp>
#include "class_board.h"
#include "pcb_expr_evaluator.h"
@ -41,13 +41,10 @@ class PCB_EXPR_BUILTIN_FUNCTIONS
return self;
}
std::string tolower( const std::string str ) const
std::string tolower( const std::string& str ) const
{
std::string rv;
std::transform(str.begin(),
str.end(),
rv.begin(),
::tolower);
std::transform( str.begin(), str.end(), rv.begin(), ::tolower );
return rv;
}
@ -62,52 +59,57 @@ class PCB_EXPR_BUILTIN_FUNCTIONS
}
private:
std::map<std::string, FPTR> m_funcs;
static void onLayer( LIBEVAL::UCODE* aUcode, LIBEVAL::UCODE::CONTEXT* aCtx, void *self )
{
auto arg = aCtx->Pop();
auto vref = static_cast<PCB_EXPR_VAR_REF*>( self );
auto conv = ENUM_MAP<PCB_LAYER_ID>::Instance();
bool value = vref->GetObject(aUcode)->IsOnLayer( conv.ToEnum( arg->AsString() ) );
auto rv = aCtx->AllocValue();
rv->Set( value ? 1.0 : 0.0 );
LIBEVAL::VALUE* arg = aCtx->Pop();
PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
PCB_LAYER_ID layer = ENUM_MAP<PCB_LAYER_ID>::Instance().ToEnum( arg->AsString() );
BOARD_ITEM* item = vref->GetObject( aUcode );
LIBEVAL::VALUE* rv = aCtx->AllocValue();
rv->Set( item->IsOnLayer( layer ) ? 1.0 : 0.0 );
aCtx->Push( rv );
}
static void isPlated( LIBEVAL::UCODE* aUcode, LIBEVAL::UCODE::CONTEXT* aCtx, void *self )
{
auto vref = static_cast<PCB_EXPR_VAR_REF*>( self );
auto item = vref->GetObject(aUcode);
PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
BOARD_ITEM* item = vref->GetObject( aUcode );
bool result = false;
if( item->Type() == PCB_PAD_T )
{
auto pad = static_cast<D_PAD*>( item );
D_PAD* pad = static_cast<D_PAD*>( item );
result = pad->GetAttribute() == PAD_ATTRIB_STANDARD;
}
auto rv = aCtx->AllocValue();
LIBEVAL::VALUE* rv = aCtx->AllocValue();
rv->Set( result ? 1.0 : 0.0 );
aCtx->Push( rv );
}
};
PCB_EXPR_BUILTIN_FUNCTIONS::PCB_EXPR_BUILTIN_FUNCTIONS()
{
m_funcs[ "onlayer" ] = onLayer;
m_funcs[ "isplated" ] = isPlated;
}
BOARD_ITEM* PCB_EXPR_VAR_REF::GetObject( LIBEVAL::UCODE* aUcode ) const
{
auto ucode = static_cast<const PCB_EXPR_UCODE*>( aUcode );
auto item = ucode->GetItem( m_itemIndex );
const PCB_EXPR_UCODE* ucode = static_cast<const PCB_EXPR_UCODE*>( aUcode );
BOARD_ITEM* item = ucode->GetItem( m_itemIndex );
return item;
}
LIBEVAL::VALUE PCB_EXPR_VAR_REF::GetValue( LIBEVAL::UCODE* aUcode )
{
auto item = GetObject( aUcode );
BOARD_ITEM* item = const_cast<BOARD_ITEM*>( GetObject( aUcode ) );
auto it = m_matchingTypes.find( TYPE_HASH( *item ) );
if( it == m_matchingTypes.end() )
@ -128,7 +130,9 @@ LIBEVAL::VALUE PCB_EXPR_VAR_REF::GetValue( LIBEVAL::UCODE* aUcode )
{
//printf("item %p Get string '%s'\n", item, (const char*) it->second->Name().c_str() );
str = item->Get<wxString>( it->second );
} else {
}
else
{
const auto& any = item->Get( it->second );
any.GetAs<wxString>( &str );
//printf("item %p get enum: '%s'\n", item , (const char*) str.c_str() );
@ -138,26 +142,42 @@ LIBEVAL::VALUE PCB_EXPR_VAR_REF::GetValue( LIBEVAL::UCODE* aUcode )
}
}
LIBEVAL::UCODE::FUNC_PTR PCB_EXPR_UCODE::createFuncCall( LIBEVAL::COMPILER* aCompiler, const std::string& name )
LIBEVAL::UCODE::FUNC_PTR PCB_EXPR_UCODE::createFuncCall( LIBEVAL::COMPILER* aCompiler,
const std::string& name )
{
auto registry = PCB_EXPR_BUILTIN_FUNCTIONS::Instance();
auto f = registry.Get( name );
PCB_EXPR_BUILTIN_FUNCTIONS& registry = PCB_EXPR_BUILTIN_FUNCTIONS::Instance();
auto f = registry.Get( boost::to_lower_copy( name ) );
return f;
}
LIBEVAL::VAR_REF* PCB_EXPR_UCODE::createVarRef( LIBEVAL::COMPILER *aCompiler,
const std::string& var, const std::string& field )
{
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
PCB_EXPR_VAR_REF* vref = nullptr;
auto classes = propMgr.GetAllClasses();
auto vref = new PCB_EXPR_VAR_REF( var == "A" ? 0 : 1 );
if( var == "A" )
{
vref = new PCB_EXPR_VAR_REF( 0 );
}
else if( var == "B" )
{
vref = new PCB_EXPR_VAR_REF( 1 );
}
else
{
aCompiler->ReportError( "var" );
return vref;
}
if( field.empty() ) // return reference to base object
return vref;
for( auto cls : classes )
for( const PROPERTY_MANAGER::CLASS_INFO& cls : propMgr.GetAllClasses() )
{
if( propMgr.IsOfType( cls.type, TYPE_HASH( BOARD_ITEM ) ) )
{
@ -167,10 +187,15 @@ LIBEVAL::VAR_REF* PCB_EXPR_UCODE::createVarRef( LIBEVAL::COMPILER *aCompiler,
{
//printf("Field '%s' class %s ptr %p haschoices %d typeid %s\n", field.c_str(), (const char *) cls.name.c_str(), prop, !!prop->HasChoices(), typeid(*prop).name() );
vref->AddAllowedClass( cls.type, prop );
if( prop->TypeHash() == TYPE_HASH( int ) )
{
vref->SetType( LIBEVAL::VT_NUMERIC );
}
else if( prop->TypeHash() == TYPE_HASH( wxString ) )
{
vref->SetType( LIBEVAL::VT_STRING );
}
else if ( prop->HasChoices() )
{ // it's an enum, we treat it as string
vref->SetType( LIBEVAL::VT_STRING );
@ -186,6 +211,9 @@ LIBEVAL::VAR_REF* PCB_EXPR_UCODE::createVarRef( LIBEVAL::COMPILER *aCompiler,
}
}
if( vref->GetType() == LIBEVAL::VT_UNDEFINED )
aCompiler->ReportError( "field" );
return vref;
}
@ -204,19 +232,16 @@ public:
return pcbUnits;
}
virtual double Convert( const std::string aString, int unitId ) const override
virtual double Convert( const std::string& aString, int unitId ) const override
{
double v = atof( aString.c_str() );
switch( unitId )
{
case 0:
return Mils2iu( v );
case 1:
return Millimeter2iu( v );
case 2:
return Mils2iu( v * 1000.0 );
default:
return v;
case 0: return Mils2iu( v );
case 1: return Millimeter2iu( v );
case 2: return Mils2iu( v * 1000.0 );
default: return v;
}
};
};

View File

@ -66,6 +66,7 @@ class PCB_EXPR_VAR_REF : public LIBEVAL::VAR_REF
public:
PCB_EXPR_VAR_REF( int aItemIndex ) :
m_itemIndex( aItemIndex ),
m_type( LIBEVAL::VT_UNDEFINED ),
m_isEnum( false )
{
//printf("*** createVarRef %p %d\n", this, aItemIndex );
@ -84,7 +85,7 @@ public:
m_matchingTypes[type_hash] = prop;
}
virtual LIBEVAL::VAR_TYPE_T GetType( LIBEVAL::UCODE* aUcode ) override
virtual LIBEVAL::VAR_TYPE_T GetType() override
{
return m_type;
}
@ -116,10 +117,7 @@ public:
~PCB_EXPR_EVALUATOR();
bool Evaluate( const wxString& aExpr );
int Result() const
{
return m_result;
}
int Result() const { return m_result; }
wxString GetErrorString();
private:

View File

@ -186,7 +186,6 @@ bool DRC::LoadRules()
if( rulesFile.FileExists() )
{
m_ruleSelectors.clear();
m_rules.clear();
FILE* fp = wxFopen( rulesFilepath, wxT( "rt" ) );
@ -196,12 +195,11 @@ bool DRC::LoadRules()
try
{
DRC_RULES_PARSER parser( m_pcb, fp, rulesFilepath );
parser.Parse( m_ruleSelectors, m_rules );
parser.Parse( m_rules );
}
catch( PARSE_ERROR& pe )
{
// Don't leave possibly malformed stuff around for us to trip over
m_ruleSelectors.clear();
m_rules.clear();
wxSafeYield( m_editFrame );

View File

@ -47,23 +47,26 @@ test::DRC_RULE_CONDITION::DRC_RULE_CONDITION()
m_ucode = nullptr;
}
test::DRC_RULE_CONDITION::~DRC_RULE_CONDITION()
{
if( m_ucode )
delete m_ucode;
}
bool test::DRC_RULE_CONDITION::EvaluateFor( BOARD_ITEM* aItemA, BOARD_ITEM* aItemB )
bool test::DRC_RULE_CONDITION::EvaluateFor( const BOARD_ITEM* aItemA, const BOARD_ITEM* aItemB )
{
m_ucode->SetItems( aItemA, aItemB );
m_ucode->SetItems( const_cast<BOARD_ITEM*>( aItemA ), const_cast<BOARD_ITEM*>( aItemB ) );
// fixme: handle error conditions
return m_ucode->Run()->AsDouble() != 0.0;
}
bool test::DRC_RULE_CONDITION::Compile()
{
PCB_EXPR_COMPILER compiler;
if (!m_ucode)
m_ucode = new PCB_EXPR_UCODE;
@ -79,6 +82,7 @@ bool test::DRC_RULE_CONDITION::Compile()
return false;
}
LIBEVAL::ERROR_STATUS test::DRC_RULE_CONDITION::GetCompilationError()
{
return m_compileError;

View File

@ -130,7 +130,7 @@ public:
DRC_RULE_CONDITION();
~DRC_RULE_CONDITION();
bool EvaluateFor( BOARD_ITEM* aItemA, BOARD_ITEM* aItemB );
bool EvaluateFor( const BOARD_ITEM* aItemA, const BOARD_ITEM* aItemB );
bool Compile();
LIBEVAL::ERROR_STATUS GetCompilationError();

View File

@ -121,17 +121,17 @@ int main( int argc, char *argv[] )
printf( "TrkA %p netclass '%s'\n", &trackA, (const char*) trackA.GetNetClassName().c_str() );
printf( "TrkB %p netclass '%s'\n", &trackB, (const char*) trackB.GetNetClassName().c_str() );
// testEvalExpr( "A.onlayer(\"F.Cu\") || A.onlayer(\"B.Cu\")", VAL(1.0), false, &trackA, &trackB );
testEvalExpr( "A.type == \"Pad\" && B.type == \"Pad\" && (A.onLayer(\"F.Cu\"))",VAL(0.0), false, &trackA, &trackB );
// testEvalExpr( "A.onlayer('F.Cu') || A.onlayer('B.Cu')", VAL( 1.0 ), false, &trackA, &trackB );
testEvalExpr( "A.type == 'Pad' && B.type == 'Pad' && (A.onLayer('F.Cu'))", VAL( 0.0 ), false, &trackA, &trackB );
return 0;
testEvalExpr( "A.Width > B.Width", VAL( 0.0 ), false, &trackA, &trackB );
testEvalExpr( "A.Width + B.Width", VAL( Mils2iu(10) + Mils2iu(20) ), false, &trackA, &trackB );
testEvalExpr( "A.Netclass", VAL( (const char*) trackA.GetNetClassName().c_str() ), false, &trackA, &trackB );
testEvalExpr( "(A.Netclass == \"HV\") && (B.netclass == \"otherClass\") && (B.netclass != \"F.Cu\")", VAL( 1.0 ), false, &trackA, &trackB );
testEvalExpr( "(A.Netclass == 'HV') && (B.netclass == 'otherClass') && (B.netclass != 'F.Cu')", VAL( 1.0 ), false, &trackA, &trackB );
testEvalExpr( "A.Netclass + 1.0", VAL( 1.0 ), false, &trackA, &trackB );
testEvalExpr( "A.type == \"Track\" && B.type == \"Track\" && A.layer == \"F.Cu\"", VAL(0.0), false, &trackA, &trackB );
testEvalExpr( "(A.type == \"Track\") && (B.type == \"Track\") && (A.layer == \"F.Cu\")", VAL(0.0), false, &trackA, &trackB );
testEvalExpr( "A.type == 'Track' && B.type == 'Track' && A.layer == 'F.Cu'", VAL( 0.0 ), false, &trackA, &trackB );
testEvalExpr( "(A.type == 'Track') && (B.type == 'Track') && (A.layer == 'F.Cu')", VAL( 0.0 ), false, &trackA, &trackB );
return 0;
}