Создание своего компилятора на Visual Basic!
Да действительно, мы сейчас попробуем стандартными средствами создать свой язык программирования! Не удивляетесь, но это не так сложно как может показаться на первый взгляд. Конечно до создания настоящего компилятора далеко, но это лишь пример и дальше всё ограничивается лишь вашей фантазией и умением.
Ну что ж, приступим. Для начала как-нибудь назовём наш новый язык. Ну например Simple Programming Language - сокращённо SPL. Любой язык программирования состоит из трёх частей:
а) правила синтаксиса;
б) операторы;
в) собственно сам компилятор.
Начнём пожалуй с пункта а. Правила синтаксиса мы может установить любые. Пусть это
будут те же правила, что и в Бейсике, но с русским синтаксисом то есть вместо:
If x = 0 Then MsgBox "Error"
вы напишете достойное:
Если икс = 0 Тогда Сообщ "Ошибка"
не правдали здорово?
Пункт б. Ну если мы выбрали Бейсик, то и операторы будут те же, что и в Бейсике, но на русском языке, это вы уже увидели в вышеописанном примере.
И наконец самое главное и интересное - реализация языка, то есть создание компилятора.
Открываем Visual Basic и создаём в нём новый проект - Standard EXE.
Перед нами пустая форма. Начнём её оформлять. Измените некоторые свойства нашей формы(их меняют на панели Properties):
Name = frmMain - так обычно называют основную форму;
Caption = "Simple Programming Language version 1.0.0" - тут вы можете написать всё, что вашей программистской душе угодно!
Так же измените длину и ширину основной формы под ваши.
И ещё измените BorderStyle на 1 - Fixed Single, а MinButton на True. Теперь мы запретили изменение размеров форм.
Из чего будет состоять наша программа? Конечно в первую очередь нам надо создать поле ввода, куда пользователь будет писать свой код.
Итак нажимаем на кнопку TextBox на панели инструментов.
Затем методом протягивания обозначьте этот компонент на вашей форме. Задайте нужную длину и ширину, а также обязательно измените свойство MultiLine на True. Это позволит писать код в текстовое поле не на одну строку, а на несколько. Также свойство ScrollBars надо установить как 2 - Vertical. При этом во время ввода текста пользователем нашей программы справа появится полоса прокрутки. Кстати, наше поле ввода я назвал txtCode (Name = txtCode).
Теперь думаю нужно создать несколько кнопок, которые собственно и будут выполнять основные действия программы. Это кнопки "Запуск" и "Выход". Можно также добавить ещё несколько кнопок по вашему усмотрению, а я объясню, что будут делать эти две кнопки. Нетрудно догадаться, что первая кнопка запустит код который впишет пользователь, а вторая - это выход из вашей программы. Поэтому в свойство Caption первой кнопки впишем "Запуск", а второй "Выход".
Кнопки я назвал cmdRun и cmdExit.
Ну что, с оформлением покончено, пора переходить к написанию кода к программе. Код я постарался сделать максимально понятным и коротким. Расскажем о том, как я собираюсь обычный текст, который вводит пользователь, превратить в работающий код. Это делается при помощи функции EbExecuteLine, которая в Visual Basic"e пятой версии находится в библиотеке vba5.dll, а в Бейсике шестой версии в библиотеке vba6.dll (на более низких версиях VB я никогда не работал). Данная функция обрабатывает обычный текст как программный код. Функция EbExecuteLine возвращает значение, которое будет равняться 1 (если код сработал и не имеет синтаксических и других ошибок) или 0 (если при обработке кода произошла ошибка). Это замечательное свойство данной функции мы и используем. В случае, если текст невозможно обработать и он содержит ошибки мы выведем сообщение об ошибке. Чтобы воспользоваться возможностями функции EbExecuteLine сначала нужно её объявить. Если вы используете VB 6.0, то в разделе General впишите следующие объявления:
Option Compare Text
Option Explicit
Private Declare Function EbExecuteLine Lib "vba6.dll" (ByVal pStringToExec As Long, _ ByVal Foo1 As Long, ByVal Foo2 As Long, ByVal fCheckOnly As Long) As Long
Если же вы используете Visual Basic пятой версии, то вместо предыдущих объявлений вы должны написать немного иное:
Option Compare Text
Option Explicit
Declare Function EbExecuteLine Lib "vba5.dll" (ByVal pStringToExec As Long, _
ByVal Foo1 As Long, ByVal Foo2 As Long, ByVal fCheckOnly As Long) As Long
То есть заменяем vba6.dll на vba5.dll и убираем Private. Теперь программируем кнопки. Что написать в процедуре обрабатывающей нажатие(Click) кнопки выхода мы знаем:
Private Sub cmdExit_Click()
End
End Sub
Как вы уже поняли после обработки события End программа завершает свою работу, то есть выключается. Теперь переходим к программированию второй, и самой главной, кнопки. Она используя функцию, возможности которой я описал выше, переводит обычный текст в исполняемый код. Итак, сначала проверим действительно ли в поле ввода содержится текст:
If Len(txtCode.Text) <> 0 Then
и если всё в порядке, то идём дальше. Объявим переменные, которые будут нам помогать:
Dim Result As Boolean, Code As String
В переменной Result будет находиться значение возвращаемое функцией EbExecuteLine. А в переменной Code текст, который отправим на обработку, поэтому сразу присвоим этой переменной текст в поле ввода:
Code = txtCode.Text
Теперь в переменной Code содержится значение поля ввода. У функции EbExecuteLine есть один недостаток - она не может обработать символ новой строки (в Бейсике это vbCrLf), но зато она понимает двоеточие (:) и нужно этим воспользоваться. Значит заменяем vbCrLf на двоеточие:
Code = Replace(Code, vbCrLf, ":")
Мы решили, что у нас будет русский синтаксис, но функция EbExecuteLine понимает только англиские операторы, поэтому все русские операторы, которые встретятся программе, переведём на английские:
Code = Replace(Code, "Если", "If", , , 2)
Code = Replace(Code, "Тогда", "Then", , , 2)
Code = Replace(Code, "Сообщ", "MsgBox", , , 2)
Code = Replace(Code, "От", "For", , , 2)
Code = Replace(Code, "До", "To", , , 2)
Code = Replace(Code, "Следующ", "Next", , , 2)
Цифра 2 в конце Replace означает, что символы должны заменяться независимо от регистра в котором находятся. Вы можете добавить и другие операторы. Я использовал лишь самые часто используемые. Осталось последнее - запустить код из текстового поля:
Result = EbExecuteLine(StrPtr(Code), 0&, 0&, False) = 0
и если есть ошибки сообщаем об этом:
If Result = False Then MsgBox "Ошибка в синтаксисе программы!", _ vbCritical, "SPL by Daniyar Atadjanov"
txtCode.SetFocus
Ну вот и всё. Пришло время вставить код, который сработает если пользователь не ввёл текста в поле:
Else
MsgBox "Поле ввода кода не должно быть пустой сткрокой!", vbCritical, _ "SPL by Daniyar Atadjanov"
End If
Ниже показан весь код программы:
Option Compare Text
Option Explicit
Private Declare Function EbExecuteLine Lib "vba6.dll" (ByVal _ pStringToExec As Long, ByVal Foo1 As Long, ByVal Foo2 As Long, ByVal _ fCheckOnly As Long) As Long
" Если вы используете VB 5-ой версии, то расскомментируйте следующее:
"Declare Function EbExecuteLine Lib "vba5.dll" (ByVal pStringToExec As _ Long, ByVal Foo1 As Long, ByVal Foo2 As Long, ByVal fCheckOnly As Long) As _ Long
Private Sub cmdRun_Click()
If Len(txtCode.Text) <> 0 Then
Dim Result As Boolean, Code As String
Code = txtCode.Text
Code = Replace(Code, vbCrLf, ":", , , 2)
Code = Replace(Code, "Если", "If", , , 2)
Code = Replace(Code, "Тогда", "Then", , , 2)
Code = Replace(Code, "Сообщ", "MsgBox", , , 2)
Code = Replace(Code, "От", "For", , , 2)
Code = Replace(Code, "До", "To", , , 2)
Code = Replace(Code, "Следующ", "Next", , , 2)
Result = EbExecuteLine(StrPtr(Code), 0&, 0&, False) = 0
If Result = False Then MsgBox "Ошибка в синтаксисе программы!", _ vbCritical, "SPL by Daniyar Atadjanov"
txtCode.SetFocus
Else
MsgBox "Поле ввода кода не должно быть пустой сткрокой!", vbCritical, "SPL by Daniyar Atadjanov"
End If
End Sub
Private Sub cmdExit_Click()
End
End Sub
А вот результат её работы:
Или вот ещё примерчик:
Увидя второй пример умный читатель подумает, что такая программа как наш маленький компилятор, настоящая находка для хакера или кракера. Вы так подумали? Да? Поздравляю вас - у вас чутье на такие вещи (из вас выйдет неплохой хакер). Нет, не подумали? Не огорчайтесь, подумайте как такие дыры в проге можно залатать и из вас получится нечто большее, чем хакер. И действительно, функция, которую мы использовали для превращения текста в код работает и с компонентами на вашей форме и не только с ними, а ещё и с самой формой, и со всеми остальными формами (если они есть) в вашем проекте!
А теперь самое-самое интересное. Попробуете ввести в поле например такое:
frmMain.Hide
Сообщ "До свидания!"
frmMain.Show
Сообщ "Приветик!"
И ещё такое:
frmMain.Top = 0
frmMain.Left = 0
If MsgBox ("Cool?", vbYesNo + vbQuestion) = vbNo Then frmMain.Left = Screen.Width - frmMain.Width:frmMain.Top = Screen.Height - frmMain.Height - 450: MsgBox "Cool!": End
Не правда ли здорово? Конечно здорово, но если вы решили защитить свою программу от подобных вторжений, то в первую очередь стоит заменить frmMain на что-нибудь типа frmMain96293867209586. Это конечно же не очень красиво, но есть хоть уверенность, что пользователь не сможет манипулировать вашей формой и элементами внутри неё. А вдруг пользователь всё-таки набрал frmMain96293867209586? Что же делать? А вы запретите набор такой комбинации символов, то есть добавьте в процедуру обрабатывающую нажатие кнопки cmdRun добавте код проверяющий наличие "frmMain96293867209586" в текстовом поле! Что-то похожее на это:
If InStr(txtCode.Text, "frmMain96293867209586") Then MsgBox "Error!"
Ну вот, мы многое успели! Даже разобрали вопросы безопасности. Надеюсь, что в будущем вам это пригодится как со стороны хакера ломающего чью-то прогу :), так и со стороны программера пытающегося повысить безопасность своей программы.
И напоследок о том где и как можно с выгодой использовать замечательную функцию EbExecuteLine. Область применения конечно очень широка и назвать все варианты я не смогу. Я могу лишь рассказать небольшой пример. Допустим одна программа как-то (не будем вдаваться в подробности как именно) передаёт другой программе некоторые параметры, например координаты объекта Z. Вторая программа, получая эти параметры, показывает местоположение объекта Z у себя на экране. Эта вторая программа принимает данные в виде: Z.Top = 50: Z.Left = 75: Timer1.Enabled = False. Как же нарисовать объект на экране используя эти параметры? Не ломайте себе голову, воспользуйтесь функцией EbExecuteLine и всё!
Очень надеюсь, что вы найдёте этой функции хорошее применение.