Автор Тема: Обёртка для AutoIt-программ, имитирующая область видимости «файл»  (Прочитано 3207 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн Spray [?]

  • Новичок
  • *
  • Сообщений: 10
  • Репутация: 1
    • Награды
  • Версия AutoIt: 3.3.14.0
Задача: создание и внедрение обработчика, который в каждом файле к именам функций и переменных добавляет уникальную приставку, но лишь к тем именам, которые начинаются с определённого символа (например, с подчёркивания), после чего будет затуднено использование при подключении через #include. Опционально обработчик должен уметь рекурсивно обработать все файлы, подключаемые через #include "с_кавычками" (чтобы не трогать библиотеки — они на совести собственных разработчиков).

Если не в теме, объясняю, для чего это может быть нужно. В AutoIt функции и глобальные переменные имеют глобальную область видимости, и включая библиотеку по #include вы видите все имена, которые там были объявлены, что может привести к конфликту имён и заставляет напрягаться, придумывая приставки типа «__MyLib_…».

Например, у вас есть два файла:

main.au3:
Код: AutoIt [Выделить]
#inclule "mylib.au3"

global $x

func print_x ()
    ConsoleWrite ($x)
endfunc
 


mylib.au3:
Код: AutoIt [Выделить]
#include-once

global $x

func print_x ()
    MsgBox (0, "", $x)
endfunc
 


Интерпретатор будет ругаться, так как переменная с именем x и функция с именем print_x объявлены дважды. Во избежание конфликта можно изменить имена в файле mylib.au3:

mylib.au3:
Код: AutoIt [Выделить]
#include-once

global $mylib_x

func mylib_print_x ()
    MsgBox (0, "", $mylib_x)
endfunc
 


Выглядит громоздко, особенно когда программа часто обращается к этим именам. Предложенный мною обработчик позволит избежать конфликт, всего лишь добавив в начало имени переменных и функций, которые необходимо скрыть, например, знак подчёркивания. Тогда вот такой файл

mylib.au3:
Код: AutoIt [Выделить]
#include-once

global $_bar1, $bar2

func _foo1 ()
    …
endfunc

func foo2 ()
    …
endfunc
 


автоматически превратится в такой (набор символов в конце имени для надёжности)

mylib.au3:
Код: AutoIt [Выделить]
#include-once

global $mylib_bar1__K3PDVOLEW3RV23K, $bar2

func mylib_foo1__K3PDVOLEW3RV23K ()
    …
endfunc

func foo2 ()
    …
endfunc
 


Так что если его подключить из другого файла с теми же именами _foo1 и _bar1, конфликта не произойдёт, что избавит от лишней нервотрёпки и позволит писать более лёгкий для восприятия код.

Моя реализация

Прикрепил как вложение. Как пользоваться:

FileScope.au3 filename

filename — имя файла, с которого начнётся обработка.

Обрабатываются рекурсивно все файлы, включаемые через #include "с_кавычками". Выходные файлы записываются в папку ~FileScope, которая будет создана рядом с файлом filename, независимо от того, где находится файл обработчика и какая директория является текущей. Взаимное расположение в дереве каталогов сохраняется.

При желании в алгоритм можно добавить обработку ключей, например
-p=nameprefix — будут обработаны имена с этой приставкой (по умолчанию знак подчёркивания)
-s — выключение рекурсивной обработки
-i — выводить файлы в те же папки, где находятся исходные файлы
-f=fix — имя папки, в которую будут записаны выходные файлы, или окончание, которое будет добавлено к именам выходных файлов, если активирован ключ -i (по умолчанию ~FileScope).

Пока что я запускаю сценарий через самописный Makefile перед тем, как код попадёт к компилятору. Неплохо бы прикрутить обработчик к AutoIt3Wrapper, но здесь у меня мало опыта. Может кто из вас осилит эту задачу.


Внимание: Для просмотра прикреплённых файлов необходимо Войти или Зарегистрироваться
« Последнее редактирование: Ноябрь 01, 2017, 07:25:42 от Spray »
GoodKeys — мой главный продукт, сделанный с использованием Python и AutoIt ссылка:https://vk.com/page-64609_51648787 [nonactive]

Русское сообщество AutoIt


Оффлайн Spray [?]

  • Новичок
  • *
  • Сообщений: 10

  • Автор темы
  • Репутация: 1
    • Награды
  • Версия AutoIt: 3.3.14.0
Сейчас работаю над версией, которая будет не в виде обёртки, которую нужно прикручивать к запуску/компиляции, а будет активироваться при простом включении через #include. Как ей пользоваться:

  • скопировать файл FileScope.au3 в папку Include
  • включить его в стартовый файл вашей программы (#include <FileScope.au3>)
  • файлы проекта, которые также нужно обработать, включить через #import "MyLib.au3"
  • запускать программу в обход AutoIt3Wrapper или с отключением проверки кода

MyProg.au3
Код: AutoIt [Выделить]
#AutoIt3Wrapper_Run_AU3Check=N
#include <FileScope.au3>
#import "MyLib.au3"

global $_private = MyLib_foo () ; Ура, конфликта с повторным объявлением не будет!

ConsoleWrite ($_private) ; В SciTE произойдёт вывод в окно Output!
 


MyLib.au3
Код: AutoIt [Выделить]
global $_private = 10

func MyLib_foo ()
    return $_private
endfunc
 


Для чего всё это нужно? (3) — если включать файлы через обычный #include, то при запуске интерпретатор AutoIt сразу найдёт повторные объявления и приостановит выполнение. (4) — AutoIt3Wrapper предварительно проверяет код, поэтому будет ругаться на все функции из библиотек, «включаемых» по #import, ибо он проигнорирует директиву и просто не найдёт соответствующих объявлении.

Как это работает? Во включаемом FileScope.au3 находится код, который приводится в действие сразу же по запуску вашей программы: автоматически обрабатываются файлы, после чего запускается новый процесс AutoIt, интерпретирующий уже преобразованный код, а текущий процесс остаётся крутиться в цикле, чтобы продолжать обмен данными с новым процессом через стандартные потоки. Обработке подлежат только файлы, включаемые по #import. Обработчик комментирует строчку #include <FileScope.au3> во избежание рекурсии, а все #import заменяет на обычный #include. Обработанные файлы получают расширение .auf и помещаются в папку исходного сценария.

Следующий этап

Новая задача: имитация оператора import наподобие того, как это сделано в Python. А именно:
  • «импортированные» публичные функции (т. е. без подчёркивания в начале) должны быть тоже «зашифрованы» для сокрытия от лишних глаз, чтобы видели их только те, кто импортирует файл напрямую
first.au3:
Код: AutoIt [Выделить]
#AutoIt3Wrapper_Run_AU3Check=N
#include <import>
#import "second.au3"
 


second.au3:
Код: AutoIt [Выделить]
#import "third.au3"
#import "fourth.au3"
 

публичные из third.au3 не должны быть видны ни в first.au3, ни в fourth.au3, только в second.au3 (и для себя, конечно)
  • возможность импортировать публичные функции как есть (будут доступны по оригинальным именам) либо заданной приставкой
Код: AutoIt [Выделить]
#AutoIt3Wrapper_Run_AU3Check=N
#include <import>
#import "foo.au3" as is
#import "bar.au3" with MyPrefix

FuncFromFoo ()
MyPrefix_FuncFromBar ()
FuncFromBar () ; ошибка
 



Внимание: Для просмотра прикреплённых файлов необходимо Войти или Зарегистрироваться
« Последнее редактирование: Ноябрь 01, 2017, 08:03:11 от Spray »

Оффлайн Mute [?]

  • Новичок
  • *
  • Сообщений: 7
  • Репутация: 0
  • Пол: Мужской
    • Награды
  • Версия AutoIt: 3.3.14.0
Спасибо, очень пригодился скрипт

Оффлайн Spray [?]

  • Новичок
  • *
  • Сообщений: 10

  • Автор темы
  • Репутация: 1
    • Награды
  • Версия AutoIt: 3.3.14.0
Следующий этап

Новая задача: имитация оператора import наподобие того, как это сделано в Python. А именно:
  • «импортированные» публичные функции (т. е. без подчёркивания в начале) должны быть тоже «зашифрованы» для сокрытия от лишних глаз, чтобы видели их только те, кто импортирует файл напрямую
  • возможность импортировать публичные функции как есть (будут доступны по оригинальным именам) либо заданной приставкой

Задача оказалась непростой. Вот моё решение для упрощённой модели.
  • Имеется дерево зависимостей, заданное массивом $DepsOf, каждый индекс которого соответствует файлу, а каждое значение — массив зависимостей файла.
    Код: AutoIt [Выделить]
    const $deps_of_1 [2] = [2, -3]
    const $deps_of_2 [0] = []
    const $deps_of_3 [0] = []
    const $DepsOf = [3, $deps_of_1, $deps_of_2, $deps_of_3]

    Это значит, что дерево состоит из трёх файлов: 1 зависит от 2 и 3, а 2 и 3 не имеют зависимостей. Причём 1 связан с 2 через обычный #include, а с 3 — через #import.
    2 <− [1] => 3

    1: #include "2", #import "3"…
    2: …
    3: …
  • Задача: по данным массива $DepsOf сгенерировать массив $SeersOf, каждый индекс которого соответствует файлу, но каждое значение — массив зависимых файлов, которые должны видеть содержимое этого файла. Т. е. для примера выше на выходе должно получиться
    Код: AutoIt [Выделить]
    $SeersOf = [3, [2], [1], [-1, -2]]

    Это значит, что для трёх файлов:
    • все имена из 1 должны быть видны в 2, но не в 3
    • все имена из 2 должны быть видны в 1, но не в 3
    • публичные имена из 3 должны быть видны и в 1, и в 2.
    Все имена из 1 должны быть видны в 2 (п. 1) неспроста, т. к. 2 по сути «слеплен» с 1 через #include. То же относится и к п. 3: 2 как часть 1 импортит 3, а значит тоже должен видеть его публичные имена.
    Код: AutoIt [Выделить]
    #include <Array.au3>

    global const $EMPTY [0] = []
    global enum $INCLUDE, $IMPORT

    local const $deps_of_1 [3] = [-2, -3, -5]
    local const $deps_of_2 [2] = [4, -5]
    local const $deps_of_3 [2] = [6, 9]
    local const $deps_of_4 [1] = [-7]
    local const $deps_of_5 [3] = [-4, 8, -9]
    local const $deps_of_6 [1] = [9]
    local const $deps_of_7 [1] = [-5]
    local const $deps_of_8 [0] = []
    local const $deps_of_9 [0] = []
    global const $DepsOf = [9, $deps_of_1, $deps_of_2, $deps_of_3, $deps_of_4, $deps_of_5, $deps_of_6, $deps_of_7, $deps_of_8, $deps_of_9]

    ; init seers table
    global $SeersOf = [0]; seers table: index is module number, element is array of seers, [0] is count of elements
    append_seers (1, $EMPTY)

    capture_seers (1)
    for $i = 1 to $SeersOf [0]
        ConsoleWrite ($i & ": " & _ArrayToString ($SeersOf [$i], ", ") & @CRLF)
    next

    func capture_seers ($module, $host=0, $character=$INCLUDE)
    ; capture seers and return seers from dependencies

        ; 1: 2 -3 --> [A: B -C]
        if $host <> 0 then
            switch $character
            case $INCLUDE
                ; append seers from host (1, 2, -3) to module A
                local $seers_from_host = $SeersOf [$host] ; add 2 and -3
                _ArrayAdd ($seers_from_host, $host) ; add 1
            case $IMPORT
                ; append seers from host (-1, -2) to module A
                local $seers_from_host = [-$host] ; add 1 as -1
                for $seer in $SeersOf [$host]
                    if character ($seer) = $INCLUDE then _ArrayAdd ($seers_from_host, -$seer); add 2 as -2
                next
            endswitch
        else
            local $seers_from_host [0]
        endif
       
        ; [A: B -C] --> X: Y -Z
        local $seers_from_deps [0]
        if append_seers ($module, $seers_from_host) or $host = 0 then
            if UBound ($DepsOf [$module]) then
                local $ascndn_seers_of_dep
                local $appended = False
                do ; recursive traversal and append seers from dependencies (X) to module A
                    for $dep in $DepsOf [$module]
                        $ascndn_seers_of_dep = capture_seers (Abs ($dep), $module, character ($dep))
                        if character ($dep) = $INCLUDE then
                            _ArrayConcatenate ($seers_from_deps, $ascndn_seers_of_dep)
                            _ArrayAdd ($seers_from_deps, $dep)
                        endif
                    next
                    $appended = append_seers ($module, $seers_from_deps)
                until not $appended
            endif
        endif
        return ($character=$INCLUDE) ? $seers_from_deps : $EMPTY
       
    endfunc

    func append_seers (const $module)
    ; return True if $SeersOf changed else False
        if $module > $SeersOf [0] then
            redim $SeersOf [$module + 1]
            $SeersOf [0] = $module
        endif
        if not IsArray ($SeersOf [$module]) then $SeersOf [$module] = $EMPTY
        if not UBound ($new_seers) then return False
        ; exclude self
            _ArrayDelete ($new_seers, _ArraySearch ($new_seers, $module))
            _ArrayDelete ($new_seers, _ArraySearch ($new_seers, -$module))
        if not UBound ($new_seers) then return False
        ; extend $SeersOf if needed
        local $seers = $SeersOf [$module]
        _ArrayConcatenate ($seers, $new_seers)
        $seers = ArrayUnique ($seers)
        if UBound ($seers) = UBound ($SeersOf [$module]) then
            local $changed = False
            for $i = 0 to UBound ($seers) - 1
                if $seers [$i] <> ($SeersOf [$module]) [$i] then
                    $changed = True
                    exitloop
                endif
            next
            if not $changed then return False
        endif
        $SeersOf [$module] = $seers
        return True
    endfunc

    func character (const $dep)
        return ($dep > 0) ? $INCLUDE : $IMPORT
    endfunc

    func ArrayUnique (const byref $array)
        local $result [0], $found, $size
        for $src_val in $array
            $found = False
            for $i = 0 to UBound ($result) - 1
                if Abs ($src_val) = Abs ($result [$i]) then
                    if $src_val > $result [$i] then $result [$i] = $src_val
                    $found = True
                    exitloop
                endif
            next
            if not $found then
                $size = UBound ($result)
                redim $result [$size + 1]
                $result [$size] = $src_val
            endif
        next
        return $result
    endfunc

  • После того как массив $SeersOf сгенерирован, останется зашифровать имена в каждом файле и такие же имена в тех файлах, которые от него зависят (в случае зависимости через #import — только публичные).
« Последнее редактирование: Февраль 11, 2019, 10:03:29 от Spray »

Русское сообщество AutoIt


Онлайн CreatoR [?]

  • Администратор
  • *
  • Сообщений: 7845
  • Репутация: 2292
  • Пол: Мужской
  • AutoIt is simple, subtle, elegant
    • CreatoR's Lab
    • Награды
  • Версия AutoIt: 3.3.10.2
Это интересно конечно, но мне кажется оно не стоит того чтобы заморачиваться.
Код нужно писать предотвращая ошибки на ходу, а не исправляя уже созданные.
Если мне нужно использовать стороннюю библиотеку где автор не продумал этот нюанс, то я поправлю библиотеку, это делается двумя заменами по рег. выражению (по Ctrl + H):

1:
Ищем:
Global\s+\$\(\w+\)
заменяем на:
Global $LIB_Prefix_\1

2:
Ищем:
^\s*Func\s+_?\(\w+\)
заменяем на:
Func _LIB_Prefix_\1


Правила, Поиск, Супер тема


AutoIt is simple, subtle, elegant.


«Не оказываю тех. поддержку через ПМ/ICQ, и по электронной почте - для этого есть форум. (C)»
«Законы Мэрфи неоспоримы!»


Мои работы

Оффлайн Spray [?]

  • Новичок
  • *
  • Сообщений: 10

  • Автор темы
  • Репутация: 1
    • Награды
  • Версия AutoIt: 3.3.14.0
Это интересно конечно, но мне кажется оно не стоит того чтобы заморачиваться.
Код нужно писать предотвращая ошибки на ходу, а не исправляя уже созданные.
Если мне нужно использовать стороннюю библиотеку где автор не продумал этот нюанс, то я поправлю библиотеку, это делается двумя заменами по рег. выражению

Согласен, заморочки. У меня больше научно-эстетический интерес, нежели практический.
Но, во-первых, повторюсь, код с префиксами выглядит громоздко, особенно когда программа часто обращается к этим именам, во-вторых, задачу я почти решил и грех не дойти до конца, в-третьих, твоё решение с заменой по регуляркам будет сложно применить, когда библиотека будет состоять из нескольких взаимозависимых файлов. Я как раз с такой ситуацией столкнулся, когда библиотека в разных файлах более чем на 1000 строк, и программа, использующая функции этой библиотеки, сама такая же.
Решение больше нацелено на разработку собственных библиотек, чем на использование сторонних. Это не исправление ошибок, а изменение стиля программирования, как бы на диалекте AutoIt с дополнительной особенностью, где можно забить на префиксы и чуть больше сил оставить на архитектурные задачи.

Оффлайн joiner [?]

  • Расмус-бродяга
  • AutoIt Гуру
  • *****
  • Сообщений: 2855
  • Репутация: 479
  • Пол: Мужской
    • Награды
  • Версия AutoIt: 3.3.12.0
Spray
чем сложнее инструмент, тем больше вероятность бага.
Верно сказано, что сразу делается проверка на ошибки и исправляется.
Лично я ни разу не сталкивался с одинаковыми именами функций. Понял еще одну вещь, что без нужды лучше не плодить глобальные переменные
для более чистой проверки кода использую http://autoit-script.ru/index.php?topic=3925.msg123627#msg123627
потому что редактор не всегда выдает предупреждения, а когда все собрано в один файл, тогда проще просканировать код.
правда, использую немного модифицированный вариант, чтобы результат сохранить в файл
(нажмите для показа/скрытия)
конечно, если это все для тренировки ума, то можно продолжать совершенствоваться.
Были времена, когда солнце было ярче, трава зеленее, а водка сорокоградуснее

Онлайн CreatoR [?]

  • Администратор
  • *
  • Сообщений: 7845
  • Репутация: 2292
  • Пол: Мужской
  • AutoIt is simple, subtle, elegant
    • CreatoR's Lab
    • Награды
  • Версия AutoIt: 3.3.10.2
Spray  [?]
Цитировать
твоё решение с заменой по регуляркам будет сложно применить, когда библиотека будет состоять из нескольких взаимозависимых файлов
Для этого я и написал FindEx )) (странно что я её не выкладывал ранее), который встраивается в оболочку SciTE.

Цитировать
Это не исправление ошибок, а изменение стиля программирования, как бы на диалекте AutoIt с дополнительной особенностью, где можно забить на префиксы и чуть больше сил оставить на архитектурные задачи.
У меня не уходят силы на префиксы так как это вошло в привычку, я считаю что как раз таки это и является стильным программированием )).

Русское сообщество AutoIt


Оффлайн Spray [?]

  • Новичок
  • *
  • Сообщений: 10

  • Автор темы
  • Репутация: 1
    • Награды
  • Версия AutoIt: 3.3.14.0
Неужели вы не любите модульное программирование, инкапсуляцию? Вам по приколу префиксы в ваших библиотеках, когда там тысячи строк и сложная архитектура? Дело ваше.

main.au3
Код: AutoIt [Выделить]
#import "module1.au3"
#import "module2.au3"
local $foo = module1_GetData ()
local $bar = module2_GetData ()


module1.au3
Код: AutoIt [Выделить]
_init ()

func _init ()
    _StartProcess ()
endfunc

func _StartProcess ()
    …
endfunc

func GetData ()
    …
endfunc


module2.au3
Код: AutoIt [Выделить]
_init ()

func _init ()
    _StartProcess ()
endfunc

func _StartProcess ()
    …
endfunc

func GetData ()
    …
endfunc


Оффлайн Alofa [?]

  • AutoIt Гуру
  • *****
  • Сообщений: 1445
  • Репутация: 217
  • Пол: Мужской
  • Windows 7 /10 [x64]
    • Награды
  • Версия AutoIt: 3.3.x.x
... Вам по приколу префиксы в ваших библиотеках...
OffTopicДело тут не в префиксах, а в порядке. Порядок должен быть везде, в том числе и в голове.
То что вы тут пытаетесь сотворить - это есть "костыль", расслабляющий и затуманивающий мозг Кодера. Когда случись не будет под рукой подобного инструмента то он (Кодер) потеряется [но это чисто ИМХО].


Добавлено: Февраль 12, 2019, 10:45:52
...Дело ваше.
OffTopicДа, да и ваше. Это чисто наши мнения.
Так что успехов Вам в ваших начинаниях
.
« Последнее редактирование: Февраль 12, 2019, 10:47:30 от Alofa »

Русское сообщество AutoIt


 

Похожие темы

  Тема / Автор Ответов Последний ответ
1 Ответов
3942 Просмотров
Последний ответ Февраль 10, 2011, 21:18:33
от Viktor217
4 Ответов
2706 Просмотров
Последний ответ Ноябрь 29, 2011, 11:58:56
от neobi
8 Ответов
4893 Просмотров
Последний ответ Январь 09, 2012, 23:51:19
от rollex
6 Ответов
3307 Просмотров
Последний ответ Апрель 29, 2012, 02:48:47
от Yashied
4 Ответов
3575 Просмотров
Последний ответ Июнь 07, 2014, 02:10:25
от CreatoR
2 Ответов
2034 Просмотров
Последний ответ Апрель 30, 2013, 16:56:48
от erlik
3 Ответов
3125 Просмотров
Последний ответ Май 04, 2013, 23:22:21
от AZJIO
2 Ответов
567 Просмотров
Последний ответ Август 19, 2017, 12:02:16
от lixar21
4 Ответов
739 Просмотров
Последний ответ Ноябрь 02, 2017, 03:29:22
от dr.room
1 Ответов
444 Просмотров
Последний ответ Январь 22, 2018, 22:18:14
от ra4o