Как написать свой язык программирования на python
Перейти к содержимому

Как написать свой язык программирования на python

  • автор:

Creating your own Programming Language with Python.

Aadit Ambadkar

So you want to create your own Programming Language, but you don’t know where to start? Well here is how I created my own programming language called TEN for Father’s Day.

P.S — You can too!

To get started, you are going to need to make sure you have Python installed. You may also want to update it to Python 3.X. I would recommend 3.9 since it is the latest one as of this article.

To install Python, simply go to www.python.org, open the download section in the navbar, and click the Python 3.9.5 button:

Run the installer and follow the steps that pop up.

Now we need to install some python packages which will allow us to convert our python files to exe files. The steps are taken from this Stack Overflow answer.

Open a new command prompt window, and type in the following command:

Then, type in this next command:

These 2 libraries are needed for converting .py files to .exe files.

Now that that is out of the way, we need to start planning out our custom language. For simplicity, we will be building a stack-based programming language.

A stack-based programming language is one that runs on a stack. First, you input some values, then an operation on those values. For example, to add 2 numbers together, you would type the following code:

number1 adds the first number to the stack, then number2 adds the second number to the stack. So, our stack now contains number1 and number2 . Then, by typing + , we add the 2 numbers to the stack. So, the end result of this is the value of number1+number2 in the stack.

As the name suggests, the entire language runs on a stack, so we need to first define a variable containing our stack. To do so, import the deque (pronounced deck) package from the collections library:

The next step is — Well there isn’t much you can do now, since you haven’t really thought about the syntax of your language. The real next step is to pull yourself away from Python, and open up a black text file. You need to start creating the syntax of your programming language.

Think about what you want it to look like. How will you perform basic operations on numbers? How will you add items to the stack? How would you define strings?

If you thought about it long enough, you should have at least 10 different commands, not including basic number arithmetic. Otherwise, you are basically just building a calculator.

Now that you have the syntax sort of thought out (and trust me, you haven’t fully thought it out yet, because you can bet the syntax will change over the period of time you make this language), we can begin our real work with python.

For clarity, let’s call the language we are going to create as CUSTOM.

We need to open up the file containing our CUSTOM script. But, before we open the file, we need the file name. If you have ever compiled a python script, you know the command you type is python <filename>.py . What we are actually doing is passing in the string <filename.py> to python.exe, and then python.exe is compiling that file. Python actually allows us to compile python files with additional arguments passed in: python <filename>.py arg1 arg2 … . arg1, arg2, … are then passed into our python script, which can use those arguments. To access those arguments, we use sys.argv . sys.argv is a list containing each additional argument. For the time being, we will just have 1 argument passed in, the file name of the CUSTOM script:

Then, we read all the contents of the file to a variable called contents .

I am assuming that your programming language will not have variables, as variable access can be a real pain. You can check out my TEN Github Page (linked above) to see how I managed variable assignment and access.

Now, pretty much all of our variables are done. Now we need to iterate character by character over the code. I made each of my commands a single character so that I could iterate character by character. You could use a string tokenizer, but that’s really not needed. (Plus, making an obfuscated programming language is all the rage)

First, the code which iterates over each character:

Let’s start simple, with checking if the user is defining a string. Suppose the user opens the string with “ , and closes with ‘ . Then we would write the following:

So just some basic things like that make the code much better!

Here is a sample, which only allows users to add strings and print them. Using the same string definition as above, and letting ; be the command to print the top of the stack:

And thats it! The script is done! Now, we just need to turn it into an EXE file. To do that, create another python file, and call it setup.py . Inside the file, paste the following code:

Don’t forget to replace CUSTOM with the name of your python file. Just run setup.py like so:

And that is IT! The file will be saved inside the build folder. Make sure the other folder (lib) and python3.dll, python3X.dll (X is your version) are all in the same directory as the actual exe file.

Now, create a text file with the following:

Run it with the command:

And, “Hello World!” should be outputted! Feel free to add more to the script as you wish. You can see a sample of my work in my GitHub, as mentioned earlier.

Создание языка программирования. Часть 0

Доброго времени суток Уважаемые Хабра пользователи! Не буду долго рассусоливать, расскажу лишь основное что подтолкнуло меня к написанию данной статьи, и к собственно разработке своего языка программирования.

Все дело в том, что я занимаюсь программированием достаточно давно, и знаю несколько языков программирования. И несмотря на их различия, я в любом языке умудряюсь наворотить сложных конструкций (даже в Python мой код иногда настолько закручен, что я сам не понимаю что я курил когда писал его). В связи с тем что мой код полностью противоречит всем канонам правильного кода, мне стало интересно как же компиляторы и интерпретаторы понимают мой кривой код.

В связи с этим, сразу даю ответ на вопросы «Зачем это надо?! Очередной велосипед написать? Заняться что ли нечем?» — делается это с целью удовлетворения интереса, а так же для того что бы такие же интересующиеся как я имели представление о том как это работает.

Теперь собственно к теории языков программирования. Посмотрим что на этот счет всеми любимая Википедия:

С этим все понятно, ничего сложного, все мы знаем что это такое.

О том, что предстоит сделать

1. Лексический анализатор. Модуль который будет проверять правильность лексических конструкций, которые предусмотрены нашим языком программирования.
2. Парсер. Данный модуль будет переводить код понятный человеку в поток токенов, которые в последующем будут исполняться или переводиться в машинный язык.
3. Обычно на этом месте стоит оптимизатор, но так как наша поделка является скорее игрушкой чем крупным проектом, я откажусь от оптимизатора. И теперь наши пути расходятся:
3.1. Транслятор. Данный модуль будет транслировать поток токенов полученных от парсера в машинный код. Данный подход используется в компиляторах
3.2. Исполнитель. Данный модуль выполняет команды записанные в виде потока токенов. Данный подход используется в интерпретаторах.

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

Немного о реализации

1. Для реализации транслятора будет использован язык программирования Python. Почему именно он? Потому что его я знаю лучше всех. К тому же, его типизация, а точнее ее полное отсутствие позволит сократить количество переменных используемых при написании кода.
2. Для реализации виртуальной машины так же будет использован Python.
3. Для сборки проекта будет использован PyInstaller, так как он позволяет упаковывать все в один файл, к тому же на данный момент можно собрать для Linux и Windows без особых заморочек.

Теперь к практике

Предлагаю поставить перед собой минимальную задачу, при выполнении которой мы будем считать задачу условно выполненной и дальше можно не идти. Для этого определимся с минимальным синтаксисом языка:

1. Есть однострочные комментарии, начинаются со знака диеза (#) и продолжаются до конца строки.
2. Есть два типа данных (integer, string).
3. Есть возможность вывода информации на экран.
4. Есть возможность ввода значений с клавиатуры.

Напишем простенькую программу на нашем новом языке, с учетом правил которые мы только что сформулировали:

Вод собственно и все. Простенькая программка, которая демонстрирует возможности только что придуманного языка. На этом я думаю следует закончить.

В следующей части начнем написание своего велосипеда, который сможет выполнить код приведенный выше.

How to create a programming language in Python [closed]

I have seen a lot of tutorials for making a programming language, but very few for writing one in Python. I would like to know how to (relatively easily) create a programming language using Python.

2 Answers 2

Not sure what you mean by «creating a programming language». But I think you might like to read Peter Norvig’s excellent article (How to Write a (Lisp) Interpreter (in Python)). This shows how you can build a Lisp interpreter in only 90 lines of Python!

  1. Imagine your language. What do you want it to look like? What features should it have?
  2. Think of an existing language that is as similar as possible to your desired language. It’s fine if the keywords are all different, but if you decided to make Python you wouldn’t start with Lisp because the structures are fundamentally very different.
  3. Find an existing grammar for the language you chose in step 2. I’d look here: http://www.antlr3.org/grammar/list.html . If you can’t find one, do step 2 again.
  4. Using ANTLR (or whatever parser generator understands the grammar you found in step 3), build a Python module that understands the language you chose in step 2. ANTLR has some level of support for a Python «target» (meaning that the parser code will be in Python, as opposed to making a parser that understands the Python language). If you get stuck with parser code in C (which you may), write Python bindings for it (probably easiest using Boost Python, but you could use the Python C API directly if you are pretty familiar with both C and Python).
  5. Start making modifications (in small steps at first) to the grammar from step 3 to make it more like the language you designed in step 1.

Do these things carefully and deliberately, and after a few days of work you may have a halfway-decent parser for your language. Then you’ll need to consume the output of the parser (if using ANTLR, consider using the Abstract Syntax Trees, or ASTs, that it can generate for you). Then you’ll need to convert the parsed syntax into a target language, such as x86 assembly or some intermediate bytecode such as the one used by Java, Lua, Microsoft .NET, or whatever.

Good luck, and be forewarned: this process will take a long time to do right.

Создаем свой язык программирования с блэкджеком и компилятором

В этом пособии с соответствующими примерами кода рассказываем о том, как написать при помощи Python свой язык программирования и компилятор к нему.

Создаем свой собственный язык программирования и его компилятор

Введение

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

Системные требования

Если вы незнакомы с нижеприведенными понятиями, не беспокойтесь – мы проясним необходимость этих компонентов далее, по ходу создания компилятора. В качестве лексера и парсера используется PLY. В роли низкоуровневого языка-посредника для генерации оптимизированного кода выступает LLVMlite.

Таким образом, к системе предъявляются следующие требования:

  • Среда Anaconda (более простой способ для инсталляции LLVMlite – conda, а не классический pip)
  • LLVMlite
  • RPLY (то же, что PLY, но с более хорошим API)
    (статический компилятор LLVM)

Свой язык программирования: с чего начать?

Начнем с того, что назовем свой язык программирования. В качестве примера он будет называться TOY. Пусть пример программы на языке TOY выглядит следующим образом:

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

Как для этой однострочной программы формально описать грамматику языка? Чтобы это сделать, необходимо использовать расширенную Бэкус – Наурову форму (РБНФ) (англ. Extended Backus–Naur Form ( EBNF )). Это формальная система определения синтаксиса языка. Воплощается она при помощи метаязыка, определяющего в одном документе всевозможные грамматические конструкции. Чтобы в деталях разобраться с тем, как работает РБНФ, прочтите эту публикацию.

Создаем РБНФ (EBNF)

Создадим РБНФ, которая опишет минимальный функционал нашей микропрограммы. Начнем с операции суммирования:

Соответствующая РБНФ будет выглядеть следующим образом:

Дополним язык операцией вычитания:

В соответствующем РБНФ изменится первая строка:

Наконец, опишем функцию print:

В этом случае в РБНФ появится новая строка, описывающая работу с выражениями:

В таком ключе развивается описание грамматики языка. Как же научить компьютер транслироваться эту грамматику в бинарный исполняемый код? Для этого нужен компилятор.

Компилятор

Компилятор – это программа, переводящая текст ЯП на машинный или другие языки. Программы на TOY в этом руководстве будут компилироваться в промежуточное представление LLVM IR (IR – сокращение от Intermediate Representation) и затем в машинный язык.

Использование LLVM позволяет оптимизировать процесс компиляции без изучения самого процесса оптимизации. У LLVM действительно хорошая библиотека для работы с компиляторами.

Наш компилятор можно разделить на три составляющие:

  • лексический анализатор (лексер, англ. lexer)
  • синтаксический анализатор (парсер, англ. parser)
  • генератор кода

Для лексического анализатора и парсера мы будем использовать RPLY, очень близкий к PLY. Это библиотека Python с теми же лексическими и парсинговыми инструментами, но с более качественным API. Для генератора кода будем использовать LLVMLite – библиотеку Python для связывания компонентов LLVM.

https://amdy.su/wp-admin/options-general.php?page=ad-inserter.php#tab-8

1. Лексический анализатор

Итак, первый компонент компилятора – лексический анализатор. Роль этого компонента заключается в том, чтобы разделять текст программы на токены.

Воспользуемся последней структурой из примера для РБНФ и найдем токены. Рассмотрим команду:

Наш лексический анализатор должен разделить эту строку на следующий список токенов:

Напишем код компилятора. Для начала создадим файл lexer.py, в программном коде которого будут определяться токены. Для создания лексического анализатора воспользуемся классом LexerGenerator библиотеки RPLY.

Создадим основной файл программы main.py. В этом файле мы впоследствии объединим функционал трех компонентов компилятора. Для начала импортируем созданный нами класс Lexer и определим токены для нашей однострочной программы:

При запуске main.py мы увидим на выходе вышеописанные токены. Вы можете изменить названия своих токенов, но для простоты согласования с функциями парсера их лучше оставить как есть.

2. Синтаксический анализатор

Второй компонент компилятора – синтаксический анализатор (он же парсер). Его роль – синтаксический анализ текста программы. Данный компонент принимает список токенов на входе и создает на выходе абстрактное синтаксическое дерево (АСД). Эта концепция более трудна, чем идея списка токенов, поэтому мы настоятельно рекомендуем хотя бы по приведенным выше ссылкам изучить принципы работы парсеров и синтаксических деревьев.

Чтобы воплотить в жизнь синтаксический анализатор, будем использовать структуру, созданную на этапе РБНФ. К счастью, анализатор RPLY использует формат, схожий с РБНФ. Самое сложное – присоединить синтаксический анализатор к АСД, но когда вы поймете идею, это действие станет действительно механическим.

Во-первых, создадим файл ast.py. Он будет содержать все классы, которые могут быть вызваны парсером, и создавать АСД.

Во-вторых, нам необходимо создать сам анализатор. Для этого в новом файле parser.py аналогично лексеру используем класс ParserGenerator из библиотеки RPLY:

Наконец, обновляем файл main.py, чтобы объединить возможности синтаксического и лексического анализаторов:

Теперь при запуске main.py мы получим значение 6. и оно действительно соответствует нашей однострочной программе «print(4 + 4 – 2);».

Таким образом, при помощи двух этих компонентов мы создали работающий компилятор, интерпретирующий язык TOY. Однако компилятор по-прежнему не создает исполняемый машинный код и не оптимизирован. Для этого мы перейдем к самой сложной части руководства – генерации кода с помощью LLVM.

3. Генератор кода

Третья и последняя часть компилятора – это генератор кода. Его роль заключается в том, чтобы преобразовывать АСД в машинный код или промежуточное представление. В нашем случае будет происходить преобразование АСД в промежуточное представление LLVM (LLVM IR).

LLVM может оказаться действительно сложным для понимания инструментом, поэтому обратите внимание на документацию LLVMlite. LLVMlite не имеет реализации для функции печати, поэтому вы должны определить собственную функцию.

Чтобы начать, создадим файл codegen.py, содержащий класс CodeGen. Этот класс отвечает за настройку LLVM и создание/сохранение IR-кода. В нем мы также объявим функцию печати.

Теперь обновим основной файл main.py, чтобы вызывать методы CodeGen:

Как вы можете видеть, для того, чтобы обработка происходила классическим образом, входная программа была вынесена в отдельный файл input.toy. Это уже больше похоже на классический компилятор. Файл input.toy содержит все ту же однострочную программу:

Еще одно изменение – передача парсеру методов module, builder и printf. Это сделано, чтобы мы могли отправить эти объекты АСД. Таким образом, для получения объектов и передачи их АСД необходимо изменить parser.py:

Наконец, самое важное. Мы должны изменить файл ast.py, чтобы получать эти объекты и создавать LLMV АСД, используя методы из библиотеки LLVMlite:

После изменений компилятор готов к преобразованию программы на языке TOY в файл промежуточного представления LLVM output.ll. Далее используем LLC для создания файла объекта output.o и GCC для получения конечного исполняемого файла:

Теперь вы можете запустить исполняем файл, для создания которого вами использовался свой язык программирования:

Следующие шаги

Мы надеемся, что после прохождения этого руководства вы разобрались в общих чертах в концепции РБНФ и трех основных составляющих компилятора. Благодаря этим знаниям вы можете создать свой язык программирования и написать оптимизированный компилятор при помощи Python. Мы призываем вас не останавливаться на достигнутом и добавить в свой язык и компилятор другие важные составляющие:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *