Д2 - это язык программирования высокого уровня. Предками языка, оказавшими на него наибольшее влияние, можно назвать Forth, Pascal (Modula-2, Oberon) и С. Язык был задуман как обладающий намеренно простым синтаксисом и возможно большей расширяемостью. Первое было достигнуто за счет сокращения до реального минимума управляющих конструкций, и отказа от такого понятия современных языков алголоидного типа как тип данных, однако же, оставить "классический" синтаксис языка типа Pascal. Формально язык содержит единственный тип данных - указатель. Косвенно поддерживаются такие типы как числа и строки символов. Второе, т.е. расширяемость, было достигнуто за счет введения понятия библиотеки или модуля, а также, в основном за счет внутреннего устройства ядра языка идею которого он позаимствовал у Forth, хотя это скорее вопросы реализации, чем синтаксиса... Язык поддерживает такие понятия как процедура (векторная процедура), локальные и глобальные переменные, параметры. В языке нет каких либо предопределенных операций и функций кроме, как я уже отмечал, операций управления процессом исполнения. Все функции ввода-вывода, математические, процедуры работы с графикой и т.д. поставляются в виде библиотек и в исходных текстах.
Для описания синтаксиса Д-2 используются Расширенные Бэкуса-Наура Формы (РБНФ). Варианты разделяются знаком |. Квадратные скобки [ и ] означают необязательность записанного внутри них выражения, а фигурные скобки { и } означают его повторение (возможно 0 раз). Терминальные символы заключаются в кавычки.
Коментарий является игнорируемой частью программы, но, по мнению некоторых идеологов теории программирования является необходимым для понимания сути программы, поэтому Д2 включает возможность создания коментария.
Коментарий заключается в фигурные скобки { и }, и может занимать несколько строк.
Коментарий не может быть вложенным
Коментарий может содержать директивы компилятору. Но об этом позднее.
Пример: {Это коментарий}
Комментарий = '{' ЛюбойСимвол '}'
Идентификатор начинается с буквы ( a..z,A..Z), вторым своим символом он может иметь букву и цифру ( 0..9 ), а также символ подчеркивания _ . В некоторых случаях в качестве идентификатора могут выступать строки, эти случаи будут оговорены подробнее.
Пример: this_is_D2_identifier
Идентификатор = буква { буква | цифра }
Под строкой в Д2 подразумевается цепочка литер, заключенная в ' ' кавычки, если символ ' необходимо употребить в качестве элемента строки, то достаточно повторить его два раза.
Пример: 'It''s a string'
Строка = "'" ЛюбойСимвол "'"
Числом является набор символов 0..9 образующих номер, лежащий в диапазоне 0..FFFFh. Основание числа можно указать явно в формате: 0 { x|h|d|e|o|b } { цифра }. Где символ определяет систему исчисления x,h - шестнадцатиричная, d,e - десятичная (по умолчанию) o - восмеричная и b - двоичная.
Пример: 345 0x90 0b10011
Число = [ 0 ] [ основание ] { цифра }
Цифра = 0..9,A..F
Основание = x|h|d|e|o|b
Д2 содержит следующие зарезервированные слова:
ASM CASE CONST CONTINUE CYCLE DO ELSE ELSIF ELSIFN END EXIT FI FOR IF IFN IN INLINE LEAVE LIBRARY LOOP MODULE NEW NEXT OF RECUR REPEAT RET SUB THEN TYPE UNTIL USE VAR VECT WEND WHILE3.6. Разделители
Разделителями в Д2 являются следующие символы: , ; ( ) [ ] .
Разделитель = ',' | ';' | '(' | ')' | '[' | ']'
3.7. СимволыСимволами в д2 являются: ` ~ ! @ # $ % ^ & * - + = " ; : < > . \ | / ? .
Из них также можно составить идентификатор.
Идентификатор = Символ { Символ }
Символ = '`' | '!' | '@' | '#' | '$' | '%' | '^' | '&' | '*' | '-' | '+' | '=' | '"' | ';' | ':' | '<' | '>' | '.' | '\' | '|' | '/' | '?'
Пример: + - ++ >>3.8. Игнорируемые символы
Символы с ascii кодами 0..32,127 игнорируются, если они не являются в данном контексте разделителями между идентификаторами или какими-либо другими лексемами.
Появление во входном потоке какого-нибудь символа, не описанного
выше, кроме как в коментарии, является ошибкой и приводит к выдаче
соответствующего сообщения.
Программа на d2 называется модулем и содержит в себе описания переменных и процедур, а также списки импорта библиотек. Программа начинается с зарезервированного слова MODULE или LIBRARY, следующего за ним имени модуля или библиотеки и заканчивается словом END, которое завершает процесс компиляции текущего модуля. Модуль должен содержать хотябы одну процедуру с именем main с которой и начинается выполнение. Процедура или переменная видны с места их описания до конца модуля, локальные переменные видны только в процедуре, для которой описаны. Из библиотеки доступны только те процедуры, которые имели соответствующую экспортную метку. Глобальные переменные экспортироваться не могут.
Программа = MODULE | LIBRARY Идентификатор { Описание } END Обявление глобальной переменной начинается с зарезервированного слова VAR и последующего перечисления идетификаторов переменных через запятую. По умолчанию под переменную резервируется место равное машинному слову. Этот размер можно переопределить, указав новый в [] скобках: [ new_size ]. По умолчанию переменная инициализируется нулями, но ее можно инициализировать другими данными, перечислив их в <> скобках: < ... data >.
Описание процедуры начинается с зарезервированного слова SUB или VECT, затем следует имя процедуры. Заканчивается описание словом RET. Процедуры описанные как VECT являются векторными, обладающими векторным полем кода, и могут переопределяться в момент исполнения.
Процедуры могут перегружаться, глобальные переменные не могут перегружаться.
После имени процедуры может идти экспортная метка, расширяющая область видимости процедуры на модули, импортирующие данный модуль (библиотеку).
Для процедуры можно установить приоритет, аналогичный приоритету операндов в алгебраических выражениях, для этого приоритет указывается в [] скобках: [ Число ]. Чем больше число, тем ниже приоритет процедуры, по умолчанию процедуре присваивается приоритет 1, переменные обладают приоритетом -1.
Процедура может содержать локальные переменные. Они описываются сразу после заголовка процедуры, аналогично описанию глобальных переменных, но могут содержать импортную метку, показывающую, что данная переменная является параметром.
Имена локальных переменных могут перегружать имена глобальных и имя самой процедуры.
Процедуры могут экспортироваться из библиотек. Библиотека, процедуры из которой должны экспортироваться должна быть явно упомянута с помощью зарезервированного слова USE, при этом все процедуры, обладающие экспортными метками становятся видимыми в текущем модуле.
Константы описываются с помощью зарезервированого слова CONST.
Язык d2 не имеет ни одной встроенной или стандартной функции, поэтому все функции создаются или на языке ассемблера или на самом d2. Описание процедуры подразумевает указание ее приоритета. Данный приоритет используется при разборе выражений. Выражение может содержать любую допустимую лексему кроме операторов.
Идентификатор, если это процедура - приводит к ее вызову, если это переменная - на стеке остается ее значение, для взятия адреса, как переменной (глобальной или локальной), так и процедуры, используется символ '@'.
Число оставляет на стеке свое значение.
Использование строки в выражении, подразумевает использование ее адреса в памяти.
В выражении могут быть использованы скобки, запятые и символ ';'. Символ ';', а также зарезервированное слово приводит к завершению вычисления текущего выражения. Выражение должно быть сбалансировано по скобкам.
Выражение = { Идентификатор | Строка | Число | Символ | '(' Выражение ')' }
Операторы обозначают действия. Есть простые и структурные операторы. Простые не содержат в себе никаких других частей, которые являются операторами. Структурные операторы состоят из частей являющихся самостоятельными операторами или выражениями.
Оператор = ОператорIf | ОператорWhile | ОператорRepeat | ОператорDo | ОператорFor | ОператорCycle | ОператорInline | ОператорContinue | ОператорLeave | ОператорRecur | ОператорExit
ОператорIf = IF | IFN Блок THEN Блок { ELSIF | ELSIFN Блок THEN Блок } [ ELSE Блок ] FI
Оператор IF позволяет выполнять ветвление по условию, блок между IF|IFN и THEN должен оставлять значение, которое интерпретируется как булевое (TRUE, если не равно 0), при этом в случае успеха (или неуспеха для IFN) выполняется блок операторов THEN, иначе блок ELSIF|ELSIFN или ELSE в случае их наличия. Части ELSIF могут повторяться произвольное число раз.
ОператорWhile = WHILE Блок DO Блок WEND
Оператор организует цикл с предусловием.
ОператорRepeat = REPEAT Блок UNTIL Блок LOOP
Оператор организует цикл с постусловием
ОператорDo = DO Блок LOOP
Оператор организует бесконечный цикл.
ОператорFor = FOR Идентификатор {, Идентификатор } IN Данные DO Блок NEXT
ОператорCycle = CYCLE Блок DO Блок LOOP
Оператор позволяет выполнить группу операторов заданное на перед число раз.
ОператорInline = INLINE ( Данные )
Оператор INLINE позволяет делать вставки непосредственно в машинном коде. При этом он может содержать не только числа, но и другие элементы. В данном случае будут компилироваться адреса этих элементов.
ОператорContinue = CONTINUE
Оператор осуществляет безусловный переход на начало цикла (начинается следующая итерация). Циклами являются операторы: WHILE, REPEAT, DO, FOR и CYCLE.
ОператорLeave = LEAVE
Оператор осуществляет безусловный выход из цикла. Выход осуществляется из самого внутреннего цикла содержащего оператор.
ОператорRecur = RECUR
Оператор осуществляет вызов текущей процедуры (рекурсия).
ОператорExit = EXIT
Оператор EXIT осуществляет выход из текущей процедуры.
4. Объявления и области видимости
Описание = ОписаниеПеременных | ОписаниеПроцедуры | ОписаниеЭкспорта | ОписаниеКонстант
Пример (минимальная d2 программа):
MODULE minimal_d2_program
SUB main
{ do nothing }
RET
END
4.1. Объявление глобальных переменных
ОписаниеПеременной = Идентификатор [ Размер ] [ Инициализатор ]
Размер = '[' Число ']'
Инициализатор = < { Числа | Строки | ИменаПроцедур } >
Пример:
VAR
a,b,c,
x<3>,y<5>,
arr[10]<0 1 2 3 4 5 6 7 8 9>,
ptrs[6]<sub_1 sub_2 sub_3>,
str_ptr[4]<'Иванов' 'Иван' 'Иваныч'>;
4.2. Описание процедур
ЭкспортнаяМетка = '*'
Приоритет = '[' Число ']'
ОписаниеЛокальныхПеременных = VAR ОписаниеПеременной | ОписаниеПараметра;
ОписаниеПараметра = Идентификатор ИмпортнаяМетка
ИмпортнаяМетка = '*'
Пример:
MODULE lview;
USE
system,fio,iostd,param,putnum,math;
CONST
buf = 10000;
SUB readlib VAR h;
@h = fopen(paramstr(1));
fread(h buf 0h7FFF);
fclose(h);
RET
SUB checklib VAR ptr<0>,str<'lib2d'>;
REPEAT
IF byte(str+ptr) <> byte(buf+ptr) THEN
FALSE;
EXIT
FI
inc(@ptr)
UNTIL ptr==length(str) LOOP
TRUE
RET
SUB viewhdr VAR pos*;
puthex(pos-buf);
bl;
putword(byte(pos+2));
bl;
putword(byte(pos+3));
bl;
putword(byte(pos+4));
bl;
putstring(pos+5);
cr
RET
SUB showlib VAR next,old;
@next=word(buf+5);
WHILE next<>0 DO
@old=next;
@next=word(buf+next);
viewhdr(old+buf);
WEND
RET
SUB main
IF paramcount THEN
readlib
IF checklib THEN
showlib
ELSE
putstring 'unknown library format'
FI
ELSE
cr;
putstring 'usage: lview [filename''.''ext]';
cr;
cr
FI
RET
END
4.3. Экспорт процедур
Пример:
USE system, math, '..\my_libs\lib', lib;
4.4. Описание констант
ОписаниеКонстанты = Идетификатор '=' Терм
Пример:
CONST
c1 = 12,
str = 'Hello, world!',
plus = + ;
5. Выражения
Пример написания библиотеки math:
include libmacro.inc
beglib 'math'
item 'inc','*'
pop bx
xchg ax, bx
inc word ptr [bx]
item 'dec','*'
pop bx
xchg ax, bx
dec word ptr [bx]
item '+','*',5
pop bx
add ax,bx
item '-','*',5
pop bx
xchg ax, bx
sub ax,bx
item '*','*',4
pop bx
imul bx
item '/','*',4
cwd
pop bx
xchg ax,bx
idiv bx
item '%','*',4
cwd
pop bx
xchg ax,bx
div bx
mov ax,dx
item '>','*',6
pop bx
cmp bx,ax
setg al
item '<','*',6
pop bx
cmp bx,ax
setl al
item '=','*',10
pop bx
mov [bx], ax
pop ax
item '<=','*',6
pop bx
cmp bx,ax
setle al
item 'byte','*',1
mov bx,ax
xor ah,ah
mov al,[bx]
item 'word','*',1
mov bx,ax
mov ax,[bx]
item '>=','*',6
pop bx
cmp bx,ax
setge al
item '==','*',6
pop bx
cmp bx,ax
sete al
item '<>','*',6
pop bx
cmp bx,ax
setne al
item '&','*',7
pop bx
and ax,bx
item '|','*',7
pop bx
or ax,bx
item '^','*',7
pop bx
xor ax,bx
endlib
6. Операторы
Блок = { Оператор | Выражение }
Пример:
IF ch==cUP THEN
@y=(y-1) & 2
ELSIF ch==cDOWN THEN
@y=(y+1) & 2
ELSIF ch==cRIGHT THEN
@x=(x+1) & 2
ELSIF ch==cLEFT THEN
@x=(x-1) & 2
FI
6.2. Оператор While
Пример:
@i=15;
WHILE i DO { печать чисел от 15 до 1 }
putword(i);
bl;
@i--
WEND
6.3. Оператор Repeat
Пример:
@i=1;
REPEAT { печать чисел от 1 до 15 }
putword(i);
bl;
@i++
UNTIL i>15 LOOP
6.4. Оператор Do
Пример:
MODULE HelloWorld
SUB main
INLINE(
0xBA,'Hello, world!$',
0xB4,0x09,
0xCD,0x21
)
RET
END
6.8. Оператор Continue
Пример:
DO
@ch=readkey()
IF ch==cESC THEN LEAVE FI
execute(ch)
LOOP
6.10. Оператор Recur
MODULE tetris; USE system,crt,math,write,timehund,putnum,iostd; CONST N = 4, n_e = 8, e_size = 25, Y = 3, X = 2, _ = 0, buf_addr = 5000; VAR { элементы 5x5 } E1 [e_size] < _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X _ _ >, E2 [e_size] < _ _ X _ _ _ _ X _ _ _ _ X _ _ _ X X _ _ _ _ _ _ _ >, E2i [e_size] < _ _ X _ _ _ _ X _ _ _ _ X _ _ _ _ X X _ _ _ _ _ _ >, E3 [e_size] < _ _ _ _ _ _ X X _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ _ >, E4 [e_size] < _ _ _ _ _ X X X _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ _ >, E5 [e_size] < _ _ _ _ _ _ _ _ _ _ _ _ X X _ X X X _ _ _ _ _ _ _ >, E6 [e_size] < _ _ _ _ _ _ _ _ _ _ _ X X X _ _ _ X _ _ _ _ _ _ _ >, E7 [e_size] < _ _ _ _ _ _ _ X _ _ _ X X X _ _ _ X _ _ _ _ _ _ _ >, E8 [e_size] < _ _ _ _ _ _ X _ _ _ _ X X X _ _ _ _ X _ _ _ _ _ _ >, E [16] < E1 E2 E3 E4 E5 E6 E2i E8 >, score ; CONST width = 17, height=24; { 17 x 22 } SUB . [9] VAR x*,y*; buf_addr+pred(y)*width+pred(x) RET SUB not VAR predicat*; IF predicat THEN FALSE ELSE TRUE FI RET SUB draw VAR i,j; @i=width; WHILE i DO @j=height; WHILE j DO w_char40(10+i,j,byte(i.j),219); dec(@j) WEND dec(@i) WEND RET SUB w_string VAR x*,y*,i<0>,str*; WHILE byte(str+i) DO x+i.y letb byte(str+i); inc(@i) WEND RET SUB write_score VAR i<5>,s; @s=score; REPEAT w_char40(i,1,4,s%10+0h30); @s=s/10; dec(@i); UNTIL s==0 LOOP RET VAR ln[18]< Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y Y 0>; SUB init VAR i<1>,j; w_mode(0); cursoroff; write_score; WHILE i<=height DO w_string(1,i,@ln); inc(@i); WEND @i=2; WHILE i; FOR x,y IN 0..N DO IF byte(Xt+x.Yt+y) & byte(x!y) THEN @F=FALSE FI NEXT F RET SUB rotate VAR NE[25],i,j; FOR i,j IN 0..N DO @NE+5*i+j letb byte(i!j) NEXT FOR i,j IN 0..N DO i!j letb byte(@NE+5*j+4-i) NEXT IF not(can(Xe,Ye)) THEN FOR i,j IN 0..N DO i!j letb byte(@NE+5*i+j) NEXT FI RET SUB show VAR x,y; FOR x,y IN 0..N DO IF byte(x!y) THEN (Xe+x.Ye+y) letb byte(x!y) FI NEXT RET SUB hide VAR x,y; FOR x,y IN 0..N DO IF byte(x!y) THEN (Xe+x.Ye+y) letb 0 FI NEXT RET SUB move VAR Xt,Yt; @Xt=Xe+dX; @Yt=Ye+dY; IF can(Xt,Yt) THEN @Xe=Xt; @Ye=Yt; FALSE ELSE TRUE FI RET SUB shift VAR line*,row; WHILE line>1 DO @row=2; WHILE row 1 DO WHILE full(line) DO shift(line) WEND dec(@line) WEND write_score RET SUB run VAR ch, delay_factor; REPEAT {пустить элемент в стакан} put; @delay_factor=5; REPEAT clrkbdbuf; show; draw; delay(delay_factor); hide; @dX=0; { обработать нажатие клавиши, если оно есть} IF keypressed THEN @ch=scancode; @dY=0; IF ch==RIGHT THEN @dX=1 ELSIF ch==LEFT THEN @dX=0hFFFF ELSIF ch==UP THEN rotate ELSIF ch==DOWN | ch==SPACE THEN @delay_factor=0 ELSIF ch==ENTER THEN REPEAT @ch=scancode UNTIL ch==ENTER | ch==ESC LOOP FI { иначе элемент опускается вниз } ELSE @dY=1 FI UNTIL ch==ESC | (move & dY) LOOP show; replace UNTIL ch==ESC LOOP RET SUB done w_mode(3); putstring('Eugene programming, KSU 2001'); RET SUB main init run done RET END