some thesis fixes

This commit is contained in:
TopchetoEU 2025-01-29 14:12:57 +02:00
parent 2dcfff689a
commit 45292990b1
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
4 changed files with 87 additions and 26 deletions

View File

@ -20,6 +20,10 @@ emit {
target = "res.html", target = "res.html",
template { template {
template = "template.html", template = "template.html",
year = os.date "%Y",
prev_year = os.date "%Y" - 1,
config,
profession(config.profession),
build { build {
"requirements.md", "requirements.md",
@ -34,12 +38,5 @@ emit {
chapter_format_plain = "Глава %s: ", chapter_format_plain = "Глава %s: ",
}, },
}, },
profession(config.profession),
year = os.date "%Y",
prev_year = os.date "%Y" - 1,
config,
}, },
} }

View File

@ -3,13 +3,21 @@ title: Дипломна работа
--- ---
# Увод {.nonum} # Увод {.nonum}
Един от най-важните принципи на програмирането е този на разширимостта. Този принцип повелява, че софтуерът трябва да изграждан по начин, по който може неговата функционалност да бъде разширена без вече написаният софтуер да бъде променян. Това е добре известен проблем в сферата на софтуерното инженерство, като едно от най-ефективните решения е интегрирането на втори помощен език, чрез който потребителя да може да разширява функционалността на оригиналният продукт. Един от най-важните принципи на програмирането е този на разширимостта. Този принцип повелява, че софтуерът трябва да бъде изграждан по начин, по който може неговата функционалност да бъде разширена, без вече написаният софтуер да бъде променян. Това е добре известен проблем в сферата на софтуерното инженерство.
Но защо разширимостта е толкова важна? Това се дължи на факта, че често потребителите на софтуерните продукти искат да разширят функционалността на продукта. Традиционно, разширяването може да стане само като потребителя се свърже с разработчика на продукта и да помоли за добавянето на функционалността. Това обаче е не само непрактично, но и води до прекомерно нарастване на софтуера, което го прави сложен за поддръжка и използване. Но защо разширимостта е толкова важна? Често потребителите на софтуерните продукти искат да разширят и персонализират функционалността на продукта. Традиционно, разширяването става само чрез модификация на кода на софтуера. Това обаче е не само непрактично, но и води до прекомерно нарастване на обема на софтуера, което го прави сложен за поддръжка. Едно добро решение на този проблем е внедряването на втори помощен език, който да служи за допълването на функционалността на продукта, без неговата промяна.
Въвеждането на скриптови езици като помощни механизми за разширяване на функционалността предлага значително по-ефективно решение. Езиците като Python, Lua и JavaScript могат да бъдат използвани за добавяне на нова функционалност, без да се променя основният код на приложението, като по този начин се осигурява гъвкавост на софтуерния продукт. JavaScript, в частност, е един от най-широко използваните езици в съвременната разработка, поради своята популярност, която се дължи на употребата на езика в уеб технологиите. Езици като Python, Lua и JavaScript са едни от най-често вгражданите езици, като има безброй такива примери:
Целта на тази дипломна работа е да създаде лесно вградим, малък на размер интерпретатор на „EcmaScript 5.1“ езика (по-добре познат като JavaScript, както ще се и нарича оттук нататък) за продукти, написани на езикът „Java“. - Roblox, платформа за създаване на игри, внедрява модифицирана версия на Lua
- Nginx, софтуер за интернет сървъри, внедрява JavaScript
- WirePlumber, софтуер за контролиране на аудиото на линукс машини, използва Lua като основен език за конфигурация
- Vim, конзолен текстов редактор, внедрява Lua като език за разширения
- Blender, софтуер за създаване на 3D модели, внедрява Python като скриптов език, както и език за разширения
JavaScript, в частност, е език, който първоначално е бил използван само за уеб приложения. Понеже обаче езикът е станал изключително популярен, е започнал да се ползва и извън уеб приложенията. Езикът има изключително богата екосистема от инструменти за разработка и библиотеки, заради което е добър избор за език, който да бъде внедрен в даден софтуерен продукт.
Заради това, тази дипломна работа цели да създаде лесно вградим и малък на размер интерпретатор на „EcmaScript 5.1“ езика (по-добре познат като JavaScript) за продукти, написани на езикът „Java“. Интерпретатора трябва да изпълнява бързо и правилно JavaScript код от голям размер и да бъде удобен за внедряване във вече съществуващи продукти.
# Проучвателна част # Проучвателна част
@ -325,6 +333,18 @@ tasks {
Методи, съответстващи на събития в интерпретатора Методи, съответстващи на събития в интерпретатора
::: :::
### Събитиен цикъл
Това е основен механизъм за поддръжката на привидно многонижково програмиране само чрез една нижка. В основата на събитийния цикъл стои опашка от „съобщения“, като всяко съобщение представлява команда, която събитийният цикъл трябва да изпълни. Командите се подреждат на тази опашка, като първите, които са постъпили в опашката биват изпълнени.
Събитийния цикъл гарантира, че съобщенията, който постъпват във събитийния цикъл, ще бъдат изпълнявани последователно. Това освобождава програмиста от нуждата да създава код, който се грижи за синхронизацията между две ядра.
Комуникацията между два събитийни цикъла може да бъде осъществена като първият цикъл изпраща съобщение на втория събитиен цикъл, който евентуално обработва това съобщение. Това съобщение пък може да направи дадени изчисление и да върне резултата чрез второ съобщение, което се праща на първия събитиен цикъл. На практика, приложението имплементира два събитийни цикъла - този, който изпълнява интерпретирания код и този, който приема резултатите от събитийния цикъл.
Друг модел на комуникация е да се изпрати съобщение от текущата нижка на друга нижка, която изпълнява съобщенията, като текущата нижка трябва да изчака изпълнението на съобщението. При този метод употребата на събитиен цикъл се обезмисля до известна степен, понеже се елиминира всякакво паралелно изпълнение на интерпретирания код и основното приложение.
В кода, интерфейса, който е основен за всички събитиини цикли е `EventLoop`, а неговата основна имплементация е `Engine`. `EventLoop` предоставя само един метод - `pushMsg`, на който се подава функционалния интерфейс `Runnable`, който се очаква да бъде изпълнен евентуално. `Engine` е имплементацията, която използва „блокираща“ опашка (опашка, при която присъства операцията за изчакване на добавяне на елемент).
---- ----
## Имплементация на JavaScript стойностите ## Имплементация на JavaScript стойностите
@ -350,8 +370,6 @@ tasks {
В кода и двете стойности са имплементирани с класа `VoidValue`, който може да бъде използван за да бъдат създадени и други празни стойности. В кода и двете стойности са имплементирани с класа `VoidValue`, който може да бъде използван за да бъдат създадени и други празни стойности.
----
### Елементарни стойности ### Елементарни стойности
В JavaScript елементарните стойности са всички стойности, които не са обекти. Такива стойности не могат да съдържат референция към друга JavaScript стойност. Такива стойности биват: В JavaScript елементарните стойности са всички стойности, които не са обекти. Такива стойности не могат да съдържат референция към друга JavaScript стойност. Такива стойности биват:
@ -380,6 +398,10 @@ tasks {
В кода, целите числа са имплементирани в класа `IntValue`, а числата с плаваща запетая - класа `DoubleValue`. Тези класове обаче не могат да бъдат инстанцирани ръчно - трябва да бъде използван метода `NumberValue.of`, който създава една от двете инстанции, в зависимост от това дали подаденото число може да бъде представено с 32-битово число. В кода, целите числа са имплементирани в класа `IntValue`, а числата с плаваща запетая - класа `DoubleValue`. Тези класове обаче не могат да бъдат инстанцирани ръчно - трябва да бъде използван метода `NumberValue.of`, който създава една от двете инстанции, в зависимост от това дали подаденото число може да бъде представено с 32-битово число.
### Потребителски стойности
Това са служебен тип стойности, които съдържат само една Java стойност. Те се използват за подаване на Java стойности на JavaScript кода, за да бъдат използвани по-късно от Java кода. Потребителските стойности се имплементират от класа „UserValue“, като той съдържа само две полета: съдържаната Java стойност, както и прототипа, който стойността да използва.
### Обекти ### Обекти
Обектите са основополагаща концепция в JavaScript. В основата на обектите стои списък от уникални низове (т. нар. ключове) и съответстващият им член [@членове] (съществува и паралелен списък със списък от ключове-символи към членове). Обектите се използват най-вече за съхранение на няколко подстойности под формата на структурирани данни (подобно на класовете на Java), но могат да бъдат използвани и като хеш-таблици. Обектите са основополагаща концепция в JavaScript. В основата на обектите стои списък от уникални низове (т. нар. ключове) и съответстващият им член [@членове] (съществува и паралелен списък със списък от ключове-символи към членове). Обектите се използват най-вече за съхранение на няколко подстойности под формата на структурирани данни (подобно на класовете на Java), но могат да бъдат използвани и като хеш-таблици.
@ -445,7 +467,7 @@ console.log(arr); // [5, 6, <empty x 2>, 8, <empty x 7> 3]
- Промяната на една от тези настройки, освен ако презаписваемо, но ненастройваемо поле се направи непрезаписваемо - Промяната на една от тези настройки, освен ако презаписваемо, но ненастройваемо поле се направи непрезаписваемо
- Изтриване на полето от обекта - Изтриване на полето от обекта
Членовете са дефинирани чрез интерфейса „Member“, полетата чрез класа „FieldMember“, а свойствата чрез „PropertyMember“. Понеже полетата могат да бъдат виртуални и контролирани от Java код, е осигурена базова имплементация за съвсем прости полета, които са само контейнер за JavaScript стойност, в класа „SimpleFieldMember“, докато по-сложна логика може да създаде собствена имплементация на „FieldMember“ класа (на този механизъм са базирани масивите). Членовете са дефинирани чрез интерфейса „Member“, полетата чрез класа „FieldMember“, а свойствата чрез „PropertyMember“. Понеже полетата могат да бъдат виртуални (като тези на масивите), е „FieldMember“ е абстрактен, а „SimpleFieldMember“ е имплементацията на нормалните полета.
---- ----
@ -1026,7 +1048,7 @@ $ for (const el of myGen(0, 30, 10)) print(el);
20i 20i
undefined undefined
``` ```
Употреба на по-нов синтаксиси, с помощта на Babel Употреба на по-нови синтаксиси, с помощта на Babel
::: :::
---- ----
@ -1055,6 +1077,8 @@ StdLib.addGlobals(env);
env.add(Compiler.KEY, Compilers.jsCompiler()); env.add(Compiler.KEY, Compilers.jsCompiler());
``` ```
Важно е да се отбележе, че събитийния цикъл работи в отделна нижка по подразбиране. Не е нужно обаче да бъде стартиран събитийния цикъл, а може да бъде повиквана само неговия метод `Engine.run(true)`, като `true` в това повикване означава „изпълнение на събитийния цикъл докато опашката от съобщения приключи“.
Ако искате да регистрирате транспилатори, може да използвате следния код (имайте в предвид, че тези функции създават нова среда и нова стандартна библиотека за всеки транспилатор, за да бъдат изолирани от останалия код.): Ако искате да регистрирате транспилатори, може да използвате следния код (имайте в предвид, че тези функции създават нова среда и нова стандартна библиотека за всеки транспилатор, за да бъдат изолирани от останалия код.):
```java ```java
@ -1081,7 +1105,7 @@ engine.pushMsg(false, env, new Filename("test", "test"), "console.log(10 + 5, \"
Работата с JavaScript обекти е лесна, трябва да бъдат използвани само описаните в [@имплементация-на-javascript-стойностите] методи: Работата с JavaScript обекти е лесна, трябва да бъдат използвани само описаните в [@имплементация-на-javascript-стойностите] методи:
```java ```java
ArrayValue doInterestingStuff(Environment env, Value input) { ArrayValue doStuff(Environment env, Value input) {
var obj = new ObjectValue(); var obj = new ObjectValue();
obj.defineOwnField(env, "test", NumberValue.of(10)); obj.defineOwnField(env, "test", NumberValue.of(10));
obj.defineOwnField(env, "a", StringValue.of("fsadf")); obj.defineOwnField(env, "a", StringValue.of("fsadf"));
@ -1095,7 +1119,7 @@ ArrayValue doInterestingStuff(Environment env, Value input) {
} }
``` ```
Често ще Ви се налага да създавате и Java функции (трябва да правите такива функции с повишено внимание, понеже те имат неограничен достъп до Java средата на изпълнение, което може да означава че зле написана Java функция може да компрометира сигурността). Следния код е пример как се създава Java функция: Често ще Ви се налага да създавате и Java функции, за да давате достъп на JavaScript до Вашият софтуер. Подобни функции обаче трябва да бъдат създавани с повишено внимание, понеже могат да бъдат използвани и злонамерено. Подобни функции се създават по следния начин:
```java ```java
var func = new NativeFunction("myFunc", args -> { var func = new NativeFunction("myFunc", args -> {
@ -1106,6 +1130,18 @@ var func = new NativeFunction("myFunc", args -> {
}); });
``` ```
`args` аргумента е от типа `Argument`. Това е служебен клас, който служи за улеснен достъп до аргументите, подадени на функцията. Той има следните основни методи и полета:
- self - „this“ аргумента
- args - масив от подадените аргументи
- env - средата, подадена при извикването
- isNew - „true“, ако е извикана в режим на конструкция (в който случай „self“ е „target“ функцията)
- setTargetProto(obj) - в контекста на конструкция, прилака прототипа на „target“ функцията на подадения обект
- n() - връща броя на аргументите
- has(i) - проверка дали този аргумент е бил подаден
- self(clazz) - ако „this“ аргумента е „UserValue“, превръща съдържаната стойност в дадения клас, иначе връща „null“
- get(i) - връща съответния на i аргумент, или „null“, ако индекса е извън обхвата на масива
# Заключение {.nonum} # Заключение {.nonum}
В настоящата дипломна работа е демонстриран интерпретатора на JavaScript, написан на Java. За да бъде създаден, беше направено проучване на съществуващите технологии, както и методите на интерпретиране. Беше отделено и голямо внимание на добрата и проста архитектура на проекта. Архитектурата се развиваше заедно с кода, докато достигне до днешната си форма, като не стана заплетена и ненужно сложна. Интерпретатора беше тестван най-вече ръчно чрез сравнение с държанието на V8 при същия код, както и чрез трите транспилатора „Babel“, „TypeScript“ и „CoffeeScript“. В настоящата дипломна работа е демонстриран интерпретатора на JavaScript, написан на Java. За да бъде създаден, беше направено проучване на съществуващите технологии, както и методите на интерпретиране. Беше отделено и голямо внимание на добрата и проста архитектура на проекта. Архитектурата се развиваше заедно с кода, докато достигне до днешната си форма, като не стана заплетена и ненужно сложна. Интерпретатора беше тестван най-вече ръчно чрез сравнение с държанието на V8 при същия код, както и чрез трите транспилатора „Babel“, „TypeScript“ и „CoffeeScript“.
@ -1203,6 +1239,22 @@ else return end.chainError(src.loc(i + n), "Expected end of statement");
Това е втората част от „lib“ компонента. Той използва `DebugHandler` механизма, за да прихване различните събития на изпълнението на кода. Специалното на дебъгера е, че имплементира V8 протокола за дебъгване [@refs-v8-debug-protocol], което означава, че дебъгването на програма, изпълнявана в този интерпретатор, е възможно в инструментите за разработчици на Chrome, както и във Visual Studio Code. Това е втората част от „lib“ компонента. Той използва `DebugHandler` механизма, за да прихване различните събития на изпълнението на кода. Специалното на дебъгера е, че имплементира V8 протокола за дебъгване [@refs-v8-debug-protocol], което означава, че дебъгването на програма, изпълнявана в този интерпретатор, е възможно в инструментите за разработчици на Chrome, както и във Visual Studio Code.
За да бъде използван дебъгера, трябва бъде създаден „DebugServer“, който имплементира прост HTTP и WebSocket сървър, който да комуникира с V8 дебъг клиента. След това, трябва да бъде създаден регистратор на дебъгера при осъществяването на връзка със сървъра. За целта може да бъде използван вградения „SimpleDebugger“. Кода, който може да използвате за целта е следния:
```java
var handler = new SimpleDebugHandler();
env.add(DebugHandler.KEY, handler);
var server = new DebugServer();
var debugTask = server.start(new InetSocketAddress("127.0.0.1", 9229), true);
server.targets.put("default", (socket, req) -> {
var debugger = new SimpleDebugger(socket);
debugger.attach(handler);
});
```
След това, за да бъде използван дебъгера може да се използва или Chrome (или всеки браузър, базиран на Chromium), или VSCode, като Chrome автоматично ще засече присъствието на Debug сървър и ще предложи стартирането на дебъг клиент чрез зелена икона на NodeJS, която ще се покаже в горния ляв ъгъл на опциите на разработчиците (достъпно чрез F12).
![Пример за дебъгване на програма от интерпретатора](./img/debugging-example.png "debug") ![Пример за дебъгване на програма от интерпретатора](./img/debugging-example.png "debug")
# Използвани термини и чуждици {.nonum} # Използвани термини и чуждици {.nonum}

View File

@ -315,7 +315,7 @@ function converters.Image(data, ctx)
return text, title and { title } or alt_plain or url or "[picture]"; return text, title and { title } or alt_plain or url or "[picture]";
end end
function converters.Figure(data, ctx) function converters.Figure(data, ctx)
local chapter_i = #(ctx.headers or {}); local chapter_i = count_headers(ctx.headers or {});
ctx.figures_indices = ctx.figures_indices or {}; ctx.figures_indices = ctx.figures_indices or {};
ctx.figures_indices[chapter_i] = (ctx.figures_indices[chapter_i] or 0) + 1; ctx.figures_indices[chapter_i] = (ctx.figures_indices[chapter_i] or 0) + 1;
local figure_i = ctx.figures_indices[chapter_i]; local figure_i = ctx.figures_indices[chapter_i];

View File

@ -183,9 +183,12 @@
flex-direction: row; flex-direction: row;
width: 100%; width: 100%;
gap: 2em; gap: 2em;
font-size: 1.25em;
text-align: right;
margin-bottom: 1em;
} }
.school-img { .school-img {
height: 5em; height: 6em;
width: unset; width: unset;
} }
@ -221,6 +224,9 @@
text-align: right; */ text-align: right; */
} }
.asm-title { .asm-title {
display: flex;
flex-direction: column;
gap: 1em;
text-align: center; text-align: center;
} }
.asm-content { .asm-content {
@ -477,6 +483,11 @@
// console.log(target, name, i); // console.log(target, name, i);
} }
} }
for (let i = 0; i < 3; i++) {
pages[i].getElementsByClassName("pagedjs_margin-bottom-right-corner-holder")[0].innerText = "";
console.log(pages[i].getElementsByClassName("pagedjs_margin-bottom-right-corner-holder")[0]);
}
}; };
</script> </script>
</head> </head>
@ -490,10 +501,10 @@
<div class="title-content"> <div class="title-content">
<h2>ДИПЛОМНА РАБОТА</h2> <h2>ДИПЛОМНА РАБОТА</h2>
<h4>по професия код {{profession}}</h4> <div>по професия код {{profession}}</div>
<h4>специалност код {{specialty}}</h4> <div>специалност код {{specialty}}</div>
<div>Тема: {{topic}}</div>
</div> </div>
<div class="title-content">Тема: {{topic}}</div>
<div class="title-authors"> <div class="title-authors">
<div class="title-author"> <div class="title-author">
<div class="author-type">Дипломант:</div> <div class="author-type">Дипломант:</div>
@ -524,11 +535,12 @@
</div> </div>
<div class="asm-content"> <div class="asm-content">
<div class="asm-title"> <div class="asm-title">
<h2>ЗАДАНИЕ</h2> <h2>ЗАДАНИЕ<br/>за дипломна работа</h2>
<h2>за дипломна работа</h2>
<h4>ДЪРЖАВЕН ИЗПИТ ЗА ПРИДОБИВАНЕ НА ТРЕТА СТЕПЕН НА ПРОФЕСИОНАЛНА КВАЛИФИКАЦИЯ</h4> <h4>ДЪРЖАВЕН ИЗПИТ ЗА ПРИДОБИВАНЕ НА ТРЕТА СТЕПЕН НА ПРОФЕСИОНАЛНА КВАЛИФИКАЦИЯ</h4>
<h4>по професия код {{profession}}</h4> <div>
<h4>специалност код {{specialty}}</h4> <div>по професия код {{profession}}</div>
<div>специалност код {{specialty}}</div>
</div>
</div> </div>
<div class="asm-requirements"> <div class="asm-requirements">
на ученика {{author}} от {{class}} клас<br/> на ученика {{author}} от {{class}} клас<br/>