NewLang Project
Yet another programm language
Loading...
Searching...
No Matches
module_test.cpp
Go to the documentation of this file.
1//#ifdef BUILD_UNITTEST
2//
3//#include "warning_push.h"
4//#include <gtest/gtest.h>
5//#include "warning_pop.h"
6//
7//#include "builtin.h"
8//#include "runtime.h"
9//#include "module.h"
10//#include "jit.h"
11//
12//using namespace newlang;
13//
15// *
16// * Дектораторы имен ($ и @) импользуютсядля указания области видимости и времени жизни объектов.
17// * $ - локальные объекты, размещаемые "условно" на стеке. Удаляются по мере выхода из текущей области видимости.
18// * Локальный объект скрывает другие локальные и глобальные объекты с таким же именем.
19// * @ - глобальные объекты, размещаемые "условно" в куче и их время жизни не зависит от текущей области видимости.
20// * Глобальный объект заменяет другой глобальный объект с таким же именем.
21// *
22// * Namespace позволяют использовать одинаковые имена объектов разделяя для них области видимости,
23// * но можно ли совместить и использовтаь namespace и дектораторы одновременно?
24// *
25// * $local; @global;
26// * $name::local; \\name::global;
27// * но ns {
28// * $name::local; # Будет $ns::name::local
29// * \\name::global; # Все равно \\name::global или тоже \\ns::name::global ????
30// * }
31// * тогда без указания области видимости (без начального символа "::", т.е. name::var - объект выбирается автоматически,
32// * т.е. сперва ищется локальный объект с таким именем, потом глобальный)
33// * Какая разница между ::name::var и name::var?
34// * Первый в глобальной области видимости, второй в текущей?
35// *
36// * $var := 0; # ::var или $var
37// * @func() := {0}; # ::func или @func - 0
38// * name {
39// * $var := 1; # ::name::var
40// * $func() := {11}; # локальная с именем - ::name::func
41// * @func() := {111}; # глобальная с именем - @func
42// * \\name::func() := {1111}; # глобальная \\name::func и тоже самое, что и локальная $func() !!!
43// * name {
44// * var := 2; # ::name::name::var или $var или \\name::name::var
45// * @var := 2; # ::var
46// * \\name::name::var := 2; # ::name::name::var или \\name::name::var
47// * func() := {2}; # ::name::name::func или \\name::name::func - 2
48// * name3 {
49// * var := 3; # ::name::name::name3::var или $name::name::name3::var - 3
50// * func() := {3}; # ::name::name::name3::func или \\name::name::name3::func() - 3
51// *
52// * var??; # name::name::name3::var или $name::name::name3::var - 3
53// * name::var??; # name::name::var или \\name::name::var - 2
54// * name::name::var??; # name::var или \\name::name::var - 2
55// * ::name::var??; # name::var или $name::var - 1
56// * ::var??; # ::var или $var - 0
57// * }
58// * }
59// * }
60// * Предположим, что: <<<<<<<<<<<<<<< НЕ АКТУАЛЬНО после изменения ситаксиса мароксов !!!!!!!!!!!!!!!!!!!!!!!!!! >>>>>>>>>>>
61// * :: - глобальный корень. Может быть заменен на @ или $
62// * @@ — родительская функция после её переопределния
63// * $$ — родительский объект
64// * тогда:
65// * $$.field - поле родительского класса
66// * @@() - вызов переопределенной функции текущего класса или области видимости
67// * @func() []= {@@() + 10}; # @func - 10
68// * ::name::name@func
69// * \\name::name::func - такая запись ненравится, но будет самой правильной и логичной, так как все остальные еще хуже для понимания и разбора
70// * @::name::name::func
71// * @::name::name@func
72// * ::name::name::func@@
73// *
74// * <<<<<<<<<<<<<<<<<< НОВАЯ КОНЦЕПЦИЯ >>>>>>>>>>>>>>>>>>
75// *
76// * Все объекты физически размещаюится в куче, но время их жизни определяется областью видимости.
77// * Объекты с декораторами $ обозначают локальные объекты (аналог "на стеке") и они удаляются по мере выхода из текущей области видимости.
78// * Объекты с декораторами @ являются модулями или его объектами, доступны из других частй программы и сохраняют состояние при выходе из текущей области видимости.
79// *
80// * Указание модуля (имя файла) @file или @dir.file или @dir1.dir2.file
81// * Указание объекта в модуле @file::var или @dir.file::ns::func() или @dir.file::ns::obj.field; @file::ns:type @file::ns$local ???
82// *
83// * Файл модуля загружается однократно при первом обращении и не выгружается до явной команды???
84// * @module <*>;
85// * @dir.module2 <*>; # Импорт всех функций модуля (кроме начинающихся на подчерк)
86// * @dir.module2 <ns::name::*>; # Импорт всех функций из указанной области имен (кроме начинающихся на подчерк)
87// * @dir.module2 <map=ns::name::*>; # Импорт всех функций из указанной области имен с переименованием (кроме начинающихся на подчерк)
88// * @dir.module2 <func1, func2=::module2::ns::name::func2>; # Импорт только конкретных функций + переименование
89// * @dir.module2 <_>; # Выгрузить модуль?????
90// *
91// * \\ns ::name::space::long@\
92// * \ns::name;
93// *
94// * @dsl{}; # Загрузка модуля с определниями макросов для DSL в самом начале любой программы?
95// *
96// * @@ - главный модуль
97// * @$ - текущий модуль
98// * $$ - родительский объект (базовый объект или переопределяемая функция)
99// * $0 - текущий объект (this)
100// *
101// * [@$.__main__] --> { # 1 или 0
102// *
103// * }
104// * @$.__@@__ := {}; # Деструктор модуля
105// * __@@__ := {}; # Деструктор модуля
106// *
107// * \destructor() { // _____ ::=
108// * }
109// *
110// *
111// * Дектораторы имен ($ и @) импользуютсядля указания области видимости и времени жизни объектов.
112// * $ - локальные объекты, размещаемые "условно" на стеке. Удаляются по мере выхода из текущей области видимости.
113// * Локальный объект скрывает другие локальные и глобальные объекты с таким же именем.
114// * @ - глобальные объекты, размещаемые "условно" в куче и их время жизни не зависит от текущей области видимости.
115// * Глобальный объект заменяет другой глобальный объект с таким же именем.
116// *
117// * Namespace позволяют использовать одинаковые имена объектов разделяя для них области видимости,
118// * но можно ли совместить и использовтаь namespace и дектораторы одновременно?
119// *
120// *
121// * Наследование
122// * Часто композиция класса более подходяща, чем наследование. Когда используйте наследование, делайте его открытым (public).
123// * Когда дочерний класс наследуется от базового, он включает определения всех данных и операций от базового. "Наследование интерфейса" - это наследование от чистого абстрактного базового класса (в нём не определены состояние или методы). Всё остальное - это "наследование реализации".
124// * Наследование реализации уменьшает размер кода благодаря повторному использованию частей базового класса (который становится частью нового класса). Т.к. наследование является декларацией времени компиляции, это позволяет компилятору понимать структуру и находить ошибки. Наследование интерфейса может быть использовано чтобы класс поддерживал требуемый API. И также, компилятор может находить ошибки, если класс не определяет требуемый метод наследуемого API.
125// * В случае наследования реализации, код начинает размазываться между базовым и дочерним классом и это может усложнить понимание кода. Также, дочерний класс не может переопределять код невиртуальных функций (не может менять их реализацию).
126// * Множественное наследование ещё более проблемное, а также иногда приводит к уменьшению производительности. Часто просадка производительности при переходе от одиночного наследования к множественному может быть больше, чем переход от обычных функций к виртуальным. Также от множественного наследования один шаг до ромбического, а это уже ведёт к неопределённости, путанице и, конечно же, багам.
127// * Любое наследование должно быть открытым (public). Если хочется сделать закрытое (private), то лучше добавить новый член с экземпляром базового класса.
128// * Не злоупотребляйте наследованием реализации. Композиция классов часто более предпочтительна. Попытайтесь ограничить использование наследования семантикой "Является": Bar можно наследовать от Foo, если можно сказать, что Bar "Является" Foo (т.е. там, где используется Foo, можно также использовать и Bar).
129// * Защищёнными (protected) делайте лишь те функции, которые должны быть доступны для дочерних классов. Заметьте, что данные должны быть закрытыми (private).
130// * Явно декларируйте переопределение виртуальных функций/деструктора с помошью спецификаторов: либо override, либо (если требуется) final. Не используйте спецификатор virtual при переопределении функций. Объяснение: функция или деструктор, помеченные override или final, но не являющиеся виртуальными просто не скомпилируются (что помогает обнаружить общие ошибки). Также спецификаторы работают как документация; а если спецификаторов нет, то программист будет вынужден проверить всю иерархию, чтобы уточнить виртуальность функции.
131// * Множественное наследование <интерфейсов> допустимо, однако множественное наследование реализации не рекомендуется от слова совсем.
132// *
133// * Для замены множественного наследования можно исопльзовать оператор утиной типизации (сравнения интерфейсов)
134// *
135// * Конструкторы и деструкторы классов
136// *
137// * Конструктор у класса может быть только один - и это обычная функция с именем класса.
138// * Конструктор должен вернуть созданный экземпляр класса, который ему передается в $0 аргументе.
139// * Другие аргументы соотетствуют передаваемым при вызове конструктора.
140// * Конструктор по умолчанию принимает произвольное количество аргументов (для переопределения значения у
141// * свойств по время вызова конструтокра) и возвращает аргумент $0, т.е. constructor(...) :- {$0($*)};
142// * Пользователський конструтор лучше переопреелять в теле класса через оператор присвоения значения,
143// * аналог override, т.е. constructor(...) **=** {...}; (чтобы исключить ошибки в написании имени функции).
144// * Конструторы базовых классов автоматически не вызываются, и если это требуется по логике работы,
145// * то вызовы конструторов базовых классов необходимо выполнять вручную, например так: $0 = $$.base_constructor();
146// *
147// * Деструктор класса определяется в теле класса анонимной функицей (имя функци подчерк),
148// * т.е. _() :- {} и по умолчанию отсутствует. Деструткоры базовых классов вызываются автоматически?????
149// *
150// *
151// */
152//
153//llvm::orc::ThreadSafeModule createDemoModule();
154//
155//TEST(Module, CreateArc) {
156//
157// if (!std::filesystem::exists(std::filesystem::path("temp"))) {
158// std::filesystem::create_directories(std::filesystem::path("temp"));
159// }
160// ASSERT_TRUE(std::filesystem::exists(std::filesystem::path("temp")));
161// ASSERT_TRUE(std::filesystem::is_directory(std::filesystem::path("temp")));
162//
163//
164// // str = AstAnalysis::MakeInclude(Parser::ParseString("::val := 1;"));
165// // ASSERT_STREQ("::val := ...;\n", str.c_str());
166// //
167// // str = AstAnalysis::MakeInclude(Parser::ParseString("::val := 1; val2 := 1; @::val3 := 1"));
168// // ASSERT_STREQ("::val := ...;\n@::val3 := ...;\n", str.c_str());
169// //
170// // str = AstAnalysis::MakeInclude(Parser::ParseString("::val, val2, @::val3 := 1"));
171// // ASSERT_STREQ("::val := ...;\n@::val3 := ...;\n", str.c_str());
172// //
173// // str = AstAnalysis::MakeInclude(Parser::ParseString("@@ macro @@ := 1; @@ macro2 @@ := @@ 2 @@; @@@@ macro @@@@;"));
174// // ASSERT_STREQ("@@ macro @@ := 1;\n@@ macro2 @@ := @@ 2 @@;\n@@@@ macro @@@@;\n", str.c_str());
175//
176// std::string source = "::val := 1; val2 := 1; @::val3 := 1";
177// TermPtr ast = Parser::ParseString(source);
178// ASSERT_TRUE(ast);
179//
180// RuntimePtr rt = RunTime::Init();
181//
182// llvm::orc::ThreadSafeModule mod = createDemoModule();
183//
184// std::filesystem::remove("temp/test-other.nlm");
185// std::filesystem::remove("temp/test-module.nlm");
186//
187// FileModule module;
188//
189// JIT::ModuleCreate(module, "test-module", ast, source, mod.getModuleUnlocked());
190//
191// ASSERT_TRUE(!module.name.empty());
192// ASSERT_STREQ("test-module", module.name.c_str());
193// ASSERT_TRUE(!module.include.empty());
194// ASSERT_TRUE(!module.source.empty());
195// ASSERT_TRUE(!module.bytecode.empty());
196//
197// ASSERT_NO_THROW(
198// ASSERT_TRUE(RunTime::ModuleSave(module, "temp/test-other.nlm", "other"));
199// );
200// ASSERT_TRUE(std::filesystem::exists(std::filesystem::path("temp/test-other.nlm")));
201//
202// ASSERT_NO_THROW(
203// ASSERT_TRUE(RunTime::ModuleSave(module, "temp/test-module.nlm"));
204// );
205// ASSERT_TRUE(std::filesystem::exists(std::filesystem::path("temp/test-module.nlm")));
206//
207// FileModule module2;
208// RunTime::ModuleRead(module2, "temp/test-module.nlm");
209// ASSERT_TRUE(!module2.name.empty());
210// ASSERT_STREQ("test-module", module2.name.c_str());
211// ASSERT_EQ(module.include.size(), module2.include.size());
212// ASSERT_EQ(module.source.size(), module2.source.size());
213// ASSERT_EQ(module.bytecode.size(), module2.bytecode.size());
214// ASSERT_TRUE(0 == memcmp(module.bytecode.data(), module2.bytecode.data(), module2.bytecode.size()));
215//
216// ASSERT_TRUE(0 == memcmp(module.source.data(), module2.source.data(), module2.source.size()));
217// ASSERT_TRUE(0 == memcmp(module.include.data(), module2.include.data(), module2.include.size()));
218// ASSERT_TRUE(0 == memcmp(module.bytecode.data(), module2.bytecode.data(), module2.bytecode.size()));
219//
220//
221// FileModule other;
222// RunTime::ModuleRead(other, "temp/test-other.nlm", "other");
223// ASSERT_TRUE(!other.name.empty());
224// ASSERT_STREQ("other", other.name.c_str());
225// ASSERT_EQ(module.include.size(), other.include.size());
226// ASSERT_EQ(module.source.size(), other.source.size());
227// ASSERT_EQ(module.bytecode.size(), other.bytecode.size());
228//
229// ASSERT_TRUE(0 == memcmp(module.source.data(), other.source.data(), other.source.size()));
230// ASSERT_TRUE(0 == memcmp(module.include.data(), other.include.data(), other.include.size()));
231// ASSERT_TRUE(0 == memcmp(module.bytecode.data(), other.bytecode.data(), other.bytecode.size()));
232//
233//}
458//
459//#endif // UNITTEST