Программа на языке 4GL может состоять из нескольких файлов (модулей) с исходными текстами на 4GL. К ней так же относятся файлы с описанием используемых экранных форм, которые компилируются отдельно. Имя каждого модуля должно иметь расширение .4gl (например, module1.4gl), а имя файла с описанием экранных форм должно иметь расширение .per (например, form2.per).
Каждый модуль содержит описания переменных и несколько процедурных блоков function (подпрограммы) и report (блоки печати). В программе должен быть один блок main - главный блок, начинающийся с ключевого слова main. На него будет передаваться управление при старте программы.
Формат записи операторов 4GL свободный. Можно писать все подряд на одной строке, один оператор на нескольких строках, слова операторов можно разделять произвольным количеством пробелов и комментариев. Никакими значками (типа ;) операторы разделять не
нужно. Окончание операторов определяется по контексту.
Весь набор ключевых слов языка зарезервирован, их нельзя занимать для других целей (на имена объектов и переменных 4GL).
Компилятору языка безразлично, большими или маленькими буквами пишутся операторы. Он их не различает.
Комментарии обозначаются знаками | { комментарий }, | или знаком # - до конца строки, | или знаком -- (два знака минус) до конца строки. |
1. Объекты SQL 2. Переменные 4GL 3. Программные Имя базы данных простая переменная функция Имя таблицы переменная типа "запись" отчет Имя столбца массив метка Имя индекса Имя псевдотаблицы Имя синонима ( database-name простые переменные function ) ( table-name переменная типа запись report ) ( column-name массивы label ) ( index-name ) ( view-name ) ( synonim-name ) 4. Имена операторов 5. Объекты экранного обмена. и курсоров window statement-id - изготовленный оператор form cursor-name - курсор screen-field screen-record screen-array
Организация программы
MAIN FUNCTION REPORTОбъявления переменных
DEFINE GLOBALSПрисвоения
LET INITIALIZEПрограммные
CALL EXIT GOTO RETURN FOR LABLE CASE WHILE RUN IF CONTINUE SLEEPПерехват прерываний
WHENEVER DEFERДинамическое создание операторов
PREPARE EXECUTE FREEМанипуляция "курсором"
DECLARE OPEN FOREACH PUT CLOSE FETCH FLUSHЭкранный обмен
MENU OPEN FORM DISPLAY ARRAY OPTIONS DISPLAY FORM SCROLL OPEN WINDOW CLOSE FORM INPUT ARRAY CURRENT WINDOW DISPLAY PROMPT CLEAR INPUT ERROR CLOSE WINDOW CONSTRUCT MESSAGEГенерация отчетов
START REPORT OUTPUT TO REPORT FINISH REPORT
В языке 4GL имеются простые переменные, переменные типа запись и массивы. Простые переменные бывают следующих типов:
INTEGER CHAR(n) DATE SMALLINT DECIMAL(m,n) DATETIME ед_врем1 TO ед_врем2 REAL MONEY(m,n) INTERVAL ед_врем1 TO ед_врем2 FLOAT
{YEAR,MONTH,DAY,HOUR,MINUTE,SECOND,FRACTION(n)}
DEFINE simw char (200), j,i,k INTEGER, ff FLOAT # Здесь объявлены символьная переменная simw длиной 200 байт, # целые i,j,k, и ff - восьмибайтовое с плавающей точкой DATABASE zawod DEFINE doljno RECORD # объявляется запись doljno, состоящая из 4 простых переменных dolzn CHAR(20), # должность zarplmin LIKE kadry.zarplata, zarplmax money(16,2), # зарплата vakansii int # вакансии END RECORD # Здесь заканчивается объявление записи doljno # Переменную можно оъявить с ключевым словом LIKE column_name. # переменная zarplmin получает такой же тип, что и столбец # zarplata таблицы kadry из базы данных zawod DEFINE rrr RECORD LIKE kadry.* # Переменную типа запись тоже можно объявить с ключевым словом # LIKE. Здесь объявлена запись rrr, содержащая элементы, имею- # щие те же самые названия и те же самые типы что и столбцы # таблицы kadry
DEFINE zap RECORD a LIKE kadry.tabnom, b array[8] OF REAL END RECORD, massiw ARRAY[2,15] OF RECORD kolwo INT, tip CHAR(8) END RECORD # massiw объявлен как массив записей. Каждая запись состоит # из двух простых элементов - kolwo и tip
# присвоить значение элементу массива можно так: LET massiw[1,i+2].kolwo = zap.a + LENGTH(massiw[1,i+2].tip)
SELECT kadry.* INTO strkt.* FROM kadry WHERE kadry.tabnom=i+j SELECT * INTO strukt.b THRU strkt.e FROM kadry
DATABASE zawod GLOBALS DEFINE zap RECORD LIKE kadry.* DEFINE ext_count INT . . . END GLOBALS
GLOBALS "progrglob" . . .
В языке 4GL при программировании функций (подпрограмм) используются операторы function. Все аргументы функции должны быть объявлены. Аргументы передаются по значению. Если функция возвращает какие-либо значения, то при вызове ее нужно воспользоваться в операторе CALL предложением RETURNING с перечислением переменных, в которые возвращается значение. Ниже приводится соответствующий фрагмент программы.
FUNCTION stroka(rec) DEFINE rec RECORD i int, st char(256) END RECORD RETURN st clipped,"автопробега" END FUNCTION . . . MAIN . . . CALL stroka(rec1.*) RETURNING simw . . . LET simw=stroka(7,"Привет участникам ") # Если функция возвращает одно значение, то ее имя мож- # но использовать в выражениях. MESSAGE simw . . . END MAIN
ѓЃѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹‚ ѓЉ Привет участникам автопробега ѓЉ ѓЉ ѓЉ ѓЉ ѓЉ ѓЉ ѓЉ ѓѓѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹„
Оператор безусловного перехода действует в пределах модуля.
GOTO metka . . . LABEL metka: . . .
CASE WHEN iscreen=1 current window is w1 WHEN iscreen=2 current window is w2 let iscreen=1 OTHERWISE current window is SCREEN END CASE CASE (a+b) # Другой формат оператора CASE WHEN 1 message "a=",a WHEN 2 message "b=",b END CASE
IF str = "завершить" OR y<0 THEN exit program # Не забывайте в конце каждого условного # оператора ставить END IF. END IF
FOR I= i1 TO 23 let a[i]=0 if b[i]=100 then EXIT FOR END IF END FOR
WHILE ff > 3 or nn="проба" PROMPT "Введите число " for n let i=n+1 message "А у меня ",i,", больше. Вы проиграли." SLEEP 5 RUN "rm *" WITHOUT WAITING END WHILE
Операторы PREPARE и EXECUTE предназначены для динамического (во время выполнения программы) изготовления и выполнения операторов языка SQL (не 4GL !!!).
В приведенном ниже фрагменте в ответ на запрос пользователь сможет ввести с клавиатуры строку с оператором языка SQL (Пусть, например, он введет строку: DROP DATABASE buhgalteriq). Программа изготовит из этой строки настоящий оператор и выполнит его с помощью оператора EXECUTE. Если при выполнении зарегистрирована ошибка, о чем сообщит установленный в отрицательное значение код завершения status, пользователя снова попросят ввести оператор.
DEFINE stroka char(200) MAIN . . . LABEL METK2:PROMPT "введите оператор языка SQL: " FOR stroka WHENEVER ERROR CONTINUE # Включить режим "В случае # ошибки продолжить выполнение # программы" PREPARE st1 FROM stroka # Изготовить оператор из # символьной строки EXECUTE st1 # Выполнить изготовленный оператор IF status<0 THEN ERROR "ошибка номер ", status, " в вашем операторе" GOTO metk2 END IF WHENEVER ERROR STOP # Восстановить режим # "В случае ошибки прервать # выполнение программы" . . . END MAIN
Если запрос к таблице возвращает несколько (больше одной) строк, то для их обработки используется так называемый курсор - указатель во множестве строк, выбранных оператором SELECT. Оператором DECLARE объявляется курсор для запроса, оператором OPEN
этот запрос фактически выполняется и выбранные строки выделяются. Курсор устанавливается на первую из выбранных строк. С помощью оператора FETCH вы можете брать очередную строку, на которую указывает курсор, и помещать ее в свои программные переменные. Курсор после этого смещается на следующую строку.
С помощью конструкции циклической FOREACH имя_курсора ... END FOREACH можно перебрать все строки, выбранные оператором SELECT. Оператор OPEN в этом случае не нужен.
DATABASE zawod DEFINE zap RECORD LIKE kadry.* DECLARE curs1 CURSOR FOR select * from kadry where datarovd>"9/25/1973" # в цикле FOREACH выводим на экран все строки таблицы kadry, # в которых столбец datarovd содержит дату после 25 сентября # 1973 года. FOREACH curs1 INTO zap.* # Берем очередную строку и по- # мещаем ее в запись zap MESSAGE zap.* # Выводим запись zap на экран PROMPT "Еще ?" FOR CHAR c END FOREACH # Конец цикла FOREACH
DATABASE zawod DEFINE z1 ARRAY[100] OF RECORD LIKE kadry.*, counter int DECLARE curs2 CURSOR FOR SELECT * FROM kadry WHERE datarovd<"9/26/1973" OPEN curs2 FOR counter=1 TO 100 FETCH curs2 INTO z1[counter].* # взять очередную строку и поместить ее в следующий элемент # массива z1 IF status=NOTFOUND THEN # если выбранные сроки кончились, закончить цикл EXIT FOR END IF END FOR LET counter=counter-1 MESSAGE "В массив z1 прочитано ",counter, " записей"
DEFINE z2 ARRAY[100] OF RECORD LIKE ceh.*, counter int, simw char(200) PROMPT "допишите оператор SELECT * FROM ceh WHERE " FOR simw IF LENGTH(simw)=0 THEN LET simw="TRUE" END IF LET simw="SELECT * FROM ceh WHERE ", simw CLIPPED PREPARE st2 FROM simw DECLARE cs2 FOR st2 let counter=1 FOREACH cs2 INTO z2[counter].* LET counter=counter+1 IF counter>100 THEN EXIT FOREACH END IF END FOREACH LET counter=counter-1 MESSAGE "В массив z2 прочитано ",counter, " записей"
В любой момент времени на экране терминала существует ТЕКУЩЕЕ окно, через которое и выполняется ввод/вывод вашей программы. С окном связаны используемые при вводе и выводе атрибуты (например, green, revers, underline и т.п.) и номера строк окна, используемых операторами MESSAGE, PROMPT и ERROR для вывода.
При открытии нового окна оно становится текущим и и весь ввод/вывод будет направляться уже в него.
В окно можно вывести экранную форму, которая, представляет собой набор экранных полей, имеющих имена, и в эти поля (из этих полей), обращаясь к ним по имени, можно выводить (вводить) данные с помощью оператора DISPLAY (INPUT). Экранные поля можно
объединять в экранные записи. Описание экранных полей и самой формы располагается отдельно от программы в файле описания экранной формы.
Ниже приведен пример программы, иллюстрирующий работу с окнами.
OPEN WINDOW wind1 AT 2,30 WITH 10 ROWS, 40 COLUMNS ATTRIBUTE(BORDER, REVERSE, MESSAGE LINE FIRST) # текущим окном является wind1 . . . OPEN WINDOW wind2 AT 5,15 WITH FORM "schoolp" ATTRIBUTE(GREEN,PROMPT LINE LAST, MESSAGE LINE LAST, FORM LINE FIRST) # текущим окном является wind2 CLEAR WINDOW wind1 . . . CURRENT WINDOW IS wind1 # текущим окном является wind1 OPEN FORM form1 from "schoolp" # Инициализировать форму form1 # Взяв ее описание из файла # schoolp.frm DISPLAY FORM form1 # Вывести форму form1 в текущее окно # т.е. в wind1
ѓ'ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ' ѓљ ѓ'ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ' окно ѓљ ѓљ ѓљ значение равно 8 ѓљ ...ѓwind1 ѓљ ѓљ ѓ'ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ' ѓљ ѓљ цех [ 2] [литейный ] ѓљ ѓљѓщѓ‹ѓ„ ѓљ ѓљ ѓљ таб.номер [26 ] ѓљ ѓљ окно ѓљ ѓљ ѓљ фамилия [Петров У.Е. ] ѓљ ѓљ ...ѓ wind2 ѓљ ѓљ ѓљ должность [бригадир ] ѓљ ѓљ ѓЉ ѓљ ѓљ ѓљ зарплата [$340 ] ѓљѓщѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ‹ѓ„ѓљ ѓљ ѓљ дата рождения [31.12.1952] ѓљ ѓљ ѓљ ѓљ ѓљ ѓљ ѓљ ѓљ ѓљ ѓ"ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ" ѓљ ѓљ 789 ѓљ ѓљ ѓљ ѓ"ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ" ѓљ ѓљ ѓљ нет таких ѓљ ѓ"ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ"
В результате работы фрагмента программы
let sta_return=podtwervdenie(" В самом деле решили закончить? ") ... function podtwervdenie(stroka) define stroka char(38) , kod_wozwr int open window podtwervdenie AT 11,10 WITH 4 rows, 39 columns ATTRIBUTE(border) display stroka at 4, 2 attribute (reverse) menu " " command key("Y") " Yes " "Действительно Да." let kod_wozwr=1 exit menu command key("N",ESC) " No " "Нет, вернуться обратно." let kod_wozwr=0 exit menu command key("A") " Abort " "Отменить. И кончить." let kod_wozwr=-1 exit menu end menu close window podtwervdenie return kod_wozwr end function
+---------------------------------------+ | : Yes No Abort | |Действительно Да. | | | | В самом деле решили закончить? | +---------------------------------------+
Оператор OPTIONS может установить новые режимы для ввода вывода, если вас не устраивают заданные по умолчанию.
OPTIONS MESSAGE LINE 23, HELP FILE "h4gl.txt", HELP KEY CONTROL-T, DISPLAY ATTRIBUTE(REVERSE, UNDERLINE)
Оператор MESSAGE выводит строку значений на экран на message line. Аргументами MESSAGE могут быть переменные и константы, но не выражения.
let ttmm=CURRENT message "Московское время ", ttmm error "Данных больше нет, прочитанно ", n, " строк"
Оператор PROMPT выводит на экран display-list - список значений переменных и констант, и вводит после этого с клавиатуры значение в указанную вслед за ключевым словом FOR переменную.
PROMPT "Да или нет ?" FOR answer ON KEY (CONTROL-U) LET answer=wozderv() EXIT PROMPT END PROMPT
Оператор DISPLAY выводит данные в поля экранной формы.
DISPLAY a,b,zap[i].nomerceh TO pole1,fscr.* ATTRIBUTE(BOLD)
DISPLAY BY NAME fio, dolvnostx
INPUT kadr.* FROM fio, dolvnostx, nomerceh BEFORE FIELD nomerceh message "Сегодня обслуживаются цеха 5 и 6" sleep 2 message "" AFTER FIELD nomerceh IF kadr.nomerceh > 6 then MESSAGE "Нет такого цеха, повторите" NEXT FIELD NOMERCEH ENF IF END INPUT
Ниже приведен пример программирования подсказки (в процессе интерактивного диалога) с использованием экранного массива. Таблица ceh содержит два столбца: номер цеха и его название. В приведенном фрагменте вызывается функция wyborceh, которая выводит содержимое таблицы ceh в экранный массив. Пользователь передвигает курсор на название нужного ему цеха и нажимает клавишу CR. Подпрограмма определяет номер цеха и возвращает его вызывающей программе.
DATABASE zawod . . . let nc= wyborceh() . . . FUNCTION wyborceh() # Выбор цеха, для внесения изменений DEFINE counter int DEFINE ceharr ARRAY[25] OF RECORD # массив для хранения nomerceh int, # номерцеха данных из таблицы nameceh char(20) # название цеха ceh END RECORD # Открыть окно с рамкой и вывести в него экранную форму cehform OPEN WINDOW cehwind AT 4 ,6 WITH FORM "cehform" ATTRIBUTE(BORDER) # Объявить курсор для выбора содержимого из таблицы ceh DECLARE cehcurs CURSOR FOR SELECT * FROM ceh ORDER BY nomerceh # Выполнить запрос и все выбранные строки поместить в програм- # ный массив ceharr LET counter = 0 FOREACH cehcurs INTO ceharr[counter+1].* LET counter = counter + 1 IF counter >=25 THEN EXIT FOREACH END IF END FOREACH # счетчик counter равен фактическому числу строк выданных в # курсор MESSAGE "Выберите цех и нажмите CR" # Вывести в экранный массив cehscreen в экранной форме cehform # counter первых строк из программного массива ceharr call set_count(counter) DISPLAY ARRAY ceharr TO cehscreen.* ON KEY (CONTROL-M) EXIT DISPLAY END DISPLAY # Прервать показ экранного массива при нажатии клавиши CR # закрыть окно с экранной формой cehform CLOSE WINDOW cehwind let counter=arr_curr() #номер строки массива, #на котором нажато CR RETURN ceharr[counter].nomerceh #номер цеха, #на котором нажато CR END FUNCTION
ѓ'ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ' ѓљ номер цех ѓљ ѓљ [3 ] [токарный ]ѓљ ѓљ [4 ] [гараж ]ѓљ ѓљ [5 ] [конюшня ]ѓљ ѓљ [6 ] [столовая ]ѓљ ѓљ [ ] [ ]ѓљ ѓљ Выберите цех и нажмите CR ѓљ ѓ"ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ›ѓ"
В приведенном выше фрагменте использована экранная форма cehform.per. Ниже приведено ее описание. Это описание должно лежать в файле cehform.per и должно быть откомпилировано компилятором экранных форм INFORMIX'а form4gl.
Описание экранной формы cehform.per
DATABASE zawod SCREEN { номер цех [f00] [f001 ] [f00] [f001 ] [f00] [f001 ] [f00] [f001 ] [f00] [f001 ] } TABLES ceh ATTRIBUTES f00 = ceh.nomerceh; f001 = ceh.nameceh; INSTRUCTIONS screen record cehscreen[5] (ceh.*) END
function kalkulator() # Калькулятор define wyravenie, kalkulator char(64), kolichestwo int define stroka_kalkulatora char(200) define beep char(1) let beep=ascii 7 open window kalkulator at 2,2 with form "kalkulator" attribute(border, form line first) input by name wyravenie, kalkulator without defaults before field kalkulator let stroka_kalkulatora= "select count(*),",wyravenie," from systables" whenever error continue prepare kalkulqtor_operator from stroka_kalkulatora if status<0 then display beep to kalkulator display "Неправильное выражение" to kalkulator next field wyravenie end if declare kalkulator cursor for kalkulqtor_operator foreach kalkulator into kolichestwo, kalkulator if status<0 then display beep to kalkulator display "Неправильное выражение" to kalkulator next field wyravenie end if end foreach whenever error stop display kalkulator to kalkulator next field wyravenie end input close window kalkulator return kalkulator end function
DATABASE formonly SCREEN { Калькулятор. Чтобы закончить нажмите ESC [wyravenie ] [kalkulator ] } ATTRIBUTES wyravenie =formonly.wyravenie; kalkulator=formonly.kalkulator; END