mainwindow.pas

Переключить прокрутку окна
Загрузить этот исходный код

{
    Этот исходный текст является частью Продвинутого векторного транслятора.

    Следующие файлы используются этим исходным текстом:
        mainwindow.lfm
    На них так же распространяются те же права, как и на этот исходный текст.

    Copyright © 2017 Малик Разработчик

    Это свободная программа: вы можете перераспространять её и/или
    изменять её на условиях Стандартной общественной лицензии GNU в том виде,
    в каком она была опубликована Фондом свободного программного обеспечения;
    либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

    Эта программа распространяется в надежде, что она может быть полезна,
    но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
    или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЁННЫХ ЦЕЛЕЙ. Подробнее см. в Стандартной
    общественной лицензии GNU.

    Вы должны были получить копию Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <http://www.gnu.org/licenses/>.
}

unit MainWindow;

{$MODE DELPHI,EXTENDEDSYNTAX ON}

interface

uses
    Windows,
    Classes, SysUtils, Forms, Graphics, Dialogs, Controls, StdCtrls, ExtCtrls,
    Menus, InterfaceBase, LCLIntf, LMessages,

    Lang, IOStream, FileIO, SettingFiles,
    TranIntf, TranType, BuildLex, BuildNms, BuildFTr,
    GenX86, Gen16bit, Gen32bit, Gen64bit, AsmGens;

{$ASMMODE INTEL,CALLING REGISTER,INLINE ON,GOTO ON}
{$H+,I-,J-,M-,Q-,R-,T-}

const
    LM_COMPILE_BEGIN = LM_USER + 1;
    LM_COMPILE_END = LM_USER + 2;
    LM_COMPILE_ERROR = LM_USER + 3;

type
    GenerationInfo = interface;
    TMainForm = class;

    GenerationInfo_Array1d = packed array of GenerationInfo;

    GenerationInfo = interface(_Interface) ['{A2619CC7-DF2E-4BFD-8017-0CF91E443ADA}']
        function getExecutableName(): String;
        function getGeneratorClass(): TextGenerator_Class;
        property executableName: String read getExecutableName;
        property generatorClass: TextGenerator_Class read getGeneratorClass;
    end;

    TMainForm = class(TForm, _Interface, Runnable)
        targetProcessor: TGroupBox;
        targetProcessorBitsLabel: TLabel;
        targetProcessorBits: TComboBox;
        targetProcessorInstructionSetLabel: TLabel;
        targetProcessorInstructionSet: TComboBox;
        source: TGroupBox;
        sourceFileList: TListBox;
        sourceAdd: TButton;
        sourceExpell: TButton;
        sourceMoveUp: TButton;
        sourceMoveDown: TButton;
        platform: TGroupBox;
        platformExecutableTypeLabel: TLabel;
        platformExecutableType: TComboBox;
        platformExitInstructionLabel: TLabel;
        platformExitInstruction: TComboBox;
        platformHeapSizeLabel: TLabel;
        platformHeapSize: TEdit;
        platformStackSizeLabel: TLabel;
        platformStackSize: TEdit;
        destination: TGroupBox;
        destinationFileName: TEdit;
        destinationSelect: TButton;
        fasm: TGroupBox;
        fasmAlsoBuild: TCheckBox;
        fasmExePathLabel: TLabel;
        fasmExePath: TEdit;
        fasmExePathSearch: TButton;
        fasmDestinationLabel: TLabel;
        fasmDestination: TEdit;
        fasmDestinationSelect: TButton;
        buttonsPanel: TPanel;
        loadProject: TButton;
        saveProject: TButton;
        buildProject: TButton;
        aboutAVT: TButton;
        closeAVT: TButton;
        sourceAddDialog: TOpenDialog;
        destinationSelectDialog: TSaveDialog;
        fasmExePathSearchDialog: TOpenDialog;
        fasmDestinationSelectDialog: TSaveDialog;
        loadProjectDialog: TOpenDialog;
        saveProjectDialog: TSaveDialog;
        procedure formDropFiles(sender: TObject; const fileNames: array of String);
        procedure targetProcessorBitsChange(sender: TObject);
        procedure sourceFileListSelectionChange(sender: TObject; user: boolean);
        procedure sourceAddClick(sender: TObject);
        procedure sourceExpellClick(sender: TObject);
        procedure sourceMoveUpClick(sender: TObject);
        procedure sourceMoveDownClick(sender: TObject);
        procedure platformExecutableTypeChange(sender: TObject);
        procedure destinationSelectClick(sender: TObject);
        procedure fasmAlsoBuildClick(sender: TObject);
        procedure fasmExePathSearchClick(sender: TObject);
        procedure fasmDestinationSelectClick(sender: TObject);
        procedure loadProjectClick(sender: TObject);
        procedure saveProjectClick(sender: TObject);
        procedure buildProjectClick(sender: TObject);
        procedure aboutAVTClick(sender: TObject);
        procedure closeAVTClick(sender: TObject);
    private
        translatorUseFasm: boolean;
        translatorBits: int;
        translatorInstructionSet: int;
        translatorHeapSize: int;
        translatorStackSize: int;
        translatorExitInstruction: int;
        translatorExecutableType: TextGenerator_Class;
        translatorSources: String_Array1d;
        translatorDestination: String;
        translatorFasmPath: String;
        translatorExecutable: String;
        procedure runFasm();
    private
        errorLine: int;
        errorChar: int;
        errorInfo: String;
        errorSource: String;
    private
        openedDialog: TForm;
        generators16bit: GenerationInfo_Array1d;
        generators32bit: GenerationInfo_Array1d;
        generators64bit: GenerationInfo_Array1d;
        generatorsCurrent: GenerationInfo_Array1d;
        procedure sourceButtonsSetEnabledState();
        procedure saveProjectToStream(stream: Output);
        procedure loadProjectFromStream(stream: Input);
    protected
        procedure wmCompileBegin(var msg: TLMessage); message LM_COMPILE_BEGIN;
        procedure wmCompileEnd(var msg: TLMessage); message LM_COMPILE_END;
        procedure wmCompileError(var msg: TLMessage); message LM_COMPILE_ERROR;
    protected
        function loadSourceFromStream(stream: Input): UnicodeString; virtual;
    public
        constructor create(theOwner: TComponent); override;
        procedure afterConstruction(); override;
        procedure run(); virtual;
        function openDialog(dialog: TForm): int; virtual; overload;
        function openDialog(const caption, text: String; dialogType: TMsgDlgType;
                buttons: TMsgDlgButtons): int; virtual; overload;

    private
        const SECTION_PROCESSOR = 'processor';
        const SECTION_SOURCES = 'sources';
        const SECTION_PLATFORM = 'platform';
        const SECTION_DESTINATION = 'destination';
        const SECTION_FASM = 'fasm';
        const IDENT_BITS = 'bits';
        const IDENT_INSTRUCTION_SET = 'instruction set';
        const IDENT_FILE = 'file ';
        const IDENT_EXE_TYPE_INDEX = 'exe type index';
        const IDENT_EXIT_INSTRUCTION = 'exit instruction';
        const IDENT_HEAP_SIZE = 'heap size';
        const IDENT_STACK_SIZE = 'stack size';
        const IDENT_FILE_NAME = 'file name';
        const IDENT_USE = 'use';
        const IDENT_PATH = 'path';
        const VALUE_SSE3 = 'SSE3';
        const VALUE_SSSE3 = 'SSSE3';
        const VALUE_SSE4_1 = 'SSE4.1';
        const VALUE_AVX_512 = 'AVX-512';
        const VALUE_RET = 'ret';
        const VALUE_RETF = 'retf';
        const VALUE_CALL = 'call';
        class var INSTANCE: TMainForm;
    public
        class procedure createInstance();
    end;

resourcestring
    msgError = 'Ошибка';
    msgWait = 'Подождите…';
    msgCompile = 'Компилировать';
    msgSource = 'Исходный код: ';
    msgLine = 'Строка: ';
    msgChar = 'Символ: ';
    msgErrorLoadingSource = 'Ошибка возникла при загрузке исходного кода.'#13#10 +
            'Проверьте выбранный путь и имя файла.'#13#10 +
            'Файл исходного кода, возбудивший ошибку: ';
    msgErrorSavingDestination = 'Ошибка возникла при сохранении целевого файла исходного кода ' +
            'на языке flat assembler.'#13#10'Проверьте выбранный путь и имя файла.';
    msgErrorLoadingProject = 'Ошибка возникла при загрузке проекта. '#13#10 +
            'Проверьте выбранный путь и имя файла.';
    msgErrorSavingProject = 'Ошибка возникла при сохранении проекта. '#13#10 +
            'Проверьте выбранный путь и имя файла.';
    msgBinaryFile = 'Двоичный файл';
    msg16bitDOS_COM = 'DOS: COM-программа';
    msg32bitGNULinux_ELF = 'GNU/Linux: ELF-программа';
    msg32bitWindows_GUI = 'Windows: графическая программа';
    msg32bitWindows_Console = 'Windows: консольная программа';
    msg32bitKolibriOS_MENUET01 = 'KolibriOS: MENUET01-программа';
    msg64bitGNULinux_ELF64 = 'GNU/Linux: ELF64-программа';
    msg64bitWindows_GUI = 'Windows: графическая программа';
    msg64bitWindows_Console = 'Windows: консольная программа';
    msgOpenDialogFormIsNull = 'openDialog: dialog = nil.';
    msgOpenDialogAlreadyOpened = 'openDialog: у этой формы уже открыто диалоговое окно.';
    msgOpenDialogDisabled = 'openDialog: эта форма недоступна.';
    msgOpenDialogFormAlreadyOpened = 'openDialog: запрашиваемое диалоговое окно уже открыто.';
    msgOpenDialogFormDisabled = 'openDialog: запрашиваемое диалоговое окно недоступно.';
    msgOpenDialogFormMDIChild = 'openDialog: запрашиваемое окно не может быть диалоговым.';
    msgAbout = 'О программе';
    msgAboutContent = 'Продвинутый векторный транслятор'#13#10 +
            'Версия: 0.3.2 бета'#13#10#13#10 +
            'Страница программы в Интернете:'#13#10 +
            'https://malik-elaborarer.ru/avt_ru/'#13#10#13#10 +
            'Copyright © 2017 Малик Разработчик'#13#10#13#10 +
            'Лицензия GPL/LGPL.'#13#10 +
            'Обратитесь к исходному коду за подробностями.';

implementation

{$R *.LFM}

type
    CodeGenerationInfo = class(RefCountInterfacedObject, GenerationInfo)
    public
        constructor create(const executableName: String; generatorClass: TextGenerator_Class);
        function getExecutableName(): String; virtual;
        function getGeneratorClass(): TextGenerator_Class; virtual;
    strict private
        executableName: String;
        generatorClass: TextGenerator_Class;
    end;

{ TMainForm }

procedure TMainForm.formDropFiles(sender: TObject; const fileNames: array of String);
var
    i: int;
    lim: int;
begin
    with sourceFileList.items do begin
        beginUpdate();
        try
            lim := length(fileNames) - 1;
            for i := 0 to lim do begin
                add(fileNames[i]);
            end;
        finally
            endUpdate();
        end;
    end;
    sourceButtonsSetEnabledState();
end;

procedure TMainForm.targetProcessorBitsChange(sender: TObject);
var
    i: int;
    lim: int;
    generators: GenerationInfo_Array1d;
begin
    generators := nil;
    case targetProcessorBits.itemIndex of
     0: generators := generators16bit;
     1: generators := generators32bit;
     2: generators := generators64bit;
    end;
    generatorsCurrent := generators;
    with platformExecutableType, items do begin
        beginUpdate();
        try
            clear();
            lim := length(generators) - 1;
            for i := 0 to lim do begin
                add(generators[i].executableName);
            end;
        finally
            endUpdate();
        end;
        itemIndex := 0;
        platformExecutableTypeChange(platformExecutableType);
    end;
end;

procedure TMainForm.sourceFileListSelectionChange(sender: TObject; user: boolean);
begin
    sourceButtonsSetEnabledState();
end;

procedure TMainForm.sourceAddClick(sender: TObject);
var
    i: int;
    lim: int;
    fileNames: TStrings;
begin
    with sourceAddDialog do begin
        if not execute() then begin
            exit;
        end;
        fileNames := files;
    end;
    with sourceFileList.items do begin
        beginUpdate();
        try
            lim := fileNames.count - 1;
            for i := 0 to lim do begin
                add(fileNames.strings[i]);
            end;
        finally
            endUpdate();
        end;
    end;
    sourceButtonsSetEnabledState();
end;

procedure TMainForm.sourceExpellClick(sender: TObject);
var
    i: int;
begin
    with sourceFileList, items do begin
        beginUpdate();
        try
            for i := count - 1 downto 0 do begin
                if selected[i] then begin
                    delete(i);
                end;
            end;
        finally
            endUpdate();
        end;
    end;
    sourceButtonsSetEnabledState();
end;

procedure TMainForm.sourceMoveUpClick(sender: TObject);
var
    currIsSelected: boolean;
    prevNonSelectedIndex: int;
    i: int;
    len: int;
begin
    with sourceFileList, items do begin
        beginUpdate();
        try
            len := count;
            prevNonSelectedIndex := 0;
            for i := 0 to len do begin
                if i < len then begin
                    currIsSelected := selected[i];
                end else begin
                    currIsSelected := false;
                end;
                if currIsSelected = false then begin
                    if i <> prevNonSelectedIndex then begin
                        move(prevNonSelectedIndex, i - 1);
                    end;
                    prevNonSelectedIndex := i;
                end;
            end;
        finally
            endUpdate();
        end;
    end;
    sourceButtonsSetEnabledState();
end;

procedure TMainForm.sourceMoveDownClick(sender: TObject);
var
    currIsSelected: boolean;
    prevNonSelectedIndex: int;
    i: int;
    lim: int;
begin
    with sourceFileList, items do begin
        beginUpdate();
        try
            lim := count - 1;
            prevNonSelectedIndex := lim;
            for i := lim downto -1 do begin
                if i >= 0 then begin
                    currIsSelected := selected[i];
                end else begin
                    currIsSelected := false;
                end;
                if currIsSelected = false then begin
                    if i <> prevNonSelectedIndex then begin
                        move(prevNonSelectedIndex, i + 1);
                    end;
                    prevNonSelectedIndex := i;
                end;
            end;
        finally
            endUpdate();
        end;
    end;
    sourceButtonsSetEnabledState();
end;

procedure TMainForm.platformExecutableTypeChange(sender: TObject);
var
    generatorClass: TextGenerator_Class;
begin
    generatorClass := generatorsCurrent[platformExecutableType.itemIndex].generatorClass;
    if generatorClass.isNeedHeapSize() then begin
        platformHeapSizeLabel.enabled := true;
        with platformHeapSize do begin
            enabled := true;
            color := clWindow;
        end;
    end else begin
        platformHeapSizeLabel.enabled := false;
        with platformHeapSize do begin
            enabled := false;
            color := clBtnFace;
        end;
    end;
    if generatorClass.isNeedStackSize() then begin
        platformStackSizeLabel.enabled := true;
        with platformStackSize do begin
            enabled := true;
            color := clWindow;
        end;
    end else begin
        platformStackSizeLabel.enabled := false;
        with platformStackSize do begin
            enabled := false;
            color := clBtnFace;
        end;
    end;
    if generatorClass.isNeedExitMethod() then begin
        platformExitInstructionLabel.enabled := true;
        with platformExitInstruction do begin
            enabled := true;
            color := clWindow;
        end;
    end else begin
        platformExitInstructionLabel.enabled := false;
        with platformExitInstruction do begin
            enabled := false;
            color := clBtnFace;
        end;
    end;
end;

procedure TMainForm.destinationSelectClick(sender: TObject);
begin
    with destinationSelectDialog do begin
        fileName := destinationFileName.text;
        if execute() then begin
            destinationFileName.text := fileName;
        end;
    end;
end;

procedure TMainForm.fasmAlsoBuildClick(sender: TObject);
begin
    if fasmAlsoBuild.checked then begin
        fasmExePathLabel.enabled := true;
        with fasmExePath do begin
            enabled := true;
            color := clWindow;
        end;
        fasmExePathSearch.enabled := true;
        fasmDestinationLabel.enabled := true;
        with fasmDestination do begin
            enabled := true;
            color := clWindow;
        end;
        fasmDestinationSelect.enabled := true;
    end else begin
        fasmExePathLabel.enabled := false;
        with fasmExePath do begin
            enabled := false;
            color := clBtnFace;
        end;
        fasmExePathSearch.enabled := false;
        fasmDestinationLabel.enabled := false;
        with fasmDestination do begin
            enabled := false;
            color := clBtnFace;
        end;
        fasmDestinationSelect.enabled := false;
    end;
end;

procedure TMainForm.fasmExePathSearchClick(sender: TObject);
begin
    with fasmExePathSearchDialog do begin
        fileName := fasmExePath.text;
        if execute() then begin
            fasmExePath.text := fileName;
        end;
    end;
end;

procedure TMainForm.fasmDestinationSelectClick(sender: TObject);
begin
    with fasmDestinationSelectDialog do begin
        fileName := fasmDestination.text;
        if execute() then begin
            fasmDestination.text := fileName;
        end;
    end;
end;

procedure TMainForm.loadProjectClick(sender: TObject);
var
    fileAsStream: Input;
    fileAsObject: FileInputStream;
    projectFileName: String;
begin
    with loadProjectDialog do begin
        if execute() = false then begin
            exit;
        end;
        projectFileName := fileName;
    end;
    fileAsObject := FileInputStream.create(stringToUTF16(projectFileName));
    if fileAsObject.hasOpenError() then begin
        fileAsObject.free();
        openDialog(msgError, msgErrorLoadingProject, mtError, [mbClose]);
        exit;
    end;
    fileAsStream := fileAsObject;
    try
        loadProjectFromStream(fileAsStream);
    finally
        fileAsStream.close();
    end;
end;

procedure TMainForm.saveProjectClick(sender: TObject);
var
    fileAsStream: Output;
    fileAsObject: FileOutputStream;
    projectFileName: String;
begin
    with saveProjectDialog do begin
        if execute() = false then begin
            exit;
        end;
        projectFileName := fileName;
    end;
    fileAsObject := FileOutputStream.create(stringToUTF16(projectFileName), false);
    if fileAsObject.hasOpenError() then begin
        fileAsObject.free();
        openDialog(msgError, msgErrorSavingProject, mtError, [mbClose]);
        exit;
    end;
    fileAsStream := fileAsObject;
    try
        saveProjectToStream(fileAsStream);
    finally
        fileAsStream.close();
    end;
end;

procedure TMainForm.buildProjectClick(sender: TObject);
var
    i: int;
    lim: int;
    sources: String_Array1d;
begin
    translatorUseFasm := fasmAlsoBuild.checked;
    case targetProcessorBits.itemIndex of
     0: translatorBits := RegistersX86.MODE_16_BIT;
     1: translatorBits := RegistersX86.MODE_32_BIT;
     2: translatorBits := RegistersX86.MODE_64_BIT;
    end;
    case targetProcessorInstructionSet.itemIndex of
     0: translatorInstructionSet := TranslatorNamespacesBuilder.EXTENSION_SSE3;
     1: translatorInstructionSet := TranslatorNamespacesBuilder.EXTENSION_SSSE3;
     2: translatorInstructionSet := TranslatorNamespacesBuilder.EXTENSION_SSE4_1;
    end;
    translatorHeapSize := stringParseInt(platformHeapSize.text, 10, -1);
    translatorStackSize := stringParseInt(platformStackSize.text, 10, -1);
    case platformExitInstruction.itemIndex of
     0: translatorExitInstruction := TextGenerator.EXIT_RET;
     1: translatorExitInstruction := TextGenerator.EXIT_RETF;
     2: translatorExitInstruction := TextGenerator.EXIT_CALL;
    end;
    translatorExecutableType := generatorsCurrent[platformExecutableType.itemIndex].generatorClass;
    with sourceFileList.items do begin
        lim := count - 1;
        sources := String_Array1d_create(lim + 1);
        for i := 0 to lim do begin
            sources[i] := strings[i];
        end;
    end;
    translatorSources := sources;
    translatorDestination := destinationFileName.text;
    translatorFasmPath := fasmExePath.text;
    translatorExecutable := fasmDestination.text;
    with Thread.create(self) do begin
        freeOnTerminate := true;
        start();
    end;
    sendMessage(handle, LM_COMPILE_BEGIN, 0, 0);
end;

procedure TMainForm.aboutAVTClick(sender: TObject);
begin
    openDialog(msgAbout, msgAboutContent, mtInformation, [mbClose]);
end;

procedure TMainForm.closeAVTClick(sender: TObject);
begin
    close();
end;

procedure TMainForm.runFasm();
var
    fasmStartupInfo: Windows.StartupInfoW;
    fasmProcessInfo: Windows.Process_Information;
    fasmArguments: UnicodeString;
begin
    initialize(fasmStartupInfo);
    initialize(fasmProcessInfo);
    fasmArguments := '"' + stringToUTF16(translatorFasmPath) + '" "' +
            stringToUTF16(translatorDestination) + '" "' +
            stringToUTF16(translatorExecutable) + '"'#$0000;
    fillChar(fasmStartupInfo, sizeof(Windows.StartupInfoW), 0);
    fasmStartupInfo.cb := sizeof(Windows.StartupInfoW);
    fasmStartupInfo.wShowWindow := 1;
    if Windows.createProcessW(nil, PWideChar(fasmArguments), nil, nil,
            false, $20, nil, nil, fasmStartupInfo, fasmProcessInfo) then begin
        Windows.closeHandle(fasmProcessInfo.hProcess);
        Windows.closeHandle(fasmProcessInfo.hThread);
    end;
end;

procedure TMainForm.sourceButtonsSetEnabledState();
var
    hasEnabled: boolean;
    hasSelected: boolean;
begin
    with sourceFileList do begin
        hasSelected := selCount > 0;
        with sourceExpell do begin
            if (hasSelected = false) and focused() then begin
                sourceFileList.setFocus();
            end;
            enabled := hasSelected;
        end;
        hasEnabled := hasSelected and (selected[0] = false);
        with sourceMoveUp do begin
            if (hasEnabled = false) and focused() then begin
                sourceFileList.setFocus();
            end;
            enabled := hasEnabled;
        end;
        hasEnabled := hasSelected and (selected[count - 1] = false);
        with sourceMoveDown do begin
            if (hasEnabled = false) and focused() then begin
                sourceFileList.setFocus();
            end;
            enabled := hasEnabled;
        end;
    end;
end;

procedure TMainForm.saveProjectToStream(stream: Output);
var
    i: int;
    lim: int;
    settings: AbstractInitializationFile;
    valueStr: String;
begin
    settings := NormalInitializationFile.create();
    try
        valueStr := copy(targetProcessorBits.text, 1, 2);
        settings.writeString(SECTION_PROCESSOR, IDENT_BITS, valueStr);
        case targetProcessorInstructionSet.itemIndex of
         0: valueStr := VALUE_SSE3;
         1: valueStr := VALUE_SSSE3;
         2: valueStr := VALUE_SSE4_1;
         3: valueStr := VALUE_AVX_512;
        end;
        settings.writeString(SECTION_PROCESSOR, IDENT_INSTRUCTION_SET, valueStr);
        with sourceFileList.items do begin
            lim := count - 1;
            for i := 0 to lim do begin
                settings.writeString(SECTION_SOURCES, IDENT_FILE + intToString(i + 1),
                        strings[i]);
            end;
        end;
        settings.writeInt(SECTION_PLATFORM, IDENT_EXE_TYPE_INDEX,
                platformExecutableType.itemIndex);
        case platformExitInstruction.itemIndex of
         0: valueStr := VALUE_RET;
         1: valueStr := VALUE_RETF;
         2: valueStr := VALUE_CALL;
        end;
        settings.writeString(SECTION_PLATFORM, IDENT_EXIT_INSTRUCTION, valueStr);
        settings.writeString(SECTION_PLATFORM, IDENT_HEAP_SIZE, platformHeapSize.text);
        settings.writeString(SECTION_PLATFORM, IDENT_STACK_SIZE, platformStackSize.text);
        settings.writeString(SECTION_DESTINATION, IDENT_FILE_NAME, destinationFileName.text);
        settings.writeBoolean(SECTION_FASM, IDENT_USE, fasmAlsoBuild.checked);
        settings.writeString(SECTION_FASM, IDENT_PATH, fasmExePath.text);
        settings.writeString(SECTION_FASM, IDENT_FILE_NAME, fasmDestination.text);
        settings.saveToStream(stream);
    finally
        settings.free();
    end;
end;

procedure TMainForm.loadProjectFromStream(stream: Input);
var
    i: int;
    valueInt: int;
    settings: AbstractInitializationFile;
    ident: String;
    valueStr: String;
begin
    settings := NormalInitializationFile.create();
    try
        settings.loadFromStream(stream);
        case settings.readInt(SECTION_PROCESSOR, IDENT_BITS, 64) of
        16: targetProcessorBits.itemIndex := 0;
        32: targetProcessorBits.itemIndex := 1;
        else
            targetProcessorBits.itemIndex := 2;
        end;
        targetProcessorBitsChange(targetProcessorBits);
        valueStr := stringToUpperCase(settings.readString(SECTION_PROCESSOR, IDENT_INSTRUCTION_SET,
                VALUE_SSE3));
        if valueStr = VALUE_SSE3 then begin
            targetProcessorInstructionSet.itemIndex := 0;
        end else
        if valueStr = VALUE_SSSE3 then begin
            targetProcessorInstructionSet.itemIndex := 1;
        end else
        if valueStr = VALUE_SSE4_1 then begin
            targetProcessorInstructionSet.itemIndex := 2;
        end;
        with sourceFileList.items do begin
            beginUpdate();
            try
                clear();
                i := 1;
                repeat
                    ident := IDENT_FILE + intToString(i);
                    inc(i);
                    if not settings.isIdentExists(SECTION_SOURCES, ident) then begin
                        break;
                    end;
                    valueStr := settings.readString(SECTION_SOURCES, ident, '');
                    if length(valueStr) > 0 then begin
                        add(valueStr);
                    end;
                until false;
            finally
                endUpdate();
            end;
        end;
        sourceButtonsSetEnabledState();
        valueInt := settings.readInt(SECTION_PLATFORM, IDENT_EXE_TYPE_INDEX, 0);
        with platformExecutableType, items do begin
            if (valueInt >= 0) and (valueInt < count) then begin
                itemIndex := valueInt;
            end;
        end;
        platformExecutableTypeChange(platformExecutableType);
        valueStr := stringToLowerCase(settings.readString(SECTION_PLATFORM, IDENT_EXIT_INSTRUCTION,
                VALUE_RET));
        if valueStr = VALUE_RET then begin
            platformExitInstruction.itemIndex := 0;
        end else
        if valueStr = VALUE_RETF then begin
            platformExitInstruction.itemIndex := 1;
        end else
        if valueStr = VALUE_CALL then begin
            platformExitInstruction.itemIndex := 2;
        end;
        valueInt := settings.readInt(SECTION_PLATFORM, IDENT_HEAP_SIZE, -1);
        if valueInt >= 0 then begin
            platformHeapSize.text := intToString(valueInt);
        end else begin
            platformHeapSize.text := '';
        end;
        valueInt := settings.readInt(SECTION_PLATFORM, IDENT_STACK_SIZE, -1);
        if valueInt >= 0 then begin
            platformStackSize.text := intToString(valueInt);
        end else begin
            platformStackSize.text := '';
        end;
        destinationFileName.text := settings.readString(SECTION_DESTINATION, IDENT_FILE_NAME, '');
        fasmAlsoBuild.checked := settings.readBoolean(SECTION_FASM, IDENT_USE, false);
        fasmExePath.text := settings.readString(SECTION_FASM, IDENT_PATH, '');
        fasmDestination.text := settings.readString(SECTION_FASM, IDENT_FILE_NAME, '');
        fasmAlsoBuildClick(fasmAlsoBuild);
    finally
        settings.free();
    end;
end;

procedure TMainForm.wmCompileBegin(var msg: TLMessage);
begin
    saveProject.setFocus();
    with buildProject do begin
        caption := msgWait;
        enabled := false;
    end;
end;

procedure TMainForm.wmCompileEnd(var msg: TLMessage);
begin
    with buildProject do begin
        caption := msgCompile;
        enabled := true;
    end;
end;

procedure TMainForm.wmCompileError(var msg: TLMessage);
begin
    with buildProject do begin
        caption := msgCompile;
        enabled := true;
    end;
    if length(errorSource) > 0 then begin
        openDialog(msgError, errorInfo +
                LINE_ENDING + msgSource + errorSource +
                LINE_ENDING + msgLine + intToString(errorLine) +
                LINE_ENDING + msgChar + intToString(errorChar),
                mtError, [mbClose]);
        exit;
    end;
    openDialog(msgError, errorInfo, mtError, [mbClose]);
end;

function TMainForm.loadSourceFromStream(stream: Input): UnicodeString;
var
    size: int;
    content: byte_Array1d;
begin
    size := int(stream.available());
    content := byte_Array1d_create(size);
    stream.read(content, 0, size);
    result := stringToUTF16(String_create(content, 0, size));
end;

constructor TMainForm.create(theOwner: TComponent);
var
    binaryFile: GenerationInfo;
begin
    inherited create(theOwner);
    binaryFile := CodeGenerationInfo.create(msgBinaryFile, TextGenerator);
    self.generators16bit := GenerationInfo_Array1d(Interface_Array1d_create([
        binaryFile,
        CodeGenerationInfo.create(msg16bitDOS_COM,
                TextGenerator_16bit_DOS_COM) as GenerationInfo
    ]));
    self.generators32bit := GenerationInfo_Array1d(Interface_Array1d_create([
        binaryFile,
        CodeGenerationInfo.create(msg32bitGNULinux_ELF,
                TextGenerator_32bit_GNULinux_ELF) as GenerationInfo,
        CodeGenerationInfo.create(msg32bitWindows_GUI,
                TextGenerator_32bit_Windows_GraphicApp) as GenerationInfo,
        CodeGenerationInfo.create(msg32bitWindows_Console,
                TextGenerator_32bit_Windows_ConsoleApp) as GenerationInfo,
        CodeGenerationInfo.create(msg32bitKolibriOS_MENUET01,
                TextGenerator_32bit_KolibriOS_MENUET01) as GenerationInfo
    ]));
    self.generators64bit := GenerationInfo_Array1d(Interface_Array1d_create([
        CodeGenerationInfo.create(msgBinaryFile,
                TextGenerator64) as GenerationInfo,
        CodeGenerationInfo.create(msg64bitGNULinux_ELF64,
                TextGenerator_64bit_GNULinux_ELF) as GenerationInfo,
        CodeGenerationInfo.create(msg64bitWindows_GUI,
                TextGenerator_64bit_Windows_GraphicApp) as GenerationInfo,
        CodeGenerationInfo.create(msg64bitWindows_Console,
                TextGenerator_64bit_Windows_ConsoleApp) as GenerationInfo
    ]));
    self.generatorsCurrent := self.generators64bit;
end;

procedure TMainForm.afterConstruction();
var
    fileAsStream: Input;
    fileAsObject: FileInputStream;
    arguments: UnicodeString_Array1d;
begin
    inherited afterConstruction();
    arguments := stringParseCommandLine();
    if length(arguments) <= 1 then begin
        targetProcessorBitsChange(targetProcessorBits);
        exit;
    end;
    fileAsObject := FileInputStream.create(arguments[1]);
    if fileAsObject.hasOpenError() then begin
        fileAsObject.free();
        targetProcessorBitsChange(targetProcessorBits);
        exit;
    end;
    fileAsStream := fileAsObject;
    try
        loadProjectFromStream(fileAsStream);
    finally
        fileAsStream.close();
    end;
end;

procedure TMainForm.run();
var
    i: int;
    lim: int;
    ext: int;
    bits: int;
    valueInt: int;
    lexemes: Lexer;
    generator: TextGenerator;
    programme: BuilderOfTrees;
    translator: BuilderOfTargetCode;
    destAsObject: FileOutputStream;
    destAsStream: Output;
    sourceAsObject: FileInputStream;
    sourceAsStream: Input;
    sources: String_Array1d;
    source: String;
begin
    bits := translatorBits;
    ext := translatorInstructionSet;
    programme := TranslatorTreeBuilder.create(bits, ext);
    try
        try
            sources := translatorSources;
            lim := length(sources) - 1;
            for i := 0 to lim do begin
                source := sources[i];
                sourceAsObject := FileInputStream.create(stringToUTF16(source));
                if sourceAsObject.hasOpenError() then begin
                    sourceAsObject.free();
                    raise IOException.create(msgErrorLoadingSource + source);
                end;
                sourceAsStream := sourceAsObject;
                try
                    lexemes := TranslatorLexer.create(stringToUTF16(source));
                    lexemes.split(false, loadSourceFromStream(sourceAsStream));
                finally
                    sourceAsStream.close();
                    sourceAsStream := nil;
                end;
                programme.addLexer(lexemes);
            end;
            programme.buildNamespaces();
            generator := translatorExecutableType.create();
            try
                valueInt := translatorHeapSize;
                if generator.isNeedHeapSize() and (valueInt >= 0) then begin
                    generator.heapSize := valueInt;
                end;
                valueInt := translatorStackSize;
                if generator.isNeedStackSize() and (valueInt >= 0) then begin
                    generator.stackSize := valueInt;
                end;
                if generator.isNeedExitMethod() then begin
                    generator.exitMethod := translatorExitInstruction;
                end;
                case bits of
                TranslatorType.SIZE_16_BIT:
                    translator := TranslatorBuilderOf16bitCode.create(generator, false);
                TranslatorType.SIZE_32_BIT:
                    translator := TranslatorBuilderOf32bitCode.create(generator, false);
                else
                    translator := TranslatorBuilderOf64bitCode.create(ext,
                            generator as TextGenerator64, false);
                end;
                try
                    destAsObject := FileOutputStream.create(
                            stringToUTF16(translatorDestination), false);
                    if destAsObject.hasOpenError() then begin
                        destAsObject.free();
                        raise IOException.create(msgErrorSavingDestination);
                    end;
                    destAsStream := destAsObject;
                    try
                        translator.buildTargetCode(programme, destAsStream);
                    finally
                        destAsStream.close();
                        destAsStream := nil;
                    end;
                finally
                    translator := nil;
                end;
            finally
                generator.free();
            end;
        finally
            programme.clear();
            programme := nil;
        end;
    except
        on e: TranIntf.CompileError do begin
            errorInfo := e.message;
            errorSource := stringToUTF8(e.sourceName);
            errorLine := e.sourceLine;
            errorChar := e.sourceChar;
            sendMessage(handle, LM_COMPILE_ERROR, 0, 0);
            exit;
        end;
        on e: SysUtils.Exception do begin
            errorInfo := e.message;
            errorSource := '';
            errorLine := 0;
            errorChar := 0;
            sendMessage(handle, LM_COMPILE_ERROR, 0, 0);
            exit;
        end;
    end;
    sendMessage(handle, LM_COMPILE_END, 0, 0);
    if translatorUseFasm and (length(translatorFasmPath) > 0) and
            (length(translatorExecutable) > 0) then begin
        runFasm();
    end;
end;

function TMainForm.openDialog(dialog: TForm): int;
var
    capture: int;
begin
    if dialog = nil then begin
        raise NullPointerException.create(msgOpenDialogFormIsNull);
    end;
    if openedDialog <> nil then begin
        raise EInvalidOperation.create(msgOpenDialogAlreadyOpened);
    end;
    if self.enabled = false then begin
        raise EInvalidOperation.create(msgOpenDialogDisabled);
    end;
    if dialog.visible = true then begin
        raise EInvalidOperation.create(msgOpenDialogFormAlreadyOpened);
    end;
    if dialog.enabled = false then begin
        raise EInvalidOperation.create(msgOpenDialogFormDisabled);
    end;
    if dialog.formStyle = TFormStyle.fsMDIChild then begin
        raise EInvalidOperation.create(msgOpenDialogFormMDIChild);
    end;

    dragManager.dragStop(false);
    if activePopupMenu <> nil then begin
        activePopupMenu.close();
    end;
    capture := getCapture();
    if capture <> 0 then begin
        sendMessage(capture, LM_CANCELMODE, 0, 0);
    end;
    releaseCapture();

    result := 0;
    openedDialog := dialog;
    try
        dialog.position := poDesigned;
        dialog.left := Math.max(0, Math.min(left + ((self.width - dialog.width) div 2),
                screen.desktopWidth - dialog.width));
        dialog.top := Math.max(0, Math.min(top + ((self.height - dialog.height) div 2),
                screen.desktopHeight - dialog.height));
        dialog.modalResult := 0;
        dialog.show();
        dialog.bringToFront();
        self.enabled := false;
        try
            repeat
                try
                    widgetSet.appProcessMessages();
                except
                    if application.captureExceptions then begin
                        application.handleException(self);
                    end else begin
                        raise;
                    end;
                end;
                if application.terminated then begin
                    dialog.modalResult := 0;
                    break;
                end;
                if dialog.modalResult <> 0 then begin
                    dialog.hide();
                    break;
                end;
                if dialog.visible = false then begin
                    break;
                end;
                application.idle(true);
            until false;
            result := dialog.modalResult;
            if result = 0 then begin
                result := mrClose;
            end;
        finally
            self.enabled := true;
            self.show();
        end;
    finally
        openedDialog := nil;
    end;
end;

function TMainForm.openDialog(const caption, text: String; dialogType: TMsgDlgType;
        buttons: TMsgDlgButtons): int;
var
    dialog: TForm;
begin
    dialog := createMessageDialog(text, dialogType, buttons);
    try
        dialog.caption := caption;
        result := openDialog(dialog);
    finally
        dialog.free();
    end;
end;

class procedure TMainForm.createInstance();
begin
    application.createForm(self, INSTANCE);
end;

{ CodeGenerationInfo }

constructor CodeGenerationInfo.create(const executableName: String;
        generatorClass: TextGenerator_Class);
begin
    inherited create();
    self.executableName := executableName;
    self.generatorClass := generatorClass;
end;

function CodeGenerationInfo.getExecutableName(): String;
begin
    result := executableName;
end;

function CodeGenerationInfo.getGeneratorClass(): TextGenerator_Class;
begin
    result := generatorClass;
end;

end.