unit Statements;

interface

procedure CompileStatement;

implementation

uses Common, Scaner, Identifiers, Expressions;

const
  CycleCount: Word = 0;
  IfCount: Word = 0;
  CycleEnclosement: Byte = 0;

procedure CompileAsm;
begin
  AllowCRLF := True;
  repeat
    ReadLexem;
    case Lexem of
      ltIdentifier: Write(DestFile, Identifier, ' ');
      ltIntConst: Write(DestFile, IntConst, ' ');
      ltCharConst: Write(DestFile, '''', CharConst, ''' ');
      ltStrConst: Write(DestFile, '''', StrConst, ''' ');
      ltCRLF: Write(DestFile, #13#10#9);
      rwEnd: Break;
      opAssign..rwRet: Write(DestFile, Identifier, ' ');
    else Error(ecSyntaxError);
    end;
  until False;
  WriteLn(DestFile);
  AllowCRLF := False;
end; { CompileAsm }

procedure CompileRet;
begin
  if ReadLexem = opLeftBracket then begin
    CompileExpression;
    if ReadLexem <> opRightBracket then Error(ecRBExpected);
  end else PredLexem;
  WriteLn(DestFile, #9'jmp'#9'_Exit');
end; { CompileRet }

procedure CompileIf;
var
  Index, EndIfIndex: Word;
begin
  Inc(IfCount); Index := IfCount; EndIfIndex := IfCount;
  CompileExpression;
  WriteLn(DestFile, #9'cmp'#9'ax, 0');
  WriteLn(DestFile, #9'jz'#9'_Else', Index);
  while not (ReadLexem in [rwEnd, rwElse]) do begin
    if Lexem = rwElsIf then begin
      WriteLn(DestFile, #9'jmp'#9'_EndIf', EndIfIndex);
      Write(DestFile, '_Else', Index, ':');
      Inc(IfCount); Index := IfCount;
      CompileExpression;
      WriteLn(DestFile, #9'cmp'#9'ax, 0');
      WriteLn(DestFile, #9'jz'#9'_Else', Index);
    end else CompileStatement;
  end;
  if Lexem = rwElse then begin
    Write(DestFile, #9'jmp'#9'_EndIf', EndIfIndex, #13#10'_Else', Index, ':');
    ReadLexem;
    repeat
      CompileStatement;
      ReadLexem;
    until Lexem = rwEnd;
  end else WriteLn(DestFile, '_Else', Index, ':');
  WriteLn(DestFile, '_EndIf', EndIfIndex, ':');
end; { CompileIf }

procedure CompileWhile;
var Index: Word;
begin
  Inc(CycleCount); Index := CycleCount;
  WriteLn(DestFile, '_Next', Index, ':');
  CompileExpression;
  WriteLn(DestFile, #9'cmp'#9'ax, 0');
  WriteLn(DestFile, #9'jz'#9'_WEnd', Index);
  while ReadLexem <> rwEnd do
    case Lexem of
      rwNext: WriteLn(DestFile, #9'jmp'#9'_Next', CycleCount);
      rwBreak: WriteLn(DestFile, #9'jmp'#9'_WEnd', CycleCount);
    else CompileStatement;
    end;
  WriteLn(DestFile, #9'jmp'#9'_Next', Index);
  WriteLn(DestFile, '_WEnd', Index, ':');
end; { CompileWhile }

procedure CompileFor;
var
  Index: Word;
  CtrlVar: TIdentStr;
  Delta: Integer;
  VarType: TType;
  IsStep: Boolean;
begin
  Inc(CycleCount); Index := CycleCount;
  if ReadLexem <> ltIdentifier then Error(ecIdentExpected);
  if FindIdent(Identifier) = nil then Error(ecUnknownIdent);
  if CurrentIdent^.IdClass <> cVar then Error(ecVarExpected);
  CtrlVar := Identifier;
  VarType := CurrentIdent^.IdType;
  if not (VarType in [tInt, tChar]) then Error(ecWrongType);
  if ReadLexem = opAssign then begin
    CompileExpression;
    if VarType = tChar
    then WriteLn(DestFile, #9'mov'#9'[', CtrlVar, '], al')
    else WriteLn(DestFile, #9'mov'#9'[', CtrlVar, '], ax');
    ReadLexem;
  end;
  if Lexem <> rwTo then Error(ecToExpected);
  CompileExpression;
  if ReadLexem = rwStep then begin
    WriteLn(DestFile, #9'push'#9'ax');
    CompileExpression;
    WriteLn(DestFile, #9'mov'#9'bx, ax');
    IsStep := True;
  end else begin
    IsStep := False;
    PredLexem;
  end;
  if IsStep then WriteLn(DestFile, #9'pop'#9'ax');
  WriteLn(DestFile, '_For', Index, ':');
  if VarType = tChar
  then WriteLn(DestFile, #9'cmp'#9'al, [', CtrlVar, ']')
  else WriteLn(DestFile, #9'cmp'#9'ax, [', CtrlVar, ']');
  WriteLn(DestFile, #9'jc'#9'_EndFor', Index);
  WriteLn(DestFile, #9'push'#9'ax');
  if IsStep then WriteLn(DestFile, #9'push'#9'bx');
  while ReadLexem <> rwEnd do
    case Lexem of
      rwNext: WriteLn(DestFile, #9'jmp'#9'_Next', CycleCount);
      rwBreak:
        begin
          if IsStep
          then WriteLn(DestFile, #9'add'#9'sp, 4')
          else WriteLn(DestFile, #9'add'#9'sp, 2');
          WriteLn(DestFile, #9'jmp'#9'_EndFor', CycleCount);
        end;
    else CompileStatement;
  end;
  Write(DestFile, '_Next', Index, ':');
  if IsStep then begin
    WriteLn(DestFile, #9'pop'#9'bx');
    if VarType = tChar
    then WriteLn(DestFile, #9'add'#9'[', CtrlVar, '], bl')
    else WriteLn(DestFile, #9'add'#9'[', CtrlVar, '], bx');
  end else WriteLn(DestFile, #9'inc'#9'[', CtrlVar, ']');
  WriteLn(DestFile, #9'pop'#9'ax');
  WriteLn(DestFile, #9'jmp'#9'_For', Index);
  WriteLn(DestFile, '_EndFor', Index, ':');
end; { CompileFor }

procedure CompileStatement;
begin
  WriteLn(DestFile, #9#9#9#9'; Line ', Line, ', Column ', Column);
  case Lexem of
    ltIdentifier: begin
      PredLexem;
      CompileExpression;
    end;
    ltEmpty: Error(ecEndOfFile);
    rwRet  : CompileRet;
    rwAsm  : CompileAsm;
    rwIf   : CompileIf;
    rwWhile: CompileWhile;
    rwFor  : CompileFor;
  else Error(ecSyntaxError) end;
end; { CompileStatement }

end.