Всё пнятненько,
smt005 - у тебя 100% скриптовый движок, работающий по принципу "читаю-исполняю". Выходит, каждый раз, как ты запускаешь скрипт, двой движок жуёт файл и исполняет его по кусочкам.
В общем, у нас только до сюда одинаково:
smt005 писал(а):
1. Сначало создаются функции (класс о котором позре напишу) в которых записывается указатель на реальную функцию, которая непосредственно работает в программе, например закрытие приложения. И создаётся переменная (о которй тоже позже наппшу) в которую записывается указатели на все важные ресурсы программы.
У меня подход похожий, но мой движок жуёт код один раз. Он точно так же нарезает код на фразы, каждая из которых содержит несколько токенов определённого вида (по одному токену каждого вида на фразу):
токен-идентификатор (переменной или функции), по-простому название, состоит из символов, отличных от
(){}[]",
Токен-аллокатор - это текст в квадратных скобках [] - определитель байтового размера переменной;
Токенов-инициализаторов у меня два - текстовый (текст между двумя двойными кавычками) и аргументный {...}. Круглые скобки тоже идут в подтип инициализаторов, но отправляют компиллер ковыряться иным алгоритмом.
Короче, мой компиллер нарезает код на фразы, затем бьёт каждую фразу на токены - имя, аллокатор и инициализатор. Если инициализатор - не круглые скобки, то компиллер воспринимает фразу как "создание переменной". Аллоцируется память, имя переменной заносится в отдельный список дополнительных ресурсов для дальнейшей компилляции данного скрипта, память заполняется данными (если инициализатор дан) и т.д.
Если же в выдернутой фразе токен-инициализатор заключён в круглые скобки, значит фраза - это вызов функции. Вызовы функций я компиллирую в скрипт так:
1) Компиллятор ищет имя функции в ресурсах. Если не нашёл - вызов функции не компиллируется. Если нашёл, 4-хбайтный адрес функции (указатель) идёт в карман компиллятору.
2) Сбор аргументов функции. Компиллер шарит внутри круглых скобок и "упрощает" каждый из аргументов функции до 4-хбайтного значения. Если аргумент - число, оно переводится в двоичный формат; если имя - компиллер ищет его в списке ресурсов и кладёт в карман 4-хбайтный указатель, этому имени соответствующий. Если это целая фразуха по созданию переменной - переменная создаётся и адрес на неё идёт компиллеру в карман.
3) Когда все аргументы функции (4-хбайтные значения) собраны, и указатель на функцию тоже есть, компиллятор формирует машинный код вызова функции, который в ассемблере выглядел бы так:
//Запихать в стак все аргументы функции, начиная с конца
push dword ptr ArgN; //Запихать в стак последний аргумент
...
push dword ptr Arg3; //Запихать в стак третий аргумент
push dword ptr Arg2; //Запихать в стак второй аргумент
push dword ptr Arg1; //Запихать в стак первый аргумент
mov ebx, FunctionPointer; //Запихать в регистр ebx адрес функции
call ebx; //Вызвать функцию, адрес которой находится в регистре ebx
Вот так формируется вызов функции формата __stdcall - пихание аргументов в стак в обратном порядке, затем вызов функции. Мой компиллер компиллирует вызовы всех функций подобным образом, составляя буффер функции скрипта, затем ставит в конце:
ret; //Возврат из функции
И возвращает указатель на буффер, где весь этот машинный код находится. Этот указатель можно вызывать, как любую из функций, уже встроенных в ехе-шник. Столько раз, сколько угодно. И работать будет так же быстро, как и если бы это всё было написано при компилляции программы.