Browse Source

Added short circuit evaluation of compile time expressions

git-svn-id: trunk@2331 -
olle 19 years ago
parent
commit
7b811ac58e
1 changed files with 165 additions and 124 deletions
  1. 165 124
      compiler/scanner.pas

+ 165 - 124
compiler/scanner.pas

@@ -519,6 +519,12 @@ The TRUE/FALSE format is new from 22 august 2005, but the above scheme
 means that units which is not recompiled, and thus stores
 means that units which is not recompiled, and thus stores
 compile time variables as the old format (0/1), continue to work.
 compile time variables as the old format (0/1), continue to work.
 
 
+Short circuit evaluation
+------------------------
+For this to work, the part of a compile time expression which is short
+circuited, should not be evaluated, while it still should be parsed.
+Therefor there is a parameter eval, telling whether evaluation is needed.
+In case not, the value returned can be arbitrary.
 }
 }
 
 
     type
     type
@@ -559,16 +565,16 @@ compile time variables as the old format (0/1), continue to work.
 
 
     function parse_compiler_expr(var compileExprType: TCTETypeSet):string;
     function parse_compiler_expr(var compileExprType: TCTETypeSet):string;
 
 
-        function read_expr(var exprType: TCTETypeSet) : string; forward;
+        function read_expr(var exprType: TCTETypeSet; eval : Boolean) : string; forward;
 
 
         procedure preproc_consume(t : ttoken);
         procedure preproc_consume(t : ttoken);
         begin
         begin
           if t<>current_scanner.preproc_token then
           if t<>current_scanner.preproc_token then
-           Message(scan_e_preproc_syntax_error);
+            Message(scan_e_preproc_syntax_error);
           current_scanner.preproc_token:=current_scanner.readpreproc;
           current_scanner.preproc_token:=current_scanner.readpreproc;
         end;
         end;
 
 
-        function preproc_substitutedtoken(var macroType: TCTETypeSet): string;
+        function preproc_substitutedtoken(var macroType: TCTETypeSet; eval : Boolean): string;
                                 { Currently this parses identifiers as well as numbers.
                                 { Currently this parses identifiers as well as numbers.
           The result from this procedure can either be that the token
           The result from this procedure can either be that the token
           itself is a value, or that it is a compile time variable/macro,
           itself is a value, or that it is a compile time variable/macro,
@@ -584,6 +590,9 @@ compile time variables as the old format (0/1), continue to work.
           w: word;
           w: word;
         begin
         begin
           result := current_scanner.preproc_pattern;
           result := current_scanner.preproc_pattern;
+          if not eval then
+            exit;
+
           mac:= nil;
           mac:= nil;
           { Substitue macros and compiler variables with their content/value.
           { Substitue macros and compiler variables with their content/value.
             For real macros also do recursive substitution. }
             For real macros also do recursive substitution. }
@@ -660,7 +669,7 @@ compile time variables as the old format (0/1), continue to work.
             macroType:= [ctetString];
             macroType:= [ctetString];
         end;
         end;
 
 
-        function read_factor(var factorType: TCTETypeSet) : string;
+        function read_factor(var factorType: TCTETypeSet; eval : Boolean) : string;
         var
         var
            hs : string;
            hs : string;
            mac: tmacro;
            mac: tmacro;
@@ -786,33 +795,36 @@ compile time variables as the old format (0/1), continue to work.
                         current_scanner.skipspace;
                         current_scanner.skipspace;
                       end
                       end
                     else
                     else
-                      Message(scan_e_error_in_preproc_expr);
-                    if searchsym(current_scanner.preproc_pattern,srsym,srsymtable) then
-                      begin
-                        l:=0;
-                        case srsym.typ of
-                          globalvarsym,
-                          localvarsym,
-                          paravarsym :
-                            l:=tabstractvarsym(srsym).getsize;
-                          typedconstsym :
-                            l:=ttypedconstsym(srsym).getsize;
-                          typesym:
-                            l:=ttypesym(srsym).restype.def.size;
-                          else
-                            Message(scan_e_error_in_preproc_expr);
-                        end;
-                        str(l,read_factor);
-                        preproc_consume(_ID);
-                        current_scanner.skipspace;
-                      end
-                    else
-                      Message1(sym_e_id_not_found,current_scanner.preproc_pattern);
+                      Message(scan_e_preproc_syntax_error);
+
+                    if eval then
+                      if searchsym(current_scanner.preproc_pattern,srsym,srsymtable) then
+                        begin
+                          l:=0;
+                          case srsym.typ of
+                            globalvarsym,
+                            localvarsym,
+                            paravarsym :
+                              l:=tabstractvarsym(srsym).getsize;
+                            typedconstsym :
+                              l:=ttypedconstsym(srsym).getsize;
+                            typesym:
+                              l:=ttypesym(srsym).restype.def.size;
+                            else
+                              Message(scan_e_error_in_preproc_expr);
+                          end;
+                          str(l,read_factor);
+                        end
+                      else
+                        Message1(sym_e_id_not_found,current_scanner.preproc_pattern);
+
+                    preproc_consume(_ID);
+                    current_scanner.skipspace;
 
 
                     if current_scanner.preproc_token =_RKLAMMER then
                     if current_scanner.preproc_token =_RKLAMMER then
                       preproc_consume(_RKLAMMER)
                       preproc_consume(_RKLAMMER)
                     else
                     else
-                      Message(scan_e_error_in_preproc_expr);
+                      Message(scan_e_preproc_syntax_error);
                   end
                   end
                 else
                 else
                 if current_scanner.preproc_pattern='DECLARED' then
                 if current_scanner.preproc_pattern='DECLARED' then
@@ -850,14 +862,19 @@ compile time variables as the old format (0/1), continue to work.
                   begin
                   begin
                     factorType:= [ctetBoolean];
                     factorType:= [ctetBoolean];
                     preproc_consume(_ID);
                     preproc_consume(_ID);
-                    hs:=read_factor(factorType);
-                    if not (ctetBoolean in factorType) then
-                      CTEError(factorType, [ctetBoolean], 'NOT');
-                    val(hs,l,w);
-                    if l<>0 then
-                      read_factor:='0'
+                    hs:=read_factor(factorType, eval);
+                    if eval then
+                      begin
+                        if not (ctetBoolean in factorType) then
+                          CTEError(factorType, [ctetBoolean], 'NOT');
+                        val(hs,l,w);
+                        if l<>0 then
+                          read_factor:='0'
+                        else
+                          read_factor:='1';
+                      end
                     else
                     else
-                      read_factor:='1';
+                      read_factor:='0'; {Just to have something}
                   end
                   end
                 else
                 else
                 if (m_mac in aktmodeswitches) and (current_scanner.preproc_pattern='TRUE') then
                 if (m_mac in aktmodeswitches) and (current_scanner.preproc_pattern='TRUE') then
@@ -875,11 +892,11 @@ compile time variables as the old format (0/1), continue to work.
                   end
                   end
                 else
                 else
                   begin
                   begin
-                    hs:=preproc_substitutedtoken(factorType);
+                    hs:=preproc_substitutedtoken(factorType, eval);
 
 
                     { Default is to return the original symbol }
                     { Default is to return the original symbol }
                     read_factor:=hs;
                     read_factor:=hs;
-                    if (m_delphi in aktmodeswitches) and (ctetString in factorType) then
+                    if eval and (m_delphi in aktmodeswitches) and (ctetString in factorType) then
                       if searchsym(current_scanner.preproc_pattern,srsym,srsymtable) then
                       if searchsym(current_scanner.preproc_pattern,srsym,srsymtable) then
                         begin
                         begin
                           case srsym.typ of
                           case srsym.typ of
@@ -947,7 +964,7 @@ compile time variables as the old format (0/1), continue to work.
            else if current_scanner.preproc_token =_LKLAMMER then
            else if current_scanner.preproc_token =_LKLAMMER then
              begin
              begin
                 preproc_consume(_LKLAMMER);
                 preproc_consume(_LKLAMMER);
-                read_factor:=read_expr(factorType);
+                read_factor:=read_expr(factorType, eval);
                 preproc_consume(_RKLAMMER);
                 preproc_consume(_RKLAMMER);
              end
              end
            else if current_scanner.preproc_token = _LECKKLAMMER then
            else if current_scanner.preproc_token = _LECKKLAMMER then
@@ -956,7 +973,7 @@ compile time variables as the old format (0/1), continue to work.
                read_factor := ',';
                read_factor := ',';
                while current_scanner.preproc_token = _ID do
                while current_scanner.preproc_token = _ID do
                begin
                begin
-                 read_factor := read_factor+read_factor(setElemType)+',';
+                 read_factor := read_factor+read_factor(setElemType, eval)+',';
                  if current_scanner.preproc_token = _COMMA then
                  if current_scanner.preproc_token = _COMMA then
                    preproc_consume(_COMMA);
                    preproc_consume(_COMMA);
                end;
                end;
@@ -968,80 +985,98 @@ compile time variables as the old format (0/1), continue to work.
              Message(scan_e_error_in_preproc_expr);
              Message(scan_e_error_in_preproc_expr);
         end;
         end;
 
 
-        function read_term(var termType: TCTETypeSet) : string;
+        function read_term(var termType: TCTETypeSet; eval : Boolean) : string;
         var
         var
            hs1,hs2 : string;
            hs1,hs2 : string;
            l1,l2 : longint;
            l1,l2 : longint;
            w : integer;
            w : integer;
            termType2: TCTETypeSet;
            termType2: TCTETypeSet;
         begin
         begin
-          hs1:=read_factor(termType);
+          hs1:=read_factor(termType, eval);
           repeat
           repeat
             if (current_scanner.preproc_token<>_ID) then
             if (current_scanner.preproc_token<>_ID) then
               break;
               break;
             if current_scanner.preproc_pattern<>'AND' then
             if current_scanner.preproc_pattern<>'AND' then
               break;
               break;
 
 
-            {Check if first expr is boolean. Must be done here, after we know
-             it is an AND expression.}
-            if not (ctetBoolean in termType) then
-              CTEError(termType, [ctetBoolean], 'AND');
-            termType:= [ctetBoolean];
+            val(hs1,l1,w);
+            if l1=0 then
+              eval:= false; {Short circuit evaluation of OR}
+
+            if eval then
+               begin
+                {Check if first expr is boolean. Must be done here, after we know
+                 it is an AND expression.}
+                if not (ctetBoolean in termType) then
+                  CTEError(termType, [ctetBoolean], 'AND');
+                termType:= [ctetBoolean];
+              end;
 
 
             preproc_consume(_ID);
             preproc_consume(_ID);
-            hs2:=read_factor(termType2);
+            hs2:=read_factor(termType2, eval);
 
 
-            if not (ctetBoolean in termType2) then
-              CTEError(termType2, [ctetBoolean], 'AND');
+            if eval then
+              begin
+                if not (ctetBoolean in termType2) then
+                  CTEError(termType2, [ctetBoolean], 'AND');
 
 
-            val(hs1,l1,w);
-            val(hs2,l2,w);
-            if (l1<>0) and (l2<>0) then
-              hs1:='1'
-            else
-              hs1:='0';
+                val(hs2,l2,w);
+                if (l1<>0) and (l2<>0) then
+                  hs1:='1'
+                else
+                  hs1:='0';
+              end;
            until false;
            until false;
            read_term:=hs1;
            read_term:=hs1;
         end;
         end;
 
 
 
 
-        function read_simple_expr(var simpleExprType: TCTETypeSet) : string;
+        function read_simple_expr(var simpleExprType: TCTETypeSet; eval : Boolean) : string;
         var
         var
            hs1,hs2 : string;
            hs1,hs2 : string;
            l1,l2 : longint;
            l1,l2 : longint;
            w : integer;
            w : integer;
            simpleExprType2: TCTETypeSet;
            simpleExprType2: TCTETypeSet;
         begin
         begin
-          hs1:=read_term(simpleExprType);
+          hs1:=read_term(simpleExprType, eval);
           repeat
           repeat
             if (current_scanner.preproc_token<>_ID) then
             if (current_scanner.preproc_token<>_ID) then
               break;
               break;
             if current_scanner.preproc_pattern<>'OR' then
             if current_scanner.preproc_pattern<>'OR' then
               break;
               break;
 
 
-            {Check if first expr is boolean. Must be done here, after we know
-             it is an OR expression.}
-            if not (ctetBoolean in simpleExprType) then
-              CTEError(simpleExprType, [ctetBoolean], 'OR');
-            simpleExprType:= [ctetBoolean];
+            val(hs1,l1,w);
+            if l1<>0 then
+              eval:= false; {Short circuit evaluation of OR}
+
+            if eval then
+              begin
+                {Check if first expr is boolean. Must be done here, after we know
+                 it is an OR expression.}
+                if not (ctetBoolean in simpleExprType) then
+                  CTEError(simpleExprType, [ctetBoolean], 'OR');
+                simpleExprType:= [ctetBoolean];
+              end;
 
 
             preproc_consume(_ID);
             preproc_consume(_ID);
-            hs2:=read_term(simpleExprType2);
+            hs2:=read_term(simpleExprType2, eval);
 
 
-            if not (ctetBoolean in simpleExprType2) then
-              CTEError(simpleExprType2, [ctetBoolean], 'OR');
+            if eval then
+              begin
+                if not (ctetBoolean in simpleExprType2) then
+                  CTEError(simpleExprType2, [ctetBoolean], 'OR');
 
 
-            val(hs1,l1,w);
-            val(hs2,l2,w);
-            if (l1<>0) or (l2<>0) then
-              hs1:='1'
-            else
-              hs1:='0';
+                val(hs2,l2,w);
+                if (l1<>0) or (l2<>0) then
+                  hs1:='1'
+                else
+                  hs1:='0';
+              end;
           until false;
           until false;
           read_simple_expr:=hs1;
           read_simple_expr:=hs1;
         end;
         end;
 
 
-        function read_expr(var exprType: TCTETypeSet) : string;
+        function read_expr(var exprType: TCTETypeSet; eval : Boolean) : string;
         var
         var
            hs1,hs2 : string;
            hs1,hs2 : string;
            b : boolean;
            b : boolean;
@@ -1050,7 +1085,7 @@ compile time variables as the old format (0/1), continue to work.
            l1,l2 : longint;
            l1,l2 : longint;
            exprType2: TCTETypeSet;
            exprType2: TCTETypeSet;
         begin
         begin
-           hs1:=read_simple_expr(exprType);
+           hs1:=read_simple_expr(exprType, eval);
            op:=current_scanner.preproc_token;
            op:=current_scanner.preproc_token;
            if (op = _ID) and (current_scanner.preproc_pattern = 'IN') then
            if (op = _ID) and (current_scanner.preproc_pattern = 'IN') then
              op := _IN;
              op := _IN;
@@ -1064,64 +1099,69 @@ compile time variables as the old format (0/1), continue to work.
              preproc_consume(_ID)
              preproc_consume(_ID)
            else
            else
              preproc_consume(op);
              preproc_consume(op);
-           hs2:=read_simple_expr(exprType2);
+           hs2:=read_simple_expr(exprType2, eval);
 
 
-           if op = _IN then
-             begin
-               if exprType2 <> [ctetSet] then
-                 CTEError(exprType2, [ctetSet], 'IN');
-               if exprType = [ctetSet] then
-                 CTEError(exprType, setElementTypes, 'IN');
-
-              if is_number(hs1) and is_number(hs2) then
-                Message(scan_e_preproc_syntax_error)
-              else if hs2[1] = ',' then
-                b:=pos(','+hs1+',', hs2) > 0   { TODO For integer sets, perhaps check for numeric equivalence so that 0 = 00 }
-              else
-                Message(scan_e_preproc_syntax_error);
-             end
-           else
+           if eval then
              begin
              begin
-               if (exprType * exprType2) = [] then
-                 CTEError(exprType2, exprType, tokeninfo^[op].str);
-
-               if is_number(hs1) and is_number(hs2) then
+               if op = _IN then
                  begin
                  begin
-                   val(hs1,l1,w);
-                   val(hs2,l2,w);
-                   case op of
-                     _EQUAL :
-                       b:=l1=l2;
-                     _UNEQUAL :
-                       b:=l1<>l2;
-                     _LT :
-                       b:=l1<l2;
-                     _GT :
-                       b:=l1>l2;
-                     _GTE :
-                       b:=l1>=l2;
-                     _LTE :
-                       b:=l1<=l2;
-                   end;
+                   if exprType2 <> [ctetSet] then
+                     CTEError(exprType2, [ctetSet], 'IN');
+                   if exprType = [ctetSet] then
+                     CTEError(exprType, setElementTypes, 'IN');
+    
+                  if is_number(hs1) and is_number(hs2) then
+                    Message(scan_e_preproc_syntax_error)
+                  else if hs2[1] = ',' then
+                    b:=pos(','+hs1+',', hs2) > 0   { TODO For integer sets, perhaps check for numeric equivalence so that 0 = 00 }
+                  else
+                    Message(scan_e_preproc_syntax_error);
                  end
                  end
                else
                else
                  begin
                  begin
-                   case op of
-                     _EQUAL :
-                       b:=hs1=hs2;
-                     _UNEQUAL :
-                       b:=hs1<>hs2;
-                     _LT :
-                       b:=hs1<hs2;
-                     _GT :
-                        b:=hs1>hs2;
-                     _GTE :
-                        b:=hs1>=hs2;
-                     _LTE :
-                       b:=hs1<=hs2;
-                   end;
+                   if (exprType * exprType2) = [] then
+                     CTEError(exprType2, exprType, tokeninfo^[op].str);
+    
+                   if is_number(hs1) and is_number(hs2) then
+                     begin
+                       val(hs1,l1,w);
+                       val(hs2,l2,w);
+                       case op of
+                         _EQUAL :
+                           b:=l1=l2;
+                         _UNEQUAL :
+                           b:=l1<>l2;
+                         _LT :
+                           b:=l1<l2;
+                         _GT :
+                           b:=l1>l2;
+                         _GTE :
+                           b:=l1>=l2;
+                         _LTE :
+                           b:=l1<=l2;
+                       end;
+                     end
+                   else
+                     begin
+                       case op of
+                         _EQUAL :
+                           b:=hs1=hs2;
+                         _UNEQUAL :
+                           b:=hs1<>hs2;
+                         _LT :
+                           b:=hs1<hs2;
+                         _GT :
+                            b:=hs1>hs2;
+                         _GTE :
+                            b:=hs1>=hs2;
+                         _LTE :
+                           b:=hs1<=hs2;
+                       end;
+                     end;
                  end;
                  end;
-             end;
+              end
+           else
+             b:= false; {Just to have something}
 
 
            if b then
            if b then
              read_expr:='1'
              read_expr:='1'
@@ -1129,11 +1169,12 @@ compile time variables as the old format (0/1), continue to work.
              read_expr:='0';
              read_expr:='0';
            exprType:= [ctetBoolean];
            exprType:= [ctetBoolean];
         end;
         end;
+
      begin
      begin
         current_scanner.skipspace;
         current_scanner.skipspace;
         { start preproc expression scanner }
         { start preproc expression scanner }
         current_scanner.preproc_token:=current_scanner.readpreproc;
         current_scanner.preproc_token:=current_scanner.readpreproc;
-        parse_compiler_expr:=read_expr(compileExprType);
+        parse_compiler_expr:=read_expr(compileExprType, true);
      end;
      end;
 
 
     function boolean_compile_time_expr(var valuedescr: String): Boolean;
     function boolean_compile_time_expr(var valuedescr: String): Boolean;