unit Expressions;

interface

procedure CompileExpression;

implementation

uses Common, Scaner, Identifiers;

procedure CompileExpression;
var
  Expression: string[32];

  procedure CompileIdentifier;
  var
    VarName: TIdentStr;
    VarType: TType;

  procedure CompileAssignment;
  var
    Operator: opAssign..opModAss;
    Accumulator: string[2];
  begin
    Operator := Lexem;
    case VarType of
      tChar: Accumulator := 'al';
      tInt, tWord: Accumulator := 'ax';
    else Error(ecWrongType) end;
    CompileExpression;
    if VarName = 'bx' then WriteLn(DestFile, #9'pop'#9'bx');
    case Operator of
      opAssign: WriteLn(DestFile, #9'mov'#9'[', VarName, '], ', Accumulator);
      opAddAss: WriteLn(DestFile, #9'add'#9'[', VarName, '], ', Accumulator);
      opDecAss: WriteLn(DestFile, #9'sub'#9'[', VarName, '], ', Accumulator);
      opMulAss:
        if VarType = tInt
        then WriteLn(DestFile, #9'imul'#9'[', VarName, ']'#13#10#9'mov'#9'[', VarName, '], ', Accumulator)
        else WriteLn(DestFile, #9'mul'#9'[', VarName, ']'#13#10#9'mov'#9'[', VarName, '], ', Accumulator);
      opDivAss, opModAss: begin
        WriteLn(DestFile, #9'mov'#9'bx, ax');
        WriteLn(DestFile, #9'mov'#9'ax, [', VarName, ']');
        WriteLn(DestFile, #9'cwd');
        if VarType = tInt
        then WriteLn(DestFile, #9'idiv'#9'bx')
        else WriteLn(DestFile, #9'div'#9'bx');
        if Operator = opModAss
        then WriteLn(DestFile, #9'mov'#9'ax, dx');
        WriteLn(DestFile, #9'mov'#9'[', VarName, '], ', Accumulator);
      end;
    end;
    if Operator in [opAddAss, opDecAss]
    then Expression := VarName
    else Expression := 'ax';
  end; { CompileAssignment }

  procedure CompileProcCall;
  var
    ProcName: TIdentStr;
    Params: Integer;
  begin
    ProcName := Identifier;
    Params := FindIdent(ProcName)^.Data;
    if ReadLexem = opLeftBracket then begin
      while ReadLexem <> opRightBracket do begin
        PredLexem;
        CompileExpression;
        WriteLn(DestFile, #9'push'#9'ax');
        Dec(Params);
      end;
    end else PredLexem;
    for Params := Params downto 1 do WriteLn(DestFile, #9'push'#9'0');
    WriteLn(DestFile, #9'call'#9, ProcName);
  end; { CompileProcCall }

  begin
    if FindIdent(Identifier) = nil then Error(ecUnknownIdent);
    VarType := CurrentIdent^.IdType;
    case CurrentIdent^.IdClass of
      cConst: WriteLn(DestFile, #9'mov'#9'ax, ', CurrentIdent^.Data);
      cProc: CompileProcCall;
      cVar, cArray:
      begin
        VarName := Identifier;

        if CurrentIdent^.IdClass = cArray then
        begin
          if ReadLexem <> opLeftParent then
          begin
            WriteLn(DestFile, #9'mov'#9, 'ax, OFFSET ', VarName);
            PredLexem;
            Exit;
          end
          else
          begin
            CompileExpression;
            if VarType = tInt then WriteLn(DestFile, #9'shl'#9'ax, 1');
            WriteLn(DestFile, #9'add'#9, 'ax, OFFSET ', VarName);
            WriteLn(DestFile, #9'mov'#9, 'bx, ax');
            VarName := 'bx';
            if ReadLexem <> opRightParent then Error(ecRPExpected);
          end;
        end;

        if ReadLexem = opArrow then
        begin
          if VarName <> 'bx' then
          begin
            WriteLn(DestFile, #9'mov'#9'bx, [', VarName, ']');
            VarName := 'bx';
          end
          else WriteLn(DestFile, #9'mov'#9'bx, [bx]');
          ReadLexem;
        end;

        if Lexem in [opAssign, opAddAss..opModAss] then
        begin
          if VarName = 'bx' then WriteLn(DestFile, #9'push'#9'bx');
          CompileAssignment;
          if Expression <> 'ax' then
          case VarType of
            tInt, tWord: WriteLn(DestFile, #9'mov'#9, 'ax, [', Expression, ']');
            tChar: WriteLn(DestFile, #9'movzx'#9, 'ax, [', Expression, ']');
          end;
        end
        else
        begin
          if VarType = tChar
          then WriteLn(DestFile, #9'movzx'#9'ax, byte ptr [', VarName, ']')
          else WriteLn(DestFile, #9'mov'#9'ax, [', VarName, ']');
          PredLexem;
        end;

      end;
    else Error(ecExpressionError)
    end;
  end; { CompileIdentifier }

  procedure Compile1st; { identifier constant () }
  const StrCount: Word = 0;
  begin
    case Lexem of
      ltIdentifier: CompileIdentifier;
      ltIntConst,
      ltCharConst: WriteLn(DestFile, #9'mov'#9'ax, ', IntConst);
      opLeftBracket:
      begin
        CompileExpression;
        if ReadLexem <> opRightBracket then Error(ecRBExpected);
      end;
      ltStrConst:
      begin
        Inc(StrCount);
        WriteLn(VarFile, '_Str', StrCount, #9'db'#9'"', StrConst, '"');
        WriteLn(DestFile, #9'mov'#9'ax, OFFSET _Str', StrCount);
      end;
      opAt:
      begin
        if ReadLexem <> ltIdentifier then Error(ecIdentExpected);
        if FindIdent(Identifier) = nil then Error(ecUnknownIdent);
        if not (CurrentIdent^.IdClass in [cVar, cProc]) then Error(ecExpressionError);
        WriteLn(DestFile, #9'mov'#9'ax, OFFSET ', Identifier);
      end;
    else Error(ecExpressionError) end;
    ReadLexem;
  end; { Compile1st }

  procedure Compile2nd; { + - }
  begin
    case ReadLexem of
      opPlus : Compile2nd;
      opMinus: begin
        Compile2nd;
        WriteLn(DestFile, #9'neg'#9'ax');
      end;
      opNot: begin
        Compile2nd;
        WriteLn(DestFile, #9'sub'#9'ax, 1');
        WriteLn(DestFile, #9'setc'#9'al');
        WriteLn(DestFile, #9'xor'#9'ah, ah');
      end;
      opArrow: begin
        Compile2nd;
        WriteLn(DestFile, #9'mov'#9'bx, ax');
        WriteLn(DestFile, #9'mov'#9'ax, [bx]');
      end;
    else  Compile1st;
    end;
  end; { Compile2nd }

  procedure Compile3rd; { * / % }
  var Operator: opMul..opMod;
  begin
    Compile2nd;
    while Lexem in [opMul..opMod] do begin
      Operator := Lexem;
      WriteLn(DestFile, #9'push'#9'ax');
      Compile2nd;
      WriteLn(DestFile, #9'pop'#9'bx');
      case Operator of
        opMul: WriteLn(DestFile, #9'imul'#9'bx');
        opDiv: WriteLn(DestFile, #9'xchg'#9'ax, bx'#13#10#9'cwd'#13#10#9'idiv'#9'bx');
        opMod: WriteLn(DestFile, #9'xchg'#9'ax, bx'#13#10#9'cwd'#13#10#9'idiv'#9'bx'#13#10#9'mov'#9'ax, dx');
      end;
    end;
  end; { Compile3rd }

  procedure Compile4rd; { + - }
  var
    Operator: opPlus..opMinus;
    OperandA, OperandB: string[32];
  begin
    Compile3rd;
    while Lexem in [opPlus..opMinus] do begin
      Operator := Lexem;
      WriteLn(DestFile, #9'push'#9'ax');
      Compile3rd;
      WriteLn(DestFile, #9'pop'#9'bx');
      case Operator of
        opPlus: WriteLn(DestFile, #9'add'#9'ax, bx');
        opMinus: WriteLn(DestFile, #9'xchg'#9'ax, bx'#13#10#9'sub'#9'ax, bx');
      end;
    end;
  end; { Compile4th }

  procedure Compile5th; { > < == <> >= <= }
  var Operator: opGreater..opLessEqual;
  begin
    Compile4rd;
    while Lexem in [opGreater..opLessEqual] do begin
      Operator := Lexem;
      WriteLn(DestFile, #9'push'#9'ax');
      Compile4rd;
      WriteLn(DestFile, #9'pop'#9'bx');
      case Operator of
        opGreater      : WriteLn(DestFile, #9'cmp'#9'ax, bx'#13#10#9'setc'#9'al');
        opLess         : WriteLn(DestFile, #9'cmp'#9'bx, ax'#13#10#9'setc'#9'al');
        opEqual        : WriteLn(DestFile, #9'cmp'#9'ax, bx'#13#10#9'setz'#9'al');
        opNotEqual     : WriteLn(DestFile, #9'cmp'#9'ax, bx'#13#10#9'setnz'#9'al');
        opGreaterEqual : WriteLn(DestFile, #9'cmp'#9'bx, ax'#13#10#9'setnc'#9'al');
        opLessEqual    : WriteLn(DestFile, #9'cmp'#9'ax, bx'#13#10#9'setnc'#9'al');
      end;
      WriteLn(DestFile, #9'xor'#9'ah, ah');
    end;
  end; { Compile5th }

  procedure Compile6th; { & | }
  var Operator: opAnd..opOr;
  begin
    Compile5th;
    while Lexem in [opAnd..opOr] do begin
      Operator := Lexem;
      WriteLn(DestFile, #9'push'#9'ax');
      Compile5th;
      WriteLn(DestFile, #9'pop'#9'bx');
      case Operator of
        opAnd: WriteLn(DestFile, #9'and'#9'ax, bx'#13#10#9'setnz'#9'al');
        opOr : WriteLn(DestFile, #9'or'#9'ax, bx'#13#10#9'setnz'#9'al');
      end;
      WriteLn(DestFile, #9'xor'#9'ah, ah');
    end;
  end; { Compile6th }

begin
  Compile6th;
  PredLexem;
end; { CompileExpression }

end.