Genero FGL 学习
- Genero FGL 简介
- Genero FGL 开发(编译、连接、执行)
- 第一个程序 Hello World
- 变量与运算符
- 变量定义(DEFINE)
- 预定义变量
- 变量集合(RECORD )
- 数据结构(TYPE)
- 变量赋值(LET)、初始化(INITIALIZE)
- 常数(CONSTANT)
- 运算符(operators)
- 全局变量(GLOBALS)
- 变数的生命周期(LOCALE、MODULE、GLOBAL)
- 控制输出格式(USING)
- 函数、流程控制
- IMPORT 引入链接库(跳过)
- SCHEMA / DATABASE 声明数据库
- MAIN 函数与设定区块
- FUNCTION 函数
- 报表结构 REPORT 函数(跳过)
- IF
- CASE
- FOR
- WHILE
- CONTINUE
- EXIT
- TRY…CATCH
- SLEEP
- LABEL 和 GOTO(❌)
- 常用的内置函数(built-in functions)
【实习】T100学习笔记
Genero FGL 简介
Genero FGL 语言,为 Four J’s(http://www.4js.com)于 2004 年所发表。整体结构为承袭 INFORMIX 数据库的 4GL 管理语言而来,即 INFORMIX-4GL 。
INFORMIX-4GL 属于第四代架构的语言,其优点在于构成程序的语法和英文近似,可以大幅减少学习的时间,但仅能使用于 INFORMIX 数据库的控制上。
Four J’s 取其优点,为了能够应用于更多后端数据库,开发出 FGL 语言,并因应图型化,改版为Genero FGL,有以下特点:
- 切分为 Client、Server 架构(GDC 与 fgl),增进执行效率
- 以 XML Bsae 做为 Client 及 Server 端数据传递的架构
- 支援更多不同平台(OS)及数据库系统
- 可在运行时间动态调整画面输出的格式(Layout Styles)
- 引入基本的 对象(Object) 概念
在 Genero FGL 语言架构中,将 程序逻辑、画面 视为不同的项目,分别撰写。
Program(程序)= MODULE(逻辑代码) + FORM(画面代码)
由上图可知:
- Program 可由许多的 Module 与 Form 构成。
- 单一的 4GL 由一个或一个以上的 Function、Report 组成。
- 一个完整的 Program 中,必需指定一个『Main』作为程序执行入口。
Genero FGL 开发(编译、连接、执行)
当程序及画面编写完成后,还需经过编绎(Compile)、连结(Link),才能够被执行(Execute)。
编译需要 Genero Development License,连结及执行需要 Genero Runtime License。
编译流程
程序的原始文件后缀名为 .4gl
,经过编译后会产生后缀为 .42m 的文件。
程序(Program)的编译:fglcomp [编译参数] 待编译文件名[.4gl]
编译前处理(preprcessor) ????????
GeneroFGL 可直接以 文字编辑程序如Vim 进行 文字格式(per 档)的开发;
也可使用 Genero Studio 内的『Form Design』功能开发 XML格式(4fd 档)的画面;
编译后都会产生后缀名为 .42f 的文件。
文字格式(per文件)编译:fglform [编译参数] 待编译文件名[.per]
XML 格式(4fd文件)编译:gsform [编译参数] 待编译文件名[.4fd]
连结流程
若该程序不需要使用其他 4gl 提供的功能,可以略过连接的程序,直接执行作业。
若该作业被切分成许多子程序,则可在执行连接前,先将子程序打包成一组动态链接函式库(Dynamic Link Library),后缀名为 .42x
,打包完成后再与原始作业进行连接。
连接语法:fgllink -o 连结后的完整文件名 待连结文件1 [待连结文件2] ….
示例:
fgllink -o test.42x test1.42m test2.42m test3.42m
(生成42x文件)fgllink -o program.42r test.42x main.42m others.42m
(生成42r文件)
注:画面文件不需要执行链接。
执行程序
执行程序前,需先开启使用者端的 Genero 桌面客户端软件(GDC:Genero Desktop
Client),以令主机端的 fglrun
可以与客户端的『GDC』进行沟通。
执行指令:fglrun [执行参数] 执行文件名[.42r]
当程序未使用到其他外部资源时,也可以直接执行含 MAIN 函数的 .42m
文件。
程序执行的过程中,所有逻辑运算均于主机端执行,只有画面异动数据会以连续的 XML 封包传递到客户端,经过 GDC 解译重组后,与使用者进行互动。
fglrun -V
可以查看 fgl 的版本号。
第一个程序 Hello World
MAINDISPLAY "hello world!!"
END MAIN
4GL中的 注释:
{ }
可以将某个范围做备注#
或--
将某行做备注
变量与运算符
变量类型
变量定义(DEFINE)
直接定义变量:DEFINE 变量名 变量类型
定义变量对应数据库字段:DEFINE 变量名 LIKE 数据表.数据字段
# 直接定义 employee_no 变量, 类型是 CHAR(10)
DEFINE employee_no CHAR(10)# 定义 p_employee_no 和数据库对应
DEFINE p_employee_no LIKE employee_file.employee_no,p_team_no SMALLINT,p_join_date, p_birthday DATE
预定义变量
Genero 的预定义变量及用途,可以直接使用。
变量集合(RECORD )
直接定义变量集合(Records)
例:直接定义 rec 这个 Records 中的各个变量类型。
DEFINE rec RECORD # 定义rec这个变量,它是个RECORD,是个变量集合id INTEGER,name VARCHAR(100),birth DATE
END RECORD
定义变量集合对应数据库字段
例:定义 cust01 这个 Record 的变量与数据库中的 customer 这个 table 的字段有相同的名称及数据类型,有以下两种方法。
DATABASE example_databaseMAINDEFINE cust01 RECORD LIKE customer.*
END MAIN
DATABASE example_databaseMAINDEFINE cust02 RECORDid LIKE customer.id,name LIKE customer.name,birth LIKE customer.birth,sales LIKE salesman.nameEND RECORD
END MAIN
数据结构(TYPE)
使用数据结构TYPE:[PRIVATE|PUBLIC] TYPE 变量名 变量类型
TYPE customer RECORDcust_num INTEGER,cust_name VARCHAR(50),cust_addr VARCHAR(200)
END RECORD
DEFINE c customer
???????
PUBLIC TYPE rpt_order RECORDorder_num INTEGER,store_num INTEGER,order_date DATE,fac_code CHAR(3)
END RECORDMAINDEFINE o rpt_order #使用数据结构 rpt_order 并取名为 oDECLARE order_c CURSOR FORSELECT order_num, store_num, order_date, fac_code FROM ordersSTART REPORT order_listFOREACH order_c INTO o.*OUTPUT TO REPORT order_list(o.*)END FOREACHFINISH REPORT order_list
END MAIN
变量赋值(LET)、初始化(INITIALIZE)
变量赋值:LET varibale = expression
MAINDEFINE c1, c2 CHAR(10)LET c1 = "Genero"LET c2 = c1
END MAIN
注:若变量形态为 CHAR 和 VARCHAR 时,指定给予的值有差异。
初始化变量集合(INITIALIZE)
用于初始化一组 RECORD 变量为 NULL,或者是初始化为数据库 Table 的默认值:
INITIALIZE 变量串行 { LIKE 字段串行 | TO NULL }
MAINDEFINE cr RECORD LIKE customer.*INITIALIZE cr.cust_name TO NULL #初始化cr的cust_name字段为NULLINITIALIZE cr.* LIKE customer.* #初始化cr的变量为customer表的值
END MAIN
常数(CONSTANT)
一些系统预定义常数:
定义常数:CONSTANT constant_id [data_type] = value
注:定义常数可以不指定类型;系统会自动判断类型,若指定错误则会纠正。
CONSTANT c1 = "Drink" -- 自行定义为 STRING
CONSTANT c2 = 4711 -- 自行定义为 INTEGER
CONSTANT c3 SMALLINT = 12000 -- 自行纠正为 INTEGER
CONSTANT c4 CHAR(10) = "abc" -- 按照定义为 CHAR(10)
运算符(operators)
比较运算符
`
MAINDEFINE a,b INTEGERLET a = b := 10DISPLAY a, b -- 10 10
END MAIN
MAINDEFINE a,b INTEGERLET a = b = 10DISPLAY a,b -- 0 0
END MAIN
逻辑运算符
数值运算符
MAINDEFINE i,j SMALLINTLET i = 9LET j = 2DISPLAY i + j --DISPLAY 11DISPLAY i - j --DISPLAY 7DISPLAY i * j --DISPLAY 18DISPLAY i / j --DISPLAY 4.5DISPLAY j ** i --DISPLAY 512DISPLAY i mod j --DISPLAY 1
END MAIN
字符串运算符
?????????
说明:表达式[start,end]
表示从字符串中取出子字符串,此表示方式仅能用在 CHAR 或
VARCHAR 上,若变量型态为 STRING,则参照如下范例:
MAINDEFINE i,j STRINGLET i = "T100"LET j = i.subString(1, 4)DISPLAY j --T100DISPLAY i.subString(1, 4) --T100
END MAIN
关联语法(Associative syntax) 运算符
日期运算符
对话框处理 (Dialog handling)运算符
全局变量(GLOBALS)
语法一:直接写定 GLOBALS 区块
GLOBALSdeclaration-statement[,...]
END GLOBALS
语法二:读入已写好的共同配置文件(外部档案)
GLOBALS "filename"
变数的生命周期(LOCALE、MODULE、GLOBAL)
LOCAL变量(Local Variables)
- 定义位置:定义在 Module 中的函式里 (MAIN、FUNCTION 等)
- 生命周期:只属于该定义的函式使用,离开此函式即不能再使用。
MODULE变量(Module Variables)
- 定义位置:Module 中,但不被任何的函式包围。
- 生命周期:为该 Module 中的共享变数。
GLOBAL变量(Global Variables)
- 定义位置:由 GLOBALS 及 END GLOBALS 所包围的变数。
- 生命周期:使用的所有 MODULE 的共享变量。
SCHEMA dsGLOBALSDEFINE g_employee CHAR(10) --GLOBAL
END GLOBALSDEFINE g_tty CHAR(32) --MODULEMAINDEFINE answer CHAR(1) --LOCAL
END MAINFUNCTION ins_employee()DEFINE flag CHAR(1), --LOCALchange SMALLINT --LOCAL
END FUNCTION
控制输出格式(USING)
针对 数值或日期 设定其 显示格式,设定时注意 溢出(overflow) 的问题。
数值格式标志
日期格式标志
???????????????????????
MAINDEFINE i,j SMALLINTLET i = 12345LET j = -12345DISPLAY iDISPLAY jDISPLAY i USING"*******"DISPLAY j USING"*******"DISPLAY i USING"&&&&&&&"DISPLAY j USING"&&&&&&&"DISPLAY i USING"#######"DISPLAY j USING"#######"DISPLAY i USING"<<<<<<<"DISPLAY j USING"<<<<<<<"DISPLAY i USING"-------"DISPLAY j USING"-------"DISPLAY i USING"+++++++"DISPLAY j USING"+++++++"DISPLAY i USING"$$$$$$$"DISPLAY j USING"$$$$$$$"DISPLAY i USING"(######)"DISPLAY j USING"(######)"DISPLAY i USING"###,###.&&"DISPLAY j USING"###,###.&&"
END MAIN
MAINDISPLAY TODAYDISPLAY TODAY USING "yyyy-mm-dd"DISPLAY TODAY USING "yy-mm-dd"DISPLAY TODAY USING "yy-mmm-ddd"
END MAIN
函数、流程控制
IMPORT 引入链接库(跳过)
SCHEMA / DATABASE 声明数据库
使用 SCHEMA
声明数据库时,只会在编译过程中使用到声明的功能,执行程序时并不会与数据库进行实质联机。
使用 DATABASE
声明数据库时,编译时功能相同,但执行时即会链接数据库。
SCHEMA ds #声明数据库dsMAINDEFINE lc_zz01 LIKE zz_file.zz01SELECT zz01 INTO lc_zz01 FROM zz_file WHERE zz01='tiptop'
END MAIN
???????????
注:使用 SCHEMA
指令,因此并未于实际登入数据库,执行时回报如下:
Program stopped at 'test_schema.4gl', line number 5.
SQL statement error number -1803 (-1). Connection does not exist.
MAIN 函数与设定区块
MAIN 函数是程序执行的入口,一个完整可执行的程序只能有一个 MAIN 函数。
最简单的完整作业:
MAINDISPLAY "hello world!"
END MAIN
MAIN 函数可简单写成如上,也可增加一些 设定区块。
DEFER 设定 可定义程序是否要拦截『当使用者按下中断(interrupt)或离开(quit)键』时所送出的系统讯号:DEFER {INTERRUPT | QUIT}
OPTIONS 设定 可变更系统默认的选项
Exceptions 设定 定义当遇到 SQL 错误时,系统要采什么操作。
语法格式:WHENEVER [ANY] ERROR { CONTINUE | STOP | CALL function | GOTO label }
复杂的 MAIN 函数示例:
MAINOPTIONS #改变一些系统默认值INPUT NO WRAP, #输入的方式:不打转# FORM LINE FIRST + 2, #画面开始的位置# MESSAGE LINE LAST, #讯息显示的位置# PROMPT LINE LAST, #提示讯息的位置FIELD ORDER FORM #整个画面会依照 p_per 所设定的字段顺序DEFER INTERRUPTWHENEVER ERROR STOP #当发生 SQL Error 时即停止程序DISPLAY “Change Exception!”WHENEVER ERROR CALL chk_err #此处的 CALL 是没有括号的END MAINFUNCTION chk_err( )DISPLAY “Error Happened!”
END FUNCTION
FUNCTION 函数
语法:[PUBLIC | PRIVATE ] FUNCTION function_name( [arg [ , … ] ] )
当设定为 PRIVATE
时,只限本 4gl 文件内使用;连结时在 42x 或 42r 内无法看到该函数。
MAINCALL say_hello_public()CALL say_hello_private()
END MAINFUNCTION say_hello_public()DISPLAY "Hello, world!"
END FUNCTIONPRIVATE FUNCTION say_hello_private()DISPLAY "Hello, Private!"
END FUNCTION
CALL
执行指定的函数,若有回传值,以 RETURNING
接回。
语法格式:CALL function ( [ parameter [,...] ] ) [ RETURNING variable [,...] ]
RETURN
返回函数所需的变量值,并停止此函式的执行。
回传单一值:
MAINDEFINE var1 CHAR(10)DEFINE var2 CHAR(2)LET var1 = foo()DISPLAY "var1 = " || var1 -- var1 = HelloCALL foo() RETURNING var2DISPLAY "var2 = " || var2 -- var2 = HeDISPLAY "foo() = " foo() -- foo() = Hello
END MAINFUNCTION foo()RETURN "Hello"
END FUNCTION
回传单一值(布尔):
MAINIF foo() THENDISPLAY "Choice is OK!”END IF
END MAINFUNCTION foo()RETURN TRUE
END FUNCTION
回传多个值: 可用 RETURNING
接收
MAINDEFINE var1 CHAR(15)DEFINE var2 CHAR(15)CALL foo() RETURNING var1, var2DISPLAY var1, var2
END MAINFUNCTION foo()DEFINE r1 CHAR(15)DEFINE r2 CHAR(15)LET r1 = "return value 1"LET r2 = "return value 2"RETURN r1, r2
END FUNCTION
文件之间互相调用:test1.4gl 调用 test2.4gl 中的函数,并且用命令行执行 test3.4gl
test1.4gl
MAINDISPLAY "MAIN FUNCTION"CALL a1()CALL a2()RUN "fglrun test3" # 利用RUN执行命令行
END MAINFUNCTION a1()DISPLAY "SUB FUNCTION a1()"
END FUNCTION
以上程序段会呼叫另外两个 Function,并且利用 RUN 指令执行一道 unix 指令
test2.4gl
FUNCTION a2()# a2 functionDISPLAY "SUB FUNCTION a2()"
END FUNCTION
test3.4gl
MAINDISPLAY "This is test3.4gl"
END MAIN
报表结构 REPORT 函数(跳过)
这是一种专门用来设定报表打印格式的函数,后续章节有详细的介绍。
IF
语法:??????MATCHES 支持正则表达式?
IF condition THENstatement[...]
ELSEstatement[...]
END IF
MAINDEFINE name CHAR(20)LET name = "John Smith"IF name MATCHES "John*" THENDISPLAY "The first name is too common to be displayed."IF name MATCHES "*Smith" THENDISPLAY "Even the last name is too common to be displayed."END IFELSEDISPLAY "The name is " , name , "."END IF
END MAIN
CASE
语法1格式:适用于判断式比较简单,如只需判断1个数字
CASE expression-1WHEN expression-2{ statement | EXIT CASE }[...]OTHERWISE{ statement | EXIT CASE }[...]
END CASE
MAINDEFINE v CHAR(10)LET v = "C1"CASE vWHEN "C1"DISPLAY "Value is C1"WHEN "C2"DISPLAY "Value is C2"WHEN "C3"DISPLAY "Value is C3"OTHERWISEDISPLAY "Unexpected value"END CASE
END MAIN
语法2格式:适用于判断式较复杂
CASEWHEN boolean-expression{ statement | EXIT CASE }[...]OTHERWISE{ statement | EXIT CASE }[...]
END CASE
MAINDEFINE v CHAR(10)LET v = "C1"CASEWHEN ( v="C1" OR v="C2" )DISPLAY "Value is either C1 or C2"WHEN ( v="C3" OR v="C4" )DISPLAY "Value is either C3 or C4"OTHERWISEDISPLAY "Unexpected value"END CASE
END MAIN
语法2注意:若有两种情况成立,系统仅会走第一条符合条件的路径。
下列代码只会显示:a is ok
MAINDEFINE a,b INTEGERLET a = b := 10CASEWHEN a = 10 DISPLAY " a is ok "WHEN b = 20 DISPLAY " b is ok "OTHERWISE DISPLAY " nothing is ok "END CASE
END MAIN
FOR
语法:默认情况 STEP = 1
FOR counter = a TO b [ STEP value ]statement[...]
END FOR
MAINDEFINE i, i_min, i_max INTEGERLET i_min = 1LET i_max = 10DISPLAY "Look how well I can count from " , i_min , " to " , i_maxDISPLAY "I can count forwards..."FOR i = i_min TO i_max # 正序DISPLAY iEND FORDISPLAY "... and backwards!"FOR i = i_max TO i_min STEP -1 #反序DISPLAY iEND FOR
END MAIN
WHILE
语法:执行程序直到条件不成立为止。
WHILE b-expressionstatement[...]
END WHILE
MAINDEFINE a,b INTEGERLET a = 20LET b = 1WHILE a > bDISPLAY a , " > " , bLET b = b + 1END WHILEEND MAIN
CONTINUE
语法:跳出本次循环。
CONTINUE { FOR | FOREACH | MENU | CONSTRUCT | INPUT | WHILE }
MAINDEFINE i INTEGERLET i = 0WHILE i < 5LET i = i + 1DISPLAY "i = " || iCONTINUE WHILEDISPLAY "This will never be displayed !"END WHILE
END MAIN
EXIT
语法:离开控制段。
EXIT { CASE | FOR | MENU | CONSTRUCT | FOREACH | REPORT | DISPLAY | INPUT | WHILE | PROGRAM }
MAINDEFINE i INTEGERLET i = 0WHILE TRUEDISPLAY "This is an infinite loop. How would you get out of here ?"LET i = i + 1IF i = 100 THENEXIT WHILEEND IFEND WHILEDISPLAY "Well done."
END MAIN
TRY…CATCH
针对重要 SQL 指令可以预先设置专用的错误处理指令。
语法:
TRY[待侦测是否会发生问题的程序段落]
CATCH[处理问题或回报讯息的程序段落]
END TRY
示例:
MAINLET lc_a = ARG_VAL(1)TRYDATABASE lc_aCATCHDISPLAY lc_a,' not in fglprofile, number:', SQLCA.SQLCODEEND TRY
END MAIN
SLEEP
语法:程序依指定秒数暂停。
SLEEP seconds
注意:如果 seconds < 0 或 seconds IS NULL,则程序不会停止。
MAINDISPLAY "Please wait 5 seconds..."SLEEP 5 #暂停5秒DISPLAY "Thank you."
END MAIN
LABEL 和 GOTO(❌)
为了程序的易读性和结构性,请不要使用该指令。
MAINDISPLAY "Before GOTO"GOTO: label_id1DISPLAY "Never Been Displayed"LABEL label_id1:DISPLAY "After GOTO"
END MAIN