1 - Linguagem 4GL
O TotvsTec 4GL surgiu com base nas linguagens nativas 4GL do banco de dados Informix e também com base na linguagem 4JS da IBM.
A linguagem 4GL se caracteriza por acessar as bases de dados através de uma linguagem comum chamada SQL (extensão do standard SQL-Structured Query Language) que não será vista neste manual. Esta filosofia permite uma maior facilidade na formação de programadores bem como uma separação efetiva entre o nível físico e lógico da exploração da base de dados tal como é definido na teoria das bases de dados relacionais.
O desenvolvimento e compilação das aplicações desenvolvidas em 4GL são realizadas via ferramenta de desenvolvimento Totvs Developer Studio disponível para download na central de download da página de suporte da TOTVS.
Quando compilados, todos os arquivos de código fonte tornam-se unidades de inteligência básicas, chamadas de RPO´s (Repository Protheus Objects). Esses RPO´s são mantidos em um repositório e carregados dinamicamente pelo servidor de aplicação (Application Server) para a execução. No entanto, não existe link ou união física do código compilado a um determinado ambiente ou aplicação, o que permite executar funções criadas em 4GL em qualquer ponto do ambiente ERP.
2 - Estrutura de um programa
Um programa é constituído por funções e declarações. A primeira função a ser executada é a função "MAIN", e, tem de existir em todos os programas.
Fonte olamundo.4gl MAIN |
As várias funções podem estar distribuídas por vários fontes a que se resolveu chamar de módulos.
2.1 Fluxo de um programa
Tal como nas linguagens de terceira geração, um programa é executado de acordo com instruções de controle de fluxo, em todas as funções exceto nos reports (os reports possuem uma estrutura de definição).
Para controlar o fluxo do programa foram implementadas diversas instruções de controle de fluxo das linguagens tradicionais (while, if, case, etc.), e, algumas com instruções específicas desta linguagem (Menus, prompts, etc.), algumas das quais não são procedurais (ex: MENU, INPUT, etc.).
O 4GL é ao mesmo tempo de definição e procedural. Como exemplo de definição temos os reports e as instruções de entrada/saída formatada, e, como exemplo de procedural a estrutura geral das funções.
2.2 Tipos e conversões de tipos
Como linguagem de gerenciamento de base de dados, o 4GL reconhece as estruturas representáveis na base de dados, e, algumas de maior complexidade. Os tipos de dados complexos, são declarados por composição de tipos elementares. Em 4GL os tipos de dados elementares existentes utilizados no produto LOGIX são:
SMALLINT
Números inteiros pequenos, uma variável deste tipo pode receber números inteiros entre –32767 e 37767.
INTEGER
Números inteiros entre -2,147,483,647 e +2,147,483,647.
DECIMAL[(m,[,n])]
Números decimais com “m” algarismos, sendo que “n” algarismos são para formar o número de casas decimais. Por exemplo, para DECIMAL(14,2), possui 14 dígitos significativos, sendo 2 deles para formar o número de dígitos a direita da vírgula, ou seja, são 12 dígitos inteiros e 2 decimais. Uma variável do tipo decimal não pode exceder 32 dígitos significativos.
CHAR(n)
Alfanumérico de comprimento “n”. Uma variável do tipo CHAR pode ter o tamanho que varia de 1 a 32767.
DATE
Tipo equivalente a uma data, é configurável através da variável de ambiente DBDATE. Internamente, este tipo é armazenado como inteiro, no formato juliano (nº de dias desde 31 de dezembro de 1899). O formato padrão do DBDATE é “mdy4/” ou seja, “mm/dd/yyyy”, onde mm é o mês (1 a 12), dd (1 a 31) é o dia e yyyy o ano (0001 a 9999). O padrão utilizado na Logocenter é “dmy4/”, ou seja, “dd/mm/yyyy”.
Existem ainda dois tipos, compostos dos acima descritos.
ARRAY tipo
Representa uma variável composta por vários elementos do mesmo “tipo”, denominado como vetor. Uma variável deste tipo pode ser definida com 1, 2 ou 3 dimensões de tamanhos distintos entre si, mas sendo todas do mesmo tipo. O tamanho de um array pode variar de 1 até um tamanho limitado ao volume de memória disponível no servidor de aplicação em uso. Uma variável do tipo ARRAY é uma variável estática, ou seja, a memória consumida será sempre conforme o tipo e o número de elementos, podendo ser bi-dimensional se houver necessidade.
RECORD
Representa uma variável composta por diferentes variáveis de qualquer tipo, incluindo os tipos simples (DATE, CHAR, SMALLINT, INTEGER, etc) e os tipos compostos (ARRAY e RECORD). Assemelha-se ao conceito de variável do tipo STRUCT da linguagem C.
DATETIME
O tipo de dados DATETIME define uma instância de tempo, expressado como uma data do calendário e uma hora do dia. As unidades do tempo que o valor de DATETIME armazena é você quem determina; a precisão pode variar de um ano até uma fração de um segundo. Internamente, os valores são armazenados no formato DECIMAL que representam uma seqüência contígua de valores de unidades de tempo.
A especificação de escala de precisão é chamada de qualificador do DATETIME. Este qualificador é utilizado no formato do "primeiro ao último" para declarar variáveis e campos de tela. Você deve substituir um ou dois destes qualificadores para o primeiro e o último termo.
Qualificador | Unidade de tempo / faixa de valores |
YEAR | Um ano, variando de 0001 até 9999. |
MONTH | Um mês, variando de 1 a 12. |
DAY | Um dia, variando de 1 a 31. |
HOUR | Uma hora, variando de 0 a 23. |
MINUTE | Um minuto, variando de 0 a 59. |
SECOND | Um segundo, variando de 0 a 59. |
FRACTION(escala) | Um décimo de fração de um segundo, com uma escala de até 5 dígitos, sendo que a escala padrão é de 3 dígitos. |
O qualificador especificado por último não pode representar uma unidade de tempo maior do que o primeiro. Assim, o YEAR TO SECOND ou HOUR TO HOUR são válidos, mas o DAY TO MONTH resulta em um erro do compilador, porque o valor para o último qualificador (aqui MONTH) especifica uma unidade de tempo maior do que o DAT, o primeiro qualificador.
O formato de uma variável do tipo DATETIME é “yyyy-mm-dd hh:mm:ss.fffff”, onde yyyy é o ano, mm é o mês, dd é o dia, hh é a hora, mm é os minutos, ss é os segundos e fffff é a fração de segundos.
2.3 Variáveis
Uma linguagem, deve possuir sempre alguma forma de armazenar em memória uma informação necessária para execução dos programas. Tal como nas linguagens de terceira geração, no 4GL existem variáveis.
Uma variável pode ser de qualquer dos tipos definidos anteriormente.
A declaração de variáveis é sempre efetuada depois da palavra chave "DEFINE". Uma variável declara-se da seguinte forma:
DEFINE nome_da_variavel TIPO |
No segundo caso cria-se uma nova variável cuja estrutura condiz com a estrutura de uma coluna de uma tabela do banco de dados. Sempre que uma variável for declarada usando a instrução LIKE, deve existir no início do módulo (fonte .4gl) uma instrução DATABASE que indica o banco de dados a ser usado para obter informações de definição das colunas utilizadas na declaração de variáveis.
Um caracter ou uma faixa de caracteres (substring) pertencentes a uma variável do tipo CHAR pode ser acessado utilizando o operador “[]” (colchetes), assim:
l_texto[1] |
No primeiro caso está se obtendo o primeiro caracter da variável l_texto. Já no segundo caso, está se obtendo 4 caracteres da variável l_texto, referente a posição inicial 2 até a posição 5.
Se a variável for um DATETIME deve-se declarar da seguinte forma:
nome_do_datetime DATETIME qualificador_inicial TO qualidificador_final |
Para uma variável DATETIME que tem o formato que vai do ano até 5 dígitos de fração de segundo, deve-se definir assim:
l_datahora DATETIME YEAR TO FRACTION(5) |
Se a variável for um RECORD deve-se apenas declarar da seguinte forma:
nome_do_record RECORD OU nome_do_record RECORD LIKE tabela.* |
No segundo caso é criada uma estrutura com elementos idênticos a todas as colunas de uma tabela do banco de dados corrente.
Um elemento de uma variável tipo RECORD pode ser acessado através do operador "." (ponto), assim:
lr_dados.codigo
OU
lr_dados.*
|
Este representa o elemento “codigo” do RECORD “lr_dados”. A notação ".*" pode ser usada em alguns contextos para indicar todos os elementos de um RECORD.
Se a variável elementos das variáveis tipo ARRAY podem ser acessadas através do operador [] (colchetes) e dá indicação do número do elemento a que se quer ter acesso, assim:
la_dados[10]
|
Neste caso representa o 10º elemento do ARRAY la_dados.
Se a variável for um array deve ser declarada da seguinte forma:
nome_da_variavel ARRAY[m,n,...] OF tipo
|
Os elementos das variáveis tipo ARRAY podem ser acessadas através do operador [] (colchetes) e dá indicação do número do elemento a que se quer ter acesso, assim:
la_dados[10]
|
Neste caso representa o 10º elemento do ARRAY la_dados.
Para acessar um elemento de uma variável do tipo ARRAY do tipo RECORD utiliza-se o “.” (ponto) após a determinação da posição desejada do ARRAY, assim:
la_dados[5].codigo
|
Neste caso, representa o valor da variável “codigo” pertencente ao 5º elemento do ARRAY la_dados.
Para acessar um conjunto de caracteres de uma variável do tipo CHAR, sendo esta um componente de um ARRAY do tipo RECORD, utiliza-se o ponto após a determinação da posição desejada do ARRAY e o operador “[]” (colchetes) para determinar o conjunto de caracteres desejados da variável tipo CHAR, assim:
la_dados[3].descricao[5,10]
|
Neste caso está se obtendo o conjunto de caracteres da posição 5 até a 10 da variável descrição pertencente ao 3º elemento da variável la_dados (ARRAY do tipo RECORD).
As variáveis podem ser locais a uma função, globais a um módulo (modulares) ou programa (globais).
O escopo de referência de uma variável é determinado pelo lugar em que a cláusula DEFINE aparece no fonte .4gl. Os identificadores de variáveis podem ser locais, modulares, ou (em alguns casos) globais em seu escopo:
1.3.1 Variáveis locais
DEFINE nome_da_variável tipo [, ...]
|
As variáveis locais precisam ser declaradas no início de cada função e somente são reconhecidas dentro do escopo da função em que foram declaradas.
varlocal.4gl MAIN DEFINE l_inteiro INTEGER
LET l_inteiro = 1
CALL mostra_local() CALL mostra_local_agora(l_inteiro) END MAIN
FUNCTION mostra_local() DEFINE l_inteiro INTEGER
DISPLAY “INTEIRO: “, l_inteiro SLEEP 2 END FUNCTION
FUNCTION mostra_local_agora(l_inteiro) DEFINE l_inteiro INTEGER
DISPLAY “INTEIRO AGORA: “,l_inteiro SLEEP 2
RESULTADO: INTEIRO: 0 INTEIRO AGORA: 1
|
Exemplo com variáveis locais
Se declarar duas variáveis com o mesmo nome em duas funções distintas, cada variável somente será reconhecida na função de declaração. Concluindo, as variáveis com o mesmo nome localizadas em funções diferentes são variáveis diferentes.
1.3.2 Variáveis globais
GLOBALS "fonte.4gl"
OU
GLOBALS {instrução DEFINE} END GLOBALS}
|
Definição genérica de uma declaração global
Como se pode ver acima, a instrução GLOBALS tem duas formas possíveis. Na primeira indica-se o nome de um fonte que contém um conjunto de instruções DEFINE e uma instrução DATABASE. A vantagem deste sistema é o conhecimento das variáveis globais em todos os módulos sem necessidade de declará-las em cada um deles.
Da outra forma, a instrução GLOBALS é constituída por uma instrução DEFINE.
varglob1.4gl GLOBALS DEFINE g_inteiro INTEGER END GLOBALS
MAIN LET g_inteiro = 5
DISPLAY "inteiro VARGLOB1: ",g_inteiro SLEEP 2
CALL mostra_varglob2()
DISPLAY "VARGLOB1 após VARGLOB2: ",g_inteiro SLEEP 2 END MAIN
varglob2.4gl GLOBALS DEFINE g_inteiro INTEGER END GLOBALS
FUNCTION mostra_varglob2() DISPLAY "inteiro VARGLOB2: ",g_inteiro SLEEP 2 LET g_inteiro = 10 END FUNCTION
RESULTADO: inteiro VARGLOB1: 5 inteiro VARGLOB2: 5 VARGLOB1 após VARGLOB2:: 10 |
Exemplo com variáveis globais
No programa dado como exemplo não havia grande necessidade de declarar as variáveis como globais. Á medida que for programando pode-se sentir a necessidade de utilizar este tipo de variável.
1.3.3 Variáveis modulares
DEFINE nome_da_variável tipo [, ...]
|
As variáveis modulares tem que ser declaradas no inicio do fonte, fora do escopo de definição de variáveis globais (GLOBALS..END GLOBALS) e fora do escopo de qualquer função (MAIN, FUNCTION, REPORT). Variáveis modulares somente são reconhecidas dentro do escopo do fonte (módulo) em que foram declaradas.
varmod1.4gl DEFINE m_inteiro INTEGER
MAIN LET m_inteiro = 1
DISPLAY “inteiro VARMOD1: “,m_inteiro SLEEP 2
CALL mostra_varmod2()
DISPLAY "VARMOD1 após VARMOD2: ",g_inteiro SLEEP 2 END MAIN
varmod2.4gl DEFINE m_inteiro INTEGER
FUNCTION mostra_varmod2() DISPLAY “inteiro VARMOD2: “,m_inteiro SLEEP 2
LET m_inteiro = 3 END FUNCTION
RESULTADO inteiro VARMOD1: 1 inteiro VARMOD2: 0 VARMOD1 após VARMOD2: 1
|
Exemplo com variáveis modulares
Se declarar duas variáveis modulares com o mesmo nome em dois fontes (módulos) distintos, cada variável só vai ser reconhecida no fonte onde foi declarado. Efetivamente as variáveis com o mesmo nome localizadas em fontes diferentes são variáveis diferentes.
1.3.4 Atribuições, comparações e inicializações
Uma das operações básicas a realizar sobre variáveis é a atribuição.
No 4GL para atribuir a uma variável um valor constante, expressão ou conteúdo de outra variável, utiliza-se o seguinte formato:
LET variável = {expressão | variável | constante}
|
Definição genérica da atribuição
No 4GL pode-se inicializar uma variável de qualquer tipo básico. As variáveis compostas só podem ser inicializadas através dos seus elementos constituintes.
afecta.4gl DATABASE livros
MAIN DEFINE x INTEGER, y CHAR(20), z DECIMAL(5,2), lr_livros RECORD LIKE livros.*, lr_livros2 RECORD LIKE livros.*
LET x = 69 LET y = "Uma string" LET z = 120.596
DISPLAY "X tem ", x,", Y tem ", y,"e Z = ",z
SELECT * INTO lr_livros.* FROM livros WHERE numero = x
LET lr_livros2.* = lr_livros.* DISPLAY lr_livros2.numero, lr_livros.nome END MAIN
RESULTADO: X tem 69, Y tem Uma string e Z = 120.60 69 nome do livro 69
|
Inicializações de variáveis
Conforme se viu, inicializou-se variáveis de diversos tipos elementares e compostos. Nos tipos compostos, o número e tamanho das variáveis elementares que dele fazem parte tem de ser iguais.
Nesta linguagem, não há diferença entre o operador "=" usado na inicialização e na comparação. Este fato deve-se a identificação destas operações serem efetuadas por intermédio do contexto em que as instruções estão incluídas.
Quando se inicializa uma variável com uma expressão, esta é automaticamente convertida para o tipo da variável, se a conversão resultar em erro, o programa notifica com erro de execução. Por exemplo, na instrução LET i ="10" se a variável i for um inteiro, a expressão "10" é automaticamente convertida em inteiro.
compara.4gl
MAIN DEFINE y CHAR(20)
LET y = "Uma string"
IF y > "UMA STRING" THEN DISPLAY "Uma string maior que UMA STRING" END IF
IF y < "uma string" THEN DISPLAY "Uma string menor que UMA STRING" END IF
IF y = "Uma string" THEN DISPLAY "Uma string igual a UMA STRING" END IF END MAIN
RESULTADO: Uma string maior que UMA STRING Uma string menor que UMA STRING Uma string igual a UMA STRING
|
Vários exemplos de comparações
Em qualquer programa, muitas vezes é necessário proceder as inicializações de variáveis.
Uma das formas de o fazer é inicializando a variável com o valor pretendido, na área do programa em que se pretende fazer. Outra forma é utilizando a instrução INITIALIZE.
INITIALIZE lista_de_variáveis TO NULL
|
Definição genérica da instrução INITIALIZE
A instrução INITIALIZE inicializa as variáveis com valor NULL.
1.3.5 Operadores
Os operadores de comparação no Informix 4GL são:
Operador | Descrição | Exemplo |
> | maior que | var1 > var2 |
< | menor que | var1 < var2 |
>= | maior ou igual que | var1 >= var2 |
<= | menor ou igual que | var1 <= var2 |
= ou == | igual a | var1 = var2 |
!= ou <> | diferente de | var1 <> var2 |
NOT | negação | NOT <expressão> |
AND | e | <expressão1> AND <expressão2> |
OR | ou | <expressão1> OR <expressão2> |
MATCHES | comparação usando metacarateres (*, ?, []) | var1 MATCHES “*z?” |
LIKE | comparação usando metacaracteres (-, %) | var1 LIKE “%z_%” |
BETWEEN | intervalo de valores | BETWEEN 1 AND 100 |
IN | conjunto de valores | NOT IN (“A”,”B”,”C”) |
IS NULL | teste de valor nulo | var1 IS NULL / var2 IS NOT NULL |
Os operadores para cálculos matemáticos, no 4GL são:
Operador | Descrição | Exemplo |
+ | adição | var1 + var2 |
- | subtração | var1 - var2 |
** | exponenciação | var1 ** var2 |
MOD | módulo (divisão inteira c/ retorno do resto da divisão) | var1 MOD var2 |
* | multiplicação | var1 * var2 |
/ | divisão | var1 / var2 |
Os demais operadores existentes são:
Operador | Descrição | Exemplo |
. | Para referenciar um membro de uma variável do tipo RECORD. | lr_dados.código |
[] | Para referenciar um elemento de uma variável do tipo ARRAY ou um conjunto de caracteres de uma variável do tipo CHAR. | la_dados[10] |
() | Para chamada de funções. | CALL entrada_dados() |
UNITS | Para referenciar a unidade de qualificador. | LET data = data + 5 UNITS DAY |
|| ou , | Concatenação | “abc” || “def” ou “abc”,”def” |
ASCII | Retorna o caracter ASCII correspondente. | LET letra = ASCII(65) |
ORD | Retorna o código ASCII correspondente. | LET cod_ascii = ORD(“A”) |
CLIPPED | Elimina espaços em branco do final de um string. | LET l_texto = l_texto CLIPPED, “x” |
COLUMN | Para posicionar a coluna de impressão de dados. | PRINT COLUMN 10, “Texto”. |
SPACES | Para inserir espaços em branco. | DISPLAY 5 SPACES |
USING | Para formatar um conteúdo atribuído a um string. | LET texto = TODAY USING “dd/mm/yy” |
WORDWRAP | Impressão de um texto com quebra automática de linhas. | PRINT texto WORDWRAP |
1.3.6 Concatenação de variáveis tipo CHAR
No 4GL é muito simples concatenar strings, basta separá-las por uma virgula (o operador de concatenação de strings é a virgula).
concat.4gl MAIN DEFINE x CHAR(20), y CHAR(50)
LET x = "String 1 " LET y = "Concatenou ",x CLIPPED," com outras duas strings"
DISPLAY y END MAIN
RESULTADO Concatenou String 1 com outras duas strings
|
Exemplo de concatenação de strings