Не ми харесва OOP Направете свой собствен език за програмиране, Дмитрий Хектерук

Блог за програмиране - C #, F #, C ++, Архитектура и др

В интернет е модерно да се казва, че „ООП е шлака“ и мнозина мечтаят да създадат свой собствен език за програмиране, но се страхуват от нещо. Но всъщност, ако подходите разумно, всичко е просто. Сериозно! Знам, че искате да спорите с мен, че те казват ...

Писането на собствен компилатор в собствен код/​​IL/байт код е твърде трудно - но недей! Давате ли си сметка, че съществуващите компилатори като C или Java са били усъвършенствани през годините от стотици хора? Защо не се възползвате от цялото това богатство, като компилирате езика си в един и същ C/C ++ и след това вземете всички оптимизации и екстри от компилатора C/C ++?

Идеята да се забърквам с персонализирани файлови формати за лексиране и синтактичен анализ не ми бърза, а депресира - но недей! Просто вземете рамка, която поддържа изграждането на парсер точно във вашия код.

Вече имам написан целия код в Java/C #/C ++, как ще направя взаимодействие? - това е много просто, тъй като с подхода за транскомпилация можете да преведете вашия език в който и да е от горните, генерирайки и след това консумирайки всеки интерфейс и в двете посоки.

Ще направя езика, но какво ще кажете за езиковата поддръжка в любимата ми IDE? - о, добре, това е най-високият полет. Като начало, опитайте се да направите лаконичен малък език, за който инструментите не са критични. И тогава можете да научите и поддържате инструменти (или да напишете свои собствени).

За да покажем колко е просто, ето един бърз пример: Да предположим, че искате да разширите дефиницията на С-образна глобална функция, като добавите следните характеристики:

Определете вашия собствен набор от "къси" типове променливи, като i32 или f64, за да замените int и double .

Предайте аргументи във формат x, y: i32, т.е. повторно използване на дефиницията на типа за няколко наведнъж.

Добавете дефиниции на променливи като x = 5 към тялото на функцията, така че при условие, че x не е име на параметър, да се превърне в пълноценна декларация на променлива, в противен случай стойността просто се присвоява.

За начало подходяща ли е такава функция? Знам това малко, но не се опитвам да направя цял език тук. Ето как ще изглежда:

Структури за езика

Всички езици имат механизми за изграждане на парсери. Ще взема C ++ и Boost.Spirit като примери, но тук езикът всъщност няма значение. Първо, нека направим нови типове като f32 вместо float:

Сега дефинираме функцията:

Всяка функция има име, отнема редица параметри (добре, декларации за параметри, но краткостта е сестра на таланта) и тя има тяло, което в нашия случай ще се състои единствено от присвояване на стойности на променливи (не много практичен, знам).

Тъй като знаем как да дефинираме аргументи в стила x, y: i32, т.е. няколко с един тип, ще определим действителните структури на параметри по следния начин:

И накрая, присвояването на стойности на променливи може да се направи по следния начин:

Горното показва кода за определяне на типа, мисля, че разбирате, че по-късно това може да бъде поправено.

Всичко, структурите са готови, можете да изградите парсер. (Всъщност все още има етап за тяхната адаптация чрез Boost.Fusion, но това е подробност, специфична за изпълнението, ако погледнете източника.)

Толкова е лесно да напишете парсер за нашия език, че просто ще дам целия код изцяло и след това ще го обсъдим, добре?

Като начало тези правила qi: просто казват на анализатора, че това, което анализира, попада върху структурите, дефинирани по-рано. Например, ако искате да анализирате задание като x = 3, какво е това? Това е идентификатор (т.е. 1 или повече буквено-цифрови знаци), след това =, след това отново набор от знаци и в края; .

По-конкретно в Boost.Sprit, за разлика от обикновените, „един или повече“ се пише като + преди типа на символа, т.е. + алнум. Тоест, + означава "един или повече", * - "колкото искате" и т.н. Така се оказва, че сме анализирали заданието и тъй като нашето правило qi: го преобразува в assignment_statement, полетата на тази структура ще бъдат присвоени автоматично. Блестящо ли е, или как?

Същото е и с другите части на езика. Искате ли да анализирате няколко променливи, разделени със запетаи, и да ги натъпчете във вектор? Пишем + alnum% ',' където% операторът е как да кажа * (+ alnum >> ','), само по-кратко. Което също е удобно.

И така, имаме готов парсер, можете да го анализирате. На Spirit това се прави по следния начин:

където render () е функция, която заобикаля това, което сме анализирали, и генерира от това най-чистия, готов за компилация C (никой не ви притеснява да извеждате наведнъж на N различни езика).

Доста щампа

Накратко, анализирахме функцията, получихме OOP структури от нея и сега можем да генерираме нещо от тях. За да направите това, трябва да ги заобиколите, което не е трудно в нашия конкретен случай. Първо пишем името на функцията:

След това показваме параметрите, като не забравяме, че можем да имаме няколко имена за един и същи тип аргумент:

И тогава, спретнато, задания. Не забравяйте, че типът трябва да бъде регистриран само ако променливата не се появява някъде във функционалните параметри:

Това е всичко. Тук по принцип можете да внедрите "пълен" посетител, ако искате.

Заключение

Както разбирате, все още има стъпка за компилиране на получения код - мисля, че е толкова очевидно за всички как да се направи това, зависи от езика, който сте генерирали. Като цяло се застъпвам за „преносимия“ C/C ++, но в крайна сметка зависи от вас да решите.

Ако искате сортовете на проекта, те са тук. Моят пример е в C ++, но можете да внедрите своя език във всичко. Моралът е, че е лесно да създадете свой собствен компилиран език сега, така че вместо да хленчите за ООП и да се биете с вятърни мелници, е по-лесно да седнете и да смилате нещо свое. Така че седнете и напишете спецификацията на вашия чудо език Юдо. Късмет!