{
    Copyright (c) 1998-2002 by Peter Vreman

    This unit implements the parsing of the switches like $I-

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

 ****************************************************************************
}
unit switches;

{$i fpcdefs.inc}

interface

uses
  systems,globtype;

procedure HandleSwitch(switch,state:char);
function CheckSwitch(switch,state:char):boolean;

procedure recordpendingverbosityswitch(sw: char; state: char);
procedure recordpendingmessagestate(msg: longint; state: tmsgstate);
procedure recordpendinglocalswitch(sw: tlocalswitch; state: char);
procedure recordpendinglocalfullswitch(const switches: tlocalswitches);
procedure recordpendingverbosityfullswitch(verbosity: longint);
procedure recordpendingcallingswitch(const str: shortstring);
procedure recordpendingalignmentfullswitch(const alignment : talignmentinfo);
procedure recordpendingsetalloc(alloc:shortint);
procedure recordpendingpackenum(size:shortint);
procedure recordpendingpackrecords(size:shortint);
procedure flushpendingswitchesstate;

implementation
uses
  cpuinfo,
{$ifdef llvm}
  { override optimizer switches }
  llvminfo,
{$endif llvm}
  globals,verbose,comphook,dirparse,
  fmodule;

{****************************************************************************
                          Main Switches Parsing
****************************************************************************}

type
  TSwitchType=(ignoredsw,localsw,modulesw,globalsw,illegalsw,unsupportedsw,alignsw,optimizersw,packenumsw,pentiumfdivsw,targetsw);
  SwitchRec=record
    typesw : TSwitchType;
    setsw  : byte;
  end;
  SwitchRecTable = array['A'..'Z'] of SwitchRec;

const
  turboSwitchTable: SwitchRecTable =(
   {A} (typesw:alignsw; setsw:ord(cs_localnone)),
   {B} (typesw:localsw; setsw:ord(cs_full_boolean_eval)),
   {C} (typesw:localsw; setsw:ord(cs_do_assertion)),
   {D} (typesw:modulesw; setsw:ord(cs_debuginfo)),
   {E} (typesw:modulesw; setsw:ord(cs_fp_emulation)),
{$ifdef i8086}
   {F} (typesw:localsw; setsw:ord(cs_force_far_calls)),
{$else i8086}
   {F} (typesw:ignoredsw; setsw:ord(cs_localnone)),
{$endif i8086}
   {G} (typesw:localsw; setsw:ord(cs_imported_data)),
   {H} (typesw:localsw; setsw:ord(cs_refcountedstrings)),
   {I} (typesw:localsw; setsw:ord(cs_check_io)),
   {J} (typesw:localsw; setsw:ord(cs_typed_const_writable)),
{$ifdef i8086}
   {K} (typesw:modulesw; setsw:ord(cs_win16_smartcallbacks)),
{$else i8086}
   {K} (typesw:unsupportedsw; setsw:ord(cs_localnone)),
{$endif i8086}
   {L} (typesw:unsupportedsw; setsw:ord(cs_localnone)),
   {M} (typesw:localsw; setsw:ord(cs_generate_rtti)),
   {N} (typesw:unsupportedsw; setsw:ord(cs_localnone)),
   {O} (typesw:optimizersw; setsw:ord(cs_opt_none)),
   {P} (typesw:modulesw; setsw:ord(cs_openstring)),
   {Q} (typesw:localsw; setsw:ord(cs_check_overflow)),
   {R} (typesw:localsw; setsw:ord(cs_check_range)),
   {S} (typesw:localsw; setsw:ord(cs_check_stack)),
   {T} (typesw:localsw; setsw:ord(cs_typed_addresses)),
   {U} (typesw:pentiumfdivsw; setsw:ord(cs_localnone)),
   {V} (typesw:localsw; setsw:ord(cs_strict_var_strings)),
{$ifdef i8086}
   {W} (typesw:targetsw; setsw:ord(ts_x86_far_procs_push_odd_bp)),
{$else i8086}
   {W} (typesw:localsw; setsw:ord(cs_generate_stackframes)),
{$endif i8086}
   {X} (typesw:modulesw; setsw:ord(cs_extsyntax)),
   {Y} (typesw:unsupportedsw; setsw:ord(cs_localnone)),
   {Z} (typesw:packenumsw; setsw:ord(cs_localnone))
    );


  macSwitchTable: SwitchRecTable =(
   {A} (typesw:unsupportedsw; setsw:ord(cs_localnone)),
   {B} (typesw:localsw; setsw:ord(cs_full_boolean_eval)),
   {C} (typesw:localsw; setsw:ord(cs_do_assertion)),
   {D} (typesw:modulesw; setsw:ord(cs_debuginfo)),
   {E} (typesw:modulesw; setsw:ord(cs_fp_emulation)),
{$ifdef i8086}
   {F} (typesw:localsw; setsw:ord(cs_force_far_calls)),
{$else i8086}
   {F} (typesw:ignoredsw; setsw:ord(cs_localnone)),
{$endif i8086}
   {G} (typesw:ignoredsw; setsw:ord(cs_localnone)),
   {H} (typesw:localsw; setsw:ord(cs_refcountedstrings)),
   {I} (typesw:localsw; setsw:ord(cs_check_io)),
   {J} (typesw:localsw; setsw:ord(cs_external_var)),
{$ifdef i8086}
   {K} (typesw:modulesw; setsw:ord(cs_win16_smartcallbacks)),
{$else i8086}
   {K} (typesw:unsupportedsw; setsw:ord(cs_localnone)),
{$endif i8086}
   {L} (typesw:unsupportedsw; setsw:ord(cs_localnone)),
   {M} (typesw:localsw; setsw:ord(cs_generate_rtti)),
   {N} (typesw:unsupportedsw; setsw:ord(cs_localnone)),
   {O} (typesw:optimizersw; setsw:ord(cs_opt_none)),
   {P} (typesw:modulesw; setsw:ord(cs_openstring)),
   {Q} (typesw:localsw; setsw:ord(cs_check_overflow)),
   {R} (typesw:localsw; setsw:ord(cs_check_range)),
   {S} (typesw:localsw; setsw:ord(cs_check_stack)),
   {T} (typesw:localsw; setsw:ord(cs_typed_addresses)),
   {U} (typesw:illegalsw; setsw:ord(cs_localnone)),
   {V} (typesw:localsw; setsw:ord(cs_strict_var_strings)),
{$ifdef i8086}
   {W} (typesw:targetsw; setsw:ord(ts_x86_far_procs_push_odd_bp)),
{$else i8086}
   {W} (typesw:localsw; setsw:ord(cs_generate_stackframes)),
{$endif i8086}
   {X} (typesw:modulesw; setsw:ord(cs_extsyntax)),
   {Y} (typesw:unsupportedsw; setsw:ord(cs_localnone)),
   {Z} (typesw:localsw; setsw:ord(cs_externally_visible))
    );

procedure HandleSwitch(switch,state:char);

var
  switchTablePtr: ^SwitchRecTable;

begin
  switch:=upcase(switch);
{ Is the Switch in the letters ? }
  if not ((switch in ['A'..'Z']) and (state in ['-','+'])) then
   begin
     Message(scan_w_illegal_switch);
     exit;
   end;

{ Select switch table }
  if m_mac in current_settings.modeswitches  then
    switchTablePtr:= @macSwitchTable
  else
    switchTablePtr:= @turboSwitchTable;

{ Handle the switch }
   with switchTablePtr^[switch] do
   begin
     case typesw of
       alignsw:
         if state='+' then
           current_settings.packrecords:=4
         else
           current_settings.packrecords:=1;
       optimizersw :
         begin
           if state='+' then
             current_settings.optimizerswitches:=level2optimizerswitches
           else
             current_settings.optimizerswitches:=[];
         end;
       ignoredsw :
         Message1(scan_n_ignored_switch,'$'+switch);
       illegalsw :
         Message1(scan_w_illegal_switch,'$'+switch);
       unsupportedsw :
         Message1(scan_w_unsupported_switch,'$'+switch);
       localsw :
         recordpendinglocalswitch(tlocalswitch(setsw),state);
       modulesw :
         begin
           if current_module.in_global then
            begin
{$ifndef cpufpemu}
              if tmoduleswitch(setsw)=cs_fp_emulation then
                begin
                  Message1(scan_w_unsupported_switch_by_target,'$'+switch);
                end
              else
{$endif cpufpemu}
                begin
                  if state='+' then
                    include(current_settings.moduleswitches,tmoduleswitch(setsw))
                  else
                    begin
                      { Turning off debuginfo when lineinfo is requested
                        is not possible }
                      if not((cs_use_lineinfo in current_settings.globalswitches) and
                             (tmoduleswitch(setsw)=cs_debuginfo)) then
                        exclude(current_settings.moduleswitches,tmoduleswitch(setsw));
                    end;
                end;
            end
           else
            Message(scan_w_switch_is_global);
         end;
       globalsw :
         begin
           if current_module.in_global and (current_module=main_module) then
            begin
              if state='+' then
               include(current_settings.globalswitches,tglobalswitch(setsw))
              else
               exclude(current_settings.globalswitches,tglobalswitch(setsw));
            end
           else
            Message(scan_w_switch_is_global);
         end;
       packenumsw:
         begin
           if state='-' then
             current_settings.packenum:=1
           else
             current_settings.packenum:=4;
         end;
       pentiumfdivsw:
         begin
           { Switch u- means pentium-safe fdiv off -> fpc default. We don't }
           { support u+                                                     }
           if state='+' then
             Message1(scan_w_unsupported_switch,'$'+switch);
         end;
       targetsw:
         UpdateTargetSwitchStr(TargetSwitchStr[ttargetswitch(setsw)].name+state,current_settings.targetswitches,current_module.in_global);
     end;
   end;
end;


function CheckSwitch(switch,state:char):boolean;

var
  found : boolean;
  switchTablePtr: ^SwitchRecTable;

begin
  switch:=upcase(switch);
{ Is the Switch in the letters ? }
  if not ((switch in ['A'..'Z']) and (state in ['-','+'])) then
   begin
     Message(scan_w_illegal_switch);
     CheckSwitch:=false;
     exit;
   end;

{ Select switch table }
  if m_mac in current_settings.modeswitches then
    switchTablePtr:= @macSwitchTable
  else
    switchTablePtr:= @turboSwitchTable;

{ Check the switch }
   with switchTablePtr^[switch] do
   begin
     case typesw of
      localsw : found:=(tlocalswitch(setsw) in current_settings.localswitches);
     modulesw : found:=(tmoduleswitch(setsw) in current_settings.moduleswitches);
     globalsw : found:=(tglobalswitch(setsw) in current_settings.globalswitches);
     packenumsw : found := (current_settings.packenum = 4);
     else
      found:=false;
     end;
     if state='-' then
      found:=not found;
     CheckSwitch:=found;
   end;
end;


procedure recordpendingverbosityswitch(sw: char; state: char);
  begin
    pendingstate.nextverbositystr:=pendingstate.nextverbositystr+sw+state;
  end;


procedure recordpendingmessagestate(msg: longint; state: tmsgstate);
  var
    pstate : pmessagestaterecord;
  begin
    new(pstate);
    pstate^.next:=pendingstate.nextmessagerecord;
    pstate^.value:=msg;
    pstate^.state:=state;
    pendingstate.nextmessagerecord:=pstate;
  end;


procedure recordpendinglocalswitch(sw: tlocalswitch; state: char);
  begin
    if not (psf_local_switches_changed in pendingstate.flags) then
       pendingstate.nextlocalswitches:=current_settings.localswitches;
    if state='-' then
      exclude(pendingstate.nextlocalswitches,sw)
    else if state='+' then
      include(pendingstate.nextlocalswitches,sw)
    else { state = '*' }
      begin
        if sw in init_settings.localswitches then
         include(pendingstate.nextlocalswitches,sw)
        else
         exclude(pendingstate.nextlocalswitches,sw);
      end;
    include(pendingstate.flags,psf_local_switches_changed);
  end;


procedure recordpendingalignmentfullswitch(const alignment : talignmentinfo);
  begin
    pendingstate.nextalignment:=alignment;
    include(pendingstate.flags,psf_alignment_changed);
  end;


procedure recordpendinglocalfullswitch(const switches: tlocalswitches);
  begin
    pendingstate.nextlocalswitches:=switches;
    include(pendingstate.flags,psf_local_switches_changed);
  end;


procedure recordpendingverbosityfullswitch(verbosity: longint);
  begin
    pendingstate.nextverbositystr:='';
    pendingstate.nextverbosityfullswitch:=verbosity;
    include(pendingstate.flags,psf_verbosity_full_switched);
  end;

procedure recordpendingcallingswitch(const str: shortstring);
  begin
    pendingstate.nextcallingstr:=str;
  end;


procedure recordpendingsetalloc(alloc:shortint);
  begin
    pendingstate.nextsetalloc:=alloc;
    include(pendingstate.flags,psf_setalloc_changed);
  end;


procedure recordpendingpackenum(size:shortint);
  begin
    pendingstate.nextpackenum:=size;
    include(pendingstate.flags,psf_packenum_changed);
  end;


procedure recordpendingpackrecords(size:shortint);
  begin
    pendingstate.nextpackrecords:=size;
    include(pendingstate.flags,psf_packrecords_changed);
  end;


procedure flushpendingswitchesstate;
  var
    tmpproccal: tproccalloption;
    fstate, pstate : pmessagestaterecord;
  begin
    { process pending localswitches (range checking, etc) }
    if psf_local_switches_changed in pendingstate.flags then
      begin
        current_settings.localswitches:=pendingstate.nextlocalswitches;
        exclude(pendingstate.flags,psf_local_switches_changed);
      end;
    { process pending verbosity changes (warnings on, etc) }
    if psf_verbosity_full_switched in pendingstate.flags then
      begin
        status.verbosity:=pendingstate.nextverbosityfullswitch;
        exclude(pendingstate.flags,psf_verbosity_full_switched);
      end;
    if psf_alignment_changed in pendingstate.flags then
      begin
        current_settings.alignment:=pendingstate.nextalignment;
        exclude(pendingstate.flags,psf_alignment_changed);
      end;
    if psf_packenum_changed in pendingstate.flags then
      begin
        current_settings.packenum:=pendingstate.nextpackenum;
        exclude(pendingstate.flags,psf_packenum_changed);
      end;
    if psf_packrecords_changed in pendingstate.flags then
      begin
        current_settings.packrecords:=pendingstate.nextpackrecords;
        exclude(pendingstate.flags,psf_packrecords_changed);
      end;
    if psf_setalloc_changed in pendingstate.flags then
      begin
        current_settings.setalloc:=pendingstate.nextsetalloc;
        exclude(pendingstate.flags,psf_setalloc_changed);
      end;
    { process pending verbosity changes (warnings on, etc) }
    if pendingstate.nextverbositystr<>'' then
      begin
        setverbosity(pendingstate.nextverbositystr);
        pendingstate.nextverbositystr:='';
      end;
    fstate:=pendingstate.nextmessagerecord;
    pstate:=pendingstate.nextmessagerecord;
    while assigned(pstate) do
      begin
        pendingstate.nextmessagerecord:=pstate^.next;
        SetMessageVerbosity(pstate^.value,pstate^.state);
        if not assigned(pstate^.next) then
          begin
            pstate^.next:=current_settings.pmessage;
            current_settings.pmessage:=fstate;
            pstate:=nil;
          end
        else
          pstate:=pstate^.next;
        pendingstate.nextmessagerecord:=nil;
      end;
    { process pending calling convention changes (calling x) }
    if pendingstate.nextcallingstr<>'' then
      begin
        if not SetAktProcCall(pendingstate.nextcallingstr,tmpproccal) then
          Message1(parser_w_unknown_proc_directive_ignored,pendingstate.nextcallingstr)
        else if not(tmpproccal in supported_calling_conventions) then
          Message1(parser_e_illegal_calling_convention,pendingstate.nextcallingstr)
        else
          current_settings.defproccall:=tmpproccal;
        pendingstate.nextcallingstr:='';
      end;
  end;


end.
