SimpleBasic - інтерпретатор простої Basic-подібної мови програмування для платформ Linux та Windows.
Взагалі історія цього проекту тісно пов'язана з тим, що мені давно ще зі шкільних років дуже хотілося розробити свою мову програмування. Декілька років тому це бажання стало реальністю коли я почав розробляти розрахунковий пакет у рамках своєї дисертації. Мені потрібно було інтегрувати у свою програму нескладну мову програмування з допомогою якої можна було б описувати параметричні моделі елементів будівельних конструкцій. Глюків було досить багато, тому інтерпретатор годився лише для внутрішнього користування, тим хто всі його глюки знає. Останнім часом я вирішив переписати його з нуля, паралельно змінивши ряд алгоритмів. Певним поштовхом до цього стало моє спілкування зі своїми студентами, деяким з яких дуже важко дається навчання програмуванню. Чесно кажучи, я іноді з ностальгією згадую часи розквіту DOS, коли програми писалися швидко і просто. Це дуже важливо для навчання, бо кінцевий результат спостерігається раніше. Останнім часом же спостерігається тенденція постійного ускладнення мов програмування, віконні інтерфейси відчутно ускладнили сам процес написання програм. Тому я й вирішив спробувати розробити максимально просту мову програмування яка б дозволяла максимально швидко отримувати готовий результат і дуже б легко вчилася. Крім того мені хотілося на практиці продемонструвати роботу алгоритмів та застосування різних структур даних (як стеки наприклад) про які досить сухо згадується в університетському курсі і які студенти не розуміють де можна застосувати. Ну і ще хотілося б зробити інтерпретатор таким щоб його можна було використовувати у складі інших своїх програм. Таким чином нещодавно і з'явилася версія 0.1 мови програмування яку я назвав SimpleBasic.
Інтерпретатор написано на мові програмування Free Pascal, тому його легко можна перекомпілювати під будь-яку з платформ яка нею підтримується, а це досить суттєвий перелік. Представляє він собою єдиний бінарний файл якому у якості параметру передається ім'я файлу з програмою. Я покищо підготував варіанти для Linux та Win32.
sbrun
sbrun.exe
Для підтримки графіки також потрібно щоб у системі була присутня бібліотека GLUT, яка не входить у стандартну поставку Windows (в Ubuntu з цим проблем немає)
glut32.dll
Я ще не вирішив чи буду відкривати коди самого інтерпретатору для публічного доступу чи ні. Поки що все знаходиться на досить ранній стадії і у мене ще набагато більше планів ніж реально зроблено.
Тепер щодо самої мови. Як і у звичайному Basic кожна команда задається у окремому рядку, якщо потрібно задати декілька команд в один рядок, то вони відділяються символом двокрапки ( : ), коментарі вводяться апострофом ( ' ). Приклад стандартної програми, що виводить у консоль рядок з привітанням:
' Перша програма на SimpleBasic
print "Hello, World!"
Змінні не оголошуються, пам'ять для них виділяється автоматично при першому ж згадуванні нової змінної у тексті. Типи даних не розрізняються, одна й та ж змінна на протязі виконання програми може зберігати як числові так і символьні дані. Великі і малі літери у назвах не розрізняються, допускається використання у складі ідентифікатору латинських букв, цифр, символу підкреслення та точки. Можна використовувати масиви будь-якої розмірності вказуючи індекс у квадратних дужках. Масиви як і звичайні змінні також оголошувати не потрібно, достатньо просто почати їх використовувати. Допускається використання асоціативних масивів, де у якості індексу можна задавати символьний рядок. Більше того, допускається використання навіть дробових індексів. Але не допускається присвоювати один масив іншому, лише поелементно. Крім того, тип елементів масиву не обов'язково повинен бути однаковим, у рамках одного масиву допускається зберігати дані різних типів. Після закритої квадратної дужки можна через крапку дописувати ідентифікатор поля структури, таким чином можна працювати з записами.
a=2
b=3
print a+b
a["Hello"]="Hi!"
a["Good bye"]="Bye!"
print a["Hello"]+" --> "+a["Good bye"]
for i=1 to 10
print "Name:"+people[i].name+" Age:"+people[i].age
next
У виразах використовуються стандартні операції: +, -, *, /, ^ (піднесення до степеня), \ (ділення націло), % (залишок від ділення). Логічні операції: =, <, >, <=, >=, != (не дорівнює), ! (NOT), | (OR), & (AND). До символьних рядків також можна застосовувати операції: + (конкатенація), - (відкидання символів з кінця), * (розмноження). Тут я дозволив собі трохи творчості і відійшов від синтаксису класичного Basic. Також реалізовано великий набір різноманітних функцій, які включають у себе арифметичні, текстові, введення/виведення, роботи з файлами, роботи з текстовим терміналом, функції графічного режиму та ін. Функція eval дозволяє використовувати у своїх програмах парсер арифметичних виразів.
Є ряд стандартних конструкцій. Це оператори циклів do..until, while..wend, for..next. Їх синтаксис ідентичний Basic, за винятком циклу do який завершується не loop, а until (варіант loop while не передбачено). У next змінна циклу не вказується, а сам цикл for на етапі прекомпіляції зводиться інтерпретатором до звичайного while.
i=1
do
print i
i=i+1
until i>10
'----------------
i=1
while i<=10
print i
i=i+1
wend
'----------------
for i=1 to 10 'допускається використання модифікатору step
print i
next
Для перевірки умов використовується if..else..end if. Причому реалізовано лише повну форму, використання однорядкової скороченої форми не допускається, якщо потрібно зекономити місце, то використовують : для відокремлення ключових слів. Використання ключового слова then після умови є необов'язковим.
if i=1 then
print "One"
else
print "Two"
end if
' або так
if i=1 : print "One" : else : print "Two" : end if
Користувач може оголошувати власні функції. При цьому оголошувати вкладені функції не допускається, але дозволяється використовувати рекурсію. Функції не можуть приймати у якості параметрів масиви. Всі параметри передаються за значенням, їх зміни у тілі функції не впливають на значення глобальних змінних. Для того щоб отримати доступ до глобальних змінних, потрібно перед ім'ям змінної додати два символи підкреслення.
sub fact(i) 'обчислення факторіалу
if i=1 then
return 1
else
return i*fact(i-1)
end if
end sub
print fact(5)
От коротко і все. А тепер продемонструю декілька прикладів.
Робота у текстовому режимі. Класична задача про готель, можна селити і виселяти жильців, переглядати їх список. Дані автоматично зберігаються у зовнішній файл.
hotel.sb
' Hotel example program in SimpleBasic
sub menu(x)
terminal.background 0
terminal.color 7
terminal.clear
i=x
do
terminal.locate 10,10
if i=1 then : terminal.background 7 : terminal.color 0 :
else : terminal.background 0 : terminal.color 7 : end if
print "Поселити"
terminal.locate 10,11
if i=2 then : terminal.background 7 : terminal.color 0 :
else : terminal.background 0 : terminal.color 7 : end if
print "Виселити"
terminal.locate 10,12
if i=3 then : terminal.background 7 : terminal.color 0 :
else : terminal.background 0 : terminal.color 7 : end if
print "Показати"
terminal.locate 10,13
if i=4 then : terminal.background 7 : terminal.color 0 :
else : terminal.background 0 : terminal.color 7 : end if
print "Вихід"
c=terminal.key()
if c="up" then : i=i-1 : end if
if c="down" then : i=i+1 : end if
if i<1 then : i=4 : end if
if i>4 then : i=1 : end if
until (c="esc")|(c="enter")
if c="enter" then : return i : else : return 0 : end if
terminal.background 0
terminal.color 7
terminal.clear
end sub
size=10
open 1,"r","hotel.dat"
for j=1 to size
a[j].name=inputf(1)
a[j].from=inputf(1)
a[j].to=inputf(1)
next
close 1
i=1
do
i=menu(i)
if i=1 then ' Селимо
print "="*37
print "|"+" "*11+"Селимо жильця"+"
"*11+"|"
print "="*37
j=input("У яку кімнату?")
a[j].name=input("Ім'я:")
a[j].from=input("Поселився:")
a[j].to=input("Вибуває:")
end if
if i=2 then ' Виселяємо
print "="*38
print "|"+" "*10+"Виселяємо жильця"+"
"*10+"|"
print "="*38
j=input("З якої кімнати?")
a[j].name=""
a[j].from=""
a[j].to=""
end if
if i=3 then ' Показати список жильців
print "="*37
print "|"+" "*10+"Перелік жильців"+"
"*10+"|"
print "="*37
for j=1 to size
if a[j].name!=""
then
print j+" Ім'я: "+a[j].name+" Поселився:
"+a[j].from+" Вибуває: "+a[j].to
else
print j
end if
next
c=terminal.key()
end if
until (i=4)|(i=0)
' Зберігаємо у файл
open 1,"w","hotel.dat"
for j=1 to size
printf 1,a[j].name
printf 1,a[j].from
printf 1,a[j].to
next
close 1
Результат буде таким
Робота з графікою. Як показала практика, найкраще люди вчаться програмуванню саме в процесі роботи з графікою. Тому я вирішив цій складовій приділити особливу увагу. Особливо хотілося якомога більше зменшити кількість маніпуляцій при програмуванні графіки у сучасних операційних системах. Для відображення графіки використовується OpenGL, а для роботи з віконним інтерфейсом використовується бібліотека GLUT. Ось приклад програми яка малює графік синуса:
sinus.sb
' A graph sample for SimpleBasic
sub redraw
graph.clear 0.9,0.9,1
graph.color 0,0,0
graph.linewidth 1
graph.line -5,0,5,0
graph.line 0,5,0,-5
graph.textsize 0.0025
graph.print 0.2,4.5,"y=sin(x)"
graph.print 4.7,0.2,"x"
graph.textsize 0.0015
for x=-5 to 5
graph.line x,0.1,x,-0.1
if x!=0 then : graph.print x,-0.3,x : end if
next
for y=-5 to 5
graph.line 0.1,y,-0.1,y
if y!=0 then : graph.print -0.3,y,y : end if
next
graph.linewidth 3
graph.color 1,0,0
graph.moveto -5,sin(-5)
for x=-5 to 5 step 0.25
graph.lineto x,sin(x)
next
end sub
graph.createwindow 640,480,"y=sin(x)"
graph.drawfunc "redraw"
graph.setcs -5,5,-5,5
graph.mainloop
Ця програма створює вікно, малює у ньому систему координат та графік синуса і закривається при натисненні клавіші Esc.
Ще один графічний приклад для демонстрації інтерактивності. Квадрат можна рухати у вікні використовуючи стрілки, змінювати його розміри кнопками + та -, переміщувати з допомогою мишки, а права кнопка мишки виводить на екран контексне меню.
rects.sb
' A graph sample for SimpleBasic
sub redraw
graph.clear 0,0,0.5
graph.color 1,0.5,0
graph.rect __x-__size,__y+__size,__x+__size,__y-__size
end sub
sub readkey(key)
if key="up" then : __y=__y+__step : end if
if key="down" then : __y=__y-__step : end if
if key="left" then : __x=__x-__step : end if
if key="right" then : __x=__x+__step : end if
if key="+" then : __size=__size+0.1 : end if
if key="-" then : __size=__size-0.1 : end if
end sub
sub mouse(b,x1,y1)
if b="left" then
__x=-5+10*x1/640
__y=5-10*y1/480
end if
end sub
sub menu(i)
if i=1 then : __size=__size+0.1 : end if
if i=2 then : __size=__size-0.1 : end if
if i=3 then : halt : end if
end sub
x=0 : y=0 : step=0.2 :size=0.5
graph.createwindow 640,480,"Moving Rectangle"
graph.drawfunc "redraw"
graph.keyfunc "readkey"
graph.mousefunc "mouse"
graph.menufunc "menu"
graph.menuitem "Larger"
graph.menuitem "Smaller"
graph.menuitem "Exit"
graph.setcs -5,5,-5,5
graph.mainloop
Ось так виглядає результат роботи програми:
Звісно ж, оскільки інтерпретатор є кросплатформеним, то цю ж саму програму без будь-яких змін можна запустити і у Windows:
І на завершення ще наведу простеньку іграшку - арканоід. У даному прикладі продемонстровано роботу з таймером для створення анімації.
arkanoid.sb
' An Arkanoid example for SimpleBasic
sub redraw
if __gameover=0 then
graph.clear 0,0,0.5
graph.color 1,0.5,0
graph.rect __pos-__size,-8,__pos+__size,-9
graph.color 1,0,0
graph.circlef __x,__y,__ball
else
graph.color 1,0,0
graph.textsize 0.01
graph.print -4,0,"GAME OVER"
end if
end sub
sub readkey(key)
if key="left" then : __pos=__pos-__step : end if
if key="right" then : __pos=__pos+__step : end if
end sub
sub timer
if __x>=10 then : __tx=-1 : end if
if __x<=-10 then : __tx=1 : end if
if __y>=10 then : __ty=-1 : end if
l=__pos-__size : r=__pos+__size
if (__x>l)&(__x<r) then
if __y<-8 then : __ty=1 : end if
else
if __y<-8 then : __gameover=1 : end if
end if
__x=__x+__tx*__f
__y=__y+__ty*__f
end sub
gameover=0
pos=0 : f=0.5 : tx=1 : ty=1 : ball=0.5
x=0 : y=-3 : step=1 : size=2
graph.createwindow 400,400,"SimpleArkanoid"
graph.drawfunc "redraw"
graph.keyfunc "readkey"
graph.timerfunc "timer"
graph.timer 1000\60*2
graph.setcs -10,10,-10,10
graph.mainloop
Ось така іграшка вийшла:
Ось так. Крім вже описаних можливостей підтримується також робота у повноекранному режимі (наприклад graph.gamemode "1024x768:32"). Через деякий час планую реалізувати 3D графіку, підтримку мультимедіа даних та багатопоточність. Потім піду далі, подивлюся, що з цього вийде. По ходу і документацію ще підготую.
Через деякий час планую написати статтю про те як пишуться інтерпретатори арифметичних виразів, зокрема про алгоритм побудови зворотньої польскої нотації (постфіксної форми запису операцій, без використання дужок) з прикладами коду на FreePascal. Також розповім і про інші аспекти створення інтерпретатору, зокрема про реалізацію управляючих структур та підтримки функцій користувача. На сьогодні ж мабуть все.
Немає коментарів:
Дописати коментар