avtc.lpr

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

{
    Компилятор языка программирования
    Объектно-ориентированный продвинутый векторный транслятор

    Copyright © 2021, 2024, 2026 Малик Разработчик

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

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

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

program avtc;

    {$MODE OBJFPC}

    {%region} uses
        pascalx.lang,
        pascalx.lang.locale,
        pascalx.lang.locale.rtlang,
        pascalx.lang.math.avo,
        pascalx.lang.math.vcoverage,
        pascalx.io,
        pascalx.io.bytearray,
        pascalx.io.charset,
        pascalx.io.charset.basiclatin,
        pascalx.io.charset.eightbit,
        pascalx.io.charset.rtlang,
        pascalx.io.charset.unicode,
        pascalx.io.extension,
        platform.independent.filesystem,
        platform.independent.osservices,
        platform.independent.streamformat,
        platform.independent.streamformat.compression.zlib,
        platform.independent.streamformat.text,
        platform.independent.streamformat.text.plain,
        ru.malik.elaborarer.avtoo.lang,
        ru.malik.elaborarer.avtoo.compiler,
        ru.malik.elaborarer.avtoo.generator,
        ru.malik.elaborarer.avtoo.avtc;
    {%endregion}

    {$R *.res}
    {$R *.icons.res}
    {$IFDEF WINDOWS}{$R *@windows.res}{$ELSE}{$R *@unix.res}{$ENDIF}

    function loadParametersFrom(stream: ByteReader): UnicodeString_Array1d;
    var
        text: UnicodeString;
        decoder: PlainCodec;
    begin
        decoder := PlainCodec.create();
        try
            decoder.loadFromInputStream(stream);
            text := decoder.getText();
        finally
            decoder.free();
        end;
        if text.startsWith(#$feff) then text := text.substring(2);
        result := text.split();
    end;

    function loadParametersFrom(fileSystem: FileSystem; const fileName: UnicodeString): UnicodeString_Array1d;
    var
        stream: ByteReader;
    begin
        stream := fileSystem.openFileForRead(fileName);
        try
            result := loadParametersFrom(stream);
        finally
            stream.close();
        end;
    end;

    function main(const arguments: UnicodeString_Array1d): int;
    var
        isLibrary: boolean;
        isReflect: boolean;
        isProjectDirectorySet: boolean;
        level: int;
        debug: int;
        index: int;
        chpos: int;
        time: long;
        stdStreamCharsetName: AnsiString;
        arg: UnicodeString;
        low: UnicodeString;
        value: UnicodeString;
        prefix: UnicodeString;
        parameter: UnicodeString;
        overwritten: UnicodeString;
        classpathName: UnicodeString;
        projectRootPath: UnicodeString;
        workingDirectory: UnicodeString;
        processDirectory: UnicodeString;
        projectDirectory: UnicodeString;
        libraryDirectory: UnicodeString;
        parameters: UnicodeString_Array1d;
        stdOut: PrintStream;
        stdErr: PrintStream;
        lexThread: PrintThread;
        codeThread: PrintThread;
        currentProcess: Process;
        programme: CodeGenerator;
        projectLibrary: &Library;
        projectFileSystem: FileSystem;
        libraryFileSystem: FileSystem;
        monitor: pascalx.lang.Monitor;
        excludes: ru.malik.elaborarer.avtoo.compiler.UnicodeStringArray;
        libraries: ru.malik.elaborarer.avtoo.compiler.UnicodeStringArray;
    begin
        result := 0;
        stdOut := nil;
        stdErr := nil;
        try
            { стандартный вывод }
            currentProcess := Process.current();
            stdStreamCharsetName := Charset.getConsoleOutput().name;
            stdOut := PrintStream.create(currentProcess.standardOutput, stdStreamCharsetName);
            stdErr := PrintStream.create(currentProcess.standardError, stdStreamCharsetName);
            { проверка поддержки AVX-512 }
            if not Vector.isAVX512SupportedByCPU() then begin
                stdErr.println(AResource.readUnitResourceAsAnsiString(pascalx.lang.math.avo.UNIT_NAME, 'noavx.cpu'));
                result := 207;
                exit;
            end;
            if not Vector.isAVX512SupportedByOS() then begin
                stdErr.println(AResource.readUnitResourceAsAnsiString(pascalx.lang.math.avo.UNIT_NAME, 'noavx.os'));
                result := 207;
                exit;
            end;
            { переменные для хранения аргументов командной строки }
            isProjectDirectorySet := false;
            level := BinarySourceGenerator.LEVEL_CLASS;
            debug := 0;
            classpathName := '';
            projectDirectory := '';
            { чтение аргументов командной строки }
            for index := 1 to system.length(arguments) - 1 do begin
                arg := arguments[index];
                low := arg.toLowerCase();
                prefix := '--level=';
                if low.startsWith(prefix) then begin
                    value := low.substring(prefix.length + 1);
                    if value = 'pack' then begin
                        level := BinarySourceGenerator.LEVEL_PACKAGE;
                        continue;
                    end;
                    if value = 'lib' then begin
                        level := BinarySourceGenerator.LEVEL_LIBRARY;
                    end;
                    continue;
                end;
                prefix := '--debug=';
                if low.startsWith(prefix) then begin
                    value := low.substring(prefix.length + 1);
                    if value = 'code' then begin
                        debug := 1;
                        continue;
                    end;
                    if value = 'lexemes' then begin
                        debug := 2;
                        continue;
                    end;
                    if value = 'both' then begin
                        debug := 3;
                        continue;
                    end;
                    if value = 'trace' then begin
                        debug := 4;
                        continue;
                    end;
                    if (value = 'code+trace') or (value = 'trace+code') then begin
                        debug := 5;
                        continue;
                    end;
                    if (value = 'lexemes+trace') or (value = 'trace+lexemes') then begin
                        debug := 6;
                        continue;
                    end;
                    if (value = 'both+trace') or (value = 'trace+both') then begin
                        debug := 7;
                    end;
                    continue;
                end;
                if not isProjectDirectorySet then begin
                    isProjectDirectorySet := true;
                    projectDirectory := arg;
                    continue;
                end;
                classpathName := arg;
            end;
            try
                { папки процесса }
                workingDirectory := currentProcess.workingDirectory;
                processDirectory := FileSystemRoot.toObjectPath(arguments[0]);
                processDirectory := processDirectory.substring(1, processDirectory.lastIndexOf('/') + 1);
                { вывод приветствия }
                stdOut.printlnf(AResource.readUnitResourceAsAnsiString('', 'title'), [ CoAnsiString.create(AVTOOConstants.getVersion()) ]);
                { папка проекта }
                if not isProjectDirectorySet then begin
                    stdOut.println(AResource.readUnitResourceAsAnsiString('', 'help'));
                    result := 1;
                    exit;
                end;
                if FileSystemRoot.isInternalPathFull(projectDirectory) then begin
                    projectDirectory := FileSystemRoot.toObjectPath(projectDirectory);
                end else begin
                    projectDirectory := workingDirectory + FileSystemRoot.get(workingDirectory).fileSystem.toObjectName(projectDirectory);
                end;
                if not projectDirectory.endsWith('/') then begin
                    projectDirectory := projectDirectory + '/';
                end;
                with FileSystemRoot.get(projectDirectory) do begin
                    projectRootPath := path;
                    projectDirectory := projectDirectory.substring(projectRootPath.length + 1);
                    projectFileSystem := fileSystem;
                end;
                { файл параметров проекта }
                if classpathName.length > 0 then begin
                    classpathName := projectFileSystem.toObjectName(classpathName);
                end;
                { процесс компиляции }
                { время компиляции }
                time := TimeBase.currentTimeInMillis();
                { параметры компиляции }
                parameters := loadParametersFrom(projectFileSystem, projectDirectory + classpathName + '.classpath');
                { переменные для хранения параметров компиляции }
                isLibrary := false;
                isReflect := false;
                excludes := nil;
                libraries := nil;
                programme := nil;
                try
                    try
                        excludes := ru.malik.elaborarer.avtoo.compiler.UnicodeStringArray.create();
                        libraries := ru.malik.elaborarer.avtoo.compiler.UnicodeStringArray.create();
                        { чтение параметров компиляции }
                        for index := 0 to system.length(parameters) - 1 do begin
                            parameter := parameters[index];
                            { комментарии }
                            chpos := parameter.indexOf(';');
                            if chpos > 0 then begin
                                parameter := parameter.substring(1, chpos);
                            end;
                            { параметры компиляции }
                            chpos := parameter.indexOf('=');
                            if chpos > 0 then begin
                                prefix := parameter.substring(1, chpos).trim();
                                value := parameter.substring(1 + chpos).trim();
                                if prefix = 'pointer-size' then begin
                                    { устаревший параметр }
                                    continue;
                                end;
                                if prefix = 'library' then begin
                                    isLibrary := (value <> '0') and (value.length > 0);
                                    continue;
                                end;
                                if prefix = 'reflect' then begin
                                    isReflect := (value <> '0') and (value.length > 0);
                                    continue;
                                end;
                            end;
                            { исключаемые из обработки файлы исходного кода }
                            prefix := '-';
                            parameter := parameter.trim();
                            if parameter.startsWith(prefix) then begin
                                excludes.append(parameter.substring(prefix.length + 1).trim());
                                continue;
                            end;
                            { используемые проектом библиотеки }
                            if parameter.length > 0 then begin
                                libraries.append(parameter);
                            end;
                        end;
                        { создание экземпляра компилятора }
                        if isLibrary then begin
                            programme := BinarySourceGenerator.create();
                            BinarySourceGenerator(programme).level := level;
                        end else begin
                            programme := AssemblerSourceGenerator.create(isReflect, classpathName.replaceAll('/', '.'));
                        end;
                        programme.documentationEnabled := (debug and 2) <> 0;
                        { исключаемые из обработки файлы исходного кода }
                        with programme.excludes do for index := 0 to excludes.getLength() - 1 do begin
                            append(excludes.readComponent(index).asUnicodeString());
                        end;
                        { используемые проектом библиотеки }
                        for index := 0 to libraries.getLength() - 1 do begin
                            libraryDirectory := libraries.readComponent(index).asUnicodeString();
                            if not libraryDirectory.startsWith('/') then libraryDirectory := processDirectory + libraryDirectory;
                            if not libraryDirectory.endsWith('/') then libraryDirectory := libraryDirectory + '/';
                            with FileSystemRoot.get(libraryDirectory) do begin
                                libraryDirectory := libraryDirectory.substring(path.length + 1);
                                libraryFileSystem := fileSystem;
                            end;
                            programme.createLibrary(libraryFileSystem, libraryDirectory, false);
                        end;
                    finally
                        excludes.free();
                        libraries.free();
                    end;
                    { создание библиотеки проекта }
                    projectLibrary := programme.createLibrary(projectFileSystem, projectDirectory, not isLibrary);
                    try
                        { компиляция проекта }
                        programme.compile();
                    except
                        on exc: RuntimeException do begin
                            exc.printStackTrace();
                            result := 4;
                        end;
                        on exc: Exception do begin
                            if (debug and 4) <> 0 then begin
                                exc.printStackTrace();
                            end else begin
                                stdErr.println(exc);
                            end;
                            if exc is CompilerException then begin
                                result := 2;
                            end else begin
                                result := 3;
                            end;
                        end;
                    end;
                    { вывод времени и результатов компиляции }
                    time := (TimeBase.currentTimeInMillis() - time) div 100;
                    overwritten := programme.overwritten;
                    if (result <> 0) or (overwritten.length <= 0) then begin
                        stdOut.printlnf(AResource.readUnitResourceAsAnsiString('', 'time.only'), [
                            CoAnsiString.create(CoLong.toString(time div 10)),
                            CoAnsiString.create(CoLong.toString(time mod 10))
                        ]);
                    end else begin
                        stdOut.printlnf(AResource.readUnitResourceAsAnsiString('', 'time.overwritten'), [
                            CoAnsiString.create(CoLong.toString(time div 10)),
                            CoAnsiString.create(CoLong.toString(time mod 10)),
                            CoUnicodeString.create(FileSystemRoot.toInternalPath(projectRootPath) + projectFileSystem.toInternalName(overwritten))
                        ]);
                    end;
                    { отладочная печать }
                    if (debug and 3) <> 0 then begin
                        monitor := pascalx.lang.Monitor.create();
                        try
                            lexThread := nil;
                            codeThread := nil;
                            if (debug and 1) <> 0 then begin
                                codeThread := PrintCodeThread.create(monitor, projectFileSystem, projectDirectory + 'debug.code.txt', programme);
                                codeThread.start();
                            end;
                            if (debug and 2) <> 0 then begin
                                lexThread := PrintLexemesThread.create(monitor, projectFileSystem, projectDirectory + 'debug.lexemes.txt', projectLibrary);
                                lexThread.start();
                            end;
                            monitor.beginSynchronized();
                            try
                                repeat
                                    monitor.wait();
                                until ((codeThread = nil) or codeThread.isTerminated()) and ((lexThread = nil) or lexThread.isTerminated());
                            finally
                                monitor.endSynchronized();
                            end;
                        finally
                            monitor.free();
                        end;
                    end;
                finally
                    programme.free();
                end;
            except
                on exc: IOException do begin
                    exc.printStackTrace();
                    result := 3;
                end;
                on exc: Exception do begin
                    exc.printStackTrace();
                    result := 4;
                end;
            end;
        finally
            stdOut.free();
            stdErr.free();
        end;
    end;

begin
    exitCode := main(Process.current().commandLine);
end.